技術(shù)分享:數(shù)據(jù)不平衡問題
在學(xué)術(shù)研究與教學(xué)中,很多算法都有一個基本假設(shè),那就是數(shù)據(jù)分布是均勻的。當(dāng)我們把這些算法直接應(yīng)用于實(shí)際數(shù)據(jù)時,大多數(shù)情況下都無法取得理想的結(jié)果。因?yàn)閷?shí)際數(shù)據(jù)往往分布得很不均勻,都會存在“長尾現(xiàn)象”,也就是數(shù)據(jù)不平衡”。
以下幾種方法是針對數(shù)據(jù)不平衡問題所做的處理,具體包括:
- smote采樣
- adasyn采樣
- 欠采樣
- 一分類
- 改進(jìn)的adaboost方法
一、smote采樣
SMOTE全稱是Synthetic Minority Oversampling Technique即合成少數(shù)類過采樣技術(shù),它是基于隨機(jī)過采樣算法的一種改進(jìn)方案,由于隨機(jī)過采樣采取簡單復(fù)制樣本的策略來增加少數(shù)類樣本,這樣容易產(chǎn)生模型過擬合的問題,即使得模型學(xué)習(xí)到的信息過于特別(Specific)而不夠泛化(General),SMOTE算法的基本思想是對少數(shù)類樣本進(jìn)行分析并根據(jù)少數(shù)類樣本人工合成新樣本添加到數(shù)據(jù)集中,算法流程如下。
- 對于少數(shù)類中每一個樣本x,以歐氏距離為標(biāo)準(zhǔn)計(jì)算它到少數(shù)類樣本集中所有樣本的距離,得到其k近鄰。
- 根據(jù)樣本不平衡比例設(shè)置一個采樣比例以確定采樣倍率N,對于每一個少數(shù)類樣本x,從其k近鄰中隨機(jī)選擇若干個樣本,假設(shè)選擇的近鄰為xn。
- 對于每一個隨機(jī)選出的近鄰xn,分別與原樣本按照如下的公式構(gòu)建新的樣本 xnew=x+rand(0,1)∗|x−xn|
部分代碼如下:
- df=get_data()
- x, y = load_creditcard_data(df)
- X_resampled_smote, y_resampled_smote = SMOTE().fit_sample(x, y) # print(y_resampled_smote)
- X_train, X_test, y_train, y_test = train_test_split(X_resampled_smote, y_resampled_smote, test_size=0.3,random_state=0)
二、adasyn采樣
本文介紹的是 ADASYN: 自適應(yīng)綜合過采樣方法。
算法步驟如下:
(1)計(jì)算不平衡度
記少數(shù)類樣本為ms,多數(shù)類為ml,則不平衡度為 d = ms / ml,則d∈(0,1]。(作者在這里右邊用了閉區(qū)間,我覺得應(yīng)該用開區(qū)間,若是d = 1,則少數(shù)類與多數(shù)類樣本數(shù)量一致,此時數(shù)據(jù)應(yīng)該平衡的)
(2)計(jì)算需要合成的樣本數(shù)量
G = (ml - ms)* b,b∈[0,1],當(dāng)b = 1時,即G等于少數(shù)類和多數(shù)類的差值,此時合成數(shù)據(jù)后的多數(shù)類個數(shù)和少數(shù)類數(shù)據(jù)正好平衡
(3)對每個屬于少數(shù)類的樣本用歐式距離計(jì)算k個鄰居,為k個鄰居中屬于多數(shù)類的樣本數(shù)目,記比例r為r = / k,r∈[0,1]
(4)在(3)中得到每一個少數(shù)類樣本的 ri ,
用 計(jì)算每個少數(shù)類樣本的周圍多數(shù)類的情況
(5)對每個少數(shù)類樣本計(jì)算合成樣本的數(shù)目 (6)在每個待合成的少數(shù)類樣本周圍k個鄰居中選擇1個少數(shù)類樣本,根據(jù)下列等式進(jìn)行合成
重復(fù)合成直到滿足需要步驟(5)合成的數(shù)目為止。
部分代碼如下:
- df=get_data()
- x, y = load_creditcard_data(df)
- X_resampled_smote, y_resampled_smote = ADASYN().fit_sample(x, y)
三、欠采樣
以下兩種方法都屬于欠抽樣,不同于直接欠抽樣,他們將信息的丟失程度盡量降低。兩者的核心思想為:
1. EasyEnsemble 核心思想是:
- 首先通過從多數(shù)類中獨(dú)立隨機(jī)抽取出若干子集
- 將每個子集與少數(shù)類數(shù)據(jù)聯(lián)合起來訓(xùn)練生成多個基分類器
- 最終將這些基分類器組合形成一個集成學(xué)習(xí)系統(tǒng)
EasyEnsemble 算法被認(rèn)為是非監(jiān)督學(xué)習(xí)算法,因此它每次都獨(dú)立利用可放回隨機(jī)抽樣機(jī)制來提取多數(shù)類樣本
2. BalanceCascade 核心思想是:
- 使用之前已形成的集成分類器來為下一次訓(xùn)練選擇多類樣本
- 然后再進(jìn)行欠抽樣
四、一分類
對于正負(fù)樣本極不平衡的場景,我們可以換一個完全不同的角度來看待問題:把它看做一分類(One Class Learning)或異常檢測(Novelty Detection)問題。這類方法的重點(diǎn)不在于捕捉類間的差別,而是為其中一類進(jìn)行建模,經(jīng)典的工作包括One-class SVM等。
我們只對一類進(jìn)行訓(xùn)練,模型的結(jié)果會聚集在某個范圍內(nèi),測試集進(jìn)行測試,則模型的輸出結(jié)果為1和-1兩種,當(dāng)落在這個區(qū)間,結(jié)果為1,不在這個區(qū)間,則結(jié)果為-1
部分代碼如下:
- def MechanicalRupture_Model():
- train = pd.read_excel(normal)
- test = pd.read_excel(unnormal)
- clf = svm.OneClassSVM(nu=0.1, kernel=rbf, gamma=0.1)
- clf.fit(train)
- y_pred_train = clf.predict(train)
- y_pred_test = clf.predict(test)
五、改進(jìn)的adaboost方法
AdaCost算法修改了Adaboost算法的權(quán)重更新策略,其基本思想是對于代價高的誤分類樣本大大地提高其權(quán)重,而對于代價高的正確分類樣 本適當(dāng)?shù)亟档推錂?quán)重,使其權(quán)重降低相對較小。總體思想是代價高樣本權(quán)重增加得大降低得慢。
具體adacost代碼如下:
- #!/usr/bin/env python3# -*- coding:utf-8 -*-import numpy as npfrom numpy.core.umath_tests import inner1dfrom sklearn.ensemble import AdaBoostClassifierclass AdaCostClassifier(AdaBoostClassifier):#繼承AdaBoostClassifier
- def _boost_real(self, iboost, X, y, sample_weight, random_state):
- implement a single boost using the SAMME.R real algorithm.
- :param iboost:
- :param X:
- :param random_state:
- :param y:
- :return:sample_weight,estimator_error
- estimator = self._make_estimator(random_state=random_state)
- estimator.fit(X, y, sample_weight=sample_weight)
- y_predict_proba = estimator.predict_proba(X) if iboost == 0:
- self.classes_ = getattr(estimator, 'classes_', None)
- self.n_classes_ = len(self.classes_)
- y_predict = self.classes_.take(np.argmax(y_predict_proba, axis=1),axis=0)
- incorrect = y_predict != y
- estimator_error = np.mean(np.average(incorrect, weights=sample_weight, axis=0)) if estimator_error = 0: return sample_weight, 1., 0.
- n_classes = self.n_classes_
- classes = self.classes_
- y_codes = np.array([-1. / (n_classes - 1), 1.])
- y_coding = y_codes.take(classes == y[:, np.newaxis])
- proba = y_predict_proba # alias for readability
- proba[proba np.finfo(proba.dtype).eps] = np.finfo(proba.dtype).eps
- estimator_weight = (-1. * self.learning_rate * (((n_classes - 1.) / n_classes) *
- inner1d(y_coding, np.log(y_predict_proba)))) # 樣本更新的公式,只需要改寫這里
- if not iboost == self.n_estimators - 1:
- sample_weight *= np.exp(estimator_weight *
- ((sample_weight 0) |
- (estimator_weight 0)) *
- self._beta(y, y_predict)) # 在原來的基礎(chǔ)上乘以self._beta(y, y_predict),即代價調(diào)整函數(shù)
- return sample_weight, 1., estimator_error def _beta(self, y, y_hat):
- adjust cost function weight
- :param y:
- :param y_hat:
- :return:res
- res = [] for i in zip(y, y_hat): if i[0] == i[1]:
- res.append(1) # 正確分類,系數(shù)保持不變,按原來的比例減少
- elif i[0] == 0 and i[1] == 1: # elif i[0] == 1 and i[1] == -1:
- res.append(1) # 將負(fù)樣本誤判為正樣本代價應(yīng)該更大一些,比原來的增加比例要高
- elif i[0] == 1 and i[1] == 0: # elif i[0] == -1 and i[1] == 1:
- res.append(1.25) # 將正列判為負(fù)列,代價不變,按原來的比例增加
- else: print(i[0], i[1]) return np.array(res)
總結(jié):
其中
smote采樣 、adasyn采樣、欠采樣、一分類是針對數(shù)據(jù)集做出處理。
改進(jìn)的adaboost方法是對模型方法進(jìn)行的改進(jìn)。
具體采用哪種方式,需要結(jié)合具體情況。