文章目录
- 1. DNN
- 2. CNN
- 3. RNN
-
- 3.1 RNN的结构分析
- 3.2 RNN的使用分析
-
- 3.2.1 RNN_cell的实现
- 3.2.2 RNN的实现
- 4. Example
-
- 4.1 Use RNN_cell
-
- 4.1.1 准备数据
- 4.1.2 准备模型
- 4.1.3 准备损失函数和优化器
- 4.1.4 进行训练
- 4.2 Use RNN
-
- 4.2.1 训练的改变
- 4.2.2 模型的改变
- 4.2.3 数据的改变
- 4.2.4 完整代码
- 4.3 独热向量
-
- 4.3.1 one-hot缺点
- 4.3.2 改进
- 4.3.3 网络结构
- 4.3.4 完整代码
- 5. Exercise
-
- 5.1 Use LSTM
- 5.2 Use GRU
1. DNN
深度神经网络是一个密集的网络,Dense连接是指全连接。有许多线性层在空间上改变输入数据,也称为DNN。输入x1,x2,…,x8是数据样本的不同特征。
考虑这样一个场景:如预测天气,你需要知道前几天的数据,每天的数据包含几个特征(温度、压力、雨天),如果你知道今天的温度、压力和其他特征来预测雨,这是无用的,需要提前预测,需要前几天的数据作为输入。
假设前三天每天有三个特点(温度、气压、雨天),如何预测第四天是否有雨?
第一种方法:把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,x还考虑了2之间的连接关系x1,x2时间上的顺序(x2依赖于x1,x3依赖于x2)下一天的天气状况部分取决于前一天的天气状况,RNN有序列连接的数据主要用于处理。 数据是按顺序连接的,即当天的数据将依赖于以前的数据。 哪些数据有序列:股市、金融数据、自然语言(我爱北京天安门)
2. CNN
3. RNN
3.1 RNN的结构分析
RNN是循环神经网络。RNN实际上是线性层的复用。
RNN Cell本质是线性层(linear),将一个维度映射到另一个维度(例如,输入的三维向量)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具体计算过程: 输入xt先做线性变换,ht-1也是,xt的维度是input_size,ht-1的维度是hidden_size,输出ht的维度是hidden_size。我们需要先把xt的维度变成hidden_size,所以Wih应该是一个 hidden_sizeinput_size的矩阵,Wihxt得到一个 hidden_size矩阵(即维度为hidden_size的向量),bih是偏置。输入权重矩阵Whh是一个hidden_size*hidden_size的矩阵。
Whhht-1 bhh和Wihxt bih都是维度为hidden_size向量,然后两个向量加起来,信息融合,融合后使用tanh循环神经网络的激活函数用于激活tanh(为什么?tanh的取值在-1到 1之间),算出结果得到这一层的隐藏层输出ht。
补充:Tanh的诞生比Sigmoid晚一些,sigmoid函数的一个缺点是输出不以0为中心,使收敛变慢。Tanh这个问题就解决了。Tanh是双曲正切函数。等于双曲余弦除双曲正弦。函数表达式和图像见下图。这个函数是一个奇怪的函数。 完整的定义RNN:将RNN cell以循环的形式一个个送进去,然后依次计算隐藏层的过程,我们称之为循环神网络。
3.2 RNN的使用分析
构造RNN的方式:
- 自己写处理序列的循环
- 直接使用RNN
3.2.1 RNN_cell的实现
主要是确定输入的维度和隐层的维度。 如上图,RNN本质上还是一个线性层,要弄清楚纬度。代码如下:
# pytorch实现
cell = torch.nn.RNNcell(input_size=input_size,hidden_size=hidden_size)
hidden = cell(input,hidden) # h1 = cell(x1,h0)
: 输入有三个特征,每个特征是4维的。隐藏是一个2维的向量。
- batchSize表示批量大小
- seqLen=3表示每一个样本都有x1,x2,x3这些特征
- inputSize=4表示每一个特征都是4维的
- hiddenSize=2表示每一个隐藏层是2维的 可以发现,RNN相比之前的网络,多了一个序列的维度。
import torch
batch_size = 1
seq_len = 3
input_size = 4
hidden_size = 2
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)
3.2.2 RNN的实现
: 直接使用torch.nn.RNN()需要知道input_size、hidden_size和num_layers(RNN有多少层,默认为1)。cell(inputs,hidden)中 inputs指包含整个输入序列(x1,x2,x3,…xN),hidden指h0。 用RNN不用自己写循环,它自动循环,所以输入的时候要把所有的序列都送进去,然后给定h0,然后我们就会得到所有的隐层输出以及最后一层的输出。 当RNN有多层,同样颜色的RNNCell是同一个,所以上图是有3个线性层(一个RNNCell是一个线性层)。这样就能解释为什么隐藏层h的维度需要numLayers参数,因为每一层都需要。
输出: 输出与输入的区别就在于input_size变成了hidden_size
import torch
input_size = 4
hidden_size = 2
seq_len = 3
batch_size = 1
num_layers = 1
cell = torch.nn.RNN(hidden_size=hidden_size,input_size=input_size,num_layers=num_layers)
#(seq_len,batchsize,inputsize)
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参数可以设置:
4. Example
4.1 Use RNN_cell
举例:seq到seq。训练一个模型:输入hello,输出ohlol。 RNN Cell 的输入是向量,第一步先把字符转成向量。 在NLP中,先根据字符构造一个词典(Dictionary),然后根据indeces转换成相应的one-hot向量。这里inputsize=4,因为输入有4个字符(e h l o)这相当于一个多分类问题,输出就是一个4维的向量,每一维代表是某一个字符的概率,接交叉熵就能输出概率了。
输入和输出所需要的结构:
4.1.1 准备数据
# 准备数据
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
print(x_one_hot)
# (seqlen,batchsize,inputsize)
inputs = torch.tensor(x_one_hot).view(-1,batch_size,input_size)
# label (seqlen,1)
labels = torch.LongTensor(y_data).view(-1,1)
print(labels.shape)
print(labels)
4.1.2 准备模型
# 构造模型
class Model(torch.nn.Module):
def __init__(self,batch_size,input_size,hidden_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):
hidden = self.rnncell(input,hidden)
return hidden
def init_hidden(self):
return torch.zeros(self.batch_size,self.hidden_size)
model = Model(input_size=input_size,hidden_size=hidden_size,batch_size=batch_size)
4.1.3 准备损失函数和优化器
# 损失函数和优化器
creation = torch.nn.CrossEntropyLoss(reduction="mean")
optimizer = torch.optim.SGD(model.parameters(),lr=0.1)
4.1.4 进行训练
# 训练
for epoch in range(15):
loss = 0
optimizer.zero_grad()
hidden = model.init_hidden() # 计算h0
print("Predicted string:",end="") #end=’ ‘意思是末尾不换行,加空格。
for input,label in zip(inputs,labels): # 依次取x1,x2,x3,x4,x5
# inputs : (seqlen,batchsize,inputsize)
hidden = model(input,hidden)
loss += creation(hidden,label) # loss这里不用item,因为序列的loss需要构造计算图
_,idx = hidden.max(dim=1)
print(idx2char[idx.item()],end="")
loss.backward()
optimizer.step()
print(",Epoch {}/15 loss={:.4f}".format(epoch+1,loss.item()))
输出结果: 补充知识:torch.Tensor和torch.tensor的区别
4.2 Use RNN
4.2.1 训练的改变
# 训练
for epoch in range(15):
optimizer.zero_grad()
outputs = model(inputs) # inputs是(seq,Batchsize,Inputsize) outputs是(seq,Batchsize,Hiddensize)
loss = creation(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()))
4.2.2 模型的改变
# 构造模型
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):
# hidden = self.rnncell(input,hidden)
# return hidden
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)
# def init_hidden(self):
# return torch.zeros(self.batch_size,self.hidden_size)
model = Model(input_size=input_size,hidden_size=hidden_size,batch_size=batch_size,num_layers=1)
4.2.3 数据的改变
# 准备数据
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
# print(x_one_hot)
# (seqlen,batchsize,inputsize)
inputs = torch.Tensor(x_one_hot).view(seq_len,batch_size,input_size)
# label (seqlen,1)
labels = torch.LongTensor(y_data)
# print(labels.shape)
# print(labels)
4.2.4 完整代码
import torch
# 使用RNN
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
# print(x_one_hot)
# (seqlen,batchsize,inputsize)
inputs = torch.Tensor(x_one_hot).view(seq_len,batch_size,input_size)
# label (seqlen,1)
labels = torch.LongTensor(y_data)
# print(labels.shape)
# print(labels)
# 构造模型
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):
# hidden = self.rnncell(input,hidden)
# return hidden
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)
# def init_hidden(self):
# return torch.zeros(self.batch_size,self.hidden_size)
model = Model(input_size=input_size,hidden_size=hidden_size,batch_size=batch_size,num_layers=1)
# 损失函数和优化器
creation = 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 = creation(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()))
输出结果:
4.3 独热向量
4.3.1 one-hot缺点
缺点:
- 维度太高
- 向量稀疏
- 硬编码,不是学习出来的 embedding将高维离散的数据映射到加低维稠密的空间,就是常说的数据降维。在输入层和RNN层之中。
4.3.2 改进
: 输入是2,就去表里查找2的位置,输出向量。 取第2行的向量,我们可以通过矩阵乘法来获得。 将上述4x5行的矩阵进行转置变成5x4,然后乘以一个[0 0 1 0]就可以取出第2行的向量。
然后我们求反向传播导数的话,我们就可以通过矩阵乘法的导数来进行计算。
我们之前的RNN cell输出的隐藏是和分类的数量是一致的, 如果不一致的话,我们可以再接一个线性层。 : 第一个参数:独热向量是几维的。 输入是(seq,batchsize)输出是(seq,batchsize,embedding_dim) : : d1,d2…代表交叉熵可以加维度的,可以满足RNN的计算。
4.3.3 网络结构
: 添加嵌入层。 将输入的张量转变为嵌入曾稠密的张量表示。 batch_first设为true的话,那么batchsize维度在前,seqlen维度在后。 线性层,将hiddensize变成numclass。 最后返回成二维矩阵的形式,方便loss的计算。
4.3.4 完整代码
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