首页 > 技术文章 > TensorFlow——预训练网络

digger72 2020-11-18 14:56 原文

预训练网络(迁移学习)

预训练网络是一个保存好之前已在大型数据集(大规模图像分类任务)上训练好的卷积神经网络。
原始数据集足够大且足够通用,预训练网络学到的特征空间层次结构可以作为有效的提取视觉世界特征的模型。

注: 即使新问题和新任务与原始任务完全不同,学习到的特征在不同问题之间是可以移植的,这也是深度学习与浅层学习方法的一个重要优势。它使得深度学习对于小数据问题非常的有效。

ImgaeNet

ImageNet是一个手动标注好类别的图片数据库(为了机器视觉研究),目前已有2200个类别。
深度学习中ImageNet视觉识别比赛,称为ILSVRC

-------这个图片分类比赛是训练一个模型,能够将输入图片正确分类到1000个类别中的某个类别。训练集120万,验证集5万,测试集10万。

使用预训练网络

在这里插入图片描述

微调

冻结模型库的底部的卷积层,共同训练新添加的分类器层和顶部部分卷积层。
底部一般是通用特征,顶部是高阶特征,与特定任务更加相关。
分类器训练好之后,才能微调卷积基顶部卷积层。 否则刚开始训练误差很大,微调之前这些卷积层学到的表示会被破坏掉。

微调步骤

  1. 在预训练卷积基上添加自定义层
  2. 冻结卷积基所有层
  3. 训练添加的分类层
  4. 解冻卷积基的一部分层
  5. 联合训练解冻的卷积层和添加的自定义层

实例

Jupyter Notebook

import tensorflow as tf
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
import glob
import os

# 检测gpu是否可用
tf.test.is_gpu_available()

keras = tf.keras
layers = tf.keras.layers
train_image_path = glob.glob('./dataset/dc_2000/train/*/*.jpg')
train_image_label = [int(p.split('\\')[1] == 'cat') for p in train_image_path]
def load_preprosess_image(path,label):
    image = tf.io.read_file(path)
    image = tf.image.decode_jpeg(image,channels=3)
    image = tf.image.resize(image,[256,256])
    image = tf.cast(image,tf.float32)
    image = image/255
    return image,label
train_image_ds = tf.data.Dataset.from_tensor_slices((train_image_path,train_image_label))
AUTOTUNE = tf.data.experimental.AUTOTUNE
train_image_ds = train_image_ds.map(load_preprosess_image,num_parallel_calls=AUTOTUNE)
train_image_ds = train_image_ds.shuffle(train_count).repeat().batch(BATCH_SIZE)
BATCH_SIZE = 32
train_count = len(train_image_path)

test_image_path = glob.glob('./dataset/dc_2000/test/*/*.jpg')
test_image_label = [int(p.split('\\')[1] == 'cat') for p in test_image_path]
test_image_ds = tf.data.Dataset.from_tensor_slices((test_image_path,test_image_label))
test_image_ds = test_image_ds.map(load_preprosess_image,num_parallel_calls=AUTOTUNE)
test_image_ds = test_image_ds.repeat().batch(BATCH_SIZE)
test_count = len(test_image_path)

# keras内置经典网络实现
covn_base = keras.applications.VGG16(weights='imagenet',include_top=False)
# weights='imagenet'使用在ImageNet上预训练好的网络,若等于null表示直接使用VGG网络
# include_top是否包含最后的输出层(预训练好的分类器)

model = keras.Sequential()
model.add(covn_base)
model.add(layers.GlobalAveragePooling2D())
model.add(layers.Dense(512,activation='relu'))
model.add(layers.Dense(1,activation='sigmoid'))

# 保持不动conv_base中的权重,将其设置为不可训练
covn_base.trainable = False

model.compile(optimizer=keras.optimizers.Adam(lr=0.0005),
             loss='binary_crossentropy',
             metrics=['acc'])

history = model.fit(
    train_image_ds,
    epochs=4,
    validation_data=test_image_ds,
    steps_per_epoch=train_count//BATCH_SIZE,
    validation_steps=test_count//BATCH_SIZE)

训练结果如下:
在这里插入图片描述
为了提高准确率,对模型进行微调,微调后如下:

# 解冻卷积层
covn_base.trainable = True
# 调整最后三层
fine_tune_at = -3
# 除了最后三层将其他所有层设置为不可训练
for layer in covn_base.layers[:fine_tune_at]:
    layer.trainable = False
# 将学习速率调小,下探极值
model.compile(loss='binary_crossentropy',
             optimizer=tf.keras.optimizers.Adam(lr=0.0005/10),
             metrics=['accuracy'])
# 初始训练epoch
initial_epochs = 4
# 微调后训练epoch
fine_tune_epochs = 10
total_epochs = initial_epochs+fine_tune_epochs

history = model.fit(
    train_image_ds,
    epochs=total_epochs,
    initial_epoch=initial_epochs,
    validation_data=test_image_ds,
    steps_per_epoch=train_count//BATCH_SIZE,
    validation_steps=test_count//BATCH_SIZE
)

微调后结果如下:
在这里插入图片描述

推荐阅读