diff --git a/totrans/prac-dl-cld_17.yaml b/totrans/prac-dl-cld_17.yaml index 073a568..f41f376 100644 --- a/totrans/prac-dl-cld_17.yaml +++ b/totrans/prac-dl-cld_17.yaml @@ -4,10 +4,12 @@ prefs: - PREF_H1 type: TYPE_NORMAL + zh: 第16章。使用Keras进行端到端深度学习模拟自动驾驶汽车 - en: 'Contributed by guest authors: Aditya Sharma and Mitchell Spryn' id: totrans-1 prefs: [] type: TYPE_NORMAL + zh: 由客座作者Aditya Sharma和Mitchell Spryn贡献 - en: 'From the Batmobile to Knightrider, from robotaxis to autonomous pizza delivery, self-driving cars have captured the imagination of both modern pop culture and the mainstream media. And why wouldn’t they? How often does it happen that a science-fiction @@ -18,6 +20,7 @@ id: totrans-2 prefs: [] type: TYPE_NORMAL + zh: 从蝙蝠车到骑士骑手,从机器人出租车到自动送披萨,自动驾驶汽车已经吸引了现代流行文化和主流媒体的想象力。为什么不呢?科幻概念何时会变为现实?更重要的是,自动驾驶汽车承诺解决当今城市社会面临的一些关键挑战:道路事故、污染水平、城市越来越拥挤、在交通中浪费的生产力小时数等等。 - en: 'It should come as no surprise that a full self-driving system is quite complex to build, and cannot be tackled in one book chapter. Much like an onion, any complex problem contains layers that can be peeled. In our case, we intend to tackle one @@ -27,24 +30,29 @@ id: totrans-3 prefs: [] type: TYPE_NORMAL + zh: 毫无疑问,一个完整的自动驾驶系统构建起来相当复杂,不可能在一章中解决。就像一个洋葱一样,任何复杂的问题都包含可以剥离的层。在我们的情况下,我们打算解决一个基本问题:如何转向。更好的是,我们不需要一辆真正的汽车来做到这一点。我们将训练一个神经网络在模拟环境中自主驾驶我们的汽车,使用逼真的物理和视觉效果。 - en: But first, a brief bit of history. id: totrans-4 prefs: [] type: TYPE_NORMAL + zh: 但首先,简短的历史。 - en: '![The SAE Levels of Driving Automation (image source)](../images/00220.jpeg)' id: totrans-5 prefs: [] type: TYPE_IMG + zh: '![SAE驾驶自动化级别(图片来源)](../images/00220.jpeg)' - en: Figure 16-1\. The SAE Levels of Driving Automation ([image source](https://oreil.ly/RHKUt)) id: totrans-6 prefs: - PREF_H6 type: TYPE_NORMAL + zh: 图16-1。SAE驾驶自动化级别(图片来源) - en: A Brief History of Autonomous Driving id: totrans-7 prefs: - PREF_H1 type: TYPE_NORMAL + zh: 自动驾驶的简史 - en: Even though it might seem like autonomous driving is a very recent technological development, research in this field originated more than 30 years ago. Scientists at Carnegie Mellon University first tried their hands at this seemingly impossible @@ -68,20 +76,27 @@ id: totrans-8 prefs: [] type: TYPE_NORMAL + zh: 尽管自动驾驶似乎是一个非常近期的技术发展,但这个领域的研究起源于30多年前。卡内基梅隆大学的科学家们在1984年首次尝试了这个看似不可能的壮举,当时他们开始研究后来被称为Navlab + 1的项目。 “Navlab”是卡内基梅隆大学导航实验室的简称,这个名字并不花哨,但它将成为人类未来非常令人印象深刻的第一步。Navlab 1是一辆雪佛兰面包车,车上装有硬件机架,其中包括研究团队的工作站和Sun + Microsystems超级计算机等设备。当时它被认为是尖端技术,是一个自包含的移动计算系统。当时远程无线通信几乎不存在,云计算甚至还不是一个概念。把整个研究团队放在一辆高度实验性的自动驾驶面包车后面可能看起来像是一个危险的想法,但Navlab的最高时速只有20英里,只在空旷的道路上行驶。这确保了工作在车上的科学家们的安全。但非常令人印象深刻的是,这辆车使用的技术为今天的自动驾驶汽车所用的技术奠定了基础。例如,Navlab + 1使用视频摄像头和测距仪(激光雷达的早期版本)来观察周围环境。为了导航,他们使用了单层神经网络来根据传感器数据预测转向角度。相当不错,不是吗? - en: '![The NavLab 1 in all its glory (image source)](../images/00006.jpeg)' id: totrans-9 prefs: [] type: TYPE_IMG + zh: '![NavLab 1的全貌(图片来源)](../images/00006.jpeg)' - en: Figure 16-2\. The NavLab 1 in all its glory ([image source](https://oreil.ly/b2Bnn)) id: totrans-10 prefs: - PREF_H6 type: TYPE_NORMAL + zh: 图16-2。NavLab 1的全貌(图片来源) - en: Deep Learning, Autonomous Driving, and the Data Problem id: totrans-11 prefs: - PREF_H1 type: TYPE_NORMAL + zh: 深度学习、自动驾驶和数据问题 - en: Even 35 years ago scientists knew that neural networks were going to play a key role in making self-driving cars a reality. Back then, of course, we did not have the technology needed (GPUs, cloud computing, FPGAs, etc.) to train and deploy @@ -91,27 +106,33 @@ id: totrans-12 prefs: [] type: TYPE_NORMAL + zh: 即使35年前科学家们就知道神经网络将在实现自动驾驶汽车方面发挥关键作用。当时,当然我们没有所需的技术(GPU、云计算、FPGA等)来训练和部署大规模的深度神经网络,以使自动驾驶成为现实。今天的自动驾驶汽车使用深度学习来执行各种任务。表16-1列出了一些示例。 - en: Table 16-1\. Examples of deep learning applications in self-driving cars id: totrans-13 prefs: [] type: TYPE_NORMAL + zh: 表16-1。深度学习在自动驾驶汽车中的应用示例 - en: '| **Task** | **Examples** |' id: totrans-14 prefs: [] type: TYPE_TB + zh: '| **任务** | **示例** |' - en: '| --- | --- |' id: totrans-15 prefs: [] type: TYPE_TB + zh: '| --- | --- |' - en: '| **Perception** | Detect drivable area, lane markings, intersections, crosswalks, etc. |' id: totrans-16 prefs: [] type: TYPE_TB + zh: '| **感知** | 检测可驾驶区域、车道标线、交叉口、人行横道等 |' - en: '| Detect other vehicles, pedestrians, animals, objects on the road, etc. |' id: totrans-17 prefs: [] type: TYPE_TB + zh: '| 检测其他车辆、行人、动物、道路上的物体等 |' - en: '| Detect traffic signs and signals |' id: totrans-18 prefs: [] @@ -551,15 +572,18 @@ id: totrans-76 prefs: [] type: TYPE_NORMAL + zh: 如果我是汽车,看着我的摄像头呈现给我的这张图像,我可以将其分为三个不同的部分([图16-6](part0019.html#the_three_parts_of_the_image_as_seen_by))。首先是下面的第三部分,看起来更或多少一致。它主要由直线(道路、车道分隔线、道路围栏等)组成,颜色一致,白色、黑色、灰色和棕色。底部还有一个奇怪的黑色弧线(这是汽车的引擎盖)。图像的上面第三部分,与下面的第三部分一样,也是一致的。主要是灰色和白色(天空和云)。最后,中间的第三部分有很多事情发生。有大的棕色和灰色形状(山),它们完全不一致。还有四个高大的绿色图形(树),它们的形状和颜色与我看到的其他任何东西都不同,所以它们一定非常重要。随着我看到更多的图像,上面的第三部分和下面的第三部分并没有太大变化,但中间的第三部分发生了很多变化。因此,我得出结论,这是我应该最关注的部分。 - en: '![The three parts of the image as seen by the self-driving car](../images/00091.jpeg)' id: totrans-77 prefs: [] type: TYPE_IMG + zh: '![自动驾驶汽车所看到的图像的三个部分](../images/00091.jpeg)' - en: Figure 16-6\. The three parts of the image as seen by the self-driving car id: totrans-78 prefs: - PREF_H6 type: TYPE_NORMAL + zh: 图16-6. 自动驾驶汽车所看到的图像的三个部分 - en: Do you see the challenge in front of our car? It has no way of knowing what part of the image being presented to it is important. It might try to associate the changing steering angles with the changing scenery instead of focusing on @@ -572,6 +596,7 @@ id: totrans-79 prefs: [] type: TYPE_NORMAL + zh: 你看到我们汽车面临的挑战了吗?它无法知道呈现给它的图像的哪一部分是重要的。它可能会尝试将转向角度的变化与景观的变化联系起来,而不是专注于道路的转弯,这与其他事物相比非常微妙。景观的变化不仅令人困惑,而且与我们正在解决的问题毫不相关。尽管天空大部分时间都没有变化,但它也不提供与我们汽车转向相关的任何信息。最后,每张照片中捕捉到的引擎盖部分也是不相关的。 - en: 'Let us fix this by focusing only on the relevant portion of the image. We do this by creating a region of interest (ROI) in our image. As we can imagine, there is no hard-and-fast rule to dictate what our ROI should look like. It really depends @@ -581,6 +606,7 @@ id: totrans-80 prefs: [] type: TYPE_NORMAL + zh: 让我们通过仅关注图像的相关部分来解决这个问题。我们通过在图像中创建一个感兴趣区域(ROI)来实现这一点。正如我们可以想象的那样,没有一个硬性规则来规定我们的ROI应该是什么样子的。这真的取决于我们的用例。对于我们来说,因为摄像头处于固定位置,道路上没有高度变化,ROI是一个简单的矩形,专注于道路和道路边界。我们可以通过运行以下代码片段来查看ROI: - en: '[PRE5]' id: totrans-81 prefs: [] @@ -590,26 +616,31 @@ id: totrans-82 prefs: [] type: TYPE_NORMAL + zh: 我们应该看到[图16-7](part0019.html#the_roi_for_our_car_to_focus_on_during_t)中显示的输出。 - en: '![The ROI for our car to focus on during training](../images/00109.jpeg)' id: totrans-83 prefs: [] type: TYPE_IMG + zh: '![我们的汽车在训练期间应关注的ROI](../images/00109.jpeg)' - en: Figure 16-7\. The ROI for our car to focus on during training id: totrans-84 prefs: - PREF_H6 type: TYPE_NORMAL + zh: 图16-7. 我们的汽车在训练期间应关注的ROI - en: Our model will focus only on the road now and not be confused by anything else going on in the environment. This also reduces the size of the image by half, which will make training our neural network easier. id: totrans-85 prefs: [] type: TYPE_NORMAL + zh: 我们的模型现在将只关注道路,不会被环境中其他任何事情所困扰。这也将图像的大小减少了一半,这将使我们的神经网络训练更容易。 - en: Data Augmentation id: totrans-86 prefs: - PREF_H2 type: TYPE_NORMAL + zh: 数据增强 - en: As mentioned earlier, it is always better to have as much data as we can get our hands on while training deep learning models. We have seen in previous chapters the importance of data augmentation and how it helps to not only get more training @@ -625,6 +656,7 @@ id: totrans-87 prefs: [] type: TYPE_NORMAL + zh: 正如前面提到的,在训练深度学习模型时,尽可能获得尽可能多的数据总是更好的。我们已经在之前的章节中看到了数据增强的重要性,以及它如何帮助我们不仅获得更多的训练数据,还可以避免过拟合。然而,先前讨论的大多数用于图像分类的数据增强技术对于我们当前的自动驾驶问题并不适用。让我们以旋转为例。随机将图像旋转20度在训练智能手机相机分类器时非常有帮助,但是我们汽车引擎盖上的摄像头固定在一个位置,永远不会看到旋转的图像(除非我们的汽车在翻转,那时我们有更大的问题)。随机移位也是如此;我们在驾驶时永远不会遇到这些。然而,对于我们当前的问题,我们可以做一些其他事情来增强数据。 - en: Looking closely at our image, we can observe that flipping it on the y-axis produces an image that could have easily come from a different test run ([Figure 16-8](part0019.html#flipping_image_on_the_y-axis)). We can effectively double the size of our dataset if we used the flipped versions @@ -635,15 +667,18 @@ id: totrans-88 prefs: [] type: TYPE_NORMAL + zh: 仔细观察我们的图像,我们可以看到在y轴上翻转它会产生一个看起来很容易来自不同测试运行的图像([图16-8](part0019.html#flipping_image_on_the_y-axis))。如果我们使用图像的翻转版本以及它们的常规版本,我们可以有效地将数据集的大小加倍。以这种方式翻转图像将要求我们同时修改与之相关的转向角。因为我们的新图像是原始图像的反射,所以我们只需改变相应转向角的符号(例如,从0.212变为-0.212)。 - en: '![Flipping image on the y-axis](../images/00048.jpeg)' id: totrans-89 prefs: [] type: TYPE_IMG + zh: '![在y轴上翻转图像](../images/00048.jpeg)' - en: Figure 16-8\. Flipping image on the y-axis id: totrans-90 prefs: - PREF_H6 type: TYPE_NORMAL + zh: 图16-8。在y轴上翻转图像 - en: There is another thing we can do to augment our data. Autonomous cars need to be ready for not only changes on the roads, but also for changes in external conditions like weather, time of day, and available light. Most simulators available today @@ -655,20 +690,24 @@ id: totrans-91 prefs: [] type: TYPE_NORMAL + zh: 我们可以做另一件事来增加我们的数据。自动驾驶汽车不仅需要准备好应对道路上的变化,还需要准备好应对外部条件的变化,如天气、时间和光照条件。今天大多数可用的模拟器都允许我们合成这些条件。我们所有的数据都是在明亮的光照条件下收集的。我们可以在训练过程中通过调整图像亮度引入随机光照变化,而不是返回模拟器收集更多不同光照条件下的数据。[图16-9](part0019.html#reducing_image_brightness_by_40percent)展示了一个例子。 - en: '![Reducing image brightness by 40%](../images/00075.jpeg)' id: totrans-92 prefs: [] type: TYPE_IMG + zh: '![将图像亮度降低40%](../images/00075.jpeg)' - en: Figure 16-9\. Reducing image brightness by 40% id: totrans-93 prefs: - PREF_H6 type: TYPE_NORMAL + zh: 图16-9。将图像亮度降低40% - en: Dataset Imbalance and Driving Strategies id: totrans-94 prefs: - PREF_H2 type: TYPE_NORMAL + zh: 数据集不平衡和驾驶策略 - en: Deep learning models are only as good as the data on which they are trained. Unlike humans, the AI of today cannot think and reason for itself. Hence, when presented with a completely new situation it will fall back on what it has seen @@ -679,6 +718,7 @@ id: totrans-95 prefs: [] type: TYPE_NORMAL + zh: 深度学习模型的好坏取决于它们所训练的数据。与人类不同,今天的人工智能无法自行思考和推理。因此,当面对完全新的情况时,它会回归到以前看到的内容,并基于其训练的数据集进行预测。此外,深度学习的统计特性使其忽略那些不经常发生的实例,将其视为异常值和离群值,即使它们具有重要意义。这被称为*数据集不平衡*,对数据科学家来说是一个常见的困扰。 - en: 'Imagine that we are trying to train a classifier that looks at dermatoscopic images to detect whether a lesion is benign or malignant. Suppose that our dataset has one million images, 10,000 of which contain lesions that are malignant. It @@ -691,6 +731,7 @@ id: totrans-96 prefs: [] type: TYPE_NORMAL + zh: 想象一下,我们正在尝试训练一个分类器,该分类器查看皮肤镜图像以检测病变是良性还是恶性。假设我们的数据集有一百万张图像,其中有1万张包含恶性病变。很可能我们在这些数据上训练的分类器总是预测图像是良性的,从不预测是恶性的。这是因为深度学习算法旨在在训练过程中最小化错误(或最大化准确性)。通过将所有图像分类为良性,我们的模型达到了99%的准确性,然后结束训练,严重失败于其训练的任务:检测癌症病变。这个问题通常通过在更平衡的数据集上训练预测器来解决。 - en: Autonomous driving is not immune to the problem of dataset imbalance. Let’s take our steering-angle prediction model. What do we know about steering angles from our day-to-day driving experience? While driving down a road we mostly drive @@ -706,11 +747,13 @@ id: totrans-97 prefs: [] type: TYPE_NORMAL + zh: 自动驾驶也无法免于数据集不平衡的问题。让我们以我们的转向角预测模型为例。从我们日常驾驶经验中,我们对转向角了解多少?在驾驶过程中,我们大多数时间都是直线行驶。如果我们的模型只是在正常驾驶数据上进行训练,它将永远不会学会如何转弯,因为在训练数据集中相对稀缺。为了解决这个问题,我们的数据集不仅需要包含正常驾驶策略的数据,还需要包含“转弯”驾驶策略的数据,数量上具有统计学意义。为了说明我们的意思,让我们回到我们的*.tsv*/*.txt*文件。在本章的前面,我们指出了数据文件夹的命名。现在应该清楚了,我们的数据集包含了使用正常驾驶策略进行的六次收集运行和使用转弯驾驶策略进行的三次收集运行的数据。 - en: 'Let us aggregate the data from all *.tsv*/*.txt* files into a single DataFrame to make the analysis easier:' id: totrans-98 prefs: [] type: TYPE_NORMAL + zh: 让我们将所有*.tsv*/*.txt*文件中的数据聚合到一个DataFrame中,以便更容易进行分析: - en: '[PRE6]' id: totrans-99 prefs: [] @@ -720,6 +763,7 @@ id: totrans-100 prefs: [] type: TYPE_NORMAL + zh: 让我们在散点图上绘制来自两种驾驶策略的转向角: - en: '[PRE7]' id: totrans-101 prefs: [] @@ -730,19 +774,23 @@ id: totrans-102 prefs: [] type: TYPE_NORMAL + zh: '[图16-10](part0019.html#plot_showing_steering_angles_from_the_tw)显示了结果。' - en: '![Plot showing steering angles from the two driving strategies](../images/00299.jpeg)' id: totrans-103 prefs: [] type: TYPE_IMG + zh: '![显示两种驾驶策略的转向角的图](../images/00299.jpeg)' - en: Figure 16-10\. Plot showing steering angles from the two driving strategies id: totrans-104 prefs: - PREF_H6 type: TYPE_NORMAL + zh: 图16-10。显示两种驾驶策略的转向角的图 - en: 'Let’s also plot the number of data points collected with each strategy:' id: totrans-105 prefs: [] type: TYPE_NORMAL + zh: 让我们也绘制一下使用每种策略收集的数据点数量: - en: '[PRE8]' id: totrans-106 prefs: [] @@ -753,15 +801,18 @@ id: totrans-107 prefs: [] type: TYPE_NORMAL + zh: '[图16-11](part0019.html#dataset_split_for_the_two_driving_strate)显示了这些结果。' - en: '![Dataset split for the two driving strategies](../images/00114.jpeg)' id: totrans-108 prefs: [] type: TYPE_IMG + zh: '![两种驾驶策略的数据集拆分](../images/00114.jpeg)' - en: Figure 16-11\. Dataset split for the two driving strategies id: totrans-109 prefs: - PREF_H6 type: TYPE_NORMAL + zh: 图16-11。两种驾驶策略的数据集拆分 - en: Looking at [Figure 16-10](part0019.html#plot_showing_steering_angles_from_the_tw), as we had expected, we see that the normal driving strategy produces steering angles that we would observe during everyday driving, mostly straight on the road @@ -774,12 +825,14 @@ id: totrans-110 prefs: [] type: TYPE_NORMAL + zh: 查看[图16-10](part0019.html#plot_showing_steering_angles_from_the_tw),正如我们所预期的,我们看到正常驾驶策略产生的转向角大多数是我们在日常驾驶中观察到的,大部分是直行,偶尔转弯。相比之下,突然转向驾驶策略主要集中在急转弯,因此转向角的值较高。如[图16-11](part0019.html#dataset_split_for_the_two_driving_strate)所示,这两种策略的结合给我们一个不错的、虽然在现实生活中不太现实的分布,以75/25的比例进行训练。这也进一步巩固了模拟对自动驾驶的重要性,因为在现实生活中我们不太可能使用实际汽车进行突然转向策略来收集数据。 - en: 'Before closing our discussion on data preprocessing and dataset imbalance, let’s take a final look at the distribution of our steering angles for the two driving strategies by plotting them on a histogram ([Figure 16-12](part0019.html#steering_angle_distribution_for_the_two)):' id: totrans-111 prefs: [] type: TYPE_NORMAL + zh: '在结束关于数据预处理和数据集不平衡的讨论之前,让我们最后再看一下我们两种驾驶策略的转向角分布,通过在直方图上绘制它们([图16-12](part0019.html#steering_angle_distribution_for_the_two)): ' - en: '[PRE9]' id: totrans-112 prefs: [] @@ -789,11 +842,13 @@ id: totrans-113 prefs: [] type: TYPE_IMG + zh: '![两种驾驶策略的转向角分布](../images/00055.jpeg)' - en: Figure 16-12\. Steering angle distribution for the two driving strategies id: totrans-114 prefs: - PREF_H6 type: TYPE_NORMAL + zh: 图16-12。两种驾驶策略的转向角分布 - en: As noted earlier, the swerve driving strategy gives us a much broader range of angles compared to the normal driving strategy, as shown in [Figure 16-12](part0019.html#steering_angle_distribution_for_the_two). These angles will help our neural network react appropriately in case it ever @@ -806,6 +861,7 @@ id: totrans-115 prefs: [] type: TYPE_NORMAL + zh: 正如前面所述,与正常驾驶策略相比,突然转向驾驶策略给我们提供了更广泛的角度范围,如[图16-12](part0019.html#steering_angle_distribution_for_the_two)所示。这些角度将帮助我们的神经网络在汽车偏离道路时做出适当的反应。我们数据集中的不平衡问题在一定程度上得到解决,但并非完全解决。我们仍然有很多零,跨越两种驾驶策略。为了进一步平衡我们的数据集,我们可以在训练过程中忽略其中的一部分。尽管这给我们提供了一个非常平衡的数据集,但大大减少了我们可用的数据点总数。在构建和训练神经网络时,我们需要记住这一点。 - en: 'Before moving on, let’s make sure our data is suitable for training. Let’s take the raw data from all folders, split it into train, test, and validation datasets, and compress them into HDF5 files. The HDF5 format allows us to access @@ -816,6 +872,7 @@ id: totrans-116 prefs: [] type: TYPE_NORMAL + zh: 在继续之前,让我们确保我们的数据适合训练。让我们从所有文件夹中获取原始数据,将其分割为训练、测试和验证数据集,并将它们压缩成HDF5文件。HDF5格式允许我们以块的方式访问数据,而不需要一次性将整个数据集读入内存。这使其非常适合深度学习问题。它还可以与Keras无缝配合。以下代码将需要一些时间来运行。当运行完成后,我们将得到三个数据集文件:*train.h5*、*eval.h5*和*test.h5*。 - en: '[PRE10]' id: totrans-117 prefs: [] @@ -825,56 +882,68 @@ id: totrans-118 prefs: [] type: TYPE_NORMAL + zh: 每个数据集文件有四个部分: - en: Image id: totrans-119 prefs: [] type: TYPE_NORMAL + zh: 图像 - en: A NumPy array containing the image data. id: totrans-120 prefs: [] type: TYPE_NORMAL + zh: 包含图像数据的NumPy数组。 - en: Previous_state id: totrans-121 prefs: [] type: TYPE_NORMAL + zh: 上一个状态 - en: A NumPy array containing the last known state of the car. This is a (steering, throttle, brake, speed) tuple. id: totrans-122 prefs: [] type: TYPE_NORMAL + zh: 包含汽车最后已知状态的NumPy数组。这是一个(转向、油门、刹车、速度)元组。 - en: Label id: totrans-123 prefs: [] type: TYPE_NORMAL + zh: 标签 - en: A NumPy array containing the steering angles that we wish to predict (normalized on the range -1..1). id: totrans-124 prefs: [] type: TYPE_NORMAL + zh: 包含我们希望预测的转向角的NumPy数组(在范围-1..1上进行了标准化)。 - en: Metadata id: totrans-125 prefs: [] type: TYPE_NORMAL + zh: 元数据 - en: A NumPy array containing metadata about the files (which folder they came from, etc.). id: totrans-126 prefs: [] type: TYPE_NORMAL + zh: 包含有关文件的元数据的NumPy数组(它们来自哪个文件夹等)。 - en: We are now ready to take the observations and learnings from our dataset and start training our model. id: totrans-127 prefs: [] type: TYPE_NORMAL + zh: 现在我们已经准备好从数据集中获取观察和经验,并开始训练我们的模型。 - en: Training Our Autonomous Driving Model id: totrans-128 prefs: - PREF_H1 type: TYPE_NORMAL + zh: 训练我们的自动驾驶模型 - en: 'All the steps in this section are also detailed in the Jupyter Notebook [*TrainModel.ipynb*](https://oreil.ly/ESHVS). As before, we begin by importing some libraries and defining paths:' id: totrans-129 prefs: [] type: TYPE_NORMAL + zh: 本节中的所有步骤也在Jupyter Notebook [*TrainModel.ipynb*](https://oreil.ly/ESHVS)中详细说明。与之前一样,我们首先导入一些库并定义路径: - en: '[PRE11]' id: totrans-130 prefs: [] @@ -884,6 +953,7 @@ id: totrans-131 prefs: [] type: TYPE_NORMAL + zh: 让我们也设置好我们的数据集: - en: '[PRE12]' id: totrans-132 prefs: [] @@ -894,6 +964,7 @@ prefs: - PREF_H2 type: TYPE_NORMAL + zh: 驾驶数据生成器 - en: We were introduced to the concept of Keras data generators in previous chapters. A data generator iterates through the dataset and reads data in chunks from the disk. This allows us to keep both our CPU and GPU busy, increasing throughput. @@ -902,21 +973,25 @@ id: totrans-134 prefs: [] type: TYPE_NORMAL + zh: 我们在之前的章节中介绍了Keras数据生成器的概念。数据生成器会遍历数据集,并从磁盘中以块的形式读取数据。这使我们能够让CPU和GPU保持繁忙,提高吞吐量。为了实现前一节讨论的想法,我们创建了自己的Keras生成器,称为`DriveDataGenerator`。 - en: 'Let’s recap some of our observations from the previous section:' id: totrans-135 prefs: [] type: TYPE_NORMAL + zh: 让我们回顾一下前一节中的一些观察结果: - en: Our model should focus only on the ROI within each image. id: totrans-136 prefs: - PREF_UL type: TYPE_NORMAL + zh: 我们的模型应该只关注每个图像中的ROI。 - en: We can augment our dataset by flipping images horizontally and reversing the sign of the steering angle. id: totrans-137 prefs: - PREF_UL type: TYPE_NORMAL + zh: 我们可以通过水平翻转图像并反转转向角的符号来增强我们的数据集。 - en: We can further augment the data by introducing random brightness changes in the images. This will simulate different lighting conditions and make our model more robust. @@ -924,22 +999,26 @@ prefs: - PREF_UL type: TYPE_NORMAL + zh: 我们可以通过在图像中引入随机亮度变化来进一步增强数据。这将模拟不同的光照条件,并使我们的模型更加健壮。 - en: We can randomly drop a percentage of data points where the steering angle is zero so that the model sees a balanced dataset when training. id: totrans-139 prefs: - PREF_UL type: TYPE_NORMAL + zh: 我们可以随机删除一定百分比的转向角为零的数据点,以便在训练时模型看到一个平衡的数据集。 - en: Our overall number of data points available will be reduced significantly post-balancing. id: totrans-140 prefs: - PREF_UL type: TYPE_NORMAL + zh: 我们平衡后可用的数据点总数将显著减少。 - en: Let’s see how the `DriveDataGenerator` takes care of the first four of these items. We will return to last item when we begin designing our neural network. id: totrans-141 prefs: [] type: TYPE_NORMAL + zh: 让我们看看`DriveDataGenerator`如何处理这些前四项。当我们开始设计神经网络时,我们将回到最后一项。 - en: The ROI is a simple image crop. [76,135,0,255] (shown in the code block that follows) is the [x1,x2,y1,y2] value of the rectangle representing the ROI. The generator extracts this rectangle from each image. We can use the parameter `roi` @@ -947,12 +1026,14 @@ id: totrans-142 prefs: [] type: TYPE_NORMAL + zh: ROI是一个简单的图像裁剪。[76,135,0,255](在接下来的代码块中显示)是表示ROI的矩形的[x1,x2,y1,y2]值。生成器从每个图像中提取此矩形。我们可以使用参数`roi`来修改ROI。 - en: The horizontal flip is fairly straightforward. When generating batches, random images are flipped along the y-axis and their steering angle values are reversed if the parameter `horizontal_flip` is set to `True`. id: totrans-143 prefs: [] type: TYPE_NORMAL + zh: 水平翻转相对简单。在生成批次时,随机图像沿y轴翻转,并且如果参数`horizontal_flip`设置为`True`,它们的转向角值将被反转。 - en: For random brightness changes, we introduce the parameter `brighten_range`. Setting this parameter to `0.4` modifies the brightness of images in any given batch randomly up to 40%. We do not recommend increasing this beyond `0.4`. To @@ -961,16 +1042,19 @@ id: totrans-144 prefs: [] type: TYPE_NORMAL + zh: 对于随机亮度变化,我们引入参数`brighten_range`。将此参数设置为`0.4`会随机地使任何给定批次中的图像亮度增加或减少最多40%。我们不建议将此值增加到`0.4`之上。为了计算亮度,我们将图像从RGB转换为HSV空间,将“V”坐标缩放上下,然后转换回RGB。 - en: For dataset balancing through dropping zeros, we introduce the parameter `zero_drop_percentage`. Setting this to `0.9` will randomly drop 90% of the 0-label data points in any given batch. id: totrans-145 prefs: [] type: TYPE_NORMAL + zh: 通过删除零来平衡数据集,我们引入了参数`zero_drop_percentage`。将此设置为`0.9`将在任何给定批次中随机删除90%的0标签数据点。 - en: 'Let’s initialize our generator with these parameters:' id: totrans-146 prefs: [] type: TYPE_NORMAL + zh: 让我们使用这些参数初始化我们的生成器: - en: '[PRE13]' id: totrans-147 prefs: [] @@ -981,6 +1065,7 @@ id: totrans-148 prefs: [] type: TYPE_NORMAL + zh: 让我们通过在相应图像上绘制标签(转向角)来可视化一些样本数据点: - en: '[PRE14]' id: totrans-149 prefs: [] @@ -994,20 +1079,24 @@ id: totrans-150 prefs: [] type: TYPE_NORMAL + zh: 我们应该看到类似于[图16-13](part0019.html#drawing_steering_angles_on_images)的输出。请注意,我们现在在训练模型时只关注ROI,从而忽略原始图像中存在的所有非相关信息。线表示地面真实转向角。这是汽车在摄像机拍摄图像时行驶的角度。 - en: '![Drawing steering angles on images](../images/00182.jpeg)' id: totrans-151 prefs: [] type: TYPE_IMG + zh: '![在图像上绘制转向角](../images/00182.jpeg)' - en: Figure 16-13\. Drawing steering angles on images id: totrans-152 prefs: - PREF_H6 type: TYPE_NORMAL + zh: 图16-13。在图像上绘制转向角 - en: Model Definition id: totrans-153 prefs: - PREF_H2 type: TYPE_NORMAL + zh: 模型定义 - en: We are now ready to define the architecture of our neural network. It is here that we must take into account the problem of our dataset being very limited in size after removing zeros. Because of this limitation, we cannot build a network @@ -1016,6 +1105,7 @@ id: totrans-154 prefs: [] type: TYPE_NORMAL + zh: 现在我们准备定义神经网络的架构。在这里,我们必须考虑到在删除零后,我们的数据集非常有限的问题。因此,我们不能构建太深的网络。由于我们处理的是图像,我们将需要一些卷积/最大池化对来提取特征。 - en: Images alone, however, might not be enough to lead our model to convergence. Using only images to train also does not align with how driving decisions are made in the real world. When driving down the road, we are not only perceiving @@ -1033,6 +1123,7 @@ id: totrans-155 prefs: [] type: TYPE_NORMAL + zh: 然而,仅仅使用图像可能不足以使我们的模型收敛。仅仅使用图像进行训练也不符合现实世界中做驾驶决策的方式。在驾驶时,我们不仅感知周围环境,还意识到我们的速度、转向程度,以及油门和刹车踏板的状态。相机、激光雷达、雷达等传感器输入到神经网络中只对应驾驶员在做驾驶决策时手头上所有信息的一部分。呈现给神经网络的图像可能是从一辆静止的汽车或以60英里/小时行驶的汽车中拍摄的;网络无法知道是哪种情况。在以5英里/小时行驶时将方向盘向右转两度与以50英里/小时行驶时做同样动作将产生非常不同的结果。简而言之,试图预测转向角度的模型不应仅依赖感官输入。它还需要关于汽车当前状态的信息。幸运的是,我们有这些信息可用。 - en: At the end of the previous section, we noted that our datasets have four parts. For each image, in addition to the steering angle label and metadata, we also recorded the last known state of the car corresponding to the image. This was @@ -1043,6 +1134,8 @@ id: totrans-156 prefs: [] type: TYPE_NORMAL + zh: 在上一节的结尾,我们指出我们的数据集有四个部分。对于每个图像,除了转向角标签和元数据外,我们还记录了与图像对应的汽车上次已知状态。这以 (转向、油门、刹车、速度) + 元组的形式存储,我们将使用这些信息以及我们的图像作为神经网络的输入。请注意,这不违反我们的“Hello, World!”要求,因为我们仍然将单个摄像头作为唯一的外部传感器。 - en: Putting all we discussed together, you can see the neural network that we will be using for this problem in [Figure 16-14](part0019.html#network_architecture). We are using three convolutional layers with 16, 32, 32 filters, respectively, @@ -1058,19 +1151,25 @@ id: totrans-157 prefs: [] type: TYPE_NORMAL + zh: 将我们讨论的所有内容放在一起,您可以在 [图16-14](part0019.html#network_architecture) 中看到我们将用于此问题的神经网络。我们使用了三个卷积层,分别具有16、32、32个滤波器,并且使用了 + (3,3) 的卷积窗口。我们将图像特征(从卷积层输出)与提供汽车先前状态的输入层进行合并。然后将组合特征集传递到两个分别具有64和10个隐藏神经元的全连接层中。我们网络中使用的激活函数是 + ReLU。请注意,与我们在前几章中处理的分类问题不同,我们网络的最后一层是一个没有激活的单个神经元。这是因为我们要解决的问题是一个回归问题。我们网络的输出是转向角度,一个浮点数,而不是我们之前预测的离散类别。 - en: '![Network architecture](../images/00141.jpeg)' id: totrans-158 prefs: [] type: TYPE_IMG + zh: '![网络架构](../images/00141.jpeg)' - en: Figure 16-14\. Network architecture id: totrans-159 prefs: - PREF_H6 type: TYPE_NORMAL + zh: 图16-14\. 网络架构 - en: 'Let’s now implement our network. We can use `model.summary()`:' id: totrans-160 prefs: [] type: TYPE_NORMAL + zh: 现在让我们实现我们的网络。我们可以使用 `model.summary()`: - en: '[PRE15]' id: totrans-161 prefs: [] @@ -1081,6 +1180,7 @@ prefs: - PREF_H3 type: TYPE_NORMAL + zh: 回调 - en: 'One of the nice features of Keras is the ability to declare *callbacks*. Callbacks are functions that are executed after each epoch of training and help us to gain an insight into the training process as well as control hyperparameters to an @@ -1090,10 +1190,12 @@ id: totrans-163 prefs: [] type: TYPE_NORMAL + zh: Keras 的一个很好的特性是能够声明 *回调函数*。回调函数在每个训练周期结束后执行,帮助我们深入了解训练过程并在一定程度上控制超参数。它们还让我们定义在训练进行时执行某些操作的条件;例如,如果损失停止减少,则提前停止训练。我们将为我们的实验使用一些回调函数: - en: '`ReduceLROnPlateau`' id: totrans-164 prefs: [] type: TYPE_NORMAL + zh: '`ReduceLROnPlateau`' - en: If the model is near a minimum and the learning rate is too high, the model will circle around that minimum without ever reaching it. This callback will allow the model to reduce its learning rate when the validation loss hits a plateau @@ -1101,38 +1203,46 @@ id: totrans-165 prefs: [] type: TYPE_NORMAL + zh: 如果模型接近最小值且学习率过高,模型将在该最小值周围循环而无法达到它。当验证损失达到平稳期并停止改善时,此回调将允许模型减少学习率,使我们能够达到最佳点。 - en: '`CSVLogger`' id: totrans-166 prefs: [] type: TYPE_NORMAL + zh: '`CSVLogger`' - en: This lets us log the output of the model after each epoch into a CSV file, which will allow us to track the progress without needing to use the console. id: totrans-167 prefs: [] type: TYPE_NORMAL + zh: 这使我们能够将每个周期结束后模型的输出记录到一个 CSV 文件中,这样我们就可以跟踪进展而无需使用控制台。 - en: '`ModelCheckpoint`' id: totrans-168 prefs: [] type: TYPE_NORMAL + zh: '`ModelCheckpoint`' - en: Generally, we will want to use the model that has the lowest loss on the validation set. This callback will save the model each time the validation loss improves. id: totrans-169 prefs: [] type: TYPE_NORMAL + zh: 通常,我们希望使用在验证集上损失最低的模型。此回调将在每次验证损失改善时保存模型。 - en: '`EarlyStopping`' id: totrans-170 prefs: [] type: TYPE_NORMAL + zh: '`EarlyStopping`' - en: We will want to stop training when the validation loss stops improving. Otherwise, we risk overfitting. This option will detect when the validation loss stops improving and will stop the training process when that occurs. id: totrans-171 prefs: [] type: TYPE_NORMAL + zh: 当验证损失不再改善时,我们将停止训练。否则,我们会面临过拟合的风险。此选项将检测验证损失停止改善的时候,并在发生这种情况时停止训练过程。 - en: 'Let’s now go ahead and implement these callbacks:' id: totrans-172 prefs: [] type: TYPE_NORMAL + zh: 现在让我们继续实现这些回调: - en: '[PRE16]' id: totrans-173 prefs: [] @@ -1144,6 +1254,7 @@ id: totrans-174 prefs: [] type: TYPE_NORMAL + zh: 我们现在已经准备好开始训练了。模型需要一段时间来训练,所以这将是一个不错的Netflix休息时间。训练过程应该以约0.0003的验证损失终止: - en: '[PRE17]' id: totrans-175 prefs: [] @@ -1159,6 +1270,7 @@ id: totrans-177 prefs: [] type: TYPE_NORMAL + zh: 我们的模型现在已经训练好并准备就绪。在看到它的表现之前,让我们进行一个快速的合理性检查,并将一些预测绘制在图像上: - en: '[PRE19]' id: totrans-178 prefs: [] @@ -1172,30 +1284,36 @@ id: totrans-179 prefs: [] type: TYPE_NORMAL + zh: 我们应该看到类似于[图16-15](part0019.html#drawing_actual_and_predicted_steering_an)的输出。在这个图中,粗线是预测输出,细线是标签输出。看起来我们的预测相当准确(我们还可以在图像上方看到实际和预测值)。是时候部署我们的模型并看看它的表现了。 - en: '![Drawing actual and predicted steering angles on images](../images/00095.jpeg)' id: totrans-180 prefs: [] type: TYPE_IMG + zh: '![在图像上绘制实际和预测的转向角](../images/00095.jpeg)' - en: Figure 16-15\. Drawing actual and predicted steering angles on images id: totrans-181 prefs: - PREF_H6 type: TYPE_NORMAL + zh: 图16-15。在图像上绘制实际和预测的转向角 - en: Deploying Our Autonomous Driving Model id: totrans-182 prefs: - PREF_H1 type: TYPE_NORMAL + zh: 部署我们的自动驾驶模型 - en: All of the steps in this section are also detailed in the Jupyter Notebook [*TestModel.ipynb*](https://oreil.ly/5saDl). Now that we have our model trained, it is time to spin up our simulator and use our model to drive our car around. id: totrans-183 prefs: [] type: TYPE_NORMAL + zh: 本节中的所有步骤也在Jupyter Notebook [*TestModel.ipynb*](https://oreil.ly/5saDl)中有详细说明。现在我们的模型已经训练好了,是时候启动模拟器并使用我们的模型驾驶汽车了。 - en: 'As before, begin by importing some libraries and defining paths:' id: totrans-184 prefs: [] type: TYPE_NORMAL + zh: 与之前一样,首先导入一些库并定义路径: - en: '[PRE20]' id: totrans-185 prefs: [] @@ -1207,6 +1325,7 @@ id: totrans-186 prefs: [] type: TYPE_NORMAL + zh: 接下来,加载模型并在景观环境中连接到AirSim。要启动模拟器,在Windows机器上,打开一个PowerShell命令窗口,位于我们解压缩模拟器包的位置,并运行以下命令: - en: '[PRE21]' id: totrans-187 prefs: [] @@ -1217,6 +1336,7 @@ id: totrans-188 prefs: [] type: TYPE_NORMAL + zh: 在Jupyter Notebook中,运行以下命令将模型连接到AirSim客户端。确保模拟器在启动此过程之前已经运行: - en: '[PRE22]' id: totrans-189 prefs: [] @@ -1227,6 +1347,7 @@ id: totrans-190 prefs: [] type: TYPE_NORMAL + zh: 连接建立后,让我们现在设置汽车的初始状态以及用于存储模型输出的一些缓冲区: - en: '[PRE23]' id: totrans-191 prefs: [] @@ -1237,6 +1358,7 @@ id: totrans-192 prefs: [] type: TYPE_NORMAL + zh: 下一步是设置模型期望从模拟器接收RGB图像作为输入。我们需要为此定义一个辅助函数: - en: '[PRE24]' id: totrans-193 prefs: [] @@ -1250,6 +1372,7 @@ id: totrans-194 prefs: [] type: TYPE_NORMAL + zh: 最后,让我们为我们的模型设置一个无限循环,从模拟器中读取图像以及汽车的当前状态,预测转向角,并将其发送回模拟器。因为我们的模型只预测转向角,所以我们需要提供一个控制信号来自行维持速度。让我们设置这样一个控制信号,使汽车尝试以恒定的5m/s速度行驶: - en: '[PRE25]' id: totrans-195 prefs: [] @@ -1259,6 +1382,7 @@ id: totrans-196 prefs: [] type: TYPE_NORMAL + zh: 我们应该看到类似于以下的输出: - en: '[PRE26]' id: totrans-197 prefs: [] @@ -1271,15 +1395,18 @@ id: totrans-198 prefs: [] type: TYPE_NORMAL + zh: 我们成功了!汽车在道路上很好地行驶,大部分时间保持在右侧,小心地穿过所有急转弯和潜在的偏离道路的地方。恭喜我们训练出了我们的第一个自动驾驶模型! - en: '![Trained model driving the car](../images/00232.jpeg)' id: totrans-199 prefs: [] type: TYPE_IMG + zh: '![训练模型驾驶汽车](../images/00232.jpeg)' - en: Figure 16-16\. Trained model driving the car id: totrans-200 prefs: - PREF_H6 type: TYPE_NORMAL + zh: 图16-16。训练模型驾驶汽车 - en: Before we wrap up, there are a couple of things worth noting. First, notice that the motion of the car is not perfectly smooth. This is because we are working with a regression problem and making a steering angle prediction for every image @@ -1291,6 +1418,7 @@ id: totrans-201 prefs: [] type: TYPE_NORMAL + zh: 在我们结束之前,有几件值得注意的事情。首先,请注意汽车的运动不是完全平滑的。这是因为我们正在处理一个回归问题,并为汽车看到的每个图像帧做出一个转向角预测。解决这个问题的一种方法是在一系列连续的图像上平均预测。另一个想法可能是将其转化为一个分类问题。更具体地,我们可以为转向角定义桶(...,-0.1,-0.05,0,0.05,0.1,...),将标签分桶化,并为每个图像预测正确的桶。 - en: If we let the model run for a while (a little more than five minutes), we observe that the car eventually veers off the road randomly and crashes. This happens on a portion of the track with a steep incline. Remember our last requirement @@ -1300,11 +1428,13 @@ id: totrans-202 prefs: [] type: TYPE_NORMAL + zh: 如果让模型运行一段时间(略长于五分钟),我们会观察到汽车最终会随机偏离道路并撞车。这发生在一个有陡峭上坡的赛道部分。还记得我们在问题设置中的最后一个要求吗?高度变化需要操作油门和刹车。因为我们的模型只能控制转向角,所以在陡峭的道路上表现不佳。 - en: Further Exploration id: totrans-203 prefs: - PREF_H1 type: TYPE_NORMAL + zh: 进一步探索 - en: Having been trained on the “Hello, World!” scenario, our model is obviously not a perfect driver but don’t be disheartened. Keep in mind that we have barely scratched the surface of the possibilities at the intersection of deep learning @@ -1313,17 +1443,20 @@ id: totrans-204 prefs: [] type: TYPE_NORMAL + zh: 在“Hello, World!”场景中训练过后,我们的模型显然不是一个完美的驾驶员,但不要灰心。请记住,我们只是刚刚触及深度学习和自动驾驶汽车交汇点的可能性表面。我们能够让我们的汽车几乎完美地学会驾驶,只使用了一个非常小的数据集,这是值得骄傲的事情! - en: Here are some new ideas for us to build on top of what we learned in this chapter. You can implement all of these ideas using the setup we already have in place for this chapter. id: totrans-205 prefs: [] type: TYPE_NORMAL + zh: 以下是一些新的想法,可以在本章学到的基础上进行扩展。您可以使用本章已经准备好的设置来实现所有这些想法。 - en: Expanding Our Dataset id: totrans-206 prefs: - PREF_H2 type: TYPE_NORMAL + zh: 扩展我们的数据集 - en: As a general rule, using more data helps improve model performance. Now that we have the simulator up and running, it will be a useful exercise to expand our dataset by doing more data collection runs. We can even try combining data from @@ -1332,6 +1465,7 @@ id: totrans-207 prefs: [] type: TYPE_NORMAL + zh: 一般规则是,使用更多的数据有助于提高模型性能。现在我们已经启动并运行了模拟器,通过进行更多的数据收集运行来扩展我们的数据集将是一个有用的练习。我们甚至可以尝试将来自AirSim中各种不同环境的数据结合起来,看看我们在这些数据上训练的模型在不同环境中的表现如何。 - en: We used only RGB data from a single camera in this chapter. AirSim allows us to do a lot more. For example, we can collect images in depth view, segmentation view, surface normal view, and more for each of the cameras available. So, we @@ -1341,11 +1475,13 @@ id: totrans-208 prefs: [] type: TYPE_NORMAL + zh: 在本章中,我们只使用了单个摄像头的RGB数据。AirSim允许我们做更多的事情。例如,我们可以收集深度视图、分割视图、表面法线视图等每个可用摄像头的图像。因此,对于每个实例,我们可能有20个不同的图像(对于四种模式中的所有五个摄像头)。使用所有这些额外数据能帮助我们改进我们刚刚训练的模型吗? - en: Training on Sequential Data id: totrans-209 prefs: - PREF_H2 type: TYPE_NORMAL + zh: 在序列数据上训练 - en: 'Our model currently uses a single image and a single vehicle state for each prediction. This is not really how we drive in real life, though. Our actions always take into account the recent series of events leading up to that given @@ -1357,11 +1493,14 @@ id: totrans-210 prefs: [] type: TYPE_NORMAL + zh: 我们的模型目前对每个预测使用单个图像和单个车辆状态。然而,这并不是我们在现实生活中驾驶的方式。我们的行动总是考虑到导致给定时刻的最近一系列事件。在我们的数据集中,我们有所有图像的时间戳信息可用,我们可以使用这些信息创建序列。我们可以修改我们的模型,使用前 + *N* 个图像和状态进行预测。例如,给定过去的10个图像和过去的10个状态,预测下一个转向角度。(提示:这可能需要使用RNNs。) - en: Reinforcement Learning id: totrans-211 prefs: - PREF_H2 type: TYPE_NORMAL + zh: 强化学习 - en: After we learn about reinforcement learning in the next chapter, we can come back and try the [Distributed Deep Reinforcement Learning for Autonomous Driving](https://oreil.ly/u1hoC) tutorial from the [*Autonomous Driving Cookbook*](https://oreil.ly/bTphH). Using @@ -1372,11 +1511,14 @@ id: totrans-212 prefs: [] type: TYPE_NORMAL + zh: 在下一章学习强化学习之后,我们可以回来尝试[*自动驾驶食谱*](https://oreil.ly/bTphH)中的[Distributed Deep Reinforcement + Learning for Autonomous Driving](https://oreil.ly/u1hoC)教程。使用AirSim中包含的Neighborhood环境,该环境已经包含在我们为本章下载的软件包中,我们将学习如何通过迁移学习和云来扩展深度强化学习训练作业,并将训练时间从不到一周缩短到不到一个小时。 - en: Summary id: totrans-213 prefs: - PREF_H1 type: TYPE_NORMAL + zh: 总结 - en: This chapter gave us a sneak peek into how deep learning enables the autonomous driving industry. Taking advantage of the skills acquired in the previous chapters, we implemented the “Hello, World!” problem of autonomous driving using Keras. @@ -1388,3 +1530,4 @@ id: totrans-214 prefs: [] type: TYPE_NORMAL + zh: 本章让我们一窥深度学习如何推动自动驾驶行业。利用前几章学到的技能,我们使用Keras实现了自动驾驶的“Hello, World!”问题。通过探索手头的原始数据,我们学会了如何预处理数据,使其适合训练高性能模型。而且我们能够用一个非常小的数据集完成这个任务。我们还能够拿出我们训练好的模型,并将其部署到模拟世界中驾驶汽车。您不认为这其中有一些魔力吗?