# !/usr/bin/env Python3
# -*- coding: utf-8 -*-
# @version: v1.0
# @Author : Meng Li
# @contact: 925762221@qq.com
# @FILE : torch_NNLM.py
# @Time : 2022/7/1 10:03
# @Software : PyCharm
# @site:
# @Description : 自己实现的关于NNLM语料库模型
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
sentences = ['i like cat', 'i love coffee', 'i hate milk']
sentences_list = " ".join(sentences).split()
vocab = list(set(sentences_list))
word2idx = {i: j for i, j in enumerate(vocab)}
idx2word = {j: i for i, j in enumerate(vocab)}
V = len(vocab) # 词汇长度
embedding_size = 2 # Embedding的维度
hidden_size = 10
seq_len = 2
class NNLM(nn.Module):
def __init__(self):
super().__init__()
self.embed = torch.nn.Embedding(V, embedding_size)
self.out = torch.rand(V)
self.fc1 = nn.Linear(seq_len * embedding_size, hidden_size)
self.b1 = torch.rand(hidden_size)
self.relu1 = nn.ReLU()
self.fc2 = nn.Linear(hidden_size, V)
self.b2 = torch.rand(V)
self.crition = nn.CrossEntropyLoss()
def forward(self, input, target):
"""
:param input: [Batch_size, seq_len]
:param target: [Batch_size, seq_len]
:return: 这里要注意一点,当模型的输出和数据集的label不在一个区间时,比如label没有归一化,但是模型的输出归一化了
这样的话,模型的收敛速度会变慢
"""
input_embed = self.embed(input) # [Batch_size, seq_len, embeddding_size]
# [Batch_size, seq_len * embeddding_size]
input_embed = input_embed.view(-1, input_embed.size(-1) * input_embed.size(-2))
input_embed_temp = self.relu1(self.fc1(input_embed) + self.b1) # [Batch_size, hidden_size]
output = self.fc2(input_embed_temp) + self.b2
# output = torch.softmax(output, dim=-1) # [Batch_size, V]
loss = self.crition(output, target.view(-1))
predict = torch.argmax(output, dim=-1) # [Batch_size]
return predict, loss
def make_data(seq_data):
sentences = ['i like cat', 'i love coffee', 'i hate milk']
sentences_list = " ".join(sentences).split()
vocab = list(set(sentences_list))
idx2word = {i: j for i, j in enumerate(vocab)}
word2idx = {j: i for i, j in enumerate(vocab)}
train_data = []
target_data = []
for i in sentences:
train_data.append([word2idx[j] for j in i.split(" ")[:-1]])
target_data.append([word2idx[j] for j in i.split(" ")[-1:]])
return torch.LongTensor(train_data), torch.LongTensor(target_data)
class my_dataset(Dataset):
def __init__(self, input_data, target_data):
self.input_data = input_data
self.target_data = target_data
def __getitem__(self, index):
return self.input_data[index], self.target_data[index]
def __len__(self):
return self.input_data.size(0)
def train():
traindata, targetdata = make_data(sentences)
train_data_set = my_dataset(traindata, targetdata)
batch_size = 2
train_iter = DataLoader(train_data_set, batch_size, shuffle=True)
model = NNLM().train()
optim = torch.optim.AdamW(model.parameters())
for step in range(500):
for train_data, target_data in train_iter:
predict, loss = model(train_data, target_data)
optim.zero_grad()
loss.backward()
optim.step()
if step % 100 == 0:
print("loss", loss.detach().numpy())
print("source", [word2idx[j] for i in train_data.numpy() for j in i])
print("predict", [word2idx[i] for i in predict.cpu().numpy()])
if __name__ == '__main__':
train()
老规矩,先上代码
NNLM语言模型是采用神经网络方法对语料进行编码的一种方式
eg: i like cat
这里有一句话, i like cat . 假如我对每一个单词随机初始化一个维度为512的向量,A[1,1,...,512]、B[2,4,...,512]、C[6,-1,...,512],表面上来讲,这三个向量是互不相关的,因为我在初始化是随机生成的三个向量。
但是这三个向量其实应该是有其内在联系的,比如i like 后面的单词应该是名词。i 和 cat 中间的单词应该是一个动词。所有的这些我们人为应该存在的联系,都可以用神经网络来表达。
比如,我将i like 这两个单词作为网络的输入,那么网络的输入应该就是cat (在既有的语料环境下)。所以这里我根据自造的语料库(总共三句话,只是为了验证模型的可行性),构造了一个神经网络,网络层用的是两个全连接层,输出向量维度为V(V为词汇表的长度)。
对这个输出向量求最大值索引,就应该是cat对应的索引。
神经网络语言模型(NNLM),是在传统语言模型LM后提出的,是基于神经网络的黑盒将LM的条件概率最大化的模型。
模型中随机初始化了一个self.embed嵌入矩阵,通过对输入语料与embed矩阵的向量表查找,会得到关于输入语料的一个矩阵,将该矩阵在行向量方向上进行拼接,再通过两个全连接层即可。
模型的目标是根据前t-1个token预测当前token,但是因为embed矩阵是可学习的,这里我们得到的embed矩阵就是word embedding词向量模型
但是训练这个模型的主要目的不是为了得到模型的输出结果,而是得到模型对向量的封装