本文为 《机器学习算法竞赛实战》by 王贺、刘鹏、钱乾一书的学习手记。
仅以此书来入门Kaggle等机器学习竞赛,学习Kaggle竞赛题该如何做,具体问题该如何用机器学习的方法来解决。
要参加一次数据科学的竞赛,或者要用数据科学解决一个问题,通常有如下几步:
问题建模 --> 数据探索 --> 特征工程 --> 问题建模 --> 模型训练 --> 模型融合
问题建模,就是要审题并理解题目。机器学习的问题建模中,并不是所有数据都是特征加标签这种可以直接加入模型训练的形式,很多时候需要分析数据,进而抽象出建模目标和方案。
错误率 & 精度
准确率 & 召回率
F1-score 准确率和召回率的调和平均 F1-score = 2 * (P * R) / (P + R) 最大值是1,最小值是0
ROC曲线: 用于绘制不同分类阈值时的TP率和FP率。 降低分类阈值会导致更多样本被归为正类样本。
AUC :表示ROC曲线下的面积,因为ROC曲线通常处于y = x上方,所有AUC取值范围在0.5~1.0之间。很多时候ROC曲线并不能清晰说明那个分类器效果更好,而AUC作为一个数值,其值越大,代表分类器效果越好。
对数损失:对数损失通过惩罚错误的分类来实现对分类器准确度的量化,最小化对数损失等价于最大化分类器的准确度。 为了计算对数损失,分类器必须提供概率结果,即把输入样本喂入模型后,预测得到每个类别的概率,而不只是预测最可能的类别
平均绝对误差(MAE Mean Absoult Error) 也称为L1范数损失。平均绝对误差能够解决残差和的正负抵消问题,能较好的衡量回归模型的好坏,但是绝对值的存在导致函数不光滑,某些点上不能求导。
平均绝对误差 & 均方误差有何区别:
- 均方误差对误差取了平方,若误差大于1,则均方误差会进一步增大误差,因此相对于平均绝对误差计算损失,均方误差会赋予异常点更大的权重,即均方误差对异常值更加敏感。
- 平方绝对误差,因为平衡误差的正负,取了绝对值,导致函数不光滑,不是二阶连续可微函数。所以需要使用可导的目标函数来逼近平均绝对误差,而MES又会随着损失函数的减小而减小,随损失函数的增大而增大。因此大家在训练时,通常选择Huber损失进行替换,它会由于梯度的减小而落在最小值附近。因此Huber损失函数结合了平均绝对误差和均方误差的优点。
- 但是Huber损失函数需要不断调整超参数delta
样本数据的选择,会对预测结果造成很大的影响
比如常见的问题有:
如果拿到数据发现数据集过大以及样本不均衡该如何处理?
在数据量非常大的情况下,为了降低成本,如何提高模型训练速度:
针对正负样本不均衡的场景,如何通过数据采样解决这类问题:
通常对于竞赛的数据集不能都用于训练,这回导致没有数据对模型进行线下验证。通常会将数据集分成训练集和验证集。
House Prices - Advanced Regression Techniques
import numpy as np
import pandas as pd
from sklearn.model_selection import KFold
from sklearn.metrics import mean_squared_error #MSE
from sklearn.preprocessing import OneHotEncoder
# LightGBM(Light Gradient Boosting Machine)是一款基于决策树算法的分布式梯度提升框架
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
# KFold k折交叉验证
# n_splits k折交叉验证
# shuffle 是否每次生成数据集时进行洗牌
# random_state 仅当洗牌时有用,random_state数值相同时,生成的数据集一致
folds = KFold(n_splits=5, shuffle=True, random_state=2022)
# lightgbm 模型参数
params = {'num_leaves': 63,
'min_child_samples': 50,
'objective': 'regression',
'learning_rate': 0.01,
'boosting_type': 'gbdt',
'metric': 'rmse',
'verbose': -1,
}
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)
数据初探指前期的数据探索,主要是分析思路、分析方法和明确目标,加深对于数据的理解。要将数据探索的目的具象化。
# 统计每一列特征的唯一值和缺省值
stats = []
for col in train.columns:
stats.append((col, train[col].nunique(), train[col].isnull().sum() * 100 / train.shape[0], train[col].value_counts(normalize=True, dropna=False).values[0] * 100, train[col].dtype))
stats_df = pd.DataFrame(stats, columns=['Feature', 'Unique_values', 'Percentage of missing values', 'Percentage of values in the biggest category', 'type'])
stats_df.sort_values('Percentage of missing values', ascending=False)[:10]
missing = train.isnull().sum()
missing = missing[missing > 0]
missing.sort_values(inplace=True)
missing.plot.bar()
plt.show()
- 对于回归问题,标签值是最重要的变量,首先应该观察标签的分布情况
train['SalePrice'].describe() #查看标签数据基本统计分布
import seaborn as sns
plt.figure(figsize=(9, 8))
sns.distplot(train['SalePrice'], color='g', bins=100, hist_kws={'alpha': 0.4})
sns.distplot(np.log(train['SalePrice']), color='b', bins=100, hist_kws={'alpha': 0.4})
plt.show()
这里SalePrice为标签值,从图中可以看出呈偏离的正态分布,右倾斜类型,有些异常值在500 000 以上,需要最终去掉这些异常值。通过对数转换后,进行数据展示
- 单变量的特征变量的频率分布
df_num = train.select_dtypes(include = ['float64', 'int64'])
# df_num = df_num[df_num.columns.tolist()[1:5]]
df_num.hist(figsize=(16, 20), bins=50, xlabelsize=8, ylabelsize=8)
plt.show()
图中可以看出,OverllQual(总评)、GarageCars车库、TotalBsmtSF地下室面积、GrLivArea生活面积与房价呈正相关
import seaborn as sns
df_not_num = train.select_dtypes(include = ['O'])
fig, axes = plt.subplots(round(len(df_not_num.columns) / 3), 3, figsize=(12, 30))
for i, ax in enumerate(fig.axes):
if i < len(df_not_num.columns):
ax.set_xticklabels(ax.xaxis.get_majorticklabels(), rotation=45)
sns.countplot(x=df_not_num.columns[i], alpha=0.7, data=df_not_num, ax=ax)
fig.tight_layout()
plt.show()
# 不同房屋位置的评价分布条状图
plt.style.use('seaborn-white')
type_cluster = train.groupby(['Neighborhood','OverallQual']).size()
type_cluster.unstack().plot(kind='bar',stacked=True, colormap= 'PuBu', figsize=(13,11), grid=False)
plt.xlabel('OverallQual', fontsize=16)
# plt.show()
# 不同房屋位置,对应的房价箱型图
import seaborn as sns
var = 'Neighborhood'
data = pd.concat([train['SalePrice'], train[var]], axis=1)
f, ax = plt.subplots(figsize=(26, 12))
fig = sns.boxplot(x=var, y="SalePrice", data=data)
plt.show()
欠拟合曲线可能是一条平坦的线,或者相对较高的Loss,表明模型无法学习训练集
过拟合是指模型对训练集学习得很好,包括统计噪声或训练集中的随机波动。 导致模型对训练数据的专业化程度越高,对新数据的泛化能力就越差。
如果模型的容量超出了问题所需的容量,而灵活性又过多,则经常发生这种情况; 或者模型训练时间过长,也会发生这种情况。
特征工程通常可分为四个步骤:
因为加载数据,可能因为内存不足导致 memory error