應用LDA建立topic model

這次的實作主要來自想要練習LDA( Latent Dirichlet Allocation )建立topic model的過程,並且再次複習LDA的相關概念,因為之前已經有實作過一次,希望透過不斷的重複檢視,讓自己能更加的熟練。

LDA模型

LDA是 自然語言處理中相當有名的方法,是透過生成模型( 觀察大量資料,估計出資料的生成機制 ),在一系列文件中萃取出抽象的「主題」

LDA假設了 每篇文件都是由少數幾個「主題 (Topic)」所組成,而且每個主題都可以由少數幾個重要的「用詞 (Word)」描述。所以後續的實作中我們可以看到LDA會將每個文本分topic,且每個topic都有幾個重要的用詞。

來自 https://taweihuang.hpd.io/2019/01/10/topic-modeling-lda/

有了「文件生成機制」後,我們接著就會透過觀察大量文件資料,估計文件生成的聯合機率分配。

來自: https://taweihuang.hpd.io/2019/01/10/topic-modeling-lda/

裡面的數學實在太複雜了,有興趣的可以再嘗試去理解~

這次的實作分為兩個階段,分為資料準備以及建立topic model

資料準備

這次因為想試試看建立一篇paper的topic model,所以資料文本的原檔是pdf檔,正好可以訓練自己能夠從更多的資料源來取得資料,因此在資料準備的part,主要的流程是將pdf–>txt–>寫入python

#Run to load pdf to txt function
import os
from io import StringIO
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage

在這個階段我們需要的是io的操作,以及大量使用pdfminer這個相關的套件,他能夠幫助我們很好的將pdf轉換成txt,並且在透過io的相關操作,便可以達成我們在這階段的目標

def convert(fname, pages=None):
    if not pages:
        pagenums = set()
    else:
        pagenums = set(pages)

    output = StringIO()
    manager = PDFResourceManager()
    converter = TextConverter(manager, output, codec='utf-8', laparams=LAParams())
    interpreter = PDFPageInterpreter(manager, converter)
    infile = open(fname, 'rb')
    for page in PDFPage.get_pages(infile, pagenums):
        interpreter.process_page(page)
    infile.close()
    converter.close()
    text = output.getvalue()
    output.close
    return text

首先我們先建立一個convert函數,裡面比較主要的變數為

  • output:管理處理後的輸出
  • manager:是一個pdf資源管理器,目的是為了儲存共享資源
  • converter:主要處理轉換的地方
  • interpreter:創建一個pdf解析器
def convertMultiple(pdfDir, txtDir):
    #iterate through pdfs in pdf directory
    for pdf in os.listdir(pdfDir): 
        fileExtension = pdf.split(".")[-1]
        if fileExtension == "pdf":
            pdfFilename = pdfDir + pdf 
            #get string of text content of pdf
            text = convert(pdfFilename) 
            textFilename = txtDir + pdf[:-4] + ".txt"
            #make text file
            textFile = open(textFilename, "w",encoding='utf-8') 
            #write text to text file
            textFile.write(text) 
            textFile.close()
            print("finish convert to txt", pdf)
    print("finish convert all file")   

其中,可以透過os的操作掃描路徑資料夾裡的檔名,並且透過判斷為pdf後,便把檔案的路徑組合起來,以進行後續分析的動作

for pdf in os.listdir(pdfDir): 
        fileExtension = pdf.split(".")[-1]
        if fileExtension == "pdf":
            pdfFilename = pdfDir + pdf

後續便是將我們剛剛製成的convert運用上,並將其寫入txt中,並且最後印出完成的訊息,這裡有個小細節是,pdf[:-4],是指我們不需要.pdf(後面四個字),所以下次在排除時若是要排除從後面數的,可以嘗試此方式。

pdfdir='pdf/'
textdir='text/'
convertMultiple(pdfdir,textdir)

最後在執行完convertMiltiple,我們便有了我們的資料來源

我們的文章便從pdf檔變成了txt檔,當然也可以進行多篇文章的轉換

建立topic model

首先將我們剛剛轉換好的文章讀入,並且將其中我們不需要的資訊都排除,只關心本文就好,cite和paper基本資訊都排除

article=[]
with open('text/Twitter mood predicts the stock market.txt','r',encoding='utf-8') as f:
    for line in f.readlines():
        article.append(line.strip())
#drop ''
for line in article:
    if line =='':
        article.remove(line)
#目前不關心的資訊:標題等相關的敘述、如作者等等...
article=article[17:]
#移除cite的相關資訊
article=article[:904]

我們一樣使用強大的spicy工具

import spacy
#英文的處理工具
spacy.load('en_core_web_sm')
from spacy.lang.en import English
parser = English()

建立一個tokenize的函式以供後續使用

#將資料tokenize
def tokenize(text):
    #準備一list
    lda_tokens=[]
    #透過spacy的English模型 對text處理
    tokens = parser(text)
    for token in tokens:
        if token.orth_.isspace():
            continue
        elif token.like_url:
            lda_tokens.append('URL')
        elif token.orth_.startswith('@'):
            lda_tokens.append('SCREEN_NAME')
        else:
            lda_tokens.append(token.lower_)
    return lda_tokens 

載入nltk,並建立一個lemmatization(詞型還原)的函式get_lemma()以供後續使用

import nltk
nltk.download('wordnet')
from nltk.corpus import wordnet as wn
def get_lemma(word):
    """
    >>> print(wn.morphy('dogs'))
    dog
    >>> print(wn.morphy('churches'))
    church
    >>> print(wn.morphy('aardwolves'))
    aardwolf
    """
    lemma = wn.morphy(word)
    
    if lemma is None:
        #返回原本的字
        return word
    else:
        
        return lemma

stopword的移除

#stopword的移除
nltk.download('stopwords')
en_stop = set(nltk.corpus.stopwords.words('english'))

建立一個函式以處理資料

def prepare_text_for_lda(text):
    #將文字tokenize
    tokens = tokenize(text)
    #只要長度>4
    tokens =[token for token in tokens if len(token)>4]
    #這些字不能在stopword
    tokens =[token for token in tokens if token not in en_stop]
    return tokens

用text_data存放我們獲得的資料

text_data=[]
for line in article:
    tokens =prepare_text_for_lda(line)
    #lemmatization
    tokens=[get_lemma(w) for w in tokens]
    text_data.append(tokens)
text_data = [w for w in text_data if w !=[]]

目前text_data長得就像這樣,接著我們必須透過轉換將其變為vector的格式

[['social', 'network'],
 ['sentiment', 'tracking'],
 ['stock', 'market'],
 ['collective'],
 ['behavioral',
  'economics',
  'tell',
  'emotion',
  'profoundly',
  'affect',
  'individual',
  'behavior',
  'decision-'],

我們使用這些字建立字典,並以映照的方式,便能將文字轉為數字

from gensim import corpora
data = text_data
#透過gensim以text_data建立字典
dictionary =corpora.Dictionary(data)
#語料庫
corpus = [dictionary.doc2bow(text) for text in data]
dictionary[1]
'social'

corpus

[[(0, 1), (1, 1)],
 [(2, 1), (3, 1)],
 [(4, 1), (5, 1)],
 [(6, 1)],
 [(7, 1),
  (8, 1),
  (9, 1),
  (10, 1),
  (11, 1),
  (12, 1),
  (13, 1),
  (14, 1),
  (15, 1)],

我們可以很方便的透過gensim已經做好的lda模型來套用

#透過LDA找到5個 topics
import gensim
NUM_TOPICS  =5
ldamodel = gensim.models.ldamodel.LdaModel(\
    corpus,num_topics = NUM_TOPICS ,id2word=dictionary,passes=15)
ldamodel.save('model5.gensim')

topics = ldamodel.print_topics(num_words=4)
for topic in topics:
    print(topic)

現在我們透過這篇文章,建立了五個主題了,且每個主題都顯示了前幾個重要的權重字

(0, '0.036*"gpoms" + 0.018*"network" + 0.014*"result" + 0.014*"public"')
(1, '0.041*"values" + 0.029*"public" + 0.025*"dimension" + 0.022*"granger"')
(2, '0.034*"period" + 0.020*"positive" + 0.017*"table" + 0.017*"show"')
(3, '0.029*"market" + 0.029*"stock" + 0.024*"public" + 0.019*"prediction"')
(4, '0.028*"happy" + 0.023*"election" + 0.020*"series" + 0.016*"response"')

當我們現在有一句話時,我們可以透過比對,判斷這句話跟哪個主題較相近

new_doc ='Social media can let us interact with each other'
new_doc = prepare_text_for_lda(new_doc)
new_doc_bow = dictionary.doc2bow(new_doc)
print(new_doc_bow)
print(ldamodel.get_document_topics(new_doc_bow))

而根據結果,我們可以發現’Social media can let us interact with each other’ 這句話,和topic0 有最高的相似度0.59

[(1, 1)]
[(0, 0.59886324), (1, 0.10000233), (2, 0.10111895), (3, 0.10001235), (4, 0.10000318)]

最後,若我們要重複使用這個字典,可以將其儲存下來

#將字典存下未來可以使用
import pickle
pickle.dump(corpus,open('corpus.pkl','wb'))
dictionary.save('dictionary.gensim')

Ref

python 利用PDFMiner包操作PDF

生成模型與文字探勘:利用 LDA 建立文件主題模型

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Google photo

您的留言將使用 Google 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s

在 WordPress.com 建立自己的網站
立即開始使用
%d 位部落客按了讚:
search previous next tag category expand menu location phone mail time cart zoom edit close