單標簽二分類問題
單標簽二分類算法原理
單標簽二分類這種問題是我們最常見的算法問題,主要是指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

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_)
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

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

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_)
多標簽算法問題
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方式構建的模型效果要好。
缺點如下: 很難找到一個比較適合的標簽依賴關系。
Calibrated Label Ranking
Calibrated Label Ranking的核心思想是將多標簽分類問題進行分解,將其轉換為標簽的排序問題,最終的標簽就是排序后最大的幾個標簽值。
Calibrated Label Ranking 方式的優點如下:
- 考慮了標簽兩兩組合的情況,最終的模型相對來講泛化能力比較好。
缺點如下:
- 只考慮兩兩標簽的組合,沒有考慮到標簽與標簽之間的所有依賴關系。

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()

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()
Algorithm Adaptation
Algorithm Adaptation又叫做算法適應性策略,是一種將現有的單標簽的算法直接應用到多標簽上的一種方式,主要有以下幾種方式:
k近鄰算法(k-Nearest Neighbour, KNN)的思想:如果一個樣本在特征空間中的k個最相似(即特征空間中距離最近)的樣本中的大多數屬於某一個類別,那么該樣本屬於這個類別。
ML-kNN的思想:對於每一個實例來講,先獲取距離它最近的k個實例,然后使用這些實例的標簽集合,通過最大后驗概率(MAP)來判斷這個實例的預測標簽集合的值。
最大后驗概率(MAP):其實就是在最大似然估計(MLE)中加入了這個要估計量 的先驗概率分布。
ML-DT是使用決策樹處理多標簽內容,核心在於給予更細粒度的信息殤增益准則來構建這個決策樹模型。