今天我们要说的是,在机器学习常用的算法里面,那些需要归一化,那些不需要,通过scikit-learn中的预处理的一些方法,实际了解如何正则化和归一化数据。看完本文,应该对于一般的机器学习任务,都可以轻松上手操作。
先看一下归一化是什么意思,对于一个机器学习任务来说,首先要有数据,数据怎么来?一种情况是别人整理好给你,一种是自己造数据,根据不同的业务场景,自己提取想要的数据,一般来自各个维度的数据,也就是常说的统计口径不一样,造成的结果是得到的数据大小范围变换非常大,并且可能数据类型也不一样,统计学里面把数据分为数值型数据、分类型数据、顺序型数据,对这些数据怎么处理成统一的口径的问题,就是机器学习中数据归一化问题。
机器学习任务一般分为3种,也可以是两种,分类、回归和聚类,其中聚类也可以看做是分类。如果需要预测的值是离散型数据,就是分类任务,如果预测值是连续型数据,就是回归任务。常用的回归模型,也几乎都可以做分类,只需要把输出变为分类的类别数的概率值即可。常用的机器学习模型有广义线性模型,集成模型,线性判别分析、支持向量机、K近邻、朴素贝叶斯、决策树、感知机、神经网络等。其中广义线性模型包括线性回归、岭回归、Lasso回归、最小角回归、逻辑回归、贝叶斯回归、多项式回归、Elastic Net等。集成的方法包括随机森林、AdaBoost、梯度树提升等。
机器学习中的模型这么多,怎么分的清那个需要归一化,那个不需要呢,这里有一个一般的准则,就是需要归一化的模型,说明该模型关心变量的值,而相对于概率模型来说,关心的是变量的分布和变量之间的条件概率。所以大部分概率模型不需要归一化。还有就是如果模型使用梯度下降法求最优解时,归一化往往非常有必要,否则很难收敛甚至不能收敛。
然后说一下常用的归一化的方法,利用scikit-learn这个工具,把里面提到的归一化方法挨个过一遍。
- 均值,1标准差归一化,也叫z-score标准化
顾名思义,就是把数据的均值变到0,方差变到1,公式为:
其中x是原始数据,z是变化后的数据,u是均值,sigma是方差。一般一个机器学习的数据集都是M*N的一个大的矩阵,M代表样本数,N代表特征的个数,其中的均值和方差,指的是整个大的矩阵的均值和方差,x是任意一个样本,xij,即:
下同,不在说明。
然后用scikit-learn来实现z-score标准化的方法是下面这个:
这里把需要的方法都import进来了,后面不再提示。
import numpy as np
from sklearn import preprocessing
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import FunctionTransformer
diabetes = load_diabetes()
X = diabetes.data
y = diabetes.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
print("X shape:{},y shape:{}".format(X_train.shape, y_train.shape))
X_scaled = preprocessing.scale(X_train, axis=0, with_mean=True, with_std=True, copy=True)
m = X_scaled.mean(axis=0)
s = X_scaled.std(axis=0)
print("Mean:{}, \n Std:{}".format(m, s))
"""
X shape:(331, 10),y shape:(331,)
Mean:[ -4.46101699e-17 2.42840323e-16 2.01248887e-18 -2.12988405e-17
-2.34790368e-17 -2.49884034e-17 -1.25780554e-17 -5.16538809e-17
-1.34165924e-17 1.67707406e-17],
Std:[ 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
"""
或者
# 另一种方法
scaler = preprocessing.StandardScaler(copy=True, with_mean=True, with_std=True).fit(X_train)
X_scaled = scaler.transform(X_train)
m = X_scaled.mean(axis=0)
s = X_scaled.std(axis=0)
print("Mean:{}, \n Std:{}".format(m, s))
"""
Mean:[ -4.46101699e-17 2.42840323e-16 2.01248887e-18 -2.12988405e-17
-2.34790368e-17 -2.49884034e-17 -1.25780554e-17 -5.16538809e-17
-1.34165924e-17 1.67707406e-17],
Std:[ 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
"""
- 最大-最小归一化
就是把数据变到[0,1]区间内,公式为:
# 最大最小归一化,归一化到[0,1]之间
min_max_scaler = preprocessing.MinMaxScaler()
X_train_minmax = min_max_scaler.fit_transform(X_train)
print()
ma = X_train_minmax.max(axis=0)
mi = X_train_minmax.min(axis=0)
print("max:{},\n min:{}".format(ma, mi))
"""
max:[ 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.],
min:[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
"""
还有一种是把数据归一到[-1,1]之间,公式为:
# 最大最小归一化,归一化到[-1,1]之间
max_abs_scaler = preprocessing.MaxAbsScaler()
x_maxabs = max_abs_scaler.fit_transform(X_train)
ma = x_maxabs.max(axis=0)
mi = x_maxabs.min(axis=0)
print("max:{},\n min:{}".format(ma, mi))
"""
max:[ 1. 1. 1. 1. 1. 1. 1.
1. 1. 0.98435481],
min:[-0.96838121 -0.88085106 -0.52930243 -0.85122699 -0.70749565 -0.58158979
-0.5646737 -0.41242062 -0.94384991 -1. ]
"""
- 正则化
正则化方法包括l1,l2,max正则三种方法,在数学里也叫l1范数,l2范数,简单理解就是取绝对值和绝对值的平方在开方得到的结果。看代码
X_normalized = preprocessing.normalize(X, norm='l2', axis=1, copy=True, return_norm=False)
# l2, l1, max
print()
print("{},\n {}".format(X[0], X_normalized[0]))
"""
[ 0.03807591 0.05068012 0.06169621 0.02187235 -0.0442235 -0.03482076
-0.04340085 -0.00259226 0.01990842 -0.01764613],
[ 0.32100597 0.42726811 0.52014127 0.18439893 -0.37283438 -0.29356288
-0.36589885 -0.02185454 0.16784162 -0.14876892]
"""
# 另一种方法
normalizer = preprocessing.Normalizer(norm='l2', copy=True).fit(X)
X_normalized = normalizer.transform(X)
print()
print("{},\n {}".format(X[0], X_normalized[0]))
"""
[ 0.03807591 0.05068012 0.06169621 0.02187235 -0.0442235 -0.03482076
-0.04340085 -0.00259226 0.01990842 -0.01764613],
[ 0.32100597 0.42726811 0.52014127 0.18439893 -0.37283438 -0.29356288
-0.36589885 -0.02185454 0.16784162 -0.14876892]
"""
4 变换数据的分布
将原始数据变换成均匀分布,这样会改变原始数据的分布和变量间的相关性。其实这个和下面的多项式变换不属于归一化处理,只是一种数据变换的方式。
quantile_transformer = preprocessing.QuantileTransformer(random_state=0)
X_train_trans = quantile_transformer.fit_transform(X_train)
percentile = np.percentile(a=X_train[:, 0], q=[0, 25, 50, 75, 100], axis=None, out=None,
overwrite_input=False, interpolation='linear', keepdims=False)
print()
print(percentile)
"""
[-0.10722563 -0.0382074 0.00538306 0.03807591 0.11072668]
"""
5.多项式变换
4个特征的度为2的多项式转换公式为:
然后比如数据为(1,4)大小的数组,[[1,2,3,4]]
# 多项式转换
poly = PolynomialFeatures(degree=2, interaction_only=False, include_bias=True)
X = np.array([[1, 2, 3, 4]])
polyed = poly.fit_transform(X)
print(polyed)
print()
"""
[[ 1. 1. 2. 3. 4. 1. 2. 3. 4. 4. 6. 8. 9. 12.
16.]]
"""
print("{},{}, {}".format(polyed.shape, np.max(polyed), np.min(polyed)))
"""
(1, 15),16.0, 1.0
"""
- 自定义函数转换
就是你可以用任意你定义的函数,把数据变换到另一个值,看代码:
# 自定义函数转换
transformer = FunctionTransformer(func=np.log1p, inverse_func=None, validate=True,
accept_sparse=False, pass_y='deprecated',
kw_args=None, inv_kw_args=None)
transformered = transformer.transform(X)
print()
print("{},\n {}".format(X[0], transformered[0]))
print(np.log1p(0.03807591))
"""
[ 0.03807591 0.05068012 0.06169621 0.02187235 -0.0442235 -0.03482076
-0.04340085 -0.00259226 0.01990842 -0.01764613],
[ 0.03736891 0.04943769 0.05986782 0.02163659 -0.04523118 -0.03544146
-0.04437083 -0.00259563 0.01971284 -0.01780367]
0.0373689130909
"""
下面对几个常用的模型做个分类,需要说明的是,通常归一化之后,效果会变好,但是到底归一不归一,没有一个确定的说法,还是要用结果说话,所以经常有人在微信群里问,某某某个模型要不要归一化,其实你去试试不就知道了,归一化做一遍,不归一化做一遍,做个比较就知道需不需要归一化了。
需要归一化的模型有
神经网络,标准差归一化
支持向量机,标准差归一化
线性回归,可以用梯度下降法求解,需要标准差归一化
PCA
LDA
聚类算法基本都需要
K近邻,线性归一化,归一到[0,1]区间内。
逻辑回归
不需要归一化的模型:
决策树: 每次筛选都只考虑一个变量,不考虑变量之间的相关性,所以不需要归一化。
随机森林:不需要归一化,mtry为变量个数的均方根。
朴素贝叶斯
需要正则化的模型:
Lasso
Elastic Net
完!