特征工程就是将原始数据空间映射到新的特征向量空间,使得在新的特征空间中,模型能够更好地学习数据中的规律。如果特征工程做的足够好,即使是简单的模型,也能表现出非常好的效果。而复杂的模型可以在一定程度上减少特征工程的工作量。例如,对于线性模型,我们需要将类别变量进行独热编码等处理,但对于复杂一些的模型如树模型,则可以直接处理类别变量。像推荐系统中常用的 LR 模型,需要手工构造组合特征,而 FM 模型可以解决特征组合的问题,直接输入原始特征。而更复杂的 DNN 模型,可以自动学习特征的表示。
使用统计值进行填充,例如使用均值,中位数,众数等进行替代,需要视具体情况进行选择
使用缺失值的前一个或者后一个值作为填充值进行填充,但是如果最后一个是缺失值,那么后向填充无法处理最后一个的缺失值;如果第一个是缺失值,那么前向填充无法处理第一个的缺失值。因此,如果是数值型特征一般使用前向填充和后向填充的均值,如果是类别型特征综合前向填充和后向填充一起使用。
有时候可能缺失值也是一个特征值,可以用某个数值代替缺失值,即将缺失作为一种信息进行编码输入模型让其进行学习,比如用户性别缺失,可以直接将未知作为一种类别进行处理
使用模型预测缺失值,基本思路是把需要填充缺失值的某一列特征(Feature_A)作为新的标签(Label_A);然后找出与 Label_A 相关性较强的特征作为它的模型特征;把 Label_A 非缺失值部分作为训练集数据,而缺失值部分则作为测试集数据;若 Label_A 的值属于连续型数值,则进行回归拟合;若是类别(离散)型数值,则进行分类学习;将训练学习到评分和泛化能力较好的模型去预测测试集,从而填充好缺失值。当然也有一些模型可以直接处理缺失值特征,比如 XGBoost、LightGBM等
常用的异常值检测方法有简单统计、3σ原则、箱线图检测、 DBScan密度聚类噪音点检测等。在异常值处理中,主要有以下处理方式:
是否要删除异常值可根据实际情况考虑。因为一些模型对异常值不很敏感,即使有异常值也不影响模型效果,但是一些模型比如逻辑回归LR对异常值很敏感,如果不进行处理,可能会出现过拟合等非常差的效果。
特征提取就是对原始数据进行处理与变换的过程。
数值类型的数据具有实际统计意义,虽然一些机器学习模型可以直接输入数值类型的数据,但是通常情况下,对数值型数据进行适当的变换和处理能带来更优的效果。对于数值特征,我们主要考虑的因素是它的大小和分布。
数据的标准化或者归一化是将数据按比例缩放,将其转化为无量纲的纯数值,使得不同单位或量级的特征之间具有可比性,对于利用梯度下降来训练模型参数的算法,有助于提升模型的收敛速度。需要强调的是,不同业务的数据,其数据分布是不同的,缩放的方法一定要符合其特定的数据分布。一般会根据实际数据的情况,对常规做法或者公式进行调整,但大体思路上还是一致的
StandardScaler
:z-score 标准化,经过处理的数据符合标准正态分布,即均值为 0,标准差为 1,其中 μ 为所有样本数据的均值,σ 为所有样本数据的标准差。这种标准化方式要求原始数据的分布可以近似为高斯分布,否则效果会变得很糟糕。 可以更容易的得出最优参数,从而达到加速收敛的效果。不免疫outlier。
RobustScaler
:如果数值特征列中存在数值极大或极小的outlier(通过EDA发现),应该使用更稳健(robust)的统计数据:用中位数而不是算术平均数,用分位数(quantile)而不是方差。这种标准化方法有一个重要的参数:(分位数下限,分位数上限),最好通过EDA的数据可视化确定。免疫outlier。
Normalizer
:把每一行数据归一化,使之有unit norm,norm的种类可以选l1、l2或max。不免疫outlier。
MaxAbsScaler
:将一列的数值,除以这一列的最大绝对值。不免疫outlier。
MinMaxScaler
:是对原始数据的线性变换,使结果落到[0,1]区间,其中 max 为样本数据的最大值,min 为样本数据的最小值。这种方法有一个缺陷就是当有新数据加入时,可能会导致 max 值和 min 值的变化,需要重新定义。如果 max 值和 min 值波动较大,容易使得标准化结果不稳定,因此在实际使用中常用经验常量值来替代最大最小值,不免疫outlier。
非线性标准化,这种方法一般使用在数值差异较大的场景,通过一些数学函数,比如对数、指数、正切等,将原始值做映射变换。实际使用中,需要根据不同业务的数据分布来选择,比如对数缩放,对数缩放对于处理长尾分布且取值为正数的数值变量非常有效,可以压缩数据范围,将长尾变为短尾,像 log2 和 log10 转换在微视中也都有使用,是一种方差稳定的变换。
分桶操作可以看作是对数值变量的离散化,之后通过二值化进行 one-hot 编码。分桶的数量和宽度可以根据业务领域的经验来指定,也有一些常规做法:
分桶是离散化的常用方法,将连续特征离散化为一系列 0/1 的离散特征,离散化之后得到的稀疏向量,内积乘法运算速度更快,计算结果方便存储。离散化之后的特征对于异常数据也具有很强的鲁棒性。需要注意如下三点:
类别特征可以是标签、属性、类型 ,比如用户id、作者、类别、风格等属性特征。同时也可以将数值特征离散化,从定量数据中获得定性数据。
序号编码通常用来处理类别间具有大小关系
的数据,比如成绩(高中低),而对于国籍(中国、美国、印度等)没有大小、等级关系,而是平等的概念。
通常用于处理类别间不具有大小关系的特征,每个特征取值对应一维特征,能够处理缺失值,在一定程度上也起到了扩充特征的作用。比如血型(A型血、B型血、AB型血、O型血),独热编码会把血型变成一个稀疏向量,A型血表示为(1,0,0,0),B型血表示为(0,1,0,0),AB型血表示为(0,0,1,0),O型血表示为(0,0,0,1)
注意:
平均数编码是针对高基数定性特征(类别特征)的数据预处理,如果某一个特征是类别型特征,而这个特征的可能值非常多(高基数),那么平均数编码(mean encoding)是一种高效的编码方式。在实际应用中,这类特征工程能极大提升模型的性能。
高基数类别特征的例子:IP地址、电子邮件域名、城市名、家庭住址、街道、产品号码。这类特征不能使用LabelEncoder
和OneHotEncoder
编码,主要原因:
实现:
mean_encode = df.groupby('Temperature')['Target'].mean()
df.loc[:,'Temperature_mean_enc'] = df['Temperature'].map(mean_encode)
df
注意:在类别特征列里,有时会有一些类别,在训练集和测试集中总共只出现一次,例如特别偏僻的郊区地址。此时,保留其原有的自然数编码意义不大,可以将所有频数为1的类别合并到同一个新的类别下。但是,如果特征列的频数需要被当做一个新的特征加入数据集,需要在上述合并之前提取出频数特征。
对于有些取值特别多的类别特征,使用独热编码得到的特征矩阵非常稀疏,再加上如果还有笛卡尔积等构造的组合特征,会使得特征维度爆炸式增长。特征数量多的问题自古有之,目前也已经有很多用于降维的方法。比如聚类、PCA 等都是常用的降维方法。但这类方法在特征量和样本量很多的时候本身就计算量很大,所以对大问题也基本无能为力。特征哈希就是一种简单的降维方法,目标就是是把原始的高维特征向量压缩成较低维特征向量,且尽量不损失原始特征的表达能力,其优势在于实现简单,所需额外计算量小;降低特征维度,从而加速算法训练与预测的时间,以及降低内存消耗;但代价是通过哈希转换后学习到的模型变得很难检验,我们很难对训练出的模型参数做出合理解释。特征哈希法的另一个问题是它会把多个原始特征哈希到相同的位置上,出现哈希 collision 现象,但实际实验表明这种 collision 对算法的精度影响很小。
特征构造主要是产生衍生变量,所谓衍生变量是指对原始数据进行加工、特征组合,生成有商业意义的新变量(新特征),特征构造也可称为特征交叉、特征组合、数据变换。新构造的有效且合理的特征可提高模型的预测表现能力,但是对于新构造出来的特征不一定是对模型有正向影响作用的,也许对模型来说是没有影响的甚至是负向影响,拉低模型的性能。需要反复参与模型进行训练验证或者进行特征选择之后,才能确认特征是否是有意义的。同时需要根据具体的问题构造出与目标高度相关的新特征,如此一来说明特征构造是有点难度的。需要不断结合具体业务情况做出合理分析,才能有根据性的构造出有用的新特征。
单独特征列乘以一个常数(constant multiplication)或者加减一个常数:对于创造新的有用特征毫无用处;只能作为对已有特征的处理。
在推荐系统特征工程中,经常会用到很多统计类特征、比率特征,例如在对商品转化率预估建模时,经常会使用item的点击率这个特征,会计算最近1天的点击率、最近3天的点击率,最近一周的点击率等作为CVR模型特征使用,但是由于数据的稀疏性,这种计算方式得到的统计量通常具有较大的偏差,需要做平滑处理。如果直接使用,比如由于不同 item 的下发量是不同的,这会让推荐偏向热门的类目,使得越推越窄,无法发现用户的个体差异,也不利于多样性的探索。我们可以把曝光量进行分段,同一个曝光量级的指标进行比较,也可以用该 item 所属类目统计量的平均值进行平滑处理。对于离群值较多的数据,我们会使用更加健壮的处理方法,比如使用中位数而不是均值,基于分位数而不是方差。
贝叶斯平滑
电商领域中经常需要计算或预测一些转化率指标,比如 CTR。这些转化率可以是模型的预测值,也可以作为模型的特征使用。以商品点击率预测为例,CTR 的值等于点击量除以曝光量。理想情况下,例如某个广告点击量是 10000 次,转化量是 100 次,那转化率就是 1%。但有时,例如某个广告点击量是 2 次,转化量是 1 次,这样算来转化率为 50%。但此时这个指标在数学上是无效的。因为大数定律告诉我们,在试验不变的条件下,重复试验多次,随机事件的频率近似于它的概率。后者点击量只有 2 次,不满足“重复试验多次”的条件。如果对于一个新上线的商品,其曝光为 0,点击量也为 0,此时这件商品的 CTR 应该设为 0 还是赋一个初始值?初始值设 0 是可以的,但不太合理。当 CTR 作为特征使用时,表示这个商品完全没有点击,不太符合日常推断,通常是赋一个大于 0 的初始值。
贝叶斯平滑的思想是给 CTR 预设一个经验初始值,再通过当前的点击量和曝光量来修正这个初始值。如果某商品的点击量和曝光量都是 0,那么该商品的 CTR 就是这个经验初始值;如果商品 A 和商品 B 的曝光量差别很大,那么可以通过这个经验初始值来修正。贝叶斯平滑就是确定这个经验值的过程。贝叶斯平滑是基于贝叶斯统计推断的,因此经验值计算的过程依赖于数据的分布情况。对于一件商品或一条广告,对于某次曝光,用户要么点击,要么没点击,这符合二项分布。因此对于点击率类的贝叶斯平滑,都可以基于以下假设:对于某件商品或广告,其是否被点击是一个伯努利分布。伯努利分布的共轭分布就是 Beta 分布,也就是说,点击率服从 Beta 分布。而所有的数据有一个自身的点击率分布,这个分布可以用不同的 beta 分布来拟合。beta 分布可以看做是对点击率的一个先验知识,我们可以根据观测来修改我们的先验,所以贝叶斯平滑就是估计 Beta 分布中的参数 α 和 β,其中 C 和 I 是点击次数和曝光量。实际应用时根据历史数据得到的 α 和 β 可以帮助确定平滑参数的大致范围,防止设置参数时偏离过大。
特征交叉可以表示特征之间的相互作用,有助于表示非线性关系,增强对问题的刻画,缺点是维度快速增长,需要更多的训练样本。提升模型表达能力常见的关联方式有内积、笛卡尔积、哈达玛积等。特征交叉可以通过一些特征选择方法来选择有效的组合特征,比如卡方检验、特征相关性分析等;也可以根据经验进行组合, 有些 cross 特征,虽然可能没有直观的解释,但也常常会给模型带来很大的效果提升。除了手工构造交叉特征外,有些模型可以自动进行特征的交叉组合,比如常用的 FM 和 FFM 模型等。
在实际应用中,类别特征之间的组合方式千变万化,这类特征一般从业务逻辑的角度出发进行构造。相比类别特征之间的笛卡尔积操作,基于分组统计的特征组合方式计算更加复杂,需要对业务数据有较好的理解。比如在教育行业中老师和学生通过上课能够成单的因素除了老师本身教学水平的因素,还有老师的地域与学生的地域关系,老师的性别与学生的性别等关系,对这些类别进行组合之后的交叉特征对模型可能更加有用。
这类特征通常是在类别特征某个具体类别中计算一些统计量。例如用户对不同类目视频的完播率、平均播放时长,不同用户群体的互动指标等利用数值特征对类别特征进行处理。比如分析某个用户的样本发现类似王者荣耀_31_31 的组合,即我们的推荐系统给这个用户曝光了 31 个王者荣耀的视频,但是每个都快速划过了,如果还是继续推,这个用户体验将会是极度糟糕的,而这个特征会很大程度的解决这个问题,使得尽可能的给这个用户曝光感兴趣的类目。这个特征可以进一步细化为类目曝光次数 cross 一些统计量,比如完播次数、互动次数等,因为没有快划可能是用户愿意尝试去看,完播可能是喜欢这个视频内容,而互动比如说转发或者点赞,可以看做是用户表现出了更为强烈的兴趣。
当数据处理好之后,我们需要选择有意义的特征输入机器学习的模型进行训练,通常来说要从两个方面考虑来选择特征,特征是否发散,如果一个特征不发散,例如方差接近于0,也就是说样本在这个特征上基本上没有差异,这个特征对于样本的区分并没有什么用;特征与目标的相关性,这点比较显见,与目标相关性高的特征,应当优先选择。但是特征与特征之间相关性高的,应当优先去除掉其中一个特征。
进行特征选择可以减轻维度灾难的问题,降低学习任务的难度
过滤法特征选择一般主要考虑特征变量和目标变量之间的相关性以及特征变量之间的相互关系,一般认为相关度大的特征或者特征子集会对后续学习算法带来较高的准确率。过滤式特征选择独立于学习算法,不需要依赖任何模型,直接由数据集求得,评估依赖于数据集本身,这类方法在预处理时也使用较多,优点是计算效率高、复杂度低,独立于算法,但也可能选出冗余的特征。
与过滤方法不同,封装式特征选择直接使用机器学习算法评估特征子集的效果,直接面向算法优化,效果好,缺点是需要对每一组特征子集训练一个模型,计算复杂度高。常用的特征子集搜索算法有:完全搜索;基于贪心的启发式搜索(前向/后向搜索等);随机搜索(模拟退火、遗传算法等)。
过滤式方法与模型算法相互独立,不需要交叉验证,计算效率比较高,但是没有考虑具体模型算法的特点。封装式方法使用模型来评估特征子集的质量,需要多次训练模型,计算效率很低。嵌入式方法将特征选择本身作为组成部分嵌入到学习算法里,速度快,效果好,不足是与算法绑定,需要知识调整结构和参数配置。
在数据分布不平衡时,其往往会导致分类器的输出倾向于在数据集中占多数的类别。输出多数类会带来更高的分类准确率,但在我们所关注的少数类中表现不佳。在评估指标方面,准确率在类别不平衡数据上,说服力最差。应考虑精确率、召回率、F1 值、F-R 曲线和 AUC 曲线。
欠采样是指把占比多的类别 A 样本数量(M=900)减少到与占比少的类别 B 样本数量(N=100)一致,然后进行训练。
随机欠采样
随机欠采样是指通过随机抽取的方式抽取类别 A 中 100 个样本数据与类别 B 中的 100 个样本进行模型训练。随机欠采样只是采取少部分数据,容易造成类别 A 的信息缺失
EasyEnsemble 集成学习法
利用集成学习机制,将占比多的类别 A 样本数据划分为若干个样本子集供不同学习器使用,
这样对每个学习器来看都进行了欠采样,但在全局来看却不会丢失重要信息。
EasyEnsemble 集成学习法可以解决传统随机欠采样造成的数据信息丢失问题,且表现出较好的不均衡数据分类性能。
过采样是指把占比少的类别 B 样本数量(N=100)扩增到占比多的类别 A 样本数量(M=900)一致,然后进行训练。
随机过采样
由于随机过采样采取简单复制样本的策略来增加少数类样本,这样容易产生模型过拟合的问题,即使得模型学习到的信息过于特别(Specific)而不够泛化(General),因此很少使用这种方法。
SMOTE 算法
SMOTE 算法 是通过设计算法来人工合成一些新的少数样本。
加权处理是指通过调整不同类型标签的权重值,增加占比少的类别 B 样本数据的权重,降低占比多的类别 A 样本数据权重,从而使总样本占比少的类别 B 的分类识别能力与类别 A 的分类识别能力能够同等抗衡。
遍历每一个样本,设总样本占比多的类别 A 的权重为 W1(自定义),总样本占比少的类别 B 的权重为 W2(自定义),其中 W2 > W1。其实这个也类似于对模型进行惩罚,从而影响各个类别标签的重要性。