RNN基础回顾(1)

RNN循环神经网络(Recurrent Neural Network, RNN)是一类以序列(sequence)数据为输入,在序列的演进方向进行递归(recursion)且所有节点(循环单元)按链式连接的递归神经网络。

一、网络架构

首先回顾一下传统的卷积神经网络的架构,如下图所示,是LeNet网络结构,输入一张28x28的图像,经过多层卷积处理,然后由三层的全连接层输出结果,从图像可以看出,传统的卷积神经网络是一个不断“向前”的神经网络,图片数据不断向前传播,然后误差反向优化网络。这是由于,卷积神经网络通常用于处理一些图像数据,通常可以理解为输入一张图片,输出一个结果,图片之间的关联不大。

LeNet网络架构

考虑一下生活中的其他场景,例如生活中与他人的对话,我们要想理解别人的意思,不仅要考虑他人当前的话语,还要考虑之前的几句话的意思,前后的连贯性较强,这种场景使用卷积神经网络就不太合适了,尤其是在NLP(自然语言处理)的领域,我们同通常使用RNN(循环神经网络)用于处理一些序列模型,即前后的关联性较大的场景。

下面是一个基础的RNN Cell,是一个基础的RNN单元。RNN的输入不仅由当前的输入x_t还有隐藏层输入h_{t-1} ,最终输出当前新的输出h_t,然后循环作用。

  下面是RNN Cell内部具体的运算。

 将RNN Cell展开的可以看到的如下图所示。

 上图中几个RNN的权值是共享的,可以减小运算量,每一个RNN Cell(实际就是同一个RNN Cell的不断循环罢了)的输入不仅有当前时刻的输入x_i还有当前时刻的隐藏层输入h_{i-1},初始状态是h_0是自己预先定义的。上面就是简单RNN的基本单元的构造,下面使用PyTorch进行RNN网络的建立。

二、使用PyTorch建立RNN网络

1、使用RNNCell创建

 此时解释一下其中几个参数的含义:batch_size是批次大小,seq_len是序列长度,input_size输入size,hidden_size是输出size,input_size和hidden_size好理解就是输入输出向量的长度,输入[1,2],size就是2,我们通常将输入的文字通过one-hot进行编码为一个向量,向量的长度就是输入的size,每一个Cell都会有一个hidden的结果向量,长度就是hidden_size。

  下面是对于batch_size和seq_len进行解释,深度学习中采用mini-batch的方法进行迭代优化,在CNN中batch的思想较容易理解,一次输入batch个图片,进行迭代。但是RNN中引入了seq_len(time_step),理解较为困难。下面举一个例子:

sentences = ["i like dog", "i love coffee", "i hate milk", "i like music", "i hate you"]

上面这句话中有5个句子,即batch_size=5,每一个句子都可以看作一个sequence,seq_len可以由embedding,比如one-hot encoding,转换为一个向量。那么在RNN的训练中。
t=0时, 输入第一个batch[i, i, i, i, i]这里用字符表示,其实应该是对应的one-hot编码。
t=1时,输入第二个batch[like, love, hate, like, hate]
t=2时,输入第三个batch[dog, coffee, milk, music, you]
那么对应的时间t来说,RNN需要对先后输入的batch_size个字符进行前向计算迭代,得到输出。

或者另外一个例子,假如你有一个数据集10条句子,每个句子20个词,每个词可以用长度为64的向量表示,此时,seq_len=20,batch_size=10,input_size=64;seq_len的值决定了该RNN的结构,如果seq_len为20,那么这环要循环20次,每一步t都需要一个输入,同时也会有一个输出,共有20次输入和对应的20个输出。

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)

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('ouputs size:',hidden.shape)
    print(hidden)
'''
打印结果
==================== 0 ====================
Input size: torch.Size([1, 4])
ouputs size: torch.Size([1, 2])
tensor([[0.5464, 0.7889]], grad_fn=<TanhBackward>)
==================== 1 ====================
Input size: torch.Size([1, 4])
ouputs size: torch.Size([1, 2])
tensor([[ 0.8551, -0.3328]], grad_fn=<TanhBackward>)
==================== 2 ====================
Input size: torch.Size([1, 4])
ouputs size: torch.Size([1, 2])
tensor([[ 0.1827, -0.6966]], grad_fn=<TanhBackward>)
'''

2、使用RNN对象

  此时多出一个参数num_layers,解释一下该参数,RNN Cell也可以有多层组成,不加的话,默认层数是一层,下面解释一下输入输出。

此时的输入input_size仍然为seq_len*batch_size*input_size,hidden为num_layers*batch_size*hidden_size,此时输出out的大小为seq_len*batch_size*hidden_size,相当于seq_len个hidden的叠加, 输出的hidden为num_layers*batch_size*hiddne_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)
inputs=torch.randn(seq_len,batch_size,input_size)
hidden=torch.zeros(num_layers,batch_size,hidden_size)

out,hidden=cell(inputs,hidden)
'''
Output size: torch.Size([3, 1, 2])
Output: tensor([[[-0.8403, -0.4627]],

        [[-0.0917,  0.9727]],

        [[ 0.3013,  0.6235]]], grad_fn=<StackBackward>)
Hidden size:  torch.Size([1, 1, 2])
Hidden:  tensor([[[0.3013, 0.6235]]], grad_fn=<StackBackward>)
'''

三、实现完整的基础RNN网络 

 1、使用RNN Cell

将数据进行预处理

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]]
x_one_hot = [one_hot_lookup[x] for x in x_data]#此时的维度是seq*inputsize需要 进行转换为seq*batch*inputsize

inputs=torch.Tensor(x_one_hot).view(-1,batch_size,input_size)
labels=torch.LongTensor(y_data).view(-1,1)#标签seq*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=self.input_size,hidden_size=self.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)
    
net=Model(input_size,hidden_size,batch_size)

训练过程,该过程中,训练迭代了15次,每一次迭代过程中,又有seq_len次的循环,我们都要手动的将每一个seq_len循环中的损失进行累加,然后误差反向传播,梯度下降。

criterion=torch.nn.CrossEntropyLoss()
optimizer=torch.optim.Adam(net.parameters(),lr=0.1)
for epoch in range(15):
    loss=0
    optimizer.zero_grad()
    hidden = net.init_hidden()
    print('Predicted string: ', end='')
    # input:batch_size*input_size;inputs:seq*batch_size*input_size
    for input,label in zip(inputs,labels):
        hidden=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()))

2、使用RNN对象

此时的网络模型:

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]]
x_one_hot = [one_hot_lookup[x] for x in x_data]#此时的维度是seq*inputsize需要 进行转换为seq*batch*inputsize

inputs=torch.Tensor(x_one_hot).view(-1,batch_size,input_size)
labels=torch.LongTensor(y_data)#标签seq
class Net(torch.nn.Module):
    def __init__(self,input_size,hidden_size,batch_size,num_layers=1):
        super(Net,self).__init__()
        self.num_layers=num_layers
        self.batch_size=batch_size
        self.input_size=input_size
        self.hidden_size=hidden_size
        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)
net=Net(input_size,hidden_size,batch_size,num_layers)        

训练过程和CNN等传统神经网络的训练过程一致,将inputs和labels输入之后,PyTorch会自动帮我们完成seq_len的循环和误差累加,此时的hidden我们在forward函数中进行初始化为全0的tensor,没有手动传入。

criterion=torch.nn.CrossEntropyLoss()
optimizer=torch.optim.Adam(net.parameters(),lr=0.1)
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()
    print('Predicted: ', ''.join([idx2char[x] for x in idx]), end='')
    print(', Epoch [%d/15] loss = %.3f' % (epoch + 1, loss.item()))

  • 8
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值