【问题标题】:Keras parameter search and log lossKeras 参数搜索和日志丢失
【发布时间】:2018-07-06 08:02:37
【问题描述】:

我正在为一个类不是独占的多类问题构建一个 Keras 简单分类器模型。另外,我想添加带有交叉验证的 sklearn 详尽参数搜索。这是简单的模型:

def build_model(embedding_size):
    model = Sequential()
    model.add(Embedding(10000, embedding_size, input_length=100))
    model.add(Flatten())
    model.add(Dense(100, activation="relu"))
    model.add(Dense(10, activation="sigmoid"))
    model.compile(loss="binary_crossentropy", optimizer=Adam(), metrics=["accuracy"])
    return model

batch_size = 64
epochs = 2

这是我的第一个手动交叉验证解决方案(没有参数搜索)。

embedding_size=32

skf = KFold(n_splits=3, shuffle=True)
valid_evals = []
for train_indices in skf.split(train_full_x):
    kfold_train_x = train_full_x[train_indices[0]]
    kfold_valid_x = train_full_x[train_indices[1]]
    kfold_train_y = train_full_y[train_indices[0]]
    kfold_valid_y = train_full_y[train_indices[1]]
    model = build_model(embedding_size)
    model.fit(kfold_train_x, kfold_train_y, batch_size=batch_size, epochs=epochs)
    valid_evals.append(model.evaluate(kfold_valid_x, kfold_valid_y))

valid_evals = np.array(valid_evals)
print(valid_evals)
print("LogLoss: %.4f +/- %.4f" % (valid_evals.mean(axis=0)[0], valid_evals.std(axis=0)[0]))
print("Accuracy: %.2f%% +/- %.2f%%" % (valid_evals.mean(axis=0)[1] * 100, valid_evals.std(axis=0)[1] * 100))

结果相当一致:

[[0.05730336 0.98051361]
 [0.0606665  0.98065738]
 [0.05717109 0.9801999 ]]
LogLoss: 0.0584 +/- 0.0016
Accuracy: 98.05% +/- 0.02%

此代码运行良好,返回的验证准确度约为 98%,日志损失约为 0.06。然后我尝试添加GridSearchCV,而不是滚动我自己的参数搜索解决方案(在下面的示例中,搜索空间被简单地减少为一种选择)。

embedding_size = [32]
classifier = KerasClassifier(build_fn=build_model, epochs=epochs, batch_size=batch_size)
kfold = KFold(n_splits=3, shuffle=True)
param_grid = dict(embedding_size=embedding_size)
grid = GridSearchCV(estimator=classifier, param_grid=param_grid, return_train_score=True, cv=kfold)
results = grid.fit(train_x, train_y)
print("Best: %f using %s" % (results.best_score_, results.best_params_))
print(results.cv_results_)

这会返回

Best: 0.980600 using {'embedding_size': 32}
0.980600 (0.000187) with: {'embedding_size': 32}
{'split2_train_score': array([0.9882294]), 'mean_train_score': array([0.98768258]), 'split0_train_score': array([0.98707933]), 'split1_train_score': array([0.98773903]), 'std_train_score': array([0.00047121]), 'mean_fit_time': array([8.7320834]), 'split1_test_score': array([0.98079212]), 'split0_test_score': array([0.98066088]), 'std_fit_time': array([0.19671295]), 'rank_test_score': array([1], dtype=int32), 'std_test_score': array([0.00018667]), 'param_embedding_size': masked_array(data=[32], mask=[False], fill_value='?', dtype=object), 'std_score_time': array([0.00984431]), 'split2_test_score': array([0.98034717]), 'mean_score_time': array([0.58589784]), 'params': [{'embedding_size': 32}], 'mean_test_score': array([0.98060006])}

在这里,我得到了与 Keras 兼容的准确度,手动拆分训练/验证。但是,在我的问题中,相关的评分函数是日志丢失,所以我尝试将scoring="neg_log_loss" 作为参数添加到GridSearchCV但在这种情况下,我得到的分数类似于-0.29

Best: -0.292518 using {'embedding_size': 32}
-0.292518 (0.002988) with: {'embedding_size': 32}
{'split1_train_score': array([-0.27363595]), 'std_score_time': array([0.01245312]), 'rank_test_score': array([1], dtype=int32), 'mean_test_score': array([-0.29251778]), 'std_fit_time': array([0.17412529]), 'split0_train_score': array([-0.27816725]), 'split1_test_score': array([-0.28917072]), 'mean_score_time': array([0.40924891]), 'params': [{'embedding_size': 32}], 'split0_test_score': array([-0.29195843]), 'split2_test_score': array([-0.2964242]), 'split2_train_score': array([-0.26628012]), 'mean_fit_time': array([8.75646718]), 'mean_train_score': array([-0.27269444]), 'std_test_score': array([0.00298751]), 'std_train_score': array([0.00489836]), 'param_embedding_size': masked_array(data=[32], mask=[False], fill_value='?', dtype=object)}

我预计符号会有所不同(由于neg_log_loss 的工作方式),但价值不会。

我做错了什么?或者...还有其他方法可以让GridSearchCV 处理交叉验证和日志丢失吗?

谢谢

注意我没有设置随机种子,而是多次运行这两个示例,因此随机性不应该是显示差异的原因。

【问题讨论】:

  • 你得到负损失的事实让我认为 GridSearchCV 将 train_y 视为分类数字而不是稀疏/一个热门......
  • 使用neg_log_loss 的负日志丢失似乎to be expected。 train_y 的形式为[[0, 0, 1, ...., 1, 0], [0, 1, 0, ...., 0, 1], ...]
  • 不存在“二进制多类”问题:“二进制”是指类的数量,而不是它们的编码方式;你的是一个多类多标签的
  • 你确定 Keras 返回的 0.05 值是指 validation(而不是训练)错误吗?您没有在代码中显示这部分(我们也不会无缘无故要求minimum verifiable example)。
  • 你好@desertnaut,我用这两种情况的代码扩展了我的答案并更正了问题分类。

标签: python machine-learning scikit-learn neural-network keras


【解决方案1】:

我认为您的问题是您省略了预测步骤。 Logloss 只需要 y_test 和 y_predict 并且为了得到 y_predict 你需要调用 predict 方法。因此,当我将 GridSearchCV 与 knn 分类器一起使用时,这对我有用。 GridSearchCV 被实例化为 cv。

cv.fit(X_train, y_train)
y_predict = cv.predict(X_test)   # grid.predict(X_test) in your case

from sklearn.metrics import log_loss
print('Log_loss: {}'.format(log_loss(y_test, y_predict)))

加法:

我一直在使用一个相当不准确的 knn/GridSearchCV 分类器,并且已经意识到一些可能相关的事情:

  1. 如果我在不带和带 score='neg_log_loss' 的情况下运行 cv 并按照我上面建议的那样独立计算 log_loss,则后者的结果是不同的 - 分别为 6.536 和 6.716。因此,当优化基于优化 log_loss 而不是优化准确性时,它会更高。显然,每个优化条件的标签向量略有不同。这可能并不令人惊讶,并且正如您所注意到的那样,差异并不大。

  2. 由于您使用的是 return_train_score=True 并且所有 cv 步骤的结果都存储在 .cv_results_ 中,因此很容易可视化训练和测试分数。显然,评分用 mean_test 和 mean_train 评分列中的 log loss 代替了准确率,如下所示。

【讨论】:

  • 我正在使用 GridSearchCV 对我的模型的不同变体进行参数搜索。在fit 方法之后,我期望有一系列与损失相关的分数,所以我可以检查一些选项是否有希望。使用predict 让我不知道不同参数的行为方式,因为它已经在使用最佳估计器,重新调整之前计算的fit 分数。
  • 那么你需要在每次参数更改时以某种方式调用 predict 。我没有这样做,也无法提供现成的解决方案。可能有一种使用嵌套管道的方法。实现这一点的不太优雅但确定的方法是遍历字典并将其传递给 GridSearchCV 或者只是 kerasClassifier 一个更有限的参数切片,在每次拟合后进行预测并将 logloss 添加到字典中。
  • 我很困惑......如果这是正确的方法,那么使用 GridSearchCV 而不是使用手动 cross_validation + 参数搜索有什么意义......?
  • 再次阅读您的问题,我意识到它有两个部分。首先,尽管精度相似,但对数损失值的差异。对数损失会惩罚更多自信和错误的标签,因此尽管准确度相似,但对数损失具有不同的值似乎是合理的。第二部分是找到每个网格搜索点的对数损失。调用grid.predict()后,能否识别出pd.DataFrame(grid.cv_results_)列中的log-loss?
  • 我简化了问题,将参数搜索简单地减少到一个点,这样您就可以忽略分析的第二部分。您可以在更新的答案中找到这两个示例的结果。在此感谢。
猜你喜欢
  • 2016-07-05
  • 2016-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-03
  • 2018-09-04
  • 1970-01-01
  • 2011-11-22
相关资源
最近更新 更多