21

image

看图片识车型,自己写个这样的程序其实一点也不难:

媳妇把前车剐了,给我打电话。
媳妇:“老公,我把别人车给剐了。”
我:“啥车呀?”
媳妇:“标我不认识,B开头的。”
我:“比亚迪啊,你自己解决吧。”
媳妇:“不不不,我认识比亚迪。”
我:“宝马?没事,我去取点钱。”
媳妇:“也不是宝马。”
我:“我擦,你个败家娘们,不会怼了个宾利吧?”
这时,电话那头传来一个陌生的男声:“我那个叫布加迪。”
我:“……”

不认识车标,辨不清车型,有时候还真挺难的。

其实在基于深度学习的图像分类领域,情况也是这样。写一个能认识汽车的程序,这没啥技术难度。可要想从众多汽车的照片中找出具体的品牌型号,这就有点棘手了。

车辆检测及型号识别,这种技术就被广泛应用于物业、交通管理等场景。通过在停车场出入口、路口、高速卡口等位置采集图片数据,对车辆的数量、型号等进行识别,可以以较高效率对车型、数量信息等进行采集。随后通过采集的数据就可以在不同场景中辅助业务开展,如商场停车位的规划、路况规划或者公安系统追踪肇事车辆等。

本文所涉及的内容将引入迁移学习的思想。什么是迁移学习?深度学习中需要大量数据和计算资源,且需花费大量时间来训练模型,但在实际中难以满足这些需求。而使用迁移学习则能有效降低数据量、计算量和计算时间,并能定制在新场景的业务需求,可谓一大利器。

迁移学习不是一种算法,而是一种机器学习思想,应用到深度学习就是微调(Fine-tune)。通过修改预训练网络模型结构(如修改样本类别输出个数),选择性载入预训练网络模型权重,再用自己的数据集重新训练模型就是微调的基本步骤。微调能够快速训练好一个模型,用相对较小的数据量,还能达到不错的结果。

本解决方案使用 Amazon SageMaker,它可以帮助开发人员和数据科学家构建、训练和部署 ML 模型。Amazon SageMaker 是一项完全托管的服务,涵盖了 ML 的整个工作流,可以标记和准备数据、选择算法、训练模型、调整和优化模型以便部署、预测和执行操作。同时,本方案基于MXNet,Apache MXNet(孵化)是一个深度学习框架,旨在提高效率和灵活性。它可供我们混合符号和命令式编程,以最大限度地提高效率和生产力。

下文会重点介绍在 Amazon SageMaker 中如何基于 MXNet,使用自己的数据来微调一个预训练的图像分类模型并且达到较高的准确率来构建一个车型号分类器。

解决方案概览

在此示例中,我们将使用 Amazon SageMaker 执行以下操作:

  1. 环境准备
  2. 使用 Jupyter Notebook 下载数据集并将其进行数据预处理
  3. 使用预训练好的模型镜像进行模型微调
  4. 将模型进行托管部署
  5. 调用已部署的模型接口进行在线推理

1.环境准备

首先,我们需要创建一些使用亚马逊云科技 (Amazon Web Services)服务所需的授权和环境。包含三部分:

  1. 模型训练所需的权限,这个会自动从创建笔记本的 Role 中自动获取
  2. 存储训练数据和模型的 S3 存储桶
  3. Amazon Sagemaker 中预训练好的图像分类模型 Docker 镜像
%%time
import boto3
import re
from sagemaker import get_execution_role
from sagemaker.amazon.amazon_estimator import get_image_uri
 
role = get_execution_role()
 
bucket='app-cars-classfication' # customize to your bucket
 
training_image = get_image_uri(boto3.Session().region_name, 'image-classification')

2.数据预处理

本文使用了斯坦福大学提供的开源数据集 Cars dataset,该数据集包含了196种不同汽车品牌车型的16185张图片。其中我们将使用8144张作为训练集,8041张作为测试集,每个车型号在训练和测试集中分布均衡。您可以访问该数据集主页查看完整的数据说明和进行下载,示例图片如下:

image-recognition-of-car-models-based-on-amazon-sagemaker1.png

为提高 IO 效率,不会直接读取图片文件,而是先将图片列表和标签转换为 RecordIO 格式的二进制文件,训练时就可以顺序读取数据,大大提高了IO速率。MXNet 社区提供了一个很好的图片转换工具 im2rec.py,可进行快速图像转换。主要利用的是 MXNet im2rec.py 工具生成 List 和 Record 文件,并按照 Validation 的数据和 Training 数据的比例进行自动的数据拆分。具体命令如下图,首先生成标签为汽车种类个数的 List 文件之后,按照多个并发线程的方式进行 Record 格式转换,并存在定义的目录内。具体转换的 Shell 脚本内容如下。需要注意的是,这里的 data_path 为此 Shell 脚本的入参,需要自己指定为自己存放数据集的本地路径:

#!/usr/bin/env bash
## The process file for image classification data transform to the recordio format
# current directory contains only one zip file
 
git clone https://github.com/apache/incubator-mxnet.git
source activate amazonei_mxnet_p36
unzip *.zip
mkdir train
mkdir validation
 
data_path=$1
echo "data_path: ${data_path}"
train_path=train/
echo "train_path: ${train_path}"
val_path=validation/
echo "val_path: ${val_path}"
 
python incubator-mxnet/tools/im2rec.py \
  --list \
  --train-ratio 0.8 \
  --recursive \
  $data_path/data $data_path
 
python incubator-mxnet/tools/im2rec.py \
    --resize 224 \
    --center-crop \
    --num-thread 4 \
    $data_path/data $data_path
 
mv ${data_path}data_train.rec $train_path
mv ${data_path}data_val.rec $val_path

转换结束后,可以看到在 Train 和 Validation 目录中分别生成了两个文件:data_train.rec 和 data_val.rec,我们需要将其上传至已创建的 S3 存储桶内供后续模型训练使用:

import os 
import boto3
 
     
def upload_to_s3(file):
    s3 = boto3.resource('s3')
    data = open(file, "rb")
    key = file
    s3.Bucket(bucket).put_object(Key=key, Body=data)
 
 
# caltech-256
s3_train_key = "car_data_sample/train"
s3_validation_key = "car_data_sample/validation"
s3_train = 's3://{}/{}/'.format(bucket, s3_train_key)
s3_validation = 's3://{}/{}/'.format(bucket, s3_validation_key)
 
upload_to_s3('car_data_sample/train/data_train.rec')
 
upload_to_s3('car_data_sample/validation/data_val.rec')

3.使用迁移学习进行模型训练

数据集准备结束之后,就可以开始模型的训练了。但在开始训练任务之前,需要配置模型训练的一系列超参数,具体的超参数含义如下:

  • Num_layers:神经网络的层数,本例中可以选择18、34、50、101、152和200。很多经典网络模型的名字中包含的数字就代表了 Layer 个数,如 vgg16中的16就代表了权重层的个数。
  • Image_shape:输入图像的通道数,像素的长宽。
  • Num_training_samples:训练样本的个数。
  • Num_classes:训练样本图像分类的类目数,本例中为了简介,只选取了三个 Class 做范例。
  • mini_batch_size:每轮训练的输入一批数据包含的数目。
  • epochs:训练轮次。
  • learning_rate:训练学习率。
  • use_pretrained_model:是否使用预训练模型进行迁移学习,如为1,则初始化中使用已经基于一个较大的开源数据集,如 imagenet,学习的网络结构。
# The algorithm supports multiple network depth (number of layers). They are 18, 34, 50, 101, 152 and 200
# For this training, we will use 18 layers
num_layers = 18
# we need to specify the input image shape for the training data
image_shape = "3,224,224"
# we also need to specify the number of training samples in the training set
# for caltech it is 15420
num_training_samples = 96
# specify the number of output classes
num_classes = 3
# batch size for training
mini_batch_size =  30
# number of epochs
epochs = 100
# learning rate
learning_rate = 0.01
top_k=2
# Since we are using transfer learning, we set use_pretrained_model to 1 so that weights can be 
# initialized with pre-trained weights
use_pretrained_model = 1

之后,我们进行必要的 SageMaker API 创建,构建对应的训练任务。其中有指定训练的输入与输出,训练的计算实例配置,这里我们使用的是 ml.p2.xlarge GPU 实例。需要注意的是,这里 Sagemaker notebook 进行本地的数据处理、模型训练、模型推理是不同环境,可以根据不同的计算任务的需求进行不同的机型选择。

%%time
import time
import boto3
from time import gmtime, strftime
 
 
s3 = boto3.client('s3')
# create unique job name 
job_name_prefix = 'cars-imageclassification'
timestamp = time.strftime('-%Y-%m-%d-%H-%M-%S', time.gmtime())
job_name = job_name_prefix + timestamp
training_params = \
{
    # specify the training docker image
    "AlgorithmSpecification": {
        "TrainingImage": training_image,
        "TrainingInputMode": "File"
    },
    "RoleArn": role,
    "OutputDataConfig": {
        "S3OutputPath": 's3://{}/{}/output'.format(bucket, job_name_prefix)
    },
    "ResourceConfig": {
        "InstanceCount": 1,
        "InstanceType": "ml.p2.xlarge",
        "VolumeSizeInGB": 50
    },
    "TrainingJobName": job_name,
    "HyperParameters": {
        "image_shape": image_shape,
        "num_layers": str(num_layers),
        "num_training_samples": str(num_training_samples),
        "num_classes": str(num_classes),
        "mini_batch_size": str(mini_batch_size),
        "epochs": str(epochs),
        "learning_rate": str(learning_rate),
        "use_pretrained_model": str(use_pretrained_model)
    },
    "StoppingCondition": {
        "MaxRuntimeInSeconds": 360000
    },
#Training data should be inside a subdirectory called "train"
#Validation data should be inside a subdirectory called "validation"
#The algorithm currently only supports fullyreplicated model (where data is copied onto each machine)
    "InputDataConfig": [
        {
            "ChannelName": "train",
            "DataSource": {
                "S3DataSource": {
                    "S3DataType": "S3Prefix",
                    "S3Uri": s3_train,
                    "S3DataDistributionType": "FullyReplicated"
                }
            },
            "ContentType": "application/x-recordio",
            "CompressionType": "None"
        },
        {
            "ChannelName": "validation",
            "DataSource": {
                "S3DataSource": {
                    "S3DataType": "S3Prefix",
                    "S3Uri": s3_validatio

到这里,我们就可以直接调用 SageMaker API 来启动训练任务。需要注意的是,我们只是用一个简单的 API:sagemaker.create_training_job,并且根据第三步中的参数配置,就直接可以进行轻松的训练了。中间没有任何的环境构建,部署,甚至是神经网络模型设计的过程。

# create the Amazon SageMaker training job
sagemaker = boto3.client(service_name='sagemaker')
sagemaker.create_training_job(**training_params)
 
# confirm that the training job has started
status = sagemaker.describe_training_job(TrainingJobName=job_name)['TrainingJobStatus']
print('Training job current status: {}'.format(status))
 
try:
    # wait for the job to finish and report the ending status
    sagemaker.get_waiter('training_job_completed_or_stopped').wait(TrainingJobName=job_name)
    training_info = sagemaker.describe_training_job(TrainingJobName=job_name)
    status = training_info['TrainingJobStatus']
    print("Training job ended with status: " + status)
except:
    print('Training failed to start')
     # if exception is raised, that means it has failed
    message = sagemaker.describe_training_job(TrainingJobName=job_name)['FailureReason']
    print('Training failed with the following error: {}'.format(message))

完成上述步骤后,就可以在 Sagemaker Console 中看到自己的训练任务。当 Status 为 Completed 时,代表训练完成。整个训练过程大概持续15分钟,这个时间会根据选择的机型和设置的 epochs 个数等而有所差异。

image-recognition-of-car-models-based-on-amazon-sagemaker2.png

同时,在训练过程中,我们还可以通过监控 Cloudwatch log 来查看训练过程中的 loss 变化:

image-recognition-of-car-models-based-on-amazon-sagemaker3.png

4.模型部署

训练结束后,我们在之前配置的 S3 存储桶就获得了最新的模型文件。接下来将其进行线上部署,这样就可以通过接受来自客户端的 Restful API 请求进行预测。

首先创建模型:

%%time
import boto3
from time import gmtime, strftime
 
sage = boto3.Session().client(service_name='sagemaker') 
 
model_name="cars-imageclassification-" + time.strftime('-%Y-%m-%d-%H-%M-%S', time.gmtime())
print(model_name)
info = sage.describe_training_job(TrainingJobName=job_name)
model_data = info['ModelArtifacts']['S3ModelArtifacts']
print(model_data)
 
hosting_image = get_image_uri(boto3.Session().region_name, 'image-classification')
 
primary_container = {
    'Image': hosting_image,
    'ModelDataUrl': model_data,
}
 
create_model_response = sage.create_model(
    ModelName = model_name,
    ExecutionRoleArn = role,
    PrimaryContainer = primary_container)
 
print(create_model_response['ModelArn'])

然后配置接口,可以看到,这里推理使用的实例是 ml.m4.xlarge:


from time import gmtime, strftime
 
timestamp = time.strftime('-%Y-%m-%d-%H-%M-%S', time.gmtime())
endpoint_config_name = job_name_prefix + '-epc-' + timestamp
endpoint_config_response = sage.create_endpoint_config(
    EndpointConfigName = endpoint_config_name,
    ProductionVariants=[{
        'InstanceType':'ml.m4.xlarge',
        'InitialInstanceCount':1,
        'ModelName':model_name,
        'VariantName':'AllTraffic'}])
 
print('Endpoint configuration name: {}'.format(endpoint_config_name))
print('Endpoint configuration arn:  {}'.format(endpoint_config_response['EndpointConfigArn']))

第三步使用上面配置的模型和端口配置创建终结点,这一步通常需要比较长的时间,一般终结点的创建需要10分钟左右:

%%time
import time
 
timestamp = time.strftime('-%Y-%m-%d-%H-%M-%S', time.gmtime())
endpoint_name = job_name_prefix + '-ep-' + timestamp
print('Endpoint name: {}'.format(endpoint_name))
 
endpoint_params = {
    'EndpointName': endpoint_name,
    'EndpointConfigName': endpoint_config_name,
}
endpoint_response = sagemaker.create_endpoint(**endpoint_params)
print('EndpointArn = {}'.format(endpoint_response['EndpointArn']))

在等待端口创建的时候,可以通过查看端口创建状态来获得创建的状态:

# get the status of the endpoint
response = sagemaker.describe_endpoint(EndpointName=endpoint_name)
status = response['EndpointStatus']
print('EndpointStatus = {}'.format(status))
# wait until the status has changed
sagemaker.get_waiter('endpoint_in_service').wait(EndpointName=endpoint_name)
# print the status of the endpoint
endpoint_response = sagemaker.describe_endpoint(EndpointName=endpoint_name)
status = endpoint_response['EndpointStatus']
print('Endpoint creation ended with EndpointStatus = {}'.format(status))

如果上述代码运行结果为 Endpoint creation ended with EndpointStatus = InService,那么代表已经成功创建了一个终结点。我们也可以通过 Sagemaker Console 查看到已经创建好的终结点。

5.推理及应用

现在我们使用一个随意挑选的汽车图片进行型号分类。本例挑选了一张 Acura 的 Acura RL Sedan 2012 车型,图片如下:

image-recognition-of-car-models-based-on-amazon-sagemaker4.png

我们可以直接调用创建的终结点进行推理,可以看到结果与概率,并能看到准确判断出了相对应的分类。这里的概率并不是非常高,但鉴于我们作为范例只训练了不多的 epoch,已经是个很不错的结果了。如果想要得到更高的准确率,请使用完整数据集进行更多轮次的训练。

import json
import numpy as np
with open(file_name, 'rb') as f:
    payload = f.read()
    payload = bytearray(payload)
response = runtime.invoke_endpoint(EndpointName=endpoint_name, 
                                   ContentType='application/x-image', 
                                   Body=payload)
result = response['Body'].read()
# result will be in json format and convert it to ndarray
result = json.loads(result)
# the result will output the probabilities for all classes
# find the class with maximum probability and print the class index
index = np.argmax(result)
object_categories = ['Acura Integra Type R 2001', 'Acura RL Sedan 2012', 'Acura TL Sedan 2012']
print("Result: label - " + object_categories[index] + ", probability - " + str(result[index]))

image-recognition-of-car-models-based-on-amazon-sagemaker5.png

号外,号外,号外!!!

今晚八点,#Amazon Live Coding# 将演示如何利用 Amazon Serverless 和 Machine Learning 服务来快速构建语音翻译器:你只需要说出中文,亚马逊云科技(Amazon Lambda)就会帮你把语音转换成文本(Amazon Transcribe),翻译成选择的语言(比如英文)(Amazon Translate),然后转换成对应的语音(Amazon Polly)。
你就可以造出神奇的宝贝鱼,科技让沟通无障碍~

直播通道:https://live.bilibili.com/219...

image

image


亚马逊云开发者
2.9k 声望9.6k 粉丝

亚马逊云开发者社区是面向开发者交流与互动的平台。在这里,你可以分享和获取有关云计算、人工智能、IoT、区块链等相关技术和前沿知识,也可以与同行或爱好者们交流探讨,共同成长。