標籤:

當常規的演算法都山窮水盡之後,你可以試試python中的SMOTE演算法

歡迎關注我們的微信公眾號「人工智慧LeadAI」(ID:atleadai)

之前一直沒有用過python,最近做了一些數量級比較大的項目,覺得有必要熟悉一下python,正好用到了smote,網上也沒有搜到,所以就當做一個小練手來做一下。

首先,看下Smote演算法之前,我們先看下當正負樣本不均衡的時候,我們通常用的方法:

抽樣

常規的包含過抽樣、欠抽樣、組合抽樣

過抽樣:將樣本較少的一類sample補齊。

欠抽樣:將樣本較多的一類sample壓縮。

組合抽樣:約定一個量級N,同時進行過抽樣和欠抽樣,使得正負樣本量和等於約定量級N。

這種方法要麼丟失數據信息,要麼會導致較少樣本共線性,存在明顯缺陷。

權重調整

常規的包括演算法中的weight,weight matrix。

改變入參的權重比,比如boosting中的全量迭代方式、邏輯回歸中的前置的權重設置。

這種方式的弊端在於無法控制合適的權重比,需要多次嘗試。

核函數修正

通過核函數的改變,來抵消樣本不平衡帶來的問題。

這種使用場景局限,前置的知識學習代價高,核函數調整代價高,黑盒優化。

模型修正

通過現有的較少的樣本類別的數據,用演算法去探查數據之間的特徵,判讀數據是否滿足一定的規律。

比如,通過線性擬合,發現少類樣本成線性關係,可以新增線性擬合模型下的新點。

實際規律比較難發現,難度較高。

SMOTE(Synthetic minoritye over-sampling technique,SMOTE)是Chawla在2002年提出的過抽樣的演算法,一定程度上可以避免以上的問題。

下面介紹一下這個演算法:

正負樣本分布

很明顯的可以看出,藍色樣本數量遠遠大於紅色樣本,在常規調用分類模型去判斷的時候可能會導致之間忽視掉紅色樣本帶了的影響,只強調藍色樣本的分類準確性,這邊需要增加紅色樣本來平衡數據集。

Smote演算法的思想其實很簡單,先隨機選定n個少類的樣本,如下圖:

找出初始擴展的少類樣本

再找出最靠近它的m個少類樣本,如下圖:

再任選最臨近的m個少類樣本中的任意一點,

在這兩點上任選一點,這點就是新增的數據樣本。

R語言上的開發較為簡單,有現成的包庫,這邊簡單介紹一下:

rm(list=ls()) install.packages(「DMwR」,dependencies=T)library(DMwR)#載入smote包newdata=SMOTE(formula,data,perc.over=,perc.under=)#formula:申明自變數因變數#perc.over:過採樣次數#perc.under:欠採樣次數

效果對比:

簡單的看起來就好像是重複描繪了較少的類。

這邊的smote是封裝好的,直接調用就行了,沒有什麼特別之處。

這邊自己想拿剛學的python練練手,所有就拿python寫了一下過程:

# -*- coding: utf-8 -*-import numpy as npimport pandas as pdfrom sklearn.preprocessing import StandardScalerfrom numpy import *import matplotlib.pyplot as plt #讀數據data = pd.read_table(C:/Users/17031877/Desktop/supermarket_second_man_clothes_train.txt, low_memory=False) #簡單的預處理test_date = pd.concat([data[label], data.iloc[:, 7:10]], axis=1)test_date = test_date.dropna(how=any)

數據大致如下:

test_date.head()Out[25]: label max_date_diff max_pay cnt_time0 0 23.0 43068.0 151 0 10.0 1899.0 22 0 146.0 3299.0 213 0 30.0 31959.0 354 0 3.0 24165.0 98test_date[label][test_date[label]==0].count()/test_date[label][test_date[label]==1].count()Out[37]: 67

label是樣本類別判別標籤,1:0=67:1,需要對label=1的數據進行擴充。

# 篩選目標變數aimed_date = test_date[test_date[label] == 1]# 隨機篩選少類擴充中心index = pd.DataFrame(aimed_date.index).sample(frac=0.1, random_state=1)index.columns = [id]number = len(index)# 生成array格式aimed_date_new = aimed_date.ix[index.values.ravel(), :]

隨機選取了全量少數樣本的10%作為數據擴充的中心點。

# 自變數標準化sc = StandardScaler().fit(aimed_date_new)aimed_date_new = pd.DataFrame(sc.transform(aimed_date_new))sc1 = StandardScaler().fit(aimed_date)aimed_date = pd.DataFrame(sc1.transform(aimed_date))# 定義歐式距離計算def dist(a, b): a = array(a) b = array(b) d = ((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2 + (a[2] - b[2]) ** 2 + (a[3] - b[3]) ** 2) ** 0.5 return d

下面定義距離計算的方式,所有演算法中,涉及到距離的地方都需要標準化去除岡量,也同時加快了計算的速度。

這邊採取了歐式距離的方式,更多計算距離的方式參考:

多種距離及相似度的計算理論介紹

# 統計所有檢驗距離樣本個數row_l1 = aimed_date_new.iloc[:, 0].count()row_l2 = aimed_date.iloc[:, 0].count()a = zeros((row_l1, row_l2))a = pd.DataFrame(a)# 計算距離矩陣for i in range(row_l1): for j in range(row_l2): d = dist(aimed_date_new.iloc[i, :], aimed_date.iloc[j, :]) a.ix[i, j] = db = a.T.apply(lambda x: x.min())

調用上面的計算距離的函數,形成一個距離矩陣,

# 找到同類點位置h = []z = []for i in range(number): for j in range(len(a.iloc[i, :])): ai = a.iloc[i, j] bi = b[i] if ai == bi: h.append(i) z.append(j) else: continuenew_point = [0, 0, 0, 0]new_point = pd.DataFrame(new_point)for i in range(len(h)): index_a = z[i] new = aimed_date.iloc[index_a, :] new_point = pd.concat([new, new_point], axis=1)new_point = new_point.iloc[:, range(len(new_point.columns) - 1)]

再找到位置的情況下,再去原始的數據集中根據位置查找具體的數據,

import randomr1 = []for i in range(len(new_point.columns)): r1.append(random.uniform(0, 1))new_point_last = []new_point_last = pd.DataFrame(new_point_last)# 求新點 new_x=old_x+rand()*(append_x-old_x)for i in range(len(new_point.columns)): new_x = (new_point.iloc[1:4, i] - aimed_date_new.iloc[number - 1 - i, 1:4]) * r1[i] + aimed_date_new.iloc[ number - 1 - i, 1:4] new_point_last = pd.concat([new_point_last, new_x], axis=1)print new_point_last

最後,再根據smote的計算公式new_x=old_x+rand()*(append_x-old_x),計算出新的點即可,python練手到此就結束了。

其實,在這個結果上,我們可以綜合Tomek link做一個集成的數據擴充的演算法,思路如下:

假設,我們利用上述的演算法產生了兩個青色方框的新數據點:

我們認為,對於新產生的青色數據點與其他非青色樣本點距離最近的點,構成一對Tomek link,如下圖框中的青藍兩點:

我們可以定義規則:

當以新產生點為中心,Tomek link的距離為範圍半徑,去框定一個空間,空間內的少數類的個數/多數類的個數<最低閥值的時候,認為新產生點為「垃圾點」,應該剔除或者再次進行smote訓練;空間內的少數類的個數/多數類的個數>=最低閥值的時候,在進行保留並納入smote訓練的初始少類樣本集合中去抽樣。

所以,剔除左側的青色新增點,只保留右邊的新增數據如下:

參考文獻

1、jair.org/media/953/live

2、github.com/fmfn/Unbalan

3、Batista, G. E., Bazzan, A. L., & Monard, M. C. (2003, December). Balancing Training Data for Automated Annotation of Keywords: a Case Study. In WOB (pp. 10-18);

4、Batista, G. E., Prati, R. C., & Monard, M. C. (2004). A study of the behavior of several methods for balancing machine learning training data. ACM Sigkdd Explorations Newsletter, 6(1), 20-29。


推薦閱讀:

如果只推介一本python3的書籍,你會推介哪一本?
pandas 怎麼根據一列的數據的值的情況判斷來生成另外一列的數值?
想參考一些用 Python 做機器學習或數據挖掘的例子和資源,如何獲取?
簡歷中如何證明自己的編程能力?
GeoPython 下載安裝、操作講解和視頻演示

TAG:Python |