了解 LSTM 行为。
当你想进行疯狂处理时,这是为了调查。 (最初用于 R2D2 烧录实现)
进口
以下导入被省略,因为它们已经完成。
import random
import numpy as np
import tensorflow as tf
import tensorflow.keras as keras
from tensorflow.keras import layers as kl
import pandas as pd
import matplotlib.pyplot as plt
1. 基本 LSTM
考虑一个要对 0 或 1 的时间序列求和的数据集。
生成数据集的代码如下。
def create_data(data_num, sequence_len):
x = np.random.randint(0, 2, (data_num, sequence_len)).astype(np.float32)
y = np.sum(x, axis=1).reshape((data_num, 1))
return x[..., np.newaxis], y
x, y = create_data(1, 5)
print(x.shape) # (1, 5, 1)
print(x) # [[[0.] [1.] [0.] [1.] [1.]]]
print(y.shape) # (1, 1)
print(y) # [[3.]]
create_data 将数据的数量和长度作为参数,返回输入数据和正确数据。
模型如下。
model = keras.models.Sequential([
kl.Input(shape=(None, 1)), # ※1
kl.TimeDistributed(kl.Dense(32)), # ※2
kl.LSTM(32),
kl.Dense(1),
])
model.summary()
*1 输入为(时间序列数、形状)
*2 由于需要将时间序列保持到 LSTM,所以在输入和 LSTM 之间插入层时,使用 TimeDistributed 来保持时间序列的维度
学习如下。
时间序列的长度为 20。
# データセット作成
x_train, y_train = create_data(1000, 20)
x_valid, y_valid = create_data(100, 20)
# 学習
model.compile(loss="mse", optimizer="adam")
history = model.fit(x_train, y_train, batch_size=32, epochs=100, validation_data=(x_valid, y_valid))
# 学習過程
df_hist = pd.DataFrame(history.history)
df_hist[["loss", "val_loss"]].plot()
plt.show()
·学习过程
以下是部分学习成果:
x_test, y_test = create_data(1, 20)
y_pred = model(x_test) # 予測
print(x_test.flatten()) # [1. 1. 1. 0. 0. 1. 1. 1. 0. 1. 1. 1. 0. 0. 1. 0. 0. 0. 0. 1.]
print(y_test) # [[11.]]
print(y_pred.numpy()) # [[11.001144]]
你学得很好。
调查 1. 改变时间序列长度时的预测
学习的时间序列长度是 20,但是让我们看一下将其从 1 更改为 40 时的预测结果。
plot_x = []
plot_y = []
for i in range(1, 40):
x_valid, y_valid = create_data(100, i)
y_pred = model(x_valid)
mse = keras.metrics.MeanSquaredError()(y_valid, y_pred).numpy()
plot_x.append(i)
plot_y.append(mse)
plt.plot(plot_x, plot_y)
plt.xlabel("sequence len")
plt.ylabel("MSE")
plt.show()
在 20 时,预测很好,但在其他所有情况下,准确度都会从 20 下降。
这是因为 LSTM 不学习中间进度,而只学习最终结果。
2.学习包括计算过程
0 或 1 的时间序列计算总和的地方是一样的,但是这一次,中间进度也是作为教师数据学习的。
数据集的生成代码如下。
def create_data_timesteps(data_num, sequence_len):
x_list = []
y_list = []
for _ in range(data_num):
n = 0
for _ in range(sequence_len):
x = random.randint(0, 1)
n += x
x_list.append(x)
y_list.append(n)
x_list = np.array(x_list).reshape((data_num, sequence_len, 1))
y_list = np.array(y_list).reshape((data_num, sequence_len, 1))
return x_list, y_list
x, y = create_data_timesteps(1, 5)
print(x.shape) # (1, 5, 1)
print(x) # [[[1] [1] [1] [0] [0]]]
print(y.shape) # (1, 5, 1)
print(y) # [[[1] [2] [3] [3] [3]]]
模型如下。
model = keras.models.Sequential([
kl.Input(shape=(None, 1)),
kl.TimeDistributed(kl.Dense(32)),
kl.LSTM(32, return_sequences=True), # ※3
kl.TimeDistributed(kl.Dense(1)), # ※4
])
model.summary()
*3 如果使用return_sequences=True,则不仅会返回最后的输出,还会返回所有输出。
*4 添加 TimeDistributed 是因为我们也希望在输出层中包含时间序列。
也学习长度 20。
# データセット作成
x_train, y_train = create_data_timesteps(1000, 20)
x_valid, y_valid = create_data_timesteps(100, 20)
# 学習
model.compile(loss="mse", optimizer="adam")
history = model.fit(x_train, y_train, batch_size=32, epochs=100, validation_data=(x_valid, y_valid))
# 学習結果
df_hist = pd.DataFrame(history.history)
df_hist[["loss", "val_loss"]].plot()
plt.show()
·学习过程
・部分学习成果
x_test, y_test = create_data_timesteps(1, 20)
y_pred = model(x_test) # 予測
print(x_test.flatten()) # [1 0 0 0 1 1 1 1 0 1 1 1 1 0 0 0 1 1 0 1]
print(y_test.flatten()) # [ 1 1 1 1 2 3 4 5 5 6 7 8 9 9 9 9 10 11 11 12]
print(y_pred.numpy().flatten())
# [ 1.0127519 0.9785429 0.9656553 0.96232927 1.9568805 2.9836545
# 3.9930987 4.982367 4.981758 5.9777784 6.9905696 8.007053
# 9.018621 9.0236225 9.002367 8.9889345 10.002424 10.991257
# 10.980751 11.983499 ]
你学到了很多东西,包括你一路上取得的进步。
调查2.改变时间序列长度时的预测(中级学习完成)
同样,让我们看看将时间序列的长度从 1 更改为 40 时的结果。
plot_x = []
plot_y = []
for i in range(1, 40):
x_valid, y_valid = create_data_timesteps(100, i)
y_pred = model(x_valid)
mse = keras.metrics.MeanSquaredError()(y_valid, y_pred).numpy()
plot_x.append(i)
plot_y.append(mse)
plt.plot(plot_x, plot_y)
plt.xlabel("sequence len")
plt.ylabel("MSE")
plt.show()
训练仅使用长度为 20 的数据,但即使使用长度为 20 或更短的数据,它也可以正确预测。
由于 20 之后没有训练数据,所以预测准确率很差。
3. 使用隐藏状态保存中间进度
我想使用隐藏状态保存中间进度。
例如,如果在输入数据 [1, 1] 之后输入 [0, 1, 0],它会输出 3。
(将输入分成2次)
为了能够做到这一点,我们需要使用 LSTM 内部的隐藏状态。
模型如下。
我认为使用自定义模型创建可能是最简单的。
class MyModel(keras.Model):
def __init__(self):
super().__init__()
self.in_layer = kl.TimeDistributed(kl.Dense(32))
self.lstm_layer = kl.LSTM(32, return_sequences=True, return_state=True) # ※5
self.out_layer = kl.TimeDistributed(kl.Dense(1))
# 学習では hidden_state は入力せず、時系列全体のデータをいれる
def call(self, x, training=False):
x, h = self.predict(x, None, training)
return x
# 予測は hidden_state を入力し、途中から始める
def predict(self, x, hidden_state, training=False):
x = self.in_layer(x, training=training)
x, h, c = self.lstm_layer(x, initial_state=hidden_state, training=training) # ※6 ※7
x = self.out_layer(x, training=training)
return x, [h, c]
# 初期隠れ状態を取得する用の関数
def get_initial_state(self):
return self.lstm_layer.cell.get_initial_state(batch_size=1, dtype=tf.float32)
model = MyModel()
# 学習
x_train, y_train = create_data_timesteps(1000, 20)
x_valid, y_valid = create_data_timesteps(100, 20)
model.compile(loss="mse", optimizer="adam")
history = model.fit(x_train, y_train, batch_size=32, epochs=100, validation_data=(x_valid, y_valid))
df_hist = pd.DataFrame(history.history)
df_hist[["loss", "val_loss"]].plot()
plt.show()
*5 如果设置为return_state=True,将返回隐藏状态。
*6 在return_state=True的情况下,返回值增加2,最后2隐藏。 (短期记忆和长期记忆)
*7 您可以通过将initial_state 作为参数来指定隐藏状态。
学习结果和2中的模型一样,所以我们省略了。
调查 3. 保存进度并计算
我尝试输入 [1,1,1],然后查看 [1,0,1] → 5 的模式和 [0,1,0,1,1] → 6 的模式。
h0 = model.get_initial_state() # 初期隠れ状態
# --- [1,1,1] を入力
x_test1 = np.array([1, 1, 1], dtype="float32").reshape((1, -1, 1))
y_pred1, h_3 = model.predict(x_test1, h0)
print(y_pred1.numpy().flatten()) # [1.0058864 2.0119507 3.0231385]
# h_3 は内部的には3の情報を持っているはず
# --- h_3 + [1,0,1] を入力
x_test2 = np.array([1, 0, 1], dtype="float32").reshape((1, -1, 1))
y_pred2, _ = model.predict(x_test2, h_3)
print(y_pred2.numpy().flatten()) # [4.0048146 4.005358 5.0157046]
# --- [1,0,1] のみを入力
x_test3 = np.array([1, 0, 1], dtype="float32").reshape((1, -1, 1))
y_pred3, _ = model.predict(x_test3, h0) # h0の代わりにNoneでも同じ
print(y_pred3.numpy().flatten()) # [1.0058864 0.9834154 2.0015857]
# --- h_3 + [0,1,0,1,1] を入力
x_test4 = np.array([0,1,0,1,1], dtype="float32").reshape((1, -1, 1))
y_pred4, h_6 = model.predict(x_test4, h_3)
print(y_pred4.numpy().flatten()) # [3.0191178 4.0374146 4.0218706 5.0195336 5.9959307]
# h_6 は内部的には6の情報を持っているはず
# --- h_6 + [0,1,1,1] を入力
x_test5 = np.array([0,1,1,1], dtype="float32").reshape((1, -1, 1))
y_pred5, _ = model.predict(x_test5, h_6)
print(y_pred5.numpy().flatten()) # [5.9741497 6.9924216 7.9924083 8.983709 ]
你计算正确。
参考
・ 使用基本模型尝试 TensorFlow RNN
・在 Keras 中尝试基本的 RNN (LSTM)
原创声明:本文系作者授权爱码网发表,未经许可,不得转载;
原文地址:https://www.likecs.com/show-308623272.html