【TensorFlow】如何进行图像数据的预处理?(两个完整代码在第3.4节和第4章)(第三课)

如何进行图像数据的预处理?

目录

如何进行图像数据的预处理?

1、TFRecord输入数据格式

1.1、TFRecord格式是什么?

1.2、样例程序

(1)生成TFRecord文件

(2)读取TERecord文件

2、图像数据预处理

2.1、图像预处理函数

(1)图像编码/解码处理

(2)图像大小调整

(3)图像翻转

(4)图像色彩调整

(5)处理标注框

2.2、预处理完整样例代码

3、多线程输入数据处理框架

框架的输入流程:

3.1、队列与多线程

队列与多线程有什么关系?

(1)队列

(2)多线程协同方法:

3.2、输入文件队列

3.3、组合训练数据(batching)

此处的batching和反向传播的batch的区别?

两个生成batch的函数:

3.4、输入数据处理框架

4、数据集API(Dataset)

使用数据集的方式实现3.4节的代码功能:


1、TFRecord输入数据格式

来自实际问题中的数据往往很多格式和属性,而TFRecord能统一格式,有效的管理不同的属性。

TFRecord格式是TensorFlow所专属的。

1.1、TFRecord格式是什么?

TFRecord 文件中的数据都是通过 tf.train.Example协议内存块(Protocol Buffer)的格式存储的。

(这个是数据格式文件,不是python!)

message Example {
	Features features = 1;
};

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

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

1.2、样例程序

(1)生成TFRecord文件

原理:将数据填入到 Example 协议内存块(protocol buffer),将协议内存块序列化为一个字符串, 并且通过tf.python_io.TFRecordWriter 写入到 TFRecords 文件。

以下代码将MNIST输入数据转换为TFRecord的格式(MNIST是一个数字识别数据集,深度学习入门案例)

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import numpy as np

# 生成整数型的属性。
def _int64_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]))

#输入路径
DATASET_PATH = 'datasets/MNIST_DATA'
mnist = input_data.read_data_sets(DATASET_PATH, dtype=tf.uint8, one_hot=True)
images = mnist.train.images
# 训练数据所对应的正确答案,可以作为一个属性保存在 TFRecord 中
labels = mnist.train.labels
# 训练数据的图像分辨率,这可以作为 Example 中的一个属性。
pixels = images.shape[1]
num_example = mnist.train.num_example

# 输出 TFRecord 文件的地址。
filename = '/home/jie/Jie/codes/tf/datasets/MNIST_DATA/output.tfrecords'
# 创建一个 writer 来写 TFRecord 文件。
writer = tf.python_io.TFRecordWriter(filename)
for index in range(num_example):
	# 将图像矩阵转化成一个字符串。
	image_raw = images[index].tostring()
	# 将一个样例转化为 Example Protocol Buffer ,并将所有的信息写入这个数据结构。
	example = tf.train.Example(features=tf.train.Features(features={
		'pixels':_int64_feature(pixels),
		'label':_int64_feature(np.argmax(labels[index])),
		'image_raw':_bytes_features(images_raw)}))

	# 将一个 Example 写入 TFRecord 文件。
	writer.write(example.SerializeToString())
writer.close()

(2)读取TERecord文件

原理:使用tf.TFRecordReader的tf.parse_single_example解析器。这个操作可以将Example协议内存块(protocol buffer)解析为张量。

import tensorflow as tf

# TFRecord 文件的地址。
filename = 'datasets/MNIST_DATA/output.tfrecords'
# 创建一个 reader 来读取 TFRecord 文件中的样例。
reader = tf.TFRecordReader()
# tf.train.string_input_producer 创建一个队列来维护输入文件列表
filename_queue = tf.train.string_input_producer([filename])

# 从文件中读取一个样例。也可以使用 read_up_to 函数-次性读取多个样例。
_, serialized_example = reader.read(filename_queue)
# 解析读入的一个样例。若需要解析多个样例,可用parse_example函数
features = tf.parse_single_example(
	serialized_example,
	features={
		 # TensorFlow 提供两种不同的属性解析方法。 
		 # 1. tf.FixedLenFeature,所解析的结果为一个 Tensor。
		 # 2. tf.VarLenFeature,所得到的解析结果为SparseTensor,用于处理稀疏数据。
		 # 这里解析数据的格式需要和上面程序可入数据的格式一致。
		 'image_raw':tf.FixedLenFeature([], tf.string),
		 'pixels':tf.FixedLenFeature([], tf.int64),
		 'label':tf.FixedLenFeature([], tf.int64),
	})

# tf.decode_raw 可以将字符串解析成图像对应的像索数组。
image = tf.decode_raw(features['image_raw'], tf.uint8)
label = tf.cast(features['label'], tf.int32)
pixels = tf.cast(features['pixels'], tf.int32)

sess = tf.Session()
# 启动多线程处理输入数据
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess=sess, coord=coord)

# 每次运行可以读取 TFRecord 文件中的一个样例。
# 当所有样例都读取完之后,在此样例中程序会再重头读取
for i in range(10):
	print(sess.run([image, label, pixels]))

 

2、图像数据预处理

预处理的目的:弱化与图像识别无关的因素。

2.1、图像预处理函数

(1)图像编码/解码处理

什么是编码/解码处理?

看手绘图:

样例代码:

# -*-coding:utf-8-*-
import tensorflow as tf
import matplotlib.pyplot as plt

IMAGES_PATH = 'xzx/6987985.jpeg'
SAVE_PATH = 'xzx/output.jpeg'
# 读取图像的原始数据。
image_raw_data = tf.gfile.FastGFile(IMAGES_PATH, 'rb').read()

with tf.Session() as sess:
	# 对图像进行 jpeg 的格式解码从而得到图像对应的三维矩阵。 
	# tf.image.decode_png 函数对 png 格式的图像进行解码。
	# 解码之后的结果为一个张量,在使用它的取值之前需要明确调用运行的过程。
	img_data = tf.image.decode_jpeg(image_raw_data)

	print(img_data.eval())
	# 输出解码之后的三维矩阵,上面这一行代码将输出以下内容。

	# 使用 pyplot 工具可视化得到的图像。
	plt.imshow(img_data.eval())
	plt.show()

	# 将表示一张图像的三维矩阵重新按照jpeg格式编码并存入文件中。
	# 打开该图像,与原始图像一样
	encoded_image = tf.image.encode_jpeg(img_data)
	with tf.gfile.GFile(SAVE_PATH, "wb") as f:
		f.write(encoded_image.eval())

(2)图像大小调整

resized = tf.image.resize_images(img_data, [300, 300], method=0)

method0,1,2,3代表不同的调整大小方法,不做详解。

	# 略去加载原始图像,定义会话等过程,假设:img_data是已经解码的图像。
	
	# 1. 首先将图片数据转换为实数类型,即将0-255转化为0.0-1.0范围内的实数。
	# 大多数图像处理 API 支持整数和实数类型的输入。
	# 如果输入是整数类型,这些 API 会在内部将输入转化为实数后处理,再将输出转化为整数。
	# 如果有多个处理步骤,在格数和实数之间的反复转化将导致精度损失,
	# 因此推荐在图像处理前将其转化为实数类型。

	img_data = tf.image.convert_image_dtype(img_data, dtype=tf.float32)

	# 通过 tf.image.resize_images 函数调整图像的大小。
	# 第一个参数: 原始图像
	# 第二个和第三个参数为调整后图像的大小 
	# method 参数给出了调整图像大小的算法

	resized = tf.image.resize_images(img_data, [300, 300], method=0)
	plt.imshow(resized.eval())
	plt.show()

(3)图像翻转

翻转的意义?

必须采取随机的翻转,让相片的角度对识别不产生影响。(比如不管猫头向左还是向右,都得识别出来)

随机翻转代码:

	# 以 50% 概率上下翻转图像
	flipped1 = tf.image.random_flip_up_down(img_data)
	# 以 50% 概率左右翻转图像
	flipped2 = tf.image.random_flip_left_right(img_data)

(4)图像色彩调整

目的:

与图像翻转一样,图片的亮度、对比度、饱和度和色相都不应该对识别产生影响,所以也应该随机改变这些属性。

a、调整亮度

	# 将图像的亮度 -0.5
	adjusted1 = tf.image.adjust_brightness(img_data, -0.5) 
	# 截断在 0.0-1.0 范围区间
	adjusted1 = tf.clip_by_value(adjusted1, 0.0, 1.0)

	# 将图像的亮度 +0.5
	adjusted2 = tf.image.adjust_brightness(img_data, 0.5)
	# 在[-max_delta, max_delta)的范围随机调整图像的亮度。
	max_delta = 0.9
	adjusted3 = tf.image.random_brightness(img_data, max_delta)

b、调整对比度

	# 将图像的饱和度-5
	adjusted1 = tf.image.adjust_saturation(img_data, -5) 
	# 将图像的饱和度+5
	adjusted2 = tf.image.adjust_saturation(img_data, 5)
	# 在[lower, upper] 的范围随机调整图像的饱和度
	lower = 10
	upper = 100
	adjusted3 = tf.image.random_saturation(img_data, lower, upper)

c、调整色相

	# 分别将色相加 0.1、0.3、0.6 和 0.9 
	adjusted1 = tf.image.adjust_hue(img_data, 0.1)
	adjusted2 = tf.image.adjust_hue(img_data, 0.3) 
	adjusted3 = tf.image.adjust_hue(img_data, 0.6) 
	adjusted4 = tf.image.adjust_hue(img_data, 0.9) 
	# 在 [-max_delta, max_delta] 的范围随机调整图像的色相。
	max_delta = 0.5
	adjusted5 = tf.image.random_hue(img_data, max_delta)

d、调整饱和度

	# 将图像的饱和度-5
	adjusted1 = tf.image.adjust_saturation(img_data, -5) 
	# 将图像的饱和度+5
	adjusted2 = tf.image.adjust_saturation(img_data, 5)
	# 在[lower, upper] 的范围随机调整图像的饱和度
	lower = 10
	upper = 100
	adjusted3 = tf.image.random_saturation(img_data, lower, upper)

e、图像标准化

	adjusted = tf.image.per_image_standardization(img_data)

(5)处理标注框

加入标注框:

tf.image.draw_bounding_boxes()函数在图像中添加标注框。输入图像矩阵的数字为实数,且是一个 batch 的数据(四维张量)。所以,绘制图时,需要tf.reduce_sum()函数进行降维。

	# 将图像缩小一些,这样可视化能让标注框更加清楚。
	img_data = tf.image.resize_images(img_data, [180, 267], method=1)
	# tf.image.draw_bounding_boxes 函数要求图像矩阵中的数字为实数.
	# -> 先将图像矩阵转化为实数类型。
	# 输入是一个 bacth 的数据(多张图像组成的四维矩阵),要将解码之后的图像矩阵添加一维
	bacthed = tf.expand_dims(tf.image.convert_image_dtype(img_data, tf.float32), 0)
	# 给出每一张图像的所有标注框。一个标注框有 4 个数字,分别代表[Y_min, X_min, Y_max, X_max]
	# 注意这里给出的数字都是图像的相对位置。
	# 比如在 180 × 267 的图像中,[0.35, 0.47, 0.5, 0.56]代表了从(63,125)到(90, 150)的图像。
	boxes = tf.constant([[[0.05, 0.05, 0.9, 0.7], [0.35, 0.47, 0.5, 0.56]]])
	
	result = tf.image.draw_bounding_boxes(bacthed, boxes)
	
	result = tf.reduce_sum(result, 0)  # 这里显示的时候需要进行降维处理
	plt.imshow(result.eval())
	plt.show()

随机截取图像:

随机截取图像上有信息含量的部分也是一个提
高模型健壮性( robustness )的一种方式。
==》使得训练模型不受被识别物体大小的影响。
==》tf.image.sample_distorted_bounding_box()函数

boxes = tf.constant([[[0.05, 0.05, 0.9, 0.7], [0.35, 0.47, 0.5, 0.56]]])
	# min_object_covered=0.4 表示截取部分至少包含某个标注框 40% 的内容。
	begin, size, bbox_for_draw = tf.image.sample_distorted_bounding_box(
		tf.shape(img_data), bounding_boxes = boxes,
		min_object_covered=0.4)
	# 通过标注框可视化随机截取得到的图像。
	bacthed = tf.expand_dims(tf.image.convert_image_dtype(img_data, tf.float32), 0)
	
	image_with_box = tf.image.draw_bounding_boxes(bacthed, bbox_for_draw)
	# 截取随机出来的图像。每次结果可能不尽相同.
	distored_image = tf.slice(img_data, begin, size)

2.2、预处理完整样例代码

输入:原始训练图像
输出:神经网络的输入层

# -*-coding:utf-8 -*-

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

# 给定一张图像,随机调整图像的色彩。
# 因为调整亮度、对比度、饱和度和色相的顺序会影响最后的结果,所以定义多种不同的顺序。
# 具体顺序可在预处理时随机选取一种。从而降低无关因素对模型的影响。
# 亮度(brightness) 对比度(contrast)、饱和度(saturation)和色相(hue)
def distort_color(image, color_ordering=0):
	if color_ordering == 0:
		image = tf.image.random_brightness(image, max_delta=32./255.)  # 
		image = tf.image.random_saturation(image, lower=0.5, upper=1.5)
		image = tf.image.random_hue(image, max_delta=0.2)
		image = tf.image.random_contrast(image, lower=0.5, upper=1.5)
	elif color_ordering == 1:
		image = tf.image.random_saturation(image, lower=0.5, upper=1.5)
		image = tf.image.random_brightness(image, max_delta=32./255.)
		image = tf.image.random_contrast(image, lower=0.5, upper=1.5)
		image = tf.image.random_hue(image, max_delta=0.2)
	elif color_ordering == 2:
		image = tf.image.random_hue(image, max_delta=0.2)
		image = tf.image.random_contrast(image, lower=0.5, upper=1.5)
		image = tf.image.random_brightness(image, max_delta=32./255.)
		image = tf.image.random_saturation(image, lower=0.5, upper=1.5)

	# 截断在 0.0-1.0 范围区间
	return tf.clip_by_value(image, 0.0, 1.0)

# 给定一张解码后的图像、目标图像的尺寸以及图像上的标注框,
# 此函数可对给出的图像进行预处理
# 输入:原始训练图像
# 输出:神经网络的输入层
def preprocess_for_train(image, height, width, bbox):
	# 如果没有提供标注框,则认为整个图像就是需要关注的部分。
	if bbox is None:
		bbox = tf.constant([0.0, 0.0, 1.0, 1.0], dtype=tf.float32, 
			shape=[1, 1, 4])
	
	# 转换图像张量的类型 。
	if image.dtype != tf.float32:
		image = tf.image.convert_image_dtype(image, dtype=tf.float32)

	# 随机截取图像,减小需要关注的物体大小对图像识别算法的影响。
	bbox_begin, bbox_size, _ = tf.image.sample_distorted_bounding_box(
		tf.shape(image), bounding_boxes=bbox)
	distorted_image = tf.slice(image, bbox_begin, bbox_size)

	# 将随机截取的图像调整为神经网络输入层的大小。大小调整的算法是随机选摔的 。
	distorted_image = tf.image.resize_images(
		distorted_image, [height, width], method=np.random.randint(4))
	# 随机左右翻转图像。
	distorted_image = tf.image.random_flip_left_right(distorted_image)
	# 使用一种随机的顺序调整图像色彩。
	distorted_image = distort_color(distorted_image, np.random.randint(3))
	return distorted_image

IMAGE_PATH = '/home/jie/Desktop/6987985.jpeg'
image_raw_data = tf.gfile.FastGFile(IMAGE_PATH, "rb").read()
with tf.Session() as sess:
	img_data = tf.image.decode_jpeg(image_raw_data)
	boxes = tf.constant([[[0.05, 0.05, 0.9, 0.7], [0.35, 0.47, 0.5, 0.56]]])
	
	for i in range(6):
		result = preprocess_for_train(img_data, 299, 299, boxes)
		plt.imshow(result.eval())
		plt.show()

 

3、多线程输入数据处理框架

复杂的预处理会减慢神经模型训练的效率,所以采用多线程的方式来加速,TensorFlow提供了一套处理数据输入的框架。

框架的输入流程:

3.1、队列与多线程

队列与多线程有什么关系?

队列--吃了拉,先进先出的数据结构。

队列是用来储存数据的,而线程是来操作队列的。当有多个线程时,称之为多线程。

(1)队列

FIFOQueue先进先出队列。

RandomShuffleQueue将队列中的元素打乱,每次出队列操作得到的是从当前队列所有元素中随机选择的一个。(训练网络时希望每次使用的训练数据尽可能随机,推荐使用)

import tensorflow as tf

# 创建一个先进先出队列,指定队列中最多可以保存两个元素,并指定类型为整数。
q = tf.FIFOQueue(2, "int32")
# enqueue_many 函数:初始化队列中的元素,需要明确的调用这个初始化过程。
init = q.enqueue_many(([0, 10],))
# Dequeue 函数:队列中的第一个元素出队列,并存入x中
x = q.dequeue()

y = x + 1
# 将y重新加入队列。
q_inc = q.enqueue([y])

with tf.Session() as sess:
	# 运行初始化队列的操作。
	init.run()
	for _ in range(5):
		# 运行q_inc将执行数据出队列、出队的元素+1、重新归入队列的整个过程。
		v, _ = sess.run([x, q_inc])
		print(v)

(2)多线程协同方法:

a、tf.Coordinator类:用于协同多个线程一起停止, 并提供了 should_stoprequest_stop 和 join 三个函数。

# 线程中运行的程序,这个程序每隔1秒判断是否需要停止并打印向己的ID 。
def MyLoop(coord, worker_id):
	# 使用 tf.Coordinator 类提供的协同工具判断当前线程是否要停止。
	while not coord.should_stop():
		# 随机停止所有的线程。
		if np.random.rand() < 0.1:
			print("Stoping from id: %d\n" % worker_id)
			# 调用 coord.request_stop() 函数来通知其他线程停止。
			coord.request_stop()
		else:
			# 打印当前线程的Id。
			print("Working on id: %d\n" % worker_id)
		time.sleep(1)

# 声明一个 tf.train.Coordinator 类来协同多个线程。
coord = tf.train.Coordinator()
# 声明创建 5 个线程
threads = [threading.Thread(target=MyLoop, args=(coord, i)) for i in range(5)]
# 启动所有线程
for t in threads:
	t.start()
# 等待所有线程退出
coord.join(threads)

b、tf.QueueRunner 类

# 声明一个先进先出的队列,队列中最多100个元素,类型是实数
queue = tf.FIFOQueue(100, "float")
# 定义队列的入队操作
enqueue_op = queue.enqueue([tf.random_normal([1])])


# 使用 tf.train.QueueRunner 创建多个线程运行队列的入队操作。
# 第1个参数:被操作的队列,
# [enqueue_op]*5 需要启动5个线程,每个线程中运行的是 enqueue_op 操作。
qr = tf.train.QueueRunner(queue, [enqueue_op] * 5)

# 将定义过的QueueRunner加入TensorFlow 计算图上指定的集合。
# tf.train.add_queue_runner 函数没有指定集合,则加入默认集合 tf.GraphKeys.QUEUE_RUNNERS。
# 下面的函数就是将刚刚定义的 qr 加入默认的 tf.GraphKeys.QUEUE_RUNNERS 集合。
tf.train.add_queue_runner(qr)
# 定义出队操作。
out_tensor = queue.dequeue()

with tf.Session() as sess:
	# 使用 tf.train.Coordinator 来协同启动的线程。
	coord = tf.train.Coordinator()
	# 使用tf.train.QueueRunner时,需要明确调用tf.train.start_queue_runners来启动所有线程。
	# 否则因为没有线程运行入队操作,当调用出队操作时,程序会一直等待入队操作被运行。
	# tf.train.start_queue_runners 函数会默认启动tf.GraphKeys.QUEUE_RUNNERS集合中所有的QueueRunner。
	# 这个函数只支持启动指定集合中的 QueueRnner,
	# 所以一般来说 tf.train.add_queue_runner 函数和tf.train.start_queue_runners函数会指定同一个集合。
	threads = tf.train.start_queue_runners(sess = sess, coord=coord)
	# 获取队列中的取值。
	for _ in range(3):
		print(sess.run(out_tensor)[0])

	# 使用 tf.train.Coordinator 来停止所有的线程。
	coord.request_stop()
	coord.join(threads)

3.2、输入文件队列

输入队列的就是TensorFlow的数据格式TFRecord。

虽然一个TFRecord文件可以存储多个训练样本,但当训练数据量较大时,可以将数据分成多个TFRecord文件来提高处理效率。

(1)获取一个正则表达式的所有文件: tf.train.match_filenames_once
(2)进行有效的管理:tf.train.string_input_producer

生成样例:

# 创建 TFRecord 文件的帮助函数。
def _int64_feature(value):
	return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))

# 模拟海量数据情况下将数据写入不同文件
# num_shards : 总共写入多少个文件,
# instances_per_shard : 每个文件中有多少个数据。
num_shards = 2
instances_per_shard = 2
for i in range(num_shards):
	# 将数据分为多个文件时,可以将不同文件以类似 OOOOn-of-OOOOm 的后缀区分。
	# m:表示数据总共被存在了多少个文件中
	# n:表示当前文件的编号
	# 式样的方式既方便了通过正则表达式获取文件列表,又在文件名中加入了更多的信息
	filename = ('./datasets/data.tfrecords-%.5d-of-%.5d' % (i, num_shards))
	writer = tf.python_io.TFRecordWriter(filename)
	# 将数据封装成 Example 结构并写入 TFRecord 文件 。
	for j in range(instances_per_shard):
		# Example 结构仅包含当前样例属于第几个文件以及是当前文件的第几个样本。
		example = tf.train.Example(features=tf.train.Features(feature={
			'i':_int64_feature(i),
			'j':_int64_feature(j)}))
		writer.write(example.SerializeToString())
	writer.close()

生成样例之后,以下代码展示了两个函数的使用方法:

# 使用 tf.train.match_filenames_once 函数获取文件列表。
files = tf.train.match_filenames_once('./datasets/data.tfrecords-*')

# 通过 tf.train.string_input_producer 函数创建输入队列.
# 输入队列巾的中的文件列表为tf.train.string_input_producer函数获取的文件列表。
# 参数设置:shuffle为False,在一般解决真实问题时,shuffle设置为True
filename_queue = tf.train.string_input_producer(files, shuffle=False)

reader = tf.TFRecordReader()
_, seraialized_example = reader.read(filename_queue)
features = tf.parse_single_example(
	seraialized_example,
	features={
		'i':tf.FixedLenFeature([], tf.int64),
		'j':tf.FixedLenFeature([], tf.int64),
	})

with tf.Session() as sess:
	# 虽然在本段程序中没有声明任何变量,
	# 但使用 tf.train.match_filenames_once 函数时前要初始化一些变量 。
	tf.local_variables_initializer().run()
	print(sess.run(files))

	# 声明 tf.train.Coordinator 类来协同不同线程,并启动线程。
	coord = tf.train.Coordinator()
	threads = tf.train.start_queue_runners(sess=sess, coord=coord)

	# 多次执行技取数据的操作。
	for i in range(6):
		print(sess.run([features['i'], features['j']]))
	coord.request_stop()

3.3、组合训练数据(batching)

此处的batching和反向传播的batch的区别?

是同一个batch,取的batch先通过图像预处理,然后通过前向传播算法算出loss,然后通过反向传播算法来优化。

两个生成batch的函数:

tf.train.batch 和 tf.train.shuffle_batch 函数来将单个的样例组织成 batch 的形式输出。这两个函数都会生成一个队列,队列的入队操作是生成单个样例的方法,而每次出队得到的是一个 batch 的样例 。
唯一区别:是否会将数据顺序打乱 。tf.train.shuffle_batch 为乱序。

(1)tf.train.batch

import tensorflow as tf

# 使用 tf.train.match_filenames_once 函数获取文件列表。
files = tf.train.match_filenames_once('./datasets/data.tfrecords-*')

# 通过 tf.train.string_input_producer 函数创建输入队列.
# 输入队列巾的中的文件列表为tf.train.string_input_producer函数获取的文件列表。
# 参数设置:shuffle为False,在一般解决真实问题时,shuffle设置为True
filename_queue = tf.train.string_input_producer(files, shuffle=False)

reader = tf.TFRecordReader()
_, seraialized_example = reader.read(filename_queue)
features = tf.parse_single_example(
	seraialized_example,
	features={
		'i':tf.FixedLenFeature([], tf.int64),
		'j':tf.FixedLenFeature([], tf.int64),
	})

# 使用上面样例。这里假设 Example 结构中 i 表示一个样例的特征向量,
# 比如一张图像的像索矩阵。而 j 表示该样例对应的标签 。
example, label = features['i'], features['j']

# 一个 batch 中样例的个数。
batch_size = 3
# 文件队列中最多可以存储的样例个数
capacity = 1000 + 3 * batch_size

# 使用 tf.train.batch 函数来组合样例。[example, label]参数给出需组合的元素.
# 当队列长度等于容量时,TF将暂停入队操作,而只是等待元索出队。
# 当元素个数小于容量时, TF将自动重新启动入队操作。
example_batch, label_batch = tf.train.batch(
	[example, label], batch_size=batch_size, capacity=capacity)

tf.local_variables_initializer().run()

with tf.Session() as sess:
	# tf.initialize_all_variables().run()
	tf.local_variables_initializer().run()
	coord = tf.train.Coordinator()
	threads = tf.train.start_queue_runners(sess=sess, coord=coord)

	# 获取并打印组合之后的样例。在真实问题中,这个输出一般会作为神经网络的输入。
	for i in range(2):
		cur_example_batch, cur_label_batch = sess.run([example_batch, label_batch])
		print(cur_example_batch, cur_label_batch)

	coord.request_stop()
	coord.join(threads)

(2)tf.train.shuffle_batch

import tensorflow as tf

# 获取文件列表。
files = tf.train.match_filenames_once('./datasets/data.tfrecords-*')

# 创建文件输入队列
filename_queue = tf.train.string_input_producer(files, shuffle=False)

# 读取并解析Example
reader = tf.TFRecordReader()
_, seraialized_example = reader.read(filename_queue)
features = tf.parse_single_example(
	seraialized_example,
	features={
		'i':tf.FixedLenFeature([], tf.int64),
		'j':tf.FixedLenFeature([], tf.int64),
	})

# i代表特征向量,j代表标签
example, label = features['i'], features['j']

# 一个 batch 中样例的个数。
batch_size = 3
# 文件队列中最多可以存储的样例个数
capacity = 1000 + 3 * batch_size

# 组合样例。
# `min_after_dequeue` 是该函数特有的参数,参数限制了出队时队列中元素的最少个数,
# 但当队列元素个数太少时,随机的意义就不大了
example_batch, label_batch = tf.train.shuffle_batch(
	[example, label], batch_size=batch_size, 
	capacity=capacity, min_after_dequeue=30)

with tf.Session() as sess:
	# 使用match_filenames_once需要用local_variables_initializer初始化一些变量
	tf.local_variables_initializer().run()

	# 用Coordinator协同线程,并启动线程
	coord = tf.train.Coordinator()
	threads = tf.train.start_queue_runners(sess=sess, coord=coord)

	# 获取并打印组合之后的样例。在真实问题中,这个输出一般会作为神经网络的输入。
	for i in range(2):
		cur_example_batch, cur_label_batch = sess.run([example_batch, label_batch])
		print(cur_example_batch, cur_label_batch)

	coord.request_stop()
	coord.join(threads)

3.4、输入数据处理框架

这一节把前面的所有整合到一起,组成完整的tensorflow图像预处理框架。主要是三步:

(1)将图片格式变为TFRecord格式

(2)需要的话开辟多线程

(3)一系列的图像预处理(解码、翻转、大小、处理标注框等等)

import tensorflow as tf

# 获取文件列表。
files = tf.train.match_filenames_once('./datasets/data.tfrecords-*')

# 创建文件输入队列
filename_queue = tf.train.string_input_producer(files, shuffle=False)

# 读取并解析Example
reader = tf.TFRecordReader()
_, seraialized_example = reader.read(filename_queue)
features = tf.parse_single_example(
	seraialized_example,
	features={
		'image':tf.FixedLenFeature([], tf.string),
		'label':tf.FixedLenFeature([], tf.int64),
		'height':tf.FixedLenFeature([], tf.int64),
		'width':tf.FixedLenFeature([], tf.int64),
		'channels':tf.FixedLenFeature([], tf.int64),
	})
image, label = features['image'], features['label']
height, width = features['height'], features['width']
channels = features['channels']

# 从原始图像数据解析出像素矩阵,并根据图像尺寸还原图像。
decoded_image = tf.decoded_raw(image, tf.int8)
decoded_image.set_shape([height, width, channels])

# 定义神经网络输入层图片的大小。
image_size = 299

# 图像预处理
distorted_image = preprocess_for_train(decoded_image, image_size, image_size, None)

# 将处理后的图像和标续数据通过 tf.train.shuffle_batch 整理成神经网络训练时需要的 batch,
min_after_dequeue = 10000
batch_size = 100
capacity = min_after_dequeue + 3 * batch_size

# 组合样例。
# `min_after_dequeue` 是该函数特有的参数,参数限制了出队时队列中元素的最少个数,
# 但当队列元素个数太少时,随机的意义就不大了
image_batch, label_batch = tf.train.shuffle_batch(
	[distorted_image, label], batch_size=batch_size, 
	capacity=capacity, min_after_dequeue=min_after_dequeue)

# 定义神经网络的结构以及优化过程
learing_rate = 0.01
logit = inferenece(image_batch)
loss = calc_loss(logit, label_batch)
train_step = tf.train.GradientDescentOptimizer(learing_rate).minimize(loss)



with tf.Session() as sess:
	# 使用match_filenames_once需要用local_variables_initializer初始化一些变量
	sess.run((tf.global_variables_initializer(), tf.local_variables_initializer()))

	# 用Coordinator协同线程,并启动线程
	coord = tf.train.Coordinator()
	threads = tf.train.start_queue_runners(sess=sess, coord=coord)

	# 神经网络训练过程
	TRAINING_ROUNDS = 5000
	for i in range(TRAINING_ROUNDS):
		sess.run(train_step)

	coord.request_stop()
	coord.join(threads)

4、数据集API(Dataset)

TF核心组件:tf.data

这是一个数据集的API,可以使数据的输入和处理大大简化。(一种新的方式,和3.4节并列)

使用数据集的方式实现3.4节的代码功能:

import tensorflow as tf
import tempfile

# 1. 列举输入文件
TRAIN_PATH = 'output.tfrecords'
TEST_PATH = 'output_test.tfrecords'
train_files = tf.train.match_filenames_once(TRAIN_PATH)
test_files = tf.train.match_filenames_once(TEST_PATH)

# 2. 定义parser方法TFRecord中解析数据
# 解析一个TFRecord的方法。
def parser(record):
	features = tf.parse_single_example(
		record,
		features={
			'image_raw':tf.FixedLenFeature([],tf.string),
			'pixels':tf.FixedLenFeature([],tf.int64),
			'label':tf.FixedLenFeature([],tf.int64)
		})
	decoded_images = tf.decode_raw(features['image_raw'],tf.uint8)
	retyped_images = tf.cast(decoded_images, tf.float32)
	images = tf.reshape(retyped_images, [784])
	labels = tf.cast(features['label'],tf.int32)
	#pixels = tf.cast(features['pixels'],tf.int32)
	return images, labels

image = 299				 	# 定义神经网络输入层图片的大小
batch_size = 100			# 定义组合数据 batch 的大小
shuffle_buffer = 10000		# 定义随机打乱数据时buffer大小

# 定义读取训练集和测试集
dataset = tf.data.TFRecordDataset(train_files)
dataset = dataset.map(parser)

# 对数据依次进行预处理、shuffle、batching操作
# dataset = dataset.map(
# 	lambda image, label: (
# 		preprocess_for_train(image, image_size, image_size, None), label))
dataset = dataset.shuffle(shuffle_buffer).batch(batch_size)

# 重复NUM_EPOCHS 个epoch
NUM_EPOCHS = 10
dataset = dataset.repeat(NUM_EPOCHS)

# 定义数据集迭代器。
iterator = dataset.make_initializable_iterator()
image_batch, label_batch = iterator.get_next()

# 4. 定义神经网络结构和优化过程
def inference(input_tensor, weights1, biases1, weights2, biases2):
	layer1 = tf.nn.relu(tf.matmul(input_tensor, weights1) + biases1)
	return tf.matmul(layer1, weights2) + biases2

INPUT_NODE = 784
OUTPUT_NODE = 10
LAYER1_NODE = 500
REGULARAZTION_RATE = 0.0001
TRAINING_STEP = 5000

weights1 = tf.Variable(tf.truncated_normal([INPUT_NODE, LAYER1_NODE], stddev=0.1))
biases1 = tf.Variable(tf.constant(0.1, shape=[LAYER1_NODE]))
weights2 = tf.Variable(tf.truncated_normal([LAYER1_NODE, OUTPUT_NODE], stddev=0.1))
biases2 = tf.Variable(tf.constant(0.1, shape=[OUTPUT_NODE]))

y = inference(image_batch, weights1, biases1, weights2, biases2)

# 计算交叉熵及其平均值
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=label_batch)
cross_entropy_mean = tf.reduce_mean(cross_entropy)
    
# 损失函数的计算
regularizer = tf.contrib.layers.l2_regularizer(REGULARAZTION_RATE)
regularaztion = regularizer(weights1) + regularizer(weights2)
loss = cross_entropy_mean + regularaztion

# 优化损失函数
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(loss)

# 5. 定义测试用数据集
# 定义测试用的Dataset。
test_dataset = tf.data.TFRecordDataset(test_files)
test_dataset = test_dataset.map(parser)
test_dataset = test_dataset.batch(batch_size)

#  定义测试数据上的迭代器
test_iterator = test_dataset.make_initializable_iterator()
test_image_batch, test_label_batch = test_iterator.get_next()

# 定义测试数据上的预测结果
test_logit = inference(test_image_batch, weights1, biases1, weights2, biases2)
predictions = tf.argmax(test_logit, axis=-1, output_type=tf.int32)

# 声明会话并运行神经网络的优化过程。
with tf.Session() as sess:
	# 初始化变量。
	sess.run((tf.global_variables_initializer(),
				tf.local_variables_initializer()))

	# 初始化训练数据的迭代器。
	sess.run(iterator.initializer)

	# 循环进行训练,直到数据集完成输入、抛出OutOfRangeError错误。
	while True:
		try:
			sess.run(train_step)
		except tf.errors.OutOfRangeError as e:
			break
	
	# 初始化测试数据的迭代器。
	test_results = []
	test_labels = []
	sess.run(test_iterator.initializer)
	while True:
		try:
			pred, label = sess.run([predictions, test_label_batch])
			test_results.extend(pred)
			test_labels.extend(label)
		except tf.errors.OutOfRangeError as e:
			break

# 计算准确率
correct = [float(y==y_) for (y, y_) in zip(test_results, test_labels)]
accuarcy = sum(correct)/len(correct)
print("Test accuarcy is: ", accuarcy)

 

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值