本文目录
- 1. DNN
- 2. RNN
- 3. RNN Cell 具体计算过程
- 4. Pytorch实现RNN
- 4.1 创建RNNcell再写循环
- 4.2 直接调用RNN
- 5. 多层RNN
- 6. 案例
- 6.1 使用RNN_cell
- 6.2 使用RNN
- 7. 独热向量
- one-hot缺点
- 改进目标
- 网络结构
- 完整代码
- 课后练习1:LSTM实现之前的模型
- 代码:
- 结果:
- 课后练习2:GRU实现之前的模型
- 代码:
- 结果:
- 学习资料
- 系列文章索引
深度神经网络是稠密网络,Dense连接就是指全连接,有很多线性层对输入数据进行空间上的变换,又叫DNN。比如预测天气,假设输入前3天的特征(温度、气压、雨天),预测第4天是否下雨?
第一种方法:把x1,x2,x3拼成有9个维度的长向量,然后去训练最后一天是否有雨。用全连接稠密网络进行预测,如果输入序列很长,而且每一个序列维度很高的话,对网络训练有很大挑战,因为稠密网络(全连接网络)实际上权重是最多的。
对比CNN网络和DNN网络的权重数量:全连接网络的权重是最多的。
对于卷积层:比如输入通道是128个,输出通道是64个,如果用5x5的卷积,权重数就是 2564188=204800,卷积层的输入输出只与通道数和卷积核的大小有关,全连接层和变换之后的数据大小有关,比如3阶张量经过一系列的卷积变换还剩下4096个元素,4096我们很少直接降成1维或者10维,而是先降成1024维,全连接层的权重为4096*1024=4194304,所以相比起来,卷积层的权重并不多,而全连接层的权重较多。全连接层是在网络的全部参数中占大头的。
CNN在做卷积的时候,它的卷积核是共享的。所以它的权重数量比较少,它并不是图像上的每一个像素要和下一层的featureMap建立连接,权重数量就少。处理视频的时候,每一帧就少一张图像,我们需要把一组图像做成一个集合,如果用全连接网络的话,使用到的权重的数量就是一个天文数字,极大可能难以处理。
RNN是用来专门处理带有序列的数据,也会使用权重共享来减少权重的数量。它把x1,x2,x3看成是一个序列,不仅考虑x1,x2之间的连接关系,还考虑x1,x2的时间上的先后顺序(x2依赖于x1,x3依赖于x2),下一天的天气状况部分依赖于前一天的天气状况,RNN主要处理这种具有序列连接的数据。
数据是有先后顺序进行连接的,也就是当天数据会依赖之前数据。比如股市、金融数据、自然语言(我爱北京天安门)
RNN是循环神经网络,RNN其实是对线性层的复用。RNN Cell本质是一个线性层(linear),把一个维度映射到另一个维度(比如把输入的3维向量xt 变成输出5维向量 ht )。 这个线性层与普通的线性层的区别是这个线性层是共享的。
如上图右侧的结构是对左侧结构的展开。h0是一种先验知识,如果知道输出的维度,可以选择将其每一个维度都设置为0。也可以使用CNN+fc的网络去生成先验知识,实现图像到文本的转换。
RNN相当于一个形成层,所以可以简单的用代码进行描述:
linear = Linear()
h = 0
for x in X:
h = linear(x,h)
相当于
h1 = linear(x1,h0)
h2 = linear(x2,h1)
h3 = linear(x3,h2)
完整的定义RNN:将RNN cell以循环的形式一个一个送进去,然后依次算出隐藏层的过程,称之为循环神经网络。
循环神经网络的激活函数用的是tanh(为什么呢?因为tanh的取值在-1到+1之间)
补充:Tanh的诞生比Sigmoid晚一些,sigmoid函数我们提到过有一个缺点就是输出不以0为中心,使得收敛变慢的问题。而Tanh则就是解决了这个问题。
主要是确定输入的维度和隐藏层的维度
如上图,RNN本质上还是一个线性层,要弄清楚维度。代码如下:
# pytorch实现
cell = torch.nn.RNNcell(input_size=input_size,hidden_size=hidden_size)
hidden = cell(input,hidden) # h1 = cell(x1,h0)
举个例子:
输入有三个特征,每个特征是4维的,隐藏层是2维向量
import torch
# 参数设置
batch_size = 1
seq_len = 3
input_size = 4
hidden_size = 2
# 构造RNN cell
cell = torch.nn.RNNCell(input_size=input_size,hidden_size=hidden_size)
# 维度 (seq,batch,features)
dataset = torch.randn(seq_len,batch_size,input_size)
hidden = torch.zeros(batch_size,hidden_size) # 初始化零向量
for idx,input in enumerate(dataset):
print("="*20,idx,"="*20)
print("Input size:",input.shape)
hidden = cell(input,hidden)
print("Output size:",hidden.shape)
print(hidden)
直接使用torch.nn.RNN()需要知道input_size、hidden_size和num_layers(RNN有多少层,默认为1)
cell(inputs,hidden)中 inputs指包含整个输入序列(x1,x2,x3,…xN),hidden指h0。
用RNN自动循环,所以输入的时候要把所有的序列都送进去,然后给定h0,然后我们就会得到所有的隐层输出以及最后一层的输出。
import torch
batch_size = 1
seq_len = 5
input_size = 4
hidden_size = 2
num_layers = 3
#其他参数
#batch_first=True 维度从(SeqLen*Batch*input_size)变为(Batch*SeqLen*input_size)
cell = torch.nn.RNN(input_size = input_size, hidden_size = hidden_size, num_layers = num_layers)
inputs = torch.randn(seq_len, batch_size, input_size)
hidden = torch.zeros(num_layers, batch_size, hidden_size)
out, hidden = cell(inputs, hidden)
print("Output size: ", out.shape)
print("Output: ", out)
print("Hidden size: ", hidden.shape)
print("Hidden: ", hidden)
注:batch_first=True 维度从(SeqLenBatchinput_size)变为(BatchSeqLeninput_size)
当RNN有多层,下图中同样颜色的RNNCell是同一个即有3个线性层(一个RNNCell是一个线性层)。因为每一层都需要隐藏层h的参数,因此隐藏层维度需要numLayers参数。
多分类问题(输出维度为4)经过Softmax求得映射之后的概率分别是多少,再利用输出对应的独热向量,计算NLLLoss
import torch
input_size = 4
hidden_size = 4
batch_size = 1
#构建输入输出字典
idx2char = ['e', 'h', 'l', 'o']
x_data = [1, 0, 2, 2, 3]
y_data = [3, 1, 2, 3, 2]
one_hot_lookup = [[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]]
#构造独热向量,此时向量维度为(SeqLen*InputSize)
x_one_hot = [one_hot_lookup[x] for x in x_data]
#view(-1……)保留原始SeqLen,并添加batch_size,input_size两个维度
inputs = torch.Tensor(x_one_hot).view(-1, batch_size, input_size)
#将labels转换为(SeqLen*1)的维度
labels = torch.LongTensor(y_data).view(-1, 1)
class Model(torch.nn.Module):
def __init__(self, input_size, hidden_size, batch_size):
super(Model, self).__init__()
self.batch_size = batch_size
self.input_size = input_size
self.hidden_size = hidden_size
self.rnncell = torch.nn.RNNCell(input_size = input_size,
hidden_size = hidden_size)
def forward(self, input, hidden):
# RNNCell input = (batchsize*inputsize)
# RNNCell hidden = (batchsize*hiddensize)
# h_i = cell(x_i , h_i-1)
hidden = self.rnncell(input, hidden)
return hidden
#初始化零向量作为h0,只有此处用到batch_size
def init_hidden(self):
return torch.zeros(self.batch_size, self.hidden_size)
model = Model(input_size, hidden_size, batch_size)
criterion = torch.nn.CrossEntropyLoss(reduction="mean")
optimizer = torch.optim.Adam(model.parameters(), lr=0.1)
for epoch in range(15):
#损失及梯度置0,创建前置条件h0
loss = 0
optimizer.zero_grad()
hidden = model.init_hidden()
print("Predicted string: ",end="")
#inputs=(seqLen*batchsize*input_size) labels = (seqLen*1)
#input是按序列取的inputs元素(batchsize*inputsize)
#label是按序列去的labels元素(1)
for input, label in zip(inputs, labels):
hidden = model(input, hidden)
#序列的每一项损失都需要累加
loss += criterion(hidden, label)
#多分类取最大,找到四个类中概率最大的下标
_, idx = hidden.max(dim=1)
print(idx2char[idx.item()], end='')
loss.backward()
optimizer.step()
print(", Epoch [%d/15] loss = %.4f" % (epoch+1, loss.item()))
import torch
input_size = 4
hidden_size = 4
batch_size = 1
num_layers = 1
seq_len = 5
# 准备数据
idx2char = ['e','h','l','o'] # 字典
x_data = [1,0,2,2,3] # hello
y_data = [3,1,2,3,2] # ohlol
one_hot_lookup = [[1,0,0,0],
[0,1,0,0],
[0,0,1,0],
[0,0,0,1]] # ont_hot查询
x_one_hot = [one_hot_lookup[x] for x in x_data] # seq * input_Size
# (seqlen,batchsize,inputsize)
inputs = torch.Tensor(x_one_hot).view(seq_len,batch_size,input_size)
# label (seqlen,1)
labels = torch.LongTensor(y_data)
# 构造模型
class Model(torch.nn.Module):
def __init__(self,batch_size,input_size,hidden_size,num_layers = 1):
super(Model, self).__init__()
self.batch_size = batch_size
self.input_size = input_size
self.hidden_size = hidden_size
self.num_layers = num_layers
# input维度(batchsize,inputsize)
# hidden维度(batchsize,hiddensize)
self.rnn = torch.nn.RNN(input_size=self.input_size,hidden_size=self.hidden_size,num_layers=self.num_layers)
def forward(self,input):
hidden = torch.zeros(self.num_layers,self.batch_size,self.hidden_size)
out,_ = self.rnn(input,hidden)
return out.view(-1,self.hidden_size) # 转变维2维矩阵,seq*batchsize*1 -》((seq*batchsize),1)
model = Model(input_size=input_size,hidden_size=hidden_size,batch_size=batch_size,num_layers=1)
# 损失函数和优化器
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(),lr=0.05) # lr = 0.01学习的太慢
# 训练
for epoch in range(15):
optimizer.zero_grad()
outputs = model(inputs) # inputs是(seq,Batchsize,Inputsize) outputs是(seq,Batchsize,Hiddensize)
loss = criterion(outputs,labels) # labels是(seq,batchsize,1)
loss.backward()
optimizer.step()
_,idx = outputs.max(dim=1)
idx = idx.data.numpy()
print("Predicted:",''.join([idx2char[x] for x in idx]),end='')
print(",Epoch {}/15 loss={:.3f}".format(epoch+1,loss.item()))
综上所述,需要一种低维度的、稠密的、可学习数据的编码方式
输入是2,就去表里查找2的位置,输出向量。
取第2行的向量,我们可以通过矩阵乘法来获得:将上述4x5行的矩阵进行转置变成5x4,然后乘以一个[0 0 1 0]^T就可以取出第2行的向量。
求反向传播导数的话,我们就可以通过矩阵乘法的导数来进行计算。
之前的RNN cell输出的隐藏层是和分类的数量是一致的, 如果不一致的话,可以再接一个线性层。
import torch
# 使用RNN 有嵌入层和线性层
num_class = 4 # 4个类别
input_size = 4 # 输入维度是4
hidden_size = 8 # 隐层是8个维度
embedding_size = 10 # 嵌入到10维空间
batch_size = 1
num_layers = 2 # 两层的RNN
seq_len = 5 # 序列长度是5
# 准备数据
idx2char = ['e','h','l','o'] # 字典
x_data = [[1,0,2,2,3]] # hello 维度(batch,seqlen)
y_data = [3,1,2,3,2] # ohlol 维度 (batch*seqlen)
# (batchsize,seqlen)
inputs = torch.LongTensor(x_data)
# label (batchsize*seqlen)
labels = torch.LongTensor(y_data)
# print(labels.shape)
# print(labels)
# 构造模型
class Model(torch.nn.Module):
def __init__(self):
super(Model, self).__init__()
self.emb = torch.nn.Embedding(input_size,embedding_size)
self.rnn = torch.nn.RNN(input_size=embedding_size,hidden_size=hidden_size,num_layers=num_layers,batch_first=True)
self.fc = torch.nn.Linear(hidden_size,num_class)
def forward(self,x):
hidden = torch.zeros(num_layers,x.size(0),hidden_size)
x = self.emb(x) # (batch,seqlen,embeddingsize)
x,_ = self.rnn(x,hidden)
x = self.fc(x)
return x.view(-1,num_class) # 转变维2维矩阵,seq*batchsize*numclass -》((seq*batchsize),numclass)
model = Model()
# 损失函数和优化器
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(),lr=0.05) # lr = 0.01学习的太慢
# 训练
for epoch in range(15):
optimizer.zero_grad()
outputs = model(inputs) # inputs是(seq,Batchsize,Inputsize) outputs是(seq,Batchsize,Hiddensize)
loss = criterion(outputs,labels) # labels是(seq,batchsize,1)
loss.backward()
optimizer.step()
_,idx = outputs.max(dim=1)
idx = idx.data.numpy()
print("Predicted:",''.join([idx2char[x] for x in idx]),end='')
print(",Epoch {}/15 loss={:.3f}".format(epoch+1,loss.item()))
LSTM效果比RNN好得多,但由于计算复杂度上升,运算效率低,训练时间长。
增加c_t+1的这条路径,在反向传播时,提供更快梯度下降的路径,减少梯度消失。
torch.nn.LSTM():
import torch
num_class = 4
input_size = 4
hidden_size = 8
embedding_size = 10
batch_size = 1
num_layers = 1
seq_len = 5
# 准备数据
idx2char = ['e','h','l','o'] # 字典
x_data = [[1,0,2,2,3]] #维度(batch,seqlen)
y_data = [3,1,2,3,2] #ohlol
inputs = torch.LongTensor(x_data)
labels = torch.LongTensor(y_data)
# 构造模型
class Model(torch.nn.Module):
def __init__(self):
super(Model, self).__init__()
self.emb = torch.nn.Embedding(input_size,embedding_size) #返回(batch,seqlen,embeddingsize)
self.lstm = torch.nn.LSTM(input_size=embedding_size,
hidden_size=hidden_size,
num_layers=num_layers)
self.fc = torch.nn.Linear(hidden_size,num_class)
def forward(self,x):
hidden = torch.zeros(num_layers,x.size(0),hidden_size)
x = self.emb(x) # (batch,seqlen,embeddingsize)
x,_ = self.lstm(x)
x = self.fc(x)
return x.view(-1,num_class) # 转变2维矩阵,seq*batchsize*numclass -》((seq*batchsize),numclass)
model = Model()
# 损失函数和优化器
criterion = torch.nn.CrossEntropyLoss(reduction='mean')
optimizer = torch.optim.Adam(model.parameters(),lr=0.01) # lr = 0.01学习的太慢
# 训练
for epoch in range(15):
optimizer.zero_grad()
outputs = model(inputs) # inputs是(seq,Batchsize,Inputsize) outputs是(seq,Batchsize,Hiddensize)
loss = criterion(outputs,labels) # labels是(seq,batchsize,1)
loss.backward()
optimizer.step()
_,idx = outputs.max(dim=1) # 找到矩阵每行的最大值
idx = idx.data.numpy() # 将tensor转成n数组
print("Predicted:",''.join([idx2char[x] for x in idx]),end='')
print(",Epoch {}/15 loss={:.3f}".format(epoch+1,loss.item()))
import torch
num_class = 4
input_size = 4
hidden_size = 8
embedding_size = 10
batch_size = 1
num_layers = 1
seq_len = 5
# 准备数据
idx2char = ['e','h','l','o'] # 字典
x_data = [[1,0,2,2,3]]
y_data = [3,1,2,3,2] #ohlol
inputs = torch.LongTensor(x_data)
labels = torch.LongTensor(y_data)
# 构造模型
class Model(torch.nn.Module):
def __init__(self):
super(Model, self).__init__()
self.emb = torch.nn.Embedding(input_size,embedding_size) #返回inputs_len*embedding_size
self.lstm = torch.nn.LSTM(input_size=embedding_size,
hidden_size=hidden_size,
num_layers=num_layers)
self.fc = torch.nn.Linear(hidden_size,num_class)
def forward(self,x):
hidden = torch.zeros(num_layers,x.size(0),hidden_size)
x = self.emb(x) # (batch,seqlen,embeddingsize)
# print("emb_x",x)
x,_ = self.lstm(x)
x = self.fc(x)
return x.view(-1,num_class) # 转变2维矩阵,seq*batchsize*numclass -》((seq*batchsize),numclass)
model = Model()
# 损失函数和优化器
criterion = torch.nn.CrossEntropyLoss(reduction='mean')
optimizer = torch.optim.Adam(model.parameters(),lr=0.01) # lr = 0.01学习的太慢
# 训练
for epoch in range(15):
optimizer.zero_grad()
outputs = model(inputs) # inputs是(seq,Batchsize,Inputsize) outputs是(seq,Batchsize,Hiddensize)
loss = criterion(outputs,labels) # labels是(seq,batchsize,1)
loss.backward()
optimizer.step()
_,idx = outputs.max(dim=1) # 找到矩阵每行的最大值
idx = idx.data.numpy() # 将tensor转成n数组
print("Predicted:",''.join([idx2char[x] for x in idx]),end='')
print(",Epoch {}/15 loss={:.3f}".format(epoch+1,loss.item()))
效果很差,根本没预测出来。
- https://blog.csdn.net/m0_58327216/article/details/122854569?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-122854569-blog-119042008.pc_relevant_multi_platform_featuressortv2removedup&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-122854569-blog-119042008.pc_relevant_multi_platform_featuressortv2removedup&utm_relevant_index=1
这个代码运行出来预测是正确的,暂时不知道我直接调取LSTM的问题。
RNN和LSTM折中方案
torch.nn.GRU():
输入和输出:
import torch
num_class = 4
input_size = 4
hidden_size = 8
embedding_size = 10
batch_size = 1
num_layers = 1
seq_len = 5
# 准备数据
idx2char = ['e','h','l','o'] # 字典
x_data = [[1,0,2,2,3]] #维度(batch,seqlen)
y_data = [3,1,2,3,2] #ohlol
inputs = torch.LongTensor(x_data)
labels = torch.LongTensor(y_data)
# 构造模型
class Model(torch.nn.Module):
def __init__(self):
super(Model, self).__init__()
self.emb = torch.nn.Embedding(input_size,embedding_size) #返回(batch,seqlen,embeddingsize)
self.lstm = torch.nn.GRU(input_size=embedding_size,
hidden_size=hidden_size,
num_layers=num_layers)
self.fc = torch.nn.Linear(hidden_size,num_class)
def forward(self,x):
hidden = torch.zeros(num_layers,x.size(0),hidden_size)
x = self.emb(x) # (batch,seqlen,embeddingsize)
x,_ = self.lstm(x)
x = self.fc(x)
return x.view(-1,num_class) # 转变2维矩阵,seq*batchsize*numclass -》((seq*batchsize),numclass)
model = Model()
# 损失函数和优化器
criterion = torch.nn.CrossEntropyLoss(reduction='mean')
optimizer = torch.optim.Adam(model.parameters(),lr=0.01) # lr = 0.01学习的太慢
# 训练
for epoch in range(15):
optimizer.zero_grad()
outputs = model(inputs) # inputs是(seq,Batchsize,Inputsize) outputs是(seq,Batchsize,Hiddensize)
loss = criterion(outputs,labels) # labels是(seq,batchsize,1)
loss.backward()
optimizer.step()
_,idx = outputs.max(dim=1) # 找到矩阵每行的最大值
idx = idx.data.numpy() # 将tensor转成n数组
print("Predicted:",''.join([idx2char[x] for x in idx]),end='')
print(",Epoch {}/15 loss={:.3f}".format(epoch+1,loss.item()))
同样没有预测出来,问题待解决。
教程指路:【《PyTorch深度学习实践》完结合集】 https://www.bilibili.com/video/BV1Y7411d7Ys?share_source=copy_web&vd_source=3d4224b4fa4af57813fe954f52f8fbe7
- 线性模型 Linear Model
- 梯度下降 Gradient Descent
- 反向传播 Back Propagation
- 用PyTorch实现线性回归 Linear Regression with Pytorch
- 逻辑斯蒂回归 Logistic Regression
- 多维度输入 Multiple Dimension Input
- 加载数据集Dataset and Dataloader
- 用Softmax和CrossEntroyLoss解决多分类问题(Minst数据集)
- CNN基础篇——卷积神经网络跑Minst数据集
- CNN高级篇——实现复杂网络
- RNN基础篇——实现RNN
- RNN高级篇—实现分类