美文网首页
NNLM 的PyTorch实现

NNLM 的PyTorch实现

作者: Jarkata | 来源:发表于2021-05-10 22:42 被阅读0次

    本文为转载,原文链接: https://wmathor.com/index.php/archives/1442/

    A Neural Probabilistic Language Model

    本文算是训练语言模型的经典之作,Bengio 将神经网络引入语言模型的训练中,并得到了词向量这个副产物。词向量对后面深度学习在自然语言处理方面有很大的贡献,也是获取词的语义特征的有效方法

    其主要架构为三层神经网络,如下图所示


    现在的任务是输入w_{t-n+1},...,w_{t-1}这前n-1个单词,然后预测出下一个单词w_t

    数学符号说明:

    • C(i): 单词w对应的词向量,其中i为词w在整个词汇表中的索引
    • C: 词向量,大小为|V| \times m的矩阵
    • |V|: 词汇表的大小,即语料库中去重后的单词个数
    • m: 词向量的维度,一般是50到200
    • H: 隐藏层的weight
    • d: 隐藏层的bias
    • U: 输出层的weight
    • b: 输出层的bias
    • W: 输入层到输出层的weight
    • h: 隐藏层神经元个数

    计算流程:

    1. 首先将输入的n-1个单词索引转为词向量,然后讲这n-1个词向量进行concat,形成一个(n-1)*w的向量,用X表示
    2. X送入隐藏层进行计算,hidden_{out}=tanh(d+X*H)
    3. 输出层共有|V|个节点,每个节点y_i表示预测下一个单词i的概率,y的计算公式为y = b+X*W+hidden_{out}*U

    代码实现(PyTorch)

    # code by Tae Hwan Jung @graykode, modify by wmathor
    import torch
    import torch.nn as nn
    import torch.optim as optim
    import torch.utils.data as Data
    
    dtype = torch.FloatTensor
    
    
    sentences = ['i like cat', 'i love coffee', 'i hate milk']
    
    token_list = " ".join(sentences).split()
    # ['i', 'like', 'cat', 'i', 'love'. 'coffee',...]
    vocab = list(set(token_list))
    word2idx = {w:i for i, w in enumerate(vocab)}
    # {'i':0, 'like':1, 'dog':2, 'love':3, 'coffee':4, 'hate':5, 'milk':6}
    idx2word = {i:w for i, w in enumerate(vocab)}
    # {0:'i', 1:'like', 2:'dog', 3:'love', 4:'coffee', 5:'hate', 6:'milk'}
    
    V = len(vocab) # number of Vocabulary, just like |V|, in this task |V|=7
    
    # NNLM(Neural Network Language Model) Parameter
    n_step = len(sentences[0].split())-1 # n-1 in paper, look back n_step words and predict next word. In this task n_step=2
    n_hidden = 2 # h in paper
    m = 2 # m in paper, word embedding dim
    

    由于 PyTorch 中输入数据是以 mini-batch 小批量进行的,下面的函数首先将原始数据(词)全部转为索引,然后通过 TensorDataset()DataLoader() 编写一个实用的 mini-batch 迭代器

    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]] # [0, 1], [0, 3], [0, 5]
            target_tmp = word2idx[sen[-1]] # 2, 4, 6
    
            input_data.append(input_tmp) # [[0, 1], [0, 3], [0, 5]]
            target_data.append(target_tmp) # [2, 4, 6]
        return input_data,target_data
    
    input_data,target_data = make_data(sentences)
    input_data,target_data = torch.LongTensor(input_data),torch.LongTensor(target_data)
    
    dataset = Data.TensorDataset(input_data, target_data)
    loader = Data.DataLoader(dataset, 2, True)
    

    模型定义部分

    class NNLM(nn.Module):
        def __init__(self):
            super(NNLM,self).__init__()
            self.C = nn.Embedding(V,m) # embedding lookup : [V,m]
            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):
            '''
            :param X: [batch_size, n_step]
            :return:
            '''
            X = self.C(X) # [batch_size,n_step,m]
            X = X.view(-1,n_step*m) # [batch_size,n_step*m]
            hidden_out = torch.tanh(self.d+torch.mm(X,self.H))
            output = self.b + torch.mm(X,self.W)+torch.mm(hidden_out,self.U)
            return output
    
    

    nn.Parameter()的作用是将该参数添加进模型中,使其能够通过 model.parameters()找到、管理、并且更新。更具体的来说就是:

    1. nn.Parameter()nn.Module一起使用时会有一些特殊的属性,其会被自动加到 Module 的 parameters() 迭代器中
    2. 使用很简单:torch.nn.Parameter(data, requires_grad=True),其中 data 为 tensor

    简单解释一下执行 X=self.C(X) 这一步之后 X 发生了什么变化,假设初始 X=[[0, 1], [0, 3]]

    通过 Embedding() 之后,会将每一个词的索引,替换为对应的词向量,例如 love 这个词的索引是 3,通过查询 Word Embedding 表得到行索引为 3 的向量为 [0.2, 0.1],于是就会将原来 X 中 3 的值替换为该向量,所有值都替换完之后,X=[[[0.3, 0.8], [0.2, 0.4]], [[0.3, 0.8], [0.2, 0.1]]]

    # Training
    for epoch in range(5000):
      for batch_x, batch_y in loader:
        optimizer.zero_grad()
        output = model(batch_x)
    
        # output : [batch_size, n_class], batch_y : [batch_size] (LongTensor, not one-hot)
        loss = criterion(output, batch_y)
        if (epoch + 1)%1000 == 0:
            print('Epoch:', '%04d' % (epoch + 1), 'cost =', '{:.6f}'.format(loss))
    
        loss.backward()
        optimizer.step()
    
    # Predict
    predict = model(input_batch).data.max(1, keepdim=True)[1]
    
    # Test
    print([sen.split()[:n_step] for sen in sentences], '->', [number_dict[n.item()] for n in predict.squeeze()])
    

    参考文献

    A Neural Probabilitic Language Model 论文阅读及实战
    NLP-tutorial

    相关文章

      网友评论

          本文标题:NNLM 的PyTorch实现

          本文链接:https://www.haomeiwen.com/subject/ywlwdltx.html