过采样
- 代价敏感加权对不均衡问题有一定帮助,但如果想达到更好的效果,仍需为模型引入更多的负样本。
- 过采样是常见的一种样本不均衡的解决方案,常用的过采样方法
- 随机过采样:将现有样本复制,但训练得到的模型泛化能力通常较差
- SMOTE-少数类别过采样技术(Synthetic Minority Oversampling Technique)
SMOTE算法
- SMOTE算法是一种用于合成少数类样本的过采样技术
- 其基本思想是对少数类样本进行分析,然后在现有少数类样本之间进行插值,人工合成新样本,并将新样本添加到数据集中进行训练
- 该技术是目前处理非平衡数据的常用手段,并受到学术界和工业界的一致认同
- SMOTE算法基本步骤如下
- 采样最邻近算法,计算出每个少数类样本的K个近邻
- 从K个近邻中随机挑选N个样本进行随机线性插值
- 构造新的少数类样本
- 将新样本与原数据合成,产生新的训练集
SMOTE核心:就是通过人工合成的方式把少数类样本数量增加,直到和多数类样本数量一致。
SMOTE案例
- 接下来通过引入SMOTE算法使该模型得到更好的模型效果。由于SMOTE算法是基于样本空间进行插值的,会放大数据集中的噪声和异常,因此要对训练样本进行清洗。这里使用LightGBM算法对数据进行拟合,将预测结果较差的样本权重降低不参与SMOTE算法的插值过程。
- 导包
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score,roc_curve,auc
import lightgbm as lgb
- 获取数据
data = pd.read_csv('data/Bcard.txt')
data.head()
feature_lst = ['person_info','finance_info','credit_info','act_info']
train = data[data.obs_mth != '2018-11-30'].reset_index().copy()
val = data[data.obs_mth == '2018-11-30'].reset_index().copy()
x = train[feature_lst]
y = train['bad_ind']
val_x = val[feature_lst]
val_y = val['bad_ind']
- 创建lightGBM方法,返回AUC
def lgb_test(train_x,train_y,test_x,test_y):
clf =lgb.LGBMClassifier(boosting_type = 'gbdt',
objective = 'binary',
metric = 'auc',
learning_rate = 0.1,
n_estimators = 24,
max_depth = 4,
num_leaves = 25,
max_bin = 40,
min_data_in_leaf = 5,
bagging_fraction = 0.6,
bagging_freq = 0,
feature_fraction = 0.8,
)
clf.fit(train_x,train_y,eval_set=[(train_x,train_y),(test_x,test_y)],eval_metric = 'auc')
return clf,clf.best_score_['valid_1']['auc']
- 选择四个比较重要的特征
feature_lst = ['person_info','finance_info','credit_info','act_info']
#准备数据
train_x = train[feature_lst]
train_y = train['bad_ind']
test_x = val[feature_lst]
test_y = val['bad_ind']
#调用函数,进行训练
lgb_model,lgb_auc = lgb_test(train_x,train_y,test_x,test_y)
#模型预测
sample = train_x.copy()
sample['bad_ind'] = train_y
sample['pred'] = lgb_model.predict_proba(train_x)[:,1]
#对预测结果进行降序排序,这样,排序后的结果中,预测为正样本(坏人)概率越大的越靠前
sample = sample.sort_values(by=['pred'],ascending=False).reset_index()
sample['rank'] = sample.index.values/len(sample)
sample
- 定义函数去掉预测值与实际值不符的部分
#x:真实的结果
#y:预测的结果
#过滤预测和真实的情况严重不相符的样本
#由于上述表格中,我们是按照pred降序排序,因此越往前,是正样本(坏人)的概率越大,但是实际上是负样本(好人),所以这部分的数据要排除,同理,越往后,是负样本(好人)的概率越大,但是实际上是正样本(坏人),这部分的数据也要排除。
def weight(x, y):
# 真实标签为0,违约概率为所有样本中最高的10%
if x == 0 and y < 0.1:
return 0.1
# 真实标签为1 违约概率为所有样本中最低的30%
elif x == 1 and y > 0.7:
return 0.1
else:
return 1
sample['weight'] = sample.apply(lambda row:weight(row['bad_ind'],row['rank']),axis = 1)
#把预测相对比较准的取出来进行过采样
smote_sample = sample[sample.weight == 1]
train_x_smote = smote_sample[feature_lst]
train_y_smote = smote_sample['bad_ind']
smote_sample.shape
- 创建smote过采样函数,进行过采样
def smote(train_x_smote,train_y_smote,K=15,random_state=0):
from imblearn.over_sampling import SMOTE
smote = SMOTE(k_neighbors=K, n_jobs=1,random_state=random_state)
#fit_resample,找K个邻居,然后进行过采样
rex,rey = smote.fit_resample(train_x_smote,train_y_smote)
return rex,rey
rex,rey =smote(train_x_smote,train_y_smote)
#查看数据
train_y_smote.value_counts()
rey.value_counts()
- 使用过采样数据建模,使用训练集数据和测试集数据验证
# 使用过采样数据建模, 使用训练集数据和测试集数据验证
lr_model = LogisticRegression(C=0.1)
lr_model.fit(rex[feature_lst],rey)
x = train[feature_lst]
y = train['bad_ind']
val_x = val[feature_lst]
val_y = val['bad_ind']
y_pred = lr_model.predict_proba(x)[:,1] #取出训练集预测值
fpr_lr_train,tpr_lr_train,_ = roc_curve(y,y_pred) #计算TPR和FPR
train_ks = abs(fpr_lr_train - tpr_lr_train).max() #计算训练集KS
print('train_ks : ',train_ks)
y_pred = lr_model.predict_proba(val_x)[:,1] #计算验证集预测值
fpr_lr,tpr_lr,_ = roc_curve(val_y,y_pred) #计算验证集预测值
val_ks = abs(fpr_lr - tpr_lr).max() #计算验证集KS值
print('val_ks : ',val_ks)