如果分类问题中类别型的因变量可能严重失衡,例如欺诈问题中,欺诈类观测在样本集中占少数;客户流失问题中,忠实的客户也是往往占很少一部分;某营销活动的响应过程中,真正参与的客户也同样只是少部分。
如果数据存在严重的不平衡,预测得出的结论也是有偏的,即分类结果会偏向于较多观测的类,一般构造1:1的数据,要么将多的那一类砍掉一部分(欠采样),要么将少的那一类进行Bootstrap抽样(过采样),前者会丢失隐含信息,后者会形成简单复制,使模型产生过拟合。
对于不平衡类的分类器评价,使用ROC和AUC作为评价分类器的指标
2002年Chawla提出了SMOTE算法,即合成少数过采样技术,是目前处理不平衡数据的常用手段,对少数类别样本进行分析模拟,并将人工模拟的新样本添加到数据集中,进而使类别不再失衡。利用KNN技术,合成的策略是对每个少数类样本a,从它的最近邻中随机选一个样本b,然后在a、b之间的连线上随机选一点作为新合成的少数类样本。
XGBoost是Gradient Boosting的一种高效系统实现,并不是一种单一算法。xgboos也是以(CART)为基学习器的GB算法,但是扩展和改进了GDBT。相比GBDT的优点有:
(1)xgboost在代价函数里自带加入了正则项,用于控制模型的复杂度。
(2)xgboost在进行节点的分裂时,支持各个特征多线程进行增益计算,因此算法更快,准确率也相对高一些。
XGBoost里面的基学习器除了支持CART基础模型,也可用线性分类器(gblinear)。而GBDT则特指梯度提升决策树算法。另外不同于GBDT使用一阶导,XGBoost可以支持不同的损失函数(用户可以自定义),来优化效果,只要函数一、二阶可导即可。除此之外,支持并行运算,提高运行效率;在损失函数中加了正则项,用来控制模型复杂度,防止过拟合;采用了随机森林的思想,对字段进行抽样,可以防止过拟合,也可以降低模型计算量。
有别于随机森林集成,还有提升算法AdaBoost,梯度提升算法GBDT,升级版的梯度算法XGBoost,。
AdaBoost在解决分类问题时,是通过改变样本点的权重大小,并将各个基础模型按权重实现线性组合,最终得到拟合数据的提升树;在解决预测问题时,每一轮基础模型都是拟合上一轮模型所形成的残差,最终将各个基础模型的预测值相加。
不管是分类提升树,还是回归提升树,都是将各个基础模型以串联的形式构成最终的提升树。
在回归提升树中,如果损失函数使用的是平方损失或者指数损失,目标函数的求解会相对简单,为了能够使提升树试用于更多类型的损失函数,衍生出GBDT,即利用损失函数的导函数作为残差的近似值,方便了运算也提高了提升树的灵活性。
AdaBoost和GBDT在构建目标函数时都没有加入反映模型复杂度的正则项,而XGBoost算法实现了正则化的加入,进而可以防止模型的过拟合,并在求解最优化问题时利用了损失函数的一阶导和二阶导。还可以支持并行计算,支持线性的基础模型,支持建模字段的随机选择等。
# 读入数据
creditcard = pd.read_csv(r'F:\creditcard.csv')
creditcard.head()
因变量Class表示用户在交易中是否存在欺诈行为(1表示欺诈,0表示不欺诈)已对原始数据做了主成分分析处理,只有2个没有做(Time交易时间间隔和Amount交易金额)。
# 为确保绘制的饼图为圆形,需执行如下代码
plt.axes(aspect = 'equal')
# 统计交易是否为欺诈的频数
counts = creditcard.Class.value_counts()
# 绘制饼图
plt.pie(x = counts, # 绘图数据
labels=pd.Series(counts.index).map({0:'正常',1:'欺诈'}), # 添加文字标签
autopct='%.2f%%' # 设置百分比的格式,这里保留一位小数
)
# 显示图形
plt.show()
比例严重不平衡,先用SMOTE算法转换
# 将数据拆分为训练集和测试集
# 删除自变量中的Time变量
X = creditcard.drop(['Time','Class'], axis = 1)
y = creditcard.Class
# 数据拆分
X_train,X_test,y_train,y_test = model_selection.train_test_split(X,y,test_size = 0.3, random_state = 1234)
# 导入第三方包
from imblearn.over_sampling import SMOTE
# 运用SMOTE算法实现训练数据集的平衡
over_samples = SMOTE(random_state=1234)
over_samples_X,over_samples_y = over_samples.fit_sample(X_train, y_train)
#over_samples_X, over_samples_y = over_samples.fit_sample(X_train.values,y_train.values.ravel())
# 重抽样前的类别比例
print(y_train.value_counts()/len(y_train))
# 重抽样后的类别比例
print(pd.Series(over_samples_y).value_counts()/len(over_samples_y))
类别比例达到平衡,构建XGBoost模型(版本受限,XGBoost模块未下载成功,结果未运行)
# 导入第三方包
import xgboost
import numpy as np
# 构建XGBoost分类器
xgboost = xgboost.XGBClassifier()
# 使用重抽样后的数据,对其建模
xgboost.fit(over_samples_X,over_samples_y)
# 将模型运用到测试数据集中
resample_pred = xgboost.predict(np.array(X_test))
# 返回模型的预测效果
print('模型的准确率为:\n',metrics.accuracy_score(y_test, resample_pred))
print('模型的评估报告:\n',metrics.classification_report(y_test, resample_pred))
模型的预测准确率超过99%,而且模型对欺诈交易的覆盖率高达88%(正确预测为欺诈的交易量/实际为欺诈的交易量),对正常交易的覆盖率高达99%,以上为默认参数,还可以利用交叉验证获得更加参数组合,下面运用ROC曲线验证模型在测试集上的表现
# 计算欺诈交易的概率值,用于生成ROC曲线的数据
y_score = xgboost.predict_proba(np.array(X_test))[:,1]
fpr,tpr,threshold = metrics.roc_curve(y_test, y_score)
# 计算AUC的值
roc_auc = metrics.auc(fpr,tpr)
# 绘制面积图
plt.stackplot(fpr, tpr, color='steelblue', alpha = 0.5, edgecolor = 'black')
# 添加边际线
plt.plot(fpr, tpr, color='black', lw = 1)
# 添加对角线
plt.plot([0,1],[0,1], color = 'red', linestyle = '--')
# 添加文本信息
plt.text(0.5,0.3,'ROC curve (area = %0.2f)' % roc_auc)
# 添加x轴与y轴标签
plt.xlabel('1-Specificity')
plt.ylabel('Sensitivity')
# 显示图形
plt.show()
面积AUC高达0.98,拟合效果非常优秀
为了体现SMOTE算法在非平衡数据上的价值,利用XGBoost算法直接在非平衡数据上建模,比较差异
# 构建XGBoost分类器
xgboost2 = xgboost.XGBClassifier()
# 使用非平衡的训练数据集拟合模型
xgboost2.fit(X_train,y_train)
# 基于拟合的模型对测试数据集进行预测
pred2 = xgboost2.predict(X_test)
# 混淆矩阵
pd.crosstab(pred2,y_test)
# 返回模型的预测效果
print('模型的准确率为:\n',metrics.accuracy_score(y_test, pred2))
print('模型的评估报告:\n',metrics.classification_report(y_test, pred2))
准确率高达100%,但是是由于数据的不平衡性,导致结果的有偏
基于非平衡数据,也绘制ROC曲线,通过比对AUC的值,对比两个模型的好坏
# 计算欺诈交易的概率值,用于生成ROC曲线的数据
y_score = xgboost2.predict_proba(X_test)[:,1]
fpr,tpr,threshold = metrics.roc_curve(y_test, y_score)
# 计算AUC的值
roc_auc = metrics.auc(fpr,tpr)
# 绘制面积图
plt.stackplot(fpr, tpr, color='steelblue', alpha = 0.5, edgecolor = 'black')
# 添加边际线
plt.plot(fpr, tpr, color='black', lw = 1)
# 添加对角线
plt.plot([0,1],[0,1], color = 'red', linestyle = '--')
# 添加文本信息
plt.text(0.5,0.3,'ROC curve (area = %0.2f)' % roc_auc)
# 添加x轴与y轴标签
plt.xlabel('1-Specificity')
plt.ylabel('Sensitivity')
# 显示图形
plt.show()
AUC的值为0.97,相比平衡数据所构建的模型,AUC值要小0.1,进而验证了利用SMOTE算法实现数据的平衡是有必要的,通过平衡数据,可以获得更加稳定和更具泛化能力的模型。