目录

0. 前言

1. Seq2Seq模型简介

2. 代码复现

2.1 Introduction:

2.2 准备数据:

2.3 训练、验证和测试数据集

2.4 创建Seq2Seq Model

2.4.1 编码器Encoder:

2.4.2 Decoder

2.5 实现Seq2Seq模型

2.6 训练模型

2.7 评估:


from 恒心

研一上时的学习笔记,确实是一个序列到序列学习的好方法,建议初学者可以尝试以下欧

修改~2021.8.17

文章有些公式无效,重新更正了一下。

0. 前言

首先这部分的学习还是看代码比较直观,代码看完后,在重新看完论文图片以及公式推导,更容易理解,考虑到Pytorch 与Tensorflow 如今框架比较新 所以不建议用旧的框架实现,因此在github找到了一个不错的仓库项目。

个人的学习笔记仓库:

https://github.com/leandon/Postgraduate_Study_Notes

运行环境:

google colabratory

1. Seq2Seq模型简介

对这个模型需要有一定的理解,具体可以解读多图解读

2. 代码复现

该笔记原文是英文笔记,因此复现的时候,汉化了一部分做记录,建议还是看原文会比较好。

Sequence to Sequence(Seq2seq) Learning with Neural Networks

2.1 Introduction:

实例中演示了德语英语的翻译,但其实这个模型可以应用在任何涉及从一个序列到另一个序列,例如汇总

最常见的Seq2Seq模型是解码器-编码器模型

  • 通常使用递归神经网络(RNN)将源(输入)语句编码为单个向量。在本笔记本中,我们将将此单个向量称为上下文向量。我们可以将上下文向量视为整个输入句子的抽象表示。
  • 然后,该向量由第二个RNN解码,该第二个RNN通过一次生成一个单词来学习输出目标(输出)语句

这个图,基本上就是整个实验的核心了

编码器:

h_t = \text{EncoderRNN}(e(x_t), h_{t-1})

解码器

h_t = \text{EncoderRNN}(e(x_t), h_{t-1})

2.2 准备数据:

我们使用PyTorch对模型进行编码,并使用TorchText版主我们进行所需的所有预处理,使用spacy协助数据标记化

接下来:tokenizers是一个分词器,使用分词器将包含句子的字符串转换为组成该字符串的单个令牌的列表。句子是一系列标记。而不是一系列单词。[“ good”,“ morning”,“!”],“好”和“早上”都是单词和记号,但是“!”是一个象征,而不是一个单词

spact 具有每种语言的模型(德语为“ de”,英语为“ en”),因此我们可以访问每种模型的标记器。

使用之前需要在命令行里输入:

python -m spacy download en
python -m spacy download de

我们创建令牌生成器(分词器)函数,这些可以传递给TorchText,并将句子作为字符串接收,并将句子作为标记列表返回。

在我们正在实施的论文中,他们发现反转输入顺序是有益的,他们认为输入顺序“在数据中引入了许多短期依赖性,这使得优化问题更加容易”。在将德语句子转换为标记列表之后,我们通过反转德语句子来复制该句子。

注意翻转:

str='Runoob'print(str[::-1])

2.3 训练、验证和测试数据集

我们将使用的数据集是Multi30k数据集。这是一个具有约30,000个并行英语,德语和法语句子的数据集,每个句子每个句子含〜12个单词。

词汇表用于将每个唯一标记与索引(整数)相关联。源语言和目标语言的词汇是不同的。

使用min_freq参数,我们只允许出现至少2次的标记出现在我们的词汇表中。仅出现一次令牌转换成<UNK>(未知)令牌。

重要的是要注意,我们的词汇表应该仅基于训练集而不是验证/测试集。这可以防止“信息泄漏”进入我们的模型,从而使我们夸大了验证/测试分数。

准备数据的最后一步是创建迭代器,可以重复这些操作以返回一批数据,这些数据是一个Pytroch张量,可以说是使用词汇表将他们从一系列可读标记转换成一系列相应的索引。

我们获得一批数据后,我们需要确保所有语句的填充长度都相同,目标语句也是如此。幸运的是,TorchText迭代器为我们的处理了此问题。

使用BucketIterator 它以最小化源句和目标句中的填充量的方式创建批处理

2.4 创建Seq2Seq Model

三部分构建模型

  • 编码器
  • 解码器
  • seq2seq模型

同时提供一种相互连接的方式。

2.4.1 编码器Encoder:

两层的LSTM 网络,隐藏状态的第一层的输出(建议参照第一张图进行思考)

​​​​​​​

隐藏状态的第二层的输出(建议参照第一张图进行思考)

使用多层RNN还意味着我们还需要一个初始隐藏状态作为每层hl0的输入,并且我们还将每层输出一个上下文向量

我们采用LSTM ,是因为我们需要返回的不单单是新的隐藏状态而是要返回一个单元格状态Ct以及每个时间步长

我们可以将Ct作为另一种隐藏的状态,初始为全零的张量。我们的上下文向量现在将同时是最终的隐藏状态和最终的单元格状态

将我们的多层(multi-layer)扩展到LSTM,我们得到

如何将第一层的隐藏状态作为输入传递给第二层,而不将其作为单元状态传递给第二层

参数解释如下:

  • input_dim  输入(源)词汇量

  • emb_dim    嵌入层的尺寸

  • hid_dim    是隐藏状态和单元状态的维数。

  • n_layers 是RNN的层数

  • dropout Dropout层,用于正则化,在多层RNN的各层之间应用。

在将单词(从技术上讲,单词的索引)传递到RNN之前,有一个步骤,在那里将单词转换为向量

这个RNN 返回的数值

outputs (每个时间步的顶层隐藏状态),

hidden (the final hidden state for each layer, hT, stacked on top of each other)

cell (the final cell state for each layer, cT, stacked on top of each other).

每个张量的大小在代码中留为注释。在此实现中,n_directions始终为1,但是请注意,双向RNN(在教程3中介绍)的n_directions为2。

2.4.2 Decoder

采用2层LSTM

其实现原理和编码器类似

请记住,解码器的初始隐藏和单元状态是我们的上下文向量,它们是来自同一层的编码器的最终隐藏和单元状态

参数和初始化与Encoder类类似,除了我们现在有一个output_dim,它是输出/目标的词汇量。还添加了线性层,用于根据顶层隐藏状态进行预测

实现过程注意:

当序列长度始终为1的时候,采用nn.LSTMCell

但是当序列长度比较大的时候,采用nn.LSTM代码比较简洁

2.5 实现Seq2Seq模型

  • 接受输入/源句
  • 使用编码器产生上下文向量
  • 使用编码器产生预测的输出/目标句子

在前向方法中要做的第一件事是创建一个输出张量,该张量将存储我们的所有预测Y

在模型训练过程中,我们加入了一个teaching force的阈值,表示使用teaching force的概率。当随机生成的数字大于这个阈值时,使用teaching force;否则不使用。teacher force就是在翻译的过程中“抄答案”,将正确的单词作为后面decoder的输入。

“我们可以看到,如果使用了teacher force,不管翻译的结果是否正确,我们都使用正确的答案参与后面的decoder的计算中。简单来说,就是 “师傅带进门,修行靠个人” 。----关于teacher force 比较通俗的讲解

代码实现

class Seq2Seq(nn.Module):def __init__(self, encoder, decoder, device):super().__init__()self.encoder = encoderself.decoder = decoderself.device = deviceassert encoder.hid_dim == decoder.hid_dim, \"Hidden dimensions of encoder and decoder must be equal!"assert encoder.n_layers == decoder.n_layers, \"Encoder and decoder must have equal number of layers!"def forward(self, src, trg, teacher_forcing_ratio = 0.5):#src = [src len, batch size]#trg = [trg len, batch size]#teacher_forcing_ratio is probability to use teacher forcing#e.g. if teacher_forcing_ratio is 0.75 we use ground-truth inputs 75% of the timebatch_size = trg.shape[1]trg_len = trg.shape[0]trg_vocab_size = self.decoder.output_dim#tensor to store decoder outputsoutputs = torch.zeros(trg_len, batch_size, trg_vocab_size).to(self.device)#last hidden state of the encoder is used as the initial hidden state of the decoderhidden, cell = self.encoder(src)#first input to the decoder is the <sos> tokensinput = trg[0,:]for t in range(1, trg_len):#insert input token embedding, previous hidden and previous cell states#receive output tensor (predictions) and new hidden and cell statesoutput, hidden, cell = self.decoder(input, hidden, cell)#place predictions in a tensor holding predictions for each tokenoutputs[t] = output#decide if we are going to use teacher forcing or notteacher_force = random.random() < teacher_forcing_ratio#get the highest predicted token from our predictionstop1 = output.argmax(1) #if teacher forcing, use actual next token as next input#if not, use predicted tokeninput = trg[t] if teacher_force else top1return outputs

编码器和解码器的嵌入(embedding)维数和丢失(Dropout)量可以不同,但​​是层数和隐藏/单元状态的大小必须相同

我们初始权重(从-0.08到+0.08之间的均匀分布),并使用nn.init.uniform_从均匀分布中采样它们

def init_weights(m):for name, param in m.named_parameters():nn.init.uniform_(param.data, -0.08, 0.08)model.apply(init_weights)

就算可训练参数的数量

def count_parameters(model):return sum(p.numel() for p in model.parameters() if p.requires_grad)print(f'The model has {count_parameters(model):,} trainable parameters')

2.6 训练模型

  • 从批处理中获取源句子和目标句子X和Y

  • 将最后一批计算出的梯度归零

  • 将源和目标馈入模型以获取输出Y ^
  • 由于损失函数仅适用于具有1d目标的2d输入,因此我们需要使用.view展平它们
  • 将输出张量和目标张量的第一列切开
  • 用loss.backward()计算梯度
  • 裁剪渐变以防止其爆炸(RNN中的常见问题)
  • 通过执行优化程序步骤来更新模型的参数
  • 损失值加总
  • 最后返回所有批次的平均损失
def train(model, iterator, optimizer, criterion, clip):model.train()epoch_loss = 0for i, batch in enumerate(iterator):# 从批处理中后去源句子和目标句子X,Ysrc = batch.srctrg = batch.trg# 将最后一批计算出的梯度归零optimizer.zero_grad()# 将源和目标放入模型中输出下一个youtput = model(src, trg)#trg = [trg len, batch size]#output = [trg len, batch size, output dim]output_dim = output.shape[-1]# 由于损失函数是仅适用于具有1d目标的2d输入,因此我们用view将其展平输入# 将输出张量和目标张量的第一列切开output = output[1:].view(-1, output_dim)trg = trg[1:].view(-1)#trg = [(trg len - 1) * batch size]#output = [(trg len - 1) * batch size, output dim]loss = criterion(output, trg)# 用此函数计算梯度loss.backward()# clip the gradients 防止其梯度爆炸torch.nn.utils.clip_grad_norm_(model.parameters(), clip)# 执行优化程序步骤来更新模型的参数optimizer.step()# 损失值求和epoch_loss += loss.item()# 返回所有批次的平均损失    return epoch_loss / len(iterator)

2.7 评估:

def evaluate(model, iterator, criterion):# 设置为评估模式,关闭Dropout(弱使用批处理规范化,则也关闭)model.eval()epoch_loss = 0with torch.no_grad():# 确保该模块内不计算梯度for i, batch in enumerate(iterator):src = batch.srctrg = batch.trg# 必须确保关闭teacher_forcing 参数output = model(src, trg, 0) #turn off teacher forcing#trg = [trg len, batch size]#output = [trg len, batch size, output dim]output_dim = output.shape[-1]output = output[1:].view(-1, output_dim)trg = trg[1:].view(-1)#trg = [(trg len - 1) * batch size]#output = [(trg len - 1) * batch size, output dim]loss = criterion(output, trg)epoch_loss += loss.item()return epoch_loss / len(iterator)

计算运行时间 

def epoch_time(start_time, end_time):elapsed_time = end_time - start_timeelapsed_mins = int(elapsed_time / 60)elapsed_secs = int(elapsed_time - (elapsed_mins * 60))return elapsed_mins, elapsed_secsN_EPOCHS = 10
CLIP = 1best_valid_loss = float('inf')for epoch in range(N_EPOCHS):start_time = time.time()train_loss = train(model, train_iterator, optimizer, criterion, CLIP)valid_loss = evaluate(model, valid_iterator, criterion)end_time = time.time()epoch_mins, epoch_secs = epoch_time(start_time, end_time)if valid_loss < best_valid_loss:best_valid_loss = valid_losstorch.save(model.state_dict(), 'tut1-model.pt')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}')# 我们将加载为模型提供最佳验证损失的参数(state_dict),然后在测试集上运行模型
model.load_state_dict(torch.load('tut1-model.pt'))test_loss = evaluate(model, test_iterator, criterion)print(f'| Test Loss: {test_loss:.3f} | Test PPL: {math.exp(test_loss):7.3f} |')

运行效果:

参考文献:

  • pytorch-seq2seq::https://github.com/bentrevett/pytorch-seq2seq

  • 【详细解读】基于Seq2Seq模型实现简单的机器翻译https://blog.csdn.net/Jenny_oxaza/article/details/105759591

  • Bleu得分:https://github.com/NLP-LOVE/ML-NLP/tree/master/NLP/16.5%20seq2seq

  • TF版本基于注意力的神经机器翻译:https://tensorflow.google.cn/tutorials/text/nmt_with_attention

  • 真正的完全图解Seq2Seq Attention模型:https://mp.weixin.qq.com/s/0k71fKKv2SRLv9M6BjDo4w

深度学习(自然语言处理)Seq2Seq学习笔记(动手实践)相关推荐

  1. 最新(2019)斯坦福CS224n深度学习自然语言处理课程(视频+笔记+2017年合集)

    向AI转型的程序员都关注了这个号

  2. Ventuz教程学习笔记动手实践之时钟动画制作

    一.设计结果展示 用Ventuz制作时钟动画打算达到的效果,见下图.按A键动态出现时钟,按B键时钟从右侧划出. 二.从零开始 1.首先是制作时钟,分表盘和表针. 先新建一个Scene,然后拖动worl ...

  3. Ventuz教程学习笔记动手实践之简单逻辑动画制作

    一.设计结果展示 用Ventuz制作逻辑动画打算达到的效果,见下图. 设计五个圆圈,一个圆球,鼠标点击相应的圆圈,圆球会移动到你点击的圆圈中,中间圆圈中有逻辑数字,每点击一次数字就加1. 二.从零开始 ...

  4. pink老师课堂案例:简易的ATM机,学习前端开发一定要多动手实践

    一个js小案例,2021我正在学习前端开发,您呢? var operate = prompt('选择操作:\n 1.查询余额\n 2.存钱\n 3.取钱\n 4.退出');var money = 0; ...

  5. 利用计算机技术实现对文本篇章,自然语言处理NLP学习笔记一:概念与模型初探...

    前言 先来看一些demo,来一些直观的了解. 自然语言处理: 可以做中文分词,词性分析,文本摘要等,为后面的知识图谱做准备. 知识图谱: 还有2个实际应用的例子,加深对NLP的理解 九歌机器人: 微软 ...

  6. 李沐《动手学深度学习》第二版 pytorch笔记1 环境搭建

    李沐<动手学深度学习>第二版pytorch笔记1 搭建环境 文章目录 李沐<动手学深度学习>第二版pytorch笔记1 搭建环境 此时尚有耐心 虚拟环境搭建 创建虚拟环境 查看 ...

  7. 自然语言处理与深度学习: 集智俱乐部活动笔记

    自然语言处理与深度学习: 集智俱乐部活动笔记 04 Jul 2016 目录 简介 自然语言处理的基本任务 对语言进行建模的若干方法 语言模型简介 N-gram 语言模型 基于神经网络的语言模型 语言的 ...

  8. 【CS224n】2斯坦福大学深度学习自然语言处理课程笔记——词向量、词义和神经分类器

    Natural Language Processing with Deep Learning 课程笔记2 1. 词向量和word2vec 2. 优化基础知识 3. 我们能否通过计数更有效地抓住词义的本 ...

  9. 《深度学习进阶 自然语言处理》学习笔记(2)

    前篇链接 link 目录 第五章 RNN 语言模型 RNN模型 模型架构 一个典型RNN单元的计算图 损失函数的设置 评价指标 总结 第六章 Gated RNN 上一章RNN存在的问题 梯度爆炸与梯度 ...

  10. 深度学习入门之PyTorch学习笔记:深度学习介绍

    深度学习入门之PyTorch学习笔记:深度学习介绍 绪论 1 深度学习介绍 1.1 人工智能 1.2 数据挖掘.机器学习.深度学习 1.2.1 数据挖掘 1.2.2 机器学习 1.2.3 深度学习 第 ...

最新文章

  1. mysql top 语句简介
  2. Mysql 参数最佳实践_MySQL参数调优最佳实践
  3. amazon linux 安装nginx,linux – NGINX不显示Amazon EC2实例上的默认页...
  4. iOS-QQ临时对话、QQ群申请跳转
  5. 盘点2020 最烂密码大曝光,第一名的竟然是它?
  6. maven 公共模块依赖_idea 创建多模块依赖Maven项目
  7. 第六章 输入输出系统-作业
  8. opencv和caffe编译后怎么添加路径
  9. morhpia(4)-更新
  10. python scipy版本_Py之Scipy:Python库之Scipy库的简介、安装、使用方法详细攻略
  11. JAVA人脸识别(人脸对比)
  12. 7723Java斗破苍穹_诛天-斗破苍穹(新)
  13. 制作U盘启动盘--win7系统
  14. 安装Pytorch-gpu版本(第一次安装 或 已经安装Pytorch-cpu版本后)
  15. Unity 2D游戏制作流程用到的技巧
  16. office 安装出现安装30088-4(5)错误解决方案
  17. SPSS(十九)SPSS之时间序列模型(图文+数据集)
  18. 这些好用的跨境电商插件,你都听说几个?
  19. 重生之我是赏金猎人-漏洞挖掘(十一)-某SRC储存XSS多次BypassWAF挖掘
  20. 小波函数的数据拟合方法

热门文章

  1. python期货程序化交易高手心得_程序化交易高手的交易心得 分享~
  2. 告别烧脑,金融保险企业邮件应该这样卖产品!
  3. 基于VUE+DJANGO开发的前后端分离的官方网站系统带管理后台
  4. 百度UE富文本编辑器设置自适应大小和滚动条等
  5. C++中return 0 和return 1 的区别
  6. HTML5入门之无序列表
  7. 如何理解复连通区域的格林公式
  8. 关于工作站和台式机的区别介绍
  9. 瑞数vmp算法还原流程讲解
  10. SQL注入闯关第一关-Less1