0%

gensim — 相似性查询

gensim学习笔记,相似性查询,官网链接点击这儿

如果你想看到日志记录,做如下设置:

1
2
3
In [1]: import logging

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

相似性查询

在前面的数据集与向量空间和主题与转换两节中,我们介绍了在向量空间中创建数据集以及如何在不同向量空间之间转换数据集。做上述工作的常见原因是我们想要确定文档对之间的相似性,或者特定文档和一组其他文档(例如用户查询与索引文档)之间的相似性。

为了说明在gensim中如何做到这一点,让我们考虑与前面的例子(它最初来自Deerwester等人的“Indexing by Latent Semantic Analysis ”semion 1990 article)一样的文档库:

1
2
3
4
5
6
7
8
9
10
11
12
In[17]:  from gensim import corpora, models, similarities

In [18]: dictionary = corpora.Dictionary.load('/tmp/deerwester.dict')2016-11-29 19:43:24,555 : INFO : loading Dictionary object from /tmp/deerwester.dict
2016-11-29 19:43:24,555 : INFO : loaded /tmp/deerwester.dict

In [19]: corpus = corpora.MmCorpus('/tmp/deerwester.mm')
2016-11-29 19:44:00,602 : INFO : loaded corpus index from /tmp/deerwester.mm.index
2016-11-29 19:44:00,602 : INFO : initializing corpus reader from /tmp/deerwester.mm
2016-11-29 19:44:00,602 : INFO : accepted corpus with 9 documents, 12 features, 28 non-zero entries

In [20]: print(corpus)
MmCorpus(9 documents, 12 features, 28 non-zero entries)

为了使用Deerwester这个例子,我们用这个小文档集定义两个2维LSI空间:
1
2
3
4
5
6
7
8
9
10
11
12
13
In [21]: lsi = models.LsiModel(corpus,id2word=dictionary,num_topics=2)
2016-11-29 19:48:25,750 : INFO : using serial LSI version on this node
2016-11-29 19:48:25,751 : INFO : updating model with new documents
2016-11-29 19:48:25,752 : INFO : preparing a new chunk of documents
2016-11-29 19:48:25,753 : INFO : using 100 extra samples and 2 power iterations
2016-11-29 19:48:25,753 : INFO : 1st phase: constructing (12, 102) action matrix
2016-11-29 19:48:25,807 : INFO : orthonormalizing (12, 102) action matrix
2016-11-29 19:48:26,016 : INFO : 2nd phase: running dense svd on (12, 9) matrix
2016-11-29 19:48:26,050 : INFO : computing the final decomposition
2016-11-29 19:48:26,051 : INFO : keeping 2 factors (discarding 43.156% of energy spectrum)
2016-11-29 19:48:26,067 : INFO : processed documents up to #9
2016-11-29 19:48:26,096 : INFO : topic #0(3.341): 0.644*"system" + 0.404*"user" + 0.301*"eps" + 0.265*"time" + 0.265*"response" + 0.240*"computer" + 0.221*"human" + 0.206*"survey" + 0.198*"interface" + 0.036*"graph"
2016-11-29 19:48:26,097 : INFO : topic #1(2.542): 0.623*"graph" + 0.490*"trees" + 0.451*"minors" + 0.274*"survey" + -0.167*"system" + -0.141*"eps" + -0.113*"human" + 0.107*"time" + 0.107*"response" + -0.072*"interface"

现在假设一个用户输入以下查询“Human computer interaction”。我们希望将文档集中的9个文档按照与这个查询的相关性降序排列。与现代搜索引擎不同,这里我们只关注可能相似的一个方面 —— 对它们的文本(单词)的明显语义相关性。没有超链接,没有随机漫步静态排名,只是布尔关键字匹配的语义扩展:
1
2
3
4
5
6
7
8
In [22]: doc = "Human computer interaction"

In [23]: vec_bow = dictionary.doc2bow(doc.lower().split())

In [24]: vec_lsi = lsi[vec_bow] # 将查询转换为LSI空间

In [25]: print(vec_lsi)
[(0, 0.46182100453271602), (1, -0.070027665279000381)]

此外,我们将考虑余弦相似性(cosine similarity )以确定两个向量的相似性。余弦相似性是矢量空间建模中的标准测量,但是无论矢量表示概率分布如何,不同相似性测量(different similarity measures )可能更合适。

初始化查询结构

为了准备相似性查询,我们需要输入所有要与后续查询进行比较的文档。在我们的例子中,它们是用于训练LSI的9个文档,转换为2-D LSA空间。但这只是偶然的,我们也可能索引一个不同的文档库。

1
2
3
In [27]: index = similarities.MatrixSimilarity(lsi[corpus])
2016-11-29 20:01:22,882 : WARNING : scanning corpus to determine the number of features (consider setting `num_features` explicitly)
2016-11-29 20:01:22,885 : INFO : creating matrix with 9 documents and 2 features

警告:sililarities.MatrixSimilarity类仅当整套向量拟合到内存中时才适用。例如,当与这个类一起使用时,一百万个文档的文档库在256维LSI空间中将需要2GB的RAM。没有2G的空闲内存时,你需要similarities.Similarity类。通过在磁盘上的多个文件(称为碎片)上拆分索引,这个类在固定内存上操作。它在内部使用similarities.MatrixSimilarity与similarities.SparseMatrixSimilarity类,所以速度仍然很快,虽然稍有复杂。

通过save()与load()功能来实现索引序列化:

1
2
3
4
5
6
7
In [28]: index.save('/tmp/deerwester.index')
2016-11-29 20:07:58,905 : INFO : saving MatrixSimilarity object under /tmp/deerwester.index, separately None
2016-11-29 20:07:58,906 : INFO : saved /tmp/deerwester.index

In [29]: index = similarities.MatrixSimilarity.load('/tmp/deerwester.index')
2016-11-29 20:08:22,086 : INFO : loading MatrixSimilarity object from /tmp/deerwester.index
2016-11-29 20:08:22,087 : INFO : loaded /tmp/deerwester.index

这适用于所有索引类(similarities.Similarity, similarities.MatrixSimilarity and similarities.SparseMatrixSimilarity)。正如下面所示,index可以是这些类中的任意对象。拿不定主意的时候,用similarities.Similarity类,因为它可扩展性最好,并且支持后续增加更多文档。

执行查询

要获得我们查询的文档与已建立索引的9个文档的相似性:

1
2
3
4
In [30]: sims = index[vec_lsi]

In [31]: print(list(enumerate(sims)))
[(0, 0.99809301), (1, 0.93748635), (2, 0.99844527), (3, 0.98658866), (4, 0.90755945), (5, -0.12416792), (6, -0.10639259), (7, -0.098794632), (8, 0.050041772)]

余弦度量返回范围<-1,1>(相似度越大,越相似)的相似性,使得第一个文档的分数为0.99809301。

使用一些标准的Python神奇功能,我们将这些相似性按降序排序,并获得查询“Human computer interaction”的最终答案:

1
2
3
4
5
6
7
8
9
10
11
12
In [32]: sims = sorted(enumerate(sims),key=lambda item:-item[1])

In [33]: print(sims)
[(2, 0.99844527), # The EPS user interface management system
(0, 0.99809301), # Human machine interface for lab abc computer applications
(3, 0.9865886), # System and human system engineering testing of EPS
(1, 0.93748635), # A survey of user opinion of computer system response time
(4, 0.90755945), # Relation of user perceived response time to error measurement
(8, 0.050041795), # Graph minors A survey
(7, -0.098794639), # Graph minors IV Widths of trees and well quasi ordering
(6, -0.1063926), # The intersection graph of paths in trees
(5, -0.12416792)] # The generation of random binary unordered trees

这里要注意的是文档2(“The EPS user interface management system”)和文档4(“Relation of user perceived response time to error measurement”)将不会通过标准布尔全文搜索返回,因为它们与“Human computer interaction”没有任何共享的词。然而,在应用LSI之后,我们可以观察到它们两者都获得了相当高的相似性分数(实际上,2是最相似的!),这更好地对应于我们对它们与查询共享“计算机 - 人”相关主题的直觉。事实上,这种语义泛化是我们首先应用变换和做主题建模的原因。