當(dāng)我們?cè)谧鰴C(jī)器學(xué)習(xí)任務(wù)時(shí),會(huì)經(jīng)常遇到這樣的數(shù)據(jù)集,兩種樣本的數(shù)量相差特別懸殊。這就會(huì)導(dǎo)致一個(gè)問題,在建模的時(shí)候,模型會(huì)特別注重?cái)?shù)量多的樣本,而忽視數(shù)量少的樣本,覺得數(shù)量少的樣本不夠重要。生活中也有許多這樣的例子,如在一份患者數(shù)據(jù)集當(dāng)中,絕大多數(shù)患者都是正常的,只有極少數(shù)患者會(huì)得癌癥。在一份銀行貸款數(shù)據(jù)集中,絕大數(shù)用戶都能按時(shí)還款,只有極少數(shù)用戶會(huì)欠款不還。如果我們我們的模型只關(guān)注正常樣本,而忽視了這些極少數(shù)的異常樣本,那么這樣的模型是沒有什么實(shí)際價(jià)值的。

銀行貸款是一件犯錯(cuò)成本很高的事情,我們需要根據(jù)用戶所提交的資料來判斷是否給該用戶貸款以及貸款的金額。如果一個(gè)用戶能夠正常還款,而我們沒有給他貸款,我們損失的最多是貸款產(chǎn)生的利息;如果一個(gè)用戶無法償還貸款,而我們貸給他一大筆錢,那我們損失的將是整個(gè)本金。所以對(duì)于該情況下的模型而言,必須能夠準(zhǔn)確識(shí)別出這些極少數(shù)的異常樣本。
對(duì)于機(jī)器學(xué)習(xí)模型而言,重視多數(shù)樣本,忽視少數(shù)樣本,這是符合機(jī)器甚至是人的認(rèn)知邏輯的。但是面對(duì)這樣的問題,我們?cè)撊绾翁幚砟??我們希望異常樣本和正常樣本的?shù)量一樣多,這樣機(jī)器就會(huì)認(rèn)為它們同等重要。要實(shí)現(xiàn)正常樣本和異常樣本數(shù)量一樣多,有兩種方案:
1.下采樣:
從多數(shù)的正常樣本中隨機(jī)選出與少數(shù)樣本一樣多的數(shù)據(jù),組成新的數(shù)據(jù)集,然后進(jìn)行建模任務(wù)。這里,我們以銀行貸款數(shù)據(jù)集作為例子,該數(shù)據(jù)集已經(jīng)做過了脫敏(去敏感信息)處理。
creditcard:鏈接:https://pan.baidu.com/s/1t5opuhFew5xVP2zpIwfmVA
提取碼:khj6
具體實(shí)現(xiàn)過程:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
df = pd.read_csv('D:\\Py_dataset\\creditcard.csv')
df.head()#打印出前五行數(shù)據(jù)進(jìn)行觀察
print('該數(shù)據(jù)集規(guī)模:',df.shape)
該數(shù)據(jù)集規(guī)模: (284807, 31)
df['Class'].value_counts()
0 284315
1 492
Name: Class, dtype: int64
#樣本分布可視乎
df['Class'].value_counts(normalize = True).plot(kind = 'bar')
前五行數(shù)據(jù)信息:

樣本分布比例:

下采樣方案代碼實(shí)現(xiàn):
#異常樣本的個(gè)數(shù)
fraud_number = len(df[df.Class == 1])
#異常樣本的索引
fraud_indices = df[df.Class == 1].index
fraud_indices = np.array(fraud_indices)
#正常樣本的索引
norm_indices = df[df.Class == 0].index
norm_indices = np.array(norm_indices)
#在所有正常樣本中選出與異常樣本個(gè)數(shù)一樣多的樣本索引
undersample_norm_indices = np.random.choice(norm_indices,fraud_number,replace = False)
undersample_norm_indices = np.array(undersample_norm_indices)
#將下采樣正常樣本的索引和異常樣本的索引進(jìn)行組合
undersample_indices = np.concatenate((fraud_indices,undersample_norm_indices))
#下采樣的樣本
undersample_data = df.loc[undersample_indices,:]
#查看下采樣正常樣本和異常樣本的分布
print('下采樣數(shù)據(jù)集的大小:',undersample_indices.shape[0])
print('下采樣數(shù)據(jù)集正樣本的個(gè)數(shù):',undersample_data[undersample_data.Class == 0].shape[0])
print('下采樣數(shù)據(jù)集負(fù)樣本的個(gè)數(shù):',undersample_data[undersample_data.Class == 1].shape[0])
下采樣數(shù)據(jù)集的大小: 984
下采樣數(shù)據(jù)集正樣本的個(gè)數(shù): 492
下采樣數(shù)據(jù)集負(fù)樣本的個(gè)數(shù): 492
2.過采樣:
讓異常樣本的數(shù)量與正常樣本一樣多,這就需要對(duì)少數(shù)樣本進(jìn)行生成,這里的生成可不是復(fù)制,一模一樣的樣本是沒有用的。最長用的就是SMOTE數(shù)據(jù)生成算法。
SMOTE算法的流程如下:
- 1.對(duì)于少數(shù)類中的每一個(gè)樣本x,以歐式距離為標(biāo)準(zhǔn),計(jì)算它到少數(shù)樣本集中所有樣本的距離,經(jīng)過排序,得到其近鄰樣本。
- 2.根據(jù)樣本不平衡比例設(shè)置一個(gè)采樣倍率N,對(duì)于每一個(gè)少數(shù)樣本x,從其近鄰依次選擇N個(gè)樣本,如圖(a)所示。
- 3.對(duì)于每一個(gè)選出的近鄰樣本,分別與原樣本按照如下的公式構(gòu)建新的樣本數(shù)據(jù)。其中x為少數(shù)樣本,x^ 為少數(shù)樣本x的近鄰樣本,從0~1之間選擇一個(gè)隨機(jī)數(shù)乘以x與近鄰樣本x的距離再加上x,即可得到x與x之間的新樣本,如圖(b)所示。
SMOTE function.jpg
SMOTE sample.jpg
SMOTE算法代碼實(shí)現(xiàn):
對(duì)于SMOTE算法,可以使用imblearn這個(gè)工具包來完成,首先需要在prompt命令行中安裝該工具包(pip install imblearn)。對(duì)于SMOTE算法而言,只需要傳入特征和標(biāo)簽即可得到一個(gè)大量的異常樣本集。
#提取標(biāo)簽
labels = df['Class']
#提取特征
features = df.drop('Class',axis = 1)
#導(dǎo)入SMOTE算法
from imblearn.over_sampling import SMOTE
oversampler = SMOTE(random_state = 0)
os_features,os_labels = oversampler.fit_sample(features,labels)
#查看原始數(shù)據(jù)集中正常和異常樣本的分布
print('原始數(shù)據(jù)集正常樣本的個(gè)數(shù):',df[df['Class'] == 0].shape[0])
print('原始數(shù)據(jù)集異常樣本的個(gè)數(shù):',df[df['Class'] == 1].shape[0])
print('原始數(shù)據(jù)集的長度:',df.shape[0])
原始數(shù)據(jù)集正常樣本的個(gè)數(shù): 284315
原始數(shù)據(jù)集異常樣本的個(gè)數(shù): 492
原始數(shù)據(jù)集的長度: 284807
#新構(gòu)造的數(shù)據(jù)集中正常樣本和異常樣本的分布
print('過采樣數(shù)據(jù)集中正常樣本的個(gè)數(shù):',len(os_labels[os_labels == 0]))
print('過采樣數(shù)據(jù)集中異常樣本的個(gè)數(shù):',len(os_labels[os_labels == 1]))
print('過采樣數(shù)據(jù)集的長度:',len(os_labels))
過采樣數(shù)據(jù)集中正常樣本的個(gè)數(shù): 284315
過采樣數(shù)據(jù)集中異常樣本的個(gè)數(shù): 284315
過采樣數(shù)據(jù)集的長度: 568630
從結(jié)果中可以看出異常樣本的數(shù)量從492個(gè)變成了284315個(gè),與正常樣本的數(shù)量一致。
這一小節(jié),我們總結(jié)了處理樣本不均衡數(shù)據(jù)集的兩種方法,下采樣和過采樣。其中不涉及數(shù)據(jù)的預(yù)處理、建模以及模型評(píng)估等內(nèi)容。在具體的任務(wù)當(dāng)中,采樣方案與數(shù)據(jù)預(yù)處理、建模、模型評(píng)估,參數(shù)優(yōu)化構(gòu)成了一個(gè)整體。
3.采樣方案總結(jié):
- 1.下采樣方案比較簡單,從多數(shù)樣本中隨機(jī)選擇與少數(shù)樣本一樣多的數(shù)據(jù),方便我們處理樣本不均衡數(shù)據(jù)。但下采樣方案犧牲了原有數(shù)據(jù)的豐富性,只使用了一小部分的數(shù)據(jù)。
- 過采樣方案,通過數(shù)據(jù)生成策略,使得異常樣本的數(shù)量與正常樣本一樣多,方便我們處理樣本不均衡數(shù)據(jù)。但生成的數(shù)據(jù)畢竟是不真實(shí)的,是通過一定的規(guī)則偽造出來的。
- 3.下采樣方案和過采樣各有優(yōu)缺點(diǎn),在處理具體的任務(wù)時(shí),需通過實(shí)驗(yàn)對(duì)比兩種方案的優(yōu)劣。

