精华内容
下载资源
问答
  • 使用RNN的机器翻译 GRU LSTM 复习 Word2Vec: Glove: Nnet&Max-margin:, Multilayer Nnet&Backprop:, RNN:, Cross Entropy: Mini-batch SGD: 使用RNN的机器翻译 红圈所示特征...

    目录

    复习

    使用RNN的机器翻译

    GRU

    LSTM


    • 复习

    Word2Vec:J_{t}(\theta )=log\sigma (u_{o}^{T}v_{c})+\sum_{j\sim P(w)}^{ }[log\sigma (-u_{j}^{T}v_{c})]

    Glove:J(\theta )=\frac{1}{2}\sum_{i,j=1}^{W}f(P_{ij})(u_{i}^{T}v_{j}-logP_{ij})^{2}

    Nnet&Max-margin:s=U^{T}f(Wx+b)J=max(0,1-s+s_{c})

    Multilayer Nnet&Backprop:\delta ^{(l)}=((W^{(l)})^{T}\delta ^{(l+1)})\circ {f}'(z^{(l)})\frac{\partial }{\partial W^{(l)}}E_{R}=\delta ^{(l+1)}(a^{(l)})^{T}+\lambda W^{(l)}

    RNN:h_{t}=\sigma (W^{hh}h_{t-1}+W^{(hx)}x_{t})\widehat{y_{t}}=softmax(W^{(S)}h_{t})

    Cross Entropy:J^{(t)}(\theta )=-\sum_{j=1}^{|V|}y_{t,j}log\widehat{y}_{t,j}

    Mini-batch SGD:\theta ^{new}=\theta ^{old}-\alpha \bigtriangledown _{\theta }J_{t:t+B}(\theta )

    • 使用RNN的机器翻译

    红圈所示特征表示必须能捕捉整个原文短语的语义,但是RNN无法记住太久之前的事情,大概五六个单词就到极限了。所以这不是个实用的模型。

    在这个最简单的模型中,

    Encoder是:

    h_{t}=\phi (h_{t-1},x_{t})=f(W^{(hh)}h_{t-1}+W^{(hx)}x_{t})

    Decoder是:

    h_{t}=\phi (h_{t-1})=f(W^{(hh)}h_{t-1})

    y_{t}=softmax(W^{(S)}h_{t})

    最小化所有训练实例上的交叉熵误差:

    max_{\theta }\frac{1}{N}\sum_{n=1}^{N}logp_{\theta }(y^{(n)}|x^{(n)})

    softmax分类器中必须有个代表句子终止的“单词”,不然模型会无休止地输出下去。

    但神经网络机器翻译模型没有这么简单,必须加一些拓展。

    1.编码器和解码器训练不同的权值矩阵

    红蓝代表不同的权值。

    2.decoder中的隐藏层的输入来自3个方面:

    • 前一个时刻的隐藏层
    • encoder的最后一个隐藏层(c=h_{T})
    • 前一个预测结果\widehat{y}_{t-1}

    decoder变为:h_{D,t}=\phi _{D}(h_{t-1},c,y_{t-1})

    可以辅助训练softmax的权值矩阵,防止模型重复生成同一个单词。

    上图还有一个复杂版本,表达的是同一个意思:

    带黑点的表示离散的向量表示,否则表示连续的向量空间。

    3.使用深层RNN或双向RNN

    4.不再用A B C→X Y作为训练实例,而是逆转原文词序:C B A→X Y

    因为A更可能翻译为X,而梯度消失导致A无法影响输出,倒过来A离输出近一些。逆转词序不会带来“语法语义上的改变”,因为模型学习的就是如何从逆序的原文翻译顺序的译文。但相应的,C就离Y更远了。

    • GRU

    引入Gated Recurrent Units (GRU),这种单元可以让模型学习何时遗忘从而将记忆保持很久、允许误差根据输入的不同而不同。

    标准的RNN直接计算隐藏层:

    h_{t}=f(W^{(hh)}h_{t-1}+W^{(hx)}x_{t})

    GRU先根据当前输入的词向量和隐藏层计算一个update gate(另一个层):

    z_{t}=\sigma (W^{(z)}x_{t}+U^{(z)}h_{t-1})

    利用相同的方法不同的权值计算reset gate

    r_{t}=\sigma (W^{(r)}x_{t}+U^{(r)}h_{t-1})

    然后利用reset gate计算新的记忆:

    \widetilde{h}_{t}=tanh(Wx_{t}+r_{t}\circ Uh_{t-1})

    这里的意思是,之前的记忆由reset gate控制,如果reset gate元素都是0,则遗忘之前的事情。

    而update gate的作用是调节最后的更新,到底时刻t的记忆多少来自之前,多少来自当前:

    h_{t}=z_{t}\circ h_{t-1}+(1-z_{t})\circ \widetilde{h}_{t}

    如果z_{t}是单位向量的话,则隐藏层只是复制前一个时刻的隐藏层,梯度不发生变化(衰减)。

    把上述公式写在一起非常直观:

    示意图:

    上图中,虚线代表某个gate的调节作用。隐藏层取决于上一个时刻的隐藏层和新的记忆,而update gate负责调节它们的比例,reset gate和输入共同决定新的记忆。

    • LSTM

    LSTM的计算可以分为如下步骤:

    1. New memory generation 与GRU的New memory generation类似。使用当前词语x_{t}和之前的隐状态h_{t-1}来生成新的记忆\widetilde{c}_{t}。于是新记忆里面就包含了当前词语x^{(t)}的属性。
    2. Input Gate 使用当前词语和之前的隐状态决定当前词语是否值得保留来gate新记忆,这个“是否”是通过i_{t}来体现的。
    3. Forget Gate 与input gate类似,只不过它不是用来衡量输入单词的有用与否,而是衡量过去的记忆对计算当前记忆有用与否。它接受输入单词和上一刻的隐状态产生输出f_{t}
    4. Final memory generation 根据forget gate的建议f_{t}来遗忘过去的记忆c_{t-1}。类似地,根据input gate的建议i_{t}来gate新的记忆\widetilde{c}_{t},然后把两者加起来得到最终记忆c_{t}
    5. Output/Exposure Gate 这是GRU中不显式存在的门,用处是将最终记忆与隐状态分离开来。记忆c_{t}中的信息不是全部都需要存放到隐状态中,隐状态是个很重要的使用很频繁的东西,LSTM中每个gate都需要隐状态的参与。Output Gate产生o_{t},用来gate记忆的tanh激活值。

    用于机器翻译

    输入原文序列,将最后一个时刻的隐藏层向量PCA降维后可视化:

    发现无论词序如何,意义相同的句子在向量空间中更接近。

    进一步改进:更多门

    展开全文
  • 机器翻译引擎的基本原理 摘自:infoq 谷歌机器翻译 Zero-shot:零次 Training:训练 Google Neural Machine Translation:谷歌神经机器翻译 我们每天都在使用不同的技术,但却不知道它们的工作原理。事实上,...

    机器翻译引擎的基本原理  摘自:infoq

    谷歌机器翻译

    Zero-shot:零次

    Training:训练

    Google Neural Machine Translation:谷歌神经机器翻译

    我们每天都在使用不同的技术,但却不知道它们的工作原理。事实上,了解机器学习引擎并不容易。Statsbot团队希望通过本博客中的数据故事使机器学习更加清晰易懂。今天,我们来探讨一下机器翻译并解释谷歌翻译算法如何工作。

    几年前,翻译未知语言文本是非常耗时的。使用简单的词汇逐词逐字翻译之所以困难,是由于以下两个原因:1)读者必须知道语法规则,2)在翻译整个句子时,需要记住所有语言版本。

    现在,我们不需要太多的努力就可以将短语、句子、甚至是大段文本放在谷歌翻译中来翻译。但大多数人并不在意机器学习翻译的引擎如何工作。这篇文章专为那些在意的人而写。

    深度学习翻译的问题

    如果谷歌翻译引擎将短句的译文保留,则它将会由于可能出现的各种变数而无法工作。最好的方法是教会计算机语法规则,并让它根据这些规则来翻译句子。这听起来很简单。

    如果你曾经学过外语,你就知道规则总是有很多例外的。当尝试捕获程序中的所有这些规则、例外和例外中的例外时,翻译的质量就会下降。

    现代机器翻译系统采用了不同的方法:它们通过分析大量的文档来分配文本中的规则。

    创建一个属于你自己的简单的机器翻译器可以作为任何数据科学家应聘简历中的一个重大项目

    让我们来研究一下被称之为“黑盒”的机器翻译器中隐藏的东西。深层神经网络可以在非常复杂的任务(语音或视觉对象识别)中取得优异的效果,但尽管十分灵活,它们只能用于输入维数和目标维数固定的任务。

    循环神经网络

    而这正是长短期记忆网络(LSTM)派上用场的地方,LSTM能帮助我们处理不能被先验知道的序列。

    LSTM是一种特殊的循环神经网络(RNN),它能够学习长期性的依赖关系。所有RNN看起来都像一连串的重复模块。

    一个展开的循环神经网络

    因此,LSTM将数据从一个模块传输到另一个模块,另外,为了生成Ht,我们不仅使用Xt,还使用所有之前的输入值X。要了解有关LSTM的结构和数学模型的更多信息,请阅读这篇非常棒的文章“了解LSTM网络”。

    双向RNN

    我们的下一步是双向RNN(BRNN)。BRNN将常规RNN的神经元分成两个方向。一个方向是正向的时间或向前的状态。另一个方向是负向的时间或向后的状态。但这两个状态的输出并不与相反方向状态的输入进行连接。

    双向循环神经网络

    为什么BRNN比相对简单的RNN更好?请想象有一个9个字的句子,而我们要预测第五个字。我们可以知道前4个字,或前4个字和最后4个字。当然,第二种情况预测的质量会更好。

    序列到序列

    现在我们可以认识序列到序列模型(也称为seq2seq)了。一个基本的seq2seq模型由两个RNN组成即:处理输入的编码器网络和产生输出的解码器网络。

    序列到序列模型

    最后,我们便可以制作出自己的第一个机器翻译器了!

    但是,让我们先思考这一点。谷歌翻译目前支持103种语言,所以我们应该为每种语言提供103x102个不同的模型。当然,这些模型质量的好坏会因为语言的普及程度和训练这个网络所需的文件数量而有所不同。我们可以达到的最佳情况是使一个NN(神经网络)能将任何语言作为输入并将其翻译成任何其他语言。

    谷歌翻译

    该想法谷歌工程师在2016年底就实现了。NN的架构基于谷歌已研究的seq2seq模型之上。

    但唯一的不同是编码器和解码器之间有8层LSTM-RNN,且层与层之间仍有残留的连接,并有一些精度和速度上的调整。如果你想更深入地了解它们,请参考谷歌的神经机器翻译系统

    该方法的主要特点在于,现在谷歌翻译算法对于每对语言只使用一个系统,而不是一个庞大的集合。

    该系统在输入句子的开头需要一个“令牌”以指定你要将短语翻译成的语言。

    这一改变提高了翻译质量,甚至可以在系统从未见过的两种语言之间进行翻译,这种方法被称为“零次(Zero-Shot)翻译”。

    什么是更好的翻译?

    当谈到谷歌翻译算法的改进和更好的结果时,如何才能正确地评估第一个翻译备选比第二个更好?

    这不是一个无关紧要的问题,因为对于一些常用的句子,虽然我们有专业翻译器的参考翻译,但这些翻译仍然有一些差异。

    很多方法能部分解决这一问题,但最流行和最有效的指标是BLEU(双语评估替补)。想象一下,有两个来自机器翻译器的备选:

    备选1:Statsbot makes it easy for companies to closely monitor data from various analytical platforms via natural language. (Statsbot通过自然语言使企业能轻松地密切监控来自各种分析平台的数据。)

    备选2:Statsbot uses natural language to accurately analyze businesses’ metrics from different analytical platforms.(Statsbot使用自然语言准确地分析企业来自不同分析平台的指标。)

    虽然它们有相同的含义,但质量和结构都不同。

    我们再看看来自两个人的翻译:

    参考1:Statsbot helps companies closely monitor their data from different analytical platforms via natural language. (Statsbot通过自然语言帮助公司密切监测来自不同分析平台的数据。)

    参考2:Statsbot allows companies to carefully monitor data from various analytics platforms by using natural language. (Statsbot通过使用自然语言使企业能够密切监控来自各种分析平台的数据。)

    显然,备选1更好,与备选2相比,备选1包括更多共同的单词和短语。这就是BLEU方法的一个关键思想。我们可以将备选的n-gram(N元语法)与参考翻译的n-gram进行比较,并计算它们的匹配数(匹配数与它们的位置无关)。我们只使用n-gram精度,因为在多个参考时计算查全率很难,而且结果是n-gram分数的几何平均值。

    现在你可以开始评估复杂的机器学习翻译引擎了。下次当你使用谷歌翻译翻译某些内容时,可以想像一下:在给你最好的翻译版本之前,其实谷歌翻译已经分析了数百万份文档。

    查看英文原文https://blog.statsbot.co/machine-learning-translation-96f0ed8f19e4

    转载于:https://www.cnblogs.com/bonelee/p/7413452.html

    展开全文
  • 本文转自:http://www.hankcs.com/nlp/cs224n-mt-lstm-gru.html 其中,带黑点的表示离散的向量表示,否则表示连续的向量空间。 3、使用深度RNN LSTM...

    本文转自:http://www.hankcs.com/nlp/cs224n-mt-lstm-gru.html

     

     

     

    其中,带黑点的表示离散的向量表示,否则表示连续的向量空间。

    3、使用深度RNN

    LSTM单元结构如下:

    那时候的NN模型还是仅限于重新排序传统MT模型产生的结果,而最新的研究就是完全甩开了MT模型:

     

     

     

     

    转载于:https://www.cnblogs.com/koocn/p/7701291.html

    展开全文
  • 机器翻译之Seq2Seq介绍 2.基于Pytorch的Seq2Seq实现 2.1数据准备 2.2模型建立 2.3训练 1.机器翻译之Seq2Seq介绍 Seq2Seq模型是一个序列到序列的模型,在NLP中被广泛应用于翻译。其网络的模型结构大致如下: 在机器...

    目录

    1.机器翻译之Seq2Seq介绍

    2.基于Pytorch的Seq2Seq实现

    2.1数据准备

    2.2模型建立

    2.3训练


    1.机器翻译之Seq2Seq介绍

    Seq2Seq模型是一个序列到序列的模型,在NLP中被广泛应用于翻译。其网络的模型结构大致如下:

    在机器翻译中,其将需要被翻译的语言序列在源端(编码器)经过RNN模型最终压缩为一个上下文向量(C),接着目标端(解码器)对上下文向量进行解码,将器输出分类映射到我们需要翻译的语言空间中。这里仅仅给出最初始的Seq2Seq模型图(attention机制的c则映射到解码器中的每个解码器,而非单单是第一个解码器)。由于传统的RNN模型会产生梯度消失或梯度爆炸的问题,因此RNN常用的模型常选LSTM或GRU。(只介绍LSTM,GRU是变种的LSTM,目的为了减少参数)

    LSTM通过引入gate机制,减缓了梯度消失和梯度爆炸的问题。如结构如下:

    首先,设置了3个门,分别为遗忘门,输入门,更新门。其计算公式均为:

    gate_i=\sigma(x_tw_{xi}+h_{t-1}w_{hi}+b_i),i\in\{forget,input,output\}

    我们知道,sigmoid函数的值为0~1之间,这可以可以将对应的输入与门进行按位相乘进行过滤。

    对于遗忘门来说,前面的记忆不需要全部学习,因此,上一时刻的记忆c_{t-1}不需要全部保留,通过遗忘门会进行过滤。

    C_{t-1}*gate_f

    对于输入门来说或,我新学得的记忆也不需要全部记得清清楚楚,因为细节包含冗余。这一次学习的新记忆为(短期记忆):

    \hat{C}_t=tanh(x_tw_{xc}+h_{t-1}w_{hc}+b_c)

    经过输入门则是:

    \hat{C}_t*gate_i

    那么我们当前的记忆则不仅仅包含新学的记忆,也包括之前的记忆,那么当前记忆为(长期记忆):

    C_t=C_{t-1}*gate_f+\hat{C}_t*gate_i

    这一步加法操作也是正是LSTM梯度不消失或者爆炸的原因,传统的RNN模型这里则是通过连乘的形式,则我们可以想象1.01^{9999}0.99^{9999},这就是传统RNN存在梯度消失或者爆炸的原因。

    对于输出门,我们有了当前的记忆以后,我们要根据当前的要求有选择性的输出,选择先经过tanh将其值规范到[-1,1](有放大特征的作用),后经过输出门进行选择。

    则输出门的输出为:

    gate_o*tanh(C_t)

    我们可以发现,经过三个门过滤,最大的损失是什么?是之前的记忆,这也是LSTM的一个弊端,即当前记忆偏向于最新记忆,一旦序列过长,则长程依赖的关系会很弱。我们可以想象一下,还是这个图:

    其中的上下文向量C保存最多的肯定是源端中4时刻的信息,而1时刻的信息也保存很少。而我们在翻译的时候,第一个翻译的词很可能就很依赖于源端1时刻的输入,但是此时C包含其信息甚少,而我们也可以想象,如果第一个词都翻译不好,其作为下一时间步的输入(目标端每一时间步的输出是下一时间步的输入),那么后面的质量也会大打折扣。具体过程如下:

    我们在嵌入层(黄色)后使用编码器(绿色)来创建上下文向量(红色)。然后,我们使用将上下文向量和输入到解码器(蓝色)并通过线性层(紫色)来生成目标句子。

    针对这个问题,学术界提出各种各样的改进方法,后面我会慢慢学习总结,这里先给出最简单的:《Sequence to Sequence Learning with Neural Networks》这篇文章指出一种最简单训练技巧,即将我们源端序列倒序,即:

    I love you的输入变成you  love  I。这样我们的上下文向量C保存最多的肯定是I,在解码器第一个翻译我的时候,也会比较准确。下面我们将着手实现:

    2.基于Pytorch的Seq2Seq实现

    工具:Jupyter

    2.1数据准备

    数据集采用pytorchtext自带的Multi30k数据集,选用其中德语和英语的翻译(分词比较容易)。而pytorchtext处理数据的一般流程无非就是,定义好Field,将数据集的数据经过Field处理,给Field建立词表,建立数据迭代器。

    import spacy
    import torch
    
    from torchtext.datasets import Multi30k
    from torchtext.data  import Field,BucketIterator

    定义Field处理方式:

    #创建分词器器
    spacy_en=spacy.load("en_core_web_sm")#英语分词器
    spacy_de=spacy.load("de_core_news_sm")#德语分词器
    
    def en_seq(text):
        return [word.text for word in spacy_en.tokenizer(text)]
    
    def de_seq(text):
        return [word.text for word in spacy_de.tokenizer(text)][::-1]#源端倒序
    
    #源端的处理手段
    SRC=Field(tokenize=de_seq,
             init_token="<sos>",
             eos_token="<eos>",
             lower=True)
    
    #目标端的处理手段
    TRG=Field(tokenize=en_seq,
             init_token="<sos>",
             eos_token="<eos>",
             lower=True)

    将数据集经过Field处理,因为是pytorchtext内置数据集,所以处理起来也很容易,

    #定义dataset数据集,这里将其数据经过fiels处理
    train_data,valid_data,test_data=Multi30k.splits(exts=(".de",".en"),
                                                   fields=(SRC,TRG))
    
    print(f"Number of training examples: {len(train_data.examples)}")
    print(f"Number of validation examples: {len(valid_data.examples)}")
    print(f"Number of testing examples: {len(test_data.examples)}")

    这里也查看了训练样本,验证样本以及测试样本的规模,输出为:

    Number of training examples: 29000
    Number of validation examples: 1014
    Number of testing examples: 1000

    查看一下每一个样本,主要也是观察是否源端是否逆序:

    vars(train_data.examples[1])

    输出为:

    {'src': ['.','antriebsradsystem','ein','bedienen','schutzhelmen','mit','männer',
      'mehrere'],
     'trg': ['several','men','in','hard','hats','are','operating','a',
      'giant','pulley','system','.']}

    可以发现源端已经逆序。接下来为源端和目标端分别建立词表,要求每个词最少出现2次,否则为<unk>

    SRC.build_vocab(train_data,min_freq=2)
    TRG.build_vocab(train_data,min_freq=2)

    建立好词表以后,我们建立迭代索引,若batch_size=1的话,文本长度不相等也是无所谓的,但是batch_size>1的时候,就有有问题,同一批样本不一样长,那么我们在训练的时候,循环时间步到底设置多少,所以一般我们都会对句子进行pad操作补齐,幸运的是torchtext迭代器自动帮助pad填充,其中BucketIterator迭代器更是选择最合适的长度作为所有句子的固定长度,低于词长度的进行pad,高于此长度则进行裁剪。(这个固定长度是依据数据集里面所有的样本长度得出最合适的)

    BATCH_SIZE = 128
    cuda=torch.cuda.is_available()
    
    train_iterator, valid_iterator, test_iterator=BucketIterator.splits(
        (train_data,valid_data,test_data),
        batch_size=BATCH_SIZE,
        device=torch.device('cuda' if cuda else 'cpu')
    )

    测试数据迭代器

    for example in train_iterator:
        break
    print(example.src.device,example.trg.device,len(example))
    print(example.src.shape,example.trg.shape)

    其输出为:

    cuda:0 cuda:0 128
    torch.Size([27, 128]) torch.Size([28, 128]

    我们发现输出的样本是[seq_len,batch_size],而非[batch_size,seq_len]。我的习惯是batch_size放前面,但是Pytorch机制默认的是seq_len放在前面,后面的LSTM也是一样的。针对这个问题,可以通过permute函数进行交换

    example.src=example.src.permute(1,0)
    example.trg=example.trg.permute(1,0)
    print(example.src.shape,example.trg.shape)

    输出为:

    torch.Size([128, 27]) torch.Size([128, 28])

    这里我们必须要知道填充的位置,即<pad>的位置,因为无论是词嵌入的Embedding中还是计算损失函数的Loss中,<pad>都是不参与计算的。因此其,查看源端与目标端<pad>的index。

    TRG.vocab.stoi["<pad>"],SRC.vocab.stoi["<pad>"]

    得到其索引均为1。

    2.2模型建立

    Seq2Seq模型主要就是两个部分,即分别是编码器,解码器。我们写3个模块,分别是encoder,decoder以及集两部分大成的Seq2Seq。其中源端与目标端的网络深度均为4层(原文也是4层)

    import torch.nn as nn
    class  Encoder(nn.Module):
        #src_vocab_size德语词汇表大小,emb_model词向量维度,hidden_size隐藏向量维度,n_layers lstm深度
        def __init__(self,src_vocab_size,emb_model,hidden_size,n_layers,dropout):
            super(Encoder,self).__init__()
            
            self.embed=nn.Embedding(src_vocab_size,emb_model,padding_idx=1)
            self.lstm=nn.LSTM(input_size=emb_model,hidden_size=hidden_size,num_layers=n_layers,batch_first=True,dropout=dropout)
        
        def forward(self,src):
            #src[batch_size,seq_len]
            src=self.embed(src)
            #src[batch_size,seq_len,emb_model]
            output,(h_n,c_n)=self.lstm(src)
            #output[batch_size,seq_len,hidden_size]  最后一层每个时间步的隐状态h_t
            
            #h_n[batch_size,n_layers,hidden_size] 最后一个时间步每层的隐状态(实际上并非这样,Pytorch机制原因)
            #c_n[batch_size,n_layers,hidden_size] 最后一个时间步每层的记忆c(实际上并非这样,Pytorch机制原因)
            
            return output,(h_n,c_n)#output的意义不大,主要是(h_n,c_n),其作为上下文向量

    这里要重点提示一下的是:LSTM默认输入的是[seq_len,batch_size,embed]。若输入的是[batch_size,seq_len,embed],则需要将batch_first参数填写True。另一个值得注意的是LSTM的输出有3个变量,即output,(h_n,c_n),其中output是LSTM最后一层每个时间步的输出,h_n和_c_n为最后一个时间步每层的隐状态和记忆,其中经过batch_first以后,我们的输出是[batch_size,seq_len,hidden_size*n_direction]。但是h_n与c_n的shape均为[n_layers*n_direction,batch_size,hidden_size],其并未将batch_size放于首位,以下进行编码器模型的测试与这个注意点检验:

    #测试,参数
    emb_model=256
    hidden_size=512
    n_layers=4
    dropout=0.5
    src_vocab_size=len(SRC.vocab)
    #输入样本的维度
    example.src.shape

    输出为:

    torch.Size([128, 27])
    #建立源端模型
    enModel=Encoder(src_vocab_size,emb_model,hidden_size,n_layers,dropout)
    if(cuda):
        enModel=enModel.cuda()
    output,(h_n,c_n)=enModel(example.src)
    
    
    #只有输入input和输出output的batch会在第一维,hn和cn是不会变的。使用的时候要注意,会很容易弄混。
    output.shape,h_n.shape,c_n.shape

    输出为:

    (torch.Size([128, 27, 512]),
     torch.Size([4, 128, 512]),
     torch.Size([4, 128, 512]))

    下面建立目标端,目标端的起始与源端几乎一样,不过与源端不一样的地方在于,目标端的输入不是特定的,其采用教学的形式,即要么自学(即上一时间步的输出是当前时间步的输入),要么教学(当前时间步的输入为目标端)。因此,在目标端中,我们无法一次性给定一个单词序列,而是一个一个单词的给入,即输入的seq_len=1。最后模型的输出我们需要将其映射到英语单词的向量空间中。还有一个值得需要提的就是lstm若不传入h和c则默认帮你创建一个全为0的h和c但是我们这里需要传入,传入的h和c不能以batch为开头!格式为[n_layers*n_direction,batch_size,hidden_size]。Pytorch为什么有了batch_first这里还不管,因为我们正常这两个参数用不到可能(个人猜测)

    class Decoder(nn.Module):
        #trg_vocab_size 目标端的词汇表大小
        #emb_dim为词向量维度(我们将其设置与源端一样大小)
        #hidden_size 为目标端隐层维度(将其设置为与源端一样大小)
        #n_layers 网络层数(将其设置为一样大小)
        def __init__(self,trg_vocab_size,emb_dim,hidden_size,n_layers,dropout):
            super(Decoder,self).__init__()
            
            self.emb=nn.Embedding(trg_vocab_size,emb_dim,padding_idx=1)
            self.lstm=nn.LSTM(emb_dim,hidden_size,num_layers=n_layers,batch_first=True,dropout=dropout)
            self.classify=nn.Linear(hidden_size,trg_vocab_size)
        
        def forward(self,trg,h_n,c_n):
            #trg为应该为[batch,seq_len],不过实际训练中是一个一个传入(要考虑是否采用强制教学),所以seq_len为1
            #trg真正的输入维度为[batch]
            #h_n与c_n是源端的上下文向量(若计算不指定,则默认为0(若Encoder编码中))
            #维度均为:[n_layers,batch_size,hidden_size]
            trg=trg.unsqueeze(1)
            #trg[batch,1]
            trg=self.emb(trg)
            #trg[batch,1,emb]
            output,(h_n,c_n)=self.lstm(trg,(h_n,c_n))#这里的lstm指定了h,c,因此其内部不会自己创建一个全为0的h,c
            #output[batch,1,emb]
            #h_i[1,batch,emb]
            #c_i[1,batch,emb]
            output=self.classify(output.squeeze())#output.squeeze()使得output[batch 1 emb]->[batch emb]
            #output[batch trg_vocab_size]
            return output,(h_n,c_n) #返回(h_n,c_n)是为了下一解码器继续使用

    测试:

    #测试,参数
    trg_vocab_size=len(TRG.vocab)
    Demodel=Decoder(trg_vocab_size,emb_model,hidden_size,n_layers,dropout)
    if cuda:
        Demodel=Demodel.cuda()
    #查看目标端的shape
    example.trg.shape

    结果为:

    torch.Size([128, 28])

    我们每次的输入是一个单词,我们取其中某一列:

    trg=example.trg[:,1]#假设这一次为强制教学
    
    output,(h_n,c_n)=Demodel(trg,h_n,c_n)
    h_n.shape,c_n.shape

    输出为:

    (torch.Size([4, 128, 512]), torch.Size([4, 128, 512]), torch.Size([128, 5893]))

    下面建立Seq2Seq

    import random#生成本次强制教学的概率

    我们用一个tensor保存每一个时间步映射到英语单词向量空间的输出(其实就是每一个Decode的输出),每一个输出所在概率最大的分类即为需要翻译的单词,我们需要遍历句子中的每一个单词。还有一个值得注意的就是,由于我们在句子开头设置的<sos>,其不参与运算。

    class Seq2Seq(nn.Module):
        def __init__(self,encoder,decoder):
            super(Seq2Seq,self).__init__()
            self.encoder=encoder
            self.decoder=decoder
        
        def forward(self,src,trg,teach_rate=0.5):
            #src [bacth,seq_len]
            #trg  [bacth,seq_len]
            #teach_radio 强制教学的阈值
            batch_size=trg.shape[0]
            trg_seqlen=trg.shape[1]
            
            #保存每次输出的结果
            outputs_save=torch.zeros(batch_size,trg_seqlen,trg_vocab_size)
            if(cuda):
                outputs_save=outputs_save.cuda()
            #对源端进行编码
            _,(h_n,c_n)=self.encoder(src)
            
            #第一个输入到解码器中为<sos>
            trg_i=trg[:,0]
            #trg_i [batch]
            for i in range(1,trg_seqlen):
                output,(h_n,c_n)=self.decoder(trg_i,h_n,c_n)
                #output[batch trg_vocab_size]
                outputs_save[:,i,:]=output
                #产生一个随机概率(即是否强制教学)
                probability=random.random()
                
                #获取时间步预测的结果
                top=output.argmax(1)
                #top[batch]
                #下一时间步的输入
                trg_i=trg[:,i] if probability>teach_rate else top
            return outputs_save

    测试:

    #测试
    model=Seq2Seq(enModel,Demodel)
    if(cuda):
        model=model.cuda()
    outputs=model(example.src,example.trg)
    outputs.shape

    输出为:

    torch.Size([128, 28, 5893])

    2.3训练

    from torch.optim import Adam
    epochs=10
    optim=Adam(model.parameters())
    criterion = nn.CrossEntropyLoss(ignore_index = 1)#pad不参与损失函数的计算

    查看网络模型:

    model

    输出:

    Seq2Seq(
      (encoder): Encoder(
        (embed): Embedding(7854, 256, padding_idx=1)
        (lstm): LSTM(256, 512, num_layers=4, batch_first=True, dropout=0.5)
      )
      (decoder): Decoder(
        (emb): Embedding(5893, 256, padding_idx=1)
        (lstm): LSTM(256, 512, num_layers=4, batch_first=True, dropout=0.5)
        (classify): Linear(in_features=512, out_features=5893, bias=True)
      )
    )

    对网络参数进行初始化:-0.8,+0.8,这也是原文的配置

    #参数初始化
    def init_weights(m):
        for name, param in m.named_parameters():
            nn.init.uniform_(param.data, -0.08, 0.08)
            
    model.apply(init_weights)

    分别建立训练和测试函数(写开),其中model.train()和model.eval()主要的作用在于在训练的时候我们dropout会随机丢弃神经元,而在测试阶段的时候,不在进行丢弃神经元。 

    def train(model,train_iter,optim,criterion):
        model.train()#即dropout产生作用
        epoch_loss=0
        for i,example in enumerate(train_iter):
            src=example.src.permute(1,0)
            trg=example.trg.permute(1,0)
            #src[batch seqlen]
            #trg[batch seqlen]
    
            optim.zero_grad()
            output=model(src,trg)#output[batch trg_seqlen trq_vocab_size]
            #<sos>不参与运算,pad也不参与运算(criterion已经设置了ignore)
            output=output[:,1:,:].reshape(-1,trg_vocab_size)
            trg=trg[:,1:].reshape(-1)
            #output[batch*(trg_len-1),trg_vocab_size]
            #trg[batch*(trg_len-1)]
            loss=criterion(output,trg)
            loss.backward()
            optim.step()
            epoch_loss+=loss.item()
        return epoch_loss/len(train_iter)
    def evaluate(model,test_iter,criterion):
        model.eval()#即dropout产生作用
        epoch_loss=0
        
        with torch.no_grad():
            for i,example in enumerate(test_iter):
                
                src=example.src.permute(1,0)
                trg=example.trg.permute(1,0)
                #src[batch seqlen]
                #trg[batch seqlen]
    
    
                #即无法在进行强制教学
                output=model(src,trg,0)#output[batch trg_seqlen trq_vocab_size]
                
                #<sos>不参与运算,pad也不参与运算(criterion已经设置了ignore)
                output=output[:,1:].reshape(-1,trg_vocab_size)
                trg=trg[:,1:].reshape(-1)
                #output[batch*(trg_len-1),trg_vocab_size]
                #trg[batch*(trg_len-1)]
                loss=criterion(output,trg)
                epoch_loss+=loss.item()
    
        return epoch_loss/len(test_iter)
    import time,math

    统计每个迭代器的一个训练周期:

    def epoch_time(start_time, end_time):
        elapsed_time = end_time - start_time
        elapsed_mins = int(elapsed_time / 60)
        elapsed_secs = int(elapsed_time - (elapsed_mins * 60))
        return elapsed_mins, elapsed_secs

    训练,我们的损失函数简单表示为:困惑度

    for epoch in range(epochs):
        start_time=time.time()
        
        train_loss=train(model,train_iterator,optim,criterion)
        valid_loss=evaluate(model,valid_iterator,criterion)
        
        end_time=time.time()
        
        epoch_mins, epoch_secs = epoch_time(start_time, end_time)
        
        print(f'Epoch: {epoch+1:02} | Time: {epoch_mins}m {epoch_secs}s')
        print(f'\tTrain Loss: {train_loss:.3f} | Train PPL: {math.exp(train_loss):7.3f}')
        print(f'\t Val. Loss: {valid_loss:.3f} |  Val. PPL: {math.exp(valid_loss):7.3f}')

    输出为:

    test_loss = evaluate(model, test_iterator, criterion)
    print(f'| Test Loss: {test_loss:.3f} | Test PPL: {math.exp(test_loss):7.3f} |')
    | Test Loss: 3.742 | Test PPL:  42.194 |

    原著是基于WMT14数据集,规模较大,因此设置了4层,其实这个数据集2层即可,收敛也很快,如下:

     

    展开全文
  • Part 1、机器翻译 Part 2、基于统计的机器翻译系统 Part 3、深度学习 Part 4、基于RNN的机器翻译 Part 5、RNN翻译模型的扩展 Part 6、RNN的优化 GRN LSTM ...
  • 机器翻译概述 机器翻译就是将一种语言翻译为另一种语言。所有机器翻译系统本质上都是基于统计的,我们将总尝试使用非常大的语料库,一般称为平行语料库。在语料库中,有许多句子或段落以不同语言表述。 深度学习出现...
  • 机器翻译 机器翻译任务中的一些新颖的RNN模型 GRU(Gated Recurrent Units) LSTM(Long Short-Term Memories) 内容回顾 下图是之前的课程所提到过的一些模型的损失函数。 多层神经网络的前向传播和反向传播...
  • self).__init__() self.lstm_size = lstm_size self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_size) self.lstm = tf.keras.layers.LSTM( lstm_size, return_sequences=True, return_state=...
  • 整体由encoder和decoder两大部分组成,每部分都有一个LSTM网络,其中encoder输入原始的句子,decoder输入的是含有开始符号的翻译后的句子,输出是带有结尾标志福德目标句子。 一、处理文本数据 这一步骤包含对原...
  • 由于我们对LSTMs和RNNs有了基本的了解,让我们尝试将其中的一部分用于使用Keras开发机器翻译模型。 这将演示如何使用序列到序列LSTM网络将文本从英语翻译成法语。 from __future__ import print_function ...
  • 该模型实现的是英文到中文的翻译,下图为了更好展示模型架构借用大佬的图(这里没有用到Embeddings): 本文完整代码:Github 目录 一、处理文本数据 1.获得翻译前后的句子  2.创建关于 字符-index 和...
  • (一)从序列到序列的翻译任务 ''' https://github.com/bentrevett/pytorch-seq2seq/blob/master/1%20-%20Sequence%20to%20Sequence%20Learning%20with%20Neural%20Networks.ipynb ''' #coding=gbk import torch ...
  • 机器翻译

    2020-11-27 08:42:25
    机器翻译机器翻译机器翻译 1.多层LSTM-2014 2.机器翻译:Jointly Learning-2015 3.谷歌的神经网络机器翻译-2016 4.UMT-2018
  • 从英语到德语的翻译:使用长短期记忆(LSTM)网络的英语到德语的神经机器翻译(NMT)模型。 机器翻译是自然语言处理中的一项重要任务,不仅可用于将一种语言翻译成另一种语言,而且还可用于消除歧义(例如,确定...
  • 而在机器翻译的训练样本中,每个句子对通常时作为独立的数据来训练的。由于每个句子的长短不一致,因此在将这些句子放到同一个batch时,需要将较短的句子补齐到与同 batch 内最长句子相同的长度。用于填充长度而填入...
  • Udacity-自然语言处理纳米 自然语言处理项目
  • 目录 RNN Encoder–Decoder 简介 单个RNN原理 Encoder–Decoder原理 LSTM Seq2Seq RNN Encoder–Decoder 论文《Learning Phrase Representations using RNN Encoder–Decoder for Statistical Machine Translation》...
  • 理解encoder_outputs, state_h, state_c = encoder(encoder_inputs)的三个输出: ... inputs = tf.keras.layers.Input(shape=(3, 1)) lstm = tf.keras.layers.LSTM(1, return_state=True)(inputs) model = tf.ke
  • RNN与机器翻译

    千次阅读 2017-05-01 12:23:42
    CS224d-Day 9: GRUs and LSTMs – for machine translation 视频链接 课件链接本文结构: - 机器翻译系统整体的认识 - 什么是 parallel corpora - 三个模块 - 各模块有什么难点 ...机器翻译机器翻译是NLP问
  • 机器翻译例程tensorflow

    2018-06-13 23:06:20
    LSTM实现机器翻译,有教程,有任务,非常适合学习。
  • 本文于阿里云,神经机器翻译(NMT)是自动翻译的端到端方法,这个方法具有克服传统短语翻译系统缺点的潜力。神经机器翻译(NMT)是自动翻译的端到端方法,这个方法具有克服传统短语翻译系统缺点的潜力。最近,阿里...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 471
精华内容 188
关键字:

机器翻译lstm