NNLM

NNLM:Neural Network Language Model,神经网络语言模型。源自Bengio等人于2001年发表在NIPS上的《A Neural Probabilistic Language Model一文。

理论

模型结构

任务

根据\(w_{t-n+1}...w_{t-1}\)来预测\(w_t\)是什么单词,即用\(n-1\)个单词来预测第\(n\)个单词

符号

  • \(V\):词汇的总数,即词汇表的大小
  • \(m\):词向量的长度
  • \(C\):\(V\)行,m列的矩阵表示词向量词表
  • \(C(w)\):单词w的词向量
  • \(d\):隐藏层的偏置
  • \(H\):隐藏层的权重
  • \(U\):隐藏层到输出层的权重
  • \(b\):输出层的偏置
  • \(W\):输入层到输出层的权重
  • \(h\):隐藏层的神经元个数

Data Flow

  1. 获取\(n-1\)个词的词向量,每个词向量的长度是\(m\)
  2. 进行这\(n-1\)个词向量的拼接,形成一个\((n-1)*m\)长度的向量,记做\(X\)
  3. 将\(X\)送入隐藏层,计算\(hidden_{out}=tanh(X*H+d)\)的到隐藏层的输出
  4. 将隐藏层的输出和输入的词向量同时送入输出层,计算\(y=X*W+hidden_{out}*U+b\),得到输出层\(|V|\)个节点的输出,第\(i\)个节点的输出代表下一个单词是第\(i\)个单词的概率。概率最大的单词为预测到的单词。

代码

Import依赖模块

import torch
import torch.nn as nn
import torch.optim as optim
import torch.utils.data as Data
from torch.autograd import Variable
dtype = torch.FloatTensor

声明变量

sentences = ["i like dog", "i love coffee", "i hate milk"]  # 句子数据集
n_steps = 2 # 用前几个单词来预测下一个单词,e.g. 2个
n_hidden = 2 # 隐藏层的节点个数,e.g. 2个
m = 2 # 词向量的长度

生成词表

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)

输出

未去重词表: ['i', 'like', 'dog', 'i', 'love', 'coffee', 'i', 'hate', 'milk']
去重词表: ['coffee', 'love', 'dog', 'like', 'milk', 'hate', 'i']
单词索引: {'coffee': 0, 'love': 1, 'dog': 2, 'like': 3, 'milk': 4, 'hate': 5, 'i': 6}
索引单词: {0: 'coffee', 1: 'love', 2: 'dog', 3: 'like', 4: 'milk', 5: 'hate', 6: 'i'}
单词总数: 7

模型结构

class NNLM(nn.Module):
# NNLM model architecture
def __init__(self):
super(NNLM, self).__init__()
self.C = nn.Embedding(num_embeddings = num_words, embedding_dim = m) # 词表
self.d = nn.Parameter(torch.randn(n_hidden).type(dtype)) # 隐藏层的偏置
self.H = nn.Parameter(torch.randn(n_steps * m, n_hidden).type(dtype)) # 输入层到隐藏层的权重
self.U = nn.Parameter(torch.randn(n_hidden, num_words).type(dtype)) # 隐藏层到输出层的权重
self.b = nn.Parameter(torch.randn(num_words).type(dtype)) # 输出层的偏置
self.W = nn.Parameter(torch.randn(n_steps * m, num_words).type(dtype)) # 输入层到输出层的权重 def forward(self, input):
'''
input: [batchsize, n_steps]
x: [batchsize, n_steps*m]
hidden_layer: [batchsize, n_hidden]
output: [batchsize, num_words]
'''
x = self.C(input) # 获得一个batch的词向量的词表
x = x.view(-1, n_steps * m)
hidden_out = torch.tanh(torch.mm(x, self.H) + self.d) # 获取隐藏层输出
output = torch.mm(x, self.W) + torch.mm(hidden_out, self.U) + self.b # 获得输出层输出
return output

格式化输入

def make_batch(sentences):
'''
input_batch:一组batch中前n_steps个单词的索引
target_batch:一组batch中每句话待预测单词的索引
'''
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 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)

输出

input_batch: tensor([[6, 3],
[6, 1],
[6, 5]])
target_batch: tensor([2, 0, 4])

训练

model = NNLM()

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

输出

Epoch:100 Loss:1.945
Epoch:200 Loss:1.367
Epoch:300 Loss:0.937
Epoch:400 Loss:0.675
Epoch:500 Loss:0.537
Epoch:600 Loss:0.435
Epoch:700 Loss:0.335
Epoch:800 Loss:0.234
Epoch:900 Loss:0.147
Epoch:1000 Loss:0.094
Epoch:1100 Loss:0.065
Epoch:1200 Loss:0.047
Epoch:1300 Loss:0.036
Epoch:1400 Loss:0.029
Epoch:1500 Loss:0.023
Epoch:1600 Loss:0.019
Epoch:1700 Loss:0.016
Epoch:1800 Loss:0.014
Epoch:1900 Loss:0.012
Epoch:2000 Loss:0.011

推理

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

输出

Predict: tensor([[2],
[0],
[4]])
[['i', 'like'], ['i', 'love'], ['i', 'hate']] ----> ['dog', 'coffee', 'milk']

可以和我们的数据集做对比预测准确的。

Reference

NNLM原理及Pytorch实现的更多相关文章

  1. 3D点云重建原理及Pytorch实现

    3D点云重建原理及Pytorch实现 Pytorch: Learning Efficient Point Cloud Generation for Dense 3D Object Reconstruc ...

  2. 【优化技巧】指数移动平均EMA的原理

    前言 在深度学习中,经常会使用EMA(exponential moving average)方法对模型的参数做平滑或者平均,以求提高测试指标,增加模型鲁棒性. 参考 1. [优化技巧]指数移动平均(E ...

  3. [源码解析]PyTorch如何实现前向传播(1) --- 基础类(上)

    [源码解析]PyTorch如何实现前向传播(1) --- 基础类(上) 目录 [源码解析]PyTorch如何实现前向传播(1) --- 基础类(上) 0x00 摘要 0x01 总体逻辑 0x02 废弃 ...

  4. [源码解析]PyTorch如何实现前向传播(2) --- 基础类(下)

    [源码解析]PyTorch如何实现前向传播(2) --- 基础类(下) 目录 [源码解析]PyTorch如何实现前向传播(2) --- 基础类(下) 0x00 摘要 0x01 前文回顾 0x02 Te ...

  5. [源码解析] PyTorch如何实现前向传播(3) --- 具体实现

    [源码解析] PyTorch如何实现前向传播(3) --- 具体实现 目录 [源码解析] PyTorch如何实现前向传播(3) --- 具体实现 0x00 摘要 0x01 计算图 1.1 图的相关类 ...

  6. 线性回归-Fork

    线性回归 主要内容包括: 线性回归的基本要素 线性回归模型从零开始的实现 线性回归模型使用pytorch的简洁实现   线性回归的基本要素 模型 为了简单起见,这里我们假设价格只取决于房屋状况的两个因 ...

  7. L1线性回归

    线性回归 主要内容包括: 线性回归的基本要素 线性回归模型从零开始的实现 线性回归模型使用pytorch的简洁实现 代码下载地址 https://download.csdn.net/download/ ...

  8. 空间金字塔池化(Spatial Pyramid Pooling, SPP)原理和代码实现(Pytorch)

    想直接看公式的可跳至第三节 3.公式修正 一.为什么需要SPP 首先需要知道为什么会需要SPP. 我们都知道卷积神经网络(CNN)由卷积层和全连接层组成,其中卷积层对于输入数据的大小并没有要求,唯一对 ...

  9. PyTorch-Adam优化算法原理,公式,应用

    概念:Adam 是一种可以替代传统随机梯度下降过程的一阶优化算法,它能基于训练数据迭代地更新神经网络权重.Adam 最开始是由 OpenAI 的 Diederik Kingma 和多伦多大学的 Jim ...

随机推荐

  1. Java文件字节流

    //输出和输入流 package com.kangkang.IO; import com.sun.xml.internal.ws.util.xml.CDATA; import java.io.File ...

  2. C++入门(2):为何还学C++?

    本文首发 | 公众号:lunvey 提及编程语言,最近很火的当属Python和Java,似乎C++没落了,真的是这样吗? 转行做程序员,掌握一门编程语言,也就是职业技能,我相信更多的是在乎未来发展而不 ...

  3. 剑指 Offer 58 - II. 左旋转字符串 + 简单题

    剑指 Offer 58 - II. 左旋转字符串 Offer_58_2 题目描述 java代码 package com.walegarrett.offer; /** * @Author WaleGar ...

  4. PAT-1140(Look-and-say Sequence)字符串处理

    Look-and-say Sequence PAT-1140 #include<iostream> #include<cstring> #include<string&g ...

  5. calcite 概念和架构

    1. 前言 Flink使用Calcite构造SQL引擎,那么他们 是怎么合作的? drill, hive,storm 和其他的一干apache 大数据引擎也用calcite , 那么对于同一个sql  ...

  6. 【pytest官方文档】解读fixtures - 8. yield和addfinalizer的区别(填坑)

    在上一章中,文末留下了一个坑待填补,疑问是这样的: 目前从官方文档中看到的是 We have to be careful though, because pytest will run that fi ...

  7. pandas函数的使用

    一.Pandas的数据结构 1.Series Series是一种类似与一维数组的对象,由下面两个部分组成: values:一组数据(ndarray类型) index:相关的数据索引标签 1)Serie ...

  8. C# 基础 - Enum 的一些操作

    1. int 转换成 enum public enum Suit { Spades, Hearts, Clubs, Diamonds } Suit spades = (Suit)0; Suit hea ...

  9. 分形、分形几何、数据可视化、Python绘图

    本系列采用turtle.matplotlib.numpy这三个Python工具,以分形与计算机图像处理的经典算法为实例,通过程序和图像,来帮助读者一步步掌握Python绘图和数据可视化的方法和技巧,并 ...

  10. Nodejs学习笔记(2) 阻塞/非阻塞实例 与 Nodejs事件

    1. Node.js异步编程的特点 2. 阻塞与非阻塞的实例 2.1 阻塞代码实例 2.2 非阻塞代码实例 3. Node.js的事件驱动 4. 事件循环实例 1. Node.js异步编程的特点 参考 ...