在Gensim中按ID检索文档的字符串版本

时间:2015-02-12 22:04:24

标签: python gensim

我正在使用Gensim进行一些主题建模,我已经到了使用LSI和tf-idf模型进行相似性查询的地步。我找回了一组ID和相似之处,例如。 (299501, 0.64505910873413086)

如何获取与ID相关的文本文档,在本例中为299501?

我查看了语料库,字典,索引和模型的文档,似乎无法找到它。

2 个答案:

答案 0 :(得分:3)

可悲的是,据我所知,你必须从分析的最开始知道你想要通过id检索文档。这意味着您需要在ID和原始文档之间创建自己的映射,并确保在整个过程中保留gensim使用的ID。我认为gensim不会保留这样的映射。

我肯定是错的,事实上如果有人告诉我有一种更简单的方法,我会喜欢它,但我花了很多时间试图避免在维基百科语料库中重新运行巨大的LSI模型无济于事。最后,我不得不携带一个id列表和相关文档,以便我可以使用gensim的输出。

答案 1 :(得分:3)

我刚刚经历了同样的过程,并达到了同样的目标,即" sims"有文档ID,但想要我原来的#34;文章代码"。尽管没有完全提供,但整个Gensim库中都有元数据功能,这些示例可以提供帮助。当我记得我必须做的事情时,我会回答这个问题,以防万一这对以前的访问者有任何帮助。

参见gensim.corpora.textcorpus.TextCorpus#get_texts,它返回文本或简单的单项元数据" linenumber"如果启用了metadata标志:

def get_texts(self):
    """Iterate over the collection, yielding one document at a time. A document
    is a sequence of words (strings) that can be fed into `Dictionary.doc2bow`.
    Each document will be fed through `preprocess_text`. That method should be
    overridden to provide different preprocessing steps. This method will need
    to be overridden if the metadata you'd like to yield differs from the line
    number.
    Returns:
        generator of lists of tokens (strings); each list corresponds to a preprocessed
        document from the corpus `input`.
    """
    lines = self.getstream()
    if self.metadata:
        for lineno, line in enumerate(lines):
            yield self.preprocess_text(line), (lineno,)
    else:
        for line in lines:
            yield self.preprocess_text(line)

我已经实现了一个自定义的make_corpus.py脚本和一个试用分类器脚本,该脚本使用相似性来查找搜索文档中的相关文档。我为利用该点的元数据所做的更改如下:

在make_corpus脚本中,我将构造函数中的元数据启用到我的TextCorpus子类:

corpus = SysRevArticleCorpus(inp, lemmatize=lemmatize, metadata=True)

我还需要序列化元数据,因为我没有在语料库生成之后立即进行处理(如某些示例所做的那样),因此您还需要在序列化步骤中打开元数据:

MmCorpus.serialize(outp + '_bow.mm', corpus, progress_cnt=10000, metadata=True)

这会使gensim.matutils.MmWriter#write_corpus使用您的语料库“xxx_bow.mm.metadata.cpickle”文件保存.mm个文件。

要在元数据中添加更多项,您需要在TextCorpus子类中实现并覆盖一些内容。我已经基于WikiCorpus示例类创建了一个,因为我有自己的现有语料库来阅读。

构造函数需要接收元数据标志,例如:

def __init__(self, fname, processes=None, lemmatize=utils.has_pattern(), 
    dictionary=None, metadata=False,
...
    self.metadata = metadata

    if dictionary is None:
        # temporarily disable metadata to make internal dict
        metadata_setting = self.metadata
        self.metadata = False
        self.dictionary = Dictionary(self.get_texts())
        self.metadata = metadata_setting
    else:
        self.dictionary = dictionary

我实际上是从JSON语料库中读书,所以我已经编写了自定义解析器。我的文章有一个"代码"属性是我的规范文档ID。我还想存储"标题",文档正文在"文本"属性。 (这取代了wiki示例中的XML解析)。

def extract_articles(f, filter_namespaces=False):
    """
    Extract article from a SYSREV article export JSON = open file-like object `f`.

    Return an iterable over (str, str, str) which generates (title, content, pageid) triplets.
    """
    elems = (elem for elem in f)
    for elem in elems:
        yield elem["title"], elem["text"] or "", elem["code"]

这是在被覆盖的get_texts内调用的(在它提到的父类中,你需要覆盖它以使用自定义元数据)。总结:

def get_texts(self):
...
    with open(self.fname) as data_file:    
        corpusdata = json.load(data_file)
    texts = \
        ((text, self.lemmatize, title, pageid)
         for title, text, pageid
         in extract_articles(corpusdata['docs'], self.filter_namespaces))

... (skipping pool processing stuff for clarity)

    for tokens, title, pageid in pool.imap(process_article, group):

        if self.metadata:
            yield (tokens, (pageid, title))
        else:
            yield tokens

因此,这应该让您在corpus.mm文件旁边保存元数据。如果要在以后的脚本中重新阅读它,则需要重新阅读pickle文件 - 似乎没有任何内置方法可以重新读取元数据。幸运的是,它只是一个由Gensim生成的文档ID索引的字典,所以它很容易加载和使用。 (See wiki-sim-search

e.g。在我的试用版分类器中,我添加了两个内容:metadata = pickle.load()metadata[docID],以便最终找到原始文章。

# re-load everything...
dictionary = corpora.Dictionary.load_from_text(datapath+'/en_wordids.txt')
  corpus = corpora.MmCorpus(datapath +'/xxx_bow.mm')
metadata = pickle.load(open(datapath + 'xxx_bow.mm.metadata.cpickle', 'rb'))

lsiModel = models.LsiModel(corpus, id2word=dictionary, num_topics=4)
index = similarities.MatrixSimilarity(lsiModel[corpus])

# example search
doc = "electronic cognitive simulation"
vec_bow = dictionary.doc2bow(doc.lower().split())
vec_lsi = lsiModel[vec_bow]  # convert the query to LSI space

# perform a similarity query against the corpus
sims = index[vec_lsi]  
sims = sorted(enumerate(sims), key=lambda item: -item[1])

# Look up the original article metadata for the top hit
(docID, prob) = sims[0]
print(metadata[docID])

# Prints (CODE, TITLE)
('ShiShani2008ProCarNur', 'Jordanian nurses and physicians learning needs for promoting smoking cessation.')

我知道这并没有按照您的要求提供原始文字(我自己也不需要),但您可以非常轻松地将文字添加到&#34 ;元数据" (虽然这相当扩展了元数据的定义,但可能非常大!)。我猜Gensim假设您已经有了一些原始文档的数据库,因此它将超出范围。但是我觉得Gensim生成的ID和原始文档标识符之间需要有一个映射,元数据功能可以很好地完成。