大语言模型微调指南:从零到精通

Reddit r/LocalLLaMA 新闻

摘要

本文介绍了大语言模型微调的完整指南,通过一项案例研究,对比了微调相较于RAG(检索增强生成)和系统提示词的优势。该研究显示,对Mistral 7B模型进行微调后,其医疗报告任务的准确性从35%提升至98%。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/08/27 07:37

# 如何微调大语言模型:端到端指南 | 来自 Towards Data Science 来源:https://towardsdatascience.com/how-to-fine-tune-an-llm-an-end-to-end-guide/ ## 为什么要微调? 让我举一个真实的个人例子。 **我们微调了一个70亿参数的模型,它在表现上完全超越了基础模型**,但仅针对一个非常狭窄的子任务:*填写乳腺癌综合报告模板*。 这是一个极其困难的任务,涉及复杂的输入格式、分支逻辑以及必须严格按顺序出现的字段。大语言模型需要完美辨别40种不同的组织学亚型中哪些会触发字段子集的哪个分支,且不能出现幻觉。这在条件表中几乎不可能定义。一个字段错误,整个输出就作废了。 **当我们使用激进的系统提示结合一些轻量级RAG时,准确率(使用Claude Opus 4.6)仅约35%**。我们必须将整个模板内容放入上下文,外加一份详细指南说明何时何地使用哪个字段。每次调用大约需要3万 token。结果如何?这里漏了字段,那里多了不必要的子节,还有幻觉等等,这意味着人类仍需手动阅读和编辑整个文档。此路不通。 微调了Mistral 7B模型(使用QLoRA)后,**我们的准确率跃升至约98%**。我对此效率感到震惊。我们提升了63个百分点,并完全消除了(此任务的)API费用。根据我们最初的估算,以我们所需的规模运行(使用前沿模型)成本约为32万美元。**我们免费实现了目标。** *不包括微调费用、运行本地模型(我们已有大规模运行基础)或每次API调用的能耗测量*。 这就是为什么要微调。 **尽管普遍认为,RAG + 系统提示无法解决微调能解决的问题**,而此类问题随处可见。 ## 本文内容概述: - **何时微调**(RAG 与微调之争) - **LoRA/QLoRA背后的数学直觉** - **技术实现细节** - **使用自定义评估框架进行评估** 阅读后,你将了解何时微调、为何有效以及如何在实践中具体实现。 ## 何时微调 ❓❓*我真的需要微调吗?*❓❓ 也许。寻找以下微调模式之一: **严格、高度特定的格式要求** 你需要大语言模型输出特定格式,这些格式非常复杂且对偶尔出现的幻觉(如字段缺失或添加)毫不容错。一些显著例子: - **传统企业文档:** 大公司通常有根深蒂固、特立独行的模板,包含无数条件分支。 - **法院/法律文件:** 每个司法管辖区都有自己的格式和模板。这些表单显然不在大语言模型的输入数据中,需要作为新知识引入。 - **医疗表单:** 它们很复杂,常包含冗余信息,且必须分毫不差。 **成本约束** 系统提示中数千(或数万)的 token 在每次客户咨询时运行。规模化下,这就是真金白银和延迟。一个已内化这些模式的微调模型无需二者。 **复杂指令与组合爆炸** 系统提示适用于简单约束,但当规则重叠时常常失效。如果你的任务涉及庞大的决策树(例如,“如果A,则做B,但如果C和A,则做D,除非E存在...”),你可能会触碰上下文学习的极限。在我们的案例中,规则组合空间爆炸,无法合理编码到表中。另外请记住,上下文随长度而衰减。一个包含50条不同规则的系统提示很可能在此处或彼处遗漏一条,导致整个输出作废。 **自定义语气** 这对我们不相关,但值得一提。如果你需要客服代表具有特定的“品牌声音”,微调通常比使用系统提示更有效。相关点还有:你需要在每次客户互动中添加系统提示以维持特定声音或语气。如果你的“品牌声音”提示有2000个 token,成本会迅速累积。 ## 何时使用RAG 大多数从业者遵循的经验法则:RAG *主要* 增强模型的知识。微调 *主要* 影响输出行为。我说 *主要*,因为全量微调确实可以给大语言模型增加新知识,而RAG可以(且常被用来)修改大语言模型的默认行为。这不是非黑即白,所以请运用你的最佳判断。 **RAG可能是更好选择的情况:** - 你的知识库频繁变化 - 你很少需要增强行为 - 模型需要访问随时间演进的文档、政策或事实 - 你只有几百个高质量训练样本 - 你可以用一个小型系统提示可靠地修改行为 **可行的解决方案往往是两者的混合。** 我们并没有完全摒弃系统提示或RAG,只是大幅降低了对它们的依赖。 现在,让我们深入数学,理解微调的机制。 ## LoRA/QLoRA背后的数学直觉 在探讨LoRA(低秩适应)及其被统称为“参数高效微调(PEFT)”的衍生物的任何数学细节之前,我们需要理解微调在底层实际做了什么。 > 🤔 为什么花时间理解LoRA/QLoRA背后的数学? 理解LoRA背后的数学对于**判断其是否是当前任务的正确策略**至关重要。这是真正理解为何/何时微调与为何/何时使用RAG的人之间的分水岭。我建议不要复制粘贴训练脚本并直接使用默认值,尽管它可能开箱即用。相反,为这里发生的事情培养一种数学直觉。这样,调试就不再是猜测,而是精确工程。 例如: 误解LoRA中的“秩”是什么,会使诊断过拟合问题变得困难。 比如:为什么选择`rank` 8而不是`rank` 32?降低`alpha`参数对残差流有什么影响? 你无需从头推导任何公式。我描述的方式(希望)是易于理解的,只要你对大型Transformer模型的工作原理有任何了解。 微调是大语言模型所经历的大型预训练任务的*延续*。 在**预训练**中,目标通常是**预测自然语言序列中的下一个 token**。 在有监督微调(SFT)中,相同的因果下一个 token 预测目标应用于特定示例:`(提示, 补全)`对。 在许多SFT设置中,损失仅在助手/补全 token 上计算,而不是在用户提示上。 数学目标大致相同: token 上的交叉熵(负对数似然)。 L(θ) = −E_{(x,y)∼D} ∑_{t=1}^{T} log P_θ(y_t | x, y) 旁注,当你使用Hugging Face或PEFT时,论文中的α/r被超参数γ替代,因此这些公式等价: h = W₀x + γBAx ⇔ h = W₀x + (α/r)BAx **概念上,我们学习将输入投影到一个低秩子空间,该投影增强前向传播,类似于残差流校正。** γ用于控制这种增强的强度。 同样,需要理解的重要一点是**原始模型保持完全不变。** 我们这里学习的只是一个**小得多的伴随模型,它扰动原始模型内的内部激活,从而得到我们期望的行为。** 它有效! **这带给我们的是** - 用少得多的参数改变原始模型行为的能力 - 使用完全相同的基础模型学习多个适配器(可能用于不同任务)的能力 *🤚 很好。但这如何解决全量微调的问题?* 在全量微调中,对于我们的原始权重矩阵 W₀ ∈ ℝ^{4096×4096},我们需要学习1600万个不同的参数,以及优化器所需的3300万个动量。这意味着更大的硬件需求。 使用LoRA(例如秩为8时):我们只需要学习65k个参数,以及优化器所需的131k个动量(仅针对原始模型中的这一个权重矩阵)。 ### 对从业者意味着什么 LoRA可以**大幅降低显存需求**来微调大语言模型,因为我们只学习小的秩矩阵。**我们不需要为全尺寸模型的每个参数保存优化器状态,只需为小的秩矩阵保存优化器状态。** 让我们通过将其全部放入一个消费级GPU来使其更进一步。 ## QLoRA(量化低秩适应) 使用LoRA,我们仍然需要将整个模型的基础权重以FP16格式存储,以便学习用于子任务学习的LoRA适配器。QLoRA通过**将冻结的基础模型量化到4位**来弥合这一差距,同时将LoRA适配器本身保持为16位精度。 在 "*QLoRA: 高效微调量化大语言模型*" 论文中,作者包含了几种不同的机制使其变得可行。令人惊讶的是,他们还随论文发布了功能代码,该代码经过社区大量测试,可用于构建你自己的QLoRA微调管道。 ## 4位 NormalFloat(NF4) 该论文介绍其为一种“信息理论最优”的量化策略,优于4位浮点数。此量化按以下方法工作。 4位提供16个可能的值可供操作。我们需要将整个权重分布映射到这16个值之一。简单起见,如果我们为-1到1之间的权重选择等宽区间,并将每个可能的位组合映射到一个值,如下所示: 我们确实可以将网络的每个可能的FP16权重值映射到一个4位代码。然而,***这浪费了容量***,因为只有一小部分权重占据`0000`位区间(尾部),并且更集中在零附近。 NF4利用神经网络权重的预期分布来构建一组更具代表性的16个量化值。由于归一化后的权重通常近似高斯分布且以零为中心,因此量化值在零附近更密集,在尾部更稀疏。于是,我们(示例中的)朴素区间变得更智能,更能代表实际权重。 例如:**我们获得了精度,因为实际权重可以用更少的量化误差重建。** ## 双重量化 4位量化有效,但每一层可能具有不同的分布。如果我们创建全局缩放因子,我们将损失显著的精度。因此,我们需要将所有权重划分为**块**(通常为64个)。每个权重**块**有自己的**唯一缩放因子**,这是一个32位浮点值,用于将4位代码映射回其全精度对应项。 双重量化实现的是对唯一缩放因子的量化,这样我们就不必将所有全精度32位浮点数保留在显存中。 **第一步是将权重量化为NF4:** - 将所有模型权重划分为64个一组的块 - 每个块计算一个FP32缩放常量 - 使用上述过程将权重量化为4位NF4 **第二级(“双”部分):** - 将256个这样的FP32缩放常量分组 - 将它们量化到8位浮点数(FP8) - 每256个一组仅存储一个FP32缩放常量 **根据论文,每个参数大约可节省0.373位。** 虽然很少,但跨越数十亿参数,显存节省是值得的(可能节省2-3GB显存)。 ## 分页优化器 分页优化器是NVIDIA统一内存的一个巧妙hack。对于长上下文序列,显存需求偶尔会激增,导致可怕的OOM(内存不足)状态。 最终,分页优化器主要是处理内存峰值的机制。它们允许部分优化器状态在GPU容量受限时驻留在GPU内存之外。否则,LoRA实现仍然成立。我们可以不再在冻结的、FP16的模型上运行前向/后向传播,而是可以在高度量化的模型上运行这些传播,同时以全精度维护LoRA适配器。 **结果是一套巧妙的内存管理技术,使微调超大模型时显存效率大幅提升。** > **总结**:LoRA使微调内存高效。QLoRA使LoRA更内存高效。这是对优化的优化,在性能大幅降低的前提下保持了性能。 ### 为什么选择QLoRA而非全量微调? 我们的模型有70亿参数。全量微调是可能的,但相对于额外的内存和计算需求,它对此任务收益甚微。LoRA给了我们另一个优势:我们可以独立于基础模型实验适配器。这使得QLoRA成为显而易见的起点。 | 方法 | 我们拒绝/选择的原因 | | :--- | :--- | | 提示工程 | 太不一致 | | RAG | 无法解决行为一致性问题 | | 全量微调 | 不必要的内存/计算开销 | | LoRA | 良好的参数效率 | | QLoRA | 相同LoRA方法,基础模型内存需求大幅降低 | 对我们来说,**QLoRA具有经济意义。** 我们没有无尽的计算资源瀑布,因此需要精明行事。我们将花费更少的计算和推理成本,得到(可能)与全量微调一样准确的结果。如果成功,我们可以为不同子任务构建不同的适配器,实现更进一步的专门化。我们正在做这件事 😎 ## 技术实现细节 这是实践环节。如果你和我们一样,你将会花费: - **70%** 的时间在生成高质量训练数据上 - **20%** 的时间运行评估 - 只有 **10%** 的时间实际运行微调 事实上,实际的微调脚本很轻量,非常容易运行和理解。 > 😱**你可能会对运行自己的微调训练管道感到畏惧。** > 😎 **别怕。** 你尝试定制来自X家公司的模板化微调管道时,会浪费时间和精力。我下面提供的脚本可以轻松根据你的要求定制。 不幸的是,我不知道*你*的具体用例。但是,我可以概述*我们*用例的要求,希望能帮助你在合适的地方形成类比。无论如何,数据是这个过程中最痛苦的部分。 ## 生成高质量训练数据 **情况1:你有4,000-10,000+真实的输入/输出对。** 这是理想情况,因为当想要引导大语言模型以非常特定的方式回答时,微调过程才真正大放异彩。客服公司可能常处于这种情况。 **情况2:我们的情况。你有输出,但没有输入。** 这更困难。当没有东西可引导时,如何引导大语言模型?这类似于试图仅用目标而没有特征来构建分类器。 具体到我们的用例,我们只有最终的、结构化的综合报告的原始语料库。我们需要病理学家在创建结构化报告之前编写的自由手写笔记。 我们考虑过进行全量微调,希望大语言模型能保留最终报告的格式、结构和高度细微的要求。最终,我们决定放弃。我们需要一个非常具体的解决方案:**获取非结构化的病理学家笔记,并将其转化为结构化的综合报告。** 幸运的是,我们找到了一个解决方案。在“*使用指令反向翻译进行自我对齐*”一文中,Li等人反转了问题。而不是“*给定此输入,生成*

相似文章

本地LLM推理优化:完整指南

Reddit r/LocalLLaMA

一份关于在消费级硬件上优化本地LLM推理的全面指南,涵盖llama.cpp、vLLM和LM Studio等工具,并提供关于内存层次结构、层放置和常见故障模式的实用建议。

LLM的有效用例

Lobsters Hottest

本文分享了LLM在软件工程中的实际应用案例,包括通过RAG搜索客户对话、从日志中排查API故障以及内容精简。重点强调了效率提升和减少手动筛选工作。