9009浏览
查看: 9009|回复: 1

[漂移驴车项目] 驴车第 3 部分:神经网络—提高Pi 的准确性和推理速度(zz)

[复制链接]
本帖最后由 极凹甫 于 2022-4-16 21:25 编辑

我在https://medium.com/看到 Jason Wu 发的3个帖子,讲述了自己玩驴车的过程,我觉得值得转给大家看看。
驴车第 3 部分:神经网络 — 提高 Raspberry Pi 的准确性和推理速度

在我的上一篇文章中,我写了关于对驾驶数据进行分析以及在不更改模型的情况下改进预测结果的简单方法。今天我想谈谈在更基础的层面上改进预测,改进模型本身。

设置环境

训练驴飞行员的默认选项是在您的笔记本电脑上使用 CPU。它工作得很好,主要是因为神经网络不是很深,所以我只花了大约几个小时来训练使用这个选项。然而,为了试验不同的模型,我想加快训练时间,以便进行一些快速迭代。这就是 AWS、谷歌云或 Floidhub 等云服务发挥作用的地方。在实验时,我使用的是 Shadow 的 VM 机器,但事后看来,这在训练时间方面是较慢的选择。设置环境的详细信息因您使用的平台而异,但 Donkey Car 的文档实际上有一个在 AWS 上设置它的演练。

关于设置环境的一些注意事项。无论您使用哪个平台,您都需要安装 donkey 库、Jupyter Notebook 和 Tensorflow-gpu 1.8.0(如果您的平台运行 Windows,还需要安装 Anaconda,因为它可以更轻松地安装 GPU 版本的 Tensorflow)。请注意,Tensorflow 的默认版本不包含 GPU 支持,因此您必须在安装时指定它。Tensorlfow 版本与汽车本身使用的版本(当前为 1.8.0)相匹配也很重要,否则在 Pi 上部署时 Pilot 将无法工作。


复制驴管道

Donkey Car 库有一个数据管道,可在将图像数据分组为样本批次进行训练之前对其进行预处理。在 Jupyter Notebook 中,您可以复制它并与每个组件交互。如果您只需要训练不同的试验模型,则无需复制此管道,因为我只需修改库中的源代码即可。但是,这样做可以让我首先了解管道的工作原理,更重要的是能够可视化数据和训练结果,因此更容易弄清楚如何改进模型。在安装完所有东西、启动 Jupyter Notebook 并确保Tensorflow 正在使用 GPU之后,是时候从 donkey 库中复制训练管道了。

驴库使用 Tub 对象类来处理和格式化数据,然后再传递给模型进行训练。我基本上将 Tub 类中的每个方法复制到 Jupyter Notebook 单元格中,以便单独理解它们。在丢弃了一些在这种情况下没有用的方法之后,这是我留下的代码。

这里需要注意的是,该库使用称为数据生成器的东西来创建模型的批次,而无需加载整个数据集。


模型实验

现在进入有趣的部分,这些是我为找到更好的模型而采取的步骤。如果您在这里查看代码,请滚动到底部。我认为会有所帮助的一件事是使用多张照片而不是只使用一张来进行预测。考虑到汽车暂时偏离轨道或摄像头暂时受阻的情况,这更可靠。使用多帧的另一个原因是我想看看它是否不仅可以从图片中推断出空间信息,还可以推断出时间信息,以便更主动地进行转向。

起初,我认为结合循环过滤器的 LSTM(长短期记忆)网络效果最好,因为数据的循环性质。然而,我建立的几个初始模型在训练期间并没有收敛。在弄清楚原因的过程中,我还研究了 3D CNN(卷积神经网络)架构,发现它可能更合适,因为它可以同时处理时间维度和空间维度,而无需扁平化为一个首先是一维数组。我认为 LSTM 网络效果不佳,因为它很难解释来自上游 CNN 网络的空间模式,因此在此过程中,大部分信息在到达最终的全连接层进行解释之前就丢失了。

我还尝试使用 Keras 中的 ConLSTM2D 层,该层根据其论文 ( Shi et al 2015 ) 专门设计用于处理时空学习。这个内置层似乎非常适合这个应用程序,但是,我在实现它时并没有太大的成功。

在尝试将多个帧合并到输入中时,我使用了一个 2DTimeDistributed 层,该层围绕 Keras 中的另一个顺序模型,在本例中是一个小型 2D CNN。我发现 Jason Brownlee 的博客在解释这一层的实际实现方面非常有用。实际上,我使用这种方法取得了一些成功,它后面有一个完全连接的层。与原始模型相比,损失有所改进,这令人惊讶,因为没有直接的结构来利用其时间信息。

然后我开始从论文(下面的链接)中了解更多关于 3D CNN 的信息,并意识到它非常适合此目的并及时实施。在馈入全连接层之前,2DTimeDistributed 层将首先馈入 3D CNN。事实证明,这是对没有 3D CNN 层的最后一个架构的进一步改进。

我发现有用的论文:

但是,当我在汽车中测试模型时,又出现了另一个问题。在功能较弱的 Raspberry Pi 上进行推理花费的时间太长了。事实上,它的速度是原始架构的三倍。这是有道理的,我将三倍的数据输入到输入中,并且它们都必须在 CPU 中由卷积核按顺序处理,因为 Tensorflow 还不能在 Pi 上利用 GPU 加速。

这使我将注意力转向提高神经网络的推理速度。我找不到太多关于提高树莓派推理速度的文章,所以我必须自己做实验。

我开始剥离这些层以加快速度,并意识到主要耗时的部分之一是架构的开始,其中每个图像在合并在一起之前分别应用卷积层的层。我最初的意图是为了让网络更好地学习时间信息,我不应该太快地将帧合并在一起,让组成层首先提取一些空间特征。但由于性能的原因,我想我必须尽快将帧合并在一起。我决定一开始就使用 3D CNN 层将所有三个帧合并在一起(后来我意识到这类似于Karpathy et al 2015中的 Early Fusion 方法)。这最终使它更快。另一个有助于显着提高速度的事情是按照原始驴库中的示例中的建议从图像中裁剪前 60 个像素。

真正让我惊讶的一件事是,我实际上需要多少层才能实现相当好的损失(在验证集上),这远低于原始架构产生的损失函数(在这种情况下越低越好)。最终,我能够获得一个相当好的结果,其中只有一个 4 个内核的 3DCNN 层,然后是一个全连接层,然后拆分为角度和油门层,它们本身就是全连接层。这个模型实际上在推理速度上比原始的 CNN 模型运行得更快:

  1. #3DCNN base model
  2. img_in3D = Input(shape=(3, 120, 160, 3), name='img_in')
  3. x = img_in3D x = Cropping3D(cropping = ((0, 0),(60, 0),(0, 0)))(x)
  4. x = Convolution3D(8, (3, 3, 3), strides=(1, 2, 2), activation='relu')(x)
  5. x = MaxPooling3D(pool_size=(1, 2, 2))(x)
  6. x = BatchNormalization()(x) x = Dropout(0.1)(x)  
  7. x = Flatten(name='flattened')(x)
  8. x = Dense(50, activation='relu')(x)
  9. x = Dropout(0.2)(x)
  10. angle_out = Dense(15, activation='softmax', name='angle_out')(x)
  11. throttle_out = Dense(1, activation='relu', name='throttle_out')(x)
  12. model = Model(inputs=[img_in3D], outputs=[angle_out, throttle_out])
  13. model.compile(optimizer='adam', loss={'angle_out':'categorical_crossentropy',
  14. 'throttle_out': 'mean_absolute_error'}, loss_weights={'angle_out': 0.9, 'throttle_out': 0.01})
复制代码

测试

我为现在更快的模型创建了更多变体,以查看是否可以进一步提高准确性。因为我正在根据验证集调整模型的参数,所以表现良好的模型可能会过度拟合该验证集。这就是为什么要真正评估所有模型的有效性,需要一个包含从未用于训练的数据的测试集。

我创建的测试集由同一轨道上的一组全新记录数据组成。下表显示了基础架构变体的测试结果,为了比较,我还包括了在相同数据上训练的 Donkeycar 库中原始 CNN 模型的测试结果。除了一个 3D CNN 变体之外,其他所有变体都在转向(Angle_out_loss)轮流中击败了默认架构,这是我的主要关注点。这里的另一个惊喜是具有更深或更多内核的大型网络并没有获得更好的测试结果。由于验证损失在更大的网络上确实变得更好,我怀疑它们只是更容易过度拟合训练数据。


SatApril-202204167496..png

在 64,000 个测试集示例上测试的模型

从上到下,基本模型的衍生品:

  • base_reference:具有 1 个 3DCNN 层的基本模型,具有 4 个形状 = (3, 5, 5),步幅 = (1, 4, 4) 的过滤器。下面的代码
  • 2Dense:CNN 之后的 2 个全连接层,而不仅仅是 1 个
  • skipFrame xn:不是用最后三个连续的帧进行训练,而是用彼此相隔n帧的最后三个帧进行训练
  • n kernels:将 3D CNN 层中的过滤器数量增加到 n
  • f333s122:使用 shape = (3, 3, 3) 和 strides = (1, 2, 2) 的过滤器
  • mypilot5:DonkeyCar 默认 Keras 模型
  • skipFramex5_8Kernels_f355_s122:结合#3、#4和#5

更好的测试结果意味着汽车可以根据记录的数据更好地预测我的驾驶行为,但这是否意味着它可以更好地自行驾驶?是时候进行路试了!(视频即将发布)在实际路测中,使用多帧输入似乎确实有助于减少转向抖动,从而减少车辆在转弯时偏离轨道的机会。Pi 上增加的推理速度也意味着它现在具有更快的转向响应。然而,一个问题仍然存在,这种良好的行为仍然仅限于缓慢的车速。当速度增加时,它无法概括,这会导致在汽车开始偏离轨道之前转向变得更加笨拙。


要点、后续步骤和源代码

我犯的一些错误

  • 我没有考虑这样一个事实,即在使用多帧进行训练时,验证集和训练由于划分方式而重叠,从而降低了验证集提供的损失信息的有用性。
  • 没有做足够的测试!见上文,如果我早点使用测试集,我可能会发现并纠正这个错误

到目前为止我学到的东西:

  • 卷积层可以比全连接层消耗更多的计算能力,因此会严重影响 Raspberry Pi 等功能较弱的设备的性能
  • 在训练新模型之前做足够的测试,如果我之前使用测试集进行了测试,我会发现在这种情况下增加网络的深度或宽度并没有取得更好的结果,我会花更多的时间尝试一些东西否则或深入挖掘以找出原因。
  • 手动查看数据帮助我了解神经网络在哪里以及如何失败,以便我可以提出更好的解决方案

很有帮助的博文


下一步

  • 进一步研究和分析来自新模型的数据,并找出每种结构的优势
  • 构建视觉里程表以构建赛道地图
  • 研究如何以更高的速度进行更急转弯以及如何将其推广到以前从未见过的情况
  • 尝试添加更多帧并测量损失改进以及速度性能影响
  • 调查使用先前预测中的反馈循环的有用性
  • 开发一个控制系统,当车辆偏离轨道时允许手动接管,模拟当前处理自动驾驶汽车脱离的方法

这个项目中的所有 Jupiter Notebooks 和 python 脚本都可以在这个 GitHub repo中找到。



极凹甫  初级技师
 楼主|

发表于 2022-4-16 21:29:47

https://github.com/Muosvr/donkey   作者的github地址
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2022 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail