Gensim LDA主題模型實驗
本文利用gensim進行LDA主題模型實驗,第一部分是基於前文的wiki語料,第二部分是基於Sogou新聞語料。
1. 基於wiki語料的LDA實驗
上一文得到了wiki純文字已分詞語料 wiki.zh.seg.utf.txt,去停止詞後可進行LDA實驗。
import codecs from gensim.models import LdaModel from gensim.corpora import Dictionary train = [] stopwords = codecs.open('stopwords.txt','r',encoding='utf8').readlines() stopwords = [ w.strip() forw in stopwords ] fp = codecs.open('wiki.zh.seg.utf.txt','r',encoding='utf8') for line in fp: line = line.split() train.append([ w for w in line if w not in stopwords ]) dictionary = corpora.Dictionary(train) corpus = [ dictionary.doc2bow(text) for text in train ] lda = LdaModel(corpus=corpus, id2word=dictionary, num_topics=100)
同時gensim也提供了對wiki壓縮包直接進行抽取並儲存為稀疏矩陣的指令碼 make_wiki,可在bash執行下面命令檢視用法。
python -m gensim.scripts.make_wiki #USAGE: make_wiki.py WIKI_XML_DUMP OUTPUT_PREFIX [VOCABULARY_SIZE] python -m gensim.scripts.make_wiki zhwiki-latest-pages-articles.xml.bz2 zhwiki
執行時間比較久,具體情況可以看gensim官網,結果如下,mm字尾表示Matrix Market格式儲存的稀疏矩陣:
-rw-r--r-- 1 chenbingjin data 172M 7月 1 12:10 zhwiki_bow.mm -rw-r--r-- 1 chenbingjin data 1.3M 7月 1 12:10 zhwiki_bow.mm.index -rw-r--r-- 1 chenbingjin data 333M 7月 1 12:16 zhwiki_tfidf.mm -rw-r--r-- 1 chenbingjin data 1.3M 7月 1 12:16 zhwiki_tfidf.mm.index -rw-r--r-- 1 chenbingjin data 1.9M 7月 1 12:10 zhwiki_wordids.txt
利用 tfidf.mm 及wordids.txt 訓練LDA模型
# -*- coding: utf-8 -*- from gensim import corpora, models # 語料匯入 id2word = corpora.Dictionary.load_from_text('zhwiki_wordids.txt') mm = corpora.MmCorpus('zhwiki_tfidf.mm') # 模型訓練,耗時28m lda = models.ldamodel.LdaModel(corpus=mm, id2word=id2word, num_topics=100)
模型結果
訓練過程指定引數 num_topics=100, 即訓練100個主題,通過print_topics() 和print_topic() 可檢視各個主題下的詞分佈,也可通過save/load 進行模型儲存載入。
# 列印前20個topic的詞分佈 lda.print_topics(20) # 列印id為20的topic的詞分佈 lda.print_topic(20) #模型的儲存/ 載入 lda.save('zhwiki_lda.model') lda = models.ldamodel.LdaModel.load('zhwiki_lda.model')
對新文件,轉換成bag-of-word後,可進行主題預測。
模型差別主要在於主題數的設定,以及語料本身,wiki語料是全領域語料,主題分佈並不明顯,而且這裡使用的語料沒有去停止詞,得到的結果差強人意。
test_doc = list(jieba.cut(test_doc)) #新文件進行分詞 doc_bow = id2word.doc2bow(test_doc) #文件轉換成bow doc_lda = lda[doc_bow] #得到新文件的主題分佈 #輸出新文件的主題分佈 print doc_lda for topic in doc_lda: print "%s\t%f\n"%(lda.print_topic(topic[0]), topic[1])
2. 基於Sogou新聞語料的LDA實驗
Sogou實驗室提供了很多中文語料的下載, 全網新聞資料(SogouCA),來自若干新聞站點2012年6月—7月期間國內,國際,體育,社會,娛樂等18個頻道的新聞資料,提供URL和正文資訊。
資料轉碼處理,由於資料是Ascii檔案,容易出現亂碼情況,使用iconv命令轉成utf8,由於XML檔案處理時需要有頂級tag,這裡使用sed 命令在檔案的首行前插入<root>,在尾行後插入</root>
#!/bin/bash #將資料夾下的Ascii檔案轉成utf8 #Usage: ./iconv_encode.sh indir outdir #@chenbingjin 2016-07-01 function conv_encode() { all=`ls ${indir}` for ffile in ${all} do ifile="${indir}${ffile}" ofile="${outdir}${ffile}" echo "iconv $ifile to $ofile" iconv -c -f gb2312 -t utf8 "$ifile" > "$ofile" sed -i '1i <root>' "$ofile" sed -i '$a </root>' "$ofile" done } if [ $# -ne 2 ]; then echo "Usage: ./iconv_encode.sh indir outdir" exit 1 fi indir=$1 outdir=$2 if [ ! -d $outdir ]; then echo "mkdir ${outdir}" mkdir $outdir fi time conv_encode
總共128個檔案,存放在Sogou_data/ 資料夾下,使用iconv_encode.sh 進行處理,新檔案儲存在out資料夾,結果如下:
$ ./iconv_encode.sh Sogou_data/ out/ mkdir out/ iconv Sogou_data/news.allsites.010806.txt to out/news.allsites.010806.txt iconv Sogou_data/news.allsites.020806.txt to out/news.allsites.020806.txt iconv Sogou_data/news.allsites.030806.txt to out/news.allsites.030806.txt iconv Sogou_data/news.allsites.040806.txt to out/news.allsites.040806.txt ...... real 0m27.255s user 0m6.720s sys 0m8.924s
接下來需要對xml格式的資料進行預處理,這裡使用lxml.etree,lxm 是Python的一個html/xml解析並建立dom的庫, 比python自帶的XML解析快。
# -*- coding: utf-8 -*- import os import codecs import logging from lxml import etree logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO) ''' Sogou新聞語料預處理 @chenbingjin 2016-07-01 ''' train = [] # huge_tree=True, 防止檔案過大時出錯 XMLSyntaxError: internal error: Huge input lookup parser = etree.XMLParser(encoding='utf8',huge_tree=True) def load_data(dirname): global train files = os.listdir(dirname) for fi in files: logging.info("deal with "+fi) text = codecs.open(dirname+fi, 'r', encoding='utf8').read() # xml自身問題,存在&符號容易報錯, 用&代替 text = text.replace('&', '&') # 解析xml,提取新聞標題及內容 root = etree.fromstring(text, parser=parser) docs = root.findall('doc') for doc in docs: tmp = "" for chi in doc.getchildren(): if chi.tag == "contenttitle" or chi.tag == "content": if chi.text != None and chi.text != "": tmp += chi.text if tmp != "": train.append(tmp)
得到train訓練語料後,分詞並去停止詞後,便可以進行LDA實驗
from gensim.corpora import Dictionary from gensim.models import LdaModel stopwords = codecs.open('stopwords.txt','r',encoding='utf8').readlines() stopwords = [ w.strip() for w in stopwords ] train_set = [] for line in train: line = list(jieba.cut(line)) train_set.append([ w for w in line if w not in stopwords ]) # 構建訓練語料 dictionary = Dictionary(train_set) corpus = [ dictionary.doc2bow(text) for text in train_set] # lda模型訓練 lda = LdaModel(corpus=corpus, id2word=dictionary, num_topics=20) lda.print_topics(20)
實驗結果:訓練時間久,可以使用 ldamulticore ,整體效果還不錯,可以看出08年新聞主題主要是奧運,地震,經濟等
得到的LDA模型可用於主題預測,給定新的文件預測文件主題分佈,可用於分類。訓練文件中每個詞會分配一個主題,有paper就將這種主題資訊做Topic Word Embedding,一定程度上解決一詞多義問題。