[机器学习-逻辑回归]逻辑回归(LogisticRegression)多分类(OvR, OvO, MvM)

前言

逻辑回归分类器(Logistic Regression Classifier)是机器学习领域著名的分类模型。其常用于解决二分类(Binary Classification)问题。
利用二分类学习器进行的多分类学习可以分为三种策略:

一对一 (One vs. One, 简称OvO)
一对其余 (One vs. Rest,简称 OvR)也可是OvA(One vs. All)但是不严格
多对多(Many vs. Many,简称 MvM)

One-VS-Rest

假设我们要解决一个分类问题,该分类问题有三个类别,分别用△,□和×表示,每个实例(Entity)有两个属性(Attribute),如果把属性 1 作为 X 轴,属性 2 作为 Y 轴,训练集(Training Dataset)的分布可以表示为下图:

One-Vs-Rest 的思想是把一个多分类的问题变成多个二分类的问题。转变的思路就如同方法名称描述的那样,选择其中一个类别为正类(Positive),使其他所有类别为负类(Negative)。比如第一步,我们可以将三角形所代表的实例全部视为正类,其他实例全部视为负类,得到的分类器如图:

同理我们把 X 视为正类,其他视为负类,可以得到第二个分类器:

最后,第三个分类器是把正方形视为正类,其余视为负类:

对于一个三分类问题,我们最终得到 3 个二元分类器。在预测阶段,每个分类器可以根据测试样本,得到当前正类的概率。即 P(y = i | x; θ),i = 1, 2, 3。选择计算结果最高的分类器,其正类就可以作为预测结果。

One-Vs-Rest 最为一种常用的二分类拓展方法,其优缺点也十分明显。

优点:普适性还比较广,可以应用于能输出值或者概率的分类器,同时效率相对较好,有多少个类别就训练多少个分类器。

缺点:很容易造成训练集样本数量的不平衡(Unbalance),尤其在类别较多的情况下,经常容易出现正类样本的数量远远不及负类样本的数量,这样就会造成分类器的偏向性。

One-Vs-One

相比于 One-Vs-Rest 由于样本数量可能的偏向性带来的不稳定性,One-Vs-One 是一种相对稳健的扩展方法。对于同样的三分类问题,我们像举行车轮作战一样让不同类别的数据两两组合训练分类器,可以得到 3 个二元分类器。

它们分别是三角形与 x 训练得出的分类器:

三角形与正方形训练的出的分类器:

以及正方形与 x 训练得出的分类器:

假如我们要预测的一个数据在图中红色圆圈的位置,那么第一个分类器会认为它是 x,第二个分类器会认为它偏向三角形,第三个分类器会认为它是 x,经过三个分类器的投票之后,可以预测红色圆圈所代表的数据的类别为 x。

任何一个测试样本都可以通过分类器的投票选举出预测结果,这就是 One-Vs-One 的运行方式。

当然这一方法也有显著的优缺点,其缺点是训练出更多的 Classifier,会影响预测时间。

虽然在本文的例子中,One-Vs-Rest 和 One-Vs-One 都得到三个分类器,但实际上仔细思考就会发现,如果有 k 个不同的类别,对于 One-Vs-Rest 来说,一共只需要训练 k 个分类器,而 One-Vs-One 则需训练 C(k, 2) 个分类器,只是因为在本例种,k = 3 时恰好两个值相等,一旦 k 值增多,One-Vs-One 需要训练的分类器数量会大大增多。

当然 One-Vs-One 的优点也很明显,它在一定程度上规避了数据集 unbalance 的情况,性能相对稳定,并且需要训练的模型数虽然增多,但是每次训练时训练集的数量都降低很多,其训练效率会提高。

比较 OvO 和 OvR

在这里插入图片描述

容易看出,OvR只需训练N个分类器,而OvO则需要训练N(N-1)/2个分类器,因此,OvO的存储开销和测试时间开销通常比OvR更大。但在训练时,OvR的每个分类器均使用全部的训练样例,而OvO的每个分类器仅用到两个类的样例,因此,在类别很多的时候,OvO的训练时间开销通常比OvR更小。至于预测性能,则取决于具体的数据分布,在多数情况下两者差不多。

多对多 (Many vs Many)

多对多是每次将若干类作为正例,若干其他类作为负例。MvM的正反例构造有特殊的设计,不能随意选取。我们这里介绍一种常用的MvM技术:纠错输出码(EOOC)。

  • 编码:对N个类做M次划分,每次划分将一部分类别划分为正例,一部分划分为反例,从而形成一个二分类的训练集:这样共有M个训练集,则可训练出M个分类器。
  • 解码:M个分类器分别对测试样本进行预测,这些预测样本组成一个编码。将这个编码与每个类各自的编码进行比较,返回其中距离最小的类别作为最终预测结果。

类别划分通过"编码矩阵" (coding matrix) 指定.编码矩阵有多种形式,常见的主要有二元码 [Dietterich and iri 1995] 和三元码 [Allwein et al.,2000]. 前者将每个类别分别指定为正类和反类,**后者在正、反类之外,还可指定"停用类"**因 3.5 给出了一个示意图,在图 3.5(a) 中,分类器 Cl 类和C3 类的样例作为正例 C2 类和 C4 类的样例作为反例;在图 3.5(b) 中,分类器14 类和 C4 类的样例作为正例 C3 类的样例作为反例.在解码阶段,各分类器的预测结果联合起来形成了测试示例的编码,该编码与各类所对应的编码进行比较?将距离最小的编码所对应的类别作为预测结果.例如在图 3.5(a) 中,若基于欧民距离,预测结果将是 C3.

在这里插入图片描述
为什么要用纠错输出码呢?因为在测试阶段,ECOC编码对分类器的错误有一定的容忍和修正能力。例如上图中对测试示例正确的预测编码是(-1,1,1,-1,1),但在预测时f2出错从而导致了错误的编码(-1, -1, 1, -1,1)。但是基于这个编码仍然能产生正确的最终分类结果C3。

过拟合问题

解决过拟合问题的方法有二:

  • 减少feature的数量,人工定义留多少feature,算法选取这些feature
  • 规格化(regularization)

类别不平衡问题

如果我们的训练集有1000条数据,其中正例2,负例998。那么学习方法只需要返回一个永远将新样本预测为反例的学习器,就能达到99.8%的精度。但是这样的学习算法没有价值,因为它不能预测出任何正例。

类别不平衡问题是指分类任务中不同类别的训练例数相差很大的情况。在现实生活中,我们经常遇到类别不平衡,例如通过拆分法解决多分类问题时,即原始问题中不同类别的训练样例数目相当,在使用OvR,MvM策略后,二分类任务仍然可能类别不平衡。

处理这个问题通常有3个方法

  • 欠采样。直接对训练集里反例(当前假定反例数目远大于正例)删除一部分,直到正例和反例的数目相当。但是这样可能会丢失一些重要信息。欠采样的代表性算法是利用集成学习机制,将反例划分为若干个集合供不同的学习器使用。这样对每个学习器都进行了欠采样,但在全局来看却不会丢掉重要信息。
  • 过采样。对训练集里的正例进行过采样。增加一些正例使得正反例数目接近。但是不能直接对正例进行复制。这样容易引起过拟合。一般的采用代表性算法SMOTE算法。它是通过对训练集里的正例进行插值来产生额外的正例。
    基于原始训练集进行学习。
    令y表示正例的可能性,y1y\frac{y}{1−y}则反映了正例和反例可能性的比值。阈值设为0.5表明分类器认为正例,负例的可能性相同。即分类器规则为

y1y\frac{y}{1−y}则预测为正例
然而,当训练集中正例和负例的数目不同时,令m+m^+表示正例的数目,mm^−表示负例数目,则观察机率是m+m\frac{m+}{m−},由于我们通常假设训练集是真实样本总体的无偏差采样,因此观察机率代表了真实机率。于是,只要分类器的预测机率高于观察机率就应判定为正例。即

y1y>m+m\frac{y}{1−y}>\frac{m+}{m−}则预测为正例

但是我们的分类器是基于第一个式子进行决策,因此,需对其预测值进行调整,使其执行第一个式子的时候,实际上是在执行第二个式子。那么只需令
在这里插入图片描述
这就是类别不平衡的一个基本策略—再缩放。

Sklearn实践案例

数据集如图,怎么用OvR或MvM来线性分割并分类呢?

如图, 左边是用OVR,右边是MvM

代码如下
如果你的数据集类型是大于两类的,那么Sklearn会自动把multi_class赋值为multinomial,如果不是就是ovr 所以一般我们不需要指定multi_class和solver参数

from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
import numpy as np

cluster = 3
#cluster = 4
# 生成三类数据
def get_data():
    data, target = make_blobs(n_samples=18, n_features=2, centers=[[1, 5], [0.5, 0.5], [2.5, 3]], cluster_std=0.3)
    #data, target = make_blobs(n_samples=24, n_features=2, centers=[[1, 5], [0.5, 0.5], [2.5, 3], [2, 2.5]],
    #                          cluster_std=0.3)
    print(data)
    print(target)
    return data, target

#用逻辑回归的MvR拟合数据
def fit_ovr(X, y):
    clf = LogisticRegression(random_state=0, multi_class= 'ovr', solver='liblinear').fit(X, y)
    predict_Result = clf.predict(X[-2:, :])
    print("predict_Result", predict_Result, y[-2:])
    predict_proba = clf.predict_proba(X[-2:, :])
    print("predict_proba", predict_proba, y[-2:])
    score = clf.score(X, y)
    print(score)
    return clf

# 用逻辑回归的MvM拟合数据
def fit_mvm(X, y):
    clf = LogisticRegression(random_state=0, multi_class= 'multinomial', solver='newton-cg').fit(X, y)
    predict_Result = clf.predict(X[-2:, :])
    print("predict_Result", predict_Result, y[-2:])
    predict_proba = clf.predict_proba(X[-2:, :])
    print("predict_proba", predict_proba, y[-2:])
    score = clf.score(X, y)
    print(score)
    return clf


# 展示散点和拟合的三条直线
def show_pic(X, y, clf=None):
    # 在2D图中绘制样本,每个样本颜色不同,形状不同
    markers = ['s', '^', 'x','o']
    for i in range(cluster):
        temp = X[y == i]
        plt.scatter(temp[:, 0], temp[:, 1], marker=markers[i]);

    if clf is not None:
        print(clf.coef_)
        print(clf.intercept_)
        tx = np.arange(0, 4, 1)
        for i in range(len(clf.intercept_)):
            ty1 = -((tx * clf.coef_[i, 0] + clf.intercept_[i]) / clf.coef_[i, 1])
            print('ty'+str(i), ty1)
            plt.plot(tx, ty1)

    plt.xlim(0, 3)
    plt.ylim(0, 6)
    plt.show()


if __name__ == '__main__':
    X, y = get_data()
    clf1 = fit_ovr(X, y)
    show_pic(X, y, clf1)

    clf2 = fit_mvm(X, y)
    show_pic(X, y, clf2)
[[0.77160149 0.3430395 ]
 [1.57129503 5.60716134]
 [2.53673609 2.62478397]
 [0.89577419 4.9270717 ]
 [0.51794955 0.18661237]
 [2.41047464 3.6618884 ]
 [0.80903951 0.76956997]
 [1.01332545 4.8614653 ]
 [0.0870957  0.26844336]
 [0.73336831 4.89287325]
 [0.46639542 0.36812355]
 [2.37135768 3.23834971]
 [2.43208844 2.58746536]
 [0.82033918 4.19698766]
 [0.34178463 0.37697826]
 [2.66906683 3.43627625]
 [2.36159244 2.95517699]
 [0.8219321  4.89461876]]
[1 0 2 0 1 2 1 0 1 0 1 2 2 0 1 2 2 0]
predict_Result [2 0] [2 0]
predict_proba [[0.12760673 0.08798862 0.78440465]
 [0.87863131 0.01588694 0.10548175]] [2 0]
1.0
[[-1.5378318   0.92912115]
 [-0.23596954 -1.06787137]
 [ 1.54610956 -0.41402559]]
[-1.09527836  1.31805429 -1.35613632]
ty0 [1.17883267 2.83397938 4.48912608 6.14427278]
ty1 [1.23428189 1.01331001 0.79233813 0.57136625]
ty2 [-3.27548913  0.45884419  4.19317751  7.92751083]
predict_Result [2 0] [2 0]
predict_proba [[0.08008775 0.05752575 0.86238651]
 [0.93175172 0.00943905 0.05880923]] [2 0]
1.0
[[-0.70309501  1.05753573]
 [-0.53744439 -1.00814914]
 [ 1.2405394  -0.0493866 ]]
[-2.14667824  3.23569669 -1.08901845]
ty0 [2.02988719 2.69472998 3.35957277 4.02441556]
ty1 [3.2095417  2.67644161 2.14334152 1.61024143]
ty2 [-22.05089009   3.06805811  28.18700631  53.30595451]

总结

MvM 比OvR 的准确率高,如果你把上面的代码的cluster = 4和生成四个cluster的代码放开的执行就可以看出来了

参考
https://blog.csdn.net/siyue0211/article/details/80522595

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章