特征工程是一个面向十分广的概念,只要是在处理数据就可以认为是在做特征工程。个人理解,真正意义上的特征工程还是数据降维和数据升维的过程。
而前期对数据的处理过程:
将这三步归纳到数据准备的过程。
1、需要哪些数据?根据领导提出的一个分析要求,我们需要构思自己需要哪些数据,这个时候我们要和企业中的运维人员进行沟通,看看运维人员能够提供哪些数据。
2、数据如何存储?一般如果不是那种需要一个人顶十个人的超级创业公司,像数据存储的工作一般不会交给我们去完成。
3、数据如何清洗?对我们来说,数据准备过程中最关键的第一步操作是如何对数据进行清洗。比如数据库中存的是作者和文章所在的url地址。我们可以对作者进行哑编码的操作,对url地址返回的文章进行词袋法的处理,等等。
4、数据特征工程。数据特征工程和数据清洗有时候概念的边界比较模糊。有些在数据清理过程中也会遇到特征选择的问题,但很多时候数据本身特征就少,你再做个特征选择就更加欠拟合了。总的来说实际情况实际分析。
在进行机器学习之前,收集数据的过程中,我们主要按照以下规则找出我们所需要的数据:
基于对业务规则的理解,尽可能多的找出对因变量有影响的所有自变量数据。
在获取数据的过程中,首先需要考虑的是这个数据获取的成本;
获取得到的数据,在使用之前,需要考虑一下这个数据是否覆盖了所有情况以及这个数据的可信度情况。
PS:入职第一周可能就是让你拿数据,熟悉数据来源,这样后续工作也好上手。
一般情况下,用于后期模型创建的数据都是存在在本地磁盘、关系型数据库或者一些相关的分布式数据存储平台的。
数据清洗(data cleaning)是在机器学习过程中一个不可缺少的环节,其数据的清洗结果直接关系到模型效果以及最终的结论。在实际的工作中,数据清洗通常占开发过程的50%-80%左右的时间。推荐一本书
数据清洗过程:
在数据预处理过程主要考虑两个方面,如下:
注意:建模和做特征之前,要先了解字段含义,来源,再用data.describe()
抽取一部分数据通过人工查看,进一步分析。
作为研究者,我们不能为了完成任务而完成任务,至少在数据层面上必须要严谨,做出来的模型必须要起到作用。很多时候,客户要求模型的准确率达到98%,不过也许我们能给出的模型一般在91%~92%,但是必须保证我们分析的数据是绝对有用的,有解释意义的。这样的模型也许在指标上没有完成客户的需求,但模型也是有价值的。
反之有些分析师为了获得更好的模型评分,强行加入一些没有意义的奇怪数据,这样一来在当前的数据集中能够跑出很好的评分,到了实际运用中结果会很差。这就是过拟合。
一般情况下,数据是由用户/访客产生的,也就有很大的可能性存在格式和内容上不一致的情况,所以在进行模型构建之前需要先进行数据的格式内容清洗操作。格式内容问题主要有以下几类:
主要是通过简单的逻辑推理发现数据中的问题数据,防止分析结果走偏,主要包含以下几个步骤:
一般情况下,我们会尽可能多的收集数据,但是不是所有的字段数据都是可以应用到模型构建过程的,也不是说将所有的字段属性都放到构建模型中,最终模型的效果就一定会好,实际上来讲,字段属性越多,模型的构建就会越慢,所以有时候可以考虑将不要的字段进行删除操作。在进行该过程的时候,要注意备份原始数据。
如果数据有多个来源,那么有必要进行关联性验证,该过程常应用到多数据源合并的过程中,通过验证数据之间的关联性来选择比较正确的特征属性,比如:汽车的线下购买信息和电话客服问卷信息,两者之间可以通过姓名和手机号进行关联操作,匹配两者之间的车辆信息是否是同一辆,如果不是,那么就需要进行数据调整。
PS:工作中可能遇到的最大的问题是数据不均衡。
怎么去解决的? 上采样、下采样、SMOTE算法。
解决的效果如何? 有一点点改进,但不是很大。
事实上确实如此,很多时候即使用了上述算法对采样的数据进行改进,但是结果反而可能更差。在业界中,对数据不均衡问题的处理确实是一件比较头疼的问题。最好的处理方法还是:尽可能去获得更多的那些类别比较少的数据。
在实际应用中,数据往往分布得非常不均匀,也就是会出现“长尾现象”,即:绝大多数的数据在一个范围/属于一个类别,而在另外一个范围或者另外一个类别中,只有很少的一部分数据。那么这个时候直接使用机器学习可能效果会不太少,所以这个时候需要我们进行一系列的转换操作。
而在采样过程中修改样本的权重,一般做的比较少。
设置损失函数的权重,使得少数类别数据判断错误的损失大于多数类别数据判断错误的损失,即当我们的少数类别数据预测错误的时候,会产生一个比较大的损失值,从而导致模型参数往让少数类别数据预测准确的方向偏。可以通过scikit-learn中的class_weight参数来设置权重。
下采样/欠采样(under sampling):从多数类中随机抽取样本从而减少多数类别样本数据,使数据达到平衡的方式。
PS:比如本来样本正负例的比例是100:1,一般使用下采样将数据比例控制到4:1就是极限了。如果强行将正负例的比例控制到1:1,会损失很多样本的特性,使得模型效果还不如100:1的训练结果。
集成下采样/欠采样:采用普通的下采样方式会导致信息丢失,所以一般采用集成学习和下采样结合的方式来解决这个问题;主要有两种方式:
1、EasyEnsemble
采用不放回的数据抽取方式抽取多数类别样本数据,然后将抽取出来的数据和少数类别数据组合训练一个模型;多次进行这样的操作,从而构建多个模型,然后使用多个模型共同决策/预测。
2、BalanceCascade
利用Boosting这种增量思想来训练模型;先通过下采样产生训练集,然后使用Adaboost算法训练一个分类器;然后使用该分类器多对所有的大众样本数据进行预测,并将预测正确的样本从大众样本数据中删除;重复迭代上述两个操作,直到大众样本数据量等于小众样本数据量。
扩展一个技巧:
如果参加一个比赛,我们会在模型训练的时候将数据分成训练集和开发集。模型提交后,比赛方会提供测试集对结果进行预测。
一般来说我们训练集上的模型评分会在86 ~ 88%左右,开发集上的评分为82 ~ 84%,但是到了实际的测试集上,模型评分可能只有72%左右。
技巧来了:
1、一般来说测试集的数据是不带标签的,但是测试集依然有特征X。
2、我们都不考虑训练集和测试集的目标Y,人为创建一列目标值Z,将训练集中的Z都设为0,将测试集的目标Z都设为1。
3、寻找测试集的X和Z之间的映射。
4、根据这个X和Z之间的映射,使用训练集中的X预测Z,结果肯定是组0,1向量。
5、将预测值为1的数据提出来,作为我的开发集(用来验证我们模型的数据集合),剩下预测为0的数据作为训练集。在这个基础上对我的训练数据进行调优。
这是一个在不做任何特征的情况下对模型调优的一个技巧,一般可以将模型在真实环境中的评分提高一点点。大概72%提高到74%左右。
为什么?实际上我们做训练的目的是为了找一找比赛中人家提供给我们的训练数据和真实数据,哪些长得比较像。将更像真实测试数据的样本放到开发集中作为调参的标准,从而能够提高最终的评分。虽然没有什么科学依据,但是确实比较有效,不登大雅之堂。
Edited Nearest Neighbor(ENN): 对于多数类别样本数据而言,如果这个样本的大部分k近邻样本都和自身类别不一样,那我们就将其删除,然后使用删除后的数据训练模型。
Repeated Edited Nearest Neighbor(RENN): 对于多数类别样本数据而言,如果这个样本的大部分k近邻样本都和自身类别不一样,那我们就将其删除;重复性的进行上述的删除操作,直到数据集无法再被删除后,使用此时的数据集据训练模型。
Tomek Link Removal: 如果两个不同类别的样本,它们的最近邻都是对方,也就是A的最近邻是B,B的最近邻也是A,那么A、B就是Tomek Link。将所有Tomek Link中多数类别的样本删除。然后使用删除后的样本来训练模型。
上面是对多数样本进行删除,下面是对少数样本进行增加
过采样/上采样(Over Sampling):和欠采样采用同样的原理,通过抽样来增加少数样本的数目,从而达到数据平衡的目的。一种简单的方式就是通过有放回抽样,不断的从少数类别样本数据中抽取样本,然后使用抽取样本+原始数据组成训练数据集来训练模型;不过该方式比较容易导致过拟合一般抽样样本不要超过50%。
过采样/上采样(Over Sampling):因为在上采样过程中,是进行是随机有放回的抽样,所以最终模型中,数据其实是相当于存在一定的重复数据,为了防止这个重复数据导致的问题,我们可以加入一定的随机性,也就是说:在抽取数据后,对数据的各个维度可以进行随机的小范围变动,eg: (1,2,3) --> (1.01, 1.99, 3);通过该方式可以相对比较容易的降低上采样导致的过拟合问题。
采用数据合成的方式生成更多的样本,该方式在小数据集场景下具有比较成功的案例。常见算法是SMOTE算法,该算法利用小众样本在特征空间的相似性来生成新样本。
比如:给少数样本编号,1~100;将1、2样本连起来,取他们的中点(期望),作为一个新的样本。以此类推,最后可以新生成50个样本。用这种算法一次可以提高50%的样本量。
对于正负样本极不平衡的情况下,其实可以换一种思路/角度来看待这个问题:可以将其看成一分类(One Class Learning)或者异常检测(Novelty Detection)问题,在这类算法应用中主要就是对于其中一个类别进行建模,然后对所有不属于这个类别特征的数据就认为是异常数据,经典算法包括:One Class SVM、IsolationForest等。
特征转换主要指将原始数据中的字段数据进行转换操作,从而得到适合进行算法模型构建的输入数据(数值型数据),在这个过程中主要包括但不限于以下几种数据的处理:
机器学习的模型算法均要求输入的数据必须是数值型的,所以对于文本类型的特征属性,需要进行文本数据转换,也就是需要将文本数据转换为数值型数据。常用方式如下:
词袋法(Bag of words,BOW)是最早应用于NLP和IR领域的一种文本处理模型,该模型忽略文本的语法和语序,用一组无序的单词(words)来表达一段文字或者一个文档,词袋法中使用单词在文档中出现的次数(频数)来表示文档。
词集法(Set of words,SOW)是词袋法的一种变种,应用的比较多,和词袋法的原理一样,是以文档中的单词来表示文档的一种的模型,区别在于:词袋法使用的是单词的频数,而在词集法中使用的是单词是否出现,如果出现赋值为1,否则为0。
在词袋法或者词集法中,使用的是单词的词频或者是否存在来进行表示文档特征,但是不同的单词在不同文档中出现的次数不同,而且有些单词仅仅在某一些文档中出现(例如专业名称等等),也就是说不同单词对于文本而言具有不同的重要性,那么,如何评估一个单词对于一个文本的重要性呢?
单词的重要性随着它在文本中出现的次数成正比增加,也就是单词的出现次数越多,该单词对于文本的重要性就越高。同时单词的重要性会随着在语料库中出现的频率成反比下降,也就是单词在语料库中出现的频率越高,表示该单词与常见,也就是该单词对于文本的重要性越低。
TF-IDF(Item frequency-inverse document frequency)是一种常用的用于信息检索与数据挖掘的常用加权技术,TF的意思是词频(Item Frequency),IDF的意思是逆向文件频率(Inverse Document Frequency)。TF-IDF可以反映语料中单词对文档/文本的重要程度。
假设单词用t表示,文档用d表示,语料库用D表示,那么N(t,D)表示包含单词t的文档数量,|D|表示文档数量,|d|表示文档d中的所有单词数量。N(t,d)表示在文档d中单词t出现的次数。
TF-IDF除了使用默认的tf和idf公式外,tf和idf公式还可以使用一些扩展之后公式来进行指标的计算,常用的公式有:
有两个文档,单词统计如下,请分别计算各个单词在文档中的TF-IDF值以及这些文档使用单词表示的特征向量。
不管是前面的词袋法还是TF-IDF,都避免不了计算文档中单词的词频,当文档数量比较少、单词数量比较少的时候,我们的计算量不会太大,但是当这个数量上升到一定程度的时候,程序的计算效率就会降低下去,这个时候可以通过HashTF的形式来解决该问题。
HashTF的计算规则是:在计算过程中,不计算词频,而是计算单词进行hash后的hash值的数量(有的模型中可能存在正则化操作)。
HashTF的特点:运行速度快,但是无法获取高频词,有可能存在单词碰撞问题(hash值一样)。
在Scikit-learn中,对于文本数据主要提供了三种方式将文本数据转换为数值型的特征向量,同时提供了一种对TF-IDF公式改版的公式。所有的转换方式均位于模块:sklearn.feature_extraction.text。
名称 | 描述 |
CountVectorizer | 以词袋法的形式表示文档 |
HashingVectorizer | 以HashingTF的模型来表示文档的特征向量 |
TfidfVectorizer | 以TF-IDF的模型来表示文档的特征向量,等价于先做CountVectorizer,然后做TfidfTransformer转换操作的结果 |
TfidfTransformer | 使用改进的TF-IDF公式对文档的特征向量矩阵(数值型的)进行重计算的操作,TFIDF=TF*(IDF+1)。备注:该转换常应用到CountVectorizer或者HashingVectorizer之后 |
对于缺省的数据,在处理之前一定需要进行预处理操作,一般采用中位数、均值或者众数来进行填充,在Scikit-learn中主要通过Imputer类来实现对缺省值的填充。
哑编码(OneHotEncoder):对于定性的数据(也就是分类的数据),可以采用N位的状态寄存器来对N个状态进行编码,每个状态都有一个独立的寄存器位,并且在任意状态下只有一位有效。哑编码是一种常用的将特征数字化的方式。比如有一个特征属性:[‘male’,‘female’],那么male使用向量[1,0]表示,female使用[0,1]表示。
二值化(Binarizer):对于定量的数据根据给定的阈值,将其进行转换,如果大于阈值,那么赋值为1;否则赋值为0。
标准化:基于特征属性的数据(也就是特征矩阵的列),获取均值和方差,然后将特征值转换至服从标准正态分布。计算公式如下:
区间缩放法:是指按照数据的方差特性对数据进行缩放操作,将数据缩放到给定区间上,常用的计算方式如下。
归一化:和标准化不同,归一化是基于矩阵的行进行数据处理,其目的是将矩阵的行均转换为“单位向量”,l2规则转换公式如下:
有的书把区间缩放说为归一化,归一化说为正则化
标准化的目的是为了降低不同特征的不同范围的取值对于模型训练的影响;比如对于同一个特征,不同的样本的取值可能会相差的非常大,那么这个时候一些异常小或者异常大的数据可能会误导模型的正确率;另外如果数据在不同特征上的取值范围相差很大,那么也有可能导致最终训练出来的模型偏向于取值范围大的特征,特别是在使用梯度下降求解的算法中;通过改变数据的分布特征,具有以下两个好处:1. 提高迭代求解的收敛速度;2. 提高迭代求解的精度。
归一化 对于不同特征维度的伸缩变换的主要目的是为了使得不同维度度量之间特征具有可比性,同时不改变原始数据的分布(相同特性的特征转换后,还是具有相同特性)。和标准化一样,也属于一种无量纲化的操作方式。
正则化 则是通过范数规则来约束特征属性,通过正则化我们可以降低数据训练的模型的过拟合可能,和之前在机器学习中所讲述的L1、L2正则的效果一样。
备注:广义上来讲,标准化、区间缩放法、正则化都是具有类似的功能。在有一些书籍上,将标准化、区间缩放法统称为标准化,把正则化称为归一化操作。
PS:如果面试有人问标准化和归一化的区别:标准化会改变数据的分布情况,归一化不会,标准化的主要作用是提高迭代速度,降低不同维度之间影响权重不一致的问题。
多项式数据变换主要是指基于输入的特征数据按照既定的多项式规则构建更多的输出特征属性,比如输入特征属性为[a,b],当设置degree为2的时候,那么输出的多项式特征为
认为每个样本在决策树落在决策树的每个叶子上就表示属于一个类别,那么我们可以进行基于GBDT或者随机森林的维度扩展,经常我们会将其应用在GBDT将数据进行维度扩充,然后使用LR进行数据预测,这也是我们进行所说的GBDT+LR做预测。
先通过GBDT,将原来样本中的维度进行扩展,然后将新生成的特征放到逻辑回归或线性回归中进行模型构建。
当做完特征转换后,实际上可能会存在很多的特征属性,比如:多项式扩展转换、文本数据转换等等,但是太多的特征属性的存在可能会导致模型构建效率降低,同时模型的效果有可能会变的不好,那么这个时候就需要从这些特征属性中选择出影响最大的特征属性作为最后构建模型的特征属性列表。
在选择模型的过程中,通常从两方面来选择特征:
特征选择的方法主要有以下三种:
先计算各个特征属性的方差值,然后根据阈值,获取方差大于阈值的特征。
先计算各个特征属性对于目标值的相关系数以及相关系数的P值,然后获取大于阈值的特征属性。
检查定性自变量对定性因变量的相关性:
使用一个基模型来进行多轮训练,每轮训练后,消除若干权值系数的特征,再基于新的特征集进行下一轮训练。
在使用惩罚项的基模型,除了可以筛选出特征外,同时还可以进行降维操作。
树模型中GBDT在构建的过程会对特征属性进行权重的给定,所以GBDT也可以应用在基模型中进行特征选择。
当特征选择完成后,可以直接可以进行训练模型了,但是可能由于特征矩阵过大,导致计算量比较大,训练时间长的问题,因此降低特征矩阵维度也是必不可少的。常见的降维方法除了基于L1的惩罚模型外,还有主成分析法(PCA)和线性判别分析法(LDA),这两种方法的本质都是将原始数据映射到维度更低的样本空间中,但是采用的方式不同,PCA是为了让映射后的样本具有更大的发散性,LDA是为了让映射后的样本有最好的分类性能。
将高维的特征向量合并成为低维度的特征属性,是一种无监督的降维方法。
LDA是一种基于分类模型进行特征属性合并的操作,是一种有监督的降维方法。
缺省值是数据中最常见的一个问题,处理缺省值有很多方式,主要包括以下四个步骤进行缺省值处理:
1、确定缺省值范围。
2、去除不需要的字段。
3、填充缺省值内容。
4、重新获取数据。
注意:最重要的是缺省值内容填充。
在进行确定缺省值范围的时候,对每个字段都计算其缺失比例,然后按照缺失比例和字段重要性分别指定不同的策略。
在进行去除不需要的字段的时候,需要注意的是:删除操作最好不要直接操作与原始数据上,最好的是抽取部分数据进行删除字段后的模型构建,查看模型效果,如果效果不错,那么再到全量数据上进行删除字段操作。总而言之:该过程简单但是必须慎用,不过一般效果不错,删除一些丢失率高以及重要性低的数据可以降低模型的训练复杂度,同时又不会降低模型的效果。
填充缺省值内容是一个比较重要的过程,也是我们常用的一种缺省值解决方案,一般采用下面几种方式进行数据的填充:
1、以业务知识或经验推测填充缺省值。
2、以同一字段指标的计算结果(均值、中位数、众数等)填充缺省值。
3、以不同字段指标的计算结果来推测性的填充缺省值,比如通过身份证号码计算年龄、通过收货地址来推测家庭住址、通过访问的IP地址来推测家庭/公司/学校的家。
如果某些指标非常重要,但是缺失率有比较高,而且通过其它字段没法比较精准的计算出指标值的情况下,那么就需要和数据产生方(业务人员、数据收集人员等)沟通协商,是否可以通过其它的渠道获取相关的数据,也就是进行重新获取数据的操作。
对于缺省的数据,在处理之前一定需要进行预处理操作,一般采用中位数、均值或者众数来进行填充,在scikit中主要通过Imputer类来实现对缺省值的填充。
如m行n列 axis = 0 对第0个位置进行填充, 即对m进行填充,保持n不变。
按列进行填充
axis = 1 对第1个位置进行填充, 即对n进行填充,保持m不变。
按行进行填充
思考:按行填充好,还是按列填充好?
当然是按列填充好。 因为每列表示的都是属性,我要填充,自然需要和同一个属性内的各个值进行对比。如果按行填充,那么一个身高,一个体重的值,我们怎么分析都不知道该填充什么。
分词是指将文本数据转换为一个一个的单词,是NLP自然语言处理过程中的基础;因为对于文本信息来讲,我们可以认为文本中的单词可以体现文本的特征信息,所以在进行自然语言相关的机器学习的时候,第一操作就是需要将文本信息转换为单词序列,使用单词序列来表达文本的特征信息。
分词: 通过某种技术将连续的文本分隔成更具有语言语义学上意义的词。这个过程就叫做分词。
jieba:中文分词模块;
Python中汉字分词包:jieba
安装方式: pip install jieba
Github:https://github.com/fxsjy/jieba
https://www.jianshu.com/p/9332b04e06d3