n-gram回顾

在上一篇笔记语言模型(一)—— 统计语言模型n-gram语言模型中我们已经了解到了n-gram的不足,在理解神经网络语言模型之前,我们有必要简单地回顾一下n-gram模型的几个特点:

  • 基于统计的语言模型,是一种离散型的语言模型,所以泛化能力差
  • 参数量大,随着 n 的增大,参数空间呈指数增长容易出现维度灾难的问题;巨大的参数量也使得模型无法与n-1个词之外的词建立联系。即不能进行上下文的回溯,不能解决上下文物主代词指代问题。
  • 数据稀疏除了带来数据空间增大的问题之外,还有一个问题:无法表征词语之间的相似关系

NNLM的理解

鉴于上面的问题,人们开始尝试用神经网络来建立语言模型,最经典的无疑是Bengio 的文章A Neural Probabilistic Language Model,文章中提出了如下图所示的前馈神经网络结构:

先从整体上看,上述模型属于比较简单而传统的神经网络模型,主要由输入层-隐藏层-输出层组成,经过前向传播和反向传播来进行训练。我个人觉得,理解上面这张图的关键点在于理解词向量,即图中的C(wt-n+1)…C(wt-1)等。那我们就从词向量的映射开始,一层一层往上看。

前向传播

从单词到输入层

在最最开始,我们必须要重提一下语言模型的目的:判断一句话是不是人话。途径是啥?——通过前面的词预测后面的词。而其实神经网络语言模型是基于n-gram演变而来的,即核心是根据前n-1个词预测第n个词,那么我们模型最开始的输入就是前n-1个词。

那么我们又是怎么来表征前n-1个词的呢?答案是词向量。

感觉越说越懵是不是,那么词向量是什么?一个词又是怎么变成词向量的呢?我们慢慢来看。

从Ont-hot到Word Embedding:

词语转化为数字的最简单的形式就是One-hot,简单来说就是假设有一个大小为V的固定排序的词表,里边包含V个词,假设第二个词是“电视”,那么我们用一个维度为V的特征向量表达就是[0,1,0,0,…,0],即该词语在词表中的位置对应在特征向量中的位置的值为1,其他位置都为0。One-hot编码有一个最大的问题就是数据稀疏问题,当词表很大(比如我们现在有一个含80000个词的词表)时,数据稀疏会让整个计算量都变得很大。且词语之间的关联关系得不到表达。

那么词向量(Word Embedding)又是什么呢?人们也叫他词嵌入,就是说我现在不用One-hot那样的稀疏向量来表征我这个词了,我就用一个低维度的向量来表征我这个词,当你很难理解的时候你可以说它是玄学,反正世界上就有这么一个向量能表征我选择的这个词,并且我词表里的每一个词都有对应的表征向量。这个词向量又是怎么取得的呢?

我们给定一个词表征的矩阵C,这个C的维度是V*m,即V行,m列。V是词表的大小,也就是每一行代表了词表里的一个词;m是我们自己定的词向量的维度,比如说对于一个80000个词的词表,原先我要用80000维的One-hot向量来表征“电视”这个词,现在我想就用一个100维的向量来表征,m就是100。(事实上我们常用的就是50或者100)

那么我们用“电视”的One-hot向量[0,1,0,0,…,0]乘以上面说的矩阵C会发生什么?会得到一个m维的向量啊!这就是我们说的词向量,可以看这个过程:

那我们就会想了,你不就是想从C里边取一行么,用的了那么麻烦么,直接给个词在词表中的索引再去C里边按索引取出对应行不就完事了吗?你说的对!你看最开始那张模型图中,作者就是这么干的:

这样我们就得到了前n-1个词的词向量:C(wt-n+1)…C(wt-1);这样我们就完成了从词语到向量的映射。开不开心,但。。。是不是感觉哪里不太对?我们说给定一个矩阵C,这个C怎么来的?事实上,矩阵C是我们随机初始化来的(或者根据一些先验数据初始化来的),也就是说,在神经网络语言模型中,词向量作为一个内部参数,跟神经网络中的其他内部参数一样都是先有一个随机初始化值,正向传播后计算损失函数再反向传播更新这些参数。这也就要求神经网络语言模型是有监督的学习,词向量是学习得到的副产物,也是模型内化的一部分。

词向量全连接作为输入:

得到上面单个词向量之后,我们要将n-1个词向量做一个全连接,即把这n-1个词向量首尾相接地拼起来得到最终的输入x:

从输入层到隐藏层

这里的隐藏层就是一个很普通的神经网络的做法,权重H乘以输入加上偏置d,再加一个tanh函数作激活函数,就得到了隐藏层:
t a n h ( d + H x ) tanh(d+Hx) tanh(d+Hx)
也就是图中的:

从隐藏层到输出层

我们先计算由隐藏层到输出层未归一化的输出值y1,这里就是一个简单的线性变化:(为了方便理解,这里的描述方式跟原文不太一样,我这里将隐藏层到输出层与输入层到输出层这两部分拆开描述,不影响最后的结果。)
y 1 = U t a n h ( d + H x ) + b 1 y_1=Utanh(d+Hx)+b_1 y1=Utanh(d+Hx)+b1
这里的U是隐藏层到输出层的参数,b1代表这一部分的偏置项。

在图中表示为:

从输入层到输出层

作者原文中还加入了从输入层到输出层的直连,也是一个线性变换,这作为一个技巧的使用,也可以不用。这一部分的输出值y2可以表示为:
y 2 = W x + b 2 y_2 = Wx+b_2 y2=Wx+b2
W和b2分别是这一部分的权重和偏置项。整个过程对应图中:

输出层

由上面的两部分输出值我们可以得到最终的y:
y = y 1 + y 2 = b + W x + U t a n h ( d + H x ) y = y_1+y_2 =b+Wx+Utanh(d+Hx) y=y1+y2=b+Wx+Utanh(d+Hx)
再将y经过一个softmax函数做概率归一化,便能得到一个维度为V的概率向量,这就是我们的输出了。(找到最大的概率所在位置的索引,结合词表我们就能得到我们的预测值了)

模型训练与反向传播

模型训练的目标是最大化以下似然函数:

其中

是模型的所有参数,R是正则化项。

反向传播就是根据loss值更新参数的过程,这里不再赘述。此外模型各个参数的维度也可自行推出或参考其他文章。

代码实现

(以下代码摘自A Neural Probabilistic Language Model 论文阅读及实战,感谢原作者)

#!/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 Model1-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# Modelclass 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 outputmodel = 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()])

运行结果:

['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]

NNLM的小结

神经网络语言模型(NNLM)通过构建神经网络的方式来探索和建模自然语言内在的依赖关系。优缺点如下:

优点:

  • 词向量是可以自定义维度的,维度并不会因为新扩展词而发生改变,词向量能够很好的根据特征距离度量词与词之间的相似性;
  • 好的词向量能够提高模型泛化能力;
  • 相比于n-gram,通过词向量的降维,减小了参数空间,减少了计算量。

缺点:

  • 参数较多,模型训练时间长;
  • 神经网络黑盒子,可解释性较差。

参考文章:

A Neural Probabilistic Language Model

NNLM(神经网络语言模型)

理解 NNLM

神经网路语言模型(NNLM)的理解

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

A Neural Probabilistic Language Model ------阅读笔记


熬夜写文,您的点赞、收藏是对我最大的鼓励,加油!

语言模型(二)—— 神经网络语言模型(NNLM)相关推荐

  1. NLP基础:n-gram语言模型和神经网络语言模型

    文章目录 语言模型的计算 n-gram 语言模型 n-gram 平滑技术 神经网络语言模型(NNLM) 基本思想 神经网络语言模型小结 语言模型评价指标-困惑度 语言模型是自然语言处理中的重要技术,假 ...

  2. NLP自然语言处理:神经网络语言模型(NNLM)

    目录 一.传统语言模型 1.1 稀疏性 1.2 泛化能力差 二.神经网络语言模型 2.1 前馈神经网络模型(FFLM) 2.2 循环神经网络模型(RNNLM) 2.2.1 循环神经网络模型示例 2.2 ...

  3. NNLM神经网络语言模型简单实现词语预测(含python代码详解)

    文章目录 一.NNLM简单介绍 二.NNLM词语预测代码 1. 导入包 2. 文本数据处理 3. 自定义mini-batch迭代器 4. 定义NNLM模型 1. 定义模型结构 2. NNLM参数设置 ...

  4. 基于神经网络语言模型的词向量生成(NNLM)详解

    深度学习入门小菜鸟,希望像做笔记记录自己学的东西,也希望能帮助到同样入门的人,更希望大佬们帮忙纠错啦~侵权立删. 目录 一.NNLM的网络结构分析 二.NNLM的代码实现 一.NNLM的网络结构分析 ...

  5. 【NLP】神经网络语言模型(NNLM)

    背景 语言模型也经常会在NLP中提出.在深度学习大行其道的今天基于神经网络的语言模型与传统定义的又有什么区别呢?语言模型在NLP中有什么意义呢?不妨沉下心,了解一下. 语言模型是一个单纯的.统一的.抽 ...

  6. 自然语言处理NLP(3)——神经网络语言模型、词向量

    在上一部分中,我们了解到了统计语言模型,n-gram模型以及语料库的基本知识: 自然语言处理NLP(2)--统计语言模型.语料库 在这一部分中,我们将在此基础上介绍神经网络语言模型以及词向量的相关知识 ...

  7. 神经网络语言模型详解

    1 简介 语言模型是自然语言处理领域的基础问题,其在词性标注.句法分析.机器翻译.信息检索等任务中起到了重要作用.简而言之,统计语言模型表示为:在词序列中,给定一个词和上下文中所有词,这个序列出现的概 ...

  8. 手把手教 | 使用Keras构造日文的神经网络语言模型(内附源码)

    作者:GjZero 标签:Python, Keras, 语言模型, 日语 本文约2400字,建议阅读10分钟. 本文介绍了语言模型,并介绍如何用MeCab和Keras实现一个日文的神经网络语言模型.( ...

  9. 用飞桨做自然语言处理:神经网络语言模型应用实例

    允中 发自 凹非寺  量子位 报道 | 公众号 QbitAI 编者按: 语言模型的身影遍布在NLP研究中的各个角落,想要了解NLP领域,就不能不知道语言模型. 想要让模型能落地奔跑,就需借助深度学习框 ...

最新文章

  1. 深度分析typedef--定义自己的数据类型
  2. 数十名工程师作战5天,阿里达摩院连夜研发智能疫情机器人
  3. 用Vue撸一个『A-Z字母滑动检索菜单』
  4. UVa1467 Installations(贪心)
  5. vue xlsx 导入导出_只需三步vue实现excel文件数据提取并存为json数据
  6. 一个Win32 API实例类(代码收集)
  7. Linux的软硬链接ln
  8. IOI1999 花店橱窗布置
  9. 游戏角色制作行业标准? 快来看看吧
  10. php实现单个用户禁止重复登录,防止同一用户同时登陆
  11. java 反射解析xml_java反射获取xml元素
  12. 数学到底有多难难难难?看完这个,瞬间觉得智商都提高了!
  13. html 按钮光束,图文详解,原来3dmax光束特效的制作这么简单!
  14. 5.2 Array类型
  15. vue3 中使用动画技术
  16. 零件缝隙平行线距离检测4
  17. 58 集团面向亿级用户 IM 长连接服务设计与实践
  18. java applet插件下载_Java Applet.zip
  19. ThreeJS自带网格线
  20. 【Unity3D】制作进度条——让Image同时具有Filled和Sliced的功能

热门文章

  1. opencv-python人脸识别
  2. Java单点登录技术选型与对比 kisso, sa-token
  3. 第十二节段 -- 爬虫10:【Scarpy 框架04:练习】
  4. centos 7 升级 sudo
  5. 038Node.js后端服务处理端口号被占用的解决方案portfinder
  6. 微信H5页面兼容性解决方案
  7. 多国雪藏外星人秘密?:人类身上藏着外星人秘密?
  8. PostgreSql 执行计划
  9. sql 中的left join 的坑请大家绕着走
  10. jquery文档就绪_在jQuery中,下列关于文档就绪函效的写法错误的是( )