深度学习-NLP(2)Word2vec*

一、Word2Vec

word2vec 相比之前的 Word Embedding 方法好在什么地方?

  • 极快的训练速度。以前的语言模型优化的目标是MLE,只能说词向量是其副产品。Mikolov应该是第一个提出抛弃MLE(和困惑度)指标,就是要学习一个好的词嵌入。如果不追求MLE,模型就可以大幅简化,去除隐藏层。再利用HSoftmax以及负采样的加速方法,可以使得训练在小时级别完成。而原来的语言模型可能需要几周时间。
  • 一个很酷炫的man-woman=king-queen的示例。这个示例使得人们发现词嵌入还可以这么玩,并促使词嵌入学习成为了一个研究方向,而不再仅仅是神经网络中的一些参数。
  • word2vec里有大量的tricks,比如噪声分布如何选?如何采样?如何负采样?等等。这些tricks虽然摆不上台面,但是对于得到一个好的词向量至关重要。

谷歌2013年提出的Word2Vec是目前最常用的词嵌入模型之一。Word2Vec实际是一种浅层的神经网络模型,它有两种网络结构,分别是连续词袋(CBOW)和跳字(Skip-Gram)模型。

CBOW适合于数据集较小的情况,而Skip-Gram在大型语料中表现更好

1.1 介绍CBOW

CBOW,全称Continuous Bag-of-Word,中文叫做连续词袋模型:以上下文来预测当前词 \(w_t\) 。CBOW模型的目的是预测 $P(w_t| w_{t-k}, , w_{t-1}, w_{t+1}, , w_{t+k}) $

img

前向传播过程

  • 输入层: 输入C个单词\(x\): $x_{1k}, , x_{Ck} $,并且每个 \(x\) 都是用 One-hot 编码表示,每一个 \(x\) 的维度为 V(词表长度)。

  • 输入层到隐层

    • 首先,共享矩阵为 \(W_{V \times N}\)V表示词表长度,W的每一行表示的就是一个N维的向量(训练结束后,W的每一行就表示一个词的词向量)。
    • 然后,我们把所有输入的词转\(x\)化为对应词向量,然后取平均值,这样我们就得到了隐层输出值 ( 注意,隐层中无激活函数,也就是说这里是线性组合)。 其中,隐层输出 \(h\) 是一个N维的向量 。

    \[ h = \frac{1}{C} W^T(x_1 + x_2 + \cdots + x_c) \]

  • 隐层到输出层:隐层的输出为N维向量 \(h\) , 隐层到输出层的权重矩阵为 \(W'_{N \times V}\) 。然后,通过矩阵运算我们得到一个 $V $ 维向量 \[ u = W'^{T} * h \]

其中,向量 \(u\) 的第 \(i\) 行表示词汇表中第 \(i\) 个词的可能性,然后我们的目的就是取可能性最高的那个词。因此,在最后的输出层是一个softmax 层获取分数最高的词,那么就有我们的最终输出: \[ P(w_j| context) =y_i = \frac{exp({u_j})}{\sum_{k \in V} exp({u_k})} \]

损失函数

我们假定 \(j^*\) 是真实单词在词汇表中的下标,那么根据极大似然法,则目标函数定义如下: \[ E = -log \, p(W_O |W_I) = -log \, \frac{exp({u_j})}{\sum_{k \in V} exp({u_k})} = log \sum_{k \in V} exp(u_{k}) -u_j \]

1.2 Skip-gram模型

Skip-Gram的基本思想是:通过当前词 \(w_t\) 预测其上下文 \(w_{t-i}, \cdots , w_{t+i}\) ,模型如下图所示:

img

前向传播过程

  • 输入层: 输入的是一个单词,其表示形式为 One-hot ,我们将其表示为V维向量 \(x_k\) ,其中 \(V\) 为词表大小。然后,通过词向量矩阵 \(W_{V \times N}\) 我们得到一个N维向量 \[ h = W^T * x_k = v^{T}_{w_I} \]

  • 隐层: 而隐层中没有激活函数,也就是说输入=输出,因此隐藏的输出也是 \(h\)

  • 隐层到输出层

    • 首先,因为要输出C个单词,因此我们此时的输出有C个分布: $y_1, y_C $,且每个分布都是独立的,我们需要单独计算, 其中 \(y_i\) 表示窗口的第 \(i\) 个单词的分布。 【独立性假设

    • 其次, 因为矩阵 \(W'_{N \times V}\) 是共享的,因此我们得到的 \(V \times 1\) 维向量 \(u\) 其实是相同的,也就是有 \(u_{c,j} = u_j\) ,这里 \(u\) 的每一行同 CBOW 中一样,表示的也是评分。

    • 最后,每个分布都经过一个 softmax 层,不同于 CBOW,我们此处产生的是第 \(i\) 个单词的分布(共有C个单词),如下:

    \[ P(w_{i,j}| context) =y_i = \frac{exp({u_j})}{\sum_{k \in V} exp({u_k})} \]

损失函数

假设 \(j^*\) 是真实单词在词汇表中的下标,那么根据极大似然法,则目标函数定义如下: \[ \begin{split} E &= - log \, p(w_1, w_2, \cdots, w_C | w_I) \\ &= - log \prod_{c=1}^C P(w_c|w_i) \\ &= - log \prod_{c=1}^{C} \frac{exp(u_{c, j})}{\sum_{k=1}^{V} exp(u_{c,k}) } \\ &= - \sum_{c=1}^C u_{j^*_c} + C \cdot log \sum_{k=1}^{V} exp(u_k) \end{split} \]

二、Word2Vec 优化

以上我们讨论的模型(二元模型,CBOW和skip-gram)都是他们的原始形式,没有加入任何优化技巧。对于这些模型,每个单词存在两类向量表达:输入向量[公式]输出向量[公式](这也是为什么word2vec的名称由来:1个单词对应2个向量表示)。学习得到输入向量比较简单;但要学习输出向量是很困难的。

==2.1 Hierarchical Softmax==

https://zhuanlan.zhihu.com/p/56139075

Hierarchical Softmax对原模型的改进主要有两点,第一点是从输入层到隐藏层的映射,没有采用原先的与矩阵W相乘然后相加求平均的方法,而是直接对所有输入的词向量求和。假设输入的词向量为(0,1,0,0)和(0,0,0,1),那么隐藏层的向量为(0,1,0,1)。

Hierarchical Softmax的第二点改进是采用哈夫曼树来替换了原先的从隐藏层到输出层的矩阵W’。哈夫曼树的叶节点个数为词汇表的单词个数V,一个叶节点代表一个单词,而从根节点到该叶节点的路径确定了这个单词最终输出的词向量。

img

具体来说,这棵哈夫曼树除了根结点以外的所有非叶节点中都含有一个由参数θ确定的sigmoid函数,不同节点中的θ不一样。训练时==隐藏层的向量==与这个==sigmoid函数==进行运算,根据结果进行分类,若分类为负类则沿左子树向下传递,编码为0;若分类为正类则沿右子树向下传递,编码为1。

每个叶子节点代表语料库中的一个词于是每个词语都可以被01唯一的编码,并且其编码序列对应一个事件序列,于是我们可以计算条件概率 [公式]

在开始计算之前,还是得引入一些符号:

  1. [公式] :从根结点出发到达w对应叶子结点的路径

  2. [公式] :路径中包含结点的个数

  3. [公式] :路径 [公式] 中的各个节点

  4. [公式] :词w的编码, [公式] 表示路径 [公式] 第j个节点对应的编码(根节点无编码)

  5. [公式] :路径 [公式] 中非叶节点对应的参数向量

于是可以给出w的条件概率:

[公式]

这是个简单明了的式子,从根节点到叶节点经过了 [公式] 个节点,编码从下标2开始(根节点无编码),对应的参数向量下标从1开始(根节点为1)。

其中,每一项是一个逻辑斯谛回归

[公式]

考虑到d只有0和1两种取值,我们可以用指数形式方便地将其写到一起:

[公式]

我们的目标函数取对数似然

[公式]

[公式] 代入上式,有:

[公式]
[公式]

这也很直白,连乘的对数换成求和。不过还是有点长,我们把每一项简记为:

[公式]

WordVec 极大化化目标函数使用的算法是是随机梯度上升法

每一项有两个参数,一个是每个节点的参数向量 [公式] ,另一个是输出层的输入 [公式] ,我们分别对其求偏导数:

[公式]

因为sigmoid函数的导数有个很棒的形式:

[公式]

于是代入上上式得到:

[公式]

合并同类项得到:

[公式]

于是 [公式]的更新表达式就得到了:

[公式]

其中, [公式] 是学习率,通常取0-1之间的一个值。学习率越大训练速度越快,但目标函数容易在局部区域来回抖动。

再来 [公式] 的偏导数,注意到 [公式][公式][公式] 是对称的,所有直接将 [公式] 的偏导数中的 [公式] 替换为 [公式] ,得到关于 [公式] 的偏导数:

[公式]

不过 [公式] 是上下文的词向量的和,不是上下文单个词的词向量。怎么把这个更新量应用到单个词的词向量上去呢?word2vec采取的是直接将 [公式] 的更新量整个应用到每个单词的词向量上去

[公式]

其中, [公式] 代表上下文中某一个单词的词向量。我认为应该也可以将其平均后更新到每个词向量上去,无非是学习率的不同,欢迎指正。

2.2 Negative Sampling

Negative Sampling - 素轻的文章 - 知乎 https://zhuanlan.zhihu.com/p/56106590

为了解决数量太过庞大的输出向量的更新问题,我们就不更新全部向量,而只更新他们的一个样本

训练神经网络 意味着输入一个训练样本调整weight,让它预测这个训练样本更准。换句话说,每个训练样本将会影响网络中所有的weight。Negative sampling 解决了这个问题,每次我们就修改了其中一小部分weight,而不是全部。

负采样是另一种用来提高Word2Vec效率的方法,它是基于这样的观察:训练一个神经网络意味着使用一个训练样本就要稍微调整一下神经网络中所有的权重,这样才能够确保预测训练样本更加精确,如果能设计一种方法每次只更新一部分权重,那么计算复杂度将大大降低。

如果 vocabulary 大小为10000时, 当输入样本 ( "fox", "quick") 到神经网络时, “ fox” 经过 one-hot 编码,在输出层我们期望对应 “quick” 单词的那个神经元结点输出 1,其余 9999 个都应该输出 0。在这里,这9999个我们期望输出为0的神经元结点所对应的单词我们为 negative word. negative sampling 的想法也很直接 ,将随机选择一小部分的 negative words,比如选 10个 negative words 来更新对应的权重参数。

假设原来模型每次运行都需要300×10,000(其实没有减少数量,但是运行过程中,减少了需要载入的数量。) 现在只要300×(1+10)减少了好多。

==问题来了,如何选择10个negative sample呢?==

negative sample也是根据他们出现概率来选的,而这个概率又和他们出现的频率有关。更常出现的词,更容易被选为negative sample。

这个概率用一个公式表示,每个词给了一个和它频率相关的权重。这个概率公式为:

[公式]

在paper中说0.75这个超参是试出来的,这个函数performance比其他函数好。

负采样算法实际上就是一个带权采样过程,负例的选择机制是和单词词频联系起来的。具体做法是以 N+1 个点对区间 [0,1] 做非等距切分,并引入的一个在区间 [0,1] 上的 M 等距切分,其中 M >> N。源码中取 M = 10^8。然后对两个切分做投影,得到映射关系:采样时,每次生成一个 [1, M-1] 之间的整数 i,则 Table(i) 就对应一个样本;当采样到正例时,跳过(拒绝采样)。

img
img
img

三、Word2vec Q&A

3.1 Word2Vec与LDA的区别

  1. LDA是利用文档中单词的共现关系来对单词按主题聚类,也可以理解为对“文档-单词”矩阵进行分解,得到“文档-主题”和“主题-单词”两个概率分布

  2. Word2Vec是利用上下文-单词“矩阵进行学习,其中上下文由周围的几个单词组成,由此得到的词向量表示更多地融入了上下文共现的特征。也就是说,如果两个单词所对应的word2vec向量相似度较高,那么它们很可能经常在同样的上下文中出现。

  3. LDA模型是一种基于概率图模型生成式模型,其似然函数可以写成若干条件概率连乘的形式,其中包括需要推测的隐含变量(即主题);

  4. 而Word2Vec模型一般表达为神经网络的形式,似然函数定义在网络的输出之上,需要通过学习网络的权重以得到单词的稠密向量表示。

3.2 Word2Vec存在的问题是什么?

  • 对每个local context window单独训练,没有利用包 含在global co-currence矩阵中的统计信息。
  • 对多义词无法很好的表示和处理,因为使用了唯一的词向量

3.2 OOD of word2vec

其它单词认定其为Unknow,编号为0

3.4 项目中的word2vec

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def feature_asm2vec(data_type, inter_path):
"""Feature engineering for asm2vec feature."""

if data_type == "train":
# TODO : 模型空判断
# Train a Word2vec model by mixing traing set and test set
print("------------------------ 训练asm2vec模型 ------------------------")
sentences = PathLineSentences(f"{inter_path}/semantic/")
model = Word2Vec(sentences=sentences, vector_size=1024, window=5, min_count=5, workers=5)
model.wv.save_word2vec_format(f"{inter_path}/models/asm2vec.bin", binary=True, sort_attr='count')

# Load the trained Word2vec model
model_wv = KeyedVectors.load_word2vec_format(f"{inter_path}/models/asm2vec.bin", binary=True)

print("------------------------ 生成asm2vec特征 ------------------------")
with open(f"{inter_path}/{data_type}_filename.txt", 'r') as fp:
filename = fp.read().split()
# Feature engineering for generating string vector features
obj = StringVector()
arr = np.zeros((len(filename), obj.dim))
with tqdm(total=len(filename), ncols=80, desc=obj.name) as pbar:
for i, file in enumerate(filename):
with open(f"{inter_path}/semantic/{file}.txt", "rb") as f:
stringz = f.read().decode('utf-8', errors='ignore')
lines = ' '.join(stringz.split('\n'))
raw_words = list(set(lines.split()))
arr[i, :] = obj.feature_vector((model_wv, raw_words))
pbar.update(1)
arr[np.isnan(arr)] = 0
np.save(f"{inter_path}/feature/{data_type}_semantic.npy", arr)

3.5 Tf-Idf、Word2Vec和BERT 比较

从算法本质来说 word2vec 一旦训练好了是没法处理未登录词(OOV)的,一般的做法是给OOV一个默认的向量,下面是一个类的封装(仅列出核心部分)

https://www.leiphone.com/category/yanxishe/TbZAzc3CJAMs815p.html

  • 词袋法:用scikit-learn进行特征工程、特征选择以及机器学习,测试和评估,用lime解释。
  • 词嵌入法:用gensim拟合Word2Vec,用tensorflow/keras进行特征工程和深度学习,测试和评估,用Attention机制解释。
  • 语言模型:用transformers进行特征工程,用transformers和tensorflow/keras进行预训练BERT的迁移学习,测试和评估。

概要

在本文中,我将使用NLP和Python来解释3种不同的文本多分类策略:老式的词袋法(tf-ldf),著名的词嵌入法(Word2Vec)和最先进的语言模型(BERT)。

NLP之文本分类:「Tf-Idf、Word2Vec和BERT」三种模型比较

NLP(自然语言处理)是人工智能的一个领域,它研究计算机和人类语言之间的交互作用,特别是如何通过计算机编程来处理和分析大量的自然语言数据。NLP常用于文本数据的分类。文本分类是指根据文本数据内容对其进行分类的问题。

我们有多种技术从原始文本数据中提取信息,并用它来训练分类模型。本教程比较了传统的词袋法(与简单的机器学习算法一起使用)、流行的词嵌入模型(与深度学习神经网络一起使用)和最先进的语言模型(和基于attentiontransformers模型中的迁移学习一起使用),语言模型彻底改变了NLP的格局。

我将介绍一些有用的Python代码,这些代码可以轻松地应用在其他类似的案例中(仅需复制、粘贴、运行),并对代码逐行添加注释,以便你能复现这个例子(下面是全部代码的链接)。

词袋法:文件越多,词汇表越大,因此特征矩阵将是一个巨大的稀疏矩阵。

Bert比之Word2Vec,有哪些进步呢?

  • 静态到动态:一词多义问题

  • 词的多层特性:一个好的语言表示出了建模一词多义现象以外,还需要能够体现词的复杂特性,包括语法 (syntax)、语义 (semantics) 等。