暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

自然语言处理|主题模型之LDA:隐含狄利克雷分布

数艺学苑 2020-10-17
1183

数据分析的核心就是要从数据中获取“信息”。近年来,随着数据量的增加,尤其是非结构化数据量的增加,通过传统分析方法,我们越来越难获得相关且理想的信息了,但如今,机器学习和自然语言处理领域已开发出一些强大的方法,可用于挖掘数据并获取正在寻找的信息。主题模型(Topic Model)就是其中用于在一系列文档中发现抽象主题的一种统计模型。


不同于使用正则表达式或基于字典的关键字搜索技术的基于规则的文本挖掘方法,主题模型是一种无监督的方法,用于查找和观察大量文本集中的一堆单词(称为“主题”)。
这里的主题并不是也不需要是具体的,它可以定义抽象的“语料库中同时出现的术语的重复模式”。打个比方,假设我们有100个文本集合。在遍历每个文本后,发现其中有十个包含“机器学习”,“培训”,“监督”,“无监督”,“数据集”等词,这些词是什么意思并不重要,重要的是我们发现了100个文本中有10%包含了这些词,我们能够将这10篇文章归为同一“主题”。当我们得到从未见过的新文本时,主题模型就更加见效了,即使我们一个单词都看不懂,我们依旧可以得出结论:“啊!这10篇文章在讲同一个‘主题’!”



主题模型对于文档聚类,组织大量文本数据,从非结构化文本中检索信息以及选择都非常有用。例如,《纽约时报》就在使用主题模型来提升其用户–文章推荐引擎。在招聘行业中主题模型的使用也尤为频繁,猎头们通过提取职位描述的潜在特征,将其映射到合适的候选人上,这能有效提高他们的招聘效率和质量。除此之外,主题模型也被用于组织电子邮件,客户评论和用户社交媒体资料的大型数据集。




  • 隐含狄利克雷分布LDA

 
隐含狄利克雷分布则是主题建模里最常见的模型之一,是David M. Blei、Andrew Y. Ng、Michael I. Jordan 在2003年提出的,目前在文本挖掘领域包括文本主题识别、文本分类以及文本相似度计算方面都有广泛应用。
 
LDA是一种典型的词袋模型(Bag of Words Model),它假设每个主题都是一组基础单词的混合,而每个文档都是一组主题概率的混合。即它认为一篇文档是由一组词构成的一个集合,词与词之间没有顺序以及先后的关系。一篇文档可以包含多个主题,文档中每一个词都由其中的一个主题生成。
 
首先我们先从整体上感知一下LDA的生成过程:给定M个文档,N个单词和先前K个主题,模型训练输出的是psi(每个主题的单词分布)以及phi(每个文档的主题分布)
 
其实,LDA主要运用的是矩阵分解技术和采样技术
 
在向量空间中,任何语料库(文档集合)都可以表示为文档-词矩阵。下面的矩阵显示了N个文档D1,D2,D3 ... Dn的语料库,以及M个单词W1,W2..Wn的词汇量,框内i,j值则是文档Di中单词Wj的频率计数。


而LDA则通过降维,将这个文档术-词矩阵转化为两个较低维的矩阵M1和M2。
M1是文档-主题矩阵,M2是主题–词矩阵,其维度分别为(N,K)和(K,M),其中N是文档数,K是主题数,M是词汇量


虽然这两个矩阵已经显示出主题-词和文档-主题的分布情况,但这些分布有待改进,而这便是LDA的主要目标————利用采样技术来进行矩阵迭代。
 
  • LDA的两个参数:Alpha和Beta超参数

 
Alpha代表文档-主题密度,而Beta代表主题-词密度,其中,Alpha值越高就代表文档由更多主题组成,而Alpha值越低,文档包含的主题就会更少。另一方面,Beta越高就表示主题由语料库中的大量单词组成,而Beta值越低则说明主题由越少的单词组成。其中,我们需要再明确几个概念:
 
   1.主题数:要从语料库中提取的主题数。研究人员已经开发出通过使用Kullback Leibler发散分数来获得最佳主题数的方法。这里不会对此进行讨论,因为它太数学了,喜欢钻研的朋友可以参考这篇关于KL散度使用的元论文。
(http://link.springer.com/chapter/10.1007%2F978-3-642-13657-3_43)
 
   2.主题词数:单个主题中的单词数。这个参数一般根据具体研究要求决定,如果问题涉及提取主题或概念,则建议选择较大的数字,即主题内建议包括大量词汇;如果问题涉及提取特征或术语,则建议选择较小的数字。
 
   3.迭代次数/通过次数:LDA允许收敛的最大迭代次数。
 
  • LDA的收敛点

 
如何控制好文档-主题和主题-词语分布呢?这里需要讲到LDA在迭代中的收敛点概念,即概率P。
其概率为P,这是两个概率p1和p2的乘积。
 
对于每个主题,计算两个概率p1和p2,而概率P则是这两个概率的乘积:
   p1(主题t 文档d)=文档d中当前分配给主题t的单词所占的比例;
   p2(单词w 主题t)=主题t在该单词w产生的所有文档中的分配比例。
 
需要注意的是,在乘积更新的步骤中,LDA模型是默认所有现有单词-主题分配(当前单词除外)都是正确的。本质上,P就是主题t生成单词w的概率,因此以新的概率调整当前单词的主题是有意义的。
 
经过多次迭代后,P达到了一个稳定的状态,其中文档主题和主题词的分布相当好,LDA的收敛点便生成了。
 
  • 使用单词包的LDA应用


   数据来源:
  我们将使用15年内发布量超过一百万条新闻的标题列表数据集作为实例数据源,大家可以在Kaggle里下载。
https://www.kaggle.com/therohk/million-headlines/data

    import pandas as pd
    data = pd.read_csv('abcnews-date-text.csv', error_bad_lines=False)
    data_text = data[['headline_text']]
    data_text['index'] = data_text.index
    documents = data_text
    复制
     
    我们先来看看数据情况:
     
      print(len(documents))
      print(documents[:5])
      复制

       
      数据预处理:
       
      需要执行以下步骤:
      1. 词语切分(Tokenization)::将内容分成句子,句子分成单词。再将所有单词变为小写,去除标点;
      2. 去除小于3字符的单词;
      3. 去掉停用词;
      4. 词性还原(Lemmatization):将第三人称词变为第一人称,过去时和将来时动词变为现在时;
      5. 词干提取(Stemming):将单词简化为其词根形式
        #Loading gensim and nltk libraries
        import gensim
        from gensim.utils import simple_preprocess
        from gensim.parsing.preprocessing import STOPWORDS
        from nltk.stem import WordNetLemmatizer, SnowballStemmer
        from nltk.stem.porter import *
        import numpy as np
        np.random.seed(2018)
        import nltk
        nltk.download('wordnet')
        复制
          
        在导入模块后,我们就开始编写函数来对数据集执行lemmatize和stem预处理步骤吧:
         
          def lemmatize_stemming(text):
          return stemmer.stem(WordNetLemmatizer().lemmatize(text, pos='v'))

          def preprocess(text):
          result = []
          for token in gensim.utils.simple_preprocess(text):
          if token not in gensim.parsing.preprocessing.STOPWORDS and len(token) > 3:
          result.append(lemmatize_stemming(token))
          return result
          复制
           
          选择预处理后要预览的文档:

            doc_sample = documents[documents['index'] == 4310].values[0][0]
            print('original document: ')
            words = []
            for word in doc_sample.split(' '):
            words.append(word)
            print(words)
            print('\n\n tokenized and lemmatized document: ')
            print(preprocess(doc_sample))
            复制
             


             
            可以看到,对于文本的数据预处理完成了。
             
            接下来我们将预处理标题文本,然后将结果保存为' processed_docs '
             
              processed_docs = documents['headline_text'].map(preprocess)
              processed_docs[:10]
              复制




              数据集的词包
               
              从“processed_docs”里创建一个dictionary,其中包含单词在训练集中出现的次数。
               
                dictionary = gensim.corpora.Dictionary(processed_docs)count = 0
                for k, v in dictionary.iteritems():
                print(k, v)
                count += 1
                if count > 10:
                   break
                复制



                过滤分词
                 
                —去掉少于出现在15个文档(绝对数量)或
                —超过0.5个文档(占总语料库大小的比例,而不是绝对数量)的分词
                 
                在执行以上两个步骤之后,只保留前10万个最频繁的分词
                 
                  dictionary.filter_extremes(no_below=15, no_above=0.5, keep_n=100000)
                  复制

                   
                  使用Gensim doc2bow
                   
                  为每个文档创建一个dictionary,然后报告有多少个文档以及这些单词出现的次数。将其保存为' bow_corpus ',这以后再检查前面选中的文档。
                   
                    bow_corpus = [dictionary.doc2bow(doc) for doc in processed_docs]
                    bow_corpus[4310
                    复制



                    我们来看看样例预处理文档后的预览单词包吧:
                     
                      bow_doc_4310 = bow_corpus[4310]
                      for i in range(len(bow_doc_4310)):
                          print("Word {} (\"{}\") appears {} time.".format(bow_doc_4310[i][0], dictionary[bow_doc_4310[i][0]], bow_doc_4310[i][1]))
                      复制


                      使用单词包运行LDA
                       
                      这里我们使用gensim.model训练lda模型LdaMulticore并保存到' lda_model '

                        lda_model = gensim.models.LdaMulticore(bow_corpus, num_topics=10, id2word=dictionary, passes=2, workers=2)
                        for idx, topic in lda_model.print_topics(-1):
                        print('Topic: {} \nWords: {}'.format(idx, topic))
                        复制
                        执行以上代码后,对于每个主题,我们就能探索出在该主题中的单词及其相对权重了
                         


                        例如Topic 5,‘market’,‘news’,‘women’,‘live’的权重较高,那么我们可以推断该主题讨论的主要是经济生活相关的内容。
                         
                        这便是典型的LDA应用,当然,除了单词包外还有TF-IDF等模型用以运行LDA,源代码都可以GitHub里练习
                        https://github.com/susanli2016/NLP-with-Python/blob/master/LDA_news_headlines.ipynb




                         
                        参考来源:
                        https://towardsdatascience.com/topic-modeling-and-latent-dirichlet-allocation-in-python-9bf156893c24
                        https://www.analyticsvidhya.com/blog/2016/08/beginners-guide-to-topic-modeling-in-python/
                        https://towardsdatascience.com/end-to-end-topic-modeling-in-python-latent-dirichlet-allocation-lda-35ce4ed6b3e0


                        本文作者: 



                        指导老师:

                        长按关注我们

                        欢迎关注微信公众号“沈浩老师”

                        文章转载自数艺学苑,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                        评论