项目复盘
1. 做成了什么,没做成什么
做成了
- 一条从任务定义、造数据、训练、评测到网页应用的完整流程,每一步都实际运行过,每个数字都能追溯到脚本和原始输出。
- 一个能用的模型:
FeedLens-MiniMind v0.1.0,在 MiniMind 官方权重上全参数微调得到。输出格式合规率 100%,CPU 上单条请求约 130 毫秒,不需要 GPU。 - 一个可以真实输入、真实推理的网页应用,以及没有权重时如实标注的演示模式。
没做成
- 模型没有超过关键词规则。 在三标签全对率上,微调后的模型与一份约 90 行的规则在所有测试集上都分不出高下(同分布 66.8% 对 71.9%,分布外 58.1% 对 58.1%)。模型明确更好的只有情感判断,以及同分布数据上的摘要。
- 预先写下的五条验收标准,达成两条(格式合规、延迟),三条没有达成(同分布准确率、超过规则基线、摘要质量)。
- 第二轮数据迭代没有带来独立测试集上的改进。 照着失败案例扩充语法后,开发集从 54.0% 升到 90.5%,独立测试集从 58.1% 到 53.0%。
- 没有真实数据,没有做用户研究。
所以这个项目目前能说明的是:我能把一个小模型微调项目从头到尾做完,并且有办法知道它到底行不行。它不能说明这个模型在真实场景里有用。
我认为最有价值的三个发现
- 基线和独立测试集比模型本身更重要。 如果没有写那 90 行规则,我会把“微调后 67%,训练前 0%”当成一个不错的结果。如果没有那份手写的分布外集和按核心表达分组的划分,我看到的会是一个高得多、也假得多的数字。
- 验证损失不能用来选检查点。 两次训练里,验证损失最低的检查点都不是生成准确率最高的,其中一次选中的甚至是不到一轮的权重。
- 模型和规则犯的错不一样。 两者判断一致时准确率在 86%–99%,覆盖一半左右的样本。相比“用模型替代规则”,“把模型当作规则的第二意见”可能是这个小模型更现实的用法——这还需要真实数据验证。
2. 关键决策回顾
| 决策 | 当时的理由 | 回头看 |
|---|---|---|
| 选“反馈结构化分诊”而不是意图识别或需求结构化 | 输入输出都短、标签封闭、能量化;一个生成模型同时出分类和摘要 | 分类部分适合小模型。摘要部分是整个任务里最不可靠的:模型学到的是“从见过的摘要模板里挑一个再改几个字”,遇到没见过的问题就会拼出不通的句子。如果重来,我会把摘要改成“从原文中抽取片段”,或者干脆在 v0.1 不做摘要 |
| 基于官方权重微调,而不是从零训练 | 本机没有 NVIDIA 显卡;任务不需要重新学语言 | 对照实验支持这个选择:随机初始化只用任务数据训练,验证集上低 20 个百分点 |
| 全参数和 LoRA 都跑,按验证集选 | 模型只有 64M,没有必须省资源的理由 | 都跑是对的。但两者在验证集和测试集上的高低方向相反,只有一个种子,说不出哪种更好;按验证集选出的全参数模型,在测试集上反而是几个微调模型里最低的 |
| 数据全部自己合成 | 没有可用的、许可清晰的中文反馈标注数据 | 这是项目最大的局限。合成语法让流程能跑通、能精确控制泄漏,但也让模型学会的是“这套语法”,而不是“用户反馈” |
| 按“核心表达”分组划分,再加一份手写分布外集,再加一个规则基线 | 防止合成数据的测试分数虚高;给模型一个必须跨过的门槛 | 这是整个项目里最值得的设计。没有它们,我不会知道模型其实没有超过规则 |
| 用验证损失选检查点 | 最省事,训练时顺手就能算 | 错了。应当用验证集上的生成准确率,见训练记录第 7 节 |
| 预先写下验收标准 | 避免事后挑有利的指标讲 | 值得。其中“三标签全对率 ≥ 90%”定得没有依据;应当先跑出基线,再定目标 |
| 第二轮按失败案例手工补语法 | 最直接的改法 | 得到了一个清楚的否定结论:补到的学会了,没补到的没有改善。代价是多花了一轮训练和一份不再独立的开发集 |
| 重写训练循环,而不是直接用上游脚本 | 上游脚本不支持 MPS 混合精度以外的设备、没有验证环节 | 多写了约 250 行,换来验证集选模、结构化日志和可测试的续训。值得 |
| 输出用四行文本,不用 JSON | 小词表下 JSON 符号容易出错 | 微调后的模型格式几乎不出错,说明这个格式对小模型是友好的。没有做 JSON 的对照实验,所以不能说它“更好” |
| 应用里不用规则兜底 | 兜底会让“模型到底行不行”说不清 | 坚持了。代价是访客会直接看到模型的错误输出 |
3. 过程中犯的错和发现方式
这些都是实际发生过的,按发现顺序记录。
| 问题 | 怎么发现的 | 怎么修的 | 教训 |
|---|---|---|---|
| 冒烟测试“取前 256 条”,而数据文件按问题类型排序,取到的全是同一个类别 | 冒烟训练的训练损失降到 0.05,验证损失却停在 1.2,差得不合常理 | 改成按种子随机抽样 | 取子集一定要随机;指标反常时先怀疑数据 |
| “训练是否被中断”的判断写在循环内部,恰好停在一轮的最后一步时会判断成“已跑完” | 续训一致性测试失败 | 改为循环结束后比较已完成步数与总步数 | 边界条件靠测试发现,比靠读代码可靠 |
| 在 MPS 张量上转 float64 报错 | 第一次冒烟训练直接崩溃 | 先搬到 CPU 再转 | 换设备后第一件事是跑通最小流程 |
| 训练的同时在 CPU 上跑评测,两边都被严重拖慢 | 训练吞吐从约 35 条/秒掉到约 17 条/秒,评测十几分钟跑不完一个划分 | 停掉并发任务,改为顺序执行 | 统一内存的机器上,CPU 和 GPU 任务会互相抢资源;ft-full-v1 记录的训练用时因此偏长,不能用来比较方法快慢 |
| 以为 GPU(MPS)推理一定更快 | 在基础模型上生成 48 个 token,CPU 实测约 240 ms,MPS 约 490 ms | 应用默认用 CPU | 小模型逐 token 生成时,每一步的调度开销比计算本身大 |
| 写 v2 语法时无意中用了测试集里的词(“拖动排序”) | 写完后用程序扫描语法与 ood_test 的字面重合 |
删除该取值,改写两处相近措辞,重建数据 | 自己出题自己答,泄漏不需要故意;能做的是加检查,并在报告里明说 |
4. 实际执行过的验证
下面每一项都真的运行过,括号里是依据。
数据
- 数据流水线完整运行,产出 5 份数据文件和构建报告(
data/reports/data_report.md) - 分组不泄漏、保留领域不进训练集:流水线里的硬断言通过;测试
test_lexicon_and_group_split通过 - 训练集与各评测集的字符相似度实测,没有一条超过 0.6(v1)
- 改动生成器以支持 v2 之后,重新构建 v1,全部文件的 SHA256 与改动前一致
训练
- 上游兼容性:官方权重
strict=True加载成功,参数量 63.91M,校验和与发布页一致 - 冒烟训练:损失下降、参数更新、权重保存(
runs/smoke/) - 四次正式训练(
ft-full-v1、ft-lora-v1、scratch-v1、ft-full-v2)均跑完,params_updated为真(runs/*/summary.json) - 保存的权重重新加载后验证损失与保存时一致,并能推理:测试
test_train_updates_params_and_reload_matches通过 - 中断后续训与一次跑完的权重逐位相同(CPU、小模型):测试
test_resume_is_bit_identical通过 - LoRA 只改适配器、不动主干,适配器可单独保存和加载:测试
test_lora_adapter_roundtrip通过
评测
- 九个系统在同一批样本上评测,逐条预测、指标、失败案例保存在
eval/results/ - 系统之间的差距用按核心表达聚类的配对自助法检验(
eval/analysis/paired_comparison.md) - 发现指标存在二次舍入后,根据已保存的逐条预测重算了全部汇总指标(
--rescore),重算时断言逐条得分与原记录一致 - 指标实现用手算的例子核对:
tests/test_metrics.py5 项通过
应用
- 没有权重时:服务正常启动,页面显示演示模式横幅和“演示数据”戳记,任意输入被拒绝并说明原因(浏览器中实际操作验证)
- 有权重时:输入 → 后端 → 模型 → 结果卡的完整链路,返回真实模型输出和耗时(浏览器中实际操作验证)
- 批量分诊 10 条、排序、汇总正常
- 输入校验:空、过短、超长、批量超限都返回 422 和中文说明(
scripts/smoke_api.sh全部通过) - 模型输出格式不合规时的展示、后端连不上时的提示(在浏览器里模拟接口返回验证)
- 窄屏 375 像素下没有横向溢出,导航可横向滚动
5. 没有做的事
没有执行,只有方案或命令
| 事项 | 现状 | 需要什么才能做 |
|---|---|---|
| 在真实用户反馈上评测 | 没有数据 | 100–300 条脱敏后的真实反馈,按标注规范标注 |
| 合成数据的人工抽样复核 | 待填写的表格已生成(data/reports/review_*.csv),无人填写 |
约 1 小时人工 |
| 用户访谈与可用性测试 | 提纲和模板已写好 | 5–8 位目标用户 |
| NVIDIA GPU 上的训练、从零预训练 | 只有命令和估算 | 一张 8 GB 以上显存的显卡,几小时 |
| 多个随机种子、超参数搜索 | 每种方式只跑了一组 | 更多训练时间 |
| “从仅预训练权重微调”的对照、LoRA 在 v2 数据上的训练 | 配置已写好,没有运行 | 各约 10–30 分钟 |
| 按生成准确率选检查点 | 发现了问题,训练脚本还没改,只是额外保存了训练结束时的权重 | 约 1 小时开发 |
| BERT 类分类器对照 | 没做 | 半天 |
| 线上部署 | 只在本机运行过。给静态托管用的展示包已生成并本地验证(见 部署到个人网站),还没有公开部署;实时推理的线上版没有做 | 静态版:登录 Cloudflare 上传即可。实时版:一台有 1 GB 以上内存的服务器 |
做了但有明显不足
- 分布外测试集只有 117 条,且与语法出自同一作者。
- 标注是单人完成的,没有一致性检验。
- 摘要只用 ROUGE-L 评,没有人工评分。
- 应用没有自动化的端到端测试,界面验证是手工做的。
- 没有处理并发请求,推理是串行的。
6. 如果继续做,下一步的顺序
完整的计划和每一项的依据见 评测报告 第 11 节。顺序是:
- 先拿真实数据。 哪怕只有 100 条。在此之前,任何进一步的调参或补数据都是在对着合成数据优化。
- 修评测和选模的方法:按生成准确率选检查点;每个配置跑多个种子。这两项不需要新数据,立刻能做。
- 换一种造数据的方式:用大模型改写来扩充说法;测试集由别人或别的模型来写。
- 重新设计摘要:改为抽取,或者只在有把握时输出。
- 验证“模型与规则一致则采信”的组合策略。
不打算做的:继续按失败案例手工扩充语法。第二轮已经说明这条路走不通。
7. 需要补充的资源
| 资源 | 用途 | 优先级 |
|---|---|---|
| 一批真实用户反馈(哪怕 100 条) | 建立真实测试集;检验类别体系是否合用 | 最高 |
| 第二位标注者 | 检验标注规范是否清晰,计算一致率 | 高 |
| 5–8 位愿意聊半小时的目标用户 | 验证需求假设 | 高 |
| 一张 NVIDIA 显卡的几小时使用时间 | 更大规模的数据、多种子实验、从零预训练 | 中 |
| 代码仓库地址 | 让文档里的代码链接可点击 | 中 |
本页由 docs/10-retrospective.md 生成。