imblearn 使用筆記

在做機器學習相關項目時,通常會出現(xiàn)樣本數(shù)據(jù)量不均衡操作,這時可以使用 imblearn 包進行重采樣操作,可通過 pip install imbalanced-learn 命令進行安裝。

imblearn 包使用過程中,通常輸入項 x 多為 2D 的結構。否則會包 ``

不均衡分析

在數(shù)據(jù)化運營過程中,以下場景會經常產生樣本分布不均衡的問題:

  • 異常檢測場景 大多數(shù)企業(yè)中的異常個案都是少量的,比如惡意刷單、黃牛訂單、信用卡欺詐、電力竊電、設備故障等,這些數(shù)據(jù)樣本所占的比例通常是整體樣本中很少的一部分,以信用卡欺詐為例,刷實體信用卡的欺詐比例一般都在0.1%以內。
  • 客戶流失場景 大型企業(yè)的流失客戶相對于整體客戶通常是少量的,尤其對于具有壟斷地位的行業(yè)巨擘,例如電信、石油、網絡運營商等更是如此。
  • 罕見事件的分析 罕見事件與異常檢測類似,都屬于發(fā)生個案較少;但不同點在于異常檢測通常都有是預先定義好的規(guī)則和邏輯,并且大多數(shù)異常事件都對會企業(yè)運營造成負面影響,因此針對異常事件的檢測和預防非常重要;但罕見事件則無法預判,并且也沒有明顯的積極和消極影響傾向。例如由于某網絡大V無意中轉發(fā)了企業(yè)的一條趣味廣告導致用戶流量明顯提升便屬于此類。
  • 發(fā)生頻率低的事件 這種事件是預期或計劃性事件,但是發(fā)生頻率非常低。例如每年1次的雙11盛會一般都會產生較高的銷售額,但放到全年來看這一天的銷售額占比很可能只有1%不到,尤其對于很少參與活動的公司而言,這種情況更加明顯。這種屬于典型的低頻事件。

抽樣類別

抽樣是解決樣本分布不均衡相對簡單且常用的方法,包括過抽樣和欠抽樣兩種。

過抽樣

過抽樣(也叫上采樣、over-sampling)方法通過增加分類中少數(shù)類樣本的數(shù)量來實現(xiàn)樣本均衡,最直接的方法是簡單復制少數(shù)類樣本形成多條記錄,這種方法的缺點是如果樣本特征少而可能導致過擬合的問題;經過改進的過抽樣方法通過在少數(shù)類中加入隨機噪聲、干擾數(shù)據(jù)或通過一定規(guī)則產生新的合成樣本,例如SMOTE算法。

欠抽樣

欠抽樣(也叫下采樣、under-sampling)方法通過減少分類中多數(shù)類樣本的樣本數(shù)量來實現(xiàn)樣本均衡,最直接的方法是隨機地去掉一些多數(shù)類樣本來減小多數(shù)類的規(guī)模,缺點是會丟失多數(shù)類樣本中的一些重要信息。

總體上,過抽樣和欠抽樣更適合大數(shù)據(jù)分布不均衡的情況,尤其是第一種(過抽樣)方法應用更加廣泛。

實現(xiàn)方式

本文中使用開放的微博4種情緒數(shù)據(jù)集 simplifyweibo_4_modes.csv 作為樣本數(shù)據(jù)進行數(shù)據(jù)處理操作,其中所有的預操作如下:

import sys
import os
import pandas as pd

import jieba

from keras_preprocessing.sequence import pad_sequences
# keras 里類似
from preprocessing.text import Tokenizer
# 模型評價工具
from sklearn import metrics

# xgboost / lightgbm 模型
import xgboost as xgb
import lightgbm as lbm

# 多模型投票
from sklearn.ensemble import VotingClassifier
from collections import Counter
# 重采樣
from imblearn.under_sampling import RandomUnderSampler
sys.path.extend([os.path.dirname(os.getcwd())])

dataset = pd.read_csv("data/samples/simplifyweibo_4_moods.csv", header=0)
moods = {0: '喜悅', 1: '憤怒', 2: '厭惡', 3: '低落'}
x = dataset['review'].values

def transform(a):
    return moods[a]

# 將數(shù)字標簽轉為文字標簽
y = dataset['label'].apply(transform).values

# 進行結巴分詞并添加 padding
data_tokenizer = Tokenizer(split=jieba.cut)
data_tokenizer.fit_on_texts(x)
data_seq = data_tokenizer.texts_to_sequences(x)
data_seq = pad_sequences(data_seq, maxlen=200)

1. SMOTE 抽樣

print('Original dataset shape %s' % Counter(y))
# 建立 SMOTE模型
smote = SMOTE()
# 對x和y過抽樣處理后的數(shù)據(jù)集,將兩份數(shù)據(jù)集轉換為數(shù)據(jù)框然后合并為一個整體數(shù)據(jù)框
data_seq, y = smote.fit_resample(data_seq, y)
print('resampled dataset shape %s' % Counter(y))

# 建立 SMOTE + ENN
smote = SMOTEENN()
# 對x和y過抽樣處理后的數(shù)據(jù)集,將兩份數(shù)據(jù)集轉換為數(shù)據(jù)框然后合并為一個整體數(shù)據(jù)框
data_seq, y = smote.fit_resample(data_seq, y)
print('resampled dataset shape %s' % Counter(y))

# 建立 SMOTE + Tomek
smote = SMOTETomek()
# 對x和y過抽樣處理后的數(shù)據(jù)集,將兩份數(shù)據(jù)集轉換為數(shù)據(jù)框然后合并為一個整體數(shù)據(jù)框
data_seq, y = smote.fit_resample(data_seq, y)
print('resampled dataset shape %s' % Counter(y))

過抽樣方法通過在少數(shù)類中加入隨機噪聲、干擾數(shù)據(jù)或通過一定規(guī)則產生新的合成樣本。

2. 隨機抽樣

print('Original dataset shape %s' % Counter(y))
# 數(shù)據(jù)樣本會以最小樣本數(shù)在多樣本中進行隨機采樣
rus = RandomUnderSampler()
data_seq, y = rus.fit_resample(data_seq, y)
print('resampled dataset shape %s' % Counter(y))

# 隨機選取數(shù)據(jù)的子集
ros = RandomOverSampler(random_state=0)
data_seq, y = ros.fit_resample(data_seq, y)
print('resampled dataset shape %s' % Counter(y))

經過 RandomUnderSampler 重采樣之后,。

3. ADASYN 抽樣

from imblearn.over_sampling import ADASYN

print('Original dataset shape %s' % Counter(y))
adasyn = ADASYN()
data_seq, y = adasyn.fit_resample(data_seq, y)
print('resampled dataset shape %s' % Counter(y))

SMOTE 算法與 ADASYN 都是基于同樣的算法來合成新的少數(shù)類樣本

4. 原型生成

from imblearn.under_sampling import ClusterCentroids
 
print('Original dataset shape %s' % Counter(y))
cc = ClusterCentroids(random_state=0)
data_seq, y = cc.fit_resample(data_seq, y)
print('resampled dataset shape %s' % Counter(y))

每一個類別的樣本都會用K-Means算法的中心點來進行合成, 而不是隨機從原始樣本進行抽取.

5. 最近鄰算法下采樣

應用最近鄰算法來編輯(edit)數(shù)據(jù)集, 找出那些與鄰居不太友好的樣本然后移除. 對于每一個要進行下采樣的樣本, 那些不滿足一些準則的樣本將會被移除; 他們的絕大多數(shù)(kind_sel='mode')或者全部(kind_sel='all')的近鄰樣本都屬于同一個類, 這些樣本會被保留在數(shù)據(jù)集中.

from imblearn.under_sampling import EditedNearestNeighbours
from imblearn.under_sampling import RepeatedEditedNearestNeighbours
from imblearn.under_sampling import AllKNN

print('Original dataset shape %s' % Counter(y))
enn = EditedNearestNeighbours(random_state=0)
data_seq, y = enn.fit_resample(data_seq, y)
print('resampled dataset shape %s' % Counter(y))

# 多次執(zhí)行 EditedNearestNeighbours
renn = RepeatedEditedNearestNeighbours(random_state=0)
data_seq, y = renn.fit_resample(data_seq, y)
print('resampled dataset shape %s' % Counter(y))

# ALLKNN算法在進行每次迭代的時候, 最近鄰的數(shù)量都在增加
allknn = AllKNN(random_state=0)
data_seq, y = allknn.fit_resample(data_seq, y)
print('resampled dataset shape %s' % Counter(y))

# KMeans + SMOTE
sm = KMeansSMOTE(random_state=0)
data_seq, y = sm.fit_resample(data_seq, y)
print('resampled dataset shape %s' % Counter(y))

構建不均衡樣本

from sklearn.datasets import load_iris
from imblearn.datasets import make_imbalance
iris = load_iris()

# 指定數(shù)量生成
ratio = {0: 20, 1: 30, 2: 40}
x_imb, y_imb = make_imbalance(iris.data, iris.target, sampling_strategy=ratio)
# Out[37]: [(0, 20), (1, 30), (2, 40)]
sorted(Counter(y_imb).items()) 
 
# 當類別不指定時, 所有的數(shù)據(jù)集均導入
ratio = {0: 10}
x_imb, y_imb = make_imbalance(iris.data, iris.target, sampling_strategy=ratio)
# Out[38]: [(0, 10), (1, 50), (2, 50)]
sorted(Counter(y_imb).items())
 
# 同樣亦可以傳入自定義的比例函數(shù)
def ratio_multiplier(y):
    multiplier = {0: 0.5, 1: 0.7, 2: 0.95}
    target_stats = Counter(y)
    for key, value in target_stats.items():
        target_stats[key] = int(value * multiplier[key])
    return target_stats
x_imb, y_imb = make_imbalance(iris.data, iris.target, sampling_strategy=ratio_multiplier)
# Out[39]: [(0, 25), (1, 35), (2, 47)]
sorted(Counter(y_imb).items())

常見問題

  1. 安裝 imblearn 包之后,默認會更新 sklearn 包,這時候會導致 sklearn2pmml 報如下錯誤:
Standard output is empty
Standard error:
Apr 15, 2020 9:21:53 AM org.jpmml.sklearn.Main run
INFO: Parsing PKL..
Apr 15, 2020 9:21:53 AM org.jpmml.sklearn.Main run
INFO: Parsed PKL in 17 ms.
Apr 15, 2020 9:21:53 AM org.jpmml.sklearn.Main run
INFO: Converting..
Apr 15, 2020 9:21:53 AM org.jpmml.sklearn.Main run
SEVERE: Failed to convert
java.lang.IllegalArgumentException: The transformer object (Python class sklearn.ensemble._voting.VotingClassifier) is not a supported Transformer
    at org.jpmml.sklearn.CastFunction.apply(CastFunction.java:43)
    at sklearn.pipeline.Pipeline$1.apply(Pipeline.java:121)
    at sklearn.pipeline.Pipeline$1.apply(Pipeline.java:112)
    at com.google.common.collect.Lists$TransformingRandomAccessList.get(Lists.java:599)
    at sklearn.TransformerUtil.getHead(TransformerUtil.java:35)
    at sklearn2pmml.pipeline.PMMLPipeline.encodePMML(PMMLPipeline.java:189)
    at org.jpmml.sklearn.Main.run(Main.java:145)
    at org.jpmml.sklearn.Main.main(Main.java:94)
Caused by: java.lang.ClassCastException: Cannot cast net.razorvine.pickle.objects.ClassDict to sklearn.Transformer
    at java.lang.Class.cast(Class.java:3369)
    at org.jpmml.sklearn.CastFunction.apply(CastFunction.java:41)
    ... 7 more

Exception in thread "main" java.lang.IllegalArgumentException: The transformer object (Python class sklearn.ensemble._voting.VotingClassifier) is not a supported Transformer
    at org.jpmml.sklearn.CastFunction.apply(CastFunction.java:43)
    at sklearn.pipeline.Pipeline$1.apply(Pipeline.java:121)
    at sklearn.pipeline.Pipeline$1.apply(Pipeline.java:112)
    at com.google.common.collect.Lists$TransformingRandomAccessList.get(Lists.java:599)
    at sklearn.TransformerUtil.getHead(TransformerUtil.java:35)
    at sklearn2pmml.pipeline.PMMLPipeline.encodePMML(PMMLPipeline.java:189)
    at org.jpmml.sklearn.Main.run(Main.java:145)
    at org.jpmml.sklearn.Main.main(Main.java:94)
Caused by: java.lang.ClassCastException: Cannot cast net.razorvine.pickle.objects.ClassDict to sklearn.Transformer
    at java.lang.Class.cast(Class.java:3369)
    at org.jpmml.sklearn.CastFunction.apply(CastFunction.java:41)
    ... 7 more

更新 sklearn 版本即可 pip install --upgrade git+https://github.com/jpmml/sklearn2pmml.git

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容