在Keras中如何对超参数进行调优?-深蓝源码网


时间: 2020-09-03 00:08:26 人气: 2283 评论: 0

由于没有一个成熟的理论来解释神经网络,所以配置神经网络通常是困难的,经常被同学们调侃为“炼丹”。

对于一个给定的预测建模问题,你必须系统地尝试不同的配置然后从客观和变化的视角来审视不同配置的结果,然后尝试理解在不同的配置下分别发生了什么,从而对模型进行合理的调优。

本教程将专注于时间预测问题并讨论如何对LSTM(long-short term memory,长短期记忆,最流行的RNN网络之一)网络进行配置。

认真完成本教程后,您将掌握以下技能:

  • 如何调整训练的epoch数量并解释调整后的结果。
  • 如何调整单次训练中的batch size并解释调整后的结果。
  • 如何调整神经元的数量并解释调整后的结果。

事不宜迟,我们现在就开始吧。

摄影:[David Saddler](https://www.flickr.com/photos/80502454@N00/6585205675/),保留部分权利

教程概述

本教程可以分为以下6个部分;:

  1. 数据集准备
  2. 设计实验测试套件
  3. 调整epoch的大小
  4. 调整Batch Size的大小
  5. 调整神经元的数量
  6. 结果汇总

环境要求

本教程假设您已经安装了Python SciPy环境。Python 2或3皆可。

本教程假定您已经安装了Keras v2.0或更高版本(TensorFlow或Theano作为后端皆可)。

本教程还假设您已经安装了scikit-learn,pandas,NumPy和Matplotlib。

如果你不知道如何配置这些环境,可以参考下面的文章:

数据集准备

该数据集描述了3年期间每月洗发剂的销量。

这个数据集的基本单元是与时间对应的销量,一共有36个观测值。这个数据集是由Makridakis, Wheelwright, and Hyndman (1998)贡献。

下载并了解更多关于数据集的信息

下面的代码演示了如何导入数据集并据此绘制销量随时间的变化情况

# load and plot dataset
from pandas import read_csv
from pandas import datetime
from matplotlib import pyplot
# load dataset
def parser(x):
    return datetime.strptime('190'+x, '%Y-%m')
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# summarize first few rows
print(series.head())
# line plot
series.plot()
pyplot.show()

运行代码导入数据集,数据会被加载为Pandas Series类型并且控制台会输出前五行的数据。

Month
1901-01-01    266.0
1901-02-01    145.9
1901-03-01    183.1
1901-04-01    119.3
1901-05-01    180.3
Name: Sales, dtype: float64

绘制销量随时间变化的折线图,可以很清楚地看到销量的增长趋势。

接下来,我们来看一下实验中LSTM的配置和实验所需的测试套件。

测试套件

本节将介绍本教程中使用的测试套件。

数据分割

我们将销量数据集分为两部分:训练集和测试集。

前两年的数据作为训练集,剩余一年的数据作为测试集。

训练集将用于模型的训练,训练得到的模型将用于预测测试集中的销量。

如果以最后一个月的销量作为恒定的预测值,对测试集中的销量值预测误差为平均每月136.761的。这也是我们对模型在测试集上性能要求的下限。

模型评估

我们将使用滚动预测方案,也称为前向模型验证。

测试数据集上的时间步长每次挪动一个单位.每次挪动后模型对下一个单位时长中的销量进行预测,然后取出真实的销量同时对下一个单位时长中的销量进行预测。

这种方案模拟了真实世界当中的场景,每个月都会有新的销量数据,我们会利用过去月份的销量数据对下个月的销量进行预测。

这个过程可以通过借助训练集和测试集中的时间标记来完成,在后面我们会一次性预测出测试集中所有的销量数据。

我们将会利用测试集中所有的数据对模型的预测性能进行训练并通过误差值来评判模型的性能。在这个例子里我们使用均方根误差(RMSE),因为相比于其他损失函数它可以提供较大的罚值,从而使模型的预测值更趋近于真实的销量值。

数据准备

在我们在数据集上拟合LSTM模型之前,我们必须先对数据集格式进行转换。

下面就是我们在拟合模型进行预测前要先做的三个数据转换:

  1. 固定时间序列数据。具体到这个问题就是让销量数据严格按照时间顺序排列,下一条的数据就是下一个月的销量数据。
  2. 将时间序列信息隐含与监督学习当中,可以通过组织数据的输入输出方式来实现,在这个问题中只需将前一段时间的销量作为模型输入来预测当前月份的销量数据即可。
  3. 对销量数据进行合适的缩放。具体来说,为了让数据的输入范围与LSTM模型的激励函数输出范围相匹配,需要将销量值缩放至-1~1的范围当中。

为了让模型仍旧可以输出我们需要的销量预测值,需要对变换后的输出进行逆变换从而输出模型的原始预测值。

实验运行

每个实验场景需要重现10次以上。

这是因为即使给定了模型训练所需的超参数,LSTM模型的随机初始化也可能会引起模型训练结果间的巨大差异。

通过审视不同模型超参数下模型性能随迭代次数(epochs)的变化曲线,我们可以得到一些可能提升模型性能的超参数调整区间或方向。

在每个批次的数据集训练结束后,测试集和训练集上的得分(即均方根误差)会打印输出出来。

每个批次结束后打印输出性能评估指标可以帮助我们更好地了解到模型的现状,比如说是否发生了过拟合。

训练集和测试集上的RMSE损失值曲线在运行结束之后通过折线图展现,我们设定测试集曲线为蓝色,测试集为橙色。

下面让我们编写代码,然后对模型的结果进行分析。

调整epochs的大小

我们调整的第一个模型超参数是epochs。

为了保持其他超参数的一致,我们固定神经元数量为1,Batch Size为4。下面我们通过调整epochs来观察模型性能参数的变化。

epochs = 500

下面给出了我们实验使用的代码清单。

代码是最好的注释,同时代码也易于复用和变更,这份代码是我们下面所有的实验的基础代码,我们只需要在上面稍作变更就可以完成后续的所有实验。

from pandas import DataFrame
from pandas import Series
from pandas import concat
from pandas import read_csv
from pandas import datetime
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from math import sqrt
import matplotlib
# be able to save images on serve
matplotlib.use('Agg')
from matplotlib import pyplot
import numpy
 
# date-time parsing function for loading the dataset
def parser(x):
    return datetime.strptime('190'+x, '%Y-%m')
 
# frame a sequence as a supervised learning problem
def timeseries_to_supervised(data, lag=1):
    df = DataFrame(data)
    columns = [df.shift(i) for i in range(1, lag+1)]
    columns.append(df)
    df = concat(columns, axis=1)
    df = df.drop(0)
    return df
 
# create a differenced series
def difference(dataset, interval=1):
    diff = list()
    for i in range(interval, len(dataset)):
        value = dataset[i] - dataset[i - interval]
        diff.append(value)
    return Series(diff)
 
# scale train and test data to [-1, 1]
def scale(train, test):
    # fit scale
    scaler = MinMaxScaler(feature_range=(-1, 1))
    scaler = scaler.fit(train)
    # transform train
    train = train.reshape(train.shape[0], train.shape[1])
    train_scaled = scaler.transform(train)
    # transform test
    test = test.reshape(test.shape[0], test.shape[1])
    test_scaled = scaler.transform(test)
    return scaler, train_scaled, test_scaled
 
# inverse scaling for a forecasted value
def invert_scale(scaler, X, yhat):
    new_row = [x for x in X] + [yhat]
    array = numpy.array(new_row)
    array = array.reshape(1, len(array))
    inverted = scaler.inverse_transform(array)
    return inverted[0, -1]
 
# evaluate the model on a dataset, returns RMSE in transformed units
def evaluate(model, raw_data, scaled_dataset, scaler, offset, batch_size):
    # separate
    X, y = scaled_dataset[:,0:-1], scaled_dataset[:,-1]
    # reshape
    reshaped = X.reshape(len(X), 1, 1)
    # forecast dataset
    output = model.predict(reshaped, batch_size=batch_size)
    # invert data transforms on forecast
    predictions = list()
    for i in range(len(output)):
        yhat = output[i,0]
        # invert scaling
        yhat = invert_scale(scaler, X[i], yhat)
        # invert differencing
        yhat = yhat + raw_data[i]
        # store forecast
        predictions.append(yhat)
    # report performance
    rmse = sqrt(mean_squared_error(raw_data[1:], predictions))
    return rmse
 
# fit an LSTM network to training data
def fit_lstm(train, test, raw, scaler, batch_size, nb_epoch, neurons):
    X, y = train[:, 0:-1], train[:, -1]
    X = X.reshape(X.shape[0], 1, X.shape[1])
    # prepare model
    model = Sequential()
    model.add(LSTM(neurons, batch_input_shape=(batch_size, X.shape[1], X.shape[2]), stateful=True))
    model.add(Dense(1))
    model.compile(loss='mean_squared_error', optimizer='adam')
    # fit model
    train_rmse, test_rmse = list(), list()
    for i in range(nb_epoch):
        model.fit(X, y, epochs=1, batch_size=batch_size, verbose=0, shuffle=False)
        model.reset_states()
        # evaluate model on train data
        raw_train = raw[-(len(train)+len(test)+1):-len(test)]
        train_rmse.append(evaluate(model, raw_train, train, scaler, 0, batch_size))
        model.reset_states()
        # evaluate model on test data
        raw_test = raw[-(len(test)+1):]
        test_rmse.append(evaluate(model, raw_test, test, scaler, 0, batch_size))
        model.reset_states()
    history = DataFrame()
    history['train'], history['test'] = train_rmse, test_rmse
    return history
 
# run diagnostic experiments
def run():
    # load dataset
    series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0]
						 技术沙龙 教程文章 热点综合					

评论