当前位置:网站首页>3、NLP模型
3、NLP模型
2022-06-10 02:00:00 【C--G】
RNN











- N vs N -RNN

- N vs 1 -RNN

- 1 vs N -RNN

- ** N vs M -RNN**


传统RNN





g-blog.csdnimg.cn/b99d35a6061a471a990b0f014146c398.png)
import torch
import torch.nn as nn
# 输入x的特征维度,词嵌入的维度
# 隐藏层神经元的个数
# 隐藏层的层数
rnn = nn.RNN(5,6,1)
# 当前批次的样本个数
# 当前样本的sequence_length
# 词嵌入的维度
input1 = torch.randn(1,3,5)
# 隐藏层的层数
# 当前样本的sequence_length
# 隐藏层神经元个数
h0 = torch.randn(1,3,6)
output,hn = rnn(input1,h0)


LSTM



- 遗忘门



- 输入门




- 输出门


Bi-LSTM


import torch
import torch.nn as nn
# 输入x的特征维度,词嵌入的维度
# 隐藏层神经元的个数
# 隐藏层的层数
lstm = nn.LSTM(5,6,2)
# 当前批次的样本个数
# 当前样本的sequence_length
# 词嵌入的维度
input1 = torch.randn(1,3,5)
# 隐藏层的层数
# 当前样本的sequence_length
# 隐藏层神经元个数
h0 = torch.randn(2,3,6)
# 隐藏层的层数
# 当前样本的sequence_length
# 隐藏层神经元个数
c0 = torch.randn(2,3,6)
output,(hn,cn) = lstm(input1,(h0,c0))


GRU







import torch
import torch.nn as nn
# 输入x的特征维度,词嵌入的维度
# 隐藏层神经元的个数
# 隐藏层的层数
lstm = nn.GRU(5,6,2)
# 当前批次的样本个数
# 当前样本的sequence_length
# 词嵌入的维度
input1 = torch.randn(1,3,5)
# 隐藏层的层数
# 当前样本的sequence_length
# 隐藏层神经元个数
h0 = torch.randn(2,3,6)
output,hn = lstm(input1,h0)


注意力机制




import torch
import torch.nn as nn
import torch.nn.functional as F
class Attn(nn.Module):
def __init__(self,query_size,key_size,value_size1,value_size2,output_size):
# query_size 代表q的最后一个维度,key_size代表k的最后一个维度
# V的尺寸表示(1,value_size1,value_size2)
# output_size 代表输出的最后一个维度的大小
super(Attn,self).__init__()
self.query_size = query_size
self.key_size = key_size
self.value_size1 = value_size1
self.value_size2 = value_size2
self.output_size = output_size
# 初始化注意力机制实现中第一步的线性层
self.attn = nn.Linear(self.query_size+self.key_size,self.value_size1)
# 初始化注意力机制实现中第三步的线性层
self.attn_combine = nn.Linear(self.query_size+self.value_size2,self.output_size)
def forward(self,Q,K,V):
# 注意假定Q,K,V都是三维张量
# 第一步,将Q,K进行纵轴的拼接,然后做一次线性变换,最后使用softmax进行处理得到注意力向量
attn_weights = F.softmax(self.attn(torch.cat((Q[0],K[0]),1)),dim=1)
# 将注意力矩阵和V进行一次bmm运算
attn_applied = torch.bmm(attn_weights.unsqueeze(0),V)
# 再次取Q[0]进行降维,再次和上面的运算结果进行一次拼接
output = torch.cat((Q[0],attn_applied[0]),1)
# 第三步就是将上面的输出进行一次线性变换,然后扩展维度为三维张量
output = self.attn_combine(output).unsqueeze(0)
return output,attn_weights
query_size = 32
ket_size = 32
value_size1 = 32
value_size2 = 64
output_size = 64
attn = Attn(query_size,ket_size,value_size1,value_size2,output_size)
Q = torch.randn(1,1,32)
K = torch.randn(1,1,32)
V = torch.randn(1,32,64)
output = attn(Q,K,V)
print(output[0])
print(output[0].size())
print(output[1])
print(output[1].size())

案例:RNN模型构建人名分类器



from io import open
import glob
import os
import string
import unicodedata
import random
import time
import math
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
定义工具
获取所有常用字符包括字母和常用标点
all_letters = string.ascii_letters + " .,;'"
# 获取常用字符数量
n_letters = len(all_letters)
n_letters

字符规范化之unicode转Ascii函数
# 去掉一些语言中的重音标记
def unicodeToAscii(s):
return ''.join(c for c in unicodedata.normalize('NFD',s)
if unicodedata.category(c) != "Mn"
and c in all_letters
)
获取数据
data_path = "data/names/"
def readLines(file_name):
# 打开指定的文件并读取所有的内容,使用strip()去掉两侧的空白符,然后以‘\n’为换行符进行切分
lines = open(file_name,encoding="utf-8").read().strip().split('\n')
return [unicodeToAscii(line) for line in lines]
file_name = data_path + "Chinese.txt"
result = readLines(file_name)
result[0:20]

# 构建人名类别(所属的语言)列表与人名对应的关系字典
# 构建category_lines {"English":["Lily","Susan","kobe"],"Chinese":[zhan san]}
category_lines = {
}
# all_categories 形如:["English",...,"Chinese"]
all_categories = []
# 读取指定路径下的txt文件,使用glob,path中可以使用正则表达式
for file_name in glob.glob(data_path+"*.txt"):
# 获取每个文件的文件名,就是对应的名字类别
category = os.path.splitext(os.path.basename(file_name))[0]
# 将其逐一装到all_categories列表中
all_categories.append(category)
# 然后读取每个文件的内容,形成名字列表
lines = readLines(file_name)
# 按照对应的类别,将名字列表写入到category_lines字典中
category_lines[category] = lines
# 查看类别总数
n_categories = len(all_categories)
n_categories

category_lines["Chinese"][:5]

# 将人名转换为对应的onehot张量表示
def lineToTensor(line):
# 初始化一个0张量,(len(line),1,n_letters)
# 代表人名中的每个字母用一个1 x n_letters的张量表示
tensor = torch.zeros(len(line),1,n_letters)
# 遍历这个人名中的每个字符索引和字符
for li,letter in enumerate(line):
# 使用字符串方法find找到每个字符在all_letters中的索引
# 同时也是生存onehot张量中1的索引位置
tensor[li][0][all_letters.find(letter)] = 1
return tensor
构建模型
- 构建RNN模型
class RNN(nn.Module):
def __init__(self,input_size,hidden_size,output_size,num_layers=1):
# RNN输入最后一维尺寸,RNN的隐层最后一维尺寸,RNN层数
super(RNN,self).__init__()
self.hidden_size = hidden_size
self.num_layers = num_layers
# 实例化预定义的nn.RNN, 三个参数 input_size,hidden_size,num_layers
self.rnn = nn.RNN(input_size,hidden_size,num_layers)
# 实例化nn.Linear, 这个线性层用于将nn.RNN的输出维度转化为指定的输出维度
self.linear = nn.Linear(hidden_size,output_size)
# 实例化nn中预定的Softmax层,用于从输出层获得类别结果
self.softmax = nn.LogSoftmax(dim=-1)
def forward(self,input,hidden):
# 完成传统RNN的主要逻辑,输入参数input代表输入张量,形状是 1 * n_letters
# hidden代表RNN的隐层张量,形状是self.num_layers * 1 * self.hidden_size
# 因为预定义的nn.RNN要求输入维度一定是三位张量,因此在这里使用unsqueeze(0)扩展一个维度
input = input.unsqueeze(0)
# 将input和hidden输入到传统RNN的实例化对象中,如果num_layers=1,rr恒等于hn
rr,hn = self.rnn(input,hidden)
# 将从RNN中获得的结果通过线性变换和softmax返回,同时返回hn作为后续RNN的输入
return self.softmax(self.linear(rr)),hn
def init_hidden(self):
# 初始化隐层张量
# 初始化一个(self.num_layers,1,self.hidden_size) 形状的0张量
return torch.zeros(self.num_layers,1,self.hidden_size)
- 构建LSTM模型
class LSTM(nn.Module):
def __init__(self,input_size,hidden_size,output_size,num_layers=1):
# RNN输入最后一维尺寸,RNN的隐层最后一维尺寸,RNN层数
super(LSTM,self).__init__()
self.hidden_size = hidden_size
self.num_layers = num_layers
# 实例化预定义的nn.LSTM, 三个参数 input_size,hidden_size,num_layers
self.lstm = nn.LSTM(input_size,hidden_size,num_layers)
# 实例化nn.Linear, 这个线性层用于将nn.RNN的输出维度转化为指定的输出维度
self.linear = nn.Linear(hidden_size,output_size)
# 实例化nn中预定的Softmax层,用于从输出层获得类别结果
self.softmax = nn.LogSoftmax(dim=-1)
def forward(self,input,hidden,c):
# 完成传统RNN的主要逻辑,输入参数input代表输入张量,形状是 1 * n_letters
# hidden代表RNN的隐层张量,形状是self.num_layers * 1 * self.hidden_size
# 因为预定义的nn.RNN要求输入维度一定是三位张量,因此在这里使用unsqueeze(0)扩展一个维度
input = input.unsqueeze(0)
# 将input和hidden输入到传统RNN的实例化对象中,如果num_layers=1,rr恒等于hn
rr,(hn,c) = self.lstm(input,(hidden,c))
# 将从RNN中获得的结果通过线性变换和softmax返回,同时返回hn作为后续RNN的输入
return self.softmax(self.linear(rr)),hn,c
def initHiddenAndC(self):
# 初始化隐层张量
# 初始化一个(self.num_layers,1,self.hidden_size) 形状的0张量
c = hidden = torch.zeros(self.num_layers,1,self.hidden_size)
return hidden,c
- 构建GRU模型
class GRU(nn.Module):
def __init__(self,input_size,hidden_size,output_size,num_layers=1):
# RNN输入最后一维尺寸,RNN的隐层最后一维尺寸,RNN层数
super(GRU,self).__init__()
self.hidden_size = hidden_size
self.num_layers = num_layers
# 实例化预定义的nn.LSTM, 三个参数 input_size,hidden_size,num_layers
self.gru = nn.GRU(input_size,hidden_size,num_layers)
# 实例化nn.Linear, 这个线性层用于将nn.RNN的输出维度转化为指定的输出维度
self.linear = nn.Linear(hidden_size,output_size)
# 实例化nn中预定的Softmax层,用于从输出层获得类别结果
self.softmax = nn.LogSoftmax(dim=-1)
def forward(self,input,hidden):
# 完成传统RNN的主要逻辑,输入参数input代表输入张量,形状是 1 * n_letters
# hidden代表RNN的隐层张量,形状是self.num_layers * 1 * self.hidden_size
# 因为预定义的nn.RNN要求输入维度一定是三位张量,因此在这里使用unsqueeze(0)扩展一个维度
input = input.unsqueeze(0)
# 将input和hidden输入到传统RNN的实例化对象中,如果num_layers=1,rr恒等于hn
rr,hn = self.gru(input,hidden)
# 将从RNN中获得的结果通过线性变换和softmax返回,同时返回hn作为后续RNN的输入
return self.softmax(self.linear(rr)),hn
def initHidden(self):
# 初始化隐层张量
# 初始化一个(self.num_layers,1,self.hidden_size) 形状的0张量
return torch.zeros(self.num_layers,1,self.hidden_size)
# 因为是onehot编码,输入张量最后一维的尺寸是n_letters
input_size = n_letters
# 定义隐层的最后一维尺寸大小
n_hidden = 128
# 输出尺寸为语言类别总数n_categories
output_size = n_categories
# num_layer 使用默认值 1
# 假如使用字母B作为RNN的首次输入,它通过lineToTensor转为张量
# 因为文明的lineToTensor输出是三维张量,而RNN类需要的二维张量
# 因此使用squeeze(0)降低一个维度
input1 = lineToTensor("B").squeeze(0)
# 初始化一个三维的隐层0张量,也是初始的细胞状态张量
hidden = c = torch.zeros(1,1,n_hidden)
rnn = RNN(input_size,n_hidden,output_size)
lstm = LSTM(input_size,n_hidden,output_size)
gru = GRU(input_size,n_hidden,output_size)
rnn_output,next_hidden = rnn(input1,hidden)
print("rnn",rnn_output)
print("rnn_shape",rnn_output.shape)
print("**************")
lstm_output,next_hidden1,c = lstm(input1,hidden,c)
print("rnn",lstm_output)
print("rnn_shape",lstm_output.shape)
print("**************")
gru_output,next_hidden2 = gru(input1,hidden)
print("rnn",gru_output)
print("rnn_shape",gru_output.shape)

从输出结果中获得指定类别, 参数为输出张量output
def categoryFromOutput(output):
# 从输出张量中返回最大的值和索引对象, 我们这里主要需要这个索引
top_n, top_i = output.topk(1)
# top_i对象中取出索引的值
category_i = top_i[0].item()
# 根据索引值获得对应语言类别, 返回语言类别和索引值
return all_categories[category_i], category_i
category, category_i = categoryFromOutput(rnn_output)
print("category:", category)
print("category_i:", category_i)

随机产生训练数据
def randomTrainingExample():
# 首先使用random的choice方法从all_categories随机选择一个类别
category = random.choice(all_categories)
# 然后在通过category_lines字典取category类别对应的名字列表
# 之后再从列表中随机取一个名字
line = random.choice(category_lines[category])
# 接着将这个类别在所有类别列表中的索引封装成tensor, 得到类别张量category_tensor
category_tensor = torch.tensor([all_categories.index(category)], dtype=torch.long)
# 最后, 将随机取到的名字通过函数lineToTensor转化为onehot张量表示
line_tensor = lineToTensor(line)
return category, line, category_tensor, line_tensor
构建传统RNN训练函数
# 定义损失函数为nn.NLLLoss,因为RNN的最后一层是nn.LogSoftmax, 两者的内部计算逻辑正好能够吻合.
criterion = nn.NLLLoss()
# 设置学习率为0.005
learning_rate = 0.005
def trainRNN(category_tensor, line_tensor):
"""定义训练函数, 它的两个参数是category_tensor类别的张量表示, 相当于训练数据的标签, line_tensor名字的张量表示, 相当于对应训练数据"""
# 在函数中, 首先通过实例化对象rnn初始化隐层张量
hidden = rnn.init_hidden()
# 然后将模型结构中的梯度归0
rnn.zero_grad()
# 下面开始进行训练, 将训练数据line_tensor的每个字符逐个传入rnn之中, 得到最终结果
for i in range(line_tensor.size()[0]):
output, hidden = rnn(line_tensor[i], hidden)
# 因为我们的rnn对象由nn.RNN实例化得到, 最终输出形状是三维张量, 为了满足于category_tensor
# 进行对比计算损失, 需要减少第一个维度, 这里使用squeeze()方法
loss = criterion(output.squeeze(0), category_tensor)
# 损失进行反向传播
loss.backward()
# 更新模型中所有的参数
for p in rnn.parameters():
# 将参数的张量表示与参数的梯度乘以学习率的结果相加以此来更新参数
p.data.add(-learning_rate, p.grad.data)
# 返回结果和损失的值
return output, loss.item()
torch.add_
>>> a = torch.randn(4)
>>> a
tensor([-0.9732, -0.3497, 0.6245, 0.4022])
>>> b = torch.randn(4, 1)
>>> b
tensor([[ 0.3743],
[-1.7724],
[-0.5811],
[-0.8017]])
>>> torch.add(a, b, alpha=10) # alpha的作用是 alpha * b + a
tensor([[ 2.7695, 3.3930, 4.3672, 4.1450],
[-18.6971, -18.0736, -17.0994, -17.3216],
[ -6.7845, -6.1610, -5.1868, -5.4090],
[ -8.9902, -8.3667, -7.3925, -7.6147]])
构建LSTM训练函数
# 与传统RNN相比多出细胞状态c
def trainLSTM(category_tensor, line_tensor):
hidden, c = lstm.initHiddenAndC()
lstm.zero_grad()
for i in range(line_tensor.size()[0]):
# 返回output, hidden以及细胞状态c
output, hidden, c = lstm(line_tensor[i], hidden, c)
loss = criterion(output.squeeze(0), category_tensor)
loss.backward()
for p in lstm.parameters():
p.data.add_(-learning_rate, p.grad.data)
return output, loss.item()
构建GRU训练函数
# 与传统RNN完全相同, 只不过名字改成了GRU
def trainGRU(category_tensor, line_tensor):
hidden = gru.initHidden()
gru.zero_grad()
for i in range(line_tensor.size()[0]):
output, hidden= gru(line_tensor[i], hidden)
loss = criterion(output.squeeze(0), category_tensor)
loss.backward()
for p in gru.parameters():
p.data.add_(-learning_rate, p.grad.data)
return output, loss.item()
训练模型
- 构建时间计算函数
def timeSince(since):
"获得每次打印的训练耗时, since是训练开始时间"
# 获得当前时间
now = time.time()
# 获得时间差,就是训练耗时
s = now - since
# 将秒转化为分钟, 并向下 取整
m = math.floor(s / 60)
# 计算剩下不够凑成1分钟的秒数
s -= m * 60
# 返回指定格式的耗时
return '%dm %ds' % (m, s)
- 构建训练过程的日志打印函数
# 设置训练迭代次数
n_iters = 1000
# 设置结果的打印间隔
print_every = 50
# 设置绘制损失曲线上的制图间隔
plot_every = 10
def train(train_type_fn):
"""训练过程的日志打印函数, 参数train_type_fn代表选择哪种模型训练函数, 如trainRNN"""
# 每个制图间隔损失保存列表
all_losses = []
# 获得训练开始时间戳
start = time.time()
# 设置初始间隔损失为0
current_loss = 0
# 从1开始进行训练迭代, 共n_iters次
for iter in range(1, n_iters + 1):
# 通过randomTrainingExample函数随机获取一组训练数据和对应的类别
category, line, category_tensor, line_tensor = randomTrainingExample()
# 将训练数据和对应类别的张量表示传入到train函数中
output, loss = train_type_fn(category_tensor, line_tensor)
# 计算制图间隔中的总损失
current_loss += loss
# 如果迭代数能够整除打印间隔
if iter % print_every == 0:
# 取该迭代步上的output通过categoryFromOutput函数获得对应的类别和类别索引
guess, guess_i = categoryFromOutput(output)
# 然后和真实的类别category做比较, 如果相同则打对号, 否则打叉号.
correct = '*' if guess == category else '* (%s)' % category
# 打印迭代步, 迭代步百分比, 当前训练耗时, 损失, 该步预测的名字, 以及是否正确
print('%d %d%% (%s) %.4f %s / %s %s' % (iter, iter / n_iters * 100, timeSince(start), loss, line, guess, correct))
# 如果迭代数能够整除制图间隔
if iter % plot_every == 0:
# 将保存该间隔中的平均损失到all_losses列表中
all_losses.append(current_loss / plot_every)
# 间隔损失重置为0
current_loss = 0
# 返回对应的总损失列表和训练耗时
return all_losses, int(time.time() - start)
- 开始训练传统RNN, LSTM, GRU模型并制作对比图
# 调用train函数, 分别进行RNN, LSTM, GRU模型的训练
# 并返回各自的全部损失, 以及训练耗时用于制图
all_losses1, period1 = train(trainRNN)
all_losses2, period2 = train(trainLSTM)
all_losses3, period3 = train(trainGRU)
# 绘制损失对比曲线, 训练耗时对比柱张图
# 创建画布0
plt.figure(0)
# 绘制损失对比曲线
plt.plot(all_losses1, label="RNN")
plt.plot(all_losses2, color="red", label="LSTM")
plt.plot(all_losses3, color="orange", label="GRU")
plt.legend(loc='upper left')
# 创建画布1
plt.figure(1)
x_data=["RNN", "LSTM", "GRU"]
y_data = [period1, period2, period3]
# 绘制训练耗时对比柱状图
plt.bar(range(len(x_data)), y_data, tick_label=x_data)







构建评估函数并进行预测
传统RNN评估函数
def evaluateRNN(line_tensor):
"""评估函数, 和训练函数逻辑相同, 参数是line_tensor代表名字的张量表示"""
# 初始化隐层张量
hidden = rnn.init_hidden()
# 将评估数据line_tensor的每个字符逐个传入rnn之中
for i in range(line_tensor.size()[0]):
output, hidden = rnn(line_tensor[i], hidden)
# 获得输出结果
return output.squeeze(0)
构建LSTM评估函数
def evaluateLSTM(line_tensor):
# 初始化隐层张量和细胞状态张量
hidden, c = lstm.initHiddenAndC()
# 将评估数据line_tensor的每个字符逐个传入lstm之中
for i in range(line_tensor.size()[0]):
output, hidden, c = lstm(line_tensor[i], hidden, c)
return output.squeeze(0)
构建GRU评估函数
def evaluateGRU(line_tensor):
hidden = gru.initHidden()
# 将评估数据line_tensor的每个字符逐个传入gru之中
for i in range(line_tensor.size()[0]):
output, hidden = gru(line_tensor[i], hidden)
return output.squeeze(0)
line = "Bai"
line_tensor = lineToTensor(line)
rnn_output = evaluateRNN(line_tensor)
lstm_output = evaluateLSTM(line_tensor)
gru_output = evaluateGRU(line_tensor)
print("rnn_output:", rnn_output)
print("gru_output:", lstm_output)
print("gru_output:", gru_output)
构建预测函数
def predict(input_line, evaluate, n_predictions=3):
"""预测函数, 输入参数input_line代表输入的名字, n_predictions代表需要取最有可能的top个"""
# 首先打印输入
print('\n> %s' % input_line)
# 以下操作的相关张量不进行求梯度
with torch.no_grad():
# 使输入的名字转换为张量表示, 并使用evaluate函数获得预测输出
output = evaluate(lineToTensor(input_line))
# 从预测的输出中取前3个最大的值及其索引
topv, topi = output.topk(n_predictions, 1, True)
# 创建盛装结果的列表
predictions = []
# 遍历n_predictions
for i in range(n_predictions):
# 从topv中取出的output值
value = topv[0][i].item()
# 取出索引并找到对应的类别
category_index = topi[0][i].item()
# 打印ouput的值, 和对应的类别
print('(%.2f) %s' % (value, all_categories[category_index]))
# 将结果装进predictions中
predictions.append([value, all_categories[category_index]])
边栏推荐
- [program life] "stage summary" - at a loss
- 免费批量导入生成sitemap地图的软件
- MySQL表管理(文件)
- Using GPU accelerated training model in keras; Install CUDA; cudnn; cudnn_ cnn_ infer64_ 8.dll is not in path; device_ lib. list_ local_ Devices has no GPU; hang up
- SQL statement writing
- Introduction à la définition et au calcul de la variance Allan
- Inftnews | the future of NFT in Web3 economy
- D mapping instructions in memory and executing
- Word tutorial, how to apply styles and themes in word?
- MySql安装+测试连接+常用终端mysql命令(超详细)
猜你喜欢
![[program life]](/img/90/347b53a5725beec9107c49f8add148.png)
[program life] "stage summary" - at a loss

Free batch import software for generating sitemap maps

在keras中使用gpu加速训练模型;安装cuda;cudnn;cudnn_cnn_infer64_8.dll 不在path中;device_lib.list_local_devices无gpu;挂掉

10 common high-frequency business scenarios that trigger IO bottlenecks

Inftnews | the future of NFT in Web3 economy

LABVIEW_ Classroom notes random (XIII) sub VI

Allan方差定義與計算方法簡介

LeetCode 700:二叉搜索树中的搜索

With all due respect: programmers spend most of their time not writing code, but...

餐饮电子采购方案:采购全周期管理,节省30%成本
随机推荐
[email protected]和[email protected]装载5-Fu(5-氟尿
证明婚内出轨的几种证据
The JS mouse changes the font color and returns to normal
The idea version of postman comes out, which is easy to test and use
1265_ Implementation analysis of adding tasks to task ready linked list in FreeRTOS
MySQL 多表查询
Custom action logging annotation
[program life] "stage summary" - at a loss
iNFTnews | NFT在Web3经济里的未来
code....
Allan方差定義與計算方法簡介
28. How much do you know about subject management?
Nodejs reported an internal error typeerror: cannot read property 'destroy' of undefined
投资新手买收益率多少的理财产品合适?
LabVIEW在波形图或波形图表上显示时间和日期
“无法访问 您可能没有权限使用网络资源”解决办法
[leetcode] sword finger offer II 016 Longest substring without duplicate characters
Industrial practice example of industrial defect detection based on openvino deployment
Customized page (e.g. website map) button beautification tutorial at the bottom of WordPress website
2022年制冷与空调设备运行操作复训题库模拟考试平台操作