资讯详情

《PyTorch深度学习实践》学习笔记:循环神经网络(基础篇)

文章目录

  • 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的方式:

  1. 自己写处理序列的循环
  2. 直接使用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缺点

缺点:

  1. 维度太高
  2. 向量稀疏
  3. 硬编码,不是学习出来的 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 

标签: 4x1变送器ht2088变送器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台