当前位置:   article > 正文

LLM - model batch generate 生成文本_repetition_penalty

repetition_penalty

一.引言

LLM model 类 generate 支持传递 num_return_sequences 进行批量生成,下面简单介绍下原始模型 generate 和 lora 模型 generate 的代码并给出基于 Baichuan-7B 和 ChatGLM 的批量预测效率。

二.generate 参数

介绍 batch generate 之前,先熟悉下 generate 的几个参数。

input_ids

输入的 token 序列索引,它是将输入文本转换为模型可理解的数值表示的结果,generate 会根据该 ids 进行后续的 generate,生成后通过 tokenizer 进行反 token 即可得到文本结果。

max_length

生成文本的最大长度限制。

temperature

控制生成的随机性,较高的值会产生更多样化的输出。

top_k

控制模型生成过程中考虑的词汇范围,只从概率最高的 k 个候选词中选择。

top_p

控制模型生成过程中考虑的词汇范围,使用累计概率选择候选词,知道累计概率超过给定的阈值。该参数也可以控制生成结果的多样性,它基于累积概率选择候选词,直到累计概率超过给定的阈值为止。以下是 top_p 的工作原理:

        - 模型为每个候选词生成概率分布,表示该词被选择的可能性

        - 按照概率降序对词汇表进行排序

        - 从概率最高的词开始累积,直到累积概率超过给定阈值,一般为 0.8、0.9

        - 在累积概率超过阈值后,选择此时词汇表中的词为候选词,其余的舍弃

top_p 采样的好处在于根据上下文动态生成结果的多样性,如果上下文的概率分布比较平摊,则更多的词会保留在候选集中,增加多样性;而如果上下文中的概率分布比较尖锐,那么候选集会变小,生成的结果则集中在概率较高的词上。使用较低的阈值会获得相对保守的结果,选择较高的阈值则会产生更多样化的结果。

num_beams

使用 beam search 时同时保留的可能序列的数量。当使用 beam search 束搜索算法进行文本生成时,num_beams 参数用于控制保留可能的序列数量。它决定了在生成过程中多少个假设序列被保留下来。以下是 num_beams 的工作原理:

        - 在初始状态下,模型生成一个起始序列

        - 每个候选序列都会考虑从当前位置生成的所有可能的下一个词,并计算每个词的概率

        - 根据这些概率,根据 beam search 的规则,选择最有可能的 num_beams 个序列作为下一个候选

        - 重复上述步骤,知道达到指定的生成长度或满足停止条件

通过使用 beam search,可以生成多个候选序列,每个序列都代表着一种可能的生成结果。num_beams 参数决定了同时保留的候选序列的数量。较大的 num_beams 会产生更多的候选序列,但也会增加计算开销。较小的值可能导致模型陷入局部最优解。

repetition_penalty

repetition_penalty(重复惩罚)是一种技术,用于减少在文本生成过程中出现重复片段的概率。它对之前已经生成的文本进行惩罚,使得模型更倾向于选择新的、不重复的内容。以下是 repetition_penalty 的工作原理:

        - 在生成每个新词的候选列表时,模型会计算每个候选词的分数。

        - 如果一个候选词已经在之前的生成结果中出现过,并且其重复次数超过了预设的阈值,那么该候选词的分数将被惩罚。

        - 惩罚的方式可以是减少候选词的分数,或者增加候选词的成本。这样就降低了模型选择重复词汇的可能性。

        - 通过施加重复惩罚,模型被鼓励尝试生成与之前生成内容不同的词汇,从而增加生成结果的多样性和连贯性。

重复惩罚参数的具体取值会影响到模型对重复片段的敏感度。较高的重复惩罚值会更严厉地限制重复内容,有助于产生更多样化的生成结果。较低的重复惩罚值则会允许一定程度的重复。

length_penalty

控制生成结果长度的惩罚或奖励。长度惩罚参数的实际取值会影响到模型对生成结果长度的偏好程度。较高的长度惩罚值会更严厉地限制生成结果的长度,有助于产生更为紧凑的输出。较低的长度惩罚值则会允许生成较长的结果。

no_repeat_ngram_size

防止重复n-gram片段出现在生成结果中。

max_length

用于控制生成文本的最大长度。

max_new_tokens

允许的最大新标记数目。具体而言,"max_new_tokens" 参数控制从模型生成的文本中添加到最终输出中的最大词汇数量。通过设置这个参数,您可以确保生成结果不会过于冗长或超出预期的长度。

do_sample

使用采样策略,当设置 do_sample = True 时,生成过程将采用随机采样策略。在生成每个新词时,模型会根据词汇表中词汇的概率分布进行随机采样,从而选择下一个词。采样的随机性使得生成的文本更加多样化,且可能会产生更为创造性的结果。

num_return_sequences

返回多个生成序列。用于根据给定的 input_ids,一次生成多个候选输出。批量输出就是用该参数控制。

三.batch generate

  1. from peft import PeftModel
  2. from transformers import AutoTokenizer, AutoModel, AutoModelForCausalLM
  3. import torch
  4. import time
  5. def cost(st, end):
  6. # 转换为 ms
  7. return (end - st) * 1000
  8. # 加载原始 LLM
  9. model_path = "/model/ChatGLM-6B/chatglm-6b/"
  10. device = torch.device(0)
  11. model = AutoModel.from_pretrained(model_path, load_in_8bit=False, trust_remote_code=True).half().to(device)
  12. tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
  13. # 原始 LLM 安装上 Lora 模型
  14. lora_model = PeftModel.from_pretrained(model, "weights/simple_test_by_chatglm").half()
  15. print("Warm Up Start...")
  16. inputs = tokenizer("你好" + "\n", return_tensors='pt')
  17. inputs = inputs.to('cuda:0')
  18. pred = model.generate(**inputs, max_new_tokens=512, do_sample=True)
  19. pred = lora_model.generate(**inputs, max_new_tokens=512, do_sample=True)
  20. print("Warm Up End...")
  21. epoch=0
  22. max_epoch = 100
  23. batch_num = 10
  24. ori_all_cost = 0
  25. lora_all_cost = 0
  26. while True:
  27. time_st = time.time()
  28. #inputText = input("请输入信息 [输入'q'退出]\n")
  29. inputText = "请计算:39 * 0 = 什么?"
  30. if inputText == 'q':
  31. print("Exit!")
  32. break
  33. if epoch >= max_epoch:
  34. break
  35. #batch_num = int(input("请输入batch_num\n"))
  36. inputs = tokenizer(inputText + "\n", return_tensors='pt')
  37. inputs = inputs.to('cuda:0')
  38. time_token = time.time()
  39. ori_pred = model.generate(**inputs, max_new_tokens=512, do_sample=True, num_return_sequences=batch_num)
  40. # print("原始输出")
  41. for i in range(len(ori_pred)):
  42. ori_answer = tokenizer.decode(ori_pred.cpu()[i], skip_special_tokens=True)
  43. # print(ori_answer.strip())
  44. time_ori = time.time()
  45. lora_pred = lora_model.generate(**inputs, max_new_tokens=512, do_sample=True, num_return_sequences=batch_num)
  46. # print("Lora输出")
  47. for i in range(len(lora_pred)):
  48. lora_answer = tokenizer.decode(lora_pred.cpu()[i], skip_special_tokens=True)
  49. # print(lora_answer.strip())
  50. time_lora = time.time()
  51. print("Total Cost: %s Token Cost: %s Ori Cost: %s Lora Cost: %s" % (cost(time_st, time_lora), cost(time_st, time_token), cost(time_token, time_ori), cost(time_ori, time_lora)))
  52. ori_all_cost += cost(time_token, time_ori)
  53. lora_all_cost += cost(time_ori, time_lora)
  54. epoch += 1
  55. ori_mean = ori_all_cost / max_epoch
  56. lora_mean = lora_all_cost / max_epoch
  57. print("Total Epoch: %s Batch Num: %s Ori All: %s Lora All: %s Ori Mean: %s Lora Mean: %s" %(str(max_epoch), str(batch_num), str(ori_all_cost), str(lora_all_cost), str(ori_mean), str(lora_mean)))

这里分别加载了原始的 ChatGLM 和 lora 后的 ChatGLM 进行 batch generate 的时间测试。

ChatGLM-bB 不同 Batch 耗时

Baichuan-7B 不同 Batch 耗时 

◆ 结论

- 生成多条会带来额外的时间开销,但开销并非线性

- 小模型情况下 lora 预测并不会带来特别大的额外推理开销

后补 batch_decode

上面针对 model 预测出的多条结果,我们采用 for 循环然后用 tokenizer 依次 decode 的方式转换回答结果,下面给出更加快捷的 batch_decode 方法:

  1. with open(input_path, 'r') as f:
  2. for question in f.readlines():
  3. input_ids = tokenizer(question, return_tensors="pt")['input_ids'].to(device)
  4. input_ids = torch.repeat_interleave(input_ids, int(args.limit), dim=0)
  5. output_ids = model.generate(input_ids, gen_config)
  6. input_id_token_num = input_ids[0].shape[0]
  7. output_id_token_num = output_ids.shape[1]
  8. if output_id_token_num > input_id_token_num:
  9. res = tokenizer.batch_decode(output_ids[:, input_id_token_num:], skip_special_tokens=True)
  10. for answer in res:
  11. # 获取推理结果
  12. result.append([question, answer])

上面的逻辑为读文件批量预测:

处理 question => tokenizer 将 question 转换为 input_ids 的情况

repeat => 将 input_ids 重复多次,次数由 limit 决定

generate => model.generate 生成多条结果

batch_decode => 批量解码结果,不再使用 for 循环

四.总结

上面简单测试了 batch generate 的 demo 和效率,除此之外熟悉了 generate 的生成参数,以下参数调整可以控制多样性:

        temperature - 越高随机性越强

        top_k - 越大多样性越强

        top_p - 越高多样性越强

        do_sample - True 提高随机性

针对多个控制参数,我们可以统一放到 dict 中,然后 **kwargs 传入 generate:

  1. # Token 生成配置
  2. generation_config = dict(
  3. temperature=0.5,
  4. top_k=30,
  5. top_p=0.9,
  6. do_sample=True,
  7. num_beams=1,
  8. repetition_penalty=1.3,
  9. max_new_tokens=400
  10. )
  11. ... 此处省略模型加载等过程 ...
  12. # 批量生成
  13. pred = model.generate(
  14. input_ids = inputs["input_ids"].to(device),
  15. eos_token_id=tokenizer.eos_token_id,
  16. pad_token_id=tokenizer.pad_token_id,
  17. num_return_sequences=batch_num,
  18. **generation_config
  19. )
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/519764
推荐阅读
相关标签
  

闽ICP备14008679号