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

news2024/11/23 19:06:19

大型语言模型(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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1265877.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在Rust中处理命令行参数和环境变量

1.摘要 Rust的命令行和环境变量处理在标准库中提供了一整套实现方法, 在本文中除了探索标准库的使用方法之外, 也在不断适应Rust独有的语法特点。在本文中, 我们通过标准库函数的返回值熟悉了迭代器的使用方法, 操作迭代器精确控制保存的内容, 包括字符串和键值对的使用方法。…

SpringBoot整合EasyExcel实现复杂Excel表格的导入导出功能

文章目录 🎉SpringBoot整合EasyExcel实现复杂Excel表格的导入&导出功能 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒🍹✨博客主页:IT陈寒的博客🎈该系列文章专栏:架构设计📜其他专栏:Java学习路线 Jav…

mysql 性能排查

mysql 下常见遇到的问题有,mysql连接池耗尽,死锁、慢查、未提交的事务。等等我们可能需要看;我们想要查看的可能有 1.当前连接池连接了哪些客户端,进行了哪些操作 2.当前造成死锁的语句有哪些,是哪个客户端上的&#x…

2023网络安全产业图谱

1. 前言 2023年7月10日,嘶吼安全产业研究院联合国家网络安全产业园区(通州园)正式发布《嘶吼2023网络安全产业图谱》。 嘶吼安全产业研究院根据当前网络安全发展规划与趋势发布《嘶吼2023网络安全产业图谱》调研,旨在进一步了解…

2020年6月16日 Go生态洞察:泛型的下一步

🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…

选择跨网数据摆渡系统时,你最关注的功能是哪些?

为什么要选择跨网数据摆渡系统呢?因为做了网络隔离后,要有数据交互。那为什么要做网络隔离呢?主要还是安全方面的考虑,一般有以下几个原因: 1、数据安全保护:对于一些重要数据,比如代码数据、隐…

leetCode 39.组合总和 + 回溯算法 + 剪枝 + 图解 + 笔记

39. 组合总和 - 力扣(LeetCode) 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合 can…

IDEA 配置 gradle6.8.3 解决导入gradle项目下载太慢问题

由于平时用的是springboot 2.7 这里下载gradle-6.8.3 Gradle官网地址:https://services.gradle.org/distributions/ 1.下载gradle后,配置环境变量 GRADLE_HOME {gradle 文件路径} GRADLE_USER_HOME {jar下载路径,可以放maven jar保存路径…

RabbitMQ高级特性2 、TTL、死信队列和延迟队列

MQ高级特性 1.削峰 设置 消费者 测试 添加多条消息 拉取消息 每隔20秒拉取一次 一次拉取五条 然后在20秒内一条一条消费 TTL Time To Live(存活时间/过期时间)。 当消息到达存活时间后,还没有被消费,会被自动清除。 RabbitMQ…

浏览器触发下载Excel文件-Java实现

目录 1:引入maven 2:代码实现 3.导出通讯录信息到Excel文件 4.生成并下载Excel文件部分解释 1:引入maven 添加依赖:首先,在你的项目中添加EasyExcel库的依赖。你可以在项目的构建文件(如Maven的pom.xml)中添加以下依赖项:<dependency><groupId>com.alib…

医疗影像数据集—CT、X光、骨折、阿尔茨海默病MRI、肺部、肿瘤疾病等图像数据集

最近收集了一大波关于CT、X光等医疗方面的数据集包含骨折、阿尔茨海默病MRI、肺部疾病等类型的医疗影像数据&#xff0c;废话不多说&#xff0c;给大家逐一介绍&#xff01;&#xff01; 1、彩色预处理阿尔茨海默病MRI(磁共振成像)图像数据集 彩色预处理阿尔茨海默病MRI(磁共…

平凯星辰携手教育部教育管理信息中心,助力普惠教育数字化

近日&#xff0c;企业级开源分布式数据库厂商平凯星辰与教育部教育管理信息中心达成合作&#xff0c;TiDB 分布式数据库为全国中小学管理服务平台提供全栈服务。双方将携手深入探索领先的数据库技术在教育行业的新场景与新应用&#xff0c;既夯实教育数字化底座&#xff0c;助力…

“抓机遇,促发展”2024亚洲国际人工智能展览会(世亚智博会)

随着人工智能技术的飞速发展&#xff0c;我们正在见证一个全新的时代。2024年即将到来&#xff0c;这一年是人工智能创新将重塑传统界限的一年。从全球领先的科技大国到各类企业&#xff0c;人工智能技术正在以前所未有的速度融入我们的日常生活&#xff0c;推动行业走向未来&a…

34 - 记一次线上SQL死锁事故:如何避免死锁?

之前我参与过一个项目&#xff0c;在项目初期&#xff0c;我们是没有将读写表分离的&#xff0c;而是基于一个主库完成读写操作。在业务量逐渐增大的时候&#xff0c;我们偶尔会收到系统的异常报警信息&#xff0c;DBA 通知我们数据库出现了死锁异常。 按理说业务开始是比较简…

好用的json处理工具He3 JSON

官网地址&#xff1a;https://he3app.com/zh/ json格式化 https://portal.he3app.com/home/extension/json-to-pretty 其他 https://portal.he3app.com/home/category

笔记-PC端wireshark采集FPGA数据的操作

wireshark采集FPGA的数据 目录 一、准备工作二、操作步骤 一、准备工作 1、软件&#xff1a;wireshark 2、平台&#xff1a;PC&#xff08;本人是win11&#xff09;、带有以太网功能的zynq平台 3、网线: 用网线连接zynq板子和PC的以太口端口 二、操作步骤 1、打开任务管理器…

【算法】滑动窗口题单——1.定长滑动窗口⭐

文章目录 1456. 定长子串中元音的最大数目2269. 找到一个数字的 K 美丽值1984. 学生分数的最小差值&#xff08;排序&#xff09;643. 子数组最大平均数 I1343. 大小为 K 且平均值大于等于阈值的子数组数目2090. 半径为 k 的子数组平均值2379. 得到 K 个黑块的最少涂色次数1052…

【问题系列】消费者与MQ连接断开问题解决方案(一)

1. 问题描述 当使用RabbitMQ作为中间件&#xff0c;而消费者为服务时&#xff0c;可能会出现以下情况&#xff1a;在长时间没有消息传递后&#xff0c;消费者与RabbitMQ之间出现连接断开&#xff0c;导致无法处理新消息。解决这一问题的方法是重启Python消费者服务&#xff0c;…

okhttp系列-拦截器的执行顺序

1.将拦截器添加到ArrayList final class RealCall implements Call {Response getResponseWithInterceptorChain() throws IOException {//将Interceptor添加到ArrayListList<Interceptor> interceptors new ArrayList<>();interceptors.addAll(client.intercept…

Android控件全解手册 - 任意View缩放平移工具-源码

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列ChatGPT和AIGC &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分…