3

我知道python中的生成器至少是记忆效率高的,因为它一次处理一个项目,但这如何使它具有时间效率(如果是的话)?

具体来说,假设我使用生成器函数一次加载一个数据以执行机器学习任务。归根结底,我仍然需要遍历所有数据元素并一次加载一个(使用生成器函数)。是的,这对记忆很有效,但是与一次加载所有数据集相比,加载整个数据集应该花费更多的时间。我的直觉对吗?

#sample_code

def my_gen():
    for i in range(1000):
    features = np.random.randn(32,32,3)
    labels = np.random.randint(0,1, size = 1)
    yield features, labels
4

3 回答 3

2

将生成器视为惰性序列,它通常作为相应的急切序列的时间效率较低。

%timeit sum((x*2 for x in range(5000)))  # lazy generator
366 µs ± 9.24 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit sum([x*2 for x in range(5000)])  # eager list
308 µs ± 3.12 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

这是因为生成器保持中间状态,必须为每个项目恢复。相反,急切地创建一个序列只需要处理一次中间状态。

但是请记住,生成器的开销基本上是固定的。如果每个项目都需要很长时间来计算,那么生成器的恒定开销就可以忽略不计。当一次处理一个项目时,s 还允许释放已处理的项目,从而减少流程的整体负载——可能在某个时候达到净时间优势。


生成器的优点是惰性允许表示无限序列和延迟 ——与序列“普通 O(ni)”相比,生成器是“n 倍 O(i)”。这允许生成器以可靠的时间效率生产每个项目,即使整个过程会无限延迟。

一个无限的、急切的序列将具有无限的时间复杂度,但一个无限的惰性生成器只根据需要生成项目。

def randoms():
    """Infinite stream of random numbers"""
    while True:
        yield random.random()

同样,生成器允许外部数据源在提供每个项目之间有时间。当数据源在提供项目之间存在显着延迟时,这可以使生成器更高效。

于 2020-08-20T11:22:21.200 回答
1

不,生成器本质上比类似的替代方案(如列表推导)要慢。

如果您希望通过使用生成器加载数据来减少内存,您可能不应该担心这种性能差异。更常见的情况是,性能瓶颈出现在磁盘 I/O 和/或系统调用上。使用生成器的惩罚对整体性能的影响可以忽略不计。

所以最终的答案是:继续使用生成器。它的性能应该是最后需要担心的。

于 2020-08-20T11:09:24.913 回答
1

生成器是您使用的功能。特定任务的实施以及它是否可扩展是另一回事。
每次可以读取单个项目,也可以每次读取大量数据并处理它们。因此,根据您的情况,后者可能是更好的选择。在这种情况下,时间效率也会更高。

于 2020-08-20T11:10:41.707 回答