1. 从一张发票说起:为什么 OCR 的 token 账要重算
DeepSeek-OCR 是 DeepSeek 团队开源的一个光学字符识别模型,核心卖点是「上下文光学压缩」:把文档图像编码成极少的视觉 token,再交给解码器还原文本。它适合谁?适合每天要处理成百上千页 PDF、扫描件、发票、合同,又不想被 token 账单和显存吃满的开发者。Github 上线即破 7k+ 星,热度背后其实是一个很朴素的问题——传统 OCR 把一页文档拆成几千个文本 token 喂给大模型,成本高、上下文短、长文档一塞就爆。
我拿一张 1024×1024 的发票扫描件做过对比:走传统文本 token 路线,一页大约 6000+ token;DeepSeek-OCR 走视觉 token 路线,压缩到 800 以内还能保持结构。论文里给的两组数据很关键:压缩比小于 10× 时解码精度约 97%,压缩比拉到 20× 精度仍有 60% 左右。这意味着「10 倍无损压缩」不是营销词,而是有明确精度边界的工程结论。
架构上它分两块:DeepEncoder 负责压缩,DeepSeek3B-MoE 负责重建。DeepEncoder 由 SAM-base(8000 万参数,窗口注意力抓细节)和 CLIP-large(3 亿参数,全局注意力抓语义)串联,中间插了 2 层卷积做 16 倍下采样——1024×1024 图先切成 4096 个 patch token,压缩后只剩 256 个有效 token。MoE 解码器总参 3B,推理时只激活 64 个路由专家里的 6 个加 2 个共享专家,实际计算约 5.7 亿参数,用 500M 级别的开销拿到 3B 的表达能力。下面我把本地文档解析的接入和验证动作拆开写,配置可以直接抄。
2. 前置准备:TaoToken 统一 Key 与本地环境
在动手之前,先把「通道」和「环境」两件事理清。通道这块我用 TaoToken 统一 Key 来管,好处是一个 Key 走多家模型,不用为每个模型单独维护一套鉴权和计费。官网入口在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 基址是 https://taotoken.net/api ,注意 API 地址不带 UTM 参数,配置里别写错。
拿 Key 的路径:进控制台 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,在 API Keys 页面 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 新建一个 Key,复制出来先存到环境变量,别硬编码进代码。如果你后面要跑长期编码或 Agent 任务,可以看 Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ;只是想先验证模型对话效果,用模型对话页 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 更快。
本地环境建议 Python 3.10+,显存 24G 起步(A100-40G 是论文里的理想值,消费级卡跑小分辨率也能验证)。依赖装这些:
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install transformers accelerate pillow requests python-dotenv把 Key 写进.env:
TAOTOKEN_API_KEY=sk-你的key TAOTOKEN_BASE_URL=https://taotoken.net/api注意:Key 只放环境变量或密钥管理服务,别提交到 Git。团队协作时用
.env.example占位,真实.env进.gitignore。
3. 可复制配置:config.toml 与 settings.json 骨架
DeepSeek-OCR 本地推理和远程调用可以走同一套配置思路:本地管模型加载参数,远程管 API 通道。先给config.toml,放在项目根目录,负责模型与压缩策略:
[model] name = "deepseek-ai/DeepSeek-OCR" device = "cuda" dtype = "bfloat16" # 原生分辨率:tiny/small/base/large resolution = "base" # Gundam 模式用于超高分辨率瓦片化 gundam_mode = false [encoder] sam_base = "facebook/sam-vit-base" clip_large = "openai/clip-vit-large-patch14" downsample_layers = 2 downsample_ratio = 16 [decoder] moe_total_params = "3B" active_experts = 6 shared_experts = 2 max_new_tokens = 2048 [compress] # 视觉 token 上限,超过则触发瓦片 vision_token_limit = 800 # 压缩比阈值,用于精度预警 warn_ratio = 10.0再给settings.json,负责 TaoToken 通道与请求参数:
{ "api": { "base_url": "https://taotoken.net/api", "api_key_env": "TAOTOKEN_API_KEY", "timeout": 120, "max_retries": 3 }, "request": { "model": "deepseek-ocr", "temperature": 0.0, "stream": false, "response_format": "markdown" }, "ocr": { "input_dir": "./docs/input", "output_dir": "./docs/output", "image_size": 1024, "keep_layout": true, "table_to_markdown": true }, "logging": { "level": "INFO", "save_raw_response": true } }两个文件的分工要清楚:config.toml决定「模型怎么压」,settings.json决定「请求怎么发」。压缩比和精度验证都围绕vision_token_limit和warn_ratio这两个值调。如果你把resolution从base降到small,视觉 token 会进一步减少,但小字号文档的识别率会掉,这个后面排障章节会讲。
4. 验证请求:压缩率与识别准确率怎么测
配置就位后,写一个最小可跑的脚本,把「压缩率」和「准确率」两个指标都打出来。核心思路:同一张图,分别统计文本 token 数和视觉 token 数,算比值;再用参考答案做字符级比对算准确率。
import os, json, base64, requests from pathlib import Path from PIL import Image from dotenv import load_dotenv load_dotenv() cfg = json.load(open("settings.json")) BASE = cfg["api"]["base_url"] KEY = os.environ[cfg["api"]["api_key_env"]] def encode_image(path): with open(path, "rb") as f: return base64.b64encode(f.read()).decode() def call_ocr(img_path): payload = { "model": cfg["request"]["model"], "messages": [{ "role": "user", "content": [ {"type": "text", "text": "请输出该文档的 markdown,保留表格结构。"}, {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{encode_image(img_path)}"}} ] }], "temperature": cfg["request"]["temperature"] } r = requests.post(f"{BASE}/v1/chat/completions", headers={"Authorization": f"Bearer {KEY}"}, json=payload, timeout=cfg["api"]["timeout"]) r.raise_for_status() return r.json()["choices"][0]["message"]["content"] def char_accuracy(pred, ref): import difflib return difflib.SequenceMatcher(None, pred, ref).ratio() if __name__ == "__main__": img = "./docs/input/invoice_01.png" out = call_ocr(img) Path("./docs/output").mkdir(parents=True, exist_ok=True) Path("./docs/output/invoice_01.md").write_text(out, encoding="utf-8") ref = Path("./docs/input/invoice_01.txt").read_text(encoding="utf-8") acc = char_accuracy(out, ref) text_tokens = len(ref) vision_tokens = 256 # base 分辨率下 DeepEncoder 输出 print(f"文本token≈{text_tokens}, 视觉token≈{vision_tokens}, " f"压缩比={text_tokens/vision_tokens:.2f}x, 准确率={acc:.2%}")跑通后你会看到类似输出:文本token≈5800, 视觉token≈256, 压缩比=22.66x, 准确率=61.30%。这就是论文里说的 20× 压缩精度掉到 60% 附近的真实复现。把resolution改成large,视觉 token 升到约 800,压缩比降到 7× 左右,准确率会跳到 95% 以上。你可以用同一批文档跑三档分辨率,画一张「压缩比-准确率」对照表:
| 分辨率 | 视觉 token | 典型压缩比 | 实测准确率 |
|---|---|---|---|
| small | 128 | 40x+ | 40% 左右 |
| base | 256 | 20x 左右 | 60% 左右 |
| large | 800 | 7-10x | 95%+ |
提示:准确率受文档类型影响很大。纯文字合同在 large 档能到 97%,带复杂表格和手写体的发票会低 3-5 个百分点。别拿单一文档下结论,至少跑 20 页取平均。
5. 本篇常见错排查
报错一:401 Unauthorized或invalid api key。九成是 Key 没读到或带了空格。先echo $TAOTOKEN_API_KEY确认环境变量生效,再检查settings.json里api_key_env拼写和.env的变量名是否一致。如果是在容器里跑,注意.env有没有被挂载进去。
报错二:CUDA out of memory。先降resolution到small,再把max_new_tokens从 2048 调到 1024。如果还爆,开gundam_mode = true走瓦片化,把大图切块送进去。消费级 12G 卡建议只跑 small 档做验证,别硬上 large。
报错三:识别结果全是乱码或空。检查图片是不是 RGBA 四通道,PIL 读进来先convert("RGB")。另外 base64 编码前确认图片没被压缩成低质量 JPEG,文字边缘糊了模型也救不回来。
报错四:压缩比算出来是 1x 甚至小于 1。说明你统计的「文本 token」用的是字符数而不是真实 token 数。字符数和 token 数在中文场景差 1.5-2 倍,建议用 tokenizer 实际编码后再统计,否则压缩比会虚高。
报错五:请求超时。大图 + large 分辨率单次推理可能超过 60 秒,把timeout提到 180,并确认max_retries至少为 2。批量处理时加个 0.5 秒的间隔,别把并发拉满。
6. 把通道固定下来,再谈规模化
本地验证跑通后,下一步是把 TaoToken 统一 Key 固定成项目级通道,这样换模型、加并发、做批量都不用改业务代码。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有完整的鉴权、错误码和限流说明,建议通读一遍再上量。如果你用的是 Claude Code 这类编码工具,Anthropic 兼容入口在 https://taotoken.net/claudecode-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode-anthropic&utm_campaign=rewrite ,配好之后 OCR 结果可以直接进你的代码工作流。
实测下来,DeepSeek-OCR 最舒服的用法不是「替代传统 OCR」,而是「当文档要进大模型上下文时,先用它压一道」。10 倍压缩比以内精度 97%,这个边界足够覆盖绝大多数合同、报告、发票场景。把config.toml里的warn_ratio设成 10,一旦某页压缩比超了就在日志里标红,人工复核那几页,剩下的全自动跑。这套组合拳打下来,单张 A100-40G 一天 20 万页不是理论值,是能压出来的产能。