放牧代码和思想
专注自然语言处理、机器学习算法
    This thing called love. Know I would've. Thrown it all away. Wouldn't hesitate.

LDA入门与Java实现

目录

这是一篇面向工程师的LDA入门笔记,并且提供一份开箱即用Java实现。本文只记录基本概念与原理,并不涉及公式推导。文中的LDA实现核心部分采用了arbylon的LdaGibbsSampler并力所能及地注解了,在搜狗分类语料库上测试良好,开源在GitHub上

什么是主题模型

在我的博客上,有篇文章《基于双数组Trie树的Aho Corasick自动机极速多模式匹配》被归入算法目录,算法即为该文章的主题。而该文章因为涉及到中文分词,又被我归入了分词目录。所以该文章的主题并不单一,具体来说文中80%在讲算法,20%稍微讲了下在分词中的应用。

传统的文本分类器,比如贝叶斯、kNN和SVM,只能将其分到一个确定的类别中。假设我给出3个分类“算法”“分词”“文学”让其判断,如果某个分类器将该文归入算法类,我觉得还凑合,如果分入分词,那我觉得这个分类器不够准确。

假设一个文艺小青年来看我的博客,他完全不懂算法和分词,自然也给不出具体的备选类别,有没有一种模型能够告诉这个白痴,这篇文章很可能(80%)是在讲算法,也可能(19%)是在讲分词,几乎不可能(1%)是在讲其它主题呢?

有,这样的模型就是主题模型。

什么是LDA

简述

潜在狄立克雷分配(Latent Dirichlet Allocation,LDA)主题模型是最简单的主题模型,它描述的是一篇文章是如何产生的。如图所示:

图1

从左往右看,一个主题是由一些词语的分布定义的,比如蓝色主题是由2%几率的data,2%的number……构成的。一篇文章则是由一些主题构成的,比如右边的直方图。具体产生过程是,从主题集合中按概率分布选取一些主题,从该主题中按概率分布选取一些词语,这些词语构成了最终的文档(LDA模型中,词语的无序集合构成文档,也就是说词语的顺序没有关系)。

如果我们能将上述两个概率分布计算清楚,那么我们就得到了一个模型,该模型可以根据某篇文档推断出它的主题分布,即分类。由文档推断主题是文档生成过程的逆过程。

在《LDA数学八卦》一文中,对文档的生成过程有个很形象的描述:

图2

概率模型

LDA是一种使用联合分布来计算在给定观测变量下隐藏变量的条件分布(后验分布)的概率模型,观测变量为词的集合,隐藏变量为主题。

联合分布

LDA的生成过程对应的观测变量和隐藏变量的联合分布如下:

式子有点长,耐心地从左往右看:

式子的基本符号约定——β表示主题,θ表示主题的概率,z表示特定文档或词语的主题,w为词语。进一步说——

β1:K为全体主题集合,其中βk是第k个主题的词的分布(如图1左部所示)。第d个文档中该主题所占的比例为θd,其中θd,k表示第k个主题在第d个文档中的比例(图1右部的直方图)。第d个文档的主题全体为zd其中zd,n是第d个文档中第n个词的主题(如图1中有颜色的圆圈)。第d个文档中所有词记为wd,其中wd,n是第d个文档中第n个词,每个词都是固定的词汇表中的元素。

p(β)表示从主题集合中选取了一个特定主题,p(θd)表示该主题在特定文档中的概率,大括号的前半部分是该主题确定时该文档第n个词的主题,后半部分是该文档第n个词的主题与该词的联合分布。连乘符号描述了随机变量的依赖性,用概率图模型表述如下:

比如,先选取了主题,才能从主题里选词。具体说来,一个词受两个随机变量的影响(直接或间接),一个是确定了主题后文档中该主题的分布θd,另一种是第k个主题的词的分布βk(也就是图2中的第二个坛子)。

后验分布

沿用相同的符号,LDA后验分布计算公式如下:

分子是一个联合分布,给定语料库就可以轻松统计出来。但分母无法暴力计算,因为文档集合词库达到百万(假设有w个词语),每个词要计算每一种可能的观测的组合(假设有n种组合)的概率然后累加得到先验概率,所以需要一种近似算法。

基于采样的算法通过收集后验分布的样本,以样本的分布求得后验分布的近似。

θd的概率服从Dirichlet分布,zd,n的分布服从multinomial分布,两个分布共轭,所谓共轭,指的就是先验分布和后验分布的形式相同:

两个分布其实是向量的分布,向量通过这两个分布取样得到。采样方法通过收集这两个分布的样本,以样本的分布近似。

马氏链和Gibbs Sampling

这是一种统计模拟的方法。

马氏链

所谓马氏链指的是当前状态只取决于上一个状态。马氏链有一个重要的性质:状态转移矩阵P的幂是收敛的,收敛后的转移矩阵称为马氏链的平稳分布。给定p(x),假如能够构造一个P,转移n步平稳分布恰好是p(x)。那么任取一个初始状态,转移n步之后的状态都是符合分布的样本。

Gibbs Sampling

Gibbs Sampling是高维分布(也即类似于二维p(x,y),三维p(x,y,z)的分布)的特化采样算法。

更深入的数学知识请参考附录,能力有限,等完全看懂了再更新这部分。

开源项目

A Java implemention of LDA

LDA4j:https://github.com/hankcs/LDA4j 

其中LdaGibbsSampler.java直接照搬 Gregor Heinrich 的实现,我也考察过yangliuy和ansj的实现,二者也都是从Gregor Heinrich派生出来的。

调用方法

// 1. 从磁盘载入语料
Corpus corpus = Corpus.load("data/mini");
// 2. 创建 LDA 采样器
LdaGibbsSampler ldaGibbsSampler = new LdaGibbsSampler(corpus.getDocument(), corpus.getVocabularySize());
// 3. 训练,目标10个主题
ldaGibbsSampler.gibbs(10);
// 4. phi 矩阵是唯一有用的东西,用 LdaUtil 来展示最终的结果
double[][] phi = ldaGibbsSampler.getPhi();
Map<String, Double>[] topicMap = LdaUtil.translate(phi, corpus.getVocabulary(), 10);
LdaUtil.explain(topicMap);

输出

topic 0 :
公司=0.009538408630174017
市场=0.008848009751698062
中国=0.008756489189917975
企业=0.0068280510303913395
发展=0.005991900977658479
目前=0.004408401842957633
产品=0.0041981128106208625
服务=0.003756081561227181
已经=0.003410105744626914
记者=0.003289155629929911

topic 1 :
专业=0.00872496522205349
工作=0.008108171408190876
学生=0.00793944661866665
学校=0.006307480899983371
考生=0.005295205701518912
大学=0.0052671267600129445
教育=0.0051547106121291805
考试=0.00507254577329609
人才=0.004037747449851247
招聘=0.003913811857165103

topic 2 :
医院=0.006197066939127888
治疗=0.0048149451145789455
患者=0.0032264139617756145
健康=0.0026521203697810374
手术=0.0025525793863978826
女性=0.0023724111474892357
专家=0.0021711200905248276
发现=0.0021645199996586885
病人=0.0021567877663232846
医生=0.002155356316589454

topic 3 :
没有=0.008818728535385055
问题=0.00476170232225101
中国=0.00476161560515722
工作=0.004610303190509696
生活=0.004283310385880329
文化=0.0036558079614339278
孩子=0.003327977201447208
不能=0.0032901108349775716
知道=0.003127437274214269
已经=0.0030419673256694545

topic 4 :
公司=0.018241005428669386
股东=0.009281048036676322
股份=0.0078638937643388
搜狐=0.0065617441267974705
有限公司=0.006139808167975946
直播员=0.005439495997416965
股权=0.005353954615162839
项目=0.004984451830097043
发行=0.004511099443364358
改革=0.004489038403046334

topic 5 :
旅游=0.013331508385667979
游客=0.004296589238778804
城市=0.0032312276892446116
文化=0.0026831367778820704
旅行社=0.002242817493567529
世界=0.0021001546909288965
成都=0.001991337289815279
活动=0.001894687770595843
北京=0.0017106388886854072
公园=0.0016134766410937638

topic 6 :
美国=0.007679518424242107
日本=0.004777746687572576
训练=0.003947682941243526
系统=0.003926562149803556
飞机=0.0038757503504304267
部队=0.00365041154980242
进行=0.003644226666909795
军事=0.003637873811678725
作战=0.003407296869780034
装备=0.003319112427162246

topic 7 :
比赛=0.0092171879571152
队员=0.0036851386114063237
联赛=0.0032845199043377146
球队=0.0029432131822116707
冠军=0.0024090127058022104
俱乐部=0.002348957542679953
球员=0.0022159606741087795
决赛=0.002192739194333911
赛季=0.0020352324832133267
对手=0.001974829226645783

topic 8 :
The=0.002190604616155811
意思=0.001186435720799536
It=0.0011515962078723501
理解=0.0010433831740419728
What=9.560997173453189E-4
They=9.345962358267594E-4
听力=8.362275772461826E-4
In=8.166984660263638E-4
阅读=7.775969918239417E-4
译文=7.568900132152651E-4

topic 9 :
毛泽东=0.002633793448326645
曹操=0.0018832599387516155
曹丕=0.0016567353952110328
皇帝=0.001629990508040292
甄洛=0.0012930147890964736
中央=0.0012783947883529055
蒋介石=0.0010732052837016102
曹睿=8.511476483731437E-4
女王=8.125680914406854E-4
皇后=8.013815303127338E-4

说明

上述语料来自搜狗分类语料库,使用HanLP分词并去除停用词。如果你需要对其他语料进行中文主题分析\文本分类,那么你需要提前分词,分词后使用空格隔开即可。

TODO

Gregor Heinrich实现的LdaGibbsSampler.java非常简练,但其中的原理对于我这种外行来讲非常难懂。我所作的不过是将这份算法拿过来包装一下,做了个试验证明对方是正确的而已,另外添加了一个方法叫inference,可以推断新文档的主题分布,不知道写得对不对,请指正。

我看完了《LDA数学八卦》,依然似懂非懂,所以上面的笔记也不敢多写。LDA严格上属于ML的范畴,只不过经常被NLP界借用来做主题分析才进入我的视野。可惜我的数学修养非常有限,对于其中的数学原理感到难以理解,只是看出了大概是这么一个过程,通过这么一些公式,求出了两个矩阵。至于为什么可以这么做,如何优化,如何并行计算,我都无能为力。

我的知识体系是一种遍历的方式,从一个起点开始,在知识网上面爬行。要理解节点A,必须先理解节点B,那我就会向B移动。在NLP这个子图的边缘,由LDA这个节点进入了ML这个子图,发现这个子图更加复杂,有许多节点指向了数理统计。

这样看来,我欠缺的基础知识非常多,接下来可能会在数学/ML这几块充电。

Reference

http://blog.csdn.net/mytestmy/article/details/39269105

text-est.pdf

LDA-wangyi.pdf

LDA数学八卦.pdf

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » LDA入门与Java实现

评论 19

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #7

    怎么从数据库获取呢,Corpus能注释下吗

    wu4年前 (2021-04-28)回复
  2. #6

    theta这个矩阵也很重要啊!好像没有翻译它的方法吧,望加

    潇湘菌子7年前 (2018-02-27)回复
  3. #5

    首先感谢hankcs博主的分享,在使用LDA4j的过程中,我重写了Corpus类里的load和loadDocument方法,从数据库中读写数据测试成功。但是测试的过程中遇到了一个问题,就是先用训练集训练出来phi,然后拿来一个新文档使用这个phi推断其概率分布发现报数组越界的错误,我初步调试发现一旦新文档中包含训练集中没有的生单词,你写的Inference便无法使用,这个问题希望博主能进行一下异常处理。

    woohaoshu8年前 (2017-06-05)回复
    • 为了得到两个概率分布,那就需要知道有哪些document,topic,word。有了语料库就相当于有了document参量,那topic和word这怎么得来呢?是document分词得到word词典吗?如果是这样的话,topic怎么得来。期待您能解疑。

      陈陈7年前 (2018-01-11)回复
    • 求一下Corpus从数据库中读取数据的代码,可以吗,谢谢

      wu4年前 (2021-04-28)回复
  4. #4

    博主你好,请问如何得到topic的归纳?比如topic7,归纳为体育。

    dancychou9年前 (2016-07-05)回复
  5. #3

    《LDA漫游指南》也不错,快去看看吧,《LDA漫游指南》这本书讲的系统:http://yuedu.baidu.com/ebook/d0b441a8ccbff121dd36839a

    kingdoman9年前 (2016-01-19)回复
  6. #2

    如何去除停用词??

    songhh10年前 (2015-04-27)回复
  7. #1

    给个建议哇,尽量把需要的jar包也放进去吧~

    white ink10年前 (2015-03-22)回复

我的作品

HanLP自然语言处理包《自然语言处理入门》