Python-用sklearn做特征工程

浏览: 5153

第18章

特征工程(Feature Engineering

 

 

本章从商业数据分析和挖掘的角度详细地介绍了特征工程及其使用的响应方法:数据预处理、特征构造、特征抽取以及特征选择。系统性地说明了用于构建分析用的结构化数据的过程。

18.1 特征工程概述

在商业数据的分析挖掘当中,最常用的数据是结构化数据,其呈现为二维表的结构,数据可以用装载到二维数组当中,其中的每个数可以使用行与列进行索引。结构化数据中的每一行称为记录,也可称为样本或实例(视不同的学科而定),每一列则被称为字段,也可称为变量或特征(同样视学科领域而定),其中“特征(Feature)”这一叫法在机器学习及模式识别当中被广泛使用,在应用当中只要不引起歧义,可以不区分它们的叫法。

特征(feature)是一个被观察到的现象的可测量的属性,结构化数据中,特征通常以列的形式出现,用于描述记录在某些方面的属性;非结构化数据同样如此,例如对于一个文档,短语或单词的计数就可以是其一个特征。

特征是模型的输入,而不同的模型对输入有不同的要求。正因如此,原始数据往往需要通过一定的处理和转换才能在模型中使用,而为了提升模型的表现,又需要对所有可用的特征进行一定的筛选,既保证重要的特征进入模型,又要保证不会选择过多的特征。所有这些在建模前对数据进行处理、转换、筛选的工作被称为特征工程(Feature Engineering),其本质上是对原始数据的再加工,目的是产生进入模型的特征。“特征工程”这一名称在机器学习与模式识别领域被大量使用,在数据挖掘标准流程CRISP-DM当中,“数据准备”(Data Preparation)具有与特征工程相同的作用和类似的方法,因此在应用当中,无需严格区分,本章当中使用“特征工程”这一名称。

特征工程分为数据预处理(DataPreprocessing)、特征构造(Feature Construction)、特征抽取(Feature Extraction)和特征选择(FeatureSelection)等几个步骤,每个步骤间没有明显的顺序之分,往往需要根据需求反复执行,甚至也没有严格区分的概念边界,例如特征构造可能会与数据预处理使用同样的数据变换技术等等。

特征工程在整个数据分析的流程当中占用最多的工作时间,而且具有重要的地位,可以认为,数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已。

此外,为了演示特征工程的案例,本章采用pandas与scikit-learn进行数据的处理。因为商业数据通常是结构化的,pandas包可以很好地对这类数据进行读取、统计、汇总、变换,也可以进行一些简单的可视化,因此数据的预处理可以使用pandas中的函数和方法。同时scikit-learn包作为流行的机器学习框架,对机器学习的多种方法都有支持,包括了对数据预处理、特征选择等建模前步骤的方法支持。因此部分案例既使用了pandas,也使用了scikit-learn进行实现,目的是为了使读者能更深入地了解它们的使用方法和异同点。

下面通过对一份手机用户通话账单进行处理来演示特征工程的各步骤,原始数据包含了1个属性,包括了ID、是否流失、入网时间、是否VIP、套餐编号、月消费、月通话时长、月流量消耗、用户状态及手机串号,数据集如下所示:

其中"display.max_columns"设置了在notebook中显示的最大列数,当显示的数据超过10列时中间的部分数据会使用省略号表示,这仅仅是为了展示方便而已,如果该值被设置为"none",则会显示所有列,超出页面宽度时会出现相应滚动条。

18.2 数据预处理(Data Preprocessing)

数据预处理是为了满足数据后续处理与建模的基本步骤要求,包括对噪声数据的清洗,以及为满足模型需求的基本数据变换。其中噪声包括了错误值、异常值等类型;基本数据变换除了常用的函数变换,还包括二值化、哑编码、标准化、规范化等内容。

18.2.1错误值处理(Wrong Value Processing)

错误值是指数据集中出现的数值、格式、类型等错误,导致错误的原因包括:

(1)输入错误:录入信息时缺失、错误输入、类型或格式不符等。

(2)设备故障:例如采集设备故障、数据分析工具不够健壮等。

(3)其他:因为对数据理解不够导致数据处理失误,比如造成数据重复、错误截断数据等。

错误值产生的原因多种多样,有些错误值是显而易见的,有些则不那么明显,需要结合对业务的理解以及对多种数据源之间互相对照来发现。要修正错误,需要明确错误产生的原因,这就需要对业务与数据有深入了解,常用的处理错误值的方法包括以下几种。

(1)修正

补充正确信息:例如重新导入原始的正确数据。

对照其他信息源:例如使用CRM系统中正确的用户年龄替换手工填报的错误年龄。

视为空值:将数据清空,等待下一步处理。

(2)删除

删除记录。

删除特征。

如果通过删除来处理错误值,那么要保证该处理不会对后续工作造成重要影响,删除的记录过多会影响数据的分布,删除的特征也不能是那些重要的字段。

以下是一个通过业务规则发现错误值的例子。

在用户消费的流量“traffic”字段中,有一些是超过5000MB的,而根据业务规则,电信运营商限制了单用户每月消费的流量不得超过5000MB(有些套餐设置不得超过15GB),设置这个规则是为了避免用户在不知情的情况下消耗过多流量而引起投诉。这项规则一般可以通过联系服务商而关闭,而一些用于内部测试的号码则没有这项规则的限制。如果筛选出来的数据经确认是错误的,或者是测试号所为,则可以删除它们,因为分析的目标不应当包含测试号这类特殊用户。因此,删除(或筛选)处理方式如下。

新的数据集“data”将不包含流量消费大于5000MB的用户。

对于分类变量,可以采用一定的技术手段去识别错误值,因为错误值通常极少出现,因此可以通过对比分类变量各分类水平之间的数量差异去识别。比如对用户状态“state”进行汇总,代码如下。

通过汇总,发现用户状态为“2”和“4”的记录非常少,则这两类数据很有可能是错误的。为了更直观地展现数量差异,可以使用饼图来描述数据:

通过图形可以非常容易发现“2”与“4”这两种状态的用户很少,如果对照业务规则,发现这两种状态是不应当出现的,那么可以删除这些记录。另外一种可选的方式是先把这两种状态清空,后期再用合适的方法填补,代码如下。

当然了,根据业务规则,实际上这两种状态是确实存在的,而且是较少出现的。实际建模中,我们往往难以区分分类变量中的某些很少的编码到底是这个对应的业务发生地少,还是填报错误,因此对都作为错误进行处理。去除这类出现较少的分类有两方面考虑:1)属于这一类的客户太少,单独分类意义不大;2)数量极少的组在抽样时会出现抽样分布不均匀现象,造成模型无法使用。上例中分组为“2”的客户只有9个,有可能这9个样本在建模时都被随机抽样的到测试数据集,而在训练数据集中没有出现。我们知道分类变量在构建模型时要使用哑编码,因此训练的模型中没有“2”对应的编码,而在打分时,测试数据集有这个编码,因此模型出现报错。

18.2.2异常值处理(Outlier Processing)

异常值是指那些远远偏离多数样本的数据点。商业数据挖掘中,一般多数样本聚集在中心附近,而少数记录偏离它们,形态上接近正态分布或对数正态分布(对连续变量而言),我们仅讨论这种情况。

根据数据偏离程度的不同,可将异常值分为极端值与离群值(其中极端值偏离得更远一些)。从单变量的角度看,识别异常值可以采用平均值法、四分位数法等方法;从多变量角度看,可以使用聚类来识别异常值(某些记录可以在单个变量分布上并无异常),聚类方法会在其他章节进行介绍。

异常值有时意味着错误,有时不是。无论是否代表错误,异常值都应当被处理。通常可以根据对业务的理解来设置异常值的边界,也可以使用一定的技术手段辅助识别异常值。常用的识别方法包括以下两种。

(1)平均值法:对于正态分布来说,三倍标准差之外的样本仅占所有样本的1%,因此,设置平均值±3×标准差之外的数据为离群值,极端值被定义为平均值±5×标准差之外数据。

(2)四分位数法:设置1.5倍四分位距以外的数据为异常值,即IQR = Q3–Q1,IQR为四分位距,Q3和Q1分别为第三和第一四分位点,定义正常数据在Q1–1.5 ´ IQR ~ Q3 + 1.5 ´ IQR 之间。

例如:绘制月消费额箱线图。

其中上面单独的横线位置代表离开第三个四分位点1.5倍四分位距的数据,超出以外的数据就是异常值。

对异常值的处理方法通常包括以下几种。

(1)视为空值:待后期对空值进行处理,如果后期的处理策略是对空值进行填补,则视填补方式的不同会不同程度地改变原有数据的分布。

(2)盖帽法:异常值被重新设定为数据的边界,这种方法同样会改变原有数据的分布,但由于异常值往往是少数,因此可以采用。

(3)变量转换:通过一定的变换改变原有数据的分布,使得异常值不再“异常”,常用的转换是对数变换,这对那些严重右偏的数据非常有用,变换后的数据能够更接近正态分布,如图15-3所示。

取对数  
取对数

图17-3

(4)考虑分段建模或离散化:分段建模是将那些偏离中心的样本单独进行分段建模,其隐含的商业逻辑为离群点的内在规律与多数不同,例如收入很高的人群,其行为与普通人群必然不同,其行为所反映的内在规律必然也有较大的差异,应当单独进行分析;离散化是通过设置分割点将连续型变量转换为离散型,例如将年龄这一字段离散化,可以通过设置分割点划分为儿童、少年、青年、中年、老年,这样年龄很大的人会被作为单独一类进行分析。值得一提的是,连续型变量往往是模型不稳定的原因,离散化之后通常能提高模型的泛化能力。

使用盖帽法进行异常值处理的示例如下。

通过将离开中心点三倍标准差之外的数据重新赋值,用户月消费额被限制在了250元以内,尽管新数据使用四分位法或平均值法分析后,又可能出现了“异常值”,但一般不会再次处理。

对于月消费额这样的数据绘制直方图,可以发现其明显右偏,代码如下。

这类数据也可以通过对数转换使其更接近于正态分布,同时消除异常值,由于对数函数的定义域为(0,+∞),而消费额这样的数据往往包括0,因此可以对数据进行加1操作,再进行对数转换,这样的转换可以保证原来为0的数据转换后依然为0,新数据都是非负的,示例代码如下。

上述代码中使用了np.log函数,传入的参数正是原始数据+1后的结果。numpy中定义了一个函数log1p可以产生同样的效果。

除了使用对数函数直接转换数据之外,scikit-learn提供了preprocessing这样的预处理模块,可以利用其中定义的函数转换器达到同样的效果,示例如下:

可以看到使用scikit-learn中的函数转换可以一次性对多个字段进行处理。

对数转换后发现存在大量的0值,这些0消费额的账单比较特殊,因此完全可以单独提取出来进行分析。同时,去掉了0值的数据更加接近正态分布,因此对于后续的分析工作更加有利。

18.2.3缺失值填补(Missing Data Imputation)

缺失值在页面上显示为NaN,意味着Not a Number,等价于numpy当中的空值NaN。缺失值的出现是非常普遍的,在pandas当中,对DataFrame进行count计数,不会统计其中的缺失值,因此可以使用该方法来判别哪些字段上有缺失值以及缺失的数量(除非所有字段都有缺失值,这种情况不多见,一般ID识别号是不会有缺失的),如下所示:

为了更加准确的判断缺失值的数量,可以使用isnull函数,如果数值为空返回True,否则为False,再进行求和(True被作为1,False被作为0),就可以统计每个字段缺失值的数量。同样可以使用notnull函数进行类似分析,其返回结果与isnull相反。示例如下:

缺失值的数量会影响缺失值处理的策略,同时,缺失值的处理需要根据字段代表的业务内涵来进行,常用的方法包括:

  Ÿ  通过对比其他数据源填补空值;

  Ÿ  对缺失不多的连续型字段填补中位数或均值;

  Ÿ  对缺失较多的连续型字段,生成一个新字段用于标示哪些记录是被填补的(比如用1表示缺失记录,用0表示非空记录),原字段不再使用;

  Ÿ  对缺失不多(比如少于20%)的离散型字段填补众数或填补为“未知”这样的新分类;

  Ÿ  对缺失较多(比如多于20%并且少于50%)的离散型字段填补为“未知”这样的新分类,同时生成一个新的字段用于标示哪些记录是被填补的(比如用1表示填补的记录,用0表示之前非空的记录);

  Ÿ  对缺失很多(比如多于50%并且少于80%)的离散型变量,直接使用新字段标示哪些记录存在缺失,原字段不再使用;

  Ÿ  对缺失非常多(比如多于80%)的字段(包括离散型与连续型)直接删除;

  Ÿ  对缺失值过多的记录直接删除;

  Ÿ  通过建模从其他字段“预测”目标字段的缺失值应当为多少。

由于业务的多样性,上述方法只是一个参考,以下是一个填补缺失值的例子(见下图):

缺失值的处理可以非常灵活,比如对“营销次数”的填补可以使用众数填补,也可以填为0(如果缺失是因为在营销数据库中没有该记录造成的则填0)。此外,营销活动通常是对满足一定条件的用户开展的,因此也可以通过建模“预测”该用户“被营销”的次数。

以下是对之前的data数据集按照缺失值数量进行记录删除的一个例子。

上例使用了dropna方法,对"state"和"IMEI"这两个字段,如果缺失值超过1个(即参数thresh),则删除该条记录,即删除这两个字段都缺失的记录,因此新数据集的记录数减少了。当然了,因为数据集共有10个字段,仅仅两个字段缺失就删除有点过于冒失,因此,这个例子仅为演示代码所用,无需实际保留该结果。

除了按条件删除记录,pandas也提供了对缺失值进行填补的方法fillna,一个使用该方法填补缺失值的例子如下:

在上例中,使用0来填补手机串码"IMEI"的缺失值,用账户状态"state"的众数来填补自身的缺失值,其中mode方法用于求众数,由于众数可以有多个,因此该方法会返回一个数组,包含所有众数,使用索引取得第一个众数用于填补。fillna中的参数inplace用于指定是否直接替换原缺失值,如果设置为True,会对数据就地进行修改,否则会生成一个填补后的新dataframe对象。

在scikit-learn的preprocessing模块中,也提供了用于缺失值填补的类Imputer,其使用方法如下所示:

这种方法可以指定用于表示缺失的值,strategy参数则指定了填补策略,比如示例中使用的众数,其它策略包括均值、中位数等,具体可参考帮助或文档。

18.2.4二值化(Binarization)

二值化,顾名思义就是将一个字段转换为仅有两个可能值。二值化通过设定一个阈值,原字段大于阈值的被设定为1,否则为0,即转换函数为指示函数:

对于示例数据集当中的"IMEI"字段是手机串码(原本为15位),一部移动设备对应一个,是全球唯一的,这样的字段与ID字段一样无法进入模型。通过观察,发现有大量的记录IMEI为空值(现已填补为0),因此可以将有IMEI的设置为1,没有的设置为0。在scikit-learn的preprocessing中,使用Binarizer可以将字段二值化,示例如下:

除了使用Binarizer,在可以使用pandas.cut对数据进行离散化,这种方式通过传入一个用于表示阈值的"bins"参数,将数据进行分割,如下所示:

需要注意的是传入的阈值列表是包含了上下界的,切割的区间为左开右闭,即切割后的两个区间为(-∞,0]和(0,+∞)。如果要切割成多个区域,只需把所有阈值以列表形式传入"bins"参数即可。

18.2.5离散化(Discretization)

连续型特征经常成为模型不稳定的来源,此外,连续型特征可能与目标变量呈现复杂的相关性,而将连续型特征根据一定方法转化为离散特征后可能带来模型效果的提升。常用的离散化方法包括等宽离散、等分离散、人工离散等,二值化(Binarization)可以看做是离散化的特殊情况。

离散化的基本原理是将连续数据划分至不同的区间(也称为“分箱”),使用区间的编码来代替原始数据即可。例如:等宽离散的区间是按照数据的极差进行平均划分的,其每个区间的宽度相等;等分离散化是按照数据的分位数划分的,其每个区间内的样本量相等(或接近相等);人工离散化则是根据业务理解进行区间划分的。一个针对年龄进行不同离散化的例子如下图所示:

 

为了演示离散化,我们将示例数据集中的“入网时间”转化为“在网时长”:

至此,我们“在网时长”属于连续型特征,我们可以对其进行离散化。

1. 等宽离散

使用Pandas进行等宽离散的例子如下:

离散化后,可以看到(1.892, 36.913] 区间内的样本量最多,(141.453, 176.3]区间内样本量最少,在不同的区间内用户离网率(mean字段)呈现有规律的变化。这里可以注意一下,pandas等宽离散的每个区间都是左开右闭的。

pandas提供的cut方法可以通过传输label参数,将每个离散化后的区间用指定的编码进行表示,并且新生成的编码是有序的。例如使用0到4的整数来表示等宽离散的每个区间如下:

可见,原始的在网时长字段可以被用0到4来替代,函数同时返回了划分区间的每个阈值。

2. 等分离散

pandas提供的qcut方法可用于进行等分离散,但有bug,所以这里重新定义一个qcut函数:

该函数使用分位数确定某一列特征的划分区间,再使用pandas.cut将数据按照划分区间进行离散,这里的pandas.cut函数因为指定了区间,将不再按照默认的等宽方式离散了。

使用自定义的函数可以方便地对数据进行等分离散:

可以看到,离散化后每个区间内的样本量基本相等,各区间内的用户离网率呈现出一定的规律。也正是因为每个区间的样本量基本相等,这个离网率显得更有可信度。如果采用其他离散化方法,可能造成某些区间的样本量过少,该区间的离网率自然很难有说服力。本例中我们将离网率按等分离散的区间绘制柱状图如下:

从图中,离散化后的离网率规律一目了然。

3. 人工离散

仅需要人工指定区间划分的阈值就可以很容易地使用pandas.cut进行人工离散化,如下例:

本例中,为了保证能将所有数据都包含进来,分别使用正负无穷大作为最大与最小值。

18.2.6哑编码(Dummy coding)

对于无序的分类变量,许多模型不支持其运算(在R中许多模型,例如回归模型,会自动将因子变量转换为哑变量,省去了很多麻烦),可以先生成哑变量,再进行深入分析。哑变量被认为是量化了的分类变量,因此应用非常广泛。

哑变量又称虚拟变量或虚设变量,一般使用0和1来表示分类变量的值是否处于某一分类水平,例如账户ID1~4的办理渠道如图18-1左图所示,进行哑编码后用0和1来表示各记录是的具体取值情况,如图18-1右图所示:

 

图18-1

一般有m个分类水平的变量经过哑编码之后,会生成m个哑变量,各个哑变量之间两两互斥,应用中一般仅保留其中m-1个进入模型,留下1个作为对照组,对照组可以通过其他m-1个哑变量完全还原出来。在使用哑变量建模时,一般需要保证由这m-1个哑变量都能进入模型,或者都不进入模型。

在pandas中可以使用get_dummies函数来对分类变量进行哑编码,新生成的哑变量与原变量的分类水平数相等,对照组可自行选择,get_dummies使用方法如下所示:

另外,scikit-learn的preprocessing当中,可以使用OneHotEncoder对数据进行哑编码,同样会生成与原变量水平数相等的哑变量,代码如下:

 

18.2.8标准化(Standardization)

在商业分析中,不同的变量存在单位不同的问题,比如流量使用MB作为单位,而金额使用元作为单位,因此流量、金额无法直接进行比较;同时,单一指标采用不同单位也会无法直接比较,比如收入用元作单位和用万元作单位会在数值上相差很大。这样的差异性会强烈影响模型的结果,比如下例中的聚类方案(见图18-2)。

图18-2

同样的样本点,如果流量采用MB为单位,则A\B为一个簇,C\D为一个簇;如果使用GB为单位,则A\C一个簇,B\D一个簇。可见因为单位不同对模型的结果会产生颠覆性的影响。

为了消除字段间因为单位差异而导致模型不稳定的情况,需要将变量的单位消除,使得它们都是在一个“标准”的尺度上进行比较分析。因此需要采用标准化的技术,常用的方法包括极值标准化与学生标准化:

  Ÿ  极差标准化

  Ÿ  学生标准化

极值标准化将数据映射到[0,1]之间,受极端值影响较大;学生标准化更多应用在对称分布的字段上(因为对称分布的均值有代表性),受极端值的影响较小,是多数模型的默认标准化方法。另外,这两种标准化方法都不会改变原字段分布图形的形态,仅仅是对数据进行了线性缩放而已。

使用pandas提供的函数可以很方便地对字段进行标准化,使用scikit-learn也可以达到同样效果,而且在应用中可能更加方便,比如我们对已进行了对数转换的收入、通话时长、流量消费(接近正态分布)进行学生标准化的例子如下:

使用preprocessing.StandardScaler可以对多个字段一次性进行学生标准化,而且fit之后的转换器std_scaler可以重复使用。比如,使用训练集数据fit的转换器可以对测试集进行相同的转换(使用训练集的均值和标准差进行学生标准化)。

将标准化后的数据添加到数据集当中:

极值标准化的方法也类似:

在很多软件中,会在建模前默认对数据进行标准化,但并不是所有软件都如此,比如scikit-learn这样的开源工具。在scikit-learn中,部分模型会有用于标准化的参数选项(比如各种降维),另一些则需要手动进行标准化(例如逻辑回归、SVM等)。此外,并不是所有模型都需要预先进行数据标准化(比如决策树),而即便是同样的模型,运用在不同业务问题上也会有不同的标准化要求,这就要求我们对业务与模型都要深入了解才行。

18.2.9规范化(Normalization)

标准化方法应用于列,规范化或称归一化一般应用于行。规范化是为了数据样本向量在做点乘运算或其他核函数计算相似性时,拥有统一的标准。如果将表中的每一行作为向量来看,规范化就是将其缩放成单位向量。

对于有n条记录m个字段的数据集,使用x代表行记录形成的m维向量,使用L2范数(这里L2范数就是欧几里德距离)进行规范化的公式为:

i=1,2,…,n

scikit-learn当中可以使用preprocessing.Normalizer对每一行进行规范化,仅仅是为了演示代码,我们将通信账单数据集的后三列提取出来,使用规范化方法进行转换如下:

可以发现转换后每个行向量都是单位长度,只是具有不同的角度。需要再次强调的是,本例仅为演示代码,因为对行执行规范化后,每列的分布形态会发生改变,因此不适用于本数据集。

18.3 特征构造(Feature Construction)

特征构造指的是从原始数据构造新特征的处理过程,一般需要根据业务分析,生成能更好体现业务特性的新特征,这些新特征要与目标关系紧密,能提升模型表现或更好地解释模型。

特征构造需要花费大量的时间思考业务逻辑与探索业务数据,因此,往往需要手工创建。由于业务的多样性,以及人们认知业务逻辑的差异性,因此特征构造也相应地具备灵活性与艺术性的特点。如果说数据分析是科学与艺术的有机结合,那么其中的“艺术”便在特征构造上得以集中体现。

我们为上书中的用户账单构造新的特征,目的是为了预测用户是否会流失(churn),思路及需要生成的新特征如下:

(1)用户入网时长(duration:用户的入网时长(join_time)是时间类型的,在分类模型中无法直接使用,可以将它变换为入网时长。一般来说,入网时长越短的用户越有可能频繁换卡(即便换的是同一家服务商,服务商仍然要为此付出成本),流失可能性高。随着入网时长的增加,用户更换手机卡的成本越高(主要是社会成本),因此流失的可能性会越低。同时受到终端合约(指用户承诺在网一定时间,一般是两年,运营商对用户购买终端的费用进行补贴)的影响,入网两年左右的用户离网率会出现一个高峰。因此入网时长可能会在模型当中起到重要作用;

(2)实际流量单价(unit_price:由于套餐资费差异与消费行为的差异,实际上每个用户的实际流量单价是不同的。部分用户会将套餐内流量消费完,有些则用不完,所以套餐内标注的流量单价或超套餐后的流量单价不能代表用户消费的实际流量单价。实际流量单价高意味着用户超套餐消费很多(因为套餐外消费单价高)或者实际消费远低于套餐量(因为套餐本身与其消费习惯不符)。由此可见,实际流量单价高的用户更有理由转网或转套餐。事实上,完全可以将“实际流量单价”与“套餐内消费比(指用户消费量与套餐内给定量的比)”这两个特征都构造出来,并在后续的特征筛选中进行选择;

(3)TAC:手机串号IMEI的前6位被称为TAC号,指示了终端的品牌类型,可以通过连接外部数据将终端的价格、制式连接进来,这些指标对于用户的流失有可能具有一定的价值,因此需要进行一定的转换构造该特征。另外,IMEI本不在账单中,可以通过其他数据源引入。

根据上述分析,对数据集操作如下:

本例中计算是简化处理,鉴于0流量与0消费额的用户存在,应当定义更复杂的运算逻辑提升健壮性。

从处理方法上看,特征构造仍然是对数据的变换,其目的在于将业务专家的经验和智慧融入到分析中。而在数据预处理当中也需要做一些基本的数据变换,但预处理当中的数据变换更多是为了满足模型对训练数据类型、格式的基本要求。

特征工程的指导依据是本专业领域的方法论。比如构个人建信用评分,专家会关心个人基本信息、个人的社会经济状态、信用历史等信息,而业务人员也会关系客户的交易信息,因此这个模型的变量构造如下:

上图也只是对变量维度进行了示例,实际建模不会将这些变量直接放入模型,比如“账户余额”代表客户的负债能力,“贷款金额”代表其负债负担。将“账户余额除以贷款金额”放入模型比直接把两个原始变量放入模型的解释力要好。

特征构造没有一个统一的标准,要深入每个专业的业务领域。比如对公司盈利能力建模,可以借鉴杜邦分析、财务比率分析的已有模型;对个人进行精准营销建模,可以参考微观经济学和计量经济学中的效应模型。特征工程庞杂而艰难,非数据挖掘工程师的业务领域,要在这方面有所建树,需要把自己培养成业务专家。

18.4 特征抽取(Feature Extraction)

特征抽取指的是从原始数据抽取新特征,这种“抽取”可以使用一定的算法来自动执行,抽取的目的是将多维的或相关的特征降低到低维,以提取主要信息或生成与目标相关性更高的信息。

一般的结构化商业数据已经可以包括成百上千个维度的特征,而对于图像、音频和文本这些非结构化数据,通过转换可以很轻易地包括数以百万计的属性,这会导致计算量大,训练时间长。另外,常用的模型不能接受如此之多的特征,尤其在大量特征互相关联的情况下,这会影响模型的泛化能力。因此,特征提取自动减少这些类型到一个更小的集,达到可以建模的维数。

对于结构化数据,常用的特征提取方法包括投影方法(如主成分分析法、线性判别分析等)、无监督聚类等。

(1)主成分分析法(PCA,即Principal Component Analysis)

当随机变量两两之间具有较强的线性相关时,它们包含了比较多的共同信息,如果将共有的信息提取出来,而不损失过多原变量的信息,则可以达到简化问题的目的。

主成分分析法认为,数据变异最大的方向上包含了最大的信息。基于这样的认识,主成分分析法寻找多维数据当中变异最大的且正交的几个方向(通常要小于原始特征的维数),将特征投影到这几个方向形成的空间当中,这样可以保留数据的多数变异,而将变异较小的剩余方向忽略,并以投影后的数据作为新特征,如图18-3所示。

图18-3

图中的二维数据在某个方向上有最大的变异,因此将其投影到该方向上,以投影的坐标点作为新特征的值,这样就将二维的数据降到了一维,同时忽略掉了数据剩余的变异,从而达到抽取共同信息的目的。

在多维数据当中也是如此,先寻找数据变异最大的方向,然后在该方向的所有正交方向上寻找剩余变异最大的方向,以此类推,这些“方向”被称为主成分,以向量的形式表示。然后按照各主成分方向可解释的数据变异的多少来决定要保留几个主成分,最后将数据投影到主成分上形成新的得分(坐标值),该值就是新的特征值。例如对于经过数据预处理后的消费额、通话时长、流量三个特征,使用主成分法提取三个主成分的示例如下:

PCA被初始化为保留两个主成分(n_components=2),然后用生成的训练器训练(fit)并转换(transform)数据集,最后得到两组新的主成分得分,就是降维后的新特征。其中用于训练的数据集和要转换的数据集是同一个集合时,可以使用fit_transform方法一次性进行变换。

每个主成分能解释的数据变异比率如下:

可见第一个主成分方向能解释原始数据85.8%的变异,第二个主成分能解释9.2%的变异,两个主成分共同解释了原始数据95%的变异,相当于仅丢弃了5%的“信息”就将数据从三维降到了两维。这可以看做是将三维空间中分布的数据,投影到二维平面当中形成的新特征。

选择保留多少个主成分取决于这些主成分能解释的数据变异比率是否达到设计标准(例如80%),并且任意一个单独主成分能解释的变异量不低于标准值1,当然这两个选择标准可以根据业务需要适度放宽松。查看主成分解释的变异量的示例如下:

可见第二个主成分能解释的变异量低于1,因此可以判断保留一个主成分就可以了(只保留第一个新特征,或者将n_components设为1重新进行计算),这样可以将数据降到一维,并且保留85.8%的数据变异。

用于投影的方向如下:

第一个主成分向量与记录行做点积(投影)就可以得到第一个主成分的得分(新特征当中的第一列),第二个主成分向量与记录行做点积(投影)可以得到第二个主成分得分(新特征当中的第二列)。更简便的方法是用模型的transform方法来转换,比如对于有两条记录的测试集进行pca转换如下:

此外,一个方向可以由无穷多个向量来表示(只是长度不同),因此不同工具采用不同算法得出来的主成分向量可能在长度上是有差异的,导致投影结果也有差异,它们之间其实仅相差一个伸缩的比例而已。

关于主成分分析的更多详细算法可见前面章节。

(2)线性判别分析(LDA,即Linear Discriminant Analysis)

线性判别分析也称Fisher线性判别(FLD,即Fisher Linear Discriminant),它通过将数据投影到具有最佳分类性能的方向,而达到降维的目的。线性判别分析是一类有监督的学习方法。

以图18-4为例,空心点代表正例,实心点代表负例,使正负例能够尽量分离的投影方向,就是线性判别分析要寻找的方向,在这个方向上的投影坐标就是实例的新特征。

图18-4

由图18-4可见,线性判别分析适用于数据(近似)线性可分的情况,如果数据不是线性可分的,则可以应用核方法(kernel)进行处理。

本节仅讨论数据是(近似)线性可分,且目标变量为二分类的情况。

假设原始的多维特征用向量表示,目标变量是二分类的,每类的样本量分别是N1和N2,具有最佳分类性能的投影方向为,投影后的特征是y,则每个分类下的原始特征的中心分别为:

投影后各类的中心分别为:

投影后各类的方差分别为

为了使得投影后的特征具有最佳分类性能,需要使投影后的样本在新的子空间有尽量大的类间差异和尽可能小的类内差异,也就是要尽可能最大化目标函数,能够使该函数达到最大值的WT就是我们要求的投影方向。

线性判别分析的特点在于计算简便,适用多分类,在实践中被证明是有效的,而且没有超参(这里超参是指未知参数的参数)。当目标变量为二分类时,线性判别分析一般将数据投影到一维,示例如下:

目标变量设定为用户是否流失"churn",原始特征是之前经过变换了的消费额、通话时长、流量,数据被降到了一维。如果要预测分类结果,只需对新特征设定一个阈值,阈值两边的数据点分别为正例和负例就可以了。

用于投影的方向WT如下:

对于新的测试集,可以使用WT与其进行点积就可求得新特征值。更简便的方法是使用模型直接进行转换,例如:

这个结果与使用WT进行点积运算的结果相差一个缩放的系数而已。

从LDA的原来来看,显然除了降维,它也可以直接用于预测,例如:

由结果可见,预测的正确率为78.8%。

从计算公式来看,线性判别分析是强依赖于均值的,如果分类间的均值相差不大,或者均值不能很好地代表各类的概率分布,则效果一般。因此线性判别分析比较适合高斯分布的分类。

线性判别分析和主成分分析有很多的相似点,其本质是要将原始的样本映射到维度更低的样本空间中,但是线性判别分析和主成分分析的映射目标不一样:主成分分析是为了让映射后的数据具有最大的发散性,而线性判别分析是为了让映射后的数据有最好的分类性能;主成分分析是一种无监督的降维方法,而线性判别分析是一种有监督的降维方法。

18.5 特征选择(Feature Selection)

数据集当中的特征并不都是“平等”的,许多与问题无关的特征需要被移除掉,而有些特征则对模型表现影响很大,应当被保留,因此需要对特征进行一定的选择。

特征选择方法可以分为过滤法、包装法和集成法。

18.5.1过滤法(Filter)

过滤法是比较简单的特征选择方法,根据每个特征的统计特性,或者特征与目标值的关联程度进行排序,去掉那些未达到设定阈值的特征。常用的过滤法包括方差过滤、基于统计相关性的过滤,以及基于互信息的过滤等。

(1)方差过滤

方差过滤认为,数据的信息取决于其变异程度的大小。例如对于“太阳是否从东方升起”这一字段,其所有的取值为“是”,数据无任何变异(对应变量值的方差为0),这样的数据是没有信息量的;而对于“太阳黑子活动强度”这样的字段,其数据变异相对较大,这其中可能包含了人们想了解的信息,而且数据变异越大,其包含的信息越多,越应当被保留下来。

对于只有两个分类水平的伯努利(Bernoulli)变量,如果其中一类非常少(对应概率P较大),则该变量几乎全为另一类值,那么这个变量所包含的信息量就比较低。例如“vip”字段,因为几乎全部为0,vip用户对于模型几乎没有影响,因此不应入选。

假如设定概率的阈值为10%,(两个分类水平都要超过10%)才达到入选模型的要求,则对应的方差为p(1-p)=0.09,可以使用下面的示例进行筛选:

 

可见"vip"这一字段被筛掉,保留了"has_IMEI"。计算出的各变量方差为:

(2)基于统计相关性的过滤

当关注目标变量时,特征筛选会更有目的性,因此可以通过观察各个特征与目标变量的关联程度来进行特征选择,根据不同的目标变量与待选特征(连续变量或者离散变量),会用到不同的相关性分析方法,因此,先将待选特征与目标变量按照类型进行划分,示例如下:

其中后缀categorical代表这是离散变量,continuous代表连续变量。

①卡方检验

xy均为离散变量时,可以使用卡方检验判断它们的关联性。例如要对数据集做分类预测,y为离散变量,则可以进行如下操作:

返回的列表表示x的每个离散变量与y离散变量卡方检验得到的卡方值和P值。

在选择的"vip","state","has_IMEI"三个特征当中,"vip"的卡方检验统计量为8.15,对应p值为0.0043,其它两个变量的P值更小,几乎可以排除每一个特征“与目标变量互相独立”的这种可能性,因此可以全部予以保留。如果非要在当中筛掉一个“最不独立”的,也可以使用feature_selection模块当中的SelectKBest,示例如下:

想要了解删除的特征是什么,可以如下操作:

返回的数组对应特征的掩码,即删除了第一个特征(对应False),保留了后两个特征(对应True)

②ANOVA

当目标变量是离散的,而自变量是连续变量时,可以使用t检验或ANOVA方差分析进行变量筛选,例如对于分类问题,示例如下:

代码返回了二维数组,第一行代表t统计量,第二行代表对应的p值。

可以发现最后一个变量"duration"的p值只有0.0077,因此它与目标变量不独立,而其它变量的p值更小,因此可以判断它们与目标变量是各自都是相关的。

如果非要选择p值最低的变量保留下来,可以如下操作:

③相关系数

对于x和y均为连续变量的,可以使用相关系数来判断它们之间关联程度的大小,例如我们对数据做回归分析,使用消费量和在网时长估计消费额,则可以用如下方式进行变量筛选:

返回的二维数组中,第一行是用于检验相关系数的统计量(非相关系数本身),第二行是对应的P值。

因为在自变量中保留了消费额,它与自己毫无疑问是完全相关的,相关系数显著性检验P值最大,为1,其余都很小。这里面虽然x中包括了消费额,但并不影响方法的使用,因为利用统计相关性进行筛选是针对一个个自变量单独进行的。

同样的,删选变量可以使用SelectKbest:

如果要查看保留的是哪些特征,可以使用get_support(与①卡方检验一样)。

 

 

18.5.2包装法(Wrapper)

包装法与过滤法不同,过滤法通过一个个的判断x的统计特性,或者与y的关联性进行筛选,这样做可能会错过那些多个变量在一起才能显出与目标相关性的特征。比如,用户的通话时长与消费额可能没有显著关联,流量消耗也可能与消费额没有显著关联,但它们合起来能很好地将消费额预测出来。

因此,包装法不单看x与y的直接关联,而是从添加这个特征后模型的最终表现好坏来判断添加是否合适。这个方法可以包括前向或后向的特征选择,例如前向法的基本逻辑是这样的:先初始化进入模型的特征为空,然后一次向模型当中添加一个特征,并判断模型效果的提升程度,选择最能提升效果的特征进入模型;依此往复,直到所有剩余特征进入模型都无法带来效果提升为止。后向法则相反,先将特征一次性全部进入模型,再一个一个删去,通过判断哪个特征对模型效果的降低影响最小,则删去;依此往复,直到达到停止条件为止。前向后向法(逐步法)则是对前两种方法的融合,先逐个添加特征直到停止条件,再从中逐个删除达到停止条件,反复应用前向法与后向法,以达到对特征选择的尽可能优化。另外,可以对所有特征取其全部子集,每个子集都进入模型去判断哪个组合效果最佳。但这样的穷举开销很大,虽然它能产生最优的组合,但只能在特征较少时使用。

在feature_selection中,使用RFE(recursive feature elimination)可以用于“迭代的特征消除”,即后向法筛选特征。方法示意如下:

其中,需要指定特征进入的模型(本例中是逻辑回归),还需要指定要保留的特征数"n_features_to_select",该参数默认为None,使用默认值时会保留一半的特征。其它可调参数可参见文档。

同理,要获得删除特征的掩码,可以如下操作:

18.5.3集成法(Embedded)

有一些模型算法天然就具备判断一个特征对于模型的效果影响程度,例如决策树类的模型,在模型训练过程中就需要融合特征选择的功能,因此,如果能通过这类模型计算出一个综合的特征重要性排序,则可以用于特征的选择。这样的方法被称为集成法。

例如使用随机森林进行建模,模型会通过计算,返回一个代表特征重要性的数组,示例如下:

在结果中,选择重要性高的的特征进入模型就可以了。当然,如果特征比较多,可以采用一些方便手段,例如使用SelectFromModel,示例如下:

SelectFromModel需要传入一个可以产生特征重要性的模型,以及特征筛选的阈值,同样也能够产生特征选择的掩码,示例如下:

使用随机森林这类树模型,计算效率较高,可以在建模初期对变量进行初步筛选,之后往往还需要手动进一步的细筛特征,因此可以适当多保留一些特征,集成法的主要作用就是为了在初期减少人工工作量。

 

推荐 2
本文由 ben chang 创作,采用 知识共享署名-相同方式共享 3.0 中国大陆许可协议 进行许可。
转载、引用前需联系作者,并署名作者且注明文章出处。
本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责。本站是一个个人学习交流的平台,并不用于任何商业目的,如果有任何问题,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

0 个评论

要回复文章请先登录注册