当前位置: 首页 > news >正文

【深度学习入门篇 ⑩】Seq2Seq模型:语言翻译

【🍊易编橙:一个帮助编程小伙伴少走弯路的终身成长社群🍊】

大家好,我是小森( ﹡ˆoˆ﹡ ) ! 易编橙·终身成长社群创始团队嘉宾,橙似锦计划领衔成员、阿里云专家博主、腾讯云内容共创官、CSDN人工智能领域优质创作者 。


今天我们进入 Seq2Seq 的领域,了解这种更为复杂且功能强大的模型,它不仅能理解词汇(Word2Vec),还能把这些词汇串联成完整的句子。 

Seq2Seq

Seq2Seq(Sequence-to-Sequence),就是从一个序列到另一个序列的转换。它不仅仅能理解单词之间的关系,而且还能把整个句子的意思打包,并解压成另一种形式的表达。

seq2seq是一种神经网络架构,是由encoder(编码器)decoder(解码器)两个RNN的组成的。其中encoder负责对输入句子的理解,转化为context vector,decoder负责对理解后的句子的向量进行处理,解码,获得输出。  

Seq2seq模型中的encoder接受一个长度为M的序列,得到1个 context vector,之后decoder把这一个context vector转化为长度为N的序列作为输出,从而构成一个M to N的模型,能够处理很多不定长输入输出的问题,比如:文本翻译,问答,文章摘要,关键字写诗等等

  • 编码器的任务是读取并理解输入序列,然后把它转换为一个固定长度的上下文向量,也叫作状态向量。
  • 解码器的任务是接收编码器生成的上下文向量,并基于这个向量生成目标序列。 

可以加入注意力机制(Attention Mechanism):使解码器能够在生成每个输出元素时“关注”输入序列中的不同部分,从而提高模型处理长序列和捕捉复杂依赖关系的能力。 

Seq2Seq模型实现

 任务:

完成一个模型,实现往模型输入一串数字,输出这串数字+0

  •  输入12345678,输出123456780

实现流程

  • 文本转化为序列

  • 使用序列,准备数据集,准备Dataloader

  • 完成编码器

  • 完成解码器

  • 完成seq2seq模型

  • 完成模型训练的逻辑,进行训练

  • 完成模型评估的逻辑,进行模型评估

训练时可以使用GPU训练:

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("训练设备为:", device)

文本转化为序列

由于输入的是数字,为了把这写数字和词典中的真实数字进行对应,可以把这些数字理解为字符串

class NumSequence:UNK_TAG = "UNK" PAD_TAG = "PAD" EOS_TAG = "EOS" #句子开始SOS_TAG = "SOS" #句子结束UNK = 0PAD = 1EOS = 2SOS = 3def __init__(self):self.dict = {self.UNK_TAG : self.UNK,self.PAD_TAG : self.PAD,self.EOS_TAG : self.EOS,self.SOS_TAG : self.SOS}# 字符串和数字对应的字典for i in range(10):self.dict[str(i)] = len(self.dict)self.index2word = dict(zip(self.dict.values(),self.dict.keys()))def __len__(self):return len(self.dict)def transform(self,sequence,max_len=None,add_eos=False):sequence_list = list(str(sequence))seq_len = len(sequence_list)+1 if add_eos else len(sequence_list)if add_eos and max_len is not None:assert max_len>= seq_len, "max_len 应该大于seq+eos的长度"_sequence_index = [self.dict.get(i,self.UNK) for i in sequence_list]if add_eos:_sequence_index += [self.EOS]if max_len is not None:sequence_index = [self.PAD]*max_lensequence_index[:seq_len] =  _sequence_indexreturn sequence_indexelse:return _sequence_indexdef inverse_transform(self,sequence_index):result = []for i in sequence_index:if i==self.EOS:breakresult.append(self.index2word.get(int(i),self.UNK_TAG))return resultnum_sequence = NumSequence()if __name__ == '__main__':num_sequence = NumSequence()print(num_sequence.dict)print(num_sequence.index2word)print(num_sequence.transform("232356",add_eos=True))
准备Dataset
from torch.utils.data import Dataset,DataLoader
import numpy as np
from word_sequence import num_sequence
import torch
import configclass RandomDataset(Dataset):def __init__(self):super(RandomDataset,self).__init__()self.total_data_size = 500000np.random.seed(10)self.total_data = np.random.randint(1,100000000,size=[self.total_data_size])def __getitem__(self, idx):input = str(self.total_data[idx])return input, input+ "0",len(input),len(input)+1def __len__(self):return self.total_data_size
准备DataLoader

在准备DataLoader的过程中,可以通过定义的collate_fn来实现对dataset中batch数据的处理

def collate_fn(batch):batch = sorted(batch,key=lambda x:x[3],reverse=True)input,target,input_length,target_length = zip(*batch)input = torch.LongTensor([num_sequence.transform(i,max_len=config.max_len) for i in input])target = torch.LongTensor([num_sequence.transform(i,max_len=config.max_len,add_eos=True) for i in target])input_length = torch.LongTensor(input_length)target_length = torch.LongTensor(target_length)return input,target,input_length,target_lengthdata_loader = DataLoader(dataset=RandomDataset(),batch_size=config.batch_size,collate_fn=collate_fn,drop_last=True)

编码器

目的就是为了对文本进行编码,把编码后的结果交给后续的程序使用,使用Embedding+GRU

import torch.nn as nn
from word_sequence import num_sequence
import configclass NumEncoder(nn.Module):def __init__(self):super(NumEncoder,self).__init__()self.vocab_size = len(num_sequence)self.dropout = config.dropoutself.embedding = nn.Embedding(num_embeddings=self.vocab_size,embedding_dim=config.embedding_dim,padding_idx=num_sequence.PAD)self.gru = nn.GRU(input_size=config.embedding_dim,hidden_size=config.hidden_size,num_layers=1,batch_first=True)def forward(self, input,input_length):embeded = self.embedding(input) embeded = nn.utils.rnn.pack_padded_sequence(embeded,lengths=input_length,batch_first=True)out,hidden = self.gru(embeded)out,outputs_length = nn.utils.rnn.pad_packed_sequence(out,batch_first=True,padding_value=num_sequence.PAD)return out,hidden

解码器

主要负责实现对编码之后结果的处理,得到预测值

import torch
import torch.nn as nn
import config
import random
import torch.nn.functional as F
from word_sequence import num_sequenceclass NumDecoder(nn.Module):def __init__(self):super(NumDecoder,self).__init__()self.max_seq_len = config.max_lenself.vocab_size = len(num_sequence)self.embedding_dim = config.embedding_dimself.dropout = config.dropoutself.embedding = nn.Embedding(num_embeddings=self.vocab_size,embedding_dim=self.embedding_dim,padding_idx=num_sequence.PAD)self.gru = nn.GRU(input_size=self.embedding_dim,hidden_size=config.hidden_size,num_layers=1,batch_first=True,dropout=self.dropout)self.log_softmax = nn.LogSoftmax()self.fc = nn.Linear(config.hidden_size,self.vocab_size)def forward(self, encoder_hidden,target,target_length):decoder_input = torch.LongTensor([[num_sequence.SOS]]*config.batch_size)decoder_outputs = torch.zeros(config.batch_size,config.max_len,self.vocab_size) decoder_hidden = encoder_hidden for t in range(config.max_len):decoder_output_t , decoder_hidden = self.forward_step(decoder_input,decoder_hidden)decoder_outputs[:,t,:] = decoder_output_tuse_teacher_forcing = random.random() > 0.5if use_teacher_forcing:decoder_input =target[:,t].unsqueeze(1) else:value, index = torch.topk(decoder_output_t, 1) decoder_input = indexreturn decoder_outputs,decoder_hiddendef forward_step(self,decoder_input,decoder_hidden):embeded = self.embedding(decoder_input)  out,decoder_hidden = self.gru(embeded,decoder_hidden) out = out.squeeze(0) out = F.log_softmax(self.fc(out),dim=-1)out = out.squeeze(1)return out,decoder_hidden

seq2seq模型

完成模型的搭建

import torch
import torch.nn as nnclass Seq2Seq(nn.Module):def __init__(self,encoder,decoder):super(Seq2Seq,self).__init__()self.encoder = encoderself.decoder = decoderdef forward(self, input,target,input_length,target_length):encoder_outputs,encoder_hidden = self.encoder(input,input_length)decoder_outputs,decoder_hidden = self.decoder(encoder_hidden,target,target_length)return decoder_outputs,decoder_hidden

完成训练:

import torch
import config
from torch import optim
import torch.nn as nn
from encoder import NumEncoder
from decoder import NumDecoder
from seq2seq import Seq2Seq
from dataset import data_loader as train_dataloader
from word_sequence import num_sequenceencoder = NumEncoder()
decoder = NumDecoder()
model = Seq2Seq(encoder,decoder)
print(model)optimizer =  optim.Adam(model.parameters())
criterion= nn.NLLLoss(ignore_index=num_sequence.PAD,reduction="mean")def get_loss(decoder_outputs,target):target = target.view(-1)decoder_outputs = decoder_outputs.view(config.batch_size*config.max_len,-1)return criterion(decoder_outputs,target)def train(epoch):for idx,(input,target,input_length,target_len) in enumerate(train_dataloader):optimizer.zero_grad()##[seq_len,batch_size,vocab_size] [batch_size,seq_len]decoder_outputs,decoder_hidden = model(input,target,input_length,target_len)loss = get_loss(decoder_outputs,target)loss.backward()optimizer.step()print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(epoch, idx * len(input), len(train_dataloader.dataset),100. * idx / len(train_dataloader), loss.item()))torch.save(model.state_dict(), "model/seq2seq_model.pkl")torch.save(optimizer.state_dict(), 'model/seq2seq_optimizer.pkl')if __name__ == '__main__':for i in range(5):train(i)

Seq2Seq优点:能处理输入和输出长度不固定的序列转换任务,灵活性高

Seq2Seq缺点:使用固定上下文长度、训练和推理通常需要逐步处理输入和输出序列,以及参数量较少,面对复杂场景可能受限。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【一次记一句:SQL】从 information_schema.TABLES中查询数据库表中记录数据量
  • win安装mysql
  • windows中使用Jenkins打包,部署vue项目完整操作流程
  • Linux驱动开发-06蜂鸣器和多组GPIO控制
  • pyqt/pyside QTableWidget失去焦点后,选中的行仍高亮的显示
  • uniapp小程序项目解决键盘问题
  • 无人机的发展前景大吗?
  • 23种设计模式之命令模式
  • Python(re模块的具体使用)
  • QT通用配置文件库(QPreferences)
  • 【云原生】Prometheus整合Alertmanager告警规则使用详解
  • [集成学习]基于python的Stacking分类模型的客户购买意愿分类预测
  • CentOS 7 yum官方源失效
  • 分布式ID是什么?有哪些解决方案?
  • 准备跳槽了(仍然底层为主,ue独立游戏为辅)
  • ES6指北【2】—— 箭头函数
  • 自己简单写的 事件订阅机制
  • 10个最佳ES6特性 ES7与ES8的特性
  • AWS实战 - 利用IAM对S3做访问控制
  • ES学习笔记(12)--Symbol
  • GitUp, 你不可错过的秀外慧中的git工具
  • JavaScript的使用你知道几种?(上)
  • Java到底能干嘛?
  • Python学习之路13-记分
  • React 快速上手 - 07 前端路由 react-router
  • Redis字符串类型内部编码剖析
  • Ruby 2.x 源代码分析:扩展 概述
  • Traffic-Sign Detection and Classification in the Wild 论文笔记
  • Vue.js源码(2):初探List Rendering
  • vue的全局变量和全局拦截请求器
  • vue和cordova项目整合打包,并实现vue调用android的相机的demo
  • 关于 Cirru Editor 存储格式
  • 七牛云 DV OV EV SSL 证书上线,限时折扣低至 6.75 折!
  • 如何使用Mybatis第三方插件--PageHelper实现分页操作
  • 如何在 Tornado 中实现 Middleware
  • 软件开发学习的5大技巧,你知道吗?
  • 我这样减少了26.5M Java内存!
  • ​2021半年盘点,不想你错过的重磅新书
  • ​卜东波研究员:高观点下的少儿计算思维
  • (16)Reactor的测试——响应式Spring的道法术器
  • (27)4.8 习题课
  • (el-Transfer)操作(不使用 ts):Element-plus 中 Select 组件动态设置 options 值需求的解决过程
  • (TOJ2804)Even? Odd?
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (附源码)小程序儿童艺术培训机构教育管理小程序 毕业设计 201740
  • (过滤器)Filter和(监听器)listener
  • (接口自动化)Python3操作MySQL数据库
  • (七)理解angular中的module和injector,即依赖注入
  • (算法)前K大的和
  • (转)Sublime Text3配置Lua运行环境
  • (转)菜鸟学数据库(三)——存储过程
  • (转)人的集合论——移山之道
  • . NET自动找可写目录
  • .ai域名是什么后缀?
  • .gitignore文件使用