文章目录
内容介绍
Keras 使用快速符号数学库作为后端,例如 TensorFlow 和 Theano。
使用这些库的一个缺点是,无论您是在训练网络还是进行预测,都必须预先定义数据的形状和大小并保持不变。
在序列预测问题上,可能需要在训练网络时使用大批量,在进行预测时使用批量大小为 1,以便预测序列中的下一步。
在本教程中,您将了解如何解决这个问题,甚至在训练和预测过程中使用不同的Batch Size。后面所有的 Batch Size 使用直译的 批量大小 表示。
教程环境
假设已安装并运行 Python 2 或 3 环境。
这包括带有 NumPy 和 Pandas 的 SciPy。Keras 2.0 或更高版本必须与 TensorFlow 或 Keras 后端一起安装。
有关设置 Python 环境的帮助,请参阅:
Tensorflow-gpu1.x版本和Tensorflow-gpu2.x版本共存之法
批量大小
使用 Keras 的一个好处是它建立在符号数学库(例如 TensorFlow 和 Theano)之上,可实现快速高效的计算。这是大型神经网络所需要的。
使用这些高效库的缺点是您必须始终预先定义数据的范围。具体来说,批量大小(Batch Size)。
批量大小 限制了在执行权重更新之前要显示给网络的样本数量。当使用拟合模型进行预测时,也会施加同样的限制。
具体来说,拟合模型时使用的 批量大小 控制了一次必须进行的预测数量。
当您希望一次进行与训练期间使用的 批量大小 相同的预测时,这通常不是问题。
当您希望进行比批量大小更少的预测时,这确实会成为一个问题。例如,您可能会使用大批量获得最佳结果,但需要一次对时间序列或序列问题之类的观察进行预测。
这就是为什么在将网络拟合到训练数据时,与对测试数据或新输入数据进行预测时相比,可能需要使用不同的批大小。
在本文中,我们将探索解决此问题的不同方法。
序列预测问题描述
我们将使用一个简单的序列预测问题作为上下文来演示在训练和预测之间改变批量大小的解决方案。
序列预测问题是改变批量大小的一个很好的例子,因为您可能希望在训练期间批量大小等于训练数据集大小(批量学习),在对一步输出进行预测时批量大小为 1。
序列预测问题涉及学习预测以下 10 步序列中的下一步:
[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
我们可以在 Python 中创建这个序列,如下所示:
length = 10
sequence = [i/float(length) for i in range(length)]
print(sequence)
运行示例打印我们的序列:
[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
我们必须将序列转换为监督学习问题。这意味着当 0.0 显示为输入模式时,网络必须学会将下一步预测为 0.1。
我们可以使用 Pandas shift()函数在 Python 中执行此操作,如下所示:
from pandas import concat
from pandas import DataFrame
# 创建序列
length = 10
sequence = [i/float(length) for i in range(length)]
# 创建匹配的自变量/因变量
df = DataFrame(sequence)
df = concat([df, df.shift(1)], axis=1)
df.dropna(inplace=True)
print(df)
运行示例显示所有输入和输出对。
1 0.1 0.0
2 0.2 0.1
3 0.3 0.2
4 0.4 0.3
5 0.5 0.4
6 0.6 0.5
7 0.7 0.6
8 0.8 0.7
9 0.9 0.8
我们将使用称为长短期记忆网络的循环神经网络来学习序列。因此,我们必须将输入模式从 2D 数组(1 列 9 行)转换为由 [行、时间步长、列]组成的 3D 数组,其中时间步长为 1,因为每行每个观察只有一个时间步长。
我们可以使用 NumPy 函数reshape()来做到这一点,如下所示:
from pandas import concat
from pandas import DataFrame
# 创建序列
length = 10
sequence = [i/float(length) for i in range(length)]
# 创建匹配的自变量/因变量
df = DataFrame(sequence)
df = concat([df, df.shift(1)], axis=1)
df.dropna(inplace=True)
# 转换为LSTM格式
values = df.values
X, y = values[:, 0], values[:, 1]
X = X.reshape(len(X), 1, 1)
print(X.shape, y.shape)
LSTM 模型和不同的批次大小
在本节中,我们将为该问题设计一个 LSTM 网络。
训练批量大小将覆盖整个训练数据集(批量学习),并且一次进行一个预测(一步预测)。我们将证明,尽管模型学习了问题,但一步预测会导致错误。
我们将使用适合 1000 个 epoch 的 LSTM 网络。
权重将在每个训练时期(批量学习)结束时更新,这意味着批量大小将等于训练观察的数量 (9)。
对于这些实验,我们需要对 LSTM 的内部状态何时更新进行细粒度控制。通常 LSTM 状态会在 Keras 中的每个批次结束时清除,但我们可以通过使 LSTM 有状态并调用model.reset_state()手动管理此状态来控制它。这将在后面的部分中需要。
该网络有一个输入、一个有 10 个单元的隐藏层和一个有 1 个单元的输出层。LSTM 单元中使用默认的 tanh 激活函数,输出层中使用线性激活函数。
均方误差优化函数通过高效的 ADAM 优化算法用于此回归问题。
下面的示例配置和创建网络。
# 配置网络
n_batch = len(X)
n_epoch = 1000
n_neurons = 10
# 设计网络
model = Sequential()
model.add(LSTM(n_neurons, batch_input_shape=(n_batch, X.shape[1], X.shape[2]), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
我们将使网络适应每个时期的所有示例,并在每个时期结束时手动重置网络状态。
# 拟合网络
for i in range(n_epoch):
model.fit(X, y, epochs=1, batch_size=n_batch, verbose=1, shuffle=False)
model.reset_states()
最后,我们将一次预测序列中的每一步。
这需要批量大小为 1,这与用于拟合网络的批量大小 9 不同,并且会在示例运行时导致错误。
# 在线预测
for i in range(len(X)):
testX, testy = X[i], y[i]
testX = testX.reshape(1, 1, 1)
yhat = model.predict(testX, batch_size=1)
print('>Expected=%.1f, Predicted=%.1f' % (testy, yhat))
下面是完整的代码示例。
from pandas import DataFrame
from pandas import concat
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
# 创建序列
length = 10
sequence = [i/float(length) for i in range(length)]
# 创建匹配的自变量/因变量
df = DataFrame(sequence)
df = concat([df, df.shift(1)], axis=1)
df.dropna(inplace=True)
# 转换为LSTM格式
values = df.values
X, y = values[:, 0], values[:, 1]
X = X.reshape(len(X), 1, 1)
# 配置网络
n_batch = len(X)
n_epoch = 1000
n_neurons = 10
# 设计网络
model = Sequential()
model.add(LSTM(n_neurons, batch_input_shape=(n_batch, X.shape[1], X.shape[2]), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
# 拟合网络
for i in range(n_epoch):
model.fit(X, y, epochs=1, batch_size=n_batch, verbose=1, shuffle=False)
model.reset_states()
# 在线预测
for i in range(len(X)):
testX, testy = X[i], y[i]
testX = testX.reshape(1, 1, 1)
yhat = model.predict(testX, batch_size=1)
print('>Expected=%.1f, Predicted=%.1f' % (testy, yhat))
运行该示例可以很好地拟合模型,但在进行预测时会导致错误。
报的错误如下:
ValueError: Cannot feed value of shape (1, 1, 1) for Tensor 'lstm_1_input:0', which has shape '(9, 1, 1)'
解决方案 1:在线学习(批量大小 = 1)
这个问题的一个解决方案是使用在线学习来拟合模型。
这是批量大小设置为 1 的值,并且在每个训练示例之后更新网络权重。
这可以产生更快的学习效果,但也会增加学习过程的不稳定性,因为每个批次的权重差异很大。
尽管如此,这将使我们能够对问题进行一步预测。唯一需要更改的是将n_batch设置为 1,如下所示:
n_batch = 1
下面提供了完整的代码清单。
from pandas import DataFrame
from pandas import concat
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
# 创建序列
length = 10
sequence = [i/float(length) for i in range(length)]
# 创建匹配的自变量/因变量
df = DataFrame(sequence)
df = concat([df, df.shift(1)], axis=1)
df.dropna(inplace=True)
# 转换为LSTM格式
values = df.values
X, y = values[:, 0], values[:, 1]
X = X.reshape(len(X), 1, 1)
# 配置网络
n_batch = 1
n_epoch = 1000
n_neurons = 10
# 设计网络
model = Sequential()
model.add(LSTM(n_neurons, batch_input_shape=(n_batch, X.shape[1], X.shape[2]), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
# 拟合网络
for i in range(n_epoch):
model.fit(X, y, epochs=1, batch_size=n_batch, verbose=1, shuffle=False)
model.reset_states()
# 在线预测
for i in range(len(X)):
testX, testy = X[i], y[i]
testX = testX.reshape(1, 1, 1)
yhat = model.predict(testX, batch_size=1)
print('>Expected=%.1f, Predicted=%.1f' % (testy, yhat))
运行该示例将打印 9 个预期结果和正确的预测。
>Expected=0.0, Predicted=0.0
>Expected=0.1, Predicted=0.1
>Expected=0.2, Predicted=0.2
>Expected=0.3, Predicted=0.3
>Expected=0.4, Predicted=0.4
>Expected=0.5, Predicted=0.5
>Expected=0.6, Predicted=0.6
>Expected=0.7, Predicted=0.7
>Expected=0.8, Predicted=0.8
解决方案 2:批量预测(批量大小 = N)
另一种解决方案是在一个批次中一次进行所有预测。
这意味着我们在使用模型的方式上可能非常有限。
我们将不得不一次使用所有的预测,或者只保留第一个预测并丢弃其余的。
我们可以通过使用等于训练批量大小的批量大小进行预测,然后枚举预测的批量,来调整批量预测的示例,如下所示:
# 批量预测
yhat = model.predict(X, batch_size=n_batch)
for i in range(len(y)):
print('>Expected=%.1f, Predicted=%.1f' % (y[i], yhat[i]))
下面列出了完整的示例。
from pandas import DataFrame
from pandas import concat
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
# 创建序列
length = 10
sequence = [i/float(length) for i in range(length)]
# 创建匹配的自变量/因变量
df = DataFrame(sequence)
df = concat([df, df.shift(1)], axis=1)
df.dropna(inplace=True)
# 转换为LSTM格式
values = df.values
X, y = values[:, 0], values[:, 1]
X = X.reshape(len(X), 1, 1)
# 配置网络
n_batch = len(X)
n_epoch = 1000
n_neurons = 10
# 设计网络
model = Sequential()
model.add(LSTM(n_neurons, batch_input_shape=(n_batch, X.shape[1], X.shape[2]), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
# 拟合网络
for i in range(n_epoch):
model.fit(X, y, epochs=1, batch_size=n_batch, verbose=1, shuffle=False)
model.reset_states()
# 批量预测
yhat = model.predict(X, batch_size=n_batch)
for i in range(len(y)):
print('>Expected=%.1f, Predicted=%.1f' % (y[i], yhat[i]))
运行示例打印预期和正确的预测值。
>Expected=0.0, Predicted=0.0
>Expected=0.1, Predicted=0.1
>Expected=0.2, Predicted=0.2
>Expected=0.3, Predicted=0.3
>Expected=0.4, Predicted=0.4
>Expected=0.5, Predicted=0.5
>Expected=0.6, Predicted=0.6
>Expected=0.7, Predicted=0.7
>Expected=0.8, Predicted=0.8
解决方案 3:复制权重
更好的解决方案是使用不同的批量大小进行训练和预测。
这样做的方法是从拟合网络复制权重,并使用预先训练的权重创建一个新网络。
我们可以使用Keras API 中的get_weights()和set_weights()函数轻松完成此操作,如下所示:
# 重新定义batch大小
n_batch = 1
# 重新定义模型
new_model = Sequential()
new_model.add(LSTM(n_neurons, batch_input_shape=(n_batch, X.shape[1], X.shape[2]), stateful=True))
new_model.add(Dense(1))
# 复制权重
old_weights = model.get_weights()
new_model.set_weights(old_weights)
这将创建一个新模型,该模型的批处理大小为 1。然后我们可以使用这个新模型进行一步预测:
# 在线预测
for i in range(len(X)):
testX, testy = X[i], y[i]
testX = testX.reshape(1, 1, 1)
yhat = new_model.predict(testX, batch_size=n_batch)
print('>Expected=%.1f, Predicted=%.1f' % (testy, yhat))
下面列出了完整的示例。
from pandas import DataFrame
from pandas import concat
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
# 创建序列
length = 10
sequence = [i/float(length) for i in range(length)]
# 创建匹配的自变量/因变量
df = DataFrame(sequence)
df = concat([df, df.shift(1)], axis=1)
df.dropna(inplace=True)
# 转换为LSTM格式
values = df.values
X, y = values[:, 0], values[:, 1]
X = X.reshape(len(X), 1, 1)
# 配置网络
n_batch = 3
n_epoch = 1000
n_neurons = 10
# 设计网络
model = Sequential()
model.add(LSTM(n_neurons, batch_input_shape=(n_batch, X.shape[1], X.shape[2]), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
# 拟合网络
for i in range(n_epoch):
model.fit(X, y, epochs=1, batch_size=n_batch, verbose=1, shuffle=False)
model.reset_states()
# 重新定义batch大小
n_batch = 1
# 重新定义模型
new_model = Sequential()
new_model.add(LSTM(n_neurons, batch_input_shape=(n_batch, X.shape[1], X.shape[2]), stateful=True))
new_model.add(Dense(1))
# 复制权重
old_weights = model.get_weights()
new_model.set_weights(old_weights)
# compile model
new_model.compile(loss='mean_squared_error', optimizer='adam')
# 在线预测
for i in range(len(X)):
testX, testy = X[i], y[i]
testX = testX.reshape(1, 1, 1)
yhat = new_model.predict(testX, batch_size=n_batch)
print('>Expected=%.1f, Predicted=%.1f' % (testy, yhat))
运行该示例将打印预期值,并再次正确预测值。
>Expected=0.0, Predicted=0.0
>Expected=0.1, Predicted=0.1
>Expected=0.2, Predicted=0.2
>Expected=0.3, Predicted=0.3
>Expected=0.4, Predicted=0.4
>Expected=0.5, Predicted=0.5
>Expected=0.6, Predicted=0.6
>Expected=0.7, Predicted=0.7
>Expected=0.8, Predicted=0.8