深度卷积神经网络模型在非常大的数据集上训练可能需要数天甚至数周。
一种缩短此过程的方法是重用为标准计算机视觉基准数据集(例如 ImageNet 图像识别任务)开发的预训练模型的模型权重。可以下载顶级模型的权重并直接使用,或者将它们集成到新模型中以解决您自己的计算机视觉问题。
在本帖中,您将了解在开发计算机视觉应用的卷积神经网络时如何使用迁移学习。
阅读本文后,你将了解:
- 迁移学习涉及使用在一个问题上训练的模型作为相关问题的起点。
- 迁移学习很灵活,允许直接使用预训练模型,作为特征提取预处理,并集成到全新的模型中。
- Keras 提供了对 ImageNet 图像识别任务中许多顶级模型的便捷访问,例如 VGG、Inception 和 ResNet。
通过我的新书《深度学习计算机视觉》,其中包括分步教程和所有示例的Python源代码文件,来快速启动您的项目。
让我们开始吧。
- 已于 2020 年 8 月更新:更新了 Keras 2.4.3 和 TensorFlow 2.3 的 API。

开发卷积神经网络模型时如何使用迁移学习
照片由 GoToVan 拍摄,保留部分权利。
概述
本教程分为五个部分;它们是:
- 什么是迁移学习?
- 迁移学习用于图像识别
- 如何使用预训练模型
- 用于迁移学习的模型
- 使用预训练模型的示例
什么是迁移学习?
迁移学习通常是指这样一个过程:在一个问题上训练的模型以某种方式用于第二个相关问题。
在深度学习中,迁移学习是一种技术,其中神经网络模型首先在与正在解决的问题相似的问题上进行训练。然后,将训练模型的一个或多个层用于训练目标问题的新模型中。
这通常在监督学习的上下文中理解,其中输入相同,但目标可能性质不同。例如,在第一个场景中,我们可能学习关于一组视觉类别,如猫和狗;然后在第二个场景中,我们学习关于另一组视觉类别,如蚂蚁和黄蜂。
— 第 536 页,《深度学习》,2016 年。
迁移学习的好处是可以缩短神经网络模型的训练时间,并能带来更低的泛化误差。
重用层的权重可以用作训练过程的起点,并响应新问题进行调整。这种用法将迁移学习视为一种权重初始化方案。当第一个相关问题拥有比目标问题多得多的标记数据,并且问题结构的相似性在两个上下文中都有用时,这可能会很有用。
“…目标是利用第一种情况下的数据来提取在学习甚至在第二种情况下的直接预测时可能有用信息。”
— 第 538 页,《深度学习》,2016 年。
想通过深度学习实现计算机视觉成果吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
迁移学习用于图像识别
一系列高性能模型已用于图像分类,并在年度ImageNet 大规模视觉识别挑战赛(或 ILSVRC)上得到了展示。
这场竞赛通常简称为ImageNet,因为它使用了竞赛中的图像来源,催生了许多在卷积神经网络架构和训练方面的创新。此外,竞赛中的许多模型已根据宽松的许可证发布。
这些模型可以作为计算机视觉应用中迁移学习的基础。
这之所以可取,有多种原因,其中最重要的是
- 有用的学习特征:模型已学会从照片中检测通用特征,因为它们在 1,000 个类别上训练了超过 100 万张图像。
- 最先进的性能:模型在它们被开发的特定图像识别任务上取得了最先进的性能,并且仍然有效。
- 易于访问:模型权重作为免费可下载文件提供,并且许多库提供了方便的 API 来直接下载和使用模型。
可以使用一系列不同的深度学习库(包括 Keras)在相同的模型架构中使用下载的模型权重。
如何使用预训练模型
预训练模型的使用仅受限于您的创造力。
例如,一个模型可以按原样下载和使用,例如嵌入到应用程序中并用于对新照片进行分类。
或者,模型可以下载并用作特征提取模型。在这种情况下,模型输出层之前的层的模型输出被用作新分类器模型的新输入。
请记住,更靠近模型输入层的卷积层学习线条等低级特征,层中间学习结合输入提取的低级特征的复杂抽象特征,而更靠近输出层的层则根据分类任务解释提取的特征。
有了这个理解,就可以选择从现有预训练模型提取特征的详细程度。例如,如果一项新任务与对照片中的对象进行分类(例如,与 ImageNet 不同)截然不同,那么预训练模型的前几层的输出可能就足够了。如果新任务与对照片中的对象进行分类的任务非常相似,那么可以使用模型更深层的输出,甚至可以使用输出层之前的全连接层的输出。
预训练模型可以作为独立的特征提取程序使用,在这种情况下,输入可以通过模型或模型的一部分进行预处理,为每个输入图像生成一个输出(例如,数字向量),然后可以将其用作训练新模型时的输入。
或者,预训练模型或模型的所需部分可以直接集成到新的神经网络模型中。在这种用法中,预训练模型的权重可以被冻结,以便在新模型训练时它们不会被更新。或者,在训练新模型时,权重可能会被更新,可能使用较低的学习率,允许预训练模型在训练新模型时充当权重初始化方案。
我们可以将其中一些用法模式总结如下:
- 分类器:预训练模型直接用于对新图像进行分类。
- 独立特征提取器:预训练模型或模型的一部分用于预处理图像并提取相关特征。
- 集成特征提取器:预训练模型或模型的一部分被集成到新模型中,但在训练期间预训练模型的层被冻结。
- 权重初始化:预训练模型或模型的一部分被集成到新模型中,并且预训练模型的层与新模型一起进行训练。
每种方法都可能有效,并为开发和训练深度卷积神经网络模型节省大量时间。
对于您的新计算机视觉任务,您可能不清楚使用预训练模型的哪种方法会产生最佳结果,因此可能需要进行一些实验。
用于迁移学习的模型
用于图像识别的顶级模型可能有很多(大约十几个或更多),可以下载并作为图像识别及相关计算机视觉任务的基础。
也许其中最流行的三个模型如下:
- VGG(例如 VGG16 或 VGG19)。
- GoogLeNet(例如 InceptionV3)。
- 残差网络(例如 ResNet50)。
这些模型因其性能而被广泛用于迁移学习,同时也因为它们引入了特定架构创新的示例,即一致且重复的结构(VGG)、Inception 模块(GoogLeNet)和残差模块(ResNet)。
Keras 提供了对为图像识别任务开发的许多顶级预训练模型的访问。
它们可通过 Applications API 获得,其中包括加载带或不带预训练权重的模型的功能,以及以给定模型期望的方式准备数据(例如,调整大小和像素值)。
第一次加载预训练模型时,Keras 将下载所需的模型权重,这可能需要一些时间,具体取决于您的互联网连接速度。权重存储在您主目录下的 .keras/models/ 目录中,下次使用时将从该位置加载。
加载给定模型时,“include_top”参数可以设置为 False,在这种情况下,不会加载用于做出预测的模型中的全连接输出层,从而允许添加并训练新的输出层。例如:
1 2 3 |
... # 加载不带输出层的模型 model = VGG16(include_top=False) |
此外,当“include_top”参数为 False 时,必须指定“input_tensor”参数,允许更改模型预期的固定大小输入。例如:
1 2 3 4 |
... # 加载模型并指定新的输入形状 new_input = Input(shape=(640, 480, 3)) model = VGG16(include_top=False, input_tensor=new_input) |
没有顶层的模型将直接输出最后一个卷积层或池化层的激活。一种总结这些激活以供分类器使用或作为输入特征向量表示的方法是添加一个全局池化层,例如最大全局池化或平均全局池化。结果是一个可用于输入特征描述符的向量。Keras 通过“pooling”参数直接提供了此功能,该参数可以设置为“avg”或“max”。例如:
1 2 3 4 |
... # 加载模型并为图像指定新的输入形状以及平均池化输出 new_input = Input(shape=(640, 480, 3)) model = VGG16(include_top=False, input_tensor=new_input, pooling='avg') |
可以使用 preprocess_input() 函数为给定模型准备图像;例如,像素缩放以模型开发时用于训练数据集的方式进行。例如:
1 2 3 4 5 |
... # 准备一张图片 from keras.applications.vgg16 import preprocess_input images = ... prepared_images = preprocess_input(images) |
最后,您可能希望在自己的数据集上使用模型架构,但不使用预训练的权重,而是用随机权重初始化模型并从头开始训练模型。
这可以通过将“weights”参数设置为 None(而不是默认的“imagenet”)来实现。此外,“classes”参数可以设置为定义您数据集中类的数量,这将为您配置模型的输出层。例如:
1 2 3 4 |
... # 定义一个带有随机权重和 10 个类的新模型 new_input = Input(shape=(640, 480, 3)) model = VGG16(weights=None, input_tensor=new_input, classes=10) |
现在我们熟悉了 API,让我们来看一下如何使用 Keras Applications API 加载三个模型。
加载 VGG16 预训练模型
VGG16 模型由牛津大学的视觉图形组(VGG)开发,并在 2014 年的论文“用于大规模图像识别的超深卷积网络”中进行了描述。
默认情况下,模型期望彩色输入图像被缩放到 224×224 像素的正方形。
模型可以如下加载:
1 2 3 4 5 6 |
# 加载 vgg16 模型的示例 from keras.applications.vgg16 import VGG16 # 加载模型 model = VGG16() # 总结模型 model.summary() |
运行示例将加载 VGG16 模型,并在需要时下载模型权重。
然后,可以直接使用该模型将照片分类到 1,000 个类别之一。在这种情况下,将总结模型架构以确认它已正确加载。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
_________________________________________________________________ 层(类型) 输出形状 参数数量 ================================================================= input_1 (InputLayer) (None, 224, 224, 3) 0 _________________________________________________________________ block1_conv1 (Conv2D) (None, 224, 224, 64) 1792 _________________________________________________________________ block1_conv2 (Conv2D) (None, 224, 224, 64) 36928 _________________________________________________________________ block1_pool (MaxPooling2D) (None, 112, 112, 64) 0 _________________________________________________________________ block2_conv1 (Conv2D) (None, 112, 112, 128) 73856 _________________________________________________________________ block2_conv2 (Conv2D) (None, 112, 112, 128) 147584 _________________________________________________________________ block2_pool (MaxPooling2D) (None, 56, 56, 128) 0 _________________________________________________________________ block3_conv1 (Conv2D) (None, 56, 56, 256) 295168 _________________________________________________________________ block3_conv2 (Conv2D) (None, 56, 56, 256) 590080 _________________________________________________________________ block3_conv3 (Conv2D) (None, 56, 56, 256) 590080 _________________________________________________________________ block3_pool (MaxPooling2D) (None, 28, 28, 256) 0 _________________________________________________________________ block4_conv1 (Conv2D) (None, 28, 28, 512) 1180160 _________________________________________________________________ block4_conv2 (Conv2D) (None, 28, 28, 512) 2359808 _________________________________________________________________ block4_conv3 (Conv2D) (None, 28, 28, 512) 2359808 _________________________________________________________________ block4_pool (MaxPooling2D) (None, 14, 14, 512) 0 _________________________________________________________________ block5_conv1 (Conv2D) (None, 14, 14, 512) 2359808 _________________________________________________________________ block5_conv2 (Conv2D) (None, 14, 14, 512) 2359808 _________________________________________________________________ block5_conv3 (Conv2D) (None, 14, 14, 512) 2359808 _________________________________________________________________ block5_pool (MaxPooling2D) (None, 7, 7, 512) 0 _________________________________________________________________ flatten (Flatten) (None, 25088) 0 _________________________________________________________________ fc1 (Dense) (None, 4096) 102764544 _________________________________________________________________ fc2 (Dense) (None, 4096) 16781312 _________________________________________________________________ predictions (Dense) (None, 1000) 4097000 ================================================================= 总参数:138,357,544 可训练参数:138,357,544 不可训练参数: 0 _________________________________________________________________ |
加载 InceptionV3 预训练模型
InceptionV3 是 inception 架构的第三个迭代版本,最初是为 GoogLeNet 模型开发的。
该模型由 Google 的研究人员开发,并在 2015 年的论文“重新思考用于计算机视觉的 Inception 架构”中进行了描述。
该模型期望彩色图像的形状为 299×299 像素。
模型可以如下加载:
1 2 3 4 5 6 |
# 加载 inception v3 模型的示例 from keras.applications.inception_v3 import InceptionV3 # 加载模型 model = InceptionV3() # 总结模型 model.summary() |
运行示例将加载模型,在需要时下载权重,然后总结模型架构以确认其已正确加载。
出于简洁性,此处省略了输出,因为它是一个深度模型,包含许多层。
加载 ResNet50 预训练模型
残差网络,简称 ResNet,是一种利用包含快捷连接的残差模块的模型。
它由 Microsoft 的研究人员开发,并在 2015 年的论文“用于图像识别的深度残差学习”中进行了描述。
该模型期望彩色图像的形状为 224×224 像素。
1 2 3 4 5 6 |
# 加载 resnet50 模型的示例 from keras.applications.resnet50 import ResNet50 # 加载模型 model = ResNet50() # 总结模型 model.summary() |
运行示例将加载模型,在需要时下载权重,然后总结模型架构以确认其已正确加载。
出于简洁性,此处省略了输出,因为它是一个深度模型。
使用预训练模型的示例
现在我们熟悉了如何在 Keras 中加载预训练模型,接下来让我们看一些它们如何在实践中使用的示例。
在这些示例中,我们将使用 VGG16 模型,因为它是一个相对简单的模型,并且架构也很容易理解。
我们还需要一张照片来配合这些示例。下图是一只狗的照片,由 Justin Morgan 拍摄,并在宽松的许可证下提供。

狗的照片
下载照片并将其放在当前工作目录中,文件名为 'dog.jpg'。
预训练模型作为分类器
预训练模型可直接用于将新照片分类为 ILSVRC 图像分类任务中 1,000 个已知类别之一。
我们将使用 VGG16 模型对新图像进行分类。
首先,需要加载照片并将其重塑为模型期望的 224×224 像素正方形,并以模型期望的方式缩放像素值。该模型操作的是样本数组,因此加载图像的维度需要扩展 1,以表示一张 224×224 像素、三个通道的图像。
1 2 3 4 5 6 7 8 |
# 从文件加载图像 image = load_img('dog.jpg', target_size=(224, 224)) # 将图像像素转换为numpy数组 image = img_to_array(image) # 重塑数据以适应模型 image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2])) # 为VGG模型准备图像 image = preprocess_input(image) |
接下来,可以加载模型并进行预测。
这意味着将对照片属于 1,000 个类别中的每一个的概率进行预测。在本例中,我们只关心最可能的类别,因此我们可以解码预测并检索具有最高概率的类别的标签或名称。
1 2 3 4 5 6 |
# 预测所有输出类别的概率 yhat = model.predict(image) # 将概率转换为类别标签 label = decode_predictions(yhat) # 检索最可能的結果,例如最高概率 label = label[0][0] |
将所有这些内容结合起来,下面的完整示例将加载一张新照片并预测最可能的类别。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# 使用预训练模型作为分类器的示例 from keras.preprocessing.image import load_img from keras.preprocessing.image import img_to_array from keras.applications.vgg16 import preprocess_input from keras.applications.vgg16 import decode_predictions from keras.applications.vgg16 import VGG16 # 从文件加载图像 image = load_img('dog.jpg', target_size=(224, 224)) # 将图像像素转换为numpy数组 image = img_to_array(image) # 重塑数据以适应模型 image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2])) # 为VGG模型准备图像 image = preprocess_input(image) # 加载模型 model = VGG16() # 预测所有输出类别的概率 yhat = model.predict(image) # 将概率转换为类别标签 label = decode_predictions(yhat) # 检索最可能的結果,例如最高概率 label = label[0][0] # 打印分类结果 print('%s (%.2f%%)' % (label[1], label[2]*100)) |
运行该示例,预测结果不仅是狗,还预测出具体的品种“杜宾犬”,概率为 33.59%,这实际上可能是正确的。
1 |
杜宾犬 (33.59%) |
预训练模型作为特征提取预处理器
预训练模型可以作为独立程序,从新照片中提取特征。
具体来说,照片的提取特征可能是一个数字向量,模型将使用该向量来描述照片中的特定特征。然后,这些特征可以作为输入来开发新模型。
VGG16 模型最后几层是输出层之前的全连接层。这些层将提供一套复杂的特征来描述给定的输入图像,并可能为训练新的图像分类或相关计算机视觉任务提供有用的输入。
可以像在前一个示例中一样加载和准备图像。
我们将加载带有分类器输出部分的模型,但手动删除最后一个输出层。这意味着第二个最后的全连接层(具有 4,096 个节点)将是新的输出层。
1 2 3 4 |
# 加载模型 model = VGG16() # 移除输出层 model = Model(inputs=model.inputs, outputs=model.layers[-2].output) |
这个包含 4,096 个数字的向量将用于表示给定输入图像的复杂特征,然后可以将其保存到文件,以便稍后加载并用作训练新模型的输入。我们可以将其保存为 pickle 文件。
1 2 3 4 5 |
# 获取提取的特征 features = model.predict(image) print(features.shape) # 保存到文件 dump(features, open('dog.pkl', 'wb')) |
将以上所有内容整合起来,下面是使用模型作为独立特征提取模型的完整示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# 使用vgg16模型作为特征提取模型的示例 from keras.preprocessing.image import load_img from keras.preprocessing.image import img_to_array from keras.applications.vgg16 import preprocess_input from keras.applications.vgg16 import decode_predictions from keras.applications.vgg16 import VGG16 from keras.models import Model from pickle import dump # 从文件加载图像 image = load_img('dog.jpg', target_size=(224, 224)) # 将图像像素转换为numpy数组 image = img_to_array(image) # 重塑数据以适应模型 image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2])) # 为VGG模型准备图像 image = preprocess_input(image) # 加载模型 model = VGG16() # 移除输出层 model = Model(inputs=model.inputs, outputs=model.layers[-2].output) # 获取提取的特征 features = model.predict(image) print(features.shape) # 保存到文件 dump(features, open('dog.pkl', 'wb')) |
运行示例将加载照片,然后将模型准备为特征提取模型。
从加载的照片中提取特征,并打印出特征向量的形状,显示其包含 4,096 个数字。然后将此特征保存到当前工作目录中的新文件dog.pkl。
1 |
(1, 4096) |
此过程可以为新训练数据集中的每张照片重复执行。
预训练模型作为模型中的特征提取器
我们可以直接将预训练模型中的部分或全部层用作新模型的特征提取组件。
这可以通过加载模型,然后简单地添加新层来实现。这可能涉及添加新的卷积层和池化层来扩展模型的特征提取能力,或者添加新的全连接分类器类型的层来学习如何在新数据集上解释提取的特征,或者两者的组合。
例如,我们可以通过将“include_top”参数设置为“False”来加载不带模型分类器部分 VGG16 模型,并指定新数据集中图像的首选形状为 300×300。
1 2 |
# 加载不带分类器层的模型 model = VGG16(include_top=False, input_shape=(300, 300, 3)) |
然后,我们可以使用 Keras 函数 API 在 VGG16 模型的最后一个池化层之后添加一个新的 Flatten 层,然后定义一个具有 Dense 全连接层和输出层的新分类器模型,该层将预测 10 个类的概率。
1 2 3 4 5 6 |
# 添加新的分类器层 flat1 = Flatten()(model.layers[-1].output) class1 = Dense(1024, activation='relu')(flat1) output = Dense(10, activation='softmax')(class1) # 定义新模型 model = Model(inputs=model.inputs, outputs=output) |
添加 Flatten 层的一种替代方法是使用平均池化层定义 VGG16 模型,然后添加全连接层。您或许可以针对您的应用程序尝试这两种方法,看看哪种方法能带来最佳性能。
VGG16 模型和新模型的权重都将在新数据集上一起训练。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# 训练vgg16模型的示例 from keras.applications.vgg16 import VGG16 from keras.models import Model from keras.layers import Dense from keras.layers import Flatten # 加载不带分类器层的模型 model = VGG16(include_top=False, input_shape=(300, 300, 3)) # 添加新的分类器层 flat1 = Flatten()(model.layers[-1].output) class1 = Dense(1024, activation='relu')(flat1) output = Dense(10, activation='softmax')(class1) # 定义新模型 model = Model(inputs=model.inputs, outputs=output) # 总结 model.summary() # ... |
运行示例将定义新模型并进行训练,并总结模型架构。
我们可以看到,我们已经展平了最后一个池化层的输出,并添加了我们的新全连接层。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
_________________________________________________________________ 层(类型) 输出形状 参数数量 ================================================================= input_1 (InputLayer) (None, 300, 300, 3) 0 _________________________________________________________________ block1_conv1 (Conv2D) (None, 300, 300, 64) 1792 _________________________________________________________________ block1_conv2 (Conv2D) (None, 300, 300, 64) 36928 _________________________________________________________________ block1_pool (MaxPooling2D) (None, 150, 150, 64) 0 _________________________________________________________________ block2_conv1 (Conv2D) (None, 150, 150, 128) 73856 _________________________________________________________________ block2_conv2 (Conv2D) (None, 150, 150, 128) 147584 _________________________________________________________________ block2_pool (MaxPooling2D) (None, 75, 75, 128) 0 _________________________________________________________________ block3_conv1 (Conv2D) (None, 75, 75, 256) 295168 _________________________________________________________________ block3_conv2 (Conv2D) (None, 75, 75, 256) 590080 _________________________________________________________________ block3_conv3 (Conv2D) (None, 75, 75, 256) 590080 _________________________________________________________________ block3_pool (MaxPooling2D) (None, 37, 37, 256) 0 _________________________________________________________________ block4_conv1 (Conv2D) (None, 37, 37, 512) 1180160 _________________________________________________________________ block4_conv2 (Conv2D) (None, 37, 37, 512) 2359808 _________________________________________________________________ block4_conv3 (Conv2D) (None, 37, 37, 512) 2359808 _________________________________________________________________ block4_pool (MaxPooling2D) (None, 18, 18, 512) 0 _________________________________________________________________ block5_conv1 (Conv2D) (None, 18, 18, 512) 2359808 _________________________________________________________________ block5_conv2 (Conv2D) (None, 18, 18, 512) 2359808 _________________________________________________________________ block5_conv3 (Conv2D) (None, 18, 18, 512) 2359808 _________________________________________________________________ block5_pool (MaxPooling2D) (None, 9, 9, 512) 0 _________________________________________________________________ flatten_1 (Flatten) (None, 41472) 0 _________________________________________________________________ dense_1 (Dense) (None, 1024) 42468352 _________________________________________________________________ dense_2 (Dense) (None, 10) 10250 ================================================================= Total params: 57,193,290 Trainable params: 57,193,290 不可训练参数: 0 _________________________________________________________________ |
或者,我们可能希望使用 VGG16 模型层,但仅训练模型的新层,而不更新 VGG16 层的权重。这将允许新输出层学习解释 VGG16 模型学习到的特征。
这可以通过在训练之前将加载的 VGG 模型中每个层的“trainable”属性设置为 False 来实现。例如
1 2 3 4 5 6 |
# 加载不带分类器层的模型 model = VGG16(include_top=False, input_shape=(300, 300, 3)) # 将加载的层标记为不可训练 for layer in model.layers: layer.trainable = False ... |
您可以选择性地选择哪些层是可训练的。
例如,也许您想重新训练模型深处的一些卷积层,但不是模型早期的一些层。例如
1 2 3 4 5 6 7 8 |
# 加载不带分类器层的模型 model = VGG16(include_top=False, input_shape=(300, 300, 3)) # 将某些层标记为不可训练 model.get_layer('block1_conv1').trainable = False model.get_layer('block1_conv2').trainable = False model.get_layer('block2_conv1').trainable = False model.get_layer('block2_conv2').trainable = False ... |
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
文章
书籍
- 深度学习, 2016.
论文
- 关于迁移学习的调查, 2010.
- 深度神经网络中的特征可迁移性如何?, 2014.
- CNN 特征现成可用:识别的惊人基线, 2014.
API
文章
- 迁移学习,维基百科.
- 迁移学习——机器学习的下一个前沿, 2017.
总结
在这篇文章中,您了解了在为计算机视觉应用开发卷积神经网络时如何使用迁移学习。
具体来说,你学到了:
- 迁移学习涉及使用在一个问题上训练的模型作为相关问题的起点。
- 迁移学习具有灵活性,允许将预训练模型直接用作特征提取预处理,并集成到全新的模型中。
- Keras 提供了对 ImageNet 图像识别任务中许多顶级模型的便捷访问,例如 VGG、Inception 和 ResNet。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
很棒的文章!谢谢。
谢谢,很高兴它帮到了你!
我可以训练模型之一,使用矩形输入图像吗?例如:input_shape=(100, 150, 3)
是的。
谢谢,这很有用。如何将预训练模型与图像生成器一起使用?
仅仅使用训练生成器训练一个典型的 cnn 模型并没有产生令人信服的结果。因此,我想利用预训练模型。
本教程提供了一个示例
https://machinelearning.org.cn/how-to-develop-a-convolutional-neural-network-to-classify-photos-of-dogs-and-cats/
https://blog.keras.org.cn/building-powerful-image-classification-models-using-very-little-data.html 请看这个
感谢这篇文章
我想使用预训练模型执行人脸识别,使用 facenet(inceptionv1)架构。
我可以这样做吗?在哪里可以找到这样的预训练模型??
你好 Nuha… 下面的内容可能对你有用
https://machinelearning.org.cn/how-to-perform-face-recognition-with-vggface2-convolutional-neural-network-in-keras/
此致,
很棒的帖子
谢谢。
我读过的关于迁移学习的最好的文章。
非常感谢!
你好。迁移学习是否仅用于图像识别?
我可以在其他应用程序中使用它吗?例如,我有一些使用机器学习分类方法的模型,但不是深度学习或神经网络,
我可以使用迁移学习获取一个模型的知识并将其用作另一个模型的特征吗?
如果可以,我该如何做?
非常感谢。
不,迁移学习在 NLP(例如文本数据)中也非常普遍。
如果您拥有足够的数据和预训练模型,也可以将其用于表格数据。
您好,Brownlee先生
在保存了从上面示例中获得的特征向量之后,是否可以直接将这些向量用作 LSTM 的输入?
当然可以。
照片字幕模型可以根据记忆做到这一点,例如
https://machinelearning.org.cn/develop-a-deep-learning-caption-generation-model-in-python/
写得好,非常有帮助!
只是一个小笔误——您可能希望在“预训练模型作为特征提取器”部分下的第一段代码中将 (300,300,3) 改为 (224,224,3)
model = VGG16(include_top=False, input_shape=(224, 224, 3))
谢谢,已修复!
你好,
我正在编写代码以将概念和提到的代码用于我的数据,数据是二元类,在加载图像后,当我单击 predict 时,我得到错误:Uncaught (in promise) Error: Error when checking: expected tfjs@0.13.5.2 flatten_1_input to have shape [null,7,7,512] but got array with shape [1,224,224,3]。
你能帮我解决这个问题吗?
抱歉,我没有遇到过这个问题。
也许尝试将您的代码和错误发布到stackoverflow?
在预训练模型之上构建的模型中使用卷积层是否有意义?我正在寻找此类示例,但只发现预训练模型之后的全连接层。如果我们能做到,我们应该如何去做?如果我们不能,为什么?
这可能有用,如果您想解释预训练模型学习到的特征,而又不通过调整其权重来弄乱它们。
我可能有一个例子,也许可以看看这里的一些图像分类教程
https://machinelearning.org.cn/start-here/#dlfcv
是的,有道理!
在构建更准确的迁移学习模型时,这是否还有意义?目前我正在使用 InceptionV3 作为预训练模型处理肺炎 Kaggle 数据集。与 imagenet 相比,数据集和类数量都比较小。重新训练 Inception 中的某些层是否是个好主意?
(我感觉在这种情况下,我应该只训练模型,然后根据结果进行调整,而不是过于理论化。)
我认为从预训练模型开始几乎总是最好的方法,并且应该尝试调整输出层或添加一些新层并调整它们。
嗨,Jason,
我计划将迁移学习用于 ECG 信号相关的分类项目。我正在考虑使用 Keras API 中的现有模型(因为很难找到信号相关的预训练模型)。
关于数据集,我只有 1000 个信号样本。因此,迁移学习问题现在缩小到“目标数据集小且与基础训练数据集不同”的问题。
您能否为我提供一些处理此问题的方法(书籍、研究论文)?
您网站的忠实粉丝。谢谢。
Theekshana。
也许您可以使用与时间序列分类问题相关的模型进行迁移学习?
嗨。
我们可以使用在图像数据集上训练的 vgg16 来处理 Pima-Indian Diabetes 数据集吗?
或者可以使用基于图像的预训练模型来处理非图像数据集吗?
一点也没有。
那么,通过这种方法,我们如何训练多个类别呢?
抱歉,文章错误,请忽略…
不客气。
您好,Brownlee先生
在最后一个代码(预训练模型作为特征提取器)中,特征向量保存在哪里?
我的意思是,我该如何使用特征向量?
例如,如果我有 3000 张图像,来自 10 个类别(每个类别有 300 张图像),我该如何获得每个图像的特征向量?
在你的最后一个代码中,如果我们有 3000 张图像作为网络输入,那么我们有 3000 个特征向量吗?如果是这样,我们如何访问这些向量?
谢谢你
您可以将图像通过模型以获得图像的特征向量。
您可以对所有图像重复此操作,并根据需要将向量数组保存到文件中。
你好,
我尝试拟合以下模型但出现错误,我不知道如何修复。您能帮帮我吗?
InvalidArgumentError 回溯(最近一次调用在最后)
in
1 # 训练
—-> 2 history=model.fit(X_train, y_train, batch_size=32, epochs=1,validation_data=(X_test,y_test),shuffle=False,verbose=2)
C:\Anaconda64\lib\site-packages\keras\engine\training.py in fit(self, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, validation_freq, max_queue_size, workers, use_multiprocessing, **kwargs)
1237 steps_per_epoch=steps_per_epoch,
1238 validation_steps=validation_steps,
-> 1239 validation_freq=validation_freq)
1240
1241 def evaluate(self,
C:\Anaconda64\lib\site-packages\keras\engine\training_arrays.py in fit_loop(model, fit_function, fit_inputs, out_labels, batch_size, epochs, verbose, callbacks, val_function, val_inputs, shuffle, initial_epoch, steps_per_epoch, validation_steps, validation_freq)
194 ins_batch[i] = ins_batch[i].toarray()
195
–> 196 outs = fit_function(ins_batch)
197 outs = to_list(outs)
198 for l, o in zip(out_labels, outs)
C:\Anaconda64\lib\site-packages\tensorflow_core\python\keras\backend.py in __call__(self, inputs)
3738 value = math_ops.cast(value, tensor.dtype)
3739 converted_inputs.append(value)
-> 3740 outputs = self._graph_fn(*converted_inputs)
3741
3742 # EagerTensor.numpy() will often make a copy to ensure memory safety.
C:\Anaconda64\lib\site-packages\tensorflow_core\python\eager\function.py in __call__(self, *args, **kwargs)
1079 TypeError: For invalid positional/keyword argument combinations.
1080 “””
-> 1081 return self._call_impl(args, kwargs)
1082
1083 def _call_impl(self, args, kwargs, cancellation_manager=None)
C:\Anaconda64\lib\site-packages\tensorflow_core\python\eager\function.py in _call_impl(self, args, kwargs, cancellation_manager)
1119 raise TypeError(“Keyword arguments {} unknown. Expected {}.”.format(
1120 list(kwargs.keys()), list(self._arg_keywords)))
-> 1121 return self._call_flat(args, self.captured_inputs, cancellation_manager)
1122
1123 def _filtered_call(self, args, kwargs)
C:\Anaconda64\lib\site-packages\tensorflow_core\python\eager\function.py in _call_flat(self, args, captured_inputs, cancellation_manager)
1222 if executing_eagerly
1223 flat_outputs = forward_function.call(
-> 1224 ctx, args, cancellation_manager=cancellation_manager)
1225 else
1226 gradient_name = self._delayed_rewrite_functions.register()
C:\Anaconda64\lib\site-packages\tensorflow_core\python\eager\function.py in call(self, ctx, args, cancellation_manager)
509 inputs=args,
510 attrs=(“executor_type”, executor_type, “config_proto”, config),
–> 511 ctx=ctx)
512 else
513 outputs = execute.execute_with_cancellation(
C:\Anaconda64\lib\site-packages\tensorflow_core\python\eager\execute.py in quick_execute(op_name, num_outputs, inputs, attrs, ctx, name)
65 else
66 message = e.message
—> 67 six.raise_from(core._status_to_exception(e.code, message), None)
68 except TypeError as e
69 keras_symbolic_tensors = [
C:\Anaconda64\lib\site-packages\six.py in raise_from(value, from_value)
InvalidArgumentError: Matrix size-incompatible: In[0]: [1,802816], In[1]: [25088,1024]
[[node dense_1/Relu (defined at C:\Anaconda64\lib\site-packages\tensorflow_core\python\framework\ops.py:1751) ]] [Op:__inference_keras_scratch_graph_1052]
Function call stack
keras_scratch_graph
我的模型是
加载不带分类器层的模型,不更改输入尺寸
modelVGG = VGG16(include_top=False, input_shape=(224, 224, 3))
# 添加新的分类器层
flat1 = Flatten()(modelVGG.outputs)
class1 = Dense(1024, activation=’relu’)(flat1)
output = Dense(49, activation=’sigmoid’)(class1)
# 定义新模型
model = Model(inputs=modelVGG.inputs, outputs=output)
# 将加载的层标记为不可训练
for layer in modelVGG.layers
layer.trainable = Falsee
打印(X_train.shape)
print(y_train.shape)
print(X_test.shape)
print(y_test.shape)
(1452, 224, 224, 3)
(1452, 49)
(435, 224, 224, 3)
(435, 49)
听到这个消息很抱歉,这可能会有帮助
https://machinelearning.org.cn/faq/single-faq/why-does-the-code-in-the-tutorial-not-work-for-me
很棒的文章!
感谢您的时间。
谢谢!
你真是个伟大的支持者
谢谢!
我可以使用预训练模型和 fer2013 数据集进行面部情绪识别吗?
我不知道,也许试试?
可以将多个预训练模型串联起来用于图像分类中的特征提取吗?有意义吗?
也许吧。试试看,然后比较一下所得模型与单个模型的技能?
感谢 Jason 又一篇好文!
顺便说一句,我很有把握那是罗威纳犬,而不是杜宾犬。我使用了一个 NASNetMobile 模型,它识别对了。
感谢分享!我对狗不了解:)
很棒的文章 Jason!
我想知道是否可以将我自己的预训练 Keras 模型(具有 3 个输出类别)用于迁移学习。我的意思是,我使用 VGG16 训练了一个新的 3 个输出的模型,现在我想再添加一个类别,但不是从头开始向 VGG16 添加四个输出,我认为也许我可以使用已经训练好的 3 个类别的模型。
如果可以,你能否提供一些指导?
提前感谢!
谢谢!
是的。像上面一样将模型加载为特征提取器,然后添加一个新的输出层,并且只训练新的输出层。
我在这里给出例子
https://machinelearning.org.cn/how-to-develop-a-convolutional-neural-network-to-classify-photos-of-dogs-and-cats/
哇,感谢您的快速回复!
阅读了您给我的例子,我认为我明白了
1. 加载我已有的基线模型(VGG16+3_output_classes.h5 模型)
2. 将加载的层标记为不可训练
3. 添加具有 4 个输出类别的新分类器层
4. 定义新模型(4 个输出类别)
5. 编译新模型
6. 拟合新模型(已准备好图像集)
7. 享受吧 :)
这似乎非常简单易于实现,我一定会尝试!
再次感谢 Jason!
是的。
告诉我进展如何。
model = VGG16(include_top=False, weights=’imagenet’, input_shape=(150,150,3))
“model.outputs”是一个张量列表,是 VGG16 模型所有输出张量的列表。
而“model.output”是单个输出张量。
当我使用 model.outputs 作为我自己的层的输入时
conv1 = Conv2D(32, kernel_size=4, activation=’relu’) (model.outputs)
我收到一个错误:
“int() argument must be a string, a bytes-like object or a number, not ‘TensorShape’”
通过使用 model.output 而不是 model.outputs,我解决了这个问题。
你能解释一下为什么吗??
我用 model.output 而不是 model.outputs 是错的吗??
我期望最后一个层的输出和模型的输出是相同的。
也许检查一下代码?
嗨
我正在尝试使用 VGG16,但遇到一个问题。如果您能帮助我,我将非常感激。
这是架构的代码
vgg_conv = VGG16(weights=’imagenet’, include_top=False, input_shape=(765, 415, 3))
for layer in vgg_conv.layers[:-4]
layer.trainable = False
for layer in vgg_conv.layers
print(layer, layer.trainable)
model = models.Sequential()
model.add(vgg_conv)
model.add(layers.Flatten())
model.add(layers.Dense(128, activation=’relu’))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(15))
model.add(layers.Reshape((5,3)))
model.add(layers.Activation(‘sigmoid’))
当我调用 fit 方法时,我遇到以下错误:
ValueError: Error when checking input: expected vgg16_input to have 4 dimensions, but got array with shape (559, 1, 760, 415, 3)
我搜索过,但找不到合适的解决方案,我是这个领域的初学者,请 accordingly 指导我。
也许可以使用 summary() 函数,并使用其输出调试通过模型的数据转换。
你好
所以,我想将这些提取的特征传递给 SVM 分类器。
如何做到这一点!
谢谢
我在这里给出了一个例子
https://machinelearning.org.cn/how-to-develop-a-face-recognition-system-using-facenet-in-keras-and-an-svm-classifier/
感谢您的时间 Jason
不客气。
在尝试文章的最后一部分(微调)时,我遇到了以下错误:
InvalidArgumentError: Matrix size-incompatible: In[0]: [1,16384], In[1]: [512,10]
[[{{node Final_5/BiasAdd}}]]
我的代码
# 训练vgg16模型的示例
from keras.applications.vgg16 import VGG16
from keras.models import Model
from keras.layers import Dense
from keras.layers import Flatten
# 加载不带分类器层的模型
model = VGG16(include_top=False, input_shape=(32, 32, 3))
# 添加新的分类器层
flat1 = Flatten()(model.outputs)
class1 = Dense(1024, activation=’relu’, name=’Pre-final’)(flat1)
output = Dense(10, activation=’softmax’, name=’Final’)(class1)
# 定义新模型
model = Model(inputs=model.inputs, outputs=output)
# 总结
model.summary()
`for layer in model.layers`
layer.trainable = False
# 将某些层标记为不可训练
model.get_layer(‘Pre-final’).trainable = True
model.get_layer(‘Final’).trainable = True
model.summary()
opt = keras.optimizers.RMSprop(0.0001, decay=1e-6)
# Let’s train the model using RMSprop
model.compile(loss='categorical_crossentropy',
optimizer=opt,
metrics=['accuracy'])
x_train = x_train.astype(‘float32’)
x_test = x_test.astype(‘float32’)
x_train /= 255
x_test /= 255
model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, validation_data=(x_test, y_test), shuffle=True)
我错过了什么?
很抱歉听到这个消息,这可能会有所帮助。
https://machinelearning.org.cn/faq/single-faq/why-does-the-code-in-the-tutorial-not-work-for-me
嗨 Jason,我卡在添加我自己的层,移除最后一层密集层的地方。在那里我遇到了维度错误,无法修复。你能帮忙吗?
我在本教程中提供了示例。
https://machinelearning.org.cn/how-to-develop-a-convolutional-neural-network-to-classify-photos-of-dogs-and-cats/
精彩的文章。已收藏!
谢谢!
嗨,Jason,
我之前在理解 TensorFlow 的迁移学习时遇到困难,但阅读了这些材料后,一切都变得清晰了。
真的很感谢你的工作。
谢谢,很高兴听到这个!
再次感谢 Jason 写的这篇精彩文章。你总是让它变得简单 ????????
不客气!
嘿,Jason,感谢您的文章,非常有趣。顺便问您一个问题。所以,我很清楚如何使用预训练模型进行迁移学习,但在我想到的特定应用中可能有点复杂。我想构建一个解码器,它接收预训练网络(如 VGG)生成的高层表示,并重构初始图像。我认为要做到这一点需要一个非常复杂和难以训练的网络,所以我想知道这些预训练网络是否也有与之关联的预训练解码器。我希望我说清楚了,感谢您可能的答复。
是的,您可以使用 Unet 或 GAN 来实现。
https://machinelearning.org.cn/start-here/#gans
请告诉我如何获得特征向量的平均值。
收集您的特征向量并使用 mean() 函数。
https://numpy.com.cn/doc/stable/reference/generated/numpy.mean.html
在循环加载所有特征向量的 pickle 文件时,我收到错误“找不到特征向量文件”。我确定我已经提供了目录的绝对路径。但是,当单独加载一个特征向量 pickle 文件时,我可以打开它。请告诉我如何通过循环加载所有特征向量。
听到这个消息我很难过。
尝试从命令行运行代码。
https://machinelearning.org.cn/faq/single-faq/how-do-i-run-a-script-from-the-command-line
嗨,Jason先生,
请建议如何对灰度图像使用迁移学习。如何修改以适应黑白灰度图像?
定义输入以接受具有您数据的首选大小和通道的图像。
感谢您的快速回复。
值得注意的是,所有迁移学习模型(如 VGG、ReNet 等)都是为 RGB 开发的,即 3 个通道。然而,我想将这些模型用于灰度图像,即通道数为 1。请建议修改或如何使用迁移学习来处理黑白情况的基于 RGB 的模型。
调整大小不是问题,它是可配置的。
上面的教程向您展示了如何为 vgg 模型定义新的输入形状,包括通道。以此为起点,并将其更改为 1 个通道。
Jason 先生,您好!
根据您的指示,我将通道数改为 1,然后出现以下错误:
ValueError: The input must have 3 channels; got
input_shape=(224, 224, 1)
但是,当我只保留 3 个通道时,我得到以下错误:
InvalidArgumentError: input depth must be evenly divisible by filter depth: 1 vs 3
[[node model_4/block1_conv1/Conv2D (defined at :22) ]] [Op:__inference_train_function_48474]
请帮助解决错误。
抱歉,我无法为您调试。您必须自己开发解决方案。
您好,我正在尝试检测 10 种不同的植物病,并且我的数据量很小。您是否建议我在此问题中使用迁移学习以提高准确性?
提前感谢!
是的,我建议使用迁移学习,并可能使用图像数据增强。
感谢您的回复!
不客气。
您好亲爱的 Jason,感谢您的精彩课程!
我试图将迁移学习应用于“ssd_mobilenet_v1_coco_2017”预训练对象检测模型,并试图修改大型模型,通过减少层数和滤波器,但没有成功。
虽然我努力了,但我甚至无法将 3 色通道减少到 1 个。
您能否建议一种方法来实现这一点?
谢谢! !
很抱歉听到这个消息,我无法凭直觉知道问题出在哪里。也许将您的代码和错误消息发布到 stackoverflow。
Jason 博士您好!
非常感谢您的精彩文章!
我正在尝试应用迁移学习,但不是使用 VGG16,而是想使用我保存为 h5 文件的先前训练的 CIFAR10 模型。具体来说,我想使用迁移学习让模型能够对我的车辆数据集(20 个类别)进行分类。
请问如何使用 h5 文件作为预训练模型?我搜索过但一无所获,因为示例使用的是 Keras 的预训练模型,例如 keras.applications 中的 VGG16。我以为我遇到的问题是由于 Dense 层的输出尺寸不同。我尝试过 model.pop,但似乎也不起作用。如果您能提供一些这方面的指导,将非常有帮助,谢谢!
听起来很棒。
是的,加载模型,根据需要替换输入和/或输出层,用新层替换它们,在您的新数据集上微调这些新层,然后就可以开始了!
我会试试的。非常感谢!
不客气。
请帮帮我,我的数据集包含 38 个类别
我使用的数据集是(新植物病数据集)
train_path = Path(‘C:\\Users\\User\AnacondaProjects\\Plant Disease Detection and Classification ( Final year Project )\\New Plant Diseases Dataset\\train’)
test_path = Path(‘C:\\Users\\User\AnacondaProjects\\Plant Disease Detection and Classification ( Final year Project )\\New Plant Diseases Dataset\\test’)
valid_path = Path(‘C:\\Users\\User\AnacondaProjects\\Plant Disease Detection and Classification ( Final year Project )\\New Plant Diseases Dataset\\valid’)
print(train_path)
print(valid_path)
print(test_path)
train_batches = ImageDataGenerator().flow_from_directory(train_path, target_size=(224,224), classes=[‘Apple___Apple_scab’,’Apple___Black_rot’, ‘Apple___Cedar_apple_rust’, ‘Apple___healthy’, ‘Blueberry___healthy’, ‘Cherry_(including_sour)___healthy’, ‘Cherry_(including_sour)___Powdery_mildew’, ‘Corn_(maize)___Cercospora_leaf_spot Gray_leaf_spot’, ‘Corn_(maize)___Common_rust_’, ‘Corn_(maize)___healthy’, ‘Corn_(maize)___Northern_Leaf_Blight’, ‘Grape___Black_rot’, ‘Grape___Esca_(Black_Measles)’, ‘Grape___healthy’, ‘Grape___Leaf_blight_(Isariopsis_Leaf_Spot)’, ‘Orange___Haunglongbing_(Citrus_greening)’,’Peach___Bacterial_spot’, ‘Peach___healthy’, ‘Pepper,_bell___Bacterial_spot’, ‘Pepper,_bell___healthy’, ‘Potato___Early_blight’, ‘Potato___healthy’, ‘Potato___Late_blight’, ‘Raspberry___healthy’, ‘Soybean___healthy’, ‘Squash___Powdery_mildew’, ‘Strawberry___healthy’, ‘Strawberry___Leaf_scorch’, ‘Tomato___Bacterial_spot’, ‘Tomato___Early_blight’, ‘Tomato___healthy’, ‘Tomato___Late_blight’, ‘Tomato___Leaf_Mold’, ‘Tomato___Septoria_leaf_spot’, ‘Tomato___Spider_mites Two-spotted_spider_mite’, ‘Tomato___Target_Spot’, ‘Tomato___Tomato_mosaic_virus’, ‘Tomato___Tomato_Yellow_Leaf_Curl_Virus’], batch_size=10)
test_batches = ImageDataGenerator().flow_from_directory(test_path, target_size=(224,224), classes=[‘Apple___Apple_scab’,’Apple___Black_rot’, ‘Apple___Cedar_apple_rust’, ‘Apple___healthy’, ‘Blueberry___healthy’, ‘Cherry_(including_sour)___healthy’, ‘Cherry_(including_sour)___Powdery_mildew’, ‘Corn_(maize)___Cercospora_leaf_spot Gray_leaf_spot’, ‘Corn_(maize)___Common_rust_’, ‘Corn_(maize)___healthy’, ‘Corn_(maize)___Northern_Leaf_Blight’, ‘Grape___Black_rot’, ‘Grape___Esca_(Black_Measles)’, ‘Grape___healthy’, ‘Grape___Leaf_blight_(Isariopsis_Leaf_Spot)’, ‘Orange___Haunglongbing_(Citrus_greening)’,’Peach___Bacterial_spot’, ‘Peach___healthy’, ‘Pepper,_bell___Bacterial_spot’, ‘Pepper,_bell___healthy’, ‘Potato___Early_blight’, ‘Potato___healthy’, ‘Potato___Late_blight’, ‘Raspberry___healthy’, ‘Soybean___healthy’, ‘Squash___Powdery_mildew’, ‘Strawberry___healthy’, ‘Strawberry___Leaf_scorch’, ‘Tomato___Bacterial_spot’, ‘Tomato___Early_blight’, ‘Tomato___healthy’, ‘Tomato___Late_blight’, ‘Tomato___Leaf_Mold’, ‘Tomato___Septoria_leaf_spot’, ‘Tomato___Spider_mites Two-spotted_spider_mite’, ‘Tomato___Target_Spot’, ‘Tomato___Tomato_mosaic_virus’, ‘Tomato___Tomato_Yellow_Leaf_Curl_Virus’], batch_size=40)
valid_batches = ImageDataGenerator().flow_from_directory(valid_path, target_size=(224,224), classes=[‘Apple___Apple_scab’,’Apple___Black_rot’, ‘Apple___Cedar_apple_rust’, ‘Apple___healthy’, ‘Blueberry___healthy’, ‘Cherry_(including_sour)___healthy’, ‘Cherry_(including_sour)___Powdery_mildew’, ‘Corn_(maize)___Cercospora_leaf_spot Gray_leaf_spot’, ‘Corn_(maize)___Common_rust_’, ‘Corn_(maize)___healthy’, ‘Corn_(maize)___Northern_Leaf_Blight’, ‘Grape___Black_rot’, ‘Grape___Esca_(Black_Measles)’, ‘Grape___healthy’, ‘Grape___Leaf_blight_(Isariopsis_Leaf_Spot)’, ‘Orange___Haunglongbing_(Citrus_greening)’,’Peach___Bacterial_spot’, ‘Peach___healthy’, ‘Pepper,_bell___Bacterial_spot’, ‘Pepper,_bell___healthy’, ‘Potato___Early_blight’, ‘Potato___healthy’, ‘Potato___Late_blight’, ‘Raspberry___healthy’, ‘Soybean___healthy’, ‘Squash___Powdery_mildew’, ‘Strawberry___healthy’, ‘Strawberry___Leaf_scorch’, ‘Tomato___Bacterial_spot’, ‘Tomato___Early_blight’, ‘Tomato___healthy’, ‘Tomato___Late_blight’, ‘Tomato___Leaf_Mold’, ‘Tomato___Septoria_leaf_spot’, ‘Tomato___Spider_mites Two-spotted_spider_mite’, ‘Tomato___Target_Spot’, ‘Tomato___Tomato_mosaic_virus’, ‘Tomato___Tomato_Yellow_Leaf_Curl_Virus’], batch_size=10)
import keras
vgg16_model = keras.applications.vgg16.VGG16(include_top= True, weights=’imagenet’, input_tensor=None, input_shape=None, pooling=None, classes=38)
vgg16_model = Sequential()
for layer in model.layers[:-1]
vgg16_model.add(layer)
print(‘ model copied’)
for layer in vgg16_model.layers
layer.trainable = False
vgg16_model.add(Dense(38, activation = ‘softmax’))
print(‘add last layer’)
model.compile(Adam(lr=.0001),loss=’categorical_crossentropy’,metrics=[“accuracy”])
print(‘checked’)
train_num = train_batches.samples
valid_num = valid_batches.samples
# 检查点
from keras.callbacks import ModelCheckpoint
weightpath = “best_weights_9.hdf5”
checkpoint = ModelCheckpoint(weightpath, monitor=’val_accuracy ‘, verbose=1, save_best_only=True, save_weights_only=True, mode=’max’)
callbacks_list = [checkpoint]
在执行以下行时,它会显示最后的错误:
model.fit_generator(train_batches, steps_per_epoch=train_num//batch_size,
validation_data = valid_batches, validation_steps=valid_num//batch_size,
epochs = 25 ,callbacks=callbacks_list)
执行时
我尝试微调 VGG16 以对 38 种植物病进行分类。
这是我在这里回答的一个常见问题
https://machinelearning.org.cn/faq/single-faq/can-you-read-review-or-debug-my-code
你好,先生。
此问题不在常见问题解答中。
我直接链接到了如何调试代码的建议。
很棒
谢谢!
嗨,Jason,
这篇帖子太棒了。非常感谢您提供的详细信息。
我有一个问题,请。你说:“然后我们可以使用 Keras 函数 API 在 VGG16 模型的最后一个池化层之后添加一个新的 Flatten 层,然后定义一个新的分类器模型,其中包含一个 Dense 全连接层和一个输出层,用于预测 10 个类别的概率。”
为什么我们要添加一个 flatten 层和一个 dense 层?我们移除了模型的最后一个 Dense 层。那么,为什么我还要添加一个 flatten 层呢?
此致,
Mohamad
谢谢!
池化层的输出是一堆 2D 滤波器图,我们需要将它们展平成一个向量才能输入到密集层进行分类。
嗨,Jason,
非常感谢您的有用帖子。
我想使用 Vgg16 模型来分类组织学图像,我的数据有 10000 张图像,每张图像大小为 768*768。我想对我的数据进行归一化,您认为使用数据缩放还是批量归一化?
以及如何在迁移学习模型中使用批量归一化?
你好 Jason 先生
感谢您出色的文章。
我有一个包含 4000 张图像的数据集。我想从这些图像中提取特征。我的意思是为每张图像提取一个特征向量。我使用了您在“预训练模型作为特征提取器预处理程序”中的代码,对于一张图像,它运行得很好很快。
当我将代码更改为提取 50 张图像的特征向量时,运行大约需要 5 个小时。
您能否指导我如何为大型数据集实现此代码并保存特征向量?我想将这些特征向量用于训练 SVM 分类器。
谢谢你
谢谢!
也许可以尝试在 GPU 上运行,即使是在 AWS 上。
也许将您的特征向量保存到文件,以便您可以稍后重复使用它们。
非常感谢 Jason 先生
但是您能否指导我如何将我的代码运行在 GPU 上?
您必须将 tensorflow 配置为使用您的 GPU。抱歉,我没有关于如何执行此操作的教程,我建议您查阅 tensorflow 文档。
你好,
我的模型已经为 100 个类别训练过了,我想添加一个新类别,而且我不想从头开始训练我的模型,但我想训练并使用新的类别和标签,并将其嵌入到现有的权重文件中,我们该如何做到这一点?
从现有权重开始,将输出层更改为包含一个额外的类别,用新示例在所有数据上重新训练,但使用小的学习率,并且也许只允许输出层权重发生变化——冻结其余部分。
谢谢 Jason,我一定会试试。
不客气。
嗨,Jason,
感谢这篇很棒的教程。
对于“预训练模型作为特征提取器预处理程序”部分,您执行了
model = VGG16()
model.layers.pop()
我能否这样做
model = VGG16(include_top = False) 而不是省略 .layers.pop()?
也许可以尝试一下并进行比较?
您好。非常感谢!
我尝试运行您完全相同的代码
# 训练vgg16模型的示例
from keras.applications.vgg16 import VGG16
from keras.models import Model
from keras.layers import Dense
from keras.layers import Flatten
# 加载不带分类器层的模型
model = VGG16(include_top=False, input_shape=(300, 300, 3))
# 添加新的分类器层
flat1 = Flatten()(model.outputs)
class1 = Dense(1024, activation=’relu’)(flat1)
output = Dense(10, activation=’softmax’)(class1)
# 定义新模型
model = Model(inputs=model.inputs, outputs=output)
# 总结
model.summary()
# …
但是收到了这个错误
AttributeError: ‘list’ object has no attribute ‘shape’
这是什么问题?请帮帮我!
听到这个消息很抱歉,这可能会有帮助
https://machinelearning.org.cn/faq/single-faq/why-does-the-code-in-the-tutorial-not-work-for-me
嗨,Jason,
很棒的教程!
我想知道您是否计划发布其他关于迁移学习的教程,例如“神经风格迁移”,我认为这是一个非常有趣且有前景的应用,但是,使用Keras API。在其他方法中,他们直接使用TensorFlow和“eager_execution”,我认为这比单独使用Keras来说不太清晰。当然,因为我非常欣赏您教授AI的方法。
谢谢你
JG
感谢您的建议。
Jason,这篇文章很棒!
您能否推荐一些关于设计预训练模型(如ResNet50)的头部(head)的最佳实践的文章?谢谢!
感谢您的建议!
你好
关于预训练模型作为模型中的特征提取器
在行 class1 = Dense(1024, activation=’relu’)(flat1)
为什么是1024而不是512或2048?
谢谢
任意值。尝试不同的值,看看对您来说什么效果最好。
关于预训练模型作为模型中的特征提取器
在行 class1 = Dense(1024, activation=’relu’)(flat1)
为什么是1024而不是512或2048?
谢谢
任意值。尝试不同的值,看看对您来说什么效果最好。
________________________________________________
没有特定的最佳价格方法吗?
那么如何说添加全连接层呢?
谢谢
通过试错来找到最适合您特定数据集的方法。
https://machinelearning.org.cn/faq/single-faq/how-many-layers-and-nodes-do-i-need-in-my-neural-network
谢谢
您可以在一个名为Pocket AutoML的Android应用程序中尝试迁移学习来进行图像分类,而无需编写任何代码。它直接在您的手机上训练模型,而无需将您的照片发送到某个“云端”,因此它甚至可以离线工作。
感谢分享。
感谢您的这篇文章,
我想做一个图像推荐系统
我将使用vgg16提取所有图像的特征
然后为查询图像进行操作
然后我将使用余弦相似度来查找相似的图像。
在这种方法中,
我想问如何评估我的“性能指标”?
您有什么建议吗?
先谢谢您了。
也许选择一个最能体现您对模型性能看重的指标。
先生,
该教程只考虑了一张图片——狗的图片。
如果我们想将多张图片输入此模型。
如何做到?
如何使用此模型进行多类分类或特征提取?
代码相同。批量或逐个加载多张图片并进行预测。
这确实是您又一篇精彩的文章。简洁明了。
关于预训练网络的一个快速问题:正如您所说,有许多这样的预训练模型,但应该选择哪一个?虽然您说需要进行实验来找出答案,但我不知道是否可以限制实验次数来选择用相似数据集训练的预训练模型。例如,我试图训练一个模型来预测股票价格变动(包括每日价格变动、交易量、移动平均线等)。这些股票图表不像狗和猫。它们不是自然的,边界应该更容易检测。您知道哪个预训练模型更适合我的目的吗?
非常感谢!
谢谢!
也许可以从ImageNet上的最佳模型,或者最新发布的模型开始。
股价是不可预测的
https://machinelearning.org.cn/faq/single-faq/can-you-help-me-with-machine-learning-for-finance-or-the-stock-market
你好,
非常感谢
我有一个问题
我已经用VGG16训练了一个模型,并保存了权重,现在我想使用迁移学习来微调这个模型上的另一个数据集,以获得准确率?
请帮助我
不客气。
是的,您可以加载权重,设置一个小的学习率,然后进一步训练模型。
您能给我一个代码示例吗?我是深度学习的新手。
我不知道如何在代码中做到这一点(微调和加载已保存的权重以在新数据集上训练)。
这是我的完整问题
https://stackoverflow.com/questions/65136547/how-can-i-use-transfer-learning-to-finetuning-another-dataset
非常感谢 Jason 先生的帮助和时间。
是的,您可以在博客上找到许多关于保存模型和迁移学习的示例,您可以使用页面顶部的搜索框。
训练过程中为什么内核会崩溃?
您是指梯度消失和梯度爆炸吗?
信息量很大且简洁的文章。
它阐述了迁移学习的基本思想,这些思想在Keras书籍中很难找到。
谢谢..
谢谢。
嗨,Jason,
感谢您一如既往地发布精彩的文章。您是否有关于使用全连接层进行回归任务的迁移学习实现的建议?我对输入维度不匹配以及如何在这些网络中实现它有一些问题。
谢谢!
不客气。
您可以调整此示例。
https://machinelearning.org.cn/how-to-improve-performance-with-transfer-learning-for-deep-learning-neural-networks/
先生,您能提供用于微调预训练 AlexNet 所有层的代码吗?
感谢您的建议,也许将来会考虑。
一如既往的精彩教程,谢谢!
关于 input_shape 似乎有些困惑,我也有点困惑。
在使用预训练权重 (imagenet) 时,是否可以在创建模型时更改 input_shape 并仍使用预训练权重?还是在使用不同的 input_shape 时必须重新训练网络?
根据我对 CNN(正在进行的)的了解,input_shape 是静态的,因此必须从头开始重新训练权重。也许我误解了?
是的,您可以重新定义 input_shape,而且不必重新训练模型权重(除非您想这样做)。
例如,当您在 Keras 中加载预训练模型时,您可以指定是否要保留旧的输入层/形状,还是定义一个新的。
非常好的帖子。您能帮我说明一下如何指定新的输入层并移除旧的输入层吗?
尝试搜索 Keras Functional API。那应该是最简单的方法。
先生,我的图表没有显示准确率和损失值,只显示了训练准确率和验证损失在两个图表中。
acc += history_fine.history[‘accuracy’]
val_acc += history_fine.history[‘val_accuracy’]
loss += history_fine.history[‘loss’]
val_loss += history_fine.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.ylim([0.8, 1])
plt.plot([initial_epochs-1,initial_epochs-1],
plt.ylim(), label=’Start Fine Tuning’)
plt.legend(loc=’lower right’)
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.ylim([0, 1.0])
plt.plot([initial_epochs-1,initial_epochs-1],
plt.ylim(), label=’Start Fine Tuning’)
plt.legend(loc=’upper right’)
plt.title(‘Training and Validation Loss’)
plt.xlabel('epoch')
plt.show()
或许这些提示会有帮助
https://machinelearning.org.cn/faq/single-faq/can-you-read-review-or-debug-my-code
嗨,Jason,
希望您一切安好。这是一个很棒的教程,谢谢!
请问,如何为非 ImageNet 模型使用 decode_predictions()?有没有其他方法可以打印预训练模型的标签?
我有一个预训练的分类模型,用于对 14 种疾病的图像进行分类。我想将此模型用于我的数据集,但是,我无法打印疾病名称。
提前感谢您!
我认为每个内置模型都会有一个类似的功能。
Jason,您是否有使用预训练模型进行时间序列的示例?
我认为没有,抱歉。
感谢这篇文章。它解答了我关于使用预训练网络的好多问题。
不客气!
嗨,Jason。
如何为迁移学习选择预训练模型?例如,如果任务是根据疾病对植物叶子进行分类,那么在开始时不是需要对模型进行相似数据的训练,才能使用预训练模型的权重吗?
谢谢!
也许需要一点试错。理想情况下,数据集之间应该存在某种关系。
谢谢 Jason。
你好,
我想在不同的图像通道上使用迁移学习,例如 (224,224,9),这可能吗?
谢谢你。
也许可以尝试一下。
您好,我将把迁移学习应用于强化学习。但是,目标域比源域多一个特征。我该如何将迁移学习应用于此域,同时添加一个新特征?如果您有示例,我将不胜感激。
也许可以移除模型中的输入层,添加一个新的输入层,然后仅在新的数据集上重新训练那些权重。
感谢您的回复,请问您是否有任何示例说明如何移除输入层并更改为新的输入层,同时保留其余层?
而且我认为,如果我训练所有模型权重,包括那个新特征,我会犯更少的错误。考虑到我训练了所有权重,但从已经训练好的权重(在其余特征上)开始,模型仍然会更有效率,对吧?
是的,上述教程正是这样做的。
Jason,感谢这篇文章以及您发布的每一篇 ML 文章!我觉得它们非常有帮助。
我需要构建一个视频分类模型。我认为迁移学习会很方便,因为我的视频标签不在公共数据库中(运动和健身相关)。我该如何修改本文中的代码以适应我的情况?您知道有什么示例可供我参考吗?提前感谢您的时间和专业知识。
总的来说,当您找到一个好模型并想将其用于您的用途时,您需要确保特征与您的数据集匹配。例如,您的输入数据是否是模型期望的格式?对于示例,您需要考虑如何进行分类。希望这可以帮助您入门:https://machinelearning.org.cn/multi-label-classification-with-deep-learning/
先生,您能否提供 AlexNet 迁移学习的代码?
我认为这与这里的示例完全相同。如果您从 AlexNet 获取了训练好的模型,只需替换上面的 VGG 部分即可。
谢谢,非常有帮助。
from keras.applications.vgg16 import VGG16
应该是 from keras_applications.vgg16 import VGG16
谢谢。这取决于您的库版本。对于 TensorFlow 的 Keras,您甚至需要 “from tf.keras.applications import VGG16”。
你好 Adrian,
我有一个非图像分类项目,我希望在其上应用迁移学习,并且已经构建了自己的预训练分类器。您知道有什么参考资料详细说明如何使用您自己的预训练模型进行迁移学习吗?我遇到的所有在线示例都使用了 Keras 包中已包含的预训练模型。
这应该很简单。您已经有了自己的模型,只需参考那些示例,说明如何复用您自己的模型而不是别人的。概念上,这意味着要重建训练好的模型架构,移除最后一两层,将现有层设置为不可训练,然后添加其他层并用新数据集进行训练。因为来自训练模型的层被标记为不可训练,所以它会保留其功能。新层将被训练(即,与整个模型相比要小得多)以理解您的特定用例。
很棒!愿上帝保佑您。非常感谢
感谢您的反馈 Mashim!
您好!我正在尝试创建一个 CNN 模型,该模型可以区分伪造签名和真实签名。您知道在哪里可以找到可用于此目的的预训练模型吗?
嗨 Joseph… 以下资源可能是使用预训练模型的迁移学习的一个很好的起点。
https://link.springer.com/chapter/10.1007/978-981-16-6887-6_13
在最后一种情况,“预训练模型作为模型中的特征提取器”,为什么图像没有像其他情况那样使用 preprocess_input() 进行预处理?
# 加载不带分类器层的模型
model = VGG16(include_top=False, input_shape=(300, 300, 3))
在上面的代码行中,input_shape=(300, 300, 3),但 VGG16 模型需要 input_shape 为 (224,224,3)。那么,它是如何工作的?
你好 Gaurav…你应用了代码列表吗?如果应用了,遇到了什么问题?
你好 James。我没有遇到任何问题。代码运行正常。但我有一个疑问。
在最后的示例中,我们没有像之前的示例那样对输入图像进行预处理(以获得形状为 (224,224,3) 的图像)。为什么是这样?
你好 Gaurave…虽然我无法评论你的具体模型配置,但我建议你确保你理解各个层的影响以及相关的技术,以确保在整个模型处理阶段保持图像形状。
https://machinelearning.org.cn/how-to-visualize-filters-and-feature-maps-in-convolutional-neural-networks/
你好,
也许我来得太晚了,但我刚接触深度学习领域,我想知道是否可以为自定义的预训练模型执行迁移学习?并在此基础上构建新模型?例如:我有一个由我的团队构建的自定义模型,该模型在其自己的自定义数据集上进行了训练。现在我想使用该模型及其权重作为基础模型,并在该模型之上构建一个新模型(具有相似的问题陈述)。我们能否在此处使用具有自定义预训练模型的迁移学习,还是仅限于与 VGG、Mobilenet、Resnet 等著名预训练模型一起使用?
你好 C…你可能会发现以下内容很有趣
https://machinelearning.org.cn/how-to-improve-performance-with-transfer-learning-for-deep-learning-neural-networks/
你好 Jason。我看到你很有耐心。请再忍耐一下。
我想从同一组输入图像中推导出两个预测(例如,人数和天气状况——晴朗、多云、下雨)。我认为这需要两个单独的层,它们从预训练模型的倒数第二层分叉出来?放在最后一个 Flatten 层之后,类似这样的?
class1 = Dense(1024, activation=’relu’)(flat1)
output1 = Dense(10, activation=’softmax’)(class1) # 将预测人数
class2 = Dense(1024, activation=’relu’)(flat1)
output2 = Dense(3, activation=’softmax’)(class1) # 天气状况
# 定义新模型
model = Model(inputs=model.inputs, outputs=[output1, output2]) # 我将不得不包含两组
# 目标进行拟合……
??
感谢您的教育
你好 Eduardo…理论上,我没有看到你的算法有任何问题。你实现它了吗?请继续,让我们知道你的发现和其他任何具体问题,以便我们能更好地帮助你。
上面的代码中的第四行应该是
output2 = Dense(3, activation = ‘softmax’)(class2)
感谢您的反馈 Eduardo!
迁移学习概念的最佳解释。谢谢。
嗨 Jason Brownlee!
首先,感谢您发布的所有精彩帖子,它们对人工智能社区都非常有帮助 🙂
我正在使用您的方法作为特征提取预处理器。我的目标是提取一个小医学图像数据集(n=76)的特征(nx4096,使用 VGG16),然后,在降维后,将它们与分类器中的其他特征(如临床变量)结合起来。
我的问题是,当我在 GPU 和 CPU 上运行时,我得到的特征非常不同。代码、图像……一切都相同,唯一的区别是
-在 GPU 环境中:keras 2.3.1(使用 tensorflow 后端),tensorflow 2.2.0
通过使用这些库,我可以完全运行您编写的代码。
– 在 CPU 中:keras 2.4.3 和 tensorflow 2.3.0。
使用这些库,我必须更改一些导入(以防止某些错误消息),例如“from tensorflow.keras.utils import load_img”而不是“from keras.preprocessing.image import load_img”。
在这两种情况下,我都检查了 preprocess_input 之后的图像,并且 .npy 文件完全相同。
您能告诉我这是怎么回事吗?
非常感谢您的帮助!!
一切顺利。