《深度学习进阶:自然语言处理(第3章)》-读书笔记

第3章 word2vec

3.1 基于推理的方法和神经网络

  • 基于计数的方法的问题

基于计数的方法根据一个单词周围的单词的出现频数来表示该单词。具体来说,先生成所有单词的共现矩阵,再对这个矩阵进行 SVD,以获得密集向量(单词的分布式表示)。在现实世界中,语料库处理的单词数量非常大,使用基于计数的方法就需要生成一个庞大矩阵,但对如此庞大的矩阵执行 SVD 显然是不现实的。

  • 基于推理的方法

基于推理的方法的主要操作是“推理”。当给出周围的单词(上下文)时,预测“?”处会出现什么单词,这就是推理。you ? goodbye and i say hello.

基于推理的方法引入了某种模型,我们将神经网络用于此模型。这个模型接收上下文信息作为输入,并输出(可能出现的)各个单词的出现概率。在这样的框架中,使用语料库来学习模型,使之能做出正确的预测。另外,作为模型学习的产物,我们得到了单词的分布式表示。

基于推理的方法和基于计数的方法一样,也基于分布式假设。分布式假设假设“单词含义由其周围的单词构成”。基于推理的方法将这一假设归结为了上面的预测问题。由此可见,不管是哪种方法,如何对基于分布式假设的“单词共现”建模都是最重要的研究主题。

3.2 简单的word2vec

word2vec 一词最初用来指程序或者工具,但是随着该词的流行,在某些语境下,也指神经网络的模型。正确地说,CBOW 模型和 skip-gram 模型是 word2vec 中使用的两个神经网络。

  • CBOW模型的推理

CBOW 模型是根据上下文预测目标词的神经网络(“目标词”是指中间的单词,它周围的单词是“上下文”)。通过训练这个 CBOW 模型,使其能尽可能地进行正确的预测,我们可以获得单词的分布式表示。

CBOW 模型的输入是上下文。这个上下文用 [‘you’, ‘goodbye’] 这样的单词列表表示。我们将其转换为 one-hot 表示,以便 CBOW 模型可以进行处理。

CBOW 模型的网络有两个输入层,经过中间层到达输出层。这里,从输入层到中间层的变换由相同的全连接层(权重为 W i n W_{in} Win)完成,从中间层到输出层神经元的变换由另一个全连接层(权重为 W o u t W_{out} Wout)完成。中间层的神经元是各个输入层经全连接层变换后得到的值的“平均”。经全连接层变换后,第 1 个输入层转化为 h1,第 2 个输入层转化为 h2,那么中间层的神经元是 1 2 ( h 1 + h 2 ) \frac{1}{2}(h1+h2) 21(h1+h2)。输出层的神经元是各个单词的得分,它的值越大,说明对应单词的出现概率就越高。得分是指在被解释为概率之前的值,对这些得分应用 Softmax 函数,就可以得到概率。

从输入层到中间层的变换由全连接层(权重是 W i n W_{in} Win)完成。此时,全连接层的权重 W i n W_{in} Win 是一个矩阵。这个权重就是我们要的单词的分布式表示!!!

中间层的神经元数量比输入层少这一点很重要。中间层需要将预测单词所需的信息压缩保存,从而产生密集的向量表示。这时,中间层被写入了我们人类无法解读的代码,这相当于“编码”工作。而从中间层的信息获得期望结果的过程则称为“解码”。这一过程将被编码的信息复原为我们可以理解的形式。

实现 CBOW 模型的推理(求得分的过程):

import numpy as np
class MatMul:
    def __init__(self, W):
        self.params = [W]
        self.grads = [np.zeros_like(W)]
        self.x = None

    def forward(self, x):
        W, = self.params
        out = np.dot(x, W)
        self.x = x
        return out

    def backward(self, dout):
        W, = self.params
        dx = np.dot(dout, W.T)
        dW = np.dot(self.x.T, dout)
        self.grads[0][...] = dW
        return dx
# 样本的上下文数据
c0 = np.array([[1, 0, 0, 0, 0, 0, 0]])
c1 = np.array([[0, 0, 1, 0, 0, 0, 0]])
# 权重的初始值
W_in = np.random.randn(7, 3)
W_out = np.random.randn(3, 7)
# 生成层
in_layer0 = MatMul(W_in)
in_layer1 = MatMul(W_in)
out_layer = MatMul(W_out)
# 正向传播
h0 = in_layer0.forward(c0)
h1 = in_layer1.forward(c1)
h = 0.5 * (h0 + h1)
s = out_layer.forward(h)
print(s)
  • CBOW模型的学习

CBOW 模型在输出层输出了各个单词的得分,通过对这些得分应用 Softmax 函数,可以获得概率。这个概率表示哪个单词会出现在给定的上下文(周围单词)中间。CBOW 模型的学习就是调整权重,以使预测准确。其结果是,权重 W i n W_{in} Win(确切地说是 W i n W_{in} Win W o u t W_{out} Wout 两者)学习到蕴含单词出现模式的向量。

!!!注意:CBOW 模型只是学习语料库中单词的出现模式。如果语料库不一样,学习到的单词的分布式表示也不一样。比如,只使用“体育”相关的文章得到的单词的分布式表示,和只使用“音乐”相关的文章得到的单词的分布式表示将有很大不同。

CBOW 模型神经网络的学习,我们处理的模型是一个进行多类别分类的神经网络。因此,对其进行学习只是使用一下 Softmax 函数和交叉熵误差。首先,使用 Softmax 函数将得分转化为概率,再求这些概率和监督标签之间的交叉熵误差,并将其作为损失进行学习。亦可将这两个层实现为了一个 Softmax with Loss 层。

使用哪个权重作为单词的分布式表示呢?这里有三个选项:
A. 只使用输入侧的权重 B. 只使用输出侧的权重 C. 同时使用两个权重
就 word2vec(特别是 skip-gram 模型)而言,最受欢迎的是方案 A。在与 word2vec 相似的 GloVe 方法中,通过将两个权重相加,也获得了良好的结果。

3.3 学习数据的准备

  • 上下文和目标词

首先,将语料库的文本转化成单词 ID。然后,从单词 ID 列表 corpus 生成上下文 contexts 和目标词 target。

从单词ID列表corpus生成contexts和target的例子(上下文的窗口大小为1)

实现这个生成上下文和目标词的函数:

def create_contexts_target(corpus, window_size=1):
    """生成上下文和目标词
       单词 ID 列表(corpus);上下文的窗口大小(window_size)
    """
    target = corpus[window_size:-window_size]  # 目标词包含除两头window_size个以外的所有词
    contexts = []
    for idx in range(window_size, len(corpus)-window_size):  # 索引为除去两头的window_size个词
        cs = []
        for t in range(-window_size, window_size + 1):  # 加入目标词前后各window_size个词
            if t == 0:
                continue
            cs.append(corpus[idx + t])
        contexts.append(cs)
    return np.array(contexts), np.array(target)
  • 转化为one-hot表示

3.4 CBOW模型的实现

class SimpleCBOW:
    def __init__(self, vocab_size, hidden_size):
        V, H = vocab_size, hidden_size
        # 初始化权重
        W_in = 0.01 * np.random.randn(V, H).astype('f')
        W_out = 0.01 * np.random.randn(H, V).astype('f')
        # 生成层
        self.in_layer0 = MatMul(W_in)
        self.in_layer1 = MatMul(W_in)
        self.out_layer = MatMul(W_out)
        self.loss_layer = SoftmaxWithLoss()
        # 将所有的权重和梯度整理到列表中
        layers = [self.in_layer0, self.in_layer1, self.out_layer]
        self.params, self.grads = [], []
        for layer in layers:
            self.params += layer.params
            self.grads += layer.grads
        # 将单词的分布式表示设置为成员变量
        self.word_vecs = W_in
    def forward(self, contexts, target):
        h0 = self.in_layer0.forward(contexts[:, 0])
        h1 = self.in_layer1.forward(contexts[:, 1])
        h = (h0 + h1) * 0.5
        score = self.out_layer.forward(h)
        loss = self.loss_layer.forward(score, target)
        return loss
    def backward(self, dout=1):
        ds = self.loss_layer.backward(dout)
        da = self.out_layer.backward(ds)
        da *= 0.5
        self.in_layer1.backward(da)
        self.in_layer0.backward(da)
        return None
if __name__ == '__main__':
    # 超参数
    window_size = 1
    hidden_size = 5
    batch_size = 3
    max_epoch = 1000
    # 数据文本
    text = 'You say goodbye and I say hello.'
    # 文本转化为单词ID
    corpus, word_to_id, id_to_word = preprocess(text)
    # 单词ID转化为上下文和目标词的one-hot表示
    vocab_size = len(word_to_id)
    contexts, target = create_contexts_target(corpus, window_size)
    target = convert_one_hot(target, vocab_size)
    contexts = convert_one_hot(contexts, vocab_size)
    # 模型训练
    model = SimpleCBOW(vocab_size, hidden_size)  # 实例化模型
    optimizer = Adam()  # 优化器
    trainer = Trainer(model, optimizer)
    trainer.fit(contexts, target, max_epoch, batch_size)
    trainer.plot()

继续阅读:
《深度学习进阶:自然语言处理(第1章)》-读书笔记
《深度学习进阶:自然语言处理(第2章)》-读书笔记
《深度学习进阶:自然语言处理(第3章)》-读书笔记
《深度学习进阶:自然语言处理(第4章)》-读书笔记
《深度学习进阶:自然语言处理(第5章)》-读书笔记
《深度学习进阶:自然语言处理(第6章)》-读书笔记
《深度学习进阶:自然语言处理(第7章)》-读书笔记
《深度学习进阶:自然语言处理(第8章)》-读书笔记

  • 19
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值