这里出了什么问题?意外引用现有实例而不是创建新实例

What's wrong here? accidentally referencing an existing instance instead of making a new one

我是一个 R user 的人,希望更适应 Python。我写了一种 mini-API 可以很容易地比较适合相同数据的不同统计模型,这样我就可以预先设置所有模型超参数,然后按顺序迭代不同的模型适合他们。

这就是我要做的事情的本质:

  1. 围绕 Scikit-learn Pipeline, in turn built on one of Scikit-learn's built-in estimators, e.g. RandomForestClassifier
  2. 构建包装器 class Classifier
  3. 创建一个包含这些未拟合的 Classifier 的字典,以及一个不同的参数字典来循环
  4. 遍历两个字典,让每个未拟合的 Classifier 生成 底层管道的新实例 ,使用其 [Pipeline.fit][1] 方法对其进行拟合,并将新的、适合的管道保存在不同的字典中

但是,似乎在每次迭代中都没有生成新的管道实例,而是重新装配管道的相同实例(或者可能是底层估算器)。这是一个问题,因为 Pipeline.fit 方法修改了 Pipeline(和底层估计器),因此之前迭代的拟合结果都被最后一次迭代的拟合结果覆盖。

问题是我无法弄清楚这个 "parent instance" 是在哪里创建的以及它是如何被引用的。

具有可重现问题示例的基本设置在 this Gist 中(只是复制粘贴到这里有点太长了)。我在最后添加了一个打印语句来说明这个问题。

抱歉,如果这有点含糊,但我很难描述它。希望示例中的问题很清楚。

问题是 results['0']['rf']results['1']['rf'] 实际上是同一个对象。因此,当您在循环中安装管道时:

results = dict()
for k in features.keys():
    results[k] = dict()
    for m in classifiers.keys():
        print(len(features[k]))
        results[k][m] = classifiers[m].fit(features[k], 'species', iris)

您正在重新安装已经安装好的管道,丢失了之前的工作。

为了解决这个问题,您需要在每次适应它时创建一个新的 Classifier 实例。一种可能的方法是将 classifiers 字典从包含 Classifier 实例的字典更改为包含创建 Classifier:

所需参数的字典
classifiers = {
    'rf': (RandomForestClassifier, n_estimators=100, oob_score=True, bootstrap=True),
    'ab': (AdaBoostClassifier, n_estimators=50)
}

现在,在您的循环中,您应该使用称为 "tuple unpacking" 的 Python 习惯用法来解压缩参数并为每个组合创建一个单独的 Classifier 实例

for k in features:
    results[k] = dict()
    for m in classifiers:
        print(len(features[k]))
        classifier = Classifier(*classifiers[m])
        results[k][m] = classifier.fit(features[k], 'species', iris)

请注意,要遍历字典的键,可以简单地写 for key in dct:,而不是 for key in dct.keys()