从编号到向量
编号只是区分不同词的标签,数字大小不携带语义。怎么让向量本身携带词的语义?
编号的大小没有意义
经过分词和查词表,一句话已经变成了一串整数 ID。但这串整数还不能直接拿来表达语义, 因为 ID 的数值本身没有任何含义 。
举个例子。假设词表里 猫 = 5 、 桌子 = 6 、 狗 = 8 。单看数字,6 离 5 更近、8 离 5 更远, 于是好像「桌子」比「狗」更接近「猫」?这显然荒谬。 编号只是用来 区分不同词 的标签, 它的大小、它们之间的差值, 都不代表词义上的远近 。
我们真正想要的,是一种 本身就携带语义 的数字表示: 意思相近的词,数字也应该相近。怎么做到?先从一个最直接的尝试说起。
第一种尝试:独热编码
还记得在 Softmax 回归 里给分类标签用过的 独热编码(one-hot) 吗?这里把同一个工具迁移到词表上。
做法是:设词表大小为 , 每个 token 用一个 长度为 、只有自己那一位是 1、其余全是 0 的向量表示。词表里第几个词,就在第几位标 1。
概念是旧的,但「拿它来表示词」是新的尝试。这种表示能不能达到我们的目标, 让意思相近的词,向量也相近?要检验这一点,得先说清楚两个向量的「相近」, 究竟怎么衡量。
怎么衡量「相近」:余弦相似度
「意思相近的词,向量也相近」这条要求要能成立,「相近」就不能停留在直觉上, 得有一个可以计算的定义。最常用的标尺是 余弦相似度(cosine similarity) ,它看的是两个向量 方向 的接近程度,定义为:
分子是两个向量的 点积 (对应位置相乘再求和), 分母是各自的 模长 (向量的长度)相乘。 结果落在 之间: 越接近 1,两个向量方向越一致、越「相似」 ; 接近 0 表示方向无关;接近 表示方向相反。 这样,「意思相近」就有了可计算的对应物,也就是余弦相似度的高低。 后面查词的「最近邻」、做词向量的「类比」,靠的都是它。 带着这把标尺回头检验 one-hot,它的问题就清楚了。
独热编码的三个硬伤
动手验证「正交」
第二个硬伤最致命,因为它直接否定了 one-hot 表达语义的能力。 下面用代码验证:同一个词和自己的余弦相似度是 1, 而任意两个不同的词之间都是 0。
我们真正想要的向量
把上面的问题反过来,就描出了理想表示该有的样子:
- 稠密 :用几百维(而不是几万维)的实数向量,每一维都用上,不再稀疏;
- 可学习 :向量里的数值不是人工设定,而是模型自己学出来的;
- 带语义 :意思相近的词,向量在空间中也彼此靠近,余弦相似度不再恒为 0。
这样的向量叫做 词向量 ,得到它的方法叫 词嵌入 。 可这样的向量从哪来?能不能让模型在训练里自己把它学出来?这正是下一步要回答的。