第一篇是代码实现详解
如果想看注意力机制深入理解 请点传送门注意力机制深入理解
话不多说上代码
数据加载类
from io import open
import unicodedata
import re
import random
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch import optim
# 注册驱动
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 起始标志
SOS_token = 0
EOS_token = 1
class Lang():
def __init__(self, name):
self.name = name
self.word2index = {} # 词汇对应下标 词查数词典映射关系保存
self.index2word = {0: 'SOS', 1: 'EOS'} # 下标对应词典 数查词映射关系保存
self.n_words = 2 # 初始化索引,因为01已经被占了,所以初始值还是2
def addSentence(self, sentence):
# 添加句子函数,将句子转化为对应的数值序列
# 这里设定为以空格为分词标准 对句子进行分割,得到对应的下标
for word in sentence.split(' '):
self.addWord(word)
def addWord(self, word):
'''
添加词进入词典
:param word:分词后的词
:return:
'''
if word not in self.word2index:
self.word2index[word] = self.n_words
self.index2word[self.n_words] = word
self.n_words += 1
def demo1_testlong():
name = 'eng'
sen = 'I am IronMan'
dicts = Lang(name)
dicts.addSentence(sen)
print(dicts.word2index)
print(dicts.index2word)
print(dicts.n_words)
def unicodeToAscii(s):
'''
unicode万国码转换为ASCII码作为标准进行处理
:param s:
:return:
'''
return ''.join(
c for c in unicodedata.normalize('NFD', s)
if unicodedata.category(c) != 'Mn'
)
def normalizesString(s):
'''
字符串标准化,去除不需要的特殊符号,归纳分词规则等,清洗数据
:param s:
:return:
'''
# 字符串统一大小写并去除空白符.特殊符号
s = unicodeToAscii(s.lower().strip())
# 将以!.?符号的str钱=前加一个空格
s = re.sub(r"([.!?])", r" \1", s)
# 不是正常标点符号和大小写的字符都替换成空格
s = re.sub(r"[^a-zA-Z.!?]+", r" ", s)
return s
def readLangs(lang1, lang2, path):
lines = open(path, encoding='UTF-8').read().strip().split('\n')
list1 = []
c = 0
list1 = [[normalizesString(s) for s in l.split('\t')] for l in lines]
input_lang = Lang(lang1)
output_lang = Lang(lang2)
# print(list1)
return input_lang, output_lang, list1
def filterPair(p, maxlenght, eng_prefixes):
return len(p[0].split(' ')) < maxlenght and p[0].startswith(eng_prefixes) and len(p[1].split(' ')) < maxlenght
def filterPairs(pairs):
eng_prefixes = (
"i am ", "i m ",
"he is", "he s ",
"she is", "she s ",
"you are", "you re ",
"we are", "we re ",
"they are", "they re "
)
maxlenght = 10
return [pair for pair in pairs if filterPair(pair, maxlenght, eng_prefixes)]
def demo04_test_filterPairs(path):
lang1 = "eng"
lang2 = "fra"
input_lang, output_lang, pairs_before = readLangs(lang1, lang2, path)
print("过滤之前pairs", len(pairs_before))
pairs_after = filterPairs(pairs_before)
print("过滤之后pairs", len(pairs_after))
print("过滤后的pairs前5个:", pairs_after[:5])
print("过滤后的pairs900-905:", pairs_after[900:905])
# 文本处理 文本-->数值化
def prepareData(lang1, lang2, path):
input_lang, output_lang, pairs = readLangs(lang1, lang2, path)
print(len(pairs))
pairs = filterPairs(pairs)
print(len(pairs))
# print(input_lang.n_words)
for pair in pairs:
# print(pairs)
input_lang.addSentence(pair[0])
output_lang.addSentence(pair[1])
# print(input_lang.n_words)
print(f'{pair[0]}--> {pair[1]}')
return input_lang, output_lang, pairs
# 6-2 文本预处理函数测试:文本---> 数值化
def demo05_test_prepareData(path):
input_lang, output_lang, pairs = prepareData('eng', 'fra', path)
print("input_n_words:", input_lang.n_words) # input_n_words: 2803
print("output_n_words:", output_lang.n_words) # output_n_words: 4345
for i in range(3):
print('随机选取一个语言对 random.choice(pairs)--->', random.choice(pairs))
# random.choice(pairs)---> ['he s on his way .', 'il est en route .']route
def tensorfromSentence(lang, sentence):
# 返回下标
indexs = [lang.word2index[word] for word in sentence.split(' ')]
indexs.append(EOS_token) # 添加结束标志
# print(indexs)
return torch.tensor(indexs, dtype=torch.long, device=device).view(-1, 1)
def tensorfromPair(input_lang, output_lang, pair):
# input_lang, output_lang, paris = prepareData('english', 'french', '../data/eng-fra.txt')
input_tensor = tensorfromSentence(input_lang, pair[0])
target_tensor = tensorfromSentence(output_lang, pair[1])
# 最后返回它们组成的元组
return input_tensor, target_tensor
if __name__ == '__main__':
# demo1_testlong()
# s = 'what are you doing? hhhh iam missing you!'
# print(normalizesString(s))
path = '../data/eng-fra.txt'
# demo04_test_filterPairs(path)
demo05_test_prepareData(path)
# input_lang, output_lang, pairs = readLangs('english', 'french', '../data/eng-fra.txt')
# print("input_lang:", input_lang)
# print("output_lang:", output_lang)
# print("过滤之前pairs", len(pairs))
# pairs_after = filterPairs(pairs)
# print("过滤之后pairs", len(pairs_after))
# print("过滤后的pairs前5个:", pairs_after[:5])
# print("过滤后的pairs900-905:", pairs_after[900:905])
# input_lang, output_lang, pairs = readLangs('english', 'french', '../data/eng-fra.txt')
# print(input_lang.n_words)
# print(output_lang.n_words)
# tensorfromSentence(input_lang, 'asdaskj aslkjda asdlkn')
# for i in pairs:
# print(tensorfromPair(pairs[0]))
模型构建
import random
import torch
import torch.nn as nn
import torch.nn.functional as F
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
class EncoderRNN(nn.Module):
def __init__(self, input_size, hidden_size):
super(EncoderRNN, self).__init__()
self.input = input_size
self.hidden = hidden_size
self.embeding = nn.Embedding(input_size, hidden_size)
self.gru = nn.GRU(hidden_size, hidden_size)
def forward(self, input1, hidden):
# input1是输入张量
# hidden代表初始化隐藏层张量
# embedding处理完是一个二维张量,但是gru要求输入三维张量
output = self.embeding(input1).view(1, 1, -1)
output, hidden = self.gru(output, hidden) # 隐层层数
return output, hidden
def initHiddenTensor(self):
# 初始化隐层张量
return torch.zeros(1, 1, self.hidden, device=device)
def test_encoder():
import load_data
path = '../data/eng-fra.txt'
input_lang, output_lang, pairs = load_data.prepareData('english', 'frensh', path)
pair_tensor = load_data.tensorfromPair(random.choice(pairs))
hidden_size = 25
input_size = 20
input = pair_tensor[0][0]
# print(pair_tensor[0].item())
# print(pair_tensor[0][0], pair_tensor[0][1])
print(pair_tensor[0].shape)
print(input)
# 隐藏层神经元循环次数为25 所以词典不能超过25个词
# 获得tensor 的值 print(input.item())
if input.item() > 24:
input = torch.tensor([6])
hidden = torch.zeros(1, 1, hidden_size)
encoder = EncoderRNN(input_size, hidden_size) # 输入元素特征维度,隐层循环次数
encoder_output, hidden = encoder(input, hidden)
print('encoder 结构', encoder)
print('encoderoutput.shape---->', encoder_output.shape, encoder_output)
class DecoderRNN(nn.Module):
def __init__(self, hidden_size, output_size):
super(DecoderRNN, self).__init__()
self.hidden_size = hidden_size
self.output_size = output_size
self.embedding = nn.Embedding(output_size, hidden_size)
print('embedding: ->', self.embedding)
self.gru = nn.GRU(hidden_size, hidden_size)
self.out = nn.Linear(hidden_size, output_size) # 做尺寸变换
self.softmax = nn.LogSoftmax(dim=1)
def forward(self, input, hidden):
# print('embedding计算输出', self.embedding(input), self.embedding(input).shape)
# print('embedding形状', self.embedding(input).shape)
output = self.embedding(input).view(1, 1, -1)
output = F.relu(output)
output, hidden = self.gru(output, hidden)
print('output---->', output.shape)
output = self.softmax(self.out(output[0])) # 降维
# print('somtmax--->', output.shape)
return output, hidden
def initHidden(self):
return torch.zeros(1, 1, self.hidden_size, device=device)
def test_decodeRNN():
hidden_size = 25
output_size = 12
input = torch.tensor([1]) # 输入一个词
# 一个词,25个特征
hidden = torch.zeros(1, 1, hidden_size)
decorator = DecoderRNN(hidden_size, output_size)
for i in range(3):
output, hidden = decorator(input, hidden)
print('*' * 20)
print('decoderRnn--->', output)
print('shape', output.shape)
class AttnDecoderRNN(nn.Module):
def __init__(self, hidden_size, output_size, dropout_p=0.1, max_length=10):
'''
:param hidden_size: 解码器GRU输入尺寸,隐层循环次数
:param output_size: 解码器输出尺寸
:param dropout_p: 随机失活参数
:param max_length: 句子最大长度,词个数
'''
super(AttnDecoderRNN, self).__init__()
self.hidden_size = hidden_size
self.output_size = output_size
self.dropout_p = dropout_p
self.max_length = max_length
self.embedding = nn.Embedding(self.output_size, self.hidden_size)
# 根据QKV计算规则
# 1. q与k进行attention权重矩阵计算然后与v做矩阵乘法
# 三种计算方式
# QK纵轴拼接,做一次线性变换,使用softmax处理后的结果与v做张量乘法
# QK纵轴拼接,做一次线性变换,在使用tanh函数激活,然后再进行内部求和,最后使用softmax处理获得的结果再与v乘
# QK的转置做点积运算,然后除以一个缩放系数,在使用softmax处理获得结果最后与v相乘
# 当注意力权重矩阵和v都是三维张量时,第一维度代表batch条数时,做bmm运算
# 2.根据第一步采用的计算方式,如果是拼接方法,则需要将Q与第二步的计算结果再进行拼接
# 如果是转置点积,一般是自注意力机制Q=K=V 不需要与Q进行拼接,因此第二部的计算方式与第一步采用的方法有关
# 3.第三步 为了使整个attention结构按照指定尺寸输出,使用线性层作用在第二部的结果上做一个线性表隐患,得到最终对Query 也就是解码器输出的注意力矩阵
# 采用第一步中第一种计算方式
# 编码器输出 输入到embedding中创建词向量
self.attn = nn.Linear(self.hidden_size * 2,
self.max_length) # 第一步拼接 所以拼接完成后尺寸大小是2*hiddensize 第二个参数是句子长度,也就是batch批次数
self.attn_combine = nn.Linear(self.hidden_size * 2, self.hidden_size) # 这里是第四步骤所需要的线性层,用于规范输出大小,输入是第三层gru的输出
# 输入神经元个数,输出神经元个数 输入神经元:cat(cat(Q,K)*V,Q) 所以是Q尺寸Hiddensize*2 输出维度 Hiddensize 使用输出去词典中查词 因为是seq2seq所以要求并不严格
self.dropout = nn.Dropout(self.dropout_p)
self.gru = nn.GRU(self.hidden_size, self.hidden_size)
self.out = nn.Linear(self.hidden_size, self.output_size)
def forward(self, input, hidden, encoder_outputs):
'''
注意力计算顺序
:param input:输入 编码后的词tensor
:param hidden: 初始化隐层张量(全连接层权重矩阵)
:param encoder_outputs: 解码器的输出张量
:return:output 输出值 hidden最后一层隐层最后一遍循环锁输出的值,atten_weights: 注意力权重矩阵
'''
embedded = self.embedding(input).view(1, 1, -1) # 输入 送进embedding层创建词张量,并从原来的二维提升维度,为了加入注意力计算
# dropout 防止过拟合
embedded = self.dropout(embedded)
# 进行atten计算 第一种
# QK纵轴拼接,线性变换后送入softmax
# print(embedded[0].shape, embedded[0])
# print(hidden[0].shape, hidden[0])
# Query是 解码器的输出 Key是hidden隐藏层 Value:是编码器的输出
attn_weight = F.softmax(
self.attn(
torch.cat((embedded[0], hidden[0]), 1)
), dim=1
)
# cat 第一个参数,要拼接的tensor 第二参数 选择维度 0:行 1:列 以此类推 dim不知道什么意思
# 第一步的后半部分 权重矩阵与v做乘法计算 并做bmm计算
attn_applied = torch.bmm(attn_weight.unsqueeze(0), encoder_outputs.unsqueeze(0))
# unsqueeze 升维度 bam x bma = baa
# 第二部计算 与Q进行拼接
output = torch.cat((embedded[0], attn_applied[0]), 1)
# 第三步 使用线性层作用在第三步的结果上做一个线性变换并扩展维度 送给第四步
output = self.attn_combine(output).unsqueeze(0)
output = F.relu(output)
# 激活后的结果送入gru输入和hidden一起传入其中
output, hidden = self.gru(output, hidden)
# 将结果降维并送入softmax得到最终结果
output = F.log_softmax(self.out(output[0]), dim=1)
return output, hidden, attn_weight
def test_AttnDecoderRnn():
hidden_size = 25
output_size = 10
input = torch.tensor([3])
# 一次输入一个字
hidden = torch.zeros(1, 1, hidden_size)
# encoderoutputs是encode中每一个时间步输出堆叠而成的
# 他的形状应该是循环次数*每个词维度数
encoder_outputs = torch.randn(10, 25)
# 中间语义张量C
decoder = AttnDecoderRNN(hidden_size, output_size)
output, hidden, attnweights = decoder(input, hidden, encoder_outputs)
print(output.shape)
if __name__ == '__main__':
# test_AttnDecoderRnn()
test_decodeRNN()
模型训练
import random
import seq2seq_CreateModel
import load_data
import torch
import torch.nn as nn
import torch.nn.functional as F
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
teacher_forcing_ratio = 0.5
def train(input_tensor, target_tensor, encoder, decoder, encoder_optimizer,
decoder_optimizer, criterion, maxleght=10):
'''
训练函数
:param teacher_forcing_ratio: 学习率
:param input_tensor: English输入张量
:param target_tensor: French目标张量
:param encoder: 编码器obj
:param decoder: 解码器obj
:param encoder_optimizer: 编码器优化器方法
:param decoder_optimizer: 解码器优化器方法
:param criterion: 损失函数计算方法
:param maxleght: 句子最大长度batchsize
:return:
'''
# 初始化编码器 隐层张量
encoder_hidden = encoder.initHiddenTensor()
# 编码器解码器优化器梯度清零
encoder_optimizer.zero_grad()
decoder_optimizer.zero_grad()
# 根据原文本 和目标文本张量获取对应长度
input_length = input_tensor.size(0) # 输入的词个数
target_length = target_tensor.size(0) # 目标的词个数
# 初始化编码器输出张量 这个是用来存储编码器输出的容器
encoder_outputs = torch.zeros(maxleght, encoder.hidden, device=device) # 编码器输出 batch_size x 隐层循环次数
# 设置初始损失为0
loss = 0
# 循环遍历输入张量索引
for ei in range(input_length):
# 根据索引取出对应单词张量,因为每个句子长度不一样,所以每次循环都需要根据输入句子长度来决定
encoder_output, encoder_hidden = encoder(input_tensor[ei], encoder_hidden) # 输入tensor一个词,还有初始化隐藏层
# 存储编码器输出
encoder_outputs[ei] = encoder_output[0, 0] # 每次输出形状都是[1,1,hidden_size] 一个词,一个句子,隐层循环次数
# 因为存储容器是二维张量,所以存储的时候降维一下
# 初始化解码器的第一个输入
decoder_input = torch.tensor([[load_data.SOS_token]], device=device)
# 初始化 解码器隐层张量 也就是编码器隐层最后一层最后一遍循环的输出
decoder_hidden = encoder_hidden
# 根据随机数与teacher_forcing_ratio对比判断是否使用teacher_forching
use_teacher_forcing = True if random.random() < teacher_forcing_ratio else False
# 如果使用teacher_forcing
if use_teacher_forcing:
# 循环遍历目标张量索引,如果使用正确目标进行训练
for di in range(target_length):
# encoder_outputs 编码器输出容器
# decoder_input 解码器输入
# decoder_hidden 编码器最后一个隐层最后一次循环的输出 也就是解码器隐层输入
decoder_output, decoder_hidden, decoder_attention = decoder(decoder_input, decoder_hidden,
encoder_outputs)
# decoder_output:解码器最终输出 decoder_hidden最后一层隐层最后一遍循环锁输出的值, decoder_attention: 注意力权重矩阵
loss += criterion(decoder_output, target_tensor[di])
decoder_input = target_tensor[di] # 因为如果第一次循环结果就错了
# 第二次循环会受到很大的影响,那么就用正确的值输入到下次循环,让模型学习
else:
# 不使用正确目标值训练
for di in range(target_length):
decoder_output, decoder_hidden, decoder_attention = decoder(
decoder_input, decoder_hidden, encoder_outputs)
topv, topi = decoder_output.topk(1) # 取出结果
loss += criterion(decoder_output, target_tensor[di])
# 如果输出值是结束符,就终止循环
if topi.squeeze().item() == load_data.EOS_token:
break
# 如果不是终止符 就继续输入
decoder_input = topi.squeeze().detach() # 将top1降维作为下一次解码器的输入 因为与计算图无关,所以要从计算图中删除
# 也就是将上一次的输入作为输入进行训练
loss.backward() # 开始反向传播
encoder_optimizer.step()
decoder_optimizer.step()
# 更新完误差后,返回平均损失
return loss.item() / target_length
def timeSince(since):
import time
import math
now = time.time()
s = now - since
m = math.floor(s / 60)
s -= m * 60
return '%dm %ds' % (m, s)
def trainAgent(input_lang, output_lang, pairs, encoder, decoder, n_iters, print_every=1000, plot_every=100,
learning_rate=1e-1):
'''
训练代理函数
:param pairs: 数据集
:param encoder: 编码器obj
:param decoder: 解码器obj
:param n_iters: 总迭代步长
:param print_every: 打印日志间隔
:param plot_every: 绘制损失曲线的间隔
:param learning_rate: 学习率
:return:
'''
import time
from torch import optim
start = time.time()
plot_losses = [] # 损失统计曲线容器
print_loss_total = 0 # 每个打印日志间隔总损失
plot_loss_total = 0 # 每个绘图间隔总损失
encoder_optimizer = optim.SGD(encoder.parameters(), lr=learning_rate)
decoder_optimizer = optim.SGD(decoder.parameters(), lr=learning_rate)
# 选择损失函数
criterion = nn.NLLLoss()
num = 0
# 根据步长进行循环
for iter in range(1, n_iters + 1):
# 每次从训练集中取出一条作为训练语句
# training_pair = load_data.tensorfromPair(input_lang, output_lang, pairs)
# pari = random.choice(pairs)
pari, num = choise_random_pairs(num, pairs)
training_pair = load_data.tensorfromPair(input_lang, output_lang, pari)
input_tensor = training_pair[0]
target_tensor = training_pair[1]
loss = train(input_tensor, target_tensor, encoder, decoder, encoder_optimizer, decoder_optimizer, criterion)
print_loss_total += loss
plot_loss_total += loss
# 迭代次数达到日志打印的间隔是
if iter % print_every == 0:
print_loss_avg = print_loss_total / print_every
print_loss_total = 0
print('%s (%d %d百分比) %.4f' % (timeSince(start), iter, iter / n_iters * 100, print_loss_avg))
if iter % plot_every == 0:
plot_loss_avg = plot_loss_total / plot_every
plot_losses.append(plot_loss_avg)
plot_loss_total = 0
import matplotlib.pyplot as plt
plt.figure()
plt.plot(plot_losses)
plt.savefig('./s2sloss.png')
plt.show()
def choise_random_pairs(num, pairs):
if num == len(pairs):
num = 0
else:
num += 1
return pairs[random.randint(0, len(pairs) - 1)], num
if __name__ == '__main__':
hidden_size = 256
input_lang, output_lang, pairs = load_data.prepareData('english', 'french', '../data/eng-fra.txt')
# print(len(pairs)) 训练句子数量10599
# print(input_lang.n_words, output_lang.n_words) 英文词典数量2803 法文词典4345
encoder1 = seq2seq_CreateModel.EncoderRNN(input_lang.n_words, hidden_size).to(device)
print(encoder1)
attn_decoder1 = seq2seq_CreateModel.AttnDecoderRNN(hidden_size, output_lang.n_words, dropout_p=0.1).to(device)
print(attn_decoder1)
# 开始训练
n_iters = 7500
print_every = 100
# for i in pairs:
# trainAgent(input_lang, output_lang, pairs, encoder1, attn_decoder1, n_iters, print_every)
trainAgent(input_lang=input_lang, output_lang=output_lang, pairs=pairs, encoder=encoder1,
decoder=attn_decoder1,
n_iters=n_iters, print_every=print_every, learning_rate=0.01)
torch.save(encoder1.state_dict(), './encoder4.pth')
torch.save(attn_decoder1.state_dict(), './decoder4.pth')
模型评估
import re
import random
import torch
import torch.nn
import torch.nn.functional as F
import seq2seq_CreateModel
import seq2seq_train
import load_data
from io import open
from torch import optim
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
maxlength = 10
# 起始标志
SOS_token = 0
# 结束标志
EOS_token = 1
def evaluate(lang, output_lang, encoder, decoder, sentence, max_length=maxlength):
'''
评估函数
:param output_lang: 解码器实体类对象
:param lang: 输入实体类对象
:param encoder: 编码器
:param decoder: 解码器
:param sentence: 需要评估的句子
:param max_length: 句子词最大个数
:return:
'''
with torch.no_grad():
input_tensor = load_data.tensorfromSentence(lang, sentence) # 返回tensor 句onehot编码
input_length = input_tensor.size()[0]
encoder_hidden = encoder.initHiddenTensor() # 初始化隐层权重矩阵
encoder_outputs = torch.zeros(maxlength, encoder.hidden, device=device) # 缓存容器
# 编码器循环
for ei in range(input_length):
encoder_output, encoder_hidden = encoder(input_tensor[ei], encoder_hidden)
encoder_outputs[ei] = encoder_output[0, 0]
decoder_input = torch.tensor([[SOS_token]], device=device)
decoder_hidden = encoder_hidden
decoder_words = []
decoder_attentions = torch.zeros(maxlength, maxlength) # 初始化注意力权重矩阵
# 解码器循环
for di in range(maxlength):
decoder_output, decoder_hidden, decoder_attention = decoder(decoder_input, decoder_hidden, encoder_outputs)
# print(decoder_output.shape)
decoder_attentions[di] = decoder_attention.data # 将attention结果缓存
topv, topi = decoder_output.data.topk(1)
if topi.item() == EOS_token:
decoder_words.append('<Finall>')
break
else:
decoder_words.append(output_lang.index2word[topi.item()])
decoder_input = topi.squeeze().detach() # 将top1降维作为下一次解码器的输入 因为与计算图无关,所以要从计算图中删除
return decoder_words, decoder_attentions[:di + 1]
def agent_evaluate(encoder, decoder, n, pairs, input_lang, output_lang):
'''
测试代理函数
:param output_lang: 解码器词典对象
:param input_lang: 编码器词典对象
:param pairs: 数据对
:param encoder: 编码器对象
:param decoder: 解码器对象
:param n: 测试次数
:return:
'''
for i in range(n):
pair = random.choice(pairs)
print('input========> ', pair[0])
print('lable----> ', pair[1])
output_words, attentions = evaluate(input_lang, output_lang, encoder, decoder, pair[0], maxlength)
output_sentence = ' '.join(output_words)
print('预测值: ', output_sentence)
print('')
def agent_evaluate_onesentence(encoder, decoder, n, sentence, input_lang, output_lang):
'''
测试代理函数,输入自定义英文
:param output_lang: 解码器词典对象
:param input_lang: 编码器词典对象
:param pairs: 数据对
:param encoder: 编码器对象
:param decoder: 解码器对象
:param n: 测试次数
:return:
'''
print('input========> ', sentence)
output_words, attentions = evaluate(input_lang, output_lang, encoder, decoder, sentence, maxlength)
output_sentence = ' '.join(output_words)
print('预测值: ', output_sentence)
print('')
if __name__ == '__main__':
hidden = 256
print('Please wati a minute,it\'s load data and load model...... ')
input_lang, output_lang, pairs = load_data.prepareData('english', 'french', '../data/eng-fra.txt')
print(input_lang.n_words, output_lang.n_words)
path = '/tmp/pycharm_project_270/Pytorch/day_04_Project/transformer/encoder4.pth'
path2 = '/tmp/pycharm_project_270/Pytorch/day_04_Project/transformer/decoder4.pth'
encoder = seq2seq_CreateModel.EncoderRNN(input_lang.n_words, hidden).to(device)
encoder.load_state_dict(torch.load(path))
decoder = seq2seq_CreateModel.AttnDecoderRNN(hidden, output_lang.n_words, dropout_p=0.1).to(device)
decoder.load_state_dict(torch.load(path2))
print('model load up ')
# 开始测试
agent_evaluate(encoder, decoder, 500, pairs, input_lang, output_lang)
# 自定义测试
# agent_evaluate_onesentence(encoder, decoder, 1, 'we are friend', input_lang, output_lang)
sentence = "what re you doing ?"
output_words, attentions = evaluate(input_lang, output_lang, encoder, decoder, sentence=sentence)
print(output_words, len(output_words))
print(attentions.shape)
import matplotlib.pyplot as plt
plt.matshow(attentions.numpy())
plt.show()
首先 代码是为了实现英译法 的翻译, 但是过程异常 的艰难,我们看一下数据就知道了
这个是做AB实验对照组, 不对数据做过滤的情况下,1w条英文法文对,训练下产生的结果.这里埋下一个伏笔, 有一个横亘所有模型的bug我没有发现导致我找了4天都没找到loss降不下去的原因 嘤嘤嘤
这个是循环训练7500遍,英文数据集一共就2803句话…没啥可说的
因为是随机抽样 梯度并没有下去
那么是不是学习率的问题, 把0.01改成1e-1
这家伙,算了把老老实实的 1e-2 7500次
训练15000次是什么样的?
行吧,误差还是没下去,在3这不走了
那么是不是teacher_forceing 这个参数的影响呢,其他参数不变
这个额参数由原来的0.5 改为0.75
貌似提高纠错的能力,对误差有一点点点乐观影响
那么增加训练轮次能不能降低loss呢?
确实可以降低辖区…但是仅仅提升了0.5的误差,并不是能用的范围, 那么到底是啥到底是啥 让我的误差降低不下去?
原来是…我的解码器输入写成固定的一个词了,我的老天爷
这不仅不是最离谱的,找了4天的问题,终于找到了,
并且非常离谱的是,仅仅解码器只输入1个词, 它却能学到至少3个词的回复
这里就是注意力机制的强大了 真不是强的一定半点
我们来看看解决bug后的atten 可视化图和bug前的attn图
5
这是之前的, 因为bug 导致注意力不准确,但是可以看到,一个词可以关联3个词甚至更多,表明翻译的时候至少会考虑另外两个词与本身词 的关系再翻译
再来看看解决bug后lr=0.02 teacherfource=0.5 epoch=7500次
翻译验证
因为不懂法语,信达雅 这种评估也不会 但是仅仅7500次 已经显而易见的比之前好
那么七万五千次的效果咋样嗯?
很低
注意力矩阵咋样呢?
很优雅. 注意,这里的注意力图像全部都是对同一句话的翻译结果
参数放上
训练的经验总结
未完待续,下一篇来详解atten注意力机制 自注意力机制和QKV注意力机制在seq2seq中的应用如何理解bmm等