NNLM (A Neural Network Language Model)

NLP: nlp-tutorial系列文章

nlp-tutorial NLP学习,文章解读,代码学习。

前言

nlp-tutorial使用pytorch / tensorflow复现经典NLP领域经典文章。本文章针对Bengio的NNLM2003文章进行学习。


1. NNLM (Neural Network Language Model)

NNLM全称A Neural Probabilistic Language Model,NLP word embedding具有里程碑意义的工作。该工作是Bengio团队的工作,Bengio也是我最敬仰的AI与计算领域的大师之一,他具有的非凡气质与专注足以使他在任何领域获得成功。

这篇文章主要面对的是curse of dimensionality,以及用条件概率描述词汇转移下的句子,这样的语言模型在训练、测试以及验证时,词的顺序差异巨大。当时比较流行的处理方法存在2种劣势,其一时n-gram(尤其是trigram)考虑的词太少,其二是词汇之间的similarity的考量缺失。据此,Bengio等提出学习一个词的分布式表征(分布)。

个人认为,考虑(1)长文本的关联性 和 (2)词汇之间的相似程度 在NLP字符与文本级别的人任务中,是一直贯彻至今的重要思路。足以见得Bengio团队的贡献。

2. 关键理论与方法

2.1 语言的统计模型

句子由单词与单词之间的顺序关系构成。比如I like cat即包含单词信息,其顺序信息也包含语义。那么单词顺序构成一个序列,序列由前到后存在条件概率(转移概率),最基本的语言模型就是用条件概率刻画:
P ^ ( w 1 T ) = ∏ t = 1 T P ^ ( w t ∣ w 1 t − 1 ) \hat{P}(w_{1}^{T}) = \prod_{t=1}^{T}\hat{P}(w_{t} |w_{1}^{t-1}) P^(w1T)=t=1TP^(wtw1t1)
代表着一个长度为T的句子的概率模型,由每个词的条件概率联合构成。该模型考虑的词太多,句子长度一旦增长一些,模型就会非常的稀疏,因而带来curse of dimentionality也就是维度灾难。

当时的一个主流方法(今天有很多地方依旧在用)的是n-gram,也就是考虑当前词在内的前n个词,是一种语言模型重要的近似方式:
P ^ ( w t ∣ w 1 t − 1 ) ≈ P ^ ( w t ∣ w t − n + 1 t − 1 ) \hat{P}(w_{t} | w_{1}^{t-1}) \approx \hat{P}(w_{t} | w_{t-n+1}^{t-1}) P^(wtw1t1)P^(wtwtn+1t1)
考虑维度灾难问题,用的比较多的特征就是trigram(n-gram |n=3)。

2.2 不足与改进思路

条件概率描绘的语言模型主要两方面不足:

  • n-gram尤其是trigram考虑的词不够多;
  • 词汇之间的相似性没有考虑;

文中给出的例子如下:

  • The cat is walking in the bedroom;
  • A dog was running in a room;
  • The cat was running in a room;
  • The dog was walking in the room;
    语料库中的句子的相似结构应该能给出word之间的联系。

文章整体提供的改进思路:
在这里插入图片描述

其中1就是embedding,可能那会还没直接称呼为embedding。向量空间的维度(m)远小于词典的大小。

2.3 Neural Model

2.3.1 Mission

学习词的表征,同时学习语言模型的概率函数,用以做单词预测。即,给出上文,预测下文单词。

2.3.2 Training Set

word sequence w 1 , … , w T w_{1}, \dots, w_{T} w1,,wT,其中 w t ∈ V w_{t} \in V wtV词属于有限大小的词典,词典提供了word的封闭性。

2.3.3 Objective Model

获得模型
f ( w t , … , w t − n + 1 ) = P ^ ( w t ∣ w 1 t − 1 ) f(w_{t}, \dots, w_{t-n+1}) = \hat{P}(w_{t}|w_{1}^{t-1}) f(wt,,wtn+1)=P^(wtw1t1)
用几何均值 1 P ^ ( w t ∣ w t − 1 ) \frac{1}{\hat{P}(w_{t}|w_{t-1})} P^(wtwt1)1,i.e. perplexity,形式为softmax,来做预测。

文章将该模型函数分解成两个部分:

  • C mapping:a ∣ V ∣ ∗ m |V| * m Vm矩阵,将单词映射到向量,参数C;
  • 下一个词 w t w_{t} wt的条件概率用C表达: f ( i , w t − 1 , … , w t − n + 1 ) = g ( i , C ( w t − 1 ) , … , C ( w t − n + 1 ) ) f(i, w_{t-1},\dots,w_{t-n+1}) = g(i, C(w_{t-1}), \dots, C(w_{t-n+1})) f(i,wt1,,wtn+1)=g(i,C(wt1),,C(wtn+1)),函数g输出一个向量,第i-th元素表示单词下一个单词是字典中i-th元素的可能性。g的形式可以是MLP或RNN等,参数记为 w w w

如下图所示:
在这里插入图片描述

2.3.4 Objective Function and Parameter

参数 θ = ( C , w ) \theta = (C, w) θ=(C,w),对应上文模型分解的两个部分。
具体的,由于输出层采用softmax处理,softmax的输入记为y,y为 y = b + W x + U t a n h ( d + H x ) y = b+Wx+Utanh(d+Hx) y=b+Wx+Utanh(d+Hx),x为 x = ( C ( w t − 1 ) , … , C ( w t − n + 1 ) ) x=(C(w_{t-1}), \dots, C(w_{t-n+1})) x=(C(wt1),,C(wtn+1)),则单层MLP模型下, θ = ( b , d , W , U , H , C ) \theta = (b,d,W, U, H, C) θ=(b,d,W,U,H,C)

优化的目标函数:
L = 1 T ∑ t l o g f ( w t , … , w t − n + 1 ; θ ) + R ( θ ) L = \frac{1}{T} \sum_{t} logf(w_{t}, \dots, w_{t-n+1}; \theta) + R(\theta) L=T1tlogf(wt,,wtn+1;θ)+R(θ)
R ( θ ) R(\theta) R(θ)为正则项。

参数更新采用SGD:
θ ← θ + ε ∂ l o g ( P ^ ( w t ∣ w t − 1 , … , w t − n + 1 ) ∂ θ \theta \leftarrow \theta + \varepsilon \frac{\partial log(\hat{P}(w_{t} | w_{t-1}, \dots, w_{t-n+1})}{\partial \theta} θθ+εθlog(P^(wtwt1,,wtn+1)
其中 ε \varepsilon ε是学习率。

2.3.5 Mixture of Models

采用0.5权重,将神经网络的预测结果与trigram概率模型的预测结果进行加权,得到最终结果的性能会更佳。此之谓混合模型。

3 模型实现

代码参考
采用pytorch实现一个简单的例子,来学习本文章的算法。
主要依赖以下库:

import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim

这里构建一个语料库:

sentences = ["i like dog", "i hate cat", 
             "she like cat", "she hate dog",
             "cat is running", "cat was running", 
            "dog is running", "dog was running",
            "i was running",
            "she was running",
            ]

最后希望不仅预测结果,也检验一下i she dog cat之间的相似性度量。

根据语料构建词典:

# build the diction of word
word_list = " ".join(sentences).split()
word_list = list(set(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 hyperparameter
n_step = 2 # 前n-1个词 
n_hidden = 2 # mlp隐藏神经元
m = 3 # embedding维度

构建batch作为输入与输出验证:

def make_batch(sentences):
    input_batch = []
    target_batch = []
    
    for sen in sentences:
        word = sen.strip().split()
        i = [word_dict[w] for w in word[:-1]]
        t = word_dict[word[-1]]
        input_batch.append(i)
        target_batch.append(t)
    
    return input_batch, target_batch

模型:

# Model
class NNLM(nn.Module):
    def __init__(self):
        super(NNLM, self).__init__()
        self.C = nn.Embedding(n_class, m)
        self.H = nn.Parameter(torch.randn(n_step * m, n_hidden).type(dtype))
        self.W = nn.Parameter(torch.randn(n_step * m, n_class).type(dtype))
        self.d = nn.Parameter(torch.randn(n_hidden).type(dtype))
        self.U = nn.Parameter(torch.randn(n_hidden, n_class).type(dtype))
        self.b = nn.Parameter(torch.randn(n_class).type(dtype))
    
    def forward(self, x):
        x = self.C(x)
        x = x.view(-1, n_step * m)
        tanh = torch.tanh(torch.mm(x, self.H) + self.d)
        output = self.b + torch.mm(x, self.W) + torch.mm(tanh, self.U)
        return output

训练过程如下:

dtype = torch.float
model = NNLM()
loss = nn.CrossEntropyLoss()
opt = optim.Adam(model.parameters(),lr=0.001)

input_batch, target_batch = make_batch(sentences)
input_batch, target_batch = torch.tensor(input_batch), torch.tensor(target_batch)

for epoch in range(5000):
    opt.zero_grad()
    output_batch = model(input_batch)
    l = loss(output_batch, target_batch)
    
    if (epoch + 1) % 1000 == 0:
        print('Epoch:', '%04d' % (epoch + 1), 'cost =', '{:.6f}'.format(l))
        
    l.backward()
    opt.step()
'''
Epoch: 1000 cost = 0.379675
Epoch: 2000 cost = 0.090831
Epoch: 3000 cost = 0.022923
Epoch: 4000 cost = 0.009555
Epoch: 5000 cost = 0.004810
'''

打印embedding层的参数以及word_dict的对应瓜系:

# embedding 参数
model.C.state_dict()
'''
OrderedDict([('weight',
              tensor([[-0.1183,  1.2940, -0.6724],
                      [-0.4286,  0.2746, -0.1696],
                      [-0.9515,  0.3163,  0.8657],
                      [-2.1839, -1.4932, -1.1263],
                      [-1.6372,  1.5592,  1.7642],
                      [ 1.6881,  1.4236, -2.3015],
                      [ 1.8511,  0.2312, -2.4064],
                      [-1.0320, -0.5917, -2.0542],
                      [ 1.0510,  2.0733,  1.7233]]))])
'''
# cos余弦距离
def dist(x, y):
    return np.dot(x, y) / (np.linalg.norm(x) * np.linalg.norm(y))
# 取i和she计算举例
dist(i, she)
'''
-0.1970258
'''
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值