资讯动态

机器学习周报第40期:LoRA微调7B模型与eval loss排查实战

发布时间:2026/10/19 13:44:26 来源:尧图企业网站定制
写机器学习周报写到第四十期说实话这个数字比我预想中来得快。第四十周这七天我主要把时间花在一件很具体的事情上用LoRA微调一个本地7B开源底座模型目标是让它能按固定格式回答某个垂直场景的指令。这篇周报不会只报喜微调跑通之后我又遇到一个让我半夜睡不着的eval loss异常前前后后追了两天才定位到根因。如果你也在坚持机器学习相关的长期学习记录或者正准备做本地模型的轻量微调这篇内容多少能有点参考价值。1. 四十周之后的周报终于变成了一张“地图”先聊聊周报本身。四十期按每周一篇算就是四十周接近十个月。回看第一期写的东西和现在相比差别不只是技术深度而是整个记录逻辑都变了。前几期我的目标是“今天把某某算法跑通”周报写出来像打卡现在我的目标变成“这周弄明白一件事并且能证明我确实明白”周报更像一张地图。1.1 周报从打卡变成了复盘最开始写周报是痛苦的。一周下来没做几个大任务就觉得没法落笔结果要么记流水账要么把别人的教程要点摘抄一遍当时看有东西可交事后看完全不知道自己在说什么。后来我给自己定死了四个问题本周解决了什么问题、为什么选这个方案、踩了什么坑、下周是继续还是换方向。只允许回答这四个问题之后周报立刻好写了因为“跑通”在周报里不算答案知道为什么跑通才算。前二十期我还有个毛病总想在下一次周报里塞一个“看起来厉害”的新主题于是经常出现上周刚学完X这周就跳到Y的情况。四十周之后再回头看那些能形成积累的周几乎都是围绕同一条主线连续推进的而那些看起来很热闹的跳跃式学习最后基本都断线了连当时的代码都找不回来。所以现在我对周报的定位很明确它不是给别人看的进度条是给两周后的自己留的操作说明书。1.2 四十周路线沉淀四阶段回顾四十周不算短我按周数和主线内容大概整理了一下其实分成了四个阶段阶段周数范围主线内容这一阶段留下的核心资产第一阶段第1-8周Python、数据分析、线性代数与概率统计基础稳定的代码习惯能用数据工具完成清洗和可视化第二阶段第9-16周经典机器学习算法、树模型与集成学习建立评估意识知道什么是过拟合怎么做交叉验证第三阶段第17-32周深度学习基础、NLP与CV两个方向的入门项目能读懂论文核心思路能把结构相对简单的模型复现出来第四阶段第33-40周开源底座模型、RAG、微调与本地部署低成本微调链路能独立跑通从数据准备到模型部署的闭环这个划分不是规划出来的是被踩坑逼出来的。第二阶段我其实有点着急总觉得经典机器学习不如深度学习酷第八周之后就想往神经网络钻结果到第十八周做序列任务时发现自己对特征工程和概率分布的直觉太弱又回头补了好几周。如果重来一次我会在集成学习上多停留几周把偏差方差分解、树模型的可解释性这些内容吃得更透。进入第四阶段之后一个明显的转折是我对“机器学习的周期感”有了体会一个项目从拿到数据到能上线绝大多数时间花在数据清洗、模板设计、评估口径反复对齐上真正模型的训练时间反而是小头。这也是第40周的主线选择背景——我不想再停留在“跑通别人的示例”上而是想完整地把一个微调任务从零做到底。2. 本周主线本地7B模型的LoRA微调实验第40周的主线任务不是凭空定的。从第35周开始我连续在RAG和提示词优化上做尝试但某个固定场景的效果始终卡在一个瓶颈上不是检索不到相关内容而是底座模型拿到内容之后回答格式总是不够稳定。于是我决定进入微调环节用LoRA低成本地调出一个更适合该场景的模型。2.1 为什么在这个节点上选LoRA先说为什么不是全量微调。本地机器只有一张24GB显存的显卡7B规模的开源底座模型如果做全量微调光是AdamW优化器的状态就要占掉参数量好几倍的显存单卡根本塞不下即使有多卡全量微调的收敛稳定性也更难控制。LoRA的思路完全不同冻结原始权重不动在目标模块的旁边插入低秩矩阵只训练这部分极小的参数。这样可训练参数量通常只有原模型的百分之几显存占用大幅下降效果在很多指令微调场景下又非常接近全量微调。理解LoRA有个关键点低秩矩阵和缩放系数不是随便设的。常见配置rank8alpha16也就是说最终更新量要乘以alpha/rank这个系数16除以8等于2更新幅度大概是0到2倍之间。alpha比rank大的时候微调强度会相对高如果让alpha远大于rank容易把底座模型带偏出现所谓的灾难性遗忘。我实测下来指令微调场景r8、alpha16是一个很稳妥的起点跑出来效果不对再按倍数调整而不是一次改一个特别激进的组合。还有一个容易忽略的选型点target_modules的选择。如果你的目标是通用对话能力覆盖q_proj和v_proj就够用如果希望模型更充分地在特定领域内改变行为最好把注意力投影层都包进去也就是同时配置k_proj、q_proj、v_proj、o_proj。我这次用了四个投影层全量覆盖训练时间稍微长一点但回答稳定性比只调q/v要好不少。2.2 数据与模板最容易被低估的环节微调效果好不好数据模板的重要性占到一半以上这一点我是在连续调Prompt失败后才彻底意识到的。LoRA只负责改变模型的行为倾向但如果喂给模型的数据本身格式混乱它学到的东西就是混乱的。我的做法是构造了一个约5000条的指令微调数据集来源是某公开清洗后的对话集合每条样本统一成“指令回答”两段式。def build_sample(instruction, response): # 模板符号必须全局统一不能训练集一套验证集另一套 prompt f指令{instruction}\n回答 full_text prompt response 结束 return full_text def mask_labels(tokenizer, full_ids): labels full_ids.copy() # 只有回答之后的部分参与loss计算指令部分全部置为-100 sep_id tokenizer.convert_tokens_to_ids(回答) sep_pos full_ids.index(sep_id) labels[:sep_pos] [-100] * sep_pos return labels训练阶段我用了padding和截断统一max_length为1024。这里有个很容易踩的细节很多底座模型的tokenizer没有预设pad_token如果不显式把pad_token_id设成eos_token_id训练时padding位置会填成未知token轻则浪费显存重则让模型在生成时把填充符号当成有效内容。我这次一开始就处理了这个问题没有在它身上浪费时间。把数据准备好之后我意识到另一个真相模型并不会自动理解“指令”这两个字的含义它理解的是你在数据里反复出现的模板结构。模板里的特殊符号一旦定下来整个数据集和后续验证脚本都要保持一致任何一处不一致都会让评估结果失真。这个点直接埋下了后文的坑。2.3 配置与显存实测下面是我本次实验实际用的配置实测在单张24GB显存卡上能稳定跑完峰值显存约12到13GB余量比较多如果有更大batch的需求还能往上加。配置项数值/内容底座模型某个7B开源底座模型量化方式4bit加载加载时用bnb量化LoRA rank8LoRA alpha16LoRA dropout0.05目标模块k_proj、q_proj、v_proj、o_projper_device_train_batch_size1gradient_accumulation_steps4max_length1024优化器AdamWlr2e-4warmup_ratio0.03训练数据约5000条2个epoch训练本身花费大约三小时二十分钟速度不是亮点真正的问题出现在训练结束之后。我满怀期待地跑了验证评估结果弹出的数字让我一下清醒了训练loss一路从1.8降到0.7eval loss却从1.2涨到了1.5这个对比怎么看怎么像灾难现场。3. 一个让我半夜醒来的踩坑loss在降eval却一路飞升你如果也做过微调大概率见过这种场景训练集上的loss平稳下降图很漂亮但验证集上的表现却越来越差。常规解释是过拟合我当时也是这么判断的事实证明这个“第一反应”恰恰让我多绕了一大圈。3.1 现象与第一轮错误判断训练日志显示第一个epoch结束时eval loss最低继续训练到第二个epocheval loss开始反弹。我当时的反应很自然这模型过拟合了于是先加了dropout把LoRA dropout从0.05提到0.1又把epoch从2改回1重新训练之后eval依然没有改善。这时候我开始怀疑是学习率的问题又把lr降到1e-4结果eval loss还是维持在高位。那两天做的最蠢的事是我连续改参数重训了三轮每次都花三四个小时最后得到一堆“好像有点用但说不清哪里有用”的结果。直到我停下来仔细看验证脚本才意识到根子可能根本不在训练上而在评估本身。训练loss和eval loss走的根本不是同一套计算口径。3.2 用排除法找到真凶验证集的“prompt泄漏”我重新按顺序排查了一遍这组排查链路值得记下来检查训练集和验证集是否有重复样本用文本哈希对比结果无重复排除数据泄漏。检查tokenizer的padding和truncation是否一致确认pad_token已设置排除填充符号问题。检查训练时labels的计算方式只有回答部分参与loss计算指令部分全部置为-100没问题。检查验证时labels的计算方式问题就出在这里验证脚本直接拿完整序列计算交叉熵没有把指令部分屏蔽掉。这个问题的本质是prompt泄漏不是数据泄漏而是评估口径泄漏。训练时模型确实在学会“只看回答部分怎么生成”但验证时我又让它去预测指令部分的每个token。指令部分的模板符号和引导语对底座模型来说并不是零困惑度的它们大多在原始预训练里没有以这种固定顺序出现过所以模型在这些位置上本来就表现得很差。于是eval loss被一大段prompt token拖高看起来就像越训练越差。更隐蔽的是这种问题在训练曲线上的表现和过拟合几乎一模一样loss下降但eval上升如果第一反应是调正则、调lr只会白白浪费大量训练时间。正确的第一反应应该是先确认train和eval两条链路唯一的区别是数据而不是度量口径。修复起来其实一句话验证时也要给prompt部分打-100的掩码只对回答部分计算loss。我改完验证脚本之后eval loss立刻回到0.75左右与训练loss的趋势基本同步。那一刻的感觉很复杂一方面庆幸不是模型有问题另一方面懊恼为什么第三天才发现。3.3 修复后的对比与本轮经验修复后的对比很直观同一个模型权重、同一份验证集eval loss从1.5降到0.75完全是脚本改动带来的差异。从这件事里我提炼出一条原则训练脚本和验证脚本必须共享同一套数据构建与掩码逻辑哪怕只是一个小函数也要抽成公共模块而不是各写各的。症状第一反应实际根因train loss下降eval loss上升过拟合加dropout、减epocheval未对prompt部分做mask度量口径不一致改参数重训多轮仍无改善lr过高继续降lr模型本身没问题问题在验证脚本这轮也让我对可视化更加警惕只看loss曲线是不够的还必须知道这条曲线是怎么算出来的。同一个指标mask方式不同、损失函数对padding的处理不同、甚至batch拼装顺序不同都会得到差异很大的数字。以后我每次写评估脚本都会在周报里加一条eval口径是什么版本。4. 翻旧笔记时发现我一直在重复犯同一个错误第三周修复完eval问题之后为了写这期周报我翻了一遍前面十几个笔记文件越翻越觉得脸黑。很多周记录的结尾都是“本周跑通了某某Demo”然后下一周直接换主题那个Demo再也没有被提起过。四十周之后再看这种“跑通即完成”的习惯其实是我最贵的隐性开销。4.1 “跑通即完成”的坏习惯“跑通即完成”意味着什么意味着我把复现别人的示例误当成了自己的产出。示例代码能跑是因为作者已经把环境、数据格式、参数顺序都安排好了我只是按回车。两周之后我回头想继续用这段代码时常常连当时怎么启动的都忘了因为笔记里根本没记录启动命令和依赖版本。我统计了一下第三阶段也就是第17到32周期间我至少做了六个小项目但真正在后来被再次复用、继续迭代的只有一个。剩下五个全部卡在“当时能跑现在跑不起来”的状态。这个教训比任何一个模型技巧都值钱机器学习的进步不是靠跑通代码是靠留下可以复现的上下文。别人看你周报里的一个结论如果无法复现这个结论的价值就大打折扣自己看自己的旧代码复现不了更是直接浪费了当初的时间。4.2 本周开始强制执行的三个复盘动作基于这个反思从第40周开始我给自己的周报加了三个强制动作准确说是给实验流程加了三个约束原来的动作现在的替代动作为什么有效周报只写“跑通了”实验目录里新增README写下启动命令、关键参数和选择理由两周后的我能看懂上下文能直接复现实验配置靠脑子记每次训练都写一行记录模型、数据量、显存峰值、耗时、eval loss对比不同配置时有据可查不需要重跑盲猜从不回看旧结论每周抽20分钟回看30天前的笔记给每个结论打标签能及时发现当时判断是否成立避免带着错误认知继续走第一个动作看起来最笨其实最有用。给实验目录写README不需要文采只要回答三个问题怎么跑、为什么这样配、当时卡在哪。我这两周写下来的感觉是写完了心里会踏实很多因为代码不再是一个黑盒。第二个动作让我意识到以前浪费了多少对比机会。以前我在不同周里试过不同的lr因为没有统一记录根本分不清哪个更优只能凭感觉重跑。现在用表格记录之后横向对比几分钟就能完成。第三个动作最反直觉回看30天前的笔记会觉得很丢人因为很多当初信誓旦旦的结论其实漏洞百出。但正是这种“打脸时刻”才是进步最快的信号。如果回看旧笔记觉得全都对大概率说明这段时间没有新输入。5. 下周计划把实验“收口”成可复用的工程模块第40周的LoRA微调实验已经跑通评估口径也修好了但我很清楚现在手里的东西还只是“实验”不是“模块”。下周至少有三件事要推进都是为了把这周的积累变成能长期使用的工具。5.1 下周想推进的三件事第一把LoRA微调流程整理成可复用的模板脚本。这次踩的主要坑是验证脚本和训练脚本不一致下周我会把数据构建、mask逻辑、训练、验证拆成几个独立函数固定统一入口这样换底座模型时只需要改模型路径和模板符号不用再担心两边口径漂移。第二构造一个小型人工评测集大约20条样例覆盖这个垂直场景里最高频的几类问题。以后每次调参、换数据、换prompt模板都统一在这套评测集上跑按格式正确率和内容相关度两个维度打分。没有这个固定的尺子任何“感觉效果变好了”都是不可信的。第三补充推理阶段的对比实验。微调后的4bit模型在GPU上的生成延迟和CPU上的生成延迟差距有多大、是否值得做推理加速、能不能用更小的batch换取更快的首token时间这些问题我还没实测过。下周会专门跑一组延迟对比。5.2 四十周坚持下来的真实感受最后说句实在的很多人问我怎么能每周坚持写一篇机器学习周报我的回答是靠意志力早就断了靠的是把这件事变成系统。固定的时间、固定的格式、固定的复盘点让“写周报”成了一个不需要做决定的行为。到第40周它已经不再是负担而是我判断自己有没有在进步的主要工具。这周最大的收获不是eval loss从1.5降回了0.75也不是LoRA微调模型终于能按格式输出了而是我重新理解了“记录”在机器学习里的位置。模型参数会被忘掉实验细节会被时间冲淡只有那些写下来的、可复现的上下文才是真正属于自己的积累。四十周走完我现在才敢说我大概知道怎么让下一个四十周走得更稳。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑