1

当我多次训练一个模型时,训练迭代会减慢,即使所有相关数量都是在for循环内创建的(因此每次都应该被覆盖,这应该足以避免创建不断增长的计算图或填满内存或任何)。

这是我使用的代码:

import numpy as np
import tensorflow as tf
import time

n_samples = 300000
n_features = 100
n_targets = 5
batch_size = 100
x = np.array(range(n_samples * n_features), dtype=np.float64).reshape((n_samples, n_features))
y = np.array(range(n_samples * n_targets), dtype=np.float64).reshape((n_samples, n_targets))
for t_idx in range(10):
    dataset = [x, y]
    dataset = tf.data.Dataset.from_tensor_slices(tuple(dataset)).shuffle(n_samples).repeat().batch(batch_size=batch_size).prefetch(tf.data.experimental.AUTOTUNE)
    data_iterator = iter(dataset)

    inputs = tf.keras.Input(shape=(n_features,), name='input')
    outputs = tf.keras.layers.Dense(n_features, name='dense_1', activation=tf.keras.activations.relu)(inputs)
    outputs = tf.keras.layers.Dense(n_features, name='dense_2', activation=tf.keras.activations.relu)(outputs)
    outputs = tf.keras.layers.Dense(n_features, name='dense_3', activation=tf.keras.activations.relu)(outputs)
    outputs = tf.keras.layers.Dense(n_features, name='dense_4', activation=tf.keras.activations.relu)(outputs)
    outputs = tf.keras.layers.Dense(n_targets, name='output', activation=tf.keras.activations.linear)(outputs)
    model = tf.keras.Model(inputs=inputs, outputs=outputs)

    trainable_variables = list(model.trainable_variables)

    adam_opt = tf.optimizers.Adam(learning_rate=0.001)


    @tf.function
    def loss(batch):
        x_, y_ = batch
        y_pred_ = model(x_)
        return tf.keras.losses.MSE(y_pred_, y_)


    @tf.function
    def optimization_step():
        batch = next(data_iterator)
        def f(): return loss(batch)
        adam_opt.minimize(f, var_list=trainable_variables)

    iterations = 50000
    loop_start = time.time()
    optimization_times = []
    for idx in range(iterations):
        optimization_step()

    loop_end = time.time()
    print(f'Elapsed: {loop_end - loop_start}')

我知道我可以model.fit()用来训练模型,但这不是我的选择(上面的代码是通过剥离更复杂的代码获得的,我唯一的选择是直接调用优化器)。

输出:

Elapsed: 49.798316955566406
Elapsed: 55.18571472167969
Elapsed: 58.57510209083557
Elapsed: 64.41855955123901
Elapsed: 66.76858448982239
Elapsed: 68.3305652141571
Elapsed: 67.73438382148743
Elapsed: 69.73751258850098
Elapsed: 73.59102845191956
Elapsed: 73.14124798774719

我预计每次训练所用的时间大致相同,但有明显的上升趋势。我究竟做错了什么?

系统信息

  • 操作系统:Windows 10
  • 蟒蛇版本:3.7.9
  • 张量流版本:2.3.1

编辑

在@Nicolas Gervais 和@M.Innat 的回复之后,我一直在不同机器上试验两个版本的代码(我的和@M.Innat 的)。这是我发现的:

  • 在我测试的两台单独的 Windows 机器上(相同的 Windows 版本、相同的包版本、相同的 python 版本、在干净的 virtualenv 上),结果存在很大差异;两者中最好的仍然显示出训练时间的上升趋势,但比我最初测试的要小;两者中最好的结果如下:
    WITH MY CODE:
    Elapsed: 49.35429096221924                                                                                                                                                                                                                   
    Elapsed: 52.551310777664185                                                                                                                                                                                                                  
    Elapsed: 54.324320554733276                                                                                                                                                                                                                  
    Elapsed: 56.53233051300049                                                                                                                                                                                                                   
    Elapsed: 56.81632399559021                                                                                                                                                                                                                   
    Elapsed: 58.70533752441406                                                                                                                                                                                                                   
    Elapsed: 59.68834161758423                                                                                                                                                                                                                   
    Elapsed: 61.419353008270264                                                                                                                                                                                                                  
    Elapsed: 60.33834195137024                                                                                                                                                                                                                   
    Elapsed: 62.536344051361084

    WITH @M.Innat CODE:
    Elapsed: 50.51127886772156                                                                                                                                                                                                                   
    Elapsed: 53.94429612159729                                                                                                                                                                                                                   
    Elapsed: 52.38828897476196                                                                                                                                                                                                                   
    Elapsed: 54.5512957572937                                                                                                                                                                                                                    
    Elapsed: 58.1543083190918                                                                                                                                                                                                                    
    Elapsed: 61.21232509613037                                                                                                                                                                                                                   
    Elapsed: 60.11531925201416                                                                                                                                                                                                                   
    Elapsed: 59.95942974090576                                                                                                                                                                                                                   
    Elapsed: 60.48531889915466                                                                                                                                                                                                                   
    Elapsed: 59.37330341339111
  • 如您所见,时间看起来比我最初在第二台 Windows 机器上发布的更稳定(但请注意,当我在用于初始测试的机器上进行测试时,它们基本相同);尽管如此,趋势仍然清晰可见;这很奇怪,因为@M.Innit 发布的结果中没有发生这种情况;
  • 在 Linux 或 Mac OS 上都不会出现此问题。

我开始觉得这是 Tensorflow 中的一个错误,不知何故(即使具有相同确切配置的不同机器产生如此不同的结果很奇怪),但我真的不知道将此行为归因于什么

4

2 回答 2

0

我没有确切的原因是什么主要原因。看起来它们可能是某种内存泄漏。在您的代码中,我对其进行了一些修改。尝试这个:

import numpy as np
import tensorflow as tf
import time, gc

tf.config.run_functions_eagerly(False)

n_samples = 300000
n_features = 100
n_targets = 5
batch_size = 100
x = np.array(range(n_samples * n_features), dtype=np.float64).reshape((n_samples, n_features))
y = np.array(range(n_samples * n_targets), dtype=np.float64).reshape((n_samples, n_targets))

def get_model():
    inputs = tf.keras.Input(shape=(n_features,), name='input')
    outputs = tf.keras.layers.Dense(n_features, name='dense_1', activation=tf.keras.activations.relu)(inputs)
    outputs = tf.keras.layers.Dense(n_features, name='dense_2', activation=tf.keras.activations.relu)(outputs)
    outputs = tf.keras.layers.Dense(n_features, name='dense_3', activation=tf.keras.activations.relu)(outputs)
    outputs = tf.keras.layers.Dense(n_features, name='dense_4', activation=tf.keras.activations.relu)(outputs)
    outputs = tf.keras.layers.Dense(n_targets, name='output', activation=tf.keras.activations.linear)(outputs)
    model = tf.keras.Model(inputs=inputs, outputs=outputs)
    return model

    
for t_idx in range(7):
    tf.keras.backend.clear_session()
    gc.collect()
    
    dataset = [x, y]
    dataset = tf.data.Dataset.from_tensor_slices(tuple(dataset)).shuffle(n_samples).repeat().batch(batch_size=batch_size).prefetch(tf.data.experimental.AUTOTUNE)
    data_iterator = iter(dataset)
    
    model = get_model()
    trainable_variables = list(model.trainable_variables)
    adam_opt = tf.optimizers.Adam(learning_rate=0.001)
    
    @tf.function
    def loss(batch):
        x_, y_ = batch
        y_pred_ = model(x_)
        return tf.keras.losses.MSE(y_pred_, y_)

    @tf.function
    def optimization_step():
        batch = next(data_iterator)
        def f(): return loss(batch)
        adam_opt.minimize(f, var_list=trainable_variables)

    iterations = 50000
    loop_start = time.time()
    
    for idx in range(iterations):
        optimization_step()

    print(f'Elapsed: {time.time() - loop_start}')
    
    del model, dataset

使用tf.config.run_functions_eagerly(False)时给出如下

Elapsed: 123.39986753463745
Elapsed: 121.13122296333313
Elapsed: 118.44610977172852
Elapsed: 116.83040761947632
Elapsed: 118.46350479125977
Elapsed: 118.59502696990967
Elapsed: 120.34098505973816

没有它给出如下

Elapsed: 114.46430349349976
Elapsed: 124.23725700378418
Elapsed: 126.14825916290283
Elapsed: 127.5985496044159
Elapsed: 126.79593586921692
Elapsed: 124.1206603050232
Elapsed: 125.85739850997925

虽然我认为这不是主要问题。可能还有更多需要深入了解的东西。

[我的操作系统:Windows 10 TF: 2.4.1,RTX:2070。]

于 2021-03-19T20:25:10.680 回答
0

您可能想尝试tf.keras.backend.clear_session(),如文档中所述,它解释了在循环中创建模型时内存消耗会发生什么:

如果您在循环中创建多个模型,则此全局状态将随着时间的推移消耗越来越多的内存,您可能需要清除它。调用 clear_session() 释放全局状态:这有助于避免旧模型和层造成混乱,尤其是在内存有限的情况下。

如果没有clear_session(),此循环的每次迭代都会略微增加 Keras 管理的全局状态的大小

for _ in range(100):
  model = tf.keras.Sequential([tf.keras.layers.Dense(10) for _ in range(10)])

clear_session()开始时调用,Keras 在每次迭代时都以空白状态开始,并且随着时间的推移内存消耗是恒定的。

for _ in range(100):
  tf.keras.backend.clear_session()
  model = tf.keras.Sequential([tf.keras.layers.Dense(10) for _ in range(10)])
于 2021-03-19T16:53:25.390 回答