-
2018-11-17 18:45:06
按照惯例,先附上业界那句名言“数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已”。特征工程是指用一系列工程化的方式从原始数据中筛选出更好的数据特征,以提升模型的训练效果。简而言之,就是为算法提供更友好的输入,以最大化地发挥算法的作用。同时,特征工程虽然是一种技术,但其前提是对数据以及产生这些数据的具体业务场景有比较深入的理解。一般的,python的sklearn、spark的mllib都包含了特征工程相关的方法,这里介绍spark的mllib中特征工程相关的方法,但具体实现请参考官方文档。
spark中特征工程在mllib 中Extracting, transforming and selecting features里,其分为Feature Extractors(特征提取)、Feature Transformers(特征转换)、Feature Selectors(特征选择)以及Locality Sensitive Hashing(即LSH局部敏感哈希)。其中特征提取是从原始数据中提取特征,特征转换是从已有的特征中构建更有用的特征,特征选择是从原特征中选择最有效的的一组子特征。局部敏感哈希是一种用于海量高维数据的近似最近邻快速查找技术,本博客不做介绍。
特征提取
spark中特征提取中包含四个方法:TF-IDF、Word2Vec、CountVectorizer以及FeatureHasher。其中,TF-IDF以及Word2Vec的使用比较广泛,这里不详细展开,TF-IDF主要用于提取文档的关键词,而Word2Vec将词语或者文章转换为词向量,通过空间距离表示文档的相似度,距离越近则越相似,其中一篇文章的词向量是文章所有词语词向量的平均值,所以使用Word2Vec尽量使用关键词转换词向量。CountVectorizer与TF相似,输出词频向量,但是CountVectorizer是可逆的,而TF是不可逆的,也就是说,CountVectorizer可以通过词频向量的索引找到对应的单词,而TF则不可以。所以在使用spark做关键词提取时,通常使用CountVectorizer和IDF,而如果只需要文档关键词的特征向量的话,则使用TF和IDF。详细地,可以参考博主spark关键词提取相关的博客:
https://blog.csdn.net/u013090676/article/details/81952235
最后FeatureHasher即特征哈希,是将原来的N维特征转换为一个M维的特征向量,一般的M小于N。并且原始的N维特征可以是不同的数据类型,比如数字、字符串、布尔值等,对于不同的数据类型,FeatureHasher会使用不同的方式进行处理。
特征转换
特征转换是spark特征工程的主要部分,其中也包含了最多的方法,这里不一一介绍,总的来说,可以分为以下几类:
- 文档转换
文档转换主要对文档内容数据进行转换。主要有Tokenizer、StopWordsRemover以及n-gram。其中Tokenizer用于分词,StopWordsRemover用于去掉停用词,但是这两个方法针对的是英文,如果要做中文分词以及去掉停用词的话,需要使用中文分词相关的方法,比如ansj。n-gram并不是n-gram算法,而是将分词好的字符串根据参数n的值转换成每组n个字符串的向量。例如,输入["I", "love", "Java"]经过n=2的转换后,输出["I love", "love java"]。
- 正则化
正则化将每个样本缩放到单位范数上,对于二次型(点积)或者其他核函数方法计算两个样本之间的相似性比较有用。spark中正则化使用的方法是Normalizer,Normalizer作用于每一行(所有特征),将一个行向量的范数转换为单位范数。需要参数p确定是p范数,默认p=2,就是模长(到原点的欧式距离)。特殊的,0范数为绝对值之和,无穷范数是向量的最大值。
- 标准化
标准化是在不改变原数据分布的前提下,将数据按比例缩放,使之落入一个限定的区间,使数据之间具有可比性。但当特征明显不遵从高斯正态分布时,标准化的效果比较差。简而言之,就是让不同量纲单位的数据具有可比性。spark中标准化使用StandardScaler。StandardScaler作用于每一列(每一个特征),将特征标准化为单位标准差或是0均值,或是0均值单位标准差。有两个参数:withStd默认为真,转换为单位标准差,withMean默认为假,为真时转为0均值。
- 归一化
归一化是对数据集进行区间缩放,缩放到单位区间内,把有单位的数据转化为没有单位的数据,即统一数据的衡量标准,消除单位的影响。spark中归一化的方法有MinMaxScaler与MaxAbsScaler,他们都作用于每一列。其中MinMaxScaler将每一维特征映射到[min,max]的区间,通常情况是[0,1]。而MaxAbsScaler通过除以每一维特征最大值的绝对值,将每一维特征映射到[-1,1]上。
- 二值化
二值化是将数值型特征转换为布尔类型特征,通过设定一个阈值,当特征值大于该阈值时为1 ,小于等于则为0。spark中二值化采用Binarizer。
- 离散化
离散化即是将连续变量进行离散化,一则部门算法要求输入为离散变量,第二对离散变量的处理效率更高,噪声的影响越低。Spark中离散化的方法有Bucketizer与QuantileDiscretize。其中Bucketizer是讲连续的数据放入已经设置好的n个桶中(即每个桶的上下边界人工设置好),而QuantileDiscretize根据设置的桶的数量对连续变量进行拆分,且它可以处理NaN值。另外,通常也可以使用聚类的方法来对连续变量进行离散化。
- 独热编码
独热编码(也可称为哑变量)将分类转换为二进制向量,该向量中只有一位为1,其他为0。独热编码一般在需要计算空间距离的算法中对原始的无先后顺序的分类数据进行处理。例如一般的系统中,通过1、2、3对红灯、绿灯和黄灯进行标识,但是红黄绿三种状态本来没有大小和顺序,如果不做处理,进行距离计算时,就会出现绿灯离红灯比黄灯离红灯更近的情况。所以通过独热编码将其转换为红灯[1,0,0]、黄灯[0,1,0]、绿灯[0,0,1],这样三个分类的空间距离也就相同了。独热编码使用OneHotEncoderEstimator,作用于列,可以作用于多个列,对一个特征的值进行独热编码。
- 缺失值补全
缺失值补全使用Imputer方法。Imputer作用于列,对列中的NaN使用中位数或者平均数进行补全。
- 特征构造
特征构造通过对已有特征进行拆分、组合构建出新的特征。特征构建的过程,需要对原始数据以及业务特征有一定的理解,例如在销量分析中,通常会把时间拆分成月份和日期,来分析哪些月份、每个月哪几天销量较高。Spark中特征构造的方法包括:PolynomialExpansion、Interaction、ElementwiseProduct和VectorAssembler。其中,PolynomialExpansion将特征进行多项式展开形成新的特征;Interaction将多列向量构造成一个向量,新向量的内容是每个输入列的每个值与其他列组合的乘机;ElementwiseProduct通过将向量列与一个标量列进行乘积来对向量进行缩放;VectorAssembler对输入列进行拼接生成一个新的向量。具体地,可以运行相关示例进行详细的了解。
- 降维
降维spark中目前博主只找到了PCA。但是一般情况下,数据降维有线性方法与非线性方法。线性方法比较常用的有PCA(主成分分析)与LDA(线性判别式 Linear Discriminant Analysis,注意与Latent Dirichlet Allocation区分开,在spark聚类中,它也叫LDA)。这里不对LDA与PCA进行详细的介绍,只是在应用中需要注意,PCA是为了去掉冗余的维度,使得降低了维度的数据之间的方差最大;而LDA则是为了降维的结果数据更容易分类。
特征选择
Spark中特征选择提供了三种方法:VectorSlicer、RFormula以及ChiSqSelector。其中VectorSlicer支持用户通过索引筛选某一个向量列中特定的下标的元素组成新的向量,同时也支持通过名称来筛选,但是比较麻烦;RFormula基于R模型,根据R公式生存标签列和特征列(和博主一样不熟悉R的同学就不用纠结这个了);ChiSqSelector通过卡方独立性检验来选择特征,最终选择与标签列分布最接近(也就是最相关)的特征。因此ChiSqSelector一般用于在特征比较多的时候,用来选取预测能力比较强的特征。
更多相关内容 -
机器学习之(四)特征工程以及特征选择的工程方法
2021-02-24 22:53:47为什么要做特征工程?应该如何做特征工程?这三个方面详细叙述。关于特征工程(FeatureEngineering),已经是很古老很常见的话题了,坊间常说:“数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已... -
《精通特征工程》示例代码.zip
2021-05-22 10:12:32《精通特征工程》 / 《Feature Engineering for Machine Learning》书中的示例代码 -
特征工程(机器学习)
2020-11-04 13:26:23一共30页PPT,何为特征工程呢?顾名思义,就是对原始数据进行一系列工程处理,将其提炼为特征,作为输入供算法和模型使用。 本质上讲,特征工程是一个表示和展现数据的过程;实际工作中,特征工程的目的是去除原始... -
Python数据处理与特征工程
2021-06-30 03:32:50【特征工程是什么?】 身高不同的两人,比较体重毫无意义, 但是如果将身高体重加以计算,转化成了BMI指数:BMI=体重/(身高^2) 通过比较这个新创造的特征值,谁胖谁瘦就一目了然了。 这就是特征工程,将原始数据... -
特征工程(sklearn)
2021-02-24 22:51:37通过总结和归纳,人们认为特征工程包括以下方面:特征处理是特征工程的核心部分,sklearn提供了较为完整的特征处理方法,包括数据预处理,特征选择,降维等。首次接触到sklearn,通常会被其丰富且方便的算法模型库... -
一文看懂特征工程在金融风控中的应用
2021-01-27 14:19:49本文来自于csdn,本文以特征工程在金融风控中的应用为切入点,对特征工程的大致内容进行了概述。在建模领域人们常说,数据和特征决定机器学习的上限,而模型和算法只是不断地逼近这个上限。所以,特征工程在建模中起... -
机器学习 特征工程 Python sklearn
2020-12-22 18:34:31机器学习 特征工程 Python sklearn 本博客代码:Github_GDUT-Rp 1 特征工程 数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已。 特征处理是特征工程的核心部分,sklearn提供了较为完整的特征处理... -
干货分享|详解特征工程与推荐系统及其实践
2021-02-24 20:04:33本文来自于网络,本文主要介绍了特征工程,以及一些常见的特征工程的方法,以及自然语言处理的特征工程,希望对您的学习有所帮助。协同过滤CollaborativeFiltering特征工程FeatureEngineering推荐系统实战注意点首先... -
Python-Featuretools自动特征工程开源框架
2019-08-11 06:46:11Featuretools 自动特征工程开源框架 -
论文研究-基于概率图模型的图像整体场景理解特征工程综述.pdf
2019-07-22 21:00:42针对这一特点,对图像理解特征工程中的特征提取、表示、学习和变换,基于概率图模型的图像整体场景理解特征工程研究价值和意义、典型特征工程等多方面进行了归纳与分析,重点介绍了四种代表性的基于概率图模型的图像... -
机器学习入门之特征工程.pdf
2020-01-20 10:43:20特征工程简介 • 什么是特征工程? • 为什么特征工程很重要? 特征工程处理方法 • 数据预处理的有效方法 • 如何获取重要特征? • 如何进行有效的特征选择? • 常用的降维方法 -
特征工程-特征选择思维导图
2018-08-03 14:50:25特征工程-特征选择思维导图:主要从常见搜索算法以及经典三刀来展示。这个是自己归纳的,有什么不对的,欢迎指出来 -
机器学习特征工程基本技术
2019-04-07 09:41:47机器学习特征工程基本技术,Fundamental Techniques of Feature Engineering for Machine Learning。 -
大数据分析之特征工程
2018-12-29 15:08:33数据建模和大数据分析过程中 特征工程直接影响了数据质量和模型结果,是大数据分析在数据采样后的重要一步 -
3_特征工程.ipynb
2020-01-10 22:57:303_特征工程.ipynb,房租预测 特征工程.ipynb,房租预测 特征工程.ipynb----------------------房租预测 -
机器学习:特征工程工作流程
2021-01-27 12:46:29特征工程是使用专业背景知识和技巧处理数据,使得特征能在机器学习算法上发挥更好的作用的过程。过程包含了特征提取、特征构建、特征选择等模块。 特征工程的目的是筛选出更好的特征,获取更好的训练数据。因为好的... -
机器学习概述、特征工程、机器学习算法
2018-11-29 10:49:10本文档主要讲解机器学习概述、特征工程、机器学习算法 -
特征工程
2020-06-26 20:14:04特征工程 什么是特征工程? 特征工程将原始数据转换为更好代表预测模型潜在问题的特征的过程 特征工程的意义? 特征工程的转换结果直接影响模型预测结果 特征工程怎么做? 特征抽取 特征预处理 特征降维 其他 ...特征工程
- 什么是特征工程?
定义:特征工程是将原始数据转换为更好代表数据潜在模型的特征的过程 - 特征工程的意义?
目的:更好的特征意味着更好的鲁棒性、更简单的模型、更好的结果 - 特征工程怎么做?
- 特征抽取
- 特征预处理
- 特征降维
- 其他
特征抽取
- 特征抽取的目的
将任意数据格式转换为机器可学习的数字特征 - 文本数据特征抽取
对文本数据可先进行分词,再以根据需求以下列方法进行(分词是以空格分隔每个词,对于英文文本无需分词,对于中文文本可用分词工具进行分词,如jieba)
- 计数
将文本中出现的所有词按出现次数统计,不统计单个字符
"""Count:sklearn实现""" from sklearn.feature_extraction.text import CountVectorizer content = ["life is short,i like python","life is too long,i dislike python"] vectorizer = CountVectorizer() print(vectorizer.fit_transform(content).toarray()) # fit_fransform()默认返回sparse形式存储的稀疏矩阵,通过toarray()可转换为正常ndarray格式
- 重要性度量
度量某个词对于文本的重要程度,该方法相对于计数方法而言,能够有效避免某些高频但实际含义的词对分类的干扰
"""Importance:sklearn实现""" from sklearn.feature_extraction.text import TfidfVectorizer content = ["life is short,i like python","life is too long,i dislike python"] vectorizer = TfidfVectorizer(stop_words='english') print(vectorizer.fit_transform(content).toarray()) # fit_fransform()默认返回sparse形式存储的稀疏矩阵,通过toarray()可转换为正常ndarray格式 print(vectorizer.vocabulary_)
- 类别(标签)数据特征抽取
- one-hot编码(独热编码)
以类别个数为长度构造二值向量,对于某个样本在属于类别的位置将元素值为1,其他位置为0
类别 one-hot编码 编码顺序 类别1,类别2,类别3,类别4 类别1 1,0,0,0 类别2 0,1,0,0 类别3 0,0,1,0 类别4 0,0,0,1 from sklearn.preprocessing import label_binarize y_true = np.array([np.random.choice([0,1,2]) for i in range(10)]) y_onehot = preprocessing.label_binarize(y_true,classes=[0,1,2]) y_true = np.array(["a","b","c","a","b","c","a","b"]) y_onehot = preprocessing.label_binarize(y_true,classes=["a","b","c"])
- 字典数据
对于字典数据,其中可转换为类别的数据以one-hot编码形式表示,对于数值型非类别数据不做处理
d = [{"city":"成都","age":20,"sex":"男"}, {"city":"上海","age":18,"sex":"女"}, {"city":"北京","age":25,"sex":"男"}] # 特征抽取结果 # 元素位置对应关系["age","city:成都","city:上海","city:北京","sex:男","sex:女"] [[ 20, 1, 0, 0, 1, 0], [ 18, 0, 1, 0, 0, 1], [ 25, 0, 0, 1, 1, 0]] """字典抽取:sklearn实现""" from sklearn.feature_extraction import DictVectorizer onehot = DictVectorizer() # 如果结果不用toarray,请开启sparse=False instances = [{'city': '北京','temperature':100},{'city': '上海','temperature':60}, {'city': '深圳','temperature':30}] X = onehot.fit_transform(instances).toarray() # fit_fransform()默认返回sparse形式存储的稀疏矩阵,通过toarray()可转换为正常ndarray格式 print(onehot.get_feature_names()) # 返回经onehot编码后对应的特征名顺序 print(X) # 返回字典抽取后的ndarray形式
特征预处理
- 特征预处理的目的
将数据转换为算法需求的数据格式 - 数值特征预处理
- 目的:对于归一化和标准化预处理是为了同等看待每一特征,有效降低因特征尺度不同对模型学习结果的影响
- 归一化(MaxMinScale)
特点:将原始数据映射到指定范围,只适用于传统精确小数据场景
计算公式:
优点:计算简单方便
缺点:对异常值极为敏感
"""MinMaxScaler:sklearn实现""" from sklearn.preprocessing import MinMaxScaler import numpy as np X = np.random.randint(0,10,25) X = X.reshape(5,5).astype(np.float64) mms = MinMaxScaler() X1 = mms.fit_transform(X) print(X) print(X1)
- 标准化(Normalize)
特点:将原始数据映射到均值为0,方差为1的标准正态分布
计算公式:
优点:在数据量较大时,对异常值不敏感,鲁棒性强,比较常用
缺点:数据量较小时,不能体现其优点,且需计算样本均值及标准差,计算量相比归一化计算量稍大
"""Normalize:sklearn实现""" from sklearn.preprocessing import StandardScaler import numpy as np X = np.random.randint(0,10,25) X = X.reshape(5,5).astype(np.float64) sds = StandardScaler() X1 = sds.fit_transform(X) print(X) print(X1)
- 缺失值处理
缺失值导致了数据的不完整,而无法直接使用数据,对缺失值一般情况可采用丢弃样本或缺失值填充两种方法。对于样本较多,只有个别缺失数据时,可采用丢弃缺失样本方法。其他情况均采用缺失值填充方式,填充值一盘采用统计特征,如均值,中位数等
特征降维
- 特征降维的目的
- 减少特征数量,提升算法学习效率,使模型泛化能力更强,减少过拟合
- 增强特征和特征值之间的理解
- 特征降维的方法
- 特征选择
原因:
(1)冗余-部分特征相关度高,易消耗计算资源
(2)噪声-部分特征对预测结果有负面影响
主要方式:
过滤式(filter):方差阈值(variance threshold)
(1)方差阈值方法,删除不满足阈值要求的特征
"""Variance Threshold:sklearn实现""" from sklearn.feature_selection import VarianceThreshold X = np.random.randn(5,7) vt = VarianceThreshold(threshold=0.5) # 实例化对象,设置方差阈值为0.5,方差小于等于0.5的特征将被删除 X1 = vt.fit_transform(X) # 对数据应用方差阈值 print(X) print(X1)
嵌入式(embedded):正则化、决策树
包裹式(wrapper):
其他:神经网络- 主成分分析(PCA)
主要用于削减特征数量,该方法保存数据中对方差影响最大的那些特征,极其容易受到特征尺度的影响,因此运用该方法前需进行特征标准化处理,确保每一特征的同等重要性。PCA算法内部采用SVD(奇异值分解)实现
"""PCA:sklearn实现""" import numpy as np from sklearn.decomposition import PCA X = np.random.randn(5,10) print(X) pca = PCA(n_components=0.9) """ PCA常用参数: n_components:int/float int表示降维后的特征个数,float表示原数据的保真度,一般取0.9-0.95.该参数一般取浮点值 whiten:bool (default False) 判断是否白化,白化就是对特征数据的归一化 svd_solver:PCA所选择的SVD降维算法,一般使用默认值 """ print(pca.fit(X)) # pca.fit(X)返回当前PCA配置信息 print(pca.fit_transform(X)) # pca.fit_transform(X) 返回降维后的数据 print(pca.explained_variance_ratio_) # pca.explained_variance_ratio_返回降维后数据每一特征的方差
- 其他:线性判别分析(LDA)
scikit-learn库
- 什么是特征工程?
-
极佳的特征工程教程——Understanding Feature Engineering
2018-03-13 16:07:47特征工程是机器学习建模最重要的一步,它直接决定了模型的效果。 近日数据科学家 Dipanjan Sarkar发表了几篇特征工程的深度好文,强烈推荐大家学习观摩! -
使用神经网络的自动化特征工程
2021-02-27 12:35:09特征工程是生成精确模型的最重要步骤之一。但是没有人喜欢它,因为这个步骤非常繁琐,我坚信任何繁琐的事情都可以自动化。虽然我的解决方案并没有完全消除对手工工作的需要,但它确实大大减少了手工工作,并产生了更...如何自动化并极大地改进数据建模中最繁琐的步骤之一
特征工程是生成精确模型的最重要步骤之一。但是没有人喜欢它,因为这个步骤非常繁琐,我坚信任何繁琐的事情都可以自动化。虽然我的解决方案并没有完全消除对手工工作的需要,但它确实大大减少了手工工作,并产生了更好的结果。它还生成了一个在结构化数据集上始终优于梯度增强方法的模型。
本文将包含以下内容:
- 到底什么被自动化了?
- 它是如何工作的?
- 你如何建立它?
- 和其他型号相比怎么样?
工程特性
正确地做工程特性可能需要数周的EDA。但幸运的是,神经网络擅长的是寻找相互作用关系!
当向模型提供一组特征时,它必须了解哪些特征相互作用,以及它们是如何相互作用的。有了庞大的数据集,可以有无数的组合进行测试,这些模型往往专注于提供快速成功的交互。通过特性工程,您可以手动创建或组合特性,以确保模型正确的关注这些特征。
根据数据和问题的不同,有许多不同类型的特征工程方法。大多数可分为以下几类:
数据清理:有些人认为这个特征工程,其实并不是这样,他应该有在训练中有着自己独立的地位。总结来说,这一步的目的是特性工程成为可能之前需要确保数据是可用的。它涉及修复数据中的错误、处理缺失值、处理异常值、独热编码、扩展特性以及无数其他事情。在我看来,数据清理是唯一一个比特征设计更糟糕的步骤,所以任何能找到自动化这一步的方法的人都将成为我的新的发现。
均值编码:这一步涉及到将分类特征(如邮政编码)转换为模型可用的信息。例如,可以创建一个列,显示邮政编码的分组后的平均销售收入。
滞后变量:在数据中添加时间序列元素通常会有帮助。通过添加以前时期的价值,模型可以计算出随着时间的推移事情的趋势(例如上个月的销售额,前月的销售额,等等)。这个过程并不太复杂,可以通过简单的循环实现自动化。
交互:这个步骤涉及到以不同的方式组合特征。例如,你可以通过将广告驱动的客户购买除以广告的总点击次数来衡量在线广告的转化率。但如果转化率因产品价格的不同而变化很大呢?现在可以根据价格阈值创建单独的列。我们也很难发现并知道如何处理三阶(或更高阶)互动(例如,广告转化率因价格和类别而异)。到目前为止,这是特性工程中最微妙和最耗时的步骤。正确地做这个步骤需要做数周的EDA。幸运的是,神经网络擅长的是寻找相互作用!诀窍是确保模型确实寻找它们,这将是以后的重点。
概念
神经网络采用一组输入特征,并在它们之间创建相互作用,以最好地预测输出。 如上所述,我们可以通过设计模型来强制模型考虑某些组合。 但如果我们能迫使神经网络去考虑它们吗?如果我们可以确保神经网络以对目标输出产生最佳准确性的方式来设计这些特征的话,应该该怎么办? 这里的关键就是是训练模型应以首先关注特征为主。
假设我们具有特征A,特征B,特征C和特征D,其目标输出为Y。此问题的第一步是创建一个预测每个特征的模型。 我们为什么要关心预测特征? 因为我们希望神经网络学习特定于每个特征的交互。
如果我们可以确保神经网络以对目标输出产生最佳精确的方式来设计这些功能,该怎么办?
确保特征网络以最终模型而不是单独的过程进行训练。 这里的技巧是训练嵌入到每个特征层的嵌入层。 对您来说是个好消息:经过几个月的努力,我得以开发出一种解决方案,并且它超出了我的期望。
代码
为了演示这些方法,我们将尝试预测COVID-19病人发生严重反应的可能性。 您可以在此处找到“ Cleaned-Data.csv”数据集:https://www.kaggle.com/iamhungundji/covid19-symptoms-checker?select=Cleaned-Data.csv
让我们提取数据并创建训练,验证和测试数据集:
import pandas as pd import tensorflow as tf from sklearn.model_selection import train_test_split from tensorflow import feature_column from tensorflow.keras import layers from tensorflow.keras.callbacks import ModelCheckpoint from sklearn.metrics import log_loss X_train = pd.read_csv('covid_data.csv') y_train = X_train.pop('Severity_Severe').to_frame() X_train = X_train.iloc[:,:23] X_train, X_val, y_train, y_val = train_test_split( X_train, y_train,test_size=0.2,random_state=42) X_val, X_test, y_val, y_test = train_test_split( X_val, y_val,test_size=0.5,random_state=42)
现在,我们将要定义我们要为其创建特征模型的特征。 由于我们没有很多特征,这里就全部使用它们(嵌入时将使用Country除外)。 当模型包含数百个特征时,首先应该确定义最重要的特征,例如下面代码:
model_cols = ['Fever','Tiredness','Dry-Cough', 'Difficulty-in-Breathing', 'Sore-Throat','None_Sympton', 'Pains','Nasal-Congestion', 'Runny-Nose','Diarrhea', 'None_Experiencing','Age_0-9', 'Age_10-19','Age_20-24','Age_25-59', 'Age_60_','Gender_Female','Gender_Male', 'Gender_Transgender','Contact_Dont-Know', 'Contact_No','Contact_Yes']
这些特征中的每一个都会与我们试图预测的目标目标(Severity_Severe)一起成为整体模型的不同辅助输出。 在创建TensorFlow数据集时,我们还必须将它们定义为输出特征。 请注意,我们通过在末尾添加“ _out”来重命名每个特征,以免TensorFlow被重复的名称弄糊涂。 请注意,我们还为目标输出添加了一个额外的“ _aux_out”列。 这样一来,我们就可以围绕目标特征训练一个单独的特征模型,该模型也将输入到最终模型中。 这是一个称为跳过连接的过程,该过程使模型可以学习围绕同一特征集的深层和浅层交互。
Y_train_df = X_train[model_cols].copy() Y_train_df.columns = Y_train_df.columns + "_out" Y_train_df['Severity_Severe_out'] = y_train['Severity_Severe'] Y_train_df['Severity_Severe_aux_out'] = y_train['Severity_Severe'] trainset = tf.data.Dataset.from_tensor_slices(( dict(X_train),dict(Y_train_df))).batch(256) Y_val_df = X_val[model_cols].copy() Y_val_df.columns = Y_val_df.columns + "_out" Y_val_df['Severity_Severe_out'] = y_val['Severity_Severe'] Y_val_df['Severity_Severe_aux_out'] = y_val['Severity_Severe'] valset = tf.data.Dataset.from_tensor_slices(( dict(X_val),dict(Y_val_df))).batch(256) Y_test_df = X_test[model_cols].copy() Y_test_df.columns = Y_test_df.columns + "_out" Y_test_df['Severity_Severe_out'] = y_test['Severity_Severe'] Y_val_df['Severity_Severe_aux_out'] = y_val['Severity_Severe'] testset = tf.data.Dataset.from_tensor_slices(( dict(X_test),dict(Y_test_df))).batch(256)
我们要创建的第一个函数是add_model。 我们将向此函数提供特征名称,定义层的数量和大小,表示是否要使用批量归一化,定义模型的名称并选择输出激活。 hidden_layers变量将为每个层有一个单独的列表,第一个数字是神经元的数量,第二个数字是dropout比例。 该函数的输出将是输出层和最终的隐藏层(特征工程),这些层将作为最终模型的基础。 使用诸如hyperopt之类的工具时,此功能允许轻松进行超参数调整。
def add_model( feature_outputs=None,hidden_layers=[[512,0],[64,0]], batch_norm=False,model_name=None,activation='sigmoid'): if batch_norm == True: layer = layers.BatchNormalization()(feature_outputs) else: layer = feature_outputs for i in range(len(hidden_layers)): layer = layers.Dense(hidden_layers[i][0], activation='relu', name=model_name+'_L'+str(i))(layer) last_layer = layer if batch_norm == True: layer = layers.BatchNormalization()(layer) if hidden_layers[i][1] > 0: layer = layers.Dropout(hidden_layers[i][1])(layer) output_layer = layers.Dense(1, activation=activation, name=model_name+'_out')(layer) return last_layer, output_layer
下一个功能是用于创建嵌入层。 因为“国家/地区”是稀疏的分类特征。 此函数将提取要转换为嵌入的特征的字典,以及在此定义的该特征的唯一可能值的列表:
emb_layers = {'Country':list(X_train['Country'].unique())}
我们还稍后将对模型输入进行定义。 对于Dimensions参数,我选择遵循默认特征规则。
def add_emb(emb_layers={},model_inputs={}): emb_inputs = {} emb_features = [] for key,value in emb_layers.items(): emb_inputs[key] = model_inputs[key] catg_col = feature_column .categorical_column_with_vocabulary_list(key, value) emb_col = feature_column.embedding_column( catg_col,dimension=int(len(value)**0.25)) emb_features.append(emb_col) emb_layer = layers.DenseFeatures(emb_features) emb_outputs = emb_layer(emb_inputs) return emb_outputs
在继续使用下一个函数之前,我们需要定义哪些特征需要从不同的特征模型中排除。 从根本上讲,我们将要排除要预测的特征(数据泄漏)和用于嵌入的特征。 这里还应该小心删除可直接用于计算输出特征的特征。 例如,一个预测男女的模型,如果有类似Gender_Female之类的特征那么它基本上可以获得100%的准确率。 为了解决这个问题,我们将从相应的特征模型中排除其他性别,年龄和联系人等特征。
feature_layers = {col:[col,'Country'] for col in model_cols} feature_layers['Gender_Female'] += ['Gender_Male', 'Gender_Transgender'] feature_layers['Gender_Male'] += ['Gender_Female', 'Gender_Transgender'] feature_layers['Gender_Transgender'] += ['Gender_Female', 'Gender_Male'] feature_layers['Age_0-9'] += ['Age_10-19','Age_20-24', 'Age_25-59','Age_60_'] feature_layers['Age_10-19'] += ['Age_0-9','Age_20-24', 'Age_25-59','Age_60_'] feature_layers['Age_20-24'] += ['Age_0-9','Age_10-19', 'Age_25-59','Age_60_'] feature_layers['Age_25-59'] += ['Age_0-9','Age_10-19', 'Age_20-24','Age_60_'] feature_layers['Age_60_'] += ['Age_0-9','Age_10-19', 'Age_20-24','Age_25-59'] feature_layers['Contact_Dont-Know'] += ['Contact_No','Contact_Yes'] feature_layers['Contact_No'] += ['Contact_Dont-Know','Contact_Yes'] feature_layers['Contact_Yes'] += ['Contact_Dont-Know','Contact_No']
我们还想为我们模型添加一个feature_layer:
feature_layers['Severity_Severe_aux'] = ['Country']
现在,我们拥有构建特征模型所需的所有东西。此函数将使用所有输入特征列表,上面定义的排除特征和嵌入字典,在add_model函数中描述的hidden_layer结构以及是否应使用批处理规范的配置等。
首先,该函数将使用TensorFlow方式的读取输入特征。使用这种方法的原因我们只需要定义一次,就可以在每个特征模型中一再地重复使用它们。接下来我们将确定是否定义了任何嵌入列,并创建一个嵌入层(可选)。对于每个特征模型,我们将创建DenseFeatures输入层(不包括上面定义的特征),并使用add_model函数创建一个单独的模型。在返回之前,我们检查循环是否在跳连接模型上运行。如果是这样,我们将附加输入要素,以便最终模型也可以使用原始要素进行训练。最后,此函数将返回模型输入的字典,每个要特征模型输出层的列表以及每个最终隐藏层(即新设计的特征)的列表。
def feature_models( output_feature=None,all_features=[],feature_layers={}, emb_layers={},hidden_layers=[],batch_norm=False): model_inputs = {} for feature in all_features: if feature in [k for k,v in emb_layers.items()]: model_inputs[feature] = tf.keras.Input(shape=(1,), name=feature, dtype='string') else: model_inputs[feature] = tf.keras.Input(shape=(1,), name=feature) if len(emb_layers) > 0: emb_outputs = add_emb(emb_layers,model_inputs) output_layers = [] eng_layers = [] for key,value in feature_layers.items(): feature_columns = [feature_column.numeric_column(f) for f in all_features if f not in value] feature_layer = layers.DenseFeatures(feature_columns) feature_outputs = feature_layer({k:v for k,v in model_inputs.items() if k not in value}) if len(emb_layers) > 0: feature_outputs = layers.concatenate([feature_outputs, emb_outputs]) last_layer, output_layer = add_model( feature_outputs=feature_outputs, hidden_layers=hidden_layers, batch_norm=batch_norm, model_name=key) output_layers.append(output_layer) eng_layers.append(last_layer) if key == output_feature + '_aux': eng_layers.append(feature_outputs) return model_inputs, output_layers, eng_layers
如果使用嵌入层,则它将与这些模型的每个输入连接在一起。 这意味着这些嵌入不仅可以训练最大化整体模型的精度,还可以训练每一个特征模型。这导致了非常健壮的嵌入。
在进入最终功能之前,让我们定义将要输入的每个参数。以上大多数参数已在上面进行了描述,或对于所有TensorFlow模型都是典型的。 例如,patience 参数,当指定时间段内验证准确性没有提高时,可使用该参数停止训练模型。
params = {'all_features': list(X_train.columns), 'output_feature':y_train.columns[0], 'emb_layers':emb_layers, 'feature_layers':feature_layers, 'hidden_layers':[[256,0],[128,0.1],[64,0.2]], 'batch_norm': True, 'learning_rate':0.001, 'patience':3, 'epochs':20 }
对于最终模型,我们将从运行上一个函数开始以生成输入,输出和特征工程的特征。 然后,我们将这些层/特征中的每一个串联起来,并将其输入到最终模型中。 最后,我们构建,编译,训练和测试模型。
def final_model(params,test=True): print(params['batch_norm'],params['hidden_layers']) model_inputs, output_layers, eng_layers = feature_models( all_features=params['all_features'], feature_layers=params['feature_layers'], emb_layers=params['emb_layers'], hidden_layers=params['hidden_layers'], batch_norm=params['batch_norm'], output_feature=params['output_feature']) concat_layer = layers.concatenate(eng_layers) last_layer, output_layer = add_model( feature_outputs=concat_layer, hidden_layers=params['hidden_layers'], batch_norm=params['batch_norm'], model_name=params['output_feature']) output_layers.append(output_layer) model = tf.keras.Model( inputs=[model_inputs], outputs=output_layers) aux_loss_wgt = 0.5 / len(params['feature_layers']) loss_wgts = [aux_loss_wgt for i in range(len(params['feature_layers'])) loss_wgts.append(0.5) model.compile(loss='binary_crossentropy', optimizer=tf.keras.optimizers.Adam( lr=params["learning_rate"]), loss_weights=loss_wgts, metrics=['accuracy']) es = tf.keras.callbacks.EarlyStopping( monitor='val_loss',mode='min',verbose=1, patience=params['patience'],restore_best_weights=True) history = model.fit( trainset,validation_data=valset, epochs=params['epochs'], verbose=0, callbacks=[es]) yhat = model.predict(testset) loss = log_loss( np.array(y_test[params['output_feature']]), yhat[-1])**.5 print('Binary Crossentropy:',loss) if test==True: sys.stdout.flush() return {'loss': loss, 'status': STATUS_OK} else: return history, model
需要注意的是,函数的输入之一称为test。这个 输入可以在使用hyperopt求解最佳参数(test = True)或训练并返回最终模型(test = False)之间进行切换。 编译模型时,也可能不调整loss_weights参数。 因为我们有多个辅助输出,所以我们需要告诉TensorFlow在确定如何调整模型以提高准确性时给每个加权多少。 我个人喜欢对辅助预测(总计)给予50%的权重,对目标预测给予50%的权重。 有些人可能会觉得给辅助预测赋予任何权重是很奇怪的,因为它们在损失计算步骤中被丢弃了。问题是,如果我们不给它们赋予任何权重,模型多半会忽略它们,从而妨碍它学习有用的特征。
现在,我们只需要使用上面定义的参数来运行final_model:
history, model = final_model(params,test=False)
现在我们有了一个经过训练的模型,我们可以使用keras get_layer()函数选择性地提取要在其他模型中使用的新特性。如果这能引起足够的兴趣,我将把这一步留到以后的文章中讨论。
结果
“它最后战胜了XGBoost,这与传统观点不同,因为传统观点认为梯度增强模型更适合结构化数据集。”
你可以想象,这是一个计算上昂贵的模型训练。但是好消息是它通常会在比典型MLP少得多的试验中收敛到一个更准确的答案。如果你把不花几周时间在繁琐的工程特性上节省下来的时间也算进去,那就快得多了。此外,预测延迟足够小,可以使其成为一个生产模型(与典型的Kaggle 50+元模型相反)。如果你提取特征并用它们重新训练一个神经网络,那么它会变得更快。
问题仍然存在,它是准确的吗?在我应用这个模型的每一个案例中,它都是最准确的。它始终战胜了XGBoost,让我们看看它在这个问题上做得如何!
我测试了三种不同的模型:
- XGBoost
- 带有嵌入的标准MLP
- 上面训练的自动特征模型
对于自动特征模型,我使用hyperopt进行了20个试验,以试验不同的网络规模。 对于这两个对比的模型,由于它们的训练时间较快,因此我进行了40次试验。 结果如下:
正如预期的那样,我们的自动特征模型表现最好。需要记住的一件事是,这个简单的数据集没有足够的有用信息来允许任何模型比边际收益更好。 当我处理数百个特征的海量数据集时,自动功能模型将领先XGBoost击败5–10%也是有可能的。
作者:Michael Malin
deephub翻译组
-
python机器学习前戏 ——特征工程应用
2022-04-09 20:53:52python 机器学习前戏——特征工程机器学习笔记——特征工程应用
- 机器学习为什么需要特征工程
- 什么是特征工程
- 特征工程的实现
机器学习——特征工程应用
机器学习为什么需要特征工程
- 样本数据中的特征有可能会存在缺失值,重复值,异常值等等,那么我们是需要对特征中的相关的噪点数据进行处理的,那么处理的目的就是为了营造出一个更纯净的样本集,让模型基于这组数据可以有更好的预测能力。当然特征工程不是单单只是处理上述操作!
什么是特征工程
- 特征工程是将原始数据转换成更好的预测模型的潜在问题的特征的过程,从而提升对为主数据预测的准确性
- 举个例子(AIphaGO的机器学习中只会有相关棋谱的数据集,不会有其他类型的数据集影响程序判断。)
- 特征工程将直接影响预测结果
- 特征工程的实现工具
*sklearn工具
sklearn 工具特征抽取应用
- 目的:机器学习学习的数据如果不是数值型的数据,电脑是识别不了的,所以需要将其他类型的数据通过特征抽取转换成电脑可以识别的数据进而帮助实现预测过程。
效果演示
from sklearn.feature_extraction.text import CountVectorizer vector = CountVectorizer() res = vector.fit_transform(['lift is short,i love python','lift is too long,i hate python']) print(res.toarray())
结果:
- 这个过程就是对文本进行了特征值化的过程。为了让计算机更好的理解数据。
字典特征抽取
–模块导入
- from sklearn.feature_extraction import DictVectorizer
- fit_transform(X):X为字典或者包含字典的迭代器,返回值为sparse矩阵
- fit_transform(X):X为字典或者包含字典的迭代器,返回值为sparse矩阵
- inverse_transform(X):X为sparse矩阵或者array数组,返回值为转换之前的数据格式
- transform(X):按照原先的标准转换
- get_feature_names():返回类别名称
from sklearn.feature_extraction import DictVectorizer alist = [ {'city':'BeiJing','temp':33}, {'city':'GZ','temp':42}, {'city':'SH','temp':40} ] #实例化一个工具类对象 d = DictVectorizer() #返回的是一个sparse矩阵(存储的就是特征值化之后的结果) feature = d.fit_transform(alist) print(feature)
返回结果:
- 什么是sparse矩阵如何理解?
- 在DictVectorizer类的构造方法中中默认设定sparse=False,其返回的是一个数组。
- get_feature_names():返回类别名称
- sparse矩阵是一个变相的数组或者列表,目的是为了节省内存
- 在DictVectorizer类的构造方法中中默认设定sparse=False,其返回的是一个数组。
from sklearn.feature_extraction import DictVectorizer alist = [{'city':'BeiJing','temp':33},{'city':'GZ','temp':42},{'city':'SH','temp':40}] d = DictVectorizer(sparse=False)#sparse=False #返回的是一个二维列表 feature = d.fit_transform(alist) print(d.get_feature_names()) print(feature) #输出结果:1为是,0为不是
输出的结果是:
OneHot编码(上图中矩阵中01就是该编码)
- 为什么使用OneHot编码?
为了避免序列化导致数据优先级和权重的影响。
文本特征抽取
- 作用:对文本数据进行特征值化 - from sklearn.feature_extraction.text import CountVectorizer - fit_transform(X):X为文本或者包含文本字符串的可迭代对象,返回sparse矩阵 - inverse_transform(X):X为array数组或者sparse矩阵,返回转换之前的格式数据 - get_feature_names() - toarray():将sparse矩阵换成数组
from sklearn.feature_extraction.text import CountVectorizer vector = CountVectorizer() res = vector.fit_transform(['left is is short,i love python','left is too long,i hate python']) # print(res) #sparse矩阵 print(vector.get_feature_names()) print(res.toarray()) #将sparse矩阵转换成数组 #注意:单字母不统计(因为单个字母代表不了实际含义),然后每个数字表示的是单词出现的次数
结果:
特征预处理(数值型)
- 无量纲化: - 在机器学习算法实践中,我们往往有着将不同规格的数据转换到同一规格,或不同分布的数据转换到某个特定分布 的需求这种需求统称为将数据“无量纲化”譬如梯度和矩阵为核心的算法中,譬如逻辑回归,支持向量机,神经 网络,无量纲化可以加快求解速度;而在距离类模型,譬如K近邻,K-Means聚类中,无量纲化可以帮我们提升模 型精度,避免某一个取值范围特别大的特征对距离计算造成影响。(一个特例是决策树和树的集成算法们,对决策 树我们不需要无量纲化,决策树可以把任意数据都处理得很好。) - 那么预处理就是用来实现无量纲化的方式。 - 含义:特征抽取后我们就可以获取对应的数值型的样本数据啦,然后就可以进行数据处理了。 - 概念:通过特定的统计方法(数学方法),将数据转换成算法要求的数据 - 方式: - 归一化 - 标准化
from sklearn.preprocessing import MinMaxScaler mm = MinMaxScaler(feature_range=(0,1))#:每个特征缩放的范围 data = [[90,2,10,40],[60,5,15,45],[73,3,13,45]] data = mm.fit_transform(data)#:X需要归一化的特征 print(data)
结果:
- 问题:如果数据中存在的异常值比较多,会对结果造成什么样的影响? - 结合着归一化计算的公式可知,异常值对原始特征中的最大值和最小值的影响很大,因此也会影响对归一化之后的值。这个也是归一化的一个弊端,无法很好的处理异常值。 - 归一化总结: - 在特定场景下最大值和最小值是变化的,另外最大最小值很容易受到异常值的影响,所以这种归一化的方式具有一定的局限性。因此引出了一种更好的方式叫做:标准化!!! - 归一化和标准化总结: - 对于归一化来说,如果出现了异常值则会响应特征的最大最小值,那么最终结果会受到比较大影响 - 对于标准化来说,如果出现异常点,由于具有一定的数据量,少量的异常点对于平均值的影响并不大,从而标准差改变比较少。
- API
- 处理后,每列所有的数据都聚集在均值为0,标准差为1范围附近
- 标准化API:from sklearn.preprocessing import StandardScaler
- fit_transform(X):对X进行标准化
- mean_:均值
- var_:方差
特征选择
特征选择:从特征中选择出有意义对模型有帮助的特征作为最终的机器学习输入的数据!
- 工具: - Filter(过滤式)【主要讲解】 - Embedded(嵌入式):决策树模型会自己选择出对其重要的特征。【后期在讲解模型的时候在补充】 - PCA降维
- Filter过滤式(方差过滤):
- 原理:这是通过特征本身的方差来筛选特征的类。比如一个特征本身的方差很小,就表示样本在这个特征上基本没有差 异,可能特征中的大多数值都一样,甚至整个特征的取值都相同,那这个特征对于样本区分没有什么作用。所以无 论接下来的特征工程要做什么,都要优先消除方差为0或者方差极低的特征。
- API:from sklearn.feature_selection import VarianceThreshold
- VarianceThreshold(threshold=x)threshold方差的值,删除所有方差低于x的特征,默认值为0表示保留所有方差为非0的特征
- fit_transform(X)#:X为特征
from sklearn.feature_selection import VarianceThreshold #threshold方差的值,删除所有方差低于x的特征,默认值为0表示保留所有方差为非0的特征 v = VarianceThreshold(threshold=1) v.fit_transform([[0,2,4,3],[0,3,7,3],[0,9,6,3]])#:X为特征
结果:
- 如果将方差为0或者方差极低的特征去除后,剩余特征还有很多且模型的效果没有显著提升则方差也可以帮助我们将特征选择【一步到位】。留下一半的 特征,那可以设定一个让特征总数减半的方差阈值,只要找到特征方差的中位数,再将这个中位数作为参数 threshold的值输入就好了。
- VarianceThreshold(np.median(X.var().values)).fit_transform(X)
- X为样本数据中的特征列
- VarianceThreshold(np.median(X.var().values)).fit_transform(X)
- 【注意】
- 方差过滤主要服务的对象是:需要遍历特征的算法模型。而过滤法的主要目的是:在维持算法表现的前提下,帮 助算法们降低计算成本。