Gensim LDA主題模型實驗

本文利用gensim進行LDA主題模型實驗,第一部分是基於前文的wiki語料,第二部分是基於Sogou新聞語料。html

1. 基於wiki語料的LDA實驗python

上一文獲得了wiki純文本已分詞語料 wiki.zh.seg.utf.txt,去中止詞後可進行LDA實驗。bash

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() for w 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運行下面命令查看用法。app

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格式保存的稀疏矩陣:dom

-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模型jsp

# -*- 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)

模型結果ide

訓練過程指定參數 num_topics=100, 即訓練100個主題,經過print_topics() 和print_topic() 可查看各個主題下的詞分佈,也可經過save/load 進行模型保存加載。spa

# 打印前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後,可進行主題預測。code

模型差異主要在於主題數的設置,以及語料自己,wiki語料是全領域語料,主題分佈並不明顯,並且這裏使用的語料沒有去中止詞,獲得的結果差強人意。orm

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和正文信息。

這裏使用的是2008精簡版(一個月數據, 437MB)

數據轉碼處理,因爲數據是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
iconv_encode.sh

總共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解析快。

防止出現異常 XMLSyntaxError: internal error: Huge input lookup ,設置XMLParser參數 huge_tree=True,詳細見代碼:

# -*- 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自身問題,存在&符號容易報錯, 用&amp;代替
        text = text.replace('&', '&amp;')
        # 解析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)
preprocess.py

獲得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,必定程度上解決一詞多義問題。

參考

1. gensim:Experiments on the English Wikipedia

2. Sogou:全網新聞數據

相關文章
相關標籤/搜索