LLM预备知识——概念、技术基础(基于deepspeed的微调、量化)概念篇2

一、基本概念

  • 显卡:最原始的显卡是集成在主板上的,只完成最基本的信号输出工作,并不进行数据计算,在GPU概念出现后,显卡可以分为集成显卡、独立显卡,且两者都有GPU;
    在这里插入图片描述
  • GPU:图形处理器,是显卡上的一块芯片,早期仅用来做图形渲染任务,后来开始用来执行大规模的并行计算任务;
  • CUDA:是英伟达公司基于自家显卡、基于新的并行编程模型指令集架构开发的通用并行计算架构,其中包含CUDA指令集架构以及GPU内部的并行计算引擎,通过此架构,用户可以充分利用显卡的GPU进行复杂高效的并行计算;【不是所有的GPU都支持CUDA】
  • 显存VRAM集成显卡是显卡集成在主板上的,不能随意更换,独立显卡是显卡作为一个独立的器件插在主板的AGP接口上的,可以随时更换升级,集成显卡使用物理内存,独立显卡有自己的显存;
  • DRAM:动态随机存取存储器
  • CPU:CPU负责逻辑性强的事物处理和串行计算,GPU则专注于执行高度线程化的并行处理任务

二、LLM对齐(优化、微调)技术

使用偏好数据,使得LLM于人类偏好对齐,对模型进行优化。人类偏好可以反映在标注数据、激励模型上。

1、RLHF:基于人类反馈的强化学习(Reinforcement Learning from Human Feedback)。

  • 首先训练一个能反应人工偏好的激励模型,然后使用强化学习微调大的无监督LM来最大化这个估计的激励,而不会偏离原始模型太远。
  • 一般过程:
    ①有监督微调 (supervised fine-tuning,SFT):使用下游任务的高质量数据通过有监督学习的方式对预训练LM进行微调;
    ②用偏好标签标注数据
    ③基于偏好数据训练奖励模型
    ④RL 优化

2、DPO:直接偏好优化(Direct Preference Optimization)

三、LLM微调技术

为了使得预训练模型适应下游任务,需要对预训练模型进行微调,下面是几种常见的微调技术。

1、LoRa

  • 原文:《LoRA: Low-Rank Adaptation of Large Language Models》
  • 过程:
    • W0∈Rd×k为预训练的参数矩阵,∆W是优化过程中W0的累计梯度更新,所以针对下游任务微调预训练模型后,最终的参数矩阵结果应该是W0 + ∆W;
    • LoRa方法则不对W0直接进行微调,而是固定W0不变,将 ∆W做低秩分解,优化器仅对该部分进行优化,即∆W = BA,其中B∈Rd×r,A∈Rr×k,r ≪ min(d,k)为Lora模块中的秩(矩阵的秩相关知识);
    • h = W0x + ∆W x = W0x + BAx
    • 其中A:随机高斯初始化,B:初始化为0矩阵,则△W=BA初始状态为0;
    • 然后使用α/r对△Wx进行缩放,其中α是r中的一个常数;
    • 在使用Adam进行优化时,如果适当调整初始化的规模,那么调整α与调整学习率大致相同;
    • 所以我们将α简单的设置为r中的第一个数,并且不进行微调。在我们对r进行调整时,这种缩放操作可以尽量避免对超参的微调。
      在这里插入图片描述
  • 该方法最重要的贡献是在模型微调过程中和存储时降低了对存储空间的占用,但并没有减少计算量,同时,因没有增加模型大小和深度,并不会产生额外的推理延迟;
  • 原文中使用该方法进行微调时,仅对transformer中的self attention中的参数WQ、WK、WV、WO进行了训练、更新。

p tuning v2、QLoRA、Vicuna、Alpaca、AdaLoRA

四、LLM量化技术

1、常见的大模型精度

1、FP32,即float32,、binary32:

  • 用32位二进制表示浮点数;
    在这里插入图片描述
    • sign是符号位:1位,0表示正数,1表示负数;
    • exponent是指数位:8位,表示最大的数是11111110,即254(指数位有固定的偏置值127,所以指数位实际最大值为254-127);
    • fraction是尾数位:23位。
    • 转换为十进制的计算方式:
      resolution,分辨率,或者精度,约0.000001,精度更高且表示范围更大了
      其中指数位的偏置值是127。
      在这里插入图片描述

2、FP16,即float16、binary16:

  • 用16位二进制表示浮点数;
    在这里插入图片描述
    • sign是符号位:1位,0表示正数,1表示负数;
    • exponent是指数位:5位,表示最大的数是11110,即30(指数位有固定的偏置值15,所以指数位实际最大值为30-15),最小数是00001,即1,(11111,00000这两个有其他含义);
    • fraction是尾数位:10位,其实就是用来表示小数部分;
    • 转换为十进制的计算方式:
      resolution,分辨率,或者精度,约0.001;
      其中指数位的偏置值是15。
      在这里插入图片描述

3、BF16,即bfloat16:

  • 也是用16位二进制表示浮点数;
    在这里插入图片描述
    • sign是符号位:1位,0表示正数,1表示负数;
    • exponent是指数位:8位,表示最大的数是11111110,即254(指数位有固定的偏置值127,所以指数位实际最大值为254-127);
    • fraction是尾数位:7位。
    • 转换为十进制的计算方式:
      resolution,分辨率,或者精度,约0.01,跟fp16相比,精度降低了,但是表示范围变大了
      其中指数位的偏置值是127。
      在这里插入图片描述

4、量化后的模型常见的精度为int4、int8

LLM.int8()

论文原文:LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale

  • bitsandbytes 库;
  • 量化是在模型加载时执行的,无需运行任何后处理或准备步骤。

GPTQ

论文原文:GPTQ: ACCURATE POST-TRAINING QUANTIZATION FOR GENERATIVE PR E-TRAINED TRANSFORMERS

  • AutoGPTQ、Optimum 库
  • 采用 int4/fp16 的混合量化方案,其中模型权重被量化为 int4 数值类型,而激活值则保留在 float16,是一种仅权重量化方法;
  • 在推理阶段,模型权重被动态地反量化回 float16 并在该数值类型下进行实际的运算;
  • 同 OBQ 一样,GPTQ还是从单层量化的角度考虑,希望找到一个量化过的权重,使的新的权重和老的权重之间输出的结果差别最小。
  • GPTQ 量化需要准备校准数据集:GPTQ 将权重分组(如:128列为一组)为多个子矩阵(block)。对某个 block 内的所有参数逐个量化,每个参数量化后,需要适当调整这个 block 内其他未量化的参数,以弥补量化造成的精度损失。

五、训练、微调LLM时的加速器

1、基于accelerate工具中的deepspeed

trl == 0.15.2
accelerate == 1.6.0
deepspeed的Github:https://kkgithub.com/deepspeedai/DeepSpeed
前提是:

  • 安装好上述环境;
  • 根据模型数据格式,准备好alpaca或sharegpt格式数据。

(1)deepspeed的ZeRO优化策略

  • 核心是ZeRO优化策略,即Zero Redundancy Optimizer;
    • stage0:不采用任何内存优化方案,即普通 DDP(传统的数据并行方式),每张卡都加载完整的模型(模型参数、梯度、优化器、激活值都是加载完整的),各自吃一份数据,算一份梯度,最后对梯度进行累加来更新整体模型。vllm默认配置加载模型时就是使用该方式
      在这里插入图片描述

    • stage1:内存优化方案是分片优化器状态:
      在这里插入图片描述

    • stage2:内存优化方案是分片梯度和优化器状态
      在这里插入图片描述

    • stage2 offload:内存优化方案是梯度和优化器状态卸载到CPU

    • stage3:内存优化方案是分片模型参数和激活值、梯度、优化器状态

    • stage3 offload:内存优化方案是模型参数和激活值、梯度、优化器状态卸载到CPU

(2)基于accelerate使用deepspeed加速的实践

accelerate+trl(Transformer Reinforcement Learning)
环境(在linux系统,win10上deepspeed安装困难):
python==3.10
trl==0.15.2
deepspeed
wandb

1、使用trl和peft模块进行普通Lora微调

1、参数解读:

import torch
from peft import LoraConfig, PeftType, TaskType
from datasets import load_dataset
from transformers import AutoTokenizer
from trl import SFTTrainer, SFTConfig, DataCollatorForCompletionOnlyLM


DEVICE = "cuda" if torch.cuda.is_available() else "cpu"

dataset = load_dataset("json", data_files = "data/nl2ksql_data.jsonl", split="train")

tokenizer = AutoTokenizer.from_pretrained("/data2/test/Models/Qwen2.5-3B-Instruct")
tokenizer.pad_token = tokenizer.eos_token # 设置使用结束标识进行padding
response_template = "<|im_start|>assistant\n" # 与模型有关。QWen模型回复都是以该字符串开头的,所以设置该值,后面在计算loss时去掉该部分字符串,仅关心模型真正输出的内容
collator = DataCollatorForCompletionOnlyLM(response_template=response_template, tokenizer=tokenizer)

lora_config = LoraConfig(
    peft_type=PeftType.LORA, # 微调类型Lora
    task_type=TaskType.CAUSAL_LM, # 微调任务:掩码预测
    r=8, # 秩
    target_modules=["q_proj", "v_proj"], # 参与微调的部分是attention模块的Q、V
    lora_alpha=32, # 与梯度有关系,过大的话,容易很快得到想要的效果,但是容易过拟合
    lora_dropout=0.1,
    bias="none"
)

trainer = SFTTrainer(
    model="/data2/test/Models/Qwen2.5-3B-Instruct",
    train_dataset=dataset,
    processing_class=tokenizer,
    data_collator=collator,
    # 配置模型的train参数
    args = SFTConfig(
        run_name="nl2ksql_service_Lora", # 训练过程中,可视化显示的时候用到的名字
        max_seq_length=4096, # 模型的上下文窗口
        learning_rate=3e-5,
        lr_scheduler_type="cosine", # 学习率的主调度策略(涉及LR的预热warmup与退火)。在训练初期,学习率从较低值(如 0 或 1e-7)逐步线性/非线性增加至预设的初始学习率,之后再按该主调度策略(余弦退火)调整‌
        num_train_epochs=3,
        bf16=True,
        per_device_train_batch_size = 1, # 如果设置比较大,会受数据样本分布影响比较大,进而干扰训练
        gradient_accumulation_steps = 4, # 进行1次梯度计算的步数,适用于显存较小的情况,即每4步更新一次梯度(变相的将batch_size增大到4了)
        gradient_checkpointing=False, # 若为True,则使用时间换取空间,即检查点仅保存部分激活值(检查点),其余激活值在反向传播时重新计算,显存占用降至 O(K)(K 为检查点数量)
        logging_steps = 10,
        save_steps=50,
        model_init_kwargs={ # 配置模型加载参数
            "torch_dtype": torch.bfloat16,
            "attn_implementation": "flash_attention_2" if DEVICE == "cuda" else "eager", # linux测试的时候报错没有安装flash_attention_2,且安装不成功,直接注销该设置代码
            "use_cache": False
        },
        output_dir = "outputs/NL2KSQL",
        optim = "adamw_torch", # 指定优化器
        report_to='wandb'
    ),
    # 配置模型的微调method参数
    peft_config=lora_config
)

trainer.train()
trainer.save_model("output/NL2KSQL")

2、进行accelerate配置:

  • 运行下面命令,然后一步步执行即可。
accelerate config
  • 执行完后会生成一个.yaml配置文件。

3、使用accelerate执行上面的微调脚本:

accelerate launch --config_file .yaml配置文件 .py微调脚本
  • 4090显卡,24G显存,deepspeed走stage=3(无offload),1000条数据,epoch=3使用deepspeed微调情况:
    • 微调Qwen2.5-7b在一开始训练时就报内存不足;
    • 微调Qwen2.5-3b在训练到3%时就报内存不足;
    • 能成功微调Qwen2.5-0.5b。
  • 1000条数据,epoch=3,Qwen2.5-0.5b训练到80%时的显卡情况(随着训练时间拉长,内存占用逐渐增多):
    在这里插入图片描述
2、使用trl和peft模块进行基于GRPO的Lora微调

GRPO:Group Relative Policy Optimization,是PPO(Proximal Policy Optimization)的拓展。

  • PPO和GRPO的演示。GRPO放弃了价值模型,而是从组分中估计基线,显著减少了训练资源;
    在这里插入图片描述
  • policy model:输入一段上文,输出下一个token的概率分布。该模型需要训练,是我们最终得到的模型;
  • reference model:与policy model相同,但是在整个过程中不会进行训练更新。作为基准模型,用于维持模型在训练中的表现,防止在更新过程中出现过大偏差;
  • reward model:激励模型,事先用偏好数据已经训练好,用于对Policy model的预测进行打分;
  • value model:通常是一个神经网络,主要任务是估计状态值函数V(s),即在当前策略下,从状态s开始未来能获得的期望累积回报(即状态的价值)。value model需要与policy model进行联合训练。
  • PPO(如下图):
    • KL散度的应用:作用是缓解奖励模型的过度优化。通过对reward model rψ和reference model πref KL散度进行计算;在这里插入图片描述

    • 目标函数:其中At是基于激励{𝑟≥𝑡}和价值函数VΨ(value function)采用GEA(Generalized Advantage Estimation)方式计算的优势函数(Advantage)
      在这里插入图片描述

    • PPO-Clip (裁剪)、PPO-Penalty (惩罚):这种方法使用 KL 散度作为惩罚项(KL 散度衡量了两个概率分布之间的差异),来约束新旧策略之间的差异。

  • GRPO(如下图):
    • GRPO取消使用value model(其训练规模与policy model基本相当,会增加很大计算负担和资源消耗),使用下面方式替代:针对每个问题q,生成一组输出o1…G,计算每组的激励均值 𝐴ˆi,t
      在这里插入图片描述

    • KL散度:
      在这里插入图片描述

    • 目标函数:
      在这里插入图片描述

trl.GRPOTrainer中的GRPOConfig参数解读(未完待续):

  • num_generations:
    • 当前每个提示词的生成次数。
    • 能够整除globle_batch_size。(globle_batch_size=num_processes x per_device_train_batch_size)

GRPO出现原文《DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值