前阵子刷到飞桨 PaddleOCR 团队开源了 PP-OCRv6,三档模型。
其中最小的 Tiny 那档,只有 1.5M 参数!而且说能在浏览器里跑!
关键是效果也不差,可以支持 49 种语言!
Tiny 这个小模型,在官方评测的文本检测上是 80.6 分,图里那些通用多模态大模型最高的才 46.8 分;文本识别 73.5 分,也只比最能打的 Qwen3-VL-235B 差了一点点。
属实有点牛逼了。
说实话,我挺震惊的……
1.5M 参数意味着什么?
换成大家熟悉的单位,1.5M 就是 0.0015B(1B = 10 亿,1.5M = 150 万)。
浏览器里跑 OCR 意味着什么?
图片不用传服务器,不等排队,不按 token 计费,数据不离开你的设备。
带着好奇,我搭了个项目来验证,确实可以在浏览器里跑起来。
也把它部署上线了,感兴趣的朋友可以试试看 ocr.laifuyou.com。
纯浏览器端运行,不经过任何服务器。
图是我随手传的,上面框出来的就是识别结果。
先说几个数字
1.5M 是参数量,不是文件大小。
Tiny 档的两个模型文件 det.onnx + rec.onnx,加上字典文件,一共大约 6MB。
另外首次打开还要下载 ONNX Runtime 的 WASM 运行时,之后浏览器会缓存起来,再打开就不用下了。
PP-OCRv6 一共三档,同一套架构缩放出来的:
| 档位 | 参数量 | 模型体积 | 检测精度 | 识别精度 | 语言 |
|---|---|---|---|---|---|
| Tiny | 1.5M | ~6MB | 80.6% | 73.5% | 49(不含日文) |
| Small | 7.7M | ~30MB | 84.1% | 81.3% | 50 |
| Medium | 34.5M | ~132MB | 86.2% | 83.2% | 50 |
Tiny 为了极致压参数,识别编码器砍了不少,靠蒸馏补精度。
日文词表太大直接不支持。
识别准确率虽然比 Medium 低了 10% 左右,但是日常截图、文档照片够用。
如果有更高识别要求,也可以切换成 Small 模型。
尺寸占用能接受,识别效果也提高了不少。
OCR 和大模型不是一回事
之前我以为 OCR 和大模型是一回事。
研究了一番发现并不是。
对于 OCR 来说,答案在图片里本来就存在,模型只是把它“读出来”。
而对于 LLM 这类大模型来说,这个答案并不存在于输入文本中。
简单概括的话,可以这样说:
OCR 更像“识别问题”,LLM 更像“生成问题”。
OCR 对“像素”敏感,LLM 对“语义”敏感。
具体到 OCR 自己,主要就是两步:
检测:找到字在哪,输出一堆框。
识别:把每个框裁出来,认出框里是什么字。
对应到代码,也是这两步:
// 第一步:检测——图片送进 DBNet,输出概率图,后处理找出文本框
const detResult = await detSession.run({ x: imageTensor });
// 概率图 → 二值化 → 连通域 → 文本框
const boxes = findTextBoxes(detResult);
// 第二步:识别——每个框裁出来,逐个送进识别模型
for (const box of boxes) {
// 裁框,缩放到固定高度
const cropped = cropAndResize(image, box);
const recResult = await recSession.run({ x: cropped });
// CTC 解码:概率矩阵 → 文字
const text = ctcDecode(recResult);
}
两个模型各司其职:det 管“字在哪”,rec 管“字是啥”。加起来 1.5M 参数。
为什么 OCR 模型可以这么小
我很好奇,为什么 OCR 模型可以做到这么小。
其实可以这样理解。
LLM 是个什么都要学的全能助手。
如:编程、数学、历史、法律、医学、写作、英语、中文、推理、世界知识……
而 OCR 是个偏科生,它只干一件事,只要把这件事干好就行。
如:找到图片里文字的位置、把文字一个个认出来。
任务边界窄得多。
所以 PP-OCRv6 这样 1.5M 参数的专用模型,在 OCR 专用指标上才能跟几十 B 的通用模型掰手腕,文本检测这一项甚至甩开一大截。
这其实和人一样。
一台专业扫码枪,可能比一个机器人更快认出条形码。
不是机器人不高级,而是专用系统把全部容量都用在了一个问题上。
想一想,其实很多任务不用力大砖飞,全上大模型。
模型怎么在浏览器里跑
以前 OCR 得把图片传服务器,后端跑模型,返回结果。
现在模型直接在浏览器里跑。但浏览器以前只能跑 JavaScript,稍微重一点的计算都得丢给服务器。怎么做到的?
靠的是这几年一个关键变化:WebAssembly(WASM)。
简单说,WASM 是一套浏览器能直接执行的二进制指令格式。用 C/C++/Rust 写的程序,编译成 WASM 后就能在浏览器里跑,速度接近原生。
你可能已经在用 WASM 驱动的产品了——Figma 的画布引擎就是 C++ 编译成 WASM 跑的,FFmpeg 也有了浏览器版本,视频转码都能在本地完成。
有了 WASM,C/C++ 生态的成熟库就能被搬进浏览器——包括 AI 推理引擎。
而 AI 模型推理的核心是大量矩阵运算,这本来是 GPU 擅长的活。好在现代浏览器不只有 WASM,还有 WebGPU 和 WebGL 可以调用 GPU。推理引擎把这些能力统一封装,浏览器就有了跑模型的完整条件。
有了这个基础,OCR 模型跑在浏览器里的链路就清楚了。
具体用到的是两个工具:Paddle2ONNX 把 PaddleOCR 训练好的模型导出成 ONNX 这个通用交换格式,再交给微软的 onnxruntime-web 在浏览器里跑。
这里有个细节值得一提。onnxruntime-web 提供了统一的推理层,底层可以走不同的执行后端:WebGPU 和 WebGL 能调 GPU 加速,WASM 走 CPU。实际运行时会逐个尝试,用上能用的最快后端。
WASM 兼容性最广,几乎所有现代浏览器都支持,小模型用它就够了。WebGPU 能调显卡,但支持面窄一些,对小模型也不一定更快——数据在 CPU 和 GPU 之间搬运也要时间。
具体到代码,浏览器里加载一个 ONNX 模型是这样的:
import * as ort from "onnxruntime-web";
// 告诉运行时去哪里找 WASM 文件
ort.env.wasm.wasmPaths = "/ort/";
// 按优先级逐个尝试可用的执行后端
const candidates = ["webgpu", "webgl", "wasm"];
for (const ep of candidates) {
try {
const session = await ort.InferenceSession.create(modelBuffer, {
executionProviders: [ep],
});
// 成功,用这个后端
break;
} catch {
// 当前后端不可用,尝试下一个
}
}
没有 Python,不用装 GPU 驱动,一个 npm 包就把推理引擎带进了浏览器。
最后
做完这个小东西,我比较强烈的一个感受是:不是所有活都得上大模型。
不同场景、不同任务,合适的小模型也可以发挥很大的作用。
以前浏览器是个跑界面的容器,计算和存储都在服务器那边。
现在这个分工在变。WASM 给了通用计算的入口,WebGPU 能调显卡,编解码和本地文件也各自有了对应的接口,再算上 AI 推理,浏览器越来越像一个轻量的本地运行时。
专用模型够小,才进得了浏览器;浏览器能跑推理了,专用小模型才有了新的落点。
程序员圈子有句话流传很广——Atwood 定律:凡是能用 JavaScript 实现的,最终都会用 JavaScript 实现。
以前当段子听。JS 能写前端、写后端、写客户端,现在还能跑 AI 模型。
大声告诉我:世界上最好的语言是什么?🤓
项目地址
- 在线体验:ocr.laifuyou.com
- GitHub:github.com/JS-banana/o…