文章目录
一、关于归一化
数据归一化:将所有数据映射到等比例空间(同一个尺度中)。
量纲:连续变量进行预处理,进行数据标准化,对于无序的分类变量,需要生成哑变量。
为什么要归一化
当使用 欧式距离 评估的时候,有些数据取值范围比较大,有些比较小。
比如房间面积为 70、100、120, 房间个数为 3,4,5。数据单位不同,如果只根据数值大小来决策,是不合理的,所以需要进行数据标准化或归一化的操作。
二、归一化方法
1、最值归一化 normalization
最值归一化是最常用的归一化方式,称作 Min-Max scaling,或者叫做 normalization。就是常说的 0-1 归一化
。压缩到 0–1 的区间上,这样做可以抑制 离群值
对结果的影响。
$ x_{scale} = \frac{x - x_{min}}{x_{max} - x_{min}} $
适用于:分布有明显边界的情况;如分数(0–100分),颜色(0–255);
缺点:受 outlier 影响比较大。比如收入范围分布很广。
2、均值方差归一化 standardization
均值方差归一化,也可以称为标准化
,英文为 standardization 或 Z-score normailization。
方法:将所有数据归一到 均值为0 方差为1 的分布中。
计算公式: x s c a l e = x − x m e a n s x_{scale} = \frac{x - x_{mean}}{s} xscale=sx−xmean
也可记作: z = x − μ σ z=\frac{x-\mu}{\sigma} z=σx−μ
均值为 μ = 0 \mu=0 μ=0 ,标准差 σ = 1 \sigma=1 σ=1;
数据形态上以原点为对称,特征的取值范围都差不多。
适用于:数据没有明显的边界,有可能存在极端数据值。
三、Python 代码实现归一化
import numpy as np
x = np.random.randint(0, 100, size = 100)
x
'''
array([29, 45, 97, 97, 5, ...
82, 57, 2, 92, 14, 92, 70, 3, 56])
'''
(x - np.min(x)) / (np.max(x) - np.min(x))
'''
array([0.28125 , 0.44791667, 0.98958333, 0.98958333, 0.03125 ,
...
0.125 , 0.9375 , 0.70833333, 0.01041667, 0.5625 ])
'''
处理矩阵
X = np.random.randint(0, 100, (50, 2))
X[:10,:]
'''
array([[23, 47],
[18, 6],
[58, 48],
[65, 88]])
'''
# 将 X 转为浮点数
X = np.array(X, dtype = float)
# 对第一列数据进行特征归一化
X1 = X[:,0]
X11 = (X1 - np.min(X1))/ (np.max(X1) - np.min(X1))
X11
'''
array([0.23232323, 0.18181818, 0.11111111, 0.92929293, 0.78787879,
...
0. , 0.96969697, 0.02020202, 0.5959596 , 0.66666667])
'''
X2 = X[:,1]
X21 = (X2 - np.min(X2))/ (np.max(X2) - np.min(X2))
X21
'''
array([0.47474747, 0.06060606, 0. , 0.76767677, 0.57575758,
...
0.64646465, 0.09090909, 0.41414141, 0.08080808, 0.61616162])
'''
# 查看标准差
print('均值1:', np.mean(X11))
print('方差1:', np.std(X11))
print('均值2:', np.mean(X21))
print('方差2:', np.std(X21))
'''
均值1: 0.5335353535353535
方差1: 0.3290554389302859
均值2: 0.4656565656565656
方差2: 0.31417389159473386
'''
import matplotlib.pyplot as plt
plt.scatter(X11, X21)
# 可以看到数值都在 0--1 之间
均值方差归一化
# (X - np.min(X)) / (np.max(X) - np.min(X))
X12 = (X1 - np.mean(X1))/ np.std(X1)
X22 = (X2 - np.mean(X2))/ np.std(X2)
print('均值1:', np.mean(X12))
print('方差1:', np.std(X12))
print('均值2:', np.mean(X22))
print('方差2:', np.std(X22))
# 均值为0,方差为1
'''
均值1: -8.881784197001253e-18
方差1: 1.0
均值2: -4.5519144009631415e-17
方差2: 0.9999999999999999
'''
plt.scatter(X12, X22)
四、Sklearn 中的实现
sklearn 使用 Scaler 来处理归一化,封装方法来尽量像其他算法
from sklearn import datasets
iris = datasets.load_iris()
X = iris.data
y = iris.target
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42)
from sklearn.preprocessing import MinMaxScaler, StandardScaler
# 均值方差归一化
std_scaler = StandardScaler()
std_scaler.fit(X_train)
# StandardScaler(copy=True, with_mean=True, with_std=True)
# 查看均值;特征矩阵四个特征对应的均值
std_scaler.mean_
# array([5.80916667, 3.06166667, 3.72666667, 1.18333333])
# 查看方差;原来版本使用 std_ , 后面替换为 scale_ , 意思是 描述数据分布范围;
std_scaler.scale_
# array([0.82036535, 0.44724776, 1.74502786, 0.74914766])
X_train_std = std_scaler.transform(X_train)
X_train_std
'''
array([[-1.47393679, 1.20365799, -1.56253475, -1.31260282],
[-0.13307079, 2.99237573, -1.27600637, -1.04563275],
[ 1.08589829, 0.08570939, 0.38585821, 0.28921757],
...
[-0.01117388, -1.0322392 , 0.15663551, 0.02224751],
[ 1.57348593, -0.13788033, 1.24544335, 1.22361279]])
'''
X_test_std = std_scaler.transform(X_test)
X_test_std
'''
array([[ 0.35451684, -0.58505976, 0.55777524, 0.02224751],
[-0.13307079, 1.65083742, -1.16139502, -1.17911778],
...
[-1.23014297, -0.13788033, -1.33331205, -1.17911778],
[-1.23014297, 0.08570939, -1.2187007 , -1.31260282]])
'''
使用归一化后的数据来对 iris 进行 knn 分类
from sklearn.neighbors import KNeighborsClassifier
knn_clf = KNeighborsClassifier(n_neighbors=3)
knn_clf.fit(X_train_std, y_train)
# KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski', metric_params=None, n_jobs=None, n_neighbors=3, p=2, weights='uniform')
knn_clf.score(X_test_std, y_test) # 注意上面 X_train 是 std,这里的 X_test 也需要使用 std 之后的
# 1.0
其他数据集常见使用
from sklearn.preprocessing import StandardScaler, MinMaxScaler
std_scaler = StandardScaler().fit(df[['alcohol', 'acid']])
df_std = std_scaler.transform(df[['alcohol', 'acid']])
minmax_scaler = StandardScaler().fit( df[['alcohol', 'acid']] )
df_minmax = minmax_scaler.transform( df[['alcohol', 'acid']] )
五、封装归一化类
import numpy as np
class StandardScaler:
def __init__(self):
self.mean_ = None
self.scale_ = None
def fit(self, X):
"""根据训练数据集X获得数据的均值和方差"""
assert X.ndim == 2, "The dimension of X must be 2"
self.mean_ = np.array([np.mean(X[:,i]) for i in range(X.shape[1])])
self.scale_ = np.array([np.std(X[:,i]) for i in range(X.shape[1])])
return self
# 将X根据这个StandardScaler进行均值方差归一化处理
def transform(self, X):
""""""
# 只处理二维的数据
assert X.ndim == 2, "The dimension of X must be 2"
assert self.mean_ is not None and self.scale_ is not None, "must fit before transform!"
assert X.shape[1] == len(self.mean_), "the feature number of X must be equal to mean_ and std_"
resX = np.empty(shape=X.shape, dtype=float)
for col in range(X.shape[1]):
resX[:,col] = (X[:,col] - self.mean_[col]) / self.scale_[col]
return resX
六、测试数据的归一化
是否让 训练数据 和 测试数据 各自做归一化?
正确做法:将测试数据集 使用训练数据集 得到的 mean_train 和 std_train 来进行归一化:
(x_test - mean_train) / std_train
这样做的原因:
- 测试数据是模拟真实环境;真实环境很有可能无法得到所有的测试数据的均值和方差;
- 对数据的归一化也是算法的一部分。
sklearn 中的归一化 StandardScaler
https://scikit-learn.org/0.20/modules/generated/sklearn.preprocessing.StandardScaler.html