使用Accelerate库在多GPU上进行LLM推理

本文涉及的产品
实时数仓Hologres,5000CU*H 100GB 3个月
智能开放搜索 OpenSearch行业算法版,1GB 20LCU 1个月
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: 大型语言模型(llm)已经彻底改变了自然语言处理领域。随着这些模型在规模和复杂性上的增长,推理的计算需求也显著增加。为了应对这一挑战利用多个gpu变得至关重要。

所以本文将在多个gpu上并行执行推理,主要包括:Accelerate库介绍,简单的方法与工作代码示例和使用多个gpu的性能基准测试。

本文将使用多个3090将llama2-7b的推理扩展在多个GPU上

基本示例

我们首先介绍一个简单的示例来演示使用Accelerate进行多gpu“消息传递”。

 from accelerate import Accelerator
 from accelerate.utils import gather_object

 accelerator = Accelerator()

 # each GPU creates a string
 message=[ f"Hello this is GPU {accelerator.process_index}" ] 

 # collect the messages from all GPUs
 messages=gather_object(message)

 # output the messages only on the main process with accelerator.print() 
 accelerator.print(messages)

输出如下:

 ['Hello this is GPU 0', 
   'Hello this is GPU 1', 
   'Hello this is GPU 2', 
   'Hello this is GPU 3', 
   'Hello this is GPU 4']

多GPU推理

下面是一个简单的、非批处理的推理方法。代码很简单,因为Accelerate库已经帮我们做了很多工作,我们直接使用就可以:

 from accelerate import Accelerator
 from accelerate.utils import gather_object
 from transformers import AutoModelForCausalLM, AutoTokenizer
 from statistics import mean
 import torch, time, json

 accelerator = Accelerator()

 # 10*10 Prompts. Source: https://www.penguin.co.uk/articles/2022/04/best-first-lines-in-books
 prompts_all=[
     "The King is dead. Long live the Queen.",
     "Once there were four children whose names were Peter, Susan, Edmund, and Lucy.",
     "The story so far: in the beginning, the universe was created.",
     "It was a bright cold day in April, and the clocks were striking thirteen.",
     "It is a truth universally acknowledged, that a single man in possession of a good fortune, must be in want of a wife.",
     "The sweat wis lashing oafay Sick Boy; he wis trembling.",
     "124 was spiteful. Full of Baby's venom.",
     "As Gregor Samsa awoke one morning from uneasy dreams he found himself transformed in his bed into a gigantic insect.",
     "I write this sitting in the kitchen sink.",
     "We were somewhere around Barstow on the edge of the desert when the drugs began to take hold.",
 ] * 10

 # load a base model and tokenizer
 model_path="models/llama2-7b"
 model = AutoModelForCausalLM.from_pretrained(
     model_path,    
     device_map={"": accelerator.process_index},
     torch_dtype=torch.bfloat16,
 )
 tokenizer = AutoTokenizer.from_pretrained(model_path)   

 # sync GPUs and start the timer
 accelerator.wait_for_everyone()
 start=time.time()

 # divide the prompt list onto the available GPUs 
 with accelerator.split_between_processes(prompts_all) as prompts:
     # store output of generations in dict
     results=dict(outputs=[], num_tokens=0)

     # have each GPU do inference, prompt by prompt
     for prompt in prompts:
         prompt_tokenized=tokenizer(prompt, return_tensors="pt").to("cuda")
         output_tokenized = model.generate(**prompt_tokenized, max_new_tokens=100)[0]

         # remove prompt from output 
         output_tokenized=output_tokenized[len(prompt_tokenized["input_ids"][0]):]

         # store outputs and number of tokens in result{}
         results["outputs"].append( tokenizer.decode(output_tokenized) )
         results["num_tokens"] += len(output_tokenized)

     results=[ results ] # transform to list, otherwise gather_object() will not collect correctly

 # collect results from all the GPUs
 results_gathered=gather_object(results)

 if accelerator.is_main_process:
     timediff=time.time()-start
     num_tokens=sum([r["num_tokens"] for r in results_gathered ])

     print(f"tokens/sec: {num_tokens//timediff}, time {timediff}, total tokens {num_tokens}, total prompts {len(prompts_all)}")

使用多个gpu会导致一些通信开销:性能在4个gpu时呈线性增长,然后在这种特定设置中趋于稳定。当然这里的性能取决于许多参数,如模型大小和量化、提示长度、生成的令牌数量和采样策略,所以我们只讨论一般的情况

1 GPU: 44个token /秒,时间:225.5s

2 gpu: 88个token /秒,时间:112.9s

3 gpu: 128个token /秒,时间:77.6s

4 gpu: 137个token /秒,时间:72.7s

5 gpu: 119个token /秒,时间:83.8s

在多GPU上进行批处理

现实世界中,我们可以使用批处理推理来加快速度。这会减少GPU之间的通讯,加快推理速度。我们只需要增加prepare_prompts函数将一批数据而不是单条数据输入到模型即可:

 from accelerate import Accelerator
 from accelerate.utils import gather_object
 from transformers import AutoModelForCausalLM, AutoTokenizer
 from statistics import mean
 import torch, time, json

 accelerator = Accelerator()

 def write_pretty_json(file_path, data):
     import json
     with open(file_path, "w") as write_file:
         json.dump(data, write_file, indent=4)

 # 10*10 Prompts. Source: https://www.penguin.co.uk/articles/2022/04/best-first-lines-in-books
 prompts_all=[
     "The King is dead. Long live the Queen.",
     "Once there were four children whose names were Peter, Susan, Edmund, and Lucy.",
     "The story so far: in the beginning, the universe was created.",
     "It was a bright cold day in April, and the clocks were striking thirteen.",
     "It is a truth universally acknowledged, that a single man in possession of a good fortune, must be in want of a wife.",
     "The sweat wis lashing oafay Sick Boy; he wis trembling.",
     "124 was spiteful. Full of Baby's venom.",
     "As Gregor Samsa awoke one morning from uneasy dreams he found himself transformed in his bed into a gigantic insect.",
     "I write this sitting in the kitchen sink.",
     "We were somewhere around Barstow on the edge of the desert when the drugs began to take hold.",
 ] * 10

 # load a base model and tokenizer
 model_path="models/llama2-7b"
 model = AutoModelForCausalLM.from_pretrained(
     model_path,    
     device_map={"": accelerator.process_index},
     torch_dtype=torch.bfloat16,
 )
 tokenizer = AutoTokenizer.from_pretrained(model_path)   
 tokenizer.pad_token = tokenizer.eos_token

 # batch, left pad (for inference), and tokenize
 def prepare_prompts(prompts, tokenizer, batch_size=16):
     batches=[prompts[i:i + batch_size] for i in range(0, len(prompts), batch_size)]  
     batches_tok=[]
     tokenizer.padding_side="left"     
     for prompt_batch in batches:
         batches_tok.append(
             tokenizer(
                 prompt_batch, 
                 return_tensors="pt", 
                 padding='longest', 
                 truncation=False, 
                 pad_to_multiple_of=8,
                 add_special_tokens=False).to("cuda") 
             )
     tokenizer.padding_side="right"
     return batches_tok

 # sync GPUs and start the timer
 accelerator.wait_for_everyone()    
 start=time.time()

 # divide the prompt list onto the available GPUs 
 with accelerator.split_between_processes(prompts_all) as prompts:
     results=dict(outputs=[], num_tokens=0)

     # have each GPU do inference in batches
     prompt_batches=prepare_prompts(prompts, tokenizer, batch_size=16)

     for prompts_tokenized in prompt_batches:
         outputs_tokenized=model.generate(**prompts_tokenized, max_new_tokens=100)

         # remove prompt from gen. tokens
         outputs_tokenized=[ tok_out[len(tok_in):] 
             for tok_in, tok_out in zip(prompts_tokenized["input_ids"], outputs_tokenized) ] 

         # count and decode gen. tokens 
         num_tokens=sum([ len(t) for t in outputs_tokenized ])
         outputs=tokenizer.batch_decode(outputs_tokenized)

         # store in results{} to be gathered by accelerate
         results["outputs"].extend(outputs)
         results["num_tokens"] += num_tokens

     results=[ results ] # transform to list, otherwise gather_object() will not collect correctly

 # collect results from all the GPUs
 results_gathered=gather_object(results)

 if accelerator.is_main_process:
     timediff=time.time()-start
     num_tokens=sum([r["num_tokens"] for r in results_gathered ])

     print(f"tokens/sec: {num_tokens//timediff}, time elapsed: {timediff}, num_tokens {num_tokens}")

可以看到批处理会大大加快速度。

1 GPU: 520 token /sec,时间:19.2s

2 gpu: 900 token /sec,时间:11.1s

3 gpu: 1205个token /秒,时间:8.2s

4 gpu: 1655 token /sec,时间:6.0s

5 gpu: 1658 token /sec,时间:6.0s

总结

截止到本文为止,llama.cpp,ctransformer还不支持多GPU推理,好像llama.cpp在6月有个多GPU的merge,但是我没看到官方更新,所以这里暂时确定不支持多GPU。如果有小伙伴确认可以支持多GPU请留言。

huggingface的Accelerate包则为我们使用多GPU提供了一个很方便的选择,使用多个GPU推理可以显着提高性能,但gpu之间通信的开销随着gpu数量的增加而显著增加。

https://avoid.overfit.cn/post/8210f640cae0404a88fd1c9028c6aabb

作者:Geronimo

相关实践学习
部署Stable Diffusion玩转AI绘画(GPU云服务器)
本实验通过在ECS上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。
目录
相关文章
|
24天前
|
机器学习/深度学习 自然语言处理 测试技术
CoT神话破灭,并非LLM标配!三大学府机构联手证实,CoT仅在数学符号推理有用
【10月更文挑战第17天】链式思维(CoT)曾被认为是大型语言模型(LLM)激发推理能力的关键方法,但最新研究显示,CoT仅在数学和符号推理任务中有效,其他任务中效果不明显。加州大学伯克利分校、斯坦福大学和卡内基梅隆大学的联合研究打破了CoT作为LLM标配的神话,为重新评估LLM的推理能力提供了新视角。
29 1
|
10天前
|
人工智能 自然语言处理
重要的事情说两遍!Prompt复读机,显著提高LLM推理能力
【10月更文挑战第30天】本文介绍了一种名为“问题重读”(Question Re-reading)的提示策略,旨在提高大型语言模型(LLMs)的推理能力。该策略受人类学习和问题解决过程的启发,通过重新审视输入提示中的问题信息,使LLMs能够提取更深层次的见解、识别复杂模式,并建立更细致的联系。实验结果显示,问题重读策略在多个推理任务上显著提升了模型性能。
24 2
|
20天前
|
JSON 人工智能 算法
探索LLM推理全阶段的JSON格式输出限制方法
文章详细讨论了如何确保大型语言模型(LLMs)输出结构化的JSON格式,这对于提高数据处理的自动化程度和系统的互操作性至关重要。
|
20天前
|
人工智能 运维 Serverless
Serverless GPU:助力 AI 推理加速
近年来,AI 技术发展迅猛,企业纷纷寻求将 AI 能力转化为商业价值,然而,在部署 AI 模型推理服务时,却遭遇成本高昂、弹性不足及运维复杂等挑战。本文将探讨云原生 Serverless GPU 如何从根本上解决这些问题,以实现 AI 技术的高效落地。
|
26天前
|
机器学习/深度学习 自然语言处理 测试技术
CoT神话破灭,并非LLM标配!三大学府机构联手证实,CoT仅在数学符号推理有用
【10月更文挑战第16天】近期,加州大学伯克利分校、斯坦福大学和卡内基梅隆大学联合研究发现,链式思维(CoT)方法在数学和符号推理任务中表现优异,但在其他类型任务中效果不明显。这一研究打破了CoT作为大型语言模型(LLM)标配的神话,为重新审视LLM的推理能力提供了新视角。
24 2
|
14天前
|
机器学习/深度学习 人工智能 弹性计算
阿里云AI服务器价格表_GPU服务器租赁费用_AI人工智能高性能计算推理
阿里云AI服务器提供多种配置选项,包括CPU+GPU、CPU+FPGA等组合,支持高性能计算需求。本文汇总了阿里云GPU服务器的价格信息,涵盖NVIDIA A10、V100、T4、P4、P100等多款GPU卡,适用于人工智能、机器学习和深度学习等场景。详细价格表和实例规格见文内图表。
|
2月前
|
人工智能 Prometheus 监控
使用 NVIDIA NIM 在阿里云容器服务(ACK)中加速 LLM 推理
本文介绍了在阿里云容器服务 ACK 上部署 NVIDIA NIM,结合云原生 AI 套件和 KServe 快速构建高性能模型推理服务的方法。通过阿里云 Prometheus 和 Grafana 实现实时监控,并基于排队请求数配置弹性扩缩容策略,提升服务稳定性和效率。文章提供了详细的部署步骤和示例,帮助读者快速搭建和优化模型推理服务。
162 7
使用 NVIDIA NIM 在阿里云容器服务(ACK)中加速 LLM 推理
|
2月前
|
人工智能 Prometheus 监控
使用NVIDIA NIM在阿里云ACK中加速LLM推理
介绍在阿里云ACK集群上结合AI套件能力快速部署NVIDIA NIM模型推理服务,同时提供全面的监控指标和实现弹性伸缩。
使用NVIDIA NIM在阿里云ACK中加速LLM推理
|
2月前
|
编解码 定位技术 计算机视觉
多模态LLM视觉推理能力堪忧,浙大领衔用GPT-4合成数据构建多模态基准
【9月更文挑战第2天】浙江大学领衔的研究团队针对多模态大型模型(MLLM)在抽象图像理解和视觉推理上的不足,提出了一种利用GPT-4合成数据构建多模态基准的方法。该研究通过合成数据提高了MLLM处理图表、文档等复杂图像的能力,并构建了一个包含11,193条指令的基准,涵盖8种视觉场景。实验表明,这种方法能显著提升模型性能,但依赖闭源模型和高计算成本是其局限。论文详细内容见:https://arxiv.org/pdf/2407.07053
78 10
|
3月前
|
安全 异构计算
为大型语言模型 (LLM) 提供服务需要多少 GPU 内存?
为大型语言模型 (LLM) 提供服务需要多少 GPU 内存?
140 0
为大型语言模型 (LLM) 提供服务需要多少 GPU 内存?