TensorFlow数据集操作(使用slim和tfrecord)

在卷积神经网络中,输入的图像数据集都非常大,而且与其他数据不同,图像都需要以三维张量(height,width,channel)形式表示,这样使得神经网络读取数据非常麻烦。在TensorFlow框架中,有一种用的非常多的方法来处理数据集,就是tfrecord文件,它与TensorFlow的中层封装库slim搭配使用起来非常方便,下面我以DeepLabv3+中的数据处理代码为例解释一下该方法。

该数据处理过程主要包括三步:

  1. 读入图像和标签数据,并转化为tfrecord格式
  2. 使用slim库解码tfrecord文件,生成描述数据集信息的dataset
  3. 根据dataset得到相对应的图像和标签数据,并组成batch格式,输入队列

1.读入图像和标签数据,并转化为tfrecord格式

DeepLabv3+模型使用的是分割与检测数据集PASCAL VOC2012,在这里只使用它的分割部分在它的VOC主目录下包括5个文件夹,如图1所示。其中JPEGImages文件夹下是所有的原始图像image;SegmentationClass文件夹下是与图像相对应的标签label;ImageSets文件夹如图2所示,其中Segmentation文件夹下为输入图像的4个文件名索引文件,其内容如图4所示
在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述
下面的代码为从前面的图像和标签数据生成tfrecord的过程(源程序的简化版,省略了图像高和宽的计算过程与example中生成整型和字符串型属性的程序)。从数据集生成tfrecord文件主要步骤为:(1)读入图像文件名的索引文件,即生成索引列表;(2)将索引列表所指代的图像声明分别由多个tfrecord文件存储;(3)读取图像和标签文件;(4)将图像和标签的像素值,文件类型,图像通道数等重要信息通过tfrecord的数据结构存储。

import math
import os.path
import sys
import tensorflow as tf

dataset_splits = tf.gfile.Glob(os.path.join('./VOCdevkit/VOC2012/ImageSets/Segmentation', '*.txt')) # 查找匹配该路径名的文件(即前面提到的4个图像索引文件)
for dataset_split in dataset_splits:
	dataset = os.path.basename(dataset_split)[:-4] # 返回路径dataset_splits所对应的文件名,并且去除扩展名.txt
	filenames = [x.strip('\n') for x in open(dataset_split, 'r')] # 打开索引文件,并生成索引文件名列表
	num_images = len(filenames) # 数据集中图像的数量即索引列表的长度
	num_per_shard = int(math.ceil(num_images /4.0)) # 将图像均匀分成4份,输入4个tfrecord文件
	for shard_id in range(4):
		output_filename = os.path.join(./tfrecords,
        '%s-%05d-of-%05d.tfrecord' % (dataset, shard_id, _NUM_SHARDS)) # 4个输出的tfrecord文件的输出路径
        with tf.python_io.TFRecordWriter(output_filename) as tfrecord_writer: # 向output_filename文件夹写入tfrecord文件
        	start_idx = shard_id * num_per_shard # 每个tfrecord文件存储的起始图像索引
        	end_idx = min((shard_id + 1) * num_per_shard, num_images) # 每个tfrecord文件存储的结束图像索引;对于第四个tfrecord文件,可能仅保存剩余的图像。
        	for i in range(start_idx, end_idx):
       			# 读取图像文件.
        		image_filename = os.path.join(
            	'./VOCdevkit/VOC2012/JPEGImages', filenames[i] + '.' + 'jpeg' ) # 图像路径
        		image_data = tf.gfile.FastGFile(image_filename, 'rb').read() # FastGFile函数读取图像
        		# 读取标签文件
        		seg_filename = os.path.join(
            	FLAGS.semantic_segmentation_folder,
            	filenames[i] + '.' + FLAGS.label_format) # 标签图像路径
        		seg_data = tf.gfile.FastGFile(seg_filename, 'rb').read() # 读取标签
        		# 保存为tfrecord的example格式
        		example = tf.train.Example(features=tf.train.Features(feature={
      				'image/encoded': _bytes_list_feature(image_data),
     			    'image/filename': _bytes_list_feature(filename),
      				'image/format': _bytes_list_feature(_IMAGE_FORMAT_MAP[FLAGS.image_format]),
      				'image/channels': _int64_list_feature(3),
      				'image/segmentation/class/encoded': (_bytes_list_feature(seg_data)),
      				'image/segmentation/class/format': _bytes_list_feature(FLAGS.label_format)})) # tfrecord以字符串形式保存图像像素值,文件名,图像格式,通道数,标签像素值,标签格式
      	tfrecord_writer.write(example.SerializeToString()) # 用字符串形式存储example信息      	

2.使用slim库解码tfrecord文件,生成描述数据集信息的dataset

在生成tfrecord文件后,我们使用slim库来对其进行解读,生成slim中的一种数据结构dataset数据集。需要注意的是这里的dataset并不是真正的包含图像和标签的数据集,而是存储着数据集重要信息的一种数据结构,我们可以根据它一个一个地解码图像和标签。下面的代码是具体的生成dataset的过程,通过slim库定义了tfrecord文件的格式转换方式(注意这里并没有对其进行真正的格式转换,仅仅定义了这种方式),通过解码器对象decoder保存转换方式,后面再进行解码转换。

import os.path
import tensorflow as tf

slim = tf.contrib.slim
dataset = slim.dataset
tfexample_decoder = slim.tfexample_decoder

 file_pattern = '%s-*  '# 前面保存的tfrecord文件的文件名类似于“train-00001-of-00004.tfrecord”
 file_pattern = os.path.join(dataset_dir, file_pattern % split_name)  # dataset_dir即前面保存的tfrecord文件的路径

# 使用slim中的函数tf.FixedLenFeature将tfrecord的example反序列化成存储之前的格式,
# 字符串格式的用''表示,整型格式的用0表示,其他确定的信息还原为原来的形式,如'jpeg','png'
keys_to_features = {
     'image/encoded': tf.FixedLenFeature(
          (), tf.string, default_value=''),
     'image/filename': tf.FixedLenFeature(
          (), tf.string, default_value=''),
     'image/format': tf.FixedLenFeature(
          (), tf.string, default_value='jpeg'),
     'image/height': tf.FixedLenFeature(
          (), tf.int64, default_value=0),
     'image/width': tf.FixedLenFeature(
          (), tf.int64, default_value=0),
     'image/segmentation/class/encoded': tf.FixedLenFeature(
          (), tf.string, default_value=''),
     'image/segmentation/class/format': tf.FixedLenFeature(
          (), tf.string, default_value='png')}
# 将反序列化的数据重组为更适合网络读入的格式
items_to_handlers = {
      'image': tfexample_decoder.Image(
          image_key='image/encoded',
          format_key='image/format',
          channels=3),
      'image_name': tfexample_decoder.Tensor('image/filename'),
      'height': tfexample_decoder.Tensor('image/height'),
      'width': tfexample_decoder.Tensor('image/width'),
      'labels_class': tfexample_decoder.Image(
          image_key='image/segmentation/class/encoded',
          format_key='image/segmentation/class/format',
          channels=1)}
# 解码器进行解码,定义一个解码器对象,保存到dataset中
decoder = tfexample_decoder.TFExampleDecoder(
      keys_to_features, items_to_handlers)
# 返回由tfrecord信息所得到的数据集dataset,dataset对象定义了数据集的文件位置,解码方式等元信息
dataset = dataset.Dataset(
      data_sources=file_pattern,  # tfrecord路径
      reader=tf.TFRecordReader,   # 读取tfrecord文件的方式
      decoder=decoder,            # 解码tfrecord文件的方式
      num_samples=1464,           # PASCAL-VOC2012数据集训练样本数
      items_to_descriptions={     # 样本集图像和标签描述
      		'image': 'A color image of varying height and width.',
      		'labels_class': ('A semantic segmentation label whose size matches image.'
                     		 'Its values range from 0 (background) to num_classes.')}
      ignore_label=ignore_label,  # 忽略部分标签
      num_classes=21,             # 数据集包含类别数(20个前景类别和1个背景类别)
      multi_label=True)           # 多标签(具体我也不太清楚)

3.根据dataset得到相对应的图像和标签数据,并组成batch格式,输入队列

下面的代码是从dataset将数据集组合为batch并且输入队列的过程,该过程主要分为以下几个步骤:(1)创建一个DatasetDataProvider类的对象data_provider,该对象中存储着解析tfrecord文件的详细信息,通过调用其get函数可以得到解读的图像,标签,图像文件名,图像宽和高等。(2)图像预处理,在将图像送入网络前的图像预处理过程一般在此处进行,包括图像裁剪,亮度调整,镜像翻转等。(3)将处理后的图像通过tf.train.batch函数组合为多个batch,然后通过slim库中的prefetch_queue函数输入队列,然后就可以作为整个网络的输入。需要注意的是,dataset通过get函数对tfrecord文件进行解析的时候返回的为一个样例,即一幅图像及其标签。

import tensorflow as tf
from deeplab import common
from deeplab import input_preprocess

slim = tf.contrib.slim
dataset_data_provider = slim.dataset_data_provider
prefetch_queue = slim.prefetch_queue

# 创建一个DatasetDataProvider类的对象data_provider,根据dataset和其他的一些已知信息读取数据。
data_provider = dataset_data_provider.DatasetDataProvider(
      dataset,
      num_readers=1,
      num_epochs=None,
      shuffle=True)
# 通过调用data_provider对象的get实例函数能够根据data_provider中给出的信息解读tfrecord文件,生成图像和标签和图像文件名
image, height, width = data_provider.get(['image', 'height', 'width'])
image_name, = data_provider.get(['image_name'])
label = data_provider.get(['label'])
# 图像预处理过程,这里具体的处理过程与本文主题无关,因此省略具体的处理过程
original_image, image, label = input_preprocess.preprocess_image_and_label(
      image,
      label,
      crop_height=crop_size[0],                        # 裁剪后图像高度
      crop_width=crop_size[1],                         # 裁剪后图像宽度
      min_resize_value=min_resize_value,               # 对原图进行放缩的最小值
      max_resize_value=max_resize_value,               # 对原图进行放缩的最大值
      resize_factor=resize_factor,                     # 对原图的放缩倍数
      min_scale_factor=min_scale_factor,               # 最小放缩倍数
      max_scale_factor=max_scale_factor,               # 最大放缩倍数
      scale_factor_step_size=scale_factor_step_size,   # 每迭代多少步对图像进行一次放缩
      is_training=is_training,                         # 是否处于训练阶段
      model_variant=model_variant)                     # 网络模型选择
# 将样本图像,图像名称,高和宽组合为适合网络读入的字典形式
sample = {'image': image, 'image_name': image_name, 'height': height, 'width': width}
# 将一个batch的样本的图像和标签打包
samples = tf.train.batch(
      sample,
      batch_size=8,
      num_threads=1,
      capacity=32 * 8,
      allow_smaller_final_batch=False,
      dynamic_pad=True)
# 将打包好的数据集存入队列,数据集准备过程结束
inputs_queue = prefetch_queue.prefetch_queue(samples, capacity=128 * num_clones) # num_clones代表网络训练时的gpu数量

本文所述的过程中thread值等于1,即只使用了单线程,在实际的DeepLab程序训练过程中都是使用多线程的,这就需要用到tf.Coordinator和tf.QueueRunner两个类来完成多线程协同,这个不是本文所涉及的内容。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: TensorFlow 2于2019年发布,相对于之前的版本,进行了重大的改进和升级。因此,在TensorFlow 2中,一些旧的功能和包已经被删除或者合并到了其他的模块中,其中就包括了contrib_slimTensorFlow的contrib_slim包是一个非常常用的工具包,用于简化神经网络的构建和训练过程。它提供了许多高级的操作和函数,为用户提供了更高的灵活性和便利性。然而在TensorFlow 2中,由于追求简化和统一的目标,TensorFlow团队决定将其删除。 在TensorFlow 2中,取代contrib_slim的是tf.keras模块,它是一个基于Keras的高级API。tf.keras提供了与contrib_slim类似的功能,并且具有更好的兼容性和易用性。通过tf.keras,您可以更容易地构建和训练神经网络模型。它提供了丰富的层和模型类型,并且支持许多常见的深度学习任务,如图像分类、目标检测、自然语言处理等。 因此,在TensorFlow 2中,如果您需要类似于contrib_slim的功能,可以使用tf.keras来代替。您可以使用tf.keras.layers构建网络层,使用tf.keras.models构建模型,以及使用tf.keras.optimizers、tf.keras.losses等来定义优化器和损失函数。通过这种方式,您可以轻松构建和训练自己的深度学习模型。 总的来说,TensorFlow 2取消了contrib_slim包,转而使用tf.keras模块来提供更好的API和功能。这是为了提供更简单、更统一和更高效的开发体验。 ### 回答2: TensorFlow 2 是 TensorFlow 的最新版本,与之前的 TensorFlow 1 有很大的不同。TensorFlow 2 根据用户反馈和需求进行了重构,删除了一些不常用的功能和模块,其中就包括 contrib.slim 包。 contrib.slim 包是 TensorFlow 1 中的一个非常有用的模块,提供了许多方便的函数和工具,用于构建和训练深度学习模型。但是,由于它的功能被整合到 TensorFlow 2 的核心模块中,contrib.slim 包在 TensorFlow 2 中被删除。 在 TensorFlow 2 中,大部分 contrib.slim 包中的功能都可以通过其他的模块和函数来实现。例如,构建模型可以使用 Keras API,它提供了更简洁、易用的接口。此外,一些 contrib.slim 包中的函数可以通过使用 TensorFlow 2 的其他核心函数来替代。 如果你在迁移你的代码或项目到 TensorFlow 2 时遇到 contrib.slim 包的问题,你可以参考 TensorFlow 2 的官方文档和示例代码,了解如何使用新的模块和函数来替代 contrib.slim 包中的功能。此外,TensorFlow 社区也提供了许多迁移指南和教程,帮助用户迁移他们的代码到 TensorFlow 2。 总而言之,虽然 TensorFlow 2 中没有 contrib.slim 包,但是通过使用其他的模块和函数,你仍然可以实现相同的功能,并享受 TensorFlow 2 带来的新特性和改进。 ### 回答3: tensorflow2版本中没有contrib_slim包,这是因为从tensorflow1.x到tensorflow2.0的升级过程中,一些模块被重新组织和重构了。contrib_slim是在tensorflow1.x版本中引入的一个扩展模块,用于提供一些高级的模型定义和训练工具。在tensorflow2.0中,它被废弃了,并且其功能已经被整合到其他模块中。 在tensorflow2.0中,模型定义和训练工具主要集中在tensorflow.keras模块中。Keras是一个高级神经网络API,它提供了更简洁和易于使用的接口来定义和训练模型。与contrib_slim类似的功能可以使用tensorflow.keras模块中的各种类和函数来实现。 此外,在tensorflow2.0中,模型定义和训练的推荐方法是使用自定义模型子类化或函数式API来创建模型。这些方法提供了更大的灵活性和可拓展性,使得模型定义更易于阅读和维护。 总之,虽然tensorflow2版本中没有contrib_slim包,但可以使用tensorflow.keras模块以及自定义模型子类化或函数式API来实现类似的功能。这些改进使得tensorflow2更易于使用和扩展,并提供了更一致的编程接口。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值