ML - 数据归一化

文章目录


一、关于归一化

数据归一化:将所有数据映射到等比例空间(同一个尺度中)。

量纲:连续变量进行预处理,进行数据标准化,对于无序的分类变量,需要生成哑变量


为什么要归一化

当使用 欧式距离 评估的时候,有些数据取值范围比较大,有些比较小。
比如房间面积为 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


上一篇:机器学习实战——支持向量机


下一篇:Numpy day1 for cvpytorch