python实现em聚类算法_EM算法详解和numpy代码实现

声明: 本文由DataScience原创发表, 转载请注明本文链接mlln.cn, 并在文后留言转载.

本文代码运行环境:

windows10

python3.6

jupyter notebook

用到的资源和基本配置

在教程开始之前了解一下我们将用到的工具, 可以让你们评估一下教程的难度, 并且了解教程的大概内容。我们的教程和本站的大部分内容类似, 都运行在jupyter notebook中, 并且在后续可能会增加在线运行代码的功能。

1

2

3

4

5%matplotlib inline

import numpy as np

import matplotlib.pyplot as plt

import pandas as pd

问题介绍

这个例子来自书 Computer Age Statistical Inference By Efron和Hastie。假设我们的数据采自正太双变量:

用代码来说明就是这样:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16# 标准差

sig1=1

sig2=0.75

# 均值

mu1=1.85

mu2=1

rho=0.82

# 均值

means=np.array([mu1, mu2])

#协方差矩阵

cov = np.array([

[sig1**2, sig1*sig2*rho],

[sig2*sig1*rho, sig2**2]

])

print('均值:', means)

print('协方差矩阵:', cov)

输出(stream):

均值: [1.85 1. ]

协方差矩阵: [[1. 0.615 ]

[0.615 0.5625]]

生成我们用到的样本, 第一列表示x, 第二列表示y

1

2samples=np.random.multivariate_normal(means, cov, size=40)

samples

输出(plain):

array([[ 2.68359748, 1.48379834],

[ 1.17791924, 1.38894689],

[ 1.86489988, 0.64818678],

[ 3.46263848, 2.52003738],

[ 3.28320055, 2.09838814],

[ 2.2243539 , 0.68423906],

[-0.81846598, 0.25206792],

[ 2.8875052 , 1.21514733],

[ 2.8089101 , 2.66763322],

[-0.43096351, -0.40946548],

[ 1.59169105, 0.83110871],

[ 2.33259334, 0.74321063],

[ 1.23234749, 0.10636416],

[ 2.77710826, 1.22676301],

[ 2.30041259, 1.26537629],

[ 1.46274197, 0.86375528],

[ 2.34134553, 0.60329965],

[ 1.41575413, 0.12814199],

[ 2.89978014, 1.662521 ],

[ 1.94268688, 0.96135903],

[ 1.8706036 , 0.23341228],

[-0.95437589, -1.59097527],

[ 2.31569597, 1.16615578],

[ 2.22949385, 1.51688792],

[ 1.20585795, 0.65294385],

[ 1.35490525, 1.49116327],

[ 0.65720096, -0.04390794],

[ 3.50395539, 0.94368355],

[ 2.87762334, 2.1871402 ],

[ 1.72990085, 0.06746951],

[ 1.81482228, 1.13994364],

[ 2.90097921, 2.68693399],

[ 0.17064598, -0.57392392],

[ 3.54635827, 1.9893743 ],

[ 0.14184182, -0.16384488],

[ 1.62707491, 1.3287029 ],

[ 0.98461819, 1.14653502],

[ 2.90010676, 1.67282818],

[ 1.40230423, 0.11994606],

[ 2.9772189 , 1.59642522]])

不幸的是, 有些数据是缺失的, 所以我们伪造一些缺失数据, 用0表示缺失, 设置后20行数据的y都为0

1

2samples_censored=np.copy(samples)

samples_censored[20:,1]=0

我们将上面的样本绘制成蓝色点。现在我们丢失了最后20个数据的y值。我们留下了丢失的数据或隐藏数据或潜在变量。

1

2plt.plot(samples[:,0], samples[:,1], 'o', alpha=0.8)

plt.plot(samples_censored[:,0], samples_censored[:,1], 's', alpha=0.3)

输出(plain):

[]

如果我们已知所有的数据, 数据没有缺失, 我们可以使用MLE(极大似然估计)的方法求得下面所有的统计量, 下面的公式就是基于MLE推倒而得:

我们假设缺失值已知, 可以计算得到这些统计量, 这个过程就叫做M-step。下面先写出计算的公式:

1

2

3

4

5mu1 = lambda s: np.mean(s[:,0])

mu2 = lambda s: np.mean(s[:,1])

s1 = lambda s: np.std(s[:,0])

s2 = lambda s: np.std(s[:,1])

rho = lambda s: np.mean((s[:,0] - mu1(s))*(s[:,1] - mu2(s)))/(s1(s)*s2(s))

但是我们缺少y的部分数据,暂时用0来代替缺失值, 使用这个缺失值, 我们可以计算得到上面所有的统计量, 我们应该用迭代的方式找出缺失值的更好的替代值。下这些列表用于存放中间数据:

1

2

3

4

5

6mu1s=[]

mu2s=[]

s1s=[]

s2s=[]

# 相关系数, 后面有用到

rhos=[]

先使用缺失值0计算所有的统计量:

1

2

3

4

5

6mu1s.append(mu1(samples_censored))

mu2s.append(mu2(samples_censored))

s1s.append(s1(samples_censored))

s2s.append(s2(samples_censored))

rhos.append(rho(samples_censored))

mu1s,mu2s,s1s,s2s,rhos

输出(plain):

([1.8674222131186482],

[0.5235219836257345],

[1.1279568484950988],

[0.7551359797772168],

[0.43680179723920937])

有了这些参数, 我们可以反过来想, 那些缺失的y是不是可以被估计出来, 一种思路是用平均值代替缺失的y, 也就是下面的公式, 它指的是条件期望,也就是已知x和参数$\theta$的情况下, 去估计y的期望:

$$

E_{p(z \vert \theta, x)}[z]

$$

1

2

3

4

5

6

7

8# 用代码实现上面的公式就是:

def ynew(x, mu1, mu2, s1, s2, rho):

return mu2 + rho*(s2/s1)*(x - mu1)

# 注意计算y的时候, 只使用了缺失值对应的x

newys=ynew(samples_censored[20:,0], mu1s[0], mu2s[0], s1s[0], s2s[0], rhos[0])

newys

输出(plain):

array([ 0.52445231, -0.30164726, 0.65460922, 0.62940142, 0.3300629 ,

0.37364831, 0.16962092, 1.00208806, 0.81893182, 0.48330706,

0.50814036, 0.82576169, 0.02733923, 1.01448779, 0.01891612,

0.453238 , 0.26536647, 0.82550657, 0.38750904, 0.84805622])

上面的这个过程就叫做E-step, 因为我们计算了期望, 下面我们迭代一下看是否能收敛:

1

2

3

4

5

6

7

8

9

10

11

12for step in range(1,20):

samples_censored[20:,1] = newys

#M-step

mu1s.append(mu1(samples_censored))

mu2s.append(mu2(samples_censored))

s1s.append(s1(samples_censored))

s2s.append(s2(samples_censored))

rhos.append(rho(samples_censored))

#E-step

newys=ynew(samples_censored[20:,0], mu1s[step], mu2s[step], s1s[step], s2s[step], rhos[step])

df=pd.DataFrame.from_dict(dict(mu1=mu1s, mu2=mu2s, s1=s1s, s2=s2s, rho=rhos))

df

输出(html):

mu1

mu2

s1

s2

rho

0

1.867422

0.523522

1.127957

0.755136

0.436802

1

1.867422

0.769992

1.127957

0.656320

0.734940

2

1.867422

0.886157

1.127957

0.667636

0.827798

3

1.867422

0.940980

1.127957

0.685415

0.853448

4

1.867422

0.966894

1.127957

0.696062

0.861611

5

1.867422

0.979166

1.127957

0.701419

0.864603

6

1.867422

0.984991

1.127957

0.703944

0.865806

7

1.867422

0.987763

1.127957

0.705095

0.866312

8

1.867422

0.989086

1.127957

0.705607

0.866529

9

1.867422

0.989720

1.127957

0.705830

0.866621

10

1.867422

0.990025

1.127957

0.705925

0.866660

11

1.867422

0.990173

1.127957

0.705963

0.866676

12

1.867422

0.990244

1.127957

0.705978

0.866682

13

1.867422

0.990279

1.127957

0.705983

0.866684

14

1.867422

0.990297

1.127957

0.705984

0.866684

15

1.867422

0.990305

1.127957

0.705984

0.866684

16

1.867422

0.990309

1.127957

0.705984

0.866684

17

1.867422

0.990312

1.127957

0.705984

0.866684

18

1.867422

0.990313

1.127957

0.705983

0.866684

19

1.867422

0.990313

1.127957

0.705983

0.866684

我们可以看到, 估计得到的mu2和s2跟初始值(1, 0.75)相比有些出入, 不过也差不多, 因为EM算法只能找到局部最小值, 而不能得到全局最小值。

注意

本文由jupyter notebook转换而来, 您可以在这里下载notebook

有问题可以直接在下方留言

或者给我发邮件675495787[at]qq.com

请记住我的网址: mlln.cn 或者 jupyter.cn

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值