NNLM的pytorch实现和注意点

原理部分

NNLM模型示意图

  1. 为了通过前面的词预测后一个词。对于一个结构固定的模型来说,要求每个batch的输入数据的长度要一致
  2. 将索引表示的词,转化为向量表示,作为输入层,将前面词的向量拼接才一起作为输入向量,经过一个权值矩阵后,使用tanh作为激活函数,得到隐藏层中前面词的向量表示。
  3. 将隐藏层作为输入,同时也将输入层作为输入(注意点,也就是图上的绿色虚线),分别经过两个权值矩阵后相加得到输出层

代码

import torch
import torch.nn as nn
import torch.optim as optimizer
import torch.utils.data as Data

dtype = torch.FloatTensor

sentences = ['i like cat', 'i love coffee', 'i hate milk']
sentences_list = " ".join(sentences).split() # ['i', 'like', 'cat', 'i', 'love'. 'coffee',...]
vocab = list(set(sentences_list)) # 还得转list,不然那没有索引
word2idx = {w:i for i, w in enumerate(vocab)}
idx2word = {i:w for i, w in enumerate(vocab)}

V = len(vocab) # 词表大小

def make_data(sentences):
  input_data = []
  target_data = []
  for sen in sentences:
    sen = sen.split() # ['i', 'like', 'cat']
    input_tmp = [word2idx[w] for w in sen[:-1]] # 输入不包含最后一个单词转为索引
    target_tmp = word2idx[sen[-1]] # 最后一个单词的索引作为目标

    input_data.append(input_tmp)
    target_data.append(target_tmp)
  return input_data, target_data

input_data, target_data = make_data(sentences) # 还是list类型的
input_data, target_data = torch.LongTensor(input_data), torch.LongTensor(target_data) # 转为long张量
dataset = Data.TensorDataset(input_data, target_data) # 包装数据和目标张量的数据集

# 数据加载器,组合数据集和采样器,在数据集上提供单进程或多进程迭代器
'''
  batch_size:每个batch的样本数
  shuffle:True表示每个epoch重新打乱数据
'''
loader = Data.DataLoader(dataset, 2, True)

# parameters
m = 2  # 向量表示的维度
n_step = 2 # 输入向量的个数
n_hidden = 10

class NNLM(nn.Module):
  def __init__(self):
    super(NNLM, self).__init__()
    self.C = nn.Embedding(V, m)
    '''
      1. 将单词的索引转化为词向量表示后,前面所有词的词向量拼接在一起,作为输入向量,
      2. 然后经过一个(n_step * m, n_hidden)的加权矩阵 H ,转化为隐藏层的向量的表示
      3. 输出层以隐藏层作为输入,又以输入层作为输入
    '''
    self.H = nn.Parameter(torch.randn(n_step * m, n_hidden).type(dtype))
    self.d = nn.Parameter(torch.randn(n_hidden).type(dtype)) # 隐藏层生成的偏置
    self.b = nn.Parameter(torch.randn(V).type(dtype))
    self.W = nn.Parameter(torch.randn(n_step * m, V).type(dtype))
    self.U = nn.Parameter(torch.randn(n_hidden, V).type(dtype))

  def forward(self, X):
    '''
    X : [batch_size, n_step]
    '''
    X = self.C(X) # [batch_size, n_step, m] # 每个词转化为m维了
    X = X.view(-1, n_step * m) # [batch_szie, n_step * m]
    hidden_out = torch.tanh(self.d + torch.mm(X, self.H)) # [batch_size, n_hidden]
    output = self.b + torch.mm(X, self.W) + torch.mm(hidden_out, self.U)
    return output
model = NNLM()
optim = optimizer.Adam(model.parameters(), lr=1e-3)
criterion = nn.CrossEntropyLoss() # 损失函数为交叉熵损失

'''
  训练的标准流程
'''
for epoch in range(5000):
  for batch_x, batch_y in loader:
    pred = model(batch_x)  # 喂数据
    loss = criterion(pred, batch_y) # 计算损失

    if (epoch + 1) % 1000 == 0:
      print(epoch + 1, loss.item())
    optim.zero_grad()  # 清空上一步的参与更新参数值
    loss.backward() # 误差反向传播,计算参数更新值
    optim.step()  # 将参数更新值,加入到参数上

# Pred
pred = model(input_data).max(1, keepdim=True)[1]
print([idx2word[idx.item()] for idx in pred.squeeze()])

其中pytorch的训练代码是比较通用的代码

'''
  训练的标准流程
'''
for epoch in range(5000):
  for batch_x, batch_y in loader:
    pred = model(batch_x)  # 喂数据
    loss = criterion(pred, batch_y) # 计算损失

    if (epoch + 1) % 1000 == 0:
      print(epoch + 1, loss.item())
    optim.zero_grad()  # 清空上一步的参与更新参数值
    loss.backward() # 误差反向传播,计算参数更新值
    optim.step()  # 将参数更新值,加入到参数上

代码注意点

  1. 工作量主要集中在网络结构的设计上,注意输出层的输入是隐藏层和输入层
  2. 注意每次进行加权求和还有一个偏置矩阵要加
  3. 就是pytorch中的一些数据转换的函数view,squeeze等
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值