TensorFlow 2.0教程02:迁移学习

本教程向您展示如何使用TensorFlow 2.0进行迁移学习。我们将介绍:

  • 处理自定义数据集
  • 使用Keras的应用程序API恢复backbone
  • 从磁盘还原backbone

重现教程

本教程中的所有代码都可以在此repository找到。

python download_data.py \
--data_url=https://s3-us-west-2.amazonaws.com/lambdalabs-files/StanfordDogs120.tar.gz \
--data_dir=~/demo/data
python transfer_dogs.py

 

定制数据

在本教程中,我们将对Stanford Dogs数据集中的图像进行分类。我们使用CSV文件重新整理了原始数据。第一列是图像的路径,第二列是类ID。csv文件位于中~/demo/data/StanfordDogs120/train.csv。(如果您修改--data_dir参数,这将改变。)

我们首先将csv文件加载到图像路径列表和标签列表中:

def load_csv(file):
  dirname = os.path.dirname(file)
  images_path = []
  labels = []
  with open(file) as f:
    parsed = csv.reader(f, delimiter=",", quotechar="'")
    for row in parsed:
      images_path.append(os.path.join(dirname, row[0]))
      labels.append(int(row[1]))
  return images_path, labels
​
TRAIN_FILE = path_home + "/demo/data/StanfordDogs120/train.csv"
train_images_path, train_labels = load_csv(TRAIN_FILE)

 

接下来,我们从这些列表中创建一个TensorFlow数据集:

train_dataset = tf.data.Dataset.from_tensor_slices((train_images_path, train_labels))

这是预处理pipeline:

  • 从其路径读取图像。
  • 由于图像的大小不是标准大小,因此我们调整了它们的大小,以便可以对它们进行批处理。在调整大小时,请务必保留每个图像的纵横比。否则,物体(在这种情况下为狗)将变形。在我们的实验中,失真导致测试精度降低了10%以上。
  • 我们通过随机调整每个图像的大小来增加数据的宽度,该宽度均匀地选自一个分布之间,[256, 512]然后从其中随机裁剪出224x224子图像。在测试过程中,我们调整图像的大小,使其宽度为256,然后中心裁剪224x224的子图像。
  • 在训练期间,我们执行随机水平翻转。
  • 我们从所有图像中减去ImageNet的平均RGB值。
HEIGHT = 224
WIDTH = 224
RESIZE_SIDE_MIN = 256
RESIZE_SIDE_MAX = 512
R_MEAN = 123.68
G_MEAN = 116.78
B_MEAN = 103.94
​
def preprocess_for_train(x, y):
    x = tf.compat.v1.read_file(x)
    x = tf.image.decode_jpeg(x, dct_method="INTEGER_ACCURATE")
    resize_side = tf.random.uniform(
        [], minval=RESIZE_SIDE_MIN, maxval=RESIZE_SIDE_MAX + 1, dtype=tf.int32)
    x = _aspect_preserving_resize(x, resize_side)
    x = _random_crop([image], HEIGHT, WIDTH)[0]
    x.set_shape([HEIGHT, WIDTH, 3])
    x = tf.cast(x, tf.float32)
    x = tf.image.random_flip_left_right(image)
    x = _mean_image_subtraction(x, [R_MEAN, G_MEAN, B_MEAN])
  return x, y
​
def preprocess_for_eval(x, y):
    x = tf.compat.v1.read_file(x)
    x = tf.image.decode_jpeg(x, dct_method="INTEGER_ACCURATE")
    x = _aspect_preserving_resize(x, RESIZE_SIDE_MIN)
    x = _central_crop([x], HEIGHT, WIDTH)[0]
    x.set_shape([HEIGHT, WIDTH, 3])
    x = tf.cast(x, tf.float32)
    x = _mean_image_subtraction(image, [R_MEAN, G_MEAN, B_MEAN])
  return x, y

自定义的调整大小功能在此脚本中实现。注意该shuffle功能首先被应用。这意味着将改组应用于图像的路径,这比应用于图像本身的速度要快得多。

NUM_TRAIN_SAMPLES = len(train_images_path)
train_dataset.shuffle(NUM_TRAIN_SAMPLES).map(preprocess_for_train).map(augmentation).batch(BS_PER_GPU, drop_remainder=True)
test_dataset = test_dataset.map(preprocess_eval).batch(BS_PER_GPU, drop_remainder=True)

现在,我们可以从该数据集中采样:

for image, label in train_dataset.take(1):
  print(image.shape, label.shape) 
​
(batch_size, 224, 224, 3) (batch_size,)

这些是训练数据集生成的图像样本:

 

 

恢复骨干网(Keras应用程序)

 

Keras将许多深层倾斜模型与预训练的砝码一起包装到applications模块中。这些模型可用于转移学习。要创建权重已恢复的模型

backbone = tf.keras.applications.ResNet50(weights = "imagenet", include_top=False)
backbone.trainable = False​

设置weights = "imagenet"为恢复使用ImageNet训练的权重。设置include_top=False为在还原过程中跳过顶层。在训练过程中,切记设置trainableFalse冻结重量。当新数据集远小于用于训练骨干模型的原始数据集时,冻结骨干模型权重非常有用。通过冻结预训练的权重,模型不太可能过度拟合。

接下来,我们向主干添加附加几层。第一个是GlobalAveragePooling2D一层,它将主干的输出作为输入。该层计算特征图的每通道均值,该操作在空间上是不变的。然后,应用辍学层以提高泛化性能。最终,具有softmax的完全连接的层将输出分类概率分布。

x = tf.keras.layers.GlobalAveragePooling2D(name='avg_pool')(x)
x = tf.keras.layers.Dropout(0.5)(x)
x = tf.keras.layers.Dense(NUM_CLASSES, activation='softmax',
                          name='prediction')(x)      
model = tf.keras.models.Model(backbone.input, x, name='model')

为了训练这个模型,我们只是compile()fit()它使用我们先前创建的数据集。

NUM_EPOCHS = 10
opt = tf.keras.optimizers.SGD()
model.compile(optimizer=opt,
        loss='sparse_categorical_crossentropy',
        metrics=['accuracy'])
model.fit(train_dataset,
          epochs=NUM_EPOCHS,
          validation_data=test_dataset,
          validation_freq=1,
          callbacks=[tensorboard_callback, lr_schedule_callback])

学习速率表生成一个阶跃函数,该函数在第6个和第9个周期将初始学习速率(0.1)衰减10倍。经过十次培训,该网络达到了75%的测试准确性。

 

还原Backbone (from disk)

如果Keras应用程序模块中未包含骨干模型,则还可以通过.h5文件(遵循HDF5规范)从磁盘还原它。

为了证明这一点,我们使用Keras应用程序模块还原ResNet50,将其作为.h5文件保存在磁盘上,并将其作为主干还原。

model = tf.keras.applications.ResNet50(weights = "imagenet", include_top=True)
model.save('ResNet50.h5')
backbone = tf.keras.models.load_model('ResNet50.h5')
backbone.trainable = False

 

要将新层附加到主干,需要指定输入层。在这种情况下,使用的是倒数第三层:

x = backbone.layers[-3].output
x = tf.keras.layers.GlobalAveragePooling2D(name='avg_pool')(x)
...     
model = tf.keras.models.Model(backbone.input, x, name='model')
​

可以采用与先前的模型相同的方式训练该模型,该模型的主干已作为Keras应用程序进行了恢复。

总结

在本教程中,我们解释了如何在TensorFlow 2中执行转移学习。关键是从预先训练的模型中恢复主干并添加您自己的自定义层。为此,我们演示了两条路径:将backbone还原为Keras应用程序,并从.h5文件中还原主干。后者更为通用,因为它可用于处理Keras应用程序中未包含的定制模型。

我们还展示了如何在主干上添加新层并实现自定义数据pipeline。

本教程中的所有代码都可以在此repo找到。

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

       

 

参考文献:

https://lambdalabs.com/blog/tensorflow-2-0-tutorial-02-transfer-learning/

https://github.com/lambdal/TensorFlow2-tutorial/tree/master/02-transfer-learning

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值