完美解决TensorFlow和Keras大数据量内存溢出的问题


Posted in Python onJuly 03, 2020

内存溢出问题是参加kaggle比赛或者做大数据量实验的第一个拦路虎。

以前做的练手小项目导致新手产生一个惯性思维——读取训练集图片的时候把所有图读到内存中,然后分批训练。

其实这是有问题的,很容易导致OOM。现在内存一般16G,而训练集图片通常是上万张,而且RGB图,还很大,VGG16的图片一般是224x224x3,上万张图片,16G内存根本不够用。这时候又会想起——设置batch,但是那个batch的输入参数却又是图片,它只是把传进去的图片分批送到显卡,而我OOM的地方恰是那个“传进去”的图片,怎么办?

解决思路其实说来也简单,打破思维定式就好了,不是把所有图片读到内存中,而是只把所有图片的路径一次性读到内存中。

大致的解决思路为:

将上万张图片的路径一次性读到内存中,自己实现一个分批读取函数,在该函数中根据自己的内存情况设置读取图片,只把这一批图片读入内存中,然后交给模型,模型再对这一批图片进行分批训练,因为内存一般大于等于显存,所以内存的批次大小和显存的批次大小通常不相同。

下面代码分别介绍Tensorflow和Keras分批将数据读到内存中的关键函数。Tensorflow对初学者不太友好,所以我个人现阶段更习惯用它的高层API Keras来做相关项目,下面的TF实现是之前不会用Keras分批读时候参考的一些列资料,在模型训练上仍使用Keras,只有分批读取用了TF的API。

Tensorlow

在input.py里写get_batch函数。

def get_batch(X_train, y_train, img_w, img_h, color_type, batch_size, capacity):
  '''
  Args:
    X_train: train img path list
    y_train: train labels list
    img_w: image width
    img_h: image height
    batch_size: batch size
    capacity: the maximum elements in queue
  Returns:
    X_train_batch: 4D tensor [batch_size, width, height, chanel],\
            dtype=tf.float32
    y_train_batch: 1D tensor [batch_size], dtype=int32
  '''
  X_train = tf.cast(X_train, tf.string)

  y_train = tf.cast(y_train, tf.int32)
  
  # make an input queue
  input_queue = tf.train.slice_input_producer([X_train, y_train])

  y_train = input_queue[1]
  X_train_contents = tf.read_file(input_queue[0])
  X_train = tf.image.decode_jpeg(X_train_contents, channels=color_type)

  X_train = tf.image.resize_images(X_train, [img_h, img_w], 
                   tf.image.ResizeMethod.NEAREST_NEIGHBOR)

  X_train_batch, y_train_batch = tf.train.batch([X_train, y_train],
                         batch_size=batch_size,
                         num_threads=64,
                         capacity=capacity)
  y_train_batch = tf.one_hot(y_train_batch, 10)

  return X_train_batch, y_train_batch

在train.py文件中训练(下面不是纯TF代码,model.fit是Keras的拟合,用纯TF的替换就好了)。

X_train_batch, y_train_batch = inp.get_batch(X_train, y_train, 
                       img_w, img_h, color_type, 
                       train_batch_size, capacity)
X_valid_batch, y_valid_batch = inp.get_batch(X_valid, y_valid, 
                       img_w, img_h, color_type, 
                       valid_batch_size, capacity)
with tf.Session() as sess:

  coord = tf.train.Coordinator()
  threads = tf.train.start_queue_runners(coord=coord)
  try:
    for step in np.arange(max_step):
      if coord.should_stop() :
        break
      X_train, y_train = sess.run([X_train_batch, 
                       y_train_batch])
      X_valid, y_valid = sess.run([X_valid_batch,
                       y_valid_batch])
       
      ckpt_path = 'log/weights-{val_loss:.4f}.hdf5'
      ckpt = tf.keras.callbacks.ModelCheckpoint(ckpt_path, 
                           monitor='val_loss', 
                           verbose=1, 
                           save_best_only=True, 
                           mode='min')
      model.fit(X_train, y_train, batch_size=64, 
             epochs=50, verbose=1,
             validation_data=(X_valid, y_valid),
             callbacks=[ckpt])
      
      del X_train, y_train, X_valid, y_valid

  except tf.errors.OutOfRangeError:
    print('done!')
  finally:
    coord.request_stop()
  coord.join(threads)
  sess.close()

Keras

keras文档中对fit、predict、evaluate这些函数都有一个generator,这个generator就是解决分批问题的。

关键函数:fit_generator

# 读取图片函数
def get_im_cv2(paths, img_rows, img_cols, color_type=1, normalize=True):
  '''
  参数:
    paths:要读取的图片路径列表
    img_rows:图片行
    img_cols:图片列
    color_type:图片颜色通道
  返回: 
    imgs: 图片数组
  '''
  # Load as grayscale
  imgs = []
  for path in paths:
    if color_type == 1:
      img = cv2.imread(path, 0)
    elif color_type == 3:
      img = cv2.imread(path)
    # Reduce size
    resized = cv2.resize(img, (img_cols, img_rows))
    if normalize:
      resized = resized.astype('float32')
      resized /= 127.5
      resized -= 1. 
    
    imgs.append(resized)
    
  return np.array(imgs).reshape(len(paths), img_rows, img_cols, color_type)

获取批次函数,其实就是一个generator

def get_train_batch(X_train, y_train, batch_size, img_w, img_h, color_type, is_argumentation):
  '''
  参数:
    X_train:所有图片路径列表
    y_train: 所有图片对应的标签列表
    batch_size:批次
    img_w:图片宽
    img_h:图片高
    color_type:图片类型
    is_argumentation:是否需要数据增强
  返回: 
    一个generator,x: 获取的批次图片 y: 获取的图片对应的标签
  '''
  while 1:
    for i in range(0, len(X_train), batch_size):
      x = get_im_cv2(X_train[i:i+batch_size], img_w, img_h, color_type)
      y = y_train[i:i+batch_size]
      if is_argumentation:
        # 数据增强
        x, y = img_augmentation(x, y)
      # 最重要的就是这个yield,它代表返回,返回以后循环还是会继续,然后再返回。就比如有一个机器一直在作累加运算,但是会把每次累加中间结果告诉你一样,直到把所有数加完
      yield({'input': x}, {'output': y})

训练函数

result = model.fit_generator(generator=get_train_batch(X_train, y_train, train_batch_size, img_w, img_h, color_type, True), 
     steps_per_epoch=1351, 
     epochs=50, verbose=1,
     validation_data=get_train_batch(X_valid, y_valid, valid_batch_size,img_w, img_h, color_type, False),
     validation_steps=52,
     callbacks=[ckpt, early_stop],
     max_queue_size=capacity,
     workers=1)

就是这么简单。但是当初从0到1的过程很难熬,每天都没有进展,没有头绪,急躁占据了思维的大部,熬过了这个阶段,就会一切顺利,不是运气,而是踩过的从0到1的每个脚印累积的灵感的爆发,从0到1的脚印越多,后面的路越顺利。

以上这篇完美解决TensorFlow和Keras大数据量内存溢出的问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
django实现用户登陆功能详解
Dec 11 Python
Python 在字符串中加入变量的实例讲解
May 02 Python
python2.x实现人民币转大写人民币
Jun 20 Python
详解python3中tkinter知识点
Jun 21 Python
python 除法保留两位小数点的方法
Jul 16 Python
CentOS 7下安装Python3.6 及遇到的问题小结
Nov 08 Python
Python 输入一个数字判断成绩分数等级的方法
Nov 15 Python
浅谈pycharm的xmx和xms设置方法
Dec 03 Python
Django unittest 设置跳过某些case的方法
Dec 26 Python
python打包exe开机自动启动的实例(windows)
Jun 28 Python
Python使用configparser读取ini配置文件
May 25 Python
Python爬取用户观影数据并分析用户与电影之间的隐藏信息!
Jun 29 Python
Keras 在fit_generator训练方式中加入图像random_crop操作
Jul 03 #Python
keras的三种模型实现与区别说明
Jul 03 #Python
Keras中 ImageDataGenerator函数的参数用法
Jul 03 #Python
python程序如何进行保存
Jul 03 #Python
keras的ImageDataGenerator和flow()的用法说明
Jul 03 #Python
python如何安装下载后的模块
Jul 03 #Python
python中id函数运行方式
Jul 03 #Python
You might like
PHP初学入门
2006/11/19 PHP
PHP父类调用子类方法的代码例子
2014/04/09 PHP
Yii框架实现对数据库的CURD操作示例
2019/09/03 PHP
laravel 5.3 单用户登录简单实现方法
2019/10/14 PHP
解决iframe的frameborder在chrome/ff/ie下的差异
2010/08/12 Javascript
jQuery 获取URL的GET参数值的小例子
2013/04/18 Javascript
JS 两个字符串时间的天数差计算
2013/08/25 Javascript
jquery 倒计时效果实现秒杀思路
2013/09/11 Javascript
js中call与apply的用法小结
2013/12/28 Javascript
JavaScript使用concat连接数组的方法
2015/04/06 Javascript
js实现一个链接打开两个链接地址的方法
2015/05/12 Javascript
jQuery跨域问题解决方案
2015/08/03 Javascript
JavaScript实现简单的日历效果
2016/09/25 Javascript
JS使用tofixed与round处理数据四舍五入的区别
2017/10/25 Javascript
使用Vue的slot插槽分发父组件内容实现高度复用、更加灵活的组件(推荐)
2018/05/01 Javascript
vue实现word,pdf文件的导出功能
2018/07/31 Javascript
boostrap模态框二次弹出清空原有内容的方法
2018/08/10 Javascript
vue--点击当前增加class,其他删除class的方法
2018/09/15 Javascript
vue+elementUI动态生成面包屑导航教程
2019/11/04 Javascript
vue商城中商品“筛选器”功能的实现代码
2020/07/01 Javascript
[02:05]2014DOTA2西雅图国际邀请赛 BBC第二天小组赛总结
2014/07/11 DOTA
合并Excel工作薄中成绩表的VBA代码,非常适合教育一线的朋友
2009/04/09 Python
Django视图之ORM数据库查询操作API的实例
2017/10/27 Python
利用Python在一个文件的头部插入数据的实例
2018/05/02 Python
解决使用PyCharm时无法启动控制台的问题
2019/01/19 Python
浅谈Python 列表字典赋值的陷阱
2019/01/20 Python
Python3.5文件修改操作实例分析
2019/05/01 Python
Django对数据库进行添加与更新的例子
2019/07/12 Python
解决python DataFrame 打印结果不换行问题
2020/04/09 Python
Python爬虫爬取微博热搜保存为 Markdown 文件的源码
2021/02/22 Python
解释一下抽象方法和抽象类
2016/08/27 面试题
配置管理计划的主要内容有哪些
2014/06/20 面试题
大学生毕业求职简历的自我评价
2013/10/24 职场文书
甜点店创业计划书
2014/01/27 职场文书
学习雷锋主题班会
2015/08/14 职场文书
Win10 和 Win11可以共存吗? win10/11产品生命周期/服务更新介绍
2021/11/21 数码科技