【火爐煉AI】機器學習015-如何處理樣本數偏差較大的資料集

煉丹老頑童發表於2019-03-03

【火爐煉AI】機器學習015-如何處理樣本數偏差較大的資料集

(本文所使用的Python庫和版本號: Python 3.5, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 )

我們得到的資料集在絕大多數情況下,都不是理想的資料集,都需要經過各種各樣的處理,其中的一個處理方式就是,如何處理樣本數偏差較大的資料集。比如對於某種疾病的發生概率是1%,即獲得的自然狀態下的資料集中大約99%的樣本都是正常的,那麼此時,通過模型進行訓練,得到的模型在對新樣本進行預測時,也往往偏向於樣本數較大的類別,此時或極大的降低小樣本類別的召回率。

1. 檢視資料集的特徵

此處所使用的是書本《Python機器學習經典例項》第三章的非平衡資料集,此處我用pandas讀取到記憶體中,通過info()來檢視該資料集的基本情況,程式碼如下:

# 準備資料集
data_path=`E:PyProjectsDataSetFireAI/data_multivar_imbalance.txt`
df=pd.read_csv(data_path,header=None)
# print(df.head()) # 沒有問題
print(df.info()) # 檢視資料資訊,確保沒有錯誤
dataset_X,dataset_y=df.iloc[:,:-1],df.iloc[:,-1]
# print(dataset_X.head())
# print(dataset_X.info())
# print(dataset_y.head()) # 檢查沒問題
dataset_X=dataset_X.values
dataset_y=dataset_y.values
複製程式碼

———————————-輸———出————————

<class `pandas.core.frame.DataFrame`>
RangeIndex: 1200 entries, 0 to 1199
Data columns (total 3 columns):
0 1200 non-null float64
1 1200 non-null float64
2 1200 non-null int64
dtypes: float64(2), int64(1)
memory usage: 28.2 KB
None

—————————————–完—————————–

可以看出,該資料集只有兩個features,一個label,且整個資料集有1200個樣本。可以通過檢視資料集的2D分佈來初步的瞭解該資料集的樣本偏差。如下圖所示:

本資料集在不同類別上樣本數偏差比較大

########################小**********結########################

1. 資料集中不同類別之間樣本數偏差比較大,此時需要做進一步處理。

2. 資料集的載入和顯示等,在前面的文章中已經講爛了,此處無需贅言。

###########################################################

2. 用線性SVM分類器構建分類模型

從上面的資料集分佈圖中可以大致看出,這個模型不是簡單的線性模型,當然,為了對比效果,此處我仍然使用線性SVM分類器來構建分類模型,看一下效果。

程式碼如下:

# 將整個資料集劃分為train set和test set
from sklearn.model_selection import train_test_split
train_X, test_X, train_y, test_y=train_test_split(
    dataset_X,dataset_y,test_size=0.25,random_state=42)

# 如果用線性SVM分類器來進行分類,看看是什麼結果
# 使用線性核函式初始化一個SVM物件。
from sklearn.svm import SVC
classifier=SVC(kernel=`linear`) # 構建線性分類器
classifier.fit(train_X,train_y)
複製程式碼

然後在通過plot_classifier()函式將這個模型的分類效果畫出來。

# 模型在訓練集上的效能報告:
from sklearn.metrics import classification_report
plot_classifier(classifier,train_X,train_y)  # 分類器在訓練集上的分類效果
target_names = [`Class-0`, `Class-1`]
y_pred=classifier.predict(train_X)
print(classification_report(train_y, y_pred, target_names=target_names))
複製程式碼
線性SVM分類器對本資料集的分類效果

—————————-輸———出———————–

precision recall f1-score support
Class-0 0.00 0.00 0.00
Class-1 0.83 1.00 0.91
avg / total 0.69 0.83 0.76

———————————–完—————————–

可以看出,線性SVM分類器完全沒有將class_0區分出來,得到的各種指標都是0,從分類效果圖中也可以看到,根本就沒有線性平面。

##################小**********結###############################

1. 由於本資料集存在樣本數量偏差,故而使用線性SVM分類器沒有任何效果。

############################################################

3. 解決樣本數量偏差的方法

從上面可以看出,由於樣本數量偏差的存在,使用線性SVM分類器沒有任何效果,那麼我們該怎麼處理這種資料集了?

SVM內部存在一個class_weight引數,我們可以設定該引數為”balanced”,來調節各種類別樣本數量的權重,使其達到平衡。如下程式碼:

# 看來直接使用簡單的線性SVM分類器難以將class_0區分出來,故而我們要調整資料集中樣本的數量權重
classifier2=SVC(kernel=`linear`,class_weight=`balanced`) # 比上面的分類器增加了 class_weight=‘balanced`引數
classifier2.fit(train_X,train_y)

# 模型在訓練集上的效能報告:
plot_classifier(classifier2,train_X,train_y)  # 分類器在訓練集上的分類效果
target_names = [`Class-0`, `Class-1`]
y_pred2=classifier2.predict(train_X)
print(classification_report(train_y, y_pred2, target_names=target_names))
複製程式碼
設定class_weight引數為balanced後分類效果圖

——————————-輸———出—————————

precision recall f1-score support
Class-0 0.35 0.86 0.50
Class-1 0.96 0.68 0.79
avg / total 0.86 0.71 0.74

————————————-完——————————–

從分類效果圖中可以看出,此時可以使用線性SVM來對資料集進行訓練,並能得到效果還不錯的分類模型,從分類結果報告中可以看出,各種指標也還不錯,但也存在較大的提升空間。

#################小**********結###############################

1. 在定義SVM分類器時,只需要設定class_weight=balanced即可消除資料集中樣本數量偏差的問題。

2. 如果分類器的效果不理想,那麼需要考慮是否是資料集存在明顯的樣本數量偏差問題。

#########################################################

注:本部分程式碼已經全部上傳到(我的github)上,歡迎下載。

參考資料:

1, Python機器學習經典例項,Prateek Joshi著,陶俊傑,陳小莉譯

相關文章