TensorFlow入门教程(6)读取数据集之队列

#
#作者:韦访
#博客:https://blog.csdn.net/rookie_wei
#微信:1007895847
#添加微信的备注一下是CSDN的
#欢迎大家一起学习
#

1、概述

我们之前讲的都是基于MNIST数据集,而TensorFlow又封装了读取这个数据集的方法,所以我们虽然调用起来非常爽,但是,这也导致了我们还没掌握自己读取数据集数据来训练模型的能力,接下来我将用几讲来专门说这个。首先来看队列。

环境配置:

操作系统:Win10 64位

显卡:GTX 1080ti

Python:Python3.7

TensorFlow:1.15.0

2、队列

概念

目前我们接触的Tensorflow数据读取机制是往占位符placeholder传入feed_dict,这种机制比较简单,前面的例子也用过,这里不赘述。现在我们看队列机制,

如上图,要训练数据,得分两步,第一步,先将数据从硬盘加载到内存中,第二步,拱给CPU或者GPU运算。如果只用一个线程,那么,运行第一步的时候第二步就得等着,运行第二步的时候,第一步就得等着,这样就浪费时间了。

如上图,解决这个问题,就得将第一步和第二步分别放在两个线程中:一个线程不断的把数据读入内存,另一个线程从内存中取出数据进行计算。

为了方便管理,TensorFlow在内存队列前又加了一层“文件名队列”。

函数

对于文件名队列,使用tf.train.string_input_producer 函数,该函数有三个比较重要的参数,string_tensor参数向这个函数传入文件名list,系统就自动将它转为一个文件名队列。num_epochs参数传入的是epoch数,即将传入的list全部运算几遍。shuffle参数决定在一个epoch内,文件的顺序是否被打乱,若shuffle=False,则不打乱,否则打乱,默认是打乱的。

在tensorflow中,内存队列不需要我们自己建立,只需要使用reader对象从文件名队列中读取数据即可。

需要注意的是,使用tf.train.string_input_producer函数创建队列后,程序并没有马上将文件名加入队列,而是要运行tf.train.start_queue_runners函数后,才真正开始工作。

3、代码

为了便于理解,下面给出一个简单的例子

如上图,有三张图片,分别为1.jpg, 2.jpg,3.jpg,首先来看了当shuffle=False时的读取顺序,代码如下,

#encoding:utf-8
import tensorflow as tf
import os

def delfiles(path):
    ls = os.listdir(path)
    for i in ls:
        c_path = os.path.join(path, i)
        if os.path.isdir(c_path):
            delfiles(c_path)
        else:
            os.remove(c_path)

def main(argv=None):
    path = "MNIST_QUEUE"
    filenames = [os.path.join(path, "images", "%d.jpg" % i) for i in range(1, 4)]

    # shuffle=False表示不打乱顺序,num_epochs=3表示整个队列获取三次
    queue = tf.train.string_input_producer(filenames, shuffle=False, num_epochs=3)

    # 读取文件名队列中的数据
    reader = tf.WholeFileReader()
    key, value = reader.read(queue)

    with tf.Session() as sess:
        # 初始局部化变量,注意这个函数跟tf.global_variables_initializer.run()是不一样的
        # 因为string_input_producer函数的num_epochs=3传入的是局部变量
        tf.local_variables_initializer().run()
        threads = tf.train.start_queue_runners(sess=sess)
        i = 0
        destpath = os.path.join(path, "shuffle_False")

        if os.path.exists(destpath):
            delfiles(destpath)
        else:
            os.makedirs(destpath)

        while True:
            i += 1
            data = sess.run(value)
            with open(os.path.join(destpath, 'image_%d.jpg' % i), 'wb') as fd:
                fd.write(data)


if __name__ == '__main__':
    tf.app.run()

运行结果,

Instructions for updating:

To construct input pipelines, use the `tf.data` module.

Traceback (most recent call last):

  File "C:\Users\wilf\Anaconda3\lib\site-packages\tensorflow_core\python\client\session.py", line 1365, in _do_call

    return fn(*args)

  File "C:\Users\wilf\Anaconda3\lib\site-packages\tensorflow_core\python\client\session.py", line 1350, in _run_fn

    target_list, run_metadata)

  File "C:\Users\wilf\Anaconda3\lib\site-packages\tensorflow_core\python\client\session.py", line 1443, in _call_tf_sessionrun

    run_metadata)

tensorflow.python.framework.errors_impl.OutOfRangeError: FIFOQueue '_0_input_producer' is closed and has insufficient elements (requested 1, current size 0)

         [[{{node ReaderReadV2}}]]

运行程序时,会抛出上面所示的异常,莫慌,这是因为队列里的数被读完了,系统该“结束”了,所以抛出OutOfRangeError异常,我们也可以去捕获这个异常。来看看MNIST_QUEUE\shuffle_False文件夹下生成了什么,

可以看到,程序是顺序的读取了“1”“2”“3”,读了3次。当shuffle=True时,我们来看结果如何,代码如下,

#encoding:utf-8
import tensorflow as tf
import os

def delfiles(path):
    ls = os.listdir(path)
    for i in ls:
        c_path = os.path.join(path, i)
        if os.path.isdir(c_path):
            delfiles(c_path)
        else:
            os.remove(c_path)

def main(argv=None):
    path = "MNIST_QUEUE"
    filenames = [os.path.join(path, "images", "%d.jpg" % i) for i in range(1, 4)]

    # shuffle=True表示打乱顺序,num_epochs=3表示整个队列获取三次
    queue = tf.train.string_input_producer(filenames, shuffle=True, num_epochs=3)

    # 读取文件名队列中的数据
    reader = tf.WholeFileReader()
    key, value = reader.read(queue)

    with tf.Session() as sess:
        # 初始局部化变量,注意这个函数跟tf.global_variables_initializer.run()是不一样的
        # 因为string_input_producer函数的num_epochs=3传入的是局部变量
        tf.local_variables_initializer().run()
        threads = tf.train.start_queue_runners(sess=sess)
        i = 0
        destpath = os.path.join(path, "shuffle_True")

        if os.path.exists(destpath):
            delfiles(destpath)
        else:
            os.makedirs(destpath)
        try:
            while True:
                i += 1
                data = sess.run(value)
                with open(os.path.join(destpath, 'image_%d.jpg' % i), 'wb') as fd:
                    fd.write(data)
        except:
            print("Done...")

if __name__ == '__main__':
    tf.app.run()

运行结果,

可以看到,还是读取了3次,但顺序是被打乱了。

4、将MNIST数据集保存成图片

虽然上面已经讲清楚了队列,但是,只有3张图片,真正的数据集肯定不会只有3张图片。所以,还是以MNIST数据集为例,我们将MNIST数据集以图片的形式保存,

如上图所示,共有10个文件夹,每个文件夹下保存着对应的图片,即文件夹名称是以label命名的,例如,0文件夹下的内容如下,

以此类推,受篇幅限制,代码在最后给。

5、通过队列读取数据集并且使用CNN识别

数据集现在有了,接着我们就通过队列读取数据集,并且使用上一讲的CNN进行识别。

先来看读取数据集部分的代码。首先,将数据集中所有图片和其对应的标签导入到两个列表中,代码如下,

# 导入图片名和对应的标签
def load_files(dir):
    print("Loading files...")
    fileslist = []
    labelslist = []
    for path, dirs, files in os.walk(dir):
        for file in files:
            fileslist.append(os.path.join(path, file))
            label = os.path.basename(path)
            labelslist.append([i == int(label) for i in range(0, 10)])

    # return shuffle(np.asarray(fileslist), np.asarray(labelslist))
    return np.asarray(fileslist), np.asarray(labelslist).astype(np.float)

接着,我们批量获取数据,代码如下,

# 批量获取数据
def get_batch(fileslist, labelslist, batchsize):
    queue = tf.train.slice_input_producer([fileslist, labelslist])
    files = queue[0]
    labels = queue[1]
    imagesrf = tf.read_file(files)
    images = tf.image.decode_jpeg(imagesrf, 1)
    images = tf.image.resize_image_with_crop_or_pad(images, 28, 28)
    images = tf.reshape(images, [28 * 28])
    images = tf.cast(images, tf.float32)
    images /= 255
    image_batch, label_batch = tf.train.batch([images, labels], batch_size=batchsize, num_threads=64)
    label_batch = tf.reshape(label_batch, [batchsize, 10])
    return image_batch, label_batch

定义上面两个函数以后,我们就要用起来,用法如下,

fileslist, labelslist = load_files("MNIST_QUEUE/all_images")
images_batch, labels_batch = get_batch(fileslist, labelslist, 50)

接着,只要在会话中run images_batch, labels_batch就可以了,完整代码如下,

import os
import tensorflow.compat.v1 as tf
from sklearn.utils import shuffle
import numpy as np

# 导入图片名和对应的标签
def load_files(dir):
    print("Loading files...")
    fileslist = []
    labelslist = []
    for path, dirs, files in os.walk(dir):
        for file in files:
            fileslist.append(os.path.join(path, file))
            label = os.path.basename(path)
            labelslist.append([i == int(label) for i in range(0, 10)])

    # return shuffle(np.asarray(fileslist), np.asarray(labelslist))
    return np.asarray(fileslist), np.asarray(labelslist).astype(np.float)

# 批量获取数据
def get_batch(fileslist, labelslist, batchsize):
    queue = tf.train.slice_input_producer([fileslist, labelslist])
    files = queue[0]
    labels = queue[1]
    imagesrf = tf.read_file(files)
    images = tf.image.decode_jpeg(imagesrf, 1)
    images = tf.image.resize_image_with_crop_or_pad(images, 28, 28)
    images = tf.reshape(images, [28 * 28])
    images = tf.cast(images, tf.float32)
    images /= 255
    image_batch, label_batch = tf.train.batch([images, labels], batch_size=batchsize, num_threads=64)
    label_batch = tf.reshape(label_batch, [batchsize, 10])
    return image_batch, label_batch

def main(argv=None):
    fileslist, labelslist = load_files("MNIST_QUEUE/all_images")
    images_batch, labels_batch = get_batch(fileslist, labelslist, 50)
    # 转换后的图片保存的路径
    save_dir = 'MNIST_QUEUE/wf_images/'

    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        coord = tf.train.Coordinator()
        threads = tf.train.start_queue_runners(sess=sess, coord=coord)
        while True:
            images, labels = sess.run([images_batch, labels_batch])
            print(images.shape)
            print('label', labels.shape)


if __name__ == '__main__':
    tf.app.run()

运行结果,

通过打印images和labels的形状可以看到,我们batch设置为50,图片数据是长度为784的向量,label是one-hot格式的,跟我们之前读取的MNIST数据集格式一样的。接下来就使用上一讲的CNN代码来进行训练和识别,核心代码如下,

def main(argv=None):
    fileslist, labelslist = load_files("MNIST_QUEUE/all_images")
    images_batch, labels_batch = get_batch(fileslist, labelslist, 50)

    # 创建x占位符,用于临时存放MNIST图片的数据,
    # [None, 784]中的None表示不限长度,而784则是一张图片的大小(28×28=784)
    x = tf.placeholder(tf.float32, [None, 784])
    # label 存的是实际图像的标签,即对应于每张输入图片实际的值
    label = tf.placeholder(tf.float32, [None, 10])

    # 将图片从长度为784的向量重新还原为28×28的矩阵图片,
    # 因为MNIST是黑白图片,所以深度为1,
    # 第一个参数为-1,表示一维的长度不限定,这样就可以灵活设置每个batch的训练的个数了
    x_image = tf.reshape(x, [-1, 28, 28, 1])

    # 搭建神经网络结构
    acc, op, keep_prob, loss = net(x_image, label)

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

        coord = tf.train.Coordinator()
        # 从这里开始,队列才真正的开始工作
        threads = tf.train.start_queue_runners(sess=sess, coord=coord)
        for i in range(20000):

            images, labels = sess.run([images_batch, labels_batch])

            # 将数据传入神经网络,开始训练
            sess.run(op, feed_dict={x: images, label:labels, keep_prob: 0.5})
            if i % 100 == 0:
                train_accuracy = sess.run(acc, feed_dict={x: images, label:labels, keep_prob: 1.0})
                print("step %d, training accuracy %g" % (i, train_accuracy))

运行结果,

跟以前效果一样。

6、源码下载

完整代码链接如下,

 

https://mianbaoduo.com/o/bread/Ypaclpo=

 

下一讲,我们来看看读取数据集之TFRecord的基本概念和知识。

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值