你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

使用 Transfer Learning 生成自己的映像分类器

目录

总结

imageimageimageimage

上述图像是本教程第二部分中使用的测试映像。 任务是通过修改现有分类器模型(基模型)来训练分类器,该分类器可以在示例羊和狼) 中区分不同类别的图像 (。 在这里,我们使用在 ImageNet corpus 上训练的ResNet_18模型。 在几秒钟内,我们每类只训练 15 个图像,并正确预测所有 10 个测试图像, (记下 盐) 的几个粒度

以下是迁移学习教程的主要资源:

配方 TransferLearning.pyTransferLearning_Extended.py (请参阅 示例/图像/TransferLearning) 。
预先训练的模型 作为转移学习的基础模型,我们使用 预先训练的ResNet_18模型
数据 具有 102 个类别的花朵数据集以及羊和狼的示例图像 (请参阅设置) 。
如何运行 请按照以下说明操作。

设置

若要运行此示例中的代码,需要CNTK Python 环境, (在此处查看设置帮助) 。

若要下载所需的数据和预先训练的模型,请运行以下命令,形成 Examples/Image/TransferLearning 文件夹:

python install_data_and_model.py

运行示例

imageimageimageimage

在本部分中,我们将为 Flowers 数据集生成分类器。 数据集由牛津大学视觉几何图形组创建,用于图像分类任务。 它由英国共有的102种不同类别的花卉组成,包含大约8000张图像,这些图像分为三组6000张和1000张图像。 有关详细信息 ,请参阅 VGG 主页

在 Flowers 数据集上运行训练和评估传输学习模型

python TransferLearning.py

模型在训练 20 个时期后,对花数据集实现了 93% 的准确性。

转移学习的基本概念

当我们使用基础模型进行转移学习时,我们基本上基于基础模型训练期间学习的特性和概念。 对于卷积 DNN,在本例中ResNet_18,这意味着我们 切断 了负责预测原始基础模型的类标签的最终密集层,并将其替换为一个新的密集层,用于预测我们手头新任务的类标签。 对旧预测层和新预测层的输入是相同的,我们只需重复使用训练的特征。 然后,我们训练此修改的网络,无论是新预测层的新权重还是整个网络的所有权重。

以下代码是从基础模型创建新模型的一部分 TransferLearning.py

    # Load the pretrained classification net and find nodes
    base_model   = load_model(base_model_file)
    feature_node = find_by_name(base_model, feature_node_name)
    last_node    = find_by_name(base_model, last_hidden_node_name)

    # Clone the desired layers with fixed weights
    cloned_layers = combine([last_node.owner]).clone(
        CloneMethod.freeze if freeze else CloneMethod.clone,
        {feature_node: Placeholder(name='features')})

    # Add new dense layer for class prediction
    feat_norm  = input_features - Constant(114)
    cloned_out = cloned_layers(feat_norm)
    z          = Dense(num_classes, activation=None, name=new_output_node_name) (cloned_out)

生成自己的自定义映像分类器

在上一部分中,我们训练了一个分类器,该分类器使用大约 6000 张图像来区分 102 种不同类别的花卉进行训练。 在本部分中,我们将仅使用每个类别 15 张图像来构建一个分类器,以便从羊中告诉狼。 我们使用同一 ResNet_18 基本模型进行转移学习。 训练和评估模型运行

python TransferLearning_Extended.py

模型在每张羊和狼的五张图像上进行测试,并正确预测所有标签。 输出文件每行包含预测结果的 JSON 表示形式:

[{"class": "Sheep", "predictions": {"Sheep":1.000, "Wolf":0.000}, "image": "..."}]
[{"class": "Sheep", "predictions": {"Sheep":1.000, "Wolf":0.000}, "image": "..."}]
[{"class": "Sheep", "predictions": {"Sheep":1.000, "Wolf":0.000}, "image": "..."}]
[{"class": "Sheep", "predictions": {"Sheep":0.997, "Wolf":0.003}, "image": "..."}]
[{"class": "Sheep", "predictions": {"Sheep":1.000, "Wolf":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "unknown", "predictions": {"Sheep":0.994, "Wolf":0.006}, "image": "..."}]
[{"class": "unknown", "predictions": {"Sheep":0.614, "Wolf":0.386}, "image": "..."}]
[{"class": "unknown", "predictions": {"Wolf":0.980, "Sheep":0.020}, "image": "..."}]

请注意,最后三个图像没有分配地面真理类,这当然是一个有效的方案,例如,用于在 Web 服务中评分未见的图像。 实际图像是下面显示的三张鸟图像。 JSON 输出中这些参数的基实类设置为 unknown。 请注意,尽管训练图像很少,但对分类器训练的概念的预测相当不错。 由于预先训练的基础模型,这主要部分。 对 不可见概念(如鸟类图像)的预测当然并不十分有意义,因为分类器只知道羊和狼。 稍后将对此进行更多了解。

imageimageimage

自定义映像集的文件夹结构

可以将脚本与你自己的映像一起使用 TransferLearning_Extended.py 。 以下是你需要的内容:

  1. class_mapping - 包含类别名称的数组,例如 ['wolf', 'sheep']
  2. train_map_file - 每行包含一行的文本文件,图像 URL 和选项卡分隔相应的类别索引,例如 0 对于狼或 1 羊:
  3. test_map_file - 将地图测试图像映射到其相应类别的文本文件。 对于测试图像中的未知类别,用作 -1 类别索引。

如果按以下方式构建图像,该脚本可以生成上述所有三个项目:

<image root folder>
    Train
        Sheep
        Wolf
    Test
        Sheep
        Wolf
        <optional: image files with unknown label directly here>

请参阅 <cntk root>/Examples/Image/DataSets/Animals/ 示例。 文件夹中的每个子文件夹 Train 将被视为一个类别, (单个级别,不) 递归。 对于根文件夹中的 Train 训练单个图像,将忽略它们,因为它们没有分配的类别。 忽略文件夹中未发生Train的其他子文件夹Test。 用于测试单个 未分类 图像,也用于评分,即直接 Test 存储在文件夹中的图像,如示例中的三个鸟图像。

若要使用自定义映像文件夹,需要设置train_image_folder脚本顶部TransferLearning_Extended.pytest_image_folder顶部:

# define data location and characteristics
train_image_folder = "<your_image_root_folder>/Train"
test_image_folder = "<your_image_root_folder>/Test"

然后,只需运行 python TransferLearning_Extended.py下面介绍了如何使用其他基本模型。

对于评分,无需使用一个 test_map_file,例如,如果要逐个评分单个图像。 只需加载训练的传输学习模型一次,然后每当想要获取新图像的预测时调用 eval_single_image

    # once:
    # load the trained transfer learning model
    trained_model = load_model(new_model_file)

    # for every new image:
    # get predictions for a single image
    probs = eval_single_image(trained_model, img_file, image_width, image_height)

几粒盐

训练图像应充分涵盖稍后要评分的方案。 如果分类器看到全新的概念或上下文,它可能会执行错误。 只需几个示例:

  • 你只训练来自约束环境的图像, (说,室内) ,并尝试对不同环境的图像进行评分, (户外) 。
  • 你仅根据某一特定制作的图像进行训练,并尝试对其他人进行评分。
  • 测试图像具有完全不同的特征,例如照明、背景、颜色、大小、位置等。
  • 测试映像包含全新的概念。

添加 catch-all 类别可能是个好主意,但前提是该类别的训练数据包含的图像,这些图像与评分时预期的图像完全相似。 与上面的示例一样,如果我们训练具有羊和狼图像的分类器,并使用它来评分鸟的图像,分类器仍只能分配羊或狼标签,因为它不知道任何其他类别。 如果我们要添加一个 catch-all 类别,并向其添加鸟类的训练图像,则分类器可能会正确预测鸟类图像的类。 然而,如果我们呈现它,例如汽车的图像,它面临与以前相同的问题,因为它只知道羊,狼和鸟 (我们恰好称之为捕食) 。 因此,训练数据(也适用于全部捕获)需要充分涵盖稍后在评分时预期的概念和图像。

另一个要记住的方面是,特定的基础模型可能非常适用于某些转移学习任务,而不适用于其他任务。 例如,上述 ResNet_18 模型在 ImageNet corpus 上进行了预训练,其中包含许多动物、人、汽车和其他 许多日常对象的图像。 使用此基础模型在传输学习中为 类似的日常对象 生成分类器可以很好地工作。 使用与基本模型相同的模型为微生物或铅笔绘图的图像构建分类器可能只产生平庸的结果。

使用不同的基本模型

若要将其他模型用作基本模型,需要在 (中 TransferLearning.py 为) TransferLearning_Extended.py 调整以下参数:

# define base model location and characteristics
_base_model_file = os.path.join(base_folder, "..", "..", "..", "PretrainedModels", "ResNet_18.model")
_feature_node_name = "features"
_last_hidden_node_name = "z.x"
_image_height = 224
_image_width = 224
_num_channels = 3

若要调查模型中的节点名称以及要选取的节点名称,可以使用 last_hidden_node 以下行打印所有节点名称和节点形状, (__main__ 请参阅) 中 TransferLearning.py 的方法:

    # You can use the following to inspect the base model and determine the desired node names
    node_outputs = get_node_outputs(load_model(_base_model_file))
    for out in node_outputs: print("{0} {1}".format(out.name, out.shape))