高斯混合模型(附带实例,Python实现)
高斯混合模型(GMM)是一种软聚类模型。GMM 也可以看作是 k 均值的推广,因为 GMM 不仅是考虑到了数据分布的均值,也考虑到了协方差。和 k 均值一样,GMM 需要提前确定簇的个数。
其中,P(Xi|μj, σj) 是样本 Xi 在第 j 个高斯分布中的概率密度函数。以一维高斯分布为例:
然后重复进行以上两个步骤,直到达到迭代终止条件。
根据 W,就可以更新每一簇的占比 πm:
第 m 簇的第 k 个分量的方差为:
【实例】利用高斯混合模型对 iris 数据集进行聚类。

图 1 iris数据集显示

图 2 GMM显示效果
高斯混合模型概述
在高斯混合模型中,需要估计每一个高斯分布的均值与方差。从最大似然估计的角度来说,给定某个有 n 个样本的数据集 X,假如已知 GMM 中一共有 k 簇,我们就是要找到 k 组均值 μ1,…,μk,k 组方程 σ1,…,σk 来最大化以下似然函数 L:L((μ1, …, μk), (σ1, …, σk);X)将其改成:

其中,P(Xi|μj, σj) 是样本 Xi 在第 j 个高斯分布中的概率密度函数。以一维高斯分布为例:

最大期望算法
有了隐变量还不够,我们还需要一个算法来找到最佳的 W,从而得到 GMM 的模型参数。最大期望算法(expectation-maximization,简称 EM)就是这样一个算法,简单说来,EM 算法分两个步骤:- 第一个步骤是 E(期望)步;
- 第二个步骤是 M(最大化)步。
然后重复进行以上两个步骤,直到达到迭代终止条件。
1) E步骤
E 步骤中,主要目的是更新 W。第 i 个变量属于第 m 簇的概率:
根据 W,就可以更新每一簇的占比 πm:

2) M步骤
M 步骤中,需要根据 E 步骤得到的 W 更新均值 μ 和方差 var。μ 和 var 分别是 W 的权重的样本 X 的均值和方差。第 m 簇的第 k 个分量的均值为:
第 m 簇的第 k 个分量的方差为:

高斯混合模型实战
前面已介绍了高斯混合模型的概念、最大期望算法等相关内容,下面通过实例来演示高斯混合模型的实战。【实例】利用高斯混合模型对 iris 数据集进行聚类。
import numpy as np import matplotlib as mpl import matplotlib.pyplot as plt from sklearn.datasets import load_iris from sklearn.preprocessing import Normalizer from sklearn.metrics import accuracy_score class GMM: def __init__(self, Data, K, weights=None, means=None, covars=None): """ 初始化 :param Data: 训练数据 :param K: 高斯分布的个数 :param weights: 每个高斯分布的初始概率(权重) :param means: 高斯分布的均值向量 :param covars: 高斯分布的协方差矩阵集合 """ self.Data = Data self.K = K if weights is not None: self.weights = weights else: self.weights = np.random.rand(self.K) self.weights /= np.sum(self.weights) # 归一化 col = np.shape(self.Data)[1] if means is not None: self.means = means else: self.means = [] for i in range(self.K): mean = np.random.rand(col) self.means.append(mean) if covars is not None: self.covars = covars else: self.covars = [] for i in range(self.K): cov = np.random.rand(col, col) self.covars.append(cov) # cov 是 np.array,但是 self.covars 是 list def Gaussian(self, x, mean, cov): """ 自定义的高斯分布概率密度函数 :param x: 输入数据 :param mean: 均值数组 :param cov: 协方差矩阵 :return: x 的概率 """ dim = np.shape(cov)[0] covdet = np.linalg.det(cov + np.eye(dim) * 0.001) covinv = np.linalg.inv(cov + np.eye(dim) * 0.001) xdiff = (x - mean).reshape((1, dim)) prob = 1.0 / (np.power(np.power(2 * np.pi, dim) * np.abs(covdet), 0.5)) * \ np.exp(-0.5 * xdiff.dot(covinv).dot(xdiff.T))[0][0] return prob def GMM_EM(self): """ 利用 EM 算法进行优化 GMM 参数的函数 :return: 返回各组数据的属于每个分类的概率 """ loglikelihood = 0 oldloglikelihood = 1 len, dim = np.shape(self.Data) gammas = [np.zeros(self.K) for i in range(len)] # gammas 表示第 n 个样本属于第 k 个混合高斯的概率 while np.abs(loglikelihood - oldloglikelihood) > 0.0000001: oldloglikelihood = loglikelihood # E 步骤 for n in range(len): respons = [self.weights[k] * self.Gaussian(self.Data[n], self.means[k], self.covars[k]) for k in range(self.K)] respons = np.array(respons) sum_respons = np.sum(respons) gammas[n] = respons / sum_respons # M 步骤 for k in range(self.K): nk = np.sum([gammas[n][k] for n in range(len)]) self.weights[k] = 1.0 * nk / len # 更新每个高斯分布的概率 self.means[k] = (1.0 / nk) * np.sum([gammas[n][k] * self.Data[n] for n in range(len)], axis=0) # 更新高斯分布的均值 xdiffs = self.Data - self.means[k] self.covars[k] = (1.0 / nk) * np.sum([gammas[n][k] * xdiffs[n].reshape((dim, 1)).dot(xdiffs[n].reshape((1, dim))) for n in range(len)], axis=0) # 更新高斯分布的协方差矩阵 loglikelihood = [] for n in range(len): tmp = [self.weights[k] * self.Gaussian(self.Data[n], self.means[k], self.covars[k]) for k in range(self.K)] tmp = np.log(np.array(tmp)) loglikelihood.append(list(tmp)) loglikelihood = np.sum(loglikelihood) for i in range(len): gammas[i] = gammas[i] / np.sum(gammas[i]) self.posibility = gammas self.prediction = [np.argmax(gammas[i]) for i in range(len)] def run_main(): """ 主函数 """ # 导入 iris 数据集 iris = load_iris() label = np.array(iris.target) data = np.array(iris.data) print("iris 数据集的标签:\n", label) # 对数据进行预处理 data = Normalizer().fit_transform(data) # 解决图中中文乱码问题 mpl.rcParams['font.sans-serif'] = [u'simHei'] mpl.rcParams['axes.unicode_minus'] = False # 数据可视化 plt.scatter(data[:, 0], data[:, 1], c=label) plt.title("iris 数据集显示") plt.show() # GMM 模型 K = 3 gmm = GMM(data, K) gmm.GMM_EM() y_pre = gmm.prediction print("GMM 预测结果:\n", y_pre) print("GMM 正确率为:\n", accuracy_score(label, y_pre)) plt.scatter(data[:, 0], data[:, 1], c=y_pre) plt.title("GMM 结果显示") plt.show() if __name__ == '__main__': run_main()运行程序,输出如下:
iris数据集的标签: [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2] GMM预测结果: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] GMM正确率为: 0.6666666666666效果如图 1 及图 2 所示:

图 1 iris数据集显示

图 2 GMM显示效果