tensorflow之图片预处理

每一个懂事淡定的现在,都有一个很傻很天真的过去,每一个温暖而淡然的如今,都有一个悲伤而不安的曾经

Posted by yishuifengxiao on 2021-06-12

一 tensorflow2读取图片

1.1 读取单个文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import tensorflow as tf
import numpy as np
from matplotlib import pyplot as plt

img_path = r'C:\Users\yishui\Desktop\1111.jpg'
# 读入图片
img = tf.io.read_file(img_path)

print(" 读取的原始图片的类型为 ", type(img), "形状为 ", img.shape, "数据类型 dtype:", img.dtype)

# 解码为tensor格式
decode_img = tf.image.decode_jpeg(img)
print(" 解码后的图片的类型为 ", type(decode_img), "形状为 ", decode_img.shape, "数据类型 dtype:", decode_img.dtype)

# 将图片转换问灰度图片,即最后一维只有1
convert_img = tf.image.rgb_to_grayscale(decode_img)

print(" 图片转换问灰度图片的类型为 ", type(convert_img), "形状为 ", convert_img.shape, "数据类型 dtype:", convert_img.dtype)

# 剪裁图片, 高度和宽度
resize_img = tf.image.resize_with_crop_or_pad(convert_img, 250, 151)

print(" 剪裁图片后图片的类型为 ", type(resize_img), "形状为 ", resize_img.shape, "数据类型 dtype:", resize_img.dtype)

# 编码tensor
encode_jpeg = tf.image.encode_jpeg(resize_img, quality=100)

print(" 编码tensor后图片的类型为 ", type(encode_jpeg), "形状为 ", encode_jpeg.shape, "数据类型 dtype:", encode_jpeg.dtype)

# 写入./221.jpg这个文件里面
with tf.io.gfile.GFile('./221.jpg', 'wb') as file:
file.write(encode_jpeg.numpy())

关键API说明

  • tf.io.read_file(filename)  用来读取文件。
  • tf.image.decode_jpeg(contens)  将图片文件解码为张量,这里是jpg格式,其他的格式也在tf.image模块中。

运行结果为

1
2
3
4
5
6
7
8
9
读取的原始图片的类型为  <class 'tensorflow.python.framework.ops.EagerTensor'> 形状为  () 数据类型 dtype: <dtype: 'string'>

解码后的图片的类型为 <class 'tensorflow.python.framework.ops.EagerTensor'> 形状为 (600, 600, 3) 数据类型 dtype: <dtype: 'uint8'>

图片转换问灰度图片的类型为 <class 'tensorflow.python.framework.ops.EagerTensor'> 形状为 (600, 600, 1) 数据类型 dtype: <dtype: 'uint8'>

剪裁图片后图片的类型为 <class 'tensorflow.python.framework.ops.EagerTensor'> 形状为 (250, 151, 1) 数据类型 dtype: <dtype: 'uint8'>

编码tensor后图片的类型为 <class 'tensorflow.python.framework.ops.EagerTensor'> 形状为 () 数据类型 dtype: <dtype: 'string'>

image-20210604100217925

image-20210604100246978

1.2 批量读取文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import tensorflow as tf
import os
import numpy as np

# 训练集图片所在的文件夹,以训练集为例,文件的命名为“类别_顺序.jpg”
train_path = "C:\\Users\\yishui\\Desktop\\images\\train\\"

# 这个函数用于返回符合,可以使用正则路径,*表示任意字符
path_list = tf.data.Dataset.list_files(train_path + "*.jpg")


# 定义一个读取图片的函数
def read_image(path_list):
'''
:param path_list:文件的路径list
:return: 图片张量列表,图片标签列表
'''
images = [] # 图片列表
image_labels = [] # 图片标签列表

# 根据文件路径列表依次读取
for i in path_list:
# tesnsorflow的io读取文件
image_temp = tf.io.read_file(i)

# 根据图片的格式进行编码转化为张量,这里图片是jpg格式
image_temp = tf.image.decode_jpeg(image_temp)

# 图片加入到数据集
images.append(image_temp)

# 获取文件名加入到标签,这里要张量i转化为字符串
image_labels.append(os.path.basename(str(i)).split('_')[0])

return np.array(images), np.array(image_labels)


# 读取训练图片
train_images, train_labels = read_image(path_list=path_list)
print(" 读取的图片的类型为 ", type(train_images), "形状为 ", train_images.shape, "数据类型 dtype:", train_images.dtype)

运行结果为

1
读取的图片的类型为  <class 'numpy.ndarray'> 形状为  (2, 600, 600, 3) 数据类型 dtype: uint8

说明:

在文件夹C:\\Users\\yishui\\Desktop\\images\\train\\里只有两个图片

image-20210604101808254

1.3 使用keras读取图片

1
2
3
4
5
6
7
8
9
10
11
12
13
image_size = (180, 180)
img = keras.preprocessing.image.load_img(
"PetImages/Cat/6779.jpg", target_size=image_size
)
img_array = keras.preprocessing.image.img_to_array(img)
img_array = tf.expand_dims(img_array, 0) # Create batch axis

predictions = model.predict(img_array)
score = predictions[0]
print(
"This image is %.2f percent cat and %.2f percent dog."
% (100 * (1 - score), 100 * score)
)

参考

二、图片数据增强

2.1 基础使用  

在读入数据之后,数据可能需要做一下强化操作,这些操作可以增加模型的泛性,同时可以扩大我们的数据集,常见的数据强化操作如下:

  • 旋转 | 反射变换(Rotation/reflection): 随机旋转图像一定角度; 改变图像内容的朝向;
  • 翻转变换(flip): 沿着水平或者垂直方向翻转图像;
  • 缩放变换(zoom): 按照一定的比例放大或者缩小图像;
  • 平移变换(shift): 在图像平面上对图像以一定方式进行平移;可以采用随机或人为定义的方式指定平移范围和平移步长, 沿水平或竖直方向进行平移. 改变图像内容的位置;
  • 尺度变换(scale): 对图像按照指定的尺度因子, 进行放大或缩小; 或者参照SIFT特征提取思想, 利用指定的尺度因子对图像滤波构造尺度空间. 改变图像内容的大小或模糊程度;
  • 对比度变换(contrast): 在图像的HSV颜色空间,改变饱和度S和V亮度分量,保持色调H不变. 对每个像素的S和V分量进行指数运算(指数因子在0.25到4之间), 增加光照变化;
  • 噪声扰动(noise): 对图像的每个像素RGB进行随机扰动, 常用的噪声模式是椒盐噪声和高斯噪声;
  • 错切变换(shear):效果就是让所有点的x坐标(或者y坐标)保持不变,而对应的y坐标(或者x坐标)则按比例发生平移,且平移的大小和该点到x轴(或y轴)的垂直距离成正比。

  tensorflow的图片强化API需要一下的类及类的方法,对应的API如下:

用来创建一个图片数据集迭代器,可以在传入数据时进行一些数据加强操作

1
2
  
  tf.keras.preprocessing.image.ImageDataGenerator()

  - rescale:缩放因子,图像像素值乘以rescale,如果为255可以将像素值归一到0-1区间。
  - featurewise_center:特征均值化,若为True,对输入的图片每个通道减去每个通道对应均值,与rescale=1/255相同。
  - samplewise_center:样本均值化。
  - rotation_range:随机旋转角度范围
  - zca_epsilon:ZCA 白化的 epsilon 值,默认为 1e-6。
  - zca_whitening: 布尔值。是否应用 ZCA 白化。
  - rotation_range: 整数。随机旋转的度数范围。
  - width_shift_range:随机宽度偏移量
  - height_shift_range:随机高度偏移量
  - zoom_range:随机缩放,范围[1-n,1+n]


传入图片数据集,传入之后会按照设定好的对图片进行处理

1
2
 
ImageDataGenerator.fit()
  • x: 样本数据。维度4,即(batch,width,height,channel)的格式。对于灰度数据,channel的值应该为 1;对于 RGB 数据,channel值应该为 3。
  • augment: 布尔值(默认为 False)。是否使用随机样本扩张。
  • rounds: 整数(默认为 1)。如果数据数据增强(augment=True),表明在数据上进行多少次增强。
  • seed: 整数(默认 None)。随机种子。

  注意:这一步并不是必须的,当ImageDataGenerator构造函数中需要使用featurewise_center: samplewise_center: featurewise_std_normalization: samplewise_std_normalization:这几个参数时才需要使用fit方法,因为需要从fit方法中得到原始图形的统计信息,比如均值、方差等等,否则是不需要此步骤的。


采集数据和标签数组,生成批量增强数据迭代器,最好按照需要设定batch。

1
2
 
ImageDataGenerator.flow()
  • x: 输入数据。秩为 4 的 Numpy 矩阵或元组。如果是元组,第一个元素应该包含图像,第二个元素是另一个 Numpy 数组或一列 Numpy 数组,它们不经过任何修改就传递给输出。可用于将模型杂项数据与图像一起输入。对于灰度数据,图像数组的通道轴的值应该为 1,而对于 RGB 数据,其值应该为 3。
  • y: 标签。
  • batch_size: 整数 (默认为 32)。
  • shuffle: 布尔值 (默认为 True)。
  • sample_weight: 样本权重。
  • seed: 整数(默认为 None)。
  • save_to_dir: None 或 字符串(默认为 None)。这使您可以选择指定要保存的正在生成的增强图片的目录(用于可视化您正在执行的操作)。
  • save_prefix: 字符串(默认 '')。保存图片的文件名前缀(仅当 save_to_dir 设置时可用)。
  • save_format: “png”, “jpeg” 之一(仅当 save_to_dir 设置时可用)。默认:”png”。
  • subset: 数据子集 (“training” 或 “validation”),如果 在 ImageDataGenerator 中设置了 validation_split

  注意:flow()生成的迭代器是无限的,且是只能用model.fit_generator()将训练数据传入模型中训练。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import tensorflow as tf
import os
import numpy as np

# 训练集图片所在的文件夹,以训练集为例,文件的命名为“类别_顺序.jpg”
train_path = "C:\\Users\\yishui\\Desktop\\images\\train\\"

# 这个函数用于返回符合,可以使用正则路径,*表示任意字符
path_list = tf.data.Dataset.list_files(train_path + "*.jpg")


# 定义一个读取图片的函数
def read_image(path_list):
'''
:param path_list:文件的路径list
:return: 图片张量列表,图片标签列表
'''
images = [] # 图片聊表
image_labels = [] # 图片标签列表

# 根据文件路径列表依次读取
for i in path_list:
image_temp = tf.io.read_file(i) # tesnsorflow的io读取文件
image_temp = tf.image.decode_jpeg(image_temp) # 根据图片的格式进行编码转化为张量,这里图片是jpg格式
images.append(image_temp) # 图片加入到数据集
image_labels.append(os.path.basename(str(i)).split('_')[0]) # 获取文件名加入到标签,这里要张量i转化为字符串

return np.array(images), np.array(image_labels)


# 读取训练图片
train_images, train_labels = read_image(path_list=path_list)

# 增强数据,中心化并随机旋转45度
image_gen = tf.keras.preprocessing.image.ImageDataGenerator(featurewise_center=True,
rotation_range=45)
# featurewise_center等设计均值方差的参数为True才需要使用fit方法
image_gen.fit(x=train_images)

# 生成强化数据集迭代器
data_gen = image_gen.flow(train_images, train_labels)

# 模型训练
# model = tf.keras.Sequential().fit_generator(data_gen)

2.2 图片预处理

缩放图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import tensorflow as tf
import numpy as np

print("\n 当前的tensorflow版本为 ", tf.__version__, " \n")
keras = tf.keras


def load_model():
_new_model = keras.models.load_model('saved_model/my_model')

# 检查其架构
# _new_model.summary()

return _new_model


def prepare(img_path):
# 读入图片
_img = tf.io.read_file(img_path)
# 解码为tensor格式
_img = tf.image.decode_jpeg(_img)

# 将图片转换问灰度图片,即最后一维只有1
_img = tf.image.rgb_to_grayscale(_img)

# 剪裁图片, 高度和宽度
# _img = tf.image.resize_with_crop_or_pad(_img, 28, 28)

# 缩放图片
_img = tf.image.resize(_img, [28, 28])

# 转换张量数据类型
_img = tf.cast(_img, dtype=tf.uint8)

# 使用expand_dims来将维度加1。比如我自己代码中遇到的情况,
# 在对图像维度降到二维做特定操作后,
# 要还原成四维[batch, height, width, channels],前后各增加一维

# 写入./221.jpg这个文件里面
encode_jpeg = tf.image.encode_jpeg(_img, quality=100)
with tf.io.gfile.GFile('./2222.jpg', 'wb') as file:
file.write(encode_jpeg.numpy())

# -1表示最后一维,0表示在前面一维
# _img = (np.expand_dims(_img, 0))

_img = tf.expand_dims(_img, axis=0)
return _img


if __name__ == "__main__":
model = load_model()

img = prepare(img_path=r'C:\Users\yishui\Desktop\2222.jpg')

print("img.dtype =", img.dtype, " img.shape = ", img.shape)

predictions = model.predict(img)

print(" predictions = ", len(predictions))

print(" predictions[0] = ", predictions[0])

print(" predictions[0] index = ", np.argmax(predictions[0]), " predictions[0] val = ", np.max(predictions[0]))

运行结果为

1
2
3
4
5
6
7
8
当前的tensorflow版本为  2.5.0  

img.dtype = <dtype: 'uint8'> img.shape = (1, 28, 28, 1)

predictions = 1
predictions[0] = [-18.48248 -27.203917 -23.320944 -22.54355 -22.813362 -20.797226
-20.5719 -22.971878 -15.38544 -25.487017]
predictions[0] index = 8 predictions[0] val = -15.38544

三 使用Keras API对数据进行预处理

在TensorFlow2.0的高级API Keras中有个比较好用的图像处理的类ImageDataGenerator,它可以将本地图像文件自动转换为处理好的张量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from tensorflow.keras.preprocessing.image import ImageDataGenerator

train_data_dir = r"C:\\Users\\yishui\\Desktop\\images\\train\\"

val_data_dir = r"C:\\Users\\yishui\\Desktop\\images\\train\\"

img_width, img_height = 48, 48

batch_size = 16


train_datagen = ImageDataGenerator(

rescale=1. / 255,

shear_range=0.2,

horizontal_flip=True)


val_datagen = ImageDataGenerator(rescale=1. / 255)


train_generator = train_datagen.flow_from_directory(

train_data_dir,

target_size=(img_width, img_height),

batch_size=batch_size)


val_generator = val_datagen.flow_from_directory(

val_data_dir,

target_size=(img_width, img_height),

batch_size=batch_size)

其中重要的API说明

ImageDataGenerator是tensorflow.keras.preprocessing.image模块中的图片生成器,同时也可以使用它在batch中对数据进行增强,扩充数据集大小,从而增强模型的泛化能力。

ImageDataGenerator中有众多的参数,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
tf.keras.preprocessing.image.ImageDataGenerator(

featurewise_center=False,

samplewise_center=False,

featurewise_std_normalization=False,

samplewise_std_normalization=False,

zca_whitening=False,

zca_epsilon=1e-6,

rotation_range=0.,

width_shift_range=0.,

height_shift_range=0.,

brightness_range,

shear_range=0.,

zoom_range=0.,

channel_shift_range=0.,

fill_mode=‘nearest’,

cval=0.,

horizontal_flip=False,

vertical_flip=False,

rescale=None,

preprocessing_function=None,

data_format=K.image_data_format())

参数如下

  • featurewise_center:布尔值,使输入数据集去中心化(均值为0)

  • samplewise_center:布尔值,使输入数据的每个样本均值为0。

  • featurewise_std_normalization:布尔值,将输入除以数据集的标准差以完成标准化。

  • samplewise_std_normalization:布尔值,将输入的每个样本除以其自身的标准差。

  • zca_whitening:布尔值,对输入数据施加ZCA白化。

  • rotation_range:整数,数据增强时图片随机转动的角度。随机选择图片的角度,是一个0180的度数,取值为0180。

  • width_shift_range:浮点数,图片宽度的某个比例,数据增强时图片随机水平偏移的幅度。

  • height_shift_range:浮点数,图片高度的某个比例,数据增强时图片随机竖直偏移的幅度。

  • shear_range:浮点数,剪切强度(逆时针方向的剪切变换角度)。是用来进行剪切变换的程度。

  • zoom_range:浮点数或形如[lower,upper]的列表,随机缩放的幅度,若为浮点数,则相当于[lower,upper] = [1 - zoom_range,1+zoom_range]。用来进行随机的放大。

  • channel_shift_range:浮点数,随机通道偏移的幅度。

  • fill_mode:‘constant’,‘nearest’,‘reflect’或‘wrap’之一,当进行变换时超出边界的点将根据本参数给定的方法进行处理。

  • cval:浮点数或整数,当fill_mode=constant时,指定要向超出边界的点填充的值。

  • horizontal_flip:布尔值,进行随机水平翻转。随机的对图片进行水平翻转,这个参数适用于水平翻转不影响图片语义的时候。

  • vertical_flip:布尔值,进行随机竖直翻转。

  • rescale: 值将在执行其他处理前乘到整个图像上,我们的图像在RGB通道都是0255的整数,这样的操作可能使图像的值过高或过低,所以我们将这个值定为01之间的数。

  • preprocessing_function: 将被应用于每个输入的函数。该函数将在任何其他修改之前运行。该函数接受一个参数,为一张图片(秩为3的numpy array),并且输出一个具有相同shape的numpy array。


1
2
3
4
5
6
train_datagen.flow_from_directory(
train_data_dir,

target_size=(img_width, img_height),

batch_size=batch_size)

参数如下

  • directory:目标文件夹路径,对于每一个类,该文件夹都要包含一个子文件夹。

  • target_size:整数tuple,默认为(256, 256)。图像将被resize成该尺寸

  • color_mode:颜色模式,为”grayscale”和”rgb”之一,默认为”rgb”,代表这些图片是否会被转换为单通道或三通道的图片。

  • classes:可选参数,为子文件夹的列表,如[‘smile’,‘neutral’],默认为None。若未提供,则该类别列表将从directory下的子文件夹名称/结构自动推断。每一个子文件夹都会被认为是一个新的类。(类别的顺序将按照字母表顺序映射到标签值)。

  • class_mode: “categorical”, “binary”, “sparse”或None之一。默认为”categorical。该参数决定了返回的标签数组的形式,”categorical”会返回2D的one-hot编码标签,”binary”返回1D的二值标签。”sparse”返回1D的整数标签,如果为None则不返回任何标签,生成器将仅仅生成batch数据。

  • batch_size:batch数据的大小,默认32。

  • shuffle:是否打乱数据,默认为True。

  • seed:可选参数,打乱数据和进行变换时的随机数种子。

  • save_to_dir:None或字符串,该参数能让你将数据增强后的图片保存起来,用以可视化。

  • save_prefix:字符串,保存数据增强后图片时使用的前缀, 仅当设置了save_to_dir时生效。

  • save_format:“png”或”jpeg”之一,指定保存图片的数据格式,默认”jpeg”。

四 使用自定义图片训练

4.1 下载数据集

本教程使用了大约3700张鲜花照片的数据集。数据集包含5个子目录,每个类一个子目录

1
2
3
4
5
6
flower_photo/
daisy/
dandelion/
roses/
sunflowers/
tulips/

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import matplotlib.pyplot as plt
import numpy as np
import os
import PIL
import tensorflow as tf

from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.models import Sequential
import pathlib

dataset_url = "https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz"
data_dir = tf.keras.utils.get_file('flower_photos', origin=dataset_url, untar=True)
data_dir = pathlib.Path(data_dir)

image_count = len(list(data_dir.glob('*/*.jpg')))
print(image_count)

运行结果为

1
1853

查看其中一张图片

4.2 创建一个数据集

让我们使用有用的image_dataset_from_directory实用程序从磁盘加载这些图像,只需要几行代码就能将磁盘上的图像转换到 tf.data.Dataset

定义一些加载参数

1
2
3
batch_size = 32
img_height = 180
img_width = 180

在开发模型时使用验证分离是一种很好的做法。让我们使用80%的图像进行训练,20%用于验证。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
train_ds = tf.keras.preprocessing.image_dataset_from_directory(
data_dir,
validation_split=0.2,
subset="training",
seed=123,
image_size=(img_height, img_width),
batch_size=batch_size)

val_ds = tf.keras.preprocessing.image_dataset_from_directory(
data_dir,
validation_split=0.2,
subset="validation",
seed=123,
image_size=(img_height, img_width),
batch_size=batch_size)

class_names = train_ds.class_names
print(class_names)

运行结果为

1
2
3
Found 1853 files belonging to 3 classes.
Using 370 files for validation.
['roses', 'sunflowers', 'tulips']

4.3 数据可视化

打印数据

1
2
3
4
for image_batch, labels_batch in train_ds:
print("image_batch.shape ", image_batch.shape)
print(" labels_batch.shape ", labels_batch.shape)
break

运行结果为

1
2
image_batch.shape  (32, 180, 180, 3)
labels_batch.shape (32,)

4.4 为训练配置数据集

让我们确保使用缓冲预取,这样就可以从磁盘生成数据,而不会使I/O阻塞。这是加载数据时应该使用的两种重要方法。

Dataset.cache()将图像在第一个历元期间从磁盘加载后保存在内存中。这将确保数据集在训练模型时不会成为瓶颈。如果数据集太大,无法放入内存,也可以使用此方法创建性能磁盘缓存。

prefetch()与训练时的数据预处理和模型执行重叠。

1
2
3
4
AUTOTUNE = tf.data.AUTOTUNE

train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

4.5 数据标准化

RGB通道值在[0,255]范围内。这对神经网络来说不理想;一般来说,您应该尽量使输入值变小。在这里,您将使用重新缩放层来标准化要在[0,1]范围内的值。

1
2
3
4
5
6

normalization_layer = layers.experimental.preprocessing.Rescaling(1./255)

normalized_ds = train_ds.map(lambda x, y: (normalization_layer(x), y))
image_batch, labels_batch = next(iter(normalized_ds))
first_image = image_batch[0]

4.6 创建模型

该模型由三个卷积块组成,每个卷积块中有一个最大池层。有一个完全连接的层,上面有128个单元,由relu激活功能激活。此模型尚未针对高精度进行调整,本教程的目标是展示标准方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
num_classes = 5

model = Sequential([
layers.experimental.preprocessing.Rescaling(1./255, input_shape=(img_height, img_width, 3)),
layers.Conv2D(16, 3, padding='same', activation='relu'),
layers.MaxPooling2D(),
layers.Conv2D(32, 3, padding='same', activation='relu'),
layers.MaxPooling2D(),
layers.Conv2D(64, 3, padding='same', activation='relu'),
layers.MaxPooling2D(),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dense(num_classes)
])

4.7 编译模型

选择optimizers.Adam optimizer和loss.SparseCategoricalCrossentropy loss函数。要查看每个训练历元的训练和验证精度,请传递metrics参数。

1
2
3
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])

查看模型

1
model.summary()

运行结果为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Found 1853 files belonging to 3 classes.
Using 370 files for validation.
['roses', 'sunflowers', 'tulips']
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
rescaling_1 (Rescaling) (None, 180, 180, 3) 0
_________________________________________________________________
conv2d (Conv2D) (None, 180, 180, 16) 448
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 90, 90, 16) 0
_________________________________________________________________
conv2d_1 (Conv2D) (None, 90, 90, 32) 4640
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 45, 45, 32) 0
_________________________________________________________________
conv2d_2 (Conv2D) (None, 45, 45, 64) 18496
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 22, 22, 64) 0
_________________________________________________________________
flatten (Flatten) (None, 30976) 0
_________________________________________________________________
dense (Dense) (None, 128) 3965056
_________________________________________________________________
dense_1 (Dense) (None, 5) 645
=================================================================
Total params: 3,989,285
Trainable params: 3,989,285
Non-trainable params: 0
_________________________________________________________________

4.8 训练模型

1
2
3
4
5
6
epochs=10
history = model.fit(
train_ds,
validation_data=val_ds,
epochs=epochs
)

查看训练结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

loss = history.history['loss']
val_loss = history.history['val_loss']

epochs_range = range(epochs)

plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

image-20210604114630477

五 预测自定义图片

5.1 保存模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import tensorflow as tf
import numpy as np

print("\n 当前的tensorflow版本为 ", tf.__version__, " \n")

keras = tf.keras


def exec(images, labels):
"""
训练模型
:param images:
:param labels:
:return:
"""

model = keras.Sequential([
keras.layers.Flatten(input_shape=(28, 28)),
keras.layers.Dense(128, activation='relu'),
keras.layers.Dense(10)
])
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
model.fit(images, labels, epochs=10)
return model


def prepare():
"""
准备数据
:return:
"""
fashion_mnist = keras.datasets.fashion_mnist

(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()

return (train_images, train_labels), (test_images, test_labels)


if __name__ == "__main__":
# 准备数据
(train_images, train_labels), (test_images, test_labels) = prepare()

# 获取模型
model = exec(train_images, train_labels)
# 将整个模型另存为 SavedModel。
model.save('saved_model/my_model')

5.2 使用模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import tensorflow as tf
import numpy as np

print("\n 当前的tensorflow版本为 ", tf.__version__, " \n")
keras = tf.keras


def load_model():
_new_model = keras.models.load_model('saved_model/my_model')

# 检查其架构
# _new_model.summary()

return _new_model


def prepare(img_path):
# 读入图片
_img = tf.io.read_file(img_path)
# 解码为tensor格式
_img = tf.image.decode_jpeg(_img)

# 将图片转换问灰度图片,即最后一维只有1
_img = tf.image.rgb_to_grayscale(_img)

# 剪裁图片, 高度和宽度
_img = tf.image.resize_with_crop_or_pad(_img, 28, 28)

_img = (np.expand_dims(_img, 0))

# 也可以使用tf.expand_dims修改
#_img = tf.expand_dims(_img, axis=0)
return _img


if __name__ == "__main__":
model = load_model()

img = prepare(img_path=r'C:\Users\yishui\Desktop\1111.jpg')

print("img.dtype =", img.dtype, " img.shape = ", img.shape)

predictions = model.predict(img)

print(" predictions = ", len(predictions))

print(" predictions[0] = ", predictions[0])

print(" predictions[0] index = ", np.argmax(predictions[0]), " predictions[0] val = ", np.max(predictions[0]))

运行结果为

1
2
3
4
5
6
7

img.dtype = uint8 img.shape = (1, 28, 28, 1)

predictions = 1
predictions[0] = [ 0.3320993 -0.99404335 0.5549052 0.06155435 -0.18070048 -0.5338067
1.2070124 -0.6529884 -0.1033841 -1.7893128 ]
predictions[0] index = 6 predictions[0] val = 1.2070124

六 向量修改

6.1 np.expand_dims 增加维度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import numpy as np

x = np.array([1, 2])

print(" x 的属性为 shape = ", x.shape, " x.dtype = ", x.dtype, " x.ndim =", x.ndim, " x = \n", x)

print("\n ")

y = np.expand_dims(x, axis=0)

print(" y 的属性为 shape = ", y.shape, " y.dtype = ", y.dtype, " y.ndim =", y.ndim, " y =\n ", y)

print("\n ")

z = np.expand_dims(x, axis=-1)

print(" z 的属性为 shape = ", z.shape, " z.dtype = ", z.dtype, " z.ndim =", z.ndim, " z = \n", z)

print("\n ")

a = np.expand_dims(x, axis=1)

print(" a 的属性为 shape = ", a.shape, " a.dtype = ", a.dtype, " a.ndim =", a.ndim, " a =\n ", a)

# 这里会报错
# b = np.expand_dims(x, axis=2)
# print(" b = ", b)
# print(" b 的属性为 shape = ", b.shape, " b.dtype = ", b.dtype, " b.ndim =", b.ndim)

print("\n ")
c = np.expand_dims(x, axis=(0, 1))

print(" c 的属性为 shape = ", c.shape, " c.dtype = ", c.dtype, " c.ndim =", c.ndim, " c =\n ", c)

print("\n ")
d = np.expand_dims(x, axis=(0, 2))

print(" d 的属性为 shape = ", d.shape, " d.dtype = ", d.dtype, " d.ndim =", d.ndim, " d =\n ", d)

print("\n")
e = np.expand_dims(y, axis=(0, 1))

print(" e 的属性为 shape = ", e.shape, " e.dtype = ", e.dtype, " e.ndim =", e.ndim, " e = \n", e)

print("\n ")
f = np.expand_dims(y, axis=(0, 2))

print(" f 的属性为 shape = ", f.shape, " f.dtype = ", f.dtype, " f.ndim =", f.ndim, " f = \n", f)

运行结果为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

x 的属性为 shape = (2,) x.dtype = int32 x.ndim = 1 x =
[1 2]

y 的属性为 shape = (1, 2) y.dtype = int32 y.ndim = 2 y =
[[1 2]]

z 的属性为 shape = (2, 1) z.dtype = int32 z.ndim = 2 z =
[[1]
[2]]

a 的属性为 shape = (2, 1) a.dtype = int32 a.ndim = 2 a =
[[1]
[2]]

c 的属性为 shape = (1, 1, 2) c.dtype = int32 c.ndim = 3 c =
[[[1 2]]]

d 的属性为 shape = (1, 2, 1) d.dtype = int32 d.ndim = 3 d =
[[[1]
[2]]]
e 的属性为 shape = (1, 1, 1, 2) e.dtype = int32 e.ndim = 4 e =
[[[[1 2]]]]

f 的属性为 shape = (1, 1, 1, 2) f.dtype = int32 f.ndim = 4 f =
[[[[1 2]]]]

6.2 tf.expand_dims 增加维度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import tensorflow as tf

image = tf.zeros([2, 2, 2])

print(" image 的属性为 shape = ", image.shape, "type=", type(image), " image.dtype = ", image.dtype, " image.ndim =",
image.ndim, " image = \n",
image, "\n")

a = tf.expand_dims(image, axis=0)
print(" image 的属性为 shape = ", a.shape, "type=", type(a), " a.dtype = ", a.dtype, " a.ndim =", a.ndim, " a = \n",
a, "\n")

av = a.shape.as_list()
print(" image 的属性为 ", "type=", type(av),
" av = ", av, "\n")

b = tf.expand_dims(image, axis=-1)
print(" image 的属性为 shape = ", b.shape, "type=", type(b), " b.dtype = ", b.dtype, " b.ndim =", b.ndim, " b = \n",
b, "\n")

bv = b.shape.as_list()
print(" image 的属性为 ", "type=", type(bv),
" bv = ", bv, "\n")

运行结果为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

image 的属性为 shape = (2, 2, 2) type= <class 'tensorflow.python.framework.ops.EagerTensor'> image.dtype = <dtype: 'float32'> image.ndim = 3 image =
tf.Tensor(
[[[0. 0.]
[0. 0.]]
[[0. 0.]
[0. 0.]]], shape=(2, 2, 2), dtype=float32)
image 的属性为 shape = (1, 2, 2, 2) type= <class 'tensorflow.python.framework.ops.EagerTensor'> a.dtype = <dtype: 'float32'> a.ndim = 4

a =
tf.Tensor(
[[[[0. 0.]
[0. 0.]]
[[0. 0.]
[0. 0.]]]], shape=(1, 2, 2, 2), dtype=float32)
image 的属性为 type= <class 'list'> av = [1, 2, 2, 2]
image 的属性为 shape = (2, 2, 2, 1) type= <class 'tensorflow.python.framework.ops.EagerTensor'> b.dtype = <dtype: 'float32'> b.ndim = 4


b =
tf.Tensor(
[[[[0.]
[0.]]
[[0.]
[0.]]]
[[[0.]
[0.]]
[[0.]
[0.]]]], shape=(2, 2, 2, 1), dtype=float32)
image 的属性为 type= <class 'list'> bv = [2, 2, 2, 1]

6.3 numpy.squeeze 减少维度

1
2
3
4
5
6
7
numpy.squeeze(a,axis = None)

1)a表示输入的数组;
2)axis用于指定需要删除的维度,但是指定的维度必须为单维度,否则将会报错;
3)axis的取值可为None 或 int 或 tuple of ints, 可选。若axis为空,则删除所有单维度的条目;
4)返回值:数组
5) 不会修改原数组;

从数组的形状中删除单维度条目,即把shape中为1的维度去掉

6.4 图片属性修改

原始图片= = = > [H, W, 3] = = = = = > [N, C, H, W]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import matplotlib.pyplot as plt
from PIL import Image
import numpy as np

img = Image.open(r'C:\Users\yishui\Desktop\train_data\1.jpg')

print("原始图片数据为 ", img)

# 将读入的图片转化为float32类型的numpy.ndarray
img_array = np.array(img).astype('float32')

print("转换数组后图片数据为 ", img_array)

# 图片读入成ndarry时,形状是[H, W, 3],
# 将通道这一维度调整到最前面
hw3_img = np.transpose(img_array, (2, 0, 1))

print("形状是[H, W, 3]格式的图片数据为 ", hw3_img)

# 将数据形状调整为[N, C, H, W]格式
x = hw3_img.reshape(1, 3, img.height, img.width)

print("形状是[N, C, H, W]格式的图片数据为 ", hw3_img)

运行结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
原始图片数据为  <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=28x28 at 0x1743B9EFAC0>
转换数组后图片数据为 [[[255. 255. 255.]
[255. 255. 255.]
[255. 255. 255.]
...
...
[255. 255. 255.]
[255. 255. 255.]
[255. 255. 255.]]]
形状是[H, W, 3]格式的图片数据为 [[[255. 255. 255. ... 255. 255. 255.]
[255. 255. 255. ... 255. 255. 255.]
[255. 255. 255. ... 255. 255. 255.]
...
...
[255. 255. 255. ... 255. 255. 255.]
[255. 255. 255. ... 255. 255. 255.]
[255. 255. 255. ... 255. 255. 255.]]]
形状是[N, C, H, W]格式的图片数据为 [[[255. 255. 255. ... 255. 255. 255.]
[255. 255. 255. ... 255. 255. 255.]
[255. 255. 255. ... 255. 255. 255.]
...
...
[255. 255. 255. ... 255. 255. 255.]
[255. 255. 255. ... 255. 255. 255.]
[255. 255. 255. ... 255. 255. 255.]]]

数据属性

image-20210610113857473

七 重要的图片处理API

解码为tensor格式

1
2
# 解码为tensor格式
_img = tf.image.decode_jpeg(_img)

将图片转换问灰度图片,即最后一维只有1
1
2
# 将图片转换问灰度图片,即最后一维只有1
_img = tf.image.rgb_to_grayscale(_img)

剪裁图片, 高度和宽度

1
2

_img = tf.image.resize_with_crop_or_pad(_img, 28, 28)

缩放图片

1
2

_img = tf.image.resize(_img, [28, 28])