An unique Monkey

Be what you wanted to be

PLSA:简明教程和示例代码

主题模型

PLSA是一种经典的主题模型,关于文本的主题模型的定义我们引用wiki上的定义:直观来讲,如果一篇文章有一个中心思想,那么一些特定词语会更频繁的出现。比方说,如果一篇文章是在讲狗的,那“狗”和“骨头”等词出现的频率会高些。如果一篇文章是在讲猫的,那“猫”和“鱼”等词出现的频率会高些。而有些词例如“这个”、“和”大概在两篇文章中出现的频率会大致相等。但真实的情况是,一篇文章通常包含多种主题,而且每个主题所占比例各不相同。因此,如果一篇文章10%和猫有关,90%和狗有关,那么和狗相关的关键字出现的次数大概会是和猫相关的关键字出现次数的9倍。一个主题模型试图用数学框架来体现文档的这种特点。主题模型自动分析每个文档,统计文档内的词语,根据统计的信息来断定当前文档含有哪些主题,以及每个主题所占的比例各为多少。
举例如下:假设一篇文章可以由:arts budgets childern education 4个topic组成,那么下面这篇文章在各个topic上的分布可以用如下具体形式表现出来:
topic-model-topic-pic
并且每个topic下哪些词出现的概率比较大
topic-model-topic-pic
主题模型:wiki链接
而主题模型在文本挖掘和语义分析中有着很重大的作用,如果我们能够通过主题模型准确地获取到每篇文章的主题分布那么我们就能很轻松的对文本进行聚类和分类的分析。主题模型就是尽可能真实、准确地帮我们寻找出每篇文章在每个topic上的概率分布,以及顺手可以拿到每个topic在每个word上的概率分布。

PLSA

Probabilistic latent semantic analysis直译就是概率潜语义分析,PLSA通过统计的方法对文档进行建模,建的模型也很简单,用如下的图就可以表示:
topic-model-model-pic

建模过程

  1. doc i以一个多项分布的概率生成topic z: $p(t_z|d_i)$
  2. topic z以一个多项分布的概率生成word j: $p(w_j|t_z)$

最终我们希望通过N篇文档的语料信息求得每一个$p(t_z|d_i)$和$p(w_j|t_z)$的多项分布,转化成矩阵的形式也就是求上图的$P(T|D)$和$P(W|T)$。得到了$P(T|D)$和$P(W|T)$的分布以后我们至少可以做如下两件事情:

  • 对于训练语料的N篇文档,可以通过$p(T|d_{i})$的分布进行聚类(最简单的就是用kmeans在欧式空间里将文档进行聚类)
  • 新进来一篇文档,可以通过inference的方法推断出该文档在训练出来的topic上的分布,从而对新文档进行聚类或者分类

训练方法

接下来我们就要求解$P(T|D)$和$P(W|T)$这2个矩阵了,从统计的角度来说最直观的解法就是求$P(T|D)*P(W|T)$的概率似然函数最大。但是在实际求解的过程中会发现中间的topic是一个隐含变量对于我们的似然函数来说求导是很不方便的,经典的求解隐含变量的方法当然是EM算法,在原论文中的求解方法也是通过EM算法进行的求解:PLSA论文
在这里我们就不列出那一大堆的推倒公式了,有兴趣的同学可以参考上面的论文,如果想看中文的推导过程也可以参考以下2篇博文:
EM算法推导
EM应用在PLSA求解

PLSA代码

虽然算法看上去有点复杂,但是实现完了之后发现其实还是挺清晰明朗的,2分代码都是用C++编写的,一个是单线程版本,一个是多线程版本(用到了boost中的多线程,所以应该需要安装boost库才能编译并行的版本)。在github上的链接如下:
README里面都有编译教程、训练语料数据(IMDB上的2W5条影评数据)、使用教程
单线程版本
多线程版本

实现效果

PLSA训练的每个topic中前20个概率比较大的词:
topic101
topic7
topic81
从测试结果分析topic 101中就出现了killer murder comic villian suspect murdered 等词汇,那就表明这个topic可能跟犯罪,凶杀相关的主题比较相近
而topic 7中出现的sex female naked lover等词语可能表明这个主题和性,情爱的主题比较相近。
但是有些topic的词语在主观上却很难寻找出一些实际的物理意义,感觉不知所云,比如topic81中出现这些一大堆but always when等等词语很难抽象出其物理意义是什么。这是因为PLSA本身就是一种无监督算法,它只能帮你尽可能的去模拟每个文章的主题分布,但是这个主题分布和是不是你想要的某种实际物理含义的主题分布基本上是没有关系的。

一些技巧

  1. 在训练完成以后已经可以得到每一篇训练文档在每个主题上的分布情况,新进来的文档我们可以通过这篇文档在word上的分布和$P(W|T)$这个矩阵推断出它在各个topic上的分布。具体方法就是用这一篇doc来跑EM算法的迭代,但是每次迭代的时候只更新$p(T|d)$和$p(T)$,别的参数都用训练时候得出的值固定不变,一般这样迭代20次以后就能得出新doc在个topic上的分布。
  2. 在EM算法迭代中的E步时,我们需要求的就是$P(T|W,D)$这样一个三维矩阵,假设我们有25000篇doc,10000个word,200个topic,用浮点数表示每个元素那么这个矩阵需要的内存最起码为25000 * 10000 * 200 * 4=50G的空间。这对于一般人的电脑是无法负担的吧。所以我们在实现的时候运用了数据的稀疏性,并没有存一个全量的$P(T|W,D)$,而只是存了在doc i中出现了word j的$p(T|w_j,d_i)$,这样所需要的内存就大大减小了。然后在迭代的计算过程中也会有类似的trick让我们可以大大减少一些无用的计算量,加快计算速度。
    具体代码在上面2个版本中都有实现。

HouXin

A designer, developer and entrepreneur. Spends his time travelling the world with a bag of kites. Likes journalism and publishing platforms.

Comments

<% if(config.disqus_shortname) { %>
<% } %>