pytorch学习-12循环神经网络(基础篇)
12.循环神经网络(基础篇)_哔哩哔哩_bilibili
12.1 RNN Cell
RNN Cell 本质为一个线性层,与普通线性层区别为,该线性层输入时,同时需要
经过RNN Cell计算得到的
参与运算。如下图所示:
RNN Cell 内部运算如下图所示:
具体运算过程:
- 输入
、
,均做线性变换
- 将其线性变换的结果求和并代入tanh()函数计算
注意:
1. 的维度是input_size,
的维度是hidden_size,输出
的维度是hidden_size,我们需要先把
的维度变成hidden_size,所以
应该是一个 input_size*hidden_size的矩阵,
(矩阵运算时一般x在W前,与公式中不同) 得到一个 1*hidden_size的矩阵(就是维度为hidden_size的向量),
是偏置。输入权重矩阵
是一个hidden_size* hidden_size的矩阵。(i代表input_size,h代表hidden_size)
2. 和
都是维度为hidden_size的向量,两个向量相加,就把信息融合起来了,融合之后用tanh做激活,循环神经网络的激活函数用的是tanh,因为tanh的取值在-1到+1之间,算出结果得到隐藏层输出
。
把RNN Cell以循环的方式把序列(x1,x2,…)一个一个送进去,然后依次算出隐藏层(h1,h2…)的过程,每一次算出来的h会作为下一个RNN Cell的输入,这就叫循环神经网络。
12.2 构建RNN Cell方式构造RNN层
注意:
1.torch.nn.RNNCell 中输入 input_size和hidden_size,会自动确定线性层的和
2.hidden为求每一个所对应的
,其输入为
和
,所以需要多次循环求所有h。
- batchSize表示批量
- seqLen=3表示每一个样本都有x1,x2,x3这些特征
- inputSize=4表示每一个特征都是4维的
- hoddenSize=2表示每一个隐藏层是2维
代码演示:
import torchbatch_size=1 #表示每次处理的数据样本数量。这里设置为1,意味着每次只处理一个样本
seq_len=3 #表示每个样本的序列长度。这里设置为3,意味着每个样本包含3个特征向量
input_size=4 #表示每个特征值的维度。这里设置为4
hidden_size=2 #表示隐藏层的维度。这里设置为2RNNCell=torch.nn.RNNCell(input_size,hidden_size)
# 这里创建了一个RNN单元,该单元的输入维度为4,输出(隐藏状态)维度为2。这个RNN单元将用于处理序列数据。
# W权重矩阵为input_size*hidden_size(4*2),bias为hidden_size维度。dataset=torch.randn(seq_len,batch_size,input_size)
# 这里随机创建了一个数据集,包含3个序列,每个序列包含1个样本,每个样本包含4个特征向量。
print('dataset:', dataset)
print('dataset.size():', dataset.size())hidden=torch.zeros(batch_size,hidden_size)
# 这里创建了一个初始的隐藏状态,维度为(batch_size,hidden_size)(1*2)for index,input in enumerate(dataset):print('='*20,index,'='*20)print("input_size:",input.size())hidden=RNNCell(input,hidden) # 这里将输入数据输入到RNN单元中,并得到输出的隐藏状态。print("output_size:",hidden.size())print(hidden)
运行结果:
12.3 直接用RNN类方式构造RNN层
- inputs:所有的x
- hidden:
用RNN不用自己写循环,它自动循环,所以输入的时候要把所有的序列都送进去,然后给定,然后我们就会得到所有的隐层输出(out)以及最后一层的输出(hidden)
当有多层RNN时,可调整函数中num_layer参数,多层RNN如下图所示:
batch_first设置为TRUE,需要进行下图的调整:
代码演示:
import torchbatch_size=1
input_size=3
hidden_size=2
seq_len=4
num_layers=1RNN=torch.nn.RNN(input_size,hidden_size,num_layers,batch_first=True)
#batch_first=True表示输入的第一个维度是batch_size,第二个维度是序列长度dataset=torch.randn(batch_size,seq_len,input_size)
#由于batch_first=True,所以输入的第一个维度是batch_size,第二个维度是序列长度hidden=torch.zeros(num_layers,batch_size,hidden_size)output,hidden=RNN(dataset,hidden)print("ouput shape: ",output.shape)
print("output: ",output)
print("hidden shape: ",hidden.shape)
print("hidden: ",hidden)
运行结果:
12.4 使用RNN Cell训练hello转换到ohlol
12.4.1 准备数据集
由于hello为字符串不具备特征向量,所以先将其转换为字典形式,再生成相应的特征向量。
如下图所示:
因为输入有四个字符(e,h,l,o),input_size=4,这相当于一个多分类问题,输出就是一个4维的向量,每一维代表是某一个字符的概率,接交叉熵就能输出概率了
one-hot(独热编码):独热编码通常用于处理类别间不具有大小关系的特征。 采用独热编码后,会把具有n个特征的变量变成有一个n维的稀疏向量,
代码演示:
import torchbatch_size=1
input_size=4
hidden_size=4# 准备数据集
idx2char=['e','h','l','o']
x_data=[1,0,2,2,3] #hello
y_data=[3,1,2,3,2] #ohlol# one-hot编码
one_hot_lookup=[[1,0,0,0], #e[0,1,0,0], #h[0,0,1,0], #l[0,0,0,1]] #ox_one_hot=[one_hot_lookup[x] for x in x_data]inputs=torch.Tensor(x_one_hot).view(-1,batch_size,input_size) # (seq_len,batch_size,input_size)
labels=torch.LongTensor(y_data).view(-1,1) # (seq_len,1)#2.定义网络
class RNN(torch.nn.Module):def __init__(self,input_size,hidden_size,batch_size):super(RNN,self).__init__()self.input_size=input_sizeself.hidden_size=hidden_sizeself.batch_size=batch_sizeself.RNN_cell=torch.nn.RNNCell(input_size,hidden_size) #定义RNNCelldef forward(self,inputs,hidden):#求hidden的输出hthidden=self.RNN_cell(input,hidden)return hiddendef init_hidden(self):#初始化隐状态return torch.zeros(self.batch_size,self.hidden_size)net=RNN(input_size,hidden_size,batch_size)#3.定义损失函数和优化器
criterion=torch.nn.CrossEntropyLoss()
optimizer=torch.optim.Adam(net.parameters(),lr=0.1)#4.训练
for epoch in range(15):loss = 0optimizer.zero_grad() # 清空梯度hidden = net.init_hidden() # 初始化隐状态print('Predicted string: ', end='')for input, label in zip(inputs, labels):#RNNCell的训练每次只能求一个hidden,所以需要多次循环求所有的hiddenhidden = net(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()))
运行结果:
12.5 使用用RNN 训练hello转换到ohlol
代码演示:
import torchbatch_size=1
input_size=4
hidden_size=4
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编码
one_hot_lookup=[[1,0,0,0], #e[0,1,0,0], #h[0,0,1,0], #l[0,0,0,1]] #ox_one_hot=[one_hot_lookup[x] for x in x_data]inputs=torch.Tensor(x_one_hot).view(seq_len,batch_size,input_size) # (seq_len,batch_size,input_size)
labels=torch.LongTensor(y_data) # (seq_len,1)#2.定义网络
class RNN(torch.nn.Module):def __init__(self,input_size,hidden_size,batch_size,num_layers):super(RNN,self).__init__()self.input_size=input_sizeself.hidden_size=hidden_sizeself.batch_size=batch_sizeself.num_layers=num_layersself.RNN=torch.nn.RNN(input_size=input_size,hidden_size=hidden_size,num_layers=num_layers) #定义RNNCelldef forward(self,inputs): #求hidden的输出所有hhidden = torch.zeros(self.num_layers, self.batch_size, self.hidden_size)out,_=self.RNN(inputs,hidden)return out.view(-1,self.hidden_size)net=RNN(input_size,hidden_size,batch_size,num_layers)#3.定义损失函数和优化器
criterion=torch.nn.CrossEntropyLoss()
optimizer=torch.optim.Adam(net.parameters(),lr=0.05)#4.训练
for epoch in range(15):optimizer.zero_grad() #梯度清零outputs = net(inputs) #前向传播loss = criterion(outputs, labels) #计算损失loss.backward() #反向传播optimizer.step() #更新参数_, idx = outputs.max(dim=1) #预测结果 idx = idx.data.numpy() #转化为numpy格式print('Predicted: ', ''.join([idx2char[x] for x in idx]), end='') #打印预测结果print(', Epoch [%d/15] loss = %.3f' % (epoch + 1, loss.item()))
运行结果:
12.6 Embedding(嵌入层)编码方式
Embedding把一个高维的稀疏的样本映射到一个稠密的低维的空间里面,也就是数据的降维。降维的原理就是矩阵乘法。此外,还可以对低维的数据进行升维,把一些其他特征给放大了,或者把笼统的特征给分开了。
- one-hot表示的缺点:
1、维度太高(字符级:字符集ASIIC 128维 单词级:几万维)
2、过于稀疏
3、硬编码的,这个词向量并不是学习出来的
嵌入层降维的方式:输入一个索引,通过查表来找到对应的向量,找的方法是通过将lookup表与一个one-hot向量进行相乘,然后得出最后的嵌入向量。以下图为例
若想取出第三行数据(下标为2),可通过先将原矩阵(4x5)转置(5x4),再乘相应的(4x1)的矩阵即可。如下所示
有时候的输出的隐藏层h的维度与类别的维度o不一致,所以可以添加Linear Layer,将h的维度映射为o的维度
- num_embeddings:嵌入层的输入空间的大小
- embedding_dim:为嵌入向量的维度
假设我们有一个词汇表,其中包含10000个不同的单词。我们希望将这些单词映射到一个低维的连续向量空间中,以便在深度学习模型中使用这些词嵌入。
在这种情况下:
"num_embeddings" 将是词汇表的大小,即 10000。
"embedding_dim" 可以是词嵌入的维度,例如 300。
线性层
输入中*:代表添加的维度
输出中的*:代表处理最后一个维度,其他维度都与输入维度相同
交叉熵
N:batch一组样本的个数
C:class输入的维度大小
中间可增加维度。
代码演示:
import torch#参数初始化
num_class=4 #分类数,e,h,l,o
input_size=4 #每个特征的维度
hidden_size=8 #隐藏层大小
embedding_size=10 #Embedding层输出维度
num_layers=2 #RNN层数
batch_size=1
seq_len=5 #一个样本的特征数#1.准备数据集,输入输出
idx2char=['e','h','l','o']
x_data=[[1,0,2,2,3]] #hello (batch,seq_len)
y_data=[3,1,2,3,2] #ohlol (batch,seq_len)inputs=torch.LongTensor(x_data) #inputs为longtensor类型
labels=torch.LongTensor(y_data) #labels为longtensor类型#2.定义RNN网络
class RNN_Embedding_Linear_Net(torch.nn.Module):def __init__(self):super(RNN_Embedding_Linear_Net, self).__init__()self.Embedding=torch.nn.Embedding(input_size,embedding_size) #定义Embedding层#参数为 input_size,embedding_size#作用是将输入的数字转换为向量形式,embedding_size为输出的向量维度,之后传给RNN层self.Rnn=torch.nn.RNN(input_size=embedding_size,hidden_size=hidden_size,num_layers=num_layers,batch_first=True) #定义RNN层#参数为 embedding_size,hidden_size,num_layers,batch_first=True#经过Embedding层后,输入为(batch,seq_len,embedding_size)#input_size=embedding_size,因为Embedding层的输出维度为embedding_sizeself.Linear=torch.nn.Linear(hidden_size,num_class) #定义线性层#参数为 hidden_size,num_class#作用是将RNN的输出映射到num_class维度的空间中def forward(self,inputs):hidden=torch.zeros(num_layers,inputs.size(0),hidden_size) #初始化隐藏状态embedding_output=self.Embedding(inputs) #将输入通过Embedding层转换为向量形式#输出为(batch,seq_len,embedding_size)Rnn_output,hidden=self.Rnn(embedding_output,hidden) #将向量输入到RNN层中#输出为(batch,seq_len,hidden_size)output=self.Linear(Rnn_output) #将RNN的输出通过线性层映射到num_class维度的空间中#输出为(batch,seq_len,num_class)return output.view(-1,num_class) #将输出reshape为(batch*seq_len,num_class)model=RNN_Embedding_Linear_Net() #实例化模型#3.定义损失函数和优化器
criterion=torch.nn.CrossEntropyLoss() #定义损失函数为交叉熵
optimizer=torch.optim.Adam(model.parameters(),lr=0.05) #定义优化器为Adam#4.训练模型
for epoch in range(15):optimizer.zero_grad() #梯度清零outputs=model(inputs) #模型前向传播loss=criterion(outputs,labels) #计算损失loss.backward() #反向传播optimizer.step() #更新参数_,idx=outputs.max(dim=1) #获取预测结果idx=idx.data.numpy() #转换为numpy数组print("predict: ",''.join([idx2char[i] for i in idx]),end=' ') #预测结果print(',epoch: %d/15 loss= %.4f'%(epoch+1,loss.item())) #打印损失和epoch数
运行结果:
12.7参考
跟着刘二大人学pytorch(第---12---节课之RNN基础篇)_刘二pytorch课件-CSDN博客
一文读懂Embedding的概念,以及它和深度学习的关系 - 知乎
神经网络嵌入层中的“embedding_dim“和“num_embeddings“分别是什么_embedding dim-CSDN博客