Pytorch实现NNLM语言模型

      

# !/usr/bin/env Python3
# -*- coding: utf-8 -*-
# @version: v1.0
# @Author   : Meng Li
# @contact: [email protected]
# @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词向量模型

但是训练这个模型的主要目的不是为了得到模型的输出结果,而是得到模型对向量的封装

猜你喜欢

转载自blog.csdn.net/linxizi0622/article/details/125557780
今日推荐