资料摘要:personal chatgpt 04 — PEFT/LoRA 源码分析
本讲由真实视频转录与作者 notebook/代码交叉整理。原始层:🔒 带时间戳视频转录;课程总览:personal chatgpt — LLMs 实践系列。
本讲回答第 3 讲中“get_peft_model 调用后,LoRA 到底怎样进入原模型”的问题。课程通过断点和源码摘录,展示 PEFT 根据任务类型选择 wrapper、根据 PEFT 类型选择 tuner、根据模型类型选择默认 target modules,再执行“查找目标模块 → 创建 LoRA 版本新模块 → 继承旧权重/偏置 → 替换原模块”的过程。
最后,课程追到新模块的 forward:原始冻结分支输出与 dropout → lora_A → lora_B → scaling 分支输出相加。
1. 两层类型映射
Section titled “1. 两层类型映射”课程 notebook 给出两类映射:
task_type/model_type决定外层任务 wrapper:CAUSAL_LM → PeftModelForCausalLM- 还列出 sequence classification、seq2seq、token classification 等类型。
peft_type决定内部 tuner:LORA → LoraModel- 同表还列出 Prompt Tuning、P-Tuning、Prefix Tuning、AdaLoRA 等。
因此第 3 讲的模型包装层次在课程版本中可概括为:
PeftModelForCausalLM└── LoraModel └── BloomForCausalLM2. LoraConfig 与 target modules
Section titled “2. LoraConfig 与 target modules”课程摘录的核心字段包括:
rtarget_moduleslora_alphalora_dropouttarget_modules 指明哪些模块要添加 LoRA。课程版本的默认映射示例:
bloom → query_key_valuellama → q_proj, v_projgpt2 → c_attnbert → query, value查找逻辑使用 model.named_modules() 枚举模块名,然后:
- 若配置为正则字符串,用
re.fullmatch; - 若配置为列表,用模块名后缀
endswith匹配。
这说明 adapter 注入依赖模型模块命名,不能假定同一组 target names 适用于所有架构或所有库版本。
3. 从 BLOOM 找到 query_key_value
Section titled “3. 从 BLOOM 找到 query_key_value”视频逐层定位:
BloomForCausalLM→ BloomModel→ BloomBlock→ BloomAttention→ query_key_valuequery_key_value 是一个从隐藏维度 h 到 3h 的大线性层。课程示例中形状为:
4096 → 12288LoRA rank 为 16 后,新增矩阵为:
lora_A: 4096 → 16lora_B: 16 → 122884. find-and-replace 注入过程
Section titled “4. find-and-replace 注入过程”课程将内部过程概括为:
- 根据 target module 名称找到旧模块;
- 创建带 LoRA 分支的新模块;
- 用新模块替换父模块上的旧属性;
- 把旧模块的
weight传给新模块; - 若存在 bias,也一并继承;
- 原始权重保持冻结,新分支参数可训练。
关键不是在原网络后面随意“加一层”,而是把指定投影层替换为兼容接口的新模块,新模块内部保留原线性变换并增加旁路。
5. 新模块的 forward
Section titled “5. 新模块的 forward”课程源码讲解的逻辑:
result = frozen_linear(x)lora_result = lora_B(lora_A(lora_dropout(x))) * scalingresult += lora_result数学上:
其中 W 冻结,W_A/W_B 可训练,s 是缩放因子。
6. 可被 LoRA 化的模块
Section titled “6. 可被 LoRA 化的模块”视频口头列举:
- Linear;
- Embedding;
- Conv2d。
课程共同点是这些模块包含较大的权重矩阵/张量,可通过低秩分支表达任务增量。具体支持类型仍取决于当时 PEFT 源码,而不是理论上所有层都自动可用。
公式 / 代码
Section titled “公式 / 代码”课程版本的目标模块匹配伪代码:
key_list = [key for key, _ in model.named_modules()]for key in key_list: if isinstance(target_modules, str): found = re.fullmatch(target_modules, key) else: found = any(key.endswith(t) for t in target_modules)前向:
BLOOM 示例的参数规模:
原矩阵:4096 × 12288LoRA:4096 × 16 + 16 × 1228800:34:提出“LoRA 怎样加到原模型上”的问题。02:04:替换原 layer、保留冻结分支并增加低秩 branch。04:01:PeftModelForCausalLM → LoraModel → BloomForCausalLM包装关系。05:01:task type 映射到 PEFT wrapper。05:31:find_and_replace查找并替换 target module。06:02:BLOOM 默认 target 为query_key_value。07:01:在BloomAttention中定位目标线性层。08:01:4096×12288 与 4096×16、16×12288 的形状对比。09:30:新模块继承旧 weight/bias。10:32:LoRA 模块的 forward 已包含 A/B 分支。11:30:冻结分支与dropout → A → B → scaling分支相加。
运行边界与可复现性
Section titled “运行边界与可复现性”- notebook 的源码映射和类名对应 2023 年 PEFT;当前 PEFT 可能已重构,不能按该调用栈定位最新版代码。
- 模型实例化仍依赖 BLOOM-7B1、bitsandbytes、CUDA 和足够显存;macOS 上只能静态阅读或改用小模型重写验证。
- notebook 没有独立安装 cell,需要第 3 讲式环境。
- 课程第 4 讲的 LLaMA 默认 target 映射是
q_proj/v_proj;第 6 讲加载的具体 Alpaca-LoRA checkpoint 配置却是q/k/v/o。adapter 自身配置可覆盖默认值,二者不矛盾。 - ASR 把 source、find-and-replace、query_key_value、forward、Linear 等识别为近音词;本稿以 notebook 源码为准。
与前后讲关系
Section titled “与前后讲关系”- 解释第 3 讲:为什么一个
get_peft_model调用能改变模型结构。 - 回扣第 2 讲:数学上的
xW + xW_AW_B对应源码中的冻结分支和 LoRA 分支。 - 连接第 6 讲:
PeftModel.from_pretrained也依赖正确的 base model、adapter config 和 target modules。
task_type='CAUSAL_LM'和peft_type='LORA'分别决定哪一层对象?- PEFT 为什么要根据模型类型维护不同的默认
target_modules? - “替换模块”过程中为什么必须继承旧模块的 weight 和 bias?
- BLOOM 的
query_key_value为 4096→12288 时,rank 16 的 A/B 形状分别是什么? - 为什么不能把本讲的 2023 年调用栈直接当作最新版 PEFT 的源码事实?
- 视频:PEFT/LoRA 源码分析
- 转录:🔒 personal chatgpt 04 视频转录
- 课件 / 代码:🔒 04_PEFT_lora.ipynb
- 滴答清单抓取时学习状态:已完成。
- 视频正文采用自动语音识别;关键术语已用标题、notebook 与源码校正,无法确认的口语细节不扩写。
- notebook 未执行的 CUDA、权重下载、训练或外部 API 单元,不表述为已复现实验。