Advertisement

论文阅读:A Neural Probabilistic Language Model 一种神经概率语言模型

阅读量:

A Neural Probabilistic Language Model

一种神经概率语言模型

目录

  • 一种神经概率语言模型
    • 关键词:语言模型, 神经网络, 分布式表示, 维度灾难

    • 摘要

    • 关键词:统计语言模型, 人工神经网络, 分布式表示, 维度灾难

      • 引言

        • 1.1基于分布式表示解决维度灾难
          1.2 与现有研究的关系
      • 2. 一个神经模型

      • 3. 并行化的实现

        • 3.1 数据并行处理
    • 3.2 参数并行处理

      • 4. 实验结果
        • 4.1 N-Gram模型
    • 4.2 结果

      • 5. 结论

目标

摘要

统计语言模型建模(Statistical Language Modeling)的主要目标是训练一种能够学习语言中词序列联合概率分布模型的语言系统。由于维度灾难这一本质难题的存在:即将被测试的语言序列很可能与训练集中见过的所有词序完全不同。
传统的成功方法基于n-gram模型通过连接训练集中频繁出现的小窗口序列来推断泛化特性。
为了克服维度灾难带来的挑战,在此提出了一种基于词向量的学习方法。
这种新方法的主要优势在于每个训练语料不仅提供自身的一系列信息,
而且还能赋予模型大量关于语义相关上下文的信息,
从而显著提升其泛化能力。
根据上述理论描述,
该系统同时学习两个关键任务:
(1)每个词的分布式向量表示;
(2)不同词序列的概率分布特征。
这种系统的泛化能力来源于这样一个事实:
即使遇到从未见过的新词序列,
只要这些新词语是在某个代表性的语义上下文中出现过的,
系统就能赋予其较高的概率值。
在合理的时间内训练这样的大规模模型(包含上百万参数量级)
本身就是一个极具挑战性的任务。
我们还进行了基于神经网络的概率预测实验,
结果显示这种方法显著优于传统n-gram模型,
尤其是在捕捉更长范围内的上下文依赖性和复杂语义关系方面表现突出。

关键词:统计语言模型,人工神经网络,分布式表示,维数灾难

1.介绍

使语言建模和其他学习问题本质上的困难问题在于维数灾难现象。当一个人试图为许多离散随机变量(如句子中的单词或数据挖掘中的离散分布)构建联合概率分布模型时,这一问题尤为突出。例如,在自然语言处理中若单词表大小为10万的情况下建立包含十个相连词的联合分布模型,则自由度将达10万十减一等于约十亿五千万个自由度 。相比之下,在连续变量建模时更容易实现泛化(如光滑函数类如多层神经网络或高斯混合模型),因为这些函数通常具有局部平滑特性;但对于离散空间而言泛化结构并不明显:任何离散随机变量的变化都可能对目标函数值产生巨大影响,并且当每个变量取值范围较大时,在汉明距离意义上绝大多数样本之间的距离都会变得极其遥远

为了直观比较不同学习算法的泛化能力可以从非参数密度估计的角度出发考虑:初始的概率质量会在训练数据点上(即训练句子)集中并以较大体积进行分布通常采用某种形式局部邻居来进行建模

1

其中wt是第t个词定义子序列wji为(wi, wi+1,…,wj−1,wj)这种统计语言模型已被广泛应用于多种自然语言处理技术领域如语音识别语言翻译和信息检索等。
在建立统计语言模型时一种有效的方法是假设较近的词之间存在较强的依赖关系基于此在n-gram模型中在已知前n−1个词的前提下第n个词的概率分布被建模为

2

我们只考虑在训练集中出现的连续词的组合,或者出现足够频繁的词 。对于语料库中未出现的n元单词新组合,为避免为其分配零概率,考虑back-off trigram models (Katz, 1987)或者smoothed (or interpolated) trigram models(Jelinek and Mercer, 1980)中使用的方法 :使用更小的语料进行概率预测。获得新的单词序列的方法主要是与插值(interpolated)或者n元回退(backoff n-gram)模型相关的生成模型,通过“粘合(gluing)”训练数据中短且重复的长度为1,2甚至n个频繁出现的单词来生成新的单词序列。
当在语料中未见过的n个词的新组合出现时,将发生什么?我们不想为它们分配为0的概率,因为这样的组合确实有可能发生。一个简单的解决办法是使用更小的上下文,即使用tri-gram或者平滑后的tri-gram。本质上来说,一个新的词序列是通过“粘合 ”非常短的重叠的在训练语料中出现频繁的字片段组成。获得下一个片段的概率的规则是隐式的回退或者打折后的n-gram算法 。研究者使用典型的n=3的tri-gram ,并且获得了世界领先水平的结果。显然的是直接出现在词前面的序列携带的信息要比仅仅之前的一小段序列携带的信息多。我们在本论文中提出的方法至少在两个特点上面显著的提高了上面的问题。第一点,上面的方法没有考虑超过1或2个词的上下文;第二点,上面的方法没有考虑词与词之间的相似性。例如,在语料库中已经观测到了序列“The cat is walking in the bedroom”,可以帮助我们生成序列“A dog was running in a room”,因为“dog”和“cat”有相似的语义和语法角色。
有很多被提出来的方法可以解决这两个问题,我们在1.2节给出简洁的解释。我们首先将讨论被提出方法的基本思想。更加形式化的介绍将在2节中给出。这些思想的实现使用的是同享参数的多层神经网络 。这篇论文的另一个贡献是介绍了对大量数据训练如此大的神经网络的高效方法 。最后,一个重要的贡献是说明了训练如此大规模的模型是昂贵但是值得的
这片论文的很多运算使用矩阵符号 ,使用小写字母v代表列向量,v’代表它的转置,Aj表示矩阵A的第j行,x.y代表x’y。

1.1使用分布式表示解决维度灾难

简单来讲,本方法的思想可以被概括成以下3个步骤
1. 为在词表中的每一个词分配一个分布式的词特征向量
2. 词序列中出现的词的特征向量表示的词序列的联合概率函数
3.学习词特征向量和概率函数的参数
词特征向量代表了词的不同的方面:每个词关联向量空间的一个点。特征的数量远远小于词表的大小 。概率函数被表达成给定前面的词后面一个词的条件概率的乘积(例如,在实验中,使用多层神经网络,给定前面的词预测下一个词)。这个函数有一些参数,可以通过迭代 的方式调整这些参数来最大化对数似然函数 。这些与词关联的特征向量可以被学习得当,但是他们可以使用先验的语义特征知识来初始化
为什么这样有效?在前面的例子中,如果我们知道 “ dog”和“cat”扮演相似的角色(语义的或者句法的),类似的对于(the,a),(bedroom,room),(is,was),(running,walking),我们自然地可以由
The cat is walking in the bedroom
生成
A dog was running in a room
或者
The cat is runing in a room
A dog is walking in a bedroom
The dog was walking in the room

或者更多的其他组合。在本模型中,这些可以被生成因为相似的词被期望有相似的特征向量 ,也因为概率函数是一个这些特征值的平滑的函数 ,在特征中的小的改变将在概率中产生小的变化。因此,上述这些句子其中一个在语料库中的出现,将增加这些句子的概率。

1.2 与前面工作的关系

基于神经网络构建高维离散分布模型,在该模型中可有效推断Z₁至Zₙ等变量的联合概率分布(Bengio and Bengio, 2000a,b)。其中联合概率P(Z₁,…,Zₙ)可分解为各条件概率P(Z_i|Z₁,…,Z_{i-1})的乘积:

基于神经网络构建高维离散分布模型,在该模型中可有效推断Z₁至Zₙ等变量的联合概率分布(Bengio and Bengio, 2000a,b)。其中联合概率P(Z₁,…,Zₙ)可分解为各条件概率P(Z_i|Z₁,…,Z_{i-1})的乘积:

3

其中g(x)是由左到右结构神经网络表示的一个函数。第i个输出块gi推导出给定之前的Z及Zi的条件概率参数。在四个UCI基准数据集上该方法表现良好。由于需要处理变长序列如句子等数据该方法必须相应调整以适应不同长度的数据需求。
Miikkulainen与Dyer于1991年提出的神经网络语言模型;基于字符的文本压缩模型利用神经网络预测下一个字符的概率(Schmidhuber 1996);该模型由于缺乏隐藏层以及单一输入单元而仅能捕获单变量统计信息(Xu与Rudnicky 2000)
基于学习词汇聚类的方法包括Browne等人(1992)Pereira等人(1993)Niesler等人(1998)以及Baker与McCallum(1998)
向量空间表示方法主要应用于信息检索领域(Schutze 1993)

2. 一个神经模型

训练集由一连串词w₁,…,w_T组成 ,其中每个词w_t属于词库V。该词库V是一个规模庞大但有限的集合。模型旨在学习一个能够有效地估计条件概率 的函数:

4

需要满足的约束为:

5
6

其中,在时间序列中,变量wt代表第t个位置上的词汇;V是词汇表集合,并用|V|表示其元素的数量。通过条件概率乘积可以获得整个语义序列的概率值。我们将该函数分解为两部分:首先是一个映射C(·),它将任意一个来自词汇表中的单词i映射到Rm空间中的一个特征向量C(i)。这种映射关系通常被建模成一个自由参数矩阵C∈R^{|V|×m}。其次是一种条件概率模型g(·),它接受输入序列中前n-1个连续单词的特征向量(即从C(wt-n+1)到C(wt-1))作为输入变量,并输出下一个单词i的概率值P(wi = i | C(wt-n+1),…,C(wt-1))

7

,如下图

8

图1 神经网络语言模型结构图

9

函数f是由两个映射C和g构成的复合映射。这两个映射都涉及一些参数变量。其中映射C接受输入作为其参数,在原始特征向量输入空间中表示出来。而另一个映射g则由前馈神经网络或卷积神经网络等其他类型的参数化函数来构建。在训练过程中我们通过寻找θ使得训练数据的对数似然函数最大化来完成模型的学习任务

10

其中θ代表一个参数,在该模型设计中,
R(\theta)被视为正则化项,
其作用类似于对权重进行惩罚,
主要涉及神经网络中的权重参数以及矩阵C。
在该模型架构下,
自由参数的数量与词表大小V呈线性关系,
同时也与序列长度n呈线性关系。
在多数实验运行结果中,
神经网络通常包含一个隐藏层,
该隐藏层位于词特征映射之前,
直接连接词特征至输出部分。
因此实际上系统包含了两套共享机制:
一是共享词特征层C,
二是双曲正切型隐含层。
输出端采用
softmax函数:

11

其中yi是每个输出词i的未归一化log概率 ,计算如下:

12

其中b,W,U,d和H都是参数,x为输入,则θ=(b, W, U, d,H)。双曲正切 被一个元素接一个元素的作用域向量 中。当神经网络中隐藏单元的数目为h,词表大小为|V|时,b是|V|维的列向量,W是|V|×(n-1)m的矩阵,U是|V|×h的矩阵,d是h维的列向量,H是h×(n-1)m的矩阵。需要注意的是,一般的神经网络输入是不需要优化,而在这里,x=(C(wt-1 ),C(wt-2 ),…,C(wt-n+1)),也是需要优化的参数 。在图4-1中,如果下层原始输入 x 不直接连到输出的话,可令W=0。
自由参数的数量 是|V|(1+nm+h)+h(1+(n-1)m).其中的主要因子是|V|(nm+h)。
如果采用随机梯度算法 的话,梯度 更新的法则为:

13

其中ε代表学习速率,并被定义为学习率。值得注意的是,在常规设计中,在神经网络架构中通常情况下,在这里进行研究时,输入层x也被视为一个参数,并且存在于变量空间C中。经过优化后完成了模型的训练过程。

3. 并行化的实现

尽管参数的数量以输入窗口大小n与词表大小|V|的线性函数形式存在,并已被有效限制,在这种情况下总体计算量仍然显著超过n-gram模型所需处理的任务量。其根本原因在于,在n-gram模型中无需遍历整个词汇表进行归一化处理即可获得特定的概率值这一事实。因此,在神经网络架构设计中,输出层成为系统的性能瓶颈。

3.1 数据并行处理

基于共享内存架构的设计下,并行计算变得相对容易

4

3.2 参数并行处理

如果我们设想并行计算机由多个CPU构成一个网络系统,则应避免频繁的数据交换开销问题

14

该计算是一种估算,在实际操作中由于不同类型的计算会导致CPU运行时间的变化。
然而它表明并行计算输出层在整体性能上具有积极的影响。
所有的CPU都需要存储非常少量的关键因子这对其总体运算时间的影响微乎其微。
即使隐藏层单元的数据规模较大并行化处理依然能够带来性能提升但此处并未提供具体的实验数据来证明这一观点。
符号说明如下:
其中"."表示笛卡尔积"’"表示矩阵转置,
对于第i个CPU(i取值范围为0到M-1)负责计算从起始位置starti=i×⌈|V|/M⌉开始长度为min(⌈|V|/M⌉, |V|-starti)的一段连续区域。

15
16
17

权重惩罚正则化 没有在上面显示,但是可以简单的被实现。需要注意的是参数的更新是立即的 而不是通过一个参数梯度向量,这样做可以提高速度。
前向计算 阶段,会出现一些问题,其中一个问题是pj可以全部为0,或者他们的其中一个非常大而不能进行指数运算。为了避免这个问题,通常的解决方案是在计算指数运算之前,减去yj中最大的数。因此我们可以在计算pj之前加上一个Allreduce运算 去在M个处理器间共享yj的最大值。
在低速度的集群上,仍然可以获得有效的并行化。与其在每个训练样例计算时通信,不如在每K个训练样例计算时通信。这需要保存神经网络的K个激活和梯度 。在K个训练样例的前向阶段后,概率的和必须共享给处理器。然后K后向阶段被初始化 。在交换了这些梯度向量之后,每个处理器可以完成后向阶段并更新参数 。如果K过大,将会导致不收敛的问题。

4. 实验结果

在Browns语料库上的1181041个词序列的数据上进行了系统性对比实验研究。其中前80万份词序列用于构建基础模型参数集合;随后的20万份词序列被用来优化模型结构;剩余的181041份词序列则用于评估模型性能。统计数据显示不同词语数量共计47587项。为了提高数据处理效率我们将原始词汇表规模缩减至|V|=16383。在此过程中我们采用词汇频率作为筛选标准并实施小写转换操作将非字母字符归并处理以减少数据维度

另一个实验则在AP新闻 corpus 的文本数据上展开。其中训练数据集规模达到约1.4亿个序列;验证数据集规模约为一百万份序列;测试阶段则采用了约十万份序列的数据进行评估。经过统计分析发现该语料集中共有148721个独特词汇;最终我们将词汇表规模压缩至|V|=17964项具体方法包括优先保留高频词汇并统一转小写同时对数字符号及特殊字符实施合并处理策略从而有效降低了计算复杂度

对于神经网络体系初始学习速率设定为ε₀=0.001并通过逐次衰减公式εₜ=ε₀/(1+rt)来实现动态调节其中t代表已更新参数数量r为衰减因子取值范围为十的负八次方到十的负四次方之间

4.1 N-Gram模型

作为第一个对照对象的是采用插值法或平滑法构建的trigram模型。其条件概率表达式为

18

其中,在条件权重α_i(q_t) ≥ 0的基础上满足∑i α_i(q_t) = 1;基本预测值包括:p₀ = 1/|V|;而p₁(i)表示训练集中单词i在整体中的相对频率;此外,p₂(i|j)也是一个相对频率(即上一个单词为j时单词i在整体中的相对频率);再者,p₃(i|j,k)(即当前两个词为j,k时单词i在整体中的相对频率)也是一个相对频率。具体选择哪种模型的原因在于:当(w{t-1},w_{t-2})出现频次较高时,p₃更为可靠;反之,当其出现频次较低时,则可优先考虑较低层次的概率模型如p₂、p₁甚至基线概率值p₀进行预测。对于每一个离散化的q_t(即上下文频率区间),在验证集上大约经过5次迭代即可轻松地估计出相关参数,而不必单独估计单字组、双字母组以及三字母组等低阶n-gram的概率分布参数。这些低阶n-gram模型通过与多层前馈网络(MLP)进行混合融合的方式表现出了显著的效果。

4.2 结果

下图为基于困惑度的对不同模型的测试结果

19

上图展示了布朗语料库的相关比较结果。删除插值三元组在测试困惑度方面比达到最低验证困惑度的神经网络模型高出33%。在最佳n-gram(基于类的500词元分类模型)的情况下这一差距缩小至24%。其中,
n:模型的序列长度;
c:基于类的n-gram中的词元类别数;
h:隐层单元的数量;
m:基于类的n-gram中词元特征的数量;
direct:是否存在从功能字到输出直接连接的可能性;
mix:神经网络输出的概率是否与三元组输出进行混合(每个分支权重为0.5)。
最后三列分别列出了训练集、验证集和测试集的困惑度数值。
通过对比实验可以看出,在所有测试指标上,基于神经网络的语言模型均优于最优n-gram模型。

5. 结论

实验在两个语料库中进行:一个语料库规模超过一百万个训练样本;另一个则拥有高达1.5亿个词量。实验结果表明我们提出的方法在困惑度方面显著优于现有的trigram模型。我们相信主要原因在于该方法能够通过学习分布表示来有效应对维数灾难的问题。这一模型仍存在诸多改进空间;在架构设计、计算效率优化以及先验知识应用等方面均有机会进行提升。一种直观的方法是利用卷积神经网络来整合时间结构信息并扩展输入窗口大小。鉴于统计语言模型研究的很多努力工作都专注于限制和总结条件变量以防止过拟合问题;而在本论文中介绍的方法转移了这一难题:所需计算量进一步增加;但其对计算与内存资源的需求呈线性增长而非指数级别增长。

参考代码:

复制代码
    import numpy as np
    import tensorflow as tf
    
    tf.reset_default_graph()
    
    sentences = ["i like coffee", "i love curry", "i hate apple"]
    word_list = " ".join(sentences).split()
    word_list = list(set(word_list))
    print(word_list)
    
    word_dict = {w: i for i, w in enumerate(word_list)}
    number_dict = {i: w for i, w in enumerate(word_list)}
    n_class = len(word_dict)
    
    # Model parameters
    n_step = 2
    n_hidden = 5
    
    def make_batch(sentences):
    input_batch = []
    target_batch = []
    for sentence in sentences:
        words = sentence.split()
        input = [word_dict[word] for word in words[:-1]]
        target = word_dict[words[-1]]
    
        input_batch.append(np.eye(n_class)[input])  # np.eye()是单位对角阵
        target_batch.append(np.eye(n_class)[target])
    
    return input_batch, target_batch
    
    # Model
    
    # [batch_size, number of steps, number of Vocabulary]
    X = tf.placeholder(tf.float32, [None, n_step, n_class])
    Y = tf.placeholder(tf.float32, [None, n_class])
    
    # [batch_size, n_step * n_class]
    input = tf.reshape(X, shape=[-1, n_step * n_class])
    H = tf.Variable(tf.random_normal([n_step * n_class, n_hidden]))
    d = tf.Variable(tf.random_normal([n_hidden]))
    U = tf.Variable(tf.random_normal([n_hidden, n_class]))
    b = tf.Variable(tf.random_normal([n_class]))
    
    tanh = tf.nn.tanh(d + tf.matmul(input, H))  # [batch_size, n_hidden]
    output = tf.matmul(tanh, U) + b  # [batch_size, n_class]
    
    cost = tf.reduce_mean(
    tf.nn.softmax_cross_entropy_with_logits_v2(logits=output, labels=Y))
    optimizer = tf.train.AdamOptimizer(0.001).minimize(cost)
    prediction = tf.argmax(output, 1)
    
    # Training
    init = tf.global_variables_initializer()
    with tf.Session() as sess:
    sess.run(init)
    
    input_batch, target_batch = make_batch(sentences)
    
    for epoch in range(5000):
        _, loss = sess.run([optimizer, cost], feed_dict={
                           X: input_batch, Y: target_batch})
        if (epoch + 1) % 1000 == 0:
            print("Epoch:{}".format(epoch + 1), "Cost:{:.4f}".format(loss))
    # Predict
    predict = sess.run([prediction], feed_dict={X: input_batch})
    
    # Test
    input = [sentence.split()[:2] for sentence in sentences]
    print([sentence.split()[:2] for sentence in sentences],
          '---->', [number_dict[n] for n in predict[0]])
复制代码

全部评论 (0)

还没有任何评论哟~