深圳专业网站建设,丝芙兰网站做的好差,怎么设计个人logo,义乌比较好的外贸公司文章目录问题引入关于权重权重共享RNN CellRNN原理RNN计算过程代码实现RNN Cell维度说明代码RNN维度说明NumLayers说明计算过程代码参考实例问题分析多分类问题代码RNN CellRNN改进Embedding网络结构Embedding说明Linear说明代码课程来源#xff1a;
链接课程文本参考#xf…
文章目录问题引入关于权重权重共享RNN CellRNN原理RNN计算过程代码实现RNN Cell维度说明代码RNN维度说明NumLayers说明计算过程代码参考实例问题分析多分类问题代码RNN CellRNN改进Embedding网络结构Embedding说明Linear说明代码课程来源
链接课程文本参考Birandaの问题引入
在前篇中所提到的线性网络大致上都是稠密网络DNN这种网络的输入大多是样本的多维度的特征以此来得到一个想要的输出。 显然面对序列问题即处理视频流、预测天气、自然语言等等问题时此时输入的x1⋯xnx_1 \cdots x_nx1⋯xn实际上是一组有序列性即存在前后关系的样本。每个xix_ixi为一个样本的所包含的特征元组。
针对这样的问题我们也会想到利用线性的全连接网络来进行处理但事实上全连接网络所需要计算的权重太多并不能够解决问题。
关于权重
对于一张128通道的图片若想要利用5×55 \times 55×5的卷积核将它转换成一张64通道的图片则需要计算的权重有大约20W个 128×52×64204800128 \times 5^2 \times 64 204800 128×52×64204800 即卷积层的权重数目只与通道数以及卷积核大小有关但全连接层的权重数与转成一维向量以后的整个数据量有关 若转为一维向量以后输入为4096个元素输出为1024个元素则需要计算的权重大约有400W个 4096×102441943044096 \times 1024 4194304 4096×10244194304 显然全连接层所需要计算的权重远多于卷积层
权重共享
给定一张输入图片用一个固定大小的卷积核去对图片进行处理卷积核内的参数即为权重而卷积核是对输入图片进行步长为stride的扫描计算也就是说原图中的每一个像素都会参与到卷积计算中因此对于整个卷积核而言权重都是一样的即共享。
权重共享实际上是计划通过将输入图片的一个局部特征映射成输出图片的一个像素点而发挥作用的也就是通过卷积核使得输出层的每一个像素只与输入层一个局部的方块相连接这就避免了在全连接情形下的输出层的每个像素都在或多或少的被输入层的任意一个像素影响这一复杂的情况。
另外图像信息的特征图片底层特征与特征在图片中的位置无关也是权重共享的一个依据。例如对于图像的边缘特征无论边缘处在图像的什么位置都是以同样的方法定义作为同样的特征进行区别的。
因此为了能够较好地解决序列问题也要利用到权重共享的思路方法来减少在计算过程中所需要的权重。因此产生了RNN
RNN Cell
RNN原理
模块名称作用xtx_txt表时刻ttt时的输入数据RNN Cell本质上是一个线性层hth_tht表时刻ttt时得到的输出隐含层本质上RNN Cell为一个线性层Linear在ttt时刻下的NNN维向量经过Cell后即可变为一个MMM维的向量hth_tht,而与其他线性层不同RNN Cell为一个共享的线性层。即重复利用权重共享。将上图展开来看如下。 由于x1⋯xnx_1 \cdots x_nx1⋯xn为一组序列信息每一个xix_ixi都至少应包含xi−1x_{i-1}xi−1的信息。也就是说针对x2x_2x2的操作所得到的h2h_2h2中应当包含x1x_1x1的信息因此在设计中把x1x_1x1处理后得到的h1h_1h1一并向下传递。 上图中的h0h_0h0是一种前置信息。例如若实现图像到文本的转换可以利用CNNFC对图像进行操作再将输出的向量作为h0h_0h0参与到RNN的运算中。 若没有可获知的前置信息可将h0h_0h0设置为与xix_ixi同维度的零向量。 图中的RNN Cell为同一个Linear即让设计好的Linear反复参与运算实现权重共享。
RNN计算过程 对上图中的符号做出解释如下
符号标注hth_ththt−1h_{t-1}ht−1隐藏层hidden的结果向量xtx_txt输入层的输入向量Rhidden_sizeR ^{hidden\_size}Rhidden_size表隐藏层的向量维度Rinput_sizeR^{input\_size}Rinput_size表输入层的向量维度WihW_{ih}Wih用于计算输入的权重维度大小为hidden_size×input_sizehidden\_size \times input\_sizehidden_size×input_sizebihb_{ih}bih用于计算输入时的偏置量WhhW_{hh}Whh用于计算隐藏层的权重维度大小为hidden_size×hidden_sizehidden\_size \times hidden\_sizehidden_size×hidden_sizebhhb_{hh}bhh用于计算隐藏层时的偏置量tanh激活函数值域为(−1,1)(-1, 1)(−1,1)求和模块
在RNN计算过程中分别对输入xtx_txt以及前文的隐藏层输出ht−1h_{t-1}ht−1进行线性计算再进行求和对所得到的一维向量利用tanh激活函数进行激活由此可以得到当前隐藏层的输出hth_tht,其计算过程如下 httanh(WihxtbihWhhht−1bhh)h_t tanh(W_{ih}x_t b_{ih} W_{hh}h_{t-1} b_{hh}) httanh(WihxtbihWhhht−1bhh) 实际上在框中的RNN Cell的计算过程中为线性计算。 Whhht−1Wihxt[WhhWih][hx]W_{hh}h_{t-1}W_{ih}x_{t} \begin{bmatrix} {W_{hh}}{W_{ih}} \end{bmatrix} \begin{bmatrix} h\\ x \end{bmatrix} Whhht−1Wihxt[WhhWih][hx] 即在实际运算的过程中这两部分是拼接到一起形成矩阵再计算求和的最终形成一个大小为hidden_size×1hidden\_size \times 1hidden_size×1的张量。
代码实现
代码实现有两种模式一是实现自己的RNN Cell再自己重写循环调用等逻辑。二是直接调用RNN的网络。
重点在于控制其输入输出的维度。
RNN Cell
#输入维度input_size,隐藏层维度hidden_size
cell torch.nn.RNNCell(input_size input_size, hidden_size hidden_size)#输入的input 的维度B*input_size, hidden的维度B*hidden_size
#输出的hidden维度B*hidden_szie
hidden cell(input, hidden)维度说明
为说明输入输出维度大小假定当前有如下配置的数据
参数值说明BatchSize1批量大小SeqLen3样本数目x1x_1x1x2x_2x2x3x_3x3InputSize4输入维度HiddenSize2隐藏层输出维度
对于RNN Cell而言在hidden cell(input, hidden)中
参数值说明input([1, 4])input.shape(batch_size,input_size)input.shape (batch\_size, input\_size)input.shape(batch_size,input_size)hidden([1, 2])output.shape(batch_size,hidden_size)output.shape (batch\_size, hidden\_size)output.shape(batch_size,hidden_size)
而整个数据集的维度应为 dataset.shape(seqLen,batch_size,input_size)dataset.shape (seqLen, batch\_size, input\_size) dataset.shape(seqLen,batch_size,input_size)
代码
import torchbatch_size 1
seq_len 3
input_size 4
hidden_size 2cell torch.nn.RNNCell(input_size input_size, hidden_size hidden_size)
#维度最重要
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(outputs size: , hidden.shape)print(hidden)RNN
#说明input维度hidden维度以及RNN层数
#RNN计算耗时大不建议层数过深
cell torch.nn.RNN(input_size input_size, hidden_size hidden_size, num_layers num_layers)#inputs指的是X1……Xn的整个输入序列
#hidden指的是前置条件H0
#out指的是每一次迭代的H1……Hn隐藏层序列
#hidden_out指的是最后一次迭代得到输出Hn
out, hidden_out cell(inputs, hidden)维度说明
为说明输入输出维度大小假定当前有如下配置的数据
参数值说明BatchSize1批量大小SeqLen5样本数目x1x_1x1x2x_2x2x3x_3x3x4x_4x4x5x_5x5InputSize4输入维度HiddenSize2隐藏层输出维度numLayers3RNN层数
对于RNN而言在out, hidden_out cell(inputs, hidden)
参数值说明input([5, 1, 4])input.shape(seqLen,batch_size,input_size)input.shape (seqLen, batch\_size, input\_size)input.shape(seqLen,batch_size,input_size)hidden([3, 1, 2])hidden.shape(numLayers,batch_size,hidden_size)hidden.shape (numLayers, batch\_size,hidden\_size)hidden.shape(numLayers,batch_size,hidden_size)out([5, 1, 2])out.shape(seqLen,batch_size,hidden_size)out.shape (seqLen, batch\_size,hidden\_size)out.shape(seqLen,batch_size,hidden_size)hidden_out([3, 1, 2])hidden_out.shape(numLayers,batch_size,hidden_size)hidden\_out.shape (numLayers, batch\_size,hidden\_size)hidden_out.shape(numLayers,batch_size,hidden_size)
NumLayers
说明 其中左侧及下侧为输入部分右侧及上侧为输出部分。
左侧是每一层RNN的前置条件下侧为输入序列。右侧为每一层的隐藏层最终输出上侧为隐藏层输出序列。
计算过程
在输入序列中的第jjj个样本经过第iii层RNN Cell计算过后所产生的隐藏层输出记为hjih^i_jhji,该输出分别向更深层的Cell即i1i1i1层RNN Cell以及更靠后的序列即第j1j1j1个样本进行传递。 若iii为最后一层,则作为该样本的最终结果out_put输出。否则将继续向第i1i1i1层RNN传递。 若jjj为序列中最后一个样本,则作为本层RNN的隐藏层最终结果hidden_out输出。否则将继续向第j1j1j1个样本传递。 图示是一张展开图实际上每一层的RNN Cell是同一个网络以此来实现权重共享。
代码
import torchbatch_size 1
seq_len 5
input_size 4
hidden_size 2
num_layers 3
#其他参数
#batch_firstTrue 维度从(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)输出结果
参考实例
假定现在有一个序列到序列seq→seqseq \to seqseq→seq的任务比如将“hello”转换为“ohlol”。
即利用RNN实现如下输出 问题分析
原输入“hello”并不是一个向量需要将其转变为一组数字向量。
对输入序列的每一个字符单词构造字典词典此时每一个字符都会有一个唯一的数字与其一一对应。即将字符向量转换为数字向量。之后利用独热编码One-Hot的思想即可将每个数字转换为一个向量。 对输出序列按照上述要求构造字典。
多分类问题
对于序列中的每一个输入都有一个数字输出与其对应即本质上是在求当前输入所映射到输出字典中最大概率的值。即变为多分类问题。而此时的输出维度为3。 其中RNNCell的输出维度为4经过Softmax求得映射之后的概率分别是多少再利用输出对应的独热向量计算NLLLoss。
代码
RNN Cell
import torchinput_size 4
hidden_size 3
batch_size 1#构建输入输出字典
idx2char_1 [e, h, l, o]
idx2char_2 [h, l, o]
x_data [1, 0, 2, 2, 3]
y_data [2, 0, 1, 2, 1]
# y_data [3, 1, 2, 2, 3]
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_sizeself.input_size input_sizeself.hidden_size hidden_sizeself.rnncell torch.nn.RNNCell(input_size self.input_size,hidden_size self.hidden_size)def forward(self, input, hidden):# RNNCell input (batchsize*inputsize)# RNNCell hidden (batchsize*hiddensize)hidden self.rnncell(input, hidden)return hidden#初始化零向量作为h0只有此处用到batch_sizedef init_hidden(self):return torch.zeros(self.batch_size, self.hidden_size)net Model(input_size, hidden_size, batch_size)criterion torch.nn.CrossEntropyLoss()
optimizer torch.optim.Adam(net.parameters(), lr0.1)for epoch in range(15):#损失及梯度置0创建前置条件h0loss 0optimizer.zero_grad()hidden net.init_hidden()print(Predicted string: ,end)#inputsseqLen*batchsize*input_size labels (seqLen*1)#input是按序列取的inputs元素batchsize*inputsize#label是按序列去的labels元素1for input, label in zip(inputs, labels):hidden net(input, hidden)#序列的每一项损失都需要累加loss criterion(hidden, label)#多分类取最大_, idx hidden.max(dim1)print(idx2char_2[idx.item()], end)loss.backward()optimizer.step()print(, Epoch [%d/15] loss %.4f % (epoch1, loss.item()))RNN
import torchinput_size 4
hidden_size 3
batch_size 1
num_layers 1
seq_len 5
#构建输入输出字典
idx2char_1 [e, h, l, o]
idx2char_2 [h, l, o]
x_data [1, 0, 2, 2, 3]
y_data [2, 0, 1, 2, 1]
# y_data [3, 1, 2, 2, 3]
one_hot_lookup [[1, 0, 0, 0],[0, 1, 0, 0],[0, 0, 1, 0],[0, 0, 0, 1]]x_one_hot [one_hot_lookup[x] for x in x_data]inputs torch.Tensor(x_one_hot).view(seq_len, batch_size, input_size)
#labelsseqLen*batchSize,1为了之后进行矩阵运算计算交叉熵
labels torch.LongTensor(y_data)class Model(torch.nn.Module):def __init__(self, input_size, hidden_size, batch_size, num_layers1):super(Model, self).__init__()self.batch_size batch_size #构造H0self.input_size input_sizeself.hidden_size hidden_sizeself.num_layers num_layersself.rnn torch.nn.RNN(input_size self.input_size,hidden_size self.hidden_size,num_layersnum_layers)def forward(self, input):hidden torch.zeros(self.num_layers,self.batch_size,self.hidden_size)out, _ self.rnn(input, hidden)#reshape成SeqLen*batchsize,hiddensize便于在进行交叉熵计算时可以以矩阵进行。return out.view(-1, self.hidden_size)net Model(input_size, hidden_size, batch_size, num_layers)criterion torch.nn.CrossEntropyLoss()
optimizer torch.optim.Adam(net.parameters(), lr0.05)#RNN中的输入SeqLen*batchsize*inputsize
#RNN中的输出SeqLen*batchsize*hiddensize
#labels维度 hiddensize*1
for epoch in range(15):optimizer.zero_grad()outputs net(inputs)loss criterion(outputs, labels)loss.backward()optimizer.step()_, idx outputs.max(dim1)idx idx.data.numpy()print(Predicted string: ,.join([idx2char_2[x] for x in idx]), end )print(, Epoch [%d/15] loss %.3f % (epoch1, loss.item()))改进
独热编码在实际问题中容易引起很多问题
独热编码向量维度过高每增加一个不同的数据就要增加一维独热编码向量稀疏每个向量是一个为1其余为0独热编码是硬编码编码情况与数据特征无关
综上所述需要一种低维度的、稠密的、可学习数据的编码方式
Embedding
目的是为了对数据进行降维 若从Input_Size转化为Embedding_Size以下图为例即需要把四维的向量转换为5维则仅需要如下图所示的矩阵将独热编码的向量与Embedding的矩阵相乘即可。 即将上图矩阵转置再右乘向量即可 如此即可将原先四维的One-Hot编码变为五维的Embedding编码
网络结构
增加Embedding层实现降维增加线性层使之在处理输入输出维度不同的情况下更加稳定。 其中的Embedding层的输入必须是LongTensor类型。
Embedding说明
字段类型说明num_embeddingtorch.nn.Embedding的参数表示输入的独热编码的维数embedding_dimtorch.nn.Embedding的参数表示需要转换成的维数Inputtorch.nn.Embedding的输入量LongTensor类型Outputtorch.nn.Embedding的输出量为数据增加一个维度(embedding_dim)
Linear说明
对于线性层输入和输出的第一个维度Batch一直到倒数第二个维度都会保持不变。但会对最后一个维度in_features做出改变out_features 代码
import torchinput_size 4
num_class 4
hidden_size 8
embedding_size 10
batch_size 1
num_layers 2
seq_len 5idx2char_1 [e, h, l, o]
idx2char_2 [h, l, o]x_data [[1, 0, 2, 2, 3]]
y_data [3, 1, 2, 2, 3]#inputs 作为交叉熵中的Inputs维度为batchsizeseqLen
inputs torch.LongTensor(x_data)
#labels 作为交叉熵中的Target维度为batchsize*seqLen
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)self.rnn torch.nn.RNN(input_size embedding_size,hidden_size hidden_size,num_layersnum_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)x, _ self.rnn(x, hidden)x self.fc(x)return x.view(-1, num_class)net Model()criterion torch.nn.CrossEntropyLoss()
optimizer torch.optim.Adam(net.parameters(), lr0.05)for epoch in range(15):optimizer.zero_grad()outputs net(inputs)loss criterion(outputs, labels)loss.backward()optimizer.step()_, idx outputs.max(dim1)idx idx.data.numpy()print(Predicted string: ,.join([idx2char_1[x] for x in idx]), end )print(, Epoch [%d/15] loss %.3f % (epoch1, loss.item()))输出结果