【问题标题】:Can I send callbacks to a KerasClassifier?我可以向 KerasClassifier 发送回调吗?
【发布时间】:2017-07-18 11:36:50
【问题描述】:

如果耐心达到我设置的数字,我希望分类器运行得更快并提前停止。在下面的代码中,它会进行 10 次迭代来拟合模型。

import numpy
import pandas
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.wrappers.scikit_learn import KerasClassifier
from keras.callbacks import EarlyStopping, ModelCheckpoint
from keras.constraints import maxnorm
from keras.optimizers import SGD
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import StratifiedKFold
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
# fix random seed for reproducibility
seed = 7
numpy.random.seed(seed)
# load dataset
dataframe = pandas.read_csv("sonar.csv", header=None)
dataset = dataframe.values
# split into input (X) and output (Y) variables
X = dataset[:,0:60].astype(float)
Y = dataset[:,60]
# encode class values as integers
encoder = LabelEncoder()
encoder.fit(Y)
encoded_Y = encoder.transform(Y)

calls=[EarlyStopping(monitor='acc', patience=10), ModelCheckpoint('C:/Users/Nick/Data Science/model', monitor='acc', save_best_only=True, mode='auto', period=1)]

def create_baseline(): 
    # create model
    model = Sequential()
    model.add(Dropout(0.2, input_shape=(33,)))
    model.add(Dense(33, init='normal', activation='relu', W_constraint=maxnorm(3)))
    model.add(Dense(16, init='normal', activation='relu', W_constraint=maxnorm(3)))
    model.add(Dense(122, init='normal', activation='softmax'))
    # Compile model
    sgd = SGD(lr=0.1, momentum=0.8, decay=0.0, nesterov=False)
    model.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=['accuracy'])
    return model

numpy.random.seed(seed)
estimators = []
estimators.append(('standardize', StandardScaler()))
estimators.append(('mlp', KerasClassifier(build_fn=create_baseline, nb_epoch=300, batch_size=16, verbose=0, callbacks=calls)))
pipeline = Pipeline(estimators)
kfold = StratifiedKFold(n_splits=10, shuffle=True, random_state=seed)
results = cross_val_score(pipeline, X, encoded_Y, cv=kfold)
print("Baseline: %.2f%% (%.2f%%)" % (results.mean()*100, results.std()*100))

这是产生的错误-

RuntimeError: Cannot clone object <keras.wrappers.scikit_learn.KerasClassifier object at 0x000000001D691438>, as the constructor does not seem to set parameter callbacks

我在下面更改了 cross_val_score-

numpy.random.seed(seed)
estimators = []
estimators.append(('standardize', StandardScaler()))
estimators.append(('mlp', KerasClassifier(build_fn=create_baseline, nb_epoch=300, batch_size=16, verbose=0, callbacks=calls)))
pipeline = Pipeline(estimators)
kfold = StratifiedKFold(n_splits=10, shuffle=True, random_state=seed)
results = cross_val_score(pipeline, X, encoded_Y, cv=kfold, fit_params={'callbacks':calls})
print("Baseline: %.2f%% (%.2f%%)" % (results.mean()*100, results.std()*100))

现在我得到了这个错误-

ValueError: need more than 1 value to unpack

This code came from here。该代码是迄今为止我使用过的最准确的代码。问题是代码中的任何地方都没有定义model.fit()。它也需要永远适应。 fit() 操作发生在 results = cross_val_score(...) 并且没有参数可以在那里引发回调。

我该怎么做呢? 另外,如何运行在测试集上训练的模型?

我需要能够保存经过训练的模型以供以后使用...

【问题讨论】:

  • 能否也给我们提供一个回调列表定义?
  • 你能说明错误发生在哪里吗?更多的痕迹
  • 并从 KerasClassifier 构造函数中删除回调列表。
  • 解决了,看我的编辑:)

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


【解决方案1】:

试试:

estimators.append(('mlp', 
                   KerasClassifier(build_fn=create_model2,
                                   nb_epoch=300,
                                   batch_size=16,
                                   verbose=0,
                                   callbacks=[list_of_callbacks])))

其中list_of_callbacks 是您要应用的回调列表。您可以找到详细信息here。那里提到,提供给KerasClassifier 的参数可能是合法的拟合参数。

还值得一提的是,如果您使用 GPU 进行多次运行,则可能会由于多次报告的内存泄漏而出现问题,尤其是在您使用 theano 时。我还注意到,因此运行多次拟合可能会显示使用sklearn API 时似乎不独立的结果。

编辑:

也可以试试:

results = cross_val_score(pipeline, X, encoded_Y, cv=kfold, fit_params = {'mlp__callbacks': calls})

而不是将回调列表放在包装实例中。

【讨论】:

  • RuntimeError: Cannot clone object ,因为构造函数似乎没有设置参数回调
  • 新编辑显示完整代码。这不是一个大的变化,但我在KerasClassifier 中加入了回调部分。
  • 立即尝试。更新后。我是用手机写的,可能有错别字
  • 还是不行ValueError: fit_params is not a legal parameter
  • 当您尝试第二个命题时,请从 KerasClassifier 中删除回调。
【解决方案2】:

阅读from here,这是 KerasClassifier 的源代码,您可以将 fit 的参数传递给它,并且应该使用它们。 我没有您的数据集,因此无法对其进行测试,但是您可以告诉我这是否有效,如果无效,我将尝试调整解决方案。更改此行:

estimators.append(('mlp', KerasClassifier(build_fn=create_baseline, nb_epoch=300, batch_size=16, verbose=0, callbacks=[...your_callbacks...])))

对正在发生的事情的一个小解释:KerasClassifier 为fitpredictscore 获取所有可能的参数,并在调用每个方法时相应地使用它们。他们制作了一个函数来过滤应该传递给可以在管道中调用的上述每个函数的参数。 我猜StratifiedKFold 步骤中有几个fitpredict 调用,每次都在不同的拆分上进行训练。

正如你所问的,它需要永远拟合并且拟合 10 次的原因是因为一次拟合正在执行 300 个 epoch。所以 KFold 在不同的折叠上重复这个步骤:

  • 调用fit,将所有参数提供给KerasClassifier(300 epochs and batch size = 16)。它对 9/10 的数据进行训练,并使用 1/10 作为验证。

编辑:

好的,所以我花时间下载了数据集并尝试了您的代码...首先您需要纠正网络中的“一些”问题:

  • 您的输入有 60 个特征。您在数据准备中清楚地展示了它:

    X = dataset[:,:60].astype(float)
    

    那你为什么会有这个:

    model.add(Dropout(0.2, input_shape=(33,)))
    

    请改成:

    model.add(Dropout(0.2, input_shape=(60,)))
    
  • 关于您的目标/标签。您将目标从原始代码 (binary_crossentropy) 更改为 categorical_crossentropy。但是你没有改变你的 Y 数组。所以要么在你的数据准备中这样做:

    from keras.utils.np_utils import to_categorical
    encoded_Y = to_categorical(encoder.transform(Y))
    

    或将您的目标改回binary_crossentropy

  • 现在网络的输出大小:最后一个密集层上的输出大小为 122?您的数据集显然有 2 个类别,那么您为什么要输出 122 个类别?它不会匹配目标。请将您的最后一层改回:

    model.add(Dense(2, init='normal', activation='softmax'))
    

    如果您选择使用categorical_crossentropy,或者

    model.add(Dense(1, init='normal', activation='sigmoid'))
    

    如果你回到binary_crossentropy

现在你的网络编译好了,我可以开始解决问题了。

这是您的解决方案

所以现在我可以得到真正的错误信息了。事实证明,当您在 cross_val_score() 函数中输入 fit_params=whatever 时,您将这些参数输入到管道中。为了知道您要将这些参数发送到管道的哪个部分,您必须像这样指定它:

fit_params={'mlp__callbacks':calls}

您的错误是说该进程无法将 'callbacks'.split('__', 1) 解压缩为 2 个值。它实际上是在寻找将其应用到的管道步骤的名称。

它现在应该可以工作了:)

results = cross_val_score(pipeline, X, encoded_Y, cv=kfold, fit_params={'mlp__callbacks':calls})

但是,您应该知道这里发生了什么……交叉验证实际上调用 create_baseline() 函数从头开始重新创建模型 10 次,并在数据集的不同部分对其进行 10 次训练。所以它不是像你说的那样做 epochs,而是做 300 个 epochs 10 次。 使用此工具还会发生什么:由于模型总是不同的,这意味着 fit() 方法在不同模型上应用了 10 次,因此,回调也应用了 10 次不同的时间,并且文件由 @ 保存987654351@ 被覆盖,您会发现自己只有上次运行的最佳模型。

这是您使用的工具的内在因素,我看不出有任何解决办法。这是使用不同的通用工具的结果,这些工具并未特别考虑与所有可能的配置一起使用。

【讨论】:

  • 如果没有你的帮助,我怎么可能弄明白!?先生,您真是太棒了!另外,我只分享了我的一半代码,所以你提到的所有更改都不适用于我的实际代码......无论如何,它都有效。
  • 实际上你可以在管道文档中找到'__'信息:管道的目的是在设置不同参数的同时组合几个可以交叉验证的步骤。为此,它可以使用它们的名称和由“__”分隔的参数名称来设置各个步骤的参数,如下例所示。可以通过将参数及其名称设置为另一个估计器来完全替换步骤的估计器,或者通过设置为 None 来删除转换器。链接:scikit-learn.org/stable/modules/generated/…
  • 我有办法保存每个最佳模型而不是覆盖它们...现在我想知道是否有一种方法可以合并不同的模型。
【解决方案3】:

这就是我所做的

results = cross_val_score(estimator, X, Y, cv=kfold,
                      fit_params = {'callbacks': [checkpointer,plateau]})

到目前为止一直有效

【讨论】:

    【解决方案4】:

    尽管 TensorFlowKerasSciKeras 文档建议您可以通过 fit 方法定义训练回调,但对于我的设置,事实证明(就像 @NassimBen 建议的那样)您应该改为通过模型构造函数来完成。

    而不是这样:

    model = KerasClassifier(..).fit(X, y, callbacks=[<HERE>])
    

    试试这个:

    model = KerasClassifier(callbacks=[<HERE>]).fit(X, y)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-12
      相关资源
      最近更新 更多