1、什么是多分类?

参考:https://www.jianshu.com/p/9332fcfbd197

针对多类问题的分类中,具体讲有两种,即multiclass classification和multilabel classification。multiclass是指分类任务中包含不止一个类别时,每条数据仅仅对应其中一个类别,不会对应多个类别。multilabel是指分类任务中不止一个分类时,每条数据可能对应不止一个类别标签,例如一条新闻,可以被划分到多个板块。

无论是multiclass,还是multilabel,做分类时都有两种策略,一个是one-vs-​the-rest(one-vs-all),一个是one-vs-one。

在one-vs-all策略中,假设有n个类别,那么就会建立n个二项分类器,每个分类器针对其中一个类别和剩余类别进行分类。进行预测时,利用这n个二项分类器进行分类,得到数据属于当前类的概率,选择其中概率最大的一个类别作为最终的预测结果。

在one-vs-one策略中,同样假设有n个类别,则会针对两两类别建立二项分类器,得到k=n*(n-1)/2个分类器。对新数据进行分类时,依次使用这k个分类器进行分类,每次分类相当于一次投票,分类结果是哪个就相当于对哪个类投了一票。在使用全部k个分类器进行分类后,相当于进行了k次投票,选择得票最多的那个类作为最终分类结果​。

在scikit-learn框架中,分别有sklearn.multiclass.OneVsRestClassifier和sklearn.multiclass.OneVsOneClassifier完成两种策略,使用过程中要指明使用的二项分类器是什么。另外在进行mutillabel分类时,训练数据的类别标签Y应该是一个矩阵,第[i,j]个元素指明了第j个类别标签是否出现在第i个样本数据中。例如,np.array([[1, 0, 0], [0, 1, 1], [0, 0, 0]]),这样的一条数据,指明针对第一条样本数据,类别标签是第0个类,第二条数据,类别标签是第1,第2个类,第三条数据,没有类别标签。有时训练数据中,类别标签Y可能不是这样的可是,而是类似[[2, 3, 4], [2], [0, 1, 3], [0, 1, 2, 3, 4], [0, 1, 2]]这样的格式,每条数据指明了每条样本数据对应的类标号。这就需要将Y转换成矩阵的形式,sklearn.preprocessing.MultiLabelBinarizer提供了这个功能。

2、构建多个二分类器进行分类

使用的数据集是sklearn自带的iris数据集,该数据集总共有三类。

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from sklearn import svm,datasets
  4. from itertools import cycle
  5.  
  6. from sklearn import svm, datasets
  7. from sklearn.metrics import roc_curve, auc
  8. from sklearn.model_selection import train_test_split
  9. from sklearn.preprocessing import label_binarize
  10. from sklearn.multiclass import OneVsRestClassifier
  11. from scipy import interp
  12.  
  13. # 导入鸢尾花数据集
  14. iris = datasets.load_iris()
  15. X = iris.data # X.shape==(150, 4)
  16. y = iris.target # y.shape==(150, )
  17.  
  18. # 二进制化输出
  19. y = label_binarize(y, classes=[0, 1, 2]) # shape==(150, 3)
  20. n_classes = y.shape[1] # n_classes==3
  21.  
  22. #np.r_是按列连接两个矩阵,就是把两矩阵上下相加,要求列数相等。
  23. #np.c_是按行连接两个矩阵,就是把两矩阵左右相加,要求行数相等。
  24. # 添加噪音特征,使问题更困难
  25. random_state = np.random.RandomState(0)
  26. n_samples, n_features = X.shape # n_samples==150, n_features==4
  27. X = np.c_[X, random_state.randn(n_samples, 200 * n_features)] # shape==(150, 84)
  1. # 打乱数据集并切分训练集和测试集
  2. X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.5,
  3. random_state=0)
  4. # X_train.shape==(75, 804), X_test.shape==(75, 804), y_train.shape==(75, 3), y_test.shape==(75, 3)
  5.  
  6. # 学习区分某个类与其他的类
  7. classifier = OneVsRestClassifier(svm.SVC(kernel='linear', probability=True,
  8. random_state=random_state))
  9. y_score = classifier.fit(X_train, y_train).decision_function(X_test)

这里提一下classifier.fit()后面接的函数:可以是decision_function()、predict_proba()、predict()

predict():返回预测标签、

predict_proba():返回预测属于某标签的概率

decision_function():返回样本到分隔超平面的有符号距离来度量预测结果的置信度

这里我们分别打印一下对应的y_score,只取前三条数据:

预测标签:[[0 0 1] [0 1 0] [1 0 0]...]

概率:[[6.96010030e-03 1.67062907e-01 9.65745632e-01] [4.57532814e-02 3.05231268e-01 4.58939259e-01] [7.00832624e-01 2.32537226e-01 4.92996070e-02]...]

距离:[[-1.18047012 -2.60334173 1.48134717] [-0.72354789 0.15798952 -0.08648247] [ 0.22439326 -1.15044791 -1.35488445]...]

同时,我们还要注意使用到了:OneVsRestClassifier,如何理解呢?

我们可以这么看:OneVsRestClassifier实际上包含了多个分类器,有多少个类别就有多少个分类器,这里有三个类别,因此就有三个分类器,可以通过:

  1. print(classifier.estimators_)

来查看:

  1. [SVC(C=1.0, break_ties=False, cache_size=200, class_weight=None, coef0=0.0,
  2. decision_function_shape='ovr', degree=3, gamma='scale', kernel='linear',
  3. max_iter=-1, probability=True,
  4. random_state=RandomState(MT19937) at 0x7F480F316A98, shrinking=True,
  5. tol=0.001, verbose=False),
  6. SVC(C=1.0, break_ties=False, cache_size=200, class_weight=None, coef0=0.0,
  7. decision_function_shape='ovr', degree=3, gamma='scale', kernel='linear',
  8. max_iter=-1, probability=True,
  9. random_state=RandomState(MT19937) at 0x7F480F316CA8, shrinking=True,
  10. tol=0.001, verbose=False),
  11. SVC(C=1.0, break_ties=False, cache_size=200, class_weight=None, coef0=0.0,
  12. decision_function_shape='ovr', degree=3, gamma='scale', kernel='linear',
  13. max_iter=-1, probability=True,
  14. random_state=RandomState(MT19937) at 0x7F480F316DB0, shrinking=True,
  15. tol=0.001, verbose=False)]

对于每一个分类器,都是二分类,即将当前的类视为一类,另外的其他类视为一类,比如说我们可以取得其中的分类器进行分类,以第一个标签为例:

  1. y_true=np.where(y_test==1)[1]

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

  1. #这里重新定义标签,1代表当前标签,0代表其他标签
  2. y0=[0 if i==0 else 1 for i in y_true]
  3. print(y0)
  4. print(classifier.estimators_[0].fit(X_train,y0).predict(X_test))

[0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0]

[0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 1 0 1 1 1 1 0 0 0 0 0 0 1 0 1 0 1 0 1 0 0 0 1 0 1 0 1 0 0 1 0 1 0 0]

我们直接打印y_score中第0列的结果y_score[:,0]:

[0 0 1 0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 1 1 0 0 1 1 0 1 1 0 0 1 0 0 1 0 0 0 1 0 0 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 1 1 0]

就是对应的第一个分类器的结果。(这里的结果不一致是因为classifier.estimators_[0].fit(X_train,y0).predict(X_test)相当于有重新训练并预测了一次。

从而,y_score中的每一列都表示了每一个分类器的结果。

所以,在y_score的结果中出现了:[1,1,0]这种就不足为怪了。但是有个问题,如果其中有两个分类器都将某个类认为是当前类,那么这类到底属于哪一个类呢?所以不能直接就对每一个分类器的概率值取得标签值,而是要计算出每一个分类器的概率值,最后再进行映射成标签。回过头来才发现的,以下使用的是predict(),因此是有问题的,但是基本方式是差不多的,再修改就有点麻烦了,酌情阅读了= =。

多分类问题就转换为了oneVsRest问题,可以分别使用二分类评价指标了,可参考:

https://www.cnblogs.com/xiximayou/p/13682052.html

比如说绘制ROC和计算AUC:

  1. from sklearn.metrics import roc_curve, auc
  2. # 为每个类别计算ROC曲线和AUC
  3. fpr = dict()
  4. tpr = dict()
  5. roc_auc = dict()
  6. n_classes=3
  7. for i in range(n_classes):
  8. fpr[i], tpr[i], _ = roc_curve(y_test[:, i], y_score[:, i])
  9. roc_auc[i] = auc(fpr[i], tpr[i])
  10. # fpr[0].shape==tpr[0].shape==(21, ), fpr[1].shape==tpr[1].shape==(35, ), fpr[2].shape==tpr[2].shape==(33, )
  11. # roc_auc {0: 0.9118165784832452, 1: 0.6029629629629629, 2: 0.7859477124183007}
  12.  
  13. plt.figure()
  14. lw = 2
  15. for i in range(n_classes):
  16. plt.plot(fpr[i], tpr[i], color='darkorange',
  17. lw=lw, label='ROC curve (area = %0.2f)' % roc_auc[i])
  18. plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')
  19. plt.xlim([0.0, 1.0])
  20. plt.ylim([0.0, 1.05])
  21. plt.xlabel('False Positive Rate')
  22. plt.ylabel('True Positive Rate')
  23. plt.title('Receiver operating characteristic example')
  24. plt.legend(loc="lower right")
  25. plt.show()

3、多分类评价指标?

宏平均 Macro-average

Macro F1:将n分类的评价拆成n个二分类的评价,计算每个二分类的F1 score,n个F1 score的平均值即为Macro F1。

微平均 Micro-average

Micro F1:将n分类的评价拆成n个二分类的评价,将n个二分类评价的TP、FP、TN、FN对应相加,计算评价准确率和召回率,由这2个准确率和召回率计算的F1 score即为Micro F1。

对于二分类问题:

  1. TP=cnf_matrix[1][1] #预测为正的真实标签为正
  2. FP=cnf_matrix[0][1] #预测为正的真实标签为负
  3. FN=cnf_matrix[1][0] #预测为负的真实标签为正
  4. TN=cnf_matrix[0][0] #预测为负的真实标签为负
  5. accuracy=(TP+TN)/(TP+FP+FN+TN)
  6. precision=TP/(TP+FP)
  7. recall=TP/(TP+FN)
  8. f1score=2 * precision * recall/(precision + recall)

ROC曲线:

横坐标:假正率(False positive rate, FPR),预测为正但实际为负的样本占所有负例样本的比例;

FPR = FP / ( FP +TN)

纵坐标:真正率(True positive rate, TPR),这个其实就是召回率,预测为正且实际为正的样本占所有正例样本的比例。

TPR = TP / ( TP+ FN)

AUC:就是roc曲线和横坐标围城的面积。

对于上述的oneVsRest:

  1. # 计算微平均ROC曲线和AUC
  2. fpr["micro"], tpr["micro"], _ = roc_curve(y_test.ravel(), y_score.ravel())
  3. roc_auc["micro"] = auc(fpr["micro"], tpr["micro"])
  4.  
  5. # 计算宏平均ROC曲线和AUC
  6.  
  7. # 首先汇总所有FPR
  8. all_fpr = np.unique(np.concatenate([fpr[i] for i in range(n_classes)]))
  9.  
  10. # 然后再用这些点对ROC曲线进行插值
  11. mean_tpr = np.zeros_like(all_fpr)
  12. for i in range(n_classes):
  13. mean_tpr += interp(all_fpr, fpr[i], tpr[i])
  14.  
  15. # 最后求平均并计算AUC
  16. mean_tpr /= n_classes
  17.  
  18. fpr["macro"] = all_fpr
  19. tpr["macro"] = mean_tpr
  20. roc_auc["macro"] = auc(fpr["macro"], tpr["macro"])
  21.  
  22. # 绘制所有ROC曲线
  23. plt.figure()
  24. lw = 2
  25. plt.plot(fpr["micro"], tpr["micro"],
  26. label='micro-average ROC curve (area = {0:0.2f})'
  27. ''.format(roc_auc["micro"]),
  28. color='deeppink', linestyle=':', linewidth=4)
  29.  
  30. plt.plot(fpr["macro"], tpr["macro"],
  31. label='macro-average ROC curve (area = {0:0.2f})'
  32. ''.format(roc_auc["macro"]),
  33. color='navy', linestyle=':', linewidth=4)
  34.  
  35. colors = cycle(['aqua', 'darkorange', 'cornflowerblue'])
  36. for i, color in zip(range(n_classes), colors):
  37. plt.plot(fpr[i], tpr[i], color=color, lw=lw,
  38. label='ROC curve of class {0} (area = {1:0.2f})'
  39. ''.format(i, roc_auc[i]))
  40.  
  41. plt.plot([0, 1], [0, 1], 'k--', lw=lw)
  42. plt.xlim([0.0, 1.0])
  43. plt.ylim([0.0, 1.05])
  44. plt.xlabel('False Positive Rate')
  45. plt.ylabel('True Positive Rate')
  46. plt.title('Some extension of Receiver operating characteristic to multi-class')
  47. plt.legend(loc="lower right")
  48. plt.show()

接下来我们将分类视为一个整体:

  1. from sklearn.metrics import confusion_matrix
  2. classes=[0,1,2]
  3. y_my_test=np.where(y_test==1)[1]
  4. y_my_score=np.zeros(y_my_test.shape)
  5. for i in range(len(classes)):
  6. y_my_score[np.where(y_score[:,i]==1)]=i
  7. confusion = confusion_matrix(y_my_test, y_my_score)# 绘制热度图
  8. plt.imshow(confusion, cmap=plt.cm.Greens)
  9. indices = range(len(confusion))
  10. plt.xticks(indices, classes)
  11. plt.yticks(indices, classes)
  12. plt.colorbar()
  13. plt.xlabel('y_pred')
  14. plt.ylabel('y_true')
  15.  
  16. # 显示数据
  17. for first_index in range(len(confusion)):
  18. for second_index in range(len(confusion[first_index])):
  19. plt.text(first_index, second_index, confusion[first_index][second_index])
  20.  
  21. # 显示图片
  22. plt.show()

我们首先要将测试标签和预测标签转换为非One-hot编码,才能计算出混淆矩阵:

计算出每一类的评价指标:

  1. from sklearn.metrics import classification_report
  2. t = classification_report(y_my_test, y_my_score, target_names=['0', '1', '2'])
  1. precision recall f1-score support
  2.  
  3. 0 0.52 0.71 0.60 21
  4. 1 0.60 0.40 0.48 30
  5. 2 0.73 0.79 0.76 24
  6.  
  7. accuracy 0.61 75
  8. macro avg 0.62 0.64 0.61 75
  9. weighted avg 0.62 0.61 0.60 75

如果要使用上述的值,需要这么使用:

  1. t = classification_report(y_my_test, y_my_score, target_names=['0', '1', '2'],output_dict=True)

{'0': {'precision': 0.5172413793103449, 'recall': 0.7142857142857143, 'f1-score': 0.6000000000000001, 'support': 21}, '1': {'precision': 0.6, 'recall': 0.4, 'f1-score': 0.48, 'support': 30}, '2': {'precision': 0.7307692307692307, 'recall': 0.7916666666666666, 'f1-score': 0.76, 'support': 24}, 'accuracy': 0.6133333333333333, 'macro avg': {'precision': 0.6160035366931919, 'recall': 0.6353174603174603, 'f1-score': 0.6133333333333334, 'support': 75}, 'weighted avg': {'precision': 0.6186737400530504, 'recall': 0.6133333333333333, 'f1-score': 0.6032000000000001, 'support': 75}}

我们可以分别计算每一类的相关指标:

  1. import sklearn
  2. for i in range(len(classes)):
  3. precision=sklearn.metrics.precision_score(y_test[:,i], y_score[:,i], labels=None, pos_label=1, average='binary',
  4. sample_weight=None)
  5. print("{} precision:{}".format(i,precision))

也可以整体计算:

  1. from sklearn.metrics import precision_score
  2. print(precision_score(y_test, y_score, average="micro"))

average可选参数micro、macro、weighted

具体的计算方式可以去参考:

https://zhuanlan.zhihu.com/p/59862986

参考:

https://blog.csdn.net/hfutdog/article/details/88079934

https://blog.csdn.net/wf592523813/article/details/95202448

https://blog.csdn.net/vivian_ll/article/details/99627094

python实现多分类评价指标的更多相关文章

  1. python的数据结构分类,以及数字的处理函数,类型判断

    python的数据结构分类: 数值型 int:python3中都是长整形,没有大小限制,受限内存区域的大小 float:只有双精度型 complex:实数和虚数部分都是浮点型,1+1.2J bool: ...

  2. 多分类评价指标python代码

    from sklearn.metrics import precision_score,recall_score print (precision_score(y_true, y_scores,ave ...

  3. 【原】Spark之机器学习(Python版)(二)——分类

    写这个系列是因为最近公司在搞技术分享,学习Spark,我的任务是讲PySpark的应用,因为我主要用Python,结合Spark,就讲PySpark了.然而我在学习的过程中发现,PySpark很鸡肋( ...

  4. python 之 决策树分类算法

    发现帮助新手入门机器学习的一篇好文,首先感谢博主!:用Python开始机器学习(2:决策树分类算法) J. Ross Quinlan在1975提出将信息熵的概念引入决策树的构建,这就是鼎鼎大名的ID3 ...

  5. [Python机器学习]鸢尾花分类 机器学习应用

    1.问题简述 假设有一名植物学爱好者对她发现的鸢尾花的品种很感兴趣.她收集了每朵鸢尾花的一些测量数据: 花瓣的长度和宽度以及花萼的长度和宽度,所有测量结果的单位都是厘米. 她还有一些鸢尾花的测量数据, ...

  6. 一文弄懂pytorch搭建网络流程+多分类评价指标

    讲在前面,本来想通过一个简单的多层感知机实验一下不同的优化方法的,结果写着写着就先研究起评价指标来了,之前也写过一篇:https://www.cnblogs.com/xiximayou/p/13700 ...

  7. learn python, ref, diveintopython 分类: python 2015-07-22 14:42 14人阅读 评论(0) 收藏

    for notes of learing python. // just ignore the ugly/wrong highlight for python code. ""&q ...

  8. Python的方法分类

    1.Python的类方法,实例方法,和静态方法 class S(object): def Test(self): print("TEST") @classmethod#类方法 de ...

  9. Python 类型的分类

    1.存储模型,对象可以保存多少个值.如果只能保存一个值,是原子类型.如果可以保存多个值,是容器类型.数值是原子类型,元组,列表,字典是容器类型.考虑字符串,按道理,字符串应该是容器类型,因为它包含多个 ...

随机推荐

  1. Java8中list.sort的lamba表达式

    最近写代码,需要对list集合排序,IDEA总是黄色警告: Reports calls to Collections.sort(list, comparator) which could be rep ...

  2. 计算机网络-传输层(1)UDP协议

    UDP协议基于Internet IP协议,只提供两个基础功能: 分用/复用 分用:主机接收到IP数据报(datagram),每个数据报携带源IP地址.目的IP地址且携带一个传输层的段(Segment) ...

  3. Git clone时出现fatal:the remote end hung up unexpectedly

    以HTTPS方式进行git clone时出现如下错误: 方法1:增大缓存 git config http.postBuffer 524288000 尝试无效: 方法2:配置git的最低速度和最低速度时 ...

  4. ubuntu18.04 开机定时启动任务

    1,crontab 格式:M H D m d cmd == 分 时 天 月 周几 命令 参数 : crontab -e : 执行文字编辑器来设定时程表,内定的文字编辑器是 VI,如果你想用别的文字编辑 ...

  5. Mac上Safari不能关键字搜索

    今天打开Mac,用Safari浏览器搜索的时发现不能进行关键字搜索,搜索栏只能打开网址. 现在问题已经解决,只要删除Safari上的cookies就可以了.操作步骤如下: Safari ->pr ...

  6. 启动Spring后,连接mysql报错

    连接失败,原因是Mysql服务未启动 解决方法:启动mysql服务 方法一: 以管理员身份运行CMD 输入命令:net start mysql 方法二: 右键计算机-管理-服务和应用程序-服务 右键启 ...

  7. 8点了解Java服务端单元测试

    一. 前言 单元测试并不只是为了验证你当前所写的代码是否存在问题,更为重要的是它可以很大程度的保障日后因业务变更.修复Bug或重构等引起的代码变更而导致(或新增)的风险. 同时将单元测试提前到编写正式 ...

  8. JS - 对金额数字实现千分位格式化处理

    添加千分位处理: function fmoney(s, n) { n = n > 0 && n < = 20 ? n : 2; s = parseFloat((s + &q ...

  9. python3笔记-函数

    创建函数 def 函数名(参数列表): 函数语句 函数的命名规则:一个单词直接小写 # 多个单词,每个单词小写,以下划线分隔 文档化说明 函数首行加 '' 或 ''' ''' 使用函数名.__doc_ ...

  10. Funny Positive Sequence (思维+前缀)

    There are n integers a 1,a 2,…,a n-1,a n in the sequence A, the sum of these n integers is larger th ...