Python 深度学习架构实用指南:第三、四、五部分(1)https://developer.aliyun.com/article/1427000
用于文本生成的 LSTM RNN
在基于 LSTM 的文本生成器中,我们将序列长度增加到 160 个字符,因为它可以更好地处理长序列。 记住要用新的seq_length
= 160 重新生成训练集X
和Y
。
为了轻松地将该模型的表现与以前的原始模型进行比较,我们将保留一个相似的结构-两个循环层,两个循环层均包含800
单元,0.4
的丢弃率和 tanh (默认情况下)作为激活函数:
>>> from keras.layers.recurrent import LSTM >>> batch_size = 100 >>> n_layer = 2 >>> hidden_units = 800 >>> n_epoch= 300 >>> dropout = 0.4
现在,创建并编译网络:
>>> model = Sequential() >>> model.add(LSTM(hidden_units, input_shape=(None, n_vocab), return_sequences=True)) >>> model.add(Dropout(dropout)) >>> for i in range(n_layer - 1): ... model.add(LSTM(hidden_units, return_sequences=True)) ... model.add(Dropout(dropout)) >>> model.add(TimeDistributed(Dense(n_vocab))) >>> model.add(Activation('softmax'))
优化器RMSprop
的学习速度为0.001
:
>>> optimizer = optimizers.RMSprop(lr=0.001, rho=0.9, epsilon=1e-08, decay=0.0) >>> model.compile(loss="categorical_crossentropy", optimizer=optimizer)
让我们总结一下我们刚刚组装的 LSTM 模型:
>>> print(model.summary()) _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= lstm_1 (LSTM) (None, None, 800) 2745600 _________________________________________________________________ dropout_1 (Dropout) (None, None, 800) 0 _________________________________________________________________ lstm_2 (LSTM) (None, None, 800) 5123200 _________________________________________________________________ dropout_2 (Dropout) (None, None, 800) 0 _________________________________________________________________ time_distributed_1 (TimeDist (None, None, 57) 45657 _________________________________________________________________ activation_1 (Activation) (None, None, 57) 0 ================================================================= Total params: 7,914,457 Trainable params: 7,914,457 Non-trainable params: 0 _________________________________________________________________
有 800 万个参数需要训练,几乎是原始模型中训练参数的四倍。 让我们开始训练他们:
>>> model.fit(X, Y, batch_size=batch_size, verbose=1, epochs=n_epoch, callbacks=[ResultChecker(model, 10, 500), checkpoint, early_stop])
生成器为每个10
周期写入500
个字符长的文本。
以下是周期151
,201
和251
的结果:
Epoch 151
:
Epoch 151/300 19976/19976 [==============================] - 250s 12ms/step - loss: 0.7300 My War and Peace: ing to the countess. "i have nothing to do with him and i have nothing to do with the general," said prince andrew. "i am so sorry for the princess, i am so since he will not be able to say anything. i saw him long ago. i am so sincerely that i am not to blame for it. i am sure that something is so much talk about the emperor alexander's personal attention." "why do you say that?" and she recognized in his son's presence. "well, and how is she?" asked pierre. "the prince is very good to make Epoch 00151: loss improved from 0.73175 to 0.73003, saving model to weights/weights_epoch_151_loss_0.7300.hdf5
Epoch 201
:
Epoch 201/300 19976/19976 [==============================] - 248s 12ms/step - loss: 0.6794 My War and Peace: was all the same to him. he received a story proved that the count had not yet seen the countess and the other and asked to be able to start a tender man than the world. she was not a family affair and was at the same time as in the same way. a few minutes later the count had been at home with his smile and said: "i am so glad! well, what does that mean? you will see that you are always the same." "you know i have not come to the conclusion that i should like to send my private result. the prin Epoch 00201: loss improved from 0.68000 to 0.67937, saving model to weights/weights_epoch_151_loss_0.6793.hdf5
Epoch 251
:
Epoch 251/300 19976/19976 [==============================] - 249s 12ms/step - loss: 0.6369 My War and Peace: nd the countess was sitting in a single look on her face. "why should you be ashamed?" "why do you say that?" said princess mary. "why didn't you say a word of this?" said prince andrew with a smile. "you would not like that for my sake, prince vasili's son, have you seen the rest of the two?" "well, i am suffering," replied the princess with a sigh. "well, what a delightful norse?" he shouted. the convoy and driving away the flames of the battalions of the first day of the orthodox russian Epoch 00251: loss improved from 0.63715 to 0.63689, saving model to weights/weights_epoch_251_loss_0.6368.hdf5
最后,在周期300
,训练因0.6001
损失而停止。
每个周期大约需要四到五分钟,才能在 Tesla K80 GPU 上完成。 借助大约 22 个小时的训练,文本生成器借助 LSTM 架构能够编写出更逼真有趣的《战争与和平》脚本。
此外,用于字符生成的 LSTM RNN 不限于文本。 他们可以从任何字符数据中学习,例如源代码,HTML,LaTex,并希望自动编写软件程序,网页和科学论文。
GRU RNN
在 LSTM 之后的十多年中,发明了具有门控机制的替代架构 GRU。 GRU 和 LSTM 的表现相当,在某些情况下一个要优于另一个。 但是,GRU 只有两个信息门,这比 LSTM 稍微复杂一些。 GRU 的循环单元描述如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C2E84AW3-1681704851869)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/ce928dd1-04ac-4581-92ae-ba6f2c3d3e33.png)]
上图中从左到右的关键组件说明如下:
r
表示复位门,它控制要忘记前一存储器s[t-1]
的多少信息。 给定连接到先前隐藏状态s[t-1]
的权重W[r]
和连接到当前输入x[t]
的U[r]
,复位门r
的输出在时间步t
计算为r = sigmoid(U[r] x[t] + W[r] s[t-1])
。p
代表更新门,它确定可以从前一个内存中传递多少信息。 将权重W[p]
和U[p]
分别连接到先前的存储状态和当前输入,将时间t
的更新门p
的输出计算为p = sigmoid(U[p] x[t] + W[p] s[t-1])
。- tanh 是隐藏状态的激活函数,并基于当前输入
x[t]
和先前存储状态的重置进行计算。 给定它们的相应权重W[c]
和U[c]
,将当前存储器c'
在时间步t
的输出计算为c = tanh(U[c] x[t] + W[c] (r .* s[t-1]))
。 - 最后,在时间步
t
的隐藏状态s[t]
被更新为s[t] = (1 - p) .* c' + p .* s[t-1]
。 同样,p
决定用于更新当前内存的先前内存的比例–越接近1
,则保留的内存就越多; 距离0
越近,发生的当前存储器越多。 - 有趣的是,如果
p
是全零向量,而r
是全一向量(例如我们没有明确保留任何先前的内存),则该网络只是一个普通的 RNN。
总体而言,GRU 与 LSTM 非常相似。 它们都使用光栅机制进行长期建模,并且与门相关的参数通过 BPTT 进行训练。 但是,有一些区别值得注意:
- LSTM 中有三个信息门,而 GRU 中只有两个。
- GRU 中的更新门负责输入门和 LSTM 中的遗忘门。
- GRU 中的重置门直接应用于先前的隐藏状态。
- LSTM 显式地对存储单元
c[t]
进行建模,而 GRU 则不。 - 附加的非线性将应用于 LSTM 中更新的隐藏状态。
- LSTM 于 1997 年推出,近年来已得到研究和广泛使用。 GRU 于 2014 年发明,至今尚未得到充分的探索。 这就是为什么 LSTM 比 GRU 更受欢迎的原因,即使不能保证一个 LSTM 胜过另一个。
- 通常认为,与 GSTM 相比,训练 GRU RNN 相对更快并且需要更少的数据,因为 GRU RNN 具有较少的参数。 因此,有人认为 GRU 在训练量较小的情况下效果更好。
尽管还有许多谜团,我们还是将 GRU RNN 应用于十亿(或万亿)美元的问题:股价预测。
用于股价预测的 GRU RNN
预测股票会使许多人感兴趣。 多年以来,已经开发出了大量使用机器学习技术预测股票价格的方法。 例如,在《Python 机器学习示例的》的“第 7 章”中,线性回归,随机森林和支持向量机被用于预测股票价格。 在像这样的传统机器学习解决方案中,特征工程可能是最费力的阶段。 这是手动创建特定于域的特征或信号的过程,这些特征或信号对于定向预测比原始输入更为重要。 典型的发明特征包括x
天移动平均线,一段时间内的波动率和x
天回报率。 相反,基于 RNN 的深度学习解决方案不涉及以手工为特色的手工制作,而是自己找出及时或顺序的关系。 我们将通过使用 GRU RNN 预测道琼斯工业平均指数(DJIA)来展示循环架构的强大功能。
尽管我们强调了深度学习的优势,但我们并未断言深度学习方法优于传统的机器学习方法。 在机器学习中,没有一种适合所有的。
DJIA 由 30 只大型和重要股票(例如 Apple,IBM,GE 和 Goldman Sachs)组成,是全球投资者最常关注的市场指数之一。 它代表了整个美国市场价值的四分之一,这使该项目更加令人兴奋。
我们可以在这个页面上查看其历史每日数据。 其中一些数据的屏幕截图如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9XxZi1g9-1681704851869)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/d23cae0b-59ea-486e-9a51-9f2579b92f79.png)]
五个值说明了交易日内股票在一段时间内的走势:开盘价和收盘价,即交易日的起始价和最终价,低点和高位,即股票交易价格范围和交易量,即在一个交易日交易的股票总数。 例如,我们将重点关注使用历史收盘价来预测未来收盘价。 但是,合并其他四个指标也是可行的。
让我们从数据获取和探索开始。 首先,我们将数据从 2001-01-01 下载到 2017-12-31:在同一网页上,将时间段更改为 2001 年 1 月 1 日至 2017 年 12 月 31 日。单击“应用”按钮,最后单击“下载数据”。 然后,我们可以加载并查看数据:
>>> import numpy as np >>> import matplotlib.pyplot as plt >>> import pandas as pd >>> raw_data = pd.read_csv('^DJI.csv') >>> raw_data.head() Date Open High Low Close 0 2001-01-02 10790.919922 10797.019531 10585.360352 10646.150391 1 2001-01-03 10637.419922 11019.049805 10581.089844 10945.750000 2 2001-01-04 10944.940430 11028.000000 10888.419922 10912.410156 3 2001-01-05 10912.809570 10919.419922 10627.750000 10662.009766 4 2001-01-08 10658.730469 10700.849609 10516.019531 10621.349609 Adj Close Volume 0 10646.150391 253300000 1 10945.750000 420720000 2 10912.410156 382800000 3 10662.009766 272650000 4 10621.349609 225780000
使用以下代码行绘制收盘价数据:
>>> data = raw_data.Close.values >>> len(data) 4276 >>> plt.plot(data) >>> plt.xlabel('Time period') >>> plt.ylabel('Price') >>> plt.show()
前面的代码将创建以下图形:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xoueth9q-1681704851869)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/b6b9c2a2-d03b-4658-ae7e-324bca65610d.png)]
一年平均有 252 个交易日。 这就是为什么在 17 年中仅选择了 4,276 个数据点的原因。
接下来,我们需要从原始时间序列构造顺序输入,以便提供 RNN 模型,这与我们在文本生成中所做的类似。 回想一下,在“多对一”架构中,该模型采用一个序列,并经过序列中的所有时间步长后产生一个输出。 在我们的案例中,我们可以将过去T
天的价格序列提供给 RNN 模型,并输出第二天的价格。
将价格时间序列表示为x[1]
,x[2]
,…,x[n]
(N = 4276
),并以T = 5
为例。 通过这样做,我们可以创建训练样本,如下所示:
输入 | 输出 |
{x[1], x[2], x[3], x[4], x[5]} |
x[6] |
{x[2], x[3], x[4], x[5], x[6]} |
x[7] |
{x[3], x[4], x[5], x[6], x[7]} |
x[8] |
… | … |
{x[n - 1], x[n - 2], x[n - 3], x[n - 4], x[n - 5]} |
x[n] |
在这里,我们通过回顾前 5 个交易日(一周)来预测第二天的价格。 我们还在网络中对其进行了描述:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c1yAoCBe-1681704851869)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/c7066555-976f-489a-9a0c-704b1ec5900f.png)]
因此,我们实现了序列生成函数:
>>> def generate_seq(data, window_size): ... """ ... Transform input series into input sequences and outputs based on a specified window size ... @param data: input series ... @param window_size: int ... @return: numpy array of input sequences, numpy array of outputs ... """ ... X, Y = [], [] ... for i in range(window_size, len(data)): ... X.append(data[i - window_size:i]) ... Y.append(data[i]) ... return np.array(X),np.array(Y)
然后,我们以T = 10
构造输入和输出数据集(回溯 2 周):
>>> window_size = 10 >>> X, Y = generate_seq(data, window_size) >>> X.shape (4266, 10) >>> Y.shape (4266,)
接下来,我们将数据分为 70% 的训练和 30% 的测试:
>>> train_ratio = 0.7 >>> train_n = int(len(Y) * train_ratio) >>> X_train = X[:train_n] >>> Y_train = Y[:train_n] >>> X_test = X[train_n:] >>> Y_test = Y[train_n:]
我们现在可以开始对训练数据进行建模吗? 当然不需要-需要数据缩放或预处理。 从上图可以看出,测试数据与训练数据不成比例,更不用说将来的数据了。 回归模型无法预测超出范围的值。 为了解决这个问题,我们通常使用最大最小缩放x_scaled = (x - x_min) / (x_max / x_min)
将数据缩放到给定范围,例如 0 到 1。 但是,没有可靠的方法来预测股票的x_max
(或x_min
。 这与已知最小值和最大值(例如,图像预测中的 0 和 255)的情况不同。 为了解决这个问题,我们将每个窗口内的价格标准化。 我们只需将时间窗口中的每个价格除以最近的已知价格即可。 再次使用前面的T = 5
示例,我们可以如下预处理训练样本:
输入 | 输出 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4dhOlwOZ-1681704851870)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/56e83be1-8079-4a8a-881c-0882cd25ba4b.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tSKg9MaY-1681704851870)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/a0ac7583-e8c0-4766-a2a4-a27c2dd2dceb.png)] |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gDyc1OP5-1681704851870)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/788e734a-706d-42c8-9275-b30b24b00e91.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tLoWsub7-1681704851871)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/0967eb5b-f561-4d03-85fd-c8a80f160c60.png)] |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BYNLWaID-1681704851871)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/d409f6b4-d088-4055-830d-7db5c56ca9c7.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YJj3Gcdd-1681704851871)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/6ea4abfa-8e60-4c19-ab3f-1d0abe3440bc.png)] |
… | … |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DIJa2eRE-1681704851871)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/b96e30bb-3650-4a7c-80b7-77050aee2097.png)] | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oYZALDCZ-1681704851872)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/be9ead53-baa8-4357-8db1-9b3505882ec4.png)] |
我们基本上将绝对值转换为相对值。 预处理函数实现如下:
>>> def scale(X, Y): ... """ ... Scaling the prices within each window ... @param X: input series ... @param Y: outputs ... @return: scaled input series and outputs ... """ ... X_processed, Y_processed = np.copy(X), np.copy(Y) ... for i in range(len(X)): ... x = X[i, -1] ... X_processed[i] /= x ... Y_processed[i] /= x ... return X_processed, Y_processed
扩展训练和测试数据:
>>> X_train_scaled, Y_train_scaled = scale(X_train, Y_train) >>> X_test_scaled, Y_test_scaled = scale(X_test, Y_test)
终于到了构建 GRU RNN 模型的时候了:
>>> from keras.models import Sequential >>> from keras.layers import Dense, GRU >>> from keras import optimizers >>> model = Sequential() >>> model.add(GRU(256, input_shape=(window_size, 1))) >>> model.add(Dense(1))
在这里,由于我们只有 2986 个训练样本,因此我们正在设计一个相对简单的模型,该模型具有一个 256 个单元的循环层。 对于优化器,使用 RMSprop,学习率为 0.006,以最小化均方误差:
>>> optimizer = optimizers.RMSprop(lr=0.0006, rho=0.9, epsilon=1e-08, decay=0.0) >>> model.compile(loss='mean_squared_error', optimizer=optimizer)
除了早期停止和模型检查点之外,我们还使用 TensorBoard 作为回调函数。 TensorBoard 是 TensorFlow 的表现可视化工具,可提供用于训练和验证指标的动态图:
>>> from keras.callbacks import TensorBoard, EarlyStopping, ModelCheckpoint >>> tensorboard = TensorBoard(log_dir='./logs/run1/', write_graph=True, write_images=False)
验证损失是在提前停止和模型检查点中使用的度量标准:
>>> early_stop = EarlyStopping(monitor='val_loss', min_delta=0, patience=100, verbose=1, mode='min') >>> model_file = "weights/best_model.hdf5" >>> checkpoint = ModelCheckpoint(model_file, monitor='val_loss', verbose=1, save_best_only=True, mode='min')
与往常一样,重塑输入数据以提供 Keras RNN 模型:
>>> X_train_reshaped = X_train_scaled.reshape( (X_train_scaled.shape[0], X_train_scaled.shape[1], 1)) >>> X_test_reshaped = X_test_scaled.reshape( (X_test_scaled.shape[0], X_test_scaled.shape[1], 1))
该模型适合训练集并通过测试集进行了验证,最大历时为300
,批量为100
:
>>> model.fit(X_train_reshaped, Y_train_scaled, validation_data= (X_test_reshaped, Y_test_scaled), epochs=300, batch_size=100, verbose=1, callbacks=[tensorboard, early_stop, checkpoint])
以下是周期1
,11
,52
和99
的结果:
Epoch 1
:
Epoch 1/300 2986/2986 [==============================] - 1s 386us/step - loss: 0.0641 - val_loss: 0.0038 Epoch 00001: val_loss improved from inf to 0.00383, saving model to weights/best_model.hdf5
Epoch 11
:
Epoch 11/300 2986/2986 [==============================] - 1s 353us/step - loss: 0.0014 - val_loss: 9.0839e-04 Epoch 00011: val_loss improved from 0.00128 to 0.00091, saving model to weights/best_model.hdf5
Epoch 52
:
Epoch 52/300 2986/2986 [==============================] - 1s 415us/step - loss: 4.2122e-04 - val_loss: 6.0911e-05 Epoch 00052: val_loss improved from 0.00010 to 0.00006, saving model to weights/best_model.hdf5
Epoch 99
:
Epoch 99/300 2986/2986 [==============================] - 1s 391us/step - loss: 2.1644e-04 - val_loss: 5.2291e-05 Epoch 00099: val_loss improved from 0.00005 to 0.00005, saving model to weights/best_model.hdf5
每个周期大约需要 1 秒才能在 CPU(Core i7)上完成。 训练在周期 242 停止,因为验证损失不再减少:
Epoch 241/300 2986/2986 [==============================] - 1s 370us/step - loss: 1.9895e-04 - val_loss: 7.5277e-05 Epoch 00241: val_loss did not improve Epoch 242/300 2986/2986 [==============================] - 1s 368us/step - loss: 1.9372e-04 - val_loss: 9.1636e-05 Epoch 00242: val_loss did not improve Epoch 00242: early stopping
同时,我们可以在终端中输入以下命令行来检出 TensorBoard:
tensorboard --logdir=logs
它返回以下输出:
Starting TensorBoard b'41' on port 6006 (You can navigate to http://192.168.0.12:6006)
如果转到http://192.168.0.12:6006
,您将能够查看一段时间内的训练损失和验证损失。
平滑为 0(无指数平滑)时的训练损失:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VmXUqgkA-1681704851872)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/998f6430-6107-49c0-aff4-785f88dc76cc.png)]
平滑为 0(无指数平滑)时的验证损失:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5nyZlM4B-1681704851872)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/9d8559d1-fce4-4b80-b99c-89e2f4084a9d.png)]
学习进展顺利,两种损失都随着时间而减少。 通过将其与基本事实进行比较,我们可以进一步将预测可视化。 首先,加载我们刚刚获得的最佳模型,并为训练数据和测试数据计算预测:
>>> from keras.models import load_model >>> model = load_model(model_file) >>> pred_train_scaled = model.predict(X_train_reshaped) >>> pred_test_scaled = model.predict(X_test_reshaped)
我们还需要将按比例缩放的预测转换回其原始比例。 我们可以编写一个函数来简化此操作:
>>> def reverse_scale(X, Y_scaled): ... """ ... Convert the scaled outputs to the original scale ... @param X: original input series ... @param Y_scaled: scaled outputs ... @return: outputs in original scale ... """ ... Y_original = np.copy(Y_scaled) ... for i in range(len(X)): ... x = X[i, -1] ... Y_original[i] *= x ... return Y_original
将反向缩放应用于缩放的预测:
>>> pred_train = reverse_scale(X_train, pred_train_scaled) >>> pred_test = reverse_scale(X_test, pred_test_scaled)
最后,绘制预测以及基本事实:
>>> plt.plot(Y) >>> plt.plot(np.concatenate([pred_train, pred_test])) >>> plt.xlabel('Time period') >>> plt.ylabel('Price') >>> plt.legend(['original series','prediction'],loc='center left') >>> plt.show()
前面的代码将创建以下图形:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lhQAOvxr-1681704851872)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/0adccac3-fc1e-4a77-b627-9e6edf8ee601.png)]
结果图表明预测是非常准确的。 为了进行比较,我们还使用了仅经过 10 个周期训练的模型,得出了效果不佳的结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UCTbJxyQ-1681704851873)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/06e0cfde-0aee-4f96-a2bc-e901d81f8e8e.png)]
应该注意的是,该项目主要是为了演示 GRU RNN 的应用,而不是用于实际的股票交易。 实际上,它要复杂得多,应考虑许多外部和内部因素,例如基本面,技术模式,利率,波动性,周期,新闻和情感。
双向 RNN
到目前为止,在 RNN 架构中,输入序列的信息是从过去到当前状态再到未来的一个方向学习的。 它限制了当前状态以利用将来的输入信息。 让我们看一个缺失单词生成的简单示例:
He said, "Machine __ combines computer science and statistics."
对于仅学习前三个单词的 RNN 模型来说,很难生成适合整个句子的下一个单词。 但是,如果给出了剩余单词,则该模型将更好地捕获上下文,并且更有可能预测下一个单词,即learning
。 为了克服单向 RNN 的局限性,引入了双向 RNN(BRNN)。
在 BRNN 中,隐藏层由两个独立的循环层组成。 这两层是相反的方向:一层为正时间方向,也称为正向,其中输入信息从过去流向当前状态。 另一个为负时间方向,也称为反向,其中从将来到当前状态处理输入信息。 下图描述了 BRNN 的一般结构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L3qzLANc-1681704851873)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/4ee06bf0-c7d5-426e-a033-fff39a4b4629.png)]
在此,f ->
和f <-
分别表示正向和反向循环层。 它们连接在一起,形成隐藏层f
,并保留来自过去和将来状态的信息。
当需要并提供完整的上下文(包括过去和将来的信息)时,BRNN 特别有用。 例如,在词性标记,实体识别或手写识别中,可以通过了解当前单词或字母之后的单词或字母来提高性能。 其他出色的用例包括语音识别,机器翻译和图像字幕。
接下来,我们将 BRNN 和 LSTM 结合起来用于情感分类。 我们将看到捕获移动评论的完整上下文信息是否有助于增强其情感极性。
用于情感分类的双向 RNN
Keras 包含来自 IMDb 的 50,000 条电影评论的数据集,并用情感极性(1
为正,0
为负)标记。 评论已经过预处理,每个词都由字典中的相应索引表示。 字典中的单词将根据整个数据集中的频率进行排序。 例如,编码为4
的单词是数据中第 4 个最常见的单词。 您可以猜测1
代表the
,2
代表and
,并且最高索引用于停用词。
可以通过以下代码获得单词索引字典:
>>> from keras.datasets import imdb >>> word_to_id = imdb.get_word_index()
我们可以使用load_data
函数加载数据,该数据返回两个元组,即训练数据集和测试数据集:
>>> max_words = 5000 >>> (x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=max_words, skip_top=10, seed=42)
在这里,我们仅考虑前 5,000 个最常用的词,但排除了前 10 个最常用的词。 种子用于复制目的:
>>> print(len(y_train), 'training samples') 25000 training samples >>> print(len(y_test), 'testing samples') 25000 testing samples
您会在某些输入样本中找到许多 2。 这是因为2
用于表示由于我们应用了频率过滤器而被切掉的单词,包括前 10 个最常用的单词和第 5,000 个最常用的单词。 实际单词从索引3
开始。
您可能会注意到输入样本的长度不同; 例如,第一个训练样本中有467
个单词,第二个训练样本中有138
个单词。 但是,训练 RNN 模型需要输入相同长度的样本。 我们需要将序列填充到相同的长度:短样本的末尾用 0 填充,而长样本则被截断。 我们使用pad_sequences
函数,将序列长度指定为200
:
>>> from keras.preprocessing import sequence >>> maxlen = 200 >>> x_train = sequence.pad_sequences(x_train, maxlen=maxlen) >>> x_test = sequence.pad_sequences(x_test, maxlen=maxlen) >>> print('x_train shape:', x_train.shape) x_train shape: (25000, 200) >>> print('x_test shape:', x_test.shape) x_test shape: (25000, 200)
现在,我们有 200 个单词的输入序列,每个单词由0
至4999
的整数表示。 我们可以进行传统的单热编码,但是所生成的 3,000 维稀疏输出将使训练相应的 RNN 模型过慢。 取而代之的是,我们执行单词嵌入以将单词索引转换为较低维的密集向量。 在 Keras 中,我们将嵌入层用作模型的第一层:
>>> from keras.models import Sequential >>> from keras.layers import Embedding >>> model = Sequential() >>> model.add(Embedding(max_words, 128, input_length=maxlen))
嵌入层将5000
(max_words
)值的索引输入转换为 128 维向量。 接下来,我们将双向 RNN 与 LSTM 相结合:
>>> from keras.layers import Dense, Embedding, LSTM, Bidirectional >>> model.add(Bidirectional(LSTM(128, dropout=0.2, recurrent_dropout=0.2)))
我们仅在 LSTM 层上应用Bidirectional
包装器。 LSTM 层具有 128 个隐藏的单元,其中输入单元的丢弃率为 20%,循环连接的丢弃率为 20%。
最后一层生成逻辑输出:
>>> model.add(Dense(1, activation='sigmoid'))
对于优化器,RMSprop
与0.001
的学习率一起使用,以最小化二分类的交叉熵:
>>> optimizer = optimizers.RMSprop(0.001) >>> model.compile(optimizer=optimizer,loss='binary_crossentropy', metrics=['accuracy'])
最后,我们将测试集作为验证数据训练模型,并根据验证损失提前停止训练:
>>> from keras.callbacks import EarlyStopping >>> early_stop = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1, mode='min') >>> model.fit(x_train, y_train, batch_size=32, epochs=100, validation_data=[x_test, y_test], callbacks=[early_stop])
让我们看一下周期1
,5
,8
和15
的日志:
Epoch 1
:
Train on 25000 samples, validate on 25000 samples Epoch 1/100 5504/25000 [=====>........................] - ETA: 15:04 - loss: 0.6111 - acc: 0.6672 25000/25000 [==============================] - 1411s 56ms/step - loss: 0.4730 - acc: 0.7750 - val_loss: 0.3765 - val_acc: 0.8436
Epoch 5
:
Epoch 5/100 5088/25000 [=====>........................] - ETA: 15:22 - loss: 0.2395 - acc: 0.9025 25000/25000 [==============================] - 1407s 56ms/step - loss: 0.2367 - acc: 0.9070 - val_loss: 0.2869 - val_acc: 0.8848 Epoch 00005: val_loss did not improve from 0.27994
Epoch 8
:
Epoch 8/100 5088/25000 [=====>........................] - ETA: 15:16 - loss: 0.1760 - acc: 0.9347 25000/25000 [==============================] - 1404s 56ms/step - loss: 0.1815 - acc: 0.9314 - val_loss: 0.2703 - val_acc: 0.8960
Epoch 15
:
Epoch 15/100 5408/25000 [=====>........................] - ETA: 15:08 - loss: 0.0936 - acc: 0.9680 25000/25000 [==============================] - 1413s 57ms/step - loss: 0.0975 - acc: 0.9656 - val_loss: 0.3588 - val_acc: 0.8816 Epoch 00015: val_loss did not improve from 0.27034
训练在周期18
停止,因为提前停止触发:
Epoch 00018: val_loss did not improve from 0.27034 Epoch 00018: early stopping
使用 BRNN 和 LSTM 可以达到 89.6% 的测试精度。
总结
我们刚刚完成了关于 DL 架构-RNN 的学习旅程的重要部分! 在本章中,我们更加熟悉了 RNN 及其变体。 我们从 RNN 是什么,RNN 的发展路径以及它们如何成为顺序建模的最新解决方案入手。 我们还研究了四种 RNN 架构,并按输入和输出数据的形式进行了分类,并提供了一些工业示例。
接下来,我们讨论按循环层分类的各种架构,包括原始 RNN,LSTM,GRU 和双向 RNN。 首先,我们应用了原始架构来编写我们自己的《战争与和平》 ,尽管有点荒谬。 我们通过使用 LSTM 架构 RNN 生成了更好的版本。 股票价格预测中采用了另一种内存增强型架构 GRU。
最后,除了过去的信息外,我们还引入了双向架构,该模型允许模型保留序列的过去和将来上下文中的信息。 我们还使用双向 RNN 和 LSTM 进行电影评论情感分类。 在下一章中,我们将探讨 DL 模型的另一项伟大发明:生成对抗网络。
第 4 节:生成对抗网络(GAN)
在本章中,您将学习 GAN 及其基准。 连同 GAN 模型的演变路径,将通过工业示例来说明架构和工程最佳实践。 我们还将探讨如何仅生成一个输出,一个输出是否可以是一个序列以及它是否仅适用于一个输入元素。
本节将介绍以下章节:
- “第 7 章”,“生成对抗网络”
七、生成对抗网络
在本章中,我们将解释最有趣的深度学习模型之一,即生成对抗网络(GAN)。 我们将首先回顾 GAN 是什么以及它们的用途。 在简要介绍了 GAN 模型的演化路径之后,我们将说明各种 GAN 架构以及图像生成示例。
想象一下,您正在模仿一个最初并不了解的艺术品(例如梵高的《星空》)。 您可以随意参加多次。 每次您提交作品时,评委都会向您反馈真实艺术品的外观以及复制品的接近程度。 在最初的几次尝试中,由于您对原始作品的了解非常有限,因此您的作品得分不高。 经过几番尝试,由于法官提供了有用的提示,您的作品越来越接近真实的艺术品。 您会不断尝试和改进,并将法官的反馈纳入您的尝试中,而在最近的几次尝试中,您的工作看起来与原始工作非常接近。 希望您最终能赢得比赛。
GAN 在合成图像,声波,视频和任何其他信号方面几乎具有相同的作用,我们将在本章中对此进行深入探讨。
在本章中,我们将介绍以下主题:
- 什么是 GAN?
- 生成模型
- 对抗训练
- GAN 的演进路径
- 原始 GAN 架构
- 实现原始 GAN
- 生成图像
- 深度卷积 GAN 架构
- 实现深度卷积 GAN
- 条件 GAN 架构
- 实现条件 GAN
- 信息最大化 GAN 架构
- 实现信息最大化 GAN
什么是 GAN?
GAN 可能是深度神经网络最有趣的类型之一。 自从 Goodfellow 等人首次引入 GAN 以来。 2014 年,围绕它们开展的研究项目和应用越来越多,其中一些确实很有趣。 以下是我们挑选的有趣的东西:
- 图像生成,例如猫图片,假名人面孔,甚至是现代艺术品
- 音频或视频合成,例如 DeepMind 的 WaveNets,它们能够为天文图像生成人类语音和 3D 重建
- 时间序列生成,例如用于股票市场预测的医学数据和高频数据
- 统计推断,例如通过分析一堆图片来设计服装的亚马逊算法
那么,为什么 GAN 如此强大? 对于 GAN,最好先讨论生成模型,因为 GAN 基本上是生成模型。
生成模型
机器学习中有两种主要类型的模型,即生成模型和判别模型。 顾名思义,判别模型试图在两个(或多个)类之间区分数据。 例如,我们在“第 4 章”,“CNN 架构”中讨论过的 CNN 模型,学会告诉我们一个图像是猫还是狗,给定其中一个图像, 以及 “第 6 章”,“循环神经网络”中的 RNN 模型经过训练,可以输出给定段落的正面或负面情感。 判别模型着重于根据数据的特征预测数据类别。 相反,生成模型不尝试将特征映射到类,而是在给定特定类的情况下生成特征。 例如,训练高斯混合模型(GMM)以生成适合训练集分布的新数据。 生成模型对给定单个类的特征分布进行建模。 也就是说,可以使用生成模型对数据进行分类,例如朴素贝叶斯和玻尔兹曼机,我们在“第 3 章”,“受限玻尔兹曼机和自编码器”中进行了讨论。 但是,他们的首要任务是弄清楚某些特征的可能性,而不是识别标签。 然后将学习到的特征分布用于分类。 如果仍然感到困惑,这是区分生成模型和判别模型的简单方法:
- 判别模型对寻找边界或规则来分离数据感兴趣
- 生成模型侧重于对数据分布进行建模
GAN 模型由两个网络组成。 一个称为生成器的网络负责生成新的数据样本,从而生成 GAN 生成模型。 另一个网络称为判别器,对生成的数据进行真实性评估。 具体而言,它确定单独生成的样本是否属于真实训练数据集。 再次,GAN 仍然是生成模型,因为它们专注于生成特别感兴趣的数据分布,并且添加了判别器以提供反馈以更好地生成数据。 让我们看看它是如何工作的。
对抗 - 以对抗方式进行训练
如果我们还记得梵高(Van Gogh)的例子,那么 GAN 模型就在做类似的事情。 我们模仿艺术品,而 GAN 中的生成器生成候选对象,例如感兴趣的特定数据分布; 法官评估我们的副本,而 GAN 中的判别器则通过从真实数据分布中区分生成的候选对象和实例来评估生成的候选对象。
GAN 中的生成器和判别器以对抗性方式进行训练,也就是说,它们在零和框架中相互竞争,以便生成模拟真实数据分布的数据。 就像在美术比赛中一样,我们不断改进复制品,以期获得法官的高分,并且法官不断评估我们的工作并提供反馈。 GAN 中的生成器旨在生成被视为来自真实数据分布的合成实例,即使它们是伪造的,而判别器的目标是识别单个实例是否是伪造的(已合成) 或真实的。 从优化的角度来看,生成器的训练目标是增加判别器的错误-判别器犯的错误越多,生成器的表现就越好。 判别器的目的是减少其误差,这是显而易见的。 在每次迭代中,两个网络都使用梯度下降来实现其目标,这并不是什么新鲜事。 有趣的是,每个网络都在试图击败另一个网络,即生成器试图欺骗判别器,而判别器则试图不被欺骗。 最终,来自生成器的合成数据(例如图像,音频,视频,时间序列)(希望)能够欺骗最复杂的判别器,类似于艺术品复制品竞赛,我们能够从最严格的标准中获得最高分但最有帮助的裁判。
实际上,生成器从具有高斯分布的多元高斯分布的预定义分布中抽取随机样本作为最受欢迎的输入,并生成看起来好像可能来自目标分布的数据。 这与复制品竞赛非常相似,在复制品竞赛中,我们最初对艺术品一无所知,并一直朝着像样的复制品努力。
以图像生成为例,下图可以表示 GAN 模型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IzgsrI33-1681704851873)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/7a2f478c-04d6-4247-89a2-9453d5c3d56d.png)]
GAN 采取的步骤如下:
- 生成器网络从高斯分布中获取随机样本并输出图像。
- 这些生成的图像然后被馈送到判别器网络。
- 判别器网络接收生成的图像和从实际数据集中获取的图像。
- 判别器输出概率,其中上限 1 表示输入图像被认为是真实的,下限 0 表示输入图像被认为是假的。
- 生成器的损失(成本函数)是基于判别器认为真实的伪图像的交叉熵来计算的。
- 判别器的损失(成本函数)是根据被认为是伪造的伪图像的交叉熵加上被认为是真实的真实图像的交叉熵来计算的。
- 对于每个周期,两个网络都经过优化,分别将其各自的损失降至最低。
- 在某个时候,当融合判别器将融合生成器生成的图像视为真实图像时,该模型将得到很好的训练。
- 最终,训练有素的生成器会生成图像作为最终输出,从而模仿真实图像的输入。
GAN 的演进路径
对抗训练的思想可以追溯到 1990 年代早期的工作,例如 Schmidhuber 的《通过可预测性最小化学习阶乘代码》。 2013 年,在《通过受控相互作用学习动物行为的协同进化方法》中提出了无模型推断的对抗模型。 2014 年,Goodfellow 等人在《生成对抗网络》中首次引入了 GAN。
Li 等人(提出动物行为推断的同一作者)在《图灵学习:一种无度量的行为推断方法及其在群体中的应用》中于 2016 年提出了图灵学习 一词。 图灵学习与图灵测试相关,并且是 GAN 的概括,如《概括 GAN:图灵的视角》中所总结。 在图灵学习中,模型不仅限于 GAN 或神经网络; 判别器会影响生成器的输入,在图灵测试中充当询问器。
提出的第一个 GAN 由用于生成器和判别器的全连接层组成。 自从原始架构以来,已经开发了许多新的创新方法,例如,深度卷积 GAN,条件 GAN 和信息最大化的 GAN。 在下一节中,我们将详细研究这些架构,并从头开始实现每个架构。
GAN 架构和实现
如所承诺的,我们将仔细研究我们在前几节中详细提到的 GAN 的变体,并将其应用于实际问题。 最常用的 GAN 包括深度卷积 GAN,条件 GAN 和信息最大化 GAN。 让我们从最基本的架构开始。
原始 GAN
在最基本的 GAN 模型中,生成器和判别器都是全连接神经网络。 原始 GAN 的架构可以描述如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CTm5SmaR-1681704851873)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/df219698-e593-49cf-94e6-07bc0d8639e4.png)]
生成器的输入是来自特定分布的随机样本,我们通常将其称为噪声或潜在变量。 第二层和后面的几层是隐藏层,在这种情况下,它们是全连接层。 隐藏层通常比其先前的隐藏层具有更多的单元。 输出层的大小与预期的生成大小相同,与实际数据的大小相同。 对于判别器,其输入是真实或生成的数据,其后是一个或多个隐藏层,以及一个单元的输出层。 每个隐藏层通常比其先前的隐藏层具有更少的单元。 通常,生成器和判别器具有相同数量的隐藏层。 而且,两组隐藏层通常是对称的。 例如,原始 GAN 的生成器和判别器如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-38wka9Wx-1681704851874)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-dl-arch-py/img/90f1c978-7819-4873-954b-7fae6c1d984f.png)]
现在我们已经了解了什么是普通 GAN,我们可以开始在 TensorFlow 中从头开始实现它们了。 从这里开始,我们将以 MNIST 手写数字数据集为例,以便我们可以应用 GAN 生成我们自己的 MNIST。
我们将从加载用于模型训练的 MNIST 数据集开始:
>>> import numpy as np >>> import tensorflow as tf >>> def load_dataset(): ... (x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data('./mnist_data') ... train_data = np.concatenate((x_train, x_test), axis=0) ... train_data = train_data / 255. ... train_data = train_data * 2\. - 1 ... train_data = train_data.reshape([-1, 28 * 28]) ... return train_data
该函数读取并合并原始训练和测试集(不包括标签),因为在原始模型中不需要它们。 它还将数据从[0, 255]
到[-1, 1]
的范围重新缩放,这是神经网络模型预处理的非常重要的一部分,并且还将单个样本重塑为一维样本。
调用此函数并检查已加载数据的大小:
>>> data = load_dataset() >>> print("Training dataset shape:", data.shape) Training dataset shape: (70000, 784)
总共有 70,000 个训练样本,每个样本有 784 个尺寸(28 x 28
)。 如果您忘记了 MNIST 数据的外观,我们将使用定义如下的函数显示一些示例:
>>> import matplotlib.pyplot as plt >>> def display_images(data, image_size=28): ... fig, axes = plt.subplots(4, 10, figsize=(10, 4)) ... for i, ax in enumerate(axes.flatten()): ... img = data[i, :] ... img = (img - img.min()) / (img.max() - img.min()) ... ax.imshow(img.reshape(image_size, image_size), cmap='gray') ... ax.xaxis.set_visible(False) ... ax.yaxis.set_visible(False) ... plt.subplots_adjust(wspace=0, hspace=0) ... plt.show()
此函数在 4 行 10 列中显示 40 张图像。 稍后将重新使用它以显示生成的图像。 看一下前 40 个真实样本:
>>> display_images(data)
Python 深度学习架构实用指南:第三、四、五部分(3)https://developer.aliyun.com/article/1427002