1. 简介

在机器学习领域,LDA是两个常用模型的简称:Linear Discriminant Analysis 和 Latent Dirichlet Allocation。本文的LDA仅指代Latent Dirichlet Allocation. LDA 在主题模型中占有非常重要的地位,常用来文本分类。

LDA由Blei, David M.、Ng, Andrew Y.、Jordan于2003年提出,用来推测文档的主题分布。它可以将文档集中每篇文档的主题以概率分布的形式给出,从而通过分析一些文档抽取出它们的主题分布后,便可以根据主题分布进行主题聚类或文本分类。

2. 先验知识

LDA 模型涉及很多数学知识,这也许是LDA晦涩难懂的主要原因。

LDA涉及到的先验知识有:二项分布、Gamma函数、Beta分布、多项分布、Dirichlet分布、马尔科夫链、MCMC、Gibs Sampling、EM算法等。

2.1 词袋模型

LDA 采用词袋模型。所谓词袋模型,是将一篇文档,我们仅考虑一个词汇是否出现,而不考虑其出现的顺序。在词袋模型中,“我喜欢你”和“你喜欢我”是等价的。与词袋模型相反的一个模型是n-gram,n-gram考虑了词汇出现的先后顺序。

2.2 二项分布

二项分布是N重伯努利分布,即为X ~ B(n, p). 概率密度公式为:
LDA - 图1

2.3 多项分布

多项分布,是二项分布扩展到多维的情况. 多项分布是指单次试验中的随机变量的取值不再是0-1的,而是有多种离散值可能(1,2,3…,k).概率密度函数为:
LDA - 图2

2.4 Gamma函数

Gamma函数的定义:
LDA - 图3
分部积分后,可以发现Gamma函数如有这样的性质:
LDA - 图4
Gamma函数可以看成是阶乘在实数集上的延拓,具有如下性质:
LDA - 图5

2.5 Beta分布

Beta分布的定义:对于参数 LDA - 图6 取值范围为[0, 1]的随机变量 x 的概率密度函数为:
LDA - 图7 (1)
其中,
LDA - 图8 (2)

2.6 共轭先验分布

在贝叶斯概率理论中,如果后验概率 LDA - 图9 和先验概率 LDA - 图10 满足同样的分布律,那么,先验分布和后验分布被叫做共轭分布,同时,先验分布叫做似然函数的共轭先验分布。
LDA - 图11 (3)
Beta分布是二项式分布的共轭先验分布,而狄利克雷(Dirichlet)分布是多项式分布的共轭分布。

共轭的意思是,以Beta分布和二项式分布为例,数据符合二项分布的时候,参数的先验分布和后验分布都能保持Beta分布的形式,这种形式不变的好处是,我们能够在先验分布中赋予参数很明确的物理意义,这个物理意义可以延续到后续分布中进行解释,同时从先验变换到后验过程中从数据中补充的知识也容易有物理解释。

2.7 Dirichlet分布

Dirichlet的概率密度函数为:
LDA - 图12 (4)
其中,
LDA - 图13 (5)
根据Beta分布、二项分布、Dirichlet分布、多项式分布的公式,我们可以验证上一小节中的结论 — Beta分布是二项式分布的共轭先验分布,而狄利克雷(Dirichlet)分布是多项式分布的共轭分布。

2.8 Beta / Dirichlet 分布的一个性质

如果 LDA - 图14 ,则
LDA - 图15 (6)
LDA - 图16 (7)
LDA - 图17 (8)
上式右边的积分对应到概率分布 LDA - 图18 , 对于这个分布,有
LDA - 图19
把上式带入E(p)的计算式,得到
LDA - 图20 (9)
LDA - 图21 (10)
LDA - 图22 (11)

这说明,对于Beta分布的随机变量,其均值可以用 LDA - 图23 来估计。Dirichlet分布也有类似的结论,如果 LDA - 图24 , 同样可以证明:

LDA - 图25 (12)
这两个结论非常重要,后面的LDA数学推导过程会使用这个结论。

2.9 MCMC 和 Gibbs Sampling

在现实应用中,我们很多时候很难精确求出精确的概率分布,常常采用近似推断方法。近似推断方法大致可分为两大类:第一类是采样(Sampling), 通过使用随机化方法完成近似;第二类是使用确定性近似完成近似推断,典型代表为变分推断(variational inference).

在很多任务中,我们关心某些概率分布并非因为对这些概率分布本身感兴趣,而是要基于他们计算某些期望,并且还可能进一步基于这些期望做出决策。采样法正是基于这个思路。具体来说,假定我们的目标是计算函数f(x)在概率密度函数p(x)下的期望
LDA - 图26 (13)
则可根据p(x)抽取一组样本 LDA - 图27 ,然后计算f(x)在这些样本上的均值
LDA - 图28 (14)
以此来近似目标期望E[f]。若样本 LDA - 图29 独立,基于大数定律,这种通过大量采样的办法就能获得较高的近似精度。

可是,问题的关键是如何采样?对概率图模型来说,就是如何高效地基于图模型所描述的概率分布来获取样本。概率图模型中最常用的采样技术是马尔可夫链蒙特卡罗(Markov chain Monte Carlo, MCMC). 给定连续变量 LDA - 图30 的概率密度函数p(x), x在区间A中的概率可计算为
LDA - 图31 (15)
若有函数 LDA - 图32 , 则可计算f(x)的期望
LDA - 图33 (16)
若x不是单变量而是一个高维多元变量x, 且服从一个非常复杂的分布,则对上式求积分通常很困难。为此,MCMC先构造出服从p分布的独立同分布随机变量 LDA - 图34 , 再得到上式的无偏估计
LDA - 图35 (17)
然而,若概率密度函数p(x)很复杂,则构造服从p分布的独立同分布样本也很困难。MCMC方法的关键在于通过构造“平稳分布为p的马尔可夫链”来产生样本:若马尔科夫链运行时间足够长,即收敛到平稳状态,则此时产出的样本X近似服从分布p.如何判断马尔科夫链到达平稳状态呢?假定平稳马尔科夫链T的状态转移概率(即从状态X转移到状态 LDA - 图36 的概率)为 LDA - 图37 , t时刻状态的分布为LDA - 图38, 则若在某个时刻马尔科夫链满足平稳条件

LDA - 图39 (18)

则p(x)是马尔科夫链的平稳分布,且马尔科夫链在满足该条件时已收敛到平稳条件。

也就是说,MCMC方法先设法构造一条马尔科夫链,使其收敛至平稳分布恰为待估计参数的后验分布,然后通过这条马尔科夫链来产生符合后验分布的样本,并基于这些样本来进行估计。这里马尔科夫链转移概率的构造至关重要,不同的构造方法将产生不同的MCMC算法。

Metropolis-Hastings(简称MH)算法是MCMC的重要代表。它基于“拒绝采样”(reject sampling)来逼近平稳分布p。算法如下:

  • 输入:先验概率 LDA - 图40
  • 过程:
  • 1. 初始化x^0;
  • 2. for t = 1, 2, … do
  • 3. 根据 LDA - 图41 采样出候选样本 LDA - 图42
  • 4. 根据均匀分布从(0, 1)范围内采样出阈值u;
  • 5. if u LDA - 图43
  • 6. LDA - 图44
  • 7. else
  • 8. LDA - 图45
  • 9. end if
  • 10. enf for
  • 11. return LDA - 图46
  • 输出:采样出的一个样本序列
    于是, 为了达到平稳状态,只需将接受率设置为

LDA - 图47 (19)

吉布斯采样(Gibbs sampling)有时被视为MH算法的特例,它也使用马尔科夫链读取样本,而该马尔科夫链的平稳分布也是采用采样的目标分布p(x).具体来说,假定 LDA - 图48 , 目标分布为p(x), 在初始化x的取值后,通过循环执行以下步骤来完成采样:

  • 1. 随机或以某个次序选取某变量LDA - 图49;
  • 2. 根据 x 中除LDA - 图50外的变量的现有取值,计算条件概率LDA - 图51, 其中LDA - 图52;
  • 3. 根**LDA - 图53对变量LDA - 图54采样,用采样值代替原值.**

3. 文本建模

一篇文档,可以看成是一组有序的词的序列 LDA - 图55 . 从统计学角度来看,文档的生成可以看成是上帝抛掷骰子生成的结果,每一次抛掷骰子都生成一个词汇,抛掷N词生成一篇文档。在统计文本建模中,我们希望猜测出上帝是如何玩这个游戏的,这会涉及到两个最核心的问题:

  • 上帝都有什么样的骰子;
  • 上帝是如何抛掷这些骰子的;

第一个问题就是表示模型中都有哪些参数,骰子的每一个面的概率都对应于模型中的参数;
第二个问题就表示游戏规则是什么,上帝可能有各种不同类型的骰子,上帝可以按照一定的规则抛掷这些骰子从而产生词序列。

3.1 Unigram Model

在Unigram Model中,我们采用词袋模型,假设了文档之间相互独立,文档中的词汇之间相互独立。假设我们的词典中一共有 V 个词 LDA - 图56 ,那么最简单的 Unigram Model 就是认为上帝是按照如下的游戏规则产生文本的。

  • 1. 上帝只有一个骰子,这个骰子有V面,每个面对应一个词,各个面的概率不一;
  • 2. 每抛掷一次骰子,抛出的面就对应的产生一个词;如果一篇文档中N个词,就独立的抛掷n次骰子产生n个词;

3.1.1 频率派视角

对于一个骰子,记各个面的概率为 LDA - 图57 , 每生成一个词汇都可以看做一次多项式分布,记为 LDA - 图58 。一篇文档 LDA - 图59 , 其生成概率是 LDA - 图60
文档之间,我们认为是独立的,对于一个语料库,其概率为:LDA - 图61
假设语料中总的词频是N,记每个词 LDA - 图62 的频率为 LDA - 图63 , 那么 LDA - 图64 , 服从多项式分布
LDA - 图65
整个语料库的概率为
LDA - 图66
此时,我们需要估计模型中的参数 LDA - 图67 ,也就是词汇骰子中每个面的概率是多大,按照频率派的观点,使用极大似然估计最大化p(W), 于是参数 LDA - 图68 的估计值为
LDA - 图69

3.1.2 贝叶斯派视角

对于以上模型,贝叶斯统计学派的统计学家会有不同意见,他们会很挑剔的批评只假设上帝拥有唯一一个固定的骰子是不合理的。在贝叶斯学派看来,一切参数都是随机变量,以上模型中的骰子 LDA - 图70 不是唯一固定的,它也是一个随机变量。所以按照贝叶斯学派的观点,上帝是按照以下的过程在玩游戏的:

  • 1. 现有一个装有无穷多个骰子的坛子,里面装有各式各样的骰子,每个骰子有V个面;
  • 2. 现从坛子中抽取一个骰子出来,然后使用这个骰子不断抛掷,直到产生语料库中的所有词汇

坛子中的骰子无限多,有些类型的骰子数量多,有些少。从概率分布角度看,坛子里面的骰子 LDA - 图71 服从一个概率分布 LDA - 图72 , 这个分布称为参数 LDA - 图73 的先验分布。在此视角下,我们并不知道到底用了哪个骰子 LDA - 图74 ,每个骰子都可能被使用,其概率由先验分布 LDA - 图75 来决定。对每个具体的骰子,由该骰子产生语料库的概率为 LDA - 图76 , 故产生语料库的概率就是对每一个骰子 LDA - 图77 上产生语料库进行积分求和
LDA - 图78
先验概率有很多选择,但我们注意到 LDA - 图79 . 我们知道多项式分布和狄利克雷分布是共轭分布,因此一个比较好的选择是采用狄利克雷分布
LDA - 图80
此处 LDA - 图81 ,就是归一化因子 LDA - 图82 , 即
LDA - 图83
由多项式分布和狄利克雷分布是共轭分布,可得:
LDA - 图84 (20)
此时,我们如何估计参数 LDA - 图85 呢?根据上式,我们已经知道了其后验分布,所以合理的方式是使用后验分布的极大值点,或者是参数在后验分布下的平均值。这里,我们取平均值作为参数的估计值。根据第二小节Dirichlet分布中的内容,可以得到:
LDA - 图86 (21)
对于每一个 LDA - 图87 , 我们使用下面的式子进行估计
LDA - 图88 (22)
LDA - 图89 在 Dirichlet 分布中的物理意义是事件的先验的伪计数,上式表达的是:每个参数的估计值是其对应事件的先验的伪计数和数据中的计数的和在整体计数中的比例。由此,我们可以计算出产生语料库的概率为:
LDA - 图90 (23)
LDA - 图91 (24)
LDA - 图92 (25)
LDA - 图93 (26)
LDA - 图94 (27)

3.2 PLSA模型

Unigram Model模型中,没有考虑主题词这个概念。我们人写文章时,写的文章都是关于某一个主题的,不是满天胡乱的写,比如一个财经记者写一篇报道,那么这篇文章大部分都是关于财经主题的,当然,也有很少一部分词汇会涉及到其他主题。所以,PLSA认为生成一篇文档的生成过程如下:

  • 1. 现有两种类型的骰子,一种是doc-topic骰子,每个doc-topic骰子有K个面,每个面一个topic的编号;一种是topic-word骰子,每个topic-word骰子有V个面,每个面对应一个词;
  • 2. 现有K个topic-word骰子,每个骰子有一个编号,编号从1到K;
  • 3. 生成每篇文档之前,先为这篇文章制造一个特定的doc-topic骰子,重复如下过程生成文档中的词:
  • 3.1 投掷这个doc-topic骰子,得到一个topic编号z;
  • 3.2 选择K个topic-word骰子中编号为z的那个,投掷这个骰子,得到一个词;

PLSA中,也是采用词袋模型,文档和文档之间是独立可交换的,同一个文档内的词也是独立可交换的。K 个topic-word 骰子,记为 LDA - 图95 ; 对于包含M篇文档的语料 LDA - 图96 中的每篇文档 LDA - 图97 ,都会有一个特定的doc-topic骰子 LDA - 图98 ,所有对应的骰子记为 LDA - 图99 。为了方便,我们假设每个词 LDA - 图100 都有一个编号,对应到topic-word 骰子的面。于是在 PLSA 这个模型中,第m篇文档 LDA - 图101 中的每个词的生成概率
LDA - 图102 (28)
LDA - 图103 (29)
一篇文档的生成概率为:
LDA - 图104 (30)
LDA - 图105 (31)
由于文档之间相互独立,很容易写出整个语料的生成概率。求解PLSA 可以使用著名的 EM 算法进行求得局部最优解,有兴趣的同学参考 Hoffman 的原始论文,或者李航的《统计学习方法》,此处略去不讲。

3.3 LDA 模型

3.3.1 PLSA 和 LDA 的区别

首先,我们来看看PLSA和LDA生成文档的方式。在PLSA中,生成文档的方式如下:

  • 1. 按照概率LDA - 图106选择一篇文档LDA - 图107
  • 2. 根据选择的文档LDA - 图108,从主题分布中按照概率LDA - 图109选择一个隐含的主题类别LDA - 图110
  • 3. 根据选择的主题LDA - 图111, 从词分布中按照概率LDA - 图112选择一个词LDA - 图113

LDA 中,生成文档的过程如下:

  • 1. 按照先验概率LDA - 图114选择一篇文档LDA - 图115
  • 2. 从Dirichlet分布LDA - 图116中取样生成文档LDA - 图117的主题分布LDA - 图118,主题分布LDA - 图119由超参数为LDA - 图120的Dirichlet分布生成
  • 3. 从主题的多项式分布LDA - 图121中取样生成文档LDA - 图122第 j 个词的主题LDA - 图123
  • 4. 从Dirichlet分布LDA - 图124中取样生成主题LDA - 图125对应的词语分布LDA - 图126,词语分布LDA - 图127由参数为LDA - 图128的Dirichlet分布生成
  • 5. 从词语的多项式分布LDA - 图129中采样最终生成词语LDA - 图130


可以看出,LDA 在 PLSA 的基础上,为主题分布和词分布分别加了两个 Dirichlet 先验。
我们来看一个例子,如图所示:
LDA - 图131
上图中有三个主题,在PLSA中,我们会以固定的概率来抽取一个主题词,比如0.5的概率抽取教育这个主题词,然后根据抽取出来的主题词,找其对应的词分布,再根据词分布,抽取一个词汇。由此,可以看出PLSA中,主题分布和词分布都是唯一确定的。
但是,在LDA中,主题分布和词分布是不确定的,LDA的作者们采用的是贝叶斯派的思想,认为它们应该服从一个分布,主题分布和词分布都是多项式分布,因为多项式分布和狄利克雷分布是共轭结构,在LDA中主题分布和词分布使用了Dirichlet分布作为它们的共轭先验分布。
LDA模型属于概率图模型可以由板块表示法表示LDA模型中,每个主题的单词分布、每个文本的主题分布、文本的每个位置的主题隐变量,文本的每个位置的单词观测变量
所以,也就有了一句广为流传的话 — LDA 就是 PLSA 的贝叶斯化版本。下面两张图片很好的体现了两者的区别:
LDA - 图132LDA - 图133
在PLSA和LDA的两篇论文中,使用了下面的图片来解释模型,它们也很好的对比了PLSA和LDA的不同之处。
LDA - 图134
LDA - 图135

3.3.2 LDA 解析一

现在我们来详细讲解论文中的LDA模型,即上图。
LDA - 图136 , 这个过程表示在生成第m篇文档的时候,先从抽取了一个doc-topic骰子 LDA - 图137 , 然后投掷这个骰子生成了文档中第n个词的topic编号 LDA - 图138 ;
LDA - 图139 , 这个过程表示,从K个topic-word骰子 LDA - 图140 中,挑选编号为 LDA - 图141 的骰子进行投掷,然后生成词汇 LDA - 图142 ;
在LDA中,也是采用词袋模型,M篇文档会对应M个独立Dirichlet-Multinomial共轭结构;K个topic会对应K个独立的Dirichlet-Multinomial共轭结构。

3.3.3 LDA 解析二

上面的LDA的处理过程是一篇文档一篇文档的过程来处理,并不是实际的处理过程。文档中每个词的生成都要抛两次骰子,第一次抛一个doc-topic骰子得到 topic, 第二次抛一个topic-word骰子得到 word,每次生成每篇文档中的一个词的时候这两次抛骰子的动作是紧邻轮换进行的。如果语料中一共有 N 个词,则上帝一共要抛 2N次骰子,轮换的抛doc-topic骰子和 topic-word骰子。但实际上有一些抛骰子的顺序是可以交换的,我们可以等价的调整2N次抛骰子的次序:前N次只抛doc-topic骰子得到语料中所有词的 topics,然后基于得到的每个词的 topic 编号,后N次只抛topic-word骰子生成 N 个word。此时,可以得到:
LDA - 图143 (32)
LDA - 图144 (33)

3.3.4 使用Gibbs Sampling进行采样

LDA模型的学习与推理不能直接求解。通常采用的方法是吉布斯抽样算法和变分EM算法,前者是蒙特卡罗法而后者是近似算法。
根据上一小节中的联合概率分布 LDA - 图145 , 我们可以使用Gibbs Sampling对其进行采样。
语料库 LDA - 图146 中的第i个词我们记为 LDA - 图147 , 其中i=(m,n)是一个二维下标,对应于第m篇文档的第n个词,用 LDA - 图148 表示去除下标为i的词。根据第二小节中的Gibbs Sampling 算法,我们需要求任一个坐标轴 i 对应的条件分布 LDA - 图149 。假设已经观测到的词 LDA - 图150 , 则由贝叶斯法则,我们容易得到:
LDA - 图151 (34)
由于 LDA - 图152 只涉及到第 m 篇文档和第k个 topic,所以上式的条件概率计算中, 实际上也只会涉及到与之相关的两个Dirichlet-Multinomial 共轭结构,其它的 M+K−2 个 Dirichlet-Multinomial 共轭结构和 LDA - 图153 是独立的。去掉一个词汇,并不会改变M + K 个Dirichlet-Multinomial共轭结构,只是某些地方的计数减少而已。于是有:
LDA - 图154 (35)
LDA - 图155 (36)
下面进行本篇文章最终的核心数学公式推导:
LDA - 图156 (37) LDA - 图157 (38)
LDA - 图158 (39)
LDA - 图159
LDA - 图160 (40)
LDA - 图161
LDA - 图162 (41)
LDA - 图163 (42)
LDA - 图164 (43)
LDA - 图165 (44)

最终得到的 LDA - 图166 就是对应的两个 Dirichlet 后验分布在贝叶斯框架下的参数估计。借助于前面介绍的Dirichlet 参数估计的公式 ,有:
LDA - 图167 (45)
LDA - 图168 (46)
最终,我们得到LDA 模型的 Gibbs Sampling 公式为:
LDA - 图169 (47)

3.3.5 LDA Training

根据上一小节中的公式,我们的目标有两个:

  • 1. 估计模型中的参数**LDA - 图170LDA - 图171 ;**
  • 2. 对于新来的一篇文档,我们能够计算这篇文档的 topic 分布LDA - 图172

训练的过程:

  • 1. 对语料库中的每篇文档中的每个词汇LDA - 图173,随机的赋予一个topic编号z
  • 2. 重新扫描语料库,对每个词LDA - 图174,使用Gibbs Sampling公式对其采样,求出它的topic,在语料中更新
  • 3. 重复步骤2,直到Gibbs Sampling收敛
  • 4. 统计语料库的topic-word共现频率矩阵,该矩阵就是LDA的模型;

根据这个topic-word频率矩阵,我们可以计算每一个p(word|topic)概率,从而算出模型参数 LDA - 图175 , 这就是那 K 个 topic-word 骰子。而语料库中的文档对应的骰子参数 LDA - 图176 在以上训练过程中也是可以计算出来的,只要在 Gibbs Sampling 收敛之后,统计每篇文档中的 topic 的频率分布,我们就可以计算每一个 p(topic|doc) 概率,于是就可以计算出每一个 LDA - 图177 。由于参数 LDA - 图178 是和训练语料中的每篇文档相关的,对于我们理解新的文档并无用处,所以工程上最终存储 LDA 模型时候一般没有必要保留。通常,在 LDA 模型训练的过程中,我们是取 Gibbs Sampling 收敛之后的 n 个迭代的结果进行平均来做参数估计,这样模型质量更高。

3.3.6 LDA Inference

有了 LDA 的模型,对于新来的文档 doc, 我们只要认为 Gibbs Sampling 公式中的 LDA - 图179 部分是稳定不变的,是由训练语料得到的模型提供的,所以采样过程中我们只要估计该文档的 topic 分布 LDA - 图180 就好了. 具体算法如下:

  • 1. 对当前文档中的每个单词LDA - 图181, 随机初始化一个topic编号z;
  • 2. 使用Gibbs Sampling公式,对每个词LDA - 图182, 重新采样其topic;
  • 3. 重复以上过程,直到Gibbs Sampling收敛;
  • 4. 统计文档中的topic分布,该分布就是LDA - 图183

3.3.7 使用变分EM算法

变分推理的基本想法如下。
假设模型是联合概率分布LDA - 图184,其中LDA - 图185是观测变量(数据),LDA - 图186是隐变量。目标是学习模型的后验概率分布LDA - 图187。考虑用变分分布LDA - 图188近似条件概率分布LDA - 图189,用KL散度计算两者的相似性找到与LDA - 图190在KL散度意义下最近的LDA - 图191,用这个分布近似LDA - 图192。假设LDA - 图193中的LDA - 图194的所有分量都是互相独立的。利用Jensen不等式,得到KL散度的最小化可以通过证据下界的最大化实现。因此,变分推理变成求解以下证据下界最大化问题:
LDA - 图195

LDA的变分EM算法如下。**
针对LDA模型定义变分分布,应用变分EM算法。目标是对证据下界LDA - 图196进行最大化,其中LDA - 图197LDA - 图198是模型参数,LDA - 图199LDA - 图200是变分参数。交替迭代E步和M步,直到收敛。

  • (1)E步:固定模型参数LDA - 图201LDA - 图202,通过关于变分参数LDA - 图203LDA - 图204的证据下界的最大化,估计变分参数LDA - 图205LDA - 图206
  • (2)M步:固定变分参数LDA - 图207LDA - 图208,通过关于模型参数LDA - 图209LDA - 图210的证据下界的最大化,估计模型参数LDA - 图211LDA - 图212

3.3.8 代码

  1. from gensim import corpora, models, similarities
  2. from pprint import pprint
  3. import warnings
  4. f = open('data/LDA_test.txt')
  5. stop_list = set('for a of the and to in'.split())
  6. # texts = [line.strip().split() for line in f]
  7. # print 'Before'
  8. # pprint(texts)
  9. print('After')
  10. After
  1. texts = [[
  2. word for word in line.strip().lower().split() if word not in stop_list
  3. ] for line in f]
  4. print('Text = ')
  5. pprint(texts)
  6. Text =
  7. [['human', 'machine', 'interface', 'lab', 'abc', 'computer', 'applications'],
  8. ['survey', 'user', 'opinion', 'computer', 'system', 'response', 'time'],
  9. ['eps', 'user', 'interface', 'management', 'system'],
  10. ['system', 'human', 'system', 'engineering', 'testing', 'eps'],
  11. ['relation', 'user', 'perceived', 'response', 'time', 'error', 'measurement'],
  12. ['generation', 'random', 'binary', 'unordered', 'trees'],
  13. ['intersection', 'graph', 'paths', 'trees'],
  14. ['graph', 'minors', 'iv', 'widths', 'trees', 'well', 'quasi', 'ordering'],
  15. ['graph', 'minors', 'survey']]
  1. dictionary = corpora.Dictionary(texts)
  2. print(dictionary)
  3. Dictionary(35 unique tokens: ['abc', 'applications', 'computer', 'human', 'interface']...)
  1. V = len(dictionary)
  2. corpus = [dictionary.doc2bow(text) for text in texts]
  3. corpus_tfidf = models.TfidfModel(corpus)[corpus]
  4. corpus_tfidf = corpus
  5. print('TF-IDF:')
  6. for c in corpus_tfidf:
  7. print(c)
  8. TF-IDF:
  9. [(0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1)]
  10. [(2, 1), (7, 1), (8, 1), (9, 1), (10, 1), (11, 1), (12, 1)]
  11. [(4, 1), (10, 1), (12, 1), (13, 1), (14, 1)]
  12. [(3, 1), (10, 2), (13, 1), (15, 1), (16, 1)]
  13. [(8, 1), (11, 1), (12, 1), (17, 1), (18, 1), (19, 1), (20, 1)]
  14. [(21, 1), (22, 1), (23, 1), (24, 1), (25, 1)]
  15. [(24, 1), (26, 1), (27, 1), (28, 1)]
  16. [(24, 1), (26, 1), (29, 1), (30, 1), (31, 1), (32, 1), (33, 1), (34, 1)]
  17. [(9, 1), (26, 1), (30, 1)]
  1. print('\nLSI Model:')
  2. lsi = models.LsiModel(corpus_tfidf, num_topics=2, id2word=dictionary)
  3. topic_result = [a for a in lsi[corpus_tfidf]]
  4. pprint(topic_result)
  5. LSI Model:
  6. [[(0, 0.9334981916792652), (1, 0.10508952614086528)],
  7. [(0, 2.031992374687025), (1, -0.047145314121742235)],
  8. [(0, 1.5351342836582078), (1, 0.13488784052204628)],
  9. [(0, 1.9540077194594532), (1, 0.21780498576075008)],
  10. [(0, 1.2902472956004092), (1, -0.0022521437499372337)],
  11. [(0, 0.022783081905505403), (1, -0.7778052604326754)],
  12. [(0, 0.05671567576920905), (1, -1.1827703446704851)],
  13. [(0, 0.12360003320647955), (1, -2.6343068608236835)],
  14. [(0, 0.23560627195889133), (1, -0.9407936203668315)]]
  1. print('LSI Topics:')
  2. pprint(lsi.print_topics(num_topics=2, num_words=5))
  3. LSI Topics:
  4. [(0,
  5. '0.579*"system" + 0.376*"user" + 0.270*"eps" + 0.257*"time" + '
  6. '0.257*"response"'),
  7. (1,
  8. '-0.480*"graph" + -0.464*"trees" + -0.361*"minors" + -0.266*"widths" + '
  9. '-0.266*"ordering"')]
  1. similarity = similarities.MatrixSimilarity(lsi[corpus_tfidf]) # similarities.Similarity()
  2. print('Similarity:')
  3. pprint(list(similarity))
  4. Similarity:
  5. [array([ 1. , 0.9908607 , 0.9997008 , 0.9999994 , 0.9935261 ,
  6. -0.08272626, -0.06414512, -0.06517283, 0.13288835], dtype=float32),
  7. array([0.9908607 , 0.99999994, 0.9938636 , 0.99100804, 0.99976987,
  8. 0.0524564 , 0.07105229, 0.070025 , 0.2653665 ], dtype=float32),
  9. array([ 0.9997008 , 0.9938636 , 0.99999994, 0.999727 , 0.99600756,
  10. -0.05832579, -0.03971674, -0.04074576, 0.15709123], dtype=float32),
  11. array([ 0.9999994 , 0.99100804, 0.999727 , 1. , 0.9936501 ,
  12. -0.08163348, -0.06305084, -0.06407862, 0.13397504], dtype=float32),
  13. array([0.9935261 , 0.99976987, 0.99600756, 0.9936501 , 0.99999994,
  14. 0.03102366, 0.04963995, 0.04861134, 0.24462426], dtype=float32),
  15. array([-0.08272626, 0.0524564 , -0.05832579, -0.08163348, 0.03102366,
  16. 0.99999994, 0.99982643, 0.9998451 , 0.97674036], dtype=float32),
  17. array([-0.06414512, 0.07105229, -0.03971674, -0.06305084, 0.04963995,
  18. 0.99982643, 1. , 0.9999995 , 0.9805657 ], dtype=float32),
  19. array([-0.06517283, 0.070025 , -0.04074576, -0.06407862, 0.04861134,
  20. 0.9998451 , 0.9999995 , 1. , 0.9803632 ], dtype=float32),
  21. array([0.13288835, 0.2653665 , 0.15709123, 0.13397504, 0.24462426,
  22. 0.97674036, 0.9805657 , 0.9803632 , 1. ], dtype=float32)]
  1. print('\nLDA Model:')
  2. num_topics = 2
  3. lda = models.LdaModel(
  4. corpus_tfidf,
  5. num_topics=num_topics,
  6. id2word=dictionary,
  7. alpha='auto',
  8. eta='auto',
  9. minimum_probability=0.001,
  10. passes=10)
  11. doc_topic = [doc_t for doc_t in lda[corpus_tfidf]]
  12. print('Document-Topic:\n')
  13. pprint(doc_topic)
  14. LDA Model:
  15. Document-Topic:
  16. [[(0, 0.02668742), (1, 0.97331256)],
  17. [(0, 0.9784582), (1, 0.021541778)],
  18. [(0, 0.9704323), (1, 0.02956772)],
  19. [(0, 0.97509205), (1, 0.024907947)],
  20. [(0, 0.9785106), (1, 0.021489413)],
  21. [(0, 0.9703556), (1, 0.029644381)],
  22. [(0, 0.04481229), (1, 0.9551877)],
  23. [(0, 0.023327617), (1, 0.97667235)],
  24. [(0, 0.058409944), (1, 0.9415901)]]
  1. for doc_topic in lda.get_document_topics(corpus_tfidf):
  2. print(doc_topic)
  3. [(0, 0.026687337), (1, 0.9733126)]
  4. [(0, 0.9784589), (1, 0.021541081)]
  5. [(0, 0.97043234), (1, 0.029567692)]
  6. [(0, 0.9750935), (1, 0.024906479)]
  7. [(0, 0.9785101), (1, 0.021489937)]
  8. [(0, 0.9703557), (1, 0.029644353)]
  9. [(0, 0.044812497), (1, 0.9551875)]
  10. [(0, 0.02332762), (1, 0.97667235)]
  11. [(0, 0.058404233), (1, 0.9415958)]
  1. for topic_id in range(num_topics):
  2. print('Topic', topic_id)
  3. # pprint(lda.get_topic_terms(topicid=topic_id))
  4. pprint(lda.show_topic(topic_id))
  5. similarity = similarities.MatrixSimilarity(lda[corpus_tfidf])
  6. print('Similarity:')
  7. pprint(list(similarity))
  8. hda = models.HdpModel(corpus_tfidf, id2word=dictionary)
  9. topic_result = [a for a in hda[corpus_tfidf]]
  10. print('\n\nUSE WITH CARE--\nHDA Model:')
  11. pprint(topic_result)
  12. print('HDA Topics:')
  13. print(hda.print_topics(num_topics=2, num_words=5))
  14. Topic 0
  15. [('system', 0.094599016),
  16. ('user', 0.073440075),
  17. ('eps', 0.052545987),
  18. ('response', 0.052496374),
  19. ('time', 0.052453455),
  20. ('survey', 0.031701956),
  21. ('trees', 0.03162545),
  22. ('human', 0.03161709),
  23. ('computer', 0.031570844),
  24. ('testing', 0.031543963)]
  25. Topic 1
  26. [('graph', 0.0883405),
  27. ('trees', 0.06323685),
  28. ('minors', 0.06296622),
  29. ('interface', 0.03810195),
  30. ('computer', 0.03798469),
  31. ('human', 0.03792907),
  32. ('applications', 0.03792245),
  33. ('abc', 0.037920628),
  34. ('machine', 0.037917122),
  35. ('lab', 0.037909806)]
  36. Similarity:
  37. [array([1. , 0.04940351, 0.05783966, 0.05292428, 0.04934979,
  38. 0.05791992, 0.99981046, 0.99999374, 0.99940336], dtype=float32),
  39. array([0.04940351, 1. , 0.99996436, 0.9999938 , 1. ,
  40. 0.99996364, 0.06883725, 0.04587576, 0.08387101], dtype=float32),
  41. array([0.05783966, 0.99996436, 1.0000001 , 0.99998796, 0.99996394,
  42. 1. , 0.07726298, 0.05431345, 0.09228647], dtype=float32),
  43. array([0.05292428, 0.9999938 , 0.99998796, 1. , 0.9999936 ,
  44. 0.9999875 , 0.07235384, 0.04939714, 0.08738345], dtype=float32),
  45. array([0.04934979, 1. , 0.99996394, 0.9999936 , 1. ,
  46. 0.99996316, 0.06878359, 0.04582203, 0.08381741], dtype=float32),
  47. array([0.05791992, 0.99996364, 1. , 0.9999875 , 0.99996316,
  48. 0.99999994, 0.07734313, 0.05439373, 0.09236652], dtype=float32),
  49. array([0.99981046, 0.06883725, 0.07726298, 0.07235384, 0.06878359,
  50. 0.07734313, 0.99999994, 0.9997355 , 0.9998863 ], dtype=float32),
  51. array([0.99999374, 0.04587576, 0.05431345, 0.04939714, 0.04582203,
  52. 0.05439373, 0.9997355 , 0.99999994, 0.9992751 ], dtype=float32),
  53. array([0.99940336, 0.08387101, 0.09228647, 0.08738345, 0.08381741,
  54. 0.09236652, 0.9998863 , 0.9992751 , 1. ], dtype=float32)]
  55. USE WITH CARE--
  56. HDA Model:
  57. [[(0, 0.18174982193320122),
  58. (1, 0.02455260642448283),
  59. (2, 0.741340573910992),
  60. (3, 0.013544078061059922),
  61. (4, 0.010094377639823477)],
  62. [(0, 0.39419292675663636),
  63. (1, 0.2921969355337328),
  64. (2, 0.26125786014858376),
  65. (3, 0.013539627392486701),
  66. (4, 0.01009410883245766)],
  67. [(0, 0.5182077872999125),
  68. (1, 0.3880947736463974),
  69. (2, 0.023895609845034207),
  70. (3, 0.01805202212531745),
  71. (4, 0.013458421673222807)],
  72. [(0, 0.03621384798236036),
  73. (1, 0.5504573172680752),
  74. (2, 0.020442846194997377),
  75. (3, 0.348529241707211),
  76. (4, 0.011535562414627153)],
  77. [(0, 0.9049762450848856),
  78. (1, 0.024748801100993395),
  79. (2, 0.017919024335434904),
  80. (3, 0.013543460312481508),
  81. (4, 0.010093932388992328)],
  82. [(0, 0.04681359723231631),
  83. (1, 0.03233799461088905),
  84. (2, 0.8510430252219996),
  85. (3, 0.01805587061936895),
  86. (4, 0.013458128836093802)],
  87. [(0, 0.42478083784052273),
  88. (1, 0.03858547281122597),
  89. (2, 0.4528531768644199),
  90. (3, 0.021680841796584305),
  91. (4, 0.016150009359845837),
  92. (5, 0.011953757612369628)],
  93. [(0, 0.2466808290730598),
  94. (1, 0.6908552821243853),
  95. (2, 0.015924569811569197),
  96. (3, 0.012039668311419834)],
  97. [(0, 0.500366457263008),
  98. (1, 0.048221177670061226),
  99. (2, 0.34671234963274666),
  100. (3, 0.02707530995137571),
  101. (4, 0.02018763747377598),
  102. (5, 0.014942188361070167),
  103. (6, 0.010992923111633942)]]
  104. HDA Topics:
  105. [(0, '0.122*graph + 0.115*minors + 0.098*management + 0.075*random + 0.063*error'),
  106. (1, '0.114*human + 0.106*system + 0.086*user + 0.064*iv + 0.063*measurement')]

**