[python] LDA處理文件主題分佈及分詞、詞頻、tfidf計算

CopperDong發表於2017-11-07
這篇文章主要是講述如何通過LDA處理文字內容TXT,並計算其文件主題分佈,主要是核心程式碼為主。其中LDA入門知識介紹參考這篇文章,包括安裝及用法:
        [python] LDA處理文件主題分佈程式碼入門筆記

        1.輸入輸出

        輸入是test.txt檔案,它是使用Jieba分詞之後的文字內容,通常每行代表一篇文件。
        該文字內容原自部落格:文字分析之TFIDF/LDA/Word2vec實踐 ,推薦大家去閱讀。
[plain] view plain copy
  1. 新春 備 年貨 , 新年 聯歡晚會  
  2. 新春 節目單 , 春節 聯歡晚會 紅火  
  3. 大盤 下跌 股市 散戶  
  4. 下跌 股市 賺錢  
  5. 金猴 新春 紅火 新年  
  6. 新車 新年 年貨 新春  
  7. 股市 反彈 下跌  
  8. 股市 散戶 賺錢  
  9. 新年 , 看 春節 聯歡晚會  
  10. 大盤 下跌 散戶  
        輸出則是這十篇文件的主題分佈,Shape(10L, 2L)表示10篇文件,2個主題。
        具體結果如下所示:
[plain] view plain copy
  1. shape: (10L, 2L)  
  2. doc: 0 topic: 0  
  3. doc: 1 topic: 0  
  4. doc: 2 topic: 1  
  5. doc: 3 topic: 1  
  6. doc: 4 topic: 0  
  7. doc: 5 topic: 0  
  8. doc: 6 topic: 1  
  9. doc: 7 topic: 1  
  10. doc: 8 topic: 0  
  11. doc: 9 topic: 1  
        同時呼叫 matplotlib.pyplot 輸出了對應的文件主題分佈圖,可以看到主題Doc0、Doc1、Doc8分佈於Topic0,它們主要描述主題新春;而Doc2、Doc3、Doc9分佈於Topic1,主要描述股市。


        其過程中也會輸出描述LDA執行的資訊,如下圖所示:


        2.核心程式碼

        其中核心程式碼如下圖所示,包括讀取文字、LDA執行、輸出繪圖等操作。

[python] view plain copy
  1. # coding=utf-8           
  2. import os    
  3. import sys  
  4. import numpy as np  
  5. import matplotlib  
  6. import scipy  
  7. import matplotlib.pyplot as plt  
  8. from sklearn import feature_extraction    
  9. from sklearn.feature_extraction.text import TfidfTransformer    
  10. from sklearn.feature_extraction.text import CountVectorizer  
  11. from sklearn.feature_extraction.text import HashingVectorizer   
  12.   
  13. if __name__ == "__main__":  
  14.   
  15.   
  16.     #儲存讀取語料 一行預料為一個文件   
  17.     corpus = []  
  18.     for line in open('test.txt''r').readlines():  
  19.         #print line  
  20.         corpus.append(line.strip())  
  21.     #print corpus  
  22.       
  23.     #將文字中的詞語轉換為詞頻矩陣 矩陣元素a[i][j] 表示j詞在i類文字下的詞頻  
  24.     vectorizer = CountVectorizer()  
  25.     print vectorizer  
  26.   
  27.     X = vectorizer.fit_transform(corpus)  
  28.     analyze = vectorizer.build_analyzer()  
  29.     weight = X.toarray()  
  30.   
  31.     print len(weight)  
  32.     print (weight[:5, :5])  
  33.   
  34.     #LDA演算法  
  35.     print 'LDA:'  
  36.     import numpy as np  
  37.     import lda  
  38.     import lda.datasets  
  39.     model = lda.LDA(n_topics=2, n_iter=500, random_state=1)  
  40.     model.fit(np.asarray(weight))     # model.fit_transform(X) is also available  
  41.     topic_word = model.topic_word_    # model.components_ also works  
  42.   
  43.     #文件-主題(Document-Topic)分佈  
  44.     doc_topic = model.doc_topic_  
  45.     print("type(doc_topic): {}".format(type(doc_topic)))  
  46.     print("shape: {}".format(doc_topic.shape))  
  47.   
  48.     #輸出前10篇文章最可能的Topic  
  49.     label = []        
  50.     for n in range(10):  
  51.         topic_most_pr = doc_topic[n].argmax()  
  52.         label.append(topic_most_pr)  
  53.         print("doc: {} topic: {}".format(n, topic_most_pr))  
  54.           
  55.     #計算文件主題分佈圖  
  56.     import matplotlib.pyplot as plt    
  57.     f, ax= plt.subplots(61, figsize=(88), sharex=True)    
  58.     for i, k in enumerate([012389]):    
  59.         ax[i].stem(doc_topic[k,:], linefmt='r-',    
  60.                    markerfmt='ro', basefmt='w-')    
  61.         ax[i].set_xlim(-12)     #x座標下標  
  62.         ax[i].set_ylim(01.2)    #y座標下標  
  63.         ax[i].set_ylabel("Prob")    
  64.         ax[i].set_title("Document {}".format(k))    
  65.     ax[5].set_xlabel("Topic")  
  66.     plt.tight_layout()  
  67.     plt.show()    
        同時如果希望查詢每個主題對應的問題詞權重分佈情況如下:
[python] view plain copy
  1. import matplotlib.pyplot as plt    
  2. f, ax= plt.subplots(21, figsize=(66), sharex=True)    
  3. for i, k in enumerate([01]):         #兩個主題  
  4.     ax[i].stem(topic_word[k,:], linefmt='b-',    
  5.                markerfmt='bo', basefmt='w-')    
  6.     ax[i].set_xlim(-2,20)    
  7.     ax[i].set_ylim(01)    
  8.     ax[i].set_ylabel("Prob")    
  9.     ax[i].set_title("topic {}".format(k))    
  10.     
  11. ax[1].set_xlabel("word")    
  12.     
  13. plt.tight_layout()    
  14. plt.show()  
        執行結果如下圖所示:共2個主題Topics,15個核心詞彙。
        繪圖推薦文章:http://blog.csdn.net/pipisorry/article/details/37742423
        PS:講到這裡,整個完整的LDA演算法就算結束了,你可以通過上面的程式碼進行LDA主題分佈的計算,下面是一些問題。


        3.TFIDF計算及詞頻TF計算

        特徵計算方法參考:Feature Extraction - scikit-learn

[python] view plain copy
  1. #計算TFIDF  
  2. corpus = []  
  3.   
  4. #讀取預料 一行預料為一個文件   
  5. for line in open('test.txt''r').readlines():  
  6.     #print line  
  7.     corpus.append(line.strip())  
  8. #print corpus  
  9.   
  10. #將文字中的詞語轉換為詞頻矩陣 矩陣元素a[i][j] 表示j詞在i類文字下的詞頻  
  11. vectorizer = CountVectorizer()  
  12.   
  13. #該類會統計每個詞語的tf-idf權值  
  14. transformer = TfidfTransformer()  
  15.   
  16. #第一個fit_transform是計算tf-idf 第二個fit_transform是將文字轉為詞頻矩陣  
  17. tfidf = transformer.fit_transform(vectorizer.fit_transform(corpus))  
  18.   
  19. #獲取詞袋模型中的所有詞語    
  20. word = vectorizer.get_feature_names()  
  21.   
  22. #將tf-idf矩陣抽取出來,元素w[i][j]表示j詞在i類文字中的tf-idf權重  
  23. weight = tfidf.toarray()  
  24.   
  25. #列印特徵向量文字內容  
  26. print 'Features length: ' + str(len(word))  
  27. for j in range(len(word)):  
  28.     print word[j]  
  29.   
  30. #列印每類文字的tf-idf詞語權重,第一個for遍歷所有文字,第二個for便利某一類文字下的詞語權重    
  31. for i in range(len(weight)):  
  32.     for j in range(len(word)):  
  33.         print weight[i][j],  
  34.     print '\n'  
        輸出如下圖所示,共統計處特徵詞15個,對應TF-IDF矩陣,共10行資料對應txt檔案中的10個文件,每個文件15維資料,儲存TF-IDF權重,這就可以通過10*15的矩陣表示整個文件權重資訊。
[plain] view plain copy
  1. Features length: 15  
  2. 下跌 反彈 大盤 年貨 散戶 新年 新春 新車 春節 紅火 聯歡晚會 股市 節目單 賺錢 金猴  
  3.   
  4. 0.0 0.0 0.0 0.579725686076 0.0 0.450929562568 0.450929562568 0.0 0.0 0.0 0.507191470855 0.0 0.0 0.0 0.0   
  5. 0.0 0.0 0.0 0.0 0.0 0.0 0.356735384792 0.0 0.458627428458 0.458627428458 0.401244805261 0.0 0.539503693426 0.0 0.0   
  6. 0.450929562568 0.0 0.579725686076 0.0 0.507191470855 0.0 0.0 0.0 0.0 0.0 0.0 0.450929562568 0.0 0.0 0.0   
  7. 0.523221265036 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.523221265036 0.0 0.672665604612 0.0   
  8. 0.0 0.0 0.0 0.0 0.0 0.410305398084 0.410305398084 0.0 0.0 0.52749830162 0.0 0.0 0.0 0.0 0.620519542315  
  9. 0.0 0.0 0.0 0.52749830162 0.0 0.410305398084 0.410305398084 0.620519542315 0.0 0.0 0.0 0.0 0.0 0.0 0.0  
  10. 0.482964462575 0.730404446714 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.482964462575 0.0 0.0 0.0   
  11. 0.0 0.0 0.0 0.0 0.568243852685 0.0 0.0 0.0 0.0 0.0 0.0 0.505209504985 0.0 0.649509260872 0.0   
  12. 0.0 0.0 0.0 0.0 0.0 0.505209504985 0.0 0.0 0.649509260872 0.0 0.568243852685 0.0 0.0 0.0 0.0   
  13. 0.505209504985 0.0 0.649509260872 0.0 0.568243852685 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0   
        但是在將TF-IDF用於LDA演算法model.fit(np.asarray(weight))時,總是報錯如下:
        TypeError: Cannot cast array data from dtype('float64') to dtype('int64') according to the rule 'safe'
        所以後來LDA我採用的是統計詞頻的方法進行的,該段程式碼如下:
[python] view plain copy
  1. #儲存讀取語料 一行預料為一個文件   
  2. corpus = []  
  3. for line in open('test.txt''r').readlines():  
  4.     #print line  
  5.     corpus.append(line.strip())  
  6. #print corpus  
  7.   
  8. #將文字中的詞語轉換為詞頻矩陣 矩陣元素a[i][j] 表示j詞在i類文字下的詞頻  
  9. vectorizer = CountVectorizer()  
  10.   
  11. #fit_transform是將文字轉為詞頻矩陣  
  12. X = vectorizer.fit_transform(corpus)  
  13.   
  14. #獲取詞袋模型中的所有詞語   
  15. word = vectorizer.get_feature_names()  
  16. analyze = vectorizer.build_analyzer()  
  17. weight = X.toarray()  
  18.   
  19. #列印特徵向量文字內容  
  20. print 'Features length: ' + str(len(word))  
  21. for j in range(len(word)):  
  22.     print word[j],   
  23.   
  24. #列印每類文字詞頻矩陣  
  25. print 'TF Weight: '  
  26. for i in range(len(weight)):  
  27.     for j in range(len(word)):  
  28.         print weight[i][j],  
  29.     print '\n'  
  30.   
  31. print len(weight)  
  32. print (weight[:5, :5])  
        輸出如下所示:
[plain] view plain copy
  1. Features length: 15  
  2. 下跌 反彈 大盤 年貨 散戶 新年 新春 新車 春節 紅火 聯歡晚會 股市 節目單 賺錢 金猴 TF Weight:  
  3.    
  4. 0 0 0 1 0 1 1 0 0 0 1 0 0 0 0   
  5. 0 0 0 0 0 0 1 0 1 1 1 0 1 0 0   
  6. 1 0 1 0 1 0 0 0 0 0 0 1 0 0 0   
  7. 1 0 0 0 0 0 0 0 0 0 0 1 0 1 0   
  8. 0 0 0 0 0 1 1 0 0 1 0 0 0 0 1   
  9. 0 0 0 1 0 1 1 1 0 0 0 0 0 0 0   
  10. 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0   
  11. 0 0 0 0 1 0 0 0 0 0 0 1 0 1 0   
  12. 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0   
  13. 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0   
  14.   
  15. 10  
  16. [[0 0 0 1 0]  
  17.  [0 0 0 0 0]  
  18.  [1 0 1 0 1]  
  19.  [1 0 0 0 0]  
  20.  [0 0 0 0 0]]  
        得到weight權重後,然後呼叫對應的演算法即可執行不用的應用,如:
        import lda
        model = lda.LDA(n_topics=20, n_iter=500, random_state=1)
        model.fit(np.asarray(weight)) 
        from sklearn.cluster import KMeans  
        clf = KMeans(n_clusters=4)   #景區 動物 人物 國家  
        s = clf.fit(weight)  


        4.百度互動主題分佈例子

        輸入資料主要是前面講述過的爬取百度百科、互動百科的景區、動物、人物、國家四類資訊,具體如下所示:

        輸出如下所示,共12行資料,其中doc0~doc2主題分佈為topic1,其主題表示景區;doc3~doc5主題分佈為topic3,其主題表示動物;doc6~doc8主題分佈為topic0,其主題表示人物;doc9~doc11主題分佈為topic2,其主題表示國家。
[plain] view plain copy
  1. shape: (12L, 4L)  
  2. doc: 0 topic: 1  
  3. doc: 1 topic: 1  
  4. doc: 2 topic: 1  
  5. doc: 3 topic: 3  
  6. doc: 4 topic: 3  
  7. doc: 5 topic: 3  
  8. doc: 6 topic: 0  
  9. doc: 7 topic: 0  
  10. doc: 8 topic: 0  
  11. doc: 9 topic: 2  
  12. doc: 10 topic: 2  
  13. doc: 11 topic: 2  

        5.計算主題TopN

        主要是回覆讀者的問題,如何計算主題的TopN關鍵詞。核心程式碼如下:

[python] view plain copy
  1. #LDA演算法    
  2. print 'LDA:'    
  3. import numpy as np    
  4. import lda    
  5. import lda.datasets    
  6. model = lda.LDA(n_topics=2, n_iter=500, random_state=1)    
  7. model.fit(np.asarray(weight))     # model.fit_transform(X) is also available    
  8. topic_word = model.topic_word_    # model.components_ also works  
  9.   
  10. #輸出主題中的TopN關鍵詞  
  11. word = vectorizer.get_feature_names()  
  12. for w in word:  
  13.     print w  
  14. print topic_word[:, :3]  
  15. n = 5    
  16. for i, topic_dist in enumerate(topic_word):    
  17.     topic_words = np.array(word)[np.argsort(topic_dist)][:-(n+1):-1]    
  18.     print(u'*Topic {}\n- {}'.format(i, ' '.join(topic_words)))    
  19.   
  20.   
  21. #文件-主題(Document-Topic)分佈    
  22. doc_topic = model.doc_topic_    
  23. print("type(doc_topic): {}".format(type(doc_topic)))    
  24. print("shape: {}".format(doc_topic.shape))    

        通過word = vectorizer.get_feature_names()獲取整個預料的詞向量,其中TF-IDF對應的就是它的值。然後再獲取其位置對應的關鍵詞即可,程式碼中輸出5個關鍵詞,如下圖所示:



        講到此處你也應該理解了LDA的基本用法和適用場景,你可以通過它進行新聞主題分佈,同時再進行引文推薦、聚類演算法等操作。
        總之,希望這篇基礎性的文章對你有所幫助吧!還是那句話:
        雖然我寫這類文章看起來很簡單,尤其對於機器學習的大牛來說,感覺沒什麼實質內容;但是如果你剛接觸這類知識,還是非常頭疼的,想找到一個可執行的演算法很困難。
        這也是為什麼總感覺以前學習了一些原理或理論的東西,而實際應用不是很明白,這種感覺就像學游泳,在岸上看別人感覺什麼都會了,但想要學會還是得下水,一步一步來,而我寫的這類基礎文章就相當於帶你下水吧!後面你才能做些自己喜歡的演算法和研究。
        最近真的很忙,同時我認識了一位很優秀的女生,總算邁出了人生最重要的一步,就是真正的勇敢的出去接觸些異性朋友,這感覺非常不錯的。同時學校工作那邊仍然在等訊息,真心想回家當一名軟體相關的教師啊~
        最後附上最近朋友圈的一條資訊:

        哎!感嘆下時光吧,僅以此詩紀念這三年寫部落格的堅持和北理最後的四個月:
        但行好事,莫問前程。
        待隨滿天李桃,再追學友趣事。

        (By:Eastmount 2016-03-15 深夜3點  http://blog.csdn.net/eastmount/ )

相關文章