Skip to main content

第 14 课:自然语言处理 与 词嵌入

(Natural Language Processing and Word Embeddings)

(一)词嵌入

(1)词嵌入的概念

我们之前讲到用一个 one-hot 向量 来表示一个词。
比如如果 man在词典里是第 5391个,那么对应的零向量只在第 5391处为 1,表示为 O5391O_{5391},这里的 O 表示“oneshot”

oneshot 的缺点就是它把每个词孤立起来,这样使得算法对相关词的泛化能力不强。比如不能从apple 类比到 orange
举个例子,假如一个训练好的语言模型已经学到了 “I want a glass of orange juice”
但如果看到 “I want a glass of apple ___”,
因为算法不知道 apple和 orange的关系很接近,所以算法很难得到 apple juice。
这是因为任何两个 one-hot向量的内积都是 0 ,也就是任何两个 one-hot向量 之间都没有关系

如果我们不用one-hot表示,而是用 特征向量 来表示每个词,这就是 词嵌入(word embedding)

下图中,我们列出了各个词 对应各种性质的 数值

比如假定男性的性别为 -1,女性的性别为 +1,
那么 man的性别值可能就是 -1,而 woman就是 -1, king就是 -0.95 queen是 +0.97
apple和 orange没有性别可言。

除了性别,还有很多的特征,比如是否是实物,年龄,尺寸等等

image-20250619223748162

我们假设有300个不同的特征,而 man在词典里是第 5391个,就可以用一个 300维的向量来表示,记为 e5391e_{5391}

由于 apple和 orange的大部分特征实际上都一样,因此算法可以从 orange juice类比到 apple juice, 也就是泛化能力更强

(2)t-SNE算法

由于我们知道一个词根据其各种特征嵌入在 300维空间的某个位置
t-SNE算法 可以把这个 300维空间 映射到 2维 ,便于观察。这种映射很复杂而且很非线性。
特征相近的词在300维空间中会 在某种程度上更靠近

(3)词嵌入的使用

在RNN网络以及其他网络中,只需要把输入从 one-hot向量 变成 词嵌入 即可
如果你只有一个很小的标记的训练集,但是有一个已经学好的词嵌入,遇到未知词时,它可以可以给你其 词嵌入,从而正常工作
这是因为词嵌入的算法会考察 非常大的无标签文本的训练集,可以是 1亿个单词,甚至达到 100亿。
同时 300维的词嵌入 特征向量 比原来的 10000维的 one-hot向量 更好算 当你的任务的训练集相对较小时,词嵌入的作用最明显,所以它广泛用于NLP领域
而词嵌入在语言模型、机器翻译领域用的少一些,因为这些任务有大量的数据。

在其他的迁移学习情形中也一样,如果你从某一任务 A迁移到某个任务 B,只有 A中有大量数据,而 B中数据少时,迁移的过程才有用

总体流程如下:

  1. 先从非常大的文本集中学习词嵌入。或者下载网上预训练好的词嵌入模型
  2. 用这些 词嵌入模型 把它迁移到 你的只有少量标注训练集的任务 中,比如说 用300维的词嵌入来表示单词。
  3. 在新任务(比如人名识别)上训练模型时,选择要不要微调,用调整过的 词嵌入。
    如果你标记的数据集不是很大,不需要微调词嵌入

注意:
人脸识别中的算法和词嵌入有点像,都是把一个东西进行编码,因此术语编码( encoding)和嵌入 (embedding)可以互换,
但是人脸识别中的算法可能涉及到海量的人脸照片,而NLP只有一个固定的词汇表,没有出现过的单词就记为未知单词。

嵌入矩阵

如下图所示,左边的嵌入矩阵E 的尺寸是 300×10000 ,右边的one-hot矩阵 O6257O_{6257} 尺寸是 10000×1
两者相乘得到一个 300×1 的矩阵 e6257e_{6257}
假如说有某个单词w,那么 ewe_w 就代表单词 w 的嵌入向量

image-20250619223809068

注意:
因为 one-hot向量是一个维度非常高的向量,并且几乎所有元素都是 0,所以矩阵和向量相乘效率低下,
所以在实践中会使用一个专门的函数来查找矩阵 𝐸的某列,而不是用通常的矩阵乘法来做,
例如在 Keras中就有一个嵌入层,然后我们用这个嵌入层更有效地从嵌入矩阵中提取出你需要的列

(4)词嵌入进行类比推理

词嵌入还可以用于类比推理, 比如 man如果对应 woman,这种算法可以推断出 king应该对应的是 queen。

具体实现如下:
假设你用的是四维的嵌入向量,对两个向量进行减法运算,即

emanewoman=[10.010.030.09][10.020.020.01][20.010.010.08][2000]e_{\text{man}} - e_{\text{woman}} = \begin{bmatrix} -1 \\ 0.01 \\ 0.03 \\ 0.09 \end{bmatrix} - \begin{bmatrix} 1 \\ 0.02 \\ 0.02 \\ 0.01 \end{bmatrix} \approx \begin{bmatrix} -2 \\ -0.01 \\ 0.01 \\ 0.08 \end{bmatrix} \approx \begin{bmatrix} -2 \\ 0 \\ 0 \\ 0 \end{bmatrix} ekingequeen=[0.950.930.700.02][0.970.950.690.01][1.920.020.010.01][2000]e_{\text{king}} - e_{\text{queen}} = \begin{bmatrix} -0.95 \\ 0.93 \\ 0.70 \\ 0.02 \end{bmatrix} - \begin{bmatrix} 0.97 \\ 0.95 \\ 0.69 \\ 0.01 \end{bmatrix} \approx \begin{bmatrix} -1.92 \\ -0.02 \\ 0.01 \\ 0.01 \end{bmatrix} \approx \begin{bmatrix} -2 \\ 0 \\ 0 \\ 0 \end{bmatrix}

我们可以发现 man和 woman主要的差异是 性别,而 king和 queen之间的主要差异也是 性别
因此:

emanewomanekingequeene_{\text{man}} - e_{\text{woman}} \approx e_{\text{king}} - e_{\text{queen}}

由此我们不难知道,这种类比推理其实就是找到一个词满足下面的等式

emanewomanekinge?e_{\text{man}} - e_{\text{woman}} \approx e_{\text{king}} - e_{\text{?}}

换而言之,你需要的就是找到单词 w 来最大化 ewe_{w}ekingeman+ewomane_{\text{king}}-e_{\text{man}} + e_{\text{woman}} 的相似度,即

Find word w:argmaxSim(ew,ekingeman+ewoman)\text{Find word } w: \arg\max \text{Sim}(e_w, e_{\text{king}} - e_{\text{man}} + e_{\text{woman}})

其中SIM为 余弦相似度函数,因为该式是 𝑢和 𝑣的夹角的余弦值
所以夹角为 0 度时,余弦相似度就是 1,夹角为 90 度时, 余弦相似度就是 0

sim(u,v)=uTvu2v2=cos(θ)\text{sim}(u, v) = \frac{u^T v}{\|u\|_2 \|v\|_2} = cos(\theta)

注:这种类比推理的准确度在 30%-75%

(5)词嵌入 的学习

我们在之前讲到了 词嵌入模型 的使用方法,这里讲讲 词嵌入模型 的生成/训练方法

建立一个语言模型是学习词嵌入的好方法
假如你在用 神经网络 来构建一个语言模型。
在训练过程中,你可能想要你的神经网络能够做到输入 :“I want a glass of orange ___.”,然后预测下一个词。

image-20250619223832487

训练过程如下:

  1. 从第一个词 I开始,建立一个 10000 维的 one-hot向量 𝑂4343𝑂_{4343} 表示单词 "I"
  2. 生成一个参数矩阵 𝐸,𝐸乘以 𝑂4343𝑂_{4343},得到嵌入向量 e4343e_{4343}
  3. 对其他的词也做相同的操作,得到 e9665e_{9665} 等等
  4. 现在你有许多300维的嵌入向量。把它们全部放进神经网络中,最后 softmax层得到 10000个可能的输出预测
    神经网络输入向量尺寸为 6×300=1800维 ,这是 6个嵌入向量 堆在一起得到的
    这个隐藏层的参数记为 𝑊[1]和 𝑏[1],softmax层 的参数记为 𝑊[2]和 𝑏[2]。

假如说在训练集中有 juice这个词,训练过程中 softmax 的目标就是预测出单词 juice,,我们把它叫做目标词
在这个算法的激励下, apple和 orange会学到很相似的嵌入,因为神经网络有时看到的是 orange juice,有时看到的是 apple juice。
因此算法想要最好地拟合训练集,就要使 apple、orange、grape 和 pear 梨水果都拥有相似的特征向量,这就是学习的过程

注意:

  1. 上面我们使用了 6 个词,这个数随句子长度而变化,然而实际上使用几个词是可以固定的,称为 “固定的历史窗口”
    举个例子,我们可以一直使用一个 4个词 的历史窗口,因此神经网络的输入尺寸始终是 1200维,这样预测出来的词只和这 4 个词有关
    就意味着你可以处理任意长度的句子,因为输入的维度固定是 1200维
    这么做的原因是 我们现在的目的不是训练一个句子生成模型,而是在训练 词与词之间的关系,也就是训练 嵌入矩阵E

  2. 在1.中我们用 4 个词来预测输出的词,这 4 个词称为“上下文”,
    上下文有很多种,可以是前面几个词,前后几个词,前面一个词,甚至是附近隔开的一个词(Skip-Gram模型)。
    不同的上下文种类 对应 不同的学习目的
    如果目标是建立一个语言模型,选取目标词之前的几个词作为上下文。
    如果目标是学习词嵌入,可以用其他类型的上下文

  3. 不同的位置上的不同单词 用的都是同一个矩阵 𝐸, 所以这个模型的参数就是矩阵 𝐸 和 神经网络的权重



(二)Word2Vec 算法 训练嵌入矩阵

(1)Word2Vec 算法的基本概念

在上一讲中我们使用 神经网络 来训练 嵌入矩阵E, 而 Word2Vec算法 提供了一种更高效的办法

假设在训练集中给定了一个这样的句子:“I want a glass of orange juice to go along with my cereal.”,
在 Skip-Gram 模型中,我们要随机在一定词距内选一个词 作为上下文词,比如在前后 5个词内,记为 c
再用同样的方法,随机选出一个 目标词 , 记为 t
于是我们要用这个 上下文词c 去预测这个 目标词t, 显然准确率不行
但是这里我们的目标并不是 解决这个监督学习问题本身,而是想要学到一个好的词嵌入模型。

我们要解决的基本的监督学习问题是学习一种从 上下文词 到 某个目标词 的映射关系,
假设 第 6257个单词 orange 是上下文词c , 第 4834个 juice是 目标词t

首先还是 ec=EOce_c = E * O_c, ,得到了输入的上下文词的嵌入向量, 然后把 ece_c 输入 softmax层, 预测不同目标词的概率

Softmax: p(tc)=eθtTecj=110,000eθjTec\text{Softmax: } p(t \mid c) = \frac{e^{\theta_t^T e_c}}{\sum_{j=1}^{10,000} e^{\theta_j^T e_c}}

其中 θt\theta_t 是一个与输出t有关的参数,即某个词 t 和标签相符的概率是多少

最后的损失函数是:

L(y^,y)=i=110,000yilogy^iL(\hat{y}, y) = - \sum_{i=1}^{10,000} y_i \log \hat{y}_i

最后通过损失函数来 优化嵌入矩阵E中的和softmax层的 参数,这大体上就是一个可以找到词嵌入的简化模型和神经网络,
由于这里的 目标词t 和 上下文词c 是随机跳跃选取的,这就是 skip-gram模型

注意:

  1. 在采样 上下文词c 时, 如果对语料库均匀随机地采样,那么有一些词像the、 of会出现得过于频繁,而少见的单词没有得到充分学习
    因此实际上词 𝑝(𝑐)的分布 并不是均匀且随机的,而是采用了不同的分级来平衡更 常见的词 和 不常见的词。
  2. 这里省略了softmax的偏差项,可以加上
  3. y,y^y,\hat y 都是 one-hat 向量。 y^\hat y 包含10000个词的各自概率, yy 是只有一个1的10000维零向量

然而我们注意到softmax层的分母要进行求和计算,这个过程是相当慢的
这里有一些解决方案,如 分级(hierarchical) 的 softmax分类器和 负采样 Negative Sampling)

(2)分级的softmax分类器

分级的softmax分类器 会告诉你目标词是在词汇表的前 5000个中还是在词汇表的后 5000个词中,
假如在前 5000个词中,第二个分类器会告诉你 在前 2500个词中 或者 2500~5000个词中,诸如此类,直到找到词
这形成一个树形的分类器,树上的每一个节点都可以是一个二分类器,比如逻辑回归分类器,
因此你不需要对所有的 10,000个词求和了,最后计算成本 与 词汇表大小的对数 成正比

image-20250619223902712

在实践中分级softmax分类器不会使用一棵 左边和右边分支的词数相同的 对称树
实际上,常用词 会在顶部,然而不常用的词会在树的更深处
因为常见的词会更频繁,所以这样只需要少量检索就可以获得 常用单词
然而 更少见的词 就更合适在树的较深处,因为一般不需要到那样的深处,

(3)负采样

( Negative Sampling)

我们之前讲到给定一对单词,比如 orange和 juice 作为 上下文词 和 目标词,用上下文词去预测目标值
而 负采样算法 则是 预测这是否是一对 上下文词 -目标词( context-target)

在这个例子中,orange和 juice就是个正样本,记为 1
那么 orange和 king就是个负样本,记为 0。

正样本跟上个视频中生成的方式一模一样:
先抽取一个上下文词,在一定词距内内选一个目标词,这两个词组合成为正样本。

负样本生成方式有点不同:
首先使用用相同的上下文词,再在字典中随机选一个目标词,注意没有词距限制,完全随机
由于是随机的,认为 该目标词 跟 上下文词没关联,于是 这两个词组合成为负样本。
注意:即使选中的目标词 恰好就在上下文词前面,这个组合也要被标记为负样本

image-20250619223922530

选好一对正样本和 K对负样本 后,接下来我们将构造一个监督学习问题
输入: 上下文词 和 目标词 的词对
输出: 标签 1 或 0 (是否是正样本)

下面我们讲讲学习从 输入 映射到 输出 的监督学习模型, 首先我们知道 softmax层 还是如下

Softmax: p(tc)=eθtTecj=110,000eθjTec\text{Softmax: } p(t \mid c) = \frac{e^{\theta_t^T e_c}}{\sum_{j=1}^{10,000} e^{\theta_j^T e_c}}

我们要做的就是定义一个逻辑回归模型,给定输入的 𝑐 𝑡对的条件下, 𝑦=1的概率,即:

P(y=1c,t)=σ(θtTec)P(y = 1 \mid c, t) = \sigma(\theta_t^T e_c)

和之前一样,你对每一个可能的目标词有一个参数向量 θt\theta_t和 上下文词的嵌入向量 ece_c, 我们将用这个公式估计 𝑦=1的概率。
每一个正样本 都有 𝐾个对应的负样本来训练

整个训练过程

  1. 选择一个训练样本(一个词及其上下文)。
  2. 选择若干负样本。
  3. 使用sigmoid函数更新词向量,以区分正样本和负样本。

这样每次更新只需处理少量的负样本(通常是5到20个),而不是整个词汇表。
也就是不使用一个巨大的 10000维度的 softmax,而是把它转变为 10000个二分类问题,
每次迭代我们要做的只是训练它们其中的 𝐾+1 个,其中 𝐾个负样本和 1个正样本。这也是为什么这个算法计算成本更低

注意:

  1. 数据集很小, 𝐾取 520。数据集很大,𝐾就选的小一点。对于更大的数据集 𝐾取 25。这个例子中 𝐾=4。
  2. 在选择负样本的目标词时, 直接通过 词出现的频率(经验频率)采样 或者是直接 均匀且随机地采样 效果都不好
    采用下面这个采样方式的效果最好,它位于这两个极端的采样方法之间,既不用经验频率(实际观察到的英文文本的分布),也不用均匀分布:
P(wi)=f(wi)34j=110,000f(wj)34P(w_i) = \frac{f(w_i)^{\frac{3}{4}}}{\sum_{j=1}^{10,000} f(w_j)^{\frac{3}{4}}}

如果 f(wi)f(w_i) 是语料库中某个英文词的词频,通过 3/4次方的计算,使其处于完全独立的分布和训练集的观测分布两个极端之间 这是一个经验公式,没有理论依据。



(三)GloVe 算法训练嵌入矩阵

这个算法相比于Word2Vec和 SKip-gram 更加简单

GloVe代表用词表示的全局变量( global vectors for word representation)

假定 XijX_{ij} 是 单词 𝑖 在 单词 𝑗 上下文中出现的次数,遍历你的训练集,然后数出单词 𝑖在不同单词 𝑗上下文中出现的个数
如果你将 目标词的范围 限制在上下文词的左右各 10词以内的话,那么就会有一种对称关系。Xij=XjiX_{ij} =X_{ji}

在 GloVe算法 中,我们可以定义上下文和目标词为任意两个位置相近的单词,
假设是左右各10词的距离,那么 XijX_{ij} 就是一个能够获取单词 𝑖 和单词 𝑗 出现 位置相近 时的频率的计数器。
然后我们将他们之间的差距进行最小化处理:

minimizei=110,000j=110,000f(Xij)(θiTej+bi+bjlogXij)2\text{minimize} \sum_{i=1}^{10,000} \sum_{j=1}^{10,000} f(X_{ij}) \left(\theta_i^T e_j + b_i + b_j' - \log X_{ij}\right)^2

注意:

  1. 对于其中的 θiTej\theta_i^T e_j ,由于 𝑖 和 𝑗 与 t 和 𝑐 的功能一样,因此就类似于之前的 θtTec\theta_t^T e_c
    也就是告诉你这两个单词之间有多少联系, 𝑡和 𝑐之间有多紧密, 𝑖和 𝑗之间联系程度如何,换句话说就是他们同时出现的频率是多少
  2. 如果 Xij=0X_ij =0 , log0 无意义,所以要加上一个 f(Xij)f(X_ij), 用来处理当 Xij=0X_ij =0时,约定 0log0=0
    因此 Xij=0X_ij =0 时,舍弃这一项而不求和,所以这个求和公式 求的和 仅是 连续出现至少一次的词对 的和。
  3. f(Xij)f(X_ij) 的另一个作用是 作为一个加权因子, 给一些不常用的词 不会太小的权值,使其被考虑在内,但又不像那些常用词这样频繁。
    同时也能够给像 is、a 这样的频繁词 更大但不至于过分的权重。
  4. 𝜃和 𝑒 在这个特定的公式里 是完全对称的,功能也一样,所以 𝜃𝑖 和 𝑒𝑗 也是对称的。
    因此可以一致地初始化 𝜃和 𝑒,然后使用梯度下降来最小化输出,当每个词都处理完之后取平均值
  5. 我们之前用来举例的特征比如“性别”,“是否是食物”,都是可以被人类理解的,
    但是使用上面的任何一种算法 来学习一个词嵌入时,不能保证嵌入向量的独立组成部分是能够理解的
    具体而言,第一个特征可能是个 Gender、 Roya、 Age、 Food Cost和 Size的组合


(四)情感分类

情感分类任务就是看一段文本,然后分辨这个人是否喜欢他们在讨论的这个东西

image-20250619224011163

基本结构如上图,和我们之前学过的算法没什么区别,
把得到的 嵌入矩阵e 都进行求和或者平均计算,然后输入softmax层,输出 5 个可能结果的概率值,从一星到五星

这个算法有一个问题就是没考虑词序,比如 "Completely lacking good taste, good service, and good ambiance.",
good这个词出现了很多次,如果你用的算法跟这个一样,忽略词序,很可能认为这是 一个好的评论,

因此可以使用如下方式:

image-20250619223956896



(五) 词嵌入除偏

( Debiasing Word Embeddings)

由于常见训练集的问题,我们的嵌入矩阵可能有一些不正确的 词与词关联,也就是偏见
比如 “医生” 对应 “男人”,“护士” 对应 “女人”
这里讲怎么消除这种偏见

image-20250619224047587

首先我们要做的事就是辨别出我们想要减少或想要消除的特定偏见的趋势。这里以性别歧视为例

步骤如下:

  1. 选择本来就应该 性别不同 的词,计算 eheesheemaleefemalee_{he}-e_{she}、e_{male}-e_{female} 等,然后将这些差值取平均,由此我们得到了无偏见基准线
    图中横轴就是性别趋势或者偏见趋势,有1维 (实际这个偏见趋势可以比 1维更高)
    图中竖轴与性别不相干,是无偏见趋势,有299维。
  2. 把存在偏见的词移动到 无偏见趋势 的轴上
  3. 把本来就存在性别定义的一对词 移至与中间轴线等距的一对点上。这样可以使他们到 如doctor这类中心词 的距离相同

注意:

  1. 实际使用中第一步其实不是取平均,而是使用SVU算法(奇异值分解)
  2. 你需要平衡的词对的数实际上是很小的,至少对于性别歧视这个例子来说,用手都能够数出来你需要平衡的大部分词对。