我编写了一个模拟EM算法的小脚本,并可视化其迭代步骤。然而,在第5次迭代之后,当试图绘制更新的估计双变量高斯分布时它停止。
我怀疑我的协方差矩阵有些可疑,但我并不确定。如果我对等高线图进行评论,那么脚本运行正常并且可以像它应该的那样工作(但是当然跟随估计分布的演变会很好)。任何帮助将不胜感激。
import numpy as np
import scipy.stats as sp
import matplotlib.pyplot as plt
from matplotlib.mlab import bivariate_normal
def expectationMaximization():
# define multivariate gaussian distributions and generate observations
u1 = [-1.5, -1.5]
cov1 = [[0.2, 0.4],
[0, 0.1]]
u2 = [1, 1]
cov2 = [[0.3, 0.4],
[0, 0.3]]
samples = 1000
x1, y1 = np.random.multivariate_normal(u1, cov1, samples // 2).T
x2, y2 = np.random.multivariate_normal(u2, cov2, samples // 2).T
x = np.concatenate([x1, x2])
y = np.concatenate([y1, y2])
points = np.concatenate([np.column_stack((x1, y1)),
np.column_stack((x2, y2))])
# initialization of classifier models
uk1 = np.array([-1.5, 1])
covk1 = np.array([[1, 0], [0, 1]])
uk2 = np.array([1.5, -1])
covk2 = np.array([[1, 0], [0, 1]])
w = np.array([1., 1.])
gamma = np.zeros((2, samples))
# sim loop
for idx in range(9):
##########################################################
# expectation #
##########################################################
# update gamma
gamma[0] = (w[0] * sp.multivariate_normal.pdf(points, uk1, covk1) /
(w[0] * sp.multivariate_normal.pdf(points, uk1, covk1) +
w[1] * sp.multivariate_normal.pdf(points, uk2, covk2)))
gamma[1] = (w[1] * sp.multivariate_normal.pdf(points, uk2, covk2) /
(w[0] * sp.multivariate_normal.pdf(points, uk1, covk1) +
w[1] * sp.multivariate_normal.pdf(points, uk2, covk2)))
##########################################################
# plot #
##########################################################
plt.subplot(3, 3, idx + 1)
plt.title('Iteration {}'.format(idx + 1))
axes = plt.gca()
axes.set_xlim([-3, 3])
axes.set_ylim([-3, 3])
# setup grid for bivariate gaussian plot (only needed once)
if idx < 1:
xmin, xmax = axes.get_xlim()
ymin, ymax = axes.get_ylim()
delta = 0.1
xticks = np.arange(xmin, xmax, delta)
yticks = np.arange(ymin, ymax, delta)
xmesh, ymesh = np.meshgrid(xticks, yticks)
# update mesh values
z1 = bivariate_normal(xmesh, ymesh, covk1[0, 0], covk1[1, 1],
uk1[0], uk1[1], covk1[0, 1])
z2 = bivariate_normal(xmesh, ymesh, covk2[0, 0], covk2[1, 1],
uk2[0], uk2[1], covk2[0, 1])
z = (z1 - z2) * 10
# plot pdf map and sample points
plt.contour(xmesh, ymesh, z)
plt.scatter(x, y, c=(gamma[0] - gamma[1]) * 10)
plt.grid(True)
##########################################################
# maximization #
##########################################################
# update means
uk1[0] = sum(gamma[0] * x) / sum(gamma[0])
uk1[1] = sum(gamma[0] * y) / sum(gamma[0])
uk2[0] = sum(gamma[1] * x) / sum(gamma[1])
uk2[1] = sum(gamma[1] * y) / sum(gamma[1])
# update covariance matrices
# calc all distances
dist1 = points - uk1[None, :]
dist2 = points - uk2[None, :]
# calc all outer products
matrixSchaar1 = np.einsum('...i,...j->...ij', dist1, dist1)
matrixSchaar2 = np.einsum('...i,...j->...ij', dist2, dist2)
# calculate sum product of matrices and gammas
covk1 = ((matrixSchaar1 * gamma[0][:, None, None]).sum(axis=0) /
sum(gamma[0]))
covk2 = ((matrixSchaar2 * gamma[1][:, None, None]).sum(axis=0) /
sum(gamma[1]))
# update w
w[0] = sum(gamma[0]) / len(gamma[0])
w[1] = sum(gamma[1]) / len(gamma[1])
def main():
expectationMaximization()
plt.show()
if __name__ == "__main__":
main()
回溯:
/usr/lib64/python3.5/site-packages/matplotlib/mlab.py:1926: RuntimeWarning: invalid value encountered in sqrt
denom = 2*np.pi*sigmax*sigmay*np.sqrt(1-rho**2)
Traceback (most recent call last):
File "bsp4.py", line 120, in <module>
main()
File "bsp4.py", line 115, in main
expectationMaximization()
File "bsp4.py", line 76, in expectationMaximization
plt.contour(xmesh, ymesh, z)
File "/usr/lib64/python3.5/site-packages/matplotlib/pyplot.py", line 2766, in contour
ret = ax.contour(*args, **kwargs)
File "/usr/lib64/python3.5/site-packages/matplotlib/__init__.py", line 1815, in inner
return func(ax, *args, **kwargs)
File "/usr/lib64/python3.5/site-packages/matplotlib/axes/_axes.py", line 5644, in contour
return mcontour.QuadContourSet(self, *args, **kwargs)
File "/usr/lib64/python3.5/site-packages/matplotlib/contour.py", line 1424, in __init__
ContourSet.__init__(self, ax, *args, **kwargs)
File "/usr/lib64/python3.5/site-packages/matplotlib/contour.py", line 864, in __init__
self._process_levels()
File "/usr/lib64/python3.5/site-packages/matplotlib/contour.py", line 1202, in _process_levels
self.vmin = np.amin(self.levels)
File "/usr/lib64/python3.5/site-packages/numpy/core/fromnumeric.py", line 2359, in amin
out=out, keepdims=keepdims)
File "/usr/lib64/python3.5/site-packages/numpy/core/_methods.py", line 29, in _amin
return umr_minimum(a, axis, None, out, keepdims)
ValueError: zero-size array to reduction operation minimum which has no identity
答案 0 :(得分:1)
作为评论开始,但变得太长了......
你怀疑你的协变矩阵是对的。在第5次或第6次迭代后,您的bivariate_normal
发行版z1
和z2
始终成为nan
数组。但是,我认为这并不一定意味着你的代码有缺陷,它可能只是一个不方便的数字问题(我对这里的理论一无所知)。
无论如何,您可以通过设置等高线图的等级,在分解前将轮廓保持到i
次迭代
levels = np.linspace(z.min(), z.max(), 20)
plt.contour(xmesh, ymesh, z, levels=levels)
因为据我所知,ValueError
来自轮廓无法设定其最小值。
答案 1 :(得分:0)