在消费级GPU调试LLM的三种方法:梯度检查点,LoRA和量化

LLM的问题就是权重参数太大,无法在我们本地消费级GPU上进行调试,所以我们将介绍3种在训练过程中减少内存消耗,节省大量时间的方法:梯度检查点,LoRA和量化。

梯度检查点

梯度检查点是一种在神经网络训练过程中使动态计算只存储最小层数的技术。

为了理解这个过程,我们需要了解反向传播是如何执行的,以及在整个过程中层是如何存储在GPU内存中的。

1、前向和后向传播的基本原理

前向传播和后向传播是深度神经网络训练的两个阶段。

在前向传递过程中,输入被矢量化(将图像转换为像素,将文本转换为嵌入),并且通过一系列线性乘法和激活函数(如sigmoid或ReLU等非线性函数)在整个神经网络中处理每个元素。

神经网络的输出,被称为头部,被设计用来产生期望的输出,例如分类或下一个单词预测。然后将矢量化的预测结果与预期结果进行比较,并使用特定的损失函数(如交叉熵)计算损失。

基于损失值,以最小化损失为目标更新每层的权值和偏差。这个更新过程从神经网络的末端开始并向起点传播。

上面就是一个简单的过程,下面才是我们主要关注的:计算是如何存储在内存中的。

2、减少存储数量

一种简单的方法是只保留反向传播所需的基本层,并在它们的使用完成后从内存中释放它们。

从上图可以看出,同时存储在内存中的层的最大数量并不是最优的。所以我们需要找到一种方法,在保持反向传播工作的同时,在内存中存储更少的元素。

3、减少计算时间

减少内存占用的一种方法是在神经网络开头的反向传播过程中重新计算每一层。

但是在这种情况下,计算时间会明显增加,使得训练在大模型的情况下不可行。

4、优化计算和内存梯度检查点

该技术通过保存“检查点”以计算反向传播期间“丢失”的层。该算法不是从头开始计算层,如前面的示例所示,而是从最近的检查点开始计算。

平衡内存存储和计算时间的最佳策略是设置O(sqrt(n))个检查点,层数为n。这样,一次反向传播计算的额外计算次数将对应于一次额外的前反向传播。

这种技术可以在较小的gpu上训练较大的模型,但代价是需要额外的计算时间(约20%)。

5、如何实现梯度检查点

transformer库已经提供了梯度检查点技术。

 from transformers import AutoModelForCausalLM, TraininArgumentsmodel = AutoModelForCausalLM.from_pretrained(model_id,use_cache=False, # False if gradient_checkpointing=True**default_args)model.gradient_checkpointing_enable()

LoRA

LoRA是微软团队开发的一种技术,用于加速大型语言模型的微调。他们在GPT-3 175B上实施了这种方法,并大大减少了训练参数的数量。

他们的方法冻结预训练模型的所有参数,并将新的可训练参数嵌入到transformer架构中的特定模块中,如注意力模块(查询、键、值,但也适用于其他模块)。

为了实现这些适配器,他们利用线性层,如下面的等式所示,其中x (dimension: d)和h (dim: k)作为乘法前后的层,Wo作为预训练的权重,B和A作为新的权重矩阵。

矩阵B和A的维数分别为(d × r)和(r × k),且r << min(d, k)。

也就是说在不使训练过程复杂化的情况下,将新的密集层添加到现有的层上。在微调过程中,权重矩阵BA初始化为0,并遵循α/r的线性尺度,α为常数。当使用Adam算法优化权重时,α与学习率大致相同。

对不同的LoRA配置进行了测试,论文得出的结果是,将r=8(或更高)应用于各种模块的性能最好。

一旦对LoRA模型进行了微调,就可以将权重合并在一起以获得单个模型,或者只单独保存适配器,并将预训练模型与现有模型分开加载。

Hugging Face开发的PEFT库,可以利用LoRA技术。

 from peft import LoraConfig, TaskTypelora_config = LoraConfig(r=16,lora_alpha=16,target_modules=["query_key_value"]lora_dropout=0.1,bias="none",task_type=TaskType.CAUSAL_LM,)

还可以针对transformer架构中的所有密集层:

 # From https://github.com/artidoro/qlora/blob/main/qlora.pydef find_all_linear_names(args, model):cls = torch.nn.Linearlora_module_names = set()for name, module in model.named_modules():if isinstance(module, cls):names = name.split('.')lora_module_names.add(names[0] if len(names) == 1 else names[-1])

然后就是将“初始化”适配器添加到预训练模型中。

 from transformers import AutoModelForCausalLMfrom peft import get_peft_modelmodel = AutoModelForCausalLM.from_pretrained(model_id)lora_model = get_peft_model(model, peft_config)lora_model.print_trainable_parameters()

训练完成后,可以单独保存适配器,也可以将它们合并到模型中。

 # Save only adapaterslora_model.save_pretrained(...)# Save merged modelmerged_model = lora_model.merge_and_unload()  merged_model.save_pretrained(...)

量化

谈到LoRA,我就还需要说一下量化。这两种技术在论文QLORA得到了高效的融合,并且已经通过bitsandbytes、peft和accelerayte整合到了Hugging Face 的transformer中。

1、什么是量化?

量化是一种技术,可以降低元素的精度,但不会失去元素的整体意义。例如在图片的情况下,量化包括减少像素的数量,同时保持图像的一个体面的分辨率。

上图肉眼基本看不出区别,但是存储空间却少了很多。在解释量化之前,需要了解计算机如何表示数字的

2、浮点数基本原理

计算机是二进制的,这意味着它们只通过0和1交换信息。为了表示数字,科学家设计了一种称为浮点格式的特殊系统,它允许计算机理解大范围的数值。最常见的表示形式是单精度浮点格式,由32位组成(1位= 0或1)。

除此以外还存在各种格式,例如半精度(16位)或双精度(64位)。简而言之,使用的比特数越多,可以容纳的数字范围就越广。

像GPT-3.5或Bloom-175B这样的模型非常大。在FP32格式中,这将表示:

175*10⁹. 4字节= 700Gb,半精度为350Gb,基本不可能加载到GPU内存中,那么我们如何缩小这些模型呢?

3、从FP32到Int8

Int8表示[- 127,127]之间的任何数字。

我们想将一个浮点数向量简化为Int8格式:

 v = [-1.2, 4.5, 5.4, -0.1]

我们需要做的是定义v的最大值(这里是5.4),并将所有数字缩放到Int8[- 127,127]的范围内。所以需要计算系数

 α = 127 / max(v) = 127 / 5.4 ~ 23.5

然后把v中的所有数乘以α,然后四舍五入,得到:

 α.v = [-28, 106, 127, -2]

如果想去量化这个向量,只需要做相反的操作,就能够得到初始向量!

 v = [-1.2, 4.5, 5.4, -0.1]

可以看到量化和反量化不会丢失任何信息。但实际上在四舍五入每个值时确实会失去精度。然而,在这个特定的例子中差异并不大,因为我们决定只用一个小数来表示数字,另外就是对于大模型来说,参数相互很大,之间也有关系,所以四舍五入的精度丢失不会对模型的结果产生很大的影响(是不产生很大影响,不是没影响),为了节省内存丢失一些小小的精度还是可以接受的。

那么,如果有异常值存在会发生什么?假设我们现在有这个向量:

 v’ = [-1.2, 70, 5.4, -0.1]

目前的最高数字是70,这可以被视为一个异常值。如果我们重现完全相同的过程,我们在量化之后得到:

 de-quantized v’ = [-1.1, 70, 5.5, 0.0]

精度的损失开始出现了,让如果我们将同样的损失应用于由70亿个参数组成的LLM:缺乏精度将在整个神经网络中积累,导致有意义的信息完全丢失,并导致纯噪声。而且我们现在使用的是8位格式,如果是4位甚至3位,结果会更糟,对吧。

但是大佬们找到了一种将量化应用于LLM的方法!

4、LLM.int8()使大规模量化成为可能

论文LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale 介绍了一种绕过此异常值问题的方法。

量化参数的完整性会导致性能下降,而在矩阵乘法过程中使用量化,结合混合精度分解和向量量化。在矩阵乘法过程中,从权重矩阵中提取包含异常值(高于阈值)的向量,从而产生两次乘法。小数字矩阵(根据论文代表 99.9% 的值)被量化,而大数字则保留在 FP16 中。

按照混合精度分解原理,对小数乘法输出进行反量化,并添加到其他输出。

也就是说量化技术仅在推理(矩阵乘法)期间使用,这意味着实际上没有8位数字组成的更小的模型!由于这种技术实现,我们甚至得到了一个更大的模型!(根据该论文,对于13B以下的模型,误差为0.1%)但是在BLOOM-175B上的实验表明,在没有任何性能下降的情况下,内存占用减少了1.96倍!这种技术可以访问以前无法装入GPU内存的大型模型

5、可以微调这个量化模型吗?

不行,因为这种技术只适用于推理,不适合训练。

如果我们可以使用量化减少GPU内存占用,并使用LoRA技术训练新的适配器,会怎么样?

还记得我们以前介绍的QLoRA吗,它就干的是这个事,他们成功地将预训练模型量化为4位!它们通过一些新技术来成功地量化模型,比如双量化和4位NormalFloat。

6、如何在代码中使用量化?

首先需要安装bitsandbytes和accelerate 库

 pip install -q bitsandbytespip install -q acceleratepip install -q peft==0.4.1

然后,在调用from_pretrained方法时,可以通过传递参数load_in_4bit=True或load_in_8bit= true来加载4位或8位量化的模型。

 from transformers import AutoModelForCausalLMmodel = AutoModelForCausalLM.from_pretrained("facebook/opt-350m", load_in_4bit=True, device_map="auto")

也可以使用BitsAndBytesConfig类来进行高级的设置

 from transformers import BitsAndBytesConfignf4_config = BitsAndBytesConfig(load_in_4bit=True,bnb_4bit_quant_type="nf4",bnb_4bit_use_double_quant=True,bnb_4bit_compute_dtype=torch.bfloat16)model_nf4 = AutoModelForCausalLM.from_pretrained(model_id, device_map="auto"quantization_config=nf4_config)

这样模型差不多可以进行推断了。但是我们还需要设置一下的参数:

冻结量化参数以防止训练,

在所有归一化层和 LM 头中使用FP32(未量化),以确保模型的稳定性

如果使用梯度检查点,需要配置model.enable_input_require_grad()

 for name, param in model.named_parameters():# freeze base model's layersparam.requires_grad = False# cast all non int8 or int4 parameters to fp32for param in model.parameters():if (param.dtype == torch.float16) or (param.dtype == torch.bfloat16):param.data = param.data.to(torch.float32)if use_gradient_checkpointing:# For backward compatibilitymodel.enable_input_require_grads()

在最新的peft==0.4.1库中,使用prepare_model_for_kbit_training()方法可以处理这个准备工作。

这样我们就有了一个量子的模型!

一段代码总结

我们已经介绍了梯度检查点、LoRA和量化,让我们编写代码来对LLM进行微调。

先安装必要的库:

 pip install -q -U bitsandbytespip install -q -U git+https://github.com/huggingface/transformers.gitpip install -q -U git+https://github.com/huggingface/peft.gitpip install -q -U git+https://github.com/huggingface/accelerate.git

然后就是代码:

 from transformers import (AutoModelForCausalLM,BitsAndBytesConfig)from peft import (get_peft_model,LoraConfig,TaskType,prepare_model_for_kbit_training)# Import the modelgradient_checkpointing = Truemodel = AutoModelForCausalLM.from_pretrained(args.model_id,use_cache=False if gradient_checkpointing else True,  # this is needed for gradient checkpointingdevice_map="auto",load_in_4bit=True)# Prepare the model (freeze, cast FP32, enable_require_grads, activate gradient checkpointing)model = prepare_model_for_kbit_training(model, use_gradient_checkpointing=gradient_checkpointing)# Prepare Peft model by adding Lorapeft_config = LoraConfig(r=64,lora_alpha=16,target_modules=modules,lora_dropout=0.1,bias="none",task_type=TaskType.CAUSAL_LM,)model = get_peft_model(model, peft_config)

这样模型就可以在本地的GPU上进行微调了。通过创建SFTTrainer (Trainer的一个子类,可以处理我们到目前为止讨论的所有内容)使这个过程变得更加容易。

 from trl import SFTTrainermodel = AutoModelForCausalLM.from_pretrained("EleutherAI/gpt-neo-125m",load_in_4bit=True,device_map="auto",)trainer = SFTTrainer(model,train_dataset=dataset,dataset_text_field="text",torch_dtype=torch.bfloat16,peft_config=peft_config,)trainer.train()

总结

在本文中,介绍了大型语言模型微调过程中出现的一个挑战:如何在单个GPU上进行微调。我们介绍了3种技术来减少内存占用:梯度检查点、LoRA和量化。我们看到了如何通过利用PEFT、BitsAndBytes和Transformers将这些技术应用到我们的代码中。

本文的目标是提供一个深入而简单的视图,利用的现有技术,以便在你的项目中微调自己的llm。

https://avoid.overfit.cn/post/7d68614b936a431a8973ff825091a795

作者:Jeremy Arancio

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

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

相关文章

26 MFC序列化函数

文章目录 Serialize对于存储文件的序列化 Serialize Serialize 是一个在 MFC (Microsoft Foundation Classes) 中常用的函数或概念。它用于将对象的数据进行序列化和反序列化&#xff0c;便于在不同的场景中保存、传输和恢复对象的状态。 在 MFC 中&#xff0c;Serialize 函数…

EdgeBox_tx1_A200 PyTorch v1.9.0 环境部署

大家好&#xff0c;我是虎哥&#xff0c;今天远程帮助几个小伙伴在A200 控制器上安装PyTorch v1.9.0 torchvision v0.10.0&#xff0c;中间也是经历了很多波折&#xff0c;当然&#xff0c;大部分是网络问题和版本适配问题&#xff0c;所以完事后&#xff0c;将自己完整可用的过…

esp32c3 xiao 脚本记录

oled显示网络时间, wifi链接网络 // ntp_get_date.h #include "time.h"String week[8] {"Sun", "Mon", "Tues", "Wednes", "Thur", "Fri", "Sat" };void printLocalTime(Adafruit_SSD1306 …

开源进展 | WeBASE v3.1.0发布,新增多个实用特性

WeBASE是一个友好、功能丰富的区块链中间件平台&#xff0c;通过一系列通用功能组件和实用工具&#xff0c;助力社区开发者更快捷地与区块链进行交互。 目前WeBASE已更新迭代至v3.1.0版本&#xff0c;本次更新中&#xff0c;WeBASE带来了最新的合约Java脚手架导出功能&#xff…

【音视频SDK测评】线上K歌软件开发技术选型

摘要 在线K歌软件的开发有许多技术难点&#xff0c;需考虑到音频录制和处理、实时音频传输和同步、音频压缩和解压缩、设备兼容性问题等技术难点外&#xff0c;此外&#xff0c;开发者还应关注音乐版权问题&#xff0c;确保开发的应用合规合法。 前言 前面写了几期关于直播 …

十四.redis哨兵模式

redis哨兵模式 1.概述2.测试3.哨兵模式优缺点 redis哨兵模式基础是主从复制 1.概述 主从切换的技术方法&#xff1a;当主节点服务器宕机后&#xff0c;需要手动把一台从服务器切换为主服务器&#xff0c;这就需要人工干预&#xff0c;费时费力&#xff0c;还会造成一段时间内服…

单例模式和工厂模式

目录 今日良言&#xff1a;关关难过关关过&#xff0c;步步难行步步行 一、单例模式 1.饿汉模式 2.懒汉模式 二、工厂模式 今日良言&#xff1a;关关难过关关过&#xff0c;步步难行步步行 一、单例模式 首先来解释一下&#xff0c;什么是单例模式。 单例模式也就是单个…

【动态规划刷题 4】礼物的最大价值下降路径最小和

礼物的最大价值 在一个 m*n 的棋盘的每一格都放有一个礼物&#xff0c;每个礼物都有一定的价值&#xff08;价值大于 0&#xff09;。你可以从棋盘的左上角开始拿格子里的礼物&#xff0c;并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值…

以指标驱动,保险、零售、制造企业开启精益敏捷运营的新范式

近日&#xff0c;以“释放数智生产力”为主题的 Kyligence 用户大会在上海前滩香格里拉大酒店成功举行。大会包含上午的主论坛和下午的 4 场平行论坛&#xff0c;并举办了闭门会议、Open Day 等活动。来自金融、零售、制造、医药等行业的客户及合作伙伴带来了超过 23 场主题演讲…

AI驱动的靶点发现综述

疾病建模和靶点识别是药物发现中最关键的初始步骤。传统的靶点识别是一个耗时的过程&#xff0c;需要数年至数十年的时间&#xff0c;并且通常从学术报告开始。鉴于其分析大型数据集和复杂生物网络的优势&#xff0c;人工智能在现代药物靶点识别中发挥着越来越重要的作用。该综…

奥威BI系统:零编程建模、开发报表,提升决策速度

奥威BI是一款非常实用的、易用、高效的商业智能工具&#xff0c;可以帮助企业快速获取数据、分析数据、展示数据。值得特别注意的一点是奥威BI系统支持零编程建模、开发报表&#xff0c;是一款人人都能用的大数据分析系统&#xff0c;有助于全面提升企业的数据分析挖掘效率&…

[Docker实现测试部署CI/CD----自由风格的CI操作[最终架构](5)]

目录 11、自由风格的CI操作&#xff08;最终&#xff09;Jenkins容器化实现方案修改 docker.sock 权限修改 Jenkins 启动命令后重启 Jenkins构建镜像推送到Harbor修改 daemon.json 文件Jenkins 删除构建后操作Jenkins 添加 shell 命令重新构建 Jenkins通知目标服务器拉取镜像目…

C#核心知识回顾——19.插入排序

1.插入排序的基本原理 871542639 两个区域 排序区 未排序区 用一个索引值做分水岭 未排序区元素 与排序区元素比较 插入到合适位置 直到未排序区清空 int[] arr { 8, 6, 7, 2, 9, 4 };//第一步//能取出未排序区…

IL汇编实现两数相加输出结果

话说前面没有实现IL汇编2数相加&#xff1b;鼓捣了一下&#xff0c;实现的代码如下&#xff1b; .assembly extern mscorlib {}.assembly Test{.ver 1:0:1:0}.module test.exe.method static void main() cil managed{.maxstack 8.entrypoint.locals init (int32 V_0, int3…

[FlareOn6]FlareBear

前言 apk的逆向&#xff0c;没有壳&#xff0c;但可能做的不是太多&#xff0c;没能想到整个算法的运作原理 分析 搜索flag会发现存在这么一个函数&#xff0c;那么显示flag的时候应该是熊会跳舞显示flag,只要满足熊happy和ecsstatic就可以&#xff0c;happy只要一直存在点击…

Open3D (C++) 计算矩阵的广义逆

目录 一、算法原理1、广义逆2、计算过程二、代码实现三、结果展示四、参考链接本文由CSDN点云侠原创,原文链接。爬虫网站自重,把自己当个人,爬些不完整的误导别人有意思吗???? 一、算法原理 1、广义逆 非方阵不存在逆,但是存在广义逆(伪逆)。对于一个矩阵

FPGA初步学习之串口发送模块【单字节和字符串的发送】

串口相关简介 UART 在发送或接收过程中的一帧数据由4部分组成&#xff0c;起始位、数据位、奇偶校验位和停止位&#xff0c;如图所示。其中&#xff0c;起始位标志着一帧数据的开始&#xff0c;停止位标志着一帧数据的结束&#xff0c;数据位是一帧数据中的有效数据。 通常用…

springboot-mybatis的增删改查

目录 一、准备工作 二、常用配置 三、尝试 四、增删改查 1、增加 2、删除 3、修改 4、查询 五、XML的映射方法 一、准备工作 实施前的准备工作&#xff1a; 准备数据库表 创建一个新的springboot工程&#xff0c;选择引入对应的起步依赖&#xff08;mybatis、mysql驱动…

【BASH】回顾与知识点梳理(五)

【BASH】回顾与知识点梳理 五 五. 数据流重导向5.1 什么是数据流重导向standard output 与 standard error output/dev/null 垃圾桶黑洞装置与特殊写法standard input &#xff1a; < 与 << 5.2 命令执行的判断依据&#xff1a; ; , &&, ||cmd ; cmd (不考虑指…

骑砍二 ATC MOD 使用教程与应用案例解析

骑砍二 ATC MOD 使用教程与应用案例解析 作者&#xff1a;blibli-财不外漏 / NEXUSMODS-PuepleKarmen 案例MOD依赖&#xff1a;ATC - Adonnay’s Troop Changer & AEW - Adonnay’s Exotic Weaponry & New Armor 文本编辑工具&#xff1a;VS Code&#xff08;推荐使用&…