Doc2Vec 模型中的 tqdm 函数问题

Problem in tqdm function in a Doc2Vec model

我正在使用这篇文章 https://actsusanli.medium.com/ 来实现 Doc2Vec 模型,但我在训练步骤中遇到了问题。

model_dbow.train(utils.shuffle([x for x in tqdm(train_tagged.values)]), total_examples=len(train_tagged.values), epochs = 40)

如您所见,我正在使用 tqdm 函数。当我 运行 代码时,tqdm 是 100%,几分钟后,但算法仍然在相同的 shell 中运行了很长时间。

您是否知道这是 tqdm 函数的问题还是其他问题?

通过使用“列表理解”([..])...

[x for x in tqdm(train_tagged.values)]

...您正在让 tqdm 在您的 train_tagged.values 序列上迭代一次,进入实际的内存中 Python 列表。这将显示 tqdm 进展相当快——然后完全结束与 tqdm 的任何参与。

然后,您将那个简单的结果列表(没有任何 tqdm 特征)传递到 Doc2Vec.train(),其中 Doc2Vec 进行 epochs=40 训练。 tqdm 不再涉及,因此不会有增量进度条输出。

您可能想尝试(或已经尝试过)跳过额外 list 创建的东西,直接传递 tqdm-wrapped 序列,例如:

corpus = utils.shuffle(train_tagged.values)
model_dbow.train(tqdm(corpus), total_examples=len(corpus), epochs = 40)

但这有一个不同的问题:tqdm-wrapper 仅设计为允许(并报告进度)one 迭代包装序列。所以这将显示一次迭代的增量进度。

但是当 .train() 尝试其下一个必要的 39 次重复迭代以完成其 epochs=40 训练运行时,单通道 tqdm 对象将被耗尽,从而防止完全 &适当的培训。

请注意,通过将 Python 日志级别(全局,或仅针对 class Doc2Vec)设置为 [=34,Gensim 中有一个进度日志选项=]. Doc2Vec 然后将发出一条日志行,显示每个时期内和时期之间的进度,大约每 1 秒一次。但是:您还可以通过为 .train() 的可选 report_delay 参数提供不同的秒值来降低此类日志记录的频率,例如 report_delay=60 (每分钟而不是每第二)。

如果你真的想要一个进度条,应该可以使用 tqdm - 但你必须绕过它的假设,即你用 tqdm() 包装的可迭代对象只会是迭代一次。

我相信有两种可能的方法,每种方法都有不同的权衡:

(1) 与其让 .train() 重复语料库 N 次,不如自己动手 - 相应地调整其他 .train() 参数。粗略地说,这意味着改变一行...

model.train(corpus, total_examples=len(corpus), epochs=40)

...将你想要的 40 个时期变成看起来像 one 迭代到 tqdm 和 Gensim 的 .train() 的东西,比如...

repeated_corpus = itertools.chain(*[corpus]*40)
repeated_len = 40 * len(corpus)
model.train(tqdm(repeated_corpus, total=repeated_len), total_examples=repeated_len, epochs=1)

(请注意,您现在 必须 tqdm 一个关于序列长度的提示,因为来自 itertools.chain() 的一次性链式迭代器不报告自己的长度。)

然后您将在整个训练语料库中获得一个进度条 - 模型现在将其视为对更大语料库的一次传递,但最终涉及相同的 40 次传递。

考虑到这一变化,您将需要重新解释任何剩余的日志行,并且您将失去通过模型的纪元结束回调机制安装自己的每个纪元回调的机会。 (但是,无论如何,这是一个很少使用的功能。)

(2) 不是用单个 tqdm() 包装语料库(只能显示一次迭代的进度条),而是将语料库包装为新的 fully-re -iterable object 本身每次都会启动一个 new tqdm()。例如,类似于:

class TqdmEveryIteration(object):
    def __init__(self, inner_iterable):
        self.inner_iterable = inner_iterable
    def iter(self):
        return tqdm(inner_iterable)

然后,使用这个新的额外 tqdm 添加包装器,您应该能够做到:

corpus = utils.shuffle(train_tagged.values)
model_dbow.train(TqdmEveryIteration(corpus), total_examples=len(corpus), epochs = 40)

在这种情况下,你应该得到一个进度条 per epoch,因为每次训练 pass 都会启动一个新的 tqdm() wrapper。

(如果您尝试了这两种方法中的任何一种并且效果很好,请告诉我!它们应该大致正确,但我还没有测试过。)

另外:如果您正在为自己的工作建模的 actsusanli.medium.com 作者的文章是...

https://towardsdatascience.com/multi-class-text-classification-with-doc2vec-logistic-regression-9da9947b43f4

...请注意,它使用的是过于复杂和脆弱的反模式,在循环中多次调用 .train() 并手动进行 alpha 管理。这有问题 。但是这种方法 也会 也有副作用,每次都在新的 tqdm 中重新包装语料库(就像上面的 TqdmEveryIteration class ),因此尽管存在其他问题,每次调用 .train().

都会实现一个实际进度条

(大约一个月前我通过 Medium 给作者发了一封关于这个问题的私信。)