機器學習-二分類SVC中的樣本不均衡問題:重要參數class_weight

樣本不均衡問題

對於分類問題,永遠都逃不過的一個痛點就是樣本不均衡問題。樣本不均衡是指在一組數據集中,標籤的一類天生佔有很大的比例,但我們有着捕捉出某種特定的分類的需求的狀況。比如,我們現在要對潛在犯罪者和普通人進行分類,潛在犯罪者佔總人口的比例是相當低的,也許只有2%左右,98%的人都是普通人,而我們的目標是要捕獲出潛在犯罪者。這樣的標籤分佈會帶來許多問題。
首先,分類模型天生會傾向於多數的類,讓多數類更容易被判斷正確,少數類被犧牲掉。因爲對於模型而言,樣本量越大的標籤可以學習的信息越多,算法就會更加依賴於從多數類中學到的信息來進行判斷。如果我們希望捕獲少數類,模型就會失敗。其次,模型評估指標會失去意義。這種分類狀況下,即便模型什麼也不做,全把所有人都當成不會犯罪的人,準確率也能非常高,這使得模型評估指標accuracy變得毫無意義,根本無法達到我們的“要識別出會犯罪的人”的建模目的。
所以現在,我們首先要讓算法意識到數據的標籤是不均衡的,通過施加一些懲罰或者改變樣本本身,來讓模型向着捕獲少數類的方向建模。然後,我們要改進我們的模型評估指標,使用更加針對於少數類的指標來優化模型。
要解決第一個問題,可以採用上採樣下采樣的方法。但這些採樣方法會增加樣本的總數,對於支持向量機這個樣本總是對計算速度影響巨大的算法來說,我們完全不想輕易地增加樣本數量。況且,支持向量機中的決策僅僅受決策邊界的影響,而決策邊界又僅僅受到參數C和支持向量的影響,單純地增加樣本數量不僅會增加計算時間,可能還會增加無數對決策邊界無影響的樣本點。因此在支持向量機中,我們要大力依賴我們調節樣本均衡的參數:SVC類中的class_weight和接口fit中可以設定的sample_weight。
在邏輯迴歸中,參數class_weight默認None,此模式表示假設數據集中的所有標籤是均衡的,即自動認爲標籤的比例是1:1。所以當樣本不均衡的時候,我們可以使用形如{“標籤的值1”:權重1,“標籤的值2”:權重2}的字典來輸入真實的樣本標籤比例,來讓算法意識到樣本是不平衡的。或者使
用”balanced“模式,直接使用n_samples/(n_classes * np.bincount(y))作爲權重,可以比較好地修正我們的樣本不均衡情況。
但在SVM中,我們的分類判斷是基於決策邊界的,而最終決定究竟使用怎樣的支持向量和決策邊界的參數是參數C,所以所有的樣本均衡都是通過參數C來調整的。

SVC的參數:class_weight

可輸入字典或者"balanced”,可不填,默認None
對SVC,將類i的參數C設置爲class_weight [i] * C。如果沒有給出具體的class_weight,則所有類都被假設爲佔有相同的權重1,模型會根據數據原本的狀況去訓練。如果希望改善樣本不均衡狀況,請輸入形如{“標籤的值1”:權重1,“標籤的值2”:權重2}的字典,則參數C將會自動被設爲:
標籤的值1的C:權重1 * C,標籤的值2的C:權重2*C
或者,可以使用“balanced”模式,這個模式使用y的值自動調整與輸入數據中的類頻率成反比的權重爲n_samples/(n_classes * np.bincount(y))

SVC的接口fit的參數:sample_weight

數組,結構爲 (n_samples, ),必須對應輸入fit中的特徵矩陣的每個樣本。
每個樣本在fit時的權重,讓權重 * 每個樣本對應的C值來迫使分類器強調設定的權重更大的樣本。通常,較大的權重加在少數類的樣本上,以迫使模型向着少數類的方向建模。
通常來說,這兩個參數我們只選取一個來設置。如果我們同時設置了兩個參數,則C會同時受到兩個參數的影響,即 class_weight中設定的權重 * sample_weight中設定的權重 * C。
我們接下來就來看看如何使用這個參數。
首先,我們來自建一組樣本不平衡的數據集。我們在這組數據集上建兩個SVC模型,一個設置有class_weight參數,一個不設置class_weight參數。我們對兩個模型分別進行評估並畫出他們的決策邊界,以此來觀察class_weight帶來的效果。

  1. 導入需要的庫和模塊
import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC
from sklearn.datasets import make_blobs
  1. 創建樣本不均衡的數據集
class_1 = 500 #類別1有500個樣本
class_2 = 50 #類別2只有50個
centers = [[0.0, 0.0], [2.0, 2.0]] #設定兩個類別的中心
clusters_std = [1.5, 0.5] #設定兩個類別的方差,通常來說,樣本量比較大的類別會更加鬆散
X, y = make_blobs(n_samples=[class_1, class_2],
                  centers=centers,
                  cluster_std=clusters_std,
                  random_state=0, shuffle=False)

#看看數據集長什麼樣
plt.scatter(X[:, 0], X[:, 1], c=y, cmap="rainbow",s=10);
#其中紅色點是少數類,紫色點是多數類

在這裏插入圖片描述
3. 在數據集上分別建模

#不設定class_weight
clf = SVC(kernel='linear', C=1.0)
clf.fit(X, y)
#設定class_weight
wclf = SVC(kernel='linear', class_weight={1: 10})
wclf.fit(X, y)
#給兩個模型分別打分看看,這個分數是accuracy準確度
clf.score(X,y)
0.9418181818181818
wclf.score(X,y)
0.9127272727272727

可以看出,從準確率的角度來看,不做樣本平衡的時候準確率反而更高,做了樣本平衡準確率反而變低了,這是因爲做了樣本平衡後,爲了要更有效地捕捉出少數類,模型誤傷了許多多數類樣本,而多數類被分錯的樣本數量 > 少數類被分類正確的樣本數量,使得模型整體的精確性下降。現在,如果我們的目的是模型整體的準確率,那我們就要拒絕樣本平衡,使class_weight被設置之前的模型。
然而在現實中,我們往往都在追求捕捉少數類,因爲在很多情況下,將少數類判斷錯的代價是巨大的。比如我們之前提到的,判斷潛在犯罪者和普通人的例子,如果我們沒有能夠識別出潛在犯罪者,那麼這些人就可能去危害社會,造成惡劣影響,但如果我們把普通人錯認爲是潛在犯罪者,我們也許只是需要增加一些監控和人爲甄別的成本。所以對我們來說,我們寧願把普通人判錯,也不想放過任何一個潛在犯罪者。我們希望不惜一切代價來捕獲少數類,或者希望捕捉出盡量多的少數類,那我們就必須使用class_weight設置後的模型。

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