Skip to content

主题和转换

别忘了设置

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

如果你想看到记录事件。

转换接口

在上一篇关于Corpora和Vector Spaces的教程中,我们创建了一个文档语料库,表示为向量流。要继续,让我们启动gensim并使用该语料库:

>>> from gensim import corpora, models, similarities
>>> if (os.path.exists("/tmp/deerwester.dict")):
>>>    dictionary = corpora.Dictionary.load('/tmp/deerwester.dict')
>>>    corpus = corpora.MmCorpus('/tmp/deerwester.mm')
>>>    print("Used files generated from first tutorial")
>>> else:
>>>    print("Please run first tutorial to generate data set")

MmCorpus(9个文件,12个特征,28个非零项)

在本教程中,我将展示如何将文档从一个矢量表示转换为另一个矢量表示。这个过程有两个目标:

  1. 为了在语料库中显示隐藏的结构,发现单词之间的关系并使用它们以新的(希望)更加语义的方式描述文档。
  2. 使文档表示更紧凑。这既提高了效率(新表示消耗更少的资源)和功效(边际数据趋势被忽略,降噪)。

创建转换

转换是标准的Python对象,通常通过训练语料库进行初始化:

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

我们使用教程1中的旧语料库初始化(训练)转换模型。不同的转换可能需要不同的初始化参数; 在TfIdf的情况下,“训练”仅包括通过提供的语料库一次并计算其所有特征的文档频率。训练其他模型,例如潜在语义分析或潜在Dirichlet分配,涉及更多,因此需要更多时间。

注意

转换总是在两个特定的向量空间之间转换。必须使用相同的向量空间(=同一组特征id)进行训练以及后续的向量转换。无法使用相同的输入要素空间,例如应用不同的字符串预处理,使用不同的特征ID,或使用预期为TfIdf向量的词袋输入向量,将导致转换调用期间的特征不匹配,从而导致垃圾中的任何一个输出和/或运行时异常。

变换向量

从现在开始,tfidf 被视为一个只读对象,可用于将任何向量从旧表示(bag-of-words整数计数)转换为新表示(TfIdf实值权重):

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

或者将转换应用于整个语料库:

>>> corpus_tfidf = tfidf[corpus]
>>> for doc in corpus_tfidf:
...     print(doc)
[(0, 0.57735026918962573), (1, 0.57735026918962573), (2, 0.57735026918962573)]
[(0, 0.44424552527467476), (3, 0.44424552527467476), (4, 0.44424552527467476), (5, 0.32448702061385548), (6, 0.44424552527467476), (7, 0.32448702061385548)]
[(2, 0.5710059809418182), (5, 0.41707573620227772), (7, 0.41707573620227772), (8, 0.5710059809418182)]
[(1, 0.49182558987264147), (5, 0.71848116070837686), (8, 0.49182558987264147)]
[(3, 0.62825804686700459), (6, 0.62825804686700459), (7, 0.45889394536615247)]
[(9, 1.0)]
[(9, 0.70710678118654746), (10, 0.70710678118654746)]
[(9, 0.50804290089167492), (10, 0.50804290089167492), (11, 0.69554641952003704)]
[(4, 0.62825804686700459), (10, 0.45889394536615247), (11, 0.62825804686700459)]

在这种特殊情况下,我们正在改变我们用于训练的同一语料库,但这只是偶然的。一旦初始化了转换模型,它就可以用在任何向量上(当然它们来自相同的向量空间),即使它们根本没有用在训练语料库中。这是通过LSA的折叠过程,LDA的主题推断等来实现的。

注意 调用model[corpus]仅在旧corpus 文档流周围创建一个包装器- 实际转换在文档迭代期间即时完成。我们无法在调用 corpus_transformed = model[corpus] 时转换整个语料库,因为这意味着将结果存储在主存中,这与gensim的内存独立目标相矛盾。如果您将多次迭代转换,并且转换成本很高,请先将生成的语料库序列化为磁盘并继续使用它。

转换也可以序列化,一个在另一个之上,在一个链中:

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

在这里,我们通过潜在语义索引将我们的Tf-Idf语料库 转换为潜在的2-D空间(因为我们设置了2-D num_topics=2)。现在你可能想知道:这两个潜在的维度代表什么?让我们检查一下models.LsiModel.print_topics()

>>> lsi.print_topics(2)
topic #0(1.594): -0.703*"trees" + -0.538*"graph" + -0.402*"minors" + -0.187*"survey" + -0.061*"system" + -0.060*"response" + -0.060*"time" + -0.058*"user" + -0.049*"computer" + -0.035*"interface"
topic #1(1.476): -0.460*"system" + -0.373*"user" + -0.332*"eps" + -0.328*"interface" + -0.320*"response" + -0.320*"time" + -0.293*"computer" + -0.280*"human" + -0.171*"survey" + 0.161*"trees"

(主题打印到日志 - 请参阅本页顶部有关激活日志记录的说明)

根据LSI的说法,“树”,“图”和“未成年人”都是相关词(并且对第一个主题的方向贡献最大),而第二个主题实际上与所有其他词有关。正如所料,前五个文件与第二个主题的关联性更强,而剩下的四个文件与第一个主题相关:

>>> for doc in corpus_lsi: # both bow->tfidf and tfidf->lsi transformations are actually executed here, on the fly
...     print(doc)
[(0, -0.066), (1, 0.520)] # "Human machine interface for lab abc computer applications"
[(0, -0.197), (1, 0.761)] # "A survey of user opinion of computer system response time"
[(0, -0.090), (1, 0.724)] # "The EPS user interface management system"
[(0, -0.076), (1, 0.632)] # "System and human system engineering testing of EPS"
[(0, -0.102), (1, 0.574)] # "Relation of user perceived response time to error measurement"
[(0, -0.703), (1, -0.161)] # "The generation of random binary unordered trees"
[(0, -0.877), (1, -0.168)] # "The intersection graph of paths in trees"
[(0, -0.910), (1, -0.141)] # "Graph minors IV Widths of trees and well quasi ordering"
[(0, -0.617), (1, 0.054)] # "Graph minors A survey"

使用save()load()函数实现模型持久性:

>>> lsi.save('/tmp/model.lsi') # same for tfidf, lda, ...
>>> lsi = models.LsiModel.load('/tmp/model.lsi')

接下来的问题可能是:这些文件之间的相似程度如何?有没有办法形式化相似性,以便对于给定的输入文档,我们可以根据它们的相似性订购一些其他文档?下一个教程将介绍相似性查询。

可用的转换

gensim实现了几种流行的矢量空间模型算法:

  • 术语频率*反向文档频率,Tf-Idf 期望初始化期间的词袋(整数值)训练语料库。在变换期间,它将采用向量并返回具有相同维度的另一个向量,除了在训练语料库中罕见的特征将增加其值。因此,它将整数值向量转换为实值向量,同时保持维度的数量不变。它还可以任选地将得到的矢量归一化为(欧几里得)单位长度。

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

  • 潜在语义索引,LSI(或有时LSA) 将文档从单词袋或(优选地)TfIdf加权空间转换为较低维度的潜在空间。对于上面的玩具语料库,我们只使用了2个潜在维度,但在实际语料库中,建议将200-500的目标维度作为“黄金标准” [1]

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

    LSI培训的独特之处在于我们可以随时继续“培训”,只需提供更多培训文件即可。这是通过在称为在线培训的过程中对底层模型的增量更新来完成的。由于这个特性,输入文档流甚至可能是无限的 - 只需在LSI新文档到达时继续提供它们,同时使用计算的转换模型作为只读!

    ```py

    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使用了一种新颖的在线增量流分布式训练算法(相当满口!),我在[5]中发表过。gensim还执行Halko等人的随机多遍算法。[4]内部,加速核心部分的计算。另请参阅英语维基百科上的实验,以便通过在计算机集群中分配计算来进一步提高速度。

  • 随机投影,RP旨在减少向量空间维度。这是一种非常有效的(内存和CPU友好的)方法,通过投入一点随机性来近似文档之间的TfIdf距离。建议的目标维度再次为数百/数千,具体取决于您的数据集。

    >>> model = models.RpModel(tfidf_corpus, num_topics=500)

  • Latent Dirichlet Allocation,LDA 是另一种从词袋计数转变为低维度主题空间的转变。LDA是LSA(也称为多项PCA)的概率扩展,因此LDA的主题可以解释为对单词的概率分布。与LSA一样,这些分布也是从训练语料库中自动推断出来的。文档又被解释为这些主题的(软)混合(再次,就像LSA一样)。

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

    gensim使用基于[2]的在线LDA参数估计的快速实现,修改为在计算机集群上以分布式模式运行。

  • 分层Dirichlet过程,HDP 是一种非参数贝叶斯方法(请注意缺少的请求主题数):

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

    gensim使用基于[3]的快速在线实现。HDP模型是gensim的新成员,并且在学术方面仍然很粗糙 - 谨慎使用。

添加新的VSM转换(例如不同的加权方案)相当简单; 有关更多信息和示例,请参阅API参考或直接参阅Python代码

值得重申的是,这些都是独特的增量实现,不需要整个训练语料库一次性存在于主存储器中。有了内存,我现在正在改进分布式计算,以提高CPU效率。如果您认为自己可以做出贡献(通过测试,提供用例或代码),请告诉我们

继续阅读下一个关于相似性查询的教程。


[1] 布拉德福德。2008.对大规模潜在语义索引应用程序所需维度的实证研究。 [2] 霍夫曼,布莱,巴赫。2010.潜在Dirichlet分配的在线学习。 [3] 王,佩斯利,布莱。2011.层级Dirichlet过程的在线变分推理。 [4] Halko,Martinsson,Tropp。2009.找到随机性的结构。 [5] Řehůřek。2011.潜在语义分析的子空间跟踪。


我们一直在努力

apachecn/AiLearning

【布客】中文翻译组