从鸢尾花数据到视觉洞察:Python实战KMeans与t-SNE的降维艺术

鸢尾花数据集在机器学习领域就像"Hello World"之于编程新手。但不同于简单的分类任务,这次我们要用无监督学习的方式,让算法自己发现数据中隐藏的模式。本文将带你用Python和sklearn库,从数据加载到最终可视化,完整实现KMeans聚类分析,并借助t-SNE这一强大的降维工具,让高维数据的聚类结果跃然纸上。

1. 环境准备与数据初探

工欲善其事,必先利其器。我们先确保环境配置正确:

# 基础库导入
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
from sklearn.cluster import KMeans
from sklearn.manifold import TSNE
from sklearn.datasets import load_iris

鸢尾花数据集包含150个样本,每个样本有4个特征:

  • 花萼长度(sepal length)
  • 花萼宽度(sepal width)
  • 花瓣长度(petal length)
  • 花瓣宽度(petal width)

加载数据只需几行代码:

iris = load_iris()
X = iris.data  # 特征矩阵
y = iris.target  # 真实标签(仅用于后期评估)
feature_names = iris.feature_names

提示:虽然我们有真实标签y,但聚类是无监督学习,算法不会使用这些标签信息。

初步查看数据分布:

pd.DataFrame(X, columns=feature_names).describe()

输出显示各特征的数值范围差异较大,这正是我们需要标准化的原因。

2. 数据预处理:标准化的必要性

不同特征的量纲差异会影响距离计算。比如花萼长度范围是4.3-7.9cm,而花瓣宽度只有0.1-2.5cm。如果不处理,KMeans会过度依赖数值范围大的特征。

我们采用MinMaxScaler将各特征缩放到[0,1]范围:

scaler = MinMaxScaler()
X_scaled = scaler.fit_transform(X)

标准化前后的数据分布对比:

特征 原始范围 标准化后范围
花萼长度 4.3-7.9 0-1
花萼宽度 2.0-4.4 0-1
花瓣长度 1.0-6.9 0-1
花瓣宽度 0.1-2.5 0-1

3. KMeans聚类实战

KMeans的核心思想是迭代寻找最优的簇中心(质心),使得样本到所属簇中心的距离之和最小。在sklearn中实现非常简单:

kmeans = KMeans(n_clusters=3, random_state=42)
kmeans.fit(X_scaled)
y_pred = kmeans.labels_

几个关键输出:

  • cluster_centers_ : 各簇的质心坐标
  • labels_ : 每个样本的预测簇标签
  • inertia_ : 簇内平方和(SSE),衡量聚类紧密程度

我们可以查看质心位置:

print("簇中心坐标:\n", kmeans.cluster_centers_)
print("簇内平方和:", kmeans.inertia_)

4. 高维数据的可视化难题与t-SNE解决方案

四维数据难以直接可视化,我们需要降维。t-SNE(t-分布随机邻域嵌入)特别适合将高维数据降到2D或3D可视化:

  • 保留局部相似性:高维空间中相近的点在低维中也相近
  • 非线性降维:能捕捉复杂的流形结构
  • 对簇结构展示效果极佳

实现代码:

tsne = TSNE(n_components=2, random_state=42)
X_tsne = tsne.fit_transform(X_scaled)

plt.figure(figsize=(10,6))
plt.scatter(X_tsne[:,0], X_tsne[:,1], c=y_pred, cmap='viridis')
plt.title('t-SNE可视化KMeans聚类结果')
plt.colorbar()
plt.show()

5. 聚类效果的多维度评估

虽然聚类是无监督学习,但我们仍需要评估结果质量。常用指标包括:

5.1 轮廓系数(Silhouette Score)

衡量一个样本与同簇和其他簇的相似度差异,范围在[-1,1]:

  • 接近1表示样本聚类合理
  • 接近0表示样本在两个簇的边界上
  • 接近-1表示样本可能被分错簇

计算代码:

from sklearn.metrics import silhouette_score
score = silhouette_score(X_scaled, y_pred)
print(f"轮廓系数:{score:.3f}")

5.2 Calinski-Harabasz指数

簇间离散度与簇内离散度的比值,值越大越好:

from sklearn.metrics import calinski_harabasz_score
ch_score = calinski_harabasz_score(X_scaled, y_pred)
print(f"Calinski-Harabasz指数:{ch_score:.1f}")

5.3 与真实标签的对比(可选)

虽然聚类不需要标签,但如果有的话可以对比:

from sklearn.metrics import adjusted_rand_score
ari = adjusted_rand_score(y, y_pred)
print(f"调整兰德指数:{ari:.3f}")

6. 参数调优与最佳K值选择

KMeans需要预先指定簇数量K。如何确定最佳K值?以下是几种实用方法:

6.1 肘部法则(Elbow Method)

绘制不同K值对应的SSE曲线,寻找拐点:

inertias = []
for k in range(1, 8):
    kmeans = KMeans(n_clusters=k, random_state=42)
    kmeans.fit(X_scaled)
    inertias.append(kmeans.inertia_)

plt.plot(range(1,8), inertias, marker='o')
plt.xlabel('K值')
plt.ylabel('SSE')
plt.title('肘部法则选择最佳K值')
plt.show()

6.2 轮廓系数法

计算不同K值的平均轮廓系数:

sil_scores = []
for k in range(2, 8):
    kmeans = KMeans(n_clusters=k, random_state=42)
    y_pred = kmeans.fit_predict(X_scaled)
    sil_scores.append(silhouette_score(X_scaled, y_pred))

plt.plot(range(2,8), sil_scores, marker='o')
plt.xlabel('K值')
plt.ylabel('平均轮廓系数')
plt.title('轮廓系数法选择最佳K值')
plt.show()

6.3 综合评估结果

将三种评估指标可视化对比:

K值 SSE 轮廓系数 Calinski-Harabasz
2 72.6 0.68 513.9
3 39.8 0.55 561.6
4 28.6 0.50 530.5

从表中可见,K=3时各项指标相对均衡,确实是鸢尾花数据的合理簇数。

7. 高级可视化技巧

为了让聚类结果更加直观,我们可以结合多种可视化手段:

7.1 平行坐标图

展示各特征在不同簇中的分布:

df = pd.DataFrame(X_scaled, columns=feature_names)
df['cluster'] = y_pred

pd.plotting.parallel_coordinates(df, 'cluster', colormap='viridis')
plt.title('平行坐标图展示簇特征分布')
plt.show()

7.2 热力图展示簇中心

centers = pd.DataFrame(kmeans.cluster_centers_, 
                      columns=feature_names)
plt.figure(figsize=(10,4))
sns.heatmap(centers.T, cmap='YlGnBu', annot=True)
plt.title('各簇中心特征值热力图')
plt.show()

7.3 3D散点图

如果使用t-SNE降到3维,可以创建交互式3D图:

from mpl_toolkits.mplot3d import Axes3D

tsne3d = TSNE(n_components=3, random_state=42)
X_tsne3d = tsne3d.fit_transform(X_scaled)

fig = plt.figure(figsize=(10,7))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(X_tsne3d[:,0], X_tsne3d[:,1], X_tsne3d[:,2], 
           c=y_pred, cmap='viridis', depthshade=True)
plt.title('3D t-SNE可视化')
plt.show()

8. 实际应用中的注意事项

经过这个完整流程后,总结几点实践经验:

  • 数据预处理至关重要 :标准化对基于距离的算法如KMeans影响很大
  • t-SNE参数调整 :perplexity参数影响可视化效果,通常建议在5-50间尝试
  • KMeans的局限性 :假设簇是凸形且大小相似,对复杂形状数据效果不佳
  • 多次运行取最优 :KMeans受初始质心影响,可设置n_init参数多次运行
# 更稳健的KMeans实现
kmeans = KMeans(n_clusters=3, n_init=20, random_state=42)

对于更复杂的数据集,可能需要考虑层次聚类、DBSCAN等更高级的算法。但KMeans+t-SNE的组合仍然是探索性数据分析的强大工具。

更多推荐