首页 > 编程笔记 > 通用技能 阅读:3

RNN循环神经网络详解(新手必看)

神经网络有多种,而 RNN 在序列数据(如时间序列或自然语言)建模方面非常强大,很适合用于时间序列建模。那么 RNN 是如何做到这一点的呢?

我们从 RNN 的原理说起。简单来说,RNN 层会使用 for 循环对序列的时间步骤进行迭代,同时维持一个内部状态,对截至目前所看到的时间步骤信息进行编码。这个原理表示,其他神经网络是假设所有输入和输出都彼此独立,而 RNN 的输出取决于先前的计算。

RNN 可以捕获到目前为止计算的信息。理论上,RNN 可以利用任意长度序列中的信息,虽然实际上这个长度并不长。这种特性称为“记忆”。

为了让大家有个感性的认识,我们通过图 1 展示 RNN 与前馈神经网络的对比情况:


图 1 RNN与前馈神经网络的对比情况

我们可以看到左侧的前馈神经网络与时间序列长度没有关系,而右侧的 RNN 则多了一个概念:时间步骤(数量为时间序列长度)。这样输入不仅和当前时刻的输入相关,也与其过去一段时间的输出相关。

接下来,我们讲解具体代码实现。我们将使用 Keras 来构建 RNN:
然后,通过 SimpleRNN 类构建 RNN(In [3])。可以看到我们输入了多个参数:

激活函数

激活函数是用来确定神经网络结构输出的数学方程。

激活函数在隐藏层中引入非线性,以便我们能够对非线性问题进行建模。如果不用激活函数,每一层输出都将是上一层输入的线性函数。这样的话,无论神经网络有多少层,输出都是输入的线性组合,与没有隐藏层效果一样。

常见的激活函数如下:
1) sigmoid:这个激活函数允许我们在模型中引入小的变化时合并少量的输出。它的取值范围为(0,1)。sigmoid 函数的数学表达式为:


其中,w 表示权重,x 表示数据,b 表示偏差,下标 i 表示特征。

2) tanh:如果要处理的是负数,tanh 函数适合。与 sigmoid 函数不同,它的取值范围为(−1,1)。tanh 函数的公式为:


3) linear:使用 linear 函数可以在自变量和因变量之间建立线性关系。线性激活函数的形式为:

f(x) = wx


4) Rectified Linear Unit:简称 ReLU,即在代码中先输入的 relu。如果输入为 0 或小于 0,则取 0。如果大于 0,则按照以下公式取值:

ReLU(x) = max(0, x)


5) softmax:与 sigmoid 函数一样,这个激活函数也广泛适用于分类问题,因为 softmax 函数将输入转换成与输入数的指数成正比的概率分布。softmax 函数的公式为:


return_sequences 参数表示是返回输出序列中的最后一个输出,还是全部序列。我们设置为 True,表示返回全部序列。

看完知识点之后,来看看以下具体的实现代码:
In [1]: import numpy as np
        import pandas as pd
        import math
        import datetime
        import yfinance as yf
        import matplotlib.pyplot as plt
        import tensorflow as tf
        from tensorflow.keras.models import Sequential
        from tensorflow.keras.callbacks import EarlyStopping
        from tensorflow.keras.layers import (Dense, Dropout,
                                             Activation, Flatten,
                                             MaxPooling2D, SimpleRNN)
        from sklearn.model_selection import train_test_split
 
In [2]: n_steps = 13 ❶
        n_features = 1 ❷
 
In [3]: model = Sequential() ❸
        model.add(SimpleRNN(512, activation='relu',
                            input_shape=(n_steps, n_features),
                            return_sequences=True)) ❹
        model.add(Dropout(0.2)) ❺
        model.add(Dense(256, activation = 'relu')) ❻
        model.add(Flatten()) ❼
        model.add(Dense(1, activation='linear')) ❽
 
In [4]: model.compile(optimizer='rmsprop',
                      loss='mean_squared_error',
                      metrics=['mse']) ❾
 
In [5]: def split_sequence(sequence, n_steps):
            X, y = [], []
            for i in range(len(sequence)):
                end_ix = i + n_steps
                if end_ix > len(sequence) - 1:
                   break
                seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
                X.append(seq_x)
                y.append(seq_y)
            return np.array(X), np.array(y) ❿

配置好模型及变量之后,我们开始提取 Apple 公司和 Microsoft 公司的股价数据并进行预测:
In [6]: ticker = ['AAPL', 'MSFT']
        start = datetime.datetime(2019, 1, 1)
        end = datetime.datetime(2020, 1 ,1)
        stock_prices = yf.download(ticker,start=start, end = end, interval='1d')\
                       .Close
        [*********************100%***********************]  2 of 2 completed
 
In [7]: diff_stock_prices = stock_prices.diff().dropna()
 
In [8]: split = int(len(diff_stock_prices['AAPL'].values) * 0.95)
        diff_train_aapl = diff_stock_prices['AAPL'].iloc[:split]
        diff_test_aapl = diff_stock_prices['AAPL'].iloc[split:]
        diff_train_msft = diff_stock_prices['MSFT'].iloc[:split]
        diff_test_msft = diff_stock_prices['MSFT'].iloc[split:]
 
In [9]: X_aapl, y_aapl = split_sequence(diff_train_aapl, n_steps) ❶
        X_aapl = X_aapl.reshape((X_aapl.shape[0],  X_aapl.shape[1],
                                 n_features)) ❷
 
In [10]: history = model.fit(X_aapl, y_aapl,
                             epochs=400, batch_size=150, verbose=0,
                             validation_split = 0.10) ❸
 
In [11]: start = X_aapl[X_aapl.shape[0] - n_steps] ❹
         x_input = start ❺
         x_input = x_input.reshape((1, n_steps, n_features))
 
In [12]: tempList_aapl = [] ❻
         for i in range(len(diff_test_aapl)):
             x_input = x_input.reshape((1, n_steps, n_features)) ❼
             yhat = model.predict(x_input, verbose=0) ❽
             x_input = np.append(x_input, yhat)
             x_input = x_input[1:]
             tempList_aapl.append(yhat) ❾
 
In [13]: X_msft, y_msft = split_sequence(diff_train_msft, n_steps)
         X_msft = X_msft.reshape((X_msft.shape[0],  X_msft.shape[1],
                                  n_features))
 
In [14]: history = model.fit(X_msft, y_msft,
                             epochs=400, batch_size=150, verbose=0,
                             validation_split = 0.10)
 
In [15]: start = X_msft[X_msft.shape[0] - n_steps]
         x_input = start
         x_input = x_input.reshape((1, n_steps, n_features))
 
In [16]: tempList_msft = []
         for i in range(len(diff_test_msft)):
             x_input = x_input.reshape((1, n_steps, n_features))
             yhat = model.predict(x_input, verbose=0)
             x_input = np.append(x_input, yhat)
             x_input = x_input[1:]
             tempList_msft.append(yhat)

然后,使用以下代码可视化结果:
In [17]: fig, ax = plt.subplots(2,1, figsize=(18,15))
         ax[0].plot(diff_test_aapl, label='Actual Stock Price', linestyle='--')
         ax[0].plot(diff_test_aapl.index, np.array(tempList_aapl).flatten(),
                    linestyle='solid', label="Prediction")
         ax[0].set_title('Predicted Stock Price-Apple')
         ax[0].legend(loc='best')
         ax[1].plot(diff_test_msft, label='Actual Stock Price', linestyle='--')
         ax[1].plot(diff_test_msft.index,np.array(tempList_msft).flatten(),
                    linestyle='solid', label="Prediction")
         ax[1].set_title('Predicted Stock Price-Microsoft')
         ax[1].legend(loc='best')
 
         for ax in ax.flat:
             ax.set(xlabel='Date', ylabel='Differenced Price')
         plt.show()
最终输出下图所示的图表。只要仔细观察一下就可以看到,模型的预测性能还有改进的空间。


图 5 RNN 模型预测结果

即使改进之后得到令人满意的预测性能,RNN 模型的缺点也不容忽视。RNN 的主要缺点如下:
在设计不当的深度学习场景中,梯度消失是一个很常见的问题。当进行反向传播时,如果梯度变小,就会出现梯度消失问题。这意味着神经元学习速度太慢,以致优化过程陷入停顿。

梯度爆炸是指当进行反向传播时,反向传播的微小变化导致网络权重的大幅更新。梯度消失或梯度爆炸在原理上是相似的。

相关文章