LDA 指南

LDA 作为文本分析中非常有用的算法,无论是在学术界还是工业界都已被广泛运用。本文是写给自己的 LDA 理解指南。


更新历史

  • 2017.05.03: 完成初稿

关于 LDA 算法,网上的资料不胜枚举,除了 wiki,我最推荐 《 LDA 漫游指南》。那么为什么我还要自己写一篇呢?一是加深理解,二是给出我在工作中应用 LDA 的一些思考。

基础知识

我们首先需要知道的是,LDA 是一种无监督机器学习技术,用来识别大规模文档集或语料库中潜在隐藏的主题信息。具体是怎么识别主题的呢?LDA 中最小单位是词,并假定每个词是由一个潜在隐藏的主题中抽取出来的。

当然,本文不会涉及具体的推导(用 mathjax 敲这些公式太痛苦了)。我们用上帝视角来还原整个过程:

  1. 拿到一篇文章,文章是由『词』组成的
  2. 因为开了上帝视角,我们能够一眼看出每个『词』所属的『主题』
  3. 又因为开了上帝视角,我们在知道了每个『词』所属的『主题』之后,就可以判断出这篇文章所对应的『主题』了。

那么 LDA 在这里做了什么?其实就是通过概率和计算,帮我们开了这么个上帝视角。如果对具体的推导感兴趣,可以参阅最后的链接。

应用场景

因为每个文章会被『划归』到某个『主题』中,所以我们可以:

  • 相似文档发现,具体到产品中可以是『相关新闻』『类似文章』
  • 自动打标签,具体步骤为:分词 - 词性标注 - 去掉副词、介词 - LDA 训练 - 获取主题相关词,这些相关词即为标签
  • 新闻个性化推荐,具体步骤为:用 LDA 得到文章 Topic - 用 LR 进行点击预测 - 推荐同 Topic 最可能点击(且未点击)的新闻。另一种可能的方式是(不需要 LR):记录用户的点击偏好(比如最常看哪类 topic) - 推荐这个 topic 中 topK 的文章(这个方法更加简答)

主题打分

因为 LDA 生成的主题是没有排序的,所以我们可以把显著的、有特色的主题排到前面,而将不明意义的主题往后面排。

注:这个在客服行业中非常有用,因为有时候会需要从大量的客服日志中提取出通用的 FAQ 问题并建立知识库,如果有这么一个排序,就可以减少人工评判的工作量。

算法的核心思路是当前 topic 的概率分本与垃圾分布的距离。如果你跟我一样用 gensim 这个 python 包的话,那么下面这几个函数需要重点留意:

get_document_topics(bow, minimum_probability=None, minimum_phi_value=None, per_word_topics=False)
Return topic distribution for the given document bow, as a list of (topic_id, topic_probability) 2-tuples.
Ignore topics with very low probability (below minimum_probability).
If per_word_topics is True, it also returns a list of topics, sorted in descending order of most likely topics for that word. It also returns a list of word_ids and each words corresponding topics’ phi_values, multiplied by feature length (i.e, word count)
get_term_topics(word_id, minimum_probability=None)
Returns most likely topics for a particular word in vocab.
get_topic_terms(topicid, topn=10)
Return a list of (word_id, probability) 2-tuples for the most probable words in topic topicid.
Only return 2-tuples for the topn most probable words (ignore the rest).

这里我们需要计算两个距离:

  1. topic -> doc 距离。主要看文章分布是不是过于平均
  2. topic -> word 距离。主要看关键词排序是不是过于平均

topic -> doc 距离

我们先由前面的 get_document_topics 获取到每个 document 对应 topic,然后转换成每个 topic 对应的 document。因为每个 document 对应到 topic 是有一个概率的,我们需要归一化一下,这里举个例子就清楚了。

# 假设 topic A 中只有四篇文章
doc1 - 属于 topic A 的概率是 0.9
doc2 - 属于 topic A 的概率是 0.8
doc3 - 属于 topic A 的概率是 0.7
doc4 - 属于 topic A 的概率是 0.6
# 那么 topic A -> doc 的概率分布向量经过归一化后是
# 这里的 3 = 0.9 + 0.8 + 0.7 + 0.6,保证这个向量每个元素相加最后等于 1 即可
[0.9/3, 0.8/3, 0.7/3, 0.6/3]
# 我们需要跟下面这个背景噪音向量进行比较
# 这里 4 是文章的数目
[1/4, 1/4, 1/4, 1/4]

然后我们只要计算这两个向量之间的距离即可,各类距离的计算方法可以看这里,KL divergence, Cosine, 皮尔逊相关度之类的都可以,距离越大,主题越有特色。

topic -> word 距离

我们先由 get_topic_terms 获取 topic -> word 的概率分布,然后我们计算出词向量,具体我们还是看例子

# 假设 Topic A 有 4 个关键词(可以自己设置,这里只是一个例子)
word1 0.30
word2 0.25
word3 0.25
word4 0.20
# 转化成向量比之前的要简单
[0.3, 0.25, 0.25, 0.2]
# 我们需要跟下面这个背景噪音向量进行比较
# 这里 4 是该 topic 下词的数目
[1/4, 1/4, 1/4, 1/4]

然后我们只要计算这两个向量之间的距离即可,各类距离的计算方法可以看这里,KL divergence, Cosine, 皮尔逊相关度之类的都可以,距离越大,主题越有特色。(和前面一样的只是复制粘贴一下)。

最终得分是一个加权的排序,我们的公式是

$$Final\;Score=a\times Topic2DocScore + b\times Topic2WordScore$$

这里的 a 和 b 可以根据需要自己调整。

写在最后

抛开原理不谈,其实 LDA 的具体应用更多是在给出一个排序的依据,无论是相似还是推荐,本质都是排序(只是排序的标准不同),而在这个排序的过程中所给出的隐含 topic,如果可以加以利用,就能得到意想不到的效果。

参考链接

捧个钱场?