数据和特征工程决定了模型的上限,改进算法只不过是逼近这个上限。
数据矩阵:
标签:
探索性数据分析
博客上写了三个 plot ,我想大概是用画图来看看数据的分布情况。预处理
需要将训练集变成矩阵,大小为[n_samples,n_features]和矢量,长度:[n_samples]。矢量可以表示成矩阵,大小为[n_samples,n_classes]。
常用函数:
- sklearn.preprocessing.LabelBinarizer
preprocessing.LabelBinarizer(neg_label=0,pos_label=1,sparse_output=False)
输入参数:
neg_label = 0:让负标签为0;
pos_label = 1:让正标签为1;
sparse_output = False:如果希望从transform返回的是CSR格式,就写成True。
输出参数:
classes_:有多少个类,类分别是什么;
y_type_:计算的目标的数据类型,有'continuos(连续)',‘continuous-multioutput(连续多输出)’,'multiclass(多目标)','binary’,'multiclass', 'multiclass-multioutput', 'multilabel-indicator(多标签指示符)????'
lb = preprocessing.LabelBinarizer()
lb.fit([1,2,6,4,2])
print(lb.classes_)
print(lb.transform([1,2,6,4,3,5]))
print(lb.y_type_)
结果:
array([1, 2, 4, 6])
array([[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 1],
[0, 0, 1, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]])
感觉类似于one-hot编码
-sklearn.preprocessing.MultiLabelBinarizer(多标签二值化)
sklearn.preprocessing.MultiLabelBinarizer(classes=None, sparse_output=False)
输入参数:
classes:设置classes时候,label从classes里面出,如果没有设置,则从训练集里面统计
sparse_out:同上
返回参数:
classes_:同上
mlb = preprocessing.MultiLabelBinarizer()
print(mlb.fit_transform([(1,2),(3,4),(5,)]))
print(mlb.classes_)
mlb1 = preprocessing.MultiLabelBinarizer(classes=[2,3,4,5,6,1])
print(mlb1.fit_transform([(1,2),(3,4),(5,)]))
print(mlb1.classes_)
结果:
array([[1, 1, 0, 0, 0],
[0, 0, 1, 1, 0],
[0, 0, 0, 0, 1]])
array([1, 2, 3, 4, 5])
array([[1, 0, 0, 0, 0, 1],
[0, 1, 1, 0, 0, 0],
[0, 0, 0, 1, 0, 0]])
array([2, 3, 4, 5, 6, 1])
-sklearn.preprocessing.LabelEncoder
之前看过这个,可以把male,female这些变成0,1的二值,但是存在值的大小关系,可能会造成机器学习的算法过程中一些偏好。
- 处理也有的特征
类别特征
类别特征,表示某个数据点属于某一个类别,或具有某一种类的特性。一列类别特征,默认用自然数表示(可以用LabelEncoder将字符串转化为自然数)。如果一列类别特征里有种不同类别,其取值范围是。自然数编码
默认的编码方式,消耗内存小,训练时间快,但是特征的质量不高。
这个特征质量不高我感觉是因为LabelEncoder的出来的东西是无序的,经常需要再用one-hot编码独热编码
如果类别特征的数据本身是无序的,例如male,female里面不存在male的值大于female,以及female大于male的情况,则需要用one-hot编码。
它会使一组数据变得只保留一个位置上为1,其余都为0的情况,会特别稀疏。聚类编码
(百度也没查到,这玩意儿没看懂太,待补)平均数编码
博客上说这是针对高基数类别特征的有监督编码。
如果一个特征是定性的并且这个特征的可能值非常多(高基数),那么平均数编码是一种高效的编码方式。(比如:家庭住址?,email地址)
LabelEncoder编码高基数定性特征,虽然只会产生一列,但每个自然数都会产生不同的重要意义,对于而言线性不可分(?????仅仅是因为维度高了所以不可分,还是因为什么?)
OneHotEncoder编码高基数定义特征,必然会产生上万列的稀疏矩阵,易消耗大量内存和训练时间。
这时候平均数编码是一种好的选择。在经验贝叶斯的架构下,利用所要预测的因变量,有监督地确定最适合这个定性特征的编码方式。
基本思路与原理:
平均数编码是一种有监督的编码方式,适用于分类和回归问题。
平均数编码的基本的基本思想是:把特征工程中的每一个变量变成它所估算的label的概率。具体可见。
先验概率,后验概率的计算感觉上是通过频率来估计概率!
先验概率的计算:
后验概率的计算:
后验概率的理解:用条件概率展开,然后使用频数估计概率。(我是这么感觉的)
权重:
平均编码使用的是先验概率和后验概率的一个凸组合,引入先验概率权重来计算编码时候所使用的概率。
感觉上这个是手动调节的,不知道可不可以弄成自动调节(根据先验概率和后验概率的比值,那一部分值越大,使得它的比值越大)。
权重函数:
定义一个权重函数,输入是特征类别在训练集中出现的次数,输出是对于这个特征类别的先验概率的权重。
- 只出现一次的类别
在类别特征里,有时候有一些类别,在训练集和测试集中总共只出现一次,这时候,继续保留这个编码的意义不大,不然将所有只出现一次的编码融合成一个新的类别里面。
数值特征
数值特征可以是连续的,也可以是离散的,一般表示为一个实数值。
不同算法对于数值的要求不一样,有些算法是可以不用标准化/归一化的,例如:树模型;但是神经网络这样的模型是需要标准化的。
- 标准化
- Z-score标准化
preprocessing.StandardScaler(copy = True, with_mean = True, with_stand = True)
计算公式是:
是训练样本的均值,是训练样本的标准差。
输入参数:
copy:默认是True,不会覆盖原数据,(特别是处理一些DataFrame文件时候);
with_mean:默认是True,如果是False,会使得为0;
with_std:默认是True,如果是False,会使得为0。
返回参数:
scale_:每个功能相对缩放数据;
mean_:训练集的每个特征的平均值;
var_:训练集的每个特征的方差;
n_samples_seen:对每个特征通过估算器处理的样本数。(试了下,这个应该是返回的是处理的多少样本数目)
scaler = preprocessing.StandardScaler()
data = [[0,0],[0,0],[1,1],[1,1]]
scaler.fit(data)
print(scaler.mean_)
print(scaler.var_)
print(scaler.n_samples_seen_)
print(scaler.transformer(data))
结果是:
array([0.5, 0.5])
array([0.25, 0.25])
4
array([[-1., -1.],
[-1., -1.],
[ 1., 1.],
[ 1., 1.]])
- RobustScaler(稳健的放缩??)
如果数值特征列中存在数值极大或者极小的异常点outlier(通过EDA发现,e.g. 箱形图,ps.我好像也只能想到箱形图),应该使用更稳健的统计数据:用中位数取代均值(因为均值易受离群点的影响),用分位数而不是方差。
prepocessing.RobustScaler(with_center=True, with_scaling=True, quantile_range=(25.0, 75.0), copy=True)
感觉和Z-score的区别就是使用对离群点更鲁棒的统计学参数。
输入参数:
with_center:默认是True,缩放之前让数据居中。这将导致变换在稀疏矩阵上尝试时引发异常,因为对它们进行居中需要构建一个密集矩阵,在常见的情况下,该矩阵可能太大而无法放入内存中。(意思就是说,对稀疏矩阵要选择False?);
with_scaling:默认是True,将数据放缩到4分位数。
quantile_range:默认是(25.0,75.0),1分位数和3分位数,计算范围;
copy:同之前写的一样。
输出参数:
center_:训练集每个属性的中值;
scale_:训练集中每个特征的(缩放)四分位数范围。
from sklearn.preprocessing import RobustScaler
X = np.array([[1,2],[2,3]])
scaler = RobustScaler()
scaler.fit(X)
print(scaler.transform(X))
结果为:
array([[-1., -1.],
[ 1., 1.]])
- 归一化
至少存在一个非0成分的每一行(每一个样本)被分别缩放使得它的L2范数等于1。
这个transformer对稀疏矩阵,密集矩阵都适用
preprocessing.Normalizer(norm='l2', copy = True)
输入参数:
norm:用于标准化每个非0样本的标准;
copy:同上。
from sklearn.preprocessing import Normalizer
X2 = [[4,1,2,2],[1,3,9,3],[5,7,5,1]]
scaler2 = Normalizer(norm='l2')
scaler2.fit(X2)
print(scaler2.transform(X2))
结果是:
array([[0.8, 0.2, 0.4, 0.4],
[0.1, 0.3, 0.9, 0.3],
[0.5, 0.7, 0.5, 0.1]])
- 区间缩放
- MaxAbsScaler
根据最大绝对值放缩每个特征。
单独的缩放和转换每个特征,使得训练集上的最大绝对值的值变为1,它不会移动/居中数据,因此不会破坏稀疏性。
prepocessing.MaxAbsScarer(copy = True)
输入参数:
copy:同上。
输出参数:
scale_:数据的每个特征的缩放;
max_abs:每个特征的绝对值最大值;
n_samples_seen_:同上。
from sklearn.preprocessing import MaxAbsScaler
X = np.array([[1,-1,2],[2,0,0],[0,1,-1]])
print(X)
scaler = MaxAbsScaler()
scaler.fit(X)
print(scaler.max_abs_)
print(scaler.scale_)
print(scaler.n_samples_seen_)
print(scaler.transform(X))
结果:
[[ 1 -1 2]
[ 2 0 0]
[ 0 1 -1]]
[2. 1. 2.]
[2. 1. 2.]
3
[[ 0.5 -1. 1. ]
[ 1. 0. 0. ]
[ 0. 1. -0.5]]
- MinMaxScaler
把每一个特征放缩到一个区域内。
其中,min,max 是特征区域,e.g. [0,1]。
preprocessing.MinMaxScaler(featur_range(0,1), copy = True)
输入参数:
feature_range:期望的转换数据范围;
copy:同上。
输出参数:
scale_:数据的每个特征的缩放;
data_min_:每个特征的最小值;
data_max_:每个特征的最大值;
data_range_:每个特征的范围。
from sklearn.preprocessing import MinMaxScaler
X1 = np.array([[-1,2],[-0.5,6],[0,10],[1,18]])
print(X1)
scaler1 = MinMaxScaler()
scaler1.fit(X1)
print(scaler1.min_)
print(scaler1.scale_)
print(scaler1.data_min_)
print(scaler1.data_max_)
print(scaler1.data_range_)
print(scaler1.transform(X1))
结果:
[ 0.5 -0.125]
[0.5 0.0625]
[-1. 2.]
[ 1. 18.]
[ 2. 16.]
[[0. 0. ]
[0.25 0.25]
[0.5 0.5 ]
[1. 1. ]]
- 缺失值处理
LightGBM/XGBoost都能将NaN作为数据的一部分进行学习,所以不需要处理缺失值,但是别的方法一般都不行,需要进行填充。
- 用平均值,中值,分位数,众数,随机值等替代,效果一般,因为认为增加了噪声;
- 先根据欧氏距离或者Pearson相似度,来确定和缺失数据样本最近的K个样本,将这K个样本的属性加权平均来估计该样本的缺失值;
(感觉上前面部分是推荐系统里面的东西,找出近邻的样本); - 将变量映射到高维空间
3.a. 对于离散型变量:男,女,缺失三种情况,采用one-hot编码映射成三个变量;
3.b. 对于连续型变量:首先对连续型变量进行变量分箱,采用一定数据的数据平滑方式(平均值/中值/箱边界)进行离散化,然后增加是否缺失这种维度。
- 创造新的特征
- 数值特征的简单变换
1.a. 单独特征列乘以一个常数或者加减一个常数:对于创造新的有用特征毫无用处,只能作为对已有特征的处理;
1.b. 任何针对单独特征列的单调变换,e.g. 把变成;不适合于决策树之类的算法
1.c. 线性组合;仅适用于决策树以及基于决策树的ensemble算法;
1.d. 多项式特征;
生成多项式和相互特征。
生成一个新的特征矩阵,该特征矩阵由特征的所有多项式组合组成,其度数小于或等于指定的度数。也就是说给定,度数是2,那么新的特征矩阵是。(给我一种集合里的生成元的感觉)
prepocessing.PolynomialFeatures(degree=2, interaction_only=False, include_bias=True,Order='C')
输入参数:
degree:默认是2,多项式特征的度;
interaction_only:(没看懂这个);
include_bias:默认是True,会包含一个偏置列,度数是0,也就是会有0这一项;
Order:密集情况下的输出数组的顺序,'F'顺序计算速度更快,但可能会降低后续估算器的速度。
输出参数:
powers_:powers_[i,j] 是第i个输出中第j个输入的指数;
n_input_features_:输入特征的总数;
n_output_features:输出特征的总数。
from sklearn.preprocessing import PolynomialFeatures
import numpy as np
X = np.arange(6).reshape(3,2)
print(X)
poly = PolynomialFeatures()
poly.fit(X)
print(poly.powers_)
print(poly.n_input_features_)
print(poly.n_output_features_)
print(poly.transform(X))
结果是:
[[0 1]
[2 3]
[4 5]]
[[0 0]
[1 0]
[0 1]
[2 0]
[1 1]
[0 2]]
2
6
[[ 1. 0. 1. 0. 0. 1.]
[ 1. 2. 3. 4. 6. 9.]
[ 1. 4. 5. 16. 20. 25.]]
1.e 比例特征:;
1.f 绝对值;
1.g ,。
- 类别特征与数值特征的组合
用表示数值特征,用表示类别特征,利用Pandas的groupby操作,可以创造出以下几种有意思的特征:(知乎上说可以是离散化的,这是不是可以说,我们对一个数值特征分成几个类别,然后对它进行操作,重复使用会不会对数据利用的效果更好。)
仅仅将已有的数值特征和类别特征进行以上有效组合,就能够得到更多的有效特征。
pandas.DataFrame.groubpy(by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, observed=False, **Kwargs)
使用映射器或者一系列的列对DataFrame或者Series进行分组。groupby操作涉及拆分对象,应用函数和组合结果的某种组合。 这可用于对这些组上的大量数据和计算操作进行分组。
输入参数:
by:用于确定作为groupby操作的group;
axis:对行操作还是对列操作,axis=0/1;
level:如果轴是MultiIndex(分层),则按特定级别或级别分组;
ax_index:对于聚合输出,返回以组标签作为索引的对象。 仅与DataFrame输入相关。 as_index = False实际上是“SQL风格”的分组输出;
sort:对组键进行排序,关闭它可以获得更好的性能,请注意,这不会影响每组内观察的顺序,groupby保留每个组中的行顺序;
group_keys:调用apply时,将组键添加到索引以标识片段;
squeeze:如果可能,减少返回类型的维度,否则返回一致类型;
observed:这仅适用于任何group是分类的情况。 如果为True:仅显示分l类group的观察值。 如果为False:显示分类group的所有值;
**Kwargs:可选,只接受关键字参数'mutated'并传递给groupby。
输出参数:取决于调用对象并返回包含有关组的信息的groupby对象。
import pandas as pd
data = pd.DataFrame({'Animal':['Falcon','Falcon','Parrot','Parrot'],
'Max Speed':[380,370,24,26]})
data.groupby(['Animal']).mean()
data:
结果:
将这种方法与线性组合等基础特征工程方法结合(仅适用于决策树),可以得到更多有意义的特征。
e.g.
N1 - median(N1)_by(C1)
N1 - mean(N1)_by(C1)
- 用基因编程创造新的特征
目前,Python环境下最好用的基因编程库是gplearn。
基因编程的两大用法:
3.a 转换:将已有特征进行组合转换,组合的方式(一元,二元,多元算子)可以由用户自行定义,也可以使用库中自带的函数(e.g. 加减乘除,min,max,三角函数,对数等)。组合的目的是为了创造出与目标值最相关的新特征。这种相关程度可以用spearman或者pearson的相关系数进行测量。spearman多用于决策树(免疫单特征单调变换),pearson多用于线性回归等其他算法。
3.b 回归:原理同上,只不过直接用于回归而已。
关于gplearn的中文介绍:https://zhuanlan.zhihu.com/p/31185882
官方文档说明:https://gplearn.readthedocs.io/en/stable/intro.html#initialization - 用决策树创造新的特征
在决策树系列算法中(单颗决策树,GBDT,随机森林中),每一个样本都会被映射到决策树的一片叶子上。因此,我们可以把样本经过每一棵决策树映射后的index(自然数)或one-hot-vector(哑编码得到的稀疏矢量)作为一项新的特征,加入到模型中。
具体实现:apply()以及decision_path()方法,在scikit-learn和xgboost里都可以用。 - 决策树,基于决策树的ensemble
5.a spearman correlation coefficient
- 特征选择
当数据预处理完后,我们需要选择有意义的特征输入机器学习的算法和模型进行训练。通常来说,从两个方面考虑来选择特征:
- 特征是否发散:如果一个特征不发散,例如方差接近于0,也就是说样本在这个特征上基本上没有差异,这个特征对于样本的区分并没有什么用。
- 特征与目标的相关性:这点比较显而易见,与目标相关性高的特征,应当优选选择。
根据特征选择的形式又可以将特征选择方法分为三种。
2.a Filter:过滤法,按照发散性或者相关性对各个特征进行评分,设定阈值或者待选择阈值的个数,选择特征。
aa. 方差选择法。使用方差选择法,先要计算各个特征的方差,然后根据阈值,选择方差大于阈值的特征;
bb. 相关系数法。使用相关系数法,先要计算各个特征对目标值的相关系数以及相关系数的P值。 e.g. 使用pearson相关系数法
cc. 卡方检验。 (这一部分不太能理解,待补)
dd. 互信息法。 (这个也是,不太能理解为啥这个能选择出来)。
补充下个人的其它看法,这个过滤法,感觉只要能算出属性的什么什么值,然后用阈值去截断就好了,本科毕设里面对BP神经网络加了个灰色关联度去截断属性能够获得比BP神经网络更好的效果,现在理解就是这么个意思
2.b Wrapper:包装法,根据目标函数(通常是预测效果评分),每次选择若干特征,或者排除若干特征。
aa. 递归消除法:递归消除特征法使用一个基模型来进行多轮训练,每轮训练后,消除若干权值系数的特征,再基于新的特征集进行下一轮训练。
2.c Embedded:嵌入法,先使用某些机器学习的算法和模型进行训练,得到各个特征的权值系数,根据系数从大到小选择特征。类似于Filter方法,但是是通过训练来确定特征的优劣。
aa. 基于惩罚项的特征选择法。
参考资料:
https://zhuanlan.zhihu.com/p/26444240
https://blog.csdn.net/fisherming/article/details/80105891
https://zhuanlan.zhihu.com/p/26308272
https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.PolynomialFeatures.html
https://zhuanlan.zhihu.com/p/31185882
https://www.cnblogs.com/peizhe123/p/7412364.html