Bagging 思想回顾
所谓 Bagging 思想简单来说就是多个模型取平均,以增强模型的预测结果。
注:集成算法参考文档 http://ml-ensemble.com/info/tutorials/start.html 。
Bagging 的思想可以参考 ML-ENSEMBLE 官网的一张图来理解还是非常形象的:对于数据源分别选择 2 种预处理方案,在不同预处理方案中再建立不同模型,最终汇总所有模型结果。
我们从图片的左边开始看,原始数据集通过不同的数据预处理方式,如标准化、归一化等,得到不同的数据预处理数据集。再通过选择不同的机器学习模型,如 KNN、逻辑回归、决策树、随机森林、支持向量机等。最后将所有模型预测的结果取平均、众数来做为集成算法的预测结果。
数据预处理及数据集切分
导入工具包,读取数据集:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
df = pd.read_csv('input.csv')
df.head()
读取的数据集:
该数据集是关于美国民主党和共和党历年来的捐赠信息:其中第一列 cand_pty_affiliation
为我们接下来要预测的标签,即民主党和共和党,一个典型的二分类任务。
特征列中,蓝色框中的特种为非数值类型的分类特征(属性特征),如:
-
entity_tp
表示个人还是组织 -
classification
表示捐赠的领域 -
rpt_tp
为贡献的等级 - 等等
以及数值型特征:
-
cycle
表示捐赠在哪年 -
transaction_amt
表示捐增的金额
对于分类类型的特征,我们需要使用 pd.get_dumies
以度热编码的形式返回。什么是独热编码呢?举个例子就了解啦~
上述的数据集中,特征 a
为分类特征,那么使用 OneHot Encoding 编码之后就是:
pd.get_dummies(df)
编码之后,原来的特征 a
拆分成了 a_cat
和 a_dog
两列,对于每一条记录,a_cat
和 a_dog
只有 1 位有效。
比如:第一条数据 a_cat
为有效位,第二条数据 a_dog
为有效位。因此,独热编码又被成为 1 位有效码,使用 N 位来表示 N 个状态。
接下来,我们对数据集进行拆分、以及编码:
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
SEED = 666
np.random.seed(SEED)
def get_train_test(test_size=0.95):
y = 1 * (df.cand_pty_affiliation == "REP")
X = df.drop(["cand_pty_affiliation"], axis=1)
# 对分类型变量进行编码处理
X = pd.get_dummies(X)
return train_test_split(X, y, test_size=test_size, random_state=SEED)
首先,小鱼设置了随机数的种子为 666 ,这样每次运行代码时,都会按照相同的策略随机,保证多次运行的随机结果是一样的。
接下来是拆分数据集的函数 get_train_test(test_size=0.95)
,关键字参数 test_size=0.95
,也就是说训练集小鱼只取了 5% 的样本,这里小鱼只是为了初次编写代码时,尽快得到结果,因为原始数据集包含了 10 万个样本。
标签的处理方式中,使用 1
表示 REP
政党,使用 0
表示 DEM
政党,对应的代码为 y = 1 * (df.cand_pty_affiliation == "REP")
。
最后是特征的独热编码,然后调用 sklearn.model_selection
提供的 train_test_split
将数据集划分为训练集和测试集,拆分时指定了测试集的占比以及随机数的种子。
>> xtrain, xtest, ytrain, ytest = get_train_test()
>> xtrain
目前,测试集数据包含了 5000 个样本,每个样本具有 158 个特征。接下来,我们来观察一下训练集特征的标准差:
>> xtrain.std().sort_values()
state_AE 0.000000
cand_office_st_VI 0.000000
state_AP 0.000000
cand_office_st_GU 0.000000
state_VI 0.000000
...
classification_Engineer 0.431895
cand_office_st_US 0.445443
cand_office_P 0.445443
cycle 2.904429
transaction_amt 503.121407
Length: 158, dtype: float64
我们发现,158 个特征中存在很多标准差为 0 的特征,也就是训练集样本中这些特征的值都是一样的,整个数据集中基本上所有样本的这些特征也都是相等的。
值都一样,对于我们的分类任务,这些特征自然也就没有意义啦~接下来,就把标准差为 0 的特征 DROP 掉。
>> drop_columns = xtrain.columns[xtrain.std() == 0]
>> drop_columns
Index(['cand_office_st_DC', 'cand_office_st_GU', 'cand_office_st_VI',
'rpt_tp_M11', 'rpt_tp_M12', 'rpt_tp_MY', 'transaction_tp_11',
'entity_tp_CAN', 'entity_tp_IND', 'entity_tp_ORG', 'state_AE',
'state_AP', 'state_AS', 'state_GU', 'state_QC', 'state_VI'],
dtype='object')
将训练集和测试集中的这些列全部删除:
>> xtrain.drop(drop_columns, axis=1, inplace=True)
>> xtest.drop(drop_columns, axis=1, inplace=True)
>> xtrain.head()
目前剩下了 142 个特征:
标签也完成了 0 和 1 的转换:
>> ytrain.head()
2337 1
76735 1
74508 0
57640 0
39640 0
Name: cand_pty_affiliation, dtype: int32
评估方法 - ROC 曲线与 AUC 面积
前面的连载中,小鱼曾为大家介绍过召回率、精度以及混淆矩阵的概念。我们先来回顾一下:
上图是小鱼绘制的混淆矩阵示意图,横轴为真实值,纵轴为预测值,其中正对角线上的值为预测正确的样本数:
- TP:正确地预测为了正样本;
- TN:正确地预测为了负样本;
反对角线上则为预测错的样本:
- FP:错误地预测成了正样本,实际为负样本,即误杀项;
- FN:错误地预测成了负样本,实际为正样本,即漏网之鱼。
由于我们预测任务时,关注的是正样本即 Positive ,为此引出了 TPR 和 FPR:
- TPR:TP / P = TP / (TP+FN) 即召回率 Racall ;
- FPR:FP / N = FP / (FP+TN)
此外,根据混淆矩阵,还可以计算精度,即模型整体预测对了多少:
- accuracy = (TP + TN) / (P + N)
有了 FPR 和 TPR 就可以引入我们的 ROC 曲线啦~
ROC 曲线的横坐标为 FPR ,即负样本中被误杀的比率;TPR 为纵坐标,即召回率,正样本中被成功召回(预测)的比率。
下面,我们来看 ROC 曲线中 4 个具有特殊意义的点:
-
(0, 1):我们希望模型的召回率 TPR 越高越好,同时误杀比率 FPR 越低越好,因此
(0, 1)
就是模型的完美位置。 - (0, 0):TPR 和 FPR 都为 0 ,即召回率和误杀率皆为 0 ,所有样本都被预测成了负样本,这样的分类器是毫无价值的。
- (1, 0):误杀率 FPR = 1,召回率 TPR = 0,这是个最糟糕的分类器,把所有样本都完美地预测错了,正样本预测为了负样本,负样本预测成了正样本。
- (1, 1):误杀率 FPR = 1,召回率 TPR = 1,这个分类器把所有样本都预测成了正样本,宁可错杀以前不肯放过一个,这样的分类器也是没有价值的。
综上所述,我们希望 ROC 曲线越靠近左上角的 (0,1) 点越好,这样可以得到较高的召回率,同时误杀率也很低。ROC 曲线越靠近 (0,1)点,那么图中 ROC 曲线下方的面积(图中绿色部分)也就会越大。
- ROC 曲线下方的面积就是 AUC 面积了,我们发现在(0,0)位置和(1,0)位置,都是没有面积的,对应的这两个位置也非常糟糕。
- 那
y=x
直线呢?也就是我们图中(0,0)到(1,1)之间的虚线,改直线上的点 FPR=TPR ,即预测出的正样本中,总是有一半是对的,另一半是错的,就像抛硬币一样,是一个随机的结果。 -
y=x
直线对应的 AUC 面积为 0.5,对于一般模型而言,预测结果大部分情况下还是好过于随机蒙的。因此,通常情况下 AUC > 0.5,ROC 曲线分布于y=x
直线的上方。
最后,小鱼要强调的是:ROC 曲线中的每一个 (FPR,TPR) 点都对应着一个阈值 threshold ;对于一个分类器,每一个 threshold 下会有一个 (FPR,TPR)。
比如,threshold 最大时,表示判定为正样本的条件极度严苛,所有样本都被判定为了负样本,因此 FPR=TPR=0,对应于原点。Threshold 最小是,判定为正样本的条件又极度宽松,所有的样本都被判定为了正样本,因此 FPR=TPR=1,对应于 (1,1) 点。
可见,(0,0)点和(1,1)点是两个极端点,随着阈值增加,判定为正样本的条件越来越严苛,TP 和 FP 都越来越小,TPR 和 FPR 也越来越小,ROC 点像左下移动。
总结:有了 ROC 曲线和 AUC 面积,我们在训练模型时,就可以找到使得 AUC 面积最大的 ROC 曲线,这样才能找到距离 (0,1)点最近的位置,以及该位置对应的阈值。