深度学习基础知识-[未完]-神经网络超参数调优

文章介绍了如何利用scikeras库替代过时的KerasRegressor进行模型构建,并使用RandomizedSearchCV进行超参数调优。在示例中,作者展示了如何设置模型结构、定义参数分布,并通过交叉验证找到最佳参数。此外,文章还讨论了隐藏层数量、神经元数量、学习率以及优化器选择对模型性能的影响,并提到了迁移学习和早停法等策略。
摘要由CSDN通过智能技术生成

参考书籍:《Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow, 2nd Edition (Aurelien Geron [Géron, Aurélien])》

代码运行环境:win10 + python 3.9.12 + tensorflow 2.12.0 + cpu

使用RandomizedSearchCV

可以通过先做一次随机的搜索,然后再缩小范围继续搜索。但是这样比较耗时。

from scikeras.wrappers import KerasRegressor

# 模型构建
def build_model(n_hidden=1, n_neurons=30, learning_rate=3e-3, input_shape=[8]):
    model = keras.models.Sequential()
    model.add(keras.layers.InputLayer(input_shape=input_shape))
    for layer in range(n_hidden):
        model.add(keras.layers.Dense(n_neurons, activation="relu"))
    model.add(keras.layers.Dense(1))
    optimizer = keras.optimizers.SGD(lr=learning_rate)
    model.compile(loss="mse", optimizer=optimizer)
    return model

# 下面这句会提示KerasRegressor过时
#keras_reg = keras.wrappers.scikit_learn.KerasRegressor(build_model)
keras_reg = KerasRegressor(build_fn=build_model, n_hidden=1, n_neurons=30, learning_rate=3e-3)

# 这是一种特殊的分布
from scipy.stats import reciprocal
from sklearn.model_selection import RandomizedSearchCV

param_distribs = {
    "n_hidden": [0, 1, 2, 3],
    "n_neurons": np.arange(1, 100),
    "learning_rate": reciprocal(3e-4, 3e-2),
}

rnd_search_cv = RandomizedSearchCV(keras_reg, param_distribs, n_iter=10, cv=3)

rnd_search_cv.fit(X_train, y_train, epochs=100,
                  validation_data=(X_valid, y_valid),
                  callbacks=[keras.callbacks.EarlyStopping(patience=10)])
                  
print(rnd_search_cv.best_params_)
print(rnd_search_cv.best_score_)
'''
{'learning_rate': 0.007527490846816864, 'n_hidden': 2, 'n_neurons': 92}
0.7710459058982116
'''

# 获取模型,随后可以保存
model = rnd_search_cv.best_estimator_.model

报错:DeprecationWarning: KerasRegressor is deprecated, use Sci-Keras(https://github.com/adriangb/scikeras) instead. 并且提示xx参数找不到

先pip安装

pip install scikeras

>>> scikeras.__version__
'0.10.0'

然后修改:(主要是要把build_model里要测试的参数写进KerasRegressor,不然会找不到参数)

keras_reg = KerasRegressor(build_fn=build_model, n_hidden=1, n_neurons=30, learning_rate=3e-3)

其他影响因素

有一些用来调超参数的库:Hyperopt, Hyperas, kopt, Talos, Kera Tuner, Scikit-Optimize(skopt), Spearmint, Hyperband, Sklearn-Deap。google cloud ai平台也提供调优服务。

隐藏层数

一些情况下,使用一个隐藏层就足以得到较好结果了,但是对于很复杂的问题,相比浅层网络,深层网络可以使用更少的神经元(指数级地少),并且效果更好。

Transfer learning:举个例子,我已经训练好一个识别图片中人脸的模型,如果我想再训练一个神经网络以识别发型,那我就可以把识别人脸的网络放在底层,重用参数,然后再加层、训练。

所以对于很多复杂问题来说,一般可以通过加层,直到overfitting,以提高准确率。对于更复杂的问题,一般先找别人训练好的、处理类似问题的网络,再训练,这样可以使用较少的训练数据,并且缩短训练时间。

隐藏层上的神经元数目

一般来说,如果每层神经元数目太少,可能学习的能力会下降。我们也不会给每个隐藏层设计单独的神经元数目,因为这样参数太多了,而且每层神经元数目一样,很多时候表现是相同甚至更好的。通常来说,增加层数比增加每层的神经元数目更能提升性能。

学习率和Optimizer

这俩比较要紧,后面会专门写一篇。

可以从小的学习率(如10^-5)开始,到比较大的(如10),每个跑几百次,然后绘制loss曲线,找比较好的那个。

Batch size等一众超参数会影响学习率。如果调整了其他超参数,应该更新学习率。

Batch size

大的batch size可以充分利用gpu,能缩短训练时间,但可能不稳定,最终模型可能不如小batch size的泛化能力强。一种策略是先用大的batch size(有人用到8192也没有generalization gap),然后用learning rate warmup,如果训练不稳定或者最终结果不好,再用小的batch size。

激活函数

隐藏层用ReLU就挺好,输出层则根据任务决定。

循环数

一般不用调,用early stopping。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Java中,泛型是一种强类型机制,它可以让你在编译时检查类型错误,从而提高代码的安全性和可读性。在使用泛型时,我们经常会遇到父类和子类的泛型转换问题。 首先,我们需要明确一点:子类泛型不能转换成父类泛型。这是因为Java中的泛型是不协变的。例如,如果有一个类A和它的子类B,那么List<A>和List<B>之间是不存在继承关系的。 下面我们来看一个例子: ```java public class Animal { //... } public class Dog extends Animal { //... } public class Test { public static void main(String[] args) { List<Animal> list1 = new ArrayList<>(); List<Dog> list2 = new ArrayList<>(); list1 = list2; // 编译错误 } } ``` 在这个例子中,我们定义了Animal类和它的子类Dog。然后我们定义了两个List,分别是List<Animal>和List<Dog>。如果将List<Dog>赋值给List<Animal>,会出现编译错误。这是因为List<Animal>和List<Dog>之间不存在继承关系。 那么,如果我们想要让子类泛型转换成父类泛型,应该怎么办呢?这时我们可以使用通配符来解决问题。通配符可以表示任意类型,包括父类和子类。例如,我们可以将List<Dog>赋值给List<? extends Animal>,这样就可以实现子类泛型转换成父类泛型了。 下面我们来看一个使用通配符的例子: ```java public class Animal { //... } public class Dog extends Animal { //... } public class Test { public static void main(String[] args) { List<Animal> list1 = new ArrayList<>(); List<Dog> list2 = new ArrayList<>(); list1 = list2; // 编译错误 List<? extends Animal> list3 = new ArrayList<>(); list3 = list2; // 正确 } } ``` 在这个例子中,我们定义了List<? extends Animal>来表示任意继承自Animal的类型。然后我们将List<Dog>赋值给List<? extends Animal>,这样就可以实现子类泛型转换成父类泛型了。 总结一下,Java中的泛型是不协变的,子类泛型不能转换成父类泛型。如果需要实现子类泛型转换成父类泛型,可以使用通配符来解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值