WA_automat

【NNLM】:A Neural Probabilistic Language Model

N 人看过

的任务是:根据来预测是什么单词,即用个单词来预测第个单词。

该模型通过词的分布式表示,解决了维数灾难的问题。这种方法允许每一个训练语句给模型提供关于语义相邻句子的指数级别的信息。

该模型同时学习:

  1. 每个词的分布式表示
  2. 词序列的概率函数

模型改进-模型,利用更长的上下文,在文本语料库上显示很好的效果(当然,这个很好的效果是对当时而言了,现在已经不算什么,但是这个模型推动了后来其他模型的发展)

语言模型与维数灾难

利用上述联合概率计算词语序列,会带来维数灾难的问题(参数过多,导致过拟合)

假设要建模一个有个单词的句子的联合概率分布,词表大小为,参数量为

-模型

-模型引入了马尔可夫假设:一个句子中的词仅与它之前的个词语相关。

其中:

-模型缺点:

  1. 训练语料里面有些元组没有出现过,其对应的条件概率就是,导致计算一整句话的概率为。尤其在取值较大时,这种数据稀疏导致的计算为的现象变得特别严重;
  2. 基于统计的语言模型无法把取得很大,参数量随着的增大而增大;
  3. 该模型不考虑单词之间的相似性。

模型

输入:长度为的词序列

输出:下一个词为词表中各个词的条件概率

网络结构:主体是一个三层的前馈神经网络,包括输入层,隐藏层和输出层。(本质上也是一个-模型)

第一部分:

将一个单词映射到维的向量空间,是句子中每个单词词向量拼接的结果

第二部分:从到隐藏层的映射(通过矩阵

第三部分:从隐藏层到输出层的映射(通过矩阵

第四部分:从到输出层的映射(通过矩阵

得到

最后经过函数的输出:

代码实现

import torch
import torch.nn as nn
import torch.optim as optim


class NNLM(nn.Module):
    def __init__(self, num_word, m, n_step, n_hidden):
        super(NNLM, self).__init__()
        self.num_word = num_word
        self.m = m
        self.n_step = n_step
        self.n_hidden = n_hidden
        self.C = nn.Embedding(num_embeddings=num_word, embedding_dim=m)
        self.H = nn.Parameter(torch.randn(n_step * m, n_hidden).type(torch.FloatTensor))
        self.d = nn.Parameter(torch.randn(n_hidden).type(torch.FloatTensor))
        self.U = nn.Parameter(torch.randn(n_hidden, num_word).type(torch.FloatTensor))
        self.W = nn.Parameter(torch.randn(n_step * m, num_word).type(torch.FloatTensor))
        self.b = nn.Parameter(torch.randn(num_word).type(torch.FloatTensor))

    def forward(self, input):
        x = self.C(input)
        x = x.view(-1, self.n_step * self.m)
        hidden_out = torch.tanh(torch.mm(x, self.H) + self.d)
        out = self.b + torch.mm(x, self.W) + torch.mm(hidden_out, self.U)
        return out


def make_batch(sentences):
    input_batch = []
    target_batch = []
    for sentence in sentences:
        word = sentence.split()
        input = [word_dict[w] for w in word[:-1]]
        target = word_dict[word[-1]]
        input_batch.append(input)
        target_batch.append(target)
    return input_batch, target_batch


if __name__ == '__main__':
    # 句子
    sentences = ["i like dog", "i love coffee", "i hate milk"]

    # 预处理
    word_list = " ".join(sentences).split(" ")  # 获取所有的单词
    print("未去重词表:", word_list)
    word_list = list(set(word_list))  # 去重
    print("去重词表:", word_list)
    word_dict = {w: i for i, w in enumerate(word_list)}  # 单词->索引
    print("单词索引:", word_dict)
    number_dict = {i: w for i, w in enumerate(word_list)}  # 索引->单词
    print("索引单词:", number_dict)
    num_words = len(word_dict)  # 单词总数
    print("单词总数:", num_words)
    input_batch, target_batch = make_batch(sentences)
    input_batch = torch.LongTensor(input_batch)
    target_batch = torch.LongTensor(target_batch)
    print("input_batch:", input_batch)
    print("target_batch:", target_batch)

    # 训练
    model = NNLM(num_words, 2, 2, 2)
    criterion = nn.CrossEntropyLoss()  # 使用cross entropy作为loss function
    optimizer = optim.Adam(model.parameters(), lr=0.001)  # 使用Adam作为optimizer

    for epoch in range(2000):
        # 梯度清零
        optimizer.zero_grad()
        # 计算predication
        output = model(input_batch)
        # 计算loss
        loss = criterion(output, target_batch)
        if (epoch + 1) % 100 == 0:
            print("Epoch:{}".format(epoch + 1), "Loss:{:.3f}".format(loss))
        # 反向传播
        loss.backward()
        # 更新权重参数
        optimizer.step()

    # 验证
    pred = model(input_batch).data.max(1, keepdim=True)[1]  # 找出概率最大的下标
    print("Predict:", pred)
    print([sentence.split()[:2] for sentence in sentences], "---->", [number_dict[n.item()] for n in pred.squeeze()])

文章参考文献

论文原文

本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 (CC BY-NC-ND 4.0) 进行许可。