解决简单全连接神经网络的 Keras 和 scikit-learn 之间的差异

Resolving differences between Keras and scikit-learn for simple fully-connected neural network

提问人:Finncent Price 提问时间:12/5/2018 最后编辑:Finncent Price 更新时间:9/12/2023 访问量:2504

问:

我已经使用 TensorFlow 后端 (v 1.12.0) 在 scikit-learn (v 0.20.0) 和 Keras (v 2.2.4) 中构建了一个全连接的神经网络。单个隐藏层中有 10 个单元。在这两种情况下,我都通过调用 scikit-learn 的 train_test_split 函数来选择训练和测试数据,并将其设置为 0。然后,它们都使用 scikit-learn 的 .事实上,到目前为止,每种情况的代码实际上都是相同的。random_stateStandardScaler

在 scikit-learn 中,我使用 MLPRegressor 定义神经网络。该函数调用的输出

MLPRegressor(activation='logistic', alpha=1.0, batch_size='auto', beta_1=0.9,
   beta_2=0.999, early_stopping=False, epsilon=1e-08,
   hidden_layer_sizes=(10,), learning_rate='constant',
   learning_rate_init=0.001, max_iter=200, momentum=0.9,
   n_iter_no_change=10, nesterovs_momentum=True, power_t=0.5,
   random_state=None, shuffle=True, solver='sgd', tol=0.0001,
   validation_fraction=0.2, verbose=False, warm_start=False)

这些参数中的大多数都没有使用,但一些相关参数是有 200 次迭代、没有提前停止、恒定的学习率、求解器是 SGD 和 。nesterovs_momentum=Truemomentum=0.9

Keras 中的定义是(称之为 Keras 1)

mlp = Sequential() # create a sequential neural network using Keras
mlp.add(Dense(units=10,activation='sigmoid',input_dim=X.shape[1],
          kernel_regularizer=skl_norm))
mlp.add(Dense(units=1,activation='linear'))
opt = optimizers.SGD(lr=0.001,momentum=0.9,decay=0.0,nesterov=True)
mlp.compile(optimizer=opt,loss='mean_squared_error')
mlp.fit(X_train,y_train,batch_size=200,epochs=200,verbose=0)

我对 Keras 的理解是,这应该与 scikit-learn 网络相同,但有一个可能的例外,scikit-learn 应该正则化层之间的所有权重,而这个 Keras 网络只正则化从输入层进入隐藏层的权重。我可以通过以下方式将隐藏层的权重正则化添加到输出层(称之为 Keras 2)

mlp = Sequential() # create a sequential neural network using Keras
mlp.add(Dense(units=10,activation='sigmoid',input_dim=X.shape[1],
          kernel_regularizer=skl_norm))
mlp.add(Dense(units=1,activation='linear',kernel_regularizer=skl_norm))
opt = optimizers.SGD(lr=0.001,momentum=0.9,decay=0.0,nesterov=True)
mlp.compile(optimizer=opt,loss='mean_squared_error')
mlp.fit(X_train,y_train,batch_size=200,epochs=200,verbose=0)

为了确保 Keras 中的正则化与 scikit-learn 中的正则化相匹配,我在 Keras 中实现了一个自定义正则化函数:

def skl_norm(weight_matrix):
    alpha = 1.0 # to match parameter I used in sci-kit learn
    return alpha * 0.5 * K.sum(K.square(weight_matrix))

其中 alpha 参数与 scikit-learn 中显示的参数相同。遵循这些定义的代码仅在每个 API 使用的方法名称上有所不同。

我的结果表明,两个 API 中的正则化并不相同,或者更有可能的是,我在 Keras 中的实现不是我认为的那样。以下是神经网络输出之间的比较:

Comparison between sci-kit learn and Keras

顶行是 alpha = 0,底行是 alpha = 1.0。左列是 scikit-learn,中间列是 Keras 1,右列是 Keras 2。与其讨论图之间的所有差异,不如立即让我想到的是,当正则化被“关闭”(alpha=0)时,拟合非常相似。当正则化被“打开”(alpha=1)时,scikit-learn的性能优于Keras,尤其是当隐藏层的输出被正则化时,scikit-learn的性能优于Keras 2。

在不同的运行中,R^2 值略有不同,但不足以解释底行的差异。那么,这两种网络实现之间有什么区别呢?

更新:

从那以后我发现,如果我在 Keras 中使用“无界”激活函数,训练将完全失败,所有预测都返回 nan,而在 scikit-learn 中则很好。我所说的“无界”是指允许输出无穷大值的激活,例如 linear/identity、softplus 或 relu。

当我打开 TensorBoard 回调时,我收到一个错误,以(编辑以省略不相关的潜在敏感信息)结尾:

InvalidArgumentError(有关回溯,请参阅上文):Nan 在摘要直方图中:dense_2/bias_0 [[node dense_2/bias_0(在 /Users/.../python2.7/site-packages/keras/callbacks.py:796 定义) = HistogramSummary[T=DT_FLOAT, _device=“/job:localhost/replica:0/task:0/device:CPU:0”](dense_2/bias_0/tag, dense_2/bias/read)]]

基于这个错误,我猜第二层的偏置单元变得非常大,但我不知道为什么这会发生在 Keras/TF 而不是 scikit-learn 中。

由于 softplus 在 x=0 时没有 f(x)=0 的属性,因此我认为问题不在于输入几乎为零。此外,tanh 激活效果非常好。所以我认为我没有遇到输入聚类接近零的问题。当 x->-infinity 时,sigmoid/logistic 和 softplus 都具有 f(x)=0 属性,并且 sigmoid/logistic 在 softplus 失败时工作良好。所以我认为我没有输入到-infinity的问题。

python tensorflow scikit-learn keras

评论

0赞 SuperCodeBrah 5/27/2022
我刚刚在这里发布了一个问题:github.com/scikit-learn/scikit-learn/discussions/23475,所以也许在sklearn上工作的人会回答。
0赞 venkata krishnan 1/4/2023
使用什么初始化器?
0赞 Finncent Price 1/5/2023
使用了默认的初始值设定项。但我怀疑这很重要,因为当我不使用正则化时,我会得到相同的答案。
0赞 The Guy with The Hat 6/16/2023
Sklearn 的默认正则化除以批量大小。Keras 的活动正则化也是如此,但从源代码来看,Keras 的内核正则化似乎没有。这能解释这个问题吗?
0赞 Greg7000 7/20/2023
默认情况下,Keras 为 float32。也许你的 Keras run 是 float32,而你的 scikit-learn 是 float64?确保两种技术使用相同的浮点类型。您能确认跑步中使用的类型吗?

答: 暂无答案