tensorflow core ---Image classification图片分类
learn -tutorial -tensorflow core —Image classification
文章目录
本教程显示如何对花朵图像进行分类。它使用
keras.Sequential
模型创建图像分类器,并使用加载数据
preprocessing.image_dataset_from_directory
您将获得以下概念的实践经验:
有效地从磁盘加载数据集。
识别过度拟合并应用techiniques 减轻过拟合,包括 data augmentation and Dropout.。
本教程遵循基本的机器学习工作流程:
检查并了解数据
建立输入管道
建立模型
训练模型
测试模型
改进模型并重复该过程
导入TensorFlow和其他库
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
下载并浏览数据集
本教程使用约3700张花朵照片的数据集。数据集包含5个子目录,每个类一个:
flower_photo/
daisy/
dandelion/
roses/
sunflowers/
tulips/
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)
# PosixPath('/root/.keras/datasets/flower_photos')
下载后,您现在应该具有可用的数据集副本。总图片有3,670张:
image_count = len(list(data_dir.glob('*/*.jpg')))
print(image_count)
下载后,您现在应该具有可用的数据集副本。总图片有3,670张:
image_count = len(list(data_dir.glob('*/*.jpg')))
print(image_count)
3670
这是一些玫瑰:
roses = list(data_dir.glob('roses/*'))
PIL.Image.open(str(roses[0]))
# open 只需要传入str类型的地址即可显示

roses是glob后得到的路径列表,posixpath是unix上所使用的斜杠路径表示格式
roses[1] :
PosixPath(’/root/.keras/datasets/flower_photos/roses/15674450867_0ced942941_n.jpg’)
PIL.Image.open(str(roses[1]))

还有一些郁金香:
tulips = list(data_dir.glob('tulips/*'))
PIL.Image.open(str(tulips[0]))

PIL.Image.open(str(tulips[1]))

使用keras.preprocessing加载
让我们使用有用的image_dataset_from_directory实用工具从磁盘上加载这些图像。tf.data.Dataset只需几行代码,您就可以将磁盘上的映像目录带到。如果愿意,您还可以通过访问加载图像教程从头开始编写自己的数据加载代码。
创建一个数据集
为加载程序定义一些参数
batch_size = 32
img_height = 180
img_width = 180
在开发模型时,最好使用验证拆分。让我们将80%的图像用于训练,将20%的图像用于验证。
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)
Found 3670 files belonging to 5 classes.
Using 2936 files for training.
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)
Found 3670 files belonging to 5 classes.
Using 734 files for validation.
您可以class_names在这些数据集的属性中找到类名称。这些以字母顺序对应于目录名称。
class_names = train_ds.class_names
print(class_names)
['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']
可视化数据
这是训练数据集中的前9张图像。
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 10))
for images, labels in train_ds.take(1):
for i in range(9):
ax = plt.subplot(3, 3, i + 1)
plt.imshow(images[i].numpy().astype("uint8"))
plt.title(class_names[labels[i]])
plt.axis("off")
由于前面读取的时候是batch_size是32
train_ds.take(1), images, labels 都是batch =32,有32个
labels 是
tf.Tensor(3, shape=(), dtype=int32)
tf.Tensor(3, shape=(), dtype=int32)
tf.Tensor(4, shape=(), dtype=int32)
tf.Tensor(4, shape=(), dtype=int32)
tf.Tensor(1, shape=(), dtype=int32)
tf.Tensor(3, shape=(), dtype=int32)
tf.Tensor(4, shape=(), dtype=int32)
tf.Tensor(3, shape=(), dtype=int32)
tf.Tensor(4, shape=(), dtype=int32)
即label[i]是class_names
[‘daisy’, ‘dandelion’, ‘roses’, ‘sunflowers’, ‘tulips’]
的标号index
通过class_names[labels[i]]获得花名

您将通过传递这些数据集来训练使用这些数据集的模型model.fit。如果愿意,还可以手动遍历数据集并检索成批图像:
for image_batch, labels_batch in train_ds:
print(image_batch.shape)
print(labels_batch.shape)
break
(32, 180, 180, 3)
(32,)
image_batch是形状(32, 180, 180, 3)的张量。这是一批32个形状180x180x3的图像(最后一个尺寸是指颜色通道RGB)。label_batch是形状(32,)的张量,这些都是32个图像的 对应标签
您可以调用.numpy()在image_batch和labels_batch张量将它们转换为一个numpy.ndarray
配置数据集以提高性能
让我们确保使用缓冲预取,以便您可以从磁盘产生数据而不会阻塞I / O。这是加载数据时应使用的两种重要方法。
Dataset.cache()在第一个时期将图像从磁盘加载后,将图像保留在内存中。这将确保在训练模型时数据集不会成为瓶颈。如果您的数据集太大而无法容纳到内存中,则也可以使用此方法来创建高性能的磁盘缓存。
Dataset.prefetch() 训练时与数据预处理和模型执行重叠。
有兴趣的读者可以在数据性能指南中了解有关这两种方法以及如何将数据缓存到磁盘的更多信息。
AUTOTUNE = tf.data.AUTOTUNE
train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)
标准化数据
RGB通道值在[0, 255]范围内。这对于神经网络而言并不理想;通常,您应该设法减小输入值。在这里,您将[0, 1]使用“重新缩放”图层将值标准化为该范围内的值。
normalization_layer = layers.experimental.preprocessing.Rescaling(1./255)
注意:
本节中介绍的Keras预处理实用程序和图层目前处于实验阶段,可能会更改。
有两种使用此层的方法。您可以通过调用map将其应用于数据集:
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]
# Notice the pixels values are now in `[0,1]`.
print(np.min(first_image), np.max(first_image))
0.0 1.0
或者,您可以在模型定义中包括该层,这可以简化部署。让我们在这里使用第二种方法。
注意:
您之前使用的image_size参数调整了图片的大小image_dataset_from_directory。如果您还希望在模型中包括调整大小逻辑,则可以使用“调整大小”层。Resizing layer.
创建模型
该模型由三个卷积块组成,每个卷积块中都有一个最大池层。有一个完全连接的层,上面有128个单元,可以通过relu激活功能激活该层。该模型尚未针对高精度进行调整,本教程的目的是展示一种标准方法。
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)
])
编译模型
对于本教程,选择optimizers.Adam优化器和losses.SparseCategoricalCrossentropy损失函数。要查看每个训练时期的训练和验证准确性,请传递metrics参数。
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
model summary
使用模型的summary方法查看网络的所有层:
model.summary()
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
_________________________________________________________________
训练模型
epochs=10
history = model.fit(
train_ds,
validation_data=val_ds,
epochs=epochs
)
可视化training结果
在训练和验证集上创建损失和准确性图。
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()

从图中可以看出,训练精度和验证精度相差很大,模型仅在验证集上获得了约60%的精度。
让我们看一下出了什么问题,并尝试提高模型的整体性能。
过度拟合
在上面的图中,训练精度随时间呈线性增长,而验证精度在训练过程中停滞在60%左右。同样,训练和验证准确性之间的准确性差异也很明显,这是过度拟合的标志。
当训练示例数量很少时,该模型有时会从训练示例中的噪音或不必要的细节中学习,从而对模型在新示例上的性能产生负面影响。这种现象称为过拟合。这意味着该模型很难推广到新的数据集。
在训练过程中,有多种方法可以解决过拟合的问题。在本教程中,您将使用数据扩充Data augmentation并将Dropout添加到模型中。
Data augmentation
训练量很少时,通常会发生过度拟合。数据增强采用通过使用随机变换对现有示例进行增强以生成看起来可信的图像来生成其他训练数据的方法。这有助于将模型暴露于数据的更多方面,并且可以更好地进行概括。
您将使用中的图层来实现数据扩充tf.keras.layers.experimental.preprocessing。这些可以像其他图层一样包含在模型中,并在GPU上运行。
data_augmentation = keras.Sequential(
[
layers.experimental.preprocessing.RandomFlip("horizontal",
input_shape=(img_height,
img_width,
3)),
layers.experimental.preprocessing.RandomRotation(0.1),
layers.experimental.preprocessing.RandomZoom(0.1),
]
)
让我们通过将数据增强多次应用于同一幅图像来形象化一些增强示例的外观:
plt.figure(figsize=(10, 10))
for images, _ in train_ds.take(1):
for i in range(9):
augmented_images = data_augmentation(images)
ax = plt.subplot(3, 3, i + 1)
plt.imshow(augmented_images[0].numpy().astype("uint8"))
plt.axis("off")

您将立即使用数据增强来训练模型。
Dropout
减少过度拟合的另一种技术是将Dropout引入网络,这是一种正则化形式。
在将Dropout应用于图层时,它会在训练过程中从该图层中随机退出(通过将激活设置为零)许多输出单元。dropout 采用分数形式作为其输入值,形式为0.1、0.2、0.4等。这意味着从所施加的层中随机退出输出单元的10%,20%或40%。
让我们使用创建一个新的神经网络layers.Dropout,然后使用增强图像对其进行训练
model = Sequential([
data_augmentation,
layers.experimental.preprocessing.Rescaling(1./255),
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.Dropout(0.2),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dense(num_classes)
])
编译和训练模型
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
model.summary()
epochs = 15
history = model.fit(
train_ds,
validation_data=val_ds,
epochs=epochs
)
可视化training结果
在应用数据增强和Dropout之后,与以前相比,过度拟合的情况减少了,并且训练和验证的准确性更加紧密。
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()

预测新数据
最后,让我们使用我们的模型对训练或验证集中未包含的图像进行分类。
注意:
在推理时,数据增强和数据丢失层是不活动的。
sunflower_url = "https://storage.googleapis.com/download.tensorflow.org/example_images/592px-Red_sunflower.jpg"
sunflower_path = tf.keras.utils.get_file('Red_sunflower', origin=sunflower_url)
img = keras.preprocessing.image.load_img(
sunflower_path, target_size=(img_height, img_width)
)
img_array = keras.preprocessing.image.img_to_array(img)
img_array = tf.expand_dims(img_array, 0) # Create a batch
predictions = model.predict(img_array)
score = tf.nn.softmax(predictions[0])
print(
"This image most likely belongs to {} with a {:.2f} percent confidence."
.format(class_names[np.argmax(score)], 100 * np.max(score))
)
这里 tf.nn.softmax(predictions[0]),因为网络最后一个dense层并没有给它激活函数softmax,只是在训练的时候指定了loss =tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
预测可用不到
所以最后必须使用
总结
显示某一路径的图片
PIL.Image.open()
文件夹类名的列表
train_ds.class_names
通过数据的url获得数据
tf.keras.utils.get_file('名字',origin=sunflower_url)
keras.preprocessing.image.img_to_array(img)
keras.preprocessing里面的几个函数
image.load_img
image.img_to_array
tf.expand_dims
model.predict()
最后显示预测的得分
.format(class_names[np.argmax(score)], 100 * np.max(score))