教程:使用 AutoML 和 Python 训练物体检测模型(预览版)(v1)

适用于:Python SDK azureml v1

重要

本文中介绍的功能处于预览阶段。 这些功能属于实验性预览功能,随时可能发生更改。

本教程介绍如何通过 Azure 机器学习 Python SDK 使用 Azure 机器学习自动化 ML 训练物体检测模型。 此物体检测模型可识别图像是否包含对象(如罐、纸箱、奶瓶或水瓶)。

自动化 ML 接受训练数据和配置设置,并自动循环访问不同特征规范化/标准化方法、模型和超参数设置的组合,以实现最佳模型。

在本教程中,你将使用 Python SDK 编写代码,并了解以下任务:

  • 下载和转换数据
  • 训练自动化机器学习物体检测模型
  • 为模型指定超参数值
  • 执行超参数扫描
  • 部署模型
  • 直观呈现检测结果

先决条件

  • 如果没有 Azure 订阅,可在开始前创建一个试用帐户。 立即试用免费版或付费版的 Azure 机器学习。

  • 此功能支持 Python 3.7 或 3.8

  • 如果还没有 Azure 机器学习工作区,请完成快速入门:Azure 机器学习入门

  • 下载并解压缩 odFridgeObjects.zip 数据文件*。 数据集以 Pascal VOC 格式进行注释,其中每个图像对应一个 xml 文件。 每个 xml 文件都包含有关其对应图像文件所在位置的信息,还包含有关边界框和对象标签的信息。 若要使用此数据,首先需要将其转换为所需的 JSONL 格式,如笔记本的将下载的数据转换为 JSONL 部分中所示。

如果你想要在自己的本地环境中运行此教程,也可以在 GitHub 上的 azureml-examples 存储库中找到它。 若要获取所需的包,

计算目标设置

首先需要设置用于自动化 ML 模型训练的计算目标。 用于图像任务的自动化 ML 模型需要 GPU SKU。

本教程将使用 NCsv3 系列(具有 V100 GPU),因为此类计算目标会利用多个 GPU 来加速训练。 此外,还可以设置多个节点,以在优化模型的超参数时利用并行度。

以下代码创建大小为标准 _NC24s_v3 的 GPU 计算,该计算具有四个附加到工作区 ws 的节点。

警告

确保订阅对想要使用的计算目标具有足够的配额。

from azureml.core.compute import AmlCompute, ComputeTarget

cluster_name = "gpu-nc24sv3"

try:
    compute_target = ComputeTarget(workspace=ws, name=cluster_name)
    print('Found existing compute target.')
except KeyError:
    print('Creating a new compute target...')
    compute_config = AmlCompute.provisioning_configuration(vm_size='Standard_NC24s_v3',
                                                           idle_seconds_before_scaledown=1800,
                                                           min_nodes=0,
                                                           max_nodes=4)

    compute_target = ComputeTarget.create(ws, cluster_name, compute_config)

#If no min_node_count is provided, the scale settings are used for the cluster.
compute_target.wait_for_completion(show_output=True, min_node_count=None, timeout_in_minutes=20)

试验设置

接下来,在工作区中创建一个 Experiment,以跟踪模型训练运行。


from azureml.core import Experiment

experiment_name = 'automl-image-object-detection'
experiment = Experiment(ws, name=experiment_name)

直观呈现输入数据

JSONL(JSON 行)格式准备好输入图像数据后,就可以直观呈现图像的地面实况边界框。 若要执行此操作,请确保你已安装 matplotlib

%pip install --upgrade matplotlib

%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import matplotlib.patches as patches
from PIL import Image as pil_image
import numpy as np
import json
import os

def plot_ground_truth_boxes(image_file, ground_truth_boxes):
    # Display the image
    plt.figure()
    img_np = mpimg.imread(image_file)
    img = pil_image.fromarray(img_np.astype("uint8"), "RGB")
    img_w, img_h = img.size

    fig,ax = plt.subplots(figsize=(12, 16))
    ax.imshow(img_np)
    ax.axis("off")

    label_to_color_mapping = {}

    for gt in ground_truth_boxes:
        label = gt["label"]

        xmin, ymin, xmax, ymax =  gt["topX"], gt["topY"], gt["bottomX"], gt["bottomY"]
        topleft_x, topleft_y = img_w * xmin, img_h * ymin
        width, height = img_w * (xmax - xmin), img_h * (ymax - ymin)

        if label in label_to_color_mapping:
            color = label_to_color_mapping[label]
        else:
            # Generate a random color. If you want to use a specific color, you can use something like "red".
            color = np.random.rand(3)
            label_to_color_mapping[label] = color

        # Display bounding box
        rect = patches.Rectangle((topleft_x, topleft_y), width, height,
                                 linewidth=2, edgecolor=color, facecolor="none")
        ax.add_patch(rect)

        # Display label
        ax.text(topleft_x, topleft_y - 10, label, color=color, fontsize=20)

    plt.show()

def plot_ground_truth_boxes_jsonl(image_file, jsonl_file):
    image_base_name = os.path.basename(image_file)
    ground_truth_data_found = False
    with open(jsonl_file) as fp:
        for line in fp.readlines():
            line_json = json.loads(line)
            filename = line_json["image_url"]
            if image_base_name in filename:
                ground_truth_data_found = True
                plot_ground_truth_boxes(image_file, line_json["label"])
                break
    if not ground_truth_data_found:
        print("Unable to find ground truth information for image: {}".format(image_file))

def plot_ground_truth_boxes_dataset(image_file, dataset_pd):
    image_base_name = os.path.basename(image_file)
    image_pd = dataset_pd[dataset_pd['portable_path'].str.contains(image_base_name)]
    if not image_pd.empty:
        ground_truth_boxes = image_pd.iloc[0]["label"]
        plot_ground_truth_boxes(image_file, ground_truth_boxes)
    else:
        print("Unable to find ground truth information for image: {}".format(image_file))

对于任何给定的图像,利用上述帮助程序函数,可以运行以下代码来显示边界框。

image_file = "./odFridgeObjects/images/31.jpg"
jsonl_file = "./odFridgeObjects/train_annotations.jsonl"

plot_ground_truth_boxes_jsonl(image_file, jsonl_file)

上传数据并创建数据集

若要使用数据进行训练,请通过数据存储将其上传到工作区。 数据存储提供了一种用于上传或下载数据以及从远程计算目标与其交互的机制。

ds = ws.get_default_datastore()
ds.upload(src_dir='./odFridgeObjects', target_path='odFridgeObjects')

上传到数据存储后,可以从数据创建 Azure 机器学习数据集。 数据集会将数据打包为一个可供训练使用的对象。

以下代码创建用于训练的数据集。 由于未指定验证数据集,因此默认情况下,20% 的训练数据会用于验证。

from azureml.core import Dataset
from azureml.data import DataType

training_dataset_name = 'odFridgeObjectsTrainingDataset'
if training_dataset_name in ws.datasets:
    training_dataset = ws.datasets.get(training_dataset_name)
    print('Found the training dataset', training_dataset_name)
else:
    # create training dataset
        # create training dataset
    training_dataset = Dataset.Tabular.from_json_lines_files(
        path=ds.path('odFridgeObjects/train_annotations.jsonl'),
        set_column_types={"image_url": DataType.to_stream(ds.workspace)},
    )
    training_dataset = training_dataset.register(workspace=ws, name=training_dataset_name)

print("Training dataset name: " + training_dataset.name)

直观呈现数据集

你还可以直观呈现此数据集中图像的地面实况边界框。

将数据加载到 pandas 数据帧。

import azureml.dataprep as dprep

from azureml.dataprep.api.functions import get_portable_path

# Get pandas dataframe from the dataset
dflow = training_dataset._dataflow.add_column(get_portable_path(dprep.col("image_url")),
                                              "portable_path", "image_url")
dataset_pd = dflow.to_pandas_dataframe(extended_types=True)

对于任何给定的图像,可以运行以下代码以显示边界框。

image_file = "./odFridgeObjects/images/31.jpg"
plot_ground_truth_boxes_dataset(image_file, dataset_pd)

配置物体检测试验

若要为与图像相关的任务配置自动化 ML 运行,请使用 AutoMLImageConfig 对象。 在 AutoMLImageConfig 中,可以使用 model_name 参数指定模型算法,并配置设置以对定义的参数空间执行超参数扫描,以查找最佳模型。

本示例使用 AutoMLImageConfig 训练具有yolov5fasterrcnn_resnet50_fpn 的物体检测模型,这两者都在 COCO 上预先进行了训练,COCO 是一个大规模物体检测、分段和字幕数据集,其中包含 80 多个标签类别的数千个带标签的图像。

适用于图像任务的超参数扫描

可以对已定义的参数空间执行超参数扫描,以查找最佳模型。

以下代码定义了参数空间,准备对每个已定义的算法 yolov5fasterrcnn_resnet50_fpn 进行超参数扫描。 在参数空间中,指定 learning_rateoptimizerlr_scheduler 等的值范围,以便 AutoML 在尝试生成具有最佳主要指标的模型时从中进行选择。 如果未指定超参数值,则对每个算法都使用默认值。

对于优化设置,请通过导入 GridParameterSampling, RandomParameterSamplingBayesianParameterSampling 类,使用随机采样从此参数空间中选取样本。 这样做可以让自动化 ML 尝试使用这些不同样本总共进行 20 次迭代,在使用四个节点进行设置的计算目标上一次运行四次迭代。 空间的参数越多,查找最佳模型所需的迭代次数就越多。

还使用了“Bandit 提前终止”策略。 此策略将终止性能不佳的配置;也就是那些与最佳性能配置相差不在 20% 的容许范围内的配置,这样可显著节省计算资源。

from azureml.train.hyperdrive import RandomParameterSampling
from azureml.train.hyperdrive import BanditPolicy, HyperDriveConfig
from azureml.train.hyperdrive import choice, uniform

parameter_space = {
    'model': choice(
        {
            'model_name': choice('yolov5'),
            'learning_rate': uniform(0.0001, 0.01),
            #'model_size': choice('small', 'medium'), # model-specific
            'img_size': choice(640, 704, 768), # model-specific
        },
        {
            'model_name': choice('fasterrcnn_resnet50_fpn'),
            'learning_rate': uniform(0.0001, 0.001),
            #'warmup_cosine_lr_warmup_epochs': choice(0, 3),
            'optimizer': choice('sgd', 'adam', 'adamw'),
            'min_size': choice(600, 800), # model-specific
        }
    )
}

tuning_settings = {
    'iterations': 20,
    'max_concurrent_iterations': 4,
    'hyperparameter_sampling': RandomParameterSampling(parameter_space),
    'policy': BanditPolicy(evaluation_interval=2, slack_factor=0.2, delay_evaluation=6)
}

定义参数空间和优化设置后,可以将它们传递到 AutoMLImageConfig 对象中,然后提交试验以使用训练数据集来训练图像模型。

from azureml.train.automl import AutoMLImageConfig
automl_image_config = AutoMLImageConfig(task='image-object-detection',
                                        compute_target=compute_target,
                                        training_data=training_dataset,
                                        validation_data=validation_dataset,
                                        primary_metric='mean_average_precision',
                                        **tuning_settings)

automl_image_run = experiment.submit(automl_image_config)
automl_image_run.wait_for_completion(wait_post_processing=True)

执行超参数扫描时,使用 HyperDrive UI 直观呈现所尝试的不同配置会很有用。 可以导航到此 UI,方法是从上级(即 HyperDrive 父运行)转到主 automl_image_run 的 UI 中“子运行”选项卡。 然后,可以转到此项的“子运行”选项卡。 或者,也可在下面直接看到 HyperDrive 父运行,然后导航到其“子运行”选项卡:

from azureml.core import Run
hyperdrive_run = Run(experiment=experiment, run_id=automl_image_run.id + '_HD')
hyperdrive_run

注册最佳模型

运行完成后,可以注册从最佳运行创建的模型。

best_child_run = automl_image_run.get_best_child()
model_name = best_child_run.properties['model_name']
model = best_child_run.register_model(model_name = model_name, model_path='outputs/model.pt')

将模型部署为 Web 服务

有了经过训练的模型,就可以将模型部署到 Azure 上。 可在 Azure 容器实例 (ACI) 或 Azure Kubernetes 服务 (AKS) 上将经过训练的模型部署为 Web 服务。 ACI 非常适用于测试部署,而 AKS 更适合大规模生产使用。

在本教程中,我们将模型部署为 AKS 中的 Web 服务。

  1. 创建 AKS 计算群集。 本示例将 GPU 虚拟机 SKU 用于部署群集

    from azureml.core.compute import ComputeTarget, AksCompute
    from azureml.exceptions import ComputeTargetException
    
    # Choose a name for your cluster
    aks_name = "cluster-aks-gpu"
    
    # Check to see if the cluster already exists
    try:
        aks_target = ComputeTarget(workspace=ws, name=aks_name)
        print('Found existing compute target')
    except ComputeTargetException:
        print('Creating a new compute target...')
        # Provision AKS cluster with GPU machine
        prov_config = AksCompute.provisioning_configuration(vm_size="STANDARD_NC6",
                                                            location="chinanorth3")
        # Create the cluster
        aks_target = ComputeTarget.create(workspace=ws,
                                          name=aks_name,
                                          provisioning_configuration=prov_config)
        aks_target.wait_for_completion(show_output=True)
    
  2. 定义描述如何设置包含模型的 Web 服务的推理配置。 可以在推理配置中使用训练运行中的评分脚本和环境。

    注意

    若要更改模型的设置,请打开下载的评分脚本,并修改 model_settings 变量,然后再部署模型。

    from azureml.core.model import InferenceConfig
    
    best_child_run.download_file('outputs/scoring_file_v_1_0_0.py', output_file_path='score.py')
    environment = best_child_run.get_environment()
    inference_config = InferenceConfig(entry_script='score.py', environment=environment)
    
  3. 然后,可以将模型部署为 AKS Web 服务。

    
    from azureml.core.webservice import AksWebservice
    from azureml.core.webservice import Webservice
    from azureml.core.model import Model
    from azureml.core.environment import Environment
    
    aks_config = AksWebservice.deploy_configuration(autoscale_enabled=True,
                                                    cpu_cores=1,
                                                    memory_gb=50,
                                                    enable_app_insights=True)
    
    aks_service = Model.deploy(ws,
                               models=[model],
                               inference_config=inference_config,
                               deployment_config=aks_config,
                               deployment_target=aks_target,
                               name='automl-image-test',
                               overwrite=True)
    aks_service.wait_for_deployment(show_output=True)
    print(aks_service.state)
    

测试 Web 服务

可以测试已部署的 Web 服务,以预测新图像。 对于本教程,请传递数据集中的随机图像,并将其传递给评分 URI。

import requests

# URL for the web service
scoring_uri = aks_service.scoring_uri

# If the service is authenticated, set the key or token
key, _ = aks_service.get_keys()

sample_image = './test_image.jpg'

# Load image data
data = open(sample_image, 'rb').read()

# Set the content type
headers = {'Content-Type': 'application/octet-stream'}

# If authentication is enabled, set the authorization header
headers['Authorization'] = f'Bearer {key}'

# Make the request and display the response
resp = requests.post(scoring_uri, data, headers=headers)
print(resp.text)

直观呈现检测结果

为测试图像评分后,可以直观呈现此图像的边界框。 若要执行此操作,请确保已安装 matplotlib。

%pip install --upgrade matplotlib
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import matplotlib.patches as patches
from PIL import Image
import numpy as np
import json

IMAGE_SIZE = (18,12)
plt.figure(figsize=IMAGE_SIZE)
img_np=mpimg.imread(sample_image)
img = Image.fromarray(img_np.astype('uint8'),'RGB')
x, y = img.size

fig,ax = plt.subplots(1, figsize=(15,15))
# Display the image
ax.imshow(img_np)

# draw box and label for each detection
detections = json.loads(resp.text)
for detect in detections['boxes']:
    label = detect['label']
    box = detect['box']
    conf_score = detect['score']
    if conf_score > 0.6:
        ymin, xmin, ymax, xmax =  box['topY'],box['topX'], box['bottomY'],box['bottomX']
        topleft_x, topleft_y = x * xmin, y * ymin
        width, height = x * (xmax - xmin), y * (ymax - ymin)
        print('{}: [{}, {}, {}, {}], {}'.format(detect['label'], round(topleft_x, 3),
                                                round(topleft_y, 3), round(width, 3),
                                                round(height, 3), round(conf_score, 3)))

        color = np.random.rand(3) #'red'
        rect = patches.Rectangle((topleft_x, topleft_y), width, height,
                                 linewidth=3, edgecolor=color,facecolor='none')

        ax.add_patch(rect)
        plt.text(topleft_x, topleft_y - 10, label, color=color, fontsize=20)

plt.show()

清理资源

如果打算运行其他 Azure 机器学习教程,请不要完成本部分。

如果不打算使用已创建的资源,请删除它们,以免产生任何费用。

  1. 在 Azure 门户中,选择最左侧的“资源组”。
  2. 从列表中选择已创建的资源组。
  3. 选择“删除资源组”
  4. 输入资源组名称。 然后选择“删除”。

还可保留资源组,但请删除单个工作区。 显示工作区属性,然后选择“删除”。

后续步骤

在本自动化机器学习教程中,你已完成以下任务:

  • 配置了工作区并准备了试验数据。
  • 训练了自动化物体检测模型
  • 为模型指定了超参数值
  • 执行了超参数扫描
  • 部署了模型
  • 直观呈现了检测结果

注意

可通过 MIT 许可证下的许可使用 fridge 对象数据集。