用c语言实现EM算法,EM算法

缘起

最近室友都找实习。面试时,常被问到的是,一些基础的机器学习知识和最新的深度学习模型,比如 逻辑回归、正则项、f1的计算、类别不平衡的处理、LSTM、Attention、Transformer、word2vec、glove、ELMo、BERT等等。因此,我打算系统地、扎实地将这些知识学习一遍,同时将学习的笔记写成博客,以供自己以后查看。如果不幸帮助了别人,还可以收获一些虚荣:-)。对吧~

万事开头难。所以,先从学过的EM算法开始吧。

从贝叶斯公式到极大似然估计

贝叶斯估计

已知样本的特征向量

math?formula=x,为了求样本的类别

math?formula=w,我们可以使用经典的贝叶斯公式,求解已知特征向量

math?formula=x时不同类别的概率

math?formula=P(w_i%7Cx)

math?formula=P(w_i%7Cx)%3D%5Cfrac%7BP(x%7Cw_i)P(w_i)%7D%7BP(x)%7D.

然后,选择使得

math?formula=P(w_i%7Cx)中最大的

math?formula=w_i作为样本类别的估计值,即有

math?formula=%5Cbegin%7Balign%7D%20%5Chat%7Bw%7D%26%3D%5Carg%20%5Cmax_%7Bi%7D%20P(w_i%7Cx)%5C%5C%20%26%3D%5Carg%20%5Cmax_%7Bi%7D%20P(x%7Cw_i)P(w_i).%20%5Cend%7Balign%7D

其中,

math?formula=P(w_i)被称为先验概率,可通过统计标记样本中不同类别样本的比例得到,即

math?formula=P(w_i)%3D%5Cfrac%7B%5C%23%20w_i%7D%7BN%7D.

math?formula=P(x%7Cw_i) 被称为类条件概率,当样本的特征数为1且是为离散值时,

math?formula=P(x%7Cw_i)%3D%5Cfrac%7B%5C%23(x%2Cw_i)%7D%7B%5C%23%20w_i%7D.

其中,

math?formula=%5C%23(x%2Cw_i)表示特征为

math?formula=x且类别为

math?formula=w_i的样本数目。样本特征数不为1的情况,这里暂且不讨论。当特征

math?formula=x的取值是连续值的时候,问题就复杂了起来。我们可以使用极大似然估计来解决这个问题。

极大似然估计

在极大似然估计中,我们首先假设

math?formula=P(x%7Cw_i)服从一个概率分布,常用的是正态分布,即

math?formula=P(x%7Cw_i%3B%5Cmu%2C%5Csigma)%3D%5Cfrac%7B1%7D%7B%5Csqrt%7B2%5Cpi%7D%5Csigma%7D%5Cexp%5Cleft%5B-%5Cfrac%7B1%7D%7B2%7D%5Cleft(%5Cfrac%7Bx-%5Cmu%7D%7B%5Csigma%7D%5Cright)%5E2%5Cright%5D.

记参数

math?formula=%5Ctheta%3D(%5Cmu%2C%5Csigma%20),当我们得到一组

math?formula=%5Ctheta时,类条件概率

math?formula=P(x%7Cw_i)随之确定。

为了求解

math?formula=%5Ctheta,令所有类别为

math?formula=w_i的样本集合为

math?formula=D,假设

math?formula=D中样本

math?formula=x_1%2C%5Ccdots%2Cx_n之间相互独立,那么有

math?formula=%5Cbegin%7Balign%7D%20P(D%7C%5Ctheta)%26%3DP(x_1%2Cx_2%2C%5Ccdots%2Cx_n%7C%5Ctheta)%3D%5Cprod_%7Bi%3D1%7D%5En%20p(x_i%7C%5Ctheta)%20%5Cend%7Balign%7D

math?formula=P(D%7C%5Ctheta)被称为似然函数,它描述了参数

math?formula=%5Ctheta的合理程度。一组合适的参数

math?formula=%5Ctheta应能使得

math?formula=P(D%7C%5Ctheta)尽量大。所以,求解

math?formula=%5Ctheta最直观的想法是找到一组

math?formula=%5Ctheta使得

math?formula=P(D%7C%5Ctheta)最大。为了计算的方便,我们定义对数似然函数

math?formula=l(%5Ctheta)%3D%5Cln%20P(D%7C%5Ctheta)%3D%5Csum_%7Bi%3D1%7D%5En%20%5Cln%20P(x_i%7C%5Ctheta).

于是,

math?formula=%5Ctheta的估计值为

math?formula=%5Chat%7B%5Ctheta%7D%3D%5Carg%20%5Cmax_%7B%5Ctheta%7D%20l(%5Ctheta).

由于极值点往往在导数等于0的地方取得,我们常常通过求导的方法求解上式,即

math?formula=%5Cfrac%7B%5Cpartial%20l(%5Chat%7B%5Ctheta%7D)%7D%7B%5Cpartial%20%5Cmu%7D%20%3D%200%2C%20%5Cfrac%7B%5Cpartial%20l(%5Chat%7B%5Ctheta%7D)%7D%7B%5Cpartial%20%5Csigma%7D%3D0.

解得

math?formula=%5Cbegin%7Balign%7D%20%5Chat%7B%5Cmu%7D%26%3D%5Cfrac%7B1%7D%7Bn%7D%20%5Csum_%7Bi%3D1%7D%5En%20x_i%2C%5C%5C%20%5Chat%7B%5Csigma%7D%26%3D%5Cfrac%7B1%7D%7Bn%7D%5Csum_%7Bi%3D1%7D%5En%20(x_i-%5Chat%7B%5Cmu%7D)%5E2.%20%5Cend%7Balign%7D

引入EM算法

在上一节,我们看到极大似然估计通过最大化似然函数的方法,找到模型的最佳参数。在这个过程中,我们需要对对数似然函数求偏导,令导数等于0,进而得到参数的估计值。然而,在某些复杂的模型中,由于存在“隐变量”,我们难以通过对对数似然函数求偏导的方法得到参数的估计值。

我们考虑如下的三硬币问题

假设有3枚硬币,分别记作A、B、C,这些硬币正面朝上的概率分别为

math?formula=%5Cpi%2Cp%2Cq。现进行如下的抛掷实验:首先掷硬币A,根据其结果选择硬币B和硬币C,如果是正面选择硬币B,如果是反面选择硬币C;然后掷选出的硬币,记录掷硬币的结果,正面记作1,反面记作0;独立地重复n次实验(这里,n=10),观测结果如下:

math?formula=1%2C1%2C0%2C1%2C0%2C0%2C1%2C0%2C1%2C1.

假设只能观测到掷硬币的结果,不能观测掷硬币的过程。问如何估计三硬币模型的参数

math?formula=%5Cpi%2Cp%2Cq

math?formula=%5Ctheta%3D(%5Cpi%2Cp%2Cq)。首先,让我们尝试使用极大似然估计的方法,写出对数似然函数,然后求解

math?formula=%5Ctheta。不妨令每次实验中硬币A的抛掷结果为

math?formula=z,硬币B和C的抛掷结果为

math?formula=y。于是

math?formula=P(y%7C%5Ctheta)%3D%5Csum_z%20P(y%2Cz%7C%5Ctheta)%3D%5Csum_z%20P(z%7C%5Ctheta)P(y%7Cz%2C%5Ctheta).

显然,

math?formula=z的取值有0、1两种,并且

math?formula=P(z%3D0%7C%5Ctheta)%3D1-%5Cpi%2CP(z%3D1%7C%5Ctheta)%3D%5Cpi。而当

math?formula=z%3D0时,意味着选择了硬币C,此时

math?formula=P(y%7Cz%2C%5Ctheta)%3Dq%5Ey(1-q)%5E%7B1-y%7D;同理,当

math?formula=z%3D1时,有

math?formula=P(y%7Cz%2C%5Ctheta)%3Dp%5Ey(1-p)%5E%7B1-y%7D。于是,上式可展开为

math?formula=P(y%7C%5Ctheta)%3D%5Cpi%20p%5Ey(1-p)%5E%7B1-y%7D%2B(1-%5Cpi)q%5Ey(1-q)%5E%7B1-y%7D.

对于三硬币模型多次抛掷的结果

math?formula=Y%3D%5Cleft%5By_1%2Cy_2%2C%5Ccdots%2Cy_n%5Cright%5D,有

math?formula=P(Y%7C%5Ctheta)%3D%5Cprod_%7Bi%3D1%7D%5En%20P(y_i%7C%5Ctheta).

相应的对数似然函数为

math?formula=l(%5Ctheta)%3D%5Cln%20P(Y%7C%5Ctheta)%3D%5Csum_%7Bi%3D1%7D%5En%20%5Cln%20%5Cleft%5B%20%5Cpi%20p%5Ey_i(1-p)%5E%7B1-y_i%7D%2B(1-%5Cpi)q%5E%7By_i%7D(1-q)%5E%7B1-y_i%7D%20%5Cright%5D.

事实证明对上式求偏导,令导数等于0,是无法得到解析解的。也就是极大似然估计无法解决上述问题。

使用EM算法求解

一方面,假设我们知道模型参数

math?formula=%5Cpi%2Cp%2Cq,我们就可以预测某次实验中的抛掷结果来源于硬币B的概率,不妨记

math?formula=%5Cmu%3DP(z%3D1%7Cy%2C%5Ctheta),有

math?formula=%5Cmu%20%3D%20%5Cfrac%7BP(y%7Cz%3D1%2C%5Ctheta)%7D%7BP(y%7Cz%3D1%2C%5Ctheta)%2BP(y%7Cz%3D0%2C%5Ctheta)%7D%20%3D%20%5Cfrac%7B%5Cpi%20p%5Ey(1-p)%5E%7B1-y%7D%7D%7B%5Cpi%20p%5Ey(1-p)%5E%7B1-y%7D%20%2B%20(1-%5Cpi)%20q%5Ey(1-q)%5E%7B1-y%7D%7D.

另一方面,假设我们已知每次实验中抛掷结果来源于硬币B的概率,我们就可以估计模型参数

math?formula=%5Cpi%2Cp%2Cq,即

math?formula=%5Cbegin%7Balign%7D%20%5Chat%7B%5Cpi%7D%20%26%3D%20%5Cfrac%7B1%7D%7Bn%7D%20%5Csum_%7Bi%3D0%7D%5En%20%5Cmu_i%2C%5C%5C%20%5Chat%7Bp%7D%20%26%3D%20%5Cfrac%7B%5Csum_%7Bi%3D0%7D%5En%20%5Cmu_iy_i%7D%7B%5Csum_%7Bi%3D0%7D%5En%20%5Cmu_i%7D%2C%5C%5C%20%5Chat%7Bq%7D%20%26%3D%20%5Cfrac%7B%5Csum_%7Bi%3D0%7D%5En%20(1-%5Cmu_i)y_i%7D%7B%5Csum_%7Bi%3D0%7D%5En%20(1-%5Cmu_i%EF%BC%89%7D.%20%5Cend%7Balign%7D

也就是说,当我们已知模型参数的时候,我们可以计算

math?formula=%5Cmu的值;当我们知道

math?formula=%5Cmu的值时,我们可以估计模型参数。对于这种蛋鸡问题,使用迭代法求解是最好不过了。也就是,先假设一组模型参数,比如

math?formula=%5Cpi%3D0.5%2Cp%3D0.5%2Cq%3D0.5,然后计算

math?formula=%5Cmu的值;接着使用

math?formula=%5Cmu的值,去估计模型参数。重复上述过程多次之后,我们就可以得到

math?formula=%5Chat%7B%5Cpi%7D%3D0.5%2C%5Chat%7Bp%7D%3D0.6%2C%5Chat%7Bq%7D%3D0.6

在上述过程中,我们称

math?formula=%5Cmu为模型的隐变量,也即是无法观测到但也不是模型参数的变量。估计

math?formula=%5Cmu的过程称为E步,使用

math?formula=%5Cmu估计模型参数的过程称为M步。下面使用python实现了对三硬币模型的求解。

def E_step(pi,p,q,Y):

Mu = []

for y in Y:

pyz1 = pi * p**y * (1-p)**(1-y)

pyz0 = (1-pi) * q**y * (1-q)**(1-y)

mu = pyz1 / (pyz1 + pyz0)

Mu.append(mu)

return Mu

def M_step(Mu,Y):

n = len(Mu)

pi = sum(Mu)/n

p = sum([Mu[i]*Y[i] for i in range(n)]) / sum(Mu)

q = sum([(1-Mu[i])*Y[i] for i in range(n)]) / (n-sum(Mu))

return pi,p,q

def solve_tree_coin(Y,iter_num):

pi = p = q = 0.5

for i in range(iter_num):

Mu = E_step(pi,p,q,Y)

pi,p,q = M_step(Mu,Y)

print(pi,p,q)

if __name__ == "__main__":

Y = [1,1,0,1,0,0,1,0,1,1]

solve_tree_coin(Y,5)

说句题外话,三硬币问题是没有办法正确估计模型的参数。如

math?formula=%5Cpi%20%3D%200.6%2Cp%20%3D%201%2Cq%20%3D%200

math?formula=%5Cpi%3D0.5%2Cp%3D0.6%2Cq%3D0.6 在输出结果上是等价的。毕竟,对于这个模型观测数据太少了,任何方法对此都无能为力。

下面我们将正式地对EM算法进行介绍,并运用EM算法求解混合高斯模型。

EM算法的思想

在这里不介绍EM算法的形式化定义(因为没有必要),只是概括一下EM算法的核心思想。

EM算法的全称是Expectation Maximization Algorithm,也就是期望极大算法。EM算法适用于含有隐变量的模型。求解过程分为E步和M步:在E步中求解隐变量的期望;在M步中使用隐变量的期望代替隐变量的值,求解模型参数。

理论上(有兴趣的见李航的《统计学习方法),EM算法并不能保证得到全局最优值,而且很依赖所选取初始值的好坏,但EM算法简化了很多复杂问题的求解。计算机科学中的许多方法都是基于这样的思路。

使用EM求解GMM

首先介绍一下GMM。

GMM的全称是Gauss Mixture Model,即混合高斯模型,也就是将多个高斯分布叠加在一起。假设GMM中高斯分布的数量为M,那么相应的概率密度函数为

math?formula=p(x)%20%3D%20%5Csum_%7Bi%3D1%7D%5EM%20a_iN(x%3B%5Cmu%2C%5CSigma).

如下图就是一个GMM概率密度函数的函数图像。

775cef353705

概率密度函数P(x)=0.7N(-10,2)+0.3N(5,3)的函数图像

GMM产生样本的过程和三硬币模型有些类似,首先按照先验概率

math?formula=a_i选择一个高斯分布,然后产生一个满足这个高斯分布的样本。如下图即为GMM产生的2维样本数据。

775cef353705

为了使用EM算法,我们首先要确定模型中的隐变量。在GMM中,隐变量是样本属于不同高斯分布的概率

math?formula=P(y%3Di)。于是在E步,我们这样估计隐变量

math?formula=P(y_t%3Di)%3D%5Cfrac%7Ba_iN(x_t%3B%5Cmu_i%2C%5CSigma_i)%7D%7B%5Csum_%7Bi%3D1%7D%5EM%20a_iN(x_t%3B%5Cmu_i%2C%5CSigma_i)%7D.

在M步,模型参数的估计过程如下

math?formula=%5Cbegin%7Balign%7D%20a_i%20%26%20%3D%5Cfrac%7B1%7D%7Bn%7D%5Csum_%7Bt%3D1%7D%5En%20P(y_t%3Di)%5C%5C%20%5Cmu_i%20%26%3D%20%5Cfrac%7B%5Csum_%7Bt%3D1%7D%5En%20P(y_t%3Di)x_t%7D%7B%5Csum_%7Bt%3D1%7D%5En%20P(y_t%3Di)%7D%5C%5C%20%5CSigma_t%20%26%3D%20%5Cfrac%7B%5Csum_%7Bt%3D1%7D%5En%20P(y_t%3Di)(x_t-%5Cmu_i)(x_t-%5Cmu_i)%5Et%7D%7B%5Csum_%7Bt%3D1%7D%5En%20P(y_t%3Di)%7D%20%5Cend%7Balign%7D

介绍EM算法的大多文章都是拿GMM举例的,但作为机器学习中的一类重要的思想,EM算法的应用非常广泛,如IBM翻译模型、主题模型等等。

先写到这里吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
凯撒加密算法是一种简单的移位密码,其核心思想是将明文的每一个字符在密码系统所支持的字符序列中向右平移N,映射得到新的字符从而实现加密,解密则相反向左平移N。加密的Key即为N。[1] 在C语言实现凯撒加密算法,可以通过编写代码来实现。首先,需要定义一个函数来进行加密和解密操作。这个函数接受两个参数,一个是明文字符串,另一个是平移的位数N。然后,使用循环遍历明文字符串中的每一个字符,对每个字符进行平移操作,得到加密或解密后的字符,最后将加密或解密后的字符拼接成最终的结果字符串。 以下是一个简单的C语言凯撒加密算法的实现示例: ```c #include <stdio.h> void caesarEncryptDecrypt(char *str, int key) { int i = 0; char ch; while (str[i != '\0') { ch = str[i]; // 判断字符是否为大写字母 if (ch >= 'A' && ch <= 'Z') { ch = ch + key; if (ch > 'Z') { ch = ch - 'Z' + 'A' - 1; } else if (ch < 'A') { ch = ch + 'Z' - 'A' + 1; } str[i = ch; } // 判断字符是否为小写字母 else if (ch >= 'a' && ch <= 'z') { ch = ch + key; if (ch > 'z') { ch = ch - 'z' + 'a' - 1; } else if (ch < 'a') { ch = ch + 'z' - 'a' + 1; } str[i = ch; } i++; } } int main() { char strs", str); printf("请输入平移的位数N:"); scanf("%d", &key); caesarEncryptDecrypt(str, key); printf("加密/解密后的字符串为:%s\n", str); return 0; } ``` 通过编写以上的代码,可以实现C语言中使用凯撒加密算法进行字符串的加密和解密操作。用户需要输入明文字符串和平移的位数N,程序会输出加密或解密后的字符串作为结果。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [C语言实现简单加密算法 凯撒密码 RSA算法 简介及实现](https://blog.csdn.net/weixin_53538467/article/details/129813068)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [2022电子设计竞赛-基于c语言实现的四轴飞行器跟踪小车源码及资料.zip](https://download.csdn.net/download/RuanJian_GC/88270428)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值