当前位置:   article > 正文

关于lora的理解_lora parent target target_name

lora parent target target_name
非常推荐看参考中的文章,对lora的原理和代码,包括细节都讲得很清楚!

 参考:【OpenLLM 007】大模型炼丹术之小参数撬动大模型-万字长文全面解读PEFT参数高效微调技术 - 知乎 (zhihu.com)图解大模型微调系列之:大模型低秩适配器LoRA(原理篇) - 知乎 (zhihu.com)

一、为什么选lora

目前主流的微调方法:全参数微调、Adapter Tuning、Prefix Tuning和LoRA。

全参数微调太贵,没钱玩不起。Adapter Tuning是往大模型中额外的模块,会导致训练和推理的延迟。Prefix Tuning是在输入添加前缀,导致减少有效数据长度

二、lora的原理

对比可以看出,lora把全参数微调的\Delta W用矩阵A和B代替了。

lora的公式如下:

d = Wx + \frac{\alpha }{r}BAx

r:秩,r越大代表当前的A和B矩阵参数越多,也就越接近全参数微调,但是带来的噪声也就越多。

A:shape为[r, d],用高斯初始化。

B:shape为[d, r],初始化为0。

\alpha/r:缩放比例,lora的作者发现数据在经过B但是还没有经过激活层时的数值的波动幅度与r有相关性,所以需要除以r来消除。\alpha可以解释为大模型对新知识的侧重,越大就越重视。两个结合在一起就是\alpha/r。

对A和B初始化是为了在训练刚开始不给模型带来额外的噪声(就是数据经过AB值为0)。参考文章的作者去问了lora的作者得到答复如下:

r的取值:lora作者通过实验得到r的取值设为4,8的效果跟64差不多,甚至要好一点

三、peft的lora代码

这是我根据自己理解画的图,可能不大详细,有什么问题欢迎提出来。

下面是与lora有关的函数的添加注释版本,源代码太长了,我把一些代码用pass代替:

inject_adapter函数,调用函数创建和替换lora模块,冻结参数

  1. def inject_adapter(self, model: nn.Module, adapter_name: str):
  2. r"""
  3. 创建适配器层并用适配器层替换目标模块。
  4. This method is called under the hood by `peft.mapping.get_peft_model` if a non-prompt tuning adapter class is passed.
  5. The corresponding PEFT config is directly retrieved from the `peft_config` attribute of the BaseTuner class.
  6. Args:
  7. model (`nn.Module`):
  8. The model to be tuned.
  9. adapter_name (`str`):
  10. The adapter name.
  11. """
  12. # 得到lora参数
  13. peft_config = self.peft_config[adapter_name]
  14. self._check_new_adapter_config(peft_config)
  15. is_target_modules_in_base_model = False
  16. # 得到baichuan大模型的key
  17. key_list = [key for key, _ in model.named_modules()]
  18. model_config = getattr(model, "config", {"model_type": "custom"})
  19. if hasattr(model_config, "to_dict"):
  20. model_config = model_config.to_dict()
  21. peft_config = self._prepare_adapter_config(peft_config, model_config)
  22. for key in key_list:
  23. # 只有后缀是down_proj,up_proj,W_pack,gate_proj,o_proj才进行替换
  24. if not self._check_target_module_exists(peft_config, key):
  25. continue
  26. is_target_modules_in_base_model = True
  27. # 从模型中获取指定的子模块,返回子模块的父模块,子模块,子模块名
  28. parent, target, target_name = _get_submodules(model, key)
  29. optionnal_kwargs = {
  30. "loaded_in_8bit": getattr(model, "is_loaded_in_8bit", False),
  31. "loaded_in_4bit": getattr(model, "is_loaded_in_4bit", False),
  32. "current_key": key,
  33. }
  34. # 替换
  35. self._create_and_replace(peft_config, adapter_name, target, target_name, parent, **optionnal_kwargs)
  36. self._mark_only_adapters_as_trainable()
  37. if self.peft_config[adapter_name].inference_mode:
  38. for n, p in self.model.named_parameters():
  39. if adapter_name in n:
  40. p.requires_grad = False

_create_and_replace函数:创建和替换lora模块

  1. def _create_and_replace(
  2. self,
  3. lora_config,
  4. adapter_name,
  5. target,
  6. target_name,
  7. parent,
  8. **optionnal_kwargs,
  9. ):
  10. bias = hasattr(target, "bias") and target.bias is not None
  11. kwargs = {
  12. # lora的秩,矩阵A和矩阵B相连接的宽度,r<<d
  13. "r": lora_config.r,
  14. # 归一化超参数,减少改变r rr时需要重新训练的计算量
  15. "lora_alpha": lora_config.lora_alpha,
  16. # lora层的dropout比率
  17. "lora_dropout": lora_config.lora_dropout,
  18. # 只有应用在Conv1D层时置为True,其他情况False
  19. "fan_in_fan_out": lora_config.fan_in_fan_out,
  20. # 布尔值,是否初始化权重
  21. "init_lora_weights": lora_config.init_lora_weights,
  22. }
  23. kwargs["loaded_in_8bit"] = optionnal_kwargs.pop("loaded_in_8bit", False)
  24. kwargs["loaded_in_4bit"] = optionnal_kwargs.pop("loaded_in_4bit", False)
  25. kwargs["bias"] = bias
  26. quantization_config = get_quantization_config(self.model, method="gptq")
  27. if quantization_config is not None:
  28. kwargs["gptq_quantization_config"] = quantization_config
  29. if isinstance(target, LoraLayer) and isinstance(target, torch.nn.Conv2d):
  30. pass
  31. elif isinstance(target, LoraLayer) and isinstance(target, torch.nn.Embedding):
  32. pass
  33. elif isinstance(target, LoraLayer):
  34. pass
  35. else:
  36. # 创建新的module
  37. new_module = self._create_new_module(lora_config, adapter_name, target, **kwargs)
  38. # 替换
  39. self._replace_module(parent, target_name, new_module, target)

_create_new_module:创建新模块,如果是8bit训练新模块为Linear8bitLt,4bit训练新模块为Linear4bitLt,否则为Linear。

  1. def _create_new_module(lora_config, adapter_name, target, **kwargs):
  2. # 值为None
  3. gptq_quantization_config = kwargs.get("gptq_quantization_config", None)
  4. # 根据量化配置文件获取正确的 AutoGPTQQuantLinear 类,因为配置文件是None,所以AutoGPTQQuantLinear也是None
  5. AutoGPTQQuantLinear = get_auto_gptq_quant_linear(gptq_quantization_config)
  6. loaded_in_8bit = kwargs.pop("loaded_in_8bit", False)
  7. loaded_in_4bit = kwargs.pop("loaded_in_4bit", False)
  8. bias = kwargs.pop("bias", False)
  9. if loaded_in_8bit and isinstance(target, bnb.nn.Linear8bitLt):
  10. eightbit_kwargs = kwargs.copy()
  11. eightbit_kwargs.update(
  12. {
  13. "has_fp16_weights": target.state.has_fp16_weights,
  14. "memory_efficient_backward": target.state.memory_efficient_backward,
  15. "threshold": target.state.threshold,
  16. "index": target.index,
  17. }
  18. )
  19. new_module = Linear8bitLt(
  20. adapter_name, target.in_features, target.out_features, bias=bias, **eightbit_kwargs
  21. )
  22. elif loaded_in_4bit and is_bnb_4bit_available() and isinstance(target, bnb.nn.Linear4bit):
  23. pass
  24. elif AutoGPTQQuantLinear is not None and isinstance(target, AutoGPTQQuantLinear):
  25. pass
  26. elif isinstance(target, torch.nn.Embedding):
  27. pass
  28. elif isinstance(target, torch.nn.Conv2d):
  29. pass
  30. else:
  31. pass
  32. return new_module

替换模块

  1. def _replace_module(parent, child_name, new_module, child):
  2. """
  3. 将父模块中的子模块替换为新模块,同时设置新模块的属性与子模块相同。
  4. 参数:
  5. parent:父模块
  6. child_name:子模块名称
  7. new_module:新模块
  8. child:子模块
  9. """
  10. # 设置新模块为父模块的子模块
  11. setattr(parent, child_name, new_module)
  12. # 设置新模块的权重属性与子模块相同
  13. new_module.weight = child.weight
  14. # 如果子模块存在偏差属性,且偏差不为空,则设置新模块的偏差属性与子模块相同
  15. if hasattr(child, "bias"):
  16. if child.bias is not None:
  17. new_module.bias = child.bias
  18. # 如果子模块存在状态属性,设置新模块的状态属性与子模块相同,并将新模块移动到与子模块权重属性相同的设备上
  19. if getattr(child, "state", None) is not None:
  20. new_module.state = child.state
  21. new_module.to(child.weight.device)
  22. # 将新模块中的特定模块移动到与子模块权重属性相同的设备上
  23. for name, module in new_module.named_modules():
  24. if "lora_" in name:
  25. module.to(child.weight.device)
  26. if "ranknum" in name:
  27. module.to(child.weight.device)

Linea8bits class

  1. class Linear8bitLt(bnb.nn.Linear8bitLt, LoraLayer):
  2. # Linear8bitLt类继承自bnb.nn.Linear8bitLt类和LoraLayer类
  3. def __init__(
  4. self,
  5. adapter_name, # 适配器名称
  6. in_features, # 输入特征数量
  7. out_features, # 输出特征数量
  8. r: int = 0, # r值(Lora的参数)
  9. lora_alpha: int = 1, # Lora的alpha值
  10. lora_dropout: float = 0.0, # Lora的dropout值
  11. **kwargs, # 其他关键字参数
  12. ):
  13. bnb.nn.Linear8bitLt.__init__(
  14. self,
  15. in_features,
  16. out_features,
  17. bias=kwargs.get("bias", True),
  18. has_fp16_weights=kwargs.get("has_fp16_weights", True),
  19. memory_efficient_backward=kwargs.get("memory_efficient_backward", False),
  20. threshold=kwargs.get("threshold", 0.0),
  21. index=kwargs.get("index", None),
  22. )
  23. LoraLayer.__init__(self, in_features=in_features, out_features=out_features)
  24. # 将权重参数设置为不可训练
  25. self.weight.requires_grad = False
  26. # 是否初始化Lora权重,默认为True
  27. init_lora_weights = kwargs.pop("init_lora_weights", True)
  28. # 创建Lora层
  29. self.update_layer(adapter_name, r, lora_alpha, lora_dropout, init_lora_weights)
  30. # 当前激活的适配器名称,默认为adapter_name
  31. self.active_adapter = adapter_name
  32. def forward(self, x: torch.Tensor):
  33. # 调用父类的前向传播方法,得到输出结果
  34. result = super().forward(x)
  35. # 根据条件判断是否启用Lora
  36. if self.disable_adapters or self.active_adapter not in self.lora_A.keys():
  37. return result
  38. elif self.r[self.active_adapter] > 0:
  39. # 检查是否启用了自动混合精度
  40. if not torch.is_autocast_enabled():
  41. expected_dtype = result.dtype
  42. if x.dtype != torch.float32:
  43. x = x.float()
  44. # Lora计算过程:先输入A模型,再输入B模型,最后乘以self.scaling
  45. output = (
  46. self.lora_B[self.active_adapter](
  47. self.lora_A[self.active_adapter](self.lora_dropout[self.active_adapter](x))
  48. ).to(expected_dtype)
  49. * self.scaling[self.active_adapter]
  50. )
  51. else:
  52. output = (
  53. self.lora_B[self.active_adapter](
  54. self.lora_A[self.active_adapter](self.lora_dropout[self.active_adapter](x))
  55. )
  56. * self.scaling[self.active_adapter]
  57. )
  58. # 将Lora计算结果加到原有结果中
  59. result += output
  60. return result

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

闽ICP备14008679号