2张卡部署72B大模型
0x00 前言
在23年初,还只是勉强跑起来6B的我,是怎么也想不到,在24年的时候,72B也能勉勉强强跑起来了。
直接说结论:
- • 部署的模型 Qwen-72B-Int4
- • 性能指标
- • token处理速度 5.86 token/s
- • 每次对话上下文上限 1000 token
- • 基准测试情况:
- • 跑了3000个任务,输入500个字,输出500个字左右,稳定跑完。
如果上面的性能指标能够满足你的业务诉求又或者想试一下最顶级的大模型,那就接着继续往下看吧。
0x01 部署环境
1. 硬件环境
- • 两张 3090( 24G*2 = 48G 内存)
- • 内存 64G
- • CPU 32核
总计花费 2W出头。(8千一张卡)
2. 软件环境
- • ubuntu22.04
- • cuda11.8.0
- • py310
- • torch2.1.0
- • tf2.14.0-1.10.0
3. 模型选型
https://github.com/QwenLM/Qwen/blob/main/README_CN.md
Qwen-72B-Chat(Int4)基本没什么太大的损失。
但是 Qwen-72B-Chat(Int4)只需要 BF16的1/3的资源。
0x02 部署
直接一把梭,很遗憾,没成功。
根据魔搭的样例 , 怀着侥幸的心理,想蒙混过关,啥也没弄,直接跑了一把:
from modelscope import AutoTokenizer, AutoModelForCausalLM
from modelscope import snapshot_download
model_dir = snapshot_download('qwen/Qwen-72B-Chat-Int4', revision='master')
tokenizer = AutoTokenizer.from_pretrained(model_dir, revision='master', trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
model_dir, revision='master',
device_map="auto" ,
trust_remote_code=True
).eval()
response, history = model.chat(tokenizer, "你好", history=None)
print(response)
结果报错:
device_map 导致内存分配不均
仔细分析后,发现是 device_map 导致的内存分配不均。当前GPU0 用了22G,GPU1 用了16G,还有8G没打满,想了想,应该还是有机会加载完成的。
在官方的文档中 https://huggingface.co/docs/transformers/main_classes/model , 以及 博客 https://huggingface.co/blog/accelerate-large-models 发现device_map 有4种用法:
模式 | 作用 |
auto | 模型均匀地分配到所有可用的 GPU 上。 |
balanced | 模型均匀地分配到所有可用的 GPU 上。 |
balanced_low_0 | 除了第1个GPU,模型均匀地分配其它GPU上。 |
sequential | 从第1个GPU开始分配,直到最后1个GPU分配完。 |
但是理想很美好,显示很骨感,无论怎么修改,始终没办法加载成功。
正当以为没辙之际,突然看到还有一句 You can also pass your own device_map as long as it follows the format we saw before (dictionary layer/module names to device). 即 自己控制每一层分配到哪个GPU。
然后找了一下千问的官方人员咨询了一下,不一会儿,官方人员把手动的切好的 device_map 发了过来。
device_map={'transformer.wte': 0, 'transformer.drop': 0, 'transformer.rotary_emb': 0, 'transformer.h.0': 0,'transformer.h.1': 0, 'transformer.h.2': 0, 'transformer.h.3': 0, 'transformer.h.4': 0, 'transformer.h.5': 0, 'transformer.h.6': 0, 'transformer.h.7': 0, 'transformer.h.8': 0, 'transformer.h.9': 0, 'transformer.h.10': 0, 'transformer.h.11': 0, 'transformer.h.12': 0, 'transformer.h.13': 0, 'transformer.h.14': 0, 'transformer.h.15': 0, 'transformer.h.16': 0, 'transformer.h.17': 0, 'transformer.h.18': 0, 'transformer.h.19': 0, 'transformer.h.20': 0, 'transformer.h.21': 0, 'transformer.h.22': 0, 'transformer.h.23': 0, 'transformer.h.24': 0, 'transformer.h.25': 0, 'transformer.h.26': 0, 'transformer.h.27': 0, 'transformer.h.28': 0, 'transformer.h.29': 0, 'transformer.h.30': 0, 'transformer.h.31': 0, 'transformer.h.32': 0, 'transformer.h.33': 0, 'transformer.h.34': 0, 'transformer.h.35': 0, 'transformer.h.36': 0, 'transformer.h.37': 0, 'transformer.h.38': 0, 'transformer.h.39': 0, 'transformer.h.40': 0, 'transformer.h.41': 1, 'transformer.h.42': 1, 'transformer.h.43': 1, 'transformer.h.44': 1, 'transformer.h.45': 1, 'transformer.h.46': 1, 'transformer.h.47': 1, 'transformer.h.48': 1, 'transformer.h.49': 1, 'transformer.h.50': 1, 'transformer.h.51': 1, 'transformer.h.52': 1, 'transformer.h.53': 1, 'transformer.h.54': 1, 'transformer.h.55': 1, 'transformer.h.56': 1, 'transformer.h.57': 1, 'transformer.h.58': 1, 'transformer.h.59': 1, 'transformer.h.60': 1, 'transformer.h.61': 1, 'transformer.h.62': 1, 'transformer.h.63': 1, 'transformer.h.64': 1, 'transformer.h.65': 1, 'transformer.h.66': 1, 'transformer.h.67': 1, 'transformer.h.68': 1, 'transformer.h.69': 1, 'transformer.h.70': 1, 'transformer.h.71': 1, 'transformer.h.72': 1, 'transformer.h.73': 1, 'transformer.h.74': 1, 'transformer.h.75': 1, 'transformer.h.76': 1, 'transformer.h.77': 1, 'transformer.h.78': 1, 'transformer.h.79': 1, 'transformer.ln_f': 1, 'lm_head': 1}
重新加载了一下,成功输出。
有没有可能,在没有官方人员的协助下,自己来完成这个device_map的分解?
当然是可以的。 如果遇到一个大模型,自己算了一下,显存是可以放的下的话,那么可以通过来加载。
from transformers import AutoConfig, AutoModelForCausalLM
config = AutoConfig.from_pretrained(modelpath)
with init_empty_weights():
model = AutoModelForCausalLM.from_config(config)
如图,可以看到Qwen的结构如下,此时并没有加载模型。
可以通过 for name,md in model.named_children(): 打印每一层的size , 这样遇到自己加载失败的模型就可以自己手搓 device_map 了。
最后,安装 flash-attn 。
在软件环境和我相同的情况下,直接一个命令 pip install flash-attn --no-build-isolation
就是编译和时间会等很久。
0x03 测试
使用 脑筋急转弯 100 道题目,统计 histroy的token,提问和回答累计达到的 x个 token 来评估能力。
每轮跑3000次,次数是随便拍的,想起周星驰电影,要你命3000,评估是否会OOM。
测试结果如下,到了2000就直接跑不动了 😢,放弃继续测试了:
x | 没有OOM | token /s |
1000 | ✅ | 5.86 |
2000 | ❌ |
0x04 总结
遇到没办法加载的大模型怎么办?
- • 评估一下显存是否足够。
- • 通过 AutoConfig 加载模型的参数,评估一下是否可以拆分。
- • 手写 device_map 尝试加载。