亚马逊AWS官方博客

《制作无人驾驶车辆》第 4 部分:通过 Apache MXNet,为您的自动驾驶车辆应用行为克隆技术

在我们无人驾驶车辆系列的第一篇博客文章中,您制作了一辆 Donkey 车并将导航服务器部署到了 Amazon EC2 实例上。在第二篇博客文章中,您学习了如何驾驶 Donkey 车辆,Donkey 车辆也学习了如何自行驾驶。在第三篇博客文章中,您学习了如何通过 AWS IoT 将 Donkey 车辆的遥测数据流式传输到 AWS 的过程。

在这篇博文中,我们将深入探讨实现车辆自行驾驶的深度学习框架,并介绍采用卷积神经网络 (CNN) 的行为克隆概念。CNN 是一项面向计算机视觉任务的先进建模技术,可帮助解答车辆可能会遇到的问题,例如,“我前面是轨道还是圆锥形路标?”


1) 在 AWS 上制造一辆无人驾驶车辆,并参加 re:Invent 机器人车拉力赛
2) 《制作无人驾驶车辆》第 2 部分:驾驶您的车辆
3) 《制作无人驾驶车辆》第 3 部分:连接您的无人驾驶车辆
4) 《制作无人驾驶车辆》第 4 部分:通过 Apache MXNet,为您的自动驾驶车辆应用行为克隆技术


P2 上的 Donkey 训练数据设置

我们已在博客文章 2 中详细演练了如何运行训练。但是,让我们在这里回顾一下关键步骤和命令:

  1. 将数据从 Pi 复制到 Amazon EC2 实例:
    $ rsync -rva --progress -e "ssh -i /path/to/key/DonkeyKP-us-east-1.pem" /home/pi/d2// ec2-user@ec2-your-ip.compute-1.amazonaws.com:~/d2/data/
  2. 启动训练过程:
    $ python ~/d2/manage.py train --model /path/to/myfirstpilot
  3. 将训练过的模型复制回 Pi:
    $: rsync -rva --progress -e "ssh -i /path/to/key/DonkeyKP-us-east-1.pem" ec2-user@ec2-your-ip.compute-1.amazonaws.com:~/d2/models/ /home/pi/d2/models/

模型幕后

在本部分中,我将讨论模型学习的内容以及它如何能够自行驾驶。目前制作的 Donkey 车辆使用 Keras 作为其默认的深度学习框架。AWS 正在添加面向 Apache MXNet、Gluon 和 PyTorch 等其他框架的支持。在这篇博文中,我们将使用 Apache MXNet 来深入分析实现自动驾驶的模型的内部工作原理。如前所述,我们使用名为行为克隆的技术来实现车辆的自动驾驶。基本上,此模型基于训练数据学习驾驶,而训练数据则是通过环绕轨道行驶收集而来的。保证大部分数据都很“干净”非常重要,也就是说,假定我们的目标始终未偏离轨道,那么训练数据中车辆脱离轨道或转弯错误的图像就不会太多。就像驾驶员控制方向盘,保证车辆在车道上行驶一样,我们将制作一个会根据当前场景确定转向角的模型,引导我们将问题模型化为“根据输入图像,我们需要采用哪种转向角?”。实际驾驶情况更复杂,因为涉及到了加速和变速齿轮等更多组件。为了简单起见,我们在开始的时候先把油门固定到某个百分比,然后再让车辆行驶。在实践中我们发现,对于备用训练数据,25-30% 的油门已被证明是 Donkey 车辆的最佳行驶速度。

为实现这一目标,我们将使用名为卷积神经网络 (CNN) 的深度学习技术。CNN 已成为面向计算机视觉问题的实际网络。CNN 由多个卷积层组成,其中每个节点都与一个名为感受野的小窗口关联。这可以让我们提取图像中的局部特征。像“图像中是否有轨道或人?”这样的问题就可以通过使用先前计算出来的这些局部特征进行计算。您可以在此处找到有关 CNN 工作原理的详细说明。

数据集

在这篇博文中我将使用一个数据集,这个数据集是车辆环绕轨道行驶大约 15 分钟收集来的。如前所述,我们会先过滤一下,将车辆明显不在轨道上的图像丢弃。Donkey 软件已经提供了一个基于浏览器的 UI,用来删除“坏”的图像 (命令: donkey tubclean <包含 Tub 的文件夹>)。与此类似的、车辆在轨道上行驶的图像数据集可在此处获取

制作 CNN 模型

通过 im2rec.py 工具,我们将图像数据集转换为二进制文件,以便提高处理速度。要了解有关 Apache MXNet 内部工作原理的更多信息,请参阅教程页面。

import mxnet as mx
import numpy as np

data = mx.symbol.Variable(name="data")

body = mx.sym.Convolution(data=data, num_filter=24,  kernel=(5, 5), stride=(2,2)) 
body = mx.sym.Activation(data=body, act_type='relu', name='relu1')
body = mx.symbol.Pooling(data=body, kernel=(2, 2), stride=(2,2), pool_type='max')

body = mx.sym.Convolution(data=body, num_filter=32,  kernel=(5, 5), stride=(2,2))
body = mx.sym.Activation(data=body, act_type='relu')
body = mx.symbol.Pooling(data=body, kernel=(2, 2), stride=(2,2), pool_type='max')

flatten = mx.symbol.Flatten(data=body)

body = mx.symbol.FullyConnected(data=flatten, name='fc0', num_hidden=32)
body = mx.sym.Activation(data=body, act_type='relu', name='relu6')
body = mx.sym.Dropout(data=body, p=0.1)

body = mx.symbol.FullyConnected(data=body, name='fc1', num_hidden=16)
body = mx.sym.Activation(data=body, act_type='relu', name='relu7')

out = mx.symbol.FullyConnected(data=body, name='fc2', num_hidden=1)
out = mx.symbol.LinearRegressionOutput(data=out, name="softmax")

考虑到我们需要确定车辆的转向角,我们将使用单输出的线性回归输出层。为评估训练过程执行的进度如何,我们可以使用平均绝对误差 (MAE) 作为评估指标。由于不同角度之间的距离在 Euclidean 系统中是可以判断的,因而 MAE 对于优化损失来说是一个不错的指标。

训练模型

我们的 S3 存储桶中提供了可以在训练中使用的二进制文件。

# Get Iterators

def get_iterators(batch_size, data_shape=(3, 120, 160)):
    train = mx.io.ImageRecordIter(
        path_imgrec         = 'train.rec', 
        data_name           = 'data',
        label_name          = 'softmax_label',
        batch_size          = batch_size,
        data_shape          = data_shape,
        shuffle             = True,
        rand_crop           = True,
        rand_mirror         = True)
    val = mx.io.ImageRecordIter(
        path_imgrec         = 'valid.rec',
        data_name           = 'data',
        label_name          = 'softmax_label',
        batch_size          = batch_size,
        data_shape          = data_shape,
        rand_crop           = False,
        rand_mirror         = False)
    return (train, val)

batch_size = 16
train_iter, val_iter = get_iterators(batch_size)

#Training

batch_size = 8
num_gpus = 1
num_epoch = 10
mod = mx.mod.Module(out, context=[mx.gpu(i) for i in range(num_gpus)])
mod.fit(train_data=train_iter, 
        eval_data=val_iter, 
        eval_metric='mae', 
        optimizer='adam',
        optimizer_params={'learning_rate': 0.0001},
        num_epoch=num_epoch,
        batch_end_callback = mx.callback.Speedometer(batch_size, 100),        
       )

评估和模拟器

现在,我们已经训练完模型,可以将它部署在车辆上进行试驾。我们的验证集中的 MAE 错误很少,说明我们对模型的训练和泛化效果不错。但是,如果我们能在部署之前就从视觉上了解车辆将会如何行驶,那就太棒了。为实现这一点,我将使用模拟器来查看车辆会如何在轨道上行驶。

import os	
import time
%matplotlib inline
from IPython import display
import matplotlib.patches as patches

PATH = 'trainingdata/'
all_files = sorted(os.listdir(PATH))
sym, arg_params, aux_params = mx.model.load_checkpoint('my-car', num_epoch)
mod = mx.mod.Module(symbol=sym) 
fig, ax = plt.subplots(1)
for fname in all_files:
    org_img = Image.open(PATH + fname)    
    img = np.array(org_img)
    img = np.swapaxes(img, 0, 2)
    img = np.swapaxes(img, 1, 2) 
    img = img[np.newaxis, :] 
    mod.forward(Batch(data=[mx.nd.array(img)]))
    exp = mod.get_outputs()[0].asnumpy()[0]
    angle = 180*exp
    left = 80 * exp + 80
    rect = patches.Rectangle((left, 85),20,30, angle=angle,
                             linewidth=2,edgecolor='r',facecolor='none')
    patch = ax.add_patch(rect)
    display.clear_output(wait=True)
    display.display(plt.gcf())
    plt.imshow(org_img)
    time.sleep(0.1)
    patch.remove()

结论

我们的无人驾驶车辆系列到此结束。我们期待在 re:Invent 2017 年机器人车拉力赛上与您相遇,这是一场有关深度学习、无人驾驶车辆以及 Amazon AI 和 IoT 服务方面亲身体验的、为期两天的黑客马拉松赛。我们鼓励您继续完成以下 Donkey 车辆并加入社区