最近打了两个比赛,一直忙着工作和打比赛,没有时间总结,今天抽空好好总结一番
先说一下比赛结果吧,队名全为OfferGo
唯品会购买预测第五名,携程房屋预测复赛第六名,两个比赛打的都不算太好,只能算勉强及格,虽然离大神的距离还有十万八千米,不过总算可以称的上入了门,现在来总结一下我入门的经验吧。
观察数据 #
我参加过很多群,发现很多新手缺乏观察数据的能力,他们每次进入一个群总是嚷嚷这让大神发
baseline
这一点对于新手来说很不利的,比赛考的就是你对数据的掌握能力,你对数据把握的越好,你的比赛成绩就越好,要真正掌握数据就要从观察数据入手
在我看来观察数据主要从四个方面来,我总结为望闻问切
望 #
观察数据缺失值,缺失值对数据影响很大,有时候我们能够从缺失值里面了解很多信息,而且对于缺失值,后期我们对不同的缺失值要采取不同的手段,比如补全、统计占比、丢弃等等。
对于缺失值我一般从两个方面来观察
- 全局观察
-
- 一般采用
datafram.info(null_counts=True, verbose=True)
方法来观察全局数据缺失情况
- 一般采用
- 局部观察
-
- 一般采用
series.isnull().count()
和series.loc[series.notnull()]
观察单一列表缺失情况
- 一般采用
这个阶段我们主要从大的方向远远的望
一下数据,主要建立对数据的全局观。
闻 #
对于数据来说,一般分为三种,一种为数值型数据(整数、浮点数、时间等),一种为字符型,最后一种为图像型,三种类型数据处理难度依次增强
对于大多数比赛都是设计前两种数据,第三种只有牵扯到图像处理才能遇到。对于前两种数据,我们在闻的阶段,主要是探查数据分布情况,了解数据分布情况,我们才能对症下药。
了解数据分布情况有两种方法
- 图像观察
- 数学统计观察
图像观察主要使用Pandas
的Matplotlib
绘图接口,或者使用seaborn
(一个友好的封装了Matplotlib
的包),一般我们可以从直方图、饼图、频率图方向来观察数据
数学统计我们主要采用Pandas
的describe
方法,对于数值型数据,主要从平均值(mean
)、中位数(50%
)、标准差(std
)、最大(max
)、最小(min
)、非空总数(count
)来探测数据,对于字符型我们主要从最频繁的值(top
)、最频繁的值的个数(freq
),非空总数(count
)、不相同的值(unique
)。
通过上面两种方法,我们能够从数据分布的角度大致勾画出数据的轮廓。
问 #
比赛的目的就是找到最优解,而最优解的跟相关特征紧密联系的,你的特征对结果影响越大,你就要审
问
这个特征
举个例子,我们要预测三组数据
- 1 1.8 2
- 2 3.5 4
- 3 5.4 16
第一行为我们要训练的值,我们发现第二行的数据是第一行的1.8倍,而第三行只是2的次方,对于这两个特征来说,第二个特征就是最好的特征,我们只要建立一个映射,准确率能接近100%,而第三个特征对预测结果毫无联系,这个特征不但对结果没有作用,而且有时候会起到反作用
当然在这里我们举这么一个例子在实际中不可能遇到,我们遇到是更多数据,而数据之间的联系并不是这么简单的线性关系,但是线性关系有的时候能让我察觉到特征与预测值的关联,毕竟如果特征值是随机值那么与预测值之间的相关性是非常低的。
在Python
里面探测线性关系最简单的方法是调用特征值和预测值的相关性系数(corr
),我们可以简单的使用df[['feature', 'target']].corr()
就可以得到线性相关系数,这个数的绝对值越接近1,相关性越大,一般来说相关性越大和越好对结果都不好,最好的特征相关性处于中间位置。
相关性低我们可以理解,为什么相关性高反而不好呢,因为数据比赛里面给我们的数据大部分都是不平衡的数据,正负样本失衡,一般相关性很高的值一般为分类同预测值相同,比如一个二分类问题,预测值为0和1,给的样本正负比为1000:1,那么如果有一个特征全为0或者其他,那么他与预测值的相关性会达到90%以上,然而这个值是毫无作用的。
所以我们通过简单的相关系数并不能很好的观察特征真正相关性,一般我们要辅助图像法和统计法。
图像法就是通过将特征值分布与预测值相关性图表画在同一个图表里,具体可以参考可视化特征
统计法类似图标,使用统计方法观察,特征值与预测值的相关性,一般使用groupby
方法对两个特征进行统计就可以进行简单的观察
问
只是一个简单的手段,一般我们在大量添加特征的时候,为了节省模型训练时间,在将特征放入管道之前进行一个简单的过滤删除的工作,真正重要的步骤在切
这个方面
切 #
切
这个步骤放在最后是因为,这个步骤也是我们一趟循环下来的最后一步
数据比赛中前期大家最喜欢用的模型是树模型,比如随机森林、Xgboost
,LightBoost
等,这些模型属于弱学习器组合模型,我们最后可以从训练结果得到每个特征在模型占的比重
对于这个比重,是非常重要的,他代表了每个特征对应在模型中占的权重,也可以理解特征与结果的相关性
对于相关性很强的不同的特征,我们可以将他们组合,有时候这种强强组合生成出来的特征会比原来母特征相关性更强,当然组合的方法有千种万种,如何验证他们有效就要从头开始对数据进行望闻问切
了
总结: 数据比赛就如同问诊,我们不断对特征进行望闻问切
,对于高手来说他们能很快的从原始特征中挑选出病根
,对症下药,而新手的话,一阵摸瞎,经常会碰到在比赛中期做出一个很好的结果,接下来很长一段时间都没有进步的情况。掌握科学有效的挑选特征方法需要一个“医者心”,必须学会对特征“负责”,要学会望闻问切
。
#
并行化算法 #
由于
Python
本身对多核利用不好,如何利用多核加快特征生成对于比赛来说意义重大
就拿我来举例子,我每天下班打比赛的时间不超过8个小时,前期算法没有并行化的时候,走一遍管道要四个小时,这意味着我一天只能跑两次,而进行并行化优化以后,我跑一遍四线程全开(笔记本双核四线程)只要十分钟就能跑完,每次生成新特征只有10分钟就能拿到特征相关性数据,来验证特征的好坏。
下面我从三个方面来谈谈怎么实现并行话算法
1 . 使用系统自带函数,拒绝for
循环
#
举个例子,作为新手,实现对两个个特征求平均,一般采用for
循环将每一行两个特征值加起来然后除以2,假如有1000万行,每行加法和除法运算花0.001ms,那1000万也要10秒钟,只是进行一个最简单的求平均,你就花掉10秒钟,上百个特征你得运行几天
学过矩阵的都知道,矩阵就是一种高效的并行化结构,它将集合统一进行计算,可能一个大矩阵运算要比单一计算要慢,但是单一计算要1000万次的话,大矩阵运算只需要两次就够了,这个效率比就出来了
而Python
由于是一门解释性语言,比其他静态语音速度要慢许多,你一方面使用for
循环加大运算次数,一方面执行一次时间长,这相重叠加你的算法会跑的比蜗牛还慢
所以我们避免使用我们写的函数,尽量使用库系统函数,因为库系统函数底层是使用C
或C++
实现的,而且他们在底层进行使用矩阵话运算代替单一浮点计算,我们使用库的函数(比如mean
,groupby
等)一方面能底层能使用C加快速度,一方面使用矩阵运算加快速度,两个叠加你的算法跑的比飞机还快。
2 . 使用多进程,充分发挥使用多核性能 #
由于Python
的GIL
锁,使得Python
无法利用多核进行计算,所以我们只能使用多个进程来充分利用多核
实现多进程有两个要点(具体可以参考我携程比赛代码 Github地址)
- 特征提取模块化
- 进程池的搭建和维护
我在携程比赛中的mult_run.ipynb
中搭建了一个进程池,通过第三方调度和监控进程内存CPU等信息,达到充分“榨干”每个核的功效
3 . 压缩数据,让矩阵运算更快 #
由于在对特征进行提取过程中,Python
会自动将低位制值转换成高位制值,比如float16
在进行一次groupby
之后就会转换成float64
,由于在矩阵运算时候,高进制值会占更多内存和运行时间,所以为了加快算法运行,我们要将其压缩,一方面节省内存,一方面能够让算法运行的更快
在携程的比赛中,原始数据有一个G,我将其压缩之后只占用300M内存空间,这为我后面在一台12G内存的笔记本实现并行化算法提供了巨大帮助,当然我每次在生成新特征的时候也会进行压缩,具体可以参考我携程的utils.py
文件