一个样本能同时像两个簇吗?高斯混合模型与 EM 软聚类
从重叠客群的硬分配不足出发,推导高斯混合模型的责任度与 EM 更新,手算一轮软聚类,并实现数值稳定、可诊断的概率聚类。
上一篇的 PCA 用连续坐标压缩数据;更早的 K 均值则把每个样本硬分给唯一中心。但在两类用户的消费行为明显重叠时,“只属于 A 或 B”会抹掉边界样本的不确定性,而且圆形等方差簇也未必符合真实几何。
高斯混合模型(Gaussian Mixture Model,GMM)假设数据由若干高斯成分混合生成。它不只学习中心,还学习成分比例与协方差,并输出每个样本来自各成分的后验概率。本文只讲透三个紧密环节:混合似然、软责任度,以及期望最大化(Expectation-Maximization,EM)的 E 步—M 步循环。
01 为什么“最近中心”表达不了重叠?#
设两个顾客与两类中心的距离几乎相同。K 均值仍必须输出标签 0 或 1;标签在分界线两侧会突然翻转,也无法回答“模型有多确定”。GMM 改为描述生成过程:
先选潜在成分 zᵢ 再由该成分生成 xᵢ
π = [π₁,...,πₖ] ──抽样──► zᵢ ──选择 μₖ,Σₖ──► xᵢ [D]
│
观测训练数据只有 X [N,D] ◄─────┘ zᵢ 未被观测
推理:xᵢ ──各成分密度──► 未归一化证据 [K]
└────────► 责任度 γᵢ [K],各项和为 1text对第 个样本,先以概率 选择成分 ,再生成:
其中 ,,混合权重满足 且 。
02 混合模型优化的是什么?#
边缘化看不见的 ,单个样本密度是:
训练最大化全部样本的对数似然(Log-Likelihood):
困难来自“对数里面还有求和”。若每个 已知,就能分别统计每个成分;若参数已知,又能推断 。EM 正是交替解决这两个容易的子问题。
03 E 步怎样把证据变成责任度?#
给定旧参数 ,E 步计算后验:
称为责任度(Responsibility)矩阵。每行和为 1;每列和
是成分 的有效样本数,不必是整数。与 K 均值的 one-hot 分配相比, 保留了重叠区域的不确定性。
04 M 步为何是加权均值与协方差?#
M 步固定 ,最大化完整数据对数似然的期望。更新式为:
数据流与形状如下:
| 量 | 形状 | 含义 |
|---|---|---|
| 个 维样本 | ||
| 每个样本对每个成分的责任度 | ||
| 各成分有效样本数 | ||
| 各成分均值 | ||
full 协方差 | ||
| 混合权重 |
EM 每轮不会降低训练似然,但只保证走向局部最优或鞍点附近;初始化不同,答案可能不同。
05 用三个一维点手算一轮 E 步#
取 ,两个成分初始权重都为 ,均值 、,方差都为 1。省略两个成分共有的 ,高斯密度只需比较 。
对 ,两个未归一化证据为:
所以 、。同理:
于是 ,新的权重仍为 ,均值更新为:
对称地 。中间点没有被武断地独占,而是向两个均值各贡献 个样本。
06 训练与推理的完整伪代码#
输入:X [N,D],成分数 K
初始化:π [K],μ [K,D],Σ [K,D,D]
repeat:
# E 步:必须在 log 空间计算
log_joint[i,k] = log π[k] + log Normal(X[i] | μ[k], Σ[k])
log_norm[i] = logsumexp(log_joint[i,:])
Γ[i,k] = exp(log_joint[i,k] - log_norm[i])
# M 步
N_k[k] = sum_i Γ[i,k]
π[k] = N_k[k] / N
μ[k] = sum_i Γ[i,k] X[i] / N_k[k]
Σ[k] = weighted covariance around new μ[k] + reg_covar · I
lower_bound = mean_i log_norm[i]
until lower_bound improvement < tol or max_iter reached
输出:π、μ、Σ;推理时重新执行 E 步得到概率 [Q,K]text这里的 logsumexp 先减最大值再求指数,避免高维高斯密度下溢到 0。直接计算许多很小的密度再相除,常会得到 0 / 0 -> NaN。
07 用 NumPy 写出可检查的一维 EM#
下面刻意限制为一维、对角方差,让更新本体保持透明:
import numpy as np
X = np.array([[0.0], [1.0], [2.0]]) # [N=3,D=1]
means = np.array([[0.0], [2.0]]) # [K=2,D=1]
variances = np.ones((2, 1)) # [K,D]
weights = np.array([0.5, 0.5]) # [K]
for _ in range(20):
diff = X[:, None, :] - means[None, :, :] # [N,K,D]
log_gaussian = -0.5 * (
np.log(2 * np.pi * variances)[None, :, :]
+ diff**2 / variances[None, :, :]
).sum(axis=2) # [N,K]
log_joint = np.log(weights)[None, :] + log_gaussian
row_max = log_joint.max(axis=1, keepdims=True)
log_norm = row_max + np.log(
np.exp(log_joint - row_max).sum(axis=1, keepdims=True)
) # [N,1]
responsibilities = np.exp(log_joint - log_norm) # [N,K]
effective_count = responsibilities.sum(axis=0) # [K]
weights = effective_count / X.shape[0]
means = responsibilities.T @ X / effective_count[:, None]
diff = X[:, None, :] - means[None, :, :]
variances = (
responsibilities[:, :, None] * diff**2
).sum(axis=0) / effective_count[:, None]
variances = np.maximum(variances, 1e-6)
assert responsibilities.shape == (3, 2)
assert np.allclose(responsibilities.sum(axis=1), 1.0)
assert np.isclose(weights.sum(), 1.0)
assert np.all(variances > 0)python多维 full 协方差还需稳定计算 log-determinant 与线性方程,生产代码不应手写矩阵逆。
08 用 scikit-learn 1.9 正确落地#
当前官方 GaussianMixture API ↗ 提供 predict_proba、逐样本 score_samples、平均对数似然 score、AIC/BIC 与收敛属性:
import numpy as np
from sklearn.mixture import GaussianMixture
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
model = make_pipeline(
StandardScaler(),
GaussianMixture(
n_components=2,
covariance_type='full',
init_params='k-means++',
n_init=10,
reg_covar=1e-6,
tol=1e-3,
max_iter=300,
random_state=42,
),
)
model.fit(X_train) # X_train [N,D]
gmm = model.named_steps['gaussianmixture']
prob = model.predict_proba(X_new) # [Q,K]
label = model.predict(X_new) # [Q],argmax(prob)
log_density = model.score_samples(X_new) # [Q]
assert prob.shape == (X_new.shape[0], 2)
assert np.allclose(prob.sum(axis=1), 1.0)
assert gmm.means_.shape == (2, X_train.shape[1])
assert gmm.covariances_.shape == (2, X_train.shape[1], X_train.shape[1])
print(gmm.converged_, gmm.n_iter_, gmm.lower_bound_)pythonfit_predict 的最终标签可能与“先 fit 再 predict(X_train)”在边界样本上略有不同,因为前者返回最后一次 E 步的标签,而参数还可能在最后一次 M 步改变。若业务需要最终参数下的统一推理语义,应显式 fit 后调用 predict 或 predict_proba。
09 协方差类型控制什么几何?#
covariance_type | 参数形状 | 等概率线几何 | 代价与偏差 |
|---|---|---|---|
spherical | 每簇一个半径的圆/球 | 最省参数,限制最强 | |
diag | 轴对齐椭圆 | 不能表达特征相关 | |
tied | 各簇共享形状 | 类似共享协方差思想 | |
full | 每簇可旋转椭圆 | 最灵活,也最易过拟合 |
full 的协方差参数量随 增长。小样本高维数据中,应先考虑降维、diag/tied、更强 reg_covar 或带先验的 BayesianGaussianMixture,而不是默认使用最自由的模型。
10 怎样选择 K,而不是只看训练似然?#
增加成分几乎总能提高训练似然,因此不能用它单独选 。常用贝叶斯信息准则(Bayesian Information Criterion,BIC):
是自由参数数目,值越小越好。还应结合留出集平均对数似然、成分稳定性、最小有效样本数和业务可解释性:
from sklearn.mixture import GaussianMixture
candidates = []
for k in range(1, 7):
gm = GaussianMixture(
n_components=k,
covariance_type='full',
n_init=10,
reg_covar=1e-5,
random_state=42,
).fit(X_train_scaled)
candidates.append({
'k': k,
'bic_train': gm.bic(X_train_scaled),
'valid_log_likelihood': gm.score(X_valid_scaled),
'smallest_weight': gm.weights_.min(),
'converged': gm.converged_,
})pythonBIC 假设候选模型与独立同分布数据足够吻合;它不是“真实簇数探测器”。
11 最常见的失败与调试路径#
- 协方差塌缩:某成分抓住单个点,方差趋近 0、似然趋向无穷。检查最小特征值、
weights_,提高reg_covar。 - 没有收敛:检查
converged_、n_iter_和警告;增加max_iter前先缩放特征、换初始化并排查异常值。 - 局部最优:提高
n_init,比较不同种子的lower_bound_和留出似然,不只看一次结果。 - 概率过度解读:
predict_proba是模型假设下的成分后验,不是经真实类别校准的置信度。 - 数据泄漏:缩放器只能在训练集拟合;时序数据必须用过去训练、未来验证。
- 离群点牵引:高斯尾部仍可能用巨大协方差解释异常点;检查稳健预处理或显式异常模型。
- 维度过高:样本协方差近奇异;画特征值谱,核对每个成分的有效样本数是否远小于维数。
线上至少监控平均 score_samples、低密度样本比例、成分权重、均值漂移、协方差特征值和最大责任度分布。最大责任度普遍下降,可能表示簇开始重叠或出现了训练外模式。
12 GMM 与相近方法的边界#
| 方法 | 分配/表示 | 核心假设 | 主要边界 |
|---|---|---|---|
| K 均值 | 最近中心硬分配 | 近似等方差球形簇 | 不给概率,不建模协方差 |
| GMM | 后验概率软分配 | 有限个高斯混合 | 需给上限或 ,怕奇异与异常值 |
| 核密度估计 | 每个样本贡献核 | 平滑密度 | 不直接产生少数全局成分 |
| LDA/QDA | 有标签的类后验 | 类条件高斯 | 是监督分类,标签已知 |
| DBSCAN | 密度连通与噪声 | 局部密度阈值 | 可找非凸簇,但不输出生成概率 |
当真实结构是月牙、环或不同密度的连通区域时,多加几个高斯也许能近似密度,却未必给出符合语义的簇。
13 今天真正需要记住什么?#
GMM 用 、 和 描述多个高斯成分;E 步把每个样本的成分证据归一化为责任度,M 步用责任度做加权统计。EM 单调改善训练目标,却不保证全局最优,也不能自动证明簇真实存在。可靠实践需要 log 空间计算、多次初始化、协方差正则、留出似然与稳定性诊断。
14 思考题与小练习#
- 延续三点例子,用更新后的两个均值和原方差再做一次 E 步。中间点责任度是否改变?两端样本为何变得更不确定或更确定?
- 对同一二维数据分别拟合
spherical、diag、tied与fullGMM,列出参数形状、BIC、留出似然和最小协方差特征值。 - 人为加入一个远离主体的孤立点,逐渐减小
reg_covar,观察成分权重、协方差行列式与训练似然;解释“似然更高但模型更坏”的原因。
相关工作#
- Dempster, Laird & Rubin (1977), Maximum Likelihood from Incomplete Data via the EM Algorithm ↗:EM 算法的经典统一表述。
- Redner & Walker (1984), Mixture Densities, Maximum Likelihood and the EM Algorithm ↗:有限混合模型极大似然与 EM 的系统综述。
- Schwarz (1978), Estimating the Dimension of a Model ↗:BIC 的理论来源。
- Tipping & Bishop (1999), Probabilistic Principal Component Analysis ↗:把上一篇 PCA 放入概率潜变量模型。
15 下一篇预告#
GMM 能把圆团推广成重叠椭圆,却仍用若干参数化分布解释全部样本。下一篇将研究 DBSCAN:不预先指定簇数,只用 邻域、核心点和密度可达关系,怎样沿弯曲形状扩展簇并把稀疏点标为噪声。