A Neural Probabilistic Language Model 论文阅读及实战

技术交流QQ群:1027579432,欢迎你的加入!

1.词向量介绍

  • 在NLP任务中,第一步首先将自然语言转化成数学符号表示。一般常用的词汇表示方法:one-hot表示,这种方法是将每个单词表示为一个很长的向量,这个向量的长度是词汇表的大小,其中绝大数元素是0,只有一个元素是1,如“男人”表示为:[0 0 0 1 0 0 0 0 0 0...],“男孩”表示为:[0 1 0 0 0 0 0 0 0 0...]。one-hot方法采用稀疏的方式进行单词的表示,非常的简洁。即为每个单词分配一个数字ID号,数字ID号对应于每个单词在词汇表中的索引。比如“国王”这个词语在词汇表中的索引是3,“男孩”这个词语在词汇表中的索引是1。
  • one-hot编码单词存在的问题:每个one-hot向量之间是相互正交的,任意两个单词之间是相互独立的,仅从one-hot表示出的词向量中无法看出两个单词之间是否有关系,即使是同义词也是相互独立。
  • 大多数NLP任务中,一般用到的词向量并不是one-hot表示出来的维数很长的词向量,而是采用一种"Distributed Representation"的表示方法来表示一种低维实数向量。这个词向量的表示一般是这样的:[0.792,-0.177,-0.107,0.109,-0.542,...],维度以5o和100维比较常见,这种向量的表示也不唯一。
  • Distributed Representation最大的贡献是让相关或相似的词,在距离上更接近。向量的距离可以是传统的欧式距离衡量,也可以使用余弦相似度cosine来衡量。

2.词向量的来历

  • Distributed representation 最早是 Hinton 在 1986 年的论文《Learning distributed representations of concepts》中提出的。Distributed representation 用来表示词,通常被称为“Word Representation”或“Word Embedding”,中文俗称“词向量”或“词嵌入”。
  • 如果用传统的稀疏表示法表示单词,在解决某些任务的时候(比如构建语言模型)会造成维数灾难[Bengio 2003]。使用低维的词向量就没这样的问题。同时从实践上看,高维的特征如果要套用 Deep Learning,其复杂度几乎是难以接受的,因此低维的词向量在这里也备受追捧。

3.词向量的训练

  • 在介绍如何训练词向量之前,必须先介绍语言模型。因为对于大多数的训练方法,都是在训练语言模型的同时,顺便得到词向量的,词向量只是训练语言模型过程中的“意外之物”。从大量未标注的普通文本数据中无监督地学习出词向量,建立一个语言模型。
  • 语言模型的作用就是检测一句话是不是正常人说出来的,语言模型意义重大。比如机器翻译、语音识别得到若干候选选项后,可以利用语言模型挑选一个尽量准确的结果。在其他NLP任务中都能用到。
  • 语言模型形像化的解释是:给定一句话,看它是自然语言的概率P(w1,w2,w3,..,wt)是多少。w1到wt依次表示这句话中的每个单词,简单的推论是:P(w1,w2,w3...,wt)=P(w1)P(w2|w1)P(w3|w1,w2)*...P(wt|w1,w2,...,wt-1),常用的语言模型都是在求P(wt|w1,w2,...,wt-1)的值,比如n-gram模型就是用P(wt|w1,w2,...,wt-1)近似代替P(w1,w2,w3...,wt)

4.A Neural Probabilistic Language Model 原理解释

  • 训练语言模型的最经典之作,要数 Bengio 等人在 2001 年发表在 NIPS 上的文章《A Neural Probabilistic Language Model》,Bengio 用了一个三层的神经网络来构建语言模型,同样也是 n-gram 模型,如下图所示。


    Neural Probabilistic Language Model原理图.png
  • 目标:上图中最下方的wt-n+1,...,wt-2,wt-1就是前n-1个单词,现在根据这已知的n-1个单词预测下一个单词wt。
  • 数学符号说明:
    • C(w):表示单词w对应的词向量,整个模型中使用一套唯一的词向量。
    • C:词向量C(w)存在于矩阵C(|V|*m)中,矩阵C的行数表示词汇表的大小;列数表示词向量C(w)的维度。矩阵C的某一行对应一个单词的词向量表示。
    • |V|:表示词汇表的大小,即语料库中的单词总数
    • m:表示词向量C(w)的维度,一般是50到100
    • w到C(w)的转化:从矩阵C中取出一行
    • d:隐藏层偏置bias(h)
    • H: 隐藏层的权重(h*(n-1)m)
    • U:隐藏层到输出层的权重(|V|*h)
    • b:输出层的偏置bias(|V|)
    • W:输入层到输出层权重(|V|*(n-1)m)
    • h:隐藏层神经元的数量
  • 网络的第一层(输入层)是将C(wt-n+1),...,C(wt-2),C(wt-1)这已知的n-1和单词的词向量首尾相连拼接起来,形成(n-1)w的向量,下面用x表示。
  • 网络的第二层(隐藏层)直接用d+Hx计算得到,d是一个偏置项。之后,用tanh作为激活函数。
  • 网络的第三层(输出层)一共有|V|个节点,每个节点yi表示下一个单词i的未归一化log概率。最后使用softmax函数将输出值y归一化成概率,最终y的计算公式如下:
    y = b + Wx + Utanh(d+Hx)
  • 最后,用随机梯度下降法把这个模型优化出来就可以了。

5.代码实现---PyTorch版本

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2019-02-26 14:15:49
# @Author  : cdl (1217096231@qq.com)
# @Link    : https://github.com/cdlwhm1217096231/python3_spider
# @Version : $Id$


import torch
import numpy as np
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.autograd import Variable

"""
1.Basic Embedding Model
    1-1. NNLM(Neural Network Language Model)
"""

dtype = torch.FloatTensor
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_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)  # 单词的总数

# NNLM parameters
n_step = 2   # 根据前两个单词预测第3个单词
n_hidden = 2  # 隐藏层神经元的个数
m = 2  # 词向量的维度


# 由于pytorch中输入的数据是以batch小批量进行输入的,下面的函数就是将原始数据以一个batch为基本单位喂给模型
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

# Model


class NNLM(nn.Module):
    def __init__(self):
        super(NNLM, self).__init__()
        self.C = nn.Embedding(n_class, embedding_dim=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)
        # x: [batch_size, n_step*n_class]
        tanh = torch.tanh(self.d + torch.mm(x, self.H))
        # tanh: [batch_size, n_hidden]
        output = self.b + torch.mm(x, self.W) + torch.mm(tanh, self.U)
        # output: [batch_size, n_class]
        return output


model = NNLM()

criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 制作输入
input_batch, target_batch = make_batch(sentences)
input_batch = Variable(torch.LongTensor(input_batch))
target_batch = Variable(torch.LongTensor(target_batch))


# 开始训练
for epoch in range(5000):
    optimizer.zero_grad()
    output = model(input_batch)
# output : [batch_size, n_class], target_batch : [batch_size] (LongTensor, not one-hot)
    loss = criterion(output, target_batch)
    if (epoch + 1) % 1000 == 0:
        print("Epoch:{}".format(epoch + 1), "Loss:{:.3f}".format(loss))
    loss.backward()
    optimizer.step()

# 预测
predict = model(input_batch).data.max(
    1, keepdim=True)[1]  # [batch_size, n_class]
print("predict: \n", predict)
# 测试
print([sentence.split()[:2] for sentence in sentences], "---->",
      [number_dict[n.item()] for n in predict.squeeze()])

6.结果

['i', 'like', 'dog', 'i', 'love', 'coffee', 'i', 'hate', 'milk']
去重后的word_list: ['coffee', 'i', 'hate', 'dog', 'love', 'milk', 'like']
Epoch:1000 Loss:0.114
Epoch:2000 Loss:0.021
Epoch:3000 Loss:0.007
Epoch:4000 Loss:0.003
Epoch:5000 Loss:0.002
predict: 
 tensor([[3],
        [0],
        [5]])
[['i', 'like'], ['i', 'love'], ['i', 'hate']] ----> ['dog', 'coffee', 'milk']
[Finished in 4.5s]

7.代码实现----TensorFlow版本

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2019-02-26 21:25:01
# @Author  : cdl (1217096231@qq.com)
# @Link    : https://github.com/cdlwhm1217096231/python3_spider
# @Version : $Id$

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]])

8.结果

['like', 'love', 'apple', 'coffee', 'hate', 'curry', 'i']
Epoch:1000 Cost:0.1147
Epoch:2000 Cost:0.0324
Epoch:3000 Cost:0.0127
Epoch:4000 Cost:0.0057
Epoch:5000 Cost:0.0029
[['i', 'like'], ['i', 'love'], ['i', 'hate']] ----> ['coffee', 'curry', 'apple']
[Finished in 7.0s]

9.参考文献

1.Yoshua Bengio, Rejean Ducharme, Pascal Vincent, and Christian Jauvin. A neural probabilistic language model. Journal of Machine Learning Research (JMLR), 3:1137–1155, 2003.
2.LICSTAR的博客
3.A Neural Probabilistic Language Model CSDN博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,417评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,921评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,850评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,945评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,069评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,188评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,239评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,994评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,409评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,735评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,898评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,578评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,205评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,916评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,156评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,722评论 2 363
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,781评论 2 351