前言
最近在看话题模型相关的论文。有关话题模型现在比较主流的解决方法有LDA,PLSA以及mixture of unigrams,本人研究了LDA(Latent Dirichlet Allocation),BTM等话题模型。首先说明在研究和实验LDA话题模型时发现,在解决short text话题分析时,这是由于其基于文档词共现的实现原理分析时无法解决short text的离散问题。之后通过查阅相关文献,找到了一篇有关BTM 话题模型的文章,于是就想象着找个第三方python包看一下算法的效果,但是没有找到,于是乎就自己动手造了个轮子,以下具体介绍造轮子的步骤。
什么是BTM?
简单讲就是一个话题分析的聚类模型,其原理和LDA很相似,如果对LDA不实很了解的可以查阅相关文献或技术博客,下面附上个人学习是的博客和论文仅供大家参考。
简书:LDA话题模型与推荐系统:·http://www.jianshu.com/p/50295398d802
有关BTM的论文参考文献有
A Biterm Topic Model for Short Texts
Xiaohui Yan, Jiafeng Guo, Yanyan Lan, Xueqi Cheng
Institute of Computing Technology, CAS
Beijing, China 100190 yanxiaohui@software.ict.ac.cn, {guojiafeng, lanyanyan, cxq}@ict.ac.cn
BTM 于LDA的不同在于,LDA是document-level,
BTM是corpus-level,LDA 计算的是topic-document-word
的概率分布,BTM计算的是topic-Biterm的概率分布。
相同点在于,都是基于词共现,只是BTM基于word pair pattern。
BTM 模型能够离散词共现(原文:sparse word co-occurrence patterns at document-level,本人英语水平有限,如有理解不当,还请指正)的问题。
BTM 的算法流程:
- For each topic z(a) draw a topic-specific word distribution φz ∼ Dir(β)
- Draw a topic distribution θ ∼ Dir(α) for the whole collection
- For each biterm b in the biterm set B
(a) draw a topic assignment z ∼ Multi(θ)
(b) draw two words: wi,wj ∼ Mulit(φz)
BTM实现
针对实现主要介绍核心部分的实现,主要涉及的问题主要有:
计算P(z) 、P(b|z)迭代计算的更新
代码块
def assign_biterm_topic(self, bit, topic_id):
"""
为 biterm 赋予 topic ,并更新 相关nwz 及 nb_z 数据
:param bit:
:param topic_id:
:return:
"""
w1 = int(bit.get_word()) - 1
w2 = int(bit.get_word(2)) - 1
bit.setTopic(topic_id)
self.nb_z[topic_id] += 1
self.nwz[int(topic_id)][w1] = self.nwz[int(topic_id)][w1] + 1
self.nwz[int(topic_id)][w2] = self.nwz[int(topic_id)][w2] + 1
def compute_pz_b(self, bit, pz):
"""
更新 话题的概率分布
:param bit:
:param pz:
:return:
"""
w1 = bit.get_word() - 1
w2 = bit.get_word(2) - 1
for k in xrange(self.topic_num):
if self.has_background and k == 0:
pw1k = self.pw_b[w1]
pw2k = self.pw_b[w2]
else:
pw1k = (self.nwz[k][w1] + self.beta) / (2 * self.nb_z[k] + self.voca_size * self.beta)
pw2k = (self.nwz[k][w2] + self.beta) / (2 * self.nb_z[k] + 1 + self.voca_size * self.beta)
pk = (self.nb_z[k] + self.alpha) / (len(self.biterms) + self.topic_num * self.alpha)
pz[k] = pk * pw1k * pw2k
def mult_sample(self, pz):
"""
sample from mult pz
:param pz:
:return:
"""
for i in xrange(1, self.topic_num):
pz[i] += pz[i - 1]
u = random.random()
k = None
for k in xrange(0, self.topic_num):
if pz[k] >= u * pz[self.topic_num - 1]:
break
if k == self.topic_num:
k -= 1
return k
分析结果的应用,主要计算
P(z|d)=p(z|B)p(B|d)
B 表示句子中的所有Biterm项b,上式表示计算:
P(z|b)P(b|d)的乘积的最大值
代码块
def infer_sentence_topic(self, sentence, topic_num=1, min_pro=0.001):
“”"
BTM topic model to infer a document or sentence 's topic
基于 biterm s 计算问题
:param sentence: sentence
:param topic_num: 返回 可能话题数目 最多返回
:param min_pro: 话题概率最小阈值,只有概率大于该值,才是有效话题,否则不返回
:return: 返回可能的话题列表,及话题概率
“”"
sentence_biterms = self.SentenceProcess(sentence)
topic_pro = [0] * self.topic_num
# 短文本分析中,p (b|d) = nd_b/doc(nd_b) doc(nd_b) 表示 计算的query 的所有biterm的计数
# 因此,在short text 的p(b|d) 计算为1/biterm的数量
bit_size = len(sentence_biterms)
if not sentence_biterms:
return [(1.0, -1)]
for bit in sentence_biterms:
# cal p(z|d) = p(z|b)*p(b|d)
# cal p(z|b)
pz = [0] * self.topic_num
self.compute_pz_b(bit, pz)
pz_sum = sum(pz)
pz = map(lambda pzk: pzk / pz_sum, pz)
for x, y in zip(range(self.topic_num), pz):
topic_pro[x] += y / bit_size
min_result = zip(topic_pro, range(self.topic_num))
min_result.sort(key=lambda x: x[0], reverse=True)
result = []
for re in min_result:
if re[0] > min_pro:
result.append(re)
return result[:topic_num]
至此BTM的python实现介绍完毕,其中文文本分析源码见个人github:
https://github.com/liguoyu1/python/tree/master/TopicModel
其中MyCode函数未找到,会在codeNLP/tools下找到,或者修改注释掉即可
本人是个NLP新人,许多地方可能还存在一些问题,如果发现,请不吝赐教!