当前位置:   article > 正文

NLP大规模语言模型推理实战:大语言模型BLOOM推理工具测试实践与效果分析实录...

bloom 7b

来自:老刘说NLP

进NLP群—>加入NLP交流群

LLM(大语言模型)推理存在两个两个问题:(1) 单张显卡无法容纳整个模型;(2) 推理速度太慢。针对这类问题,本文初步整理了一些推理大模型的工具和代码,并简单测试了推理速度。

下面是本文测试的一些背景:目前是使用7B模型bloom-7b1-mt、4张3090(但在实际推理中仅使用2张3090)、依赖包的版本:transformers==4.26.0、tensor-parallel==1.0.24、deepspeed==0.7.7、bminf==2.0.1

其中,BLOOM是由HuggingFace推出的大模型,其参数量达到176B(GPT-3是175B)。目前超过100B参数量且能够支持中文的开源大模型只有BLOOM和GLM-130B。由于HuggingFace是著名开源工具Transformers的开发公司,很多推理工具都会支持Transformers中的模型。

在此背景下,我们得到的结论:DeepSpeed-Inference的速度是最快的;张量并行比自带的层并行快一些;8 bit量化虽然速度慢一些,但是能够实现单卡推理;BMInf虽然速度最慢,但是其可能在不损失模型精度的情况下,单卡推理。供大家一起参考。

一、辅助函数

  1. # utils.py
  2. import numpy as np
  3. from time import perf_counter
  4. def measure_latency(model, tokenizer, payload, device, generation_args={}):
  5.     input_ids = tokenizer(payload, return_tensors="pt").input_ids.to(device)
  6.     latencies = []
  7.     # 预热
  8.     for _ in range(2):
  9.         _ =  model.generate(input_ids, **generation_args)
  10.     # 统计时间
  11.     for _ in range(10):
  12.         start_time = perf_counter()
  13.         _ = model.generate(input_ids, **generation_args)
  14.         latency = perf_counter() - start_time
  15.         latencies.append(latency)
  16.     # 计算统计量
  17.     time_avg_ms = 1000 * np.mean(latencies) # 延时均值
  18.     time_std_ms = 1000 * np.std(latencies) # 延时方差
  19.     time_p95_ms = 1000 * np.percentile(latencies,95) # 延时的95分位数
  20.     return f"P95延时 (ms) - {time_p95_ms}; 平均延时 (ms) - {time_avg_ms:.2f} +\- {time_std_ms:.2f};"
  21. def infer(model, tokenizer, payload, device):
  22.     input_ids = tokenizer(payload, return_tensors="pt").input_ids.to(device)
  23.     logits = model.generate(input_ids, num_beams=1, max_length=512)
  24.     out = tokenizer.decode(logits[0].tolist())
  25.     return out

二、层并行

BLOOM是Huggingface开发的,所以在transformers库中提供了支持。具体来说,在使用from_pretrained加载模型时,指定参数devce_map即可。其通过将模型的不同层放置在不同的显卡上,从而将单个大模型分拆至多张卡上(流水线并行也会将层分拆,然后采用流水线的方式训练模型)。下面是调用的示例代码:

  1. # layer_parallel_test.py
  2. import os
  3. import transformers
  4. from utils import measure_latency, infer
  5. from transformers import AutoTokenizer, AutoModelForCausalLM
  6. transformers.logging.set_verbosity_error()
  7. os.environ['CUDA_VISIBLE_DEVICES'] = "0,1"
  8. def run():
  9.     model_name = "bigscience/bloomz-7b1-mt"
  10.     payload = """
  11.     参考下面的文章,然后用与文章相同的语言回答问题: 段落:当细菌突破免疫系统的防御而开始增生时,疾病会由结核菌感染进展到症状明显的结核病。在原发型结核病 (占 1-5% 的比例),这种现象会在感染刚开始的时候很快的发生。然而>多数人感染模式为潜伏结核感染,通常没有明显症状。在5-10%潜伏结合感染的案例中,这些休眠的细菌经常会在感染后数年的时间制造出活动的结核。 问题:What is the next stage after TB infection?
  12.     """
  13.     tokenizer = AutoTokenizer.from_pretrained(model_name)
  14.     model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto")
  15.     model = model.eval()
  16.     out = infer(model, tokenizer, payload, model.device)
  17.     print("="*70+" 模型输入输出 "+"="*70)
  18.     print(f"模型输入: {payload}")
  19.     print(f"模型输出: {out}")
  20.     print("\n\n"+"="*70+" 模型延时测试 "+"="*70)
  21.     print(measure_latency(model, tokenizer, payload, model.device))
  22.     print("\n\n"+"="*70+" 显存占用 "+"="*70)
  23.     print(os.system("nvidia-smi"))
  24. if __name__ == "__main__":
  25.     run()
  26.     pass

模型的时延结果:P95延时 (ms) - 118.402308691293; 平均延时 (ms) - 117.72 +- 0.58;显存占用:7bab91b134d20180abbd7061e6d21351.jpeg

三、张量并行

张量并行是将矩阵乘法进行分块,从而将大矩阵拆分为更小的矩阵,这样就能把不同的矩阵放置在不同的显卡上。(具体原理会在后续的文章中介绍) 这里使用开源工具包tensor_parallel来实现。

  1. # tensor_parallel_test.py
  2. import os
  3. import transformers
  4. import tensor_parallel as tp
  5. from utils import measure_latency, infer
  6. from transformers import AutoTokenizer, AutoModelForCausalLM
  7. transformers.logging.set_verbosity_error()
  8. os.environ['CUDA_VISIBLE_DEVICES'] = "0,1"
  9. def run():
  10.     model_name = "bigscience/bloomz-7b1-mt"
  11.     payload = """
  12.     参考下面的文章,然后用与文章相同的语言回答问题: 段落:当细菌突破免疫系统的防御而开始增生时,疾病会由结核菌感染进展到症状明显的结核病。在原发型结核病 (占 1-5% 的比例),这种现象会在感染刚开始的时候很快的发生。然而>多数人感染模式为潜伏结核感染,通常没有明显症状。在5-10%潜伏结合感染的案例中,这些休眠的细菌经常会在感染后数年的时间制造出活动的结核。 问题:What is the next stage after TB infection?
  13.     """
  14.     tokenizer = AutoTokenizer.from_pretrained(model_name)
  15.     model = AutoModelForCausalLM.from_pretrained(model_name, low_cpu_mem_usage=True)
  16.     model = tp.tensor_parallel(model, ["cuda:0""cuda:1"])
  17.     model = model.eval()
  18.     out = infer(model, tokenizer, payload, model.device)
  19.     print("="*70+" 模型输入输出 "+"="*70)
  20.     print(f"模型输入: {payload}")
  21.     print(f"模型输出: {out}")
  22.     print("\n\n"+"="*70+" 模型延时测试 "+"="*70)
  23.     print(measure_latency(model, tokenizer, payload, model.device))
  24.     print("\n\n"+"="*70+" 显存占用 "+"="*70)
  25.     print(os.system("nvidia-smi"))
  26. if __name__ == "__main__":
  27.     run()
  28.     pass

模型的时延结果:P95延时 (ms) - 91.34029923006892; 平均延时 (ms) - 90.66 +- 0.46;显存占用:327396ed8318f3fecb6987ac379535af.jpeg

四、模型量化

量化是一种常见的模型压缩技术,我们在之前的文章中介绍了用于大型Transformer的8-bit矩阵乘法,其核心思想是将模型参数从高精度转换为低精度。

在BLOOM上使用8-bit量化只需要在调用from_pretrained时,设置参数load_in_8bit=True, device_map="auto"。

(注:bloom在实现量化时,会按照是否超越阈值来分拆矩阵,然后对低于阈值的模型参数进行量化,这会拖慢推理速度)

  1. # int8_test.py
  2. import os
  3. import transformers
  4. from utils import measure_latency, infer
  5. from transformers import AutoTokenizer, AutoModelForCausalLM
  6. transformers.logging.set_verbosity_error()
  7. os.environ['CUDA_VISIBLE_DEVICES'] = "0,1"
  8. def run():
  9.     model_name = "bigscience/bloomz-7b1-mt"
  10.     payload = """
  11.     参考下面的文章,然后用与文章相同的语言回答问题: 段落:当细菌突破免疫系统的防御而开始增生时,疾病会由结核菌感染进展到症状明显的结核病。在原发型结核病 (占 1-5% 的比例),这种现象会在感染刚开始的时候很快的发生。然而>多数人感染模式为潜伏结核感染,通常没有明显症状。在5-10%潜伏结合感染的案例中,这些休眠的细菌经常会在感染后数年的时间制造出活动的结核。 问题:What is the next stage after TB infection?
  12.     """
  13.     max_memory_mapping = {0"24GB"1"0GB"}
  14.     tokenizer = AutoTokenizer.from_pretrained(model_name)
  15.     model = AutoModelForCausalLM.from_pretrained(model_name, load_in_8bit=True, device_map="auto", max_memory=max_memory_mapping)
  16.     model = model.eval()
  17.     out = infer(model, tokenizer, payload, model.device)
  18.     print("="*70+" 模型输入输出 "+"="*70)
  19.     print(f"模型输入: {payload}")
  20.     print(f"模型输出: {out}")
  21.     print("\n\n"+"="*70+" 模型延时测试 "+"="*70)
  22.     print(measure_latency(model, tokenizer, payload, model.device))
  23.     print("\n\n"+"="*70+" 显存占用 "+"="*70)
  24.     print(os.system("nvidia-smi"))
  25. if __name__ == "__main__":
  26.     run()
  27.     pass

模型的时延结果:P95延时 (ms) - 147.89210632443428; 平均延时 (ms) - 143.30 +- 3.02;显存占用:27ff5fde671461421a2cbcd1da44f19d.jpeg

五、DeepSpeed-Inference

DeepSpeed-Inference是分布式训练工具DeepSpeed中用户模型推理的功能。

  1. # deepspeed_test.py
  2. import os
  3. import torch
  4. import deepspeed
  5. import transformers
  6. from utils import measure_latency, infer
  7. from transformers import AutoTokenizer, AutoModelForCausalLM
  8. transformers.logging.set_verbosity_error()
  9. os.environ['CUDA_VISIBLE_DEVICES'] = "0,1"
  10. def run():
  11.     model_name = "bigscience/bloomz-7b1-mt"
  12.     payload = """
  13.     参考下面的文章,然后用与文章相同的语言回答问题: 段落:当细菌突破免疫系统的防御而开始增生时,疾病会由结核菌感染进展到症状明显的结核病。在原发型结核病 (占 1-5% 的比例),这种现象会在感染刚开始的时候很快的发生。然而>多数人感染模式为潜伏结核感染,通常没有明显症状。在5-10%潜伏结合感染的案例中,这些休眠的细菌经常会在感染后数年的时间制造出活动的结核。 问题:What is the next stage after TB infection?
  14.     """
  15.     tokenizer = AutoTokenizer.from_pretrained(model_name)
  16.     model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype=torch.float16)
  17.     model = deepspeed.init_inference(
  18.             model=model,      # Transformers模型
  19.             mp_size=2,        # 模型并行数量
  20.             dtype=torch.float16, # 权重类型(fp16)
  21.             replace_method="auto", # 让DS自动替换层
  22.             replace_with_kernel_inject=True, # 使用kernel injector替换
  23.             )
  24.     out = infer(model, tokenizer, payload, model.module.device)
  25.     print("="*70+" 模型输入输出 "+"="*70)
  26.     print(f"模型输入: {payload}")
  27.     print(f"模型输出: {out}")
  28.     print("\n\n"+"="*70+" 模型延时测试 "+"="*70)
  29.     print(measure_latency(model, tokenizer, payload, model.module.device))
  30.     print("\n\n"+"="*70+" 显存占用 "+"="*70)
  31.     print(os.system("nvidia-smi"))
  32. if __name__ == "__main__":
  33.     run()
  34.     pass

这里不能使用python来自动脚本,需要使用下面的命令:deepspeed --num_gpus 2 --master_port 60000 deepspeed_test.py 模型的时延结果:P95延时 (ms) - 31.88958093523979; 平均延时 (ms) - 30.75 +- 0.64;显存占用:640cf28b2e6b7f87ece50fe15a3e44c5.jpeg

六、BMInf BMInf能够在单张显卡下加载完整的模型,但是推理速度非常慢(应该是利用了Offload技术)。

  1. import os
  2. import bminf
  3. import transformers
  4. from utils import measure_latency, infer
  5. from transformers import AutoTokenizer, AutoModelForCausalLM
  6. transformers.logging.set_verbosity_error()
  7. os.environ['CUDA_VISIBLE_DEVICES'] = "0,1"
  8. def run():
  9.     model_name = "bigscience/bloomz-7b1-mt"
  10.     payload = """
  11.     参考下面的文章,然后用与文章相同的语言回答问题: 段落:当细菌突破免疫系统的防御而开始增生时,疾病会由结核菌感染进展到症状明显的结核病。在原发型结核病 (占 1-5% 的比例),这种现象会在感染刚开始的时候很快的发生。然而>多数人感染模式为潜伏结核感染,通常没有明显症状。在5-10%潜伏结合感染的案例中,这些休眠的细菌经常会在感染后数年的时间制造出活动的结核。 问题:What is the next stage after TB infection?
  12.     """
  13.     tokenizer = AutoTokenizer.from_pretrained(model_name)
  14.     model = AutoModelForCausalLM.from_pretrained(model_name, low_cpu_mem_usage=True)
  15.     model = model.eval()
  16.     model = bminf.wrapper(model, quantization=False, memory_limit=8 << 30)
  17.     out = infer(model, tokenizer, payload, model.device)
  18.     print("="*70+" 模型输入输出 "+"="*70)
  19.     print(f"模型输入: {payload}")
  20.     print(f"模型输出: {out}")
  21.     print("\n\n"+"="*70+" 模型延时测试 "+"="*70)
  22.     print(measure_latency(model, tokenizer, payload, model.device))
  23.     print("\n\n"+"="*70+" 显存占用 "+"="*70)
  24.     print(os.system("nvidia-smi"))
  25. if __name__ == "__main__":
  26.     run()
  27.     pass

模型的时延结果:P95延时 (ms) - 719.2403690889478; 平均延时 (ms) - 719.05 +- 0.14;显存占用:

762d4afbaf14a20f3f2350771b7629f2.jpeg

六、总结

LLM(大语言模型)推理的两个问题,包括单张显卡无法容纳整个模型,推理速度太慢,通过对比实验,我们总结出了一些结论:
DeepSpeed-Inference的速度是最快的;张量并行比自带的层并行快一些;8 bit量化虽然速度慢一些,但是能够实现单卡推理;BMInf虽然速度最慢,但是其可能在不损失模型精度的情况下,单卡推理;
不过,但这也说明本文并不是这些推理工具的最佳实践,仅是罗列和展示这些工具如何使用;这些工具从不同的角度来优化模型推理,对于希望进一步了解具体如何实现的人来说,可以阅读源代码;

参考文献

1、https://zhuanlan.zhihu.com/p/608004441

进NLP群—>加入NLP交流群

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号