LLM优化:开源星火13B显卡及内存占用优化
1. 背景
本qiang~这两天接了一个任务,部署几个开源的模型,并且将本地经过全量微调的模型与开源模型做一个效果对比。
部署的开源模型包括:星火13B,Baichuan2-13B, ChatGLM6B等
其他两个模型基于transformers架构封装,因此推理服务启动还是十分丝滑,但星火13B是基于Megatron-DeepSpeed框架实现,地址是:https://gitee.com/iflytekopensource/iFlytekSpark-13B,启动推理服务的过程中发现启动13B的显卡占用71G-78G,有些反直觉。
此文就是整理开源星火13B的显存及内存排查并优化的整理过程,至于哪家开源模型效果好,不在此文的讨论范围内。
2. 原因分析
直观上来说,13B的模型,数据类型为bf16,显卡占用大概在26G左右,但星火13B直接占用70G+,不可思议,怪不得网上关于星火开源模型的讨论少之又少,原因显而易见,这么大的显存占用只能用多卡或者A800等80G显卡才能适配。穷人家的孩子,哪有这么多余粮。
排查原因的过程中,少不了源码的调试与分析。在排查的过程中,启动推理服务的文件run_iFlytekSpark_text_generation.py中,model_provider方法是初始化模型并加载模型文件的方法。
def model_provider(pre_process=True, post_process=True):
"""Build the model."""
print_rank_0('building iFlytekSpark model ...')
args = get_args()
config = core_transformer_config_from_args(args) ### 初始化星火模型
model = iFlytekSparkModel(
config,
num_tokentypes=0,
parallel_output=False,
pre_process=pre_process,
post_process=post_process,
return_moe_loss=False
) if args.from_pretrained is not None:
assert os.path.exists(args.from_pretrained)
ckpt_path = get_checkpoint_name(args.from_pretrained)
print_rank_0('Loading from {} '.format(
args.from_pretrained))
# 模型加载权重文件
state_dict = torch.load(ckpt_path, map_location=f"cuda:{torch.cuda.current_device()}")
if 'module' in state_dict:
state_dict = state_dict['module']
model.load_state_dict(state_dict)
return model
其中,加载权重文件可以看到,加载state_dict时,直接将权重文件加载到显卡中,而非加载至CPU,然后再执行to方法,转移到GPU。因此该处是一个潜在的优化点。
再打入iFlytekSparkModel内部,词表Embedding层,线性转换层,等初始化weight时,也是直接将weight分配在GPU上运行。例如下例:
class RowParallelLinear(torch.nn.Module):
def __init__(self, input_size: int, output_size: int, *,
config: ModelParallelConfig,
init_method: Callable,
bias: bool = True,
input_is_parallel: bool = False,
stride: int = 1,
keep_master_weight_for_test: bool = False,
skip_bias_add: bool = False,
moe=False, enable_expert_tensor_parallelism=False):
super(RowParallelLinear, self).__init__() # ......... if config.use_cpu_initialization:
self.weight = Parameter(torch.empty(self.output_size,
self.input_size_per_partition,
dtype=config.params_dtype))
if config.perform_initialization:
self.master_weight = _initialize_affine_weight_cpu(
self.weight, self.output_size, self.input_size,
self.input_size_per_partition, 1, init_method,
stride=stride, return_master_weight=keep_master_weight_for_test,
params_dtype=config.params_dtype)
else:
# 默认按照启动sh命令,会走该分支
self.weight = Parameter(torch.empty(
self.output_size, self.input_size_per_partition,
device=get_accelerator().current_device_name(), dtype=config.params_dtype))
if config.perform_initialization:
_initialize_affine_weight_gpu(self.weight, init_method,
partition_dim=1, stride=stride)
if bias:
if config.use_cpu_initialization:
self.bias = Parameter(torch.empty(self.output_size,
dtype=config.params_dtype))
else:
# 默认按照启动sh命令,会走该分支
self.bias = Parameter(torch.empty(
self.output_size, device=get_accelerator().current_device_name(),
dtype=config.params_dtype))
setattr(self.bias, 'sequence_parallel', self.sequence_parallel) if config.perform_initialization:
# Always initialize bias to zero.
with torch.no_grad():
self.bias.zero_()
else:
self.register_parameter('bias', None)
3. 优化方案
1. 模型初始化时,模型的Embedding,线性层的权重weight均直接加载至GPU,因此可以优化为先将这些weight加载至CPU。
改进的方式也很简单,从上面的源码层面,可以看到,当增加参数” use_cpu_initialization”,将使用CPU进行初始化权重,因此只需要在启动推理服务的脚本中增加” --use-cpu-initialization”参数即可。
2. 加载模型文件时,直接加载至GPU,然后run_iFlytekSpark_text_generation.py中的get_model方法中,当模型加载完成后,会进行分配至GPU以及FP16的转换的操作。如下代码所示。
def get_model(model_provider_func, model_type=ModelType.encoder_or_decoder, wrap_with_ddp=True):
"""Build the model."""
args = get_args()
args.model_type = model_type # .......... # GPU allocation.
for model_module in model:
model_module.to(get_accelerator().current_device_name()) # Fp16 conversion.
if args.fp16 or args.bf16:
model = [Float16Module(model_module, args) for model_module in model] # ....... return model
因此,优化的方式也很简单,可以优化为先加载至CPU,再运行get_model中的默认分配至GPU,加载完后,再使用垃圾回收机制清除CPU占用的内存即可。
话不多说,优化后的代码如下:
def model_provider(pre_process=True, post_process=True):
"""Build the model."""
print_rank_0('building iFlytekSpark model ...')
args = get_args()
config = core_transformer_config_from_args(args)
model = iFlytekSparkModel(
config,
num_tokentypes=0,
parallel_output=False,
pre_process=pre_process,
post_process=post_process,
return_moe_loss=False
) if args.from_pretrained is not None:
print(args.from_pretrained)
assert os.path.exists(args.from_pretrained)
ckpt_path = get_checkpoint_name(args.from_pretrained)
print_rank_0('Loading from {} '.format(
args.from_pretrained)) # state_dict = torch.load(ckpt_path, map_location=f"cuda:{torch.cuda.current_device()}")
# CPU进行加载
state_dict = torch.load(ckpt_path, map_location=f"cpu")
if 'module' in state_dict:
state_dict = state_dict['module']
model.load_state_dict(state_dict) # 加载完成,删除state_dict,并垃圾回收
del state_dict
gc.collect()
torch.cuda.empty_cache() return model
4. 效果对比
(1) 优化前的显卡占用: 71.5G

(2) 优化前的内存占用: 虚拟内存占用94.5G

(3) 优化后的显卡占用: 26G

(4) 优化后的内存占用: 43.1G

5. 总结
一句话足矣~
本文主要是针对开源星火13B的显存及内存占用过大的一个代码优化。核心思想是使用CPU预加载模型,再转换至GPU。
后期如有遇到此类问题,可以借鉴之~

LLM优化:开源星火13B显卡及内存占用优化的更多相关文章
- mariadb 内存占用优化
本文由云+社区发表 作者:工程师小熊 摘要:我们在使用mariadb的时候发现有时候不能启动起来,在使用过程中mariadb占用的内存很大,在这里学习下mariadb与内存相关的配置项,对mariad ...
- es 内存占用优化
对6.3: 修改Elasticsearch中JVM配置文件jvm.options: Dlog4j2.enable.threadlocals=false 注: 本文主要针对ES 2.x. “该给ES分配 ...
- EasyDarwin开源流媒体服务器内存管理优化
-本文由EasyDarwin开源团队成员Fantasy贡献 前言 最近在linux上跑EasyDarwin发现一个很奇怪的问题,当有RTSPSession连接上来的时候,发现进程的虚拟内存映射一下就多 ...
- 【转载】Unity 优雅地管理资源,减少占用内存,优化游戏
转自:星辰的<Unity3D占用内存太大的解决方法> 最近网友通过网站搜索Unity3D在手机及其他平台下占用内存太大. 这里写下关于Unity3D对于内存的管理与优化. Unity3D ...
- android app性能优化大汇总(内存性能优化)
转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 写在最前: 本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上 ...
- Java内存溢出优化性能优化
高性能应用构成了现代网络的支柱.LinkedIn有许多内部高吞吐量服务来满足每秒数千次的用户请求.要优化用户体验,低延迟地响应这些请求非常重要. 比如说,用户经常用到的一个功能是了解动态信息——不断更 ...
- 感悟优化——Netty对JDK缓冲区的内存池零拷贝改造
NIO中缓冲区是数据传输的基础,JDK通过ByteBuffer实现,Netty框架中并未采用JDK原生的ByteBuffer,而是构造了ByteBuf. ByteBuf对ByteBuffer做了大量的 ...
- Java解读内存,优化编程
1.别用new Boolean 在很多场景中Boolean类型是必须的,比如JDBC中boolean类型的set与get都是通过Boolean封装传递的,大部分ORM也是用Boolean来封装bool ...
- Redis内存使用优化与存储
抄自http://www.infoq.com/cn/articles/tq-redis-memory-usage-optimization-storage 本文将对Redis的常见数据类型的使用场景以 ...
- Android内存性能优化(内部资料总结)
eoe上看到的一个很好的文章 摘抄了下来留着自己看看 刚入门的童鞋肯能都会有一个疑问,Java不是有虚拟机了么,内存会自动化管理,我们就不必要手动的释放资源了,反正系统会给我们完成.其实Java中没有 ...
随机推荐
- PostgreSQL 函数稳定性在索引与全表访问下的性能差异
一.构建测试数据 create or replace function test_volatile(id integer) returns bigint volatile language sql a ...
- Rust 实现日志记录功能
目录 log 日志库标准 简单示例 使用方法 库的开发者 应用开发者 日志库开发者 使用 log4rs 添加依赖 配置文件 运行项目 参考文章 log 日志库标准 log 是 Rust 的日志门面库, ...
- #dp,高精度#洛谷 4295 [SCOI2003]严格N元树
题目 求有多少棵严格 \(n\) 叉树深度为 \(k\) 分析 考虑往下放子孙挺难维护的,考虑在上面换新的根. 设 \(dp[i]\) 表示深度不超过 \(i\) 的方案数,那么 \(dp[i]=dp ...
- #树链剖分,线段树#洛谷 2146 [NOI2015]软件包管理器
题目传送门 分析 安装时1到\(x\)路径上都变为1,删除时\(x\)的子树都变为0, 显然可以用树链剖分+线段树实现 代码 #include <cstdio> #include < ...
- 【LGR-069】洛谷 2 月月赛 II & EE Round 2
目录 前言 洛谷 6101 [EER2]出言不逊 分析 代码 洛谷 6102 [EER2]谔运算 分析 代码 洛谷 6103 [EER2] 直接自然溢出啥事没有 分析 代码 洛谷 6105 [Ynoi ...
- #差分约束,SPFA#洛谷 1993 小 K 的农场
题目 分析 对于描述1,也就是\((a,b,-c)\),\(b\)比\(a\)至多多\(-c\) 对于描述2,也就是\((b,a,c)\),\(a\)比\(b\)至多多\(c\) 对于描述3,也就是\ ...
- 如何利用OpenHarmony ArkUI的Canvas组件实现涂鸦功能?
简介 ArkUI是一套UI开发框架,提供了开发者进行应用UI开发时所需具备的能力.随着OpenAtom OpenHarmony(以下简称"OpenHarmony")不断更新迭代,A ...
- 构筑智能未来的开源 .Net AI知识库/智能体项目
在这个信息爆炸的时代,我们如何快速准确地从汪洋大海的数据中抽取真正有价值的知识呢?AntSK,一个基于.NET开发的人工智能知识库和智能体项目,似乎给出了一个新颖的答案.今天,就让我们一起深入了解An ...
- Linux忘记root密码修改方法
1. 再开机启动的时候按键盘上的"e"键会出现如下界面.再次按"e"键进行选择相应的内核. 2. 选择相应的内核,再次按"e". 3.经过第 ...
- Git 11 设置项目提交人
前面介绍了可以给 Git 设置全局提交人,这样当前电脑所有项目提交人都会变成设置的值. 但实际开发中有时候需要给不同项目设置不同提交人. 比如工作的项目是一个提交人,自己维护的开源项目又是另一个提交人 ...