gmm的java实现_4. EM算法-高斯混合模型GMM详细代码实现

本文详细介绍了如何用Java实现高斯混合模型(GMM)的EM算法,包括数据预处理、参数初始化、E步和M步的计算过程,以及解决underflow问题的LogSumExp技巧。提供了完整的可运行代码,并预告了下一阶段将对GMM进行改进和优化。
摘要由CSDN通过智能技术生成

1. 前言

EM的前3篇博文分别从数学基础、EM通用算法原理、EM的高斯混合模型的角度介绍了EM算法。按照惯例,本文要对EM算法进行更进一步的探究。就是动手去实践她。

2. GMM实现

我的实现逻辑基本按照GMM算法流程中的方式实现。需要全部可运行代码,请移步我的github。

输入:观测数据\(x_1,x_2,x_3,...,x_N\)

对输入数据进行归一化处理

#数据预处理

def scale_data(self):

for d in range(self.D):

max_ = self.X[:, d].max()

min_ = self.X[:, d].min()

self.X[:, d] = (self.X[:, d] - min_) / (max_ - min_)

self.xj_mean = np.mean(self.X, axis=0)

self.xj_s = np.sqrt(np.var(self.X, axis=0))

输出:GMM的参数

初始化参数

#初始化参数

def init_params(self):

self.mu = np.random.rand(self.K, self.D)

self.cov = np.array([np.eye(self.D)] * self.K) * 0.1

self.alpha = np.array([1.0 / self.K] * self.K)

E步:根据当前模型,计算模型\(k\)对\(x_i\)的影响

\[\gamma_{ik}=\frac{\pi_k\mathcal{N}(\boldsymbol{x}|\boldsymbol{\mu}_k,\boldsymbol{\Sigma}_k)}{\sum_{k=1}^K\pi_k\mathcal{N}(\boldsymbol{x}|\boldsymbol{\mu}_k,\boldsymbol{\Sigma}_k)}

\]

#e步,估计gamma

def e_step(self, data):

gamma_log_prob = np.mat(np.zeros((self.N, self.K)))

for k in range(self.K):

gamma_log_prob[:, k] = log_weight_prob(data, self.alpha[k], self.mu[k], self.cov[k])

log_prob_norm = logsumexp(gamma_log_prob, axis=1)

log_gamma = gamma_log_prob - log_prob_norm[:, np.newaxis]

return log_prob_norm, np.exp(log_gamma)

M步:计算\(\mu_{k+1},\Sigma_{k+1}^2,\pi_{k+1}\)。

\[n_k=\sum_{i=1}^N\gamma_{ik}

\]

\[\mu_{k+1}=\frac{1}{n_k}\sum_{i=1}^N\gamma_{ik}x_i

\]

\[\Sigma_{k+1}^2=\frac{1}{n_k}\sum_{i=1}^N\gamma_{ik}(x_i-\mu_k)^2

\]

\[\pi_{k+1}=\frac{n_k}{N}

\]

#m步,最大化loglikelihood

def m_step(self):

newmu = np.zeros([self.K, self.D])

newcov = []

newalpha = np.zeros(self.K)

for k in range(self.K):

Nk = np.sum(self.gamma[:, k])

newmu[k, :] = np.dot(self.gamma[:, k].T, self.X) / Nk

cov_k = self.compute_cov(k, Nk)

newcov.append(cov_k)

newalpha[k] = Nk / self.N

newcov = np.array(newcov)

return newmu, newcov, newalpha

重复2,3两步直到收敛

最后加上loglikelihood的计算方法。

基本的计算方法按照公式定义。

\[L(\theta) = \sum\limits_{i=1}^m log\sum\limits_{z^{(i)}}Q_i(z^{(i)})P(x^{(i)},z^{(i)}|\theta)\;\;\;s.t.\sum\limits_{z}Q_i(z^{(i)}) =1

\]

实现如下

def loglikelihood(self):

P = np.zeros([self.N, self.K])

for k in range(self.K):

P[:,k] = prob(self.X, self.mu[k], self.cov[k])

return np.sum(np.log(P.dot(self.alpha)))

但是这样的实现会有2个问题。

非矩阵运算,速度慢。

非常容易underflow,因为\(P.dot(self.alpha)\)非常容易是一个很小的数,系统把它当作0处理。

使用以下\(LogSumExp\)公式进行改进,并且令\(a_h = log(Q_i(z^{(i)}))+log(P(x^{(i)},z^{(i)}|\theta))\),具体实现看github:

\[log(\sum_hexp(a_h)) = m + log(\sum_hexp(a_h - m))\;\;\;m=max(a_h)

\]

3. 总结

首先gmm算法会很容易出现underflow和overflow,所以处理的时候有点麻烦。但是\(LogSumExp\)能解决大部分这个问题。还有就是我的实现方式是需要协方差矩阵一定要是正定矩阵,所以我的代码中也做了处理。我们好想还不能够满足于最基础的GMM算法,所以在下一篇文章中我们要对GMM加入一个惩罚项,并且用对角矩阵的方式代替协方差矩阵。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值