利用LDA分析《天龙八部》中每十回的话题演变情况

来源:互联网 发布:美萍进销存软件客服 编辑:程序博客网 时间:2024/06/07 00:07

利用LDA分析《天龙八部》中每十回的话题演变情况

一.LDA主题分配模型简介

1.1 LDA是什么

在机器学习领域LDA是两种模型的简称,一种是统计学中的线性判别分析(Linear Discriminant Analysis,简称LDA主要用于降维),另一种是隐狄利克雷分配模型(Latent Dirichlet Allocation,简称LDA)是一种非监督学习技术,可以用来识别大规模文档集(documentcollection)或语料库(corpus)中潜藏的主题信息。它采用了词袋(bag of words)的方法,这种方法将每一篇文档视为一个词频向量,从而将文本信息转化为了易于建模的数字信息。本文主要讨论的为后一种LDA。

传统判断两个文档相似性的方法是通过查看两个文档共同出现的单词的多少,如TF-IDF(TFIDF实际上是:TF* IDF,TF词频,IDF逆向文件频率)等。这些基于词频统计的方法没有考虑到文字背后的语义关联,可能在两个文档共同出现的单词很少甚至没有,但两个文档是相似的。

举个例子,有两个句子分别如下:

 “川普王,大美兴”

 “希拉里大选落败”

 可以看到上面这两个句子没有共同出现的单词,但这两个句子是相似的,如果按传统的方法判断这两个句子肯定不相似,所以在判断文档相关性的时候需要考虑到文档的语义,而语义挖掘的利器是主题模型,LDA就是其中一种比较有效的模型。

1.2 LDA涉及到的基本概念

LDA话题模型中的四个基本概念:

   词(word):数据处理的基本单元,如一个英文单词,一个英文词语

   文档(document):待处理的数据对象,由一系列无序的词组成

   话题(topic):一些列相关的词,以及它们的联合概率

   字典:待处理文档集合中的所有词

如图,一个话题就像一个箱子,里面装着在这个概念下出现频率较高的那些词。

 

假定数据集中包含K个话题和T篇文档,文档中的词来自一个包含N个词的字典。那么我们可以用T个N维向量来表示数据集,每一行代表一篇文档,每一列代表该词在该文档中出现的频度。K个N维向量表示K个话题,每一行表示一个话题,每一列表示该词在该话题中出现的频数。

1.3什么是生成式模型

如果我们要生成一篇文档,它里面的每个词语出现的概率为:

这个概率公式可以用矩阵表示:

其中”文档-词语”矩阵表示每个文档中每个单词的词频,即出现的概率;”主题-词语”矩阵表示每个主题中每个单词的出现概率;”文档-主题”矩阵表示每个文档中每个主题出现的概率。

 给定一系列文档,通过对文档进行分词,计算各个文档中每个单词的词频就可以得到左边这边”文档-词语”矩阵。主题模型就是通过左边这个矩阵进行训练,学习出右边两个矩阵。

LDA的三位作者在原始论文中给了一个简单的例子。比如给定这几个主题:Arts、Budgets、Children、Education,在这几个主题下,可以构造生成跟主题相关的词语,如下图所示:

然后可以根据这些词语生成如下图所示的一篇。文章(其中不同颜色的词语分别对应上图中不同主题下的词)

表面上理解LDA比较简单,无非就是:当看到一篇文章后,我们往往喜欢推测这篇文章是如何生成的,我们可能会认为某个作者先确定这篇文章的几个主题,然后围绕这几个主题遣词造句,表达成文。

根据维基百科上,面的介绍,在LDA模型中一篇文档生成的方式如下:

·          从狄利克雷分布 中取样生成文档i的主题分布 

·          从主题的多项式分布  中取样生成文档i第j个词的主题 

·          从狄利克雷分布  中取样生成主题  的词语分布 

·          从词语的多项式分布  中采样最终生成词语 

其中,类似Beta分布是二项式分布的共轭先验概率分布,而狄利克雷分布是多项式分布的共轭先验概率分布。

此外,LDA贝叶斯网络结构如下图所示:

我们可以看到上面短短的6句话却接连不断或重复出现了beta分布、狄利克雷分布(Dirichlet分布)、共轭先验概率分布、取样,那么请问,这些都是啥呢?

二.LDA数学推倒

2.1 gamma函数

所谓的gamma函数其实就是阶乘的函数形式,上过小学我们都知道n!=1⋅2⋅3…n,如果我问你3的阶乘是多少,你立即回答出1⋅2⋅3=6,但是如果我问你0.5阶乘是什么,如果没有gamma函数就无法回答了,欧拉经过不懈努力,终于发现阶乘的更一般的函数形式gamma函数,直接给出‍gamma函数如下:

1.  

2.  

2.2 二项分布(Binomial distribution)

在概率论中,二项分布即重复n次独立的伯努利试验。在每次试验中只有两种可能的结果(成功/失败),每次成功的概率为p,而且两种结果发生与否互相对立,并且相互独立,与其它各次试验结果无关,事件发生与否的概率在每一次独立试验中都保持不变,则这一系列试验总称为n重伯努利实验,当试验次数为1时,二项分布就是伯努利分布。

在给出二项分布之前,我们来做一个例子,假设你在玩CS这个游戏,你拿着狙击枪,敌人出现你打中敌人的概率是p,打不中敌人的概率是1-p,那么敌人第一次出现你没打中而第二次出现你打中的概率是 (1-p)⋅p。如果敌人出现了n次,而你打中了其中的k次,而不确定具体在哪k次(第1次,还是第4次),这样从n次中任取k次的次数是[图片],而这不确定的k次打中敌人的概率是:

二项分布的概率密度函数是:

2.3 beta分布(beta distribution)

在概率论中,beta分布是指一组定义在区间(0,1)的连续概率分布,有两个参数和,且>0,。

Beta分布的概率密度函数是:

随机变量X服从参数为的beta分布通常写作:

Beta分布主要在LDA算法Gibbs Sampling采样公式中使用,这个关系也就是B函数和Gamma函数的关系(该公式也被称为第一型欧拉积分):

 

 

 


2.4 多项分布

多项分布是二项分布的推广扩展,在n次独立试验中每次只输出k种结果中的一个,且每种结果都有一个确定的概率p。多项分布给出了在多种输出状态的情况下,关于成功次数的各种组合的概率。

5.狄利克雷分布(dirichlet distribution)

dirichlet分布是beta分布在多项情况下的推广,也是多项分布的共轭先验分布。dirichlet分布的概率密度函数如下:

二项分布和多项分布很相似,Beta分布和Dirichlet 分布很相似,而至于“Beta分布是二项式分布的共轭先验概率分布,而狄利克雷分布(Dirichlet分布)是多项式分布的共轭先验概率分布”。

三.LDA算法简介

LDA算法的输入与输出

 

算法输入:分词后的文章集(通常为一篇文章一行)

          主题数K,超参数和

 

算法输出:1.每个主题下词的概率分布

          2.每篇文档的主题词概率分布

 

#伪代码

输入:文章集合(分词处理后),K(类的个数)

输出:已经随机分派了一次的lda模型

begin

申请几个统计量:

p 概率向量维度:K

nw 词在类上的分布维度:M*K 其中M为文章集合的词的总个数

nwsum 每个类上的词的总数维度:K

nd 每篇文章中,各个类的词个数分布维度:V*K 其中V为文章的总个数

ndsum 每篇文章中的词的总个数维度:V

Z 每个词分派一个类维度:V*每篇文章词的个数

theta 文章->类的概率分布 维度:V*K

phi 类->词的概率分布 维度:K*M

#初始化随机分配类

for x in 文章数:

统计ndsum[文章id][词的个数]

for y in 每篇文章的词个数:

给所有词随机分派一个类

词在此类上的分布数目+1

此文章中此类的词的个数+1

此类的总词数 +1

end

 

 

 

四.利用LDA分析《天龙八部》

4.1原始文本处理

在利用LDA主题模型对《天龙八部》剧情进行分析前,我们需要做一些基本的准备工作。首先是对原始文本进行处理,在此我选择了python,如同大多数高级编程语言一样,变长字符串是Python 中的基本类型。Python 在“后台”分配内存以保存字符串(或其它值),程序员不必为此操心。Python 还有一些其它高级语言没有的字符串处理功能。在 Python 中,字符串是“不可改变的序列”。尽管不能“按位置”修改字符串(如字节组),但程序可以引用字符串的元素或子序列,就象使用任何序列一样。Python 使用灵活的“分片”操作来引用子序列,字符片段的格式类似于电子表格中一定范围的行或列。

第一步:去掉中文标点符号,在此利用python自带的正则表达式选取子串功能实现

第二步:去掉异常字符,仅保留汉字

4.2 分词

得到预处理的原始文本后,我们要做的是对原始文本进行分词处理,因为在最终的LDA算法训练阶段我们使用的都是数值型的特征。当前中文分词一般有两大类算法,分别是基于条件随机场和基于隐马尔科夫。在python中有给予条件随机场的genius和基于隐马尔科夫的jieba。在此我选择的是jieba,以下对jieba进行简要说明:

其主要的处理思路如下:

1.加载词典dict.txt

2.从内存的词典中构建该句子的DAG(有向无环图)

3.对于词典中未收录词,使用HMM模型的viterbi算法尝试分词处理

4.已收录词和未收录词全部分词完毕后,使用dp寻找DAG的最大概率路径

5.输出分词结果

第一步:读取文档集并分词

 

4.3初步统计

第一步:去掉停用词,由于汉语的语言特点,文本中可能存在部分无实际意义的词语,为了使分析出的结果更具实际意义,在进行分析前我们先去掉这些无意义词语。主要利用的工具为pandas,其可以很方便的进行一些列的数据统计性处理。

   第二步:4进行词频统计(IDF)

简单词语统计结果

第一部分

第二部分

第三部分

第四部分

段誉

木婉清

甚么

段正淳

鳄神

保定

南海

钟万仇

钟灵

云中鹤

说道

内力

钟夫人

武功

叶二娘

大理

乔峰

段誉

王语嫣

丐帮

阿朱

说道

武功

帮主

鸠摩智

阿碧

什么

赵钱孙

徐长老

契丹

慕容公子

西夏

 

萧峰

游坦之

段正淳

阿紫

乔峰

说道

马夫人

星宿

阿朱

丁春秋

什么

契丹

丐帮

阿紫道

武功

师父

 

虚竹

慕容复

童姥

乌老大

李秋水

武功

王语嫣

丁春秋

苏星河

说道

段誉

女童

师父

小僧

什么

少林

 

 

全书词云:

4.4 LDA主题模型

第一步:得到词频矩阵,统计每个词语在每篇文档中出现的次数

第二步:训练模型并得到结果

最终结果

 

 

 

 

 

 

 

 

 

 

五.附录

5.1 参考文档

5.2 最终结果

主题 0: 帮主 少林 和尚 大哥 说话 姑娘 长老 英雄 言语 大伙儿 大宋 慕容 讯息 不少 黑衣 云中鹤 十分 汉人 一刀尚未

主题 1: 乔峰 段誉 阿朱 王语嫣 丐帮 阿碧 契丹 鸠摩智 西夏 徐长老 赵钱孙 武士 慕容公子 司马林 乔帮主 神医 王夫人诸保昆 表哥 段公子

主题 2: 脸上 原来 跟着 当真 今日 当下 没有 正是 大声 眼见 自然 不由得 右手 本来 天下 胸口 明白 来到 不到敌人

主题 3: 大理 报仇 对方 可惜 不了 一场 这种 寻常 怒道 不久 桌上 一柄 阁下 肩头 清楚 老爷 接过 功夫 物事 正好

主题 4: 段誉 木婉清 保定 段正淳 南海鳄神 钟灵 钟万仇 云中鹤 女郎 大理 钟夫人 叶二娘 左子穆 黄眉僧 司空玄 鸠摩智无量 青袍 本因 刀白凤

主题 5: 女人 范骅 成为 图形 一阳指 率领 伤心 判官笔 匕首 妇人 皇位 萧大王 百姓 无用 段王爷 几个 说起 合十衣襟 义父

主题 6: 姑娘 慢慢 姊姊 对方 小子 一次 吩咐 一张 我要 几个 功力 向前 之色 伤口 躬身 回去 放开 上去 心头 冷冷的

主题 7: 身子 两人 问道 只怕 功夫 不肯 不禁 轻轻 无法 只觉 心道 二人 不得 早已 忽然 啊哟 这才 立即 取出 眼前

主题 8: 说话 全身 不住 适才 神色 想起 之上 内功 出手 容易 中原 老人家 着实 竟然 武林中 隐隐 神情 面前 这是一股

主题 9: 点头 僧人 远远 慕容氏 招数 已经 大石 现下 日后 认得 施展 情势 交给 得知 外面 二来 加上 慕容 望去青年

主题 10: 这位 不错 穴道 十分 高手 居然 左手 不好 解药 身形 就此 磕头 暗器 第一 全然 目光 小姑娘 自行 六脉一拳

主题 11: 萧峰 慕容复 段誉 虚竹 阿紫 王语嫣 鸠摩智 公主 西夏 丐帮 大理 游坦之 王夫人 巴天石 钟灵 星宿 耶律洪基丁春秋 段正淳 契丹

主题 12: 老者 古怪 规矩 害怕 露出 之处 所在 小小 妙极 脚下 一把 了不起 两声 身分 此处 两下 山坡 不小 随手几天

主题 13: 心下 大叫 之间 左手 不见 暗暗 怎地 进来 蓦地 四人 用力 几步 指着 坐在 走出 皇帝 一定 腰间 一座两步

主题 14: 姑娘 南海鳄神 男子 剑法 段誉见 容貌 凌波微 段誉笑 孩儿 段誉心 不信 老僧 吐蕃 真的 欺侮 一株 男女火焰刀 步法 贤弟

主题 15: 段誉 少林寺 包不同 玄慈 段延庆 段公子 慕容公子 群豪 心下 主人 风波恶 方丈 长剑 之间 七十二 戒律 高僧打死 不少 公冶乾

主题 16: 虚竹 乌老大 童姥 慕容复 李秋水 王语嫣 女童 丁春秋 苏星河 和尚 少林 星宿 生死 波罗星 功夫 不平道人阿紫 鸠摩智 缥缈 童姥道

主题 17: 下去 提起 鲜血 渐渐 向来 这件 手下 一件 一日 少年 个个 头上 肩头 神功 之情 连连 找到 万万 跪下中间

主题 18: 萧峰 阿紫 阿朱 游坦之 段正淳 马夫人 星宿 乔峰 契丹 丁春秋 玄难 包不同 丐帮 阮星竹 姊夫 摘星子 白世镜邓百川 耶律洪基 段延庆

主题 19: 施主 身子 干什么 棋盘 客店 机关 掌力 段家 禀告 没什么 雪地 家伙 本门 机密 这一掌 投降 全舵主 师门折磨 舌头

第一部分 (top 主题: 4)

第二部分 (top 主题: 1)

第三部分 (top 主题: 18)

第四部分 (top 主题: 16)

第五部分 (top 主题: 11)

 

 

 

 

 

 

 

 

 

 

 

5.3 源代码:

# coding=utf-8
import os
import string
import sys
import numpy as np
import matplotlib
import scipy
import matplotlib.pyplot as plt
from sklearn import feature_extraction
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import HashingVectorizer
import jieba
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
import re
import pandas as pd
import numpy as np
import lda
if __name__ == "__main__":
    tian_file =open('mynovel.txt').readlines()
    tian_par1 = tian_file[0]
    tian_par2 = tian_file[1]
    tian_par3 = tian_file[2]
    tian_par4 = tian_file[3]
    tian_par5 = tian_file[4]

    corpus = []
    corpus.append(tian_par1.split(','))
    corpus.append(tian_par2.split(','))
    corpus.append(tian_par3.split(','))
    corpus.append(tian_par4.split(','))
    corpus.append(tian_par5.split(','))

    '得到字典'
    tianwords = []
    for i in range(len(corpus)):
        tempwords = corpus[i]
        for word in tempwords:
            if len(word) >=6:
                tianwords.append(word)
    tianwords =pd.DataFrame(tianwords,columns=['segment'])

    '移除停用词语'
    stopuserwords =pd.read_csv('stopusewords.txt',encoding= 'utf-8')
    stopuserwords =pd.DataFrame(list(set(stopuserwords['stopusewords'])),columns=['stopusewords'])

    final_stopuse_words = []
    for stopuse in stopuserwords['stopusewords']:
       final_stopuse_words.append(str(stopuse))

    final_tianwords =tianwords[~tianwords['segment'].isin(final_stopuse_words)]

    tianwords =list(set(final_tianwords['segment']))
    tianwords.sort()

    weight =  [[0 for col in range(len(tianwords))]forrowin range(len(corpus))]
    for i in range(len(weight)):
        for j in range(len(weight[i])):
            weight[i][j] =corpus[i].count(tianwords[j])
    weight = np.array(weight)

    model = lda.LDA(n_topics=20,n_iter=1500, random_state=1)
    model.fit(weight)
    topic_word = model.topic_word_  # 得到每个词语在该篇文档中的权重
    n_top_words = 20 #每个主题选择20个单词

#整体的主题分布
    for i, topic_dist in enumerate(topic_word):
       topic_words =np.array(tianwords)[np.argsort(topic_dist)][:-(n_top_words+1):-1]
       print('主题 {}: {}'.format(i, ' '.join(topic_words)))


    titles = ['第一部分','第二部分','第三部分','第四部分','第五部分']
#每篇文档的主题分布
    doc_topic = model.doc_topic_
    for i in range(5):
        print("{} (top 主题: {})".format(titles[i], doc_topic[i].argmax()))

0 0
原创粉丝点击