目录
5.1 线性模型
5.1.1 Lasso回归
Lasso回归,是对普通的线性回归采用L1正则化进行优化,通过乘法或限制估计值的绝对值之和,可以使某些系数为零,从而达到特征稀疏化和特征选择的效果。
当我们需要一些自动的特征、变量选择,或者处理高度相关的预测因素时,很方便。
代码实现:
from sklearn.linear_model import Ridge, Lasso
lasso_model = Lasso(alpha = 0.1, normalize = True)
只保留不相关的特征,其他为0,可能会导致信息损失
5.1.2 Ridge回归
L2正则化
代码实现:
from sklearn.linear_model import Ridge, Lasso
ridge_model = Ridge(alpha = 0.05, normalize = True)
不会减少特征数量,不利于特征缩减。
两者合并:Elastic Net Regression
5.2 树模型
本节将介绍竞赛中常见的树模型,这些模型简单易用,能够带来高收益。
可将树模型分为随机森林(Random Forest, RF)和梯度提升树(GBDT), 这两者最大的差异是前者并行、后者串行。在梯度提升树部分我们将介绍如今竞赛中大火的三种树模型: XGBoost、 LightGBM 和CatBoost。能够灵活运用这三种模型是竞赛中的必备技能。接下来将详细介绍各种树模型的数学形式、优缺点、使用细节和应用场景。
机器学习面试笔试知识点-决策树、随机森林、梯度提升决策树(GBDT)、XGBoost、LightGBM、CatBoost - 知乎
5.2.1 随机森林
随机森林的优缺点
- 随机森林优点非常明显:
- 不仅可以解决分类和回归问题,还可以同时处理类别特征和数值特征;
- 不容易过拟合,通过平均决策树的方式,降低过拟合的风险;
- 非常稳定,即使数据集中出现了一个新的数据点,整个算法也不会受到过多影响,新的数据点只会影响到一棵决策树,很难对所有决策树都产生影响。
- 很多缺点都是相对而言的,随机森林算法虽然比决策树算法更复杂,计算成本更高,但是
其拥有天然的并行特性,在分布式环境下可以很快地训练。梯度提升树需要不断地训练残差,进行所以结果准确度更高,但是随机森林更不容易过拟合,更加稳定,这也是因为其Bagging的特性。
- 代码实现
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier(max_ features=' auto', oob_ score=True, random state=1, n_ jobs=-1)
5.2.2 梯度提升树
梯度提升树(GBDT)是基于Boosting 改进而得的,在Boosting 算法中,一系列基学习器都需要串行生成,每次学习一棵树,学习目标是上棵树的残差。和AdaBoost 一样,梯度提升树也是基于梯度下降函数。梯度提升树算法已被证明是Boosting算法集合中最成熟的算法之一,它的特点是估计方差增加,对数据中的噪声更敏感(这两个问题都可以通过使用子采样来减弱),以及由于非并行操作而导致计算成本显著,因此要比随机森林慢很多。
梯度提升树作为XGBoost、LightGBM 和CatBoost的基础,这里将对其原理进行简单介绍。我们知道梯度提升树是关于Boosting的加法模型,由K个模型组合而成,其形式如式:
一般而言,损失函数描述的是预测值与真实值之间的关系,梯度提升树是基于残差为前一个模型)来不断拟合训练集的,这里使用平方损失函数。那么对于n个样本来说,则可以写成式:
进一步,
其中代表基模型的复杂度,若基模型是树模型,则树的深度、叶子节点数等指标均可以反映树的复杂度。
对于Boosting来说,它采用的是前向优化算法,即从前往后逐渐建立基模型来逼近目标函数,具体过程如:
5.2.3 XGBoost
主要特点
- 采用稀疏感知算法,XGBoost可以利用稀疏矩阵,节省内存(不需要密集矩阵)和节省计算时间(零值以特殊方式处理)。
- 近似树学习(加权分位数略图),这类学习方式能得到近似的结果,但比完整的分支切割探索要省很多时间。
- 在一台机器上进行并行计算(在搜索最佳分割阶段使用多线程),在多台机器上进行类似的分布式计算。
- 利用名为核外计算的优化方法,解决在磁盘读取数据时间过长的问题。将数据集分成多个块存放在磁盘中,使用一个独立的线程专门从磁盘读取数据并加载到内存中,这样一来,从磁盘读取数据和在内存中完成数据计算就能并行运行。
- XGBoost还可以有效地处理缺失值,训练时对缺失值自动学习切分方向。基本思路是在每次的切分中,让缺失值分别被切分到决策树的左节点和右节点,然后通过计算增益得分选择增益大的切分方向进行分裂,最后针对每个特征的缺失值,都会学习到一个最优的默认切分方向。
代码实现:
import xgboost as xgb
params = {'eta': 0.01, ' max depth': 11, 'objective': 'reg:linear', 'eval_ metric': 'rmse' }
dtrain = xgb.DMatrix(data=X_train, label=y_train)
dtest = xgb.DMatrix(data=X_valid, label=y_valid)
watchlist = [(train.data, 'train'), (valid_data, 'valid_ data')]
model=xgb. train(params, train_data,num_boost_round=20000,evals=watchlist,early_stopping_rounds=200,verbose_eval=500)
y_pred = model. predict(xgb .DMatrix(X_test), ntree_limit=model.best_ntree_limit)
5.2.4 LightGBM
LightGBM是微软的一个团队在Github上开发的一个开源项目,高性能的LightGBM算法具有分布式和可以快速处理大量数据的特点。LightGBM虽然基于决策树和XGBoost而生,但它还遵循其他不同的策略。
XGBoost使用决策树对一个变量进行拆分,并在该变量上探索不同的切割点(按级别划分的树生长策略),而LightGBM则专注于按叶子节点进行拆分,以便获得更好的拟合(这是按叶划分的树生长策略)。这使得LightGBM能够快速获得很好的数据拟合,并生成能够替代XGBoost的解决方案。从算法上讲,XGBoost将决策树所进行的分割结构作为一个图来计算,使用广度优先搜索(BFS),而LightGBM使用的是深度优先搜索(DFS)。
主要特点
- 比XGBoost准确性更高,训练时间更短。
- 支持并行树增强,即使在大型数据集上也能提供比 XGBoost更好的训练速度。
- 通过使用直方图算法将连续特征提取为离散特征,实现了惊人的快速训练速度和较低的内存使用率。
- 通过使用按叶分割而不是按级别分割来获得更高精度,加快目标函数收敛过程,并在非常复杂的树中捕获训练数据的底层模式。使用num_leaves和max_depth超参数控制过拟合。
代码实现
import lightgbm as lgb
params = {'num_leaves': 54, 'objective': 'regression', 'max_depth': 18,'learning_rate': 0.01, 'boosting': 'gbdt', 'metric': 'rmse', 'lambda_11': 0.1}
model = lgb.LGBMRegressor(**params, n_estimators = 20000, nthread = 4, n_jobs = -1)
model.fit(x_train, y_train, eval_set=[(X_train, y_train), (X_valid, y_valid)], eval_metric='rmse', verbose=1000, early_stopping_rounds=200)
y_pred= model.predict(X_test, num_iteration=model.best_iteration_)
5.2.5 CatBoost
CatBoost是由俄罗斯搜索引擎Yandex在2017年7月开源的一个GBM算法,它最强大的点是能够采用将独热编码和平均编码混合的策略来处理类别特征。
CatBoost用来对类别特征进行编码的方法并不是新方法,是均值编码,该方法已经成为一种特征工程方法,被广泛应用于各种数据科学竞赛中,如Kaggle。
均值编码,也称为似然编码、影响编码或目标编码,可将标签转换为基于它们的数字,并与目标变量相关联。如果是回归问题,则基于级别典型的平均目标值转换标签;如果是分类问题,则仅给定标签的目标分类概率(目标概率取决于每个类别值)。均值编码可能看起来只是一个简单而聪明的特征工程技巧,但实际上它也有副作用,主要是过拟合,因为会把目标信息带入预测中。
主要特点
- 支持类别特征,因此我们不需要预处理类别特征(例如通过label encoding或独热编码)。事实上,CatBoost文档中讲到不要在预处理期间使用独热编码,因为“这会影响训练速度和结果质量”。
- 提出了一种全新的梯度提升机制(Ordered Boosting),不仅可以减少过拟合的风险,也大大提高了准确性。
- 支持开箱即用的GPU训练(只需设置task_type="GPU")。
- 训练中使用了组合类别特征,利用了特征之间的联系,极大丰富了特征维度。
代码实现:
from catboost import CatBoostRegressor
params = {'learning_rate': 0.02, 'depth': 13,'bootstrap_type': 'Bernoulli', 'od_type': 'Iter', 'od_wait': 50, 'random_seed': 11}
model = CatBoostRegressor(iterations=20000, eval_metric='RMSE', **params)
model.fit(X_train, y_train, eval_set=(X_valid, y_valid), cat_features=[], use_best_model=True, verbose=False)
y_pred = model.predict(X_test)
每类树模型都其与众不同的地方,接下来将从决策树的生长策略、梯度偏差、类别特征处理和参数对比四个方面深入理解这些树模型,帮助参赛者更好地将它们应用到竞赛中。
更多功能
CatBoost目前还支持输入文本特征,因此不需要像以前那样先进行烦琐的操作获得标准化输入,再喂给模型。
文本特征跟类别特征的标记方式一样,只需在训练时把文本变量名的列表赋给text_features 即可。
那么CatBoost内部是怎么处理文本特征的呢?
操作其实非常常规,CatBoost内部将输入的文本特征转化为了数值特征,具体过程是分词、创建字典、将文本特征转化为多值的数值特征,接下来的处理方法可选择项就比较多了,比如完全展开成布尔型0/1特征,或者进行词频统计。
5.2.6 模型深入对比
XGBoost,LightGBM 和 CatBoost是三个非常核心的树模型,本节将对它们进行分析,因为三者之间有着千丝万缕的关系,只有厘清其中的关系,才能更好地运用这三个模型。
(1)决策树生长策略
- XGBoost使用的是Level-wise按层生长,可以同时分裂同一层的叶子,从而进行多线程优化,不容易过拟合,但很多叶子节点的分裂增益较低,会影响性能。
- LightGBM 使用的是Leaf-wise分裂方式,每次都从当前叶子中选择增益最大的结点进行分裂,循环迭代,但会生长出非常深的决策树,从而导致过拟合,这时可以调整参数max_depth来防止过拟合。
- CatBoost 使用的是oblivious-tree(对称树),这种方式使得节点是镜像生长的。相对于传统的生长策略,oblivious-tree能够简单拟合方案,快速生成模型,这种树结构起到了正则化的作用,因此并不容易过拟合。
(2)梯度偏差(Gradient bias)
- XGBoost 和LightGBM 中的提升树算法都是有偏梯度估计,在梯度估计中使用的数据与目前建立的模型所使用的数据是相同的,这样会导致数据发生泄漏,从而产生过拟合。
- CatBoost 改进了提升树算法,将原来的有偏梯度估计转换为了无偏梯度估计。具体做法是利用所有训练集(除第i条)建立模型Mi,然后使用第1条到第i-1条数据来建一个修正树M,累加到原来的模型Mi上。
(3)类别特征处理
- XGBoost并不能处理类别特征,因此需要我们根据数据实际情况进行独热编码、count编码和目标编码。
- LightGBM 直接支持类别特征,不需要独热展开。这里使用many-vs-many的切分方式来处理类别特征,并且可以把搜索最佳分割点的时间复杂度控制在线性级别,和原来one-vs-other方式的时间复杂度几乎一致。该算法先按照每个类别对应的标签均值(即avg(y)=Sum(y)/Count(y))进行排序,然后根据排序结果依次枚举最优分割点。和数值型特征的切分方式不同,它是将某一类别当作一类,然后将其余所有类别作为一类。
- CatBoost在处理类别特征方面做了更细致的操作。或许在使用LightGBM时,还需要对类别特征进行更多的编码方式,但对于CatBoost,则可以选择不进行多余的编码方式。
具体实现流程是首先对输入的样本集随机排序,然后针对类别特征中的某个取值,在将每个样本的该特征转换为数值型时,都基于排在该样本之前的类别标签取均值。对所有的类别特征值结果都进行如式(5-10)所示的运算,使之转化为数值结果,
(4)参数对比
如图5.3所示,从三个方面对树模型的参数进行对比,分别是用于控制过拟合、用于控制训练速度和调整类别特征的三类参数。这里只是枚举一些重要的参数,还有大量有用的参数就不一一进行介绍了。
参数作用\树模型 | XGBoost | LightGBM | CatBoost |
用于控制过拟合的参数 | 1.learning_rate/eta:减少每一步的权重(Shinkage方法)、一般在0.01和0.2之间 2.max_depth:树分裂的最大深度 3.min_child_weight:默认为1;最小叶子节点样本权重和 | 1.learning_rate:学习率 2.max_depth:默认为20;树的最大深度;另外 num_leaves=2^(max_depth),表示最大叶节点个数 3.min_data_in_leaf:默认为20,每个叶子节点对应最小数据量 | 1.learning_rate:学习率 3.没有与min_child_weight类似的参数 |
用于控制训练 速度的参数 | 1.colsample_bytree:随机列采样的比例 2.subsample:随机样本采样的比例 3.n_estimators:决策树的最大数量,越高的值有可能导致过拟合 | 1.feature_fraction:每次代随机列采样比例 2.bagging_fraction:每次迭代随机样本采样比例 3.num_iterations:默认为100,选代次数 | 1.rsm:随机子空间,每次分裂选择的特征比例 2.没有与样本采样类似的参数 3.iterations:树能够建立的最大数量 |
调整类别特征的参数 | 没有这类参数 | 1.categorical_feature:对应类别特征的位置索引 | 1.cat_features:对应类别特征的位置索引 2.one_hot_max_size:用来限制ont-hot特征向量的长度,默认为False |
5.3 神经网络
略
5.4 实战案例
本节仅需选择多个模型运行出结果即可,上文给出的模型并不完整,这里也将添加验证方式,让结果变得更加可靠。对于多个模型的结果可以对比着进行分析,以助模型融合部分的优化。
#接第5章实战案例代码,构造训练集和测试集
x_train = data[:ntrain][all_cols]
x_test = data[ntrain:][all_cols]
#对售价进行log处理
y_train = np.loglp(data[data.SalePrice.notnull()]['SalePrice'].values)
XGBoost
这里使用比较常规的五折交叉验证,
import xgboost as xgb
from sklearn.model_selection import KFold
kf = KFold(n_splits=5,shuffle=True,random_state=2020)
for i,(train_index,valid_index)in enumerate(kf.split(x_train,y_train)):
trn_x,trn_y,val_x,val_y = x_train.iloc[train_index],y_train[train_index],x_train.iloc[valid_index],y_train[valid_index]
params ={'eta':0.01,'max_depth':11,'objective':'reg:linear','eval_metric':'mae'}
dtrain = xgb.DMatrix(data=trn_x,label=trn_y)
dtest = xgb.DMatrix(data=val_x,label=val_y)
watchlist =[(dtrain,'train'),(dtest,'valid_data')]
model=xgb.train(params,dtrain,num_boost_round=20000,evals=watchlist,early_stopping_rounds=200,verbose_eval=500)
多层感知机
from sklearn. model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
x_train = x_train. fillna(0)
x_train = StandardScaler(). fit_transform(x_train)
trn_x, val_x, trny, val_y = train_test_split(
x_train, y_train, random_state=2020)
def create_mlp(shape):
x_input = Input((shape, ))
X = Dropout(0.2)(BatchNormalization()(
Dense(256, activation=' relu')(X_input)))
X = Dropout(0.2)(BatchNormalization()(Dense(128, activation=' relu')(X)))
X = Dropout(0.2)(BatchNormalization()(Dense(64, activation=' relu')(X)))
X = Dense(1)(X)
model = Model(inputs=X_input, outputs=X)
model. compile(optimizer=' adam', loss=' mse', metrics=[' mae'])
return model
mlp_model = create_mlp(trn_x. shape[1])
mlp_model. fit(x=trn_x, y=trn_y, validation_data=(
val_x, val_y), epochs=30, batch_size=16)
目前为止给出的模型都是比较容易实现的,这有助于快速反馈出结果。对比XGBoost(结果取对数后平均绝对误差:0.08x)和多层感知机(平均绝对误差:0.21x)的线下结果,发现后者的效果差了很多,2000多条的训练数据很难让多层感知机取得一个较好的结果。
5.5 思考练习
1.在Lasso回归和Ridge回归部分,我们知道L1和L2能够减少过拟合的风险,那这个参数究竟取多大合适?
2.树模型在分裂的时候其实可以看着特征的交叉组合阶段,那么还有必要构造交叉特征喂人树模型吗?
3.本章介绍了树模型的核心参数,还有很多没有介绍到,请尝试分析参数之间的关系,以及具体参数在算法中的哪个步骤中出现,加深对参数的理解。
4.常用的激活函数还是蛮多的,在进行深度学习相关模型训练时,不同的激活函数对于结果的影响还是蛮大的,尝试整理 sigmoid,tanh,ReLU,leaky ReLU,SELU 和 GELU 等激活函数的优缺点以及适用场景,