Gensim官方教程翻译(五)——英文维基百科的实验

9 篇文章 3 订阅

仅供个人学习之用,如有错误,敬请指正。原文地址


为了测试gensim的性能,我们在维基百科英文版上运行了一些实验。
这个页面描述了获取与处理维基百科的过程,以便任何人都能再现这个结果。本教程要求已经正确安装了gensim

译者注:维基百科的内容在不断更新,因此本文的结果仅供参考,可能与实际情况有出入。

准备语料库

  1. 首先,从 http://download.wikimedia.org/enwiki/下载维基百科文档的存档(你可能需要一个像这样的文件enwiki-latest-pages-articles.xml.bz2)。这个文件大约8GB包含了所有的英文维基百科的文章(译者注:实际上应该更大)。
  2. 将文章转换为普通文本(处理wiki标记)并且将结果以稀疏TF-IDF向量的形式存储。在Python中,非常轻松就能完成这个任务,我们甚至不需要将整个压缩文件解压到硬盘上。Gensim中有一个脚本可以完成这个任务,仅需:

已经废弃 | Depressed

$ python -m gensim.scripts.make_wiki

当前可用 | Recommend(2017年7月19日)

>>> import genism.corpora.WikiCorpus  
>>> wiki = WikiCorpus('enwiki-20100622-pages-articles.xml.bz2') # 创建word到word_id的映射,需要很长时间,请耐心    
>>> MmCorpus.serialize('wiki_en_tfidf.mm', wiki) # 将语料库存到硬盘中,需要很长时间,请耐心    
>>> wiki.dictionary.save_as_text('wiki_en_wordids.txt')  

注:预处理步骤对8.2G压缩的维基存档实施了2个操作(提取字典、创建于存储稀疏向量),在我的笔记版上消耗了大约9小时,因此你可能想要来1、2杯咖啡。
你需要大概35GB空闲硬盘空间来存储输出的稀疏向量。我建议直接压缩这些文件,例如bzip2(压缩至约13GB)。Gensim可以直接使用压缩文件,可以帮助你节约硬盘空间。

潜在语义分析

首先让我们加载在上述步骤创建的语料库迭代器与字典:

>>> import logging, gensim, bz2
>>> logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)

>>> # 加载id->word映射(字典),上述步骤的结果之一
>>> id2word = gensim.corpora.Dictionary.load_from_text('wiki_en_wordids.txt')
>>> # 加载语料库迭代器
>>> mm = gensim.corpora.MmCorpus('wiki_en_tfidf.mm')
>>> # mm = gensim.corpora.MmCorpus(bz2.BZ2File('wiki_en_tfidf.mm.bz2')) # 如果你压缩了tf-idf输出,请使用这个

>>> print(mm)
MmCorpus(3931787 documents, 100000 features, 756379027 non-zero entries)

我们看到我们的语料库用稀疏TF-IDF矩阵表示时,包含了3.9M的文档、100K属性(不同的记号——这里就是单词)、0.76G非零输入(译者注:代表总词数)。英文维基百科语料库包括大约22.4亿记号。
现在,我们已经准备好计算英文维基百科的LSA结果了:

>>> # 提取400LSI主题,使用默认的单程算法
>>> lsi = gensim.models.lsimodel.LsiModel(corpus=mm, id2word=id2word, num_topics=400)

>>> # 打印对前10个主题贡献最多的单词(无论积极与消极)
>>> lsi.print_topics(10)
topic #0(332.762): 0.425*"utc" + 0.299*"talk" + 0.293*"page" + 0.226*"article" + 0.224*"delete" + 0.216*"discussion" + 0.205*"deletion" + 0.198*"should" + 0.146*"debate" + 0.132*"be"
topic #1(201.852): 0.282*"link" + 0.209*"he" + 0.145*"com" + 0.139*"his" + -0.137*"page" + -0.118*"delete" + 0.114*"blacklist" + -0.108*"deletion" + -0.105*"discussion" + 0.100*"diff"
topic #2(191.991): -0.565*"link" + -0.241*"com" + -0.238*"blacklist" + -0.202*"diff" + -0.193*"additions" + -0.182*"users" + -0.158*"coibot" + -0.136*"user" + 0.133*"he" + -0.130*"resolves"
topic #3(141.284): -0.476*"image" + -0.255*"copyright" + -0.245*"fair" + -0.225*"use" + -0.173*"album" + -0.163*"cover" + -0.155*"resolution" + -0.141*"licensing" + 0.137*"he" + -0.121*"copies"
topic #4(130.909): 0.264*"population" + 0.246*"age" + 0.243*"median" + 0.213*"income" + 0.195*"census" + -0.189*"he" + 0.184*"households" + 0.175*"were" + 0.167*"females" + 0.166*"males"
topic #5(120.397): 0.304*"diff" + 0.278*"utc" + 0.213*"you" + -0.171*"additions" + 0.165*"talk" + -0.159*"image" + 0.159*"undo" + 0.155*"www" + -0.152*"page" + 0.148*"contribs"
topic #6(115.414): -0.362*"diff" + -0.203*"www" + 0.197*"you" + -0.180*"undo" + -0.180*"kategori" + 0.164*"users" + 0.157*"additions" + -0.150*"contribs" + -0.139*"he" + -0.136*"image"
topic #7(111.440): 0.429*"kategori" + 0.276*"categoria" + 0.251*"category" + 0.207*"kategorija" + 0.198*"kategorie" + -0.188*"diff" + 0.163*"категория" + 0.153*"categoría" + 0.139*"kategoria" + 0.133*"categorie"
topic #8(109.907): 0.385*"album" + 0.224*"song" + 0.209*"chart" + 0.204*"band" + 0.169*"released" + 0.151*"music" + 0.142*"diff" + 0.141*"vocals" + 0.138*"she" + 0.132*"guitar"
topic #9(102.599): -0.237*"league" + -0.214*"he" + -0.180*"season" + -0.174*"football" + -0.166*"team" + 0.159*"station" + -0.137*"played" + -0.131*"cup" + 0.131*"she" + -0.128*"utc"

创建这个LSI模型在我的笔记本上消耗了4小时9分钟[1]。大约16,000个文档每分钟,包括所有的I/O。

注:如果你需要更快得到结果,参见《分布式计算》教程。Gensim中的BLAS库显然利用了多核,以便相同的数据可以在一台多核机器上免费地更快处理,而且不用任何分布式安装。

我们看到总处理时间主要是用于预处理阶段总原始维基百科XML中提取TF-IDF语料库,消耗了9小时。
gensim中使用的算法对于每个输入只需浏览一遍,因此适合用于文档以不重复流的形式输入及存储/迭代多次语料库消费太大的情形。

隐含狄利克雷分配

正如上面的潜在语义分析,首先载入迭代器与字典。

>>> import logging, gensim, bz2
>>> logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)

>>> # load id->word mapping (the dictionary), one of the results of step 2 above
>>> id2word = gensim.corpora.Dictionary.load_from_text('wiki_en_wordids.txt')
>>> # load corpus iterator
>>> mm = gensim.corpora.MmCorpus('wiki_en_tfidf.mm')
>>> # mm = gensim.corpora.MmCorpus(bz2.BZ2File('wiki_en_tfidf.mm.bz2')) # use this if you compressed the TFIDF output

>>> print(mm)
MmCorpus(3931787 documents, 100000 features, 756379027 non-zero entries)

我们将会运行在线LDA(Hoffman等[3]),这个算法可以不断利用新到的文档更新LDA模型。在线LDA可以与批量LDA进行对比,后者是一次处理整个语料库,若要更新只能再次处理整个语料库。不同点是,给定一个相对稳定的文档流(没有太多主题偏移),在线更新只需遍历较小的文档块(子语料库)本身是相当不错的,因此模型收敛更快( The difference is that given a reasonably stationary document stream (not much topic drift), the online updates over the smaller chunks (subcorpora) are pretty good in themselves, so that the model estimation converges faster)。结果是,我们可能只需要处理一遍全部语料库:如果语料库有3M文章,我们在读入10K文章后更新一次,这意味着在一次遍历中我们需要更新300次,很可能得到一个比较准确的主题估计:

>>> # 提取100维LDA主题,使用一次遍历,每1万文档更新一次
>>> lda = gensim.models.ldamodel.LdaModel(corpus=mm, id2word=id2word, num_topics=100, update_every=1, chunksize=10000, passes=1)
using serial LDA version on this node
running online LDA training, 100 topics, 1 passes over the supplied corpus of 3931787 documents, updating model once every 10000 documents
...

不像LSA,来自LDA的主题更容易解释:

>>> # print the most contributing words for 20 randomly selected topics
>>> lda.print_topics(20)
topic #0: 0.009*river + 0.008*lake + 0.006*island + 0.005*mountain + 0.004*area + 0.004*park + 0.004*antarctic + 0.004*south + 0.004*mountains + 0.004*dam
topic #1: 0.026*relay + 0.026*athletics + 0.025*metres + 0.023*freestyle + 0.022*hurdles + 0.020*ret + 0.017*divisão + 0.017*athletes + 0.016*bundesliga + 0.014*medals
topic #2: 0.002*were + 0.002*he + 0.002*court + 0.002*his + 0.002*had + 0.002*law + 0.002*government + 0.002*police + 0.002*patrolling + 0.002*their
topic #3: 0.040*courcelles + 0.035*centimeters + 0.023*mattythewhite + 0.021*wine + 0.019*stamps + 0.018*oko + 0.017*perennial + 0.014*stubs + 0.012*ovate + 0.011*greyish
topic #4: 0.039*al + 0.029*sysop + 0.019*iran + 0.015*pakistan + 0.014*ali + 0.013*arab + 0.010*islamic + 0.010*arabic + 0.010*saudi + 0.010*muhammad
topic #5: 0.020*copyrighted + 0.020*northamerica + 0.014*uncopyrighted + 0.007*rihanna + 0.005*cloudz + 0.005*knowles + 0.004*gaga + 0.004*zombie + 0.004*wigan + 0.003*maccabi
topic #6: 0.061*israel + 0.056*israeli + 0.030*sockpuppet + 0.025*jerusalem + 0.025*tel + 0.023*aviv + 0.022*palestinian + 0.019*ifk + 0.016*palestine + 0.014*hebrew
topic #7: 0.015*melbourne + 0.014*rovers + 0.013*vfl + 0.012*australian + 0.012*wanderers + 0.011*afl + 0.008*dinamo + 0.008*queensland + 0.008*tracklist + 0.008*brisbane
topic #8: 0.011*film + 0.007*her + 0.007*she + 0.004*he + 0.004*series + 0.004*his + 0.004*episode + 0.003*films + 0.003*television + 0.003*best
topic #9: 0.019*wrestling + 0.013*château + 0.013*ligue + 0.012*discus + 0.012*estonian + 0.009*uci + 0.008*hockeyarchives + 0.008*wwe + 0.008*estonia + 0.007*reign
topic #10: 0.078*edits + 0.059*notability + 0.035*archived + 0.025*clearer + 0.022*speedy + 0.021*deleted + 0.016*hook + 0.015*checkuser + 0.014*ron + 0.011*nominator
topic #11: 0.013*admins + 0.009*acid + 0.009*molniya + 0.009*chemical + 0.007*ch + 0.007*chemistry + 0.007*compound + 0.007*anemone + 0.006*mg + 0.006*reaction
topic #12: 0.018*india + 0.013*indian + 0.010*tamil + 0.009*singh + 0.008*film + 0.008*temple + 0.006*kumar + 0.006*hindi + 0.006*delhi + 0.005*bengal
topic #13: 0.047*bwebs + 0.024*malta + 0.020*hobart + 0.019*basa + 0.019*columella + 0.019*huon + 0.018*tasmania + 0.016*popups + 0.014*tasmanian + 0.014*modèle
topic #14: 0.014*jewish + 0.011*rabbi + 0.008*bgwhite + 0.008*lebanese + 0.007*lebanon + 0.006*homs + 0.005*beirut + 0.004*jews + 0.004*hebrew + 0.004*caligari
topic #15: 0.025*german + 0.020*der + 0.017*von + 0.015*und + 0.014*berlin + 0.012*germany + 0.012*die + 0.010*des + 0.008*kategorie + 0.007*cross
topic #16: 0.003*can + 0.003*system + 0.003*power + 0.003*are + 0.003*energy + 0.002*data + 0.002*be + 0.002*used + 0.002*or + 0.002*using
topic #17: 0.049*indonesia + 0.042*indonesian + 0.031*malaysia + 0.024*singapore + 0.022*greek + 0.021*jakarta + 0.016*greece + 0.015*dord + 0.014*athens + 0.011*malaysian
topic #18: 0.031*stakes + 0.029*webs + 0.018*futsal + 0.014*whitish + 0.013*hyun + 0.012*thoroughbred + 0.012*dnf + 0.012*jockey + 0.011*medalists + 0.011*racehorse
topic #19: 0.119*oblast + 0.034*uploaded + 0.034*uploads + 0.033*nordland + 0.025*selsoviet + 0.023*raion + 0.022*krai + 0.018*okrug + 0.015*hålogaland + 0.015*russiae + 0.020*manga + 0.017*dragon + 0.012*theme + 0.011*dvd + 0.011*super + 0.011*hunter + 0.009*ash + 0.009*dream + 0.009*angel

创建该LDA模型在我的笔记本上消耗了6小时20分钟。如果你需要更快些,考虑在计算机集群上使用分布式LDA
请注意LDA和LSA运行中的两个不同点:我要求LSA提取400主题,LDA只要100主题(因此速度的差异实际上可能更大)。第二,gensim中的LSA实现是真正的在线:如果输入流的性质(nature)及时改变,LSA将会重定向以反映这些变化,只用一个相对较小的更新。相反地,LDA不是真正在线(虽然文章[3]的标题如此),因为后来更新对模型的影响逐渐变小。如果输入的文档流有主题偏移,LDA将会感到困惑,并且调整自己以适应新状况的速度会越来越慢。
一句话,多次使用LDA的更新功能时请小心。当事先知道整个语料库并且没有表现出主题偏移时,批量LDA的用法是可以的,并没有影响。
要运行批量LDA(非在线),训练LdaModel

>>> # extract 100 LDA topics, using 20 full passes, no online updates
>>> lda = gensim.models.ldamodel.LdaModel(corpus=mm, id2word=id2word, num_topics=100, update_every=0, passes=20)

像往常一样,一个训练好的模型可以用来将新的文档、没见过的文档(普通词袋计数向量)转化为LDA主题分布:

>>> doc_lda = lda[doc_bow]

[1] (1, 2) 我的笔记本 = MacBook Pro, Intel Core i7 2.3GHz, 16GB DDR3 RAM, OS X with libVec.
[2] 这里我们最关心的是性能问题,但是检索出来LSA概念也十分有趣。我不是维基百科专家,没有深入剖析过它,但是Brian Mingus针对这个结果进行了这样的分析:

你的数据集中呈现出大量噪音。头三个主题可能是关于维基百科的管理和清理的元主题。这些结果的出现时因为你没有排除这些模板,为了控制质量这些模板被包含在大多数文章中:http://en.wikipedia.org/wiki/Wikipedia:Template_messages/Cleanup

第四、五个主题清楚地表明导入的大量关于城市、国家等的人口、经济等方面的统计数据库的影响。

第六个主题表示体育的影响,第七个是音乐。因此,前10概念显然被维基百科的机器人和扩充模板主导,这提示我们LSA是一个非常强力的数据分析工具,但是不是万金油(silver bullet)。通常,也是渣进滓出(garbage in, garbage out)。顺带一提,欢迎完善维基百科标记解析代码。^_^

[3] (1, 2) Hoffman, Blei, Bach. 2010. Online learning for Latent Dirichlet Allocation [pdf] [code]

  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值