【技术分享】高斯混合模型

article/2025/10/12 9:03:41

本文原作者:尹迪,经授权发布。

原文链接:https://cloud.tencent.com/developer/article/1480731

导语:现有的高斯模型有单高斯模型(SGM)和高斯混合模型(GMM)两种。从几何上讲,单高斯分布模型在二维空间上近似于椭圆,在三维空间上近似于椭球。在很多情况下,属于同一类别的样本点并不满足“椭圆”分布的特性,所以我们需要引入混合高斯模型来解决这种情况。

1 单高斯模型

多维变量X服从高斯分布时,它的概率密度函数PDF定义如下:

在上述定义中,x是维数为D的样本向量,mu是模型期望,sigma是模型协方差。对于单高斯模型,可以明确训练样本是否属于该高斯模型,所以我们经常将mu用训练样本的均值代替,将sigma用训练样本的协方差代替。 假设训练样本属于类别C,那么上面的定义可以修改为下面的形式:

这个公式表示样本属于类别C的概率。我们可以根据定义的概率阈值来判断样本是否属于某个类别。

2 高斯混合模型

高斯混合模型,顾名思义,就是数据可以看作是从多个高斯分布中生成出来的。从中心极限定理可以看出,高斯分布这个假设其实是比较合理的。 为什么我们要假设数据是由若干个高斯分布组合而成的,而不假设是其他分布呢?实际上不管是什么分布,只K取得足够大,这个XX Mixture Model就会变得足够复杂,就可以用来逼近任意连续的概率密度分布。只是因为高斯函数具有良好的计算性能,所GMM被广泛地应用。

每个GMMK个高斯分布组成,每个高斯分布称为一个组件(Component),这些组件线性加成在一起就组成了GMM的概率密度函数 (1):

根据上面的式子,如果我们要从GMM分布中随机地取一个点,需要两步:

  • 随机地在这K个组件之中选一个,每个组件被选中的概率实际上就是它的系数pi_k
  • 选中了组件之后,再单独地考虑从这个组件的分布中选取一个点。

怎样用GMM来做聚类呢?其实很简单,现在我们有了数据,假定它们是由GMM生成出来的,那么我们只要根据数据推出GMM的概率分布来就可以了,然后GMMK个组件实际上就对应了K个聚类了。 在已知概率密度函数的情况下,要估计其中的参数的过程被称作“参数估计”。

我们可以利用最大似然估计来确定这些参数,GMM的似然函数 (2) 如下(此处公式有误,括号中的x应该为x_i):

可以用EM算法来求解这些参数。EM算法求解的过程如下:

  • E-步。求数据点由各个组件生成的概率(并不是每个组件被选中的概率)。对于每个数据$x_{i}$来说,它由第k个组件生成的概率为公式 (3) :

在上面的概率公式中,我们假定musigma均是已知的,它们的值来自于初始化值或者上一次迭代。

  • M-步。估计每个组件的参数。由于每个组件都是一个标准的高斯分布,可以很容易分布求出最大似然所对应的参数值,分别如下公式 (4)(5)(6)(7) :

3 源码分析

3.1 实例

在分析源码前,我们还是先看看高斯混合模型如何使用。

import org.apache.spark.mllib.clustering.GaussianMixture
import org.apache.spark.mllib.clustering.GaussianMixtureModel
import org.apache.spark.mllib.linalg.Vectors
// 加载数据
val data = sc.textFile("data/mllib/gmm_data.txt")
val parsedData = data.map(s => Vectors.dense(s.trim.split(' ').map(_.toDouble))).cache()
// 使用高斯混合模型聚类
val gmm = new GaussianMixture().setK(2).run(parsedData)
// 保存和加载模型
gmm.save(sc, "myGMMModel")
val sameModel = GaussianMixtureModel.load(sc, "myGMMModel")
// 打印参数
for (i <- 0 until gmm.k) {println("weight=%f\nmu=%s\nsigma=\n%s\n" format(gmm.weights(i), gmm.gaussians(i).mu, gmm.gaussians(i).sigma))
}

由上面的代码我们可以知道,使用高斯混合模型聚类使用到了GaussianMixture类中的run方法。下面我们直接进入run方法,分析它的实现。

3.2 高斯混合模型的实现

3.2.1 初始化

run方法中,程序所做的第一步就是初始化权重(上文中介绍的pi)及其相对应的高斯分布。

val (weights, gaussians) = initialModel match {case Some(gmm) => (gmm.weights, gmm.gaussians)case None => {val samples = breezeData.takeSample(withReplacement = true, k * nSamples, seed)(Array.fill(k)(1.0 / k), Array.tabulate(k) { i =>val slice = samples.view(i * nSamples, (i + 1) * nSamples)new MultivariateGaussian(vectorMean(slice), initCovariance(slice))})}}

在上面的代码中,当initialModel为空时,用所有值均为1.0/k的数组初始化权重,用值为MultivariateGaussian对象的数组初始化所有的高斯分布(即上文中提到的组件)。 每一个MultivariateGaussian对象都由从数据集中抽样的子集计算而来。这里用样本数据的均值和方差初始化MultivariateGaussianmusigma

//计算均值
private def vectorMean(x: IndexedSeq[BV[Double]]): BDV[Double] = {val v = BDV.zeros[Double](x(0).length)x.foreach(xi => v += xi)v / x.length.toDouble}
//计算方差
private def initCovariance(x: IndexedSeq[BV[Double]]): BreezeMatrix[Double] = {val mu = vectorMean(x)val ss = BDV.zeros[Double](x(0).length)x.foreach(xi => ss += (xi - mu) :^ 2.0)diag(ss / x.length.toDouble)}

3.2.2 EM算法求参数

初始化后,就可以使用EM算法迭代求似然函数中的参数。迭代结束的条件是迭代次数达到了我们设置的次数或者两次迭代计算的对数似然值之差小于阈值。

 while (iter < maxIterations && math.abs(llh-llhp) > convergenceTol)

在迭代内部,就可以按照E-步M-步来更新参数了。

  • E-步:更新参数gamma
 val compute = sc.broadcast(ExpectationSum.add(weights, gaussians)_)val sums = breezeData.aggregate(ExpectationSum.zero(k, d))(compute.value, _ += _)

我们先要了解ExpectationSum以及add方法的实现。

private class ExpectationSum(var logLikelihood: Double,val weights: Array[Double],val means: Array[BDV[Double]],val sigmas: Array[BreezeMatrix[Double]]) extends Serializable

ExpectationSum是一个聚合类,它表示部分期望结果:主要包含对数似然值,权重值(第二章中介绍的pi),均值,方差。add方法的实现如下:

def add( weights: Array[Double],dists: Array[MultivariateGaussian])(sums: ExpectationSum, x: BV[Double]): ExpectationSum = {val p = weights.zip(dists).map {//计算pi_i * N(x)case (weight, dist) => MLUtils.EPSILON + weight * dist.pdf(x)}val pSum = p.sumsums.logLikelihood += math.log(pSum)var i = 0while (i < sums.k) {p(i) /= pSum  sums.weights(i) += p(i)  sums.means(i) += x * p(i)  //A := alpha * x * x^T^ + ABLAS.syr(p(i), Vectors.fromBreeze(x),Matrices.fromBreeze(sums.sigmas(i)).asInstanceOf[DenseMatrix])i = i + 1}sums}

从上面的实现我们可以看出,最终,logLikelihood表示公式 (2) 中的对数似然。pweights分别表示公式 (3) 中的gammapimeans表示公式 (6) 中的求和部分,sigmas表示公式 (7) 中的求和部分。

调用RDDaggregate方法,我们可以基于所有给定数据计算上面的值。利用计算的这些新值,我们可以在M-步中更新musigma

  • M-步:更新参数musigma
 var i = 0while (i < k) {val (weight, gaussian) =updateWeightsAndGaussians(sums.means(i), sums.sigmas(i), sums.weights(i), sumWeights)weights(i) = weightgaussians(i) = gaussiani = i + 1}private def updateWeightsAndGaussians(mean: BDV[Double],sigma: BreezeMatrix[Double],weight: Double,sumWeights: Double): (Double, MultivariateGaussian) = {//  mean/weightval mu = (mean /= weight)// -weight * mu * mut +sigmaBLAS.syr(-weight, Vectors.fromBreeze(mu),Matrices.fromBreeze(sigma).asInstanceOf[DenseMatrix])val newWeight = weight / sumWeightsval newGaussian = new MultivariateGaussian(mu, sigma / weight)(newWeight, newGaussian)}

基于 E-步 计算出来的值,根据公式 (6) ,我们可以通过(mean /= weight)来更新mu;根据公式 (7) ,我们可以通过BLAS.syr()来更新sigma;同时,根据公式 (5), 我们可以通过weight / sumWeights来计算pi

迭代执行以上的 E-步和 M-步,到达一定的迭代数或者对数似然值变化较小后,我们停止迭代。这时就可以获得聚类后的参数了。

3.3 多元高斯模型中相关方法介绍

在上面的求参代码中,我们用到了MultivariateGaussian以及MultivariateGaussian中的部分方法,如pdfMultivariateGaussian定义如下:

class MultivariateGaussian @Since("1.3.0") (@Since("1.3.0") val mu: Vector,@Since("1.3.0") val sigma: Matrix) extends Serializable

MultivariateGaussian包含一个向量mu和一个矩阵sigma,分别表示期望和协方差。MultivariateGaussian最重要的方法是pdf,顾名思义就是计算给定数据的概率密度函数。它的实现如下:

private[mllib] def pdf(x: BV[Double]): Double = {math.exp(logpdf(x))
}
private[mllib] def logpdf(x: BV[Double]): Double = {val delta = x - breezeMuval v = rootSigmaInv * deltau + v.t * v * -0.5}

上面的rootSigmaInvu通过方法calculateCovarianceConstants计算。根据公式 (1) ,这个概率密度函数的计算需要计算sigma的行列式以及逆。

sigma = U * D * U.t
inv(Sigma) = U * inv(D) * U.t = (D^{-1/2}^ * U.t).t * (D^{-1/2}^ * U.t)
-0.5 * (x-mu).t * inv(Sigma) * (x-mu) = -0.5 * norm(D^{-1/2}^ * U.t  * (x-mu))^2^

这里,UD是奇异值分解得到的子矩阵。calculateCovarianceConstants具体的实现代码如下:

private def calculateCovarianceConstants: (DBM[Double], Double) = {val eigSym.EigSym(d, u) = eigSym(sigma.toBreeze.toDenseMatrix) // sigma = u * diag(d) * u.tval tol = MLUtils.EPSILON * max(d) * d.lengthtry {//所有非0奇异值的对数和val logPseudoDetSigma = d.activeValuesIterator.filter(_ > tol).map(math.log).sum//通过求非负值的倒数平方根,计算奇异值对角矩阵的根伪逆矩阵val pinvS = diag(new DBV(d.map(v => if (v > tol) math.sqrt(1.0 / v) else 0.0).toArray))(pinvS * u.t, -0.5 * (mu.size * math.log(2.0 * math.Pi) + logPseudoDetSigma))} catch {case uex: UnsupportedOperationException =>throw new IllegalArgumentException("Covariance matrix has no non-zero singular values")}}

上面的代码中,eigSym用于分解sigma矩阵。

4 参考文献

【1】漫谈 Clustering (3): Gaussian Mixture Model


http://chatgpt.dhexx.cn/article/WWhn7yvt.shtml

相关文章

高斯混合模型(GMM)

高斯混合模型 k-means 聚类模型非常简单并且易于理解&#xff0c;但是它的简单性也为实际应用带 来了挑战。特别是在实际应用中&#xff0c;k-means 的非概率性和它仅根据到簇中心点的距离来指 派簇的特点将导致性能低下。这一节将介绍高斯混合模型&#xff0c;该模型可以被看…

机器学习笔记 - 什么是高斯混合模型(GMM)?

1、高斯混合模型概述 高斯混合模型 (GMM) 是一种机器学习算法。它们用于根据概率分布将数据分类为不同的类别。高斯混合模型可用于许多不同的领域,包括金融、营销等等!这里要对高斯混合模型进行介绍以及真实世界的示例、它们的作用以及何时应该使用GMM。 高斯混合模型 (GMM) …

机器学习算法(二十九):高斯混合模型(Gaussian Mixed Model,GMM)

目录 1 混合模型&#xff08;Mixture Model&#xff09; 2 高斯模型 2.1 单高斯模型 2.2 高斯混合模型 3 模型参数学习 3.1 单高斯模型 3.2 高斯混合模型 4 高斯混合模型与K均值算法对比 1 混合模型&#xff08;Mixture Model&#xff09; 混合模型是一个可以用来表示在总…

重启虚拟机异常:Unmount and run xfs_repair

重启虚拟机异常&#xff1a;Unmount and run xfs_repair 解决办法&#xff1a; 原因&#xff1a;看出来应该是sda3分区损坏&#xff0c;修复就可以了 1&#xff1a;启动虚拟机E进入单用户模式 2&#xff1a;在linux16开头的哪一行后面添加rd.break&#xff0c;ctrlx进入救援模式…

Unmount and run xfs_repair

开启虚拟机报错&#xff1a;Unmount and run xfs_repair 从错误可以查看到是vda3错误。 解决办法&#xff1a; 1、umount /dev/vda3 2、xfs_repair -L /dev/vda3 3、reboot 就ok了。

Linux mount/unmount命令

开机自动挂载 如果我们想实现开机自动挂载某设备&#xff0c;只要修改/etc/fstab文件即可。 文件挂载的配置文件&#xff1a;/etc/fstab 查看此文件可知 每行定义一个要挂载的文件系统&#xff1b; 其每行的格式如下 要挂载的设备或伪文件系统 挂载点 文件系统类型 挂载选项…

android.permission.MOUNT_UNMOUNT_FILESYSTEMS添加权限报错

<!--这是在sd卡内创建和删除文件权限--> <uses-permission android:name"android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <uses-permission android:name"android.permission.WRITE_SETTINGS" /> 出现报错有两种解决方法&#xff1…

Centos7 虚拟机非法关机导致系统无法启动报Unmount and run xfs_repair的解决方法

1&#xff0c;电脑不正常启动导致我的虚拟机无法启动&#xff0c;报以下错误Unmount and run xfs_repair&#xff0c;大概意思是xfs文件系统出现问题了&#xff0c;导致无法挂载&#xff0c;需要使用xfs_repair进行修复。 2&#xff0c;执行如下命令 xfs_repair /dev/mapper/c…

linux下unmount了移动硬盘之后,硬盘灯还是常亮并且硬盘还在一直转动

linux下unmount了移动硬盘之后&#xff0c;硬盘灯还是常亮并且硬盘还在一直转动 ​ 参考:https://www.zhihu.com/question/23362385 希捷2T移动硬盘 在windows下卸载硬盘之后硬盘灯就不亮了&#xff0c;手摸也没有震动感。 在ubuntu下卸载硬盘之后&#xff0c;硬盘灯仍然常…

centos7异常断电,重启提示Unmount and run xfs_repair

周末服务器由于阵列卡问题&#xff0c;异常重启&#xff0c;重启后前台异常报错&#xff0c;提示磁盘元数据问题&#xff0c;需要修复 如故障图所示&#xff0c;sdb1&#xff0c;sdf1&#xff0c;sdi1 3块磁盘出现问题&#xff0c; 下面的解决办法&#xff1a; 解决办法&…

Android/C/C++ 中解决 USB UnMount(禁止USB端口)

引&#xff1a;因为某些特殊需求&#xff0c;需要在某些设备接入车机的时候&#xff0c;动态UnMount USB设备&#xff0c;因为代码其中有一些方法是自定义过的&#xff0c;所以该文章仅供思路参考。 &#xff08;20200319 更新&#xff09;&#xff1a;在后续跟进中&#xff0…

linux移动硬盘unmount报错处理

备份数据用的移动硬盘unmount时报&#xff1a; 通过fuser命令来kill掉设备进程&#xff0c;再unmount移动设备 先看fuser命令帮助信息&#xff1a; [rootathenadb2 ~]# fuser -helpUsage: fuser [ -a | -s | -c ] [ -n SPACE ] [ -SIGNAL ] [ -kimuv ] NAME... [ - ] [ -n SPAC…

虚拟机 报错:Unmount and run xfs_repair

1&#xff0c;启动虚拟机的时候&#xff0c;摁 E 键 找到linux16 这一行(在fi下一行) 的最后&#xff0c;添加&#xff1a; rd.break 摁 &#xff1a;ctrlx进入救援模式 2&#xff0c;执行&#xff1a;umount /dev/sda3 xfs_repair -L /dev/sda3 …

VMware 安装Centos7 虚拟机 报Unmount and run xfs_repair

VMware 安装Centos7 虚拟机 报Unmount and run xfs_repair 问题描述 安装完虚拟机后重启无法进入用户登录界面 报错&#xff1a;Unmount and run xfs_repair 原因&#xff1a; dm-0分区损坏 解决办法 卸载目录 umount /dev/mapper/centos-root 修复目录 xfs_repair -L…

linux 开机遇见unmount and run xfs_repair

vmware 开机提示 看不懂反正提示 dm-0 没有正确挂载&#xff0c;需要修复 直接上干货执行如下两条命令 ls -l /dev/mapper xfs_repair /dev/mapper/cl_root 或是 xfs_repair -L /dev/mapper/cl_root echo $? 如果返回零&#xff0c;代表成功 最后重启服务器即可 init 6

Linux虚拟机(lvm)报Unmount and run xfs_repair

原因&#xff1a;因为突然断电&#xff0c;导致机器关闭 结果&#xff1a;发现有一台虚拟机无法启动&#xff0c;一直报错 Unmount and run xfs_repair 分析&#xff1a;主机异常掉电后里面的虚拟机无法启动&#xff0c;主要是损坏的分区 解决办法&#xff1a; 原因&#x…

Ubuntu远程装载硬盘Mount and unmount disk

小虎本来以为Ubuntu&#xff08;Linux系统&#xff09;不能够远程装载硬盘&#xff0c;每次重开机都要线下重新装载硬盘。但是苦于工作站不在身边&#xff0c;跑来跑去太麻烦&#xff0c;所以想远程装载一下。 解决方法 遍历搜索所有硬盘。找到对应名字。 lsblk创建一个文件…

mount unmount oracle,umount- 0506-349 Cannot unmount -dev-cd0- The requested reso

umount- 0506-349 Cannot unmount -dev-cd0- The requested reso (2011-12-21 04:20:37) 标签&#xff1a; requested 杂谈 umount: 0506-349 Cannot unmount /dev/cd0: The requested reso请问&#xff1a;我在安装oracle的时候&#xff0c;当系统提示我放入第二张光盘的时候&…

umount卸载磁盘_如何使用umount命令在Linux,Ubuntu,CentOS中卸载磁盘

umount卸载磁盘 Linux distributions like Ubuntu, Debian, CentOS, RHEL, and others use disks by mounting them to the file system. We have already examined mount command in the following tutorial. In this tutorial we will learn how to umount the disk in a Lin…

Linux之磁盘管理df、du、mount 、unmount 命令

1、Linux磁盘管理常用命令为 df、du df &#xff1a;列出文件系统的整体磁盘使用量du&#xff1a;检查磁盘空间使用量 2、df 列出文件系统的整体磁盘使用量 df命令参数功能&#xff1a;检查文件系统的磁盘空间占用情况。可以利用该命令来获取硬盘被占用了多少空间&#xff0c;目…