多分類及多標簽分類算法


單標簽二分類問題

單標簽二分類算法原理

單標簽二分類這種問題是我們最常見的算法問題,主要是指label標簽的取值只有兩種,並且算法中只有一個需要預測的label標簽;直白來講就是每個實例的可能類別只有兩種(A or B);此時的分類算法其實是在構建一個分類線將數據划分為兩個類別。常見的算法:Logistic、SVM、KNN、決策樹等。

Logistic算法原理

單標簽多分類問題

單標簽多分類問題其實是指待預測的label標簽只有一個,但是label標簽的取值可能有多種情況;直白來講就是每個實例的可能類別有K種(t1 ,t2 ,...tk ,k≥3);常見 算法:Softmax、KNN、決策樹等;

 

在實際的工作中,如果是一個多分類的問題,我們可以將這個待求解的問題轉換 為二分類算法的延伸,即將多分類任務拆分為若干個二分類任務求解,具體的策 略如下:

  • One-Versus-One(ovo):一對一
  • One-Versus-All / One-Versus-the-Rest(ova/ovr): 一對多
  • Error Correcting Output codes(糾錯碼機制):多對多

ovo

原理:將K個類別中的兩兩類別數據進行組合,然后使用組合后的數據訓練出來一個模型,從而產生K(K-1)/2個分類器,將這些分類器的結果進行融合,並將分類器的預測結果使用多數投票的方式輸出最終的預測結果值。

def ovo(datas,estimator):
    '''datas[:,-1]為目標屬性'''
    import numpy as np
    Y = datas[:,-1]
    X = datas[:,:-1]
    y_value = np.unique(Y)

    #計算類別數目
    k = len(y_value)
    modles = []
    #將K個類別中的兩兩類別數據進行組合,並對y值進行處理
    for i in range(k-1):
        c_i = y_value[i]
        for j in range(i+1,k):
            c_j = y_value[j]
            new_datas = []
            for x,y in zip(X,Y):
                if y == c_i or y == c_j:
                    new_datas.append(np.hstack((x,np.array([2*float(y==c_i)-1]))))
            new_datas = np.array(new_datas)
            algo = estimator()
            modle = algo.fit(new_datas)
            modles.append([(c_i,c_j),modle])
    return modles
def argmaxcount(seq):
    '''計算序列中出現次數最多元素'''
    '''超極簡單的方法'''
    # from collections import Counter
    # return Counter(seq).values[0]

    '''稍微復雜的'''
    # dict_num = {}
    # for item in seq:
    #     if item not in dict_num.keys():
    #         dict_num[item] = seq.count(item)
    # # 排序
    # import operator
    # sorted(dict_num.items(), key=operator.itemgetter(1))

    '''字典推導'''
    dict_num = dict_num = {i: seq.count(i) for i in set(seq)}

def ovo_predict(X,modles):
    import operator
    result = []
    for x in X:
        pre = []
        for cls,modle in modles:
            pre.append(cls[0] if modle.predict(x) else cls[1])
        d = {i: pre.count(i) for i in set(pre)} #利用集合的特性去重
        result.append(sorted(d.items(),key=operator.itemgetter(1))[-1][0])
    return result

https://scikit-learn.org/0.19/modules/generated/sklearn.multiclass.OneVsOneClassifier.html#sklearn.multiclass.OneVsOneClassifier

from sklearn import datasets
from sklearn.multiclass import OneVsOneClassifier
from sklearn.svm import LinearSVC
from sklearn.neighbors import KNeighborsClassifier

# 加載數據
iris = datasets.load_iris()

# 獲取X和y
X, y = iris.data, iris.target
print("樣本數量:%d, 特征數量:%d" % X.shape)
# 設置為3,只是為了增加類別,看一下ovo和ovr的區別
y[-1] = 3

# 模型構建
clf = OneVsOneClassifier(LinearSVC(random_state=0))
# clf = OneVsOneClassifier(KNeighborsClassifier())
# 模型訓練
clf.fit(X, y)

# 輸出預測結果值
print(clf.predict(X))
print("效果:{}".format(clf.score(X, y)))

# 模型屬性輸出
k = 1
for item in clf.estimators_:
    print("第%d個模型:" % k, end="")
    print(item)
    k += 1
print(clf.classes_)
View Code

ovr

原理:在一對多模型訓練中,不是兩兩類別的組合,而是將每一個類別作為正例,其它剩余的樣例作為反例分別來訓練K個模型;然后在預測的時候,如果在這K個模型中,只有一個模型輸出為正例,那么最終的預測結果就是屬於該分類器的這個類別;如果產生多個正例,那么則可以選擇根據分類器的置信度作為指標,來選擇置信度最大的分類器作為最終結果,常見置信度:精確度、召回率

def ovr(datas,estimator):
    '''datas[:,-1]為目標屬性'''
    import numpy as np
    Y = datas[:,-1]
    X = datas[:,:-1]
    y_value = np.unique(Y)

    #計算類別數目
    k = len(y_value)
    modles = []
    #准備K個模型的訓練數據,並對y值進行處理
    for i in range(k):
        c_i = y_value[i]
        new_datas = []
        for x,y in zip(X,Y):
            new_datas.append(np.hstack((x,np.array([2*float(y==c_i)-1]))))
        new_datas = np.array(new_datas)
        algo = estimator()
        modle = algo.fit(new_datas)
        confidence = modle.score(new_datas) #計算置信度
        modles.append([(c_i,confidence),modle])
    return modles

def ovr_predict(X,modles):
    import operator
    result = []
    for x in X:
        pre = []
        cls_confi = []
        for cls,modle in modles:
            cls_confi.append(cls)
            pre.append(modle.predict(x))
        pre_res = []
        for c,p in zip(cls_confi,pre):
            if p == 1:
                pre_res.append(c)
        if not pre_res:
            pre_res = cls_confi
        result.append(sorted(pre_res,key=operator.itemgetter(1))[-1][0])
    return result

https://scikit-learn.org/0.19/modules/generated/sklearn.multiclass.OneVsRestClassifier.html#sklearn.multiclass.OneVsRestClassifier

from sklearn import datasets
from sklearn.multiclass import OneVsRestClassifier
from sklearn.svm import LinearSVC
from sklearn.metrics import accuracy_score

# 數據獲取
iris = datasets.load_iris()
X, y = iris.data, iris.target
print("樣本數量:%d, 特征數量:%d" % X.shape)
# 設置為3,只是為了增加類別,看一下ovo和ovr的區別
y[-1] = 3

# 模型創建
clf = OneVsRestClassifier(LinearSVC(random_state=0))
# 模型構建
clf.fit(X, y)

# 預測結果輸出
print(clf.predict(X))

# 模型屬性輸出
k = 1
for item in clf.estimators_:
    print("第%d個模型:" % k, end="")
    print(item)
    k += 1
print(clf.classes_)
View Code

OvO和OvR的區別

Error Correcting

原理:將模型構建應用分為兩個階段:編碼階段和解碼階段;編碼階段中對K個類別中進行M次划分,每次划分將一部分數據分為正類,一部分數據分為反類,每次划分都構建出來一個模型,模型的結果是在空間中對於每個類別都定義了一個點;解碼階段中使用訓練出來的模型對測試樣例進行預測,將預測樣本對應的點和類別之間的點求距離,選擇距離最近的類別作為最終的預測類別。

https://scikit-learn.org/0.19/modules/generated/sklearn.multiclass.OutputCodeClassifier.html#sklearn.multiclass.OutputCodeClassifier

from sklearn import datasets
from sklearn.multiclass import OutputCodeClassifier
from sklearn.svm import LinearSVC
from sklearn.metrics import accuracy_score

# 數據獲取
iris = datasets.load_iris()
X, y = iris.data, iris.target
print("樣本數量:%d, 特征數量:%d" % X.shape)

# 模型對象創建
# code_size: 指定最終使用多少個子模型,實際的子模型的數量=int(code_size*label_number)
# code_size設置為1,等價於ovr子模型個數;
# 設置為0~1, 那相當於使用比較少的數據划分,效果比ovr差; 
# 設置為大於1的值,那么相當於存在部分模型冗余的情況
clf = OutputCodeClassifier(LinearSVC(random_state=0), code_size=30, random_state=0)
# 模型構建
clf.fit(X, y)

# 輸出預測結果值
print(clf.predict(X))
print("准確率:%.3f" % accuracy_score(y, clf.predict(X)))

# 模型屬性輸出
k = 1
for item in clf.estimators_:
    print("第%d個模型:" % k, end="")
    print(item)
    k += 1
print(clf.classes_)
View Code

多標簽算法問題

 Multi-Label Machine Learning(MLL算法)是指預測模型中存在多個y值,具體分為兩類不同情況:

  • 多個待預測的y值;
  • 在分類模型中, 一個樣例可能存在多個不固定的類別。

根據多標簽業務問題的復雜性,可以將問題分為兩大類:

  • 待預測值之間存在相互的依賴關系;
  • 待 預測值之間是不存在依賴關系的。

對於這類問題的解決方案可以分為兩大類:

  • 轉換策略(Problem Transformation Methods);
  • 算法適應(Algorithm Adaptation)。

注意:在多標簽中一般認為每個標簽只有兩個類別,即(+1,和 - 1),如果一個標簽有多個類別,則需要把類別分解成取值為+1或者- 1的新標簽。

Problem Transformation Methods

Problem Transformation Methods又叫做策略轉換或者問題轉換,是一種將多標簽的分類問題轉換成為單標簽模型構造的問題,然后將模型合並的一種方式,主要有以下幾種方式:

  • Binary Relevance(first-order)
  • Classifier Chains(high-order)
  • Calibrated Label Ranking(second-order)

Binary Relevance

Binary Relevance的核心思想是將多標簽分類問題進行分解,將其轉換為q個二元分類問題,其中每個二元分類器對應一個待預測的標簽

def Binary_Relevance(X,Y,estimator):
    '''Y是一個只有0和1的數組'''
    import numpy as np

    # 計算標簽的個數
    q = Y.shape[1]
    Y_label = [i for i in range(q)]

    modles = []
    #准備K個模型的訓練數據,並對y值進行處理
    for j in Y_label:
        D_j = []
        for x,y in zip(X,Y):
            D_j.append(np.hstack((x,np.array([1 if j in Y_label[y==1] else -1]))))
        new_datas = np.array(D_j)
        algo = estimator()
        g_j = algo.fit(new_datas)
        modles.append(g_j)

    # Y = Y.replace(0,-1) #把所有的0替換成-1
    # for j in Y_label:
    #     new_datas = np.hstack((X,Y[:,j].reshape(-1,1)))
    #     new_datas = np.array(new_datas)
    #     algo = estimator()
    #     g_j = algo.fit(new_datas)
    #     modles.append(g_j)

    return modles

def Binary_Relevance_predict(X,modles,label_name):
    import operator
    result = []
    for x in X:
        pre_res = []
        for g_j in modles:
            pre_res.append(g_j.predict(x))
        Y = set(np.array(label_name)[np.array(pre_res)>0]).union(label_name[pre_res.index(max(pre_res))])
        result.append(Y)
    return result

Binary Relevance方式的優點如下:

  • 實現方式簡單,容易理解;
  • 當y值之間不存在相關的依賴關系的時候,模型的效果不錯。

缺點如下:

  • 如果y直接存在相互的依賴關系,那么最終構建的模型的泛化能力比較弱;
  • 需要構建q個二分類器,q為待預測的y值數量,當q比較大的時候,需要構建的模型 會比較多。

Classifier Chains

Classifier Chains的核心思想是將多標簽分類問題進行分解,將其轉換成為一個二元分類器鏈的形式,其中鏈后的二元分類器的構建是在前面分類器預測結果的基礎上的。在模型構建的時候,首先將標簽順序進行shuffle打亂排序操作,然后按照從頭到尾分別構建每個標簽對應的模型。

Classifier Chains模型構建

Classifier Chains模型預測

Classifier Chains方式的優點如下:

  • 實現方式相對比較簡單,容易理解;
  • 考慮標簽之間的依賴關系,最終模型的泛化能力相對於Binary Relevance方式構建的模型效果要好。

缺點如下: 很難找到一個比較適合的標簽依賴關系。

https://scikit-learn.org/0.19/modules/generated/sklearn.multioutput.ClassifierChain.html#sklearn.multioutput.ClassifierChain

Calibrated Label Ranking

Calibrated Label Ranking的核心思想是將多標簽分類問題進行分解,將其轉換為標簽的排序問題,最終的標簽就是排序后最大的幾個標簽值。

Calibrated Label Ranking 方式的優點如下:

  • 考慮了標簽兩兩組合的情況,最終的模型相對來講泛化能力比較好。

缺點如下:

  • 只考慮兩兩標簽的組合,沒有考慮到標簽與標簽之間的所有依賴關系。

https://scikit-learn.org/0.19/modules/generated/sklearn.multioutput.MultiOutputClassifier.html#sklearn.multioutput.MultiOutputClassifier

import numpy as np
import matplotlib.pyplot as plt

from sklearn.datasets import make_multilabel_classification
from sklearn.multioutput import MultiOutputClassifier
from sklearn.svm import SVC
from sklearn.preprocessing import LabelBinarizer
from sklearn.decomposition import PCA

def plot_hyperplane(clf, min_x, max_x, linestyle, label):
    # 畫圖
    w = clf.coef_[0]
    a = -w[0] / w[1]
    xx = np.linspace(min_x - 5, max_x + 5)  
    yy = a * xx - (clf.intercept_[0]) / w[1]
    plt.plot(xx, yy, linestyle, label=label)

def plot_subfigure(X, Y, subplot, title):
    # 將X進行降維操作,變成兩維的數據
    X = PCA(n_components=2).fit_transform(X)
    
    min_x = np.min(X[:, 0])
    max_x = np.max(X[:, 0])

    min_y = np.min(X[:, 1])
    max_y = np.max(X[:, 1])

    classif = MultiOutputClassifier(SVC(kernel='linear'))
    classif.fit(X, Y)

    plt.subplot(2, 2, subplot)
    plt.title(title)

    zero_class = np.where(Y[:, 0])
    one_class = np.where(Y[:, 1])
    plt.scatter(X[:, 0], X[:, 1], s=40, c='gray')
    plt.scatter(X[zero_class, 0], X[zero_class, 1], s=160, edgecolors='b',
               facecolors='none', linewidths=2, label='Class 1')
    plt.scatter(X[one_class, 0], X[one_class, 1], s=80, edgecolors='orange',
               facecolors='none', linewidths=2, label='Class 2')

    plot_hyperplane(classif.estimators_[0], min_x, max_x, 'r--',
                    'Boundary\nfor class 1')
    plot_hyperplane(classif.estimators_[1], min_x, max_x, 'k-.',
                    'Boundary\nfor class 2')
    plt.xticks(())
    plt.yticks(())

    plt.xlim(min_x - .5 * max_x, max_x + .5 * max_x)
    plt.ylim(min_y - .5 * max_y, max_y + .5 * max_y)
    if subplot == 1:
        plt.xlabel('First principal component')
        plt.ylabel('Second principal component')
        plt.legend(loc="upper left")

plt.figure(figsize=(8, 6))

X, Y = make_multilabel_classification(n_classes=2,
                                      allow_unlabeled=False, # 該參數控制是否有類別缺省的數據,False表示沒有
                                      random_state=1)

plot_subfigure(X, Y, 1, "With unlabeled samples + CCA")


plt.subplots_adjust(.04, .02, .97, .94, .09, .2)
plt.show()
View Code
import numpy as np
import matplotlib.pyplot as plt

from sklearn.datasets import make_multilabel_classification
from sklearn.multiclass import OneVsRestClassifier
from sklearn.svm import SVC
from sklearn.preprocessing import LabelBinarizer
from sklearn.decomposition import PCA

def plot_hyperplane(clf, min_x, max_x, linestyle, label):
    # 畫圖
    w = clf.coef_[0]
    a = -w[0] / w[1]
    xx = np.linspace(min_x - 5, max_x + 5)  
    yy = a * xx - (clf.intercept_[0]) / w[1]
    plt.plot(xx, yy, linestyle, label=label)

def plot_subfigure(X, Y, subplot, title):
    # 將X進行降維操作,變成兩維的數據
    X = PCA(n_components=2).fit_transform(X)
    min_x = np.min(X[:, 0])
    max_x = np.max(X[:, 0])

    min_y = np.min(X[:, 1])
    max_y = np.max(X[:, 1])

    classif = OneVsRestClassifier(SVC(kernel='linear'))
    classif.fit(X, Y)

    plt.subplot(2, 2, subplot)
    plt.title(title)

    zero_class = np.where(Y[:, 0])
    one_class = np.where(Y[:, 1])
    plt.scatter(X[:, 0], X[:, 1], s=40, c='gray')
    plt.scatter(X[zero_class, 0], X[zero_class, 1], s=160, edgecolors='b',
               facecolors='none', linewidths=2, label='Class 1')
    plt.scatter(X[one_class, 0], X[one_class, 1], s=80, edgecolors='orange',
               facecolors='none', linewidths=2, label='Class 2')

    plot_hyperplane(classif.estimators_[0], min_x, max_x, 'r--',
                    'Boundary\nfor class 1')
    plot_hyperplane(classif.estimators_[1], min_x, max_x, 'k-.',
                    'Boundary\nfor class 2')
    plt.xticks(())
    plt.yticks(())

    plt.xlim(min_x - .5 * max_x, max_x + .5 * max_x)
    plt.ylim(min_y - .5 * max_y, max_y + .5 * max_y)
    if subplot == 1:
        plt.xlabel('First principal component')
        plt.ylabel('Second principal component')
        plt.legend(loc="upper left")


plt.figure(figsize=(8, 6))

X, Y = make_multilabel_classification(n_classes=2, n_labels=1,
                                      allow_unlabeled=False, # 該參數控制是否有類別缺省的數據,False表示沒有
                                      random_state=1)

plot_subfigure(X, Y, 1, "With unlabeled samples + CCA")


plt.subplots_adjust(.04, .02, .97, .94, .09, .2)
plt.show()
多標簽分類問題(OneVsRestClassifier)

Algorithm Adaptation

Algorithm Adaptation又叫做算法適應性策略,是一種將現有的單標簽的算法直接應用到多標簽上的一種方式,主要有以下幾種方式:

k近鄰算法(k-Nearest Neighbour, KNN)的思想:如果一個樣本在特征空間中的k個最相似(即特征空間中距離最近)的樣本中的大多數屬於某一個類別,那么該樣本屬於這個類別。

ML-kNN的思想:對於每一個實例來講,先獲取距離它最近的k個實例,然后使用這些實例的標簽集合,通過最大后驗概率(MAP)來判斷這個實例的預測標簽集合的值。

最大后驗概率(MAP):其實就是在最大似然估計(MLE)中加入了這個要估計量 的先驗概率分布。

ML-DT是使用決策樹處理多標簽內容,核心在於給予更細粒度的信息殤增益准則來構建這個決策樹模型。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM