NLP、language model、lstm、attention model

参考代码:https://github.com/yunjey/pytorch-tutorial/tree/master/tutorials/02-intermediate/language_model

lstm简单介绍:https://zhuanlan.zhihu.com/p/24720659

一、language model

语言模型的工作是计算一句话是否为正常的语言。
注意模型中:每个短句无重叠。
batch_size: 样本分批训练的批次大小
seq_len:是序列长度(人为定义大小,一般取30),就是默认的语句长度
corpus:是字典集合,语料库。


第一步:将所有文本切词添加到语料库

#-*- coding:utf-8 -*-
import torch
import os

class Dictionary(object):
    def __init__(self):
        self.word2idx = {}
        self.idx2word = {}
        self.idx = 0
    
    def add_word(self, word):
        if not word in self.word2idx:
            self.word2idx[word] = self.idx
            self.idx2word[self.idx] = word
            self.idx += 1
    
    def __len__(self):
        return len(self.word2idx)
    
class Corpus(object):
    def __init__(self, path='./data'):
        self.dictionary = Dictionary()
        self.train = os.path.join(path, 'train.txt')
        self.test = os.path.join(path, 'test.txt')

    def get_data(self, path, batch_size=20):
        # Add words to the dictionary
        with open(path, 'r') as f:
            tokens = 0
            for line in f:
                words = line.split() + ['<eos>']
                tokens += len(words)
                for word in words: 
                    self.dictionary.add_word(word)  
        
        # Tokenize the file content
        # 长整型向量,保存整个文章中词的idx信息
        ids = torch.LongTensor(tokens) 
        token = 0
        with open(path, 'r') as f:
            for line in f:
                words = line.split() + ['<eos>']
                for word in words:
                    ids[token] = self.dictionary.word2idx[word]
                    token += 1
        num_batches = ids.size(0) // batch_size
        ids = ids[:num_batches*batch_size]
        return ids.view(batch_size, -1)
# Load Penn Treebank Dataset
train_path = './data/train.txt'
sample_path = './sample.txt'
corpus = Corpus()
ids = corpus.get_data(train_path, batch_size) # 样本按顺序单词在字典库中的下标
vocab_size = len(corpus.dictionary) # 整个字典库的收录单词大小
num_batches = ids.size(1) // seq_length

第二步:将文本语料无交叉构造成训练数据矩阵

data矩阵的每一行表示一句话(长度固定30),其中包含分句符。每个值为该句话中该单词所在语料库中的id值(一般用idx表示)。
target矩阵的每一行对应data矩阵每一行一句话向右平移后的(长度为30)的语句。
datas = Variable(ids[:, i:i+seq_length]) 
targets = Variable(ids[:, (i+1):(i+1)+seq_length].contiguous())

注: ids的大小为[ batchsize*(seq_length*t)  ] 应该分t组数据(loader)处理。

第三步:建立模型

模型的输入为上述矩阵,输出为原语句平移1个单位后的语句。

第四步:查看语言模型预测效果

二、pytorch 的Embedding与LSTM接口用法

官方文档: http://pytorch-cn.readthedocs.io/zh/latest/package_references/torch-nn/#recurrent-layers

1、nn.Embedding接口初始化

class Embedding(Module):
    def __init__(
        self, 
        num_embeddings, # vocabulary_size len(word2idx.keys()) 
        embedding_dim, # 最终生成的word2vec的维度
        padding_idx=None,
        max_norm=None, 
        norm_type=2, 
        scale_grad_by_freq=False,
        sparse=False):

2、nn.LSTM接口初始化

class RNNBase(Module):
    def __init__(
        self,
        mode,
        input_size, # word2vec的维度
        hidden_size, # 隐藏层一个节点的维度
        num_layers=1, # 隐藏层的个数,层数
        bias=True,
        batch_first=False,
        dropout=0,
        bidirectional=False):

范例:

lstm = nn.LSTM(input_size=50, hidden_size=1024, num_layers=2, batch_first=True)
input = Variable(torch.randn(1000, 40, 50))
# h0 = Variable(torch.randn(3, 1000, 1024))
# c0 = Variable(torch.randn(3, 1000, 1024))
output, (hn,cn) = lstm(input)
print( output.size(),hn.size(),cn.size() )
# output.size()=(1000*40*1024) hn.size()=2*1000*1024 cn.size()=2*1000*1024

上述例子反映: 

    一个batch有1000个句子, 句子中单词的word2vec的维度为50,句子最大长度为40,不够40的补,多于40的截断。

    最后输出的output的size为:1000个句子*句子最大长度为40*每个单词的size为1024, hn的size为:2个隐层*句子最大长度1000*每个单词的维度1024。    

该LSTM设计结构如下:

     输入单词的维度(input_size)=50, 隐藏层有2层, 隐节点的维度为1024。

三、attention model

参考网址: https://zhuanlan.zhihu.com/p/22081325 这里的4个公式一定要看
attention model翻译模型整体结构如下:



pytorch的60分钟程序是按顺序执行,翻译对。
如:
法语:"vous etes fort ingenieuse . ",转换成下标形式为[8,1150,97,32,59,1];存在于法语vocabulary集
对应英语: " you are vary clever . "转换成对应下标形式为[77,1150,4,8,11,1];存在于英语vocabulary集
注:这里的是双集合,1代表<EOS>句子末尾。
翻译模型句子1和2长度可以不同,  设置全局变量MAX_Length,按0补全空缺位置。
4个重要公式
train方法是单个epoch执行的部分
# 每个epochs中执行的部分,每个epochs执行一个语句
def train(input_variable, target_variable, encoder, decoder, encoder_optimizer, decoder_optimizer, criterion, max_length=MAX_LENGTH):
    # criterion:负对数似然损失函数
    encoder_hidden = encoder.initHidden() # 初始化隐藏层
    encoder_optimizer.zero_grad()         # 初始化优化器
    decoder_optimizer.zero_grad()         # 初始化优化器
    input_length = input_variable.size()[0] # 单个输入语句
    target_length = target_variable.size()[0] # 单个标签正确翻译后的语句
    
    # 站位符 输出长度匹配
    encoder_outputs = Variable( torch.zeros(max_length, encoder.hidden_size) )
    encoder_outputs = encoder_outputs.cuda() if use_cuda else encoder_outputs


    loss = 0
    ##############################  Encoder  ##############################
    # 获取encoder对于单个语句的输出
    for ei in range(input_length):
        encoder_output, encoder_hidden = encoder( input_variable[ei], encoder_hidden)
        encoder_outputs[ei] = encoder_output[0][0]

    ############################## Attention Decoder ##############################
    # decoder 是一步一步按照顺序执行seq中的word
    decoder_input = Variable(torch.LongTensor([[SOS_token]]))
    decoder_input = decoder_input.cuda() if use_cuda else decoder_input
    decoder_hidden = encoder_hidden
    # 随机算法
    use_teacher_forcing = True if random.random() < teacher_forcing_ratio else False
    if use_teacher_forcing:
        # Teacher forcing: Feed the target as the next input
        for di in range(target_length):
            decoder_output, decoder_hidden, decoder_attention = decoder( decoder_input, decoder_hidden, encoder_output, encoder_outputs)
            loss += criterion(decoder_output, target_variable[di])  # 计算误差
            decoder_input = target_variable[di]  # Teacher forcing
    else:
        # Without teacher forcing: use its own predictions as the next input
        for di in range(target_length):
            decoder_output, decoder_hidden, decoder_attention = decoder( decoder_input, decoder_hidden, encoder_output, encoder_outputs)
            topv, topi = decoder_output.data.topk(1)
            ni = topi[0][0]
            decoder_input = Variable(torch.LongTensor([[ni]]))
            decoder_input = decoder_input.cuda() if use_cuda else decoder_input
            loss += criterion(decoder_output, target_variable[di])  # 计算误差
            if ni == EOS_token:  break
    loss.backward()
    encoder_optimizer.step()
    decoder_optimizer.step()
    return loss.data[0] / target_length

Encoder



注: 输入input是word对应的idx值。该程序是按单词一次一次输入到encoder中,最后保留一个hidden层 为Decoder做准备。
decoder_hidden = encoder_hidden

Simple Decoder



注:decode部分是按照word一个一个循环训练的
并且每次输入都要使用上一次的输出作为参数。

Attention Decoder


注:
这里的input是单个word,最开始的word是<SOS>句子的开始
encoder_outputs:是encoder模块层所有的输出。
bmm:对存储在两个批batch1和batch2内的矩阵进行批矩阵乘操作。



猜你喜欢

转载自blog.csdn.net/u012969412/article/details/77767541