scikit 上的多标签分类指标

机器算法验证 scikit-学习 多级 多标签
2022-01-27 09:53:47

我正在尝试构建一个多标签分类器,以便使用 scikit 将主题分配给现有文档

我正在处理我的文档,通过TfidfVectorizer标签将它们传递给 ,MultiLabelBinarizer并创建了OneVsRestClassifier一个SGDClassifier作为估计器的。

然而,在测试我的分类器时,我只能得到高达0.29的分数,从我读过的内容来看,这对于类似的问题来说是相当低的。我在 TfidfVectorizer 上尝试了多个选项,例如停用词、unigrams、词干,似乎没有什么能改变结果。

我也曾经GridSearchCV为我的估算器获取最佳参数,目前我对下一步要尝试什么一无所知。

同时,据我所知,我不能使用scikit.metricsOneVsRestClassifier所以我怎样才能获得一些指标(F1、Precision、Recall 等)以找出问题所在?

我的数据语料库有问题吗?

更新:我也尝试过使用CountVectorizerHashingVectorizer管道化它们,TfidfTransformer但结果是相似的。所以我猜测词袋方法在标记化领域做得最好,其余的取决于分类器......

4个回答

子集准确度确实是一个苛刻的指标。为了了解 0.29 的好坏,一些想法:

  • 查看每个样本的平均标签数量
  • 查看注释器间协议,如果可用(如果没有,请尝试自己查看当您是分类器时获得的子集精度)
  • 思考主题是否明确
  • 看看每个标签有多少样本

您可能还想计算汉明分数,以查看您的分类器是否毫无头绪,或者是相当不错但在正确预测所有标签时存在问题。请参阅下文以计算汉明分数。

同时,据我了解,我不能将 scikit.metrics 与 OneVsRestClassifier 一起使用,那么我怎样才能获得一些指标(F1、Precision、Recall 等)以找出问题所在?

请参阅如何计算多类多标签分类的精度/召回率?. 我忘了 sklearn 是否支持它,我记得它有一些限制,例如sklearn 不支持混淆矩阵的多标签确实看到这些数字是个好主意。


汉明分数

在多标签分类设置中,sklearn.metrics.accuracy_score仅计算子集准确度(3):即为样本预测的标签集必须与 y_true 中的相应标签集完全匹配。

这种计算准确度的方法有时被命名为精确匹配率(1),或许不太含糊:

在此处输入图像描述

计算准确度的另一种典型方法在 (1) 和 (2) 中定义,并且不那么含糊地称为汉明分数(4)(因为它与汉明损失密切相关)或基于标签的准确度)。计算如下:

在此处输入图像描述

这是计算汉明分数的python方法:

# Code by https://stackoverflow.com/users/1953100/william
# Source: https://stackoverflow.com/a/32239764/395857
# License: cc by-sa 3.0 with attribution required

import numpy as np

y_true = np.array([[0,1,0],
                   [0,1,1],
                   [1,0,1],
                   [0,0,1]])

y_pred = np.array([[0,1,1],
                   [0,1,1],
                   [0,1,0],
                   [0,0,0]])

def hamming_score(y_true, y_pred, normalize=True, sample_weight=None):
    '''
    Compute the Hamming score (a.k.a. label-based accuracy) for the multi-label case
    https://stackoverflow.com/q/32239577/395857
    '''
    acc_list = []
    for i in range(y_true.shape[0]):
        set_true = set( np.where(y_true[i])[0] )
        set_pred = set( np.where(y_pred[i])[0] )
        #print('\nset_true: {0}'.format(set_true))
        #print('set_pred: {0}'.format(set_pred))
        tmp_a = None
        if len(set_true) == 0 and len(set_pred) == 0:
            tmp_a = 1
        else:
            tmp_a = len(set_true.intersection(set_pred))/\
                    float( len(set_true.union(set_pred)) )
        #print('tmp_a: {0}'.format(tmp_a))
        acc_list.append(tmp_a)
    return np.mean(acc_list)

if __name__ == "__main__":
    print('Hamming score: {0}'.format(hamming_score(y_true, y_pred))) # 0.375 (= (0.5+1+0+0)/4)

    # For comparison sake:
    import sklearn.metrics

    # Subset accuracy
    # 0.25 (= 0+1+0+0 / 4) --> 1 if the prediction for one sample fully matches the gold. 0 otherwise.
    print('Subset accuracy: {0}'.format(sklearn.metrics.accuracy_score(y_true, y_pred, normalize=True, sample_weight=None)))

    # Hamming loss (smaller is better)
    # $$ \text{HammingLoss}(x_i, y_i) = \frac{1}{|D|} \sum_{i=1}^{|D|} \frac{xor(x_i, y_i)}{|L|}, $$
    # where
    #  - \\(|D|\\) is the number of samples  
    #  - \\(|L|\\) is the number of labels  
    #  - \\(y_i\\) is the ground truth  
    #  - \\(x_i\\)  is the prediction.  
    # 0.416666666667 (= (1+0+3+1) / (3*4) )
    print('Hamming loss: {0}'.format(sklearn.metrics.hamming_loss(y_true, y_pred))) 

输出:

Hamming score: 0.375
Subset accuracy: 0.25
Hamming loss: 0.416666666667

(1) Sorower, Mohammad S.“关于多标签学习算法的文献调查。 ”俄勒冈州立大学,科瓦利斯 (2010)。

(2) Tsoumakas、Grigorios 和 Ioannis Katakis。多标签分类:概述。 ”希腊塞萨洛尼基亚里士多德大学信息学系(2006 年)。

(3) 加姆拉维、纳迪亚和安德鲁·麦卡勒姆。集体多标签分类。 ”第14届ACM信息与知识管理国际会议论文集。ACM,2005 年。

(4) Godbole、Shantanu 和 Sunita Sarawagi。多标签分类的判别方法。 ” 知识发现和数据挖掘的进展。施普林格柏林海德堡,2004. 22-30。

Hamming-Loss 和精确匹配(也称为子集精度)可以使用Scikit-learn如下计算。

import numpy as np
from sklearn.metrics import hamming_loss, accuracy_score 
y_true = np.array([[0,1,0],
                   [0,1,1],
                   [1,0,1],
                   [0,0,1]])

y_pred = np.array([[0,1,1],
                   [0,1,1],
                   [0,1,0],
                   [0,0,0]])

print("accuracy_score:", accuracy_score(y_true, y_pred))
print("Hamming_loss:", hamming_loss(y_true, y_pred))

输出

accuracy_score: 0.25
Hamming_loss: 0.4166666666666667

0.29的分数还不够吗?你的混淆矩阵是什么样的?是否有一些仅看单词内容无法分离的主题?

否则,试着扭转你的问题:假设低分实际上是你的分类器可以对你的数据做的最好的。这意味着您的文档无法使用这种方法进行分类。

为了检验这个假设,您需要一组具有已知词袋特征的测试文档(您自己创建)。你应该得到 100% 的分数。

如果你不这样做,那么你有一个错误。否则,您需要一种不同的方法来对文档进行分类。问问自己:来自不同类别的文档有何不同?我是否需要查看我的文档的其他功能等?

以下是汉明分数的矢量化版本:

import numpy as np


def hamming_score(pred, answer):
    out = ((pred & answer).sum(axis=1) / (pred | answer).sum(axis=1)).mean()
    if np.isinf(out):
        out = np.array(1.0)
    return out


pred = np.array([[0, 1, 1], [0, 1, 1], [0, 1, 0], [0, 0, 0]])
answer = np.array([[0, 1, 0], [0, 1, 1], [1, 0, 1], [0, 0, 1]])

hamming_score(pred, answer)

或在 PyTorch 中

import torch


def hamming_score(pred, answer):
    out = ((pred & answer).sum(dim=1) / (pred | answer).sum(dim=1)).mean()
    if out.isnan():
        out = torch.tensor(1.0)
    return out

answer = torch.tensor([[0, 1, 0], [0, 1, 1], [1, 0, 1], [0, 0, 1]])
pred = torch.tensor([[0, 1, 1], [0, 1, 1], [0, 1, 0], [0, 0, 0]])

hamming_score(pred, answer)