使用生成器进行 model.fit 期间内存应该这么高吗?

Bil*_*Kid 5 python keras tensorflow

我仍然可以重新创建此行为的张量流版本是:2.7.0, 2.7.3, 2.8.0, 2.9.0。其实这些都是我尝试过的版本;我无法在任何版本中解决该问题。

  • 操作系统:Ubuntu 20
  • 显卡:RTX 2060
  • 内存:16GB

我正在尝试使用生成器将数据输入模型:

class DataGen(tf.keras.utils.Sequence):
    def __init__(self, indices, batch_size):
        self.X = X
        self.y = y
        self.indices = indices
        self.batch_size = batch_size
    
    def __getitem__(self, index):
        X_batch = self.X[self.indices][
            index * self.batch_size : (index + 1) * self.batch_size
        ]
        y_batch = self.y[self.indices][
            index * self.batch_size : (index + 1) * self.batch_size
        ]
        return X_batch, y_batch
    
    def __len__(self):
        return len(self.y[self.indices]) // self.batch_size

train_gen = DataGen(train_indices, 32)
val_gen = DataGen(val_indices, 32)
test_gen = DataGen(test_indices, 32)
Run Code Online (Sandbox Code Playgroud)

其中X,是使用 、y从文件加载的数据集,, ,是将在和上使用的每个集合的索引。.h5h5pytrain_indicesval_indicestest_indicesXy

我正在创建模型并使用以下方式提供数据:

# setup model
base_model = tf.keras.applications.MobileNetV2(input_shape=(128, 128, 3),
                                                include_top=False)
base_model.trainable = False

mobilenet1 = Sequential([
    base_model,
    Flatten(),
    Dense(27, activation='softmax')
])

mobilenet1.compile(optimizer=tf.keras.optimizers.Adam(),
                   loss=tf.keras.losses.CategoricalCrossentropy(),
                   metrics=['accuracy'])
# model training
hist_mobilenet = mobilenet1.fit(train_gen, validation_data=val_gen, epochs=1)
Run Code Online (Sandbox Code Playgroud)

训练前的记忆值为 8%,但训练开始后,它开始获取从 30% 到 60% 的值。由于我使用生成器并一次加载 32 个观测值的一小部分数据,因此内存爬升这么高对我来说似乎很奇怪。此外,即使训练停止,记忆力仍保持在 30% 以上。我检查了所有全局变量,但没有一个有这么大的大小。如果我开始另一个训练课程,内存就会开始具有更高的使用值,最终 jupyter 笔记本内核会崩溃。

我的实现有问题还是这是正常的?

编辑1:一些附加信息。

  • 每当训练停止时,内存使用量都会下降一点,但我可以通过调用垃圾收集器来进一步减少它。然而,即使我删除了 fit 创建的历史记录,我也无法将其恢复到 8%
  • x 和 y 批次的大小总和为 48 字节;这让我很愤怒!为什么一次加载 48 个数据会导致内存使用量增加这么多?据说我使用 HDF5 数据集能够在不使 RAM 过载的情况下处理数据。我想到的下一件事是fit创建一些变量,但它需要这么多 GB 的内存来存储它们是没有意义的

Bil*_*Kid 3

如何最大限度地减少 RAM 使用

从其他朋友的非常有用的评论和回答中,我得出了这样的结论:

  • 首先,我们必须将数据保存到 HDF5 文件,这样我们就不必将整个数据集加载到内存中。
import h5py as h5
import gc
file = h5.File('data.h5', 'r')
X = file['X']
y = file['y']
gc.collect()
Run Code Online (Sandbox Code Playgroud)

我使用垃圾收集器只是为了安全起见。

  • 然后,我们不必将数据传递给生成器,因为 X 和 y 对于训练、验证和测试来说是相同的。为了区分不同的数据,我们将使用索引图
# split data for validation and testing
val_split, test_split = 0.2, 0.1

train_indices = np.arange(len(X))[:-int(len(X) * (val_split + test_split))]
val_indices = np.arange(len(X))[-int(len(X) * (val_split + test_split)) : -int(len(X) * test_split)]
test_indices = np.arange(len(X))[-int(len(X) * test_split):]


class DataGen(tf.keras.utils.Sequence):
    def __init__(self, index_map, batch_size):
        self.X = X
        self.y = y
        self.index_map = index_map
        self.batch_size = batch_size
    
    def __getitem__(self, index):
        X_batch = self.X[self.index_map[
            index * self.batch_size : (index + 1) * self.batch_size
        ]]
        y_batch = self.y[self.index_map[
            index * self.batch_size : (index + 1) * self.batch_size
        ]]
        return X_batch, y_batch
    
    def __len__(self):
        return len(self.index_map) // self.batch_size

train_gen = DataGen(train_indices, 32)
val_gen = DataGen(val_indices, 32)
test_gen = DataGen(test_indices, 32)
Run Code Online (Sandbox Code Playgroud)
  • 最后要注意的是我如何实现内部数据获取__getitem__

正确的解决方案:

X_batch = self.X[self.index_map[
            index * self.batch_size : (index + 1) * self.batch_size
        ]]
Run Code Online (Sandbox Code Playgroud)

错误的解决方案:

X_batch = self.X[self.index_map][
            index * self.batch_size : (index + 1) * self.batch_size
        ]
Run Code Online (Sandbox Code Playgroud)

y 也一样

注意到区别了吗?在错误的解决方案中,我将整个数据集(训练、验证或测试)加载到内存中!相反,在正确的解决方案中,我仅加载用于在方法中提供的批次fit

通过此设置,我设法将 RAM 仅提高到 2.88 GB,这非常酷!