网格搜索法调参神经网络

网格搜索法调参神经网络

通过包装 Keras 模型来使用 scikit-learn,并学习怎么使用网格搜索(grid search) - 怎样网格搜索通用神经网络参数,如学习速率(learning rate),丢弃率(dropout rate),迭代次数(epoches)及其神经元的个数 - 在自己的项目上怎么定义超参调节实验。

通过 Keras 提供的 KerasClassifierKerasRegressor 类,将 Keras 模型包装起来就可以在 scikit-learn 中使用了。KerasClassifier 的构造器可以接收默认参数,这些参数被传递给调用model.fit(),如迭代的次数、批次的大小。例如:

def create_model():
    ...
    return model
model = KerasClassifier(build_fn=create_model, epochs=10)

KerasClassifier 的构造器也可以接收新的参数,它可以被传递给你自己定义的create_model()函数。这些新的参数必须定义在你的create_model()函数的签名通过默认参数。例如:

def create_model(dropout_rate=0.0):
    ...
    return model
model = KerasClassifier(build_fn=create_model, dropout_rate=0.2)
如何使用 scikit-learn 中网格搜索

构建这个类的时候,你必须提供超参的字典来评估param_grid参数。他是模型参数名字与一组用于尝试值的映射。默认情况下,accuracy 是被用于优化的分数,但通过 GridSearchCV 构造器的 score 参数,可以指定其他的得分评估方法。

默认情况下,网格搜索只是用一个线程,通过将 GridSearchCV 构造器的 n_jobs 参数设置为 -1,进程会使用你机器上的所有内核。依据不 Keras 的后端,这可能会影响神经网络的训练进程。

GridSearchCV 进程会为每一个参数组合构建并训练一个模型。交叉验证(Cross validation)被用于评估每一个独立的模型,同时默认的3折交叉验证(3-fold cross validation)被使用,尽管通过 GridSearchCV构造器的 cv 参数可以重新指定他的值。

param_grid = dict(epochs=[10,20,30])
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=-1)
grid_result = grid.fit(X, Y)

数据:pima-indians-diabetes.csv

调节batch size 与 epochs

batch size 是权重更新前输入网络的样本数。在训练网络时他也是一个优化,他决定了一次有多少样本读入到内存中。epochs 数是在训练期间,整个训练数据集输入到网络的次数。

import numpy as np
from sklearn.model_selection import GridSearchCV
from keras.models import Sequential
from keras.layers import Dense
from keras.wrappers.scikit_learn import KerasClassifier
# 构建模型的函数
def create_model():
    # 创建模型
    model = Sequential()
    model.add(Dense(12, input_dim=8, activation='relu'))
    model.add(Dense(1, activation='sigmoid'))
    # 编译模型
    model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
    return model
def print_res(grid_result):
    print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
    means = grid_result.cv_results_['mean_test_score']
    stds = grid_result.cv_results_['std_test_score']
    params = grid_result.cv_results_['params']
    for mean, stdev, param in zip(means, stds, params):
        print("%f (%f) with: %r" % (mean, stdev, param))
# 为了复现,设置随机种子
seed = 7
np.random.seed(seed)

# 加载数据
dataset = np.loadtxt("pima-indians-diabetes.csv", delimiter=",")
# 切分数据为输入 X 和输出 Y
X = dataset[:,0:8]
Y = dataset[:,8]

# 创建模型
model = KerasClassifier(build_fn=create_model, verbose=0)
# 定义网格搜索参数
batch_size = [10, 20, 40, 60, 80, 100]
epochs = [10, 50, 100]
param_grid = dict(batch_size=batch_size, epochs=epochs)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=-1)
grid_result = grid.fit(X, Y)

# 总结结果
print_res(grid_result)
调节优化算法
# 构建模型的函数
def create_model(optimizer='adam'):
    # 创建模型
    model = Sequential()
    model.add(Dense(12, input_dim=8, activation='relu'))
    model.add(Dense(1, activation='sigmoid'))

    # 编译模型
    model.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=['accuracy'])
    return model
def print_res(grid_result):
    print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
    means = grid_result.cv_results_['mean_test_score']
    stds = grid_result.cv_results_['std_test_score']
    params = grid_result.cv_results_['params']
    for mean, stdev, param in zip(means, stds, params):
        print("%f (%f) with: %r" % (mean, stdev, param))
# 为了复现,设置随机种子
seed = 7
np.random.seed(seed)

# 加载数据
dataset = np.loadtxt("pima-indians-diabetes.csv", delimiter=",")
# 切分数据为输入 X 和输出 Y
X = dataset[:,0:8]
Y = dataset[:,8]

# 创建模型,使用到了上一步找出的 epochs、batch size 最优参数
model = KerasClassifier(build_fn=create_model, epochs=100, batch_size=80, verbose=0)
# 定义网格搜索参数
optimizer = ['SGD', 'RMSprop', 'Adagrad', 'Adadelta', 'Adam', 'Adamax', 'Nadam']
param_grid = dict(optimizer=optimizer)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=1)
grid_result = grid.fit(X, Y)

# 总结结果
print_res(grid_result)
调节Learning Rate与Momentum
from keras.optimizers import SGD
# 构建模型的函数
def create_model(learn_rate=0.01, momentum=0):
    # 创建模型
    model = Sequential()
    model.add(Dense(12, input_dim=8, activation='relu'))
    model.add(Dense(1, activation='sigmoid'))

    # 编译模型
    optimizer = SGD(lr=learn_rate, momentum=momentum)
    model.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=['accuracy'])
    return model
def print_res(grid_result):
    print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
    means = grid_result.cv_results_['mean_test_score']
    stds = grid_result.cv_results_['std_test_score']
    params = grid_result.cv_results_['params']
    for mean, stdev, param in zip(means, stds, params):
        print("%f (%f) with: %r" % (mean, stdev, param))
# 为了复现,设置随机种子
seed = 7
np.random.seed(seed)

# 加载数据
dataset = np.loadtxt("pima-indians-diabetes.csv", delimiter=",")
# 切分数据为输入 X 和输出 Y
X = dataset[:,0:8]
Y = dataset[:,8]

# 创建模型,使用到了上一步找出的 epochs、batch size 最优参数
model = KerasClassifier(build_fn=create_model, epochs=100, batch_size=80, verbose=0)
# 定义网格搜索参数
learn_rate = [0.001, 0.01, 0.1, 0.2, 0.3]
momentum = [0.0, 0.2, 0.4, 0.6, 0.8, 0.9]
param_grid = dict(learn_rate=learn_rate, momentum=momentum)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=1)
grid_result = grid.fit(X, Y)

# 总结结果
print_res(grid_result)
调节权重初始化

神经网络权重初始化被简单的运用:使用较小的随机值。 现在可以从 Keras 提供的一组列表来选择不同的初始化技术。

# 构建模型的函数
def create_model(init_mode='uniform'):
    # 创建模型
    model = Sequential()
    model.add(Dense(12, input_dim=8, kernel_initializer=init_mode, activation='relu'))
    model.add(Dense(1, kernel_initializer=init_mode, activation='sigmoid'))

    # 编译模型
    model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
    return model

# 为了复现,设置随机种子
seed = 7
np.random.seed(seed)

# 加载数据
dataset = np.loadtxt("pima-indians-diabetes.csv", delimiter=",")
# 切分数据为输入 X 和输出 Y
X = dataset[:,0:8]
Y = dataset[:,8]

# 创建模型,使用到了上一步找出的 epochs、batch size 最优参数
model = KerasClassifier(build_fn=create_model, epochs=100, batch_size=80, verbose=0)
# 定义网格搜索参数
init_mode = ['uniform', 'lecun_uniform', 'normal', 'zero', 'glorot_normal', 
             'glorot_uniform', 'he_normal', 'he_uniform']
param_grid = dict(init_mode=init_mode)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=1)
grid_result = grid.fit(X, Y)

# 总结结果
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
    print("%f (%f) with: %r" % (mean, stdev, param))
激活函数

激活函数控制着神经元的非线性在激活时。通常,relu 激活函数是最经常使用的,但对于不同的问题,sigmoid 和 tanh 这些函数可能更合适。

# 构建模型的函数
def create_model(activation='relu'):
    # 创建模型
    model = Sequential()
    model.add(Dense(12, input_dim=8, kernel_initializer='uniform', activation=activation))
    model.add(Dense(1, kernel_initializer='uniform', activation='sigmoid'))

    # 编译模型
    model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
    return model

# 为了复现,设置随机种子
seed = 7
np.random.seed(seed)

# 加载数据
dataset = np.loadtxt("pima-indians-diabetes.csv", delimiter=",")
# 切分数据为输入 X 和输出 Y
X = dataset[:,0:8]
Y = dataset[:,8]

# 创建模型,使用到了上一步找出的 epochs、batch size 最优参数
model = KerasClassifier(build_fn=create_model, epochs=100, batch_size=80, verbose=0)
# 定义网格搜索参数
activation = ['softmax', 'softplus', 'softsign', 'relu', 
              'tanh', 'sigmoid', 'hard_sigmoid', 'linear']
param_grid=dict(activation=activation)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=1)
grid_result = grid.fit(X, Y)

# 总结结果
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
    print("%f (%f) with: %r" % (mean, stdev, param))
调节正则化参数Dropout
from keras.layers import Dropout
from keras.constraints import maxnorm
# 构建模型的函数
def create_model(dropout_rate=0.0, weight_constraint=0):
    # 创建模型
    model = Sequential()
    model.add(Dense(12, input_dim=8, kernel_initializer='uniform', activation='relu'))
    model.add(Dropout(dropout_rate))
    model.add(Dense(1, kernel_initializer='uniform', activation='sigmoid'))

    # 编译模型
    model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
    return model

# 为了复现,设置随机种子
seed = 7
np.random.seed(seed)

# 加载数据
dataset = np.loadtxt("pima-indians-diabetes.csv", delimiter=",")
# 切分数据为输入 X 和输出 Y
X = dataset[:,0:8]
Y = dataset[:,8]

# 创建模型,使用到了上一步找出的 epochs、batch size 最优参数
model = KerasClassifier(build_fn=create_model, epochs=100, batch_size=80, verbose=0)
# 定义网格搜索参数
weight_constraint = [1, 2, 3, 4, 5]
dropout_rate = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
param_grid = dict(weight_constraint=weight_constraint, dropout_rate=dropout_rate)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=1)
grid_result = grid.fit(X, Y)

# 总结结果
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
    print("%f (%f) with: %r" % (mean, stdev, param))
调节隐藏层的神经元数
from keras.layers import Dropout
from keras.constraints import maxnorm
# 构建模型的函数
def create_model(neurons=1):
    # 创建模型
    model = Sequential()
    model.add(Dense(neurons, input_dim=8, kernel_initializer='uniform', activation='relu'))
    model.add(Dropout(0.2))
    model.add(Dense(1, kernel_initializer='uniform', activation='sigmoid'))

    # 编译模型
    model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
    return model

# 为了复现,设置随机种子
seed = 7
np.random.seed(seed)

# 加载数据
dataset = np.loadtxt("pima-indians-diabetes.csv", delimiter=",")
# 切分数据为输入 X 和输出 Y
X = dataset[:,0:8]
Y = dataset[:,8]

# 创建模型,使用到了上一步找出的 epochs、batch size 最优参数
model = KerasClassifier(build_fn=create_model, epochs=100, batch_size=80, verbose=0)
# 定义网格搜索参数
neurons = [1, 5, 10, 15, 20, 25, 30]
param_grid = dict(neurons=neurons)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=1)
grid_result = grid.fit(X, Y)

# 总结结果
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
    print("%f (%f) with: %r" % (mean, stdev, param))
超参优化技巧

这一张列出了一些遍历的技巧当你调节你的神经网络超参时。

  • k 折交叉验证:这篇文章的案例中,你可以看到结果中展示了一些方差。默认的 3 折交叉验证被使用,但也许 k=5 或者 k=10会是的结果更加稳定。小心的选择你的交叉验证配置以确保你的结果是稳定的。
  • 检查整个网格:不要只盯着最优的结果,检查整个网格的结果,寻找支持配置决策的趋势
  • 并行:如果可能使用全部的内核,神经网络训练很慢,通常我们又会想尝试很多不同的参数。
  • 使用数据的一个采样:因为神经网络训练很慢,尝试在训练集的小采样上训练他们,仅仅是获得一般的参数方向而不是优化的配置
  • 从粗糙的网格开始:从粗略的网格开始,一旦缩小了范围,就可以缩放到细粒度上。
  • 不要转换结果:结果通常针对特定的问题。试着避免偏好配置在每一个新的问题上。这不像你在一个问题上发现的最优结果可以转换到你的下一个项目上。相反是寻找更广阔的趋势,像层数或者参数之间的关系
  • 问题的重现:虽然我们设置了种子为 numpy 中的随机数生成器,但结果并不是可以百分百的复现。当网格搜索包含 Keras 模型时,复现较比文中呈现的更容易。

你可能感兴趣的:(神经网络,深度学习,keras)