Gensim官方教程翻译(二)——语料库与向量空间(Corpora and Vector Spaces)

译文出处:https://blog.csdn.net/questionfish/article/details/46739207

官方教程原文:http://radimrehurek.com/gensim/tut2.html

仅作个人学习资料备份,侵删

====================正==========文====================

如果你想记录日志,请不要忘记设置:

 
  1. >>> import logging

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

从字符串到向量

这次,让我们从用字符串表示的文档开始

 
  1. >>> from gensim import corpora, models, similarities

  2. >>>

  3. >>> documents = ["Human machine interface for lab abc computer applications",

  4. >>> "A survey of user opinion of computer system response time",

  5. >>> "The EPS user interface management system",

  6. >>> "System and human system engineering testing of EPS",

  7. >>> "Relation of user perceived response time to error measurement",

  8. >>> "The generation of random binary unordered trees",

  9. >>> "The intersection graph of paths in trees",

  10. >>> "Graph minors IV Widths of trees and well quasi ordering",

  11. >>> "Graph minors A survey"]

这是一个由9篇文档组成的微型语料库,每个文档仅有一个句子组成。

(记号化 or tokenize)

首先,让我们对这些文档进行记号化(tokenize,或称标记化等)处理,屏蔽常用词(利用停用词表)和整个语料库中仅仅出现一次的词:

 
  1. >>> # 去除停用词并分词

  2. >>> # 译者注:这里只是例子,实际上还有其他停用词

  3. >>> # 处理中文时,请借助 Py结巴分词 https://github.com/fxsjy/jieba

  4. >>> stoplist = set('for a of the and to in'.split())

  5. >>> texts = [[word for word in document.lower().split() if word not in stoplist]

  6. >>> for document in documents]

  7. >>>

  8. >>> # 去除仅出现一次的单词

  9. >>> from collections import defaultdict

  10. >>> frequency = defaultdict(int)

  11. >>> for text in texts:

  12. >>> for token in text:

  13. >>> frequency[token] += 1

  14. >>>

  15. >>> texts = [[token for token in text if frequency[token] > 1]

  16. >>> for text in texts]

  17. >>>

  18. >>> from pprint import pprint # pretty-printer

  19. >>> pprint(texts)

  20. [['human', 'interface', 'computer'],

  21. ['survey', 'user', 'computer', 'system', 'response', 'time'],

  22. ['eps', 'user', 'interface', 'system'],

  23. ['system', 'human', 'system', 'eps'],

  24. ['user', 'response', 'time'],

  25. ['trees'],

  26. ['graph', 'trees'],

  27. ['graph', 'minors', 'trees'],

  28. ['graph', 'minors', 'survey']]

在这里我仅利用空格切分字符串来记号化,并将它们都转成小写;而你处理文档的方式很可能会有所不同。实际上,我是在用这个特殊(简单且效率低)的设置来模仿Deerwester等人的原始LSA文章中的实验。[1]

(总结属性字典)

处理文档的方法应该视应用情形、语言而定,因此我决定不对处理方法做任何的限制。取而代之的是,一个文档必须由其中提取出来的属性表示,而不仅仅是其字面形式;如何提取这些属性由你来决定(可以是单词、文档长度数量等)。接下来,我将描述一个通用的、常规目的的方法(称为词袋),但是请记住不同应用领域应使用不同的属性。如若不然,渣进滓出(garbage in, garbage out)。
为了将文档转换为向量,我们将会用一种称为词袋的文档表示方法。这种表示方法,每个文档由一个向量表示,该向量的每个元素都代表这样一个问-答对:

“‘系统’这个单词出现了多少次?1次。”

我们最好用这些问题的(整数)编号来代替这些问题。问题与编号之间的映射,我们称其为字典(Dictionary)。

 
  1. >>> dictionary = corpora.Dictionary(texts)

  2. >>> dictionary.save('/tmp/deerwester.dict') # 把字典保存起来,方便以后使用

  3. >>> print(dictionary)

  4. Dictionary(12 unique tokens)

上面这些步骤,我们利用gensim.corpora.dictionary.Dictionary类为每个出现在语料库中的单词分配了一个独一无二的整数编号。这个操作收集了单词计数及其他相关的统计信息。在结尾,我们看到语料库中有12个不同的单词,这表明每个文档将会用12个数字表示(即12维向量)。如果想要查看单词与编号之间的映射关系:

 
  1. >>> print(dictionary.token2id)

  2. {'minors': 11, 'graph': 10, 'system': 5, 'trees': 9, 'eps': 8, 'computer': 0,

  3. 'survey': 4, 'user': 7, 'human': 1, 'time': 6, 'interface': 2, 'response': 3}

(产生稀疏文档向量)

为了真正将记号化的文档转换为向量,需要:

 
  1. >>> new_doc = "Human computer interaction"

  2. >>> new_vec = dictionary.doc2bow(new_doc.lower().split())

  3. >>> print(new_vec) # "interaction"没有在dictionary中出现,因此忽略

  4. [(0, 1), (1, 1)]

函数doc2bow()简单地对每个不同单词的出现次数进行了计数,并将单词转换为其编号,然后以稀疏向量的形式返回结果。因此,稀疏向量[(0, 1), (1, 1)]表示:在“Human computer interaction”中“computer”(id 0) 和“human”(id 1)各出现一次;其他10个dictionary中的单词没有出现过(隐含的)。

 
  1. >>> corpus = [dictionary.doc2bow(text) for text in texts]

  2. >>> corpora.MmCorpus.serialize('/tmp/deerwester.mm', corpus) # 存入硬盘,以备后需

  3. >>> print(corpus)

  4. [(0, 1), (1, 1), (2, 1)]

  5. [(0, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1)]

  6. [(2, 1), (5, 1), (7, 1), (8, 1)]

  7. [(1, 1), (5, 2), (8, 1)]

  8. [(3, 1), (6, 1), (7, 1)]

  9. [(9, 1)]

  10. [(9, 1), (10, 1)]

  11. [(9, 1), (10, 1), (11, 1)]

  12. [(4, 1), (10, 1), (11, 1)]

(通过上面的操作,我们看到了这次我们得到的语料库。)到现在为止,我们应该明确,上面的输出表明:对于前六个文档来说,编号为10的属性值为0表示问题“文档中‘graph’出现了几次”的答案是“0”;而其他文档的答案是1。事实上,我们得到了《快速入门》中的示例语料库。

语料库流——一次一个文档

需要注意的是,上面的语料库整个作为一个Python List存在了内存中。在这个简单的例子中,这当然无关紧要。但是我们因该清楚,假设我们有一个百万数量级文档的语料库,我们不可能将整个语料库全部存入内存。假设这些文档存在一个硬盘上的文件中,每行一篇文档。Gemsim仅要求一个语料库可以每次返回一个文档向量:

 
  1. >>> class MyCorpus(object):

  2. >>> def __iter__(self):

  3. >>> for line in open('mycorpus.txt'):

  4. >>> # assume there's one document per line, tokens separated by whitespace

  5. >>> yield dictionary.doc2bow(line.lower().split())

请在这里下载示例文件mycorpus.txt

这里假设的在一个单独的文件中每个文档占一行不是十分重要;你可以改造 __iter__ 函数来适应你的输入格式,无论你的输入格式是什么样的,例如遍历文件夹、解析XML、访问网络等等。你仅需在每个文档中解析出一个由记号(tokens)组成的干净列表,然后利用dictionary将这些符号转换为其id,最后在__iter__函数中产生一个稀疏向量即可。

 
  1. >>> corpus_memory_friendly = MyCorpus() # 没有将整个语料库载入内存

  2. >>> print(corpus_memory_friendly)

  3. <__main__.MyCorpus object at 0x10d5690>

现在的语料库是一个对象。我们没有定义任何打印它的方法,所以仅能打印该对象在内存中的地址,对我们没什么帮助。为了查看向量的组成,让我们通过迭代的方式取出语料库中的每个文档向量(一次一个)并打印:

 
  1. >>> for vector in corpus_memory_friendly: # 一次读入内存一个向量

  2. ... print(vector)

  3. [(0, 1), (1, 1), (2, 1)]

  4. [(0, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1)]

  5. [(2, 1), (5, 1), (7, 1), (8, 1)]

  6. [(1, 1), (5, 2), (8, 1)]

  7. [(3, 1), (6, 1), (7, 1)]

  8. [(9, 1)]

  9. [(9, 1), (10, 1)]

  10. [(9, 1), (10, 1), (11, 1)]

  11. [(4, 1), (10, 1), (11, 1)]

虽然输出与普通的Python List一样,但是现在的语料库对内存更加友好,因为一次最多只有一个向量寄存于内存中。你的语料库现在可以想多大就多大啦!
相似的,为了构造dictionary我们也不必将全部文档读入内存:

 
  1. >>> # 收集所有符号的统计信息

  2. >>> dictionary = corpora.Dictionary(line.lower().split() for line in open('mycorpus.txt'))

  3. >>> # 收集停用词和仅出现一次的词的id

  4. >>> stop_ids = [dictionary.token2id[stopword] for stopword in stoplist

  5. >>> if stopword in dictionary.token2id]

  6. >>> once_ids = [tokenid for tokenid, docfreq in dictionary.dfs.iteritems() if docfreq == 1]

  7. >>> dictionary.filter_tokens(stop_ids + once_ids) # 删除停用词和仅出现一次的词

  8. >>> dictionary.compactify() # 消除id序列在删除词后产生的不连续的缺口

  9. >>> print(dictionary)

  10. Dictionary(12 unique tokens)

这就是你需要为他准备的所有,至少从词袋模型的角度考虑是这样的。当然,我们用该语料库做什么事另外一个问题,我们并不清楚计算不同单词的词频是否真的有用。事实证明,它确实也没有什么用,我们将需要首先对这种简单的表示方法进行一个转换,才能计算出一些有意义的文档及文档相似性。
转换的内容将会在下个教程讲解,在这之前,让我们暂时将注意力集中到语料库持久上来。
 

各种语料库格式

(存储语料库)

我们有几种文件格式来序列化一个向量空间语料库(~向量序列),并存到硬盘上。Gemsim通过之前提到的语料库流接口实现了这些方法,用一个惰性方式来将文档从硬盘中读出(或写入)。一次一个文档,不会将整个语料库读入主内存。
所有的语料库格式中,一种非常出名的文件格式就是 Market Matrix格式。想要将语料库保存为这种格式:

 
  1. >>> from gensim import corpora

  2. >>> # 创建一个玩具级的语料库

  3. >>> corpus = [[(1, 0.5)], []] # 让一个文档为空,作为它的heck

  4. >>>

  5. >>> corpora.MmCorpus.serialize('/tmp/corpus.mm', corpus)

其他格式还有Joachim’s SVMlightBlei’s LDA-CGibbsLDA++等:

 
  1. >>> corpora.SvmLightCorpus.serialize('/tmp/corpus.svmlight', corpus)

  2. >>> corpora.BleiCorpus.serialize('/tmp/corpus.lda-c', corpus)

  3. >>> corpora.LowCorpus.serialize('/tmp/corpus.low', corpus)

(载入语料库)

相反地,从一个Matrix Market文件载入语料库:

>>> corpus = corpora.MmCorpus('/tmp/corpus.mm')

语料库对象是流式的,因此你不能直接将其打印出来

 
  1. >>> print(corpus)

  2. MmCorpus(2 documents, 2 features, 1 non-zero entries)

如果你真的特别想看看语料库的内容,也不是没有办法:

 
  1. >>> # 将语料库全部导入内存的方法

  2. >>> print(list(corpus)) # 调用list()将会把所有的序列转换为普通Python List

  3. [[(1, 0.5)], []]

或者

 
  1. >>> # 另一种利用流接口,一次只打印一个文档

  2. >>> for doc in corpus:

  3. ... print(doc)

  4. [(1, 0.5)]

  5. []

第二种方法显然更加内存友好,但是如果只是为了测试与开发,没有什么比调用list()更简单了。(*^_^*)

(转存语料库)

想将这个 Matrix Market格式的语料库存为Blei’s LDA-C格式,你只需:

>>> corpora.BleiCorpus.serialize('/tmp/corpus.lda-c', corpus)

这种方式,gensim可以被用作一个内存节约型的I/O格式转换器:你只要用一种文件格式流载入语料库,然后直接保存成其他格式就好了。增加一种新的格式简直是太容易了,请参照我们为SVMlight语料库设计的代码

与NumPy和SciPy的兼容性

Gensim包含了许多高效的工具函数来帮你实现语料库与numpy矩阵之间互相转换:

 
  1. >>> corpus = gensim.matutils.Dense2Corpus(numpy_matrix)

  2. >>> numpy_matrix = gensim.matutils.corpus2dense(corpus, num_terms=number_of_corpus_features)

以及语料库与scipy稀疏矩阵之间的转换:

 
  1. >>> corpus = gensim.matutils.Sparse2Corpus(scipy_sparse_matrix)

  2. >>> scipy_csc_matrix = gensim.matutils.corpus2csc(corpus)

想要更全面的参考(例如,压缩词典的大小、优化语料库与NumPy/SciPy数组的转换),参见API文档,或者继续阅读下一篇《主题与转换》教程。

==================================================

[1]This is the same corpus as used inDeerwester et al. (1990): Indexing by Latent Semantic Analysis, Table 2.

猜你喜欢

转载自blog.csdn.net/yocencyy/article/details/89736723
今日推荐