Fill In the Middle:让语言模型学会“瞻前顾后“
本文涉及的 FIM 推理实现已开源:RWKV-Server https://github.com/AUXStar/RWKV-Server —— 基于 RWKV7 的高性能批量推理引擎,单卡 RTX 4090 峰值 11,081 tok/s,支持 FIM 代码补全。
从一个场景说起
程序员在编辑器中写代码时,光标往往不在文件末尾——它在函数体中间、在两行已有代码之间、在 import 语句和函数签名之后。此时代码补全面临一个根本问题:模型需要同时理解光标前的代码和光标后的代码,才能生成正确的填充内容。
传统的自回归语言模型只能从左到右生成。它们看到 prefix 后就开始逐 token 输出,对 suffix 一无所知。这在文件末尾续写时没问题,但在文件中间编辑时,丢失了半数上下文。
Fill In the Middle(FIM)正是为解决这个问题而生的技术。它的核心思想极其简洁:不改模型架构,只改训练数据的排列方式,就让因果语言模型同时利用前后文信息。
一、FIM 的原理
1.1 核心变换
FIM 的做法是将一篇文档在随机位置切分为三段——prefix、middle、suffix——然后把 middle 移到末尾,用特殊 token 拼接:
原始文档: [prefix] [middle] [suffix] FIM 变换后: [prefix] [suffix] [middle] ↑ ↑ ↑ <fim_prefix> <fim_suffix> <fim_middle>模型仍然从左到右自回归生成,但此时它已经"看到"了 suffix。当生成到<fim_middle>之后的内容时,模型同时拥有 prefix 和 suffix 的信息,可以生成连贯的中间填充。
推理时,只需将用户提供的 prefix 和 suffix 按相同格式拼接,模型就会在<fim_middle>之后输出填充内容:
输入: <fim_prefix>{prefix}<fim_suffix>{suffix}<fim_middle> 输出: {模型在此生成中间内容}1.2 SPM 与 PSM
原始论文定义了两种排列变体[1]:
PSM(Prefix-Suffix-Middle):
<pre>{prefix}</pre><suf>{suffix}</suf><mid>{middle}</mid>SPM(Suffix-Prefix-Middle):
<suf>{suffix}</suf><pre>{prefix}</pre><mid>{middle}</mid>区别在于 suffix 和 prefix 的先后顺序。SPM 把 suffix 放在 prefix 前面,使 suffix 离生成位置更近,模型更容易利用 suffix 信息。论文实验表明 SPM 在 FIM 任务上表现更好,但联合训练 PSM+SPM(各 50%)是最佳实践。
1.3 “FIM-for-free”——零代价获得新能力
FIM 最优雅的属性在于:在预训练阶段混合 FIM 数据不会损害模型原有的从左到右生成能力[1]。
论文作者训练了从 50M 到 6.9B 参数的 16 个模型进行对照实验。结果表明,以 50% 的 FIM 变换概率混合训练后,模型在 perplexity、PIQA、Winograd、DROP、HellaSwag、LAMBADA、HumanEval 等标准基准上的表现与纯自回归模型完全一致。FIM 能力是"免费"附赠的。
反过来,如果留到微调阶段才学 FIM,则需要大量额外算力才能达到同等效果。这意味着 FIM 应该在预训练阶段就引入。
二、主流实现
2.1 各模型的 FIM 格式
几乎所有主流代码模型都在预训练中采用了 FIM。它们的特殊 token 命名不同,但原理完全一致:
| 模型 | FIM Token 格式 | 训练策略 |
|---|---|---|
| OpenAI Codex | <|fim_prefix|>...<|fim_suffix|>...<|fim_middle|> | PSM 为主 |
| StarCoder (BigCode) | <fim_prefix>...<fim_suffix>...<fim_middle> | PSM+SPM 混合,50% FIM rate |
| DeepSeek-Coder | <|fim▁begin|>...<|fim▁hole|>...<|fim▁end|> | 三片段输入 |
| Qwen2.5-Coder | <|fim_prefix|>...<|fim_suffix|>...<|fim_middle|>+<|fim_pad|> | PSM+SPM 混合 |
| CodeLlama (Meta) | 类似 Codex 格式 | FIM 训练 |
| SantaCoder | <fim_prefix>...<fim_suffix>...<fim_middle> | PSM+SPM 混合 |
| RWKV (rwkv_lightning / RWKV-Server) | ✿prefix✿✿suffix✿{suffix}✿middle✿{prefix} | Unicode 字符标记,无需特殊 token |
2.2 推理示例
以 Qwen2.5-Coder 为例[5]:
fromtransformersimportAutoTokenizer,AutoModelForCausalLM tokenizer=AutoTokenizer.from_pretrained("qwen/qwen2.5-coder-7b")model=AutoModelForCausalLM.from_pretrained("qwen/qwen2.5-coder-7b")prefix="def hello_world():"suffix="\n print(result)"prompt=f"<|fim_prefix|>{prefix}<|fim_suffix|>{suffix}<|fim_middle|>"inputs=tokenizer(prompt,return_tensors="pt")outputs=model.generate(**inputs,max_new_tokens=100)以 DeepSeek API 为例[4]:
response=client.fim_completions.create(model="deepseek-fim",prefix="def factorial(n):\n if n == 0:\n return 1\n ____",suffix="\n print(factorial(5)) # 输出120")三、FIM vs 其他"双向"方案
FIM 并非让语言模型"看到两边"的唯一方案,但它是最简洁、最实用的一种。
3.1 方案对比
| 方案 | 代表模型 | 架构修改 | 训练开销 | 生成质量 | 工业采用 |
|---|---|---|---|---|---|
| FIM (Infilling) | Codex, StarCoder, DeepSeek-Coder | 无 | 零额外 | 高 | 广泛 |
| Permutation LM | XLNet[12] | 双流注意力 | 中等 | 中高 | XLNet |
| Span Corruption | T5, SpanBERT | Encoder / Enc-Dec | 中等 | 中 | T5 |
| Masked LM | BERT | Encoder-only | 中等 | 低(生成) | BERT 系列 |
3.2 为什么 FIM 胜出
Permutation Language Modeling(XLNet)对输入 token 序列做随机排列,通过双流自注意力让每个 token 看到所有位置。理论上更通用,但需要修改模型架构(双流注意力),计算开销更大,且在代码生成任务上没有明显优势。
Span Corruption(T5、SpanBERT)随机遮蔽文本中的连续片段,让模型预测被遮蔽的内容。通常用于 encoder 或 encoder-decoder 架构,可以腐蚀多个 span,但训练目标是 denoising 而非自回归,在开放式生成任务上表现不如因果模型。
FIM的优势在于:不改变模型架构、不增加推理开销、不损失原有能力、实现极其简单(只需数据变换)。它是一个"免费的午餐"。
四、适用场景
FIM 并非在所有场景下都优于传统的从左到右生成。选择取决于上下文的可用性:
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 代码续写(文件末尾) | L2R | suffix 为空,FIM 退化为 L2R |
| 代码中间补全 | FIM | 光标前后都有代码 |
| 函数体生成 | FIM | 有函数签名(prefix)和返回语句(suffix) |
| import 语句生成 | FIM | 后续代码作为 suffix 约束类型 |
| docstring 生成 | FIM | 函数体作为 suffix 提供类型信息 |
| 对话 / 问答 | L2R | 天然顺序生成 |
| 文本编辑 / 修改 | FIM | 替换段落时前后文都存在 |
实际应用中,代码编辑器(如 rwkv-code-completion[9])会根据光标位置自动切换:光标后有代码时走 FIM,光标在文件末尾时走普通续写。RWKV-Server 的 FIM 端点同样遵循这一策略——suffix 为空时自动退化为普通续写,无需额外适配。
五、RWKV 生态中的 FIM
5.1 rwkv_lightning 的实现
rwkv_lightning[8]使用 Unicode 字符✿(U+273F)作为 FIM 标记,而非传统的<fim_prefix>格式:
✿prefix✿✿suffix✿{suffix}✿middle✿{prefix}这种设计的好处是✿是普通 Unicode 字符,不需要在 tokenizer 中添加特殊 token,任何 RWKV 模型都可以直接使用。
rwkv_lightning 的 FIM 接口支持批量推理:prefix 和 suffix 为列表,一次请求处理多组不同的 FIM 任务,充分利用 RWKV 的批量推理能力。
5.2 RWKV-Server 的 FIM
RWKV-Server 在 rwkv_lightning 的基础上进一步简化了接口。POST /v1/tasks/fim接收单个 prefix + suffix,内部构造相同的 FIM prompt 后,复用现有的 Task 调度器和批量推理引擎:
curl-XPOST http://localhost:8000/v1/tasks/fim\-H"Content-Type: application/json"\-d'{ "prefix": "def hello_world():\n print(", "suffix": ")\n return True", "max_tokens": 50, "stream": false }'整个 FIM 功能只改了两个文件——schemas.py新增请求模型,routers.py新增端点——核心 prompt 构造只有一行。FIM 的实现完全复用了 RWKV-Server 的现有基础设施——Task 调度器、动态扩缩容、SSE 流式输出——零侵入底层。
5.3 RWKV 架构对 FIM 的天然适配
RWKV 的 O(1) 线性注意力和固定大小 state 使其天然适合高并发 FIM 场景:
- state 不随序列长度膨胀:长 prefix + suffix 不会像 Transformer 的 KV Cache 那样消耗大量显存
- 批量吞吐线性增长:batch_size 越大,GPU 利用率越高,单卡可以支撑数百并发 FIM 请求
- 状态复用:同一文件的多次编辑可以复用 prefix 部分的 state,避免重复 prefill
在 RTX 4090 上,RWKV-Server 使用 RWKV7 2.9B 模型可达到峰值 11,081 tok/s(256 并发),7.2B 模型峰值 6,373 tok/s。
六、关键超参数
原始论文的消融实验揭示了几个关键超参数[1]:
FIM Rate(变换概率):推荐 50%,即一半训练数据做 FIM 变换,一半保持原始顺序。低于 50% 会削弱 FIM 能力,高于 50% 会轻微影响 L2R 能力。
Span 选择策略:推荐在 context 级别(而非 document 级别)做 FIM,并使用均匀的 span 长度分布。对于代码,优先在行/段落边界分割效果更好。
SPM vs PSM 比例:联合训练 SPM+PSM(各 50%)是最佳实践,单一格式的效果略逊。
评估方式:论文特别指出,仅看 test loss 会误导——不同超参数下 FIM test loss 差异很小,但在采样基准(pass rate)上差异巨大。评估 FIM 能力必须用采样而非 perplexity。
Sources
- Bavarian et al., "Efficient Training of Language Models to Fill in the Middle", arXiv:2207.14255, 2022.https://arxiv.org/abs/2207.14255
- Li et al., SantaCoder: Training Open-Source Code LLMs with 1 Trillion Tokens, BigCode, 2023.https://blog.csdn.net/gitblog_02761/article/details/144662538
- Ray et al., StarCoder: May the Source Be with You!, BigCode, 2023.https://hub.baai.ac.cn/view/26590
- DeepSeek FIM 功能解析, CSDN, 2024.https://blog.csdn.net/qinzhenyan/article/details/154409461
- Qwen2.5-Coder 技术报告, CSDN, 2024.https://blog.csdn.net/jinselizhi/article/details/143759611
- Seed-Coder FIM+SPM 训练策略, 2025.https://www.dodgeindustrial.com.cn/dy/article/20250508796423665.shtml
- tiktoken FIM 特殊 token 定义, OpenAI.https://blog.csdn.net/gitblog_00536/article/details/151175018
- rwkv_lightning 项目 + Albatross 推理引擎, RWKV-Vibe.https://juejin.cn/post/7597641580104581174
- rwkv-code-completion VS Code 插件, xun082.https://juejin.cn/post/7597641580104581174
- RWKV7-G1c 系列模型发布, RWKV 社区, 2025.https://juejin.cn/post/7600888960106070052
- Yang et al., XLNet: Generalized Autoregressive Pretraining for Language Understanding, 2019.https://blog.csdn.net/DarrenXf/article/details/96449450
