《Python数据分析与机器学习实战-唐宇迪》读书笔记第6章--逻辑回归项目实战 ——信用卡欺诈检测
第6章--逻辑回归项目实战 ——信用卡欺诈检测
本章从实战的角度出发,以真实数据集为背景,一步步讲解如何使用Python工具包进行实际数据分析与建模工作。
6.1数据分析与预处理
假设有一份信用卡交易记录,遗憾的是数据经过了脱敏处理,只知道其特征,却不知道每一个字段代表什么含义,没关系,就当作是一个个数据特征。在数据中有两种类别,分别是正常交易数据和异常交易数据,字段中有明确的标识符。要做的任务就是建立逻辑回归模型,以对这两类数据进行分类。
在机器学习建模任务中,包括数据预处理、特征提取、模型调参等,每一步都会对最终的结果产生影响。
6.1.1数据读取与分析
先把任务所需的工具包导入进来
1 import pandas as pd 2 import matplotlib.pyplot as plt 3 import numpy as np 4 5 %matplotlib inline
信用卡交易记录数据是一个.csv文件,里面包含约28万条数据,规模很大,原始文件:
首先使用Pandas工具包读取数据:
1 data = pd.read_csv("creditcard.csv") 2 data.head()
如上,原始数据为个人交易记录,该数据集总共有31列,其中数据特征有30列,Time列暂时不考虑,Amount列表示贷款的金额,Class列表示分类结果,若Class为0代表该条交易记录正常,若Class为1代表交易异常。
拿到这样一份原始数据之后,直观感觉就是数据已经是处理好的特征,只需要对其进行建模任务即可。但是,上述输出结果只展示了前5条交易记录并且发现全部是正常交易数据,在实际生活中似乎正常交易也占绝大多数,异常交易仅占一少部分,那么,在整个数据集中,样本分布是否均衡呢?也就是说,在Class列中,正常数据和异常数据的比例是多少?绘制一份图表更能清晰说明:
1 count_classes = pd.value_counts(data['Class'], sort = True).sort_index() 2 count_classes.plot(kind = 'bar') 3 plt.title("Fraud class histogram") 4 plt.xlabel("Class") 5 plt.ylabel("Frequency")
上述代码首先计算出Class列中各个指标的个数,也就是0和1分别有多少个。为了更直观地显示,数据绘制成条形图,从上图中可以发现,似乎只有0没有1(仔细观察,其实是1的比例太少),说明数据中绝大多数是正常数据,异常数据极少。
这个问题看起来有点严峻,数据极度不平衡会对结果造成什么影响呢?模型会不会一边倒呢?认为所有数据都是正常的,完全不管那些异常的,因为异常数据微乎其微,这种情况出现的可能性很大。我们的任务目标就是找到异常数据,如果模型不重视异常数据,结果就没有意义了,所以,首先要做的就是改进不平衡数据。
在机器学习任务中,加载数据后,首先应当观察数据是否存在问题,先把问题处理掉,再考虑特征提取与建模任务。
6.1.2样本不均衡解决方案
那么,如何解决数据标签不平衡问题呢?首先,造成数据标签不平衡的最根本的原因就是它们的个数相差悬殊,如果能让它们的个数相差不大,或者比例接近,这个问题就解决了。基于此,提出以下两种解决方案。
- (1)下采样。既然异常数据比较少,那就让正常样本和异常样本一样少。例如正常样本有30W个,异常样本只有500个,若从正常样本中随机选出500个,它们的比例就均衡了。虽然下采样的方法看似很简单,但是也存在瑕疵,即使原始数据很丰富,下采样过后,只利用了其中一小部分,这样对结果会不会有影响呢?
- (2)过采样。不想放弃任何有价值的数据,只能让异常样本和正常样本一样多,怎么做到呢?异常样本若只有500个,此时可以对数据进行变换,假造出来一些异常数据,数据生成也是现阶段常见的一种套路。虽然数据生成解决了异常样本数量的问题,但是异常数据毕竟是造出来的,会不会存在问题呢?
这两种方案各有优缺点,到底哪种方案效果更好呢?需要进行实验比较。
在开始阶段,应当多提出各种解决和对比方案,尽可能先把全局规划制定完整,如果只是想一步做一步,会做大量重复性操作,降低效率。
6.1.3特征标准化
既然已经有了解决方案,是不是应当按照制订的计划准备开始建模任务呢?千万别心急,还差好多步呢,首先要对数据进行预处理,可能大家觉得机器学习的核心就是对数据建模,其实建模只是其中一部分,通常更多的时间和精力都用于数据处理中,例如数据清洗、特征提取等,这些并不是小的细节,而是十分重要的核心内容。目的都是使得最终的结果更好,该书作者经常说:“数据特征决定结果的上限,而模型的调优只决定如何接近这个上限。”
观察图6-2可以发现,Amount列的数值变化幅度很大,而V1~V28列的特征数据的数值都比较小,此时Amount列的数值相对来说比较大。这会产生什么影响呢?模型对数值是十分敏感的,它不像人类能够理解每一个指标的物理含义,可能会认为数值大的数据相对更重要(此处仅是假设)。但是在数据中,并没有强调Amount列更重要,而是应当同等对待它们,因此需要改善一下。
特征标准化就是希望数据经过处理后得到的每一个特征的数值都在较小范围内浮动,公式如下:
其中,Z为标准化后的数据;X为原始数据;Xmean为原始数据的均值;std(X)为原始数据的标准差。
如果把式(6.1)的过程进行分解,就会更加清晰明了。首先将数据的各个维度减去其各自的均值,这样数据就是以原点为中心对称。其中数值浮动较大的数据,其标准差也必然更大;数值浮动较小的数据,其标准差也会比较小。再将结果除以各自的标准差,就相当于让大的数据压缩到较小的空间中,让小的数据能够伸张一些,对于图6-3所示的二维数据,就得到其标准化之后的结果,以原点为中心,各个维度的取值范围基本一致。
图6-3 数据标准化过程
接下来,很多数据处理和机器学习建模任务都会用到sklearn工具包,这里先做简单介绍,该工具包提供了几乎所有常用的机器学习算法,仅需一两行代码,即可完成建模工作,计算也比较高效。不仅如此,还提供了非常丰富的数据预处理与特征提取模块,方便大家快速上手处理数据特征。它是Python中非常实用的机器学习建模工具包,在后续的实战任务中,都会出现它的身影。
Sklearn工具包提供了在机器学习中最核心的三大模块(Classification、Regression、Clustering)的实现方法供大家调用,还包括数据降维(Dimensionality reduction)、模型选择(Model selection)、数据预处理(Preprocessing)等模块,功能十分丰富,如图6-4所示。在初学阶段,大家还可以参考Examples模块,基本上所有算法和函数都配套相应的实例程序供大家参考。
API: https://scikit-learn.org/stable/modules/classes.html
Sklearn工具包还提供了很多实际应用的例子,并且配套相应的代码与可视化展示方法。
示例:https://scikit-learn.org/stable/auto_examples/index.html
使用sklearn工具包来完成特征标准化操作,代码如下:
1 from sklearn.preprocessing import StandardScaler 2 3 data['normAmount'] = StandardScaler().fit_transform(data['Amount'].values.reshape(-1, 1)) 4 data = data.drop(['Time','Amount'],axis=1) 5 data.head()
上述代码使用 StandardScaler方法对数据进行标准化处理,调用时需先导入该模块,然后进行fit_transform操作,相当于执行公式(6.1)。reshape(−1,1)的含义是将传入数据转换成一列的形式(需按照函数输入要求做)。最后用drop操作去掉无用特征。上述输出结果中的normAmount列就是标准化处理后的结果,可见数值都在较小范围内浮动。
数据预处理过程非常重要,绝大多数任务都需要对特征数据进行标准化操作(或者其他预处理方法,如归一化等)。
6.2下采样方案
下采样方案的实现过程比较简单,只需要对正常样本进行采样,得到与异常样本一样多的个数即可,代码如下:
1 X = data.iloc[:, data.columns != 'Class'] 2 y = data.iloc[:, data.columns == 'Class'] 3 4 # 得到所有异常样本的索引 5 number_records_fraud = len(data[data.Class == 1]) 6 fraud_indices = np.array(data[data.Class == 1].index) 7 8 # 得到所有正常样本的索引 9 normal_indices = data[data.Class == 0].index 10 11 # 在正常样本中随机采样出指定个数的样本,并取其索引 12 random_normal_indices = np.random.choice(normal_indices, number_records_fraud, replace = False) 13 random_normal_indices = np.array(random_normal_indices) 14 15 # 有了正常和异常样本后把它们的索引都拿到手 16 under_sample_indices = np.concatenate([fraud_indices,random_normal_indices]) 17 18 # 根据索引得到下采样所有样本点 19 under_sample_data = data.iloc[under_sample_indices,:] 20 21 X_undersample = under_sample_data.iloc[:, under_sample_data.columns != 'Class'] 22 y_undersample = under_sample_data.iloc[:, under_sample_data.columns == 'Class'] 23 24 # 下采样 样本比例 25 print("正常样本所占整体比例: ", len(under_sample_data[under_sample_data.Class == 0])/len(under_sample_data)) 26 print("异常样本所占整体比例: ", len(under_sample_data[under_sample_data.Class == 1])/len(under_sample_data)) 27 print("下采样策略总体样本数量: ", len(under_sample_data))
整体流程比较简单,首先计算异常样本的个数并取其索引,接下来在正常样本中随机选择指定个数样本,最后把所有样本索引拼接在一起即可。上述输出结果显示,执行下采样方案后,一共有984条数据,其中正常样本和异常样本各占50%,此时数据满足平衡标准。
正常样本所占整体比例: 0.5 异常样本所占整体比例: 0.5 下采样策略总体样本数量: 984
6.2.1交叉验证
得到输入数据后,接下来划分数据集,在机器学习中,使用训练集完成建模后,还需知道这个模型的效果,也就是需要一个测试集,以帮助完成模型测试工作。不仅如此,在整个模型训练过程中,也会涉及一些参数调整,所以,还需要验证集,帮助模型进行参数的调整与选择。
突然出现很多种集合,感觉很容易弄混,再来总结一下。
首先把数据分成两部分,左边是训练集,右边是测试集,如图6-8所示。训练集用于建立模型,例如以梯度下降来迭代优化,这里需要的数据就是由训练集提供的。测试集是当所有建模工作都完成后使用的,需要强调一点,测试集十分宝贵,在建模的过程中,不能加入任何与测试集有关的信息,否则就相当于透题,评估结果就不会准确。可以自己设定训练集和测试集的大小和比例,8︰2、9︰1都是常见的切分比例。
图6-8 训练集与测试集
接下来需要对数据集再进行处理,如图6-9所示,可以发现测试集没有任何变化,仅把训练集划分成很多份。这样做的目的在于,建模尝试过程中,需要调整各种可能影响结果的参数,因此需要知道每一种参数方案的效果,但是这里不能用测试集,因为建模任务还没有全部完成,所以验证集就是在建模过程中评估参数用的,那么单独在训练集中找出来一份做验证集(例如fold5)不就可以了吗,为什么要划分出来这么多小份呢?
图6-9 验证集划分
如果只是单独找出来一份,恰好这一份数据比较简单,那么最终的结果可能会偏高;如果选出来的这一份里面有一些错误点或者离群点,得到的结果可能就会偏低。无论哪种情况,评估结果都会出现一定偏差。
为了解决这个问题,可以把训练集切分成多份,例如将训练集分成10份,如图6-10所示。在验证某一次结果时,需要把整个过程分成10步,第一步用前9份当作训练集,最后一份当作验证集,得到一个结果,以此类推,每次都依次用另外一份当作验证集,其他部分当作训练集。这样经过10步之后,就得到10个结果,每个结果分别对应其中每一小份,组合在一起恰好包含原始训练集中所有数据,再对最终得到的10个结果进行平均,就得到最终模型评估的结果。这个过程就叫作交叉验证。
图6-10 交叉验证
交叉验证看起来有些复杂,但是能对模型进行更好的评估,使得结果更准确,从后续的实验中,大家会发现,用不同验证集评估的时候,结果差异很大,所以这个套路是必须要做的。在sklearn工具包中,已经实现好数据集切分的功能,这里需先将数据集划分成训练集和测试集,切分验证集的工作等到建模的时候再做也来得及,代码如下:
1 # from sklearn.cross_validation import train_test_split 2 #ModuleNotFoundError: No module named 'sklearn.cross_validation' 3 #改为model_selection,邀月注 4 from sklearn.model_selection import train_test_split 5 6 # 整个数据集进行划分 7 X_train, X_test, y_train, y_test = train_test_split(X,y,test_size = 0.3, random_state = 0) 8 9 print("原始训练集包含样本数量: ", len(X_train)) 10 print("原始测试集包含样本数量: ", len(X_test)) 11 print("原始样本总数: ", len(X_train)+len(X_test)) 12 13 # 下采样数据集进行划分 14 X_train_undersample, X_test_undersample, y_train_undersample, y_test_undersample = train_test_split(X_undersample 15 ,y_undersample 16 ,test_size = 0.3 17 ,random_state = 0) 18 print("") 19 print("下采样训练集包含样本数量: ", len(X_train_undersample)) 20 print("下采样测试集包含样本数量: ", len(X_test_undersample)) 21 print("下采样样本总数: ", len(X_train_undersample)+len(X_test_undersample))
通过输出结果可以发现,在切分数据集时做了两件事:首先对原始数据集进行划分,然后对下采样数据集进行划分。我们最初的目标不是要用下采样数据集建模吗,为什么又对原始数据进行切分操作呢?
原始训练集包含样本数量: 199364 原始测试集包含样本数量: 85443 原始样本总数: 284807 下采样训练集包含样本数量: 688 下采样测试集包含样本数量: 296 下采样样本总数: 984
6.2.2模型评估方法
接下来,没错,还没到实际建模任务,还需要考虑模型的评估方法,为什么建模之前要考虑整个过程呢?因为建模是一个过程,需要优先考虑如何评估其价值,而不是仅仅提供一堆模型参数值。
准确率是分类问题中最常使用的一个参数,用于说明在整体中做对了多少。下面举一个与这份数据集相似的例子:医院中有1000个病人,其中10个患癌,990个没有患癌,需要建立一个模型来区分他们。假设模型认为病人都没有患癌,只有10个人分类有错,因此得到的准确率高达990/1000,也就是0.99,看起来是十分不错的结果。但是建模的目的是找出患有癌症的病人,即使一个都没找到,准确率也很高。这说明对于不同的问题,需要指定特定的评估标准,因为不同的评估方法会产生非常大的差异。
选择合适的评估方法非常重要,因为评估方法是为整个实验提供决策的服务的,所以一定要基于实际任务与数据集进行选择。
在这个问题中,癌症患者与非癌症患者人数比例十分不均衡,那么,该如何建模呢?既然已经明确建模的目标是为了检测到癌症患者(异常样本),应当把关注点放在他们身上,可以考虑模型在异常样本中检测到多少个。对于上述问题来说,一个癌症病人都没检测到,意味着召回率(Recall)为0。这里提到了召回率,先通俗理解一下:就是观察给定目标,针对这个目标统计你取得了多大成绩,而不是针对整体而言。
如果直接给出计算公式,理解起来可能有点吃力,现在先来解释一下在机器学习以及数据科学领域中常用的名词,理解了这些名词,就很容易理解这些评估方法。
下面还是由一个问题来引入,假如某个班级有男生80人,女生20人,共计100人,目标是找出所有女生。现在某次实验挑选出50个人,其中20人是女生,另外还错误地把30个男生也当作女生挑选出来(这里把女生当作正例,男生当作负例)。
表6-1列出了TP、TN、FP、FN四个关键词的解释,这里告诉大家一个窍门,不需要死记硬背,从词表面的意思上也可以理解它们。
表6-1 TP、TN、FP、FN解释
- (1)TP。首先,第一个词是True,这就表明模型预测结果正确,再看Positive,指预测成正例,组合在一起就是首先模型预测正确,即将正例预测成正例。返回来看题目,选出来的50人中有20个是女生,那么TP值就是20,这20个女生被当作女生选出来。
- (2)FP。FP表明模型预测结果错误,并且被当作Positive(也就是正例)。在题目中,就是错把男生当作女生选出来。在这里目标是选女生,选出来的50人中有30个却是男的,因此FP等于30。
- (3)FN。同理,首先预测结果错误,并且被当作负例,也就是把女生错当作男生选出来,题中并没有这个现象,所以FN等于0。
- (4)TN。预测结果正确,但把负例当作负例,将男生当作男生选出来,题中有100人,选出认为是女生的50人,剩下的就是男生了,所以TN等于50。
- 上述评估分析中常见的4个指标只需要掌握其含义即可。下面来看看通过这4个指标能得出什么结论。
•准确率(Accuracy):表示在分类问题中,做对的占总体的百分比。
•召回率(Recall):表示在正例中有多少能预测到,覆盖面的大小。
•精确度(Precision):表示被分为正例中实际为正例的比例。
上面介绍了3种比较常见的评估指标,下面回到信用卡分类问题,想一想在这份检测任务中,应当使用哪一个评估指标呢?由于目的是查看有多少异常样本能被检测出来,所以应当使用召回率进行模型评估。
6.2.3正则化惩罚
本小节讨论的是正则化惩罚,这个名字看起来有点别扭,好好的模型为什么要惩罚呢?先来解释一下过拟合的含义。
建模的出发点就是尽可能多地满足样本数据,在图6-11中,图6-11(a)中直线看起来有点简单,没有满足大部分数据样本点,这种情况就是欠拟合,究其原因,可能由于模型本身过于简单所导致。再来看图6-11(b),比图6-11(a)所示模型稍微复杂些,可以满足大多数样本点,这是一个比较不错的模型。但是通过观察可以发现,还是没有抓住所有样本点,这只是一个大致轮廓,那么如果能把模型做得更复杂,岂不是更好?再来看图6-11(c),这是一个非常复杂的回归模型,竟然把所有样本点都抓到了,给人的第一感觉是模型十分强大,但是也会存在一个问题—模型是在训练集上得到的,测试集与训练集却不完全一样,一旦进行测试,效果可能不尽如人意。
图6-11 过拟合问题
在机器学习中,通常都是先用简单的模型进行尝试,如果达不到要求,再做复杂一点的,而不是先用最复杂的模型来做,虽然训练集的准确度可以达到99%甚至更高,但是实际应用的效果却很差,这就是过拟合。
我们在机器学习任务中经常会遇到过拟合现象,最常见的情况就是随着模型复杂程度的提升,训练集效果越来越好,但是测试集效果反而越来越差,如图6-12所示。
对于同一算法来说,模型的复杂程度由谁来控制呢?当然就是其中要求解的参数(例如梯度下降中优化的参数),如果在训练集上得到的参数值忽高忽低,就很可能导致过拟合,所以正则化惩罚就是为解决过拟合准备的,即惩罚数值较大的权重参数,让它们对结果的影响小一点。
图6-12 过拟合现象
还是举一个例子来看看其作用,假设有一条样本数据是x:[1,1,1,1],现在有两个模型:
•θ1:[1,0,0,0]
•θ2:[0.25,0.25,0.25,0.25]
可以发现,模型参数θ1、θ2与数据x组合之后的结果都为1(也就是对应位置相乘求和的结果)。这是不是意味着两个模型的效果相同呢?再观察发现,两个参数本身有着很大的差异,θ1只有第一个位置有值,相当于只注重数据中第一个特征,其他特征完全不考虑;而θ2会同等对待数据中的所有特征。虽然它们的结果相同,但是,如果让大家来选择,大概都会选择第二个,因为它比较均衡,没有那么绝对。
在实际建模中,也需要进行这样的筛选,选择泛化能力更强的也就是都趋于稳定的权重参数。那么如何把控参数呢?此时就需要一个惩罚项,以惩罚那些类似θ1模型的参数,惩罚项会与目标函数组合在一起,让模型在迭代过程中就开始重视这个问题,而不是建模完成后再来调整,常见的有L1和L2正则化惩罚项:
•L1正则化:
•L2正则化:
两种正则化惩罚方法都对权重参数进行了处理,既然加到目标函数中,目的就是不让个别权重太大,以致对局部产生较大影响,也就是过拟合的结果。在L1正则化中可以对|w|求累加和,但是只直接计算绝对值求累加和的话,例如上述例子中θ1和θ2的结果仍然相同,都等于1,并没有作出区分。这时候L2正则化就登场了,它的惩罚力度更大,对权重参数求平方和,目的就是让大的更大,相对惩罚也更多。θ1的L2惩罚为1,θ2的L2惩罚只有0.25,表明θ1带来的损失更大,在模型效果一致的前提下,当然选择整体效果更优的θ2组模型。
细心的读者可能还会发现,在惩罚项的前面还有一个α系数,它表示正则化惩罚的力度。以一种极端情况举例说明:如果α值比较大,意味着要非常严格地对待权重参数,此时正则化惩罚的结果会对整体目标函数产生较大影响。如果α值较小,意味着惩罚的力度较小,不会对结果产生太大影响。
最终结果的定论是由测试集决定的,训练集上的效果仅供参考,因为过拟合现象十分常见。
6.3逻辑回归模型
历尽千辛万苦,现在终于到建模的时候了,这里需要把上面考虑的所有内容都结合在一起,再用工具包建立一个基础模型就非常简单,难点在于怎样得到最优的结果,其中每一环节都会对结果产生不同的影响。
6.3.1参数对结果的影响
在逻辑回归算法中,涉及的参数比较少,这里仅对正则化惩罚力度进行调参实验,为了对比分析交叉验证的效果,对不同验证集分别进行建模与评估分析,代码如下:
1 #Recall = TP/(TP+FN) 2 from sklearn.linear_model import LogisticRegression 3 # from sklearn.cross_validation import KFold, cross_val_score 4 from sklearn.metrics import confusion_matrix,recall_score,classification_report 5 from sklearn.model_selection import cross_val_predict, KFold, cross_val_score 6 7 #print(help(KFold)) 8 9 def printing_Kfold_scores(x_train_data,y_train_data): 10 #fold = KFold(len(y_train_data),5,shuffle=False) 11 fold = KFold(5,shuffle=False) 12 13 # 定义不同力度的正则化惩罚力度 14 c_param_range = [0.01,0.1,1,10,100] 15 # 展示结果用的表格 16 results_table = pd.DataFrame(index = range(len(c_param_range),2), columns = ['C_parameter','Mean recall score']) 17 results_table['C_parameter'] = c_param_range 18 19 # k-fold 表示K折的交叉验证,这里会得到两个索引集合: 训练集 = indices[0], 验证集 = indices[1] 20 j = 0 21 #循环遍历不同的参数 22 for c_param in c_param_range: 23 print('-------------------------------------------') 24 print('正则化惩罚力度: ', c_param) 25 print('-------------------------------------------') 26 print('') 27 28 recall_accs = [] 29 30 #一步步分解来执行交叉验证 31 #for iteration, indices in enumerate(fold,start=1): 32 for iteration, indices in enumerate(fold.split(x_train_data)): 33 34 # 指定算法模型,并且给定参数 35 #lr = LogisticRegression(C = c_param, penalty = 'l1') 36 lr = LogisticRegression(C = c_param, penalty = 'l1',solver='liblinear') 37 38 # 训练模型,注意索引不要给错了,训练的时候一定传入的是训练集,所以X和Y的索引都是0 39 lr.fit(x_train_data.iloc[indices[0],:],y_train_data.iloc[indices[0],:].values.ravel()) 40 41 # 建立好模型后,预测模型结果,这里用的就是验证集,索引为1 42 y_pred_undersample = lr.predict(x_train_data.iloc[indices[1],:].values) 43 44 # 有了预测结果之后就可以来进行评估了,这里recall_score需要传入预测值和真实值。 45 recall_acc = recall_score(y_train_data.iloc[indices[1],:].values,y_pred_undersample) 46 # 一会还要算平均,所以把每一步的结果都先保存起来。 47 recall_accs.append(recall_acc) 48 print('Iteration ', iteration,': 召回率 = ', recall_acc) 49 50 51 # 当执行完所有的交叉验证后,计算平均结果 52 results_table.loc[j,'Mean recall score'] = np.mean(recall_accs) 53 j += 1 54 print('') 55 print('平均召回率 ', np.mean(recall_accs)) 56 print('') 57 58 #找到最好的参数,哪一个Recall高,自然就是最好的了。 59 best_c = results_table.loc[results_table['Mean recall score'].astype('float32').idxmax()]['C_parameter'] 60 61 # 打印最好的结果 62 print('*********************************************************************************') 63 print('效果最好的模型所选参数 = ', best_c) 64 print('*********************************************************************************') 65 66 return best_c 67 68 #交叉验证与不同参数结果 69 best_c = printing_Kfold_scores(X_train_undersample,y_train_undersample)
先来喘口气,迪哥的这段代码稍微有点年代了,所以sklearn的版本是个问题,邀月引用的是最新的0.22版本。
1 >>pip show Scikit-learn 2 Name: scikit-learn 3 Version: 0.22 4 Summary: A set of python modules for machine learning and data mining 5 Home-page: http://scikit-learn.org 6 Author: None 7 Author-email: None 8 License: new BSD 9 Location: d:\tools\python37\lib\site-packages 10 Requires: joblib, numpy, scipy 11 Required-by: sklearn, imbalanced-learn
所以报了4个错:
Tips:ModuleNotFoundError: No module named 'sklearn.cross_validation' 原因: 新版已弃用sklearn.cross_validation, # from sklearn.cross_validation import KFold, cross_val_score修改为 from sklearn.model_selection import KFold,cross_val_score Tips:TypeError: __init__() got multiple values for argument 'shuffle' 原因:sklearn版本升级,引用路径修改后,参数也有变化。 #fold = KFold(len(y_train_data),5,shuffle=False) 改为: fold = KFold(5,shuffle=False) Tips:'KFold' object is not iterable 原因:上述修改引发的联动问题。 #for iteration, indices in enumerate(fold,start=1): 改为: for iteration, indices in enumerate(fold.split(x_train_data)): Tips: ValueError: Solver lbfgs supports only 'l2' or 'none' penalties, got l1 penalty. 参看:https://blog.csdn.net/qq_22592457/article/details/103504796 原因: solver这个参数上,这个参数定义的是分类器,‘newton-cg’,‘sag’和‘lbfgs’等solvers仅支持‘L2’regularization,‘liblinear’ solver同时支持‘L1’、‘L2’regularization,
若dual=Ture,则仅支持L2 penalty。 决定惩罚项选择的有2个参数:dual和solver,如果要选L1范数,dual必须是False,solver必须是liblinear 把上面代码改成: lr = LogisticRegression(C = c_param, penalty = 'l1',solver='liblinear')
上述代码中,KFold用于选择交叉验证的折数,这里选择5折,即把训练集平均分成5份。C_param是正则化惩罚的力度,也就是正则化惩罚公式中的a。为了观察不同惩罚力度对结果的影响,在建模的时候,嵌套两层for循环,首先选择不同的惩罚力度参数,然后对于每一个参数都进行5折的交叉验证,最后得到其验证集的召回率结果。在sklearn工具包中,所有算法的建模调用方法都是类似的,首先选择需要的算法模型,然后.fit()传入实际数据进行迭代,最后用.predict()进行预测。
上述代码可以生成如下的输出。先来单独看正则化惩罚的力度C为0.01时,通过交叉验证分别得到5次实验结果,可以发现,即便在相同参数的情况下,交叉验证结果的差异还是很大,其值在0.93~1.0之间浮动,但是千万别小看这几个百分点,建模都是围绕着一步步小的提升逐步优化的,所以交叉验证非常有必要。
------------------------------------------- 正则化惩罚力度: 0.01 ------------------------------------------- Iteration 0 : 召回率 = 0.958904109589041 Iteration 1 : 召回率 = 0.9452054794520548 Iteration 2 : 召回率 = 1.0 Iteration 3 : 召回率 = 0.972972972972973 Iteration 4 : 召回率 = 0.9696969696969697 平均召回率 0.9693559063422077 ------------------------------------------- 正则化惩罚力度: 0.1 ------------------------------------------- Iteration 0 : 召回率 = 0.8493150684931506 Iteration 1 : 召回率 = 0.863013698630137 Iteration 2 : 召回率 = 0.9322033898305084 Iteration 3 : 召回率 = 0.9459459459459459 Iteration 4 : 召回率 = 0.8939393939393939 平均召回率 0.8968834993678272 ------------------------------------------- 正则化惩罚力度: 1 ------------------------------------------- Iteration 0 : 召回率 = 0.8493150684931506 Iteration 1 : 召回率 = 0.8904109589041096 Iteration 2 : 召回率 = 0.9491525423728814 Iteration 3 : 召回率 = 0.9459459459459459 Iteration 4 : 召回率 = 0.9090909090909091 平均召回率 0.9087830849613994 ------------------------------------------- 正则化惩罚力度: 10 ------------------------------------------- Iteration 0 : 召回率 = 0.863013698630137 Iteration 1 : 召回率 = 0.8904109589041096 Iteration 2 : 召回率 = 0.9661016949152542 Iteration 3 : 召回率 = 0.9594594594594594 Iteration 4 : 召回率 = 0.9090909090909091 平均召回率 0.9176153441999739 ------------------------------------------- 正则化惩罚力度: 100 ------------------------------------------- Iteration 0 : 召回率 = 0.863013698630137 Iteration 1 : 召回率 = 0.8904109589041096 Iteration 2 : 召回率 = 0.9661016949152542 Iteration 3 : 召回率 = 0.9459459459459459 Iteration 4 : 召回率 = 0.9090909090909091 平均召回率 0.9149126414972711 ********************************************************************************* 效果最好的模型所选参数 = 0.01 *********************************************************************************
下采样数据集逻辑回归评估分析
在sklearn工具包中,C参数的意义正好是倒过来的,例如C=0.01表示正则化力度比较大,而C=100则表示力度比较小。看起来有点像陷阱,但既然工具包这样定义了,也只好按照其要求做,所以一定要参考其API文档(见图6-14)。
Cfloat, default=1.0 Inverse of regularization strength; must be a positive float. Like in support vector machines, smaller values specify stronger regularization.
该页面同时也可看到刚才那个报错的原因:
solver{‘newton-cg’, ‘lbfgs’, ‘liblinear’, ‘sag’, ‘saga’}, default=’lbfgs’ Algorithm to use in the optimization problem. For small datasets, ‘liblinear’ is a good choice, whereas ‘sag’ and ‘saga’ are faster for large ones. For multiclass problems, only ‘newton-cg’, ‘sag’, ‘saga’ and ‘lbfgs’ handle multinomial loss;
‘liblinear’ is limited to one-versus-rest schemes. ‘newton-cg’, ‘lbfgs’, ‘sag’ and ‘saga’ handle L2 or no penalty ‘liblinear’ and ‘saga’ also handle L1 penalty ‘saga’ also supports ‘elasticnet’ penalty ‘liblinear’ does not support setting penalty='none' Note that ‘sag’ and ‘saga’ fast convergence is only guaranteed on features with approximately the same scale.
You can preprocess the data with a scaler from sklearn.preprocessing. New in version 0.17: Stochastic Average Gradient descent solver. New in version 0.19: SAGA solver. Changed in version 0.22: The default solver changed from ‘liblinear’ to ‘lbfgs’ in 0.22.
再来对比分析不同参数得到的结果,直接观察交叉验证最后的平均召回率值就可以,不同参数的情况下,得到的结果各不相同,差异还是存在的,所以在建模的时候调参必不可少,可能大家都觉得应该按照经验值去做,但更多的时候,经验值只能提供一个大致的方向,具体的探索还是通过大量的实验进行分析。
现在已经完成建模和基本的调参任务,只看这个90%左右的结果,感觉还不错,但是,如果想知道模型的具体表现,需要再深入分析。
6.3.2混淆矩阵
预测结果明确之后,还可以更直观地进行展示,这时候混淆矩阵就派上用场了(见图6-15)。
图6-15 混淆矩阵
混淆矩阵中用到的指标值前面已经解释过,既然已经训练好模型,就可以展示其结果,这里用到Matplotlib工具包,大家可以把下面的代码当成一个混淆矩阵模板,用的时候,只需传入自己的数据即可:
1 def plot_confusion_matrix(cm, classes, 2 title='Confusion matrix', 3 cmap=plt.cm.Blues): 4 """ 5 绘制混淆矩阵 6 """ 7 plt.imshow(cm, interpolation='nearest', cmap=cmap) 8 plt.title(title) 9 plt.colorbar() 10 tick_marks = np.arange(len(classes)) 11 plt.xticks(tick_marks, classes, rotation=0) 12 plt.yticks(tick_marks, classes) 13 14 thresh = cm.max() / 2. 15 for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])): 16 plt.text(j, i, cm[i, j], 17 horizontalalignment="center", 18 color="white" if cm[i, j] > thresh else "black") 19 20 plt.tight_layout() 21 plt.ylabel('True label') 22 plt.xlabel('Predicted label')
定义好混淆矩阵的画法之后,需要传入实际预测结果,调用之前的逻辑回归模型,得到测试结果,再把数据的真实标签值传进去即可:
1 import itertools 2 # lr = LogisticRegression(C = best_c, penalty = 'l1') 3 lr = LogisticRegression(C = best_c, penalty = 'l1', solver='liblinear') 4 lr.fit(X_train_undersample,y_train_undersample.values.ravel()) 5 y_pred_undersample = lr.predict(X_test_undersample.values) 6 7 # 计算所需值 8 cnf_matrix = confusion_matrix(y_test_undersample,y_pred_undersample) 9 np.set_printoptions(precision=2) 10 11 print("召回率: ", cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1])) 12 13 # 绘制 14 class_names = [0,1] 15 plt.figure() 16 plot_confusion_matrix(cnf_matrix 17 , classes=class_names 18 , title='Confusion matrix') 19 plt.show()
在这份数据集中,目标任务是二分类,所以只有0和1,主对角线上的值就是预测值和真实值一致的情况,深色区域代表模型预测正确(真实值和预测值一致),其余位置代表预测错误。数值10代表有10个样本数据本来是异常的,模型却将它预测成为正常,相当于“漏检”。数值30代表有30个样本数据本来是正常的,却把它当成异常的识别出来,相当于“误杀”。
最终得到的召回率值约为0.9387,看起来是一个还不错的指标,但是还有没有问题呢?用下采样的数据集进行建模,并且测试集也是下采样的测试集,在这份测试集中,异常样本和正常样本的比例基本均衡,因为已经对数据集进行过处理。但是实际的数据集并不是这样的,相当于在测试时用理想情况来代替真实情况,这样的检测效果可能会偏高,所以,值得注意的是,在测试的时候,需要使用原始数据的测试集,才能最具代表性,只需要改变传入的测试数据即可,代码如下:
1 # lr = LogisticRegression(C = best_c, penalty = 'l1') 2 lr = LogisticRegression(C = best_c, penalty = 'l1', solver='liblinear') 3 lr.fit(X_train_undersample,y_train_undersample.values.ravel()) 4 y_pred = lr.predict(X_test.values) 5 6 # 计算所需值 7 cnf_matrix = confusion_matrix(y_test,y_pred) 8 np.set_printoptions(precision=2) 9 10 print("召回率: ", cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1])) 11 12 # 绘制 13 class_names = [0,1] 14 plt.figure() 15 plot_confusion_matrix(cnf_matrix 16 , classes=class_names 17 , title='Confusion matrix') 18 plt.show()
还记得在切分数据集的时候,我们做了两手准备吗?不仅对下采样数据集进行切分,而且对原始数据集也进行了切分。这时候就派上用场了,得到的召回率值为0.9319,虽然有所下降,但是整体来说还是可以的。
在实际的测试中,不仅需要考虑评估方法,还要注重实际应用情况,再深入混淆矩阵中,看看还有哪些实际问题。上图中左下角的数值为10,看起来没有问题,说明有10个漏检的,只比之前的9个多1个而已。但是,右上角有一个数字格外显眼——3600,意味着有3600个样本被误杀。好像之前用下采样数据集进行测试的时候没有注意到这一点,因为只有30个样本被误杀。但是,在实际的测试集中却出现了这样的事:整个测试集一共只有100多个异常样本,模型却误杀掉3600个,有点夸张了,根据实际业务需求,后续肯定要对检测出来的异常样本做一些处理,比如冻结账号、电话询问等,如果误杀掉这么多样本,实际业务也会出现问题。
在测试中还需综合考虑,不仅要看模型具体的指标值(例如召回率、精度等),还需要从实际问题角度评估模型到底可不可取。
问题已经很严峻,模型现在出现了大问题,该如何改进呢?是对模型调整参数,不断优化算法呢?还是在数据层面做一些处理呢?一般情况下,建议大家先从数据下手,因为对数据做变换要比优化算法模型更容易,得到的效果也更突出。不要忘了之前提出的两种方案,而且过采样方案还没有尝试,会不会发生一些变化呢?下面就来揭晓答案。
6.3.3分类阈值对结果的影响
回想一下逻辑回归算法原理,通过Sigmoid函数将得分值转换成概率值,那么,怎么得到具体的分类结果呢?默认情况下,模型都是以0.5为界限来划分类别:
可以说0.5是一个经验值,但是并不是固定不变的,实践时可以根据自己的标准来指定该阈值大小。如果阈值设置得大一些,相当于要求变得严格,只有非常异常的样本,才能当作异常;如果阈值设置得比较小,相当于宁肯错杀也不肯放过,只要有一点异常就通通抓起来。
在sklearn工具包中既可以用.predict()函数得到分类结果,相当于以0.5为默认阈值,也可以用.predict_proba()函数得到其概率值,而不进行类别判断,代码如下:
1 # 用之前最好的参数来进行建模 2 # lr = LogisticRegression(C = 0.01, penalty = 'l1') 3 lr = LogisticRegression(C = 0.01, penalty = 'l1',solver='liblinear') 4 5 # 训练模型,还是用下采样的数据集 6 lr.fit(X_train_undersample,y_train_undersample.values.ravel()) 7 8 # 得到预测结果的概率值 9 y_pred_undersample_proba = lr.predict_proba(X_test_undersample.values) 10 11 #指定不同的阈值 12 thresholds = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9] 13 14 plt.figure(figsize=(10,10)) 15 16 j = 1 17 18 # 用混淆矩阵来进行展示 19 for i in thresholds: 20 y_test_predictions_high_recall = y_pred_undersample_proba[:,1] > i 21 22 plt.subplot(3,3,j) 23 j += 1 24 25 cnf_matrix = confusion_matrix(y_test_undersample,y_test_predictions_high_recall) 26 np.set_printoptions(precision=2) 27 28 print("给定阈值为:",i,"时测试集召回率: ", cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1])) 29 30 class_names = [0,1] 31 plot_confusion_matrix(cnf_matrix 32 , classes=class_names 33 , title='Threshold >= %s'%i)
给定阈值为: 0.1 时测试集召回率: 1.0 给定阈值为: 0.2 时测试集召回率: 1.0 给定阈值为: 0.3 时测试集召回率: 1.0 给定阈值为: 0.4 时测试集召回率: 0.9931972789115646 给定阈值为: 0.5 时测试集召回率: 0.9387755102040817 给定阈值为: 0.6 时测试集召回率: 0.8979591836734694 给定阈值为: 0.7 时测试集召回率: 0.8367346938775511 给定阈值为: 0.8 时测试集召回率: 0.7482993197278912 给定阈值为: 0.9 时测试集召回率: 0.5782312925170068
代码中设置0.1~0.9多个阈值,并且确保每一次建模都使用相同的参数,将得到的概率值与给定阈值进行比较来完成分类任务。
现在观察一下输出结果,当阈值比较小的时候,可以发现召回率指标非常高,第一个子图竟然把所有样本都当作异常的,但是误杀率也是很高的,实际意义并不大。随着阈值的增加,召回率逐渐下降,也就是漏检的逐步增多,而误杀的慢慢减少,这是正常现象。当阈值趋于中间范围时,看起来各有优缺点,当阈值等于0.5时,召回率偏高,但是误杀的样本个数有点多。当阈值等于0.6时,召回率有所下降,但是误杀样本数量明显减少。那么,究竟选择哪一个阈值比较合适呢?这就需要从实际业务的角度出发,看一看实际问题中,到底需要模型更符合哪一个标准。
6.4过采样方案
在下采样方案中,虽然得到较高的召回率,但是误杀的样本数量实在太多了,下面就来看看用过采样方案能否解决这个问题。
6.4.1SMOTE数据生成策略
如何才能让异常样本与正常样本一样多呢?这里需要对少数样本进行生成,这可不是复制粘贴,一模一样的样本是没有用的,需要采用一些策略,最常用的就是SMOTE算法(见图6-16),其流程如下。
第①步:对于少数类中每一个样本x,以欧式距离为标准,计算它到少数类样本集中所有样本的距离,经过排序,得到其近邻样本。
第②步:根据样本不平衡比例设置一个采样倍率N,对于每一个少数样本x,从其近邻开始依次选择N个样本。
第③步:对于每一个选出的近邻样本,分别与原样本按照如下的公式构建新的样本数据。
图6-16 SMOTE算法
总结一下:对于每一个异常样本,首先找到离其最近的同类样本,然后在它们之间的距离上,取0~1中的一个随机小数作为比例,再加到原始数据点上,就得到新的异常样本。对于SMOTE算法,可以使用imblearn工具包完成这个操作,首先需要安装该工具包,可以直接在命令行中使用pip install imblearn完成安装操作。再把SMOTE算法加载进来,只需要将特征数据和标签传进去,接下来就得到20W+个异常样本,完成过采样方案。
1 import pandas as pd 2 from imblearn.over_sampling import SMOTE 3 from sklearn.metrics import confusion_matrix 4 from sklearn.model_selection import train_test_split
6.4.2过采样应用效果
过采样方案的效果究竟怎样呢?同样使用逻辑回归算法来看看:
1 import pandas as pd 2 from imblearn.over_sampling import SMOTE 3 from sklearn.metrics import confusion_matrix 4 from sklearn.model_selection import train_test_split 5 6 credit_cards=pd.read_csv('creditcard.csv') 7 8 columns=credit_cards.columns 9 # 在特征中去除掉标签 10 features_columns=columns.delete(len(columns)-1) 11 12 features=credit_cards[features_columns] 13 labels=credit_cards['Class'] 14 15 16 features_train, features_test, labels_train, labels_test = train_test_split(features, 17 labels, 18 test_size=0.3, 19 random_state=0) 20 21 oversampler=SMOTE(random_state=0) 22 os_features,os_labels=oversampler.fit_sample(features_train,labels_train) 23 24 len(os_labels[os_labels==1]) 25 #199019 26 27 os_features = pd.DataFrame(os_features) 28 os_labels = pd.DataFrame(os_labels) 29 best_c = printing_Kfold_scores(os_features,os_labels)
------------------------------------------- 正则化惩罚力度: 0.01 ------------------------------------------- Iteration 0 : 召回率 = 0.9142857142857143 Iteration 1 : 召回率 = 0.88 Iteration 2 : 召回率 = 0.9716742539200809 Iteration 3 : 召回率 = 0.9622395015513711 Iteration 4 : 召回率 = 0.9619254588164358 平均召回率 0.9380249857147204 ------------------------------------------- 正则化惩罚力度: 0.1 ------------------------------------------- Iteration 0 : 召回率 = 0.9142857142857143 Iteration 1 : 召回率 = 0.88 Iteration 2 : 召回率 = 0.9728882144663632 Iteration 3 : 召回率 = 0.9640483877045989 Iteration 4 : 召回率 = 0.9640986345421885 平均召回率 0.939064190199773 ------------------------------------------- 正则化惩罚力度: 1 ------------------------------------------- Iteration 0 : 召回率 = 0.9142857142857143 Iteration 1 : 召回率 = 0.88 Iteration 2 : 召回率 = 0.9732928679817906 Iteration 3 : 召回率 = 0.9640735111233937 Iteration 4 : 召回率 = 0.9643121836019446 平均召回率 0.9391928553985686 ------------------------------------------- 正则化惩罚力度: 10 ------------------------------------------- Iteration 0 : 召回率 = 0.9142857142857143 Iteration 1 : 召回率 = 0.88 Iteration 2 : 召回率 = 0.9730905412240769 Iteration 3 : 召回率 = 0.9640986345421885 Iteration 4 : 召回率 = 0.9644126772771239 平均召回率 0.9391775134658207 ------------------------------------------- 正则化惩罚力度: 100 ------------------------------------------- Iteration 0 : 召回率 = 0.9142857142857143 Iteration 1 : 召回率 = 0.88 Iteration 2 : 召回率 = 0.9730905412240769 Iteration 3 : 召回率 = 0.9643624304395342 Iteration 4 : 召回率 = 0.9642996218925471 平均召回率 0.9392076615683745 ********************************************************************************* 效果最好的模型所选参数 = 100.0 *********************************************************************************
在训练集上的效果还不错,再来看看其测试结果的混淆矩阵:
1 # lr = LogisticRegression(C = best_c, penalty = 'l1') 2 lr = LogisticRegression(C = best_c, penalty = 'l1', solver='liblinear') 3 lr.fit(os_features,os_labels.values.ravel()) 4 y_pred = lr.predict(features_test.values) 5 6 # 计算混淆矩阵 7 cnf_matrix = confusion_matrix(labels_test,y_pred) 8 np.set_printoptions(precision=2) 9 10 print("召回率: ", cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1])) 11 12 # 绘制 13 class_names = [0,1] 14 plt.figure() 15 plot_confusion_matrix(cnf_matrix 16 , classes=class_names 17 , title='Confusion matrix') 18 plt.show()
得到的召回率值与之前的下采样方案相比有所下降,毕竟在异常样本中,很多都是假冒的,不能与真实数据相媲美。值得欣慰的是,这回模型的误杀比例大大下降,原来误杀比例占到所有测试样本的10%,现在只占不到1%,实际应用效果有很大提升。
经过对比可以明显发现,过采样的总体效果优于下采样(还得依据实际应用效果具体分析),因为可利用的数据信息更多,使得模型更符合实际的任务需求。但是,对于不同的任务与数据源来说,并没有一成不变的答案,任何结果都需要通过实验证明,所以,当大家遇到问题时,最好的解决方案是通过大量实验进行分析。
6.5用原始数据直接建模
1 best_c = printing_Kfold_scores(X_train,y_train)
------------------------------------------- 正则化惩罚力度: 0.01 ------------------------------------------- Iteration 0 : 召回率 = 0.4925373134328358 Iteration 1 : 召回率 = 0.6027397260273972 Iteration 2 : 召回率 = 0.6833333333333333 Iteration 3 : 召回率 = 0.5692307692307692 Iteration 4 : 召回率 = 0.45 平均召回率 0.5595682284048672 ------------------------------------------- 正则化惩罚力度: 0.1 ------------------------------------------- Iteration 0 : 召回率 = 0.5671641791044776 Iteration 1 : 召回率 = 0.6164383561643836 Iteration 2 : 召回率 = 0.6833333333333333 Iteration 3 : 召回率 = 0.5846153846153846 Iteration 4 : 召回率 = 0.525 平均召回率 0.5953102506435158 ------------------------------------------- 正则化惩罚力度: 1 ------------------------------------------- Iteration 0 : 召回率 = 0.5522388059701493 Iteration 1 : 召回率 = 0.6164383561643836 Iteration 2 : 召回率 = 0.7166666666666667 Iteration 3 : 召回率 = 0.6153846153846154 Iteration 4 : 召回率 = 0.5625 平均召回率 0.612645688837163 ------------------------------------------- 正则化惩罚力度: 10 ------------------------------------------- Iteration 0 : 召回率 = 0.5522388059701493 Iteration 1 : 召回率 = 0.6164383561643836 Iteration 2 : 召回率 = 0.7333333333333333 Iteration 3 : 召回率 = 0.6153846153846154 Iteration 4 : 召回率 = 0.575 平均召回率 0.6184790221704963 ------------------------------------------- 正则化惩罚力度: 100 ------------------------------------------- Iteration 0 : 召回率 = 0.5522388059701493 Iteration 1 : 召回率 = 0.6164383561643836 Iteration 2 : 召回率 = 0.7333333333333333 Iteration 3 : 召回率 = 0.6153846153846154 Iteration 4 : 召回率 = 0.575 平均召回率 0.6184790221704963 ********************************************************************************* 效果最好的模型所选参数 = 10.0 *********************************************************************************
#原始数据直接建模结果 # lr = LogisticRegression(C = best_c, penalty = 'l1') lr = LogisticRegression(C = best_c, penalty = 'l1', solver='liblinear') lr.fit(X_train,y_train.values.ravel()) y_pred_undersample = lr.predict(X_test.values) # Compute confusion matrix cnf_matrix = confusion_matrix(y_test,y_pred_undersample) np.set_printoptions(precision=2) print("Recall metric in the testing dataset: ", cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1])) # Plot non-normalized confusion matrix class_names = [0,1] plt.figure() plot_confusion_matrix(cnf_matrix , classes=class_names , title='Confusion matrix') plt.show()
项目过程小结:
- 1.在做任务之前,一定要检查数据,看看数据有什么问题。在此项目中,通过对数据进行观察,发现其中有样本不均衡的问题,针对这些问题,再来选择解决方案。
- 2.针对问题提出两种方法:下采样和过采样。通过两条路线进行对比实验,任何实际问题出现后,通常都是先得到一个基础模型,然后对各种方法进行对比,找到最合适的,所以在任务开始之前,一定要多动脑筋,做多手准备,得到的结果才有可选择的余地。
- 3.在建模之前,需要对数据进行各种预处理操作,例如数据标准化、缺失值填充等,这些都是必要的,由于数据本身已经给定特征,此处还没有涉及特征工程这个概念,后续实战中会逐步引入,其实数据预处理工作是整个任务中最重、最苦的一个工作阶段,数据处理得好坏对结果的影响最大。
- 4.先选好评估方法,再进行建模实验。建模的目的就是为了得到结果,但是不可能一次就得到最好的结果,肯定要尝试很多次,所以一定要有一个合适的评估方法,可以选择通用的,例如召回率、准确率等,也可以根据实际问题自己指定合适的评估指标。
- 5.选择合适的算法,本例中选择逻辑回归算法,详细分析其中的细节,之后还会讲解其他算法,并不一定非要用逻辑回归完成这个任务,其他算法效果可能会更好。但是有一点希望大家能够理解,就是在机器学习中,并不是越复杂的算法越实用,反而越简单的算法应用越广泛。逻辑回归就是其中一个典型的代表,简单实用,所以任何分类问题都可以把逻辑回归当作一个待比较的基础模型。
- 6.模型的调参也是很重要的,通过实验发现,不同的参数可能会对结果产生较大的影响,这一步也是必须的,后续实战中还会再来强调调参的细节。使用工具包时,建议先查阅其API文档,知道每一个参数的意义,再来进行实验。
- 7.得到的预测结果一定要和实际任务结合在一起,有时候虽然得到的评估指标还不错,但是在实际应用中却出现问题,所以测试环节也是必不可少的。
第6章完。
该书资源下载,请至异步社区:https://www.epubit.com