Donut 全称 Document Understanding Transformer,出自 NAVER CLOVA,论文《OCR-free Document Understanding Transformer》发表在 ECCV 2022(arXiv 2111.15664),代码在 GitHub 的 clovaai/donut,MIT 许可证。它的核心主张写在名字里:不要 OCR。传统文档抽取是「OCR 引擎识别文字 → 版面分析 → 规则或模型拼字段」的三段式流水线,每一段的误差都会往下游传,换个版式就要重调一遍。Donut 把这一整条流水线塞进一个 Swin Transformer 编码器加四层 mBART 解码器的模型里,输入一张图片,直接自回归吐出一段带标签的序列,再用 processor.token2json() 转成你要的 JSON。整个模型 fp32 权重 809MB,对照今天动辄 3B 起步的 OCR 大模型,这是一个数量级的差距。
但要把话说完整:Donut 的上游已经不再更新了。donut-python 最后一个版本 1.0.9 停在 2022 年 11 月,官方 Colab demo 最后一次刷新是 2023 年 6 月,仓库 issue 区里「Provide a working list of deps」「Colab Demo CORD is Broken」这类环境问题一路挂到 2026 年 2 月还没关。naver-clova-ix 这个组织在 Hugging Face 上一共 9 个模型,全部是 Donut 家族,2022 年之后没有再发新东西。今天你要做的如果是「任意版式文档转 Markdown 全文」,DeepSeek-OCR(3B,MIT,2025 年 10 月)、GLM-OCR、GOT-OCR2.0 这些新一代 OCR-free VLM 已经把 Donut 甩开了,别硬用。但如果你的场景是「固定版式 + 固定 JSON schema」—— 电商回单、加油发票、体检报告、报关单、快递面单 —— Donut 依然是最优解:200M 参数意味着它的显存占用和推理成本比 3B 模型低一个量级,输出严格锁死在你微调时定义的字段上,不会像通用 VLM 那样偶尔发挥自由意志给你多编一个字段出来,而且 MIT 许可证让你可以随便商用、随便闭源分发。
显存上 Donut 几乎不构成门槛,真正的成本在微调。donut-base 的预训练用了 64 张 A100 跑 2.5 天,donut-proto 用了 8 张 V100 跑 5 天 —— 但那是从零预训练,你不需要重复。基于官方 checkpoint 做下游微调,社区最常被引用的实测是在一张 16GB 的 V100 上完成的:分辨率降到 720×960、fp16、batch size 2、624 张标注图跑 3 个 epoch。NexGPU 上 Tesla V100 32GB 是 $0.188/卡·时,RTX 3090 24GB 是 $0.193/卡·时,两张卡都比那台机器宽裕。按秒计费、没有起租时长、没有配额申请,2000+ 预置镜像里的 PyTorch 镜像开机即用,51 个国家和地区的 1,175 个已验证节点让你挑一个离数据近的位置落地。
01 —
Donut 官方 checkpoint 全表
naver-clova-ix 下全部 6 个可用权重,参数量都在 200M 上下,差别在输入分辨率和解码器输出上限
| 版本 | 参数量 | 显存 | 上下文 | 说明 |
|---|---|---|---|---|
| naver-clova-ix/donut-base | 约 200M(Swin 编码器 depths [2,2,14,2] + 4 层 mBART,d_model 1024,词表 57525) | fp32 权重 809MB/fp16 约 400MB;2560×1920 下 fp16 推理峰值约 3~5GB | 解码器 1536 token | 只做过 SynthDoG 合成数据预训练的裸底座,直接推理不会输出任何有意义的结构 —— 它是给你微调用的起点,不是成品。做全新语种或全新版式时从这里出发。 |
| naver-clova-ix/donut-base-finetuned-cord-v2 | 约 200M(词表 57580,权重 806MB) | fp16 推理约 1.5~2GB;1280×960 + fp16 + batch 4 微调约 10GB | 解码器 768 token | 收据结构化,输入 1280×960(高×宽),任务提示词 <s_cord-v2>。开箱即用能直接把小票读成 menu/sub_total/total 三段 JSON,也是社区微调自有票据 schema 时最常用的起点权重。 |
| naver-clova-ix/donut-base-finetuned-docvqa | 约 200M(词表 57532) | fp16 推理约 3~5GB(2560×1920 的 Swin 激活是显存大头,不是权重) | 解码器仅 128 token | 文档问答,提示词格式 <s_docvqa><s_question>你的问题</s_question><s_answer>。注意 128 token 的输出上限意味着它只能回答短答案,想让它总结整页会被硬截断。 |
| naver-clova-ix/donut-base-finetuned-rvlcdip | 约 200M(词表 57544) | fp16 推理约 3~5GB;解码只出一个类别标签,KV cache 几乎为零 | 解码器仅 8 token | RVL-CDIP 十六类文档分类(发票、简历、广告、手写件……),提示词 <s_rvlcdip>,输出形如 {'class': 'advertisement'}。想拿它当抽取模型用一定会失败 —— 8 token 装不下任何 JSON。 |
| naver-clova-ix/donut-base-finetuned-zhtrainticket | 约 200M(词表 57542) | fp16 推理约 1.5~2GB(960×1280 横向,像素量只有 base 的四分之一) | 解码器 256 token | 唯一一个官方中文 checkpoint,中国火车票信息抽取。分辨率是 960×1280 的横向配置,正好贴合车票的形状。做中文单据时它比 cord-v2 更接近你的分布,值得当微调起点试一次。 |
| naver-clova-ix/donut-proto | 约 200M(Swin depths [2,2,18,2],标准 Swin-B 深度,词表 57524) | fp32 权重约 810MB;2048×1536 下 fp16 推理约 3~4GB | 解码器 768 token | 早期原型底座,输入 2048×1536,用 8 张 V100 训了 5 天。它的第三阶段有 18 个 block(donut-base 缩到了 14),结构和后续版本不完全兼容,除非要复现论文早期结果,一般直接用 donut-base。 |
02 —
NexGPU 选卡建议
Donut 只有 200M 参数,显存从来不是瓶颈 —— 该关心的是分辨率带来的激活开销和自回归解码的速度
跑通官方 checkpoint,验证 Donut 能不能读懂你的单据
Tesla V100 32GB$0.188/卡·时
全站最便宜的一张卡,Volta 的 fp16 张量核跑 Donut 完全够用(社区那份广被引用的微调实测就是在 V100 上做的),32GB 显存装得下 donut-base 原生 2560×1920 分辨率,20 分钟就能得出「这条路走不走得通」的结论。
用几百到几千张自有标注单据微调专属 schema(1280×960、fp16、batch 4~8)
RTX 3090 24GB$0.193/卡·时
AdamW 的优化器状态对 200M 模型只要约 3.2GB,加上梯度和 1280×960 的激活,24GB 剩下大把余量把 batch 开到 8;每小时不到两毛美金,一整轮微调的算力成本还不到一杯咖啡。
保留 2560×1920 原生分辨率微调,或者想把训练时间压掉一半
RTX 4090 24GB$0.540/卡·时
原生分辨率下 Swin 第一阶段有 30 万个 token,激活量是 1280×960 的四倍,需要开梯度检查点才塞得进 24GB;Ada 架构的 fp16/bf16 吞吐大约是 3090 的两倍,按秒计费下更快跑完往往总价更低。
用 SynthDoG 合成数据为新语种/新领域重做预训练底座
A100 SXM4 80GB$1.088/卡·时
官方 donut-base 是 64 张 A100 跑 2.5 天出来的,重做底座是唯一真正需要规模的场景;NexGPU 单节点最多 14 张卡、最大整机显存 2,152GB,配合 synthdog-zh 的 6.37 万张中文合成文档,一次 8 卡 NVLink 任务就能起步。
03 —
四步在 NexGPU 上跑起来
别碰 donut-python 这个包 —— 它的依赖已经锁死在 2022 年的 timm 和 pytorch-lightning 上,直接用 Transformers 的 VisionEncoderDecoder 接口
- 01
开一张卡,装干净的 Transformers 环境
在控制台选 RTX 3090 24GB 和 PyTorch 预置镜像,SSH 或 Jupyter 进去。仓库里 issue 区那些「装不上」「Colab 挂了」的帖子几乎全是 donut-python 的锅,Transformers 官方已经把 Donut 完整实现进去了,不需要原仓库的任何代码。注意 Transformers v5 起 image-to-text 这个 pipeline task 已经废弃,改用 image-text-to-text,模型类用 AutoModelForImageTextToText 或 VisionEncoderDecoderModel。
pip install -U "transformers>=5.0" torch torchvision pillow sentencepiece protobuf datasets - 02
先用 cord-v2 跑一张真票据,确认卡和分辨率没问题
加载 donut-base-finetuned-cord-v2,用 fp16 推到 GPU 上,喂一张你自己的收据照片,任务提示词是 <s_cord-v2>。generate 时务必带上 bad_words_ids=[[tokenizer.unk_token_id]] 和 use_cache=True —— 前者防止解码器在遇到词表外字符时死循环吐 <unk>,后者是速度的关键。输出序列用 processor.token2json() 转 JSON。这一步跑通,你就知道 Donut 对你这类版式的原始识别能力在什么水位。
model = VisionEncoderDecoderModel.from_pretrained("naver-clova-ix/donut-base-finetuned-cord-v2", dtype=torch.float16).to("cuda") - 03
换成你自己的字段体系:加任务 token、扩词表、放开输出长度
这是 Donut 微调最容易翻车的一步,仓库 issue #332 那种「测试集 TED 和 F1 双双为 0」基本都出在这里。给 tokenizer 加上你的任务起始 token 和所有字段 token,然后必须调用 decoder.resize_token_embeddings() 让嵌入矩阵跟着变大,并把 config.decoder_start_token_id 指到新 token 上 —— 三步少一步,模型就会从一个它从没见过的 id 开始解码,训练看着在收敛,评测直接归零。同时检查 max_position_embeddings:cord-v2 只有 768,rvlcdip 只有 8,你的 JSON 如果比这长必须先扩。
processor.tokenizer.add_special_tokens({"additional_special_tokens": ["<s_myreceipt>"]}); model.decoder.resize_token_embeddings(len(processor.tokenizer)); model.config.decoder_start_token_id = processor.tokenizer.convert_tokens_to_ids("<s_myreceipt>") - 04
降分辨率、开 fp16,开始微调
分辨率是显存和精度的唯一旋钮:改 image_processor.size 的同时也要改 encoder.image_size,并且让 Swin 的位置偏置跟着插值(from_pretrained 时带 ignore_mismatched_sizes=True)。宽高都建议保持能被 window_size 10 整除。社区实测把 2560×1920 降到 720×960 后,16GB 的 V100 上 batch 2 就能跑;在 24GB 的 3090 上你可以保留 1280×960 并把 batch 开到 8。训练完直接 save_pretrained,800MB 的权重拉回本地部署,出网只按 $0.0081/GB 中位价收费。
processor.image_processor.size = {"height": 1280, "width": 960}; model.config.encoder.image_size = (1280, 960)
微调一个专属 Donut 要花多少钱
以「600 张自有单据,微调出一个只认你家版式的结构化模型」为例,全部按 NexGPU 列表价算。微调用 RTX 3090 24GB,$0.193/卡·时,1280×960 加 fp16 加 batch 4,留 3 小时跑完 epoch 和中途评估:0.193 × 3 = $0.579。跑完再开 1 小时对全量测试集做推理和 TED 打分:0.193 × 1 = $0.193。数据集、HuggingFace 缓存和几个 checkpoint 加起来约 20GB,存储按 $0.414/GB·月 的中位价占用 3 天:20 × 0.414 × 3 ÷ 30 = $0.828。最后把 800MB 成品权重拉回本地,出网按 $0.0081/GB 中位价:0.8 × 0.0081 ≈ $0.01。合计约 $1.61 —— 一顿早饭的钱,换一个跑在你自己机器上、不给任何第三方看一眼单据内容的抽取模型。这里要提醒的是计费规则:算力按秒计,训练脚本一停就不再计费;但存储要等你销毁实例才停,上面那 $0.828 是这笔账里最容易被忘掉的一项,跑完记得把卷删干净。如果你只是想先验证 donut-base-finetuned-cord-v2 读不读得懂你的收据,Tesla V100 32GB $0.188/卡·时,二十分钟足够:0.188 × ⅓ ≈ $0.06。
04 —
