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。

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用Git把写好的项目放到github上

把之前的文章差缺补漏了一下,发现少一个TUserController文件,然后加上了。 以及发现前后端交互时的跨域问题需要处理。 在Controller文件里加入注释 CrossOrigin(origins "*")即可。 不然数据在Vue里显示不出来。 ** 壹 首先先把前端项目 de…

ElasticSearch教程入门到精通——第四部分(基于ELK技术栈elasticsearch 7.x新特性)

ElasticSearch教程入门到精通——第四部分(基于ELK技术栈elasticsearch 7.x新特性) 1. Elasticsearch进阶1.1 核心概念1.1.1 索引Index1.1.1.1 索引创建原则1.1.1.2 Inverted Index 1.1.2 类型Type1.1.3 文档Document1.1.4 字段Field1.1.5 映射Mapping1.…

AI项目二十一:视频动态手势识别

若该文为原创文章,转载请注明原文出处。 一、简介 人工智能的发展日新月异,也深刻的影响到人机交互领域的发展。手势动作作为一种自然、快捷的交互方式,在智能驾驶、虚拟现实等领域有着广泛的应用。手势识别的任务是,当操作者做出…

翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习六

合集 ChatGPT 通过图形化的方式来理解 Transformer 架构 翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习一翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习二翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深…

设备能源数据采集新篇章

在当今这个信息化、智能化的时代,设备能源数据的采集已经成为企业高效运营、绿色发展的重要基石。而今天,我们要向大家介绍的就是一款颠覆传统、引领未来的设备能源数据采集神器——HiWoo Box网关! 一、HiWoo Box网关:一站式解决…

virtualbox kafka nat + host-only集群 + windows 外网 多网卡

virtualbox kafka nat + host-only集群 + windows 映射访问 kafka集群搭建背景kafka集群搭建 背景 使用virtualbox搭建kafka集群,涉及到不同网络策略的取舍 首先 桥接 网络虽说 啥都可以,但是涉及到过多ip的时候,而且还不能保证使用的ip不被占用,所以个人选择kafka虚拟机…

2024.5.5 机器学习周报

引言 Abstract 文献阅读 1、题目 SuperGlue: Learning Feature Matching with Graph Neural Networks 2、引言 本文介绍了SuperGlue,这是一种神经网络,它通过联合寻找对应关系并拒绝不匹配的点来匹配两组局部特征。通过求解一个可微的最优运输问题…

【Mac】Photoshop 2024 for mac最新安装教程

软件介绍 Photoshop 2024是Adobe公司推出的一款图像处理软件,它支持Windows和Mac OS系统。Adobe Photoshop是业界领先的图像编辑和处理软件之一,广泛用于设计、摄影、数字绘画等领域。 Photoshop 2024的功能包括: 1.图像编辑:提…

如何提高商务认知与情商口才(3篇)

如何提高商务认知与情商口才(3篇) **篇:提高商务认知 商务认知的提升是一个系统工程,需要我们不断地积累知识、理解市场和关注行业动态。以下是一些具体的方法: 持续学习:通过阅读商业书籍、参加行业研讨…

相机知识的补充

一:镜头 1.1MP的概念 相机中MP的意思是指百万像素。MP是mega pixel的缩写。mega意为一百万,mega pixel 指意为100万像素。“像素”是相机感光器件上的感光最小单位。就像是光学相机的感光胶片的银粒一样,记忆在数码相机的“胶片”&#xff…

【深耕 Python】Data Science with Python 数据科学(17)Scikit-learn机器学习(二)

写在前面 关于数据科学环境的建立,可以参考我的博客: 【深耕 Python】Data Science with Python 数据科学(1)环境搭建 往期数据科学博文一览: 【深耕 Python】Data Science with Python 数据科学(2&…

论文精读-基于FPGA的卷积神经网络和视觉Transformer通用加速器

论文精读-基于FPGA的卷积神经网络和视觉Transformer通用加速器 优势: 1.针对CNN和Transformer提出了通用的计算映射(共用计算单元,通过不同的映射指令,指导数据通路和并行计算) 2.非线性与归一化加速单元&#xff0…

路由器的构成

一、路由器简介 路由器是互联网中的关键设备: 连接不同的网络路由器是多个输入端口和多个输出端口的专用计算机,其任务是转发分组(转发给下一跳路由器)下一跳路由器也按照这种方法处理分组,直到该分组到达终点为止 …

线程的概念

文章目录 1、什么是线程2、进程和线程的区别3、多线程的概述4、在Java中实现多线程的方法1.继承Thread类2.实现Runnable接口3.使用匿名内部类来继承Thread类,实现run方法4.使用匿名内部类来实现Runnable接口,实现run方法5.使用 lambda表达式 1、什么是线…

STM32 DMA直接存储器存取

单片机学习! 目录 文章目录 前言 一、DMA简介 1.1 DMA是什么 1.2 DMA作用 1.3 DMA通道 1.4 软硬件触发 1.5 芯片资源 二、存储器映像 2.1 存储器 2.2 STM32存储器 三、DMA框图 3.1 内核与存储器 3.2 寄存器 3.3 DMA数据转运 3.4 DMA总线作用 3.5 DMA请求 3.6 DMA结构…

linux的常见命令

📝个人主页:五敷有你 🔥系列专栏:Linux ⛺️稳中求进,晒太阳 Linux中检查进程是否存在: ps -ef | grep [进程名或进程ID] pgrep -f [进程名|进程ID] pidof [进程名] Linux中检查某个端口是否被…

neo4j 的插入速度为什么越来越慢,可能是使用了过多图谱查询操作

文章目录 背景描述分析解决代码参考neo4j 工具类Neo4jDriver知识图谱构建效果GuihuaNeo4jClass 背景描述 使用 tqdm 显示,处理的速度; 笔者使用 py2neo库,调用 neo4j 的API 完成节点插入; 有80万条数据需要插入到neo4j图数据中&am…

目标检测发展概述

前言 本篇文章只是简单介绍一下目标检测这一计算机视觉方向的发展历史,因此重点在于介绍而不是完整阐述各个时期的代表算法,只要能够简单了解到目标检测的发展历史那么本文的目的就达到了。 目标检测的任务 从上图不难看出,目标检测是计算机…

第十五届蓝桥杯

经历半年以来的学习,终于出结果了。期间无数次想要放弃,但是我都挺过来了,因为我还不能倒下。期间有很多次焦虑,一直在想,我要是没拿奖我是不是白学了。但是学到最后就释怀了,因为在备赛期间,我…

unity制作app(1)--登录 注册 界面

把学到的知识投入到生产中反而是一件简单的事情! 1.调整canvas的形状,这里和camera没有任何关系! overlay! 2.既然自适应,空间按钮的位置比例就很重要了! game窗口中新增720*1280的分辨率! 3.再回到can…