FeedLens反馈透镜

技术架构

总览

flowchart TB
    subgraph 离线["离线:数据、训练、评测"]
        LX[lexicon.py<br/>标注规范与语法词表] --> GEN[generate.py]
        OOD[ood_handwritten.txt] --> BUILD
        GEN --> BUILD[build.py<br/>清洗/去重/划分/泄漏检查]
        BUILD --> DS[(data/processed/*.jsonl)]
        DS --> TRAIN[train.py]
        BASE[(MiniMind 官方权重)] --> TRAIN
        TRAIN --> RUN[(runs/&lt;name&gt;/<br/>日志 检查点 best.pth)]
        RUN --> EVAL[evaluate.py]
        DS --> EVAL
        RULES[baseline_rules.py] --> EVAL
        EVAL --> RES[(eval/results/<br/>预测 指标 失败案例)]
        RUN --> PKG[package.py]
        RES --> PKG
        PKG --> MODEL[(models/feedlens-minimind-v0.1/<br/>权重 配置 说明卡)]
    end
    subgraph 在线["在线:网页应用"]
        WEB[浏览器<br/>原生 HTML/CSS/JS] <--> API[FastAPI<br/>app/server.py]
        API --> FM[FeedLensModel<br/>modeling.py]
        MODEL --> FM
        RES --> API
    end
    subgraph 上游["上游:MiniMind(只读引用)"]
        UP[model_minimind.py<br/>model_lora.py<br/>tokenizer]
    end
    UP --> TRAIN
    UP --> FM
    SCHEMA[schema.py<br/>标签/提示词/解析] --- GEN
    SCHEMA --- TRAIN
    SCHEMA --- EVAL
    SCHEMA --- API

目录结构

My Minimind Project/
├── feedlens/                 本项目的 Python 包
│   ├── schema.py             任务定义:标签、提示词、输出解析(唯一来源)
│   ├── modeling.py           对接上游模型;统一的提示词拼接与批量解码
│   ├── train.py              训练:LoRA / 全参 / 从零,支持续训
│   ├── evaluate.py           评测与报告生成
│   ├── metrics.py            指标实现(纯 Python)
│   ├── baseline_rules.py     关键词规则基线
│   ├── infer.py              命令行推理
│   ├── package.py            打包模型目录与说明卡
│   └── data/
│       ├── lexicon.py        标注规范 + 合成语法词表
│       ├── generate.py       合成数据生成器
│       ├── build.py          数据流水线
│       └── review.py         抽样人工复核
├── configs/                  训练配置(YAML)
├── data/raw | processed | reports
├── third_party/minimind/     上游源码,固定在提交 f659b55,未修改
├── weights/base/             上游官方权重(下载,不入库)
├── runs/                     每次训练的配置、日志、检查点
├── models/                   打包后的可发布模型
├── eval/                     系统定义、原始结果、自动生成的报告
├── app/                      网页应用(FastAPI + 静态前端)
├── site/                     项目介绍页与文档中心(由 docs/ 生成)
├── docs/                     本文档
├── scripts/                  批处理脚本
└── tests/                    流程级测试

模块说明

任务定义:schema.py

标签集合、输入长度限制、提示词模板、输出解析都只在这一个文件里定义。数据生成、训练、评测、规则基线、后端接口全部从这里读取。

这样做是为了防止一类很常见的错误:训练用的格式和评测或上线解析的格式悄悄不一致,导致指标虚低或虚高却难以发现。

与上游的边界:modeling.py

来自上游 MiniMind(未修改) 本项目实现
模型结构 MiniMindForCausalLM(RoPE、RMSNorm、分组注意力) 权重加载(weights_only=True,只反序列化张量)
分词器与对话模板 提示词拼接函数 render_prompt
LoRA 的 apply_lora / save_lora / load_lora 按长度分组的批量贪心解码
自回归生成循环 generate(含 KV 缓存) FeedLensModel 推理封装

上游代码放在 third_party/minimind/,通过把该目录加入 sys.path 来导入,不复制、不改动。好处是升级上游时只需切换提交并重跑测试,也让“哪些是我写的”一目了然。

训练:train.py

上游提供了 train_full_sft.py 和 train_lora.py。本项目没有直接调用它们,而是重写了训练循环,原因如下:

需求 上游脚本的情况 本项目的做法
在 Apple MPS 上训练 混合精度写死为 torch.cuda.amp 设备自动选择;仅 CUDA 启用混合精度
验证集与最优检查点 无验证环节,按步数覆盖保存 定期计算验证损失,保存最优
结构化日志 仅打印到终端 同时写 JSONL,供曲线和文档读取
训练与推理前缀一致 以 80% 概率删除空的思考标签 始终保留,与推理时逐 token 相同
精确续训 支持,依赖固定长度 padding 支持,样本顺序只由(种子,轮次)决定
数据加载依赖 需要 datasets 库 直接读 JSONL

学习率调度、权重保存格式(half 精度的 state dict)与上游保持一致,所以本项目训练出的全参数权重可以被上游的 eval_llm.py 直接加载。

评测:evaluate.py、metrics.py

网页应用:app/

层 选型 理由
后端 FastAPI + Uvicorn 几个 JSON 接口加静态文件,足够轻;自带接口文档
前端 原生 HTML / CSS / JS,无构建步骤 只有一个页面、四个视图;不引入框架意味着克隆下来就能运行,也方便嵌入个人网站
推理设备 默认 CPU 实测这个 64M 模型的单条请求在 CPU 上比 MPS 更快(见 评测报告)
状态 无数据库、无会话 v0.1 不保存任何用户输入

文档站点:site/

scripts/build_site.py 把 docs/*.md、自动生成的数据报告、评测报告和模型说明卡转成静态 HTML。站点只使用相对路径,可以整个目录复制到任何静态托管位置,或挂在个人网站的子路径下。

关键技术决策

决策 选择 放弃的方案 理由与代价
输出格式 四行“字段:值” JSON 6400 词表下符号被切碎,JSON 更长更易错。代价:需要自己写解析器
解码方式 贪心 采样、约束解码 可复现。约束解码能保证格式合规,但会掩盖模型真实的格式学习情况,留作后续优化
起点权重 官方 full_sft 官方 pretrain;随机初始化 随机初始化作为对照实际训练并评测;从 pretrain 出发的对照配置已写好但没有运行。见 训练记录
微调方式 全参与 LoRA 都做,按结果选 只做一种 模型只有 64M,全参微调本机几分钟可完成,没有必须用 LoRA 的资源压力
选检查点 验证损失最低 训练结束时的权重;按测试集选 不能用测试集选。事后看,按验证损失选是错的:两次训练里它选中的检查点都不是生成准确率最高的。应当按验证集生成准确率选,训练脚本目前只是额外保存了结束时的权重,见 训练记录 第 7 节
训练框架 自写循环 Hugging Face Trainer / PEFT 模型是自定义结构,上游 LoRA 也是手写的;自写循环约 250 行,可完全解释

运行环境

项目 版本
硬件 Apple M5,24 GB 统一内存,无独立显卡
系统 macOS 27.0.1(arm64)
Python 3.12.13(uv 管理的虚拟环境)
PyTorch 2.14.1(MPS 可用)
Transformers 4.57.6(与上游 requirements.txt 一致)
上游 MiniMind 提交 f659b55

依赖列表见仓库根目录 requirements.txt。

本页由 docs/06-architecture.md 生成。