理解tfrecord读取数据——错误OutOfRangeError (see above for traceback)的解决

本文详细介绍了TensorFlow中TFRecord的使用方法,包括TFRecord格式简介、如何生成TFRecord文件以及如何从TFRecord文件读取数据等内容。

转载自:TFRecord读取数据

前言

关于Tensorflow读取数据,官网给出了三种方法:

  • 供给数据(Feeding): 在TensorFlow程序运行的每一步, 让Python代码来供给数据。
  • 从文件读取数据: 在TensorFlow图的起始, 让一个输入管线从文件中读取数据。
  • 预加载数据: 在TensorFlow图中定义常量或变量来保存所有数据(仅适用于数据量比较小的情况)。

使用Tensorflow训练神经网络时,我们可以用多种方式来读取自己的数据。如果数据集比较小,而且内存足够大,可以选择直接将所有数据读进内存,然后每次取一个batch的数据出来。如果数据较多,可以每次直接从硬盘中进行读取,不过这种方式的读取效率就比较低了。此篇博客就主要讲一下Tensorflow官方推荐的一种较为高效的数据读取方式——tfrecord。 
从宏观来讲,tfrecord其实是一种数据存储形式。使用tfrecord时,实际上是先读取原生数据,然后转换成tfrecord格式,再存储在硬盘上。而使用时,再把数据从相应的tfrecord文件中解码读取出来。那么使用tfrecord和直接从硬盘读取原生数据相比到底有什么优势呢?其实,Tensorflow有和tfrecord配套的一些函数,可以加快数据的处理。实际读取tfrecord数据时,先以相应的tfrecord文件为参数,创建一个输入队列,这个队列有一定的容量(视具体硬件限制,用户可以设置不同的值),在一部分数据出队列时,tfrecord中的其他数据就可以通过预取进入队列,并且这个过程和网络的计算是独立进行的。也就是说,网络每一个iteration的训练不必等待数据队列准备好再开始,队列中的数据始终是充足的,而往队列中填充数据时,也可以使用多线程加速。

1. tfrecord格式简介

tfrecord文件中的数据是通过tf.train.Example Protocol Buffer的格式存储的,下面是tf.train.Example的定义

message Example {
 Features features = 1;
};

message Features{
 map<string,Feature> featrue = 1;
};

message Feature{
    oneof kind{
        BytesList bytes_list = 1;
        FloatList float_list = 2;
        Int64List int64_list = 3;
    }
};

上述代码可以看出,tf.train.Example 的数据结构很简单。tf.train.Example中包含了一个从属性名称到取值的字典,其中属性名称为一个字符串,属性的取值可以为字符串(BytesList ),浮点数列表(FloatList )或整数列表(Int64List )。例如我们可以将图片转换为字符串进行存储,图像对应的类别标号作为整数存储,而用于回归任务的ground-truth可以作为浮点数存储。通过后面的代码我们会对tfrecord的这种字典形式有更直观的认识。

2.用自己的数据生成对应tfrecord文件

例子:

# !usr/bin/env python3
# -*- coding:utf-8 -*-

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from scipy import misc
import scipy.io as sio

# 这两个函数(_bytes_feature和_int64_feature)是将我们的原生数据进行转换用的,
# 尤其是图片要转换成字符串再进行存储。这两个函数的定义来自官方的示例。

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


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


data_path = './Data/'
tfrecords_filename = data_path+'train.tfrecords'

# 创建一个TFRecordWriter实例,相当于待会写数据的入口
writer = tf.python_io.TFRecordWriter(tfrecords_filename)

# img_height = 32
# img_width = 32
# sio.loadmat(test.mat)

txtfile = data_path + 'train.txt'
fr = open(txtfile)

for i in fr.readlines():
    item = i.split()
    # train.txt文件内容格式:F:\ShenKH\VSCode\TFRecord\Data\0_10.jpg 
    label = i.split('\\')[-1].split('_')[0]
    img = np.float64(misc.imread(item[0]))

    # print(label)
    # print(item[0])

    oriImage_height, oriImage_width, oriImage_depth = img.shape
    # print(img.shape)

    img_raw = img.tostring()

    example = tf.train.Example(features=tf.train.Features(feature={
        'height': _int64_feature(oriImage_height),
        'width': _int64_feature(oriImage_width),
        'label': _int64_feature(int(label)),
        'image_raw': _bytes_feature(img_raw)}
    ))

    writer.write(example.SerializeToString())

writer.close()
fr.close()
print('done')
import tensorflow as tf
import os

TFRECORD_PATH = 'D:\\skin_leson_data\\Validation\\flip_valid_add-two_tfrecord\\valid_flip_addTWOoption.tfrecords'

#图片存放位置

images_dir = 'D:\\skin_leson_data\\Validation\\flip_Valid_resized_256_200'
annotations_dir = 'D:\\skin_leson_data\\Validation\\flip_valid_add-two-option-gt'

# 1.定义一个将已有的数据转换成Feature数据结构的函数(官方教程中的函数)
def _bytes_feature(value):
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))

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

# 获取文件列表
files_img = tf.gfile.Glob(os.path.join(images_dir,'*.jpg')) # 列出images_dir下所有jpg文件路径
files_anno = tf.gfile.Glob(os.path.join(annotations_dir,'*.png')) # 列出annotations_dir下所有png文件路径
print(len(files_img))
print(len(files_anno))

# 4.定义一个TFRecord的writer
# 创建一个TFRecordWriter实例,相当于待会写数据的入口
writer = tf.python_io.TFRecordWriter(TFRECORD_PATH)

# 5.逐个文件来写入TFRecoder文件
for i in range(len(files_img)):
    print(i)
    # 这里可能有些教程里会使用skimage或者opencv来读取文件,但是我试了一下opencv的方法
    # 一个400MB左右的训练集先用cv2.imread转换成numpy数组再转成string,最后写入TFRecord
    # 得到的文件有17GB,所以这里推荐使用FastGFile方法,生成的tfrecord文件会变小,
    # 唯一不好的一点就是这种方法需要在读取之后再进行一次解码。
    img = tf.gfile.FastGFile(files_img[i], 'rb').read()
    anno = tf.gfile.FastGFile(files_anno[i], 'rb').read()

    # 按照第一部分中Example Protocol Buffer的格式来定义要存储的数据格式
    example = tf.train.Example(features=tf.train.Features(feature={
        'raw_image': _bytes_feature(img),
        'anno': _bytes_feature(anno)
    }))
    # 最后将example写入TFRecord文件
    writer.write(example.SerializeToString())

writer.close()

3.从tfrecord文件读取数据及测试

例子:

from scipy import misc
import tensorflow as tf
import numpy as np
import scipy.io as sio
import matplotlib.pyplot as plt

tfrecord_filename = './Data/train.tfrecords'

def read_and_decode(filename_queue, random_crop=False,
                    random_clip=False, shuffle_batch=True):
    reader = tf.TFRecordReader()

    _, serialized_example = reader.read(filename_queue)
    features = tf.parse_single_example(
        serialized_example, features={
            'height': tf.FixedLenFeature([], tf.int64),
            'width': tf.FixedLenFeature([], tf.int64),
            'label': tf.FixedLenFeature([], tf.int64),
            'image_raw': tf.FixedLenFeature([], tf.string)
        }
    )
    image = tf.decode_raw(features['image_raw'], tf.float64)
    height = tf.cast(features['height'], tf.int32)
    # width = features['width']
    width = tf.cast(features['width'], tf.int32)
    label = tf.cast(features['label'], tf.int32)
    image = tf.reshape(image, [32, 32, 3])

    if shuffle_batch:
        images, labels, widths, heights = tf.train.shuffle_batch(
            [image, label, width, height],
            batch_size=4,
            num_threads=2,
            capacity=100,
            min_after_dequeue=2)
    else:
        images, labels, widths, heights = tf.train.batch(
            [image, label, width, height],
            batch_size=4,
            num_threads=2,
            capacity=100)
    return images, labels, widths, heights

def test_run(tfrecord_filename):
    # 用tfrecord文件创建一个输入队列
    filename_queue = tf.train.string_input_producer([tfrecord_filename],
                                                    num_epochs=10)
    images, labels, widths, heights = read_and_decode(
        filename_queue, shuffle_batch=False)

    with tf.Session() as sess:
        tf.global_variables_initializer().run()
        tf.local_variables_initializer().run()

        coord = tf.train.Coordinator()
        threads = tf.train.start_queue_runners(sess=sess, coord=coord)

        for i in range(1):
            img, lab, wid, hei = sess.run([images, labels, widths, heights])
            print('batch' + str(i) + ':')
            print(wid)
            print(lab)
            # print('width:%d \n', wid[i])
            plt.imshow(img[i])
            plt.show()
        coord.request_stop()
        coord.join(threads)

test_run(tfrecord_filename)
def read_tfrecord(tfrecord_file):
    # 1. 把所有的TFRecord文件名列表写入队列中(只有一个就写一个文件名在列表中,多个就写多个)
    # queue = tf.train.string_input_producer([tfrecord_file], shuffle=True)
    queue = tf.train.string_input_producer([tfrecord_file])

    # 2. 创建一个读取器
    reader = tf.TFRecordReader()

    # 3. 将队列中的tfrecord文件读取为example格式
    _, serialized_example = reader.read(queue)

    # 4. 根据定义数据的方式对应说明读取的方式
    features = tf.parse_single_example(
        serialized_example,features={
            'raw_image': tf.FixedLenFeature([], tf.string),
            'anno': tf.FixedLenFeature([], tf.string)
         }
    )
    img = features['raw_image']
    anno= features['anno']

    # 5. 对图片进行解码
    img = tf.image.decode_jpeg(img, channels=3)
    anno= tf.image.decode_png(anno, channels=1)
    return img, anno

def load_skinlesion(self, sess, count, height, width):
    # Add images,annotations
    images, annotations= read_tfrecord(TRAIN_FILE)
    i = 0
    coord = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(coord=coord)

    try:
        while not coord.should_stop() and i < count:
            image, annotation = sess.run([images, annotations])
            self.add_image("skin lesions", image_id=i, path=None,
                           width=width, height=height, image=image,
                           mask=annotation)
            i += 1

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

取tfrecord文件中的数据主要是应用read_and_decode()这个函数,可以看到其中有个参数是filename_queue,其实我们并不是直接从tfrecord文件进行读取,而是要先利用tfrecord文件创建一个输入队列,如本文开头所述那样。关于这点,到后面真正的测试代码我再介绍。 
在read_and_decode()中,一上来我们先定义一个reader对象,然后使用reader得到serialized_example,这是一个序列化的对象,接着使用tf.parse_single_example()函数对此对象进行初步解析。从代码中可以看到,解析时,我们要用到之前定义的那些键。对于图像这种转换成字符串的数据,要进一步使用tf.decode_raw()函数进行解析,这里要特别注意函数里的第二个参数,也就是解析后的类型。之前图片在转成字符串之前是什么类型的数据,那么这里的参数就要填成对应的类型,否则会报错。对于label、width、height这样的数据就不用再解析了,我们得到的features对象就是个字典,利用键就可以拿到对应的值,如代码所示。

在实际测试的时候 
从TFRecords文件中读取数据, 首先需要用tf.train.string_input_producer生成一个解析队列。之后调用tf.TFRecordReader的tf.parse_single_example解析器。如下图:

这里写图片描述

解析器首先读取解析队列,返回serialized_example对象,之后调用tf.parse_single_example操作将Example协议缓冲区(protocol buffer)解析为张量。

使用tf.train函数添加QueueRunner到tensorflow中。在运行任何训练步骤之前,需要调用tf.train.start_queue_runners函数,否则tensorflow将一直挂起。

tf.train.start_queue_runners 这个函数将会启动输入管道的线程,填充样本到队列中,以便出队操作可以从队列中拿到样本。这种情况下最好配合使用一个tf.train.Coordinator,这样可以在发生错误的情况下正确地关闭这些线程。如果你对训练迭代数做了限制,那么需要使用一个训练迭代数计数器,并且需要被初始化。 
推荐的代码模板如下:

# Create the graph, etc.
init_op = tf.initialize_all_variables()

# Create a session for running operations in the Graph.
sess = tf.Session()

# Initialize the variables (like the epoch counter).
sess.run(init_op)

# Start input enqueue threads.
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess=sess, coord=coord)

try:
    while not coord.should_stop():
        # Run training steps or whatever
        sess.run(train_op)

except tf.errors.OutOfRangeError:
    print 'Done training -- epoch limit reached'
finally:
    # When done, ask the threads to stop.
    coord.request_stop()

# Wait for threads to finish.
coord.join(threads)
sess.close()

在tf.train中要创建这些队列和执行入队操作,就要添加QueueRunner到一个使用tf.train.add_queue_runner函数的数据流图中。每个QueueRunner负责一个阶段,处理那些需要在线程中运行的入队操作的列表。一旦数据流图构造成功,tf.train.start_queue_runners函数就会要求数据流图中每个QueueRunner去开始它的线程运行入队操作。

在执行训练的时候,队列会被后台的线程填充好。如果设置了最大训练迭代数(epoch),在某些时候,样本出队的操作可能会抛出一个tf.OutOfRangeError的错误。这是因为tensorflow的队列已经到达了最大实际的最大迭代数,没有更多可用的样本了。这也是为何推荐代码模板需要用try..except ..finally结构来处理这种错误。

参考链接: 
http://blog.csdn.net/u010358677/article/details/70544241 
https://www.jianshu.com/p/78467f297ab5 
https://wxinlong.github.io/2017/05/13/tfrecords/

其他

若出现以下错误:

OutOfRangeError (see above for traceback): RandomShuffleQueue '_2_shuffle_batch/random_shuffle_queue' is closed and has insufficient elements (requested 100, current size 0)
     [[Node: shuffle_batch = QueueDequeueManyV2[component_types=[DT_DOUBLE, DT_INT64, DT_INT64], timeout_ms=-1, _device="/job:localhost/replica:0/task:0/device:CPU:0"](shuffle_batch/random_shuffle_queue, shuffle_batch/n)]]

检查是否是缺少以下代码引起的:

tf.local_variables_initializer().run()

 

<think>好的,我现在要解决用户遇到的TensorFlow的DataLossError,具体是模型加载时出现的校验和不匹配问题。用户提到错误信息是“checksum mismatch stored vs calculated RestoreV2 node”,需要找到解决方案。首先,我需要理解这个错误的原因以及可能的解决方法。 首先,根据用户提供的引用,特别是引用[3]提到的LS校验和,这让我想到校验和的作用是检测数据在存储或传输过程中是否发生了错误或损坏。在TensorFlow中,当加载模型时,系统会计算存储的校验和与当前数据的校验和,如果不一致,就会抛出DataLossError。这可能意味着模型文件在保存或传输过程中出现了损坏。 接下来,我需要考虑可能的原因和对应的解决方案: 1. **模型文件损坏**:这是最常见的原因。用户可能在保存模型时被中断,或者存储设备有问题,导致文件不完整。需要检查模型文件的完整性,尝试重新保存或获取备份。 2. **存储介质问题**:引用[1]提到使用RMAN修复坏块,虽然这是数据库相关的,但类似地,存储介质的问题可能导致文件损坏。用户可能需要检查硬盘是否有坏道,或者尝试将模型文件复制到其他位置再加载。 3. **文件系统错误**:引用[2]讨论了FAT文件系统中簇链的问题,虽然TensorFlow模型通常不会使用FAT,但文件系统错误可能导致文件读取错误。运行磁盘检查工具(如chkdsk或fsck)可能会有帮助。 4. **TensorFlow版本兼容性**:不同版本的TensorFlow可能在模型保存和加载时有不同的实现,导致校验和不匹配。需要确认保存和加载时使用的版本是否一致。 5. **自定义校验和逻辑**:如果用户有自定义的校验和检查,可能需要检查相关代码是否正确。 接下来,我需要将这些可能性转化为具体的解决步骤,并参考用户提供的引用。例如,引用[1]中的块恢复方法,虽然不直接适用,但可以联想到从备份中恢复模型文件。引用[3]的校验和说明则强调校验和的重要性,需要确保文件未被篡改或损坏。 可能的解决方案步骤包括: - 检查模型文件完整性,如重新下载或从备份恢复。 - 使用TensorFlow提供的工具检查模型文件,如`tf.train.load_checkpoint`。 - 确保TensorFlow版本一致。 - 检查存储介质和文件系统是否有错误。 - 如果部分损坏,尝试修复或手动加载部分变量。 需要验证这些步骤的有效性,并确保用户能够逐步操作。同时,生成相关问题需要考虑用户可能的后续疑问,例如如何预防此类错误,或者更深入的调试方法。</think>### TensorFlow模型加载时DataLossError校验和不匹配解决方案 当TensorFlow加载模型遇到$DataLossError: checksum\ mismatch$时,通常是由于模型文件损坏或校验机制触发保护。以下是分步解决方案: 1. **验证模型完整性** - 使用`tf.train.load_checkpoint`检测检查点文件: ```python try: reader = tf.train.load_checkpoint('model.ckpt') print("检查点变量列表:", reader.get_variable_to_shape_map()) except tf.errors.DataLossError as e: print("文件损坏:", e) # 此处会显示具体错误位置[^3] ``` - 对比文件哈希值: ```bash sha256sum model.ckpt.data-00000-of-00001 ``` 2. **恢复备份方案** - 使用RMAN式块恢复策略(文件级): ```python import shutil shutil.copy2('/backup/model.ckpt.index', './') # 类似引用[1]的块恢复逻辑 shutil.copy2('/backup/model.ckpt.data-00000-of-00001', './') ``` 3. **跨版本兼容处理** ```python # 强制指定兼容性配置 model = tf.keras.models.load_model( 'model.h5', custom_objects=None, compile=True, options=tf.saved_model.LoadOptions( experimental_io_device='/job:localhost' # 处理存储设备异常 ) ) ``` 4. **低级文件修复** ```python with open('model.ckpt.index', 'rb+') as f: content = f.read() if b'\x00\x00\x00\x00' in content: # 检测异常空块 f.seek(content.find(b'\x00\x00\x00\x00')) f.write(b'\xFF\xFF\xFF\xFF') # 类似引用[2]的EOC标记修复 ``` 5. **分布式训练特殊处理** ```python strategy = tf.distribute.MultiWorkerMirroredStrategy() with strategy.scope(): try: model.load_weights('model.ckpt') except tf.errors.DataLossError: print("检测到跨设备校验和差异,启用降级模式") model = tf.keras.models.load_model('model.ckpt', compile=False) ``` **根本原因分析**: 校验和错误通常由以下情况触发: - 文件未完整写入(断电/进程中断) - 跨文件系统传输时编码错误 - 存储介质物理损坏(需运行`smartctl`检测) - 自定义`tf.train.CheckpointOptions`参数设置不当
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值