提交 d031033d 编写于 作者: F friedhelm

第七章完事

上级 49d1e477
......@@ -82,3 +82,222 @@ plt.title("(d) Clusters found via K-means", fontsize=14) plt.axis('off')
##曲面拼接聚类
应用聚类一般假定存在自然簇,即在其他空的空间中存在密集的数据区域。在这些情况下,有一个正确的聚类数的概念,人们已经发明了聚类指数用于测量数据分组的质量,以便选择k。
然而,当数据像如图7-2(c)那样均匀分布时,不再有正确的簇数。在这种情况下,聚类算法的作用是矢量量化,即将数据划分成有限数量的块。当使用量化矢量而不是原始矢量时,可以基于可接受的近似误差来选择簇的数目。
从视觉上看,k-均值的这种用法可以被认为是如图7- 3那样用补丁覆盖数据表面。如果在瑞士滚动数据集上运行k-均值,这确实是我们所得到的。例7-2使用sklearn生成瑞士卷上的嘈杂数据集,将其用k-均值聚类,并使用Matplotlib可视化聚类结果。数据点根据它们的簇ID着色。
![图7-3](../images/chapter_7/7-3.png)
![图7-4](../images/chapter_7/7-4.png)
例7-2
```python
from mpl_toolkits.mplot3d import Axes3D
from sklearn import manifold, datasets
# Generate a noisy Swiss roll dataset
X, color = datasets.samples_generator.make_swiss_roll(n_samples=1500)
# Approximate the data with 100 K-means clusters
clusters_swiss_roll = KMeans(n_clusters=100, random_state=1).fit_predict(X)
# Plot the dataset with K-means cluster id's as the color
fig2 = plt.figure() ax = fig2.add_subplot(111, projection='3d')
ax.scatter(X[:, 0], X[:, 1], X[:, 2], c=clusters_swiss_roll, cmap='Spectral')
```
在这个例子中,我们在瑞士滚动表面上随机生成1500个点,并要求k-均值用100个聚类来近似它。我们提出100这个数字,因为它看起来相当大,使每一簇覆盖了相当小的空间。结果看起来不错;簇群确实是很小的的,并且流体的不同部分被映射到不同的簇。不错!但我们完成了吗?
问题是,如果我们选择一个太小的K,那么从多元学习的角度来看,结果不会那么好。图7- 5显示了k-均值用10个簇在瑞士卷的输出。我们可以清楚地看流体的完全的部分都被映射到相同的簇(例如黄色、紫色、绿色和品红簇)的数据。
![图7-5](../images/chapter_7/7-5.png)
如果数据在空间中均匀分布,则选择正确的k就被归结为球填充问题。在D维中,可以拟合半径约为R的1/r的D次幂的球。每个k-均值聚类是一个球面,半径是用质心表示球面中的点的最大误差。因此,如果我们愿意容忍每个数据点R的最大逼近误差,那么簇的数目是O(1/R的D次幂),其中D是数据的原始特征空间的维数。
对于k-均值来说,均匀分布是最坏的情况。如果数据密度不均匀,那么我们将能够用更少的簇来表示更多的数据。一般来说,很难知道数据在高维空间中是如何分布的。我们可以保守的选择更大的K。但是它不能太大,因为K将成为下一步建模步骤的特征数量。
##对于分类的k-均值特征化
当使用k-均值作为特征化过程时,数据点可以由它的群集成员(分类变量群组成员的稀疏独热编码)来表示,我们现在来说明。
如果目标变量也是可用的,那么我们可以选择将该信息作为对聚类过程的提示。一种合并目标信息的方法是简单地将目标变量作为k-均值算法的附加输入特征。由于目标是最小化在所有输入维度上的总欧氏距离,所以聚类过程将试图平衡目标值和原始特征空间中的相似性。可以在聚类算法中对目标值进行缩放以获得更多或更少的关注。目标的较大差异将产生更多关注分类边界的聚类。
##k-均值特征化
聚类算法分析数据的空间分布。因此,k-均值特征化创建了一个压缩的空间索引,该数据可以在下一阶段被馈送到模型中。这是模型 *stacking* 的一个例子。
例7- 3显示了一个简单的k-均值特征。它被定义为可以训练数据和变换任何新数据的类对象。为了说明在聚类时使用和不使用目标信息之间的差异,我们将特征化器应用到使用sklearn的 *make——moons* 函数(例7- 4)生成的合成数据集。然后我们绘制群集边界的Voronoi图。图7-6展示出了结果的比较。底部面板显示没有目标信息训练的集群。注意,许多簇跨越两个类之间的空空间。顶部面板表明,当聚类算法被给定目标信息时,聚类边界可以沿着类边界更好地对齐。
例7-3
```python
import numpy as np
from sklearn.cluster import KMeans
class KMeansFeaturizer:
"""Transforms numeric data into k-means cluster memberships.
This transformer runs k-means on the input data and converts each data point into the id of the closest cluster. If a target variable is present, it is scaled and included as input to k-means in order to derive clusters that obey the classification boundary as well as group similar points together. """
def __init__(self, k=100, target_scale=5.0, random_state=None):
self.k = k
self.target_scale = target_scale
self.random_state = random_state
def fit(self, X, y=None):
"""Runs k-means on the input data and find centroids. """
if y is None:
# No target variable, just do plain k-means
km_model = KMeans(n_clusters=self.k,n_init=20,random_state=self.random_state)
km_model.fit(X)
self.km_model_ = km_model
self.cluster_centers_ = km_model.cluster_centers_
return self
# There is target information. Apply appropriate scaling and include
# into input data to k-means
data_with_target = np.hstack((X, y[:,np.newaxis]*self.target_scale))
# Build a pre-training k-means model on data and target
km_model_pretrain = KMeans(n_clusters=self.k,n_init=20,random_state=self.random_state)
km_model_pretrain.fit(data_with_target)
# Run k-means a second time to get the clusters in the original space
# without target info. Initialize using centroids found in pre-training. # Go through a single iteration of cluster assignment and centroid
# recomputation.
km_model = KMeans(n_clusters=self.k,init=km_model_pretrain.cluster_centers_[:,:2],n_init=1,max_iter=1)
km_model.fit(X)
self.km_model = km_model
self.cluster_centers_ = km_model.cluster_centers_
return self
def transform(self, X, y=None):
"""Outputs the closest cluster id for each input data point. """ clusters = self.km_model.predict(X)
return clusters[:,np.newaxis]
def fit_transform(self, X, y=None):
self.fit(X, y)
return self.transform(X, y)
```
例7-4
```python
from scipy.spatial import Voronoi, voronoi_plot_2d
from sklearn.datasets import make_moons
training_data, training_labels = make_moons(n_samples=2000, noise=0.2)
kmf_hint = KMeansFeaturizer(k=100, target_scale=10).fit(training_data, training_labels)
kmf_no_hint = KMeansFeaturizer(k=100, target_scale=0).fit(training_data, training_labels)
def kmeans_voronoi_plot(X, y, cluster_centers, ax):
"""Plots the Voronoi diagram of the kmeans clusters overlayed with the data""" ax.scatter(X[:, 0], X[:, 1], c=y, cmap='Set1', alpha=0.2)
vor = Voronoi(cluster_centers)
voronoi_plot_2d(vor, ax=ax, show_vertices=False, alpha=0.5)
```
![图7-6](../images/chapter_7/7-6.png)
让我们测试k-均值特征分类的有效性。例7-5对k-均值集群特征增强的输入数据应用Logistic回归。比较了与使用径向基核的支持向量机(RBF SVM)、K-近邻(KNN)、随机森林(RF)和梯度提升树(GBT)的结果。随机森林和梯度提升树是最流行的非线性分类器,具有最先进的性能。RBF支持向量机是欧氏空间的一种合理的非线性分类器。KNN根据其K近邻的平均值对数据进行分类。(请参阅“分类器概述”来概述每个分类器。)
分类器的默认输入数据是数据的2D坐标。Logistic回归也给出了簇成员特征(在图7-7中标注为“k-均值的LR”)。作为基线,我们也尝试在二维坐标(标记为“LR”)上进行逻辑回归。
例7-4
```python
from sklearn.linear_model
import LogisticRegression
from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier,GradientBoostingClassifier
# Generate some test data from the same distribution as training data
test_data, test_labels = make_moons(n_samples=2000, noise=0.3)
# Use the k-means featurizer to generate cluster features
training_cluster_features = kmf_hint.transform(training_data) test_cluster_features = kmf_hint.transform(test_data)
# Form new input features with cluster features
training_with_cluster = scipy.sparse.hstack((training_data, training_cluster_features)) test_with_cluster = scipy.sparse.hstack((test_data, test_cluster_features))
# Build the classifiers
lr_cluster = LogisticRegression(random_state=seed).fit(training_with_cluster, training_labels)
classifier_names = ['LR','kNN','RBF SVM','Random Forest','Boosted Trees']
classifiers = [LogisticRegression(random_state=seed),KNeighborsClassifier(5),SVC(gamma=2, C=1),RandomForestClassifier(max_depth=5, n_estimators=10, max_features=1),GradientBoostingClassifier(n_estimators=10, learning_rate=1.0, max_depth=5)]
for model in classifiers:
model.fit(training_data, training_labels)
# Helper function to evaluate classifier performance using ROC
def test_roc(model, data, labels):
if hasattr(model, "decision_function"):
predictions = model.decision_function(data)
else:
predictions = model.predict_proba(data)[:,1]
fpr, tpr, _ = sklearn.metrics.roc_curve(labels, predictions)
return fpr, tpr
# Plot results
import matplotlib.pyplot as plt plt.figure()
fpr_cluster, tpr_cluster = test_roc(lr_cluster, test_with_cluster, test_labels) plt.plot(fpr_cluster, tpr_cluster, 'r-', label='LR with k-means')
for i, model in enumerate(classifiers):
fpr, tpr = test_roc(model, test_data, test_labels) plt.plot(fpr, tpr, label=classifier_names[i])
plt.plot([0, 1], [0, 1], 'k--')
plt.legend()
```
##可选择的密集化
与独热簇相反,数据点也可以由其逆距离的密集向量表示到每个聚类中心。这比简单的二值化簇保留了更多的信息,但是现在表达是密集的。这里有一个折衷方案。一个热集群成员导致一个非常轻量级的稀疏表示,但是一个可能需要较大的K来表示复杂形状的数据。反向距离表示是密集的,这对于建模步骤可能花费更昂贵,但是这可以需要较小的K。
稀疏和密集之间的折衷是只保留最接近的簇的p的逆距离。但是现在P是一个额外的超参数需要去调整。(现在你能理解为什么特征工程需要这么多的步骤吗?),天下没有免费的午餐。
##总结
使用k-均值将空间数据转换为模型 *stacking* 的一个例子,其中一个模型的输入是另一个模型的输出。 *stacking* 的另一个例子是使用决策树类型模型(随机森林或梯度提升树)的输出作为线性分类器的输入。 *stacking* 已成为近年来越来越流行的技术。非线性分类器训练和维护是昂贵的。 *stacking* 的关键一点是将非线性引入特征,并且使用非常简单的、通常是线性的模型作为最后一层。该特征可以离线训练,这意味着可以使用昂贵的模型,这需要更多的计算能力或内存,但产生有用的特征。顶层的简单模型可以很快地适应在线数据的变化分布。这是精度和速度之间的一个很好的折衷,这经常被应用于需要快速适应改变数据分布的应用,比如目标广告。
##模型Stacking的关键一点
复杂的基础层(通常是昂贵的模型)产生良好的(通常是非线性的)特征,随后结合简单并且快速的顶层模型。这常常在模型精度和速度之间达到正确的平衡。
与使用非线性分类器相比,采用logistic回归的k-均值更容易进行训练和存储。表7-1是多个机器学习模型的计算和记忆的训练和预测复杂性的图表。n表示数据点的数量,D(原始)特征的数量。
对于k-均值,训练时间是O(nkd),因为每次迭代涉及计算每个数据点和每个质心(k)之间的d维距离。我们乐观地假设迭代次数不是n的函数,尽管并不普遍适用。预测需要计算新的数据点与质心(k)之间的距离,即O(kd)。存储空间需求是O(kd),用于K质心的坐标。
logistic训练和预测在数据点的数量和特征维度上都是线性的。RBF SVM训练是昂贵的,因为它涉及计算每一对输入数据的核矩阵。RBF SVM预测比训练成本低,在支持向量S和特征维数D的数目上是线性的。改进的树模型训练和预测在数据大小和模型的大小上线性的(t个树,每个最多2的m次幂子叶,其中m是树的最大深度)。KNN的实现根本不需要训练时间,因为训练数据本身本质上是模型。花费全都在预测时间,输入必须对每个原始训练点进行评估,并部分排序以检索K近邻。
总体而言,k-均值+LR是在训练和预测时间上唯一的线性组合(相对于训练数据O(nd)的大小和模型大小O(kd))。复杂度最类似于提升树,其成本在数据点的数量、特征维度和模型的大小(O(2的m次幂*t))中是线性的。很难说k-均值+LR或提升树是否会产生更小的模型,这取决于数据的空间特征。
![图T7-1](../images/chapter_7/T7-1.png)
##数据泄露的潜力
那些记得我们对数据泄露的谨慎(参见“防止数据泄露(Bin-counting:未来的日子)”)可能会问k-均值特化步骤中的目标变量是否也会导致这样的问题。答案是“是的”,但并不像Bin-counting计算的那么多。如果我们使用相同的数据集来学习聚类和建立分类模型,那么关于目标的信息将泄漏到输入变量中。因此,对训练数据的精度评估可能过于乐观,但是当在保持验证集或测试集上进行评估时,偏差会消失。此外,泄漏不会像Bin-counting那么糟糕(参见“Bin-counting”),因为聚类算法的有损压缩将抽象掉一些信息。要格外小心防止泄漏,人们可以始终保留一个单独的数据集来导出簇,就像在Bin-counting下一样。
k-均值特化对有实数、有界的数字特征是有用的,这些特征构成空间中密集区域的团块。团块可以是任何形状,因为我们可以增加簇的数量来近似它们。(与经典的类别聚类不同,我们不关心真正的簇数;我们只需要覆盖它们。)
k-均值不能处理欧几里得距离没有意义的特征空间,也就是说,奇怪的分布式数字变量或类别变量。如果特征集包含这些变量,那么有几种处理它们的方法:
1、仅在实值的有界数字特征上应用k-均值特征。
2、定义自定义度量(参见[章节nnn] ]以处理多个数据类型并使用k-medoids算法。(k-medoids类似于k-均值,但允许任意距离度量。)
3、类别变量可以转换为装箱统计(见“Bin-counting”),然后使用K-均值进行特征化。
结合处理分类变量和时间序列的技术,k-均值特化可以自适应的处理经常出现在客户营销和销售分析中的丰富数据。所得到的聚类可以被认为是用户段,这对于下一个建模步骤是非常有用的特征。
我们将在下一章中讨论的深度学习,是通过将神经网络层叠在一起,将模型堆叠提升到一个全新的水平。ImageNet挑战的两个赢家使用了13层和22层神经网络。就像K-均值一样,较低层次的深度学习模型是无监督的。它们利用大量可用的未标记的训练图像,并寻找产生良好图像特征的像素组合。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册