【问题标题】:Generator is not an iterator?生成器不是迭代器?
【发布时间】:2016-03-13 23:48:10
【问题描述】:

我有一个生成器(一个产生东西的函数),但是当试图将它传递给 gensim.Word2Vec 时,我收到以下错误:

TypeError:您不能将生成器作为语句参数传递。试试迭代器。

生成器不是一种迭代器吗?如果没有,我如何从中制作迭代器?

查看库代码,它似乎只是简单地迭代像for x in enumerate(sentences) 这样的句子,这与我的生成器配合得很好。那么是什么导致了错误呢?

【问题讨论】:

  • 嗯...他们费了很多力气阻止你使用生成器:github.com/piskvorky/gensim/blob/…
  • 这毫无意义。
  • @user2357112 -- 也许输入需要多次迭代。文档说列表是一个好的输入。 (当然,在这种情况下,iterator 绝对是 错误 放入错误消息的术语)。
  • @riv 然后您可以将您的生成器更改为列表综合。
  • I found the issue the check was supposed to address. 看来评论链里的人当时对词汇的理解不是很清楚。这个错误消息肯定应该改变(也许他们应该添加or iter(sentences) is iter(sentences) 来捕获其他迭代器类型)。

标签: python gensim word2vec


【解决方案1】:

生成器在一个循环之后用尽。 Word2vec 只需要多次遍历句子(并且可能获取给定索引的项目,这对于只是一种只能弹出的堆栈的生成器是不可能的),因此需要更可靠的东西,例如列表。

特别是在他们的代码中,他们调用了两个不同的函数,都迭代句子(因此,如果你使用生成器,第二个将在空集上运行)

self.build_vocab(sentences, trim_rule=trim_rule)
self.train(sentences)

它应该适用于任何实现__iter__ 而不是GeneratorType 的东西。所以将你的函数包装在一个可迭代的接口中,并确保你可以多次遍历它,这意味着

sentences = your_code
for s in sentences:
  print s
for s in sentences:
  print s

两次打印您的收藏

【讨论】:

  • 所有迭代器在一个循环后都被耗尽,不仅仅是那些由生成器创建的。 (事实上​​,在用尽之后对next 的后续调用中继续引发StopIteration 是必需的。)错误消息可能意味着说iterable 而不是iterator。正如您的答案中正确解释的那样,某些可迭代对象可以循环多次。
  • 我明白了,但是如果迭代器在__iter__ 中返回 self ,它不是只能迭代一次吗?我也认为错误消息的意思是可迭代的。
  • @riv 一个迭代器必须__iter__中返回selfiterable 不能,但是,它可以(并且在内置容器的情况下)返回一个从开头开始的新迭代器。
  • 我刚刚创建了一个类,它在 __iter__ 方法中返回我的生成器(并且没有其他方法)并且它有效。
  • @riv 它可能看起来好像在工作,因为您不再遇到异常,但它是否正常工作?如果build_vocabtrain 都在sentences 迭代器上进行迭代,train 将遇到一个空迭代器。代码很可能根本没有经过训练。 Alex Volkov 解释了正确的修复方法,但它可以更短地拼写为list(generator_obj)
【解决方案2】:

似乎 gensim 抛出了一个误导性的错误信息。

Gensim 想要多次迭代您的数据。大多数库只是从输入构建一个列表,因此用户不必关心提供多个可迭代序列。当然,生成内存中的列表可能会非常消耗资源,例如,在迭代文件时,可以在不将整个文件存储在内存中的情况下完成。

在您的情况下,只需将生成器更改为列表综合即可解决问题。

【讨论】:

    【解决方案3】:

    正如之前的海报所提到的,生成器的行为类似于迭代器,但有两个显着差异:生成器会耗尽,并且您不能索引一个。

    我很快在这个页面上查找了文档——https://radimrehurek.com/gensim/models/word2vec.html

    文档指出

    gensim.models.word2vec.Word2Vec(sentences=None, size=100, alpha=0.025, window=5, min_count=5, max_vocab_size=None, sample=0, seed=1, workers=1, min_alpha=0.0001, sg=1, hs=1,negative=0, cbow_mean=0, hashfxn=, iter=1, null_word=0, trim_rule=None, sorted_vocab=1) ...

    从可迭代的句子中初始化模型。每个句子都是将用于训练的单词列表(unicode 字符串)。

    我大胆猜测,函数内部的逻辑本质上需要一个或多个列表属性,例如项目索引,可能存在显式断言语句或引发错误的 if 语句。

    一个可以解决您的问题的简单技巧是将您的生成器变成列表理解。您的程序将承受 CPU 性能损失并增加其内存使用量,但这至少应该使代码正常工作。

    my_iterator = [x for x in generator_obj]
    

    【讨论】:

    • 请注意,由您的答案中的理解创建的列表是可迭代的(具有 __iter__ 方法),但不是迭代器(没有 next 方法)。
    • 是的,你是对的,它是列表理解,而不是迭代器,我会改变我的答案。
    • 但这意味着您无法在非常大的语料库上训练 word2vec。然而,Gensim 库以内存高效而自豪。
    • @osa 这是针对这个特定问题的快速解决方案。您可能需要为您的案例编写更多涉及的内容,即实现您自己的迭代器,该迭代器可以循环多次,您可以通过多次重新读取文件来权衡 I/O 的内存使用,请参阅 -- 示例迭代器stackoverflow.com/questions/19151/build-a-basic-python-iterator;见 itertools.cycle -- docs.python.org/2/library/itertools.html#itertools.cycle
    【解决方案4】:

    其他答案指出,Gensim 需要两次通过来构建Word2Vec 模型:一次构建词汇表(self.build_vocab),第二次训练模型(self.train)。您仍然可以通过拆分 build_vocabtrain 方法将生成器传递给 train 方法(例如,如果您正在流式传输数据)。

    from gensim.models import Word2Vec
    
    model = Word2Vec()
    sentences = my_generator()  # first pass
    model.build_vocab(sentences)
    
    sentences = my_generator()  # second pass of same data
    model.train(sentences2, 
                total_examples=num_sentences,  # total number of documents to process
                epochs=model.epochs)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-05-16
      • 2020-06-29
      • 1970-01-01
      • 2018-05-24
      • 1970-01-01
      • 2017-04-06
      • 2017-09-03
      相关资源
      最近更新 更多