第 3 章 文本怎样变成 Token、ID 和嵌入向量
3.1 文本千变万化,模型怎样接收?
Section titled “3.1 文本千变万化,模型怎样接收?”第 2 章 中,输入还是一段文字,没过几步就变成了一行行数字。这中间其实要作出不少选择。文本可能有陌生单词、标点、不同语言的文字,也可能出现训练样本里从未见过的符号。但模型必须有一套明确的输入条目,还得能按同样的规则反复识别它们。
所以,我们要解决的不只是“怎样给词编号”,而是:怎样用有限的词表表示各种文本,保留需要区分的信息,再让这些编号进入数值计算?这里有三步:切分文本、给片段编号、按编号取出数值表示。如果只说“把词变成向量”,就容易混淆这些步骤,也难以看清问题出在哪里。
为什么要把文字拆成片段?看一个小例子就容易明白。英语 cat 表示单数的“猫”,cats 是它的复数形式。如果词表只收录了完整的 cat,就没法把 cats 整体认作已有条目。但只要再有一个 s,就能把它写成 cat 接上 s。有限的片段经过组合,可以表示比整词列表更多的文本。
沿着这个思路,可以先比较两种办法。一种是整词编码:常用词只占一个条目,很省位置,但遇到词表里没有的新拼写,可能只能记成“未知”。另一种是字符编码:没见过的词可以用已知字符拼出来,不过同样一段文本通常会占用更多位置;遇到词表之外的字符,也仍然需要处理办法。子词方案则在词表中存入常见的多字符片段,有些词可以整体使用,有些可以拆成更小的片段来表示。
表 3.1——cats 在三种方案下怎样表示。
| 假定采用的方案 | 词表中需要的条目 | 编码结果 | 需要注意的问题 |
|---|---|---|---|
| 整词 | cats | [cats] | 词表里必须有这个完整的词 |
| 字符 | c、a、t、s | [c][a][t][s] | 要占四个位置,而不是一个 |
| 子词 | cat、s | [cat][s] | 怎样切分,取决于词表和匹配规则 |
这不意味着编码越短,模型就越好。词表大小、序列长度、能覆盖哪些文本,以及怎样训练,都会相互影响。Sennrich 等人的子词研究讨论了怎样表示低频词。本章先用一套规则完整、可以手算的教学方案,不重现论文中的 BPE 训练算法。Sennrich 等,2016
下面的词表和二维向量都是为教学设计并人工赋值的,规模足够小,可以逐项核查。它们不是从某个现有大语言模型的分词器或检查点中取出的。
3.2 先规定词表和编码方法
Section titled “3.2 先规定词表和编码方法”要照着这个办法处理文字,先得有一份共同使用的片段清单。可以把每个条目想成一块能反复使用的文字积木:一段话里两次出现 cat,不必在清单上再加一个 cat。这里借用的只是“同一种片段可以反复使用”这一点,程序里保存的仍然是文字和数字。
词表里可以使用的一种条目,称为Token 类型(token type)。同一类型在序列中可以反复出现,计数时每次出现(token occurrence)都要单独算。词表(vocabulary)列出这些类型及其编号,也就是 ID。编号只负责区分条目,数字大小不表示含义有多大或多强。
表 3.2——toy-longest-v1 的完整词表和嵌入向量。␠ 表示普通空格,编号从零开始。0 是第一行的编号,不表示这一行不存在。先在中间一列找到 cat,向左就能读出它的 ID:16。接下来切分文本时,只用这两列;最后一列先放着,等文字处理完了再用。
| ID | 片段 | 存储的嵌入向量 |
|---|---|---|
| 0 | <unk> | [0,0] |
| 1 | The | [1,0] |
| 2 | ␠capital | [1,1] |
| 3 | ␠of | [0,1] |
| 4 | ␠France | [2,1] |
| 5 | ␠is | [1,−1] |
| 6 | ␠Paris | [2,2] |
| 7 | . | [0,−1] |
| 8 | 法 | [1,0] |
| 9 | 国 | [1,1] |
| 10 | 的 | [0,1] |
| 11 | 首都 | [2,1] |
| 12 | 是 | [1,−1] |
| 13 | 巴黎 | [2,2] |
| 14 | 。 | [0,−1] |
| 15 | ␠ | [0.5,0.5] |
| 16 | cat | [1,0] |
| 17 | s | [0,1] |
切分文本时,暂时不用看嵌入向量这一列。每次从还没处理的文本开头出发,在词表中寻找能与开头完全匹配的片段,并选最长的一个。输出它的 ID,然后越过这一段,继续处理后面的文字。全部读完就停止。如果找不到任何匹配片段,就读过一个 Unicode 码点,输出专门表示未知内容的 ID 0。Unicode 是一套表示文字的标准,其中每个有编号的元素叫作码点。后面例子中那些无法匹配的字母和符号,每次都恰好读过一个看得见的字符。不过,其他文本中一个可见字符可能包含多个码点,所以规则要准确写成“一个码点”。Unicode 的编号属于文字标准,和我们给词表条目编的 ID 是两套编号。
这套规则区分大小写,大写字母不能拿小写字母来匹配;不做规范化处理,也就是不会先把原文改写成某种统一形式再匹配;不在原文前后添加开始或结束标记。ID 0 只在找不到匹配时使用,<unk> 这几个字符本身不参加文本匹配。其余片段互不重复:如果两个不同片段一样长,它们就不可能同时与同一段开头完全相同,因此不需要另定规则来处理并列的最长匹配。
规定了这些细节,才能照着规则得到确定结果。只说“用子词”,还缺很多信息:词表是什么、怎样选边界、是否规范化、匹配不上怎么办,都要说明。同一段文本有多少个 Token,也必须先指定编码方案和版本才有答案。
3.3 按规则走完几个例子
Section titled “3.3 按规则走完几个例子”3.3.1 用已有片段拼出新词
Section titled “3.3.1 用已有片段拼出新词”规则有了,接下来就能一步步执行。每一步只盯住三样东西:还没读的文字、从开头选中的片段,以及已经写下的 ID。
例 3.1——编码 cats。v1 中没有完整的 cats,但开头能匹配 cat,于是先读过这三个字母。剩下 s,词表里也有对应条目。
| 步骤 | 尚未处理的文本 | 最长匹配 | 输出 ID | 目前的 ID 序列 |
|---|---|---|---|---|
| 1 | cats | cat | 16 | [16] |
| 2 | s | s | 17 | [16,17] |
| 3 | 空 | 停止 | — | [16,17] |
同一个 cats,按词数是一,按字母数是四,按本例的 Token 数则是二。这三个数统计的不是同一种东西。一个 Token 不必是完整的词,也不必单独拿出来就有明确、实用的含义。
现在规定第二版 toy-longest-v2:保留 v1 全部条目,再添加 cats,ID 为 18,向量为 [1,1]。编码 cats 时,cat 和 cats 都能匹配开头,但后者更长,所以结果变成 [18]。输入没变,匹配规则没变,只是词表变了。
如果输入的是 cat,v2 仍会选 ID 16。词表里有更长的片段,并不意味着任何时候都能用它;它必须与眼前的文本匹配。再看 catscats:v1 得到 [16,17,16,17],v2 得到 [18,18]。一个片段重复出现,并不需要在词表中再增加一个条目。
从两个 Token 变成一个,只能说明编码变短了,不能单凭这一点认定模型理解得更好、回答更准确。新的表示方式,还得能让后面的模型正确使用。
3.3.2 Token 不一定按空格或汉字切分
Section titled “3.3.2 Token 不一定按空格或汉字切分”刚才看到,一个单词可以拆成几个片段。换成句子,还会遇到反过来的问题:一个片段能不能把平时与单词分开的空格也包含进去?答案仍由同一条匹配规则决定。
例 3.2——给英语句子编码。用同一套规则处理 The capital of France is Paris.:
| 位置 | 本步匹配的片段 | ID | 处理后剩下的文本 |
|---|---|---|---|
| 1 | The | 1 | ␠capital␠of␠France␠is␠Paris. |
| 2 | ␠capital | 2 | ␠of␠France␠is␠Paris. |
| 3 | ␠of | 3 | ␠France␠is␠Paris. |
| 4 | ␠France | 4 | ␠is␠Paris. |
| 5 | ␠is | 5 | ␠Paris. |
| 6 | ␠Paris | 6 | . |
| 7 | . | 7 | 空 |
第二步时,单独的空格也能匹配,但 ␠capital 更长,所以空格与后面的单词一起成为一个片段。整个过程没有扔掉空格或标点,把选中的片段依次拼起来,就能还原原文。
例 3.3——给中文句子编码。法国的首都是巴黎。 表达的是同一个事实。中文没有用空格标出词与词的边界,但仍然可以从剩余文本的开头逐次匹配:
| 尚未处理的文本 | 本步匹配的片段 | ID |
|---|---|---|
法国的首都是巴黎。 | 法 | 8 |
国的首都是巴黎。 | 国 | 9 |
的首都是巴黎。 | 的 | 10 |
首都是巴黎。 | 首都 | 11 |
是巴黎。 | 是 | 12 |
巴黎。 | 巴黎 | 13 |
。 | 。 | 14 |
“首都”两个字是一个 Token,“巴黎”也是一个 Token,“法国”却分成了两个。原因在于表 3.2 的具体条目,而不是有什么普遍规则说,一个 Token 就等于一个词、一个汉字,或者两个空格之间的内容。
这两句话在本词表下恰好都用了七个 Token,只是所选例句和词表造成的巧合,不能拿来当中英文的固定换算比例。SentencePiece 展示了不必先把原始文本分好词的分词方法,具体算法留到第 19 章介绍。Kudo 与 Richardson,2018
3.4 编码会不会丢掉信息?
Section titled “3.4 编码会不会丢掉信息?”前面处理的每一段文字,都在词表中找到了完全相同的片段。如果找不到,编码后还能保留原文中的区别吗?在把 ID 交给模型之前,我们先检查这个问题。
检查编码时,可以做一次往返检查(round trip):先把原文编码成 ID,再解码回文字,看看与原文是否相同。前面的例子能完整还原,是因为每一步都精确匹配原文,解码时又按原顺序拼回了同样的片段。
例 3.4——不同原文得到相同编码。输入 cat🙂 时,先匹配 cat,输出 ID 16。笑脸在词表中找不到,只能按未知内容处理,输出 ID 0。解码时,能查回来的就只有 cat 和 <unk>。换成 cat🚀,结果也一样。
表 3.3——编码碰撞:原文中的区别丢失了。
| 原始输入 | 编码 ID | 解码文本 | 编码后无法知道什么 |
|---|---|---|---|
cat🙂 | [16,0] | cat<unk> | 原来是哪一个未知符号 |
cat🚀 | [16,0] | cat<unk> | 原来是哪一个未知符号 |
解码器收到的都是 [16,0],怎么知道原来是笑脸还是火箭?只靠这两个 ID,不可能对两种原文都保证还原正确。不是解码不够仔细,而是输入中已经没有这项区别。如果另存一份原文,当然还能查到,但那就用了 ID 之外的信息。
这里每次按未知内容处理的是一个Unicode 码点(code point),也就是 Unicode 标准中带编号的元素。一个肉眼可见的字符可能由多个码点组成,本例的笑脸只有一个。第 8 章会进一步介绍这些细节。计算机保存文字时会用到一种小的存储单位,叫作字节。能覆盖全部字节取值的分词器可以避免本例这种未知符号碰撞,而规范化又可能带来其他无法原样还原的情况。本例只说明这套有限规则的问题,不能推广到所有分词器。
大小写也由规则决定。The 对应 ID 1,小写 the 却在三个位置都找不到匹配,得到 [0,0,0]。程序不会暗中把大小写改过来。如果想加这一步预处理,就已经改了编码规则,也必须把变化记录下来。
3.5 ID 是编号,不是含义的大小
Section titled “3.5 ID 是编号,不是含义的大小”3.5.1 按编号取出一行数
Section titled “3.5.1 按编号取出一行数”现在已经知道模型会收到哪些 ID,也知道哪些信息可能在编码时丢失。回到表 3.2 的最后一列,就接上了第 2 章的下一步:编号负责定位,查出的数才参与模型后面的计算。具体做法是:按每个 ID 查出对应的嵌入向量,并按 Token 在文本中的顺序排列。
例 3.5——查出 v1 中 cats 的嵌入向量。
文本片段: [cat] [s]词表 ID: 16 17读取的行: [1,0] [0,1]有序结果: [[1,0], [0,1]]一个词 → 两个片段 → 两行数值
catscat | s按输入顺序保留取出的两行:[[1,0], [0,1]]。这里没有对 ID 做加法或乘法。
例子最后一行的括号,可以从里往外读:[1,0] 是 cat 对应的一组数,[0,1] 是 s 对应的一组数。最外层的括号把这两组数按文本顺序放在一起,并不是要求把它们相加。
图 3.1 分别画出文本片段、编号和查出的数值。结果有两行,因为编码后有两个 Token;每行有两个数,因为我们把嵌入维度设成了二。这里的维度(dimension)就是向量里坐标的个数,既不是 Token 数,也不是由 ID 的大小决定的。
嵌入查表(embedding lookup)只是读取参数,不会在查表时学习参数。神经语言模型通常可以在学习序列概率函数的同时学习数值表示。不同输入通过这些表示和共用的预测函数,就能利用彼此相关的统计信息。Bengio 等人提出过这类模型的早期形式。本章的向量只用来演示怎么查表,数值都是人为给定的,例如第一个坐标并不表示“有多像动物”。Bengio 等,2003
3.5.2 编号相近,就说明含义相近吗?
Section titled “3.5.2 编号相近,就说明含义相近吗?”cat 和 s 的 ID 是 16 和 17,只差一,能据此说它们含义接近吗?想一想图书馆里编号相邻的两本书:重新编一次号,书的内容并不会跟着变。词表也一样,我们可以换一套编号,同时保留每个编号所指的内容,再看原来的说法是否成立。
把 cat 和 The 的 ID 互换,同时交换嵌入表中对应的行。现在 cat 的 ID 是 1,查出的仍是原来的 [1,0];The 的 ID 是 16,也仍然查出自己的原向量。完整的生成器在输出时也要使用这些编号:它先给出 ID,再把 ID 读回文字。如果只改输入这一头,输出的同一个编号就可能被读成另一个片段。因此,还要同步调整输出 ID 与文字的对应关系,以及其他所有按 Token 编号排列的参数。这样,只是换了编号,每个 Token 在计算中的作用可以保持不变。
这时,cat 和 s 的编号差从一变成了十六,模型的预期行为却没有变。可见,编号差不能衡量它们本身的语义关系。注意,这里要求所有对应关系一起调整,并不是说单改输入 ID 就没事。
再看一个会出错的改法:交换 cat 与空格条目的 ID,却不动嵌入表。空格原来的向量是 [0.5,0.5],新的 cat 编号就会查到这一行。后面的计算从一开始就拿错了数。因此,分词器、输入嵌入表、输出映射和检查点,必须配套使用。
v2 新增 cats 时,也有兼容问题。原封不动的 v1 模型没有第 18 号嵌入行,只换分词器,不会自动补上这一行参数,也不会补齐相应的输出条目。扩充词表时,编码和模型都需要相应调整;不是把输入变短,就自动得到了更好的模型。
3.6 用余弦相似度比较向量
Section titled “3.6 用余弦相似度比较向量”3.6.1 长度不同,方向也可能不同
Section titled “3.6.1 长度不同,方向也可能不同”编号只是标签,所以编号的差值不能衡量相似程度。嵌入向量的作用不同,它里面的数确实要参与计算。要比较两组这样的数,仍然得先选定比较什么。一种常见办法是比较它们的方向。为了看清“方向”是什么意思,先把两个数画在纸上。
画一条横轴和一条竖轴,它们相交的零点叫作原点。第一个数表示横向移动多少,正数向右、负数向左;第二个数表示纵向移动多少,正数向上、负数向下。所以 [1,0] 对应右边一个单位处的点,[1,1] 对应右边一个单位、上方一个单位处的点。从原点向各自的点画一支直箭头,就把向量画出来了。后一支箭头是斜的,并不是沿横轴、竖轴拐一次弯;它比前一支更长,方向也不同。
u · v = 1;|u| = 1;|v| = √2。u 与 w 的方向相反。
图 3.2 把它们画在同一个坐标系里,还加上了朝左的 [-1,0]。这些坐标只规定几何位置,我们没有给坐标轴指定词义。余弦相似度(cosine similarity)用来比较非零向量的方向,排除长度的影响。
为什么要把方向和长度分开?[2,2] 和 [1,1] 指向同一条斜线,只是前者伸得更远。如果我们只想比较方向,就应该把它们看成一样。余弦相似度通过除去长度的影响来做到这一点。下面先算出要用的几个数,再看怎么除。
例 3.6——比较水平向量与斜向向量。令 、,先算下面三个量。
| 计算量 | 算法 | 结果 |
|---|---|---|
| 点积 | 对应坐标相乘,再相加 | 1 × 1 + 0 × 1 = 1 |
| u 的长度 | 坐标分别平方、求和,再取正平方根 | √(1² + 0²) = 1 |
| v 的长度 | 同样计算 | √(1² + 1²) = √2 |
第一行这种“对应相乘再求和”的运算,叫作点积(dot product)。“平方”就是一个数乘以它自己。求箭头长度时,横向位移、纵向位移和直箭头构成直角三角形。根据勾股定理,箭头长度的平方等于两个位移的平方之和。对于 [1,1],这个和是 1 + 1 = 2;要得到长度本身,还要找出平方后等于 2 的正数。2 的正平方根,是平方后等于 2 的那个正数,约为 1.4142。
这里的 u、v 只是两支向量的名字。下面式子中的分数线表示相除,≈ 表示“约等于”。用点积除以两个向量长度的乘积,就得到:
本例中,第一支箭头水平向右,所以结果也就是斜箭头的横向位移除以它的长度:1 ÷ 1.4142,约为 0.7071。这正是两支箭头夹角的余弦,“余弦相似度”的名称也就有了来由。一般式中的分子是点积,同时受方向和长度影响;除以两个长度的乘积,就排除了长度的影响。把 [1,1] 换成 [2,2] 看看:分子从 1 变成 2,第二个向量的长度也从 √2 变成 2√2,所以比值仍是 1/√2。箭头加长了一倍,方向没变,余弦也没变。
3.6.2 方向相反和没有方向,要分清
Section titled “3.6.2 方向相反和没有方向,要分清”刚才把箭头拉长,方向和余弦都没变。那把箭头掉个头,比较结果又会怎样?
例 3.7——把箭头转向另一边。仍取 ,改用 。点积为 1 ×(−1)+ 0 × 0 = −1,两个向量的长度都是 1,所以:
对于非零向量,余弦取值在 −1 到 1 之间:方向相反为 −1,垂直为 0,同向为 1。同向不要求每个坐标都相等,例如 [1,0] 和 [2,0] 长度不同,但余弦仍为 1。
如果 ,它的长度为零,也就谈不上方向了。代入余弦公式会除以零,因此结果未定义,不能当作零。零表示方向垂直,“未定义”则表示这里根本无法计算。本章未知条目对应的嵌入恰好是零向量;如果没有额外约定,就不能计算它的余弦相似度。
表 3.4——固定第一个向量为 [1,0],比较不同方向。
| 第二个向量 | 点积 | 第二个向量的长度 | 余弦 | 说明 |
|---|---|---|---|---|
[1,1] | 1 | √2 | ≈ 0.7071 | 斜向 |
[2,2] | 2 | 2√2 | ≈ 0.7071 | 与上一行同向 |
[-1,0] | −1 | 1 | −1 | 反向 |
[0,1] | 0 | 1 | 0 | 垂直 |
[0,0] | 0 | 0 | 未定义 | 没有方向 |
余弦本身只说明几何关系。要用于语言任务,还得看这套表示是否让几何关系反映了任务关心的信息。假如我们给“批准”和“拒绝”赋同一个向量,余弦就是 1,可这两个词表达的决定可能正好相反。真实模型的向量由训练得到,不是随意赋值的;即便如此,相似度高也不能单独证明两者同义、存在逻辑蕴含关系,或内容符合事实。
3.7 不同场合说的“嵌入”,是同一回事吗?
Section titled “3.7 不同场合说的“嵌入”,是同一回事吗?”我们已经会比较向量的方向,但光看一组数,还不知道它表示什么。把向量用于语言任务之前,要先问:它从哪里来,模型经过怎样的训练,想用它表示什么?不同场合都可能提到“嵌入”,指的却不一定是同一样东西。
Token 的输入嵌入(input embedding),就是模型最初按 ID 查出的那一行数。分词器和嵌入表不变,同一个 ID 即使出现在不同句子里,查出的行也相同。例 3.5 用的就是输入嵌入。
上下文表示(contextual representation)则结合当前可用的周围 Token 和位置信息计算。同一个 Token 换一个句子,结果就可能变。两者不矛盾:查表给出固定的初始值,后面的计算再结合其他输入。关于上下文化表示的研究,考察的正是这些额外信息有什么用。Peters 等,2018
检索嵌入(retrieval embedding)用向量表示查询、段落或其他待搜索的内容,服务于检索任务。生成它的模型和训练目标,可能与语言模型不同。检索索引把这些向量组织起来,方便搜索。因此,本章的输入嵌入表不是文档索引,单靠它也没有说明怎样为整句话算出可用于检索的表示。
这也解释了为什么不能只比较两个 Token 的嵌入行,就判断语言模型的全部能力。这两行数只是计算早期的表示,后面还会结合上下文。单独一个余弦分数,反映不了这些后续计算。
3.8 本章小结
Section titled “3.8 本章小结”文本怎样编码,要看具体规则,并没有统一的“按词切分”办法。词表和规则决定 Token 边界、ID、能处理哪些字符,以及编码后能否还原原文。本章的小词表用已有片段表示了新词 cats,但遇到未知符号,就丢掉了区分它们的信息。换词表既可能改变 Token 数,也要求模型作相应配套。
ID 用于定位条目;嵌入查表按编号取数,不能把编号的大小当作含义的大小。余弦相似度排除长度,只比较非零向量的方向,本身不能判断事实真假。输入嵌入、上下文表示和检索嵌入,也要按各自的计算阶段和用途分清。
我们已经弄清文本怎样进入模型,还没有算过下一 Token 的概率。第 4 章 会用计数搭建一个模型,直接把字符当作 Token,让我们暂时不用实现神经嵌入模型,也能研究怎样估计概率。通用的向量和矩阵计算从第 5 章开始介绍,分词器如何学习则留到第 19 章。
3.9 习题
Section titled “3.9 习题”- 按规则编码。分别用 v1、v2 编码
cats cats,把中间的普通空格明确标出来。写出 ID 和 Token 数,再说明用了几种 Token 类型,哪些重复出现了。 - 大小写与边界。用 v1 编码
Thecat和thecat。前一个字符串没有空格,为什么仍能用到 ID 1?后一个为什么不能? - 能否还原原文?解码器只拿到
[16,0]。如果它是确定性的,为什么不可能保证总能分清原文是cat🙂还是cat🚀? - 扩充词表。第三个版本把完整的
cat🙂加进词表。例 3.4 的结果会怎样变化?神经模型要使用这个新 ID,还需要作哪些调整? - 重新编号。在完整生成器中交换两个词表 ID 时,哪些对应关系要一起改?为什么通常不能只交换分词器里的 ID?
- 长度与方向。分别计算
[1,0]与[3,3]、[1,0]与[-3,-3]的余弦。哪种缩放保持原来的方向,哪种会把方向反过来? - 零与未定义。分别求
[1,0]与[0,1]、[1,0]与[0,0]的点积和长度。第二组的余弦计算究竟在哪一步进行不下去? - 含义与几何。表 3.2 给
cat和The指定了相同的向量。它们的余弦是多少?这能否证明二者同义?经过后续计算,上下文表示还一定相同吗? - 检查表示方法。有人说:“把 Token ID 求平均,就得到了文档嵌入。”请用重新编号说明,这个平均值为什么不能直接代表文档本身的语义。对合适的嵌入向量求平均,又与此有什么不同?为什么后者是否有用也需要论证?
3.10 参考解答
Section titled “3.10 参考解答”- v1 得到
[cat][s][␠][cat][s],ID 为[16,17,15,16,17],共五个 Token;v2 得到[cats][␠][cats],ID 为[18,15,18],共三个 Token。v1 用了 cat、s、空格三种类型,cat 和 s 各出现两次;v2 用了 cats 和空格两种类型,cats 出现两次。 Thecat得到[1,16],thecat得到[0,0,0,16]。匹配规则只要求片段与剩余文本的开头完全相同,不要求前后有空格,所以第一个字符串可以先匹配 The。小写的 t、h、e 没有对应条目,规则也不会自动转换大小写。- 两种原文都编码成相同输入,确定性解码器就只能给出同一个结果。但原文有两种,因此不可能保证都还原正确。要区分它们,必须另外保存信息,或改用能保留这项区别的编码。
- 最长匹配会为
cat🙂选中新增的完整条目,解码时就能还原笑脸。cat🚀如果仍未收入词表,火箭就继续按未知内容处理。模型还要补齐新 ID 对应的输入、输出条目和配套状态;单改分词器,不会自动得到这些数值参数。 - 片段到 ID、ID 到片段、输入嵌入行、输出标签或输出行,以及其余按 Token 编号索引的参数,都要同步调整。否则,一个片段会查出别的片段的向量,或者输出 ID 被还原成错误的文字。
- 对
[3,3],点积是 3,两个长度是 1 和 3√2,余弦为 1/√2。对[-3,-3],点积变为 −3,长度不变,余弦为 −1/√2。乘以正数只改变长度;乘以负数还会反转方向。 - 与
[0,1]比较时,点积为 0,两个长度都是 1,所以余弦为 0。换成[0,0],点积仍为 0,但第二个长度也为 0,最后要算 0/0,无法继续。出问题的是余弦的除法,点积本身仍有定义。 - 两个向量都是非零的
[1,0],余弦为 1,但不能据此说 cat 与 The 同义,因为这些数是人为赋的。后续还会用到周围 Token 和位置,初始向量相同,算出的上下文表示也不一定相同。 - 同步改好各处编号,可以保持模型的预期行为,却改变 ID 的平均值,说明这个平均值受任意编号影响。对合适的嵌入向量求平均,则是在处理数值表示本身,属于另一种方法。但得到的结果是否适合表示文档,仍取决于向量怎样训练、要完成什么检索任务。
3.11 参考文献
Section titled “3.11 参考文献”- Sennrich、Haddow 与 Birch,Neural Machine Translation of Rare Words with Subword Units(2016)。论文用子词处理机器翻译中的低频词,可以进一步了解为什么需要子词。本章的固定最长匹配规则是另行设计的教学例子。
- Kudo 与 Richardson,SentencePiece(2018)。论文介绍从原始文本学习分词的方法,不要求预先分好词。它有助于理解为什么不能把分词一概理解成按空格切分。
- Bengio 等,A Neural Probabilistic Language Model(2003)。模型同时学习词向量与序列概率函数,说明本章查表得到的数值可以怎样从训练中学得。本章自己的示例向量仍是人工指定的。
- Peters 等,Deep Contextualized Word Representations(2018)。论文结合周围句子计算词的表示。可以把它与固定的嵌入表作比较,理解为什么 Token ID 不变,后面的表示仍会变化。