Scikit-learn 多目标

Posted

技术标签:

【中文标题】Scikit-learn 多目标【英文标题】:Scikit-learn multiple targets 【发布时间】:2016-06-05 20:25:17 【问题描述】:

我留下了这个 example 以使用 scikit-learn 创建分类器图像。

虽然每张图片都属于一个类别一切正常,但每张图片可能属于多个类别,例如:白天狗的照片、晚上猫的照片、晚上猫和狗的照片等...... 我写道:

target=[[0,1],[0,2],[1,2],[0,2,3]]
target = MultiLabelBinarizer().fit_transform(target)

classifier = svm.SVC(gamma=0.001)
classifier.fit(data, target)

但我收到此错误:

Traceback (most recent call last):
  File "test.py", line 49, in <module>
    classifier.fit(data, target)
  File "/home/mezzo/.local/lib/python2.7/site-packages/sklearn/svm/base.py", line 151, in fit
    y = self._validate_targets(y)
  File "/home/mezzo/.local/lib/python2.7/site-packages/sklearn/svm/base.py", line 514, in _validate_targets
    y_ = column_or_1d(y, warn=True)
  File "/home/mezzo/.local/lib/python2.7/site-packages/sklearn/utils/validation.py", line 551, in column_or_1d
    raise ValueError("bad input shape 0".format(shape))
ValueError: bad input shape (4, 4)

完整代码

import numpy as np
import PIL
from PIL import Image
import matplotlib.image as mpimg

# The digits dataset
digits = datasets.load_digits()

def normalize(old_im):
    base = 400

    if (old_im.size[0] > old_im.size[1]):
        wpercent = (base/float(old_im.size[0]))
        hsize = int((float(old_im.size[1])*float(wpercent)))
        old_im = old_im.resize((base,hsize), PIL.Image.ANTIALIAS)
    else:
        wpercent = (base/float(old_im.size[1]))
        wsize = int((float(old_im.size[0])*float(wpercent)))
        old_im = old_im.resize((wsize, base), PIL.Image.ANTIALIAS)

    old_size = old_im.size

    new_size = (base, base)
    new_im = Image.new("RGB", new_size)
    new_im.paste(old_im, ((new_size[0]-old_size[0])/2,
                          (new_size[1]-old_size[1])/2))

    #new_im.show()
    new_im.save('prov.jpg')
    return mpimg.imread('prov.jpg')

# To apply a classifier on this data, we need to flatten the image, to
# turn the data in a (samples, feature) matrix:
imgs = np.array([normalize(Image.open('/home/mezzo/Immagini/1.jpg')),normalize(Image.open('/home/mezzo/Immagini/2.jpg')),normalize(Image.open('/home/mezzo/Immagini/3.jpg')),normalize(Image.open('/home/mezzo/Immagini/4.jpg'))])
n_samples = len(imgs)
data = imgs.reshape((n_samples, -1))

target=[[0,1],[0,2],[1,2],[0,2,3]]
target = MultiLabelBinarizer().fit_transform(target)

# Create a classifier: a support vector classifier
classifier = svm.SVC(gamma=0.001)

# We learn the digits on the first half of the digits
classifier.fit(data, target)

# Now predict the value of the digit on the second half:
predicted = classifier.predict(data)

print("Classification report for classifier %s:\n%s\n"
      % (classifier, metrics.classification_report(target, predicted)))
print("Confusion matrix:\n%s" % metrics.confusion_matrix(target, predicted))

【问题讨论】:

【参考方案1】:

Scikit-learn 的 SVM 实现本身并不支持多标签分类,although it has various other classifiers that do:

支持多标签:Decision Trees、Random Forests、Nearest Neighbors、Ridge Regression。

还可以通过将每个唯一的标签组合视为一个单独的类来使用 SVM 进行多标签分类。您可以简单地将 target 矩阵中的每个唯一行替换为单个整数标签 which can be done efficiently using np.unique

d = np.dtype((np.void, target.dtype.itemsize * target.shape[1]))
_, ulabels = np.unique(np.ascontiguousarray(target).view(d), return_inverse=True)

然后您可以像处理单标签分类问题一样训练 SVM:

clf = svm.SVC()
clf.fit(data, ulabels)

一个潜在的警告是,如果您没有大量的训练示例,您的分类器的性能可能会因标签的稀有组合而不佳。

【讨论】:

抱歉回复晚了,但我收到您传递给我的代码时出现此错误。:AttributeError: 'list' object has no attribute 'dtype' 感谢您的回答,现在我看到了:IndexError: tuple index out of range 用这一行创建文件.py,可以复制错误:target=[[0,1],[0,2],[1,2],[0,2,3 ]] 目标 = np.array(target) d = np.dtype((np.void, target.dtype.itemsize * target.shape[1])) _, ulabels = np.unique(np.ascontiguousarray(target)。视图(d),return_inverse=True) 您需要target 作为您的二进制多标签表示,即:target = MultiLabelBinarizer().fit_transform([[0,1],[0,2],[1,2],[0,2,3]])。如果您尝试将嵌套列表直接转换为数组,您将得到一个 np.object 数组,因为行长不相等。【参考方案2】:

发生这种情况是因为您的目标是:

array([[1, 1, 0, 0],
       [1, 0, 1, 0],
       [0, 1, 1, 0],
       [1, 0, 1, 1]])

您的目标必须是形状 (m,),其中 m 是示例数。 解决这个问题的一种方法是将二进制数组转换为标签,如下所示:

for item in target:
    print(sum(1<<i for i, b in enumerate(item) if b))

这个输出将是:

3
5
6
13

现在您可以使用[3,5,6,13] 作为您的目标。

【讨论】:

您为每个可能类别的子集创建一个新标签,就像在示例中一样。

以上是关于Scikit-learn 多目标的主要内容,如果未能解决你的问题,请参考以下文章

使用 scikit-learn 进行多目标回归

scikit learn中的多目标岭回归如何工作?

Python / Scikit-Learn - 无法处理多类和连续的混合

如何在 scikit-learn 中有效地编码数字目标变量?

目标的缩放导致 Scikit-learn SVM 回归崩溃

scikit-learn 中的 MultiOutputRegressors 和 MultiOutputClassifiers 是不是也使用目标作为输入?