
本文介绍在内存受限场景下,通过数据采样策略与生成器设计缓解深度学习模型灾难性遗忘问题,重点讲解如何构建跨文件均匀采样的数据生成器以保持模型对历史数据的记忆能力。
本文介绍在内存受限场景下,通过数据采样策略与生成器设计缓解深度学习模型灾难性遗忘问题,重点讲解如何构建跨文件均匀采样的数据生成器以保持模型对历史数据的记忆能力。
当训练数据规模远超内存容量时,常见的分块加载(chunked loading)策略若采用“逐文件训练”方式(即先训完文件A,再训文件B……),极易引发灾难性遗忘(Catastrophic Forgetting):模型在适配新数据分布的过程中,快速覆盖旧数据所习得的判别特征,最终性能仅反映最新批次(如最后500个样本)的统计特性,严重损害泛化能力。
根本原因在于:标准 model.fit() 对每个 .npy 文件独立调用,等价于顺序执行多个小型任务学习——这违背了监督学习的基本假设(训练样本应独立同分布,且遍历充分)。模型权重持续单向更新,缺乏对历史数据的周期性重访机制。
✅ 正确解法不是调低学习率或换优化器,而是重构数据供给逻辑,确保每轮训练批次(batch)都包含来自全部数据分片的代表性样本。以下为推荐实现:
1. 构建跨文件交错采样的生成器
import numpy as np
from tensorflow.keras.utils import Sequence
class ChunkedDataGenerator(Sequence):
def __init__(self, file_paths, batch_size=32, num_samples=None):
self.file_paths = file_paths
self.batch_size = batch_size
self.num_samples = num_samples or float('inf')
# 预加载所有 .npy 文件句柄(mmap 模式节省内存)
self.file_handles = [np.load(fp, mmap_mode='r') for fp in file_paths]
# 假设所有文件具有相同样本数(如每文件500条)
self.samples_per_file = self.file_handles[0]['array1'].shape[0]
self.num_files = len(self.file_handles)
# 总有效样本数 = min(指定总数, 所有文件总样本数)
self.total_available = self.num_files * self.samples_per_file
self.max_index = min(self.num_samples, self.total_available) if num_samples else self.total_available
def __len__(self):
return int(np.ceil(self.max_index / self.batch_size))
def __getitem__(self, index):
start_idx = index * self.batch_size
end_idx = min(start_idx + self.batch_size, self.max_index)
# 每个 batch 中的样本索引在 [0, samples_per_file) 内循环取
local_indices = np.arange(start_idx, end_idx) % self.samples_per_file
file_indices = (np.arange(start_idx, end_idx) // self.samples_per_file) % self.num_files
X_batch = np.empty((end_idx - start_idx, *self.file_handles[0]['array1'].shape[1:]))
y_batch = np.empty((end_idx - start_idx,), dtype=self.file_handles[0]['array2'].dtype)
for i, (local_i, f_i) in enumerate(zip(local_indices, file_indices)):
X_batch[i] = self.file_handles[f_i]['array1'][local_i]
y_batch[i] = self.file_handles[f_i]['array2'][local_i]
return X_batch, y_batch
def on_epoch_end(self):
# 可选:每轮结束打乱全局索引顺序(增强随机性)
pass
# 使用示例
train_generator = ChunkedDataGenerator(
file_paths=[f"{TRAINING_FOLDER}/{f}" for f in input_file_names],
batch_size=32,
num_samples=NUM_SAMPLES
)
model.fit(
train_generator,
epochs=EPOCHS,
verbose=2,
callbacks=[early_stopping, lr_schedule]
)2. 关键设计说明
- 均匀覆盖:每个 batch 包含来自不同文件的样本(如 batch[0] 来自文件0第i条,batch[1] 来自文件1第i条……),强制模型同步学习多源数据分布。
- 内存友好:全程使用 mmap_mode='r',仅在读取时按需加载页,不占用额外 RAM。
- 长度可控:通过 num_samples 精确控制总训练样本量,避免过拟合或截断。
- 兼容现代 Keras:Sequence 子类天然支持多进程(workers > 1)与自动批处理,比旧版 fit_generator() 更健壮。
⚠️ 注意事项
- 所有分块文件必须保证结构一致(相同 array1/array2 键名、相同样本维度、相同 dtype);
- 若各文件样本数不等,需在 __init__ 中动态计算每文件有效长度,并在 __getitem__ 中做边界检查;
- 避免在生成器内进行耗时预处理(如图像增强);建议提前离线完成,或使用 tf.data 进行流水线加速;
- 对于极大规模数据,可进一步结合 tf.data.Dataset.from_generator + cache().prefetch() 提升 I/O 效率。
该方案本质是将“顺序任务学习”转化为“在线随机采样”,在不增加显存压力的前提下,恢复了 SGD 的统计有效性,从根本上抑制灾难性遗忘——模型不再“学完就忘”,而是在持续流式输入中稳定收敛。










