聚类算法之简介
1 课程概览
本课讲解聚类算法的简介。聚类是无监督学习,有特征无标签,根据样本之间的相似性(距离)进行分类。重点介绍KMeans算法和不同的相似度计算方法。
2 核心概念与定义
- 聚类:无监督学习,有特征无标签,根据相似性分类。
- 相似性:通过距离衡量,距离越短相似性越高。
- 无监督学习:有特征,没有标签。
- 先验知识:事先已有的知识(如现代的动植物分类)。
- KMeans:K均值聚类算法。
3 算法与模型详解
3.1 聚类算法概述
类型:无监督学习
特点:
- 有特征,没有标签
- 根据样本之间的相似性分类
- 自动发现数据集的内在结构和模式
应用场景:顾客数据分群
- 年收入
- 支出
- 可分成五大类:赚少花少、赚多花多等
3.2 相似度计算方法
距离计算方法:
- 欧式距离:对应维度坐标差值的平方和开平方根
- 曼哈顿距离:差值绝对值求和
- 切比雪夫距离:求最大值
- 米氏距离:对其他距离加度量
欧式距离: $$d = \sqrt{\sum_{i=1}^{n}(x_i - y_i)^2}$$
本质:勾股定理(两直角边平方和开平方根求斜边)
3.3 聚类算法评估指标
评估方法:
- SSE:误差平方和
- 肘部法:通过SSE找最佳K值
- SC:轮廓系数
- CH:Calinski-Harabasz指数
与以往评估的区别:
- 线性回归:均方误差、均方根误差、平均绝对误差、最小二乘
- 分类问题:准确率、精确率、召回率、F1值
- 聚类:SSE、肘部法、SC、CH
3.4 聚类算法分类
常见的聚类算法:
- KMeans(K均值)
- 层次聚类
- DBSCAN(密度聚类)
3.5 聚类的目的
目的:在没有先验知识的情况下,自动发现数据集的内在结构和模式
示例:
- 现代有详细的动植物分类(有先验知识)
- 古代没有分类,需要自动发现(无先验知识)
3.6 顾客数据分群案例
特征:
- 年龄(不用)
- 性别(不用)
- 年收入
- 支出
分群结果:
- 赚少花少
- 赚少花多(年轻人)
- 赚多花少(太抠)
- 赚多花多
- 正常(赚多花一般)
4 代码示例
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
# 1. 生成聚类数据
print("=== 1. 生成聚类数据 ===")
X, y_true = make_blobs(
n_samples=300,
centers=4,
cluster_std=0.60,
random_state=42
)
print(f"数据形状: {X.shape}")
print(f"真实类别数: {len(np.unique(y_true))}")
# 2. 距离计算示例
print("\n=== 2. 距离计算示例 ===")
point1 = np.array([1, 2])
point2 = np.array([4, 6])
# 欧式距离
euclidean_dist = np.sqrt(np.sum((point1 - point2) ** 2))
print(f"欧式距离: {euclidean_dist:.4f}")
# 曼哈顿距离
manhattan_dist = np.sum(np.abs(point1 - point2))
print(f"曼哈顿距离: {manhattan_dist:.4f}")
# 切比雪夫距离
chebyshev_dist = np.max(np.abs(point1 - point2))
print(f"切比雪夫距离: {chebyshev_dist:.4f}")
# 米氏距离(p=3)
minkowski_dist = np.sum(np.abs(point1 - point2) ** 3) ** (1/3)
print(f"米氏距离(p=3): {minkowski_dist:.4f}")
# 3. KMeans聚类
print("\n=== 3. KMeans聚类 ===")
kmeans = KMeans(
n_clusters=4,
random_state=42,
n_init=10
)
kmeans.fit(X)
# 预测
y_pred = kmeans.predict(X)
print(f"预测类别数: {len(np.unique(y_pred))}")
# 聚类中心
centers = kmeans.cluster_centers_
print(f"聚类中心形状: {centers.shape}")
print(f"聚类中心:\n{centers}")
# 4. 可视化
print("\n=== 4. 可视化 ===")
plt.figure(figsize=(12, 5))
# 原始数据
plt.subplot(1, 2, 1)
plt.scatter(X[:, 0], X[:, 1], c=y_true, cmap='viridis')
plt.title('原始数据(真实标签)')
# 聚类结果
plt.subplot(1, 2, 2)
plt.scatter(X[:, 0], X[:, 1], c=y_pred, cmap='viridis')
plt.scatter(centers[:, 0], centers[:, 1], c='red', marker='x', s=200, linewidths=3)
plt.title('KMeans聚类结果')
plt.tight_layout()
plt.savefig('kmeans_clustering.png', dpi=100)
print("聚类结果图已保存")
# 5. 顾客数据分群模拟
print("\n=== 5. 顾客数据分群模拟 ===")
np.random.seed(42)
# 模拟顾客数据:年收入和支出
n_customers = 300
# 5个群体
group1 = np.random.randn(60, 2) * 10 + [20, 20] # 赚少花少
group2 = np.random.randn(60, 2) * 10 + [20, 80] # 赚少花多(年轻人)
group3 = np.random.randn(60, 2) * 10 + [50, 50] # 正常
group4 = np.random.randn(60, 2) * 10 + [80, 20] # 赚多花少
group5 = np.random.randn(60, 2) * 10 + [80, 80] # 赚多花多
customer_data = np.vstack([group1, group2, group3, group4, group5])
print(f"顾客数据形状: {customer_data.shape}")
print(f"年收入范围: {customer_data[:, 0].min():.2f} - {customer_data[:, 0].max():.2f}")
print(f"支出范围: {customer_data[:, 1].min():.2f} - {customer_data[:, 1].max():.2f}")
# KMeans聚类
kmeans_customer = KMeans(n_clusters=5, random_state=42, n_init=10)
customer_labels = kmeans_customer.fit_predict(customer_data)
# 可视化
plt.figure(figsize=(10, 8))
scatter = plt.scatter(
customer_data[:, 0], customer_data[:, 1],
c=customer_labels, cmap='viridis', alpha=0.6
)
plt.scatter(
kmeans_customer.cluster_centers_[:, 0],
kmeans_customer.cluster_centers_[:, 1],
c='red', marker='x', s=200, linewidths=3
)
plt.xlabel('年收入(万元)')
plt.ylabel('支出(万元)')
plt.title('顾客数据分群')
plt.colorbar(scatter)
plt.savefig('customer_clustering.png', dpi=100)
print("顾客分群图已保存")
# 6. 不同K值的效果
print("\n=== 6. 不同K值的效果 ===")
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
for idx, k in enumerate([2, 3, 4, 5, 6, 7]):
ax = axes[idx // 3, idx % 3]
kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
labels = kmeans.fit_predict(X)
ax.scatter(X[:, 0], X[:, 1], c=labels, cmap='viridis')
ax.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1],
c='red', marker='x', s=100)
ax.set_title(f'K={k}')
plt.tight_layout()
plt.savefig('different_k_values.png', dpi=100)
print("不同K值效果图已保存")
# 7. 相似度计算函数
print("\n=== 7. 相似度计算函数 ===")
def calculate_similarity(point1, point2, method='euclidean'):
"""计算两个点的相似度(距离)"""
point1 = np.array(point1)
point2 = np.array(point2)
if method == 'euclidean':
# 欧式距离
return np.sqrt(np.sum((point1 - point2) ** 2))
elif method == 'manhattan':
# 曼哈顿距离
return np.sum(np.abs(point1 - point2))
elif method == 'chebyshev':
# 切比雪夫距离
return np.max(np.abs(point1 - point2))
elif method == 'minkowski':
# 米氏距离(p=3)
p = 3
return np.sum(np.abs(point1 - point2) ** p) ** (1/p)
else:
raise ValueError(f"未知距离度量: {method}")
# 测试
p1 = [1, 2]
p2 = [4, 6]
for method in ['euclidean', 'manhattan', 'chebyshev', 'minkowski']:
dist = calculate_similarity(p1, p2, method)
print(f"{method}: {dist:.4f}")
输出示例:
=== 1. 生成聚类数据 ===
数据形状: (300, 2)
真实类别数: 4
=== 2. 距离计算示例 ===
欧式距离: 5.0000
曼哈顿距离: 7.0000
切比雪夫距离: 4.0000
米氏距离(p=3): 4.4979
=== 3. KMeans聚类 ===
预测类别数: 4
聚类中心形状: (4, 2)
=== 5. 顾客数据分群模拟 ===
顾客数据形状: (300, 2)
年收入范围: -5.23 - 105.23
支出范围: -2.45 - 102.34
=== 7. 相似度计算函数 ===
euclidean: 5.0000
manhattan: 7.0000
chebyshev: 4.0000
minkowski: 4.4979
5 重难点与易错提醒
- ❗重点:聚类是无监督学习,有特征无标签。
- ❗重点:相似性通过距离衡量,距离越短相似性越高。
- ❗重点:欧式距离 = 勾股定理。
- ❗重点:聚类评估指标:SSE、肘部法、SC、CH。
- ⚠️易错:混淆聚类和分类(聚类无标签,分类有标签)。
- ⚠️易错:混淆不同的距离计算方法。
- 💡深入理解:聚类是在无先验知识下发现数据内在结构。
6 课堂问答精选
Q: 聚类和分类有什么区别?
A:
- 分类:有监督学习,有标签,预测类别
- 聚类:无监督学习,无标签,根据相似性分组
分类是已知类别,预测新样本属于哪个类别;聚类是不知道类别,自动发现数据的内在结构。
Q: 如何衡量样本之间的相似性?
A: 通过距离衡量:
- 欧式距离:$\sqrt{\sum(x_i - y_i)^2}$(勾股定理)
- 曼哈顿距离:$\sum|x_i - y_i|$
- 切比雪夫距离:$\max|x_i - y_i|$
- 米氏距离:对其他距离加度量
距离越短,相似性越高。
7 本课小结
- 聚类:无监督学习,有特征无标签。
- 相似性:通过距离衡量。
- 距离方法:欧式、曼哈顿、切比雪夫、米氏。
- 评估指标:SSE、肘部法、SC、CH。
- 案例:顾客数据分群。
8 延伸思考与实践
- 实践:运行KMeans聚类示例。
- 预习:聚类算法API。
- 思考:如何选择合适的K值?