在分割训练和测试数据之前或之后标准化数据?

hem*_*ant 20 split regression machine-learning normalization train-test-split

我想将我的数据分成火车和测试集,我应该在拆分之前或之后对数据进行规范化吗?在构建预测模型时是否有任何区别?提前致谢.

Gio*_*ous 45

您首先需要将数据拆分为训练和测试集(可能还需要验证集).

不要忘记测试数据点代表真实世界的数据.归一化特征(或数据标准化)的说明(或预测)的变量是用于中心的技术和通过减去平均值和通过方差除以归一化数据.如果您采用整个数据集的均值和方差,您将把未来的信息引入训练解释变量(即均值和方差).

因此,您应该对训练数据执行特征规范化.然后在测试实例上执行nomralisation,但这次使用训练解释变量的均值和方差.通过这种方式,我们可以测试和评估我们的模型是否可以很好地推广到新的,看不见的数据点.

  • 我对此仍然很困惑。在机器学习圣经《统计学习的要素》中,它说在分割之前执行任何形式的无监督预处理是可以的。争论的焦点是,由于您没有使用标签,因此您的估计器不会产生偏差。此外,任何 ML 模型的基本假设是训练、验证、测试分割都是来自相同群体的样本。因此,“总体平均值”(或方差或任何时刻)是“唯一的”,无论我们使用整个可用数据集还是其中的子集来估计它,只会影响我们估计它的效果 (7认同)
  • 最后,由于 L2 范数仅适用于*单独*的行,因此训练/测试的区别是无关紧要的。调用“transform”时,“preprocessing.Normalizer()”不使用有关训练数据的信息。它只需要提供“转换”的数据。 (3认同)
  • 请注意,这个答案关于中心化和缩放数据以及训练/测试分割的内容基本上是正确的(尽管通常除以*标准差*而不是*方差*);以这种方式进行预处理可以显着提高基于梯度的优化器的速度。但提供的代码并未按照文本描述的方式居中或缩放数据。 (3认同)
  • 但后来我也理解了另一个_实用_的观点,那就是在现实世界中我们无法访问测试集,所以我们甚至不应该真正使用它来计算总体平均值等。 (2认同)
  • 此答案中的代码并不执行其描述的操作 - 减去平均值或除以方差。相反,它计算每行的长度(L2 范数)并将行中的每个元素除以长度。检查起来很容易:第一列的平均值是 10/3,但转换后的数据在第一列的第二行中没有负数 ( 0 - 10/3 ) / (某个正数)。但我们可以检查每一行,发现平方元素之和为 1。此外,文档也说了同样的事情。https://scikit-learn.org/stable/modules/ generated/sklearn.preprocessing.normalize.html (2认同)

Syc*_*ica 8

在训练/测试分割的具体设置中,我们需要区分两种转换:

  1. 根据有关特征(列)的信息更改观察值(行)的值的转换,以及
  2. 根据单独观察的信息改变观察值的转换。

(1) 的两个常见示例是均值中心化(减去特征的均值)或缩放到单位方差(除以标准)。减去平均值并除以标准差是一种常见的变换。在 中sklearn,它是在 中实现的sklearn.preprocessing.StandardScaler重要的是,这与Normalizer. 请参阅下文了解详尽的详细信息。

(2) 的一个示例是通过取对数或对每个值求幂(例如平方)来变换特征。

第一种类型的转换最好应用于训练数据,保留中心和缩放值并随后应用于测试数据。这是因为使用有关测试集的信息来训练模型可能会使模型比较指标过于乐观。这可能会导致过度拟合和选择虚假模型。

可以应用第二类型的转换,而不考虑训练/测试分割,因为每个观察的修改值仅取决于关于观察本身的数据,而不取决于任何其他数据或观察。


这个问题得到了一些误导性的答案。本答案的其余部分致力于展示它们如何以及为何具有误导性。

“规范化”一词是含糊不清的,不同的作者和学科将以不同的方式使用“规范化”一词。在没有具体阐明“正常化”含义的情况下,我认为最好从最普遍的意义上来处理这个问题。

从这个角度来看,问题并不是sklearn.preprocessing.Normalizer具体的。事实上,Normalizer问题中没有提到班级。就此而言,也没有提及任何软件、编程语言或库。此外,即使意图是询问Normalizer,答案仍然具有误导性,因为它们错误地描述了Normalizer所做的事情。

即使在同一个库中,术语也可能不一致。例如,PyTorch 实现了标准化torchvision.transforms.Normalizetorch.nn.functional.normalize. 其中之一可用于创建平均值为 0、标准差为 1 的输出张量,而另一个则创建范数为 1 的输出。


Normalizer班级做什么

该类Normalizer是 (2) 的一个示例,因为它单独重新调整每个观测值(行) ,以便每行的平方和为 1。(在行的平方和等于 0 的极端情况下,不会进行重新缩放。)文档Normalizer的第一句话说

将样本单独标准化为单位范数。

这个简单的测试代码验证了这种理解:

X = np.arange(10).reshape((5, 2))
normalizer = preprocessing.Normalizer()
normalized_all_X = normalizer.transform(X)
sum_of_squares = np.square(normalized_all_X).sum(1)
print(np.allclose(sum_of_squares,np.ones_like(sum_of_squares)))
Run Code Online (Sandbox Code Playgroud)

打印此内容True是因为结果是 1 的数组,如文档中所述。

规范化器实现fit,transformfit_transform方法,即使其中一些只是“传递”方法。这是为了跨预处理方法具有一致的接口,而不是因为方法的行为需要区分不同的数据分区。


误导性演示 1

Normalizer不减去列均值

另一个答案写道:

不要忘记测试数据点代表真实世界的数据。解释(或预测)变量的特征归一化(或数据标准化)是一种通过减去均值并除以方差来对数据进行中心化和归一化的技术。

好的,让我们试试这个。使用答案中的代码片段,我们有

X = np.arange(10).reshape((5, 2))

X_train = X[:3]
X_test = X[3:]

normalizer = preprocessing.Normalizer()
normalized_train_X = normalizer.fit_transform(X_train)
column_means_train_X = normalized_train_X.mean(0)
Run Code Online (Sandbox Code Playgroud)

这就是 的价值column_means_train_X。它不是零!

[0.42516214 0.84670847]
Run Code Online (Sandbox Code Playgroud)

如果从列中减去列平均值,则居中的列平均值将为 0.0。(这很容易证明。n数字之和x=[x1,x2,x3,...,xn]S。这些数字的平均值是S / n。然后我们有sum(x - S/n) = S - n * (S / n) = 0。)

我们可以编写类似的代码来显示列尚未除以方差。(列也没有除以标准差,这将是更常见的选择)。

误导性演示 2

Normalizer类应用于整个数据集不会改变结果。

如果您采用整个数据集的均值和方差,您将把未来的信息引入训练解释变量(即均值和方差)。

这种说法就其本身而言是正确的,但它对班级绝对没有影响Normalizer。事实上,Giorgos Myrianthous 所选择的例子实际上不受他们所描述的影响的影响。

如果该类Normalizer确实涉及特征的平均值,那么我们预计标准化结果将根据训练集中包含的数据而变化。

例如,样本均值是样本中每个观测值的加权和。如果我们计算列均值并减去它们,则将其应用于所有数据的结果将不同于仅将其应用于训练数据子集。但我们已经证明它Normalizer不会减去列均值。

此外,这些测试表明,应用于Normalizer所有数据或仅应用于部分数据对结果没有影响。

如果我们单独应用这个方法,我们有

[[0.         1.        ]
 [0.5547002  0.83205029]
 [0.62469505 0.78086881]]

[[0.65079137 0.7592566 ]
 [0.66436384 0.74740932]]
Run Code Online (Sandbox Code Playgroud)

如果我们一起应用它,我们有

[[0.         1.        ]
 [0.5547002  0.83205029]
 [0.62469505 0.78086881]
 [0.65079137 0.7592566 ]
 [0.66436384 0.74740932]]
Run Code Online (Sandbox Code Playgroud)

唯一的区别是,由于分区,在第一种情况下我们有 2 个数组。让我们仔细检查一下组合数组是否相同:

normalized_train_X = normalizer.fit_transform(X_train)
normalized_test_X = normalizer.transform(X_test)
normalized_all_X = normalizer.transform(X)
assert np.allclose(np.vstack((normalized_train_X, normalized_test_X)),normalized_all_X )
Run Code Online (Sandbox Code Playgroud)

没有例外;它们在数字上是相同的。

但 sklearn 的转换器有时是有状态的,所以让我们创建一个新对象以确保这不是一些与状态相关的行为。

new_normalizer = preprocessing.Normalizer()
new_normalized_all_X = new_normalizer.fit_transform(X)
assert np.allclose(np.vstack((normalized_train_X, normalized_test_X)),new_normalized_all_X )
Run Code Online (Sandbox Code Playgroud)

在第二种情况下,我们仍然没有提出异常。

我们可以得出结论,对于Normalizer类来说,数据是否分区并没有什么区别。


小智 5

你可以使用 fit 然后转换学习

normalizer = preprocessing.Normalizer().fit(xtrain)
Run Code Online (Sandbox Code Playgroud)

转变

xtrainnorm = normalizer.transform(xtrain) 
xtestnorm = normalizer.transform(Xtest) 
Run Code Online (Sandbox Code Playgroud)