机器学习技术(四)——特征工程与模型评估(1️⃣)
文章目录
- 机器学习技术(四)——特征工程与模型评估(:one:)
- 一、特征工程
- 1、标准化
- 2、特征缩放
- 3、缩放有离群值的数据
- 4、非线性转换
- 5、样本归一化
- 6、特征二值化
- 7、标称特征编码(one-hot编码)
- 8、缺失值插补
- 9、生成多项式特征
- 10、过滤法
- 1、导入数据集
- 2、采用方差过滤
- 3、采用卡方检验
- 11、Wrapper
- 12、嵌入法 Embedding
- 1、基于惩罚项的特征选择法
- 2、基于树模型的特征选择法
- 特征工程总结
🚀机器学习技术(四)包含了十二种特征工程的应用方法,主要包括标准化,特征缩放,缩放有离群的值的数据,非线性转换,样本归一化,特征二值化,one-hot编码,缺失值插补以及生成多项式特征等步骤。
🚢通过这些步骤可以显著提高数据的质量。同时,实验包含了基于Python代码的对特征集进行筛选的多种方法。一个典型的机器学习任务,是通过样本的特征来预测样本所对应的值。而现实中的情况往往是特征太多了,需要减少一些特征。
🚢减少特征具有重要的现实意义,不仅减少过拟合、减少特征数量(降维)、提高模型泛化能力,而且还可以使模型获得更好的解释性,增强对特征和特征值之间的理解,加快模型的训练速度,一般的,还会获得更好的性能。基于sklearn自带数据集iris,应用多种特征筛选方法进行特征选择。
🚀包含关于几个评估模型指标的参数计算及其原理。在日常业务有中,当我们训练模型时常常需要在多个模型中选择出最优模型,因此本实验中precision, recall rate等参数就成为评判的依据,帮助我们选择和评价模型表现。
一、特征工程
在实际应用中,数据往往都需要进行预处理,提高数据质量,也有利于后续数据分析以及挖掘,
主要包括标准化,特征缩放,缩放有离群的值的数据,非线性转换,样本归一化,特征二值化,one-hot编码,缺失值插补以及生成多项式特征等步骤。通过这些步骤可以显著提高数据的质量。基于Python代码的对特征集进行筛选的多种方法
1、标准化
导入相关库,并自定义训练集数据以及测试集数据
from sklearn import preprocessing
import numpy as np
X_train = np.array([[ 1., -1., -2.],
[ 2., 0., 0.],
[ 3., 1., -1.]])
X_test = [[-1., 1., 0.]]
将每组特征减去均值并除以标准差,使得均值为0,方差为1
#计算数据集的尺度(也就是数据集的均值和方差)(各列)
scaler = preprocessing.StandardScaler().fit(X_train) # 计算均值和方差
print('均值:',scaler.mean_ )
print('方差:',scaler.scale_ )
#通过尺度去处理另一个数据集,当然另一个数据集仍然可以是自己。
X_scaled = scaler.transform(X_train)
print('均值:',X_scaled.mean(axis=0)) # transform会转化数据集为均值为0
print('方差:',X_scaled.std(axis=0)) # transform会转化数据集为方差为1
#上面两步的综合:缩放样本,是样本均值为0,方差为1(各列)
X_scaled = preprocessing.scale(X_train,axis=0) # 标准化:去均值和方差
print('均值:',X_scaled.mean(axis=0))
print('方差:',X_scaled.std(axis=0))
均值以及方差以及标准化后均值以及方差显示如下
均值: [ 2. 0. -1.]
方差: [0.81649658 0.81649658 0.81649658]
均值: [0. 0. 0.]
方差: [1. 1. 1.]
均值: [0. 0. 0.]
方差: [1. 1. 1.]
2、特征缩放
计算样本和数据集中所有已知标签样本的欧氏距离并排序进行投票。
MinMaxScaler将特征缩放至特定范围内(默认为0-1)
min_max_scaler = preprocessing.MinMaxScaler()
X_train_minmax = min_max_scaler.fit_transform(X_train) # 训练同时转换
print('每列最大值:',X_train_minmax.max(axis=0)) # 每列最大值为1
print('每列最小值:',X_train_minmax.min(axis=0)) # 每列最小值为0
X_test_minmax = min_max_scaler.transform(X_test) # 转换实例应用到测试数据:实现和训练数据一致的缩放和移位操作:
#MaxAbsScaler通过除以每个特征的最大值将训练数据特征缩放至 [-1, 1] 范围内。可以应用在稀疏矩阵上保留矩阵的稀疏性。
X_train = np.array([[ 0., -1., 0.],
[ 0., 0., 0.2],
[ 2., 0., 0]])
max_abs_scaler = preprocessing.MaxAbsScaler()
X_train_maxabs = max_abs_scaler.fit_transform(X_train)
print('每列最大值:',X_train_maxabs.max(axis=0)) # 每列最大值为1
print('每列最小值:',X_train_maxabs.min(axis=0)) # 每列最小值不低于-1
print('缩放比例:',max_abs_scaler.scale_)
X_test_maxabs = max_abs_scaler.transform(X_test) # 转换实例应用到测试数据:实现和训练数据一致的缩放和移位操作:
print('缩放后的矩阵仍然具有稀疏性:\n',X_train_maxabs)
缩放对象是记录了,平移距离和缩放大小,再对数据进行的操作
print('先平移:',min_max_scaler.min_)
print('再缩放:',min_max_scaler.scale_)
转换实例应用到测试数据:实现和训练数据一致的缩放和移位操作:
MaxAbsScaler
通过除以每个特征的最大值将训练数据特征缩放至 [-1, 1]
范围内。可以应用在稀疏矩阵上保留矩阵的稀疏性。
X_train = np.array([[ 0., -1., 0.],
[ 0., 0., 0.2],
[ 2., 0., 0]])
max_abs_scaler = preprocessing.MaxAbsScaler()
X_train_maxabs = max_abs_scaler.fit_transform(X_train)
print('每列最大值:',X_train_maxabs.max(axis=0)) # 每列最大值为1
print('每列最小值:',X_train_maxabs.min(axis=0)) # 每列最小值不低于-1
print('缩放比例:',max_abs_scaler.scale_)
X_test_maxabs = max_abs_scaler.transform(X_test) # 转换实例应用到测试数据:实现和训练数据一致的缩放和移位操作:
print('缩放后的矩阵仍然具有稀疏性:\n',X_train_maxabs)
输出为特征缩放的步骤以及缩放后的矩阵
每列最大值: [1. 1. 1.]
每列最小值: [0. 0. 0.]
先平移: [-0.5 0.5 1. ]
再缩放: [0.5 0.5 0.5]
每列最大值: [1. 0. 1.]
每列最小值: [ 0. -1. 0.]
缩放比例: [2. 1. 0.2]
缩放后的矩阵仍然具有稀疏性:
[[ 0. -1. 0.]
[ 0. 0. 1.]
[ 1. 0. 0.]]
3、缩放有离群值的数据
根据百分位数范围(默认值为IQR:四分位间距)缩放数据。
X_train = np.array([[ 1., -11., -2.],
[ 2., 2., 0.],
[ 13., 1., -11.]])
robust_scale = preprocessing.RobustScaler()
X_train_robust = robust_scale.fit_transform(X_train) # 训练同时转换
print('缩放后的矩阵离群点被处理了:\n',X_train_robust)
输出:
缩放后的矩阵离群点被处理了:
[[-0.16666667 -1.84615385 0. ]
[ 0. 0.15384615 0.36363636]
[ 1.83333333 0. -1.63636364]]
4、非线性转换
sklearn 将数据集映射到均匀分布的方式主要是通过分位数转换的方式实现,通过类QuantileTransformer 类以及quantile_transform 函数实现。
X_train = np.array([[ 1., -1., -2.],
[ 2., 0., 0.],
[ 3., 1., -1.]])
quantile_transformer = preprocessing.QuantileTransformer(n_quantiles=3,random_state=0)
#将数据映射到了零到一的均匀分布上(默认是均匀分布)
X_train_trans = quantile_transformer.fit_transform(X_train)
#查看分位数信息,经过转换以后,分位数的信息基本不变
print('源分位数情况:',np.percentile(X_train[:, 0], [0, 25, 50, 75, 100]))
print('变换后分位数情况:',np.percentile(X_train_trans[:, 0], [0, 25, 50, 75, 100]))
#下面将数据映射到了零到一的正态分布上:输入的中值称为输出的平均值,并且以0为中心。正常输出被剪切,使得输入的最小和最大值分别对应于1e-7和1-1e-7分位数
quantile_transformer = preprocessing.QuantileTransformer(output_distribution='normal',random_state=0)
输出:
源分位数情况: [1. 1.5 2. 2.5 3. ]
变换后分位数情况: [0. 0.25 0.5 0.75 1. ]
5、样本归一化
归一化 是 缩放单个样本以具有单位范数 的过程。Normalizer 工具类通过使用 Transformer API实现了相同的归一化效果。但和其他转换器不一样的是,这个转换器没有状态,其fit函数并没有对转换保留状态的。fit函数只是对X进行数组校验,可见它并无状态,整个转换的过程,实际是全在 transform 函数。实际上,该类调用的依旧是normalize 函数。值得一提的是,Normalizer 工具类和 normalize 函数都支持稀疏矩阵的输入,并会自动转化为压缩的稀疏行形式。
使用 l1
或 l2
范式。缩放使每个样本(每行)的一范数或二范数为1
X = [[ 1., -1., 2.],
[ 2., 0., 0.],
[ 0., 1., -1.]]
X_normalized = preprocessing.normalize(X, norm='l1') #
print('样本归一化:\n',X_normalized)
当然仍然可以先通过样本获取转换对象,再用转换对象归一化其他数据
normalizer = preprocessing.Normalizer().fit(X) # 获取转换对象
normalizer.transform(X) # 转换任何数据,X或测试集
输出为进行l1范数归一化的数据,以及进行l2范数归一化的数据
样本归一化:
[[ 0.25 -0.25 0.5 ]
[ 1. 0. 0. ]
[ 0. 0.5 -0.5 ]]
array([[ 0.40824829, -0.40824829, 0.81649658],
[ 1. , 0. , 0. ],
[ 0. , 0.70710678, -0.70710678]])
6、特征二值化
特征二值化 是将数值特征用阈值过滤得到布尔值的过程,可以通过binarize 函数实现
获取转换模型,生成的门限,默认为0
binarizer = preprocessing.Binarizer().fit(X) #
print(binarizer)
#binarizer = preprocessing.Binarizer(threshold=1)
自定义转换器。门限以上为1,门限(包含)以下为0
X_normalized = binarizer.transform(X) # 转换任何数据,X或测试集
print('特征二值化:\n',X_normalized)
输出:
Binarizer(copy=True, threshold=0.0)
特征二值化:
[[1. 0. 1.]
[1. 0. 0.]
[0. 1. 0.]]
7、标称特征编码(one-hot编码)
独热码,在英文文献中称做 one-hot code, 直观来说就是有多少个状态就有多少比特,而且只有一个比特为1,其他全为0的一种码制。举例如下: 假如有三种颜色特征:红、黄、蓝
。 在利用机器学习的算法时一般需要进行向量化或者数字化。那么你可能想令 红=1,黄=2,蓝=3
. 那么这样其实实现了标签编码,即给不同类别以标签。然而这意味着机器可能会学习到“红<黄<蓝”
,但这并不是我们的让机器学习的本意,只是想让机器区分它们,并无大小比较之意。所以这时标签编码是不够的,需要进一步转换。因为有三种颜色状态
,所以就有3个比特。即红色:1 0 0 ,黄色: 0 1 0,蓝色:0 0 1
。如此一来每两个向量之间的距离都是根号2,在向量空间距离都相等,所以这样不会出现偏序性,基本不会影响基于向量空间度量算法的效果。
自然状态码为:000,001,010,011,100,101
独热编码为:000001,000010,000100,001000,010000,100000
以下为例子
输入:
from sklearn.preprocessing import OneHotEncoder
enc = OneHotEncoder(categories='auto')
enc.fit([[0, 1, 2], # 每列一个属性,每个属性一种编码
[1, 0, 0],
[0, 2, 1],
[1, 0, 1]])
print('编码后:',enc.transform([[0, 1, 1]]).toarray())
转换目标对象。根据可取值所占位数进行罗列。前2位为第一个数字one-hot编码,紧接着的3位为第二个数字的编码,最后3位为第三个数字的编码
编码后: [[1. 0. 0. 1. 0. 0. 1. 0.]]
8、缺失值插补
在scikit-learn的模型中都是假设输入的数据是数值型的,并且都是有意义的,如果有缺失数据是通过NAN,或者空值表示的话,就无法识别与计算了。要弥补缺失值,可以使用均值,中位数,众数等等。Imputer这个类可以实现。
import numpy as np
from sklearn.impute import SimpleImputer
imp_mean = SimpleImputer(missing_values=np.nan, strategy='mean')
x = [[np.nan, 2], [6, np.nan], [7, 6]]
imp = imp_mean.fit(x)
new_x = imp_mean.transform(x)
print(imp)
print('缺失值插值后:\n',new_x)
输出:
SimpleImputer(add_indicator=False, copy=True, fill_value=None,
missing_values=nan, strategy='mean', verbose=0)
缺失值插值后:
[[6.5 2. ]
[6. 4. ]
[7. 6. ]]
9、生成多项式特征
有的时候线性的特征并不能做出美的模型,于是我们会去尝试非线性。非线性是建立在将特征进行多项式地展开上的。比如将两个特征
(
X
1
,
X
2
)
(X_1, X_2)
(X1,X2)
,它的平方展开式便转换成5个特征
(
1
,
X
1
,
X
2
,
X
1
2
,
X
1
X
2
,
X
2
2
)
.
(1, X_1, X_2, X_1^2, X_1X_2, X_2^2).
(1,X1,X2,X12,X1X2,X22).
from sklearn.preprocessing import PolynomialFeatures
X = np.array([[0, 1],
[2, 3],
[4, 5]])
# 最大二次方。interaction_only参数设置为True,则会只保留交互项
poly = PolynomialFeatures(2,interaction_only=False)
# 从 (X_1, X_2) 转换为 (1, X_1, X_2, X_1^2, X_1X_2, X_2^2)
print('生成多项式:\n',poly.fit_transform(X))
输出:
生成多项式:
[[ 1. 0. 1. 0. 0. 1.]
[ 1. 2. 3. 4. 6. 9.]
[ 1. 4. 5. 16. 20. 25.]]
10、过滤法
1、导入数据集
我们本实验中特征数据主要是依赖于sklearn自带数据集iris,对其进行多种方法的特征筛选。所以我们首先导入数据集。
#加载数据集
from sklearn.datasets import load_iris
iris = load_iris() # 导入IRIS数据集
iris.data # 特征矩阵
iris.target # 目标向量
2、采用方差过滤
方差过滤法需要计算每个特征的方差,然后根据阈值删除取值小于阈值的特征。
例如,假设某特征的取值为0和1,且训练集中有90%以上的数据在该特征的取值为1,那么可认为该特征对于区分不同数据的作用不大。方差过滤法只能用于筛选离散的特征,如果特征的取值是连续的,就需要将连续值离散化之后才能用。
from sklearn.feature_selection import VarianceThreshold
#方差选择法,返回值为特征选择后的数据
#参数threshold为方差的阈值
VarianceThreshold(threshold=3).fit_transform(iris.data)
通过筛除方差较小的特征数据,只保留了一个特征。
优点:效果最好速度最快,模式单调,快速并且效果明显。
缺点:但是如何参数设置, 需要深厚的背景知识。
3、采用卡方检验
经典的卡方检验是检验定性自变量对定性因变量的相关性。假设自变量有N种取值,因变量有M种取值,考虑自变量等于i且因变量等于j的样本频数的观察值与期望的差距。
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
#选择K个最好的特征,返回选择特征后的数据
SelectKBest(chi2, k=2).fit_transform(iris.data, iris.target)
通过卡方检验只留下两个特征值
11、Wrapper
递归消除特征法使用一个基模型来进行多轮训练,每轮训练后,移除若干权值系数的特征,再基于新的特征集进行下一轮训练。
#递归特征消除
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
#递归特征消除法,返回特征选择后的数据
#参数estimator为基模型
#参数n_features_to_select为选择的特征个数
rfe=RFE(estimator=LogisticRegression(C=1.0, class_weight=None, dual=False,
fit_intercept=True, intercept_scaling=1,
l1_ratio=None, max_iter=1000,
multi_class='auto', n_jobs=None, penalty='l2',
random_state=None, solver='lbfgs', tol=0.0001,
verbose=0, warm_start=False),
n_features_to_select=2, step=1, verbose=0)
rfe.fit_transform(iris.data, iris.target)
上面的代码中,首先,通过全部特征利用logistic回归训练评估函数
,得出每个特征的权重。然后,将最小权重的特征从特征集合中去除
。
循环执行
以上两个过程,直到特征数达成需要。
array([[1.4, 0.2],
[1.4, 0.2],
[1.3, 0.2],
[1.5, 0.2],
[1.4, 0.2],
[1.7, 0.4],
[1.4, 0.3],
[1.5, 0.2],
[1.4, 0.2],
..........
由数据可知,递归特征消除,只留下了两个特征。
12、嵌入法 Embedding
1、基于惩罚项的特征选择法
#通过加入L1惩罚项,将许多系数压缩至0以实现特征筛选
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression
SelectFromModel(LogisticRegression(penalty="l1", C=0.3,solver='saga',multi_class='multinomial',max_iter=10000)).fit_transform(iris.data, iris.target)
通过加入L1惩罚项,将许多系数压缩至0以实现特征筛选
array([[1.4, 0.2],
[1.4, 0.2],
[1.3, 0.2],
[1.5, 0.2],
[1.4, 0.2],
[1.7, 0.4],
[1.4, 0.3],
[1.5, 0.2],
[1.4, 0.2],
[1.5, 0.1],
..........])
2、基于树模型的特征选择法
树模型中GBDT也可用来作为基模型进行特征选择,使用feature_selection库的SelectFromModel类结合GBDT模型
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import GradientBoostingClassifier
#GBDT作为基模型的特征选择
SelectFromModel(GradientBoostingClassifier()).fit_transform(iris.data, iris.target)
通过特征选择只留下2个特征
输出:
array([[1.4, 0.2],
[1.4, 0.2],
[1.3, 0.2],
[1.5, 0.2],
[1.4, 0.2],
[1.7, 0.4],
.....)
特征工程总结
通过特征工程的一系列代码,可以了解在面对不同数据集时,先进行怎么样的预处理来清洗数据提高数据质量,使得后续实验可以事半功倍。特征选择是数据挖掘中非常重要的一步,可以为后续建模省去很多时间,提高效率以及模型表现能力。在具体业务中,需要根据场景不同选择不同的方法。