Jasaxion一只大雄

风打,碎琉璃; 打不碎的,是那阳光漫地。The wind strikes, shattering the glazed glass; Yet unbroken remains the sunlight spilling across the earth.

[实习面试]LLM 大模型岗位实习考点[RL 强化学习|多模态] (一)

2025-02-25


强化学习相关

PPO 算法

PPO 的思路:Actor + Critic(鼓励增长) + Clip (防止不良增长) + KL 惩罚(防止作弊行为,学习水平的不良增长)(Critic、Advantage、Clip、Reference Model)

**Critic 的意义:**它为每个状态或阶段提供“合理预期”,大幅降低了训练方差;

**Clip & min 机制:**约束策略更新幅度,避免一次考试“爆发”带来的巨幅震荡;

**Reference Model:**限制“作弊”或极端行为,让策略不要过度偏离最初合规范围;

PPO的实现细节,核心思想与损失函数。

image-20250225203036939

GRPO

在实际应用尤其是大型语言模型(LLM)上,Critic(价值函数)通常需要跟 Actor 同等大小的网络去估计,否则很难评估到位,成本很高,而且有些场景(比如只在回答末尾才有一个整体 Reward)并不太适合训练出精细的价值函数。

GRPO的话,不再需要一个模型来设置是奖励函数,模型取同时输出的若干条数据的平均值作新的 Critic。

多步时序的情况

  1. 需要用 TD 残差(Temporal Difference)来衡量“实际回报”和“之前对价值的估计”之差;
  2. 为了更好地估计 Advantage,既不想只用单步 TD,也不想全靠蒙特卡洛,“GAE(Generalized Advantage Estimation)”应运而生;
  3. 它通过对多步 TD 残差进行衰减累加,提供了一个兼顾偏差与方差的折衷方案;
  4. 状态价值函数 与动作价值函数 的定义也要放到时序多步的语境下去;在每周进行一次学习决策、每周获得一个奖励,形成了更丰富也更复杂的训练过程。
image-20250225123758879

多模态与模型架构相关

CLIP 相关细节

  1. 预训练目标:CLIP 使用对比学习作为预训练目标。它将配对的图像和文本作为正样本,非配对的图像和文本作为负样本,通过最大化正样本的相似度和最小化负样本的相似度来学习表示。
  2. 模型架构:CLIP 由两个主要组件组成:
    • 图像编码器:通常是一个视觉变换器(ViT)或 ResNet,将图像编码为向量表示
    • 文本编码器:一个基于 Transformer 的模型,将文本编码为向量表示
  3. 训练数据:CLIP 使用从互联网上收集的 4 亿图像-文本对进行训练,这种大规模数据使其能够学习到丰富的视觉概念和语言关系。
  4. 训练过程:在每个训练批次中,CLIP 计算 N 个图像和 N 个对应文本之间的余弦相似度,形成一个 N×N 的矩阵,并使用交叉熵损失函数来最大化对角线元素(正样本对)的相似度。
  5. 零样本能力:CLIP 的一个主要创新点在于其零样本迁移能力。通过将分类任务转换为文本-图像匹配问题,CLIP 可以直接应用于新的视觉任务,而无需额外的训练。

BLIP 相关细节

BLIP (Bootstrapping Language-Image Pre-training) 是一种多模态模型,它在 CLIP 的基础上进行了扩展和改进。BLIP 由 Salesforce Research 在 2022 年提出,主要目标是解决视觉-语言预训练中的数据噪声问题并增强模型的生成能力。以下是 BLIP 的基本方法:

  1. 多任务预训练框架:BLIP 同时训练三种不同类型的任务:

    • 图像-文本对比学习(类似 CLIP)

    • 图像-文本匹配(判断图像和文本是否匹配)

    • 图像-文本生成(根据图像生成描述文本)

  2. 模型架构

    • 图像编码器:通常采用 ViT 作为视觉骨干网络

    • 文本编码器/解码器:使用双向(编码器)和单向(解码器)Transformer 模块处理文本(encoder/decoder)

    • 多模态融合:通过注意力机制实现图像和文本特征的交互

  3. 引导式语言-图像预训练

    • BLIP 引入了一种新型的"引导"预训练策略

    • 使用模型自身生成的伪标签来提升带噪图像-文本数据的质量

    • 包含一个"字幕器"(生成描述)和一个"过滤器"(过滤低质量描述)

  4. CapFilt:一种自举策略,用于改进网络上收集的嘈杂图像-文本对:

    • 使用当前模型为图像生成新的描述(Caption)
    • 过滤(Filter)掉低质量的网络收集的描述
    • 这个过程迭代进行,逐步提升训练数据质量
  5. 统一的视觉-语言理解和生成

    • BLIP 能够同时处理理解任务(如图像-文本检索)和生成任务(如图像字幕生成)
    • 可以通过冻结部分网络参数进行高效微调

CLIP 与 BLIP 的区别

BLIP 与 CLIP 的主要区别在于:BLIP 是多任务框架,同时支持对比学习、匹配和生成任务,而 CLIP 仅专注于对比学习;BLIP 引入了 CapFilt 自举策略来提升训练数据质量,解决网络数据噪声问题;BLIP 采用双向和单向 Transformer 结合的架构,能同时处理理解和生成任务,而 CLIP 只有编码能力,不具备文本生成功能;BLIP 更适合图像字幕生成等需要文本生成的应用场景。

LLAVA 模型的相关细节

LLAVA (Large Language and Vision Assistant) 是一种多模态 AI 模型,结合了大型语言模型(LLM)和视觉编码器的能力。以下是 LLAVA 的主要技术细节:

  1. 架构设计

    • 视觉编码器:通常使用 CLIP 的视觉编码器(如 ViT)提取图像特征

    • 语言模型:基于 LLaMA 等大型语言模型作为语言骨干

    • 连接层:使用一个投影层将视觉特征映射到语言模型的嵌入空间

  2. 训练方法

    • 两阶段训练:首先在图像-文本对上预训练视觉-语言连接,然后在多模态指令数据上进行微调

    • 指令微调:使用包含图像和相关指令-回复对的数据集进行监督微调

    • 基础模型固定:通常冻结视觉编码器和大部分语言模型参数,只训练连接层和少量参数

    • 预训练任务

      • LLAVA 的预训练阶段主要是学习将图像特征与语言模型的嵌入空间对齐
      • 使用图像-文本对(如图像及其描述)训练模型生成与图像内容相关的文本
      • 核心任务是给定图像作为输入模型需要生成相应的描述文本
  3. 数据集

    • 使用 **CC3M(Conceptual Captions)**等数据集进行预训练

    • 利用 COCO 等数据集生成高质量的图像描述数据

    • 构建指令跟随数据集,包含多种视觉任务,如描述图像、回答问题、分析场景等

  4. 关键创新

    • 轻量级连接:通过简单的投影层实现视觉和语言模态的桥接,无需大量额外参数

    • 指令微调策略:有效利用有限的高质量多模态指令数据

    • 零样本泛化:能够处理训练中未见过的视觉任务和指令类型

  5. 应用能力

    • 图像描述和分析

    • 视觉问答(VQA)

    • 多模态推理和理解

    • 遵循视觉相关指令的对话

大模型杂项

模型架构

多头注意力的作用是什么?从并行计算的角度解释一下

​ 多头注意力的作用是增强模型的表达能力,多头注意力允许模型同时关注输入序列的不同位置和不同表示子空间,捕捉更为丰富的语义信息。

​ 从并行角度出发:引入多头注意力机制后,每个注意力头独立且并行地执行自己的查询(Query)、键(Key)和值(Value)的线性变换,并计算注意力权重。这些独立的计算可以在GPU/TPU上同时执行,大幅提高了训练和推理速度。

rlhf阐述,ppo解释,dpo怎么从ppo来的,请进行阐述

  1. RLHF 包含三个核心步骤:
    1. 预训练语言模型
    2. 构建人类偏好数据集并训练奖励模型;
    3. 使用强化学习算法(PPO)基于奖励模型来优化语言模型;
  2. PPO的核心是一个经过裁剪的目标函数,用于限制每次策略更新的幅度。
    1. 对于语言模型来说,这个目标函数为: $L^{CLIP(θ)} = E[min(r_t(θ)A_t, clip(r_t(θ), 1-ε, 1+ε)A_t)]$ 其中$$r_t(θ)$$是新旧策略的概率比,$A_t$是优势函数,$ε$是裁剪参数。
    2. KL散度约束:PPO通常还会添加一个KL散度项,限制新策略与原始策略的偏离程度,防止模型偏离原始的语言建模能力。
    3. 实现复杂性:PPO需要多次采样、计算奖励、估计优势函数等步骤,实现和调试都相对复杂。
  3. DPO(Direct Preference Optimization)从PPO的演化
    1. 理论等价性:DPO的关键洞见是在某些条件下,可以将PPO的强化学习目标重新表述为一个直接的偏好优化问题。
    2. 数学推导:DPO从最优贝叶斯奖励推断出发,证明了可以直接从人类偏好数据中学习,而不需要显式训练奖励模型。具体来说,DPO将PPO的目标函数重写为: $L_{DPO(π_θ; π_ref)} = E_(x,y_w,y_l)~D[log σ(β log(π_θ(y_w|x)/π_ref(y_w|x)) - β log(π_θ(y_l|x)/π_ref(y_l|x)))]$ 其中$π_θ$是当前策略,$π_ref$是参考策略,$y_w$和$y_l$分别是人类偏好的"胜者"和"败者"回答,$β$是温度参数。
    3. 实现简化:DPO直接使用人类偏好数据对(更好的回答vs较差的回答)进行训练,不需要:显式构建和训练奖励模型、执行强化学习的采样和优势估计、处理复杂的PPO训练循环。
    4. 计算效率:DPO比PPO更高效,训练过程更稳定,因为它将RLHF简化为一个监督学习问题,直接从人类偏好数据学习。

如何评估大模型的性能?你认为哪些指标最重要?

  1. 基准测试分数:MMLU(多领域知识)、BBH(推理能力)、HumanEval(编程能力)等标准化测试结果
  2. 幻觉率:模型产生错误信息的频率,通过事实一致性检查评估
  3. 指令遵循能力:模型正确理解并执行用户请求的程度
  4. 上下文学习能力:利用少量示例快速适应新任务的能力
  5. 推理延迟与吞吐量:实际应用中的响应速度和处理能力
  6. 安全性与对齐度:拒绝有害请求的能力和符合人类价值观的程度
  7. 真实世界任务评估:通过人类评估员对模型在实际应用场景中表现的评分

MoE (Mixture of Experts) 架构的优势和实现挑战

MoE (Mixture of Experts) 架构的优势:

  1. 计算效率提升:每次前向只激活部分专家,减少计算量
  2. 模型容量扩展:可大幅增加参数量而不等比增加计算资源
  3. 领域特化能力:不同专家可专注于不同任务或知识领域
  4. 性能与效率平衡:训练更大规模模型的经济可行方式
  5. 动态适应性:智能路由机制可根据输入动态选择合适专家

实现挑战:

  1. 路由决策优化:设计高效路由器避免专家使用不均衡问题
  2. 计算负载均衡:防止部分专家过载,其他专家闲置
  3. 通信开销大:分布式训练时专家间通信可能成为瓶颈
  4. 训练不稳定性:路由决策梯度和专家权重更新交互复杂
  5. 推理部署复杂:比传统Transformer部署更复杂,需特殊优化
  6. 异构硬件适配:在不同算力设备上部署需特殊策略

MoE代表了大模型扩展的重要方向,通过"稀疏激活"实现更高效的参数利用,但其工程实现复杂度也显著提高。

如何提高大模型的对话能力和指令遵循能力

提高大模型对话能力和指令遵循能力的关键方法包括:高质量指令数据集构建涵盖多样化指令和对话场景采用人类反馈强化学习(RLHF)通过人类偏好训练奖励模型并优化策略;引入对抗训练,创建难以遵循的指令来针对性提升;使用思维链(CoT)提示训练,强化逐步推理能力;构建多轮对话数据,提升连贯性和上下文理解隐式提示学习,例如P-tuning等技术;多样化评估体系设计,实时反馈模型弱点;红蓝对抗评估,发现并修复指令漏洞;混合专家系统整合,在不同任务间动态切换解决方案。

如何处理和准备大规模语料库用于预训练?

数据收集与整合

数据清洗

数据处理

数据格式化

数据质量保障

DP 与 DDP

数据并行(Data Parallel,DP)是PyTorch中的一种单进程多线程并行训练方法,通过torch.nn.DataParallel实现。它将模型复制到多个GPU上将输入数据分成多份分配给各GPU每个GPU独立计算其分配数据的梯度然后在主GPU(通常是GPU 0)上汇总所有梯度并更新模型参数,最后将更新后的模型参数复制回所有GPU。DP实现简单,只需将模型包装在nn.DataParallel()中即可使用,适合快速原型开发和小规模实验。它不需要修改原有代码结构,可以动态适应可用GPU数量,但主GPU需要存储所有梯度,成为性能和内存瓶颈。

分布式数据并行(Distributed Data Parallel,DDP)是PyTorch中的多进程并行训练方法,通过torch.nn.parallel.DistributedDataParallel实现。DDP为每个GPU分配一个独立的Python进程,每个进程维护完整的模型副本和优化器状态。在反向传播中,DDP使用高效的环形全归约(ring-allreduce)算法在所有进程间同步梯度,确保所有模型副本保持同步。实现DDP需要初始化进程组、设置本地排名、创建分布式采样器等步骤,虽然代码较为复杂,但可以实现接近线性的扩展性,支持单机多GPU和多机多GPU训练,并能与混合精度训练、梯度累积等高级技术无缝集成。

DP与DDP的主要区别在于架构模式、通信效率和扩展性。DP采用单进程多线程模式,所有操作都在同一Python进程中进行,通信效率低,主GPU成为瓶颈,扩展性有限,不适合大规模训练。而**DDP采用多进程模式,每个GPU运行独立进程,通过高效的通信原语实现梯度同步,消除了中心化瓶颈,内存使用更均衡,扩展性更好。**在实际性能上,当GPU数量增加时,DDP的性能优势愈发显著:在8GPU配置下,DP通常只能获得4-5倍的加速比,而DDP可以达到接近线性的7.5-7.8倍加速比。此外,DDP支持多节点训练,而DP仅限于单机使用。因此,虽然DDP配置更复杂,但对于大型模型训练或需要高效利用计算资源的场景,DDP是明显更优的选择。

分布式训练策略(如数据并行、模型并行、流水线并行)及其实现考虑

数据并行(Data Parallel)

模型并行(Tensor Parallel)

流水线并行(Pipeline Parallel)

混合并行

模型微调

预训练-微调范式的优势是什么?

预训练-微调范式优势:利用大规模无标注数据捕获通用语言知识,再针对特定任务调整,节省计算资源,加速收敛,提高性能,克服数据稀缺问题。

常见微调方法:

  1. 全参数微调:调整模型所有参数,计算资源需求大
  2. LoRA:在原始权重旁添加低秩矩阵,仅更新少量参数
  3. P-tuning/Prompt-tuning:仅更新输入端的连续提示向量
  4. Adapter-tuning:在层间插入小型可训练模块,冻结原模型
  5. QLoRA:结合量化技术与LoRA,进一步降低显存需求
  6. Prefix-tuning:在自注意力层前添加可训练的前缀向量

阐述一下 Lora 微调的原理和方法;

基本原理

  1. 低秩假设:LoRA假设预训练权重矩阵的更新可以通过低秩分解来近似表示。

  2. 数学表达:对于原始的预训练权重矩阵 W ∈ ℝ^(d×k),LoRA不直接更新W,而是将更新参数化为:

    W + ΔW = W + BA

    其中 B ∈ ℝ^(d×r) 和 A ∈ ℝ^(r×k),r « min(d,k) 是一个很小的秩值(通常为8、16或32)。

  3. 前向传播:对于输入 x,输出计算为:

    h = Wx + BAx = Wx + h_Δ

  4. 冻结预训练权重:原始权重 W 在训练过程中保持冻结,只有 A 和 B 参与梯度更新。

实施方法

  1. 选择目标层:通常应用于注意力权重矩阵(查询、键、值投影)和前馈网络,不是所有层都需要应用LoRA。

  2. 初始化:B通常用高斯初始化,A通常初始化为零,这样在开始训练时ΔW=0。

  3. 缩放因子:引入一个缩放超参数α,使得更新变为:

    W + α(BA)/r

    这样调整可以更好地控制更新的大小。

  4. 训练和合并

    • 在训练期间只更新A和B
    • 推理时可以将BA直接加到W上形成一个合并后的权重矩阵,不增加推理延迟

peft 是什么?请阐述一下。

​ PEFT(Parameter-Efficient Fine-Tuning)是一种用于大型预训练模型的参数高效微调技术。它的核心思想是在保持模型性能的同时,只调整或添加极少量的参数,大大降低了计算资源需求。

PEFT 主要包括以下几种常见方法:

1. LoRA (Low-Rank Adaptation) - 通过添加低秩分解矩阵来近似模型权重的更新,而不是直接更新所有权重。只需训练这些小型的低秩矩阵,显著减少了需要更新的参数数量。

2. Prompt Tuning - 在输入层添加可学习的连续提示向量,仅训练这些提示向量而保持预训练模型参数冻结。

3. Prefix Tuning - 类似于Prompt Tuning,但在每个Transformer层都添加了可训练的前缀向量。

4. Adapter - 在Transformer层之间插入小型可训练的神经网络层,主模型参数保持不变。

5. QLoRA - 结合了量化技术和LoRA,使用4位量化加载模型,然后应用LoRA进行微调。

大规模语言模型训练中的常见挑战及解决方案(如显存限制、训练不稳定性)

显存限制

  1. 混合精度训练:使用FP16/BF16加速并节省显存
  2. 梯度检查点:牺牲计算换取显存,仅存储部分激活值
  3. 模型并行:将模型分割到多个设备上
  4. ZeRO优化:将优化器状态、梯度和参数分布在多设备上

训练不稳定性

  1. 学习率调度:使用热身策略和衰减机制
  2. 梯度裁剪:防止梯度爆炸
  3. LayerNorm与初始化:优化残差连接的放缩方式
  4. Adam优化器变体:AdamW、8-bit Adam等

训练效率

  1. 数据并行:多设备复制模型,分批处理数据
  2. 流水线并行:模型层级划分到不同设备
  3. 张量并行:将单个运算分散到多设备
  4. 高效通信:NCCL、NVLink等通信优化

KV Cache 的工作原理及其对推理性能的影响

KV Cache的工作原理是在自回归生成过程中缓存并重用已计算的Key和Value矩阵,避免重复计算。当生成新token时,模型仅需计算新token的K和V并追加到缓存中,而不必重新计算整个序列的注意力参数。

模型量化技术的原理及实现方法

模型量化技术的原理是将模型权重和激活值从高精度(如FP32/FP16)转换为低精度表示(如INT8/INT4),降低计算和存储需求。

实现方法:

  1. 后训练量化(PTQ):训练后直接转换,无需重训练
    • 线性量化:将浮点值映射到整数范围
    • 最小最大值缩放:基于权重分布确定量化范围
    • 校准:使用少量数据校准量化参数
  2. 量化感知训练(QAT):训练时模拟量化效果
    • 前向传播时应用量化操作
    • 反向传播时使用直通估计(STE)处理不可微的量化操作
    • 学习适应量化噪声的权重分布
  3. 混合精度量化
    • 对敏感层保留高精度
    • 非敏感层使用低精度
    • 基于重要性分析选择量化程度
  4. 非均匀量化
    • 权重聚类(如K-means)后分配不同位宽
    • 基于权重分布密度进行自适应量化

模型推理

如何评估 prompt 好坏?如何优化 Prompt?

评估prompt的好坏主要基于几个关键维度:

  1. 任务完成度 - prompt是否能引导模型生成符合预期的输出,解决特定问题

  2. 一致性与可靠性 - 相同或相似的prompt是否能够稳定地产生类似质量的结果

  3. 效率 - prompt是否简洁明了,避免不必要的冗长内容,同时提供足够的上下文信息

  4. 泛化能力 - prompt是否能够处理同一类问题的不同变体

  5. 评估指标 - 根据具体任务,可以使用自动化指标(如BLEU, ROUGE, Perplexity)或人工评估

优化方法:

  1. 结构化优化
    1. 明确角色与期望 - 定义模型应该扮演的角色,清晰阐述期望输出格式
    2. 任务分解 - 将复杂任务分解为多个步骤,引导模型逐步思考
    3. 添加思维链 - 在prompt中包含"让我们一步一步思考"等引导语,促使模型展示推理过程
    4. 提供示例 - 使用少样本学习(Few-shot learning),在prompt中包含高质量的示例
  2. 迭代优化
    1. A/B测试 - 比较不同prompt变体的效果,保留表现更好的版本
    2. 系统性实验 - 控制变量法测试prompt的不同元素(如长度、格式、示例数量)
    3. 错误分析 - 分析模型在哪些情况下表现不佳,针对性地修改prompt
    4. 使用提示工程模式 - 应用已被证明有效的提示模式,如"我将扮演X角色,你将扮演Y角色"
  3. 高级优化
    1. 反向提示工程 - 从理想输出反推有效prompt
    2. 自动化提示优化 - 使用强化学习或遗传算法自动搜索最优prompt
    3. 提示链接 - 将多个prompt串联起来,将前一步的输出作为后一步的输入
    4. 使用自反思 - 在prompt中引导模型评估自己的回答并改进
    5. 上下文学习 - 通过在上下文中提供更多相关信息来提高模型理解

Transformers 细节

手写 Transformers 架构

Transformer架构的关键组件包括:多头自注意力机制(计算序列中每个元素与其他元素的关联度);位置编码(注入位置信息);残差连接与层归一化(防止梯度消失并稳定训练);前馈神经网络(增强表达能力);编码器-解码器结构(编码器处理输入序列,解码器生成输出)。其工作原理是通过自注意力机制并行捕获序列中的长距离依赖关系,解决了RNN的序列处理瓶颈,大幅提高了模型性能与训练效率

  1. MultiHeadAttention 类:实现了多头注意力机制,将输入投影到多个子空间进行并行的注意力计算,然后拼接结果。

  2. PositionwiseFeedForward 类:实现了位置前馈网络,由两个线性变换和一个 ReLU 激活函数组成。

  3. PositionalEncoding 类:使用正弦和余弦函数实现位置编码,为序列中的每个位置添加位置信息。

  4. EncoderLayer 类:编码器层,包含自注意力机制和前馈网络,还有残差连接和层归一化。

  5. DecoderLayer 类:解码器层,包含自注意力、交叉注意力和前馈网络,同样使用残差连接和层归一化。

  6. Transformer 类:整合了所有组件,包括编码器、解码器、嵌入层和输出层。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
import torch
import torch.nn as nn
import math


class MultiHeadAttention(nn.Module):
    def __init__(self, d_model, num_heads):
        super(MultiHeadAttention, self).__init__()
        self.num_heads = num_heads
        self.d_model = d_model
        
        assert d_model % num_heads == 0, "d_model must be divisible by num_heads"
        
        self.d_k = d_model // num_heads
        
        # 创建Q, K, V的线性投影层
        self.q_linear = nn.Linear(d_model, d_model)
        self.k_linear = nn.Linear(d_model, d_model)
        self.v_linear = nn.Linear(d_model, d_model)
        
        self.out_linear = nn.Linear(d_model, d_model)
    
    def forward(self, q, k, v, mask=None):
        batch_size = q.size(0)
        
        # 线性变换
        q = self.q_linear(q).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
        k = self.k_linear(k).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
        v = self.v_linear(v).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
        
        # 注意力计算
        scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.d_k)
        
        # 应用mask(如果提供)
        if mask is not None:
            scores = scores.masked_fill(mask == 0, -1e9)
        
        # softmax获取注意力权重
        attn = torch.softmax(scores, dim=-1)
        
        # 应用注意力权重
        output = torch.matmul(attn, v)
        
        # 转换回原始形状
        output = output.transpose(1, 2).contiguous().view(batch_size, -1, self.d_model)
        
        # 最终的线性层
        return self.out_linear(output)


class PositionwiseFeedForward(nn.Module):
    def __init__(self, d_model, d_ff):
        super(PositionwiseFeedForward, self).__init__()
        self.fc1 = nn.Linear(d_model, d_ff)
        self.fc2 = nn.Linear(d_ff, d_model)
        self.relu = nn.ReLU()
    
    def forward(self, x):
        return self.fc2(self.relu(self.fc1(x)))


class PositionalEncoding(nn.Module):
    def __init__(self, d_model, max_seq_length=5000):
        super(PositionalEncoding, self).__init__()
        
        # 创建简单的位置编码矩阵
        pe = torch.zeros(max_seq_length, d_model)
        
        # 位置索引
        position = torch.arange(0, max_seq_length).unsqueeze(1).float()
        
        # 生成不同频率的正弦和余弦
        div_term = torch.pow(10000, torch.arange(0, d_model, 2).float() / d_model)
        
        # 偶数位置使用正弦,奇数位置使用余弦
        pe[:, 0::2] = torch.sin(position / div_term)
        pe[:, 1::2] = torch.cos(position / div_term)
        
        # 注册为buffer以便模型保存和加载
        self.register_buffer('pe', pe.unsqueeze(0))
    
    def forward(self, x):
        # 添加位置编码到输入
        return x + self.pe[:, :x.size(1)]


class EncoderLayer(nn.Module):
    def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
        super(EncoderLayer, self).__init__()
        
        self.self_attn = MultiHeadAttention(d_model, num_heads)
        self.feed_forward = PositionwiseFeedForward(d_model, d_ff)
        
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        
        self.dropout = nn.Dropout(dropout)
    
    def forward(self, x, mask=None):
        # 自注意力层 + 残差连接 + 层归一化
        attn_output = self.self_attn(x, x, x, mask)
        x = self.norm1(x + self.dropout(attn_output))
        
        # 前馈网络 + 残差连接 + 层归一化
        ff_output = self.feed_forward(x)
        x = self.norm2(x + self.dropout(ff_output))
        
        return x


class DecoderLayer(nn.Module):
    def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
        super(DecoderLayer, self).__init__()
        
        self.self_attn = MultiHeadAttention(d_model, num_heads)
        self.cross_attn = MultiHeadAttention(d_model, num_heads)
        self.feed_forward = PositionwiseFeedForward(d_model, d_ff)
        
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.norm3 = nn.LayerNorm(d_model)
        
        self.dropout = nn.Dropout(dropout)
    
    def forward(self, x, enc_output, src_mask=None, tgt_mask=None):
        # 自注意力层
        self_attn_output = self.self_attn(x, x, x, tgt_mask)
        x = self.norm1(x + self.dropout(self_attn_output))
        
        # 编码器-解码器注意力层
        cross_attn_output = self.cross_attn(x, enc_output, enc_output, src_mask)
        x = self.norm2(x + self.dropout(cross_attn_output))
        
        # 前馈网络
        ff_output = self.feed_forward(x)
        x = self.norm3(x + self.dropout(ff_output))
        
        return x


class Transformer(nn.Module):
    def __init__(self, src_vocab_size, tgt_vocab_size, d_model=512, num_heads=8, 
                 num_encoder_layers=6, num_decoder_layers=6, d_ff=2048, dropout=0.1):
        super(Transformer, self).__init__()
        
        # 词嵌入层
        self.src_embedding = nn.Embedding(src_vocab_size, d_model)
        self.tgt_embedding = nn.Embedding(tgt_vocab_size, d_model)
        
        # 位置编码
        self.positional_encoding = PositionalEncoding(d_model)
        
        # 编码器层
        self.encoder_layers = nn.ModuleList([
            EncoderLayer(d_model, num_heads, d_ff, dropout) 
            for _ in range(num_encoder_layers)
        ])
        
        # 解码器层
        self.decoder_layers = nn.ModuleList([
            DecoderLayer(d_model, num_heads, d_ff, dropout) 
            for _ in range(num_decoder_layers)
        ])
        
        # 最终的线性层和softmax
        self.output_linear = nn.Linear(d_model, tgt_vocab_size)
        
        self.dropout = nn.Dropout(dropout)
        self.d_model = d_model
    
    def encode(self, src, src_mask=None):
        # 源序列嵌入和位置编码
        #“* math.sqrt(self.d_model)“的目的是:1.缩放嵌入向量的方差:当我们初始化词嵌入向量时,通常使用均值为0、方差为1的分布(如正态分布);2.平衡嵌入和位置编码的贡献
        src = self.dropout(self.positional_encoding(self.src_embedding(src) * math.sqrt(self.d_model)))
        
        # 通过编码器层
        for encoder_layer in self.encoder_layers:
            src = encoder_layer(src, src_mask)
        
        return src
    
    def decode(self, tgt, enc_output, src_mask=None, tgt_mask=None):
        # 目标序列嵌入和位置编码
        tgt = self.dropout(self.positional_encoding(self.tgt_embedding(tgt) * math.sqrt(self.d_model)))
        
        # 通过解码器层
        for decoder_layer in self.decoder_layers:
            tgt = decoder_layer(tgt, enc_output, src_mask, tgt_mask)
        
        return tgt
    
    def forward(self, src, tgt, src_mask=None, tgt_mask=None):
        # 编码阶段
        enc_output = self.encode(src, src_mask)
        
        # 解码阶段
        dec_output = self.decode(tgt, enc_output, src_mask, tgt_mask)
        
        # 最终的线性变换和softmax
        output = self.output_linear(dec_output)
        
        return output


# 创建Transformer模型的简单示例
def create_transformer_model():
    src_vocab_size = 10000
    tgt_vocab_size = 10000
    
    model = Transformer(
        src_vocab_size=src_vocab_size,
        tgt_vocab_size=tgt_vocab_size,
        d_model=512,
        num_heads=8,
        num_encoder_layers=6,
        num_decoder_layers=6,
        d_ff=2048,
        dropout=0.1
    )
    
    return model

自注意力机制相比传统RNN/LSTM的优势

自注意力机制相比传统RNN/LSTM的优势:

  1. 并行计算:可同时处理序列中的所有元素,显著提高训练速度
  2. 长距离依赖:直接建立序列中任意位置间的连接,克服长序列梯度消失问题
  3. 全局视野:每个位置都能直接获取全序列信息,不受距离限制
  4. 可解释性:注意力权重提供可视化解释模型决策的方式
  5. 计算效率:特别是在长序列处理上,自注意力避免了RNN的循环依赖
  6. 灵活性:可通过多头注意力捕获不同特征子空间的信息模式

解释 Masked Self-Attention 的目的及实现方式

Masked Self-Attention的目的是防止信息泄露,确保模型在生成序列时只能看到当前位置之前的信息,而不能看到未来信息,这对自回归式生成至关重要。实现方式是在计算注意力分数后、应用softmax前,将未来位置(上三角部分)的注意力分数设为负无穷(如-1e9)。这样softmax后,未来位置的权重接近零,从而屏蔽了对未来信息的访问。在解码器训练中,通常使用下三角掩码矩阵来实现,保证位置i只能关注位置0到i的信息。

LLM 大模型相关指标

准确率类指标

准确率(Accuracy)

  1. 含义:正确预测的样本数量与总样本数量的比值
  2. 作用:提供模型整体性能的基本度量
  3. 计算方法:$\text{Accuracy} = \frac{\text{正确预测数量}}{\text{总样本数量}}$
  4. 优点:简单直观,易于理解和计算
  5. 缺点:在数据不平衡情况下可能产生误导;无法区分不同类型的错误

精确率(Precision)

  1. 含义:真正例数量与所有被预测为正例数量的比值
  2. 作用:衡量模型预测为正例时的准确程度
  3. 计算方法:$\text{Precision} = \frac{\text{真正例(TP)}}{\text{真正例(TP) + 假正例(FP)}} $
  4. 优点:在减少假阳性重要的场景中很有用
  5. 缺点:不考虑假阴性(FN);可能通过极度保守的预测获得高精确率

召回率(Recall)

  1. 含义:真正例数量与所有实际正例数量的比值
  2. 作用:衡量模型发现真实正例的能力
  3. 计算方法:$\text{Recall} = \frac{\text{真正例(TP)}}{\text{真正例(TP) + 假负例(FN)}} $
  4. 优点:在减少漏报重要的场景中很有用
  5. 缺点:不考虑假阳性;可能通过预测过多正例获得高召回率

F1分数(F1 Score)

  1. 含义:精确率和召回率的调和平均值
  2. 作用:平衡精确率和召回率,提供综合评估
  3. 计算方法:$\text{F1} = 2 \times \frac{\text{Precision} \times \text{Recall}}{\text{Precision} + \text{Recall}}$
  4. 优点:综合考虑精确率和召回率;在数据不平衡情况下比准确率更有效
  5. 缺点:可能在某些应用中需要偏向精确率或召回率的权衡

语言生成质量指标

困惑度(Perplexity)

  1. 含义:评估语言模型预测文本的不确定性程度
  2. 作用:测量模型对测试数据的预测能力
  3. 计算方法:$\text{Perplexity} = 2^{-\frac{1}{N}\sum_{i=1}^{N}\log_2 P(w_i|w_1,...,w_{i-1})} $
  4. 优点:自动化评估;不需要参考答案
  5. 缺点:较低的困惑度不一定对应高质量的文本;跨模型比较困难
  6. 注意:困惑度计算时的分母实际上是计算了模型预测整个文本序列的平均信息量(或称为交叉熵)。值越大意味着模型对文本的预测能力越强(不确定性越小)。但是困惑度公式对这个分母取负号再以2为底取指数,将其转换为更直观的度量 - 困惑度值越小,表示模型预测能力越强。

BLEU(Bilingual Evaluation Understudy)

  1. 含义:评估生成文本与参考文本的n-gram重叠度
  2. 作用:主要用于评估机器翻译和文本生成质量
  3. 计算方法:基于n-gram精确率,带有简短惩罚:$\text{BLEU} = \text{BP} \times \exp\left(\sum_{n=1}^{N} w_n \log p_n\right)$
  4. 优点:广泛使用;相对容易计算
  5. 缺点:仅基于精确匹配;忽略语义和上下文;需要多个参考

ROUGE(Recall-Oriented Understudy for Gisting Evaluation)

METEOR

BERTScore

推理能力指标

通过率(Pass@k)

解决率(Solve Rate)

对话评估指标

响应相关性(Response Relevance)

一致性(Consistency)

信息丰富度(Informativeness)

知识与事实准确性指标

事实准确率(Factual Accuracy)

校准错误率(Calibration Error)

综合指标

MMLU分数

HELM分数

Elo评分