LLM大模型学习:大模型生成回复,解决“复读机”的原理

前言

在阅读本文前,需要熟悉torch的常用算子,现列举如下:

  1. torch.where(condition, x, y) 根据条件选择张量

    • condition 条件
    • x,当condition=True时,选择 x 的值
    • y,当condition=False时,选择 y 的值
  2. Tensor.scatter_(dim, index, src) 类似于torch.scatter

  3. torch.scatter(output, dim, index, src) 将张量 src 中的值,按照元素索引写入output中。

    • output 输出
    • dim要填充的维度
    • index 要填充的维度
    • src 输入
  4. torch.gather(input, dim, index, ***, sparse_grad=False, out=None) torch.scatter的反向操作

    • input 输入
    • dim维度
    • index 元素的索引
    • out 输出
  5. torch.sort(input, dim=- 1, descending=False, stable=False, out=None) 排序

    • dim 维度
    • descending TRUE表示降序排列,False表示升序排列
  6. torch.softmax(input, dim=None)

  7. cumsum(input, dim, dtype=None) 累加

    • 输入
    • dim 维度
  8. Tensor.masked_fill(mask, value) 填充值

    • mask bool类型,或者值是0,1的int类型
    • value
    • 将mask中为True的元素对应位置的设为value。
  9. torch.topk(input, k, dim=None, largest=True, sorted=True, ***, out=None) 返回k个最大的值

  10. torch.multinomial(input, num_samples, replacement=False, ***, generator=None, out=None) 抽取样本

*   num\_samples 抽取的样本数,注意这个时候不一定抽取到最大的值,增加了随机性
  1. torch.div(input, other, ***, rounding_mode=None, out=None) 除法
*   input 输入
*   other 一个数字或者和input维度相同的向量

模型输出示例程序如下:

response, history = self.model.chat(
        self.tokenizer,
        prompt,
        history=[],
        max_length=8192,
        top_p=1, do_sample=False,
        temperature=0.001)


实际上每个大模型都继承了PreTrainedModel,在预测时,会调用GenerationMixin的generate方法,类之间的继承关系如下

2024-02-19_11-54.png

模型生成回答,主要有以下几种搜索方法:

Contrastive Search


Multinomial sampling

与总是选择概率最高的标记作为下一个标记的贪婪搜索相反,Multinomial sampling根据模型给出的整个词汇表的概率分布随机选择下一个标记。

只需要将do_sample设为True即可

outputs = model.generate(**inputs, do_sample=True, num_beams=1, max_new_tokens=100)


源码解读

while True:
    # forward pass to get next token
    outputs = self(
        **model_inputs,
        return_dict=True,
        output_attentions=output_attentions,
        output_hidden_states=output_hidden_states,
    )

    next_token_logits = outputs.logits[:, -1, :]

    next_token_scores = logits_processor(input_ids, next_token_logits)
    next_token_scores = logits_warper(input_ids, next_token_scores)

    probs = nn.functional.softmax(next_token_scores, dim=-1)
    next_tokens = torch.multinomial(probs, num_samples=1).squeeze(1)



Beam Search的实现

beam search是对贪心策略一个改进。思路也很简单,就是稍微放宽一些考察的范围。在每一个时间步,不再只保留当前分数最高的1个输出,而是保留num_beams个。当num_beams=1时集束搜索就退化成了贪心搜索。

下图是一个实际的例子,每个时间步有ABCDE共5种可能的输出,即,图中的num_beams=2,也就是说每个时间步都会保留到当前步为止条件概率最优的2个序列。

beam_search.png

要实现beam search,只需要以下代码:

outputs = model.generate(**inputs, num_beams=5, max_new_tokens=50)


源码解读

代码很优雅

input_ids = input_ids.repeat_interleave(expand_size, dim=0)

// 每次输出num_beams个结果
while True:
    outputs = self(
        **model_inputs,
        return_dict=True,
        output_attentions=output_attentions,
        output_hidden_states=output_hidden_states,
    )

    next_token_logits = outputs.logits[:, -1, :]
    next_token_scores = nn.functional.log_softmax(
        next_token_logits, dim=-1
    )  # (batch_size * num_beams, vocab_size)

    # reshape for beam search
    vocab_size = next_token_scores.shape[-1]
    next_token_scores = next_token_scores.view(batch_size, num_beams * vocab_size)

    # Sample 1 + len(eos_token_id) next tokens for each beam so we have at least 1 non eos token per beam.
    n_eos_tokens = len(eos_token_id) if eos_token_id else 0
    next_token_scores, next_tokens = torch.topk(
        next_token_scores, max(2, 1 + n_eos_tokens) * num_beams, dim=1, largest=True, sorted=True
    )

    next_indices = torch.div(next_tokens, vocab_size, rounding_mode="floor")
    next_tokens = next_tokens % vocab_size

    # stateless
    beam_outputs = beam_scorer.process(
        input_ids,
        next_token_scores,
        next_tokens,
        next_indices,
        pad_token_id=pad_token_id,
        eos_token_id=eos_token_id,
        beam_indices=beam_indices,
        decoder_prompt_len=decoder_prompt_len,
    )

    beam_scores = beam_outputs["next_beam_scores"]
    beam_next_tokens = beam_outputs["next_beam_tokens"]
    beam_idx = beam_outputs["next_beam_indices"]

    input_ids = torch.cat([input_ids[beam_idx, :], beam_next_tokens.unsqueeze(-1)], dim=-1)

// 句子预测完,最后处理多个beams的结果
sequence_outputs = beam_scorer.finalize(
            input_ids,
            beam_scores,
            next_tokens,
            next_indices,
            pad_token_id=pad_token_id,
            eos_token_id=eos_token_id,
            max_length=stopping_criteria.max_length,
            beam_indices=beam_indices,
            decoder_prompt_len=decoder_prompt_len,
        )


流程:

  1. 将input 扩张成num_beams 个

    input_ids = input_ids.repeat_interleave(expand_size, dim=0)
    
    
    
  2. 模型预测

    outputs = self(
            **model_inputs,
            return_dict=True,
            output_attentions=output_attentions,
            output_hidden_states=output_hidden_states,
        )
    
    
    
  3. 获取最好的num_beams个结果

    next_token_logits = outputs.logits[:, -1, :]
    ......
    next_token_scores, next_tokens = torch.topk(
            next_token_scores, max(2, 1 + n_eos_tokens) * num_beams, dim=1, largest=True, sorted=True
    )
    
    
    
  4. 将结果保存

    beam_outputs = beam_scorer.process(
        input_ids,
        next_token_scores,
        next_tokens,
        next_indices,
        pad_token_id=pad_token_id,
        eos_token_id=eos_token_id,
        beam_indices=beam_indices,
        decoder_prompt_len=decoder_prompt_len,
    )
    
    
    
    
  5. 将最好的k个添加到input_ids

    input_ids = torch.cat([input_ids[beam_idx, :], beam_next_tokens.unsqueeze(-1)], dim=-1)
    
    
    
    
  6. 当预测完毕

    sequence_outputs = beam_scorer.finalize(
                input_ids,
                beam_scores,
                next_tokens,
                next_indices,
                pad_token_id=pad_token_id,
                eos_token_id=eos_token_id,
                max_length=stopping_criteria.max_length,
                beam_indices=beam_indices,
                decoder_prompt_len=decoder_prompt_len,
            )
    
    
    
    

因为考虑到多种情况,可能有多个batch_size,所以下面的代码将已完成(eos_token_id)的添加到_beam_hyps中

class BeamSearchScorer(BeamScorer):
   
    def __init__(self):
        self._beam_hyps = [
            BeamHypotheses(
                num_beams=self.group_size,
                length_penalty=self.length_penalty,
                early_stopping=self.do_early_stopping,
                max_length=max_length,
            )
            for _ in range(batch_size * self.num_beam_groups)
        ]

    def process(self) -> Dict[str, torch.Tensor]:
      
        batch_size = len(self._beam_hyps) // self.num_beam_groups

        device = input_ids.device
        next_beam_scores = torch.zeros((batch_size, self.group_size), dtype=next_scores.dtype, device=device)
        next_beam_tokens = torch.zeros((batch_size, self.group_size), dtype=next_tokens.dtype, device=device)
        next_beam_indices = torch.zeros((batch_size, self.group_size), dtype=next_indices.dtype, device=device)

        if isinstance(eos_token_id, int):
            eos_token_id = [eos_token_id]

        for batch_idx in range(batch_size):
            beam_idx = 0
            for beam_token_rank, (next_token, next_score, next_index) in enumerate(
                zip(next_tokens[batch_idx], next_scores[batch_idx], next_indices[batch_idx])
            ):
                batch_beam_idx = batch_idx * self.group_size + next_index
                # add to generated hypotheses if end of sentence
                if (eos_token_id is not None) and (next_token.item() in eos_token_id):
                    # if beam_token does not belong to top num_beams tokens, it should not be added
                    is_beam_token_worse_than_top_num_beams = beam_token_rank >= self.group_size
                    if is_beam_token_worse_than_top_num_beams:
                        continue
                    if beam_indices is not None:
                        beam_index = beam_indices[batch_beam_idx]
                        beam_index = beam_index + (batch_beam_idx,)
                    else:
                        beam_index = None

                    self._beam_hyps[batch_group_idx].add(
                        input_ids[batch_beam_idx].clone(),
                        next_score.item(),
                        beam_indices=beam_index,
                        generated_len=cur_len - decoder_prompt_len,
                    )



实现添加beam的功能,如果数量超过了设置的num_beams,结果最差的会被删除

如果有类似这样的需求,可以这样来实现

class BeamHypotheses:

    def __len__(self):
        return len(self.beams)

    def add(self):
        if len(self) < self.num_beams or score > self.worst_score:
            self.beams.append((score, hyp, beam_indices))
            if len(self) > self.num_beams:
                sorted_next_scores = sorted([(s, idx) for idx, (s, _, _) in enumerate(self.beams)])
                del self.beams[sorted_next_scores[0][1]]
                self.worst_score = sorted_next_scores[1][0]
            else:
                self.worst_score = min(score, self.worst_score)



下面的代码展示了有多个batch_size,要保存多个结果时怎么写代码

其中

  • i 循环的是batch_size
  • j 循环的是每个问题要保存的回答数
def finalize(
    self,
    input_ids: torch.LongTensor,
    final_beam_scores: torch.FloatTensor,
    final_beam_tokens: torch.LongTensor,
    final_beam_indices: torch.LongTensor,
    max_length: int,
    pad_token_id: Optional[int] = None,
    eos_token_id: Optional[Union[int, List[int]]] = None,
    beam_indices: Optional[torch.LongTensor] = None,
    decoder_prompt_len: Optional[int] = 0,
) -> Tuple[torch.LongTensor]:

    # select the best hypotheses
    sent_lengths = input_ids.new(batch_size * self.num_beam_hyps_to_keep)
    best = []
    best_indices = []
    best_scores = torch.zeros(batch_size * self.num_beam_hyps_to_keep, device=self.device, dtype=torch.float32)

    # retrieve best hypotheses
    for i in range(batch_size):
        beam_hyps_in_batch = self._beam_hyps[i * self.num_beam_groups : (i + 1) * self.num_beam_groups]
        candidate_beams = [beam for beam_hyp in beam_hyps_in_batch for beam in beam_hyp.beams]
        sorted_hyps = sorted(candidate_beams, key=lambda x: x[0])
        for j in range(self.num_beam_hyps_to_keep):
            best_hyp_tuple = sorted_hyps.pop()
            best_score = best_hyp_tuple[0]
            best_hyp = best_hyp_tuple[1]
            best_index = best_hyp_tuple[2]
            sent_lengths[self.num_beam_hyps_to_keep * i + j] = len(best_hyp)

            # append hyp to lists
            best.append(best_hyp)

            # append indices to list
            best_indices.append(best_index)

            best_scores[i * self.num_beam_hyps_to_keep + j] = best_score

    # prepare for adding eos
    sent_lengths_max = sent_lengths.max().item() + 1
    sent_max_len = min(sent_lengths_max, max_length) if max_length is not None else sent_lengths_max
    decoded: torch.LongTensor = input_ids.new(batch_size * self.num_beam_hyps_to_keep, sent_max_len)

    # fill with hypotheses and eos_token_id if the latter fits in
    for i, (hypo, best_idx) in enumerate(zip(best, best_indices)):
        decoded[i, : sent_lengths[i]] = hypo
        if sent_lengths[i] < sent_max_len:
            decoded[i, sent_lengths[i]] = eos_token_id[0]

    return UserDict(
        {
            "sequences": decoded,
            "sequence_scores": best_scores,
            "beam_indices": indices,
        }
    )




下面是一些处理方法

一般可以设置这些参数:

  • temperature 温度为 0 将始终产生相同的输出。温度越高随机性越大!

  • top_p

    动态设置tokens候选列表的大小。 将可能性之和不超过特定值的top tokens列入候选名单。 Top p 通常设置为较高的值(如 0.75),目的是限制可能被采样的低概率 token 的长度。

  • top_k

    允许其他高分tokens有机会被选中。 这种采样引入的随机性有助于在很多情况下生成的质量。 top-k 参数设置为 3意味着选择前三个tokens。将如果 k 和 p 都启用,则 p 在 k 之后起作用。

  • repetition_penalty

    重复性惩罚方法通过在模型推理过程中加入重复惩罚因子

优雅的实现处理操作

class LogitsProcessorList(list):
    def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor, **kwargs) -> torch.FloatTensor:
        for processor in self:
            scores = processor(input_ids, scores)
        return scores



RepetitionPenaltyLogitsProcessor

def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor) -> torch.FloatTensor:
    score = torch.gather(scores, 1, input_ids)

    # if score < 0 then repetition penalty has to be multiplied to reduce the token probabilities
    score = torch.where(score < 0, score * self.penalty, score / self.penalty)

    scores.scatter_(1, input_ids, score)
    return scores



TopPLogitsWarper

def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor) -> torch.FloatTensor:
    sorted_logits, sorted_indices = torch.sort(scores, descending=False)
    cumulative_probs = sorted_logits.softmax(dim=-1).cumsum(dim=-1)

    # Remove tokens with cumulative top_p above the threshold (token with 0 are kept)
    sorted_indices_to_remove = cumulative_probs <= (1 - self.top_p)
    # Keep at least min_tokens_to_keep
    sorted_indices_to_remove[..., -self.min_tokens_to_keep :] = 0

    # scatter sorted tensors to original indexing
    indices_to_remove = sorted_indices_to_remove.scatter(1, sorted_indices, sorted_indices_to_remove)
    scores = scores.masked_fill(indices_to_remove, self.filter_value)
    return scores



TopKLogitsWarper

def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor) -> torch.FloatTensor:
    indices_to_remove = scores < torch.topk(scores, top_k)[0][..., -1, None]
    scores = scores.masked_fill(indices_to_remove, self.filter_value)
    return scores



TemperatureLogitsWarper

def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor) -> torch.FloatTensor:
    scores = scores / self.temperature
    return scores



大模型&AI产品经理如何学习

求大家的点赞和收藏,我花2万买的大模型学习资料免费共享给你们,来看看有哪些东西。

1.学习路线图

在这里插入图片描述

第一阶段: 从大模型系统设计入手,讲解大模型的主要方法;

第二阶段: 在通过大模型提示词工程从Prompts角度入手更好发挥模型的作用;

第三阶段: 大模型平台应用开发借助阿里云PAI平台构建电商领域虚拟试衣系统;

第四阶段: 大模型知识库应用开发以LangChain框架为例,构建物流行业咨询智能问答系统;

第五阶段: 大模型微调开发借助以大健康、新零售、新媒体领域构建适合当前领域大模型;

第六阶段: 以SD多模态大模型为主,搭建了文生图小程序案例;

第七阶段: 以大模型平台应用与开发为主,通过星火大模型,文心大模型等成熟大模型构建大模型行业应用。

2.视频教程

网上虽然也有很多的学习资源,但基本上都残缺不全的,这是我自己整理的大模型视频教程,上面路线图的每一个知识点,我都有配套的视频讲解。

在这里插入图片描述

在这里插入图片描述

(都打包成一块的了,不能一一展开,总共300多集)

因篇幅有限,仅展示部分资料,需要点击下方图片前往获取

3.技术文档和电子书

这里主要整理了大模型相关PDF书籍、行业报告、文档,有几百本,都是目前行业最新的。
在这里插入图片描述

4.LLM面试题和面经合集

这里主要整理了行业目前最新的大模型面试题和各种大厂offer面经合集。
在这里插入图片描述

👉学会后的收获:👈
• 基于大模型全栈工程实现(前端、后端、产品经理、设计、数据分析等),通过这门课可获得不同能力;

• 能够利用大模型解决相关实际项目需求: 大数据时代,越来越多的企业和机构需要处理海量数据,利用大模型技术可以更好地处理这些数据,提高数据分析和决策的准确性。因此,掌握大模型应用开发技能,可以让程序员更好地应对实际项目需求;

• 基于大模型和企业数据AI应用开发,实现大模型理论、掌握GPU算力、硬件、LangChain开发框架和项目实战技能, 学会Fine-tuning垂直训练大模型(数据准备、数据蒸馏、大模型部署)一站式掌握;

• 能够完成时下热门大模型垂直领域模型训练能力,提高程序员的编码能力: 大模型应用开发需要掌握机器学习算法、深度学习框架等技术,这些技术的掌握可以提高程序员的编码能力和分析能力,让程序员更加熟练地编写高质量的代码。
在这里插入图片描述

1.AI大模型学习路线图
2.100套AI大模型商业化落地方案
3.100集大模型视频教程
4.200本大模型PDF书籍
5.LLM面试题合集
6.AI产品经理资源合集

👉获取方式:
😝有需要的小伙伴,可以保存图片到wx扫描二v码免费领取【保证100%免费】🆓

本文转自 https://blog.csdn.net/2401_84494441/article/details/141993983?spm=1001.2014.3001.5501,如有侵权,请联系删除。

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值