当前位置:   article > 正文

LoRA大模型加速微调和训练算法解读_lora增量微调

lora增量微调

理论

Lora( Low-Rank Adaotation),低秩自适应模型微调的方法,它冻结预训练模型的权重,并将可训练的秩分解矩阵注入到transformer架构的每一层,从而大大减少下游任务的可训练参数的数量,

怎么微调下游任务:利用LoRA对下游任务数据训练时,只通过训练新加部分的参数来适配下游任务,当训练好新的参数后,将新的参数与老的参数合并,利用重参的方式,这样既能在新的任务上达到fine-tune整个效果,又不会在模型推理中增加耗时。

效果

以GPT3为例,Lora可以将训练参数的数量减少10000倍,GPU内存需求减少3倍,LoRA在RoBertTa,GPT2和3上的模型推理结果表现的于微调相当或者更好,并且多加网络层,所以推理没有延时

实现的方法

1 低秩参数化更新矩阵

神经 网络 包 含许 多 执行 矩阵 乘 法的 密集 层 。这 些层 中 的权 重 矩阵 通常 是 满秩 的。 在 适应 特定 任 务时, ag hajya n 等 人 (202 0)表 明 , 预 训练 的 语 言模 型 具 有较 低 的“内 在 维度 ” , 尽管 随 机 投影 到 较小的 子空 间 ,但 仍 然可 以有 效 地学 习。 受 此启 发, 我 们假 设 在适 应过 程 中对 权重 的 更新 也具 有 较低的 “内 在秩 ”。 对于 预训 练的 权重 矩阵 W0 ∈ Rd ×k, 我们 用低 秩分 解 W 0 +∆ W = W 0 + BA 来表示它的 更新 ,其 中 B∈ Rd× r, a∈ Rr× k, 秩 R ? min(d, k) .在 训练 期间 , Wis 冻结 0 并 且不 接收 梯度 更新 ,而 A 和 B 包 含可 训练 的参 数。 注意 两个 Wa nd∆ W = B A0 乘以 相同 的输 入, 它们 各自 的输 出向 量按坐标求和。对于 h = Wx,修正 后的 正 向 0 传递 收益 率为 :

原始权重矩阵W0加上微调权重∆ W,两边参数相加就是整个模型,整个过程通俗来讲,蓝色部分为预训练的模型参数,LoRA在预训练号的模型结构旁加上一个分支,这个分支包含A,B两个结构,这两个参数分别初始化为高斯分布和0,在训练刚开始,附加的参数就是0,A的输入维度和B的输出维度分别与原始模型的输入输出维度相同,而A的输出维度和和B的输入维度是一个远小于原始模型输入输出维度的值,这样做就可以极大的减少待训练的参数,在训练时只更新A,B的参数,预训练好的模型参数固定不变,将AB与原始模型参数矩阵W合并,这样九不会在推断中引入额外的计算,对于不同的下游任务只需要在预训练模型的基础上重新训练A,B就可以。计算的过程例如,原始矩阵是512 * 512,假设r=4,现有用512 * 4的矩阵进行降维,最后的输出再用4 * 512 还原维度,r也可以取值为512,相当于对fine -tuing

作者通过实验表明,lora施加到transformer中的q,k,v,和原始矩阵w上时候,出现的效果是做好的,并且r=4时候效果是最好的。

总结:

冻结预训练模型权重,并将可训练的秩分解矩阵注入到Transformer层的每个权重中,大大减少了下游任务的可训练参数数量

LoRA 几个关键优势

1,预训练模型可以共享,例如Chinese-LLaMA或者stable-difficution ,为不同的任务构建许多小型的LoRA模块,我们可以通过替换矩阵A和B来共享冻结模型并有效切换任务,从而降低存储需要和任务切换开销,

2,当使用Adam这种自适应优化器时候,LoRA可以让训练更有效,并将硬件门槛减低3倍,LORA不需要计算梯度或者维护大多数参数的优化器状态,只需要优化注入,小的多的低秩矩阵

3,LoRA简单的显示设计允许在部署时将训练矩阵与冻结的权重合并,没有多加层,与完全微调模型相比,没有推理延时,

代码

https://github.com/microsoft/LoRA/tree/main

以Linear层为例:

  1. ```Plain Text
  2. class Linear(nn.Linear, LoRALayer):
  3. # LoRA implemented in a dense layer
  4. def __init__(
  5. self,
  6. in_features: int,
  7. out_features: int,
  8. r: int = 0,
  9. lora_alpha: int = 1,
  10. lora_dropout: float = 0.,
  11. fan_in_fan_out: bool = False, # Set this to True if the layer to replace stores weight like (fan_in, fan_out)
  12. merge_weights: bool = True,
  13. **kwargs
  14. ):
  15. nn.Linear.__init__(self, in_features, out_features, **kwargs)
  16. LoRALayer.__init__(self, r=r, lora_alpha=lora_alpha, lora_dropout=lora_dropout,
  17. merge_weights=merge_weights)
  18. self.fan_in_fan_out = fan_in_fan_out
  19. # Actual trainable parameters
  20. if r > 0:
  21. self.lora_A = nn.Parameter(self.weight.new_zeros((r, in_features))) # 用0初始化A的权重,forward中会进行转置
  22. self.lora_B = nn.Parameter(self.weight.new_zeros((out_features, r))) # 用0初始化B的权重,forward中会进行转置
  23. self.scaling = self.lora_alpha / self.r
  24. # Freezing the pre-trained weight matrix
  25. self.weight.requires_grad = False # 冻结预训练的权重
  26. self.reset_parameters()
  27. if fan_in_fan_out: # False
  28. self.weight.data = self.weight.data.transpose(0, 1)
  29. def reset_parameters(self):
  30. nn.Linear.reset_parameters(self)
  31. if hasattr(self, 'lora_A'): # 再次初始化A的权重
  32. # initialize A the same way as the default for nn.Linear and B to zero
  33. nn.init.kaiming_uniform_(self.lora_A, a=math.sqrt(5))
  34. nn.init.zeros_(self.lora_B)
  35. def train(self, mode: bool = True):
  36. def T(w):
  37. return w.transpose(0, 1) if self.fan_in_fan_out else w
  38. nn.Linear.train(self, mode)
  39. if mode:
  40. if self.merge_weights and self.merged:
  41. # Make sure that the weights are not merged
  42. if self.r > 0:
  43. self.weight.data -= T(self.lora_B @ self.lora_A) * self.scaling
  44. self.merged = False
  45. else:
  46. if self.merge_weights and not self.merged:
  47. # Merge the weights and mark it
  48. if self.r > 0:
  49. self.weight.data += T(self.lora_B @ self.lora_A) * self.scaling
  50. self.merged = True
  51. def forward(self, x: torch.Tensor):
  52. print("self.merged: ", self.merged)
  53. def T(w):
  54. return w.transpose(0, 1) if self.fan_in_fan_out else w
  55. if self.r > 0 and not self.merged:
  56. result = F.linear(x, T(self.weight), bias=self.bias) # 原来的输出
  57. result += (self.lora_dropout(x) @ self.lora_A.transpose(0, 1) @ self.lora_B.transpose(0, 1)) * self.scaling
  58. return result
  59. else:
  60. return F.linear(x, T(self.weight), bias=self.bias)
  61. ```

代码分步解读:

1、创建lora_A和lora_b

  1. self.lora_A = nn.Parameter(self.weight.new_zeros((r, in_features))) # 用0初始化A的权重,forward中会进行转置
  2. self.lora_B = nn.Parameter(self.weight.new_zeros((out_features, r))) # 用0初始化B的权重,forward中会进行转置

2、对lora_A和lora_b参数初始化

  1. def reset_parameters(self):
  2. nn.Linear.reset_parameters(self)
  3. if hasattr(self, 'lora_A'): # 再次初始化A的权重
  4. # initialize A the same way as the default for nn.Linear and B to zero
  5. nn.init.kaiming_uniform_(self.lora_A, a=math.sqrt(5))
  6. nn.init.zeros_(self.lora_B)

3、通过合并lara_a和lora_b以及预训练模型饿参数,部署时相当于没有增加额外的层:

  1. if mode:
  2. if self.merge_weights and self.merged:
  3. # Make sure that the weights are not merged
  4. if self.r > 0:
  5. self.weight.data -= T(self.lora_B @ self.lora_A) * self.scaling
  6. self.merged = False
  7. else:
  8. if self.merge_weights and not self.merged:
  9. # Merge the weights and mark it
  10. if self.r > 0:
  11. self.weight.data += T(self.lora_B @ self.lora_A) * self.scaling
  12. self.merged = True

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/277614
推荐阅读
相关标签
  

闽ICP备14008679号