在深度学习领域,数据问题尤为突出,常常面临数据量不足或数据质量不高的挑战。一个明显的解决方案是获取更多的高质量数据,但这往往意味着需要在数据量和数据质量之间做出权衡。幸运的是,有一种更具创新性的解决方案——迁移学习。
迁移学习是一种利用已经训练好的模型来完成另一项任务的方法。最初的训练步骤被称为预训练。其核心思想是,预训练阶段让模型学习到更通用的特征,而最终的微调阶段则让模型学习到特定于自己(有限的)数据的特征。
迁移学习在医学等领域尤其有用,因为这些领域长期面临数据短缺的问题。各种在ImageNet数据上预训练的CNN模型已在不同的医学任务中证明是成功的。只需要几行代码就可以将它们转移到医学数据上。
本文将学习如何使用TensorFlow——截至2021年世界上最广泛使用的深度学习平台——来实现这一点。在深入代码之前,让快速回顾一下TensorFlow和驱动它的Keras API。
TensorFlow是一个端到端的平台,可以构建和部署机器学习模型。只对构建模型感兴趣,而不是部署它们,因此需要使用Keras。Keras是一个为“人类而不是机器”设计的API,用他们自己的话来说,这意味着Keras是为像这样的编码者设计的,想要构建自定义模型。其简单且易于记忆的语法使其几乎令人上瘾。
虽然Keras API本身可以作为一个独立的Python库使用,但它也是TensorFlow库的一部分。推荐使用tensorflow.keras而不是Keras本身,因为它由TensorFlow团队维护,这确保了与其他TensorFlow模块的一致性。
作为第一个例子,将尝试二元图像分类。将使用Kaggle上的“热狗-非热狗”数据集,并尝试预测给定的图像是否是热狗。为此,将使用在ImageNet数据集上预训练的ResNet50模型。ResNet指的是使用残差连接来解决精度退化问题的一系列架构。
上面的图表展示了残差映射。这种连接跳过一个(或多个)层并执行恒等映射,F(x) + x。这种网络架构的微小调整在解决退化问题方面取得了巨大的成功。因此,ResNet架构可以深达1000层。特定的模型选择,ResNet50,是一个相对较浅的例子。可以在下面的图表中看到它的整体架构:
ResNet家族有替代品:MobileNets、Inception等也在图像分类中证明是成功的。也可以选择这些中的一个,或者一个完全不同的网络,并在其上执行迁移学习。
将在Google Colab上工作,推荐给任何计算机不胜任任务的人,尽管这不是一个严格的要求。可以在任何选择的环境中运行代码,包括Jupyter Notebook或PyChram。
注意:这一步可能根据首选环境而有所不同。
from google.colab import files
files.upload()
! mkdir ~/.kaggle
! cp kaggle.json ~/.kaggle/
! chmod 600 ~/.kaggle/kaggle.json
! pip install -q kaggle
! kaggle datasets download -d dansbecker/hot-dog-not-hot-dog
import tensorflow as tf
from tensorflow import keras
from PIL import Image
import os
import numpy as np
接下来,将加载数据并开始迁移学习的过程。
!unzip /content/hot-dog-not-hot-dog.zip
for image in list(os.walk("/content/train/not_hot_dog"))[0][2]:
a = Image.open(f"/content/train/not_hot_dog/{image}")
print(np.asarray(a).shape)
这只是输出的一部分,但已经可以看到图像大小不是恒定的。ImageDataGenerator可以处理这类问题,以及其他许多问题。
图像数据本质上是一个数字数组。彩色图像由三个2D矩阵的组合表示。这些矩阵中的每一个都由0到255之间的值组成(这可能有所不同)。这三个值结合在一起(每个值来自一个矩阵)代表像素的颜色。在例子中,图像形状是(512, 512, 3)。也就是说,有512*512=262144个像素和3个通道。(正如已经说过的,不是所有的图像都符合512*512的大小,但将处理它。)
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator()
test_datagen = tf.keras.preprocessing.image.ImageDataGenerator()
train_data_generator = train_datagen.flow_from_directory(
"/content/train",
target_size=(512,512)
)
test_data_generator = train_datagen.flow_from_directory(
"/content/test",
target_size=(512,512)
)
ImageDataGenerator对象将在需要时为模型批量生成数据。这使能够直接使用存储在硬盘上的数据,而不会过度消耗RAM。train_data_generator和test_data_generator将作为参数分别传递给x和validation_data参数。由于ImageDataGenerator从文件夹名称中获取类别,不需要y参数。(如果尝试传递y参数,Python将抛出错误。)
现在训练和测试数据已经设置好了,可以构建和训练模型了。
首先,将加载Keras实现的ResNet50模型。
resnet_50 = tf.keras.applications.resnet50.ResNet50(include_top=False, weights='imagenet')
resnet_50.trainable=False
include_top=False确保ResNet50模型的最后一层不会被加载。weights='imagenet'加载了ImageNet权重。如果设置weights=None,那么权重将被随机初始化(在这种情况下,将不会执行迁移学习)。通过将trainable属性设置为False,确保模型的原始(ImageNet)权重保持不变。
inputs = keras.Input(shape=(512,512,3))
x = resnet_50(inputs)
x = keras.layers.GlobalAveragePooling2D()(x)
outputs = keras.layers.Dense(2, activation="softmax")(x)
model = keras.Model(inputs=inputs, outputs=outputs, name="my_model")
model.compile(optimizer="Adam", loss="binary_crossentropy", metrics=["accuracy"])
model.summary()
model.fit(train_data_generator, validation_data=test_data_generator, epochs=5)