第四次打卡(3月29日-4月1日)
菜鸡的学习之路^__^(自己学习,复习使用)
Task3:建模与调参
4.1 相关内容(教程给的目录)
- 线性回归模型:
线性回归对于特征的要求;
处理长尾分布;
理解线性回归模型; - 模型性能验证:
评价函数与目标函数;
交叉验证方法;
留一验证方法;
针对时间序列问题的验证;
绘制学习率曲线;
绘制验证曲线; - 嵌入式特征选择:
Lasso回归;
Ridge回归;
决策树; - 模型对比:
常用线性模型;
常用非线性模型; - 模型调参:
贪心调参方法;
网格调参方法;
贝叶斯调参方法;
此次学习的话我主要学习了几种模型,增强理解它们是什么个东西,其中包括线性回归模型、决策树模型、GBDT模型、XGBoost模型、LGMB模型。学习过程可真让人家头大啊。。。
4.2 模型学习
4.2.1 线性回归模型
线性:两个变量之间的关系是一次函数关系的——图象是直线,叫做线性。
非线性:两个变量之间的关系不是一次函数关系的——图象不是直线,叫做非线性。
回归:说白了就是人们在测量事物时,求得的都是测量值,而不是事物真实的值,为了能够得到真实值,无限次的进行测量,最后通过这些测量数据计算回归到真实值,这就是回归。
一般表达式是: , 是权重系数, 是偏置项,线性回归模型模型训练的训练过程就是利用梯度下降法找到最小值点,也就是最小误差,不断更新这两个参数,从而学到一个很好拟合数据的参数值的过程。
在用线性回归模型拟合数据之前,首先要求数据应符合或近似符合正态分布,否则得到的拟合函数不正确。
4.2.2 决策树模型
刚好最近在学习数据结构树这一部分,很好理解,线性回归模型是一股脑儿地把所有特征塞入学习,而决策树更像是编程语言中的if-else一样,去做条件判断,这就是两者根本性的区别。
决策树基于“树”结构进行决策的,这时我们就要面临两个问题 :
1. “树”怎么长?
2. 这颗“树”长到什么时候停?
弄懂了这两个问题,那么这个模型就已经建立起来了,决策树的总体流程是“分而治之”的思想,一是自根至叶的递归过程,一是在每个中间节点寻找一个“划分”属性,相当于就是一个特征属性了。
一. 这颗“树”长到什么时候停?
- 当前结点包含的样本全属于同一类别,无需划分;例如:样本都属于同一类别,就是不管特征如何改变都不会影响结果,这种就不需要划分了。
- 当前属性集为空,或是所有样本在所有属性上取值相同,无法划分;例如:所有的样本特征都是一样的,就造成无法划分了,训练集太单一。当前结点包含的样本集合为空,不能划分。
二. “树”怎么长?
在生活当中,我们都会碰到很多需要做出决策的地方,例如:吃饭地点、数码产品购买、旅游地区等,你会发现在这些选择当中都是依赖于大部分人做出的选择,也就是跟随大众的选择。其实在决策树当中也是一样的,当大部分的样本都是同一类的时候,那么就已经做出了决策。
我们可以把大众的选择抽象化,这就引入了一个概念就是纯度,想想也是如此,大众选择就意味着纯度越高。好,再深入一点,就涉及到一句话:信息熵越低,纯度越高。我相信大家或多或少都听说过“熵”这个概念,信息熵通俗来说就是用来度量包含的“信息量”,如果样本的属性都是一样的,就会让人觉得这包含的信息很单一,没有差异化,相反样本的属性都不一样,那么包含的信息量就很多了。
哈哈,一到这里就头疼了,因为马上要引入信息熵的公式,其实也很简单:
熵:
表示的是:当前样本集合
中第
类样本所占的比例为
。
信息增益:
特征
对训练数据集
的信息增益
,定义为集合
的经验熵
与特征
给定的条件下
的经验条件熵
之差
熵与条件熵的差称为互信息,决策树中的信息增益等价于训练数据集中的类与特征的互信息。
简单一句话就是:划分前的信息熵–划分后的信息熵。表示的是向纯度方向迈出的“步长”。
好了,有了这些知识,我们就可以开始“树”的生长了
直接粘贴李航老师《统计学习方法》上的算法:
算法1 信息增益
输入:训练数据集 和特征
输出:特征 对训练数据集 的信息增益
- 数据集 的经验熵
- 特征 对数据集 的经验条件熵
- 信息增益
算法2 ID3算法
输入:训练数据集 , 特征集 ,阈值
输出:决策树
- 如果 属于同一类 , 为单节点树,类 作为该节点的类标记,返回
- 如果 是空集,置 为单节点树,实例数最多的类作为该节点类标记,返回
- 计算 , 选择信息增益最大的特征
- 如果 的信息增益小于 , 为单节点树, 中实例数最大的类 作为类标记,返回
- 划分若干非空子集 ,
- 训练集, 为特征集,递归调用前面步骤,得到 ,返回
算法3 C4.5生成
输入:训练数据集 , 特征集 ,阈值
输出:决策树
- 如果 属于同一类 , 为单节点树,类 作为该节点的类标记,返回
- 如果 是空集, 置 为单节点树,实例数最多的作为该节点类标记,返回
- 计算 , 选择信息增益比最大的特征
- 如果 的信息增益比小于 , 为单节点树, 中实例数最大的类 作为类标记,返回
- 划分若干非空子集 ,
- 训练集, 为特征集,递归调用前面步骤,得到 ,返回
ID3和C4.5在生成上,差异只在准则的差异。
算法4 树的剪枝
决策树损失函数摘录如下:
树 的叶结点个数为 , 是树 的叶结点,该结点有 个样本点,其中 类的样本点有 个, 为叶结点 上的经验熵, 为参数,决策树学习的损失函数可以定义为
其中
这时有
其中 表示模型对训练数据的误差, 表示模型复杂度,参数 控制两者之间的影响。
4.4.3 GBDT模型
GBDT(Gradient Boosting Decision Tree),全名叫梯度提升决策树,使用的是Boosting的思想。
Boosting方法训练基分类器时采用串行的方式,各个基分类器之间有依赖。它的基本思路是将基分类器层层叠加,每一层在训练的时候,对前一层基分类器分错的样本,给予更高的权重。测试时,根据各层分类器的结果的加权得到最终结果。
Bagging与Boosting的串行训练方式不同,Bagging方法在训练过程中,各基分类器之间无强依赖,可以进行并行训练。
GBDT的原理很简单,就是所有弱分类器的结果相加等于预测值,然后下一个弱分类器去拟合误差函数对预测值的残差(这个残差就是预测值与真实值之间的误差)。当然了,它里面的弱分类器的表现形式就是各棵树。
举一个非常简单的例子,比如我今年20岁了,但计算机或者模型GBDT并不知道我今年多少岁,那GBDT咋办呢?
- 它会在第一个弱分类器(或第一棵树中)随便用一个年龄比如10岁来拟合,然后发现误差有10岁;
- 接下来在第二棵树中,用6岁去拟合剩下的损失,发现差距还有4岁;
- 接着在第三棵树中用3岁拟合剩下的差距,发现差距只有1岁了;
- 最后在第四课树中用1岁拟合剩下的残差,完美。
最终,四棵树的结论加起来,就是真实年龄20岁(实际工程中,gbdt是计算负梯度,用负梯度近似残差)。
4.2.4 XGBoost模型
XGBoost全称是eXtreme Gradient Boosting,可译为极限梯度提升算法。它由陈天奇所设计,致力于让提升树突破自身的计算极限,以实现运算快速,性能优秀的工程目标。和传统的梯度提升算法相比,XGBoost进行了许多改进,它能够比其他使用梯度提升的集成算法更加快速,并且已经被认为是在分类和回归上都拥有超高性能的先进评估器。除了比赛之中,高科技行业和数据咨询等行业也已经开始逐步使用XGBoost,了解这个算法,已经成为学习机器学习中必要的一环。
先来举个例子,我们要预测一家人对电子游戏的喜好程度,考虑到年轻和年老相比,年轻更可能喜欢电子游戏,以及男性和女性相比,男性更喜欢电子游戏,故先根据年龄大小区分小孩和大人,然后再通过性别区分开是男是女,逐一给各人在电子游戏喜好程度上打分,如下图所示:
这样,训练出了2棵树tree1和tree2,类似之前gbdt的原理,两棵树的结论累加起来便是最终的结论,所以小孩的预测分数就是两棵树中小孩所落到的结点的分数相加:2 + 0.9 = 2.9。爷爷的预测分数同理:-1 + (-0.9)= -1.9。具体如下图所示:
恩,你可能要拍案而起了,惊呼,这不是跟上文介绍的gbdt乃异曲同工么?事实上,如果不考虑工程实现、解决问题上的一些差异,xgboost与gbdt比较大的不同就是目标函数的定义。xgboost的目标函数如下图所示:
其中:
红色箭头所指向的L 即为损失函数;
红色方框所框起来的是正则项(包括L1正则、L2正则);
红色圆圈所圈起来的为常数项;
对于
,xgboost利用泰勒展开三项,做一个近似。
我们可以很清晰地看到,最终的目标函数只依赖于每个数据点在误差函数上的一阶导数和二阶导数。
除了算法上与传统的GBDT有一些不同外,XGBoost还在工程实现上做了大量的优化。总的来说,两者之间的区别和联系可以总结成以下几个方面。
- GBDT是机器学习算法,XGBoost是该算法的工程实现。
- 在使用CART作为基分类器时,XGBoost显式地加入了正则项来控制模 型的复杂度,有利于防止过拟合,从而提高模型的泛化能力。
- GBDT在模型训练时只使用了代价函数的一阶导数信息,XGBoost对代 价函数进行二阶泰勒展开,可以同时使用一阶和二阶导数。
- 传统的GBDT采用CART作为基分类器,XGBoost支持多种类型的基分类 器,比如线性分类器。
- 传统的GBDT在每轮迭代时使用全部的数据,XGBoost则采用了与随机 森林相似的策略,支持对数据进行采样。
- 传统的GBDT没有设计对缺失值进行处理,XGBoost能够自动学习出缺 失值的处理策略。
4.4.5 LightGBM模型
LightGBM是什么东东??
不久前微软DMTK(分布式机器学习工具包)团队在GitHub上开源了性能超越其他boosting工具的LightGBM,在三天之内GitHub上被star了1000次,fork了200次。知乎上有近千人关注“如何看待微软开源的LightGBM?”问题,被评价为“速度惊人”,“非常有启发”,“支持分布式”,“代码清晰易懂”,“占用内存小”等。
LightGBM (Light Gradient Boosting Machine)(请点击https://github.com/Microsoft/LightGBM)是一个实现GBDT算法的框架,支持高效率的并行训练。
LightGBM在Higgs数据集上LightGBM比XGBoost快将近10倍,内存占用率大约为XGBoost的1/6,并且准确率也有提升。GBDT在每一次迭代的时候,都需要遍历整个训练数据多次。如果把整个训练数据装进内存则会限制训练数据的大小;如果不装进内存,反复地读写训练数据又会消耗非常大的时间。尤其面对工业级海量的数据,普通的GBDT算法是不能满足其需求的。
LightGBM提出的主要原因就是为了解决GBDT在海量数据遇到的问题,让GBDT可以更好更快地用于工业实践。
LightGBM在哪些地方进行了优化 (区别XGBoost)?
- 基于Histogram的决策树算法
- 带深度限制的Leaf-wise的叶子生长策略
- 直方图做差加速直接
- 支持类别特征(Categorical Feature)
- Cache命中率优化
- 基于直方图的稀疏特征优化多线程优化。
4.3 代码
我这里用了LightGBM来实现二手车的预测:
效果还不错,嘿嘿!
import lightgbm as lgb
导入LightGBM,前面部分的代码就不粘贴了。
from sklearn.preprocessing import LabelEncoder
for f in tqdm(df_feature.select_dtypes('object')):
lbl = LabelEncoder()
df_feature[f] = lbl.fit_transform(df_feature[f].astype(str))
0%| | 1/199037 [00:00<1:55:47, 28.65it/s]
打标签
ycol = 'price'
feature_names = list(
filter(lambda x: x not in [ycol, 'SaleID', 'regDate', 'creatDate'],
df_train.columns))
model = lgb.LGBMRegressor(num_leaves=64,
max_depth=10,
learning_rate=0.1,
n_estimators=10000000,
subsample=0.8,
feature_fraction=0.8,
reg_alpha=0.5,
reg_lambda=0.5,
random_state=seed,
metric=None)
定义好模型,开始训练
oof = []
prediction = df_test[['SaleID']]
prediction['price'] = 0
df_importance_list = []
kfold = KFold(n_splits=5, shuffle=False, random_state=seed)
for fold_id, (trn_idx,
val_idx) in enumerate(kfold.split(df_train[feature_names])):
X_train = df_train.iloc[trn_idx][feature_names]
Y_train = df_train.iloc[trn_idx][ycol]
X_val = df_train.iloc[val_idx][feature_names]
Y_val = df_train.iloc[val_idx][ycol]
print('\nFold_{} Training ================================\n'.format(
fold_id + 1))
lgb_model = model.fit(X_train,
Y_train,
eval_names=['train', 'valid'],
eval_set=[(X_train, Y_train), (X_val, Y_val)],
verbose=500,
eval_metric='mae',
early_stopping_rounds=50)
pred_val = lgb_model.predict(X_val,
num_iteration=lgb_model.best_iteration_)
df_oof = df_train.iloc[val_idx][['SaleID', ycol]].copy()
df_oof['pred'] = pred_val
oof.append(df_oof)
pred_test = lgb_model.predict(df_test[feature_names],
num_iteration=lgb_model.best_iteration_)
prediction['price'] += pred_test / 5
df_importance = pd.DataFrame({
'column': feature_names,
'importance': lgb_model.feature_importances_,
})
df_importance_list.append(df_importance)
del lgb_model, pred_val, pred_test, X_train, Y_train, X_val, Y_val
gc.collect()
把各个特征对价格预测的重要性展示出来
# 各个特征重要性程度显示
df_importance = pd.concat(df_importance_list)
df_importance = df_importance.groupby(['column'])['importance'].agg(
'mean').sort_values(ascending=False).reset_index()
df_importance
4.4 LightGBM与XGBoost模型测试说明
这是我查看手机文档时,偶然看到的一份整理,忘记是从哪里获得的了,里面有一些关于这两个模型的一些常见问题及结论。
4.4.1 lightgbm模型测试说明
问题1:数据需不需要填充缺失值?字符型分类变量需不需要哑变量编码(直接指定categorical_feature)?
结论:
- 不需要填充缺失值。
- 对于字符型分类变量,必须进行哑变量编码。对于数值型分类变量,可以直接指定categorical_feature。
对于字符型分类变量,直接指定categorical_feaure时,报错:ValueError: DataFrame.dtypes for data must be int, float or bool。比如性别sex有3种字符取值:Male、Female和Other,指定categorical_feaure=sex时,lightgbm不会自动进行哑变量编码,导致代码报错。对于数值型分类变量,直接指定categorical_feaure时,可以正常运行,且不会报错。因为数值型分类变量的取值都是int型,所以不会报错。
问题2:数值型分类变量是否显式转换为类别型astype(‘category’)?
结论:
lightgbm数值型分类变量可以显式转换为category。是否转为category,对于模型结果没有明显影响。
问题3:数据不填充的话,能否对连续型变量进行标准化处理?
结论:
数据不填充的话,不能够直接对连续型变量进行标准化处理,报如下错误:ValueError: Input contains NaN, infinity or a value too large for dtype(‘float64’).
问题4:连续型变量使用-1填充后,标准化与否对建模结果的影响?
结论:
lightgbm在用-1填充连续型变量之后,标准化之后的结果变差了。
问题5:字符型类别变量使用missed填充,数值型类别变量使用-1填充,连续型变量使用-1填充后,标准化与否对建模结果的影响?
结论:
- 字符型类别变量的补缺、不补缺的结果是一样的(哑变量之后),可能的原因是模型自动会判断其哑变量对应的各个值是否全为0,如果全为0,即可判断该值是缺失的。与补缺之后,进行哑变量编码之后,全为0的哑变量分类,最后1位数字是1,所以与不补缺的结果是一样的。
- 在连续型变量使用-1填充的前提下,数值型分类变量用不用-1填充,对模型结果没有影响。
4.4.2 xgboost模型测试说明
问题1:连续型变量和类别型变量都不补缺,行不行?字符型分类变量需不需要哑变量编码?
结论:
- 连续型变量和类别型变量不补缺,可以正常计算。
- 字符型变量必须进行哑变量编码,否则报错:ValueError: DataFrame.dtypes for data must be int, float or bool.
问题2:在不补缺的前提下,数值型分类变量能不能显式地转为category?
结论:
数值型分类变量不能转为category,astype(‘category’),否则报错:ValueError: DataFrame.dtypes for data must be int, float or bool.
问题3:连续型变量不进行补缺,能不能直接进行标准化?
结论:
不补缺的话,不能够进行标准化,否则报错:ValueError: Input contains NaN, infinity or a value too large for dtype(‘float64’).
问题4:在类别型变量不填充的前提下,连续型变量使用-1填充之后,对连续型变量标准化与否,影响建模效果吗?
结论:
对连续型变量的标准化,并不影响建模效果。标准化前后的建模结果完全一样。
问题5:字符型分类变量的缺失值使用missed填充之后,进行哑变量编码,对建模结果有影响吗?
结论:
字符型分类变量的缺失值的填充与否,并不影响建模结果,填充缺失值前后,建模结果完全一样。
简单说明一下原因:假设有4类职业,其中A代表警察,B代表教师,C代表销售,D代表司机。
假设现在有5个人(sample 1、2、3、4、5),其职业分别是警察A、销售C、司机D、教师B和空缺。
那么对应的哑变量编码之后的结果为:
哑变量名称 | 职业_A | 职业_B | 职业_C | 职业_D |
---|---|---|---|---|
sample 1 | 1 | 0 | 0 | 0 |
sample 2 | 0 | 0 | 1 | 0 |
sample 3 | 0 | 0 | 0 | 1 |
sample 4 | 0 | 1 | 0 | 0 |
sample5 | 0 | 0 | 0 | 0 |
如果使用missed填充之后,进行哑变量编码:
哑变量名称 | 职业_A | 职业_B | 职业_C | 职业_D | 职业_missed |
---|---|---|---|---|---|
sample 1 | 1 | 0 | 0 | 0 | 0 |
sample 2 | 0 | 0 | 1 | 0 | 0 |
sample 3 | 0 | 0 | 0 | 1 | 0 |
sample 4 | 0 | 1 | 0 | 0 | 0 |
sample5 | 0 | 0 | 0 | 0 | 1 |
在构造树的过程中,对于sample 5,其哑变量_A、_B、_C、_D均为0,则代表其缺失,自动生成一个空缺哑变量,并将其值自动填为1,与missed补缺,进行哑变量编码之后的结果是一样的。
问题6:数值型分类变量使用-1填补前后,对建模结果有影响吗?
结论:
数值型分类变量使用-1填充后,模型结果变差。此结论对于不同的数据集,可能的结果是不一样的。
参考内容:
- 《统计学习方法》
- 《数据挖掘导论》
- 《机器学习实战:基于Scikit-Learn和TensorFlow》
- 菜菜的sklearn课堂
- 《机器学习》(西瓜书)
- 赛事Baceline
- Datawhale 零基础入门数据挖掘-Task4 建模调参
- https://blog.csdn.net/v_JULY_v/article/details/81410574
- https://github.com/NLP-LOVE/ML-NLP/tree/master/Machine%20Learning
- https://zhuanlan.zhihu.com/p/29765582
- https://zhuanlan.zhihu.com/p/65304798
- https://zhuanlan.zhihu.com/p/45145899
关于Datawhale:
Datawhale是一个专注于数据科学与AI领域的开源组织,汇集了众多领域院校和知名企业的优秀学习者,聚合了一群有开源精神和探索精神的团队成员。Datawhale 以“for the learner,和学习者一起成长”为愿景,鼓励真实地展现自我、开放包容、互信互助、敢于试错和勇于担当。同时 Datawhale 用开源的理念去探索开源内容、开源学习和开源方案,赋能人才培养,助力人才成长,建立起人与人,人与知识,人与企业和人与未来的联结。
在此特别感谢DataWhale提供的学习机会,为其开源精神点赞,希望自己能在自己的努力下不断进步,很幸运能遇到DataWhale这么优秀的平台。加油加油!!!!