读懂PaddleOCR-VL-1.6-5bit的1000个LOC位置token:OCR模型如何实现文字定位
读懂PaddleOCR-VL-1.6-5bit的1000个LOC位置token:OCR模型如何实现文字定位
PaddleOCR-VL-1.6-5bit 是一个面向 Apple Silicon 的 5-bit 量化 OCR 视觉语言模型,它通过一套名为 <|LOC_0|> 到 <|LOC_1000|> 的 1001 个位置 token,把"文字在图片中的位置"变成可以逐词生成的数字串,从而用文本生成的方式实现文字定位。下面带你彻底读懂这套机制。
LOC token 长什么样:藏在词表里的坐标数字
打开词表扩展文件 added_tokens.json,你会发现一个非常有规律的 token 家族:
| Token 名称 | 词表 ID | 作用 |
|---|---|---|
<|LOC_0|> |
100297 | 坐标值 0 |
<|LOC_1|> |
100298 | 坐标值 1 |
| … | … | … |
<|LOC_1000|> |
101297 | 坐标值 1000 |
<|LOC_BEGIN|> |
101298 | 一个位置描述的起点 |
<|LOC_END|> |
101299 | 一个位置描述的终点 |
<|LOC_SEP|> |
101300 | 多个坐标值之间的分隔符 |
也就是说,从 LOC_0 到 LOC_1000 一共 1001 个 token,词表 ID 连续地占据 100297 ~ 101297 这一段,正好排在图片占位符 <|IMAGE_PLACEHOLDER|>(ID 100295)之后。词表总规模 vocab_size 为 103424,可在 config.json 中确认。
💡 这种设计的精妙之处在于:模型输出一个坐标,就像输出一个"词"一样自然,不需要解析 JSON 字符串或浮点数。
为什么要用整数 token 而不是直接写数字?
普通大模型输出 "x=123.45" 需要逐个字符生成 1、2、3、.……既慢又容易出错。PaddleOCR-VL 的做法是把归一化坐标(0~1000 的整数)整体离散化成单个 token:
- 一步出值:一个 token 就代表一个完整的坐标分量,生成 4 个坐标只需 4 步解码;
- 格式零风险:不可能生成 "12.3" 这种小数或漏掉负号,输出天然是合法整数;
- 精度足够:把图片坐标轴 0~1000 等分,每个刻度对应约 0.1% 图像宽度,定位精度对文档文字框绰绰有余;
- 训练更稳定:坐标预测和文字转录共用同一套自回归生成框架,一份损失函数同时学"内容"和"位置"。
配合 <|LOC_BEGIN|> / <|LOC_END|> 作为位置段的起止边界、<|LOC_SEP|> 分隔 x、y 等分量,模型就能输出一段自解释的结构化序列,例如(示意):
<|LOC_BEGIN|><|LOC_120|><|LOC_SEP|><|LOC_85|><|LOC_SEP|><|LOC_342|><|LOC_SEP|><|LOC_110|><|LOC_END|>
从像素到坐标:视觉塔如何为定位铺路
定位能力的第一块基石是视觉编码器。在 config.json 的 vision_config 中可以看到关键参数:
| 参数 | 取值 | 含义 |
|---|---|---|
image_size |
384 | 输入图像统一缩放到 384×384 |
patch_size |
14 | 图像被切成 14×14 像素的小块 |
spatial_merge_size |
2 | 相邻 2×2 个 patch 合并为一个 token |
num_hidden_layers |
27 | 视觉塔共 27 层 Transformer |
384 ÷ 14 ≈ 27 个 patch,再经 2×2 合并后,一张图在语言模型侧大约对应 14×14 = 196 个视觉 token。图片在输入序列中的位置由 image_token_id(100295,即 <|IMAGE_PLACEHOLDER|>)标记,而 <|IMAGE_START|> / <|IMAGE_END|> 包裹图像区段,这些定义可以在 chat_template.jinja 和 config.json 中对照查看。
语言模型通过注意力机制"看"向这些视觉 token 时,能感知到图像的空间结构;当它决定输出某个文字框的位置时,就切换到生成 LOC 系列 token 的模式,把 2D 坐标"翻译"成 4 个离散的整数 token。
完整管线:一次文字定位是如何发生的
把前面的零件串起来,PaddleOCR-VL-1.6-5bit 处理一张带文字图片的完整流程是:
- 图像编码:图片缩放到 384×384,切成 14×14 的 patch,27 层视觉塔提取特征,相邻 2×2 patch 合并后得到一串视觉 token(预处理逻辑见 image_processing_paddleocr_vl.py);
- 序列拼接:模板把
<|IMAGE_START|><|IMAGE_PLACEHOLDER|><|IMAGE_END|>与你的提示词拼成一条输入序列,占位符按视觉 token 数量展开(见 processing_paddleocr_vl.py); - 内容识别:18 层语言模型(
num_hidden_layers: 18)先读出文字内容; - 位置输出:模型在需要标注位置时,依次生成
<|LOC_BEGIN|>、4 个LOC_N坐标 token(用<|LOC_SEP|>分隔)、<|LOC_END|>; - 结果还原:解码端把这串 token 映射回 0~1000 整数,按比例放大到原图像素坐标,得到标准文字框 (x1, y1, x2, y2)。
整个定位过程不需要额外的检测头或 bbox 回归网络——位置就是文本,文本就是位置。这也是"VL(Vision-Language)"范式做 OCR 的标志性设计。
5-bit 量化版会损失定位精度吗?
本项目 mlx-community/PaddleOCR-VL-1.6-5bit 是对原版 958M 参数模型做 5-bit(group_size 64)MLX 量化,磁盘体积约 0.75 GB。量化对坐标 token 的"威胁"比想象中低:
- 权重保真度高:README 实测 5-bit 权重的 SNR 为 24.2 dB、余弦相似度 0.9981,相对 L2 误差仅 6.2%,是三种量化档位中严格优于 4-bit 的(详见 README.md 的 "Measured fidelity" 一节);
- 视觉塔同样参与量化:84 个
visual.*张量带.scales量化,说明定位所依赖的空间特征也在保真范围内; - 离散输出天然抗噪:坐标是 1001 选 1 的分类问题,只要模型对正确 token 的置信度排第一,输出就是精确整数,不会像浮点回归那样出现"漂 2 个像素"的模糊结果。
在 M2 Pro 上 5-bit 版本实测约 385.8 tok/s 的解码速度,定位与转录一体的输出效率可观。
上手体验:一条命令跑通文字定位
安装 mlx-vlm 后,直接用仓库名加载即可(README 提供了完整示例,见 README.md):
pip install mlx-vlm
python -m mlx_vlm generate \
--model mlx-community/PaddleOCR-VL-1.6-5bit \
--image document.png \
--prompt "Transcribe this document to markdown." \
--max-tokens 512
如果你关注的是定位输出,可以在输出中留意 <|LOC_BEGIN|> 到 <|LOC_END|> 之间的坐标段——那就是这套 1000+ 位置 token 正在工作的现场。
小结:一句话读懂这套机制
<|LOC_0|>~<|LOC_1000|>:1001 个"整数字典",把归一化坐标离散化,每个 token 即一个坐标值;<|LOC_BEGIN|>/<|LOC_END|>/<|LOC_SEP|>:位置段的起止与内部分隔符,让坐标串结构自描述;- 384/14/2 的视觉配置:决定图像在序列中的 token 化方式,为空间感知打底;
- 5-bit 量化:以 0.75 GB 的体积保留了 0.9981 的权重余弦相似度,定位精度损失极小。
这套"用词表当坐标系"的设计,正是新一代 OCR 大模型把识别与定位统一进文本生成的聪明之处——读懂它,也就读懂了 PaddleOCR-VL 系列最核心的工程哲学。