很多教程说用更小的样本数或者换显存更大的硬件。。。其实是治标不治本。
如果你是直接将全部训练样本以np.narray数组的形式输入fit,这种情况下的内存不够,即使使用更小的batchsize,也不会有明显的改善。
先说解决方案——那就是官方自定义的数据格式tfrecords.
在这种格式下,每次只从存储硬盘中读取一个batchsize的数据入内存(显存),而不是将整个训练样本一次性全部读入,因此可以大大减小内存开支,但会牺牲小部分IO性能。
这种方式是一般机器学习最基本的方式,但是仅仅适用于数据量较小的情况。
数据加载方式是自己在内存中直接创建数据,省去了每个batch从硬盘到内存这个步骤。
但是此方法受到内存大小的严格限制,对于内存放不下的大数据无法使用此方法。
这种方式就需要每次从硬盘存一个batch的数据到内存中,然后送入到占位符中,在图中进行计算。这种方式的优势在于训练过程只需要一个batch的内存,并且如果你暂时不熟悉tensorflow相关的数据才做,可以利用python相关的库处理之后再送入到占位符中。
但是这种方式也有很大缺陷,就是当数据规模较大的时候,也会出现数据传输的较大时间损耗,效率偏低。这里的传输只从python读入的数据传入到图中计算的数据,这之间存在一个数据转换的问题。
tensorflow内置了三种格式的文件读取管道,这种文件读取流程是如上图所示的高效率的双队列机制。
其中,读取数据的线程不断将文件系统中的数据读入,另一个线程则是负责计算,计算需要的数据直接从内存中取即可。在TF中还提供一种高效的数据管理机制,双队列机制。在内存队列之前有一个文件名队列,更好的管理epoch。综上所述,在这里讨论一种适用于大规模数据的方法。
这三种文件格式分别为:
tf.data.TFRecordDataset、tf.data.TextLineDataset、tf.contrib.data.CsvDataset
针对我们的高维数据类型,我们采用TFRecordDataset格式。
tfrecords的存储方式为:将所有(float, int, bytes等)格式数据全部转为string格式的字符串数据;读取时再根据原数据格式反编译。
具体可参考:
Tensorflow官网: https://tensorflow.google.cn/tutorials/load_data/tfrecord
知乎相关文章: https://zhuanlan.zhihu.com/p/363999842
登录后复制
# 将根据数据的具体格式进行编译,并存储为tfrecords格式文件。
# 其中float格式编译为float_list,字符串、高维array等格式直接编译为bytes_list格式。
def save_tfrecords(data, label, desfile):
with tf.io.TFRecordWriter(desfile) as writer:
for i in range(len(data)):
features = tf.train.Features(
feature = {
"data":tf.train.Feature(bytes_list = tf.train.BytesList(value = [tf.io.serialize_
tensor(data[i]).numpy()])),
"label":tf.train.Feature(float_list = tf.train.FloatList(value = label[i])),
}
)
example = tf.train.Example(features = features)
serialized = example.SerializeToString()
writer.write(serialized)
可以将样本和标签拆分后分块保存:
登录后复制
save_tfrecords(x_in_sample1,y_in_sample1, "path1.tfrecords")
save_tfrecords(x_in_sample2,y_in_sample2, "path2.tfrecords")
登录后复制
################### TFR数据反编译
def map_func(example):
feature_description = {
'data': tf.io.FixedLenFeature([], tf.string),
'label': tf.io.FixedLenFeature([], tf.float32),
}
parsed_example = tf.io.parse_single_example(example, features=feature_description)
x_sample = tf.io.parse_tensor(parsed_example['data'], tf.float32)
y_sample = parsed_example['label']
return x_sample, y_sample
################### 加载数据集
def load_dataset(filepaths):
shuffle_buffer_size = 3000
batch_size = 256
dataset = tf.data.TFRecordDataset(filepaths)
dataset = dataset.shuffle(shuffle_buffer_size)
dataset = dataset.map(map_func=map_func, num_parallel_calls= 8)
dataset = dataset.batch(batch_size).prefetch(64)
return dataset
train_set = load_dataset(["path1.tfrecords","path2.tfrecords"])
valid_set = load_dataset(["path3.tfrecords","path4.tfrecords"])
################### 模型部分
hist = model.fit(train_set,epochs=model_epochs, validation_data=valid_set, callbacks=[early_stopping])
至此,完成。
牺牲了小部分数据IO的性能,换来了仅需很小内存开支的模型大样本训练。
小tips:
使用了tfrecords后,训练模型所需要的内存大大减少,因此,为了充分利用GPU资源,可以手动将GPU的内存分割,从而用一个GPU同时训练多个模型。
GPU内存获取限制代码如下,这里设置memory_limit=1024:
登录后复制
gpus = tf.config.experimental.list_physical_devices('GPU')
if gpus:
# Restrict TensorFlow to only allocate 1GB of memory on the first GPU
try:
tf.config.experimental.set_virtual_device_configuration(
gpus[0],
[tf.config.experimental.VirtualDeviceConfiguration(memory_limit=1024)])
logical_gpus = tf.config.experimental.list_logical_devices('GPU')
print(len(gpus), "Physical GPUs,", len(logical_gpus), "Logical GPUs")
except RuntimeError as e:
# Virtual devices must be set before GPUs have been initialized
print(e)
在每个模型程序中,加入此代码,设置memory_limit,即可在一个GPU中,同时训练多个模型。
可以看到,这里同时跑了4个模型,每个模型使用2099M内存(虽然设置中仅为memory_limit=1024,但实际运行中可能会多1一个G左右,这个需要注意一下),且将GPU算力用到了100%,充分利用了GPU的资源。
免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删