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

Fin*_*ice 6 python scikit-learn keras tensorflow

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

在 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)
Run Code Online (Sandbox Code Playgroud)

大多数这些参数没有使用,但一些相关参数是有 200 次迭代,没有提前停止,一个恒定的学习率,求解器是 SGD nesterovs_momentum=True、 和momentum=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)
Run Code Online (Sandbox Code Playgroud)

我对 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)
Run Code Online (Sandbox Code Playgroud)

为了确保 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))
Run Code Online (Sandbox Code Playgroud)

alpha 参数应该与 scikit-learn 中出现的参数相同。这些定义后面的代码仅在每个 API 使用的方法名称上有所不同。

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

sci-kit learn 和 Keras 的比较

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

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

更新:

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

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

InvalidArgumentError(回溯见上文):Nan 在汇总直方图中:dense_2/bias_0 [[node density_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 失败时,sigmoid/logistic 和 softplus 都具有 f(x)=0 属性。所以我不认为我有输入到无穷大的问题。