[機器學習-邏輯迴歸]邏輯迴歸(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

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