主题模型评价指标一览
前言
在训练好LDA模型后,一个很自然的举动就是尝试去衡量模型的好坏。在介绍这些评价方法之前,有些东西是需要意识到的
-
使用gensim包进行LDA建模时对于相同语料集会每次建模会产生不同的模型,这将导致影响模型的评价指标。可以通过
numpy.dandom.seed(1)
来固定随机种子似的每次训练都能得到相同的模型。
一、主题距离(Topic distance)
引入与思考
在我们训练一个LDA模型时可能会考虑如下问题
- 因为训练LDA模型是一个十分漫长的过程,如果我们能知道模型何时收敛,那么就可以及时停止训练,节省训练时间。因此一个很自然的想法就是每隔特定的时间或者轮数或者文档数就输出一次模型,然后进行模型之间的比较。具体模型之间的比较方法请往下看。
在我们得到一个LDA模型后通常会思考两件事:
-
看看这个模型和其他模型相比的好坏。
我们都知道LDA可以看做两个矩阵,分别是文档和主题的矩阵以及主题和单词的矩阵。gensim的dictionary可以通过训练语料集得到corpus,所以其实每个模型可以用 θ ( T o p i c ∗ D i c t i o n a r y ) \theta(Topic*Dictionary) θ(Topic∗Dictionary)的矩阵来表示。
横向研究一个模型的好坏,很重要的一点是计算它与其他模型之间的相似性和差异性。
对于一个模型中的一个主题,使用最能体现该主题的 t o p n top_n topn个词进行标注表示(annotation),再通过距离计算公式计算矩阵 [ t o p i c i ] [ t o p i c j ] [topic_i][topic_j] [topici][topicj]的大小来刻画主题i和主题j之间的差异性和相似性。
-
看看自己聚出来的模型中每个主题之间有哪些不同。
如下图,对于单个模型评价可视化指标的最终的效果是这样的:
- 我使用LDA把语料集聚成了10类,所以横纵坐标长度为10,格子的颜色代表纵坐标主题和横坐标主题的相关程度,非对角线上的格子越蓝越好,z值越趋近于1越好,因为这说明两个主题之间的区分度比较大,交集较少。
- +++的意思是两个主题之间共享的主题词
- .- - -的意思是“ words from the symmetric difference of topics”,也就是最能体现两个主题差异的单词
- 对于两个模型进行比较,方法也很简单,只需要把横轴或者纵轴换成另一个模型的主题表示即可。
函数与原理
本文重点分析一下该方法实现的原理是是什么。先看下实现该指标可视化的函数:
def plot_difference_plotly(mdiff, title="", annotation=None):
"""Plot the difference between models.
Uses plotly as the backend."""
import plotly.graph_objs as go
import plotly.offline as py
annotation_html = None
if annotation is not None:
annotation_html = [
[
"+++ {}<br>--- {}".format(", ".join(int_tokens), ", ".join(diff_tokens))
for (int_tokens, diff_tokens) in row
]
for row in annotation
]
data = go.Heatmap(z=mdiff, colorscale='RdBu', text=annotation_html)
layout = go.Layout(width=950, height=950, title=title, xaxis=dict(title="topic"), yaxis=dict(title="topic"))
py.iplot(dict(data=[data], layout=layout))
可以看到该函数有三个输入参数,mdiff,title,annotation。其中title就是可视化图片的标题,mdiff是不同主题之间的距离矩阵,annotation则记录了不同主题之间的共享主题词和差异主题词(就是上面提到的+++和- - -),可以通过
mdiff, annotation = lda_model.diff(lda_model, distance='jaccard', num_words=50)
得到。其中lda_model就是你用gensim包训练的lda模型, distance='jaccard’说明采用了jaccard距离进行计算, 除了jaccard,还有‘kullback—leibler’,‘hellinger’以及‘jensen-shannon‘共四种距离计算方法可以选择。num_words=50说明使用了相关性最高的50个单词代表主题进行计算。
Jaccard distance
Jaccard系数常用来衡量两个样本之间的相似性,公式为:
J
(
A
,
B
)
=
∣
A
⋂
B
∣
∣
A
⋃
B
∣
J(A,B)=\frac{|A\bigcap B|}{|A\bigcup B|}
J(A,B)=∣A⋃B∣∣A⋂B∣
通过维基百科上的图片可以很好理解这一概念,其实就是两个数据集重合的部分所占的比例。
至于Jaccard distance,有两种不同的表示,其一是 J d ( A , B ) = 1 − J ( A , B ) J_d(A,B)=1-J(A,B) Jd(A,B)=1−J(A,B),其二是等于非交集(symmetric difference),即 J d ( A , B ) = ∣ A ⋃ B ∣ − ∣ A ⋂ B ∣ J_d(A,B)=|A\bigcup B|-|A\bigcap B| Jd(A,B)=∣A⋃B∣−∣A⋂B∣
Kullback-Leibler divergence
KL散度用来衡量两个分布的差异性。就是说对于
t
o
p
i
c
i
topic_i
topici来说,
t
o
p
i
c
j
topic_j
topicj有多大程度的不同。值得注意的是,KL散度是不对称的。
D
K
L
(
P
∥
Q
)
=
∑
x
∈
X
P
(
x
)
ln
(
P
(
x
)
Q
(
x
)
)
D_{\mathrm{KL}}(P \| Q)=\sum_{x \in \mathcal{X}} P(x) \ln \left(\frac{P(x)}{Q(x)}\right)
DKL(P∥Q)=x∈X∑P(x)ln(Q(x)P(x))
Aurélien Géron的这个视频对KL散度的物理意义解释的非常清晰
Hellinger distance
同样是用来衡量两个分布差异性或相似性的方法,属于f-divergence的一种。对于离散的两个分布,有如下定义:
H
(
P
,
Q
)
=
1
2
∥
P
−
Q
∥
2
H(P, Q)=\frac{1}{\sqrt{2}}\|\sqrt{P}-\sqrt{Q}\|_{2}
H(P,Q)=21∥P−Q∥2
系数的目的是为了让值在区间[0,1]之间
注意事项
- 此函数仅适用于gensimLDA模型,gensim的HDP和malletLDA并不可用,如果使用会报错如:
AttributeError: 'LdaMallet' object has no attribute 'diff'
- Jaccard 的鲁棒性较好,但是敏感度较低,(就是训练得到的效果看起来会好一点);hellinger的敏感度要高一些。
个人经验
此指标需要与其他指标综合进行考虑。仅仅看模型在该指标下可视化效果还不错,并不能说明你的模型很好。我在实际训练过程中发现不管我的预处理做的有多烂(正常范围内的烂),coherence score有多低(只有0.3多一点),pyvislda看到的效果有多差(很大面积的重叠),这项指标总会以一个“相对很让我满意”的结果安慰我一下。
所以切记不可太过依赖该指标。你可以考虑在其他指标都还可以的情况下,看看这项指标,了解一下主题间的一些差异性,而不应该通过该指标来进行预处理工作中和LDA中的参数修改。
二、pyldavis
简介
这是一个常用的LDA结果可视化工具包,使用pip install pyldavis进行导入。
调用函数:
pyLDAvis.enable_notebook()
#mLDA时需要添加
#mallet_model = gensim.models.wrappers.ldamallet.malletmodel2ldamodel(ldamallet)
vis = pyLDAvis.gensim.prepare(model, corpus, dictionary)
pyLDAvis.save_html(vis,'model.html')
输入参数有模型,语料集以及字典。
可视化效果如下所示:
左边的气泡代表聚出来的主题,气泡的大小体现该主题的重要程度。左边则是对整个语料集和每个主题内关键词进行打分排序。
注意事项
两个坐标轴是通过PCoA(principal coordinate analysis,主坐标分析)得到的,但也在某种程度上是无意义的。它其实只是把每个主题给在坐标轴上表示出来了,但是仅此而已,因为语义空间是高维的。
- 你很难直观的比较每个主题之间的重要程度,仅仅通过比较每个气泡的大小
- 你也很难比较每个主题之间的相关性关系,因为气泡之间的位置关系和他们的相关性没多大关系。
三、困惑度(perplexity)
perplexity是一个很垃圾的评价指标,别用它。因为它并没有进行任何语义上的分析。让我们仅仅看下perplexity是如何评价模型的:
H
(
D
′
)
=
∑
D
′
P
(
w
d
)
log
2
P
(
w
d
)
P
P
=
2
H
(
D
′
)
\begin{aligned} H\left(D^{\prime}\right) &=\sum_{D^{\prime}} P\left(w_{d}\right) \log _{2} P\left(w_{d}\right) \\ P P &=2^{H\left(D^{\prime}\right)} \end{aligned}
H(D′)PP=D′∑P(wd)log2P(wd)=2H(D′)
这只是个对于保留集(hold out data)的极大似然估计罢了。
D
′
D^{\prime}
D′保留集数据,我们假设你有个很捞的模型,不管输入什么最后的分类预测结果都是cat,这时恰好你的预留集里面只有cat,结果只能是自欺欺人罢了。
所以我们说,perplexity并没有告诉你语料集的语义信息。
四、语义一致性(Coherence Score)
概念及流程
现在考虑一种能捕捉到模型中语义信息的评价方法。
假设我们没有模型,我们想看下面这两句话的语义一致性
那么我们可能会首先提取出这些关键词来表示这两句话的语义
然后这两句话实际上都是描写曲棍球(ice hockey)的,那么再去掉那些低信息的词后,我们就得到了如下表示曲棍球的关键词词袋。
现在的问题是如果再给我们一个新的语料集,我们怎么分析它们和这些表示曲棍球词袋的语义一致性。
具体处理步骤如上图流水线所示,
-
t是你模型训练得到的主题集,如下图所示
-
S是你模型得到的每个主题中的前n个关键词的组合。必须是两个两个一组,可以是两个单词一组,两个(两个单词)一组,或者两个(三个单词)一组。
-
然后引入外部语料集计算probability:
-
计算语义确信度衡量(confirmation messure)之 C U M a s s C_{UMass} CUMass: C U M a s s = 2 N ∗ ( N − 1 ) ∑ i = 2 N ∑ j = 1 i − 1 log P ( w i , w j ) P ( w j ) C_{U M a s s}=\frac{2}{N *(N-1)} \sum_{i=2}^{N} \sum_{j=1}^{i-1} \log \frac{P\left(w_{i}, w_{j}\right)}{P\left(w_{j}\right)} CUMass=N∗(N−1)2i=2∑Nj=1∑i−1logP(wj)P(wi,wj)
可以看到这其实是单词间同时出现的条件似然,同时包括了两个单词的信息。所以说这是能体现语境(context)的,不像perplexity。
最后一步的Aggregation其实就是求个均值。
注意事项
- 除了
C
U
M
a
s
s
C_{UMass}
CUMass之外,还有很多计算coherence score的指标,比如
C
V
C_{V}
CV,
C
U
C
I
C_{UCI}
CUCI等等,但是有几个需要注意的问题:
- 在流水线中仅在最后计算了均值,但是却没有关注方差。所以能难去确认你算的均值能否代表整个语料集的相似性。
- 流水线中的数据处理没有做归一化
- 需要格外注意测试集的选取,有时结果不好可能并不是你的模型不好,而是你的测试集和训练集不匹配。
- 有时你模型聚出来100类,发现效果没有35类的分数高,可能是因为那100类中有55类是没用的主题,拉低了分数。但是!那100类中还有45类有用的主题,却没有被该评价指标反映出来。
五、主观参考评价(Human Judgement Based Methods)
Word Intrusion
当我们用模型训练得到一个主题时,主观判断这个模型好坏的一个方法是先得到这个主题的关键词,然后人为的插入一个不属于该主题的词,再让别人把这个插入词给找出来。如果有很多人一下就能找出来这个插入词,比如下面这个集合中apple就是插入词,那么就说明这个模型聚出来的主题效果还不错。
{dog, cat, horse, apple, pig, cow}
反之,如果别人找不出来所谓的插入词,则说明你聚出来的主题效果可能不大行,比如下面这个集合。
{cat, airport, yarn, horse, security, tomorrow}
Topic Intrusion
对于一篇文档我们也可以训练出一些主题的概率分布,对于那些概率分布极低的主题,我们可以相信这篇文档大概说的主要不是那些内容。
如果同时给出几个主题,你认为的那些和文档讨论内容相关度高的主题恰好是模型聚出来概率分布高的主题,而那些和文档讨论内容相关度低的主题恰好是模型聚出来概率分布低的主题;则说明模型的效果不错。
总结:主题模型很难评价!
- 定性分析费时费力且主观因素强
- 定量分析通常是一些被封装好的算法(intrinsic methods),有时很难去理解结果。
最后,模型的好坏终究还是取决于你自己。
参考
papers
- Chang et. al Reading Tea Leaves: How Humans Interpret Topic Models, NIPS 2009
- Wallach et. al Evaluation Methods for Topic Models, ICML 2009
- Lau et. al Machine Reading Tea Leaves: Automatically Evaluating Topic Coherence and Topic Model Quality, ACL 2014
- Röder et. al Exploring the Space of Topic Coherence Methods, Web Search and Data Mining 2015
- Sievert et. al LDAvis: A method for visualizing and interpreting topics ACL 2014 Workshop on Interactive Language Learning, Visualization, and Interfaces
- Chuang et. al Termite: Visualization Techniques for Assessing Textual Topic Models, AVI 2012
- Chuang et. al Topic Model Diagnostics: Assessing Domain Relevance via Topical Alignment, ICML 2013
others
- https://github.com/RaRe-Technologies/gensim/pull/1243
- https://www.youtube.com/watch?v=UkmIljRIG_M&list=RDCMUCOjD18EJYcsBog4IozkF_7w&start_radio=1&t=0
- https://radimrehurek.com/gensim/models/ldamodel.html
- https://en.wikipedia.org/wiki/Jaccard_index
- https://en.wikipedia.org/wiki/Hellinger_distance
- https://rare-technologies.com/what-is-topic-coherence/
- https://mattilyra.github.io/2017/07/30/evaluating-topic-models.html