Keras(六)keras模型封装转化为sklearn模型、使用超参数搜索

本文将介绍:

  • 超参数搜索简介
  • 手动实现超参数搜索
  • sklearn封装keras模型
  • sklearn实现超参数搜索
  • 实现问题答疑

一,超参数搜索

1,什么是超参数
  • 神经网络有很多训练过程中不变的参数
    • 网络结构参数:层数,每层宽度,每层激活函数等
    • 训练参数:batch_size,学习率,学习率衰减算法等
2,搜索策略
  • 网格搜索
  • 随机搜索
  • 遗传算法搜索
  • 启发式搜索
1)网格搜索
  • 定义n维网格
  • 每个网格对应一组超参数
  • 一组一组尝试参数
    在这里插入图片描述
2)随机搜索
  • 参数的生成方式为随机
  • 可探索的空间更大
    在这里插入图片描述
3)遗传算法搜索
  • 对自然界的模拟
  • A.初始化候选参数集合->训练->得到模型指标作为生存概率
  • B.选择->交叉->变异->产生下一代集合
  • C.重新到A
4)启发式搜索
  • 研究热点-AutoML
  • 使用循环神经网络来生成参数
  • 使用强化学习来进行反馈,使用模型来训练生成参数

二,手动实现超参数搜索

此方法只为了实验,并不推荐使用

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import sklearn
import pandas as pd
import os
import sys
import time
import tensorflow as tf
from tensorflow import keras

# 1,打印使用的python库的版本信息
print(tf.__version__)
print(sys.version_info)
for module in mpl, np, pd, sklearn, tf, keras:
    print(module.__name__, module.__version__)

# 2,下载并使用sklearn中的“fetch_california_housing”数据集
from sklearn.datasets import fetch_california_housing

housing = fetch_california_housing()
print(housing.DESCR)
print(housing.data.shape)
print(housing.target.shape)

# 3,拆分数据集中的数据为 训练数据、验证数据、测试数据
from sklearn.model_selection import train_test_split

x_train_all, x_test, y_train_all, y_test = train_test_split(housing.data, housing.target, random_state = 7)
x_train, x_valid, y_train, y_valid = train_test_split(x_train_all, y_train_all, random_state = 11)
print(x_train.shape, y_train.shape)
print(x_valid.shape, y_valid.shape)
print(x_test.shape, y_test.shape)

# 4,在将数据带入到模型之前,先进行预处理-训练、验证、测试数据标准化
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
x_train_scaled = scaler.fit_transform(x_train)
x_valid_scaled = scaler.transform(x_valid)
x_test_scaled = scaler.transform(x_test)

# 5,构建wide_deep回归模型、模型层级图、编译模型(添加损失函数、优化器)、添加回调函数
# learning_rate: [1e-4, 3e-4, 1e-3, 3e-3, 1e-2, 3e-2]
# W = W + grad * learning_rate

learning_rates = [1e-4, 3e-4, 1e-3, 3e-3, 1e-2, 3e-2]
histories = []
for lr in learning_rates:
    model = keras.models.Sequential([
        keras.layers.Dense(30, activation='relu',
                           input_shape=x_train.shape[1:]),
        keras.layers.Dense(1),
    ])
    optimizer = keras.optimizers.SGD(lr)
    model.compile(loss="mean_squared_error", optimizer=optimizer)
    callbacks = [keras.callbacks.EarlyStopping(
        patience=5, min_delta=1e-2)]
    # 训练构建的模型
    history = model.fit(x_train_scaled, y_train,
                        validation_data = (x_valid_scaled, y_valid),
                        epochs = 100,
                        callbacks = callbacks)
    histories.append(history)
    
# 6,得到训练曲线图
def plot_learning_curves(history):
    pd.DataFrame(history.history).plot(figsize=(8, 5))
    plt.grid(True)
    plt.gca().set_ylim(0, 1)
    plt.show()
for lr, history in zip(learning_rates, histories):
    print("Learning rate: ", lr)
    plot_learning_curves(history)

三,sklearn封装keras模型

1,使用keras内置接口将keras模型转化为sklearn模型

在这里插入图片描述

def build_model(hidden_layers = 1,layer_size = 30,learning_rate = 3e-3):
    model = keras.models.Sequential()
    model.add(keras.layers.Dense(layer_size, activation='relu',input_shape=x_train.shape[1:]))
    for _ in range(hidden_layers - 1):
        model.add(keras.layers.Dense(layer_size,activation = 'relu'))
    model.add(keras.layers.Dense(1))
    optimizer = keras.optimizers.SGD(learning_rate)
    model.compile(loss = 'mse', optimizer = optimizer)
    return model

sklearn_model = keras.wrappers.scikit_learn.KerasRegressor(build_fn = build_model)
callbacks = [keras.callbacks.EarlyStopping(patience=5, min_delta=1e-2)]
2,总结代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import sklearn
import pandas as pd
import os
import sys
import time
import tensorflow as tf
from tensorflow import keras

# 1,打印使用的python库的版本信息
print(tf.__version__)
print(sys.version_info)
for module in mpl, np, pd, sklearn, tf, keras:
    print(module.__name__, module.__version__)
    
# 2,下载并使用sklearn中的“fetch_california_housing”数据集
from sklearn.datasets import fetch_california_housing

housing = fetch_california_housing()
print(housing.DESCR)
print(housing.data.shape)
print(housing.target.shape)

# 3,拆分数据集中的数据为 训练数据、验证数据、测试数据
from sklearn.model_selection import train_test_split

x_train_all, x_test, y_train_all, y_test = train_test_split(housing.data, housing.target, random_state = 7)
x_train, x_valid, y_train, y_valid = train_test_split(x_train_all, y_train_all, random_state = 11)
print(x_train.shape, y_train.shape)
print(x_valid.shape, y_valid.shape)
print(x_test.shape, y_test.shape)

# 4,在将数据带入到模型之前,先进行预处理-训练、验证、测试数据标准化
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
x_train_scaled = scaler.fit_transform(x_train)
x_valid_scaled = scaler.transform(x_valid)
x_test_scaled = scaler.transform(x_test)

# 5,构建模型、模型层级图、编译模型(添加损失函数、优化器)、添加回调函数
# RandomizedSearchCV
# -1-. 转化为sklearn的model
# -2-. 定义参数集合
# -3-. 搜索参数

def build_model(hidden_layers = 1,layer_size = 30,learning_rate = 3e-3):
    model = keras.models.Sequential()
    model.add(keras.layers.Dense(layer_size, activation='relu',input_shape=x_train.shape[1:]))
    for _ in range(hidden_layers - 1):
        model.add(keras.layers.Dense(layer_size,activation = 'relu'))
    model.add(keras.layers.Dense(1))
    optimizer = keras.optimizers.SGD(learning_rate)
    model.compile(loss = 'mse', optimizer = optimizer)
    return model

sklearn_model = keras.wrappers.scikit_learn.KerasRegressor(build_fn = build_model)
callbacks = [keras.callbacks.EarlyStopping(patience=5, min_delta=1e-2)]

# 6,训练构建的模型
history = sklearn_model.fit(x_train_scaled, y_train,
                            epochs = 10,
                            validation_data = (x_valid_scaled, y_valid),
                            callbacks = callbacks)

# 7,得到训练曲线图
def plot_learning_curves(history):
    pd.DataFrame(history.history).plot(figsize=(8, 5))
    plt.grid(True)
    plt.gca().set_ylim(0, 1)
    plt.show()
plot_learning_curves(history)

四,sklearn实现超参数搜索

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import sklearn
import pandas as pd
import os
import sys
import time
import tensorflow as tf
from tensorflow import keras

# 1,打印使用的python库的版本信息
print(tf.__version__)
print(sys.version_info)
for module in mpl, np, pd, sklearn, tf, keras:
    print(module.__name__, module.__version__)
    
# 2,下载并使用sklearn中的“fetch_california_housing”数据集
from sklearn.datasets import fetch_california_housing

housing = fetch_california_housing()
print(housing.DESCR)
print(housing.data.shape)
print(housing.target.shape)

# 3,拆分数据集中的数据为 训练数据、验证数据、测试数据
from sklearn.model_selection import train_test_split

x_train_all, x_test, y_train_all, y_test = train_test_split(housing.data, housing.target, random_state = 7,test_size=0.3)
x_train, x_valid, y_train, y_valid = train_test_split(x_train_all, y_train_all, random_state = 11,test_size=0.3)
print(x_train.shape, y_train.shape)
print(x_valid.shape, y_valid.shape)
print(x_test.shape, y_test.shape)

# 4,在将数据带入到模型之前,先进行预处理-训练、验证、测试数据标准化
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
x_train_scaled = scaler.fit_transform(x_train)
x_valid_scaled = scaler.transform(x_valid)
x_test_scaled = scaler.transform(x_test)

# 5,构建模型、模型层级图、编译模型(添加损失函数、优化器)、添加回调函数
# RandomizedSearchCV
# 1. 转化为sklearn的model
# 2. 定义参数集合
# 3. 搜索参数

def build_model(hidden_layers = 1,layer_size = 30,learning_rate = 3e-2):
    model = keras.models.Sequential()
    model.add(keras.layers.Dense(layer_size, activation='relu',input_shape=x_train.shape[1:]))
    for _ in range(hidden_layers - 1):
        model.add(keras.layers.Dense(layer_size,activation = 'relu'))
    model.add(keras.layers.Dense(1))
    optimizer = keras.optimizers.SGD(learning_rate)
    model.compile(loss = 'mse', optimizer = optimizer)
    return model

sklearn_model = keras.wrappers.scikit_learn.KerasRegressor(build_fn = build_model)

callbacks = [keras.callbacks.EarlyStopping(patience=5, min_delta=1e-2)]

# 6,训练构建的模型
# history = sklearn_model.fit(x_train_scaled, y_train,
#                             epochs = 10,
#                             validation_data = (x_valid_scaled, y_valid),
#                             callbacks = callbacks)

# # 7,得到训练曲线图
# def plot_learning_curves(history):
#     pd.DataFrame(history.history).plot(figsize=(8, 5))
#     plt.grid(True)
#     plt.gca().set_ylim(0, 1)
#     plt.show()
# plot_learning_curves(history)

# 8,使用sk-learn实现网格搜索,交叉验证
from scipy.stats import reciprocal
reciprocal.rvs(1e-4,1e-2,size=10)

from scipy.stats import reciprocal
# f(x) = 1/(x*log(b/a)) a <= x <= b

param_distribution = {
    
    
    "hidden_layers":[ 3, 4],
    "layer_size": np.arange(3, 7),
    "learning_rate": reciprocal(1e-4, 1e-3),
    # "layer_size": [1,2,3,4,5,6,7,8,9,10],
    # "learning_rate": [1e-4, 5e-5, 1e-3, 5e-3, 1e-2],
}

from sklearn.model_selection import RandomizedSearchCV

random_search_cv = RandomizedSearchCV(sklearn_model,  # 调用的模型
                                      param_distribution,# 参数字典
                                      n_iter = 10,  # 输出多少参数集合
                                      cv = 4,   # 指定几折交叉验证
                                      n_jobs = 1)# 多少核的并行计算

random_search_cv.fit(x_train_scaled, y_train, epochs = 5,
                      validation_data = (x_valid_scaled, y_valid),
                      callbacks = callbacks)

# cross_validation: 训练集分成n份,n-1训练,最后一份验证.

# 9,打印最佳
print(random_search_cv.best_params_)    # 最佳参数
print(random_search_cv.best_score_)     # 最佳得分(为-loss)
print(random_search_cv.best_estimator_) # 最佳估计器返回的模型

model = random_search_cv.best_estimator_.model
# print(model.evaluate(x_test_scaled, y_test))
print(model.evaluate(x_test_scaled, y_test,verbose = 0))

另外分享一篇文章:https://zhuanlan.zhihu.com/p/65974369
注意:

  • RandomizedSearchCV 中的参数 “n_iter ”是指定输出多少参数集合
  • RandomizedSearchCV 中的参数 “cv”是指定多少折交叉验证,n-1个测试集,1个验证集
  • 在网格/随机搜索训练中,在每次训练中,训练集为 ( 训练集测试数据 * (cv数 - 1/ cv数)),验证集为(训练集测试数据 * (1/ cv数))
  • 当训练好得到最佳的参数集合后,将所有的训练数据和最佳参数带入模型训练,然后使用x_valid_std去进行验证,总的程序训练的次数也就等于n_iter * cv +1
  • 在tf2.2中,会存每组打印出的测试数据值偏小,使用tf2.1中会恢复正常。

五,实现存在的问题

1,关于RandomizedSearchCV方法中的n_jobs参数不能取非1的值

普通的scikit模型把n_jobs设为大于1的值可以成功,但对于tensorflow的模型来说,把n_jobs设为大于1的数的时候会遇到一个job不能被序列化的问题。此问题暂时还无解。

2,在cross_validation中已经分离出了验证数据,是否还有必要传入validation_data ?

cross_validation用于寻找参数,在找好之后,最后一次会在全部的训练集上训练,在validation_data上验证。

3,在cross_validation每组参数的多次训练中,使用的是最低loss的参数还是最后的参数?

最后将使用每组参数中最后一个epoch的参数验证。

4,当param_distribution 中使用np.arange或reciprocal会报错

可能是新版本tensorflow中的KerasRegressor的问题,新版本的参数在做deep copy的时候出现了问题,导致在拷贝复杂的numpy对象的时候出错。因为deep copy这个操作对用户不可见,所以结论是,退回到sklearn 0.21.3版本,或者在param_distribution中使用普通列表。

5,tensorflow是否可以进行超参数搜索呢?

tensorflow中目前没有这样的库实现,需要自己自行实现。AutoML还是很多其他的复杂算法,不仅仅是超参数搜索,还包括模型结构搜索。这些算法都需要一一实现,目前还没有可用的比较完整的算法库。

6,best_score_为什么是负值?

因为在实现中,搜索的时候会调用KerasRegressor的score函数,而这个函数的实现是返回-loss,而mse(均方误差)是正数,所以score就成了负数。之所以用负数,是因为搜索的过程中希望score越高越好,而mse是越低越好,所以为了统一,就返回负数。

7,为什么RandomizedSearchCV在fit的过程中,在验证穿进去的x_valid_std和y_valid的时候会出现很多的"======"?

可能是版本的原因,有一版(我记得是2.0.0)上会有很多等号,可以用verbose = 0来设置不输出进度。我更新到2.1.0没有发现类似问题。

猜你喜欢

转载自blog.csdn.net/TFATS/article/details/110191447
今日推荐