案例:基于集成算法GBDT和RandomForest的投票组合模型的异常检测
part 1 导入库
import numpy as np
import pandas as pd
from imblearn.over_sampling import SMOTE #过抽样处理库SMOTE
from sklearn.ensemble import VotingClassifier,GradientBoostingClassifier,RandomForestClassifier #集成分类库和投票方法库
from sklearn.model_selection import StratifiedKFold,cross_val_score #交叉检验算法
from sklearn.preprocessing import OrdinalEncoder #字符串转数值
part 2函数模块
定义了一个基于日期和时间派生出更多特征的函数
def datetime_exp(data):
date_set = [pd.datetime.strptime(dates,'%Y-%m-%d') for dates in data['order_date']] #将data中的order_date列转换为特定日期格式
data['weekday_data'] = [data.weekday() for data in date_set]
data['daysinmonth_data'] = [data.day for data in date_set]
data['month_data'] = [data.month for data in date_set]
time_set = [pd.datetime.strptime(times, '%H:%M:%S') for times in data['order_time']] #将data中的order_time列转换为特定日期格式
data['second_data'] = [data.second for data in time_set]
data['minute_data'] = [data.minute for data in time_set]
data['hour_data'] = [data.hour for data in time_set]
return data.drop(['order_date','order_time'],axis=1)
part 3 读取数据
raw_data = pd.read_table('abnormal_orders.txt',delimiter=',')
part 4 数据审查
#查看基本状态
print('{:*^60}'.format('Data overview:'))
print(raw_data.tail(2))
print('{:*^60}'.format('Data dtypes:'))
print(raw_data.dtypes)
print('{:*^60}'.format('Data DESC:'))
print(raw_data.describe().round(2).T)
结果:
***********************Data overview:***********************
order_id order_date order_time cat attribution pro_id \
134188 4285770012 2013-09-19 23:55:06 家居日用 GO 1000335947
134189 4285770056 2013-05-20 23:58:59 生活电器厨卫电器 GO 1000009280
pro_brand total_money total_quantity order_source pay_type \
134188 炊大师 79.0 1 抢购 合并支付
134189 海尔 799.0 1 抢购 合并支付
user_id city abnormal_label
134188 shukun 东莞市 0
134189 544975322_ 海口市 0
************************Data dtypes:************************
order_id int64
order_date object
order_time object
cat object
attribution object
pro_id int64
pro_brand object
total_money float64
total_quantity int64
order_source object
pay_type object
user_id object
city object
abnormal_label int64
dtype: object
*************************Data DESC:*************************
count mean std min \
order_id 134190.0 4.214285e+09 1.510533e+08 3.000316e+09
pro_id 134190.0 3.404167e+09 3.287444e+09 1.000000e+09
total_money 134189.0 6.601100e+02 2.901210e+03 5.000000e-01
total_quantity 134190.0 1.200000e+00 3.230000e+00 1.000000e+00
abnormal_label 134190.0 2.100000e-01 4.100000e-01 0.000000e+00
25% 50% 75% max
order_id 4.203350e+09 4.276630e+09 4.281996e+09 4.285770e+09
pro_id 1.000321e+09 1.000369e+09 8.001623e+09 8.002352e+09
total_money 2.900000e+01 9.840000e+01 3.720000e+02 7.660000e+05
total_quantity 1.000000e+00 1.000000e+00 1.000000e+00 1.000000e+03
abnormal_label 0.000000e+00 0.000000e+00 0.000000e+00 1.000000e+00
Data overview:
每个字段都已经被正确识别。数据里有散列特殊字段:order_id、pro_id和user_id。Order_id在所有记录中都是唯一值,没有实际意义,之后会去掉。pro_id和user_id作为商品和用户的唯一识别标志,可以有一定的参考价值,后续不会去掉。
Data DESC:
total_money和total_quantity存在极大值,由于案例是针对异常值检测,所以不处理。
#缺失值审查:
na_cols = raw_data.isnull().any(axis=0)
print('{:*^60}'.format('NA Cols:'))
print(na_cols[na_cols]==True)
print('Total number of NA lines is: {0}'.format(raw_data.isnull().any(axis=1).sum()))
**************************NA Cols:**************************
cat True
pro_brand True
total_money True
city True
dtype: bool
Total number of NA lines is: 1429
按列查看缺失值
na_cols[na_cols]==True 过滤出包含缺失值为True的列
通过结果分析,总的缺失记录为1429,占总体样本量的1%左右可以考虑直接丢弃。
#类样本均衡审查
print('{:*^60}'.format('Labesl samples cpunt:'))
print(raw_data.iloc[:,-1].value_counts())
*******************Labesl samples cpunt:********************
0 105733
1 28457
Name: abnormal_label, dtype: int64
负样本的比例只有正样本的20%左右,需要做样本均衡处理。
part 5 数据预处理
直接使用dropna方法丢弃缺失值
#Nan处理:
drop_na_set = raw_data.dropna()
#丢弃订单ID列:
drop_na_set = drop_na_set.drop(['order_id'],axis = 1)
字符串转数值
convert_cols = ['cat', 'attribution', 'pro_id', 'pro_brand', 'order_source', 'pay_type','user_id', 'city'] # 定义要转换的列
enc = OrdinalEncoder()
drop_na_set[convert_cols]=enc.fit_transform(drop_na_set[convert_cols])
分割测试机和训练集
num = int(0.7*data_final.shape[0])
X_raw, y_raw = data_final.drop(['abnormal_label'],axis=1), data_final['abnormal_label']
X_train,X_test = X_raw.iloc[:num,:],X_raw.iloc[num:,:]
y_train,y_test = y_raw.iloc[:num],y_raw.iloc[num:]
样本均衡
model_smote = SMOTE()
x_smote_resampled,y_smote_resampled = model_smote.fit_resample(x_train,y_train)
使用SMOTE方法对不均衡样本进行处理,先建立模型,然后使用fit_sample方法做训练后的抽样处理。
part 6 模型训练
#交叉检验:
model_rf = RandomForestClassifier(max_features=0.8,random_state=0)
model_gdbc = GradientBoostingClassifier(max_features=0.8,random_state=0)
estimators = [('randomforest',model_rf),('gradientboosting',model_gdbc)] #建立组合评估器列表
model_vot = VotingClassifier(estimators=estimators,voting='soft',weights=[0.9,1.2],n_jobs=1) #建立组合评估模型
cv = StratifiedKFold(5) #交叉检验方法
cv_score = cross_val_score(model_gdbc,x_smote_resampled,y_smote_resampled,cv=cv) #交叉检验
print('{:*^60}'.format('Cross val scores:'),'\n',cv_score)
print('Mean score is: %.2f' % cv_score.mean())
*********************Cross val scores:**********************
[0.52150039 0.81140029 0.89191384 0.79624115 0.75687382]
Mean score is: 0.76
#训练模型:
model_vot.fit(x_smote_resampled,y_smote_resampled)
从交叉检验结果看,5词交叉检验除第一次结果较差外,其他检验效果都不错,整体得分达到0.76。
part 7 新数据集做预测
#读取数据
x_new = pd.read_csv('new_abnormal_orders.csv')
#丢掉order_id列
x_new_drop = x_new.drop(['order_id'],axis=1)
#字符串转数值
x_new_drop[convert_cols] = enc.transform(x_new_drop[convert_cols])
#日期特征拓展
x_new_final = datetime_exp(x_new_drop)
#预测结果
predict_label = model_vot.predict(x_new_final)
predict_proba = model_vot.predict_proba(x_new_final)
predict_np = np.hstack((predict_label.reshape(-1,1),predict_proba))
predict_pd = pd.DataFrame(predict_np,columns=['label','proba_0','proba_1'])
#输出
print('{:*^60}'.format('Predicted Labesls:'),'\n',predict_pd)
*********************Predicted Labesls:*********************
label proba_0 proba_1
0 1.0 0.383849 0.616151
1 0.0 0.745019 0.254981
2 0.0 0.990146 0.009854
3 0.0 0.989648 0.010352
4 0.0 0.990495 0.009505
5 0.0 0.768635 0.231365
6 0.0 0.823290 0.176710
总结
在该案例中,76%的准确值是一个中等结果。由于我们没有优化参数,所以准确度不是特别高。
需要注意的是:字符串转整数型分类,应用的前提是训练集中被转换的唯一值域必须是固定的。还需注意预测的新数据集中是否存在NA值。