目录

  • 说明
  • 配置环境
  • 此节说明
  • 代码

说明

本博客代码来自开源项目:《动手学深度学习》(PyTorch版)
并且在博主学习的理解上对代码进行了大量注释,方便理解各个函数的原理和用途

配置环境

使用环境:python3.8
平台:Windows10
IDE:PyCharm

此节说明

此节对应书本上10.12节
此节功能为:机器翻译
由于此节相对复杂,代码注释量较多

代码

# 本书链接https://tangshusen.me/Dive-into-DL-PyTorch/#/
# 10.12 机器翻译
# 注释:黄文俊
# E-mail:hurri_cane@qq.comimport collections
import os
import io
import math
import torch
from torch import nn
import torch.nn.functional as F
import torchtext.vocab as Vocab
import torch.utils.data as Dataimport sys
sys.path.append("..")
import d2lzh_pytorch as d2lPAD, BOS, EOS = '<pad>', '<bos>', '<eos>'
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')# 将一个序列中所有的词记录在all_tokens中以便之后构造词典,然后在该序列后面添加PAD直到序列
# 长度变为max_seq_len,然后将序列保存在all_seqs中
def process_one_seq(seq_tokens, all_tokens, all_seqs, max_seq_len):all_tokens.extend(seq_tokens)seq_tokens += [EOS] + [PAD] * (max_seq_len - len(seq_tokens) - 1)all_seqs.append(seq_tokens)# 使用所有的词来构造词典。并将所有序列中的词变换为词索引后构造Tensor
def build_data(all_tokens, all_seqs):vocab = Vocab.Vocab(collections.Counter(all_tokens),specials=[PAD, BOS, EOS])indices = [[vocab.stoi[w] for w in seq] for seq in all_seqs]return vocab, torch.tensor(indices)def read_data(max_seq_len):# in和out分别是input和output的缩写in_tokens, out_tokens, in_seqs, out_seqs = [], [], [], []with io.open('F:/PyCharm/Learning_pytorch/data/fr-en-small.txt') as f:lines = f.readlines()for line in lines:in_seq, out_seq = line.rstrip().split('\t')in_seq_tokens, out_seq_tokens = in_seq.split(' '), out_seq.split(' ')if max(len(in_seq_tokens), len(out_seq_tokens)) > max_seq_len - 1:continue  # 如果加上EOS后长于max_seq_len,则忽略掉此样本process_one_seq(in_seq_tokens, in_tokens, in_seqs, max_seq_len)process_one_seq(out_seq_tokens, out_tokens, out_seqs, max_seq_len)in_vocab, in_data = build_data(in_tokens, in_seqs)out_vocab, out_data = build_data(out_tokens, out_seqs)'''in_vocab为输入句子中所有单词组成的vocab(包含词频统计、词与索引(stoi)的对应关系、索引与词(itos)的对应关系)in_data为输入句子被填充至设定的最大字数max_seq_len后根据stoi将词转换为索引后的序列'''return in_vocab, out_vocab, Data.TensorDataset(in_data, out_data)# 将序列的最大长度设成7,即每个句子最大长度在7或7以下
max_seq_len = 7
in_vocab, out_vocab, dataset = read_data(max_seq_len)
print(dataset[0])# 含注意力机制的编码器—解码器
class Encoder(nn.Module):def __init__(self, vocab_size, embed_size, num_hiddens, num_layers,drop_prob=0, **kwargs):super(Encoder, self).__init__(**kwargs)self.embedding = nn.Embedding(vocab_size, embed_size)self.rnn = nn.GRU(embed_size, num_hiddens, num_layers, dropout=drop_prob)def forward(self, inputs, state):# 输入形状是(批量大小, 时间步数)。将输出互换样本维和时间步维# a = self.embedding(inputs.long())   # torch.Size([4, 7, 8])# b = a.permute(1, 0, 2)      # torch.Size([7, 4, 8])embedding = self.embedding(inputs.long()).permute(1, 0, 2)  # (seq_len, batch, input_size)return self.rnn(embedding, state)def begin_state(self):return None # 隐藏态初始化为None时PyTorch会自动初始化为0encoder = Encoder(vocab_size=10, embed_size=8, num_hiddens=16, num_layers=2)
output, state = encoder(torch.zeros((4, 7)), encoder.begin_state())
print(output.shape, state.shape)
# GRU的state是隐藏状态h, 而LSTM的是一个元组(隐藏状态h, 记忆细胞c)# 注意力机制
def attention_model(input_size, attention_size):model = nn.Sequential(nn.Linear(input_size,attention_size, bias=False),nn.Tanh(),nn.Linear(attention_size, 1, bias=False))return modeldef attention_forward(model, enc_states, dec_state):"""enc_states: (时间步数, 批量大小, 隐藏单元个数)dec_state: (批量大小, 隐藏单元个数)"""# 将解码器隐藏状态广播到和编码器隐藏状态形状相同后进行连结dec_states = dec_state.unsqueeze(dim=0).expand_as(enc_states)enc_and_dec_states = torch.cat((enc_states, dec_states), dim=2)e = model(enc_and_dec_states)  # 形状为(时间步数, 批量大小, 1)alpha = F.softmax(e, dim=0)  # 在时间步维度做softmax运算return (alpha * enc_states).sum(dim=0)  # 返回背景变量seq_len, batch_size, num_hiddens = 10, 4, 8
model = attention_model(2*num_hiddens, 10)
enc_states = torch.zeros((seq_len, batch_size, num_hiddens))
dec_state = torch.zeros((batch_size, num_hiddens))
print(attention_forward(model, enc_states, dec_state).shape)
print("*" * 50)# 含注意力机制的解码器
class Decoder(nn.Module):def __init__(self, vocab_size, embed_size, num_hiddens, num_layers,attention_size, drop_prob=0):super(Decoder, self).__init__()self.embedding = nn.Embedding(vocab_size, embed_size)self.attention = attention_model(2*num_hiddens, attention_size)# GRU的输入包含attention输出的背景向量c和实际输入, 所以尺寸是 num_hiddens+embed_sizeself.rnn = nn.GRU(num_hiddens + embed_size, num_hiddens,num_layers, dropout=drop_prob)self.out = nn.Linear(num_hiddens, vocab_size)def forward(self, cur_input, state, enc_states):"""cur_input shape: (batch, )state shape: (num_layers, batch, num_hiddens)"""# 使用注意力机制计算背景向量c = attention_forward(self.attention, enc_states, state[-1])# 将嵌入后的输入和背景向量在特征维连结, (批量大小, num_hiddens+embed_size)input_and_c = torch.cat((self.embedding(cur_input), c), dim=1)      # torch.Size([2, 128])# 为输入和背景向量的连结增加时间步维,时间步个数为1output, state = self.rnn(input_and_c.unsqueeze(0), state)# 移除时间步维,输出形状为(批量大小, 输出词典大小)output = self.out(output).squeeze(dim=0)return output, statedef begin_state(self, enc_state):# 直接将编码器最终时间步的隐藏状态作为解码器的初始隐藏状态return enc_state# 先实现batch_loss函数计算一个小批量的损失
def batch_loss(encoder, decoder, X, Y, loss):batch_size = X.shape[0]enc_state = encoder.begin_state()enc_outputs, enc_state = encoder(X, enc_state)# 初始化解码器的隐藏状态dec_state = decoder.begin_state(enc_state)# 解码器在最初时间步的输入是BOSdec_input = torch.tensor([out_vocab.stoi[BOS]] * batch_size)# 我们将使用掩码变量mask来忽略掉标签为填充项PAD的损失mask, num_not_pad_tokens = torch.ones(batch_size,), 0l = torch.tensor([0.0])for y in Y.permute(1,0): # Y shape: (batch, seq_len)dec_output, dec_state = decoder(dec_input, dec_state, enc_outputs)l = l + (mask * loss(dec_output, y)).sum()dec_input = y  # 使用强制教学num_not_pad_tokens += mask.sum().item()# EOS后面全是PAD. 下面一行保证一旦遇到EOS接下来的循环中mask就一直是0mask = mask * (y != out_vocab.stoi[EOS]).float()return l / num_not_pad_tokens# 训练模型
def train(encoder, decoder, dataset, lr, batch_size, num_epochs):enc_optimizer = torch.optim.Adam(encoder.parameters(), lr=lr)dec_optimizer = torch.optim.Adam(decoder.parameters(), lr=lr)loss = nn.CrossEntropyLoss(reduction='none')data_iter = Data.DataLoader(dataset, batch_size, shuffle=True)for epoch in range(num_epochs):l_sum = 0.0for X, Y in data_iter:enc_optimizer.zero_grad()dec_optimizer.zero_grad()l = batch_loss(encoder, decoder, X, Y, loss)l.backward()enc_optimizer.step()dec_optimizer.step()l_sum += l.item()if (epoch + 1) % 10 == 0:print("epoch %d, loss %.3f" % (epoch + 1, l_sum / len(data_iter)))
# 创建模型实例并设置超参数
embed_size, num_hiddens, num_layers = 64, 64, 2
attention_size, drop_prob, lr, batch_size, num_epochs = 10, 0.5, 0.01, 2, 50
encoder = Encoder(len(in_vocab), embed_size, num_hiddens, num_layers,drop_prob)
decoder = Decoder(len(out_vocab), embed_size, num_hiddens, num_layers,attention_size, drop_prob)
train(encoder, decoder, dataset, lr, batch_size, num_epochs)
print("*" * 50)# 预测不定长的序列(贪婪搜索)
def translate(encoder, decoder, input_seq, max_seq_len):in_tokens = input_seq.split(' ')in_tokens += [EOS] + [PAD] * (max_seq_len - len(in_tokens) - 1)enc_input = torch.tensor([[in_vocab.stoi[tk] for tk in in_tokens]]) # batch=1enc_state = encoder.begin_state()enc_output, enc_state = encoder(enc_input, enc_state)dec_input = torch.tensor([out_vocab.stoi[BOS]])dec_state = decoder.begin_state(enc_state)output_tokens = []for _ in range(max_seq_len):dec_output, dec_state = decoder(dec_input, dec_state, enc_output)pred = dec_output.argmax(dim=1)pred_token = out_vocab.itos[int(pred.item())]if pred_token == EOS:  # 当任一时间步搜索出EOS时,输出序列即完成breakelse:output_tokens.append(pred_token)dec_input = predreturn output_tokensinput_seq = 'ils regardent .'
print(translate(encoder, decoder, input_seq, max_seq_len))
print("*" * 50)# 评价翻译结果BLEU
def bleu(pred_tokens, label_tokens, k):len_pred, len_label = len(pred_tokens), len(label_tokens)score = math.exp(min(0, 1 - len_label / len_pred))for n in range(1, k + 1):num_matches, label_subs = 0, collections.defaultdict(int)for i in range(len_label - n + 1):label_subs[''.join(label_tokens[i: i + n])] += 1for i in range(len_pred - n + 1):if label_subs[''.join(pred_tokens[i: i + n])] > 0:num_matches += 1label_subs[''.join(pred_tokens[i: i + n])] -= 1score *= math.pow(num_matches / (len_pred - n + 1), math.pow(0.5, n))return scoredef score(input_seq, label_seq, k):pred_tokens = translate(encoder, decoder, input_seq, max_seq_len)label_tokens = label_seq.split(' ')print('bleu %.3f, predict: %s' % (bleu(pred_tokens, label_tokens, k),' '.join(pred_tokens)))score('ils regardent .', 'they are watching .', k=2)
score('ils sont canadienne .', 'they are canadian .', k=2)print("*" * 50)

《动手学深度学习》(PyTorch版)代码注释 - 56 【Machine_translation】相关推荐

  1. 伯禹公益AI《动手学深度学习PyTorch版》Task 04 学习笔记

    伯禹公益AI<动手学深度学习PyTorch版>Task 04 学习笔记 Task 04:机器翻译及相关技术:注意力机制与Seq2seq模型:Transformer 微信昵称:WarmIce ...

  2. 伯禹公益AI《动手学深度学习PyTorch版》Task 07 学习笔记

    伯禹公益AI<动手学深度学习PyTorch版>Task 07 学习笔记 Task 07:优化算法进阶:word2vec:词嵌入进阶 微信昵称:WarmIce 优化算法进阶 emmmm,讲实 ...

  3. 伯禹公益AI《动手学深度学习PyTorch版》Task 03 学习笔记

    伯禹公益AI<动手学深度学习PyTorch版>Task 03 学习笔记 Task 03:过拟合.欠拟合及其解决方案:梯度消失.梯度爆炸:循环神经网络进阶 微信昵称:WarmIce 过拟合. ...

  4. 【动手学深度学习PyTorch版】6 权重衰退

    上一篇移步[动手学深度学习PyTorch版]5 模型选择 + 过拟合和欠拟合_水w的博客-CSDN博客 目录 一.权重衰退 1.1 权重衰退 weight decay:处理过拟合的最常见方法(L2_p ...

  5. 【动手学深度学习PyTorch版】12 卷积层

    上一篇移步[动手学深度学习PyTorch版]11 使用GPU_水w的博客-CSDN博客 目录 一.卷积层 1.1从全连接到卷积 ◼ 回顾单隐藏层MLP ◼ Waldo在哪里? ◼ 原则1-平移不变性 ...

  6. 【动手学深度学习PyTorch版】27 数据增强

    上一篇请移步[动手学深度学习PyTorch版]23 深度学习硬件CPU 和 GPU_水w的博客-CSDN博客 目录 一.数据增强 1.1 数据增强(主要是关于图像增强) ◼ CES上的真实的故事 ◼ ...

  7. 【动手学深度学习PyTorch版】13 卷积层的填充和步幅

    上一篇移步[动手学深度学习PyTorch版]12 卷积层_水w的博客-CSDN博客 目录 一.卷积层的填充和步幅 1.1 填充 1.2 步幅 1.3 总结 二.代码实现填充和步幅(使用框架) 一.卷积 ...

  8. 【动手学深度学习PyTorch版】23 深度学习硬件CPU 和 GPU

    上一篇请移步[动手学深度学习PyTorch版]22续 ResNet为什么能训练出1000层的模型_水w的博客-CSDN博客 目录 一.深度学习硬件CPU 和 GPU 1.1 深度学习硬件 ◼ 计算机构 ...

  9. 【动手学深度学习PyTorch版】15 池化层

    上一篇请移步[动手学深度学习PyTorch版]14 卷积层里的多输入多输出通道_水w的博客-CSDN博客 目录 一.池化层 1.1 池化层 ◼池化层原因 ◼ 二维最大池化 1.2 填充.步幅与多个通道 ...

  10. 伯禹公益AI《动手学深度学习PyTorch版》Task 05 学习笔记

    伯禹公益AI<动手学深度学习PyTorch版>Task 05 学习笔记 Task 05:卷积神经网络基础:LeNet:卷积神经网络进阶 微信昵称:WarmIce 昨天打了一天的<大革 ...

最新文章

  1. 数据结构与算法:07 Leetcode同步练习(二)
  2. Sqlserver 查询语句性能测试
  3. 自由自在公司冰淇淋甜美的健康文化
  4. 静态初始化块的执行顺序
  5. Halcon例程(基于多个标定图的单目相机标定)详解—— Camera_calibration_multi_image.hdev
  6. 基于.NET平台的分层架构实战(二)——需求分析与数据库设计
  7. 系统管理员必须知道的PHP安全实践
  8. maven 项目上传私服pom配置
  9. 超级计算机操作系统有什么不同,超级计算机功能强大吗?它与普通计算机不同,但也使用Windows系统...
  10. JSP 语法2 都在百分号里面: 5.@符号加JSP三种指令标签 6
  11. ios中input输入无效
  12. shader 如何声明数组_聊聊如何正确向Compute Shader传递数组
  13. 极客时间课程总结:那些编程之外的能力
  14. springBoot接入阿里云oss
  15. 如何继承字走三国武器
  16. 为苏州企业解读高新技术企业八大申报模块
  17. 中兴服务器车间,走进中兴通讯车间 探秘智能手机生产链(多图)
  18. python学生成绩管理系统-增删查改
  19. 史上最强的美名腾智能起名成功发布
  20. 大数据赋能,如何精细化运营?

热门文章

  1. Word排版(小计)
  2. 树莓派调用百度API实现果蔬识别部署
  3. Java实现 LeetCode 365 水壶问题
  4. matlab数字信号处理常用函数
  5. sigmoid二分类
  6. C#创建临时文件夹方法
  7. linux tac命令_Linux tac命令入门
  8. Ultimate Tic-Tac-Toe
  9. 网易云那些触动人心的经典热评
  10. 《触动人心设计优秀iphone应用》读后感