利用卫星数据进行深度学习
“我们现在正在创造的火箭、卫星、宇宙飞船,
我们正在给宇宙授粉。”——尼尔·杨
概述—卫星数据—数据收集—模型—结果
概述
今年 1 月在意大利贝内文托的桑尼奥大学,我和我的朋友图马斯·奥伊卡里宁(Tuomas Oikarinen)创建了一个(半自动)下载公开图片的管道,并根据数据训练了一个三维卷积神经网络。最终,我们的模型在 Sentinel-2 光学卫星图像上实现了大约 0.65 的平衡精度。我将更详细地介绍结果(以及为什么这个模型实际上是有用的)。在 Silvia Ullo 的指导下,在她的研究生 Maria Pia Del Rosso、Alejandro Sebastianelli 和 Federico Picarillo 的帮助下,我们最终向一个遥感会议提交了一篇论文[1]!
如果你想直接进入代码,这里的是到 Github 库的直接链接。
我将以相对技术性的方式(包括代码)描述如何从 google earth 引擎快速下载一些卫星图像,然后用它们来训练一个 3 维卷积神经网络。
具体来说,我们使用成对的图像(一个滑坡图像,一个相同地理区域但没有滑坡的图像)作为训练示例。然而,这对于涉及使用公共卫星数据作为影像模型的任何情况都是有帮助的。
卫星数据
在开始收集数据之前,有必要大致了解一下我们将要使用的卫星图像的类型。最容易处理和广泛提供的最新卫星数据来自哥白尼卫星计划。构成哥白尼计划的卫星被称为哨兵卫星。为了观察地面的变化,哨兵 1 号和哨兵 2 号卫星是最有用的。Sentinel-1 提供雷达成像和比 Sentinel-2 更高的分辨率,但也更难使用。与 Sentinel-2 图像相比,你必须对雷达图像进行更多的处理,Sentinel-2 图像基本上适合在裁剪和下载后立即使用。我们最终使用 Sentinel-2 的图像来训练我们的模型。
下面是我们能找到的山体滑坡前后卫星图像的最佳例子。

Before the landslide

After the landslide
我们拥有的绝大多数其他图像对都不那么清晰。即使是这个例子,你也可以看到云层覆盖,光照条件和植被颜色都有很大的不同。此外,我们可以找到的所有自动处理滑坡的不同方法都使用了输入支持向量机的特征,如坡度、土地覆盖、坡向、地形湿度指数等(关于这些方法的论文,见此处和此处)。这里我们感兴趣的问题是一个模型能从少量的光学图像中学到任何关于危险探测的东西吗?
数据采集
给我们的标签来自 NASA 全球滑坡目录,记录了 2000 年代初至 2017 年末的滑坡。这是我们必须处理的编辑版本的截屏:

Processed Landslide Catalog
绿色表示好数据,红色表示差数据,灰色表示位置精度太低,无法保证裁剪会捕捉到它。
整个项目的第一个也是最困难的部分是收集标记数据的干净样本。在花了大量时间摆弄 ESA Copernicus SciHub(欧洲航天局用于下载卫星图像的在线用户界面)之后,我们意识到要实现下载过程的自动化是相当困难的。第一个问题是,你不能在网站上的任何地方手动输入坐标,还有一个事实是,你必须下载整个卫星“图像”,在给定的日期,它覆盖了大约三分之一非洲大小的区域。对于每个示例来说,这最终都超过了 7gb;对我们笔记本电脑的存储容量来说太多了。雪上加霜的是,你不得不花半个小时在地理数据处理软件 SNAP 中预处理图像(顺便提一下,这也很难自动化)。
相反,我们求助于谷歌地球引擎,它可以根据日期、作物进行过滤,显示云密度并提供下载链接,只需点击一个按钮!我们确实花了一段时间来弄清楚如何做到这一点,因为缺乏关于地球引擎的例子/稀疏的文档。入门指南确实给出了你可以期待的 Javascript API 的功能概述,所以我仍然推荐从那里开始。
快速提示:你必须注册成为谷歌地球引擎开发者才能访问我们使用的控制台。幸运的是,这个过程非常简单,你应该很快就能获得批准。按照说明在这里报名。
我们的最终(半)自动化流水线是如何结束工作的,如下所示:
- 在
[cropping_coordinates.py](https://github.com/Yichabod/natural_disaster_pred/blob/master/cropping_coordinates.py)中输入坐标,计算一个 10 公里见方的坐标。 - 复制输出并替换 Google Earth 引擎控制台中 javascript 代码顶部的变量(我们使用的 javascript 代码)。不要忘记将日期范围更改为您喜欢的日期范围。
- 单击控制台输出中打印的链接,下载平均云密度小于 100 的图像。
瞧啊。您已经有了预处理过的数据集。

Google Earth Engine Dev Console
(简单的技术说明:对于我们的模型,我们选择对每幅图像使用 12 个可用波段中的 5 个。您可以在 javascript 代码中更改这一点。每个波段捕捉不同的波长光谱,你可以在谷歌地球引擎 Sentinel-2 数据集描述上了解更多信息。
型号

Our CNN
对于我们的模型,我们在 Tensorflow 中定义了一个 8 层 CNN(参见这里的代码)。这没什么特别的。我们从 5 层开始,并注意到随着我们增加更多的层,性能略有提高。与你对常规前馈网络的期望相反(参见这个不错的堆叠交换讨论了解一些背景理论),对于 CNN 的深度没有共识。你只需要试一试。
因为我们最终处理的是大约 120 的图像对,而不是我们想要的 12,000,所以我们增加了几个扩展来使我们的模型更加健壮。首先我们使用tf.random_crop函数随机裁剪图像,然后我们随机翻转和旋转图像的随机部分。我们还尝试在图像中添加随机高斯噪声,但这降低了性能,并真的减慢了训练过程。
结果
我们使用了 5 重交叉验证,这样我们可以使用更多的训练数据,并且仍然可以很好地了解我们的模型在评估集上的表现。

Graph of Accuracy over Epochs
仅仅从光学图像中学习一些东西是可能的,而不需要接触一堆私人的陆地特征!然而,很明显,当验证精度稳定在 0.65 左右时,信号很弱。最明显的改进来自更多的训练例子。虽然我们已经穷尽了 NASA 公开的滑坡目录中的好例子,但是应该有很多国家的滑坡目录以及它们的日期、大小和坐标。
我希望你觉得这个指南有用,或者至少有点意思。我当然喜欢研究它,并解释我们研究背后的动机。

Tuomas and I (third and second from the right) at the European Space Agency
再见。
[1] S. L. Ullo,M.S. Langenkamp,T.P. Oikarinen,M.P. Del Rosso,A. Sebastianelli,F. Piccirillo,S. Sica,“使用 Sentinel-2 图像数据使用卷积神经网络进行滑坡地质灾害评估”,IGARSS 2019–2019 IEEE 国际地球科学与遥感研讨会,日本横滨,2019 年,第 9646–9649 页。
使用 CSV 文件和无代码的机器学习(适用于新手)
免责声明 : 这是一个针对机器学习完全初学者,无需任何代码如何入门的帖子。如果你已经知道如何编码和使用机器学习库,你可能不会从中获得太多。
机器学习的核心是函数逼近。给定一组输入变量,机器学习算法试图得出一个可以准确预测输出的函数。
有各种算法来得出这个函数近似值,也称为模型。压力最大的是神经网络,也称为深度学习,这是我们将在本文中讨论的内容。
机器学习过程有三个主要步骤:
- 数据源&准备
- 模特培训
- 预测输出
我们将在本教程中全面介绍前两个步骤,而只简要介绍最后一个步骤。
数据源和准备
数据来源、清理和转换是任何机器学习项目中最耗时,也是最有影响力和最重要的部分。垃圾输入和垃圾输出在大多数编程中都成立,但在机器学习中更是如此。
当你读到关于图像识别误入歧途或医疗保健人工智能无法正常工作的新闻报道时,最有可能的是数据问题,无论是没有收集足够的数据还是收集了不代表真实人口的数据。
在现实世界的机器学习项目中,找到你需要的数据,清理它并转换它将花费大量的时间和令人头痛的事情。然而,出于学习的目的,我们可以利用已经为机器学习而管理和组织的数据集:UCI 库(T16)、Kaggle 数据集(T19)和 ML 数据(T21)。
在这篇文章中,我们将使用一个包含 1985 年国会议员对 16 个不同问题的投票的数据集。利用这些选票,我们将尝试预测该成员是民主党人还是共和党人。
选票的值可以是 y , n 和*?因此,我们不需要清理任何东西,然而,让我们假设值是 Yes,Y,Y,no,No,??和空白。然后,我们需要清理不一致的值,并确保所有的是、否和缺失的投票都只与一个值相关。*
模特培训
为了训练模型,我们需要了解训练和测试数据的概念。
与演绎或基于规则的方法相比,机器学习是一种估计函数的归纳方法。不是使用逻辑和理论来近似函数,而是将数据提供给算法,并使用数据来得出函数。
如果您要训练您的模型,并使用所有数据测试其准确性,那么该模型将非常准确,因为它没有看到任何新的东西。这就好像你练习了一套 100 道数学题,而你的数学考试正好有这 100 道题。你的考试分数可能会很高。
为了避免这个问题,我们将数据集分成测试集和训练集。我们将使用 70%的数据点来训练模型,另外 30%来测试模型的准确性。这样我们就能更好地了解我们的模型有多精确。
我们可以在开始时进行训练和测试分割,但幸运的是,我们将用于训练模型的软件会为我们做到这一点。
我们将使用一个叫做 Weka 的免费软件。使用 Weka 前需要安装 Java。Weka 可以在这里下载:https://sourceforge.net/projects/weka/。
**注意:**我将使用 Weka 3–8,因此如果您使用其他版本,可能会有一些变化。
下载数据集并安装 Weka 后,启动它。在主屏幕上点击打开文件,转到您下载数据集的文件夹,在文件类型下,选择 CSV 文件并打开文件。

打开它后,您将看到所有不同的列,也称为属性及其值,既有可视化的,也有表格形式的。类下拉列表应该被选择到输出属性或我们试图预测的内容。在这种情况下,这是在 267 名民主党人和 168 名共和党人中划分的政党属性。

现在我们的数据已经导入,是时候训练我们的模型了。呜!
点击顶部工具栏上的分类选项卡。然后点击大选择右下方的按钮。弹出的是 Weka 为你训练模型的所有算法列表。
我们将选择函数下的神经网络算法- >多层感知器。

现在我们要做两件至关重要的事情:
- 指定我们要将多少数据用于训练和测试集
- 指定我们希望算法使用的超参数的值
训练和测试集
在测试选项下,选择百分比分割并指定 70%。这意味着我们将使用 70%的数据进行训练,剩下的 30%进行测试。没有确切的方法知道最好的分割应该是什么,这使得机器学习与其他编程相比更像是一种实验。一般标准是使用 65–80%的数据集进行训练。

高参数
这也是机器学习的另一个实验领域。超参数(算法的不同变量)因算法而异,改变它们会极大地影响模型的准确性。
超参数对不同算法的意义以及如何找到它们的理想值超出了本教程的范围。你可能需要参加一个机器学习课程,或者看几个关于这些主题的教程来理解它们。
还有一种被称为 AutoML 的新技术也开始被采用,其中软件程序通过编程在超参数的不同值之间循环,并确定理想值。
对于本教程,我们将通过试错法,看看哪些超参数值给我们最好的结果。
要选择数值直接点击上的多层感知器-L 0.3 -M 0.2 -N 500 -V 0 -S 0 -E 20 -H 一条线,紧挨着选择按钮。这将弹出一个窗口,如下图所示。

我们今天要修改的两个超级参数是隐藏层和学习率。这些是指神经网络应该具有的层数和函数中的权重应该更新的因子。
让我们试着用提供的学习率和隐藏层运行,看看我们的结果。确保课程为政治 _ 政党并点击开始。

运行模型后,您将获得大量结果。这个数据集和实验的关键结果是正确分类的实例的数量,瞧!我们的模型有 96%的准确率。只是提醒一下,根据数据集或模型,其他错误率可能更适用。

由于我们的默认参数的准确性如此之好,我不会用超参数做实验,但你一定要试一试。尝试不同的超参数值、数据集和测试训练分割将使你对什么有效有更好的直觉,并使你成为更好的机器学习实践者。
但就是这样!恭喜你。你只是用一个文件做了深度学习,没有写代码!
现在我们有了模型,有趣的部分是预测国会议员是民主党人还是共和党人,只要知道他们对这些问题的投票方式。
预测输出
首先我们需要保存我们的模型。右键单击左下方窗格中的模型,然后单击保存模型并保存您的模型。
要完成其余的预测,你可以在这里阅读更多的。或者被我们张贴另一个教程。
快乐机器学习:)
用 x 光眼深度学习
随着每年执行数百万次诊断检查,胸部 X 射线是用于检测许多疾病的重要且可获得的临床成像工具。然而,它们的有用性可能受到解释方面的挑战的限制,这需要对描述复杂的三维器官和疾病过程的二维图像进行快速和彻底的评估。事实上,早期肺癌(T1)或气胸(T3)在胸部 x 光检查中可能会被遗漏,给患者带来严重的不良后果。
深度学习可以有效地用于发现 X 射线中的异常,以检测各种疾病。由于开源图像的可用性,深度学习在医学图像上的应用变得更加容易。我们将使用来自这个 CXR 数据集的 X 射线图像,尽管我们将只使用其中的两个类。
CXR 数据预处理—增强
现在让我们对 CXR 数据集进行预处理。

- x 射线图像的分辨率非常高;图像大小为 1024 像素 x 1024 像素…
- x 射线图像是单通道图像,而不是 RGB(三通道图像)。
- 中心裁剪不应应用于 x 射线图像,以确保图像角落内的异常不会被遗漏。
这就把我们带到了数据预处理的下一个方面——数据扩充。很多时候,我们拥有的数据量不足以很好地执行分类任务。在这种情况下,我们执行数据扩充。
我们知道池化增加了**不变性。**如果图像的左上角有异常迹象,通过合并,我们将能够识别左上角附近的左/右/上/下是否有异常。但是训练数据由类似于 翻转、旋转、裁剪、平移、照明、缩放、添加噪声 等的数据扩充组成。模型学习所有这些变化。这大大提高了模型的准确性。因此,即使异常出现在图像的任何角落,模型也能够以很高的准确度识别它。
增强由以下模块应用。数据生成器用于增强。
data gen = imagedata generator(
feature wise _ center = True,
feature wise _ STD _ normalization = True,
rotation_range=10,
width_shift_range=0,
height_shift_range=0,
vertical_flip=False,)
垂直翻转需要设置为“假”。这是因为 CXR 图像有一个自然的方向——从上到下。
CXR 数据预处理——归一化
规范化使得训练过程更加顺畅。这是一个重要的预处理步骤,我们简单讨论一下。
例如,假设你有一些数据点 x1,x2,x3,…,xn。大多数数据点的值的范围在(比如说) -10 到 10 之间,但是少数数据点(比如说 x11 和 x18)的值在 -900 到 1000 之间。
现在,在反向传播中,梯度(直接或间接)与导数 f′(x)相关,其中 f 是激活函数。假设您使用的是乙状窦激活。在 sigmoid 中,f′(x)在 x=-800 和 x=900 处的值几乎为零,但在 x=-1 和+1 之间是一个小正数。

Shape of Sigmoid Function
这使得相对于 X11 和 x18 的梯度下降到几乎为零,因此权重更新不能在正确的方向上发生。虽然 sigmoid 很少用在现代深度学习架构中,但这个问题也会出现在其他激活函数中,并且可以使用归一化来减少。
由于 CXR 图像不是“自然图像”,我们不使用“除以 255”策略。相反,我们采用最大-最小法进行归一化。由于您无法确定每个像素的范围是 0–255,因此您可以使用最小-最大值进行归一化。
def preprocess_img(img,mode):
img =(img—img . min())/(img . max()—img . min())
img = rescale(img,0.25,多通道=True,mode = ’ constant ')if mode = = ’ train ':
if NP . random . randn()>0:
img = data gen . random _ transform(img)
返回 img
在条件语句“if mode == train”中,使用一个随机数生成器,这样只有一部分图像被转换(而不是所有图像)。
CXR:网络建设
我们将使用 resnet 进行分类。由于 ResNets 在行业中已经变得相当普遍,所以有必要花一些时间来理解其架构的重要元素。先说这里提出的原架构。ResNet 已经解决的基本问题是,训练非常深的网络在计算上是困难的——例如,56 层网络的训练精度比 20 层网络的训练精度低。顺便说一下,在 ResNets 之前,任何超过 20 层的东西都被称为非常深。

现在让我们看看 ResNets 是如何解决这个问题的。考虑上图(来自论文)。假设网络的某个“单元”的输入是 x(该单元有两个权重层)。假设,理想情况下,该单元应该已经学习了某个函数 H(x ),即给定输入 x,该单元应该已经学习产生期望输出 H(x)。
在正常的神经网络中,这两层(即这个单元)将试图学习函数 H(x)。但雷斯内特尝试了一种不同的技巧。他们认为:让 F(x)表示 H(x)和 x 之间的残差,即 F(x)= H(x)——x。他们假设学习残差函数 F(x)比学习 H(x)更容易。在极端情况下,该单元应该简单地让信号通过它(即 H(x)=x 是学习的最佳值),将残差 F(x)推到零比学习 H(x)更容易。
在 deep 网上的实验证明了这个假设确实是真的——如果学习让信号通过是要做的最佳事情(即减少损失),那么单元学习 F(x)= 0;但是,如果要学些有用的东西,这些单元就学会了。这些单元被称为剩余单元。
让我们回到我们的项目。我们有以下模型建立过程的输入:一个图像通道,两个不同的类,如“渗出”与“找不到”,以及 256 行和列。
img _ channels = 1
img _ rows = 256
img _ cols = 256nb_classes = 2
我们使用数据生成过程来只加载需要的数据,以确保不会超出内存空间限制。
构建模型的基本步骤是:
- 导入 resnet,因为我们将使用迁移学习。
- 运行扩充数据生成器。
- 执行消融运行。
- 符合模型。
CXR:火车模型
现在,该训练模型了。模型训练的第一步是进行消融实验。这样做是为了检查代码是否按预期运行。我们试图用小数据集和较少的时期来运行它,以识别任何潜在的问题。

我们可以从第一次消融中了解一些事实。数据类高度不平衡。“渗出”与“找不到”的比率几乎是 10 (107/1000)。由于大部分数据只属于一个类别,在这种情况下简单的训练将不起作用,因为模型将主要学习和分类大部分数据为“无发现”,从而导致高精度。如果你注意到,大约 90%的数据都是“找不到的”。通过对所有数据进行相同的分类,准确率将达到 90 %,接近我们达到的 96%的准确率。因此,正确分类“积液”的目标没有实现。高精度明显误导了我们,因此我们将使用曲线下面积(AUC)来验证结果。
CXR:有 AUC 的火车模型
什么是曲线下面积(AUC)?
AUC-ROC 曲线是在各种阈值设置下对分类问题的性能测量。ROC 是概率曲线,AUC 代表可分性的程度或度量。它告诉我们模型在多大程度上能够区分不同的类。AUC 越高,模型预测 0 为 0 和 1 为 1 的能力越强。以此类推,AUC 越高,该模型在区分患病和未患病患者方面就越好。

AUC vs Accuracy
关键时刻
我们观察到精确度很高,大约 0.93 到 0.96。相反,AUC 低得多,为. 29。因为 AUC 低于 0.5,所以模型以相反的顺序工作,将阳性类别识别为阴性类别。
我们在第二轮消融中观察到:
- 该模型在我们选择的 AUC 指标上表现不佳。
- 造成这种情况的主要原因是普遍性问题。数据集中的异常案例并不多。这个问题会出现在几乎所有的医学成像问题中,就此而言,会出现在大多数类别不平衡的数据集中。
- 为了解决这个问题,我们引入了“加权分类交叉熵”。这是一种衡量损失的方法,它将权重应用于不同形式的错误。
低患病率的解决方案
低患病率问题的一个常见解决方案是使用加权交叉熵损失。损失被修改,使得低流行等级的错误分类比其他等级受到更重的惩罚。
因此,每当模型在异常类别(在本例中为“渗出”)上出错时,我们会通过将损失乘以高权重值来对其进行严厉惩罚。这导致错误分类类别的损失增加,因此由于反向传播而导致的权重变化更大。因此,对错误分类负责的权重的学习曲线更多。
假设“没有发现”是 0 级,“积液”是 1 级。
bin_weights[0,0]: 实际类:0,预测类:0,所以没有惩罚,只是正常的权重 1。
bin_weights[1,1]: 实际类:1,预测类:1,所以没有惩罚,只是正常的权重为 1。
出现异常情况时:
bin_weights[1,0] —实际类为 1,预测类为 0,按权重罚 5。
bin_weights[0,1] —实际类为 0,预测类为 1,按权重罚 5。
def w _ category _ cross entropy(y _ true,y_pred,weights):
nb _ cl = len(weights)
final _ mask = k . zeros _ like(y _ pred[:,0])
y _ pred _ max = k . max(y _ pred,axis = 1)
y _ pred _ max = k . shape(y _ pred)[0],1)
y _ pred _ max _ matbin_weights = np.ones((2,2))
bin_weights[0,1] = 5
bin_weights[1,0]= 5
ncce = partial(w _ category _ cross entropy,weights=bin_weights)
ncce。_ _ name _ _ = ’ w _ category _ cross entropy ’
CXR:最后一轮
我们确定了问题的根本原因,并提出了具体的解决方案— 加权分类交叉熵,并开始了我们的最终运行。

只有当“验证准确性”提高时,我们才保存模型权重。保存模型权重的方法称为检查点,使用 **keras 回调调用。**结果清楚地表明 AUC从 0.19(无重量)显著增加到 0.86(有重量)。
做一个预测
最佳拟合模型用于预测 X 射线图像是“渗出”还是“无发现”
不用于训练或验证数据集 X 射线图像是测试预测的最佳候选。

Test image looks like above
在上述最后运行步骤中,我们重新加载 Resnet 并从最佳拟合权重中加载权重。

Loading weight from best fit model
并使用测试图像作为预测输入。

Prediction of the Test image datapoint
所以根据模型测试图像是一个积液。我们可以清楚地看到这个结果是正确的。
结论和下一步措施
我刚刚开始在医学图像上使用 CNN。展望未来,我将扩展模型以预测更多的输出,并尝试提高准确性。欢迎你加入我的 Github 回购项目。我已经在 google Drive 上传了我的训练数据。
让我们一起构建或者你自己尝试一下,然后抢先我一步。无论哪种方式,都要不断学习,不断提升自己。下次见,保持警惕!
用于图像和单词标签识别的深度多输入模型迁移学习
一种用于图像和文本理解的多模型深度学习方法

随着深度学习如卷积神经网络(即conv net)【1】,计算机视觉再次成为热门科研课题。现在计算机视觉的一个主要目标是使用机器学习(尤其是深度学习)来训练计算机从数字图像、文本或视频中获得人类级别的理解。
随着它的广泛使用,ConvNet 成为图像识别的事实模型。如[1]所述,一般来说,有两种方法可以将 ConvNet 用于计算机视觉:
- 从头开始培训新的 ConvNet 模型
- 使用 转移学习 ,即使用预先训练好的 ConvNet 模型
如下图所示,一个 ConvNet 模型由两部分组成:一个卷积基和一个全连通分类器。

**图 1:**conv net 迁移学习的典型场景。
ConvNet 迁移学习可以进一步细分为三种方法:
- 方法一:无需图像论证的特征提取【1】
这种方法首先使用预先训练的卷积基将新图像转换为数组,例如 Numpy 数组(如果需要,可以将其保存到文件中),然后使用内存中图像的那些数组表示,使用随机初始化的权重训练单独的新分类模型。 - 方法二:带图像论证的特征提取【1】
该方法以预先训练好的卷积基为输入层建立新的模型,冻结卷积基的权值,最后加入一个新的随机初始化权值的输出分类器。 - 方法三:微调【1】
这种方法不使用整个冻结的预训练卷积基。它允许解冻冻结的预训练卷积基的一些顶层,使得那些解冻的顶层可以与新的完全连接的分类器联合训练。
方法 2 在本文中用于多输入模型迁移学习。
迁移学习背后的主要思想不仅可以用于有监督的 ConvNet,还可以用于其他深度学习算法,例如用于自然语言处理的无监督单词嵌入模型(NLP)【4】。
目前流行的预训练单词嵌入模型有两种: word2vec 和GloVe【3】。像[4]中使用的 word2vec-keras 模型,这些预训练的单词嵌入模型通常与其他有监督的深度学习算法相结合,如文本分类等递归神经网络(RNN)LSTMNLP【4】。
ConvNet 模型或 NLP 模型(例如,单词嵌入与 LSTM 的组合)可以单独用于解决计算机视觉和 NLP 中的许多有趣问题。正如本文所示,这些不同类型的模型还可以以各种方式组合起来[1]形成更强大的模型,以解决更具挑战性的问题,如保险索赔流程自动化不仅需要图像识别能力,还需要自然语言(如文本)理解能力。
本文使用 Kaggle 中一个有趣但具有挑战性的数据集,表征学习中的挑战:多模态学习 [2],提出了一个新的多输入迁移学习模型,该模型将两个输入模型与一个完全连接的分类层相结合,同时用于图像识别和单词标签识别(见图 2)。
新的多输入模型背后的主要思想是将图像和单词标签识别的问题转化为机器学习分类问题,即确定给定的图像是否与给定的一组单词标签匹配(0-否,1-是)。
1.数据准备
在将图像文件和单词标签文件的 Kaggle 数据集[2]下载到本地机器之后,可以使用下面的代码来构建和打乱图像文件名和相关单词标签文件名的列表。在用于训练目的的数据集中有 100,000 个图像文件和 100,000 个相应的单词标签文件。
original_dataset_dir = './multi_task_learning/data/ESPGame100k'
base_dataset_dir = './multi_task_learning/data/ESPGame100k_small'original_label_path = original_dataset_dir + '/labels'
original_label_files = [f for f in listdir(original_label_path) if isfile(join(original_label_path, f))]original_image_path = original_dataset_dir + '/thumbnails'
original_image_files = [f for f in listdir(original_image_path) if isfile(join(original_image_path, f))]original_image_files = np.array(original_image_files)
original_label_files = np.array(original_label_files)dataset_size = original_label_files.shape[0]
perm = np.arange(dataset_size)
np.random.shuffle(perm)
original_image_files = original_image_files[perm]
original_label_files = original_label_files[perm]
为了在合理的时间内(几个小时)在笔记本电脑上训练新的多输入模型,我随机选择了 2,000 张图像和相应的 2,000 个 word 标记文件用于本文的模型训练:
if not os.path.isdir(base_dataset_dir):
os.mkdir(base_dataset_dir)
small_label_path = os.path.join(base_dataset_dir, 'labels')
small_image_path = os.path.join(base_dataset_dir, 'thumbnails')
if not os.path.isdir(small_label_path):
os.mkdir(small_label_path)
if not os.path.isdir(small_image_path):
os.mkdir(small_image_path)for fname in original_label_files[:2000]:
src = os.path.join(original_label_path, fname)
dst = os.path.join(small_label_path, fname)
shutil.copyfile(src, dst)for fname in original_label_files[:2000]:
img_fname = fname[:-5]
src = os.path.join(original_image_path, img_fname)
dst = os.path.join(small_image_path, img_fname)
shutil.copyfile(src, dst)
下面的代码将 2,000 个图像标签文件名和相应的 2,000 个单词标签加载到 Pandas DataFrame 中:
label_map = {'label_file' : [], 'word_tags' : []}
for fname in listdir(small_label_path):
f = join(small_label_path, fname)
if isfile(f):
f = open(f)
label_map['label_file'].append(fname)
line = f.read().splitlines()
label_map['word_tags'].append(line)
label_df = pd.DataFrame(label_map)
label_df.head()

与[4]类似,Jupyter notebook [5]中包含一个文本数据预处理程序,用于执行最少的数据预处理,如删除停用词和数字,以防产生重大差异:

如[4]中所述,文本数据预处理的影响是不显著的,因此在本文中,未经预处理的原始单词标签用于模型训练。
2.多输入模型迁移学习体系结构
如下图所示,新的多输入迁移学习模型使用预训练的 ConvNet 模型 VGG16 来接收和处理图像,并使用新的 NLP 模型(预训练的单词嵌入模型 GloVe 和 Keras LSTM 的组合)来接收和处理单词标签。这两个输入模型首先被合并在一起,然后与完全连接的输出分类模型组合,该完全连接的输出分类模型使用图像识别模型输出和 NLP 模型输出来确定图像和一组单词标签的输入对是否匹配(0-否,1-是)。

图 2: 多输入模型迁移学习的新深度学习模型的架构。
3.图像识别中的迁移学习
如图 2 所示,新的多输入迁移学习模型使用预训练的 ConvNet 模型 VGG16 进行图像识别。VGG16 型号已经包含在 Keras 库中。来自[1]的以下代码用于将 VGG16 卷积基与新的全连接分类器相结合,以形成新的图像识别输入模型:
from keras.applications import VGG16
image_input = Input(shape=(150, 150, 3), name='image')
vgg16 = VGG16(weights='imagenet',
include_top=False,
input_shape=(150, 150, 3))(image_input)
x = layers.Flatten()(vgg16)
x = layers.Dense(256, activation='relu')(x)
4.面向文本分类的迁移学习
如图 2 所示,新的多输入迁移学习模型使用预训练的单词嵌入模型 GloVe [3]将单词标签转换为紧凑向量。一旦手套数据集[3]被下载到本地机器,来自[1]的以下代码可用于将单词嵌入模型加载到存储器中:
glove_dir = './multi_task_learning/data/'embeddings_index = {}
f = open(os.path.join(glove_dir, 'glove.6B.100d.txt'))
for line in f:
values = line.split()
word = values[0]
coefs = np.asarray(values[1:], dtype='float32')
embeddings_index[word] = coefs
f.close()
如图 2 所示,GloVe 单词嵌入与 Keras LSTM 相结合,形成了一个新的预测/识别单词标签的 NLP 输入模型:
tag_input = Input(shape=(None,), dtype='int32', name='tag')
embedded_tag = layers.Embedding(max_words, embedding_dim)(tag_input)
encoded_tag = layers.LSTM(512)(embedded_tag)
5.多输入模型与全连接分类器的结合
一旦创建了新的图像识别输入模型和新的 NLP 输入模型,以下代码可以将它们与新的输出分类器组合成一个多输入迁移学习模型:
concatenated = layers.concatenate([x, encoded_tag], axis=-1)
output = layers.Dense(1, activation='sigmoid')(concatenated)model = Model([image_input, tag_input], output)
model.summary()

如[1]中所述,预训练的 VGG16 卷积基和手套字嵌入层都必须被冻结,使得这些模型的预训练权重在新的多输入模型训练期间不会被修改:
# model.layers[1].trainable = False # freeze VGG16
model.layers[4].set_weights([embedding_matrix])
model.layers[4].trainable = False # freeze GloVe word embedding
然而,关于 VGG16 卷积基,有趣的是,我尝试了两种方法(冻结或不冻结),但在模型训练时间或模型预测结果方面没有看到显著差异。
6.多输入模型训练
原始 Kaggle 训练数据集仅包括正确的图像对和相应的单词标签。在本文中,每一个这样的正确对都被标记为 1(匹配)(另请参见下面的代码)。为了创建一个平衡的数据集,除了现有的 2,000 对正确的图像和单词标记之外,下面的代码还创建了 2,000 对不正确的图像和单词标记。为简单起见,这是通过将所选择的 2000 幅图像中的每一幅(比如图像 I)与下一个图像文件的单词标签(即图像 i+1 的单词标签)配对来实现的。
import cv2dim = (150, 150)
X_image_train = []
X_tag_train = tag_data
y_train = []
for fname in listdir(small_image_path):
fpath = os.path.join(small_image_path, fname)
im = cv2.imread(fpath)
im_resized = cv2.resize(im, dim, interpolation = cv2.INTER_AREA)
X_image_train.append(im_resized)
y_train.append(1)
# add incorrect image and tag pairs
num_negative_samples = len(y_train)
for i in range(num_negative_samples):
image = X_image_train[i]
X_image_train.append(image)
j = (i + 1) % num_negative_samples # get a different tag
tag = X_tag_train[j]
X_tag_train = np.append(X_tag_train, tag)
y_train.append(0)
总共有 4000 对图片和文字标签,2000 对正确,2000 对错误。
每个图像单词标签需要被编码为整数,并且在单词标签可以被单词嵌入模型消费之前,单词标签的每个列表/序列需要被转换为整数值序列。这是通过使用和修改[1]中的代码来实现的:
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequencesmaxlen = 100
training_samples = num_of_samples
tag_vocabulary_size = 10000
max_words = tag_vocabulary_size
num_of_samples = label_df.shape[0]tokenizer = Tokenizer(num_words=max_words)
texts = []
for tag_list in label_df_clean['word_tags']:
texts.append(' '.join(tag_list))
tokenizer.fit_on_texts(texts)
sequences = tokenizer.texts_to_sequences(texts)
word_index = tokenizer.word_index
print('Found {} unique tokens'.format(len(word_index)))
tag_data = pad_sequences(sequences, maxlen=maxlen)
得到的图像和单词标签训练数据集被转换成 Numpy 数组并被混洗用于模型训练:
X_image_train = np.array(X_image_train)
X_tag_train = np.array(X_tag_train)
y_train = np.array(y_train)
perm = np.arange(y_train.shape[0])
np.random.shuffle(perm)
X_image_train = X_image_train[perm]
X_tag_train = X_tag_train[perm]
y_train = y_train[perm]
新的多输入模型被编译和训练如下,仅具有 30 个时期和 4,000 对平衡的图像和单词标签:
model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc'])
model.fit([X_image_train, X_tag_train], y_train, epochs=30, batch_size=64)

7.模型预测法
如下所示,[2]中的私有测试数据集包括 500 个图像,每个图像与两组单词标签相关联:

给定测试数据集中的图像,新的多输入迁移学习模型需要能够预测给定的两组单词标签中的哪一组与图像匹配。
以下代码用于将测试图像加载到内存中:
dim = (150, 150)
X_image_test = []for fname in listdir(test_image_dir):
fpath = os.path.join(test_image_dir, fname)
im = cv2.imread(fpath)
im_resized = cv2.resize(im, dim, interpolation = cv2.INTER_AREA)
X_image_test.append(im_resized)
测试字标签被转换成如下编码的整数值序列:
tokenizer_test = Tokenizer(num_words=max_words)
texts_1 = []
texts_2 = []
texts_all = []
for tag_list in test_image_label_df['word_tags_1']:
texts_1.append(' '.join(tag_list))
for tag_list in test_image_label_df['word_tags_2']:
texts_2.append(' '.join(tag_list))
texts_all.extend(texts_1)
texts_all.extend(texts_2)
tokenizer_test.fit_on_texts(texts_all)
sequences_1 = tokenizer_test.texts_to_sequences(texts_1)
sequences_2 = tokenizer_test.texts_to_sequences(texts_2)word_index_test = tokenizer_test.word_index
print('Found {} unique tokens in test'.format(len(word_index_test)))
tag_data_test_1 = pad_sequences(sequences_1, maxlen=maxlen)
tag_data_test_2 = pad_sequences(sequences_2, maxlen=maxlen)
然后,生成的图像和单词标签的 Python 数组被转换为 Numpy 数组,并适合训练好的预测模型:
X_image_test = np.array(X_image_test)
X_tag_test_1 = np.array(tag_data_test_1)
X_tag_test_2 = np.array(tag_data_test_2)
y_predict_1 = loaded_model.predict([X_image_test, X_tag_test_1])
y_predict_2 = loaded_model.predict([X_image_test, X_tag_test_2])
下表显示了前 20 个预测结果:

下图是测试数据集中的影像 201.png:

两个相关的单词标签集如下:
word-tag-set-0: ['bloom', 'glow', 'overexposed', 'bright', 'white', 'face', 'woman', 'blonde']
word-tag-set-1: ['iron', 'nuggets', 'samples', 'metal', 'ore', 'shadow', 'white', 'grey', 'gray', 'rust', 'shiny']
该模型预测:
word-tag-set-0: probability of 0.797
word-tag-set-1: probability of 0.999
概率较高的 0.999 的答案是:
[‘铁’,‘金块’,‘样本’,‘金属’,‘矿石’,‘阴影’,‘白色’,‘灰色’,‘铁锈’,‘闪亮’]
作为另一个正面例子,下面是测试数据集中的影像 76.png:

以下是相关的两组单词标签:
word-tag-set-0: ['person', 'man', 'shirt', 'pinstripe', 'smile', 'balding', 'grey', 'gray']
word-tag-set-1: ['country', 'music', 'man', 'instrument', 'guitar', 'musician', 'person', 'playing', 'watch', 'striped', 'shirt', 'red', 'glasses']
该模型预测:
word-tag-set-0: probability of 0.997
word-tag-set-1: probability of 0.530
概率较高的 0.997 的答案是:
[‘人’,‘男人’,‘衬衫’,‘细条纹’,‘微笑’,‘谢顶’,‘灰色’]
作为一个假阳性示例,以下是测试数据集中的图像 189.png:

以下是相关的两组单词标签:
word-tag-set-0: ['necklace', 'woman', 'earring', 'jewelry', 'mouth', 'chin', 'closeup']
word-tag-set-1: ['circle', 'lines', 'round', 'window', 'porthole', 'man', 'face', 'beard', 'person', 'dark', 'shadow']
该模型预测:
word-tag-set-0: probability of 0.016
word-tag-set-1: probability of 0.999
概率较高的 0.999 的误报答案是:
[‘圆’,‘线’,‘圆’,‘窗’,‘舷窗’,‘人’,‘脸’,‘胡子’,‘人’,‘黑暗’,‘影子’]
上面的测试结果表明,即使新的多输入迁移学习模型仅用 4000 对图像和单词标签以及 30 个时期来训练,该模型也能够在准确性方面获得相当合理的结果。
然而,由于模型过度拟合,该模型也产生了相当多的假阳性。
摘要
本文提出了一种新的多输入深度迁移学习模型,该模型将两个预训练的输入模型(VGG16 和 GloVe & LSTM)与一个新的全连接分类层相结合,用于同时识别图像和单词标签。
新的多输入深度学习方法的关键点是将图像和单词标签识别的问题转化为分类问题,即确定给定的图像是否与给定的一组单词标签匹配(0-否,1-是)。
Kaggle 中具有挑战性的公共数据集表征学习中的挑战:多模态学习【2】,用于训练和评估新模型。
模型预测结果表明,新模型在用于演示目的的有限模型训练(只有 30 个时期和 4,000 对图像和单词标签)下表现得相当好。然而,毫不奇怪,由于模型过度拟合,该模型也产生了相当多的假阳性。这个问题可以通过用更多时期和/或更多对图像和单词标签来训练模型来解决。
显然,随机选择的 2000 幅训练图像不足以代表总共 100000 幅可用的训练图像。通过将训练图像的数量从 2,000 个增加到更大的尺寸,如 10,000 个,模型性能将得到显著提高。
Github [5]中提供了一个包含所有源代码的 Jupyter 笔记本。
参考
[1] F. Chollet,用 Python 进行深度学习,曼宁出版公司,2018
[2] 表征学习的挑战:多模态学习
[3] J. Pennington,R. Socher,C.D. Manning, GloVe:单词表示的全局向量
[4] Y. Zhang,利用 word2vec-keras 进行自然语言处理的深度学习
[5] Y. Zhang, Github 中的 Jupyter 笔记本
披露声明:2019 首创一。观点是作者个人的观点。除非本帖中另有说明,否则 Capital One 不隶属于所提及的任何公司,也不被这些公司认可。使用或展示的所有商标和其他知识产权是其各自所有者的财产。
深度多任务学习— 3 个经验教训
在过去的一年里,我和我的团队一直致力于在 Taboola feed 中实现个性化的用户体验。我们使用多任务学习 (MTL)来预测同一组输入特征的多个关键性能指标(KPI),并在 TensorFlow 中实现了一个深度学习(DL)模型来实现这一点。回到我们开始的时候,MTL 对我们来说似乎比现在复杂得多,所以我想分享一些经验教训。
已经有不少关于用 DL 模式实现 MTL 的帖子( 1 、 2 、 3 )。在这篇文章中,我将分享一些在神经网络(NN)中实现 MTL 时需要考虑的具体问题。我也将提出简单的张量流解决方案来克服所讨论的问题。
分享是关怀
我们想从硬参数共享的基本方法开始。硬共享意味着我们有一个共享子网,后面是特定于任务的子网。

在 TensorFlow 中开始使用这种模型的一个简单方法是使用带有多个头部的估算器。因为它看起来和其他神经网络架构没有什么不同,你可能会问自己哪里出错了?
第 1 课—组合损失
我们在 MTL 模型中遇到的第一个挑战是为多个任务定义一个损失函数。虽然单个任务有明确定义的损失函数,但多个任务会带来多个损失。
我们尝试的第一件事就是简单地将不同的损失相加。很快我们可以看到,当一个任务收敛到好的结果时,其他的看起来很糟糕。当仔细观察时,我们很容易明白为什么。损失的规模如此不同,以至于一项任务主导了整体损失,而其余的任务没有机会影响共享层的学习过程。
一个快速解决方案是用加权总和代替损失总和,这样所有损失的规模大致相同。然而,这个解决方案涉及到另一个可能需要偶尔调整的超参数。
幸运的是,我们发现了一篇很棒的论文,提出用不确定性来衡量 MTL 的损失。其实现方式是通过学习另一个噪声参数,该参数被整合到每个任务的损失函数中。这允许有多个任务,可能的回归和分类,并使所有损失达到相同的规模。现在我们可以回到简单地总结我们的损失。
我们不仅得到了比加权总和更好的结果,还可以忘记额外的权重超参数。这里的是本文作者提供的一个 Keras 实现。
第 2 课—调整学习率
学习率是调节神经网络的最重要的超参数之一,这是一个普遍的约定。所以我们尝试调整,发现一个学习率看起来非常适合任务 A,另一个非常适合任务 b。选择较高的学习率会导致其中一个任务的死亡,而使用较低的学习率会导致另一个任务收敛缓慢。那我们能做什么?我们可以为每个“头”(特定于任务的子网)调整一个单独的学习速率,为共享子网调整另一个速率。
虽然听起来很复杂,但实际上很简单。通常,在 TensorFlow 中训练神经网络时,您会使用类似以下的内容:
optimizer = tf.train.AdamOptimizer(learning_rate).minimize(loss)
AdamOptimizer 定义如何应用渐变,而 minimize 计算并应用渐变。我们可以将最小化替换为我们自己的实现,在应用梯度时,我们将对计算图中的每个变量使用适当的学习率:
all_variables = shared_vars + a_vars + b_vars
all_gradients = tf.gradients(loss, all_variables)
shared_subnet_gradients = all_gradients[:len(shared_vars)]
a_gradients = all_gradients[len(shared_vars):len(shared_vars + a_vars)]
b_gradients = all_gradients[len(shared_vars + a_vars):]
shared_subnet_optimizer = tf.train.AdamOptimizer(shared_learning_rate)
a_optimizer = tf.train.AdamOptimizer(a_learning_rate)
b_optimizer = tf.train.AdamOptimizer(b_learning_rate)
train_shared_op = shared_subnet_optimizer.apply_gradients(zip(shared_subnet_gradients, shared_vars))
train_a_op = a_optimizer.apply_gradients(zip(a_gradients, a_vars))
train_b_op = b_optimizer.apply_gradients(zip(b_gradients, b_vars))
train_op = tf.group(train_shared_op, train_a_op, train_b_op)
顺便说一下,这个技巧实际上对单任务网络也很有用。
第 3 课—使用评估作为特征
一旦我们通过了创建一个预测多项任务的神经网络的第一阶段,我们可能会希望使用我们对一项任务的估计作为另一项任务的特征。在向前传球时,这真的很容易。估计是一个张量,所以我们可以像任何其他层的输出一样连接它。但是在 backprop 中会发生什么呢?
假设任务 A 的估计值作为一个特征传递给任务 B。我们可能不想将渐变从任务 B 传播回任务 A,因为我们已经有了一个 A 的标签。
别担心,TensorFlow 的 API 有 tf.stop_gradient 就是因为这个原因。当计算梯度时,它让你传递一个你希望当作常数的张量列表,这正是我们所需要的。
all_gradients = tf.gradients(loss, all_variables, stop_gradients=stop_tensors)
同样,这在 MTL 网络中是有用的,但不是唯一的。每当您想用 TensorFlow 计算一个值,并需要假设该值是一个常数时,都可以使用这种技术。例如,当训练生成性对抗网络(GANs)时,你不希望通过对抗例子的生成过程反向推进。
那么,下一步是什么?
我们的模型已经启动并运行,Taboola feed 正在被个性化。然而,仍有很大的改进空间,还有许多有趣的架构有待探索。在我们的用例中,预测多个任务也意味着我们基于多个 KPI 做出决策。这可能比使用单一的 KPI 要复杂一些…但这已经是一个全新的话题了。
感谢阅读,希望这篇文章对你有用!
基于人脸照片的深度神经网络亲属关系预测

Photo by Alex Guillaume on Unsplash
给定两个人的面部照片,我们能预测他们是否有血缘关系吗?
这就是我们在本帖中试图通过使用野外家庭:一个亲属识别基准数据集来做的事情,数据集的格式在 Kaggle 上共享:https://www.kaggle.com/c/recognizing-faces-in-the-wild/data
我们将探索深度神经网络的有效性,并转移学习技术来训练一个神经网络,该网络将预测两个人是否有血缘关系或是否给定了他们的面部照片。
数据集:
我们将使用在 kaggle 上共享的野生数据集中的家族。这是同类中规模最大的数据集,其中人脸照片按人分组,然后按家庭分组。

Image organization in the FIW dataset
除了图像文件夹之外,我们还有一个文件,列出了一个家庭中两个人有血缘关系的所有情况,但并不是所有家庭成员都有血缘关系。(就像母亲和父亲,除非我们说的是兰尼斯特😄)
型号:
为了解决这个任务,我们将使用一个暹罗网络,它拍摄一对图像,如果照片中的人是相关的,则预测 1,否则预测 0。

Siamese Network
图像编码器应用于每个输入图像,并将它们中的每一个编码成固定长度的向量。两个图像向量之间的差的平方被馈送到完全连接的层,该层然后预测亲属关系的二进制标签。

Example of input/output
迁移学习:
我们的解决方案将基于使用两种不同设置的预训练图像编码器:
ImageNet 上的预处理:1400 万张手动标记图像的数据集,用于分类,如狗、猫、飞机、草莓…
vgg face 2上的预训练:一个由330 万张人脸图像和 9000 多个不同种族、口音、职业和年龄的身份组成的数据集。
预训练技术是有用的,因为它们允许我们将在源任务(这里是图像分类或回归)中学到的表征转移到目标任务,在这种情况下是亲属关系预测。
这有助于减少过度拟合,并实现更快的收敛速度,尤其是在源任务和目标任务有些接近的情况下。
结果:
我们将使用准确性和 AUC 分数来评估每个模型的结果。
Resnet50 Imagenet 测试 ROC AUC : 0.70

Evaluation on the test set through kaggle submission
Resnet50 VGGFACE2 测试 ROC AUC : 0.81

Evaluation on the test set through kaggle submission

Validation Accuracy Comparison
我们可以看到,即使两种不同设置中的架构是相同的,在 VGGFace2 上预训练的模型上的结果也要好得多,因为与 Imagenet 相比,它是更接近亲属预测目标任务的源任务。
结论:
我们展示了我们可以训练一个暹罗网络来预测两个人是否有血缘关系。通过使用迁移学习,我们能够在 AUC ROC 中获得令人鼓舞的结果 0.81 ,特别是考虑到亲缘关系的任务👪即使对人类来说,预测也很难。
可在 https://github.com/CVxTz/kinship_prediction获得重现结果的代码
如果你有任何问题,请随意评论。
Python 中从零开始的深度神经网络
在本指南中,我们将构建一个深度神经网络,你想要多少层就有多少层!该网络可应用于二分类的监督学习问题。

Figure 1. Example of neural network architecture
注释
上标[ l 表示与 l ᵗʰ层相关的量。
上标( i )表示与 i ᵗʰ示例相关的量。
Lowerscript i 表示向量的 i ᵗʰ条目。
本文假设读者已经熟悉神经网络的概念。不然我推荐看这个好看的介绍https://towardsdatascience . com/how-to-build-your-own-neural-network-from-scratch-in-python-68998 a08e 4 F6
单个神经元

Figure 2. Example of single neuron representation
神经元计算一个线性函数(z = Wx + b ),然后是一个激活函数。我们一般说一个神经元的输出是 a = g(Wx + b)其中 g 是激活函数(sigmoid,tanh,ReLU,…)。
资料组
让我们假设我们有一个非常大的数据集,其中包含温度、湿度、大气压力和降雨概率等天气数据。
问题陈述:
- 标记为下雨(1)或不下雨(0)的 m_train 天气数据的训练集
- 标记为下雨或不下雨的 m_test 天气数据的测试集
- 每个天气数据包括 x1 =温度,x2 =湿度,x3 =大气压力
机器学习中一个常见的预处理步骤是对数据集进行居中和标准化,这意味着您从每个示例中减去整个 numpy 数组的平均值,然后用整个 numpy 数组的标准偏差除每个示例。
随机变量、统计总体、数据集或概率分布的标准偏差是…
en.wikipedia.org](https://en.wikipedia.org/wiki/Standard_deviation)
一般方法(构建我们算法的各个部分)
我们将遵循深度学习方法来构建模型:
- 定义模型结构(如输入要素的数量)
- 初始化参数并定义超参数:
- 迭代次数
- 神经网络的层数 L
- 隐藏层的大小
- 学习率α
3.num_iterations 的循环:
- 正向传播(计算电流损耗)
- 计算成本函数
- 反向传播(计算电流梯度)
- 更新参数(使用参数和来自反向投影的梯度)
4.使用训练好的参数来预测标签
初始化
更深的 L 层神经网络的初始化更复杂,因为有更多的权重矩阵和偏置向量。我提供下面的表格是为了帮助你保持正确的结构尺寸。

Table 1. Dimensions of weight matrix W, bias vector b andactivation Z for layer l

Table 2. Dimensions of weight matrix W, bias vector b andactivation Z for the neural network for our example architecture
表 2 帮助我们为图 1 中示例神经网络架构的矩阵准备正确的维度。
Snippet 1. Initialization of the parameters
使用小随机数进行参数初始化是一种简单的方法,但它保证了我们的算法有足够好的起点。
请记住:
- 不同的初始化技术,如零,随机,他或泽维尔导致不同的结果
- 随机初始化确保不同的隐藏单元可以学习不同的东西(将所有的权重初始化为零,这样每一层中的每个神经元都将学习相同的东西)
- 不要初始化太大的值
激活功能
激活函数使神经网络具有非线性。在我们的例子中,我们将使用 sigmoid 和 ReLU。
Sigmoid 输出一个介于 0 和 1 之间的值,这使它成为二进制分类的一个非常好的选择。如果小于 0.5,可以将输出分类为 0,如果大于 0.5,可以将输出分类为 1。
Snippet 2. Sigmoid and ReLU activation functions and their derivatives
在代码片段 2 中,你可以看到激活函数及其导数的矢量化实现(https://en.wikipedia.org/wiki/Derivative)。该代码将用于进一步的计算。
正向传播
在正向传播过程中,在层的正向函数中 l 你需要知道层中的激活函数是什么(Sigmoid,tanh,ReLU 等。).给定来自前一层的输入信号,我们计算 Z,然后应用选定的激活函数。

Figure 3. Forward propagation for our example neural network
线性正向模块(对所有示例进行矢量化)计算以下等式:

Equation 1. Linear forward function
Snippet 3. Forward propagation module
我们使用“缓存”(Python 字典,其中包含为特定层计算的 A 和 Z 值)将向前传播期间计算的变量传递给相应的向后传播步骤。它包含向后传播计算导数的有用值。
损失函数
为了监控学习过程,我们需要计算成本函数的值。我们将使用下面的公式来计算成本。

Equation 2. Cross-entropy cost
Snippet 4. Computation of the cost function
反向传播
反向传播用于计算损失函数相对于参数的梯度。这个算法是微积分中已知的“链式法则”的递归使用。
反向传播计算中使用的方程:

Equation 3. Formulas for backward propagation calculation
链式法则是计算复合函数导数的公式。复合函数是由其他函数内部的函数组成的函数。

Equation 4. Chain rule examples
没有“链式法则”(以方程 5 为例),很难计算损耗。

Equation 5. Loss function (with substituted data) and its derivative with respect to the first weight.
我们的神经网络模型的反向传播的第一步是从最后一层计算我们的损失函数相对于 Z 的导数。等式 6 包括两个分量,来自等式 2 的损失函数的导数(相对于激活函数)和来自最后一层的激活函数“sigmoid”相对于 Z 的导数。

Equation 6. The derivative of the loss function with respect to Z from 4ᵗʰ layer
等式 6 的结果可用于计算等式 3 的导数:

Equation 7. The derivative of the loss function with respect to A from 3ᵗʰ layer
损失函数相对于来自第三层的激活函数的导数(等式 7)用于进一步的计算。

Equation 8. The derivatives for the third layer
等式 7 的结果和来自第三层的激活函数“ReLU”的导数用于计算等式 8 的导数(损失函数相对于 Z 的导数)。接下来,我们对等式 3 进行计算。
我们对等式 9 和 10 进行类似的计算。

Equation 9. The derivatives for the second layer

Equation 10. The derivatives for the first layer
总的想法是:
来自 lᵗʰ层的损失函数相对于 z 的导数有助于计算来自 l-1)ᵗʰ层(前一层)的损失函数相对于 a 的导数。然后将结果与激活函数的导数一起使用。

Figure 4. Backward propagation for our example neural network
Snippet 5. Backward propagation module
更新参数
该函数的目标是使用梯度优化来更新模型的参数。
Snippet 6. Updating parameters values using gradient descent
全模型
神经网络模型的完整实现由片段中提供的方法组成。
Snippet 7. The full model of the neural network
为了进行预测,您只需要使用接收到的权重矩阵和一组测试数据运行一个完整的前向传播。
您可以修改片段 1 中的 nn_architecture 来构建一个具有不同层数和隐藏层大小的神经网络。此外,准备激活函数及其派生函数的正确实现(片段 2 )。实现的函数可以用来修改片段 3 中的linear _ activation _ forward方法和片段 5 中的linear _ activation _ backward方法。
进一步的改进
如果训练数据集不够大,您可能会面临“过拟合”问题。这意味着学习过的网络不会归纳出它从未见过的新例子。你可以使用正则化方法,比如 L2 正则化(它包括适当修改你的
代价函数)或者丢弃(它在每次迭代中随机关闭一些神经元)。
我们使用梯度下降来更新参数并最小化成本。您可以学习更高级的优化方法,这些方法可以加快学习速度,甚至让您获得更好的成本函数最终值,例如:
- 小批量梯度下降
- 动力
- Adam 优化器
参考资料:
[1]https://www . coursera . org/learn/neural-networks-deep-learning
https://www.coursera.org/learn/deep-neural-network
[3]https://ml-cheatsheet.readthedocs.io/en/latest/index.html
[5]https://towards data science . com/gradient-descent-in-a-shell-EAF 8c 18212 f 0
[6]https://medium . com/datadriveninvestor/math-neural-network-from-scratch-in-python-d6da 9 f 29 ce 65
用于横竿的深度 Q 学习
这篇文章的目的是介绍深度 Q 学习的概念,并用它来解决 OpenAI 健身房的 CartPole 环境。
该员额将由以下部分组成:
- 开放式人工智能健身房环境介绍
- 随机基线策略
- 深度 Q 学习
- 具有重放记忆的深度 Q 学习
- 双重深度 Q 学习
- 软更新
环境
横拉杆环境由一根沿无摩擦轨道移动的杆子组成。通过对推车施加+1 或-1 的力来控制该系统。钟摆开始直立,目标是防止它翻倒。状态空间由四个值表示:小车位置、小车速度、磁极角度和磁极尖端的速度。动作空间由两个动作组成:向左移动或向右移动。杆保持直立的每个时间步长提供+1 的奖励。当柱子偏离垂直方向超过 15 度,或者手推车偏离中心超过 2.4 个单位时,该集结束。
下面的单元格绘制了环境中的一组示例帧:
# Demonstration
env = gym.envs.make("CartPole-v1")def get_screen():
''' Extract one step of the simulation.'''
screen = env.render(mode='rgb_array').transpose((2, 0, 1))
screen = np.ascontiguousarray(screen, dtype=np.float32) / 255.
return torch.from_numpy(screen)# Speify the number of simulation steps
num_steps = 2# Show several steps
for i in range(num_steps):
clear_output(wait=True)
env.reset()
plt.figure()
plt.imshow(get_screen().cpu().permute(1, 2, 0).numpy(),
interpolation='none')
plt.title('CartPole-v0 Environment')
plt.xticks([])
plt.yticks([])
plt.show()
根据剧集的数量,输出如下所示:
Untrained Agent
正如我们所看到的,代理还没有经过训练,所以它只能走几步。我们将很快探讨一些能显著提高性能的策略。但是首先,让我们定义绘图函数,它将帮助我们分析结果:
def plot_res(values, title=''):
''' Plot the reward curve and histogram of results over time.'''
# Update the window after each episode
clear_output(wait=True)
# Define the figure
f, ax = plt.subplots(nrows=1, ncols=2, figsize=(12,5))
f.suptitle(title)
ax[0].plot(values, label='score per run')
ax[0].axhline(195, c='red',ls='--', label='goal')
ax[0].set_xlabel('Episodes')
ax[0].set_ylabel('Reward')
x = range(len(values))
ax[0].legend()
# Calculate the trend
try:
z = np.polyfit(x, values, 1)
p = np.poly1d(z)
ax[0].plot(x,p(x),"--", label='trend')
except:
print('')
# Plot the histogram of results
ax[1].hist(values[-50:])
ax[1].axvline(195, c='red', label='goal')
ax[1].set_xlabel('Scores per Last 50 Episodes')
ax[1].set_ylabel('Frequency')
ax[1].legend()
plt.show()
最终的剧情由两个支线剧情组成。第一个图显示了代理在一段时间内累积的总奖励,而另一个图显示了代理在过去 50 集的总奖励的直方图。当我们分析我们的策略时,我们会看到一些图表。
基线随机模型
在实现任何深度学习方法之前,我写了一个简单的策略,其中动作是从动作空间中随机采样的。这种方法将作为其他策略的基线,并使其更容易理解如何使用开放的人工智能健身房环境与代理合作。
def random_search(env, episodes,
title='Random Strategy'):
""" Random search strategy implementation."""
final = []
for episode in range(episodes):
state = env.reset()
done = False
total = 0
while not done:
# Sample random actions
action = env.action_space.sample()
# Take action and extract results
next_state, reward, done, _ = env.step(action)
# Update reward
total += reward
if done:
break
# Add to the final reward
final.append(total)
plot_res(final,title)
return final
一个环境步骤返回几个值,比如next_state、reward,以及模拟是否为done。下图显示了 150 集(模拟运行)的总累积奖励:

Random Strategy
上面的图展示了随机策略。不出所料,用这种方法解决环境问题是不可能的。代理没有从他们的经验中学习。尽管有时很幸运(获得差不多 75 的奖励),但他们的平均表现低至 10 步。
深度 Q 学习
Q-learning 背后的主要思想是我们有一个函数𝑄:𝑆𝑡𝑎𝑡𝑒×𝐴𝑐𝑡𝑖𝑜𝑛→ℝ,它可以告诉代理什么行为会导致什么回报。如果我们知道𝑄的价值,我们就有可能制定一个最大化回报的政策:
𝜋(𝑠)=argmax𝑎 𝑄(𝑠,𝑎)
然而,在现实世界中,我们无法获得全部信息,这就是为什么我们需要想出近似𝑄.的方法一种传统的方法是创建一个查找表,其中𝑄的值在代理的每个动作之后被更新。然而,这种方法很慢,并且不能扩展到大的动作和状态空间。由于神经网络是通用函数逼近器,我将训练一个可以逼近𝑄.的网络
DQL 类的实现包括一个在 PyTorch 中实现的简单神经网络,它有两个主要的方法——预测和更新。网络将代理的状态作为输入,并返回每个动作的𝑄值。代理选择最大𝑄值来执行下一个操作:
class DQL():
''' Deep Q Neural Network class. '''
def __init__(self, state_dim, action_dim, hidden_dim=64, lr=0.05):
self.criterion = torch.nn.MSELoss()
self.model = torch.nn.Sequential(
torch.nn.Linear(state_dim, hidden_dim),
torch.nn.LeakyReLU(),
torch.nn.Linear(hidden_dim, hidden_dim*2),
torch.nn.LeakyReLU(),
torch.nn.Linear(hidden_dim*2, action_dim)
)
self.optimizer = torch.optim.Adam(self.model.parameters(), lr)def update(self, state, y):
"""Update the weights of the network given a training sample. """
y_pred = self.model(torch.Tensor(state))
loss = self.criterion(y_pred, Variable(torch.Tensor(y)))
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()def predict(self, state):
""" Compute Q values for all actions using the DQL. """
with torch.no_grad():
return self.model(torch.Tensor(state))
q_learning 函数是后面所有算法的主循环。
它有许多参数,即:
env代表我们要解决的开放 Ai 健身房环境(CartPole。)episodes代表我们想玩的游戏数量。gamma是一个贴现因子,乘以未来奖励,以抑制这些奖励对代理人的影响。它的目的是让未来的奖励不如眼前的奖励有价值。epsilon表示随机行动相对于行动者在事件中积累的现有“知识”所告知的行动的比例。这种策略被称为“贪婪搜索策略”在玩游戏之前,代理没有任何经验,因此通常会将 epsilon 设置为较高的值,然后逐渐降低其值。eps_decay表示代理学习时ε减小的速度。0.99 来自最初的 DQN 论文。
稍后当我们到达相应的代理时,我将解释其他参数。
def q_learning(env, model, episodes, gamma=0.9,
epsilon=0.3, eps_decay=0.99,
replay=False, replay_size=20,
title = 'DQL', double=False,
n_update=10, soft=False):
"""Deep Q Learning algorithm using the DQN. """
final = []
memory = []
for episode in range(episodes):
if double and not soft:
# Update target network every n_update steps
if episode % n_update == 0:
model.target_update()
if double and soft:
model.target_update()
# Reset state
state = env.reset()
done = False
total = 0
while not done:
# Implement greedy search policy
if random.random() < epsilon:
action = env.action_space.sample()
else:
q_values = model.predict(state)
action = torch.argmax(q_values).item()
# Take action and add reward to total
next_state, reward, done, _ = env.step(action)
# Update total and memory
total += reward
memory.append((state, action, next_state, reward, done))
q_values = model.predict(state).tolist()
if done:
if not replay:
q_values[action] = reward
# Update network weights
model.update(state, q_values)
breakif replay:
# Update network weights using replay memory
model.replay(memory, replay_size, gamma)
else:
# Update network weights using the last step only
q_values_next = model.predict(next_state)
q_values[action] = reward + gamma * torch.max(q_values_next).item()
model.update(state, q_values)state = next_state
# Update epsilon
epsilon = max(epsilon * eps_decay, 0.01)
final.append(total)
plot_res(final, title)
return final
最直接的代理基于其最近的观察更新其 Q 值。它没有任何记忆,但它通过首先探索环境,然后逐渐降低其ε值来做出明智的决定。让我们来评估这样一个代理的性能:

Deep Q Learning
上图显示代理的性能有了显著的提高。它达到了 175 步,正如我们之前看到的,这对于一个随机的代理人来说是不可能的。趋势线也是正的,我们可以看到性能随着时间的推移而增加。同时,代理在 150 个纪元后没有成功达到目标线以上,其平均性能仍在 15 步左右,因此有足够的改进空间。
重放记忆
一次使用一个样本的𝑄近似不是很有效。上面的图表很好地说明了这一点。与随机代理相比,网络设法实现了更好的性能。然而,它无法到达 195 级台阶的门槛线。我实现了经验重放,以提高网络稳定性,并确保以前的经验不会被丢弃,而是用于培训。
体验回放将代理的体验存储在内存中。成批的经验是从记忆中随机抽取的,并用于训练神经网络。这种学习包括两个阶段——获得经验和更新模型。重放的大小控制了用于网络更新的体验的数量。内存是一个数组,存储代理的状态、奖励和动作,以及动作是否完成游戏和下一个状态。
# Expand DQL class with a replay function.
class DQN_replay(DQN):
def replay(self, memory, size, gamma=0.9):
""" Add experience replay to the DQN network class. """
# Make sure the memory is big enough
if len(memory) >= size:
states = []
targets = []
# Sample a batch of experiences from the agent's memory
batch = random.sample(memory, size)
# Extract information from the data
for state, action, next_state, reward, done in batch:
states.append(state)
# Predict q_values
q_values = self.predict(state).tolist()
if done:
q_values[action] = reward
else:
q_values_next = self.predict(next_state)
q_values[action] = reward + gamma * torch.max(q_values_next).item()targets.append(q_values)self.update(states, targets)

DQL with Replay
正如预期的那样,与只记住最后一个动作的神经网络相比,具有重放功能的神经网络似乎更加健壮和智能。大约 60 集之后,代理人设法达到了获奖门槛,并保持在这一水平。它还设法获得了可能的最高奖励——500 英镑。
双重深度 Q 学习
传统的深度 Q 学习往往会高估回报,导致训练不稳定,质量策略较低。让我们考虑 Q 值的等式:

The Bellman Equation. Source: Link
等式的最后一部分是对最大值的估计。这一程序导致系统性高估,从而引入最大化偏差。由于 Q-learning 涉及从估计中学习估计,这样的高估尤其令人担忧。
为了避免这种情况,我将定义一个新的目标网络。Q 值将取自这个新网络,这意味着反映主 DQN 的状态。然而,它没有相同的权重,因为它只在一定数量的集后更新。这个想法在 Hasselt et al .,2015 中首次提出。
添加目标网络可能会降低训练速度,因为目标网络不会持续更新。然而,随着时间的推移,它应该具有更稳健的性能。
q_learning循环中的n_update指定更新目标网络的时间间隔。
class DQN_double(DQN):
def __init__(self, state_dim, action_dim, hidden_dim, lr):
super().__init__(state_dim, action_dim, hidden_dim, lr)
self.target = copy.deepcopy(self.model)
def target_predict(self, s):
''' Use target network to make predicitons.'''
with torch.no_grad():
return self.target(torch.Tensor(s))
def target_update(self):
''' Update target network with the model weights.'''
self.target.load_state_dict(self.model.state_dict())
def replay(self, memory, size, gamma=1.0):
''' Add experience replay to the DQL network class.'''
if len(memory) >= size:
# Sample experiences from the agent's memory
data = random.sample(memory, size)
states = []
targets = []
# Extract datapoints from the data
for state, action, next_state, reward, done in data:
states.append(state)
q_values = self.predict(state).tolist()
if done:
q_values[action] = reward
else:
# The only difference between the simple replay is in this line
# It ensures that next q values are predicted with the target network.
q_values_next = self.target_predict(next_state)
q_values[action] = reward + gamma * torch.max(q_values_next).item()targets.append(q_values)self.update(states, targets)

Double DQL with Replay
双 DQL 重放已经超过了以前的版本,并一直执行 300 步以上。由于动作选择和评估的分离,性能似乎也更加稳定。最后,让我们探索一下对 DQL 代理的最后修改。
软目标更新
上面实现的用于更新目标网络的方法在最初的 DQN 论文中介绍过。在本节中,我们将探索另一种完善的更新目标网络权重的方法。我们将在每次运行后使用以下公式递增地更新目标网络,而不是在一定数量的步骤后更新权重:
目标权重=目标权重*(1-τ)+模型权重*τ
其中 0 < TAU < 1
This method of updating the target network is known as “soft target network updates” and was introduced in Lillicrap 等人,2016 。该方法的实现如下所示:
class DQN_double_soft(DQN_double):
def target_update(self, TAU=0.1):
''' Update the targer gradually. '''
# Extract parameters
model_params = self.model.named_parameters()
target_params = self.target.named_parameters()
updated_params = dict(target_params)for model_name, model_param in model_params:
if model_name in target_params:
# Update parameter
updated_params[model_name].data.copy_((TAU)*model_param.data + (1-TAU)*target_params[model_param].data)self.target.load_state_dict(updated_params)

DDQL with Soft Update
具有软目标更新的网络表现相当好。但是,好像并不比硬权更新好。
这张 gif 图展示了一名训练有素的特工的表现:
Trained Agent
结论
经验重放和目标网络的实现显著提高了开放人工智能平台环境下深度 Q 学习代理的性能。对代理的一些其他修改,如决斗网络架构(王等,2015 ),可以添加到该实现中,以提高代理的性能。该算法也可推广到其他环境。请随意测试它解决其他任务的能力!
笔记本链接:https://github . com/ritakurban/Practical-Data-Science/blob/master/DQL _ 卡特波尔. ipynb
参考
(1)强化 Q——用 OpenAI Gym 在 Python 中从头学习。(2019).Learndatasci.com。检索于 2019 年 12 月 9 日,来自https://www . learn data sci . com/tutorials/reinforcement-q-learning-scratch-python-open ai-gym/
(2)帕兹克,a .(2019)。强化学习(DQN)教程。检索自:https://py torch . org/tutorials/intermediate/reinforcement _ q _ learning . html
(3) Lillicrap,T. P .,Hunt,J. J .,Pritzel,a .,Heess,n .,Erez,t .,Tassa,y .,… & Wierstra,D. (2015)。深度强化学习的连续控制。arXiv 预印本 arXiv:1509.02971。
(4)范·哈瑟尔特(2016 年 3 月出版)。双 q 学习的深度强化学习。在第三十届 AAAI 人工智能会议上。
(5)王,z .,绍尔,t .,赫塞尔,m .,范哈瑟尔特,h .,兰托特,m .,&德弗莱塔斯,N. (2015)。用于深度强化学习的决斗网络架构。arXiv 预印本 arXiv:1511.06581。
(6)双 DQN 实现解决 OpenAI 健身房的横撑 v-0。(2019).中等。检索于 2019 年 12 月 20 日,来自https://medium . com/@ Leo Simmons/double-dqn-implementation-to-solve-open ai-gyms-cart pole-v-0-df 554 CD 0614d
利用索尼的 NNabla 实现深度 Q 网络

NNABLA 是什么?
索尼发布了神经网络库,简称“NNabla”。
NNabla 是设备就绪的,通过有效的内存管理,具有快速的 GPU 训练速度。最有趣的特性是 NNabla 默认允许运行定义和运行定义。例如,定义并运行风格的代码如下所示。
# build static graph like tensorflow
x = nn.Variable((2, 3))
out = PF.affine(x, 10)# inference like sess.run
x.d = np.random.random((2, 3))
out.forward()
value = out.d
另一方面,由运行定义的样式可以写成这样。
with nn.auto_forward():
x = nn.Variable.from_numpy_array(np.random.random(2, 3))
out = PF.affine(x, 10)
value = out.d
更多示例代码可在https://github.com/sony/nnabla-examples获得。
NNABLA 中的深度强化学习
虽然索尼分享了许多监督学习实现,但没有任何深度强化学习(DRL)的例子。如果你曾经尝试过实现你自己的 DRL 代码,你知道有一些在监督学习中很少见到的技巧。
在本帖中,我将向您展示 Deep Q-Network (DQN)的实现,作为 DRL NNA bla 的第一步。完整的实现可在这里获得。超参数和实现细节遵循最初的 DQN,您可以将此代码用作学术论文的基线。
q 函数
由于算法的概述在许多其他博客帖子中有解释,我们只关注神经网络部分。这是一个完整的神经网络定义。
我必须解释上面代码中的两个技巧。
参数同步(更新目标)
如果熟悉 tensorflow,就知道参数同步可以用tf.assign来写。但是,NNabla 目前在图形定义中没有参数赋值。要做到这一点,你必须直接操纵变量。
# copy source variable to destination variable
variable_dst.data.copy_from(variable_src.data)
data属性表示参数的引用。您可以通过该属性访问和操作它们。
由于我通常是一个 tensorflow 程序员,我真的希望tf.assign实现这种行为。最终我提交了一个 pull 请求让 NNabla F.assign函数。
渐变剪辑(序列)
为了避免参数变化太大,在 DQN 中使用了剪辑渐变。实际上,NNabla 有F.clip_grad_by_norm功能,但不清楚如何使用,因为关于这方面的文档较少。所以,我实现了如下。
grad = 10.0 * variable.grad / np.sqrt(np.sum(variable.g ** 2))
variable.grad = grad
10.0是超参数尺寸。你也可以像data属性一样访问渐变。在更新参数之前,您可以直接裁剪渐变。
结论
我用 NNabla 展示了 DQN 的例子,它也展示了你可以用 NNabla 编写你自己的代码。如果你在 GPU 机器上运行全部代码,你可能会注意到它的速度。在大多数情况下,由于良好的内存管理架构,NNabla 是最快的。此外,索尼报告了用 2176 个 GPU 进行最快的 ImageNet 训练。此外,NNabla 提供了简单的 Python APIs,如果你已经在其他库中编写了深度学习算法,你可以在一分钟内编写自己的代码。特别是,NNabla 针对嵌入式设备进行了优化。如果你对边缘深度学习感兴趣,NNabla 将是一个不错的选择。
使用 Connect 4 进行深度强化学习和蒙特卡罗树搜索
在之前的文章中,我写了如何使用 TD(0)算法实现井字游戏的强化学习代理。我实现了两种代理。第一个是表格强化学习代理,这意味着价值函数存储为一个表,该表包含游戏中存在的最佳策略的每个状态的所有值(在算法迭代期间学习)。因为游戏的状态少于 6000 个,所以可以存储所有的值。第二个实现是深度强化学习代理,这意味着我们不是将所有值保存在一个表中,而是训练一个神经网络来预测给定状态的值。这种方法更加通用和鲁棒,因为网络可以学习相似性,如状态空间中的平移或反射。
在这篇文章中,我想更进一步,尝试实现一个学习如何玩游戏 Connect 4 的代理。游戏包含一个 7 列 6 行的棋盘。每个玩家在他的回合中选择一列并从顶部丢一个彩色圆盘到最低的空位。第一个在水平、垂直或对角线上有四个相同颜色的圆盘的人获胜。Connect 4 远比井字游戏复杂,因为它有超过 10 个⁴状态。在这篇文章中,我将描述两种不同的方法。第一种方法是著名的深度 Q 学习算法或 DQL,第二种是蒙特卡罗树搜索(或 MCTS)。
深度 Q 学习
让我们首先定义我们的马尔可夫过程。正如在上一篇文章中,学习过程将是特定于每个玩家的,第一个或第二个,他们之间没有混合。这是因为它们具有不同的状态空间(例如,空棋盘在第一个玩家的状态空间中,而不在第二个玩家的状态空间中):
- 状态空间是每个玩家看到的所有状态。对于第一个玩家来说,它包括所有具有偶数个盘的板,而对于第二个玩家来说,它包括所有具有奇数个盘的板。
- 行动空间是玩家可以玩的每一栏的数字 1-7。
- 赢了奖励 1 分,输了奖励 1 分,平手奖励 0.5 分,否则奖励 0 分。
这里要注意的是,像每一个双人游戏一样,下一个状态不是由所采取的行动决定的,因为它也取决于对手的行动。两种状态之间的转换概率取决于玩家和对手的策略。
学习与初学者对战和学习与职业玩家对战是不一样的,因为转移概率是不同的。我将在文章中进一步讨论后者。
目标是学习 Q 函数:一个将状态-动作对映射到一个值的函数,该值表示每个玩家在特定状态下玩这个动作后将获得的未来奖励的预期平均值(实际上这并不完全正确,因为 Q 函数是在该动作之后的特定策略下定义的,但是 Q 学习算法收敛了最优策略的值)。在我们学习了 Q 函数之后,我们将选择玩使这个值最大化的动作。
q 函数的域包含超过 7*10 个⁴(超过 10 个⁴国家的 7 个可能的动作),并将它们映射到-1 到 1 之间的数字(奖励范围)。为了表示这个函数,我们将使用 CNN。
CNN 的输入将是一个矩阵 7*7,其中第一行是与动作相对应的一个热点向量,其他 6 行是状态表示,其中 1,-1 和 0 分别是玩家的盘、对手的盘或空点。
下图描述了该网络:

其中第一个卷积层的核选择为 5,其余卷积层的核选择为 4(对我来说选择 4 似乎是合理的,因为玩家需要连续 4 次才能获胜)。
根据 Q 学习算法计算目标:
Q(s,a) = Q(s,a)+α(max(Q(s’,a’)+gR-Q(s,a))
其中 Q 是 Q 函数, s 是状态, a 是动作, α 是学习率, R 是奖励, g 是折扣因子。
关于学习过程的几点注意事项:
- 我发现有计划的离线学习比在线学习效果更好。离线学习意味着成批的状态-动作对被存储,并且网络在这些成批的状态-动作对上被训练。我用了 300 个游戏的批次。规划意味着每一批都被训练了不止一次,而目标在每个时期都被重新计算。我使用了大约 5 个时期进行批处理。
- 博弈策略是一个贪婪策略,这意味着选择了探索因子 e。该因子根据所学习的 Q 函数来定义采取随机移动或最优移动的机会。该因子在训练期间下降。开始的时候我想鼓励探索,而后来我想学习和越来越多的优秀选手比赛。然而,偶尔我会增加探索系数,因为以下原因:我发现第一个玩家很快就会知道最好的第一步是在中间走,所以第二个玩家主要(当探索系数很小时)学会如何对付“中间的第一步”,而没有其他选择。为了解决这个问题(以及其他类似的问题),我偶尔会增加探索系数。
- 在第一次训练尝试中,我使用 ReLu 函数作为激活函数。我发现网络学习得不是很好,因为训练集有很多零标签样本,而 ReLu 函数学习起来很容易输出零。我用 LeakyReLu 函数替换了它,获得了更好的结果。
- 为了测试代理,我让它对着随机播放器播放了 300 集,测量了胜率。不到 2000 集就达到了 80%以上的成功率,但当我试图对抗它时,我很容易就赢了。后来,我通过让代理与固定代理竞争来测试学习,并测量了胜率。
- 最后我找到了一个很好的代理人。这不是一个超人的水平,但仍然很难击败它(你可以自己尝试)。
- 下图显示了 Q 代理在学习过程开始时相对于随机玩家的胜率。它还显示了平均 300 场比赛中每场比赛的平均移动次数。我们可以看到,随着胜率的增加,移动的次数减少,这表明代理人学会了赢,而且赢得很快。

DRL agent
蒙特卡洛树搜索
作为一种完全不同的方法,我使用蒙特卡罗树搜索算法或 MCTS 实现了一个代理。
这种算法背后的想法是创建一个博弈树,但不是探索所有可能的博弈,而是只选择最有希望的路线。树中的每个节点存储 2 个值:该节点被访问的次数,以及玩家访问该节点时赢得的胜利数。该算法包含重复多个情节的 4 个步骤。
- 选择——您从根——状态开始,并选择一个子——移动。我用置信上限(UCB1)来选择一个孩子。对于每个孩子,我计算了表达式: w/n+ csqrt(ln(N)/n)* 其中 w 是获胜的次数, n 是节点被访问的次数, N 是父节点被访问的次数, c 是在探索和利用之间平衡的因子。这是 MCTS 最重要的一点。选择最有希望的子节点,探索的机会很小。
- 展开—当您到达一个节点,其中有尚未访问的子节点时,随机选取一个节点并展开树。
- 模拟-玩随机模拟,直到游戏结束。
- 反向传播—反向传播到所有被访问的节点,访问次数增加 1,如果您获胜,获胜次数增加 1。
关于 MCTS 学习过程的几点说明:
- 代理要比 Q 学习代理多学很多集,但是学的快很多。在我的电脑上(不是很好的一台),100 多万集要学几个小时。
- 为了方便起见,每个玩家都有自己的树,尽管两棵树共享相同的信息。我发现这样做更严谨,而且与其他类型的智能体(例如 Q 学习智能体或随机智能体)进行游戏和学习更灵活。
- 存储一个 100 万集的树需要大约 800MB,并且当代理继续学习时它还在增长。所以它比 CNN 要大得多,CNN 在整个学习过程中保持不变。
- 下图显示了随着学习的进行,MCTS 代理相对于随机玩家的胜率。

MCTS agent
关于代码
我试图尽可能通用地实现代码。代码包含 4 种类型的类。
- 游戏类,包含了双人游戏的所有基础知识。特定的游戏类继承自这个类。我实现了 Connect 4 和 Tic-Tac-Toe 类。游戏类声明了所有需要在特定游戏类中实现的功能。
- 一个 Q 代理类——包含了一个双人游戏代理的所有基本功能。connect 4 Q 学习代理继承自该类。
- 模型类-包含神经网络模型和所有相关函数。
- MCTS 代理类——类似于 Q 代理类,但方法不同
如果你愿意,你可以通过只实现父类的已声明的未实现的函数来使用这个代码作为其他游戏或其他代理的框架。
总结
我们在这篇文章中看到了如何训练代理学习玩 Connect 4。尽管 Connect 4 有比 10⁴更多的可能状态,这意味着可能每一个玩过的游戏都是独一无二的,代理们学会了归纳并玩得很好。Q 代理玩家通过学习不同状态之间的相似性来学习归纳,而 MCTS 代理学习采取最有希望的路径。
我们现在可以尝试让两个代理相互竞争:

从结果中我们可以看到,第一个玩家在大多数情况下都是赢的,也就是说虽然看起来不是那样,但是第一个玩家有很大的优势。
原代码可以在这里找到
感谢您的阅读,如果您发现我的代码中有任何错误,请告诉我,我会修复它,如果您喜欢这篇文章,请鼓掌,如果您有问题,我将非常乐意回答。
深度强化学习:建立一个深度 Q 网络(DQN)来玩钢管舞

Photo by the author
在本教程中,我将向您介绍如何训练一个深度 Q-net(DQN)模型来玩掷骰子游戏。我们会用 OpenAI 的健身房和 TensorFlow 2。本文假设您对强化学习和深度学习有所了解。
什么是强化学习和 DQN?
让我们从强化学习和 DQN 算法的快速复习开始。强化学习是机器学习的一个领域,它专注于训练代理在环境中在某些状态采取某些动作,以最大化回报。比方说我想做一个打扑克的机器人(代理)。该机器人将与其他机器人在有筹码和卡片的扑克桌上玩(环境)。这个机器人应该能够根据桌上的牌、手里的牌和其他机器人的下注(状态)来弃牌或下注(行动)。机器人想要最大化它赢得游戏的筹码数量(奖励)。
DQN 是一种强化学习算法,其中建立了一个深度学习模型,以找到代理在每个状态下可以采取的行动。
技术定义
RL 的基本术语包括但不限于:*当前状态【s】****下一步状态【s’】****动作【a】****政策【p】奖励【r】。状态-动作-值函数 (Q(s,a)) 是一个主体从当前状态开始的期望总报酬,其输出称为 Q 值。正如我所说的,我们的目标是在状态【s】选择某个动作【a】*,以最大化回报,或 Q 值。
DQN 是深度学习和强化学习的结合。模型目标是逼近 Q(s,a) ,并通过反向传播进行更新。假设 Q(s,a) 的近似值为 y(hat ),损失函数为 L,我们有:
预测: y(hat) = f(s,θ)
损耗: L(y,y(hat)) = L(Q(s,a),f(s,θ))
在反向传播过程中,我们对损失函数求θ的偏导数,以找到使损失最小的θ值。因为这是监督学习,你可能想知道如何找到基本事实 *Q(s,a)。*答案是用贝尔曼方程。
贝尔曼方程: Q(s,a)= max(r+Q(s’,a))
在哪里
Q(s’,a)= f(s’,θ) ,如果 s 不是终端状态(最后一步的状态)
Q(s ',a) = 0 ,如果 s 则为终端状态
我们可以看到,当 s 为终态时, Q(s,a) = r. 因为我们是用模型预测 f(s ',θ) 来逼近 *Q(s ',a)*的真实值,我们把这个叫做半梯度。正如你可能已经意识到的,使用半梯度的一个问题是,模型更新可能非常不稳定,因为每次模型更新时,真实的目标都会改变。解决方案是创建一个目标网络,其中本质上是在特定时间步骤的训练模型的副本,因此目标模型更新不太频繁。
该模型的另一个问题是过度拟合。当我们在每局结束后更新模型时,我们已经潜在地玩了几百步,所以我们本质上是在做批量梯度下降。因为每一批总是包含一个完整游戏的步骤,模型可能无法很好地从中学习。为了解决这个问题,我们创建了一个体验重放缓冲区,它存储了几百个游戏的 (s,s ',a,r) 值,每次都从中随机选择一批来更新模型。
什么是横竿?
CartPole 是一种游戏,其中一根杆子通过一个未驱动的关节连接到一辆小车上,小车沿着无摩擦的轨道移动。钟摆开始直立,目标是通过增加和减少小车的速度来防止它倒下。单个状态由 4 个元素组成:小车位置、小车速度、磁极角度和磁极尖端的速度。
要移动杆子有两个动作:向左或向右移动。每走一步(包括终止一步),它获得+1 奖励。当杆倒下时,游戏结束,即当杆角度大于 12°,或手推车位置大于 2.4°(手推车的中心到达显示器的边缘)。新的健身房版本也有一个长度限制,当剧集长度超过 200 集时就会终止游戏。

履行
完整的代码是这里是。
1.构建 tf.keras 模型类
我们先在 TensorFlow 中实现深度学习神经网络模型 f(s,θ) 。在 TF2,急切执行是默认模式,因此我们不再需要先创建操作,然后在会话中运行它们。另外,TF2 在tf.function()提供亲笔签名。有两种方法可以实例化一个模型。更简单的方法是通过链接 Keras 层来指定模型的正向传递,并从输入和输出创建模型。然而,为了训练更复杂和定制的模型,我们需要通过子类化 Keras 模型来构建模型类。更多详情,请见此处。
在 MyModel 类中,我们在__init__中定义了所有层,并在call().中实现了模型的向前传递。注意,输入形状是【批量大小,状态大小(本例中为 4)】,输出形状是【批量大小,动作数量(本例中为 2)】。本质上,我们向模型提供状态,并输出在每个状态下采取每个动作的值。call() 的@tf.function 注释支持自动签名和自动控制依赖。
2.构建主 DQN 模型类
主 DQN 类是创建、调用和更新深度 Q-net 模型的地方。我们刚刚建立的神经网络模型是深度 Q-net 模型的一部分。
在__init__()中,我们定义了梯度下降的动作数量、批量大小和优化器。折扣因子γ是一个介于 0 和 1 之间的值,在下一步乘以 Q 值,因为代理人更不关心遥远未来的回报,而不是眼前的回报。我们还将 MyModel 初始化为实例变量self.mode,并创建体验回放缓冲区self.experience.,代理不会开始学习,除非缓冲区的大小大于self.min_experience,一旦缓冲区达到最大大小self.max_experience,它将删除最早的值,为新值腾出空间。
实例方法predict()接受单个状态或一批状态作为输入,运行self.model的正向传递并返回模型结果(动作的逻辑)。请注意,tf.keras 模型默认将输入识别为批处理,因此我们希望确保输入至少有 2 个维度,即使它是单个状态。
在train()中,我们首先随机选择一批 (s,s’,a,r) 值,用布尔 done 表示当前状态 (s) 是否为终止状态。然后我们调用predict()来获得下一个状态的值。注意,我们在这里使用复制的目标网络来稳定这些值。接下来,我们从贝尔曼函数中得到地面真值。如前所述,如果 state (s) 是终端状态,target Q(s,a) 只是奖励 *®。*在tf.GradientTape()内,我们计算真实目标和预测的均方损失。因为我们没有使用内置的损失函数,所以我们需要使用tf.one_hot()手动屏蔽逻辑。一旦我们得到损失张量,我们就可以使用方便的 TensorFlow 内置 ops 来执行反向传播。
RL 中的另一个重要概念是ε贪婪。ε是随时间衰减的 0 到 1 之间的值。这个想法是为了平衡探索和开发。当模型在开始时不太精确时,我们希望通过选择随机动作来探索更多,所以我们选择更大的ε。随着我们从玩游戏中收集更多的数据,我们逐渐衰减ε来更多地利用模型。ε-greedy 的实现在get_action()。
在add_experience()和copy_weights()中,我们实现了前面提到的经验回放缓冲和目标网络技术。每次我们从玩游戏中收集新数据时,我们会将数据添加到缓冲区,同时确保它不会超过定义为self.max_experiences的限制。我们将创建 DQN 类的两个实例:一个训练网和一个目标网。当训练网络用于更新权重时,目标网络仅执行两个任务:在下一步骤Q(s’,a) 预测值,以供训练网络在train()中更新,以及从训练网络复制权重。
3.玩游戏
终于可以玩游戏了!
让我们通过向play_game()函数传递 5 个参数来开始游戏:健身房预定义的横竿环境、训练网、目标网、epsilon 和重量复制的间隔步数。在函数内部,我们首先重置环境以获得初始状态。然后我们创建一个循环来玩这个游戏,直到它到达终端状态。在循环内,我们ε-greedy 选择一个动作,移动一步,将 (s,s ',a,r) 和 done 对添加到缓冲区,并训练模型。默认情况下,环境总是为每个时间步长提供+1 的奖励,但为了惩罚模型,我们在完成完整集之前,当奖励达到终止状态时,我们为奖励分配-200。iter记录我们在一场比赛中走了多少步,这样我们就可以在每一步copy_step将重量复制到目标网。游戏结束后,我们会返还全部奖励。
4.制作测试视频
训练完模型后,我们想看看它在横竿游戏中的实际表现。为此,我们简单地将 CartPole 环境包装在wrappers.Monitor中,并定义一个保存视频的路径。我们通过充分利用模型来玩游戏,游戏结束后会保存一段视频。
5.定义超参数和 Tensorboard 日志,并把一切放在一起
DQN 模型现在已经建立起来了,我们需要做的就是定义我们的超参数,为 Tensorboard 输出日志并训练模型。让我们看看这是如何在main()函数中实现的。
我们首先创建了体操侧身环境、训练网和目标网。然后,我们定义超参数和张量流摘要编写器。当前的超参数设置将在 15000 集后产生 200 的集奖励,这是当前 200 集长度内的最高奖励。然而,我们的模型很不稳定,进一步的超参数调整是必要的。
在 for 循环中,我们玩 50000 次游戏,随着玩游戏次数的增加而衰减ε。对于 Tensorboard 可视化,我们还跟踪每个游戏的奖励,以及窗口大小为 100 的运行平均奖励。最后我们通过调用make_video()做一个视频,关闭环境。

一旦测试完成,你应该可以在你指定的文件夹中看到这样的视频。
要启动 Tensorboard,只需输入tensorboard --logdir *log_dir*(the path of your Tensorflow summary writer)。

在您的终端(Mac)中,您将看到一个带有 Tensorflow 端口的本地主机 IP。点击它,您将能够在 Tensorboard 上查看您的奖励。
结论及对未来工作的建议
在本文中,我们了解到:
- 强化学习和 DQN 算法:
- 通过在 TF 2 中子类化 tf.keras.Model 来构建定制模型;
- 用 tf 训练一个 tf.keras.Model。gradient . Tape();
- 在包装器中创建视频。监测以检验 DQN 模型;
- 在 Tensorboard 上显示奖励。
但值得一提的是,由于自举、采样以及值函数逼近带来的不稳定性,DQN 并不能保证收敛。对 DQN 来说,横竿也是一个相对难学的环境。事实上,学习经常失败,上面显示的结果是在多次尝试后取得的。我很幸运地在 50k 个纪元内实现了收敛,但我怀疑这种情况每次都会发生。为了复制它,我强烈建议你至少运行几百万个纪元。
如果想要实现快速学习,可以使用较小的初始ε值,或者以较快的速率衰减。我当前的 epsilon 是 0.99,这意味着在 epsilon 衰减到一个很小的值之前,策略会渲染很长时间的随机动作。然而,ε的小初始值可能由于缺乏探索而导致学习陷入次优。
另一个重要的事实是,DQN 是一种偏离策略的算法,只要满足 Robbins-Monro 算法并且所有状态和动作已经被访问了无限次,Q 值甚至可以收敛于随机策略。因此,Q 值可能已经收敛,并准备在许多时代后用于预测,即使回报看起来并不完美。
为了使策略收敛,一旦值收敛,我们需要确保策略接近贪婪(ε接近 0),这可以通过播放足够多的剧集来实现。
为了进一步改进,我鼓励你探索更多的模型结构(更多的层,卷积层,其他激活函数等)。)和超参数设置(目标净复制步长、经验大小、ε衰减值等。).
我希望你能从这篇文章中得到乐趣。😃
3D 现实环境中无人机的深度强化学习
一个完整的代码,让您开始使用虚幻游戏引擎和 Python 在逼真的环境中实现深度强化学习。
注 1: 文中提到的 Github 资源库 DRLwithTL 已经过时。请使用以下更详细的存储库,而不是 https://github.com/aqeelanwar/PEDRA 的
注 2: 关于无人机强化学习更详细的文章可以在这里找到
概述:
上周,我公开了一个 GitHub 知识库,其中包含一个独立的详细 python 代码,使用虚幻游戏引擎在 3D 模拟环境中的无人机上实现深度强化学习。我决定在本文中介绍一个详细的文档。3D 环境在 Epic Unreal 游戏引擎上制作,Python 用于与环境交互,并使用 TensorFlow 进行深度强化学习。

Drone navigating in a 3D indoor environment.[4]
在本文结束时,您的机器上将拥有一个工作平台,能够在逼真的无人机环境中实现深度强化学习。你将能够
- 设计您的定制环境
- 将它与您的 Python 代码连接起来
- 使用/修改 DRL 的现有 Python 代码
对于本文,基本目标将是无人机自主导航。没有开始或结束位置,而是无人机必须尽可能长时间地导航,而不会撞到障碍物。代码可以修改为任何用户定义的目标。
完整的模拟包括三个主要部分
- 3D 模拟平台 —创建并运行模拟环境
- 接口平台 —模拟无人机物理以及虚幻和 Python 之间的接口
- DRL python 代码平台 —包含基于 TensorFlow 的 DRL 代码
这三个平台有多种选择。但是对于本文,我们将选择以下内容
- 3D 仿真平台— 虚幻引擎【1】
- 接口平台— AirSim [2]
- DRL python 代码平台— DRLwithTL GitHub 库[3]

本文的其余部分将分为三个步骤
- 步骤 1 —安装平台
- 步骤 2-运行 python 代码
- 步骤 3 —控制/修改代码参数
步骤 1 —安装三个平台:
建议为这个项目创建一个新的虚拟环境并安装依赖项。可以采取以下步骤来下载这些平台的入门指南。
- **克隆存储库:**包含 DRL 代码的存储库可以使用
git clone https://github.com/aqeelanwar/DRLwithTL.git
2.**下载 Alexnet 的 Imagenet 权重:**DNN 在初始化时使用 AlexNet 的 Imagenet 学习权重,而不是随机权重。这给了 DNN 一个更好的训练起点,并有助于趋同。
以下链接可用于下载 imagenet.npy 文件。
下载完成后,在 DRLwithTL 根文件夹中创建一个文件夹*‘models’*,将下载的文件放在那里。
models/imagenet.py
2.**安装需要的包:**提供的 requirements.txt 文件可以用来安装所有需要的包。使用以下命令
cd DRLwithTL
pip install -r requirements.txt
这将在激活的 python 环境中安装所需的包。
3.**安装 Epic 虚幻引擎:**您可以按照下面链接中的指南在您的平台上安装虚幻引擎
4.安装 AirSim: AirSim 是微软为代理(无人机和汽车)开发的虚幻引擎开源插件,具有物理和视觉上的逼真模拟。为了在 Python 和模拟环境之间建立接口,需要安装 AirSim。可以从下面的链接下载
一旦一切安装妥当,我们就可以进入运行代码的下一步了。
步骤 2 —运行 DRLwithTL-Sim 代码:
一旦下载并运行了所需的包和软件,就可以采取以下步骤来运行代码
创建/下载模拟环境
您可以使用虚幻引擎手动创建您的环境,也可以从下面的链接下载一个示例环境并运行它。
以下环境可通过上面的链接下载
- 室内长环境
- 室内扭转环境
- 室内通风环境
- 室内技术环境
- 室内金字塔环境
- 室内冰箱环境
上面的链接将帮助您下载 64 位 windows 环境的打包版本。运行可执行文件(。exe)来启动环境。如果您在运行环境时遇到问题,请确保 Documents/AirSim 中的 settings.json 文件已经被正确配置。您可以尝试使用 F、M 和反斜杠键来更改环境中的相机视图。此外,键 1、2、3 和 0 可用于查看 FPV、分割图、深度图和切换子窗口视图。
编辑配置文件(可选)
DRL 模拟的 RL 参数可以使用提供的配置文件进行设置,并在最后一节进行说明。
cd DRLwithTL\configs
notepad config.cfg **(# for Windows)**
运行 Python 代码
可以使用以下命令启动 DRL 代码
cd DRLwithTL
python main.py
运行 main.py 执行以下步骤
- 尝试加载配置文件
- 尝试连接虚幻引擎(室内 _ 长环境必须运行,python 才能与环境连接,否则将出现连接拒绝警告——除非建立连接,否则代码不会继续运行)
- 尝试创建 DNN 的两个实例(使用双 DQN)并用选定的权重初始化它们。
- 尝试为用户界面初始化 Pygame 屏幕
- 启动 DRL 算法
此时,可以看到无人机在环境中四处移动,收集数据点。下面的框图显示了所使用的 DRL 算法。

Block diagram of DRL Training and associated segments
使用 tensorboard 查看学习参数
在模拟过程中,可以在 tensorboard 上查看 RL 参数,如ε、学习率、平均 Q 值、损耗和返回。张量板日志文件的路径取决于配置文件中设置的环境类型、环境名称、和训练类型,由下式给出
models/trained/<env_type>/<env_name>/Imagenet/ **# Generic path**
models/trained/Indoor/indoor_long/Imagenet/ **# Example path**
一旦确定了日志文件的存储位置,就可以在终端上使用以下命令来激活 tensorboard。
cd models/trained/Indoor/indoor_long/Imagenet/
tensorboard --logdir <train_type> **# Generic**
tensorboard --logdir e2e **# Example**
终端将显示可以在任何浏览器上打开的本地 URL,并且 tensorboard 显示器将在运行时显示 DRL 参数。

使用 PyGame 屏幕的运行时控件
众所周知,DRL 渴望数据。对于复杂的任务,如仅使用前置摄像头在逼真的环境中进行无人机自主导航,在 DRL 收敛之前,模拟可能需要数小时的训练(通常在 GTX1080 GPU 上需要 8 到 12 小时)。在模拟过程中,如果您觉得需要更改一些 DRL 参数,可以使用模拟过程中出现的 PyGame 屏幕来完成。这可以通过以下步骤完成
- 更改配置文件以反映修改(例如降低学习率)并保存。
- 选择 Pygame 屏幕,点击 【退格】 。这将暂停模拟。
- 按下 ’ L’ 键。这将加载更新的参数,并将其打印在终端上。
- 点击 【退格】 键恢复模拟。
现在模拟只更新了学习率。其他变量也可以通过编辑模块 check_user_input 的 aux_function.py 文件进行更新。

Editing check_user_input module to update other parameters too
第 187 行的 cfg 变量有所有更新的参数,你只需要把它赋给相应的变量并返回值就可以激活它了。
步骤 3 —控制/修改 DRLwithTL-Sim 中的参数:
该代码使您能够控制
- 更改 DRL 配置
- 改变深度神经网络(DNN)
- 修改无人机行动空间
1.更改 DRL 配置:
提供的配置文件可用于在开始模拟之前设置 DRL 参数。

Config file used for DRL and sample values
- num_actions: 动作空间中动作的数量。该代码通过将摄像机帧划分为 sqrt(num _ actions)* sqrt(num _ actions)的网格来使用基于感知的动作空间[4]。
- train_type: 决定在 DNN 训练的层数。支持的值为 e2e、最后 4 个、最后 3 个和最后 2 个。更多的值可以被删除
- wait_before_train: 该参数用于设置训练应该开始的迭代。模拟在开始训练阶段之前收集了这么多的数据点。
- **最大迭代次数:**确定用于 DRL 的最大迭代次数。当这些迭代完成后,模拟停止。
- buffer_len: 用于设置体验回放缓冲区的大小。模拟继续收集数据点,并开始将它们存储在重放缓冲区中。从该重放缓冲器中采样数据点并用于训练。
- batch_size: 决定一次训练迭代的批量大小。
- **ε_ saturation:**ε贪心法用于从勘探阶段过渡到开采阶段。当迭代次数接近该值时,ε接近 0.9,即 90%的动作是通过 DNN(利用)预测的,只有 10%是随机的(探索)
- crash_threshold :该值与深度图一起使用,确定无人机何时被视为虚拟坠毁。当到深度图上中心动态窗口中最近障碍物的平均深度低于该值时,奖励-1 被分配给数据元组。
- Q_clip: 如果设置为真,Q 值在超出某个值时会被剪切。有助于 DRL 的融合。
- train_interval: 该值决定训练的频率。例如,如果设置为 3,则每 3 次迭代后进行训练。
- update_target_interval: 模拟使用双 DQN 方法来帮助收敛 DRL 损失。update_target_interval 确定模拟在两个 Q 网络之间转换的频率。
- dropout_rate: 确定连接被丢弃的频率,以避免过度拟合。
- switch_env_steps: 决定无人机改变其初始位置的频率。这些初始位置设置在相应环境名称下的 environments/initial _ positions . py 中。
- **ε_ 模型:**线性或指数
2.更改 DNN 拓扑:
可在以下 python 文件中修改用于将 Q 值映射到其状态的 DNN。
network/network.py #Location of DNN
不同的 DNN 拓扑可以在这个 python 文件中定义为一个类。代码带有三个不同版本的修改后的 AlexNet 网络。如果需要,可以根据用户需要定义更多的网络。一旦定义了一个新的网络,可以修改net owork/agent . py文件,在第 30 行使用所需的网络,如下所示
3.修改无人机行动空间:
当前版本的代码支持基于感知的动作空间。更改配置文件中的 num_actions 参数会更改前置摄像头被分成的箱数。

Perception-based action space — Default action space used in the DRLwithTL code [4]
如果需要使用完全不同类型的动作空间,用户可以通过修改以下模块来定义它
**Module:** take_action
**Location: ** network/agent.py
如果修改,这个模块应该能够将动作号(比如 0,1,2,…,num_actions)映射到无人机的相应偏航和俯仰值。
总结:
本文旨在让您在真实的 3D 环境中开始使用深度强化学习平台的工作平台。文章还提到了可以根据用户需求修改的代码部分。工作中的完整代码可以在文献[4]中看到。
参考资料:
- https://www.unrealengine.com
- https://github.com/microsoft/airsim
- https://github.com/aqeelanwar/DRLwithTL.git
- http://arxiv.org/abs/1910.05547
如果这篇文章对你有帮助,欢迎鼓掌、分享和回复。如果想了解更多关于机器学习和数据科学的知识,请关注我@Aqeel an war或者在LinkedIn上与我联系。
深度强化学习
像素乒乓:Keras 版本

这是 Andrej Karpathy 关于强化学习的博客文章的后续。我希望这篇文章能简化 Karpathy 的文章,去掉数学运算(感谢 Keras)。这篇文章应该是独立的,即使你还没有看过其他的博客。
这里的博客是为了配合更深入的视频教程(YouTube 视频描述中的代码):
什么是强化学习
与机器学习/深度学习中的其他问题不同,强化学习的问题在于我们没有一个合适的“y”变量。然而,输入“X”也没有什么不同。例如,在这个特定的例子中,我们将使用 openAI 中的 pong 环境。输入将是游戏当前状态的图像。输出是要播放的移动。
奖励函数
RL 中的任务给定游戏/环境的当前状态(X ),以采取将最大化**未来* *预期折扣奖励的行动。一个替代方法是,我们将游戏玩到最后,用一个折扣变量 gamma(数字在 0 到 1 之间)将当前时间点的所有奖励相加,如下所示:

R variable
为什么不简单的当前奖励 r_t?这是由于延迟奖励。当一个动作被采取时,它的影响不仅影响当前状态,还影响随后的状态,但是以衰减的速率。因此,当前行动负责当前奖励和未来奖励,但是越来越少的责任移动到未来。
模型
该模型用于生成动作。因此,在我们的例子中,我们使用图像作为输入,通过 sigmoid 输出来决定是向上还是向下。
关于上面提到的 R 变量,请注意我们的模型所产生的行动是如何导致奖励的。这很大程度上是一个盲人领盲人的例子。但是随着更多的迭代完成,我们收敛到更好的输出。
我们将使用的模型不同于 AK 博客中使用的模型,因为我们使用了卷积神经网络(CNN ),如下所述。使用 CNN 的优势在于我们需要处理的参数数量明显减少。具有 100 个神经元的 1 个隐藏层的密集网络将产生大约 640000 个参数(因为我们有 6400 = 80×80 像素)。然而在下面显示的模型中我们只有 3100 个参数。
另请注意,最后一层有一个 sigmoid 输出。这是为了让模型预测桨向上或向下移动的概率。
model = Sequential()
model.add(Conv2D(4, kernel_size=(3,3), padding='same', activation='relu', input_shape = (80,80,1)))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Conv2D(8, kernel_size=(3,3), padding='same', activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Conv2D(16, kernel_size=(3,3), padding='same', activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(2, activation='softmax'))
预处理
我们裁剪图像的顶部和底部,并在水平和垂直方向上每隔一个像素进行二次采样。我们将球拍和球的值设置为 1,而背景设置为 0。然而,输入到 DL 算法中的是两个连续帧的差。这导致输入图像的大小为 80x80。
损失函数
拼图的最后一块是损失函数。我们有自己的输入,也就是上面提到的 X 变量,但是,目标 y 变量是在那个时间步采取的动作。即向上将是 1,向下将是 0。
但是等等,y 变量不是模型规定的吗?是的,你完全正确。因此,我们不能简单地使用通常的交叉熵损失,因为概率 p(X)和 y 是由同一模型生成的。相反,我们所做的是用在那个时间点的预期未来回报来衡量。
因此,在每集结束时,我们运行以下代码进行训练:
model.fit(x, y, sample_weight=R, epochs=1)
然而,实际损失函数保持不变,
model.compile(optimizer='rmsprop',loss='sparse_categorical_crossentropy')
关键是,如果这一步棋走得好,我们使用上面的sample_weight功能来衡量他们。
摘要
总而言之,当你不关心实际的梯度计算时,政策梯度更容易理解。所有当前的深度学习框架都考虑到了你可能需要的任何衍生品。
策略梯度是更基本的强化学习问题之一。如果你想了解更多关于强化学习的知识,请订阅我的 YouTube 频道。该播放列表包含更高级 RL 算法的教程,如 Q-learning。
看这里是我关于机器学习和深度学习的课程(使用代码 DEEPSCHOOL-MARCH 到 85 折)。
深度 RL 案例研究:混沌梯度
为什么一些 RL 算法有臭名昭著的不可靠的梯度,最近的研究对此做了什么,以及替代方案的有效性如何?
策略梯度是 RL 中的经典算法。普通政策梯度(VPG)归结为试图增加产生高回报的行动的可能性。估计更新规则需要一些假设,但是它在实践中已经被证明是可行的。本文将假设 RL 中的一个简短背景,所以如果像马尔可夫决策过程、重要性抽样、梯度、似然性等词。我建议回到一些机器学习的基础知识。本文底部的一些资源可能会有所帮助。本文回顾了标准策略梯度,介绍了修改后的策略更新规则,并解释了为什么这种变化可能意味着更有用的学习算法。
最终,VPG 使用了一种仅基于行动概率和通过轨迹的回报的策略估计器,但 RL 的一篇新论文 PIPPS 建议通过学习动力学模型传播粒子来估计梯度,以显著减少方差,从而潜在地实现了 RL 策略生成的新的基于模型的方法的收敛。

Gif of policy gradient enabled cart pole, directly from source tutorial.
RL 复习工具
这里有一些你会在这篇文章中看到的符号。

RL 问题的大背景是观察在部分观察的世界中采用 RL 策略的代理所采取的行动。这些反馈循环是代理采取的动作以及它如何更新其策略参数。

Big picture of an RL problem. Source: Berkeley’s Deep RL Course
审查:普通政策梯度
在 VPG,梯度是在没有任何动力学模型知识假设的情况下估计的。首先,我们有一个 RL 问题的总体公式——优化参数以根据某个代理的进化最大化回报。

General formulation of any RL algorithm — maximize expected reward. Source: Berkeley Deep RL Course
这里需要注意的一点是,状态转换和动作概率是如何结合起来生成轨迹概率的。读者的一个练习是考虑在给定初始状态和转移概率的情况下,如何表达当前时间 T 的动作概率。

Trajectory evolution probability subject to a probabilistic policy. Source: Berkeley Deep RL Course
在政策梯度中,我们希望估计价值函数的梯度,并使我们的政策逐步达到最优。下面是关于轨迹演变的回报预期的扩展(稍后将更多地讨论状态转换的省略)。蓝色部分是一个叫做重要性抽样的关系——ML 中改变积分和期望形式的一个常用技巧。

The formulation of a standard policy gradient. Arrive at this from expanding the expectation with respect to the transition probabilities. Source: Berkeley Deep RL Course

Aside showing the relation for importance sampling. This comes from the derivative of the logarithm. Source: Berkeley Deep RL Course
具体来说,让我们看看构成一个策略的对数概率的项。如果我们将对数概率展开,得到一个带初始项的和(这个等式是我前面提到的练习的结果),我们会看到有两个相关项 1)政策概率和 2)转移概率。在这种情况下,转移概率不依赖于模型参数,并趋向于 0。如果我们可以用这种进化来通知我们的梯度呢?

Source: Berkeley Deep RL Course

Source: Berkeley Deep RL Course
有了这些公式和几个步骤,下面的政策梯度更新就出现了。这里,我们包括给定状态和动作的估计优势,这是一个状态的当前值和给定当前动作的当前状态的估计回报之间的差。这是您将在许多教程中找到的标准策略渐变。


策略梯度使用在确定性状态转换空间中采样的随机策略作为其轨迹。这不需要了解系统的演变,但假设一个确定性的模型。这就是经典的无模型强化学习。
这里有一段摘自 scholarpedia 的摘录,我发现它对于总结普通政策梯度中的估计假设特别有用:

Direct capture from source: http://www.scholarpedia.org/article/Policy_gradient_methods
注意,当在 RL 中使用成本函数时,我们通常镜像传播奖励梯度的设置,但是现在相对于一些函数 c(s)的负值最小化。这个负号经常被忽略,但是你可以思考一下为什么负成本函数最大化反映了回报。没有这种对偶,成本函数公式将会不同。
混沌诅咒和其他渐变谜题
这里有一些需要解决的政策梯度方法分歧的原因。
- VPG 历史上有很高的方差。这种差异是由用于生成梯度的所有样本都是从政策轨迹生成的这一事实引起的。如同在任何实验中一样,这些轨迹通常包括产生噪声梯度的不同数据集。大多数先进的政策梯度技术都是建立在核心理念之上的,即在减少差异的同时,将梯度推向具有更高回报的行动,如基线或改变抽样方法。
- 合成任何高度非线性的函数经常会导致数值发散。神经网络动力学模型已经显示出对于简单的实验很好地收敛,但是当在有噪声的数据上训练时,导数信息通常变得高度敏感。使用高斯过程动力学模型可以通过解析梯度缓解这一问题,但 GPs 缺乏神经网络的规模和效率。
- 混沌的诅咒:在下面的论文中,PIPPS 在基于模型的 RL 中模拟渐变时引入了这个想法。想法是用于初始化的神经网络策略,当通过任何类型的学习的动力学模型传播梯度时,对初始条件的小变化有很强的敏感性。对于一致且稳定的算法,初始条件的微小变化会导致结果的微小变化,PIPPS 开始着手解决这一问题。
PIPPS:灵活的基于模型的策略搜索,对混乱的诅咒具有鲁棒性

Illustration of methods for calculating gradients. PIPPS uses particle methods to estimate the true underlying distribution rather than attempting to analytically derive the underlying distribution. Source: PIPPS.
PIPPs 是我研究领域的一篇最新论文,以基于文章的政策研究的 P 机器人学 I 会议命名,解决基于模型的 RL 的政策研究中的正则化梯度问题。本文使用基于模型的 RL 在已知系统动力学的背景下计算政策梯度,在上文讨论的更典型的无模型变体之上构建基于模型的框架。在 PIPPS 的第 3.2 节中,作者使用带有随机动力学模型的确定性策略,通过动力学模型将基于粒子的梯度直接传播到策略中。回想一下 VPG 框架中对政策梯度的估计,但是由于确定性政策和随机模型,现在我们有不同的项趋向于 0。

Note, with a probabilistic state evolution, and deterministic policy, the action at time t is a result of the policy parameters, and the probability of an action given a state goes to 0 because the logarithm of 1 is 0 (deterministic policy).
策略参数梯度的这一新结果为我们提供了策略梯度更新规则的新的总表达式!此更新规则通过动力学模型对一组运行策略的粒子进行采样。这是一个基于粒子的动力学模型调节的渐变。

The gradient estimator based on particle based estimation with a deterministic policy and stochastic dynamics model. Source: PIPPS. This is the new policy gradient equation.
上面要注意的主要项目是状态转移的概率代替梯度估计中的动作概率。在 PIPPS 中,期望是跨越模拟粒子的,而在 VPG 中,期望是跨越实验轨迹的。PIPPS 设计用于学习动力学模型,而不仅仅是通过实验学习。**该梯度方程使用已知的模型导数和似然比来减少方差。**他们的基础实验结果显示了梯度方差的数量级减少。核心是动力学模型导数比神经网络策略导数更可靠。
如果您更详细地阅读论文中的,您将会看到更深入的方程,解释该估计器(似然比梯度)与重新参数化梯度和批处理模式并行计算的组合。这种直接梯度计算的激动人心的部分在结果中,第 4.1 节,图 3,其中这种 LR 梯度与它们的总传播算法具有相同的方差数量级。
与我的研究有关
PIPPS 为我们提供了一种工具,可以在传播梯度和进行“无模型 RL like”策略更新时使用学习到的动力学模型(我可能会添加一篇总结基于模型的实验 RL 最新进展的文章的链接)。由于估计量的高方差,仅基于随机策略的标准策略梯度估计在实验中有规律地发散。加上组成神经网络动力学模型的高方差,普通策略梯度需要一些增强来收敛。 PIPPS 允许我们使用基于粒子的方法调整基于 VPG 轨迹的梯度的方差,利用潜在的动力学模型。

Standard policy gradient learning process. All online experiments aimed to maximize a reward signal of on policy rollouts.
关于这种正则化技术在我们的情况下是否有效的最终问题是 PIPPS 梯度和轨迹估计是否可以处理实验学习的动力学模型的高认知不确定性(认知方差是由实验中的噪声引起的,该噪声可以被模型捕获,但不能,与重复过程中的随机性或方差相比,它是任意的不确定性)。我们的模拟使用了一个动力学模型,该模型被训练成用方差上的正则化项来匹配状态的变化,作为基本事实。策略梯度通常被认为是一种基于策略的算法,因此我们通过将我们学到的动态作为基础事实来欺骗这一标准,这在策略学习中存在明显的风险,以利用模型中的不准确性。
**假设从动态模型引入的方差明显小于改变梯度传播方式所获得的减少量。**我们正在使用实验数据来学习动态模型,并制定不同类型的政策。希望 PIPPS 能够通过依赖一个训练有素的动态模型而不是实验中的部署来实现更加高效的离线策略生成。我们将基于 PIPPS 的政策梯度作为基线的其他方法有:MBRL+MPC、模仿 MPC 和 ME-SAC(模型集合软演员评论家)。PIPPS 是以下问题答案的一部分:给定一组来自机器人的初始实验数据,我们对系统动力学没有基本的了解,那么生成控制策略的最佳方式是什么?

Our experimental sketch. We will use the regularization of PIPPS to enable offline model-based policy generation on purely experimental data. We can now use experimental data from any run to generate a dynamics, create a simulation environment, and train a policy.
梯度架构中的这种微妙变化可以利用我们已经或可以学习的知识,这就是为什么我对基于模型的 RL 的实际使用感到兴奋。通过使用我们拥有的背景,我们应该能够做出更可靠的梯度估计和政策改进。我希望在未来几年看到越来越多基于模型的强化学习的应用,但我确实看到无模型学习的总体目标在试图理解人类大脑如何运作方面更令人信服和有趣。
其他链接
我正在进行使用 PIPPS 在已学习的动力学模型上训练政策的实验,你可以在这里跟随并试验代码:https://github . com/NATO Lambert/dynamics-learn/blob/master/PIPPS _ psuedocode . py。我可能会创建一个关于实验的后续帖子,但在发表之前不会发布完整的细节。向 nol@berkeley.edu 提出问题。
参考
- 来自的方程式 OpenAI 在 RL 上关于政策梯度的旋转。他们的文档并没有包含所有的算法,但是质量是一流的。
- [伯克利的深度 RL 课程。](http://rail.eecs.berkeley.edu/deeprlcourse/)我在 2018 年秋季上了这门课——很棒的东西。我很幸运能与谢尔盖·莱文的研究小组中多名极其聪明的学生合作。
- Scholarpedia 关于政策梯度的注释。本资源的新内容,但它们为理解本主题提供了很好的范围&细节。
- 皮普斯论文:帕尔马斯,p .,拉斯姆森,C.E .,彼得斯,J. &多亚,K…(2018).PIPPS:灵活的基于模型的策略搜索,对混乱的诅咒具有鲁棒性。第 35 届机器学习国际会议论文集,PMLR 80:4065–4074。
厚三明治

Picture a room with grid lines on the walls, and look at the corner. If you’ve ever tried to learn some A.I., you’ll have heard that deep neural networks are “stacks of layers built from linear transformations followed by rectified nonlinearities.” All that jargon hides a simple idea. Our room is just the “graph” of one layer. Each layer takes a grid of squares, and bends it to look like the room. As intimidating as the mathematics of modern A.I. can seem from the outside, behind the scenes we’re mostly just taking the space where your data lives and then doing the room thing over and over. (Photo by Kevin Laminto on Unsplash).
想象一下,通过食物基本定律的奇迹,有人发现我们可以制作任何我们想要的东西,只要用两种原料:面包和火腿制作三明治。如果我们只是单独堆放面包,这种现象就不会出现。一大堆面包只是,嗯,面包。但是假设我们发现用正确的烹饪程序,一个足够大的顺序面包+火腿+面包+火腿+等等。可以变成鸡汤、千层面或鱼子酱——或者人类厨师从未想过要创造的食物。
如果这个例子看起来不可信,很好!大多数人工智能研究人员也没有预见到它的到来。但从 2012 年(或 2009 年(或 20 世纪 80 年代(或 20 世纪 60 年代,取决于你开始计算的时间))开始,某个专注于“如何从数据中学习东西”的数学领域有了自己的“不可能的三明治”时刻。从那时起,那个领域的“杂货店”,有着各种不同的成分,用于不同的特定目的,在“通用火腿”的稳步前进下,开始变空并改变形式。在非常真实的意义上,这就是最近围绕深度学习的所有炒作的全部内容。
人工神经网络是我们的三明治。深度神经网络只是有很多层的三明治。什么算“深”?那并不重要。为了理解最近围绕深度学习的所有炒作的原因,我们真正需要理解的是(1)“成分”,和(2)“烹饪”过程。
“面包”层就是数学家所说的线性变换。想象空间是一张有网格线的有弹性的图纸。任何扰乱空间的方式,其中之前是线的东西保持在之后是线,都是面包的一个例子。所以,旋转是面包,因为当我们旋转空间时,任何一条线都是一条线。通过把所有东西都变大一倍(或一半,等等)来延伸空间。)是另一种面包。我们也可以抓住空间的两边,向下移动我们的左手,向上移动我们的右手,这样垂直线保持垂直,水平线倾斜,小格子变成平行四边形。那叫剪羊毛,也是一种面包。这差不多是所有的面包了。如果我们把一堆这样的东西一个接一个地堆叠起来,那么由于每片面包都保持线条作为线条,整个堆叠也会保持线条作为线条。
所以,一堆面包不是三明治。仅仅靠堆积面包本身,我们不会取得任何进展[1–3]。那么,什么是“火腿”?
事实证明火腿几乎可以是任何东西。重要的是,这是一些步骤,弯曲网格线,使他们不再是线。所以,一个随机选择的三明治会旋转/缩放/剪切空间,然后弯曲一些网格线,然后再次旋转/缩放/剪切,然后弯曲更多的网格线。如果我们把三明治做得足够“深”,我们可以想象原来的空间可能会弯曲并折叠很多。
目前尚不清楚的是,我们究竟应该如何利用这一切完成任何事情。
现实世界中的深度神经网络
现实世界的使用问题都归结于烹饪过程。
实际上,我们并不知道如何写出好的神经网络。我们知道如何训练他们。训练神经网络意味着向它展示我们希望它解决的问题的例子,然后告诉它如何稍微改变自己,使其更像一台机器,让给出正确的答案。我们一遍又一遍地这样做,直到网络“学会”解决问题。
这给了我们一种与计算机互动的新方式——一种通常被称为“软件 2.0”的方法[6]。有了软件 2.0,就好像你不知道如何烹饪千层面,但你有一些旧的千层面,你有一个神奇的烤箱——一个让你只需把一个大(即“深”)火腿三明治放在一堆千层面旁边,然后等一会儿,瞧!…是千层面。你刚刚“煮”了千层面吗?有点——还不清楚。显而易见的是,你现在又多了一份千层面,而不需要知道怎么做。
这对整个计算来说意味着我们现在可以解决我们不知道如何解决的问题。我们可以编写我们不知道如何编写的程序。委婉地说,这改变了一切。
这种方法在现实世界中的用例(和限制)或多或少是您从烹饪类比中所期望的。如果我们没有这些千层面样品展示给我们万能火腿,我们就不可能做出新的千层面。这在实践中意味着,深度学习最直接适用的用例是那些我们有很多很多例子的用例。也就是说,大数据集,沿着我们想要解决的任何问题的边界被清理和分离。如果我们想训练一个深度神经网络来检测图片中的猫,我们不需要知道如何编写代码来实现一个检测猫的视觉系统,一行一行,一条一条地。我们确实需要大量的猫的照片。加上一个巨大的未煮过的网络,和一些时间。
深度神经网络的市场采用
深度学习已经被你能说出的每个行业的市场所采用,并且它的采用没有放缓的迹象。你的手机中有深层网络,可以将语音转录成文本,听你说“好的,谷歌”或“嘿,Siri”。有网络可以检测相机视野中的人脸,并根据他们的位置和地点设置自动对焦。他们已经学会了如何阅读。他们是 Google Translate 等系统的支持者,Google Translate 曾经包含大量短语表以及每一对语言的可能翻译。他们不仅打败了国际象棋和围棋的世界冠军,而且更令人印象深刻的是(也很少有人强调),他们打败了 30 多年来最优秀的人工智能研究人员试图通过使用特定领域的知识来设计手工编码的“智能”算法来玩这些游戏 AlphaZero 的最新版本不是通过对单个游戏的内置知识来实现超人的性能,而是通过让算法一次又一次地与自己对弈,直到它超越最熟练的人类和我们人类所构想的最佳算法。
甚至在科学研究中,以前不可逾越的障碍也开始消失。生物化学中最基本的问题之一是蛋白质折叠。这个问题对于药物研发来说至关重要,因为蛋白质在体内的功能是由其三维形状决定的。2018 年 12 月,谷歌的 DeepMind 发布了一个名为 alpha fold[7–8]的模型。他们训练了一个神经网络,根据其残基之间的成对距离来预测蛋白质的结构,这样做成功地远远超过了现有的方法,导致一位科学家评论说,在第一次听到结果时,他觉得自己和他所在领域的其他学者已经过时了[9-10]。
虽然距离我们学会在所有活动中超越人类智力还有很长一段时间,但有一点是肯定的:深度学习将会持续下去。人类生活的任何领域都不可能不变。
进一步阅读
面包
【1】3 蓝色 1 棕色。线性变换和矩阵。
【2】3 蓝色 1 棕色。矩阵相乘作为合成。
【3】3 蓝色 1 棕色。三维线性变换。
火腿
【4】deep mind。深度学习第三讲。神经网络基础。(相关章节从 37:47 开始)。
【5】蒙图法尔,帕斯卡努,乔,&本吉奥(2014)。关于深度神经网络的线性区域数。
烹饪
【6】卡帕西。软件 2.0。
结果
【7】罗伯特·f·服务。谷歌的 DeepMind aces 蛋白质折叠。
【8】deep mind。 AlphaFold:利用人工智能进行科学发现。
【9】西加尔缪尔。一位科学家如何应对艾在他一生的工作中击败他。
【10】穆罕默德·阿尔库莱希。刚刚发生了什么?
深海对比

Depth’s correlation with temperature
“世纪发现”
当我在互联网的黑暗深处搜寻数据时,我偶然发现了*“世纪发现”*数据集不仅非常有趣,而且相当大。这对于测试我的机器学习模块车床的可用性来说非常棒,我即将为它发布一个新的稳定版本(0.0.3),这个版本确实增加了可用性,所以如果你对 Julia 感兴趣,但不想被困在 GitHub read-me 文档中,现在是时候做出改变了。此外,凭借高达 864,863 次的观察和 74 个功能,我正式指出,我肯定想要一个巨大的深度学习桌面。还有,笔记本在这里。
应该指出的是,这个问题与车床或 Julia 中的机器学习毫无关系,而是:我的个人电脑内存不足。此外,没有一个性能问题来自执行测试或获得预测,这仅需要 1-6 秒,而是绘制了 200,000 个点。增加性能问题的是我过时的 R9–290 4GB,带有 AMDGPU Mesa 驱动程序(通过重新编译 Grub 实现。)对于像我一样好奇的硬件爱好者来说,我只有 8GB 的单通道 DDR3,股票时钟是 2666 MhZ,但我不认为它会以那个频率运行,这可能是影响最大的(Jupyter 不断返回说它的内存不足。)
阅读和清洁
首先,我们必须使用 CSV.jl 读入我们的数据帧
using CSV
df = CSV.read("bottle.csv")
为了更好地查看数据,我喜欢这样做:
show(df, allcols = true)
我拿出了几个看起来很有趣的连续特性,我想对它们进行测试,当然这是一种标准做法,但我也要注意,用 dataframes.jl 分析数据并不像我希望的那样好:
using DataFrames
df = DataFrame(:TempC => df.T_degC, :Salinity => df.R_SALINITY, :Depth => df.Depthm)
该数据帧包含大量缺失值,但是在删除之前将我们的要素删除是不可或缺的,这样我们就不会删除多余的内容。为了删除数据,我使用了:
df = dropmissing(df)
主意
我想我们应该从我们所拥有的开始,或者更确切地说,是我决定在这个特定的时间里保留的东西。我们有三个特征,深度,温度和盐度。利用这个我可以做一点基于物理学的科学推断,并提出以下假设:
假设 1
如果水的深度更低,那么温度应该更低,因为水应该更重,离太阳更远。
假设 2
如果水的深度较低,那么水的盐度应该较高,因为含盐量少的水应该比含盐量多的水浮力大。
假设 3
如果水的盐度更高,那么水的温度应该更低,因为更高的盐度将导致更高的深度,以及相同质量的更密集的分子。
构成盐的两种元素是钠和氯。)Na 有 11 个质子,Cl 有 17 个。每个分子加起来有 28 个质子。另一方面,H2O 是两个氢原子,都有一个质子,而氧原子有八个质子,加起来每个原子只有 10 个质子。快速浏览周期表可以发现,氢的原子质量是 1.0079,氧的原子质量是 15.9994。至于氯化钠,钠的原子量是 22.9897,氯的原子量更高,为 35.453,我们当然可以把这个代入朱莉娅:
function molecular_weight(atomm1,n1,atomm2,n2)
res = (atomm1 * n1) + (atomm2 * n2)
return(res)
end
然后我们可以输入我们的数字:

所以,当然,水中的氯化钠越少,水的浮力就越小。
但是现在化学已经足够了,因为我是数据科学家,不是化学家…
预处理
预测建模最重要的部分之一是预处理。幸运的是,车床已经为我们覆盖了这一部分。这就是我们在 Julia 使用车床时需要做的:
using Lathe.preprocess: StandardScalar, TrainTestSplit
现在,我们需要将数据放入测试集和训练集。车床使这变得非常简单,只需做:
train,test = TrainTestSplit(df)
很简单,对吧?现在,我们必须选择我们的目标和功能。我们将会看到我已经进行了统计实验的数据。我也将在这里应用标准标量。
对于我们的功能,我们将从预测深度对温度开始,看看我们有什么。
trainX = StandardScalar(train.TempC)
trainy = train.Depth
testX = StandardScalar(test.TempC)
testy = test.Depth
简单! 现在让我们做一些机器学习。
获取基线
为了快速获得基线,我们将:
using Lathe.models: MeanBaseline
bs = MeanBaseline(trainy)
请注意,我们不必拟合 x。然后,我们可以用以下公式预测我们的 train-x:
baser = predict(bs,testX)
确认
为了验证我们的回归模型和基线,我们将使用平均绝对误差和 R。当然,R 并不是对平均基线预测的真正验证,所以我们将使用 MAE。
using Lathe.validate: mae,r2
mae(testy,baser)
我们的回报是:
211.39011214882413
我选择研究 TestX 和 TestY 的平均值,鉴于这里有如此多的数据,我可能会考虑回去调整更大的训练集和更小的测试集,因为测试是在大约 250,000 个观察值的基础上进行的,这是非常巨大的,只会使验证和模型探索无法进行。

嗯,结果是不幸的…至少,老实说,我需要双重通灵。我不会担心,因为我们可以很快重启内核…
我能够将数据分割成大约 95 %,仍然有大约 40,000 个观察值的训练集,在我(和我的计算机)看来,这已经足够了。
线性回归
首先,像往常一样,我们拟合我们的数据:
linrmod = LinearRegression(trainX,trainy)
然后我们将构建的模型类型放入 predict()方法中。
preds.LinearRegression = predict(linrmod,testX)
那么模型做的怎么样呢?
嗯……很复杂。

我们的平均绝对误差低得离谱,远低于 1 的一半。然而,我认为考虑我们的模型和数据是很重要的。首先,我们需要数据的平均值,以便很好地了解一个大的误差会产生多大的影响。
using Lathe.stats: mean
mean(testy)
返回了:
10.864412863141867
虽然,在某种程度上,平均值为 11,平均绝对误差为 0.00550,这并不坏,但当我们得到回归的相关系数®时,问题就来了

那么:这告诉了我们什么?线性回归不是预测该数据的最佳模型。这当然是逻辑回归的工作,但我还没有为此编写模型,至少现在还没有,所以让我们尝试一些其他的回归模型,但首先,让我们看看这里发生了什么。

上面我们看到的是实际数据,它显然有一个负斜率,但是非常非线性。换句话说,假设方差在低深度和高深度基本不存在,线性模型可以给出我们可以预期的想法,但实际上不能精确建模。这是我们模型的样子:

为了给出一个更好的想法,我认为在 GNU 图像处理程序中用 alpha 覆盖这两个可视化效果会产生一些思考:

另外,如果你在 Julia 工作,并且想使用 Plots.jl 快速深入地了解 GR,你可能会发现这篇小文章很有用。
该模型实际上在这里找到了相关性,然而,问题在于相关性并不是真正线性的。这当然是任何线性模型的弱点,即一条线在它的整个行程中以一定的斜率延伸。所以看这个,很明显我们需要一个不同的模型来预测这个。我们也看到了为什么我们有这么低的平均绝对误差,同时也有这么低的 R 值,模型预测是负面的。因此,由于我们还没有在车床 0.0.3 逻辑回归,我想我们会尝试一些其他模型,只是为了好玩,每当我编程逻辑回归,和一些更多的模型,我们将重新访问这些数据。
安装一个方形模型(上帝保佑我的电脑)
using Lathe.models: FourSquare
m = FourSquare(trainX,trainy)
与之前相同的过程,我们将获得一个预测,并验证它。但是在的意思是时间(明白吗?)如果你想了解更多关于 FourSquare 模型的信息,我有一篇关于它的文章在这里,尽管现在它是基于 LinearLeastSquare(回归),(将来可能会有 LLSQ 类型参数。)
仅仅是并排看这两个模型就可以很好地看出,FourSquare 模型在这里确实做得很好。我很好奇将来进入车床的新型号将如何处理这样的测试。


Left: FourSquare | Right: Original Data
我们学到了什么
我认为这对我来说是一次非常宝贵的学习经历,我希望对你也是如此。我总是说可视化可以真正帮助我们理解我们的数据到底发生了什么,但永远不要把它等同于任何事情。我认为这个建议的有效性在这里明确地显示出来。
我期待将这个数据集用于其他模型(并获得更好的准确性)。)我觉得这里的相关性还不足以证明一个模型的合理性。不管怎样,我的目的是测试我的软件包,这就是我所做的,并且从中获得了很多乐趣。
多领域深度学习应用案例分享
898

被折叠的 条评论
为什么被折叠?



