大模型预备知识

本文记录了解大模型技术前可以先学习的细小知识点。

🔥注意力机制

注意力机制计算

  • QKV都是输入的矩阵,可以理解为推理时,拿着Q去查找每个词的K,根据QK相似度的不同从而得到一个权重矩阵,再取K对应的V进行矩阵乘法计算。
  • 多头注意力(MHA)本质上就像CNN里面的多通道,学习不同方面的特征,有多组的QKV经过各自结构一致的网络共同整合得到整个模块的输出。
    各种多头注意力
  • 多查询注意力机制(MQA)是MHA的变体,所有注意力头直接共享同一组KV,相当于减少了参数。
  • 组查询注意力(GQA)将MHA和MQA进行折中,也就是若干个头一组共享KV。

🔥KV Cache

KV Cache是一种优化技术,用于提升模型推理效率,通过缓存K、V减少重复计算,从而加速解码器的矩阵运算。DeepSeek里面的MLA等MHA变体都是基于该技术进行改进的。

  • KV Cache仅用于推理阶段,该阶段KV保持不变,因此可以缓存;
  • KV Cache只存在于解码器,因为解码器生成时需要多次运算矩阵;
  • KV Cache因为缓存了KV,会加速Q@K@V的矩阵乘法运算,提高推理速度;
  • KV Cache的缺点是会增加内存占用。

不使用KV Cache的注意力

  • 如上图,每次自回归时,之前的QKV三者本身的生成、Q与所有K相乘的结果、QKV计算的结果都是要重复计算的,因此会增加推理时间。

KV Cache

  • 如上图每次自回归时,QK相乘得到结果与V矩阵相乘,紫色部分是过去的K、V,它们本来会被新的K、V所覆盖,但是每一步计算都要用到,需要重新生成,把它们缓存下来就能得到很明显的性能提升。

  • 为什么没有Q Cache,如上图,因为Q每次都是新生成的那个token的Q与所有的K进行相乘,根本用不到之前的Q,也就不用缓存了。

  • QK相乘结果也不缓存,因为过去的QK和Q一样也是不需要用于生成新的token的。

由于内存限制,LLM的回复能力确实会随着问答的进行逐渐变差,这种现象可以理解为模型具备一定的“遗忘性”。具体而言,由于硬件资源的限制,KV Cache的大小是有限的。当缓存达到其容量上限时,旧的信息可能会被新的信息覆盖或丢弃。其表现为随着问答的进行,早期的对话内容可能会因为KV Cache的容量限制而被移除或覆盖,导致模型逐渐“遗忘”之前的上下文。由于模型无法访问完整的对话历史,其生成的回复可能会变得不够准确或连贯,尤其是在需要依赖早期信息的情况下。所以,在长对话或多轮问答中,模型的性能可能会显着下降,因为它无法有效地利用整个对话历史。

🔥精度

FP32/FP16/BF16

fp32、fp16、bf16分别指单精度浮点数(float32)、Intel提出的半精度浮点数(float16)、nvidia提出的半精度浮点数(bfloat16)。名字当中的数字就对应了该种浮点数表示方法所占的bit数,那么fp16和bp16的存储空间天然就是fp32的一半。

精度示意图

  • 以fp16为例,它占有16bit(2字节),其中5bit用来表示指数位(表示2的幂次),10bit用来表示小数位(也叫尾数位,表示浮点数的有效数字部分),还有一个符号位。5个指数位本来可以表示00000-11111,但是全0和全1有特殊含义,换算成10进制也就是1至30,减去偏置15、能表示的正负区间为-14至15,尾数位可以表示1.0000000000至1.1111111111(此时尾数位前的隐藏项为1),换算成10进制也就是[1, 2)。
  • 指数位全0表示非规格数,也就是+0、-0, 以及非常靠近0的数, 比如1E-38。
  • 指数全1表示特殊数,有Inf和NaN两种情况。小数位全0表示无穷大inf,根据符号位不同可以分为+inf和-inf。小数位不全为0,表示NaN。
  • 规格数和非规格数拼接起来才是一个完整的取值范围。
  • 所以fp16最大是 二进制 ±1.1111111111×2^11110 => 十进制±(1+1023/1024)×2^15 = ±65504;最小是1/2024×2^(-14) = 5.96E−8。也就是fp16的动态范围为(5.96E−8 ~ 65504)
  • fp32(8指数+23尾数+1符号)的动态范围为(1.4E-45 ~ 3.40E38),bf16相当于尾数位为7的fp32,动态范围是(9.2E−41~3.38E38),也就是牺牲精度换来高取值范围

混合精度

混合精度就是通过一些设计,使得我们可以享受半精度的优点,又可以一定程度规避数据溢出和舍入误差(超小值被忽略导致误差)。

  • 我们使用 fp32 权重作为精确的 “主权重 (master weight)”进行备份,而其他所有值(weights,activations, gradients)均使用 fp16 进行计算以提高训练速度,最后在梯度更新阶段再使用半精度的梯度更新单精度的主权重,这样当很小的梯度乘上学习率后要跟权重(fp32的)做运算时,就不会被舍弃了。
  • 由于 fp16 混合精度大大减少了内存需求, 并可以实现更快的速度, 因此只有在在此训练模式下表现不佳时, 才考虑不使用混合精度训练。

🔥新的激活函数

新的激活函数
下列公式中 σ 即 sigmoid 函数,σ(x) = 1 / (1 + exp(-x)) 。

  1. GLU:GLU(x) = (xW + b) ⊗ σ(xV + c);
  2. Swish:Swish(x) = x · σ(x);
  3. SwiGLU:SwiGLU(x, W, V, b, c) = Swish_1(xW + b) ⊗ (xV + c) ,其中Swish_β(x) = x σ(β x),⊗是哈达玛积(对位元素乘);

    SwiGLU函数可以提升性能,有可微性、自适应性(因为GLU是一种类似于长短期记忆网络(LSTM)带有门机制的网络结构,通过门机制控制信息通过的比例,来让模型自适应地选择哪些单词和特征对预测下一个词有帮助)。

  4. GELU:GELU(x) = x · P(X⩽x) = x · Φ(x),其中Φ(x)表示正态分布的累积分布函数。

🔥归一化

归一化公式

  • 归一化统一公式,包括了归一化和仿射变换(γx+β)。
    BN和LN
  • Batch Normalization和Layer Normalization的区别:BN就是对整个batch的数据的特征进行逐一归一化,也就是每个特征各自归一化。LN则是单个样本的所有特征进行归一化。
  • RMSNorm:对x求均方根,然后直接仿射变换,不计算方差和均值,降低计算量。
    DyT公式
  • Dynamic Tanh:将x映射到tanh,获得非线性能力,α也是可学习的动态缩放因子。可以大大提升计算效率,性能指标大差不差甚至可能提升。是传统归一化的很好替代方案。

🔥位置编码

正弦编码

正弦编码的问题

  • t是绝对位置,w是频率(w=b^(-2d/D))。
  • 最早transformer的位置编码是正弦编码,通过正余弦公式为序列不同位置编码,然后加上嵌入向量就是最终的表达。
    正弦编码可视化
  • 但对于高维度特征的相对位置的表达能力不足。位置编码点积仅仅与相对位置k相关,但通过注意力计算后矩阵乘法会削弱这种关系。上图中高纬度的编码区别已经不是很大了。
  • 所以好的位置编码要 1、每个位置编码唯一;2、有良好的外推性(训练时短,推理时仍然能应对长的);3、不同句子相同相对位置时,相关性应该一致。

RoPE

RoPE

  • 旋转位置编码RoPE:m,n是两个不同位置的token,对每个token计算q、k,然后分别与旋转矩阵进行乘法(第一个式子),最后两者相乘(第二个式子),也就是直接将旋转位置编码直接参与注意力计算,而非在嵌入层(embedding)进行编码后再与原向量相加。
  • θ=b^(-2d/D),b是固定的基数,d是当前隐藏层所处维度(也就是特征向量的特征索引),D是隐藏层维度,这样高维度旋转慢,负责捕捉长距离信息;低维度旋转快,负责捕捉细节。
  • 可以看到两个位置的RoPE计算点积时自带相对距离m-n,对相对位置敏感。
    RoPE可视化
  • 如上图,对于长文本,旋转位置编码在词附近会提供更多注意力,远离词时也能有固定震荡的注意力。
  • RoPE外推限制性:每个位置的θ在预训练时是固定的,θ整体的分布也是固定的(b不变),无法适应更长的序列,需要采取插值的方法应对。
  • RoPE插值:
    1. 线性插值PI:通过比例缩放L/L'将位置(公式中m、n)从预训练的序列长度L映射到新的序列长度L’。
    2. 非线性插值NTK-Aware scaled:改变θ公式中的基数b。
    3. 基于波长局部分段插值NTK-by-parts:计算波长λ=2π/θ,根据序列与波长比值L/λ,对不同维度进行不同策略插值。
    4. Yarn:在3的基础上引入温度因子t,在计算注意力时qk点积除以一个t,来缓解插值对点积结果增大影响长上下文理解的问题。

🔥强化学习

PPO

  • 四个模型:actor(要训练的模型)、critic(预测当前动作的价值)、reward(计算当前价值)、reference(和actor初始化一致,防止模型训练偏离原模型太远)。RLHF过程中reward/reference两个模型不会更新
  • reward相当于critic的学习目标,reference则是actor的约束(也算是目标之一吧)。
    actor的损失函数
  • actor的损失:如上,内核是优势Adv,它表示critic和reward的输出的差值,也就是实际收益和预测收益之差。P代表概率,收益越高的动作,其概率应该越高。Pold表示参考模型的概率,奖励的计算还引入KL散度,是为了不让模型变化太大。clip是裁切,控制上下限。
  • 总的来说,actor的损失就是:在critic对价值(Vt)的预测准确的前提下,尽可能采取最高价值的动作,并且不太偏离原模型。
  • critic的损失:就是优势的平方。

DPO

  • DPO直接偏好对齐,本质上是SFT的分类任务而不是强化学习,去掉了奖励模型和强化学习过程,直接使用偏好数据来微调。
    DPO
  • DPO的损失如上,E是期望,β是超参数控制正则化强度,π是模型输出的概率,yw和yl代表偏好数据对(也就是两个回答),w是更好的结果,l是更差的。其它和PPO类似,也是要用KL散度。
  • DPO的目标就是通过似然函数尽可能拉大模型对两个答案的评价,以让模型区分回答的好与坏。

GRPO

GRPO

  • 相比于PPO,GRPO结构可以说相当简单,GRPO在PPO的基础上去掉了critic模型,也就是不预测V,而是一次采样多个输出,得到多个奖励r,统计均值和标准差,用来计算优势A,Ai=(ri-均值)/标准差。
  • 还可以看到,PPO的KL散度在计算r的时候算到(也就是PPO把KL当做奖励的一部分),而GRPO的散度在损失函数算到(也就是GRPO把KL当做损失的一部分)。
    GRPO损失函数
  • 损失函数如上,来自两部分,第一部分就是min,也就是PPO的公式;第二部分则是KL散度。
  • 两句话小结GRPO:
    1. GRPO是什么:把A看成一种评分的话,该算法就是让奖励模型给LLM的输出直接评分,评分直接计算损失(只不过依然要考虑不能过度偏离参考模型)。
    2. GRPO和PPO的区别:有/无critic模型(这改变了A的算法)、KL散度计算位置不一样。这两个也是损失函数的区别。

🔥思维链CoT

  • 标准思维链,在提供模型题目和答案的同时,需要编写得出答案的思考过程,过程可能是链式的逐步思考,也可能每一步都有多种假设。需要手工撰写,比较费力。

Zero-Shot CoT

  • Zero-Shot CoT:不需要撰写思考过程,而是告诉模型“逐步思考”即可,模型就会自己去完成思考过程。然后把思考过程拼接到问题上,模型给出最后的答案。无需标注,但这比较依赖模型本身的思考能力。

Auto CoT

  • Auto CoT:就是提供few-shot的zero-shot CoT
    1. 在zero-shot CoT的基础上实现,首先有一个问题库存放所有问题,使用Bert对问题进行表达,然后对表达进行K-means聚类。
    2. 当用户输入问题,可以从聚类中找到类似的问题,对这些问题进行zero-shot CoT生成prompt,然后和用户问题进行拼接后给予“逐步思考”的提示即可。
    3. 该方法依然不需要标注,性能超过one-shot CoT。

思维树ToT

  • 思维树ToT:三思而后行
    1. 拆解:将复杂问题拆分成多个简单子问题,每个子问题的解答过程对应一个
      思维过程。
    2. 衍生:模型需要根据当前子问题生成可能的下一步推理方向。衍生有两种模式:样本启发和命令提示。
    3. 评估:利用模型评估推理节点合理性。根据任务是否便于量化评分,选择投票或打分模式。
    4. 搜索:从一个或多个当前状态出发,搜索通往问题解决方案的路径。可以用广度优先、深度优先、A*等算法实现。

Self-Consistency

  • Self-Consistency:集思广益,引入多样推理路径,也就是思考多次,最终选择最合适的答案。该方法可以与其它CoT方法一起使用。
  • 步骤很简单,模型生成多个思维链答案,然后统计每个答案的频率,选择最高的那个作为最终答案。
  • Universal Self-Consistency:对于无法给出一个确切答案的问题(摘要等),可以整合答案让大模型自己选。
  • Copyrights © 2023-2025 LegendLeo Chen
  • 访问人数:2100 | 浏览次数:3858

请我喝杯咖啡吧~

支付宝
微信