R语言中EM算法估计高斯混合模型参数

EM算法

leengsmile
2016年9月24日

EM 算法

本文档介绍如何在R语言中,通过EM算法,估计高斯混合模型的参数。首先通过简单的例子,用简单的程序描述EM算法估计高斯混合模型参数的过程,再介绍如何使用第三方包实现相应的估计。

为保证数据结果的可重复性,设置随机数种子

set.seed(123)

首先需要高斯混合模型的数据

n <- 1000
mean_s <- c(1, 7)
y <- sample(c("head", "tail"), size = n, replace = TRUE, prob = c(0.25, 0.75))
x <- rnorm(n = 1000, mean = mean_s[1])
tails <- y %in% c("tail")
x[tails] <- rnorm(sum(tails), mean = mean_s[2])

上述产生的混合模型是均值分别为1和7,标准差均为1的混合模型,且混合的概率为(0.25, 0.75)。 也就是说,混合模型中的观测值有0.25的概率来自于均值为1的高斯分布,有0.75的概率来自于均值为7的高斯分布。

其概率概率密度函数为

require(lattice)
densityplot(~x, par.settings = list(plot.symbol = list(col = factor(y))))
density-plot.png

数据分布明显,呈现很好的可分性。下面需要估计对应的正太分布的均值,以及混合概率。这里假定方差恒定且相等,均为1。

probs <- c(0.5, 0.5)
mu_s <- c(0, 1)
sigma_s <- c(1, 1)
for(i in seq(10))
{
    ps <- matrix(0, ncol = 2, nrow = n)
    for(j in seq(2))
    {
        ps[, j] <- probs[j] * dnorm(x, mean = mu_s[j], sd = sqrt(sigma_s[j]))
    }
    ps <- ps / rowSums(ps)
    
    for(j in seq(2))
    {
        sigma_s[j] <- sum( ps[, j] * (x - mu_s[j])^2) / sum(ps[, j])
        mu_s[j] <- sum(x * ps[, j]) / sum(ps[, j])
        probs[j] <- mean(ps[, j])
        
    }
    
}

cat(
    "mean:", mean_s, "\n", 
    "sigma:", sqrt(sigma_s), "\n", 
    "prob:", probs, "\n", 
    sep = " "
)
## mean: 1 7 
##  sigma: 0.9415133 0.9913065 
##  prob: 0.2469451 0.7530549

估计的均值分别为0.9748991, 6.9999522,混合概率为0.2469451, 0.7530549。经过10次迭代,估计值已经很接近精确值。

可以将上述求解的过程封装成一个函数

gmm <- function(x, mean, sd = NULL)
{
    num <- length(mean)
    if(is.null(sd))
    {
        sd <- rep(1, num)
    }
    
    epsilon <- 1e-4
    probs <- rep(1/num, num)
    mu_s <- mean
    sigma_s <- sd ^ 2
    n <- length(x)
    while(TRUE)
    {
        
        ps <- matrix(0, ncol = num, nrow = n)
        for(j in seq(num))
        {
            ps[, j] <- probs[j] * dnorm(x, mean = mu_s[j], sd = sqrt(sigma_s[j]))
        }
        ps <- ps / rowSums(ps)
        
        sigma_s_p <- sigma_s
        for(j in seq(num))
        {
            sigma_s[j] <- sum( ps[, j] * (x - mu_s[j])^2) / sum(ps[, j])
            mu_s[j] <- sum(x * ps[, j]) / sum(ps[, j])
            probs[j] <- mean(ps[, j])
            
        }
        
        if (max(abs(sigma_s_p - sigma_s)) < epsilon)
        {
            break
        }
    
    }
    
    return (list(mu = mu_s, sd = sqrt(sigma_s), prob = probs))
    
}

上述封装的函数gmm用以估计高斯混合模型的参数,包括各个混合成分的均值mu,标准差sd,混合成分的概率prob

gmm估计前面提到的数据x

gmm(x, mean = c(0, 1), sd = c(1, 1))
## $mu
## [1] 0.9749062 6.9999548
## 
## $sd
## [1] 0.9415230 0.9913026
## 
## $prob
## [1] 0.2469457 0.7530543

R语言中,可以通过mixtools包实现上述的EM算法估计过程。

首先载入mixtools

require(mixtools)

mixtoolsnormalmixEM可以实现高斯混合模型的参数估计。

em <- normalmixEM(x, mu = c(0, 1), sigma = c(1, 1), sd.constr = c(1, 1))
## number of iterations= 6

估计的结果中,lambda含有混合比例,mu是混合成分的均值。

print(em$lambda)
## [1] 0.2471721 0.7528279
print(em$mu)
## [1] 0.9777237 7.0008419

从上面的结果可知,normalmixEM的估计结果与前面编写的程序估计出的参数一致。

plot(em, whichplots = 2)
estimated-density-plot.png

同时,返回的em变量,含有许多有用的信息

str(em)
## List of 9
##  $ x         : num [1:1000] 6.179 0.0063 6.6927 1.7511 -0.5092 ...
##  $ lambda    : num [1:2] 0.247 0.753
##  $ mu        : num [1:2] 0.978 7.001
##  $ sigma     : num [1:2] 1 1
##  $ loglik    : num -1955
##  $ posterior : num [1:1000, 1:2] 6.14e-07 1.00 2.78e-08 1.00 1.00 ...
##   ..- attr(*, "dimnames")=List of 2
##   .. ..$ : NULL
##   .. ..$ : chr [1:2] "comp.1" "comp.2"
##  $ all.loglik: num [1:7] -15574 -2260 -1955 -1955 -1955 ...
##  $ restarts  : num 0
##  $ ft        : chr "normalmixEM"
##  - attr(*, "class")= chr "mixEM"

后验概率是一个$n \times k$的矩阵,是每个观测值由各个混合成分产生的概率,可以据此得到每个观测值的可能类别。

label <- c("head", "tail")[apply(em$posterior, 1, which.max)]

数据真正的标签在y中,可以得到混淆矩阵

xtabs( ~ y + label)
##       label
## y      head tail
##   head  247    1
##   tail    0  752

248个head,有1被标记为tail,标记错误。

可以进一步查看该观测值

x[y != label]
## [1] 4.390371

该值更靠近tail,所以高斯混合模型的结果判定为tail也不足为奇。

参考文献

[1]. http://rstudio-pubs-static.s3.amazonaws.com/1001_3177e85f5e4840be840c84452780db52.html

[2]. https://en.wikibooks.org/wiki/Data_Mining_Algorithms_In_R/Clustering/Expectation_Maximization_(EM)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容