SMOTE(Synthetic Minority Over-Sampling Technique)非平衡數據處理方法

歡迎各位同學學習python信用評分卡建模視頻系列教程(附代碼, 博主錄製,包含非平衡數據處理方法講解) :

騰訊課堂報名入口

網易雲課堂報名入口

(微信掃一掃報名)

一、SMOTE原理

SMOTE的全稱是Synthetic Minority Over-Sampling Technique 即“人工少數類過採樣法”,非直接對少數類進行重採樣,而是設計算法來人工合成一些新的少數樣本。

SMOTE步驟__1.選一個正樣本

紅色圈覆蓋

 

SMOTE步驟__2.找到該正樣本的K個近鄰(假設K = 3)

 

SMOTE步驟__3.隨機從K個近鄰中選出一個樣本

綠色的

 

SMOTE步驟__4.在正樣本和隨機選出的這個近鄰之間的連線上,隨機找一點。這個點就是人工合成的新正樣本了

 

二、調包實現

2.1 R調包實現_SMOTE

 

2.2 Python 調包實現_SMOTE

    imblearn.over_sampling.SMOTE(

    sampling_strategy = ‘auto’,

    random_state = None, ## 隨機器設定

    k_neighbors = 5, ## 用相近的 5 個樣本(中的一個)生成正樣本

    m_neighbors = 10, ## 當使用 kind={'borderline1', 'borderline2', 'svm'}

    out_step = ‘0.5’, ## 當使用kind = 'svm'

    kind = 'regular', ## 隨機選取少數類的樣本

    – borderline1: 最近鄰中的隨機樣本b與該少數類樣本a來自於不同的類

    – borderline2: 隨機樣本b可以是屬於任何一個類的樣本;

    – svm:使用支持向量機分類器產生支持向量然後再生成新的少數類樣本

    svm_estimator = SVC(), ## svm 分類器的選取

    n_jobs = 1, ## 使用的例程數,爲-1時使用全部CPU

    ratio=None )

 

from imblearn.over_sampling import SMOTE
 
sm = SMOTE(random_state = 42, n_jobs = -1)
 
x, y = sm.fit_sample(x_val, y_val)

 僅用正樣本的K近鄰生成新正樣本是正是SMOTE方法,考慮到(SMOTE的最終目的是分清正負樣本的邊界),所以需要對樣本生成進行優化

2.2.1 SMOTE優化 borderline1 方法簡述

Dgr = [] # 危險集

for i in 正樣本:

      1) 計算點 i 在訓練集 D 上的 m 個最近鄰。

         x =  i 的最近鄰中屬於負樣本的數量

      2) 如果 x  = m,則 p 是一個噪聲

          next

      3) 如果 0 ≤ x ≤ m/2, 則說明p很安全

          next

      4) 如果 m/2 ≤ x ≤ m, 那麼點p就很危險了,我們需要在這個點附近生成一些新的少數類點

         Dgr.append(x)

最後,對於每個在危險集(Dgr)中的點,使用SMOTE算法生成新的樣本

 

2.2.2 SMOTE優化 borderline2 方法簡述

前面1-4步驟均同 borderline1 方法

在最後進行SMOTE的時候:

採用了 比例分配 生成新樣本

for i in Dgr:

    1) 正樣本 K 個近鄰

    2) 負樣本 K 個近鄰

    3) 正樣本 K 個近鄰選取 alpha 比例的樣本點

     和 i 作隨機的線性插值 ==>> 新正樣本點

    4) 負樣本K個近鄰選取 (1 - alpha) 比例的樣本點

     和 i 作隨機的線性插值 ==>> 新正樣本點

 

三、算法實現

#! /user/bin/python 3
# -*- coding: utf-8 -*-
# author: Scc_hy
# 2018-11-17
# SMOTE
from sklearn.neighbors import NearestNeighbors
import numpy as np 
import pandas as pd 
import copy
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
 
class TWO_SMOTE():
    """
    不平二分類人工插值法採樣
    """
    def __init__(self, 
                 K_neighbors = 5,
                 N_need = 200,
                 random_state = 42):
        self.K_neighbors = K_neighbors
        self.N_need = N_need
        self.random_state = 42
    
 
    def get_param_describe(self):
        print(
            "算法參數: \n"+
            'K_neighbors: 和正樣本相近的隨機樣本數' + "\n" +
            "N_need: 需要增加的正樣本數 (N_need // 100 * a)" + "\n" +
            "random_state: 隨機器設定" + "\n"
            "\nover_sample 參數:\n" +
            "x_data: 需要進行過採樣的全部數據集(非文本DataFrame)" + "\n" +
            "y_label: 類別標籤(非文本DataFrame.Series)"+ "\n" 
        )
 
    def div_data(self, x_data, y_label):
        """
        將數據依據類分開
        """
        tp = set(y_label)
        tp_less = [a for a in tp if sum(y_label == a) < sum(y_label != a)][0]
        data_less = x_data.iloc[y_label == tp_less, :]
        data_more = x_data.iloc[y_label != tp_less, :]
        tp.remove(tp_less)
        return data_less, data_more, tp_less, list(tp)[0]
    
    def get_SMOTE_sample(self, x_data, y_label):
        """
        獲取需要抽樣的正樣本
        """
        sample = []
        data_less, data_more, tp_less, tp_more = self.div_data(x_data, y_label)
        n_integ = self.N_need // 100
        data_add = copy.deepcopy(data_less)
        if n_integ == 0 :
            print('WARNING: PLEASE RE-ENTER N_need')
        else:
            for i in range(n_integ-1):
               data_out =  data_less.append(data_add)
 
        data_out.reset_index(inplace = True, drop = True)
        return data_out, tp_less
 
    def over_sample(self, x_data, y_label):
        """
        SMOTE算法簡單實現
        """
        sample, tp_less = self.get_SMOTE_sample(x_data, y_label)
        knn = NearestNeighbors(n_neighbors = self.K_neighbors ,n_jobs = -1).fit(sample)
        n_atters = x_data.shape[1]
        label_out = copy.deepcopy(y_label)
        new = pd.DataFrame(columns = x_data.columns)
        for i in range(len(sample)): # 1. 選擇一個正樣本
            # 2.選擇少數類中最近的K個樣本
            k_sample_index = knn.kneighbors(np.array(sample.iloc[i, :]).reshape(1, -1),
                                            n_neighbors = self.K_neighbors + 1,
                                            return_distance = False)
 
            # 計算插值樣本
            # 3.隨機選取K中的一個樣本
            np.random.seed(self.random_state)
            choice_all = k_sample_index.flatten()
            choosed = np.random.choice(choice_all[choice_all != 0])
 
            # 4. 在正樣本和隨機樣本之間選出一個點
            diff = sample.iloc[choosed,] - sample.iloc[i,]
            gap = np.random.rand(1, n_atters)
            new.loc[i] = [x for x in sample.iloc[i,] + gap.flatten() * diff]
            label_out = np.r_[label_out, tp_less]
 
        new_sample = pd.concat([x_data, new])
        new_sample.reset_index(inplace = True, drop = True)
        return new_sample, label_out
 
if __name__ == '__main__':
    iris = load_iris()
    irisdf = pd.DataFrame(data = iris.data, columns = iris.feature_names)     
    y_label = iris.target
    # 生成不平二分類數據
    iris_1 = irisdf.iloc[y_label == 1,]
    iris_2 = irisdf.iloc[y_label == 2,]
    iris_2imb = pd.concat([iris_1, iris_2.iloc[:10, :]])
    label_2imb =np.r_[y_label[y_label == 1], y_label[y_label == 2][:10]]
    iris_2imb.reset_index(inplace = True, drop = True)
 
    smt  = TWO_SMOTE()
    x_new, y_new = smt.over_sample(iris_2imb, label_2imb)

 轉https://blog.csdn.net/haoji007/article/details/106166305/

 

python金融風控評分卡模型和數據分析微專業課

騰訊課堂報名入口

網易雲課堂報名入口

(微信掃一掃報名)

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章