LLM Tutorial 2. Fine tune
预训练部分先往后推推,从Fine tunning讲起
大模型往往预训练于比较general的数据集。当运用到实际场景时,大模型往往会遇到没见过数据集而回答混乱的情况,因此需要Fine tune网络来提升模型的性能。
按照以往的Full Fine tune方法(如添加适应层,优化参数等),需要从头到尾再训练一次大模型,这对于动则几十B的大模型来说并不现实。
1. LoRA
低秩自适应技术(Low Rank Adaptation)https://arxiv.org/abs/2106.09685 是一种通过使用低维结构近似大模型的高维结构,以降低模型复杂性的技术。概括来说就是在预训练模型的权重矩阵中添加一个低秩矩阵,冻结原有预训练权重,只训练新添加低秩矩阵,使得模型能够更有效地学习特定于任务的信息
Aghajanyan等人的研究表明,在某些神经网络训练过程中,预训练模型有较低的”内在维度(instrisic dimension)“,仅仅由这些内在维度,就能够较好地描述大模型的特征,意即用较低维度的数据代表更高维度的数据,并由此降低矩阵运算的成本。LoRA的思想即通过优化网络中低维度少量参数的形式,提高模型的泛化能力

具体而言就如上图,在某一层训练好的预训练权重旁,增加一个旁路
, 借鉴PCA的思想,将
分解为维度较低的矩阵A和B,举个例子,假设该层预训练权重有
,可以用两个分别为
和
的矩阵AB模拟预训练权重的低秩特性
代码实现:
class LoRALayer(torch.nn.Module):
def __init__(self, in_dim, out_dim, rank, alpha):
super().__init__()
std_dev = 1 / torch.sqrt(torch.tensor(rank).float())
self.A = torch.nn.Parameter(torch.randn(in_dim, rank) * std_dev)
self.B = torch.nn.Parameter(torch.zeros(rank, out_dim))
self.alpha = alpha
def forward(self, x):
x = self.alpha * (x @ self.A @ self.B)
return x
python

from functools import partial
# default hyperparameter choices
lora_r = 8
lora_alpha = 16
lora_dropout = 0.05
lora_query = True
lora_key = False
lora_value = True
lora_projection = False
lora_mlp = False
lora_head = False
layers = []
assign_lora = partial(LinearWithLoRA, rank=lora_r, alpha=lora_alpha)
for layer in model.distilbert.transformer.layer:
if lora_query:
layer.attention.q_lin = assign_lora(layer.attention.q_lin)
if lora_key:
layer.attention.k_lin = assign_lora(layer.attention.k_lin)
if lora_value:
layer.attention.v_lin = assign_lora(layer.attention.v_lin)
if lora_projection:
layer.attention.out_lin = assign_lora(layer.attention.out_lin)
if lora_mlp:
layer.ffn.lin1 = assign_lora(layer.ffn.lin1)
layer.ffn.lin2 = assign_lora(layer.ffn.lin2)
python

2. QLoRA
QLoRA在LoRA基础上引入了量化策略,进一步节省内存。
在QLoRA中,模型的原始预训练权重被量化到4位浮点数(4bit normal Float NF4),并在微调过程中保持固定(冻结)。然后在微调过程中引入了少量可训练参数,这些参数以低秩适配器的形式存在。这些适配器以32位浮点格式训练,以适应预训练模型到特定任务。
量化是一个用有限的离散符号集或整数值集来近似一个连续值集的过程。在4位正常浮点数的情况下,模型的权重从32位浮点数压缩到4位整数。为了减少精度降低带来的损失,QLoRA采用了双重量化技术

这个过程包含两步操作。第一部分涉及输入X(BF16)通过一个函数进行处理,该函数使用量化因子C1(FP32)和C2(FP8)以及权重W(NF4)执行双重反量化。这个操作的输出是Bfloat16格式,保留了训练过程所需的精度。
操作的第二部分涉及对输入X(BF16)的另一次操作,这次是乘以权重的低秩近似L1(BF16)和L2(BF16)。这部分不涉及量化和反量化,因为LORAs保持未量化状态,并将网络的其余部分保持在4位表示中。
这两部分的结果相加得到最终输出Y(BF16)。因此,这个方程式说明通过双重量化和低秩近似的结合,在训练过程中显著减少了内存占用,实现了计算效率与精度的平衡。
