我做 MoFont 之前,一直以为汉字字体是个"有限"的问题。你写几百个字,模型学会你的笔迹风格,剩下的照着推就行了——毕竟常用字翻来覆去就那么多,对吧。
后来我学到中文常用字其实有 6500 个,而"常用"是一句客套话。真正麻烦的不在这 6500 里,在它外面那条长长的尾巴。我测试过的每一个模型,都是在长尾里翻的车。
先把数字摆清楚
很多人对"汉字到底有多少个"没概念,我们先用几个标准把刻度建起来。
最老的字符集 GB 2312-1980 收了 6763 个汉字。这就是大家口头说的"六千多常用字"的来源,够日常读报,发消息,写文章。再往上,国家 2013 年颁布的 《通用规范汉字表》一共 8105 字,分三级:一级 3500 个常用字,二级 3000 个次常用字,三级 1605 个——而这第三级,官方的定义就是"由姓氏人名,地名,科技术语和中小学文言文用字构成"。
注意第三级的措辞:姓氏人名,地名。也就是说,国家在制定规范时就承认了,有一大批字,你平时根本用不到,可一旦轮到写某个人的名字或某个村的地名,它就突然变成你这辈子最需要的那个字。
继续往上爬。GBK 收了两万多字,GB 18030-2005 收到了 70244 个汉字,而 Unicode 的 CJK 统一表意文字到 17.0 版本已经定义了约 10.2 万个字符。
所以刻度是这样的:
- 日常够用:~3500
- "常用"的体面说法:~6500
- 国家规范全集:8105
- 编码标准能装下的:7 万 ~ 10 万+
3500 到 100000,中间差了快三十倍。这中间绝大部分字,你一辈子可能只会遇到几个——但每个人遇到的那几个,都不一样。这就是长尾的本质:对整体是低频,对个人是刚需。
为什么模型偏偏在长尾翻车
如果你把一个手写字体模型的生成结果一张张铺开看,会发现一个很残忍的规律:越常见的字越漂亮,越生僻的字越糊。"的地得是了我你"这种字,几乎所有模型都能写得有模有样;可一旦碰到笔画密集,部首罕见,结构刁钻的字,笔画就开始发虚,断裂,粘连,甚至拓扑都错了——该连的没连,该断的连上了。
原因不复杂,但很本质,我分三层讲。
第一层,训练数据本身就是长尾分布。 任何用来训字体生成的语料,字频都严重向高频字倾斜。模型见"我"见了几万次,见某个生僻字可能一次都没见过。它对常用字的笔形了如指掌,对长尾字则基本靠猜。
第二层,少样本风格迁移的本质是"内插"。 现在主流的少样本(few-shot)字体生成,做的事情是:给我几十个你写的样字,我从里面提取你的风格(笔锋,倾斜,粗细,连笔习惯),然后把这个风格"迁移"到你没写过的字上。说白了是在你写过的字之间做插值。常用字之所以生成得好,是因为它的结构和你写过的样字高度相似,插值很稳。而长尾字恰恰是插值断裂的地方——它的部首,它的结构,可能在你的样张里压根没出现过,模型没有可以内插的锚点,只能外推,一外推就崩。
第三层,复杂结构放大了所有误差。 一个十几画的生僻字,任何一笔的位置偏一点,粗细错一点,在密集的笔画里就会糊成一团。简单字容错率高,复杂字容错率近乎为零。
这不是我一个人的牢骚,是这个领域公开承认的难题。DG-Font 在论文里就点明,汉字这种"字符数量庞大的书写系统"对字体生成是个特别难的问题;后来的扩散模型方法 FontDiffuser 更是直接把"复杂字符"列为它要攻克的核心痛点,专门设计了多尺度内容聚合模块,就为了保住复杂字那些细密笔画。一个研究方向会专门为"复杂字写不好"立一堆论文,本身就说明:长尾是真的难,不是某个产品偷懒。
长尾才是真正的产品及格线
讲到这里,可能有人会说:既然长尾这么难,那我把常用字做好不就行了?用户大部分时间用的也是常用字啊。
这话听起来合理,但它漏掉了一个最要命的场景——人名。
一个字体,把 1000 个常用字写得龙飞凤舞,却把用户自己名字里那个生僻字写糊了,对这个用户来说,它的价值是零。不是打八折,是零。因为人对字体最在意的第一个字,往往就是自己的名字,自己孩子的名字。你在这个字上翻车,前面一千个字写得再好也救不回来。
所以我对长尾的态度很明确:它不是锦上添花的加分项,它是 demo 和能交付的成品之间的那条分界线。
一个只在常用字上好看的模型,只能做演示,截个图发朋友圈很惊艳;一个连长尾都能稳住的模型,才能导出一份你敢真正拿去用的 OTF。Demo 和产品的差距,80% 就在这条尾巴上。把高频字做好是基本功,把长尾做到不崩,才是真功夫。
MoFont 在长尾上怎么交代
说回我自己的产品。MoFont 做的事情是:你写 30–50 个字,我帮你训练出覆盖 6500 个常用字的完整手写字体,导出 OTF + TTF 两种格式,大约 3–4 小时出包,一次买断,不订阅。
我对范围说实话:6500 常用字是我承诺能稳住的范围,这一档我反复用真浏览器渲染验证过笔形,不靠生成时好看的截图自我感动。而 6500 之外那条真正的长尾——极冷僻的古籍字,某些只在特定地名里出现的字——我不会拍胸脯说每个都完美。能做到的我做,做不到的我宁可先讲清楚,也不把外推崩坏的字硬塞给你当成品。
这是我做这件事的底线。汉字的长尾是一面照妖镜,照得出哪些是认真训练的字体,哪些只是套了个模型的演示。我宁可把镜子先递给你,也不愿意你导出字体的那一刻,才发现自己的名字写糊了。
—— 砚秋
