Python深度学习之理解循环神经网络

本文探讨了循环神经网络(RNN)的概念,解释了RNN如何处理序列数据并保留状态。通过Numpy实现了一个简单的RNN示例,然后介绍了Keras中的RNN层,包括SimpleRNN、LSTM和GRU。LSTM和GRU旨在解决RNN中的梯度消失问题,以更好地处理长期依赖。在IMDB数据集上的实验表明,尽管LSTM和GRU优于SimpleRNN,但对于情感分析任务,全连接层可能更为有效。
摘要由CSDN通过智能技术生成

Deep Learning with Python

这篇文章是我学习《Deep Learning with Python》(第二版,François Chollet 著) 时写的系列笔记之一。文章的内容是从 Jupyter notebooks 转成 Markdown 的,你可以去 GitHubGitee 找到原始的 .ipynb 笔记本。

你可以去这个网站在线阅读这本书的正版原文(英文)。这本书的作者也给出了配套的 Jupyter notebooks

本文为 第6章 深度学习用于文本和序列 (Chapter 6. Deep learning for text and sequences) 的笔记之一。

6.2 Understanding recurrent neural networks

理解循环神经网络

之前我们用的全连接网络和卷积神经网络都有是被叫做 feedforward networks (前馈网络) 的。这种网络是无记忆的,也就是说,它们单独处理每个输入,在输入与输入之间没有保存任何状态。在这种网络中,我们要处理时间/文本等序列,就必须把一个完整的序列处理成一个大张量,整个的传到网络中,让模型一次看完整个序列。

这个显然和我们人类阅读、学习文本等信息的方式有所区别。我们不是一眼看完整本书的,我们要一个词一个词地看,眼睛不停移动获取新的数据的同时,记住之前的内容,将新的、旧的内容联系在一起来理解整句话的意思。说抽象一些,我们会保存一个关于所处理内容的内部模型,这个模型根据过去的信息构建,并随着新信息的进入而不断更新。我们都是以这种渐进的方式处理信息的。

按照这种思想,我们又得到一种新的模型,叫做循环神经网络(recurrent neural network, RNN),这网络会遍历处理所有序列元素,并保存一个记录已查看内容相关信息的状态(state)。而在处理下一条序列之时,RNN 状态会被重置。使用 RNN 时,我们仍可以将一个序列整个的输出网络,不过在网络内部,数据不再是直接被整个处理,而是自动对序列元素进行遍历。

循环网络:带有环的网络

为了理解循环神经网络,我们用 Numpy 手写一个玩具版的 RNN 前向传递。考虑处理形状为 (timesteps, input_features) 的一条序列,RNN 在 timesteps 上做迭代,将当前 timestep 的 input_features 与前一步得到的状态结合算出这一步的输出,然后将这个输出保存为新的状态供下一步使用。第一步时,没有状态,因此将状态初始化为一个全零向量,称为网络的初始状态。

伪代码:

state_t = 0
for input_t in input_sequence:
    output_t = f(input_t, state_t)
    state_t = output_t

这里的 f(...) 其实和我们的 Dense 层比较类似,但这里不仅处理输出,还要同时加入状态的影响。所以它就需要包含 3 个参数:分别作用与输出和状态的矩阵 W、U,以及偏移向量 b:

def f(input_t, state_t):
    return activation(
        dot(W, input_t) + dot(U, state_t) + b
    )

画个图来表示这个程序:

一个简单的 RNN,沿时间展开

下面把它写成真实的代码:

import numpy as np

# 定义各种维度大小
timesteps = 100
input_features = 32
output_features = 64

inputs = np.random.random((timesteps, input_features))

state_t = np.zeros((output_features))

W = np.random.random((output_features, input_features))
U = np.random.random((output_features, output_features))
b = np.random.random((output_features))

successive_outputs = []

for input_t in inputs:    # input_t: (input_features, )
    output_t = np.tanh(   # output_t: (output_features, )
        np.dot(W, input_t) + np.dot(U, state_t) + b
    )
    successive_outputs.append(output_t)
    
    state_t = output_t
    
final_output_sequence = np.stack(successive_outputs, axis=0)  # (timesteps, output_features)

print(successive_outputs[-1].shape)
print(final_output_sequence.shape)

(64,)
(100, 64)

在这里,我们最终输出是一个形状为 (timesteps, output_features) ,是所有 timesteps 的结果拼起来的。但实际上,我们一般只用最后一个结果 successive_outputs[-1] 就行了,这个里面已经包含了之前所有步骤的结果,即包含了整个序列的信息。

Keras 中的循环层

把刚才这个玩具版本再加工一下,让它能接收形状为 (batch_size, timesteps, input_features) 的输入,批量去处理,就得到了 keras 中的 SimpleRNN 层:

from tensorflow.keras.layers import SimpleRNN

这个 SimpleRNN 层和 keras 中的其他循环层都有两种可选的输出模式:

输出形状 说明 使用
(batch_size, timesteps, output_features) 输出每个 timestep 输出的完整序列 return_sequences=True
(batch_size, output_features) 只返回每个序列的最终输出 return_sequences=False (默认)
# 只返回最后一个时间步的输出

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding, SimpleRNN

model = Sequential()
model.add(Embedding(10000, 32))
model.add(SimpleRNN(32))
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding (Embedding)        (None, None, 32)          320000    
_________________________________________________________________
simple_rnn (SimpleRNN)       (None, 32)                2080      
=================================================================
Total params: 322,
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值