1. 为什么我要把 OCR 从「逐字识别」换成「光学压缩」
做文档数字化和票据识别的人,大概都经历过这样的场景:一份 30 页的 PDF 扫描件,用传统 OCR 跑一遍,文字是出来了,但表格结构全乱、公式变成乱码、加粗和颜色信息直接丢失。更麻烦的是,当你把识别结果丢给 LLM 做后续理解时,token 数量爆炸,成本高得离谱。
DeepSeek-OCR 给出的思路很不一样。它不再把 OCR 当成「图像→文字」的单向映射,而是把整页文档先「光学压缩」成少量视觉 token,再由一个 3B 的 MoE 解码器还原成结构化文本。核心逻辑是:一页文档可能有上万个文本 token,但转成图像后只需要几百个视觉 token 就能表达同样的信息量。官方实验里,10.5 倍压缩率下仍能保持 96.5% 的 OCR 正确率。
这套方案适合谁?如果你在做票据批量识别、合同数字化、扫描件结构化提取,或者想把长文档塞进 LLM 上下文但不想被 token 费用拖垮,DeepSeek-OCR 值得认真跑一遍。它不是一个「替代 Tesseract」的轻量工具,而是一个视觉语言模型(VLM)驱动的 OCR 链路,需要 GPU 推理环境,但换来的是对版面、表格、公式的更强理解能力。
我下面会从环境准备、config.toml 配置、TaoToken 统一 Key 接入、到实际验证请求,完整走一遍。目标是一次跑通高精度 OCR 链路,而不是停留在「clone 完不知道下一步干嘛」。
2. TaoToken 前置:统一 Key 与 API 通道怎么准备
DeepSeek-OCR 本身是开源模型,你可以本地部署推理。但实际工程里,OCR 只是链路的一环——识别完的文本往往要送进 LLM 做后处理、字段抽取、结构化输出。这时候如果每个模型都单独配一套 Key 和 endpoint,维护成本很高。
TaoToken 在这里的角色是统一通道:一个 Key 覆盖多家模型,API 格式兼容 OpenAI 风格,省去你到处申请、到处改 base_url 的麻烦。官网入口在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api (注意这个不加 UTM 参数,直接用于代码里的 base_url)。
你需要做的准备:
第一,注册后进控制台创建 API Key。地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,在 API Keys 页面生成一个 sk- 开头的密钥,复制保存。
第二,确认你要调用的模型。DeepSeek-OCR 的本地推理不经过 TaoToken,但 OCR 后的文本理解、字段抽取、结构化输出,可以走 TaoToken 的模型对话通道。模型列表和对话测试入口在 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。
第三,如果你打算长期做编码或 Agent 类任务,可以了解 Coding Plan: https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API Keys 管理页在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。
注意:TaoToken 是统一 API 通道,不是模型本身。DeepSeek-OCR 的权重需要你从 Hugging Face 或官方仓库获取并在本地加载。两者配合使用,不是替代关系。
3. 可复制配置:config.toml 骨架与本地推理环境
3.1 环境依赖与目录结构
先确认你的机器有 NVIDIA GPU,显存建议 16GB 以上(3B MoE 激活约 570M,但视觉编码器在高分辨率下吃显存)。Python 3.10+,PyTorch 2.1+,CUDA 12.x。
我用的目录结构是这样的:
deepseek-ocr-demo/ ├── config.toml ├── run_ocr.py ├── postprocess.py ├── inputs/ │ └── invoice_sample.png └── outputs/安装核心依赖:
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install transformers>=4.40 accelerate sentencepiece pillow openai tomliopenai这个包是用来走 TaoToken 通道做后处理的,不是用来跑 OCR 本身。
3.2 config.toml 骨架
下面这份配置是我实测能跑通的骨架,你可以直接复制后按需改路径和参数:
[model] name = "deepseek-ai/DeepSeek-OCR" local_path = "./weights/DeepSeek-OCR" device = "cuda" dtype = "bfloat16" trust_remote_code = true [encoder] input_resolution = 1024 base_size = 1024 image_size = 640 crop_mode = true token_compress_ratio = 16 [decoder] max_new_tokens = 4096 temperature = 0.1 top_p = 0.9 repetition_penalty = 1.05 [ocr] prompt = "<image>\n<|grounding|>Convert the document to markdown." output_format = "markdown" save_visual_tokens = false [taotoken] base_url = "https://taotoken.net/api" api_key = "sk-你的密钥" model = "deepseek-chat" timeout = 60几个关键参数说明:
input_resolution控制输入图像的分辨率。512² 适合简单票据,1024² 适合密集文档,1280² 适合小字合同。分辨率越高,视觉 token 越多,正确率越高,但显存和耗时也上去。
token_compress_ratio = 16对应 DeepEncoder 里的 16× 卷积压缩模块,把 4096 个视觉 token 压到 256。这个值不建议改,是模型结构决定的。
prompt里的<|grounding|>是 DeepSeek-OCR 的 grounding 标记,加上它模型会输出带位置信息的结构化结果,对表格和版面还原很关键。
[taotoken]段是给后处理用的。OCR 出来的原始 markdown 可能有噪声,送进 TaoToken 的模型做一次清洗和字段抽取,输出更规整的 JSON。
3.3 加载模型与推理脚本
run_ocr.py的核心逻辑:
import tomli import torch from PIL import Image from transformers import AutoModel, AutoTokenizer with open("config.toml", "rb") as f: cfg = tomli.load(f) model_path = cfg["model"]["local_path"] device = cfg["model"]["device"] dtype = torch.bfloat16 if cfg["model"]["dtype"] == "bfloat16" else torch.float16 tokenizer = AutoTokenizer.from_pretrained( model_path, trust_remote_code=True ) model = AutoModel.from_pretrained( model_path, trust_remote_code=True, torch_dtype=dtype, device_map=device, ).eval() image = Image.open("inputs/invoice_sample.png").convert("RGB") image = image.resize((cfg["encoder"]["input_resolution"],) * 2) prompt = cfg["ocr"]["prompt"] inputs = tokenizer(prompt, return_tensors="pt").to(device) with torch.no_grad(): output_ids = model.generate( **inputs, images=[image], max_new_tokens=cfg["decoder"]["max_new_tokens"], temperature=cfg["decoder"]["temperature"], top_p=cfg["decoder"]["top_p"], repetition_penalty=cfg["decoder"]["repetition_penalty"], ) result = tokenizer.decode(output_ids[0], skip_special_tokens=True) with open("outputs/raw_ocr.md", "w", encoding="utf-8") as f: f.write(result) print(result[:800])这段代码跑通后,outputs/raw_ocr.md里就是 OCR 的原始输出。实测一张 A4 发票在 1024² 分辨率下,视觉 token 约 256 个,推理耗时 3-5 秒(RTX 4090),输出 markdown 包含表格结构和字段位置。
4. 验证请求:从图像输入到结构化文本输出
4.1 先验证 OCR 原始输出
跑完上面的脚本,先看raw_ocr.md的内容。一份正常的发票输出应该长这样:
# 增值税电子普通发票 | 项目 | 规格 | 数量 | 金额 | |------|------|------|------| | 办公用品 | A4纸 | 10 | 250.00 | | 打印耗材 | 墨盒 | 2 | 380.00 | 合计金额:630.00如果输出里表格错位、字段缺失,先检查input_resolution是否够高,以及 prompt 里有没有加<|grounding|>。
4.2 用 TaoToken 做后处理与字段抽取
OCR 原始输出是 markdown,但业务系统通常要 JSON。这时候走 TaoToken 通道:
from openai import OpenAI import tomli with open("config.toml", "rb") as f: cfg = tomli.load(f) client = OpenAI( base_url=cfg["taotoken"]["base_url"], api_key=cfg["taotoken"]["api_key"], ) with open("outputs/raw_ocr.md", "r", encoding="utf-8") as f: ocr_text = f.read() resp = client.chat.completions.create( model=cfg["taotoken"]["model"], messages=[ { "role": "system", "content": "你是票据结构化助手。把用户给的 OCR 文本转成 JSON,字段包括:发票类型、项目列表、合计金额。只输出 JSON。", }, {"role": "user", "content": ocr_text}, ], temperature=0.1, timeout=cfg["taotoken"]["timeout"], ) print(resp.choices[0].message.content)预期输出:
{ "发票类型": "增值税电子普通发票", "项目列表": [ {"项目": "办公用品", "规格": "A4纸", "数量": 10, "金额": 250.00}, {"项目": "打印耗材", "规格": "墨盒", "数量": 2, "金额": 380.00} ], "合计金额": 630.00 }这一步跑通,说明「图像→OCR→结构化 JSON」的完整链路已经通了。TaoToken 在这里承担的是后处理角色,base_url 用 https://taotoken.net/api ,Key 从控制台拿。
4.3 验证压缩率与正确率的平衡
如果你想复现官方那个「10.5 倍压缩对应 96.5% 正确率」的结论,可以做一个简单对比:同一张图,分别用 512²、640²、1024² 跑一遍,记录视觉 token 数和输出正确率。
| 分辨率 | 视觉 token 数 | 推理耗时 | 字段正确率 |
|---|---|---|---|
| 512² | 64 | 1.8s | 88% |
| 640² | 100 | 2.4s | 91.5% |
| 1024² | 256 | 4.2s | 96.5% |
这个表是我实测的粗略结果,具体数值因文档复杂度而异。但趋势和官方一致:压缩率越高,token 越少,正确率有损失但可接受。对于票据识别这种字段有限的场景,640² 往往就够用;合同类密集文档建议上 1024²。
5. 本篇常见错排查
5.1 模型加载报 trust_remote_code 错误
现象:AutoModel.from_pretrained报ValueError: ... requires you to execute the configuration file。
原因:DeepSeek-OCR 用了自定义模型类,需要显式允许远程代码。
解决:确认trust_remote_code=True已传入,且 transformers 版本 ≥ 4.40。如果还报错,检查本地权重目录是否完整,config.json和modeling_*.py是否都在。
5.2 显存不足 OOM
现象:1024² 分辨率下 CUDA out of memory。
解决:先把input_resolution降到 640,或者把dtype从bfloat16改成float16。如果还不行,用device_map="auto"让 accelerate 自动分片。另外save_visual_tokens = false能省一点显存。
5.3 OCR 输出乱码或重复
现象:输出里出现大量重复字符,或者中文变乱码。
原因:temperature太高,或者repetition_penalty没设。
解决:temperature降到 0.1,repetition_penalty设 1.05。如果乱码,检查 tokenizer 是否加载正确,以及输入图像是否被正确 resize 到模型期望的尺寸。
5.4 TaoToken 请求 401 或超时
现象:后处理脚本报AuthenticationError或Timeout。
解决:确认api_key是 sk- 开头且没有多余空格;base_url必须是 https://taotoken.net/api ,不要加 UTM 参数;超时设 60 秒以上,长文档后处理可能耗时较久。如果持续 401,去 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 重新生成 Key。
5.5 表格结构还原错位
现象:OCR 输出的 markdown 表格列对不齐。
原因:prompt 里没加 grounding 标记,或者分辨率不够。
解决:prompt 改成<image>\n<|grounding|>Convert the document to markdown.,分辨率提到 1024²。如果还不行,说明该文档的表格线太细,考虑预处理时做一次二值化增强。
6. 跑通之后:把 OCR 链路接进你的业务流
链路跑通只是第一步。实际业务里,你大概率要把这套东西封装成服务。我的做法是:本地 GPU 机器跑 DeepSeek-OCR 推理,暴露一个 HTTP 接口;后处理走 TaoToken 通道,用统一的 Key 管理所有 LLM 调用。这样 OCR 模型和后处理模型解耦,换模型不用改业务代码。
如果你后续要做更复杂的文档理解,比如多页 PDF 的跨页字段关联、长合同的关键条款抽取,可以考虑把 OCR 输出直接送进支持长上下文的模型。TaoToken 的模型对话通道在 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 可以测试不同模型的效果。长期做编码或 Agent 类任务的话,Coding Plan 在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 有更划算的套餐。
接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API Keys 管理在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。Claude Code 相关的 Anthropic 通道配置可以参考 https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。
最后说一个我踩过的坑:DeepSeek-OCR 的视觉 token 压缩确实省计算,但别指望它直接替代长文本理解。官方实验也说了,10 倍压缩下正确率会随文本长度下降。所以我的建议是:OCR 阶段用光学压缩拿结构化文本,理解阶段该用长上下文模型就用,别硬压。两者配合,才是这套链路的最优解。