前面六章默认了一件事:模型说英语和中文。这一章换个前提——一本模型要服务几十种语言。 难点不在「多学几门语言」,在词表就那么大,多给一种语言就要从别人碗里拿。 这一节先把这笔零和账算清楚。
这是全书最后一章。 前六章讲「怎么把一个模型做出来、用好」,这一章讲「它凭什么只对一部分人好用」。 42 课 图解人工智能 讲过语言模型的基本原理; 这一节关注的是语言之间的不平等怎么被技术放大。
一、为什么要做多语言:三个动机
第一个动机最实在:用户就在那里。世界上说英语的人不到十分之一,而绝大多数模型的第一语言是英语。 不做多语言,等于放弃九成市场。 第二个是数据杠杆。一种语言里的知识,往往在另一种语言里没有对应表达。 让模型会多种语言,等于让它能读到更多种写法的同一个事实。 第三个是迁移收益,也是最容易被高估的。 学过英语的模型去学法语,比从零学快得多——因为它们共享字母表和大量词根。
但这条对中文、日文、阿拉伯文不成立。 共享字母表才有迁移红利,不共享的时候,多语言训练基本是各学各的,还要抢同一份词表。
二、社区的三步走
第一步是「一个语言一个模型」。 2018 年之前基本如此,每种语言单独训一份,效果最好,成本也最高,小语种根本没人做。 第二步是「共享词表的多语模型」。多语 BERT 把上百种语言塞进同一套参数,一个模型干所有语言的活。 代价是小语种被大语种压着,效果比单语模型差一截。 第三步是「大规模覆盖加专门优化」。 多语翻译模型把语言数推到两百种,同时在采样、词表、数据配比上做专门设计。
这三步的共同趋势是:语言数越多,每种语言分到的资源越少。这不是工程没做好,是结构决定的。 Llama 2 站的位置有点特殊:它以英语为主,官方文档明确说非英语能力有限。 所以社区拿到它之后,第一件事往往是扩词表、补语料,而不是直接拿来用。
三、三条技术路线,各有各的账
第一条,从零训一个多语模型。 效果上限最高,成本也最高,而且要求每种目标语言都得有足够语料——小语种根本凑不出来。 第二条,拿现成模型继续预训练。补上目标语言的语料,再训一段。 成本低得多,代价是原始能力可能退化,得靠混一部分原语料兜住。 第三条,扩词表。把目标语言的常用字组塞进词表,然后重训嵌入层。 它解决的是 7.1 节这张表里的问题:token 膨胀。
三条可以叠加。 实践里最常见的是「扩词表 + 继续预训练」,因为扩词表能立刻降低 token 数, 继续预训练能让新词表真正被用起来。 要注意扩词表有个坑: 只扩词表不继续训练,新词是随机初始化的,模型不会用。表现是 token 数降下来了,但效果反而变差。
四、词表是零和的:一笔账算到底
词表大小是个超参,一般是 32000 到 128000 之间。 它不会随语言数一起涨——涨了嵌入矩阵就大,输出层就大,显存和算力都跟着涨。 所以词表是一个固定大小的名额池。英语占得多,别的语言就占得少。 而谁占得多,取决于预训练语料里谁的字组重复得多。 演示里把这件事放大了:同一段技术规范的五种语言版本,用「每种语言分五分之一」的词表, 各语言每 token 能装 1.6 到 2.0 个字符。
换成「英语占八成」的词表,中文的 token 数涨到 1.64 倍,英语自己缩到 0.66 倍。 一边省下来的,正是另一边多花的。 这个倍数直接换成钱和上下文:同样的窗口,中文能装的内容少了 39%; 按 token 计费的接口,同一段内容对中文用户贵 64%。 而这一切跟「模型聪不聪明」没关系。它只是词表里没有那些字组,只能逐字切。
五、一个常被忽略的区分:多语言不等于多文化
会说法语不等于懂法国的办事规矩。 模型能生成通顺的阿拉伯语,不代表它知道这个地区的合同怎么写、称呼怎么用、哪些话不能提。 语料里英语占比高,模型的世界观、举例、默认设定就都偏英语世界。 换一种语言提问,它可能只是把英语的答案翻译过去。 所以做行业落地时,语言能力只是第一层。第二层是领域语料,第三层是本地常识。 三层缺一层,用户就会觉得「它说话怪」。
这一条在 7.4 节讲客服时会再出现一次:同一个问题,用户不满意的往往不是语法, 是「你没听懂我在问什么」。
六、动手 Demo:同一段话五种语言,换词表看 token 怎么涨
五段同义文本,内容是「灯具防护等级、接线盒、通电测试」。 三种词表都是同样的名额总数,只有分配比例不同。 词表用「字符加相邻字组」按频次取,切分用贪心最长匹配——BPE 的简化版。 看两个数:每 token 装几个字符,以及换词表之后 token 涨了几倍。
QUOTA = [
(u'均衡', {u'中文': .2, u'英语': .2, u'日语': .2}),
(u'英语优先', {u'英语': .8, u'中文': .05, u'日语': .05}),
]
def build(quota): # 按配额从每种语言里取候选
v = set()
for lang, t in TEXTS:
v.update(cands(t)[:int(VSIZE * quota[lang])])
return v
def tok(s, v, L=3): # 贪心最长匹配
while i < len(s):
for k in range(min(L, len(s) - i), 0, -1):
if s[i:i + k] in v:
i += k; break
实跑输出:
--------------------------------------------------------------------
同一段话五种语言,换词表看 token 怎么涨
--------------------------------------------------------------------
① 五段同义文本
--------------------------------------------------------------------
| 语言 | 字符数 |
|---|---:|
| 英语 | 287 |
| 中文 | 73 |
| 日语 | 114 |
| 阿拉伯语 | 236 |
| 印地语 | 269 |
内容一样,都是「户外灯具防护等级不低于 IP65」。字符数差三倍——中文最短,阿拉伯语和印地语最长。
! 注意:字符数和 token 数是两件事。字符多的语言未必 token 多,要看词表里有没有它常用的字组。
② 均衡词表:每种语言都分到五分之一
--------------------------------------------------------------------
| 语言 | token | 每 token 字符 |
|---|---:|---:|
| 英语 | 146 | 1.97 |
| 中文 | 36 | 2.03 |
| 日语 | 71 | 1.61 |
| 阿拉伯语 | 120 | 1.97 |
| 印地语 | 149 | 1.81 |
词表上限 600 个,五种语言各分 120 个名额,并集 566 个(标点数字重复)。
五种语言的压缩率都在 1.6 到 2.0 之间。
! 均衡的时候谁都不吃亏,压缩率差不多。这一格是基准线——下面换配额,看差距从哪里出来。
③ 换词表:英语优先 vs 自然分布
--------------------------------------------------------------------
| 语言 | 均衡 | 英语优先 | 自然分布 |
|---|---:|---:|---:|
| 英语 | 146 | 96 | 109 |
| 中文 | 36 | 59 | 26 |
| 日语 | 71 | 92 | 78 |
| 阿拉伯语 | 120 | 177 | 161 |
| 印地语 | 149 | 222 | 209 |
三张词表都是 600 个 token,区别只在名额怎么分。
词表大小固定,多给一种语言,就少给另一种——这是零和。
④ 膨胀倍率:同一个意思要多花几倍 token
--------------------------------------------------------------------
| 语言 | 英语优先 | 自然分布 |
|---|---:|---:|
| 英语 | 0.66 | 0.75 |
| 中文 | 1.64 | 0.72 |
| 日语 | 1.30 | 1.10 |
| 阿拉伯语 | 1.48 | 1.34 |
| 印地语 | 1.49 | 1.40 |
! 英语优先词表下,中文的 token 数涨到基准的 1.64 倍。
换个说法:同样的上下文窗口,它能装的内容少了 39%;
英语反过来缩到 0.66 倍,这是同一笔零和账的两面。
! 成本完全跟着走。
按 token 计费的接口,同一段内容对该语言用户贵 64%——不是因为内容更多,是因为词表里没有它的字。
⑤ 真实规模下没那么夸张,但方向一样
--------------------------------------------------------------------
这里用 600 个 token 把效应放大了。
真实词表 32000 个,中文大约占几百到一千多个,膨胀倍数在 1.5 上下,不会到两倍。
! 但方向是完全一样的:词表是按频次长的,语料里谁多谁就占名额。
英语在预训练语料里占大头,词表自然向英语倾斜。
! 这也解释了为什么后来很多模型把词表从 32000 扩到 10 万以上——不是词表越大越好,
是要让长尾语言也有名额。
①先看字符数。同一段内容,中文 73 个字符,英语 287 个,印地语 269 个,差了三倍多。 但字符数不等于 token 数——要看词表里有没有它常用的字组。这一句是整节的引子。 ②均衡词表下,五种语言的压缩率都在 1.6 到 2.0 之间,差距很小。 这就是配额公平的样子:谁都不占便宜,谁也不吃亏。 这一格是基准线。下面换配额,看差距从哪里冒出来。 ③三张词表的名额总数一样,只有分法不同。英语优先那张,英语拿走八成,其余四种各分 5%。 结果很直接:英语从 146 个 token 降到 96 个,中文从 36 涨到 59,阿拉伯语从 120 涨到 177。 这是零和,不是双赢。 ④这张表是把 ③ 换成倍率。英语 0.66,中文 1.64。 1.64 倍意味着什么:同样的 4096 上下文,中文能装的内容少了 39%; 按 token 买单的接口,同一句话贵 64%。 而这些差别全在词表里,跟模型的层数、参数量、训练数据质量都没关系。换个词表,账就变了。 ⑤最后要诚实一点:这里用 600 个 token 把效应放大了。 真实词表 32000 个,中文大约占几百到一千多个,膨胀在 1.5 倍上下,没有演示里这么夸张。 但方向完全一样。 词表是按频次长的,语料里谁多谁占名额——所以词表倾斜不是设计失误,是数据分布的镜子。 这也解释了后来很多模型把词表扩到十万以上:不是词表越大越好,是要给长尾语言留出名额。
30 秒实验
- 把「均衡」词表的名额总数从 600 改成 120,重跑第 ② 步。五种语言的压缩率还挨得这么近吗? 这说明什么?
- 给词表加一条规则:每种语言至少保底 30 个名额。重跑第 ③ 步,中文的膨胀倍率会降到多少?
- 把日语换成一种和中文共享汉字的语言(比如繁体中文),它的膨胀倍率会比中文低还是高?为什么?
常见误区
- 以为多语言就是「多喂几种语言的语料」。 词表、采样、评测集三样不同步改,喂进去的语料也只是被逐字切成 token,学不到东西。
- 拿英语效果去推小语种效果。 「英语能到 90 分,小语种大概 70 分」——这种估算没有任何依据,实测里差距经常是三十分以上。
- 以为扩词表就完事。 新词是随机初始化的,不继续预训练模型根本不会用,表现是 token 数降了、效果反而差了。
- 把「会说这门语言」当成「懂这个地方」。语言对了,常识、规矩、忌讳全不对,用户照样不买账。
自测三问(点开对答案)
1. 为什么说词表大小是个零和游戏? —— 因为词表总量被嵌入矩阵和输出层的规模卡住,不能随语言数无限涨。 名额固定,一种语言多占,另一种就少占。谁占得多,取决于语料里谁的字组重复得多。 2. 「多语言训练有迁移收益」这句话,对哪种语言不成立? —— 不共享字母表和词根的语言。 英语和法语之间迁移很明显,英语和中文之间基本没有——它们连切分单位都不一样,谈不上互相借力。 3. 扩词表之后为什么必须继续预训练? —— 新加的词嵌入是随机初始化的,模型不知道它是什么意思、该接在什么位置。 只扩不训,token 数是降下来了,但模型对这些 token 的处理全是噪声,效果反而更差。下一节看料从哪来。多语言的难,一半难在词表,另一半难在语料根本收不到。
- ☐ (本篇完,下一篇 7.2 多语言大模型的预训练资源和评测任务)
相关笔记
- 42 课 图解人工智能:语言模型的基本原理
- 资源与评测:语料分布与温度采样
- 优化方向:词表是零和的那笔账
- 🦙 Llama 大模型实践 · 索引