
微调一个 Embedding 模型后我又问了 7 个问题上一篇博客里我用 50 条数据、8 分钟时间在自己的 MacBook 上微调了一个 m3e-base 模型。微调完之后我盯着屏幕上的文件冒出了一连串问题。这篇文章就是这些问题和答案的记录。Q1模型下载到哪里去了跑step1_test_model.py的时候脚本里写的是modelSentenceTransformer(moka-ai/m3e-base)没指定任何路径400MB 的模型凭空就加载了。它藏在哪答案是 HuggingFace 的默认缓存目录~/.cache/huggingface/hub/models--moka-ai--m3e-base/首次运行时从 HuggingFace Hub 下载之后直接读本地缓存。你可以用du -sh看一眼$du-sh~/.cache/huggingface/hub/models--moka-ai--m3e-base/ 391M391MB和博客里说的 400MB 基本吻合。所以如果你哪天想清理磁盘空间知道去哪里找它了。Q2脚本的正确运行姿势是什么四个脚本要按顺序跑但在此之前有一个容易忽略的前置步骤——激活虚拟环境。cdembedding-finetunesourcevenv/bin/activate# 激活虚拟环境pipinstall-rrequirements.txt# 安装依赖装进 venv 里python step1_test_model.py# 下载模型测试推理python step2_prepare_data.py# 查看和验证训练数据python step3_finetune.py# 微调CPU 约 5~10 分钟python step4_compare.py# 对比微调前后效果每个脚本跑完都会告诉你下一步该干什么像游戏里的任务指引一样。Q3pip install 会污染我的全局环境吗不会。关键在于你先执行了source venv/bin/activate。激活虚拟环境之后pip指向的是 venv 里的那个 pip所有依赖都装进了embedding-finetune/venv/lib/python3.13/site-packages/里跟系统环境完全隔离。你可以验证一下$whichpip /Users/你的用户名/.../embedding-finetune/venv/bin/pip# ← 在 venv 里看到路径里有venv就对了。反过来说如果你忘了激活虚拟环境就直接pip install那就真的装到全局去了。一句话先 activate再 install。顺序不能反。Q4原始模型是一个文件微调后变成一个文件夹其实两者都是一组文件只是存储格式不同。原始模型在 HuggingFace 缓存里长这样models--moka-ai--m3e-base/ ├── blobs/ │ ├── d2c4a5f5d0a9... ← 其实就是 model.safetensors390MB │ ├── 15df79188... ← 其实就是 tokenizer.json │ └── ... ← 文件名全是哈希值人看不懂 └── snapshots/ └── 764b537a.../ ├── model.safetensors → ../../blobs/d2c4a5f5... 符号链接 └── ...HuggingFace 借鉴了 Git 的设计真正的文件放在blobs/里用内容哈希做文件名方便去重和版本管理。对人类不友好但对程序很高效。微调后的模型就直观多了output/finetuned-m3e/final/ ├── model.safetensors ← 390MB一亿多个微调后的参数 ├── config.json ← 模型结构 ├── tokenizer.json ← 分词器 ├── tokenizer_config.json ├── sentence_bert_config.json ├── modules.json └── 1_Pooling/config.json文件名都是人话一目了然。但本质上两者装的东西完全一样模型权重 分词器 配置文件。模型结构、参数数量都没变唯一的区别是那 1 亿多个参数的数值被你的训练数据调过了。打个比方同一个 Excel 模板填了不同的数据。模板没变数据变了。Q5重复跑 step3权重会不断变化吗不会。因为脚本里写死了从原始模型开始modelSentenceTransformer(moka-ai/m3e-base)# 每次都从这里出发所以不管你跑多少次流程都是第 1 次原始模型 train_data.csv → 微调 → 保存 第 2 次原始模型 train_data.csv → 微调 → 覆盖保存 第 3 次原始模型 train_data.csv → 微调 → 覆盖保存起点相同数据相同结果基本一致有微小的随机性差异但可以忽略。如果你想让权重不断变化需要改成加载上一次的微调结果modelSentenceTransformer(./output/finetuned-m3e/final)# 从上次的结果继续但这通常不是个好主意——下一个问题会解释为什么。Q6那正确的微调姿势是什么往train_data.csv里追加新数据然后从原始模型全量重训。train_data.csv 的增长过程 第 1 周50 条 → python step3_finetune.py 第 2 周80 条 → python step3_finetune.py 第 3 周120 条 → python step3_finetune.py ... 每次都是原始 m3e-base 全部数据 → 微调你可能会问为什么不在上一次微调的基础上继续训练那样不是更快吗问题在于灾难性遗忘——模型学新东西的时候会把旧东西忘掉。就像一个学生复习第三章的时候把第一章忘了。而全量重训等于每次考试前把所有章节从头复习一遍最笨但最稳。以你目前的数据量几十到几百条全量重训也就 5~10 分钟完全没必要搞增量训练。Q7数据量大到没法全量重训了怎么办如果有一天你的训练数据涨到了几十万条全量重训要好几天那就需要对抗遗忘的策略了。从简单到复杂有这么几种方法 1旧数据回放最实用训练新数据时随机抽 20%~30% 的旧数据混进去一起训练。本轮训练数据 全部新数据 随机抽样 30% 旧数据就像每次考试前新章节全看旧章节挑着复习。效果好实现简单。方法 2降低学习率learning_rate2e-5# 初次微调正常步幅learning_rate2e-6# 增量训练小步慢走少改旧参数步子小了新东西学得慢一点但旧东西也不会被踩掉太多。通常配合其他方法一起用。方法 3冻结底层模型有很多层底层是通用能力中文语法、词语关系顶层是任务能力酒店相似度。┌─────────┐ │ 顶层 │ ← 只调这里 ├─────────┤ │ 底层 │ ← 冻结不许动 └─────────┘冻结底层 保护通用知识不被破坏。方法 4LoRA / 适配器大模型主流方案根本不改原始参数而是在模型旁边挂一个小插件只训练这个插件。原始 m3e-base冻结永远不动 │ ├── 适配器 A酒店行业数据训练 → 酒店场景挂载 ├── 适配器 B餐饮行业数据训练 → 餐饮场景挂载 └── 适配器 C交通行业数据训练 → 交通场景挂载原始模型纹丝不动不同场景换不同插件。压根就没有遗忘问题因为没改过原始参数。每个适配器只有几 MB训练也快。这也是现在大模型微调比如 ChatGPT 的行业定制最主流的方案。选哪个方法实现难度适用场景全量重训最简单数据 几万条你现在的场景旧数据回放简单数据太多全量训练太慢降低学习率简单配合其他方法使用冻结底层简单数据少、怕过拟合LoRA / 适配器中等多场景切换、大模型微调但说实话对于 Embedding 模型微调这个场景99% 的情况下全量重训就够了。等你真正遇到需要 LoRA 的那一天你的认知和工程能力也足以应对了。写在最后这些问题看起来琐碎——模型存在哪怎么装依赖重复跑会怎样但正是这些小问题构成了真正的理解。会跑脚本不等于懂模型就像会开车不等于懂发动机。但当你知道油箱在哪、变速箱怎么工作、为什么不能一直踩油门不换挡——你就从会开车变成了懂车的人。微调模型也是一样。知道权重存在哪、每次训练从哪个起点出发、为什么不能无脑叠加训练——这些才是让你从跑通了脚本变成真正掌握了微调的关键。