《机器学习算法竞赛实战》学习笔记,记录一下自己的学习过程,详细的内容请大家购买作者的书籍查阅。
问题建模
当参赛者拿到竞赛题目时,首先应该考虑的事情就是问题建模
,同时完成基线(baseline)模型
的pipeline搭建,从而能够第一时间获得结果上的反馈,帮助后续工作的进行。
重点:竞赛的存在都依赖于真实的业务场景和复杂的数据,线上的提交结果次数往往有限。因此,合理地切分训练集和验证集,以及构建可信的线下验证就变得十分重要,这也是保障模型具有泛化性的基础。
竞赛中的问题建模主要可以分为赛题理解
、样本选择
、线下评估策略
三个部分。
1 赛题理解
从赛题背景引发的赛题任务出发,理解其中的业务逻辑以及可能对赛题有意义的外在数据有哪些,并对赛题数据有一个初步的认识。一般塞梯任务会给出赛题背景
、赛题数据
和评价指标
。
1.1 数据理解
可以将数据理解分为两个部分,分别是数据基础层
和数据描述层
,在问题建模阶段,我们只需要做基本的分析即可。
数据基础层
:重点关注每个数据字段的来源、生产过程、取数逻辑、计算逻辑等,方便后面选取。
数据描述层
:在处理好的数据基础层上进行统计分析和概括描述,尽可能通过一些简单的统计量(均值、最值、分布、增幅、趋势等)来概括整体数据的状况。对于时间序列问题,可以统计其增幅、趋势和周期;对于常规的数值特征,可以观察其均值、最值和方差等统计量。
1.2 评价指标(分类指标)
竞赛中常见的分类指标包括错误率
、精度
、准确率
(precision,也称查准率)、召回率
(recall,也称查全率)、F1-score
、ROC曲线
、AUC
和对数损失
(logloss)等。这些指标衡量的都是模型效果的好坏,且互相之间是有关系的,只是各自的侧重点不同。
1.2.1 精度与错误率
精度
是分类结果正确的样本数占样本总数的比例,错误率
是分类结果错误的样本数占样本总数的比例,错误率=1-精度
,不要将精度
与准确率
搞混。
1.2.2 准确率与召回率
以为分类为例给出混淆矩阵的定义来源,其中TP
、FP
、FN
、TN
属于不同的四种情况。
1 | 0 | |
---|---|---|
1 | TP | FP |
0 | FN | TN |
准确率
:是指被分类器判别为正类的所有样本中有多少真正的正类样本。
P
=
T
P
T
P
+
F
P
P=\frac{TP}{TP+FP}
P=TP+FPTP
召回率
:是指所有正类样本中有多少被分类器判为正类样本。
R
=
T
P
T
P
+
F
N
R=\frac{TP}{TP+FN}
R=TP+FNTP
准确率
和召回率
反映了分类器性能的两个方面,单依靠其中一个并不能较为全面地评价一个分类器的性能,准确率越高,召回率越低,反之召回率越高,准确率越低。为了平衡准确率和召回率地影响,较为全面的评价一个分类器,便有了F1-score
这一综合二者地指标。
1.2.3 F1-score
很多机器学习分类问题都希望准确率和召回率都高,所以可以考虑使用调和平均公式,以均衡这两个指标:
F
1
−
s
c
o
r
e
=
2
×
P
×
R
P
+
R
F1-score=2\times\frac{P\times R}{P+R}
F1−score=2×P+RP×R
F1分数的最大值是1,最小值是0。
y_train = [1, 1, 1, 0, 0, 0, 0, 1]
y_pred = [1, 1, 0, 1, 0, 1, 0, 1]
from sklearn.metrics import precision_score, recall_score, f1_score, accuracy_score
accuracy = accuracy_score(y_train, y_pred)
precision = precision_score(y_train, y_pred)
recall = recall_score(y_train, y_pred)
f1 = f1_score(y_train, y_pred)
print(f'accuracy:{accuracy*100}%')
print(f'precision:{precision*100}%')
print(f'recall:{recall*100}%')
print(f'f1:{f1*100}%')
accuracy:62.5%
precision:60.0%
recall:75.0%
f1:66.66666666666666%
1.2.4 ROC曲线和AUC
一种常用来度量分类中的非均衡性工具ROC曲线
(接受者操作特征曲线)。ROC曲线
用于绘制采用不同分类阈值时的TP率(真正率)和FP率(假正率)。降低分类阈值会导致更多样本被归为正类别,从而增加假正例和真正例的个数。
AUC
(Area Under the Curve)和ROC
(Receiver Operating Characteristic)曲线是一种评估二分类模型性能的常用方法。相比于其他评估指标(如准确度),AUC和ROC曲线对于样本不均衡的情况具有一定的优势。
AUC是ROC曲线下的面积,因为ROC曲线一般都处于y=x这条直线上方,所以取值范围在0.5到1之间,AUC作为一个数值,其值越大就代表分类器的效果越好。值得一提的是AUC的排序特性。相对准确率、召回率等指标,AUC指标本身和模型预测的概率绝对值无关,它只关注样本间的排序效果,因此特别适合做排序相关问题建模的评价指标。AUC是一个概率值,我们随机挑选一个正样本和一个负样本,由当前的分类算法根据计算出的分数将这个正样本排在负样本前面的概率就是AUC值。所以,AUC值越大,当前分类算法就越有可能将正样本排在负样本值前面,即能够更好的分类。在推荐系统中,我们可以将推荐算法看作是一个分类器,其中用户是否会接受推荐结果作为一个二元分类问题。在这种情况下,AUC指标可以用来衡量推荐算法的性能,即在不同的阈值下,推荐结果是否能够被用户接受。具体来说,对于推荐系统而言,AUC指标可以告诉我们,在推荐列表中,随机选择一个用户已经接受的推荐和一个用户没有接受的推荐,分类器能够将二者正确分类的概率大小。
from sklearn.metrics import roc_auc_score
roc_auc_score = roc_auc_score(y_train, y_pred)
print(f'roc_auc_score:{roc_auc_score*100}%')
roc_auc_score:62.5%
1.2.5 对数损失
对数损失用于评价分类器的概率输出。对数损失通过惩罚错误的分类来实现对分类器的准确度的量化。最小化对数损失基本等价于最大化分类器的准确度。为了计算对数损失,分类器必须提供概率结果,把输入样本喂入模型后,预测得到每个类别的概率值(0-1之间),而不只是预测最可能的类别。
l
o
g
l
o
s
s
=
−
1
N
∑
i
=
1
(
y
i
log
p
i
+
(
1
−
y
i
)
log
(
1
−
p
i
)
)
logloss=-\frac{1}{N}\sum_{i=1}^{}\left ( y_{i}\log_{}{p_{i}+\left ( 1-y_{i} \right )\log_{}\left({1-p_{i}}\right)} \right )
logloss=−N1i=1∑(yilogpi+(1−yi)log(1−pi))
对数损失主要评价模型预测的概率是否准确,它更关注和观察数据的吻合程度,而AUC评价则是模型把正样本排在前面的能力。两个评价指标的侧重点不一样。
在各种数据竞赛的分类问题中,AUC和对数损失基本上是最常见的模型评价指标,因为很多机器学习模型对分类问题的预测结果都是概率值,如果计算其他指标就需要先把概率转化成类别,这需要人为设置一个阈值,使用AUC或对数损失可以避免把预测概率转换成类别的麻烦。
from sklearn.metrics import log_loss
log_loss = log_loss(y_train, y_pred)
print(f'log_loss:{log_loss}')
log_loss:12.952241047449053
1.3 评价指标(回归指标)
1.3.1 平均绝对误差
如何去衡量一个回归模型的效果呢?大家自然而然会想到利用残差(真实值与预测值的差值)的均值:
r
e
s
i
d
u
a
l
(
y
,
y
′
)
=
1
n
∑
i
=
1
n
(
y
i
−
y
i
′
)
residual\left ( y,y^{'} \right )=\frac{1}{n}\sum_{i=1}^{n}\left ( y_{i} -y_{i}^{'} \right )
residual(y,y′)=n1i=1∑n(yi−yi′)
这样会存在一个问题,当真实值分布在拟合曲线两侧时,对于不同样本而言,残差有正负,直接相加会相互抵消,因此考虑真实值和预测值之间的距离来衡量模型效果,即平均绝对误差(MAE, Mean Absolute Error
),又被称做L1范数
损失:
M
A
E
(
y
,
y
′
)
=
1
n
∑
i
=
1
n
∣
y
i
−
y
i
′
∣
MAE\left ( y,y^{'} \right ) =\frac{1}{n}\sum_{i=1}^{n}\left | y_{i}-y_{i}^{'} \right |
MAE(y,y′)=n1i=1∑n
yi−yi′
平均绝对误差虽然解决了残差相加的正负抵消问题,能较好衡量回归模型的好坏,但是绝对值的存在导致函数不光滑,在某些点上不能求导,即平均绝对误差不是二阶连续可微的,同时二阶导数总为0。
1.3.2 均方误差
均方误差(MSE,Mean Squared Error),又被称为L2范数损失:
M
S
E
(
y
,
y
′
)
=
1
n
∑
i
=
1
n
(
y
i
−
y
i
i
)
2
MSE\left ( y,y_{'} \right )=\frac{1}{n}\sum_{i=1}^{n}\left (y_{i}-y_{i}^{i}\right )^{2}
MSE(y,y′)=n1i=1∑n(yi−yii)2
相对于使用平均绝对误差,使用均方误差的模型会赋予异常点更大的权重,也就是说均方误差对异常值敏感。
1.3.3 均方根误差
由于均方误差与数据标签的量纲不一致,因此为了保证量纲一致性,通常需要对均方误差进行开方,这就出现了均方根误差(RMSE)。
R
M
S
E
(
y
,
y
′
)
=
1
n
∑
i
=
1
n
(
y
i
−
y
i
′
)
RMSE\left ( y,y^{'} \right )=\sqrt{\frac{1}{n}\sum_{i=1}^{n}\left ( y_{i}-y_{i}^{'} \right ) }
RMSE(y,y′)=n1i=1∑n(yi−yi′)
1.3.4 平均绝对百分比误差
平均绝对百分比误差(MAPE)是通过计算每个预测值和真实值之差的绝对值所占真实值的比例来衡量相对误差的。因此,MAPE在计算误差时不受极端值的影响,因为它所计算的是相对误差而非绝对误差。MAPE在处理包含极端值的数据集时更加稳健,而RMSE在处理普通的误差分布时更加适用。因此,在选择MAPE或RMSE时,需要根据具体问题而定。需要注意的是,MAPE在计算时需要保证真实值不为0,因为当真实值为0时,MAPE的值将变得无穷大。
M
A
P
E
(
y
,
y
′
)
=
1
n
∑
i
=
1
n
∣
y
i
−
y
i
′
∣
y
i
′
MAPE\left ( y,y^{'} \right )=\frac{1}{n}\sum_{i=1}^{n} \frac{\left| y_{i}-y_{i}^{'} \right |}{y_{i}^{'}}
MAPE(y,y′)=n1i=1∑nyi′
yi−yi′
1.3.5 R2分数
R2分数是用于衡量回归模型拟合度的一种统计量。它表示模型所能解释的因变量方差的比例,数值范围从0到1,越接近1说明模型的拟合效果越好。具体计算方式是将模型预测值与实际值之间的平均差别与总平均值之间的平均差别比值,即(总平均值 - 模型预测值之间的平均差别)/ 总平均值。
R
2
(
y
,
y
′
)
=
1
−
∑
i
=
1
n
(
y
i
−
y
i
′
)
2
∑
i
=
1
n
(
y
i
−
y
ˉ
)
2
R^{2} \left ( y,y^{'} \right )=1-\frac{\sum_{i=1}^{n}\left (y_{i}-y_{i}^{'}\right ) ^{2} }{\sum_{i=1}^{n}\left (y_{i}-\bar{y} \right ) ^{2}}
R2(y,y′)=1−∑i=1n(yi−yˉ)2∑i=1n(yi−yi′)2
R2_score = 1,样本中预测值和真实值完全相等,没有任何误差,表示回归分析中自变量对因变量的解释越好。
R2_score = 0。此时分子等于分母,样本的每项预测值都等于均值。
R2_score不是r的平方,也可能为负数(分子>分母),模型等于盲猜,还不如直接计算目标变量的平均值。
1.3.6 对称平均绝对百分比误差
SMAPE是对称平均绝对百分比误差(Symmetric Mean Absolute Percentage Error)的缩写。它是一种表示预测误差的百分比的指标,可以衡量预测值和真实值的相对误差。SMAPE不仅考虑了预测值大于真实值的情况,还考虑了真实值大于预测值的情况。具体计算方式是将每个预测值与相应的真实值之间的百分比误差加权平均,即(预测值-真实值)/((预测值+真实值)/2)。
S
M
A
P
E
(
y
,
y
′
)
=
1
n
∑
i
=
1
n
∣
y
i
−
y
i
′
∣
(
∣
y
i
′
∣
+
∣
y
i
∣
)
/
2
SMAPE\left ( y,y^{'} \right )=\frac{1}{n}\sum_{i=1}^{n} \frac{\left| y_{i}-y_{i}^{'} \right |}{(\left |y_{i}^{'} \right|+\left |y_{i} \right|)/2}
SMAPE(y,y′)=n1i=1∑n(
yi′
+∣yi∣)/2
yi−yi′
import numpy as np
y_train = [1.5, 1.3, 1.1, 4.5, 1.67, 0.9, 0.2, 1]
y_pred = [1.4, 1.4, 0.9, 4.9, 1, 1, 0.3, 1]
from sklearn.metrics import mean_squared_error, mean_absolute_error, mean_absolute_percentage_error, r2_score
def smape(y_true, y_pred):
return 2.0 * np.mean(np.abs(y_pred - y_true) / (np.abs(y_pred) + np.abs(y_true)))
print(f'mean_squared_error:{mean_squared_error(y_train, y_pred)}')
print(f'rmse:{np.sqrt(np.array(mean_squared_error(y_train, y_pred)))}')
print(f'mean_absolute_error:{mean_absolute_error(y_train, y_pred)}')
print(f'mean_absolute_percentage_error:{mean_absolute_percentage_error(y_train, y_pred)}')
print(f'r2_score:{r2_score(y_train, y_pred)}')
print(f'smape:{smape(np.array(y_train), np.array(y_pred))}')
mean_squared_error:0.08611250000000004
rmse:0.29344931419241727
mean_absolute_error:0.20875000000000005
mean_absolute_percentage_error:0.17832569127479303
r2_score:0.9402270876106127
smape:0.17941022392061795
2 样本选择
在比赛中,主办方提供的数据也有可能存在令参赛者们十分头痛的质量问题,主要有四个问题:数据集过大严重影响了模型的性能,噪声和异常数据导致准确率不高,样本数据冗余或不相关数据没有给模型带来收益,以及正负样本不均衡导致数据存在倾斜。
2.1 主要原因
2.1.1 数据集过大
过大的数据集会严重影响各自特征工程和建模方式的快速验证。在大多数情况下,我们的计算资源有限,需要考虑数据采样处理,然后在小数据集上建模分析。此外,在特定业务场景下,可以过滤掉一些对建模无意义的数据。
2.1.2 数据噪声
数据的噪声主要来自于两种来源,一种是采集数据时操作不当导致信息表征出现错误,一种则是数据本身的特性存在合理范围内的抖动导致噪声与异常。数据噪声的存在会导致数据质量变低,影响模型的效果;但另一方面,我们也能通过在训练集中引入噪声数据使得模型的健壮性更强。若是噪声数据的来源为第一种,则需要对应地去看是否能够解码出正确数据,这种情况会极大的提升建模效果。因此,当需要处理噪声数据时,首先考虑是否为采集错误导致的,其次再去衡量模型的泛化性和模型的当前效果。
2.1.3 数据冗余
数据冗余与数据集过大是不同的概念。提到数据集会自然而然地想到是样本的集合,它的大小通常表示纵向的样本数量,而数据冗余侧重于描述数据特征的冗余。数据冗余不仅会影响模型性能更会引入噪声和异常,冗余的一个经典解决方案就是进行特征选择。
2.1.4 正负样本分布不均衡
在二分类正负样本不均衡的机器学习场景中,为了让模型可以更好地学习数据中的特征,让模型效果变得更佳,有时需要数据采样,这同时也避免了数据集较大而导致计算资源不足的麻烦。更本质上,数据采样就是模拟随机现象,根据给定的概率分布去模拟一个随机事件。
2.2 准确方法
在数据量非常大的情况下,为了降低成本,如何提高模型训练速度;针对正负样本分布不均衡的场景,如何通过数据采样解决这类问题。
首先针对第一种情况:
简单随机抽样
:有放回和无放回两种。
分层采样
:该方法分别针对每个类别进行采样,按规定的比例从不同类别中随机抽取样本的方法。
针对第二种情况:
评分加权处理
:遍历所有样本,看样本是否满足某个要求来给予其权重,例如在不均衡的二分类中,如果样本的标签为1,我们就将其权重设置为w1,样本标签为0,我们就将其权重设置为w2,然后将样本权重带入模型进行训练和测试。
欠采样
:从数量较多的一类样本中随机选取一部分并剔除,使得最终样本的目标类别不太失衡,常用的方法有随机欠采样
和Tomek Links
。
过采样
:主要是针对样本较少的类别进行重新组合,构造新样本,常用的方法有随机过采样
和SMOTE算法
。
2.3 应用场景
如果竞赛任务对于召回有特别大的需求
:也就是说每个正样本的预测都远远比对负样本的预测更加重要,这时候如果不做任何处理,就很难取得比较好的建模效果。
竞赛的评价指标是AUC
:这时候处理和不处理样本不均衡问题的差别就没有那么大。但这也好比一个参数的波动,将处理后的结果和不处理的结果进行融合后,评价指标一般都是会有细微提升的。
在竞赛中正样本和负样本是同等重要的
:预测正确一个正样本和预测正确一个负样本同等重要,那么其实不做其他处理也影响不大。
3 线下评估策略
在数据竞赛中,参赛者是不能将全部数据都用于训练模型的,因为这样会导致没有数据集对该模型的效果进行线下验证,从而无法评估模型的预测效果。为了解决这一问题,就需要考虑如何对数据进行切分,构建合适的线下验证集。
3.1 强时序性问题
对于含有时间序列因素的赛题,可将其看作强时序问题,即线上数据的时间都在离线数据集之后,这种情况下就可以使用采用时间上最接近测试集的数据做验证集,且验证集的时间分布在训练集之后。
3.2 弱时序问题
这类问题主要采用K折交叉验证
(K-Fold Cross Validation
),我们一般取K=5或K=10,例如将K取做5,把完整的训练数据分为5份,用其中的4份数据来训练数据,用剩余的1份来评价模型的质量。然后这个过程在5份数据上依次循环,并对得到的5个评价结果进行合并,比如求平均值
或投票
。
4 实战案例
import pandas as pd
from sklearn.model_selection import KFold
import lightgbm as lgb
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
all_data = pd.concat((train, test))
all_data = pd.get_dummies(all_data) # 实现 onehot encoding
all_data = all_data.fillna(all_data.mean()) # 填充缺失值
X_train, x_test = all_data[:train.shape[0]], all_data[train.shape[0]:] # 数据切分
y = train.SalePrice
params = {
'num_leaves': 63,
'min_child_samples': 50,
'objective': 'regression',
'learning_rate': 0.01,
'boosting_type': 'gbdt',
'metric': 'rmse',
'verbose': -1
}
folds = KFold(n_splits=5, shuffle=True, random_state=2023)
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)
# 每隔verbose_eval次迭代就输出一次信息
# early_stopping_rounds指定迭代多少次没有得到优化则停止训练
bst = lgb.train(params, dtrn, num_boost_round=1000, valid_sets=[dtrn, dval], early_stopping_rounds=100, verbose_eval=100)
…
笔记本-Github