gensim教程翻译学习记录(三)

主题与变换(Topics and Transformations)

引入转换并演示它们在语料库中的使用。

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

在此教程中,我将会展示如何将一个文档从一种向量表示转换为另一种表示。此过程实现两个目标:

  1. 要在语料库中找出隐藏的结构,发现单词之间的关系,并用它们以一种新的且更语义的方式来描述文档。
  2. 使文档表示更紧凑。这既提高了效率(新表示消耗更少的资源)和效力(忽略边际数据趋势,减少噪音)。

创建语料库(Creating the Corpus)

首先,我们需要创建一个可以工作的语料库。这一步和先前的教程一样:如果你已经完成了,请直接跳到下一节。

from collections import defaultdict
from gensim import corpora

documents = [
    "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",
]

# remove common words and tokenize
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 documents
]

# remove words that appear only once
frequency = defaultdict(int)
for text in texts:
    for token in text:
        frequency[token] += 1

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

dictionary = corpora.Dictionary(texts)
corpus = [dictionary.doc2bow(text) for text in texts]

创建一个变换

变换时表示的Python对象,通常通过各种训练语料库进行初始化:

from gensim import models

tfidf = models.TfidfModel(corpus) # step 1 -- initialize a model

我们使用我们来自教程1的旧语料库来初始化(训练)这个变换模型。不同的变换可能需要不同的初始化参数;在Tfidf变换中,训练过程仅包含一次浏览所提供的语料库以及计算其所有特征的文档频率。训练其他的模型,如 Latent Semantic Analysis或Latent Dirichlet Allocation,则涉及更多,因此需要更多时间。

注意:变换始终在两个特定的向量空间之间转换。同样的向量空间(=同样的特征id集)必须用于训练以及后续的向量变换。不能使用同样的输入特征空间,如应用不同的字符串处理、使用不同的特征id、或者在应该使用TfIdf向量时却使用了词袋输入向量。这会导致在调用变化时特征不匹配,进而导致无意义的输出和/或运行时间异常。

变换向量

从现在起,tfidf是一个只读对象,它能够用于将旧表示向量(词袋整数计数)转换为新的表示(TfIdf实值权重):

doc_bow = [(0, 1), (1, 1)]
print(tfidf[doc_bow]) # step 2 -- use the model to transform vectors

结果为:

[(0, 0.7071067811865476), (1, 0.7071067811865476)]

或者在整个语料库上应用一个变换:

corpus_tfidf = tfidf[corpus]
for doc in corpus_tfidf:
    print(doc)

结果为:

[(0, 0.5773502691896257), (1, 0.5773502691896257), (2, 0.5773502691896257)]
[(0, 0.44424552527467476), (3, 0.44424552527467476), (4, 0.44424552527467476), (5, 0.3244870206138555), (6, 0.44424552527467476), (7, 0.3244870206138555)]
[(2, 0.5710059809418182), (5, 0.4170757362022777), (7, 0.4170757362022777), (8, 0.5710059809418182)]
[(1, 0.49182558987264147), (5, 0.7184811607083769), (8, 0.49182558987264147)]
[(3, 0.6282580468670046), (6, 0.6282580468670046), (7, 0.45889394536615247)]
[(9, 1.0)]
[(9, 0.7071067811865475), (10, 0.7071067811865475)]
[(9, 0.5080429008916749), (10, 0.5080429008916749), (11, 0.695546419520037)]
[(4, 0.6282580468670046), (10, 0.45889394536615247), (11, 0.6282580468670046)]

在这个特殊的情况下,我们正在变化我们用于训练的相同语料库,但这只是偶然的。一旦变换模型被初始化,它能够被用于任意的向量(当然,假设它们来自同样的向量空间),即使它们根本不用于训练语料库。这是通过一个称为LSA 折叠(olding-in for LSA)、LDA主题推理(topic inference for LDA)等过程实现的。

注意:调用模型model[corpus]只围绕旧corpus文档流创建一个包装——实际转换是在文档迭代期间完成的。我们不能够在调用corpus_transformed = model[corpus]时转换整个语料库,因为那会将讲过存储在主内存中,这也与gensim的内存独立( memory-indepedence)意愿相违背。如果您将多次迭代变换的corpus_transformed,且转换成本高昂,请将生成的语料库序列化到磁盘再继续使用。

变换也可以在某种一个接一个的链型中进行序列化:

lsi_model = models.LsiModel(corpus_tfidf, id2word=dictionary, num_topics=2)  # initialize an LSI transformation
corpus_lsi = lsi_model[corpus_tfidf] # create a double wrapper over the original corpus: bow->tfidf->fold-in-lsi

这里我们通过Latent Semantic Indexing将Tf-Idf语料库变换到2维空间(2维是因为我们设置num_topics=2)。现在,你可能在疑惑“这两个潜在的表示代表什么?让我们通过models.LsiModel.print_topics()来检查一下:

lsi_model.print_topics(2)

结果为:

[(0,
  '0.703*"trees" + 0.538*"graph" + 0.402*"minors" + 0.187*"survey" + 0.061*"system" + 0.060*"time" + 0.060*"response" + 0.058*"user" + 0.049*"computer" + 0.035*"interface"'),
 (1,
  '-0.460*"system" + -0.373*"user" + -0.332*"eps" + -0.328*"interface" + -0.320*"time" + -0.320*"response" + -0.293*"computer" + -0.280*"human" + -0.171*"survey" + 0.161*"trees"')]

主题打印到了日志——请参阅此页面顶部有关激活日志的注释。

看起来,根据LSI,“tree”、“graph”和“minors”是相关单词(在第一个主题方面贡献最大),而第二个主题实际上与所有其他词都有关。不出所料,前五个文档与第二个主题的关系更为密切,其余四个文档与第一个主题有关:

# both bow->tfidf and tfidf->lsi transformations are actually executed here, on the fly
for doc, as_text in zip(corpus_lsi, documents):
    print(doc, as_text)

结果为

[(0, 0.06600783396090514), (1, -0.5200703306361844)] Human machine interface for lab abc computer applications
[(0, 0.19667592859142746), (1, -0.7609563167700034)] A survey of user opinion of computer system response time
[(0, 0.08992639972446688), (1, -0.7241860626752502)] The EPS user interface management system
[(0, 0.07585847652178383), (1, -0.6320551586003426)] System and human system engineering testing of EPS
[(0, 0.10150299184980341), (1, -0.5737308483002946)] Relation of user perceived response time to error measurement
[(0, 0.7032108939378303), (1, 0.16115180214026076)] The generation of random binary unordered trees
[(0, 0.877478767311982), (1, 0.1675890686465975)] The intersection graph of paths in trees
[(0, 0.9098624686818566), (1, 0.14086553628719362)] Graph minors IV Widths of trees and well quasi ordering
[(0, 0.6165825350569276), (1, -0.053929075663891414)] Graph minors A survey

通过save()和load()函数实现模型持久性:

import os
import tempfile

with tempfile.NamedTemporaryFile(prefix='model-', suffix='.lsi', delete=False) as tmp:
    lsi_model.save(tmp.name)  # same for tfidf, lda, ...

loaded_lsi_model = models.LsiModel.load(tmp.name)

os.unlink(tmp.name)

下一个问题可能是:这些文档彼此间到底有多相似?有方法来形式化相似性,使得给定一个输入文档,我们可以根据文档间的相似性对其他文档进行排序?相似性查询包含在下一个教程中(相似性查询(Similarity Queries))。

可用的变换(Available transformations)

Gensim实现了几个就行的向量空间模型算法:

Term Frequency * Inverse Document Frequency,Tf-Idf在初始化时期望一个词袋(整型)训练语料库。在变换中,它将一个向量作为输入,并输出另一个相同维度的向量。新向量中在训练语料库中的稀有特征将具有较大的权重。因此,Tf-Idf将整型向量转换为实值向量,且向量维度不发生任何变化。它还可以选择性地将生成的向量正则化到(欧几里德)单位长度。

model = models.TfidfModel(corpus, normalize=True)

Latent Semantic Indexing,LSI(有时也称LSA)将文档从词袋空间或(最好地)TfIdf加权空间变换为低维的潜在空间。在上面的示范的语料库中,我们仅使用2个潜在维度,但在实际语料库中,建议将200-500的目标维度作为“黄金准则”。

model = models.LsiModel(corpus, id2word=dictionary, num_topics=300)

LSI训练的独特之处在于,只要提供更多的训练文档,我们就可以随时继续"训练"。这是通过对基础模型的增量更新完成的,这个过程称为在线训练。由于LSI的性质,输入文档流甚至可能是无限的——以只读模式使用已计算的转换模型的同时,可以继续给LSI“喂养”获得的新文档!

model.add_documents(another_tfidf_corpus) # now LSI has been trained on tfidf_corpus + another_tfidf_corpus
lsi_vec = model[tfidf_vec] # convert some new document into the LSI space, without affecting the model

model.add_documents(more_documents) # tfidf_corpus + another_tfidf_corpus + more_documents
lsi_vec = model[tfidf_vec]

具体详情请查阅gensim.models.lsimodel文档以获得如何使LSI逐渐在无限流中“忘记”旧观测。如果您想使模型更加复杂,这里也有参数可以调整以影响速度、内存足迹以及LSI算法的数值精度。

gensim使用一种新的在线增量流分布式训练算法^{[1]}。gensim内部还执行Halko等人提出的随机多通算法^{[2]},以加快计算的核心部分。通过在计算机群中的分布计算来进一步加速,请请参阅 Experiments on the English Wikipedia

Latent Dirichlet Allocation,LDA时另一个从词袋计数到低维主题空间的变换。LDA是LSA的概率扩展(也称为多项PCA)。因此,LDA的主题可以被解释为单词间的概率分布。这些分布也和LSA一样,都是自动地在训练语料库推导。文档又被解释为这些主题的(软)混合(同样,就像LSA一样)。

model = model.LdaModel(corpus, id2word=dictionary, num_topics=100)

gensim基于^{[3]}快速地实现了一个LDA参数估计,其修改为在计算机集群的分布式模式下运行。

Hierarchical Dirichlet Process,HDP是一个非参数贝叶斯方法(注意缺失的需要主题的数量):

model = models.HdpModel(corpus, id2word=dictionary)

gensim基于^{[4]}使用了一个快速的,在线实现方式。HDP模型是gensim的新补充,在学术边界仍比较粗糙——谨慎使用。

添加新的VSM变换(如不同的加权方案)是相当细碎的;有关更多信息和示例,请参阅API Reference或直接查看Python Code

值得重申的是,这些都是独特的增量实施,不需要整个训练语料库同时出现在主内存中。在内存处理方面,我现在也在改进分布式计算(Distribution Computing),以提高CPU效率。如果您觉得可以通过测试、提供用例或代码做出贡献,请参Gensim Developer guide

下一步(What Next?)

继续下一个相似性查询(Similarity Queries)教程。

参考(References)

[1] Řehůřek. 2011. Subspace tracking for Latent Semantic Analysis.
[2] Halko, Martinsson, Tropp. 2009. Finding structure with randomness.
[3] Hoffman, Blei, Bach. 2010. Online learning for Latent Dirichlet Allocation.
[4] Wang, Paisley, Blei. 2011. Online variational inference for the hierarchical Dirichlet process.

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容