You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用sklearn的MLPClassifier与skopt优化神经网络层数及隐藏层大小

嘿,这个问题问到点子上了——用skopt来调MLP的隐藏层结构确实比手动试高效多了,我来给你拆解下具体怎么做。

核心思路

首先得明确:hidden_layer_sizes是一个元组,长度对应隐藏层的层数,每个元素是对应层的神经元数量。所以我们要优化的其实是两个维度:隐藏层的层数每层的神经元数。skopt的Space可以通过组合不同的空间类型来实现这种结构化参数的搜索。

两种实用方案

方案1:灵活搜索层数+每层神经元数(推荐)

这种方式适合你想让skopt自动探索不同层数(比如1-3层)和每层神经元数范围的场景,步骤如下:

1. 定义搜索空间

我们先定义层数的范围,再为最多可能的层数定义每层的神经元数范围,最后组合成空间:

from skopt import Space
from skopt.space import Integer, Tuple, Real

space = Space([
    # 搜索1到3层隐藏层(可根据需求调整范围)
    Integer(low=1, high=3, name='n_hidden_layers'),
    # 为最多3层分别定义神经元数范围(比如32-256)
    Tuple(
        Integer(low=32, high=256, name='layer1_size'),
        Integer(low=32, high=256, name='layer2_size'),
        Integer(low=32, high=256, name='layer3_size')
    ),
    # 你原来要调的其他参数,比如alpha
    Real(10**-5, 10**0, "log-uniform", name='alpha')
])

2. 在目标函数中组装参数

拿到skopt给出的参数后,我们需要根据层数截取对应的神经元数,组装成hidden_layer_sizes元组:

from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import cross_val_score

def objective(params):
    n_hidden_layers, layer_sizes, alpha = params
    # 根据实际层数截取元组,比如层数为2时,取前2个元素
    hidden_layer_sizes = layer_sizes[:n_hidden_layers]
    
    # 初始化MLP模型
    mlp = MLPClassifier(
        hidden_layer_sizes=hidden_layer_sizes,
        alpha=alpha,
        activation='relu',
        solver='adam',
        max_iter=1000,
        random_state=42
    )
    
    # 用交叉验证评估模型性能,返回负分(因为skopt默认最小化目标)
    return -cross_val_score(mlp, X, y, cv=5).mean()

方案2:枚举候选隐藏层结构(简单直接)

如果你已经有一些经验性的候选结构(比如常见的(64,)、(128,64)、(256,128,64)),可以直接用Categorical枚举,省去层数判断的步骤:

from skopt.space import Categorical

space = Space([
    Categorical([(32,), (64,), (128,), (64,32), (128,64), (256,128,64)], name='hidden_layer_sizes'),
    Real(10**-5, 10**0, "log-uniform", name='alpha')
])

这种情况下,目标函数可以直接用这个参数,不用额外处理,非常直观。

完整运行示例

这里给一个可直接跑的小例子,用鸢尾花数据集演示:

from sklearn.neural_network import MLPClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import cross_val_score
from skopt import gp_minimize
from skopt.space import Integer, Tuple, Real
from skopt.utils import use_named_args

# 加载数据集
X, y = load_iris(return_X_y=True)

# 定义搜索空间
space = [
    Integer(low=1, high=3, name='n_hidden_layers'),
    Tuple(
        Integer(low=32, high=256, name='layer1'),
        Integer(low=32, high=256, name='layer2'),
        Integer(low=32, high=256, name='layer3')
    ),
    Real(1e-5, 1e0, "log-uniform", name='alpha')
]

# 用use_named_args让参数更清晰
@use_named_args(space)
def objective(n_hidden_layers, layer1, layer2, layer3, alpha):
    # 组装隐藏层结构
    layers = [layer1, layer2, layer3][:n_hidden_layers]
    hidden_layer_sizes = tuple(layers)
    
    # 初始化模型
    mlp = MLPClassifier(
        hidden_layer_sizes=hidden_layer_sizes,
        alpha=alpha,
        activation='relu',
        solver='adam',
        max_iter=1000,
        random_state=42
    )
    
    # 计算交叉验证得分
    score = cross_val_score(mlp, X, y, cv=5).mean()
    return -score

# 运行贝叶斯优化
result = gp_minimize(
    objective,
    space,
    n_calls=20,  # 迭代次数,可根据需求调整
    random_state=42
)

# 输出最优结果
print("最优参数:")
print(f"隐藏层结构: {tuple([result.x[1][i] for i in range(result.x[0])])}")
print(f"alpha值: {result.x[2]}")
print(f"最优交叉验证得分: {-result.fun:.4f}")
实用小技巧
  • 神经元数建议用2的幂次(比如32、64、128),可以让计算更高效,你可以把Integer换成Categorical([32,64,128,256])来限定候选值。
  • 如果想让隐藏层神经元数递减(比如第一层多,后面层少,这是常见的特征压缩思路),可以在目标函数里对layers排序:hidden_layer_sizes = tuple(sorted(layers, reverse=True))
  • 若想用对数尺度搜索神经元数,可以把Integer换成Real,然后在目标函数里取幂:比如Real(5, 8, name='layer1_log'),然后layer1_size = 2**layer1_log(这样得到32到256的数)。

内容的提问来源于stack exchange,提问作者Marco Fumagalli

火山引擎 最新活动