Day 86:【99天精通Python】机器学习进阶 - K-Means 聚类 - 让数据自动"站队"
前言
欢迎来到第86天!
在之前的机器学习课程中,我们处理的都是有监督学习 (Supervised Learning)。
这意味着我们的数据都带有标签 (Label),比如:这张图片是"猫",这个房价是"300万"。
但现实中,大量的数据是没有标签的。比如,你有一堆用户的消费记录,但你不知道他们属于哪个消费群体。
无监督学习 (Unsupervised Learning)就是要让机器在没有标签的数据中,自动发现隐藏的结构。聚类 (Clustering)是其中最常用的一种。
K-Means是最经典的聚类算法,它的目标是把数据分成 K 个簇,让同一个簇内的数据尽可能相似,不同簇之间的数据尽可能不同。
本节内容:
- K-Means 算法原理
- Sklearn 中的
KMeans - 评估指标:轮廓系数 (Silhouette Score)
- 手肘法 (Elbow Method) 确定 K 值
- 实战练习:客户分群
一、K-Means 原理:“物以类聚,人以群分”
算法流程很简单:
- 初始化:随机选择 K 个点作为初始的"质心" (Centroids)。
- 分配 (Assignment):计算每个数据点到各个质心的距离,把它分到最近的那个簇。
- 更新 (Update):重新计算每个簇的平均值,将质心移动到这个平均值的位置。
- 重复2-3 步,直到质心不再移动。
[外链图片转存中…(img-346iHRze-1768773799828)]
二、Sklearn 实现
我们用make_blobs生成一些用于聚类的数据。
fromsklearn.datasetsimportmake_blobsfromsklearn.clusterimportKMeansimportmatplotlib.pyplotasplt# 1. 生成数据# n_samples: 样本数, centers: 簇中心数, cluster_std: 簇内标准差X,y=make_blobs(n_samples=300,centers=4,cluster_std=0.8,random_state=42)# 可视化原始数据plt.scatter(X[:,0],X[:,1],s=50)# plt.show()# 2. 创建并训练模型# n_clusters=4: 我们想分成 4 个簇kmeans=KMeans(n_clusters=4,random_state=42,n_init='auto')kmeans.fit(X)# 3. 获取结果labels=kmeans.labels_# 每个点的标签 (0, 1, 2, 3)centers=kmeans.cluster_centers_# 4 个质心的坐标# 4. 可视化结果plt.scatter(X[:,0],X[:,1],c=labels,s=50,cmap='viridis')plt.scatter(centers[:,0],centers[:,1],c='red',s=200,alpha=0.75,marker='X')plt.title("K-Means Clustering Result")# plt.show()三、如何确定 K 值?
在实际问题中,我们事先并不知道数据应该分成几类。K-Means 的一个缺点就是需要手动指定 K 值。
我们有两种常用的方法来辅助判断。
3.1 手肘法 (Elbow Method)
计算不同 K 值下的簇内平方和 (Inertia)。Inertia 越小,说明簇内越紧凑。
随着 K 增大,Inertia 肯定会减小。我们寻找那个"拐点"(手肘),即 K 再增大,Inertia 下降得不那么明显的地方。
inertia_list=[]forkinrange(1,11):kmeans=KMeans(n_clusters=k,random_state=42,n_init='auto')kmeans.fit(X)inertia_list.append(kmeans.inertia_)plt.plot(range(1,11),inertia_list,marker='o')plt.xlabel("Number of clusters (K)")plt.ylabel("Inertia")plt.title("Elbow Method For Optimal K")# plt.show()# 在图中,K=4 处是一个明显的拐点3.2 轮廓系数 (Silhouette Score)
轮廓系数同时考虑了簇内紧凑度和簇间分离度。
- 分数范围 [-1, 1]。
- 越接近 1,聚类效果越好。
fromsklearn.metricsimportsilhouette_score score=silhouette_score(X,labels)print(f"轮廓系数:{score:.2f}")# e.g. 0.82四、实战练习:客户分群
假设我们有一份商场客户数据,包含"年收入"和"消费分数 (1-100)"。
importpandasaspd# 1. 准备数据data={'Annual Income (k$)':[15,15,16,16,17,18],'Spending Score (1-100)':[39,81,6,77,40,6]}# (为了演示,这里只用少量数据,实际中应该是上千条)df=pd.DataFrame(data)# 2. 标准化 (距离敏感,必须标准化)fromsklearn.preprocessingimportStandardScaler scaler=StandardScaler()X_scaled=scaler.fit_transform(df)# 3. 用手肘法找 K# ... (略)# 4. 训练 (假设我们决定 K=3)kmeans=KMeans(n_clusters=3,random_state=42,n_init='auto')df['Cluster']=kmeans.fit_predict(X_scaled)# 5. 可视化plt.figure(figsize=(8,6))sns.scatterplot(data=df,x='Annual Income (k$)',y='Spending Score (1-100)',hue='Cluster',palette='viridis')plt.title("Customer Segments")# plt.show()# 6. 分析结果print(df)# 簇 0: 低收入高消费 (冲动型)# 簇 1: 低收入低消费 (保守型)# 簇 2: ...五、常见问题
Q1:K-Means 对初始点敏感吗?
非常敏感。不同的初始质心可能导致完全不同的聚类结果。
Sklearn 的KMeans默认会跑 10 次(n_init=10),每次用不同的初始点,然后返回最好的那次结果。
Q2:数据不是圆形怎么办?
K-Means 假设簇是凸形的(类似圆形),对于不规则形状(如月牙形)效果很差。
这时候需要用其他聚类算法,如DBSCAN或谱聚类 (Spectral Clustering)。
Q3:如何处理分类特征?
K-Means 是基于欧式距离的,不能直接处理字符串。
需要先将分类特征进行独热编码 (One-Hot)。
六、小结
关键要点:
- K-Means是最简单、最常用的聚类算法。
- 它需要手动指定 K 值,并对数据尺度敏感(记得标准化)。
- 手肘法和轮廓系数是确定 K 值的两大辅助工具。
七、课后作业
- 图片颜色聚类:读取一张彩色图片,将其像素点的 RGB 值作为特征,用 K-Means (K=8) 进行聚类。然后用每个簇的质心颜色替换该簇所有像素点,看看图片变成了什么样(主题色提取)。
- DBSCAN:查阅 sklearn 文档,使用
DBSCAN算法处理make_moons(月牙形)数据集,并与 K-Means 的效果进行对比。 - 文本聚类:将一堆新闻标题进行 TF-IDF 向量化,然后用 K-Means 进行聚类,看看能否自动把"体育"、"财经"等主题分开。
下节预告
Day 87:机器学习进阶 - PCA 降维- 特征太多了怎么办?1000 个维度的特征可能只有 10 个是真正有用的。明天我们学习如何给数据"瘦身",提取核心特征。
系列导航:
- 上一篇:Day 85 - 支持向量机SVM
- 下一篇:Day 87 - PCA降维(待更新)