transformers==4.28.1
源码地址:transformers/configuration_utils.py at v4.28.1 · huggingface/transformers (github.com)
文档地址:Generation (huggingface.co)
对于生成任务而言:text-decoder, text-to-text, speech-to-text, and vision-to-text models,有以下几种生成的方法:
~generation.GenerationMixin.greedy_search
] if num_beams=1
and
do_sample=False
~generation.GenerationMixin.contrastive_search
] if penalty_alpha>0.
and top_k>1
~generation.GenerationMixin.sample
] if num_beams=1
and
do_sample=True
~generation.GenerationMixin.beam_search
] if num_beams>1
and
do_sample=False
~generation.GenerationMixin.beam_sample
] if
num_beams>1
and do_sample=True
~generation.GenerationMixin.group_beam_search
], if
num_beams>1
and num_beam_groups>1
~generation.GenerationMixin.constrained_beam_search
], if
constraints!=None
or force_words_ids!=None
具体有以下参数可供选择:
(1)控制输出长度的参数
num_beams
个句子后停止 beam search,默认是False。(2)控制输出策略的参数
do_sample (bool, optional, defaults to False) - 是否使用采样,否则使用贪婪解码 。
num_beams (int, optional, defaults to 1) - 集束搜索的集束数量。1意味着没有集束搜索 。
num_beam_groups (int, optional, defaults to 1) - 将num_beam分成的组数,以确保不同组的beams的多样性。https://arxiv.org/pdf/1610.02424.pdf
penalty_alpha (float, optional) - 平衡模型置信度和对比搜索解码中的退化惩罚的数值。
use_cache (bool, optional, defaults to True) - 模型是否应该使用过去最后的键/值注意力(如果适用于模型)来加速解码。
(3)控制模型输出Logits的参数
(4)定义generate
输出变量的参数
(5)可在生成时使用的特殊参数
(6)编码器-解码器模型独有的生成参数
encoder_no_repeat_ngram_size (int, optional, defaults to 0) - 如果设置为int > 0,所有出现在encoder_input_ids中的该大小的ngrams都不能出现在decoder_input_ids中 。
decoder_start_token_id (int, optional) - 如果一个编码器-解码器模型以不同于bos的 token开始解码,则这就是该token的id。
我们可以这么使用、保存预训练模型已经定义好的参数:
from transformers import AutoTokenizer, AutoModelForCausalLM, TextGenerationPipeline, GenerationConfig model_name_or_path = "uer/gpt2-chinese-cluecorpussmall" tokenizer = AutoTokenizer.from_pretrained(model_name_or_path) model = AutoModelForCausalLM.from_pretrained(model_name_or_path) generation_config = model.generation_config generation_config_dict = generation_config.to_dict() generation_config_dict["num_beams"] = 2 generation_config = GenerationConfig.from_dict(generation_config_dict) print(generation_config) generation_config.save_pretrained("./") """ { "_from_model_config": true, "bos_token_id": 50256, "eos_token_id": 50256, "num_beams": 2, "transformers_version": "4.28.1" } """
需要注意的是,如果参数是默认的值得话,则不会显示出来。另外,GenerationConfig类里面有许多可用的方法,具体可以去看看源代码。
在定义好config之后,我们可以这么使用:
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer, GenerationConfig tokenizer = AutoTokenizer.from_pretrained("t5-small") model = AutoModelForSeq2SeqLM.from_pretrained("t5-small") translation_generation_config = GenerationConfig( num_beams=4, early_stopping=True, decoder_start_token_id=0, eos_token_id=model.config.eos_token_id, pad_token=model.config.pad_token_id, ) translation_generation_config.save_pretrained("t5-small", "translation_generation_config.json", push_to_hub=True) # You could then use the named generation config file to parameterize generation # 可以加载我们自己本地保存的generation_config generation_config = GenerationConfig.from_pretrained("t5-small", "translation_generation_config.json") inputs = tokenizer("translate English to French: Configuration files are easy to use!", return_tensors="pt") outputs = model.generate(**inputs, generation_config=generation_config) print(tokenizer.batch_decode(outputs, skip_special_tokens=True))
使用transformers库的生成模型生成结果有三种方式,暂时不要在意参数:
指定为text-generation
from transformers import pipeline generator = pipeline( 'text-generation', model="uer/gpt2-chinese-cluecorpussmall", ) text_inputs = ["昨天已经过去,"] generator(text_inputs, max_length=100)
from transformers import AutoTokenizer, AutoModelForCausalLM, TextGenerationPipeline tokenizer = AutoTokenizer.from_pretrained("uer/gpt2-chinese-cluecorpussmall") model = AutoModelForCausalLM.from_pretrained("uer/gpt2-chinese-cluecorpussmall") text_generator = TextGenerationPipeline(model, tokenizer) text_inputs = ["昨天已经过去,"] text_generator(text_inputs, max_length=100)
from transformers import AutoTokenizer, AutoModelForCausalLM import torch, os tokenizer = AutoTokenizer.from_pretrained("uer/gpt2-chinese-cluecorpussmall") model = AutoModelForCausalLM.from_pretrained("uer/gpt2-chinese-cluecorpussmall") device = 'cuda' if torch.cuda.is_available() else 'cpu' model = model.to(device) texts = ["昨天已经过去,"] #用batch输入的时候一定要设置padding encoding = tokenizer(texts, return_tensors='pt', padding=True).to(device) model.eval() with torch.no_grad(): generated_ids = model.generate(**encoding, max_length=100) generated_texts = tokenizer.batch_decode(generated_ids, skip_special_tokens=True) for text in generated_texts: print(text)
我们捋一捋它们之间的关系:最基础的还是model.generate(),而TextGenerationPipeline在_forward里面调用了model.generate(),pipeline实际上是对TextGenerationPipeline的进一步封装:
"text-generation": { "impl": TextGenerationPipeline, "tf": TFAutoModelForCausalLM if is_tf_available() else None, "pt": AutoModelForCausalLM if is_torch_available() else None, "default": {"model": {"pt": "gpt2", "tf": "gpt2"}}, },
在介绍不同的生成方法之前,先介绍下流式打印。使用过ChatGPT的玩家都知道,在生成结果的时候,它是一部分一部分的返回生成的文本并展示的,transformers该版本也有这个功能,我们接下来看。
from transformers import AutoModelForCausalLM, AutoTokenizer, TextStreamer tokenizer = AutoTokenizer.from_pretrained("uer/gpt2-chinese-cluecorpussmall") model = AutoModelForCausalLM.from_pretrained("uer/gpt2-chinese-cluecorpussmall") input_text = "昨天已经过去," inputs = tokenizer([input_text], return_tensors="pt", add_special_tokens=False) streamer = TextStreamer(tokenizer) # Despite returning the usual output, the streamer will also print the generated text to stdout. _ = model.generate(**inputs, streamer=streamer, max_new_tokens=86)
如果想要一次性返回结果再打印,则是这样的:
from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer from threading import Thread tokenizer = AutoTokenizer.from_pretrained("uer/gpt2-chinese-cluecorpussmall") model = AutoModelForCausalLM.from_pretrained("uer/gpt2-chinese-cluecorpussmall") input_text = "昨天已经过去," inputs = tokenizer([input_text], return_tensors="pt", add_special_tokens=False) streamer = TextIteratorStreamer(tokenizer) # Run the generation in a separate thread, so that we can fetch the generated text in a non-blocking way. generation_kwargs = dict(inputs, streamer=streamer, max_new_tokens=100) thread = Thread(target=model.generate, kwargs=generation_kwargs) thread.start() generated_text = "" for new_text in streamer: generated_text += new_text generated_text
接下来将以之前训练好的观点评论生成的GPT来生成不同的结果,我们每次都使用三种方式对比看看结果。
generate默认使用贪婪的搜索解码,所以你不需要传递任何参数来启用它。这意味着参数num_beams被设置为1,do_sample=False。
如图上所属,每次选择概率值最高的词。贪心搜索的主要缺点是它错过了隐藏在低概率词后面的高概率词,比如has=0.9不会被选择到。
from transformers import AutoTokenizer, AutoModelForCausalLM, TextGenerationPipeline, pipeline tokenizer = AutoTokenizer.from_pretrained("./gpt2-chinese") model = AutoModelForCausalLM.from_pretrained("./gpt2-chinese") from datasets import load_dataset data_file = "./ChnSentiCorp_htl_all.csv" dataset = load_dataset("csv", data_files=data_file) dataset = dataset.filter(lambda x: x["review"] is not None) dataset = dataset["train"].train_test_split(0.2, seed=123) import random example = random.choice(dataset["train"]) text = example["review"] input_text = text[:10] print(input_text) # greedy search model.eval() with torch.no_grad(): encoding = tokenizer(input_text, return_tensors='pt', padding=False, add_special_tokens=False, return_token_type_ids=False, return_attention_mask=False,) generated_ids = model.generate(**encoding, max_length=100, eos_token_id=0, pad_token_id=0, num_beams=1, do_sample=False) generated_texts = tokenizer.batch_decode(generated_ids, skip_special_tokens=True) print(generated_texts) text_generator = TextGenerationPipeline(model, tokenizer) print(text_generator(input_text, max_length=100, eos_token_id=0, num_beams=1, do_sample=False, pad_token_id=0)) generator = pipeline("text-generation", model=model, tokenizer=tokenizer) generation_config = { "max_length": 100, "eos_token_id": 0, "pad_token_id": 0, "num_beams": 1, "do_sample": False, } print(generator(input_text, **generation_config)) """ 虽然说是4星级,不过 ['虽 然 说 是 4 星 级 , 不 过 感 觉 和 3 星 没 什 么 两 样 , 只 是 服 务 水 准 差 了 点 而 已'] [{'generated_text': '虽然说是4星级,不过 感 觉 和 3 星 没 什 么 两 样 , 只 是 服 务 水 准 差 了 点 而 已'}] [{'generated_text': '虽然说是4星级,不过 感 觉 和 3 星 没 什 么 两 样 , 只 是 服 务 水 准 差 了 点 而 已'}] """
答案是一致的,和我们之前的推测一样,但需要注意的是model.gneerate()对单条预测的时候我们在tokenizer的时候设置padding为False了,如果设置为True,则得不到相同的结果。
对比搜索解码策略是在2022年的论文A Contrastive Framework for Neural Text Generation https://arxiv.org/abs/2202.06417中提出的。它展示了生成非重复但连贯的长输出的优越结果。要了解对比性搜索的工作原理,请查看这篇博文https://huggingface.co/blog/introducing-csearch。启用和控制对比性搜索行为的两个主要参数是punice_alpha和top_k:
from transformers import AutoTokenizer, AutoModelForCausalLM, TextGenerationPipeline, pipeline tokenizer = AutoTokenizer.from_pretrained("./gpt2-chinese") model = AutoModelForCausalLM.from_pretrained("./gpt2-chinese") from datasets import load_dataset data_file = "./ChnSentiCorp_htl_all.csv" dataset = load_dataset("csv", data_files=data_file) dataset = dataset.filter(lambda x: x["review"] is not None) dataset = dataset["train"].train_test_split(0.2, seed=123) import random example = random.choice(dataset["train"]) # text = dataset["train"][0] text = example["review"] input_text = text[:10] print(input_text) # greedy search model.eval() with torch.no_grad(): encoding = tokenizer(input_text, return_tensors='pt', padding=False, add_special_tokens=False, return_token_type_ids=False, return_attention_mask=False,) generated_ids = model.generate(**encoding, max_length=100, eos_token_id=0, pad_token_id=0, do_sample=False, num_beams=1, penalty_alpha=0.6, top_k=4) generated_texts = tokenizer.batch_decode(generated_ids, skip_special_tokens=True) print(generated_texts) text_generator = TextGenerationPipeline(model, tokenizer) print(text_generator(input_text, max_length=100, eos_token_id=0, num_beams=1, do_sample=False, pad_token_id=0, penalty_alpha=0.6, top_k=4 )) generator = pipeline("text-generation", model=model, tokenizer=tokenizer) generation_config = { "max_length": 100, "eos_token_id": 0, "pad_token_id": 0, "num_beams": 1, "do_sample": False, # "penalty_alpha":0.6, # "top_k":4, } print(generator(input_text, **generation_config)) """ ['极 差 ! 停 车 收 十 元 钱 ! 穷 则 思 变 ! 房 间 潮 湿 得 不 得 了 , 晚 上 居 然 停 了 一 个 多 小 时 , 上 网 一 会 有 信 号 一 会 没 有 。 电 视 遥 控 器 不 管 用 , 打 电 话 给 客 房 中 心 , 得 到 的 回 复 居 然 是 坏 的 房 间 在 维 修 , 不 知 道'] [{'generated_text': '极差!停车收十元钱! 穷 则 思 变 ! 房 间 潮 湿 得 不 得 了 , 晚 上 居 然 停 了 一 个 多 小 时 , 上 网 一 会 有 信 号 一 会 没 有 。 电 视 遥 控 器 不 管 用 , 打 电 话 给 客 房 中 心 , 得 到 的 回 复 居 然 是 坏 的 房 间 在 维 修 , 不 知 道'}] [{'generated_text': '极差!停车收十元钱! 穷 则 思 变 ! 房 间 设 施 差 就 一 个 招 待 所 , 最 多 三 星 级 !'}] """
可以对比和贪婪解码看一下结果。
与总是选择概率最高的标记作为下一个标记的贪婪搜索相反,多项式抽样(也称为祖先抽样)根据模型给出的整个词汇的概率分布来随机选择下一个标记。每个概率不为零的符号都有机会被选中,从而减少了重复的风险。
from transformers import AutoTokenizer, AutoModelForCausalLM, TextGenerationPipeline, pipeline tokenizer = AutoTokenizer.from_pretrained("./gpt2-chinese") model = AutoModelForCausalLM.from_pretrained("./gpt2-chinese") from datasets import load_dataset data_file = "./ChnSentiCorp_htl_all.csv" dataset = load_dataset("csv", data_files=data_file) dataset = dataset.filter(lambda x: x["review"] is not None) dataset = dataset["train"].train_test_split(0.2, seed=123) import random example = random.choice(dataset["train"]) # text = dataset["train"][0] text = example["review"] input_text = text[:10] print(input_text) # greedy search model.eval() with torch.no_grad(): encoding = tokenizer(input_text, return_tensors='pt', padding=False, add_special_tokens=False, return_token_type_ids=False, return_attention_mask=False,) generated_ids = model.generate(**encoding, max_length=100, eos_token_id=0, pad_token_id=0, do_sample=True, num_beams=1, ) generated_texts = tokenizer.batch_decode(generated_ids, skip_special_tokens=True) print(generated_texts) text_generator = TextGenerationPipeline(model, tokenizer) print(text_generator(input_text, max_length=100, eos_token_id=0, num_beams=1, do_sample=True, pad_token_id=0, )) generator = pipeline("text-generation", model=model, tokenizer=tokenizer) generation_config = { "max_length": 100, "eos_token_id": 0, "pad_token_id": 0, "num_beams": 1, "do_sample": True, } print(generator(input_text, **generation_config)) """ ['房 间 : 建 筑 风 格 比 较 独 特 , 但 不 显 现 空 间 特 色 。 地 理 位 置 不 是 很 好 , 离 九 华 山 比 较 远 , 出 租 车 还 比 较 难 找 。 门 童 服 务 蛮 好 , 门 口 迎 宾 也 很 热 情 。 房 间 设 施 : 住 9 楼 标 房 , 朝 西 , 马 路 上 的 喧 嚣 比 较'] [{'generated_text': '房间:建筑风格比较独 特 , 墙 壁 由 黑 色 为 主 , 给 人 一 种 温 馨 的 感 觉 , 房 间 内 少 点 什 么 装 饰 , 总 体 还 算 可 以 。 交 通 : 订 一 辆 出 租 车 , 一 天 之 内 送 完 了 , 一 天 后 再 打 车 , 车 子 要 走 到 春 熙 路 , 十 分 方 便'}] [{'generated_text': '房间:建筑风格比较独 特 , 比 较 特 别 的 是 窗 外 的 自 然 环 境 , 很 漂 亮 , 房 间 内 的 设 施 也 不 错 , 有 独 立 的 阳 台 , 所 谓 的 山 景 房 看 风 景 也 能 看 到 大 草 坪 和 远 处 的 大 海 。 服 务 : 因 为 我 和 的 朋 友 预 定 的 是 山'}] """
与贪婪搜索不同的是,集束搜索解码在每个时间步骤中保留几个假设,并最终选择对整个序列具有最高概率的假设。这具有识别高概率序列的优势,这些序列从较低概率的初始标记开始,会被贪婪搜索所忽略。
要启用这种解码策略,需要指定num_beams(又称要跟踪的假说数量)大于1。集束搜索通过在每个时间步保留最可能的 num_beams
个词,并从中最终选择出概率最高的序列来降低丢失潜在的高概率序列的风险。以 num_beams=2
为例:
最终得到:the dog has (0.4+0.9) > the nice woman (0.5+0.4)。
缺点:虽然结果比贪心搜索更流畅,但输出中仍然包含重复。
from transformers import AutoTokenizer, AutoModelForCausalLM, TextGenerationPipeline, pipeline tokenizer = AutoTokenizer.from_pretrained("./gpt2-chinese") model = AutoModelForCausalLM.from_pretrained("./gpt2-chinese") from datasets import load_dataset data_file = "./ChnSentiCorp_htl_all.csv" dataset = load_dataset("csv", data_files=data_file) dataset = dataset.filter(lambda x: x["review"] is not None) dataset = dataset["train"].train_test_split(0.2, seed=123) import random example = random.choice(dataset["train"]) # text = dataset["train"][0] text = example["review"] input_text = text[:10] print(input_text) # greedy search model.eval() with torch.no_grad(): encoding = tokenizer(input_text, return_tensors='pt', padding=False, add_special_tokens=False, return_token_type_ids=False, return_attention_mask=False,) generated_ids = model.generate(**encoding, max_length=100, eos_token_id=0, pad_token_id=0, do_sample=False, num_beams=4, ) generated_texts = tokenizer.batch_decode(generated_ids, skip_special_tokens=True) print(generated_texts) text_generator = TextGenerationPipeline(model, tokenizer) print(text_generator(input_text, max_length=100, eos_token_id=0, num_beams=4, do_sample=False, pad_token_id=0, )) generator = pipeline("text-generation", model=model, tokenizer=tokenizer) generation_config = { "max_length": 100, "eos_token_id": 0, "pad_token_id": 0, "num_beams": 4, "do_sample": False, } print(generator(input_text, **generation_config)) """ 酒店的整体服务意识相 ['酒 店 的 整 体 服 务 意 识 相 当 好 , 对 于 未 按 照 预 订 时 间 到 达 的 客 户 , 还 能 够 保 留 预 订 , 但 是 沟 通 技 巧 不 是 很 好 , 还 有 对 于 未 按 预 订 时 间 到 达 的 客 户 , 还 要 给 携 程 的 工 作 带 来 很 大 麻 烦 。'] [{'generated_text': '酒店的整体服务意识相 当 好 , 对 于 未 按 照 预 订 时 间 到 达 的 客 户 , 还 能 够 保 留 预 订 , 但 是 沟 通 技 巧 不 是 很 好 , 还 有 对 于 未 按 预 订 时 间 到 达 的 客 户 , 还 要 给 携 程 的 工 作 带 来 很 大 麻 烦 。'}] [{'generated_text': '酒店的整体服务意识相 当 好 , 对 于 未 按 照 预 订 时 间 到 达 的 客 户 , 还 能 够 保 留 预 订 , 但 是 沟 通 技 巧 不 是 很 好 , 还 有 对 于 未 按 预 订 时 间 到 达 的 客 户 , 还 要 给 携 程 的 工 作 带 来 很 大 麻 烦 。'}] """
顾名思义,这种解码策略结合了集束搜索和多指标采样。你需要指定num_beams大于1,并设置do_sample=True来使用这种解码策略。
from transformers import AutoTokenizer, AutoModelForCausalLM, TextGenerationPipeline, pipeline tokenizer = AutoTokenizer.from_pretrained("./gpt2-chinese") model = AutoModelForCausalLM.from_pretrained("./gpt2-chinese") from datasets import load_dataset data_file = "./ChnSentiCorp_htl_all.csv" dataset = load_dataset("csv", data_files=data_file) dataset = dataset.filter(lambda x: x["review"] is not None) dataset = dataset["train"].train_test_split(0.2, seed=123) import random example = random.choice(dataset["train"]) # text = dataset["train"][0] text = example["review"] input_text = text[:10] print(input_text) # greedy search model.eval() with torch.no_grad(): encoding = tokenizer(input_text, return_tensors='pt', padding=False, add_special_tokens=False, return_token_type_ids=False, return_attention_mask=False,) generated_ids = model.generate(**encoding, max_length=100, eos_token_id=0, pad_token_id=0, do_sample=True, num_beams=4, ) generated_texts = tokenizer.batch_decode(generated_ids, skip_special_tokens=True) print(generated_texts) text_generator = TextGenerationPipeline(model, tokenizer) print(text_generator(input_text, max_length=100, eos_token_id=0, num_beams=4, do_sample=True, pad_token_id=0, )) generator = pipeline("text-generation", model=model, tokenizer=tokenizer) generation_config = { "max_length": 100, "eos_token_id": 0, "pad_token_id": 0, "num_beams": 4, "do_sample": True, } print(generator(input_text, **generation_config)) """ ['酒 店 在 肇 庆 闹 市 区 , 但 交 通 非 常 方 便 , 酒 店 服 务 员 态 度 非 常 好 , 酒 店 硬 件 条 件 还 可 以 , 就 是 房 间 隔 音 效 果 非 常 不 好 , 隔 壁 的 电 视 声 音 、 走 廊 人 说 话 声 等 清 清 楚 楚 , 住 在 一 楼 还 能 听 到 隔 壁 房 间 的 电'] [{'generated_text': '酒店在肇庆闹市区,但 交 通 非 常 方 便 , 酒 店 服 务 态 度 很 好 , 房 间 干 净 整 洁 , 下 次 去 肇 庆 还 会 选 择 该 酒 店 。'}] [{'generated_text': '酒店在肇庆闹市区,但 交 通 非 常 方 便 , 酒 店 环 境 不 错 , 房 间 比 较 干 净 , 服 务 员 态 度 也 很 好 , 总 的 来 说 是 一 家 不 错 的 酒 店 。'}] """
多样化集束搜索解码策略是对集束搜索策略的扩展,可以生成更多样化的集束序列供人们选择。要了解它的工作原理,请参考《多样化集束搜索》https://arxiv.org/pdf/1610.02424.pdf: 从神经序列模型解码多样化的解决方案。这种方法有两个主要参数:num_beams和num_beam_groups。组的选择是为了确保它们与其他组相比有足够的区别,并在每个组内使用常规集束搜索。
from transformers import AutoTokenizer, AutoModelForCausalLM, TextGenerationPipeline, pipeline tokenizer = AutoTokenizer.from_pretrained("./gpt2-chinese") model = AutoModelForCausalLM.from_pretrained("./gpt2-chinese") from datasets import load_dataset data_file = "./ChnSentiCorp_htl_all.csv" dataset = load_dataset("csv", data_files=data_file) dataset = dataset.filter(lambda x: x["review"] is not None) dataset = dataset["train"].train_test_split(0.2, seed=123) import random example = random.choice(dataset["train"]) # text = dataset["train"][0] text = example["review"] input_text = text[:10] print(input_text) # greedy search model.eval() with torch.no_grad(): encoding = tokenizer(input_text, return_tensors='pt', padding=False, add_special_tokens=False, return_token_type_ids=False, return_attention_mask=False,) generated_ids = model.generate(**encoding, max_length=100, eos_token_id=0, pad_token_id=0, do_sample=False, num_beams=4, num_beam_groups=4, ) generated_texts = tokenizer.batch_decode(generated_ids, skip_special_tokens=True) print(generated_texts) text_generator = TextGenerationPipeline(model, tokenizer) print(text_generator(input_text, max_length=100, eos_token_id=0, num_beams=4, do_sample=False, pad_token_id=0, num_beam_groups=4, )) generator = pipeline("text-generation", model=model, tokenizer=tokenizer) generation_config = { "max_length": 100, "eos_token_id": 0, "pad_token_id": 0, "num_beams": 4, "do_sample": False, "num_beam_groups": 4, } print(generator(input_text, **generation_config)) """ 住过如此之多的如家酒 ['住 过 如 此 之 多 的 如 家 酒 店 , 这 一 家 是 最 差 的 , 服 务 差 , 房 间 老 旧 , 而 且 价 格 还 不 低 。 下 次 不 会 再 住 了 。'] [{'generated_text': '住过如此之多的如家酒 店 , 这 一 家 是 最 差 的 , 服 务 差 , 房 间 老 旧 , 而 且 价 格 还 不 低 。 下 次 不 会 再 住 了 。'}] [{'generated_text': '住过如此之多的如家酒 店 , 这 一 家 是 最 差 的 , 服 务 差 , 房 间 老 旧 , 而 且 价 格 还 不 低 。 下 次 不 会 再 住 了 。'}] """
num_return_sequences <= num_beams
采样意味着根据当前条件概率分布随机选择输出词 ,使用采样方法时文本生成本身不再是确定性的。对单词序列进行采样时的大问题: 模型通常会产生不连贯的乱码。可以设置top_k=0关闭采样。缓解这一问题的一个技巧是通过降低所谓的 softmax 的“温度”使分布 更陡峭。而降低“温度”,本质上是增加高概率单词的似然并降低低概率单词的似然。
将温度应用到于我们的例子中后,结果如下图所示。
时刻单词的条件分布变得更加陡峭,几乎没有机会选择单词 “car” 了。虽然温度可以使分布的随机性降低,但极限条件下,当“温度”设置为 0 时,温度缩放采样就退化成贪心解码了,因此会遇到与贪心解码相同的问题。
在 Top-K 采样中,概率最大的 K 个词会被选出,然后这 K 个词的概率会被重新归一化,最后就在这重新被归一化概率后的 K 个词中采样。 GPT2 采用了这种采样方案,这也是它在故事生成这样的任务上取得成功的原因之一。
假设:top_k=6
输入:the, the的下一个词从概率最大的top6里面采样到car,the car的下一个词从概率最大的top6里面采样。可以看到后面一些奇怪的词就可以被忽略掉。
在 Top-p 中,采样不只是在最有可能的 K 个单词中进行,而是在累积概率超过概率 p 的最小单词集中进行。然后在这组词中重新分配概率质量。这样,词集的大小 (又名 集合中的词数) 可以根据下一个词的概率分布动态增加和减少。好吧,说的很啰嗦,一图胜千言。
假设 p=0.92 , Top-p 采样对单词概率进行降序排列并累加,然后选择概率和首次超过 p=0.92 的单词集作为采样池,可以看出,在单词比较不可预测时,它保留了更多的候选词。而当单词似乎更容易预测时,只保留了几个候选词。
一般而言,结合top_k和top_p会有不错的效果。
Text generation strategies (huggingface.co)
transformers/configuration_utils.py at v4.28.1 · huggingface/transformers · GitHub
transformers/text_generation.py at v4.28.1 · huggingface/transformers · GitHub
基于 transformers 的 generate() 方法实现多样化文本生成:参数含义和算法原理解读_transformers generate_木尧大兄弟的博客-CSDN博客
https://zhuanlan.zhihu.com/p/624636122
文中部分文字和图摘自上述文章。