无论是哑编码还是序号化,它们本身都存在一个根本性的缺陷,那就是无法表达词与词之间的语义关系。在这些表示方法中,不同词的向量通常是正交的(如哑编码),或者其 ID 大小关系是随机的,导致模型无法理解“国王”与“女王”的语义比“国王”与“苹果”更近。为了解决这个问题,分布式表示(Distributed Representation)被提出,目的是将词语映射到一个低维、稠密、且蕴含丰富语义信息的连续向量空间中。理想中的词向量需要同时满足语义蕴含和低维稠密两个主要目标。语义蕴含要求向量之间的距离能够度量词语之间的语义相似度,这背后的原理就是分布式假设的朴素应用,也就是说如果两个词经常在相似的上下文中共同出现,那么它们的向量在空间上应该是彼此靠近的。例如,“被子”和"床铺"经常一起出现,它们的向量就应该接近;而"椰子"和"企鹅"则应该相互远离。而低维稠密则是为了摆脱维度灾难。词向量的维度应该是一个较小的、可控的超参数,而不是动辄数万的词典大小。并且,向量中的每一维都应是有意义的浮点数,而非绝大部分为 0 的稀疏表示。
为实现这一目标,研究者们探索了不同的技术路径。其中一条是基于全局文档统计的主题模型。另一条是后来居上、并成为主流的、基于局部上下文预测的神经网络模型。作为早期探索的代表,主题模型利用全局统计初步实现了语义捕捉,而随后的 Word2Vec 则通过全新的局部预测范式,真正释放了分布式表示的强大威力。
主题模型是基于机器学习和传统数学思想的经典方法。它尝试从宏观的视角,通过分析大量文档的词语共现统计,来发现词语间的潜在语义关联。它的关键假设是一篇文档由多个“主题”按一定比例混合而成,而一个主题又由多个“词语”按一定概率组成。词语之所以会一同出现在某篇文档中,是因为它们都在共同描述这篇文章所包含的某个或某些潜在主题。例如,一篇关于"人工智能"的文档,会高频出现"深度学习"、"Transformer"、"注意力机制"等词。正是因为这些词都强关联于“AI技术”这个主题,它们才频繁地共现在一起。所以,一个词的向量,就可以用它与各个主题的关联强度来表示。而这其中最核心的技术就是矩阵分解(Matrix Factorization)。
如图 2-2 所示,该方法将获取词向量的过程,巧妙地转化成了一个矩阵分解问题。具体步骤如下:
(1)构建“词-文档”矩阵:首先,我们需要以整个语料库为基础,构建一个巨大的词-文档矩阵
(2)矩阵分解:从线性代数的角度看,这个巨大的稀疏矩阵
其中:
-
$X_{m \times n}$ 是原始的词-文档矩阵,$m$ 是词典大小,$n$ 是文档数量。 -
$k$ 是一个远小于$m$ 和$n$ 的超参数,代表期望发现的潜在主题数量。 -
$W_{topic}$ 表示 “词-主题矩阵”。它的每一行,都是一个$k$ 维的稠密向量,表示一个词与$k$ 个主题的关联度。 -
$H_{topic}$ 表示 “文档-主题矩阵”。它的每一列,都是一个$k$ 维的稠密向量,表示一篇文档在$k$ 个主题上的分布。
(3)获取词向量:分解完成后,我们真正关心的是 “词-主题”矩阵
从机器学习的角度看,主题模型本质上是一个聚类算法。其中,文档主题矩阵
尽管主题模型(如其更广为人知的名字 LSA, Latent Semantic Analysis 1)通过对全局的"词-文档"共现矩阵进行分解,成功地将词语映射到了一个低维的"主题空间",得到了能够表达语义的稠密词向量,但它也存在明显的局限性。比如对一个大型语料库进行 SVD 分解,计算量和内存开销都极大,导致计算代价高昂;其次,它依赖的是全局的、粗粒度的文档级别共现信息,忽略了词语在句子中的局部上下文和词序信息,使得它难以捕捉更精细的语义关系;而且这种“先统计,再分解”的流程,很难与现代的深度学习模型进行端到端的联合训练,难以集成。
与主题模型从全局文档统计中挖掘主题不同,由 Google 在 2013 年提出的 Word2Vec 算法 2,将视角聚焦于词语的局部上下文。它的思想来源于语言学中的分布式假设,即一个词的含义,由其上下文中的词语所决定 3。换言之,如果两个词的上下文经常是相似的,那么这两个词的语义就是相近的。Word2Vec 正是这一思想的数学实现。
Word2Vec 通常被认为是一种浅层神经网络模型(Shallow Neural Network)。它的“浅层”体现在网络结构的简单性上,因为它移除了传统神经概率语言模型(NNLM)中计算昂贵的非线性隐藏层,直接将投影层与输出层相连。这种简洁的设计使 Word2Vec 的计算非常高效,能够在大规模语料库上进行训练。理解 Word2Vec 的关键在于区分它的最终目标和实现手段,神经网络结构本身只是获取词向量的一种方式,并非模型的最终目的。
Word2Vec 的最终目标是获取一个高质量的词向量查询表,本质上是一个巨大的矩阵
从数学上看,将一个单词的 ID 转换为其稠密向量的过程,在概念上可以分解为三步。首先输入一个代表单词的 ID(例如 3);接着进行哑编码,将 ID 3 转换为一个维度等于词典大小
$$ \begin{bmatrix} 0 & 0 & 0 & \color{#42b983}{1} & 0 & 0 \end{bmatrix} \times \begin{bmatrix} 2 & 8 & 5 & 3 & 1 \ 9 & 4 & 7 & 2 & 6 \ 3 & 1 & 8 & 5 & 0 \ 5 & 6 & 2 & 9 & 4 \ 8 & 0 & 3 & 7 & 2 \ 4 & 2 & 9 & 6 & 1 \end{bmatrix}
\begin{bmatrix} \color{#42b983}{5} & \color{#42b983}{6} & \color{#42b983}{2} & \color{#42b983}{9} & \color{#42b983}{4} \end{bmatrix} \tag{2.11} $$
在实践中,为了极大地提升效率,程序并不会真的执行稀疏的矩阵乘法,而是直接实现一个 查询 操作:根据输入的单词 ID,直接从
以 PyTorch 为例,它的
nn.Embedding层本质上就是维护了这个$W_{in}$ 矩阵(词向量查找表)。当我们在后续章节中搭建模型时,第一层通常都是 Embedding 层。它接收输入序列的整数 ID,直接通过查表将其映射为稠密的词向量,而这个矩阵的参数会随着整个模型的训练(反向传播)而被自动更新和学习。
Word2Vec 包含 CBOW 和 Skip-gram 两种具体的实现模型。两者在任务设计上恰好相反,但最终都实现了相同的目标,即通过训练过程得到一个高质量的词向量查询表。
(1)CBOW 模型详解
如图 2-3,CBOW(Continuous Bag-of-Words)的任务是 “根据上下文预测中心词”。
在数学公式层面,最开始要进行的是词向量转换,对于上下文中的每个词
其中:
-
$u_c$ 是目标中心词的输出向量 -
$h$ 是上下文向量
为了直观理解主要流程,我们以一个具体的例子为例。假设批大小 [[1, 8, ...], [8, 5, ...]],代表两个句子片段的上下文词 ID。第二步词向量转换,通过查表($W_{in}$ 矩阵,大小
(2)Skip-gram 模型详解
与 CBOW 恰好相反,Skip-gram 的任务是 “根据中心词预测上下文”。在具体实现上,它将一个预测任务,分解成了多个独立的子任务。
Skip-gram 与 N-gram 的关系
- 名字渊源:Skip-gram 这个名字确实源于传统的 k-skip-n-gram 模型(允许跳过中间词的 N-gram)。
- 核心区别:虽然借用了“跳跃”的思想,但 Word2Vec 的 Skip-gram 是一种预测模型,而非统计模型。它并不是为了“修复”N-gram,而是为了更高效地学习稠密词向量。它通过“用中心词预测上下文”这一任务,强迫模型学习到词语的语义特征,从而彻底解决了传统 N-gram 面临的稀疏性和维度灾难问题。
同样从公式来看,开始的词向量转换对于中心词
其中:
-
$v_c$ 是中心词的输入向量 -
$u_{c-m+j}$ 是上下文词的输出向量
同样以具体数值为例,假设批大小
Skip-gram 为每个"中心词-上下文词"对都创建了一个独立的学习任务,这使得它能够更好地学习到词与词之间更精细的关系。在处理低频词和大数据集时,通常能得到质量更高的词向量,但由于其任务量是 CBOW 的
$S$ 倍,训练速度相对较慢。
在了解了模型的基本构造后,可以深入探讨 Word2Vec 是如何真正捕捉到语义的。以 CBOW 模型为例,其关键在于滑动窗口机制如何生成大量高度重叠的训练样本。假设有一个很长的句子,并设窗口大小为
面对这两个拥有几乎相同上下文却对应不同目标词的样本,模型的目标看似矛盾,因为既要调整参数使第一个样本的上下文向量成功预测出
两个向量的点积是余弦相似度公式的分子部分。所以,最大化这个点积得分,在几何上就是在促使上下文向量
尽管 Word2Vec 是里程碑式的算法,但存在一个根本性的局限性。它产生的是静态词向量,具体表现在以下两个方面:
(1)上下文无关:对于词典中的任意一个词,Word2Vec 只会生成一个固定的向量表示。这个向量是在整个语料库上训练得到的“平均”语义,与该词出现的具体上下文无关。这直接导致了 Word2Vec 无法解决一词多义的问题。例如,“小米”这个词,无论是在“农民伯伯正在收割小米”的语境中,还是在“小米公司发布了新手机”的语境中,Word2Vec 赋予它的词向量都是完全相同的。
(2)静态的本质:Word2Vec 的输出是一个巨大的查询表。训练完成后,这个表就固定下来了。在使用时,只是根据单词 ID 去查找对应的行向量,整个过程不涉及对上下文的动态分析。
要做哦,别偷懒 😘
- 根据已经学过的内容使用
20newsgroups数据(from sklearn.datasets import fetch_20newsgroups)实现基于全连接的文本分类模型训练和推理代码(若自行实现困难,可以参考文本分类简单实现)
Footnotes
-
Deerwester, S., Dumais, S. T., Furnas, G. W., Landauer, T. K., & Harshman, R. (1990). Indexing by latent semantic analysis. Journal of the American Society for Information Science, 41(6), 391-407 ↩
-
Mikolov, T., Chen, K., Corrado, G., & Dean, J. (2013). Efficient Estimation of Word Representations in Vector Space. arXiv:1301.3781 ↩
-
Harris, Z. S. (1954). Distributional structure. Word, 10(2-3), 146-162 ↩
-
Mikolov, T., Sutskever, I., Chen, K., Corrado, G., & Dean, J. (2013). Distributed Representations of Words and Phrases and their Compositionality. arXiv:1310.4546 ↩
