GENSIM官方文档(4.0.0beta最新版)-面向新手的核心教程


译文目录

GENSIM官方文档(4.0.0beta最新版)-面向新手的核心教程

GENSIM官方教程(4.0.0beta最新版)-LDA模型

GENSIM官方教程(4.0.0beta最新版)-Word2Vec词向量模型

GENSIM官方教程(4.0.0beta最新版)-LDA模型评价与可视化

博主全天在线,欢迎评论或私信讨论NLP相关问题


1. 核心概念

本模块介绍Documents, Corpora, Vectors and Models:他们是理解和使用gensim所需的基本概念和术语。

import pprint

gensim的核心概念有:

  1. Document:一些文本;文档。
  2. Corpus:文档的集合。
  3. Vector:文档的数学表达形式,向量化文档。
  4. Model:把文档由一种Vector转化为另一种Vector的算法。

Document(文档)

Gensim中文档是str类型的文本序列,文档可以是简短的140个字符的推文,单个段落(即期刊文章摘要),新闻文章或书籍中的任何内容。

document = "Human machine interface for lab abc computer applications"

Corpus(语料库)

语料库是文档的集合,(集合表达不准确,应该是列表)语料库在Gensim中有两个作用:

  1. 作为训练集输入模型,通过训练语料集来得到常见主题(话题),同时初始化内部模型参数。
    Gensim专注于训练无监督模型,因此不需要人工干预,如费时费力的文档注释或手工标记。
  2. 文档组织化:再训练后,一个主题模型可以被用来从新的文档中提取主题。
    此类语料集可以建立索引并通过语义相似性或聚类做作相似性查询。
    下面是一个示例语料库。它由9个文档组成,其中每个文档都是一个由单个句子组成的字符串。
text_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",
]

注意!上面的示例将整个语料库加载到内存中。实际上,语料库可能非常大,因此将它们加载到内存中可能是不可能的。Gensim通过对文档的逐次流式处理来智能地处理此类语料库。详见下文。

出于说明目的,这仅展示了一个极小的语料库。另一个示例可以是莎士比亚撰写的所有戏剧的列表,所有维基百科文章的列表或特定感兴趣的人的所有推文。

收集完语料库后,通常需要执行许多预处理步骤。我们为了简化步骤,只删除一些常用的英语单词(例如“ the”)和仅在语料库中出现一次的单词。在此过程中,我们将解析标记(tokenize)数据。这将文档分解为词袋(在这种情况下,使用空格作为分隔符)。

有更好的方法来执行预处理,而不仅仅是把文档所有字母全换成小写字母和按空格分割。有效的预处理超出了本教程的范围:如果您有兴趣,请查看 gensim.utils.simple_preprocess()函数。

# 创建停用词列表
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 text_corpus]

# 计算词频
from collections import defaultdict
frequency = defaultdict(int)
for text in texts:
    for token in text:
        frequency[token] += 1

#仅保留出现超过一次的单词
processed_corpus = [[token for token in text if frequency[token] > 1] for text in texts]
pprint.pprint(processed_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(processed_corpus)
print(dictionary)

输出:

Dictionary(12 unique tokens: ['computer', 'human', 'interface', 'response', 'survey']...)

由于我们的语料库很小,因此其中只有12个不同的标记 。对于较大的语料库,包含成千上万个标记的字典是很常见的。

Vector (向量)

为了推断语料库中的潜在结构,我们需要一种可以进行数学处理的文档表示方式。一种方法是将每个文档表示为特征向量。例如,每个特征可能被认为是一个问答对:

  1. Splonge这个单词在文档中出现了几次?零。
  2. 该文件由几段组成?二。
  3. 文档使用多少种字体?五。

问题通常只由它的一个整数ID表示(如所表示的1,2和3)。然后,该文档的表示形式将变成一系列类似的实数对:(1, 0.0), (2, 2.0), (3, 5.0)。这被称为稠密向量,因为它包含对上述每个问题的明确答案。

如果我们事先知道全部问题,则可以隐去它们,将文档简单的表示成:(0,2,5),这就是该文档的三维稠密向量化表示。出于实际目的,Gensim仅允许答案可以转化成单浮点类型的问题。

实践过程中,通常有大量的零向量存在。为了节省内存,Gensim省略了所有零向量。上例因此变成(2,2.0),(3,5.0)。这就是稀疏矩阵或是词袋向量。在这种稀疏表达形式中所有缺失的特征值被明确的解析为零。

假设对于不同文档的问题相同,我们可以比较两篇不同文档的向量,比如(0.0, 2.0, 5.0)和(0.1, 1.9, 4.9)。因为这两个向量非常相似,所以我们可以说相对应的两篇文档相似度也很高。当然,结论的正确性取决于我们如何选择问题。

另一种向量化表示文档的方法是词袋模型。在词袋模型下,每个文档都由一个向量表示,该向量包含字典中每个单词的频数。比如,假设我们有一个包含单词[‘coffee’, ‘milk’, ‘sugar’, ‘spoon’]的词典,一篇文档由字符串“coffee milk coffee”组成的字符串则会被表示成[2,1,0,0],向量的长度就是字典中的条目数。词袋模型的主要特征之一就是它完全忽略了文档中单词出现的顺序,这也是“词袋”名称的来源。

我们处理后的语料集包含12个不同的单词,这意味着每个文档在词袋模型下将表示成一个12维的向量。我们可以用dictionary的token2id得到词典中单词和其对应的ID。

pprint.pprint(dictionary.token2id)

输出:

{'computer': 0,
 'eps': 8,
 'graph': 10,
 'human': 1,
 'interface': 2,
 'minors': 11,
 'response': 3,
 'survey': 4,
 'system': 5,
 'time': 6,
 'trees': 9,
 'user': 7}

现在假设我们项向量化一个新的短语“Human computer interaction” ,可以通过使用dictionary的doc2bow方法得到该文档的稀疏矩阵表达形式(译者注:输入参数是词袋列表)。

new_doc = "Human computer interaction"
new_vec = dictionary.doc2bow(new_doc.lower().split())
print(new_vec)

输出:

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

元组的第一条为字典的单词ID,第二条为词频。没出现在词典中的词不显示。

可以将整个原始语料库转化为向量列表:

bow_corpus = [dictionary.doc2bow(text) for text in processed_corpus]
pprint.pprint(bow_corpus)

输出:

[[(0, 1), (1, 1), (2, 1)],
 [(0, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1)],
 [(2, 1), (5, 1), (7, 1), (8, 1)],
 [(1, 1), (5, 2), (8, 1)],
 [(3, 1), (6, 1), (7, 1)],
 [(9, 1)],
 [(9, 1), (10, 1)],
 [(9, 1), (10, 1), (11, 1)],
 [(4, 1), (10, 1), (11, 1)]]

注意,这整个列表都完整的储存在内存中,在大多数应用中我们需要一个更具拓展性的解决方案。幸运的是gensim允许我们使用一次返回单个文档的迭代器。

注意!文档和向量的区别是前者是文本,后者是数学上更方便的文本表示形式。有时人们说的“文本”其实就是指“向量表示的文本”

注意!两个不同的文本可能会有相同的向量表示(译者注:如词袋模型下“I like apple but don’t like banana”和“I like banana but don’t like apple”向量表示相同)

Model(模型)

我们已经向量化表示了我们的语料集,现在可以开始使用模型对其进行变换了。变换(transformation)指的是把文档的从一种表达形式变成另一种表达形式。在gensim中,文档由向量组成,所以变换(transformation)可以理解成两个向量空间间的映射函数。可以通过训练语料库进行该模型的学习。

tf-idf 是一个简单的例子,tf-idf模型把词袋化的向量转化为语料集中频数由相对稀有度加权处理的向量空间。

下面是一个简单的例子。我们初始化tf-idf模型,在语料库上对其进行训练,并转换字符串“ system minors”:

from gensim import models

# 训练模型,输入是文档稀疏表达的元祖二维列表。
tfidf = models.TfidfModel(bow_corpus)

# 转化 "system minors" 字符串
words = "system minors".lower().split()
print(tfidf[dictionary.doc2bow(words)])

输出:

[(5, 0.5898341626740045), (11, 0.8075244024440723)]

tfidf模型返回一个元祖列表,第一条是词典ID,第二条是tf-idf权重

gensim提供了多种多样的模型和转换方法,详见第三章(主题和变换)。

创建模型后,您可以使用它进行各种有趣的工作。如要通过tfidf转换整个语料并对其进行索引,以准备相似性查询:

from gensim import similarities

index = similarities.SparseMatrixSimilarity(tfidf[bow_corpus], num_features=12)

输入待查询文档,查寻其与语料库中每篇文档的相似性。

query_document = 'system engineering'.split()
query_bow = dictionary.doc2bow(query_document)
sims = index[tfidf[query_bow]]
print(list(enumerate(sims)))

输出

[(0, 0.0), (1, 0.32448703), (2, 0.41707572), (3, 0.7184812), (4, 0.0), (5, 0.0), (6, 0.0), (7, 0.0), (8, 0.0)]

输出结果表示文档3与其的相似度为71.8%,文档2与其相似度为41.2%,等等。我们可以通过排序使结果更具可读性。

总结

gensim的核心概念有是:

  1. 文件:一些文字。

  2. 语料库:文件的集合。

  3. 向量:文档的数学方便表示。

  4. 模型:一种将向量从一种表示转换为另一种表示的算法。

我们在实践中看到了这些概念。首先,我们从文档集开始。接下来,我们将这些文档转换为向量空间表示。之后,我们创建了一个模型,将原始向量表示形式转换为TfIdf。最后,我们使用模型来计算某些查询文档和语料库中所有文档之间的相似度。


2. 语料库和向量空间

本章节展示把文本转换成向量空间表示,同时介绍各种格式的语料流(corpus streaming)和一些存储到磁盘的方式。

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

从字符串到向量

首先创造一个有9个简短文档的小型语料库。

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",
]

这是一个由9个文档组成的小语料库,每个文档仅包含一个句子。

首先,让我们对文档进行标记化,删除常用单词以及仅在语料库中出现一次的单词:

from pprint import pprint  
from collections import defaultdict

# 标记化,启用停用词
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
]

# 删除只出现一次的单词
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
]

pprint(texts)

输出

[['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']]

您处理文件的方式可能会有所不同;在这里,我们仅在空白处分割以进行标记化,然后将每个单词都小写。实际上,我使用这种特定的(简单且低效的)设置来模仿Deerwester等人在原始LSA文章中所做的实验。

处理文档的方式是如此多样,并且取决于应用程序和语言,以至于我决定不通过任何接口来限制它们。取而代之的是,文档由提取的特征表示,而不是简单的由字符串形式表示;如何获取要素取决于您。下面,我们将描述一种常见且通用的方法(称为词袋模型),但是请记住,不同的特征适用于不同的应用领域;此外,数据集很重要!!!否则就会garbage in, garbage out~

要将文档转换为向量,我们将使用称为词袋模型的文档表示形式。在这种表示形式中,每个文档都由一个向量表示,其中每个向量元素代表一个问题-答案对,类似于:

  • 问题:单词系统在文档中出现几次?
  • 答:一次。

如前文所说,用整数ID表示问题,corpora.Dictionary类通过输入原始语料,生成字典。

from gensim import corpora
dictionary = corpora.Dictionary(texts)
dictionary.save('/tmp/deerwester.dict') 
print(dictionary)

输出

2020-10-28 00:52:02,550 : INFO : adding document #0 to Dictionary(0 unique tokens: [])
2020-10-28 00:52:02,550 : INFO : built Dictionary(12 unique tokens: ['computer', 'human', 'interface', 'response', 'survey']...) from 9 documents (total 29 corpus positions)
2020-10-28 00:52:02,550 : INFO : saving Dictionary(12 unique tokens: ['computer', 'human', 'interface', 'response', 'survey']...) under /tmp/deerwester.dict, separately None
2020-10-28 00:52:02,552 : INFO : saved /tmp/deerwester.dict
Dictionary(12 unique tokens: ['computer', 'human', 'interface', 'response', 'survey']...)

在这里,我们为出现在gensim.corpora.dictionary.Dictionary该类的语料库中的所有单词分配了唯一的整数id 。这遍及所有文本,收集了字数统计和相关统计信息。最后,我们看到在处理后的语料库中有十二个不同的词,这意味着每个文档将由十二个数字表示(即,由12维向量表示)。要查看单词及其ID之间的映射:

print(dictionary.token2id)

输出

{'computer': 0, 'human': 1, 'interface': 2, 'response': 3, 'survey': 4, 'system': 5, 'time': 6, 'user': 7, 'eps': 8, 'trees': 9, 'graph': 10, 'minors': 11}

实际将标记化文档转化为向量:

new_doc = "Human computer interaction"
new_vec = dictionary.doc2bow(new_doc.lower().split())
print(new_vec) 

输出

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

语料流-一次一篇文档

请注意,以上语料库作为纯Python列表完全位于内存中。在这个简单的示例中,它没什么大不了,但是只是为了使事情变得清楚,我们假设语料库中有数百万个文档。将所有这些都存储在RAM中是行不通的。我们假设文档存储在磁盘上的文件中,每行一个文档。Gensim仅要求语料库一次必须能够返回一个文档向量

from smart_open import open  

class MyCorpus:
    def __iter__(self):
        for line in open('https://radimrehurek.com/gensim/mycorpus.txt'):
            yield dictionary.doc2bow(line.lower().split())
            #这个远程txt一行一个文档

Gensim牛逼在语料库不一定是list或是numpy数组,或是pandas dataframe,Gensim接受任何在迭代时依次产生文档个的对象。(译者注:原理:迭代器省内存)

corpus_memory_friendly = MyCorpus()  
print(corpus_memory_friendly)

输出

<__main__.MyCorpus object at 0x11e77bb38>

语料库现在是一个对象,print仅输出内存中对象的地址不是很有用。要查看组成向量,得遍历语料库并打印每个文档向量(一次一个):

for vector in corpus_memory_friendly:  
    print(vector)

输出

[(0, 1), (1, 1), (2, 1)]
[(0, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1)]
[(2, 1), (5, 1), (7, 1), (8, 1)]
[(1, 1), (5, 2), (8, 1)]
[(3, 1), (6, 1), (7, 1)]
[(9, 1)]
[(9, 1), (10, 1)]
[(9, 1), (10, 1), (11, 1)]
[(4, 1), (10, 1), (11, 1)]

尽管输出与普通Python列表的输出相同,但语料库现在对内存更友好了,因为一次最多只有一个向量驻留在RAM中。现在,您的语料库可以随您所需而变大。

同样,构造字典时也不要将所有文本加载到内存中:

dictionary = corpora.Dictionary(line.lower().split() for line in open('https://radimrehurek.com/gensim/mycorpus.txt'))
stop_ids = [
    dictionary.token2id[stopword]
    for stopword in stoplist
    if stopword in dictionary.token2id
]
once_ids = [tokenid for tokenid, docfreq in dictionary.dfs.items() if docfreq == 1]
dictionary.filter_tokens(stop_ids + once_ids)  
dictionary.compactify()
print(dictionary)

语料库格式

存在几种用于将向量空间语料库(向量序列)序列化到磁盘的文件格式。 Gensim通过前面提到的流式语料库接口实现它们:以惰性方式从磁盘读取文档(或将其存储到磁盘),一次只一个文档,而整个语料库不会一次读入主存储器。

市场矩阵格式(Market Matrix format)是一种比较著名的文件格式。
要以矩阵市场格式保存语料库,样例如下:

corpus = [[(1, 0.5)], []]  

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

读取样例如下

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

语料库是流对象,因此通常无法直接打印它们:

print(corpus)

输出

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

正确打印方法:

print(list(corpus))
#输出
#[[(1, 0.5)], []]
for doc in corpus:
    print(doc)
#输出
[(1, 0.5)]
[]

第二种方法显然对内存更友好,但是出于测试和开发目的,没有什么比直接用list直接调用更好了,因为简单。

可见,gensim也可以用作内存效率高的I / O格式转换工具:只需使用一种格式加载文档流,也可以将其保存为另一种格式。(译者注:说得好,我选择pickle)

与Numpy和Scipy的兼容性

Gensim还包含高效简单的函数,可以从Numpy或者Scipy中得到语料

import gensim
import numpy as np
numpy_matrix = np.random.randint(10, size=[5, 2])
corpus = gensim.matutils.Dense2Corpus(numpy_matrix)
import scipy.sparse
scipy_sparse_matrix = scipy.sparse.random(5, 2) 
corpus = gensim.matutils.Sparse2Corpus(scipy_sparse_matrix)
scipy_csc_matrix = gensim.matutils.corpus2csc(corpus)

3. 主题和变换

本章节介绍一些转换手段并在玩具主题上展示它们的用法。

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

在本章节,我们将为您展示如何把文档从一种向量表示形式转换成另一种,变换主题要达到以下目的:

  1. 找到语料中介隐藏结构,发现单词之间的关系,并用它们更语义(semantic)的描述文档(就是更好的还原文档内容)
  2. 使文档表示更加紧凑,这样既能节省内存,又因忽视了噪声而显得更加高效。

创建语料库

步骤同上
输入:

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",
]

训练tfidf模型,输入格式是doc2bow后的元祖二维列表:

from gensim import models
tfidf = models.TfidfModel(corpus)

将该变换模型运用于整体语料库:

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)]

这种情况下tfidf转换模型被用于了和训练模型相同的语料库,其实,一旦转换模型被训练好了,它可以用在任何相同的向量空间上,即使根本没有在训练语料库中出现过。这一过程是通过LSA折叠或LDA主题推断实现的。

注意!model[corpus]只是封装corpus,而不是转换整个语料库,因为这将耗费大量内存,转换是在文档迭代过程中实时进行的,如果想频繁使用转换后的语料corpus_transformed,请先把它存入磁盘后进行调用。

转换模型也可以被序列化:

lsi_model = models.LsiModel(corpus_tfidf, id2word=dictionary, num_topics=2)  
# bow->tfidf->fold-in-lsi
corpus_lsi = lsi_model[corpus_tfidf]  

在这里,我们通过潜在语义索引将Tf-Idf语料库 转换为潜在的二维空间(因为我们设置了2-D空间num_topics=2),下面看下这两个维度代表啥:

lsi_model.print_topics(2)

输出

[(0, '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"'), (1, '-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"')]

可见,trees,graph,minors和主题一关系较大,剩下的词和主题二都有一点关系。可以理解成主题的关键词

下面看看文档的训练结果。

for doc, as_text in zip(corpus_lsi, documents):
    print(doc, as_text)

输出

[(0, 0.06600783396090518), (1, -0.520070330636184)] Human machine interface for lab abc computer applications
[(0, 0.19667592859142694), (1, -0.7609563167700047)] A survey of user opinion of computer system response time
[(0, 0.08992639972446678), (1, -0.72418606267525)] The EPS user interface management system
[(0, 0.07585847652178407), (1, -0.6320551586003417)] System and human system engineering testing of EPS
[(0, 0.10150299184980252), (1, -0.5737308483002961)] Relation of user perceived response time to error measurement
[(0, 0.7032108939378307), (1, 0.16115180214025954)] The generation of random binary unordered trees
[(0, 0.8774787673119826), (1, 0.16758906864659615)] The intersection graph of paths in trees
[(0, 0.9098624686818572), (1, 0.14086553628719237)] Graph minors IV Widths of trees and well quasi ordering
[(0, 0.6165825350569278), (1, -0.05392907566389235)] 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)  

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

os.unlink(tmp.name)

(译者注:说得好,我选择pickle)

下一个问题是:这些文档彼此之间到底有多相似?是否有一种形式化相似性的方法,以便对于给定的输入文档,我们可以根据相似性对其他文档集进行排序?第四章我们将会学习这一内容。

主题变换模型

Gensim实现了基本所有主流的主题模型,下面依次来看看:
1. TF-IDF(Term Frequency * Inverse Document Frequency)

TF-IDF模型输入词袋向量进行训练,变换后的输出向量能体现单词的稀有程度,这个值可以归一化在01之间。

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

2. LSI(Latent Semantic Indexing, LSA)

将文档从词袋或tfidf加权向量空间转换为维数较低的隐空间(latent space)。实际工程中通常维数设定在200-500。

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

LSI独特之处在于它可以online training,只要给它新的训练文档,它就能实时更新模型,同时我们也能使用该模型。

#假设已有一个用tfidf_corpus训练好的模型,现在用another_tfidf_corpus进行增量训练。
model.add_documents(another_tfidf_corpus)  
lsi_vec = model[tfidf_vec]  # 用新训练好的模型转换tfidf_vec得到lsi_vec,同时不影响model。
#下面一轮操作同上。
model.add_documents(more_documents) 
lsi_vec = model[tfidf_vec]

如果你想让模型逐步遗忘过于成就的训练集,或者想权衡训练速度和内存占有量以等等细节,去看官方文档gensim.models.lsimodel
3. RP(Random Projections)
随机投影旨在减少向量空间维数。这是一种非常有效的方法(对内存和CPU友好)因为不用SVD,通过引入一些随机性来近似文档之间的tfidf距离。根据数据集的不同,建议的目标维数在 1 0 2 ~ 1 0 3 10^2~10^3 102103

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

4. LDA(Latent Dirichlet Allocation)
LDA主题模型可以通过单词概率进行解释,主题从训练语料库中获得,格式和LSA很像。

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

gensim使用一种基于分布式集群计算的高效在线参数估计的LDA算法,论文见Hoffman, Blei, Bach. 2010. Online learning for Latent Dirichlet Allocation.
5. HDP(Hierarchical Dirichlet Process)
这是一种非参贝叶斯方法,不需要我们去决定主题数。

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

HDP是gensim新增的模型,算法基于论文Wang, Paisley, Blei. 2011. Online variational inference for the hierarchical Dirichlet process,注意!该方法目前还比较粗糙且未成熟,请谨慎用于工程实践。


4. 相似度查询

创建语料库

步骤同上

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]

相似度接口

在前面的章节中我们介绍了如何进行转换操作,这是文档相似性查询的基础。

为了说明如何在gensim中完成此操作,让我们使用与前面的示例相同的微小语料集,并定义LSI维度为2。

from gensim import models
lsi = models.LsiModel(corpus, id2word=dictionary, num_topics=2)

出于本教程的目的,您仅需要了解LSI的两件事。首先,这只是另一种转换:将向量从一个空间转换到另一个空间。其次,LSI的好处是可以识别单词与主题之间的映射关系。我们的LSI空间是二维的(num_topics = 2),因此有两个主题,但此参数可以任意调节。

现在假设用户输入查询“Human computer interaction”。我们想以相似性查询结果降序对我们的九个语料库文档进行排序。不像现在的搜索引擎,这里我们仅关注相似性的一个方面,即它们的文本在概率上的相似性。

doc = "Human computer interaction"
vec_bow = dictionary.doc2bow(doc.lower().split())
vec_lsi = lsi[vec_bow]  # convert the query to LSI space
print(vec_lsi)

输出

[(0, 0.4618210045327157), (1, -0.07002766527900028)]

此外,相似性还可以用向量的余弦相似性来度量。余弦相似性是向量空间建模的一种度量标准,但对于同的情况,应应用不同的度量方式。

初始化查询结构

为了准备相似性查询,我们需要先预处理所有待查询的文档。在我们的案例中,它们与用于训练LSI的9个文档相同,也转换为2维LSA空间。当然我们也可以用训练好的模型转换其他语料集。

from gensim import similarities
index = similarities.MatrixSimilarity(lsi[corpus])

注意!仅当整个向量集都能放入内存时,similarities.MatrixSimilarity类才适用。一百万个文档的语料库在256维LSI空间中需要2GB RAM。如果没有2GB的可用RAM,则需要使用similarities.Similarity类。此类通过在磁盘上的多个文件(称为分片)中拆分索引来在固定内存中运行。它在内部使用的是similarities.MatrixSimilarity和similarities.SparseMatrixSimilarity,所以它仍然很快,只是稍微复杂一些。

索引通过save()和load()保存到磁盘

index.save('/tmp/deerwester.index')
index = similarities.MatrixSimilarity.load('/tmp/deerwester.index')

上述的保存和加载方法对所有的索引类都有效(包括similarities.Similarity,similarities.MatrixSimilarity和 similarities.SparseMatrixSimilarity)。如果出现异常,可以尝试使用 similarities.Similarity,它的兼容性最好,且支持增量索引。

执行查询

sims = index[vec_lsi]  
print(list(enumerate(sims)))

输出

[(0, 0.998093), (1, 0.93748635), (2, 0.9984453), (3, 0.9865886), (4, 0.90755945), (5, -0.12416792), (6, -0.10639259), (7, -0.09879464), (8, 0.050041765)]

余弦测度返回的相似度范围为<-1,1>(越大,越相似),因此第一个文档的分数为0.99809301,(译者注:对于余弦相似性,负数可以看成0)。

再通过降序排列等处理后,我们得到对“Human computer interaction”相似性查询的结果:

sims = sorted(enumerate(sims), key=lambda item: -item[1])
for doc_position, doc_score in sims:
    print(doc_score, documents[doc_position])

输出

0.9984453 The EPS user interface management system
0.998093 Human machine interface for lab abc computer applications
0.9865886 System and human system engineering testing of EPS
0.93748635 A survey of user opinion of computer system response time
0.90755945 Relation of user perceived response time to error measurement
0.050041765 Graph minors A survey
-0.09879464 Graph minors IV Widths of trees and well quasi ordering
-0.10639259 The intersection graph of paths in trees
-0.12416792 The generation of random binary unordered trees

需要说明的是,如果采用标准的全文搜索,第二个文档"The EPS user interface management system"和第四个文档"Relation of user perceived response time to error measurement"不会和"Human computer interaction"体现任何相似关系,因为它们没有一词是相同的。然而,在使用LSI后,我们可以看到这两篇文档都得到了不错的分数,因为他们所属的主题相同,都是“computer-human”。事实上,这也是我们为什么要采用这种主题模型并且进行变换的原因。

  • 7
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值