第一课 创建一个数据队列

课程:使用Tensorflow来创建自己的神经网络

作为Tensorflow的入门第一课,我想先来讲一下如何创建一个用于数据训练的队列。 通过这个简单例子,同时也会向大家展现Tensorflow的工作方式,这与我们以往接触的编程架构都很不一样,也让很多刚入门的小伙伴非常困惑。

Tensorflow编程套路

在进入主题之前,先简单讲一下Tensorflow的编程套路,这个和其他组件还是有很大区别的。Tensorflow的编程要分两步走。

第一步,Tensorflow要求定义一个Graph()对象,,这Graph就好比一张空白的画纸,你可以在上面写下你需要的Tensorflow运算操作。比如在Graph里写下代码 sum = tf.add(A, B) 时你只是定义了一个运算步骤,返回值sum代表的是tf.add这个运算,而不是运算结果。

第二步,建立运算Session对象,并在Session内通过调用Session.run()方法来执行之前定义的运算。比例,在Graph里已经定义了sum = tf.add(A, B),此时就可以调用sum_value = Session.run([sum]) 来真正执行运算,而run方法的返回值才是真正的运算结果。

下面给出了一最个简单的Tensorflow结构框架,初学者可以套用这个框架来编写你自己的代码。

With tf.Graph() as default:
         …
         定义Tensorflow运算
         …
         With tf.Session() as sess:
                   …
                  调用Session.run() 执行运算
                   …

任务描述

接下来让我们进入到今天的主题,我们的目标是要生成一组图像和标签一一对应的训练数据,其中图像是一个5x5像素大小的图片。并且我们可以定义每个训练批次使用的数据样本个数 - n。即获得两个Tensor对象:

  1. image_batch:n张图片的列表

  2. label_batch:n个标签数据的列表

并且image_batch和label_batch的顺序是一致的,即label_batch里的第一个标签是image_batch里的第一个图像的标签, 以此类推。在本文的最后我将给出完整的代码。

生成随机数据

让我来先生成一些数据,以下代码定义了一个名为generate_data()的方法来生成一批数据。

def generate_data():
  num = 5
  label = np.asarray(range(0, num))
  print('generate_data:'+str(label))
  images = np.random.random([num, 5, 5, 3])
  print('label size :{}, image size {}'.format(label.shape, images.shape))
  return label, images

生成的数据结构为5组图像(image)和标签(label)相对应的数组,图像的尺寸是5x5,每个像素点分别有R,G,B三个像素数据组成(如图一)。而对应于每一幅图片,还会生成一个数据标签,这个数据标签是按照0到4顺序生成的,也就是第一张图的标签是0, 第二张是1, 而最后一张即第五张是4 (如图二)。图像和标签各种生成队列并返回。

图一:每张图含有的数据量是5x5x3 = 75,一次生成5张这样的图片
图二:每个数据标签含有1个数字,分别为0-4

创建一个Tensorflow数据队列

我们定义一个get_batch_data()的方法来创建一个Tensorflow数据队列。 这里调用了3个Tensorflow的运算, 分别是tf.cast,tf.train.slice_input_producer和tf.train.batch。正如前文所说,这里调用这些方法仅仅是描述出Tensorflow运算的步骤,而没有进行实际的运算。下面我们来详细讲述这些Tensorflow运算究竟是什么含义。

def get_batch_data():
  labels, images = generate_data()
  labels = tf.cast(label, tf.int32)
  images = tf.cast(images, tf.float32)
  input_queue = tf.train.slice_input_producer([images, label], shuffle=False)
  image_batch, label_batch = tf.train.batch(input_queue, batch_size=3, \
              num_threads=1, capacity=64, min_after_dequeue=5)
  return image_batch, label_batch

该方法首先调用我们之前的generate_data()方法来获取label列表和images列表,并且使用tf.cast方法将起数据类型分别转换成 int32和float32。

然后调用了tf.train.slice_input_producer()方法来切割刚才的images列表和label列表,生成一个image和一个label为一组的数据组列表,并且将其压入队列返回以备用(见图三)。为什么要做这步处理了?因为这样的数据结构是tf.train.batch方法的输入参数要求的。

图三:slice_input_producer()方法将图上的两个列表(list)images和labels重新组合成一个列表,新列表的每个元素为一个image和一个label的组合

最后使用tf.train.batch()方法,将之前生成的数据队列作为参数传递给它,并且通过batch_size来指定每个批次的元素个数。它的返回值个数是根据你传给他的数据队列里第1维的(Tensor维度从0开始算)的维度决定的。

建立运算图并执行运算

先来看一下完整代码,这段代码使用了在第一章里描述的框架。

with tf.Graph()as_default(): #创建一个Graph对象并作为默认的Graph
         #调用之前定义的get_batch_data()方法获得两个tensor算符
         image_batch, label_batch = get_batch_data() 
         #至此的运算图定义完毕,开启一个Session来执行运算
         with tf.Session() as sess:
              #创建一个tf.train.Coordinator对象来协调不同线程使用队列
              coord = tf.train.Coordinator()
              #启动队列
              threads = tf.train.start_queue_runners(sess, coord)
              i = 0
              try:
                 #如果队列应当停止或计数大于5次则结束
                 while (not coord.should_stop()) and i < 5: 
                      #调用Session对象的run()方法执行之前在Graph里定义的算符
                      #并返回指定大小的一批数据
                       image_batch_v, label_batch_v = sess.run([image_batch, \
                                 label_batch])
                       print('  ')                                    
                       print('batch' + str(i)+'---------------------------')
                       print('image_batch_v:'+str(image_batch_v))
                       print('label_batch_v:'+str(label_batch_v))
                       i += 1
              #判断是否超出队列数据范围, 如果超出则显示完成
              except tf.errors.OutOfRangeError:
                     print('done')
              #最终退出前停止队列
              finally:
                     coord.request_stop()
              coord.join(threads)

代码详解

1. 定义运算图

第一行代码定义了一个tf.Graph对象并设为当前默认Graph。 接下来调用之前定义的get_batch_data ()获得两个tensorflow算子image_batch, label_batch。这里要强调的是,行代码并没有真正返回实际的数据,仅仅需要执行的tensorflow运算。

接下来创建了一个Session对象并将它作为当前Session。然后启动之前定义好的队列,启动队列的方式如下:

 #创建一个tf.train.Coordinator对象来协调不同线程使用队列
 coord = tf.train.Coordinator()
 #启动队列
 threads = tf.train.start_queue_runners(sess, coord)
  1. 先创建一个Coordinator对象,这是用来协调多线程中的队列的,对我们这个例子来说,仅有一个线程在获取数据,所以并没有什么用。但是,由于对于Tensorflow编程来说,多线程是非常见的,所以为了培养良好的编程习惯,我会建议大家保留这个对象定义以便将来的程序扩展。

  2. 接下来我们就调用start_queue_runners()方法来启动队列。该方法有两个参数,一个是指定一个有效的Session,另外一个是指定他所使用的Coordinator对象。这两个参数都是可选参数。如果不指定Session的话,程序就会使用当前默认Session。

2. 确定运算执行的次数

我们着这里定义了一个while循环来监测队列的状态以及循环的次数。当Coordinator的should_stop()方法返回False时,或者计数器i大于5时,结束运算。关于Coordinator的should_stop()方法是如何来判断队列是否该继续运行,这一点Tensorflow官方文档写的似乎不是很明白,但这是官方推荐的判读队列是否有效的方法。

3. 执行运算

在tf.Session对象里,我们可以使用Session的run的方法来执行运算并获得运算结果。run方法的参数是将之前定义的运算符以一组tensor的形式传递给它,而该方法的返回值即运算结果,按照你传入参数的顺序返回。比如我们将我们之前定义的两个运算[image_batch, label_batch]使用[ ]将其组合成一个tensor变量传递给run, 而他的返回值就是运行该运算获得的结果,返回值的顺序是和你传入的运算符在tensor变量里的排序是一致的, image队列在前,label队列在后。

实际上image_batch和label_batch是指向同一组tensorflow的运算的,只是返回的结果集不同。如果我们把代码改成如下形式,实际上运算的过程是相同的,只是返回值仅有image列表,没有label列表。

4. 捕获异常

一般在进行队列操作时,我们会使用try来捕获异常错误。如一下这段代码,我们使用try捕获OutOfRangeError异常从而来监测数据队列是否已经被清空,当数据队列被清空时则打印出“Done”表示已经到达数据队列的末尾了。实际上对于我们上面这段程序而已,由于generate_data()方法会一直产生随机数据,所以队列始终不会为空。最终,在程序退出前使用Coordinator 对象的request_stop来停止数据请求。

5. 运行结果

数据生成后,我们使用print方法将它打印出来, 方便大家直观的看到程序运行的结果。我们要求每批次返回3组数据,一共执行5个批次。返回的每个批次数据分别是3个5x5x3的图像和其对应的标签数组。为便于理解,这里只给image的结构如下(图四)。

图四:一个5x5RGB图像的数据存储结构

完整源码

Github 源码: https://github.com/deechean/Using-Tensorflow-to-create-your-own-neural-network-samples

import Tensorflow as tf
import Numpy as np

def generate_data():
  num = 5
  label = np.asarray(range(0, num))
  print('generate_data:'+str(label))
  images = np.random.random([num, 5, 5, 3])
  print('label size :{}, image size {}'.format(label.shape, \
              images.shape))
return label, images

def get_batch_data():
  labels, images = generate_data()
  labels = tf.cast(label, tf.int32)
  images = tf.cast(images, tf.float32)
  input_queue = tf.train.slice_input_producer([images, label], \
                shuffle=False)
  image_batch, label_batch = tf.train.batch(input_queue, \
                batch_size=3, num_threads=1, capacity=64, \
                min_after_dequeue=5)
  return image_batch, label_batch

with tf.Graph()as_default():
         image_batch, label_batch = get_batch_data()
         with tf.Session() as sess:
              coord = tf.train.Coordinator()
              threads = tf.train.start_queue_runners(sess, coord)
              i = 0
              try:
                 while (not coord.should_stop()) and i < 5:
                       image_batch_v, label_batch_v = sess.run([image_batch, \
                                 label_batch])
                       print('  ')                                    
                       print('batch' + str(i)+'---------------------------')
                       print('image_batch_v:'+str(image_batch_v))
                       print('label_batch_v:'+str(label_batch_v))
                                    i += 1
              except tf.errors.OutOfRangeError:
                     print('done')
              finally:
                     coord.request_stop()
              coord.join(threads)

 

猜你喜欢

转载自blog.csdn.net/deecheanW/article/details/82318863
今日推荐