高斯混合聚类资产:用软分配把股票分成潜在风格群
k-means 给每只股票发一张唯一的『风格身份证』:要么成长、要么价值,边界上的股票被硬塞进某一边。但真实资产风格是渐变的——一只『半成长半价值』的股票,硬分类会抹掉这个信息。GMM 把每个风格群建模成一个高斯分布,输出的不是标签而是责任度(responsibility):这只股票 62% 像成长、35% 像价值。合成实验:3 个真实风格群、协方差形状各异且相互重叠,GMM 分类准确率 89.0% 碾压 k-means 的 68.3%——因为 k-means 只会画球形边界,而 GMM 的满协方差能贴合拉长、倾斜的簇。更关键的是 GMM 自带『不确定度报告』:15.7% 的资产最大责任度低于 0.8,这些正是躺在风格边界上的股票,k-means 对它们的标签几乎是抛硬币。用 BIC 选簇数,K=3 处 BIC 达到最小值 1755.2,精确对上真实群数。纯 numpy 实现 EM 算法(E 步算责任度、M 步更新均值协方差权重),拆穿 GMM 总比 k-means 好/EM 保证全局最优/责任度=真实概率/K 越大似然越高所以越好/满协方差永远优于对角五类陷阱(中阶)。
先说结论:资产的风格归属不是非黑即白的标签,而是一组连续的隶属度;k-means 强行发身份证,GMM 发的是成分表。 本文合成实验里,3 个协方差形状各异、相互重叠的风格群,GMM 的分类准确率是 89.0%,k-means 只有 68.3%——差距不在算力,而在两个假设:k-means 假设簇是球形的、每个点只属于一个簇;GMM 允许簇被拉长、倾斜,并给每个点一份「62% 成长 + 35% 价值」式的软分配。

一、问题:风格边界上的股票,硬分类等于抛硬币#
给每只股票算一组因子暴露(动量、价值、质量、波动……),它就成了因子空间里的一个点。做风格分群时最常见的动作是直接上 k-means:设 K=3,跑完每只股票领到一个标签——成长、价值、或防御。
问题出在两个地方。
第一,簇不是球。 k-means 的目标函数是最小化点到质心的欧氏距离平方和,隐含假设每个簇是等方差的球形。但真实的风格群几乎总是拉长的:高动量的股票往往也偏高波动,簇沿着「动量-波动」这条斜线延展。k-means 面对一个斜着躺的椭圆簇,会把椭圆两端切给邻近的簇。
第二,边界上的点没有『部分归属』的说法。 一只 PE 中等、增速中等的股票,既有点像成长又有点像价值。k-means 必须把它塞进某一边,塞进哪边取决于它离哪个质心近 0.01——这个标签的信息量约等于抛硬币,但下游使用者看到的是一个和核心资产同样确凿的标签。
GMM(Gaussian Mixture Model,高斯混合模型)同时解决这两件事:每个簇是一个带完整协方差矩阵的高斯分布(可以拉长、倾斜),每个点得到的是对各簇的责任度(responsibility)——一组和为 1 的隶属概率。
二、模型:混合密度与责任度#
GMM 假设数据由 K 个高斯分布混合生成:
其中 是第 k 个成分的混合权重()。生成过程可以想象成:先按权重 掷骰子选一个风格群,再从那个群的高斯分布里抽一个点。
聚类问题反过来问:看到点 ,它来自第 k 个群的后验概率是多少?这就是责任度:
参数用 EM 算法迭代估计——E 步固定参数算责任度,M 步固定责任度更新参数,每轮迭代保证对数似然不降。
三、纯 numpy 实现 EM#
核心不到 40 行。数值上有两个关键点:用 Cholesky 分解算高斯对数密度(避免直接求逆),用 log-sum-exp 技巧算责任度(避免下溢)。
import numpy as np
def log_gauss(X, mu, cov):
d = X.shape[1]
L = np.linalg.cholesky(cov)
diff = X - mu
sol = np.linalg.solve(L, diff.T).T
maha = np.sum(sol**2, axis=1)
logdet = 2 * np.sum(np.log(np.diag(L)))
return -0.5 * (d * np.log(2 * np.pi) + logdet + maha)
def fit_gmm(X, K, iters=200, seed=0, reg=1e-6):
rng = np.random.default_rng(seed)
N, d = X.shape
mu = X[rng.choice(N, K, replace=False)].copy()
cov = np.array([np.cov(X.T) + reg * np.eye(d) for _ in range(K)])
w = np.ones(K) / K
for it in range(iters):
# E 步:log-sum-exp 稳定计算责任度
logp = np.stack([np.log(w[k]) + log_gauss(X, mu[k], cov[k])
for k in range(K)], axis=1)
m = logp.max(axis=1, keepdims=True)
lse = m[:, 0] + np.log(np.exp(logp - m).sum(axis=1))
R = np.exp(logp - lse[:, None]) # N x K 责任度
# M 步:加权更新参数
Nk = R.sum(axis=0)
w = Nk / N
mu = (R.T @ X) / Nk[:, None]
for k in range(K):
diff = X - mu[k]
cov[k] = (R[:, k][:, None] * diff).T @ diff / Nk[k] \
+ reg * np.eye(d)
return mu, cov, w, Rpython注意 M 步协方差里加了 reg * np.eye(d):如果某个成分「塌缩」到单个点上,协方差趋于奇异、似然发散到无穷——这是 GMM 最经典的病态解,正则项是最便宜的保险。
四、实验:3 个重叠风格群,GMM 89.0% vs k-means 68.3%#
合成 300 只「资产」,在(动量暴露, 价值暴露)二维因子空间里由 3 个真实高斯群生成,混合权重 (0.30, 0.30, 0.40)。三个群刻意设计成协方差形状各异——一个正相关拉长、一个负相关拉长、一个扁平横躺——且均值相互靠近,制造真实世界里常见的边界重叠。
结果(与真实标签做最优排列匹配后):
| 方法 | 分类准确率 | 边界处理 |
|---|---|---|
| k-means(硬分配,球形) | 68.3% | 椭圆簇两端被切错 |
| GMM(软分配,满协方差) | 89.0% | 贴合拉长簇形状 |

左图 k-means 的错误集中在拉长簇的两端——那些点离「别人家的质心」更近,被硬划过去。右图 GMM 的椭圆等高线贴着簇的真实形状走,边界处的点颜色是混合色——那正是软分配在起作用。
参数还原也够硬:估计的混合权重 (0.275, 0.326, 0.398),对上真实的 (0.30, 0.30, 0.40);EM 迭代 94 轮收敛,最终对数似然 -829.1。
软分配的独有价值:不确定度自报告。 每个点的最大责任度就是模型对这个分类的信心。实验里 15.7% 的资产最大责任度低于 0.8——这些正是躺在风格边界上的股票。k-means 对它们同样给出标签,但你无从知道哪些标签是抛硬币抛出来的;GMM 直接把「我不确定」写在脸上。

在组合构建里这个信息很值钱:风格轮动策略可以只对高置信资产下重注,对边界资产降权或用责任度加权——而不是把 51% 像成长的股票当成 100% 的成长股来交易。
五、选 K:BIC 在 K=3 触底#
GMM 的似然随 K 单调不降——成分越多拟合越好,这是废话。要惩罚复杂度,标准做法是 BIC:
其中 p 是参数个数(K 个均值 + K 个协方差 + K−1 个权重)。对 K=1…6 各跑 5 个随机初始化取最优:
| K | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| BIC | 1843.5 | 1810.3 | 1755.2 | 1778.9 | 1806.7 | 1828.1 |

BIC 在 K=3 精确触底,之后掉头向上——增加第 4 个成分带来的似然提升(-829.1 → -823.9)不足以抵消参数惩罚。真实群数被干净地识别出来。
六、五个常见陷阱#
陷阱一:「GMM 总比 k-means 好」。 不是。GMM 每个成分要估 d(d+1)/2 个协方差参数,样本少、维度高时方差巨大,反而不如 k-means 稳。粗略经验:每个簇的有效样本数低于 10d 时,先考虑对角协方差或直接 k-means。
陷阱二:「EM 收敛 = 全局最优」。 EM 只保证似然单调不降,收敛到的是局部极大。不同初始化可能差很远——本文 BIC 实验里每个 K 跑 5 个种子取最优,生产环境建议更多重启,或用 k-means 结果热启动。
陷阱三:「责任度就是真实概率」。 责任度是模型内的后验概率,前提是「数据真由 K 个高斯混合生成」。真实资产收益是厚尾的,高斯假设下尾部点的责任度会被扭曲。把 0.62 读成「精确的 62% 概率」是过度解读;读成「明显偏向但不笃定」才合适。
陷阱四:「K 越大似然越高,所以多多益善」。 似然单调上升是数学必然,不是信息增加。不做 BIC/AIC 惩罚直接看似然选 K,永远会选到上限。更隐蔽的版本是用同一份数据既选 K 又评估效果——选择偏差直接进结果。
陷阱五:「满协方差永远优于对角」。 满协方差表达力强,但参数是对角版的 (d+1)/2 倍。因子维度上到 20+ 时,满协方差 GMM 很容易过拟合出「贴着噪声长的椭圆」。先跑对角版做基线,满协方差版 BIC 没有明显更好就不要上。
七、工程要点#
- 协方差正则必须加:
cov += 1e-6 * I防塌缩,这不是可选项。 - log 域计算:责任度必须用 log-sum-exp,直接乘密度在 30 维以上必然下溢。
- 标准化先行:因子量纲不统一时,方差大的因子会主导协方差估计,先 z-score。
- 标签对齐:GMM 的成分编号是任意的,跨期滚动聚类时要用匈牙利算法或均值最近邻把本期成分对齐到上期,否则「风格 1」的含义每期都在漂。
GMM 不是万能聚类器,它是「你愿意假设群内近似高斯」换来的软分配 + 形状自适应 + 不确定度报告。这三样东西,恰好都是硬分类给不了的。