经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 大数据/云/AI » 人工智能基础 » 查看文章
LLM优化:开源星火13B显卡及内存占用优化
来源:cnblogs  作者:mengrennwpu  时间:2024/4/29 9:27:27  对本文有异议

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方法是初始化模型并加载模型文件的方法。

  1. def model_provider(pre_process=True, post_process=True):
  2. """Build the model."""
  3. print_rank_0('building iFlytekSpark model ...')
  4. args = get_args()
  5. config = core_transformer_config_from_args(args)
  6. ### 初始化星火模型
  7. model = iFlytekSparkModel(
  8. config,
  9. num_tokentypes=0,
  10. parallel_output=False,
  11. pre_process=pre_process,
  12. post_process=post_process,
  13. return_moe_loss=False
  14. )
  15. if args.from_pretrained is not None:
  16. assert os.path.exists(args.from_pretrained)
  17. ckpt_path = get_checkpoint_name(args.from_pretrained)
  18. print_rank_0('Loading from {} '.format(
  19. args.from_pretrained))
  20. # 模型加载权重文件
  21. state_dict = torch.load(ckpt_path, map_location=f"cuda:{torch.cuda.current_device()}")
  22. if 'module' in state_dict:
  23. state_dict = state_dict['module']
  24. model.load_state_dict(state_dict)
  25. return model

 

其中,加载权重文件可以看到,加载state_dict时,直接将权重文件加载到显卡中,而非加载至CPU,然后再执行to方法,转移到GPU。因此该处是一个潜在的优化点。

再打入iFlytekSparkModel内部,词表Embedding层,线性转换层,等初始化weight时,也是直接将weight分配在GPU上运行。例如下例:

  1. class RowParallelLinear(torch.nn.Module):
  2. def __init__(self, input_size: int, output_size: int, *,
  3. config: ModelParallelConfig,
  4. init_method: Callable,
  5. bias: bool = True,
  6. input_is_parallel: bool = False,
  7. stride: int = 1,
  8. keep_master_weight_for_test: bool = False,
  9. skip_bias_add: bool = False,
  10. moe=False, enable_expert_tensor_parallelism=False):
  11. super(RowParallelLinear, self).__init__()
  12. # .........
  13. if config.use_cpu_initialization:
  14. self.weight = Parameter(torch.empty(self.output_size,
  15. self.input_size_per_partition,
  16. dtype=config.params_dtype))
  17. if config.perform_initialization:
  18. self.master_weight = _initialize_affine_weight_cpu(
  19. self.weight, self.output_size, self.input_size,
  20. self.input_size_per_partition, 1, init_method,
  21. stride=stride, return_master_weight=keep_master_weight_for_test,
  22. params_dtype=config.params_dtype)
  23. else:
  24. # 默认按照启动sh命令,会走该分支
  25. self.weight = Parameter(torch.empty(
  26. self.output_size, self.input_size_per_partition,
  27. device=get_accelerator().current_device_name(), dtype=config.params_dtype))
  28. if config.perform_initialization:
  29. _initialize_affine_weight_gpu(self.weight, init_method,
  30. partition_dim=1, stride=stride)
  31. if bias:
  32. if config.use_cpu_initialization:
  33. self.bias = Parameter(torch.empty(self.output_size,
  34. dtype=config.params_dtype))
  35. else:
  36. # 默认按照启动sh命令,会走该分支
  37. self.bias = Parameter(torch.empty(
  38. self.output_size, device=get_accelerator().current_device_name(),
  39. dtype=config.params_dtype))
  40. setattr(self.bias, 'sequence_parallel', self.sequence_parallel)
  41. if config.perform_initialization:
  42. # Always initialize bias to zero.
  43. with torch.no_grad():
  44. self.bias.zero_()
  45. else:
  46. 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的转换的操作。如下代码所示。

  1. def get_model(model_provider_func, model_type=ModelType.encoder_or_decoder, wrap_with_ddp=True):
  2. """Build the model."""
  3. args = get_args()
  4. args.model_type = model_type
  5. # ..........
  6.  
  7. # GPU allocation.
  8. for model_module in model:
  9. model_module.to(get_accelerator().current_device_name())
  10. # Fp16 conversion.
  11. if args.fp16 or args.bf16:
  12. model = [Float16Module(model_module, args) for model_module in model]
  13. # .......
  14.  
  15. return model

因此,优化的方式也很简单,可以优化为先加载至CPU,再运行get_model中的默认分配至GPU,加载完后,再使用垃圾回收机制清除CPU占用的内存即可。

话不多说,优化后的代码如下:

  1. def model_provider(pre_process=True, post_process=True):
  2. """Build the model."""
  3. print_rank_0('building iFlytekSpark model ...')
  4. args = get_args()
  5. config = core_transformer_config_from_args(args)
  6. model = iFlytekSparkModel(
  7. config,
  8. num_tokentypes=0,
  9. parallel_output=False,
  10. pre_process=pre_process,
  11. post_process=post_process,
  12. return_moe_loss=False
  13. )
  14. if args.from_pretrained is not None:
  15. print(args.from_pretrained)
  16. assert os.path.exists(args.from_pretrained)
  17. ckpt_path = get_checkpoint_name(args.from_pretrained)
  18. print_rank_0('Loading from {} '.format(
  19. args.from_pretrained))
  20. # state_dict = torch.load(ckpt_path, map_location=f"cuda:{torch.cuda.current_device()}")
  21. # CPU进行加载
  22. state_dict = torch.load(ckpt_path, map_location=f"cpu")
  23. if 'module' in state_dict:
  24. state_dict = state_dict['module']
  25. model.load_state_dict(state_dict)
  26. # 加载完成,删除state_dict,并垃圾回收
  27. del state_dict
  28. gc.collect()
  29. torch.cuda.empty_cache()
  30. return model

  

4. 效果对比

(1) 优化前的显卡占用: 71.5G

 

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

 

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

 

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

  

5. 总结

一句话足矣~

本文主要是针对开源星火13B的显存及内存占用过大的一个代码优化。核心思想是使用CPU预加载模型,再转换至GPU。

后期如有遇到此类问题,可以借鉴之~

 

 

原文链接:https://www.cnblogs.com/mengrennwpu/p/18164027

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号