溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

Python中怎么解決非平衡數(shù)據(jù)問(wèn)題

發(fā)布時(shí)間:2021-07-05 17:25:52 來(lái)源:億速云 閱讀:220 作者:Leah 欄目:編程語(yǔ)言

Python中怎么解決非平衡數(shù)據(jù)問(wèn)題,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來(lái)學(xué)習(xí)下,希望你能有所收獲。

SMOTE算法的介紹

在實(shí)際應(yīng)用中,讀者可能會(huì)碰到一種比較頭疼的問(wèn)題,那就是分類(lèi)問(wèn)題中類(lèi)別型的因變量可能存在嚴(yán)重的偏倚,即類(lèi)別之間的比例嚴(yán)重失調(diào)。如欺詐問(wèn)題中,欺詐類(lèi)觀(guān)測(cè)在樣本集中畢竟占少數(shù);客戶(hù)流失問(wèn)題中,非忠實(shí)的客戶(hù)往往也是占很少一部分;在某營(yíng)銷(xiāo)活動(dòng)的響應(yīng)問(wèn)題中,真正參與活動(dòng)的客戶(hù)也同樣只是少部分。

如果數(shù)據(jù)存在嚴(yán)重的不平衡,預(yù)測(cè)得出的結(jié)論往往也是有偏的,即分類(lèi)結(jié)果會(huì)偏向于較多觀(guān)測(cè)的類(lèi)。對(duì)于這種問(wèn)題該如何處理呢?最簡(jiǎn)單粗暴的辦法就是構(gòu)造1:1的數(shù)據(jù),要么將多的那一類(lèi)砍掉一部分(即欠采樣),要么將少的那一類(lèi)進(jìn)行Bootstrap抽樣(即過(guò)采樣)。但這樣做會(huì)存在問(wèn)題,對(duì)于***種方法,砍掉的數(shù)據(jù)會(huì)導(dǎo)致某些隱含信息的丟失;而第二種方法中,有放回的抽樣形成的簡(jiǎn)單復(fù)制,又會(huì)使模型產(chǎn)生過(guò)擬合。

為了解決數(shù)據(jù)的非平衡問(wèn)題,2002年Chawla提出了SMOTE算法,即合成少數(shù)過(guò)采樣技術(shù),它是基于隨機(jī)過(guò)采樣算法的一種改進(jìn)方案。該技術(shù)是目前處理非平衡數(shù)據(jù)的常用手段,并受到學(xué)術(shù)界和工業(yè)界的一致認(rèn)同,接下來(lái)簡(jiǎn)單描述一下該算法的理論思想。

SMOTE算法的基本思想就是對(duì)少數(shù)類(lèi)別樣本進(jìn)行分析和模擬,并將人工模擬的新樣本添加到數(shù)據(jù)集中,進(jìn)而使原始數(shù)據(jù)中的類(lèi)別不再?lài)?yán)重失衡。該算法的模擬過(guò)程采用了KNN技術(shù),模擬生成新樣本的步驟如下:

  • 采樣最鄰近算法,計(jì)算出每個(gè)少數(shù)類(lèi)樣本的K個(gè)近鄰;

  • 從K個(gè)近鄰中隨機(jī)挑選N個(gè)樣本進(jìn)行隨機(jī)線(xiàn)性插值;

  • 構(gòu)造新的少數(shù)類(lèi)樣本;

  • 將新樣本與原數(shù)據(jù)合成,產(chǎn)生新的訓(xùn)練集;

為了使讀者理解SMOTE算法實(shí)現(xiàn)新樣本的模擬過(guò)程,可以參考下圖和人工新樣本的生成過(guò)程:

Python中怎么解決非平衡數(shù)據(jù)問(wèn)題

如上圖所示,實(shí)心圓點(diǎn)代表的樣本數(shù)量要明顯多于五角星代表的樣本點(diǎn),如果使用SMOTE算法模擬增加少類(lèi)別的樣本點(diǎn),則需要經(jīng)過(guò)如下幾個(gè)步驟:

  • 利用KNN算法,選擇離樣本點(diǎn)x1最近的K個(gè)同類(lèi)樣本點(diǎn)(不妨最近鄰為5);

  • 從最近的K個(gè)同類(lèi)樣本點(diǎn)中,隨機(jī)挑選M個(gè)樣本點(diǎn)(不妨M為2),M的選擇依賴(lài)于最終所希望的平衡率;

  • 對(duì)于每一個(gè)隨機(jī)選中的樣本點(diǎn),構(gòu)造新的樣本點(diǎn);新樣本點(diǎn)的構(gòu)造需要使用下方的公式:

Python中怎么解決非平衡數(shù)據(jù)問(wèn)題

其中,xi表示少數(shù)類(lèi)別中的一個(gè)樣本點(diǎn)(如圖中五角星所代表的x1樣本);xj表示從K近鄰中隨機(jī)挑選的樣本點(diǎn)j;rand(0,1)表示生成0~1之間的隨機(jī)數(shù)。

假設(shè)圖中樣本點(diǎn)x1的觀(guān)測(cè)值為(2,3,10,7),從圖中的5個(gè)近鄰中隨機(jī)挑選2個(gè)樣本點(diǎn),它們的觀(guān)測(cè)值分別為(1,1,5,8)和(2,1,7,6),所以,由此得到的兩個(gè)新樣本點(diǎn)為:

Python中怎么解決非平衡數(shù)據(jù)問(wèn)題
  • 重復(fù)步驟1)、2)和3),通過(guò)迭代少數(shù)類(lèi)別中的每一個(gè)樣本xi,最終將原始的少數(shù)類(lèi)別樣本量擴(kuò)大為理想的比例;

通過(guò)SMOTE算法實(shí)現(xiàn)過(guò)采樣的技術(shù)并不是太難,讀者可以根據(jù)上面的步驟自定義一個(gè)抽樣函數(shù)。當(dāng)然,讀者也可以借助于imblearn模塊,并利用其子模塊over_sampling中的SMOTE“類(lèi)”實(shí)現(xiàn)新樣本的生成。有關(guān)該“類(lèi)”的語(yǔ)法和參數(shù)含義如下:

SMOTE(ratio=’auto’, random_state=None, k_neighbors=5, m_neighbors=10,      out_step=0.5, kind=’regular’, svm_estimator=None, n_jobs=1)
  • ratio:用于指定重抽樣的比例,如果指定字符型的值,可以是’minority’,表示對(duì)少數(shù)類(lèi)別的樣本進(jìn)行抽樣、’majority’,表示對(duì)多數(shù)類(lèi)別的樣本進(jìn)行抽樣、’not  minority’表示采用欠采樣方法、’all’表示采用過(guò)采樣方法,默認(rèn)為’auto’,等同于’all’和’not  minority’;如果指定字典型的值,其中鍵為各個(gè)類(lèi)別標(biāo)簽,值為類(lèi)別下的樣本量;

  • random_state:用于指定隨機(jī)數(shù)生成器的種子,默認(rèn)為None,表示使用默認(rèn)的隨機(jī)數(shù)生成器;

  • k_neighbors:指定近鄰個(gè)數(shù),默認(rèn)為5個(gè);

  • m_neighbors:指定從近鄰樣本中隨機(jī)挑選的樣本個(gè)數(shù),默認(rèn)為10個(gè);

  • kind:用于指定SMOTE算法在生成新樣本時(shí)所使用的選項(xiàng),默認(rèn)為’regular’,表示對(duì)少數(shù)類(lèi)別的樣本進(jìn)行隨機(jī)采樣,也可以是’borderline1’、’borderline2’和’svm’;

  • svm_estimator:用于指定SVM分類(lèi)器,默認(rèn)為sklearn.svm.SVC,該參數(shù)的目的是利用支持向量機(jī)分類(lèi)器生成支持向量,然后再生成新的少數(shù)類(lèi)別的樣本;

  • n_jobs:用于指定SMOTE算法在過(guò)采樣時(shí)所需的CPU數(shù)量,默認(rèn)為1表示僅使用1個(gè)CPU運(yùn)行算法,即不使用并行運(yùn)算功能;

分類(lèi)算法的應(yīng)用實(shí)戰(zhàn)

本次分享的數(shù)據(jù)集來(lái)源于德國(guó)某電信行業(yè)的客戶(hù)歷史交易數(shù)據(jù),該數(shù)據(jù)集一共包含條4,681記錄,19個(gè)變量,其中因變量churn為二元變量,yes表示客戶(hù)流失,no表示客戶(hù)未流失;剩余的自變量包含客戶(hù)的是否訂購(gòu)國(guó)際長(zhǎng)途套餐、語(yǔ)音套餐、短信條數(shù)、話(huà)費(fèi)、通話(huà)次數(shù)等。接下來(lái)就利用該數(shù)據(jù)集,探究非平衡數(shù)據(jù)轉(zhuǎn)平衡后的效果。

# 導(dǎo)入第三方包 import pandas as pd import numpy as np import matplotlib.pyplot as plt from sklearn import model_selection from sklearn import tree from sklearn import metrics from imblearn.over_sampling import SMOTE # 讀取數(shù)據(jù)churn = pd.read_excel(r'C:\Users\Administrator\Desktop\Customer_Churn.xlsx') churn.head()
Python中怎么解決非平衡數(shù)據(jù)問(wèn)題
# 中文亂碼的處理 plt.rcParams['font.sans-serif']=['Microsoft YaHei']  # 為確保繪制的餅圖為圓形,需執(zhí)行如下代碼 plt.axes(aspect = 'equal') # 統(tǒng)計(jì)交易是否為欺詐的頻數(shù) counts = churn.churn.value_counts()  # 繪制餅圖 plt.pie(x = counts, # 繪圖數(shù)據(jù)         labels=pd.Series(counts.index).map({'yes':'流失','no':'未流失'}), # 添加文字標(biāo)簽         autopct='%.2f%%' # 設(shè)置百分比的格式,這里保留一位小數(shù)        ) # 顯示圖形 plt.show()
Python中怎么解決非平衡數(shù)據(jù)問(wèn)題

如上圖所示,流失用戶(hù)僅占到8.3%,相比于未流失用戶(hù),還是存在比較大的差異的??梢哉J(rèn)為兩種類(lèi)別的客戶(hù)是失衡的,如果直接對(duì)這樣的數(shù)據(jù)建模,可能會(huì)導(dǎo)致模型的結(jié)果不夠準(zhǔn)確。不妨先對(duì)該數(shù)據(jù)構(gòu)建隨機(jī)森林模型,看看是否存在偏倚的現(xiàn)象。

原始數(shù)據(jù)表中的state變量和Area_code變量表示用戶(hù)所屬的“州”和地區(qū)編碼,直觀(guān)上可能不是影響用戶(hù)是否流失的重要原因,故將這兩個(gè)變量從表中刪除。除此,用戶(hù)是否訂購(gòu)國(guó)際長(zhǎng)途業(yè)務(wù)international_plan和語(yǔ)音業(yè)務(wù)voice_mail_plan,屬于字符型的二元值,它們是不能直接代入模型的,故需要轉(zhuǎn)換為0-1二元值。

# 數(shù)據(jù)清洗 # 刪除state變量和area_code變量 churn.drop(labels=['state','area_code'], axis = 1, inplace = True)  # 將二元變量international_plan和voice_mail_plan轉(zhuǎn)換為0-1啞變量 churn.international_plan = churn.international_plan.map({'no':0,'yes':1}) churn.voice_mail_plan = churn.voice_mail_plan.map({'no':0,'yes':1}) churn.head()
Python中怎么解決非平衡數(shù)據(jù)問(wèn)題

如上表所示,即為清洗后的干凈數(shù)據(jù),接下來(lái)對(duì)該數(shù)據(jù)集進(jìn)行拆分,分別構(gòu)建訓(xùn)練數(shù)據(jù)集和測(cè)試數(shù)據(jù)集,并利用訓(xùn)練數(shù)據(jù)集構(gòu)建分類(lèi)器,測(cè)試數(shù)據(jù)集檢驗(yàn)分類(lèi)器:

# 用于建模的所有自變量 predictors = churn.columns[:-1] # 數(shù)據(jù)拆分為訓(xùn)練集和測(cè)試集 X_train,X_test,y_train,y_test = model_selection.train_test_split(churn[predictors], churn.churn, random_state=12)  # 構(gòu)建決策樹(shù) dt = tree.DecisionTreeClassifier(n_estimators = 300) dt.fit(X_train,y_train) # 模型在測(cè)試集上的預(yù)測(cè) pred = dt.predict(X_test)  # 模型的預(yù)測(cè)準(zhǔn)確率 print(metrics.accuracy_score(y_test, pred)) # 模型評(píng)估報(bào)告 print(metrics.classification_report(y_test, pred))
Python中怎么解決非平衡數(shù)據(jù)問(wèn)題

如上結(jié)果所示,決策樹(shù)的預(yù)測(cè)準(zhǔn)確率超過(guò)93%,其中預(yù)測(cè)為no的覆蓋率recall為97%,但是預(yù)測(cè)為yes的覆蓋率recall卻為62%,兩者相差甚遠(yuǎn),說(shuō)明分類(lèi)器確實(shí)偏向了樣本量多的類(lèi)別(no)。

# 繪制ROC曲線(xiàn) # 計(jì)算流失用戶(hù)的概率值,用于生成ROC曲線(xiàn)的數(shù)據(jù) y_score = dt.predict_proba(X_test)[:,1] fpr,tpr,threshold = metrics.roc_curve(y_test.map({'no':0,'yes':1}), y_score)  # 計(jì)算AUC的值 roc_auc = metrics.auc(fpr,tpr) # 繪制面積圖 plt.stackplot(fpr, tpr, color='steelblue', alpha = 0.5, edgecolor = 'black') # 添加邊際線(xiàn) plt.plot(fpr, tpr, color='black', lw = 1) # 添加對(duì)角線(xiàn) plt.plot([0,1],[0,1], color = 'red', linestyle = '--') # 添加文本信息 plt.text(0.5,0.3,'ROC curve (area = %0.3f)' % roc_auc) # 添加x軸與y軸標(biāo)簽 plt.xlabel('1-Specificity') plt.ylabel('Sensitivity') # 顯示圖形 plt.show()
Python中怎么解決非平衡數(shù)據(jù)問(wèn)題

如上圖所示,ROC曲線(xiàn)下的面積為0.79***UC的值小于0.8,故認(rèn)為模型不太合理。(通常拿AUC與0.8比較,如果大于0.8,則認(rèn)為模型合理)。接下來(lái),利用SMOTE算法對(duì)數(shù)據(jù)進(jìn)行處理:

# 對(duì)訓(xùn)練數(shù)據(jù)集作平衡處理 over_samples = SMOTE(random_state=1234)  over_samples_X,over_samples_y = over_samples.fit_sample(X_train, y_train)  # 重抽樣前的類(lèi)別比例 print(y_train.value_counts()/len(y_train)) # 重抽樣后的類(lèi)別比例 print(pd.Series(over_samples_y).value_counts()/len(over_samples_y))
Python中怎么解決非平衡數(shù)據(jù)問(wèn)題

如上結(jié)果所示,對(duì)于訓(xùn)練數(shù)據(jù)集本身,它的類(lèi)別比例還是存在較大差異的,但經(jīng)過(guò)SMOTE算法處理后,兩個(gè)類(lèi)別就可以達(dá)到1:1的平衡狀態(tài)。下面就可以利用這個(gè)平衡數(shù)據(jù),重新構(gòu)建決策樹(shù)分類(lèi)器了:

# 基于平衡數(shù)據(jù)重新構(gòu)建決策樹(shù)模型 dt2 = ensemble.DecisionTreeClassifier(n_estimators = 300) dt2.fit(over_samples_X,over_samples_y) # 模型在測(cè)試集上的預(yù)測(cè) pred2 =dt2.predict(np.array(X_test))  # 模型的預(yù)測(cè)準(zhǔn)確率 print(metrics.accuracy_score(y_test, pred2)) # 模型評(píng)估報(bào)告 print(metrics.classification_report(y_test, pred2))
Python中怎么解決非平衡數(shù)據(jù)問(wèn)題

如上結(jié)果所示,利用平衡數(shù)據(jù)重新建模后,模型的準(zhǔn)確率同樣很高,為92.6%(相比于原始非平衡數(shù)據(jù)構(gòu)建的模型,準(zhǔn)確率僅下降1%),但是預(yù)測(cè)為yes的覆蓋率提高了10%,達(dá)到72%,這就是平衡帶來(lái)的好處。

# 計(jì)算流失用戶(hù)的概率值,用于生成ROC曲線(xiàn)的數(shù)據(jù) y_score = rf2.predict_proba(np.array(X_test))[:,1] fpr,tpr,threshold = metrics.roc_curve(y_test.map({'no':0,'yes':1}), y_score) # 計(jì)算AUC的值 roc_auc = metrics.auc(fpr,tpr) # 繪制面積圖 plt.stackplot(fpr, tpr, color='steelblue', alpha = 0.5, edgecolor = 'black') # 添加邊際線(xiàn) plt.plot(fpr, tpr, color='black', lw = 1) # 添加對(duì)角線(xiàn) plt.plot([0,1],[0,1], color = 'red', linestyle = '--') # 添加文本信息 plt.text(0.5,0.3,'ROC curve (area = %0.3f)' % roc_auc) # 添加x軸與y軸標(biāo)簽 plt.xlabel('1-Specificity') plt.ylabel('Sensitivity')  # 顯示圖形 plt.show()
Python中怎么解決非平衡數(shù)據(jù)問(wèn)題

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI