深度学习进阶:自然语言处理
上QQ阅读APP看书,第一时间看更新

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

用向量表示单词的研究最近正在如火如荼地展开,其中比较成功的方法大致可以分为两种:一种是基于计数的方法;另一种是基于推理的方法。虽然两者在获得单词含义的方法上差别很大,但是两者的背景都是分布式假设。

本节我们将指出基于计数的方法的问题,并从宏观角度说明它的替代方法——基于推理的方法的优点。另外,为了做好word2vec的准备工作,我们会看一个用神经网络处理单词的例子。

3.1.1 基于计数的方法的问题

如上一章所说,基于计数的方法根据一个单词周围的单词的出现频数来表示该单词。具体来说,先生成所有单词的共现矩阵,再对这个矩阵进行SVD,以获得密集向量(单词的分布式表示)。但是,基于计数的方法在处理大规模语料库时会出现问题。

在现实世界中,语料库处理的单词数量非常大。比如,据说英文的词汇量超过100万个。如果词汇量超过100万个,那么使用基于计数的方法就需要生成一个100万×100万的庞大矩阵,但对如此庞大的矩阵执行SVD显然是不现实的。

对于一个n×n的矩阵,SVD的复杂度是On3),这表示计算量与n的立方成比例增长。如此大的计算成本,即便是超级计算机也无法胜任。实际上,利用近似方法和稀疏矩阵的性质,可以在一定程度上提高处理速度,但还是需要大量的计算资源和时间。

基于计数的方法使用整个语料库的统计数据(共现矩阵和PPMI等),通过一次处理(SVD等)获得单词的分布式表示。而基于推理的方法使用神经网络,通常在mini-batch数据上进行学习。这意味着神经网络一次只需要看一部分学习数据(mini-batch),并反复更新权重。这种学习机制上的差异如图3-1所示。

图3-1 基于计数的方法和基于推理的方法的比较

如图3-1所示,基于计数的方法一次性处理全部学习数据;反之,基于推理的方法使用部分学习数据逐步学习。这意味着,在词汇量很大的语料库中,即使SVD等的计算量太大导致计算机难以处理,神经网络也可以在部分数据上学习。并且,神经网络的学习可以使用多台机器、多个GPU并行执行,从而加速整个学习过程。在这方面,基于推理的方法更有优势。

基于推理的方法和基于计数的方法相比,还有一些其他的优点。关于这一点,在详细说明基于推理的方法(特别是word2vec)之后,我们会在3.5.3节再次讨论。

3.1.2 基于推理的方法的概要

基于推理的方法的主要操作是“推理”。如图3-2所示,当给出周围的单词(上下文)时,预测“?”处会出现什么单词,这就是推理。

图3-2 基于两边的单词(上下文),预测“?”处出现什么单词

解开图3-2中的推理问题并学习规律,就是基于推理的方法的主要任务。通过反复求解这些推理问题,可以学习到单词的出现模式。从“模型视角”出发,这个推理问题如图3-3所示。

图3-3 基于推理的方法:输入上下文,模型输出各个单词的出现概率

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

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

3.1.3 神经网络中单词的处理方法

从现在开始,我们将使用神经网络来处理单词。但是,神经网络无法直接处理you或say这样的单词,要用神经网络处理单词,需要先将单词转化为固定长度的向量。对此,一种方式是将单词转换为one-hot表示(one-hot向量)。在one-hot表示中,只有一个元素是1,其他元素都是0。

我们来看一个one-hot表示的例子。和上一章一样,我们用“You say goodbye and I say hello.”这个一句话的语料库来说明。在这个语料库中,一共有7个单词(“you”“say”“goodbye”“and”“i”“hello”“.”)。此时,各个单词可以转化为图3-4所示的one-hot表示。

图3-4 单词、单词ID以及它们的one-hot表示

如图3-4所示,单词可以表示为文本、单词ID和one-hot表示。此时,要将单词转化为one-hot表示,就需要准备元素个数与词汇个数相等的向量,并将单词ID对应的元素设为1,其他元素设为0。像这样,只要将单词转化为固定长度的向量,神经网络的输入层的神经元个数就可以固定下来(图3-5)。

图3-5 输入层的神经元:各个神经元对应于各个单词。图中神经元为1的地方用黑色绘制,为0的地方用白色绘制

如图3-5所示,输入层由7个神经元表示,分别对应于7个单词(第1个神经元对应于you,第2个神经元对应于say)。

现在事情变得很简单了。因为只要将单词表示为向量,这些向量就可以由构成神经网络的各种“层”来处理。比如,对于one-hot表示的某个单词,使用全连接层对其进行变换的情况如图3-6所示。

图3-6 基于神经网络的全连接层的变换:输入层的各个神经元分别对应于7个单词(中间层的神经元暂为3个)

如图3-6所示,全连接层通过箭头连接所有节点。这些箭头拥有权重(参数),它们和输入层神经元的加权和成为中间层的神经元。另外,本章使用的全连接层将省略偏置(这是为了配合后文对word2vec的说明)。

没有偏置的全连接层相当于在计算矩阵乘积。在很多深度学习的框架中,在生成全连接层时,都可以选择不使用偏置。在本书中,不使用偏置的全连接层相当于MatMul层(该层已经在第1章中实现)。

在图3-6中,神经元之间的连接是用箭头表示的。之后,为了明确地显示权重,我们将使用图3-7所示的方法。

图3-7 基于全连接层的变换的简化图示:将全连接层的权重表示为一个7 × 3形状的W矩阵

现在,我们看一下代码。这里的全连接层变换可以写成如下的Python代码。

        import numpy as np

        c = np.array([[1, 0, 0, 0, 0, 0, 0]])  # 输入
        W = np.random.randn(7, 3)                # 权重
        h = np.dot(c, W)                          # 中间节点
        print (h)
        # [[-0.70012195  0.25204755 -0.79774592]]

这段代码将单词ID为0的单词表示为了one-hot表示,并用全连接层对其进行了变换。作为复习,全连接层的计算通过矩阵乘积进行。这可以用NumPy的np.dot()来实现(省略偏置)。

这里,输入数据(变量c)的维数(ndim)是2。这是考虑了mini-batch处理,将各个数据保存在了第1维(0维度)中。

希望读者注意一下c和W进行矩阵乘积计算的地方。此处,c是one-hot表示,单词ID对应的元素是1,其他地方都是0。因此,如图3-8所示,上述代码中的c和W的矩阵乘积相当于“提取”权重的对应行向量。

图3-8 在上下文c和权重W的矩阵乘积中,对应位置的行向量被提取(权重的各个元素的大小用灰度表示)

这里,仅为了提取权重的行向量而进行矩阵乘积计算好像不是很有效率。关于这一点,我们将在4.1节进行改进。另外,上述代码的功能也可以使用第1章中实现的MatMul层完成,如下所示。

        import sys
        sys.path.append('..')
        import numpy as np
        from common.layers import MatMul

        c = np.array([[1, 0, 0, 0, 0, 0, 0]])
        W = np.random.randn(7, 3)
        layer = MatMul(W)
        h = layer.forward(c)
        print (h)
        # [[-0.70012195  0.25204755 -0.79774592]]

这里,我们先导入了common目录下的MatMul层。之后,将MatMul层的权重设为了W,并使用forward()方法执行正向传播。