当前位置: 首页 > news >正文

【深度学习】Transformer 注意力机制与 LoRA target_modules 详解

1. Transformer 自注意力机制结构

1.1 基础组件

class SelfAttention(nn.Module):def __init__(self, d_model, n_heads):super().__init__()self.d_model = d_modelself.n_heads = n_headsself.d_k = d_model // n_heads# 这就是 target_modules 中的四个关键组件!self.q_proj = nn.Linear(d_model, d_model)  # Query 投影self.k_proj = nn.Linear(d_model, d_model)  # Key 投影  self.v_proj = nn.Linear(d_model, d_model)  # Value 投影self.o_proj = nn.Linear(d_model, d_model)  # Output 投影def forward(self, x):batch_size, seq_len, d_model = x.shape# 1. 通过线性变换得到 Q, K, VQ = self.q_proj(x)  # [batch, seq_len, d_model]K = self.k_proj(x)  # [batch, seq_len, d_model]  V = self.v_proj(x)  # [batch, seq_len, d_model]# 2. 重塑为多头形式Q = Q.view(batch_size, seq_len, self.n_heads, self.d_k).transpose(1, 2)K = K.view(batch_size, seq_len, self.n_heads, self.d_k).transpose(1, 2)V = V.view(batch_size, seq_len, self.n_heads, self.d_k).transpose(1, 2)# 3. 计算注意力attention_scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)attention_weights = F.softmax(attention_scores, dim=-1)attention_output = torch.matmul(attention_weights, V)# 4. 合并多头输出attention_output = attention_output.transpose(1, 2).contiguous().view(batch_size, seq_len, d_model)# 5. 通过输出投影output = self.o_proj(attention_output)  # 最后的线性变换return output

2. 每个投影矩阵的作用详解

2.1 q_proj (Query Projection)

# 作用:将输入转换为"查询"向量
# 可以理解为:我在寻找什么信息?def explain_q_proj():# 假设输入是:"我喜欢吃苹果"input_embedding = torch.randn(1, 5, 768)  # [batch, seq_len, hidden_size]q_proj = nn.Linear(768, 768)queries = q_proj(input_embedding)print("Query的语义解释:")print("- 对于'我'这个token:它可能查询'谁在执行动作'")print("- 对于'喜欢':它可能查询'情感状态相关的词'") print("- 对于'苹果':它可能查询'与食物相关的动作'")return queries

2.2 k_proj (Key Projection)

# 作用:将输入转换为"键"向量
# 可以理解为:我能提供什么信息?def explain_k_proj():input_embedding = torch.randn(1, 5, 768)k_proj = nn.Linear(768, 768)  keys = k_proj(input_embedding)print("Key的语义解释:")print("- '我'的key:'我是一个执行者,主语'")print("- '喜欢'的key:'我是一个表达情感的动词'")print("- '苹果'的key:'我是一个食物名词,可以被喜欢'")return keys

2.3 v_proj (Value Projection)

# 作用:将输入转换为"值"向量
# 可以理解为:我实际包含的信息内容是什么?def explain_v_proj():input_embedding = torch.randn(1, 5, 768)v_proj = nn.Linear(768, 768)values = v_proj(input_embedding)print("Value的语义解释:")print("- '我'的value:包含主语、人称等语义信息")print("- '喜欢'的value:包含正面情感、偏好等语义信息")  print("- '苹果'的value:包含水果、食物、健康等语义信息")return values

2.4 o_proj (Output Projection)

# 作用:将多头注意力的结果投影回原始维度
# 可以理解为:整合所有注意力信息并输出def explain_o_proj():# 假设多头注意力已经计算完毕multi_head_output = torch.randn(1, 5, 768)  # 已经concat了所有heado_proj = nn.Linear(768, 768)final_output = o_proj(multi_head_output)print("Output Projection的作用:")print("- 将8个注意力头的信息融合")print("- 学习如何最好地组合不同类型的注意力")print("- 为下一层提供refined的表示")return final_output

3. 为什么选择这四个模块进行 LoRA?

3.1 参数量分析

def analyze_parameter_distribution():# 假设 d_model = 768 的模型d_model = 768modules = {'q_proj': d_model * d_model,      # 768 * 768 = 589,824'k_proj': d_model * d_model,      # 768 * 768 = 589,824  'v_proj': d_model * d_model,      # 768 * 768 = 589,824'o_proj': d_model * d_model,      # 768 * 768 = 589,824'feed_forward': d_model * d_model * 4,  # 通常是4倍大小}total_attention_params = sum([modules[k] for k in ['q_proj', 'k_proj', 'v_proj', 'o_proj']])total_ff_params = modules['feed_forward']print(f"注意力层参数: {total_attention_params:,}")print(f"前馈层参数: {total_ff_params:,}")print(f"注意力层占比: {total_attention_params/(total_attention_params+total_ff_params)*100:.1f}%")return modules# 输出:
# 注意力层参数: 2,359,296
# 前馈层参数: 2,359,296  
# 注意力层占比: 50.0%

3.2 为什么注意力层是最佳选择?

语义理解核心

注意力机制是 Transformer 理解语义关系的核心:

  • Q-K 交互决定了词与词之间的关联强度
  • V 决定了传递什么信息
  • O 决定了如何整合这些信息
影响范围广

每个 attention head 都学习不同类型的语义关系:

  • Head 1: 可能学习语法依赖关系
  • Head 2: 可能学习语义相似性
  • Head 3: 可能学习长距离依赖
参数效率高

相比全量微调:

  • 原始参数: 589,824 * 4 = 2,359,296 per layer
  • LoRA参数 (r=8): (7688 + 8768) * 4 = 49,152 per layer
  • 效率提升: 98% 参数减少!

4. target_modules 的不同选择策略

4.1 完整注意力策略(推荐)

最常用的配置

target_modules = ["q_proj", "k_proj", "v_proj", "o_proj"]

优点:

  • 覆盖注意力机制的所有关键组件
  • 平衡性能和效率
  • 适用于大多数微调任务

4.2 精简策略

只微调 Q 和 V

target_modules = ["q_proj", "v_proj"]

适用场景:

  • 计算资源极度受限
  • 任务相对简单
  • 快速原型验证

理由:Q 决定查询什么,V 决定传递什么信息,这两个最核心

4.3 扩展策略

包含前馈网络

target_modules = ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"]

适用场景:

  • 需要深度适应特定领域
  • 计算资源充足
  • 复杂的微调任务

注意:参数量会显著增加

4.4 实验对比

def compare_strategies():strategies = {"minimal": ["q_proj", "v_proj"],"standard": ["q_proj", "k_proj", "v_proj", "o_proj"], "extended": ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj"]}for name, modules in strategies.items():param_count = len(modules) * (768 * 8 + 8 * 768)  # 假设 r=8print(f"{name:10}: {len(modules)} 模块, ~{param_count:,} 参数")# 输出:
# minimal   : 2 模块, ~24,576 参数
# standard  : 4 模块, ~49,152 参数  
# extended  : 6 模块, ~73,728 参数

5. 实际代码示例

5.1 查看模型结构

from transformers import AutoModeldef inspect_model_modules():model = AutoModel.from_pretrained("Qwen/Qwen3-Embedding-0.6B")print("模型的主要模块:")for name, module in model.named_modules():if any(target in name for target in ["q_proj", "k_proj", "v_proj", "o_proj"]):print(f"  {name}: {module}")# 输出类似:
# model.encoder.layer.0.attention.self.q_proj: Linear(768, 768)
# model.encoder.layer.0.attention.self.k_proj: Linear(768, 768)  
# model.encoder.layer.0.attention.self.v_proj: Linear(768, 768)
# model.encoder.layer.0.attention.output.o_proj: Linear(768, 768)

5.2 LoRA 配置与应用

from peft import LoraConfig, get_peft_modeldef apply_lora_with_explanation():base_model = AutoModel.from_pretrained("Qwen/Qwen3-Embedding-0.6B")# 查看原始参数量original_params = sum(p.numel() for p in base_model.parameters())print(f"原始模型参数: {original_params:,}")# 应用 LoRAlora_config = LoraConfig(r=8,                                           # 低秩分解的秩lora_alpha=16,                                 # 缩放因子target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],  # 目标模块lora_dropout=0.05,bias="none",task_type=TaskType.FEATURE_EXTRACTION)model = get_peft_model(base_model, lora_config)# 查看可训练参数trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)total_params = sum(p.numel() for p in model.parameters())print(f"总参数: {total_params:,}")print(f"可训练参数: {trainable_params:,}")  print(f"可训练比例: {trainable_params/total_params*100:.2f}%")# 显示具体的 LoRA 模块model.print_trainable_parameters()return model

5.3 不同 target_modules 的效果对比

def compare_target_modules():base_model = AutoModel.from_pretrained("Qwen/Qwen3-Embedding-0.6B")configurations = {"只有QV": ["q_proj", "v_proj"],"标准QKV": ["q_proj", "k_proj", "v_proj"], "完整注意力": ["q_proj", "k_proj", "v_proj", "o_proj"],"包含FFN": ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj"]}print("不同配置的参数对比:")print("-" * 50)for config_name, target_modules in configurations.items():lora_config = LoraConfig(r=8,lora_alpha=16,target_modules=target_modules,lora_dropout=0.05,bias="none",task_type=TaskType.FEATURE_EXTRACTION)try:model = get_peft_model(base_model, lora_config)trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)print(f"{config_name:12}: {trainable_params:,} 个可训练参数")except Exception as e:print(f"{config_name:12}: 配置错误 - {e}")

6. 选择建议

6.1 基于任务类型选择

task_recommendations = {"句子相似性": ["q_proj", "k_proj", "v_proj", "o_proj"],"文档分类": ["q_proj", "v_proj"],  # 简化版本通常足够"语义搜索": ["q_proj", "k_proj", "v_proj", "o_proj"],  # 需要完整注意力"多语言适配": ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj"],  # 扩展版本"领域适应": ["q_proj", "k_proj", "v_proj", "o_proj"]  # 标准配置
}for task, modules in task_recommendations.items():print(f"{task}: {modules}")

6.2 基于资源限制选择

def resource_based_selection(gpu_memory_gb, training_time_hours):if gpu_memory_gb < 8:return ["q_proj", "v_proj"]elif gpu_memory_gb < 16:return ["q_proj", "k_proj", "v_proj"] else:return ["q_proj", "k_proj", "v_proj", "o_proj"]# 示例
print("8GB GPU:", resource_based_selection(8, 2))    # ['q_proj', 'v_proj']
print("16GB GPU:", resource_based_selection(16, 4))  # ['q_proj', 'k_proj', 'v_proj']
print("24GB GPU:", resource_based_selection(24, 8))  # ['q_proj', 'k_proj', 'v_proj', 'o_proj']

总结

target_modules=["q_proj", "k_proj", "v_proj", "o_proj"] 的选择是基于以下考虑:

  1. 覆盖注意力核心:这四个模块构成了自注意力机制的完整流程
  2. 参数效率平衡:在性能和计算效率之间取得最佳平衡
  3. 广泛适用性:适用于大多数 embedding 微调任务
  4. 实践验证:在众多实验中证明了其有效性

对于 embedding 模型微调,这个配置通常是最佳选择!

http://www.dtcms.com/a/352199.html

相关文章:

  • 如何安装 VS2019 和 .NET Core SDK 2.2.301(winx64)?完整操作步骤(附安装包下载)
  • 基于YOLOv11训练无人机视角Visdrone2019数据集
  • 区块链技术探索与应用:从密码学奇迹到产业变革引擎
  • 从入门到理解:支持向量机的核心原理与实战思路
  • 计数组合学7.21(有界部分大小的平面分拆)
  • 车载铁框矫平机:一辆“会熨衣服”的工程车
  • 高性能异步任务编排框架:Gobrs-Async
  • 【项目】深房数据通——深圳房价可视化系统
  • 嵌入式第三十七课!!!TCP机制与HTTP协议
  • 【学习笔记】系统时间跳变会影响time接口解决措施
  • 相关法律、法规知识(五)
  • 单层膜可改善无铅钙钛矿太阳能电池
  • Java 企业应用单点登录(SSO)实现方案详解
  • 创维桌面云终端-创维LB2002-白盒-晶晨S905L3A-2+8G-线刷刷机包
  • 实验2 天气预报
  • Ultra Accelerator Link(UALink)Consortium
  • 网站测试报告:WEB应用反CSRF的本质与防御机制
  • 解决 pdf.mjs 因 MIME 类型错误导致的模块加载失败问题
  • day1_线性回归的实现 李沐动手学深度学习pytorch记录
  • 吱吱企业通讯软件保障企业办公安全与效率,助力企业高效发展
  • (LeetCode 每日一题) 3000. 对角线最长的矩形的面积(数组)
  • Jmeter基础:Jmeter聚合报告
  • 6pen Art
  • 校园勤工俭学微信小程序的设计与实现:基于数字化服务生态的赋能体系构建
  • Vue2(七):配置脚手架、render函数、ref属性、props配置项、mixin(混入)、插件、scoped样式
  • C/C++ 数据结构 —— 树(2)
  • Leetcode 1049. 最后一块石头的重量 II 动态规划-01背包
  • LeetCode-22day:多维动态规划
  • Docker详细学习
  • 税务岗位职场能力解析与提升路径规划