《统计学习方法》学习笔记(第九章)

EM算法及其推广

Likelihood & Maximum likelihood
似然与概率

在统计学中,似然函数(likelihood function,通常简写为likelihood,似然)是一个非常重要的内容,在非正式场合似然和概率(Probability)几乎是一对同义词,但是在统计学中似然和概率却是两个不同的概念。
概率是在特定环境下某件事情发生的可能性,也就是结果没有产生之前依据环境所对应的参数来预测某件事情发生的可能性,比如抛硬币,抛之前我们不知道最后是哪一面朝上,但是根据硬币的性质我们可以推测任何一面朝上的可能性均为50%,这个概率只有在抛硬币之前才是有意义的,抛完硬币后的结果便是确定的。
似然刚好相反,是在确定的结果下去推测产生这个结果的可能环境(参数),还是抛硬币的例子,假设我们随机抛掷一枚硬币1,000次,结果500次人头朝上,500次数字朝上(实际情况一般不会这么理想,这里只是举个例子),我们很容易判断这是一枚标准的硬币,两面朝上的概率均为50%,这个过程就是我们运用出现的结果来判断这个事情本身的性质(参数),也就是似然。
P ( Y ∣ θ ) = ∏ [ π p y i ( 1 − p ) 1 − y i + ( 1 − π ) q y i ( 1 − q ) 1 − y i ] P(Y|\theta) = \prod[\pi p^{y_i}(1-p)^{1-y_i}+(1-\pi) q^{y_i}(1-q)^{1-y_i}] P(Yθ)=[πpyi(1p)1yi+(1π)qyi(1q)1yi]

如果用 θ 表示环境对应的参数,x 表示结果,那么概率可以表示为: P ( x ∣ θ ) P(x|θ) P(xθ)
p(x|θ)是条件概率的表示方法,θ 是前置条件,理解为在 θ 的前提下,事件 x 发生的概率
相对应的似然可以表示为: L ( θ ∣ x ) L(θ|x) L(θx)
L(θ|x)可以理解为已知结果为 x ,参数为 θ (似然函数里 θ 是变量,这里说的参数和变量是相对与概率而言的)对应的概率,即: L ( θ ∣ x ) = P ( x ∣ θ ) L(θ|x)=P(x|θ) L(θx)=P(xθ)

需要说明的是两者在数值上相等,但是意义并不相同,L是关于 θ 的函数,而 P 则是关于 x 的函数,两者从不同的角度描述一件事情。

似然函数的最大值
概率描述的是在一定条件下某个事件发生的可能性,概率越大说明这件事情越可能会发生;而似然描述的是结果已知的情况下,该事件在不同条件下发生的可能性,似然函数的值越大说明该事件在对应的条件下发生的可能性越大。

例题9.1(三硬币模型)
E step:
计算在模型参数 π i , p i , q i \pi^i,p^i,q^i πi,pi,qi下观测数据 y i y_i yi来自掷硬币B的概率:
μ i + 1 = π ( p i ) y i ( 1 − ( p i ) ) 1 − y i π ( p i ) y i ( 1 − ( p i ) ) 1 − y i + ( 1 − π ) ( q i ) y i ( 1 − ( q i ) ) 1 − y i \mu^{i+1}=\frac{\pi (p^i)^{y_i}(1-(p^i))^{1-y_i}}{\pi (p^i)^{y_i}(1-(p^i))^{1-y_i}+(1-\pi) (q^i)^{y_i}(1-(q^i))^{1-y_i}} μi+1=π(pi)yi(1(pi))1yi+(1π)(qi)yi(1(qi))1yiπ(pi)yi(1(pi))1yi
M step
计算模型参数的新估计值
π i + 1 = 1 n ∑ j = 1 n μ j i + 1 \pi^{i+1}=\frac{1}{n}\sum_{j=1}^n\mu^{i+1}_j πi+1=n1j=1nμji+1 p i + 1 = ∑ j = 1 n μ j i + 1 y i ∑ j = 1 n μ j i + 1 p^{i+1}=\frac{\sum_{j=1}^n\mu^{i+1}_jy_i}{\sum_{j=1}^n\mu^{i+1}_j} pi+1=j=1nμji+1j=1nμji+1yi q i + 1 = ∑ j = 1 n ( 1 − μ j i + 1 y i ) ∑ j = 1 n ( 1 − μ j i + 1 ) q^{i+1}=\frac{\sum_{j=1}^n(1-\mu^{i+1}_jy_i)}{\sum_{j=1}^n(1-\mu^{i+1}_j)} qi+1=j=1n(1μji+1)j=1n(1μji+1yi)

源代码:
https://github.com/fengdu78/lihang-code/blob/master/code/第9章 EM算法及其推广(EM)/em.ipynb

class EM:
    def __init__(self, prob):
        self.pro_A, self.pro_B, self.pro_C = prob
        
    # e_step
    def pmf(self, i):
        #选择硬币B
        pro_1 = self.pro_A * math.pow(self.pro_B, data[i]) * math.pow((1-self.pro_B), 1-data[i])
        #选择硬币C
        pro_2 = (1 - self.pro_A) * math.pow(self.pro_C, data[i]) * math.pow((1-self.pro_C), 1-data[i])
        return pro_1 / (pro_1 + pro_2)
    
    # m_step
    def fit(self, data):
        count = len(data)
        print('init prob:{}, {}, {}'.format(self.pro_A, self.pro_B, self.pro_C))
        for d in range(count):
            _ = yield
            _pmf = [self.pmf(k) for k in range(count)]
            pro_A = 1/ count * sum(_pmf)
            pro_B = sum([_pmf[k]*data[k] for k in range(count)]) / sum([_pmf[k] for k in range(count)])
            pro_C = sum([(1-_pmf[k])*data[k] for k in range(count)]) / sum([(1-_pmf[k]) for k in range(count)])
            print('{}/{}  pro_a:{:.3f}, pro_b:{:.3f}, pro_c:{:.3f}'.format(d+1, count, pro_A, pro_B, pro_C))
            self.pro_A = pro_A
            self.pro_B = pro_B
            self.pro_C = pro_C
data=[1,1,0,1,0,0,1,0,1,1]
#初始值均为0.5
em = EM(prob=[0.5, 0.5, 0.5])
f = em.fit(data)
next(f)
#第一次迭代
f.send(1)
out:
1/10  pro_a:0.500, pro_b:0.600, pro_c:0.600
# 第二次
f.send(2)
out:
2/10  pro_a:0.500, pro_b:0.600, pro_c:0.600

#初始改变
em = EM(prob=[0.4, 0.6, 0.7])
f2 = em.fit(data)
next(f2)
f2.send(1)
out:
1/10  pro_a:0.406, pro_b:0.537, pro_c:0.643

  • send() 和next()

next() 方法在文件使用迭代器时会使用到,在循环中,next()方法会在每次循环中调用,该方法返回文件的下一行,如果到达结尾(EOF),则触发 StopIteration
如果send不携带参数,那么send(None) 和next()的作用的相同的

def a():
    print('aaa')
    p = yield '123'
#print(p)
    print('bbb')
r = a()
print(next(r))
out:
aaa
123

def a():
    print('aaa')
    p = yield '123'
#print(p)
    print('bbb')
r = a()
print(r.send(None))
out:
aaa
123
#使用next(r) 和 r.send(None)输出的结果相同
#注意的是,这里的p变量的值都是None

如果send的参数不是None,则是把yield 当成一个表代式,且把send的参数的值赋给了p;而后的操作同next一样

def a():
    print('aaa')
    p1 = yield '123'
    print('bbb')
    if (p1 == 'hello'):
        print('p1是send传过来的')
    p2= yield '234'
    print(p2)

r = a()
next(r)

out:
aaa
'123'

执行的顺序,首先a()是个生成器;第一次执行要么next( r )要么r.send(None),不能使用r.send(‘xxxxx’);这会报错的。
第一次执行时next( r )时,首先打印出aaa,然后遇到yield即跳出

r.send('hello')
out:
bbb
p1是send传过来的
'234'

然后执行r.send(‘hello’)时,p1则被赋值为hello了,然后继续接着上次运行,下一步打印出bbb,然后打印出’p1是send传过来的’,当再次遇到第二个yield时跳出,所以结果只打印了三行,后面的p2没有执行。

def a():
    print('aaa')
    p1 = yield  
    print('bbb')
    p2 = yield
    print('123')
    p3 = yield
    print('456')
r = a()
next(r)
out:
aaa
r.send(1)
out:
bbb
r.send(2)
out:
123
r.send(3)
out:
456
StopIteration #触发 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值