1.5M 参数的 OCR 模型,我把它跑进了浏览器

前阵子刷到飞桨 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 自己,主要就是两步:

检测:找到字在哪,输出一堆框。

识别:把每个框裁出来,认出框里是什么字。

对应到代码,也是这两步:

ts 复制代码
// 第一步:检测------图片送进 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 模型是这样的:

ts 复制代码
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 模型。

大声告诉我:世界上最好的语言是什么?🤓

项目地址

相关推荐
风骏时光牛马1 小时前
AI编程场景下故障根因分析与复盘总结
前端
Sterting2 小时前
Element-Plus 入门与整体布局
前端·vue.js·elementui
chaors7 小时前
DeepResearchSystem 0x08:KB 知识记忆
langchain·agent·ai编程
程序员黑豆9 小时前
Java 注释详解:单行、多行与文档注释的完整指南
java·前端·ai编程
剪刀石头布啊9 小时前
antd中可编辑表格中巧用useWatch实现联动高性能效果,以及定制延伸学习
前端
油丶酸萝卜别吃9 小时前
前端转全栈学习路线
前端·学习
浮生望10 小时前
前端路由进阶:History API原理与手写HistoryRouter
前端
陆枫Larry10 小时前
JavaScript 中的竞态是什么,为啥会有竟态?
前端
飞哥数智坊10 小时前
实测7套 Code Agent组合:最终效果,真不只取决于模型
ai编程