昇思25天学习打卡营第14天|LLM-文本解码原理--以MindNLP为例

打卡

目录

打卡

自回归语言模型

预备环境

理论与实践

[贪心搜索 - 下一个最优](#贪心搜索 - 下一个最优)

代码示例

输出示例

[Beam Search - 序列最优](#Beam Search - 序列最优)

代码示例

输出示例

[采样输出 - 生成多样性](#采样输出 - 生成多样性)

代码示例

输出示例

[Temperature 设置](#Temperature 设置)

代码示例

输出示例

[TopK 采样输出:概率最大采样、固定采样池](#TopK 采样输出:概率最大采样、固定采样池)

代码示例

输出示例

[Top-P 采样输出 : 累积概率、动态采样池](#Top-P 采样输出 : 累积概率、动态采样池)

代码示例

输出示例

top_k_top_p

代码示例

输出示例


本小解通过 mindnlp.transformers 加载 gpt2 模型的实践,对比观察了不同参数设置下的gpt2模型生成输出结果。包括只关心下一个最优的贪心搜索、序列最优的Beam搜索、采样生成 及其在不同参数(如温度、top-k、top-p设置)下的输出效果,不同的算法和参数设置有不同的效果,可以根据实际场景选择。

自回归语言模型

要点:根据前文预测下一个单词。

一个文本序列的概率分布可以分解为每个词基于其上文的条件概率的乘积:

,其中,

  • : 初始上下文单词序列
  • T:时间步
  • 当生成EOS标签时,停止生成。

MindNLP/huggingface Transformers提供的文本生成方法:分为 beam 方法和 非beam方法。

预备环境

pip install mindnlp
pip install -i https://pypi.mirrors.ustc.edu.cn/simple mindspore==2.2.14

理论与实践

贪心搜索 - 下一个最优

  • 在每个时间步 t 都简单地选择概率最高的词作为当前输出词,缺点是错过了隐藏在低概率词后面的高概率词:
  • 用MindNLP库实现的贪婪搜索生成文本的过程:

1、导入了GPT2模型的分词器(tokenizer)和语言模型头(LMHeadModel)。

2、从镜像源 'modelscope' 加载 "iiBcai/gpt2" 项目的预训练的GPT2分词器和GPT2语言模型头,并将EOS(End Of Sequence)标记作为PAD(Padding)标记,这样做是为了避免在模型处理序列时出现警告。

3、使用分词器将字符串 'I enjoy walking with my cute dog' 编码成模型可以理解的输入ID。return_tensors='ms' 表示返回的是MindSpore格式的张量。

4、使用模型进行贪婪搜索生成文本,其中 max_length=50 指定了生成文本(包括输入的上下文)的最大长度为50个token。

5、tokenizer.decode 函数将模型的输出ID转换回文本字符串。skip_special_tokens=True 参数表示在解码过程中忽略特殊token,例如 EOS 和 PAD token。

代码示例

python 复制代码
#greedy_search
from mindnlp.transformers import GPT2Tokenizer, GPT2LMHeadModel

## 加载 gpt2 tokenizer
tokenizer = GPT2Tokenizer.from_pretrained("iiBcai/gpt2", mirror='modelscope')

# add the EOS token as PAD token to avoid warnings
model = GPT2LMHeadModel.from_pretrained(
                "iiBcai/gpt2", 
                pad_token_id=tokenizer.eos_token_id, mirror='modelscope')
# encode context the generation is conditioned on
input_ids = tokenizer.encode('I enjoy walking with my cute dog',
                              return_tensors='ms')

# generate text until the output length (which includes the context length) reaches 50
greedy_output = model.generate(input_ids, max_length=50)

print("Output:\n" + 100 * '-')
print(tokenizer.decode(greedy_output[0], skip_special_tokens=True))

输出示例

修改输入为中文后的输出。

Beam search通过在每个时间步保留最可能的 num_beams 个词,并从中最终选择出概率最高的序列来降低丢失潜在的高概率序列的风险。

优点:一定程度保留最优路径

缺点:1. 无法解决重复问题;2. 开放域生成效果差

如下图,句子序列的概率是每个可能生成的字的概率的乘积,比如,("The","dog","has") : 0.4 * 0.9 = 0.36

代码实现流程,其中,1、2、3 步骤同贪心搜索过程。

4.1、使用模型进行Beam搜索生成文本,其中 num_beams=5 表示维护了大小为 5 的 beam,即在每一步考虑5个可能的输出序列, max_length=50 限制了输出序列的最大长度为 50 个 token。early_stopping=True 表示一旦所有beam中的序列都完成了(即生成了EOS token),则停止生成。

4.2、通过设置 no_repeat_ngram_size 来避免重复的 n-gram,比如,设置为2表示在生成文本时避免重复2-gram(即连续两个token)。这有助于提高生成文本的多样性。

4.3、通过设置 num_return_sequences=5 表示返回前5个beam中的序列。

5、tokenizer.decode 函数将模型的输出ID转换回文本字符串,解码了第一个beam中的序列。其中,skip_special_tokens=True 参数表示在解码过程中忽略特殊token,例如 EOS 和 PAD token。

代码示例
python 复制代码
from mindnlp.transformers import GPT2Tokenizer, GPT2LMHeadModel

tokenizer = GPT2Tokenizer.from_pretrained("iiBcai/gpt2", mirror='modelscope')

# add the EOS token as PAD token to avoid warnings
model = GPT2LMHeadModel.from_pretrained("iiBcai/gpt2", pad_token_id=tokenizer.eos_token_id, mirror='modelscope')

# encode context the generation is conditioned on
input_ids = tokenizer.encode('I enjoy walking with my cute dog', return_tensors='ms')

# activate beam search and early_stopping
beam_output = model.generate(
    input_ids, 
    max_length=50, 
    num_beams=5, 
    early_stopping=True
)

print("Output:\n" + 100 * '-')
print(tokenizer.decode(beam_output[0], skip_special_tokens=True))
print(100 * '-')

# set no_repeat_ngram_size to 2
beam_output = model.generate(
    input_ids, 
    max_length=50, 
    num_beams=5, 
    no_repeat_ngram_size=2, 
    early_stopping=True
)

print("Beam search with ngram, Output:\n" + 100 * '-')
print(tokenizer.decode(beam_output[0], skip_special_tokens=True))
print(100 * '-')

# set return_num_sequences > 1
beam_outputs = model.generate(
    input_ids, 
    max_length=50, 
    num_beams=5, 
    no_repeat_ngram_size=2, 
    num_return_sequences=5, 
    early_stopping=True
)

# now we have 3 output sequences
print("return_num_sequences, Output:\n" + 100 * '-')
for i, beam_output in enumerate(beam_outputs):
    print("{}: {}".format(i, tokenizer.decode(beam_output, skip_special_tokens=True)))
print(100 * '-')
输出示例

如上两个图,当出现重复问题时,进行n-gram 惩罚,讲出现过的候选词概率设置为0,比如,当设置no_repeat_ngram_size=2 ,任意 2-gram 不会出现两次,但实际上有时需要重复的文本出现。

修改输入为中文后的输出,很差,感觉日文占比很多。

采样输出 - 生成多样性

根据当前条件概率分布随机选择 输出词

  • 优点:文本生成多样性高
  • 缺点:生成文本不连续

例子:

("car") ~P(w∣"The") ,car 的输出由基于The条件下的nice、dog、car的分布概率选择出。

("drives") ~P(w∣"The","car") , drivers 的输出由基于The 和 Car 条件下的 drivers、is、turns 的分布概率选择出。

代码示例

代码实现流程,其中,1、2、3 步骤同贪心搜索过程。

4、设置了MindSpore的随机种子为0,确保每次运行代码时生成的结果都是可复现的。

5、模型生成文本时,通过设置 do_sample=True 激活了模型的采样生成策略, 限制输出序列的最大长度为50个token。top_k=0 表示禁用了top-k采样,因为在top-k采样中,通常是从概率最高的k个token中随机选择一个token作为下一个输出,而这里设置为0表示不限制token的选择,实际上这将等同于使用 softmax 概率分布直接进行采样。

6、tokenizer.decode 函数将模型的输出ID转换回文本字符串,解码了第一个beam中的序列。其中,skip_special_tokens=True 参数表示在解码过程中忽略特殊token,例如 EOS 和 PAD token。

python 复制代码
import mindspore
from mindnlp.transformers import GPT2Tokenizer, GPT2LMHeadModel

tokenizer = GPT2Tokenizer.from_pretrained("iiBcai/gpt2", mirror='modelscope')

# add the EOS token as PAD token to avoid warnings
model = GPT2LMHeadModel.from_pretrained("iiBcai/gpt2", pad_token_id=tokenizer.eos_token_id, mirror='modelscope')

# encode context the generation is conditioned on
input_ids = tokenizer.encode('I enjoy walking with my cute dog', return_tensors='ms')

mindspore.set_seed(0)
# activate sampling and deactivate top_k by setting top_k sampling to 0
sample_output = model.generate(
    input_ids, 
    do_sample=True, 
    max_length=50, 
    top_k=0
)

print("Output:\n" + 100 * '-')
print(tokenizer.decode(sample_output[0], skip_special_tokens=True))
输出示例

中文输出依旧很垃圾。

Temperature 设置

降低 softmax 的 temperature 使 分布更陡峭,即增加高概率单词的似然并降低低概率单词的似然。

代码示例

代码实现流程,其中,1、2、3、4、5 步骤同采样输出过程。

6、temperature=0.7 控制了生成的 token 的概率分布的平滑程度。较高的temperature值会使得分布更加平滑,从而增加输出的多样性,而较低的temperature值会使得分布更加尖锐,使得模型更倾向于选择概率最高的token

7、tokenizer.decode 函数将模型的输出ID转换回文本字符串,解码了第一个beam中的序列。其中,skip_special_tokens=True 参数表示在解码过程中忽略特殊token,例如 EOS 和 PAD token。

python 复制代码
import mindspore
from mindnlp.transformers import GPT2Tokenizer, GPT2LMHeadModel

tokenizer = GPT2Tokenizer.from_pretrained("iiBcai/gpt2", mirror='modelscope')

# add the EOS token as PAD token to avoid warnings
model = GPT2LMHeadModel.from_pretrained("iiBcai/gpt2", pad_token_id=tokenizer.eos_token_id, mirror='modelscope')

# encode context the generation is conditioned on
input_ids = tokenizer.encode('I enjoy walking with my cute dog', return_tensors='ms')

mindspore.set_seed(1234)
# activate sampling and deactivate top_k by setting top_k sampling to 0
sample_output = model.generate(
    input_ids, 
    do_sample=True, 
    max_length=50, 
    top_k=0,
    temperature=0.7
)

print("Output:\n" + 100 * '-')
print(tokenizer.decode(sample_output[0], skip_special_tokens=True))
输出示例

temperature=0.7 时

temperature=0.3 时

TopK 采样输出:概率最大采样、固定采样池

选出概率最大的 K 个词,重新归一化,最后在归一化后的 K 个词中采样。

将采样池限制为固定大小 K

  • 在分布比较尖锐的时候产生胡言乱语
  • 在分布比较平坦的时候限制模型的创造力

例子展示:

代码示例

代码实现流程,其中,1、2、3、4 步骤同采样输出过程。

5、模型生成文本时,通过设置 do_sample=True 激活了模型的采样生成策略 , 限制输出序列的最大长度为50个token。top_k=0 表示禁用了top-k采样,因为在top-k采样中,通常是从概率最高的k个token中随机选择一个token作为下一个输出,而这里设置为0表示不限制token的选择,实际上这将等同于使用 softmax 概率分布直接进行采样。

6、tokenizer.decode 函数将模型的输出ID转换回文本字符串,解码了第一个beam中的序列。其中,skip_special_tokens=True 参数表示在解码过程中忽略特殊token,例如 EOS 和 PAD token。

python 复制代码
import mindspore
from mindnlp.transformers import GPT2Tokenizer, GPT2LMHeadModel

tokenizer = GPT2Tokenizer.from_pretrained("iiBcai/gpt2", mirror='modelscope')

# add the EOS token as PAD token to avoid warnings
model = GPT2LMHeadModel.from_pretrained("iiBcai/gpt2", pad_token_id=tokenizer.eos_token_id, mirror='modelscope')

# encode context the generation is conditioned on
input_ids = tokenizer.encode('I enjoy walking with my cute dog', return_tensors='ms')

mindspore.set_seed(0)
# activate sampling and deactivate top_k by setting top_k sampling to 0
sample_output = model.generate(
    input_ids, 
    do_sample=True, 
    max_length=50, 
    top_k=50
)

print("Output:\n" + 100 * '-')
print(tokenizer.decode(sample_output[0], skip_special_tokens=True))

输出示例

Top-P 采样输出 : 累积概率、动态采样池

在累积概率超过概率 p 的最小单词集中进行采样,重新归一化。

其中,采样池可以根据下一个词的概率分布动态增加和减少。

例子:

代码示例

代码实现流程,其中,1、2、3、4 步骤同采样输出过程。

5、模型生成文本时,通过设置 do_sample=True 激活了模型的采样生成策略 , 限制输出序列的最大长度为50个token。top-p=0.92,top-p采样表示在每一步生成token时,只从概率分布中累计概率达到92%的token中进行采样。这有助于保持生成文本的流畅性和质量,同时允许一些低概率的token被选中,从而增加多样性。top_k=0 表示禁用了top-k采样。

6、tokenizer.decode 函数将模型的输出ID转换回文本字符串,解码了第一个beam中的序列。其中,skip_special_tokens=True 参数表示在解码过程中忽略特殊token,例如 EOS 和 PAD token。

python 复制代码
import mindspore
from mindnlp.transformers import GPT2Tokenizer, GPT2LMHeadModel

tokenizer = GPT2Tokenizer.from_pretrained("iiBcai/gpt2", mirror='modelscope')

# add the EOS token as PAD token to avoid warnings
model = GPT2LMHeadModel.from_pretrained("iiBcai/gpt2", pad_token_id=tokenizer.eos_token_id, mirror='modelscope')

# encode context the generation is conditioned on
input_ids = tokenizer.encode('I enjoy walking with my cute dog', return_tensors='ms')

mindspore.set_seed(0)

# deactivate top_k sampling and sample only from 92% most likely words
sample_output = model.generate(
    input_ids, 
    do_sample=True, 
    max_length=50, 
    top_p=0.92, 
    top_k=0
)

print("Output:\n" + 100 * '-')
print(tokenizer.decode(sample_output[0], skip_special_tokens=True))

​​​​​​​输出示例

top_k_top_p

代码示例

代码实现流程,其中,1、2、3、4 步骤同采样输出过程。

5、模型生成文本时,通过设置 do_sample=True 激活了模型的采样生成策略 , 限制输出序列的最大长度为50个token。top-p=0.95,top-p采样表示在每一步生成token时,只从概率分布中累计概率达到95%的token中进行采样,有助于保持生成文本的流畅性和质量,同时允许一些低概率的token被选中,从而增加多样性。top_k=5 表示在每一步生成token时,模型将考虑概率最高的5个token进行采样。num_return_sequences=3 指示模型生成3个不同的输出序列。

6、tokenizer.decode 函数将模型的输出ID转换回文本字符串,解码了第一个beam中的序列。其中,skip_special_tokens=True 参数表示在解码过程中忽略特殊token,例如 EOS 和 PAD token。

python 复制代码
import mindspore
from mindnlp.transformers import GPT2Tokenizer, GPT2LMHeadModel

tokenizer = GPT2Tokenizer.from_pretrained("iiBcai/gpt2", mirror='modelscope')

# add the EOS token as PAD token to avoid warnings
model = GPT2LMHeadModel.from_pretrained("iiBcai/gpt2", pad_token_id=tokenizer.eos_token_id, mirror='modelscope')

# encode context the generation is conditioned on
input_ids = tokenizer.encode('I enjoy walking with my cute dog', return_tensors='ms')

mindspore.set_seed(0)
# set top_k = 50 and set top_p = 0.95 and num_return_sequences = 3
sample_outputs = model.generate(
    input_ids,
    do_sample=True,
    max_length=50,
    top_k=5,
    top_p=0.95,
    num_return_sequences=3
)

print("Output:\n" + 100 * '-')
for i, sample_output in enumerate(sample_outputs):
  print("{}: {}".format(i, tokenizer.decode(sample_output, skip_special_tokens=True)))

输出示例

相关推荐
开心工作室_kaic2 小时前
基于Vue的兴趣活动推荐APP的设计与实现_kaic
java·c语言·开发语言·数据库·python·sqlite·php
说私域2 小时前
开源 AI 智能名片 S2B2C 商城小程序相关角色的探索
人工智能·搜索引擎·小程序
大数据AI人工智能培训专家培训讲师叶梓2 小时前
大模型从失败中学习 —— 微调大模型以提升Agent性能
人工智能·学习·性能优化·微调·agent·代理·大模型微调
youcans_4 小时前
OpenAI全新发布o1模型:开启 AGI 的新时代
人工智能·chatgpt·agi
黑色叉腰丶大魔王4 小时前
《自然语言处理 Transformer 模型详解》
人工智能·自然语言处理·transformer
ersaijun7 小时前
【Obsidian】当笔记接入AI,Copilot插件推荐
人工智能·笔记·copilot
易雪寒7 小时前
Maven从入门到精通(三)
java·python·maven
FreakStudio7 小时前
全网最适合入门的面向对象编程教程:49 Python函数方法与接口-函数与方法的区别和lamda匿名函数
python·嵌入式·面向对象·电子diy
lgbisha7 小时前
828华为云征文|华为云Flexus X实例docker部署Jitsi构建属于自己的音视频会议系统
docker·华为云·音视频
Good_tea_h7 小时前
如何实现Java中的多态性
java·开发语言·python