n-gram 之后的重要技术是 NNLM(Neural Network Language Model),由Yoshua Bengio等人在 2003 年提出,原本只是想用神经网络改进语言建模,却意外地发明了词嵌入。
表示问题:从顺序编码到 one-hot#
在深入 NNLM 之前,需要先理解一个更基础的问题:如何让计算机理解词汇?
机器学习发展初期,研究者面临一个根本性问题:计算机只能理解数字,但现实世界中大量的数据是分类别的。比如性别有男 / 女,颜色有红 / 绿 / 蓝 / 黑等等。
最初的想法很简单粗暴:给每个类别分配一个数字。比如性别类中男 = 1、女 = 2,颜色类中红 = 1、绿 = 2、蓝 = 3。但这种顺序编码有个严重问题 —— 它隐含了实体之间的大小关系和距离关系。
举个例子,如果红 = 1、绿 = 2、蓝 = 3,在机器学习中进行距离计算时:
- 红色和绿色之间的距离:|1-2|=1
- 红色和蓝色之间的距离:|1-3|=2
- 绿色和蓝色之间的距离:|2-3|=1
模型会认为红色和绿色更相似,红色和蓝色差异较大。但其实红绿蓝三原色之间应该是等距离关系,没有谁更接近谁。
为了解决这种语义歧义问题,one-hot 编码应运而生。它的核心思想是用向量的正交性表示类别的独立性。还是以红绿蓝为例:
- 红:[1, 0, 0]
- 绿:[0, 1, 0]
- 蓝:[0, 0, 1]
每个类别都是一个正交向量,彼此之间距离相等,没有隐含的大小关系。这种思想对应了线性代数中的基向量概念 —— 每个类别都是高维空间中的一个标准基向量,互相正交且模长相等。
one-hot 的困境#
one-hot 编码解决了顺序编码的问题,但带来了新的困扰:
维度灾难:对于一个拥有 10000 个词的词汇表,使用 one-hot 编码需要 10000 维的向量空间。现实中的词汇表往往更大,编码空间会变得非常庞大。
稀疏性:当维度过大时,整个向量中有效信息特别少。一个 10000 维的向量里只有 1 个位置是 1,其余全是 0,这种稀疏性不利于计算和存储。
语义鸿沟:one-hot 彻底抛弃了实体之间真正的关联关系。比如 "狗" 和 "猫" 都是动物,语义上有相似性,但在 one-hot 编码中它们的距离和 "狗" 与 "汽车" 的距离是一样的。
这些问题在语言建模中尤为突出。无论是 n-gram 的精确匹配方法,还是神经网络中的 one-hot 编码,都无法捕捉词汇间的语义相似性,导致模型泛化能力差。
NNLM 的解决思路#
NNLM 的出现同时解决了两个问题:n-gram 的维度诅咒和 one-hot 编码的局限性。
n-gram 面临的维度诅咒:当测试的词序列在训练数据中从未出现过时,传统方法很难给出合理的概率估计。就像背书一样,只能记住见过的句子模式,遇到新的组合就无能为力。
NNLM 的想法很巧妙:如果一个词序列由语义相似的词组成,即使这个序列从未见过,也应该得到较高的概率。比如说,如果模型见过 "I love cats",那么对于 "I adore dogs" 也应该给出合理的概率,因为 "love" 和 "adore"、"cats" 和 "dogs" 在语义上是相似的。
要实现这种能力,模型需要学习词汇之间的相似性,而这就需要将词汇表示为某种连续的向量空间,而不是 one-hot 那种离散的正交向量。
NNLM 的工作机制#
NNLM 的架构并不复杂,由两层 MLP 组成。它同时学习两个东西:每个词的向量表示,以及基于这些向量的概率预测。
嵌入层:从稀疏到密集的转换#
假设要预测句子中的下一个词,NNLM 先把前面几个词(比如前 3 个词)的 one-hot 向量通过一个嵌入矩阵 C 转换成低维的密集向量:
假设词汇表大小是 V=10000,嵌入维度是 d=300,那么 C 就是一个 300×10000 的矩阵。当我们有一个词的 one-hot 向量(维度是 10000×1,只有第 j 位是 1,其余都是 0)时,矩阵乘法实际上就是取出矩阵 C 的第 j 列,得到一个 300 维的向量。
换句话说,嵌入矩阵 C 的每一列对应词汇表中一个词的向量表示。训练开始时这些向量是随机初始化的,但随着训练的进行,语义相似的词会逐渐在这个 300 维空间中聚集在一起。
拼接层:构建上下文表示#
接下来把这些词向量拼接起来,形成完整的上下文表示:
如果每个词向量是 300 维,拼接 3 个词后,就变成了 900 维的向量。
为什么是简单拼接而不是相加或者其他操作?
拼接保留了词序信息,让模型能够区分 "A B C" 和 "B A C" 这样的不同组合。
隐藏层:非线性特征提取#
拼接好的向量 X 被送入隐藏层进行非线性变换:
这个隐藏层的作用是提取更高级的特征。线性变换将 900 维的输入映射到隐藏层维度(比如 500 维),然后激活函数引入非线性。
为什么需要非线性?
如果没有激活函数,整个网络就是一系列线性变换的组合,等价于一个单层的线性模型,表达能力会大打折扣。tanh 函数能够让模型学习到词汇组合的复杂模式。
输出层:概率分布计算#
最后,隐藏层的输出被映射到词汇表大小的向量,然后通过 softmax 得到概率分布:
这里是 500×10000 的矩阵,将隐藏层的 500 维输出映射到 10000 维(词汇表大小)。函数确保所有词的概率和为 1:
其中是第 i 个词对应的 logit 值(得分)。
参数更新#
训练时使用交叉熵损失函数,目标是最大化正确词的概率。反向传播会同时更新所有参数:,以及最关键的嵌入矩阵 C。
嵌入矩阵 C 的更新是最重要的部分。为了更好地预测下一个词,梯度会 "鼓励" 语义相似的词在向量空间中距离更近。比如,如果 "cat" 和 "dog" 经常出现在相似的上下文中,它们的向量表示就会逐渐靠近。
维度变化的完整流程
整个过程中的维度变化是这样的:
输入:3 个 10000 维的 one-hot 向量
嵌入后:3 个 300 维的密集向量
拼接后:1 个 900 维的向量
隐藏层:1 个 500 维的向量
输出层:1 个 10000 维的概率分布
这样的架构能够学习到语义相似性,是因为模型被迫在有限的嵌入空间中为每个词找到合适的位置,而预测任务会自然地将出现在相似上下文中的词聚集在一起。
意外的收获#
当时 Bengio 他们的主要目标是改进语言建模,但训练完模型后发现,那个嵌入矩阵 C 学到了非常有意思的东西。相似的词确实在向量空间中聚集在一起,而且这些向量还能进行数学运算,比如著名的 king - man + woman ≈ queen。
这个发现一举解决了 one-hot 编码的三个主要问题:
- 维度问题:从 10000 维降到几百维
- 稀疏性问题:变成了密集向量
- 语义鸿沟问题:相似词在空间中距离相近
虽然当时还没有 "词嵌入" 这个专门术语,但这个想法很快就被其他研究者注意到了。2008 年,Collobert 和 Weston 证明了预训练词向量在下游任务中的威力。到了 2013 年,Mikolov 等人发布了 Word2Vec 工具包,专门用来学习词向量,这时候词嵌入技术才真正普及开来。
Word2Vec 简化了 NNLM 的架构,提出了两种模型:CBOW(根据上下文预测目标词)和 Skip-gram(根据目标词预测上下文)。Skip-gram 在小数据集和稀有词上表现更好,CBOW 训练更快,在频繁词上效果更佳。
NNLM 的优势和局限#
相比 n-gram,NNLM 最大的优势就是泛化能力。在 Bengio 他们的实验中,NNLM 在两个文本语料库上都显著超越了当时最先进的 trigram 模型。而且词嵌入这个 "副产品" 后来证明价值巨大。
但 NNLM 也有不少问题。
首先是计算复杂度,训练神经网络比简单的 n-gram 计数要慢得多。其次,虽然不再受马尔可夫假设限制,但还是只能看固定长度的历史,无法处理任意长的上下文。另外,每个词只有一个固定的向量表示,处理不了多义词的问题。
还有一个实际的问题是在大词表上计算 softmax 很慢。
这些问题启发了后续的研究。
一些思考#
从表示学习的角度看,NNLM 证明了:好的表示是解决问题的关键。从顺序编码到 one-hot 再到词嵌入,每一步都在优化数据的表示方式,而表示的改进往往能带来性能的显著提升。
技术的进步是渐进的,每一代都在前一代的基础上解决问题。顺序编码解决了基本的数值化问题,one-hot 解决了语义歧义问题,词嵌入解决了稀疏性和语义相似性问题。并且,有时结果可能不是那么重要,反而是过程才是最重要的,就像由 NNLM 衍生的Word2Vec一样。