前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >迁移学习之快速搭建【卷积神经网络】

迁移学习之快速搭建【卷积神经网络】

原创
作者头像
一颗小树x
修改2021-05-14 10:26:20
1.9K0
修改2021-05-14 10:26:20
举报
文章被收录于专栏:用户5033944的专栏

前言

卷积神经网络 概念认识:https://cloud.tencent.com/developer/article/1822928

卷积神经网络 简单模型搭建:https://cloud.tencent.com/developer/article/1822778

本篇文章带大家熟悉“迁移学习”的开发流程,介绍如何使用预先训练好的神经网络,结合实际的功能需求,来实现一些图像任务;比如:实现对猫和狗的图像进行分类。

预先训练好的神经网络,通常称为“预训练模型”,它在大型数据集上进行训练,取得业界认可的效果,开源给广大开发者使用的模型。本文主要介绍在keras中的关于图像任务的开源模型。

比如:在ImageNet数据集上预训练过的用于图像分类的模型 VGG16、VGG19、ResNetV2、InceptionV3、MobileNetV2、DenseNet、NASNet等等模型。

top-1 accuracy和 top-5 accuracy 是指模型在 ImageNet 验证数据集上的性能;

Depth 是指网络的拓扑深度;这包括激活层、批次规范化层等。

这是官网的链接keras预训练模型,大家可以去探索一下

迁移学习方式

我们可以直接使用预训练模型,毕竟效果挺好的;提供输入信息,经过模型处理,直接输出结果。

也可以使用预训练模型的一部分网络结构,使用其特定的功能(比如:特征提取),然后根据给定任务自定义搭建一部分网络结构(比如:实现分类),最后组合起来就形成一个完整的神经网络啦。本文主要将这种方式。

预训练模型的优点

1)模型在足够大的数据集中训练,通常是业界的通用模型(比如:图像视觉的模型);

2)预训练模型的权重是已知了,往往不用再花时间去训练;只需训练我们自定义的网络结构即可。

思路流程

  1. 导入数据集
  2. 探索集数据,并进行数据预处理
  3. 构建模型(搭建神经网络结构、编译模型)预训练模型 + 自定义模型
  4. 训练模型(把数据输入模型、评估准确性、作出预测、验证预测)
  5. 使用训练好的模型

一、导入数据集

使用谷歌开源的数据集,包含几千张猫和狗图像;然后把数据集分为训练集、验证集、测试集。

代码语言:javascript
复制
# 该数据集包含几千张猫和狗图像;
# 下载并提取包含图像的 zip 文件,然后创建一个tf.data.Data.Dataset,
_URL = 'https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip'
path_to_zip = tf.keras.utils.get_file('cats_and_dogs.zip', origin=_URL, extract=True)
PATH = os.path.join(os.path.dirname(path_to_zip), 'cats_and_dogs_filtered')

train_dir = os.path.join(PATH, 'train')
validation_dir = os.path.join(PATH, 'validation')

BATCH_SIZE = 32
IMG_SIZE = (160, 160)

# 用于使用tf.keras.preprocessing.image_dataset_from_directory 进行拆分训练集和验证集。
train_dataset = image_dataset_from_directory(train_dir,
                                             shuffle=True,
                                             batch_size=BATCH_SIZE,
                                             image_size=IMG_SIZE)

validation_dataset = image_dataset_from_directory(validation_dir,
                                                  shuffle=True,
                                                  batch_size=BATCH_SIZE,
                                                  image_size=IMG_SIZE)

由于原始数据集不包含测试集,因此将创建一个测试集。在验证集中将其中 20% 移动到测试集。

代码语言:javascript
复制
val_batches = tf.data.experimental.cardinality(validation_dataset)
test_dataset = validation_dataset.take(val_batches // 5)
validation_dataset = validation_dataset.skip(val_batches // 5)
print('Number of validation batches: %d' % tf.data.experimental.cardinality(validation_dataset))
print('Number of test batches: %d' % tf.data.experimental.cardinality(test_dataset))

二、探索集数据,并进行数据预处理

将训练集的前 9 张图片和标签打印出来

代码语言:javascript
复制
class_names = train_dataset.class_names

plt.figure(figsize=(10, 10))
for images, labels in train_dataset.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")

数据集预处理——数据增强

由于该数据集只有几千张猫和狗图像,属于小数据集,在模型训练时容易产生过拟合的;于是使用数据增强,对训练图像进行随机旋转和水平翻转,使得训练样本多样性。

代码语言:javascript
复制
data_augmentation = tf.keras.Sequential([
  tf.keras.layers.experimental.preprocessing.RandomFlip('horizontal'),
  tf.keras.layers.experimental.preprocessing.RandomRotation(0.2),
])

RandomFlip("horizontal") 对图片进行 随机水平反转

RandomRotation(0.2) 对图片进行 随机旋转

查看一下效果:

代码语言:javascript
复制
for image, _ in train_dataset.take(1):
  plt.figure(figsize=(10, 10))
  first_image = image[0]
  for i in range(9):
    ax = plt.subplot(3, 3, i + 1)
    augmented_image = data_augmentation(tf.expand_dims(first_image, 0))
    plt.imshow(augmented_image[0] / 255)
    plt.axis('off')

数据集预处理——缩放像素值

将图片像素值从[0,255]重新缩放到[-1,1]

代码语言:javascript
复制
preprocess_input = tf.keras.applications.mobilenet_v2.preprocess_input
rescale = tf.keras.layers.experimental.preprocessing.Rescaling(1./127.5, offset= -1)

为什么是除以127.5,再整体偏移-1呢?由于图片的像素范围是0~255,我们把它变成-1~1的范围,于是每张图像(训练集、测试集)都除以127.5,这时得到的范围是0~2,再整体偏移-1,就得到-1~1的范围啦。

三、构建模型

常见卷积神经网络(CNN),主要由几个 卷积层Conv2D 和 池化层MaxPooling2D 层组成。卷积层池化层的叠加实现对输入数据的特征提取,最后连接全连接层实现分类。

  • 特征提取——卷积层池化层
  • 实现分类——全连接层

这里用到“迁移学习”的思想,使用“预训练模型”作为特征提取;实现分类的全连接层有我们自己搭建。

3.1)特征提取——MobileNet V2

特征提取使用谷歌开发的MobileNet V2 模型,它是再ImageNet 数据集上预先训练的,该数据集由 140 万张图像和 1000 个类组成。

我们只使用MobileNet V2 模型的卷积层和池化层,生成base_model;不用它的全连接层,毕竟我们的输出只是识别猫和狗,不用识别1000多个类嘛。

代码语言:javascript
复制
# 使用MobileNet V2 的特征提取的网络结构、权重
IMG_SHAPE = IMG_SIZE + (3,)
base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE,
                                               include_top=False,
                                               weights='imagenet')

include_top=False,是指不用MobileNet V2模型的全连接层。

对图像进行特征提取,生成feature_batch

代码语言:javascript
复制
image_batch, label_batch = next(iter(train_dataset))
feature_batch = base_model(image_batch)
print(feature_batch.shape)

冻结base_model,对预先训练好的模型,不再重新进行训练了

代码语言:javascript
复制
base_model.trainable = False

查看一下base_model的网络结构 base_model.summary( )

base_model的模型结构实在太大了,这里显示了一小部分。

3.2)实现分类

使用GlobalAveragePooling2D将每张图像转换为单个 1280 元素矢量

代码语言:javascript
复制
global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
feature_batch_average = global_average_layer(feature_batch)
print(feature_batch_average.shape)

由于进行猫和狗分类,对每个图像进行单个预测,正数预测类 1,负数预测类 0;于是全连接层用1个神经元就可以了。

代码语言:javascript
复制
prediction_layer = tf.keras.layers.Dense(1)
prediction_batch = prediction_layer(feature_batch_average)
print(prediction_batch.shape)

3.3)搭建整体网络结构

通过使用Keras 功能 API将数据增强、重新缩放、base_model、feature_batch层、分类层prediction_batch 串联在一起来,构建模型。

代码语言:javascript
复制
inputs = tf.keras.Input(shape=(160, 160, 3))
x = data_augmentation(inputs)
x = preprocess_input(x)
x = base_model(x, training=False)
x = global_average_layer(x)
x = tf.keras.layers.Dropout(0.2)(x)
outputs = prediction_layer(x)
model = tf.keras.Model(inputs, outputs)

3.4)编译模型

由于有两个类(猫和狗),使用二进制交叉熵损失;

代码语言:javascript
复制
base_learning_rate = 0.0001
model.compile(optimizer=tf.keras.optimizers.Adam(lr=base_learning_rate),
              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=['accuracy'])

查看一下整体的网络结构 tf.keras.utils.plot_model(model)

或者用这种方式看看 model.summary( )

四、训练模型

这里我们输入准备好的训练集数据(包括图像、对应的标签),测试集的数据(包括图像、对应的标签),模型一共训练10次。

代码语言:javascript
复制
history = model.fit(train_dataset,
                    epochs=initial_epochs,
                    validation_data=validation_dataset)

下图是训练过程的截图:

通常loss越小越好,对了解释下什么是loss;简单来说是 模型预测值 和 真实值 的相差的值,反映模型预测的结果和真实值的相差程度;

通常准确度accuracy 越高,模型效果越好。从上图可以看到验证集的准确性高达95%。

评估模型

使用预训练模型MobileNet V2 作为图像特征提取器时,结合我们自定义分类层,看看训练集和验证集的准确性/损失的学习曲线。

代码语言:javascript
复制
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

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

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

plt.subplot(2, 1, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.ylabel('Cross Entropy')
plt.ylim([0,1.0])
plt.title('Training and Validation Loss')
plt.xlabel('epoch')
plt.show()

五、使用模型

通常使用 model.predict( ) 函数进行预测。

完整代码

代码语言:javascript
复制
'''
Tensorflow2.x  Python3 
'''
# 导入库
import matplotlib.pyplot as plt
import numpy as np
import os
import tensorflow as tf

from tensorflow.keras.preprocessing import image_dataset_from_directory

# 该数据集包含几千张猫和狗图像;
# 下载并提取包含图像的 zip 文件,然后创建一个tf.data.Data.Dataset,
_URL = 'https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip'
path_to_zip = tf.keras.utils.get_file('cats_and_dogs.zip', origin=_URL, extract=True)
PATH = os.path.join(os.path.dirname(path_to_zip), 'cats_and_dogs_filtered')

train_dir = os.path.join(PATH, 'train')
validation_dir = os.path.join(PATH, 'validation')

BATCH_SIZE = 32
IMG_SIZE = (160, 160)

# 用于使用tf.keras.preprocessing.image_dataset_from_directory 进行拆分训练集和验证集。
train_dataset = image_dataset_from_directory(train_dir,
                                             shuffle=True,
                                             batch_size=BATCH_SIZE,
                                             image_size=IMG_SIZE)

validation_dataset = image_dataset_from_directory(validation_dir,
                                                  shuffle=True,
                                                  batch_size=BATCH_SIZE,
                                                  image_size=IMG_SIZE)

# 由于原始数据集不包含测试集,因此将创建一个测试集。在验证集中将其中 20% 移动到测试集。
val_batches = tf.data.experimental.cardinality(validation_dataset)
test_dataset = validation_dataset.take(val_batches // 5)
validation_dataset = validation_dataset.skip(val_batches // 5)
print('Number of validation batches: %d' % tf.data.experimental.cardinality(validation_dataset))
print('Number of test batches: %d' % tf.data.experimental.cardinality(test_dataset))

# 将训练集的前 9 张图片和标签打印出来
class_names = train_dataset.class_names
plt.figure(figsize=(10, 10))
for images, labels in train_dataset.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")


# 数据集预处理——数据增强,对训练图像进行随机旋转和水平翻转,使得训练样本多样性。
data_augmentation = tf.keras.Sequential([
  tf.keras.layers.experimental.preprocessing.RandomFlip('horizontal'),
  tf.keras.layers.experimental.preprocessing.RandomRotation(0.2),
])

# 数据集预处理——缩放像素值,将图片像素值从[0,255]重新缩放到[-1,1].
preprocess_input = tf.keras.applications.mobilenet_v2.preprocess_input
rescale = tf.keras.layers.experimental.preprocessing.Rescaling(1./127.5, offset= -1)

# 使用MobileNet V2 模型的卷积层和池化层,生成base_model;不用它的全连接层
IMG_SHAPE = IMG_SIZE + (3,)
base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE,
                                               include_top=False,
                                               weights='imagenet')

# 对图像进行特征提取,生成feature_batch
image_batch, label_batch = next(iter(train_dataset))
feature_batch = base_model(image_batch)
print(feature_batch.shape)

# 冻结base_model,对预先训练好的模型,不再重新进行训练了
base_model.trainable = False

# 查看一下base_model的网络结构
base_model.summary()

# 实现分类
# 使用GlobalAveragePooling2D将每张图像转换为单个 1280 元素矢量
global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
feature_batch_average = global_average_layer(feature_batch)
print(feature_batch_average.shape)
# 对每个图像进行单个预测,正数预测类 1,负数预测类 0;
prediction_layer = tf.keras.layers.Dense(1)
prediction_batch = prediction_layer(feature_batch_average)
print(prediction_batch.shape)

# 搭建整体网络结构
inputs = tf.keras.Input(shape=(160, 160, 3))
x = data_augmentation(inputs)
x = preprocess_input(x)
x = base_model(x, training=False)
x = global_average_layer(x)
x = tf.keras.layers.Dropout(0.2)(x)
outputs = prediction_layer(x)
model = tf.keras.Model(inputs, outputs)

# 编译模型
base_learning_rate = 0.0001
model.compile(optimizer=tf.keras.optimizers.Adam(lr=base_learning_rate),
              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=['accuracy'])

# 查看一下整体的网络结构
tf.keras.utils.plot_model(model)
model.summary()

# 训练模型
history = model.fit(train_dataset,
                    epochs=initial_epochs,
                    validation_data=validation_dataset)

# 评估模型
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

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

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

plt.subplot(2, 1, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.ylabel('Cross Entropy')
plt.ylim([0,1.0])
plt.title('Training and Validation Loss')
plt.xlabel('epoch')
plt.show()

参考

Tensorflow官网案例

卷积神经网络 概念认识:https://cloud.tencent.com/developer/article/1822928

卷积神经网络 简单模型搭建:https://cloud.tencent.com/developer/article/1822778

卷积神经网络 训练模型遇到过拟合问题,如何解决:https://cloud.tencent.com/developer/article/1822920

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 迁移学习方式
  • 预训练模型的优点
  • 思路流程
  • 一、导入数据集
  • 二、探索集数据,并进行数据预处理
  • 三、构建模型
    • 3.1)特征提取——MobileNet V2
      • 3.2)实现分类
        • 3.3)搭建整体网络结构
          • 3.4)编译模型
          • 四、训练模型
          • 五、使用模型
          • 完整代码
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档