分批训练过大的数据集

在深度学习中训练网络时,往往会出现训练数据过于庞大从而无法全部加载到内存中的情况,这里讲述的就是如何分批训练一个庞大的数据集,下面会以 Keras 中的训练为例进行讲述。

分批处理的思路就是先将那个较大的数据处理成若干个较小的数据文件(如共 1000000 条记录,处理成 1000 个小文件,每个小文件 1000 条记录),然后依次读取各个小的数据文件到内存中进行训练,这里的利用了 python 的 generator 特性来依次读取各个文件的内容。

如下代码所示,就是每次读取 num_files 个文件并合并成 X_trainY_train 并返回,直到整个目录下的文件都被遍历一遍。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def train_batch_generator(train_data_dir = './processed_data/train/', num_files = 1):
files = sorted(os.listdir(train_data_dir))
count = num_files
embeddings, labels = [], []
for file in files:
print('Reading file {0}...........'.format(file))
gc.collect()
with open(train_data_dir + file, 'rb') as rf:
data = pickle.load(rf)
embeddings.append(data['embedding'])
labels.append(data['label'])
count -= 1
if count == 0:
X_train, Y_train = np.concatenate(embeddings), np.concatenate(labels)
gc.collect()
count = num_files
embeddings, labels = [], []
yield (X_train, Y_train)

这样读取文件对应的训练方法如下(以 Keras 中的模型训练为例进行说明)

1
2
3
4
5
6
7
8
NUM_EPOCHS = 10
BATCH_SIZE = 32
for i in range(NUM_EPOCHS):
print('################{0} epochs#############'.format(i+1))
for x_train, y_train in train_batch_generator(num_files = 3):
print(x_train.shape, y_train.shape)
gc.collect()
model.fit(x_train, y_train, batch_size = BATCH_SIZE, epochs = 100, validation_data = (x_test, y_test))

另一种方法不需要将大文件分成若干个小文件,而是直接打开整个大文件逐行读取,然后读取了一定数目的行后通过 yield 返回, 这种方法的一个问题就是训练过程中必须要保持整个文件为打开状态,此时如果发生系统故障等异常可能会损坏文件,而将大文件分为若干的小文件则能够很大程度上避免这个问题。

这种方法的另外一个问题就是不能对训练样本进行的 shuffle,由于这深度神经网络的训练都是基于 SGD 模式的,因此需要对其训练样本进行 shuffle,具体可参考这个问题。但是当训练样本很大时,显然无法读取整个文件后在内存进行 shuffle,但是如果将大文件分成小文件后,可在系统内存能够承受的范围内对几个小文件进行 shuffle (如相邻的三个小文件),虽然这种 shuffle 是一种局部的 shuffle,但是可以通过改变进行 shuffle 的小文件的间隔并进行多次的 shuffle(如间隔分别从 0 递增),从而近似全局的 shuffle。