第2章 问题建模
当参赛者拿到竞赛题目的时候,首先应该考虑的事情就是问题建模,同时完成基线(baseline)模型的管道(pipeline)搭建,从而能够第一时间获得结果上的反馈,帮助后续工作的进行。此外,竞赛的存在都依赖于真实的业务场景和复杂的数据,参赛者通常对此会有很多想法,但是线上提交结果验证的次数往往有限。因此,合理地切分训练集和验证集,以及构建可信的线下验证就变得十分重要,这也是保障模型具有泛化性的基础。
竞赛中的问题建模主要可以分为赛题理解、样本选择、线下评估策略三个部分,本章先从这三个部分介绍问题建模的对应工作,同时给出相应的使用技巧和应用代码。然后带领读者进行一个实战案例的演练,以帮助读者理解和应用本章内容。
2.1 赛题理解
赛题理解其实是从直观上梳理问题,分析问题可解的方法、赛题背景、赛题的主要痛点。厘清一道赛题要从赛题背景引发的赛题任务出发,理解其中的业务逻辑以及可能对赛题有意义的外在数据有哪些,并对于赛题数据有一个初步的了解,如知道现在和任务相关的数据有哪些,其中数据之间的关联逻辑是什么样的。通常,赛题任务会给出赛题背景、赛题数据和评价指标。赛题理解的这一部分工作会成为竞赛的重要组成部分和先决条件,通过对赛题的理解,对真实业务的分析,我们可以用自身的先验知识进行初步分析,很好地为接下来的部分做出铺垫。
2.1.1 业务背景
深入业务
竞赛本身是因特定场景而存在的,同时很多操作也会因场景的不同而大不一样。这里提到的场景指的就是业务,那么该如何分析业务呢?
比如,分析用户的购买行为,这里就需要知道用户购买的目的、所购买产品能够吸引用户的地方、公司能够提供的产品、产品与用户需求是否一致、目标用户定位、用户复购情况、用户的购买能力和支付方式。简而言之,就是把自己当作商家或者用户来换位思考和梳理这个过程。
接下来,进行一次更直观的业务理解,以此展示出实际竞赛中的分析过程,如图2.1所示。这是一个以互联网金融信贷业务为背景的赛题,目标是预测用户的还款金额与日期,也就是预测用户的还款情况。如果从商家的角度考虑,用户的还款意愿和还款能力将成为影响还款情况的关键因素。接下来,我们一起串一下业务线。首先,用户去借贷,商家就会考虑用户借贷的金额、用户是否有欺诈倾向和历史借贷情况;然后,用户借贷成功,商家要考虑用户当前的负债情况、距离还款时间以及工资日;最后,用户成功还款,商家要分析用户逾期情况、所剩欠款和当前期数等。这样就模拟出了基础的业务线。
图2.1 用户还款情况预测业务
上文介绍了如何进行业务理解,在接下来的内容中,我们将阐述如何将赛题目标与业务紧密联合起来,为竞赛成绩带来收益。
明确目标
真实业务涵盖的内容通常来说比竞赛涉及的更加广泛,因此赛题目标只是其中的一部分,真实业务还包括主办方提供的数据。在上面的例子中,赛题目标是预测用户还款金额与日期,那么参赛者可以先根据此目标来分析相关业务,即影响用户还款的因素等;然后再将业务中的信息反馈到赛题目标中,即工资日、总借款次数等。将赛题目标与真实业务紧密连接在一起的是数据,有了具体的数据,才能根据业务提取出特征来显性表示用户还款的情况,所以为了进一步深入理解赛题,还需要对数据有一个初步的认识。
2.1.2 数据理解
我们可以将数据理解分为两个部分,分别是数据基础层和数据描述层。当然,在问题建模阶段,并不需要对数据有特别深的理解,只需要做基本的分析即可。在后面的数据探察阶段,再深入理解数据,从数据中发现关键信息。
- 数据基础层。各种竞赛主办方提供的原始数据质量良莠不齐,数据形态如数据类型、存储格式等也是多种多样。为了进一步分析和建模,往往需要对原始数据进行清洗、加工和计算等处理。数据基础层重点关注的是每个数据字段的来源、生产过程、取数逻辑、计算逻辑等,了解了这些才能正确地理解、选取并使用每一个原始字段,从而加工计算得出所需的更多衍生字段,数据最终的呈现方式通常是数据表格。
- 数据描述层。数据描述层主要是在处理好的数据基础层上进行统计分析和概括描述,这个层面的重点在于尽可能地通过一些简单统计量(如均值、最值、分布、增幅、趋势等)来概括整体数据的状况,也使得参赛者能够清晰地知晓数据的基本情况。然而具体使用哪些统计量,并没有统一的标准,这要根据数据呈现的具体场景而定。比如,对于时间序列问题,可以统计其增幅、趋势和周期;对于常规的数值特征,则可以观察其均值、最值和方差等统计量;对于存在多类别的样本集合,则可以使用分布、分位点等进行描述。
基于以上这两个层面的数据探索,参赛者可以对数据有一个基本的认识,这些理解将会对之后进行的数据预处理、特征提取等起到关键性的作用。
2.1.3 评价指标
分类指标
分类问题不仅是竞赛中常出现的一种核心问题,也是实际应用中常见的一种机器学习问题。评价分类问题的效果要比评价回归问题的效果困难很多,这两类问题都包含各式各样的评价指标。本书将会撇开传统的介绍方式,结合实际应用出发,总结评价指标的特性和优缺点,帮助参赛者在竞赛中获得一定收益。
竞赛中常见的分类指标包括错误率、精度、准确率(precision,也称查准率)、召回率(recall,也称查全率)、F1-score、ROC 曲线、AUC 和对数损失(logloss)等。其实这些指标衡量的都是模型效果的好坏程度,且相互之间是有关系的,只是各自的侧重点不同。在我们理解了各指标的定义后,就能找出它们的区别与联系。下面将对上述指标进行简单的介绍,并给出一个例子来解释这些指标。
错误率与精度
在分类问题中,错误率是分类结果错误的样本数占样本总数的比例,精度则是分类结果正确的样本数占样本总数的比例。即,错误率 = 1–精度。
准确率与召回率
以最简单的二分类为例,图2.2 给出了混淆矩阵的定义来源,其中 TP、FN、FP、TN 分别表示各自群体的样本数量。
图2.2 混淆矩阵
其中基本的逻辑是,对模型预测出的概率值给定一个阈值。若概率值超过阈值,则将样本预测为 1(Positive,正类),否则预测为 0(Negative,负类)。
- True Positive(TP):预测类别为 1,真实类别为 1,预测正确。
- False Positive(FP):预测类别为 1,真实类别为 0,预测错误。
- True Negative(TN):预测类别为 0,真实类别为 0,预测正确。
- False Negative(FN):预测类别为 0,真实类别为 1,预测错误。
准确率 是指被分类器判定为正类的样本中真正的正类样本所占的比重,即被分类器判为正类的所有样本中有多少是真正的正类样本,其公式定义见式 (2-1):
由此易知,如果只做一个单独的正样本预测,并且预测类别正确,则通过此公式可得到 100% 的准确率。但这没有什么意义,这会使得分类器忽略除正样本之外的数据,因此还需考虑另一个指标,即召回率 。
召回率是指被分类器正确判定的正类样本占总的正类样本的比重,即所有正类样本中有多少被分类器判为正类样本,定义如式 (2-2):
准确率和召回率反映了分类器性能的两个方面,单依靠其中一个并不能较为全面地评价一个分类器的性能。一般来说,鱼与熊掌不可兼得,你的准确率越高,召回率越低;反之,召回率越高,准确率越低。继而为了平衡准确率和召回率的影响,较为全面地评价一个分类器,便有了 F1-score 这个综合了这两者的指标。
F1-score
很多机器学习分类问题都希望准确率和召回率能同时都高,所以可以考虑使用调和平均公式,以均衡这两个指标,从而避免在使用算术平均时,由于其中一个较高,另一个较低,出现均值虚高的现象。F1-score 就能起到这样一个作用,其定义如式 (2-3):
我们很容易看出,其最大值是1,最小值是0。构建一个计算准确率、召回率和 F1-score 的评价代码也很简单,具体实现代码如下:
from sklearn.metrics import precision_score, recall_scroe, f1_score precision = precision_score(y_train, y_pred) recall = recall_scroe(y_train, y_pred) f1 = f1_score(y_train, y_pred)
ROC 曲线
除了上述几种评价指标之外,还有一种常用于度量分类中的非均衡性的工具,即 ROC 曲线(接受者操作特征曲线)。ROC 曲线用于绘制采用不同分类阈值时的 TP 率与 FP 率。降低分类阈值会导致更多样本被归为正类别,从而增加假正例和真正例的个数。图2.3 是一个比较典型的 ROC 曲线。另外,ROC 曲线与 AUC 常被用来评价一个二值分类器的优劣,那么这里就有一个问题:既然已经有了这么多评价指标,那么为什么还要使用 ROC 曲线呢?
图2.3 不同分类阈值下的 TP 率与 FP 率
在实际的数据集中,经常会出现正负样本不均衡的现象,即负样本比正样本多很多(或者相反),而且测试集中正负样本的分布也可能随着时间发生变化。ROC 曲线有一个很好的特质,那就是在这种情况下,它依然能够保持不变。不过 ROC 曲线在竞赛中倒是不常见,反而 AUC 可以说是我们的老朋友,在分类问题中经常出现。
AUC
在互联网的搜索、推荐和广告的排序业务中,AUC 是一个极其常见的评价指标。它定义为 ROC 曲线下的面积,因为 ROC 曲线一般都处于 这条直线的上方,所以取值范围在 0.5和1 之间。之所以使用 AUC 作为评价指标,是因为 ROC 曲线在很多时候并不能清晰地说明哪个分类器的效果更好,而 AUC 作为一个数值,其值越大就代表分类器的效果越好。
值得一提的是AUC 的排序特性。相对于准确率、召回率等指标,AUC 指标本身和模型预测的概率绝对值无关,它只关注样本间的排序效果,因此特别适合用作排序相关问题建模的评价指标。AUC 是一个概率值,我们随机挑选一个正样本和一个负样本,由当前的分类算法根据计算出的分数将这个正样本排在负样本前面的概率就是AUC 值。所以,AUC 值越大,当前的分类算法就越有可能将正样本排在负样本值前面,即能够更好地分类。
深度思考
既然 AUC 与模型预测的分数值无关,那这为何是很好的特性?假设你采用的是准确率、F1-score 等指标,而模型预测的分数是个概率值,那么就必须选择一个阈值来决定把哪些样本预测为 1,哪些预测为 0。阈值的选择不同,准确率、召回率与 F1-score 的值就会不同,而 AUC 可以直接使用模型预测分数本身,参考的是相对顺序,更加好用。在竞赛任务中,参赛者更是省去了试探阈值的麻烦。
对数损失
该指标可用于评价分类器的概率输出。对数损失通过惩罚错误的分类来实现对分类器的准确度的量化。最小化对数损失基本等价于最大化分类器的准确度。为了计算对数损失,分类器必须提供概率结果,即把输入样本喂入模型后,预测得到每个类别的概率值(0和1 之间),而不只是预测最可能的类别。对数损失函数 1 的函数标准形式见式 (2-4):
对于样本点 来说, 是真实标签,在二分类问题中,其取值只可能为 0 或 1。假设某个样本点的真实标签为 ,该样本点取 的概率为 ,则该样本点的损失函数如式 (2-5):
我们不妨想想,AUC 同样是只需要给出模型预测的概率值,就能计算并衡量模型效果,那么对数损失与它的区别在哪里呢?
对数损失主要是评价模型预测的概率是否足够准确,它更关注和观察数据的吻合程度,而 AUC 评价的则是模型把正样本排到前面的能力。由于两个指标评价的侧重点不一样,因此参赛者考虑的问题不同,所选择的评价指标就会不同。对于广告 CTR 预估问题,如果考虑广告排序效果,就可以选择 AUC,这样也不会受到极端值的影响。此外,对数损失反映了平均偏差,更偏向于将样本数量多的那类划分准确。
深度思考
在各种数据竞赛的分类问题中,AUC 和对数损失基本是最常见的模型评价指标。通常来说,AUC 和对数损失比错误率、精度、准确率、召回率、F1-score 更常用,这是为什么呢?因为很多机器学习模型对分类问题的预测结果都是概率值,如果要计算上述这些指标,就需要先把概率转化成类别,这需要人为设置一个阈值,如果对一个样本的预测概率高于这个阈值,就把这个样本判到相应类别里面;如果低于这个阈值,则放进另一个类别里面。所以阈值的选取在很大程度上影响了分值的计算,不利于准确评价参赛者的模型效果,而使用 AUC 或者对数损失则可以避免把预测概率转换成类别的麻烦。
回归指标
平均绝对误差
首先考虑一个问题,如何去衡量一个回归模型的效果呢?大家自然而然地会想到采用残差(真实值与预测值的差值)的均值,即式 (2-6):
可是这里会存在一个问题,当真实值分布在拟合曲线两侧时,对于不同样本而言,残差有正有负,直接相加就会相互抵消,因此考虑采用真实值和预测值之间的距离来衡量模型效果,即平均绝对误差(MAE,Mean Absolute Error),又被称为 L1 范数损失,其定义如式 (2-7):
平均绝对误差虽然解决了残差加和的正负抵消问题,能较好衡量回归模型的好坏,但是绝对值的存在导致函数不光滑,在某些点上不能求导,即平均绝对误差不是二阶连续可微的,同时二阶导数总为 0。
扩展学习
在 XGBoost 里面,可以使用平均绝对误差作为损失函数进行模型训练,但是由于其存在局限性,大家通常会选择 Huber 损失进行替换。那为何要使用 Huber 损失呢?由于平均绝对误差并不是连续可导的(在 0 处不可导),所以需要使用可导目标函数来逼近平均绝对误差。而对于下述将会讲到的均方误差(MSE),梯度又会随着损失的减小而减小,使预测结果更加精确。在这种情况下,Huber 损失就非常有用,它会由于梯度的减小而落在最小值附近。比起均方误差,Huber 损失对异常点更具健壮性。因此,Huber 损失结合了平均绝对误差和均方误差的优点。但是,Huber 损失的问题可能需要我们不断地调整超参数
delta
。均方误差
和平均绝对误差对应的还有均方误差(MSE,Mean Squared Error),又被称为 L2 范数损失,其定义如式 (2-8):
由于均方误差与数据标签的量纲不一致,因此为了保证量纲一致性,通常需要对均方误差进行开方,这也就出现了均方根误差(RMSE)。
深度思考
那么平均绝对误差与均方误差有何区别呢?均方误差对误差(真实值–预测值)取了平方,因此若误差> 1,则均方误差会进一步增大误差。此时如果数据中存在异常点,那么误差值就会很大,而误差的平方则会远大于误差的绝对值。因此,相对于使用平均绝对误差计算损失,使用均方误差的模型会赋予异常点更大的权重。也就是说,均方误差对异常值敏感,平均绝对误差对异常值不敏感。
均方根误差
均方根误差用来评价回归模型的好坏,是对均方误差进行开方,缩小了误差的数值,其定义如式 (2-9):
上面介绍的几种衡量标准的取值大小都与具体的应用场景有关,因此很难定义统一的规则来衡量模型的好坏。同样,均方根误差也存在一定的局限性。例如,在计算广告的应用场景中,需要预测广告的流量情况时,某些离群点可能导致均方根误差指标变得很差,即使模型在 95%的数据样本中,都能预测得很好。如果我们不选择过滤掉离群点,就需要找一个更合适的指标来评价广告流量的预测效果。下面将介绍平均绝对百分百误差(MAPE),它是比均方根误差更加健壮的评价指标,相当于把每个点的误差进行了归一化,降低了个别离群点对绝对误差带来的影响。
平均绝对百分比误差
平均绝对百分比误差(MAPE)与平均绝对误差的二阶导数都是不存在的。但不同于平均绝对误差,平均绝对百分比误差除了考虑预测值与真实值的误差,还考虑了误差与真实值之间的比例,比如 2019 腾讯广告算法大赛,虽然预测值与真实值的差值是一样的,但是由于使用了平均绝对百分比误差来评测,因此其真实值越大,误差反而越小,平均绝对百分比误差的定义如式 (2-10):
1如无特殊说明,本书中的 log 函数表示以 e 为底的对数
2.2 样本选择
即使是在实际的竞赛当中,主办方提供的数据也有可能存在令参赛者们十分头疼的质量问题。这无疑会对最终预测结果造成很大的影响,因此需要考虑如何选择出合适的样本数据进行训练,那么如何才能够选择出合适的样本呢?在回答这个问题之前,先来看看影响结果的具体原因又是什么,这里总结出四个主要原因:分别是数据集过大严重影响了模型的性能,噪声和异常数据导致准确率不够高,样本数据冗余或不相关数据没有给模型带来收益,以及正负样本分布不均衡导致数据存在倾斜。
2.2.1 主要原因
数据集过大
机器学习算法相关竞赛由于涉及各行各业的应用场景,数据量也是多寡不一。类似搜索推荐以及广告等相关竞赛的数据量级达到千万级甚至亿级,过大的数据集会严重影响各种特征工程和建模方式的快速验证。在大多数情况下,我们的计算资源有限,需要考虑数据采样处理,然后在小数据集上建模分析。此外,在特定的业务场景下,也许可以过滤掉一些对建模没有意义的数据,这样可以帮助提高模型性能。
数据噪声
数据的噪声主要有两种来源,一种是采集数据时操作不当导致信息表征出现错误,另一种则是数据本身的特性存在合理范围内的抖动导致噪声与异常。数据噪声的存在具有两面性,一方面,噪声的存在会导致数据质量变低,影响模型的效果;但另一方面,我们也能通过在训练集中引入噪声数据的方法使得模型的健壮性更强。此外,若是噪声数据的来源为第一种,则需要对应地去看是否能够解码出正确数据,这种情况有时会极大地提升建模效果。因此,当需要处理噪声数据的时候,首先考虑是否为采集错误导致的,其次再去权衡模型的泛化性和模型的当前效果。有时候去噪反而会导致模型的泛化性能变差,在换了数据集之后,模型的效果无法得到很好的保证。
要去噪,首先要识别出噪声,然后采取直接过滤或者修改噪声数据等多种办法。噪声数据可能是特征值不对,比如特征值缺失、超出特征值域范围等;也可能是标注不对,比如二分类问题的正样本标注成了负样本。数据去噪很多是检测和去除训练数据中标注带噪声的实例。3.1 节将展示去除噪声或异常数据的具体处理办法。
数据冗余
通常来说,数据冗余与数据集过大是不同的概念。提到数据集,会自然而然地想到是样本的集合,它的大小通常表示纵向的样本数量,而数据冗余则侧重于描述数据特征的冗余。数据中存在的冗余不仅会影响到模型性能,更会引入噪声与异常,有可能为模型带来反效果。数据冗余的一个典型解决方案就是进行特征选择,这部分会在 4.4 节中着重进行讲解。
正负样本分布不均衡
在二分类正负样本不均衡的机器学习场景中,数据集往往是比较大的,为了让模型可以更好地学习数据中的特征,让模型效果更佳,有时需要进行数据采样,这同时也避免了因为数据集较大而导致计算资源不足的麻烦。这是比较浅层的理解,更本质上,数据采样就是模拟随机现象,根据给定的概率分布去模拟一个随机事件。另外,有一种说法是用少量的样本点去近似一个总体分布,并刻画总体分布中的不确定性。大多数竞赛提供的数据都是主办方从真实完整的数据中提取出来的一部分,并且会保证数据分布的一致性,降低竞赛难度,保证效率。更进一步,也可以从总体样本数据中抽取出一个子集(训练集)来近似总体分布,然后训练模型的目的就是最小化训练集上的损失函数,训练完成后,需要用另一个数据集(测试集)来评价模型。数据采样也有一些高级用法,比如对样本进行过采样或者欠采样,或者在目标信息保留不变的情况下,不断改变样本的分布来适应模型训练与学习,这常用于解决样本不均衡的问题。
2.2.2 准确方法
在竞赛中,若是在拿到数据后发现数据存在数据集过大以及正负样本不均衡这两种情况,则需要在一开始就针对性地给出解决方案。即如何处理以下两个问题:在数据量非常大的情况下,为了降低成本,如何提高模型训练速度;针对正负样本分布不均衡的场景,如何通过数据采样解决这类问题。
首先,针对第一个问题,主要推荐以下两种解决办法。
- 简单随机抽样。这里分为无放回和有放回两种,做法均比较简单,不做具体介绍。
- 分层采样。该方法分别对每个类别进行采样。这是从一个可以分成不同子集(或称为层、类别)的数据集中,按规定的比例从不同类别中随机抽取样本的方法。其优点是样本的代表性比较好,抽样误差比较小;缺点是抽样过程比简单随机抽样要繁杂些。
针对第二个问题,则主要有以下三种解决方法。
评分加权处理。分布不均衡的问题时有出现,包括欺诈交易识别和垃圾邮件识别等,其正负样本的数量分布差距极大。图2.4 给出了某个竞赛正负样本的分布情况,正样本的比例只有 2% 左右。考虑到正样本的重要性高于负样本,在模型训练以及评价的时候就可以设计相应的得分权重,使得模型能够学习到需要获得关注的部分。评分加权处理的办法是比较常见的一种。当然,在不同的应用场景中可以选择不同的加权方式,例如多分类问题中的 Micro Fscore 指标以及 KDD CUP 2019 竞赛中采用的 Weighted Fscore 指标,这两种评价指标对不同类别的权重是不一样的,通过对不同类别进行加权的方式可以提升模型的预测效果。
图2.4 目标变量分布
此方法具体的操作步骤是,首先遍历所有样本,根据样本是否满足某个要求来给予其权重。例如,在不均衡的二分类中,如果样本的标签为 1,我们就将其权重设置为 (自定义);如果样本标签为 0,就将其权重设置为 (自定义)。然后将样本权重代入模型进行训练和测试。
加权的直观含义从业务上理解就是认为一个正样本的价值大于多个负样本的,因此希望模型在训练的时候能更多地从正样本身上学到关键信息,当它学得不好的时候,就要对它加大惩罚力度。
欠采样。就是从数量较多的一类样本中随机选取一部分并剔除,使得最终样本的目标类别不太失衡。常用的方法有随机欠采样和 Tomek Links,其中 Tomek Links 先是找出两个各项指标都非常接近的相反类样本,然后删除这类样本中标签(label)占比高的,这类算法能够为分类器提供一个非常好的决策边界。
过采样。主要是对样本较少的类别进行重新组合,构造新样本。常用的方法有随机过采样和 SMOTE 算法。SMOTE 算法并不是简单复制已有的数据,而是在原有数据的基础上,通过算法产生新生数据。欠采样与过采样的示意图见图2.5。
图2.5 欠采样与过采样
2.2.3 应用场景
那么,在什么样的场景下需要处理样本的不均衡问题呢?下面给出了一些具体的场景,以帮助参赛者更好地应对这类问题。
- 如果竞赛任务对于召回有特别大的需求,也就是说对每个正样本的预测都远远比对负样本的预测更重要,那么这个时候如果不做任何处理,就很难取得比较好的建模结果。
- 如果竞赛的评价指标是AUC,那么参赛者在实战过程中就会发现,这个时候处理和不处理样本不均衡问题的差别没那么大。但这也好比一个参数的波动,将处理后的结果和不处理的结果进行融合后,评价指标一般都是有细微提升的。
- 如果在竞赛任务中正样本和负样本是同等重要的,即预测正确一个正样本和预测正确一个负样本是同等重要的,那么其实不做其他的处理也影响不大。
2.3 线下评估策略
通常在数据竞赛中,参赛者是不能将全部数据都用于训练模型的,因为这会导致没有数据集对该模型的效果进行线下验证,从而无法评估模型的预测效果。为了解决这一问题,就需要考虑如何对数据进行切分,构建合适的线下验证集。针对不同类型的问题,需要不同的线下验证方式,本书将这些问题大致分为强时序性和弱时序性两类,然后以此来确定线下验证方式。
2.3.1 强时序性问题
对于含有明显时间序列因素的赛题,可将其看作强时序性问题,即线上数据的时间都在离线数据集之后,这种情况下就可以采用时间上最接近测试集的数据做验证集,且验证集的时间分布在训练集之后。图2.6 为时间序列分割验证方式。
图2.6 时间序列分割验证
例如,天池平台上的“乘用车零售量预测”竞赛,初赛提供 2012年1月至 2017年10月盐城分车型销量配置数据,需要参赛者预测 2017年11月的盐城分车型销量数据。这是一个很明显的含时间序列因素的问题,那么我们可以选择数据集的最后一个月作为验证集,即 2017年10月的数据。
2.3.2 弱时序性问题
这类问题的验证方式主要为 折交叉验证(-fold Cross Validation),根据 的取值不同,会衍生出不同的交叉验证方式,具体如下。
(1) 当 时,这是最简单的 折交叉验证,即 2 折交叉验证。这个时候将数据集分成两份:D1和D2。首先,D1 当训练集,D2 当验证集;然后,D2 当训练集,D1 当验证集。2 折交叉验证存在很明显的弊端,即最终模型与参数的选取将在极大程度上依赖于事先对训练集和验证集的划分方法。对于不同的划分方式,其结果浮动是非常大的。
(2) 当 时,也就是 折交叉验证,被称作留一验证(Leave-one-out Cross Validation)。具体做法是只用一个数据作为测试集,其他数据都作为训练集,并重复 次( 为数据集的数据量)。这种方式的优点和缺点都是很明显的。其优点在于,首先它不受测试集和训练集划分方法的影响,因为每一个数据都单独做过测试集;其次,它用了 个数据训练模型,也几乎用到了所有的数据,从而保证模型的偏差更小。同时,其缺点也很明显,那就是计算量过大。如果数据集是千万级的,那么就需要训练千万次。
(3) 为了解决 (1)和(2) 的缺陷,我们一般取 或 10,作为一种折中处理,这也是最常用的线下验证方式。比如,,如图2.7所示,我们把完整的训练数据分为 5 份,用其中的 4 份数据来训练模型,用剩余的 1 份来评价模型的质量。然后将这个过程在 5 份数据上依次循环,并对得到的 5 个评价结果进行合并,比如求平均值或投票。下面给出通用的交叉验证代码,其中参数 NFOLDS
用来控制折数,具体代码如下:
from sklearn.model_selection import KFold
NFOLDS = 5
folds = KFold(n_splits= NFOLDS, shuffle=True, random_state=2021)
for trn_idx, val_idx in folds.split(X_train, y_train):
train_df, train_label = X_train.iloc[trn_idx, :] , y_train[trn_idx]
valid_df, valid_label = X_train.iloc[val_idx, :] , y_train[val_idx]
图2.7 五折交叉验证
2.4 实战案例
作为本章的总结,下面将引领读者对本章内容学以致用,进行一个 Kaggle 经典入门竞赛实战——房价预测,赛题主页如图2.8所示。本节包含赛题理解和线下验证。希望读者在理解本章内容、认真分析业务、完善线下评估工作后,能够快速搭建起一个 baseline(基线),并得到令自己满意的结果。
图2.8 赛题主页展示
2.4.1 赛题理解
首先,熟悉下赛题的业务背景,本赛题要求预测最终的房价,其数据集中有 79 个变量,几乎涵盖了爱荷华州艾姆斯(Ames, Iowa)住宅的方方面面,可以看到影响房价的因素有很多,具体如图2.9所示。
图2.9 房屋价格的影响因素
在上面影响房价的因素中,有如下几个关键的价值影响因素。
- 房屋位置。位置是高估值的关键,比如靠近大型商圈、重点学校或者接近市中心的位置,一般房价是比较高的。
- 形状尺寸。房子包含的空间、房间和土地越多,估价就越高。
- 内部组成。最新的公用设施和附加设施(如车库)是非常可取的价值影响因素。
这些初步的认识和业务分析对于第4章有很大的帮助。接下来,我们将导入数据,并观察数据的基本信息,以得到一个基本的认识,这些都将对之后的数据预处理、特征提取等起到关键性的作用。
首先,导入基本的模块:
import numpy as np
import pandas as pd
from sklearn.model_selection import KFold
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import OneHotEncoder
import lightgbm as lgb
接着,加载数据:
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
然后,看下数据的基本信息:
train.describe()
最后,对数据进行基本处理:
all_data = pd.concat((train,test))
all_data = pd.get_dummies(all_data)
# 填充缺失值
all_data = all_data.fillna(all_data.mean())
# 数据切分
X_train = all_data[:train.shape[0]]
X_test = all_data[train.shape[0]:]
y = train.SalePrice
本赛题使用均方误差作为评价指标,其计算方式如式 (2-11):
2.4.2 线下验证
验证的代码如下:
# K 折交叉验证
from sklearn.model_selection import KFold
folds = KFold(n_splits= 5, shuffle=True, random_state=2021)
# 模型参数
params = {'num_leaves': 63, 'min_child_samples': 50, 'objective': 'regression',
'learning_rate': 0.01, 'boosting_type': 'gbdt', 'metric': 'rmse'}
for trn_idx, val_idx in folds.split(X_train, y):
trn_df, trn_label = X_train.iloc[trn_idx, :], y[trn_idx]
val_df, val_label = X_train.iloc[val_idx, :], y[val_idx]
dtrn = lgb.Dataset(trn_df, label = trn_label)
dval = lgb.Dataset(val_df, label = val_label)
bst = lgb.train(params,dtrn, num_boost_round=1000,valid_sets=[dtrn, dval],
early_stopping_rounds=100, verbose_eval=100)
至此就完成了基本的问题建模,我们不仅对赛题有了初步的理解,同时还搭建出了 baseline,可以快速地反馈预测结果。根据初步的结果对模型进行不断的优化就是之后的重要工作。在接下来的章节中,我们将带着大家进行数据探索,发现数据特征,从中挖掘更多有用的信息。
2.5 思考练习
- 对于多分类问题,可否将其看作回归问题进行处理,对类别标签又有什么要求?
- 目前给出的都是已有的评价指标,那么这些评价指标(分类指标和回归指标)的损失函数如何实现?
- 解决样本分布不均衡问题时,尝试用代码实现样本加权、类别加权和采样算法等几种方式,并对比使用权重前后的分数变化。
- 在对不均衡的数据集进行采样时,是否会影响训练集和测试集之间的独立同分布关系?
- 在进行 折交叉验证的时候,对于 值的选取,是否越大越好呢?
- 在大多数情况下,我们会选择使用 折交叉验证,那么 折交叉验证为什么能够帮助提升效果呢?