大模型推理:
目前在项目中试验了多种大模型:llama,vicuna,chatglm,ziya,baichuan,以及模型微调+模型推理。总的来讲目前在模型推理方面还有很大的优化空间,比如:因为没有beam-search的缘故,流式输出的效果不如直接输出。所以目前优先考虑优化模型推理方面的效果。
transformer中对于推理的方式有以下这些,逐一了解下:
Greedy Search
“直接选概率最大的词”称为"Greedy Search"策略,也就是在每个时间步t,总是选择最高概率的词作为下一个词。
这个策略实现起来非常简单,但是对应的缺点也很明显:
- 第一,因为每次只是选取概率最高的词,因此生成的文本是完全确定的,即不具有任何多样性,但是现实场景的需求,通常需要这种多样性,例如客户要求对一个给定商品,生成10条不同的文案供他挑选。
- 第二,greedy search是一个贪心且短视的策略,通常不是全局最优的,例如在上图中,如果第二个词选择概率第二高的"dog",而第三个词选择"has",则最终生成文本序列为"The dog has",对应的联合概率为0.4*0.9=0.36,显然好于greedy search生成的"The nice woman"。
在transformer库中的generate方法默认使用的策略就是greedy search,调用时不传任何参数即可,或者同时指定do_sample=False, num_beams=1
。示例代码如下:
from transformers import AutoModelForCausalLM, AutoTokenizer
prompt = "I look forward to"
checkpoint = "distilgpt2"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
inputs = tokenizer(prompt, return_tensors="pt")
model = AutoModelForCausalLM.from_pretrained(checkpoint)
for _ in range(5):
outputs = model.generate(**inputs, do_sample=False, num_beams=1)
print(tokenizer.batch_decode(outputs, skip_special_tokens=True))
Beam Search
前面提到,greedy search是一个贪心且短视的策略,而beam search就是针对这一点做出的改进策略。它的基本思想是:在每一个时间步t,都保留多个不同的候选结果,直到最后一步才确定到底哪一个候选结果更为占优。候选结果的数量就是参数"num_beams"。
首先,第一个词“The”是已经确定的,在下一个词,我们并不只选择当前概率最高的"nice",同时还要把概率次高的“dog“也记录下作为候选结果,此时记录的两个候选结果为:
- (The, nice): 0.5
- (The, dog): 0.4
再接下来,两个候选结果再继续选择各自概率top2高的词(因为“The nice house”和“The nice guy”同分,“The dog runs”和“The dog and”同分,实际这里选的是top3高的词),记录为:
- (The, nice, woman): 0.5*0.4=0.2
- (The, nice, horse): 0.5*0.3=0.15
- (The, nice, guy): 0.5*0.3=0.15
- (The, dog, has): 0.4*0.9=0.36
- (The, dog, and): 0.4*0.05=0.02
- (The, dog, runs): 0.4*0.05=0.02
因为num_beams=2,这里仍然只保留联合概率top2的序列作为候选:
- (The, dog, has): 0.4*0.9=0.36
- (The, nice, woman): 0.5*0.4=0.2
如果还没到终止条件,beam search会重复上述过程继续执行下去,如果已经到了最大长度,则会选取当前分数最高的(The, dog, has)进行输出。
显然,beam search生成过程中的计算量大于greedy search,且beam search生成的文本序列的概率至少不会低于greedy search,也就是效果一定不差于greedy search。但是要注意,beam search生成的序列仍然不保证概率上是全局最优的!
在transformer库中使用beam search,需要指定do_sample=False且num_beams>1`。示例代码如下:
from transformers import AutoModelForCausalLM, AutoTokenizer
prompt = "I look forward to"
checkpoint = "distilgpt2"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
inputs = tokenizer(prompt, return_tensors="pt")
model = AutoModelForCausalLM.from_pretrained(checkpoint)
for _ in range(5):
outputs = model.generate(**inputs, do_sample=False, num_beams=20)
print(tokenizer.batch_decode(outputs, skip_special_tokens=True))
Multinomial Sampling
目前为止的两种解码策略都只能生成确定性的结果,而Multinomial Sampling则是一种不确定的解码策略,可以提供文本生成的多样性。它的基本思想是:在生成下一个候选词时,选定一个采样范围进行随机采样,在采样范围的词只要概率不为0,就有可能采样到这个词。在transformers库中,只需要设置num_beams=1且do_sample=True,就会进入Multinomial Sampling策略。
Top-K Sampling
Top-K Sampling的思路为:当要生成下一个词的时候,只截断选取概率最高的前K个词,对这K个词的概率重新进行归一化,然后在其中进行采样。 在transformers中使用Top-K Sampling的方法如下:
outputs = model.generate(**inputs, do_sample=True, num_beams=1, top_k=5)
当top_k=2时的输出如下:
['I look forward to seeing you again!\n\n\n\n\nAdvertisements']
['I look forward to the next chapter in the series.']
['I look forward to seeing you all again.›\n\n\n※\n※\n']
['I look forward to the next chapter in my life.›\nAdvertisements']
['I look forward to seeing you all in the future!']
当top_k=10时的输出如下:
['I look forward to the next day!›\n\n\n›\n※\n�']
['I look forward to seeing her in the future and I hope you all enjoy it.”']
['I look forward to working closely with your friends!\n\n\n\n\n\n\n\n\n\n']
['I look forward to working with you, my son-in-law and my great-grand-']
['I look forward to a new chapter in the history of the game.\n\nAdvertisements']
可以看到,选取的K越大,生成文本的多样性越强;选取的K越小,生成文本的多样性越小;进一步,当K=1时,多样性最弱,这个方法就退化为greedy search。
Top-P(nucleus) Sampling
Top-P Sampling的思路为:当要生成下一个词的时候,只截断选取从高到低累积概率达到top_p的词,对这些词的概率重新进行归一化,然后在其中进行采样。 在transformers中使用Top-P Sampling的方法如下(需要设置top_k=0来禁用Top-K Sampling):
outputs = model.generate(**inputs, do_sample=True, num_beams=1, top_p=0.5, top_k=0)
当top_p=0.1时的输出如下:
['I look forward to seeing you all again!\n\n\n\n\n\n\n\n\n\n\n']
['I look forward to seeing you all again!\n\n\n\n\n\n\n\n\n\n\n']
['I look forward to seeing you all again!\n\n\n\n\n\n\n\n\n\n\n']
['I look forward to seeing you all again!\n\n\n\n\n\n\n\n\n\n\n']
['I look forward to seeing you all again!\n\n\n\n\n\n\n\n\n\n\n']
当top_p=0.5时的输出如下:
['I look forward to seeing you in a much bigger role.›']
['I look forward to seeing you in New York City.\n\n\n\n\n\n\n\n\n']
['I look forward to the release of my new book, Surgical Confinement: The Ancient Egyptian History']
['I look forward to hearing from you!\n\n\n-Stuart\nAdvertisements']
['I look forward to the next year.›\n\n\n\n\n\n\n\n\n\n']
可以看到,选取的top_p越小,生成文本的多样性越弱;选取的top_p越大,生成文本的多样性越强;进一步,当top_p=1时,多样性最强,此时对词表范围内的所有词进行采样(人人都有机会:)。
到目前为止的策略,可以分为两类:
- 确定性策略,如greedy search/beam search,这种策略在生成的文本上具有较好的语义连贯性,但是不具备多样性的生成能力,同时存在重复生成的风险。
- 随机采样策略,如top-k sampling/top-p sampling,这种策略可以生成多样性的文本,但是在语义连贯性上无法保证。
因此,在这两类解码策略之外,通常以做到“语义连贯性”和“生成多样性”兼得为目标来设计一些新的解码策略。
Beam-search multinomial sampling
故名思义,Beam-search multinomial sampling结合了beam search和multinomial sampling两种策略。不难想到,此类策略最简单的实现方式就是在beam search每一步,不在每一个beam中按照概率最大化选词,而是按照概率分布采样选词。
Contrastive search
Contrastive search策略来自2022年的论文“ A Contrastive Framework for Neural Text Generation”,这个方法综合考虑了top-k sampling/惩罚重复生成/序列联合概率等多个因素,在生成不重复同时语义连贯的长文本上展示了较好的效果。
Diverse beam search decoding
Diverse beam search decoding来自2016年的论文“Diverse Beam Search: Decoding Diverse Solutions from Neural Sequence Models”,它在beam search的基础上提出了beam group的概念,也是希望在保留beam search语义连贯的基础上做到多样性。