TensorFlow 2.0 教程01: 图像分类基础

 

TensorFlow 2现已上线!本教程将引导您完成使用深度学习构建简单的CIFAR-10图像分类器的过程。在本教程中,我们将:

  • 定义模型
  • 建立数据管道
  • 训练模型
  • 多个GPU加快训练速度
  • 添加callback以监视进度/更新学习进度

本教程中的代码可在此处获得

 

定义模型

TensorFlow 2使用Keras作为其高级API。Keras提供了两种定义模型的方法:顺序API和功能API。

使用Keras序列API定义模型

from tf.keras.models import Sequential
from tf.keras.layers import Conv2, MaxPooling2D, Flatten, Dense


model = Sequential([
    Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
    MaxPooling2D(pool_size=(2, 2)),
    Flatten(),
    Dense(10, activation='softmax')
])

 

使用Keras的功能性API定义相同的模型

from tf.keras.models import Model
from tf.keras.layers import Input, Conv2, MaxPooling2D, Flatten, Dense


inputs = Input(shape=(32, 32, 3))
x = Conv2D(32, (3, 3), activation='relu')(inputs)
x = MaxPooling2D(pool_size=(2, 2))(x)
x = Flatten()(x)
x = Dense(10, activation='softmax')(x)
model = Model(inputs=inputs, outputs=x)

 

顺序API与功能API

这些API之间的主要区别在于,顺序API要求为其提供第一层input_shape,而功能性API要求为其提供第一层,tf.keras.layers.Input并且需要tf.keras.models.Model在最后调用构造函数。

顺序API更简洁,而功能性API更灵活,因为它允许模型为非顺序模型。例如,在ResNet中具有跳过连接。本教程改编了TensorFlow的ResNet的官方Keras实现,该实现使用了功能性API。

input_shape = (32, 32, 3)
img_input = Input(shape=input_shape)
model = resnet_cifar_model.resnet56(img_input, classes=10)

 

 

建立数据管道

现在,我们定义了一个模型。要训​​练该模型,我们需要一个数据管道来为其提供标记的训练数据。数据管道执行以下任务:

  • 加载:将数据集(例如图像和标签)从存储设备复制到程序的内存中。
  • 预处理:转换数据集。例如,在图像分类中,我们可能会调整图像的大小,变白,混洗或批处理。
  • 馈送:将示例从数据集中铲入训练循环。

 

从存储中加载数据

首先,我们将CIFAR-10从存储加载到numpy ndarrays

(x, y), (x_test, y_test) = keras.datasets.cifar10.load_data()

注意:

  • 首次引用时keras.datasets.cifar10.load_data,CIFAR-10将从互联网上下载到~/.keras/datasets/cifar-10-batches-py.tar.gz.后续引用不涉及网络。
  • x 代表50,000张图像,尺寸为32 x 32 x 3(宽度,高度和三个RGB通道)。
  • y 代表这50,000张图片的标签。
 print(type(x), type(y))
(<type 'numpy.ndarray'>, <type 'numpy.ndarray'>)

 

print(x.shape, y.shape)
((50000, 32, 32, 3), (50000, 1))

从理论上讲,我们可以简单地将这些原始numpy.ndarray对象馈送到训练循环中,并将其称为数据管道。但是,为了获得更高的模型精度,我们需要对数据进行预处理(即在使用前对其进行某些转换)。为此,我们利用Tensorflow的Dataset类

 

tf.data.Dataset类

TensorFlow数据集类主要用于两个目的:

  • 它充当保存训练数据的容器。
  • 它可用于对训练数据的元素进行更改。

我们实例化一个tensorflow.data.Dataset表示CIFAR-10数据集的对象,如下所示:

# Load data from storage to memory.
(x, y), (x_test, y_test) = keras.datasets.cifar10.load_data()


# Instantiate the Dataset class.
train_dataset = tf.data.Dataset.from_tensor_slices((x, y))

在training期间,train_dataset将通过take()迭代器访问存储在其中的CIFAR-10训练示例:

for image, label in train_dataset.take(1):
    (image.shape, label.shape)

 

照原样,我们不执行任何数据预处理。调用take()只是发出原始的CIFAR-10图片;前20张图片如下:

资料扩充

增强通常用于“膨胀”训练数据集,这可以提高泛化性能。

让我们通过对每个图像执行以下步骤来扩充CIFAR-10数据集:

  1. 用黑色的四像素边框填充图像。
  2. 从填充的图像中随机裁剪32 x 32区域。
  3. 翻转硬币以确定是否应水平翻转图像。

为此,我们首先定义一个给定图像的函数,该函数执行上述步骤1-3:

def augmentation(x, y):
    x = tf.image.resize_with_crop_or_pad(
        x, HEIGHT + 8, WIDTH + 8)
    x = tf.image.random_crop(x, [HEIGHT, WIDTH, NUM_CHANNELS])
    x = tf.image.random_flip_left_right(x)
    return x, y

接下来,我们调用方法map;此调用返回一个新Dataset 对象,该对象包含将CIFAR-10中的每个图像传递到的结果augmentation。这个新对象将以原始顺序发出变换后的图像:

train_dataset = train_dataset.map(augmentation)

这些是增强后的前20张图像:

注意:扩充只能应用于训练集;在推理过程中应用增强将导致不确定的预测和验证分数。

Shuffling(改组)

我们随机地对数据集进行洗牌。TensorFlow数据集具有一种shuffle方法,可以将其链接到我们的扩充,如下所示:

train_dataset = (train_dataset
                 .map(augmentation)
                 .shuffle(buffer_size=50000))

为了进行完美的混洗,的值buffer_size应大于或等于数据集的大小(在这种情况下为50,000);对于大型数据集,这是不可能的。

改组后,以下是来自数据集的20张图像:

 

Normalization(正常化)

规范化数据是常见的做法。在这里,定义一个线性缩放每个图像以具有零均值和单位方差的函数:

def normalize(x, y):
   x = tf.image.per_image_standardization(x)
   return x, y

接下来,我们将其与我们的扩充和混排操作链接起来:

train_dataset = (train_dataset
                 .map(augmentation)
                 .shuffle(buffer_size=50000)
                 .map(normalize))

Batching(批处理)

最后,我们batch是数据集。我们设置drop_remainderTrue删除足够的训练示例,以使训练集的大小可被整除batch_size

train_dataset = (train_dataset.map(augmentation)
                 .map(normalize)
                 .shuffle(50000)
                 .batch(128, drop_remainder=True)

现在,我们有了完整的数据管道。现在我们可以开始训练了。

 

训练模型

训练之前需要先编译Keras模型。编译本质上定义了三件事:损失函数优化器和评估指标

model.compile(
      loss='sparse_categorical_crossentropy',
      optimizer=keras.optimizers.SGD(learning_rate=0.1, momentum=0.9),
      metrics=['accuracy'])

请注意,我们在此处使用sparse_categorical_crossentropysparse_categorical_accuracy,因为每个标签都由单个整数(类的索引)表示。每个人都应该使用categorical_crossentropycategorical_accuracy如果一个热矢量表示每个标签。

Keras使用fitAPI训练模型。可以选择在每个validation_freq训练时期在验证数据集上测试模型。

请注意,我们仅将测试数据集用于验证,因为CIFAR-10本身并不提供验证集。模型的验证应在与训练集分离的一组数据上进行。

model.fit(train_dataset,
          epochs=60,
          validation_data=test_dataset,
          validation_freq=1)

请注意,在此示例中,该fit函数采用TensorFlow Dataset对象(train_datasettest_dataset)。如前所述,它也可以将numpy ndarrays作为输入。使用数组的缺点是缺乏将变换应用于数据集的灵活性。

model.fit(x, y,
          batch_size=128, epochs=5, shuffle=True,
          validation_data=(x_test, y_test))

要评估模型,请evaluate使用测试数据集调用该方法:

model.evaluate(test_loader)

 

多GPU

到目前为止,我们已经展示了如何使用TensorFlow的Dataset API创建数据管道,以及如何使用Keras API定义模型并进行训练和评估。下一步是使代码在多个GPU上运行。

实际上,Tensorflow 2使将单GPU实现转换为可与多个GPU一起运行变得非常容易。您需要做的就是定义一个分发策略并在该策略的范围内创建模型:

mirrored_strategy = tf.distribute.MirroredStrategy()
with mirrored_strategy.scope():
    model = resnet.resnet56(classes=NUM_CLASSES)
    model.compile(
      optimizer=keras.optimizers.SGD(learning_rate=0.1, momentum=0.9),
      loss='sparse_categorical_crossentropy',
      metrics=['accuracy'])

我们MirroredStrategy在这里使用,它在一台机器上支持在多个GPU上进行同步分布式训练。默认情况下,它使用NVIDIA NCCL作为多GPU全缩减实现。

请注意,您将要batch根据使用的GPU数量,使用数据管道的方法来缩放批处理大小。

train_loader = train_loader.map(preprocess).shuffle(50000).batch(BS_PER_GPU*NUM_GPUS)
test_loader = test_loader.map(preprocess).batch(BS_PER_GPU*NUM_GPUS)

 

Adding callbacks

我们经常需要在训练期间执行自定义操作。例如,您可能希望在训练期间记录统计信息,以进行调试或优化。实施学习率表,以提高训练效率;或在过滤器汇聚时保存它们的可视快照。在TensorFlow 2中,您可以在训练期间使用callback功能来实现自定义事件。

Tensorboard

TensorBoard主要用于在训练期间记录和可视化信息。这对于检查模型的性能非常方便。通过tensorflow.keras.callbacks.TensorBoardcallback函数提供Tensorboard支持:

from tensorflow.keras.callbacks import TensorBoard
log_dir="logs/fit/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
tensorboard_callback = TensorBoard(
log_dir=log_dir, update_freq='batch', histogram_freq=1)

model.fit(...,
callbacks=[tensorboard_callback])

在上面的示例中,我们首先创建一个TensorBoardcallback,记录每个训练步骤的数据(通过update_freq=batch),然后将此callback附加到fit函数。TensorFlow将生成tfevents文件,可以使用TensorBoard可视化。例如,这是训练过程中分类准确性的可视化(蓝色是训练准确性,红色是验证准确性):

 

 

Learning Rate Schedule

通常,随着训练的进行,我们希望对学习率有很好的控制。可以将自定义学习率进度表实现为callback函数。在这里,我们创建了一个自定义schedule函数,该函数使用步进函数(第30个时期和第45个时期)来降低学习率。此计划将转换为keras.callbacks.LearningRateScheduler并附加到该fit函数。

from tensorflow.keras.callbacks import LearningRateScheduler


BASE_LEARNING_RATE = 0.1
LR_SCHEDULE = [(0.1, 30), (0.01, 45)]


def schedule(epoch):
initial_learning_rate = BASE_LEARNING_RATE * BS_PER_GPU / 128
learning_rate = initial_learning_rate
for mult, start_epoch in LR_SCHEDULE:
if epoch >= start_epoch:
learning_rate = initial_learning_rate * mult
else:
break
tf.summary.scalar('learning rate', data=learning_rate, step=epoch)
return learning_rate

lr_schedule_callback = LearningRateScheduler(schedule)


model.fit(...,
callbacks=[..., lr_schedule_callback])

这些是在60周期训练中自定义学习率的统计信息:

 

总结

本教程以图像分类为例说明TensorFlow 2.0的基础。我们介绍了:

  • 使用TensorFlow 2的数据集API的数据管道
  • 使用Keras训练,评估,保存和还原模型(TensorFlow 2的官方高级API)
  • 具有分布式策略的多GPU
  • 带有callback的定制训练

以下是本教程的完整代码。您还可以使用此Tensorflow 2.0教程存储库复制有关TensorFlow 2.0的教程

import datetime


import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.callbacks import TensorBoard, LearningRateScheduler


import resnet




NUM_GPUS = 2
BS_PER_GPU = 128
NUM_EPOCHS = 60


HEIGHT = 32
WIDTH = 32
NUM_CHANNELS = 3
NUM_CLASSES = 10
NUM_TRAIN_SAMPLES = 50000


BASE_LEARNING_RATE = 0.1
LR_SCHEDULE = [(0.1, 30), (0.01, 45)]




def preprocess(x, y):
x = tf.image.per_image_standardization(x)
return x, y




def augmentation(x, y):
x = tf.image.resize_with_crop_or_pad(
x, HEIGHT + 8, WIDTH + 8)
x = tf.image.random_crop(x, [HEIGHT, WIDTH, NUM_CHANNELS])
x = tf.image.random_flip_left_right(x)
return x, y




def schedule(epoch):
initial_learning_rate = BASE_LEARNING_RATE * BS_PER_GPU / 128
learning_rate = initial_learning_rate
for mult, start_epoch in LR_SCHEDULE:
if epoch >= start_epoch:
learning_rate = initial_learning_rate * mult
else:
break
tf.summary.scalar('learning rate', data=learning_rate, step=epoch)
return learning_rate




(x,y), (x_test, y_test) = keras.datasets.cifar10.load_data()


train_dataset = tf.data.Dataset.from_tensor_slices((x,y))
test_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test))


tf.random.set_seed(22)
train_dataset = train_dataset.map(augmentation).map(preprocess).shuffle(NUM_TRAIN_SAMPLES).batch(BS_PER_GPU * NUM_GPUS, drop_remainder=True)
test_dataset = test_dataset.map(preprocess).batch(BS_PER_GPU * NUM_GPUS, drop_remainder=True)


input_shape = (32, 32, 3)
img_input = tf.keras.layers.Input(shape=input_shape)
opt = keras.optimizers.SGD(learning_rate=0.1, momentum=0.9)


if NUM_GPUS == 1:
model = resnet.resnet56(img_input=img_input, classes=NUM_CLASSES)
model.compile(
optimizer=opt,
loss='sparse_categorical_crossentropy',
metrics=['sparse_categorical_accuracy'])
else:
mirrored_strategy = tf.distribute.MirroredStrategy()
with mirrored_strategy.scope():
model = resnet.resnet56(img_input=img_input, classes=NUM_CLASSES)
model.compile(
optimizer=opt,
loss='sparse_categorical_crossentropy',
metrics=['sparse_categorical_accuracy'])


log_dir="logs/fit/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
file_writer = tf.summary.create_file_writer(log_dir + "/metrics")
file_writer.set_as_default()
tensorboard_callback = TensorBoard(
log_dir=log_dir,
update_freq='batch',
histogram_freq=1)


lr_schedule_callback = LearningRateScheduler(schedule)


model.fit(train_dataset,
epochs=NUM_EPOCHS,
validation_data=test_dataset,
validation_freq=1,
callbacks=[tensorboard_callback, lr_schedule_callback])
model.evaluate(test_dataset)


model.save('model.h5')


new_model = keras.models.load_model('model.h5')

new_model.evaluate(test_dataset)

 

给大家介绍一下租用GPU做实验的方法,我们是在智星云租用的GPU,使用体验很好。具体大家可以参考:智星云官网: http://www.ai-galaxy.cn/,淘宝店:https://shop36573300.taobao.com/公众号: 智星AI,

       

 

 

参考文献:

https://lambdalabs.com/blog/tensorflow-2-0-tutorial-01-image-classification-basics/

https://github.com/lambdal/TensorFlow2-tutorial/tree/master/01-basic-image-classification

https://www.cs.toronto.edu/~kriz/cifar.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值