🧡💛💚TensorFlow2实战-系列教程 总目录
有任何问题欢迎在下面留言
本篇文章的代码运行界面均在Jupyter Notebook中进行
本篇文章配套的代码资源已经上传
5、图像数据处理实例
5.1 读数据
import os
import glob
from datetime import datetimeimport cv2
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
%matplotlib inline
image_path = '../img/'
images = glob.glob(image_path + '*.jpg')for fname in images:image = mpimg.imread(fname)f, (ax1) = plt.subplots(1, 1, figsize=(8,8))f.subplots_adjust(hspace = .2, wspace = .05)ax1.imshow(image)ax1.set_title('Image', fontsize=20)image_labels = {'dog': 0,'kangaroo': 1,
}
5.2 制作TFRecord
# 读数据,binary格式
image_string = open('./img/dog.jpg', 'rb').read()
label = image_labels['dog']
打开一张图像和它对应的标签
def _bytes_feature(value):"""Returns a bytes_list from a string/byte."""if isinstance(value, type(tf.constant(0))):value = value.numpy() # BytesList won't unpack a string from an EagerTensor.return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))def _float_feature(value):"""Return a float_list form a float/double."""return tf.train.Feature(float_list=tf.train.FloatList(value=[value]))def _int64_feature(value):"""Return a int64_list from a bool/enum/int/uint."""return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))
前面3个处理字符、浮点数、整型的函数
# 创建图像数据的Example
def image_example(image_string, label):image_shape = tf.image.decode_jpeg(image_string).shapefeature = {'height': _int64_feature(image_shape[0]),'width': _int64_feature(image_shape[1]),'depth': _int64_feature(image_shape[2]),'label': _int64_feature(label),'image_raw': _bytes_feature(image_string),}return tf.train.Example(features=tf.train.Features(feature=feature))
定义一个函数,指定要保存的指标,以及要用什么格式存这批数据
image_shape 就是图像长、宽、通道数
feature 中定义了图像h、w、c、标签、矩阵特征
最后构建Example返回一条数据
#打印部分信息
image_example_proto = image_example(image_string, label)for line in str(image_example_proto).split('\n')[:15]:print(line)
print('...')
调用刚刚的函数,传进实际的数据和标签
把转换的数据打印出来
# 制作 `images.tfrecords`.image_path = './img/'
images = glob.glob(image_path + '*.jpg')
record_file = 'images.tfrecord'
counter = 0with tf.io.TFRecordWriter(record_file) as writer:for fname in images:with open(fname, 'rb') as f:image_string = f.read()label = image_labels[os.path.basename(fname).replace('.jpg', '')]tf_example = image_example(image_string, label)writer.write(tf_example.SerializeToString())counter += 1print('Processed {:d} of {:d} images.'.format(counter, len(images)))print(' Wrote {} images to {}'.format(counter, record_file))
- 指定所有数据的路径
- 指定最后保存的数据的路径和名称
- 计数器
- 打开TFRecordWriter,准备写数据
- 遍历所有的图像数据路径
- 打开当前路径的文件
- 读取图像
- 映射图像文件名(无扩展名)到标签
- 使用 image_example 函数(需要事先定义)创建 tf.Example 对象
- 将其序列化后写入 TFRecord 文件
- 每处理一个图像文件,计数器增加
- 并打印出已处理的图像数量
- 所有图像处理完成后,打印出写入 TFRecord 文件的总图像数量
打印结果:
Processed 1 of 2 images.
Processed 2 of 2 images.
Wrote 2 images to images.tfrecord
5.3 加载制作好的TFRecord
raw_train_dataset = tf.data.TFRecordDataset('images.tfrecord')
raw_train_dataset
<TFRecordDatasetV2 shapes: (), types: tf.string>
example数据都进行了序列化,还需要解析以下之前写入的序列化string,即反序列化
- tf.io.parse_single_example(example_proto, feature_description)函数可以解析单条example
这个函数是专门用来解析图像数据的
# 解析的格式需要跟之前创建example时一致
image_feature_description = {'height': tf.io.FixedLenFeature([], tf.int64),'width': tf.io.FixedLenFeature([], tf.int64),'depth': tf.io.FixedLenFeature([], tf.int64),'label': tf.io.FixedLenFeature([], tf.int64),'image_raw': tf.io.FixedLenFeature([], tf.string),
}
现在看起来仅仅完成了一个样本的解析,实际数据不可能一个个来写吧,可以定义一个映射规则map函数
解析的格式,要和原来一致
def parse_tf_example(example_proto):parsed_example = tf.io.parse_single_example(example_proto, image_feature_description)x_train = tf.image.decode_jpeg(parsed_example['image_raw'], channels=3)x_train = tf.image.resize(x_train, (416, 416))x_train /= 255.lebel = parsed_example['label']y_train = lebelreturn x_train, y_traintrain_dataset = raw_train_dataset.map(parse_tf_example)
train_dataset
- 定义一个专门用来解析的函数
- 传进解析的样本、解析的对照关系image_feature_description
- 进行预处理操作,将原始的二进制 JPEG 图像数据解码为Tensor
- 将图像大小调整为 416x416 像素
- 将图像数据归一化到 0 到 1 的范围
- 提取 label 字段并赋值给 y_train
- 返回处理后的图像数据和标签
- 将 parse_tf_example 函数应用于原始的训练数据集 raw_train_dataset,map 函数会对数据集中的每个元素应用 parse_tf_example 函数
打印结果:
<MapDataset shapes: ((416, 416, 3), ()), types: (tf.float32, tf.int64)>
5.4 制作训练集
num_epochs = 10train_ds = train_dataset.shuffle(buffer_size=10000).batch(2).repeat(num_epochs)
for batch, (x, y) in enumerate(train_ds):print(batch, x.shape, y)
把数据转换成batch形式,然后把转换的数据打印出来
打印结果:
0 (2, 416, 416, 3) tf.Tensor([0 1], shape=(2,), dtype=int64)
1 (2, 416, 416, 3) tf.Tensor([0 1], shape=(2,), dtype=int64)
…
8 (2, 416, 416, 3) tf.Tensor([1 0], shape=(2,), dtype=int64)
9 (2, 416, 416, 3) tf.Tensor([1 0], shape=(2,), dtype=int64)
model = tf.keras.Sequential([tf.keras.layers.Flatten(),tf.keras.layers.Dense(2, activation='softmax')
])
model.compile(optimizer='adam',loss=tf.keras.losses.SparseCategoricalCrossentropy(),metrics=['accuracy'])
model.fit(train_ds, epochs=num_epochs)
定义一个简单的模型、训练器,然后进行训练
打印结果:
Epoch 1/10 10/10 1s 51ms/step - loss: 55.1923 - accuracy: 0.6500
…
Epoch 9/10 10/10 0s 19ms/step - loss: 0.0000e+00 - accuracy: 1.0000
Epoch 10/10 10/10 0s 19ms/step - loss: 0.0000e+00 - accuracy: 1.0000
<tensorflow.python.keras.callbacks.History at 0x274f2524400>