03 - 无监督学习¶
🎯 无监督学习概述¶
核心思想¶
数据没有标签,算法自动发现数据内在结构和模式
三大任务类型¶
1. 聚类 (Clustering)
将相似样本分组
例: 客户分群、新闻聚合
2. 降维 (Dimensionality Reduction)
压缩数据维度,保留主要信息
例: 可视化、特征提取
3. 密度估计 (Density Estimation)
估计数据分布
例: 异常检测、生成模型
📊 聚类算法¶
K-Means 聚类¶
核心思想¶
将数据分成K个簇,使簇内样本相似,簇间样本差异大
算法步骤¶
初始化:
迭代:
重复直到收敛:
1. 分配: 每个样本分配到最近中心
中心1: ●●●●●
中心2: ○○○
中心3: ▲▲▲▲
2. 更新: 重新计算每个簇的中心
新中心 = 簇内所有点的均值
3. 检查: 中心不再变化则停止
目标函数 (损失函数)¶
- \(\mu_i\): 第i个簇的中心
- 目标: 最小化所有点到其所属中心的距离平方和
K值选择¶
肘部法则 (Elbow Method):
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
# 计算不同K值的损失
losses = []
K_range = range(1, 11)
for k in K_range:
kmeans = KMeans(n_clusters=k, random_state=42)
kmeans.fit(X)
losses.append(kmeans.inertia_) # 损失函数值
# 绘制肘部图
plt.plot(K_range, losses, 'bo-')
plt.xlabel('K值')
plt.ylabel('损失 (SSE)')
plt.show()
# 找到"肘部" (下降速度明显变缓的点)
轮廓系数 (Silhouette Score):
轮廓系数 = (b - a) / max(a, b)
a: 样本到同簇其他点的平均距离 (簇内相似度)
b: 样本到最近异簇点的平均距离 (簇间分离度)
范围: [-1, 1]
- 接近1: 聚类效果好
- 接近0: 样本在簇边界
- 接近-1: 聚类效果差
代码实现¶
from sklearn.cluster import KMeans
import numpy as np
# 示例数据
X = np.array([
[1, 2], [1, 4], [1, 0],
[10, 2], [10, 4], [10, 0]
])
# 训练
kmeans = KMeans(
n_clusters=2, # 簇的数量
init='k-means++', # 初始化方法
max_iter=300, # 最大迭代次数
random_state=42
)
kmeans.fit(X)
# 预测
labels = kmeans.labels_ # 每个样本的簇标签
centers = kmeans.cluster_centers_ # 簇中心
print(f"簇标签: {labels}")
print(f"簇中心:\n{centers}")
# 预测新样本
new_samples = [[1, 1], [10, 3]]
predictions = kmeans.predict(new_samples)
print(f"预测: {predictions}")
优缺点¶
优点: - 简单易实现,速度快 - 可解释性强 - 适合凸形簇
缺点: - 需要预先指定K值 - 对初始中心敏感 (可用K-Means++缓解) - 对异常值敏感 - 只能发现球形簇 (不适合非凸形状)
层次聚类 (Hierarchical Clustering)¶
核心思想¶
通过逐步合并或分裂簇,构建聚类层次树
两种策略¶
凝聚 (自底向上):
1. 初始: 每个样本是一个簇
2. 迭代: 合并最相似的两个簇
3. 终止: 所有样本合并为一个簇
示例:
步骤1: 🔴🔴🔴🔴🔴 (5个簇)
步骤2: 🔴🔴🔴 🔴🔴 (4个簇)
步骤3: 🔴🔴🔴🔴 (3个簇)
步骤4: 🔴🔴 🔴🔴 (2个簇)
步骤5: 🔴🔴🔴🔴🔴 (1个簇)
分裂 (自顶向下):
链接方式 (Linkage)¶
如何计算簇间距离?
单链接 (Single Linkage):
两个簇中最近样本的距离
优点: 能发现非凸形状
缺点: 链式效应 (可能将不应该连接的点连起来)
完全链接 (Complete Linkage):
两个簇中最远样本的距离
优点: 避免链式效应
缺点: 对异常值敏感
平均链接 (Average Linkage):
两个簇所有样本对的平均距离
优点: 折中方案
缺点: 计算量大
Ward链接:
合并后簇内方差增加最小的两个簇
优点: 效果通常最好
缺点: 只适合欧式距离
代码实现¶
from sklearn.cluster import AgglomerativeClustering
from scipy.cluster.hierarchy import dendrogram, linkage
import matplotlib.pyplot as plt
# 凝聚层次聚类
agg = AgglomerativeClustering(
n_clusters=3, # 簇的数量
linkage='ward' # 链接方式
)
labels = agg.fit_predict(X)
# 绘制层次树
linkage_matrix = linkage(X, method='ward')
plt.figure(figsize=(10, 7))
dendrogram(linkage_matrix)
plt.title('层次聚类树')
plt.xlabel('样本索引')
plt.ylabel('距离')
plt.show()
优缺点¶
优点: - 不需要指定K值 (可通过层次树确定) - 可视化层次结构 - 能发现不同尺度的簇
缺点: - 计算复杂度高 O(n³) 或 O(n²) - 一旦合并/分裂不能撤销 - 对噪声敏感
📉 降维算法¶
PCA (主成分分析, Principal Component Analysis)¶
核心思想¶
找到数据方差最大的方向,将高维数据投影到低维空间
数学原理¶
步骤1: 数据标准化 $\(x_i' = \frac{x_i - \mu}{\sigma}\)$
步骤2: 计算协方差矩阵 $\(C = \frac{1}{n}X^TX\)$
步骤3: 特征值分解 $\(C \cdot v = \lambda \cdot v\)$ - \(v\): 特征向量 (主成分方向) - \(\lambda\): 特征值 (该方向的方差)
步骤4: 选择前k个主成分 选择特征值最大的k个特征向量
步骤5: 投影 $\(X_{new} = X \cdot V_k\)$
解释方差 (Explained Variance)¶
每个主成分解释的方差比例:
方差比例 = 特征值 / 所有特征值之和
累计解释方差:
前k个主成分的累计方差比例
示例:
主成分1: 50% 方差
主成分2: 30% 方差
主成分3: 10% 方差
前2个累计: 80% (保留80%信息)
代码实现¶
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
# 标准化 (重要!)
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# PCA降维
pca = PCA(n_components=2) # 降到2维
X_pca = pca.fit_transform(X_scaled)
# 查看解释方差
print(f"各主成分解释方差: {pca.explained_variance_ratio_}")
print(f"累计解释方差: {pca.explained_variance_ratio_.cumsum()}")
# 可视化
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=labels)
plt.xlabel(f'主成分1 ({pca.explained_variance_ratio_[0]:.2%})')
plt.ylabel(f'主成分2 ({pca.explained_variance_ratio_[1]:.2%})')
plt.title('PCA降维可视化')
plt.show()
# 选择保留95%方差
pca_95 = PCA(n_components=0.95)
X_pca_95 = pca_95.fit_transform(X_scaled)
print(f"保留95%方差需要 {pca_95.n_components_} 个主成分")
应用场景¶
- 可视化: 高维数据投影到2D/3D
- 特征压缩: 减少特征数量,加速训练
- 噪声过滤: 丢弃方差小的主成分 (通常是噪声)
- 图像压缩: 人脸识别 (Eigenfaces)
优缺点¶
优点: - 数学原理清晰 - 无参数需要调优 - 降维效果好
缺点: - 只能捕获线性关系 - 对数据尺度敏感 (必须标准化) - 主成分可解释性差
t-SNE (t-Distributed Stochastic Neighbor Embedding)¶
核心思想¶
保持数据间的局部相似性,适合可视化高维数据
算法原理¶
步骤1: 在高维空间计算样本间相似度
步骤2: 在低维空间也计算相似度
步骤3: 最小化两个分布的差异
代码实现¶
from sklearn.manifold import TSNE
# t-SNE降维到2维
tsne = TSNE(
n_components=2, # 只支持2或3维
perplexity=30, # 类似"最近邻数量" (5-50)
n_iter=1000, # 迭代次数
random_state=42
)
X_tsne = tsne.fit_transform(X)
# 可视化
plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=labels, cmap='viridis')
plt.colorbar()
plt.title('t-SNE可视化')
plt.show()
超参数: Perplexity¶
优缺点¶
优点: - 能发现复杂的非线性结构 - 可视化效果好 - 保持局部邻域关系
缺点: - 计算慢 (不适合大数据) - 结果不稳定 (每次运行不同) - 不能用于新数据 (需要重新训练) - 全局结构可能失真
🎲 EM算法与高斯混合模型¶
EM算法 (Expectation-Maximization)¶
核心思想¶
用于含有隐变量的概率模型参数估计
问题:
如果数据有缺失值或隐变量,
如何估计模型参数?
示例:
身高数据,但不知道性别 (隐变量),
如何估计男生和女生的身高分布?
EM算法:
E步: 估计隐变量的期望
M步: 基于期望更新参数
迭代直到收敛
算法流程¶
初始化: 随机初始化参数 θ
重复:
E步 (Expectation):
计算隐变量的后验概率
Q(θ|θ⁽ᵗ⁾) = E[log P(X,Z|θ) | X, θ⁽ᵗ⁾]
M步 (Maximization):
最大化Q函数,更新参数
θ⁽ᵗ⁺¹⁾ = argmax Q(θ|θ⁽ᵗ⁾)
直到收敛 (参数变化很小)
直观示例: 抛硬币¶
问题:
有两枚硬币A和B,随机选一个抛10次,
重复5次实验,得到如下结果 (但不知道每次是A还是B):
实验1: 正正反正反正正正正反
实验2: 反正正反正反反正正
...
EM算法:
E步: 根据当前参数,估计每次实验是A还是B的概率
M步: 更新A和B的正面概率
迭代:
初始: P(A正面)=0.6, P(B正面)=0.5
E步: 实验1有0.7概率是A, 0.3概率是B
M步: 更新P(A正面)=0.65, P(B正面)=0.48
...
收敛: 得到最终估计
高斯混合模型 (Gaussian Mixture Model, GMM)¶
核心思想¶
用多个高斯分布的加权和来建模数据分布
数学模型¶
- \(\pi_k\): 第k个高斯的混合系数 (权重)
- \(\mu_k\): 第k个高斯的均值
- \(\Sigma_k\): 第k个高斯的协方差矩阵
约束: $\(\sum_{k=1}^{K} \pi_k = 1\)$
用EM算法求解GMM¶
参数: \(\theta = \{\pi_k, \mu_k, \Sigma_k\}_{k=1}^{K}\)
E步(Expectation):计算每个样本 \(x_n\) 属于第 \(k\) 个高斯的后验概率(责任度):
其中 \(\mathcal{N}(x | \mu, \Sigma) = \frac{1}{(2\pi)^{d/2}|\Sigma|^{1/2}} \exp\left(-\frac{1}{2}(x-\mu)^T\Sigma^{-1}(x-\mu)\right)\)
M步(Maximization):利用责任度更新参数。
完整推导:M 步的目标是最大化 Q 函数:
对 \(\mu_k\) 求导并令其为零:
解出:
对 \(\Sigma_k\) 求导并令其为零(利用 \(\frac{\partial}{\partial \Sigma^{-1}} \left[-\frac{1}{2}(x-\mu)^T \Sigma^{-1}(x-\mu) + \frac{1}{2}\ln|\Sigma^{-1}|\right] = 0\)):
对 \(\pi_k\) 采用拉格朗日乘子法(约束 \(\sum_k \pi_k = 1\)):
记 \(N_k = \sum_{n=1}^{N} \gamma(z_{nk})\)(第 \(k\) 个高斯的有效样本数),以上结果即:
反复迭代 E 步和 M 步,直到对数似然 \(\log P(X|\theta)\) 收敛。
代码实现¶
from sklearn.mixture import GaussianMixture
# 拟合GMM
gmm = GaussianMixture(
n_components=3, # 高斯数量 (簇数)
covariance_type='full', # 协方差类型
max_iter=100,
random_state=42
)
gmm.fit(X)
# 预测簇标签
labels = gmm.predict(X)
# 预测概率 (软聚类)
proba = gmm.predict_proba(X)
# proba[i, k] = 样本i属于簇k的概率
# 查看参数
means = gmm.means_ # 各高斯的均值
covariances = gmm.covariances_ # 各高斯的协方差
weights = gmm.weights_ # 混合系数
print(f"各高斯权重: {weights}")
print(f"各高斯均值:\n{means}")
GMM vs K-Means¶
K-Means:
- 硬聚类 (每个点只属于一个簇)
- 假设簇是球形的
- 基于距离 (几何方法)
- 快速简单
GMM:
- 软聚类 (每个点有属于各簇的概率)
- 假设簇是椭球形的 (通过协方差)
- 基于概率 (统计方法)
- 更灵活但更慢
优缺点¶
优点: - 软聚类 (提供概率) - 能建模不同形状的簇 (通过协方差) - 能生成新样本 (从分布采样)
缺点: - 需要指定簇数量 - 对初始化敏感 - 假设数据符合高斯分布 (可能不成立) - 计算复杂度高
📊 算法对比¶
聚类算法对比¶
| 算法 | 簇形状 | 簇数量 | 速度 | 可扩展性 | 适用场景 |
|---|---|---|---|---|---|
| K-Means | 球形 | 需指定 | ⭐⭐⭐⭐⭐ | 好 | 凸形簇,大数据 |
| 层次聚类 | 任意 | 可选 | ⭐⭐ | 差 | 小数据,需层次 |
| GMM | 椭球形 | 需指定 | ⭐⭐⭐ | 中 | 软聚类,密度估计 |
降维算法对比¶
| 算法 | 类型 | 保持结构 | 速度 | 用途 |
|---|---|---|---|---|
| PCA | 线性 | 全局方差 | ⭐⭐⭐⭐⭐ | 降维,特征压缩 |
| t-SNE | 非线性 | 局部邻域 | ⭐⭐ | 可视化 |
| LDA | 线性 | 类别可分性 | ⭐⭐⭐⭐ | 监督降维 |
🎯 实践建议¶
聚类评估指标¶
from sklearn.metrics import silhouette_score, calinski_harabasz_score
# 轮廓系数 (-1到1,越大越好)
score = silhouette_score(X, labels)
# Calinski-Harabasz指数 (越大越好)
score_ch = calinski_harabasz_score(X, labels)
降维实践流程¶
下一步¶
继续阅读 04-时序模型.md,学习时间序列分析!