原文链接
介绍了基本概念,以及理解和使用gensim的基本元素,并提供了一个简单的例子。
从宏观来看,gensim提供了一个发现文档语义结构的工具,通过检查词出现的频率。gensim读取一段语料,输出一个向量,表示文档中的一个词。词向量可以用来训练各种分类器模型。这三个模型是理解gensim的核心概念,所以接下来依次介绍。同时,会以一个简单例子贯穿讲述。
语料是指一组电子文档的集合。这个集合是gensim的输入,gensim会从这个语料中推断出它的结构,主题等。从语料中推断出的隐含结构,可以用来对一个新的文档指定一个主题。我们也把这个集合叫做训练语料。这个训练过程不需要人工参与,所以主题分类是无监督的。
在我们的例子中,语料是一个包含9个字符串的数组,每个字符串包含一个句子。
raw_corpus = ["Human machine interface for lab abc computer applications",
"A survey of user opinion of computer system response time",
"The EPS user interface management system",
"System and human system engineering testing of EPS",
"Relation of user perceived response time to error measurement",
"The generation of random binary unordered trees",
"The intersection graph of paths in trees",
"Graph minors IV Widths of trees and well quasi ordering",
"Graph minors A survey"]
这个语料特别的小,当然是为了举例子用。其他语料可以是所有莎士比亚写的文章,或者所有维基百科的文章,或者一个特定的人发的推文。
收集语料之后,需要做一些预处理。我们这里的预处理比较简单,移除了一些英文虚词以及在预料中仅仅出现一次的词。这个处理过程,我们需要对数据进行单词切分。把文档分成一个个单词。(这里以空格作为分隔符)
stoplist = set('for a of the and to in'.split(' '))
texts = [[word for word in document.lower().split() if word not in stoplist]
for document in raw_corpus]
from collections import defaultdict
frequency = defaultdict(int)
for text in texts:
for token in text:
frequency[token] += 1
precessed_corpus = [[token for token in text if frequency[token] > 1] for text in texts]
precessed_corpus
[['human', 'interface', 'computer'],
['survey', 'user', 'computer', 'system', 'response', 'time'],
['eps', 'user', 'interface', 'system'],
['system', 'human', 'system', 'eps'],
['user', 'response', 'time'],
['trees'],
['graph', 'trees'],
['graph', 'minors', 'trees'],
['graph', 'minors', 'survey']]
在正式处理之前,我们想对语料中的每一个单词关联一个唯一的ID。这可以用gensim.corpora.Dictionary来实现。这个字典定义了我们要处理的所有单词表。
from gensim import corpora
dictionary = corpora.Dictionary(precessed_corpus)
print(dictionary)
Dictionary(12 unique tokens: [u'minors', u'graph', u'system', u'trees', u'eps']...)
由于我们的语料不大,在我们定义的字典中,只有12个不同的单词。对于更大一些的语料,包含上百万个单词的字典是很常见的。
如果要对文档的隐含结构进行推断,就需要一种数学上能处理的文档表示方法。一种方法是把每个文档表达为一个向量。有很多种表示方法,一种常见的方法是bag-of-words模型。在词袋模型中,每篇文档表示被表示成一个向量,代表字典中每个词出现的次数。例如,给定一个包含[‘coffee’,’milk’,’sugar’,’spoon’]的字典,一个包含[‘coffee milk coffee’]字符串的文档可以表示成向量[2,1,0,0]。向量各个元素的位置和字典的顺序相对应,向量的长度就是字典的长度。词袋模型的一个重要特点是,它完全忽略的单词在句子中出现的顺序,这也就是“词袋”这个名字的由来。
print dictionary.token2id
{u'minors': 11, u'graph': 10, u'system': 6, u'trees': 9, u'eps': 8, u'computer': 1, u'survey': 5, u'user': 7, u'human': 2, u'time': 4, u'interface': 0, u'response': 3}
例如,我们要对“human computer interaction”向量化(注意到这句话并没有出现在原始的预料中)。我们可以使用字典中的doc2bow方法来获得词袋向量表达:
new_doc = "human computer interaction"
new_vec = dictionary.doc2bow(new_doc.lower().split())
print new_vec
[(1, 1), (2, 1)]
列表中每个元组中,第一个元素表示字典中单词的ID,第二个表示在这个句子中这个单词出现的次数。
注意到,“interaction”并没有出现在原始语料中,而且,这个向量长度为句子中出现过字典中单词的数量。由于任何给定的句子只会包含字典中一个单词,其他没有包含的隐含地表示为0。
我们可以把原始的语料转化为一组向量:
bow_corpus = [dictionary.doc2bow(text) for text in precessed_corpus]
bow_corpus
[[(0, 1), (1, 1), (2, 1)],
[(1, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1)],
[(0, 1), (6, 1), (7, 1), (8, 1)],
[(2, 1), (6, 2), (8, 1)],
[(3, 1), (4, 1), (7, 1)],
[(9, 1)],
[(9, 1), (10, 1)],
[(9, 1), (10, 1), (11, 1)],
[(5, 1), (10, 1), (11, 1)]]
这个列表是完全存在内存中的,但在大多数应用中,你会看到一种更加灵活的处理方法。幸运地是,gensim提供了迭代器(python的福利)可以每次返回一个文档向量。想了解更多细节,可以查看文档手册。
现在我们已经向量化了语料,接下来可以使用各种模型了。我们所说的模型,指的是把文档转化成另一个。在gensim中,文档用向量来表示,所以模型可以认为是在两个向量空间进行转换。这个转换是从语料训练集中学习出来的。
各种模型中比较简单的一个叫tf-idf。tf-idf模型把词袋表达的向量转换到另一个向量空间,这个向量空间中,词频是根据语料中每个词的相对稀有程度(relative rarity)进行加权处理的。
看一个简单的例子。首先初始化一个tf-idf,在我们的语料中进行训练,然后对“system minors”进行处理。
from gensim import models
tfidf = models.TfidfModel(bow_corpus)
string = "system minors"
string_bow = dictionary.doc2bow(string.lower().split())
string_tfidf = tfidf[string_bow]
print string_bow
print string_tfidf
[(6, 1), (11, 1)]
[(6, 0.5898341626740045), (11, 0.8075244024440723)]
tfidf返回了一组元组。元组中第一个元素表示ID,第二个表示tf-idf权重。注意到,“system”在原语料中出现4次,“minors”出现2次,所以第一个权重比第二个小。