Apr 24, 2025
2 min read
Rust,
pytorch,
candle,
typescript,
wasm,

从零开始构建手写输入法:准备篇

这一系列文章你会看到:

  1. 使用 pytorch 模型训练。
  2. 使用 rust candle 模型推理。
  3. 使用 rust 实现 wasm 部署。
  4. 使用 typescript + web worker 实现 wasm 调用。

前言

我是一名五笔使用者,五笔虽然可以打出用户不认识的字,但是也会碰到不会一些不知道怎么拆字根的汉字。所以也会使用“老人输入法”=> 手写输入来解决。

身为技术人员,很多年以前,我就好奇手写输入是怎么实现的?以前还没有没有深度学习,却能实现出识别率不错的手写汉字输入,确实是一件神奇的事。

刚好最近想找一个识别手写汉字的模型,发现大多数模型已经停留在差不多 10 年之前。

那要不自己实现一个呢?

思路

中文一共有多少个汉字?

Unicode 标准里收录了超过 90000 个汉字,但是我们常用的汉字其实只有 3000-4000 左右。 这两个不同的数量级实现起来应该是不一样的。如果是 4000 个汉字,我们只需要使用简单的分类模型即可实现。如果你要实现 90000 个汉字的识别,简单的分类已经不太可行了,因为全连接层的原因,参数很有可能会直接膨胀到上亿,并且向前传播的计算量也变得复杂。

针对超大汉字集,思路大概有几种:

  1. 分组分类: 先进行粗分类,再进行细分类。
  2. 相似度: 使用特征进行相似度计算。

个人使用的话,没必要实现这么大的汉字集,因此我这里使用中科院的手写汉字数据集 casia-hwdb,里面大概有3,740 个汉字,外加一些符号、外来字母等,一共有 4037 个字符。

技术实现

为了实现可用的汉字手写识别,我们需要考虑的是,模型推理的实时性,以及易部署。如果不考虑实时性,很多小模型的任务都可以直接使用多模态大模型来实现,这可能也是小模型式微的原因。

这里我们会采用多种技术和框架,使用 python 实现训练,使用 rust 实现部署,typescript 用于界面呈现。

训练

使用 pytorch lightning 作为训练框架,lightning 框架基于pytorch 再次封装,对于简单任务来说,它能减少很多样板代码。

为了能在 cpu 下也能保证实时性,使用 mobilenetv2 模型作为底座。当然我这里考虑到的是要部署到WASM 上,基于浏览器纯前端推理。如果是server 部署的话,使用 resnet 能获得更好的识别率。

推理

推理采集 rust candle 框架进行推理。由于之前的文章中对 mobilenetv2和resnet 系统的模型结构有过实现,因此使用 candle 作为推理框架是是合理的选择,更重要的是, candle 支持 wasm 环境。

部署环境

由于 mobilenetv2 足够轻量化,这里直接使用 wasm 部署到浏览器环境。

应用

大概实现为:使用 react + konva 实现画布,模拟手写输入,并起动一个web worker 进程处理wasm 模型推理,获得结果后呈现到页面上。

当然这只是其中一个应用场景,也可以实现一个桌面级的输入法,也可以导出 onnx 权重,再把onnx 权重转成 ncnn 推理框架,集成到 android 或者 ios 应用上。

最终效果:

ochw.ximei.me