第34课:TensorFlow|TF迁移学习实战【调用官方预训练模型微调训练】

文章目录

    • [1. 课前导读](#1. 课前导读)
      • [1.1 本节课学习目标](#1.1 本节课学习目标)
      • [1.2 知识重难点](#1.2 知识重难点)
      • [1.3 学习前置条件](#1.3 学习前置条件)
      • [1.4 学完可掌握能力](#1.4 学完可掌握能力)
      • [1.5 行业应用场景](#1.5 行业应用场景)
    • [2. 核心理论精讲](#2. 核心理论精讲)
      • [2.1 `tf.keras.applications` 预训练模型库](#2.1 tf.keras.applications 预训练模型库)
      • [2.2 微调的最佳实践](#2.2 微调的最佳实践)
      • [2.3 防止过拟合的技巧](#2.3 防止过拟合的技巧)
      • [2.4 TensorFlow Hub简介](#2.4 TensorFlow Hub简介)
    • [3. 环境搭建与工具配置](#3. 环境搭建与工具配置)
    • [4. 代码实战教学](#4. 代码实战教学)
      • [4.1 使用 EfficientNetB0 进行微调(猫狗分类)](#4.1 使用 EfficientNetB0 进行微调(猫狗分类))
      • [4.2 使用 TensorFlow Hub 加载预训练模型(EfficientNet)](#4.2 使用 TensorFlow Hub 加载预训练模型(EfficientNet))
      • [4.3 多分类案例:花朵识别(5类)](#4.3 多分类案例:花朵识别(5类))
    • [5. 案例实操演练](#5. 案例实操演练)
      • [5.1 数据集准备(模拟)](#5.1 数据集准备(模拟))
    • [6. 常见坑点与排错总结](#6. 常见坑点与排错总结)
      • [6.1 预处理不匹配](#6.1 预处理不匹配)
      • [6.2 冻结与解冻策略](#6.2 冻结与解冻策略)
      • [6.3 训练稳定性](#6.3 训练稳定性)
      • [6.4 TensorFlow Hub坑点](#6.4 TensorFlow Hub坑点)
    • [7. 知识点总结 + 课后作业](#7. 知识点总结 + 课后作业)
      • [7.1 核心知识点梳理](#7.1 核心知识点梳理)
      • [7.2 基础作业](#7.2 基础作业)
      • [7.3 进阶实操作业](#7.3 进阶实操作业)
      • [7.4 思考拓展题](#7.4 思考拓展题)
  • [🔗《TensorFlow2.x: 深度学习入门到高阶实战教程》系列课程导航](#🔗《TensorFlow2.x: 深度学习入门到高阶实战教程》系列课程导航)

1. 课前导读

1.1 本节课学习目标

  • 掌握使用tf.keras.applications加载多种预训练模型(ResNet、EfficientNet、MobileNet等)。
  • 理解TensorFlow Hub的作用,并能够从TF Hub加载预训练模型进行迁移学习。
  • 熟练掌握微调训练的标准流程:加载预训练基、冻结、添加分类头、编译、训练分类头、解冻部分层、降低学习率继续训练。
  • 学会为不同预训练模型设置正确的预处理函数。
  • 能够评估迁移学习模型的性能,并与从头训练模型对比。
  • 通过实践案例,掌握在小数据集上利用预训练模型达到高准确率的技巧。

1.2 知识重难点

类别 内容
重点 tf.keras.applications中预训练模型的加载与参数配置;微调的分阶段训练策略;TensorFlow Hub的使用
难点 不同预训练模型的输入尺寸与预处理差异;微调时学习率的选择与层解冻策略;避免过拟合(小数据集+增强)
易混淆点 weights='imagenet'weights=None的区别;include_top=False与全局池化的配合;TF Hub中模型签名的处理

1.3 学习前置条件

  • 已完成第33课,掌握迁移学习的基本原理。
  • 熟悉TensorFlow的数据加载和模型训练流程。
  • 能够使用ImageDataGeneratortf.data加载图像数据。

1.4 学完可掌握能力

  • 独立使用官方预训练模型进行图像分类任务的迁移学习。
  • 根据任务和数据量选择合适的预训练模型和微调策略。
  • 利用TensorFlow Hub快速获取最新预训练模型(如EfficientNet、BERT)。
  • 解决微调过程中的过拟合和训练不稳定问题。

1.5 行业应用场景

  • 医疗影像分类:利用在ImageNet上预训练的模型微调肺结节分类。
  • 遥感图像分析:使用预训练模型识别卫星图像中的建筑物。
  • 缺陷检测:工业产品表面缺陷分类。
  • 自然语言处理:使用BERT预训练模型进行情感分类、命名实体识别。

2. 核心理论精讲

2.1 tf.keras.applications 预训练模型库

TensorFlow内置了多种在ImageNet上预训练的模型,包括:

  • VGG16/VGG19:经典但参数量大(138M),速度慢。
  • ResNet50/101/152:引入残差连接,支持更深网络。
  • InceptionV3/V4:使用Inception模块,计算高效。
  • MobileNetV1/V2/V3:轻量级,适合移动端。
  • EfficientNetB0-B7:基于神经架构搜索,精度和速度平衡最佳。

每个模型可通过tf.keras.applications.{model_name}加载,常用参数:

  • weights'imagenet'(预训练权重)或None(随机初始化)。
  • include_top:是否包含顶部的全连接分类器(ImageNet的1000类)。迁移学习时通常设为False
  • input_shape:可选输入尺寸,需与模型兼容。

2.2 微调的最佳实践

根据目标数据集大小和与ImageNet的相似度,建议采用以下流程:

  1. 数据准备 :确保数据预处理与预训练模型要求一致(尺寸、归一化方式)。使用模型的preprocess_input函数。
  2. 加载预训练基 :设置include_top=Falseweights='imagenet'
  3. 冻结预训练基base_model.trainable = False
  4. 添加分类头 :通常包含全局池化(GlobalAveragePooling2D)、Dropout、全连接层(输出类别数)。
  5. 第一阶段训练(特征提取):只训练分类头,使用中等学习率(如0.001),训练若干轮(10-20)。
  6. 解冻部分顶层 :设置base_model.trainable = True,并选择性解冻最后几层(如最后2-3个卷积块)。
  7. 第二阶段训练(微调):使用非常小的学习率(如1e-5),继续训练10-20轮,同时可降低批次大小。

为什么分阶段:如果一开始就解冻所有层,随机初始化的分类头会产生较大梯度,破坏预训练特征。

2.3 防止过拟合的技巧

小数据集迁移学习时,过拟合风险高。可采取:

  • 大幅数据增强(旋转、翻转、缩放、亮度调整)。
  • Dropout(在分类头中,率0.2~0.5)。
  • 早停(EarlyStopping)。
  • L2正则化(在全连接层)。
  • 减少微调的层数或降低学习率。

2.4 TensorFlow Hub简介

TensorFlow Hub是一个预训练模型库,包含更多模型(如EfficientNet、BERT、Universal Sentence Encoder)。使用方式:

python 复制代码
import tensorflow_hub as hub
model = tf.keras.Sequential([
    hub.KerasLayer("https://tfhub.dev/google/imagenet/mobilenet_v2_100_224/classification/5", trainable=False),
    layers.Dense(num_classes, activation='softmax')
])

TF Hub模型通常已经包含预处理(归一化),但需确认输入范围。

3. 环境搭建与工具配置

沿用第33课环境,额外安装tensorflow-hub

bash 复制代码
conda activate tf213
pip install tensorflow-hub

下载示例数据集(猫狗分类、花朵分类)。可用TensorFlow内置的tf.keras.utils.get_file或手动下载。

4. 代码实战教学

4.1 使用 EfficientNetB0 进行微调(猫狗分类)

python 复制代码
import tensorflow as tf
from tensorflow.keras import layers, models, optimizers, callbacks
from tensorflow.keras.applications import EfficientNetB0
from tensorflow.keras.applications.efficientnet import preprocess_input
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import matplotlib.pyplot as plt
import os

# 设置路径
_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')
val_dir = os.path.join(PATH, 'validation')

# 数据增强与预处理
train_datagen = ImageDataGenerator(
    preprocessing_function=preprocess_input,
    rotation_range=40,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,
    fill_mode='nearest'
)
val_datagen = ImageDataGenerator(preprocessing_function=preprocess_input)

batch_size = 32
img_size = 224  # EfficientNetB0 默认输入224
train_generator = train_datagen.flow_from_directory(
    train_dir, target_size=(img_size, img_size), batch_size=batch_size, class_mode='binary')
val_generator = val_datagen.flow_from_directory(
    val_dir, target_size=(img_size, img_size), batch_size=batch_size, class_mode='binary')

# 加载预训练基(不包含顶层)
base_model = EfficientNetB0(weights='imagenet', include_top=False, input_shape=(img_size, img_size, 3))
base_model.trainable = False  # 冻结

# 构建模型
inputs = tf.keras.Input(shape=(img_size, img_size, 3))
x = base_model(inputs, training=False)
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dropout(0.2)(x)
outputs = layers.Dense(1, activation='sigmoid')(x)
model = tf.keras.Model(inputs, outputs)
model.compile(optimizer=optimizers.Adam(0.001), loss='binary_crossentropy', metrics=['accuracy'])

# 第一阶段:特征提取
early_stop = callbacks.EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True)
history1 = model.fit(train_generator, epochs=10, validation_data=val_generator, callbacks=[early_stop], verbose=1)

# 第二阶段:微调(解冻顶层)
base_model.trainable = True
# 冻结除最后50层之外的所有层(EfficientNetB0约237层,解冻最后50层)
for layer in base_model.layers[:-50]:
    layer.trainable = False
# 重新编译,使用更小学习率
model.compile(optimizer=optimizers.Adam(1e-5), loss='binary_crossentropy', metrics=['accuracy'])
history2 = model.fit(train_generator, epochs=10, validation_data=val_generator, callbacks=[early_stop], verbose=1)

# 最终评估
val_loss, val_acc = model.evaluate(val_generator)
print(f"Validation accuracy after fine-tuning: {val_acc:.4f}")

# 绘制训练曲线
acc = history1.history['accuracy'] + history2.history['accuracy']
val_acc = history1.history['val_accuracy'] + history2.history['val_accuracy']
plt.plot(acc, label='Train acc')
plt.plot(val_acc, label='Val acc')
plt.axvline(x=len(history1.history['accuracy']), color='r', linestyle='--', label='Start fine-tune')
plt.legend()
plt.title('Transfer Learning with EfficientNetB0')
plt.show()

4.2 使用 TensorFlow Hub 加载预训练模型(EfficientNet)

python 复制代码
import tensorflow_hub as hub

# 加载TF Hub模型(特征提取版本,不包含分类头)
feature_extractor_url = "https://tfhub.dev/tensorflow/efficientnet/b0/classification/1"
# 或者使用特征向量版本(无分类头)
feature_vector_url = "https://tfhub.dev/tensorflow/efficientnet/b0/feature-vector/1"

# 创建模型
def build_model_from_hub(num_classes):
    model = tf.keras.Sequential([
        hub.KerasLayer(feature_vector_url, trainable=True, input_shape=(224,224,3)),
        layers.Dense(num_classes, activation='softmax')
    ])
    return model

model_hub = build_model_from_hub(2)
model_hub.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
# 注意:TF Hub模型通常已经包含预处理(将输入从[0,255]映射到[-1,1]),但需确认。
# 使用时直接传入原始像素值(0-255)即可,无需额外预处理。
# 训练代码同上(需调整生成器的class_mode为'sparse')

4.3 多分类案例:花朵识别(5类)

使用TensorFlow内置的花朵数据集(来自tf.keras.preprocessing.image_dataset_from_directory)。

python 复制代码
import pathlib
dataset_url = "https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz"
data_dir = tf.keras.utils.get_file(origin=dataset_url, fname='flower_photos', extract=True)
data_dir = pathlib.Path(data_dir).parent / 'flower_photos'

batch_size = 32
img_height = 224
img_width = 224

# 使用image_dataset_from_directory
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)
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)

class_names = train_ds.class_names
num_classes = len(class_names)

# 应用预处理(ResNet50的预处理)
from tensorflow.keras.applications.resnet50 import preprocess_input
def preprocess(image, label):
    image = tf.cast(image, tf.float32)
    image = preprocess_input(image)
    return image, label

train_ds = train_ds.map(preprocess).cache().prefetch(tf.data.AUTOTUNE)
val_ds = val_ds.map(preprocess).cache().prefetch(tf.data.AUTOTUNE)

# 加载ResNet50
base_model = tf.keras.applications.ResNet50(weights='imagenet', include_top=False, input_shape=(224,224,3))
base_model.trainable = False

inputs = tf.keras.Input(shape=(224,224,3))
x = base_model(inputs, training=False)
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dropout(0.2)(x)
outputs = layers.Dense(num_classes, activation='softmax')(x)
model_resnet = tf.keras.Model(inputs, outputs)
model_resnet.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])

# 特征提取阶段
history = model_resnet.fit(train_ds, validation_data=val_ds, epochs=15, verbose=1)

# 微调:解冻最后3个卷积块(ResNet50的卷积块名称: conv2_block, conv3_block, conv4_block, conv5_block)
base_model.trainable = True
for layer in base_model.layers:
    if 'conv5_block' in layer.name or 'conv4_block' in layer.name:
        layer.trainable = True
    else:
        layer.trainable = False
model_resnet.compile(optimizer=tf.keras.optimizers.Adam(1e-5),
                     loss='sparse_categorical_crossentropy', metrics=['accuracy'])
history_finetune = model_resnet.fit(train_ds, validation_data=val_ds, epochs=10, verbose=1)

val_loss, val_acc = model_resnet.evaluate(val_ds)
print(f"Flower classification accuracy: {val_acc:.4f}")

5. 案例实操演练

案例:使用EfficientNetB3进行细粒度图像分类(狗品种识别)

使用Stanford Dogs数据集(120个品种),演示迁移学习在细粒度分类中的效果。

5.1 数据集准备(模拟)

由于完整数据集较大,我们模拟数据集结构并演示代码框架。

python 复制代码
# 假设数据目录结构: dogs/train/breed1, dogs/val/breed1, ...
# 使用tf.keras.preprocessing.image_dataset_from_directory加载
train_ds = tf.keras.preprocessing.image_dataset_from_directory(
    'dogs/train', image_size=(300,300), batch_size=32, label_mode='categorical')
val_ds = tf.keras.preprocessing.image_dataset_from_directory(
    'dogs/val', image_size=(300,300), batch_size=32, label_mode='categorical')

# 使用EfficientNetB3(输入尺寸300x300)
from tensorflow.keras.applications import EfficientNetB3
from tensorflow.keras.applications.efficientnet import preprocess_input

def preprocess(image, label):
    image = tf.cast(image, tf.float32)
    image = preprocess_input(image)
    return image, label

train_ds = train_ds.map(preprocess).cache().prefetch(tf.data.AUTOTUNE)
val_ds = val_ds.map(preprocess).cache().prefetch(tf.data.AUTOTUNE)

base_model = EfficientNetB3(weights='imagenet', include_top=False, input_shape=(300,300,3))
base_model.trainable = False
inputs = tf.keras.Input(shape=(300,300,3))
x = base_model(inputs, training=False)
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dropout(0.3)(x)
outputs = layers.Dense(120, activation='softmax')(x)
model = tf.keras.Model(inputs, outputs)
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

# 训练
model.fit(train_ds, validation_data=val_ds, epochs=20)

# 微调
base_model.trainable = True
for layer in base_model.layers[:-30]:
    layer.trainable = False
model.compile(optimizer=tf.keras.optimizers.Adam(1e-5), loss='categorical_crossentropy', metrics=['accuracy'])
model.fit(train_ds, validation_data=val_ds, epochs=10)

6. 常见坑点与排错总结

6.1 预处理不匹配

  • 坑1 :使用preprocess_input时,有些模型要求输入为0,255的像素值,有些要求-1,10,1。EfficientNet的preprocess_input将输入从0,255转换为-1,1

    • 解决:查看文档,确保数据生成器不额外除以255。
  • 坑2:输入尺寸与模型不匹配,例如ResNet50要求至少32×32,但最佳224×224。

    • 解决 :使用target_size调整。

6.2 冻结与解冻策略

  • 坑3 :忘记在特征提取阶段将base_modeltrainable=False,导致预训练权重被破坏。

  • 坑4:微调时学习率过高导致损失震荡。

    • 解决:从1e-5开始,若震荡则降低。
  • 坑5:解冻的层数过多,小数据集过拟合。

    • 策略:数据量<1000时,只解冻最后2-3层;数据量>5000时可解冻更多。

6.3 训练稳定性

  • 坑6 :BatchNormalization层在training=Falsetraining=True下的行为不同。特征提取阶段应设为False,微调阶段可设为True
  • 坑7 :使用ImageDataGeneratorflow_from_directory时,class_mode应匹配分类头输出(二分类用binary,多分类用categoricalsparse)。

6.4 TensorFlow Hub坑点

  • 坑8:TF Hub模型可能已包含Flatten或分类头,需确认是否需要添加自己的层。
  • 坑9 :从TF Hub加载的模型默认不可训练(trainable=False),需设置trainable=True才能微调。

7. 知识点总结 + 课后作业

7.1 核心知识点梳理

  • 预训练模型库tf.keras.applications支持VGG、ResNet、EfficientNet等;TensorFlow Hub提供更丰富模型。
  • 迁移学习流程 :加载预训练基(include_top=False)→ 冻结 → 添加分类头 → 训练分类头 → 解冻顶层 → 低学习率微调。
  • 数据预处理 :使用对应模型的preprocess_input,保证输入范围正确。
  • 防止过拟合:数据增强、Dropout、早停、分层微调。

7.2 基础作业

  1. 使用MobileNetV2作为预训练模型,在猫狗数据集上进行特征提取,报告验证准确率。
  2. 修改EfficientNetB0的微调解冻层数(解冻最后20层、50层、全部层),比较最终准确率。
  3. 在花朵数据集上,不使用数据增强,重复微调实验,观察过拟合现象。

7.3 进阶实操作业

任务:在CIFAR-10上使用EfficientNetB0进行迁移学习并对比

要求:

  • 由于CIFAR-10图像尺寸小(32×32),需调整输入尺寸或使用layers.Resizing上采样到224×224。
  • 比较三种方法:从头训练小CNN、特征提取(冻结)、微调。
  • 分析领域差异对迁移效果的影响(自然图像 vs 小尺寸物体)。

7.4 思考拓展题

  1. 如果你的目标数据集与ImageNet的类别完全不相关(例如医学MRI图像),迁移学习还有效吗?如何改进?

  2. 在微调阶段,为什么通常解冻靠近输出的层,而不是靠近输入的层?请从特征通用性角度解释。

  3. 当目标数据集非常大(如百万级)且与ImageNet差异大时,从头训练与迁移学习哪个更优?为什么?


下一课预告:模型轻量化优化------我们将学习量化、剪枝、蒸馏等模型压缩技术,帮助你在资源受限设备上部署深度学习模型。


🔗《TensorFlow2.x: 深度学习入门到高阶实战教程》系列课程导航

去订阅

第一部分:基础入门(1-10 课)

第二部分:神经网络核心(11-25 课)

第三部分:进阶网络与框架高阶(26-40 课)

第四部分:企业实战与项目落地(41-50 课)
🌟 感谢您耐心阅读到这里!

💡 如果本文对您有所启发欢迎:

👍 点赞📌 收藏 📤 分享给更多需要的伙伴。

🗣️ 期待在评论区看到您的想法, 共同进步。

🔔 关注我,持续获取更多干货内容~

🤗 我们下篇文章见~

相关推荐
deepdata_cn2 天前
元学习、迁移学习、小样本学习的区别
深度学习·迁移学习
空堂与归4 天前
迁移学习怎么落地?Transformers 库微调实战
人工智能·机器学习·自然语言处理·transformer·迁移学习
weixin_440213294 天前
7大机器学习范式:有监督/自监督/半监督/主动学习/弱监督/自训练/迁移学习
机器学习·迁移学习·自监督学习·有监督学习·主动学习
Thomas.Sir5 天前
第26课:TensorFlow|循环神经网络RNN原理【时序数据处理、序列依赖关系讲解】
人工智能·rnn·tensorflow
Thomas.Sir5 天前
第24课:TensorFlow|图像分类实战训练【手写数字、日常图像分类完整项目】
人工智能·分类·tensorflow
2601_962077986 天前
机器学习及其Python实践
pytorch·python·机器学习·tensorflow·scikit-learn
2601_962300476 天前
Python TensorFlow对比PyTorch_Python TensorFlow和PyTorch在机器学习中的差异
pytorch·python·深度学习·机器学习·tensorflow
2601_962381587 天前
[Python人工智能] 九.gensim词向量Word2Vec安装及《庆余年》中文短文本相似度计算
人工智能·python·tensorflow·word2vec·文本相似度
小江的记录本7 天前
【CSS】CSS 核心:盒模型、BFC/IFC、Flex/Grid 布局、响应式布局、移动端适配(附《思维导图》)
前端·css·面试·前端框架·tensorflow·html5·xss