TensorFlow 自定义生成 .record 文件

一、生成 .record 文件

        前面的文章 TensorFlow 训练自己的目标检测器 中的第二部分第 2 小节中我们已经预先说过,会在后续的文章中阐述怎么自定义的将图像转化为 .record 文件,今天我们就来说一说这件事。

        在文章 TensorFlow 训练 CNN 分类器 中我们生成了 50000 张 28 x 28 像素的图像,我们的目标就是将这些图像全部写入到一个后缀为 .record 的文件中。 .recordtfrecord 文件是 TensorFlow 中的标准数据读写格式,它是一种能够高效读写的二进制文件,能够快速的复制、移动、读写和存储等。

        在文章 TensorFlow 训练 CNN 分类器 和文章 TensorFlow-slim 训练 CNN 分类模型 中,我们在训练模型时导入数据的方式都是一次性的将所有图像读入,然后循环的从中选择一个批量来训练。这对于小数据集来说不会产生问题,但如果训练数据异常大,那么很可能由于内存限制无法一次性将说有数据导入,这样前面的训练方式便不能采用了。此时,我们可以将数据转化为 .record 文件格式,然后再分批次的、逐步的读入 .record 文件进行训练。

        要将图像写入 .record 文件,首先要将图像编码为字符或数字特征,这需要调用类 tf.train.Feature。然后,在调用 tf.train.Example 将特征写入协议缓冲区。最后,通过类 tf.python_io.TFRecordWriter 将数据写入到 .record 文件中。比如,我们将前面提到的 50000 张图像写入 train.record 文件,使用如下代码(命名为 generate_tfrecord.py):

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Mar 26 09:02:10 2018

@author: shirhe-lyh
"""

"""Generate tfrecord file from images.

Example Usage:
---------------
python3 train.py \
    --images_path: Path to the training images (directory).
    --output_path: Path to .record.
"""

import glob
import io
import os
import tensorflow as tf

from PIL import Image

flags = tf.app.flags

flags.DEFINE_string('images_path', None, 'Path to images (directory).')
flags.DEFINE_string('output_path', None, 'Path to output tfrecord file.')
FLAGS = flags.FLAGS


def int64_feature(value):
    return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))


def int64_list_feature(value):
    return tf.train.Feature(int64_list=tf.train.Int64List(value=value))


def bytes_feature(value):
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))


def bytes_list_feature(value):
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=value))


def float_list_feature(value):
    return tf.train.Feature(float_list=tf.train.FloatList(value=value))


def create_tf_example(image_path):
    with tf.gfile.GFile(image_path, 'rb') as fid:
        encoded_jpg = fid.read()
    encoded_jpg_io = io.BytesIO(encoded_jpg)
    image = Image.open(encoded_jpg_io)
    width, height = image.size
    label = int(image_path.split('_')[-1].split('.')[0])
    
    tf_example = tf.train.Example(
        features=tf.train.Features(feature={
            'image/encoded': bytes_feature(encoded_jpg),
            'image/format': bytes_feature('jpg'.encode()),
            'image/class/label': int64_feature(label),
            'image/height': int64_feature(height),
            'image/width': int64_feature(width)}))
    return tf_example


def generate_tfrecord(images_path, output_path):
    writer = tf.python_io.TFRecordWriter(output_path)
    for image_file in glob.glob(images_path):
        tf_example = create_tf_example(image_file)
        writer.write(tf_example.SerializeToString())
    writer.close()
    
    
def main(_):
    images_path = os.path.join(FLAGS.images_path, '*.jpg')
    images_record_path = FLAGS.output_path
    generate_tfrecord(images_path, images_record_path)
    
    
if __name__ == '__main__':
    tf.app.run()

在该文件目录的终端执行:

python3 generate_tfrecord.py \
    --images_path /home/.../datasets/images \
    --output_path /home/.../datasets/train.record

便会在输出路径下生成 train.record 文件。以上代码中,最重要的部分是:1. 函数 create_tf_example,该函数首先得到图像的二进制格式、图像的宽和高、以及图像对应的类标号等,然后将图像的这些信息写入协议缓冲区;2. 函数 generate_tfrecord,该函数使用 tf.python_io.TFRecordWriter 类将协议缓冲区内的数据写入到 .record 文件中。

二、读取 .record 文件

        一旦将图像转化为了 .record 文件,接下来我们关心的就是怎么读取这个 .record 文件用于模型训练了。这可以借助我们前面使用过的模块 tf.contrib.slim

slim = tf.contrib.slim

def get_record_dataset(record_path,
                       reader=None, image_shape=[28, 28, 3], 
                       num_samples=50000, num_classes=10):
    """Get a tensorflow record file."""
    if not reader:
        reader = tf.TFRecordReader
        
    keys_to_features = {
        'image/encoded': 
            tf.FixedLenFeature((), tf.string, default_value=''),
        'image/format': 
            tf.FixedLenFeature((), tf.string, default_value='jpeg'),
        'image/class/label': 
            tf.FixedLenFeature([1], tf.int64, default_value=tf.zeros([1], 
                               dtype=tf.int64))}
        
    items_to_handlers = {
        'image': slim.tfexample_decoder.Image(shape=image_shape, 
                                              #image_key='image/encoded',
                                              #format_key='image/format',
                                              channels=3),
        'label': slim.tfexample_decoder.Tensor('image/class/label', shape=[])}
    
    decoder = slim.tfexample_decoder.TFExampleDecoder(
        keys_to_features, items_to_handlers)
    
    labels_to_names = None
    items_to_descriptions = {
        'image': 'An image with shape image_shape.',
        'label': 'A single integer between 0 and 9.'}
    return slim.dataset.Dataset(
        data_sources=record_path,
        reader=reader,
        decoder=decoder,
        num_samples=num_samples,
        num_classes=num_classes,
        items_to_descriptions=items_to_descriptions,
        labels_to_names=labels_to_names)

主要是借助了 tf.contib.slim 模块中的

slim.dataset.Dataset(data_sources, reader, decoder,
                  num_samples, items_to_descriptions,
                  **kwargs)

slim.tfexample_decoder.TFExampleDecoder(keys_to_features,
                                        items_to_handlers)

这两个类。

        使用时,直接传入 train.record 路径即可:

dataset = get_record_dataset('./xxx/train.record')
data_provider = slim.dataset_data_provider.DatasetDataProvider(dataset)
image, label = data_provider.get(['image', 'label'])

函数 get_record_dataset 返回 slim.dataset.Dataset 类的一个对象,之后通过类

slim.dataset_data_provider.DatasetDataProvider(dataset, num_readers=1,
                                               reader_kwargs=None,
                                               shuffle=True, num_epochs=None,
                                               common_queue_capacity=256,
                                               common_queue_min=128,
                                               record_key='record_key',
                                               seed=None, scope=None)

get 方法得到图像和类标号的序列数据。参数 num_readers=1 表示一次读取一个数据,即一次读取一张图像,因此实际使用是还需要使用函数 tf.train.batch 将数据形成批量再用于训练,见下一篇文章。

预告:下一篇文章将说明怎么完全使用 tf.contrib.slim 来构建和训练模型。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容

  • 原文链接:https://yq.aliyun.com/articles/178374 0. 简介 在过去,我写的主...
    dopami阅读 5,640评论 1 3
  • 涛和我说:这个证书拿到之后,每年有十来万的固定收入,好爽,不用担心突然失业的风险了。 很开心,一方面是因为他有了固...
    沐筱一阅读 1,124评论 5 3
  • 胡小月,你还活着吗?我还没死!生死未卜很暗淡……真阴森! 别看我快四十了,其实不抵一个孩子,不敢明目张胆地无理取闹...
    师先生阅读 857评论 27 10
  • 我这个人一直都大大咧咧,有什么就说的人,在上大学之前没觉得这些有什么不好,反倒是觉得这样很直白很干脆,朋友们也没有...
    猫背上的鱼阅读 194评论 0 0