作为一名数据科学家,我经常遇到需要将无标签数据进行分组的场景。传统的K-means算法简单高效,但它假设所有簇都是圆形且大小相同,这在实际数据中往往不成立。今天我要分享的是混合高斯模型(Gaussian Mixture Model, GMM)这种更强大的聚类方法,它能识别椭圆形、不同大小甚至部分重叠的簇。
GMM的核心思想是将数据看作由多个高斯分布混合生成的。与K-means的"硬分配"不同,GMM属于"软聚类"——它会计算每个样本属于各个簇的概率。这种特性使得GMM在现实场景中表现更加灵活,比如在金融客户分群、医学图像分割等领域都有广泛应用。
高斯分布(正态分布)是统计学中最基础的概率分布之一,其概率密度函数为:
code复制p(x|μ,Σ) = (1/((2π)^d/2 |Σ|^1/2)) * exp(-1/2 (x-μ)^T Σ^-1 (x-μ))
其中μ是均值向量,Σ是协方差矩阵。在二维情况下,这个公式描述的就是我们熟悉的"钟形曲线"。
混合高斯模型则是多个高斯分布的线性叠加:
code复制p(x) = Σ π_k * p_k(x|μ_k,Σ_k)
π_k是第k个高斯分布的混合系数(权重),满足Σπ_k=1。通过调整这些参数,GMM可以拟合各种复杂的数据分布。
软聚类 vs 硬聚类:
簇形状假设:
密度感知:
实践建议:当数据明显不是球形分布,或者需要概率解释时,优先选择GMM。对于超大规模数据,K-means计算效率更高。
首先确保安装必要的Python库:
bash复制pip install numpy pandas matplotlib seaborn scikit-learn
数据加载函数设计考虑以下几点:
python复制def load_and_preprocess_data(file_path=None):
if file_path:
print(f"正在加载数据: {file_path}")
if file_path.endswith('.csv'):
df = pd.read_csv(file_path)
elif file_path.endswith('.xlsx'):
df = pd.read_excel(file_path)
else:
raise ValueError("不支持的文件格式")
X = df.values
feature_names = df.columns.tolist()
else:
# 生成三个不同形态的高斯簇
np.random.seed(42)
n_samples = 600
X1 = np.random.multivariate_normal(mean=[2, 2], cov=[[1, 0.5], [0.5, 1]], size=n_samples//3)
X2 = np.random.multivariate_normal(mean=[-2, -2], cov=[[1.5, 0], [0, 1.5]], size=n_samples//3)
X3 = np.random.multivariate_normal(mean=[2, -3], cov=[[1, -0.5], [-0.5, 1]], size=n_samples//3)
X = np.vstack([X1, X2, X3])
feature_names = ['Feature_1', 'Feature_2']
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
return X, X_scaled, feature_names
选择聚类数K是GMM应用中的关键问题。我们使用BIC(贝叶斯信息准则)和轮廓系数双重评估:
python复制def find_best_k(X_scaled, k_range=range(2, 8)):
bic_scores = []
silhouettes = []
for k in k_range:
gmm = GaussianMixture(n_components=k, covariance_type='full', random_state=42, n_init=10)
gmm.fit(X_scaled)
bic_scores.append(gmm.bic(X_scaled))
labels = gmm.predict(X_scaled)
if len(np.unique(labels)) > 1:
silhouettes.append(silhouette_score(X_scaled, labels))
else:
silhouettes.append(-1)
# 可视化评估曲线
plt.figure(figsize=(10, 4))
plt.subplot(121)
plt.plot(k_range, bic_scores, 'bo-')
plt.xlabel('聚类数量 K')
plt.ylabel('BIC 分数')
plt.subplot(122)
plt.plot(k_range, silhouettes, 'go-')
plt.xlabel('聚类数量 K')
plt.ylabel('轮廓系数')
best_k = k_range[np.argmin(bic_scores)]
return best_k
BIC的计算公式为:
code复制BIC = -2 * log_likelihood + k * log(n)
其中k是模型参数数量,n是样本数。BIC越小表示模型越好,它在拟合优度和模型复杂度之间取得了平衡。
设置GMM的关键参数:
python复制gmm = GaussianMixture(
n_components=best_k,
covariance_type='full', # 最灵活但计算量最大
random_state=42,
n_init=10, # 避免局部最优
max_iter=300,
tol=1e-4 # 收敛阈值
)
gmm.fit(X_scaled)
# 获取软聚类结果
probabilities = gmm.predict_proba(X_scaled)
print(f"样本0的类别概率: {probabilities[0]}")
评估指标解读:
对于高维数据,我们使用PCA降维展示:
python复制def visualize_results(X_raw, X_scaled, labels, gmm, feature_names):
if X_scaled.shape[1] > 2:
pca = PCA(n_components=2, random_state=42)
X_plot = pca.fit_transform(X_scaled)
x_label = f'PC1 ({pca.explained_variance_ratio_[0]:.1%})'
y_label = f'PC2 ({pca.explained_variance_ratio_[1]:.1%})'
else:
X_plot = X_scaled
x_label, y_label = feature_names
plt.figure(figsize=(10, 8))
scatter = plt.scatter(X_plot[:,0], X_plot[:,1], c=labels, cmap='viridis', alpha=0.7)
# 绘制置信椭圆
if X_scaled.shape[1] == 2:
for i in range(gmm.n_components):
v, w = np.linalg.eigh(gmm.covariances_[i])
angle = np.degrees(np.arctan2(w[0][1], w[0][0]))
v = 2 * np.sqrt(2) * np.sqrt(v)
ell = plt.matplotlib.patches.Ellipse(
gmm.means_[i], v[0], v[1], angle=angle,
color=scatter.cmap(i/gmm.n_components),
fill=False, linestyle='--'
)
plt.gca().add_artist(ell)
plt.colorbar(scatter, label='Cluster Label')
plt.xlabel(x_label)
plt.ylabel(y_label)
plt.title(f'GMM Clustering (K={gmm.n_components})')
收敛警告
奇异矩阵错误
所有样本归为一类
协方差类型选择:
初始化策略:
正则化技巧:
python复制GaussianMixture(reg_covar=1e-6) # 防止协方差矩阵奇异
概率阈值过滤:
python复制probs = gmm.predict_proba(X_new)
confidence = np.max(probs, axis=1)
reliable_samples = X_new[confidence > 0.9] # 只保留高置信度样本
异常检测:
python复制log_prob = gmm.score_samples(X_test)
threshold = np.percentile(log_prob, 5) # 取最低5%作为异常
anomalies = X_test[log_prob < threshold]
半监督学习:
python复制# 已知部分标签
gmm.fit(X_unlabeled)
gmm.means_ = initialize_with_labeled_data(X_labeled, y_labeled)
将上述模块整合后的完整脚本:
python复制import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.mixture import GaussianMixture
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.metrics import silhouette_score
def main():
# 1. 数据加载
X, X_scaled, features = load_and_preprocess_data('your_data.csv')
# 2. 确定最佳K值
best_k = find_best_k(X_scaled)
# 3. 训练模型
gmm = GaussianMixture(n_components=best_k, covariance_type='full')
gmm.fit(X_scaled)
labels = gmm.predict(X_scaled)
# 4. 结果分析
visualize_results(X, X_scaled, labels, gmm, features)
# 保存结果
results = pd.DataFrame(X, columns=features)
results['Cluster'] = labels
results.to_csv('clustering_results.csv', index=False)
if __name__ == "__main__":
main()
使用建议:
某电商平台用户行为数据:
python复制# 加载业务数据
user_data = pd.read_csv('user_behavior.csv')
features = ['purchase_freq', 'avg_order_value', 'days_since_last_purchase']
# 特别处理:对days_since_last_purchase取对数
user_data['log_days'] = np.log1p(user_data['days_since_last_purchase'])
# 聚类分析
X = user_data[['purchase_freq', 'avg_order_value', 'log_days']].values
X_scaled = StandardScaler().fit_transform(X)
gmm = GaussianMixture(n_components=4, covariance_type='tied')
user_data['segment'] = gmm.fit_predict(X_scaled)
使用GMM对图像颜色进行聚类,实现色彩压缩:
python复制from skimage import io
image = io.imread('photo.jpg')
h, w, c = image.shape
pixels = image.reshape(-1, 3) / 255.0 # 归一化
gmm = GaussianMixture(n_components=8)
gmm.fit(pixels)
colors = gmm.means_ # 获取主色调
对于海量数据,可以采用以下策略:
python复制from sklearn.mixture import MiniBatchGaussianMixture
mbgmm = MiniBatchGaussianMixture(batch_size=1000)
自动确定聚类数量的变体:
python复制from sklearn.mixture import BayesianGaussianMixture
bgmm = BayesianGaussianMixture(n_components=10, weight_concentration_prior=0.1)
bgmm.fit(X)
print(f"实际使用的聚类数: {np.sum(bgmm.weights_ > 0.01)}") # 有效成分数
特征工程:
Pipeline构建:
python复制from sklearn.pipeline import Pipeline
pipe = Pipeline([
('scaler', StandardScaler()),
('pca', PCA(n_components=0.95)),
('gmm', GaussianMixture(n_components=5))
])
pipe.fit(X)
GMM通过期望最大化(EM)算法迭代优化参数:
E步(期望):
计算样本n属于簇k的责任(responsibility):
code复制γ(z_nk) = π_k * N(x_n|μ_k,Σ_k) / Σ_j π_j * N(x_n|μ_j,Σ_j)
M步(最大化):
更新参数:
code复制μ_k = (Σ_n γ(z_nk)*x_n) / N_k
Σ_k = (Σ_n γ(z_nk)*(x_n-μ_k)(x_n-μ_k)^T) / N_k
π_k = N_k / N
其中N_k = Σ_n γ(z_nk)是簇k的有效样本数。
不同covariance_type对应的Σ_k形状:
选择建议:
金融领域:
生物医学:
工业制造:
经典教材:
实用工具包:
前沿论文:
在实际项目中,我通常会先用简单方法(如K-means)建立baseline,再尝试GMM等更复杂的方法。记住没有放之四海而皆准的聚类方法,关键是要理解数据特性和业务需求。当需要概率解释或处理非球形簇时,GMM无疑是你的有力工具。