【问题标题】:Use sample weight in multi-label classification在多标签分类中使用样本权重
【发布时间】:2018-09-07 03:18:27
【问题描述】:

当我使用线性 SVM 在 scikit-learn 中处理分类问题时,我可以将自定义权重应用于每个训练样本,如下所示:

from sklearn.linear_model import SGDClassifier

X = [[0.0, 0.0], [1.0, 1.0]]
y = [0, 1]
sample_weight = [1.0, 0.5]
clf = SGDClassifier(loss="hinge")
clf.fit(X, y, sample_weight=sample_weight)

现在,当我有一个多标签分类任务时,我需要转换标签,SGDClassifier 必须包装在像 OneVsRestClassifier 这样的元估计器中:

from sklearn.preprocessing import MultiLabelBinarizer
from sklearn.multiclass import OneVsRestClassifier
from sklearn.linear_model import SGDClassifier

X = [[0.0, 0.0], [1.0, 1.0], [1.0, 0.0]]
y = [[0], [1], [0, 1]]
y_mlb = MultiLabelBinarizer().fit_transform(y)
sample_weight = [1.0, 0.5, 0.8]
clf = OneVsRestClassifier(SGDClassifier(loss="hinge"))
clf.fit(X, y_mlb)   # unable to pass `sample_weight`

但是,OneVsRestClassifier 不允许我向fit 方法传递除Xy 之外的任何参数,因此我无法像以前那样应用样本权重。在这种情况下如何应用我自己的样本权重?

【问题讨论】:

  • 我尝试通过 hack 解决这个问题:将 SGDClassifier 实例的 fit 方法替换为自定义实现,该实现引用我的样本权重列表,但是:原来OneVsRestClassifier 克隆了估计器。当然,不是使用标准的 Python 工具,而是通过使用从 get_params 方法检索到的参数来实例化同一类的新估计器。因此猴子补丁没有被保留,我回到第一方......
  • 所以现在我可以继承SGDClassifier,添加样本权重作为实例参数,修补get_params 方法并修补fit 方法以使用存储在类中的样本权重...这太可怕了????

标签: python scikit-learn classification


【解决方案1】:

而是尝试子类化 OneVsRestClassifier 以更改 fit 方法以允许传递 sample_weight。 您需要更改其中使用的 fit() 和 _fit_binary() 方法。

尝试将source from here 编辑为:

import warnings
import numpy as np
from sklearn.externals.joblib import Parallel, delayed
from sklearn.base import clone
from sklearn.multiclass import _ConstantPredictor, OneVsRestClassifier
from sklearn.preprocessing import LabelBinarizer, MultiLabelBinarizer
from sklearn.linear_model import SGDClassifier

def _fit_binary_new(estimator, X, y, sample_weight, classes=None):
    unique_y = np.unique(y)
    if len(unique_y) == 1:
        if classes is not None:
            if y[0] == -1:
                c = 0
            else:
                c = y[0]
            warnings.warn("Label %s is present in all training examples." %
                          str(classes[c]))
        estimator = _ConstantPredictor().fit(X, unique_y)
    else:
        estimator = clone(estimator)

        # Only this changed
        estimator.fit(X, y, sample_weight=sample_weight)
    return estimator

class OneVsRestClassifierNew(OneVsRestClassifier):

    def fit(self, X, y, sample_weight=None):
        self.label_binarizer_ = LabelBinarizer(sparse_output=True)
        Y = self.label_binarizer_.fit_transform(y)
        Y = Y.tocsc()
        self.classes_ = self.label_binarizer_.classes_
        columns = (col.toarray().ravel() for col in Y.T)
        self.estimators_ = Parallel(n_jobs=self.n_jobs)(delayed(_fit_binary_new)(
            self.estimator, X, column, sample_weight, classes=[
                "not %s" % self.label_binarizer_.classes_[i],
                self.label_binarizer_.classes_[i]])
            for i, column in enumerate(columns))

        return self


X = [[0.0, 0.0], [1.0, 1.0], [1.0, 0.0]]
y = [[0], [1], [0, 1]]
y_mlb = MultiLabelBinarizer().fit_transform(y)
sample_weight = [1.0, 0.5, 0.8]
clf = OneVsRestClassifierNew(SGDClassifier(loss="hinge"))
clf.fit(X, y_mlb, sample_weight=sample_weight)

clf.predict(X)
# Output: array([[1, 0],
#                [0, 1],
#                [1, 1]])

注意:这仅适用于在 fit() 方法中定义了 sample_weight 的分类器,因为我不检查 _fit_binary_new() 中是否存在。

【讨论】:

  • 这似乎可行,但我可以看到一旦 sklearn-devs 决定触及这些功能之一,它会如何爆炸。也许我应该为此提交错误报告(或功能请求)。无论如何,谢谢你的回答。
  • @Klamann 也许是因为 OneVsRestClassifier 旨在与所有在 fit() 中没有 sample_weight 的估计器一起工作。无论如何,我尝试搜索此请求的现有问题,但找不到。随意添加。另外,如果它有效且有帮助,请接受答案。
  • 我为此提交了feature request。我看到 OneVsRestClassifier 也应该能够与其他估计器一起工作,因此显而易见的解决方案是将任意 kwargs 传递给底层估计器。我将把它打开一段时间,也许有人会想出一个不那么侵入性的解决方法。
猜你喜欢
  • 2020-04-25
  • 2018-07-07
  • 1970-01-01
  • 2018-10-14
  • 2020-09-09
  • 1970-01-01
  • 2020-10-21
  • 2016-05-25
  • 2016-06-14
相关资源
最近更新 更多