二元分问题会是咱们生存中比拟常见的一类问题,比方邮件能够分为垃圾邮件和非垃圾邮件、一个人患病或者不患病,但除此之外也会遇到一些多元分类问题,比方天气能够分为晴、阴、雨、雪等等。

咱们通过算法构建的分类器就以分为二元分类器和多元分类器,前者能够辨别两个类别标签,后者则能够辨别两个以上的类别标签。对于算法而言,像SVM、逻辑回归等是严格的二元分类算法,而像奢侈贝叶斯、随机森林这类算法则能够间接解决多元分类问题。但利用二元分类器解决多分类问题是可行的,上面将以逻辑回归联合鸢尾花数据集为例介绍。

OvA、OvO策略

利用二元分类器解决多分类问题能够分为两种策略:

  • one-versus-all(OvA)策略,也能够称one-versus-rest(OvR),简称一对多。
  • one-versus-one(OvO)策略,简称一对一,应该有人用OvO当过文字表情吧。

用过鸢尾花数据集的搭档应该晓得这份数据集的类别标签共有三类,别离是山鸢尾(setosa)、变色鸢尾(versicolor)和维吉尼亚鸢尾(virginica),因为有三个类别嘛,所以就结构三个二元分类器,假如为山-分类器、变-分类器和维-分类器。训练时将某个类别的样本归为一类,其余类别的样本归为另一类,这样对于某个未知类别的样本,三个分类器都会有一个决策分数(概率),而后取最高决策分数的一个类别作为该样本的类别,这种形式就属于一对多法。

而一对一的做法是构建多个任意两类样本间的二元分类器,原理相似于组队,比方上述三个标签变量能够组成山和变、山和维、变和维,如果类别为n的话,须要的分类器个数为$\frac{n(n-1)}{2}$。最初也是取决策分数最高的一个类别作为某个未知类别的样本最终分类。

从下面介绍中也很容易可得出两者的优缺点:

  • OvA毛病:因为是一个类别对多个类别(1:N的关系),所以在训练时可能会更偏差多类别的一方。
  • OvA长处:假如有n个类别,只须要构建n个分类器。
  • OvO毛病:在标签类别很多的状况下,须要构建很多个二元分类器,不管构建还是训练的过程都比拟麻烦
  • OvO长处:每个分类器只须要在蕴含两个类别的局部数据上训练,无需在整个数据集上。

这两种策略的根本思维都是通过构建多个二元分类器来解决多分类问题。大多数二元分类算法比拟实用OvA策略,当然并不是全副,并且还须要依据数据集的特点而定。上面利用逻辑回归在鸢尾花数据集上建模,因为数据集比较简单,咱们又是只讲这种办法,所以省略掉了剖析之类的操作。

手推实现OvA策略

我集体会习惯将数据集转化为容易察看的DataFrame格局:

import pandas as pdfrom sklearn.datasets import load_irisfeature_names = load_iris().feature_namesdataset_data = pd.DataFrame(load_iris().data,columns=feature_names)dataset_target = pd.DataFrame(load_iris().target,columns=['target'])data = pd.concat([dataset_data,dataset_target],axis = 1)

数据集共有150个样本、四个特色和一个类别标签:

咱们采纳一对多(OvA策略)联合逻辑回归解决这个多分类问题,先介绍一下手推的形式,建模过程与二分类是一样的,只不过咱们须要留神一下OvA策略的思维。

首先须要划分数据集,取七份作为训练集,残余三份为测试集,根底局部就不贴代码啦,文末会给出残缺代码获取形式。OvA策略是有多少个类别就构建多少个分类器,所以须要晓得标签变量所有类别,能够利用unique索引,而后利用字典格局存储所有的分类器。

#获取标签变量的类别unique_targets = data['target'].unique()'''array([0, 1, 2])'''# 采纳OvA策略,三个类别对应三个模型,用字典格局存储models = {}

每一个分类器都会把一个类别归为一类,残余的类别归为另一类,所以这里暂定每次循环和target雷同的为一类,标签设为1,残余的两类标签设为0。为了代码的简洁度,这里利用了管道流将每个分类器和标准化解决封装起来。

y_train_copy = y_train.copy()for target in unique_targets:    #管道流封装    models[target] = make_pipeline(StandardScaler(),LogisticRegression())    y_train_list = y_train_copy.tolist()    # 每次都要批改训练集的标签,将以后类别的标签设为1,其它类别设为0    for i in range(len(y_train_list)):        if y_train_list[i] == target:            y_train_list[i] = 1        else:            y_train_list[i] = 0    y_train = np.array(y_train_list)        models[target].fit(X_train,y_train)

创立相应的分类器之后,上面须要做的就是在测试集上利用,三个分类器最终会失去三个标签的预测概率。

test_probs = pd.DataFrame(columns=unique_targets)for target in unique_targets:    #[:,1]返回的是属于1的概率,[:,0]是属于0的概率    test_probs[target] = models[target].predict_proba(X_test)[:,1]print(test_probs)

能够失去的对于概率的DataFrame如下:

在pandas中有一个idxmax()办法能够索引出一个样本中值最大的列索引,在这里就是一个样本最终被划分的类别。

predicted_target = test_probs.idxmax(axis=1)'''0     01     02     13     04     25     1.......模型错误率为:6.67%'''

最初能够通过和原标签比对计算出模型的准确率,至此就是如何利用二元分类器实现多元分类问题的手推办法。

sklearn调用

在sklearn中也有能够实现OvA和OVO策略的类,和手推办法相比会更加简略便捷,别离为OneVsOneClassifier或OneVsRestClassifier。

from sklearn.multiclass import OneVsOneClassifier,OneVsRestClassifierOvO = OneVsOneClassifier(make_pipeline(StandardScaler(),LogisticRegression()))OvO.fit(X_train,y_train)ovo_predict = OvO.predict(X_test)

对于这类库的调用应该都比拟相熟,不在过多介绍,利用相应的办法能够查看类标签和分类器的个数,3个类别比拟巧,OvA和OvO两个策略所需构建二元分类器的个数都为3个。

print('类别标签有:%s' % OvO.classes_)print('分类器个数:%d' % len(OvO.estimators_))'''类别标签有:[0 1 2]分类器个数:3'''

其实在逻辑回归的multi_class参数中,就有ovr(OvA)这个策略可供选择,然而没有OvO策略。毕竟手推的形式会更容易帮忙了解一个策略的思维,了解之后咱们再调用类或者调参时才会晓得到底做的是什么操作,综上就是对于如何利用二分类算法解决多元分类问题的概述。

参考链接:
[1].https://blog.csdn.net/zm71498...

公众号【奶糖猫】回复关键字"多元分类"可获取文中代码参考