在之前的两篇文章中, 我探讨了预处理数据在机器学习管道中的作用。特别是, 我检查了k最近邻(k-NN)和逻辑回归算法, 并发现缩放数值数据如何强烈影响前者的性能, 而不是后者的性能, 例如通过准确性来衡量(参见以下词汇表或以前的文章中有关缩放, k-NN和其他相关术语的定义)。这里真正传达的信息是, 预处理不是在真空中进行的, 也就是说, 你可以先处理好数据, 但要证明是在布丁中:模型的性能如何?
缩放数值数据(即, 将变量的所有实例乘以常数以更改该变量的范围)具有两个相关目的:i)如果你的度量单位为米, 而我的度量单位为英里, 那么, 如果我们都按比例缩放数据), 最终结果是相同的;&ii)如果两个变量的范围相差很大, 则范围较大的变量可能会主导你的预测模型, 即使它对目标变量的重要性不如范围较小的变量重要。我们看到的是, ii)中确定的问题是在k-NN中发生的, 它明确地查看了数据之间的接近程度, 而不是逻辑回归, 而在进行逻辑回归时, 这会缩小相关系数以解决缺乏缩放。
由于我们在前几篇文章中使用的数据是真实世界的数据, 因此我们只能看到模型在缩放前后的性能。在这里, 为了查看干扰变量形式的噪声(那些不会影响目标变量但可能影响你的模型的噪声)如何在缩放前和缩放后改变模型性能, 我将合成一个数据集, 控制麻烦变量的精确性质。我们会看到, 合成数据越嘈杂, 对于k-NN来说, 缩放比例就越重要。本文中的所有示例都将使用Python。如果你不熟悉Python, 可以在这里查看我们的srcmini课程。我将利用熊猫库满足我们的DataFrame需求, 并使用scikit-learn满足我们的机器学习需求。
在下面的代码块中, 我们使用scikit-learn的make_blobs函数生成4个簇中的2000个数据点(每个数据点具有2个预测变量和1个目标变量)。
# Generate some clustered data (blobs!)
import numpy as np
from sklearn.datasets.samples_generator import make_blobs
n_samples=2000
X, y = make_blobs(n_samples, centers=4, n_features=2, random_state=0)
绘制合成数据
现在, 我们将在飞机上绘制合成的数据。每个轴都是预测变量, 颜色是目标变量的关键:
%matplotlib inline
import matplotlib.pyplot as plt
plt.style.use('ggplot')
plt.figure(figsize=(20, 5));
plt.subplot(1, 2, 1 );
plt.scatter(X[:, 0] , X[:, 1], c = y, alpha = 0.7);
plt.subplot(1, 2, 2);
plt.hist(y)
plt.show()
注意:我们可以在第二图中看到所有可能的目标变量均被均等地表示。在这种情况下(或即使它们近似相等地表示), 我们说y类是平衡的。
我现在想绘制特征(预测变量)的直方图:
import pandas as pd
df = pd.DataFrame(X)
pd.DataFrame.hist(df, figsize=(20, 5));
现在, 我们将其分为测试和培训集, 并绘制两个集:
from sklearn.cross_validation import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
plt.figure(figsize=(20, 5));
plt.subplot(1, 2, 1 );
plt.title('training set')
plt.scatter(X_train[:, 0] , X_train[:, 1], c = y_train, alpha = 0.7);
plt.subplot(1, 2, 2);
plt.scatter(X_test[:, 0] , X_test[:, 1], c = y_test, alpha = 0.7);
plt.title('test set')
plt.show()
看起来不错!现在, 让我们实例化一个k最近邻居投票分类器, 并在我们的训练集中进行训练:
from sklearn import neighbors, linear_model
knn = neighbors.KNeighborsClassifier()
knn_model = knn.fit(X_train, y_train)
现在我们已经训练了模型, 我们可以将其拟合到我们的测试集中并计算准确性:
print('k-NN score for test set: %f' % knn_model.score(X_test, y_test))
`k-NN score for test set: 0.935000`
我们还可以将其重新调整为我们的训练集并计算准确性。我们希望它在训练集上比测试集表现更好:
print('k-NN score for training set: %f' % knn_model.score(X_train, y_train))
`k-NN score for training set: 0.941875`
值得重申的是, 在scikit-learn中, k-NN的默认评分方法是准确性。要查看各种其他指标, 我们还可以使用scikit-learn的分类报告:
from sklearn.metrics import classification_report
y_true, y_pred = y_test, knn_model.predict(X_test)
print(classification_report(y_true, y_pred))
precision recall f1-score support
0 0.87 0.90 0.88 106
1 0.98 0.93 0.95 102
2 0.90 0.92 0.91 100
3 1.00 1.00 1.00 92
avg / total 0.94 0.94 0.94 400
现在可以缩放
现在, 我将缩放预测变量, 然后再次使用k-NN:
from sklearn.preprocessing import scale
Xs = scale(X)
Xs_train, Xs_test, y_train, y_test = train_test_split(Xs, y, test_size=0.2, random_state=42)
plt.figure(figsize=(20, 5));
plt.subplot(1, 2, 1 );
plt.scatter(Xs_train[:, 0] , Xs_train[:, 1], c = y_train, alpha = 0.7);
plt.title('scaled training set')
plt.subplot(1, 2, 2);
plt.scatter(Xs_test[:, 0] , Xs_test[:, 1], c = y_test, alpha = 0.7);
plt.title('scaled test set')
plt.show()
knn_model_s = knn.fit(Xs_train, y_train)
print('k-NN score for test set: %f' % knn_model_s.score(Xs_test, y_test))
`k-NN score for test set: 0.935000`
缩放后效果不会更好!这很可能是因为两个功能都已经在同一范围内。当变量的范围变化很大时, 按比例缩放确实有意义。为了了解这一点, 我们将添加另一个功能。此外, 此功能与目标变量无关:仅仅是噪声。
给信号增加噪声:
我们添加高斯噪声的第三个变量, 平均值为0, 变量标准偏差为\(\ sigma \)。我们将噪声称为\(\ sigma \), 我们会发现噪声越强, k近邻的性能越差。
# Add noise column to predictor variables
ns = 10**(3) # Strength of noise term
newcol = np.transpose([ns*np.random.randn(n_samples)])
Xn = np.concatenate((X, newcol), axis = 1)
现在, 我们将使用mplot3d软件包来绘制3D数据:
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(15, 10));
ax = fig.add_subplot(111, projection='3d' , alpha = 0.5);
ax.scatter(Xn[:, 0], Xn[:, 1], Xn[:, 2], c = y);
现在, 让我们看看我们的模型如何在新数据上执行:
Xn_train, Xn_test, y_train, y_test = train_test_split(Xn, y, test_size=0.2, random_state=42)
knn = neighbors.KNeighborsClassifier()
knn_model = knn.fit(Xn_train, y_train)
print('k-NN score for test set: %f' % knn_model.score(Xn_test, y_test))
测试集的k-NN分数:0.400000
这是一个可怕的模型!我们如何扩展和检查性能?
Xns = scale(Xn)
s = int(.2*n_samples)
Xns_train = Xns[s:]
y_train = y[s:]
Xns_test = Xns[:s]
y_test = y[:s]
knn = neighbors.KNeighborsClassifier()
knn_models = knn.fit(Xns_train, y_train)
print('k-NN score for test set: %f' % knn_models.score(Xns_test, y_test))
`k-NN score for test set: 0.907500`
很好, 因此在缩放数据后, 该模型的性能几乎与未引入噪声的情况一样好。现在, 我们来看看模型性能与噪声强度的关系。
噪音越强, 问题就越大:
现在, 我们将看看噪声强度如何影响模型的准确性。由于我们需要多次使用相同的代码, 因此我们实际上将主要部分包装成一个小函数:
def accu( X, y):
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
knn = neighbors.KNeighborsClassifier()
knn_model = knn.fit(X_train, y_train)
return(knn_model.score(X_test, y_test))
noise = [10**i for i in np.arange(-1, 6)]
A1 = np.zeros(len(noise))
A2 = np.zeros(len(noise))
count = 0
for ns in noise:
newcol = np.transpose([ns*np.random.randn(n_samples)])
Xn = np.concatenate((X, newcol), axis = 1)
Xns = scale(Xn)
A1[count] = accu( Xn, y)
A2[count] = accu( Xns, y)
count += 1
现在, 我们将精度绘制为噪声强度的函数(请注意对数x轴):
plt.scatter( noise, A1 )
plt.plot( noise, A1, label = 'unscaled', linewidth = 2)
plt.scatter( noise, A2 , c = 'r')
plt.plot( noise, A2 , label = 'scaled', linewidth = 2)
plt.xscale('log')
plt.xlabel('Noise strength')
plt.ylabel('Accuracy')
plt.legend(loc=3);
从上图中可以看出, 扰动变量中存在的噪声越多, 针对k-NN模型缩放数据就越重要!在下面, 你将有机会对逻辑回归进行相同的操作。总而言之, 我们已经看到了通过预处理, 缩放和居中化身在数据科学管道中占据的重要位置, 并且这样做是为了促进采用整体方法来应对机器学习的挑战。我希望在以后的文章中将讨论扩展到其他类型的预处理, 例如数值数据的转换和分类数据的预处理, 这都是任何数据科学家工具包的基本方面。
狂热读者的练习:尝试将逻辑回归模型拟合到上述综合数据集, 并检验模型的性能。准确度分别是按比例缩放和不按比例缩放的数据的噪声强度的函数吗?你可以在下面的srcmini Light小部件中执行此操作!更改指数10以更改噪声量(首先尝试我在上面针对k-NN尝试的范围), 如果要缩放特征, 则将sc设置为True。你也可以在Github上查看srcmini Light!
词汇表
有监督的学习:从预测变量中推断目标变量的任务。例如, 从预测变量(例如”年龄”, “性”和”吸烟状态”)推断目标变量”心脏病的存在”。
分类任务:如果目标变量由类别(例如”点击”或”非”, “恶性”或”良性”肿瘤)组成, 则监督学习任务就是分类任务。
回归任务:如果目标变量是连续变化的变量(例如房屋价格)或有序的分类变量(例如”葡萄酒质量等级”), 则监督学习任务就是回归任务。
k最近邻居:一种用于分类任务的算法, 其中为数据点分配由其k个最近邻居的多数票决定的标签。
预处理:科学家将使用任意数量的操作数据将其数据转换成更适合他们想要处理的形式。例如, 在对Twitter数据进行情感分析之前, 你可能希望删除所有html标签, 空白, 扩展缩写并将推文拆分为包含它们的单词的列表。
居中和缩放:这两种都是预处理数值数据的形式, 例如, 由数字组成的数据, 而不是类别或字符串。以变量居中表示从每个数据点减去变量的平均值, 以便新变量的平均值为0;缩放变量是将每个数据点乘以一个常数, 以更改数据范围。有关这些重要性的重要性, 请参见本文的正文以及示例。
本文来自Jupyter笔记本。你可以在此处下载笔记本。
评论前必须登录!
注册