数据标准化
通常情况下是为了消除量纲的影响。譬如一个百分制的变量与一个5分值的变量在一起怎么比较?只有通过数据标准化,都把它们标准到同一个标准时才具有可比性,一般标准化采用的是Z标准化,即均值为0,方差为1,当然也有其他标准化,比如0–1标准化等等,可根据自己的数据分布情况和模型来选择
适用情况
看模型是否具有伸缩不变性。
不是所有的模型都一定需要标准化,有些模型对量纲不同的数据比较敏感,譬如SVM等。当各个维度进行不均匀伸缩后,最优解与原来不等价,这样的模型,除非原始数据的分布范围本来就不叫接近,否则必须进行标准化,以免模型参数被分布范围较大或较小的数据主导。但是如果模型在各个维度进行不均匀伸缩后,最优解与原来等价,例如logistic
regression等,对于这样的模型,是否标准化理论上不会改变最优解。但是,由于实际求解往往使用迭代算法,如果目标函数的形状太“扁”,迭代算法可能收敛得很慢甚至不收敛。所以对于具有伸缩不变性的模型,最好也进行数据标准化。
三种数据变换方法的含义与应用
Rescaling(重缩放/归一化):通常是指增加或者减少一个常数,然后乘以/除以一个常数,来改变数据的衡量单位。例如:将温度的衡量单位从摄氏度转化为华氏温度。
Normalizing(正则化):通常是指除以向量的范数。例如:将一个向量的欧氏长度等价于1 。在神经网络中,“正则化”通常是指将向量的范围重缩放至最小化或者一定范围,使所有的元素都在[0,1]范围内。通常用于文本分类或者文本聚类中。
Standardizing(标准化):通常是为了消除不同属性或样方间的不齐性,使同一样方内的不同属性间或同一属性在不同样方内的方差减小。例如:如果一个向量包含高斯分布的随机值,你可能会通过除以标准偏差来减少均值,然后获得零均值单位方差的“标准正态”随机变量。
那么问题是,当我们在训练模型的时候,一定要对数据进行变换吗?这得视情况而定。很多人对多层感知机有个误解,认为输入的数据必须在[0,1]这个范围内。虽然标准化后在训练模型效果会更好,但实际上并没有这个要求。但是最好使输入数据中心集中在0周围,所以把数据缩放到[0,1]其实并不是一个好的选择。
如果你的输出激活函数的范围是0,1,那你必须保证你的目标值也在这个范围内。但通常请款下,我们会使输出激活函数的范围适应目标函数的分布,而不是让你的数据来适应激活函数的范围。
当我们使用激活函数的范围为[0,1]时,有些人可能更喜欢把目标函数缩放到[0.1,0.9]这个范围。我怀疑这种小技巧的之所以流行起来是因为反向传播的标准化太慢了导致的。但用这种方法可能会使输出的后验概率值不对。如果你使用一个有效的训练算法的话,完全不需要用这种小技巧,也没有必要去避免溢出(overflow)
标准化代码
用 FunctionTransformer自定义一个转化器,并且可以在Pipeline中使用
在做一个分类任务时,发现第一主成分与这个不相关,你可以用FunctionTransformer把第一列除去,剩下的列用PCA:
import matplotlib.pyplot as plt
import numpy as np
from sklearn.cross_validation import train_test_split
from sklearn.decomposition import PCA
from sklearn.pipeline import make_pipeline
# from sklearn.preprocessing import FunctionTransformer
# 如果报错ImportError: cannot import name FunctionTransformer,可以使用下面的语句
from sklearn.preprocessing import *
def _generate_vector(shift=0.5, noise=15):
return np.arange(1000) + (np.random.rand(1000) - shift) * noise
def generate_dataset():
"""
This dataset is two lines with a slope ~ 1, where one has
a y offset of ~100
"""
return np.vstack((
np.vstack((
_generate_vector(),
_generate_vector() + 100,
)).T,
np.vstack((
_generate_vector(),
_generate_vector(),
)).T,
)), np.hstack((np.zeros(1000), np.ones(1000)))
def all_but_first_column(X):
return X[:, 1:]
def drop_first_component(X, y):
"""
Create a pipeline with PCA and the column selector and use it to
transform the dataset.
"""
pipeline = make_pipeline(
PCA(), FunctionTransformer(all_but_first_column),
)
X_train, X_test, y_train, y_test = train_test_split(X, y)
pipeline.fit(X_train, y_train)
return pipeline.transform(X_test), y_test
if __name__ == '__main__':
X, y = generate_dataset()
plt.scatter(X[:, 0], X[:, 1], c=y, s=50)
plt.show()
X_transformed, y_transformed = drop_first_component(*generate_dataset())
plt.scatter(
X_transformed[:, 0],
np.zeros(len(X_transformed)),
c=y_transformed,
s=50,
)
plt.show()
结果: