TowardsDataScience-博客中文翻译-2021-七十五-

龙哥盟 / 2024-10-22 / 原文

TowardsDataScience 博客中文翻译 2021(七十五)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

合理的车辆统治道路

原文:https://towardsdatascience.com/reasonable-vehicles-rule-the-road-7a605ac2e1be?source=collection_archive---------45-----------------------

自主车辆的语义推理和本体

德乐思教授在 Unsplash 上拍照

自主是第四次工业革命的中心支柱。然而,全自动驾驶汽车仍然是一个发展领域。自动驾驶汽车在高风险环境中运行——如果出现问题,生命可能会受到威胁。为了有效运行,该系统需要类似人类的推理和决策能力。

智能交通系统社区对基于本体的道路环境建模的兴趣越来越大。本体为自动驾驶汽车的发展提供了许多好处;它们已被用于表示道路交叉口,创建驾驶决策系统,以及为传感器数据提供隐含和明确的含义和上下文,例如感知的道路对象之间的关系。

可以使用语义推理(规则)来集成各种级别的本体,以创建“共同的高级本体,从而产生统一的知识库”。[1]使用本体方法可以模拟道路环境,推理道路状况。

决策和车辆运动规划所需的知识可以从这个统一的知识库中推导出来。本体提供了一种灵活的架构,因为各种较低级别的地图可以用于向高级别本体提供信息。

为了补充这篇文章,我们还制作了一个动画视频“在路上推理”,它用大致的笔触展示了 RDFox 的能力。请继续阅读,我们将在下面详细探讨其主题。

这篇文章的灵感来自于邱浩南和他在宝马集团和乌尔姆大学的同事所做的获奖研究。[2]此处表达的观点是作者的观点。

完整的白皮书请点击这里。

自主车辆的语义推理

宝马的研究人员提出了一种自动驾驶汽车发展的新方法,在其知识空间架构中使用了语义推理引擎rd fox。

语义推理是“从明确可用的信息中进行逻辑推理的能力”。这种方法模拟道路环境,提供动态更新机制,并以语义推理为核心。推理使信息得以聚合和整合,从而了解环境并做出有效的决策,例如,在适当的时候改变车道,使汽车离开高速公路。

RDFox 提供了一个全面的软件组件,可用于以有限的处理能力实时处理和集成数据。由于其增量处理能力,RDFox 可以处理大量的、持续的数据流。这意味着当新数据进来时,只有受影响的计算会被更新。

使用规则 RDFox 可以进行实时决策,从而实现运动控制。通过利用语义推理的能力,逻辑可以集成到软件中。这意味着与驾驶相关的规则和最佳实践可以嵌入系统中。

由康纳·威廉姆斯在 Unsplash 上拍摄的照片

合理驾驶

自动驾驶汽车的出现带来了许多好处,包括提高道路安全性、实时路线优化、增加车道容量、降低能耗以及提高用户的时间效率。RDFox 适合这个应用程序,因为它像一个(称职的)人类驾驶员一样,是一个复杂的推理器,可以扩展,并针对快速、增量推理进行了优化。思维速度在自动驾驶汽车中至关重要,因为毫秒级的差异可能是高速公路上的一起事故。

在现实中,自动驾驶汽车的安全运行参数将决定它们是否运行以及如何运行。例如,这将确定最大速度、车辆之间的最小距离以及在允许机动之前必须满足的最小条件,例如变道。推理越快越准确,安全操作窗口越大越灵活,自主运输的好处也就越大。

RDFox 提供的内存解决方案和经济存储也增强了其可行性,因为它提供了有效保存车载信息的能力,同时还集成了各种数据类型。

虽然 RDFox 拥有与图形数据库同义的所有功能,但其增量语义推理能力使其特别适合满足自动驾驶的需求。

由约瑟夫·库珀在 Unsplash 上拍摄的照片

自动驾驶汽车需要语义推理

语义推理是赋予自治系统显著信息和决策能力的关键,同时降低它们对大数据馈送和巨大处理能力的依赖。

宝马研究人员的贡献表明了各个领域的改进,包括:嵌入安全驾驶实践领域知识的能力;数据量问题;传感器的监控;和可解释性。

1.安全驾驶实践

规则和常识有助于人类规划和推断道路状况下负责任的驾驶行为。在阳光明媚的周末,司机可能会在开车经过住宅区时放慢速度,因为他们预计会有更多的行人和孩子在路边玩球。语义推理使得对这些隐含的概念进行建模成为可能,从而可以大规模地应用适当的决策,而不必单独地明确陈述每个变化。

自动驾驶汽车还必须能够预测潜在的结果,并实时做出反应。例如,如果有人意外走到马路上,汽车必须做出反应。通过将安全驾驶实践领域的专业知识建模到系统中,我们可以提高自动驾驶汽车的安全性。

此外,在人类驾驶所犯的错误中, 98.3%是由于注意力不集中或无效驾驶。当这种错误与通过超速或驾驶过于靠近前方车辆而故意降低安全裕度相结合时,结果是严重且潜在致命的事故。通过消除容易致命地结合这些错误和违规的人为因素,可能会减少道路事故,道路事故在全球范围内每年导致超过 120 万人死亡和 5000 万人受伤。虽然你永远无法消除人为错误(毕竟,必须编写软件和设计传感器),但通过将决策时刻从时间关键的情况转移到实验室,灾难的可能性会降低到与其他制造公差相当,如断裂故障。

罗斯·索科洛夫斯基在 Unsplash 上的照片

2.数据卷宗

数据处理的量和计算压力是那些使用 HD 地图开发方法的人非常关心的问题。邱和她的同事发现,使用 RDFox,他们可以在内存数据存储中保留所有相关信息,只要需要。

通过使用空间推理,可以创建一个“空间窗口”。这使用汽车周围的特定距离定义了一个范围,将存储该范围的信息。它为即将进入空间窗口的区域确定要预加载的信息,一旦超出该范围,该信息将从系统中逐渐删除。

这限制了在任何给定时间存储和处理的数据量,大大提高了处理效率。地图在沿着路线行进的同时被递增地更新。这是第一次有可能在船上直接做这些事情。

3.数据集成

目前,由于缺乏标准化,制造商可获得的各种地图都是不兼容的格式。RDFox 非常适合解决这个问题,因为它能够将来自不同位置和格式的数据集成到一个统一的知识图中,然后可以对其进行推理。

通过使用 RDFox,制造商可以将现有的地图知识库集成到他们的系统中,并在此基础上进行构建,而不是从头开始。这代表了地图发展的重大突破。

4.传感器

规则为传感器的安全问题提供了解决方案。当由于传感器错误或缺失而导致信息不完整时,可以使用规则来确定决策,并且可以依赖于整合到系统中的大量其他信息。例如,如果一个传感器出现故障,那么汽车将能够聚集来自其他传感器的信息,以确保安全驾驶继续进行。

此外,当传感器数据不一致时,可以使用规则将这种情况实时标记给汽车。例如,规则可以通过验证从融合的传感器和地图数据本体中导出的知识的一致性来防止错误的紧急中断。

照片由纳比尔·赛义德在 Unsplash 拍摄

5.可解释性

由于安全性是将自动驾驶汽车集成到主流道路系统中的一个巨大问题,因此能够解释车辆的行为是开发人员感兴趣的。目前,机器学习技术无法提供可解释性。如果发生事故,或者用户对自动驾驶汽车为什么选择特定路径感兴趣,他们无法从系统中获得解释。

使用 RDFox,系统可以提供可解释性。这将考虑到问责制,也为制造商和调查人员提供透明的信息。正如黑匣子和学习文化有助于改变飞行安全一样,自动驾驶汽车控制系统的透明度将有助于在道路运输中实现同样的目标。事实上,认为航空比开车安全几个数量级是令人吃惊的。

展望未来

越来越多的研究在自主车辆和其他自主应用中使用本体和语义推理。宝马和乌尔姆大学的同事邱浩南(音译)在研究中提出的新方法,探索了动态地图处理的本体论方法的使用,以及决策和运动控制的推理。

将 RDFox 纳入环境建模和知识空间架构以促进自主车辆决策,为将类人推理应用于该领域开辟了新的可能性。选择 RDFox 是这项研究不可或缺的一部分,因为它具有独特的能力,能够在自动驾驶汽车的严格限制和时间敏感的驾驶舱中快速准确地执行任务。

结果令人鼓舞,表明使用语义推理的本体论方法可能有助于实现车辆的完全自主,当然还有机器人的其他领域。

附加参考

  1. 贝卢瓦尔、布齐德和穆阿迪卜。(2010).基于本体的人机交互空间规划。在第 17th Int。时间表示和推理研讨会(TIME 2010) ,第 103-110 页,美国加利福尼亚州洛斯阿拉米斯。IEEE 计算机学会。
  2. 邱、Ayara 和 Glimm (2020a)。自主车辆中地图数据的知识架构层。乌尔姆大学人工智能研究所。
  3. 邱、Ayara 和 Glimm (2020b)。自动驾驶中基于本体的动态地图处理。在 KEOD。

本文中的视频是牛津语义技术公司的财产。

选择 gRPC 而不是 REST 的原因,以及如何在 Python APIs 中采用它

原文:https://towardsdatascience.com/reasons-to-choose-grpc-over-rest-and-how-to-adopt-it-into-your-python-apis-197ac28e22b4?source=collection_archive---------6-----------------------

小窍门

gRPC 什么时候会成为 REST 的替代消息传递解决方案?

照片由 Mathyas Kurmann 在 Unsplash 上拍摄

很长一段时间以来,我们一直在休息(表象状态转移)。REST APIs 舒适、温暖、友好,并且非常灵活。如果允许我在这里有点唐突,感谢上帝我不用再用肥皂了!

那么,我们究竟为什么要再次改变一切呢?为什么我们一直在发明新的闪亮的东西来代替工作得如此好的工具?这个 gRPC 现在是什么东西?

嗯,简单的回答是,“没有放之四海而皆准的尺寸。”如果你要尽可能可靠、快速地传递一封信,你会怎么做?我脑海中闪现的第一个想法是我会自己做这件事。跳上我的车,开到目的地,用我自己的手指按门铃。

但也许我没有时间自己做。因此,我可能会呼叫优步并使用相应的服务。现在就等;这太贵了。还有,如果目的地在世界的另一边呢?特快专递服务是最终要走的路。同一问题的不同解决方案。

在任何情况下,当设计微服务之间的通信 API 时,了解使用一种形式的消息传递技术优于另一种形式的许多优点和缺点是至关重要的。在这个故事中,我们看到了何时选择 gRPC 而不是 REST,并在 Python 中实现了 gRPC 服务。

学习率是为那些对 AI 和 MLOps 的世界感到好奇的人准备的时事通讯。你会在每周五收到我关于最新人工智能新闻和文章的更新和想法。在这里订阅!

什么是休息?

正如我们已经看到的,REST 代表“代表性状态转移”它提供了一套关于如何创建 web APIs 的指南。但也仅此而已,一套准则。它不试图强制执行任何东西。

回到我们的邮件递送类比,有一些写信的指南。你可以从日期、信息和收件人的地址开始,然后是称呼。最后,你可以在信的末尾签上自己的名字。

然而,有没有人或事来执行那些规则呢?邮递员会拒绝投递不符合这些准则的信件吗?不要!如果你愿意,你甚至可以在里面画一幅画。毕竟他们说一张图胜过千言万语。

那么 REST 的属性到底是什么呢?

  1. 客户机-服务器:客户机发出请求,并从服务器接收响应。客户端不需要知道服务器是如何实现的。它也不需要知道服务器是否发送回缓存的响应,或者它的请求在得到响应之前是否经过了一百层。
  2. 无状态:每个请求都是独立的,并且包含了处理它所需的所有信息。服务器不跟踪任何上下文。
  3. 统一接口:API 接口应该是一致的,记录消息格式、端点和消息头。

用 Python 设计 REST API 非常简单,但是超出了本文的范围。如果你想看看它是如何做到的,请参考 Python 的 Flask 文档。

gRPC 呢?

gRPC 是 Google 开发的一种消息传递技术。但 gRPC 中的“g”并不代表“Google”;实际上是随机的。比如 gRPC 1.12中的“g”代表“光荣”。

gRPC 与编程语言无关,这似乎是微服务通信领域的新趋势。与 REST 相比,gRPC 以较低的灵活性为代价提供了更高的性能。

所以,gRPC 没有提供一套关于如何创建 web APIs 的指南;它执行规则。这一次,如果你在信的开头没有恰当的称呼,邮差会非常生气的!

这是它相对于 REST 的主要优势:在大多数情况下,gRPC 更快、更健壮,因为它定义了每个请求和响应应该遵守的一组特定的规则。

gRPC 和 Python

现在让我们用 Python 构建一个 gRPC 服务器。我们应该采取的第一步是将我们的消息结构定义为一个protobuf文件。在这个例子中,我们假设我们是一家书店,我们想获得目录中的书籍并添加新书。

这个文件(即book.proto)定义了一个新的消息BookMessage,该消息必须包含 ISBN 代码、标题、作者、描述、价格和条件,并且按照这个顺序。

然后,它定义了一个新的服务(即BookService),可以创建一本新书或者获取所有现有的书。

接下来,用 Python 实现 gRPC 涉及两个库:

  • grpcio运行客户端和服务器端代码
  • grpcio-tools生成定义代码

您可以使用以下命令安装这两个程序:

pip install grpcio grpcio-tools

安装完这些库之后,使用grpcio-tools生成构建 gRPC 服务器和客户机所需的代码。运行以下命令:

python -m grpc_tools.protoc -I./ *--python_out=./ --grpc_python_out=./ book.proto*

该命令应该生成两个文件:

  • book_pb2.py
  • book_pb2_grpc.py

直接编辑那些文件并不是一个好主意。如果您需要更改什么,请更改.proto文件并重新运行该命令来重新生成两个 Python 文件。

我们现在可以使用这些文件来创建 gRPC 服务:

最后,让我们创建一个客户端来测试我们的服务:

客户端创建一个新的BookMessage对象并提交给服务器。由此,我们假设服务器将这本新书存储在商店的目录中。

结论

gRPC 似乎是镇上新的、酷的孩子,但它不是来取代 REST 的。一般来说,REST 有更广泛的应用。另一方面,gRPC 更加结构化,开箱即用通常速度更快。

假设你需要速度或者结构,用 gRPC。因此,gRPC 的一个很好的用例是微服务的内部通信。另一方面,最好向外界提供一个 REST 端点,因为每个人都在使用 REST。

关于作者

我叫 Dimitris Poulopoulos ,是一名为 Arrikto 工作的机器学习工程师。我曾为欧洲委员会、欧盟统计局、国际货币基金组织、欧洲央行、经合组织和宜家等主要客户设计和实施过人工智能和软件解决方案。

如果你有兴趣阅读更多关于机器学习、深度学习、数据科学和数据操作的帖子,请关注我的 Medium 、 LinkedIn 或 Twitter 上的 @james2pl 。

所表达的观点仅代表我个人,并不代表我的雇主的观点或意见。

重建链式法则以自动微分

原文:https://towardsdatascience.com/rebuild-the-chain-rule-to-automatic-differentiation-d77afaa361c4?source=collection_archive---------23-----------------------

从链式法则到自动微分有一个环节。让我们跨越从数学到计算机科学的桥梁。

作者图片

神经网络很酷。不同的机器学习框架甚至更酷。您可能知道,现代神经网络是一个包含大量变量的大型公式。给定一个问题,这些框架会帮你找到一组合适的参数值,这个过程叫做“训练”。由于自动微分或自动微分,这个训练过程可以非常有效地完成。与其说是“幕后”,我倒不如说它影响了整个培训过程幕后。这是整个训练过程的基础。

Auto Diff 通常是通过首先理解所需的数学知识,然后从一个空白源文件实现它来实现的。但是也有可能从链规则的定义中发展出 Auto Diff。人们应该对基础数学仍有一些了解,但看着链式法则演变成自动微分会给人一种不同的满足感。

自动差异

作者图片

自动差异给了我们什么?它可以自动告诉我们几乎任何公式中的每个参数如何单独影响最终结果。“但是这有什么用呢?”,你可能会想。想象一下,你有一个幸福公式,输入是冰淇淋、煎蛋卷、书籍和睡眠的数字,输出是幸福程度的单一值。对于任何一组输入值,auto diff 可以告诉你每个输入对幸福输出的影响程度和影响方向。然后,你可以用这些新知识来改变输入,以接近你想要的幸福水平。

当然,一个更重要的应用是神经网络。输入可以由两部分组成。首先,一个固定的部分,像图像的像素值。然后我们需要第二部分,这是我们的公式与图像数据相结合的一组变量/参数。输出也可以分成两部分。第一个值对于狗可以是 0,对于猫可以是 1。最后一个值是第一个狗/猫值和输入图像的真实动物类型之间的误差。Auto diff 可以发现每个参数如何影响误差值。然后我们可以调整参数,使误差在下一次变得更小。

这个计划

文章的其余部分是关于写一个基本版本的自动微分。它以使用自动差分求解方程结束。我们将:

  1. 将链规则的定义转换为代码
  2. 将链式规则的简单实现转换为自动差异
  3. 使用自动差异解决问题

实施链式规则

这是两个自变量的链式法则, ab :

法典版本中最重要的部分:

显示了实现上述链规则的本质

备注:

  • 第 8 & 9 行:值被封装在节点对象中。这使得它更容易使用。
  • 第 11–13 行:定义显示了三个不同的函数***【g()【h()【f()。这里,我们使用同一个函数,一个mul***tiplication 函数。
  • 第 16 行: pdo_x_wrt_a 表示“partialderivativeofxtwwithrSPECTtoa,对应 ∂x/∂a 其值通过求导 x = a * b 得到。由于是*相对于,所以只把''作为一个变量。然后我们可以把方程解释为 x = a 常数 。这就给了我们x′=常数 ,在我们这里就是 b 。****
  • 第 15–28 行:我们将把这段代码移到系统的其他部分,并用一个单独的调用替换它。

转换为自动微分

目标是将主计算下面的所有代码移动到系统的其他部分。最终,我们希望避免任何衍生代码,同时,能够调用一个方法来触发几乎任何计算的梯度计算。

我们将 pdo_z_wrt_apdo_z_wrt_b 移入一个新的 渐变 属性中各自的节点 ab 。gradient 属性将始终包含最后一个值/节点的偏导数,在我们的示例中为z相对于渐变值所属的节点。此外,我们还拆分了行:

我们可以把所有的偏导数值移到它们所属的节点上。它们存储在地图/字典中。偏导数值所涉及的变量成为每个值的关键:

接下来,我们找到其余节点的梯度,' x ',' y ',以及' z '。x 的梯度是指最后一个值相对于' x '的偏导数。在我们的例子中是指' z '关于' x '的偏导数,我们已经得到为z . pdwrt . get(x)。这同样适用于 y 的梯度。z 的梯度必然意味着本身相对于本身*的偏导数。不确定这是否会影响到数学家,但我会推导出 z = z ,这将给我们 1 😗

然后我们可以重写这些行,使其具有相同的形式。这是通过用 x 和 y 的梯度代替对一些偏导数的直接访问来完成的。每个部分都被重新排列,所以我们每次从单个节点读取 :

此后,该部分可以由环代替:

…我们可以将其转换为节点类中的实例方法:

对 x 和 y 上的**pushGradientToDependencies()**的调用可以移动到那个完全相同的方法中,从而让一个节点递归地调用其依赖关系上的相同方法:

可惜的是,推梯度法有一个 bug。如果我们的主计算多次引用同一个节点,就会破坏我们的计算。那是因为渐变的累积(第 17 行,dependency . gradient+=)是使用一个节点的总渐变计算的(第 17 行,读取实例字段 渐变 )。当我们第二次访问同一个节点时,我们将合并第一次已经推送的梯度量。修复方法只是累计一个根据当前推送的数量计算的值:

此外,我们通过将梯度的累积移动到方法的顶部,对代码进行了一些清理。这将累积的责任转移到节点本身,外部调用方不再需要在调用此方法之前添加渐变。

最后一步是将乘法运算的偏导数值移入特定函数:

最终实现的核心:

为了简洁起见,没有列出诸如加法、平方、减法和求反等其他数学运算的实现。详见代码报告。

解决年龄问题

现在,我们可以通过梯度下降使用 auto diff 实现来求解方程。

问题是:

亚当比贝儿大 24 岁,但再过 6 年,
亚当将比贝儿大 3 倍。
问:亚当多大了?

这可以通过首先计算亚当和贝尔年龄的任何值的总误差来解决。然后,我们可以让我们的 auto diff 计算出梯度,并使用它来逐渐修改值,因此误差最终几乎为零:

有了这个误差函数,我们就可以写所谓的‘训练循环’了。亚当和贝尔的年龄首先被初始化为随机值。然后根据我们的自动区分代码的梯度对它们进行修改。我们使用梯度下降来改变这些值:

产生的误差接近于零,而 adamValuebelleValue 分别接近于 30 和 6:

控制台输出中的选定行

限制

根据整个计算图的整体连通性,这种简单而不刺激的实现可以多次处理计算图的一部分。结果还是会是正确的,但是效率低。

它也不能处理在相同的操作中使用相同的变量。这意味着像 mul(a,a) 这样的代码将无法工作。这是由于使用地图/字典来存储偏导数值。这可以通过使用另一种类型的数据结构来弥补。

尽管如此,梯度计算仍然有效,我们可以使用它来解决问题,而无需手动编码函数的导数。

超越自动差异

自动微分算出的梯度当然有用。令人着迷的是,我们可以简单地通过加/减梯度找到几乎任何函数的好解。这几乎是不可思议的,让我想起仰望天空的感觉,想到每一颗星星都是一个类似太阳的物体。

它不止于此。本文只描述了简单的“梯度下降”。你可以用更复杂的方式使用渐变。这将允许您更快地找到解决方案,甚至找到用我们的标准梯度下降法很难达到的解决方案。你可以通过研究“带动量的梯度下降”和“RMSProp”来找到这些被称为“优化器”的算法。

最后的想法

我觉得有趣的是,这样一个美妙的算法可以用几行代码来表达(我确信它甚至可以更短,但我是一个 Java 程序员,1k 行以下的都很短)。我不是指我们在这里实现了什么,而是这个过程本身,它在机器学习和许多其他领域都非常重要,可以由一个人非常快地编程。我知道,这个版本只支持标量值不支持张量,但还是自动微分*。我认为这仍然很酷。*

最后一个有趣的因素是,我们或多或少可以用两个独立变量来重构链式规则,以实现 auto diff。这让我笑了,以一种超级书呆子的方式。

代码库

你可以在这里找到代码:【https://github.com/codecicles/autodiff-simple】T3

你可以在这里找到作者一步一步的详细视频:

回顾 Python 基础知识,开始学习数据科学

原文:https://towardsdatascience.com/recap-on-the-python-basics-to-get-started-for-data-science-ae2d3874a08f?source=collection_archive---------17-----------------------

66 天的数据

复习学过的东西,学习新的东西。

背景:基思·米斯纳在 Unsplash 上拍摄的照片

经过长时间的学习,我们会对这些学科有更广泛的理解。复习我们过去学过的知识可能会给我们提供关于某一特定主题的新见解。

肯·吉的“第 66 天的数据是什么”视频激励了我。该视频解释了由他发起的一项挑战,包括两部分:

  1. 连续 66 天每天学习数据科学,哪怕只有 5 分钟。
  2. 使用#66DaysOfData 在我们选择的社交媒体平台上分享我们的进展。

我决定在 2021 年 7 月 24 日开始我的旅程,并选择 Medium 作为我的分享平台。

在我 66 天数据之旅的第一天🎉,我决定写下我在阅读朋友推荐的一本书时得到的想法,“用 Python 掌握机器学习”。这本书解释了如何端到端地进行机器学习项目。这本书从基本的 Python 语法和一些用于机器学习的常用库开始,如 PandasNumpy 、 *Matplotlib、*和 ScikitLearn

这让我想起了我进机器学习第一个班的时候。我在上课前自学了 Python 基础,我以为我准备好了,但我错了。机器学习的编程实验室从一些基本的 Python 语法开始,直接跳到上面提到的公共库。我在学期开始时不知所措🤢。

幸运的是,事情没有那么困难,同学们都支持🥰.

好了,回忆够了,我们开始吧!

本文分享一些我自学 Python 基础时没有学到的东西,但这对我的数据科学学习之旅和我的工作非常有用(也很有趣)。

可变的

大多数时候,当我们开始学习一门编程语言时,我们是从变量赋值开始的。Python 中的变量赋值和其他编程语言中的一样。我们可以在 Python 中把字符串数字(包括整数和浮点)、以及布尔赋值为变量。此外,我们可以在 Python 中将一个空值或者所谓的 None 赋值为变量。

string_variable = "Hello"
numerical_variable = 5 
boolean_variable = True
no_value_variable = None

背景:图片由里尔在 Unsplash 上拍摄

a.字符串变量有趣的事实

我们可以编写下面的脚本,而不是编写一个 for 循环来打印一个短语 100 次。

print(“phrase\n” * 100)

“\n”表示新的一行,因此,脚本每次都会在新的一行中打印单词“短语”。

b.f 弦

用字符串打印变量的方法很少,但是 f-string 是最有效的方法。

name = "Janelle"
years = 10
print(f"My name is {name}, I have been working in this company for {years} years")
# output: My name is Janelle, I have been working in this company for 10 years

是的,通过在括号后添加一个**f**并用一个花括号将变量名称括起来,我们可以很容易地在一长串字符串中打印多个变量。与使用+符号连接字符串和变量,或者使用字符串格式化操作 相比,代码将更容易阅读。

c.多重赋值

通常我们一次给一个变量赋值,但有时多次赋值也很方便。

a,b = 3,5
print(f"a: {a}; b: {b}")
# output:a: 3; b: 5

比如刷变量也是一种多赋值的类型。

d.滑动两个变量

a,b = b,a
print(f"a: {a}; b: {b}")
# output: a: 5; b: 3

在 Python 中,我们可以直接取两个变量的值,而不是声明一个新变量作为其中一个变量的值的临时存储。

数据结构

Python 的常见数据结构是列表、元组和字典。

a_list = ["Tom", 26, "Anywhere"]
a_tuple = (25, 7)
a_dictionary = {"name": "Tom", "age": 26, "home": "Anywhere"}

在数据科学中,经常使用的数据结构包括 DataFrame 和 NumPy 数组。一个数据科学项目通常使用几个库来完成不同的任务。例如,数据预处理和数据可视化,不同的库并不总是使用相同的数据结构进行输入。为了有效地使用不同的库,我们还应该知道如何将一种数据结构转换成另一种。

背景:图片由 Lea L 在 Unsplash 上拍摄

数字阵列

NumPy 是一个 Python 库,用于处理数组。NumPy 数组类似于链表,除了它存储在内存中一个连续的位置**。因此,Numpy 数组的处理速度比列表快[2]。**

import numpy

lists = [1, 2, 3, 4, 5]
lists = [x + 5 for x in lists]
print(lists)
# output: [6, 7, 8, 9, 10]arrays = numpy.array([1, 2, 3, 4, 5])
arrays += 5
print(arrays)
# output: [ 6  7  8  9 10]

NumPy 数组和列表都可以用作数学运算的输入。

熊猫数据框

DataFrame 是一个多维数组,其中的列和行可以标记为[1]。换句话说,DataFrame 类似于 Excel 表格。

Excel 表格。图片来自作者。

将一种数据结构转换成另一种数据结构

更好地理解数据帧,以及如何从列表和数组创建数据帧。

下图解释了上面脚本的输出。

图片作者。

流控制

流控制指的是 While 循环、For 循环和 If-Then-Else。

time_now = 0
while time_now <= 8:
    print("Continue to work")
    time_now += 1For x in range(n):
    print(x)age = 23
if age < 21:
    print("You cannot drive as you under the legal age to own a license.")
elif age >= 21:
    print("You can drive.")

**While 循环:**继续执行动作,直到条件不成立。

**循环:**重复任务 n 次

If-Then-Else: 如果条件满足,则执行动作

背景:图片由 Lea L 在 Unsplash 上拍摄

def adult(age):
    return age >= 21

age = 23
if adult(age):
    print("You can drive.")
else:
    print("You cannot drive as you under legal age to own a license.")

这样,我们可能需要编写更多的代码行,但这使得代码更具可读性。对我来说,在为大型项目编码时,我觉得这是一个很好的实践(在这种情况下,我往往会忘记数字代表什么)。

功能

在 Python 基础知识中,我们学习了如何声明一个新函数以及如何使用内置函数,比如 print()。随着学习的进行,可能会出现新的术语,如递归和 method。

背景:图片由 Lea L 在 Unsplash 上拍摄

a.递归

引用函数的递归函数在函数本身内部被调用,在一定条件下。如果没有设置适当的条件,可能会意外地创建一个无限循环。

# Recursive function
def recursive_factorial(n):
    if n == 1:
        return n
    else:
        return n * recursive_factorial(n - 1)

# user input
num = 6

# check if the input is valid or not
if num < 0:
    print("Invalid input ! Please enter a positive number.")
elif num == 0:
    print("Factorial of number 0 is 1")
else:
    print("Factorial of number", num, "=", recursive_factorial(num))# output: Factorial of number 6 = 720

Python 中递归的完整解释。

b.方法

方法与函数相同,它执行一项任务。当一个函数被附加到一个类上时,它就被称为方法。

可以将类定义为创建新对象的蓝图,这些对象具有相同的变量(如果附加到类,则称为特性),并且可以执行某些方法(当函数附加到类或在类中定义时,该函数称为方法)。

你注意到了吗,我总是在 Class 的第一个字母上用大写字母,这是因为我们是这样命名一个类的。类名总是以大写字母开头[3]。

Python 中的类的一个典型例子是学生标记集合。在下面的例子中,在输入被收集后,学生的名字和标记将被打印在屏幕上。

输出如下图所示。

图片作者。

通过创建一个Student Class,我们可以省去使用print()函数逐个打印学生姓名和分数的工作。相反,我们可以使用在Student Class中声明的方法。

在下图的下拉列表中,有

1.f' *(代表特性)*在名称和标记之前—在学生类中创建的变量

2.m' *(代表方法)*在 print_result()之前—在学生类中定义的函数

如果有效地使用它,这个类可以使我们的工作更容易。

这是 Python 语法的基础。

要开始数据科学之路,我们需要更进一步。就像 web 开发人员至少应该了解 Flask 或 Django 库一样,数据科学家应该了解:

  1. 熊猫 —加载和处理结构数据
  2. Numpy —数字数据的更快计算
  3. Matplotlib —创建静态、动画和交互式可视化
  4. ScikitLearn —机器学习,数据预处理和评估

以上是我们开始数据科学之旅需要了解的基础知识。成为一名数据科学家需要学习的东西还很多。请记住,科学家可以永不停止学习。

有多个库可用于执行一项任务。总是有更新更好的库来完成任务。作为科学家,我们应该始终跟踪技术的更新,并学会最有效和最准确地解决问题。

使用 Python 总会有一些乐趣!

学习很有趣!这是我周末用 Python 做的一个有趣的项目,用 Python 库、colorgram.pyturtle重现了达明安·赫斯特的点画。一个简单而有趣的项目。

鸣谢:这个项目的灵感来自于 Angela Yu 博士的 100 天代码课程。

用 Python 海龟库创建的点画。作者 GIF。

剧本如下。

如果你还没试过,也自己试试吧😄

边注

Python 中的文本处理 —在本文的最后,我分享了如何针对不同的目的,将输出的当前数据结构更改为所需的数据结构。

保持联系

订阅 YouTube

参考

[1]Jason Brownlee 的《掌握 Python 的机器学习》

[2]W3School 对 Numpy 的介绍

[3] 100 天的代码课程,由 Angela Yu 博士主讲

*祝贺并感谢你阅读到最后。希望你喜欢这篇文章。*😊

照片由安在 Unsplash 上拍摄

可解释的神经网络:最新进展,第 1 部分

原文:https://towardsdatascience.com/recent-advancements-in-explainable-neural-networks-2cd06b5d2016?source=collection_archive---------25-----------------------

回顾十年(2010–2020),四集系列

神经网络的兴起

神经网络已经超越了许多其他机器学习模型,如 SVM,adaboost,梯度提升树等。在各种任务中,如图像识别、视觉问答、句子完成等。,在许多机器学习问题上确立了自己的新地位。多年来的多项研究已经证明了神经网络在学习数据中隐含模式的有效性。但是,对于神经网络究竟学习什么以及它如何做出决策,很大程度上是未知的,有时甚至是神秘的。

交代

图像对人类来说很容易理解。例如,人类可以容易地指出图像中感兴趣的对象,区分背景和前景,并列出图像中感兴趣的不同对象。

另一方面,机器学习模型只将图像视为像素值的矩形阵列,没有形状和色调的内在表示。尽管存在这种限制,但当神经网络在大量标记图像上训练时,是什么使它们如此擅长识别图像?

这个博客是关于什么的?

在 2010 年至 2020 年的十年中,随着寻找性能更好、更容易训练的深度神经网络的发展,越来越多的研究人员也对深入了解神经网络学习什么以及如何做出决策感兴趣。

这篇博客聚焦于后面的工作,突出了在理解和解释神经网络所做的决定方面的重要进展,特别是关于图像理解。我们按照大致的时间顺序描述了这个领域的发展,展示了这些年来思想的流动。

我们将我们的演讲分为四个博客系列:

  • 第一部分讲述了图像像素的可视化梯度对于解释 CNN 的 pre-softmax 类得分的有效性。
  • 第 2 部分讲述了一些更先进的/改进的基于梯度的方法,如解卷积导向反向传播用于解释 CNN。
  • 第 3 部分讲述了基于梯度的方法的一些缺点,并讨论了替代的公理化方法,如逐层相关性传播泰勒分解深度提升。
  • 第 4 部分讲述了一些最近的发展,如集成渐变(上接第 3 部分)以及 CNN 架构中最近的创新,如类激活地图,开发这些地图是为了使特征地图更易理解。

基于梯度的方法

给定一个类的模型评分函数,哪个输入对评分影响最大?这个问题的一个自然答案是分数相对于每个输入的梯度。梯度越高,分数对相应输入像素的变化越敏感。

可视化渐变(2014)

一般来说,我们是否可以使用与图像的单个像素相关的类激活分数(pre-softmax)的梯度来了解哪些像素在决策中起着最重要的作用?

这个问题最早是由卡伦·西蒙扬、安德里亚·韦达尔迪和安德鲁·齐塞曼在他们的工作“ 卷积网络深处:可视化图像分类模型和显著图,(ICLR 2014) ”中研究的。从那以后,大多数关于可解释神经网络的工作都采用了某种形式的梯度或定义类似梯度的候选项,这些候选项可以向下反向传播到输入端,以便理解网络的决策。

作者在他们的工作中提出了两个重要的问题:

1。显著图:给定一幅图像,图像中的哪些像素对帮助网络做出决定最重要?

对于第一个问题,作者提出,可视化关于图像像素的类激活分数(pre-softmax)的梯度:

显著图,来源:https://arxiv.org/pdf/1312.6034.pdf

猴子图像的显著图,来源:https://arxiv.org/pdf/1312.6034.pdf

2。类别模型:根据神经网络,典型的“狗”或“猫”或任何此类目标图像类别看起来像什么?对于第二个问题,作者建议,寻找最佳图像,限制其亮度,这将最大化类得分:

班级模型,来源:【https://arxiv.org/pdf/1312.6034.pdf

班鹅的班模,来源:【https://arxiv.org/pdf/1312.6034.pdf

SmoothGRAD (2017)

回到 Simonyan 等人关于可视化梯度的想法,人们经常观察到梯度可视化经常是有噪声的。Daniel Smilkov,Nikhil Thorat,Been Kim,Fernanda Viegas 和 Martin Wattenberg 在他们的工作“ SmoothGrad:通过添加噪声去除噪声(ICML 2017) ”中建议了一些简单的技巧来更好地实现梯度可视化。以下是他们的一些建议:

  • 向输入图像添加一个小的高斯噪声,并从该分布中采样多个图像。计算梯度后,平均梯度。这清除了梯度可视化中的噪声,仅突出显示了在所有采样图像中已经被激活的重要像素。
  • 图像识别任务在颜色和光照变化下是不变的。例如,考虑白色背景中的黑色球与黑色背景中的白色球。在这种情况下,可视化梯度的绝对值通常就足够了。一个明显的例外是 MNIST 数字识别,所有的图像都是白色背景下的黑色。在这种情况下,梯度的符号变得更加重要。
  • 通过多项研究已经发现,梯度图通常具有一些具有非常高的梯度值的像素。盖住它们可以改善视觉效果。
  • 有时,将图像与渐变相乘可以提供更清晰的视觉效果。但这有时会产生不良影响,使较暗的像素看起来没有原始渐变显示的像素重要。
  • 所有上述即兴创作也可用于任何基于梯度的方法,如引导反向传播(在第 2 部分中描述)。

以下是作者提供的一些结果:

制导反投影 Vs SmoothGRAD,来源:https://arxiv.org/pdf/1706.03825.pdf

使用 SmoothGRAD 进行引导后投影,来源:https://arxiv.org/pdf/1706.03825.pdf

作者指出,当图像在恒定背景下时,SmoothGRAD 优于引导反向传播,而当图像具有纹理背景时,引导反向传播表现更好。此外,平滑方法在与引导反向传播一起使用时增强了结果可视化。

接下来是什么?

继续基于梯度的方法,研究人员开发了更先进的方法,如去卷积引导反向传播。这些方法改进了“香草”梯度反向传播,以提供更清晰的可视化。我们将在下一部分讨论这些很酷的技术。

要阅读更多关于神经网络可解释性的激动人心的作品,你可以点击这里阅读下一部分: 链接到第二部分

图形卷积网络(GCN)的最新进展

原文:https://towardsdatascience.com/recent-advances-in-graph-convolutional-network-gcn-9166b27969e5?source=collection_archive---------29-----------------------

用通俗易懂的英语介绍 GCN 最新发展的便车指南

图卷积网络(GCN)因其在解决深度互联的现实世界问题中的多功能性而越来越受欢迎。如果您需要快速复习 GNN/GCN,请在继续之前点击此处。在本帖中,我们将用更简单的语言来强调 GCN 建筑的一些进步…

阿丽娜·格鲁布尼亚在 Unsplash 上的照片

目录:

  1. GCN 积木
  2. 萨格科夫
  3. 金康夫
  4. 图形注意网络

GCN 积木

GCN 将更传统的卷积神经网络(CNN)的卷积原理结合到图形数据结构中。让我们更深入地探讨这个问题。

卷积和消息传递

简而言之,图中的卷积聚合来自相邻节点的信息,应用特定的聚合函数,并输出某些内容(例如,新特征嵌入、输出)。下图可以清楚地说明这一点。

GCN 的卷积法

上图中感兴趣的节点(即。绿色节点)正在从它的直接邻居(即蓝色节点),应用平均值作为其集合函数,并输出新的嵌入。这个操作被称为消息传递(MP) ,由所述图中的每个节点执行。

聚合函数

聚合函数是许多 GCN 变量的关键。上述 GCN 的初始公式使用归一化作为其聚合函数,定义如下。

GCN 的聚合函数

其中 D 为度矩阵(即。每个节点有多少个邻居), A 是邻接矩阵(即每个节点如何与其他节点连接),以及 X 特征矩阵(即如何描述节点)。

上面的规格化公式仅仅意味着一个平均运算:如果一个节点比其他节点有更多的邻居,按比例平均 X。

现在我们已经有了基本的构建模块,让我们讨论一些在 GCN 更受欢迎的进展。

萨格科夫

(论文: ICLR )

想象训练一个复杂的图形。如果再增加一个节点呢?你需要从头开始重新训练整个图形吗?

SAGEConv 从这个问题出发,通过归纳学习使 GCN 训练更加健壮。这是通过在集合函数中引入可学习的 W1 和 W2 权重矩阵,并对每个节点邻居的特征矩阵应用均值运算(类似于 GCN)来实现的。

SAGEConv 聚集函数

在训练之后,如果您需要将一个额外的节点包括到现有的图中,您不必从头开始重新学习:您可以应用学习到的权重矩阵来为新的节点生成新的嵌入。

金康夫

(论文: ICLR )

如果您想在聚合步骤中学习非线性表示法(不是传统的平均值、加法等),以近似模拟不同节点之间真实世界的复杂交互,该怎么办?

接下来是 GINConv,它将聚集的特征矩阵输入到可学习的人工神经网络(ANN)中。

GINConv 聚合函数

其中 h 是前馈神经网络。该公式简单地意味着,向邻接矩阵中的每个元素添加标量值ε,与聚集的特征矩阵相乘,并将结果馈送到可学习的 ANN 中。

该论文报告说,这种架构比 GCN 的早期变体更稳定,尤其是在区分更简单的图形时。

手枪

(论文: ICLR )

如果你能在 GCN 中加入注意力机制会怎么样?通过专注于重要的特征节点而将其余的降级?

这就是 GAT 通过修改下面的聚合函数所要达到的目的。

GAT 聚合函数

其中 alpha 为关注系数(在这篇帖子中可以了解更多关于关注的内容),X 为每个节点的特征矩阵。

结论

我们已经到了帖子的末尾。在这里,我们讨论了一些更重要的 GCN 变种。如果你注意到,这些变化大部分来自聚合函数中的新公式:线性(均值、求和)或非线性(ANN、注意系数)运算。所以下次你阅读任何 GCN 最先进的模型论文时,请留意这些变化!

做订阅我的邮件简讯:https://tinyurl.com/2npw2fnz在那里我定期用通俗易懂的语言和漂亮的可视化方式总结 AI 研究论文。

计算机视觉 x 变形金刚的最新发展和看法

原文:https://towardsdatascience.com/recent-developments-and-views-on-computer-vision-x-transformer-ed32a2c72654?source=collection_archive---------13-----------------------

《变形金刚》和 CNN 的区别,为什么《变形金刚》很重要,它的弱点是什么。

关于这篇文章

本文讨论了自视觉转换器问世以来,Transformer x 计算机视觉研究中的一些有趣的研究和见解。这篇文章的四个主题如下

  1. Transformer 和 CNN 感受野大小和行为的差异
  2. 自我关注对《变形金刚》来说是否必不可少?
  3. 视觉变压器的缺点和改进方向
  4. 变压器的快速扩张及其重要性

作为本文的总结,我的观点如下:

  • 自视觉变压器以来,变压器的应用范围迅速扩大。我个人认为这样做的原因是它可以应用于各种各样的数据,并且很容易在不同的模态之间建立关联。
  • 《变形金刚》和 CNN 的主要区别之一就是视野开阔。也许正因为如此,变形金刚比 CNN 更能适应纹理的变化,并生成不同模式的对抗性补丁。
  • 根据最近的一项研究,自我关注在《变形金刚》中可能并不重要。在我看来,编码器块中有两个部分很重要,一个处理全局信息,另一个在本地传播。
  • Vision Transformer 的弱点是需要大量内存和大量数据,但它正在快速改进。

变压器和视觉变压器

变压器

首先,我想解释一下 Vision Transformer 中使用的 Transformer 编码器,这是一个在论文“注意力是你所需要的全部”中提出的模型。这篇论文的标题对那些使用 LSTM 和 CNN 的人来说是挑衅性的。

它既不是 CNN,也不是 LSTM,而是一种叫做点积注意力的机制,建立在它之上的模型(Transformer)已经远远超过了现有的方法。

摘自[2],变压器模型概述

在转换器中使用的(点积)注意中有三个变量:查询、键和值。简而言之,系统计算查询和关键词的关注权重,并将每个关键词乘以与之相关联的值。

点积注意力

多头注意力,使用多个注意力头(在 MLP 的术语中,“隐藏层数”增加),定义如下。上图中的(单个头)注意力原样使用 q,k,但在多头注意力中,每个头都有自己的投影矩阵 W_iQ、W_iK 和 W_i^V,这些矩阵投影的特征用于创建注意力。

多头注意。左上图取自[2]

在这个点积注意力中使用的 Q、K 和 V,当它们都来自相同的数据时,称为自我注意。当点积注意力中使用的 Q、K 和 V 来自相同的数据时,这被称为自我注意力。在《变形金刚》中,编码器的部分和解码器下面的部分是自关注的。解码器的上半部分不是自关注的,因为查询来自编码器,K,V 来自解码器。下图显示了注意力权重的示例。在该图中,单词“making”被用作查询,并且每个关键词的注意力权重被计算和可视化。注意力集中的每个键都在学习不同的依赖性。“关键”这几个字用多种方式着色,代表每个人头的注意力权重。

引自[2],变压器的自我关注的重量。我加上字幕。

视觉转换器的工作原理

Vision Transformer 是将 Transformer 应用于图像分类任务的模型,于 2020 年 10 月提出。内容和原来的 Transformer 几乎一样,但是有一种别出心裁的方式,像自然语言处理一样处理图像。

视觉变压器架构,引自[1]。

视觉转换器将图像分成 N 个 16x16 大小的小块。由于补丁本身是三维数据,它们不能由处理语言处理的转换器直接处理。因此,在将它们展平后,他们做一个线性投影,将它们转换成二维数据。通过这样做,每个补丁可以被视为一个令牌(像一个单词),可以输入到转换器中。

此外,Vision Transformer 采用了预训练→微调的策略:Vision Transformer 在包含 3 亿幅图像的数据集 JFT-300M 上进行预训练,在 ImageNet 等下游任务上进行微调。Vision Transformer 是第一款在 ImageNet 上实现 SotA 性能的纯变压器型号。这是对 Transformer x 计算机视觉研究大量增加的开始。

为什么《视觉变形金刚》这么准?

关于 Transformer x 计算机视觉的研究由来已久,但一直无法在 ImageNet 上实现 SotA 性能。作者从模型的归纳偏差和数据数量方面解释了原因。归纳偏差是模型对数据的一种假设。比如 CNN 用 3x3 内核处理数据,这是基于数据信息是本地聚合的数据假设。在 RNNs 中,当前时间的数据与前一时间的数据高度相关,但前一时间的数据仅通过前一时间的数据相关。在 RNN,当前时间数据与先前时间数据高度相关,但是先前 x2 时间数据仅通过先前时间数据相关。这个过程也是基于数据与上一次高度相关的数据假设。另一方面,由于自我关注只与每个数据相关,可以说与 CNN 和 RNN 相比,其归纳偏差相对较低。

(左)CNN,它有一个强烈的归纳偏见,即信息是本地聚合的。(中间)RNN,它有很强的归纳偏差,因为它与前一次(右)自我注意强烈相关,而后者有相对较弱的归纳偏差,因为它只与所有特征相关。

作者将 ViT 的强解释为:“在数据很少的情况下,具有强归纳偏差的模型比那些具有弱归纳偏差的模型更强,因为它们对数据有假设。另一方面,当有大量数据时,假设就成了阻碍,所以在有大量数据的情况下,具有弱归纳偏差的模型就变得更强”。下图加强了这种解释:通过预训练数据集的大小来比较 Vision Transformer 和 CNN。在使用 JFT-300M 进行预训练的情况下,它优于 CNN(具有强归纳偏差的模型)。

数据量和准确性

变形金刚 x 计算机视觉的发展趋势

从现在开始,我将介绍 Transformer x 计算机视觉的研究动态以及最近研究中发现的视觉变压器系统的有趣行为。下面有四个主题。请注意,它们包含了我的许多个人观点。

  1. 扩大变压器的应用领域及其原因。
  2. Transformer 和 CNN 感受野范围和行为的差异
  3. 自我关注对《变形金刚》来说是否必不可少?
  4. 视觉转换器的缺点和改进方向

1.扩大变压器的应用领域及其原因

自从引入视觉转换器以来,将转换器应用于各种数据和任务的研究数量迅速增加,尤其是在计算机视觉领域。Vision Transformer 用于图像分类任务,但也有其他应用,如应用于语义分割、对象检测的 Swin Transformer 和应用于深度估计的 DPT[17]。在不同的数据格式方面,点转换器[18]适用于点云数据,感知者[19]适用于音频、视频、点云以及图像。

在视觉和语言领域,它将最初 transformer 使用的领域自然语言与计算机视觉任务相结合,也有许多应用程序,如 UniT,它可以同时执行多个任务,如计算机视觉。

如你所见,变压器已经广泛应用于各个领域。为什么变压器被这样使用?在我看来,有以下原因。

1–1。人们发现它不仅可以应用于自然语言处理,也可以应用于图像处理,因此得到了很大的发展。

1–2。不需要根据数据更换网络,方便。

1–3。不同数据之间很容易建立关联。

1–1。人们发现它不仅可以应用于自然语言处理,也可以应用于图像处理,因此得到了很大的发展。

最初,Transformer 用于自然语言处理领域,其有效性在自然语言处理和语音识别中得到证实,如 Transformer Transducer[20]在语音方面的成功。感谢 Vision Transformer,我们现在知道它在图像领域甚至比 CNN 更好。单单语音和自然语言处理的研究领域就很广阔。现在,随着同样具有广泛研究领域的计算机视觉的加入,以及在语音+图像、视觉&语言等复杂领域中使用它的能力,应用范围迅速扩大。

1-2。不需要根据数据更换网络,方便。

以前需要把网络分开,比如图像-> CNN,自然语言-> Transformer,现在所有的数据都可以通过 Transformer 处理,使用起来很方便,比如感知者[19]。

他们提出 Preciever,一个可以处理超过 10 万个特征的高维输入的 transformer 模型,可以处理视频+音频、图像、点云等多种数据格式。它通过从潜在空间中检索 Q 来减少计算量。不仅实现了对图像和点云的高性能,还获得了对视频+音频的 SotA 性能。这些图片引自[19]。

1-3。不同数据之间易于关联。

事实上,我们不需要有一个图像分支网络和一个自然语言分支网络,因为我们可以作为一个令牌处理一切,包括图像和语言,这可能是它越来越多地使用的另一个原因。在低层次上,我们可以将不同的模态与自我关注联系起来。典型的例子有维尔特[21],VATT[22]和 VL-T5[23]。

通过将不同模态的数据视为记号,很容易获得不同数据之间的相关性。(上)VL-T5[23],(下)VLiT[21]。

2.Transformer 和 CNN 感受野范围和行为的差异

《变形金刚》现在在计算机视觉上和 CNN 一样好甚至更好,但是两者有什么区别呢?

首先在视野上有区别:CNN 用的是 3x3 或者 7x7 大小的内核,所以每一层只能有一个对应的视野。并且视场随着其在各层中的传播而扩大,但是这种扩大随着深度而线性增加。

另一方面,转换器使用自我关注,这允许网络从初始层看到整个图像。由于每个面片被视为一个令牌,并且所有面片都是相互关联和计算的,因此可以从头开始学习全局特征。

将 CNN (kernel_size=3)和自我关注应用于一个大小为 32x32 的图像时,视场大小的差异。CNN 线性增加视野,而自我关注(视觉转换者)从一开始就拥有整个视野。

事实上,下图显示了视觉转换器感受野的扩展。大部分视图是线性扩展的,但也有一部分是从初始层获取全局信息。

受过训练的视觉转换者的视野。图片取自[1]。

此外,也许是由于其视野的大小,研究表明,与 CNN 相比,Transformer 可以根据形状做出物体决策。有一项研究[3]声称,CNN 的决策更多的是基于纹理而不是基于形状,这与常规理论不同,并建议 CNN 根据纹理而不是形状对对象进行分类。例如,在下图(c)中,人类倾向于根据猫的形状来判断猫,但基于 CNN 的模型根据纹理来判断印度大象。

CNN 是根据纹理而不是形状来判断的。图片取自[3]。

下图定量显示了这一点。该图显示了在保持形状的同时对纹理进行扰动的图像的分类精度。红点是人类,蓝点是基于 CNN 的模型。人类的判断对纹理扰动是鲁棒的,而 CNN 的准确性大大降低。换句话说,CNN 的标准高度依赖于纹理,而不是形状。

定量评价 CNN 是用纹理而不是形状来判断的。比较人类(红色)和基于 CNN 的模型(蓝色)对纹理有扰动但形状保持不变的图像的分类精度。图片取自[3]。

与基于 CNN 的模型相比,Transformer 模型(ViT)对纹理扰动相对更加鲁棒。与 CNN 模型相比,变压器模型(ViT)对纹理扰动相对稳健[4]。这可能和变形金刚视野广有关。

变形金刚比 CNN 更少依赖纹理。图片取自[4]。

还有其他有趣的特性可能归因于视场的大小。有一种技术称为对抗性例子[5],它使用故意创建的噪声来误判模型,并且已经发现基于 CNN 的模型和 Transformer [6]之间的噪声存在差异。在导致狗图像误判的噪声中,CNN (ResNet)噪声有很多高频成分,具有局部结构。另一方面,变压器噪声(ViT)具有相对较低的频率分量和较大的结构。这也可能是由于视野的大小。(很有意思的是,你可以在 16x16 的补丁中清晰地看到边界。

反面例子的例子。通过添加故意制造的噪音,该模型将熊猫图像误解为长臂猿。图片摘自[5]。

对抗性噪音的比较;ViT 的噪声具有较大的结构,具有相对较低的频率分量,而 ResNet 由高频分量组成。图片摘自[6]。

3.自我关注是变形金刚必备的吗?

ViT 在计算机视觉方面取得了巨大的成功,但也有很多研究在探索是否存在比自我关注更好的结构。例如,MLP 混合器[7]不使用自我关注,而是使用多层感知器(MLP),这是最基本的深度学习方法,结果与视觉变压器相当。与 Vision Transformer 一样,它需要在 JFT-300M 等庞大的数据集上进行预训练,但它可以在不使用自关注等复杂机制的情况下实现高性能。

基本结构是,混合器层是一个包含 MLP1 和 MLP2 的块,ml P1 处理跨面片的信息,MLP 2 处理每个面片的信息,这些块堆叠在一起,而不是 Transformer 编码器。这种方法类似于 ViT,它将图像分成小块,并处理小块的 2D 数据投影,用 MLP 代替 ViT 的变换器。

MLP 密炼机的结构。混合器层是一个包含 MLP1 和 MLP2 的块,ml P1 处理跨面片的信息,MLP 2 处理每个面片的信息,并且堆叠在彼此的顶部。图片摘自[7]。

MLP 混频器、ViT 和 Big Transfer 的比较(BiT,基于 CNN)。和 ViT 一样,MLP 混频器在低数据条件下不如 CNN,但在大数据时超过 CNN。图片摘自[7]。

受此影响,一篇论文[8]出现,声称自我关注不是一个必不可少的元素。该论文中提出的 gMLP 还提出了一种仅使用 MLPs 的结构,类似于 MLP 混频器,但是具有类似于挤压和激励的结构,该结构还具有用于处理面片方向上的信息的部分和用于处理空间方向上的信息的部分。即使不使用 JFT-300M 这样的庞大数据集,仅通过训练 ImageNet 就可以达到高于 EfficientNet 的精度。同样需要注意的是,它不仅在计算机视觉任务上和 BERT 一样准确,在自然语言处理上也是如此。

gMLP 的结构,分为通道方向处理信息的机制和空间方向处理信息的机制。图片摘自[8]。

gMLP 的结果。在图像识别领域,没有 JFT-300M,ViT 无法超越 CNN,但 gMLP 仅用 ImageNet 就取得了与 EfficientNet 相同的结果。此外,在自然语言处理领域,gMLP 已经产生了可与 BERT 相媲美的结果。图片摘自[8]。

还有一项研究提出了更为激进的主张,即自我关注可以被一种完全没有学习参数的机制所取代,如果它可以接受全部信息的话。在自然语言处理领域的研究 FNet [9]中,自我注意被傅立叶变换所取代。傅立叶变换没有学到任何东西,因为它只是改变了基础,并且它具有通过在傅立叶变换之前和之后添加特征而在物理上难以解释的结构,但是即使具有这样的结构,它也产生合理的结果。在这篇论文中,作者认为在令牌之间混合信息就足够了,除了傅立叶变换之外,他们还使用随机矩阵(具有固定参数)代替自关注对网络进行了实验。

FNet 的结构,其中变换器的自关注部分由傅立叶变换代替。图片取自[9]。

最近几天,有很多关于网络的研究,这些网络可以在不使用自我关注的情况下取得结果,如 MLP 混合器,gMLP 和 FNet。这三种机制处理全局信息(ViT 中的自我注意,MLP 混合器中的 MLP1,FNet 中的傅立叶变换)并在局部传播它(ViT 和 FNet 中的前馈,MLP 混合器中的 MLP2)。

虽然我的个人意见是混合的,但重要的是要有一个块结构,它不仅像 FNet 声称的那样在令牌(图像的情况下是补丁)之间混合信息,而且在考虑全局信息后在局部传播。

4.视觉转换器的缺点和改进方向

虽然《视觉变形金刚》在超越 CNN 方面成绩斐然,但它有两大弱点。然而,改进正在迅速进行,这些弱点正在被克服。

  • 由于感应偏差较弱,需要比 ImageNet(130 万个数据集)大的 JFT-300M (3 亿个数据集)才能获得良好的精度
  • 由于自我关注的性质,它需要的内存大小等于图像长度的四次方。

由于感应偏差较弱,需要比 ImageNet(130 万个数据集)大的 JFT-300M (3 亿个数据集)才能获得良好的精度。Vision Transformer 之所以能够超越 CNN,是因为它的感性偏置弱,但另一方面,如果数据量不够大,无法利用感性偏置弱的优势,准确性就会下降。具体来说,如果没有 3 亿个数据集,我们还无法超越基于 CNN 的模型。为了克服这一点,已经提出了各种改进方法。

使用 CNN

人们试图通过使用 CNN 来减少所需的数据量:DeiT [10]使用了一个知识提取框架,其中 CNN 被用作教师模型,而知识被输入到转换器模型。通过这样做,即使仅使用 ImageNet,结果也不仅超过 ViT,而且超过 EffcientNet。也有报道称,判决的倾向很大程度上通过蒸馏更接近 CNN。

(左)在 DeiT 中执行的知识提炼的概念图(右)使用 CNN 作为教师模型执行知识提炼的 ViT DeiT 的结果。图片取自[10]。

此外,由于 Vision Transformer 通过线性投影 16x16 大小的补丁以非常简单的方式处理本地信息,因此有研究使用 CNN,它对本地信息更具抵抗力。

(左)ViT 在 Transformer 中使用 patchy 嵌入表示,而 CeiT 使用带 CNN 卷积的抽象嵌入表示。(右)CNN 放置在变压器内部,使其对局部特征获取更加鲁棒。图片取自[11]、[12],我添加了注释。

由于自我关注的性质,它需要的内存大小是图像边长的四次方。因为自注意计算所有补丁之间的相关性,所以它需要与边长的四次方成比例的内存大小。这使得难以处理高分辨率图像。然而,有研究通过采用分层结构解决了这一问题,如 CNN,初始层处理高分辨率图像,随着越来越深,分辨率逐渐降低。例如,PVT [14]使用了类似 CNN 的高分辨率到低分辨率的层次结构,而 Swin [13]不仅使用了层次结构,还缩小了自我注意的视野,以减少斑块大小,获得更详细的信息。

(左)ViT,固定分辨率和低分辨率。(中间)PVT,它可以通过逐渐降低分辨率来处理高分辨率信息。(右)Swin Tranformer,它像 CNN 一样通过逐渐扩大视野来处理高分辨率信息,而不是使用整个注意力区域。图片取自[13]、[14]。

其他改进

除了与 CNN 相关的改进之外,还提出了 Transformer 独有的各种其他改进。例如,T2T 模块[15]提出将图像嵌入与周围的补片(记号)混合,允许重叠。Swin Transformer [13]使用局部注意,不像 ViT 使用全局注意,但是注意的组在不同的层是不同的。

此外,ViT 还有一个问题,就是随着越来越深入,会产生同样类型的注意力地图。在《走向更深的视觉转换器》中,作者重点阐述了保持头部之间注意力多样性的观点,并引入了一个参数来混合不同头部的注意力图,即使在加深后也成功地保持了注意力图的多样性。

(左)考虑到 ViT 中的图像标记化(嵌入)过于简单,他们提出了一个 T2T 模块,允许通过混合周围的标记进行复制和重新标记化。(右上)与使用全局注意力的 ViT 不同,Swin 在红色框架内获得局部注意力,但通过改变每层的注意力组来传播它。(右下)引入一个混合不同头部注意力的学习参数,提高了注意力的多样性。

Vision Transformer 过去存在“需要大量数据”和“需要大内存大小”等问题,但在过去几个月中已经提出了许多改进。在实际使用中,CNN 仍然是主流,但可能用不了多久,它也会在实际使用中取代 CNN。

结论

在这篇文章中,我讨论了自 Vision Transformer 问世以来,Transformer x 计算机视觉研究中一些有趣的研究和见解。我对这篇文章的总结如下。我非常兴奋地看到未来将在变压器上做什么研究。

  • 自视觉变压器以来,变压器的应用范围迅速扩大。我个人认为这样做的原因是它可以应用于各种各样的数据,并且很容易在不同的模态之间建立关联。
  • 《变形金刚》和 CNN 的主要区别之一就是视野开阔。也许正因为如此,变形金刚比 CNN 更能适应纹理的变化,并生成不同模式的对抗性补丁。
  • 根据最近的一项研究,自我关注在《变形金刚》中可能并不重要。在我看来,编码器块中有两个部分很重要,一个处理全局信息,另一个在本地传播。
  • Vision Transformer 的弱点是需要大量内存和大量数据,但它正在快速改进。

— — — — — — — — — — — — — — — — — — –

🌟我每周发布时事通讯!请订阅!🌟

https://www.getrevue.co/profile/akiratosei

— — — — — — — — — — — — — — — — — — –

关于我

制造工程师/机器学习工程师/数据科学家/物理学硕士/http://github.com/AkiraTOSEI/,

https://www.linkedin.com/in/%E4%BA%AE%E5%AE%8F-%E8%97%A4%E4%BA%95-999868122/?locale=en_US

推特,我贴一句纸评论。

— — — — — — — — — — — — — — — — — — –

参考

  1. Dosovitskiy、Lucas Beyer、Alexander、Dirk Weissenborn、翟晓华、Thomas Unterthiner、Mostafa Dehghani、Matthias Minderer、Georg Heigold、Sylvain Gelly、Jakob Uszkoreit、Neil Houlsby。一幅图像抵得上 16x16 的文字:大规模图像识别的变形金刚。第十四次评估报告(2019 年)
  2. Ashish Vaswani,Noam Shazeer,Niki Parmar,Jakob Uszkoreit,Llion Jones,Aidan N. Gomez,Lukasz Kaiser,Illia Polosukhin。你需要的只是关注。第十四次评估报告(2017 年)
  3. 罗伯特·盖尔霍斯、帕特里夏·鲁比什、克劳迪奥·米歇尔斯、马蒂亚斯·贝赫、费利克斯·a·威其曼、维兰德·布伦德尔。ImageNet 训练的 CNN 偏向于纹理;增加形状偏差可以提高精确度和鲁棒性。第十四次评估报告(2018 年)
  4. Shikhar Tuli,Ishita Dasgupta,Erin Grant,Thomas L. Griffiths。卷积神经网络还是变形金刚更像人类的视觉?第十四次评估报告(2021 年)
  5. 伊恩·j·古德菲勒,黄邦贤·史伦斯,克里斯蒂安·塞格迪。解释和利用对立的例子。第十四次评估报告(2021 年)
  6. Srinadh Bhojanapalli,Ayan Chakrabarti,Daniel,Daliang Li,Thomas Unterthiner,Andreas Veit。理解用于图像分类的变压器的鲁棒性。第十四次评估报告(2021 年)
  7. Ilya Tolstikhin、Neil Houlsby、Alexander、Lucas Beyer、Zhai Hua Zhai、Thomas Unterthiner、Jessica Yung、Andreas Steiner、Daniel Keysers、Jakob Uszkoreit、Mario Lucic、Alexey Dosovitskiy。MLP 混合器:全 MLP 的视觉建筑。第十四次评估报告(2021 年)
  8. 、戴子航、苏大伟、郭诉乐。关注 MLPs。第十四次评估报告(2021 年)
  9. 李中清-索普,约书亚·安斯利,伊利亚·埃克斯坦,圣地亚哥·翁塔农。FNet:用傅立叶变换混合记号。第十四次评估报告(2021 年)
  10. Hugo Touvron,Matthieu Cord,Matthijs Douze,Francisco Massa,Alexandre Sablayrolles,Hervé Jégou。训练数据有效的图像转换器&通过注意力进行提炼。第十四次评估报告(2020 年)
  11. 吴,,诺埃尔·柯黛拉,,刘,戴喜阳,陆源,。CvT:将卷积引入视觉变形器。第十四次评估报告(2021 年)
  12. 袁昆,,郭,,周傲军,,于。将卷积设计结合到视觉转换器中。第十四次评估报告(2021 年)
  13. 、林语桐、、韩虎、、魏、、林、郭柏宁。Swin Transformer:使用移位窗口的分层视觉转换器。第十四次评估报告(2021 年)
  14. 、谢恩泽、、邓、、宋、、、罗平、邵凌。金字塔视觉转换器:无卷积密集预测的通用主干。第十四次评估报告(2021 年)
  15. 李远,,陈,,,俞,,蒋子航,刘永泰,冯家实,严水成。Token-to-Token ViT:在 ImageNet 上从头开始训练视觉变形人。arXiv(2021)。
  16. 周大全、康、金晓杰、杨、连、姜子航、侯启斌、冯家实。DeepViT:走向更深层的视觉转换。第十四次评估报告(2021 年)
  17. 勒内·兰福特,阿列克谢·博奇科夫斯基,弗拉德连·科尔敦。用于密集预测的视觉转换器。第十四次评估报告(2021 年)
  18. 赵恒爽,,贾亚佳,菲利普托尔,弗拉德连科尔顿。点变压器。第十四次评估报告(2020 年)
  19. 安德鲁·耶格尔,菲利克斯·吉梅诺,安德鲁·布洛克,安德鲁·齐泽曼,奥里奥尔·维尼亚尔斯,若昂·卡雷拉。感知者:具有重复注意的一般感知。第十四次评估报告(2021 年)
  20. 张倩、卢汉、哈西姆·萨克、安舒曼·特里帕蒂、埃里克·麦克德莫特、斯蒂芬·库、尚卡尔·库马尔。变压器传感器:一个流语音识别模型与变压器编码器和 RNN-T 损失。第十四次评估报告(2021 年)
  21. 金元宰,孙宝庆,金一斗。无卷积或区域监控的视觉-语言转换器。第十四次评估报告(2021 年)
  22. 哈桑·阿克巴里、李良哲、袁瑞倩、庄伟红、张世福、尹翠、龚柏青。VATT:从原始视频、音频和文本进行多模式自我监督学习的变形金刚。第十四次评估报告(2021 年)
  23. 赵在民,解磊,郝坦,莫希特.班萨尔。通过文本生成统一视觉和语言任务。第十四次评估报告(2021 年)

最近发现的用例(第 1 部分)

原文:https://towardsdatascience.com/recently-discovered-use-cases-part-1-5afe7ca9b32?source=collection_archive---------41-----------------------

扩展我们的预测加工产品

丹尼尔·史密斯在 Unsplash 上的照片

在过去的几个月里,我们一直在悄悄地监测和发现我们的预测加工产品的新用例。由于简化的操作界面,它也方便地兼作“幕后”标记的数据收集平台,迄今为止,我们已经看到了数百个工具故障、轴承故障和其他机器故障场景。

在与客户的合作中,我们从电机数据的角度提出了一个大型的典型机器故障目录,今天我们很高兴与您分享。我们将回顾我们在多个客户场所看到的六个重复场景,以及它们从“图上的点”的角度和它们在车间的物理表现看起来是什么样子。这些措施如下:

  1. 端铣刀故障导致加工质量差和零件报废
  2. 滚珠轴承故障导致主轴外壳损坏
  3. 粗加工刀具故障,导致精加工刀具上的级联刀具故障
  4. 不正确的偏移,导致导套错位
  5. 工具故障,导致不完全切断
  6. 对工具寿命的估计不正确,导致工具过度使用或使用不足

在进入正题之前,让我们回顾一下预测加工产品的主题:

我们从机器上的嵌入式传感器中提取丰富的数据,以预测和预防可能证明代价高昂的不同场景。

  1. 丰富的数据:1 毫秒采样率->每台电机每天 1.7 亿次观察,主轴转速精度达到 001 RPM,功率/负载连续额定值为 0.01%
  2. 嵌入式传感器:不需要额外的传感器,因为数据必须在所有现代数控机床上可用。我们只提取机器用来运行内部控制反馈回路的数据。演示的所有场景都是在新冠肺炎疫情期间部署的,机器度量人员从未踏足过工厂车间
  3. 预测和预防代价高昂的情况:概述的情况会让我们的客户花费金钱,预防这些情况会为他们省钱。我们从节省下来的钱中抽取一部分来赚钱

立铣刀故障

回顾一下,在每一台 MachineMetrics 机器上,我们都有一个操作员平板电脑,它基本上只是一个三星 Galaxy 平板电脑或 iPad,指向我们 SaaS 产品中特定机器的网页。

使用我们操作员平板电脑的客户。

操作员平板电脑直接连接到我们的平台,因此每当机器停止时,就会有一个信号从机器发送到我们的边缘设备,然后被发送到云,在云上与其他指标一起汇总和显示。然后,平板电脑会反映这一新信息,并在机器网页上弹出一个窗口,提示操作员解释机器停机的原因。

当机器停止时,会出现一个弹出窗口,要求操作员添加原因代码。

随着时间的推移,这些注释越来越多,让我们可以更详细地了解机器何时因何种原因发生故障。

使用我们从机器电机收集的极其精细的数据,由 MachineMetrics 数据科学提出机器故障的原因和方式。在这种情况的一个实施例中,我们可以查看成千上万个制造的部件和几十个注释来识别模式,如在下面的例子中,其中黑线是注释的故障,点是每个制造的部件消耗的功率。

黑线是注释失败,而红点是异常负载。一些红点与黑线不在一条线上,这是由于操作人员有时会遗漏注释。

随着时间的推移,每种类型的失败都会出现不同的模式。上图中的黑线都代表了立铣刀故障的次数,此时一个特定的立铣刀断裂,无法完成完成零件所需的工作。在之后进入的刀具需要消耗额外的能量来进行切削,这导致了你在上图中看到的红色负载信号。这是一次又一次地重复,允许我们创建一个简单的阈值算法,每当我们看到第一个提升的负载信号时,就停止机器的运行。

在它的物理表现中,你可以看到一个好的零件,由一个好的端铣刀切割,在槽中有一个漂亮而闪亮的表面。一个坏零件,由一个坏的立铣刀切割,有一个粗糙的槽,因为立铣刀不能创造一个光滑的光洁度,随后的工具也不能正确地完成它的工作。

在我们的产品中,我们现在监控这台机器上的负载,以便在制造任何这些坏的槽零件之前发出进料保持。在过去的 4 个月里,我们在多台机器上发布了 60 个 feed holds,其中 3 个为误报,2 个为漏检(95%的准确率,96%的召回率),防止了 2,000 多个零件报废,节省了无数的机器重新配置工时。当然,这些都是小零件,由小工具制造,当我们提取非常敏感的电机数据时,尺寸不是很大的限制。从这些机器中出来的所有零件现在都是崭新的,没有了它们曾经带有的缺陷。

很整洁,是吧?这只是众多使用案例中的一个。你不会以为我们只是只会一招的小马吧?

滚珠轴承故障

与立铣刀故障类似,轴承故障会在材料和人力方面造成巨大损失。

与立铣刀故障不同,轴承故障在我们需要寻找的模式方面看起来完全不同。

与立铣刀故障类似,轴承故障使用相同类型的丰富电机数据和收集方法。

与立铣刀故障不同,轴承故障会出现很长时间(几天到几周),并且可以提前很长时间预防。

让我们首先通过故障前 2 小时的数据快照,研究轴承故障前机器上的负载。

这些线中的每一条都被加载到特定的工具上。不需要一个火箭科学家就能弄清楚这里发生了什么。在每个工具的轴承实际出现故障前约 15 分钟,负载急剧增加,这可能是因为机器的关键部件越来越脆弱,导致机器承受了额外的压力。

如果我们观察故障前 15 分钟内一个部件接一个部件的进展,另一个有趣的模式出现了,即负载似乎随着时间的推移而退化。您可以看到,对于每次切割,负载都呈指数增长,越来越超出规格。

将此与制造相同零件的另一台机器上的另一个轴承故障进行比较,模式非常相似。这是因为这是轴承故障的独特特征。虽然导致零件报废的立铣刀故障表现为后续刀具上的载荷突然增加,但是轴承故障表现为所有刀具上的载荷急剧增加。

二月(左)与十一月(右)轴承故障,加速约 20 倍

随着时间的推移,我们实际上看到这种退化在实际失败前几天就开始了。在两台机器的两个独立实例中,您可以看到,在断裂前的最后 15 分钟内,负载在最后一次陡峭爬升之前很久就有所提升。那是因为轴承不会凭空出现。在最后一次开斋节之前的几天里,可能会有松动或骨折没有得到检查,这是由误用或缺乏维护造成的。在这种情况下,故障轴承还导致主轴箱损坏,这又花费了几千美元的修理费。

11 月轴承故障,显示大约 2 天前负荷升高。

2 月轴承故障,显示大约 1 周前负荷升高。

我们看到的相似类型的失败的显著一致性是由于我们正在观察物理现象,令人惊讶的是,这些现象受物理定律支配。因此,故障遵循确定性模式,我们可以从足够高保真的数据中辨别出来。诀窍在于获取这些数据并对其进行清理,使其变得有用,这是我们知识产权的一大部分。

我们将这一创新归功于 MachineMetrics 团队的特殊技能组合——我们认为,让经验丰富的机器操作员与网络物理协议专家一起工作,辅之以来自 CERN 的物理学家和来自全球智库的数据科学家,并得到相信我们正在开创一个新的工业时代的投资者的支持,这并不常见。

级联工具故障

对于任何机械师来说,一整列工具断裂都是令人沮丧的经历。不幸的是,这在我们的行业中太常见了。如果我们可以看到第一个工具何时损坏,然后在其他工具取出之前立即停止机器,会怎么样?

事实证明,你可以。在第一个工具消失后,后续工具上的负载特征的改变几乎是轻而易举的事情。

在这种情况下,当检测到灾难性事件时,我们看到第一个工具在机器自行停止之前中断了许多零件循环。当自停发生时,其他工具也已被取走,使车间花费了价值数百美元的不必要的工具更换。第一个工具很可能是因为磨损和过期而损坏的,所以无论如何也不可能保存。但是没有理由为什么在它之后的其他工具,一些新替换的,也需要被淘汰。

你可以在下面看到一个动画,根据操作者所说,工具故障,当机器自己停止时,是在第 40 部分。事实上,信号在第 37 部分开始减弱,整整 3 个周期后,机器才自我识别出有问题。在这三个周期中,跟随坏工具的工具破产了。

除了零件号之外,让我们来分解一下工具使用期,工具由不同的线型指定。

让我们把问题放大一点。这有点难以辨别,但看起来 T101 是损坏的令人不快的工具,T117 是在紧接着的周期中经历更高负载的工具,然后是平坦线(死亡)。一旦工具被替换,负载特征大致回到它之前的样子(由于有新的工具,导致了一点滑动)。

在这种情况下的注释是“H1t1 198/200 粗破,取出座。”—意思是,“头 1、刀具 1(粗加工刀具)在工作过程中断裂,在 200 个要制造的零件中的 198 个处断裂,并取出了定位(后续)刀具”。

事实上,事情就是这样。

继续关注第 2 部分,我们将回顾刀具寿命估计、机床上不正确偏置的后果以及刀具故障的不完全切断。

用机器学习重新分类库存——艺术爱好者版

原文:https://towardsdatascience.com/reclassifying-inventory-with-machine-learning-art-lovers-edition-53818c287996?source=collection_archive---------25-----------------------

预测大都会艺术博物馆收藏的艺术品的受欢迎程度和重要性

艾莉森·考特尼在 Unsplash 上拍摄的照片

大都会艺术博物馆,也被称为 Met,是美国最大的艺术博物馆,永久收藏超过 200 万件作品。永久收藏从古典绘画到广泛的现代艺术收藏都有。我仍然记得我第一次走进博物馆时的激动心情。感觉我在短短几个小时内经历了几个世纪的人类历史。我是如此的敬畏,以至于作为我在 Metis 数据科学训练营的一个项目,我决定做一个关于预测大都会博物馆藏品中艺术品的受欢迎程度和重要性的项目。

博物馆保留了一份精选作品清单,这些精选作品是从大都会博物馆的永久收藏中挑选出来的,被认为是受欢迎的和重要的。这些还不到收藏中所有艺术品的 1%。我的项目的目标是创建一个分类器,它能够根据描述艺术品的特征来识别这些突出显示的项目。Met 可以使用这种类型的模型来识别他们的集合中他们可能认为重新分类为重要的对象。它还可以用于对新添加到集合中的内容进行初始分析。

数据和方法

对于这个问题,我使用了 Met BigQuery 公共数据集,其中有超过 200,000 件艺术品。它包含关于艺术品的信息,如标题、艺术家、部门、城市、国家、文化、分类等。

这是我用来从 Google Cloud BigQuery 数据仓库中提取艺术品数据的 SQL 查询,它利用 Google 基础设施的处理能力实现了超快速的 SQL 查询。

提取插图数据的 SQL 查询

为了找到与突出显示的艺术品最接近的项目,我需要创建一个分类器,以高精度找到突出显示的项目,然后挑选得分最高的非突出显示的项目。希望这些是应该被强调的艺术品,但可能不知何故“从裂缝中滑落”。

下图显示了生成需要重新考虑以突出显示的候选人列表所需的步骤。第一步是创建一个分类器来预测突出显示的项目。下一步是用分类器对所有观察结果进行评分,并根据分数进行排序。最后一步是挑选得分最高的非高亮项目。

生成要重新考虑以突出显示的候选艺术品列表所需的步骤

这是一个大海捞针式的问题,其中类严重不平衡,我只对列表顶端的精度感兴趣。Precision@k 是最适合这个问题的度量标准,其中我挑选了 k 个得分最高的项目,并在这个集合上测量分类器的精度。

特征工程

数据集包含描述艺术品尺寸的列。为了能够使用它,我必须解析它的内容。根据物体是二维还是三维,我计算并存储了面积或体积。

解析维度列(面积单位为厘米,体积单位为厘米)

许多分类特征,如国家、城市、时期、部门等,都有数百个(如果不是数千个的话)唯一值,频率计数按照 Zipf 定律随着等级快速减少。这是一个使用数理统计制定的经验法则,指的是这样一个事实,即对于物理和社会科学中研究的许多类型的数据,等级-频率分布是一种反比关系。

在左图中,您可以看到各部门的绝对频率,在右图中,是相同的数据,但 Y 轴是对数刻度。我将从这些分类变量中创建虚拟特征,因此,为了保持特征的合理数量,我只保留了每个类别中的前 X 个值,并将其余的标记为“其他”。保留值的数量因变量而异。

频率计数随着等级快速减少

机器学习

这个项目中我最喜欢的部分来了——使用机器学习来实现我的主要目标,即创建一个分类器来找到与突出显示的艺术品最接近的项目。为了这个项目,我尝试了不同的 ML 模型——随机森林、袋装决策树、增强决策树和神经网络(DNN)。

对于每个模型,我选择了一个不同的阈值,该阈值将产生正好 100 个阳性结果,允许我使用 Precision@k 来比较它们,其中 k 是 100。尽管 DNN 的性能比表现最好的随机森林稍低,但 DNN 的输出粒度更好,允许我正确地对对象进行排序。这是一个很好的例子,说明性能指标分数不是影响模型选择的唯一标准。

下面你可以在测试集上找到 DNN 模型的混淆矩阵和 ROC 曲线。

DNN 测试集混淆矩阵

DNN 检验集 ROC 曲线

我在整个数据集上重新训练了我的神经网络,对所有对象进行了评分,并挑选了前 10 个得分最高的非突出显示项目。然后我使用大都会艺术博物馆收藏 API 来显示结果。

从 Met Collection API 中检索和显示图像

请访问我的 Github 帐户来查看这个和我的其他项目的代码。也可以随时通过 LinkedIn 联系我。

培养皿上微生物的识别和计数

原文:https://towardsdatascience.com/recognition-and-counting-of-microorganisms-on-petri-dishes-53a499283dc8?source=collection_archive---------17-----------------------

引入深度学习方法用于分析微生物图像

雅罗斯瓦夫 西尔维娅

作者图片。

制药、化妆品或食品行业的生产过程受到严格的政策和法规的约束,制造商有义务进行持续的微生物监控。这意味着成千上万的样本,通常以标准培养皿(琼脂培养基上生长的微生物培养物)的形式,必须由经验丰富的微生物学家人工分析和计数。这是一个耗时且容易出错的过程,需要训练有素的专业人员。为了避免这些问题,应用于该任务的自动化方法将是非常受欢迎的。

在这篇文章中,我们将介绍由 NeuroSYS 研究团队开发的分析微生物图像的深度学习方法。训练机器学习模型的关键是获得大的、构造良好的数据集。因此,我们将利用在我们之前的帖子中介绍的琼脂数据集来训练一个模型,该模型根据细菌菌落的 RGB 图像对培养皿上生长的细菌菌落进行计数和分类。

你知道这条线索吗?—拥有大量数据的愚蠢算法胜过拥有少量数据的聪明算法。

微生物菌落的检测

好的,那么让我们从检测微生物开始。想象一下,我们有一个培养皿的图像(这种圆形玻璃通常用于保存生长培养基,以便在实验室繁殖微生物细胞)。在不同琼脂图像设置中的这种培养皿的示例性照片显示在图 1 的左栏中。其他 5 列显示了包含 5 种不同微生物类型的照片片段(我们称之为小块*)。现在很容易理解微生物检测是什么意思了。我们只需确定每个微生物的确切位置和大小,用蓝色矩形标记它(我们称之为边界框),如图 1 所示,并在图 2 中放大。*

图一。不同类型的微生物(第 2-6 列)在不同照明设置(每行)的培养皿(第 1 列)中生长。每个蓝色边界框都是由一个训练有素的探测器预测的。作者图片。

对于训练有素的专业人员来说,这似乎很容易,但请注意,微生物菌落边缘可能模糊不清,菌落本身非常小(甚至只有几个像素大),或者相机设置(例如聚焦或照明)非常不充分(例如,参见图 1 中第三行的照明条件)。此外,一些群体可能重叠,这使得一个群体结束而另一个群体开始的决定非常具有挑战性。这就是为什么要建立一个微生物菌落定位分类的自动化系统真的很难。

图二。由我们的探测器进行预测的放大补丁。请注意,菌落可能有非常不同的大小:一些菌落非常小,一些菌落很大,边缘模糊,一些菌落可能重叠——这使得检测非常具有挑战性。作者图片。

为此,我们开发了一种用于微生物检测的深度学习模型。深度学习是一系列人工智能模型,主要利用人工神经网络。这种现代方法在许多领域都非常成功,例如在计算机视觉或机器翻译领域。深度学习物体检测器(在我们的例子中是检测微生物菌落)是非常复杂的多级模型,有数百层,每层由数百个神经元组成。

人工智能试图解决对人类来说相对容易但极难编程的任务。

这里,我们采用基于区域的卷积神经网络(R-CNN)系列的两级检测器[2,3],已知这种检测器速度慢但非常精确(与单级检测器相比,例如著名的 YOLO [4])。关于其工作原理的简短解释,请参见图 3。关于各种物体检测算法的更详细的解释,请参见我们之前关于这个问题的博文。

图 3。R-CNN 检测器的两级结构。第一阶段(a)生成区域建议,这些区域建议只是原始图像的较小部分,我们认为它们可能包含我们正在搜索的对象。在第二阶段:(b)我们采用每个区域提议,并使用深度 CNN 创建表示该区域的特征向量,并对每个提议进行分类:它是否相关,如果是,它包含哪类对象。图改编自[2]。

训练探测器

展示完微生物检测结果后,让我们看看检测机是如何工作的。这包含神经网络监督训练过程,但不仅如此:数据预处理和后处理也出现在图 4 的训练方案中。为了以监督的方式训练深度学习模型,我们需要一个带标签的数据集。如前所述,我们在此使用琼脂数据集,该数据集由带有标记微生物菌落的培养皿的图像组成。

神经网络的典型特征是模型的结构严格对应于输入大小。当训练(和评估)网络时,我们受到可用存储器的限制,因此我们不能一次处理整个高分辨率图像,所以我们必须将它分成许多小块。这个过程并不简单,因为在切割成小块的过程中,我们必须确保给定的菌落完整地出现在至少一个小块上。

之后,我们准备训练检测器(图 4 中的上一行)。我们从 R-CNN 家族中挑选了 8 款不同的机型进行综合对比。训练完探测器后,我们在照片上(实际上是在补丁上)测试了探测器(图 4 中的下部管道)在训练期间看不到,以确保测试公平进行。请注意,为测试准备的补丁只是被均匀地切掉了——在这个阶段,我们不能包含关于边界框所在位置的信息。

图 4。监督训练和评估(测试)我们的微生物菌落检测器神经网络模型的流程图。作者图片。

检测和计数结果

我们在图 1 和图 2 中看到,我们的模型很好地检测到了菌落。但是如何定量描述检测的性能呢?我们可以计算一些标准数字(度量)来描述模型在测试集上的性能。其中最受欢迎的一种称为平均精度(AP)或多类检测情况下的平均精度(mAP)(详细定义见本文帖子)。在琼脂数据集的两个子集(较高和较低分辨率)上评估的两个选定的 R-CNN 模型(更快的 ResNet-50 和 Cascade HRNet)的 AP 和 mAP 结果如图 5 所示(左侧表格)。

一般来说,AP 值越高,检测越精确——预测的和真实的边界框彼此更好地匹配。然而,请注意,这里的情况有点复杂,因为我们有不同的微生物类型,这意味着除了找到菌落,detector 还需要对它们进行分类。

用不同的 fidelites 检测不同种类的微生物,这影响了图 5 所示的 mAP。例如,金黄色葡萄球菌细菌边缘锋利的小菌落比假单胞菌* 铜绿细菌边缘模糊的大菌落(AP 约 50%)更容易被检测和标记(AP 约 65%)。同样值得一提的是,与在著名的 COCO 数据集上使用相同架构完成的报告相比,我们的结果似乎非常出色:级联 R-CNN 为 45%,更快的 R-CNN 为 37%[5]。*

与检测培养皿上的每个菌落严格相关的最后一项任务是计数。在检测到所有的微生物菌落后,我们将它们相加,并将该数值与给定培养皿中菌落的真实数值进行比较。在琼脂试验子集上用相同的两个模型计数的结果显示在图 5 中(右边的图)。

x-轴上,我们有由训练有素的专业人员估计的不同培养皿的地面真实菌落数,而在 y 轴上,我们有由我们的模型预测的值——每对(真实值预测值)由这些图上的单个黑点表示。很明显,在理想预测的情况下,所有点都应该位于由黑线表示的 y = x 曲线上。幸运的是,绝大多数点都位于这条曲线附近——模型计算得相当好。两条额外的蓝色曲线标记+/- 10%的计数误差,我们可以看到只有少数点(特别是具有超过 50 个菌落的更高密度的培养皿)位于该区域之外。

平均计数误差通过平均绝对误差(MAE)来测量,例如在本博客的中定义的,以及所谓的对称平均绝对百分比误差(sMAPE),其基于百分比误差来测量准确度【6】。一般来说,sMAPE 不超过 5%,这是一个相当合理的结果。

图 5。微生物菌落检测的质量:左边是描述检测本身保真度的平均精度度量的结果,而右边是菌落计数的比较——预测与真实菌落数(理想预测位于 y=x 黑色曲线上)。作者图片。

结论

总之,在这篇文章中,我们介绍了关于在培养皿上识别 微生物的深度学习研究。所选的 R-CNN 模型在检测微生物菌落方面表现非常好。菌落具有相似的形状并且所有种类的微生物在训练数据中都有很好的表现,这一事实有助于检测,证明了琼脂数据集的实用性。此外,用基本的更快的 R-CNN 和更复杂的级联 R-CNN 获得的结果差别不大。****

如上所述,对于菌落数少于 50 的样品,检测器更加准确。然而,对于具有数百甚至数千个菌落的培养皿,它们仍然给出了非常好的估计,如图 6 所示,在高度密集的样品中正确地识别出单个菌落。在极端情况下,一个平板上检测到的最大菌落数等于 2782 个。值得注意的是,深度学习系统需要几秒钟,而在人工计数的情况下,可能需要一个小时。此外,在某些情况下,探测器能够识别人类难以看到和错过的群体。这些案例证实了建立自动微生物检测系统的好处,并且这可以通过使用现代深度学习技术成功实现。

参考

[1] P. Domingos,关于机器学习要知道的一些有用的事情,Commun。美国计算机学会,第 55 卷,第 78-87 页,2012 年。

[2] R. Girshick 等,精确对象检测和语义分割的丰富特征层次,IEEE 计算机视觉和模式识别会议论文集,2014。

[3] A. Mohan,使用 R-CNNs 的物体检测和分类,关于 RCNN 模型的非常详细的博客,2018。

[4] J. Redmon 等,你只看一次:统一的、实时的物体检测,IEEE 计算机视觉与模式识别会议论文集,2016。

[5] J. Wang 等,面向视觉识别的深度高分辨率表征学习,IEEE 模式分析与机器智能汇刊,2020。

[6] S. Majchrowska,J. Pawł owski,G. Guł,T. Bonus,A. Hanas,A. Loch,A. Pawlak,J. Roszkowiak,T. Golan,和 Z. Drulis-Kawa,AGAR a Microbial Colony Dataset for Deep Learning Detection,2021 年 7 月 7 日,预印本可在 arXivarXiv:2108.01234查阅。

由欧洲区域发展基金下的欧盟基金共同资助的项目,作为精明增长运营计划的一部分。作为国家研究与发展中心的一部分实施的项目:快速通道。

原载于 2021 年10 月 13 日https://neurosys.com。**

https://github.com/majsylw/microbial-counting-review

https://arxiv.org/abs/2108.01234

利用张量流识别猫和狗

原文:https://towardsdatascience.com/recognizing-cats-and-dogs-with-tensorflow-105eb56da35f?source=collection_archive---------12-----------------------

使用卷积深度神经网络来参加 Kaggle 的狗与猫比赛

来自像素的 Snapwire 图像

在学习深度神经网络的过程中,我喜欢用实际的例子和项目来帮助我记忆理论。寻找这些实际项目的一个极好的资源是 Kaggle 。Kaggle 是一个由数据科学家和机器学习实践者组成的在线社区。

Kaggle 允许您搜索和发布数据集,探索和建立模型。您可以在基于 web 的环境中执行这些功能。Kaggle 还提供带有实际问题的机器学习竞赛,并为获胜者提供奖品。

目前在用 TensorFlow 研究深度学习。我想学的科目之一是图像识别。这篇文章描述了我试图解决 2013 年的一场名为“狗与猫”的前 Kaggle 比赛为了实现这个解决方案,我使用了 Python 3.8 和 TensorFlow 2.3.0。

最初的“狗与猫”比赛的目标是编写一个算法来分类图像是包含狗还是猫。请注意,2013 年没有 TensorFlow 或 PyTorch 等其他框架来提供帮助。

虽然比赛结束了,但还是可以上传,让 Kaggle 给你的预测打分。

准备培训数据

照片由 Glomad Marketing 在 Unsplash

在创建和训练模型之前,我们必须准备好训练数据。这项比赛的训练数据涉及 25,000 张狗和猫的图像。每个图像的文件名指定图像是狗还是猫。

要为竞赛创建提交,您必须从测试集中为 12,500 幅图像中的每一幅图像创建一个预测。你必须预测图片中是一只狗还是一只猫。您可以通过上传包含 12,500 幅图像中每一幅图像的一行的 CSV 来对提交的图像进行评分。每一行必须包含图像的 id 和它是狗还是猫的预测(1 =狗,0 =猫)。

从图像中读取数据

Kaggle 通过一个包含所有猫和狗图像的 zip 文件提供了训练数据。

为了在训练期间将图像数据输入到模型中,我们首先必须从磁盘加载一个图像,并将其转换为一个字节数组。然后,训练程序将这个字节数组与“猫”或“狗”的标签一起输入神经网络,以学习它是猫还是狗。

我没有编写 Python 程序从磁盘读取文件,而是使用 Tensorflow 中的ImageDataGenerator。预处理模块。该类可以从磁盘加载图像,并生成训练过程可以直接使用的批量图像数据。它还可以做其他的事情,稍后我们优化模型的时候我会展示给你们看。

要使用 ImageDataGenerator,必须在磁盘上以某种方式组织数据。对于您想要识别的每个标签或类别,您必须创建一个与标签同名的子目录。在该目录中,放置该特定类别的图像。

结构化数据,以便 ImageDataGenerator 可以使用它,image by the author

分发训练图像

如前所述,我们必须将图像从 Kaggle 的 zip 文件复制到目录结构中。此外,为了表明我们的模型有多好,我们将从 Kaggle 获得的训练图像分成一个训练和验证集。例如,我们从 25,000 张照片中保留 5,000 张照片,用于验证经过训练的模型。

下面的 Python 函数分发了来自文件夹中的猫和狗的图像,在该文件夹中,您解压了带有 Kaggle 图片的 zip 文件。该过程创建一个可由 ImageDataGenerator 直接使用的结构。

该函数检索一个名为 validation_size 的参数。验证大小是一个介于 0.0 和 1.0 之间的数字,表示验证集在拆分中所占的比例。

分发和分割培训数据

该函数在将图像复制到正确的目录之前,会打乱图像名称列表。有了这个目录结构,我们就可以开始使用 ImageDataGenerator 了。

准备 ImageDataGenerator

我们将使用 ImageDataGenerator 中的flow_from_directory方法。该方法创建一个迭代器,从指定的目录中检索并返回图像。

创建 ImageDataGenerator 以向处理管道提供图像

我在创建实例时使用 rescale 参数来自动缩放图像的像素。我们没有使用范围从 0 到 255 的 RGB 值,而是将它们重新调整为从 0 到 1 的浮点数。这种重新标度使得训练深度学习模型变得更加容易。

我们在flow_from_directory方法中使用以下参数:

  • 目录—包含带有类别的子目录的目录。
  • target _ size——每个图像在提交到深度学习管道之前必须具有相同的大小。ImageDataGenerator 可以动态执行此操作。
  • batch _ size——我们分批将图像提交给深度学习管道。这将设置单个批次的大小。
  • class_mode —我们读取的数据类型的提示。在我们的例子中,“二进制”表明我们将使用两个类别。

当我们运行代码时,flow_from_directory会报告在哪些类别中找到了多少张图片。在我们的例子中,训练生成器报告 20000 幅图像,验证生成器报告 5000 幅图像。

flow_from_directory 报告由作者找到的图像和类

当 ImageDataGenerator 准备就绪时,我们可以开始创建和训练深度学习模型

创建和训练深度学习模型

照片由耐嚼在 Unsplash 上拍摄

由于我们已经准备好了数据,我们现在可以开始使用 TensorFlow 构建深度学习模型。我们使用Sequential实例添加层。

定义卷积神经网络的各层

卷积层

我们将模型分成三个主要部分。首先,Conv2DMaxPool2D 层有三种组合。这些被称为卷积层。一个Conv2D图层将滤镜应用于原始图像,以放大图片的某些特征。MaxPool2D层缩小了图像的大小,减少了所需参数的数量。减小图像的大小将提高训练网络的速度。

第二个也是实际的深度学习网络从第八行开始,我们在那里展平阵列。我们创建了一个 512 个单元的隐藏密集层,并使用校正线性单元(relu)作为激活函数。

最后一部分是输出层。第十行的最后一层有一个输出神经元。输出神经元将包含从 0 到 1 的值,其中 0 代表猫,1 代表狗。

编译模型

在开始训练之前,我们必须使用compile方法编译模型。编译为训练配置模型。

编译要配置的模型

我选择 Adam 优化器,因为这是一个很好的默认设置。损失函数是binary_cross_entropy,因为我们正在训练一个具有两种可能输出的分类模型。我想报训练时的准确率;因此,我添加了准确性作为度量标准。

第二行调用模型上的summary()。调用 summary 方法会产生层和参数数量的概述。这是一个很好的方法来验证你是否创造了你想要创造的一切。

显示模型的摘要

我们在总结中认识到的是图像的大小通过不同的层而减小。图片以 150x150 的图像开始,在进入深度神经网络之前,通过多层缩小为 17x17 大小的图像。

训练模型

编译完模型后,我们可以开始训练模型。我们可以通过在模型实例上调用fit方法来开始训练。

开始训练 50 个纪元

第一个参数是我们通过调用训练数据的ImageDataGeneratorflow_from_directory创建的迭代器。第二个参数是来自验证数据的迭代器。

epoch 参数显示模型将处理整个训练集的次数。在这里,我想将所有 20,000 个训练图像处理 50 次。

steps_per_epoch显示了在它结束这个纪元之前应该处理多少个批处理。我们之前在flow_from_directory上建立了 200 的批量大小,这给出了 200 * 100 = 20,000-我们的训练集的图像数量。

同样的道理也适用于validation_steps,50 * 100 = 5,000,这是我们验证集中的图像数量。

在训练期间,fit方法打印各种指标。下面你可以看到,在训练结束时,我们的训练集的精度是 0.99。验证集的精确度为 0.79。

fit 方法的控制台输出

一旦训练结束,model.fit方法返回一个包含所有训练数据的历史对象。这个历史对象可用于可视化指标。

可视化培训结果

我们可以将历史对象与 Matplotlib 库结合使用,以可视化每个时期的损失和准确性。下面的函数创建了两个图表,一个显示训练和验证准确性,另一个显示训练和验证损失。

使用 Matplotlib 可视化训练结果

如果您训练模型 50 个时期,则plot_result函数显示以下两个图表。

可视化培训结果,图片由作者提供

有趣的是,训练集的准确率慢慢增加到几乎 1 (100%)。然而,在前五个时期内,验证集的准确度增加到大约 0.8,然后在 0.8 处持平。这与验证集的丢失是一样的。它减少前四个时期,然后再次增加。

这是一个明显的过度拟合的案例。训练集的准确性提高了,但不能一般化,这从验证集的准确性停滞不前可以清楚地看出。

过度拟合

当模型很好地学习了训练数据,以至于它只能预测来自训练集的图像时,就会发生过度拟合。如果我们给它提供不是来自训练集的图像,它的表现会很差。

我们可以通过在您的训练数据集中使用更多数据来克服过度拟合。在这里,这是困难的,因为我们没有更多的来自 Kaggle 的猫和狗的图像。幸运的是,我们还有其他的可能性。

优化模型

在我创建一个预测并提交给 Kaggle 之前,我想看看我们是否可以优化这个模型。

图像增强

我们看到,在训练过程中,验证集的准确性没有变化。我们还讨论了可以通过增加训练数据量来提高准确性。

除了添加更多的训练数据,我们还可以使用图像增强。图像增强是一种从现有数据创建新的人工训练数据的技术。图像增强通过缩放、旋转、裁剪和翻转来改变原始训练图像。它人为地创造了用于训练的图像。

我们使用的 ImageDataGenerator 可以在从磁盘加载图像后扩充内存中的图像。我们可以在实例化 ImageDataGenerator 时设置图像增强选项。

之前,我们已经使用 ImageDataGenerator 从磁盘中重新缩放图像。现在,我们添加额外的选项来增强图像。

向 ImageDataGenerator 添加图像增强选项

我们指示 ImageDataGenerator 在特定范围内旋转、移动、缩放、翻转和剪切。ImageDataGenerator 随机执行增强。如果我们再次使用 ImageDataGenerator 对模型进行 50 个时期的训练,我们会看到下面的准确度和损失图。

使用图像增强将训练结果可视化,作者提供的图像

请看左图,它显示了验证准确性,我们看到它没有像我们在之前的培训中看到的那样在 80%停止。相反,它慢慢攀升到 90%的准确率。

右图显示了训练集和验证集的损失。这里我们也看到它们缓慢而稳定地减少。

好得多的结果!

创建预测

在进一步优化模型之前(这将在另一篇文章中讨论),我们应该将我们对测试集的预测上传到 Kaggle,看看我们的模型得分如何。在我训练了模型之后,我使用模型上的save方法保存了它。

load_and_predict方法中,我们首先加载模型,然后创建一个新的ImageDataGenerator来为模型提供来自测试集的图像。然后,我们在模型上调用 predict 方法,从测试集中创建预测。

加载保存的模型并创建预测

提交给 Kaggle 的文件必须是 CSV 格式。我们使用熊猫从预测数组中创建一个数据帧。该数据帧保存为 CSV 文件,并上传至 Kaggle。使用以下对数损失函数对每个提交进行评分。

我从使用 CNN 和图像增强的模型提交的得分为 0.26211。这将使我在公共排行榜上名列第 833 位。

上传提交的内容,查看作者的评分和图片

这是一个好的开始,但我想看看我能不能进一步提高精确度。但这是另一篇文章的内容。

结论

在本文中,我描述了如何使用 TensorFlow 创建和使用卷积神经网络。这个神经网络被用来从图像中识别猫和狗。猫和狗的图像取自于这一古老的猫狗比赛。

尽管竞赛已经停止,但您仍然可以上传您对测试集的预测并对其进行评分。我的 CNN 获得了 0.26211 的分数,这使我们在公共排行榜上名列第 833 位。

在下一篇文章中,我将描述我们如何使用迁移学习来进一步提高准确性。

本文的源代码可以在 GitHub 上找到。这个存储库非常大,因为它包含了所有的训练和测试图像。

感谢您的阅读!

推荐使用 Scikit-Learn 和 Tensorflow 推荐器

原文:https://towardsdatascience.com/recommend-using-scikit-learn-and-tensorflow-recommender-bc659d91301a?source=collection_archive---------11-----------------------

针对每位客户售出的销售项目(二进制)的协作筛选

远大前程,布勒,2011,绝缘胶带,木材,纸板,丙烯酸玻璃,喷雾疼痛 t(经我的朋友马蒂亚斯·布勒友好许可)

动机:

我们将学习如何向顾客推荐销售商品,查看顾客的个人购买记录。在我之前的文章中,我详细解释了推荐引擎的许多概念:

本帖重点推荐使用 Scikit-Learn 和 Tensorflow 推荐器。

解决方案:

首先,让我们看看我们的数据框架(数据存储在我的 github 存储库):

import pandas as pd
import numpy as np
data = pd.read_excel(‘/content/gdrive/MyDrive/DDDDFolder/DDDD.xlsx’) 
data.head()

这就是我们的数据帧最初的样子

因此,我们得到的是每个销售日的销售额、客户(例如,客户代码 0 是一个特定的客户)和销售项目(类似于客户,销售项目 0 是一个特定的销售项目)。

其他栏目与我们的产品推荐无关,因此我们将删除这些栏目:

DataPrep = data[['SalesItem', 'SalesAmount', 'Customer']] 
#we will only use SalesItem, SalesAmount and Customer
DataPrep.head()

我们希望了解哪些顾客购买了哪些销售商品:

DataGrouped = DataPrep.groupby(['Customer', 'SalesItem']).sum().reset_index() # Group together
DataGrouped.head()

我们的协同过滤将基于二进制数据。对于每个数据集,我们将在购买时添加 1。这意味着,该客户已经购买了该商品,不管该客户过去实际购买了多少。我们使用这种二进制数据方法作为我们的推荐示例。另一种方法是使用 SalesAmount 并将其规范化,以防您希望将购买的 SalesItems 的数量视为一种口味因素,这意味着某人购买了 SalesItem x 100 次——而另一个客户只购买了相同的 sales item x 5 次——并不喜欢它。我认为,在销售建议中,二元方法通常更有意义,但当然这真的取决于您的数据:

def create_DataBinary(DataGrouped):DataBinary = DataGrouped.copy()DataBinary['PurchasedYes'] = 1return DataBinaryDataBinary = create_DataBinary(DataGrouped)DataBinary.head()

最后,让我们去掉 SalesAmount 列:

purchase_data=DataBinary.drop(['SalesAmount'], axis=1)purchase_data.head()

我们准备的数据框架

为了更方便,我们在每个 SalesItem 中添加 I 作为 Item 的前缀。否则,我们将只有客户和销售项目编号,这可能有点令人费解:

purchase_data[‘SalesItem’] = ‘I’ + purchase_data[‘SalesItem’].astype(str)

现在我们的数据框架最终为构建推荐器做好了准备。为什么你会问所有的数据?这样做的原因是我希望它能帮助你在数据中采用这个例子,特别是如果你是新手的话。

现在我们将用 Scikit-Learn 导入推荐所需的库:

from scipy.sparse import coo_matrix, csr_matrixfrom sklearn.metrics.pairwise import cosine_similarityfrom sklearn.preprocessing import LabelEncoder

现在让我们来计算项目-项目余弦相似度:

def GetItemItemSim(user_ids, product_ids):SalesItemCustomerMatrix = csr_matrix(([1]*len(user_ids), (product_ids, user_ids)))similarity = cosine_similarity(SalesItemCustomerMatrix)return similarity, SalesItemCustomerMatrix

在数据框架中接收每个客户的前 10 个销售项目建议,我们将通过创建 SalesItemCustomerMatrixs(每行销售项目和客户作为填充二元关联的列)来使用上面单元格中的项目-项目相似性矩阵。

def get_recommendations_from_similarity(similarity_matrix, SalesItemCustomerMatrix, top_n=10):CustomerSalesItemMatrix = csr_matrix(SalesItemCustomerMatrix.T)CustomerSalesItemScores = CustomerSalesItemMatrix.dot(similarity_matrix) # sum of similarities to all purchased productsRecForCust = []for user_id in range(CustomerSalesItemScores.shape[0]):scores = CustomerSalesItemScores[user_id, :]purchased_items = CustomerSalesItemMatrix.indices[CustomerSalesItemMatrix.indptr[user_id]:CustomerSalesItemMatrix.indptr[user_id+1]]scores[purchased_items] = -1 # do not recommend already purchased SalesItemstop_products_ids = np.argsort(scores)[-top_n:][::-1]recommendations = pd.DataFrame(top_products_ids.reshape(1, -1),index=[user_id],columns=['Top%s' % (i+1) for i in range(top_n)])RecForCust.append(recommendations)return pd.concat(RecForCust)

计算建议:

def get_recommendations(purchase_data):user_label_encoder = LabelEncoder()user_ids = user_label_encoder.fit_transform(purchase_data.Customer)product_label_encoder = LabelEncoder()product_ids = product_label_encoder.fit_transform(purchase_data.SalesItem)# compute recommendationssimilarity_matrix, SalesItemCustomerMatrix = GetItemItemSim(user_ids, product_ids)recommendations = get_recommendations_from_similarity(similarity_matrix, SalesItemCustomerMatrix)recommendations.index = user_label_encoder.inverse_transform(recommendations.index)for i in range(recommendations.shape[1]):recommendations.iloc[:, i] = product_label_encoder.inverse_transform(recommendations.iloc[:, i])return recommendations

让我们开始推荐:

recommendations = get_recommendations(purchase_data)

每位客户的 10 大建议

这意味着,例如,客户 0(上面截图的第一行)应该对购买商品 I769、第二相关的 I253、第三相关的 I1146 等非常感兴趣。

如果您想将建议导出到 excel 等文件中,您可以这样做:

dfrec = recommendationsdfrec.to_excel("ExportCustomerName-Itemname.xlsx")

现在,我们已经使用 Scikit-Learn 向客户推荐了他们应该非常感兴趣(但尚未购买)的销售商品。

Tensorflow 推荐器:

出于好奇,我们再重复一遍,这次使用 Tensorflow 推荐器。的原始代码 [1]取自谷歌的大脑团队,只是稍微被我们的数据集采用。我在 Google Colab 中运行了代码:

!pip install -q tensorflow-recommenders!pip install -q --upgrade tensorflow-recommendersfrom typing import Dict, Textimport numpy as npimport tensorflow as tfimport tensorflow_recommenders as tfrs

我们还将添加 C(代表客户)作为每个客户 id 的前缀:

purchase_data['Customer'] = 'C' + purchase_data['Customer'].astype(str)uniq = data.SalesItem.unique()uniq = pd.DataFrame(uniq)uniq.columns = ['SalesItem']uniqrat = purchase_data[['Customer', 'SalesItem', 'SalesAmount']]dataset = tf.purchase_data.Dataset.from_tensor_slices(dict(purchase_data))ratings = dataset.from_tensor_slices(dict(rat))SalesItem = dataset.from_tensor_slices(dict(uniq))ratings = ratings.map(lambda x: {"Customer": x["Customer"],"SalesItem": x["SalesItem"]})SalesItem = SalesItem.map(lambda x: x["SalesItem"])ratings.take(1)CustomerID_vocabulary = tf.keras.layers.experimental.preprocessing.StringLookup(mask_token=None)CustomerID_vocabulary.adapt(ratings.map(lambda x: x["Customer"]))SalesItem_vocabulary = tf.keras.layers.experimental.preprocessing.StringLookup(mask_token=None)SalesItem_vocabulary.adapt(SalesItem)#Define a model#We can define a TFRS model by inheriting from tfrs.Model and implementing the compute_loss method:class SalesItemRecModel(tfrs.Model):def __init__(self,CustomerModel: tf.keras.Model,SalesItemModel: tf.keras.Model,task: tfrs.tasks.Retrieval):super().__init__()# Set up Customer and SalesItem representations.self.CustomerModel = CustomerModelself.SalesItemModel = SalesItemModel# Set up a retrieval task.self.task = taskdef compute_loss(self, features: Dict[Text, tf.Tensor], training=False) -> tf.Tensor:# Define how the loss is computed.CustEmbeddings = self.CustomerModel(features["Customer"])SalesItemEmbeddings = self.SalesItemModel(features["SalesItem"])return self.task(CustEmbeddings, SalesItemEmbeddings)

定义客户和销售项目模型:

CustomerModel = tf.keras.Sequential([CustomerID_vocabulary,tf.keras.layers.Embedding(CustomerID_vocabulary.vocab_size(), 64)])SalesItemModel = tf.keras.Sequential([SalesItem_vocabulary,tf.keras.layers.Embedding(SalesItem_vocabulary.vocab_size(), 64)])task = tfrs.tasks.Retrieval(metrics=tfrs.metrics.FactorizedTopK(SalesItem.batch(128).map(SalesItemModel)))

现在,我们将创建模型,对其进行训练,并生成预测:

model = SalesItemRecModel(CustomerModel, SalesItemModel, task)model.compile(optimizer=tf.keras.optimizers.Adagrad(0.5))# Train for 3 epochs.model.fit(ratings.batch(4096), epochs=3)# Use brute-force search to set up retrieval using the trained representations.index = tfrs.layers.factorized_top_k.BruteForce(model.CustomerModel)index.index(SalesItem.batch(100).map(model.SalesItemModel), SalesItem)customers = data.Customer.unique().tolist()fcst = pd.DataFrame()for x in customers:_, SalesItem = index(np.array([x]))fcst = pd.concat((fcst, pd.DataFrame(SalesItem[0, :10].numpy()).transpose()))fcst['Customer'] = customersfcst.to_excel('RecFc.xlsx', index=False)

非常感谢您的阅读!希望这篇文章对你有帮助。请随时在 LinkedIn 、 Twitter 或工作室与我联系。

https://jesko-rehberg.medium.com/membership

最初发表在我的网站 DAR-Analytics 上,这里也有 Jupyter 笔记本和数据文件。

参考:[1] 马切伊和詹姆斯陈,谷歌大脑,介绍 TensorFlow 推荐器(2020),https://blog . tensor flow . org/2020/09/Introducing-tensor flow-re commenders . html

推荐系统解释

原文:https://towardsdatascience.com/recommendation-systems-explained-a42fc60591ed?source=collection_archive---------0-----------------------

用 Python 解释和实现基于内容的协同过滤和混合推荐系统

由水花的穆罕默德·扎赫里拍摄的图像

目录

  • 什么是推荐系统
  • 什么定义了一个好的推荐
    • K 倍交叉验证
    • MSE
    • RMSD
  • 数据
    -要求
    -代码
  • 协同过滤推荐系统
    -直觉
    -优点
    -缺点
    -实例
    -实现
  • 基于内容的推荐系统
    -直觉
    -优点
    -缺点
    -示例
    -实现
  • 混合推荐系统
    -直觉
    -优点
    -缺点
    -实例
    -实现
  • 结束语
  • 资源

什么是推荐系统

推荐引擎是机器学习的一个子类,通常处理产品/用户的排名或评级。不严格地说,推荐系统是一个预测用户对某个特定项目的评价的系统。这些预测将被排序并返回给用户。

他们被各种大公司使用,如谷歌、Instagram、Spotify、亚马逊、Reddit、网飞等。通常是为了增加用户和平台的参与度。例如,Spotify 会推荐与你反复听过或喜欢的歌曲相似的歌曲,这样你就可以继续使用他们的平台来听音乐。亚马逊使用推荐,根据用户收集的数据向不同的用户推荐产品。

推荐系统通常被视为“黑箱”,这些大公司创建的模型不太容易解释。生成的结果通常是对用户需要/想要的东西的推荐,但是直到他们被推荐给他们,他们才意识到他们需要/想要它。

有许多不同的方法来建立推荐系统,一些使用算法和公式化的方法,如页面排名,而另一些使用更多的建模为中心的方法,如协同过滤,基于内容,链接预测等。所有这些方法的复杂性都各不相同,但是复杂性并不意味着“好”的性能。通常简单的解决方案和实现会产生最强的效果。例如,像 Reddit、Hacker News 和 Google 这样的大公司已经使用推荐引擎的简单公式化实现来推广他们平台上的内容。在本文中,我将提供一个直观的技术概述,介绍推荐系统的体系结构,以及在一个示例生成的数据集上实现一些不同的变体。

什么是好的推荐?

确定什么是好的推荐本身就是一个问题,许多公司都在努力解决这个问题。这个“好”推荐的定义有助于评估你建立的推荐器的性能。推荐的质量可以通过各种衡量覆盖率和准确性的策略来评估。准确度是所有可能推荐中正确推荐的比例,而覆盖率衡量系统能够为其提供推荐的搜索空间中对象的比例。评估建议的方法完全取决于数据集和用于生成建议的方法。推荐系统与分类和回归建模问题在概念上有一些相似之处。在理想情况下,你会希望看到真实用户对推荐的反应,并跟踪用户周围的指标以改进你的推荐,然而,这是很难做到的。评估推荐器准确性的常用统计准确性度量有 RMSD 、 MAE 和 k 倍交叉验证。

K 折交叉验证

  • 假设您已经建立了一个模型,该模型将根据一组特征预测用户对某个项目的评价。k 折叠交叉验证可用于通过准确性度量来推断模型的结果
  • 除了我们创建 K 个随机分配的训练和测试集之外,与训练测试分割的想法相同
  • 每个单独的训练集/折叠用于独立地在推荐系统上进行训练,然后相对于测试集测量结果系统的准确性
  • 我们取准确度分数的平均值来看推荐系统学习得有多好
  • 这种方法有利于防止模型过度拟合,但是这是一个计算量很大的过程

平均绝对误差

图片取自统计如何到

  • 表示评级预测中每个误差的平均绝对值
  • MAE 分数越低越好

RMSD(均方根偏差)

图片取自统计如何到

  • 与 MAE 类似的度量标准,但当预测值与真实值相差甚远时惩罚更强,而当预测值与真实值更接近时惩罚更弱
  • 去掉真实值和预测值之差的平方,而不是绝对值之和。这确保了结果值总是正的,并且当差值高时较大,当差值低时较小。
  • RMSD 分数越低越好

这些指标通常用于评估推荐的质量,但是它们缺少各种组件。拥有与推荐相关的用户数据对于了解推荐的真实质量至关重要。能够跟踪推荐的点击率、平台参与度、响应度等。将提供更清晰的推荐质量观点。需要注意的其他组件是,当用户已经有 X 段时间没有与推荐进行交互时,要知道何时改变推荐,或者何时根据新的评级或来自用户的交互来重新训练推荐者。你还需要注意这些推荐是否会将用户限制在产品的某一部分,推荐者如何处理新颖性、多样性和选择偏差。A/B 测试通常是用来跟踪这些度量的方法(查看我关于贝叶斯和频繁者 a/b 测试的文章)。

数据

在接下来的章节中,我们将更深入地讨论创建推荐引擎的不同方法以及 Python 中的相关实现。本节将提供一个脚本,该脚本将合成一个与书籍相关的数据集。该数据集将用于推荐系统的应用。在下面的部分中,本文的目标不是获得有意义的结果,而是向用户展示各种类型的推荐引擎背后的直觉和实现。因此,这些建议的结果将是无意义的,但这些方法将类似于工业生产级环境中的方法。

要求

  • Python = 3.8.8
  • 熊猫= 1.2.4

代码

协同过滤系统

直觉

协同过滤是通过识别许多用户的偏好和信息来预测用户兴趣的过程。这是通过使用涉及多个代理、数据源等之间的协作的技术来过滤信息或模式的数据来完成的。协同过滤背后的潜在直觉是,如果用户 A 和 B 对某个产品有相似的品味,那么 A 和 B 很可能对其他产品也有相似的品味。

在协同过滤中有两种常见的方法,基于记忆的方法和基于模型的方法。

  1. 基于记忆的方法——通常也称为邻域协同过滤。本质上,用户项目组合的评级是基于它们的邻域来预测的。这可以进一步分为基于用户的协同过滤和基于项目的协同过滤。以用户为基础本质上意味着志同道合的用户会产生强烈的和相似的推荐。基于项目的协同过滤基于项目之间的相似性来推荐项目,该相似性是使用那些项目的用户评级来计算的。
  2. 基于模型的方法—是使用机器学习的预测模型。与数据集相关联的特征被参数化为模型的输入,以尝试解决优化相关问题。基于模型的方法包括使用决策树、基于规则的方法、潜在因素模型等。

优势

使用协同过滤模型的主要优点是实现简单,并且覆盖面广。这也是有益的,因为它捕捉到了微妙的特征(对于潜在因素模型来说非常正确),并且不需要理解项目内容。

劣势

这种模式的主要缺点是它对推荐新项目不友好,这是因为没有用户/项目与之交互。这被称为冷启动问题。众所周知,基于内存的算法在高度稀疏的数据集上表现不佳。

例题

协作过滤算法的一些例子:

  • 向用户推荐 YouTube 内容——根据与您订阅/观看过类似视频的其他用户向您推荐视频。
  • CourseEra 课程推荐——根据其他已经完成你已经完成的课程的人向你推荐课程。

实施

  1. 从 generate_data 函数(上面提供的函数)导入数据或从这里下载 CSV
  2. 生成一个数据透视表,其中读者在索引上,书籍在列上,值是评级
  3. 使用 svds 计算项目和用户之间的相似度
  4. 根据 user_id 生成项目建议

基于内容的系统

直觉

基于内容的系统基于用户偏好和简档生成推荐。他们试图将用户与他们以前喜欢的项目匹配起来。项目之间的相似性级别通常是基于用户喜欢的项目的属性来建立的。与大多数利用目标用户和其他用户之间的评级的协同过滤模型不同,基于内容的模型关注由目标用户自己提供的评级。本质上,基于内容的方法利用不同的数据源来生成推荐。

最简单的基于内容的系统需要以下数据源(这些需求会随着您要构建的系统的复杂程度而增加):

  1. 项目级数据源—您需要一个与项目属性相关联的强大数据源。对于我们的场景,我们有诸如图书价格、页数、出版年份等信息。您对该物品了解的信息越多,对您的系统就越有利。
  2. 用户级数据源——您需要某种用户反馈,基于您提供推荐的项目。这种级别的反馈可以是隐式的,也可以是显式的。在我们的样本数据中,我们处理的是用户对他们读过的书的评价。你跟踪的用户反馈越多,对你的系统就越有利。

优势

当没有足够数量的可用评级数据时,基于内容的模型最有利于推荐项目。这是因为用户可能已经对具有相似属性的其他项目进行了评级。因此,即使没有大量数据,模型也应该能够利用评分和项目属性来生成推荐。

劣势

基于内容的系统有两个主要缺点。

  1. 基于用户已经消费的项目/内容,所提供的推荐是“明显的”。这是一个缺点,因为如果用户从未与特定类型的项目交互过,则该项目将永远不会被推荐给用户。例如,如果你从未读过推理小说,那么通过这种方法,你将永远不会被推荐推理小说。这是因为该模型是特定于用户的,并且没有利用来自类似用户的知识。这降低了建议的多样性,这对许多企业来说是一个负面的结果。
  2. 它们对于向新用户提供推荐是无效的。构建模型时,您需要项目的显式/隐式用户级数据的历史记录。通常重要的是,要在不过度拟合的情况下做出稳健的预测,要有一个大的评级数据集。

例句

基于内容的系统的一些例子有:

  • 亚马逊产品源(向您推荐与您之前购买的产品相似的产品)
  • Spotify 音乐推荐

有许多优秀的基于内容的系统是通过算法构建的,不依赖于基于模型的方法。例如,众所周知,Hacker Rank 和 Reddit 等公司以前使用算法向用户推荐他们平台上的新帖子。建立基于内容的推荐算法的关键在于为你的业务定义一套规则,用来对项目进行排序。在 Reddit 的例子中,他们的推荐受到发布时间、喜欢数量、不喜欢数量、评论数量等的限制。这可以作为一个公式中的一个因素来产生一个帖子的分数,高分将产生高推荐,反之亦然。

实施

  1. 从 generate_data 函数(上面提供的函数)导入数据或从这里下载 CSV
  2. 正常化图书价格、图书评级、页数
  3. 一个热编码出版年份、图书类型、文本语言
  4. 给定 book_id 输入,计算余弦相似度并返回与输入相似的前 n 本书

混合推荐系统

直觉

推荐系统的各种方法各有利弊。通常,这些方法中的许多在单独使用时可能看起来有局限性,特别是当问题有多个数据源时。混合推荐系统被设计成使用不同的可用数据源来生成健壮的推理。

混合推荐系统有两种主要的设计,并行和顺序。并行设计向多个推荐系统提供输入,这些推荐中的每一个被组合以生成一个输出。顺序设计向单个推荐引擎提供输入参数,输出被传递给序列中的下一个推荐器。请参考下图,了解两种设计的直观表示。

并行和顺序推荐系统体系结构。图片由 C.C. Aggarwal 提供,推荐系统:教科书

优点

混合系统将不同的模型结合起来,以克服一种模型与另一种模型的缺点。这总体上减少了使用单个模型的缺点,并有助于生成更可靠的建议。这为用户提供了更加强大和个性化的推荐。

缺点

这些类型的模型通常具有很高的计算复杂度,并且需要一个大的评级和其他属性的数据库来保持最新。没有最新的指标(用户参与度、评级等。)这使得很难重新训练和提供具有来自各种用户的更新项目和评级的新推荐。

例子

网飞是一家使用混合推荐系统的公司,他们根据相似用户的观看和搜索风格(协同过滤)以及用户评级的具有相似特征的电影(基于内容)向用户提供推荐。

实施

  1. 从 generate_data 函数(上面提供的函数)导入数据或从这里下载 CSV
  2. 使用基于内容的模型(cosine_similarity)计算 50 本最相似的书
  3. 使用协作式
    过滤模型(SVD)计算用户可能给这 50 本书的预测评分
  4. 返回预测评分最高的前 n 本书

如果您想要一步一步地了解如何以一种不同的方式实现混合推荐系统,您可以参考我的关于使用 Node2Vec 构建链接预测推荐引擎的文章。

https://vatsal12-p.medium.com/link-prediction-recommendation-engines-with-node2vec-c97c429351a8

结束语

请注意,这只是对推荐引擎的介绍。还有更多类型和方法可以构建强大的推荐引擎,本文没有涉及。一些值得注意的是通过链接预测,算法方法,贝叶斯模型,马尔可夫模型等产生推荐…

这篇文章的目的是提供对推荐系统所使用的基本方法(协同过滤、基于内容和混合)的直观理解和实现。协同过滤是通过识别来自许多用户的偏好和信息来预测用户兴趣的过程,而基于内容的系统基于用户的偏好和简档来生成推荐。混合系统通常是许多推荐系统的组合。

我将在下面列出一些用于评估推荐质量的思考材料。本文没有回答这些问题,但是在构建一个好的推荐系统时,这些问题是必不可少的。

  • 你如何跟踪推荐的流失率、响应度、参与度和新颖性?
  • 当你知道用户已经有 X 段时间没有和之前的推荐互动了,你应该什么时候给他们看一个新的推荐呢?
  • 当用户评价新产品或与新产品互动时,它会立即影响你的推荐系统,还是稍后?
  • 随着用户与平台互动的增加,推荐是如何变化的?
  • 你怎么知道你的推荐没有把你的用户限制在企业的小部分?

我特意尝试实现每种类型的推荐系统的不同版本,以便对它们的实现有更广泛的理解。所有用于生成这些结果的代码和数据都可以在这里查看。

资源

  • science direct . com/science/article/pii/S1110866515000341 # s 0160
  • https://www . statistics show to . com/absolute-error/#:~:text = E % 20 = % 20x experimental % 20% E2 % 80% 93% 20x true,true % 20 is % 20 the % 20 true % 20 measure。
  • https://www . statisticshowto . com/probability-and-statistics/regression-analysis/RMSE-root-mean-square-error/#:~:text = If % 20 you % 20 don t % 20 like,square % 20 root % 20 of % 20 the % 20 result。
  • https://en.wikipedia.org/wiki/Collaborative_filtering
  • https://en . Wikipedia . org/wiki/Cold _ start _(recommender _ systems)
  • https://link.springer.com/book/10.1007/978-0-387-85820-3

如果你喜欢读这篇文章,看看其他你可能也感兴趣的文章:

[## 贝叶斯 A/B 测试解释

towardsdatascience.com](/bayesian-a-b-testing-explained-344a6df88c1a)

基于矩阵分解的推荐系统

原文:https://towardsdatascience.com/recommendation-systems-via-matrix-factorization-28e7d0aa8ca7?source=collection_archive---------29-----------------------

探索 MovieLens 100k 数据集与新币,亲笔签名,和惊喜包。

查尔斯·德鲁维奥在 Unsplash 上拍摄的照片

由加文·史密斯和阮宣汉

这个项目是我这个学期机器学习课的第三个项目。该项目旨在通过优化模型的预测能力,使用 MovieLens 100k 数据集训练一种机器学习算法,用于电影推荐。我们得到了一个干净的预处理版本的 MovieLens 100k 数据集,其中有 943 个用户对 1682 部电影的评级。我们的预测系统的输入是(用户 id,电影 id)对。我们的预测器的输出将是范围(1,5)内的标量等级 y-等级 1 是最差的,等级 5 是最好的。我们的主要任务是预测所有用户-电影对的评级。使用四种不同的模型来执行推荐系统。

  • 问题 1 遵循简单基线模型与 SGD 和自动签名的使用。我们考虑了最简单的可能基线模型——无论考虑什么用户或电影,该模型都对电影的评级做出相同的标量预测。
  • 问题 2 是在 SGD 和 Autograd 中使用每个项目一个标量的基线后出现的。
  • 问题 3 是在 SGD 和 Autograd 中使用每项一个向量的协同过滤后出现的。
  • 问题 4 是开放式的。我们得到了一个省略了评级的数据集。

对于所有问题,我们的任务是在验证集和测试集上获得关于平均绝对误差(MAE)的最佳可能预测结果。所有章节都解释了方法,并附有相应的图表。

问题 1:带有 SGD 和自动签名的简单基线模型

图 1a:简单基线模型历元与平均绝对误差的关系

图 1a 显示了简单基线模型的平均绝对误差如何随时间变化,该模型仅预测每个示例的μ值。左侧图的批次大小为 10000,由于其批次大小较大,因此稳定地收敛到 MAE 值,而右侧图的批次大小较小,为 100,开始不稳定地收敛到某个值。这个问题我们选择 n_epoch=10。对于 batch_size=10000,我们在 4 个纪元后开始看到一些学习;MAE 开始接近恒定值,同时历元的数量保持增加。对于 batch_size=100,在 4 个历元之后,数据噪声较小。

1b:您如何计算该模型的最佳μ值?

可以计算最佳 mu 值的行应该是“print(ag _ NP . mean(ag _ NP . as array(train _ tuple[2]))”,它将计算所有示例的平均值。该计算产生的值为 3.58,与我们模型的 mu 值相同。这是真的,因为如果我们为每个例子预测相同的值,分数的平均值将在大多数情况下是“最”正确的。

问题 SGD 和亲笔签名的每个项目一个标量基线

图 2a:标量模式模型历元与平均绝对误差的关系

图 2a 示出了标量模式模型的平均绝对误差随时间随随机梯度下降的变化。左图显示了批量为 10000 时误差的变化情况,由于批量越大,误差以越稳定的速率降低,然后稳定在 0.75 左右。右图显示了相同的过程,但批量大小为 100,因此误差会快 0.75 左右,但其值更不可预测,因为批量更小。这个问题我们选择 n_epoch=250。对于 batch_size=10000,我们在 200 个纪元后开始看到一些学习;MAE 开始接近恒定值,同时历元的数量保持增加。对于 batch_size=100,在 200 个历元之后,数据噪声更小。为了选择步长,我们首先为步长选择一个大的数字(step_size=1.5),我们看到训练图发散。因此,我们将步长减小 0.25。最终,我们发现小于或等于 0.75 的步长不会使训练损失发散。对于这个问题,我们选择步长等于 0.75。

表 2b:精选电影片名对比 c j

表 2b 按顺序示出了所选列表中的每部电影及其学习的每部电影分级调整参数 cj。我们在验证集上选择具有最佳 MAE 的模型,其中批量大小为 10000,250 个时期,步长为 0.75。该模型给出了列表结果。从这个列表中,我们可以看到,具有较大正 cj 值的电影往往是更受普遍“称赞”的电影,或“经典”,如星球大战印第安纳琼斯,而值较低的电影往往不那么受欢迎,如侏罗纪公园 2 或*尖叫 2、*这些都是恐怖电影,并不总是吸引广泛的观众。在我们的问题中,一部电影的偏见将描述这部电影与所有电影的平均水平相比有多好。该值仅取决于电影,不考虑用户和电影之间的交互。因此,对于一部电影来说,大而正,意味着这部电影很可能被人们评为高,而大的负值意味着这部电影很可能被评为低。

问题 SGD 和自动签名的单向量协同过滤

图 3a:历元模式模型历元与平均绝对误差的关系,α=0

该图显示了每个项目一个向量的协作过滤器模型的平均绝对误差如何随时间变化,每个图显示了具有不同数量的维度 K 的模型,这些维度用于表示用户和电影。对于所有的图表,我们可以看到,当仅仅过了很短的时间时,模型对数据拟合不足,因为训练集和验证集误差都非常高。所有的模型在 250 个时期后开始过度拟合,这可以从训练集误差减小,但是验证集误差又开始增加的事实中看出。随着 K 的增加,我们看到验证集误差在 350 个历元附近有一个更明确的下降,并且随着 K 值的增加,模型过拟合得更快。

图 3b:历元模式模型历元与平均绝对误差的关系,α = 0.1

该图显示了对于 alpha 值为 0.1 的模型,平均绝对误差如何随历元数而变化。我们通过使用网格搜索训练具有许多不同 alpha 值的模型来选择这个 alpha 值,我们发现 alpha 值为 0.1 可以最大程度地降低平均绝对误差。使用此 alpha 值,我们能够将平均绝对误差降低到比模型更低的值,该模型使用 0 的 alpha 值进行训练。对于这个问题,批量固定为 1000。我们很早就停下来寻找在验证集上表现最好的参数。0.75 是我们能得到的最大步长,它不会使训练损失发散。为了找到在验证集上表现最好的参数,我们使用了早期停止。

表 3c:验证和测试集的平均绝对误差

此表显示了我们在验证集和测试集的问题 1 至 3 中训练的每个模型的平均绝对误差。为了确定每个模型的最佳版本,我们试图最小化验证集上的误差。对 M1 来说,这很容易,因为我们只是选择一个固定的变量来进行每次猜测,所以只有一个真正的最优值。对于 M2,我们选择了使用 10000 批次大小的模型,因为它最小化了验证集的均方误差。我们对每个 M3 车型都采用了提前停车;我们搜索了不同的 alpha 值、批量大小和步长大小,以确定哪个版本的模型产生的平均绝对误差最小。我们建议使用 50 的 K 值,因为它具有最小的误差。尝试具有较大 K 值的模型可能是有益的,因为似乎 K 值越大,误差越小。我们找到的最佳模型是 M3,K 值为 50,alpha 值为 0.1;这个模型减少了验证集和测试集的错误。此外,L2 罚款被添加到 M3 模型。在向量 u 和 v 上设置 L2 正则化会强制这些值尽可能小。它可以帮助我们避免过度拟合。

图 3d:所选电影的 v j 值

该图示出了 select_movies 数据集中的电影的所学习的每电影向量 vj 的二维嵌入。这是使用 K=2 因子的最佳 M3 模型创建的。在这个图表中,电影是根据它们的因素向量来放置的。我们可以看到,有些相似的电影往往会被归为一类;但是,分组对我们来说并不是超级明显的。在右下方,我们可以看到《呐喊》《榆树街的噩梦》这样的恐怖片。我们注意到,具有相似评级的电影将在嵌入空间中更接近。解释这一点的一个原因是,我们的 M3 模型已经了解到这些电影与类似的评级相关联。为了检查我们的观察,我们从 ratings_all_development_set 数据集计算平均评分。《T2》《西雅图不眠夜》《T3》的平均评分是 3.55,而《T4》《当你熟睡时》《T5》的平均评分是 3.56。而这两部电影在嵌入空间里放置的很近。

问题 4:开放式推荐挑战

4a:开放式推荐挑战方法说明

对于我们的开放式推荐模型,我们选择使用来自 surprise 包的 KNNWithMeans 分类器。KNNWithMeans 的工作原理与常规的 K 最近邻算法相同,它计算 K 个点之间的相似性,并返回与这些点最一致的预测。KNNWithMeans 的不同之处在于,您必须指定最小和最大 K 值,因为该算法只查看相似性为正的点。毕竟,使用负相关的点是没有意义的。此外,该模型考虑了每个用户的平均评分,这有助于数据的标准化。我们选择使用这种模式,因为向某人推荐电影的一种好方法是让他们选择最像他们已经喜欢的电影,而且由于 KNN 通过基于数据点之间的相似性进行预测来工作,我们认为这将是最佳选择。我们还选择使用该模型的另一个原因是,SVD 等其他选项需要更长的训练时间,因此我们在这些模型上进行深入超参数搜索的时间会更少。

为了训练我们的模型,我们使用网格搜索来寻找最佳超参数值,并使用 5 重交叉验证来验证我们的模型。我们搜索的超参数是 K(要比较的最大点数)、min_k(要比较的最小点数)和 sim_option(控制如何计算点之间的相似性)。我们发现 K 值为 50,min_k 为 2,Pearson 相似性选项是最好的。我们通过选择使交叉折叠验证的验证集上的平均绝对误差最小化的值来确定这些是最好的。

图 4b:模型超参数选择

该图显示了我们为 KNNWithMeans 模型调整的三个超参数。最左边的图显示了平均绝对误差如何随着 K(与进行比较的点数)的增加而变化。随着数字的增加,模型开始减少过度拟合,因为训练和验证误差都减少了。对于 200 的 K 值,模型开始对数据进行欠拟合,因为预测变得更加依赖于与预测点不相似的数据点。从第二个图表中,我们可以看到,要考虑的最小邻域数的任何增加都会增加误差,因此最佳值应为 1。最后,右图显示了相似性选项对我们模型的性能几乎没有影响,但是 Pearson 选项比其他两个选项稍有优势。

表 4c:模型平均绝对误差性能

该图显示了我们的模型在用于排行榜的保留数据集上的平均绝对误差,以及在训练我们的模型时它在验证集和测试集上的性能。

4d:模型性能分析

表 4c 显示,我们的模型在保留数据集上的平均绝对误差低于我们的交叉验证集上的误差。这是因为与我们的训练数据(89992 个评分)相比,不公开的数据集具有较小的大小(10000 个评分)。可能存在测试数据不能很好地代表我们的训练数据的情况。因此,它表现良好,误差很低。

当将该数据与表 3c 中的模型结果进行比较时,我们可以看到,我们的排行榜平均绝对误差最低,我们的测试误差高于任何 M3 误差。其原因是因为 SVD 分解原始矩阵,sense 矩阵用于预测评级(使用,项目)对。而在 KNN 中,预测是通过找到与要预测其评级的输入用户相似的用户群,然后取这些评级的平均值来进行预测。奇异值分解在学习训练数据方面做得更好;我们在验证集上看到一个较小的错误。

4e:模型局限性和未来工作的机会

我们面临的一个限制是,像 SVD 这样更复杂的模型花费的时间太长,我们无法有效地调整模型;如果我们有更好的计算能力,使用这样的模型会更可行。另一件可以改进的事情是寻找记录损失的不同方法;当训练我们的模型时,我们只使用平均绝对误差,但是当非常不正确的猜测被加权得更重时,模型可能表现得更好,就像使用均方误差一样。此外,我们会考虑其他用户特征,如年龄,性别,国籍,口语等。,以及诸如主要演员、电影时长、口语等项目特征。仍然考虑用户和项目,我们将尝试模拟这样一个事实,即说某种语言的人更有可能观看该语言的电影,或者年龄较大的用户更有可能观看经典电影。为了进行预测,我们查看用户简档并预测评级。

从零开始的推荐系统

原文:https://towardsdatascience.com/recommender-system-from-scratch-fa6b70fc3e7f?source=collection_archive---------5-----------------------

直观的漫游

来源:知识共享

介绍

当我开始我的机器学习之旅时,有几个概念让我非常着迷,其中之一就是推荐系统。这是你在日常生活中经常看到的 ML 应用之一。在亚马逊购物、在 Myntra 买衣服、在网飞看电影的时候,我们到处都有推荐,当然,著名的****Netflix Prize competition,阅读关于比赛、球队以及他们两年来在比赛中的努力让我对推荐系统感到非常兴奋(我建议你们看一下这部关于获胜球队的纪录片)。这篇博客将尝试给出一个从零开始构建一个非常基本的推荐系统的直观演示。****

推荐系统

首先,什么是推荐系统,这是维基百科对推荐系统的定义。

推荐系统是一个信息过滤系统,它试图预测用户对一个项目的“评分”或“偏好”

嗯,这基本上概括了,基于这些预测,系统向顾客建议/推荐相关商品。

概括地说,有三种方法可以用来创建推荐系统

  • 协同过滤(我们将在博客中关注这一点)
  • 基于内容的过滤
  • 混合(以上两者的混合物)

来源:作者图片

协作与内容

协同过滤使用过去的用户-项目交互来提出新的推荐,例如,如果一个人喜欢电影 m1,并且另外 5 个人同时喜欢电影 m1 和电影 m2,则电影 m2 可以被推荐给该用户(具体地说,这是用户-用户协同过滤的一个例子),而基于内容的过滤需要关于用户(如年龄、性别等)和项目/电影(电影的类型、持续时间, etc) 为了生成预测,协作方法的好处在于它们不需要关于用户/项目的额外信息而是从过去的用户-项目交互中提取有用的模式来创建它自己的特征,而不需要任何额外的信息

协同过滤的类型

有两种类型的协同过滤方法

  • 基于内存
  • 基于模型(我们将在这个博客中建立一个基于模型的推荐系统)

基于内存的

基于记忆的协同过滤直接使用用户-项目交互矩阵,并获取相似的用户/项目。

  • 用户-用户相似度:它首先找到与我们给定的查询用户最相似的用户,然后根据最相似的客户评级预测项目偏好,例如,如果我们想预测客户 C 将如何获得电影 m1 的评级,那么我们将对与该用户相似的用户给出的评级进行加权求和,“与你相似的用户也喜欢..”
  • 项目-项目相似度:方法类似于用户-用户相似度,唯一的区别是这里相似度计算是以项目而不是用户为中心的,假设我们想为用户预测电影,那么我们将首先找到用户评分最高的电影,然后找到这部电影与所有其他电影的相似度,并建议前 K 部电影“喜欢这个项目的用户也喜欢..”

对于相似度计算,我们使用皮尔逊相关系数或调整后的余弦相似度(将评分集中在零附近,以避免将未评分的项目视为负面项目,0 表示完全不喜欢)

基于模型的方法和基于记忆的方法之间的主要区别在于,我们不学习任何参数/解决任何优化问题在基于记忆的方法中,仅通过从用户-项目矩阵计算相似性来给出建议。

基于内存的方法的主要问题是它不可扩展,因为用户基数增加,用户-项目矩阵变得更加稀疏,计算相似性变得困难(尤其是计算用户-用户相似性)。

基于模型的

现在我们转到基于模型的方法,但在此之前,让我们讨论一下矩阵分解

矩阵分解将矩阵分解成多个因子,在最大似然术语中,存在一个低维的潜在特征空间,其中我们可以表示项目和用户,用户-项目矩阵的近似可以通过乘以这些用户-项目矩阵来获得。

作为一个例子,让我们假设我们已经将用户-电影矩阵分解为 3 个潜在维度,这不是它的确切含义,但为了给你一个直觉,我们可以将这 3 个潜在向量视为电影类型,如动作片、喜剧片、浪漫片。因此,当我们把一个用户和一部电影分成这三个维度时,我们实际上试图把用户和电影分成这些概念/类型。

********

来源:作者图片

像在上面的例子中,我们已经用他们的潜在维度表示来表示用户、电影,每个维度表示一个类型,这个潜在向量表示帮助**计算用户和项目的交互,并最终帮助推荐项目,**例如,用户 A 更喜欢动作电影,现在如果我们计算用户 A 与所有三部电影的余弦相似性,我们将发现 Singham 具有最高的相似性,因为它在动作类型中具有高值,因此暗示这很可能是一部动作电影。(注:这只是一个获得事物直觉的例子,这些潜在向量不具备这种直接的可解释性)**

代号

理论说够了,让我们谈谈代码吧!我们将构建一个非常简单的基于模型的推荐系统。为了解决机器学习问题(受监督的),我们总是有一个优化问题,一个我们试图最大化/最小化的函数,以在不过度拟合数据的情况下降低成本,并在此过程中通过梯度下降等技术学习我们的模型参数。因此,首先让我们定义我们的优化问题

来源:作者图片

其中预测等级(r_hat)计算如下

来源:作者图片

优化函数可能看起来有点吓人,但是让我们试着一点一点地分解它。

就像任何其他成本函数一样,该成本函数可以分解为两个部分,与第一部分(实际-预测)计算的净成本相关联的,第二项是正则化项。

现在让我们把这些可学习的参数和最终的预测计算搞清楚。

我们可以将预测评级分为四个部分

  • 全局平均值(mu): 全局平均值是我们的训练数据集中所有评级的平均评级,这一术语可以被认为是我们的基线预测评级,并且模型学习对此进行多少调整以获得正确的预测。
  • ****用户偏见:用户偏见可以被认为是一个捕捉用户行为的术语,例如,与其他用户相比,一些用户过于挑剔,因此他们倾向于给出稍低的评级。
  • 电影偏见:电影偏见可以被认为是一个术语捕捉一部电影的受欢迎程度例如,有一些像《阿凡达》、《肖申克的救赎》这样的邪教经典电影通常有很高的评级,因此这些电影往往有更高的推荐评级。
  • ****用户-电影交互:最后,我们有一个术语,它是通过计算相似性 b/w 用户和电影潜在向量,基于特定用户和电影之间的交互来计算的(在上面的部分中解释)

好了,差不多了,我们已经定义了优化函数,现在我们需要最小化成本,在这种情况下,我们将 RMSE 作为我们的性能指标。

数据集

我们采用了一个非常受欢迎的数据集,名为 MovieLens 100k [2],该数据集包括从 1 到 5 的 100k 个评级,来自 1682 部电影的 943 个用户,并基于时间进行了 80-20 的训练测试划分,即使用前 80%的评级进行训练,剩余的 20%用于测试。

我们已经定义了成本函数,现在我们将计算梯度并更新模型参数以最小化成本。

****for** each epoch: **for** each pair of (user, movie): b_i =  b_i - learning_rate * dL/db_i c_j =  c_j - learning_rate * dL/dc_j**

为了简单起见,我们只将 user_biasmovie bias 作为可训练参数,我们使用 SVD 来计算用户和电影向量(你可以将 SVD 视为一种矩阵分解技术)。

这里 b_i 是用户偏差的数组,其中该数组的维数等于我们的训练集中的总用户数(最大用户指数)(每个用户都有偏差),而 c_j 指的是我们的训练集中的总电影数(最大电影指数)。

她的 dl/db_i 指衍生 w.r.t 用户偏差,dl/dc_j 指衍生 w.r.t 电影偏差

首先,让我们设定 RMSE 的上限,通过简单地设定所有预测评级到全球平均评级来计算 RMSE。

来源:作者图片

现在我们知道了上限,即 3.5 左右的任何值都可以作为全局平均值来预测所有值,我们已经为我们的推荐方法定义了定制方法,下面是代码

****def fit_recommender**(total_epochs,learning_rate,train_sparse_matrix,train_df,test_df,svd_components = 5):
    *'''learns parameters for the recommednder'''*
    total_train_mse = []
    total_test_mse = []
    U1, Sigma, V1 = randomized_svd(train_sparse_matrix,      n_components=svd_components,n_iter=2, random_state=24)
    total_train_mse = []
    total_test_mse = []
    **for** epoch in range(total_epochs):
        **for** user,movie,rate in train_df[['user','movie','rating']].values:
            b_i[user] = b_i[user] - learning_rate *  **derivative_db**(user,movie,rate,U1,V1,mu,alpha) 
            c_j[movie] = c_j[movie] - learning_rate *  **derivative_dc**(user,movie,rate,U1,V1,mu,alpha)
        train_error = get_prediction(train_df,b_i,c_j,mu)
        test_error = get_prediction(test_df,b_i,c_j,mu)
        total_train_mse.append(train_error)
        total_test_mse.append(test_error)
        **print**("After Epoch {}------Train rmse:{}  Test rmse:{}".format(epoch,train_error,test_error))
     **print**("=======================================================")**

我们的拟合方法首先使用 truncatedSVD 计算用户电影潜在向量表示(理想情况下,这些用户电影表示也应该在训练过程中学习),然后通过执行梯度下降步骤在每个时期学习用户电影偏差,下面是我们运行定制拟合方法后的结果

来源:作者图片

训练和测试 RMSE 的都比我们的基线模型好,因此我们可以得出结论,模型确实从这些用户和电影偏见形式的数据中学习了一些隐藏的模式

现在你可能会觉得这是一个有点太多的手工工作,难道我们没有像 scikit_learn 这样的东西,我们只需要调用 fit 方法,所有的事情都是自动完成的,那么你将会得到一个惊喜,所有这些以及更多的事情都可以通过这个神奇的库来完成,这个库被称为用于构建和测试推荐系统的惊喜

我们需要建立一个稍微不同的数据集来让惊奇发挥作用

****from** surprise **import** Reader, Dataset
reader = Reader(rating_scale=(1,5))
*# create the traindata from the dataframe...* train_data = Dataset.load_from_df(train_df[['user', 'movie', 'rating']], reader)
*# build the trainset from traindata.., It is of dataset format from s*urprise library..
trainset = train_data.build_full_trainset() # we have to convert testset into list tuples of user,movie,rating
testset = list(zip(test_df.user.values, test_df.movie.values, test_df.rating.values))**

从数据集类中,我们调用 load_from_df 函数,该函数以所需的格式构建训练集,现在让我们构建我们的推荐系统

****def** run_surpise(surprise_algo,trainset):
    svd.fit(trainset)
    train_preds = svd.test(trainset.build_testset())
    train_actual_ratings, train_pred_ratings = **get_ratings**(train_preds)
    train_rmse = **get_errors**(train_preds) 
    test_preds = svd.test(testset)
    test_actual_ratings, test_pred_ratings = get_ratings(test_preds)
    test_rmse = get_errors(test_preds)    
    print("Train RMSE : {}  Test RMSE : {}".format(train_rmse,test_rmse))**

现在,让我们运行我们的惊喜模型,并查看结果(优化函数与我们之前讨论的相同,现在我们正在学习用户、电影偏差以及来自训练集的项目潜在向量,即使用机器学习来执行矩阵分解)

来源:作者图片

不错的训练和测试 RMSE 改进,这只是一种算法,惊喜有许多其他预测算法可以使用请随意阅读官方文档为惊喜和尝试其他,这就像 scikit-learn,只是即插即用。

冷启动问题

在结束之前,我想讨论一下协同过滤方法的一个主要缺点,即冷启动问题。测试集中会有一些用户/电影根本没有出现在训练集中,因此你不能使用协作方法,因为你没有这些电影/用户的任何过去的交互。为了处理冷启动问题,我们可以引入额外的用户级、项目级特征,如用户年龄、电影类型等,基本上合并了基于内容和协作的方法以获得更好的预测。

结论

这个博客的目的是给你一个推荐系统的高层次概述,让你开始。对于那些感兴趣的人,我建议通过在实际数据集上进行尝试来获得一些实践经验。下面是博客中提到的代码的 Github 知识库的链接

资源库链接:https://github . com/ravi 207/re commender system/blob/main/recommender _ system . ipynb

这里有一些我觉得非常有用的好资源。

  • https://www . researchgate . net/publication/223460749 _ The _ big chaos _ Solution _ to _ The _ 网飞 _Grand_Prize
  • https://www.youtube.com/watch?v=UyAfmAZU_WI
  • https://www.youtube.com/watch?v=h9gpufJFF-0

参考文献

[1]https://en.wikipedia.org/wiki/Recommender_system

[2]麦克斯韦·哈珀和约瑟夫·康斯坦。2015.电影镜头数据集:历史和背景。美国计算机学会交互式智能系统汇刊 5,4:19:1–19:19。https://doi.org/10.1145/2827872

推荐系统:商品-顾客协同过滤

原文:https://towardsdatascience.com/recommender-systems-item-customer-collaborative-filtering-ff0c8f41ae8a?source=collection_archive---------1-----------------------

用 Python 代码一步步解释稀疏性、相似性和隐式二元协同过滤

推荐器使用相似性作为距离的补充(作者的图像)

动机:

了解我们的客户应该对哪些商品高度感兴趣,这在许多业务中至关重要。回顾一下我们的 FPGrowth 数据挖掘,我们发现产品经常是相互关联的。因此,如果一个顾客购买了产品 A,那么这个顾客很可能也会购买产品 B,因为这两件商品是相关的(不管出于什么原因,可能就像啤酒和尿布)。由于这个关联规则,我们可以估计客户在一次销售交易中购买的不止一种产品。为了增加一些额外的味道,能够向我们的客户推荐他们自己还不知道他们感兴趣(但他们确实感兴趣)的商品,这不是很棒吗?让我们继续下一步,学习如何用 Python 建立这样一个推荐系统。

解决方案:

我们将从 购物篮 (在销售交易 id 级别上)转移到我们客户的更全面的购买行为(我们客户过去总体购买了哪些商品),建立一个 ML 推荐系统。我们的目标是向我们的客户推荐他们尚未购买但会非常感兴趣的产品(他们自己还不一定知道他们的需求)。这里的诀窍是看看其他类似顾客的购物习惯。由于我们没有任何可用的标记数据来告诉我们哪些客户彼此相似,我们将简单地从他们过去购买的相同商品之间的联系中提取客户之间的相似性。一般来说,相似性是一个非常有趣的指标,用于许多分析目的,而不仅仅与商品-客户推荐相关联(例如,参见我们的聊天机器人中的单词/句子之间的相似性)。在购买历史中寻找相似性,我们预计客户的相似偏好也将延伸到未来。因此,如果客户 A 购买了产品 2、5 和 6(不一定在同一个篮子里),而客户 B 购买了产品 2、5、6 和 9,那么我们可以估计客户 A 也对产品 9 感兴趣。请注意,产品 9 可能与客户 A 以前购买过的产品非常不同,因为我们要看的是客户之间的相似性(而不是商品之间的相似性)。建立在这个概念上的 ML 算法被称为协同过滤** (CF)算法。**

数据管理

首先让我们看看我们的数据并做必要的数据准备(数据和 Jupyter 笔记本都存储在我的 github 库):

import pandas as pd
data = pd.read_excel('/content/gdrive/MyDrive/DDDDFolder/DDDD.xlsx')  
data.head()

这就是我们的数据帧最初的样子。希望它很容易适应您的数据。

因此,我们得到的是每个销售日的销售额、客户(例如,客户代码 0 是一个特定的客户)和销售项目(类似于客户,销售项目 0 是一个特定的销售项目)。其他栏目与我们的产品推荐无关,因此我们将删除这些栏目:

DataPrep = data[[‘SalesItem’, ‘SalesAmount’, ‘Customer’]] 
DataPrep.head()

我们将只使用销售项目、销售金额和客户作为我们推荐人的基础。

用熊猫的。info 函数我们看到在我们的 341422 个数据行中不存在 NaN(不是数字)值(这是一件好事):

现在我们想使用 groupby 计算出每个客户购买了哪些商品:

DataGrouped = DataPrep.groupby([‘Customer’,‘SalesItem’]).sum().reset_index() 
DataGrouped.head()

由于 Panda 的 Groupby 功能,每个客户和商品一行。

稀疏比密集好(Python 的禅)

我们的数据是这样的:36 个不同的客户(从 0 到 35)和 3751 个不同的商品。有趣的问题是,我们的顾客实际上购买了多少不同的商品?我们将使用 Numpy 的。用于获取不同数量的客户和商品的销售额的列表函数:

import numpy as np 
customers = list(np.sort(DataGrouped.Customer.unique()))
# a list of values, so customers now stores 36 unique customersproducts = list(DataGrouped.SalesItem.unique()) 
quantity = list(DataGrouped.SalesAmount)

使用 Panda 的数据帧,我们可以将 Numpy 的列表聚合回 Panda 的数据帧:

from pandas import DataFrame 
DfCustomerUnique = DataFrame(customers,columns=[‘Customer’]) DfCustomerUnique.head()

DfCustomerUnique 在一个数据框架中包含所有唯一的客户

现在我们可以构建稀疏矩阵:

from scipy import sparse 
from pandas.api.types import CategoricalDtype 
rows = DataGrouped.Customer.astype(CategoricalDtype(categories=customers)).cat.codes 
# We have got 35 unique customers, which make up 13837 data rows (index) 
# Get the associated row indices 
cols = DataGrouped.SalesItem.astype(CategoricalDtype(categories= products)).cat.codes 
# We have got unique 3725 SalesItems, making up 13837 data rows (index) 
# Get the associated column indices 
#Compressed Sparse Row matrix 
PurchaseSparse = sparse.csr_matrix((quantity, (rows, cols)), shape=(len(customers), len(products))) 
#len of customers=35, len of products=3725 
#csr_matrix((data, (row_ind, col_ind)), [shape=(M, N)]) 
#where data, row_ind and col_ind satisfy the relationship a[row_ind[k], col_ind[k]] = data[k]. , see [3]
PurchaseSparse

稀疏矩阵(不是熊猫 df)对于行切片和快速矩阵矢量积是有效的

我们总共有 35 个客户和 3725 件商品。对于这些用户/项目交互,这些项目中的 13837 个实际上有购买。根据矩阵的稀疏性,这使得:

MatrixSize = PurchaseSparse.shape[0]*PurchaseSparse.shape[1] # 130375 possible interactions in the matrix (35 unique customers * 3725 unique SalesItems=130375)PurchaseAmount = len(PurchaseSparse.nonzero()[0]) # 13837 SalesItems interacted with;sparsity = 100*(1 - (PurchaseAmount/MatrixSize))sparsity

由于我们将使用矩阵分解进行协作过滤,89.3%的交互矩阵稀疏应该不成问题。简单地说,在我们的案例中,89.3%意味着只有 10.7%的客户-商品互动已经完成,这意味着大多数商品还没有被客户购买。据说协同过滤甚至可以很好地处理更稀疏的数据。当我们最终检查我们的体面推荐时,我们可以证明它是有效的。余弦相似度对于稀疏数据来说是一个很好的度量,所以我们将坚持余弦(而不是皮尔逊、欧几里德、曼哈顿等。).

二进制数据与销售额

我们的协同过滤将基于二进制数据(一组只有两个值的数据),这是分类数据的一个重要特例。对于每个数据集,我们将在购买时添加 1。这意味着,该客户已经购买了该商品,不管该客户过去实际购买了多少。我们决定在推荐的例子中使用这种二进制数据方法。另一种方法是使用 SalesAmount 并将其规范化,以防您希望将购买的 SalesItems 的数量视为一种口味因素,这意味着某人购买了 SalesItem x 100 次——而另一个客户只购买了相同的 sales item x 5 次——并不喜欢它。我相信,在销售建议中,二元方法通常更有意义,但当然这真的取决于你的数据。

def create_DataBinary(DataGrouped):
DataBinary = DataGrouped.copy()DataBinary['PurchasedYes'] = 1
return DataBinary
DataBinary = create_DataBinary(DataGrouped)
DataBinary.head()

最后,让我们去掉 SalesAmount 列:

purchase_data=DataBinary.drop(['SalesAmount'], axis=1)
purchase_data.head()

这是我们为提出建议而准备的现成数据。

协同过滤

为了进行协同过滤,我们现在可以看看胡一帆、耶胡达·科伦和克里斯·沃林斯基的论文。据推测,脸书和 Spotify 基本上也使用本文描述的方法。你读过吗?你理解了吗,知道怎么把这个放进 Python 代码里吗?老实说,第一个问题我可以说是,但第二个问题肯定不行。如果你也有类似的回答,为了更好地理解,我很荣幸你能一步步地和我一起经历这一切。

CF 是研究和实现最广泛的推荐算法之一(根据 Aberger [ 2 ])。CF 是一种 ML 算法,它从许多客户(协作,因为它正在组合收集的信息)收集偏好或口味信息,并使用这些信息对其他客户的兴趣进行自动预测(过滤掉可能性较小的选项,以找到可能性最大的预测)。当然,我们只会推荐顾客尚未购买的商品。

显式比隐式好(Python 的禅)

..但是含蓄总比没有强。使用 implicit,我们无法真正判断我们的客户是否喜欢某个特定的产品。我们可以判断出顾客确实购买了特定的商品。此外,这些信息还可以细分为客户是否购买了某个特定的商品(二进制,是或否)或客户购买了该商品多少次(作为一种品味因素:该商品购买的次数越多,它似乎就越相关)。我们将在二进制水平上推荐客户购买的销售量(购买是或否),因为我相信这与我们大多数读者最相关。我认为,在现实生活中,我们往往只能得到隐性反馈。隐性反馈,如购买的商品数量、网站点击量、观看的电影等。不是明确的反馈(“我喜欢”等)。所以有可能即使我们买了某本书,我们在阅读的时候并不真正喜欢它。尽管这是隐式反馈的一个缺点,但我们通常没有明确的客户反馈,所以我们必须坚持隐式反馈。此外,如果隐式反馈的数量超过了显式反馈的数量,我们可能会决定在隐式数据上构建我们的推荐引擎(有时大量数据会支配较少的数据,即使数据质量更好)。我们寻找顾客之间的联系,而不是产品之间的联系。因此,协同过滤仅依赖于观察到的客户行为来做出推荐,而不需要关于客户或项目的信息。

一句话:我们的建议是基于这样一个假设,即购买了相似数量产品的客户共享一个或多个隐藏的偏好。由于这种共有的潜在的隐藏的特征,顾客很可能会购买类似的产品。

相似性度量

我们如何判断一个客户与另一个客户的相似程度?或者一个物品和其他物品的距离有多近?要回答这个问题,说明相似性方法是对距离的补充是有帮助的。虽然两点之间的距离可以使用欧几里德距离(通过计算连接两点的直线的长度)来获得,但相似性是关于点“有多接近”。相似性值通常表示为 0 到 1 之间的数字,其中 0 表示低相似性,1 表示高相似性。让我们把它分解成简单的 Google Sheets/ Microsoft Excel 公式(下面是使用 Google Sheets 的表单截图,你也可以在我的 Github 库中找到):

假设我们的 9 个客户购买了这 7 个销售项目,如下所示(1 表示已购买,0 表示未购买),则“PurchaseHistory”选项卡如下所示:

例如,顾客 3 曾经购买过销售物品 2。

“PurchHistNorm”选项卡中的标准化数据如下所示:

二进制数据的缩放有意义吗?这个稍后会回答。

..因为例如对于客户 1,为了标准化,我们必须检查幅度:

因此,最终项目-项目相似性矩阵看起来像这样:

当然,一个产品总是与其自身相关度最高(销售项目 1 与销售项目 1 相关度最高,得分为 1)。我希望这个简单的电子表格示例能够帮助您了解建立项目-项目相似性背后的基本思想。

使用哪种相似性度量?

如果数据存在等级膨胀(不同的客户可能使用不同的量表),建议使用 Pearson。如果我们的数据很密集(几乎所有属性都有非零值)并且属性值的大小很重要,则使用距离度量,如欧氏距离或曼哈顿距离。但是对于我们的稀疏单变量数据的例子,我们将坚持余弦相似性。

Python 中的项目-客户矩阵

现在,我们将构建商品-客户矩阵。为了更方便,我们在每个 SalesItem 前添加 I 作为前缀。否则,我们将只有客户和销售项目编号,这可能会变得有点令人费解:

purchase_data[‘SalesItem’] = ‘I’ + purchase_data[‘SalesItem’].astype(str) 
DfMatrix = pd.pivot_table(purchase_data, values=’PurchasedYes’, index=’Customer’, columns=’SalesItem’)
DfMatrix.head()

因为我们只使用 1 和 0,所以我们不需要考虑标准化,这是一种缩放方法。标准化将数据的范围缩小到从 0 到 1:

X normalized = X Xmin/(Xmax Xmin)

另一种形式的缩放是标准化,它将数据的缩放比例缩小到平均值(μ)为 0,标准差(σ)为 1:

X standardized = Xμ/σ
但是空谈是没有用的,让我们来看看,即使我们归一化,结果当然是一样的:

DfMatrix=DfMatrix.fillna(0) 
#NaN values need to get replaced by 0, meaning they have not been purchased yet.DfMatrixNorm3 = (DfMatrix-DfMatrix.min())/(DfMatrix.max()-DfMatrix.min())DfMatrixNorm3.head()

我们需要通过 reset_index 和 rename_axis 将数据透视表转换成所需的格式:

DfResetted = DfMatrix.reset_index().rename_axis(None, axis=1) DfResetted.head() 
#Now each row represents one customer`s buying behaviour: 1 means the customer has purchased, NaN the customer has not yet purchased it

DfMatrix.shape

客户必须是 nvarchar!如果客户是 int,则在客户相似期间失败!

df=DfResetted 

我们需要用 0 替换 NaN 值,因为我们的函数不能处理 NaN 值。请注意,我们只是检查一个特定的客户是否购买了一个特定的项目,是或否。这就是所谓的二进制。如果客户购买了特定的商品,这意味着 1。如果不是,则为 0。由于这个二进制问题,使用任何进一步的缩放技术都没有用。

df=df.fillna(0) 
df.head()

现在我们创建一个只包含销售项目的数据框架,而客户则被编入索引。

DfSalesItem = df.drop(‘Customer’, 1) 
DfSalesItem.head()

因此,我们现在可以计算基于项目的推荐。出于比较原因,我们将标准化数据帧:

import numpy as np
DfSalesItemNorm = DfSalesItem/np.sqrt(np.square(DfSalesItem).sum(axis=0))   DfSalesItemNorm.head()

正在计算。计算余弦相似度的点矢量:

ItemItemSim = DfSalesItemNorm.transpose().dot(DfSalesItemNorm) ItemItemSim.head()

如果要导出此结果,请使用 item item sim . to _ excel(" export item-item . xlsx ")。

为项目的最近邻居创建占位符项目

ItemNeighbours = pd.DataFrame(index=ItemItemSim.columns,columns=range(1,10))
ItemNeighbours.head()

为项目的近邻创建占位符项目

循环浏览我们的相似性数据框架,并填写相邻的项目名称:

for i in range(0,len(ItemItemSim.columns)): ItemNeighbours.iloc[i,:9] = ItemItemSim.iloc[0:,i].sort_values(ascending=False)[:9].index #we only have 9 items, so we can max recommend 9 items (itself included) ItemNeighbours.head()

您可以看到第一列存储了项目本身。

ItemNeighbours.head().iloc[:11,1:9]
#it needs to start at position 1, because position 0 is item itself

现在我们去掉了第 1 列,它只是商品本身。

# You might want to export this result to Excel now
ItemNeighbours.to_excel(“ExportItem-Item-data_neighbours.xlsx”)

现在,我们将创建一个基于客户的推荐,我们需要我们的项目相似性矩阵。然后,我们将查看我们的客户购买了哪些商品,并获得每个商品的前 N 名邻居。之后,我们为每个邻居计算顾客的购买历史,并为他们计算相似性得分。所以最后我们只需要推荐得分最高的项目。因此,我们将创建一个相似性占位符矩阵,并填写客户列:

CustItemSimilarity = pd.DataFrame(index=df.index,columns=df.columns) CustItemSimilarity.iloc[:,:1] = df.iloc[:,:1] CustItemSimilarity.head()

最后,我们将计算相似性得分

def getScore(history, similarities):return sum(history*similarities)/sum(similarities)

这需要很长时间(35 个客户* 3725 个项目),因为我们现在循环遍历行和列,用相似性分数填充空白空间(在这里找到一个改进的解决方案)。请注意,我们将顾客已经消费过的商品评分为 0,因为没有必要再次推荐这些商品。

from timeit import default_timer as timer 
#to see how long the computation will take 
start = timer() for i in range(0,len(CustItemSimilarity.index)): for j in range(1,len(CustItemSimilarity.columns)): user = CustItemSimilarity.index[i] product = CustItemSimilarity.columns[j]if df.loc[i][j] == 1: CustItemSimilarity.loc[i][j] = 0 else: ItemTop = ItemNeighbours.loc[product][1:9] 
ItemTopSimilarity = ItemItemSim.loc[product].sort_values(ascending=False)[1:9] 
#here we will use the item dataframe, which we generated during item-item matrix 
CustomerPurchasings = DfSalesItem.loc[user,ItemTop] CustItemSimilarity.loc[i][j] = getScore(CustomerPurchasings,ItemTopSimilarity) end = timer() print(‘\nRuntime: %0.2fs’ % (end — start)) 
CustItemSimilarity.head()

如果出现一个奇怪的错误 tz=getattr(series.dtype,' tz ',None)..熊猫指数..那么这可能是因为使用 int #而不是 string 作为列标题

现在,我们将生成一个基于客户的建议矩阵,如下所示:

# Get the top SalesItems 
CustItemRecommend = pd.DataFrame(index=CustItemSimilarity.index, columns=[‘Customer’,’1',’2',’3',’4',’5',’6']) #Top 1,2..6 CustItemRecommend.head()

我们希望看到产品名称,而不是用相似性分数填充矩阵。因此循环:

for i in range(0,len(CustItemSimilarity.index)): CustItemRecommend.iloc[i,1:] = CustItemSimilarity.iloc[i,:].sort_values(ascending=False).iloc[1:7,].index.transpose() 
CustItemRecommend.head()

客户 0 的第一项建议是项目 1134,项目 999 是第二项,项目 2128 是第三项,依此类推。

CustItemRecommend.to_excel(“ExportCustomer-Item-CustItemRecommend.xlsx”) 

恭喜你,我们已经编码了一个二进制推荐引擎,它在一个小数据集上就足够了。让我们看看是否可以使用另一个故事中的相同数据集来增强性能和可伸缩性。你可以在我的 Github 中找到 Jupyter 笔记本和 Excel 的例子。

非常感谢您的阅读!希望这篇文章对你有帮助。请随时在 LinkedIn 、 Twitter 或工作室与我联系。

https://jesko-rehberg.medium.com/membership

如果你想推荐使用 Scikit-Learn 或 Tensorflow 推荐器,你可能会发现这篇文章很有帮助:

最初发表在我的网站 DAR-Analytics 。

参考:

[1]http://yifanhu.net/PUB/cf.pdf胡一帆、耶胡达·科伦和克里斯·沃林斯基,《隐式反馈数据集的协同过滤》

[2] Christopher R. Aberger,Recommender:a Analysis of Collaborative Filtering Techniques:https://www . semantic scholar . org/paper/Recommender-% 3A-An-Analysis-of-Collaborative-aber ger-caberger/53bd 928 ce aab 4572594590970 b 877 da 1278 b7a 79

[3] scipy.sparse.csr_matrix,https://docs . scipy . org/doc/scipy/reference/generated/scipy . sparse . CSR _ matrix . html

利用 U-Net 从强引力透镜图像重建源星系图像

原文:https://towardsdatascience.com/reconstruct-source-galaxy-images-from-strong-gravitational-lens-images-using-u-net-1d8221100601?source=collection_archive---------30-----------------------

如何生成遥远星系的无透镜图像

引力使遥远星系的光线弯曲,这使得它们看起来像透镜。下面的图像向我们展示了来自遥远类星体的光如何由于透镜星系的强大引力而弯曲,透镜星系的作用类似于类星体(源)和观察者之间的光学透镜。这产生了一个扭曲的类星体图像,显示在图像的右侧。

强引力透镜的简单说明 | 作者图,星系图像:曼德尔鲍姆、蕾切尔、莱克纳、克莱尔、利奥特、亚历克西、&罗、巴纳比。(2012).宇宙真实星系数据集。芝诺多。http://doi.org/10.5281/zenodo.3242143

这个项目试图重建被引力透镜扭曲的遥远光源的原始图像。

使用卷积神经网络、 U-Net 和带有结构相似性指数的均方误差,我们能够生成这些星系的无透镜图像。即消除强引力透镜的影响,生成源图像。此外,我们能够预测导致这种扭曲的强引力透镜的透镜参数。我们致力于开发一种算法来消除这种视觉效应,以便我们可以检测其他可能的未识别引力透镜。即重建源星系的图像。

解除强引力透镜效应| 作者图,星系图像:曼德尔鲍姆、蕾切尔、拉克纳、克莱尔、莱奥陶德、亚历克西、&罗、巴纳比。(2012).宇宙真实星系数据集。芝诺多。http://doi.org/10.5281/zenodo.3242143

资料组

由于没有多少强引力透镜的图像可用,我们决定使用宇宙数据集来模拟数据。Cosmos 数据集有 60K 个星系图像。我们利用红移特性过滤出星系,用于我们的项目。过滤星系后,我们使用透镜体和随机生成的透镜参数来模拟源星系的透镜图像。我们还使用旋转数据增强来生成更多的模拟数据。我们的数据集由生成的透镜图像和它的源真实星系对组成。这些图像是 64x64 灰度图像。基于 galaxy 独特的源 id,它们被分布到测试、验证和训练数据集,以避免数据集之间的数据泄漏。透镜图像用于预测星系引力透镜的透镜参数。给定透镜图像,无透镜图像是用于预测无透镜星系的同一星系的正确描述。

数据增强创建模拟强引力透镜数据|作者图, 星系图像:曼德尔鲍姆、雷切尔、拉克纳、克莱尔、利乌托德、亚历克西、&罗、巴纳比。(2012).宇宙真实星系数据集。芝诺多。http://doi.org/10.5281/zenodo.3242143

方法

我们把问题分成两步。

首先,预测 8 个透镜参数:

  • 爱因斯坦半径(θ_ E)
  • 透镜的幂律质量分布指数(伽马)
  • 透镜的椭圆度分量(e1,e2)
  • 镜头在图像中的位置(center_x,center_y ),单位为弧秒
  • 透镜图像的外部剪切分量(γ1,γ2)。

其次,U-net 从它的透镜对图像生成无透镜源星系图像。

架构第 1 部分:预测透镜参数

预测透镜参数|作者图表,银河影像:曼德尔鲍姆,瑞秋,拉克纳,克莱尔,利奥特,亚历克西,&罗,巴纳比。(2012).宇宙真实星系数据集。芝诺多。http://doi.org/10.5281/zenodo.3242143

在我们预处理我们的图像之后,我们在具有三个块的卷积神经网络中使用我们的训练数据集,使用相应的透镜参数作为最后一层的输出,这产生了我们的第一部分的模型。在每个模块中,我们应用了 2D 卷积、最大池化、批量标准化和一个 Relu 激活函数。最后,我们将这些层展平,并将输入传递到两个密集的层中,一个是 Relu,另一个是线性函数。我们将所有 8 个要预测的参数指定为每个数据集中的标签,然后对数据集中的所有图像运行该函数。它能够跨所有三个数据集以良好的度量来预测透镜化参数。

第 1 部分的超参数:我们调整了 Adam 的学习速率、CNN 的层数、小批量和纪元。

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3),input_shape=(64, 64, 1),use_bias=**False**)) 
model.add(layers.MaxPooling2D((2, 2))) model.add(layers.BatchNormalization()) model.add(layers.Activation("relu"))
model.add(layers.Conv2D(64, (3, 3) ,use_bias=**False**)) model.add(layers.MaxPooling2D((2, 2))) model.add(layers.BatchNormalization()) model.add(layers.Activation("relu"))
model.add(layers.Conv2D(64, (3, 3),use_bias=**False**)) model.add(layers.MaxPooling2D((2, 2))) model.add(layers.BatchNormalization()) model.add(layers.Activation("relu")) model.add(layers.Flatten()) model.add(layers.Dense(64, activation='relu')) model.add(layers.Dense(8,activation='linear'))

CNN 预测透镜参数

测试集的每个透镜参数类的“均方根误差”输出的度量:

每个输出类参数的 RMS,作者的图表,

这表明θE(爱因斯坦半径)比透镜中心坐标和γ更容易预测,但比透镜椭圆度分量(e1,e2)更难预测。

架构第 2 部分:生成源星系图像

使用 U-Net 将有透镜的图像转换为无透镜的源星系图像|作者的图表, 星系图像:曼德尔鲍姆、雷切尔、拉克纳、克莱尔、利乌托德、亚历克西、&罗、巴纳比。(2012).宇宙真实星系数据集。芝诺多。http://doi.org/10.5281/zenodo.3242143

在第二部分中,我们将一张有透镜的图像转换成一张无透镜的源星系图像。为此,我们使用我们的 U-net 编码器/解码器网络和图像相似性作为我们的误差/损失函数。

U-Net 实验和超参数调谐

我们开始了一个简单的 U-Net ,它具有多个 Conv2D、最大池、转置卷积层,以及带有 MSE 损耗和 Adam 优化器的最后一层的线性激活。之后,我们在 U-Net 中添加了更多的隐藏块,提高了它的性能。然后我们实验了不同的激活函数:relu,elu 等。Relu 为我们提供了更好的中间层结果。然后,我们加入了对结果没有太大改善的辍学者,所以把它去掉了。此外,添加批处理规范化有助于 U-Net。仍然没有从人工误差分析中为螺旋星系生成更好的图像,所以我们决定使用不同的图像相似性损失函数。

初始模型

MSE 生成正确形状的中间模型**|星系图像**:曼德尔鲍姆、蕾切尔、莱克纳、克莱尔、利奥特、亚历克西、&罗、巴纳比。(2012).宇宙真实星系数据集。芝诺多。【http://doi.org/10.5281/zenodo.3242143

指标和自定义损失函数:

我们用图像结构相似性 SSIM 指数、峰值信噪比(PSNR)和均方误差(MSE)作为损失函数进行实验。SSIM 给了我们更好的结果。我们通过编写一个结合了 SSIM 和 MSE 的自定义损失函数来进一步改进我们的结果。

具有定制损耗功能的 U-Net

inputs = Input((64, 64, 1))s = Lambda(lambda x: x / 255) (inputs)
c1 = Conv2D(16, (3, 3),  padding='same',use_bias=False) (s)c1 = BatchNormalization()(c1)
c1 = Activation('relu')(c1)
c1 = Conv2D(16, (3, 3), padding='same',use_bias=False) (c1)
c1 = BatchNormalization()(c1)
c1 = Activation('relu')(c1)
p1 = MaxPooling2D((2, 2)) (c1)c2 = Conv2D(32, (3, 3),  padding='same',use_bias=False) (p1)
c2 = BatchNormalization()(c2)
c2 = Activation('relu')(c2)
c2 = Conv2D(32, (3, 3), padding='same',use_bias=False) (c2)
c2 = BatchNormalization()(c2)
c2 = Activation('relu')(c2)
p2 = MaxPooling2D((2, 2)) (c2)c3 = Conv2D(64, (3, 3), padding='same',use_bias=False) (p2)
c3 = BatchNormalization()(c3)
c3 = Activation('relu')(c3)
c3 = Conv2D(64, (3, 3), padding='same',use_bias=False) (c3)
c3 = BatchNormalization()(c3)
c3 = Activation('relu')(c3)
c3 = Conv2D(64, (3, 3), padding='same',use_bias=False) (c3)
c3 = BatchNormalization()(c3)
c3 = Activation('relu')(c3)
p3 = MaxPooling2D((2, 2)) (c3)c4 = Conv2D(128, (3, 3), padding='same', use_bias=False) (p3)
c4 = BatchNormalization()(c4)
c4 = Activation('relu')(c4)
c4 = Conv2D(128, (3, 3), padding='same',use_bias=False) (c4)
c4 = BatchNormalization()(c4)
c4 = Activation('relu')(c4)
p4 = MaxPooling2D(pool_size=(2, 2)) (c4)c5 = Conv2D(256, (3, 3), padding='same',use_bias=False) (p4)
c5 = BatchNormalization()(c5)
c5 = Activation('relu')(c5)
c5 = Conv2D(256, (3, 3),  padding='same',use_bias=False) (c5)
c5 = BatchNormalization()(c5)
c5 = Activation('relu')(c5)u6 = Conv2DTranspose(128, (2, 2), strides=(2, 2), padding='same') (c5)
u6 = concatenate([u6, c4])
c6 = Conv2D(128, (3, 3),  padding='same',use_bias=False) (u6)
c6 = BatchNormalization()(c6)
c6 = Activation('relu')(c6)
c6 = Conv2D(128, (3, 3),  padding='same',use_bias=False) (c6)
c6 = BatchNormalization()(c6)
c6 = Activation('relu')(c6)u7 = Conv2DTranspose(64, (2, 2), strides=(2, 2), padding='same') (c6)
u7 = concatenate([u7, c3])
c7 = Conv2D(64, (3, 3),  padding='same',use_bias=False) (u7)
c7 = BatchNormalization()(c7)
c7 = Activation('relu')(c7)
c7 = Conv2D(64, (3, 3),  padding='same',use_bias=False) (c7)
c7 = BatchNormalization()(c7)
c7 = Activation('relu')(c7)
c7 = Conv2D(64, (3, 3),  padding='same',use_bias=False) (c7)
c7 = BatchNormalization()(c7)
c7 = Activation('relu')(c7)u8 = Conv2DTranspose(32, (2, 2), strides=(2, 2), padding='same') (c7)
u8 = concatenate([u8, c2])
c8 = Conv2D(32, (3, 3), padding='same',use_bias=False) (u8)
c8 = BatchNormalization()(c8)
c8 = Activation('relu')(c8)
c8 = Conv2D(32, (3, 3), padding='same',use_bias=False) (c8)
c8 = BatchNormalization()(c8)
c8 = Activation('relu')(c8)u9 = Conv2DTranspose(16, (2, 2), strides=(2, 2), padding='same') (c8)
u9 = concatenate([u9, c1], axis=3)
c9 = Conv2D(16, (3, 3),  padding='same',use_bias=False) (u9)
c9 = BatchNormalization()(c9)
c9 = Activation('relu')(c9)
c9 = Conv2D(16, (3, 3), padding='same',use_bias=False) (c9)
c9 = BatchNormalization()(c9)
c9 = Activation('relu')(c9)out = Conv2D(1, (1, 1), use_bias=False) (c9)
out = BatchNormalization()(out)
outputs = Activation('linear')(out)# Optimizer
optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)model = Model(inputs=[inputs], outputs=[outputs])model.compile(loss=mse_ssim_loss, optimizer=optimizer, metrics=['mse',ssim_loss,psnr_loss])model.summary()

结合了 SSIM 和均方误差的定制损失函数

def mse_ssim_loss(y_true, y_pred):
return tf.reduce_mean(tf.math.squared_difference(y_true, y_pred)) - tf.reduce_mean(tf.image.ssim(y_true, y_pred, 1.0))

这个损失函数也为螺旋星系带来了更好的结果。

val _ MSE:0.0023—val _ ssim _ loss:0.0468—val _ PSNR _ loss:30.6308。损失曲线。

**测试集改进模型的 U-Net 结果:**更多结果如下

改进后的模型还带有自定义损失函数生成图案| 银河影像:曼德尔鲍姆、蕾切尔、莱克纳、克莱尔、利奥特、亚历克西、&罗、巴纳比。(2012).宇宙真实星系数据集。芝诺多。http://doi.org/10.5281/zenodo.3242143

见解和未来工作:

我们试图从我们的训练模型中为来自互联网的原始强引力透镜图像生成一个源,但我们需要对这些数据进行更多的预处理和噪声消除。我们也将尝试高斯噪声消除以及增加更多的数据(基于数据的方法)。我们还将对 RGB 图像进行实验,并继续完善这两个部分的模型。这样我们就可以更接近预测这些源星系的实际样子。此外,我们将公开模拟数据。

贡献

Git 储存库:从强引力透镜图像生成源星系图像

数据:透镜数据

承认

我和我的团队成员 Anand Bhavsar 要感谢我们的导师 Jelle Aalbers 先生,他是斯坦福大学的 Kavli 博士后,天体物理学领域的专家,帮助我们建立数据,为我们建立模型提供指导和建议,并对我们的测试结果给予反馈。

更多结果:

样本输出图像| 星系图像:曼德尔鲍姆、蕾切尔、拉克纳、克莱尔、利乌托德、亚历克西、&罗、巴纳比。(2012).宇宙真实星系数据集。芝诺多。http://doi.org/10.5281/zenodo.3242143

参考资料:

https://en.wikipedia.org/wiki/Strong_gravitational_lensing

宇宙真实星系数据集

https://www.nature.com/articles/nature23463 https://github.com/sibirrer/lenstronomy

结构相似度 (SSIM)

峰值信噪比 (PSNR)

https://kipac.stanford.edu/ http://spiff.rit.edu/classes/phys240/lectures/grav_lens/grav_lens.html https://naokishibuya.medium.com/up-sampling-with-transposed-convolution-9ae4f2df52d0 https://up42.com/blog/tech/image-similarity-measures https://www.tensorflow.org/tutorials/images/ https://www.nature.com/articles/nature23463 https://hubblesite.org/resource-gallery/images https://www.freepik.com https://www.flaticon.com/

重建传奇的佛罗里达人的年代

原文:https://towardsdatascience.com/reconstructing-the-year-of-the-legendary-florida-man-1aae9b7ed85e?source=collection_archive---------48-----------------------

一个用 Python 编写的数据科学微型项目,用于编辑每日头条

迈阿密海滩,佛罗里达人的潜在地点。图片由塔蒙通过皮克斯贝拍摄

你可能知道迷因。谷歌一下*‘佛罗里达人’+你的生日*,最奇怪的搜索结果就蹦了出来。一年中的每一天,这位神秘的佛罗里达男子似乎都在尝试比前一天更奇特、更暴力、更悲惨或更离奇的事情。通常,标题是如此的华而不实,以至于不禁让你会心一笑。

在谷歌上搜索佛罗里达人在你生日那天做了什么很有趣,但是你一定很好奇他在这一年的其他时间都做了什么(至少我是这样)。谷歌搜索 366 天有点太多了——尽管这可能比为它编写脚本更快——所以让我们试着用 Python 来实现自动化吧!我们执行了一个数据科学微型项目,涉足了一些云供应、web 抓取、多线程、字符串操作、正则表达式和单词云。

数据科学管道

对于任何数据科学项目(甚至是一个微型项目),我发现利用加州大学圣地亚哥分校[1]概述的数据科学管道是有用的。它包含涵盖整个项目的五个阶段:

  • 获取:使用 Google 定制搜索引擎和 Google CSE JSON APIs 来检索查询数据。
  • 准备:可视化原始输出,从查询结果中过滤出标题。
  • 分析:用一组禁忌词和相似性检查去掉不合适的标题。使用字符串操作和正则表达式产生统一样式的输出。
  • 报表:显示样本输出,可视化单词云中的常用单词。
  • 行为:根据项目撰写并发表一篇媒体文章。

数据科学管道(作者自己的作品,灵感来自[1])

设置 Google 自定义搜索

对于抓取谷歌的定制搜索引擎——在他们的常规搜索引擎上的自动请求是不允许的——我们使用谷歌定制搜索 API 。这个 API 每天提供多达 100 个免费查询,以 JSON 格式返回输出。使用 Python,我们为每天创建一个查询,并将它们传递给 Google 定制搜索引擎 JSON API,以提取每个搜索的头条。关于创建和配置 API 和谷歌搜索定制引擎的更多信息,请查看我的教程用 Python 抓取谷歌——一步一步的教程。在这个项目中,我们配置搜索引擎,使其在中包含来自美国的结果。com 域。

https://medium.com/codex/scraping-google-with-python-a-step-by-step-tutorial-11b38dd5691c [## 用 Python 抓取 Google 一步一步的教程

medium.com](https://medium.com/codex/scraping-google-with-python-a-step-by-step-tutorial-11b38dd5691c)

生成数据查询

一旦自定义搜索引擎建立起来,我们就会创建一个包含 366 个生日的列表(是的,包括那个讨厌的 2 月 29 日)。2020 年是闰年,所以如果我们取那一年的所有日期(从*“1 月 1 日”*到“ 31 日-12 月”),我们就被覆盖了:

现在我们简单地将每个日期与*‘佛罗里达人’*连接起来,以有序的方式生成 366 个搜索查询:search_term_list = [‘Florida Man‘ + date for date in date_list]

准备好我们的查询列表后,我们就可以开始讨论了!

Denys Kostyuchenko 在 Unsplash 上的照片

运行查询

嗯,差不多准备好了。正如您所记得的,我们每天只能运行 100 个请求,我们绝对不想在这个宠物项目上花费四天时间。幸运的是,我们可以在一个帐户下提供多达 12 个 API,因此我们可以简单地创建四个副本,并通过切换键对每个副本依次运行 92 个查询。

让我们看看我们是否能做得更好!如果我们无论如何都必须将工作负载分布在四个独立的 API 上,我们还不如做一点多线程。很可能它不会为我们节省大量的时间,但是当我们处理一个需要大量等待时间的输入输出过程时,我们应该正确的处理它。

好了,现在我们准备隆隆作响。

让我们运行一个查询,并检查单个结果的外观。即使是prettyprint也不能让它变得漂亮,我们有 3660 个这样的输出。幸运的是,我们只对标题感兴趣。让我们看看我们的信息实际上需要什么,并做一些数据清理。

搜索查询的 JSON 输出示例

我们遇到的主要问题是,谷歌搜索页面上显示的真实标题由于篇幅原因往往不完整。*佛罗里达州一名男子在东圣路易斯将马桶扔出窗外……’*毫无疑问,这是一个扣人心弦的故事,但点击阅读全文正是我们试图避免的。我们选择的解决方案是改为读取字段og:title,它包含完整的标题。og代表 Open Graph protocol,这是一种数据格式,旨在使社交媒体之间的共享更容易。不幸的是,并不是每个网站都包括它,所以我们放弃了这些结果。尽管如此,仍然有大量的原始资料。

现在我们有了一本只有全称的字典;事情越来越有可读性了。与此同时,我们也注意到许多标题并不完全是我们想要的。是时候进行更深入的分析了。

瑞安·斯潘塞在 Unsplash 上的照片

数据分析

首先,我们要重新格式化标题。许多标题只包含小写单词,一个标题使用双引号,而另一个使用单引号,一些媒体在标题中发布自己的名字……我们需要一个漂亮、统一的格式,在标题中的某些模式上使用字符串操作和正则表达式,并相应地更新。一些例子:

在检查哪些标题是我们真正想要保留的之前对其进行格式化可能并不是最有效的方法,但是在过滤的时候让标题具有统一的格式肯定是有帮助的。此外,速度并不是这个脚本的关键目标。

已经很好地格式化了标题,让我们看看我们想要删除哪些。由于佛罗里达男子在流行文化中获得了相当大的吸引力,许多文章都是关于现象而不是他本人。在我们的概述中,我们不希望看到这样的标题污染我们的结果:

— 90 天本年度最狂野佛罗里达男子头条(至今)
—佛罗里达男子 2 月 28 日(2/28) //你是哪个佛罗里达男子?#弗洛里达曼
——“弗洛里达曼”挑战成为病毒式轰动:你试过吗?

为了删除上述结果,我们创建了一个禁忌集,以排除任何包含以下单词的标题:如“#、?”、月份以及社交媒体平台,例如“*Twitter”*和“脸书”。大写和小写变体也被考虑。这是一个有点反复试验的过程,需要几次迭代来检查是否所有的头条都符合预期的结果。此外,我们希望“*佛罗里达人”*成为每一个标题的前两个词,这样我们就能得到一个有凝聚力的列表。有关部署禁忌集的示例,请查看下面的gist

我们正在取得进展。现在我们有了一套不错的标题,我们遇到的最后一个问题是重复。相似(甚至相同)的标题往往会连续几天成为新闻,例如:

一名打扮成弗雷德·燧石族的佛罗里达男子因驾驶“步兵车”超速行驶而被拦下

为了消除这样的结果,我们使用difflib’s SequenceMatcher执行重叠检查。它执行一个相当简单的检查来测量两个序列之间的重叠率,但是对于我们的目的来说它工作得相当好。由于将每个标题与未来日期的多个标题进行比较的计算量很大,我们将前瞻范围限制为 10 天。为了检查有意义的重叠,我们删除了子串佛罗里达人以及所有特殊符号,并将每个单词都变成小写。重叠阈值设置为 60%。这看起来很低,但是精确的匹配通常很难找到。

重叠检查效果很好,删除了许多类似的标题,但并没有完全完成工作。偶尔,这样的标题会出现:

——佛罗里达男子携带近 500 克大麻试图偷飞机见女友,警方称
——佛罗里达男子携带大麻偷飞机见女友

对于读者来说,很明显两个标题都是关于同一个事件的。虽然语义上有相似的意思,但是SequenceMatcher显然不能证实这一点。理论上,自然语言处理可能是一个不错的解决方案,例如,使用Doc2Vec库。不幸的是,我们只有 3660 个标题,太少了,不足以训练一个复杂的神经网络。构建匹配名词的自定义搜索听起来可行(例如,上面示例中的*、【Weed】、、【Plane】、、【女朋友】、),手动移除键也是可行的(尽管从自动化的角度来看并不令人满意)。在这一点上,我认为额外的设计工作不值得潜在的利益。鉴于这个项目缺乏真正的附加值或目的,人们应该知道何时停止。*

结果和可视化

经过这么多的努力,我们自然希望看到最终的概述。事不宜迟,让我们打印并展示我们精心策划的谷歌头条列表。实际上,完整列表有点冗长,所以让我们坚持一个月:

11 月的示例输出

尽管这可能不是表示信息的最佳方式,但让我们创建一个单词云,用一个漂亮的视觉效果来包装这个项目。不过,常规的方形单词云已经有点过时了。为了活跃一下气氛,我们将云放入一些漂亮的棕榈树的形状中,从图像mask = np.array(Image.open(“img/palm tree.jpg”))创建一个numpy遮罩,并使用wordcloud 库中的ImageColorGenerator

这就结束了——一张图片概括了佛罗里达人的一年,就像这个人自己一样难以理解!

用棕榈树图像的遮罩构建的词云(作者自己的作品)

好奇更多?完整的项目代码可以在我的 GitHub 资源库 中找到。

想看完整的标题列表吗?结账:

https://wvheeswijk.medium.com/a-year-in-the-life-of-the-florida-man-dec4781a74a2

外卖食品

  • 为了研究佛罗里达人的生活,我们执行了一个数据科学微型项目,从数据摄取到报告和视觉呈现。
  • 多线程通过多个 Google Cloud APIs 简化了抓取过程。
  • 数据格式化结合了字符串操作和正则表达式,将标题转换成统一的样式。
  • 循环使用一组禁忌词可以删除项目范围之外的标题。SequenceMatcher成功删除了大多数重复的标题。
  • 使用ImageColorGenerator,图像遮罩用于生成定制形状的单词云。
  • 佛罗里达人是个奇怪的家伙。说真的。

参考

[1]Altintas,I. & Porter,L. (2021) *UCSanDiegoX DSE200x:用于数据科学的 Python。*从 learning.edx.org 取回

用在线课程重建计算机科学学士学位

原文:https://towardsdatascience.com/recreating-a-computer-science-bachelor-degree-with-online-courses-3bb527635ca?source=collection_archive---------5-----------------------

办公时间

使用免费提供的信息创建您自己的课程

许多大学公开他们的课程,列出了获得学位的所有必修课程。计算机科学领域也不例外。使用这些可自由访问的资源(作为起点,见麻省理工学院(英语)、 JMU(德语)和工具包(德语)),人们可以创建一个定制的时间表。

这篇文章试图重建一个计算机科学学士学位,但是使用在线资源。其中一些是免费的,其他的会收取少量的费用。总而言之,它们是学习类似于大学所教内容的一种廉价的替代方式。备注:对于所使用的资源,我通常会努力坚持参考课程。这并不总是可能的;参加大学课程显然能让你接触到更广泛的讲座。

自然,第一个课程和学期侧重于基础知识,许多课程在这个阶段是强制性的。在满足这些义务之后,你可以自由地从一系列讲座中进行选择。这就是你可以选择软件工程、逻辑或机器学习的方向,这也是这篇文章的重点。在我们详细介绍这些课程之前,先来看一下课程概述:

课程概述,图片由作者提供。该列表可在此处的概念和此处的 GitHub 上获得。

第一学期

第一学期只包括入门课程。你在数学、逻辑、算法和数据结构(通常缩写为 ADS)以及编程方面积累基础知识。

弹药分配系统(Ammunition Delivery System)

“算法和数据结构”关注的是算法和数据结构。第一部分,算法,向您介绍排序,其中包括快速排序,合并排序,以及许多其他。此外,您将学习根据这些算法的输入检查它们的运行时间,这是在 Big-O 符号的帮助下完成的。

第二部分,数据结构,介绍了广泛使用的数据结构。

这些结构用于存储数据,以便可以快速找到或更新相关信息。使用的结构包括红黑树、二分搜索法树和图。当然,您还将学习如何遍历这样的结构。Coursera 上的数据结构和算法专业就是教你这些话题的课程。

线性代数

这个讲座帮助你理解用于计算机科学的数学基础。这主要是关于矩阵计算、导数和函数。涵盖这些主题的课程是由伦敦帝国学院提供的数学专业。从线性代数开始,继续优化函数,最后,学习如何压缩高维数据。

编程基础

这个讲座希望你获得编程的实践经验。因为这篇文章是关于机器学习的,所以选择 python 作为语言是必然的。我喜欢 python 的简单性——在你完成了Python for Everybody Specialization之后,你就会明白我的意思了。

逻辑

逻辑讲座集中于关系代数,从知识中推断事实,否定,以及进一步的数学证明概念。

这些话题听起来可能很无聊,但是学习它们也能在学习之外帮助你。我发现逻辑入门课程是最适合的——和以前的课程一样,不会总是 100%覆盖。你可以旁听课程,这让你可以免费使用资源,但不能参加分级考试——这是一个公平的交易!

第二学期

第一学期到此为止。第二学期继续以基础为主。你从学习统计学和概率论开始,加深你的数学理解。勾选后,你会听到软件工程和计算机基础知识。

统计和概率

作为一名数据科学家工作,或者同样意味着与数据打交道。处理数据包括探索。很明显,你不能一次处理 1000 张图片。这不是必需的,我们可以在统计的帮助下这样做。检查平均形状?有可能。列表的含义?有可能。数学在这里派上了用场。概率统计:要 p 还是不要 p?当然很适合这个。这个标题传达了幽默。

软件工程

有了我们的数学技能,我们可以进一步学习更多与计算机相关的东西。软件工程教授软件工程的概念:创建软件,使其适应性强,速度快,干净代码、继承和模式的基础。这个领域正在发生很多事情,课程软件设计和架构专门化教你更多关于 UML、OOP 和设计的知识。

计算机基础

本讲座涵盖了(现代)个人电脑的组成部分。我很难找到一个涵盖与我本科学习相同主题的讲座。最后,我选定了可汗学院的电脑和互联网课程。如果你在找一本书,那么你可以读读《量子猫的 T4 计算》,这本书也讲述了计算机的历史。

第三学期

第二学期到此为止,第三学期已经开始了。它涵盖了信号传输、高级编程和一个本科生研讨会。

信号传输

这堂课的重点是从 A 到 b 获取数字数据。但如果传输出现泄漏,我们正在丢失信息,该怎么办?还是有背景噪音?嗯,有像过滤器或编码方案这样的技术可以解决这个问题。前往数字信号处理专业了解更多信息。

编程 II

第二次编程讲座将建立在第一次的基础上,但会更深入地介绍 python。 Python 3 编程专门化是继 Python 之后的下一步。

本科生研讨会

我查阅的参考课程包含了这样的占位性研讨会。只要你是从你的领域,CS & ML 中选择一个主题,你就很好。

参观一所大学,你需要根据一篇或多篇论文讲述一个主题。然后你写一份这个领域的分析,并在简短的讨论中展示你的发现。

这种设置很难用在线课程来复制。有两种解决方法可以克服这一点:你可以找一个志同道合的人,谈论你选择的领域,或者你可以参加另一门课程。最后一个是我的首选解决方案,由于这个复制学位专注于机器学习,因此可以遵循吴恩达在斯坦福大学的 CS230 深度学习课程。这门课程给出了该领域的概述,由该领域最杰出的研究人员之一提出。

不同领域的讲座

这不是必须的,你可以把它看作一个额外的收获:听到一个来自不同领域的演讲。我推荐选择生物来拓宽你的知识面。你也可以选择经济学,物理,化学,任何你好奇的。

第四学期

理论计算机科学

第四学期从一门艰难的课程开始:理论计算机科学。我记得当时和许多志同道合的人一起听这个讲座。这是一次艰难的演讲,我们非常尊重这个主题。它涵盖了可计算性、语言理论(不一定是人类语言)、决定论、收敛和复杂性理论。

找到一门涵盖大部分内容的在线课程很有挑战性,最终,我选择了理论计算机科学导论

可计算性、复杂性和语言。我建议先学习这门课程,然后再学习这本书。

图论

如何找到 A 和 B 之间的最短路线?建模为图,我们可以选择 A 和 b 之间的最短子路线。但图存在于这样的(理论)问题、社会网络(谁跟随谁)、分子理论以及更多频繁使用的领域中。下面两门课会教你更多关于它们的知识:图论和图论导论。

软件实用教程

这门课希望你创造一些东西。它不一定是荒谬复杂的东西。我推荐一个在 web 服务器上运行的简单脚本。在 streamlit.io 查看画廊,并从中获得灵感。与其说是创造一个优秀的产品,不如说是学习一些新的东西。

不同领域的讲座

类似上学期,看看自己领域之外的。如果你上次听过生物讲座,现在就听一个关于化学的。或者物理,经济,医学,数学。

第五学期

硬件讲座

在之前的讲座中,我们听到了构建计算机的组件。我们将扩展这一点,并将我们的重点转移到与较低层次的硬件交互上。看一下嵌入式软硬件架构课程,了解一下底层固件。如果你热衷于发明新的组件,那么一门描述硬件的课程可能是个不错的选择。在这种情况下,查看FPGA 设计的硬件描述语言课程。

操作系统

现在使用电脑很容易。有效地使用你的工具集,那是另一个故事。当我开始用 PyCharm 编程时,我经常查阅文档来查找快捷键。有了它们的记忆,编程就容易多了。精通您的程序不仅仅局限于 ide,它还延伸到使用命令行、使用文本编辑器和权限管理。你不想花一生的时间去搞清楚工具,而是用它们创造东西。这就是你的计算机科学教育和操作系统和你:成为超级用户课程的目的。

数据库

您可以使用数据库作为替代方案,而不是将您的数据存储在命名的文件夹中,看着混乱增长。求图像的平均数?找到所有给 5 颗星的用户?有了查询语言,数据库很容易让人提取有用的信息。因为 SQL 是与数据库交互的事实上的标准语言,所以每种主要的编程语言都有与数据库交互的接口。因为 python 是其中之一,所以它也提供了包来隐藏所有的复杂性。剩下的工作就是建立数据库和准备查询。在 IBM 的数据库和 SQL for Data Science with Python 课程中,您将学到更多。

关于一般资格的课程

在一个充满写作、阅读、演讲和展示的世界里,开设一门专门的课程是一个明智的想法。你将学到的东西不仅适用于你的学习,而且对你生活的其他领域也有帮助。而且,由于大学要求你写很多东西和做很多报告,所以从报告技能开始比较好:写演讲稿、幻灯片和演讲专业化和有效沟通:写作、设计和报告专业化。

第六学期

你最后一个学期有两门课和你的论文。

数据挖掘技术

数据挖掘教你如何从许多领域分析和提取数据。无论是文本还是图像数据,这些技术都是普遍适用的。数据挖掘专业专注于这些概念,从数据可视化、处理文本数据、发现模式开始,到聚类结束。

数据科学数学

到目前为止还缺乏具体的 ML 课程。这是由于学士学位主要涵盖基础知识,在最后几个学期只有部分选择自由。通常,学士学位之后是硕士学位,在那里概念被深化。这就是为什么我主要把课程限制在宽泛的教育上,这也是我所学课程的一部分。尽管如此,为了让你做好更多准备,最后一门课程涵盖了数据科学的数学基础。你可能会看到与你早期数学课程的相似之处,这是不可避免的。毕竟,这是一件好事:你可以听到相同的内容,但来自另一位老师。

学士论文/ ML 课程

听过广泛的讲座(但至今仍局限于数学和计算机科学领域),你肯定会发现自己的兴趣所在。早期很难发现它们,但是随着经验的增长,你会了解自己的优势和劣势。记住它们,你就可以设计自己的论文项目,并在一篇简短的博文或视频中展示你的作品。另一个选择是参加吴恩达的机器学习课程,它将你在之前的课程中学到的东西联系起来。

接下来去哪里?

如果你在寻找更详细的资源,你可以看看这个 GitHub 资源库(感谢 Bradley Grant 分享这个!).

用网络课程再造一个 ML 硕士学位

原文:https://towardsdatascience.com/recreating-an-ml-masters-degree-with-online-courses-8f90a7088cf8?source=collection_archive---------42-----------------------

使用在线课程创建您的课程表

网上到处都是课程,提供很多学习资料。你可以利用这些资源复制一个 ML 硕士学位的课程。

MD 杜兰在 Unsplash 上的照片

学士学位的学习通常需要六个学期;硕士研究需要四个人。但这只是一个轮廓。我目睹过人们用三个学期完成学士学位,有些人用九个学期。有时候有太多令人兴奋的课程,以至于你会自愿多呆一会儿去学完它。因此,我将重新创建的课程松散地分为四个学期。主要关注点是机器学习和深度学习。

第一学期

人工智能

作为你课程表的第一门课,我推荐大家上AI。这是一个低水平的课程,也是学习通用术语、人工智能系统的能力、如何处理 ML 项目和伦理方面的理想开端。虽然这不是一门技术课程,但我仍然建议尽早学习这门课程。这样,你会对这些概念感到舒适,并为即将到来的课程做好准备。作为替代,你也可以做 IBM 的AI入门课程,也有类似的侧重点。

机器学习的数学

机器学习是一个数学密集型领域。虽然像 TensorFlow 和 PyTorch 这样的软件包使创建、训练和部署神经网络变得容易,但是如果您知道它们背后的概念,您会处于更好的位置。必修的数学不是火箭科学,而是建立在线性代数和概率论的基础上。此外,一旦您开始检查自定义数据集,您将经常发现自己在处理统计描述。

课程可以使学习过程更容易和更容易。以机器学习的数学(http://Mathematics for Machine Learning)专业化为例。整套课程只需支付少量费用,但你可以免费旁听个别课程。课程包括离散数学、图论、导数、线性代数和概率论。这些章节还附有用 python 编写的实践练习。

机器学习

斯坦福机器学习课程对机器学习的主题进行了广泛的介绍。它是由深度学习研究的最杰出人物之一吴恩达教授的。该课程从监督学习开始,包括经典的最大似然技术和神经网络。接下来,它涵盖了无监督学习,例如,在聚类算法中使用。最后,它还介绍了机器学习中使用的最佳实践。现实世界的例子伴随着这一切。本课程有认证或免费提供,但不需要证书。

第二学期

深度学习

为了深入了解深度学习领域,你可以选择三门课程。

第一个是由斯坦福大学提供的 CS230 深度学习。讲座视频免费提供,涵盖广泛的主题。它们包括对抗性攻击、可解释性和如何阅读论文。

同样由吴恩达教授的第二门课程深度学习专业化,更加注重实践。在本课程中,你将主动编码神经网络,训练它们,并改变超参数。涵盖的网络包括 CNN、LSTMs 和单词嵌入技术。这一切都是借助 TensorFlow 完成的。

第三个课程,深度学习简介,由麻省理工学院提供。我去年听过这个讲座,觉得做得非常好,很有思想。它由两位博士研究员教授,他们涉及序列建模、计算机视觉、生成网络以及强化学习。除了这个标准时间表之外,演讲还包括来自 Nvidia、Google 和类似公司的特邀演讲者的演讲。

全栈深度学习

全栈深度学习课程由加州大学伯克利分校作为官方课程提供,但所有材料都是免费提供的。要完成本课程,您应该具备一些 python 和模型培训方面的经验。然后,讲座将重点放在深度学习的生产方面:估计项目成本,选择计算基础设施,以及大规模部署模型。

本课程首先回顾基础知识(CNN、rnn、变压器),然后讨论项目管理和实验主题。这包括管理数据、监控、道德考量和团队合作。这些讲座的大部分都伴随着实践实验环节。如果你没有时间看完整的讲座视频,或者只想回顾精选的部分,你可以查阅每个视频下面的详细笔记。

生成网络

自从 2014 年“生成对抗网络”论文发表以来,生成技术变得非常流行。自那以后,人们对最初的 GAN 结构提出了许多改进,但大多数仍然依赖于双人游戏的思想。

在这个设置中,一个玩家(一个网络)生成人工样本。样本的质量由第二个玩家(另一个网络)来判断。第二个玩家看到真实的和生成的数据样本,并学会区分它们。有了第二个玩家(通常称为鉴别者)的反馈,第一个玩家(称为生成器)学习产生更真实的样本。在训练过程中,两个网络都变得更好:生成器创建更好的人工样本,而鉴别器——对抗性的(因此得名)——在检测虚假样本方面变得更好。

教授这一点的课程是生成敌对网络特殊化。在本课程中,您将通过构建基本架构来学习基础知识,然后改进您的模型。完整的专业化认证是收费的,但是您可以免费旁听单独的子课程。

第三学期

强化学习

强化学习讲座是 DeepMind 和 UCL 的教学合作项目,涵盖了强化学习的核心技术。在这些技术中有马尔可夫决策过程,这是一种模拟环境的基本方法。有了这样一个模型,您可以使用价值函数来创建一个(最优)策略。有了这个最优策略,你和你的代理可以用最好的方式解决一个目标。如果你对此感兴趣,那么请前往播放列表或访问另一个讲座系列(也是由 DeepMind 制作)这里。

自然语言处理

自然语言处理领域已经取得了巨大的进步。从简单的用于分类的独热编码向量开始,我们现在见证了基于注意力的架构的广泛使用。这种进步不是一夜之间发生的;有几个有趣的中间步骤。

以嵌入为例。不是将一个单词(或任何文本)编码成一个整数,而是使用上下文和世界知识来增加其表示的信息价值。对于术语“树”,这意味着我们不再将其编码为 17(这里是任意的索引),而是将其表示为浮点值的向量。这个向量捕捉了更多关于“树”的信息:它净化空气,它为各种动物提供庇护,它提供阴凉。

但是嵌入只是一种技术,并且可以被其他技术补充(或取代)。例如,要了解更多关于 NLP 的知识,可以参加自然语言处理专业。它从介绍经典的 NLP 技术开始,发展到基于深度学习的方法,最后介绍注意力模型。与其他专业一样,您可以免费旁听个别课程,以获得第一手印象。

计算机视觉

ImageNet 数据集仍然是神经网络分类能力的流行基准。它经常被描述为计算机视觉技术进步的关键因素。早在 2010 年,当 ImageNet 大规模视觉识别挑战赛首次举办时,分类准确率约为 50 %。如今,我们达到了 90 %的准确率,这确实是一个令人印象深刻的进步。

有几个因素促成了这一进展:

  1. 现在模特训练比较舒服。
  2. 数据处理更容易。
  3. 多年来,研究人员利用了许多图像增强技术。

要了解更多的基础知识,你可以前往计算机视觉基础。完成本课程后,您可以继续学习的 TensorFlow 高级计算机视觉课程。本课程涵盖图像分类、物体检测、分割、类别激活图等等。此外,看看 TensorFlow Lucid 来深入研究与图像相关的任务的神经网络。

第四学期

生物信息学

机器学习和计算机科学不是孤立的领域,与其他领域分开。相反,像其他科学一样,当与现实世界的问题相结合时,两者都展现了它们的力量。

想想蛋白质折叠:给定氨基酸序列,还不清楚它们是如何折叠成三维结构的。蛋白质完成许多重要的任务:它们运输物质,接收来自细胞表面的信号,是激素的一部分。

加州大学圣地亚哥分校提供的生物信息学专业涵盖了这一点,以及更多。它从引入 DNA,测序,比较基因组开始,以实际数据的基因组测序结束。这是一个广泛的专业化,包括七门单独的课程。我认为使用计算机运行测序算法使得这个领域的探索变得如此有趣。

医学人工智能

之前的课程以生物信息学为特色。因此,我们现在也将涵盖医疗信息学。斯坦福大学提供的可免费旁听的医疗保健简介课程是探索医疗保健系统的良好资源。虽然它集中在美国,但大多数知识普遍适用。毕竟,在任何国家你都需要医务人员。

在你对这个系统有了一个大概的了解之后,你可以继续进行的医药专精以获得实践经验。在这个由三部分组成的课程中,你将学习用 CNN 对疾病进行分类,预测受伤的可能性,从健康记录中提取数据,以及评估治疗的有效性。

那标志着结束。

推荐系统系列第 8 部分:评估现实推荐系统时要考虑的 14 个特性

原文:https://towardsdatascience.com/recsys-series-part-8-the-14-properties-to-take-into-account-when-evaluating-real-world-recsys-f71cc6e1f195?source=collection_archive---------29-----------------------

我们可以用多少种不同的方式来评估 RecSys?

更新: 本文是我探索学术界和工业界推荐系统系列文章的一部分。查看完整系列: 第一部分 第二部分 第三部分第四部分第五部分第六部分

在信息检索中,评价指标用于判断和比较推荐模型在基准数据集上的性能。对其准确性进行良好的定量评估对于构建成功的推荐系统至关重要。

  • 对于一个典型的离线推荐问题,我们从数据集中随机选取训练和测试样本。然后,我们在训练样本上建立一个预测模型,在测试样本上评估该模型,并使用选定的评估指标来衡量其性能。
  • 对于一个在线推荐问题,我们想在一个受控的 A/B 测试平台上对表现最好的离线模型进行在线评估。在选择新模型之前,我们评估在线用户行为和其他性能指标的差异,以确定此类差异在统计上是否显著。

无论是离线还是在线情况,在选择推荐方法时通常会考虑各种属性。这些属性各有利弊,因此了解和评估它们对整体性能和用户体验的影响至关重要。在我研究这个话题的过程中,我看到了盖伊·妮莎和阿塞拉·古纳瓦德纳早在 2009 年的一篇综述,讨论了如何根据一组与应用相关的属性来比较推荐。这篇博文是我试图简洁地总结这些特性的尝试。

注意

这篇文章是我正在进行的推荐系统系列文章的一部分:

  • 第 1 部分 提供了推荐系统的高级概述,它们是如何构建的,以及它们如何用于改善各行各业的业务。
  • 第 2 部分 很好地回顾了正在进行的关于这些模型的优势和应用场景的研究计划。
  • 第三部分 提供了几个与推荐系统学者社区相关的研究方向。
  • 第 4 部分 提供了可以构造的矩阵分解的 7 种变体的本质数学细节:从使用巧妙的侧面特征到应用贝叶斯方法。
  • 第 5 部分 提供了基于多层感知器的协同过滤模型的 5 个变体的架构设计,它们是能够以非线性方式解释特征的判别模型。
  • 第 6 部分 提供了基于自动编码器的协同过滤模型的 6 个变体的主类,这些模型是在学习潜在特征表示方面优越的生成模型。
  • 第 7 部分 提供了基于波尔兹曼机器的协同过滤模型的 3 种变体,它们与大脑中的学习有着显著的相似性。

1-用户首选项

一个显而易见的选择是进行用户研究(受试者内)并要求参与者选择一个系统。这种评估不会将受试者限制在特定的属性上,人类通常更容易做出这样的判断,而不是为体验打分。然后,我们可以选择票数最多的系统。

但是,我们必须意识到一些问题:

  • 我们假设所有用户都是平等的,这可能并不正确。在用户研究中分配正确的重要性权重可能并不容易。
  • 我们还需要找到一种方法来校准用户之间的分数,因为用户的偏好水平可能会有很大差异。
  • 虽然衡量用户满意度很重要,但是将满意度分解成更小的部分有助于理解系统并加以改进。

2 —预测准确性

预测准确性是推荐系统文献中讨论最多的属性。绝大多数推荐系统的基础是一个预测引擎。该引擎可以预测用户对项目的意见(例如,电影的评级)或使用的概率(例如,购买)。推荐系统的一个基本假设是,用户会更喜欢提供更准确预测的系统。因此,许多研究人员开始寻找能够提供更好预测的算法。

有三大类预测准确性测量:

2.1 —测量评级预测准确度

来源:【https://www.hulu.com/hub/originals】https://www.hulu.com/hub/originals

在像网飞或 Hulu 这样的应用程序中,我们希望预测用户对某个项目的评分。在这种情况下,我们希望测量系统预测评级的准确性。用于评估预测收视率准确性的流行指标是均方根误差和平均绝对误差。与 MAE 相比,RMSE 对重大失误的处罚力度更大。

  • 标准化的 RMSE标准化的 MAE 是已经通过评级范围标准化的 RMSE 和 MAE 的版本。
  • 平均 RMSE平均平均平均误差针对不平衡的测试集进行调整。如果我们需要一个度量来表示任何项目的预测误差,那么最好是分别计算每个项目的 MAE 或 RMSE,然后取所有项目的平均值。

2.2 —测量使用预测

在许多应用中,推荐系统并不预测用户对项目的偏好,例如评分,而是试图向用户推荐他们可能使用的项目*。例如,当视频被添加到队列中时,YouTube 会建议一组可能也很有趣的视频,因为添加了视频。在这种情况下,我们感兴趣的是系统是否正确地预测用户将这些视频添加到队列中(使用项目)。***

在使用预测的离线评估中,我们通常有一个由每个用户使用过的项目组成的数据集。然后,我们选择一个测试用户,隐藏她的一些选择,并要求推荐者预测用户将使用的一组项目。对于推荐和隐藏的项目,我们有四种可能的结果:真阳性、假阳性、假阴性和真阴性。使用这些结果,我们可以计算精度和召回数量。

通常,我们可以预期这些数量之间的权衡——虽然允许更长的推荐列表通常会提高召回率,但也可能会降低精确度。在向用户呈现的推荐数量是预先确定的应用中,最有用的兴趣度量是 K 处的精度。

在呈现给用户的推荐数量不是预先确定的其他应用中,最好在推荐列表长度的范围内评估算法,而不是使用固定长度。因此,我们可以计算精确召回曲线或 ROC 曲线。虽然两条曲线都测量了实际推荐的偏好项目的比例,但是精确回忆曲线强调了推荐项目被偏好的比例。相比之下, ROC 曲线强调最终被推荐的非首选项目的比例。

2.3 —排名措施

在像 Google 或 Bing 这样的搜索应用中,我们对预测明确的偏好不感兴趣,而是根据用户的偏好对结果/项目进行排序。这个排名任务可以用两种方式衡量:参考排名或者效用排名。

  • *在**参考排名中,*我们按照评级和/或使用的降序对项目进行排名。标准化的基于距离的性能测量给正确预测由参考断言的每个偏好关系的系统满分,给与每个参考偏好关系相矛盾的系统最差分。
  • 效用排名中,我们按照效用递减的顺序对项目进行排名,并根据项目在推荐列表中的位置进行因子折扣。流行的指标包括 R-Score 、归一化累积折现收益和平均交互点击排名。

3 —效用

来源:https://www.wayfair.com

效用是系统或用户从推荐中获得的值。例如,像 Wayfair 或亚马逊这样的电子商务网站利用推荐通过顾客购买来提高他们的收入。然而,用户效用或偏好很难捕捉和建模,相当多的研究已经将集中于这个问题。此外,不清楚如何汇总用户的用户效用来计算推荐者的分数。

在用户评价项目的应用程序中,我们可以使用评价作为效用度量。

  • 例如,在网飞,我们可以假设推荐一部 5 星电影比推荐一部 4 星电影对用户有更高的效用。由于用户可能会对评级做出不同的解释,因此在对用户进行汇总之前,应该对用户评级进行标准化。
  • 虽然我们通常只给成功的推荐分配正效用,但我们也可以给不成功的推荐分配负效用。例如,如果用户不喜欢特定的推荐电影,那么网飞可以为该项目分配负效用(关于该用户)。

对于任何效用函数,标准评估是计算建议的预期效用。

  • 在我们只预测单个项目的场景中,正确建议的价值是该项目的效用。
  • 在我们预测 n 个项目的场景中,我们可以对列表中正确推荐的效用求和。
  • 在我们为不正确的建议合并负效用的场景中,总和是所有建议(正确或不正确)的总和。

4 —覆盖率

4.1 —项目空间覆盖范围

覆盖率这个词指的是推荐系统可以推荐的项目的比例。对于这个定义,最简单的衡量标准是可以被推荐的所有项目的百分比。另一个衡量标准是销售多样性,它衡量当使用特定的推荐系统时,用户选择的不同商品有多不平等(如基尼指数和香农熵)。

4.2 —用户空间覆盖

覆盖范围也可以是系统可以推荐项目的用户或用户交互的比例。在许多应用中,由于对某些用户的预测可信度低,系统可能不会为该用户提供推荐。在这种情况下,我们可能更喜欢能够为更广泛的用户提供推荐的系统。因此,覆盖率可以通过进行推荐所需的用户简档的丰富程度来衡量,例如用户在接收推荐之前必须评级的项目的数量。

4.3 —冷启动

众所周知的冷启动问题衡量了一组特定项目和用户的系统覆盖率。确定“冷”项目的一般方法是使用它在系统中存在的时间量或收集的数据量。然后,我们可以对正确预测较冷项目的系统给予更多信任,而对预测的热项目给予更少信任。一条经验法则是,在计算冷物品的系统精度时,明智的做法是评估是否需要权衡整个系统的精度。

5 —自信

来源:https://www.instacart.com

对推荐的信任度可以定义为 系统对其推荐或预测的信任度 。它随着数据量的增长而增长。例如,如果 Instacart 推荐可信度非常高的西兰花和具有相同评级但可信度较低的豆芽,购物者可以立即将西兰花添加到购物车中,但在决定购买之前可能会进一步阅读豆芽商品的描述,或许还有其成分。

也许最常见的置信度度量是预测值为真的概率或预测值周围的区间。预定义的部分(95%的真实值位于其中)。最普遍的置信方法是提供可能结果的完整分布。

给定两个推荐器,它们在其他相关属性(例如预测精度)上表现相似,可能希望选择能够提供有效置信度估计的一个。在这种情况下,给定两个具有相同准确度的推荐器,它们以相同的方式报告置信界限,我们将更喜欢更好地估计其置信界限的推荐器。

置信度界限的另一个应用是过滤推荐项目,其中预测值的置信度低于某个阈值。在这种情况下,我们假设推荐者不能预测所有值的分数,这是呈现前 n 个推荐时的典型情况。因此,我们可以围绕这个过滤过程设计一个实验,在通过去除低置信度项目来过滤两个推荐器的结果之后,通过比较这两个推荐器的准确度。我们可以在这样的实验中计算曲线,估计过滤项目的每个部分或不同过滤阈值的预测精度。该评估过程不要求两种算法在置信度方法上一致。

6 —信任

虽然信心是系统对其评级的信任,但这里我们指的是用户对系统推荐的信任。例如,系统推荐用户已经知道并喜欢的几个项目可能是有益的。这样,即使用户从该推荐中没有获得任何价值,她也观察到系统提供了合理的推荐,这可以增加她对未知项目的系统推荐的信任。增强对系统信任的另一种常见方式是解释系统的建议。

评估用户信任的明显方法是询问用户在用户研究中系统推荐是否合理。在一个在线测试中,假设对推荐者更高的信任会导致更多的推荐被使用,人们可以将被关注的推荐数量与对推荐者的信任度联系起来。

或者,我们也可以假设对系统的信任与重复用户相关,因为信任系统的用户在执行未来任务时会返回。然而,这种测量可能不能很好地分离其他用户满意度因素,并且可能不准确。

7 —意外之喜

来源:https://www.scribd.com/

意外收获是衡量成功推荐有多令人惊讶的指标。我们可以把意外收获理解为推荐中对用户来说是新的相关信息的数量。例如,通过跟随 Scribd 上的成功书籍推荐,读者了解到她喜欢的一位新作者,这可以被认为是偶然发现的。

为了避免人为标记,我们可以设计一个基于内容的项目之间的距离度量。然后,我们可以根据它与协同过滤系统中的一组先前评级的项目或基于内容的推荐系统中的用户简档的距离来对成功的推荐进行评分。因此,我们奖励系统远离用户档案的成功推荐。

我们也可以认为意外之喜是与“自然”预测的偏差。给定一个高精度的预测引擎,它的推荐是“显而易见的”因此,对于预测引擎认为不太可能成功的推荐,我们会给予更高的意外收获分数。

在用户研究中,我们可以通过让用户标记他们认为出乎意料的推荐来评估推荐者的意外收获。我们还可以看到用户是否遵循了这些建议,这将使他们出乎意料地成功,因此也是偶然的。在一个在线实验中,我们可以假设我们的距离度量是正确的,并且只评估距离用户简档的距离如何影响用户遵循推荐的概率。

8 —新颖性

小说推荐是用户对 不了解的 项。一个显而易见且易于实现的方法是过滤掉用户已经评级或使用过的项目。然而,在许多情况下,用户不会报告他们过去使用过的所有项目。这种方法不足以过滤掉用户已经知道的所有项目。

虽然我们显然可以通过询问用户是否已经熟悉某个推荐项目来衡量用户研究中的新颖性,但我们也可以通过离线实验来了解系统的新颖性。对于这样的实验,我们可以按时间分割数据集,即隐藏在特定时间点之后发生的所有用户评级。我们还可以隐藏在此之前发生的一些评分,模拟用户熟悉但没有报告评分的项目。当推荐时,对于在分割时间之后推荐和评级的每个项目,系统被奖励,但是对于在分割时间之前推荐但评级的每个项目,系统将被惩罚。

另一种评价新颖性的方法是假设受欢迎的物品不太可能是新颖的。因此,可以使用精确度指标来考虑新颖性。当系统正确地预测了不流行的项目时,它不会因为正确地预测了流行的项目而得到相同的分数。

最后,我们可以评估推荐中新信息的数量,以及推荐项目的相关性。例如,当项目评级可用时,我们可以将隐藏评级乘以推荐项目的一些信息度量来产生新颖性分数。

9 —多样性

来源:https://www.tripadvisor.com/

多样性通常被定义为相似性的反义词。在某些情况下,建议一组相似的项目对用户来说可能不是很有用,因为可能需要更长时间来探索项目的范围。例如,考虑猫途鹰,在那里系统应该推荐旅行选项。为同一地点提供 10 个推荐的列表(仅在酒店和地点上有所不同)可能不如建议 10 个不同的地点有用。用户可以浏览各种位置并请求与其兴趣相关的更多细节。

测量多样性最常用的方法是使用项目间的相似性,通常基于项目内容。然后,我们可以基于项目对之间的和、平均、最小或最大距离来测量列表的多样性,或者测量将每个项目添加到推荐列表的值,作为列表中已有项目的新项目多样性的。由于多样性可能以牺牲其他属性为代价(如精确度),我们可以计算曲线来评估精确度的下降与多样性的增加。

在帮助信息搜索的推荐器中,我们可以假设更多样化的推荐将导致更短的搜索迭代。我们可以在测量相互作用序列长度的在线实验中使用这个作为多样化的代理。为了验证这种说法,在比较不同的推荐器之前,使用相同的预测引擎试验不同的多样性阈值是有用的。

10 —鲁棒性

稳健性是在存在虚假信息的情况下推荐的稳定性,虚假信息通常是为了影响推荐而故意插入的。例如,一个 Airbnb 主机可能会通过注入虚假的客人资料来提高其房源的评级,这些客人资料对房源的评级是正面的。

这种影响推荐的尝试通常被称为攻击。当恶意用户故意查询数据集或注入虚假信息以从一些用户那里了解一些私人信息时,就会发生协同攻击。总的来说,创造一个针对任何攻击的免疫系统是不现实的。估计影响推荐的成本更有用,这通常通过注入的信息量来衡量。实现这一点的一种方法是通过引入虚假信息来模拟攻击,并根据经验测量成功攻击的平均成本。

另一种类型的健壮性是系统在极端条件下的稳定性,比如大量的请求。在许多情况下,系统健壮性与基础设施有关,比如数据库软件、硬件规格和可伸缩性。

11 —风险

来源:https://robinhood.com/collections/100-most-popular

在许多情况下,建议可能与潜在风险相关联。例如,在像 Robinhood 这样推荐股票的应用程序中,规避风险的用户可能更喜欢预期增长较低、崩盘风险较低的股票。另一方面,寻求风险的用户可能更喜欢具有潜在高利润的股票。我们希望评估从建议中产生的预期价值,并最小化风险。

评估风险敏感系统的标准方法是通过考虑预期效用和 效用方差 。例如,我们可以使用一个参数 q,在 E[X] + q * Var(X)上比较两个系统。当 q 为正时,这种方法偏好寻求风险的推荐者。当 q 为负时,系统偏好风险厌恶型推荐者。

12 —隐私

在大多数协同过滤系统中,用户愿意公开自己对项目的偏好以获得有用的推荐。然而,对大多数用户来说,他们的偏好保持私密是很重要的。否则,私人信息泄露会导致类似于目标中的啤酒-怀孕相关性的情况。

一般认为推荐系统披露私人信息是不合适的,即使是对单个用户。因此,对隐私的分析往往集中在最坏的情况,举例说明用户的私人信息可能被泄露的理论情况。另一种选择是定义不同的隐私级别,如 k-identity ,并比较不同隐私级别下算法对隐私泄露的敏感度。

13 —适应性

来源:https://www.nytimes.com/trending/

真正的推荐系统可以在这样的环境中运行:项目集合快速变化或者对项目的兴趣趋势变化。明显的例子包括新闻推荐(《纽约时报》)或标签推荐(《推特趋势》)。当意外事件发生时,人们会对原本可能不感兴趣的文章/推文感兴趣。这个问题不同于冷启动问题是因为过去不感兴趣的老项目突然变得有趣了。****

这种类型的适应可以通过在推荐项目之前分析所需的信息量来离线评估。如果我们对推荐过程进行顺序建模,我们可以在算法推荐一个故事之前记录所需的证据数量,即使是在离线测试中。

另一种类型的适应性是系统如何使适应用户的个人偏好或用户简档中的变化。我们可以在向用户简档添加更多信息后,在离线实验中评估推荐列表的变化。然后,我们可以通过测量添加新信息前后推荐列表之间的差异来评估该算法。

14 —可扩展性

现实世界的推荐系统必须扩大到数百万个项目(谷歌新闻或 YouTube ),即使是以准确性或覆盖范围等其他属性为代价。评估可伸缩性的标准计算机科学方法是根据时间或空间需求来衡量算法的计算复杂性。另一个更实际的方法是测量大型数据集的资源消耗。****

可伸缩性通常是通过对不断增长的数据集进行实验来衡量的,展示了随着任务规模的扩大,速度和资源消耗如何表现。衡量可伸缩性所要求的折衷是很重要的。例如,测量数据子集的准确性性能对于特定的未来任务非常有用。

因为推荐系统被期望在线提供快速推荐,所以测量系统提供推荐的速度也是重要的。其中一个度量是系统的吞吐量(系统每秒可以提供的推荐数量)。我们还可以测量延迟(在线推荐所需的时间)。****

结论

推荐系统无疑是机器学习在实践中的主要成功案例之一。尽管他们取得了成功,但未来的研究仍有许多机会,特别是在评估他们的方法上。最佳解决方案是进行大规模的产学合作,弥合学术指标和商业价值之间的差距。另一个解决方案是更加强调以用户为中心和以影响为导向的评估指标。最后,希望改进当前的离线和在线实验方法,以处理多代理环境的复杂性并利用分布式计算。

请继续关注本系列的下一篇博文,它将探索市场中的推荐系统!

参考

妮莎和古纳瓦德纳(2011 年)。评估推荐系统。在推荐系统手册(第 257–297 页)中。马萨诸塞州波士顿斯普林格。

如果你想关注我在推荐系统、ML 研究和数据新闻方面的工作,你可以查看我的 中的 GitHub,以及在https://jameskle.com/的其他项目。你也可以在 推特 直接发邮件给我 ,或者 在 LinkedIn 上找我。 注册我的简讯 就在你的收件箱里接收我关于机器学习在研究和生产中的最新想法吧!

用于音频分类的递归神经网络

原文:https://towardsdatascience.com/recurrent-neural-nets-for-audio-classification-81cb62327990?source=collection_archive---------4-----------------------

使用张量流进行音频分类的深度学习入门

作者图片

递归神经网络

RNNs 或递归神经网络是一种可以记住序列的深度学习算法。

什么样的序列?

  • 手写/语音识别
  • 时间序列
  • 用于自然语言处理的文本
  • 事情是这样的,依靠一个先前的物品

是指音频吗?

**是的。**除非音频是随机的垃圾流(不是波段),否则音频信息往往遵循一种模式。

请看贝多芬月光奏鸣曲的前两小节:

作者图片

相当重复!*你怎么看他失聪后还坚持作曲?*模式识别和记忆。

也是天才。

本文解释了如何训练 RNN 根据音频信息对物种进行分类。

本例的数据是 Kaggle 竞赛雨林连接物种音频检测中的鸟类和青蛙录音。他们很可爱。

作者图片

要开始,加载必要的导入:

import pandas as pd
import os
import librosa
import librosa.display
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import normalize
import warnings
warnings.filterwarnings('ignore')
from sklearn.model_selection import train_test_split
import tensorflow
from tensorflow.keras.layers import LSTM, Dense

然后是数据帧:

os.chdir('/kaggle/input/rfcx-species-audio-detection')
df = pd.read_csv('train_tp.csv')

该数据集是一个 csv 文件,音频文件的名称列在 recording_id 下,标签列在 species_id 下,音频样本的开始/结束列在 t_min 和 t_max 下:

df.head()

使用 librosa 包加载并显示一个音频文件,如下所示:

sample_num=3 #pick a file to display
filename=df.recording_id[sample_num]+str('.flac') #get the filename
#define the beginning time of the signal
tstart = df.t_min[sample_num] 
tend = df.t_max[sample_num] #define the end time of the signal
y,sr=librosa.load('train/'+str(filename))
librosa.display.waveplot(y,sr=sr, x_axis='time', color='purple',offset=0.0)

建模的特征

Librosa 有关于如何提取特征的很棒的教程在这里。对于 RNNs,我发现最好的特征是 Mel 频率倒谱系数(MFCC),一种声音的频谱特征。你可以这样计算:

hop_length = 512 #the default spacing between frames
n_fft = 255 #number of samples 
#cut the sample to the relevant times
y_cut=y[int(round(tstart*sr)):int(round(tend*sr))]
MFCCs = librosa.feature.mfcc(y_cut, n_fft=n_fft,hop_length=hop_length,n_mfcc=128)
fig, ax = plt.subplots(figsize=(20,7))
librosa.display.specshow(MFCCs,sr=sr, cmap='cool',hop_length=hop_length)
ax.set_xlabel('Time', fontsize=15)
ax.set_title('MFCC', size=20)
plt.colorbar()
plt.show()

提取所有文件的特征和标签并存储在 numpy 数组中:

def get_features(df_in):
    features=[] #list to save features
    labels=[] #list to save labels
    for index in range(0,len(df_in)):
      #get the filename        
      filename = df_in.iloc[index]['recording_id']+str('.flac') 
      #cut to start of signal     
      tstart = df_in.iloc[index]['t_min'] 
      #cut to end of signal
      tend = df_in.iloc[index]['t_max'] 
      #save labels
      species_id = df_in.iloc[index]['species_id'] 
      #load the file        
      y, sr = librosa.load('train/'+filename,sr=28000)
      #cut the file from tstart to tend 
      y_cut = y[round(tstart*sr,ndigits=None)
         :round(tend*sr, ndigits= None)]
      data = np.array([padding(librosa.feature.mfcc(y_cut, 
         n_fft=n_fft,hop_length=hop_length,n_mfcc=128),1,400)])
      features.append(data)
      labels.append(species_id)
    output=np.concatenate(features,axis=0)
    return(np.array(output), labels)
X,y=get_features(df)

将数据规范化并转换为 numpy 数组

X = np.array((X-np.min(X))/(np.max(X)-np.min(X)))
X = X/np.std(X)
y = np.array(y)

提取培训、测试和验证数据集

#Split twice to get the validation set
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=123, stratify=y)
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.25, random_state=123)
#Print the shapes
X_train.shape, X_test.shape, X_val.shape, len(y_train), len(y_test), len(y_val)

创建一个 RNN

在这个示例模型中,长短期记忆 (LSTM)单元是进行记忆的部分,丢失随机将一部分数据的权重设置为零以防止过度拟合,密集单元包含与模型必须尝试拟合数据的自由度相关的隐藏层。数据越复杂,模型需要的自由度就越多*,同时要注意避免过度拟合(稍后会有更多的*)。最后的密集层输出 24 个种类,模型应该将音频记录分类到这些种类中。

RNN 模型架构示例

作者图片

在 tensorflow 中,您可以像这样创建上面的 RNN 模型:

input_shape=(128,1000)
model = keras.Sequential()
model.add(LSTM(128,input_shape=input_shape))
model.add(Dropout(0.2))
model.add(Dense(128, activation='relu'))
model.add(Dense(64, activation='relu'))
model.add(Dropout(0.4))
model.add(Dense(48, activation='relu'))
model.add(Dropout(0.4))
model.add(Dense(24, activation='softmax'))
model.summary()

激活功能增加了模型的非线性。这里使用了 relu 函数,它将负权重清零。最后一个密集层的激活函数是 softmax ,它为每个类输出一个概率。Tensorflow 还有其他激活功能,你可以在这里阅读。

输入形状可能会令人困惑,因为尽管它看起来是 2D ,但实际上是 3D 。由于创建时选择的参数,这些 MFCCs 的形状恰好是高 128,长 1000,并且它们的数量与音频文件的数量一样多。如果我们只提取 dataframe.head()图中所示的 5 个音频文件的特征,则输入的形状将是 5×128×1000。该参数称为批量大小,它不包含在输入形状中。

编译模型

model.compile(optimizer='adam',loss='SparseCategoricalCrossentropy',metrics=['acc'])

Adam 优化器管理随机梯度下降的学习速率,是一个很好的开始。损失函数是稀疏分类交叉熵,当每个样本属于一个标签时使用,而不是一个以上,不是二元分类。这就是这个分类问题的情况,其中每个音频样本属于一个物种。.)

符合模型

history = model.fit(X_train, y_train, epochs=50, batch_size=72, 
                    validation_data=(X_val, y_val), shuffle=False)

关于过度拟合的一些词语

作者图片

但是你必须暂时过度拟合才能知道过度拟合和欠拟合的界限在哪里(F. Chollet,2018)。

从最简单的模型开始,一步一步来

试试这个:

*input_shape=(128,1000)
model = tensorflow.keras.Sequential()
model.add(LSTM(**NUM**,input_shape=input_shape))
model.add(Dense(24, activation='softmax'))
model.summary()*

其中 NUM 是比你的输出图层大一些的数字,添加图层直到你的模型开始溢出数据。

你如何知道这种情况何时发生?

评估您的模型训练和验证集

  1. 如果训练集和测试集之间的性能不同(例如,训练准确率为 99%,测试准确率为 89%),则表明数据过拟合。
  2. 当选择的验证度量(在这种情况下是准确性)开始降低时,停止迭代。在下图中,这大约发生了 50 个时期。
*#Adapted from Deep Learning with Python by Francois Chollet, 2018
history_dict=history.history
loss_values=history_dict['loss']
acc_values=history_dict['acc']
val_loss_values = history_dict['val_loss']
val_acc_values=history_dict['val_acc']
epochs=range(1,51)
fig,(ax1,ax2)=plt.subplots(1,2,figsize=(15,5))
ax1.plot(epochs,loss_values,'co',label='Training Loss')
ax1.plot(epochs,val_loss_values,'m', label='Validation Loss')
ax1.set_title('Training and validation loss')
ax1.set_xlabel('Epochs')
ax1.set_ylabel('Loss')
ax1.legend()
ax2.plot(epochs,acc_values,'co', label='Training accuracy')
ax2.plot(epochs,val_acc_values,'m',label='Validation accuracy')
ax2.set_title('Training and validation accuracy')
ax2.set_xlabel('Epochs')
ax2.set_ylabel('Accuracy')
ax2.legend()
plt.show()*

使用混淆矩阵检查模型的预测效果:

如果所有条目都在矩阵的对角线上排成一行,则该模型在测试集上做出了完美的预测。其他的都被错误分类了。

*TrainLoss, Trainacc = model.evaluate(X_train,y_train)
TestLoss, Testacc = model.evaluate(X_test, y_test)
y_pred=model.predict(X_test)
print('Confusion_matrix: ',tf.math.confusion_matrix(y_test, np.argmax(y_pred,axis=1)))*

现在你知道了

现在,您已经知道如何使用音频数据创建 RNN,方法是从一个简单的模型开始,然后添加图层,直到它能够尽最大能力预测数据。修改架构,直到您的模型开始过度拟合数据,以了解这个边界在哪里,然后返回并删除层。查找训练数据和测试数据之间的性能差异,并添加剔除层以防止过度拟合训练数据。寻找验证数据中的性能下降,以了解何时停止迭代。

快乐造型!

来源

萨卡尔、迪潘坚 (2021)个人通信。

Chollet,F. 用 Python 进行深度学习(2018),v. 361,纽约:曼宁。

Gervias,Nicolas, (2021) 代码摘自https://stack overflow . com/questions/59241216/padding-numpy-arrays-to-a-specific-size,2021 年 1 月 10 日检索。

frenzykryger(2021)https://data science . stack exchange . com/questions/41921/sparse-category-cross entropy-vs-category-cross entropy-keras-accuracy #:~:text = Use % 20 sparse % 20 category % 20 cross entropy % 20 when,0.5%2C%200.3%2C%200.2%5D ,2021 年 2 月 21 日检索。

关系数据库上的递归图查询

原文:https://towardsdatascience.com/recursive-graph-queries-over-a-relational-database-5e4c71782536?source=collection_archive---------25-----------------------

你从未见过的 SQLite

关系数据库上的递归图查询。图片作者。

FactEngine (www.factengine.ai)是一项从根本上改变人们看待数据库的方式的倡议。该倡议的本质是揭示如何将所有数据库视为多模型数据库(图形或关系数据库)。作为同类中的第一个,很难在不提及该倡议的情况下谈论该科学。但是让我们来看看科学…

专用的图数据库现在因为在属性图模式下工作而有些出名,如下所示:

显示讲师节点属性的属性图模式。图片作者。

上面的模式就是所谓的 有向图模式 ,其中一个关系如 讲师在学校 被形象化地显示为一个箭头指向的 边(或图) 连接 节点 讲师学校

专用图形数据库的制造商会让你相信这种类型的建模和相关的图形查询语言是那些专用图形数据库的权限。只有当你想让它成为图形数据库或关系数据库,而又没有工具将你的数据库可视化时,这才是真的。

关系数据库传统上被绑定到一个模式,该模式被形象地表示为一个实体关系图,如下所示:

实体关系图。图片作者。

您有时希望能够像查询图形数据库一样查询关系数据库,以使工作变得简单。

假设我们想要使用我们现存的模式查看一个虚构的讲师喜欢的每个人。我们应该能够使用简单的图形查询来查询数据库,如下所示:

图模式上的图查询。图片作者。

我们的查询,(讲师:' Alexandria ',' Archer ')喜欢哪个讲师,返回一个结果,Steven Hollows。

递归图查询

现在,专用图形数据库有一个窍门,那就是如果你想知道亚历山大·阿彻喜欢的讲师,他们喜欢谁,他们喜欢谁,他们喜欢谁,那么你可以修改查询,使其递归 4 层(0..3)深度,如:

图模式上的递归图查询。图片作者。

现在我们的查询返回多个讲师和他们喜欢的人。

专用图形数据库在这类事情上受欢迎的原因是,传统上,对于关系数据库,您必须用结构化查询语言(SQL)编写查询,如下所示:

选择【讲师】。名字,[讲师]。姓氏,[讲师 1]。名字,[讲师 1]。LastName
FROM 讲师,
讲师讲师 1
,(带递归节点(讲师 Id,讲师 Id1,深度)As (
选择讲师 WITH 讲师。讲师 Id,讲师 Id1,0
FROM 讲师 like 讲师,讲师
WHERE[讲师]。名字= '亚历山大'
和[讲师]。LastName = 'Archer'
和讲师。讲师 Id =讲师。讲师 Id
工会
选择讲师。讲师号,讲师号。讲师 Id1,深度+ 1
从节点开始,讲师 like 讲师
从节点开始。讲师 Id1 =讲师类讲师。讲师 Id
限制 100
)
从深度介于 EN 0 和 3
之间的节点
中选择讲师 Id,讲师 Id1
作为讲师喜欢的讲师
讲师喜欢的讲师。讲师 Id =讲师。讲师 Id
和讲师类讲师。讲师 Id1 =讲师 1。讲师 Id

这是一场噩梦。你真的不想花时间学习如何用 SQL 编写递归查询,然后费力地编写你想要的查询。

我认为,你想要的,能够实现这种事情,是创建你的关系数据库,然后用图形查询语言查询它。

即将发布的 Boston 和 FactEngine 允许您在关系数据库上进行递归图形查询和普通图形查询。FactEngine 中的标准图形查询如下所示:

FactEngine 查询语言中的图形查询。图片作者。

您使用对象角色建模创建您的数据库,它自动为您提供数据库的图形和关系视图,然后使用图形查询语言简单地查询您的数据库。

事实上,这篇文章是关于数据科学的可能性,以及如何以一种新的方式看待数据库,让生活变得如此简单。诀窍是完全摒弃图形和关系的概念,只需将数据库视为一组连接的对象类型。

我们模式的对象-角色建模视图类似于:

带有自然语言谓词的对象-角色模型。图片作者。

神奇的事情发生在我们的例子中,我们的讲师类讲师对象化事实类型具体化了关系,并且有两个隐含的链接事实类型,它们的整体组合为图形模式和实体关系图提供了主干。链接事实类型,下面显示为虚线事实类型:

具有隐含链接事实类型的对象化事实类型。图片作者。

本文中的查询是在 SQLite 关系数据库上执行的。FactEngine 查询语言(FEQL)中的图形查询自动转换为 SQL。例如,现存的倡议是为所有关系数据库创建基于图形的查询语言。FactEngine 查询语言只是关系数据库上图形查询语言的一个例子。

关系数据库上图形查询的未来

本质上,我认为数据库的未来是一个不太关心的环境,在这个环境中,人们不会不关心他们数据库的底层管道(传统的图形或传统的关系)…只要你的数据库以适当的方式及时返回结果,并允许你在你认为合适的时候查询它。研究表明,在图形查询速度方面,传统的关系数据库可以匹配甚至超过专用的图形数据库[1][2]。毕竟,您已经花费了时间和精力来决定哪个数据库最适合您。我认为你想要的是有工具和合适的数据科学来允许你在你的数据库上做你想做的事情,并且不要大惊小怪。

感谢您的阅读,如果时间允许,我将写更多关于图形数据库、关系数据库、多模型数据库和对象-角色建模的文章。

  1. Rawlani,p .等人,“关系数据库的图形分析”,麻省理工学院,【https://dspace.mit.edu/handle/1721.1/100670 ,2020 年 8 月 24 日获取
  2. 等,“关系数据库的图形分析”,麻省理工学院。

— — — — —结束— — — —

RLS:动态学习

原文:https://towardsdatascience.com/recursive-least-squares-learning-on-the-fly-f8bb878eb270?source=collection_archive---------17-----------------------

简单在线学习算法

一个简单的模型,当新的数据进来时,它可以动态地学习更新它的权重。

作者图片

为什么要递归最小二乘法?

在线学习是人工智能研究领域中一个蓬勃发展的研究领域。当今世界的许多问题需要机器动态学习,并在收集新信息时进行改进或适应。

在本文中,我将解释如何适应最小二乘回归,以便在新数据到来时递归地计算最佳权重,从而使其适合在线学习应用程序。

最小二乘法

有了足够的数据,回归问题可以通过最小化由一组权重和目标做出的预测的平方误差来解决。

其中 E 是误差的平方,y 是目标,X 是预测数据,w 是模型的权重

这有一个解析解,可以通过称为伪逆的线性代数来获得:

最小化平方误差的最佳权重集由上面的等式给出。

对于高维问题,计算上式中的逆运算可能非常昂贵。逆运算与数据维度的平方成比例。在在线设置中,每次有新的数据点时都必须执行该操作,这是不可行的。这种方法的另一个问题是没有考虑数据的年龄。在在线环境中,每秒钟都会有新数据出现,更新的数据可能比多年前获得的数据更重要。

因此,该方法必须适于在在线环境中实施。

秩一更新,逆序更新

秩 1 更新允许通过使用使用矩阵 A 的逆矩阵及其向量的表达式来更新矩阵的逆矩阵加上向量乘以自身。首先,要求逆的项被分解为一个矩阵和一个向量加上它的转置的和。

其中 xn 是进入数据集的新数据点,Rn 是我们希望求逆的新矩阵

在每个时间步长,当接收到新的数据点时,Rn 可以表示为如上。

然后可以使用秩一更新来更新权重。如你所见,Rn 的更新逆矩阵已经被计算出来,而不需要执行矩阵的逆矩阵。更新的权重方程的完整推导不会显示,但是,这些方程足以实现您自己版本的递归最小二乘算法。

值得注意的是**更新的权重等于权重的先前估计减去增益项乘以预测误差。**这个结果在机器学习领域经常见到(看看随机梯度下降的更新公式,卡尔曼滤波,强化学习中的 Q-learning 等)。

RLS 击败 SGD 的地方

上面显示的算法是用 python 实现的,并与非常流行的随机梯度下降(SGD)算法进行了比较。

随机生成由 500 个数据点组成的具有某种相关性的合成数据集。利用随机初始化的权重,可以应用 SGD 来逼近最佳拟合线。这可以与 RLS 算法进行比较。

作者图片

正如您所看到的,RLS 算法只需要大约 100 个数据点就可以达到完美的解决方案,而 SGD 算法需要大约 40,000 次迭代才能达到相同的解决方案(即使它可以访问所有数据)。不仅如此,RLS 还在动态学习,每次接收到一个新点时都会逼近伪逆解。

与其他实现相比

最后,这个实现可以与公开提供的 python 库进行比较。

作者图片

该算法的实现与 statsmodels.api(一个流行的 python 库)提供的实现完全相同。上图显示了两种算法的三个权重的收敛,所有权重达到相同的值。

结论

RLS 算法能够根据最小二乘解来估计最佳权重,而无需显式计算伪逆中的逆运算。这使得它成为在线学习应用的强大算法,在在线学习应用中,需要即时更新估计值。该模型与 SGD 进行了比较,结果表明,在适当的情况下,该算法可以大大优于更受欢迎的同类算法。

支持我👏

希望这对你有所帮助,如果你喜欢,你可以 关注我!

您也可以成为 中级会员 使用我的推荐链接,访问我的所有文章以及更多:https://diegounzuetaruedas.medium.com/membership

你可能喜欢的其他文章

可微发电机网络:简介

傅立叶变换:直观的可视化

重新定义数据科学

原文:https://towardsdatascience.com/redefining-data-science-d7f2026a021a?source=collection_archive---------40-----------------------

成功应用机器学习指南

由 Unsplash 上的 Kvalifik 拍摄的照片

建立预测模型相对简单。然而,在整个企业中部署、扩展和维护多个模型并不简单。​

今天,一个成功的机器学习项目需要 90%的工程和 10%的科学。企业雇佣数据科学家开发端到端的机器学习解决方案。他们需要弥合科学方法和工程过程之间的差距。大多数数据科学家没有接受过大数据工程或分布式计算方面的培训,因此在功能工程、算法开发和部署阶段,扩展成为一个问题。

数据科学变得太模糊了。我们先来了解一下数据科学和机器学习的区别。

什么是机器学习?

机器学习(ML)不是数据科学的子领域。ML 是人工智能的一个子集,它最初是计算机科学的一个子领域,专注于解决人类可以做但计算机还不能做的任务。

请看下面的图表来总结我的观点:

机器学习算法基于经验(数据)自动执行大量小决策,并生成概率结果来解决业务关键问题。

什么是数据科学?

数据科学是一个基于三大支柱的跨学科领域:计算机科学、数学&统计学和商业知识。

这个定义有些松散。数据无处不在,无处不在。作为一名数据科学家,我需要解决涉及不同技能和数据复杂性的问题。构建复杂的量化算法需要强大的量化背景 统计线性代数、数据结构、编程知识 。部署、管理和优化数据管道和基础设施以转换和传输数据需要强大的 数据工程技能 。将技术分析转化为定性行动项目,并将其发现有效地传达给不同的利益相关方,这需要高水平的沟通和业务领域知识。在任何阶段(模型开发、特征工程、ETL、模型部署)智能扩展解决方案都需要 大数据 和强大的 计算机科学 技能。

因此,数据科学家是一名专业人员,拥有前面描述的使用机器学习解决关键数据业务问题所需的所有高级技能。如果你的公司里有这些专业人士,那么你已经挖到了金子。让他们保持挑战和快乐。

然而,数据世界在不断发展,以前的问题变得越来越复杂。现在是时候重新定义数据科学,建立更专业的团队,以确保 ML 项目在生产中取得成功。

建立一个成功的 ML 团队

“2018 年 1 月的 Gartner 数据科学团队调查发现,超过 60%的旨在实施的模型从未实际实施。”

为什么只有约 25%的数据科学项目能够真正投入生产?

公司雇佣背景和技能完全不同的数据科学家,并要求他们做同样的任务。我给你打个比方。

假设您正在庆祝贵公司上个季度取得的巨大成功。你办公室里有一架很棒的钢琴,所以你决定雇一名钢琴家。他美妙地演奏了几个小时。他知道如何制造钢琴吗?答案是否定的。但是他知道它是如何工作的,所以钢琴家能够演奏出优美的旋律。

所以,让我们把这个类比应用到数据上。作为分析部门的经理,你雇佣了两名数据科学家来领导一项 ML 实践。两人都有应用数学博士学位,有统计学背景和机器学习算法知识。9 个月后,他们在测试框架上创建了一个准确率高达 98%的令人敬畏的算法。然而,在生产的前三个月,性能下降了 30%。他们在自定义梯度提升算法上应用了集成学习,因此算法的可解释性变得技术性和复杂(黑盒模型)。由于生产中模型结果的不一致性,您决定在一项大投资后放弃该项目。

发生了什么事?—您未能包括一名关键团队成员。了解如何解决扩展问题、生产中的数据漂移、大数据、MLops 最佳实践和分布式计算的专业人士。你雇了一个知道如何制造钢琴的人,但不是一个知道如何在观众面前演奏的人。

“科学是关于知道,工程是关于做。”

亨利·彼得罗夫斯基

你需要一个填补科学和工程之间空白的专业人士。一个机器学习工程师

因此,我看到两种类型的数据科学家在一起工作。我们需要在这两种不同的专业人士之间建立头衔上的区别。

这些学科需要不同的“思维模式”——一个采用更科学的方法,另一个采用更实际的工程方法。因此,我建议您的数据科学家在这些角色中更加专业化,以确保最佳性能。

企业应该雇佣与其商业目标一致的专业人士。公司可能不需要博士数据科学家来重新发明轮子。事实上,他们经常雇佣机器学习工程师来训练已经构建好的 ML 算法,以便在他们的公司成功应用机器学习。一旦企业组建了一个拥有合适“厨师”的 ML 团队,他们需要遵循一个与 ML 最佳实践相一致的“食谱”,以提高生产成功的可能性。我们将在下一节讨论这个“配方”。

机器学习项目生命周期

机器学习生命周期是一个迭代过程,确保项目的大规模运作。它支持科学(ML/DS 科学家)、工程(ML 工程师)和业务领域知识(利益相关者)之间的协作。此外,它通过一个从一开始就描述每个 ML 团队成员的角色并管理期望的架构来提高成功的概率。

ML 项目生命周期是让你的 ML 团队获得实际商业价值的终极“配方”。

让我向你介绍我们的成功秘诀。

是的,我知道你在想什么!相当多的阶段,但是不用担心——为了便于解释,我们将把它分成三个不同的阶段。

你可以看到蓝色数字(ML 工程师)是如何参与几乎每一项任务的——我希望你同意我之前的说法,ML 学习项目需要 90%的工程学,只需要 10%的科学。一个 ML 工程师也应该能够在模型训练中采用“科学的思维方式”。事实上,我可以将 ML 工程师定义为接受过 MLOps 和大数据工程培训的下一代数据科学家。然而,如果你的公司想要使用“开箱即用”的算法,并且能够负担得起多年的研究和资源——雇用一名 ML 科学家。希望他能为你建立一个最先进的全新算法。

阶段 1: ML 数据工程

ML 项目生命周期是一个迭代的过程,因此每个阶段都很重要。任何阶段的错误都可能是有害的,并迫使 ML 团队回到流程的最开始。迭代不应该导致瘫痪。团队成员应该向利益相关者和管理者设定期望,他们可以在未来重新审视前进道路上的每一步。

然而,根据我的经验,第一阶段尤其重要。目标定义应该在 ML 团队和利益相关者之间设定现实的期望。数据收集准备和特征工程阶段占用了 ML 项目 80%的时间。所选功能将直接影响您使用的预测模型以及您可以实现的结果。

在第一阶段设计管道时,您需要选择正确的技术组合。例如,如果您应该确定哪种技术将允许您在数据收集和特征工程阶段扩展您的计算。您应该考虑以下事项:您需要一次一个观察值地摄取和处理实时数据的样本(在线学习)还是进行批量摄取和处理?部署呢?是否有低延迟要求?这些问题的答案超出了本文的范围。然而,我的观点是——你的团队在第一阶段做出的每个决定都会影响项目的结果。它们可能会加速其他阶段,也可能会产生瓶颈。

阶段 2:模型构建和部署

这是 ML 生命周期中迭代最多的阶段。我想强调的是,在算法进入部署阶段之前,您的团队需要确保利益相关者理解模型决策过程,并就定义“成功”的最合适的指标达成一致。在这个迭代项目中,模型可解释性是必须的。这将有助于利益相关者理解结果,并且由于算法行为的更好压缩,也将使您的团队能够改进算法。

第三阶段:MLOps

机器学习操作(MLOps)对 ML 生命周期的需求是毋庸置疑的。传统的软件系统基于确定性算法。

机器学习是随机的,不是确定性的。我们都同意 ML 开启了一个全新的任务类别。然而,非确定性软件系统(ML 算法)应该仔细设计和测试。

这就是我看到许多公司失败的地方——机器学习模型的生命周期在许多方面不同于软件开发生命周期(SDLC)。软件工程师在进入生产之前,通过称为 Q/A 的整个测试软件基础设施测试这些传统的软件系统。

“生活在变化,我们的模型接受的实时数据也在变化,”—Alexander Konduforov 承认。

在模型评估阶段,您的 ML 工程师应该已经创建了一个测试框架,以便从其他候选者中选择“冠军”。基本上,他们证明了该算法在特定时刻基于商定的度量分数工作。然而,一旦模型在生产中部署和运行,我们需要确保理解三个概念:

1。不要只关注代码版本,你需要一个地方来保存数据和模型版本。 MLOps 需要一个报告框架来保存数据和模型版本,以便重用和重新培训。

2。算法会随着时间退化,这就需要监控。 MLOps 要求您监控模型输入(数据)和输出的统计数据。监控阶段的目标是帮助 ML 工程师建立一个稳定的环境。

**3。训练永无止境。**模型维护阶段确保识别模型退化的方法。一旦发现性能下降,ML 工程师和 ML 科学家应该分析算法行为,并决定重新训练或添加更多功能来增强性能。

关键要点:

  • 机器学习端到端管道是产品——而不是模型本身。
  • 任何企业的目标都是成功,要在 ML 领域取得成功,你的团队需要专业化。随着 ML 从研究转向应用商业解决方案,企业需要改进他们的操作流程。专业化是一种推动增长的创新。
  • 一旦你设计了你的 ML 团队来交付所提到的能力,他们应该执行 ML 项目生命周期来确保端到端的项目在生产上的成功。
  • MLOps 有潜力使 ML 项目生命周期自动化,促进生产成功的变化,并弥合科学和工程之间的差距。

大规模机器学习的自动化是任何智能系统的关键。ML 不是未来——它正在发生。

请随时联系我-我们是一家国际咨询工程公司,拥有强大的应用机器学习团队。

谢了。

plopez@pkglobal.com

【pkglobal.com 情报与分析—PK

巴勃罗·萨尔瓦多·洛佩兹|领英

pablosalvador10(巴勃罗·萨尔瓦多·洛佩兹)GitHub

参考资料:

MLOps:用于机器学习的 DevOps 方法| AltexSoft

https://towards data science . com/the-machine-learning-life cycle-912 a 70647d 61

https://www.kdnuggets.com/2020/08/top-10-lists-data-science.html

重新设计传统的统计学课程以教授数据消费者

原文:https://towardsdatascience.com/redesigning-the-traditional-statistics-class-to-teach-data-consumers-8c057e55ddc4?source=collection_archive---------22-----------------------

我们是在教学生当司机还是机械师?

由 cottonbro 在 Pexels.com拍摄的照片

当人们发现我有统计学学位时,话题不可避免地会转向他们在大学上的统计学课。虽然有些人称赞这门课,并认为它很有用,但大多数人讲述了他们在理解这些概念时的挣扎。它并不经常出现在他们最喜欢的课程列表中。虽然我开玩笑说,我的工作保障在于许多人不喜欢统计学,但这错过了机会。为什么统计课几乎普遍存在负面体验?

打个比方:学开车和学做机械师

我相信有更好的方法来设计这些统计类。我来打个比方解释一下。假设你正在教某人如何开车。你会怎么做?一开始,你会告诉他们如何发动汽车,如何松开紧急刹车,如何调整座椅和后视镜。你肯定会解释油门踏板和刹车的区别,以及如何倒车。你可以讲述交通规则,并展示如何在交通中保持安全。经过一些练习,新司机会很快达到基本水平。

现在想想你怎么可能不教别人开车。你不会一开始就打开引擎盖,详细解释让汽车发挥作用的所有部件。你不会解释运动的物理原理和内燃机中发生的反应。如果汽车坏了,你也不会告诉他们如何修理。不知道引擎盖下发生了什么,也不知道油门踏板是如何连接到车轮上并使汽车行驶的,你也可以成为一名优秀的司机。了解汽车如何工作会很方便吗?当然可以。但是你不需要成为一名机械师来学习如何驾驶汽车。

类比在统计课中的应用

那和统计课有什么关系?我相信大多数统计学入门课程是为了迎合统计学家(技工)而不是数据消费者(司机)。虽然一个好的机械师价值连城,但并不是每个人都需要成为机械师。同样,虽然统计学家可以在处理数据时提供很多价值,但并不是每个人都需要成为统计学家。当然,你需要为那些将要成为统计学家的人上统计学入门课。但是更大的招生需求是面向所有学生的普通统计学课程。

因此,我们需要的不是描述统计学背后的东西,而是教授人们如何恰当地使用统计学的课程。你不需要理解概率密度函数和中心极限定理就能很好地使用数据。对于大多数人来说,知道如何计算标准差或如何从教科书后面的表格中查找 p 值没有什么价值。要成为一名合格的数据消费者,你不需要理解理论和数学方程式。

一种重新设计统计课的新方法

统计学入门课程需要彻底改革。大学应该提供一门适用于广大普通人群的单一的终结课程。本课程将涵盖以下主题:如何利用数据进行推理,数据分析的局限性,数据收集的影响,数据的基本可视化,以及如何从数据中得出不正确的结论。这不应该是数学教授教的,因为这不是数学课。相反,老师应该探索和提问,挑战假设,帮助人们看到问题的两面,并仔细权衡结论的有效性。这是一门用数据进行推理和批判性思考的课程。

因此,教学方法遵循类似于法学院课程的案例研究方法。学生们会研究一篇博客文章、报纸上的一篇文章,或者一项包含一些数据结论的科学研究。在任何感兴趣的主题中,都有大量好的和坏的例子可供选择。我在统计文献中看到了一些在这方面的努力。但是,基于统计学理论基础的案例研究方法仍然专注于如何更好地培训技工。除非你有正确的内容,否则使用案例研究方法是不够的。同样,内容必须迎合数据消费者(驱动者)。

老师会仔细选择这些例子,以提供不同领域的各种应用,并突出不同的统计问题。然后学生们会做一个批判性的评估来决定研究的结论是否被数据所证实。他们可以考虑一些问题,如数据是如何收集的,在收集过程中可能会出现什么偏差,哪些数据没有收集,数据是如何分析的,等等。有了这些例子,学生成为了更好的数据消费者。

理想的结果

自从 COVID 疫情开始以来,关于数据的讨论越来越多。我看到越来越多的人试图利用研究数据来支持他们关于口罩、封锁、社会距离和疫苗的立场。虽然有令人钦佩的尝试使用数据来推动决策,但我也看到了许多数据使用的糟糕例子。当数据没有被很好地使用时,不可避免地会产生误解和错误的结论。统计学课程的重新设计是对这个错误信息问题的一个解决方案。案例研究方法将创造一代精明的数据消费者,并提高我们公共话语的质量。

原载于 2021 年 8 月 21 日【https://cocreativelythinking.com】

Redis:在任何速度下都不安全

原文:https://towardsdatascience.com/redis-unsafe-at-any-speed-f2731f738a25?source=collection_archive---------2-----------------------

[图片由 Unsplash 提供]

行业笔记

代价换来的性能

NoSQL/NewSQL 产品的激增为软件工程界提供了大量可行的存储和检索数据的替代方案,这些方案具有各种形状和大小,具有不同的性能、可伸缩性、一致性和可用性特征。高度僵化且有些老化的关系数据模型不再被随意使用。结果,开发人员蜂拥至替代的持久性堆栈,这主要是由可伸缩性的前景和更好地符合其本地应用程序领域的数据模型所驱动的。有趣的是,人们很少关注他们不太时尚的特点;例如,事务支持、隔离级别、一致性、网络分区的处理、工具、安全性等等。

很难想出比 Redis 更受关注的数据存储。在栈溢出上,它连续三年被选为最受欢迎的数据库。它也是AWS上最受欢迎的数据库,超过了 MySQL 和 Postgres,以及亚马逊专有的 NoSQL 产品。

这是我们中的一些人开始担心的地方,而且不无道理。当然,对于一个工程团队来说,寻找一个与他们的技术目标更加一致的数据库是非常正常的;例如,当应用程序利用以文档为中心的数据模型时,文档存储通常更容易使用。这不仅仅是一个工程师的突发奇想——这里的“愉快”直接转化为降低的复杂性、提高的开发人员生产力和士气,以及最终的“胜利”——对于业务来说,不是别的。但事实上,数据对组织及其利益相关者具有内在价值,当数据的持久性或一致性处于危险之中时,企业有权知道。不幸的是,工程团队在选择技术基础设施时优先考虑某些质量,往往忽略了对关键利益相关者至关重要的方面,他们可能被排除在决策循环之外。正如读者可能已经猜到的那样,这个讨论将集中在 Redis 的安全属性上——特别是持久化数据的持久性和一致性。

本文试图解释为什么 Redis 不适合用作 NoSQL 数据库,因为持久化数据的持久性和一致性是必不可少的。澄清一下,这不是针对 Redis 的。Redis 是一个优秀的缓存或二级数据存储,具有方便的 NoSQL 功能、分片、复制和尾随持久性。它在耐用性方面的损失,在吞吐量和延迟方面得到了弥补。在适当的情况下,Redis 是一个极好的技术选择。本讨论的背景是它作为高价值业务记录的主数据存储库的用途,这些业务记录有严格的持久性要求,并且一旦丢失或损坏就无法轻易恢复。

推荐阅读

在开始分析之前,有必要重申一些基本的分布式计算概念。如果你已经认为自己对分布式数据库的架构很了解,那么跳到期望部分。

预期

Redis 集群属于由领导者-跟随者架构支持的复制状态机的范畴,在基于多数的法定系统的配置中包含多个进程。在分布式系统文献领域,这是一个众所周知的、久经考验的拓扑。根据我们陈述的持久性和一致性要求,系统需要满足布鲁尔上限定理的 CP(一致性和分区容忍)标准——换句话说,面对网络分区,它需要将数据一致性置于可用性之上。我强烈怀疑,大多数采用 Redis 集群并对持久性和一致性抱有强烈期望的人会很自然地认为 Redis 集群是一个 CP 系统,而不是 AP 系统。也就是说,假设 Redis 是安全的,或者至少,足够安全

对于“足够安全”的含义,目前还没有达成普遍共识,而且也不可能达成共识,因为安全要求是由商业驱动因素决定的,而商业驱动因素对每个组织来说都是独一无二的。在这里,我将尝试为安全定义一个较弱的定义,它可能对相当多的应用程序都是合理的。我从与不同行业的众多客户打交道的个人经验中得出结论,我建议的是大多数从业者都会做出的合理假设。有人可能会说,这种对安全性的弱化不够客观;我说,没有某种基线,我们就无法前进。

“足够安全”的基于领导者-跟随者的复制状态机通常需要满足以下标准:

  1. **领袖奇点。**在任何给定时间,至多一个领导者可以操作,由配置中的法定数量的复制品(包括领导者)一致接受。前任被取代的领导者不能干涉现任领导者的行为。严格地说,安全性能允许零领导在琐碎的情况下;然而,在某些时候,必须有一个选举的领导者才能最终出现,从而满足活性属性。
  2. **复制功效。**所有副本(包括领导者)按照领导者规定的顺序忠实地将领导者发出的更新应用到它们的状态机。由引导者执行的所有更新被传递给一个法定数量的复制品(包括引导者)。未被复制副本的完整仲裁所接受的写入不得由主设备安装或向发起应用程序(客户端)确认。这个性质与 CAP 定理的 CP 约束有关。
  3. **法定人数交集。**根据我们之前的通信,法定人数系统中的每个有效法定人数至少通过一个过程与其他所有法定人数相交。
  4. **一致的副本提升。**在故障转移期间,只有一致的副本可以竞争领导权。其他的必须保持休眠状态。一旦副本被选中,它将从领导者停止的地方继续。请注意,可能有未确认的写入在领导者上挂起;安全属性不关心这些写操作;继任的领导人可以自行决定合并它们。
  5. **有界限的陈腐的坚持。**每个副本都连接到一个持久存储设备,该设备在进程失败后仍然存在。持久状态反映了在失败之前进程的瞬时状态,关于已经由复制品在关于陈旧性的一些商定界限内确认的写入。有限的陈旧性可以用经过的时间或未提交记录的数量来衡量。(后者更可取,因为它更准确地反映了最坏情况下的损失程度。)在运行时,该进程不会从其临时存储中丢弃数据,直到永久存储已经确认了相关的写入。重新启动的进程可以确定身份(偏移量、序列号等。)最近提交的写入。作为一个推论,一个重新启动的过程决不能自动取得领导权,即使它在失败前碰巧是领导者。

除了上述标准,我还提出了几个假设来补充弱安全属性:

  1. 法定人数系统被设计为容许 f 失败,其中 f ≥ 1。换句话说,我们正在处理一个高度可用的系统,其中单个进程的故障不会导致系统不可操作。实际上,对于基于多数的法定人数系统,我们至少需要三个过程。
  2. 过程不受可能的共模故障的影响。共模故障涉及影响多个独立元素的单一原因;例如,电源故障或物理入侵事件。将服务器分散在不同的机架或更好的数据中心,可以降低共模故障的可能性。
  3. **流程配备有P故障检测器。**所有失败的进程最终都被所有正在运行的进程所怀疑;所有活动的进程最终都不会被怀疑。准确性-完整性对偶的精确调整虽然对活性有实际意义,但不影响安全性。
  4. **弱持久的持久性。**持久化的数据以约定的可能性被原始地召回,该可能性随着时间而减小。数据丢失和损坏很有可能被检测到。

上述弱安全属性与其常规(强)变体之间的差异集中在标准 5 和假设 4 中。它们放宽了通常的持久性标准,这些标准几乎肯定是在更理论化的环境中假设的。通常情况下,标准 5 类似于以下内容:

…持久状态准确地反映了流程在失败之前的瞬时状态,与副本已确认的写入相关。

类似地,假设 4 应该是:

持久化的数据在未来的每一点都被原始地召回。

我们在这里做出了两项重要的让步。首先,我们说,确认的写入不一定需要提交到稳定的存储,前提是这些写入在某个“合理”的时间段内被刷新,并且我们可以在重启时准确地检测到最后一次提交是什么。在实践中,这可能不是一个硬约束;更有可能的是,这将是一个软(尽力)执行。我们称之为“未提交的写入让步”。其次,我们不假设完美的存储—我们允许磁盘偶尔出现故障,尽管故障率应该在“可接受”的范围内,并且故障应该是可检测的。我们称之为“不完美磁盘让步”。请注意,弱化版本中标准 5 和假设 4 的措辞有点模糊:我们戏谑了术语“合理”和“可接受”,而没有进一步的澄清。

在处理多节点配置(假设 1)时,未提交的写入让步通常是可接受的,其中节点位于不同的机架或数据中心,并且共模故障的可能性足够低(假设 2)。即使副本没有将其更新提交到稳定的存储,并且最终由于过程失败而丢失写入,配置中的其他副本也将保持这些更新完好无损。这假设至少有一个幸存的仲裁有一个一致的副本,并且副本不会快速连续失败,以便任何幸存的副本都有机会被选为领导者,并将其状态传递给其对等体。标准 2 和 3 证明了这一假设的合理性,并支持标准 4。

不完美的磁盘让步只是清醒地接受现实。不管存储设备中采用的物理冗余的程度如何,都存在数据损坏的非零可能性。错误检测和纠正算法也是如此——它们本质上是概率性的。我们可以通过增加冗余来将故障的可能性降低到可以忽略的水平,冗余会导致成本和延迟。此外,配置的大小本身也有助于存储冗余。如果我们选择添加更多的处理节点,我们不必添加更多的磁盘;事实上,后者具有增加流程可用性的额外优势。

敏锐的读者会发现,拜占庭式的错误没有得到明确解决。在讨论不完美磁盘和共模故障时,我们偶然提到了它们。此外,网络可以拒绝流量,或者更糟的是,以一种接收者无法检测的方式改变数据包,从而以拜占庭的方式行事。后者在实践中是极不可能的,取决于错误检测算法的准确性。最后,软件可能会产生拜占庭错误,在这种情况下,进程可能会发出或保存任意数据,包括使其看起来像一个正常运行的程序的数据。拜占庭行为可能是实现缺陷的结果。这在实践中很有可能,因为软件是由人类制造的,而且 T2 的错误是人为的。更不用说,这些进程可能是恶意的。

分析

首先,几个条款来源于官方文件,作为序言。有些甚至本身就很有趣。至少,Redis 的所有用户都应该非常熟悉它们,不管他们对 Redis 或这篇文章有什么看法。在整个分析过程中,我们将通过它们的编号来引用它们。

  1. 借助开箱即用的默认设置,Redis 作为一个半持久的数据存储区运行,牺牲了持久性,而有利于吞吐量和写入延迟。这在官方文档的 Redis 持久性章节中有详细说明。具体来说,AOF(仅附加文件)的默认刷新间隔为一秒钟,在此期间,已确认的写入可能会不可挽回地丢失。这是appendfsync everysec选项,它是 AOF 配置中的默认选项,也是推荐选项。
  2. 持久性规范列出了appendfsync always选项,其中所有暂存的 AOF 写入在返回之前都要经过一个fsync();然而,在这种模式下,Redis 的性能在官方文件中被描述为,逐字引用,“非常[原文如此]非常慢,非常安全”。因此,推荐使用appendfsync everysec选项,因为它“非常快而且非常安全”。读者会注意到 Redis 文档似乎回避了经验或定量的断言,而倾向于使用定性的形容词,如“非常”、“缓慢”、“快速”等。
  3. 关于复制的第章指出 Redis 在 leader-follower(按其说法是主-从)拓扑中采用异步复制协议,其中从设备独立于主设备对发起客户端的响应来重放主设备应用的写操作。在主设备响应的时候,从设备可能任意落后于主设备。
  4. 从版本 3.0.0 开始,Redis 包含了 WAIT 命令。WAIT在客户端上阻塞,直到所有以前的写入在指定的超时时间内被复制到用户指定数量的复制副本,返回超时前已确认的复制副本数量。表面上,WAIT将 Redis 的异步复制机制变成了同步复制机制。
  5. WAIT 文档指出“WAIT 不会使 Redis 成为一个强一致的存储:虽然同步复制是复制状态机的一部分,但它不是唯一需要的东西”。它继续阐明“Sentinel 和 Redis 集群都将尽最大努力提升可用副本集中的最佳副本。然而[原文如此]这只是一种尽力而为的尝试[原文如此],因此同步复制到多个副本的写入仍有可能丢失”。在这里,文档没有明确说明“尽力而为”的含义。具体来说,最佳副本升级失败的条件并没有被维护者公开。我们有责任解决这个问题。
  6. WAIT文档并未声明命令会阻塞,直到主服务器和/或副本服务器刷新了暂存的写操作,仅声明指定的最小数量的副本服务器“成功传输并确认了命令”。换句话说,WAIT命令是对相应副本的瞬态而非持久状态的断言。
  7. 复制章节指出“Redis 不是一个具有强一致性的 CP 系统”以及“确认的写入在故障切换期间仍可能丢失”。它继续说道,“使用WAIT,故障事件后丢失写入的概率大大降低到某些难以触发的故障模式”,但没有详细说明导致数据丢失的特定故障模式,也没有量化这些事件发生的可能性。表面上,这是指第 5 条,这是不完整的。它也可以指第 6 条。Redis 把细节留给了众所周知的“读者练习”。
  8. 官方的 Redis 集群规范描述了“从节点等级”算法,其中从节点相对于其对等节点在复制数据量方面建立其等级,并延迟领导者选举一段时间,该时间大致与其等级成比例。(我们说“大致”是因为等待时间包含随机延迟成分。)更完整(排序更接近 0)的从模块被设置为在不太完整(排序更接近 N-1,其中 N 是配置的复制品的数量)的从模块之前投票。
  9. 在同一节中,集群规范声明“主设备不会以任何方式选择最佳从设备”。没有机制来确保后继的领导者在幸存的复制品中排名最高。

基于对 Redis 集群文档和“精美小册子”的随意阅读,更不用说它在行业中的广泛采用,大多数人会天真地认为 Redis 集群大致以如下方式工作。

当足够的进程和网络链接是可选的时,写操作从主服务器(进程 A )镜像到法定数量的副本服务器上。(基于前面例子中的三进程配置。)所有副本都参与一个八卦协议(定期交换心跳),每个副本都知道其他副本的存在和复制高水位标记。(高水位线是最近复制的写入的标识。)如果客户端调用WAIT,写操作不会向客户端确认,直到它们暂时反映在大多数进程上。(没有WAIT,客户端立即返回。)此外,Redis 过程给每个连续视图分配一个单调递增的纪元编号,以便复制品同意它们是哪个视图的一部分。在下面的示例中, A 已经选择了{ AB }个仲裁进行复制,允许 C 进行跟踪。如果有足够数量的副本处于同步状态,尾随副本完全符合规则。

正常操作[图片由作者提供]

在某些情况下,主节点要么出现故障,要么与其余节点隔离。这两种情况实际上是等效的。结果是暂时停机,因为客户端无法将其请求路由到主服务器,或者主服务器无法将写入传递到仲裁,因此它永远不会响应。 BC 都保持休眠状态;它们无法独立处理写操作,因为它们在当前时代都不是主服务器。

master @ epoch 1 失败[图片由作者提供]

副本最终会注意到主服务器没有心跳,并通过其本地P检测器推测其故障。这两个复制品都启动了一个倒计时定时器,该定时器根据其相对等级大致按比例启动。(还增加了一点随机性。)

故障检测[图片由作者提供]

最新的复制品的计时器最快到期,进程 B 获得 C 的投票成为新的主人。Redis 使用一个名为FAILOVER_AUTH_REQUEST的消息来获得对等副本的投票。那些支持将请求者设置为提议时代的新领导者的人用FAILOVER_AUTH_ACK来回应。 BC 都增加它们的历元计数器,从而排除 A 的干扰。

故障转移[作者图片]

一旦 B 被指定为下一个纪元的新主机,客户端应用程序最终会发现这一事实,并将它们的请求路由到 B 。可能有写入在 A 中排队—可能 A 仍然存在;无论如何,这都不重要——那些写的东西还没有被确认,它们的丢失对客户来说应该是无关紧要的。到那时,任何进行中的请求都将被路由到新的主节点。

新主人@纪元 2[图片由作者提供]

请记住, BC 无法判断 A 是否出现故障、被隔离或反应缓慢。如果 A 有效,它也将启动自己的倒计时定时器;然而,它将无法获得足够的票数以形成法定人数。允许 A 稍后重新加入,但必须先经过一个发现过程;如果 A 试图取得主控权,纪元计数器会在合并时阻止它。现在应该很明显,视图时代二重奏是至关重要的;没有它,主人将会被无条件地允许与他们的前奴隶乱搞。

被废黜大师的击剑[图片由作者提供]

理论如何符合现实?

Redis 群集的默认配置不安全。

默认异步复制模型(没有WAIT)意味着客户端可以从主机接收更新已经被接受的确认,而底层命令既不在主机自己的持久预写日志上,也不复制到甚至一个复制品上。这是从第 1 条和第 3 条中轻易推断出来的。如果主设备出现故障,配置中可能就没有副本包含主设备确认的所有写入。这里没有什么要补充的了。

我们现在来看看加强任何可配置参数是否能满足弱安全属性。我们可以选择的两个选项是appendfsync alwaysWAIT。两者都会以不同的方式对应用程序的性能产生重大影响。

强制 AOF 在每次写入时发出fsync()会使主节点线性化,但会显著影响总吞吐量。引用 Sanfilippo 在第 2 条中的话,表演退化为“非常非常慢”。我们不知道这到底意味着什么,但 Percona 进行的基准测试表明,与appendfsync everysec相比,性能损失大约为一个数量级。这听起来是一个显著的下降,但实际上可能已经足够了,这取决于您的吞吐量需求。不可否认,因为 Redis 主要是内存中的数据存储,在 Redis 术语中被认为“非常慢”的东西在 IO 绑定数据库的上下文中很容易被认为“非常快”。

在应用程序代码返回之前调用WAIT会影响延迟——我们会阻塞,直到副本确认写入,并且需要两次往返(WAIT是一个单独的命令)。额外的网络流量也会影响吞吐量。虽然额外的WAIT命令也会对主服务器产生一些影响,但是影响主要在客户端层面。进行两次调用是低效的,人们不禁想知道为什么WAIT不能是一个自动应用于所有命令的会话级属性,而不是一个显式命令。也就是说,使WAIT显式化在批处理中提供了一个优势——当多个命令需要作为一个非原子逻辑操作的一部分执行时,在最后调用单个WAIT会缩短整体延迟。但是对于需要单独确认的命令的串行执行,WAIT可能会极大地降低吞吐量和延迟。尽管如此,我们接受WAIT从客户端的角度将异步复制模型转变为同步模型*。(稍后将详细介绍。)*

除了效率和性能方面的考虑,问题是这些能否增强 Redis 的安全性,以及增强到什么程度?

强制 AOF 同步并不能使 Redis“足够安全”,因为我们宽松的安全标准已经考虑到了持久层中有限的陈旧性。具体来说,标准 5 规定,对于已确认的写入,允许持久存储落后于瞬态,直到某个界限。appendfsync always设置对于安全的强形式是必要的,其中不允许有滞后,但是如果配置中有一个以上的过程,它对于弱形式是多余的。为了理解为什么,考虑一个只有一个进程的配置——一个主进程,没有从进程。如果主服务器失败,系统将会非常安全,但不会被激活。如果主设备最终重新启动,其连接的存储将使其恢复到上次确认的状态。相反,有了appendfsync everysec,在没有其他副本的情况下,我们可能会丢失数据。然而,在配置中有多个节点的情况下,按照我们的标准 4,主节点的故障将导致主节点选举,并最终将控制权转移到与主节点一致的另一个副本节点。老师傅因为appendfsync everysec丢失了写不要紧;只要至少还有一个一致的副本(标准 2),该副本就可以用于维护安全性和活性,并在离开的主设备最终重新加入时作为其种子。在多节点配置中,复制功效标准(2)是确定(弱)安全性的决定性因素;耐久性标准(5)的存在只是为了最小化丢失大部分复制品的影响。

那么,同步复制的使用满足复制效率标准(2)吗?它会使 Redis 集群“足够安全”吗?

号码

WAIT的使用部分满足标准 2。从客户端的角度来看,复制协议是同步的。这意味着,在一定数量的复制副本(包括主副本)确认收到这些写入之前,不会向客户端确认更新。但是,其他客户端可能会观察到分配的值,即使该值尚未稳定。确认可能无法到达,或者主服务器可能崩溃,最终导致在更新无效的备用法定人数中选举新的领导者。

作为一个具体的例子,再次回忆我们的可信配置,其中三个节点{ ABC }、 A 是当前的主节点。键 x 当前被赋予值"1"。客户 N 希望通过发布SET x "2"来更改x。命令成功返回。 N 然后通过调用WAIT 1 0做“正确的事情”并阻塞,直到写入被多数反映。与此同时,客户端 M 执行一个GET x并看到值"2"。新写入尚未被复制,并且在有机会复制之前 A 失败。首领选举随之而来, B 成为新的主人, C 成为奴隶。 M 发出另一个对x的读取并得到"1"M 早先对x="2"的观察被称为脏读。顺便提一下,这种行为对应于 ISO/ANSI SQL 标准下允许的最低隔离级别。直截了当地说,就普遍接受的隔离级别而言,不会比这更差了。

问题是WAIT没有全局同步复制;它只是从发起客户端的角度出发。WAIT不隔离事务。其他客户端可能会观察到不一致的结果,因为复制在本质上仍然是异步的。大多数用户会天真地认为 Redis 对单个更新自动操作,隔离级别至少是读提交。事实并非如此。标准 2 明确禁止这种行为,规定在更新稳定之前不能对外透露。

假设我们的应用程序不关心隔离。我们是否可以通过稍微放宽标准 2,将安全属性进一步稀释到“几乎足够安全”的水平(或者其他的反常情况)?在某些情况下,这可能是合适的,尽管有人可能会说,很少有认真对待数据一致性的组织愿意处理允许脏读的不断发展的数据集。尽管如此,我们还是不要纠结于人们可能期望什么。

使用WAIT很可能满足标准 2 的弱化形式,除非 Redis 中有我不知道的 bug。事实上,Redis 主机会将更新复制到一个 quorum 中。此外,Redis 使用的多数法定人数系统很容易满足“法定人数交集”标准。(还是那句话,除非有 bug。)我们将注意力转向标准 1 和 4——“领导者奇点”和“一致性副本提升”。这就是事情不顺利的地方。

考虑如何提升副本。为了扩展第 8 条,幸存副本的相对排序是通过在src/cluster.c clusterGetSlaveRank()中的本地检查来建立的,如下所示。这是一个简单的函数,它通过遍历复制状态的本地快照中对等副本的复制高水位标记来计算当前副本的相对等级。

int clusterGetSlaveRank(void) {
    long long myoffset;
    int j, rank = 0;
    clusterNode *master; serverAssert(nodeIsSlave(myself));
    master = myself->slaveof;
    if (master == NULL) return 0; /* Never called 
                     by slaves without master. */ myoffset = replicationGetSlaveOffset();
    for (j = 0; j < master->numslaves; j++)
        if (master->slaves[j] != myself &&
            !nodeCantFailover(master->slaves[j]) &&
            master->slaves[j]->repl_offset > myoffset) rank++;
    return rank;
}

replicationGetSlaveOffset()src/replication.c中定义,返回当前节点从主节点接收到的复制偏移量的高水位标记;

long long replicationGetSlaveOffset(void) {
    long long offset = 0; if (server.masterhost != NULL) {
        if (server.master) {
            offset = server.master->reploff;
        } else if (server.cached_master) {
            offset = server.cached_master->reploff;
        }
    }
    /* offset may be -1 when the master does not support 
       it at all, however this function is designed to 
       return an offset that can express the amount of 
       data processed by the master, so we return a 
       positive integer. */
    if (offset < 0) offset = 0;
    return offset;
}

注意,排名完全是从局部视角计算出来的。一个进程如何确定它的每个对等体的复制偏移量?这是通过定期交换PING / PONG消息(以及偶尔的MEET消息)来实现的,作为其八卦协议的一部分。除其他属性外,标准消息头还包含发送方的复制偏移量。如果你对细节感兴趣,请看src/cluster.c clusterBuildMessageHdr()

在建立等级中使用的复制偏移取自在最后一次消息交换期间公开的历史快照。在myself->slaveof->slaves数组中的repl_offset属性是值,单独来看,在某个时间点是正确的;然而,它们作为一个集合不一定是正确的(即,不是一致的快照),并且它们不代表系统的当前状态。此外,在该阵列上的视图内(即,在一些法定数量的节点内)没有一致的协议。每个节点都有自己对偏移量的看法,这是基于它最后一次收到消息的时间。

在启动故障转移过程之前,作为故障检测的一部分,会交换额外的消息,这在一定程度上缓解了上述不一致性。如果主服务器死亡,复制将暂停一段时间,直到足够多的节点怀疑并同意主服务器的故障,此时,其中一个副本(排名最高)将试图被选中,然后是下一个最佳副本,依此类推,间隔大约一秒钟。假设主服务器处于静止状态,并且系统在诱导领导者选举之前有足够的时间来稳定,幸存的副本将可能有机会交换它们最近的复制偏移,这将停止前进。(这是一个可能的结果,而不是绝对的保证。)在此期间,节点将可能已经收敛于一组统一的复制偏移;即,每个节点将持有与其对等节点相同的意见。不幸的是,实际上事情并没有这么简单。

Redis 节点不能确定主节点实际上有故障。在异步系统中,完美的故障检测是不可能的,因为无法将无响应的进程与失败的进程区分开来。节点只能根据经过的时间使用P故障检测器进行推测。如前所述,这是通过 gossip 协议中的心跳来实现的。当副本怀疑主服务器已经死亡(通常是因为主服务器没有响应或者已经被分区)时,副本服务器启动本地排名过程,然后进行竞争投票,这是从它们各自的有利位置观察到的。一个代理大师可能没有意识到这个事件,他在一个它认为是正常运行的系统的范围内工作。我们称这些节点为僵尸。僵尸主机可能会继续接受写入并像往常一样复制它们,从而使之前的排序无效。碰巧的是,Redis 确实有一个防护机制,可以防止僵尸主机复制写操作,从而干扰新选出的主机。这是通过单调递增的configEpoch计数器实现的,该计数器由每个副本跟踪并与入站复制请求进行比较。僵尸的configEpoch总是比他们选出的继任者低,因此会被拒绝。满足标准 1(先导奇点)。然而,这一机制仅在领导人选举期间发挥作用,并在领导人任期内持续存在;在排名和投票之间有一段时间,僵尸主宰者可以自由支配其下属。基于不正确排名的投票可能会将过时的副本提升为主副本,在此过程中丢失已确认的写入。

有趣的是,就安全性而言,上面的故障条件并不是 Redis 唯一的失败。如果没有防备的僵尸主宰者被允许干扰原本清白的投票过程,那将是不幸的;然而,后者从一开始就远非完美无缺。从表面上看,投票过程似乎类似于流行的分布式共识算法,如 Viewstamped Replication (VR)、Zab 和 Raft,它们也是基于领导者-追随者拓扑结构,因为在任何给定的选举任期内不能投两次票,这保证了当选领导者在该任期内的排他性(标准 1)。然而,VR、Zab 和 Raft 规定只提升最新的副本(标准 4),并且这些规定是绝对的,而不是尽力而为或概率性的。Redis Cluster 依靠投票时间的微小差异来设置最佳副本,两个连续排名的竞争者之间的差异在 500 到 1000 毫秒之间。(这些界限是不可配置的,并且包括随机性元素,以最小化具有并列等级的竞争副本的群集效应。)投票可能被延迟或者可能完全超时;一个低劣的复制品可能会在追求领导地位的过程中超越一个优秀的竞争者。(这似乎与现实生活中的公司或政治阶梯没什么不同。)不幸的后果:领导者的故障可能导致在转换到新领导者的过程中丢失确认的写入,即使那些写入已经被复制到配置中的大多数副本。这解释了第 9 条—事实上,代表集群选择最佳副本的努力很少。几个因素—网络状态、副本中的时钟、进程调度中的延迟、传输延迟的可变性,以及最后的主服务器的状态(即,它是死的还是活的)—都会对提升正确副本的可能性产生负面影响。

题外话:上述基于领导者的共识协议在异步网络假设下是可验证安全的,在异步网络假设下,没有可靠的时间概念,网络链路可能失败或被分割,消息可能延迟或无序到达。这些协议可以使用超时来推断故障,从而满足它们的活性标准;然而,在影响安全的任何方面,他们都不依赖于时间的统一流逝。信不信由你,你不需要梦寐以求的 P 故障检测器来实现一个绝对安全的共识协议。鼓励读者熟悉共识算法,特别是 Leslie Lamport ( 【兼职议员 )、a.k.a. Paxos )和 Barbara lis kov(view stamped Replication)的开创性工作,以及相位对称圆形结构和合作共识( Spire )的最新研究。Redis 的维护者实施了一个自制的复制协议来支持一个已经在业界和学术界被仔细检查过的协议,这给他们自己造成了很大的伤害。

雪上加霜的是,我们无法以概率的方式轻松量化错误的副本升级导致的数据丢失。对于任何给定的配置,我们可以凭经验观察进程调度延迟和传输延迟的分布,以导出一个模型来帮助我们预测不完整副本被提升为领导者的可能性。作为单独的练习,我们还可以根据观察到的平均故障间隔时间(MTFB)来模拟故障转移的可能性。但是,在这种情况下,我们无法有意义地确定数据丢失的程度,因为理论上它可能是无限的——副本服务器可能落后于主服务器任意数量。在最不利的情况下,我们可能已经注册了一个新节点来增加复制因子;如果在新副本从空白状态同步的过程中发生故障转移,最坏的结果是全部数据丢失。类似地,一个现有的副本可能已经与其对等体进行了长时间的分区;如果分区合并与主分区同时发生,其影响可能同样是灾难性的。

这两种情况都比人们想象的更有可能发生。首先:当添加新的副本时,主节点和网络上的 IO 需求会增加,以使副本尽可能快地达到速度。网络和主机上的额外压力会导致资源饱和,并增加感知的故障的可能性。(记住,失败不一定是真实的;通过P故障检测器,延迟可以等同于故障。)对于第二种情况:分区通常是重新配置网络的结果;管理员可能会调整路由或防火墙规则,这两者都会导致分区被形成或恢复。或者,最关键的是,两者都有!先前被划分的(子)网络可以被合并,并且合并的网络可以被划分。我们都有过这样的尴尬时刻,友好的基础设施团队被要求解决一个问题,但最终在这个过程中引入了另一个问题。

推荐

提供的唯一明智的指导是,对于我们在此考虑的各种工作负载,着重避免 Redis 集群。当一个系统不能证明我们弱化的安全概念时,我们可以通过降低系统暴露和易受攻击的事件的可能性来使它变得稍微安全一点。因此,如果无情的约束阻止您从 Redis 切换到更合适的持久性堆栈,下面的内容可能会有所启发。

我们对脏读无能为力;这是你需要忍受的。使用WAIT对你没有帮助。

使用appendfsync everysec时缺乏 AOF 耐久性通常不是多节点配置中的问题,这些配置经过精心部署,以最大限度地减少共模故障。其他系统——特别是 Apache Kafka——也避免了本地写操作的fsync,以获得性能,这很有说服力。然而,他们(倾向于)使用 surefire 共识协议进行领导者分配(包括隔离)和复制。

在节点必须物理配置的地方,考虑启用appendfsync always。这可以防止轻微的共模故障(如断电)导致多个节点同时瘫痪。

我们可以通过过度指定群集来最小化不一致的副本升级的可能性。您可能不想满足于传统的 3 或 5 节点配置,而是希望过度配置集群,并将WAIT中的复制因子增加到明显大于简单多数。(WAIT将其replicas参数设为比所需的仲裁大小小一,因为它将主服务器从副本集中排除。)例如,一个集群有 7 个节点,复制因子为 6 ( WAIT 5 ... ) —一个主节点的故障会留下 5 个一致的副本和一个可能不一致的副本。显而易见的权衡:成本、增加的复制延迟和网络利用率,以及容忍更少故障的能力。

结论

Redis 集群在任何配置下都是不安全的,即使考虑到已经做出的所有让步。在默认配置中,Redis 的性能非常好,但是客观上不安全。当我们固定配置时,Redis 显示出边际改进,但是甚至不能满足我们愿意考虑的最宽松的安全属性,同时性能折衷变得明显。就目前情况来看,可以说“在任何速度下”都是不安全的。

Redis 对于特定的工作是一个有效的工具,并在其他关键领域做出了某些妥协。知道这些区域是什么!作为磁盘支持的高速缓存,Redis 已经无可匹敌。并且它的一些数据结构是非常好的。但是,如果您追求的是一个可扩展的 NoSQL 数据库,它提供一致性、持久性和某种程度的隔离(即,一个体面的安全概念),那么您会在市场上找到客观上更适合您需求的替代产品。

相关阅读

雷迪斯 vs 金斯伯里

在 2014 年对 Redis 的评估中,Kyle Kingsbury——臭名昭著的用于验证分布式系统安全性的 Jepsen 套件的作者——推测 Redis“不可能是一个 CP 系统”,是一个“弱一致性、尽力而为的服务,就像一个缓存”在一次常规测试中,金斯伯里展示了在一次网络分区中丢失了高达 56%的数据。Kingsbury 的发现是基于 Redis 早期版本的完整裂脑场景,运行 Sentinel——Redis 集群的前身。Kingsbury 最后的建议:Redis Sentinel(以及 Redis Cluster)作为数据库、锁服务或队列使用是不安全的。虽然几个问题已经得到解决,但复制协议基本上保持不变。

Redis vs Hazelcast

2018 年,Redis 实验室的 Keren Ouaknine 发布了一项基准测试,显示 Redis 集群据称在具有持久持久性的写密集型工作负载中消灭了 Hazelcast 一种开源的内存数据网格。这出戏在 2019 年得到了 Hazelcast 的首席技术官 Greg Luck 的回报,他能够证明 Redis 集群中的数据丢失与这里讨论的一致性副本提升问题无关。在一个类似的基准中,Luck 表明,由于所做的耐久性牺牲,结果不具有直接可比性,这一点作者要么没有披露,要么更有可能是没有意识到。在同等条件下,Hazelcast 明显更快。

在不使用WAIT的情况下,当写入吞吐量达到一定(适度高)水平时,负责对复制副本的复制命令进行排队的主服务器传输缓冲区将溢出,导致复制副本立即断开连接。这将导致无休止的循环——副本重新连接,缓冲区立即不堪重负,连接又被丢弃。只有当负载减少时,副本才能保持足够稳定的连接,最终赶上并保持同步。Luck 指出WAIT似乎可以解决同步问题,但是用 Luck 的话来说,它的使用将“显著降低 Redis 的吞吐量”,这与其他基准一致。勒克在结束语中总结道:“如果 Redis 用户认为建立副本意味着他们有数据冗余,并且需要多次失败才能丢失数据,那他们就错了。”

这篇文章对你有用吗?我很想听听你的反馈,所以不要退缩。如果你对 Kafka,Kubernetes,微服务,或者事件流感兴趣,或者只是有什么疑问, 在 Twitter 上关注我 。我也是Kafdrop的维护者, 有效的卡夫卡 尖顶 分布式共识算法的发明者。

重新发现单元测试:ML 模型的测试能力

原文:https://towardsdatascience.com/rediscovering-unit-testing-testing-capabilities-of-ml-models-b008c778ca81?source=collection_archive---------18-----------------------

身份鉴定有什么问题吗?我们如何识别能力?我们如何创建测试?该模型能推广到非分布数据吗?

受两篇论文[1,2]的启发,我最近一直在阅读和思考关于机器学习模型的测试。除了测量模型的平均预测精度之外,似乎还有一种测试模型能力的增长趋势,这让我想起了许多单元测试,并强烈地改变了如何考虑模型评估。

简而言之,我们可以尝试评估模型是否学习了某些能力,通常是问题的关键概念,要避免的常见错误的模式,或者人类如何处理任务的策略,而不是仅仅测量某个目标人群中的概括。我们可以测试一个模型是否已经分别学习了这些能力,这不同于标准的准确性评估,更类似于黑盒单元测试。总的来说,希望是一个已经学习了预期能力的模型将对非分布数据和攻击更加健壮

传统精度评估及其假设

为了理解测试能力的相关性,理解传统准确性评估背后的假设和限制是很重要的。

示例机器学习挑战:在放射学图像中检测癌症。图片:伊万·萨姆科夫

首先,在评估模型时,我们不是测试模型对于所有输入是否都是"正确的(我们通常甚至没有一个规范来确定正确可能意味着什么),而是测试它对于来自目标人群的输入是否适合,或者它对于一个问题是否有用。记住*所有的模型都是错的,但有些是有用的*——我们在努力识别有用的。一个准确率为 95%的模型可能非常适合一个问题,而对于一个问题的有用解决方案来说,5%的错误是可以接受的,比如说在放射图像中检测癌症。

第二,我们评估独立于训练数据的测试数据的模型,以测量该模型对看不见的数据的概括程度。重要的是,我们不关心对任何数据的推广,而只关心对来自 T2 目标人群的数据的推广。例如,我们不会期望癌症检测模型在风景摄影上工作得很好,但是我们希望它在用某些设备拍摄的放射学图像上工作得很好。与目标人群不匹配的数据称为非分布数据。

许多机器学习理论和实践中的一个关键假设(不总是明确的;我花了一段时间才意识到)就是训练和测试数据都是独立抽取自同一个目标人群***——独立同分布或简称 i.i.d. 。主要策略是从目标人群中收集单个数据集,然后将其随机分为训练数据和测试数据,确保两者来自同一分布。任何 i.i.d .评估策略都将衡量该模型在该人群中的推广程度。也就是说,我们从我们想要推广的目标人群中选择训练数据,比如一家医院的放射学图像,然后根据从相同人群中提取的测试数据对其进行评估,以衡量该模型在该目标人群 中推广的效果。***

重要的是,如果训练和测试数据不是预期目标人群的代表性、无偏见的样本,整个方案就会失败。例如,如果我们希望我们的癌症检测器适用于所有类型的设备和患者统计数据,但我们在单个医院的单个扫描仪的扫描上对其进行了训练和测试,我们的模型可能会很好地推广到用于训练和测试的分布,但可能仍然不能很好地用于预期的目标人群。数据收集方式的系统偏差和目标分布随时间的变化是我们的培训和测试分布可能与目标分布不一致的进一步原因——这是在尝试部署机器学习解决方案时常见的问题。

训练和测试数据并不总是代表全部目标人群。除了目标人群,当然还有许多与问题无关的输入。图片作者。

超越训练分布的一般化

理想情况下,我们希望模型能够很好地推广到看不见的数据,甚至是与模型的训练数据所代表的群体不完全匹配的数据。如果我们想将一个问题从一个群体转移到另一个群体,比如说,在一家医院的设备和患者上训练的癌症模型转移到在其他设备上拍摄的放射学图像,或者转移到在原医院中没有很好代表的患者人口统计数据,这是很有用的。当世界和目标人群发生变化时,例如当一种新的时尚饮食导致非癌性放射学成像伪影的异常形式时,这种伪影在收集训练数据时或当新的放射学硬件被不同地校准时是不存在的,这同样是有用的。

事实上,与之前对 i.i.d .数据的离线评估相比,在生产中看到机器学习模型的准确性较低是非常常见的。生产数据的分布通常不同于训练数据的分布,这可能是因为最初没有代表性地采样,或者是因为随着世界的发展,分布发生了变化。

人们可以争辩说(许多机器学习研究人员也有力地争辩说)我们没有理由批评一个对非分布数据做出错误预测的模型。这类似于批评一个小学生在高中数学考试中没有取得好成绩。为什么我们会期望一个模型对我们还没有教会它的事情做出准确的预测?

对实际目标分布中的不良表现的典型反应是收集更多的训练数据以更好地代表目标分布,在采样方法中寻找潜在的偏差以确保代表性,并随着目标分布的变化更新训练和测试数据。

然而,我们希望我们可以找到一种训练模型的方法,使它们能够更好地泛化,甚至超越训练数据,这样,这些模型对训练数据收集中的潜在偏差更加鲁棒,对分布变化甚至敌对攻击更加鲁棒。

能力的承诺

最近在一些文献中出现的一种识别模型的策略是,尝试区分已经学习了人类发现对解决任务至关重要的关键能力的模型。简单地说,我们正在寻找更好地反映人类解决问题的策略的模型(或使用我们对该问题的其他形式的领域知识),希望这些模型比模型可能提出的任何替代策略都更健壮。

功能本质上是特定于领域的,我看到的大多数讨论都是围绕 NLP 任务进行的,但这里有几个不同的例子:

  • 在情感分析中,一个模型应该能够理解否定表情符号,以及讽刺*。***
  • 在对象检测中,模型不应该主要依赖于图像的背景,而应该更喜欢形状而不是纹理(就像人类一样)。
  • 在问题回答中,模型应该能够推理出数字同义词,而不应该仅仅因为问题和答案之间的单词重叠而分心。**
  • 在癌症检测中,模型应该像放射科医生一样在相同的区域寻找相同的模式。

对象检测模型应该在不受背景干扰的情况下推理所讨论的对象。Beery 等人【9】的例子和图片。

简而言之,关于这种做法可能奏效的理由如下:

  1. ****对于每个训练问题,我们可以找到许多不同的模型,这些模型在训练和测试数据的分布上具有相似的准确性,但它们学习的模式非常不同。其中一些模型比其他模型更能概括非分布数据。事实上,引导我进入这个兔子洞的谷歌论文[2]有几十个例子显示了许多学习问题的这种模式。
  2. 一些模型学习特定于用于训练和测试的分布的数据模式,但是不针对更大的问题进行推广。 数据集上捷径学习的例子比比皆是:物体检测器检测到草地上的奶牛,却检测不到沙滩上的奶牛,因为训练数据从来不包括沙滩上的奶牛[9];坦克探测器的城市传说学会了如何识别天气而不是坦克,因为所有有坦克的训练数据都是在晴天拍摄的,没有坦克的数据都是在雨天拍摄的;癌症检测器了解图像是在移动还是固定扫描仪上拍摄的,根据人类的判断判断患者是否足够健康,可以移动到扫描仪上[10]。在所有这些情况下,模型都找到了在训练和测试数据中运行良好的模式,但这仅仅是因为收集数据的方式不能代表模型最终应该使用的目标分布。
  3. ****我们希望我们认为对解决问题很重要的学习模式,比如上面列出的能力,能够更好地推广。基本原理是,能力代表我们已经拥有的领域知识,而不仅仅是在特定数据集中发现的模式,这些模式可能代表数据收集方式偏差导致的数据捷径。例如,我们期望在物体检测器中检测牛的形状应该是比分析图像的背景更健壮的机制。
  4. 因此,每当我们在具有相似精确度的多个模型之间进行选择时,我们应该选择具有更好学习相关能力的模型。我们甚至可能想要引导我们的训练(例如,通过模型结构、归纳偏差或训练数据扩充)来学习这些能力。请注意,这种方法并不像在符号人工智能的好日子里那样,只针对推理和固定的能力列表。我们仍然从训练数据中学习模型,这些数据可能会发现各种模式,其中许多模式人类可能永远不会发现,但我们控制这个过程,以包括一些模式,这些模式为模型提供一些我们认为重要的功能,并将使模型更加健壮。**

测试能力

让我们暂时搁置我们如何识别能力,假设我们已经知道我们想要测试的能力,比如情绪分析中的理解否定。同样,我们不测试模型是否正确地学习了功能(即,我们不寻找单独的反例),而是评估模型对于受益于该功能的问题有多适合。****

为了测试一个能力,我们管理特定于能力的测试数据,独立于传统上用于一般准确性评估的 i.i.d .测试数据,并与训练数据的分布相匹配。也就是说,我们为每个功能单独创建测试数据,这样,拥有该功能的模型将比没有该功能的模型在该测试数据上实现更高的预测准确性。

有几种不同的策略:

  • ****特定领域生成器:一个非常常见的策略是从模板中生成测试数据,或者用特定的策略来生成需要该能力的数据示例[1,3,5,8]。比如测试一个情感分析模型是否理解否定,模板“I the 。”可以自动填充各种否定、动词和事物,以生成许多测试句子,如“我不喜欢这种食物”这些句子都包含否定,并且都被认为具有消极情绪,尽管有积极的词语[1]。作为另一个例子,生成器可以创建具有一个对象的形状但填充了另一个对象的纹理的人工图像,以测试对象识别模型偏好形状而非纹理的能力[5]。人们通常需要为每个功能编写一个或多个生成器。

人工生成一张纹理-形状冲突的图片,以测试模型优先考虑形状而不是纹理的能力。Geirhos 等人【5】的例子和图片。

  • **改变现有的输入:不是从零开始生成测试输入,而是通过修改现有的输入来生成新的测试输入[3,6]。例如,为了测试 NLP 模型理解同义词的能力,我们可以用同义词替换现有标记句子中的单词,以创建具有相同标签的新测试数据。对于与噪音和干扰相关的功能,我们也可以轻松地修改输入,为句子添加中性信息(例如,添加“和假不真” [3]或随机 URL[1])或自动引入错别字[3]。如果能力可以被表达为不变量(见变形测试)来描述一个模型的预测对于某些类型的修改应该如何改变(或不改变),那么突变是特别有效的。
  • ****众包测试创建:当我们不能自动生成真实的测试时,我们可以让人类参与到任务中[4,6]。例如,这种策略可以用于测试情绪分析模型理解讽刺的能力,方法是要求人群工作者通过引入讽刺来最小程度地修改电影评论以翻转其情绪[6];对于一个物体探测器,我们可以让人群工作者用不同的,可能是意想不到的背景来拍摄一个物体的照片[4]。在每种情况下,我们都需要为人类创建特定的指令,以创建挑战我们感兴趣的特定能力的测试用例。

人群工作者为改变一个句子(红色替换为蓝色)的情绪而做出的微小改变的例子,需要模型具有区分事实和希望、检测讽刺和理解修饰语的能力。来自 Kaushik 等人【6】的示例

  • ****切片测试数据:最后,我们还可以在一个大的测试数据池中搜索与我们的能力相关的测试实例,例如,所有包含否定的句子。如果我们想避免从原始测试数据中取样,我们也可以在生产中收集以前未标记的数据,识别潜在的挑战性案例,然后将它们标记为我们能力的测试数据。这种测试数据切片的策略在测试模型中已经很常见,用于检查重要子群体的准确性和公平性——它也可能用于功能。

这些为能力筛选测试数据的策略都不便宜。除了已经为获取原始训练和测试数据所做的投资之外,编写生成器或雇佣人员来创建或标记测试数据涉及到大量的工作和成本。然而,正如所讨论的,希望在这些能力上做得更好的模型在训练数据的分布之外概括得更好,从多篇论文中得到有希望的结果。

旁白:培训与测试。这里的大多数讨论都有培训和测试的双重见解。正如我们可以筛选测试数据来测试模型是否已经学习了特定的能力,我们通常可以使用相同的过程来生成额外的训练数据,以驱动训练来更好地学习该能力。这种数据扩充是常见的,并且超出了 i.i.d. 的假设,就像测试能力一样,但是潜在地具有相同的好处。为了避免陷入另一个兔子洞,我不打算进入不同的数据增强策略以及它们如何与能力相关联,但是我期望能力方面的思考可能不仅是制定测试策略的途径,也是培训策略的途径。相反,数据扩充论文[例如,6]可以从识别能力的角度阅读,也可以为测试提供灵感。

识别能力

****现在剩下的最后一个问题是:我们如何识别要测试的能力?令人惊讶的是,我没有看到任何论文明确讨论这一点。许多论文关注特定问题的单个功能或少数功能,并展示测试和检测更好模型的可行性,但通常没有任何理由说明为什么选择该功能而不是其他功能。字里行间似乎有几个不同的想法:

  • ****分析常见错误:大多数论文似乎都专注于模型所犯的特定常见错误[3,4,5,8],其中大多数都是捷径推理,例如当模型使用图像的背景来识别前景中的对象时。一些常见的问题已经被社区很好地理解和广泛地研究,例如 NLP 模型使用单词重叠而不是理解文本的内容[8]或者模型关注纹理而不是形状[5],使能力成为明确的候选。更系统地说,我们可以仔细分析一个有代表性的错误样本,以确定常见的问题类型,并将这些问题映射到能力——压力测试论文[3]就是一个很好的例子:研究人员对自然语言推理任务的模型所犯的 100 个错误进行了人工分类,发现除了单词重叠之外,错误输出通常与否定和反义词、数字推理、歧义和缺失的现实世界知识有关。虽然有些问题,比如缺少真实世界的知识,对模型(和人类)来说确实很难,但其他问题可以更容易地作为模型应该具有的可测试能力来捕获。

一个自然语言推理模型的错误分析:X->Y 表示 X 是预期的,但 Y 是预测的,选项有蕴涵(E,假设给定前提为真),矛盾(C,假设给定前提为假),或中性(N,真值无法确定)。纳伊克等人的表格[3]。

  • **利用关于问题的现有知识:**虽然我们通常会使用机器学习,因为我们并不完全了解如何解决问题,但对于许多问题,我们至少有一些部分了解。例如,在深度神经网络出现之前,语言学已经被研究了很长时间,我们知道很多关于,比如说,句子结构,单词如何联系,以及句子的哪些部分比其他部分更重要。许多功能似乎与我们已经拥有的关于问题的理论直接相关,包括来自清单论文[1]的 NLP 模型的大多数功能,包括同义词、匿名、识别命名实体、语义角色标记、否定和共指。
  • 观察人类:在我们没有领域知识的地方,我们可以研究人类如何解决问题。一个有趣的例子是Learning the difference that make the difference论文[4],该论文观察了当被指示对一个句子进行最小程度的修改以改变其情感时,人类对文本做出了什么样的改变(示例如上图所示)。通过这种方式,他们确定了改变情绪的重要机制(通常但不一定对模型具有挑战性),这些机制可以映射到能力,如讽刺和区分事实与愿望。**
  • 源自需求:**一些能力对应于模型或期望目标的需求,通常是最终模型应该保持的不变量。这尤其包括公平性要求,这些要求在原始训练数据中可能不成立或不可见。例如,情感分析不应该根据句子中演员的性别而不同。
  • ****从观察数据中发现因果关系:机器学习社区的一个子领域专注于编码和发现因果关系,而不仅仅是统计关系[7]。如果我们能够发现和回顾因果关系,许多可能很好地映射到能力。

虽然我没有看到任何人明确地提供关于如何识别实际质量保证或模型开发活动的能力的指导,但是对常见问题、关于问题和非 ML 解决方案的知识以及需求的仔细分析似乎可以用来识别许多问题的能力。

如果我们最后一次查看癌症检测模型的示例,我们会发现我们可以使用这些策略中的许多策略:我们可能会发现,当亮度没有在多个扫描仪上校准时,现有的解决方案表现不佳(分析模型错误),因此识别模型应该对不同亮度水平具有鲁棒性的能力,我们可以将这种能力很好地转化为测试数据,以识别那些学习这种能力的模型。我们可能会问放射科医生为什么他们不同意一个模型,以了解更多专家使用的模型可能缺少的功能(观察人类)。此外,我们可能还应该研究深度学习之前的文献,比如 90 年代早期医学成像研究中使用的策略,这些策略通常利用对问题的特定见解作为手工制作的数学模型的一部分。此外,关于癌症诊断的非机器学习文献也可能识别放射科医师在放射科医师的经验研究或放射科医师的培训材料(现有知识)中寻找癌症时使用的能力。**

为了总结这一点,现在让我回到单元测试:识别功能,然后为它创建测试数据,这与创建单元测试时选择输入没有什么不同。虽然我们对机器学习问题没有强有力的规范,但我们对这个问题和过去的错误有一些了解。识别能力就像在不看程序实现的情况下为程序选择测试输入一样,被称为黑盒测试或基于规格的测试。从常见错误中识别功能,并开发测试以确保它们通常在未来的模型中被避免,这类似于回归测试,开发人员添加测试用例以避免未来的变化破坏现有的功能。

读物

[1]里贝罗、、吴同双、卡洛斯·盖斯特林和萨梅尔·辛格。"超越准确性:使用清单对 NLP 模型进行行为测试."美国 ACL 会议论文集,第 4902–4912 页。(2020).

[2] D'Amour,Alexander,Katherine Heller,Dan Moldovan,Ben Adlam,Babak Alipanahi,Alex Beutel,Christina Chen 等.指定不足对现代机器学习的可信度提出了挑战。arXiv 预印本 arXiv:2011.03395 (2020)。

[3]纳伊克、阿坎克沙、阿比拉莎·拉维钱德尔、诺曼·萨德赫、卡罗琳·罗斯和格雷厄姆·纽比格。"自然语言推理的压力测试评估"第 27 届计算语言学国际会议论文集,第 2340–2353 页(2018)。

[4]巴尔布、安德烈、戴维·梅奥、朱利安·阿尔韦里奥、威廉·罗、克里斯托弗·王、丹尼·古特弗罗因德、约书亚·特南鲍姆和鲍里斯·卡茨。 ObjectNet:一个大规模偏倚控制的数据集,用于推动对象识别模型的极限。进行中。NeurIPS (2019)。

[5]盖尔霍斯、罗伯特、帕特里夏·卢比什、克劳迪奥·米切里斯、马蒂亚斯·贝赫、费利克斯·a·威奇曼和维兰德·布伦德尔。" ImageNet 训练的 CNN 偏向于纹理;增加形状偏差可以提高精确度和鲁棒性。进行中。学习代表国际会议(ICLR)(2019)。

[6] Kaushik、Divyansh、Eduard Hovy 和 Zachary C. Lipton。"学习与反事实增强数据的差异。"进行中。学习表征国际会议(ICLR)(2020)。

[7]谢尔科普夫,伯恩哈德。"机器学习的因果关系" arXiv 预印本 arXiv:1911.10500 (2019)。

[8]麦科伊、r .托马斯、埃莉·帕夫利克和塔尔·林森。正确的原因是错误的:诊断自然语言推理中的句法启发。继续。ACL (2019)。

[9]贝利、萨拉、格兰特·范·霍恩和彼得罗·佩罗娜。"未知领域中的识别"在欧洲计算机视觉会议(ECCV) 的会议记录中,第 456–473 页。2018.**

[10] Agrawal、Ajay、Joshua Gans 和 Avi Goldfarb。预测机器:人工智能的简单经济学。哈佛商业出版社,2018。

PS:关于命名的一句话。测试能力的想法隐含在许多论文和讨论中,但很少被有意命名为策略。我找到的论文,大部分是通过跟踪其他论文中的参考文献找到的。纳伊克等人[3]将测试能力的思想介绍为“压力测试模型”,甚至引用了一些软件工程教科书;不规范论文[2]采用了该术语。不过,我发现“压力测试”具有误导性:压力测试通常是指在高负载和随机性下测试性能和错误处理,但往往不太结构化。清单论文【1】引入了术语“测试能力”,我更喜欢这个术语。“能力”的概念表明,有多种不同的特征应该被测试,而不会与“需求”“规格”混淆,这是我们在处理机器学习问题时经常不具备的。此外,我认为黑盒单元测试通常是一个有用的类比,因为我们正在测试系统的单个单元(模型),并且我们在不知道模型内部的情况下以某种系统化的方式这样做,一次一个能力。

行动中的再研究

原文:https://towardsdatascience.com/redisearch-in-action-redis-labs-52b170b56b43?source=collection_archive---------37-----------------------

一个实际的例子:实时摄取推文,并使用 Redis 灵活地查询它们

Redis 有一套通用的数据结构,从简单的字符串一直到强大的抽象,如 Redis 流。原生数据类型可以带您走很长一段路,但是有些用例可能需要一个解决方法。一个例子是 Redis 中使用二级索引的要求,以便超越基于关键字的搜索/查找,获得更丰富的查询功能。虽然您可以使用有序集合、列表等等来完成工作,但是您需要考虑一些权衡。

输入重新搜索!Redis 模块提供的 RediSearch 提供了灵活的搜索功能,这要归功于一流的二级索引引擎。它提供了强大的功能,如全文搜索、自动完成、地理索引等等。

为了展示 rede search 的强大功能,这篇博客文章提供了一个实际的例子,说明如何在使用rede search Go 客户端构建的 Go 服务的帮助下,通过 Azure Cache for Redis 使用 rede search。它旨在给你一套应用程序,让你实时摄取推文,并使用 RediSearch 灵活地查询它们。

具体来说,您将学习如何:

  • 使用重新搜索索引
  • 使用不同的重新搜索数据类型,如TEXTNUMERICTAG
  • 如何构建一个应用程序来显示重新搜索功能
  • 如何用几个命令将服务组件部署到 Azure
  • 通过查询 RediSearch 分析推文数据

应用概述

如前所述,示例服务允许您实时使用 tweets,并通过 RediSearch 进行查询。

(图片由作者提供)

它有两个组成部分:

  1. **消费者/索引器:**读取 Twitter 流 API,创建索引,并在到达时不断添加 tweet 数据(在 Redis 散列中)。
  2. **搜索服务:**REST API,允许你使用再搜索查询语法搜索推文。

现在,我将深入探讨如何启动并运行该解决方案,以便您可以看到它的实际应用。然而,如果你有兴趣了解各个组件是如何工作的,请参考下面的部分的代码遍历,以及本博客的 GitHub repo:https://github.com/abhirockzz/redisearch-tweet-analysis。

先决条件

  1. 首先,你需要一个微软 Azure 账户:在这里免费获得一个!
  2. 上面列出的服务组件将使用本地 Docker CLI 命令部署到 Azure 容器实例。Docker 和 Azure 之间的集成实现了这一功能。
  3. 你需要 Docker 桌面版 2.3.0.5 或更高版本,用于 Windows 、 macOS ,或者安装 Docker ACI 集成 CLI 用于 Linux 。要使用 Twitter 流 API,您还需要一个 Twitter 开发者帐户。如果您还没有,请遵循这些说明。

首先,使用这个快速入门教程在 Azure 上设置 Redis 企业级缓存。完成设置后,确保手头有 Redis 主机名和访问密钥:

我们服务的两个组件都可以作为 Docker 容器获得:Tweet 索引服务和搜索 API 服务。(如果您需要构建自己的 Docker 映像,请使用 GitHub repo 上提供的相应 Docker 文件。)

现在,您将看到将这些部署到 Azure 容器实例是多么方便,这允许您在托管的、无服务器的 Azure 环境中按需运行 Docker 容器。

部署到 Azure

version: "2"
services:
  tweets-search:
    image: abhirockzz/redisearch-tweets-search
    ports:
      - 80:80
    environment:
      - REDIS_HOST=<azure redis host name>
      - REDIS_PASSWORD=<azure redis access key>
      - REDISEARCH_INDEX_NAME=tweets-index
  tweets-indexer:
    image: abhirockzz/redisearch-tweets-consumer
    environment:
      - TWITTER_CONSUMER_KEY=<twitter api consumer key>
      - TWITTER_CONSUMER_SECRET_KEY=<twitter api consumer secret>
      - TWITTER_ACCESS_TOKEN=<twitter api access token>
      - TWITTER_ACCESS_SECRET_TOKEN=<twitter api access secret>
      - REDIS_HOST=<azure redis host name>
      - REDIS_PASSWORD=<azure redis access key>
      - REDISEARCH_INDEX_NAME=tweets-index

一个docker-compose.yml文件定义了各个组件(tweets-search 和 tweets-indexer)。你所需要做的就是更新它来替换你的 Azure Redis 实例的值以及你的 Twitter 开发者帐户凭证。这是完整的文件:

创建 Azure 上下文:

docker login azure 
docker context create aci aci-context 
docker context use aci-context

克隆 GitHub repo:

git clone https://github.com/abhirockzz/redisearch-tweet-analysis 
cd redisearch-tweet-analysis

将两个服务组件作为容器组的一部分进行部署:

docker compose up -p azure-redisearch-app

注意,Docker 编写当前在 ACI 环境中可用的命令以 *docker compose* 开始。那跟 *docker-compose*不一样用连字符。

您将看到类似如下的输出:

[+] Running 1/3 
⠿ Group azure-redisearch-app Created 8.3s ⠸ tweets-search Creating 6.3s ⠸ tweets-indexer Creating 6.3s

等待服务启动,你也可以查看 Azure 门户。一旦这两个服务都启动并运行,您可以检查它们各自的日志:

docker logs azure-redisearch-app_tweets-indexer 
docker logs azure-redisearch-app_tweets-search

如果一切顺利,推特消费者服务应该已经启动。它将读取一系列推文,并将它们保存到 Redis。

关键时刻到了。

是时候查询推文数据了。为此,您可以使用 IP 地址和完全限定的域名(FQDN)访问 Azure 容器实例中的 REST API(在容器访问中了解更多)。要找到 IP,运行docker ps并检查输出中的PORTS部分

您现在可以运行各种查询了!在开始之前,这里有一个可以在搜索查询中使用的索引属性的快速概念:

**id** - this is a the Tweet ID ( TEXT attribute) 
**user** - the is the screen name ( TEXT attribute) 
**text** - tweet contents ( TEXT attribute) 
**source** - tweet source e.g. Twitter for Android, Twitter Web App, Twitter for iPhone ( TEXT attribute) 
**hashtags** - hashtags (if any) in the tweet (available in CSV format as a TAG attribute) 
**location** - tweet location (if available). this is a user defined location (not the exact location per se) 
**created** - timestamp (epoch) of the tweet. this is NUMERIC field and can be used for range queries 
coordinates - geographic location (longitude, latitude) if made available by the client ( GEO attribute)

(注意,我在下面的例子中使用了卷曲

设置搜索服务 API 的基本 URL:

export REDISEARCH_API_BASE_URL=<for example, http://20.197.96.54:80/search>

启动 simple 并查询所有文档(使用* ):

curl -i $REDISEARCH_API_BASE_URL?q=*

您将看到类似如下的输出:

HTTP/1.1 200 OK 
Page-Size: 10 
Search-Hits: 12 
Date: Mon, 25 Jan 2021 13:21:52 GMT 
Content-Type: text/plain; charset=utf-8 
Transfer-Encoding: chunked 
//JSON array of documents (omitted)

注意头文件Page-SizeSearch-Hits:这些是从应用程序传递来的自定义头文件,主要用于演示分页和限制。对于我们的“获取所有文档”查询,我们在 Redis 中找到了 12 个结果,但是 JSON 主体返回了 10 个条目。这是因为 RediSearch Go API 的默认行为,您可以使用不同的查询参数来更改该行为,例如:

curl -i "$REDISEARCH_API_BASE_URL?q=*&offset_limit=0,100" 

offset_limit=0,100 will return up to 100 documents ( limit ) starting with the first one ( offset = 0).

或者,例如,搜索从iPhone发来的推文:

curl -i "$REDISEARCH_API_BASE_URL?q=@source:iphone"

您可能不总是需要查询结果中的所有属性。例如,这就是如何取回用户(Twitter 屏幕名称)和 tweet 文本:

curl -i "$REDISEARCH_API_BASE_URL?q=@location:india&fields=user,text"

对用户名进行查询怎么样(例如从jo开始):

curl -i "$REDISEARCH_API_BASE_URL?q=@user:jo*"

您也可以在查询中使用属性的组合:

bash curl -i $REDISEARCH_API_BASE_URL?q=@location:India @source:android

不如我们找找有特定标签的推文?可以使用多个 hashtags(用|分隔)?

curl -i "$REDISEARCH_API_BASE_URL?q=@hashtags:\{potus|cov*\}"

想知道最近有多少带有biden标签的推文被创建了吗?使用范围查询:

curl -i "$REDISEARCH_API_BASE_URL?q=@hashtags:{biden} @created:[1611556920000000000 1711556930000000000]"

如果你幸运地在 tweets 上获得了一些坐标信息,你可以尝试提取它们,然后查询坐标属性:

docker compose down -p azure-redisearch-app

这些只是几个例子。请随意进一步试验,尝试其他查询。重新搜索文档中的这一部分可能会派上用场!

打扫

**重要提示:**完成后,不要忘记停止 Azure 容器实例中的服务和各自的容器:

使用 Azure 门户删除您已经创建的 Azure Redis 实例。

代码遍历

本节提供了单个组件代码的高级概述。这将使在 GitHub repo 中导航源代码变得更加容易。

推文消费者/索引器:

go-twitter 库已经用于与 twitter 交互。

它向 Twitter 流 API 认证:

config := oauth1.NewConfig(GetEnvOrFail(consumerKeyEnvVar), GetEnvOrFail(consumerSecretKeyEnvVar)) 
token := oauth1.NewToken(GetEnvOrFail(accessTokenEnvVar), GetEnvOrFail(accessSecretEnvVar)) 
httpClient := config.Client(oauth1.NoContext, token) 
client := twitter.NewClient(httpClient)

并在单独的 goroutine 中收听一系列推文:

demux := twitter.NewSwitchDemux() 
demux.Tweet = func(tweet *twitter.Tweet) { 
  if !tweet.PossiblySensitive { 
    go index.AddData(tweetToMap(tweet)) 
    time.Sleep(3 * time.Second) 
  } 
} 
go func() { 
  for tweet := range stream.Messages { 
    demux.Handle(tweet) 
  } 
}()

注意go index.AddData(tweetToMap(tweet))——这是索引组件被调用的地方。它连接到 Redis 的 Azure 缓存:

host := GetEnvOrFail(redisHost) 
password := GetEnvOrFail(redisPassword) 
indexName = GetEnvOrFail(indexNameEnvVar) pool = &redis.Pool{Dial: func() (redis.Conn, error) { 
  return redis.Dial("tcp", host, redis.DialPassword(password), redis.DialUseTLS(true), redis.DialTLSConfig(&tls.Config{MinVersion: tls}
  }

然后,在重新创建索引之前,它会删除索引(以及现有的文档):

rsClient := redisearch.NewClientFromPool(pool, indexName) err := rsClient.DropIndex(true) schema := redisearch.NewSchema(redisearch.DefaultOptions). 
AddField(redisearch.NewTextFieldOptions("id", redisearch.TextFieldOptions{})). 
AddField(redisearch.NewTextFieldOptions("user", redisearch.TextFieldOptions{})). 
AddField(redisearch.NewTextFieldOptions("text", redisearch.TextFieldOptions{})). 
AddField(redisearch.NewTextFieldOptions("source", redisearch.TextFieldOptions{})). 
//tags are comma-separated by default 
AddField(redisearch.NewTagFieldOptions("hashtags", redisearch.TagFieldOptions{})). 
AddField(redisearch.NewTextFieldOptions("location", redisearch.TextFieldOptions{})). 
AddField(redisearch.NewNumericFieldOptions("created", redisearch.NumericFieldOptions{Sortable: true})). 
AddField(redisearch.NewGeoFieldOptions("coordinates", redisearch.GeoFieldOptions{})) indexDefinition := redisearch.NewIndexDefinition().AddPrefix(indexDefinitionHashPrefix) err = rsClient.CreateIndexWithIndexDefinition(schema, indexDefinition)

索引及其相关文档被删除,以便您可以从一个干净的状态开始,这使得实验/演示更加容易。如果您愿意,可以选择注释掉这一部分。

使用HSET操作将每条 tweet 的信息存储在HASH(名为tweet:<tweet ID>)中:

func AddData(tweetData map[string]interface{}) { 
conn := pool.Get() 
hashName := fmt.Sprintf("tweet:%s", tweetData["id"]) 
val := redis.Args{hashName}.AddFlat(tweetData) 
_, err := conn.Do("HSET", val...) 
}

Tweets 搜索公开了一个 REST API 来进行查询重搜索。所有选项(包括查询等。)是以查询参数的形式传递的。比如http://localhost:8080/search?q = @来源:iphone 。提取所需的查询参数:

qParams, err := url.ParseQuery(req.URL.RawQuery) 
if err != nil { 
log.Println("invalid query params") 
http.Error(rw, err.Error(), http.StatusBadRequest) 
return 
} 
searchQuery := qParams.Get(queryParamQuery) 
query := redisearch.NewQuery(searchQuery)

q参数是强制的。但是,您也可以使用以下参数进行搜索:

  • 字段:指定你想在结果中返回哪些属性,
  • offset_limit :如果您想要指定您想要搜索的位置的偏移量以及您想要包含在结果中的文档数量(默认情况下,offset 为 0,limit 为 10 —根据 RediSearch Go 客户端)。

例如:

http://localhost:8080/search?q=@source:Web&fields=user,source&offset_limit=5,100 
fields := qParams.Get(queryParamFields) 
offsetAndLimit := qParams.Get(queryParamOffsetLimit)

最后,结果被迭代并作为 JSON(文档数组)传回:

docs, total, err := rsClient.Search(query) 
response := []map[string]interface{}{} 
for _, doc := range docs { 
response = append(response, doc.Properties) 
} 
rw.Header().Add(responseHeaderSearchHits, strconv.Itoa(total)) 
err = json.NewEncoder(rw).Encode(response)

本节到此为止!

Azure Cache for Redis 上的 Redis 企业层

Redis Enterprise 作为 Azure 上的原生服务,以两个新的 Redis Azure 缓存层的形式提供这两个缓存层由微软和 Redis 实验室运营和支持。这项服务使开发人员能够访问 Redis 企业版的丰富功能,包括像 RediSearch 这样的模块。有关更多信息,请参见以下资源:

  • 面向 Redis 企业级的 Azure 缓存现已正式推出
  • 深化我们与微软的合作伙伴关系,在云中发展 Redis 企业
  • 微软和 Redis 实验室合作为开发者提供新的 Azure Cache for Redis 功能
  • Redis 的 Azure Cache 上的 Redis Enterprise 特性

结论

这个端到端的应用程序演示了如何使用索引,接收实时数据以创建文档(tweet 信息),这些文档由 RediSearch 引擎进行索引,然后使用通用的查询语法来提取对这些 tweet 的见解。

想了解当您在 Redis Labs 文档上搜索一个主题时幕后发生了什么吗?查看这篇博文了解 Redis 实验室网站如何将全文搜索与 RediSearch 结合在一起!或者,您可能对探索如何在无服务器应用程序中使用 RediSearch 感兴趣。

如果您仍在起步阶段,请访问重新搜索快速启动页面。

如果您想了解更多关于 Azure Cache for Redis 的企业功能,可以查看以下资源:

原载于 2021 年 3 月 30 日https://redislabs.com

使用 Python 减少 GeoTIFF 磁盘空间的使用

原文:https://towardsdatascience.com/reduce-geotiff-disk-space-usage-with-python-3b394b6c0572?source=collection_archive---------18-----------------------

导出大型栅格文件的良好实践

克里斯蒂安·科伦的【二进制代码】在 CC BY-SA 2.0 下获得许可

GeoTIFF 文件的大小有时绝对令人头疼。计算资源可用性的激增诱使我们在大量卫星数据上部署(或测试)我们的模型。虽然我们专注于优化代码/模型以有效利用资源,但我们中的许多人往往会忽略一些简单的概念,这些概念可以大大减少 GeoTIFF 文件的磁盘空间使用。在过去的几年里,我曾经犯过这个错误,直到我意识到“良好实践”可以在不影响数据质量的情况下减少 GeoTIFF 文件的大小。

在本教程中,我将使用 Python 的“pyrsgis”包演示两种不同的元素来减小单波段和多波段栅格文件的大小。第一个要素是使用正确的数据类型,第二个要素是使用压缩。

首先,我将使用来自我以前的博客之一的数据,这些数据可以在 GitHub 资源库中找到。您可以下载以下两个文件:

  1. l5 _ Bangalore 2011 _ raw . rar
  2. l5 _ Bangalore 2011 _ builtup . rar

一个以“raw”结尾的是班加罗尔的 Landsat 5 多光谱数据,另一个是二元组合图层。我压缩了 GeoTIFF 文件(大约两年前),以减小 GitHub 上的文件大小。这种方法的问题在于可扩展性——我们不能一直压缩硬盘上的每一个 GeoTIFF 文件。下图显示了驱动器上 RAR 文件和提取内容的大小。

作者图片

第一部分:使用正确的数据类型减小文件大小

让我们使用' pyrsgis ' 模块在 Python 中读取这些数据,并检查两个栅格的数据类型。下面的代码块将完成这项工作:

输出:

Data type of multispectral file: 6
Data type of single band file: 6
Following data types are supported:
{‘byte’: 1,
 ‘cfloat32’: 10,
 ‘cfloat64’: 11,
 ‘cint16’: 8,
 ‘cint32’: 9,
 ‘float’: 6,
 ‘float32’: 6,
 ‘float64’: 7,
 ‘int’: 3,
 ‘int16’: 3,
 ‘int32’: 5,
 ‘uint8’: 1,
 ‘uint16’: 2,
 ‘uint32’: 4}

可以注意到,这两个文件的数据类型都是 6,这是“float32”类型的数据。现在,我知道 Landsat 5 多光谱文件的像元值范围为[0,255],单波段二进制类文件的值为 0 和 1,但是如果您不知道数据的值范围,可以通过显示最小值和最大值来轻松地做到这一点。

Min and max value of multispectral raster: 1.0 255.0
Min and max value of single band raster: -3.4028235e+38 1.0

多光谱栅格的范围符合预期,但二进制栅格的最小值不符合预期。这可能是由于栅格边缘附近的任意随机 NoData 像元造成的。可以使用以下代码修复它:

Min and max value of single band raster: 0 1

现在我们已经很清楚栅格中值的范围,下一个基本问题是正确的数据类型。对于不熟悉数据类型和像素深度(也称为卫星数据的光谱分辨率)的人来说,快速复习一下:数据类型决定了栅格将具有的值的类型,包括值的数量、值的范围、是否可以保存负数或小数等等。

“int8”类型是指包含 2 的 8 次幂整数值(256 个值)的数据。“uint8”数据类型也存储 256 个值,但意味着它只能保存“无符号”值(0 到 255)。而‘int 8’数据类型可以保存从-128 到 127 的值。但是' int8 '和' uint8 '都是整型值。另一个例子是“uint16”数据类型,它可以保存范围从 0 到 65535 的值(65536 个值— 2 的 16 次幂)。现在出现了“float”数据类型,它指的是十进制值,数字“32”和“64”决定了它能容纳多大的数。

这里的关键点是,如果栅格的实际像素值具有较低的像素深度,则存储具有较高像素深度的数据不是明智的选择,因为栅格文件会在驱动器上保留额外的空间。“正确”的方法是用尽可能低的数据类型导出数据。例如,让我们使用 pyrsgis 包将下载的栅格数据导出为“uint8”(也称为“byte”)数据类型。pyrsgis 包的细节可以在文档页面这里找到。

现在在资源管理器中检查文件的大小。

作者图片

通过简单地导出较低数据类型的文件,多光谱栅格从 98 MB 减少到 24 MB,二进制栅格从 16 MB 减少到 4 MB。您可以在 GIS 软件中检查这两个文件,并发现这两个文件完全相同。很明显,使用正确的数据类型可以节省大量的磁盘空间,但是还有更多空间。

第二部分:使用压缩减小文件大小

如果我说您可以在不影响数据质量的情况下进一步减小文件的大小,那会怎么样呢?简单的方法是定义栅格文件的压缩类型。虽然压缩听起来像是压缩并创建一个. zip 或。rar 文件,在这种情况下它是不同的。

一些支持的压缩类型有“DEFLATE”和“LZW ”,在使用 pyrsgis 包导出 GeoTIFF 文件时,可以使用“compress”参数传递这些压缩类型。您可以通过使用以下代码来实现这一点。在这里,我使用“紧缩”压缩导出两个栅格。

在浏览器中检查栅格的大小。

作者图片

多光谱文件现在已减少到 19.5 MB,单波段栅格已减少到 165 KB。如果将这与我们开始时的 96 MB 和 16 MB 进行比较,这是磁盘空间使用量的巨大减少,大约比原来的小 5 到 100 倍。

过去使用这两种简单的技术,我已经成功地将 GeoTIFF 文件的大小从大约 58 GB 减少到不到 500 MB(小了 100 倍)。具体来说,58 GB 文件是使用 QGIS 中的栅格计算器工具生成的全国 30m 网格化人口栅格。

尝试一下,看看这些如何为你推出。如果您在 Python 中安装 GDAL 或 pyrsgis 包时遇到问题,请查看下面的帖子,该帖子演示了安装和设置环境的自动化方法。

Python 和 GDAL 安装自动化适用于 Windows 10

希望这有助于你提高你的游戏:)

使用 Python 根据帕累托原则减少仓库空间

原文:https://towardsdatascience.com/reduce-warehouse-space-with-the-pareto-principle-using-python-e722a6babe0e?source=collection_archive---------20-----------------------

使用 python 实现的 80/20 规则如何优化您的布局、降低空间利用率并提高拣选效率

仓库货架布局—(图片由作者提供)

如果你对供应链数据科学相关的文章感兴趣,可以随意看看我的作品集:【https://samirsaci.com】

一位名叫维尔弗雷多·帕累托的意大利经济学家在 1906 年提出了一个数学公式来描述意大利的财富分配。他发现 80%的财富属于 20%的人。

维尔弗雷多·帕累托—维基百科(链接)

几十年后,这一规则已被推广到许多其他应用,包括供应链和物流管理。

这一原则被称为“帕累托原则”、“80-20 法则”或“琐碎的多数和关键的少数法则”,可以翻译为物流从业者

  • 贵公司 80%的收入来自 20%的参考
  • 80%的货物在 20%的提货地点提货
  • 80%的补货量将在 20%的提货地点进行

在本文中,我们将使用一个真实的操作示例来探索如何应用这个帕累托原则

  • 1 月份的拣货单
  • 144339订单行
  • 59372订单
  • 4864活动参考

💌新文章直接免费放入你的收件箱:时事通讯

如果你喜欢看,看看这篇文章的视频版本

帕累托原理的可视化

你可以在这个 Github 资源库中找到完整的代码:链接。
我的其他项目组合: Samir Saci

I .利用熊猫进行数据处理

你可以在这里下载数据集:链接。

  1. 导入库和数据集

2。计算每 SKU(箱)准备的体积

为了绘制帕累托图,我们需要

  • 合计每个 SKU 挑选的箱子数量
  • 按盒子数量降序排列数据框
  • 计算盒子的累积和
  • 计算 SKU 的累计数量

结果

在第 5 行中,您可以看到 0.1%的 SKU 代表 12.7% (20,987 箱)。

二。帕累托原理的可视化

  1. 第一可视化

使用您处理过的数据框架,让我们现在绘制(%BOX) = f(%SKU)来显示帕累托原理

帕累托原理的可视化(二)——(图片由作者提供)[ 教程

2。为 80/20 添加标记

标记 1: x = 20%的 SKU(蓝色)
标记 2: y = 80%的方框(红色)

帕累托原理的可视化(二)——(图片由作者提供)[ 教程

洞察力

我们可以看到,在拥有 20%的 SKU (sku_80 = 12.55%)之前,已经达到了 80%的量的阈值。

试着自己看看有多少%的方框代表了 10%的 SKU 被选中。

http://samirsaci.com

如何优化仓库空间?

我们如何利用这些见解来提高您的拣选效率并减少空间占用?

I .将高转速 SKU 归入专用拣货区

基于提货数量百分比的仓库热图示例—(图片由作者提供)

上面的热图是帕累托原则的 2D 表示,将每个 SKU 与其提货地点联系起来。

优化杆
通过将非常高的旋转分组到靠近码头的特定区域,减少拣选过程中的平均距离。

要了解更多信息,请看我以前的文章:使用 Python Scipy ( 链接)的空间聚类提高仓库生产率。

二。为非常低的周转次数增加提货地点的密度

什么是补给任务?

具有 4 个存储级别的完整托盘提货位置示例—(图片由作者提供)

第一层是位于地面的**拣货位置;**这是仓库提货人拿箱子准备订单的地方。

当您的提货地点的数量水平低于某个阈值时,您的 WMS 将触发补充任务:从存储层(第 3 层)取出托盘,并将其放入提货地点(第 1 层)。

帕累托原则如何影响您的提货地点布局?

全托盘(左)|半托盘(中)|货架(右)有三层存储的提货位置—(图片由作者提供)

完整托盘库位类型是指每个 SKU 占用一个地板托盘库位。

但是,我们可以通过使用来增加位置的密度

  • 半托盘位置:每个地板托盘位置 2 个 SKU
  • 货架位置:每层托盘位置 9/2 SKU

表面优化与补给移动次数

半托盘和货架的一个主要问题是与全托盘相比存储容量有限。对于每月挑选的相同数量,半托盘的补货量是原来的 2 倍,货架的补货量甚至更多。

使用帕累托原则和 SKU 旋转分析将帮助我们通过使用以下规则选择提货地点类型来找到最佳折衷方案

  • 全托盘/半托盘位置:仅适用于高跑步者(前 20%)
  • 货架位置:针对 80%的低销量者,他们的销量只占总销量的 20%

这些阈值必须适应您的仓储业务的特殊性

  • 劳动力每小时成本(欧元/小时)和你的提货生产率(生产线/小时)补货(移动/小时)
  • 仓库租赁费用(欧元/平方米/月)
  • **您箱子的尺寸(宽(毫米)x 高(毫米)x 长(毫米))**这将决定您不同提货地点的存储容量

目标是找到高补货生产率(满托盘)和减少地面占用(货架)之间的最佳折衷。

基于上述布局的快速示例

**Pallet dimension**: 0.8 x 0.12 (m x m)
**Alley width**: 3.05 (m)
**Dx:** 0.1 (m) Distance between two pallets
**Dy**: 0.15 (m)**Ground surface occupied(including alley)**
**Full Pallet** = (0,8 + 0,1) x (1,2 + 0,15 + 3,05/2) = 2,5875 (m2)
**Half Pallet** = 2,5875 / 2 = 1,29375 (m2)
**Shelves** = 2 x 2,5875/9 =  0,575 (m2)**Warehouse Rental Cost (Jiaxing, China)**
C_rent = 26,66 (Rmb/Sqm/Month) = 3,45 (Euros/Sqm/Month)**Forklift Driver Hourly Cost**
C_driv = 29 (Rmb/Hour) = 3,76 (Euros/Hour)**Replenishment Productivities**
Full Pallet: 15  (Moves/Hour)
Half Pallet: 13  (Moves/Hour)
Shelve     : 3,2 (Moves/Hour)**Picking Location Capacity**
Full Pallet: 30 (Boxes)
Half Pallet: 15 (Boxes)
Shelve     : 4  (Boxes)

示例 1: 极高转速

极高旋转 SKU 的估计—(图片由作者提供)

结论
全托盘定位是最便宜的解决方案

例 2: 高转速

高旋转 SKU 的估计—(图片由作者提供)

结论
半托盘定位是最便宜的解决方案

例 3: 低速旋转

低旋转 SKU 的估计—(图片由作者提供)

结论
货架位置是最便宜的解决方案

超出

基于您的优化布局,您可以构建一个模拟模型来评估几种单一提货人路线问题策略对您的提货生产率的影响

https://www.samirsaci.com/improve-warehouse-productivity-using-order-batching-with-python/

结论

关注我的 medium,了解更多与供应链数据科学相关的见解。

我们在这里介绍了一个简单的方法,如何在您的仓库拣货订单配置文件中可视化和应用 Pareto 原则,以估计优化的潜力。

在第二个参考链接中,您可以找到一系列应用上述概念的文章,通过减少拣货员的步行距离来提高拣货效率。

关于我

让我们连接上 Linkedin 和 Twitter ,我是一名供应链工程师,正在使用数据分析来改善物流运作和降低成本。

如果你对数据分析和供应链感兴趣,可以看看我的网站

https://samirsaci.com

参考

[1] 乔瓦尼·布西诺,维尔弗雷多·帕累托社会学的意义,《欧洲社会科学杂志》,链接

[2] Samir Saci ,使用 Python Scipy 的空间聚类提高仓库生产率,链接

高维时间序列预测的降秩向量自回归模型

原文:https://towardsdatascience.com/reduced-rank-vector-autoregressive-model-for-high-dimensional-time-series-forecasting-bdd17df6c5ab?source=collection_archive---------10-----------------------

介绍

如今,随着数据收集/可用性技术的显著发展,我们有更多的机会来处理许多科学和工业领域中的多种时间序列数据。时间序列数据有多种类型,包括单变量时间序列、多变量时间序列和多维时间序列。对于多元时间序列,数据具有多个时间相关变量,并且每个变量都依赖于其他变量。因此,向量自回归(VAR)模型是多元时间序列分析的经典方法,主要是因为它能够识别时间相关变量的共同演化模式。

然而,有一种特殊情况,如果我们有大量的变量(例如,数千或数百万个变量),但只有有限数量的时间步长,那么多元时间序列将是高维的。例如,图 1 直观地展示了高维时间序列数据的风险值模型。这里,高维时间序列数据被表示为**“瘦高”矩阵**。

图一。高维多元时间序列的 VAR 模型图解{ x 1, x 2, x 3, x 4}。该模型由系数矩阵*(即一个方阵)参数化。*

在这个简单的图示中,不难看出系数矩阵中的参数很多,超过了时间序列观测值的数量。在大多数情况下,这个风险值模型是不适定的,因为它会受到过度参数化问题的影响。为了解决 VAR 中的这一问题,一个重要的方向是开发矩阵分解参数化的降秩 VAR 模型。

在这篇博文中,我们介绍了一个用于多元时间序列分析的降秩 VAR 模型。该模型是由韦卢*、赖恩塞尔威彻恩于 1986 年提出的[1]。在此之前, Velu 在威斯康星大学未发表的博士论文中也讨论了某些降秩自回归模型的估计。后来在 1998 年, VeluReinsel 出版了《多元降秩回归:理论与应用》一书[2]。*

虽然降秩 VAR 有很长的历史,但在过去的几十年里,这篇论文并没有引起太多的关注。由于我们有许多种高维时间序列数据,许多高维 VAR 模型实际上持有与降秩 VAR 相似的思想。

本帖不限于之前最降级的 VAR 论文内容。我们还提供了降秩 VAR 模型的简单模型描述,并给出了一个玩具例子来说明其应用。此外,我们用 Python 重现了这个模型,特别是用 Numpy 包。

模型描述

图 1 显示了 VAR 的基本思想。给定时间序列数据 X 的大小 N -by- T 其中 N 是变量的数量, T 是时间步长的数量,则在任何时间步长 t 中,一阶 VAR 或 VAR(1)的形式为

其中 x t 表示时间 t 的快照向量,大小为N-1。 A 是系数矩阵,大小为 N -by- N

为了解决 VAR 中的过度参数化问题,我们可以假设系数矩阵具有降低的秩,并且定义两个矩阵***【W】*(大小为 N -by- R )和 V (大小为 R -by- N ),使得

=WV

其中系数矩阵通过矩阵分解被重新参数化。在这种情况下,它导致一个降秩的 VAR 模型,如图 2 所示。很明显,如果我们施加一个合适的降秩 R ,分量矩阵 WV 中的参数将小于系数矩阵中的参数。这实际上为解决高维时间序列数据中 VAR 的过参数化问题提供了一条技术路径。

图二。由组件矩阵 WV 参数化的降秩风险值模型的图示。这里, W 是高瘦矩阵,而 V 是矮胖矩阵。

从机器学习的角度来看,为了估计降秩 VAR 模型中的参数,我们可以将自回归误差公式化为 L2 范数损失函数:

对于这个优化问题,我们可以得到向量形式的 WV 的闭式解。然而,向量形式并不是开发算法的最佳选择。这里,我们考虑一个新的优化问题:

我们定义的地方

并且 WV 的闭合解现在由下式给出

算法

由于 W 的闭式解涉及 V ,而的闭式解涉及 W ,我们可以使用交替最小化方案。对于最小二乘解,算法实际上是交替最小二乘(ALS)。这个迭代算法只有三个主要步骤:**

  • 用随机值初始化 WV
  • 迭代第一步:用上面提到的最小二乘解更新 W
  • 迭代第二步:用上面提到的最小二乘解更新 V

我们可以在 Python 中为降秩 VAR 模型定义一个函数。

***import numpy as npdef rrvar(data, R, pred_step, maxiter = 100):
    """Reduced-rank VAR algorithm."""

    N, T = data.shape
    X1 = data[:, : -1]
    X2 = data[:, 1 :]
    V = np.random.randn(R, N)
    for it in range(maxiter):
        W = X2 @ np.linalg.pinv(V @ X1)
        V = np.linalg.pinv(W) @ X2 @ np.linalg.pinv(X1)
    mat = np.append(data, np.zeros((N, pred_step)), axis = 1)
    for s in range(pred_step):
        mat[:, T + s] = W @ V @ mat[:, T + s - 1]
    return mat[:, - pred_step :]***

这是一种降秩 VAR 算法,其中我们有多变量时间序列数据和降秩等输入。

玩具示例

我们通过考虑以下示例来评估该算法:

示例数据的大小为 20 乘 10,这是一个“高瘦”数据。我们将尝试将降秩设置为 2,并测试算法。

用 Python 编写代码:

***X = np.zeros((20, 10))
for i in range(20):
    X[i, :] = np.arange(i + 1, i + 11)
pred_step = 2
R = 2
mat_hat = rrvar(X, R, pred_step)
print(mat_hat)***

结果是:

***[[11\. 12.]
 [12\. 13.]
 [13\. 14.]
 [14\. 15.]
 [15\. 16.]
 [16\. 17.]
 [17\. 18.]
 [18\. 19.]
 [19\. 20.]
 [20\. 21.]
 [21\. 22.]
 [22\. 23.]
 [23\. 24.]
 [24\. 25.]
 [25\. 26.]
 [26\. 27.]
 [27\. 28.]
 [28\. 29.]
 [29\. 30.]
 [30\. 31.]]***

结果与地面真实数据完全一致。

结论

降秩 VAR 是高维数据下一种重要的时间序列预测方法。它有许多优点,如压缩系数和解决 VAR 中的过参数化问题。除了降秩 VAR 中的矩阵分解工具,还有一些其他工具,如张量分解。依靠这篇文章,不难将降秩 VAR 模型扩展到更高阶。

参考

[1]韦卢,R. P .,赖因塞尔,G. C .,,威彻恩,D. W. (1986 年)。多时间序列的降秩模型。生物计量学,73(1),105–118。

[2]韦卢和赖因塞尔(1998 年)。多元降秩回归:理论与应用。斯普林格科学与商业媒体。

[3]陈新宇(2021)。多维时间序列预测的矩阵自回归模型。中等。网址:https://towards data science . com/matrix-auto regressive-model-for-dimensional-time-series-forecasting-6a 4d 7d ce 5143

使用较小的数据类型减少熊猫的内存使用

原文:https://towardsdatascience.com/reducing-memory-usage-in-pandas-with-smaller-datatypes-b527635830af?source=collection_archive---------14-----------------------

理解大数据

通过有效使用数据类型来优化 pandas 的内存使用

照片由托尔加·乌尔坎在 Unsplash 拍摄

用熊猫管理大型数据集是一个相当常见的问题。因此,开发了许多库和工具来减轻这种痛苦。以下面提到的 pydatatable 库为例。

尽管如此,还是有一些技巧和提示可以在一定程度上帮助我们解决熊猫的记忆问题。他们可能不会提供最好的解决方案,但这些技巧有时会被证明是方便的。因此,了解他们没有坏处。在我以前的一篇文章中,我谈到了在 pandas 中加载大型数据集的两种替代方法。

这些技术是:

  • 分块:将数据集细分成更小的部分
  • 使用 SQL 和 pandas 读取大数据文件

本文是上述技术的延续。因此,如果你还没有读过上一篇文章,现在读一读是个不错的主意😃。在本文中,我们将介绍通过有效使用数据类型来优化内存使用的方法。但是首先,让我们详细了解熊猫的数据类型。

熊猫数据类型

数据类型是指数据在内存中的存储方式。为了更简洁,我在这里引用维基百科:

数据类型或简称为类型是数据的一种属性,它告诉编译器或解释器程序员打算如何使用数据。

主要的数据类型包括整数、浮点数、布尔值和字符。pandas图书馆也遵循同样的论述。以下是 pandas 支持的各种数据类型的简要概述:

作者图片

int 和 float 数据类型有更多的子类型,这取决于它们用来表示数据的字节数。以下是完整的列表:

出处 : r 麻省理工执照

这是一个很长的列表,但让我们触及几个关键点:

  • 数据类型名称后面的数字表示存储一个值所需的内存位数。例如,int8使用 8 个位或 1 个字节;int16采用 16 位或 2 字节,依此类推。
  • 范围越大,消耗的内存就越多。这意味着int16使用的内存是int8的两倍,而int64使用的内存是int8的八倍。
  • uint8uint16 等。指无符号整数,而 int指有符号整数。分配的内存量没有区别,但是顾名思义,对于uint8,无符号整数只能存储正值,即 0–255。这同样分别适用于uint16uint32uint64

数据类型很重要,因为数据的存储方式决定了可以用它做什么。

观看活动中的事物

既然我们已经对 pandas 的各种数据类型及其表示有了很好的了解,那么让我们看看在使用它们时优化存储的方法。我将使用一个包含 1.6 GB 数据的文件来总结 2016 年 3 月的黄色出租车出行数据。我们将从使用read_csv()函数将数据集导入熊猫的数据帧开始:

import pandas as pd
df = pd.read_csv('yellow_tripdata_2016-03.csv')

让我们看看它的前几列:

作者图片

默认情况下,当 pandas 加载任何 CSV 文件时,它会自动检测各种数据类型。这是一件好事,但有一个问题。如果一列全部由整数组成,默认情况下,它会将int64 dtype 分配给该列。类似地,如果一列由浮点值组成,则该列被赋予float64 dtype。

df.info()

作者图片

如上所述,在这种情况下使用了三种数据类型:

  • int64对于整数值,
  • float64对于浮点值和、
  • object日期时间和分类值的数据类型

数据

在检查我们的数据框架时,我们发现某些列的最大值永远不会大于 **32767。**在这种情况下,使用int64作为数据类型是不明智的,我们可以很容易地将其简化为int16。我们通过一个例子来更具体的理解一下。

为了进行演示,让我们分析一下passenger count列并计算它的内存使用情况。为此,我们将使用熊猫的[memory_usage](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.memory_usage.html)()功能。

为了理解较小的数据类型是否足够,让我们来看看该列的最大值和最小值。

由于该列只包含最大值为 9 的正值,我们可以轻松地将数据类型降级为int8,而不会丢失任何信息。

这大大减少了所使用的内存。我们再举一个例子。这次我们将分析由浮点值组成的pickup_longitude列。

现在,您可能会同意,对于经度(和纬度)列,最多两位小数的值在传递信息方面是合适的。另一方面,在减少内存使用方面的优势将是巨大的。我们可以将数据类型从float64改为float16,这样可以减少 1/4 的内存使用。

这太棒了。我们可以通过分析其他列来类似地向下转换它们,这样可以节省大量的内存。

分类数据

到目前为止,我们只看了数字列。有没有一种方法也可以优化分类列?是的,也有减少分类列内存消耗的方法。让我们以**store_and_fwd_flag** 列为例,按照上一节所示,计算存储它所需的内存。

数据类型“O”指的是object数据类型。现在,如果我们要查看该列中的唯一值,我们将得到:

只有两个唯一值,即 N 和 Y,分别代表 No 和 Yes。在值的数量有限的情况下,我们可以使用一种更紧凑的数据类型,称为[Categorical](https://pandas.pydata.org/pandas-docs/stable/user_guide/categorical.html) dtype。以下是来自文档本身的摘录:

类别是一种 pandas 数据类型,对应于统计学中的分类变量。分类变量采用有限的、通常是固定的可能值(类别;R 中的级别)。例子有性别、社会阶层、血型、国家归属、观察时间或通过李克特量表的评级。

如果我们将对象类型向下转换为分类数据类型,内存使用量的减少将如下所示:

同样,也实现了相当数量的内存减少。

最后,我们还可以在加载 CSV 文件时为不同的列指定数据类型。这对于加载时抛出内存不足错误的数据很有用。

结论

在本文中,我们看到了如何优化数据集使用的内存。如果我们的 RAM 有限,并且我们的数据集放不进内存,这就特别有用。然而,看看其他一些能够更有效地处理大数据问题的库将会很有帮助。

通过主动学习降低模型训练成本

原文:https://towardsdatascience.com/reducing-model-training-cost-with-active-learning-255b40a2aa98?source=collection_archive---------39-----------------------

高斯过程模型的完整案例研究

在 Unsplash 上由 nine koepfer 拍摄的照片

在回归分析中,标注训练样本通常非常耗时,并且会占用很大一部分计算预算。

在我们的工程团队中,我们一直面临这种挑战。

我们的任务是设计航空发动机部件。在给定设计参数的情况下,我们需要不断地训练回归模型来预测产品性能。对我们来说,标记训练样本需要进行高保真的物理模拟,这很容易需要几天甚至几周的时间才能在集群上运行。

显然,如果模型需要许多样本才能达到令人满意的精度,由此产生的计算负担将是一场噩梦。

幸运的是,我们找到了一个简单的解决方案,可以在保持预测准确性的同时大幅减少训练样本的数量。这个解决方案就是主动学习

基本思想很简单:我们从一个小的训练数据集开始,然后随着训练的进行逐渐丰富训练数据集。在每一次学习迭代中,我们标记一个新的样本,对于该样本,当前模型预计会产生最大的预测误差

这种直观的策略在实践中表现得非常好。在某些情况下,我们可以训练一个同样精确的模型,只需要原来计算预算的 1/3!

在这篇文章中,我想和你分享我们主动学习策略背后的技术细节。为此,我会

  • 呈现整体工作流程;
  • 讨论如何基于偏差-方差分解估计预测误差;
  • 浏览一个完整的案例研究,看看这个想法在实践中是如何应用的。

我还创建了一个配套的 Jupyter 笔记本 ,演示了如何在 Python 中进行主动学习。这篇文章中的所有插图都复制在笔记本上。

我们在这里讨论的主动学习策略着重于建立一个在参数空间中处处精确的模型。如果您更关心构建一个仅在底层函数的全局最大值/最小值附近准确的模型(这是执行优化任务所需的特性),请看看这篇文章,了解另一种实现这一点的主动学习策略:

目录
1。主动学习工作流程
2。偏差-方差分解
3。估计预测误差
4。案例分析
∘ 4.1 测试函数
∘ 4.2 模型
∘ 4.3 第一次迭代
∘ 4.4 第二次迭代
∘ 4.5 第三次迭代
∘ 4.6 进一步迭代
∘ 4.7 确定收敛【T33
参考
关于作者

1.主动学习工作流程

实施主动学习包括以下步骤:

  1. 基于当前可用的数据训练模型;
  2. 使用学习功能来决定下一个要贴标签的样品;
  3. 标记所选样本;
  4. 用标记的样本丰富当前的训练数据集;
  5. 重复步骤 1–4,直到模型达到目标精度。

下面给出了上述步骤的直观说明。

图 1 主动学习流程。(图片由作者提供)

主动学习确保在每次迭代中选择并标记对模型准确性贡献最大的样本。结果,需要更少的样本来达到期望的精度,从而降低了总的计算预算。

主动学习有两个关键因素:

  • 该模型可以估计其预测的不确定性;
  • 精心设计的学习功能。

首先,模型要估计自己的预测不确定性,通常是以预测方差的形式。这一点至关重要,因为方差值反映了模型对基础输入输出函数的了解。

第二,我们需要一个专门设计的学习函数,它编码了我们希望我们的模型如何学习。这个学习函数指导样本富集:在每次学习迭代中,最大化或最小化学习函数的样本被选择和标记。

对于我们当前的情况,由于我们想在模型产生最大预测误差的地方加强我们的模型,我们将使用一个学习函数来计算未知样本的预测误差。随后,在每次迭代中,我们将标记那些最大化所采用的学习函数的看不见的样本之一,并将其附加到当前的训练数据集。

问题来了,我们如何设计一个学习函数,可以计算出未知样本的预测误差?偏差-方差分解可能对此有所启发。

2.偏差-方差分解

我们将未知样本的预测误差 x 定义为真实函数值 y ( x )(我们假设它是确定性的)和模型预测值 M ( x )之间的平方差:

由于模型预测 M ( x )不确定*,*相关的预测误差 L ( x )也将不确定。因此,我们可以将 L ( x )视为随机变量,并使用其平均值 E[ L ( x )]来表示我们对样本 x 的预期预测误差。

经过冗长的代数运算,我们可以写出 E[ L ( x )]为:

其中规定预期预测误差 E[ L ( x )]可以分解为

  • 偏差项,表示模型预测和实际函数输出之间的平均差异;
  • 描述模型预测方差的方差项。

上面这个方程就是机器学习中著名的偏差-方差分解。期望预测误差 E[ L ( x )]是我们的目标学习函数。

3.估计预测误差

然而,在实践中,我们无法使用上面简单形式的方程来计算 E[ L ( x )]:尽管获得方差值很容易(=模型预测方差),但是计算真实的偏差值是根本不可能的,因为真实的函数输出 y ( x )是未知的(否则我们首先就不需要建立模型了!).

为了避免这个问题,我们可以用真实偏差值的近似值来代替。具体来说,我们可以使用留一交叉验证误差来估计未知样本的预测偏差 x *。以下是步骤:

1.我们计算所有训练样本 xᵢi =1,…,n 的交叉验证误差:

这种计算是通过首先用除了[ xᵢy ( xᵢ )]之外的所有训练样本训练一个新模型,然后计算真实标签 y ( xᵢ )和在 xᵢ 的新模型预测之间的平方差来完成的。

2.我们找到最接近训练样本 xᵢx ,简单地用这个训练样本的交叉验证误差作为 x 的预测偏差:

一旦我们知道如何正确估计未知样本的预测偏差值 x *,我们现在可以将估计的偏差项插回到 E[ L ( x )]等式中,并计算所需的预期预测误差值。

我们主动学习策略中缺失的部分终于找到了!

4.个案研究

是时候通过一个案例研究来看看这种主动学习策略在实践中的表现了。

4.1 测试功能

我们选择以下测试函数:

我们的目标是训练一个回归模型来逼近这个测试函数。在实际设置中, y ( x )具有未知表达式,只能通过复杂、耗时的计算机模拟来评估。因此,为了反映现实,在下面的模型训练过程中,我们将

  • y ( x )视为黑盒函数;
  • 限制 y ( x )评估的数量,即标注尽可能少的训练样本。

在我们开始模型训练之前,让我们先画出这个函数来获得一些直觉。

图 2 采用的测试函数。(图片由作者提供)

从图中可以看出,这个测试函数描述了输入𝑥和输出 y 之间相当复杂的关系(𝑥):在范围[-4,-3]内存在一个平坦的区域,而多模态响应支配着剩余的区域)。由于 yy(𝑥)在参数空间的表现非常不同,因此构建一个全局精确的回归模型具有挑战性。

4.2 模型

在本案例研究中,我们选择高斯过程 ( GP ) 作为期望的回归模型。这里,来自 Scikit-learn 的 GP 实现被用来训练 GP 模型。

我们喜欢这种模型类型,因为它在小数据区域表现很好,而且它自动估计其预测不确定性:它将其预测 M ( x )分配给正态分布:M(x)~ N(μ(x)、σ ( x ),其中μ σ ( x )将作为 E[L( x )]方程中的方差项。

4.3 第一次迭代

首先,我们基于均匀分布在参数空间中的 5 个样本来训练初始 GP 模型。生成的 GP 模型如下所示。

图 3 初始 GP 模型。(图片由作者提供)

正如我们所看到的,由于训练样本数量不足,GP 预测与测试函数匹配不佳。同时,预测方差很大,这意味着 GP 模型对其预测不太有信心。如果我们仔细观察区域[0,1],最初的 GP 模型已经完全错过了“山谷”景观。更糟糕的是,95%的置信区间未能覆盖“谷”!显然,我们需要更多的训练样本。

现在,让我们通过向当前训练数据集添加新样本来开始主动学习过程。为此,我们将确定一个 x *,使其对所有可能的 x 最大化 E[L( x )】。E[L( x )]的计算基于我们之前讨论的偏差-方差分解。

在下图中,我们在左侧重新绘制了 GP 预测,在右侧绘制了整个参数空间的 E[L( x )]分布。

图 4 第一次迭代。(图片由作者提供)

最大 E[L( x )]值用绿点标出,出现在 x *=-2.12。这是初始 GP 模型预测误差最大的地方。

因此,在第一次迭代中,我们将通过调用测试函数计算 y ( x *)来标记样本 x * 】,并将新数据[ x =-2.12,y(x)=-0.89]追加到当前训练数据集中。

4.4 第二次迭代

使用新丰富的训练数据集,我们可以更新 GP 模型及其预测,如下图左所示。

图 5 第二次迭代。(图片由作者提供)

丰富的训练数据集极大地提高了 GP 模型在区域[-4,-1.5]中的准确性。这也可以在右侧的 E[L( x )]图中看到,在该图中,预期预测误差在同一区域中相对较小。

采用主动学习策略的一个好处是,它自动检测出模型精度仍然较低的区域,可以进一步提高。在这次迭代中,E[L( x )]的最大值出现在 x *=0.38 处,这恰好是当前 GP 模型偏离真实测试函数最大的地方。

这正是我们想要我们的主动学习策略表现的方式!在现实中,当我们不知道底层函数是什么样子时,主动学习的这一功能可以真正帮助我们定位最有价值的样本以提高模型准确性,从而降低整体模型训练成本。

在这第二次迭代中,我们将把新数据[ x *=0.38, y ( x *)=0.18]追加到当前训练数据集中。

4.5 第三次迭代

我们不断用新增加的训练数据集更新 GP 模型。在下面左图中,我们可以看到更新后的 GP 模型的性能,总共有 7 个训练样本。

图 6 第三次迭代。(图片由作者提供)

由于新添加的训练样本,更新的 GP 模型现在可以轻松捕获“山谷”区域[0,1]。事实上,当前的 GP 模型在整个参数空间的任何地方几乎都是准确的,正如 GP 预测和真实测试函数之间显著降低的 MSE 值所表明的那样。

但是,请注意,与上一次迭代相比,E[L( x )]再次上升。这种现象实际上很普遍,因为 GP 模型学到了一些“新奇”的东西,这极大地改变了它的“世界观”。下一次迭代应该可以解决这个问题。

在这第三次迭代中,我们将把新数据[ x =-0.87,y(x)=-0.46]追加到当前训练数据集中。

4.6 进一步迭代

现在我们已经到了第四次迭代。像往常一样,在左边,我们有 GP 预测。右边是 E[L( x )]图。

图 7 第四次迭代。(图片由作者提供)

现在 GP 预测与潜在的测试函数更加一致。同时,E[L( x )]值相对于上一次迭代再次下降。这是个好消息!我们回到了正轨。

我们继续更多的迭代。这是我们在 7 次迭代后得到的结果,即我们当前的训练数据集有 12 个样本。

图 8 最后一次迭代。(图片由作者提供)

我们可以清楚的看到 E[L( x )]最大值明显下降。相应地,GP 预测与真实的测试函数几乎没有区别。

4.7 确定收敛性

实际上,我们无法获得底层函数的确切形式。因此,我们不能通过计算 MSE 来判断模型的准确性。那么,我们如何确定何时停止主动学习迭代呢?

根据我的经验,我建议在迭代期间监控最大 E[L( x )]值的演变,并使用以下规则来确定何时终止训练:

在第一次迭代时,当前最大 E[L( x )]值低于最大 E[L( x )]值的 5%

当然,确切的百分比可以根据您的精度要求和可用的计算预算来定制。我更喜欢 5%,因为它通常在我的项目中工作得很好。

这是当前案例研究的最大 E[L( x )]值的收敛历史。在第 8 次迭代时,最大 E[L( x )]值小于初始最大 E[L( x )]值的 5%。因此,我们在这里终止我们的学习迭代,并决定不再丰富更多的样本。

图 9 The 辐合历史。(图片由作者提供)

5.外卖食品

在本文中,我们介绍了主动学习策略的基本思想、工作流程和技术细节,这可以显著降低模型培训成本。这一战略的主要特点包括:

  • 在每次迭代中,它将样本放在模型预计具有最大预测误差的地方;
  • 预测误差估计基于偏差-方差分解;
  • 采用留一交叉验证来逼近偏倚项;
  • 当最大预测误差值下降到初始预测误差值的 5%以下时,可以终止学习。

我们在这里讨论的主动学习策略主要集中在建立一个在所研究的参数空间内处处精确的模型。如果你也有兴趣建立一个只在底层函数的全局最大值/最小值附近精确的模型(这是执行优化任务所需的特性),看看这篇文章,学习另一个主动学习策略:

参考

[1] H. Liu,J. Cai,Y. S. Ong,一种通过最大化期望预测误差进行克立格元建模的自适应采样方法,2017,计算机与化学工程。

关于作者

我是一名博士研究员,研究航空航天应用的不确定性量化和可靠性分析。统计学和数据科学是我日常工作的核心。我喜欢分享我在迷人的统计世界中学到的东西。查看我以前的帖子以了解更多信息,并在 Linkedin 上与我联系

使用 Binner 降低非靶向代谢组学中的噪声

原文:https://towardsdatascience.com/reducing-noise-in-untargeted-metabolomics-with-binner-fd7d4e946e0f?source=collection_archive---------33-----------------------

一种新的特征标注方法

来源:作者

我们采访了软件工具 Binner 的合著者之一 Hani Habra:这是一个桌面应用程序,用于注释电喷雾电离液相色谱质谱(ESI-LC/MS)产生的非靶向代谢组学数据中的同位素、加合物和来源内片段。

像 Binner 这样的工具大大降低了非目标代谢组学数据集的复杂性。没有它们,就很难从测量中获得有意义的见解,因为噪音往往会淹没我们要寻找的信号。

一种代谢物如何产生多种信号

来源:作者

如果我们用电喷雾电离质谱仪分析生物样品(例如,血液),那么单个代谢物(例如,血液中的小分子)不仅仅产生一个信号,而是许多信号。这有四个原因:

  1. 同位素:一个分子通常有几种同位素版本(同位素)。这意味着它是同一个分子,但它的一个或多个原子具有不同数量的中子。例如:地球上约 1.1%的碳原子有 7 个中子,而不是 6 个。如果我们的分子有 1 个碳原子,那么我们可能还会观察到第二个质量峰,其质量(M+1)高出约 1 道尔顿,在质谱中的丰度大约低 99%。
  2. **加合物:**两个或两个以上分子结合形成新分子。我们想要测量的一部分分子也与其他分子结合形成新的分子(加合物)。可能形成的加合物取决于许多因素:如何制备样品(例如,使用哪种溶剂:丙酮或甲酸)、如何储存样品,以及是以正离子模式还是负离子模式运行机器。一个简单而常见的例子是钠原子(Na)或质子(H+)加入时形成的加合物。
  3. **碎片:**当分子在质谱仪中被电离时,一些离子变得能量不稳定,并可能随后分裂。通常其中一个碎片带有中性电荷,因此无法被检测到,而其他带电荷的碎片离子——现在更小了——仍然存在。这些片段作为额外的信号出现。如果我们知道预期会有什么样的中性损失,那么我们就可以把这些碎片和原始分子组合在一起。
  4. **多聚体:**除了带电物质外,两种或多种代谢物单体之间可形成复合物,产生的 m/z 值通常约为单个代谢物的 2-3 倍。

因此,原本只是一个分子的东西变成了许多测量结果——这是任何分析的一个重大障碍。

为什么分离信号会增加下游分析的难度

所以一个分子产生不止一个而是许多信号。这使得我们很难精确地确定我们正在测量的样品中有多少特定的分子。

这提出了几个重要的问题:

  • 模式更难发现:如果你在寻找两组患者(例如,患病者和未患病者)之间的差异,如果信息分散在许多信号中,找到这些差异将会更加困难。你寻找的模式可能会完全消失。
  • **多重比较创造机会模式:**我们搜索的信号越多,我们就越有可能找到一个看似很好的模式,它实际上是由机会产生的(见多重比较问题)。我们拥有的独特化合物越多,这个问题就越严重。
  • **特征是冗余的:**源自相同源离子的信号可能是高度相关的。因此,当有趣的特征数量成倍增加时,我们的下游分析就变得更加繁琐和多余。
  • **生物相关性被隐藏:**冗余特征之间的相关性使得寻找相关代谢物变得更加困难——使得更加难以了解所涉及的生物途径。
  • **信号被错误识别:**我们拥有的信号越多,就越容易出错。我们可能会把不属于一起的信号联系起来,或者我们可能会把一个信号识别为它自己的代谢产物,而它实际上是从另一个信号衍生而来的。

所有这些都让我们更难找到我们想要的有意义的见解。更重要的是,我们甚至可能发现一些完全错误的见解。

这就是为什么正确识别尽可能多的衍生特征并将它们组合在一起非常重要。确定信号如何组合在一起的过程被称为注释,因为我们用它们如何从先驱离子发展的描述来注释信号。这就是宾纳帮助我们做得更好的地方。

在 Binner 之前,特征注释是如何工作的

Binner 并不是第一个帮助标注要素的工具。R-tool 相机已经流行了快 10 年了,还有很多其他的。

在 Binner 之前,有五种以注释工具为特征的主要技术用于确定哪些信号属于一起:

  • 保留时间分组/阈值:相关特征的保留时间必须非常相似,即这些特征应该共洗脱。通常,该工具会为保留时间组设置一定的容差,如果两个信号之间的间隔超过该设置,则该工具会创建一组新的要素。
  • 相关性:源自同一代谢物的特征可能相关。因此,您可以使用批中的所有测量值来计算要素之间的相关性,成对相关性较高的要素更有可能属于同一组。
  • 聚类:在通过保留时间宁滨特征并计算成对相关性之后,工具通常使用几种聚类算法中的一种来基于相似性度量(例如相关性)将特征分组在一起。
  • 色谱峰形相似性:加合物和碎片应具有相似的峰形,因为它们来自相同的代谢物,后者以特定的丰度曲线离开液相色谱柱。
  • 相对加合物频率:基于先验知识和在相似数据集中观察到的不同加合物的频率,我们可以假设哪些片段比其他片段更可信。

Binner 结合了几种现有的技术,然后更进一步,提供了有用的可视化。

宾纳如何注释特征

像其他工具一样,Binner-Hani Habra 共同开发的工具-自动对来自同一代谢物的特征进行分组。

它旨在显著减少数据集中的要素,尽可能在数据集中的要素和现实世界的化合物(分子)之间保留 1-1 关系。

以下是宾纳工作原理的逐步概述:

  1. 宁滨保留时间 : Binner 根据洗脱或保留时间的差异将特征分组到称为箱的独立单元中。
  2. 计算相关性:接下来,它根据 bin 中每个特征的丰度值计算成对相关性。
  3. 检测同位素:它发现并分组特征的 C13 同位素版本,假设同位素的丰度随着其质量的增加而减少(因为 C13 的丰度自然低于 C12)。
  4. 分层聚类:接下来,Binner 使用相关向量对特征进行聚类,并优化聚类数量,以最小化每个特征的平均轮廓值。
  5. 以最丰富的特征为中心:使用最丰富的特征作为以下加合假设的基础。
  6. 测试假设以解释质量差异:然后,Binner 测试最常见的离子,包括用户提供的加合物和中性损失/收益列表,以解释质量差异。然后,它保持注释特定组中最大数量的特征的假设。
  7. **重复:**最后,宾纳试图注释剩余的离子,从下一个最丰富的未注释离子开始。

注释文件如何指导宾纳的假设

与其他一些工具一样,您可以为 Binner 提供一个注释文件,其中包含您希望在数据集中看到的电荷携带、中性增益和中性损耗。

与其他一些工具不同,Binner 并不局限于您提供的加合物配方。它还允许注释文件中定义的任何东西的加合组合。所以如果你指定一个电荷载流子为 Na (22.982),中性增益为 Na — H + NaCOOH (89.97),那么可以想象 Binner 会将特征标注为“M+Na + Na — H + NaCOOH”(可以简化为 M+2Na-H+NaCOOH)。

但是宾纳并没有止步于此——它不仅仅做自动注释。

使注释透明

Binner 还为其注释提供了可视化解释。这有助于您理解为什么要以特定的方式对特性进行分组和注释,从而增强您对注释的信心。

Binner 还显示可用于查找和定义新的、以前未知的加合物以添加到注记文件中的信息,从而显著增加可注记的要素数量。

让我们来看看这是如何工作的。

可视化特征相关性和注释

对于 Binner 找到的每个聚类,它会向您显示

  • 它选定的注释,以及导致该注释的中性损失和加合的计算;
  • 聚类中每个特征之间的相关性,既有值又有热图。

每个聚类中最丰富的要素以绿色突出显示,其他要素以黄色突出显示。

看到每个注释背后的推理也可以让你快速地进行合理性检查。例如,如果你看到多个加合物被标注:M+COOH+NaCOOH,M+COOH+ 2 NaCOOH,M+Cl+ 3 NaCOOH,M+Cl+ 4 NaCOOH,M+Cl+ 5 NaCOOH,那么因为每个“步骤”都存在,所以你可以看到,即使是非常复杂的加合物 M+Cl+ 5 NaCOOH,也很可能是一个合理的注释。

但是,如果“2、3 和 4”从集群中丢失,那么像 M+Cl+ 5 NaCOOH 这样的复杂加合注释似乎不太可能是正确的。

宾纳还会给你提示,帮助你找到复杂的新加合物(T21),哈尼称这个过程为“深度注释”

深度注释:寻找新颖、复杂的加合物

即使在勤奋的自动化注释之后,许多特性仍然不会被注释。

事实上,由于源内事件,加合物和碎片离子可以形成什么还没有完全了解。因此,现有的注释文件是不完整的——它们没有包括所有可能发生的加合和中性损失。

深度注释有助于您发现可能要添加到注释文件中的当前未知的加合物:

  • Binner 收集数据集中所有未标注离子之间的质量差
  • 它统计了每个质量差异的频率,突出显示了最频繁的
  • 然后提出哪些加合物可以解释这些常见的质量差异。

查看最常见的差异不仅可以向您展示您可能想要添加到注释文件中的加合物,还可以帮助您解释这些通常很复杂的加合物是如何形成的。这里有一个例子:

一个常见的质量差是 43.9639,可以注释为+2Na-2H;另一个常见的质量差是 67.9877,对应的是+NaCOOH。这些合在一起可以解释一个常见但先前未知的质量差 111.952,相当于+2Na-2H+NaCOOH。然后您可以将它添加到您的注释文件中。

可视化常见的质量差异可以帮助您找到注释文件的新颖、合理的补充,这反过来又可以帮助 Binner 注释更多以前未注释的特征。

马上试试宾纳

你可以在这里下载安装宾纳:https://binner.med.umich.edu/。很简单。

宾纳背后的团队

宾纳是由威廉·杜伦和詹尼斯·威金顿开发的;该项目由 Maureen Kachman 博士(分析化学家兼代谢组学核心董事总经理)和 Alla Karnovsky 博士(密歇根大学计算医学和生物信息学副教授)以及 Hani 的博士顾问领导,George Michailidis 博士对该项目做出了大量的统计/数字贡献。

您正在优化您的代谢组学工作流程吗?

我们的机器学习团队在代谢组学工作流程方面拥有丰富的经验。如果您担心如何将研究代码转化为健壮的生产应用程序,请联系。

将 Jupyter 笔记本重构为可维护的管道:分步指南(第一部分)

原文:https://towardsdatascience.com/refactoring-a-jupyter-notebook-into-a-maintainable-pipeline-a-step-by-step-guide-part-i-e9e9789c3368?source=collection_archive---------15-----------------------

从 Jupyter 开发可维护的项目

将 Jupyter 笔记本转换成模块化可维护项目的详细指南。

图片作者。

更新:我们发布了 [***soorgeon***](https://github.com/ploomber/soorgeon) ,一款自动重构遗留笔记本的工具!

更新:第二部此处可用。

在过去的几个月里,我们与许多数据团队进行了交谈,以帮助他们开始使用 Ploomber 来开发可维护的和生产就绪的管道。经常出现的一个问题是,*我们如何重构现有的笔记本电脑?*这个博客系列提供了一步一步的指南,将单体 Jupyter 笔记本转换成可维护的流水线。

为了确保你知道第二部何时上映,在 Twitter 上关注我们或订阅我们的时事通讯。让我们开始吧。

介绍

驻留在.ipynb中的代码变得非常混乱。因此,在笔记本创建几天后,这样的文件包含数百个单元格和几十个表格和图表是很常见的。

这种笔记本就是我们所说的 Jupyter monolith:一个包含从数据加载、清理、绘图等所有内容的笔记本。不幸的是,维护 Jupyter monolith 是非常困难的,因为任务之间没有明确的界限,并且没有一种简单的方法来单独测试各个部分。所以我们的目标是把一个凌乱的笔记本变成这样:

图片作者。

步骤 1:确保笔记本运行

假设您的项目目前如下所示:

这种文件布局是典型的:.py文件可能包含一些实用函数,但是大部分代码都在analysis.ipynb笔记本中。在重构我们的代码之前,让我们首先添加三个有助于我们测试管道的文件:

  1. 从属关系:requirements.txt
  2. Ploomber 管道规格:pipeline.yaml
  3. 一份analysis.ipynb(取名pipeline.ipynb)。我们创建一个副本来保存我们的原始输出单元格作为参考。

如果你已经有一个requirements.txt,使用它。否则,创建一个包含以下内容的文件:

您的pipeline.yaml应该是这样的:

然后,添加以下内容作为pipeline.ipynb的顶部单元格:

并用parameters标记该单元格。点击此处获取细胞标记说明。

此时,您的项目如下所示:

现在,让我们创建一个虚拟环境并运行我们的管道。创建虚拟环境的确切步骤取决于您使用的包管理器和虚拟环境管理器;如果在 Linux/macOS 上使用pip,这里有一些示例代码:

由于缺少依赖关系,管道可能会崩溃(也就是说,您将看到一个ModuleNotFound错误)。所以首先,使用pip install {name}安装任何缺失的依赖项,并在requirements.txt中添加一个带有包名的新行;请重试,直到管道运行。

我们的requirements.txt文件包含运行项目所需的依赖项列表。尽管如此,它并没有指定特定的版本;因此,今天运行pip install -r requirements.txt并在一个月后运行它将会产生一组不同的依赖版本(例如pandas 1.2pandas 1.3),这可能会破坏我们的管道。要防止这种情况,请执行以下操作:

pip freeze打印依赖项及其相应版本的详尽列表。下次运行您的管道时,您可以执行pip install -r requirements.lock.txt来获得您当前拥有的相同环境。确保每次修改requirements.txt时都重新生成requirements.lock.txt

如果您消除了ModuleNotFound错误,但是您仍然看到其他类型的错误,您的管道可能由于不同的原因而损坏。所以让我们进入下一阶段来解决这个问题。

步骤 2:使用数据样本进行冒烟测试

数据管道往往是长时间运行的过程,可能需要几个小时才能运行。为了加速调试过程,我们让它快速失败,修复错误,然后重复。

要启用快速测试,首先,在analysis.ipynb笔记本的parameters单元格中添加一个新变量:

并向您的pipeline.yaml添加一个新参数:

我们希望sample参数控制是使用整个数据集还是一个样本。现在,定位笔记本中正在加载或下载原始数据的所有单元格,例如:

并在那里使用sample参数:

您可以用一部分数据重现数据管道中的许多错误。这里的目标是加快执行时间,以便更快地删除这些错误。既然我们的管道默认使用一个样本运行,让我们重新运行它:

要运行整个数据集,执行ploomber build --help,您将看到要传递的 CLI 参数来切换sample参数。

继续使用示例运行管道,直到它成功执行。

解决常见不兼容问题的一些建议

在执行笔记本时,一个常见的错误来源是最初用于开发它的依赖版本(如pandas 1.2)与最新版本(如pandas 1.3)不同。如果任何库引入了破坏 API 的更改,您的管道将不再运行。修复依赖版本问题是一个反复试验的过程,所以要注意错误的类型,并查看库的文档来寻找 API 中的变化。如果是这种情况,请降级依赖关系,直到问题得到解决:

如果您做了这样的更改,记得再次生成requirements.lock.txt文件。

如果由于 API 更改而无法追溯错误,事情会变得更加复杂,所以让我们进入下一步。

第三步:修复破裂的管道

注意:如果步骤 1 中创建的管道运行正常,可以跳过这一步。

这一步的目标是确保您的笔记本从开始运行到结束。最有可能的是,该项目不包含任何测试,所以您必须知道代码是否做了它应该做的事情的唯一参考是原始的analysis.ipynb笔记本和记录的输出。

在 Jupyter 中打开pipeline.ipynb并从上到下运行它(确保转动sample=False以使用整个数据集)。然后,在每个单元格上,比较analysis.ipynb的输出(如果有的话),确保它匹配。这就是你的判断发挥作用的地方,因为一些代码行可能涉及随机过程(例如,绘制随机数);因此,输出不会完全匹配。

继续,直到:

  1. 输出不匹配,或者
  2. 密码被破解了

如果密码被破解了

如果某个单元格出现问题,请执行以下操作:

前面的命令从最新引发的异常开始调试会话。

%pdb进入 Python 调试器;学习如何使用它对于调试 Python 代码是非常宝贵的。Ploomber 的文档包含了一个在数据科学项目中使用调试器的例子。如果您想在调试模式下学习每个可用的命令,请查阅文档。

希望通过使用调试器,您能够找到并修复错误。

如果输出不匹配

这个场景比前一个场景更具挑战性,因为我们的代码没有失败,所以我们不知道在哪里调试。然而,您可能对代码中失败的地方有所了解;在每个地方,您都可以启动调试会话。

假设故障单元如下所示:

在仔细检查之后,您确定process_data_frame调用了some_function,并且错误可能就在那里。所以你去some_function源代码并编辑它:

现在,当调用process_data_frame(df)时,依次调用some_function(df),调试会话将开始。

调试不是一件容易的事情,但是使用调试器比在你的代码中使用print语句要好得多。查看 Ploomber 文档中的调试示例来学习一些调试技巧。

第四步:将.ipynb转换为.py

此时,您应该能够从头到尾无误地执行您的管道。这个步骤和接下来的步骤确保您的管道保持可维护性和稳定性。

对每个git push进行小的提交和测试对于确保您知道何时您的管道中断是至关重要的。此外,在修改了十行代码的提交中发现问题要比在二十次提交和数百行源代码更改之后容易得多。不幸的是,.ipynb很难管理,因为它们是包含代码和输出的 JSON 文件。例如,如果您有一个pipeline.ipynb文件,编辑单个单元格,并提交新版本,您将看到如下内容:

图片作者。

杂乱无章,很难说是什么发生了变化。为了解决笔记本不同的问题,我们将使用jupytext把我们的.ipynb文件转换成.py:

但是不要担心!多亏了 jupytext 的 Jupyter 插件,你仍然可以将它们作为笔记本打开。

图片作者。

注意: Jupytext 有许多输出格式,查看文档了解更多信息。

一旦您将pipeline.ipynb转换为.py,请更新您的pipeline.yaml文件:

确保一切仍然正常工作:

ploomber build

如果管道运行,删除pipeline.ipynb

下一个

到目前为止,我们能够在一个全新的环境中执行我们的管道:我们安装依赖项,然后执行ploomber build,但是我们的管道仍然是一个整体:我们所有的代码都在一个文件中。在本系列的下一部分中,我们将解决这个问题,并开始将我们的逻辑分成更小的步骤。

为了确保你知道第二部何时上映,在 Twitter 上关注我们或订阅我们的时事通讯。回头见!

更新:第二部此处可用。

发现错误?点击这里让我们知道。

原载于ploomber . io

将 Jupyter 笔记本重构为可维护的管道:分步指南(第二部分)

原文:https://towardsdatascience.com/refactoring-a-jupyter-notebook-into-a-maintainable-pipeline-a-step-by-step-guide-part-ii-138a8c395764?source=collection_archive---------25-----------------------

从 Jupyter 开发可维护的项目

将 Jupyter 笔记本转换成模块化可维护项目的详细指南。

图片作者。

更新:我们发布了 [***soorgeon***](https://github.com/ploomber/soorgeon) ,一款自动重构遗留笔记本的工具!

在本系列的第一部分中,我们描述了如何从传统笔记本过渡到我们可以用ploomber build执行的单任务流水线。在第二部分,我们将打破单一的笔记本,将我们的工作模块化为更小的任务。

步骤 5:确定任务

存在于单个文件中的项目很难调试,因为变量可能会意外交互。因此,我们想要一个有清晰边界的结构化项目,一个“任务”的输出成为下一个任务的输入;我们称之为数据管道

图片作者。

在这一步,我们检查代码以确定应该在哪里拆分笔记本。例如,假设您的笔记本看起来像这样:

你的代码可能会比这个例子更加广泛和混乱,但是目标是在代码段之间定义分割。例如,在这个例子中,我们对数据做了三件事:加载、清理和绘图;因此,我们可以把它分成三个部分。我们通过添加 Markdown 标题来做到这一点,这样我们可以快速识别每个部分的开始和结束位置(要了解如何在 Jupyter 中插入 Markdown 单元格,单击此处)。

注意: Markdown 有多种类型的标题;一个 H1 头球看起来像这样# I'm an H1 heading,而一个 H2 头球看起来像这样:## I'm an H2 heading

大多数笔记本都比这本书混乱得多,所以在定义节之前,您可能需要稍微重新组织一下代码单元。如果要加载多个数据集,请确保每个部分一次只处理一个数据集:

定义好笔记本的分区后,请转到下一步。

第六步:打破整块石头!

这一步是我们最终打破整体,并开始组装一个多级管道。通过在任务之间提供明确的界限,我们为每个任务建立了更精确的目的,并且可以快速测试我们的输出。

定义任务边界有些武断,但这里有一些经验法则:

  1. 在单独的分支中转换每个数据集(例如,如果使用两个数据集,您的管道应该有两个分支);当两个数据集需要连接时,合并分支。
  2. 对于每个数据集:一个任务是下载数据,另一个任务是清理数据(如果在 ML 管道上工作,还有一个任务是生成要素)。
  3. 如果处理 ML 管道:一个任务用于连接所有特征和标签,一个用于训练模型,另一个用于评估。

例如,假设我们有两个数据集需要加载、清理和绘制,我们希望我们的管道看起来像这样:

图片作者。

所以现在是时候把我们的项目分成两个文件了。按照我们的例子,我们向后应用切割,我们制作的第一个切割在我们的笔记本的末尾:

我们将绘制数据的代码移到一个新的脚本中,并编辑我们的pipeline.yaml文件:

编辑完pipeline.yaml文件后,执行以下命令来生成plot.py文件:

我们在这里做了一些改变。首先,我们修改第一个任务的第一个产品,因为它现在生成两个输出:笔记本和数据集 B 的干净版本。然后,我们添加了一个新任务,它执行plot.py(将绘制数据集 B 的代码复制到这里)并生成另一个笔记本。

我们希望实现以下目标:

图片作者。

如何实现这一点需要解释如何在 Ploomber 中构建管道。很简单,只需要做一些代码修改,把标绘代码移到plot-b.py;查看我们在文档中的完整示例。

一旦你学会了 Ploomber 的基础,重复同样的过程,直到你有一个包含许多小脚本的管道。确保使用ploomber build运行管道,并检查输出是否仍然匹配。

步骤 7:添加集成测试

到目前为止,我们已经手动检查了每个管道任务的已执行笔记本输出,以确保在进行更改后结果仍然匹配。不幸的是,从长远来看这是不实际的,所以让我们自动化它。

构建我们的管道允许我们嵌入验证每个输出完整性的集成测试。例如,让我们假设我们已经将我们的分析分解为两个步骤(如上一节中的图像所示);我们可以在执行下一个任务之前测试每个任务的输出:

图片作者。

为了测试我们的管道任务,我们可以在每个阶段定义一些期望的属性;例如,我们可以声明数据集 B 的干净版本必须具有以下属性:

  • age中没有 NAs
  • 正数小于 100 的列age
  • category必须有值abc

我们可以将这样的数据期望转换成集成测试,以便在每次运行管道时验证它们。最简单的方法是在每个任务的末尾添加一些assert语句:

或者,我们可以使用 Ploomber on_finish钩子在任务正确执行后运行任意函数。要获得完整的示例,请查看我们的管道测试教程。

测试数据管道很棘手;请查看我在 2020 年 PyData 全球大会上的演讲,我会详细介绍这一点。

步骤 8:在每个git push上运行您的管道

到目前为止,我们已经通过执行ploomber build在本地测试了我们的管道。然而,最好在每个git push上自动执行管道;启用自动化测试就是创建一个小脚本来安装依赖项并运行我们的管道:

如何在每个git push上运行一些脚本取决于我们使用的 git 服务。例如,如果使用 GitHub,我们可以使用 GitHub Actions ,所有托管 git 库的服务都提供类似的东西;查看 git 服务的文档以了解更多信息。

结语:保持你的管道正常运行

恭喜你!您现在有了一个健壮的、可维护的管道,它允许您更快、更有信心地引入变更。但是记住永远保持这种方式;如果不小心处理,很容易将管道退化为类似于我们已经开始的东西。因此,这里有一些应对你可能遇到的情况的技巧。

修改任务

无论何时你必须修改一个任务,都要一小步一小步地去做,并且运行你的管道来确保在每一个小的修改之后一切都正常工作。

添加新数据集

如果需要向分析中添加新的数据集,请创建一个新的笔记本。然后,探索这些数据,直到您对其有了基本的了解,并决定是否将其纳入您的管道。

如果您决定将数据集合并到管道中,通过将您的探索性代码作为新任务合并到管道中来添加一个新分支(记住将您的.ipynb转换为.py文件):

图片作者。

随着你对数据了解的越来越多,你可能会开始添加代码来清理它,如果你正在开发一个机器学习管道,你可能会开始创建功能;所以把逻辑分解成更小的部分;典型的结构如下:

图片作者。

记得添加集成测试。此外,确保加载 take 的任务可以使用一个sample参数来运行带有数据样本的代码,以便进行快速测试。

添加新的数据集是项目中的一个里程碑,因此请确保记录下来。我强烈建议您创建一个CHANGELOG.md文件来记录项目中这些类型的重大变更:

删除数据集

如果不再需要某个数据集,请删除处理该数据集的整个分支。保留死代码没有任何意义。确保你在你的CHANGELOG.md档案中记录了这一点。

代码质量

为了保持项目的可维护性,保持代码的可读性是必不可少的。像 flake8 和 black 这样的包是对你的代码进行 lint 和自动格式化的好选择。要了解更多关于林挺的知识并获得保持代码整洁的技巧,请查看我们的指南写整洁的笔记本。

最后的想法

转换传统的基于笔记本电脑的管道需要花费很多精力,但这是值得的。根据我的经验,许多数据项目失败是因为它们建立在薄弱的基础上,阻碍了它们取得稳定的进展。通过构建和测试您的管道,您已经走在了游戏的前面,您将能够自信地对您的工作进行更改,并确保您可以随时重现您的结果。

发现错误?点击这里让我们知道。

最初发布于ploomber . io

重构机器学习项目

原文:https://towardsdatascience.com/refactoring-machine-learning-projects-f566607a7b6f?source=collection_archive---------34-----------------------

托德·夸肯布什在 Unsplash 上的照片

从好(或坏)到好

作为数据科学家,我们花费大量的精力从头开始构建新的工具、管道和工作流。然而,随着我们的工作越来越成熟,我们最终都会发现自己面临这样的情况:我们需要对已经构建好的东西进行调整,而不是从头开始。拥有重构技能变得至关重要。

想象一下:你的公司有一个机器学习管道正在生产,它的预测正逐渐失去准确性。商业利益相关者在抱怨。他们指望你去“解决它”。现在怎么办?是时候进行重构了!

我们从什么开始?

有些代码,功能性的(ish),不知何故不能满足用户的所有需求。出于我们的目的,我们正在谈论机器学习模型或管道。

谁写的?

你或者其他人。这可能重要,也可能无关紧要——不久前的你对你现在的想法来说,可能和一个完全不同的人没什么不同。很难记住我们过去做了什么以及为什么,除非我们总是写完美的文档,但没人会这样做。

为什么要重构?

要么它总是需要一些工作(当我们开发最小可行的产品时,这是常见和正常的),要么它很棒,但是需求和环境已经改变了,所以你需要让它再次起作用。

你可能认为从头开始会更容易——这是可能的,但很可能会导致更多的工作。一个好的重构应该会节省时间,因为你可以在原作品的基础上构建。如果原作根本无法出售,那就是另一个问题了。

因此,让我们来讨论一些可能出现这种情况的特定 ML 示例。

情节

新维护者

这可能意味着你正在接手别人做的项目,或者别人正在接手你的项目。这并不一定意味着重构是必要的,但是如果重构有价值的话,这是评估和实施重构的好机会。信息将被传递给一个新的人,而这个新的人可能有如何改进现有工具的想法!

缩放

模型通常从对性能的最低期望开始。您的管道可能每小时服务一两个用户,或者每周接收新数据,但是现在它需要每分钟服务二十个用户,或者每小时接收数据。这种期望尺度的转变表明重构正在有序进行。

从研发阶段&转向生产阶段

如果您构建了一个模型来演示什么是可能的,那么将它转移到生产实现肯定需要一些更改。你必须确保该模型与它所依赖的或馈入的管道的其他元素相集成,并且你还需要确保它在规模上的性能。

模型漂移

当模型数据环境中的某些因素发生变化,并且模型对数据中关系的预期不再正确时,就会发生模型漂移。这可能意味着你的模型认为变量 A 和结果 B 是线性相关的,但是现在结果 B 发生了变化,所以关系也发生了变化。你的模型不知道为什么,但是突然它对结果 B 的预测就错了。这意味着是时候回顾这个模型了,检查这些关系,也许重新训练或者重构这个模型来更好地反映它周围的世界。

源数据变化

这在业务中经常发生——模型上游的数据收集系统或数据工程管道发生变化,现在数据可能以不同的方式测量,或者以不同的量出现,或者添加或删除了一些功能。这些都会影响模型的性能,并且可能需要重构。即使在只有新特性可用的情况下,如果您的模型没有使用它们,但是可以通过添加它们来提高性能,那么您应该考虑重构来合并这些新信息。

???

最后,还有一种情况是“它不起作用”,没有人知道为什么。这种情况比你想象的要多,尤其是当项目的原作者已经不在的时候。所有这些情况都说明了文档在机器学习中的关键重要性,因为理解代码和模型的情况比这要好得多。

因此,你正面临一个机器学习项目,其中一种情况适用于你。现在怎么办?

时间

你需要意识到你周围的业务和实际限制。理想情况下,您将有时间和资源来深思熟虑地进行这种重构,并产生一个有弹性的结果,并将在相当长的时间内继续工作。

当然,我们都知道情况并非总是如此。你可能时间紧迫,需要这个项目尽快重新开始。如果这是你的情况,那么你最好对项目做一个小小的修补,让事情正常运行。

例如,如果提供给模型的数据流发生了变化,也许要找出您需要做的最小调整,这样模型才能产生合理的结果。完整的重构将涉及检查新的数据流,将其与原始数据流进行比较,可能使用新的数据选项重新训练模型,并设计新的管道。但权宜之计可能意味着找到最接近原始特征的新数据通道,格式化/重命名它们以适应模型预期,然后将它们粘合在一起。这并不理想,但短期内可能行得通。

不过,从这里开始,让我们假设您有时间认真地进行重构。如何进行?

规划

有一件事你不应该做,那就是一头扎进代码编写中。相反,想想这个重构是关于什么的,你想要达到什么目的。

设定目标来规划任务,避免任务进展

为什么要重构这个?这种重构成功的衡量标准是什么?你怎么知道什么时候完成了?

一个模型可以有很多种改进:更短的代码,更干净的代码,更多的 pythonic 代码,更快的预测,更容易使用,更可读,更可解释,降维,更快的训练…你明白了。选择一两个,但是不要期望你会写出完美的管道或者模型。认真思考你能做什么,而不仅仅是在理想世界中什么会很酷或者你想做什么。事情会出现,优先事项会转移,你需要考虑什么是现实的。

看大局。

有时发现小问题、小麻烦更容易,但是如果你要重构一段代码或一个项目,那么这个小问题可能预示着一个你可以修复的系统问题。在公共卫生领域,我们可以称之为根本原因分析——不要只治疗症状,而要着眼于造成这种情况的决定因素。

例如,如果您的数据流中的一个通道发生了变化并出了问题,请不要认为您的所有其他数据通道仍然正常!他们可能是,或者他们可能已经悄悄地改变了,需要不同的处理。

了解功能

即使当前的项目由于某种原因不够充分,它仍然有历史。不像从零开始一个项目,在重构中,对于项目应该如何运行有预先存在的期望。输出可能需要特定的格式,并且可能已经做出决定以适应原始代码的行为。当然,这并不意味着那些原始的行为是好的——但是你必须与用户的期望和组织的现实以及任何技术挑战作斗争。发展不是生活在真空中,它必须为周围的人服务。

所有这些都引出了突破性变革的问题。如果你真的确定原来的行为是不好的,或者如果你需要的改进将要发生,它是不可持续的,那么你就在谈论做出突破性的改变。用户期望的行为将不再是这样,他们的旧代码很可能需要更改。你基本上是在为更多的重构创造级联需求。这可能是正确的做法!但你需要非常、非常确定和现实地认识到这对除你之外的人意味着什么。

记录下来!

了解我的人都知道我非常喜欢文档。我只是认为让我们的工作真正有用非常重要。这在重构中有两种方式。

首先,原始项目是否有文档记录?是否清楚所有元素做了什么,为什么?如果没有,你重构的第一步将是深入理解原始代码。阅读它,就像你计划记录它(或做它!),你就会真正理解代码了。这是重构成功的关键。

第二,你的重构需要被很好地记录,以便人们可以使用它,并且它将具有持久的力量。一般来说,这对于代码项目来说是正确的,但是在这种情况下,你的代码至少要替换掉某些以前被某人理解的东西,所以新的替换物需要易于插入和适应。另外,你的重构可能需要证明它足够好或者增加了价值,文档是一个很好的方法。

做工作

此时,你知道你想要实现什么,你知道最初的项目做了什么,你知道哪里出了问题。您已经准备好编写代码了!因为你参与的计划,这应该容易得多。当你工作的时候,要记住更多的注意事项。

  • 尽可能保留原项目。如果有人已经写了一些你可以使用的东西,不要重新发明轮子。同时,砍掉没用的。不要做代码包鼠。
  • 尝试创造风格上的一致性。如果原始代码使用特定的代码风格约定,要么保留它,要么使整个项目适应您的新偏好。理想情况下,重构的结果不是不同代码约定的弗兰肯斯坦怪物,而是其他人可以容易理解的内聚整体。
  • 在没有替代品或给用户一个好的理由/解释的情况下,不要取消功能。不要在主分支上砍去,在平行分支上开发,直到你的工作为用户准备好并且已经被评审。如果你决定需要一个突破性的改变,就像我们之前讨论的那样,那么就把它说清楚,并适当地设定人们的期望。
  • 如果可以的话,将测试或质量控制元素添加到你的重构中,这样将来的中断或问题更容易被识别。您现在是代码应该做什么的专家,所以您最有资格编写该功能的测试。这为你未来的成功和更轻松的工作打下了基础。

完成任务

数据科学家常常会因为追求完美而陷入项目中。我从商业数据科学中学到的最大教训之一是,获得一个最低限度的可行产品并不可耻,尤其是当你能够快速产生商业价值的时候。你不必因为完成了基本要素就放弃改进工作,但是你也不能永远拖着这个项目。

重构也是如此。正如我们在规划部分所讨论的,你不可能在一次重构中使这个项目完美——完美的代码是不存在的。代码总是一项进行中的工作,模型也是如此。无论你的重构做得多好,你的模型周围的环境总是有可能发生变化,需要一个新的重构。不要让这个阻碍你完成好工作。

相反,要确保尽可能地向重构中添加更多的通用性和文档,以便将来的重构(几乎肯定会发生)更容易、更快——给未来的自己一份礼物。

结论

我希望这能帮助你满怀信心地设想和实施你的下一次机器学习重构!深思熟虑地提前计划会让你和你的用户现在和将来的整个努力变得更容易。

免责声明:我是 Saturn Cloud 的高级数据科学家,Saturn Cloud 是一个使用 Dask 实现 Python 易于使用的并行化和扩展的平台。如果你想了解更多关于土星云的信息,请访问我们的www . Saturn Cloud . io

Quick plug:如果您有关于生产化和/或扩展 ML 工作流的需求,Saturn Cloud 可以提供帮助。我们提供了一个平台,可以消除扩展模型的苦差事,让您轻松访问多 GPU 集群和 Dask,并让您做自己最擅长的事情——机器学习。我们还为企业客户提供重构和扩展模型支持。 今天就联系我们了解更多!

DAX 中半可加测度的精化

原文:https://towardsdatascience.com/refinement-of-semi-additive-measures-in-dax-4e148fa83f56?source=collection_archive---------28-----------------------

在之前的故事中,我解释了一些关于半加性测度的细节。这里有更多关于这个话题的提示

丹尼·米勒在 Unsplash 上的照片

介绍

在我的故事中,我解释了一些关于半加性度量和可能的副作用的细节。

在与一些客户讨论了这些解决方案后,我们发现了基础措施的一些奇怪的后果。现在,我将向您展示如何解决这些后果,并为您提供更详细的解决方案的灵感。

让我们从数据开始

对于本文,我使用了微软的 Contoso 数据集。

您可以从这里获得 Power BI 文件: ContosoSalesForPowerBI

由于 Power BI 文件缺少库存表,所以我从这里获得了原始数据库: Microsoft Contoso BI 零售业演示数据集

我将它加载到 Azure SQL 数据库中。我在数据库上创建视图来创建一个纯星型模式,而不是原始的雪花型模式,并且只将需要的数据导入到 Power BI 文件中。

我使用了自己创建的日历表,而不是原始的日期表,并使用了原始数据集的库存表。

基础措施

基本度量具有以下定义:

Last Stock by Date Variant 1 =VAR LastDateWithInventory = LASTNONBLANK(‘Date’[Date],
    SUM(‘Inventory’[OnHandQuantity]))VAR Result =
    CALCULATE(
        SUM(‘Inventory’[OnHandQuantity]),
        LastDateWithInventory)RETURN
    Result

此度量通过 SUM()表达式的结果获取最后一个日期。然后返回该日期的表达式结果。

问题是,当您使用日期层次结构时,LASTNONBLANK()返回包含数据的周期的最后一个日期。

因此,当此最后日期存在值时,该度量将仅返回一个值。

示例见下图:

图 1-没有小计值的结果

因为我们只有 2009 年 10 月最后一天的数据,所以我们只能看到这个月的小计。该度量不返回其他月份的小计,也不返回 2009 年的总计,因为不存在 31 个月份的数据。2009 年 12 月

在对《使用 Power BI 和 Power Pivot for Excel 分析数据》一书*(商业技能)* *进行了一些研究后,我发现了这个变体:

Last Stock by Date Variant 2 =VAR LastDateWithInventory = **LASTNONBLANK(‘Inventory’[DateKey],
    NOT ( ISEMPTY(‘Inventory’) )**
    )VAR Result =
    CALCULATE(
        SUM(‘Inventory’[OnHandQuantity])
        ,TREATAS(LastDateWithInventory
                ,’Date’[Date])
    )RETURN
    Result

不同之处在于度量如何使用数据评估最后一个日期。LASTNONBLANK()从库存表中获取最新的日期。

CALCULATE()使用 LASTNONBLANK()的结果,并借助 TREATAS()将库存表中的日期映射到日期表。

你可以在下面的图片中看到结果。

图 2 —每月总计的新度量

图 3——日级别的详细新度量

在图 2 中,您可以看到我们得到了每个月的结果。

哪个结果是正确的?

但是,这可能不是正确的结果。正如您在图 3 中看到的,我们用数据获得了最后一天的值。

如果我们想获得一个月中最后一天的股票价值,但是我们没有该月最后一天的数据,那么结果可能是不正确的。

下表显示了两种产品的库存:

图 4—原始库存数据

看看二月的 M2101 蓝色笔记本电脑。
二月底,股票是 24。

但是三月有什么?
股票是 19 还是 0?

这同样适用于 M1548 白色笔记本电脑。
二月的股票可以是 24 也可以是 0。三月也一样。是 19 还是 0?

这取决于数据的定义。数据定义了两个度量中哪一个是正确的。

如果没有库存数量的日期意味着库存是空的,那么 0 将是三月和两种产品的正确结果。另一方面,如果我们只在某些事情发生变化时才获得库存数量,那么最新的值(19)将是 products 和 March 的正确结果。

要考虑的数据定义是使用半累加性度量时需要考虑的一个问题。但是还有更多可能的问题。

没有时间背景会发生什么

有时我想创建一个不设置日期过滤器的报告。在本例中,我想从 Inventory 表中获取最新的库存数量。

要解决这样的请求,我需要遵循两步走的方法:

步骤 1:为库存数据的最后日期添加一个标志。

我将使用上下文转换向库存表添加一个计算列:

Is Last Snapshot =VAR LastSnapshot = CALCULATE(
    MAXX(‘Inventory’
    ,’Inventory’[DateKey])
    ,ALL(‘Inventory’)
    )VAR Flag = IF(‘Inventory’[DateKey] = LastSnapshot, TRUE(), FALSE())RETURN
    Flag

现在我可以创建一个度量,它检查日期是否被过滤。否则,该度量将只针对是最后一个快照 = TRUE()的行。

所需测量的代码如下:

Last Stock by Date Variant 3 =VAR LastDateWithData =
    LASTNONBLANK(‘Date’[Date],
        SUM(‘Inventory’[OnHandQuantity])
    )VAR isTime =
    ISFILTERED ( ‘Date’ )RETURN
    IF (
        isTime
        ,CALCULATE(
            SUM ( ‘Inventory’[OnHandQuantity] )
            ,TREATAS( LastDateWithData
            ,’Date’[Date]
            )
        )
        ,CALCULATE (
            SUM ( ‘Inventory’[OnHandQuantity] ),
            FILTER ( ‘Inventory’
                ,‘Inventory’[Is Last Snapshot] = TRUE ()
                )
            )
        )

如您所见,该度量基于基本度量的第一个变体。

结果如下:

图 5—有日期和无日期的库存

这项措施有两个好处:

  1. 如果没有筛选日期,您可以定义要使用的日期。
  2. 性能提高了约 30%,因为基于具有两个不同值的列过滤库存比基于相关(日期)表过滤数据要容易得多。

如果您使用第二种方法,就不需要这个解决方案,因为它会返回相同的结果。

好了,现在我们有三种半可加测度的工作方法。但是当我们有矛盾的需求时会发生什么呢?

结合多种需求

我的一个客户要求我开发一种方法来计算正确的股票数量。

他加载股票数据的 ETL 过程被设计成每天加载当前的股票数量,甚至在周末也是如此。

主要的请求是只显示每个月最后一天的库存数量。

当用户没有在报告中选择任何日期时,该度量必须返回最近可用的库存数量。

第三个变体正好提供了所需的结果。

但是当月呢?没有当月最后一天的可用数据。这意味着该度量不会返回任何值,这是错误的。

现在我需要创造一种方法来改变这种行为。

第一步是在实际过滤器上下文中选择具有最新数据集的日期:

VAR LastDateWithData =
LASTNONBLANK (
        ‘Inventory’[DateKey]
        ,SUM ( ‘Inventory’[OnHandQuantity] )
        )

但是,由于我还需要库存表中数据的最后可能日期,所以我需要另一个变量:

VAR LastDateWithData_FromData =
CALCULATE (
    LASTNONBLANK (
        ‘Inventory’[DateKey]
        ,NOT ISEMPTY ( ‘Inventory’ )
        )
    ,REMOVEFILTERS(‘Inventory’)
    )

REMOVEFILTERS('Inventory ')的使用确保了我可以获得整个 Inventory 表的最后一个可能的日期。

如果您有一个标记最新股票价值的计算列,您可以使用一个更简单的公式,就像上面显示的公式创建[Is Last Snapshot]列,

VAR LastDateWithData_FromData =
    CALCULATE(
        MAX(‘Inventory’[DateKey])
        ,’Inventory’[Is Last Snapshot]
        )

现在,我需要计算存储在变量 LastDateWithData_FromData 中的日期所在月份内的所有日期:

VAR AllDatesWithLastData =
    CALCULATETABLE(
        SUMMARIZE(
            ‘Date’
            ,’Date’[Date]
            )
        ,FILTER(‘Date’
        ,INT( ‘Date’[MonthKey] ) = ( YEAR(LastDateWithData_FromData) * 100 ) + MONTH(LastDateWithData_FromData)
        )
        ,ALL ( ‘Date’[Date] )
        )

最后一个变量是检查日期表上是否有过滤器:

VAR isTime =
    ISFILTERED ( ‘Date’ )

现在我可以组合所有内容并返回正确的值:

RETURN
    IF (
        AND ( isTime, NOT LastDateWithData IN AllDatesWithLastData )
        ,CALCULATE(
            SUM ( ‘Inventory’[OnHandQuantity] )
            ,TREATAS( LastDateWithData
                    ,’Date’[Date]
                    )
        )
        ,CALCULATE (
            SUM ( ‘Inventory’[OnHandQuantity] ),
            FILTER ( ‘Inventory’
                     ,‘Inventory’[Is Last Snapshot] = TRUE () )
             )
         )

关键部分是 IF 条件。

  1. 当日期表
    上出现过滤器时
  2. 实际筛选器上下文中的日期不是库存表中最新数据集所在月份的一部分

2-a .返回由变量 LastDateWithData 过滤的股票数量。
需要 TREATAS()来将过滤器的沿袭修正到数据表中

3.如果 If 条件不为真,则

3-a .我们要么是上个月出现在数据中的
要么是

3-b .日期表上没有过滤器

结果看起来像下面的截图:

图 6—最终结果

第一个表显示了第三个变量的结果,第二个表显示了组合测量的结果。

在下面的屏幕截图中,您可以比较度量的第三个变量的结果与没有对日期表进行任何筛选的组合度量的结果。

图 7 —没有日期过滤器的最终结果

如您所见,第二个表显示了数据集中最后一天的库存数量:2009 年 12 月。

全部措施

在这里,您可以找到组合所有变量的测量的完整代码:

Last Stock by Date Variant 4 =
VAR LastDateWithData =
    LASTNONBLANK (
        ‘Inventory’[DateKey]
        ,SUM ( ‘Inventory’[OnHandQuantity] )
        )VAR LastDateWithData_FromData =
    CALCULATE(
        MAX(‘Inventory’[DateKey])
        ,’Inventory’[Is Last Snapshot]
        )// CALCULATE (
// LASTNONBLANK (
// ‘Inventory’[DateKey]
// ,NOT ISEMPTY ( ‘Inventory’ )
// )
// ,REMOVEFILTERS(‘Inventory’)
// )VAR AllDatesWithLastData =
    CALCULATETABLE(
        SUMMARIZE(
            ‘Date’
            ,’Date’[Date]
         )
         ,FILTER(‘Date’
         ,INT( ‘Date’[MonthKey] ) = ( YEAR(LastDateWithData_FromData) * 100 ) + MONTH(LastDateWithData_FromData)
         )
         ,ALL ( ‘Date’[Date] )
         )VAR isTime =
     ISFILTERED ( ‘Date’ )RETURN
    IF (
        AND ( isTime, NOT LastDateWithData IN AllDatesWithLastData )
        ,CALCULATE(
            SUM ( ‘Inventory’[OnHandQuantity] )
            , TREATAS( LastDateWithData
                       ,’Date’[Date]
                       )
        )
        ,CALCULATE (
            SUM ( ‘Inventory’[OnHandQuantity] ),
            FILTER ( ‘Inventory’
                   ,‘Inventory’[Is Last Snapshot] = TRUE () )
                   )
)

在这个 DAX-Measure 中,您可以找到变量 LastDateWithData_FromData 的两个变量。

注意——不要简单地复制和粘贴本文中的代码

在开始复制本文中显示的代码之前,您需要仔细确定想要的结果。

哪种变体最符合您的需求?

最后一种组合变体是最慢的,但也是最灵活的。但是,你需要这样的灵活性吗?如果没有,使用更简单的变体。

此处显示的变体适用于 Power BI 和 SQL Server Analysis Services (SSAS)。

但是,当您专门在 Power BI 中工作时,您可能会对使用

Last Stock by Date =
    LASTNONBLANKVALUE('Date'[Date],
        SUM('Inventory'[OnHandQuantity])
    )

同样,您需要用不同的场景测试结果,并决定这种变体是否在任何情况下都能提供正确的结果。

结论

我总是试图理解需求,并向我的客户展示不同的场景,以确保我们谈论的是同一件事。很难避免认为“这很容易。我知道我的客户在问我什么。但是最重要的是,至少要经历两到三次迭代,才能确保双方对解决方案的预期有相同的理解。

最后但同样重要的是,试着理解为什么一个度量返回一个特定的结果。

当你理解你的方法是如何工作的,以及为什么结果会因不同的场景而不同时,你的生活会变得容易得多。

一旦你理解了你的指标是如何工作的,你就离成为 DAX 大师更近了一步。

吉姆·泰格曼在 Unsplash 上的照片

我希望你喜欢这个旅程,谢谢你一直读到最后。

*书籍参考:
使用 Power BI 和 Power Pivot for Excel 分析数据(业务技能)
微软出版社
ISBN-13:978–1509302765
ISBN-10:150930276 x

参考链接与符号链接与硬链接,以及它们如何帮助机器学习项目

原文:https://towardsdatascience.com/reflinks-vs-symlinks-vs-hard-links-and-how-they-can-help-machine-learning-projects-b77b89cdbab1?source=collection_archive---------19-----------------------

行业笔记

作者图片

硬链接和符号链接自古以来就有了,我们无时无刻不在使用它们,甚至连想都没想过。在机器学习项目中,当建立新的实验时,它们可以帮助我们在机器学习项目中快速有效地重新排列数据文件。然而,对于传统的链接,我们冒着错误编辑污染数据文件的风险。在这篇博文中,我们将回顾使用链接的细节,现代文件系统中一些很酷的新东西(引用链接),以及 DVC(数据版本控制,https://dvc.org/)如何利用这一点的例子。

当我在学习机器学习时,我希望有一个工具能让我们像检查常规软件工程项目一样检查 ML 项目。也就是说,在任何给定的时间检索项目的状态,基于项目的早期状态创建分支或标签(用 Git ),处理与同事的协作,等等。让 ML 项目与众不同的是巨大的数据量,数以千计的图像、音频或视频文件,经过训练的模型,以及使用 Git 等常规工具管理这些文件的难度。在我之前的文章中,我解释了为什么 Git 本身是不够的,为什么 Git-LFS 不是机器学习项目的解决方案,以及一些看起来对管理 ML 项目的工具有用的原则。

事实证明,DVC 非常擅长管理 ML 项目数据集和工作流程。它与 Git 协同工作,可以显示与任何 Git 提交相对应的数据集的状态。只需签出一个提交,DVC 就可以重新安排数据文件,使其与提交时的数据完全匹配。

考虑到潜在的数十亿字节的数据几乎在瞬间被重新排列,这个速度是相当神奇的。所以我在想:DVC 是如何完成这个魔术的?

重新排列千兆字节训练数据的诀窍

事实证明,DVC 像 Git 一样快速地重新排列数据和模型文件的秘诀是链接文件而不是复制文件。当然,Git 在检查提交时会将文件复制到位,但是 Git 通常处理相对较小的文本文件,而不是 ML 项目中使用的大型二进制 blobs。像 DVC 那样链接一个文件,速度快得令人难以置信,可以在眨眼之间重新排列任何数量的文件,同时避免复制,从而节省磁盘空间。

实际上,使用文件链接技术对这个领域来说并不新鲜。一些数据科学团队使用符号链接来节省空间并避免复制大型数据集。但是符号链接并不是唯一可以使用的链接。我们将从将文件复制到适当位置的策略开始,然后使用硬链接和符号链接,最后使用一种新类型的链接 reflinks,它在文件系统中实现了写时复制功能。我们将以 DVC 为例,说明工具如何使用不同的链接策略。

测试设置

因为我们将测试不同的链接策略,所以我们需要一个样例工作空间。工作区设置在我的笔记本电脑上,一台 MacBook Pro,主驱动器用 APFS T4 文件系统格式化。进一步的测试是在用 XFS 格式化的 Linux 驱动器上进行的。

使用的数据是从两个不同的日子检索的维基百科网站的两个“存根文章”转储。每个都是大约 38 GB 的 XML,给我们足够的数据来类似于一个 ML 项目。然后我们建立一个 Git/DVC 工作区,在这里可以通过检查不同的 Git 提交在这两个文件之间切换。

$ ls -hl wikidatawiki-20190401-stub-articles.xml
-rw-r--r--  1 david  staff    35G Jul 20 21:35 wikidatawiki-20190401-stub-articles.xml
$ time cp wikidatawiki-20190401-stub-articles.xml wikidatawiki-stub-articles.xmlreal    14m16.918s
...

作为一项基本措施,我们将注意到,将这些文件复制到工作区大约需要 15 分钟。很明显,如果在存储库中切换提交需要 15 分钟,那么 ML 研究员的生活不会很愉快。

相反,我们将探索 DVC 和其他一些工具使用的另一种技术——链接。现代操作系统支持两种类型的链接:硬链接和符号链接。一种新的链接类型,Reflink(写时复制),开始在 Mac OS X 和 Linux 的新版本中可用(为此需要所需的文件系统驱动程序)。我们将依次使用它们,看看它们的效果如何。

DVC 在其文档中讨论了与三种链接类型相对应的四种策略(第四种策略是复制文件)。所使用的策略取决于文件系统的功能,以及“dvc config cache.type”命令是否更改了配置。

DVC 默认使用引用链接,如果不可用,就退回到文件复制。它避免使用符号链接和硬链接,因为存在意外缓存或存储库损坏的风险。我们将在接下来的章节中看到所有这些。

使用文件复制的版本化数据集

在签出 Git 标记时将文件复制到适当位置的基本(或幼稚)策略相当于以下命令:

$ rm data/wikidatawiki-stub-articles.xml
$ cp .dvc/cache/40/58c95964df74395df6e9e8e1aa6056 data/wikidatawiki-stub-articles.xml

这可以在任何文件系统上运行,但是复制文件需要很长时间,并且会消耗两倍的磁盘空间。

奇怪的文件名是怎么回事?它是 DVC 缓存中的文件名,十六进制数字是 MD5 校验和。DVC 缓存中的文件通过该校验和进行索引,允许同一文件有多个版本。 DVC 文档包含了更多关于 DVC 缓存实现的细节。

实际上,DVC 是这样运作的:

作者图片

为了设置工作空间,我们创建了两个 Git 标记,每个标记对应于我们下载的一个文件。关于版本化数据集的 DVC 示例 ( 或本替代教程)应该会让您对设置工作空间涉及的内容有所了解。为了测试不同的文件复制/链接模式,我们首先更改 DVC 配置,然后检查给定的 Git 提交。运行“dvc checkout”会将相应的数据文件插入到目录中。

哦,伙计,那确实花了很长时间。这个的 Git 部分非常快,但是 DVC 花了很长时间。这是意料之中的,因为我们告诉 DVC 执行文件复制,我们已经知道使用cp命令复制文件大约需要 16 分钟。

至于磁盘空间,显然现在有数据文件的两个副本。一个拷贝在 DVC 缓存目录中,另一个拷贝在工作区中。

来源:https://www.xkcd.com/981/(知识共享)

使用硬链接和符号链接的版本化数据集

显然,复制文件来处理数据集版本是缓慢的,并且是对磁盘空间的低效使用。在类 Unix 环境中,一个自古以来就存在的选项是硬链接和符号链接。虽然 Windows 历史上不支持文件链接,但“mklink”也可以支持这两种类型的链接。

作者图片

硬链接是 Unix 文件系统模型的副产品。我们所认为的文件名实际上只是目录文件中的一个条目。目录文件条目包含文件名和“inode number ”,它只是 inode 表中的一个索引。Inode 表条目是包含文件属性和指向实际数据的指针的数据结构。硬链接只是两个具有相同索引节点号的目录条目。实际上,它是出现在文件系统中两个位置的完全相同的文件。硬链接只能在给定的装入卷中进行。

符号链接是一个特殊的文件,其属性包含指定链接目标的路径名。因为它包含路径名,所以符号链接可以指向文件系统中的任何文件,甚至可以跨已挂载的卷或跨网络文件系统。

这种情况下的等效命令是:

$ rm data/wikidatawiki-stub-articles.xml
$ ln .dvc/cache/40/58c95964df74395df6e9e8e1aa6056 data/wikidatawiki-stub-articles.xml

这是一个硬链接。对于符号链接,使用“ln -s”。

然后执行硬链接场景:

作者图片

符号链接场景是相同的,但是将cache.type设置为symlink。两种情况下的时间是相似的。

两秒钟(或更短)肯定比复制文件所需的 16 分钟要快得多。它发生得如此之快,以至于我们用了“瞬间”这个词。文件链接比到处复制文件要快得多。这是一个巨大的胜利。

至于磁盘空间消耗,请考虑以下情况:

$ ls -l data/
total 8
lrwxr-xr-x  1 david  staff   70 Jul 21 18:43 wikidatawiki-stub-articles.xml -> /Users/david/dvc/linktest/.dvc/cache/2c/82d0130fb32a17d58e2b5a884cd3ce

该链接占用的磁盘空间可以忽略不计。但是有一个问题需要考虑。

好吧,看起来不错,对吧?快速,没有额外的空间消耗…但是,让我们想想如果你在工作区编辑data/wikidatawiki-stub-articles.xml会发生什么。因为该文件链接到 DVC 缓存中的文件,所以缓存中的文件会被更改,从而污染缓存。你需要采取额外的措施,并学会如何避免这个问题。 DVC 文档中有使用 DVC 时避免该问题的说明。这意味着永远记住使用一个特定的过程来编辑数据文件,虽然不是一个交易破坏者,但不太方便。不过更好的选择是使用 reflinks。

使用引用链接的版本化数据集

硬链接和符号链接在 Unix/Linux 生态系统中已经存在很长时间了。我第一次使用符号链接是在 1984 年的 4.2BSD 上,硬链接可以追溯到更早。硬链接和符号链接都可以用来做 DVC 做的事情,即快速重新安排工作目录中的数据文件。但是在过去的 35 年多时间里,文件系统肯定有了一两个进步吧?

作者图片

确实有,Mac OS X 的“clonefile”和 Linux 的“reflink”功能就是例子。

写链接时复制,也称为引用链接,提供了一种快速将文件链接到工作区的解决方案,同时避免了污染缓存的任何风险。硬链接和符号链接方法因其速度而大获全胜,但这样做有污染缓存的风险。使用引用链接,写时复制行为意味着如果有人修改数据文件,缓存中的副本不会被污染。这意味着我们将拥有与传统链接相同的性能优势,以及额外的数据安全性优势。

也许,你和我一样,不知道什么是 reflink。这种技术意味着在磁盘上复制一个文件,这样“副本”就是一个类似于硬链接的“克隆”。与两个目录条目引用同一个索引节点条目的硬链接不同,引用链接有两个索引节点条目,共享的是数据块。它和硬链接一样快,但是有一个重要的区别。对克隆文件的任何写入都会导致分配新的数据块来保存该数据。克隆的文件看起来发生了变化,而原始文件未被修改。克隆非常适合复制数据集的情况,允许在不污染原始数据集的情况下修改数据集。

与硬链接一样,引用链接仅在给定的已装入卷内起作用。

参考链接在 Mac OS X 上很容易找到,在 Linux 上也很容易找到。此功能仅在某些文件系统上受支持:

  • Linux: BTRFS,XFS,OCFS2
  • 麦克·OS X:APFS

APFS 在 macOS 上是开箱即用的,苹果强烈建议我们使用它。对于 Linux,XFS 是最容易设置的,如本教程所示。

对于 APFS,等效的命令是:

$ rm data/wikidatawiki-stub-articles.xml
$ cp -c .dvc/cache/40/58c95964df74395df6e9e8e1aa6056 data/wikidatawiki-stub-articles.xml

使用-c选项,macOS cp命令使用clonefile(2)系统调用。clonefile函数设置命名文件的 reflink 克隆。在 Linux 上,cp命令使用--reflink选项。

然后运行测试:

作者图片

正如预期的那样,性能类似于硬链接和符号链接策略。我们了解到引用链接和硬链接、符号链接一样快,磁盘空间消耗也可以忽略不计。

这个链接很酷的一点是,即使文件是连接的,你也可以编辑文件,而不用修改缓存中的文件。被改变的数据在幕后被复制。

在 Linux 上,相同的场景以相似的性能运行。

结论

来源——DVC

我们已经了解了如何有效地管理大型数据集,就像机器学习项目中的典型情况一样。如果我们需要重新访问此类项目中的任何开发阶段,我们将需要一个系统来有效地重新安排大型数据集以匹配每个阶段。

我们已经看到,在任何 Git 提交时保存文件列表是可能的。有了这个列表,我们可以将这些文件链接或复制到工作目录中。这正是 DVC 在项目中管理数据文件的方式。使用链接,而不是文件复制,让我们快速有效地在项目的不同版本之间切换。

Reflinks 是文件系统的一个有趣的新特性,非常适合这个场景。Reflinks 创建起来和传统的硬链接和符号链接一样快,让我们可以快速复制一个文件,或者整个目录结构,而消耗的额外空间可以忽略不计。而且,由于 reflinks 在链接的文件中保存修改,它们给了我们比传统链接更多的可能性。在本文中,我们研究了在机器学习项目中使用引用链接,但它们也用于其他类型的应用程序。例如,一些数据库系统利用它们来更有效地管理磁盘上的数据。现在你已经了解了引用链接,你将如何使用它们呢?

此文最初发布于 dev.to

重构——代表机器学习中的问题

原文:https://towardsdatascience.com/reframing-representing-problems-in-machine-learning-e1805130db29?source=collection_archive---------23-----------------------

如何在定义你的 ML 问题时进行整体思考

詹姆斯·皮科克在 Unsplash 上拍摄的照片

我希望你在保持健康的同时阅读这封邮件。本周,我想让你思考一下你是如何构建你的 ML 问题的。我将介绍重构设计模式,它打破了表示问题的挑战。

设计模式是一种将专家的经验和知识标准化的方式,以解决任何领域的主要挑战。我们在人工智能领域已经走得太远了,是时候我们至少开始讨论这些设计模式了。

再构造

重构设计模式解决了使用变化的上下文输出提出直观的机器学习问题的挑战。这里,我们改变问题输出的表示。例如,一个直观的回归问题可以被重构为一个分类问题,反之亦然。

让我们更深入地理解这一点。

问题

每个机器学习项目都是从框定问题开始的。我们试图回答这样的问题:“这是一个有人监督的问题还是无人监督的问题?”,“我们在使用什么功能?”,“如果问题被监督,标签是什么?”、“什么样的精度或误差值是可接受的?”。您尝试根据手头的数据和构建模型的业务目标来回答这些问题。

举个例子,我们想预测给定位置在 20 分钟内的降雨量。现在,这似乎是一个简单的时间序列预测问题,我们将考虑历史气候和天气条件来预测降雨量。或者,这也可以定义为回归问题,因为标签(降雨量)是一个实数(例如 0.5 厘米)。

在多次训练您的模型后,您意识到所有您预测的降雨量都偏离了实际值。模型显示将会下 0.2 厘米的雨,但实际上却下了 0.4 厘米,这对于相同的特征集来说是令人惊讶的。问题是我们应该如何处理这种不确定性?我们应该如何改进我们的预测?

解决办法

这里的关键问题是降雨量遵循概率分布。对于相同的天气条件和特征,有时会下雨 0.2 厘米,有时会下雨 0.4 厘米。回归模型仅限于预测单个数字,而且预测正确的几率很小。

我们可以将我们的目标重新定义为一个分类问题,而不是试图使用回归模型来预测降雨量。这种转换可以通过不同的方式实现。一种方法是使用离散概率分布将输出建模为多类分类。该模型将返回一定范围内降雨量的概率,如下所示:

按作者

分类模型允许我们预测不同范围内降雨量的概率分布,而不是估计分布的平均值。这种模拟分布的方法是有利的,因为降水量不呈现正态分布,而是遵循特威迪分布,这允许点的出现率为零。

在的谷歌研究论文中采用了同样的方法,他们记录了一个 512 路分类模型来预测给定位置的降雨量。

为什么有效

在构建 ml 驱动的应用程序时,重构问题会有所帮助。我们不是将预测缩小到一个单一的实数,而是将预测目标放宽到一个离散的概率分布。

这种权衡的形式是由于 bucketing 造成的精度损失,但我们获得了全概率密度函数(PDF)的口才。与这种离散化的预测相比,回归模型的僵化本质使它们不太擅长学习。

反过来——我们什么时候应该坚持回归?

有些情况下,你会想把一个问题重新组织成一个回归任务。回答这个问题的一个问题是,我们希望我们的预测有多精确?

PDF(概率密度函数)的锐度影响回归模型的精度。

按作者

更清晰的 PDF 表示输出分布的标准偏差更小,而更宽的 PDF 表示标准偏差更大。因此有更多的差异。在密度函数非常尖锐的情况下,最好使用回归模型。

这些重构原则可以应用于许多应用中,如构建推荐引擎、客户细分等。通常,我们有多种合适的方法,但重要的是在你制定解决方案之前,全面地思考问题。

本周有趣的读物

这是我决定从本周开始增加的一个新的部分,我将分享至少一个有趣的博客/文章/章节,来自一本我读得非常喜欢的书。

本周有趣的阅读是网飞如何使用 ML 来提高其流媒体质量 。有趣的是,他们是如何从播放数据中构建这个机器学习问题的,这些数据是他们在向全球 1.17 亿观众播放时收集的。

该博客解释了分解网络质量和表征的复杂问题,并使用统计建模来预测网络在给定过去 15 分钟数据的情况下未来 15 分钟的吞吐量的思想。他们考虑的变量范围之广令人震惊。你会喜欢读这个的!

本周就到这里,下周五见!在那之前保持安全,戴上口罩!

和我联系!

如果你觉得这有帮助,可以考虑订阅我的时事通讯,因为我每周都会继续揭开这些挑战的神秘面纱。

如果您有一些建议、问题或想法,请随时回复本博客或对视频发表评论,或者您可以通过 Twitter 或 LinkedIn 与我联系。

正则表达式对 NLP 至关重要

原文:https://towardsdatascience.com/regex-essential-for-nlp-ee0336ef988d?source=collection_archive---------9-----------------------

理解各种正则表达式,并将其应用于自然语言处理中经常遇到的情况

照片由纳撒尼尔·舒曼在 Unsplash 拍摄

为什么正则表达式对 NLP 至关重要?

每当我们处理文本数据时,它几乎总是不以我们想要的形式出现。文本可能包含我们想要删除的单词、不需要的标点符号、可以删除的超链接或 HTML 以及可以简化的日期或数字实体。在本文中,我将使用 re 模块描述 Python 中的一些基本正则表达式,以及在 NLP 中我最终使用它们的一些常见情况。请注意,本文中的正则表达式是按照复杂度的递增顺序提到的。

一些基本术语

在我们开始之前,让我们先弄清楚一些基本情况。我将只解释本文后面用到的术语,这样就不会太难理解了。

**\w  represents any alphanumeric characters (including underscore)****\d  represents any digit**.   **represents ANY character (do not confuse it with a period )****abc literally matches characters abc in a string****[abc] matches either a or b or c (characters within the brackets)****?   after a character indicates that the character is optional*****   after a character indicates it can be repeated 0 or more times****+   after a character indicates it can be repeated 1 or more times****\   is used to escape special characters (we'll use this alot!)**

转义特殊字符

从上一节你可以看到许多字符被 re 作为特殊字符使用,并且有它们自己的意思。比如**。?甚至/也是一些可以避免文字匹配的字符。在这种情况下,如果我们真的想要匹配这些字符,我们必须在它们前面加一个反斜杠()。**考虑 **\?**这将从字面上匹配字符串为 a?

让我们把手弄脏吧!

我将使用 python 中的 re 模块来替换和搜索字符串中的模式。简单地使用它import re. 下面的正则表达式和用例是按照复杂程度递增的顺序排列的,所以你可以随意跳来跳去。

情况 1:删除出现在字符串开头或结尾的单词

说我们有一句话友好的男孩有一只漂亮的狗,这只狗很友好

现在,如果我们想删除第一个“the ”,我们可以简单地使用正则表达式 ^the.在使用 re.sub 时,第二个参数是被替换的单词,因为我们想完全删除它,所以我们用空引号替换我们的单词。

同样,为了删除 friendly ,我们在最后使用正则表达式 friendly$

请注意,只有开头和结尾的单词被删除,而其他出现的单词保持不变。

情况 2:删除数字和货币

这个很简单。在 NLP 中,我们经常删除数值,因为模型并没有真正地从中学习,特别是当我们有很多不同的数值时。

在句子中,**我有 500 卢比。**我们可以使用表达式 d+ 来匹配一个或多个数字。

现在我们来看一个微小的变化。假设我们有一个句子我有 500 美元,你有 200 美元。注意美元符号的位置是如何不同的。为了处理这个问题,我们可以使用 [$\d+\d+$] ,它使用\来逐字匹配$并确保出现在美元符号之前或之后的数字都匹配。

场景 3:处理各种日期格式

日期的问题在于你可以用不同的方式书写它们。2021 年 7 月 14 日、2021 年 7 月 14 日和 2021 年 7 月 14 日的含义相同。下面的表达式处理所有格式。

情况 4:删除超链接

当我们遇到完全想要删除的 URL 时,我们可以使用表达式https?:\/\/.*[\r\n]*来匹配以 https:// 开头的 URL

情况 5:提取 URL 的主域名

在 NLP 中,我们可能需要分析 URL。如果我们只对域名感兴趣,而对特定页面或查询参数的链接不感兴趣,那么我们需要使用一个表达式来使所有这样的链接统一。

说我要从https://rajsangani.me/abouthttps://rajsangani.me/或者 www.rajsangani.me(这最后一个不存在)****

在这种情况下,我可以使用下面的代码

注意我们在这里是如何使用 re.search 而不是 re.sub 的。我们要求代码在“”之后搜索 URL或一个' /' ,并在一个'之前结束。'****

场景 6:去掉一些标点符号

在自然语言处理中,我们必须根据手头的任务对文本进行预处理。在某些情况下,我们需要删除字符串中的所有标点符号,但是在情感分析这样的任务中,保留一些标点符号是很重要的,比如“!”表达了强烈情感。

如果我们想删除所有标点符号,我们可以简单地使用[^a-za-z0–9 ],它匹配除字母数字字符之外的所有内容。

但是说我们要把感叹号以外的东西都去掉。然后我们可以使用下面这段代码。

****#Removing all punctuation except exclamation marks**re.sub(r'[\.;:,\?\"\'\/]','','''Hi, I am :" Raj Sangani ! Nice to meet you!!!!!!''')**#Result: Hi I am  Raj Sangani ! Nice to see you!!!!!!****

根据需要,可以随意使用表达式中的标记。

情况 7:用一个感叹号代替两个或更多的感叹号

****同样,在进行情感分析时,我们可能会遇到有不止一个标点符号组合在一起的句子。感叹号很常见,尤其是在 Twitter 这样的社交媒体平台上。在句子嗨!我是拉吉·桑加尼!!很高兴见到你!!!!!!有多余的感叹号。如果我们想用一个感叹号代替两个或更多的感叹号,我们可以使用下面的表达式。 {2,}表示 2 个以上。

情况 8:用一个空格替换两个或多个连续的空格

当把 pdf 抓取或转换成文本文件时,我们经常会遇到单词间有不止一个空格的字符串。更糟糕的是,在一些地方,单词被完全分开(有一个空格),而在另一些地方,有 2 个甚至 3 个空格。为了使间距一致,我们可以使用下面的代码。

再次注意引号内{2,}前的空格,这是与空格匹配的部分。

情况 9:组合用连字符或空格分隔的单词

这是我最近在为一个项目做预处理时遇到的一个非常有趣的情况。像 cheesecake 这样的单词有时被写成两个间隔的单词 cheesecake,或者出现在连字符形式 cheese-cake 中。如果我们希望两个事件都被芝士蛋糕取代,我们可以使用下面的代码。

例如,在句子中,比起蓝莓芝士蛋糕或巧克力芝士蛋糕,我更喜欢草莓芝士蛋糕。

那个?这里表示空格和连字符是可选的。

结论

我希望这对在预处理文本时经常遇到这些情况的人有所帮助。请让我知道我是否错过了其他一些人们经常遇到的情况。

如果你喜欢这篇文章,这里有更多!

**** [## 轻松的探索性数据分析(EDA)

towardsdatascience.com](/effortless-exploratory-data-analysis-eda-201c99324857)

查看我的 GitHub 其他一些项目。你可以在这里联系我https://rajsangani.me/* 感谢您的配合!*****

区域和在线学习领域

原文:https://towardsdatascience.com/regional-and-online-learnable-fields-cbb18b74e01e?source=collection_archive---------37-----------------------

一种新的,不太新的聚类算法

(图片由作者提供)

这篇文章将引导你了解地区和在线学习领域。一个看似未被识别的聚类算法,它提供了 k-Means 和 k-Nearest-neighbors 的几个优点。

区域和在线学习领域

首先由 Rolf Schatten、Nils Goerke 和 Rolf Eckmiller 在 2005 年提出,区域和在线可学习领域是一种在线和无监督的聚类技术[1]。总的想法是用所谓的 ROLF 神经元来近似我们的数据点,在后面的步骤中,可以将这些神经元连接起来形成簇。该算法源于 k-Means 和 k-Nearest-Neighbors,旨在对它们进行改进。让我们先来看看,前者有哪些地方可以加强。

k-Means 和 KNN 哪里可以改进?

  1. 原始 k-Means 算法的一个常见问题是我们必须预先选择聚类数 k。虽然有时我们可以做出有根据的猜测,但大多数时候我们不得不求助于某种形式的试错法。这需要时间,最好将 k 的选择合并到聚类过程本身中。
  2. 另一个问题是,k-Means 不能聚类某些形状,如两个同心圆或两个互锁的半月形。一般来说,当聚类不是圆形或者当它们的质心彼此靠近时,k-Means 失败。
  3. k-Means 不是在线自适应的。假设您在两个聚类上训练了 k-Means,现在用第三个聚类的数据点来呈现它。这些点将被分配给训练期间发现的两个集群。这是不希望的,并且如果该算法将为所述点创建新的聚类,或者将它们标记为“未知”会好得多。
  4. 此外,kNN 还面临着复杂性的问题。为了给一个聚类分配一个数据点,我们必须计算它到所有训练点的距离。你可以想象,如果我们有大量的训练点,这将很快变得计算效率低下。因此,我们必须找到一种方法来减少距离计算的数量。

ROLF 神经元

区域和在线可学习领域的核心是所谓的 ROLF 神经元。这些充当我们的数据点的原型,并且有他们负责的区域。这个区域被称为感知领域。ROLF 神经元由 3 个参数组成:

  1. 中心 c ,定义神经元在空间中的位置。
  2. 宽度 σ ,定义了每个神经元的宽度。
  3. 感知领域 r = pσ ,定义其负责的区域。初始设置时,常数 p 对所有神经元都是相等的。它定义了每个神经元的感知范围和宽度之间的比率。

p = 2 (图片作者)

学习网络

这基本上分两步走,首先,我们必须在我们的训练数据中找到 ROLF 神经元或原型。之后,我们在它们之间构建一个邻域图,最终定义聚类。

如何找到罗尔夫神经元?

首先,我们反复检查所有的训练模式,看看它们是否在一个已经存在的神经元的感知范围内。如果在多个神经元的感知范围内,我们选择最接近模式的那个神经元。在我们决定了这个所谓的“获胜神经元”之后,我们调整它的中心 c 和宽度 σ

(图片由作者提供)

如果模式不在任何现有神经元的感知范围内,我们根据初始化策略创建一个新的。这个策略必须在一开始就选择。我们稍后将讨论这方面的细节。

获胜神经元如何适配?

x 为训练模式, c 为获胜神经元的中心。为了调整中心 c 和宽度 σ ,我们首先必须计算 cx 之间的距离 d 。之后,我们将 cx 移动,并将 σd 调整。最后,我们重新计算感知领域。

(图片由作者提供)

从公式中,我们可以看到常量参数 p 的使用。它允许神经元接受 *σ之外的模式。*如果 x 和 c 之间的距离大于 *σ,前者增加。在相反的情况下,它会减少。没有 *p,感知范围只能缩小,因为只有在 σ 内的点才会被接受。

如何初始化一个新的神经元?

当我们初始化一个新的神经元时,我们总是将 c 设置为模式 x 。我们改变的值是神经元的宽度 σ 。这可以通过四种不同的方式来设置。

  1. Init-σ →σ 初始化为恒定值
  2. 最小值-σ →将 σ 初始化为所有现有神经元的最小值 σ
  3. 最大值-σ →初始化 σ 为所有已经存在的神经元的最大值 σ
  4. 均值-σ →初始化 σ 为均值 σ 整体已经存在的神经元

现在,观察力敏锐的读者可能会想:如果我选择均值-σ作为策略,我该如何初始化第一个神经元?毕竟,没有σ值来计算平均值。嗯,这是正确的。第一个神经元的σ总是必须由预定义的值来设置。接下来的一切将由你选择的策略决定。

建设社区

在学习算法的第二步中,我们希望将我们之前发现的神经元分组为簇。这是通过构建一个邻域图来实现的,其中具有重叠感知域的神经元用一条边连接起来。从数学上来说,如果两个神经元的中心距离小于其感知范围(半径)之和,则这两个神经元是连通的。

(图片由作者提供)

现在,所有可以通过一系列边连接的神经元都属于同一个簇。它们的感知区域的联合是总区域,其中在预测期间新的数据点被分配给所述聚类。

ROLF 的优势

  1. 该算法可以自己选择聚类数 k
  2. 该算法不受聚类形状的限制。特别是 k-均值,连锁半月形和同心圆的问题案例可以用 ROLF 聚类。下一节将给出例子。
  3. 通过用 ROLF 神经元表示训练数据点,只需对前者进行距离计算。此外,必须专门存储神经元,而模式将仅呈现一次,然后可以被丢弃。这也减少了内存的使用。
  4. 该算法可以自己检测异常值,然后据此采取相应的行动。

表演

现在,我想向大家展示几个地区和在线可学习领域在实践中表现的例子。

20000 点,219 个神经元(图片由作者提供)

在给出的例子中,你可以看到 ROLF 是如何将 k-Means 的两个失败案例进行聚类的。对于这两个例子,对于 cσ 使用了 0.05 的学习率。用最小σ策略初始化新的神经元。第一个神经元初始化为σ = 0.4,而 p 设置为 2。黑线显示了邻域图形。两个数据集都由 20000 个点组成,并减少到约 1%的神经元。绿色和蓝色区域显示了点将被分配给相应的聚类的位置。

20000 点,239 个神经元(图片由作者提供)

评论

  1. ROLF 神经元的中心 c 的维数不一定是 2。在最初的论文中,ROLF 被用于从 720 维的输入空间中聚类点[1]。
  2. 用于生成示例的代码可以在这里找到。

来源:

[1] Schatten R .,Goerke N .,Eckmiller R. (2005)区域和在线可学习领域。在:Singh S .,Singh M .,Apte C .,Perner P. (eds)模式识别和图像分析。ICAPR 2005。计算机科学讲义,第 3687 卷。斯普林格,柏林,海德堡。https://doi.org/10.1007/11552499_9

RegNet:最灵活的计算机视觉网络体系结构

原文:https://towardsdatascience.com/regnet-the-most-flexible-network-architecture-for-computer-vision-2fd757f9c5cd?source=collection_archive---------5-----------------------

为高效率或高精度而缩放的模型设计

传统上,卷积神经网络架构是为一个特定目的而设计和优化的。例如,ResNet 模型系列在最初发布时就在 ImageNet 上进行了最高精度的优化。顾名思义,MobileNets 是为在移动设备上运行而优化的。最后,EfficientNet 被设计成在视觉识别任务中非常高效。

在他们的论文“设计网络设计空间”中,Radosavovic 等人决定设定一个非常不同寻常但非常有趣的目标:他们着手探索和设计一个高度灵活的网络架构。一个可以被调整为高效的或者在移动设备上运行的,但是当被调整为最佳分类性能时也是高度准确的。这种适应应该通过在量化的线性函数(一组具有特定参数的公式)中设置正确的参数来控制,以确定网络的宽度和深度

网络设计空间的可视化。空间不断优化,以达到一个更小的设计空间与最好的模型。来源:【1】

他们采用的方法也是非传统的:他们没有手工制作模型架构,而是建立了他们所谓的网络设计空间。

从网络设计空间中导出 RegNet 模型

如果您只是在这里看到 RegNet 模型的描述,请随意跳过这一部分。我强烈建议您阅读全文,因为如果不了解网络设计领域,就很难理解 RegNet。说到底,RegNet 其实不是一个架构,而是一个网络设计空间

一个网络设计空间不仅仅是,顾名思义,由不同的模型架构组成,而是由不同的参数组成,这些参数定义了一个可能的模型架构空间。这与神经架构搜索不同,你所做的只是尝试不同的架构,搜索最合适的架构。这些参数可以是宽度、深度、组等。网络的一部分。RegNet 也仅使用许多不同架构中的一种类型的网络块,例如瓶颈块。

为了达到最终的 RegNet 设计空间,作者首先定义了一个所有可能模型的空间,他们称之为 **AnyNet。**该空间通过不同参数的各种组合创建各种模型。所有这些模型都使用一致的训练机制(时期、优化器、权重衰减、学习率调度程序)在 ImageNet 数据集上进行训练和评估。

AnyNet 设计空间的统计数据,W4 代表第 4 阶段的网络宽度。来源:【1】

从这个 AnyNet 空间,他们通过分析什么参数负责 AnyNet 设计空间中最佳模型的良好性能,创建初始 AnyNet 设计空间的逐步简化版本。基本上,他们正在试验不同参数的重要性,以便将设计空间缩小到只有好的型号。

这些从当前设计空间到更窄设计空间的改进包括设置共享瓶颈比率和共享组宽度,将宽度和深度参数化以随后期阶段增加。

最后,他们到达优化的 RegNet 设计空间,其中仅包含好的模型以及定义模型所需的量化线性函数!

RegNet 设计空间

该网络由多个阶段组成,该多个阶段由多个块组成,形成茎(开始)、体(主要部分)和头(结束)。

RegNet 由茎、体和头组成。来源:【1】

在主体内部,定义了多个阶段,每个阶段由多个块组成。如前所述,在 RegNet 中只使用了一种类型的块,即具有群卷积的标准剩余瓶颈块。

用群卷积实现剩余瓶颈阻塞。在右侧,应用了步幅 2。来源:【1】

如上所述,RegNet 模型的设计不是由诸如深度和宽度的固定参数定义的,而是由所选参数控制的**量化线性函数。**优化后,块宽计算如下:

uj 是 j 阶段的块宽度,w0 是初始宽度,wa 是斜率参数。j 从 0 开始,到网络的深度结束。来源:【1】

值得注意的是,对于每个额外的块,每个块的宽度以 wa 的因子增加。
作者随后引入一个额外的参数 wm (这可以由你设置)并计算 sj :

引入 wm 计算 sj 的必要公式。来源:【1】

最后,为了量化 uj ,作者舍入 sj 并计算量化的每块宽度:

使用初始宽度和 wm 的 sj 次幂计算的每块宽度。来源:【1】

既然已经计算了每个块的宽度,让我们移动到阶段级别。为了得出每个阶段 i 的宽度,所有具有相同宽度的块被简单地计数以形成一个阶段,因为一次所有的块应该具有相同的宽度。

现在要在 RegNet 设计空间之外创建一个 RegNet,必须设置参数 d (深度) w0 (初始宽度) wa (斜率) wm (宽度参数) b (瓶颈)和 g (组)。

作者现在不同地设置这些参数,以获得具有不同属性的不同 RegNets:

  • 为移动使用而优化的 RegNet
  • 一个有效的 RegNet
  • 高度精确的 RegNet

让我们看看这些网络与其他架构相比表现如何。

结果

首先,让我们检查 RegNet 的移动性能。

与其他架构相比的 RegNets。X 或 Y 代表剩余块或挤压退出块,其余代表网络所需的触发器。来源:【1】

在所需的 FLOPS 数量相同的情况下,两种 RegNets 都优于其他移动优化网络,或者表现出相似的性能。但是它并没有就此停止。

如简介中所述,RegNet 被设计成高度灵活的。接下来的两次评估完美地展示了这一点。

首先,RegNets 高效性能与 EfficientNet 架构。

RegNet 与 EfficientNet。来源:【1】

令人印象深刻的是,在所有比较中,RegNet 都有优势。**要么是在更高的训练和推理速度下具有相似的准确性,要么是更准确和更快,特别是在低端。**此外,作者声称 RegNetX-F8000 大约比 EfficientNet-B55 × 。这是一个不可思议的飞跃!

当 RegNet 被配置为高精度时,结果看起来也不错。

RegNet 与 ResNet 和 ResNe(X)t 的比较来源:【1】

这再次显示了 RegNet 的灵活性:**模型可以指定为高效快速或者高度准确。**这在以前的单一架构中是可能的。

包装它

在本文中,您了解了 RegNet,这是一个高度灵活的模型设计空间,采用了非常不同的方法。RegNet 不是单一的架构,它是一个由量化的线性函数定义的设计空间。虽然我希望这个故事能让你对这篇论文有一个很好的初步了解,但是还有很多东西需要发现。因此,我会鼓励你自己阅读这篇论文,即使你是这个领域的新手。你必须从某个地方开始;)

如果你对论文中介绍的方法有更多的细节感兴趣,请随时在 Twitter 上给我留言,我的账户链接在我的媒体简介上。

我希望你喜欢这篇论文的解释。如果你对这篇文章有任何意见,或者如果你看到任何错误,请随时留下评论。

最后但同样重要的是,如果你想在高级计算机视觉领域更深入地探索,考虑成为我的追随者。我试着每周发一篇文章,让你和其他人了解计算机视觉研究的最新进展。

参考资料:

[1] Radosavovic,Ilija 等,“设计网络设计空间”IEEE/CVF 计算机视觉和模式识别会议论文集。2020.https://open access . the CVF . com/content _ CVPR _ 2020/papers/Radosavovic _ Design _ Network _ Design _ Spaces _ CVPR _ 2020 _ paper . pdf

初学者回归分析—第二部分

原文:https://towardsdatascience.com/regression-analysis-for-beginners-using-tree-based-methods-2b65bd193a7?source=collection_archive---------3-----------------------

建立鱼体重预测模型

使用基于树的算法(决策树、随机森林、XGboost)构建一个 ML 回归模型

在 Unsplash 上 veeterzy 拍摄的照片

简介
Part 2.1 构建机器学习管道
∘ 第一步:收集数据
∘ 第二步:可视化数据(自问自答)
∘ 第三步:清洗数据
∘ 第四步:训练模型
∘ 第五步:评估

∘ 什么是随机森林?
∘ 什么是极限梯度推进?(XGBoost)
∘ 决策树 vs 随机森林 vs XGBoost
∘ 线性模型 vs 基于树的模型。
结论

介绍

正如我在上一篇文章中解释的那样,真正的数据科学家从问题/应用的角度思考问题,并在编程语言或框架的帮助下找到解决问题的方法。在第一部分中,鱼重估计问题是使用线性 ML 模型解决的,但是,今天我将介绍基于树的算法,如决策树、随机森林、XGBoost 来解决相同的问题。在文章第 2.1 部分的前半部分,我将建立一个模型,在第 2.2 部分的后半部分,我将从理论上解释每种算法,将它们相互比较,并找出其优缺点。

YouTube 视频 决策树!

第 2.1 部分构建机器学习管道

为了建立一个 ML 模型,我们需要遵循下面几乎所有模型的流水线步骤。

作者图片

由于我们正在解决的问题与之前相同,所以一些流水线步骤将是相同的,例如 1。收集数据和 2。将数据可视化。但是,其他步骤会有一些修改。

第一步:收集数据

数据是公共数据集,可以从 Kaggle 下载。

**import** pandas **as** pd
**import** seaborn **as** sns
**import** matplotlib.pyplot **as** plt
**from** itertools **import** combinations
**import** numpy **as** np
data **=** pd**.**read_csv("Fish.csv")

第二步:可视化数据(问自己这些问题并回答)

  • 数据看起来怎么样?
data**.**head()

  • 数据是否有缺失值?
data**.**isna()**.**sum()

  • 数字特征的分布是什么?
data_num **=** data**.**drop(columns**=**["Species"])

fig, axes **=** plt**.**subplots(len(data_num**.**columns)**//**3, 3, figsize**=**(15, 6))
i **=** 0
**for** triaxis **in** axes:
    **for** axis **in** triaxis:
        data_num**.**hist(column **=** data_num**.**columns[i], ax**=**axis)
        i **=** i**+**1

  • 目标变量(重量)相对于鱼种的分布情况如何?
sns**.**displot(
  data**=**data,
  x**=**"Weight",
  hue**=**"Species",
  kind**=**"hist",
  height**=**6,
  aspect**=**1.4,
  bins**=**15
)
plt**.**show()

关于物种的目标变量分布表明,有些物种,如狗鱼,与其他物种相比具有巨大的重量。这种可视化为我们提供了关于“**物种”**特征如何用于预测的附加信息。

第三步:清理数据

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import  LabelEncoder
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import RandomForestRegressor
import xgboost as xgb
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
data_cleaned =   data.drop("Weight", axis=1)
y = data['Weight']x_train, x_test, y_train, y_test = train_test_split(data_cleaned,y, test_size=0.2, random_state=42)
print(x_train.shape, x_test.shape, y_train.shape, y_test.shape)# label encoder
label_encoder = LabelEncoder()
x_train['Species'] = label_encoder.fit_transform(x_train['Species'].values)
x_test['Species'] = label_encoder.transform(x_test['Species'].values)

我们使用基于树的模型,因此我们不需要特征缩放。此外,为了将文本转换成数字,我刚刚使用 LabelEncoder 为每种鱼分配了唯一的数值。

第四步:训练模型

**def** evauation_model(pred, y_val):
  score_MSE **=** round(mean_squared_error(pred, y_val),2)
  score_MAE **=** round(mean_absolute_error(pred, y_val),2)
  score_r2score **=** round(r2_score(pred, y_val),2)
  **return** score_MSE, score_MAE, score_r2score**def** models_score(model_name, train_data, y_train, val_data,y_val):
    model_list **=** ["Decision_Tree","Random_Forest","XGboost_Regressor"]
    *#model_1*
    **if** model_name**==**"Decision_Tree":
        reg **=** DecisionTreeRegressor(random_state**=**42)
    *#model_2*
    **elif** model_name**==**"Random_Forest":
      reg **=** RandomForestRegressor(random_state**=**42)

    *#model_3*
    **elif** model_name**==**"XGboost_Regressor":
        reg **=** xgb**.**XGBRegressor(objective**=**"reg:squarederror",random_state**=**42,)
    **else**:
        print("please enter correct regressor name")

    **if** model_name **in** model_list:
        reg**.**fit(train_data,y_train)
        pred **=** reg**.**predict(val_data)

        score_MSE, score_MAE, score_r2score **=** evauation_model(pred,y_val)
        **return** round(score_MSE,2), round(score_MAE,2), round(score_r2score,2)model_list **=** ["Decision_Tree","Random_Forest","XGboost_Regressor"]
result_scores **=** []
**for** model **in** model_list:
    score **=** models_score(model, x_train, y_train, x_test, y_test)
    result_scores**.**append((model, score[0], score[1],score[2]))
    print(model,score)

我训练了决策树,随机森林 XGboost 并存储了所有的评估分数。

第五步:评估

df_result_scores **=** pd**.**DataFrame(result_scores,columns ["model","mse","mae","r2score"])
df_result_scores

基于树的模型

结果真的很吸引人,因为你记得线性模型取得了低得多的结果(也如下所示)。因此,在我们进行任何类型的超参数调整之前,我们可以说,在这种数据集中,所有基于树的模型都优于线性模型。

线性模型

第六步:使用 hyperopt 调节超参数

今天,我们使用超视来调整使用TPE 算法的超参数。TPE 算法不是从搜索空间中取随机值,而是考虑到已知某些超参数赋值(x)与其他元素的特定值无关。在这种情况下,搜索比随机搜索更有效,比贪婪搜索更快。

**from** hyperopt **import** hp
**from** hyperopt **import** fmin, tpe, STATUS_OK, STATUS_FAIL, Trials
**from** sklearn.model_selection **import** cross_val_scorenum_estimator **=** [100,150,200,250]

space**=** {'max_depth': hp**.**quniform("max_depth", 3, 18, 1),
        'gamma': hp**.**uniform ('gamma', 1,9),
        'reg_alpha' : hp**.**quniform('reg_alpha', 30,180,1),
        'reg_lambda' : hp**.**uniform('reg_lambda', 0,1),
        'colsample_bytree' : hp**.**uniform('colsample_bytree', 0.5,1),
        'min_child_weight' : hp**.**quniform('min_child_weight', 0, 10, 1),
        'n_estimators': hp**.**choice("n_estimators", num_estimator),
    }

**def** hyperparameter_tuning(space):
    model**=**xgb**.**XGBRegressor(n_estimators **=** space['n_estimators'], max_depth **=** int(space['max_depth']), gamma **=** space['gamma'],
                         reg_alpha **=** int(space['reg_alpha']) , min_child_weight**=**space['min_child_weight'],
                         colsample_bytree**=**space['colsample_bytree'], objective**=**"reg:squarederror")

    score_cv **=** cross_val_score(model, x_train, y_train, cv**=**5, scoring**=**"neg_mean_absolute_error")**.**mean()
    **return** {'loss':**-**score_cv, 'status': STATUS_OK, 'model': model}

trials **=** Trials()
best **=** fmin(fn**=**hyperparameter_tuning,
            space**=**space,
            algo**=**tpe**.**suggest,
            max_evals**=**200,
            trials**=**trials)

print(best)

最佳超参数的结果

这是算法在 200 次试验后找到的最佳超参数的结果。但是,如果数据集太大,可以相应减少试验次数。

best['max_depth'] **=** int(best['max_depth']) *# convert to int*
best["n_estimators"] **=** num_estimator[best["n_estimators"]] #assing value based on indexreg **=** xgb**.**XGBRegressor(******best)
reg**.**fit(x_train,y_train)
pred **=** reg**.**predict(x_test)
score_MSE, score_MAE, score_r2score **=** evauation_model(pred,y_test) 
to_append **=** ["XGboost_hyper_tuned",score_MSE, score_MAE, score_r2score]
df_result_scores**.**loc[len(df_result_scores)] **=** to_append
df_result_scores

结果太棒了!与其他算法相比,超调模型非常好。例如,XGboost 将 MAE 的结果从 41.65 提高到 36.33。这是一个很好的说明,超参数调整是多么强大。

第七步:选择最佳模型和预测

*# winner*
reg **=** xgb**.**XGBRegressor(******best)
reg**.**fit(x_train,y_train)
pred **=** reg**.**predict(x_test)
plt**.**figure(figsize**=**(18,7))
plt**.**subplot(1, 2, 1) *# row 1, col 2 index 1*
plt**.**scatter(range(0,len(x_test)), pred,color**=**"green",label**=**"predicted")
plt**.**scatter(range(0,len(x_test)), y_test,color**=**"red",label**=**"True value")
plt**.**legend()

plt**.**subplot(1, 2, 2) *# index 2*
plt**.**plot(range(0,len(x_test)), pred,color**=**"green",label**=**"predicted")
plt**.**plot(range(0,len(x_test)), y_test,color**=**"red",label**=**"True value")
plt**.**legend()
plt**.**show()

预测与真实

可视化清楚地展示了预测值和真实值的接近程度,以及调优后的 XGBoost 的性能。

第 2.2 部分分析最大似然算法

什么是决策树?

决策树是一种受监督的 ML 算法,擅长捕捉特征和目标变量之间的非线性关系。算法背后的直觉类似于人类的逻辑。在每个节点中,该算法找到将数据分成两部分的特征和阈值。下图是一个决策树。

鱼体重预测决策树

首先,让我们看看图中每个变量代表什么。我们以第一个节点为例。

宽度≤5.154 :算法决定分割数据样本的特征和值阈值。

样本= 127 :拆分前有 127 个数据点。

value = 386.794 :预测特征(鱼重)的平均值。

Squared _ error = 122928.22:同 MSE(真,pred)——其中 pred 同(样本的平均鱼重)。

所以基于宽度≤5.154 阈值的算法将数据分成两部分。但问题是算法是怎么找到这个阈值的?有几个分割标准,因为回归任务 CART 算法试图通过以贪婪的方式搜索来找到阈值,使得两个子组的 MSE 的加权平均值最小化。

例如,在我们的案例中,在第一次分割后,两个小组的加权平均 MSE 与其他分割相比是最小的。

J(k,t_k) = 88/127 *20583.394 + 39/127 *75630.727 = 37487.69

决策树的问题:

树对训练数据中的微小变化非常敏感。数据的微小变化可能会导致决策树结构的重大变化。这个限制的解决方案是随机森林。

什么是随机森林?

随机森林是决策树的集合。随机森林背后的直觉是构建多个决策树,并且在每个决策树中,不是搜索最佳特征来分割数据,而是在特征的子集中搜索最佳特征,因此这提高了树的多样性。然而,它比简单的决策树更难理解。此外,它需要建立大量的树,这使得算法对于实时应用来说很慢。一般来说,算法训练起来很快,但创建预测却很慢。决策树的一个改进版本也是 XGBoost。

什么是极限梯度提升?(XGBoost)

XGBoost 也是一种基于树的集成监督学习算法,它使用了梯度推进框架。这种算法背后的直觉是,它试图用新的预测器来拟合前一个预测器产生的残余误差。它非常快,可伸缩,可移植。

决策树 vs 随机森林 vs XGBoost

因此,在我们的实验中,XGboost 在性能方面优于其他产品。从理论上讲,决策树是最简单的基于树的算法,它有不稳定的局限性——数据的变化会引起树结构的巨大变化,但是它有很好的可解释性。随机森林和 XGboost 更复杂。区别之一是随机森林在过程结束时组合结果(多数规则),而 XGboost 在过程中组合结果。一般来说,XGBoost 比随机森林有更好的性能,但是,当我们的数据中有很多噪声时,XGboost 不是一个好的选择,它会导致过拟合,并且比随机森林更难调整。

线性模型与基于树的模型。

  • 线性模型捕捉自变量和因变量之间的线性关系,这在现实世界的大多数情况下并非如此。然而,基于树的模型捕捉更复杂的关系。
  • 线性模型大多数时候需要特征缩放,而基于树的模型不需要。
  • 基于树的模型的性能是线性模型的大多数倍。我们的实验很好地说明了这一点,最佳超调优线性模型达到了 66.20 MAE,最佳基于树的模型达到了 36.33,这是一个很大的改进。
  • 基于树的算法比线性模型更容易解释。

结论

如前所述,哪种算法最有效并没有现成的答案,一切都取决于数据和任务。这就是为什么要测试和评估几种算法的原因。然而,了解每种算法背后的直觉、它们的优缺点以及如何应对其局限性是有益的。

这里是我的 GitHub 中的完整代码。

你可以在媒体上关注我,了解最新文章。

https://medium.com/@gkeretchashvili

参考文献

[1] Stephanie Glen 决策树 vs 随机森林 vs 梯度推进机器:简单解释 (2018)

[2] Vishal Morde XGBoost 算法:愿她统治多久! (2019)

[3] GAURAV SHARMA,你应该知道的 5 种回归算法——入门指南!

[4] Aarshay Jain,使用 Python 代码在 XGBoost 中调整参数的完整指南

[5]scikit-learn.org,决策树,了解决策树结构

[6]hyperpt:分布式异步超参数优化

[7] XGboost, XGBoost 参数

[8]TINU·罗希斯·D,(2019)超参数调整—超点贝叶斯优化(Xgboost 和神经网络)

[9]工作管理,你需要知道的替代超参数优化技术—hyperpt(2020)

[10] Aurelien Geron,使用 Scikit-learn 和 Tensorflow 进行动手机器学习(2019 年)

分类回归|实践经验

原文:https://towardsdatascience.com/regression-for-classification-hands-on-experience-8754a909a298?source=collection_archive---------10-----------------------

具有核心概念的逻辑回归和 Softmax 回归

兰斯·安德森在 Unsplash 上拍照

在我们的生活中,我们都开发了许多回归模型。但是只有少数人熟悉使用回归模型进行分类。所以我的意图是揭示这个隐藏世界的美丽。

众所周知,当我们想要从多个自变量中预测一个连续的因变量时,我们使用线性/多项式回归。但是到了分类的时候,就不能再用那个了。

基本上,分类是预测一个标签,回归是预测一个数量。

为什么线性回归不能用于分类?主要原因是预测值是连续的,而不是概率性的。所以我们无法得到一个确切的类来完成分类。浏览下面的预测,你会进一步理解。

作者图片

概率介于 0 和 1 之间。但是在线性回归中,我们预测的是一个绝对数字,其范围可以在 0 和 1 之外。

是的,您仍然可以将该值标准化到 0–1 的范围内,但是结果可能会更糟。这是因为线性回归拟合受包含异常值的影响很大。即使一个小的异常值也会破坏你的分类。

另一方面,使用线性回归进行多类预测是没有意义的。线性回归假设顺序在 0、1 和 2 之间,而在分类体系中,这些数字仅仅是分类占位符。

为了克服上述问题,有两个很好的解决方案。

  1. 逻辑回归—用于二元分类
  2. Softmax 回归-用于多类分类

我用红酒质量数据集(在 Kaggle 中)向你展示这一点。

https://www.kaggle.com/uciml/red-wine-quality-cortez-et-al-2009

原始数据集在 UCI 机器学习知识库中公开。

注意:我今天不会执行任何详细的预处理或降维技术,因为我的意图是主要在分类模型上向您介绍。

好吧!首先,我们将看到如何使用逻辑回归实现二元分类。

逻辑回归

在应用模型之前,让我们先了解一下逻辑回归中的一些核心概念。

我将通过一个例子向您展示逻辑回归是如何工作的。考虑投掷硬币的两种结果概率。

作者图片

让我们把显示头部的概率作为 p,显示尾部的概率作为 q,然后我们定义一个新概念叫做赔率作为 p/q.

在这种情况下,我们只有两种可能。那不是头就是尾。所以我们可以把 q 写成 1-p,现在我们将看到 p 的变化,它可以从 0 变化到 1。我们取 3 个公平点,用赔率来分析。

作者图片

现在我们可以得到几率的对数。那么它将被称为 Log OddsLogit【Log(p/q)】。这些对数优势仅适用于 2 个类别的值条件。

作者图片

如你所见,它呈现出对称分布。当 p=0.5 时,Logit 函数不偏向任何人。当 p=1 时,受到+inf 的青睐,同时 q (p-1)受到-inf 的青睐。这个特征是逻辑回归的直觉,因为它可以用它的性质对二元问题进行分类。

作者图片

然后我们可以从这个 Logit 函数推导出逻辑函数。

作者图片

如果我们绘制图表,它将被视为如下。

作者图片

下图简要显示了逻辑回归的工作原理。

作者图片

您可以更深入地挖掘片段,如分析损失函数(二元交叉熵)和制定决策边界。这超出了本文的范围,因为需要更多的时间来解释。让我们看看实现,没有任何进一步的原因。

首先,让我们将 CSV 文件加载到熊猫数据框架中。

wineData = pd.read_csv('winequality-red.csv')
wineData.head()

作者图片

这里我们将质量作为目标列。

wineData.quality.unique()

我们可以把葡萄酒的质量分为 3 到 8 级

作者图片

这其实是一个多类分类问题。(有 6 节课)我将在 softmax 回归上解释这个场景。为了解释逻辑回归,我添加了另一个具有以下条件的列。

  • 如果葡萄酒质量大于或等于 6 = >“好”(编码为 1)
  • 否则= >“坏的”(编码为 0)

添加类别列后,我删除了质量列。所以现在我们的目标将是类别列。

wineData['category'] = np.where(wineData['quality'] >= 6, 1, 0)
wineData = wineData.drop(['quality'], axis = 1)
wineData.head()

作者图片

让我们来看看数据分布,以观察任何阶级的不平衡。如果您为类别列绘制计数图,您将看到如下图。

作者图片

我们可以看到阶级不平衡。你可以丢弃一些数据点,或者使用重采样 技术如欠采样(例如:接近缺失)或过采样(例如:SMOTE)来克服这个问题。至于简单性,我放弃了数据点。之后,您可以观察到一个平衡的数据集,如下所示。

作者图片

您可以进一步分析特征之间的任何相关性。这是所有列的相关矩阵。

作者图片

让我们对此做一些小的预处理。首先,我将分离数据集进行训练和测试,因为我们需要查看准确性度量。

target = wineData['category'].copy()
features = wineData.drop('category', 1)X_train, X_test, y_train, y_test = train_test_split(features, target, test_size = 0.2, random_state = 101)

数据集没有正确标准化。在应用模型之前,让我们对数据应用标准的缩放函数。

scaler = StandardScaler()# we only fit the training data to scaler
scaler.fit(X_train)train_scaled = scaler.transform(X_train)
test_scaled = scaler.transform(X_test)X_train = pd.DataFrame(train_scaled, columns = X_train.columns)
X_test = pd.DataFrame(test_scaled, columns = X_test.columns)

标准化后,训练集如下。

作者图片

现在我们可以应用逻辑回归模型。Scikit-learn 库为逻辑回归提供了一个简单的实现。

from sklearn.linear_model import LogisticRegression

logReg = LogisticRegression()
logReg.fit(X_train,y_train)

太容易了吧!我们可以通过调用model.predict函数来获得测试集的模型预测。

predictions = logReg.predict(X_test)
y_hat = pd.DataFrame(predictions, columns=["predicted"])

作者图片

嘣!没有任何连续值。如你所见,我们的预测以类的形式出现。进一步的精度测量可以计算如下。

from sklearn.metrics import classification_report
print(classification_report(y_test,y_hat))

作者图片

这里我们只做了标准化就达到了 76%的准确率。此外,您可以使用异常值处理、转换和离散化技术来改进这个模型。

让我们也画出混淆矩阵。

from sklearn.metrics import confusion_matrixcf_matrix=confusion_matrix(y_test,y_hat)
ax= plt.subplot()
sns.heatmap(cf_matrix/np.sum(cf_matrix), annot=True,fmt='.2%', cmap='Blues', ax=ax);  
ax.set_xlabel('Predicted labels');
ax.set_ylabel('True labels'); 
ax.set_title('Confusion Matrix');

作者图片

从混淆矩阵的结果中,我们可以看到我们的模型表现得相当好。

太好了!我们成功地实现了逻辑回归模型。现在让我们看看 softmax 回归是如何工作的。

Softmax 回归

正如我之前提到的,Softmax 回归用于多类分类。希望大家还记得逻辑回归的最后一张图。Softmax 的工作方式与此类似,但这里我们有多个类。请参见下图。

作者图片

对于一个给定的数据点,我们逐个计算那个数据点属于每一类的概率,然后就可以在其中找到最大的概率。它将是该数据点的正确类别。

让我们看看计算是如何进行的。

作者图片

这被称为 Softmax 函数。

作者图片

为了估计 w 权重,我们需要最小化损失函数。它被称为范畴交叉熵。

最终,我们可以找到 max(softmax)来预测正确的类。

好吧!让我们亲身体验一下。之前我已经将葡萄酒质量分为好的和坏的。这里,我将原始列离散化为

  • 如果葡萄酒质量大于或等于 7 = >“好”(编码为 2)
  • 如果葡萄酒质量等于 6 = >“好”(编码为 1)
  • 否则= >“坏的”(编码为 0)

所以这将把我们引向一个多类分类问题。(有 3 个类)让我们检查数据分布。

作者图片

这里我们也可以看到阶级的不平衡。让我们去掉一些数据点来消除不平衡。完整的实现可以在我的 colab 笔记本上看到。删除后,数据将如下所示。

作者图片

注意:如果要应用重采样技术,应该仅将其应用于训练数据。(分成训练和测试后)

好吧!让我们做一个训练测试分割,并应用一些预处理技术。拆分和标准化与之前相同。标准化之后,你的训练集如下。

作者图片

现在让我们应用 Softmax 回归。

我们将再次使用 scikit-learn 中的逻辑回归,但是我们需要将 multi_class 参数设置为multinomial,以便该函数执行 softmax 回归。我们还需要一个支持 softmax 回归的解算器,比如 solver='lbfgs '。

这些求解器用于查找最小化成本函数的参数权重。

softReg = LogisticRegression(multi_class = 'multinomial', solver = 'lbfgs')
softReg.fit(X_train,y_train)

测试数据的模型预测如下。

predictions = softReg.predict(X_test)
y_hat = pd.DataFrame(predictions, columns=["predicted"])
print(y_hat.head())

作者图片

很简单,对吗?因此,我们的 softmax 回归模型能够对多类问题进行分类。让我们看看评价。

作者图片

从分类报告中,我们可以看到 70%的准确率。这实际上并不坏,因为我们没有去深度预处理阶段。正如我前面说过的,可以通过适当的预处理技术和重采样技术来进一步增强这一点。

作者图片

如混淆矩阵所示,我们的模型很好地分类了 0 和 2 类,但在识别 1 类时有一些问题。你可以试试上面建议的方法,然后告诉我结果。我的笔记本会支持你的实现。

太好了!现在你知道如何使用回归进行分类了。

资源

  • 完整的合作实验室 Python 笔记本。

https://colab.research.google.com/drive/1QyVpFToKva_NceVKUzQ1cbYmKEbTSFsr?usp=sharing

  • 红酒质量数据集。

这个数据集在数据库内容许可(DbCL)1.0 版下,在 UCI 机器学习库和 Kaggle 中公开提供。

https://www.kaggle.com/uciml/red-wine-quality-cortez-et-al-2009

[1] Paulo Cortez,葡萄牙吉马雷斯米尼奥大学,http://www3.dsi.uminho.pt/pcorteza . Cerdeira,F. Almeida,T. Matos 和 J. Reis,葡萄牙波尔图 Vinho Verde 地区葡萄栽培委员会,2009 年

结论

今天我们学习了如何使用回归技术来解决分类问题。具体来说,我们学习了逻辑回归和 softmax 回归中的主要直觉和核心概念。此外,我们已经从头开始实现了这两个模型。

感谢您通读这篇文章。希望对你有帮助。欢迎留言,提出您的宝贵意见和建议。关注我获取介质最新文章。注意安全!快乐学习!❤️

回归归回归:数据分布和模型不可知漂移恶性肿瘤检测

原文:https://towardsdatascience.com/regression-for-regressions-data-distribution-and-model-agnostic-drift-malignancy-detection-4d09d98e4135?source=collection_archive---------30-----------------------

为人工智能提供透明度和可解释性不仅是最负责任的事情,而且还可以帮助我们找到改进数据驱动产品的方法,并为用户提供价值。

由卢克·切瑟在 Unsplash 上拍摄的照片

随着大规模机器学习系统部署在世界各地的脆弱空间,没有制衡的黑箱模型变得越来越危险。

例如,在一个医疗保健系统中,无法解释的人工智能模型正在为慢性病的分类或某些风险因素的回归提供动力。每个预测都会对人类生活产生实际影响,我们在“训练与生产”准确性方面的典型差异现在变得更加重要。

更讽刺的是,由于生产数据分布变化而导致的这些错误中的一些可能要到数年后才会出现(如慢性病预测的情况),这使得“防止数据漂移损害”与一旦发生的“检测数据漂移”同等重要。为此,我们希望预测漂移损害并创建专门针对当前模型弱点的备份模型。但在此之前,我们需要一种方法来首先解释和理解我们的模型,从而了解我们的模型的弱点。

在这篇博客中,我们将探索一种替代方法来保护模型免受不可信概念和特征漂移的影响:使用回归模型的回归来预测潜在数据漂移的恶性程度

为模型创建通用模型的想法受到了元学习中最近发表的文章的启发,但我们没有使用传统的梯度方法,而是对以后生成我们自己的特征感兴趣。

1 问题陈述

在这篇博客中,我们将模型性能称为恶性,因为并非所有的数据分布变化都会导致显著的性能下降,并且并非每个模型的所有评估指标都是相同的(我们将在下一节中看到)。

图 1 :作者的问题陈述

为了简化我们的问题陈述,我们在上图 1 中划分了我们可能面临的问题。我们正在创建一个预测器模型,它从任意基础模型的数据分布汇总和模型汇总中获取特征。

在纵轴中,我们区分预测模型,因为回归模型可用于直接预测恶性程度,而分类模型用于预测最易受攻击的特征(基于分位数)。目标显然是不同的,因为在一种情况下,我们希望确切地知道数据漂移会造成多大的损害,而我们只是在寻找易受攻击的特征。

在水平轴上,我们将试图评估漂移的模型类型分开。我们把回归问题和分类问题分开的原因很简单。用于评估我们的基本模型退化的度量(即损失函数)对于两个任务来说是完全不同的,因此对于两个任务来说“恶性”不能等同。

2 数据生成

图 2: 作者生成的数据

总的来说,我们感兴趣的是创建一个概化模型,该模型可以预测单个要素中漂移的损害。通过这种方式,我们可以估计对抗性攻击、意外分布或者概念转变对模型度量的影响。

要做到这一点,我们首先需要创建一个模型不可知的回归器(或分类器),该回归器(或分类器)只针对许多数据集的特征和模型摘要的元数据进行训练。我们不会使用实际的数据集本身,因为它们不仅维数太高,而且不同模型的数据形状也不同。在以前的文献中,作者经常使用降维技术来做到这一点。我们对解释特性概要感兴趣,所以我们选择不这样做。

然后,我们将需要针对一个单独的、看不见的数据集(和模型)测试该模型,以查看它们是否可以预测任意模型在这个新数据集上的性能。

虽然预测器模型不能访问每个模型的训练数据点(或数据点的降维版本),但我们将允许特征汇总和特征的可解释性度量,例如 SHAP 值的汇总,我们将在后面解释。

换句话说,我们将基于特征生成训练样本,并且预测器模型的每个训练数据条目将仅从单个特征的特征总结和可解释性中生成。我们不知道数据的任何其他信息,也不知道将会发生的潜在变化:只知道模型经历的性能损失。更多细节见图 2 。

3 漂移产生

我们通过逐渐增加添加到每个特征的高斯噪声来产生漂移。虽然渐进性不是必需的,但这确保了噪声是随机的,但仍然反映了现实世界中的变化。

我们有几个关键的常数来维护数据的完整性。首先,我们一次只扰动一个特征,因此我们可以隔离该特征漂移的恶性得分。第二,我们没有在初始数据分割后重新调整模型,而是简单地使用它来预测漂移数据上的恶性肿瘤。第三,所有加性噪声相对于每个特征的标准偏差被归一化。

完成上述漂移生成步骤后,我们继续进行数据生成中提到的特征生成部分。

4 个预测器可解释性特征

在我们生成漂移之后,我们需要为模型和每个特性提供特性概要。简单特征包括数据分布数据和模型摘要数据(即树模型、神经模型、线性模型等)。

我们包括的更复杂的特性是模型可解释性度量(或者特性重要性度量)的总结。我们选择使用沙普利附加解释(SHAP) ,一种解释特征重要性的博弈论方法。尽管有其他方法来生成可解释性,我们选择了 SHAP,因为生成方法不依赖于模型,因此它可以为所有类型的模型生成。查看在 MLP 回归基础模型上从加州住房数据集生成的 SHAP 要素的示例。

图 3:加州住房数据集的 SHAP 值

直观地说,我们可以把特征重要性度量看作每当模型发生变化时,偏向回归(或分类)一侧的“权重”。因此,由于 SHAP 值的生成可以是模型不可知的,所以我们可以为任何模型和任何数据集生成这样的特征。

**最后,跨每个数据集的特征标准化至关重要,因为每个数据集都是完全不同的。**虽然归一化可以最大限度地减少要素之间的差异,但它不能用于某些模型要素,例如线性模型的系数值。对于这样的模型概要特征,我们必须对它们中的一些进行不同的转换和归一化,例如通过关于特征分布分位数的归一化。

5 次实验及结果

在我们的实验中,我们使用了 Google Colab 或 sklearn 内置的几个数据集。我们选择的测试集是使用一个不可见的数据集作为模型来预测漂移的恶性程度。

我们首先看回归预测器(即预测由于漂移导致的模型退化的实际数量)。

对于恶性肿瘤的回归基础模型,我们使用了糖尿病数据集、波士顿房价数据集和加州房价数据集。对于恶性肿瘤的分类基础模型,我们使用了虹膜数据集、葡萄酒质量数据集和乳腺癌数据集。我们使用多个回归模型的最佳结果。

图 4 :作者预测实验结果(回归)

然后,我们将相同的特征集用于分类预测器(即,通过分位数预测最脆弱特征的前 k%)。

图 5 :分类预测器精度示例(加州住房)

我们可以看到,精度根据我们想要预测的特征的分位数而变化。总的来说,我们发现分类预测器(不考虑基础模型)具有更好的相对成功,并且对于每个数据集,准确度的范围在 55%到 90%之间。这在直觉上是有意义的,因为通过简单地观察特征的重要性(SHAP 总结),我们通常可以知道我们的模型高度依赖于哪些特征。

6 可解释人工智能:使用可解释性度量来评估可解释性特征

撇开模型不谈,我们决定用 SHAP 来解释我们的预测模型,它使用 SHAP 作为它的许多特征。事实证明,SHAP 值是模型退化的重要预测指标。详情见图 6。

图 6 :回归预测模型对回归模型的特征重要性

**尽管具有讽刺意味的是递归,就像标题“为回归而回归”**一样,这种洞察力给了我们很多理由去关心我们的模型可解释性。这就把我们带了回来,突出了这篇博客的潜台词:

不仅提供人工智能透明度和可解释性是最负责任的事情,而且它还可以帮助我们找到改进数据驱动产品的方法,并为客户提供价值。

7 经验教训和未来工作

虽然我们每次都可以简单地手动漂移新数据的特征,并以这种方式计算漂移的恶性程度,但这种漂移几乎总是会有类似于我们选择数据集和模型时可能无意中引入的人为偏差。

我们希望在结束时指出,我们的博客旨在展示,在不了解漂移的底层结构的情况下,估计漂移的损害是可能的(我们只编码了与训练集相关的可解释性、特性摘要信息和模型摘要信息,但没有编码漂移类型本身)。

在非学术环境中,我们可以访问包含真实世界、可检测和非随机数据漂移的大型数据湖,我们可以使用相同的方法来估计类似漂移可能对我们的模型造成的潜在损害,并采取预防措施,例如在检测到漂移时拟合第二个模型或增强模型。在未来,我们希望探索能够更好地总结模型和功能的其他功能。

面对杂乱的离群值回归?试试 Huber 回归器

原文:https://towardsdatascience.com/regression-in-the-face-of-messy-outliers-try-huber-regressor-3a54ddc12516?source=collection_archive---------10-----------------------

数据中的异常值无处不在,它们会搞乱你的回归问题。尝试哈伯回归器来解决这个问题。

图片来源:作者创作

有什么问题?

假设您有一个包含两个要素 X 1 和 X 2 的数据集,您正在对其执行线性回归。然而,数据集中引入了一些噪声/异常值。

比方说,y-值异常值与它们应有的值相比特别低。那看起来像什么?

这是你得到的数据。

图片来源:作者创作

然而,如果你真的考虑一下 X - Y 数据的斜率,那些 X 值的预期 Y 值应该要高得多。类似于下面的内容,

图片来源:作者创作

这些是明显的异常值,您可以运行一个简单的探索性数据分析(EDA ),在构建回归模型之前从数据集中捕捉并丢弃它们。

但是你不能指望捕捉到所有尺度和所有维度上的异常值*。具有 100 或 1000 个维度(特征)的数据集的可视化具有足够的挑战性,以手动检查图并发现异常值。*

对异常值稳健的回归算法听起来像是对那些讨厌的坏数据点的一个好赌注。胡伯回归器就是我们将在本文中讨论的一个工具。

胡伯回归

在统计学中,Huber 损失是一种特殊的损失函数(由瑞士数学家 Peter Jost Huber 于 1964 年首次引入),广泛用于稳健回归问题——存在异常值的情况会降低基于误差的最小平方损失的性能和准确性。

*

技术细节

损失由下式给出:

图片来源:维基百科

我们可以看到,只有当残差的绝对值小于某个固定参数时,损失才是通常残差的平方( yf ( x ))。这个参数的选择和调整对于获得一个好的估计器是很重要的。当残差大于该参数时,损耗是残差绝对值和 Huber 参数的函数。

现在,您可能还记得基础统计学中的平方损失来自均值附近的无偏估计量,而绝对差损失来自中位数附近的无偏估计量。中位数比平均数对异常值更稳健。

Huber 损失是这两种类型的平衡折衷。它对异常值是稳健的,但也不会完全忽略它们。当然,可以使用自由参数进行调整。

图片来源:作者创作

Python 演示

演示笔记本在我的 Github repo 中。

我们创建了合成数据,并用以下代码添加了一些噪声异常值,

import numpy as np
from sklearn.datasets import make_regressionrng = np.random.RandomState(0)
X, y, coef = make_regression(
    n_samples=200, n_features=2, noise=4.0, coef=True, random_state=0)**# The first four data points are outlier**
X[:4] = rng.uniform(10, 20, (4, 2))
y[:4] = rng.uniform(10, 20, 4)

现在,你所要做的就是调用 Scikit-learn 的内置HuberRegressor估算器并拟合数据。为了比较,我们也调用了标准的LinearRegression方法。

from sklearn.linear_model import HuberRegressor, LinearRegressionhuber = HuberRegressor().fit(X, y)
linear = LinearRegression().fit(X, y)

现在,我们知道前 4 个数据点是异常值。所以,如果我们试图用第一个数据点预测 y 值,我们会得到这样的结果,

linear.predict(X[:1,])>> array([87.38004436])

正如所料,线性回归预测是 y 的低值。为什么?因为线性拟合(基于最小平方损失)因其巨大的杠杆作用而偏向异常值。

图片来源:作者创作

然而,Huber 估计量预测了更合理(高)的值,

huber.predict(X[:1,])>> array([806.72000092])

为了进一步证明 Huber 估计量的稳健性,我们可以使用估计的系数并绘制最佳拟合线,

huber_y1 = np.arange(-2.5,20,0.01)*huber.coef_[0] + \
            np.arange(-2.5,20,0.01)*huber.coef_[1] + \
            huber.intercept_plt.figure(dpi=120)
plt.scatter(X[:,0],y)
plt.plot(np.arange(-2.5,20,0.01), 
         huber_y1,
         color='red',linestyle='--')
plt.show()

图片来源:作者创作

泰尔-森估计量

虽然我们不在本文中讨论它,但鼓励读者检查 Theil-Sen 估计器,这是另一种稳健的线性回归技术,并且对异常值高度不敏感。

正如所料,Scikit-learn 也为这个估计器提供了一个内置方法: Scikit-learn Theil-Sen 估计器

对于具有大量特征的多元线性回归,这是所有稳健估计量中非常有效和快速的回归算法。

摘要

在这篇简短的文章中,我们讨论了数据集中存在异常值时的线性回归估计量问题。我们用一个简单的例子证明,基于传统最小二乘损失函数的线性估计可能会预测出完全错误的值,因为它们倾向于离群值。

我们讨论了几个稳健估计量,并详细演示了休伯回归。非参数统计在许多地方使用这些稳健的回归技术,尤其是当数据预计会特别嘈杂时。

数据科学专业的学生和专业人士也应该具备这些强大的回归方法的工作知识,以便在存在异常值的情况下自动对大型数据集进行建模。*

喜欢这篇文章吗?成为 中等会员 继续 无限制学习 。如果您使用下面的链接, ,我将收取您的一部分会员费,而不会对您产生额外费用

*https://medium.com/@tirthajyoti/membership *

使用 TensorFlow 简化回归建模—在 10 分钟内训练您的第一个模型

原文:https://towardsdatascience.com/regression-modelling-with-tensorflow-made-easy-train-your-first-model-in-10-minutes-497d829c7338?source=collection_archive---------7-----------------------

从数据收集和准备到模型训练和评估—包括源代码。

克里斯蒂安·维利奇科夫在 Unsplash上拍摄的照片

如今,深度学习是一件大事。见鬼,这甚至是大多数数据科学工作的要求,甚至是入门级的工作。没有比回归更好的入门讲座了。您已经了解了基础统计学和机器学习的概念,现在是时候将神经网络融入其中了。

这篇文章将告诉你如何做。最终,你将拥有一个预测房价的全功能模型,你可以将它附加到你的投资组合中——在做了一些修改之后,这是你的首选。

不想看书?请观看我的视频:

今天的文章涵盖以下主题:

**·** [**Dataset used**](#da81)
**·** [**Dataset exploration and preparation**](#524c)
  ∘ [Deleting unnecessary columns](#4dc7)
  ∘ [Feature engineering](#455c)
  ∘ [Target variable visualization](#4b17)
  ∘ [Data preparation for ML](#4e53)
· [**Training a regression model with TensorFlow**](#bc82)
  ∘ [Loss tracking](#8539)
  ∘ [Building a model](#61fb)
  ∘ [Making predictions](#c0c7)
  ∘ [Model evaluation](#684a)

你可以在 GitHub 上下载源代码。

使用的数据集

今天让我们把事情简单化,坚持使用一个众所周知的房价数据集:

图片 Kaggle 的房价数据集(图片由作者提供)

它有一堆最初无法用神经网络模型使用的特性,所以你必须花一些时间来处理它们。下载数据集,提取 ZIP 文件,并将 CSV 数据集放在安全的地方。

然后激活安装了 TensorFlow 2+的虚拟环境,启动 JupyterLab。您可以自由使用任何其他 IDE,但下面所有的截图都将来自 Jupyter。

数据集探索和准备

第一步是导入 Numpy 和 Pandas,然后导入数据集。下面的代码片段实现了这一点,并随机打印了几行:

import numpy as np
import pandas as pd

df = pd.read_csv('data/data.csv')
df.sample(5)

以下是数据集的外观:

图片 2-房价数据集(图片由作者提供)

你绝对不能以这种格式传递给神经网络。

删除不必要的列

因为我们想避免花太多时间准备数据,所以最好去掉大部分非数字特征。只保留city列,因为它很容易编码:

to_drop = ['date', 'street', 'statezip', 'country']
df = df.drop(to_drop, axis=1)

df.head()

现在应该是这样的:

图 3-移除大部分字符串列后的数据集(作者提供的图片)

您肯定可以保留所有列,并对它们进行一些特性工程。这可能会提高模型的性能。但即使这样也足够满足你今天的需求了。

特征工程

现在,您将花一些时间调整数据集。yr_renovated列的值有时为 0。我想那是因为房子没有翻新。您将创建几个要素-房屋年龄、房屋是否翻修过、是否在过去 10 年翻修过以及是否在过去 30 年翻修过。

你可以对每个提到的特性使用列表理解。方法如下:

**# How old is the house?**
df['house_age'] = [2021 - yr_built for yr_built in df['yr_built']]

**# Was the house renovated and was the renovation recent?**
df['was_renovated'] = [1 if yr_renovated != 0 else 0 
    for yr_renovated in df['yr_renovated']]
df['was_renovated_10_yrs'] = [1 if (2021 - yr_renovated) <= 10 
    else 0 for yr_renovated in df['yr_renovated']]
df['was_renovated_30_yrs'] = [1 if 10 < (2021 - yr_renovated) <= 30
    else 0 for yr_renovated in df['yr_renovated']]

**# Drop original columns**
df = df.drop(['yr_built', 'yr_renovated'], axis=1)
df.head()

以下是数据集现在的样子:

图片 4-特征工程后的数据集(1)(图片由作者提供)

接下来让我们处理city列。许多城市只列出了几栋房子,因此您可以声明一个函数来删除所有不常出现的城市值。这就是remap_location()函数要做的——如果那个城市的房子少于 50 栋,它会被其他的东西代替。这只是减少选项数量的一种方式:

def remap_location(data: pd.DataFrame, 
                   location: str, 
                   threshold: int = 50) -> str:
    if len(data[data['city'] == location]) < threshold:
        return 'Rare'
    return location

让我们来测试一下这个功能——西雅图的城市有很多房子,而秋天的城市只有 11:

图 5-重新映射城市值(图片由作者提供)

让我们将这个函数应用于所有城市,并打印一个 10 行的样本:

df['city'] = df['city'].apply(
    lambda x: remap_location(data=df, location=x)
)
df.sample(10)

图片 6-特征工程后的数据集(2)(图片由作者提供)

一切看起来都是应该的,那我们继续吧。

目标变量可视化

每当你处理价格时,目标变量不太可能是正态分布的。这个住房数据集也不例外。让我们通过导入 Matplotlib 并用直方图可视化分布来验证它:

import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams['figure.figsize'] = (16, 6)
rcParams['axes.spines.top'] = False
rcParams['axes.spines.right'] = False

plt.hist(df['price'], bins=100);

它看起来是这样的:

图 7 —目标变量直方图(1)(图片由作者提供)

离群值肯定是存在的,所以接下来让我们来处理它们。最常见的事情是计算 Z 分数。它们让你知道一个值离平均值有多少标准差。在正态分布的情况下,任何低于或高于 3 个标准差的都被归类为异常值。价格的分布不是正态分布,但是我们仍然要做 Z 测试来移除最右边的房子。

你可以用 Scipy 计算 Z 值。您将把它们指定为一个新的数据集列— price_z,然后只保留 Z 的绝对值小于或等于 3 的行。

还有大约 50 栋价格为 0 美元的房子,所以你也要删除它们:

from scipy import stats

**# Calculate Z-values**
df['price_z'] = np.abs(stats.zscore(df['price']))

**# Filter out outliers**
df = df[df['price_z'] <= 3]

**# Remove houses listed for $0**
df = df[df['price'] != 0]

**# Drop the column**
df = df.drop('price_z', axis=1)

**# Draw a histogram**
plt.hist(df['price'], bins=100);

下面是现在的分布情况:

图 8 —目标变量直方图(2)(图片由作者提供)

仍然有一点倾斜,但是让我们宣布它足够好

作为最后一步,让我们将数据转换成机器学习的格式。

ML 的数据准备

神经网络喜欢只看到相同规模的数字数据。我们的数据集不是,我们也有一些非数字数据。这就是数据缩放和一次性编码发挥作用的地方。

您现在可以单独转换每个功能,但有一个更好的方法。您可以使用 Scikit-Learn 中的make_column_transformer()函数来一次性应用缩放和编码。

您可以忽略waterfrontwas_renovatedwas_renovated_10_yrswas_renovated_30_yrs等特征,因为它们已经是您需要的格式:

from sklearn.compose import make_column_transformer
from sklearn.preprocessing import MinMaxScaler, OneHotEncoder

transformer = make_column_transformer(
    (MinMaxScaler(), 
        ['sqft_living', 'sqft_lot','sqft_above', 
         'sqft_basement', 'house_age']),
    (OneHotEncoder(handle_unknown='ignore'), 
        ['bedrooms', 'bathrooms', 'floors', 
         'view', 'condition'])
)

接下来,让我们将特征从目标变量中分离出来,并将数据集分成训练和测试部分。训练集将占 80%的数据,我们将使用其他所有数据进行测试:

from sklearn.model_selection import train_test_split

X = df.drop('price', axis=1)
y = df['price']

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

最后,你可以应用一分钟前声明的变换。您将调整和转换训练特征,并且仅将转换应用于测试集:

**# Fit**
transformer.fit(X_train)

**# Apply the transformation**
X_train = transformer.transform(X_train)
X_test = transformer.transform(X_test)

您将无法直接检查X_trainX_test,因为它们现在存储为稀疏矩阵:

图 9-稀疏矩阵(图片由作者提供)

TensorFlow 将无法读取该格式,因此您必须将其转换为多维 Numpy 数组。您可以使用toarray()功能。这里有一个例子:

X_train.toarray()

图 10-稀疏矩阵到 Numpy 数组(图片由作者提供)

将这两个特性集转换成一个 Numpy 数组,就可以开始了:

X_train = X_train.toarray()
X_test = X_test.toarray()

最后来训练模型。

使用 TensorFlow 训练回归模型

现在,您将构建一个由完全连接的层组成的顺序模型。有许多导入工作要做,所以让我们先把它解决掉:

import tensorflow as tf
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import Adam
from tensorflow.keras import backend as K

损失跟踪

你正在处理这里的房价,所以如果你跟踪它,比如说,均方差,损失可能会很大。这个指标对你来说也不是很有用,因为它基本上是告诉你你的模型在平方单位上有多错误。

你可以计算 MSE 的平方根,回到原来的单位。默认情况下不支持该指标,但是我们可以手动声明它。请记住,您必须使用 Keras 后端的函数才能使它工作:

def rmse(y_true, y_pred):
    return K.sqrt(K.mean(K.square(y_pred - y_true)))

建立模型

现在你终于可以声明一个模型了。这将是一个简单的,只有 256,256 和 128 个单位的三个隐藏层。请随意尝试这些,因为建立神经网络没有对错之分。这些层之后是一个节点的输出层,因为您预测的是一个数值。

然后,您将使用 RMSE 编译一个模型,作为一种跟踪损失和评估指标的方法,并使用 Adam 优化器优化该模型。

最后,您将根据 100 个时期的训练数据训练模型:

tf.random.set_seed(42)

model = Sequential([
    Dense(256, activation='relu'),
    Dense(256, activation='relu'),
    Dense(128, activation='relu'),
    Dense(1)
])

model.compile(
    loss=rmse,
    optimizer=Adam(),
    metrics=[rmse]
)

model.fit(X_train, y_train, epochs=100)

培训应该在一分钟左右完成,具体取决于硬件:

图 11 —使用 TensorFlow 进行回归模型训练(图片由作者提供)

训练集的最终 RMSE 值刚刚超过 192000,这意味着对于一个普通的房子,该模型的价格估计错误 192000 美元。

做预测

您可以对测试集进行预测:

predictions = model.predict(X_test)
predictions[:5]

以下是前五个预测的样子:

图片 12 —前 5 个预测(图片由作者提供)

如果你想计算任何指标,你必须把它们转换成一维数组。您可以使用 Numpy 的ravel()函数来实现:

predictions = np.ravel(predictions)
predictions[:5]

结果如下:

图 13-作为 1D 数组的前 5 个预测(图片由作者提供)

模型评估

现在让我们通过使用 RMSE 来评估测试集上的预测:

rmse(y_test, predictions).numpy()

您将得到 191000 作为错误值,这表明模型没有过度拟合训练数据。对于更多的时期,使用更复杂的模型训练可能会得到更好的结果。这是你可以自己尝试的事情。

离别赠言

这就做到了——到目前为止,您已经训练了一个简单的神经网络模型,并且知道如何对新数据进行预测。不过,你还有很多可以改进的地方。

例如,你可以花更多的时间准备数据。我们删除了日期-时间特性、街道信息、邮政编码等等,这些对模型性能可能很有价值。问题是——这些需要太多的时间来准备,我想让这些文章简短一些。

您还可以向网络添加额外的层,增加神经元的数量,选择不同的激活函数,选择不同的优化器,添加丢弃层,等等。可能性几乎是无穷无尽的,所以一切都归结于实验。

下一篇文章将介绍如何使用 TensorFlow 建立分类模型,如果您想了解更多信息,请继续关注。

感谢阅读。

喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。

https://medium.com/@radecicdario/membership

保持联系

  • 注册我的简讯
  • 订阅 YouTube
  • 在 LinkedIn 上连接

带有 Seaborn 的 Python 中的回归图

原文:https://towardsdatascience.com/regression-plots-in-python-with-seaborn-118472b12e3d?source=collection_archive---------2-----------------------

当处理简单的图表时,熊猫绘图给了你很多你需要的东西,但是有一个明显的遗漏

回归图示例-作者图片

散点图是探索数据中的关系或模式的好方法。但是添加一条回归线可以使这些模式突出出来,这是熊猫图 API 没有内置的一件事。

您可以使用 Statsmodels 等统计库,甚至 Numpy,根据您的数据创建回归模型,并将其包含在您的绘图中。但是,如果你需要的只是数据中关系的可视化指南, Seaborn 可以很容易地为你做到这一点。

Seaborn 是一个统计绘图库,可以读取 Pandas 数据帧(以及其他数据结构),并提供简单的方法将回归线添加到散点图中。

我们将了解如何使用 Seaborn 中的 regplot 函数来实现这一点,我们还将了解帮助我们了解数据中简单关系的线性回归,以及显示更复杂情况的高阶回归。

为了介绍 regplot 函数,我们将首先制造一些数据来产生一些理想化的图,然后使用世界人口数据来说明它在现实世界中的用途。

回归图

下面是散点图和添加了线性回归线的同一图的示例。回归线试图通过散点图中的点找到最佳拟合。线周围的阴影区域是置信区间

散点图—作者提供的图片

带有线性回归线的散点图-图片由作者提供

你可能能够从第一个图中看出线性关系,但是在散点图的顶部添加一条回归线会使它更加清晰。

锡伯恩图书馆

让我们直接进入代码,看看 Seaborn 如何帮助我们。首先导入 Seaborn 库。

import seaborn as sns

现在,我们将创建几个 Numpy 数组来表示我们将要绘制的 xy 数据。在下面的代码中,我们导入 Numpy 库,然后创建一个从-5 到 5 的整数数组——该数组表示 x 数据。为了创建 y 数组,我们使用一个公式来创建 xy 之间的线性关系:

y = x*2 + 3

当我们绘制该图时,我们期望看到截距为 3 的直线图,以及 xy 之间的二对一关系。下面代码的最后一行创建了一个散点图,我们可以看到它是一条直线的形式。

import numpy as npx=np.array([-5,-4,-3,-2,-1,0,1,2,3,4,5])
y=x*2+3
sns.scatterplot(x=x,y=y)

一个线性的情节——作者的形象

因此,当我们创建一个 regplot ,一个包含回归线的图时,我们希望该线与分散点重合。

sns.regplot(x=x,y=y)

一个线性的情节——作者的形象

当然,确实如此。

但是回归不一定是线性的。在下一个代码块中,我们定义了 xy 之间的二次关系。然后我们绘制曲线,但我们设置了一个二阶回归函数order=2,而不是默认的线性选项。这指示 regplot 找到二次关系。

y2=x**2+2*x+3
sns.regplot(x=x,y=y2,order=2)

二次曲线——作者的图像

回归线再次与散点图中的点精确匹配。

我们可以继续,但我们将停止在三阶回归,如下图所示。

y3=x**3+x**2+2*x+3
sns.regplot(x=x,y=y3,order=3)

三阶图—作者提供的图像

真实世界数据

当然,这些例子是不现实的。我们给出了 regplot 一阶、二阶和三阶图的精确表示,不出所料,回归线与散点图点完全吻合。真实数据通常更嘈杂:有随机变化,测量误差。或者,也许数据根本不符合我们理想的线性、二次或三次公式。

所以,现在我们将看到相同类型的图,但有一些真实的数据。

我们将加载一些世界人口数据,选择一个国家,并尝试看看回归图是否能给我们一些启示。

在这里,我们设置下载到 Github 存储库的数据的 url 数据最初来自 Gapminder(您可以在下面看到完整的确认*)。

然后,我们将数据加载到熊猫数据框架中。

这里需要注意一些事情:这个文件是一个 tsv 文件,类似于 csv 文件,但是用制表符代替逗号作为分隔符,所以我们需要在转换成 dataframe 时指定它;此外,我还将 year 字段转换为整数,以便更容易进行比较(否则它将作为浮点数加载)。

人口总数是实数,当然是以百万计。为了让这些在图表上看起来更好,我把它们都除以一百万。

popDataURL = "https://raw.githubusercontent.com/jennybc/gapminder/master/inst/extdata/gapminder.tsv"import pandas as pdpopData = pd.read_csv(popDataURL, delimiter='\t', 
   dtype=({'year':int}))
popData['pop']=popData['pop']/1000000
popData

人口表-按作者分类的图像

最初,我只关注一个国家,所以我创建了一个名为SpainData的新数据框架。然后,我在一个 regplot 上绘制了西班牙过去几十年的人口,寻找时间和人口之间默认的线性关系。

SpainData = popData[popData['country']=='Spain' 
sns.regplot(x="year", y="pop", data=SpainData)

西班牙的人口增长—图片由作者提供

从这个角度来看,人口似乎在稳步增长,但如果仔细观察分散点,就会发现在最初的几十年里,曲线更陡,而在最近,曲线变得更缓。也许直线不是最好的拟合。让我们试试高阶回归。

sns.regplot(x="year", y="pop", data=SpainData, order=2, ci=None)

西班牙的人口增长—图片由作者提供

sns.regplot(x="year", y="pop", data=SpainData, order=3, ci=None)

西班牙的人口增长—图片由作者提供

只看上面的图,看起来二阶图比其他图更接近真实数据。这并不是说西班牙的人口可以用严格的二次模型来解释——它可能比这更复杂——但是这条曲线让我们更好地理解正在发生的事情;人口增长不是以稳定的速度增长,而是在放缓。

试图用这样一个简单的数学模型来预测一个国家可能的人口增长可能是错误的,因为有太多的因素需要考虑。但与没有回归线的散点图相比,Seaborn 的纯视觉方法能让我们更好地理解趋势。

多重情节

如果我们想比较不同的国家呢?Seaborn 为我们提供了一种使用hue参数的简洁方法。

让我们用五个重要的欧洲国家,法国、德国、西班牙、意大利和荷兰,创建一个新的数据框架,看看他们的人口增长如何比较。

最终的剧情是用 lmplot 完成的。这类似于 regplot ,但是允许我们通过设置hue='country'用不同的颜色绘制不同的国家。

topeucountries = ['France','Germany','Spain','Italy','Netherlands']europeData = popData[popData['country'].isin(topeucountries)]
sns.lmplot(x="year", y="pop", data=europeData, hue='country',
   order=2, ci=False)

一些欧洲国家的人口增长情况—图片由作者提供

在结果图中,你可以看到,虽然人口增长仍处于明显上升的轨道,但似乎在放缓。在意大利的例子中,这一点尤其明显。

让我们看另一个例子。

你越富有,你活得越久

无可争议的是,平均而言,你拥有的钱越多,你的预期寿命就越长。财富和预期寿命是联系在一起的,因为可以获得更好的医疗设施、更好的饮食等等。

因此,你挣得越多,你活得越久,但这只是在一定程度上。寿命不能随着财富持续增长;必须有个限度。

这里有两个图表展示了这种联系。这里,我们用人均 GDP 来表示财富(这是一个国家的总 GDP 除以居住在那里的人口数量,可以认为是一个特定国家的平均财富)。

第一个线性图似乎是一个相当好的拟合,但它不可能随着收入的增加而延伸到 100 年、150 年或 200 年。必须有个限度;收入的增加必然遵循收益递减规律。

order=2的版本证实了这一点,并给出了一个更好的现实图景,即随着 GdpPercap 的增加,预期寿命的增加逐渐减少。

sns.lmplot(x="gdpPercap", y="lifeExp",data=europeData, 
   ci=False, order=1)
sns.lmplot(x="gdpPercap", y="lifeExp",data=europeData, 
   ci=False, order=2)

预期寿命与 GdpPercap——作者图片

预期寿命与 GdpPercap——作者图片

希望您可以从上面的例子中看到回归图可以使解释数据变得更加容易,而 Seaborn 为您提供了轻松做到这一点的工具。

一如既往,感谢阅读。如果你想知道我什么时候发表新文章,请考虑在这里注册一个电子邮件提醒。

如果你不是一个媒体订阅者,那就注册吧,这样你就可以每月花 5 美元阅读尽可能多的文章。在这里注册,我将赚取一小笔佣金。

R 和 Python 中的回归样条

原文:https://towardsdatascience.com/regression-splines-in-r-and-python-cfba3e628bcd?source=collection_archive---------5-----------------------

线性模型的扩展

这篇文章讨论了回归样条的基础知识以及 R 和 Python 中的实现。

卢卡斯·戴维斯在 Unsplash 上的照片

T 线性模型之所以这样命名,是因为输入(自变量)和输出(因变量)之间存在线性关系。尽管我们知道现实世界的数据很有可能表现出非线性,但人们通常会将线性模型视为最佳选择之一。

原因主要有两点。首先,通过可接受的近似,线性模型是解释起来最简单的模型之一。第二,线性模型的低复杂性使得它不太可能过度拟合数据,尤其是当你有小的 n(样本大小)和大的 p(可变数)时。

如果来自线性模型的估计偏差太大而无法接受,我们需要在我们的模型中超越线性。这并不意味着我们必须直接进入神经网络,因为那里有许多更简单的模型,复杂性(方差)更低。

多项式回归的概念

“超越线性”的最直接的方法是简单地提高预测器的等级。例如,三次回归使用 X、X 和 X 作为预测值。“立方”意味着模型中变量 X 的最高幂是 3。三次回归模型的训练也非常简单,它估计 X、X 和 X 的系数,就像我们在具有三个独立变量的简单线性模型中所做的那样。这些提高原始预测能力的非线性模型被称为多项式回归模型。

多项式回归模型实际上是基函数方法的一个特例。基函数使用应用于 x 的变换:b₁(X)、b₂(X)、b₃(X)、…、Bk(X)作为新变量,而不是线性模型中的 x。

基函数方法。(图片来自 James,Gareth 等人统计学习入门。第 112 卷。纽约:施普林格,2013 年。)

对于多项式回归,基函数是 bj(x) = x^j.

在实践中,多项式回归模型的缺点之一是它用单个多项式函数来拟合整个训练数据。在这种情况下,为了跟踪跨越整个 X 范围的大多数数据点,模型曲线将会超级波动(我们讨论的是只有一个自变量 X 的情况)。换句话说,单一多项式模型需要高次项(如 X⁵)才能如此灵活。多项式模型的次数越高,模型在边界处越不稳定。

其中一个解决方案是使用分段多项式模型,将变量的范围分成几个不同的局部区域,在每个区域内,一个多项式函数与数据拟合。然而,这种类型的分段多项式有一个主要问题:它在节点(局部区域边界)是不连续的。

回归样条的概念

如果我们强制(约束)分段多项式在节点处平滑连接,我们得到一种新的方法,称为 回归样条 。这里的光滑节点意味着两个分段函数在节点处是连续的,在一阶和二阶导数处也是连续的。*“统计学习简介”*中的一张图很好地说明了这个概念,如下所示。

分段三次:不局限于连续;连续分段三次:只约束有连续值;三次样条:被约束为在节点处具有连续值,并且在一阶和二阶导数中也是连续的。线性样条:在节点处具有连续性的线性函数。

在上面的图中,我们可以看到回归样条(左下角)在节点处产生了一个平滑的连接。如果我们只将分段函数约束为连续的,那么接合点看起来就不那么平滑了,如右上图所示。

类似于多项式回归的基表示,回归样条也可以由基函数表示。让我们用 K 结的三次样条作为例子模型。它可以被框定为

表示带有 K 个节点的三次样条的基本模型。(《统计学习概论》)

三次样条的基函数总数为 K+3 ,这里我们在最小二乘回归中使用 K+3 预测器。与简单的三次模型相比,它具有 K 额外的预测值(X、X 和 X 作为三个预测值),因为这些额外的函数用于调整节点处的变化。

三次样条中最常用的基函数之一是 截断幂基函数 定义为,

截断的幂基函数,(图片来自“统计学习简介”)

其中ξ是结。

所以,总的来说,三次样条有以下预测值,X,X2,X3,h(X,ξ1),h(X,ξ2),...,h(X,ξK)。

不要被截断的幂基函数吓到。它的概念很简单,就像一个开关,当 X 大于它对应的纽结时,这个开关就打开了。

很难直接想象三次样条中的函数,所以让我们看看 1 次样条的动画。下面的 GIF 来自这篇伟大的文章,用来说明截断的幂函数是如何打开的。

图来源:https://stats . stack exchange . com/questions/433458/spline-basis-function-notation-to-include-constraint-for-continuity-at-the-knots

黑色曲线的转折点准确显示了结的位置。在上面的图中总共有四个结,因此有四个额外的截断幂函数用四条彩色虚线突出显示。

当我们看三次样条中的截断幂函数时,也是同样的想法。

图来源:https://stats . stack exchange . com/questions/433458/spline-basis-function-notation-to-include-constraint-for-continuity-at-the-knots

在黑色曲线中也有四个“转折点”,并且当 X 大于相应的结时,每个截断的幂函数被打开。

回归样条的缺点之一是当 X 很小或很大(在边界区域)时,回归样条的方差很大。这可以通过向模型添加边界约束来解决,其中我们强制模型在边界区域是线性的(X 的 1 度)。这也叫做 自然样条 。因为这不是本文的重点,我们将在以后的文章中讨论。

回归样条的实现

好了,在了解了回归样条的基本概念之后,让我展示一下 R 和 Python 中的实现。

幸运的是,已经为回归样条函数建立了函数,所以标准实现在 R 和 Python 中都非常简单。我将只展示基本的实现,但是您可能需要在实际情况下修改代码。

R 中,我们将使用示例数据集 工资 中的 ISLR 。执行回归样条的 R 包是 样条

library(splines)
library(ISLR)
attach(Wage)
spl_mod <- lm(wage ~ bs(age, knots = c(30,50)), data = Wage)

其中函数 bs() 生成样条基函数的整个矩阵, 节点 取 X 中节点的位置(此处为变量 年龄 ), 数据=工资 指定数据源,工资~ 表示因变量为 工资 (变量名),以及 【T23)

然后,我们可以通过以下方式检查拟合参数:

summary(spl_mod)

Python 中,我们不能像在 R 中那样用一行代码编写,而是需要先生成样条的基函数矩阵,然后用线性回归模型拟合。

我们需要几个统计模型包,

**import pandas as pd
import numpy as np
import statsmodels.api as sm
from patsy import dmatrix** df = pd.read_csv('Wage.csv')
basis_x = dmatrix("bs(df.age, knots=(30,50), degree=3, include_intercept=False)", {"df.age": df.age}, return_type='dataframe') 

其中 df工资 数据集, basis_x 是基函数的矩阵, bs 是生成基函数的函数。装配步骤是,

spl_mod = sm.GLM(df.wage, basis_x).fit()

我们可以通过以下方式检查拟合参数:

spl_mod.params

就是这样。

如果你完全理解回归样条的思想,R/Python 代码只是小菜一碟,因为你所需要的是首先将 X 变换为基函数矩阵,然后拟合一个简单的线性回归。

干杯!希望这篇短文有帮助。

照片由 Yutacar 在 Unsplash 上拍摄

回归尾部与回归均值*

原文:https://towardsdatascience.com/regression-to-the-mean-regression-to-the-tail-how-to-mitigate-covid-19-climate-change-and-other-c0e9b62aca5c?source=collection_archive---------14-----------------------

思想和理论

更糟糕的疫情和更极端的气候将袭击我们。对于政府、企业和公众来说,导航的基本原则是什么?

马特·帕尔默在 Unsplash 拍摄的照片

回归到均值是美好可靠的,回归到尾部是可靠吓人的。我们生活在向尾部回归的时代。一场比迄今为止最糟糕的疫情袭击我们,气候比我们迄今为止见过的任何一场都更极端,这只是时间问题。对于政府、企业和公众来说,应对这种局面的基本原则是什么?

弗朗西斯·高尔顿爵士创造了“回归均值”这个术语,或者他最初称之为“回归平庸”。它现在是统计学中广泛使用的一个概念,描述了当样本数量足够多时,样本均值的测量如何趋向于总体均值,尽管在单个测量中可能有很大的变化。

高尔顿举例说明了他的原理,高个子父母的孩子长大后往往比他们的父母矮,更接近于人口的平均值,反之亦然。

在一个更简单的例子中,在统计上独立的事件中,一个轮盘赌轮盘可以连续显示红色五次——在轮盘的任何连续五次旋转中,它会出现这种情况的 3%——但在接下来的轮盘旋转中,红色与黑色的赔率是 50–50。因此,轮盘旋转的次数越多,结果就越接近 50–50 的红色对黑色,即使开始时有连续五次红色。当进行大量旋转时,随着数量的增加,旋转的平均结果会回归到预期的平均值,无论起点是什么。

还有一个因诺贝尔经济学奖获得者丹尼尔·卡内曼而著名的例子,在最近几次飞行中表现良好的飞行员在以后的飞行中表现不佳,更接近多次飞行的平均水平。这不是因为飞行员的技能下降了,而是因为他们最近的良好表现不是因为技能的提高,而是随机事件的幸运组合。

回归均值的前提是均值存在。对于一些具有重大社会影响的随机事件来说,情况并非如此。

没有什么比正确的理论更实际的了。回归均值已经在数学上被证明适用于许多类型的统计数据,并且在保险、赌场和风险管理(例如飞行安全)中非常有用。

但是回归均值的前提是存在一个总体均值。对于一些具有重大社会影响的随机事件来说,情况并非如此。

例如,洪水、森林火灾、地震、战争、恐怖袭击、犯罪和 IT 投资的规模分布没有总体平均值,或者由于无限的变化而很难定义平均值。换句话说,均值和/或方差不存在。对于这种分布来说,回归均值是一个没有意义的概念,而所谓的“回归尾部”是有意义的,也是必然的。

回归到尾部适用于任何非零概率密度趋于无穷大的分布。新极值出现的频率以及它们超出先前记录的程度对于分布的厚尾程度具有决定性作用,例如,它是否具有无限方差和均值。超过某个频率和极值大小,平均值随着测量的事件越多而增加,平均值最终接近无穷大而不是收敛。在这种情况下,回归到平均值意味着回归到无穷大,即不存在的平均值。深度灾难(如地震、海啸、流行病和战争)往往遵循这种类型的分布。

尾部回归定律表明,总会有一个事件比迄今为止最极端的事件还要极端。它的出现只是时间问题。

我建议我们将这种现象命名为“尾部回归定律”,即事件以足够的规模和频率返回到尾部,使均值不会收敛该定律描述了一个有许多极端事件的情况,无论最极端的事件有多极端,总会有比这更极端的事件。它的出现只是时间问题。

当面临遵循尾部回归定律的风险时,谨慎的决策者不会指望运气,也不会指望传统的高斯风险管理,高斯风险管理比指望运气更糟糕,因为它给人一种虚假的安全感。相反,决策者想要做两件事:(a)“切尾”,通过缓解来降低风险,以及(b)实践“预防原则”,即通过过度谨慎来完全避免尾部风险。

许多认知和其他偏见——包括简单的一厢情愿——欺骗我们看到温和的风险,而实际上风险是巨大的。

在任何给定的情况下,谨慎的决策者和他们的风险经理必须能够决定他们是否面临回归均值(轻度高斯风险)或回归尾部(极端厚尾风险)的情况,最重要的是,永远不要将前者误认为后者。这是一项艰巨的任务,因为许多认知和其他偏见——包括简单的一厢情愿——诱使我们看到温和的风险,而实际上风险是巨大的。

流行病似乎遵循广义的帕累托分布。回归到尾部的法则因此是相关的,有三个重要的含义。

举例来说,考虑当前的新冠肺炎疫情。Cirillo 和 Taleb (2020)认为流行病(以死亡人数衡量)似乎遵循广义帕累托分布。回归到尾部的法则因此是相关的,有三个重要的含义。

新冠肺炎·疫情是完全可以预测的。事实上,疫情早在几年前就被纳西姆·尼古拉斯·塔勒布和比尔·盖茨这样截然不同的人预测到了。

首先,新冠肺炎疫情是完全可以预测的。事实上,疫情早在几年前就被各种不同的人预测到了,比如《因瑟托》的作者纳西姆·尼古拉斯·塔勒布、慈善家比尔·盖茨以及众多流行病学家,他们在被政府、企业和媒体忽视多年后,现在理所当然地成为了“我说了什么”的预言家。

第二,任何了解尾部退化的人都清楚,一旦危机发展,主要的缓解措施应该是什么,即:(a)切断尾部(通过封锁、个人保护设备、测试、开发疫苗等打破传播链)。)和(b)预防原则(宁可封锁太多也不要封锁太少)——在全球范围内以 T4 的速度和规模立即推出 T2。关闭湿货市场和改变食品加工业将有助于从一开始就防止危机的发展。

早期缓解和预防会在面临尾部退化时获得千倍的回报。

早期缓解和预防会在面临尾部退化时获得千倍的回报。不幸的是,中国领导层起初试图压制有关病毒的信息,从而推迟了缓解措施。然后,一旦数据公布,其他地方的领导人——包括美国和英国——很慢才意识到他们面临的是极端风险,而不是较温和的风险。因此,他们在做出正确决定时行动迟缓。封闭的政府也减缓了进步。

第三,应急措施必须到位,以便快速扩大规模。当领导人终于明白新冠肺炎是一个厚尾现象并开始做出正确的决定时,事实证明卫生服务、政府和企业准备不足,以至于口罩、长袍和其他卫生工作者防护装备等基本用品立即告罄。准备金的缺乏使其无法有效快速地扩大规模——就像没有准备金的银行在危机中毫无用处一样。

由于这三点中的每一点都失败了,许多地方的缓解措施来得,并且的规模不足,也就是说,与面临尾部衰退时所需要的正好相反,在生命损失、苦难和财富破坏方面带来了毁灭性的后果。

为了避免未来出现类似的情况,领导者和公民必须理解并按照回归到尾部的规律行事。就流行病而言,我们可以吸取两个教训。

每个人都需要诚实面对,并牢记在心,未来会有更多的大流行,其中之一将比新冠肺炎疫情更糟糕。

首先,每个人都需要诚实地面对并记住,未来将会有更多的流行病,其中之一将会比新冠肺炎疫情更严重。这一令人不安的事实直接来自流行病的幂律分布和相关的尾部回归定律。

其次,一旦领导人和公民明白大流行意味着回归到尾部,他们也将明白如何应对下一个疫情:立即、快速、大规模地切断尾部并实施预防原则,同时做好必要的应急准备。

这两个教训是一般性的。它们不仅适用于流行病,还适用于所有服从尾部回归定律的现象,例如:洪水、森林火灾、地震、海啸、雪崩、犯罪、战争、恐怖袭击、停电、破产和网络犯罪,以及灾难性较小但财务风险较高的风险项目,如举办奥运会、建设核电站、高速铁路系统、水电大坝、新城市,甚至是采购新 IT 系统这样看似无害的事情,后者是当前全球数字化工作中的一个严重缺陷。

政府用来重启衰退经济的大规模刺激计划,通常包括具有厚尾金融风险的大型建筑和投资项目。

疫情危机后的经济重建也将遵循尾部回归定律,尽管没有那么明显。生命损失不会成为主要风险,但金融脆弱性和财富毁灭将继续成为主要风险。政府用来重启衰退经济的大规模刺激支出计划通常包括具有厚尾金融风险的大型建筑和投资项目,如 IT、交通、能源、水、教育、住房、医疗和国防领域的大型项目。

通过选择风能而不是核能,回归尾部的风险将大大降低。埃隆·马斯克和奥斯泰德明白这一点。大能源没有。

有些项目比其他项目更厚尾,也就是说,它们更容易受到尾部回归定律的影响。应该使用数据来区分厚尾项目和薄尾项目,并尽可能坚持后者。例如,核电站是定制的,建造缓慢,金融风险大;而风力发电场和能量储存是模块化的、快速的和薄尾的。通过选择风能而不是核能,回归尾部的风险将大大降低。埃隆·马斯克和奥斯泰德明白这一点。大能源没有。每一种投资选择都必须以这种方式进行评估,以确保刺激支出成为对经济的提振而不是拖累,后者发生的频率比我们想象的要高。

下表显示了服从尾部回归定律的十大现象,按尾部的肥度排序。表中的所有现象都具有无限的方差,即它们是高度厚尾的。我们看到,最粗的尾部——表明对尾部的最大和最频繁的回归——被发现用于地震(以强度衡量),这有充分的理由经常被认为是幂律分布深度灾难的典型案例。流行病(以死亡人数衡量)处于中间,电力中断(以受影响的客户数量衡量)处于底部。

服从回归到尾部法则的前 10 个现象,排在尾部的肥度之后。排名越高,尾巴越粗,对尾巴的回归就越大,越频繁。所有的现象都有无限的变化。下表显示了有数据可查的现象。

来源:作者,【https://bit.ly/2TMbCg5

存在四种有效的缓解措施:a)切断尾巴,b)使用预防原则,c)确保必要的应急措施到位,以及 d)立即、快速、大规模地采取行动。这是在尾部回归时代降低风险的四个基本原则。

对于遵循尾部回归定律的这些现象和许多其他现象,其影响是显而易见的:事件总是会回归到尾部,即极端的结果,迟早会出现一个比迄今为止最极端的事件更极端的事件,往往会危及生命和财富。同样清楚的是,存在四种有效的缓解措施:a)切断尾巴,b)使用预防原则,c)确保必要的应急措施到位,以及 d)立即、快速、大规模地采取行动。这是在尾部回归时代降低风险的四个基本原则。

如果我们积极地遵循这些原则,回归到尾部将是可管理的。如果我们不这样做,尾部事件将一次又一次地回来困扰我们,导致不必要的大屠杀,而我们则被动地追赶缓解措施,任何了解尾部回归的人都会承认,这些措施早在我们陷入尾部之前就应该到位,就像新冠肺炎疫情事件一样。

对于我们今天面临的最大、最紧迫的尾部风险:气候变化,新冠肺炎可能最终只是一场彩排

许多人正确地观察到,对于我们今天面临的最大和最紧迫的尾部风险:气候变化,新冠肺炎可能最终只是一场彩排。如果气候科学是正确的——没有理由认为它是错误的——回归到尾部的法则在这里将会特别相关。它告诉我们,如果现在不以前所未有的速度和规模减缓气候变化,没有时间浪费在所涉及的每一个步骤上,那么大量的生命和财富损失可能会随之而来。

回归到尾部的法则进一步告诉我们,气候缓解的重点必须是:(a)确定哪些缓解措施在闪电战般的速度下特别具有可扩展性,哪些没有,( b)加速和加强那些有可扩展性的措施,同时无情地摒弃那些没有可扩展性的措施,这两项措施在今天都做得不好。

如果我们真正理解气候变化尾部回归定律的紧迫性,我们就有机会在这种特殊的尾部风险中生存下来。如果我们不…

如果我们做到了这一点,也就是说,如果我们真正理解了气候变化尾部回归定律的紧迫性,我们就有机会在这种特殊的尾部风险中生存下来。如果我们不这样做,这很可能意味着告别我们所知的世界,大规模毁灭生命和财富,相比之下,使新冠肺炎看起来像一个野餐。


)有关本文的更长版本,请参见 Flyvbjerg,Bent,2020,“回归到尾部的法则:如何在新冠肺炎、气候危机和其他灾难中生存”,《环境科学与政策*,第 114 卷,12 月,第 614-618 页。免费 pdf 这里。


参考

西里洛、帕斯夸莱和纳西姆·尼古拉斯·塔勒布,2020,“传染病的尾部风险”, arXiv ,4 月 18 日。

Clauset,a .,Shalizi,C. R .和 Newman,M. E .,2009,“经验数据中的幂律分布”, SIAM Review ,51(4),661–703。

Flyvbjerg,Bent,Alexander Budzier,Dirk W. Bester 和 Daniel Lunn,正在进行中,“数字化灾难:走向 IT 投资风险理论”

Flyvbjerg,Bent,Alexander Budzier,和 Daniel Lunn,进行中,“回归到尾部:为什么奥运会爆炸。”

洪炳辉、李克伟、李建伟,2007,“企业破产中的权力法”,物理学报 A ,361(1-2),6-8。

Maillart,t .和 Sornette,d .,2010,“网络风险的重尾分布”,《欧洲物理杂志 B ,75(3),357–364。

Malamud,B. D .和 Turcotte,D. L .,2006,“幂律频率统计对洪水的适用性”,《水文杂志》,322(1–4),168–180。

纽曼,M. E .,2005,“幂定律、帕累托分布和齐夫定律”,当代物理学,46(5),323–351。

Python 中的回归树从头开始

原文:https://towardsdatascience.com/regression-tree-in-python-from-scratch-9b7b64c815e3?source=collection_archive---------6-----------------------

用 Python 编写流行的回归树算法,并解释其本质

回归树的图形;作者模式

本文旨在向读者展示 python 中回归树算法背后的代码和直觉。我发现浏览一个算法的代码是一个非常好的教育工具,可以帮助我理解在引擎盖下发生了什么。我希望读者也能从中受益。

解释的算法是回归树算法。它用于模拟连续变量 Y 和一组特征 X: 之间的关系

**Y = f(X)**

函数 f 是一组特征和特征值的规则,它在给定特征 x 的情况下做了解释 Y 变量的“最好”工作

本文是上一篇关于决策树的文章的延续:

我假设读者熟悉节点、分裂和树的层次的概念。所有这些概念在前一篇文章中都有解释。

在上一篇文章中, Y 变量是一个包含两个值——0 和 1 的二进制变量。

回归树是处理连续响应变量 Y. 的估计器,例如,身高、工资、点击量等。该算法在我的 GitHub 存储库中编码和实现(以及附带的笔记本):

https://github.com/Eligijus112/decision-tree-python

回归的节点类非常类似于上一篇文章中的二叉分类树类:

回归节点;作者代码

每个节点,无论其级别如何,都具有以下主要属性:

落入某个节点的 Y 变量的平均值

残差(真实 Y 值和平均值之间的差值)

每个节点的均方误差

节点的特征和响应变量

上述所有属性都将用于拆分过程。

分割前的特征准备与分类决策树中的相同:

对于数据集中的每个数字特征,我们对特征值进行排序,并获取两个相邻值的平均值。

例如,假设我们的特征 1 如下:

feature_1 = [7, 5, 9, 1, 2, 8]

已排序:

feature_1 = [1, 2, 5, 7, 8, 9]

邻居的方式:

feature_1 = [1.5, 3.5, 6.5, 7.5, 8.5]

我们对所有的数字特征做同样的事情。

对于分类特征,比如

feature_2 = [‘cat’, ‘dog’, ‘dog’, ‘cow’, ‘cat’, ‘cow’] 

我们发现该特性的独特价值:

feature_2 = [‘cat’, ‘dog’, ‘cow’]

通过这样的预处理,对于每个特征和每个特征值,我们将节点的数据分成两个子集,左和右:

对于数字特征(例如,feature_p):

**left = X[X[feature_p]<value]****right = X[X[feature_p]≥value]**

对于分类特征(例如,feature_c):

**left = X[X[feature_c]==feature_c]****right = X[X[feature_c]!=feature_c]**

在分类树中,我们需要计算每个数据集(左侧和右侧)中的观察值数量。在回归树中,我们将两个数据集的所有残差放在一起,并计算联合的均方误差统计量。给出最低 mse 的特征和特征值是分裂标准。

例如,让我们考虑两种可能的拆分, X 在 4X 在 6.5 :

示例拆分 1;按作者分类的图表

示例拆分 2;按作者分类的图表

左侧节点的第一个示例中的残差是**【3.5,4.5】**

右节点第一个例子中的残差是**【2,0,-2】**

左节点第二个例子中的残差是 [-0.67,0.33,0.33]

右节点的第二个例子中的残差是**【1,-1】**

因此,第一次拆分的残差为:

r1 = [3.5,4.5,2,0,-2]

第二个:

r2 = [-0.67,0.33,0.33,1,-1]

然后,我们找到两个残差集的 mse 统计量,并对它们进行比较:

第一次分割残差的 MSE

第二次分割残差的 MSE

正如我们所见,第二个 mse 比第一个小得多。因此,在两种可能的分裂中,第二种 (X ≥ 6) 更好。

让我们用一个例子来试试定制的 NodeRegression 类。

我们想要创建一个树 f ,这样:

**mpg = f(horsepower, weight)**

这里

每加仑汽油跑多少英里

马力 —一辆车的马力数;发动机功率

重量——一辆汽车的重量,单位为千克

mpg 与马力和重量的关系:按作者分类的图表

实现与前面的树方法非常相似:

# Reading data
d = pd.read_csv("data/regression/auto-mpg.csv")# Subsetting
d = d[d['horsepower']!='?']# Constructing the X and Y matrices
features = ['horsepower', 'weight']# Ensuring the correct types 
for ft in features:
    d[ft] = pd.to_numeric(d[ft])# Constructing the X and Y matrices
X = d[features]
Y = d['mpg'].values.tolist()

节点初始化和树的生长:

# Initiating the Node
root = NodeRegression(Y, X, max_depth=2, min_samples_split=3)# Growing the tree
root.grow_tree()

最后打印出这棵树:

# Printing tree
root.print_tree()

长成的树;图片来自作者的笔记本

我们可以将结果与回归树的 scikit-learn 实现进行比较,https://scikit-learn . org/stable/modules/generated/sk learn . tree . decision tree regressor . html具有相同的超参数:

Scikit 学习实现;图片来自作者的笔记本

正如我们所看到的,分割值和特征都是相同的。

最后要注意的是,节点的预测是节点中 Y 个观测值的平均值。在分类器决策树中,预测是节点中具有最多观察值的类。

在上面生长的树木中,如果我们遵循规则:

**weight ≤2764.5** **→****horsepower ≤70.5**

我们会得到这个节点有 69 个观察值。如果我们对这些变量的 mpg 变量进行平均,我们会得到这些汽车的平均 mpg 值为 33.68。这是对所有未来汽车的预测,它们遵循上述路径。

总之,我强烈推荐仔细阅读 NodeRegressor 类的代码,因为我试图非常明确地编写它。通过从我的 GitHub 存储库中加载数据并一行一行地检查代码,您很快就会发现回归树是一种非常简单和直观的算法。

在我的 git 存储库中随意派生、复制、克隆代码并创建 pull 请求,这在第一段中提到过。

如果任何概念仍然不清楚,请随时留下评论。

感谢阅读和快乐编码!

重磨咖啡:粒子分布

原文:https://towardsdatascience.com/regrinding-coffee-particle-distributions-add74209992e?source=collection_archive---------40-----------------------

咖啡数据科学

近距离观察

几个月前,詹姆斯·霍夫曼开始了一场关于重新研磨咖啡豆的讨论。目的是将咖啡研磨成粗粒,然后再将粗粒研磨得更细。我最初是这样做的,但我没看出有什么不同。然而,最初的挑战是有一个手动研磨机,这使得这种类型的实验具有挑战性。

在查看颗粒分布的同时,我想再看一看,以了解重新研磨是否改变了颗粒分布以及如何改变的。

我开始用一个小生磨床,我看着设置 50 的粗设置。这已经有一个非常双峰分布。然后,我把这些研磨设置为 15。设定 13 是我打得很好的地方,但我在设定 15 有更多的数据。

如果我们取出设置为 50 的分布并放大,我们可以看到两个分布非常相似。

所以我看了看负 0,这是逆时针转动表盘,直到它在 0 标记。这是超过设置 50 的刻度盘的半圈。分布情况大不相同,但罚款金额仍然很高。

然后我重新研磨这些研磨物进行比较。此外,当机器运行时,我在慢慢倒入研磨料的同时观察了二次研磨(二次研磨 15 运行)。这似乎比其他任何事情都更有影响。从负 0 开始也会导致较小的偏移。

我把这四张图分开来仔细看看。在这里,我们将初始粗糙度作为变量,开始时较粗会导致细微的偏移。

我可以比较研磨机在倒入研磨料和不倒入研磨料时的运转情况。随着研磨机的运行,有一个明显的转变为粗糙。

如果重新研磨对味道有什么影响,我很想知道。我总是很好奇,一个更好的研磨机是否会有两个阶段,粗磨和精磨。然而,这也是两个毛刺套件,因此会贵得多。

我不相信有趣的变化是由重新研磨引起的,而是研磨粉或咖啡豆是如何倒入机器的。

如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。也可以关注我中。

我的进一步阅读:

浓缩咖啡系列文章

工作和学校故事集

个人故事和关注点

乐高故事启动页面

摄影启动页面

使用图像处理测量咖啡研磨颗粒分布

使用筛子改进咖啡研磨测量

使用先进的图像处理技术测量咖啡渣中的粉末

使用自适应阈值改善咖啡粉分布

拍摄咖啡渣进行研磨分布分析

用例子清楚地解释正则表达式

原文:https://towardsdatascience.com/regular-expressions-clearly-explained-with-examples-822d76b037b4?source=collection_archive---------0-----------------------

任何数据分析师在处理字符串时都应该具备的最被低估的技能之一

布莱恩·纳塔内尔在 Unsplash 上的照片

这篇博文的诞生源于我自己最长时间以来对正则表达式(regex)这个话题的沮丧和回避。

几个月来,我一直在推迟学习 regex 的想法,因为说实话,它们看起来非常可怕,尤其是当你第一次遇到它们的时候。我的意思是,一串字符绑在一起,背后似乎没有任何逻辑——没有人有时间!

直到最近我在工作中接到一个任务,涉及到检索一个字符串的元素,我才最终对正则表达式的能力有了一个认识。事实证明,一旦你理解了基本面,它其实并没有那么糟糕。

因此,在本文中,我将解释什么是正则表达式,介绍一些基本的正则表达式字符,最重要的是,使用几个实际的例子演示如何使用 R 编程语言执行正则表达式。具体来说,我们将讨论在正则表达式中捕获组的概念。

如果你是一个 Python 爱好者,你可以在我的 GitHub 这里找到 Python 版本的代码。

什么是正则表达式?

正则表达式不是一个库,也不是一种编程语言。相反,正则表达式是在任何给定文本(字符串)中指定搜索模式的字符序列。

文本可以包含从字母到数字,从空格到特殊字符的任何内容。只要字符串遵循某种模式,regex 就足够健壮,能够捕获这种模式并返回字符串的特定部分。

您需要知道的基本正则表达式字符

现在,在我们进入本质之前,我认为我们首先回顾正则表达式的一些基础知识是至关重要的。

本文后面的例子将建立在这里举例说明的一些主要概念之上,即:字符、分组和量词。

特性

  • 转义符:\
  • 任意字符:.
  • 数字:\d
  • 不是数字:\D
  • 字符:\w
  • 非文字字符:\W
  • 空白:\s
  • 不是空白:\S
  • 字界:\b
  • 不是单词边界:\B
  • 字符串的开头:^
  • 字符串结尾:$

分组

  • 匹配括号中的字符:[ ]
  • 匹配不在括号中的字符:[^ ]
  • 非此即彼:|
  • 捕获组:( )

量词

  • 0 或更多:*
  • 1 或更多:+
  • 0 或 1: ?
  • 准确的字符数:{ }
  • 字符数范围:{Minimum, Maximum}

正则表达式示例

如果上面的正则表达式字符对您来说没有太大意义,也不要担心——它们只是作为我们将要经历的例子的参考。

在这一节中,我们将关注 6 个不同的例子,希望它们能加深你对正则表达式的理解。实际上,我们将着眼于:

  • 2 个数字示例(电话号码和日期)
  • 2 个字母示例(名称和 URL)
  • 2 个数字和字母的例子(电子邮件地址和地址)

在我们开始之前,确保您已经将 tidyverse 包安装并加载到您的工作环境中。

# Install tidyverse package 
install.packages("tidyverse")# Load tidyverse package 
library(tidyverse)

1.电话号码

假设我们有一个名为 phone 的数据帧,包含如下电话号码列表:

我们希望将这些电话号码分成 3 个独立的部分:区号(前 3 位)、交换机(后 3 位)和线路号码(后 4 位)。

正如我们所看到的,这里的数字模式并不总是一致的,也就是说,它们有不一致的括号、连字符和空格。但是,在正则表达式的帮助下,我们可以轻松地捕获数字组。

首先,我们需要定义一个正则表达式模式。

phone_pattern = ".?(\\d{3}).*(\\d{3}).*(\\d{4})"

我们到底该如何解读这一点?好吧,让我们一步一步来,从左到右:

  • .? 0 或 1 个字符,用于说明可选的左括号
  • (\\d{3}) 3 位数字符(第一个捕获组,即前 3 位数)
  • .* 0 个或多个字符,说明可选的右括号、连字符和空格字符
  • (\\d{3}) 3 位数字符(第二个捕获组,即接下来的 3 位数)
  • .* 0 个或更多的字符来说明可选的连字符和空格字符
  • (\\d{4}) 4 位字符(第三个捕获组,即最后 4 位)

然后,我们可以使用 str_match 函数,使用我们定义的 regex 模式检索捕获组,并将它们放入数据帧中的各个列。

phone$area_code = str_match(phone$original_number, phone_pattern)[, 2]
phone$exchange = str_match(phone$original_number, phone_pattern)[, 3]
phone$line_number = str_match(phone$original_number, phone_pattern)[, 4]

2.日期

假设我们有另一个名为 date 的数据帧,它由分隔符不一致的日期组成,我们希望提取日、月和年。

使用与我们刚刚看到的电话号码非常相似的方法,我们需要首先定义一个 regex 模式,然后将该模式与原始日期列匹配,最后为每个捕获组创建一个新列。

首先,定义日期的正则表达式模式。

date_pattern = "(\\d{2}).(\\d{2}).(\\d{4})"

下面是代码解释:

  • (\\d{2}) 2 位数字符(第一个捕获组即日)
  • .单个字符代表所有特殊字符
  • (\\d{2}) 2 位数字符(第二个捕获组即月)
  • .单个字符代表所有特殊字符
  • (\\d{4}) 4 位数字符(第三个捕获组即年份)

现在,我们可以匹配模式并为日、月和年创建单独的列。

date$day = str_match(date$original_date, date_pattern)[, 2]
date$month = str_match(date$original_date, date_pattern)[, 3]
date$year = str_match(date$original_date, date_pattern)[, 4]

3.名称

到目前为止,我们已经研究了两个只包含数字和特殊字符的字符串示例。现在让我们学习如何捕捉单词和字母。

这里我有一个叫做名字的数据框,里面有人们的姓氏、头衔和名字。

让我们把它们分开,这样它们每个都有自己的专栏。

name_pattern = "(\\w+),\\s(Mr|Ms|Mrs|Dr).?\\s(\\w+)"

这是这样的解释:

  • (\\w+) 1 个或多个单词字符(第一个捕获组,即姓氏)
  • ,逗号字符
  • \\s一个空白字符
  • (Mr|Ms|Mrs|Dr)先生、女士、夫人或博士(第二个捕获组,即头衔)
  • .?标题后 0 或 1 个句号字符
  • \\s单个空白字符
  • (\\w+)一个或多个单词字符(第三个捕获组,即名字)

现在,将它们放入单独的列中。

names$family_name = str_match(names$full_name, name_pattern)[, 2]
names$title = str_match(names$full_name, name_pattern)[, 3]
names$given_name = str_match(names$full_name, name_pattern)[, 4]

4.资源定位符

让我们来看另一个包含单词和字母的字符串的例子。

现在,您应该已经熟悉了这个过程。

url_pattern = "(https?)://(www)?.?(\\w+).(\\w+)/?(\\w+)?"

解释是:

  • (https?) http 或 https(第一个捕获组,即模式)
  • ://特定的特殊字符串
  • (www)?可选 www(第二个捕获组,即子域)
  • .? 0 或 1 个句号字符
  • (\\w+)一个或多个单词字符(第三捕获组,即二级域名)
  • .单个句号字符
  • (\\w+)一个或多个单词字符(第四捕获组,即顶级域名)
  • /? 0 或 1 个反斜杠字符
  • (\\w+)?可选 1 个或多个单词字符(第五捕获组即子目录)

将捕获组分成单独的列,我们得到:

url$schema = str_match(url$full_url, url_pattern)[, 2]
url$subdomain = str_match(url$full_url, url_pattern)[, 3]
url$second_level_domain = str_match(url$full_url, url_pattern)[, 4]
url$top_level_domain = str_match(url$full_url, url_pattern)[, 5]
url$subdirectory = str_match(url$full_url, url_pattern)[, 6]

5.电子邮件地址

利用到目前为止我们已经获得的关于正则表达式的知识,现在让我们看两个包含字母和数字的最后的字符串例子。

假设我们在一个名为 email 的数据框架中有一个电子邮件列表:

现在,生成一个正则表达式模式来匹配用户名、域名和域。

email_pattern = "([a-zA-Z0-9\\_\\-\\.]+)@([a-zA-Z]+).(.+)"

让我们仔细看看这个正则表达式,并解读它的含义。

  • ([a-zA-Z0-9\\_\\-\\.]+)一个或多个小写字母、大写字母、数字和特殊字符,包括下划线、连字符和句号(第一个捕获组,即用户名)
  • @ at 符号
  • ([a-zA-Z]+) 1 个或多个小写和大写字母(第二个捕获组即域名)
  • .单个句号字符
  • (.+) 1 个或多个字符(第三捕获组即域)

然后,我们将这个正则表达式模式应用于电子邮件列表:

email$username = str_match(email$full_email, email_pattern)[, 2]
email$domain_name = str_match(email$full_email, email_pattern)[, 3]
email$domain = str_match(email$full_email, email_pattern)[, 4]

6.地址

当然,我把最好的例子留到了最后。这个例子和我在工作中所做的是一样的。

在努力重建那件作品的过程中,我用假想的地址制作了一个名为 address 的数据帧。目标是检索门牌号、街道名称、郊区、州和邮政编码。

像往常一样,我们需要首先定义一个正则表达式模式。

address_pattern = "(\\d*)\\s?(.+),\\s(.+)\\s([A-Z]{2,3})\\s(\\d{4})"

以及代码解释:

  • (\\d*) 0 个或更多数字字符,因为有些地址没有门牌号(第一个捕获组,即门牌号)
  • \\s? 0 或 1 个空白字符
  • (.+) 1 个或多个字符(第二个捕获组,即街道名称)
  • ,逗号
  • \\s单个空白字符
  • (.+) 1 个或多个字符(第三捕获组,即郊区)
  • \\s单个空白字符
  • ([A-Z]{2,3}) 2 或 3 个大写字母(第四个捕获组,即状态)
  • \\s单个空白字符
  • (\\d{4}) 4 位字符(第五个捕获组,即邮政编码)

将此模式与地址列表进行匹配,我们得到:

address$house_number = str_match(address$full_address, address_pattern)[, 2]
address$street_name = str_match(address$full_address, address_pattern)[, 3]
address$suburb = str_match(address$full_address, address_pattern)[, 4]
address$state = str_match(address$full_address, address_pattern)[, 5]
address$postcode = str_match(address$full_address, address_pattern)[, 6]

我希望,通过我在这篇博文中展示的 6 个例子,您不仅对正则表达式的工作原理有了更好的理解,更重要的是,对它们在匹配复杂字符串模式时的灵活性有了更好的理解。

如果你渴望成为一名数据分析师,或者只是渴望提高你的数据辩论技能,我强烈建议将 regex 添加到你的工具包中。为了进一步练习,我建议查看一下 regex101 或 regexone 。

非常感谢你的阅读和快乐学习!

更多关于 R 的文章

https://medium.com/swlh/customer-segmentation-using-k-means-clustering-in-r-a74d512a4cfa

正则化和线性回归

原文:https://towardsdatascience.com/regularization-and-linear-regression-bcaeba547c46?source=collection_archive---------15-----------------------

我们纠正过度拟合的那个

正则化和线性回归|照片由 Jr Korpa 拍摄

这篇文章是我关于线性回归和 bootstrap 和贝叶斯统计系列的延续。

之前我详细地讨论了线性回归,现在我将继续这个话题。正如我先前暗示的,我将提出正规化的话题。正则化的作用是简化模型。你想要一个更简单的模型的原因是,通常,一个更简单的 will 模型会在大多数测试中表现得更好。

名词(noun 的缩写)用训练数据作为测试数据是不合适的。当您使用测试数据(模型尚未看到的数据)测试您的模型时,您只能清楚地看到正则化的全部效果。

我的写作主要来自对机器学习效果较差的事物的统计建模。为了简单起见,下面的许多例子打破了这个规则,我们使用训练数据而不是测试数据来评估我们的模型。

因此,我们模型中的决定系数并不能真正代表模型的有效性,相反,它们只是相关系数

寻找最佳模型

模型选择——有时也称为特征选择,是指我们使用不同的特征/属性/可用数据列为现有数据选择最佳模型。为模型选择不同的特征实质上创建了不同的模型。

哪一个都可以。选择我们的模型后,我们将调整它。正则化就是简化它:正则化=简化

我们之所以想要一个更简单的模型,是因为我们希望避免一种叫做过拟合或过参数化的现象。过度拟合或过度参数化是一种经常发生的现象,尤其是当您有很多参数时。

有多种正则化技术可供使用,包括:

  • 逐步地
  • 里脊回归
  • 套索
  • PCR — PCA +回归(主成分分析和正则化)

PCR 是用于正则化的最重要的技术。

模型适合度

概括地说,一个好的模型“很好地符合数据”然而,这是有细微差别的,因为不同的型号有不同的用途。如果你有一个数据集,你想知道这个数据集是否可以建模;如果你可以用一个数学公式来解释模型,你所拥有的数据——这就是你所想做的——那么使用相同的数据并使用该数据评估你的模型,或者使用我们的训练数据评估我们的模型,这是非常好的和合法的。

然而,如果你希望能够做任何类型的预测,并确定这个模型在预测某事方面有多强大——这通常是人们希望对模型做的事情:他们希望实际利用它——那么我们需要用测试数据测试模型。

好的模型需要很好的概括。例如,它需要从您的训练数据推广到您的测试数据。考虑下面的例子:

  • 过度拟合:模型对训练数据非常有效,但不能推广到现实世界
  • 欠拟合:模型过于简单,笼统但没有意义。更少的差异,但更多的偏向错误的答案

为什么我把最左边的图像称为非常适合,也就是刚刚好的那个?因为当我们看这个的时候,黑线似乎很好的解释了那些蓝点。如果我们要做一个简单的线性回归,我们可能会得到类似中间图像上的线。我们称之为不合身。我们的模型会缺少方差,我们的模型会有太多的偏差;这就是我们要说的。

现在,在右边,我们过度拟合。在这里,我们在模型中有太多的方差。所以模型方差非常明显,因为它上下波动,到处跳跃。这种差异有助于拟合这个特定的数据集。但是我们称之为过度拟合,我们说它对数据拟合得太好了。你可能会问,怎么/为什么这是一件坏事?嗯,这是一件坏事,因为如果我加入测试数据,测试数据可能会比其他两个模型更不适合。因为测试数据不会正好在这条黑线的最大值或最小值的这些波峰或波谷处。

过度拟合

过度参数化通常会导致过度拟合,并且还会带来其他一些问题:

  • 参数数量>数据维度
  • 输入要素的线性相关性
  • 在训练数据集上效果很好,但在真实世界(测试数据)中性能很差(如果不是很糟糕的话)

我们所说的输入要素的线性相关性存在一个问题。因此,如果您的一个输入要素可以用其他输入要素以线性方式解释,那么我们称之为线性依赖。也就是说,如果你可以线性组合(也就是说,通过加法、减法和乘法,添加一些列来得出或确定另一个已经存在的列),那么这意味着你有一个线性依赖;这意味着其中一列,一列或多列只是其他列的组合。当你计算和确定模型时,这会导致问题。

防止过度拟合

防止过度拟合的工具是正则化。正规化有不同的方式。从早期开始,逐步回归就是正则化的一种形式。什么是逐步回归?逐步删除/添加功能。有两种味道,向前和向后。向前逐步回归差不多是这样工作的:你选择你认为是你最好的特征。假设你有输入特征 x_1,x_2,x_3,x_4 等等;你选择一个你认为最好的(有很多种方法可以选择。)然后你来,你会创建一个线性回归,基于那个特征,假设是 x_2,现在是输入,y 是输出。然后你选择下一个最好的,比如说 x_4,你用 x_2 来测试那个。如果模型变得更好,有所改善,那么你保留第二个 x 值。如果它不好,如果模型性能没有提高,那么你就不要保留它,然后你又回到只有 x_2 的状态。然后你尝试这些功能中的另一个。这被称为向前逐步回归。

向后逐步回归是更常见的方法。你得到所有的值,比如 x_1,x_2,x_3,x_4,然后从这四个输入中建立一个线性回归,得到一个解释 y 的函数;计算/预测 y,然后你去掉其中一列,看看模型是否有所改进。通常情况下,你会不太适应。但是您的模型可能会基于标准而改进,这些标准不仅仅是纯粹的拟合,它们可能会更好,因为您还会对参数进行惩罚,或者使用一种称为信息标准的测量方法,该方法要求您需要从任何给定的输入参数中获取如此多的信息。继续移除特征,直到模型性能停止提高。这被称为向后逐步回归。

其他的正则化技术是你为你的每一列设置一个惩罚;与该列相关的系数的罚值。这些是其他正规化技术。

测量模型

我们如何度量模型?有 Akaike 信息标准(AIC):

AIC 公式

  • k:估计参数的数量
  • 可能性

这是确定一个模型是否有所改进的最受欢迎的标准。随着可比模型的 AIC 变得越来越小,你说 AIC 在改进,你的模型也在改进。因此,通过添加更多的列,或者删除更多的列,如果你的 AIC 有所改善,那么你就知道你在向正确的方向前进。如果你的 AIC 没有提高,那意味着没有下降,那么你知道你在错误的方向上迈出了一步;这意味着最后一次添加列或最后一次删除列应该被撤消,您可以返回并尝试其他操作。

另一个模型度量是贝叶斯信息标准(BIC),但它很少在这种情况下使用,它非常类似于 AIC:

BIC 公制

  • k:估计参数的数量
  • 可能性
  • n:记录的测量次数

不管你选择的标准是什么,你的最佳拟合模型是具有最小 AIC 或 BIC 的模型。

费用

当你确定任何函数时,任何机器学习算法都有一个所谓的代价函数。这是一个非常基本的概念。一个成本函数基本上是说,“你的模型有多差?”

我之前讨论过线性回归,差是残差的平方。提醒一下,残差是输出 y 的预测值和实际值之间的差值;然后你把它平方,然后你把所有的平方相加。这是一个成本函数,但以前没有明确地调用过。

RSS 也被称为 SSR,但它们并不总是一回事!

名词(noun 的缩写)b:在某些文献中,SSR 不是残差平方和,而是平方和回归——一个不同的量。在本文的其余部分,我坚持使用 RSS 来避免对上述数量的混淆。

我们在这里和那里改变了一些细节,我们可以用案例的数量来划分,这意味着正在使用的值的数量—数据点的数量。我们可以在这里放入一个常数,但总的来说,我们希望将它最小化:

最小化成本函数

这可能是最常见的成本函数之一。还有其他的,特别是逻辑回归。然而,在很大程度上,这是我们的主要成本函数——它可能会以一些小的方式进行修改。

成本函数计算惩罚。所以这里的惩罚就是这些平方和:计算事情有多糟糕。对于给定的训练建模数据集,这是一个损失。我们的想法是最小化这个损失或者最小化这个成本。所以你需要注意一些事情:

  • 模型需要相互比较
  • 成本函数决定方差
  • 根据数据集、目标变量和目的(要回答的业务问题),选择正确的成本函数是至关重要的

相似的模型必须相互比较,这一点很重要。因此,你无法比较均方根误差(RMSE),它与上面的成本函数密切相关,你无法真正比较一个模型与下一个模型,除非它们非常非常相似,除非有很多相同的东西。

如果你只有两个不同来源的不同模型,做不同的事情,有不同的目的,比较 RMSE 没有任何意义。这些均方根误差,或这些成本,只有在与一个非常相似的模型的另一个成本相比较时,才是可以理解的。

方差和偏差

一个好的成本函数试图平衡方差和偏差,使其成为方差和偏差的最佳组合。

什么是方差和偏差?

展示方差与偏差

在右边的过度拟合例子中,有很强的方差,方差太大;虽然在中心欠拟合的例子中有很大的偏差,但是偏差太大了。

回归成本

拟合度是最小化成本函数的另一种说法。到目前为止,我们一直在最小化残差平方和。非常容易混淆的是,这可以被称为 SSR 和 RSS,我将使用 RSS。总成本的另一个组成部分是我们系数的权重,只是系数的另一种说法。

提醒你一下,这个特殊情况下的系数,假设我们有两列:x _ 1 列和 x_2 列。好吧,那我们就有:

b 和 c 是系数

由于系数是我们回归的一部分(我们之前把它们当作 1),我们应该把它们包括在总成本函数中。实际上,我们把它们的平方加进去,我们会因为有这个系数而得到另一个惩罚。

如果这个系数变为零,基本上就等于零了。这样的话,我们实际上可以去掉列。或者去掉这个惩罚,或者这个惩罚的一部分。很自然,如果我们去掉所有的列,就什么都没有了,那么我们只有 y 本身的平均值。

所以我们需要,当我们添加一列时,我们会说,“好的,我们可以添加一列,我们可以使用这些信息。但我们将接受因添加该栏而受到的处罚。”不管那列得到什么系数,都将是我们的惩罚。这意味着如果它是一个相当大的,重要的列,它将有一个大的系数,所以它最好是值得的。因为这个系数也将被算作这个所谓的惩罚的一部分,它将增加总成本。通过把这个放入我们的计算中,我们实际上不再寻找最佳拟合,T4,我们寻找最佳拟合和较小系数之间的平衡。一个非常非常小的系数就是零。负系数也被认为是大的。

总成本 =拟合度+系数大小:

总成本,而不是“总成本减去数量成本”

  • 当拟合度(RSS)较小时=良好拟合
  • 当系数的幅度(误差平方和)很小时=良好拟合

如果你知道你的线性代数,这个重量(w)实际上叫做 L2 范数。对这个故事来说,重要的是把它想成系数的平方,有时叫做权重的平方,但它确实是一个系数。

在之前的一篇文章中,我们引入了误差项ε。m 和 b 项是系数(斜率和 y 截距)

ε表示误差,I 表示它是哪个数据点(第一、第二等。)

看起来我们只有一个输入列,也就是 x 和一个系数 b,但实际上,取决于我们如何看待这个问题,x 可以是列的一个完整向量,也可以是系数的一个完整向量。

特征选择

选择作为目标的良好预测器的特征子集的过程被称为特征选择。如上所述,它是型号选择的另一个术语。两者相同的原因是,如果你有不同的功能集,你实际上有不同的模型。

它有助于:

  • 控制模型的复杂性
  • 提高模型性能
  • 提高泛化能力
  • 加速模型学习而不降低精度

我个人的偏好是尽可能多地使用所有的特性,然后一旦我有了所有的特性,我就可以精简并只找到重要的特性。所以我想尽可能多地去掉一些特性。我为什么要这么做?我想要一个更简单的模型。为什么我想要一个更简单的模型?因为一个简单的模型概括得更好。泛化是什么意思?这意味着你可以在现实世界中使用它。该模型不仅解释了训练数据,而且也有更好的机会解释我们以前没有见过的数据。首先是您的测试数据,然后是您从未见过的数据,即您的运营数据。

如前所述,逐步回归是一个重要的正则化工具。向后是最常见的,从所有特征开始,删除解释最小差异的列,直到模型性能达到峰值。向前是从平均值开始添加特性,直到模型性能达到峰值。然后是“两者”——在每一步检查是添加一个特性还是删除一个特性。说你往前走,你加了一个栏目。当你倒退的时候。现在,从这一点来看,最好删除的列可能不是您刚刚添加的列。可能是另一个。其原因是,列之间存在复杂的相互作用,这通常是无法预测的。这可能不是我正在做的线性回归的情况。但是在其他类型的回归中:树和神经网络,情况肯定是这样的。

我敢说每个人都使用逐步回归。这并不意味着你只用逐步回归。其他类型的正则化方法包括脊,套索,以及称为弹性网的东西,这是脊和套索的组合。然后在此基础上,我做逐步回归。所以我认为结合使用这些是一个人进步的典型方式,是一个人工作得最好的方式。

逐步回归:Python 示例

接下来的部分使用高尔顿的身高数据集,逐步回归的一个例子,我在我的 bootstrap 重采样文章中使用了相同的数据集。

# importsimport pandas as pd
import numpy as np
import statsmodels.formula.api as sm
import seaborn as sns
import matplotlib.pyplot as plt%matplotlib inline# load data# I previously saved the Galton data as data
family_data = pd.read_csv(data, delimiter=’\t’)# column labels
family_data.columns = [“family”,”father”,”mother”,”gender”,”childHeight”, “kids”]# check data is well-imported
family_data.head()

前 5 行,数据看起来不错

# check data types
family_data.dtypes

# subset the data with a Boolean flag, capture male children
isMale = family_data.loc[:,”gender”] == “M”# create just the male children df
male_only = family_data[isMale].copy()
male_only.head()

在此 df 中仅捕获(M)名男性儿童

print(‘Number of rows: {}, Number of Males: {}’.format(len(family_data), len(male_only)))

数据很平衡!M = F

# create new df for new feature
male_df = male_only.copy()# add in squares of mother and father heights
male_df[‘father_sqr’] = male_df[‘father’] **2
male_df[‘mother_sqr’] = male_df[‘mother’] **2# drop columns family, gender, kids
Drop = [“family”, “gender”, “kids”]
for x in Drop:
 male_df = male_df.drop(x, axis=1)

# reset index
male_df=male_df.reset_index(drop=True)
male_df.head()

成功添加新功能

名词(noun 的缩写)childHeight 特性只是针对儿子的,我们之前只是复制并捕捉了儿子。我们稍后用女儿做 PCR 分析。

接下来,我们将 z-score 标准化数据。我们为什么要标准化数据?较大的值,比如 father_sqr,会有较小的系数,较小的值会有较大的系数。这是不“公平”的。为了公平起见,我们对数据进行了缩放,使它们彼此相等,其中一种方法是 z 分数标准化,即从身高中减去儿童身高的平均值,然后除以儿童身高的标准差。这是 z 值:

# scale all columns but the individual height (childHeight)
# normalization function for a column in a pandas df
def scale(col):
 mean_col = np.mean(col)
 sd_col = np.std(col)
 std = (col — mean_col) / sd_col
 return std# add scaled x to data frame.
male_df[‘father’] = scale(male_df[‘father’])
male_df[‘mother’] = scale(male_df[‘mother’])
male_df[‘father_sqr’] = scale(male_df[‘father_sqr’])
male_df[‘mother_sqr’] = scale(male_df[‘mother_sqr’])
male_df.head()

值根据列平均值的标准差进行缩放

注意第一个父亲的身高是四。四个什么?离平均值四个标准差,这已经很高了。

计算具有所有功能的模型

下面的代码使用所有可用的功能计算所有男孩的身高。到目前为止,这是我展示的全部内容,但今天的特别之处在于,现在我们可以找到可以扔掉的功能。为什么我们要找到可以抛弃的特性?我们想让模型更简单。为什么我们要让模型更简单?事实证明,越简单的模型越好。

名词(noun 的缩写)b:ols_model中的+ 1表示将会有一个偏移。sklearn这是自动的。总有截距,偏移量的另一个术语。因为一切都是按比例缩放的,所以在这种情况下,截距就是孩子的平均身高。

ols_model = sm.ols(formula = ‘childHeight ~ father + mother + father_sqr + mother_sqr + 1’, data=male_df)results = ols_model.fit()
n_points = male_df.shape[0]
y_output = male_df[‘childHeight’].values.reshape(n_points, 1)
print(‘Intercept, Slopes : \n{}’.format(results.params))# print  hypothesis test stats
print(‘Intercept t-value, Slope t-values: \n{}’.format(results.tvalues))
print(‘\nHypothesis test summary for each coefficient if they differ from zero:’)
print(results.pvalues)print(‘\nSSE, SST, SSR, and RMSE:’)
mean_y = np.mean(y_output)
sst = np.sum((y_output — mean_y)**2)
sse = sst — results.ssr
print(‘SSE: {}’.format(sse))
print(‘SST: {}’.format(sst))
print(‘SSR: {}’.format(results.ssr))
print(‘RMSE: {}’.format(np.sqrt(results.mse_resid))) # ESHx# print summary stats
print(results.summary())# plot a histogram of the residuals
sns.distplot(results.resid, hist=True)
plt.xlabel(‘Residual’)
plt.ylabel(‘Frequency’)
plt.title(‘Residual Histogram’)
plt.show()

基线模型

残差呈正态分布

现在我们有了一个基线模型,我们能用正则化技术改进它吗?

应用逐步回归

在前面,我定义了用于定义模型拟合度的指标:AIC

AIC:模型对数似然调整的参数数量

  • k:估计参数的数量
  • 可能性

我们可以使用后向特征选择对该函数应用逐步回归:

def backward_selected(data, response):
 “””Linear model designed by backward selection. Feature selection based on AICParameters:
 — — — — — -
 data : pandas dfwith all possible predictors and responseresponse: string, name of response column in dataReturns:
 — — — — 
 model: an “optimal” fitted statsmodels linear model
 with an intercept
 selected by backward selection
 “””
 # begin with all factors and intercept
 possible_factors = set(data.columns)
 possible_factors.remove(response)
 formula = “{} ~ {} + 1”.format(response, ‘ + ‘.join(possible_factors))
 best_aic = sm.ols(formula, data).fit().aic
 current_aic = best_aic

 # create a non-empty set of columns that will be labeled as “to remove and try”
 to_try_remove = possible_factors

 # check which features remain
 while to_try_remove and current_aic == best_aic:
 aic_candidates = []
 for candidate in to_try_remove:

 columns = possible_factors — set([candidate])
 # removing the candidate column
 formula = “{} ~ {} + 1”.format(response, ‘ + ‘.join(columns))
 # print AIC
 aic = sm.ols(formula, data).fit().aic

 # append tuple of the form (aic, response)
 aic_candidates.append((aic, candidate))

 # sort all the pairs by the first entry of tuple 
 aic_candidates.sort()
 # change sort/pop order!
 best_new_aic, best_candidate = aic_candidates.pop(0)

 # check if we have something better:
 if best_new_aic < current_aic:
 # Remove the best candidate’s name from possible_factors

 possible_factors.remove(best_candidate)
 current_aic = best_new_aic

 # repeat the process with all the remaining candidate columns# final formula
 formula = “{} ~ {} + 1”.format(response, ‘ + ‘.join(possible_factors))
 # model object
 model = sm.ols(formula, data).fit()
 return modelbackwards_model = backward_selected(male_df, ‘childHeight’)print(backwards_model.model.formula)print(‘Adjusted R-Squared: {}’.format(backwards_model.rsquared_adj))
print(‘AIC: {}’.format(backwards_model.aic))

向后逐步回归,AIC 较好

反向选择的公式childHeight ~ father + mother + mother_sqr + 1现在被输入到标准sm模板中:

ols_model_forward = sm.ols(formula = ‘childHeight ~ father + mother + mother_sqr + 1’, data=male_df)
results = ols_model_forward.fit()
n_points = male_df.shape[0]
y_output = male_df[‘childHeight’].values.reshape(n_points, 1)# slope (m) and y-intercept (b)
print(‘Intercept, Slopes : \n{}’.format(results.params))# t-values (hypothesis test statistics) 
print(‘Intercept t-value, Slope t-values: \n{}’.format(results.tvalues))# p-values for above t-value statistics
print(‘\nHypothesis test summary for each coefficient if they differ from zero:’)
print(results.pvalues)print(‘\nSSE, SST, SSR, and RMSE:’)
mean_y = np.mean(y_output)
sst = np.sum((y_output — mean_y)**2)
sse = sst — results.ssr
print(‘SSE: {}’.format(sse))
print(‘SST: {}’.format(sst))
print(‘SSR: {}’.format(results.ssr))
print(‘RMSE: {}’.format(np.sqrt(results.mse_resid))) # ESHx
print(results.summary())# plot a histogram of the residuals:
sns.distplot(results.resid, hist=True)
plt.xlabel(‘Residual’)
plt.ylabel(‘Frequency’)
plt.title(‘Residual Histogram’)
plt.show()

部分斜率是系数

其余的更好

残差呈正态分布

名词(noun 的缩写)逐步回归似乎是一种简单的特征选择方法;请注意逐步回归的伸缩性不好。与任何多重比较方法一样,逐步回归法有很高的假阳性结果概率。在这种情况下,由于低 p 值或 AIC,不应删除的要素可能不会被删除。

主成分回归:Python 示例

为了简洁起见,我省略了 PCR 背后的大量线性代数。诀窍就是做 PCA,主成分分析。PCA 将帮助你确定哪个主成分是最好的。这是通过创建初始变量的线性组合并创建一个解释最大方差的新组合来实现的。然后另一个解释了稍微少一点的差异,等等。

从模型的角度来看,我们已经把它正规化了。没有任何信息被丢弃,每台电脑都包含所有功能的线性组合。但是模型看到了新的特性,使得确定哪些特性应该被丢弃变得容易。

名词(noun 的缩写)b:这个靠的是幕后的 SVD。

PCA 中的变换创建线性独立的特征,它们之间具有最佳的独立性。具有少量差异的 PC 可能对我们的模型不重要,因此我们通过移除差异极小的特征来简化我们的模型。

首先,在将 PCR 应用于高尔顿数据集之前,我将对其进行演示:

from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScalernp.random.seed(21)# right-multiply a 2x2 matrix to a 2x100 matrix to transform the points
X = np.dot(np.random.randn(100, 2), np.random.rand(2, 2))
# scale data
X = StandardScaler().fit_transform(X)plt.scatter(X[:, 0], X[:, 1])
plt.grid()
plt.xlabel(‘x’)
plt.ylabel(‘y’)
plt.title(‘Generated x-y data with dependence’)
plt.show()

现在,我们将创建变量的线性组合来创建 PC1 和 PC2,即主成分 1 和主成分 2。PC1 的方差最大。请记住,在 PCA 中,输入特征与创建的主成分之间存在 1:1 的映射

pca = PCA(n_components=2)
pca_result = pca.fit_transform(X)
pca_df = pd.DataFrame(data = pca_result , columns = [‘pc1’, ‘pc2’])print(pca_df.head())
print(pca_df.shape)

100 双

绘制输出

plt.axis(‘equal’) 
plt.scatter(pca_df.loc[:, ‘pc1’], pca_df.loc[:, ‘pc2’])
plt.ylabel(‘Component 2’)
plt.xlabel(‘Component 1’)
plt.title(‘Two Dimensional PCA’)
plt.grid()
plt.show()

数据在我们新的坐标系中重新绘制。

PC1 和 PC2 是原始要素的线性组合,在我们的新坐标系(PC1 和 PC2)中附加了一些权重,而不是 x 和 y。

print(pca.explained_variance_)
print(pca.components_)

这些电脑解释了 100%的差异!

exp_var = pca.explained_variance_
components = pca.components_# plot vector
# = plot scale * component direction * component length
v1 = 2 * components[0] * np.sqrt(exp_var[0])
v2 = 2 * components[1] * np.sqrt(exp_var[1])
c = (0, 0) # Center is at 0,0 because of our standardizationplt.scatter(X[:, 0], X[:, 1], alpha=0.5)
plt.annotate(‘’, c + v1, c, arrowprops={‘arrowstyle’: ‘->’, ‘shrinkA’: 0,
 ‘shrinkB’: 0, ‘linewidth’: 3})
plt.annotate(‘’, c + v2, c, arrowprops={‘arrowstyle’: ‘->’, ‘shrinkA’: 0,
 ‘shrinkB’: 0, ‘linewidth’: 3})
plt.axis(‘equal’)
plt.grid()
plt.title(‘F-vector functions (Principal Components)’)
plt.xlabel(‘x’)
plt.ylabel(‘y’)
plt.show()

矢量在新的坐标系中确实是垂直的

根据 PCR,我们创建了两个线性独立的特征,如上图所示。因为 PC1 比 PC2 解释了更多的差异,这个模型可以通过去掉 PC2 来简化。

这可以扩展到更高维的数据集。通过计算主成分,我们消除了数据中的多重共线性问题。

现在,让我们在高尔顿数据集上使用 PCR 看看女儿。由于 df 中有四个特征,因此 PCA 之后将有四个主要成分。

# subset the data with a Boolean flag to capture daughters
isFemale = family_data.loc[:,”gender”] == “F”# create just the female df
female_data = family_data[isFemale].copy()# create new df for new feature set
female_df = female_data.copy()# feature engineer squares of mother and father heights
female_df[‘father_sqr’] = female_df[‘father’] **2
female_df[‘mother_sqr’] = female_df[‘mother’] **2# drop columns for family, gender, kids
Drop= [“family”, “gender”, “kids”]
for x in Drop:
 female_df = female_df.drop(x, axis=1)

# reset the index
female_df=female_df.reset_index(drop=True)# add scaled x to df
female_df[‘father’] = scale(female_df[‘father’])
female_df[‘mother’] = scale(female_df[‘mother’])
female_df[‘father_sqr’] = scale(female_df[‘father_sqr’])
female_df[‘mother_sqr’] = scale(female_df[‘mother_sqr’])
female_df.head()

仅子数据集看起来很好

# calculate all the principal components (4)
X = female_df[[‘father’, ‘mother’, ‘father_sqr’, ‘mother_sqr’]].values
y = female_df[‘childHeight’]
pca = PCA()
pca_result = pca.fit_transform(X)
pca_df = pd.DataFrame(data = pca_result, columns=[‘pc1’, ‘pc2’, ‘pc3’, ‘pc4’])# our data projected onto the four principal components.
print(pca_df.head())
print(pca_df.shape)pca_df[‘childHeight’] = female_df[‘childHeight’]

方差图:

plt.plot([i + 1 for i in range(4)], pca.explained_variance_)
plt.title(‘Scree plot for PCA decoposition’)
plt.xlabel(‘Component’)
plt.ylabel(‘Variance explained’)
plt.show()

前两个部分解释了最大的差异

pca.explained_variance_

解释了四个 PCs 的差异

既然我们已经将所有的数据都投影到了 4 个主成分上,我们可以看看解释的方差了。前两个 PC 解释了最大的方差,因此我们可能会删除 PC3 和 PC4,因为它们解释的方差如此之低。

pcr_model = sm.ols(formula = ‘childHeight ~ pc1 + pc2 + pc3 + pc4’, data=pca_df)results = pcr_model.fit()
n_points = pca_df.shape[0]
y_output = pca_df[‘childHeight’].values.reshape(n_points, 1)
print(results.summary())# plot a histogram of the residuals
sns.distplot(results.resid, hist=True)
plt.xlabel(‘Residual’)
plt.ylabel(‘Frequency’)
plt.title(‘Residual Histogram’)
plt.show()

在这里,你可以看到,最后两个主要组成部分并不需要一个重要的拟合。你也可能已经注意到,从每个组件的“解释方差”中-以及最后两个组件如何解释近 4 个数量级的方差。

现在,我们可以只使用两个分量来尝试 PCA 回归;解释大部分差异的因素包括:

pcr_model = sm.ols(formula = ‘childHeight ~ pc1 + pc2’, data=pca_df)results = pcr_model.fit()
n_points = pca_df.shape[0]
y_output = pca_df[‘childHeight’].values.reshape(n_points, 1)
print(results.summary())# plot a histogram of the residuals
sns.distplot(results.resid, hist=True)
plt.xlabel(‘Residual’)
plt.ylabel(‘Frequency’)
plt.title(‘Residual Histogram’)
plt.show()

请注意,所有模型系数现在都很重要

PC3 和 PC4 的 p 值较大,因此不显著。此外,他们各自的 95%置信区间跨越零。因此,他们没有帮助模型解释女儿的身高,我们怀疑他们解释的低方差。如果我们没有执行 PCR,我们很可能不会丢弃任何初始特征(父、母、父 _sqr、母 _sqr)。

如果您注意到 R 在改进的模型上并没有更好,请记住我们犯了一个非犹太行为:我们在训练数据上测试了我们的模型。一个大禁忌。几乎可以肯定的是,如果我们把以前看不到的测试数据输入到最后一个模型,只有两台电脑的模型,R 就会增加。这是因为这是一个更简单的模型,不容易过度拟合。如果数据是训练/调整/测试分割的,那么模型的改进将是可论证的。

降维起到了预期的效果!调整后的 R 和 AIC 是该模型的最低值。正则化帮助我们创建了更好、更简单、更通用的模型。

如果你想知道为什么父母的身高不是孩子身高的好指标,这就是“回归平均值”这个短语的来源。与 18 世纪的普遍看法相反,高尔顿的家庭分析表明,高个子父母的孩子往往身高回归到人口平均水平。

接下来,我将用 Ridge、LASSO 和 Elasticnet regressions 来讨论正则化!

在 Linkedin 上找到我

物理学家兼数据科学家——可用于新机遇| SaaS |体育|初创企业|扩大规模

基于变分原理的正则化核回归

原文:https://towardsdatascience.com/regularized-kernel-regression-from-a-variational-principle-d2b0c03eb919?source=collection_archive---------31-----------------------

H 如何通过变分法推导和推广Nadaraya–Watson 估计量

约翰·安维克在 Unsplash 上的照片

K Nadaraya–Watson 估计量就是这样一种方法。它通常来源于计算因变量的条件期望,基于对自变量分布和因变量与自变量联合分布的核密度估计。

在本文中,我将介绍推导 Nadaraya–Watson 估计量的另一种基本原理(我也简称为“核回归”)。这个基本原理激发了一个变分原理,它将允许我们制定一个可以被称为“正则化核回归”的修改。

许多回归技术可以通过最小化相对于二次损失函数的经验风险,或者相对于数据点 **( x ₁,y₁)…**的残差平方和rf来导出:

关于未知回归函数 f 最小化这个表达式的问题是不适定的,需要对 f 施加进一步的假设。在参数建模中,我们将 f 限制在某个假设空间,以便问题变得适定。比如在线性回归中,我们把 f 限制在仿射线性函数的空间,f(x)=m⋅x+c。* 确定斜率m 和截距c 使得上述残差平方和最小化,将产生最佳拟合线。***

现在,让我们对上面的公式进行一些数学变换,然后一步一步地解释:

第一个等式只是将正方形展开,并将平方后的 y 作为自己的和。根据第二个等式,我们认识到 y 的平方和对我们稍后想要应用的最小化过程没有贡献,因为它不依赖于我们想要最小化的函数 f 。因此,我们可以称之为“const”再也不用担心了。**

下一步至关重要。我们可以通过狄拉克δ函数在单个固定位置计算 f ,如下所示:

这将允许我们将总损失R[f]写成一个积分,并且经验风险最小化变得可以从变分法的标准工具中获得。

“delta”的正确定义需要对函数分析有所了解,更准确地说是分布理论或“广义函数”。然而,对于所有实际的意图和目的,你可以把狄拉克δ函数想象成一个以原点为中心的非常窄的峰值。事实上,我们可以通过对初始δ函数的限制来近似狄拉克δ函数,初始δ函数的度量在原点附近变得越来越集中。

一如既往,最受欢迎的选择是高斯分布:

该函数族在消失带宽的极限内朝着狄拉克δ函数收敛 h > 0

最后,用上述近似代替狄拉克δ函数后,我们给积分下的公式起了自己的名字: L 代表 拉格朗日 。(这个特定的拉格朗日量实际上并不依赖于导数’,但是我们稍后会需要这个普遍性。)**

寻找一个像这样的泛函的驻点——即一个被表示为拉格朗日积分的泛函——在数学和理论物理中有许多应用。例如,基于最小作用原理,经典力学可以重新表述为拉格朗日力学。另一个应用是描述光线的路径,它遵循费马原理,也叫最短时间原理。

因此,这个问题有一个众所周知的通用解决方案。重复一下,我们感兴趣的是最小化以下形式的泛函:

函数 f 是该泛函的驻点当且仅当它满足以下欧拉-拉格朗日方程*:***

对于我们到目前为止导出的拉格朗日量,最小二乘 R [ f ] “涂抹掉”的和,右手边消失是因为导数 f ' 中没有依赖项。

在这种情况下,欧拉-拉格朗日方程可以简单地用代数方法求解f*(x):*

这正是 Nadaraya 和 Watson 提出的核回归公式。

因此,到目前为止,我们可以对我们的方法感到相当有信心,因为我们能够推导出一种经过验证的回归技术。现在是时候改变一下了,对我们的变分原理进行一些修改。例如,我们可以添加一个将正则化模型的项,并惩罚任何整体导数大的解:

其中 λ > 0 为正则化参数。我们还引入了常数因子“ 1 / N ”,因此我们实际上是将平均经验风险与正则化项进行对比。计算相应的欧拉-拉格朗日方程是一项简单的任务:

当然,对于 λ = 0 ,该公式简化为传统的核回归。这是一个二阶线性微分方程,一旦给定边界条件或初始条件,它就有唯一的解。在 R 中, deSolvebvpSolve 软件包可用于数值求解常微分方程。

L et 的模型一些真实数据。下图显示了由伯克利地球(http://berkeleyearth.org/data/)提供的 1850 年至 2019 年间地球平均全球温度的时间序列:

作者图片

虚线是带宽 h = 10.0 的常规核回归,实线是带宽和正则化参数 λ = 0.5 相同的正则化核回归的结果。欧拉-拉格朗日方程是通过强加一个边界值问题来求解的,该问题的最早/最晚温度由前五年/后五年的温度中值给出。

这里提出的正则化核回归有一些明显的缺点,例如:

  • 需要指定边界条件,这看起来像是一个临时的过程,
  • 试图应用初始条件似乎不实际,并导致无意义的爆破解,
  • 数值会发生不稳定,特别是对于小数值*。***

**然而,这款车型似乎也有一些令人满意的特点。例如,对于不同的带宽选择,它看起来相当健壮。下图显示了相同的数据和回归函数,但带宽 h = 1.0 😗*

作者图片

虽然传统的核回归似乎在很大程度上过度拟合数据,但正则化版本“保持在正确的方向上”。

这个模型的另一个特点是:它可能更擅长输入缺失数据。这是一个图表,展示了同样的回归技术,但是 1920 年到 1970 年之间的所有数据点都被排除在分析之外:

作者图片

我们可以利用这种稳健性来应对缺失数据,并尝试将时间序列外推至未来情景。虽然传统的核回归对于插值肯定是有用的,但是我们可以预见这项技术在这项任务中会惨败。

然而,正则化的内核回归可能会成功,因为添加了“惯性” λ 下面的图表显示了预测假设 先验2040 年全球平均气温将分别上升到 15.2 摄氏度、15.8 摄氏度和 16.4 摄氏度的未来情景:

作者图片

对于每次外推,使用了相同的模型超参数 h = 10.0λ = 0.5 。尽管在拟合最终模型之前已经预先给出,但 2040 年 15.8℃的选择并不是特别的:外推至 2040 年 15.8℃实际上是最好的预测,因为有了这个参数,(传统的)残差平方和将最小化,这可以通过简单的网格搜索来验证。

结论

核回归是一种技术,可以从最小化关于二次损失函数的经验风险的“平滑”或“模糊”版本中获得。这种方法产生了一种变分原理,例如通过增加正则化项可以扩展这种原理。

对所得模型的一些实验显示了一些令人满意的性质,它可能在预测时间序列中找到有用的应用。

参考

**** **https://en.wikipedia.org/wiki/Dirac_delta_function https://en.wikipedia.org/wiki/Lagrangian_mechanics https://cran.r-project.org/web/packages/bvpSolve/index.html http://berkeleyearth.org/data/ **

使用岭回归克服普通最小二乘法的缺点(OLS)

原文:https://towardsdatascience.com/regularized-linear-regression-models-44572e79a1b5?source=collection_archive---------18-----------------------

正则化线性回归模型

OLS 的弱点,获得岭模型估计量的优化,以及使用 Numpy 在 Python 中的实现

模型系数值随着正则化罚值的增大而变化(图片由作者提供)

再次向你问好,希望欢迎回来👋

在这一深入探究正则化线性回归建模技术的三个部分的最后一部分中,涵盖了几个主题:线性回归模型下的响应和特征变量之间的方程、误差平方和(SSE)损失函数、O 正则最小二乘 (OLS)模型,以及找到 OLS 模型估计器的必要优化步骤,该估计器可以根据样本数据进行训练,以产生给定新特征数据的响应预测。

接下来,在这一部分中,将讨论 OLS 的缺点、潜在的补救措施以及 岭回归 模型。

与上一部分类似,这里建议的所有实现都在一个葡萄酒质量预测数据集上进行了验证,这个数据集和其他相关文件可以在项目的存储库中找到,这里是http://github.com/wyattowalsh/regularized-linear-regression-deep-dive

OLS 的缺点

与大多数事情一样,建模时必须做出权衡。其中一个主要的权衡就是偏差-方差的权衡。任何模型的误差都可以分解成两个部分:偏差和方差。偏差可以被认为是建模算法中隐含的误差,而方差可以被认为是从训练数据集的特质差异中得出的误差。一个好的模型应该具有最小的总误差,因此偏差和方差都应该最小。然而,有一个权衡要考虑,因为增加偏差往往会减少方差。****

对于 OLS 模型,高方差是一个问题。由于 SSE 正在优化,该模型倾向于拟合异常数据点,因为它们会由于损失函数中的平方项而产生更高的误差值。通过拟合这些异常点,OLS 模型随后可以基于仅存在于训练数据中的建模模式(特殊的异常点)进行预测,这些模式并不代表整个群体。这种现象被称为过拟合,在推广到新的预测时会导致预测模型准确度低。

由于 OLS 是一个低偏差模型,它非常适合通过添加偏差来降低其方差,这可能会导致更高的整体预测能力。增加偏差的一种方法是通过收缩*,使模型系数估计值偏向零。这种收缩可以通过将正则化罚值添加到损失函数来实现,该损失函数将独特形式的收缩应用于整体系数估计。***

在下一节中,我将介绍 岭回归 正则化,方法是向 OLS 损失函数添加一个调谐参数(λ)系数控制的 L₂ 罚值。

请务必查看本系列 的下一部分和最后一部分,了解其他两种形式的正则化线性回归,即套索弹性网。

里脊回归

这种形式的回归也被称为 吉洪诺夫正则化 ,并修改 OLS 损失函数(第一部分:等式。#7)增加了一个 L₂ 罚值和一个相关联的调谐参数 λ 。这个损失函数可以用向量符号描述为:

方程式#1

类似于 OLS 的情况,该损失函数然后可以公式化为最小二乘优化问题,以找到最小化损失函数的模型系数的估计值,如下所示:

等式#2

就像 OLS 的情况一样,添加了 1/(2n)项,以便简单地求解梯度,并允许目标函数通过大数定律收敛到模型误差的期望值。

该问题也是无约束的,并且通过将损失函数(目标)的梯度设置为零并求解所得方程,可以找到岭估计量的闭合形式解。这将产生以下估计结果:

方程式#3

这个估计量也应该是唯一的。在这种情况下,关联的 黑森 矩阵是:

方程式#4

事实证明,这个矩阵可以表示为正定的*:***

方程式#5

因此,由于岭损失函数的相关 Hessian 矩阵是正定的,所以该函数是强凸的,这意味着岭估计(等式)。#3)是岭回归 问题的唯一全局极小点。

注意到该问题可以通过数据扩充重新表述为 OLS 问题,可以简化估计器的实现。这将采取以下形式:

方程式#6

因此,通过利用上述数据扩充,与本系列最后一部分中的等式 9 相同的结果可用于求解系数估计值。该结果在此重现:

方程式#7

使用 Python 和 NumPy 实现估计器

类似于 OLS 的情况,矩阵求逆不能很好地缩放,因此使用了 LAPACK _gesv 例程的 NumPy 函数solve来寻找最小二乘解。该函数在 A 为平方且满秩(线性无关列)的情况下解方程。然而,在 A 不是满秩的情况下,则应该使用函数lstsq,该函数利用 xGELSD 例程,从而找到 A 的奇异值分解。

具有可选截距项的 岭回归 的一个可能的 Python 实现是:

结论

感谢阅读 正则化线性回归模型 的第二部分!🙌

如果您还没有,请务必查看 第一部分

继续到 第三部分 来学习一下的套索 ,最后两个规则化线性回归技巧!**

此处 为创建这一系列职位所利用的不同来源。

如果你愿意,请留下评论!我一直在努力改进我的帖子(逻辑上,语法上,或者其他方面),并且很乐意讨论任何相关的东西!👍

线性回归建模和普通最小二乘法的基础(OLS)

原文:https://towardsdatascience.com/regularized-linear-regression-models-57bbdce90a8c?source=collection_archive---------7-----------------------

正则化线性回归模型

线性回归的上下文、获得 OLS 模型估计量的优化以及使用 Numpy 的 Python 实现

模型系数值随着正则化罚值的增大而变化(图片由作者提供)

嘿👋

欢迎来到关于正则化线性回归建模的三部分深入探讨的第一部分——用于监督学习任务的一些最流行的算法

在进入方程式和代码之前,让我们先讨论一下本系列将涵盖的内容。

第一部分将包括关于回归的介绍性讨论、对线性回归建模的解释以及对*(OLS)模型的介绍(通过使用 NumPy 在 Python 中实现发现,使用应用优化理论推导出模型估计器)。*

OLS 模式的弊端和一些可能的补救措施将在第二部分中讨论。一个这样的补救方法, 岭回归 ,将在这里给出解释,包括其模型估计器的推导和 Python 中的 NumPy 实现。

***第三部分 以对的解释结束这一系列的帖子,剩下的规则化线性模型: ***【套索】弹性网 。求解这些模型比以前的情况更复杂,因为需要离散优化技术。这种复杂性的原因,路径坐标下降算法及其基于 NumPy 的 Python 实现,以及一些总结性的评论都在本文中给出。

模型和包含的实现在葡萄酒质量预测数据集上进行了测试,其代码和结果可以在项目资源库 这里 中查看

介绍

管理决策、组织效率和创收都是可以通过利用基于数据的洞察力来改进的领域。目前,随着技术可及性进一步延伸,市场竞争优势更加难以获得,人们更容易找到这些见解。一个寻求在收集的数据样本中实现价值的领域是预测分析。通过利用数学/统计技术和编程,从业者能够识别数据中的模式,从而产生有价值的见解。

回归 是预测分析中的一种技术,用于在给定一个或多个相关特征变量的情况下预测连续响应变量的值。这类算法通过对样本数据集进行训练来学习输入(特征)变量和输出(响应)变量之间的关系,从而完成这项任务。如何学习这些关系,并进一步用于预测,因算法而异。从业者面临着回归建模算法的选择,然而,线性回归模型由于其易于应用和高度可解释性而倾向于在过程的早期探索。

线性回归建模

线性回归模型通过对样本数据拟合线性函数来学习输入-输出关系。这可以用数学方法形式化为:

方程式#1

因此,响应被建模为输入变量乘以线性系数的加权和,其中包含误差项。在涉及优化的未来步骤中,使用向量符号将被证明是有用的。线性建模方程可以这样表示:

等式#2

上面等式中需要注意的一个重要方面是有一列 1 被附加到设计矩阵中。这使得系数向量的第一个系数可以用作截距项。如果在此之后不寻求截距,此栏可以省略。

因此,模型训练的目标是找到系数向量的估计值,【β̂】,*** ,其然后可以与上述方程一起使用,以在给定新特征数据的情况下对响应进行预测。这可以通过将最优化理论应用于上述模型方程以导出模型系数估计器的方程来实现,该方程最小化通过对样本数据进行训练而发现的模型误差的概念。***

最小化模型误差的概念

为了考虑如何最小化模型误差,必须首先考虑模型误差。单次预测的预测误差可以表示为:

方程式#3

因此,在向量表示法中,所有预测的总模型误差可以被发现为:

方程式#4

然而,对于寻找最小整体模型误差的用途,上面的l₂范数不是一个好的目标函数。这是因为负误差和正误差将相互抵消,因此最小化将发现目标值为零,即使实际上模型误差要高得多。**

这种有符号误差消除问题可以通过对模型的预测误差求平方来解决,产生误差平方和(SSE)项:

方程式#5

这个术语可以用向量符号表示为:

方程式#6

正如在未来的优化应用中所看到的,该函数更适合用作损失函数,一个最小化的函数,它恰当地模拟了给定技术的误差。除了正则化线性模型,许多不同的模型都使用 SSE 误差项作为它们各自损失函数中的一项。

普通最小二乘法

既然已经介绍了线性建模和误差,我们可以继续讨论最简单的线性回归模型,(OLS)。在这种情况下,简单的 SSE 误差项就是模型的损失函数,可以表示为:**

方程式#7

使用这个损失函数,现在可以将问题形式化为最小二乘优化问题。该问题用于导出模型参数的估计值,使结果的实际值和预测值之间的 SSE 最小化,并形式化为:

等式 8

添加 1/(2n)项是为了简化梯度的求解,并允许目标函数通过大数定律收敛到模型误差的期望值。****

借助于问题的无约束性质,通过设置损失函数(目标)的梯度等于零并求解系数向量的合成方程, β̂.,可以获得 OLS 估计器的封闭形式的解 这产生了以下估计量:

方程式#9

然而,这可能不是唯一的最优估计,因此它的唯一性应该得到证明。要做到这一点,只需表明损失函数(等式)。#8)是凸的,因为凸函数的任何局部最优性也是全局最优性,因此是唯一的。

一种可能的方法是通过二阶凸性条件来说明这一点,二阶凸性条件规定,如果一个函数是连续的、两次可微的,并且有一个相关的半正定的 Hessian 矩阵,则该函数是凸的。由于其二次性质,OLS 损失函数(方程式。#8)既是连续的又是两次可微的,满足前两个条件。

为了建立最后一个条件,OLS·海森矩阵被发现为:

方程式#10

此外,这个 Hessian 可以表示为正半定的:

方程式#11

因此,通过凸性的二阶条件,OLS 损失函数(等式)。#8)是凸的,因此上面找到的估计量(等式#9)是 OLS 问题的 唯一的 全局最小化器。

使用 Python 和 NumPy 实现估计器

使用矩阵求逆来求解 OLS 估计器不能很好地缩放,因此使用 LAPACK _gesv 例程的 NumPy 函数solve被用来寻找最小二乘解。该函数在 A 为平方且满秩(线性无关列)的情况下解方程。然而,在 A 不是满秩的情况下,则应该使用函数lstsq ,该函数利用 xGELSD 例程,从而找到 A 的奇异值分解。

在 OLS 的 Python 中,带有可选截取项的一个可能的实现是:

使用 Python 和 NumPy 实现 OLS

结论

**希望你喜欢 正则化线性回归模型的第一部分。👍

**请务必查看第二部分以了解为什么 OLS 模型有时无法准确执行,以及如何使用 岭回归 进行帮助,并阅读第三部分 以了解两个更正规化的模型: 【套索】弹性网。

此处 为创建这一系列帖子所利用的不同来源。

如果你愿意,请留下评论!我一直在努力改进我的帖子(逻辑上,语法上,或者其他方面),并且很乐意讨论任何相关的东西!😊

使用 NumPy 在 Python 中实现套索和弹性网的路径坐标下降

原文:https://towardsdatascience.com/regularized-linear-regression-models-dcf5aa662ab9?source=collection_archive---------13-----------------------

正则化线性回归模型

解释解决一些最流行的监督学习算法

模型系数值随着正则化罚值的增大而变化(图片由作者提供)

你好。👋

欢迎来到深入探讨正则化线性回归建模的三部分的最后一部分!在第一部分 中,建立了线性模型,并推导出 OLS,显示了如何求解模型系数,以预测给定新特征数据的响应。接下来,在第二部分中,讨论了 OLS 模型的过拟合 问题,并且提出了 岭回归 作为通过正则化来帮助减少过拟合的技术。建筑脱离了与山脊回归相同的概念, 套索弹性网 现已呈现。本系列以对所介绍技术的用例的一般考虑作为结束。

模型和包含的实现在葡萄酒质量预测数据集上进行了测试,其代码和结果可以在项目资源库 这里 中查看。

回归的套索

套索,或 最小绝对收缩和选择操作符 ,包括添加一个 L₁ 惩罚到 OLS 损失函数(第一部分 : Eq。#7),对于相关调谐参数 λ 的足够大的值,使选择性模型参数为零。换句话说, 套索 执行自动特征选择产生模型系数的向量,其稀疏度(为零的元素的数量)根据调谐参数的大小而变化。

拉索 损失函数可以形式化为:

方程式#1

与前面的情况类似,截距项可以通过设计矩阵的数据扩充来包含,设计矩阵有一列 1 s。此外,将问题公式化为最小二乘优化问题产生:

等式#2

然而,与以前的情况不同,这个问题没有封闭形式的解决方案。这是由于添加了 L₁ 罚值使得函数由于非平滑的绝对分量而不再是连续可微的。为了解决这个问题,需要应用离散优化技术来搜索最优解。

为此存在许多算法,例如 LARS (最小角度回归)和 向前逐步回归 ,然而,在本工作中利用了 路径坐标下降 算法。简而言之,该算法在保持所有其他参数不变的情况下,一次优化一个参数。

路径坐标下降

在开始算法之前,所有特征应该被标准化为具有零平均值和一的方差。从那里, p+1 长度系数向量被初始化为零。然后,对所有系数进行循环,直到达到收敛,此时系数值稳定,且变化不超过某个容差。在每个周期内,对于每个系数,计算更新,随后对其应用软阈值算子。

最简单形式的 坐标下降更新使用设计矩阵中所有其他特征的部分残差,为每个系数计算简单(单一变量,而非多元回归)最小二乘系数值。在这种情况下,部分残差被发现为:

方程式#3

因此,特定系数值的估计值可由下式得出:

方程式#4

现在,由调谐参数决定的惩罚通过软阈值算子被包括在模型中。这表示为:

方程式#5

幼稚更新 可以用来提高效率。这些更新可通过以下途径找到:

方程式#6

其中 rᵢ 是所有样本的当前模型残差, n

当样本的数量远大于特征的数量(n>>p)时,可以通过使用协方差更新来进一步提高效率。对于这些更新, 的第一项天真的更新了上面的 等式(Eq。#6)被替换为:

方程式#7

利用 热启动 也能带来效率提升。使用 热启动 ,拟合一系列模型——调谐参数值从最大调谐参数值下降到最小调谐参数值,最小调谐参数值是最大值的某个小因子(千分之一)——将每次迭代的系数初始化为最后一次迭代的解。很多情况下,对于一些小的 λ,拟合这条路径的模型其实比单个模型更快。 因此路径可以表示为:

等式 8

其中 𝜖 通常为 0.001,在对数标度上有 100 个间隔值。

此外,可以通过找到将使所有模型系数的估计为零的最小值来找到开始路径的调谐参数的最大值。这是因为任何高于该值的值都会导致系数向量的总体稀疏性。路径(起点)的最大值可由下式得出:

方程式#9

通过提供开始搜索最优性的起点,热启动可以多次加速收敛,并且还将路径方式置于 路径方式 坐标下降 算法中。

使用 NumPy 在 Python 中实现套索

【套索】 (带有调整收敛容差、路径长度和返回路径的选项)实现 路径坐标下降 t 的一种可能方式是:

使用 Lasso 的 Numpy 进行回归的 Python 实现

弹性网

在这种形式的正则化线性回归中,OLS 损失函数通过向损失函数添加*【l₁】【l₂】*罚值来改变,其中调谐参数控制正则化的强度和不同罚值之间的平衡。该损失函数可以形式化为:

方程式#10

既有*【l₁】惩罚,弹性网** 用于在脊回归和套索之间提供一种折衷,使系数趋向于零,并选择性地趋向于零。这里, α 可以认为是决定 L₁ 罚加比例的参数,而 λ 可以认为是要应用的正则化强度。***

弹性网 损失函数也用于形式化一个最小二乘优化问题:

方程式#11

与套索类似,通过设计矩阵扩充包括截距,为数学完整性添加 1/(2n)项,并且由于罚项导致闭合形式的解不存在,因此实施 路径坐标下降 来求解。**

就像在 套索中一样,路径坐标下降 用于求解模型系数估计值,但是需要对更新方程进行更改,以考虑双重惩罚。在这种情况下,在软阈值化之后获得的第 j 个系数值现在被发现为:

方程式#12

软阈值操作符与 套索 更新中应用的操作符相同(等式。#5):

方程式#13

此外,幼稚更新协方差更新可以与热启动一起使用。对于热启动,最大 λ 值可以计算为:

方程式#14

使用 NumPy 在 Python 中实现

实现 路径式坐标下降 的一种可能方法是求解 弹性网 (带有调整收敛容差、路径长度和返回路径的选项):

使用 NumPy 在 Python 中回归的弹性网

结论

在整个系列中,不同的正则化形式的线性回归已经被检验为克服过度拟合 普通最小二乘 模型的训练数据的趋势的工具。 弹性网——由于其在正则化种类之间的平衡——倾向于最健壮地帮助过拟合问题,然而, 【套索】 由于其自动特征选择在许多情况下肯定可以证明是有帮助的。 岭回归 也是一个很好的工具,可以用来确保减少可能的模型过拟合,因为它将模型系数收缩到零,从而减少模型方差。

该系列的标题图像很好地展示了 岭回归套索 之间的区别,可以看出,对于 岭回归 情况,左边的模型系数都向零收缩,而对于 套索 情况,右边的系数以选择性的顺序变为零。

恭喜你!🎉🎊🥳

你坚持到了这个系列的结尾!

我希望这些帖子内容丰富,有助于您了解更多关于 正则化线性回归 以及求解相关模型所需的必要的优化

这里 为不同的来源,利用创造这一系列的职位。

如果你刚刚开始这个系列,请确保查看第一部分和第二部分

如果你愿意,请留下评论!我一直在努力改进我的帖子(逻辑上,语法上,或者其他方面),并且很乐意讨论任何相关的东西!👋

以 Pix2Pix 为助手,用彩色再现历史

原文:https://towardsdatascience.com/reimagining-history-in-color-with-pix2pix-as-an-assistant-23d6e4a64e75?source=collection_archive---------29-----------------------

实践教程

创意和人工智能合作:一个训练有素的 pix2pix 模型如何帮助着色

使用 pix2pix 帮助着色史密森尼国家肖像画廊的历史肖像的结果集锦

人类天生就有创造力。新的工具释放了潜伏在尚未开发的创客浪潮中的创造力,他们找到了意想不到的方式来展示他们的艺术能力。根据 Pfeiffer 咨询公司的一项研究,即使是有经验的艺术家也认为新技术是创造力的推动者。这项研究发现,74%的艺术过程,从设想一件艺术作品到实现它,由许多艺术家不介意自动化的繁琐步骤组成。这就是一些艺术家认为机器学习可以有所帮助的地方。作为一名业余艺术家,我正在接受机器学习方面的研究生培训,我一直在探索这种可能性。在本文中,我将描述我如何训练 pix2pix 模型成为黑白照片着色的有用助手。该模型的输出提供了一个非常有用的基础,减少了我需要在 Adobe Photoshop 中对史密森尼国家肖像画廊的历史肖像进行着色和恢复的手工工作。虽然我不能声称彩色是历史上准确的,我很高兴与结果!

如果你是彩色化的新手,看看一些艺术家通过这些 Reddit 线程这里和这里制作的迷人作品。除了在原始黑白照片上绘制彩色图层的手工流程之外,彩色艺术家还根据相似的彩色图像和历史记录进行广泛的研究,以尽可能准确地添加颜色。这是一个劳动密集型过程,但产生的结果往往是惊人的。例如,Mario Unger 花了 3000 个小时来修复和着色一组历史照片,但是看看他惊人的结果。我发现,当历史看起来更像我们生活的世界时,我们可以更好地与历史联系起来。

最近,史密森尼博物馆以开放数据的形式分享了数百万份资产,鼓励公众投入其中进行创作。史密森尼开放存取馆包括了许多来自国家肖像画廊的历史肖像。给这些肖像上色不是很好吗?由于我没有彩色艺术家的精细技能,我决定训练一个 pix2pix 模型来帮助我。

为着色训练 pix2pix】

pix2pix 模型是用于图像到图像翻译的条件生成对抗网络(cGAN)。对于彩色化,模型被训练成将黑白(b&w)图像翻译成彩色图像。该模型由两个子组件组成:(1)一个被训练为向黑白图像添加颜色的生成器,以及(2)一个学习区分原始彩色图像和由生成器创建的彩色图像的鉴别器。彩色图像数据集用于训练数据。从每个图像中去除颜色以创建黑白和彩色图像对。生成器学习给 b&w 图像着色,以便它们欺骗鉴别器认为它们是原件,同时尽可能地再现图像的原始颜色。鉴别器学习识别图像何时被发生器着色。通过这个竞争过程,模型学习在多次迭代中改进它的着色。自从论文的作者发布他们的代码以来,许多研究人员和艺术家已经以创造性的方式使用 pix2pix。此处的社区贡献部分显示了示例。

用于着色的 pix2pix,高级概念(图片由作者提供)

现在有了模型本身的一些背景,让我们想象 pix2pix 是一个彩色化艺术家的学徒。对于学习如何将黑白图像转换成彩色图像的学徒来说,我们必须向其展示许多例子——越生动越好。我使用了 COCO 数据集 (35k 张图片)的一个子集,只过滤最丰富多彩的图片。在对这个数据集进行训练后,pix2pix 了解了一些世界的颜色,但它仍然不知道如何很好地给人的图像着色。因为目标是给肖像上色,它的下一步训练需要集中在这个任务上。我用我的个人照片和从 Unsplash 选择的照片创建了一个更小的高质量的人物图像数据集(2k 图像)。然后,因为旧的历史图像通常比现代数码照片模糊,我也给训练图像添加了随机模糊。现在,pix2pix 将建立在它已经从 COCO 那里学到的基础上,并进一步训练将稍微模糊的人的照片翻译成彩色图像。

模型结果

经过培训,我的 pix2pix 助手学会了很有帮助。使用史密森尼肖像作为我的测试数据,pix2pix 很好地为黑白图像添加了颜色,尽管我的训练数据存在明显的偏差。由于我的训练集很小,该模型更容易过度拟合——也就是说,该模型学习了特定于训练数据的模式,并错误地将这些特定内容应用于看不见的图像。例如,我的许多训练图像包含涂口红的人,结果,一些历史人物在输出中被涂上了口红😬。改进模型的一个简单方法是使用更大、更多样化的训练集。

总的来说,结果当然不是完美的,但仍然提供了一个很好的起点。虽然我没有彩色艺术家的精湛技巧,但 pix2pix 帮助我尝试了彩色化。

用 Photoshop 做最后的润色

使用模型输出,用 Photoshop 添加最后的润色是非常容易的。

突出显示工具的 Photoshop 截图(作者提供的图片)

因为基色已经在图像中了,所以我使用了带有“3x3 样本平均值”的滴管工具来从图像的特定部分拾取颜色。选择颜色后,我使用“颜色”模式下的画笔工具,在模型遗漏或颜色不正确的区域涂色。该画笔将仅调整图像中的颜色,而不改变像素强度值(即高光/阴影等)。不会被打扰)。我使用的具体设置在上图中突出显示。然后,为了消除原始照片的损坏,如划痕或钢笔痕迹,我使用标准笔刷设置来覆盖它。我花了不到 5 分钟编辑每张图片。继续滚动查看最终结果!

最终结果

按照顺序,该图显示了原始图像、经过训练的 pix2pix 模型输出和 Photoshop 编辑后的最终图像(作者提供的图)(文章末尾的图像参考链接)

感谢阅读,我希望你喜欢最后的结果。像 pix2pix 这样的模型有可能使着色这样的任务变得更容易实现。熟练的着色艺术家也可以发现这种模型有助于减少完成成品所需的繁琐工作。他们可以利用节省下来的时间专注于更新颜色,以获得历史准确性或艺术天赋。非常感谢史密森尼博物馆发布了如此令人难以置信的数据集,供研究人员探索历史——在这种情况下,用颜色重新想象历史🎨

思考的食粮——机器学习和循环中的创意还能产生什么?💡

图片参考(原件链接):

1.阿米莉亚·埃尔哈特

2.布克·华盛顿

3.海伦·凯勒

4.多洛里斯·德尔·利奥

5.莉莉·格林

6.泰迪·罗斯福

7.阿尔伯特·爱因斯坦

8.爱丽丝·罗斯福·朗沃斯

9.林肯——内战照片

小鼠的强化学习

原文:https://towardsdatascience.com/reinforcement-learning-3f87a0290ba2?source=collection_archive---------28-----------------------

没有动物的生活不值得 living❤️去过

相似的动物和计算机是如何学习的?

强化学习

强化学习(RL)是一种深度学习,在过去几年中受到了很多关注。这对于我们想要训练 AI 掌握某些我们不完全理解的技能的情况是有用的。

RL 有一个代理人,他在不确定的环境中采取行动,目标是最大化累积报酬**。代理从它的错误中学习,并且它的决策算法改进。**

当我读到 RL 概念时,我认为它与动物如何学习和做决定非常相似。

小鼠迷宫实验:

下面是马蒂亚斯·万德尔非常著名的视频。他正在制作不同类型的迷宫,并在老鼠探索不同的迷宫时观察它们。老鼠正在复杂的动态环境中学习智能行为。

RL 与动物学习

下面是老鼠迷宫实验和 RL 概念之间的类比。

作者图片

**代理:**决定采取什么动作的组件。在这种情况下,我们的代理是鼠标。

**环境:**代理运行的物理世界。迷宫就是环境。

**动作:**代理的方法,允许它交互和改变它的环境,从而在状态之间转移。在这种情况下,鼠标向右、向左、向前和向后的运动就是动作。

状态:任务的当前世界或环境的表示。鼠标相对于迷宫的位置状态

**奖励:**环境的反馈。在这种情况下,吃花生或葵花籽就是奖励。这是让老鼠继续探索的即时奖励。

惩罚:负面奖励,对不良行为的惩罚。当鼠标卡在一个窄孔中时,被扣分。

奖励最大化:在 RL 中,代理人的座右铭是获得最高的奖励。我们的老鼠的目标是最大化奖励,这意味着吃了最多的食物。

目标:让老鼠解决迷宫。如果它快速解决迷宫,它导航更快,在更短的时间内获得更多的花生。

**利用:**当老鼠学会如何在迷宫中导航时,它可以利用,即使没有效率也要继续走同样的路。

**探索:**我们的老鼠认为尝试许多不同的行动是值得的,以便能够最大化未来的回报。所以,这只老鼠的贪婪和好奇让它不断尝试其他路径,寻找最短的一条。

**策略:**鼠标获得最多奖励的策略。正如你在这个视频中看到的,它有一个贪婪的方法。老鼠是既贪婪又好奇的哺乳动物,所以它们更喜欢探索和利用。

勘探开发权衡

代理不能同时探索和利用。如果它利用,它会选择最安全的方式,以便能够在它开始探索的路径上吃到最多的食物,或者探索,找到能够在迷宫中导航的其他路径。

代理人应该如何平衡探索和开发?该策略为代理提供了一些关于如何平衡这两者的信息。

在这个实验中,老鼠的探索-利用困境根据食物的数量和迷宫的难度而变化。

在 6:00 的视频中,马蒂亚斯放了很多花生和种子,老鼠停止探索,它只是继续吃。

**价值函数:**几乎所有的强化学习算法都是基于估计价值函数——状态的函数,估计智能体处于给定状态有多好。

它是从状态 1 开始的预期收益,遵循预定的政策。

在这种情况下,值函数是鼠标到目前为止所做工作的总结。

马尔可夫决策过程:

马尔可夫决策过程(MDP)是一个描述强化学习环境的数学框架。下图显示了 MDP 中代理与环境的交互:

作者图片

结论

强化学习正在以与人类和动物开发智能相同的方式产生人工智能。RL 是直观的,因为我们在世界上行动,我们得到奖励或惩罚,我们学习。这是一种强化学习。如果我们可以教会机器如何像人类和/或动物一样学习和行动,RL 可能是人工智能的未来。

强化学习和 Q 学习 Python 中“出租车问题”的一个例子

原文:https://towardsdatascience.com/reinforcement-learning-and-q-learning-an-example-of-the-taxi-problem-in-python-d8fd258d6d45?source=collection_archive---------9-----------------------

Americana 使用 Canva 制作的图像

新手入门指南

数据科学家的典型路径是从探索性数据分析到机器学习,再到深度学习,然后再到强化学习,就像机器学习课程的结构一样,比如著名的吴恩达“机器学习”课程。**从机器学习到深度学习往往更直观,然而,当继续处理强化学习问题时,可能会更令人困惑。马尔可夫决策过程、奖励函数、贝尔曼方程等概念似乎在不同的宇宙中。在我自己的学习过程中,我发现通过一个例子和强化学习模型的实际实施可以帮助我理清概念。**通过玩模型来调整参数,欣赏可视化效果,都可以帮助你加深对强化学习的理解。

在这篇文章中,我将向你简单明了地介绍强化学习和 Q-learning,然后分享一个使用 Q-learning 技术解决强化学习问题的例子——python中的“出租车问题”。你可以下载代码并坚持下去,我希望这能给你一些关于什么是强化学习以及如何在简单的问题环境中实现它的基本概念。

一些注意事项:

  • 一些代码可能不是最有效的,因为我自己也是初学者,欢迎在评论中给出任何建议!
  • 代码的基本结构受到了其中一门 Udemy 课程的启发——机器学习、数据科学和用 Python 进行深度学习由 Frank Kane 教授的 Sundog Education。出于探索的目的,我在模型构建中做了参数调整部分和一些更改,以及我的解释和见解。

什么是强化学习?

图片来源:unsplash byArseny Togulev

简而言之,强化学习不同于典型的“输入 x,输出 y”监督学习问题,因为**它涉及一个代理与其周围环境进行交互,以确定采取什么样的最佳行动。****环境可能是不确定的、复杂的,代理的行为也可能是概率性的,而不是确定性的。**这使得问题看起来极其复杂,因此我们需要首先定义以下典型强化学习问题的设置:

  1. 状态空间

这定义了代理可能处于的所有可能状态,注意,当我们定义状态时,我们必须非常小心,以使定义包含我们需要知道的所有信息,以确定下一个状态。例如,如果我们正在解决一个汽车驾驶问题,我们可能需要知道位置的(x,y)坐标,x 方向的速度,y 方向的速度,甚至 x 和 y 方向的加速度。

状态空间可以是离散的(有限的)或连续的(无限的)。

2。动作空间

这通常比定义状态空间更简单。动作空间是代理可以采取的所有可能的动作。例如,在网格中行走的简单机器人具有向左、向右、向前、向后行走的动作空间。因此,典型地,动作空间是有限的,并且与状态空间相比具有少得多的元素。

3。转移概率

转移概率是到达下一个状态的概率— ( St+1),假定代理处于当前状态(St)并执行了一个动作(at)。这一部分表明代理的行为是随机的,机器人不按照你的指示行动的可能性总是存在的!这个概念在马尔可夫决策过程中使用,但在 Q-Learning 中是一个必要的组件,所以我们不会对它进行过多的阐述。

4。奖励

回报是状态和行动 R(s,a)的函数,我们可以把它看作是对代理人快速有效地达到预期目标的激励。我们通常为我们希望代理达到的状态设置一个正的奖励值,并为代理采取的每一个额外步骤设置一个小的惩罚(负奖励)。

在许多实际情况下,奖励并没有很好地定义,所以我们需要首先运行一个由代理执行的随机行为的大样本,并收集信息来估计奖励。

5。政策

该策略将状态映射为动作,因此它将当前状态作为输入并输出供代理采取的动作,在 MDP 问题中,我们希望找到最优策略,然而在 Q 学习问题中,它也不是关键的。

在 RL 问题中,代理人希望最大化其总报酬。它不断地与环境互动,探索并收集信息,了解在特定状态下执行某个动作会得到什么样的回报,然后在每个状态下搜索要采取的最佳动作。

q 学习

q 学习是一种基于价值的学习算法**。代理的目标是优化一个“价值函数”以适应它所面临的问题。我们之前已经定义了一个奖励函数 R(s,a),在 Q 学习中,我们有一个值函数,它类似于奖励函数**,但是它针对给定的策略评估特定状态中的特定动作。它考虑了采取特定行动所带来的所有未来回报,而不仅仅是当前的回报。在 Q 学习过程中,我们有一个 Q 表,它存储每个状态和每个可能动作的 Q 值,代理探索环境并迭代地更新 Q 值。

学习过程

第一步:初始化

将 Q 表中的所有 Q 值初始化为 0,代理不知道它所处的环境。

第二步:探索空间

代理通过在其所处的状态下执行动作来不断探索环境。这里我们有一个探索 vs .开发的问题:虽然如果我们将动作定义为代理继续执行返回最高价值函数的动作,问题肯定会收敛到全局最优,但这个过程可能会很慢,很痛苦。在具有大状态动作空间的设置中,我们希望代理偶尔随机选择其动作,并且有机会更快地找到最优值。

你可以考虑这样一种情况,亚马逊不断推荐与你购买的产品相似的产品,但偶尔它可能会随机向你展示一些东西,而且总有可能产品实际上符合客户的偏好。这为推荐系统提供了关于客户的新信息,否则需要更长时间才能变得清楚。

我们可以实现探索策略的方式是使用ε贪婪策略,其中存在ε的概率,我们采取随机行动,而不是最大化价值函数的行动,当我们到达代码部分时,这将变得更清楚。

第三步:观察奖励

当探索时,代理将观察它从执行状态(st)中的特定动作(at)到下一个状态(st+1)得到什么奖励。

第四步:更新价值函数

在观察到奖励之后,代理使用下面的公式更新特定状态和动作对的值函数,这返回更新的 Q 表。

我们有学习率和贴现率作为两个超参数。您可以将贴现率视为一个调整参数,以激励代理更快地实现目标,因为它“贴现”状态(st+1)中的未来值。学习率控制着当前值相对于新值的权重。

更新规则(图片由 Americana 制作)

出租车问题

图片来源:unsplash 作者凯·皮尔格

出租车问题是由 OpenAI Gym 的贡献者构建的,open ai Gym 是一个开源库,你可以用 python 安装它来访问不同的环境,供你探索、开发、测试和练习强化学习算法。我强烈建议你安装它并跟随它,在看完我的例子后,你也可以探索其他的标准环境,如“GymFC”:一个飞行控制调整和训练框架,“GymGo”:棋盘游戏 Go,它们都将是你练习和“强化”你对 RL 理解的很好的资源。

https://github.com/openai/gym/blob/master/gym/envs/toy_text/taxi.py

问题说明:

  • 从随机状态开始,我们的工作就是把出租车开到乘客所在的位置,接乘客开车到目的地,把客户放下,然后这一集就结束了。
  • 网格中有 4 个指定位置,用红色— 0、绿色— 1、黄色— 2 和蓝色— 3 表示,蓝色字母对应上车位置,紫色字母表示下车位置。实线表示出租车不能通过的墙,而实心矩形表示出租车,当它是黄色时,它是空的,当它是绿色时,它载着一名乘客。

  • 每个状态由 4 个条目的元组定义:(taxi_row,taxi_col,passenger_location,destination)。例如,图像显示 state (2,3,2,0),这意味着我们位于行索引 2(注意 python 索引从 0 开始,因此这意味着行 3)和列索引 3,乘客位于黄色,编码为 2,我们的目的地为红色,编码为 0。
  • 状态空间:我们可以看到,我们的状态空间由 500 个可能的状态组成,有 25 个可能的出租车位置、5 个乘客可能的位置(包括乘客在出租车中的情况)和 4 个目的地位置
  • 动作空间:有 6 个离散的确定性动作: 0 —向南移动,1 —向北移动,2 —向东移动,3 —向西移动,4 —搭载乘客,5 —放下乘客
  • 奖励:除了运送乘客获得+20 奖励外,每多走一步有 R=-1 的惩罚,非法执行“上车”和“下车”行为导致 R=-10

下面是代码!

首先,我们导入开源库——gym,并开始使用 taxi 环境。在这里,我们使用版本 3,但新版本不断发布,所以如果它不工作,然后尝试 v2 或 v4。

我们可以用可爱的小出租车使用 streets.render() 输出我们环境的可视化。

我们将为我们的问题设置一个初始状态,代理从行索引 2 和列索引 3 开始,我们需要从黄色点接客户,并在红色点放下他。

q 表的初始化被生成为 2D Numpy 数组,该数组表示我们的虚拟空间中的每个可能的状态和动作对。参数是指定的,10000 个历元意味着我们将重复让出租车探索 10000 次。

当我们从 0 和 1 之间的均匀分布生成随机值并将其与我们的 epsilon(探索率)进行比较时,epsilon 贪婪策略开始发挥作用,如果随机值较小,我们从我们的动作空间中采取随机动作,如果不是,我们查看当前的 Q 表并采取使价值函数最大化的动作。

然后,我们执行动作,观察奖励,更新 Q 值,并将状态(st)更新为“下一个状态”(st+1)。

现在我们来看看结果!如果我们在初始状态,最高值是-2.3639511,这对应于向左走一步,如果我们回头看我们最初的可视化,我们可以看到,如果出租车想从黄点搭载乘客,向左移动显然是最佳选择!你可以玩这个,检查不同的状态,这是一个很好的检查,看看你的模型是否收敛。

使用我们计算的 Q 表,我们可以指导出租车司机现在做他的工作。这段代码随机初始化一个状态,并要求出租车司机根据生成的 Q 表执行任务。我们已经将出租车可以走的步数限制为最多 25 步,以防我们的出租车司机运气真的不好,我们重复这个过程 10 次,计算出租车司机完成评估我们模型的任务所需的平均步数。

我们可以看到平均步数是 13.9,还不错!

为了改进我们现有的模型,我们需要调整我们的超参数。如果我们希望我们的模型达到更好的结果,这一步是非常重要的,在这种情况下,我们希望出租车司机能够以更少的步骤执行任务。因此,首先我重新定义了 Q 学习过程和平均行程长度函数的计算,以测试不同的学习率、折扣因子和探索率(ε)。

需要注意的一点是,因为我们在测试平均步数时,初始状态是随机生成的,每次结果都可能不同,所以**我们取 10 次平均步数的平均值!**听起来令人困惑,但是如果你仔细观察上面的代码,它会变得更加清晰。最佳的折现因子是 0.9,所以我们固定 0.9 并尝试不同的学习率,对探索率也是如此。

最后,结合每个参数的最优结果,我们取得了更好的结果,平均步数从 13.9 减少到 11.8!

你可以从 Jovian 下载这段代码,玩得开心点!

https://jovian.ai/americanachen/qlearning-med

访问【www.enlightmentblog.com】获取更多真知灼见,优质文章

Enlightment 简介:

Enlightment是一个博客和网络平台,围绕分享、激励和联系的主题而构建。它旨在促进全球有抱负的大学生之间的思想交流。

该平台继续提供由有抱负的学生创作的高质量内容,可以分发这些内容以激励他人,帮助大学生培养分析和创新思维。我们也渴望建立社区,为有相似兴趣、志向或来自同一所大学的学生牵线搭桥。

今天就加入我们,享受无广告、无订阅的无缝阅读体验。

关注我们/连接到:

脸书:https://www.facebook.com/Enlightmentbloginsta gram:@ enlightmentblog 2021Linkedin:https://www.linkedin.com/company/enlightment-blog

直观解释的强化学习(第 6 部分):策略梯度,循序渐进

原文:https://towardsdatascience.com/reinforcement-learning-explained-visually-part-6-policy-gradients-step-by-step-f9f448e73754?source=collection_archive---------3-----------------------

直观强化学习系列

一个温和的指南,加强算法,在简单的英语

迈克尔·泽兹奇在 Unsplash 上的照片

这是我关于强化学习(RL)系列的第六篇文章。我们现在已经很好地理解了构成 RL 问题的基础的概念,以及用于解决它们的技术。我们还详细研究了两种基于值的算法——Q 学习算法和深度 Q 网络(DQN),这是我们深入强化学习的第一步。

在本文中,我们将继续我们的深度强化学习之旅,并了解我们第一个使用策略梯度技术的基于策略的算法。我们将一步一步地介绍增强算法,这样我们可以看到它与 DQN 方法的对比。

这里是对本系列之前和之后文章的一个快速总结。我的目标是不仅要理解事物是如何工作的,还要理解它为什么会这样工作。

  1. 基本概念和术语介绍 (什么是 RL 问题,以及如何使用马尔可夫决策过程和概念(如回报、价值和政策)中的技术将 RL 问题解决框架应用于该问题)
  2. 解决方案方法 (概述流行的 RL 解决方案,并根据这些解决方案之间的关系进行分类。贝尔曼方程的重要提示,它是所有 RL 算法的基础。)
  3. 无模型算法 (基于价值和基于策略的解决方案的异同,使用迭代算法逐步提高预测。剥削、探索和贪婪的政策。)
  4. Q-Learning (对该算法进行深入分析,这是后续深度学习方法的基础。发展直觉,了解为什么这个算法会收敛到最优值。)
  5. 深度 Q 网络 (我们的第一个深度学习算法。一步一步地演示它是如何工作的,以及为什么做出那些架构选择。)
  6. 策略梯度 —本文 (我们第一个基于策略的深度学习算法。)
  7. 演员评论家*(复杂的深度学习算法,结合了 Deep Q 网络和策略梯度的优点。)*
  8. 惊喜话题😄*(敬请期待!)*

如果您还没有阅读之前的文章,那么最好先阅读一下,因为本文建立在我们在那里讨论的许多概念之上。

政策梯度

通过深 Q 网络,我们间接地获得了最优策略。网络学习输出给定状态的最佳 Q 值。这些 Q 值随后被用于导出最优策略。

因此,它需要使用一个隐式策略,这样它就可以训练自己,例如,它使用一个ε-贪婪策略。

另一方面,我们可以建立一个直接学习最优策略的神经网络。它不是学习一个将状态作为输入并输出所有动作的 Q 值的函数,而是学习一个输出可以从该状态采取的最佳动作的函数。

更准确地说,它输出的不是单一的最佳行动,而是从该状态可以采取的行动的概率分布。然后可以通过从概率分布中取样来选择动作。

(图片由作者提供)

这正是最佳政策。

架构组件

策略网络是一个相当标准的神经网络体系结构,它被训练来预测最佳策略。就像 DQN 一样,如果你的状态可以通过一组数字变量来表示,那么它可以像一个带有几个隐藏层的线性网络一样简单。或者,如果您的州数据表示为图像或文本,您可能会使用常规的 CNN 或 RNN 架构。

策略网络获取当前状态,并预测从该状态要采取的动作的概率分布。

(图片由作者提供)

训练数据是在与环境交互并存储观察结果时从实际经验中收集的。储存了一段经历。每个样本由一个(状态、动作、回报)元组组成。

高级工作流

高层次的工作流程是策略梯度经过许多时期的训练。每个时期有两个阶段。在第一阶段,它从环境中收集一集的训练数据。在第二阶段,它使用训练数据来训练策略网络。

(图片由作者提供)

现在让我们放大这个第一阶段。

(图片由作者提供)

策略网络充当代理,并与情节的环境进行交互。从当前状态,策略网络预测动作的概率分布。然后,它从这个分布中抽取一个动作,在环境中执行它,并返回一个奖励和下一个状态。它将每个观察结果保存为训练数据的样本。

政策网络保持固定,在此阶段不会改善。

(图片由作者提供)

现在,我们将放大流程的下一个阶段。

(图片由作者提供)

在一集结束时,训练数据被输入到策略网络。它从数据样本中提取当前状态和动作,并预测该动作的概率。

(图片由作者提供)

贴现回报是通过从数据样本中提取回报来计算的。

(图片由作者提供)

贴现收益和行动概率用于计算损失以训练策略网络。策略网络学习预测更好的行动,直到找到最佳策略。

(图片由作者提供)

基于策略的解决方案比基于价值的解决方案有一些优势

基于值的解决方案学习一个值函数,该值函数输出可以从一个状态采取的所有动作的 Q 值。他们会努力解决行动数量非常大的问题。例如,您可以有连续的动作(例如,机器人可以旋转任何角度,包括从 0 度到 360 度的分数角度,抬起手臂或转动头部任何角度,以某个范围内的任何实数速度移动,等等),这导致无限数量的动作。

另一方面,像策略梯度这样的基于策略的解决方案学习输出动作概率分布的策略函数,因此,它们可以处理具有大量动作的问题。

这也意味着政策梯度可以学习确定性政策和随机政策,在随机政策中不总是存在单一的最佳行动。因为代理通过从概率分布中取样来选择动作,所以它每次都可以改变它的动作。

另一方面,对于基于价值的解决方案,通过挑选具有最高 Q 值的动作,从最优 Q 值中导出最优策略。因此,他们只能学习确定性政策。

最后,因为基于策略的方法直接找到策略,所以在训练时间方面,它们通常比基于值的方法更有效。

政策梯度确保充分探索

与使用隐式ε-贪婪策略的基于值的解决方案相反,策略梯度在进行中学习它的策略。

在每一步,它通过对预测的概率分布进行采样来选择一个动作。结果,它最终采取了各种不同的行动。

当训练开始时,它输出的动作的概率分布是相当均匀的,因为它不知道哪些动作是优选的。这鼓励了探索,因为这意味着所有的行为都是同样可能的。随着训练的进行,概率分布收敛于最佳动作,从而最大限度地利用。如果某个状态只有一个最佳行动,那么这个概率分布就是一个退化分布,导致一个确定性策略。

政策梯度损失函数

通过 Q-学习和 DQN,我们有了一个目标 Q 值来与我们预测的 Q 值进行比较。所以为它定义一个损失函数是相当容易的。然而,对于政策梯度,没有这样的目标特征来比较其预测的动作概率分布。

我们所能得到的是一个选择的行为是否会带来积极的或消极的回报。在这种情况下,我们如何用它来定义损失函数呢?

让我们考虑一下,我们希望损失函数达到的目标。如果一个行为导致积极的回报,我们希望这个行为变得更有可能(即。增加它的可能性)并且如果它导致一个负的回报,我们想要那个行动变得不太可能(即。降低其概率)。

然而,对于积极行动,我们不想将其概率增加到 1,这意味着它成为唯一要采取的行动,因为所有其他行动的概率将减少到 0。类似地,对于一个消极的行动,我们不想把它的概率降低到 0,这意味着这个行动将永远不会被采取。我们想继续探索和尝试不同的行动多次。

相反,对于一个积极的行动,我们希望概率稍微增加,同时稍微减少所有其他行动的概率(因为它们的总和必须是 1)。这使得这一行动的可能性略有增加。

换句话说,如果我们采取行动 a1,P(a)是采取行动' a '的概率,我们希望最大化 P(a1)。由于损失函数通过最小化而不是最大化来工作,因此我们可以等效地使用 1 — P(a1)作为我们的损失。当 P(a1)接近 1 时,它接近 0,因此它鼓励网络通过增加 P(a1)来修改其权重以调整概率分布。

类似地,对于消极行为。

概率表示为浮点数,当这些数字非常小(接近 0)或非常大(接近 1)时,这些计算会导致数字溢出问题。

为了避免这种情况,我们通常使用-log (P(a1)),它的行为相当于 1-P(a1)。当 P(a1)接近 1 时,它也接近 0,但是“log”不会遇到溢出问题,因为这些值的优势是具有从(–∞,0)到(0,1)的更宽范围。

最后,我们不想给每一个行动分配相同的权重。那些在事件中导致更多奖励的行为应该得到更高的权重。因此,我们使用每个行动的贴现回报来确定其贡献。所以损失函数是-log (P(a)) * Rt,其中 Rt 是行动' a '的贴现收益。这样,每一个行动的概率都与该行动的贴现回报成比例地更新。

详细操作

现在让我们来看看策略梯度的加强算法的详细操作。从某个初始起始状态开始,策略网络将当前状态作为输入,并输出所有动作的概率分布。

(图片由作者提供)

然后,我们通过从该分布中取样来选择一个动作。

(图片由作者提供)

这个动作被反馈给环境,环境产生下一个状态和奖励。下一个状态被反馈到策略网络中。

(图片由作者提供)

观察到的经验的每个样本被存储为训练数据。请注意,在此阶段,策略网络权重保持固定。

(图片由作者提供)

我们继续这样做,直到这一集结束。之后,我们开始使用训练数据作为输入来训练策略网络的阶段。训练批次包含一集样本。

(图片由作者提供)

该事件中访问的每个州都被输入到策略网络中。

(图片由作者提供)

策略网络预测来自这些状态中的每一个的所有动作的概率分布。

(图片由作者提供)

获得每个状态下实际采取行动的概率。

(图片由作者提供)

计算每个样本的折现回报,作为奖励的加权和。

(图片由作者提供)

然后将行动概率乘以折现回报。我们希望最大化这一点,但由于神经网络通过最小化损失来工作,我们取其负值,这达到了相同的效果。最后,把所有状态的这些负数加起来。

(图片由作者提供)

综上所述,这是计算损失的完整流程。

(图片由作者提供)

这种损失用于训练策略网络,然后在下一个时期重复该循环。

(图片由作者提供)

结论

我们现在已经了解了为什么使用基于策略的策略梯度方法,以及该算法是如何工作的。在之前的文章中,我们也看到了基于值的 DQN 算法。

在下一篇文章中,我们将最终了解一种更高级的 RL 解决方案,称为 Actor-Critic,它将以创新的方式结合这两种方法的优势。

最后,如果你喜欢这篇文章,你可能也会喜欢我关于变形金刚和音频深度学习的其他系列。

让我们继续学习吧!

强化学习

原文:https://towardsdatascience.com/reinforcement-learning-fda8ff535bb6?source=collection_archive---------12-----------------------

思想和理论

对这种特殊形式的机器学习的历史、现代和未来应用的回顾

强化学习(图片由图标上的平面图标显示授权给克里斯·马奥尼)

内容

1.简介
2。历史发展(1992 年以前)
— 2.1。并行开发
———2 . 1 . 1。试错学习
— — 2.1.2。最优控制问题
———2 . 1 . 3。时间差异学习方法
— 2.2。综合发展
3。现代发展(1992 年后) — 3.1。桌游发展
— 3.2。电脑游戏的发展。当前动态
5。未来发展
6。结论
7。参考文献

1.介绍

强化学习并不是一个新概念,而是在 70 多年的学术严谨中发展成熟的。从根本上说,强化学习是一种机器学习的方法,通过这种方法,算法可以在给定的环境中做出决定并采取行动,并通过反复试错的行动来学习做出什么样的适当决定。在 20 世纪 90 年代的研究中,强化学习的学术论述追求三个并行的研究“线索”(试错法、最优控制和时间差)。然后,强化学习能够继续掌握下棋、下围棋和无数电子游戏。强化学习的现代应用使企业能够优化、控制和监控各自的流程,达到惊人的准确度和精细度。因此,强化学习的未来既令人兴奋又令人着迷,因为该研究旨在提高算法的可解释性、可问责性和可信度。

2.历史发展(1992 年以前)

萨顿和巴尔托( 2018 )讨论了强化学习的三个“线索”:1)通过试错法学习;2)最优控制问题;和 3)时间差异学习方法。这些线索在 20 世纪 80 年代交织在一起之前,由研究人员独立进行,导致了我们今天所知道的强化学习的概念。

2.1.并行发展

2.1.1。通过反复试验学习

桑代克效应定律(图片由迪亚尔·贾巴里在研究之门上拍摄)

在观察动物智力时,桑代克( 1911 )将试错法定义为“效果法则”,它与给定情况下产生的满意或不适的感觉相关联。当明斯基( 1954 )假设使用 SNARCs(随机神经模拟强化计算器)时,这一概念被整合到机器学习模拟中,当明斯基( 1961 )解决“信用分配问题”时,这一概念被进一步具体化;也就是如何在许多可能产生成功的决策中分配成功的功劳。计算试错过程的研究被推广到模式识别(Clark&Farley 1955; Farley & Clark 1954 )之前,通过使用错误信息更新连接权重来适应监督学习(Rosenblatt 1962; Widrow &霍夫 1960 )。由于传统的试错法和错误损失函数之间的区别模糊,在整个 20 世纪 60 年代和 70 年代,很少有出版物专门讨论强化学习。约翰·安德瑞就是这一领域的一名研究者,他开发了斯特拉系统(安德瑞 1963 ),该系统通过与环境的互动进行学习,还开发了具有“内心独白”的机器(安德瑞&卡辛 1969 ),以及后来可以向老师学习的机器(安德瑞 1977 )。不幸的是,正如萨顿&巴尔托( 2018 )所讨论的,安德烈的开创性研究并不为人所知,也没有对后续的强化学习研究产生很大影响。

2.1.2。最优控制的问题

最优控制的问题(图像由 Pradyumna Yadav 对 AnalyticsVidhya

对“最优控制”的研究始于 20 世纪 50 年代,被定义为“一个控制器,以最小化动态系统随时间推移的行为的测量”(萨顿&巴尔托 2018 )。Bellman ( 1957a )在 Hamilton ( 1833 、 1834 )和 Jacobi ( 1866 )的工作基础上开发了一种专门用于强化学习的方法,该方法使用动态系统的状态动态定义函数方程,并返回最优值函数。这种“最优回报函数”现在通常被称为贝尔曼方程,从技术上来说,这是一类用于解决控制问题的方法,也是贝尔曼的书动态规划 ( 贝尔曼 1957a )的主要焦点。贝尔曼继续介绍了马尔可夫决策过程(1957 b)——他将其定义为“最优控制问题的离散随机版本”——霍华德( 1960 )利用该过程定义了马尔可夫决策过程的政策迭代方法。在 20 世纪 60 年代和 70 年代,在“最优控制”领域没有太多的研究;然而,Bryson ( 1996 )指出,自 20 世纪 80 年代以来,在诸如部分可观测的马尔可夫决策过程及其应用、近似方法、异步方法和动态规划的现代处理等领域发表了许多论文。

2.1.3。时间差异学习方法

时间差分学习从数学微分中获得灵感,旨在从一组已知变量中获得预测。但一个重要的区别是,它是通过从价值函数的当前估计值进行引导来实现的。它类似于 Monde Carlo 方法( Hammersley 1964 ),但它能够在最终结果已知之前,在后期迭代阶段调整最终预测(蒙特卡罗无法做到);如 Sutton ( 1988 )举例说明的,在给定的星期天,为下一个星期六创建预报,然后在实际的星期六天气已知之前,在星期五更新星期六预报。

时间差异法的起源植根于动物学习心理学( Thorndike 1911 ),尤其是“次级强化物”的概念。实际上,当次级强化物(如刺激物)与初级强化物(如食物或疼痛)配对时,次级强化物具有与初级强化物相似的性质。明斯基( 1954 )是第一个意识到时间差异方法对于强化学习的重要性的人,尽管萨缪尔( 1959 )似乎是第二个意识到这种重要性的人,但是他在他的工作中并没有提到明斯基。Minsky ( 1961 )在 Samuel ( 1959 )的工作基础上,加强了这个概念对强化学习理论的重要性。当 Klopf ( 1972 , 1975 )研究大系统中的强化学习时,时间差分法和试错法变得纠缠不清,这些强化学习由大系统的单个子组件概念化,每个子组件都有自己的兴奋性输入作为奖励,抑制性输入作为惩罚,并且每个子组件都可以相互强化。

2.2.综合发展

利用 Klopf 的观点,Sutton ( 1978a 、 1978b 、 1978c 、 1984 )进一步发展了与动物学习理论的联系,并进一步探索了学习受时间连续预测变化驱动的规则。这项工作实际上为强化学习的研究打开了学术闸门,随后几年见证了许多有影响力的发展,例如:

  • 经典条件作用的心理模型(巴尔托&萨顿 1981a 、 1981b 、1982;萨顿&巴尔托 1981a ,1981 b);
  • 经典条件作用的神经元模型(Klopf 1988;特索罗 1986);
  • 经典条件作用的时差模型(萨顿和巴尔托 1987 ,1990);
  • 应用于极点平衡问题的“演员-评论家架构”(巴尔托等人,1983);
  • “演员-评论家架构”的扩展以及与反向传播神经网络技术的集成(Anderson 1986;萨顿 1984);
  • 将时间差学习从控制决策中分离出来,并引入lambda()属性( Sutton 1988 )。

当 Watkins ( 1989 )开发了 Q-Learning(Watkins 1989)时,强化学习起源的三个“线程”最终被联合起来,该 Q-Learning 由 Werbos ( 1987 )加强,他主张试错学习和动态编程的融合。在这之后,研究人员探索了强化学习的自动化电子实现,并能够取得一些惊人的结果。

3.现代发展(1992 年后)

3.1.棋盘游戏的发展

1992 年,Tesauro 能够在他开发的 TD-Gammon 程序中实现这些概念( Tesauro 1994 ),该程序能够在双陆棋游戏中达到“大师级别”。研究已经转向尝试将这一成功应用于国际象棋游戏(Baxter 等人 2000 ,2001; Thrun 1995 )。IBM 开发 DeepBlue 的唯一目的就是下棋。然而,它遭受了维数灾难,因为算法花费了太多的时间来计算,它无法在未来'看'得足够远,并且它因非常糟糕的开局移动而臭名昭著( Szita 2012 )。然而,深蓝在 1997 年战胜了一位世界冠军。征 1997;新生儿 2000;桑托 1997 。

研究人员接着进行了一个更大甚至更复杂的游戏:围棋。虽然做了很多尝试,试图学习如何玩这个游戏(布兹 2006a ,2006 b; Bouzy &赫尔姆斯泰特 2004;库仑 2007a 、2007 b;达尔 1999;盖利&银 2008;盖利等人 2006 年; Schraudolph 等人 2001 年; Silver et al. 2008 ),没有人能够战胜世界冠军,直到谷歌的 AlphaGo 在 2016 年夺冠(Borowiec 2016;莫耶 2016 )。DeepBlue 和 AlphaGo 的区别在于,DeepBlue 利用并行化的基于树的搜索方法和定制的硬件进步,有效地使用“蛮力”来计算赢得游戏所需的所有步骤(boro wiec 2016;新生儿 2000);但是这种方法在围棋中是不可能的,因为有太多的走法和可能的组合,计算开销是不可能的。相比之下,AlphaGo 利用了蒙特卡洛模拟、蒙特卡洛树搜索、贝叶斯优化和实际观看世界冠军之前的比赛的组合(陈等人 2018;福 2016;惠勒 2017 ),以便建立一个足够强大的模型,并且不需要对未来的移动进行蛮力计算。这些发展标志着强化学习领域的重大进步,并使整个世界能够看到这种特殊的机器学习技术的一些可能性。

3.2.电脑游戏的发展

雅达利游戏(图片由脸书雅达利 2600

随着二十世纪八十年代和九十年代儿童的成长,许多人玩雅达利电子游戏。它们于 1977 年发布,包含 526 款游戏,包括经典游戏,如 Pong、Breakout 和 Space Invaders。Atari 模拟器为强化学习算法提供了一个理想的环境来学习如何玩游戏(Hausknecht et al . 2014;凯泽等人 2019; Mnih 等人 2013 )由于其基于像素的显示和简单的控制选项。玩了一段时间游戏后,研究人员观察到算法使用了一些令人印象深刻的技术;比如在《突围》中,它能够钻一个洞,以便用更少的努力赢得比赛。这表明了强化学习的力量,特别是它能够学习某些规则和实践,而这些规则和实践不是以编程方式告诉模型的( Berges 等人和;帕特尔等人 2019;两分钟论文 2015 。许多其他计算机游戏也看到了强化学习的发展,包括像 Snake,Flappy Bird,Angry Birds 和 Candy Crush 这样的游戏。自 Atari 游戏以来,现代计算机游戏已经取得了实质性的进步,并且它们提供了更加复杂和动态的环境,这为强化学习的应用提供了大量的学习机会。

4.当前的发展

红绿灯自动化(图片由小源等人在 Arxiv 上拍摄)

虽然强化学习的历史令人着迷,但现代强化学习的应用确实令人兴奋。在最近的一篇文章中,Garychl ( 2018 )列举了一些强化学习在当今工业中应用的例子;包括:计算机集群中的资源管理、交通灯控制、机器人、网络系统配置、化学、广告和游戏。洛里卡( 2017 )将这些机会整合为三个主要的应用主题,这些主题可以应用于任何行业的任何业务:优化(如流程规划、产量管理、供应链)、控制(如自动驾驶汽车、工厂自动化、风力涡轮机控制)以及监控和维护(如质量控制、预测性维护、库存监控)。所有这些应用程序都提供了一个环境、一个动作、一个响应和一种优化方法,因此为强化学习算法应用于这些特定情况提供了机会。

很简单,当企业面临以下任何场景时,它们应该寻求在自己的人工智能战略中实施强化学习( Chahill 2017 ):

  • 他们使用模拟,因为系统或过程太复杂(或太危险),无法通过试错法来教授机器;或者
  • 他们正在处理大的国家空间;或者
  • 他们正在寻求通过优化运营效率和提供决策支持来增强人工分析和领域专家的能力。

5.未来发展

强化学习的未来是一个非常主观的讨论,在这个讨论中,许多人可能会有不同的观点。强化学习的未来有两个主要方向,可以概括如下:

  1. 未来是光明的,强化学习被广泛采用和实施,并继续对人类产生积极影响;或者
  2. 未来是严峻的,人们反对强化学习在他们生活中的逐步渗透,人工智能的未来发展集中在强化学习以外的技术上。

强化学习领域所取得的惊人进步及其对我们社会的积极影响表明,这一趋势将会持续下去,并将延续到未来。戈德布( 2018 )讨论了强化学习的光明前景,而基尔希特( 2019 )讨论了强化学习将继续取得实质性进展,并将使企业的日常生活变得更加轻松和高效。在这两种选择中,这种光明的未来是最有可能的。

自动驾驶汽车(图片由 metamorworks 在 Shutterstock 授权给 Chrism Mahoney)

然而,如果不讨论人工智能兴起背后的一些社会担忧,以及一些好莱坞电影对人工智能产生的一些焦虑,那将是不负责任的。Frye ( 2019 )围绕 AI &特别是强化学习讨论了一些令人担忧的问题,提到强化学习是不安全的,因为任务规范(难以精确指定 AI 智能体应该执行什么任务),以及不安全的探索(智能体从试错中学习,这意味着它必须先犯错误才能知道不做什么),这可能导致我们的同胞发生事故和受伤(或更糟)。特别是如果我们考虑自动驾驶汽车的例子。Knight ( 2017 )明确探索了这个例子,重点关注 Nvidia 自动驾驶汽车,它不是通过给它的任何编程命令,而是通过观察其他司机来学习驾驶。虽然这本身是一个令人印象深刻的壮举,但潜在的问题是,创造者不知道计算机如何或为什么做出决定,这无疑会将人们的生命置于危险之中。因此,在强化学习被更广泛的社区广泛接受和采用之前,还需要做一些改进。那些领域是对创作者的可解释性和对用户的责任性。一旦这两个问题得到整改,那么人工智能和强化学习无疑会变得更加值得信赖。

6.结论

强化学习自 20 世纪 50 年代出现以来已经走过了漫长的道路;它在发展和成熟的道路上还有很长的路要走。从 20 世纪 90 年代的理论和概念进步来看,强化学习已经征服了国际象棋、围棋和无数电子计算机游戏。强化学习也开始在商业和工业中出现,并且在我们现代社会日益增长的挑战中继续被证明是有益和有用的。强化学习的未来将很快以许多许多不同的方式渗透到我们的日常生活中;但在此之前,它的可解释性、可问责性和可信度等一些基本问题都得到了纠正。尽管如此,强化学习的未来似乎是漫长而光明的,我们将继续从这个强大的人工智能领域看到许多伟大的事情。

7.参考

安德森,C. 1986,用多层联结主义系统学习和解决问题(适应性,策略学习,神经网络,强化学习),马萨诸塞大学阿姆赫斯特分校博士论文。

Andreae,J. 1963,“Stella:一个学习机器的方案”,国际会计师联合会会议录卷,第 1 卷,第 2 号,第 497–502 页,ISSN:1474–6670,DOI:10.1016/s 1474–6670(17)69682–4。

安德烈,J. 1977,用可教的机器思考,ISBN:学术出版社,伦敦。

Andreae,J. & Cashin,P. 1969,“具有独白的学习机器”,国际人机研究杂志,第 1 卷,第 1 期,第 1–20 页,ISSN:0020–7373,DOI:10.1016/s 0020–7373(69)80008–8。

巴尔托,A. &萨顿,R. 1981a,适应性智能的目标寻求成分:初步评估

巴尔托,A. &萨顿,R. 1981b,“地标学习:联想搜索的一个例证”,生物控制论,第 42 卷,第 1 期,第 1-8 页,ISSN:0340-1200,DOI: 10.1007/BF00335152。

巴尔托,A. &萨顿,R. 1982,“通过神经元样适应性元素模拟经典条件作用中的预期反应”,行为大脑研究,第 4 卷,第 3 期,第 221–35 页,ISSN:0166–4328,DOI:10.1016/0166–4328(82)90001–8。

巴尔托,a .,萨顿,R. &安德森,C. 1983,“能解决困难的学习控制问题的神经元样自适应元件”, IEEE 系统、人与控制论汇刊,第 SMC-13 卷,第 5 期,第 834–46 页,ISSN:0018–9472,DOI:10.11109/台积电。19804.888686666007

Baxter,j .,Tridgell,A. & Weaver,L. 2000,“利用时间差学习下棋”,机器学习,第 40 卷第 3 期,第 243 页,ISSN:0885–6125,DOI: 10.1023/A:1007634325138。

Baxter,j .,Tridgell,A. & Weaver,L. 2001,“强化学习与国际象棋”,学习玩游戏的机器,第 91–116 页。

贝尔曼,R. 1957a,动态规划,ISBN: 069107951x,普林斯顿大学出版社。

Bellman,R. 1957b,“一个马尔可夫决策过程”,数学与力学杂志,第 6 卷第 5 期,第 679–84 页,ISSN: 00959057。

伯杰斯,v .、饶,P. & Pryzant,R. nd,“雅达利突围的强化学习”,斯坦福大学,<https://cs.stanford.edu/~rpryzant/data/rl/paper.pdf>。

Borowiec,S. 2016,alpha Go Seals 4-1 战胜围棋特级大师 Lee Sedol 《卫报》查看 2020 年 5 月 31 日,<https://www . FBE . hku . hk/f/page/75261/Reading % 201 _ alpha Go % 20 Seals % 204-1% 20 Victory % 20 over % 20Go % 20g rand master % 20 Lee % 20 Sedol . pdf【T12

Bouzy,B. 2006a,“将浅的和选择性的全局树搜索与蒙特卡罗用于围棋相关联”,计算机和游戏,第 3846 卷,第 67-80 页,DOI: 10.1007/11674399_5,施普林格柏林海德堡,柏林,海德堡。

Bouzy,B. 2006b,“蒙特卡罗围棋的移动修剪技术”,计算机游戏进展,第 4250 卷,第 104-19 页,DOI: 10.1007/11922155_8。

Bouzy,b .和 Helmstetter,B. 2004,“蒙特卡罗围棋发展”,计算机游戏的进步,第 159–74 页,施普林格。

布赖森,A. 1996,“最优控制”, IEEE 控制系统,第 16 卷第 3 期,第 26–33 页,ISSN:1066–033 x,DOI: 10.1109/37.506395。

Chahill,D. 2017,为什么强化学习可能是复杂工业系统的最佳 Ai 技术,查看 2020 年 5 月 31 日,<https://www . bons . Ai/blog/Ai-Reinforcement-Learning-strategy-Industrial-Systems>。

Chen,y .,Huang,a .,Wang,z .,Antonoglou,I .,Schrittwieser,J. & Silver,D. 2018,《Alphago 中的贝叶斯优化》,,ISSN:2331–8422,< , >。

Clark,W. & Farley,B. 1955,“自组织系统中模式识别的一般化”,1955 年 3 月 1-3 日西方联合计算机会议论文集,第 86-91 页,DOI: 10.1145/1455292.1455309,<【https://dl.acm.org/doi/abs/10.1145/1455292.1455309】>。

库隆,R. 2007a,“计算围棋游戏中走法的 Elo 等级”,【https://hal.inria.fr/inria-00149859/document】电脑游戏工作坊、<、、、>。

Coulom,R. 2007b,“蒙特卡罗树搜索中的有效选择性和备份算子”,载于 H. Herik,P. Ciancarini 和 H. Donkers。(编辑。),电脑与游戏,第 4630 卷,第 72–83 页,施普林格。

达尔,F. 1999,“Honte,一个使用神经网络的围棋程序”,学习玩游戏的机器,第 205–23 页,<http://citeseerx.ist.psu.edu/viewdoc/download?doi = 10 . 1 . 1 . 50 . 2676&rep = re P1&type = pdf>。

法利,B. &克拉克,W. 1954,“用数字计算机模拟自组织系统”,信息理论 IRE 专业组汇刊,第 4 卷,第 4 期,第 76–84 页,ISSN:2168–2690,DOI:10.11109/tit . 1991996

Frye,C. 2019,现实世界中强化学习的危险,查看 2020 年 5 月 31 日,<https://faculty . ai/blog/The-Dangers-of-Reinforcement-Learning-in-The-Real-World/>。

傅,M. 2016,“Alphago 与蒙特卡罗树搜索:仿真优化视角”,载于 2016 冬季仿真会议,IEEE,第 659–70 页,<https://doi-org . ez proxy . lib . uts . edu . au/10.1109/WSC . 2016.7822130>。

Garychl 2018,强化学习在现实世界中的应用,查看 2020 年 5 月 31 日,<https://towardsdatascience . com/Applications-of-Reinforcement-Learning-in-Real-World-1a 94955 BCD 12>。

Gelly,s .和 Silver,D. 2008,“在 9 X 9 计算机围棋中达到大师水平”,载于 https://www.aaai.org/Papers/AAAI/2008/AAAI08-257.pdf>*AAAI*第 8 卷第 1537-40 页。

Gelly,s .、Wang,y .、Munos,R. & Teytaud,O. 2006 年,用蒙特卡罗围棋、<、、>中的模式修改 Uct。

戈德布,C. 2018,强化学习的光明前景,查看 31/5/2020,<https://medium . com/apteo/The-Bright-Future-of-Reinforcement-Learning-a 66173694 f88>。

汉密尔顿,W. 1833,关于通过特征函数的系数表达光的路径的一般方法,&行星,ISBN:p . d .哈代印刷。

汉密尔顿,W. 1834,关于以前应用于光学的一般数学方法在动力学中的应用,ISBN:p . d .哈代印刷。

哈默斯利,J. 1964,蒙特卡罗方法,ISBN: Methuen,伦敦。

Hausknecht,m .、Lehman,j .、Miikkulainen,R. & Stone,P. 2014,“一种用于一般 Atari 游戏的神经进化方法”, IEEE 计算智能和游戏中的人工智能汇刊,第 6 卷,第 4 期,第 355–66 页,ISSN:1943–068 x,DOI:10.11109/TCI AIG . 2013 . 20133106

霍华德,R. 1960,“动态规划和马尔可夫过程”。

Jacobi,K. 1866 年,在克莱布什的《科学知识分子学术讨论会》之前。

Kaiser,l .,Babaeizadeh,m .,Milos,p .,Osinski,b .,Campbell,r .,Czechowski,k .,Erhan,d .,Finn,c .,Kozakowski,p .,Levine,s .,Mohiuddin,a .,Sepassi,r .,Tucker,G. & Michalewski,H. 2019,“基于模型的强化学习用于 Atari”,载于 arXiv.orgICLR,https://arxiv.org/pdf/1903.00374.pdf>。

金,D. 1997,《卡斯帕罗夫对深蓝:终极人对机器挑战》,机器挑战,特拉法尔加广场

Kirschte,M. 2019,对强化学习有什么期待?,2020 年 5 月 31 日查看,<https://towards data science . com/what-to-expect-from-reinforcement-learning-a 22 e 8 c 16 f 40 c>。

Klopf,A. 1972,大脑功能和适应系统:一种异质理论

Klopf,A. 1975,“自然智能与人工智能的比较”, ACM SIGART Bulletin ,第 52 期,第 11–3 页,ISSN:0163–5719,DOI:10.1145/1045236.104236 . 1045237。

Klopf,A. 1988,“经典条件作用的神经元模型”,心理生物学,第 16 卷第 2 期,第 85–125 页,ISSN:0889–6313,DOI:10.3758/BF 0333113。

骑士,W. 2017,Ai 核心的黑暗秘密,2020 年 5 月 31 日查看,<https://www . technology review . com/2017/04/11/5113/The-Dark-Secret-at-The-Heart-of-Ai/>。

Levy,S. 1997,“人与机器”,新闻周刊,第 129 卷第 18 期,第 50–6 页,ISSN: 00289604。

Lorica,B. 2017,强化学习在工业中的实际应用:强化学习的商业和工业应用概述,查看 2020 年 5 月 31 日,<https://www . oreilly . com/radar/Practical-Applications-of-Reinforcement-Learning-in-Industry/>。

明斯基,M. 1954,神经模拟强化系统理论及其对脑模型问题的应用,普林斯顿大学博士论文。

明斯基,M. 1961,“迈向人工智能的步骤”,IRE 会议录,第 49 卷,第 1 期,第 8-30 页,ISSN:0096-8390,DOI:10.1109/Jr proc . 1961 . 2877771 . 1961 . 287771

Mnih,v .、Kavukcuoglu,k .、Silver,d .、Graves,a .、Antonoglou,I .、Wierstra,d .、Riedmiller,M. 2013,《用深度强化学习玩雅达利》,ArXiv.org、<https://arxiv.org/pdf/1312.5602.pdf>。

Moyer,C. 2016,谷歌的 Alphago 如何击败一个围棋世界冠军,《大西洋》,查看 31/May/2020,<https://www . thealantic . com/technology/archive/2016/03/The-invisible-opposite/475611/>。

新生儿,M. 2000,《深蓝对 Ai 的贡献》,数学与人工智能年鉴,第 28 卷第 1 期,第 27–30 页,ISSN:1012–2443,DOI: 10.1023/A:1018939819265。

Patel,d .、Hazan,h .、Saunders,d .、Siegelmann,H. & Kozma,R. 2019,“应用于 Atari Breakout Game 的转换到脉冲神经元网络平台后增强学习策略的改进鲁棒性”,神经网络,第 120 卷,第 108–15 页,ISSN:0893–6080。

罗森布拉特,F. 1962,神经动力学原理:感知机和大脑机制理论,ISBN:斯巴达图书公司,华盛顿特区

Samuel a . 1959,“使用跳棋游戏进行机器学习的一些研究”, IBM 研发杂志,第 3 卷第 3 期,第 210–29 页,ISSN:0018–8646,DOI: 10.1147/rd.33.0210

Santo,B. 1997,“IBM 为人机复赛调整深蓝”,电子工程时报,第 946 期,第 111–2 页,ISSN:0192–1541。

Schraudolph,n .,Dayan,P. & Sejnowski,T. 2001,“通过时间差分方法学习评估围棋位置”,博弈中的计算智能,第 77–98 页,Springer,https://snl.salk.edu/~schraudo/pubs/SchDaySej01.pdf>。

Silver,d .、Sutton,r .和 m .üller,2008,“基于样本的学习和带有永久和短暂记忆的搜索”,载于国际机器学习会议,第 968–75 页。

萨顿,R. 1978a,学习理论对大脑单通道理论的支持,ISBN。

萨顿,R. 1978b,“单通道理论:学习的神经元理论”,大脑理论通讯,第 3 卷,第 3 期,第 72–4 页。

萨顿,R. 1978c,经典和工具条件作用中的期望的统一理论,ISBN。

萨顿,R. 1984,强化学习中的时间学分分配,博士论文,ProQuest 学位论文出版。

Sutton,R. 1988,“通过时间差异的方法学习预测”,机器学习,第 3 卷第 1 期,第 9–44 页,ISSN:0885–6125,DOI: 10.1023/A:1022633531479。

萨顿,R. &巴尔托,A. 1981a,“一个构建和使用其世界内部模型的适应性网络”,认知和大脑理论,第 4 卷,第 3 期,第 217–46 页。

萨顿,r .和巴尔托,A. 1981b 年 b,“走向适应网络的现代理论:期望和预测”,心理评论,第 88 卷,第 2 期,第 135-70 页,ISSN:0033-295 x,DOI:10.1037/0033-295 x . 88 . 2 . 135

萨顿,r .和巴尔托,A. 1987,“经典条件作用的时间差模型”,载于认知科学学会第九届年会会议录,华盛顿州西雅图,第 355–78 页。

萨顿,r .和巴尔托,A. 1990,“巴甫洛夫强化的时间导数模型”,m .加布里埃尔和 j .摩尔(编辑),学习和计算神经科学:适应性网络的基础,第 497–537 页,麻省剑桥麻省理工学院出版社。

萨顿,r .和巴尔托,A. 2018,强化学习:导论,第二版。,ISBN: 9780262039246,麻省理工学院出版社,马萨诸塞州剑桥,<https://web . Stanford . edu/class/psych 209/Readings/suttonbartoiprlbook 2 nded . pdf>。

Szita,I. 2012,“游戏中的强化学习”,载于 M. Wiering 和 M. van Otterlo(编辑。),强化学习:最先进的,第 539–77 页,DOI:10.1007/978–3–642–27645–3 _ 17,施普林格柏林海德堡,柏林,海德堡,<https://doi.org/10.1007/978-3-642-27645-3_17>。

Tesauro,G. 1986,“经典条件作用的简单神经模型”,生物控制论,第 55 卷,第 2–3 期,第 187–200 页,ISSN:0340–1200,DOI: 10.1007/BF00341933。

Tesauro,G. 1994,“Td-Gammon,一个自学的双陆棋程序,实现大师级的游戏”,神经计算,第 6 卷,第 2 期,第 215–9 页,ISSN:0899–7667。

桑代克,E. 1911,动物智力:实验研究,ISBN: 9780765804822,麦克米伦公司。

Thrun,S. 1995,“学习下棋”,载于神经信息处理系统进展,第 1069–76 页,<http://papers . neur IPS . cc/paper/1007-学习下棋. pdf >。

TwoMinutePapers 2015, Google Deepmind 的深度 Q-Learning 玩雅达利突围,2020 年 5 月 31 日查看,<https://www.youtube.com/watch?v=V1eYniJ0Rnk>。

沃特金斯,C. 1989,从延迟奖励中学习,博士论文,https://www . research gate . net/publication/33784417 _ Learning _ From _ Delayed _ Rewards>。

Werbos,P. 1987,“构建和理解适应性系统:工厂自动化和大脑研究的统计/数值方法”, IEEE 系统、人和控制论汇刊,第 17 卷第 1 期,第 7-20 页,ISSN:0018-9472,DOI: 10.1109/TSMC.1987.289329,< https://www.aaai

惠勒,T. 2017,alpha go Zero——它是如何和为什么工作的,2020 年 5 月 31 日观看,http://tim.hibal.org/blog/alpha-zero-how-and-why-it-works/T2。

Widrow,b .和 Hoff,M. 1960,“自适应开关电路”, 1960 年 Wescon 会议记录第四部分,第 94–104 页,麻省理工学院出版社,马萨诸塞州剑桥。

维基百科 2020,雅达利 2600 ,浏览 2020 年 5 月 31 日,<https://en.wikipedia.org/wiki/Atari_2600>。

面向任何人的强化学习:开放 AI 健身房和 Ray

原文:https://towardsdatascience.com/reinforcement-learning-for-anyone-open-ai-gym-and-ray-ee365a7d1ecc?source=collection_archive---------17-----------------------

使用开放 AI Gym 和 Ray Python 库创建和优化强化学习代理的实用介绍

(图片转载自https://imgur.com/gallery/OVvOuYZ)

介绍

Open AI Gym 是典型强化学习(RL)任务的开源接口。因此,使用开放式人工智能健身房可以变得非常容易地开始强化学习,因为我们已经提供了各种不同的环境和代理。我们剩下要做的就是编写不同的算法并测试它们,以便让我们的代理学习如何以最好的方式完成不同的任务(正如我们将在后面看到的,使用雷可以很容易地做到这一点)。此外,Gym 还兼容其他 Python 库,如 Tensorflow 或 PyTorch,因此可以轻松创建深度强化学习模型。

开放人工智能健身房提供的不同环境和代理的一些例子有:雅达利游戏、机器人任务、控制系统等…

图 1:雅达利游戏示例[1]

如果你有兴趣了解更多关于强化学习主要概念背后的理论,更多信息可以在我的前一篇文章中找到。

开放 AI 健身房

在这一节中,我们现在将浏览开放人工智能健身房图书馆的一些基础知识。首先,我们可以通过使用以下命令轻松安装该库:

pip install gym

一旦安装了这个库,我们就可以实例化一个环境。对于这个例子,我们将使用月球着陆器。在这个场景中,我们的目标是让我们的代理学会正确地将他们的飞船降落在两面旗帜之间(我们的着陆平台)。代理人着陆越精确,他所能获得的总回报就越大。为了实现这个目标,代理可以在任何时间点选择以下四个动作中的任何一个:启动左发动机,启动右发动机,关闭发动机,什么也不做。

为了快速可视化这个场景,让我们实例化环境,并让我们的代理迭代地采取从环境中采样的随机动作(图 2)。

import gymenv = gym.make("LunarLander-v2")
env.reset() # Instantiate enviroment with default parameters
for step in range(300):
    env.render() # Show agent actions on screen
    env.step(env.action_space.sample()) # Sample random action
env.close()

图 2:代理执行随机操作

为了提高代理的性能,我们可以利用 步骤 函数返回的信息:

  • 观察 =执行动作后的环境信息(如棋盘游戏中的棋盘位置、像素坐标等……)。
  • 奖励 =量化表示我们之前的行为有多积极(越积极越好)。
  • done =是一个布尔值,显示我们是否到达了可用的最后一次迭代(例如,我们达到了目标,我们用完了可用的最大步骤)。
  • 信息 =调试实用程序。

利用这些信息和学习算法(例如 Q 学习、SARSA 或深度强化学习),我们将能够快速解决不同环境中的大量问题。

光线

Ray [2]是一个用于多处理的开源 Python 框架,它还提供了一个强化学习库(RLlib)和一个超参数优化库(Tune ),作为其信息结构的一部分,以便大规模创建强化学习模型。

在我们之前的例子中,使用开放式人工智能健身房可以轻松地创建环境和代理,而 Ray 将帮助我们更容易地测试和优化我们代理的不同学习算法。

可以使用以下命令为 Linux 用户轻松安装 Ray:

pip install ray

对于 Windows 用户来说,虽然还没有提供完全的支持,但是可能需要使用一个用于 Linux 的 Windows 子系统(或者使用免费工具,比如 Google Colab!).

深度 Q 学习示例

现在我们已经成功地引入了 Gym 和 Ray,我们准备着手解决之前描述的月球着陆器问题。本文使用的所有代码都可以在我的 Github 档案中找到。首先,我们需要导入所有需要的库。

import ray
from ray import tune
from ray.rllib import agents
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()

接下来,使用 Ray 我们可以很容易地实例化一个深度 Q 网络 (DQN)架构来训练我们的代理。使用深度 Q 网络是为了通过用能够学习如何正确估计 Q 值(函数逼近)的神经网络来替换基于表 Q 学习的算法,从而容易地扩展它们。如果你有兴趣了解更多关于这个话题的信息,我强烈建议你看一看理查德·萨顿和安德鲁·巴尔托的《强化学习:导论》。

在这个链接中可以找到所有不同的算法的完整列表。

在 run 函数中,我们可以设置允许的最大时间步长数,并传递不同的配置参数,以定制我们的网络。最后,使用 tune.grid() 功能,我们可以额外执行网格搜索,以便实时改进我们的一些模型超参数(例如,gamma 和学习速率)。

results = tune.run(
    'DQN', 
    stop={
        'timesteps_total': 50000
    },
    config={
    "env": 'LunarLander-v2',
    "num_workers": 3,
    "gamma" : tune.grid_search([0.999, 0.8]),
    "lr": tune.grid_search([1e-2, 1e-3, 1e-4]),
    }
)

训练完我们的代理后,将返回一个包含所有主要训练指标的数据帧(图 3 ),然后我们可以使用它来确定哪个超参数组合可以为我们的代理带来更大的回报。

图 3:培训指标的总结

利用返回的数据框架和记录的信息,我们可以很容易地看到一些关键的训练指标,例如使用不同的参数(如 gamma 和学习率值)如何随着时间的推移产生不同的奖励(图 4)。

图 4:超参数优化报告

最后,使用我们确定的最佳超参数,我们可以实例化一个新的 DQN 模型,并使用模型配置设置中的***“monitor】:True***将代理的性能记录为视频。

ray.init()config = {'gamma': 0.999,
          'lr': 0.0001,
          "n_step": 1000,
          'num_workers': 3,
          'monitor': True}
trainer2 = agents.dqn.DQNTrainer(env='LunarLander-v2', config=config)
results2 = trainer2.train()

正如我们在图 5 中看到的,我们的代理成功地学会了如何完成我们的任务!

图 5:训练有素的代理

希望你喜欢这篇文章,感谢你的阅读!

联系人

如果你想了解我最新的文章和项目请关注我的媒体并订阅我的邮件列表。以下是我的一些联系人详细信息:

  • Linkedin
  • 个人博客
  • 个人网站
  • 中等轮廓
  • GitHub
  • 卡格尔

文献学

[1]2018 秋季 CS498DL,作业 5:深度强化学习(伊利诺伊大学香槟分校),SVETLANA LAZEBNIK。访问地点:http://slazebni.cs.illinois.edu/fall18/assignment5.html

[2] PyPI: Ray:统一 ML 生态系统的并行分布式 Python 系统。访问地点:https://pypi.org/project/ray/

生产调度的强化学习

原文:https://towardsdatascience.com/reinforcement-learning-for-production-scheduling-809db6923419?source=collection_archive---------5-----------------------

面向应用的综述我们最新的关于组合优化的强化学习的论文

照片由丹尼尔·史密斯在 Unsplash 上拍摄

最近,我有幸成为在 SoCS'21 上发表论文“SOLO:在线搜索,离线学习”的团队的一员。虽然这篇论文相当实用,但它仍然是作为科学出版物来写的,对于不习惯的人来说可能很难读懂。因此,我决定写一个简短的概述,并强调其主要发现。

首先,让我描述一下生产调度的问题,这是 SOLO 最初的动机。

问题设置

给定一组异构机器和一组异构生产作业,计算最小化指定度量的处理调度。异构意味着机器和作业可以具有不同的属性,例如,机器的不同吞吐量和作业的不同所需处理时间,以及实际中更多的属性。此外,真正的问题由于一组强加的约束而变得复杂,例如类别**A**的作业不能在类别**B**的机器上处理,等等。

从理论上讲,该问题是“作业调度”问题的一个复杂实例,与“有能力限制的车辆路径”问题一起被认为是组合优化的经典问题。虽然这个问题是 NP 难的(在多项式时间内没有精确的解),但 CO 社区对它进行了很好的研究,提供了一些方法来解决它的理论(简化)版本。然而,大多数方法都无法处理现实世界中的问题规模或我上面提到的额外约束。这就是为什么大多数时候业内人士求助于某种形式的随机搜索结合特定领域的试探法。

生产计划概述。时间表以时间线图的形式呈现。条形的颜色与作业相对应,其长度决定了加工时间。红色条纹表示机器的停机时间。[机器图标通过“flatcoin.com”在“带归属的 flatcoin 许可证”下显示。其余由作者提供]

在我看来,生产调度问题结合了对现实世界应用的潜在影响和学习方法领域令人兴奋的研究机会。听起来像是应用研究小组的天作之合,不是吗?因此,我认为建立这个项目来开发一个基于学习的解决方案是一个非常容易的决定。在那之后不久,我就加入了这个项目。

从组合优化到马尔可夫决策过程

我们的努力始于众所周知的观察,即一些 CO 问题可以转化为马尔可夫决策过程(MDP)或在线组合问题,因为它们在 CO 社区中被提及。为此,我们定义了代表部分解决方案的状态、动作和描述问题状态如何随着所采取的每个动作而演变的转移函数。让我们考虑一个“作业调度”问题的在线公式的例子。在高层次上,我们可以将我们的状态定义为队列中所有作业和所有机器的聚合信息。动作可以定义为“在空闲机器**M1****”**上从队列中处理作业**J1**。然后,转换函数将负责从队列中删除作业**J1**,并更新机器**M1**的状态,推进当前时间以及您的特定公式所需的任何其他内容。

在线调度问题。图例相当于概览图。机器名称上的绿色阴影表示它在当前时间步长是自由的(红色-被占用)。计划图中的红色虚线表示当前时间步长。[机器图标通过“flatcoin.com”在“带归属的 flatcoin 许可证”下显示。其余由作者提供]

根据 MDP 公式,行动的顺序可以针对特定的指标(奖励)进行优化。沿着这条路走下去的第一步是为你的决策过程开发一个模拟器。我们已经基于 SimPy 包将我们的生产调度问题模拟器实现为一个离散事件模拟器。因为我们的目标是一个真实的用例,所以我们必须确保:

  1. 工件和机器属性的分布尽可能地符合实际情况。这需要大量的数据科学工作。
  2. 模拟器包括额外工作的到达。这对于模拟调度解决方案在真实工厂中连续运行的条件是必要的。同样,这个到达过程必须是随机的,分布尽可能地与现实相匹配(数据科学工作)。

寻找经典还是寻找经典

好吧,你的 MDP 有一个模拟器,现在呢?同样,这里有一些方法可以应用。正如我已经提到的,实践中最流行的选择是某种形式的随机搜索结合启发式搜索。对于我们的项目,我们决定从蒙特卡罗树搜索(MCTS)开始,因为它有吸引力的理论保证,并且在强化学习社区(顺便说一下,我们来自那里)很受欢迎。在我们的日程安排问题的背景下,需要了解 MCTS 的要点:

  1. MCTS 必须在生产中实时运行,它的性能受到适合您时间预算的迭代次数(即树扩展)的限制。您不希望为接下来的几个作业优化调度的时间超过处理这些作业的时间,是吗?
  2. 对于随机问题,MCTS 需要大量的迭代来计算一个像样的结果。如上所述,我们的问题必须是随机的,以模拟现实世界的设置。
  3. 对于问题中的任何随机过程,MCTS 计算一个蒙特卡罗估计。在搜索过程中,没有简单的方法可以明确地使用可靠的作业到达分布估计值(根据历史数据计算)。

前两点可以通过优化模拟器和 MCTS 实现的运行时,以及将计算扩展到月球来解决。然而,对于大多数真实的用例,您可以扩展的范围是有限的。这就是为什么有大量的研究工作旨在通过利用某种形式的学习来优化 MCTS 的趋同性(你知道它将走向何方,不是吗?).不过,第三点更有趣。人们可以想象,考虑到一个良好估计的工作到达分布可以解锁一些先进的战略。例如,为特定类别的作业(经常到达的)预留一台机器,或者打赌更适合当前时间表的作业的到达,等等。这些策略可能会导致更好的时间表,并根据您的生产规模节省大量成本。

搜索还是强化学习还是两者都有?

那么,我们如何超越 MCTS 的表现呢?鉴于我们的背景和对基于学习的方法的偏见,答案很简单。“让我们应用强化学习”。我们确信 RL 非常适合解决这个问题的一些原因:

  1. 大多数 RL 算法需要相当长的离线训练时间。然而,一旦经过训练,它们的执行速度会比基于搜索的方法快得多。该属性也称为计算压缩。
  2. 给定足够强大的函数近似(即神经网络)和足够的训练时间,RL 算法在模拟中击败了竞争对手。幸运的是,对于我们的生产调度问题,使用历史数据,我们可以建立一个相当精确的模拟,并可以在一定程度上缩小“模拟与真实”的差距。
  3. 可以将关于环境中随机性的明确知识注入到 RL 算法中。一些 RL 方法甚至可以以原则的方式解决问题的随机性(贝叶斯 RL)。因此,我们有可能解开上述“亲”调度策略。

在设计我们的 RL 解决方案时,我们受到最近大量研究的启发,这些研究证明 Alpha系列算法可以成功应用于 CO 问题。Alpha指的是 AlpahGo、AlphaZero(你一定听说过它们),以及在训练或执行过程中利用 MCTS 或其他搜索形式的任何 RL 算法。

AlphaZero 是一种非常先进的算法,需要大量的工程工作和大量的培训时间。它依赖于一个结合了策略和状态值估计器角色的神经网络。在训练期间,MCTS 迭代被压缩到网络的策略“部分”中。网络的值“部分”被训练来预测状态值的蒙特卡罗估计。将 MCTS 投射到策略网络中,以单次转发的计算成本实现了令人难以置信的执行性能。尽管它可能是我们设置中最吸引人的一点,但策略网络也可能是该方法中最需要数据和计算的组件。当时对我们来说是不可行的。这就是为什么我们决定从简单开始。

我们在执行时离开了 MCTS,转而致力于它更快的收敛。我们离线训练了 Q-网络,使用了著名的 DQN 以及最近所有的修改,比如决斗,双 Q,优先重放等等。一旦经过训练,我们就用它来删除 MCTS 运行期间的可能动作集,即丢弃在树扩展期间被预测为具有低值的动作。此外,我们使用 Q-network 作为从非访问状态推出的策略。假设网络被适当地训练,这两点应该导致更快地收敛到 MCTS 所要求的可靠的状态值估计。因此,在生产中运行调度时,您可以在计算预算内获得更好的操作。

问题编码的问题

要回答的最后一个问题是如何对问题状态进行编码,以及为 Q-网络选择哪种架构。最正统的方法是将有关排队作业和机器的观察信息嵌入向量空间,并将离散动作定义为所有可能的“作业到机器”组合集合中的索引。然后,可以使用卷积层、递归层或全连接层来构建网络架构。但是,你已经猜到了,有两个要点必须考虑进去:

  1. 模拟的不同状态可能包括队列中不同数量的作业和可能不同数量的机器。
  2. Q-network 应该对队列中作业的顺序和机器的顺序不敏感(或者说以一种特殊的方式敏感)。

第一点意味着我们的状态-动作向量空间可以有不同的大小。然而,这个问题仍然可以通过一些实现技巧来解决,比如裁剪作业队列和动作屏蔽。第二点更重要,值得举个小例子:

让我们假设对于调度过程的某些状态,动作“作业 ***J1*** 到机器 ***M1*** ”得到最高的 Q 值。现在,我们交换了队列中的第一个和第二个作业,其他都保持不变。我敢打赌,你会期望看到得分最高的动作现在变成了“作业 ***J2*** 到机器 ***M1*** ”。

从数学上讲,对于我们的问题,Q-网络应该表示一个等变映射。等方差意味着对于映射输入中的任何排列,在输出中都必须观察到相同的排列。训练一个等变的 CNN 或 RNN 是一项相当具有挑战性的任务。幸运的是,有一种不同的神经网络可以轻松实现等变映射——图形神经网络(GNN)。这就是为什么我们决定以二分图的形式对调度问题状态进行编码。该图由两个不相交且独立的组件组成,分别代表作业和机器。每个作业节点都连接到每个机器节点。边缘对应于动作,可以解释为“在机器**V**上安排作业**U**”。此外,我们维护状态信息的全局特征向量,这些信息既不能直接归因于作业,也不能直接归因于机器。由于 GNNs 的设计和我们的图的二分结构,我们的 Q-网络现在对于工件和机器顺序的排列是等变的。

网络架构概述。在输入二分图中,正方形对应于工作顶点,圆形对应于机器顶点。底部的矩形代表整个图形的全局特征向量。在处理后的图表中,行动优势以绿色突出显示,状态值以黄色突出显示。[图片由作者提供]

GNN 的另一个有用属性是处理任意图形拓扑的能力,只要所有节点具有相同的特征维度(对于边也应该如此)。换句话说,GNN 可以处理具有不同数量的节点和边的图,这意味着我们也成功地解决了在问题状态下改变作业数量的问题。

在所提出的 Q 网络架构中,GNN 是自由设计组件。可以针对具体问题进行调整。特别地,对于生产调度问题,我们使用了具有注意力和编码解码层的消息传递 GNN。在通过 GNN 的正向传播之后,我们将输出图的边解码为状态-动作对的优点,并将全局图状态解码为当前子问题(状态)的值,即决斗图-Q-网络。

结论

当把所有的东西放在一起,我们得到了我们的方法——SOLO:在线搜索,离线学习。现在你明白这个名字的由来了。我希望我已经设法让这个方法对不习惯科学出版物的读者更加透明。然而,我在这篇文章的幕后留下了相当多的细节和有趣的见解。因此,如果我设法激起了至少一点兴趣,我强烈建议阅读原文。

虽然,这篇论文不会以任何方式传达为了使这个看似简单的方法运行而投入到这个项目中的巨大工程努力。为此,我要再次感谢所有的合作者。

如果您有任何其他问题,请随时写评论。

Python 中带 Flappy Bird 的强化学习

原文:https://towardsdatascience.com/reinforcement-learning-in-python-with-flappy-bird-37eb01a4e786?source=collection_archive---------12-----------------------

Q-learning 和 RL 考虑事项的演练

活着,活着。(*图片来自作者,*资产来自 FlapPyBird )

介绍

2014 年,热门游戏《沉睡者》席卷了移动游戏世界。它已经在 PyGame 中实现,但最有趣的是它非常适合强化学习。代理(鸟)只能执行 2 个动作(拍打或什么都不做),并且只对 1 个环境变量(即将到来的管道)感兴趣。这个问题非常简单,非常适合从头开始用 Python 实现强化学习。

本文对该项目进行了更高层次的概述。代码和结果可以在 GitHub 这里 找到。

q 学习

Q-learning 是一种无模型强化学习算法,通常用于学习给定特定状态下代理采取的最佳行动。当代理人在特定状态下采取行动时,它会收到奖励(或惩罚),代理人的目标是使其总奖励最大化,因此在采取行动时,它还必须考虑潜在的未来奖励。对于 Flappy Bird,代理是其可能的动作是不做任何事情或拍打翅膀的鸟,并且其当前状态由以下各项定义:

  • 与即将到来的管道的距离
  • y到即将到来的底部管道的距离
  • 鸟的当前 y 轴速度v
  • 顶部和底部即将到来的管道之间的距离y1

第一次达到一个新的状态时,一个 Q 值被初始化(0 就可以了),代理将执行默认的动作并获得奖励。如果以前遇到过一个状态,代理将执行具有最高 Q 值的动作(奖励最多的动作)。然后更新 Q 值,并将该值插入状态×动作 Q 表中的相应单元。

当代理对不同的状态采取不同的动作时,Q 表被更新。下一次代理遇到状态 1 时,它将摆动,对于状态 2,它将什么也不做。(图片作者)

Q 值根据几个参数更新:

# Update q values in a 2d array
Q[state, action] = Q[state, action] + alpha * (reward + gamma * np.max(Q[new_state, :]) — Q[state, action])
  • αα是步长,它决定了旧信息在多大程度上被遗忘以利于新信息。
  • γγ决定了未来奖励的重要性。例如,γ=0 意味着代理将只考虑其当前报酬。
  • Q[状态,动作]是当前的 Q 值。

结果

初步训练

奖励函数被定义为死亡罚-1000 分,否则罚 0 分,这样代理的关注点就是得到尽可能高的分数。这确保了奖励功能在每一集之后都有足够的影响,而在实现中,分数增加奖励+1 意味着惩罚几乎没有影响。

在α=0.7 和γ=0.95 的情况下,代理最初在没有任何探索的情况下被训练大约 10,000 集,并且几乎能够达到 100 万的分数。已经记录了 y 轴,以便可以更清楚地看到分数的滚动增加。

特工在学习生存。(图片作者)

经验回放:灾难性遗忘

尽管代理在初始训练后表现良好,但要进一步提高却需要更长的时间。这是因为代理需要很长时间才能到达死亡的场景,即使这样它也只能从死亡中学习一次。通过引入经验回放,代理可以尝试多次困难场景,直到克服困难或陷入恢复循环(此处设置为 100 次尝试)。在通过困难场景时,在失败时,它从开始重新开始,以避免连续增加所达到的最大分数。

加载 Q 表,从初始训练开始继续,但进行经验回放。(图片作者)

代理的性能有了初步的提高,因为它能够从更困难的场景中学习,但是它的性能很快下降到以前的最大值以下。

这被称为灾难性遗忘,它通常会导致代理性能的振荡,因为它会忘记和重新学习要采取的最佳行动。通过从相同的场景失败中反复学习,代理过度拟合,并且忘记了先前可概括的 Q 值。

体验回放:回放缓冲区

为了克服灾难性遗忘,在训练智能体时会添加阿尔法衰减,帮助它保留早期学习的信息,同时仍然从更少的场景中学习。此外,被视为陷入循环的尝试次数减少到 50 次,并且在体验重放期间,会创建一个“重放缓冲区”,其中包含所有采取的操作。然后以小批量的方式更新 Q 表,一旦体验重放完成,就对 5 次尝试进行采样。

这只鸟的记忆力更好。(图片作者)

这个代理差不多能达到一千万的评分。虽然随着训练持续超过 10,000 集,观察到表现下降,但它能够从这种最初的遗忘中恢复。虽然没有花费更多的时间来训练这个代理,但是可以预期代理的性能会随着它忘记和重新学习要采取的最佳动作而波动。随着 alpha 继续衰减,这将最终使代理在其最大值附近保持稳定。

ε贪婪政策

我们现在尝试新训练的代理,引入探索率ε,这提供了探索随机动作的机会,直到它在 10,000 集后从 0.1 衰减到 0,以及α衰减,它在 20,000 集后从 0.7 衰减到 0.1。

经典的 sigmoid 增长曲线。(图片作者)

由于α和ε衰减,该代理比初始训练中学习得更慢,但是一旦它达到略低于 100 万的最佳性能,就稳定得多。这个最高分数比我们没有探索时的分数要低,可能是因为 alpha 衰减提高了稳定性,但代价是代理学习得更慢。

最终验证和想法

从上面我们可以看到,最终表现最好的代理是用经验重放和重放缓冲区训练出来的。代理性能经过 25 次运行验证。

🐤那是个不错的高分!(图片作者)

代理表现不错,持续过 10 万,最高分达到 500 万以上。它能够通过大多数情况,但当更困难的情况出现时,它不能永远活着,这意味着它有时会很早就死去。

  • 稳定性:变异系数(标准差/平均值)为 0.967。这是预料之中的,因为在随机环境中,只有当智能体能够克服所有情况并且永不死亡时,它才能完全稳定。
  • 平均分:平均分 2001434。这是代理人始终能够达到的一个很高的分数,超过了任何人类游戏。
  • 最高分:25 轮最高分是 6,720,279。这是一个接近默认最大训练值 1000 万的高分,再次超过任何人类游戏。

未来的工作

  • 更长的训练时间,表现最好的代理总共训练了 15 个小时,只达到了 10,674 集
  • 实施优先体验回放
  • 训练一个在飞鸟环境中永不死亡的特工

参考

  • 许宏涛,用强化学习训练一只永远不会死的 flappy bird(2020),https://towards data science . com/Use-reinforcement-learning-to-train-a-flappy-bird-NEVER-to-die-35b 9625 aa ECC

强化学习不仅仅是为了玩游戏

原文:https://towardsdatascience.com/reinforcement-learning-is-not-just-for-playing-games-1de449800fe2?source=collection_archive---------9-----------------------

行业笔记

多臂强盗如何能帮助一个创业公司

图片由米歇尔·莱曼(Pexels)提供

我们都看过很多文章,在这些文章中,强化学习(RL)代理被用于穿越冰冻的湖泊、爬山、为城市中的出租车选择最佳路线等。游戏对现实生活中的代理人提出了重大挑战,因为它们需要我们做出几个决定。正常情况下,我们必须优化这些决策,以提高我们的整体得分(或者不要掉进冰封的湖中的一个洞,否则我们会遇到麻烦)。游戏也以一种美丽的方式展示了代理背后的数学原理。但是,RL 不是玩游戏;而是要培养能自己做决定的特工。

多股武装匪徒

基本的强化学习算法之一是多臂土匪。强化学习和其他类型的学习之间最重要的区别是,它使用评估所采取的行动的训练信息,而不是通过提供正确的行动来指导[1]。

在概率论中,多臂土匪问题被定义为有限数量的资源,这些资源必须在竞争的备选方案之间分配,以便最大化它们的预期回报。基本的想法是,我们可以在各种选项中进行选择,以最大化我们的回报。但是我们不知道哪些替代方案会给我们提供更高的分数。如果我们知道就不会有问题。因此,我们在一个不确定的环境中运作。虽然 MAB(多臂土匪问题)被认为是一个相当简单的 RL 方法,其潜在的应用范围可能是显着的。让我们用一个真实的例子来说明它是如何工作的。

一家电动汽车初创企业

一家新的电动汽车初创公司刚刚完成了她的商业计划。电动滑板车的市场正在增长。每年,市场都以两位数的速度扩张。因为越来越多的人使用它,市场细分正在从“驾驶乐趣”扩展到包括像上班、短途城市旅行、上大学等细分。每个新的细分市场都有新的客户需求:它不一定只是为了好玩。还有其他因素需要考虑,包括速度、安全性和舒适性。考虑到这一点,这家初创公司构建了一个极简风格的框架,可以用一系列备选方案进行修改(图 1)。因此,他们可以简单地生产这个框架并添加替代产品,而不是必须创建十个独立的产品。制造成本下降,上市时间也缩短。

**图一。**电动滑板车创意。图片作者。

图 1 显示了选项:选项 1 和 2,具有更高速度的引擎。选项 3 至 6 具有更高容量的电池(3,4),一种在制动时充电的电池(5),增加了充电时间,以及(6)一种专为全路况保护的电池。选项 8 和 9 有特殊的踏板车框架加固,旨在成为事故的安全卫士:选项 8 是铝,9 是碳纤维。最后,选项 10 包括更大的车轮和定制的悬架,乘坐更加舒适。

问题是

这个“问题”和许多初创公司一样:现金。这对于将产品推向市场至关重要。机械工程师经理向团队的其他成员提出了一个关键问题:

“我们有足够的资金制造 100 个基本框架和 100 个配件。接受和适应一个新配件需要三周时间。人们只是讨厌等这个产品等这么久。我们需要做一个初始库存,但是我们应该购买什么配件呢?我们每样都有十个吗?”

这是一个棘手的问题。配件将符合最终产品,不能组合。到目前为止,我们有十个成品要展示。我们不知道哪些会更成功。我们的理想状态应该是有更多我们知道会卖得很快的最终产品,以及一些我们知道会卖得很慢的产品。第一次销售的收入可以用来投资新的配件,让我们的公司发展得更快。

营销研究

创业团队立即开始市场调研项目的工作。他们还发现了一份关于在大都市使用电动滑板车的报告[2]。在这篇文章中,他们就电动滑板车对潜在买家进行了一系列采访。图 2 展示了结果。

图二。电动滑板车基准。转载自哈特、科尼利厄斯&博根伯格、克劳斯。(2018).电动滑板车在城市环境中的使用。交通研究程序,37。作者图片

多武装匪徒在行动

强盗问题是玩家与游戏设备交互的顺序游戏。玩家通常被称为学习者,游戏设备是环境。游戏进行了 n 轮,其中 n 是一个被称为地平线的正自然数。在每一轮中,学习者首先从一组给定的动作中选择一个动作一个一个,然后环境会显示一个奖励。武装吃角子老虎是多武装强盗问题的典型例子。玩家必须选择一只手臂,机器会显示价格(奖励)。每次玩家选择并且 arm 正在执行一个动作 一个该动作包含在玩家拥有的所有选项所代表的动作集合中。例如,如果吃角子老丨虎丨机有十只手臂,那么玩家有十个可能的动作可供选择。动作序列被记录在一个历史中,玩家从过去的动作(历史)中学习以优化未来的步骤。从历史到行动的映射称为策略** (π)。政策是玩家对这个环境的了解。玩家可以有一些关于环境的先验信息。该信息被称为环境类【3】,也称为真值**【1】,表示为*【𝑞∗(𝑎】。我们可以说一个行为的真正价值是当这个行为被选择时的平均回报。***

****图三。计算真值。图像由 Pavel Danilyuk (Pexels)提供。

想象一下,有人在赌场里看别人玩老丨虎丨机。在最后一个小时,他看到了一台报价高于其他机器的机器。其余机器价格较低。当前面的玩家离开后,我们的玩家选择这台机器(一号机器),因为他知道前一个小时有两个高价。当然,这在统计学上并不显著,但这是我们玩家拥有的所有信息。

通过重复的行动选择,我们希望通过将我们的行动集中在最佳选择上来最大化我们的赢款。由于我们不能确切地知道动作值,我们必须对其进行估计。这就是土匪算法的作用。

探索与开发

当玩家开始游戏时,他使用先前学习的信息(环境等级或真实值)从上面的例子中选择一号机器。这被称为利用,它适用于利用他已经拥有的知识。然而,我们的玩家也想尝试不同的杠杆来获得更多的知识(也许那里有一些有趣的东西)。这就是所谓的探索。土匪问题的解决方案是估计行动的价值,以便使用这些估计来创建行动选择决策,从而在某个时间段内最大化期望的总回报。

土匪问题的解决方案是估计行动的价值,以便使用这些估计来创建行动选择决策,从而在某个时间段内最大化期望的总回报。

平衡勘探和开发将是我们估算的一个基本计算。当我们总是利用当前的知识来最大化即时回报时,我们使用的是贪婪的行动选择方法。一直贪婪地行动的一个替代方案是专门花一点时间(有概率 ε )去探索其他替代方案。然后我们使用ε-贪婪行动选择方法。概率ε是帮助我们平衡勘探和开发选择方法的参数。我们什么时候使用一个或另一个?从长远来看,贪婪方法的表现要差得多,因为它经常无法执行次优的操作。当我们有低不确定性的真实值时,投入大量时间去探索就没有意义了。我们可以利用现有的知识。但是当这个真实值有很高的不确定性时,我们预计利用这个知识是不够的,所以我们应该去探索,以最大化我们的奖金。**

可以用多臂土匪解决我们的创业问题吗?

我们初创公司面临的挑战是资源分配。我们的资源有限,必须有效地管理它们,以使我们的收益最大化(以同样的投资获得更多的销售)。我们有 10 种竞争的可能性(如果我们选择一种,我们就不能选择其他的)。考虑开一家街边商店,在那里你可以每天买一件成品(镜架+配件)。选择什么样的商店将是一个行动。顾客可以走进商店,购买或不购买最终产品。然后你回家,第二天早上你带着另一个选择,重复同样的方法 n 个时间步。当然,我们不打算创建一个企业,只销售一种产品。我们打算建一个商店,在那里我们将出售我们所有的产品。然而,这是一个类似于之前解释的多武装匪徒问题的场景。然后,我们可以使用多臂 bandit 解决启动问题,以确定最佳配件选择,从而在第一个百台批量测试中最大化我们的销售额。

一些数学和伪代码

在多臂问题中,有许多方法可以估计最佳行动值。我们将采用之前描述的ε-贪婪动作选择方法以及置信上限动作选择方法。图 4 和图 5 显示了这两种方法。**

图 4 。完整 bandit 算法的伪代码,使用增量计算的采样平均值和ε-贪婪动作 a 选择[1]。图片作者。

Bandit 算法必须跟踪收集的所有奖励。它必须使用该记录( Qn )估计所有 n 奖励的平均值。这可以通过以下方式实现[1]:

新估计旧估计+步长【目标-旧估计】

其中[Target-Old Estimate]是估计误差(等于 Rn-Qn)。这个误差被称为学习者相对于策略的后悔,它被定义为使用 n 轮策略的总期望总回报与学习者在 n 轮中收集的总期望回报之间的差值。**

图五。贪婪方法和置信上限方法的区别。图片作者。

**置信上限方法基于非贪婪行为的最佳潜力选择非贪婪行为,同时考虑其估计值与最大值的接近程度以及这些估计值中的不确定性。记住非贪婪动作被ε-贪婪动作选择方法强制随机尝试。

用 python 定义我们的问题

为了开始解决我们的问题,给这个创业公司最好的建议,我们必须定义环境。我们要做的第一件事是定义我们真正的价值观。正如我们的时代开始做的那样,我们将利用市场调查[2]来估计真实价值。

图 6 。来自市场调查的真实价值。图片作者。

图 6 显示了我们所有附件的列表。我们将为我们的销售建立一个市场份额。我们将建立一个列表,条件是所有市场份额的总和为 100(或归一化为 1)。然后,我们通过给一些选项赋予更大的价值来完善这个列表,研究表明这些选项与客户更相关。较低的等级被分配给那些看起来不太重要的特征。总数是 100 英镑。这个列表可以归一化为 sum 1。然后我们有了下面的列表,可以直接用 Python 生成:

**m_size = [0.11, 0.03, 0.08, 0.05, 0.16, 0.13, 0.06, 0.16, 0.3, 0.19]**

我们在这个价值上有很多不确定性。我们的估计来自市场调查中的一些定性反应。为了包括这种不确定性,我们可以认为我们的真值不是这个值。相反,我们将把它们视为概率分布,其中 𝜇 等于这个值,而 𝜎 等于 𝜇 / 2。是的,𝜇值越高,𝜎值就越高。我们有点保守,所以我们不太确定那些我们认为会有更多销量的配件。对于那些我们认为销量会减少的车型,我们的不确定性会减少。根据经验,销售越多,不确定性就越大。**

**在这些值上,我们有一个重要的不确定性。我们的估计是基于市场调查的定性回答。我们可以通过假设我们的真值不是这些值来包含这种不确定性。相反,我们会认为它们是正态分布,其中 𝜇 等于我们的真值列表, 𝜎 =𝜇 / 2。的确,对于更高的 𝜇 值,我们也有更高的 𝜎 值。我们有点保守,所以我们不确定我们认为会畅销的配件。对于那些认为我们销量会下降的人来说,我们的不确定性更小。此外,我们估计配件的价值会更高(销售额会更高)。考虑到这种不确定性,我们的环境可以在一个类中写成如下形式:

*****class* BanditEnv:**
    ***def* __init__(*self*):**
        self.size = 10
        self.means = m_size
    ***def* step(*self*, *action*):**
        assert action == int(action)
        assert 0 <= action < self.size
        reward = np.random.normal(loc=self.means[action], 
                 scale=self.means[action]/2) **#gaussian distribution**
        **return reward****

现在我们必须根据图 4 定义我们的算法:

*****def* epsilon_greedy(*env*, *nb_total*, *eps*, *eps_factor*=1.0):**
    rewards = []
    Q = np.zeros(env.size)   ***# shape: [n_arms]***
    N = np.zeros(env.size)   ***# shape: [n_arms]***
    for _ in range(nb_total):
        if np.random.rand() > eps:
            A = np.random.choice(np.flatnonzero(Q == Q.max())) 
        else:
            A = np.random.randint(env.size)  **# explore randomly**
            R = env.step(A)
            N[A] += 1   
            Q[A] += (1/N[A]) * (R - Q[A]) ***# incremental mean***          
            eps *= eps_factor ***# epsilon decay***
            rewards.append(R) **return Q, np.array(rewards)****

其中 env 是我们的环境, nb_total 是我们实验的时间步数,EPS=εeps_factorε 的衰减因子。该计算将给出在 nb_total 步骤后计算的“市场份额”的新列表,以及每个时间步骤获得的奖励。

**env = BanditEnv()
nb_total=1000
epsilon = 0.1
**Q, rewards** = epsilon_greedy(env, nb_total= nb_total, eps=epsilon)**

图 7:ε= 0.1,1000 时间步的ε-贪婪算法结果。作者图片

图 7 显示了 ε =0.1 的结果。在 1000 个时间步之后,我们可以将我们的真实值与计算值进行比较。除了一些附件之外,结果与我们的初始值相当接近。我们可以把时间步长想象成每次消费者来到我们的商店购买一个选项。控制勘探开发平衡和时间步长是计算的重要参数。 ε =0.1 的值表示该算法将其计算成本的 10%分配给“探索”新动作。如果我们增加这个数量,我们将冒算法对低回报行为分配过多权重的风险,这将对优化问题产生影响。图 8 可以帮助我们评估这种行为。

图 8 。累积遗憾和累积回报(ε=0.1)。图片作者。

我们可以看到,累积的回报和累积的遗憾必须随时间增长。后悔不是线性上升的,这一点很关键(就像 ε =0.0 的情况一样)。我们还可以看到,较高的值( ε =0.5)比较低的值( ε =0.1)后悔增加更强,奖励也是如此。我们希望看到奖励的大幅增长。这两个指标可以被认为是我们代理的性能指标。**

在图 9 中,我们可以看到使用置信上限算法获得的结果。使用该算法时, c 的典型值为 1.41 (√2)。我们看到计算出的市场份额与我们的真实值非常相似。数值越高,不确定性越高(更高的 𝜎 ) 所以,环境的不确定性也是一个深刻影响最终结果的话题。如果我们对我们的环境说,我们非常确定我们的真实值(低不确定性),算法将显示与我们非常相似的结果。如果我们不太确定(高度不确定),值可能相差很大。

图 9 。1000 个时间步后 c= √2 的置信上限算法

让我们比较两种算法。在图 10 中,我们可以看到两种算法的后悔与时间步长的比较。ε贪婪算法的性能明显优于 T36 算法。遗憾呈线性增长,大约 300 个时间步长会降低其增长率。**

图 10 。算法上的后悔比较。图片作者。

环境不确定性

我们已经强调了环境不确定性在两种算法的最终结果中的作用。在上面的例子中,我们引入了真实值,其中𝜎的值为𝜇/的值为2。我们将对我们的算法说,我们甚至更不相信我们给出的值。我们将通过选择𝜎等于 𝜇来实现(通过 𝜎 = 𝜇,我们增加了两倍的不确定性)。两种算法都显示真实值和计算值之间的差异较大,其中 ε-greedy 的差异明显较大。在图 11 中,我们可以看到两种算法如何显示真实值和计算值之间的较大差异,ε-greedy 清楚地显示了最大的差异。**

图 11 。更高的不确定性环境, 𝜎 = 𝜇.图片作者。

结论

这是我们可以向这家初创公司展示的最终表格,比较了我们从市场研究中获得的值和我们的算法计算出的值:

图 12 :最终结果。图片作者。

我们已经展示了简化版的多臂强盗方法在解决现实世界的问题时是多么的有用。资源分配是大多数组织中经常出现的一种决策问题。这些决策可以在各种复杂的环境中重现。在这篇文章中,我们使用了一个非常简单的设置来描述一个常见的决策过程。我们已经展示了如何使用多臂 bandit 算法来自动化这个决策过程,以便在不确定的条件下获得更准确的结果。

Python 代码

我们使用 numpy 库进行算法,使用 plotly 进行绘图。代码可以在这里找到。

参考资料:

[1]理查德·萨顿和安德鲁·g·巴尔托(2020),《强化学习:导论》。麻省理工学院出版社。

[2]哈特、科尼利厄斯&博根伯格、克劳斯(2018)。电动滑板车在城市环境中的使用。运输研究程序。37.10.1016/j.trpro.2018.12.178

[3]拉蒂摩尔和 Csaba szepesva ri(2020 年)。强盗算法。剑桥大学出版社。

强化学习锁定 N' Roll

原文:https://towardsdatascience.com/reinforcement-learning-lock-n-roll-eec6719dfe1?source=collection_archive---------60-----------------------

利用双深度 Q 网络学习随机状态博弈

作者图片

Lock N' Roll 由 Armor Games 于 2009 年创建。这是一个为那些智力水平高的人设计的游戏,那些被选中的少数人拥有巨大的智商和数学及概率思维的倾向。普通的游戏玩家不可能理解如此复杂和高级的娱乐。

或者,也许这只是我告诉自己的,以证明我的朋友中有少数人听说过它,这是我最喜欢的浪费时间的方式之一。这是一种需要在 4x4 网格上以不同组合和图案放置不同颜色骰子(红色、黄色、绿色或蓝色,编号为 1-4)以最大化点数输出的游戏。当你没有骰子或者你想对棋盘的当前状态进行评分时,你选择显示“掷骰子”的按钮,它也会锁定你的移动,这样你放置的骰子就不能再被操纵了。掷骰子后获得的骰子是随机的。小丑是在达到一定的总点数后获得的,它们可以放在棋盘上的任何地方,以模仿任何给定骰子的效果。当棋盘上满是骰子并且你不再开玩笑时,游戏结束。骰子可以通过获得高分组合来清除。有什么不喜欢的?

https://armorgames.com/play/4283/lock-n-roll

作者图片

多年来,我断断续续地在手机上安装这款应用,试图完善我自己的游戏方法。我如何在下一次掷骰子时最大化清除空间的机会?设置 400 点移动清除 4 个空格,还是 200 点移动清除 6 个空格,哪个更重要?什么时候我应该使用小丑?通过多次试错,我完善了自己的策略,从我刚开始时的 1000 分是一个不错的分数,到能够持续达到 20000 分以上,甚至几次达到数百万分。然而,尽管我通过我崭露头角的摇滚天赋获得了许多荣誉(我说的“许多荣誉”是指“许多浪费的时间”),我总是想知道是否有办法表面上“解决”这个游戏,采用最佳策略,一个统治所有人的策略。这个游戏有超过 20 万亿种可能的状态,而我并没有我想要的那么强的数学背景,这个任务看起来总是令人生畏。

双重深度 Q 学习

格雷格·苏尔马出场了。格雷格·苏尔马不认识我,我也不认识他,但不管出于什么原因,当我读到他的 Medium 文章关于使用机器学习来训练一个 AI 玩雅达利游戏达到高于平均水平时,我想到了尝试用 Lock N' Roll 做同样的事情。也许有办法解决这个游戏?我是一名受过 Python 编程艺术训练的数据科学家,但我没有多少强化学习的背景。我的专业更倾向于预测时间序列。

尽管如此,我还是决定接受这个挑战。尽管数学家可能会拿着铅笔和纸坐下来,试图证明一组最佳规则,以最大化潜在的总点数,但这种方法涉及训练代理多次玩游戏,每次都评估游戏的游戏性,并形成策略,以最大化每次移动获得的奖励或远离惩罚它的移动。它怎么知道哪种移动是哪种?程序员告诉它。这个过程通常被称为强化学习。

在强化学习技术的保护伞下,有深度 Q 学习(DQL)。DQL 使用神经网络模型来“记忆”人工智能过去的动作,并通过参考其记忆来系统地重新训练自己。然后,它通过不仅预测它将立即获得的奖励,而且预测该移动将导致的下一个状态,并对在该未来状态中获得的预测点数应用折扣奖励,来决定它的未来移动。通过多次重复这个过程,并在初始训练阶段使用更多的随机探索,它在其内存中串连起足够的奖励和贴现的未来奖励,以形成“马尔可夫链”,这是一个贝叶斯概念,我认为这是游戏的潜在路径。随着足够多的马尔可夫链形成,人工智能可以开始形成一个关于最佳游戏的理论。

当可用的移动和需要考虑的变量达到一定的规模和复杂性时,双深度 Q 学习(DDQL)就变得必要了。DDQL 系统地“解除”某些移动,由于潜在学习功能中次优收敛的可能性,AI 开始错误地偏向 DQL。无偏过程是通过第二个神经网络(目标网络)完成的,它有助于平衡代理可用移动的范围,因此它不断学习什么移动实际上是最好的。这两个网络系统地同步在一起。代码如下所示:

在我看来,这是一个教授 AI Lock N' Roll 的完美框架,这需要提前考虑你当前骰子放置最可能导致的移动,我很快就知道我需要使用双重深度 Q 学习,而不是深度 Q 学习,因为训练过程中涉及到无数的复杂性。

我面临的一个挑战是弄清楚如何让计算机知道某些移动并不总是可用的。我在网上找到的关于使用 DDQL 的例子并不清楚如何解决这个问题,在线论坛上的许多讨论似乎表明,该框架只有在不需要限制动作空间时才可能实现,即所有动作在任何时候都是可用的。因此,我决定总是让人工智能从 16 步棋中选择,标记为 0-15。根据它决定采取的行动,下一个可用的骰子(按字母顺序排序)被放置在最接近所选骰子的可用空间上。如果没有骰子可用,如果已经赢得了一个小丑,将使用它。每次放置芯片后,它都会锁定并滚动。这些都是重要的限制,在我自己尝试了这些限制后,我发现获得 5,000-12,000 分是一个相当稳定的结果,但我不能获得更多的分数。

它每走一步得到的奖励是它走一步得到的分数,如果这步棋导致游戏结束,它的惩罚是-500。

我还需要处理一个很大的观察空间,这个空间造成了很多冗余。例如,因为我需要通过神经网络传递一个数字向量,所以使用了一个热编码技术来表示棋盘上的每个空格是如何被覆盖或未被覆盖的,以及哪些骰子当前可用。这意味着使用的四个变量是红色、蓝色、绿色或黄色骰子当前是否在任何给定的空间上。如果在那个空间里有一个红色的骰子,指示其他三种颜色的变量将总是零。这只是为了代表棋盘上一个空间的颜色;还有棋盘上的数字和玩家可以使用的骰子和玩笑。我想不出在这些特征之间建立顺序关系来消除编码过程中的冗余。

为了解决这个问题,我使用 PCA 将观察空间的维度从 193 减少到 154,同时保持超过 99.95%的数据集原始方差。我也尝试使用线性判别分析,但最终选择了主成分分析减少。我仍在学习其他可能更适合这种情况的减排技术。

然后,我没有意识到我会有一个问题——我的硬件。训练这该死的东西花了很长时间。为了让电脑播放并记住 50 万次,我不得不等了三天多。更不用说我很快看到的所有失败的实验都是行不通的。我敢肯定,我的 Windows Surface 笔记本电脑对于这个问题来说不是最佳选择,因为它在训练时达到了非常高的温度,也许我可以编写一个时空复杂度更低的实际游戏,但在我获得更多计算能力之前,我会让 50 万次训练迭代的结果保持原样。

郑重声明,我不认为我对这些问题的任何解决方案是完美的。我刚刚进入这个领域,意识到我还有很多东西要学习。然而,也许我分享我的一些挑战可能对其他人有用,或者我可以从关于潜在改进的评论中学习。

结果

结果喜忧参半。我认为有明确的迹象表明我的人工智能学会了,但远没有我希望的那么多。在 50 万次尝试后,它平均可以得到 200 分,并且经常超过 1000 分。它的最高分是 3000 多一点。手动测试代理时,我注意到它可以识别一些模式中的一些组合,这将为它赢得巨大的分数,但它很难识别好的移动,即使是对我来说很明显的移动。它和我的水平相差甚远,但从统计数据来看,它明显好于 random。一个随机代理平均 165 左右,很少得分超过 1000。

作者图片

random score mean: 164.47355516637478
ai train mean: 188.473811164714
ai non-random train mean: 199.63831736189402
Ttest_indResult(statistic=-4.57018461548957, pvalue=4.906525386975824e-06)
Ttest_indResult(statistic=-6.165806097995396, pvalue=7.170408706268266e-10)

在未来,我想用更有效的简化过程来完善这种方法,用遗传算法来寻找最佳神经网络,用一种方法来限制任何给定状态下的动作空间,以便人工智能正在玩的游戏更能代表真实的游戏体验,并在我的硬件中增加计算能力,以训练它进行至少 100 万次或最多 500 万次迭代。然而,对于我第一次尝试创建一个学习代理(从建立到现在只花了我一个月的空闲时间),我很满意我构建了一个学习的代理。

我打算最终创建这个应用程序的版本 2,但是现在,我要休息一下。如果您感兴趣,下面是包含所有代码的 GitHub 资源库。

https://github.com/mikekeith52/LockNRoll

强化学习 Q-从零开始用非法动作学习

原文:https://towardsdatascience.com/reinforcement-learning-q-learning-with-illegal-actions-from-scratch-19759146c8bf?source=collection_archive---------6-----------------------

这是一个井字游戏的例子,展示了当某些动作对于特定的状态是禁止的时候,如何编写 RL Q-learning 算法

井字游戏动画

有一天,我看着我 18 个月大的儿子学习如何用勺子吃饭,我意识到,一旦他的碗空了,他会立即放下勺子,向我要食物。他根据碗中食物的位置和数量来调整拿勺子的方式,而只要没有食物了,他自然不会做任何无用的努力。这让我想起了我遇到的一些 RL 问题:在一些特定的情况下,并不是所有的行为都是允许的:例如,当电梯已经在顶层时,它永远不能上升,当已经达到最低温度时,HVAC 系统将总是停止冷却。

在本文中,我将以井字游戏为例,构建一个基于游戏规则存在非法行为的 Q 学习算法。接下来,你将会读到:

  1. 井字游戏的 RL 环境。
  2. 带有非法行为的 Q 学习算法。

所有的代码都在我的 Github 上,以防你需要更多的细节。

井字游戏环境

井字游戏或 Xs 和 Os 是两个玩家的游戏,他们轮流用 XO 标记 3×3 网格中的空间。成功在水平、垂直或对角线上放置三个标记的玩家获胜。

我们将使用健身房来设置井字游戏环境。一般来说,RL 环境有四个关键功能:初始化、重置、步进和渲染。

初始化

初始化函数主要是对奖励、done(检查游戏是否结束的值)进行初始化,特别设置了动作空间和观察(状态)空间。在井字游戏中,两个玩家(让我们称他们为玩家 O 和玩家 X)中的每一个都可以采取 9 种可能的行动:他想要标记的网格之一。一个状态由一个 3×3 的数组表示,其中每个元素可以有三个可能的值:0 表示未被标记,1 表示已被玩家 O 标记,2 表示已被玩家 X 标记)。注意,并非所有状态都允许所有动作:玩家不能标记已经被占据的格子。

import gym
from gym import spaces
import numpy as npclass TicTacToe(gym.Env):
    metadata = {'render.modes': ['human']}def __init__(self):
        super(TicTacToe, self).__init__()
        self.states = np.zeros((3,3))
        self.counter = 0
        self.done = 0
        self.reward = 0
        self.action_space=spaces.Discrete(9)
        self.observation_space=spaces.Box(low=0,high=2, shape=(3,3))

重置

重置功能旨在将环境设置为初始状态。在我们的示例中,我们简单地将完成和奖励值设置为零,并将状态设置为游戏板上没有任何标记。

def reset(self):
        self.states=np.zeros((3,3))
        self.counter = 0
        self.done = 0
        self.reward = 0
        return np.array(self.states)

步骤

阶跃函数是动作的函数,它可能是环境中最重要的部分。它代表了环境状态将如何随着一个给定的行动而转变,以及我们将获得什么样的回报。在井字游戏中,一旦执行了一个合法的动作,就会有四种不同的状态:玩家 O 赢,玩家 X 赢,游戏继续或者没人赢但游戏结束。

由于篇幅限制,这部分代码被省略了,但是实现非常直接:我们只需要在每个动作之后检查游戏是否继续,如果不是,谁赢了。此外,如果玩家 O 赢了,我们设置奖励为 100,玩家 X 赢了,奖励为-100。在游戏没有结束的情况下,我们对中间步骤不给予任何奖励。请在我的 Github 上查看详细信息。

提供;给予

render 函数将环境渲染到屏幕上。例如,它打印游戏的以下报告:

作者图片

q-用非法行为学习

一旦井字游戏的环境设定好了,我们就真的要训练代理人(玩家)了。在这个例子中,我将使用最基本的 RL 算法 q-learning:在一项工作中,Q-learning 寻求学习一种使总回报最大化的策略。它是一种表格方法,创建形状[状态,动作]的 q 表,并在每次训练后更新和存储 q 函数值。训练完成后,以 q 表为参考,选择奖励最大化的动作。请注意,我们游戏中的每个状态都由一个 3 乘 3 的数组表示:我们首先需要一个函数(state_to_number)来将每个状态更改为一个整数:

def __init__(self,alpha = 0.1,gamma = 0.6,epsilon = 0.1,epochs=5000):
        self.alpha=alpha
        self.gamma=gamma
        self.epsilon=epsilon
        self.epochs=epochsdef state_to_number(self, state):
        state = state.reshape(9)
        number = 0
        for i, num in enumerate(state):
            number += num * 3 ** (len(state) - i - 1)
        return int(number)

回想一下,在我们的井字游戏环境中,q_table 不能到处填充:玩家不能标记已经被他自己或他的对手标记的网格。在这种情况下,我们简单地以下面的方式屏蔽禁止的动作:我们将表中相应的元素设置为 nan,并通过忽略相应行中的 nan 值来选择 argmax。有人建议设置一个很大的负值作为无效行为的奖励,但这不是一个好主意,因为无效行为仍有可能以很小的概率被选择。

def learn(self,env):
        if env.env_name=="tictactoe":
            self.q_table = self.q_table(env)
            for i in range(self.epochs):
                state = env.reset()epochs, reward, = 0, 0, 0
                done = Falsewhile done !=True:
                    if random.uniform(0, 1) < self.epsilon:
                        action = env.action_space.sample()  # Explore action space
                        #forbiden ilegal action
                        while state[int(action / 3)][action % 3] !=0:
                            action=env.action_space.sample()
                    else:
                        action_value_list=self.q_table[self.state_to_number(state)]
                        for action,action_value in enumerate(action_value_list):
                            if state[int(action / 3)][action % 3]!=0:
                                action_value_list[action]=np.nan
                        action = np.nanargmax(action_value_list)  # Exploit learned values
                    next_state, reward, done, info = env.step(action)
                    old_value = self.q_table[self.state_to_number(state), action]
                    next_max = np.nanmax(self.q_table[self.state_to_number(next_state)])
                    new_value = (1 - self.alpha) * old_value + self.alpha * (reward + self.gamma * next_max)
                    self.q_table[self.state_to_number(state), action] = new_value
                    state = next_stateepochs += 1

我们显示了训练集的奖励的累积平均值,我们可以看到奖励收敛到大约 80%,也就是说,玩家 O 有 80%的机会赢得游戏。并且当然在以后的游戏中不会采取无效动作。

作者图片

结论

在这篇文章中,我用井字游戏展示了当一些动作是非法的时候如何对待 RL,以及如何在训练中用一个从零开始构建的简单 q-learning 示例来屏蔽它们。必要的屏蔽步骤防止代理在学习中无效。

强化学习& SushiGo!第二部分

原文:https://towardsdatascience.com/reinforcement-learning-sushigo-pt-2-ca636a0b0a?source=collection_archive---------62-----------------------

如果你还没有检查过这个项目的 Pt.1 ,请在阅读这个之前快速浏览一下!

三文鱼 Nigiri,作者图片。(鸣谢:Kiwiwii)

上次我们讨论了用 RL 来分析寿司围棋的博弈。虽然学习算法是相对有效的,但实现对原始游戏进行了一些简化。也就是说,代理人在每场游戏中只玩一轮而不是三轮,一副牌中没有布丁和筷子,并且代理人不记住前一手牌中的牌。这一次,我们将添加一些额外的功能,看看学习进展如何!

添加布丁和内存

为了在游戏中添加布丁,玩家需要玩 3 次才能完成一场真正的游戏,因为布丁分数只会在游戏结束时添加。这在理论上应该“平滑”胜率曲线,因为玩家需要在 3 轮结束时获得最高分。

另外,我为一个深度学习代理增加了内存容量。也就是说,代理可以记住它以前的手牌,所以它知道什么样的牌还在外面。

4 名玩家,包括布丁&每场比赛使用 3 轮。底部的“回合”表示 200 次训练+ 200 次评估游戏

似乎随着随机玩家的消失和记忆代理的加入,基于规则的玩家变得最强。基于规则的播放器已根据上次实验后的结果进行了调整,并根据优先级进行抽签:

  1. 芥末
  2. 鱿鱼 Nigiri
  3. 三文鱼酱
  4. 生鱼片
  5. 布丁
  6. 天妇罗
  7. 3 Maki
  8. 2 Maki
  9. 1 Maki
  10. 饺子
  11. 鸡蛋 Nigiri

这个简单的规则打败了两个“努力”的深度学习玩家!如果你不知道如何玩这个游戏,这个图表可以作为一个很好的等级列表。

两个玩家

在让深度学习代理输给基于规则的玩家之后,我开始思考为什么会出现这种情况。从理论上讲,深度学习玩家复制基于规则的玩家的策略应该没有问题,而且他们有能力做得更多。其中一个可能性是,在 4 人游戏中,奖励发生的频率较低,尤其是当玩家表现良好的时候。因为只有赢得一场比赛才会产生积极的回报,没有导致胜利的好棋根本不会得到回报。这可能导致收敛困难。

解决这个问题的一个方法是减少对手。玩家少意味着代理商更容易赢,给它更多的学习机会!

两个玩家,无视筷子

深度学习代理似乎能够在单挑中击败基于规则的玩家!更少的玩家意味着更少的功能,因为代理不需要查看更多玩家的棋盘,记忆更少的手牌。结果,玩家能够发现更多的图案。

记忆

该游戏的主要挑战之一是记忆以前的牌局。如果你在捡生鱼片或芥末之类的东西,你需要知道剩下的牌里有多少生鱼片和芥末。我个人很难知道所有钥匙卡的号码,因为你还需要检查其他玩家拿了什么。

代理人显然可以记住以前的牌局,但在利用这些信息时可能会有问题。在之前的手里见过多个生鱼片,说明你从现在的手里拿起生鱼片更顺手。让我们看看代理是否能有效地利用这些信息。

两个玩家,有记忆对没有记忆

在实验过程中,似乎两个玩家都没有明显的优势。注意,没有记忆的代理在开始时更有效,因为较少的特征更容易学习关系。不清楚这些记忆是否对特工有帮助。

筷子

筷子确实有点讨厌。正确使用筷子可能是游戏中最难的部分,因为正确的时间很难确定。对于深度学习代理来说,这就更难了。拥有筷子开启了行动的可能性。如果选择用一双筷子,一次可以挑两张牌!因为有 12 张不同的牌,所以可能的组合数是 12 * 13 / 2 = 78!(两张卡的顺序无所谓)。如果你不用筷子,那么你可以选一张 12 的牌。基本上,使用现有的模型架构,让筷子将行动空间从 12 增加到 90。

此外,由于你并不总是有筷子,学习哪些组合很难得到的数据。让我们看看代理在双人游戏中的表现

两个玩家,用筷子

不幸的是,我们的代理人无法击败基于规则的玩家,甚至基于规则的玩家根本不会使用筷子!拥有大的行动空间当然导致了学习的退化。

冷启动

我们要评估的最后一件事是冷启动/热启动的想法。由于代理需要赢得游戏来学习,如果它从未赢得任何游戏,它将根本不会学习。想象一个代理人和一个真正优秀的人比赛,代理人赢的很少,所以它会学得很慢。在另一个极端,如果代理人与某人对弈非常糟糕,任何动作都会导致胜利,因此代理人根本不学习。

为了评价冷启动的效果,我在一个状态下有两个没有记忆的深度学习代理。他们中的一个玩了 40k 游戏,而另一个没有。

两名球员,训练 vs .冷启动

“新”代理在实验开始时有一段艰难的时间,但在大约 10k 场比赛后,它能够回升其胜率。有趣的是,预先训练的代理人后来能够重新获得优势,并在整个实验中保持良好的领先优势。

结论

第一部分展示了强化的力量,第二部分展示了它的一些弱点和局限性。

虽然行动奖励结构对于模拟类似人类的学习非常有用,但在现实生活中,奖励往往更加复杂。在这个游戏中,代理人只把赢得一场游戏作为奖励。然而,对于人类来说,像做芥末+鱿鱼套餐、成功拒绝其他玩家的生鱼片这样的事情本身就很有价值。这有助于人类更快地学习游戏,但从长远来看,也让我们更有可能做出次优决策。使用简单的奖励有助于代理人只为胜利而战,但收敛肯定会很棘手,尤其是在多人游戏中。

动作空间的大小对收敛也有重要作用。当行动空间变得太大时,代理很难画出特征和输出之间的关系。在我们的案例中,对可能的组合使用一个热编码似乎不足以让代理学习如何正确使用筷子。也许把筷子当作连续挑选两张卡片的工具会更容易学习。

最后,这个实验显示了 RL 对噪声的脆弱性。所有的特征输入都应该在一定程度上影响代理的决策,但有些特征不是很有用,比如其他玩家的棋盘。在数据量很小的情况下,有很多对手会让代理“分心”去学习什么是真正重要的。相比监督学习,我觉得 RL 对输入更敏感,当然也需要更细致的架构设计。

就是这样!我希望你喜欢这篇文章,并且学到了一些关于强化学习的东西。一如既往,这里是回购。请在评论中告诉我你的想法!

强化学习——用 Q-Learning 教出租车开车

原文:https://towardsdatascience.com/reinforcement-learning-teach-a-taxi-cab-to-drive-around-with-q-learning-9913e611028f?source=collection_archive---------10-----------------------

初学者的强化学习

用 OpenAI-gym 和 Python 温和地介绍 RL

来自彼得卡斯普日克的 Unsplash

为了娱乐和工作,我经常玩机器学习。我用 PyTorch 和 TensorFlow 为 NLP、视觉和医疗保健建立了深度学习模型。然而,强化学习对我来说仍然是一个谜,阅读大量关于 Deepmind、AlphaGo 等的内容非常有趣。现在有多一点时间,我决定深入研究 RL,试图了解基本知识。

我的选择是使用一个简单的基本例子,python 友好的,OpenAI-gym 是一个非常好的框架。我选择的玩具例子是出租车环境。这个环境很简单:

有 4 个地点(用不同的字母标记),你的工作是在一个地点接乘客,在另一个地点让他下车。你成功下落获得+20 点,每一个时间点损失 1 点。非法上下车行为还会被扣 10 分。

OpenAI taxi-V3 环境。作者图片

如文件所述,你开出租车。桌子的四个角上有四个位置。一个乘客在一个地方等出租车,你必须把他送到指定的地方。你每走一步都会得到奖励,在旅途中会受到较低的处罚,如果你在错误的地点让乘客上车或下车,则会受到较高的处罚,但如果你成功了,你会得到一大笔奖励。简单得要命。

在 RL 中,一个州是…你的代理人(你的出租车)所在的州。在出租车问题中,状态由网格上的位置(0 到 4 之间的一行和一列数字)、从四个选项中让乘客下车的位置以及可以在四个位置之一或出租车内的乘客来描述。

如果你数得好,那么我们有 5x5x5x4=500 个可能的状态。

行动

现在的行动。总共有六种可能的行动。有了你的出租车,你可以试着向四个基本方向移动:南、北、东或西。这并不意味着这一举动是允许的,也是不可能的。还有另外两个可能的动作,它们是总共六个动作的拾取或放下。

  • 0 =南方
  • 1 =北方
  • 2 =东部
  • 3 =西方
  • 4 =拾取
  • 5 =衰减

请注意,如果您试图移动到未经授权的位置,您不会受到更多处罚。例如,在上图中,你不能移动到西方,但你仍然得到-1惩罚,不多也不少。当你试图移动到 5x5 网格以外时,情况也是如此。然而,如果你试图在错误的地点让乘客上车或下车,你将受到严厉的处罚。

玩 OpenAI-gym 和 python

既然我们已经描述了环境,是时候用 python 来玩一玩了。OpenAI 为我们提供了一个已经完全编码的健身房环境,所以这个任务非常简单。

翻译

第一步是渲染随机环境。

随机环境样本状态

通过调用reset方法state = env.reset()也很容易生成一个新的环境。

四处走动

当你试图在一个环境中玩耍时,你必须告诉它你想做什么,你采取什么行动。例如,在上面的例子中,你可能想去北方。北方行动的代号是1,所以在这个方向上执行一个步骤我们称之为

这个调用返回一个元组(204, -1, False, {'prob': 1.0}),其中的组件是

  • 状态:环境的新状态,编码从 1 到 500
  • 奖励:你行动得到的奖励。-1 表示你只是移动了
  • done :一个布尔值,表示我们是否成功地放下了之前搭载的乘客
  • 信息:与我们的使用无关的信息

请注意,我们可以选择随机选择一个动作。gym框架为我们提供了一个有用的方法。

Q-learning 的温和介绍

好吧,那么 Q-learning 是关于什么的呢?我知道为了训练深度学习模型,我需要大量数据,可能是带注释的数据,我需要一种方法让模型学习一个函数,以最小化训练数据和预测之间的误差。然而,强化学习没有相同的方法。在这里,我们需要我们的代理探索它的环境,让它决定采取哪种行动来最大化它的预期回报。所以现在,我们看到了 RL 的一个基本概念,它是探索(环境)和利用(他所学到的)之间的权衡。

现在,你如何探索一个新的环境并利用你从经历中学到的东西?想象你在迷宫里。你知道有一个出口,你必须找到它。想象一下,你有一个很好的记忆,一个足够大的记忆来记住你最后的许多动作。考虑到这一点,你可以想象你可以重建一个迷宫的精神图像,以便能够后退一步,探索一条通往出口的新道路。当然,如果你设法用糖果找到出口,你会得到奖励:)你刚刚构建的是一种表格,你确切地知道该做什么,从迷宫中的任何地方去哪里找到出口。在 RL 中,这个表被称为 Q 表。

Q-table 是为每对(state, action)找到一个Q-value的表格。对于每一个state,我们可以有几个可能的actions,所以有几个Q-values。目标是学会为每个state选择更高的Q-value,这意味着选择最有机会获得最大奖励的action

要开始学习,一般会随机初始化Q-table(或者初始化为 0)。然后,代理通过随机选择一个动作来探索它的环境,并为它的动作获得奖励。这个想法是让代理探索环境并获得一些奖励。但是过了一段时间,我们需要利用收集到的信息。从给定的state中,代理必须计算其可以采取的每个行动的预期回报,并选择具有最大预期回报的行动:

预期回报

RL 的核心由贝尔曼方程给出,它告诉我们如何更新Q-tableQ-values:

q 值更新

在哪里

  • α 是学习率
  • γ 是一个折扣因子,给予下一个奖励或多或少的重要性

代理通过查看某个action的奖励和下一个state的最大奖励来学习在state中应该采取的正确行动。直觉告诉我们,较低的贴现因子设计了一个贪婪的代理人,他想要立即的回报,而不是向前看。

探索与利用

我们已经提到过几次了,但是我们现在更清楚地看到了探索和开发之间的权衡。如果我们让代理总是随机选择一个动作,它最终会学习Q-table,但是这个过程永远不会有效率。相反,如果我们只选择一个基于最大化Q-value的动作,代理将倾向于总是采取相同的路线,过度适应当前的环境设置。此外,它将遭受巨大的变化,因为它将不能在另一个环境设置中找到适当的路由。

为了防止这两种情况发生,并试图找到一种折衷方案,我们添加了另一个超参数εϵ,它是我们选择随机行动而不是根据Q-table计算的行动的概率。玩弄这个参数可以让我们找到一个平衡点。

用 python 实现

我知道你在找什么,所以首先这是代码

简单吧?但正如它看起来令人惊讶的那样,这段代码从探索一个环境中学习了一个Q-table,没有历史的良好标记的数据,就像一个基本的智能会做的那样。厉害!

但是为了说服你,为什么不玩我们的新玩具呢?

结论

哦耶!我们刚刚完成了第一个 RL-toy 代理。理解 RL 的基础既不需要复杂的数学也不需要困难的算法。只用几行代码,我们就能训练一个代理人玩出租车游戏。

旅程才刚刚开始。现在,我们可以使用各种超参数,看看它们对训练和代理性能的影响。为了评估性能,你需要做的就是和代理一起玩几次(编写一个简单的 for 循环,不要用手做!)并查看超参数如何影响平均预期奖励和完成一集的平均步骤数。

此外,请继续关注 RL 和 DL 的共轭世界,在即将到来的帖子中用 DL 模型替换Q-table

强化学习被发现

原文:https://towardsdatascience.com/reinforcement-learning-uncovered-135509cbbc4c?source=collection_archive---------25-----------------------

强化学习简介:从主体和环境到状态值和贝尔曼方程

让事情变酷的随机图像!(图片来自 pixabay 上的 geralt

介绍

强化学习是人工智能中最激动人心和最有前途的研究课题之一。在过去的几年中,它被反复证明是最有效的研究领域之一,能够在许多环境中击败人类。在 2016 年一名 RL 特工在围棋比赛中击败了 Lee Sedol,而在 2019 年在星际争霸比赛中进行了大师级的比赛。但是强化学习到底是如何工作的呢?

在本教程中,我们将以最简单的方式讲解强化学习的基础知识,试图在没有复杂公式的情况下分解和证明主要概念。我是一名数学家,所以我会死一点点,但希望最后,你已经很好地掌握了什么是强化学习,更重要的是,你想知道更多!

毕竟 RL 没那么复杂。相信我,就像这个世界上的所有事情一样,这只是一个真正掌握基础知识的问题。

代理和环境

在最核心的地方,强化学习是基于一个代理,它与一个 环境反复交互。

代理人可以代表一切能够行动的事物:一个扑克玩家,一个股票市场的交易者,我的可怜的,老的,坏的,累的,打扫我房子的机器人吸尘器。相反,对于我们的代理人来说,环境是所有的外部事物:所有的对手玩家、庄家、扑克玩家的牌、所有投资于市场的人以及交易者的市场本身、我的房子和房子里的所有物品。

代理在环境中采取的每一个动作都会导致两件事情发生:

  1. 一个奖励被提供给代理人,它量化了代理人在特定情况下采取行动所获得的好处。
  2. 环境变化:我们的行为导致环境从某个特定的情况、条件、环境(我们将使用状态这个术语)转移到另一个通常不同的情况。

这就像在电子游戏中:当你(代理人)杀死(扮演)游戏(环境)中的坏龙时,你的技能点增加(获得奖励),龙不再是你游戏的一部分(游戏状态改变)。

我确信你们中的许多人都熟悉下面的图表,它描述了代理和环境之间的循环。

强化学习循环:在时间 t,主体采取行动,环境返回奖励和新状态。在时间 t+1,循环重复。

在这一点上,一个合理的问题可能是:好的,好的,太好了,伙计!但是这种媒介与环境的相互作用会持续多久?嗯,我们有两个选择:

  • 代理人和环境之间的互动永远持续。这可能是一个温度调节器的例子,每天为房间设定合适的温度(理论上没有预定的结束时间)
  • 代理和环境之间的交互在某个点结束。这可能是游戏纸牌的情况,例如,当我们(代理)赢或输时,游戏结束。重要提示:在这种情况下,总会有一个特定的状态代表游戏的结束。这被称为结束状态,并且游戏直到结束状态的单次运行被称为一集

由于第二种情况更直观,也更容易解释,我们将重点关注它,但请记住,一切都可以自然地扩展到无止境的框架。

退货和政策

现在,作为这个世界上的每个人,我们的代理人对最大化其回报感兴趣(每个人都想变得富有,对吗?).但是我们的经纪人是个聪明人!因此,它的目标不是最大化此时此地获得的回报(我们称之为即时回报),而是最大化他现在和未来与环境的所有互动中获得的回报。我不认识你们,但是如果我能在接下来的 20 天里每天得到 2 美元,我现在不会选择 10 美元!所以,代理人真正想要的是采取行动,从现在开始最大化及时收到的回报。事实上,这个数量被称为**回报,因此代理人希望最大化其回报。**下面是我们刚才所说的数学翻译。在下面的等式中, t 代表我们现在所处的当前通用时间步长,而 T 是我们到达结束状态的时间步长。

在这样做的时候,我们可怜的代理人仍然有一个问题:在许多许多的环境中,它通过采取一些行动所达到的状态或者它得到的回报有很高的不确定性。想想吧。如果我们(代理人)在市场(环境)上买入(行为)一只股票,我们不确定第二天价格会涨(正回报)还是会跌(负回报)。如果我们(经纪人)向那个女生(环境)表白我们的爱(行为),我们不是完全确定答案对吗?形式化强化学习的人设想了在主体-环境交互过程中出现的三种不确定性:

  • 奖励。通过应用我们的行动,我们不确定环境会提供一个独特的和确定的回报。
  • **下一个州。**通过应用该动作,我们不确定环境是否会以唯一且确定的下一状态移动。
  • 我们可能会以一种不确定的方式在环境中行动。在给定的状态下,我们可能根据某种概率选择向左或向右。

其中,代理只能控制最后一个。虽然奖励和下一个状态确实依赖于环境动态,但是代理只能决定如何行动。在每种状态下,它可以通过选择一个动作来确定性地进行游戏,也可以在多个可能的动作中随机选择。我们会说代理有一个策略,这意味着它有一个在整个游戏中使用的策略。在一个给定的状态下,策略告诉代理将选择哪个动作,以及它将采取该动作的概率。

另一个随机图像让事情更酷!(图片来自图片上图片

国家的价值

所以,我们意识到我们的代理对它的未来有不确定性!但是它如何处理它们呢?可怜的家伙,它只是想最大化它的回报!嗯,诀窍不仅仅是考虑一个与特定的行动、奖励和下一个状态序列相关的可能回报,而是考虑与所有可能发生的行动、奖励和下一个状态的特定序列相关的所有可能回报。代理人需要考虑从所有可能达到的未来可能收到的所有回报,并根据其发生的概率对其进行平均。

从当前状态开始,我们可以暂时把它想象成一棵树的根,代理模拟一个可能的动态演变(即动作序列、奖励和下一个状态)直到一集结束。当到达树的末端并遍历树时,代理将其从一个状态到另一个状态获得的奖励乘以该动态发生的概率(即,选择该动作、获得该奖励并在该状态结束的概率)。一旦到达结束状态,代理通过考虑不同的可能演化创建另一个分支,并重复该过程。这是一次又一次的重复,直到所有可能从我们的状态导出的分支都被认为是。最后一步,对所有这些量求和以计算加权平均值,该平均值定义了****状态的值,因为它表达了根据所有可能的未来回报和相关不确定性,代理在该状态下有多好。

注意重要的一点。我们在上面说过,在三种类型的不确定性中,代理人只能控制其行为方式**(即其政策),而下一个状态和回报确实取决于环境动态。因此,当我们评估状态的价值时,我们可以确定某个策略并计算所有可能的下一个状态和奖励的价值。换句话说,**我们可以使状态值依赖于策略,这样改变策略就改变了状态值。对于住在你里面的数学家来说,这写为:

它大致是这样的:当我们玩策略 pi 时,状态 s 的值等于我们从考虑所有可能发生的未来开始玩策略 pi 得到的所有收益的加权平均值。

现在,让我们通过一个例子来关注这些概念。假设你正处于决定是否向那个女孩(或男孩)表白你隐藏的、巨大的、疯狂的爱情的境地。你有两个选择(行动):你申报,你不申报。如果你说有 0.5%的可能性,她会同意。如果她说是,你会很开心,所以这会给你+100 的回报。但如果她说不,你会难过,对自己多一点不自信,对人类和人际关系多一点不信任:我们可以用一个满分 60 来量化所有这些奇怪但充满激情的情感旋风。另一方面,如果你选择了另一个选项,并且没有向那个女孩表白,你生活在一个永恒的假设场景中,但是避免了自己遭受痛苦的风险,所以以 1.0 的概率你得到了(比如说)-10 的回报。那么,这种状态的价值是什么?嗯,我们还需要明确政策。的确,保单的数量是无限的,因为不仅你(代理人)可以选择申报或不申报,你也可以根据某种概率申报或不申报。为了简单起见,让我们比较来自两个确定性策略的两个最简单的情况:

  • **你坚定地选择声明。在这种情况下,出现+100 的可能性为 0.5,出现-60 的可能性为 0.5。通过展开树的分支,我们得到 100*0,5 = 50 和 0,5 *(60)=-30。如果我们加起来,我们得到 50-30 = 20。此外,我们需要考虑另一个分支。但是由于另一个分支出现的概率为零(因为我们的策略不允许选择它),所以它的返回值为 0(应该是 0 (10))。因此,再次求和,我们得到状态值,在这种情况下是 1 * 20+0 (10)= 20。没那么糟!
  • *玩家坚决选择不声明。在这种情况下,推理是相似的,我们有一个确定性的返回 0 * 20+1.0 (10)=-10

就是这样!但是 20 比-10 大,所以这里有一个数学上的解释,为什么你应该永远追求它!

贝尔曼方程

贝尔曼方程是本教程的最后一步。简单地说,它代表了一种表达状态价值的方式,而不需要每次都执行从考虑所有可能的未来中得出的所有计算。在许多设置中,实际上应用我们上面描述的加权平均过程实际上是不可能的:有太多的状态和太多可能的情节发展。以国际象棋为例,状态数大约是 10 * 120(10 乘以 120 乘以 0)。可以想象,计算每个州的可能回报确实需要很长时间!贝尔曼方程来拯救:即使它不能完全解决问题,它提供了一种简化和加速计算的方法。

贝尔曼方程的核心点是,它仅仅根据可以从它到达的下一个状态的值来表达一个状态的值,并且以这种方式忽略所有剩余的未来相关性。换句话说,如果我们知道下一个状态的确切值(实际上也知道直接的环境动态),我们就可以准确地计算出我们现在所处状态的值。从它的表情可以看出这一点至关重要:

你可以注意到,当我们玩策略 pi 时,状态 s 的值取决于即时的环境动态(即我们在 s 中采取的行动,获得下一个状态的概率和获得的奖励)和下一个状态值。

此外,贝尔曼方程提出了一种计算状态值的有趣方法。我们说过,我们现在可以计算状态的值,仅仅是根据可以到达的下一个状态的值。但是这个推理也适用于每一个下一个状态:所以如果我们知道下一个下一个状态的精确值,我们就可以精确地导出下一个状态的值。推理一个状态接一个状态地重复,直到结束一个状态:如果我们知道结束状态的确切值,我们可以计算紧接在那之前的状态的值,并且知道我们可以回到第一个状态。****

最后,重要的是要注意到,即使它加快了计算速度,贝尔曼方程也不能具体应用于真正大型的国家博弈。尽管如此,我们必须至少扫描一次所有可能的状态,,这在许多环境中是禁止的**。**

结论

就是这样。恭喜你成功地走了这么远!希望你喜欢这篇关于强化学习的介绍,就像我在写它的时候一样!到目前为止,您应该对强化学习的基本概念有了相当的了解,并且能够进入下一个级别(..还是插曲!).有问题请随时评论!

回头见,保持黄金!:)

强化学习 vs 贝叶斯优化:何时使用什么

原文:https://towardsdatascience.com/reinforcement-learning-vs-bayesian-optimization-when-to-use-what-be32fd6e83da?source=collection_archive---------4-----------------------

强化学习,贝叶斯优化,最优化,机器学习

RL 与贝叶斯优化方法的比较研究

在 Unsplash 上拍摄的 ThisisEngineering RAEng

来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

优化是大多数机器学习问题的关键。在某个地方,从某种意义上说,我们总是需要优化一个表达式。这可能是一个更大问题的一小部分。但是,优化将会存在。在一般的数学意义上,我们所说的最优化是指找到一个函数的最小值或最大值(如果存在的话)。函数的性质可能有很多种。在本文中,我们将讨论两种优化方法的区别:强化学习&贝叶斯方法。我们不会深入讨论实现的细节,我们的讨论将集中在适用性上&可以应用两种方法的用例类型。

贝叶斯优化——一种无状态方法

考虑如下任意随机函数:

看起来很简单,对吧?现在,如果你被要求找出 f 的最大(或最小)值,那么你可以简单求导并解方程。这是一种传统技术。但是,如果你不知道它的本体或代数形式,也就是说,f 是黑盒,那该怎么办?还有,解决办法是有的。您将启动一组随机参数组合(x1,x2,..,xn)并调用 f 得到不同的 y 值,得到其中的最大值(或最小值)。如果 f 容易调用的话是可行的。但是,如果它是黑匣子并且打电话的成本很高,该怎么办呢?每次调用都会给整个流程带来成本。像上面那样的随机搜索在那里是不可行的。我们必须进行某种智能搜索来检查调用的数量,但我们仍然应该能够找到最大值(或最小值)。贝叶斯方法可以做到。里面的术语很少。

代理模型

贝叶斯方法围绕原始函数迭代地建立代理模型。代理模型不过是一个高斯回归处理器。它用于获得给定的一组 x 值的近似输出(y 值),就像调用了原始函数一样,尽管它并没有被调用。该模型用于搜索参数空间并获得 x 的值,其中 y 是最大的(或最小的)。

采集功能

这是一个度量标准,有助于选择最有可能给出更好的 y 值的下一个 x 值。当然,整个过程本质上是迭代的。采集函数在 x 值的随机数组上进行评估,给出该度量最大值的函数被选为下一个 x 值。在计算该度量时,替代模型被用于 x 值的随机搜索。它不需要调用实际的 f 函数。

你可能会问,“我们对下一个 x 值或 y 值的理解是什么?”实际上,贝叶斯是一种完全迭代的方法。它使用“先验/后验”组合的贝叶斯概念。从每次迭代中,从采集函数 &中获得最佳 x 值,从实际 f 函数(后验)中获得相应 y 值。并且它们被添加到“代理”模型(如先前的),导致模型“更新”。因此,当前迭代的后验被添加到下一次迭代的先验列表中。

最后,我们从先前的列表中获取最大(或最小)值,并将其作为最优值(最大或最小)返回。

现在,问题是为什么我们称之为“无状态”方法?请注意,函数 f 的每次单独调用都是相互独立的。它不依赖于先前调用的一些状态值。当然,它是先验/后验相关的,但它们不是函数的“状态”。它们只是输入/输出值。

寻找复杂深度神经网络的超参数

考虑具有超参数阵列的非常深的神经网络。它可以是每个不同隐藏层的大小、隐藏层的数量、删除层的数量等。当然,训练这样一个复杂的模型需要大量的时间和资源。我们如何从模型中找出能够给出最佳(最大)准确度(分类或回归)的最佳超参数集?我们可以将其视为一个假设的高成本黑盒函数,它接受一系列超参数并返回准确性(分类的百分比准确性或回归的 R2 度量)。该功能涉及用交叉验证集训练模型。我们不能在这里使用随机搜索,因为它将招致大量的时间和资源。贝叶斯优化就是答案。

强化学习——一种有状态的方法

在强化学习(RL)中定义函数本身有点棘手。人们经常对 RL 实际上做什么感到困惑。它除了优化什么也不做。它不直接进行任何类型的分类、回归或聚类,而这些都是典型的 ML 方法。当然,你可以在里面使用 RL,反之亦然。RL 中有两个关键术语:环境 & 智能体

环境

它是存在的虚拟或物理黑盒系统,通过代理执行一些 I/O 操作。代理不知道环境的内部情况。它只是向环境发送信息,并作为回报获得一些输出作为奖励。

功能感知

环境保持其状态,并且在环境中存在状态转换。它接受一些动作并返回奖励作为输出。所以,它可以被认为是一个假设的函数。考虑自动驾驶汽车的使用案例。驱动程序是一个与汽车及其摄像头交互的代理。汽车和摄像机充当环境。当然,汽车有状态。状态可能是其速度和方向的组合,并且这些会不断改变导致的状态转换。代理(驱动程序应用程序)采取“行动”并将这些行动发送给汽车,作为回报,汽车获得一些“奖励”(在这种情况下,奖励可以是汽车前面的物体图像,可以进一步分析)。因此,目的是找出汽车平稳行驶的最佳方向。

我们可以将“环境”表达为一个假设的目标函数 f:

注意,状态总是与函数交换。例如,汽车的当前状态将明显影响其未来状态和奖励。

与贝叶斯方法不同,在 RL 中,由于状态的交换,函数调用相互依赖,因此使其成为“有状态的”。

现在,问题是我们是否像贝叶斯理论一样最大化“环境”?不完全是。这里采用累积的方法。序列的 fRL 的总和实际上是最大的。如下图所示:

所以,总回报实际上是最大化的。理由可以解释。

与贝叶斯方法不同,RL 是一个连续的而不是一次性的过程

RL 用于来自系统的值流的情况,并且在任何给定的情况下,值的最大累积和是期望的输出。

现在让我们看看 RL 中环境的状态转换是怎样的:

有四个州:S1、S2、S3 和 S4。假设以 0.4 的概率采取行动 a1,回报为 r1,环境从 S1 状态变为 S2 状态。类似的逻辑也适用于 S3 和 S4。从 S2、S3 或 S4 可以有其他状态转换。从前面的讨论中,我们可以说 r1 将试图找到(r1 + … + rn)的最大值,即在一个完整的状态转换周期后的总回报(例如:S1->S2->S5->..->Sn)

股票交易用例

考虑一段时间内股票交易的情况。股票是按顺序买卖的,在期末,我们的目标是利润最大化。这里可以应用 RL。我们把股市看作黑箱,每个阶段的“环境”和“利润,可以认为是“回报”。“状态”可以是投资者的位置(根据金融术语,),而“动作”可以是代理(投资者)的当前投资。RL 可以帮助我们决定投资组合的投资策略。它不仅可以获得最大利润,而且每个阶段的状态序列&最优行动将我们引向正确的方向。

由于环境是黑箱,我们无法使用传统的数值或分析优化方法(如寻找导数)。RL 为我们提供了一些特殊的技术来实现这一点。

代理人

代理是与“环境”互动并不断获取回报的程序。它还在每个阶段采取行动。动作作为消息发送到触发状态转换的“环境”。我们可以说代理是驱动程序。您已经看到了上面的状态转换图,其中提到了许多操作。请注意,环境本身不能采取行动,它只接收命令形式的行动。动作完全由代理控制。

模拟环境

像贝叶斯方法中的“代理”一样,我们经常需要对环境建模,即环境功能。实际上,在真实场景中,状态和动作的组合可以产生“计算和组合”爆炸。也许不可能遍历每个状态-行为组合并找出最大值。这就是为什么,可能需要一个模型作为环境的代理。在训练 RL 模型时,这个代理模型必须用来自真实“环境”的输出来训练。这个代理可以在任何阶段针对给定的环境状态给出最优的行动或回报。在生产设置时,这非常方便。你不必经历每一个&每一个状态-动作组合,这会导致执行非常慢。深度 Q 学习、交叉熵、策略梯度是一些常见的环境建模技术。

RL 和贝叶斯方法的比较研究

从上面的讨论中,我们可以在高层次上找出 RL 和贝叶斯方法在应用领域的差异。

贝叶斯是一个一次性优化程序,其中函数的成本是非常重要的标准。

您可以通过一次调用带有最佳参数的函数来获得最佳值。

函数本质上是无状态的,不依赖于调用历史或任何状态值。

在深度神经网络示例中,DNN 模型不依赖于具有不同超参数集的先前构建的模型。每个模型都是相互独立的。记住成本是贝叶斯方法中最重要的标准,当你总是处理重复使用的非常昂贵的东西时,你应该考虑贝叶斯。

贝叶斯方法非常常用于任何机器学习或深度学习模型的各种超参数搜索。

RL 是一个循序渐进的优化过程。环境成本一般不会发挥那么大的价值。

一般来说,你不会通过单个交互(单个函数调用)从环境中获得最优值。相反,你需要打一系列的电话,然后把这些值加起来。最重要的是,叫声相互交换状态,并决定下一个奖励。

函数调用,即与环境的交互相互依赖

在股票交易的例子中,当前头寸(多头或空头)可能会影响你在下一个窗口投资的决定。“位置”就是这里的环境状态。

在这两种情况下,功能或环境对你来说都是黑箱。从分析上来说,你不能优化它。但是,你需要分析,你试图解决什么样的优化问题:它是无状态的还是有状态的?然后决定方法。

在 RL 中使用贝叶斯方法

是的,那也是可能的。如果在任何 RL 算法或模型中,需要一些一次性优化,那么我们可以使用贝叶斯方法。考虑 RL 的深度 Q 学习方法。在那里,一个深度神经网络被训练来模拟状态与 q 值的映射。该网络可能具有许多超参数,并且可以通过前面讨论的贝叶斯方法明确地找到最佳组合。

再保险定价,数据科学方法

原文:https://towardsdatascience.com/reinsurance-pricing-the-data-science-way-a1b35d3ea715?source=collection_archive---------13-----------------------

超额损失再保险定价的介绍以及确定性、随机和核密度估计方法的比较。

斯科特·格雷厄姆在 Unsplash 上拍照

“保险公司的保险”,再保险是一个有趣和富有挑战性的工作领域。多种多样的合同类型和有限的数据历史使得再保险合约的定价在大多数情况下具有挑战性。

随着时间的推移,这些复杂的保险工具出现了不同的定价方法。

我将简要介绍和比较我见过的用于超额损失再保险合同定价的两种方法(确定性解决方案和随机模拟),然后探讨一种不同的方法(核密度估计)。

超额损失分保

超额损失再保险是一种非比例再保险,旨在限制或限制保险公司的“每次风险”或“每次事件”损失。

通常这些合同的报价是提供超出超额部分的最大赔偿额。它们通常作为相邻的“层”提供,并被称为层尺寸超出(xs)连接点

例如,超额损失合同 1m xs 2m 为超过 2m 的损失部分提供了高达 1m 的赔偿:

  • 如果发生 250 万英镑的损失,该合同将提供 50 万英镑的保险
  • 如果发生 400 万英镑的损失,该合约将提供 100 万英镑的保险
  • 如果发生 50 万英镑的损失,该合同将不提供保险

让我们来看看计算再保险层价格背后的一些理论,这将方便地渗透到确定层技术价格的方法中。

确定性方法

我们感兴趣的是计算一层非比例再保险的期望损失成本。现在,让我们假设保险提供超过 a 的 b 的赔偿——也就是说,再保险人覆盖属于区间[ a,(a+b)]的索赔部分。

如果带有概率密度函数 f 的随机变量 X 代表“地面”保险损失,我们可以将再保险合同的损失 Y 定义为:

那么该层的预期损失可以写成:

E[Y] 代表再保险层的技术价格,即保本收取的保费。再保险人通常会将资本成本、管理费用和利润加载费用添加到技术价格中,以得出办公室溢价市场价,但我们现在将忽略这些计算。

求解E[Y]——可能是数值求解——是确定超额损失保险定价的方法,因为总是产生相同的结果。虽然这在理论上是一种干净且令人愉快的方法,但是这种方法也有一些缺点:

  • 它依赖于对基础索赔分布形式的准确假设 X
  • 它要求用户精确地导出这个分布的参数

还有一个隐含的假设,即用户将能够执行复杂的集成。

Python 用户可以访问使数值积分相当简单的功能;只能访问电子表格的从业者可能会发现随机模拟照亮了他们的一天。

随机模拟

解决这个问题的随机方法非常简单,可以简单地概括如下:

  1. 从假设的(适当的)参数化索赔分布中随机抽取一个样本 X
  2. 应用再保险层结构来确定层损失并存储结果。
  3. 重复很多很多次——比如说 10 万次。
  4. 将模拟层损失成本的平均值作为该层的技术价格,即 E[Y] = 平均模拟结果。

虽然简单,但这个过程非常强大:

  • 模拟的结果是结果的分布而不是单点估计,通过足够的模拟,结果收敛到封闭形式的解。
  • 因此,计算均值、方差、峰度和偏斜度等统计数据非常简单。通过确定性方法来实现这一点可能有点棘手。
  • 结果的分布允许我们很容易地得出置信区间和百分位数——这对理解和交流结果很有用。同样,当沿着确定性路线前进时,这可能会有点棘手。

不幸的是,这种方法还要求用户正确地指定和参数化基本权利要求分布;虽然对复杂的集成没有要求,但是用户需要能够从一个发行版中提取样本(多次)。

显然,非参数方法是可取的——如果它能匹配确定性方法和随机方法的性能!

核密度估计

核密度估计(KDE)是一种估计随机变量概率密度函数的非参数方法,通常用于根据数据样本进行总体推断的情况。

KDE 方法与随机模拟方法非常相似,除了我们使用 KDE 来估计基础索赔分布并从中抽取样本。

这听起来很简单,但它非常强大,因为它消除了用户在试图指定 X 的分布(和参数)形式时引入的错误。

我们走吧!

让我们指定一个工作流程来比较不同的方法:

  1. 生成历史基础索赔数据,即 X 的实现。在这种情况下,我们将使用对数正态分布的样本。
  2. 设置超额损失再保险合同详细信息。
  3. 参数化我们观察到的对数正态分布;参数将从(1)中生成的数据中导出。
  4. 计算确定层价格。
  5. 计算模拟层价格。
  6. 计算(KDE)模拟蛋鸡价格。
  7. 比较结果和谈论商店。

在我们继续之前,先说一下对数正态分布…

对数正态分布是随机变量的连续概率分布,其对数呈正态分布。

因此,如果 Y 是对数正态分布,则𝑙𝑛(𝑌)是正态分布,即𝑙𝑛(𝑌)∞𝑁(𝜇,𝜎)。

该分布支持大于 0 的正值,并且是长尾的,非常适合于对可能属于我们再保险层的低频率、高严重性事件进行建模。

在本例中,我们将假设底层索赔分布 X 是对数正态分布。

1.生成数据

在 m 中,让我们从平均值为 15 的对数正态分布中抽取 500 个观察值。

https://gist . github . com/Brad-Stephen-shaw/e73d D7 e 915 a 47 Fe 002 b 8101 a 096420 CB

作者图片

上图显示了从“真实”的索赔分布中随机抽取的 500 个观察值的直方图。正如预期的那样,所有的观察值都是非零的,并且分布向右倾斜。

2.设置再保险合同详细信息

让我们来给一份 500 万×200 万的合约定价,即 a = 20b = 5 。简单。

3.将观察到的对数正态分布参数化

使用生成的数据,通过矩法将观察到的对数正态分布参数化。

作者图片

图表 2 显示了真实的底层索赔分布(蓝色)和根据观察结果参数化的分布(橙色)之间的差异和信息损失。我们稍后会更详细地讨论这一点。

4.确定层价格

让我们继续建设:

  1. 一个 Python 函数,用于对两个边界之间的给定函数进行数值积分。
  2. 描述参数化地面损失分布和再保险层内地面索赔的预期损失的 Python 函数。

确定层成本:598,900。

5.随机层价格

numpy的帮助下,执行随机模拟并得出该层的技术价格非常简单:

随机层成本:599,921。

读者将会注意到最终的层价格非常相似,并应记住随机模拟的结果应在足够重复的情况下收敛到闭合解。

6.KDE 蛋鸡价格

这一次我们将依靠sklearn来执行 KDE,但是从 KDE 对象中提取样本并计算图层成本也非常简单。

KDE 模拟层造价:586920 英镑。

KDE 层成本与确定性的普通随机模拟相差不远——有意思!

7.比较结果和谈论话题

在我们比较结果之前,让我们计算一下“真实的”层成本。

真层成本:630,300。

让我们总结并讨论……

作者图片

我注意到一些事情:

  1. 所有的方法都低估了该层的价格,尽管 KDE 的方法似乎是罪魁祸首。
  2. KDE 的结果并不比确定性模拟或普通模拟差多少。
  3. 确定性方法和一般随机方法都要求用户指定底层的索赔分布。这可以被认为是为流程提供了额外的“信息”,因此我们应该期待更好的性能。

由于 KDE 是非参数的,本质上是“盲的”,我认为它的性能是令人钦佩的,精度的轻微损失是为该方法提供的增加的自由度所付出的可接受的代价。

清谈俱乐部

  • 在现实中,观察到的数据将来自保险公司本身。可用的数据量取决于保险公司的地域、规模、索赔历史和业务范围。更有可能的是,数据将会很少,并且有 500 个观测数据可供使用将是一种罕见的奢侈。
  • 在参数化分布或拟合 KDE 之前,应适当清理历史数据,根据趋势和通货膨胀进行调整,以及删除一次性事件并为中断的核保实践留出余地。与以往一样,在分析中包含大约是调整前附着点大小一半的声明也是一种很好的做法。
  • sklearn中的 KDE 功能包含一个带宽参数,该参数控制数据的拟合。增大参数值会提高 KDE 对象的概化能力,而减小参数值会提高 KDE 对观测数据的拟合。
  • 在拟合 KDE 之前,将“相似的”历史数据汇集在一起,然后应用再保险合同细节,可以为分层价格提供不同的视角。举个例子:
  • 来自多个保险公司的大量英国机动保险索赔可以集中在一起(在适当调整后),并使用 KDE 计算再保险层价格。
  • KDE 方法可用于英国汽车保险公司 X 的历史索赔,以得出分层价格。
  • 保险公司 X 的价格可以与“市场”价格进行比较,并在计算市场价时进行相关调整。
  • 在这种情况下,KDE 方法可能更可取,因为可能很难参数化“组合”分布。

免责声明:本笔记本中包含的所有统计数据和工作方式纯属假设,与实际事件毫无关系。

重塑对抗性机器学习:对抗性 ML 从零开始

原文:https://towardsdatascience.com/reinventing-adversarial-machine-learning-adversarial-ml-from-scratch-c5330595ade0?source=collection_archive---------32-----------------------

理解神经网络的局限性和脆弱性

纳丁·沙巴纳在 Unsplash 上拍摄的照片

当我必须从头开始描述一件事情时,我学得最好!在“重新发明”的文章中,我将尝试描述为自己实现一项技术所必需的数学直觉!在本文中,我们将探索对抗性机器学习的激动人心的世界

首先,让我们为对抗性 ML 下一个工作定义:

对抗性 ML 涉及生成或防御旨在欺骗 ML 模型的输入的方法。

对我来说,对抗性 ML 对现代 ML 解决方案提出了最重要的理论挑战。它允许我们询问我们的网络所了解的决策界限,并迫使我们正面解决我们的模型的局限性。让我们开始吧!

一个痛苦的比喻…

忍耐一下!我觉得这可能是个还算过得去的动力吧!我想解释一下为什么我觉得对抗性 ML 这么有意思。为了说明背景,让我们从一个可笑的派对问题开始:馅饼是馄饨吗?

…隐喻性的问题

让我们来解释为什么这个问题会成为朋友间有趣的争论。从根本上说,它探索了我们对饺子定义的边缘。问题是" Boyardee 主厨是馄饨吗?"让玩笑变得不那么有趣,因为我们都同意(除了偶尔的美食势利者)。Boyardee 主厨是“典型的”馄饨。它与我们头脑中典型的柏拉图式馄饨有着几乎所有的共同特征:它是一种方形的、带馅料的酱意大利面。另一方面,现在果酱馅饼…

…隐喻的答案

人们可以用两种方式中的一种来回答。如果它是一种馄饨,那么馄饨是一种被描述为在一层薄薄的面团中填充的食品。如果不是,那么饺子更确切地说是意大利面食。

无论哪种情况,分歧都很有趣。它迫使我们解释什么样的特征是必要的和充分的,才能被归入馄饨的范畴。尽管 Boyardee 主厨是我们馄饨“决策界限”的中心,但果馅饼却在边缘。这场辩论很有趣,因为分歧揭示了人们不同的决策界限。

…不那么隐喻的答案

谢谢你对我的包容!我认为对抗性 ML 是一种系统的、有理论动机的方式,用来问神经网络这类“决策边界”问题。从一个神经网络和我 100%同意的输入样本开始(例如,“这绝对是 MNIST 数 4!”),我可以问我的神经网络在它声称样本不再来自该类之前需要进行什么编辑(即,“如果这 20 个像素是白色而不是黑色,我会将这个数字 4 称为数字 2”)。

类似于我们的馄饨问题,找到正好在边缘的样本——在类之间的边界——有助于突出我的模型和我可能不同意的地方。对我来说,这就是机器学习“可解释性”和“信任 AI”的症结所在。问题“你认为饺子是什么?”有时最好的回答是将问题重新定位为“在你称它为别的东西之前,我需要对这个饺子做什么改变?”如果我的模型和我不同意哪个输入落在边界的两边,我就不信任它!

在这里,我将通过最简单的过程在模型的决策边界附近生成“对立的例子”。在这个过程中,我们将建立一些关于对抗性机器学习的直觉。我们来看一些代码!

基本的 MNIST 建筑

为了解释对抗性的机器学习方法,我们需要一个目标模型来攻击。

资料组

我想证明,即使对于琐碎的分类问题,对抗性机器学习也是可能的:分类挑战在 ML 文献中一直被认为是“已解决”的。我还希望能够轻松地可视化我们生成的一些对立样本,这在图像数据上是最容易的。理想情况下,我也会避免自己寻找数据来源的问题;我不想要大量只处理数据加载的代码工件。

考虑到这些标准,我们将使用 MNIST 数字数据集 [1]!

体系结构

同样,我希望保持架构简单,这样我们就可以专注于对抗性 ML 的本质。

MNIST 模型训练的简单 Keras 实现。

最终的架构很简单(而且非常线性,注意没有激活)。我们有一个单一的卷积层,后面是最大池。最后,我们展平到一个有 10 个单元的密集层,每个单元对应一个输出类。

CNN 架构示例。

该网络只有大约 10k 个参数,即使没有任何 GPU 资源,也能快速适应。

结果

对我来说,早停是在纪元 3 之后触发的。此时,验证准确度约为 98%。对于这个演示来说已经足够好了!

基本的对抗性攻击

为了建立关于对抗性机器学习如何工作的直觉,我们将为自己实现一个基本的“白盒”攻击,并可视化一些结果!

“白盒”攻击定义

在本次讨论中,我们将重点关注“白盒”对抗性 ML:

“白盒”攻击者被认为完全了解被攻击的模型:架构(例如,它是什么类型的模型?),参数(即权重和偏差项是什么?),以及给定输入的预测值(即,模型对该输入的预测是什么?).

如果人们对非“白盒”攻击感兴趣,请在评论中告诉我!

遵循直觉的梯度

在神经网络训练期间,我们通常会参与一个“梯度下降的过程在每一步,我们都在估计应该如何改变参数,以最小化“损失”(即误差)。“梯度”告诉我们增加或减少重量将如何影响我们的损失,我们在这个损失面上“下降”以提高我们的表现。我们认为输入数据是固定的,输出标签是固定的,权重是可变的。我们更新我们的权重,以减少每一步的误差。

为了生成一个对抗性的样本,我们将只是翻转这个逻辑!我们现在正在寻找与真实数据相似但最终被我们的模型错误分类的输入样本。我们假设我们已经有了一个经过充分训练的、高性能的模型,所以我们的权重现在是固定的。我们继续将我们的输出标签视为固定的。但是现在,我们将输入数据视为可变的。我们不是将损失最小化,而是将损失最大化(即“上升”损失面)。我们的梯度现在将告诉我们如何增加或减少我们的像素值将影响我们的损失,我们将在损失表面“上升”。

在训练期间,我们计算我们的损失相对于我们的权重的导数,并使用梯度下降来更新我们的权重到最小化我们的损失。
在攻击期间,我们计算我们的损失相对于我们的输入像素的导数,并使用梯度上升来更新我们的权重,以最大化我们的损失。

简单的“白盒”攻击实现

首先,我们需要一些快速的 Keras 逻辑来获得模型输出相对于输入像素的梯度。我们将“瞄准”一些期望的输出类别,而不是不加选择地最大化损失。例如,如果我们希望一个 4 被误分类为 2,我们将使用 2 的 target_class

计算特定目标类的梯度。

逻辑没那么差!我们只需要调用一次 batch_jacobian 来获得我们的梯度值!然后,我们只获取与我们想要的目标类相对应的值。

接下来,我们将想要更新我们的图像来增加损失,增加模型对目标 _ 类的信心。

基于梯度值更新输入图像以增加损失。

同样,逻辑也没那么糟糕!我们所做就是使用梯度矩阵,稍微调整它以使更新大小更加一致,将它添加到图像中,并剪切以避免像素值超出[0,1]范围。

如果我们考虑输出类的梯度值在每一步告诉我们什么,这是有意义的。梯度矩阵的正值告诉我们,使该位置的像素更亮(即,更接近 1)将导致目标类别中的更高置信度。类似地,梯度矩阵的负值告诉我们,使该位置处的像素更暗(即,更接近 0)将导致目标类中更高的置信度。

通过将重新调整的梯度值添加到我们的图像中,我们逐渐制作出一个具有更高可信度的分类为目标类的图像!

可视化样品

让我们随机抽取一个样本,使用我们开发的逻辑来创建一个视觉上相似的图像,这个图像被我们的网络错误地分类了。

从 MNIST 测试数据中选择一个随机元素。

图片作者。目前预测:5;信心:1.00。

现在我们有了一个图像样本,我们可以看到图像最初是如何被我们的模型分类的。

正如预期的那样,我们的模型以非常高的置信度正确地对样本进行了分类。现在,让我们以这张图片为起点,生成一个对立的样本…

更新输入样本,直到错误分类为 4。

很简单。我们只是在循环中重复执行之前定义的 gradient_ascend 步骤,直到分类行为发生变化。一旦我们欺骗了我们的模型,我们就终止。

图片作者。目前预测:4;信心:0.32。

哇!我们现在有一个几乎相同的图像,但我们的模型将其归类为其他东西。我们简单的白盒攻击成功了!

有趣的是,这个样本仍然清晰人类可识别。事实上,图像修改似乎是对像素内容的随机更新。我们在模型定义的 a 4 和 a 5 之间找到了一个样本。

将这一点与我们最初的比喻联系起来,该模型告诉了我它认为 4s 和 5s 之间的“边界”在哪里。显然,我们不同意…

离别的思绪

这多酷啊?!我们可以快速生成我们的模型误分类的视觉上相似的样本。如果我们查看测试数据的持续准确性(约 98%),我们会确信该模型的表现符合预期。然而,在更仔细的检查下,很明显这个模型已经学习了与我们不一致的决策边界:在我看来那些不像四!

对抗性 ML 的分支对模型信任、可解释性和可解释性提出了有趣而重要的挑战。我希望你从这篇文章中带着对这个领域的类似的欣赏离开!

如果您有任何更正或想要建议更多的澄清,请留下您的评论。感谢阅读!

  1. MNIST 手写数字数据库。 http://yann。乐村。com/exdb/mnist/。1998 年 7 月
    知识共享署名-相同方式共享 3.0 许可证。

重塑 LSTM:从零开始的长短期记忆

原文:https://towardsdatascience.com/reinventing-the-lstm-long-short-term-memory-from-scratch-3bf90d40057f?source=collection_archive---------8-----------------------

对 LSTM 单位的了解足以“改造”它!

当我必须从头开始描述一件事情时,我学得最好!在“重新发明”的文章中,我将尝试描述为自己实现一项技术所必需的数学直觉!

我不喜欢我找到的任何 LSTM 图形,所以我做了自己的!在任何上下文中随意重用,无需注明出处(不保留任何权利)。

为什么是 LSTMS?

RNNs 允许我们将神经网络方法的工具包应用于时间序列问题。然而,最初的 RNN 实现常常难以了解长期的时间依赖性。LSTMs 引入了细胞状态或“记忆”的向量,以提高网络学习特征之间可能关系的能力,即使特征之间相隔数百或数千个时间点。

我们要去哪里…

每个 LSTM 单元输出两个值:一个 a(激活)的向量和一个 c(ell)状态的记忆向量。直观地说,这允许我们的网络从时间序列数据中保持相关特征的记忆。网络可以保存信息,并在以后的时间点引用重要的上下文。我们将浏览各个部分,并讨论每个部分对 LSTM 函数的贡献!

完整的 LSTM 单元图,我们将依次理解每一块!图片作者。

乙状结肠和 tanh

双曲正切函数和 sigmoid 函数并排。(左)极客 3 上维基媒体 |(右)马丁托马上维基媒体

为了理解 LSTM 行为,我们必须理解在引擎盖下使用的非线性函数。sigmoid 和双曲线正切函数在视觉上是相似的。然而,重要的是,sigmoid 函数不会呈现负值。sigmoid 的尾部大约等于零(对于域的负值,x < 0)和大约等于一(对于域的正值,x > 0)。出于这个原因,我们可以将 sigmoid 视为一个布尔选择器,它在接近 0 的值和接近 1 的值之间切换。

在 LSTM 单元中,sigmoids 应用于元素级乘法之前的值。这允许我们移除 sigmoid 为 0 的元素,并保留值为 1 的值。

创造新的记忆

让我们从时间点 t 开始,创建一个新的候选单元状态存储向量。稍后,我们将考虑使用这些值来覆盖前一个单元格的单元格状态向量( t-1 )。这里,我们将使用我们的双曲正切函数( tanh )来计算当前特征( x_t ) )和前一个单元的激活( a_t-1 )加上偏置项( b_c )的加权( W_c )和的非线性激活。

不要被吓倒!这种应用于加权和的非线性建立在与多层感知器中的单元相同的直觉上。我们通过梯度下降学习权重矩阵和偏差项。激活只是一个 tanh,而不是我们可能在其他上下文中更常见的激活函数,如 relu !

我们将这个新的候选单元状态存储向量称为 c_new

图片作者。

遗忘之门

现在,我们需要弄清楚如何改变我们从前一个单元得到的单元状态记忆向量( c_t-1 )。首先,让我们选择我们想要忘记的元素。

遗忘门根据前一个单元的状态存储向量(c_t-1)来确定要遗忘/记住哪些元素。

这个逻辑和我们之前用来确定新的候选记忆向量的逻辑完全一样。不过,这一次,我们将使用一个单独的权重矩阵( W_f )和偏差项( b_f )。我们希望这个门决定来自 c_t-1 的哪些值被保留或丢弃,所以我们希望大部分值接近 1(保留)或 0(丢弃)。如上所述,我们使用 sigmoid 激活来实现这一点,该激活主要返回 0(负尾)和 1(正尾),然后是与 c_t-1 的逐元素乘法!

图片作者。

更新门

完成我们单元的状态存储向量后,我们现在将使用相同的 sigmoid 选择器逻辑,但应用了另一个可训练矩阵( W_u )和 bias ( b_u )。结果将与我们之前计算的候选单元状态记忆向量逐元素相乘( c_new )。

更新门确定我们想要从我们的候选单元状态存储器向量(c_new)中忽略/添加哪些元素。

现在,我们有了没有想要忘记的元素的 c_t-1 和只有想要添加到内存中的新值的 c_new 。将这两个量按元素相加产生更新的单元状态存储向量 c_t ,我们将把它传递给下一个 LSTM 单元!

图片作者。

输出门

最后,我们将计算一个激活( a_t )来传递给下一个单元格。幸运的是,逻辑全部被回收了!我们将通过一个最终的非线性双曲正切函数来传递我们的细胞状态记忆向量( c_t )。然后,我们将应用一个 sigmoid——就像对其他门一样——但是使用一组最终可训练的权重( W_o )和偏差( b_o )。tanh 输出和输出门值的逐元素乘法产生我们单元的激活( a_t ):传递给下一个单元的第二个值(t+1)。

图片作者。

可选的序列间返回

如果我们愿意,我们也可以预测每个时间点的数量。例如,使用我们的激活( a_t )我们可以应用一个 final sigmoid 来预测一个二进制值,或者应用一个 softmax 来执行多类分类( y_t )。这允许我们的 LSTM 层返回与输入序列长度相同的预测序列。

图片作者。

…我们结束的地方!

你可以想象这种架构可以学习的复杂的非线性时态关系!将这些单元链接在一起能够模拟任意持续时间的复杂时序动态。

如果文章的文字或图像有助于解释 LSTMs,请为文章鼓掌!如果您有任何更正或想要建议更多的澄清,请留下您的评论。感谢阅读!

R 中的拒绝采样

原文:https://towardsdatascience.com/rejection-sampling-in-r-a3a37e9135ff?source=collection_archive---------15-----------------------

如何使用拒绝采样生成随机数

来源:图片由 geralt 从 Pixabay 拍摄

当从特定分布生成随机数时,这个过程可以在很大程度上自动化。

例如,如果想要生成 100 个属于 R 中的正态分布的随机数,执行起来很简单:

rnorm(100)

然而,这个过程实际上是如何工作的呢?一个算法如何知道一个随机数是否属于一个特定的分布?

答案是通过拒绝采样

拒绝抽样是一种生成属于特定分布的随机数的方法。

剔除取样的工作原理

笛卡儿图由穿过定义空间的 x 轴和 y 轴组成。

来源:图片由作者创建

在图形的整个区域中,给定的分布(如正态分布)只能覆盖图形的给定部分。在这点上,如果一个人向棋盘随机投掷飞镖,那么落在正态分布区域内的飞镖将被接受,而在该区域之外的飞镖将被拒绝。

AR 包的使用

虽然手动设置这种机制可能会被证明是一个繁琐的过程,但 R 中的 AR 包使这变得更加直观。

作为参考,AR 代表接受-拒绝方法,本质上意味着算法接受符合指定分布的随机数,而拒绝不符合的随机数。

让我们来看一些例子。

将 y 函数定义为均匀分布

首先,让我们考虑随机数界于 0 和 1 之间的均匀分布。

library(AR)simulation = AR.Sim( n = 200,
               f_X = function(y){dunif(y, min = 0, max = 1, log = FALSE)},
               Y.dist = "unif", Y.dist.par = c(0,1),
               Rej.Num = TRUE,
               Rej.Rate = TRUE,
               Acc.Rate = FALSE
)simulation

在这个例子中,函数(y)被定义为均匀分布dunif(y, min = 0, max = 1, log = FALSE),其中 Y.dist 也被设置为均匀分布unif -其中 Y.dist 代表随机变量的分布名称,即我们指定生成的所有随机数都应该是均匀的。

下面是生成的模拟:

Optimal c = 1
The numbers of Rejections = 0
Ratio of Rejections = 0
> simulation
  [1] 0.698931268 0.385566025 0.268550105 0.645668389
  [5] 0.074197359 0.106701437 0.413400215 0.852781124
...
[193] 0.170901990 0.846563826 0.935788635 0.794019386
[197] 0.954960048 0.185282641 0.262304122 0.499565325

假设生成的随机数来自同一分布,则拒绝率为 0 ,即所有生成的随机数都落在指定的分布内。

这是一个图形演示,我们看到所有随机数都以绿色突出显示,它们都属于接受区域:

然而,当从不同的分布中抽取随机数时,我们可以预期拒绝率将是显著的,因为均匀生成的随机数将不可避免地落在分布之外。

Y.dist =正态分布

让我们指定 Y.dist 为正态分布。

simulation = AR.Sim( n = 200,
               f_X = function(y){dunif(y, min = 0, max = 1, log = FALSE)},
               Y.dist = "norm", Y.dist.par = c(0,1),
               Rej.Num = TRUE,
               Rej.Rate = TRUE,
               Acc.Rate = FALSE
)
simulation

这是生成的样本和拒绝分数。

Optimal c = 4.132
The numbers of Rejections = 646
Ratio of Rejections = 0.764
> simulation
  [1] 0.928162718 0.263720941 0.791635163 0.787643782
  [5] 0.682266914 0.609826459 0.526126303 0.303784735
...
[193] 0.528860424 0.945110000 0.263584707 0.958508588
[197] 0.136600285 0.855113058 0.952162865 0.776729036

我们看到在这种情况下获得了 0.764 的拒绝率。

下面是一个图形表示:

Y.dist =柯西分布

柯西分布怎么样?

simulation = AR.Sim( n = 200,
               f_X = function(y){dunif(y, min = 0, max = 1, log = FALSE)},
               Y.dist = "cauchy", Y.dist.par = c(0,1),
               Rej.Num = TRUE,
               Rej.Rate = TRUE,
               Acc.Rate = FALSE
)
simulation

在这种情况下,观察到明显更高的废品率 0.839 :

Optimal c = 6.283
The numbers of Rejections = 1042
Ratio of Rejections = 0.839
> simulation
  [1] 0.942316029 0.610005076 0.653872133 0.411678591
  [5] 0.350234804 0.511995680 0.962110053 0.224207609
...
[193] 0.320808401 0.788250465 0.805645829 0.214475551
[197] 0.471248814 0.015849055 0.715283175 0.067130172

这又是一个图示:

此外,也可以改变随机数本身产生的分布。

将 y 函数定义为贝塔分布

虽然这里选择了均匀分布,但原作者选择将 y 函数定义为 beta 分布。

从均匀分布中抽取属于正态分布的随机数时,拒绝率为 0.764

如果 y 函数被定义为贝塔分布呢?在尝试从该分布中识别正态分布的随机数时,会观察到更低或更高的拒绝率吗?

simulation = AR.Sim( n = 200,
               f_X = function(y){dbeta(y,2.7,6.3)},
               Y.dist = "norm", Y.dist.par = c(0,1),
               Rej.Num = TRUE,
               Rej.Rate = TRUE,
               Acc.Rate = FALSE
)
simulation

以下是样品和拒绝率:

Optimal c = 6.898
The numbers of Rejections = 1231
Ratio of Rejections = 0.86
> simulation
  [1] 0.36115666 0.41559791 0.38940239 0.36566013 0.60793899
  [6] 0.25795174 0.10902713 0.27325963 0.64264824 0.18258016
...
[191] 0.12631125 0.26920790 0.13224107 0.33171854 0.08197576
[196] 0.22850070 0.41424580 0.34925813 0.13344548 0.57013744

在这种情况下,当从贝塔分布采样时, 0.86 的拒绝率明显更高,这意味着从均匀分布采样更有效(至少当试图从该分布生成正态分布随机数时)。

虽然这不会对生成的数字产生影响,但剔除率越低,效率越高,因为从更接近目标分布的分布中进行采样,必然会减少模拟生成适当随机数所需的时间。这就是所谓的效率比

结论

拒绝抽样(也称为接受-拒绝算法)是概率领域中的一个关键工具,用于生成符合特定分布的随机数。

在本例中,您看到了:

  • 什么是拒绝抽样及其在生成属于特定分布的随机数中的重要性
  • 如何使用 R 中的 AR 包来实现这个目的
  • 对不同分布进行采样以提高效率

非常感谢您的时间,任何问题或反馈都非常欢迎。

免责声明:本文是在“原样”的基础上编写的,没有任何担保。它旨在提供数据科学概念的概述,不应被解释为专业建议。本文中的发现和解释是作者的发现和解释,不被本文中提到的任何第三方认可或隶属于任何第三方。

参考

  • 何塞·恩平科(2016 年)。用于概率、统计和机器学习的 Python
  • 综合 R 档案网:包‘AR’

使用 Python 进行剔除采样

原文:https://towardsdatascience.com/rejection-sampling-with-python-d7a30cfc327b?source=collection_archive---------9-----------------------

使用正态分布和柯西分布的示例

来源:图片来自皮克斯拜

拒绝抽样是一种生成属于特定分布的随机数的方法。

例如,假设有人希望生成 1000 个服从正态分布的随机数。如果想在 Python 中使用 numpy 实现这一点,执行起来非常简单:

np.random.randn(1000)

然而,这个过程到底是如何工作的呢?在 Python 中生成随机数时,算法如何知道随机数是否属于特定的分布?这就是剔除抽样的用武之地。

拒绝抽样

我在本文开头提供了一张飞镖板的图片,这是有原因的——因为这是思考剔除抽样如何工作的最直观的方式。

笛卡儿图由穿过定义空间的 x 轴和 y 轴组成。

来源:图片由作者创建

在图形的整个区域中,给定的分布(如正态分布)只能覆盖图形的给定部分。在这点上,如果一个人向棋盘随机投掷飞镖,那么落在正态分布区域内的飞镖将被接受,而在该区域之外的飞镖将被拒绝。

让我们使用 Python 来看看这是如何工作的。

首先要做的是正确定义概率密度函数。参考 scipy.org 手册,提供了范数的概率密度函数。

这在 Python 中定义如下:

f=lambda x: np.exp(-(x**2)/2)/(sqrt(2*3.14))

José Unpingco 的 **Python for Probability,Statistics and Machine Learning(2016)**对拒绝抽样和其他概率方法进行了详细的概述,我会推荐这个标题,以便更深入地理解这个主题。Unpingco 使用拒绝方法来识别没有连续倒数的密度和卡方分布的样本。

在这种情况下,我改为为正态柯西分布定义密度,同时对随机数 u1u2 的规模做出不同的假设。然而,分析的最终目的保持不变——使用 u1 在 f(x)的域中选择随机数,然后 u2 根据以下标准接受或拒绝这些值:

idx,=np.where(u2<=f(u1)/M) # rejection criterion

选择 0.3 的刻度,产生 u1 和 u2 的随机样本。

M=0.3 #scale factor
Mu1=np.random.rand(10000)*3  # uniform random samples scaled out
u2=np.random.rand(10000)    # uniform random samplesidx,=np.where(u2<=f(u1)/M)

基于拒绝标准生成样本:

>>> v=u1[idx]
>>> varray([0.00879322, 0.19344109, 0.53193192, ..., 0.45176108, 0.78405714, 0.85680125])

来源:Jupyter 笔记本输出

在这里,我们可以看到遵循正态分布的样本以橙色突出显示。

我们想知道的一件事是效率比——也就是说,产生的随机数中有多大比例实际上属于定义的分布?

>>> u1_size=u1.size
>>> v_size=v.size>>> u1_size
10000>>> v_size
4931>>> efficiency=v_size/u1_size
>>> efficiency
0.4931

基于上述,49.31%的生成样本落在正态分布区域内。这意味着产生的一半样本被丢弃。

如果比例降低到 0.1 呢?

来源:Jupyter 笔记本输出

在这种情况下,正态分布覆盖更大的总面积,丢弃的样本更少。

效率也提高到 71.24%。

定义不同的概率密度

虽然缩放有助于提高效率,但理想的情况是确定一个具有更高效率的不同分布。

让我们试试柯西分布——它类似于正态分布,但尾部更重。

柯西分布的概率密度函数定义如下:

f=lambda x: 1/(3.14*(1+x**2))

再次为 M 设置 0.1 的刻度。

来源:Jupyter 笔记本输出

>>> u1_size=u1.size
>>> v_size=v.size
>>> efficiency=v_size/u1_size
>>> efficiency
0.7871

效率提高到 78.71%,表明柯西分布更适合模拟给定数据,分布的尾部越长,覆盖的区域越大。

结论

拒绝抽样(也称为接受-拒绝算法)是概率领域中的一个关键工具,用于生成符合特定分布的随机数。

在本例中,您看到了:

  • 如何使用 scipy 定义概率密度函数
  • 定义随机样本的拒绝标准
  • 对不同分布进行采样以提高效率

非常感谢您的时间,任何问题或反馈都非常欢迎。你也可以在 michael-grogan.com的找到更多我的数据科学内容。

免责声明:本文是在“原样”的基础上编写的,没有任何担保。它旨在提供数据科学概念的概述,不应被解释为专业建议。本文中的发现和解释是作者的发现和解释,不被本文中提到的任何第三方认可或隶属于任何第三方。

参考

  • 何塞·恩平科(2016 年)。用于概率、统计和机器学习的 Python
  • scipy.org:统计函数(scipy.stats)

关系数据库与非关系数据库

原文:https://towardsdatascience.com/relational-vs-non-relational-databases-f2ac792482e3?source=collection_archive---------5-----------------------

数据科学,初学者,数据库

关系数据库和非关系数据库的利弊。

作者图片

选择合适的数据库来存储数据并不总是一个容易的选择。事实上,如果在第一种情况下,使用关系数据库可能看起来更自然,在某些情况下这种选择可能不会成功。

在本文中,我试图描述关系数据库和非关系数据库之间的区别,试图强调它们的优缺点。

1 关系数据库

关系数据库是在它们之间具有预先建立的关系的项目的集合。所有的物品都组织在表格中,表格中的列代表物品属性。表格的每一行代表一个单个数据项

为了唯一地标识表中的每个项目,应该为每个表定义一个主键。主键可以包含表中的一列或多列。多个表可以通过不同的外键相关联。

在不改变表结构的情况下,可以用不同的方式查询关系数据库。结构化查询语言(SQL) 是用来与关系数据库通信的最著名的接口。

SQL 数据库引擎的示例有:

  • Mysql
  • Postgres
  • 微软 SQL Server
  • SQLlite 。

总之,在关系数据库中,所有的信息都是结构化的,也就是说,它们被组织在表格中

1.1 关系数据库的优点

  • 非常适合结构化数据
  • 使用现有的查询语言(SQL)
  • 非常适合复杂的查询
  • 轻松的数据导航
  • 由于表之间的关系和约束,数据集成程度很高
  • 交易安全
  • 高可靠性

1.2 关系数据库的缺点

  • 预先模式定义
  • 无法适应不断变化的需求:对一个项目的动态更改会影响同一表中的所有其他项目
  • 数据处理可能会很慢。使用昂贵的硬件可以实现高性能

2 个非关系数据库

非关系数据库是非结构化、结构化或半结构化项目的集合,通常以键值形式表示。集合中的每一项都可以代表一个文档、一个表或任何其他东西。

通常,相同或不同集合中的不同项目彼此不相关,尽管可以向一个项目添加引用(这样的标识符)来引用另一个项目。

非关系数据库引擎的例子有:

  • MongoDB
  • 阿帕奇卡珊德拉
  • 雷迪斯
  • 沙发底座
  • 阿帕奇 HBase

总之,非关系数据库以非表格形式存储数据

2.1 非关系数据库的优点

  • 灵活的数据模型
  • 快速适应变化的需求:一个项目的动态变化不会影响其他项目
  • 用很少的结构存储大量数据
  • 高性能

2.2 非关系数据库的缺点

  • 低可靠性
  • 手动查询语言
  • 很难验证数据的完整性和一致性

3 摘要

在本文中,我描述了关系数据库和非关系数据库的优缺点。下表总结了两个分析数据库提供的一些最重要的特性:

作者图片

如果你想了解更多关于关系数据库和非关系数据库之间的区别,你可以阅读 Kevin C Lee 提出的文章,标题为:数据库管理——NoSQL vs . SQL(或 MongoDB vs. MySQL) 。

感谢您的阅读!你可以在这篇文章中读到更多关于我的信息。

这篇文章的摘要可以从链接以幻灯片的形式下载。

相关文章

https://medium.com/analytics-vidhya/a-gentle-introduction-to-descriptive-analytics-8b4e8e1ad238

新到中?您可以每月订阅几美元,并解锁无限的文章— 点击此处。

简化的相关性排名

原文:https://towardsdatascience.com/relevance-ranking-simplified-e8eeea829713?source=collection_archive---------18-----------------------

自然语言处理

搜索引擎是如何工作的

约书亚·戈尔德在 Unsplash 上拍摄的照片

在信息检索方面,排名扮演着重要的角色。当我们使用浏览器搜索文档时,我们希望浏览器检索与我们的查询非常相似的文档。为此,我们需要一个好的检索模型。检索模型是匹配查询和文档的过程。如果检索到的文档与查询非常相似,则认为检索模型是好的。它是搜索引擎中使用的相关性排序算法的基础。在自然语言的情况下,最流行的检索模型被称为向量空间模型。

向量空间模型

向量空间模型是用于测量文档集合和查询之间的相似性的模型。查询和文档被表示为 n 维空间中的向量。将查询和文档转换成向量的过程称为文本向量化。TF-IDF 矢量器是最常用的文本矢量器之一。更多关于 TF-IDF 这里。

为了检索最相关的文档,我们计算查询和集合中每个文档之间的相似性。基于这些相似性,我们可以对文档的相关性进行排序。与查询具有最高相似性的文档是应该被检索并排列在顶部的文档。

余弦相似性

余弦相似性是衡量两个向量之间相似性的度量。从数学上讲,它度量的是多维空间中两个向量之间的夹角余弦。

作者图片

因此,它是方向的量度,而不是大小。方向相同的两个向量的余弦相似度为 1,方向相对于彼此偏离 90°的两个向量的余弦相似度为 0,因为 90°的余弦为 0,而方向相反的两个向量的相似度为-1,因为 180°的余弦为-1。余弦相似度特别用于正空间,其中结果被限制在 0 到 1 的范围内。余弦相似度由下式给出:

作者图片

正空间由下式给出:

作者图片

在文本相关性排序的情况下,余弦相似度应该限制在 0 和 1 之间,因为 TF-IDF 矩阵不包含任何负值。

例子

给定文档文本如下:

作者图片

和查询文本,如下所示:

作者图片

使用 TF-IDF 对文档文本进行矢量化

作者图片

使用 TF-IDF 对查询文本进行矢量化

作者图片

计算查询和每个文档文本之间的余弦相似度

作者图片

按升序对结果进行排序

作者图片

我们得到的文档相关性排序如下:

作者图片

结论

相关性排名是检索与查询非常相似的内容的好方法。它通常用于检索给定特定查询的最相关的文档。然而,相关性并不总是与文本有关,它可以应用于任何东西,只要数据可以用向量或矩阵表示。

先决条件

参考

https://www.sciencedirect.com/topics/computer-science/vector-space-models https://en.wikipedia.org/wiki/Vector_space_model https://www.machinelearningplus.com/nlp/cosine-similarity/ https://medium.com/swlh/relevance-ranking-and-search-a98b35ebc7b3

使用隐式表示进行重新照明和材质编辑

原文:https://towardsdatascience.com/relighting-and-material-editing-with-implicit-representations-cc967818b3c0?source=collection_archive---------22-----------------------

思想和理论

了解我们如何为使用像 NeRF 这样的隐式表示的场景添加重新光照和材质编辑功能

照片由萨米·塔卡劳蒂奥在 Unsplash 拍摄

计算机视觉最大的挑战之一是从图像中学习场景。如果我们能理解这个场景并以某种方式表现出来,我们就能从新的角度来观察这个场景。这被称为基于图像的渲染(IBR)。

想法是从 2D 图像生成 3D 重建并生成独特的视图。此外,如果我们希望在其他属性中检索场景的材质和照明,这被称为反向渲染。

有许多不同的方法来表示 3D 对象。经典方法包括将其表示为网格、体素或点云。这些年来已经被广泛研究,并且有它们的优点和缺点。典型地,它们是内存密集型的,并且不能表现非常详细的对象/场景,或者需要很多计算。虽然点云可以很好地缩放,但它们通常会因定义表面而变得不稳定。

表来自可区分渲染:调查

我们讨论一类新的表示法,叫做隐含表示法,它因为所有正确的理由而制造了许多噪音。其中之一被称为非常著名的神经辐射场或 NeRF,仅在去年一年就产生了 15-20 种变体。NeRF 非常擅长表现整个场景,并且可以从任何角度观看。然而,我们不能以任何方式编辑场景。因此,我们进一步研究了在使用 NeRF 作为场景表示时可以执行重新照明和材质编辑的变体。

隐性表征

最近出现了一种新的表示法,称为隐式表示法。区别主要在于,我们在这里学习一个描述几何的函数。例如,对于一个圆,隐式方程是 f(x,y) = x +y -R,其中 R 是圆的半径。对于任意一点(x,y),我们知道它是在圆上,在圆内,还是在圆外。因此,给定许多点及其相对于圆的位置信息,我们可以估计圆的半径。

类似地,我们也将同样的想法扩展到 3D。我们知道点是在一个特定的表面之上、之内还是之外,因此我们可以估计我们的物体表面。还有什么比神经网络更好的函数逼近器呢,神经网络是“通用函数逼近器”

根据我们想要如何渲染场景,有两类隐式表示。表面表示旨在找到对象的表面和相应的颜色。相比之下,体积表示并不明确地寻找表面,而是尝试对该点的深度及其相应的颜色进行建模。

占位网络:学习功能空间中的 3D 重建

隐式表面表示包括占用网络和带符号的距离场(SDF)。这里的想法是,我们有一个神经网络,它预测给定点相对于对象的位置,即,它是在表面上,在对象内部,还是在对象外部。因此,当我们发射一束光线并在其上采样点时,网络会学习它们相对于物体的位置。利用这一点,我们可以对更接近表面的点进行采样,并找到表面。

DeepSDF:学习用于形状表示的连续符号距离函数

占位网络和带符号距离场的主要区别在于占位网络给出的是二元答案。如果点在外面,那么它是 0,如果在里面,它是 1,在表面上,值是 0.5。另一方面,带符号的距离字段给出了点到表面的距离。因此,我们的工作是找出满足 f(x) = 0 的所有点。我们在物体内部得到正值,在外部得到负值。我们可以用光线行进法或球体追踪法找到这些表面。类似地,可以通过特定 3D 点的网络输出来找到表面的颜色。虽然它们非常有名,但它们只在光线可以与表面相互作用的地方工作。

其他作品使用隐式表面表示,如 SRN 、可微分体绘制、皮夫等。

(把汽车拆卸减重后举行的短程高速驾驶比赛中)冲撞别的汽车

NeRF:将场景表示为用于视图合成的神经辐射场

我们可以执行体绘制,而不是找到所有对象的表面。这就是 NeRF 及其变体出现的原因。这个想法是,我们不是学习一个表面,而是学习整个体积,不仅包括物体,还包括介质的影响。Neural Volumes 是第一批对场景进行编码的作品之一,但它是以不可缩放的基于体素的表示进行编码的。

另一方面,NeRF 使用 MLPs 对场景进行编码。对于从每个像素发出的光线,我们对光线上的点进行采样。现在每个点都有一个 3D 位置和相应的观察方向。我们传递这个 5D 向量,并获得相应的颜色和体积深度。我们对光线上的所有样本都这样做,然后将它们合成在一起,得到一个像素颜色。NeRF 有两个网络,一个是粗略的,它在光线上均匀地采样点,另一个是精细的网络,它做的一切都一样,除了我们使用重要性采样。这意味着我们对更有深度的点,也就是物体,进行更多的采样。采用观察方向有助于对依赖于观察的效果进行建模,例如镜面效果,例如光亮表面的反射[1]。合成这些信息使用经典的体绘制技术,这给了我们最终的图像。

到目前为止,我们读到的方法可以很好地表示场景的几何形状,并且以一种合理的内存高效的方式。然而,正如我们注意到的,这些方法直接学习和预测表面或场景的特定点的颜色。因此,它直接烘烤的材料和照明效果,我们不能编辑。因此,虽然这些网络可以很好地执行视图合成,但它们不能改变场景中的照明或对象的材质。

渲染方程

在我们继续之前,让我们了解计算机图形如何建模材料和照明。考虑一个有一个光源、一些物体和一个照相机的场景。现在我们想知道物体上的一个点是什么样子的。我们可以用一些古老的物理学来计算这个。通过利用能量平衡,在某一特定点我们可以说[6]:

功率守恒方程

即离开物体的能量和进入物体的能量之差,等于物体发出的能量和吸收的能量之差。为了加强表面的能量平衡,出射辐射 Lo 必须等于发射辐射加上散射入射辐射的分数[6]。发射辐射由 Le 给出,散射辐射由散射方程给出,该方程给出

来自源的渲染方程

不要担心它看起来太专业。在一个特定的点上,我们总结了整个半球反射光的贡献。因子 f 被称为双向反射分布函数或 BRDF,它告诉我们特定材料会反射和吸收多少功率。BRDF 告诉我们一种材料的特性。BRDF 有很多模式,像库克-托兰斯,迪士尼等。如果 BRDF 对于每个点都是不同的,比如在一个纹理中,我们称之为空间变化 BRDF 或 SVBRDF。

还有另一个版本叫做渲染方程的表面版本,我们将来也会提到它:

渲染方程的曲面形式

这里 p '是我们的表面,p 是观察者表面或相机。“p”是光线照射的表面,A 是所有的表面。g 是几何耦合项,代表:

几何耦合项

v 是可见度函数,它是能看到彼此的表面之一。

既然我们已经了解了材质和照明是如何建模的,我们就可以理解在隐式表示中赋予我们材质和照明编辑能力的各种工作思路。

神经

神经:用于重新照明和视图合成的神经反射和可见度场

用于重新照明和视图合成的神经反射和可见性场或神经尝试使用多个点光源重新照明场景。在 NeRF 中,我们假设光线上采样的点都不反射光线。然而,由于我们想执行重新照明,我们需要模拟每个点对直接和间接照明的反应。因此,现在我们需要计算每个点的反射率函数,而不是每个点都是一个发射器。

从源看,每个点都是一个反射场

因此,首先,用两个 MLP 替换 NeRF 的辐射 MLP:输出体积密度σ的“形状”MLP 和输出任何输入 3D 点的 BRDF 参数的“反射”MLP。该方法使用的 BRDF 模型用三维反照率矢量和粗糙度常数对其建模。

现在,我们可以沿着光线解析地计算每个点的反射函数。我们需要查询光线在击中一个点后击中的每个对应点的可见性。但是,这种操作非常非常昂贵,而且这只是为了直接照明。对于间接照明,我们需要递归地做同样的事情。因此,相反,我们做的是我们有一个能见度 MLP 和距离 MLP。可见性 MLP 计算给定点的可见性因子,而距离 MLP 计算一次反弹后光线的终止点。

来自源的神经素材编辑结果

总而言之,事情是这样的:

  • 对每条光线进行采样,并查询体积密度、曲面法线和 BRDF 参数的形状和反射率 MLP
  • 用直射光给光线上的每个点着色。通过使用由每个采样点的相应 MLP 预测的能见度和 BRDF 值来计算。
  • 用间接照明给光线上的每个点加阴影。使用预测的终点,然后通过沿该射线采样并结合每个点的贡献来计算其效果。
  • 像在 NeRF 中一样组合所有这些量来得到结果

NeRV 设计用于精确处理多个点光源。培训是计算密集型的。一旦训练完毕,我们就可以修改 BRDF 参数,并对整个场景进行材质编辑。

来自的神经重新点燃结果来源

TLDR;NeRV 使用形状 MLP 来预测体积,使用 BRDF MLP 来预测反照率和粗糙度,使用可见度 MLP 来预测每个点的可见度,使用距离 MLP 来预测一次反弹后光线的终止。结果通过每个点的渲染方程进行组合,然后像 NeRF 一样使用经典的体积渲染技术合成在一起。

缺乏社交能力者

NeRD:来自图像集合的神经反射分解

神经反射分解(NeRD)将基于物理的渲染或 PBR 整合到了 NeRF 框架中。如前所述,一个点的颜色是入射光和 SVBRDF 乘积在半球上的积分。一个点可能由于材质、遮挡或者表面法线指向远处而变暗[3]。所有这些因素都没有被 NeRF 考虑在内,因为它在辐射中烘烤。

书呆子网来自来源

NeRD 有两个 MLP,即采样 MLP 和分解 MLP。采样 MLP 输出独立于视图但依赖于照明的颜色和场景的体积密度。像 NeRF 一样,在这个网络中,光线上的点是均匀采样的,体积密度用于第二个网络中对象上的重要采样点。采样网络中的最后一个要素是特定图像的照明。我们不传递环境光,而是传递它的球面高斯表示。球面高斯类似于 2D 的傅立叶变换。我们这样做的原因是我们不能解析地计算渲染方程。因此,我们把它转换成球面高斯形式,积分转换成乘积运算。现在我们可以快速地计算这个方程。因此,我们从采样网络中学习照明、音量和照明颜色。

分解网络扩展了 NeRF 的第二个网络。除了颜色和体积密度,我们还计算一个向量,并将其传递给另一个小的自动编码器,以输出对象的 BRDF 参数。这里,BRDF 参数不同于 NeRV,因为模型输出反照率、金属和粗糙度。自动编码器可以优化训练并提高结果。最后,我们像 NeRF 一样组合输出,并通过经典的体绘制来输出图像。

书呆子结果来自来源

NeRD 使场景视图的颜色独立,并学习照明和 BRDF 属性。一旦学会,建模重新照明是简单的,因为我们知道照明是如何组合起来产生颜色的。

TLDR;NeRD 分解场景,分别学习场景的光照和 BRDF 参数。NeRF 的两个网络被增强以学习独立于视图和依赖于照明的颜色,并且一旦被训练,执行重新照明是简单的。

神经因素

神经因子:未知光照下形状和反射率的神经因子分解

NeRFactor 与我们迄今为止看到的所有作品都非常不同,因为它提炼了经过训练的 NeRF 模型,这是迄今为止该领域中没有其他作品做到的,因为 NeRF 在整个体积上工作,而它在表面点上工作。它可以执行自由视点重新照明以及材质编辑。

神经因素网络来自来源

首先,我们在现场训练一个 NeRF 网络。然后,我们保留粗网络并冻结其权重。然后,我们在 MERL 数据集上训练一个 BRDF MLP。MERL 数据集包含 100 种不同材料的反射率函数。然后,我们使用来自预训练的 NeRF 的预测体积来初始化法线贴图和可见性贴图。这些图非常嘈杂,因此我们没有冻结它们,而是将它们作为初始化。

来源于的神经因子结果

现在我们首先预测光线将在哪里照射到表面。使用它作为输入,我们训练 4 个 MLP,即光能见度 MLP、BRDF 识别 MLP、反照率 MLP 和正常 MLP。因为我们已经初始化了可见性和法线,所以它们被称为预训练。现在我们输入表面点并得到输出。BRDF MLP 输出将用于素材编辑的潜在向量 z。反照率网络处理漫反射颜色分量。我们还估计每个表面点的照明。NeRFactor 可以通过显式建模灯光可见性来分离阴影和反照率,并在任意光照条件下合成逼真的软阴影或硬阴影。所有的输出然后像在 NeRF 中一样被组合,并使用经典的体积渲染进行渲染。

来自的神经因子重新点燃结果来源

NeRFactor 不能预测 BRDF 参数。相反,它学习一个潜在的向量,可以很容易地用于渲染材料编辑。最重要的是,它不是在体积的任何地方取点,而是只在物体的表面取点。

TLDR;NerFactor 使用经过训练的 NeRF 来初始化法线和可见度图,并使用经过训练的 BRDF MLP 来学习潜在向量表示。然后,它搜索物体表面上的点,并学习其各种参数。学习后,我们可以进行重新光照和材质编辑。

外卖食品

我们通过三种方法让 NeRF 至少有了重新点燃的能力。NeRV 通过计算每个点的直接和间接照明的效果来实现这一点,并使用 MLP 近似可见性和光线终止。另一方面,NeRFactor 通过首先找到对象的表面进行分解,然后学习照明和 BRDF 参数(在这种情况下,是一种潜在的矢量表示)。NeRD 处于中间位置,其分解网络使用加权采样计算对象的表面法线,并使用它来渲染场景,但仍然对体积中的所有点运行。

我们注意到,越来越多的方法倾向于表面表现,以获得对场景编辑的更多控制,因为我们并不太关心介质会发生什么。很兴奋看到这个领域将会把两篇论文带向哪个方向。

参考

[1] 本·米尔登霍尔,普拉图尔·p·斯里尼瓦桑,马修·坦西克,乔纳森·t·巴伦,拉维·拉马穆尔蒂,任·吴,内尔夫:将场景表示为用于视图合成的神经辐射场【2020 ECCV】

[2] 普拉图尔·p·斯里尼瓦桑,柏杨·邓,张秀明,马修·坦西克,本·米尔登霍尔,乔纳森·t·巴伦,神经:用于重新照明和视图合成的神经反射和可见度场【2021】CVPR

[3] 马克·博斯、拉斐尔·布劳恩、瓦伦·詹帕尼、乔纳森·t·巴伦、刘策、亨德里克 P.A 伦施、讷德:来自图像集合的神经反射分解、【2021 arxiv】

[4] 张秀明,普拉图尔·p·斯里尼瓦桑,柏杨·邓,保罗·德贝维奇,威廉·t·弗里曼,乔纳森·t·巴伦,神经因子:未知光照下形状和反射率的神经因子分解【2021 arxiv】

[5] 加藤广治,德尼兹·贝克,米哈伊·莫拉里乌,田崎敬浩·安藤,松冈彻,瓦迪姆·凯尔,阿德里安·盖登,可微分渲染:综述【2020 arxiv】

[6]马特·法尔,文泽尔·雅各布,格雷格·汉弗莱斯,基于物理的渲染:从理论到实现

[7]弗兰克·德拉雷特, NeRF 爆炸 2020

[8]李泽宇,光线追踪——渲染方程式洞察

使用 Python 进行遥感

原文:https://towardsdatascience.com/remote-sensing-using-python-59a2dd94df51?source=collection_archive---------12-----------------------

Jupyter 的卫星数据分析,了解伦敦有多绿!

我一直在与一个潜在的油气能源客户互动,我为他开发了一个关于在石油钻井现场使用声音数据的概念验证。下周我还有一个会议,会上我们将与公司的城市规划部门讨论卫星图像数据的使用案例。我想以编程方式学习 GIS 数据分析的基础知识,这样下周我就不是新手了,并想出一个与城市规划部门相关的业务用例。因此,我一直在寻找一个足够强大的 python 库来完成所有复杂的遥感任务。就在那时,我遇到了 ArcGIS。

由 Ed van duijn 在 Unsplash 上拍摄的照片

石油和能源公司花费数百万购买最先进的遥感软件,但大部分用于石油勘探、测量热和湿度指数或水文研究。

尽管这超出了我的能力范围,但我想提出一个令人信服的因素,它可以为已经令人窒息的城市地区的增长提供一个可持续的解决方案。如果人类住房和其他所谓的发展活动的绿色覆盖正在减少,那么我们需要寻找替代的地方和城市。

在这项工作中,我们将使用 ArcGIS 的 python API 并探索寻找城市绿色覆盖的功能。

我们走吧!!

资料来源:Giphy.com

问题陈述

找出伦敦绿色的百分比。绿色区域包括森林、公园、灌木和从卫星上可以看到的花园形式的绿地。

首先,安装 ArcGIS API。我建议您在虚拟环境中这样做。

 pip install arcgis

检查安装是否成功完成。下面的命令应该在 Jupyter 笔记本中正确执行。

 import arcgis

在https://developers.arcgis.com/sign-up/创建一个免费账户,这样你就可以使用用户名和密码来访问 GIS API。

你可以在我的回购访问 GitHub 代码。

1.进口

2.GIS 连接

连接到 GIS 并搜索多光谱 Landsat 图层。

3.搜索英国

我们将搜索英国的边界,它将在区,县,人口普查区的边界。我在英国的时间教会了我一两件事,所以我将继续县一级,这是第一层。

UK_items = gis.content.search('United Kingdom', item_type="Feature Layer", outside_org=True)[0]UK_boundaries = UK_items.layers[1]

4.找到伦敦

资料来源:Giphy.com

使用地理编码查找伦敦,并将其添加到 Landsat 的范围对象中。伦敦的范围将是一个键值对,表示地图上伦敦城的宽度和高度。

按作者分类的图像(有一个键值对形式的范围对象)

london_area = geocode("London, GB", out_sr=landsat.properties.spatialReference)[0]landsat.extent = london_area['extent']

5.查找伦敦的经度和纬度对

在 UK_boundaries 对象中,#7 属于伦敦,我们可以提取纬度-经度的所有对。

london = UK_boundaries.query(where='OBJECTID=7')
london_geom = london.features[0].geometrylondon_geom['spatialReference'] = {'wkid':4326}london.features[0].extent = london_area['extent']

6。地球资源卫星数据中的伦敦十字路口

selected = landsat.filter_by(where="(Category = 1) AND (cloudcover             <=0.2)", time=[datetime(2015, 1, 1), datetime(2019, 12, 31)],                          geometry=arcgis.geometry.filters.intersects(london_area['extent']))

作者图片(伦敦的某处)

许多包含这种图像的数据集会出现在 Landsat 中,我们需要过滤出正确的图像。

df_selected = selected.query(out_fields="AcquisitionDate, GroupName, CloudCover, DayOfYear",order_by_fields="AcquisitionDate").sdf

将发布一个数据帧:

作者图片

让我们从上面的框架中取出 objected = 2918532,看看它对我们有什么作用;随便拿别的身份证。

london_image = landsat.filter_by('OBJECTID=2918532')apply(london_image, 'Natural Color with DRA')

作者图片

7.NDVI(归一化差异植被指数)

NDVI 是一种基于植物如何反射不同光波的植物健康指标。这是通过测量植物强烈反射的近红外线和吸收的红光之间的差异来实现的。NDVI 的范围总是从-1 到+1。

由于植物的表面反射特性,NDVI 度量可以用于识别地形中的绿色斑块。

ndvi_colorized = apply(london_image, 'NDVI Raw')ndvi_colorized

作者图片(原始 NDVI 图片)

8.将所有东西整合在一起

从上面的 NDVI 图片中截取伦敦的纬度和经度。

london_clip = clip(ndvi_colorized, london_geom)
london_clip.extent = london_area['extent']
london_clip

作者图片(伦敦金融城)

这非常整洁!我们已经从地球资源卫星图像中提取出了伦敦的地图。现在我们可以找到城市的绿色部分。

9.绿色伦敦

伦敦地图的剪辑是 NDVI 图像,我们可以在它上面放置一个遮罩,并找出哪些部分是绿色的。

这座城市看起来很绿(图片由作者提供)

10.量化调查结果

计算图像中绿色的像素,也可以通过绘制直方图来完成。

伦敦的总绿化面积约占伦敦总地理面积的 39%

作者提供的图片(我们使用遥感技术发现,伦敦 40%的地方是绿色的)

如果你曾经在伦敦生活过,那么你会知道它是一个绿色的城市,拥有大量的公园,比如摄政公园、海德公园、里士满公园、詹姆斯公园、维多利亚公园、汉普斯特德希思公园、巴特西公园等等。这座城市正处于最佳位置,森林覆盖率正适合它所服务的人口数量(约 1000 万)。

结论

1.使用 python APIs,可以完成软件能完成的所有工作。复杂的任务只能用昂贵的软件来完成的日子已经一去不复返了。

2.ArcGIS 有一个学习曲线。文档会对你有所帮助,但是你需要花几个小时来理解这个概念。

3.伦敦是一座绿色城市;应该在重复的时间间隔内密切监测绿色植被,以了解城市的动态并采取纠正措施。

我喜欢伦敦的公园和这座城市里自由漫步的狐狸。应该维持现状。

拉克伦·戈恩在 Unsplash 上的照片

参考资料:

https://developers.arcgis.com/python/api-reference/

QGIS 遥感:计算 NDVI

原文:https://towardsdatascience.com/remote-sensing-with-qgis-calculate-ndvi-c2095f0de21b?source=collection_archive---------14-----------------------

这可能是最常用的遥感指数

NDVI 根据地球资源卫星 8 号的图像计算得出。所有照片由作者提供。

植被指数是一种主要的遥感产品,归一化差异植被指数(NDVI)可能是使用最广泛的植被指数。要计算 NDVI,你只需要合适的图像和一个允许你与图像数据交互的程序。QGIS 是 GIS 程序的一个很好的免费选项,它提供了显示、分析和呈现遥感数据的工具。

使用 QGIS 栅格计算器在 QGIS 中计算 NDVI 非常简单。您所需要的只是包含红色和近红外波段的影像以及安装在您机器上的 QGIS。这两样东西都可以完全免费获得。

什么是 NDVI,为什么使用它?

NDVI 测量光谱的红色和近红外部分的反射差与红色和近红外反射之和的比率。绿色健康的植物反射光谱中近红外部分的光,吸收红光。NDVI 值的范围从 1.0 到-1.0,较大的正值表示绿色植被。

NDVI 用于许多不同的应用中。具体来说,它是野生动物和牲畜饲料数量和质量的重要指标。NDVI 还用于评估农田,确定地表水的位置和范围。

NDVI 方程

NDVI 是光谱的红色和近红外部分的反射差除以红色和近红外反射的总和。数学上它被写成如下。

如何在 QGIS 中计算 NDVI

要在 QGIS 中计算 NDVI,请使用栅格计算器从近红外(NIR)波段中减去红色波段的值,然后除以红色和 NIR 波段的总和。所有你需要的是来自任何种类的图像和 QGIS 装置的红色和 NIR 波段的反射值。本文的其余部分将带您了解如何使用 Landsat 8 影像进行这种计算。也可以观看文末视频进行演示。

下载 Landsat 卫星图像

在开始之前,你需要一些数据。如果您不确定如何获取 Landsat 数据,下面的视频将带您了解如何免费下载 Landsat Analysis Ready 数据(ARD)。

观看此视频演示如何下载地球资源卫星数据。

将 Landsat 数据添加到 QGIS

一旦有了用于计算的数据,就将红色和 NIR 波段添加到 QGIS 中。对于 Landsat 8,红色是波段 4,NIR 是波段 5。你可以在的陆地卫星网站上查看每个波段的光谱范围的描述。

下图显示了 QGIS 图层面板,其中来自 ARD 陆地资源卫星的波段 4 和波段 5 已添加到 QGIS 项目中。

QGIS 目录中 Landsat 8 的 NIR 和红色波段。

每个 Landsat 波段每个像素仅包含一个值,因此默认情况下,数据将以灰度显示(如下所示)。如果您使用不同的数据源将数据存储为多个波段,您可能会看到真彩色图像。这也没关系,你仍然需要知道哪些波段是红色和近红外波段。

单一 Landsat 8 波段示例。

使用栅格计算器计算 NDVI

打开 QGIS 栅格计算器。找到栅格计算器最简单的方法是在 QGIS 界面的左下角进行搜索(见下图)。您可能会看到多个栅格计算器选项。QGIS、GDAL 和 SAGA 都具有 QGIS 中可用的栅格计算器。对于本教程,将使用 QGIS 栅格计算器。它是由齿轮代表的(在下图中以灰色突出显示)。您也可以从处理面板访问 QGIS 栅格计算器。

定位 QGIS 栅格计算器。

在栅格计算器界面中,您会在左侧看到一个框(标记为“图层”),其中显示了 QGIS 中显示的所有栅格图层。每个栅格图层的名称后面会跟一个“@”符号和一个数字。该数字是该栅格图层的波段。在本例中,每个栅格图层只有一个波段(波段号 1)。如果您使用不同的数据,您可能会看到每次输入相同的文件名,但在末尾有不同的数字,代表该图层的各个波段。

名为“表达式”的白色方框是我们输入 NDVI 公式的地方。双击“图层”框中的栅格波段,将其添加到表达式中。您可以单击运算符按钮将它们添加到表达式中,或者使用键盘输入它们。记住使用括号来强制正确的操作顺序。

完成后,您的表达式应该类似于以下内容(用反斜杠“\”替换除法符号):

请记住,您将使用适合您的特定数据源的图层和波段组合来替换“NIRband”和“Redband”。下图显示了使用 Landsat 8 数据的表达式(尽管很难看到)。

QGIS 栅格计算器中的 NDVI 方程。

我们需要解决一个更重要的设置,以确保输出层与输入层并行(对齐)。选择省略号(。。。)用于参考图层选项。然后检查用于计算 NDVI 的任何一个图层(见下图)。这将设置所选图层的 CRS(投影)、像元大小和范围。这意味着栅格计算器结果将与您在此选择的图层对齐。

QGIS 栅格计算器 NDVI 计算的参考图层。

你会注意到“预定义表达式”可能被设置为 NDVI。这个不用担心。栅格计算器正在使用您输入的表达式进行计算。NDVI 是默认的预定义表达式,这只是巧合。

现在你只需要选择一个输出选项和文件名,然后点击“运行”。

准备运行光栅计算器。

栅格计算器运行完毕后,生成的 NDVI 图层将被添加到 QGIS 界面。NDVI 将显示为灰度图像。

NDVI 灰度显示。

创建自定义 NDVI 符号系统

用灰度的方式来看 NDVI 是一种非常无聊的方式。您可以在图层属性的“符号系统”选项卡中自定义符号系统。文章末尾的视频演示了如何创建彩色 NDVI 地图。

结论

NDVI 是一个简单的植被指数,用于识别植被的“绿色”,并在许多不同的应用中使用。计算 NDVI 很简单,最困难的部分是获取数据。QGIS 栅格计算器是计算 NDVI 和其他植被指数的有效方法。学习使用 QGIS 栅格计算器是学习遥感分析的重要一步。

观看视频

最初发表于T5【https://opensourceoptions.com】

远程工作很难让你成为一名出色的数据科学家

原文:https://towardsdatascience.com/remote-work-can-make-it-hard-to-stand-out-as-a-strong-data-scientist-71e9a2a6bc0?source=collection_archive---------37-----------------------

不要因为你太安静而被忽视

照片来自 Unsplash 上的 Alex Litvin

在我职业生涯的早期,我是一个内向的人。我自己呆着,做了我的工作,然后离开了。如果说 2020 教会了我什么的话,那就是它教会了我如何脱颖而出,展示我的工作,同时有效地远程工作。2020 年教会了我,我不能袖手旁观,任由生活发生。相反,我需要想办法引起别人的注意。工作压力很大,有时你会觉得自己的工作没有得到认可。当你不是每天都亲自见到人们时,很容易迷失在背景中。去年我关注的一项技能是提高我的沟通能力。为此,我发现,作为一名远程数据科学家,在进行有效沟通时,有三个方面帮助我的工作得到了关注:

  1. 出席会议。
  2. 尽可能展示我的作品。
  3. 找到一个能提供反馈的导师。

出席会议

当我努力提高我的沟通技巧时,我清楚地意识到我会在全年的会议中花费大量的时间,主要是因为现在一切都很遥远。会议成了一个进行实际和清晰交流的好地方。

会议是不可避免的,很多天可以叠加起来,一个接一个。当我开始远程工作时,我发现自己在开会时会分心。你可以关掉相机,开始做一个项目,回复信息和电子邮件,或者解决一个问题。从一个会议到另一个会议而不被注意变得很容易。

开会时不要一心多用。相反,从你正在做的事情上退一步,活在当下。

会议是和你的同事就正在讨论的话题进行讨论的绝佳机会。当我在日历上看到会议时,我会准备一些问题和讨论点,以便在有机会时讨论。当我在听会议时,我会记下要点和行动项目,记下对自己或团队的要求。当你看日历的时候,你需要问问自己你从会议中得到了什么,为什么你要参加这些会议?如果你不需要参加那个会议,那么要求从名单中删除。这可以让你腾出时间做重要的事情。

展示你的作品

远程工作时另一个引起注意的方法是找机会向你的同事和上级展示你的工作。有许多不同的方式可以分享你一直在做的事情。以下是我关注的几种常见格式:

  • sprint 回顾 —当我准备演示我所做的工作时,我首先想到的是在 Sprint 回顾会上做演示。Sprint Reviews 是展示您的工作成果的绝佳场所,比如仪表板或展示一个简短的分析 PPT。这个会议将把你的工作展示给你的团队和利益相关者以获得反馈。
  • 反馈会议——如果我想从我正在做的一个项目的一组人中得到反馈,我会计划一个特定的会议。这些会议往往是与那些和我一起在这个项目中密切合作的人和那些可能没有看过这个作品但能提供见解的人的会议。在这种类型的会议中,最好准备好你想要讨论的议题,并寻求反馈。您是否需要这些人与您一起审查一些代码,讨论您的分析想法,或者查看仪表板以确定接下来的步骤?知道你希望从会议中得到什么,并与他们分享你正在做的工作。
  • 午餐与学习— 如果你想向更多的观众展示你的作品,那么午餐与学习会是一个很好的论坛。午餐和学习可以将组织中不同的个人聚集在一起,为你的工作提供更广泛的受众。这些类型的会议可以用来展示你的工作,提供一个主题的深入概述,或者介绍一个新概念的教程。在这些活动中,最好为更广泛的观众量身定制演示文稿,在细节上足够专业,但不要太多,一般与会者无法理解。

平均来说,这些往往是我用来向他人展示我的工作的三个主要论坛:(1)冲刺评审,(2)反馈会议,以及(3)午餐和学习。如果这些会议中有一个不适合你的团队,那就去找一个能让你展示自己工作的会议。在这些电话中找出时间,介绍你一直在做的事情和你的发现。你不能坐等做出贡献的机会降临到你头上。你需要找到它。

这些只是我展示作品的一些会议,但还有更多。从 1:1 到与高管会面,工作场所有大量的机会让你可以分享你和你的正在做的事情、你正在产生的影响以及从中获得的经验。

找一个导师

随着我不断参加不同的会议,并在会上进行有效的交流,我找到了一位可以帮助我的导师。我需要一位导师,他是我团队中的一员,能够就我的沟通方式提供反馈。这种指导让我能够提出以下问题:

  • 在那次讨论中有哪些不清楚的地方本可以澄清?
  • 需要分享哪些信息来阐明观点?
  • 这项工作的价值是什么?是呈现的很好,还是可以调整的更清晰?
  • 我计算的指标对理解性能改进或附加值有用吗?

我选择的导师是我团队中的一名首席数据科学家,他有向不同业务级别进行演示的经验。他能够看到我如何在各种场合展示我的作品,并根据他所看到的提供反馈。这种约定让我在任何重要的演讲前后都有可以交谈的人。我和他讨论并征求意见,关于应该呈现什么信息,如何解释,什么水平适合不同的受众。

最后的想法

作为一名远程数据科学家,有三个方面帮助我的工作得到了关注:

  1. 出席会议。
  2. 尽可能展示我的作品。
  3. 寻找能提供反馈的导师。

当你遍历你的团队时,尝试不同的方法来引起别人的注意,并坚持有效的方法。

对一些人来说,坐在幕后完成你的任务可能会奏效。但对其他人来说,你可能希望在团队中变得更加积极,让别人听到你的声音,并作为关键人物受到关注。如果后者是你,那么发展有效沟通的技巧可以帮助你被别人看到。你可以通过积极参与会议和在团队中寻找平台来展示你对业务的积极影响以及你是如何做到这一点的,从而改善你的沟通。

提高有效沟通和展示你的工作的一个关键方法是有一个可以在之前、之中和之后讨论的导师。有一个导师或同事可以提供你可以改进的地方,这是了解什么进展顺利,什么不顺利的一个有价值的方法。找一个你可以学习的人,看看他们能提供什么。

作为一名远程数据科学家,你做了哪些事情而受到关注?

如果你想阅读更多,看看我下面的其他文章吧!

使用深度学习去除身体纹身

原文:https://towardsdatascience.com/remove-body-tattoo-using-deep-learning-4f12942a0fe6?source=collection_archive---------41-----------------------

只需要你 5 分钟的时间,photoshop 在这方面没有优势

照片由 马体·米罗什尼琴科 发自 像素

深度学习很有趣,也是我最喜欢的研究领域。我真的很喜欢玩深度学习实践者正在做的新研究开发。我刚刚从计算机视觉小组的一个同事那里看到了一个令人惊讶的 GitHub repo。我太喜欢它了,以至于想和你们一起分享。

GitHub 回购基于 GANs(生成对抗网络)模型。它可以去除身体部位的任何纹身。

我将带您一步步了解如何使用您自己的映像利用相同的回购。

启动 Google Colab

Google Colab 为我们提供了免费的 GPU 能力,可以在更短的时间内运行或训练我们的深度学习模型。好的一面是它是免费的。

在浏览器中键入以下 URL,然后按 enter 键

[https://colab.research.google.com/](https://colab.research.google.com/)

按作者

启动后,使用您的 Google 帐户登录。如果你有谷歌已经登录,它会选择主要帐户登录。别担心!你的数据在这里是安全的。

登录后,转到文件并打开一个新的笔记本。

按作者

克隆 GitHub Repo

现在,我们必须在刚刚创建的笔记本中运行下面的命令。

!git clone https://github.com/vijishmadhavan/SkinDeep.git SkinDeep

按作者

这个命令将在您的 colab 环境中克隆 GitHub 代码。

现在,下一步,我们必须利用克隆的回购。为此,在笔记本的下一个单元格中运行以下命令。

cd SkinDeep

安装库

在下一个单元中运行以下命令,安装所有必需的库。

!pip install -r colab_requirements.txt

按作者

定义模型架构

现在,是初始化模型架构的时候了。该架构在我们克隆的同一个 GitHub repo 中可用。

在下一个单元格中运行以下代码来初始化模型。

正在加载模型文件

一旦模型初始化,我们就可以下载预先训练好的 GANs 模型去纹身。在下一个单元格中运行以下命令。

MODEL_URL = "https://www.dropbox.com/s/vxgw0s7ktpla4dk/SkinDeep2.pkl?dl=1"urllib.request.urlretrieve(MODEL_URL, "SkinDeep2.pkl")path = Path(".")learn=load_learner(path, 'SkinDeep2.pkl')

定义输入图像

最后,您可以定义想要测试的输入图像。在下面的代码段中替换你的图片 URL。

测试模型并获得结果

现在有趣的部分来了。让我们运行模型,得到答案。在下一个单元格中运行下面几行代码。

p,img_hr,b = learn.predict(img_fast)Image(img_hr).show(figsize=(8,8))

按作者

结论

本文到此为止。我们已经讨论了如何一步一步地使用 SkinDeep 模型去除皮肤上的纹身。

感谢阅读!保持联系。

参考

https://github.com/vijishmadhavan/SkinDeep

使用 Python 从文本中移除个人信息—第二部分

原文:https://towardsdatascience.com/remove-personal-information-from-a-text-with-python-part-ii-ner-2e6529d409a6?source=collection_archive---------8-----------------------

在 Python 中实现隐私过滤器,通过命名实体识别(NER)移除个人可识别信息(PII)

照片来自 Unsplash 上的 Michael Dziedciz

这是我的上一篇关于从短信中删除个人信息的文章的后续。

GDPR 是欧盟的通用数据保护法规。其目的是保护所有欧洲居民的数据。保护数据也是开发人员的内在价值。通过控制对列和行的访问,保护行/列数据结构中的数据相对容易。但是自由文本呢?

在我的上一篇文章中,我描述了一个基于正则表达式和禁用词列表的解决方案。在本文中,我们添加了一个基于命名实体识别(NER) 的实现。完整的实现可以在 github PrivacyFilter 项目中找到。

什么是命名实体识别?

据维基百科 NER 是:

命名实体识别(NER)(也称为(命名)实体识别、实体分块和实体提取)是信息提取的一个子任务,它试图定位非结构化文本中提到的命名实体并将其分类到预定义的类别中,例如人名、组织、位置、医疗代码、时间表达式、数量、货币值、百分比等。

因此,这是所有关于寻找和识别文本中的实体。一个实体可以是一个单词,也可以是一系列连续的单词。实体被分类到预定义的类别中。例如,在下面的句子中,找到了三个实体:实体个人“巴斯蒂安·特龙】,实体组织“谷歌”,实体日期“2007”。

实体识别示例(来源: Spacy.io

NER 是人工智能领域自然语言处理 (NLP)的子集。这个领域包含处理和分析自然语言的算法。当 NER 能够识别自然语言中的实体时,如果这些实体是隐私相关的实体,如组织日期位置,则可以从文本中删除这些实体。

用 NER 过滤 PII

首先,我们需要一个 NLP 处理包。因为所有语言都有自己的语法,所以 NLP 包是按语言进行训练的。我们正在与荷兰,所以我们需要一个理解这一点。我们将使用 Spacy 作为我们的隐私过滤器。

在 Spacy 网站上可以找到一个工具来帮助安装 Spacy。在选择 Python 环境和语言之后,它会给出适当的命令来安装 Spacy:

Spacy 安装工具(来源: Spacy.io

选定的管道(效率或准确性)决定了 NER 模型的准确性与规模和速度。选择“效率”会产生更小、更快的模型,但与“准确度”相比,准确度较低。这取决于您的用例,哪个模型更合适。对于开发,我们选择使用效率模型。运行第一次 NER 分析:

在第 2 行导入 Spacy 包之后,使用 spacy.load() 方法加载一个模型。在这种情况下,加载荷兰的有效模型。模型由其名称指定,该名称与上一步中用于下载模型的名称相同。要切换到准确的荷兰语模型,请将“nl_core_news_sm”替换为“nl_core_news_lg”。对于上面的例子,这导致相同的输出。

一个快速、简单的性能测试表明,加载小模型需要大约 2.0 秒,而加载大模型需要大约 4.5 秒。分析一个句子需要 5.5 毫秒,而不是 6.0 毫秒。大型号似乎需要大约 500 MB 的额外内存。

词性(POS)标签的含义可以在这个网站上找到。对于我们的示例,它们是:

Geert PROPN PERSON     Proper noun, person
werkt VERB             Verb
sinds ADP              Adposition, case marking
2010  NUM DATE         Numeral, date
voor  ADB              Adposition
HAL   PROPN ORG        Proper noun, organisation
.     PUNCT            Punctuation

为了过滤 PII,我们对 POS 类型 NUM 和 PROPN 感兴趣。我们将用描述实体类型的标签来替换 POS 文本元素。

代码的第一部分加载语言模型,并将输入字符串解析为一个标记列表( doc )。第 8–16 行的循环通过遍历文档中的所有标记来构建过滤后的文本。如果令牌的类型是 PROPN、NOUN 或 NUMBER,则用标签< … >替换,其中标签等于 Spacy 识别的实体类型。所有标记都用前缀空格连接到新字符串。前缀是必需的,因为对字符串进行标记会移除这些前缀。对于标点符号,不添加前缀空格(第 12–13 行)。

在循环之后,新字符串的第一个字符是一个空格,因为第 11 或 13 行,所以我们需要删除这个空格(第 17 行)。这导致字符串中没有隐私信息。

有多好?

在上一篇文章中,我们已经建立了一个基于禁用单词列表的隐私过滤器。与 NER 相比,这种方法需要更多的代码和工作。但是他们如何比较呢?

  • NER 要求语法正确的句子。在这种情况下,隐私信息的替换很有效,即使名字拼错了。NER 优于禁语列表。
  • 无论上下文如何,禁用词过滤器都会替换禁用词。尤其是街道名称和城市名称的列表,导致大量不必要的删除单词。例如,像植物名称、动物或像城堡这样的物体这样的词作为街道名称是常见的,并且将从文本中移除。这可能会删除许多不必要的单词,降低结果文本的可用性。NER 会表现得更好。
  • 如果文本语法不正确(例如,问题“你叫什么名字?”的答案“Peter”不会被 NER 过滤掉。这些句子在聊天信息和谈话记录中很常见。NER 方法在这些情况下会失败,因为 NER 算法不能用一个或几个词来确定这些答案的性质。

因此,这完全取决于您的使用案例和所需的过滤级别。这种组合决定了最佳方法是使用禁用列表版本、NER 版本还是两者的组合。后者将结合两种方法的优点(但也是它们的部分缺点)。为了找到最佳的方法,请选取数据的一个子集来过滤和测试不同的算法和/或组合,以找到最合适的方法。

一些例子来比较 NER 和被禁词列表(FWL):

INPUT: Geert werkt sinds 2010 voor HAL.
NER  : <FILTERED> werkt sinds <FILTERED> voor <FILTERED>.
FWL  : <FILTERED> werkt sinds <FILTERED> voor HAL.INPUT: Heert werkt sinds 2010 voor HAL.
NER  : <FILTERED> werkt sinds <FILTERED> voor <FILTERED>.
FWL  : Heert werkt sinds <FILTERED> voor HAL.INPUT: Wat is je naam? Geert.
NER  : Wat is je naam? Geert.
FWL  : Wat is je naam? FILTERED.INPUT: Geert kijkt naar de duiven op het dak.
NER  : <FILTERED> kijkt naar de duiven op het dak.
FWL  : <FILTERED> kijkt naar de <FILTERED> op het dak.

(为了便于比较,所有像这样的标签都被替换为通用标签

第一个例子显示 tat FWL 不能删除公司名称,因为它没有公司名称列表。NER 算法已经在句子中确定“HAL”是一个名词,更具体地说是一个组织。

第二个例子表明,NER 可以处理名称中的类型错误,因为它查看句子的结构,而 FWL 不识别“Heert”作为名称。名称列表只包含正确拼写的版本。

第三个例子表明,NER 需要语法正确的句子来识别“Geert”是一个名字。这可能是对话的文字记录,也可能是聊天中的互动。它展示了 NER 如何很好地运用书面语,但却难以理解口语。

在最后一个例子中,FWL 去掉了单词“duiven ”,因为它不仅描述动物(duiven 在荷兰语中是鸽子的意思),而且还是一个城市的名称。

Github 上的隐私过滤器代码包含两种方法,在初始化期间,可以选择 NER 方法或 FWL 方法。在本文中,我们没有涉及正则表达式,但是选择 NER 方法也将执行正则表达式(NER 不能识别和替换 URL 等)。它还包含一些使用和过滤的示例文本,以查看现实生活中美国案例中两种方法之间的差异。

最后的话

本文和上一篇文章描述了两种删除文本中个人信息的方法。这两种方法各有优缺点,不可能为所有用例选择一种方法。删除更多的隐私信息也会导致删除更多的非隐私信息,从而降低过滤文本的价值。NER 在删除已识别的隐私信息方面更准确,但需要结构良好的句子才能操作。为了获得最大的安全性,甚至可以将两种方法结合起来。请随意在 Github 上试验的实现。

我希望你喜欢这篇文章。要获得更多灵感,请查看我的其他文章:

  • 用 Python 删除文本中的个人信息
  • Python 中字符串的并排比较
  • 使用 Python 实现并行 web 请求
  • 所有公共交通工具都通向乌得勒支,而不是罗马
  • 使用 OTP 和 QGIS 可视化行程时间

免责声明:本文包含的观点和看法仅归作者所有。

用 Python 删除文本中的个人信息

原文:https://towardsdatascience.com/remove-personal-information-from-text-with-python-232cb69cf074?source=collection_archive---------8-----------------------

实施 Python privacy textfilter,通过删除个人身份信息 (PII)来保护用户的隐私。

来自的照片在 Unsplash 上

GDPR 是欧盟的通用数据保护法规。其目的是保护所有欧洲居民的数据。保护数据也是开发人员的内在价值。通过控制对列和行的访问,保护行/列数据结构中的数据相对容易。但是自由文本呢?

为了满足我们的隐私要求,我们可以修改自由文本字段的内容,用标签代替隐私相关信息。文本的含义没有改变,但不能通过匿名与个人联系起来。目标是翻译以下文本(日期为荷兰):

自 2014 年以来,可能性增加了,特别是与 2012 年相比,hè Kees?该系统有不同的功能来处理数据。日期是 2021 年 1 月 12 日(或 2021 年 1 月 12 日或 2021 年 1 月 12 日)。你可以在 nam@provider.com 找到我,我住在鹿特丹。我的地址是马斯特拉大街 13 号,1234AB。我叫托马斯·德·弗里斯,我有痤疮。哦,我用雷尼替丁来做这个。

并替换为

开始可能性就增加了,尤其是和相比,hè ?该系统有不同的功能来处理数据。日期为(或)。你可以通过<邮箱>联系到我,我住在<地方>。我的地址是<地址> <号码>,<邮政编码>。我叫<名字>名字<名字>我有<病>。哦,我用<药>治这个。

本文描述了一个简单的隐私过滤器,它将执行以下操作:

  • 用标签替换日期
  • 用标签替换一个 URL
  • 替换电子邮件地址
  • 替换邮政编码
  • 替换数字
  • 替换城市和地区
  • 将街道名称替换为
  • 替换名字和姓氏
  • 将疾病替换为
  • 将药品名称替换为

添加最后两个是因为医疗信息需要额外的关注。发生的次数很少,但是当这些信息泄露时,影响是巨大的。

前四个动作将使用正则表达式执行,而后五个动作将由替换函数实现。我们的隐私过滤器类具有以下结构:

PrivacyFilter 类实现了不同的过滤器。在创建和初始化之后,这个对象可以用来过滤文本。它与正则表达式和 FlashText 文字处理器一起工作。

使用正则表达式过滤

前四个过滤器用正则表达式实现。替换数字是第一个也是最简单的替换:

这个正则表达式用标签替换所有包含一个或多个数字的单词。这将取代文本中的银行账户、电话号码、身份证号码等。最后执行这个过滤,这样邮政编码和日期就可以被它们相应标记代替,而不是一系列数字标记。

更高级一点的是删除邮政编码的功能。荷兰的邮政编码格式为 0000AA,数字和字母之间有一个可选的空格。使用下面的正则表达式来替换它们:

添加了带标点符号的可选部分,以防止单词前两个字母的四个数字序列被替换,例如,我们不想用“订单 ems”替换“订单 4000 件”。

由于电子邮件地址更复杂的性质,删除电子邮件地址变得有点棘手:

正则表达式在网站上找到的邮件正则表达式 99.99%管用。在那里可以找到用各种语言实现的电子邮件检查器。正则表达式的另一个好来源是 Murani.nl 。

用一个正则表达式删除日期是不可能的,因为月份可以写成数字、缩写和全名。要删除日期,我们需要三个正则表达式:

第一个正则表达式匹配以数字形式写成的日期 dd-mm-yyyy 。支持日期部分之间的不同分隔符。第二个和第三个匹配日期与文本中的月份名称。

使用关键字处理器过滤

如果像前面的替换集一样构建,对地点、街道、姓名、药物和疾病进行过滤需要数千个正则表达式。即使在一个正则表达式中组合一系列名称也是很昂贵的。

为了解决这个问题,Alfred V. Aho 实现了 Aho-Corasick 算法,该算法定位存储在类似字典的结构中的字符串。从所有搜索词创建一个图,遍历该图以解析文本。

示例树(作者图片)

该图包含字符串“AB”、“ABEF”、“AC”和“BD”,因为只有蓝色节点是端节点。当第一个字母是“AB”时,它是一个结束节点,除非后面是字母“C”和“E”。为了在关键字处理器中使用,替换标签与图中的末端节点相关联。这样,所有不同的隐私元素都可以被添加到一个图中,并且仍然可以被适当的标签替换。

该算法有几种实现方式,这里我们将使用 Github 的 Flashtext 实现。该算法在 中描述,在 *中替换和检索文档中的关键字。*它包含一个 KeywordProcessor ,其中添加了关键字及其替换:keyword processor . add _ keyword(' keyword ',' replacement') 。端节点存储要放置到位的替换。

数据集文件夹中,有几个文件每行都有一个关键字,例如一个全部是名字的文件,或者至少有 10.000 个最常见的名字。我们可以将该文件中的所有元素添加到图中,替换标记为<名称为>,如下所示:

在构造函数中,创建了一个区分大小写的关键字处理器。我们使用区分大小写的处理器,因为在荷兰语中有几个名字也是动词。这样,我们只在它们以输入文件中的大写字母开头时替换它们。如果想更安全,可以使用不区分大小写的处理器。

输入文件被读入一个列表(第 5 行和第 6 行),从这个列表中删除重复项(第 7 行),并根据最小长度过滤列表。列表中的每个项目都被添加到处理器中(从而添加到图表中),并带有适当的标签“”。

初始化功能中,可以为街道名称、地点、姓氏、药品等添加更多数据文件。

位置名称按大小进行过滤,因为数据是从 OpenStreetMap 中提取的,空字段、零长度字段和简短缩写位于获取的数据集中。最小尺寸可根据您的安全要求量身定制。

过滤文本

所有函数就绪后,我们可以编写实际的过滤方法:

调用基于正则表达式的方法,然后是区分大小写和不区分大小写的处理器。因为不同的数据集被集成在关键字处理器中,所以只需要执行一次。这将产生所需的输出。

但是性能呢?替换 textparts 可能会变得非常昂贵,尤其是有大量的禁用词,在这种情况下大约。136.000 (!!!).在我的计算机上,类的初始化需要 3.1 秒,但是过滤前面给出的文本只需要 0.5 毫秒。太快了!这对于在实际用例中使用来说足够快了。

最后的想法

本文介绍了一个简单但非常有效的自由文本隐私解析器。改进总是可能的,但这段代码是从文本中过滤隐私信息的最佳方法。

可以通过用记号化器替换算法来进行改进。这使得有可能引入 Levenshtein 函数来测量单词之间的距离,从而支持删除有打字错误的单词。

完整的代码可以在 Github 上找到:【https://github.com/lmeulen/PrivacyFilter

标签和例句是荷兰语,但源代码可以很容易地被其他语言采用。在知识库中,还有一个程序可以收集荷兰语的不同数据集。请注意,这些操作向数据文件添加了第一行数据名称。 PrivacyFiler 类在读取数据文件时过滤第一行。

我希望你喜欢这篇文章。要获得灵感,请查看我的其他文章:

  • 使用 Python 实现并行 web 请求
  • 使用开放数据和开普勒可视化荷兰火车的拥挤程度
  • 使用 OTP 和 QGIS 可视化行程时间
  • 所有公共交通工具都通向乌得勒支,而不是罗马

免责声明:本文包含的观点和看法仅归作者所有。

使用 CV2 和 Keras-OCR 从图像中移除文本

原文:https://towardsdatascience.com/remove-text-from-images-using-cv2-and-keras-ocr-24e7612ae4f4?source=collection_archive---------1-----------------------

如何使用 Python 自动修改图像使其无文本

使用 Cv2 和 Keras 移除文本前后的示例。来源:图片由作者处理图片由 morningbirdphoto 发自 Pixabay 。

简介

在这篇文章中,我将讨论如何快速从图像中去除文本,作为图像分类器或多模态文本和图像分类器的预处理步骤,这些分类器涉及带有文本(如模因)的图像(例如脸书的仇恨模因挑战)。

出于各种原因,删除文本可能是有用的,例如,我们可以使用无文本图像进行数据扩充,因为我们现在可以将无文本图像与新文本配对。

在本教程中,我们将使用 OCR(光学字符识别)来检测图像中的文本,并使用修复(一种填充照片缺失部分以生成完整图像的过程)来删除我们检测到的文本。

该过程

为了擦除图像中的文本,我们将经历三个步骤:

  1. 使用 Keras-ocr 识别图像中的文本并获得每个文本的边界框坐标。
  2. 对于每个边界框,应用一个遮罩来告诉算法我们应该修补图像的哪一部分。
  3. 最后,使用 cv2,应用修补算法来修补图像的蒙版区域,产生无文本图像。

从有文本的图像到无文本图像的过程的表示。来源:图片由作者提供。

实施

Keras-ocr 的简要概述

Keras-ocr提供现成的 OCR 模型和端到端的培训渠道,以构建新的 OCR 模型(参见:https://keras-ocr.readthedocs.io/en/latest/)。

在这种情况下,我们将使用预先训练好的模型,它对我们的任务相当有效。

Keras-ocr 会自动下载检测器和识别器的预训练权重。

当通过 Keras-orc 传递图像时,它将返回一个(单词,框)元组,其中框包含单词的四个框角的坐标(x,y)。

这里有一个简单的例子:

import matplotlib.pyplot as plt
import keras_ocrpipeline = keras_ocr.pipeline.Pipeline()#read image from the an image path (a jpg/png file or an image url)
img = keras_ocr.tools.read(image_path)# Prediction_groups is a list of (word, box) tuples
prediction_groups = pipeline.recognize([img])#print image with annotation and boxes
keras_ocr.tools.drawAnnotations(image=img, predictions=prediction_groups[0])

来源:图片由来自 Pixabay 的 Paul Steuber 提供。

如果我们看一看prediction_groups,我们会看到每个元素对应一对单词框坐标。

例如,prediction_groups[0][10]应该是这样的:

('tuesday',
 array([[ 986.2778 ,  625.07764],
        [1192.3856 ,  622.7086 ],
        [1192.8888 ,  666.4836 ],
        [ 986.78094,  668.8526 ]], dtype=float32))

数组的第一个元素对应左上角的坐标,第二个元素对应右下角,第三个元素是右上角,而第四个元素是左下角。

文本边界框及其坐标的表示。来源:图片由作者提供。

cv2 修复功能概述

当使用 OpenCV 应用修补算法时,我们需要提供两幅图像:

  1. 带有我们想要删除的文本的输入图像。
  2. **蒙版图像,显示我们要删除的文本在图像中的位置。**第二幅图像应该与输入图像具有相同的尺寸。蒙版将显示非零像素,这些像素对应于输入图像中包含文本并需要修复的区域,而零像素区域不会被修改。

Cv2 具有两种可能的修复算法,并允许应用矩形、圆形或线条遮罩(参见:https://opencv 24-python-tutorials . readthedocs . io/en/latest/py _ tutorials/py _ photo/py _ inpainting/py _ inpainting . html)

在这种情况下,我决定使用线条蒙版,因为它们更灵活地覆盖不同方向的文本(矩形蒙版只适用于平行或垂直于 x 轴的文字,圆形蒙版会覆盖不必要的区域)。

为了应用遮罩,我们需要提供线条起点和终点的坐标,以及线条的粗细:

起点将是框的左上角和左下角之间的中点,而终点将是右上角和右下角之间的中点。

对于厚度,我们将计算左上角和左下角之间的直线长度。

import math
import numpy as npdef midpoint(x1, y1, x2, y2):
    x_mid = int((x1 + x2)/2)
    y_mid = int((y1 + y2)/2)
    return (x_mid, y_mid)#example of a line mask for the word "Tuesday"box = prediction_groups[0][10]x0, y0 = box[1][0]
x1, y1 = box[1][1] 
x2, y2 = box[1][2]
x3, y3 = box[1][3] 

x_mid0, y_mid0 = midpoint(x1, y1, x2, y2)
x_mid1, y_mi1 = midpoint(x0, y0, x3, y3)
thickness = int(math.sqrt( (x2 - x1)**2 + (y2 - y1)**2 ))

现在我们可以创建我们的面具:

mask = np.zeros(img.shape[:2], dtype="uint8")
cv2.line(mask, (x_mid0, y_mid0), (x_mid1, y_mi1), 255, thickness) 

我们还可以检查屏蔽区域,以确保它正常工作。

masked = cv2.bitwise_and(img, img, mask=mask)
plt.imshow(masked)

与单词“星期二”相对应的掩蔽区域。

最后,我们可以修复图像。在这种情况下,我们将使用cv2.INPAINT_NS,这是指在论文**“纳维尔-斯托克斯,流体动力学,图像和视频修复”中描述的修复算法。**

img_inpainted = cv2.inpaint(img, mask, 7, cv2.INPAINT_NS)
plt.imshow(img_inpainted)

正如您所看到的,作品“星期二”已从图像中移除。

实施

现在让我们总结一下,创建一个函数来修复任何图像中的文本。

我们只需要生成一个框的列表,迭代屏蔽和修复每个文本框。

import matplotlib.pyplot as plt
import keras_ocr
import cv2
import math
import numpy as npdef midpoint(x1, y1, x2, y2):
    x_mid = int((x1 + x2)/2)
    y_mid = int((y1 + y2)/2)
    return (x_mid, y_mid)pipeline = keras_ocr.pipeline.Pipeline()def inpaint_text(img_path, pipeline):
    # read image
    img = keras_ocr.tools.read(img_path)
    # generate (word, box) tuples 
    prediction_groups = pipeline.recognize([img])
    mask = np.zeros(img.shape[:2], dtype="uint8")
    for box in prediction_groups[0]:
        x0, y0 = box[1][0]
        x1, y1 = box[1][1] 
        x2, y2 = box[1][2]
        x3, y3 = box[1][3] 

        x_mid0, y_mid0 = midpoint(x1, y1, x2, y2)
        x_mid1, y_mi1 = midpoint(x0, y0, x3, y3)

        thickness = int(math.sqrt( (x2 - x1)**2 + (y2 - y1)**2 ))

        cv2.line(mask, (x_mid0, y_mid0), (x_mid1, y_mi1), 255,    
        thickness)
        img = cv2.inpaint(img, mask, 7, cv2.INPAINT_NS)

    return(img)

而这里是最终的结果(之前 vs 之后):

我还列举了另外几个例子:

来源:图片由作者处理来自黑客日的模因生成。

来源:图片由作者通过处理来自 Pixabay 的 Alfred Derks 的图片生成。

请注意,如果您想要保存图像,您需要将其转换为 RGB 格式,否则颜色将会反转!

img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)cv2.imwrite(‘text_free_image.jpg’,img_rgb)

如果您只对删除某些单词感兴趣,可以包含如下 if 条件:

给定要删除的单词列表

remove_list = [‘tuesday’, ‘monday’]

我们可以在 for 循环中包含if条件

def inpaint_text(img_path, remove_list, pipeline):
    # read image
    img = keras_ocr.tools.read(img_path)
    # generate (word, box) tuples 
    prediction_groups = pipeline.recognize([img])
    mask = np.zeros(img.shape[:2], dtype="uint8")
    for box in prediction_groups[0]:
        if box[0] in remove_list: x0, y0 = box[1][0]
           x1, y1 = box[1][1] 
           x2, y2 = box[1][2]
           x3, y3 = box[1][3] 

           x_mid0, y_mid0 = midpoint(x1, y1, x2, y2)
           x_mid1, y_mi1 = midpoint(x0, y0, x3, y3)

           thickness = int(math.sqrt( (x2 - x1)**2 + (y2 - y1)**2 ))

           cv2.line(mask, (x_mid0, y_mid0), (x_mid1, y_mi1), 255,    
           thickness)
           img = cv2.inpaint(img, mask, 7, cv2.INPAINT_NS)

    return(img)

这当然只是一个快速区分大小写的例子,展示了如何将修补应用于特定的单词列表。

结束注释

在本文中,我们讨论了如何实现一种算法,通过使用 Keras 的预训练 OCR 模型和使用 cv2 的修复算法从图像中自动删除文本。该算法似乎可以很好地从图像中快速删除文本,而无需为这一特定任务训练模型。当文本框靠近其他对象时,它的性能通常不太好,因为它可能会扭曲周围的环境。

编辑:这个实现是使用 Python 3.7 执行的,我收到了一些使用 OpenCv 遇到的问题的反馈,这些问题在使用 Python 3.9 等其他版本时会发生。我建议尝试用 3.7 来解决这个问题。

我感谢任何反馈和建设性的批评!我的电子邮件是 carbor100@gmail.com

通过代理规范化激活消除 CNN 中的批处理依赖

原文:https://towardsdatascience.com/removing-batch-dependence-in-cnns-by-proxy-normalising-activations-bf4824eb0ba4?source=collection_archive---------33-----------------------

思想和理论

记忆高效的卷积神经网络训练

这篇文章旨在介绍我们在 NeurIPS 2021 上发表的论文“代理规范化激活以匹配批处理规范化,同时移除批处理依赖”中介绍的新技术“代理规范”。

代理范数为卷积神经网络的更高存储效率的训练铺平了道路。它保留了批处理规范化的优点,同时消除了以前导致低效执行的批处理依赖的复杂性。随着机器学习模型的规模不断增长,数据集不断变大,Proxy Norm 可以帮助人工智能工程师确保未来的执行效率。

标准化的挑战

标准化对于成功地将神经网络扩展到大而深的模型是至关重要的。虽然标准化的范围最初仅限于输入处理[1],但随着批量标准化技术[2]的引入,标准化被带到了另一个层面,批量标准化技术保持了整个网络中的中间激活标准化。

Batch Norm 强加的特定归一化是一种通道归一化。具体来说,这意味着 Batch Norm 通过减去通道方式的平均值并除以通道方式的标准偏差来标准化中间激活。值得注意的是,Batch Norm 实现了这种通道标准化,而没有改变神经网络的表达能力。这意味着批量标准化网络的表达能力与非标准化网络的表达能力相同。我们将会看到这两个特性,即通道标准化保持表达性,都是有益的。

然而,批处理规范带来了同样重要的复杂性:批处理依赖。由于整个数据集的通道均值和方差不容易计算,Batch Norm 通过将当前小批量视为整个数据集的代理来近似这些统计数据(参见图 1)。给定小批量统计在批量定额计算中的使用,由神经网络关联到给定输入的输出不仅取决于该输入,还取决于小批量中的所有其他输入。换句话说,小批量统计对全批量统计的近似在神经网络的计算中引入了批量依赖性

图 1:每个子图对应于一种特定的归一化技术,其特征在于各组分量共享相同的归一化统计量。在每个子图中,中间激活的张量由批轴 B、通道轴 C 和空间轴(H,W)构成。图片由作者提供,改编自[3]。

现在让我们更详细地看看 Batch Norm,以理解为什么通道规范化和表达性的保持是有益的,以及为什么批处理依赖是一个复杂的问题。在此之后,我们将引入新技术代理规范,如我们在 NeurIPS 2021 论文“代理规范化激活以匹配批量规范化,同时移除批量依赖性”中所述。

批处理规范的第一个好处是:通道标准化

如上所述,批处理规范在每一层保持中间激活通道式标准化“接近”非线性。这种渠道标准化有两个好处:

  1. 由于非线性“作用于”接近归一化的信道式分布,所以相对于这些信道式分布,它实际上是非线性的。每一层都增加了表达能力,神经网络有效地利用了它的整体深度。
  2. 由于不同的信道具有可比较的方差,信道被很好地平衡,并且神经网络有效地使用其整个宽度。

**简而言之,通道标准化允许神经网络有效地利用其全部能力。**然而,这种优势并没有保留在批次标准的原型批次无关替代品中(见图 1)。事实上,虽然实例范数[4]保留了通道式归一化,但层范数[5]或组范数[6]没有保留它。在图 2 的左上图中,这转化为具有层范数和组范数的不可忽略的通道方式平方均值。

批处理规范的第二个好处是:保持表达能力

如前所述,采用批次范数的通道标准化不会以改变神经网络的表达能力为代价。这意味着,如果适当选择批处理范数的尺度和移位参数,任何非标准化网络都可以等效地表示为批处理标准化网络(在全批处理设置中)。相反,如果适当选择卷积权重和偏差,任何批量标准化网络(在全批量设置中)都可以等效地表示为非标准化网络。简而言之,批处理规范相当于对神经网络的解空间进行简单的重新参数化。

这种对表达性的保留是批处理规范的第二个好处。为了理解为什么表达性的这种保持是有益的,理解为什么用与批次无关的替代物替代批次标准来改变表达性是有害的是有用的。在组范数的情况下,表现性改变的症状是实例的平均值和标准偏差的方差的缺乏,如图 2 中最右边的两个子曲线所示。这种实例统计方差的缺乏对于学习是有害的,因为它倾向于与神经网络深层中的高级概念的表达不兼容。

图 2:在 ImageNet 上用各种范数训练 ResNet-50 时,实例均值(上)和实例标准差(下)的均方值(左)和方差(右)。在层的不同深度(x 轴)归一化之后,计算实例统计。图片作者。

批次规范的复杂性:批次依赖性

批次范数的批次依赖性的主要症状是在每个小批次中随机选择不同输入产生的噪声。当用小批量统计近似全批量统计时,这种噪声在批量定额层之间传播,并在每个批量定额层“加剧”。**因此,小批量越小,噪音越大。**这种现象转化为批量范数的特定正则化[7],其强度取决于噪声幅度,因此取决于小批量大小。

不幸的是,这种规范化不容易控制。当目标是降低这种规范化的强度时,实现这一目标的唯一方法是增加小批量。根据任务和所需的调整强度,Batch Norm 会在与最佳性能兼容的情况下对最小批量施加一个下限。当“计算”小批量低于此下限时,保持最佳性能需要跨多个工人进行“昂贵的”统计同步,以产生大于“计算”小批量的“标准化”小批量[8]。批处理依赖可能导致的主要问题是执行效率低下。

当使用 Graphcore 的 IPU 时,解决这个问题将会产生真正的影响,因为 IPU 提供的额外加速和节能是以更严格的内存限制为交换条件的。即使替代加速器较少依赖本地内存,这个问题在未来也可能变得很严重。随着数据集变得越来越大,我们确实希望使用越来越大的模型会带来更强的内存约束。对于给定的模型大小,使用更大的数据集也意味着需要更少的正则化,导致需要越来越大的“归一化”小批量,以保证使用批量范数时的最佳性能。

代理定额:保留批量定额的优点,同时消除批量依赖性

那么我们如何在去除批处理依赖的同时保留批处理规范的好处呢?

批处理规范的两个好处(即通道标准化和表达能力的保持)不能同时保留与原型批处理无关的规范。一方面,Layer Norm 在保持表现性方面做得很好,但只是以通道反规范化为代价。另一方面,实例范数保证了通道规范化,但代价是表达能力的强烈改变。虽然组规范在层规范的问题和实例规范的问题之间提供了更好的折衷,但它仍然不能解决问题。简而言之,所有典型的批次无关规范都会导致性能下降。

为了解决这个问题,我们需要一个独立于批处理的规范,它可以避免通道反规范化,同时保持表达能力。我们可以通过指出以下几点来使这两个要求更加精确:

  1. 负责通道式反规格化的主要操作是:(I)可学习的仿射变换;(二)激活功能。
  2. 当将仿射运算插入到神经网络中时,保持了表达能力。

这些观察指导了我们的新技术代理规范的设计。代理范数将归一化操作的输出同化为高斯“代理”变量,该变量被假定为接近信道方式归一化。该高斯代理被馈入与真实激活相同的两个操作中,即相同的可学习仿射变换和相同的激活函数。在这两个操作之后,代理的均值和方差最终被用于标准化真实激活本身。这如图 3 所示。

图 3:通过在预先存在的黑色操作之上添加“廉价的”红色操作,代理范数被并入神经网络。图片作者。

代理范数能够保持表达性,同时补偿通道式反规格化的两个主要来源,即可学习的仿射变换和激活函数。这导致我们采用一种与批次无关的归一化方法,即将代理范数与层范数或具有少量组的组范数相结合。从图 2 中可以看出,这种与批次无关的方法保留了通道标准化,同时最小化了表达性的任何改变。通过这种方法,我们保留了 Batch Norm 的优点,同时消除了批次依赖性。

下一个问题是这种方法在实践中是否也能带来好的表现。将批次范数与批次无关方法进行比较时,需要特别小心,以正确考虑批次依赖与批次范数所带来的额外正则化。为了“减去”这种正则化效应,我们在每个实验中都加入了额外的正则化。

从图 4 中可以看出,当特别小心时,我们的独立于批次的方法的 ImageNet 性能在各种模型类型和大小上一致地匹配或超过批次标准(注意,我们的 EfficientNet 变体在另一篇论文中介绍)。这意味着我们的批处理独立方法不仅在行为上,而且在性能上也符合批处理规范!

**我们认为,作为我们分析的副产品,虽然有效的规范化对于良好的 ImageNet 性能是必要的,但它也必须伴随着适当的规范化。**在更大的数据集上,我们预计有效的归一化本身就足够了,因为需要的正则化较少[9,10]。

图 4:使用批定额、组定额和组定额+代理定额的各种模型类型和大小的 ImageNet 性能。图片作者。

结论

在这项工作中,我们深入研究了卷积神经网络归一化的内部工作原理。我们已经收集了理论和实验证据,证明有效的标准化应该:(I)保持通道标准化和(ii)保持表达性。虽然批处理规范保持了这两个属性,但它也引入了批处理依赖的复杂性。

当研究批次范数的原型批次无关替代方案时,我们发现难以同时获得通道标准化和保持表达性。这导致我们设计了新的技术代理规范,它保留了表达性,同时保持了通道的规范化。然后,我们采用了一种与批次无关的归一化方法,该方法将代理范数与层范数或具有少量组的组范数相结合。我们发现,这种方法在行为和性能上始终符合批量标准,同时始终保持批量独立性。

我们的方法为卷积神经网络的更高存储效率的训练铺平了道路。这种内存效率为 Graphcore 的 IPU 等加速器提供了优势,这些加速器利用本地内存来提高执行效率。从长远来看,我们预计这种内存效率将是至关重要的,即使是在替代硬件上。

看报

谢谢你

感谢 Dominic Masters、Zach Eaton-Rosen 和 Carlo Luschi,他们也为这项工作做出了贡献,也感谢我们其他同事富有洞察力的讨论。

参考

[1] Y. LeCun,L. Bottou,G. B. Orr 和 K.-R. Müller,有效反向投影 (1998),神经网络:交易技巧

[2] S. Ioffe 和 C. Szegedy,批量归一化:通过减少内部协变量移位加速深度网络训练 (2015),ICML 2015

[3] N. Dimitriou 和 O. Arandjelovic,重影规范化新论 (2020),arXiv 2020

[4] D. Ulyanov,A. Vedaldi 和 V. S. Lempitsky,实例规范化:快速风格化的缺失成分 (2016),arXiv 2016

[5] L. J. Ba,J. R. Kiros 和 G. E. Hinton,图层归一化 (2016),arXiv 2016

[6]吴彦祖,何国光,群体规范化 (2018),2018

[7]罗,王,邵伟,彭,【2019】,2019

[8] C. Ying,S. Kumar,D. Chen,T. Wang 和 Y. Cheng,超级计算机规模的图像分类 (2018),NeurIPS 2018 和开源软件系统研讨会

[9] A .科列斯尼科夫,l .拜尔,x .翟,j .普伊格韦,j .容,s .盖利和 n .豪斯比,大迁移(BiT):一般视觉表征学习 (2020),2020

[10] A. Brock,S. De,S. L. Smith 和 K. Simonyan,无需归一化的高性能大规模图像识别 (2021),ICML 2021

在 Python 中移除重复或相似的图像

原文:https://towardsdatascience.com/removing-duplicate-or-similar-images-in-python-93d447c1c3eb?source=collection_archive---------10-----------------------

当训练使用图像作为输入的机器学习模型时,检查数据集中相似或重复的图片是相关的。这里有一个最有效的方法。

Anton Maksimov juvnsky 在 Unsplash 上的照片

介绍

检测重复图像的最简单的方法之一是通过检查所有值是否相同来逐个像素地进行比较。然而,当测试大量图像时,这变得非常低效。

第二种非常常见的方法是提取两幅图像的加密哈希,并使用 MD5 或 SHA-1 等流行的哈希算法比较它们是否相同。虽然这种方法更有效,但它只能检测相同的图像。如果其中一个像素不同(例如,由于光照的轻微变化),哈希将完全不同。

其原因是这些加密算法使用的数据仅基于图像的像素和随机种子。相同的数据和相同的种子会产生相同的结果,但是不同的数据会产生不同的结果。

对于我们的用例,相似的图像有相似的散列更有意义,对吗?

这正是图像哈希算法的目标。他们专注于图像的结构及其视觉外观来计算散列。根据您的使用情况,有些算法会比其他算法更好。

例如,如果您希望形状相同但颜色不同的图像具有相同的哈希,可以使用感知哈希或差异哈希。

phash 算法检测到的重复项 ImageHash 存储库的第一个例子。

请记住,总是有可能使用更大的图像哈希大小,并获得不太相似的图像,这取决于用例,必须进行超调优。

履行

在 Python 中使用,我推荐 ImageHash 库,支持多种图像哈希算法,如平均感知差异小波HSV-color抗裁剪

可以使用以下命令轻松安装它:

pip install imagehash

如上所述,每个算法可以调整其散列大小,该大小越高,散列对变化越敏感。根据我的经验,我推荐使用散列大小为 8 的 dhash 算法和存储库中可用的 z 变换。

下面您可以看到加载图像、转换图像和计算 d-hash 所需的代码,这些代码封装在 dhash_z_transformed 方法中。

要在数据管道中应用它,只需用想要散列的图像的路径调用 dhash_z_transformed 方法。如果该方法为两个图像产生相同的散列,这意味着它们非常相似。然后,您可以选择通过保留一个副本或两个都不保留来删除副本,这取决于您的用例。

在我最近的一个项目中,项目按散列分组的例子。

我鼓励您通过更改 hash_size 来调整敏感度阈值,以便根据您的用例获得更好的结果。

结论

在这篇短文中,我介绍了在训练接收图像作为输入的机器学习模型时要考虑的一个重要步骤。

我没有展示如何只检测相同的图像,而是介绍了图像哈希算法来识别相似的图像(不同大小但相同的图像,亮度的轻微变化……)。最后,我提供了如何使用外部库在 Python 中计算这些图像散列的例子。

如果你想发现更多像这样的帖子,你可以在下面找到我:

  • GitHub
  • 领英
  • 个人网站

消除 80%盲点:如何使非结构化组织数据可用于决策

原文:https://towardsdatascience.com/removing-the-80-blind-spot-how-to-make-unstructured-organizational-data-available-for-decision-e5ddc8147135?source=collection_archive---------20-----------------------

图片作者。

使用预训练语言表示模型进行非结构化数据分析

据估计,超过 80%的企业数据是非结构化的(电子邮件、文档、工作描述等)。).另一方面,大多数决策纯粹基于结构化数据。

在这篇文章中,我将描述我们如何在预先训练的机器学习模型和语义搜索的帮助下,将非结构化数据转化为有用的信息。具体的用例是关于我们员工的非结构化个人资料描述,以及我们如何开始询问关于员工的任意问题(例如,我可以和谁谈论小狗?).

选择正确的技术

我们通过对知识图技术的研究,开始了非结构化数据表示的任务。

知识图是在图结构数据模型(下图)中通过数据的含义和上下文来表示数据的一种方式。这个想法很吸引人,因为你可以通过对象之间的关系而不是特定的关键字来搜索知识图。

用节点、边和属性对知识图进行传统表示(图片由作者提供)。

虽然在概念上和视觉上很吸引人,但我们意识到,与传统数据库相比,它只带来了极小的价值,因为您仍然需要自己定义所有的关系(例如{马特*访问过*卢浮宫}以及{卢浮宫法兰西} `)。

我们没有使用知识图表,因为您仍然需要自己构建数据。

接下来,我们将注意力转向预先训练的语言表示模型。这些是机器学习模型(例如 BERT ),已经在数百万个文本文档上进行训练,以学习各种上下文中单词之间的表示和连接。

我们可以将每个对象表示为一系列数字,而不是直接定义人和对象之间的关系(知识图方法)。这些数字不能直接解释,但它们带有潜在的(隐藏的)含义(例如,猫和狗比猫和大象彼此更亲近。)

一般工作流程如下:

  1. 首先,我们收集员工的非结构化描述(文本),
  2. 其次,我们将这些描述传递给预先训练好的语言模型。
  3. 第三,我们收到每个人的一个数字列表,它用数字表示了对一个人的描述。

图二。使用预先训练的语言模型将非结构化文本编码为数字向量的过程(图片由作者提供)。

在这一点上,我们似乎没有取得多大的成就,因为每个人的神秘数字列表并不比非结构化文本好多少。但是机器喜欢数字,因为这个数字向量总是有相同的长度(在我们的例子中是 300 个元素),我们可以很容易地开始计算。把每个数字想象成一个点就是空间。如果我们每个人只有 3 个数字,我们可以在三维图表上看到它们(下图)。这些点彼此越接近,它们就越相似。现在,我们不能画一个 300 维的图表,但逻辑是一样的。

图 3。预训练模型的输出是一个数字向量。当我们有了物体的数字表示,我们就可以开始测量它们之间的距离和关系。上图显示了一个人的简化(三维)表示(图片由作者提供)。

语义搜索

语义搜索是指根据搜索短语的含义搜索内容的能力。所以,当我问“谁去过法国?”我也应该能找到所有写过参观埃菲尔铁塔的人。

此时,我们已经对员工进行了编码(通过预先训练的模型),语义搜索可以按如下方式进行:

  1. 问个问题。
  2. 通过预先训练好的语言表征模型传递问题。
  3. 计算问题和员工描述之间的距离。
  4. 返回到问题表示的距离最短的员工。

图四。通过测量问题的数字表示和答案之间的距离来执行语义搜索(图片由作者提供)。

示范

最后,让我展示两个来自我们的员工搜索应用程序的例子。出于隐私原因,真实姓名和暴露的工作职位是隐藏的。

在第一个例子中,我试图通过“谁知道加密?”这个问题找到了解加密货币的员工。第一个搜索结果是一位对投资感兴趣的同事,他提到了加密货币。第二个结果也谈到了投资但没有提到加密货币。这正是我们想要实现的——系统理解我们的问题与投资等概念密切相关。

语义搜索在预先训练的语言模型的帮助下,能够根据意思而不是特定的关键字进行查询!

图 5。语义搜索的例子——引擎理解“加密”和投资股票是相似的概念(来自我们的应用程序的快照)。

下面的例子是关于寻找可以一起组建乐队的同事。请注意,我提到了两个概念:1)拥有一个车库,2)希望组建一支乐队。这个系统足够聪明,能够理解这里的重点是乐队制作。第一个人似乎有很长的乐队演奏经验,第二个人是音乐制作人(尼斯🙂).此外,第二个结果没有提到单词“band ”,但是系统知道音乐制作和乐队制作密切相关。

图 5。语义搜索的例子——引擎知道:1)重点是乐队制作,而不是拥有一个车库,2)音乐制作与乐队制作密切相关。

后续步骤

同事的语义搜索只是非结构化数据如何用于任意信息查询的例子之一。其他常见任务包括情感分析、关于文本的抽取问题回答、表格问题回答、命名实体识别和自动文本摘要。要了解更多,请看这篇文章中的(带有描述和代码示例)。

消除生成对抗网络中的“对抗”

原文:https://towardsdatascience.com/removing-the-adversarial-in-generative-adversarial-networks-5ba4110d0b8c?source=collection_archive---------17-----------------------

来源

无竞争 GAN 的再思考

生成对抗网络是深度学习的一个重要发展;它的公式启发了新一代的模型集合,它们以各种方式相互作用,产生令人难以置信的结果。

竞争是甘设计的血液——“对抗性”是它的名字。鉴别器和生成器这两个模型相互竞争。理想的结果是:生成器在生成令人信服的图像方面变得很棒,因为它在与一个有价值的对手(鉴别者)竞争。

然而,有趣的是,可以在没有竞争的情况下重构 GAN。这种重构在许多方面都优于竞争对手的 GAN 设计。

作为基础——GANs 设计用于生成数据,最常用于图像(尽管它们也可用于其他目的)。它由两个模型组成:

  • 发电机。给定一个随机输入作为“种子”,生成一幅图像。
  • 鉴频器。给定一幅图像,分类它是真的(来自数据集)还是假的(由生成器生成)。

相应地,模型的目标是:

  • 发电机的目标。为了最大化鉴别器的损失,生成与数据集图像无法区分的令人信服的图片。
  • 鉴别器的目标通过对真实或虚假图像进行高性能分类,最大限度地减少鉴别器的损失

这种竞争很重要:生成器和鉴别器持续地处于“学习循环”中。如果生成器生成令人信服的图像,鉴别器应该能够绕过它,并对生成器提出新的挑战。如果鉴别器成功地利用了生成器中的某种弱点,后者应该能够解决它。

单模型设计对损失函数进行优化。当模型对损失感到“满意”时,进展通常会在一段时间后停滞。另一方面,在竞争状态下,有两个学习实体——而不是一个。这使得合奏作为一个整体能够持续学习更长的时间。

然而,这可能是一个问题。GANs 中的竞争是以一种可能永远无法解决的往复方式制定的。GANs 的一个常用算法是梯度下降-上升,它在两个步骤之间交替。

  1. 发生器执行梯度下降步骤,将鉴频器损耗降至最低。
  2. 鉴频器执行梯度上升步骤,使鉴频器损耗最大化。

结果是——没有收敛的保证。发生器和鉴别器轮流混乱地“戳”对方,结果发生器和鉴别器都无法以更稳定的方式工作。这将理想地确保发生器和鉴别器不会“压倒”彼此,或者遵循经常导致不规则训练结果的不可预测的行为。

红线代表理想模型应该收敛的理想解。来源: OffConvex

还有另外两个问题。首先,梯度下降-上升的循环模式需要极慢的学习速度。第二,没有办法衡量学习的进展,因为鉴别器和发生器是相互对立的。

梯度下降-上升的其他变体已经提出了解决不收敛问题的解决方案。尽管如此,他们都有下降和上升的核心框架,这是两个模型之间竞争的数学公式。

这就引出了一个问题——GAN 问题可以用一种不涉及梯度下降和上升的方式来重新构建吗?

答案是肯定的。Paulina Grnarova 等人提出“生成最小化网络”(GMN)作为网络,其中鉴别器和生成器都工作以最小化损失函数,而不是以竞争的方式在相反的方向上工作。

人们可以从博弈论的角度来考虑 GAN 框架。鉴别器和生成器是零和游戏中的参与者,也就是说,一个参与者的收益是以另一个参与者的成本为代价的,所有参与者的所有收益和成本总和为零/相互抵消。

就本文的目的而言,玩家的效用表示在给定他们的状态和其他玩家的状态的情况下,他们在游戏中“赢”的可能性有多大。也就是说,效用考虑了两个参与者。

U 代表鉴频器的效用。因为这是一个零和游戏,所以生成器的效用是-U*。因此,我们有以下目标:*

  • 鉴别器想要最大化鉴别器的效用。它想增加最坏情况下的效用。
  • 发生器希望最小化鉴别器的效用。它想降低鉴别器的最坏情况效用。

在这两种情况下,玩家都试图最大化自己的效用。两个参与者可以联合收敛到一个解决方案,在这个方案中,鉴别器和生成器都不能提高它们的效用。这就是所谓的纯均衡*,我们可以定义如下(大量滥用符号):*

至关重要的是,效用 U 不是一个恒定的变量,而是根据玩家的当前状态不断变化的。均衡导致了二元差距的概念,二元差距可以被认为是两个玩家离达到均衡有多远:

因此,鉴别器和生成器的目标都可以被框定为最小化对偶间隙。虽然这是一个零和游戏,但两个模型都将对偶差距最小化,以增加自己的效用。此外,两者都朝着纯粹的平衡努力,这可以被认为是梯度下降-上升的更有组织的目标。

从经验上看,使用二元差距作为目标是有希望的。

  • 收敛速度比其他常用的 GAN 物镜更快。
  • 就解决方案的位置而言,使用二元差距来转换地貌非常接近真实地貌(见下图)。

最小最大目标——一个玩家努力上升,另一个努力下降。二元差距目标——两个玩家都努力下降。两者的最优解都在同一个地方。来源: Gnarova 等人。

在性能方面,它远远超过现有的模型。

IS =初始得分(越高越好)。DG =二元差距。WGAN =的变化。来源:格纳罗娃等人。

总而言之,

  • 生成敌对网络通常通过梯度下降和上升以竞争的方式进行优化,其中鉴别器和生成器以相反的方向进行优化。
  • 二元差距是鉴别器和生成器的目标之间的差异。它以一种方式改变了优化前景,使得两个模型都为最小值而优化。
  • 使用对偶间隙显示了改进的性能、收敛速度和收敛稳定性。

在这里看论文。

感谢阅读!

如果你喜欢,你可能会喜欢我的一些关于深度学习研究有趣的最新发展的文章:

*</5-exciting-deep-learning-advancements-to-keep-your-eye-on-in-2021-6f6a9b6d2406>

除非另有说明,否则图片由作者创作。*

重命名熊猫数据框架中的列

原文:https://towardsdatascience.com/renaming-columns-in-a-pandas-dataframe-1d909360ddc6?source=collection_archive---------8-----------------------

熊猫帮助你开始数据分析的提示和技巧

克里斯·劳顿在 Unsplash 上拍摄的照片

在数据分析中,我们可能会处理没有列名或列名包含一些不需要的字符(例如空格)的数据集,或者我们可能只是想重命名列以获得更好的名称。这些都要求我们重命名 Pandas 数据框架中的列。

在本文中,您将学习 5 种不同的方法。这篇文章的结构如下:

  1. 将名称列表传递给columns属性
  2. 使用rename()功能
  3. 读取 CSV 文件时重命名列
  4. 使用columns.str.replace()方法
  5. 通过set_axis()重命名列

为了演示,我们将使用 Kaggle 上可用的 Titanic 数据集的子集。

def load_data(): 
    df_all = pd.read_csv('data/titanic/train.csv')
    # Take a subset
    return df_all.loc[:, **['PassengerId', 'Pclass', 'Name', 'Sex']**]df = load_data()

作者图片

请查看笔记本获取源代码。

1.将姓名列表传递给columns属性

Pandas DataFrame 为我们提供了一个columns属性来访问列名。也可以通过直接将新名称列表传递给columns属性来重命名列名。

# Access column names
>>> df.**columns**
Index(['PassengerId', 'Pclass', 'Name', 'Sex'], dtype='object')# Rename PassengerId to Id, Pclass to Class
>>> df**.columns = ['Id', 'Class', 'Name', 'Sex']**

使用columns属性重命名列(图片由作者提供)

然而,这种方法的一个缺点是,我们需要为所有列提供名称,即使我们只想重命名其中的一些列。否则,我们将得到一个"值错误 : 长度不匹配":

值错误:长度不匹配

2.使用rename()功能

Pandas 有一个名为rename()的内置函数来更改列名。当您想要重命名一些选定的列时,这很有用。

2.1 使用字典映射重命名列

要重命名列,我们可以将字典传递给columns参数。键是要更改的列,值是这些列的新名称。我们还可以将参数inplace设置为True,以便在现有的数据帧中进行更改。

# Rename columns
#   PassengerId  ->  Id
#   Pclass       ->  Classdf.rename(
    **columns=({ 'PassengerId': 'Id', 'Pclass': 'Class'})**, 
    **inplace=True**,
)df.head()

用熊猫 Rename()重命名列(图片由作者提供)

2.2 使用函数重命名列

除了字典映射,我们还可以向columns参数传递一个函数。例如,要将列名转换成小写,我们可以传递一个str.lower函数:

df.rename(**columns=str.lower**).head()

用熊猫 Rename()重命名列(图片由作者提供)

我们还可以创建一个自定义函数,并将其传递给columns参数。

**def toUpperCase(string):
    return string.upper()**df.rename(**columns=toUpperCase**).head()

我们也可以使用 lambda 表达式:

df.rename(**columns=lambda s: s.upper()**).head()

当您需要用相同的命名约定更新许多列或所有列时,这很有用。

2.3 重命名索引

原来rename()也可以用来重命名索引。让我们首先创建一个数据帧:

df = pd.DataFrame(
    { "ID": [1, 2], "City": ['London', 'Oxford']}, 
    index=['A1', 'A2'],
)

我们可以使用字典映射来重命名索引:

df.rename({ **'A1': 'B1'**, **'A2': 'B2'** })

用熊猫 Rename()重命名列(图片由作者提供)

我们还可以传递一个函数,并将axis参数设置为0'index'

df.rename(**str.lower**, **axis=0**)

用熊猫 Rename()重命名列(图片由作者提供)

3.使用带有names参数的read_csv()

我们实际上可以在读取 CSV 文件时重命名列。下面是我们可以覆盖列名的方法:

new_names = ['ID', 'Survived', 'Class', 'Name', 'Sex']df = pd.read_csv(
    'data/titanic/train.csv', 
    **names=new_names,**           # Rename columns
    **header=0,**                  # Drop the existing header row
    usecols=[0,1,2,3,4],       # Read the first 5 columns
)

我们创建了一个列名列表new_names,并将其传递给names参数。CSV 文件已经包含一个标题行,因此需要参数header=0来覆盖列名。

如果你想了解更多关于read_csv()的信息,请查看下面的文章。

4.使用columns.str.replace()方法

有一种叫做columns.str.replace() [1]的方法允许我们用另一个指定的短语替换一个指定的短语,例如,用下划线替换空格。

df = pd.DataFrame(
    { "account id": [1, 2], "uk city": ['London', 'Oxford']}, 
    index=['A1', 'A2'],
)df.columns = df.**columns.str.replace(' ', '_')**

用 columns.str.replace()重命名列(图片由作者提供)

该方法也可用于通过df.index.str.replace()进行索引。

5.通过set_axis()重命名列

最后,我们还可以通过设置轴来更改列名。这种方法很好,但是它没有前两种方法有多少优势。

df.**set_axis**(**['ID', 'Survived', 'Class', 'Name', 'Sex']**, axis=1)

用熊猫 set_axis()重命名列(图片由作者提供)

结论

本文展示了在 Pandas DataFrame 中重命名列的 5 种不同方法。以下是总结:

  • columns属性是最简单的,但是它要求我们提供所有列的名称,即使我们只想重命名其中的一部分。当处理只有几列的数据帧时,这种方法是明智的。
  • 当读取 CSV 文件时,使用带有names参数的read_csv()来重命名列可能更明智。
  • 当您想要重命名一些选定的列时,rename()功能是最佳选择。
  • columns.str.replace()只有在你想替换字符的时候才有用。注意,向rename()传递一个自定义函数也可以做到这一点。
  • 最后,我们还可以通过set_axis()设置轴来更改列名。

我希望这篇文章能帮助你节省学习熊猫的时间。我推荐你去看看那些 API 的熊猫文档,并了解你可以做的其他事情。

感谢阅读。请查看笔记本获取源代码,如果您对机器学习的实用方面感兴趣,请继续关注。

你可能会对我的其他一些熊猫文章感兴趣:

  • 所有熊猫 json_normalize()你应该知道的扁平化 JSON
  • 熊猫系列实用介绍
  • 使用熊猫方法链接提高代码可读性
  • 如何对熊猫数据帧进行自定义排序
  • 为了数据分析你应该知道的所有熊猫移位()
  • 何时使用熊猫变换()函数
  • 你应该知道的熊猫串联()招数
  • Pandas 中应用()和转换()的区别
  • 所有熊猫合并()你应该知道
  • 在熊猫数据帧中处理日期时间
  • 熊猫阅读 _csv()你应该知道的招数
  • 用 Pandas read_csv() 解析日期列应该知道的 4 个技巧

更多教程可以在我的 Github 上找到

参考

  • [1]数据学校:如何重命名熊猫数据框架中的列

在地理空间 WebGL 可视化中渲染 3D 建筑物

原文:https://towardsdatascience.com/render-3d-buildings-in-geospatial-webgl-visualisations-c5325eadb347?source=collection_archive---------10-----------------------

实践教程

使用 2D GeoJSON 的 MapboxGL、MapTalks & Three.js 渲染 3D 建筑模型

作者截图|在 MapboxGL 中渲染的最终输出|展示了新加坡的 HDB 建筑层|在 MapBox WebGL 中部署在 SG HDB 建筑层

辅助项目的灵感

在过去的几个月里,一些国家已经将展示其国内 COVID 热点的地图可视化作为公共服务。一些现成的例子包括雅加达和新加坡:

作者截图|左图截图摘自Peta Sebaran | Covid 19 . go . id(雅加达的 COVID 地图 Viz) |右图截图摘自卫生部(moh.gov.sg)(新加坡的 COVID 地图 Viz)

在寻找一个探索的副业时,我被分配了创建右边地图的任务,这是一个壮举,我在几个小时内就完成了。令我非常惊讶的是,那件作品在我的国家成为了几天的头条新闻,完全超出了我的预期。

随着对地图和地理信息系统(GIS)领域越来越多的关注,我决定将我的下一个副业项目的范围也转移到与 GIS 相关的领域。此后,我开始在网上浏览作品寻找灵感,并在新加坡组屋 3D 建筑开放数据发布会上偶然发现了一个项目|城市分析实验室|新加坡(ual.sg)

作者截图|新加坡住宅公寓的 3D 建筑层记录于新加坡组屋 3D 建筑开放数据发布|城市分析实验室|新加坡(ual.sg)

注意:一定要看看研究团队的上述工作,因为这确实是他们的一个精彩项目😄

由于数据源是开源的,可供公众使用,因此不存在违反保密性的风险,因此我决定开始自己的旅程来重现团队已经完成的工作— 将新加坡的住宅建筑渲染到 3D 地图上,但进行了自己的调整和修改

数据源和数据处理步骤列表

数据来源(1/3): HDB 建筑信息数据集

该数据集中需要注意的关键数据字段是:

作者插图|由于数据集不包含空间信息,因此幸运的是,“街区编号”和“街道”值可用于后续地理编码,以获得其最终坐标。

数据来源(2/3):新加坡地址和邮政编码列表

目的:对每栋住宅楼的点坐标进行地理编码

虽然城市分析实验室的研究团队使用了一个名为 OneMap 的地图服务提供商,但由于我有一个来自完全相同的 API 的最新地址列表,我只需在相关字段之间进行映射,以标记每个建筑物的**【纬度】** & **【经度】**坐标:

作者插图|为每个建筑标注唯一的邮政编码值和[纬度]+[经度]字段,使每个建筑块能够在地图上进行空间绘制

—要了解如何抓取某个地区/城市/国家的地址列表,请参考我下面的文章(带代码实现):

这一步的输出文件可以在我的 GitHub 链接中找到: HDB_Only.json

为了将文件转换成 GeoJSON 格式,我使用了简单的 JavaScript 并导出了以下内容:

然而,由于上面的输出只给提供了建筑块的点坐标,我不得不从别处获取每个建筑的多边形几何图形

回想一下,这个项目的结果是输出每个建筑的 3D 模型。因此,先决条件是获得对应于每个积木块的 2D 多边形形状。

数据来源(3/3):新加坡建筑层来自data.gov.sg

在这一点上,我没有模仿城市分析实验室团队在所做的事情,即使用OpenStreetMap(OSM)社区提供的建筑物足迹,而是从与data.gov.sg的建筑物信息相同的数据源中检索空间数据集。

数据处理步骤【1/2】:从 GeometryCollection 数组中分离多边形几何对象

虽然 building layer 数据集包含所需的所有建筑物覆盖区,但为了轻松地建立每个住宅建筑的 3D 网格,我决定将所有嵌入的多边形几何从其多个 GeometryCollection 数组中提取到各个多边形对象中。

基本原理:将每个建筑处理为 2D 多边形,然后挤压与读取嵌套多边形数组相比,这将节省渲染阶段所需的大量后续数据处理。

在将标识符字段**【ID】&【SUBID】**分配给每个多边形对象后,我成功地导出了以下名为building _ layer . geo JSON(请注意文件大小超过 30MB)

数据处理步骤【2/2】:标注每个多边形到点建筑物

使用 TurfJS 的功能,我继续利用我在**【地理编码器工具】**选项卡下的 Tableau 数据实用程序中部署的 GIS 工具:

作者截图|这是一个 GIS 实用工具和其他 Tableau 实用工具,可从标题菜单中选择,标题菜单是我为自己的一些其他文章创建的|可在:https://tableau-data-utility.onrender.com/访问

本质上,上述工具利用 JavaScript 库 TurfJS 来交叉检查每个点坐标与上传的任何多边形几何重叠。

插图作者| TurfJS 返回一个布尔值,表明一个点坐标是否在多边形几何的边界内

对于那些对此工具感兴趣的人,请随意查看下面的文章,该文章深入探讨了如何基于自定义边界实施 TurfJS 进行地理编码:

结果输出由唯一的多边形标识符**【ID】&【SUBID】**组成,专门标记到每个点坐标。此后,基于这些标识符字段,最终得到的住宅建筑多边形层被输出为:hdb _ building _ polygons . geo JSON(请注意文件大小超过 30MB)

新加坡 HDB 住宅建筑图层的三维渲染

我总共有 2 种方法来模拟新加坡的住宅建筑。在这两个场景中,**【楼层数】**被用作渲染到地图上的建筑物高度的代理。

实现 1) MapBoxGL JS

作者图片| MapBox web GL 上的 3D 模型地图演示|部署在以下链接

这个的源代码可以在我的 GitHub Repo: 链接找到

实现 2)MapTalk JS+three JS

然而,尽管 MapBoxGL JS 可以漂亮地呈现空间文件,但在以下情况下展示 3D 地图仍是一个挑战— (1)互联网访问不存在(2)如果地图服务提供商离线,因为这将禁用几何图形的 3D 功能。为了说明这种情况,我探索了替代方案,并遇到了MapTalk JS+three JS。

ThreeJS + OneMap 底图 **(纯离线设置)**中 3D 建筑模型的最终本地实现如下:

作者图片|使用 ThreeJS 渲染的 3D 建筑物|根据每个多边形的顶点渲染每个建筑物的网格| 灰度一个地图底图使用 mbtiles 离线渲染

结语:关于 MapBoxGL & 三个 j的思考

总之,虽然 MapBoxGL 与 ThreeJS 相比提供了更快的实现形式,但是后者支持更大形式的定制,例如照明&摄像机视角。显然,通过比较和对比我上面的两个实现,在 MapBoxGL 中,建筑物网格上反射的光照看起来比 ThreeJS 中的要模糊一些,因为我没有考虑光照和摄像机角度的计算。

进一步探索:

在 2, ThreeJS 之间,肯定有更大的勘探潜力,因为它迎合了许多其他 3D 建模领域,而不仅仅是地理空间。然而,在许多情况下,由于时间的原因 MapBoxGL 仍然是将 2D 几何图形渲染到 3D 地图上的首选。

非常感谢你坚持到这篇文章的结尾!❤ 希望你喜欢我这边项目的叙述,如果你想了解更多 GIS、数据分析& Web 应用相关内容,请随时 关注我。会非常感激😀TGIF!

https://geek-cc.medium.com/membership

使用 Matplotlib 渲染交互式绘图

原文:https://towardsdatascience.com/render-interactive-plots-with-matplotlib-2cf0918d89c9?source=collection_archive---------3-----------------------

实践教程

支持交互性的 matplotlib 后端研究

作者图片

好的图表有效地传达信息。好的图表可以帮助、通知和改进决策。”—但丁·维塔利亚诺

互动图表受到所有人的喜爱,因为它们可以更有效地讲述一个故事。在数据科学和相关领域也是如此。探索性数据分析是数据预处理管道中的一个重要步骤,生态系统中有很多库可以实现这一点。下图很好地概括了这个想法。

来源:皮兹(尼古拉斯·p·罗杰尔)改编自杰克·范德普拉斯的原图

即使有这么多选择,被亲切地称为 python 可视化包的祖父的 Matplotlib 仍然是许多人的最爱。然而,缺乏交互性仍然是一个瓶颈。因此,人们设计了一些变通方法,通过一些第三方库来实现交互性。但是您知道吗,如果您使用交互式后端,也可以直接使用 matplotlib 创建交互式绘图。本文将只使用 matplotlib 来研究两个这样的后端,以及它们如何在笔记本中呈现交互性。

Matplotlib 后端

Matplotlib 迎合不同的用户,因此支持各种后端。根据文件:

“前端”是面向用户的代码,即绘图代码,而“后端”在幕后做所有的艰苦工作来制作图形。

这意味着交互性的先决条件是拥有一个交互式后端。Jupyter 笔记本的默认后端是内嵌后端,由 %matplotlib inline.启用,它擅长渲染静态图像,但不提供交互式功能,如平移、缩放或自动更新其他单元格中的数字。

相反,有后端,当启用时,呈现交互式图像。本文将介绍两种常见的方法,以便您可以在数据可视化任务中使用它们。

1\. NBA gg 后端

[backend_nbagg](https://matplotlib.org/stable/api/backend_nbagg_api.html#module-matplotlib.backends.backend_nbagg)在笔记本中渲染交互式图形。它利用了为 webagg 后端开发的基础设施。

启用后端

要在 Jupyter 笔记本中启用后端,请键入以下内容

%**matplotlib** notebook

用法

这里有一个简单的例子来展示nbagg后端的用法。

# The code is to be run in a Jupyter Notebook
import matplotlib.pyplot as plt
%matplotlib notebookplt.plot([1,2,3,4,5,6,7], [10, 50, 100, 23,15,28,45], linewidth = 3, c = 'g')

带有 nbagg 后端的交互式 matplotlib 绘图|图片由作者提供

也可以从其他单元格自动更新数字。例如,可以通过下图中后续单元格中执行的代码来更新折线图。

根据作者从 Matploltib | Image 中的其他单元格自动更新图形

使用blue button可以轻松关闭该功能🔵位于右侧角落。点击后,交互会停止,下一个单元格会生成新的地块。就这么简单。

关闭交互性|作者图片

交互性不仅限于 2D 图,还可以在 3D 图中观察到。代码摘自 matplotlib 的官方文档。

# The code is to be run in a Jupyter Notebook or Jupyter Labimport matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
%matplotlib notebook
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')# Grab some test data.
X, Y, Z = axes3d.get_test_data(0.05)# Plot a surface plot.
ax.plot_surface(X, Y, Z, rstride=10, cstride=10, )plt.show()

使用 Matplotlib(nbagg 后端)创建的 3D 交互式绘图|图片由作者提供

不足之处

这个选项工作得很好,可以完成我们的工作,但是它与 Jupyter 实验室不兼容。因此,对于 Jupyter 实验室的用户来说,它没有多大用处。此外,保存选项似乎对我不起作用。有一个更好的替代方案可以达到同样的效果,尽管后端不同。

2.Ipyml 后端——更好的选择

ipyml 后端呈现“经典”笔记本和 Jupyter 实验室中的交互式图形。ipyml 后端使用 ipywidget 框架,需要单独安装。小部件用于在笔记本环境中构建交互式图形用户界面。在滑块、文本框等控件的帮助下。,用户可以与他们的可视化无缝交互。

装置

使用pipconda可以轻松安装 Ipympl。更多细节请参考文档。

pip install ipymplor conda install -c conda-forge ipympl

对于 Jupyter 实验室用户,还需要node jsjupyterLab扩展管理器。为了更好的体验,建议使用 JupyterLab > = 3。

conda install -c conda-forge nodejs
jupyter labextension install [@jupyter](http://twitter.com/jupyter)-widgets/jupyterlab-manager jupyter-matplotlib

启用后端

为了能够使用ipyml后端,需要输入以下内容:

%matplotlib widget

现在我们已经满足了所有的要求,是时候进行演示了。

使用

我们将使用与上一节相同的例子。这也有助于我们比较这两种功能。

# The code is to be run in a Jupyter Notebook or Jupyter Lab
import matplotlib.pyplot as plt
%matplotlib widgetplt.plot([1,2,3,4,5,6,7], [10, 50, 100, 23,15,28,45])

带有 Ipyml 后端的交互式 matplotlib 绘图|图片由作者提供

ipyml 后端也适用于 3D 可视化。

使用 Matplotlib(ipyml 后端)创建的 3D 交互式绘图|图片由作者提供

在这种情况下,控件位于图的右侧,但除此之外,它与上一节中获得的图非常相似。这是真的,但是有一些微妙的区别,如果你已经注意到:

  • 图可以保存为静态图像。

将图保存为静态图像|按作者排序的图像

  • 绘图小工具可以通过用户界面调整大小。

这是一个很棒的特性,我在大多数其他可视化库中都没有见过。

按作者使用 UI |图像调整绘图大小

  • ipyml 后端支持 matplotlib 和所有基于 matplotlib 构建的库(如 Pandas、Geopandas、Seaborn 等)之间的交互。

结论

以下是我们在本文中所涉及内容的总结。我们了解了一些 matplotlib 的后端,以及支持交互性的后端。nbaggipyml看起来都很棒,但是ipyml有更多更好的特性。我相信您会喜欢试验这些后端,并亲自看看交互特性。如果您喜欢这篇文章,我相信您也会喜欢我写的其他几篇直接或间接涉及 matplotlib 的文章。

  • 如果您想使用 matplotlib 库来创建一些引人入胜的动画。
  • 使用 Pandas 绘图语法直接创建 Plotly 和 Bokeh 绘图的教程
  • matplotlib 中的一些高级图,可以让我们的分析更上一层楼。

使用 Python 渲染视频上的响应文本

原文:https://towardsdatascience.com/rendering-responsive-text-on-video-using-python-a3863c7bbb2e?source=collection_archive---------39-----------------------

使用 OpenCV 进行计算机视觉项目实践

BBC 创意在 Unsplash 上拍摄的照片

在这篇文章中,我将向你展示如何使用 Python 渲染视频中的响应文本。我们将在整个项目中使用 OpenCV python 包。这可能听起来有点难以做到,只是 OpenCV,这是一个计算机视觉包,但在这个项目中,你会看到它在视频编辑方面也很好。我做了一个和这个类似的项目,可以在这里找到。在那篇文章中,我展示了如何在视频上添加静态文本。但是这一次,我决定通过添加一个可更新的响应文本来增加一些趣味,而不是使用像 MoviePy 这样的视频编辑库。

我希望你会喜欢这个项目。我们开始吧!

目录

  • 简介
  • 步骤 1 —导入视频
  • 步骤 2 —阅读文本文档
  • 步骤 3 —响应文本功能
  • 最后一步——渲染视频

介绍

正如我前面提到的,我们将在这个项目中只使用 OpenCV 包。而且是超级酷的计算机视觉库。当处理像这样的机器学习或计算机视觉项目时,我们需要首先安装软件包。这样,我们就有权力使用它。我先分享一点 OpenCV 的定义,然后我们安装。

OpenCV (开源计算机视觉库)是一个开源的计算机视觉和机器学习软件库。OpenCV 旨在为计算机视觉应用提供一个公共基础设施,并加速机器感知在商业产品中的应用。作为一个 BSD 许可的产品,OpenCV 使得企业利用和修改代码变得很容易。

来源:https://opencv.org

现在,让我们使用 PIP 安装它。如果你想知道更多关于安装的信息,请随意搜索“如何将 OpenCV 安装到 Windows 或 macOS”。顺便说一下,在您的终端中运行这一行之前,请确保已经安装了 Python。

pip install opencv-python

安装完成后,我们可以进入文本编辑器并开始编程。这个项目我不推荐用 Jupyter 笔记本;由于我们将创建一个在新窗口上运行的视频播放器,这样更容易打开和关闭窗口。否则,程序可能会冻结并且没有响应。

这是我在文本编辑器中的第一行。我正在使用 import 方法导入我们刚刚安装到程序中的 OpenCV 包。

import cv2

完美!现在我们可以进入下一步,我们将选择一个视频并将其导入到程序中。

步骤 1 —导入视频

这将是一个简单的步骤。我们将做两件事:导入一个视频,并找到视频的帧速率。我们将使用帧速率来计算视频中的秒数,以便我们可以计算不同文本的渲染持续时间。

让我们使用视频捕获方法导入视频。如果你想了解更多关于视频捕捉的信息,这里是官方参考页面。

tree_video = cv2.VideoCapture('tree.mov')

现在,让我们来计算导入视频的帧速率。

fps = tree_video.get(cv2.CAP_PROP_FPS)
print(fps)#result
23.97

当我运行这条线时,终端上显示出 23.97。这意味着视频基本上是每秒 24 帧。所以 24 次的捕捉循环相当于 1 秒钟的视频。我将分享更多关于我如何在响应文本函数步骤中进行计算的信息。

步骤 2 —阅读文本文档

在这一步中,我们将从文本文档中读取文本。我们将使用 open 函数,这是 Python 自带的内置函数。这是我将要导入的文本文档的截图。

作者图片

这几行是我写的一首诗,叫做《大树下的https://sonsuzdesign.blog/2019/12/14/under-the-giant-tree/》。

确保每个句子都在新的一行。在阅读文本文档时,我们会将每一行转换成一个列表项。

*poem = open('under_the_giant_tree.txt')
poem_lines = list(poem)print(poem_lines)*

作者图片

步骤 3 —响应文本功能

在这一步,我们将编写一个函数来更新文本。我决定根据不同的时间戳更新文本,但可以随意将其更改为不同的大小写。

*frame_ = 0def text_update(frame_):
 if frames_ < (5*fps):
  text = str(poem_lines[2])
 elif frames_ < (10*fps):
  text = str(poem_lines[4])
 elif frames_ < (15*fps):
  text = str(poem_lines[6])
 elif frames_ < (20*fps):
  text = str(poem_lines[8])
 else:
  text = "no text found"
 return text*

我正在使用 if-else 来了解正在播放的视频的时间戳。开关盒也可以做到这一点。请随意摆弄代码。

我已经定义了一个名为" frame_" 的新变量来传入视频帧。这样,我可以用秒来计算持续时间。我们已经知道了第一步中的每秒帧数,即 24 fps。在此功能中,文本将每五秒更新一次。我将这首诗的诗句赋给文本变量。

现在,让我们进入最后一步。

最后一步——渲染视频

太好了!我们差不多完成了。在这最后一步,我们将结合我们迄今为止所做的一切。我们将使用 while 循环来触发程序。我们可以使用“escape”或“q”键结束循环。

*while(True):ret, frame = tree_video.read()font = cv2.FONT_HERSHEY_SIMPLEXon_video_text = text_update(frame_)

  cv2.putText(frame, on_video_text, (50, 50), font, 1, (0, 255, 255),
    2, cv2.LINE_4)frame_ = frame_ + 1cv2.imshow('poem on video', frame)if cv2.waitKey(1) & 0xFF == ord('q'):
    breaktree_video.release()cv2.destroyAllWindows()*

那么上面的代码中发生了什么呢:

  • 我们从读取导入的视频记录开始。
  • 然后,我们定义想要用于文本的字体。
  • 然后,我们调用 text_update 函数来更新文本。
  • 我们使用 putText 方法在视频上添加我们的响应文本。这里的是官方参考链接,在这里你可以了解更多关于 putText 的信息。
  • 之后,我们正在更新 frame 变量,以便将正确的帧传递给我们的 text_update 函数。
  • 为了在新窗口上显示视频,我们使用了 imshow 方法。
  • 最后,用 if 语句结束 while 循环。当按下“escape”或“q”键时,程序将停止运行。如果没有按任何键,视频结束时窗口将关闭。

下面是我运行程序后播放的视频截图:

作者图片

恭喜你。!我们已经学习了如何使用 Python 在视频上呈现可更新的响应文本。我希望你喜欢这个动手的计算机视觉项目。从事动手编程项目是提高编码技能的最佳方式。如果你今天学到了新东西,我会很骄傲。

如果您在执行代码时有任何问题,请随时联系我。我尽力在两周内回来。

我们来连线。查看我的博客和 youtube 以获得灵感。谢谢你,

更多计算机视觉和机器学习相关的项目

* *

将 Avro 消息复制到目标

原文:https://towardsdatascience.com/replicate-avro-messages-to-target-part2-conflicting-schema-exists-on-target-same-subject-4aafb3bad3d?source=collection_archive---------37-----------------------

第 2 部分(目标上存在冲突模式,相同主题)

当 Replicator 无法将消息插入目标主题,因为主题的主题版本与模式冲突时,该怎么办?

查尔斯·福尔舍尔在 Unsplash 上的照片

这是一篇基于 this 的后续文章,其中我们讨论了当 replicator 试图将带有 Avro 消息的主题复制到目标时会发生什么,但是目标模式注册中心已经具有驻留在不同主题中的相同模式 ID(它被嵌入到消息中),并且该模式对象与它在源上的完全不同。在本文中,我们将看到当同名主题已经存在于目标模式注册中心,而该注册中心的版本与模式冲突时,Confluent Replicator 将如何表现。

在阅读本文之前,读者需要了解代理、连接、模式注册中心和合流复制器。

下面是我们在源和目标上的环境。

作者图片

实际场景:

假设您想要复制“bar”主题,该主题包含模式 id 为“1”的 Avro 消息。在目标端,您有一个单独的 schema-registry 实例,该实例已经注册了版本 1 的 subject,即“bar-value”(主题名称策略),并且该版本与源 schema registry 上的版本冲突。

下面是重现此问题的步骤,其中汇合复制程序无法将消息插入目标主题。稍后,我将讨论我们可以实现什么变通办法来补救这一点。

  1. 在源卡夫卡上产生消息

使用 Avro 控制台生成器在源 Kafka 集群的“bar”主题中生成 Avro 消息。

/usr/bin/kafka-avro-console-producer \
--broker-list broker-1:9092 --topic bar \
--property schema.registry.url=[http://schema-registry-1:8081](/${SCHEMA_REGISTRY}:8081) \
--property value.schema='{"type":"record","name":"myrecord","fields":[{"name":"f1","type":"string"},{"name":"f2","type":"string"}]}'{"f1":"string1","f2":"string2"}
{"f1":"string1","f2":"string2"}
{"f1":"string1","f2":"string2"}
^C

记下我们发送的模式,它由字符串数据类型的“f1”和“f2”字段组成。我启用了 auto schema registered,这意味着如果 schema registry 中没有已经存在的“bar-value”主题,它将首先创建主题(使用主题名 strategy ),然后创建它的第一个版本。还有其他可用的主题命名策略,如记录名策略和主题记录名策略,但为了简单起见,我使用主题名策略。如果您有兴趣了解更多信息,请参考本文档。

让我们检查上面在源环境的模式注册表中创建的主题。

curl -s -X GET [http://schema-registry-1:8081/subjects/bar-value/versions](http://${SCHEMA_REGISTRY}:8081/subjects/bar-value/versions)
[1]curl -s -X GET [http://schema-registry-1:8081/subjects/bar-value/versions/1](http://${SCHEMA_REGISTRY}:8081/subjects/bar-value/versions/1)
{"subject":"bar-value","version":1,"id":1,"schema":"{\"type\":\"record\",\"name\":\"myrecord\",\"fields\":[{\"name\":\"f1\",\"type\":\"string\"},{\"name\":\"f2\",\"type\":\"string\"}]}"}

2。检查目标模式注册表上的模式

正如在上面的场景中所讨论的,主题的主题已经存在于目标模式注册中心,并且有一个版本是冲突的模式。

curl -s -X GET [http://schema-registry-2:8081/subjects/bar-value/versions](http://${SCHEMA_REGISTRY}:8081/subjects/bar-value/versions)
[1]curl -s -X GET [http://schema-registry-2:8081/subjects/bar-value/versions/1](http://${SCHEMA_REGISTRY}:8081/subjects/bar-value/versions/1)
{"subject":"bar-value","version":1,"id":1,"schema":"{\"type\":\"record\",\"name\":\"firstrecord\",\"fields\":[{\"name\":\"age\",\"type\":\"int\"},{\"name\":\"phone\",\"type\":\"int\"},{\"name\":\"housenumber\",\"type\":\"int\"},{\"name\":\"distance\",\"type\":\"int\"}]}"}

“bar-value”主题下的这个模式有四个字段,它们的类型都是“int”。这使得与我们在源模式注册表中的完全不同。该模式使用 4 个全“int”数据类型的字段,而不是源模式上的 2 个“string”数据类型的字段。这使得两种模式彼此不兼容,当 replicator 试图序列化消息并且无法在目标上插入/注册模式时,我们很快就会看到这一点。查看这个文档,了解 schema registry 提供的兼容性设置。

3。在目标上运行复制器

将 replicator 作为连接器部署在目标环境的连接实例上。我将使用以下配置。

如果现在检查连接器的状态,我们将看到其失败,并出现错误“io . confluent . Kafka . Schema registry . client . rest . exceptions . restclientexception:正在注册的模式与早期的模式不兼容;错误代码:409 "

下图对此进行了描述

图片由作者提供。复制器尝试在“bar-value”主题中插入新版本,但由于与版本 1 架构不兼容而失败

通过我们在属性“value.converter”中使用的“AvroConverter”类,replicator 在插入时尝试序列化数据,并且还尝试将模式注册为“bar-value”主题下的新版本,因为该模式尚不存在。schema registry 将拒绝在 subject 下注入这个新版本的模式,因为它针对 subject 的“最新”版本(即我们前面看到的“int”数据类型)所发布的内容运行模式兼容性检查,因为这个新发布的模式不兼容(如上所述),因此 replicator 将报错上述错误。

有几个变通办法可以帮助解决这个问题。我会涵盖一些

变通办法:

在 replicator 配置中使用“topic . rename . format”:使用 replicator 连接器配置中的这个附加属性,您将在目标模式注册表中获得一个新的 subject,replicator 将能够在其下成功创建它的第一个版本。让我们检查一下这个

如果现有连接器仍然存在,请将其删除

curl -s -X DELETE [http://connect-2:8083/connectors/bar-avro-replicator](http://${CONNECT}:8083/connectors/bar-avro-replicator)

使用“topic.rename.format”重新部署连接器

状态应显示“正在运行”

curl -s -X GET [http://connect-2:8083/connectors](http://${CONNECT}:8083/connectors)   
{
    "connector": {
        "state": "RUNNING",
        "worker_id": "x.x.x.x:8083"
    },
    "name": "bar-avro-replicator",
    "tasks": [
        {
            "id": 0,
            "state": "RUNNING",
            "worker_id": "x.x.x.x:8083"
        }
    ],
    "type": "source"
}

让我们检查复制器是否创建了一个带有“-repica”后缀的新主题

curl -s -X GET [http://schema-registry-2:8081/subjects/](http://${SCHEMA_REGISTRY}:8081/subjects/)
["bar-value","**bar-replica-value**"]

是的,“条形图-副本-价值”是根据主题名称策略创建的。现在让我们检查这个主题下的版本

curl -s -X GET [http://schema-registry-2:8081/subjects/bar-replica-value/versions](http://${SCHEMA_REGISTRY}:8081/subjects/bar-replica-value/versions)
[1]curl -s -X GET [http://](http://${SCHEMA_REGISTRY}:8081/subjects/bar-replica-value/versions/1)[schema-registry-2](http://${SCHEMA_REGISTRY}:8081/subjects/bar-replica-value/versions)[:8081/subjects/bar-replica-value/versions/1](http://${SCHEMA_REGISTRY}:8081/subjects/bar-replica-value/versions/1)
{"subject":"bar-replica-value","version":1,"id":**2**,"schema":"{\"type\":\"record\",\"name\":\"myrecord\",\"fields\":[{\"name\":\"f1\",\"type\":\"string\"},{\"name\":\"f2\",\"type\":\"string\"}],\"connect.version\":1,\"connect.name\":\"myrecord\"}"}

现在,replicator 已经成功创建了此模式,请注意此版本引用的模式 ID,即“ 2 ”。现在是时候从目标卡夫卡的“酒吧复制品”主题中消费我们的信息了。

/usr/bin/kafka-avro-console-consumer \
--property print.schema.ids=true \
--property schema.id.separator=: \
--property schema.registry.url=[http://schema-registry-2:8081](/${SCHEMA_REGISTRY}:8081) \
--bootstrap-server broker-2:9092 --topic bar-replica --from-beginning
{"f1":"string1","f2":"string2"}:**2**
{"f1":"string1","f2":"string2"}:**2**
{"f1":"string1","f2":"string2"}:**2**

消费者还运行并通过查找模式 ID“2”成功地反序列化了消息。如果你需要更多关于为什么我们看到的模式 ID 是“2”而不是“1”的信息,那么看看这篇文章有更多的细节。

****删除目标模式注册表上的版本:一个简单的选项(但不总是一个选项)是删除目标模式注册表上“bar-value”主题的版本“1”(这是冲突的)。

**curl -s -X DELETE [http://schema-registry-2:8081/subjects/bar-value/versions/1](http://${SCHEMA_REGISTRY}:8081/subjects/bar-value/versions/1)     
1**

为“bar-value”subject设置“NONE”兼容性设置:同样,这也很容易做到,只需将“bar-value”subject 的兼容性设置更改或更新为 NONE,复制器应该能够插入新的模式并继续复制消息。

获取当前配置设置

**curl -s -X GET [http://schema-registry-2:8081/config/bar-value](http://${SCHEMA_REGISTRY}:8081/config/bar-replica-value)
{"error_code":40401,"message":"Subject not found."}**

不要被这个误导性的错误所迷惑。当不存在主题级兼容性配置时,它将显示此错误。

更新主题兼容性设置。

**curl -s -X PUT -H "Content-Type: application/json" [http://schema-registry-2:8081/config/bar-value](http://${SCHEMA_REGISTRY}:8081/config/bar-replica-value) --data '{"compatibility": "NONE"}'
{"compatibility":"NONE"}**

如果您现在尝试执行 GET,您应该能够看到当前适用的兼容性设置。

**curl -s -X GET [http://schema-registry-2:8081/config/bar-value](http://${SCHEMA_REGISTRY}:8081/config/bar-replica-value)
{"compatibilityLevel":"NONE"}**

有了这个,复制者应该可以继续了。

请注意,设置无兼容性设置首先会破坏数据治理的目的。因此,我们在设置“NONE”时应该非常小心,因为这实际上禁用了 schema registry 为我们进行的兼容性检查,并且在生产环境中使用是不安全的。

结论:

通过使用 replicator 连接器的“topic.rename.format”属性,将会自动创建一个新的主题,并注入 replicator 从源读取的新模式(在反序列化时)。在我看来,这是最安全的选择。

**** ****

使用 dbt 种子将 csv 文件复制到您的数据仓库

原文:https://towardsdatascience.com/replicating-csv-files-to-your-data-warehouse-with-dbt-seeds-74bab7f0dca8?source=collection_archive---------19-----------------------

管理序列化 csv 文件的简单而优雅的方式

照片由 Kian Lem 在 Unsplash 上拍摄

在 HousingAnywhere ,我们使用 dbt 进行数据建模,以创建简洁的视图和物化,作为交易数据表和分析师&数据科学家之间的一层,他们将这些数据用于他们的应用。dbt 不仅仅是一个创建模型的好工具,它还提供了一个非常方便的方法将 csv 文件从 GitHub 复制到您的数据仓库。以 GitHub 作为起点有很多好处;序列化/版本控制、同行评审和低/无准入门槛(核心功能是免费的,许多组织已经在使用 GitHub)。我们一直在使用 dbt 种子特性,它提供了这种功能,在过去非常成功地存储了很少改变并且依赖于手动调整的业务逻辑。

什么是业务逻辑,为什么应该将它存储在数据仓库中

几乎所有商业智能(BI)系统中都实现了业务流程和业务规则。因为这两者经常会发生变化,所以它们应该以一种易于维护的方式在软件系统中定义。在软件工程、数据科学和商业智能等领域,维护这样的变更通常通过版本控制系统(VCS)来实现。目前,版本控制系统由分布式版本控制工具主导,如 Git——结果是 Git 存储库即服务(GitHub、GitLab、Bitbucket)。

将业务规则存储在这样的服务中非常有益,因为它为您提供了将所有内部业务定义存储在一个集中存储库中的机会。它促进了简单的可维护性,因为如果修改这些规则,只有一个地方需要调整。此外,通过使用 Git 的版本控制和同行评审特性,它提供了强大的透明性和可问责性。

dbt 和种子

dbt(数据构建工具)是一个命令行工具,使数据分析师和工程师能够更有效地转换他们仓库中的数据。dbt 的主要用例是 ELT 中的 T。它不提取或加载数据,但它非常擅长转换已经加载到您的仓库中的数据。这种“加载后转换”架构被称为 ELT(提取、加载、转换)。

来源:【https://blog.getdbt.com/what-到底-是-dbt-/

可以非常容易地建立 dbt 部署和 git 存储库之间的连接。启用这个连接后,dbt 将总是知道存储库中模型的当前状态。它还提供了其他很棒的特性,比如在 GitHub 上打开拉请求时自动构建临时 CI 模式的触发器以及种子特性。

种子是 dbt 项目(通常在数据目录中)中的 CSV 文件,dbt 可以使用 dbt seed 命令将这些文件加载到数据仓库中。因为这些 CSV 文件位于您的 git 存储库中,所以它们是受版本控制和代码可审查的。种子最适合不经常改变的静态数据。

dbt 种子的良好使用案例包括:

  • 将城市或邮政编码映射到地区(例如,报告负责不同地区的销售团队)
  • 用于报告实际 KPI 与目标 KPI 的公司 KPI 目标
  • 财务分析的总帐定义(边际贡献、CAC、CPA)
  • 为其他 dbt 模型定义配置(例如,通过 dbt 进行数据匿名)
  • 员工帐户 id 列表

工作流程

  1. 创建 dbt 项目

重要的事情先来。您需要创建一个 dbt 项目并建立到您的存储库和您想要加载数据的目的地的连接。

2.在本地创建或编辑种子文件(默认情况下在 data 文件夹中或在 dbt_project.yml 中指定),并在 GitHub 上提出一个 Pull 请求

通过 GitHub 提高公关

3.同行评审更改和合并请求

审核和批准变更

4.在您的项目上运行 dbt seed (或者更好:设置一个作业来按计划运行 dbt seed

运行“dbt 种子”

如前所述,dbt 最终是一个命令行工具。通过运行这 3 个简单的命令,整个工作负载被执行。

dbt seed —完全刷新—dbt seed 命令将位于 dbt 项目的数据路径目录(默认情况下,可以在配置文件中更改)中的 csv 文件加载到数据仓库中。默认情况下,dbt 截断现有的表并重新插入数据。如果列被重命名,或者新列被添加到 csv 文件中,这些语句将会失败,因为表结构已经改变。 —完全刷新标志将强制 dbt 在重建表之前删除它。

dbt_project.yml

dbt run — 在大多数情况下,种子将被用作创建下游模型的基础。 dbt run 针对当前目标数据库执行编译后的 sql 模型文件。dbt 连接到目标数据库,并使用指定的具体化策略运行具体化所有数据模型所需的相关 SQL

dbt 测试— 确保数据质量并检测不符合质量要求的情况是每个可靠应用的重要组成部分。 dbt 测试将根据生产数据运行项目中定义的所有测试,并通知您每个失败的测试。

用 Python 复制《我的世界》世界一代

原文:https://towardsdatascience.com/replicating-minecraft-world-generation-in-python-1b491bc9b9a4?source=collection_archive---------0-----------------------

使用 Voronoi 图和大量柏林/单纯形噪声

图片作者。

《我的世界》,有史以来最畅销的游戏,以其像素化的积木和无限世界而闻名,有着惊人的程序生成的地形,有洞穴,水域,甚至不同的生物群落。

过程生成是计算机图形学的重要组成部分。它主要用于视频游戏或电影中。它有助于生成没有“机器般”感觉的随机结构。

同样,过程化生成在机器学习中起着重要的作用。它可以帮助生成难以收集的数据。训练机器学习模型需要庞大的数据集,收集和处理这些数据集可能很困难,成本也很高。按程序生成数据可以很容易地适应所需数据的确切类型。

我小时候经常玩《我的世界》,我总是想知道它是如何产生永无止境的世界的。在本文中,我将尝试用 Python 来复制这一点。

定义和限制

我们首先要定义我们的世界将如何产生。

  • 世界是三维的、离散的(由单位大小的块组成),在 z 轴上由0255界定,在 x 轴和 y 轴上没有界限。
  • 世界包含生物群落,每一个都跨越大的水平区域,定义了生物群落所占据的空间的性质。
  • 世界包含河流、湖泊和海洋。

每个世界都是由一粒种子定义的。同样的种子将永远产生同样的世界。

生成世界

一大块。图片作者。

为了使生成过程简单,我们将把我们的世界分成块。每个块将占用 1024×1024×256 个块的空间。

每个块都是单独生成的。这将帮助我们保存、加载世界,并轻松生成世界的更多部分。

生物群落边界

我们要做的第一件事是将我们的世界分成 x 轴和 y 轴上的细胞,每一个细胞都是特定的生物群落。我们将为每个细胞指定一个代表其中心的点。

Voronoi 图

给定一组点,Voronoi 图将帮助我们把我们的世界分成单元。Voronoi 图背后的主要思想是一个点所属的单元是其中心最近的单元。

移动的点用离它最近的点的颜色来着色。图片作者。

我们可以对 xy 平面中的每一个点都这样做,以获得这 3 个点的 Voronoi 图。

3 点的 Voronoi 图,动画。作者图片

虽然这种方法很有效,但是它非常慢,尤其是当点数很多的时候。

20 点的 Voronoi 图,动画。图片作者。

在 Python 中,scipy.spatial有一个名为Voronoi的类,可以更高效地计算 Voronoi 图,并为我们提供更多关于图的信息。

作者使用 scipy.spatial. Image 计算的 Voronoi 图。

scipy.spatialVoronoi返回一个顶点、区域和脊的列表,这在以后会很有用。

Voronoi 图中的区域和脊。图片作者。

这些额外的点有助于形成所谓的德劳内镶嵌

Voronoi 镶嵌上的 Delaunay 镶嵌。图片作者。

劳埃德松弛算法

现在,我们必须生成细胞所在的随机点。

如果我们使用来自numpy.random的类似random的函数来生成多个点并计算其 Voronoi 图,我们会得到以下结果:

随机点的 Voronoi 图。图片作者。

你可能已经注意到有些点彼此靠得太近了。这就是所谓的集群。细胞应该是均匀分布的。

当我们缩小(或增加点数)时,这一点更加明显:

请注意一些点是如何聚集在一起的,而其他区域是空的。图片作者。

为了解决这个问题,我们必须把这些点分开。

解决这个问题的一种方法是使用劳埃德松弛算法,该算法利用了点的 Voronoi 图。

劳埃德算法背后的思想是计算我们点的 Voronoi 图,然后将每个点移动到其单元的质心。并且重复该过程一定次数

多边形的质心是其顶点的平均值。

这是一个 Voronoi 图,单元格点用蓝色表示,单元格质心用红色表示。

带有单元点(蓝色)和单元质心(红色)的 Voronoi 图。图片作者。

然后,我们可以一遍又一遍地用细胞质心(红色)替换细胞点(蓝色)。

劳埃德松弛算法动画。图片作者。

这会产生更好看的随机点。

柏林/单工噪声:我们为什么需要它?

为了生成随机地形,我们必须生成随空间随机变化的属性,如海拔、温度或降雨量。

人们可以想到使用random,这是有道理的。

我们将为我们世界中的 xy 平面上的每个区块生成一个 0 到 255 之间的随机数。

这会产生以下结果:

这太随意了。图片作者。

嗯,这看起来更像一个二维码,而不是《我的世界》世界。

问题是我们的随机值没有连贯的结构。每个值都是单独生成的,与相邻值无关。

为了克服这个问题,我们将使用柏林噪声。

柏林噪音。图片作者。

柏林噪音是由肯·柏林在 1983 年发明的。不像普通的随机噪音,它是有结构的。它看起来更接近于自然界中发现的随机模式(云、森林分布)。

单工噪声也是由肯·佩林自己创造的。它比柏林噪声有许多优点。柏林噪声和单纯形噪声今天被用于几乎所有的程序生成。

我们将使用 Python 中名为noise的单工噪声实现。(Python 模块)。

我们有 4 个变量可以玩:音阶八度音阶持续性空隙度。我不会解释每一个是做什么的,但是我会给你留下我制作的这些 gif,让你自己感受一下。

参数变化时的柏林噪声。图片作者。

返回的噪声值介于-1 和 1 之间。

细胞的规则性——模糊边界

虽然我们上面为细胞生成的点间隔得很好,但我们的细胞看起来几乎像正多边形。

为了克服这个问题,我们将使用柏林噪声。对于每个点,我们将在其邻域中选择一个随机点,并将新选择的点的单元分配给当前点。

为此,我们需要两张噪波图,一张用于 x 轴的位移,另一张用于 y 轴。

我们可以通过将噪声(值在-1 和 1 之间)乘以一个恒定长度来控制边界的噪声。

边界噪声长度动画。图片作者。

边界噪声八度动画。图片作者。

选择生物群落

《我的世界》有 60 多个不同的生物群落。每个都有不同的属性。既然我们已经把我们的世界分成了细胞,我们必须给每个细胞分配一个生物群落。为此,我们将使用柏林噪声。

温度-降水图

我们将根据两个参数定义生物群落:温度降水,使用温度-降水图。在环境生物学中,生物群落通常是这样定义的。

纳瓦拉的《气候对陆地生物群落的影响》属于公共领域,CC0

我们将以此图为灵感,设计我们自己的温度-降水量图。

温度-降水图。图片作者。

温度和降水图

现在,我们将使用柏林噪声为每个像元分配一个温度和一个降水量值。我们将生成两个图,每个图包含块中所有块的噪声值。

温度和降水地图。图片作者。

直方图均衡

如果我们使用上面的温度和降水图,我们会遇到一个问题。基于柏林噪声的值不一致。接近 0 的值比接近 -11 的值多。这区分了温度-降水图边缘的生物群落。

为了进一步理解这种不均匀性,我绘制了温度和降水图的 1D 直方图和令人惊叹的 2D 直方图。

温度和降水图的 1D 直方图(左)和 2D 直方图(右)。图片作者。

如你所见,边缘的值受到歧视。为了解决这个问题,我们将使我们的价值观平等。

直方图均衡用于调整图像的曝光。为此我用了skimageexposure函数。

均衡的直方图是平坦的。

温度和降水图的均衡 1D 直方图(左)和均衡 2D 直方图(右)。图片作者。

由于我们分别均衡了温度和降水量,2D 直方图并不完全平坦。

我们可能不希望直方图完全平坦。如果我们想对直方图的平坦度有一些控制,我们可以混合不均衡的值和均衡的值。

直方图均衡化动画。图片作者。

现在,我们可以控制我们的价值观有多平等。

平均单元格

我们将对每个像元内的地图进行平均,以获得每个像元的温度和降水量值。

细胞温度和降水图。图片作者。

现在,每个单元格都有一个温度和一个降水值,在 -11 之间。

量化

为了简化我们处理温度和降水值的方式,我们将把它们转换成整数。我们将使用np.uint8作为数据类型来存储这些值。

为了转换上面映射中的值,我们将它们映射到[0, 255] ,并将值四舍五入为最接近的整数。

量子化不会改变温度和降水的样子。

现在,我们可以通过一张 256×256 的图像来定义我们的温度-降水量图。

温度-降水图。图片作者。

生物群落图

我们可以使用温度-降水图、温度图和降水图为每个细胞指定一个生物群落。对每个单元格都这样做,会产生以下结果:

彩色生物群落地图。图片作者。

高度图

我们二维世界中的每一个点都有一个高度。为了生成高度图,我们将使用噪波图。

高度遮罩。图片作者。

使用这个高度图(值在-1 和 1 之间),我们可以创建一个空白的遮罩。大于 0 的值是陆地,小于 0 的值是海洋。

陆地面具。图片作者。

结合之前生成的图像:

应用了土地遮罩的生物群落地图。图片作者。

为了可视化的高度,我们将添加一些阴影到地图上。

带土地掩膜的生物群落图(左),带土地掩膜的阴影生物群落图(右)。图片作者。

到目前为止,结果看起来很有希望。但是高度与生物群落无关。我们需要改变每个生物群落的高度图。我们将通过在高度图上应用一个函数来实现这一点。

高度图细节

我们将使用两个不同细节层次的高度图。这是通过改变柏林噪声中八度音阶的数量来实现的。

这是我们的两张身高图:

清晰(左)和平滑(右)高度图。图片作者。

高度贴图过滤器

我们将使用陆地中的高度图(值在 0 和 1 之间)。每个生物群落都将使用两种高度图的组合(平滑和清晰的高度图)。然后对其应用过滤器(函数)。

应用滤镜的想法将受到 Photoshop 曲线的启发。我们将使用三次贝塞尔曲线来定义一个我们将应用于高度图的函数。

以下是一些过滤器示例:

我们将为每个生物群落创建并调整过滤器。

沙漠,稀树草原和热带林地过滤器。图片作者。

苔原、季节性森林和雨林过滤器。图片作者。

温带森林、温带雨林和北方过滤器。图片作者。

为了将这些滤镜应用到我们的高度贴图中,我们将使用遮罩。蒙版是一张地图,在某个生物群落区包含 1,在其他区包含 0。

生物群落面具。图片作者。

如果我们使用硬地图,我们将有巨大的高度变化。这就是为什么我们在使用口罩之前会将它们羽化。我们还将从这张地图中移除海洋(通过乘以陆地遮罩),因此我们只对陆地应用滤镜。

羽化(模糊)的生物群落遮罩(左),只有陆地(右)。图片作者。

我们将使用上面的遮罩在高度图上应用每个生物群落的过滤器。我们得到以下结果:

最终的 3D 高度图结果

我们可以使用 Blender 来渲染这些 3D 地图。我们将在 Blender 的置换修改器中使用高度贴图。

彩色生物群落的高度图渲染。用搅拌机做的。图片作者。

彩色生物群落的高度图渲染。用搅拌机做的。图片作者。

彩色生物群落的高度图渲染。用搅拌机做的。图片作者。

河流和湖泊

边界

我们将在生物群落的边界之间增加河流。首先,我们需要计算生物群落之间的界限。

为此,我们将遍历地图上的每个点。如果该点的所有邻居都属于同一生物群落,则该点不在边界上。如果它的邻居有一种以上的生物群落,那么它就是边界的一部分。

边框像素的插图。图片作者。

边界的示例。图片作者。

将这种技术应用于我们的生物群落图会产生以下结果。

生物群落地图(左)和河流(右)。图片作者。

我们可以通过改变包含邻居的盒子的大小来控制河流的大小。

大小不一的河流。图片作者。

我们将使用 2 种不同的河流:生物群落河和细胞河。生物群落河比较大,放在生物群落的边界,而细胞河比较小,放在细胞的边界。然后,我们使用陆地遮罩将河流限制为陆地。

河流也将被限制在中低海拔。

河流。图片作者。

我们将使用这个河流遮罩来更改高度图。我们将模糊这个河流蒙版,然后用原来的河流蒙版来蒙版。这将创建一个在河流中间具有高值的地图,这些高值在河流的边缘慢慢淡化为低值。

这里是河流面具与模糊和蒙面河流面具的比较。

河流面具。图片作者。

河流面具的横截面。图片作者。

我们将使用此地图在高度图中“雕刻”河流。

生物群落图(左)和带有河流的生物群落图(右)。图片作者。

树木和植被

为了给我们的地图添加树,我们将使用前面讨论过的劳埃德松弛算法。这种采样方法有助于我们生成间隔开的随机点。

随机采样点(左),放松点(右)。图片作者。

我们将根据生物群落生成不同密度的树木。

不同等级的树木密度。作者图片,

我们将把一组树木与之前讨论过的生物群落遮罩和土地遮罩结合起来,用树木填充生物群落。每个生物群落都有不同的密度,当然也有不同种类的树木。

有树的生物群落地图。

我的混合技能限制了我用树来可视化 3D 地图。

源代码

这里有一个 Jupyter 笔记本 包含了文章中所有步骤的代码。

**警告:**代码很乱,没有文档记录。

结论

程序生成是计算机图形学中非常强大的一部分。它让生成的内容看起来很随意,但却很有艺术感和结构感。如前所述,这可以用于机器学习,以生成覆盖使用正常真实世界数据收集难以覆盖或成本高昂的区域的数据集。

这篇文章只是一个有趣的项目,我想做一年多。一路走来,我学到了很多概念,也获得了很多乐趣。还欠缺很多。例如,我需要创建地下洞穴、村庄,并创建一个可以无缝组合组块的算法。

灵感

在写我的文章时,我受到了许多文章的启发。如果你喜欢这篇文章,那么你肯定也想看看这些:

  • 流浪者——地图生成| pvigier 的博客
  • 为游戏生成多边形地图(stanford.edu)
  • 【程序世界:2016 年 7 月(procworld.blogspot.com)T2

你也可以看看我之前的一篇关于用 Python 进行 交通模拟的文章,这篇文章讲述了如何程序化地生成交通数据。

复制 Python 的熊猫库

原文:https://towardsdatascience.com/replicating-pythons-pandas-library-4c959f8bbbe3?source=collection_archive---------24-----------------------

面向对象编程和熊猫如何在幕后工作

语境

我已经和 Python——主要是熊猫——一起工作了好几年,我喜欢它;它使用起来非常简单,并且有足够的方法来为任何需求进行非常复杂的数据建模。

然而,如果我所做的大部分都是read_csv, filtering, and groupby的话,我声称了解 Python 可能有点原始。因此,为了在更有经验的 Python 程序员面前不显得无能,我决定稍微挖掘一下熊猫源代码来找出事情是如何在引擎盖下工作的,就像深入了解本质的那种,而不是成为其各种应用程序的盲目用户。我想带着几个目标离开这个练习:(1)对 Python 中的 OOP 有一些实践经验,(2)学习 Python 中的包是如何工作的,以及(3)理解 Pandas 是如何使用它的时髦功能的。

在这篇文章中,我将详细介绍我的一些发现,以及通过一个简单的代码来内化所学到的知识,在这里,我试图重新创建 pandas 的一个核心组件的更简单的版本:它的 DataFrame 类。如果你是 Python 的新手,或者只是一个好奇的读者,那么这篇文章就是为你准备的!

先不说这个,让我们把这篇文章分成几个部分:

  • 先决条件
  • 设置我们的熊猫复制品工作空间
  • 创建我们的 DataFrame 对象
  • 将数据读入我们的数据框架
  • 实现一些数据框架方法
  • 在笔记本电脑环境中使用我们的数据框架

链接到熊猫 _ 副本源代码

先决条件

在我做这个练习的时候,我发现对一些核心 Python 概念有一个坚实的理解对于阅读源代码是非常必要的。下面我列出了几个真正有帮助的地方——我不会详细介绍它们,但我会附上有用的文档和教程链接,以帮助那些不熟悉这些概念的人。

理解 Python 中的面向对象编程(OOP)

对于任何想了解 Python 中 OOP 的人来说,有大量的资源可供参考,其中我特别喜欢的一个资源是科里·斯查费的这个演练。他非常详细地描述了类的工作方式,为什么/如何使用它,什么是属性和方法,以及更高级的主题,如 magic/dunder 方法和 decorators。因为这篇文章将依赖于对 Python 中的类的一些理解,所以请尝试一下这个演练吧!

Python 中的包

因为我们将使用 pandas(它最终是一个包),所以理解包是如何构造的(特别是常规包)是有帮助的。当谈到与 Python 中的包相关的概念时, importlib 文档和包文档解释得相当直截了当。

数据帧的定义

在我们开始复制任何东西之前,理解数据帧到底是什么是很重要的。来自熊猫文档:

二维、大小可变、潜在异构的表格数据。

数据结构还包含带标签的轴(行和列)。

让我们快速浏览一下上述定义的含义:

  • 二维:你可以把它想象成一个 excel 电子表格。
  • Size-mutable: 您可以增加/减少行数或列数。
  • 潜在的异构表格数据:列可能有不同的类型(int、str 等)。
  • 带标签的轴:你可以给你的行加标签,给你的列命名。

设置我们的熊猫复制品工作空间

在做任何事情之前,我给自己设置了一个快速挑战:找到 DataFrame 模块在 Pandas Github repo 上的位置。快速提示—检查 init 文件以找到 DataFrame 模块的引用。

眯着眼看了几分钟后,我终于在熊猫>内核> frame.py 里找到了它。根据我们可以在回购上找到的目录树,我们可以制作一个相当简单的工作区,如下所示:

pandas_replica
|
|- core
|    |-__init__.py
|    |- frame.py
|
|- __init__.py
|- test_notebook.ipynb

让我们创建这些文件,并让它们暂时为空,我们将在以后填充它们。快速浏览一下我们拥有的文件:

  • init。顾名思义,py 初始化“pandas_replica”主包和“core”子包。这也复制了熊猫的常规包结构。导入 pandas_replica.core 将隐式执行pandas_replica/__init__.pypandas_replica/core/__init__.py
  • py 本质上是我们的 DataFrame 模块,类似于我们在 pandas repo 中看到的。
  • ipynb 是一个加载了 autoreload 扩展的笔记本环境,供我们试验和测试我们的熊猫复制品。

创建我们的 DataFrame 对象

首先,让我们创建并测试我们自己的 DataFrame 对象。

英寸/core/frame.py:

class DataFrame:
    def __init__(self):
        print('I am a pandas-replica DataFrame!!')

有用资源:什么是 init?什么是“自我”?

英寸/init。py:

from pandas_replica.core.frame import DataFrame

打开 jupyter 笔记本,运行以下程序:

import pandas_replica as pdrpdr.DataFrame()**Expected Output:**
I am a pandas-replica dataframe!!
<pandas_replica.core.frame.DataFrame at xxxxxxxxxxx>

太好了!如果您获得了预期的输出,那么恭喜您,我们已经创建了自己的数据框架(虽然在功能性方面有所欠缺,但我们会实现的)。I am a pandas-replica dataframe!!成功打印的事实表明我们的类正在以正确的方式初始化,现在我们准备用更多的属性和方法来充实它。

将数据读入我们的数据框架

数据帧的主要思想是存储数据。直观地说,这意味着当我们初始化数据帧时,我们应该将传递给它的数据赋值为属性。为了更进一步,还应该有适当的检查来确保我们传递到我们的数据框架中的数据类型满足特定的标准。

从字典中读取数据

熊猫文档中列出的构建数据帧的方法之一是通过字典。为了简单起见,我们将使用字典的方式,注意将这篇文章中的思想外推至其他输入类型(pd。Series,np.array)是非常可行的。

对输入类型建立检查

当我们在 pandas 的数据框架上修改输入类型时,会出现两个明显的错误。

错误 1: ValueError: DataFrame constructor not properly called!

尝试运行下面的代码,您将得到错误 1,因为输入类型是 int 而不是 dict(或者是 pandas 支持的类型)。

import pandas as pd pd.DataFrame(100000)

错误 2: ValueError: arrays must all be same length

在下面的代码中,col1 和 col2 的长度不同,因此抛出了上面的错误。注意:我们应该把数组改成 pd 吗?不会引发错误,但这只是不同输入类型的单独情况。

import pandas as pdcol1 = [3.6, 3.5, 5.2, 6.1] # len = 4
col2 = [45, 40, 30] # len = 3
data = {'col1': col1, 'col2': col2}
pd.DataFrame(data)

复制错误 1 相当简单。pandas 的源代码中穿插了内置的isinstance,如果输入符合它的众多标准,它就像一百万个不同的检查。我们可以利用这一点来检查输入类型是否是 dict 类型。类似地,对于错误 2,我们只需要检查字典中的每个键,它的值的长度与其他键的值的长度相同(本质上是len(col1) == len(col2))。

让我们删除我们在初始 DataFrame 类中编写的所有代码,并添加以下代码。

英寸/core/frame.py:

class DataFrame: **def __init__(self, data):
        self._check_dict(data)
        self._check_array_length(data)** **def _check_dict(self, data):
        if not isinstance(data, dict):
            raise ValueError('DataFrame constructor not properly called!')** **def _check_array_length(self, data):
        raw_lengths = [len(x) for x in data.values()]
        lengths = list(set(raw_lengths))
        if len(lengths) > 1:
            raise ValueError('arrays must all be same length')**

好东西!让我们回到 jupyter 笔记本,运行下面的代码来测试我们新制作的数据帧。

**Recreate Error 1:**
import pandas_replica as pdrpdr.DataFrame(100000)**Recreate Error 2:**
import pandas_replica as pdrcol1 = [3.6, 3.5, 5.2, 6.1] # len = 4
col2 = [45, 40, 30] # len = 3
data = {'col1': col1, 'col2': col2}
pdr.DataFrame(data)**Run with no Error:**
import pandas_replica as pdrcol1 = [3.6, 3.5, 5.2, 6.1] # len = 4
col2 = [45, 40, 30, 50] # len = 4
data = {'col1': col1, 'col2': col2}
pdr.DataFrame(data)

设置数据属性

现在上面的问题已经解决了,剩下的就是将输入“附加”到我们的数据帧上。它就像在我们的 init 方法中添加下面一行一样简单。

self.data = data

实现一些数据框架方法

列方法

在 pandas 中,columns 方法返回一个包含数据帧的列标签(或者只是列标题)的列表。

在熊猫身上:

import pandas as pdcol1 = [3.6, 3.5, 5.2, 6.1] # len = 4
col2 = [45, 40, 30, 50] # len = 4
data = {'col1': col1, 'col2': col2}
pd.DataFrame(data).columns**Expected Output:**
Index(['col1', 'col2'], dtype='object')

注意,这实际上是输入字典的键,因此要在我们的熊猫崇拜者身上复制它,我们需要一个函数来返回输入字典的键。幸运的是,在 dict 上调用list函数实际上会返回 dict 的键列表。让我们在 DataFrame 类中添加一个名为columns的方法。我们包含了 [property](https://www.youtube.com/watch?v=ZDa-Z5JzLYM&list=PL-osiE80TeTsqhIuOqKhwlXsIBIdSeYtc) 装饰器,这样我们可以像调用属性一样调用它,而不是方法(本质上是使用df.columns而不是df.columns()来调用它)。

class DataFrame: def __init__(self, data):
        self._check_dict(data)
        self._check_array_length(data) def _check_dict(self, data):
        if not isinstance(data, dict):
            raise ValueError('DataFrame constructor not properly called!') def _check_array_length(self, data):
        raw_lengths = [len(x) for x in data.values()]
        lengths = list(set(raw_lengths))
        if len(lengths) > 1:
            raise ValueError('arrays must all be same length') **@property
    def columns(self):
        return list(self.data.keys())**

现在让我们回到笔记本上来测试一下:

import pandas_replica as pdrcol1 = [3.6, 3.5, 5.2, 6.1] # len = 4
col2 = [45, 40, 30, 50] # len = 4
data = {'col1': col1, 'col2': col2}
pdr.DataFrame(data).columns**Expected Output:**
['col1', 'col2']

瞧啊。非常管用!

形状方法

在 pandas 中,shape 方法返回一个表示数据帧的行数和列数的元组。

在熊猫身上:

import pandas as pdcol1 = [3.6, 3.5, 5.2, 6.1] # len = 4
col2 = [45, 40, 30, 50] # len = 4
data = {'col1': col1, 'col2': col2}
pd.DataFrame(data).shape**Expected Output:**
(4, 2)

注意,这实际上是我们计算(1)字典中数组的长度和(2)字典中键的数量。假设我们已经通过了_check_array_length方法,我们应该能够相当容易地检索(1 ),我们将把它作为我们可以在字典中找到的第一个数组的长度。类似地,对于(2),使用len()功能可以很容易地计算键的数量。

class DataFrame: def __init__(self, data):
        self._check_dict(data)
        self._check_array_length(data) def _check_dict(self, data):
        if not isinstance(data, dict):
            raise ValueError('DataFrame constructor not properly called!') def _check_array_length(self, data):
        raw_lengths = [len(x) for x in data.values()]
        lengths = list(set(raw_lengths))
        if len(lengths) > 1:
            raise ValueError('arrays must all be same length') @property
    def columns(self):
        return list(self.data.keys()) **@property
    def shape(self):
        return (len(list(self.data.values())[0]), len(self.data))**

回到我们值得信赖的笔记本:

import pandas_replica as pdrcol1 = [3.6, 3.5, 5.2, 6.1] # len = 4
col2 = [45, 40, 30, 50] # len = 4
data = {'col1': col1, 'col2': col2}
pdr.DataFrame(data).shape**Expected Output:**
(4, 2)

在笔记本电脑环境中使用我们的数据框架

我们上面所做的一切都很酷,但是如果我想将我的数据快照打印到笔记本上呢?

熊猫数据框笔记本输出

关于 jupyter notebook 如何显示这些项目的快速背景:因为 jupyter notebook 在浏览器上运行,输出的格式必须是一个 html 表格格式。在 pandas 源代码中,他们有一个叫做_repr_html_的方法,将输入数据格式化成一个时髦的 html 表格,适合浏览器。我们重建这个的想法是这样的:

  • 创建包含列标题的第一行。
  • 然后,我们将使用索引以及 array[index]的相应值来填充这些行。

让我们试一试。在 DataFrame 类的底部,我们添加以下代码:

class DataFrame:... def _repr_html_(self): **# Part 1**
        html = '<table><thead><tr><th></th>' 
        for col in self.columns:
            html += f'<th>{col}</th>'        html += '</tr></thead><tbody>' **# Part 2**
        for i in range(len(list(self.data.values())[0])):
            html += f'<tr><td><strong>{i}</strong></td>'
            for _, values in self.data.items():
                html += f'<td>{values[i]}</td>'
            html += '</tr>' html += '</tbody></table>' return html

第 1 部分:我们在这里做的是准备好表格元素来存储数据。然后我们将继续用列标题填充我们的。注意,第一个列标题是空的,因为我们需要它作为索引列。一旦我们完成了这些,我们就关闭并准备向我们的表体添加数据。

第 2 部分:然后我们继续遍历 dict 中的数组长度,添加索引 i ,并将该特定索引处的值添加到一行中。一旦我们填充了整行,然后我们继续下一行。完成后,我们关闭表格并返回刚刚创建的 html。

回到美好的老沙盒:

import pandas_replica as pdrcol1 = [3.6, 3.5, 5.2, 6.1] # len = 4
col2 = [45, 40, 30, 50] # len = 4
data = {'col1': col1, 'col2': col2}
pdr.DataFrame(data)

熊猫 _ 副本数据框笔记本输出

我看不错!

结论

所以我知道这篇文章有点长,是时候停下来了。让我们回顾一下上面的三个目标,看看我们是否已经成功检查了我们要达到的所有学习点:

  1. 对 Python 中的 OOP 有一些实践经验——我们能够创建、实例化和添加方法/属性到我们的 DataFrame 类中。除此之外,我们还接触了一点 property decorator(这非常酷)。
  2. 了解 Python 中的包是如何工作的——我们成功复制了 pandas 的包结构,并将其导入到我们的笔记本环境中。
  3. 理解 Pandas 如何使用其时髦的函数——我们能够进行一些初始输入类型检查,运行df.columnsdf.shape方法,并将表格输出打印到我们的笔记本环境中。

这绝不是 pandas 包的复杂性和全面性,但有了这种理解,它减少了理解 pandas 源代码中所发生的一切的障碍,并让像我这样的普通人深入了解那些如此融入我们日常生活的包是如何工作的。这是一个非常有趣的练习,我期待着将来能有更多的练习!

jl:创建交互式 Julia 包

原文:https://towardsdatascience.com/replmaker-jl-create-interactive-julia-packages-67be382f3eb8?source=collection_archive---------39-----------------------

Pkg 探索

快速浏览使用 Julia 的 REPLMaker.jl 包为最终用户创建交互式代码

(图片由作者提供)

介绍

交互在科学和计算计算领域的程序员中并不经常被谈论。然而,不仅拥有交互式软件,而且拥有交互式软件包也有许多独特的优势。有时,当使用许多高级编程语言的包时,可能真的需要一个简单的读取求值打印循环,或 REPL。虽然这可能并不总是必要的,但在包中使用它肯定是一件很酷的事情,并且可以通过潜在的更简单的命令和定制的语法使找到问题的解决方案变得更加容易和方便。

幸运的是,对于 Julia 用户来说,有一个非常酷的工具可以做到这一点!这个包名为 REPLMaker.jl,您当然可以像使用大多数包一样使用 Pkg 来添加它。它已经被注册到了 Julia General package registry,但是那些希望使用不同分支的人总是可以通过 Git 添加它。

using Pkg; Pkg.add("REPLMaker")

说到 REPLs,如果您仍然使用上面的方法来添加包,您可能需要熟悉一下 Pkg REPL。它提供了我前面提到的同样的好处,缺少语法和简单的命令,但也为包提供了额外的功能,使它更容易做许多您可能想不到的不同事情。

概述

在整个 Julia 生态系统的各种包中有许多使用 REPLMaker.jl 的例子。也就是说,我认为有一个特别的例子非常突出,那就是 Genie.jl。我认为 Genie.jl 提供了一个非常可靠的例子,说明各种 REPLs 是如何非常有用的,并且这个包将这些好处带到了 web 开发的奇妙世界。

使用 Genie,可以为不同的交互体验提供单独的文件,为您加载环境。这对于服务器来说尤其方便,因为命令通常是在带有虚拟监管器的 Unix 计算机上运行的。将建立一个监督程序来运行

. shell_script_file.sh

以便在没有用户在场的情况下创建应用程序的实例。然后,该服务将被添加到与原始主管拥有相同权限的进程列表中。这对 Genie 意味着,可以用来访问 Genie REPL 的相同命令也可以用来加载虚拟环境和启动使用 web 框架的 web 服务器。

我认为更多的包应该应用这种方法,因为 REPLs 肯定是一个有用的工具。此外,我认为许多软件包可以以这种方式和其他方式利用 REPLs,在 Julian 软件的整个范围内创造一个普遍的交互和愉快的体验。

使用复制器

REPLMaker 包中将要使用的主要方法几乎肯定是 initrepl()。这个方法是导出的,所以我们将通过简单地导入包来获得它:

using ReplMaker

initrepl()方法接受一个位置参数和大量决定 repl 外观的关键字参数。然而,就功能而言,我们真正需要的是一个解析器。解析器将会对我们输入到 REPL 中的字符串进行解码。我们可以使用 Base 创建一个基本的表达式解析器。Meta 的解析器与我们的字符串:

exprparse(s) = quote Meta.parse($s) end

您可能已经注意到这个函数使用了“quote”语句。对于那些不熟悉 Julian 元编程的人来说,引用表达式基本上是告诉语言,这里包含的代码将是一个一致的表达式,而不是一系列的表达式。

现在我们可以调用 initrepl()方法并提供我们的解析函数来创建一个新的 repl。我们可能还想提供另一个名为“start key”的关键字参数,这将允许我们通过按键盘上的特定键来输入 REPL。

initrepl(exprparse, 
                prompt_text="Parse />",
                prompt_color = :blue, 
                start_key=')', 
                mode_name="Expr_mode")

现在,从朱莉娅·REPL,使用我们设置的开始字符,我们可以初始化我们刚刚创建的新 REPL。这个新的 REPL 将接受并计算我们可以作为字符串传递的 Julia 表达式。这并不十分有用,但是这个基本概念为库和应用程序打开了一扇大门。

结论

REPLMaker 对于朱莉娅编程语言来说是一个非常酷的工具,它使得在朱莉娅 REPLs 上实现令人敬畏的复制器变得又快又容易。这可以使用户通过执行命令来更容易和更快地处理包,而不需要担心语法。最重要的是,创建和使用它们相当有趣,这使得它们对教育更有价值。创建解析器相当有趣,但是创建一个有效的解析器的一个非常困难的部分是创建一个求值循环,并为解析器分配要访问的内存。也就是说,有了这两件事的照顾,我们可以非常简单地获得一些非常出色的结果!

自我挑战的表示:一种有趣的鲁棒神经网络方法

原文:https://towardsdatascience.com/representation-self-challenging-an-interesting-approach-towards-robust-neural-networks-5d2380fb32af?source=collection_archive---------15-----------------------

一种以模型为中心的方法,用于组装能够跨域泛化的鲁棒神经网络

自从大约十年前深度学习的复兴以来,模型可解释性和跨领域泛化一直是学术研究和行业反思的重要组成部分,以努力生产和开发可靠和现成的人工智能系统。但是我们所说的领域泛化是什么意思呢?领域泛化指的是利用通过多个领域学习到的有用特征的概念,以便可以学习领域不可知的表示,该表示可以在超出分布的数据上相当好地执行。以这种方式训练的机器学习模型将对真实世界场景中猖獗的数据分布的变化更加鲁棒,因为它已经学习了领域的语义(例如,什么使马成为马),而不是保留训练数据中存在的分布特定的相关性。

在多个领域上训练神经网络迫使模型从多个来源学习特征,从而产生在未知测试领域上推广良好的模型。作者图片

为了使神经网络在真实世界场景中更加健壮,已经提出了无数的命题和假设,但尽管如此,可解释性和因果性仍然是一个难以捉摸的谜。尽管我们还没有找到模型可解释性的圣杯,但一切还没有失去;我们有某些工具可以用来使我们的人工智能系统更加健壮,跨领域推广,更不容易失败。这些工具可以大致分为两大类,即:

  1. 以数据为中心的方法
  2. 以模型为中心的方法

尽管大多数关于一般化、可解释性、领域随机化和不变性的文献主要集中在以模型为中心的方法上,但我所学到的——及其起伏——是以数据为中心的方法是解决这些问题的更直观和可能更合理的解决方案。下面是一些推荐的文章,阐明了这些方法及其各自的优缺点:

  • 关于从数据而非模型的角度重新思考神经网络的概述
  • 在训练数据集中引入不变性
  • 域随机化概述
  • 利用领域不变神经网络的数据扩充

然而,在某些情况下,无法利用以数据为中心的方法,即没有足够的数据来解决需要解决的问题,此外,无法以有效的方式生成有用的增强数据。在这些情况下,有几个以模型为中心的方法已经显示出前景,可以用来开发一个强大的深度学习模型。

以模型为中心的方法概述

我现在将简要地谈论一些重要的面向领域泛化的以模型为中心的方法。这个列表并不是详尽的,但是已经尽力给出了一个随着时间的推移证明是成功的方法的概述。我也会在概述之后推荐一篇我认为每个领域都值得了解的研究论文。

重要领域综合方法流程图。作者图片

  • 表征学习:这种方法的目标是学习在处理多模态或领域时有用的共享表征,特别是在一个问题的训练数据稀缺而另一个不同但相似的问题的训练数据丰富的问题中。表征学习可以进一步细分为 2)领域对齐和 2)学习解开表征。这两个分支渴望实现相同的目标,但以不同的方式着手实现这一目标。领域对齐的中心思想是最小化用于学习领域不变特征的源领域分布之间的差异。另一方面,解开表征学习学习将输入映射到特征向量的表征,该特征向量包含关于影响变化的组件的信息。推荐:【MMD】
  • 元学习:元学习是一种以数据和模型为中心的方法。这种方法在训练数据中引入了域转移,其思想是这将使模型能够在本质上具有不同域看不见域上很好地执行。这些方法将来自不同领域的数据分成元训练集和元测试集,以模拟真实世界场景中的领域转换。推荐: 深度网络快速适配元学习
  • 集成学习:用于相同任务的多个模型在大多数不同的训练和测试分裂上被训练,并且集成技术(例如多数投票)被用于平衡来自所有模型的预测的使用。推荐: 域自适应集成学习
  • 不变风险最小化:这些方法消除了在模型中引入数据特定偏差的虚假相关性,而不是学习跨领域的共享模态。这个想法是因果因子在不同的领域是不变的,如果我们去除虚假的相关性,我们就获得了对目标样本负责的因果因子。推荐: 不变风险最小化
  • 迁移学习:在大型数据集上训练模型,并为下游任务利用有用的功能。推荐: 自动综合到真实的概括
  • 训练启发法:这些方法试图迫使模型学习全局结构,例如数据中的形状,而不是专注于局部纹理。这将使模型能够放弃不同数据集之间的域转换,并在看不见的数据上执行得更好。推荐:除了阅读我们将在下面讨论的 RSC 论文之外, 统一深度监督域适配和一般化

在这篇文章中,从现在开始,我们将讨论一篇由卡内基梅隆大学的人们发表的学术论文,尽管它有着有趣的观点 “自我挑战提高跨领域概括能力” ,但相对来说它一直没有引起人们的注意。

深度学习模型从人类的神经病学中获得灵感,就像它们的灵感一样,它们是“善变、愚蠢的生物,记忆力差,具有自我毁灭的伟大天赋”——普鲁塔克。

本文试图通过提出一个非常有趣的想法来解决神经网络的反复无常。在本文中,我们将关注对这些想法及其未来前景的客观分析,而不是实现细节。但是,如果您对代码实现感兴趣,您可以阅读本文作者的代码:

RSC 的 Github 回购

自我挑战的表现:

大多数计算机视觉任务需要我们识别物体。对于我们将对象分类到不同类别的任务来说是这样的,比如将对象分类到 CIFAR-10 数据集中的 10 个类别中的任何一个。对于对象检测模型也是如此,其通常具有对象性得分,该得分给出了对象属于特定类别的置信度度量,如在 YOLO 的情况。但是是什么让一个物体成为一个物体呢?它的定义特征是什么?是什么使一个物体不同于另一个物体?在某种程度上,小汽车和公共汽车相似,它们都有四个轮子和相似的结构。那么,是什么让汽车不同于公共汽车呢?是相对大小还是别的什么?作为人类,我们如何区分马和驴?它们都有四条腿,一条尾巴,相似的大小和相似的脸,然而大多数人可以很容易地区分这两者,即使图像呈现的质量不规则。如果我们要区分马和驴,我们必须依赖马和驴的复杂特征组合,但我们不需要学习这些特征的功能来做真正好的分类。这里,**“协方差-偏移”**的概念开始发挥作用:

条件分布(即,猫的语义)在每个域中都是相同的,但是由于边缘分布的变化,模型可以学习其他东西(即,胖乎乎的脸)。— 自我挑战提高跨领域推广能力

由 2 公牛摄影在 Unsplash 上拍摄

照片由铁木尔·罗马诺夫在 Unsplash 上拍摄

这意味着在马的情况下,模型将关注虚假特征而不是因果特征,即马的语义,这可能由于较差的表示学习而导致将驴误分类为马。虽然我们人类也遭受着协变性的痛苦,但我们拥有无限的数据,学习的过程是一个持续的努力。一个从未见过驴的人很可能会把它误认为马,因为它具有马的大部分特征。但由于我们接触了如此多的物体,我们不知不觉地学会了区分两个相似的物体,而没有注意到细微的细节。曝光是一种有价值的资源,但不幸的是,这是大多数神经网络负担不起的奢侈品。

提出的解决方案是强迫模型学习需要识别的对象的启发法。为了解决这个问题,论文的作者认为,我们应该迫使网络屏蔽图像的主要表现形式,并专注于通过其他特征进行预测,就像人类在区分公共汽车和汽车、马和驴时需要专注于更细微的差异一样。就算法而言,这是如何工作的也是相当直观的:当反向传播完成时,占主导地位的特征将具有最高的梯度,如果我们将这些梯度归零,我们将无意中迫使网络在其他特征的基础上做出决定,这将增加模型的跨域泛化。

我们需要屏蔽多少功能:

表象自我挑战(RSC)通过学习图像的表象 z 来工作,然后在其迭代过程中执行以下步骤:

  • 定位:该步骤计算网络上层相对于表示 z 的梯度

上层相对于表示 z 的梯度,源

然后如果该元素在梯度中元素的前 p 个百分比中,则创建值为 0 的屏蔽向量,否则将其设置为 1。

屏蔽向量第 I 个元素,源

  • 静音:RSC 屏蔽掉与较大梯度相关的相应位,即将它们设置为零

屏蔽向量 m 和表示法 **z、**源的逐元素乘积

  • 更新:计算结果表示的 Softmax

Softmax 计算,来源

然后梯度的结果用于通过优化器更新模型。

将用于下一步更新的梯度计算,来源

因此,在训练期间,RSC 迫使神经网络使用较少的主导特征,与以传统方式训练的模型相比,这反过来有助于网络利用更多的特征进行预测。

这就留下了一个值得思考的问题:在每次迭代中,有多少特性应该被屏蔽掉?这是一个没有唯一解决方案的问题,因此作为一个超参数 p 可以根据用户的需求通过两种指导直觉进行调整:

p 越小,训练误差越小——自我挑战提高跨领域推广

因为对于网络来说,在 i.i.d(独立&同分布)数据集中进行预测会更容易,在训练期间就是这种情况

p 越大,(跨领域)泛化误差(即测试误差和训练误差之差)越小— 自我挑战提高跨领域泛化

因为我们正在迫使网络也利用其他不占优势的功能;从而提高 o.o.d(分布外)测试数据集的性能。

结论:

本文中提出的观点非常有趣,我认为应该付出更多的努力来探索这种特殊的领域泛化的途径。本文中描述的过程遵循一种直观的思维模式,并且很容易使该过程合理化。本文描述的实验结果令人印象深刻,并提供了一些关于未来探索途径的有趣见解。RSC 在 PACS、VLCs、Office-Home 和 ImageNet-Sketch 数据集上获得了最先进的领域综合结果。

PACS 上的领域概括结果。来自来源

VLCS 的领域综合结果。来自来源

RSC 展示了减少属于相同体系结构但具有不同隐藏层数的网络之间的性能差距的能力,这在诸如 ResNet50、ResNet101 和 ResNet152 之类的更高容量主干网上的实验结果中是明显的。

实际的含义是,与增加模型大小相比,RSC 可能导致更快的性能饱和。因此,我们可以缩小网络规模,以同等性能部署— 自我挑战改善跨领域推广

这种差异对于需要在实时场景中执行推理的嵌入式设备尤其有利。从 ResNet101 迁移到 ResNet50 可以使推理过程中执行的操作数量减少 2 倍以上,从而缩短响应时间。

ImageNet 上的泛化结果。来源

参考资料:

  1. 黄等《自我挑战提高跨域泛化》,2020,https://www . ri . CMU . edu/WP-content/uploads/2020/07/RSC _ ECC v2020 . pdf
  2. 莫天等《统一深度监督领域适配与泛化》,2017,https://arxiv.org/pdf/1709.10190.pdf
  3. Arjovsky 等人,“不变风险最小化”,2020 年,https://arxiv.org/pdf/1907.02893.pdf
  4. 李等《带对抗性特征学习的领域泛化》,2018,https://open access . the CVF . com/content _ cvpr _ 2018/papers/Li _ Domain _ Generalization _ With _ 2018 _ paper . pdf
  5. 芬恩等,“面向深度网络快速适应的模型不可知元学习”,2017,https://arxiv.org/pdf/1703.03400.pdf
  6. 陈等.“综合到真实的自动综合”,2020,
  7. 周等.“领域自适应集成学习”,2021,

使用 Plotly 在一个动画情节中表现 5 个特征

原文:https://towardsdatascience.com/representing-5-features-in-a-single-animated-plot-using-plotly-e4f6064c7f46?source=collection_archive---------44-----------------------

使用单个动画气泡图来分析数据和观察趋势

在 Unsplash 上温刚斋的照片

为什么要用 Plotly?

Plotly 是一个可视化库,因其惊人的功能而迅速流行起来。Plotly 与其他可视化库的区别在于它能够提供交互式、动态、易于使用的高度详细的图。使用 Plotly,我们可以通过点击按钮、下拉菜单、滑块甚至创建动画来与我们的图进行交互。更好的是,我们可以毫不费力地绘制出非常吸引人的图表,就像 Plotly 在引擎盖下为我们做所有繁重的工作一样。我们将看到使用气泡图是多么容易,只需一次函数调用就可以制作出动画。

分析过去 5 年足球运动员的趋势

对于今天的文章,我们将使用 Kaggle 的 Fifa 数据集,其中包含 2015 年至 2021 年足球运动员的详细信息。具体来说,我们将看到一个足球运动员的年龄、价值、综合评分、转让价值和比赛位置之间的关系,以及这些属性是否会随着时间的推移而显示出任何模式。为了从各种文件中提取这些特征,我必须做一些预处理,这在整个代码笔记本中都存在。下面是预处理和组合各种文件后的最终数据。

预处理后的最终数据集

什么是气泡图?

气泡图本质上是一个散点图,其中的数据点大小不同。气泡的大小可以与一个附加属性(**通常是数字)**相关联,这可以帮助更好地辨别数据点。此外,我们可以根据数据点所属的特定类别对其进行着色。

总之,下面是气泡图的组成部分

  • x 和 Y 轴-数字特征
  • 气泡的大小—数字特征
  • 气泡的颜色——分类特征

添加动画

我们将添加一个动画,自动移动滑块(负责改变我们想要可视化的年份)及时向前显示我们每年的球员属性。调整滑块时,图表会更新,以绘制给定年份的玩家属性。

让我们开始吧!

我选择了 2015 年排名前 20 位的玩家(根据总体评分)来观察他们在 2021 年的表现。注意- 我只选择了从 2015 年到 2021 年每年都活跃的球员。

  1. 我建议你在 Google Colab 上运行这段代码,因为它已经预先安装好了。如果你想在本地运行代码,你必须pip install plotly
  2. 为情节设置参数。正如你在下面看到的,我们使用了数据集 名称作为第一个参数。将 x 轴设置为绘制年龄,将 y 轴设置为绘制总体**。设置大小参数以反映玩家的** 市场价值颜色 参数设置为球员 _ 位置,分为进攻球员、中场球员、防守球员或守门员。我们还设置了 hover_name 参数来显示玩家的名字。不要忘记设置 max_size 参数,因为真实的市场价值是以百万计的,我们需要限制泡沫的大小。最重要的是,我们将 animation_frame 参数设置为year 以自动在数据集的 year 列下添加不同年份的滑块。最后,我们为 x 和 y 值设置一个范围,并为图表设置一个标题。

最终气泡图

3.创建一个描述 5 个特性的综合气泡图和一个动画只需要一个函数调用!令人印象深刻的是阴谋是多么容易,不是吗?随意使用绘图右上角的选项,例如缩放、选择特定区域或下载图像。还要注意当我们悬停在一个泡泡上时玩家的信息是如何显示的!

寻找趋势

  • 首先我们可以看到年龄市场价值的关系。一个球员的价值在他将近 30 岁的时候上升并达到顶峰,一旦他进入 30 岁就开始下降。这种下降对于由蓝色气泡描绘的进攻者来说是最剧烈的,而对于由红色气泡描绘的守门员来说是最温和的。气泡大小有助于我们直观地看到市场价值,并根据市场价值比较玩家。
  • 我们还可以看到随着 T2 年龄的增长,总体 T1 开始下降。这是有道理的,因为足球运动员的职业生涯很短。再次非常有趣地看到,攻击手和中场比后卫和守门员衰退得更快,因为他们的大部分属性依赖于体格、耐力和速度,在打了这么久的比赛后这些属性开始消失
  • 一个非常明显的相关性是整体价值和市场价值之间的关系。然而,有趣的是看到年龄也开始影响市场价值,因为俱乐部想要购买可以长期比赛的球员。

Plotly 中的动画警告

当每一行输入都存在于所有动画帧中,并且映射到符号、颜色和面的分类值在各帧中保持不变时,动画可以很好地工作。如果不满足这些约束条件,动画可能会产生误导或不一致。—官方 Plotly 文档

结论

这只是 Plotly 能力的一瞥,当与 Dash 一起使用时,Plotly 可以创建漂亮的动画仪表盘和图形,具有高度的交互性。我还想指出 Plotly 如何提供按钮和工具(用于缩放、选择等。)就其本身而言,这些在更深入的分析中是非常有用的工具,并且可以使可视化更加容易。

我希望这是有趣和好玩的。下次见!

查看我的 github 的一些其他项目和整个代码。你可以在这里联系我https://rajsangani.me/ 感谢您的配合!

表示 3D 空间中的平面和直线

原文:https://towardsdatascience.com/representing-planes-and-lines-in-3d-space-410845a8747e?source=collection_archive---------15-----------------------

迈克尔·泽兹奇在 Unsplash 上的照片

平面和直线是三维计算机视觉和计算机图形学中有用的几何实体。将它们表示为一组点是低效的,根据用于生成这些点的步长,这导致了大量的存储器需求。在本文中,我将讨论如何使用平面和直线的矢量方程来表示它们。我还将介绍如何使用向量形式找到直线与平面的交点。

三维线条

我们可以用下面的等式[1]来表示矢量形式的直线。

p =l₀+l .d d ∈ R

其中 l 是直线方向的矢量, l₀ 是直线上的一点,d 是实标量。

p 是直线上的一般点,这些点的轨迹定义了它。因此,为了定义一条线,我们只需要知道 6 个数字/参数就可以用矢量形式完整地表示它。

我已经创建了一个类来表示一个线向量并绘制它。它由一个向量和一个线上点来参数化。两者都是 3x1 numpy 列向量。

为了得到直线上的点以便绘图,我们可以使用矢量方程本身。改变 d 并将矢量的缩放版本添加到线上的点,您将获得线上的点!然后可以使用任何绘图库来绘制它们。

我将展示一些样本线。

向量(1,1,1)点(0,0,0)[来源:作者]

如果你想要一条横跨 2D 平面的线,那么你可以用一个只有两个坐标中非零值的向量,你会得到一条 2D 平面的线。

向量(1,1,0)点(0,0,0)[来源:作者]

三维平面

我们可以用下面的等式用矢量形式表示一个平面。

(p——**p₀)。n = 0,**其中 n 是平面的法向矢量 p₀ 是平面上的一点。

上式中所有点 p 的轨迹定义了该平面。术语 (pp₀) 表示平面中的矢量,而 n 是与平面正交的矢量。因此,对于位于平面上的所有点 p 的这些向量,这两个彼此正交的向量的点积将为零。

如此优雅的表达一个只有 6 个数字的平面的方式!

下面是 Python 中使用上述定义的一个平面类。

你可以在本文中了解更多平面表现方式:https://medium . com/@ Daniel . j . Lenton/part-iii-projective-geometry-in-3d-37f 36746733 b作者丹尼尔·伦顿。

接下来,我们来看看如何求一条直线与平面的交点。

3D [1]中点与平面的相交

现在我们知道了如何在 3D 中表示点和平面,我们可以看看如何找到这两个几何实体之间的交集。如果一条线和一个地方相交于一个点 p,,它将满足直线和平面方程。所以,求交点,通过把 p 的值从一条直线的方程代入一个平面的方程。

((l₀+l .d)——p₀)。n = 0

展开这些项得到下面的等式。

(法律条文) d + ( l₀ — p₀)。n = 0

求解 d 得出:

p₀——l₀。(法律名称)

这是 d 的值,它给了我们一个点,这个点既在直线上,又在平面上,平面就是定义的交点。

有三种情况相交。

  1. 首先是没有交点,即直线和平面平行,但直线不在平面内。
  2. 接下来,正好有一个交点。
  3. 最后,直线平行于平面并位于平面上,在这种情况下,直线上的每一点也将位于平面上。因此,在这种情况下会有无穷多个点同时满足这两个方程。

来源:https://en . Wikipedia . org/wiki/Image:Plane-line-intersection . png

对于前两种情况, l . n = 0 ,因为 l 将垂直于它们的法向量 n 。否则,我们将得到一个实数值 d ,它可以被代入直线方程以得到交点:

**p =l₀+l .**t34】d

我已经为平面和直线类编写了一个基于上述等式计算交集的函数。注意,函数是同一个方程,唯一不同的是代码语法。

我们现在可以使用 plane 和 line 类来查找它们之间的交点。

例如:

我们可以使用 sympy 来验证结果:

我们使用 sympy 实现也得到同样的结果,它验证了我们的代码。

来源:作者

结论

在这篇文章中,我们看了 3D 中的直线和平面。我们看到了它们的向量方程,以及如何用向量和点来表示它们。这允许只有 6 个数字的非常紧凑的表示。我们终于看到了如何找到两者之间的交集,并查看了三种可能的交集情况。我希望你觉得这很有用。关注更多有趣的文章。感谢您的阅读!

参考

[1]https://en . Wikipedia . org/wiki/Line % E2 % 80% 93 plane _ intersection

如何在不编码的情况下用 Python 分析数据

原文:https://towardsdatascience.com/reproducible-data-analysis-in-python-without-coding-806a7652dd34?source=collection_archive---------29-----------------------

使用导出 Python 代码的免费 pandas/plotly GUI

Marc-Olivier Jodoin 在 Unsplash 上拍摄的照片

免责声明 : ⚠️前方傻笑话!此外,我是 bamboolib 的共同创建者。

数据无处不在,现在比以往任何时候,我们都有免费的工具来探索它们。用于数据争论和探索的最流行的 python 库之一是 pandas(我猜你已经第 1354 次读到这句话了)。但是,说实话,它没有最直观的语法。这不仅使它很难学,而且如果你不天天和熊猫一起工作,也很难记住它的语法。

熊猫没有最直观的语法

此外,编写 pandas 代码有时感觉有点像用北欧战斧切菜:它完成了工作,我在做这件事的时候莫名其妙地觉得很酷,但当看到结果时,它看起来并不好,所以我宁愿不积极参与那种混乱。

脑海中浮现出这个怪异的画面,我有一种强烈的冲动,想创建一个 GUI 来为我生成 Python 代码,这样我就可以专注于从我的数据中获得见解,并与我心爱的人分享它们……嗯……我是说,我的同事。

遇见 bamboolib

bamboolib 是一个免费的 GUI,可以让你准备和探索你的数据。每次编辑数据以及使用该工具创建的每个交互式绘图,它都会为您创建 python 代码。这样,您可以跟踪您的步骤,重复您的分析,并创建交互式报告。

专注于获取和分享见解,而不是编码

如果您想使用 Jupyter Notebook/JupyterLab 使用 Python pandas 分析和探索数据,但是 a)完全是初学者,b)不想一直查找 pandas 语法,或者 b)只是想快速理解您的数据,而不必在笔记本中编写太多代码,那么这篇博客文章就是为您准备的。

您将了解到:

  • 如何立即探索一个你从未见过的数据集(手指交叉),而不必自己写一行代码
  • 同时还在生产 python 代码。

一个小实验

关于博客帖子的格式,我想我会做一个实验:我将探索一个我以前从未见过的数据集并和你一起经历我的 EDA 步骤。所以,不管这些数据有多乱/干净/无聊/令人兴奋,我都会探索并写一篇关于它的博客文章与你分享。而你(读这几行)是我做的见证人!

那么,从哪里获取数据呢?由于我过去经常编写 R 代码并且写了很多关于它的文章,我去了的数据科学“TidyTuesday”网站,搜索了第一个引起我注意的随机数据集。

我在那里找到了它:关于“全球森林砍伐——大豆生产和使用”❤️.的数据这听起来就像“和公公婆婆在孤岛上呆一个月……”一样有趣,但至少引起了我的注意。

简而言之,数据集显示了生产了多少吨大豆用于动物饲料、人类食物和燃料。

安装 bamboolib

在我们开始之前,让我们按照文档中的描述安装 bamboolib。

pip install bamboolib
python -m bamboolib install_extensions

探索全球大豆的使用

让我们打开一个 Jupyter 笔记本或 JupyterLab,无论你喜欢哪个。你可以自己输入数据

import pandas as pd
soybean_use = pd.read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-04-06/soybean_use.csv')

让我们用 bamboolib 来看看。在笔记本单元格中运行以下代码:

import bamboolib as bamsoybean_use

我们数据的交互视图。bamboolib 在静态熊猫 HTML 输出中添加了一个绿色小按钮。如果你点击那个按钮,你会看到如上所示的数据。

实体引起了我的注意,因为我不确定它会包含什么值。第一个观察结果来自非洲,所以我认为实体可能包含大洲,但是滚动数据会发现实体也包含国家和一些人工聚类,如“净粮食进口发展中国家”。

我滚动浏览这些行,找到了国家名称和人造集群的名称。

我只对国家感兴趣。那么剩下的怎么去除呢?

我有一种感觉代码指的是国家代码(开个玩笑,其实在数据描述里是这么说的),所以如果我把所有没有代码的行都去掉,我的数据里应该只有国家。但是在盲目丢弃任何行之前,我首先检查缺失代码的实体确实不是国家(“因为我们是谨慎的数据科学家!”).我这样做是通过过滤掉所有缺少代码的行,然后查看过滤后的数据来验证数据中不再有国家。

为此,我在搜索字段中搜索“filter ”,并使用 UI 过滤掉所有缺少代码的行

我给这个临时数据起了一个新名字 missings ,因为我不想覆盖原来的数据集。执行过滤器后,我从界面得到一些反馈,有 8,163 行(82%的数据)被删除了。另外,bamboolib 为我生成了以下代码:

missings = soybean_use.loc[soybean_use['code'].isna()]

当我点击新的缺失数据框的实体列标题时,我得到一个列摘要:

我们的缺失数据框中 实体 列的汇总

我点击了 show all value counts 按钮,滚动了所有的实体值,正如所料,数据中不再有国家(如果你认为“北非”是一个国家,请给我发消息:-)。

因为我现在知道删除缺少代码的行不会丢弃任何国家,所以我从上面撤销那个过滤器(通过点击撤销按钮)并选择所有缺少代码而不是的行。

过滤后,我的数据看起来像这样:

现在,让我们来看看其余的列。

  • 年份明显。
  • 人类食物包含用于人类食物(豆豉、豆腐、豆浆、毛豆等)的大豆数量(以吨为单位)
  • 动物饲料包含直接喂给动物的大豆数量(吨)
  • 已加工包含加工成植物油、生物燃料和加工动物饲料(如豆饼)的大豆数量(以吨计)

一点数据争论

目前,数据是宽格式的,但我希望它是长格式的,以便更容易绘制(长格式数据(也称为整齐数据)使聚合和绘制更方便。你可能会问,为什么?你一会儿就会明白了)。

为此,我们需要重塑我们的数据。

我现在可以继续尝试在文本中解释从宽到长的重塑,但我更愿意将一些❤️放入一个漂亮的彩色图像中,以便更好地解释这一点。最后,我想得到的是以下内容:

在我的例子中,ID 将对应于三列实体代码年份。

在 UI 中,我搜索“重塑”,得到两个选项可供选择。因为我想变宽→变长,所以我选择“Unpivot/Melt”转换。

重塑— 1

重塑— 2

我选择实体代码年份作为 ID 列。瞧:

生成的代码是:

*# Step: Melt columns based on the index columns entity, code, year
soybean_use_long = soybean_use.melt(id_vars=['entity', 'code', 'year'])*

我还会给两列变量起更直观的名字。为此,我使用了“重命名列”转换。

生成的代码是:

*# Step: Rename multiple columns
soybean_use_long = soybean_use_long.rename(columns={'value': 'soy_use_in_tonnes', 'variable': 'category'})*

一些交互式可视化?

有了这些数据,我觉得可以开始一些可视化了。例如,我想展示以下内容:

  1. 全球大豆产量如何随时间变化(假设我有地球上每个国家的数据)
  2. 世界范围内大豆使用在各个类别中的变化
  3. 还有更多的,但这将变得和我奶奶主持的长达 4 小时的 dia 节目一样无聊。所以让我们只关注上面的两个项目。

但在我们开始回答问题之前,我只是好奇看看哪些国家的大豆使用量最高,所以我把我的数据按大豆使用量吨降序排列。

它产生代码

*# Step: Sort column(s) soy_use_in_tonnes descending (Z-A)
soybean_use_long = soybean_use_long.sort_values(by=['soy_use_in_tonnes'], ascending=[False])*

这样做的时候,我发现我们的数据中仍然有条目不是来自一个国家,而是包含了整个世界!

这是相当幸运的!无论如何,我想显示全球大豆的使用量,所以我不用自己生成总数,而是简单地使用来自世界实体的数据。

我这样做是通过过滤实体等于“World”的行。我称这些数据为大豆使用世界。你可怜的拇指(或者指针,如果你用电脑和鼠标阅读这篇文章的话)今天可能会让你浏览更多的东西,所以我在这里给你省略了截图。生成的代码是:

*# Step: Keep rows where entity is one of: World
soy_use_world = soybean_use_long.loc[soybean_use_long['entity'].isin(['World'])]*

所以有了 soy_use_world 在我手里,让我们最终创造一些互动的剧情吧!

最后,一些交互式可视化!

随着时间的推移,全球大豆产量发生了怎样的变化?为了回答这个问题,我们使用 soy_use_world 和 sumsoy _ use _ in _ tonts。为此,我在文本字段中搜索“aggregate”。

代码:

*# Step: Group by and aggregate
soy_use_world_by_year = soy_use_world.groupby(['entity', 'year']).agg(soy_use_in_tonnes_sum=('soy_use_in_tonnes', 'sum')).reset_index()*

然后,我点击创建情节*。*

让我们创建一个面积图(因为折线图是如此 2020!)显示了大豆产量随时间的变化。

全球大豆产量(吨)

已经生成的代码是:

*import plotly.express as px
fig = px.area(soy_use_world_by_year, x='year', y='soy_use_in_tonnes_sum')
fig*

接下来,我将大豆在各个类别中的使用情况进行可视化。为此,我撤销了上面的聚合步骤,并创建了以下可视化:

按类别分列的全球大豆产量(吨)

还记得我在上面写的关于将数据从宽到长重新整形的文章吗?没有吗?别担心,你不是唯一一个浏览博客文章寻找相关内容的人;-).但是如果你阅读了到目前为止的所有内容,你最终会明白为什么我改变了数据。我这样做是因为对于长格式的数据,我只需选择类别作为我的绘图的颜色属性,就可以用 plotly 创建一个堆积条形图。这就是哈德利·韦翰称之为“整洁数据”( T21)的美妙之处。

有意思!看起来大多数大豆被加工成油、燃料和豆饼。

一些更好的可视化!

我想给你看的最后一件事是:使用 plot creator,你可以调整你的图形。让我们给它一个更好的调色板和一般的图形主题,创建一个吸引人的图形标题,并删除轴标签。

最终的剧情看起来是这样的:

很漂亮,是吧?嗯,代码并不整洁:

*import plotly.express as px
fig = px.bar(soy_use_world, x='year', y='soy_use_in_tonnes', color='category', color_discrete_sequence=px.colors.qualitative.Pastel, template='presentation', title='Where has my soy gone? Global soy use in tonnes', labels={'soy_use_in_tonnes': ' ', 'year': ' '})
fig*

还好我不用自己去谷歌那些东西!

离别的思绪

在这篇博文中,我们看了一个数据集,希望我们俩都没见过(至少我没见过!)—大豆的全球生产和使用。我们在几分钟内探索、准备并可视化了数据,没有搜索语法,也没有编写一行代码,同时仍然在创建代码。

我希望 bamboolib 能帮助你更快地从数据中获得洞察力,并在你即将到来的项目中减少认知负荷。

最后一件事:您可以用插件扩展 bamboolib(例如,创建一个类似“大写所有列名,然后只保留以' _xyz' '结尾的列,最后计算每对列之间的百分比差异”的转换)。在这里阅读更多关于如何为 bamboolib 自己编写插件的。

如果你喜欢这篇文章,请告诉我你接下来想读什么!

使用 Jupyter 笔记本和 Guild AI 进行可重复的实验

原文:https://towardsdatascience.com/reproducible-experiments-with-jupyter-notebooks-and-guild-ai-3bd3c0d84456?source=collection_archive---------40-----------------------

笔记本是为灵活的专门 Python 开发而设计的。这给再现性带来了挑战。单元可以按任何顺序运行,并可以随时修改。您如何确定电池输出准确反映了电流源?即使有仔细的训练,你也不能确定一个结果与代码一致。

Guild AI——一个轻量级、开源实验跟踪工具——使用一个简单的方案来解决这个问题:

自上而下执行一个笔记本副本作为实验神器。

结果是一个笔记本,其输出准确地反映了执行时的源代码。这是一份可靠的实验记录。

Guild 在运行 Jupyter 笔记本时会执行额外的任务:

  • 使用特定于运行的参数值修改笔记本副本(例如,在实验中使用不同的学习率)
  • 保存实验元数据、输出和生成的文件
  • 将完全执行的笔记本导出为 HTML 格式,以便于查看
  • 在 TensorBoard 中保存图像和标量汇总用于比较

示例:运行笔记本

这是一个模拟训练跑步的笔记本示例。代码改编自 带目的的贝叶斯优化

Jupyter 笔记本示例

train功能模拟训练过程。它将两个超参数作为输入,并打印一个“损失”样本。

用 Guild AI 运行这个笔记本作为实验:

$ guild run train.ipynb

公会检查笔记本,发现两个超参数:xnoise

You are about to run train.ipynb
  noise: 0.1
  x: 1.0
Continue? (Y/n)

确认后,公会首先将train.ipynb复制到一个唯一的目录中,该目录包含实验数据。这确保了该实验不同于原始笔记本和任何其他实验。Guild 修改实验副本以反映为运行指定的标志值。在这种情况下,Guild 使用笔记本中定义的默认值。接下来,Guild 从上到下运行笔记本副本中的所有单元格。每个单元格的结果都保存在笔记本副本中。原笔记本未修改。

以下是运行的输出:

INFO: [guild] Initializing train.ipynb for run
INFO: [guild] Executing train.ipynb
loss: -0.116769
INFO: [guild] Saving HTML

因为笔记本副本在运行后不会改变,所以它是一个值得信赖的记录。

再次运行原来的笔记本,这次使用不同的x:

$ guild run train.ipynb x=-1.0
You are about to run train.ipynb
  noise: 0.1
  x: -1.0
Continue? (Y/n)

公会用另一个笔记本副本创建了一个新的实验。副本对x使用不同的值。

INFO: [guild] Initializing train.ipynb for run
INFO: [guild] Executing train.ipynb
loss: 0.349546
INFO: [guild] Saving HTML

注意,在这个实验中,损耗更高。x的新值似乎是次优的。

你现在有两个实验,每个实验都有不同的x值和相应的损失。显示可用行程:

$ guild runs
[1:66cec0f3]  train.ipynb  completed  noise=0.1 x=-1.0
[2:ccbd822a]  train.ipynb  completed  noise=0.1 x=1.0

除了运行和记录实验,Guild 还提供了一些特性来验证它们:

  • 比较结果——例如,找到损失最低的运行
  • 查看特定实验的文件和元数据
  • 比较文件以查看不同实验之间的变化
  • 比较图和图像
  • 使用结果为新实验建议最佳超参数

比较运行

直接从命令行比较不同实验的标记和结果:

$ guild compare

公会显示了两个回合各自的旗值和损失。如果您对模型性能有疑问,请使用此方法快速评估结果。请注意,出现在顶行的最新实验的损耗高于第一个实验。

从命令行比较运行

q退出程序。

您也可以用图形方式比较运行:

$ guild view

这个命令会在你的浏览器中打开公会视图,你可以使用它来浏览跑步细节并并排比较结果。要退出,返回命令提示符并按下Ctrl-c

以图形方式比较运行

不同笔记本

通过将每个实验保存为单独的笔记本,您可以通过区分它们的笔记本文件来比较两个实验。Guild 使用 nbdime 如果安装的话在你的浏览器中并排显示差异。

显示两个实验笔记本文件之间的差异:

$ guild diff --path train.ipynb

该命令适用于最近两次运行的train.ipynb。您可以根据需要指定不同的管路。

并排比较实验笔记本

注意不同之处:

  • x的值从1.0变为-1.0
  • 损失增加,表明x的值-1.0是次优的

还要注意的是,每个实验的train功能都是相同的。如果修改了函数,上面会显示精确的逐行修改。这可以让你评估实验的所有相关方面。

超参数搜索

Guild 支持批处理运行用一个命令生成多个实验。这用于多种超参数搜索方法,包括网格搜索、随机搜索和顺序优化。

例如,您可以使用贝叶斯优化和高斯过程来运行搜索,以找到最小化lossx值:

$ guild run train.ipynb x=[-2.0:2.0] --optimizer gp --minimize loss
You are about to run train.ipynb with 'skopt:gp' optimizer (max 20 trials, minimize loss)
  noise: 0.1
  x: [-2.0:2.0]
Optimizer flags:
  acq-func: gp_hedge
  kappa: 1.96
  noise: gaussian
  random-starts: 3
  xi: 0.05
Continue? (Y/n)

该命令使用-2.02.0之间的x值运行一系列实验,根据之前的结果,这些值更有可能最小化loss。默认情况下,公会产生 20 个回合。您可以根据需要对其进行更改,以运行更多或更少的实验。

当 Guild 完成命令时,你有 22 次运行——最初的两次加上搜索的 20 次。

使用guild compareguild view(见上文)比较运行。

您可以使用张量板评估超参数。Guild 集成了 TensorBoard 作为跑步的可视化工具。

$ guild tensorboard

此命令在浏览器中打开 TensorBoard。点击 HPARAMS ,然后点击PARALLEL COORDINATES VIEW来可视化超参数对结果指标的影响。

loss轴的下部点击并拖动鼠标。这突出显示了使用x最佳值的实验。

使用平行坐标视图评估实验结果

您可以看到x的最佳值落在-0.50.0之间

接下来,点击散点图矩阵视图

用散点图评估超参数和度量之间的关系

这显示了比较超参数和度量的图。xloss之间的比较显示了最佳运行——即loss最低的位置——沿着底部轴聚集,使x的值刚好低于0。该区域在上面以黄色突出显示。

根据train函数,将突出显示的散点图与xloss之间的已知关系进行比较:

x 与损失(信用)的关系

散点图中的点对应于xloss之间的已知关系。与其他价值观相比,全球负面的loss结果相对更多。这表明贝叶斯模型对于预测最优超参数的某种程度的有效性,至少对于这个简单的例子是如此。

真实世界的模型没有这么简单。这个例子仅仅强调了我们已经知道的关于xloss的事情。尽管如此,您可以将这种技术应用于任何模型,以获得重要的见解和优化,如果没有系统的度量,这将是困难的。

再现性和 Jupyter 笔记本

这种方法支持完全可再现的笔记本实验。要重现实验,使用 Guild 运行笔记本,并将结果与基线进行比较。

结果可以进行数字比较,例如比较诸如损失准确性auc 等指标。也可以通过并排比较图和其他实验工件来定性地比较结果。

考虑以下两个实验的 SVM 分类界限(与我们的模拟例子无关):

比较 SVM 分类界限

分类损失的简单数字比较可能显示这些模型表现相似。他们实际上是非常不同的。当你看这些情节时,这是显而易见的。一个结果是否成功地复制了另一个结果是一个需要进一步讨论的问题。无论如何,拥有全面的评估标准是这一过程的关键。

轻量级方法

请注意 Guild 运行和跟踪实验的轻量级方法:

  • 原笔记本没有修改支持公会功能。标志、输出、度量和绘图都可以作为结果使用,无需使用特定于公会的 API。
  • 运行实验不需要额外的代码。您使用外部命令。这适用于单次运行和批量超参数优化。
  • 结果保存在本地磁盘上。你不需要安装数据库或其他服务。你不会把数据发送到云端。
  • Guild 集成了一流的工具,而不是与之竞争。您使用 TensorBoard 和 nbdime,它们都是功能丰富的工具,专门用于特定的任务。

摘要

为了用 Jupyter 笔记本准确记录实验,单元格输出反映单元格源代码是很重要的。这对于笔记本来说是一个挑战,因为你可以在任何时候以任何顺序自由地编辑和执行单元格。虽然这种灵活性适用于特定的实验性开发,但是您需要可靠的控制来进行精确的实验跟踪。

公会人工智能通过自动化笔记本执行来解决这个问题。当你用 Guild 运行一个笔记本时,没有手动干预或无序单元执行的机会。Guild 为每个实验创建一个笔记本副本,并从上到下执行其单元格。这将产生一致的结果,并确保对原始笔记本的更改不会影响它们。

除了这个执行模型,Guild 还提供了查看和比较实验的特性。有了这些工具,您可以利用您的工作来对后续步骤做出明智的决策。您可以检测回归并捕捉意外的变化。

Guild 作为一个轻量级的、功能丰富的工具提供了这种支持,可以在 Apache 2 开源许可下免费获得。

链接

有关更多信息,请参见以下链接:

  • 朱庇特笔记本实验文档
  • 公会 AI 网站
  • 在线社区
  • GitHub 库
  • 训练二元分类器的分步示例笔记本

R 中的可复制作品

原文:https://towardsdatascience.com/reproducible-work-in-r-e7d160d5d198?source=collection_archive---------12-----------------------

为什么以及如何在日常工作流程中使用 Docker

疼痛源

  • 作为数据科学家,我们在处理生产级代码库时面临的最大挑战之一是确保端到端的可重复性和长期稳定性。
  • 这也会延伸到学术界,但我不是院士。
  • 基本的 R setup 没有内置的功能来存储包版本和依赖关系(例如,不像在 conda 环境中运行)。
  • 如果你的默认库是 CRAN,这是一个大问题,因为 CRAN 每天都在更新。
  • 我发现像pkgdown这样的解决方案用起来太麻烦了。我甚至尝试过在 conda 环境下工作,但我不是一个粉丝。
  • 数据科学家之间的团队合作增加了额外的复杂性。根据我的经验:
  • 一个团队中的成员可以拥有所有东西的不同版本——OS、R 内核、包、库、环境变量…!
  • 一些人在升级他们的系统上是保守的;其他人很乐意尝试新版本的软件包
  • 跨操作系统工作经常会带来意想不到的问题(IME,特别是那些允许并行计算的函数mcapplyfuture)。)
  • 当在本地系统(可能是 Windows)上开发代码库,但在另一个环境(可能是 Linux)中部署代码库时,这就成了问题

解决办法

根据我的经验,我下面概述的解决方案为我提供了一个长期可再现性和代码稳定性的优秀方法:

  1. 使用 MRAN 快照锁定您的 R 包版本
  2. 使用 Docker 图像锁定您的工作环境
  3. 在 Docker 容器中进行所有开发
  4. 为所有项目附上 Docker 图像

这非常有效,我已经有半年多没有在本地机器上使用 R/Rstudio 了。

1.MRAN 时间机器

微软 R 应用网(MRAN)提供了一款“时光机”。这项服务为 CRAN 仓库仓库拍摄每日快照,时间可以追溯到 2014 年 9 月。您可以在他们的页面上浏览快照:

MRAN 网站截图。来自作者。

MRAN 快照使用日期作为“索引”,帮助我们锁定软件包版本。例如,运行:

install.packages("lattice", 
                 repos = "https://mran.microsoft.com/snapshot/2020-10-01")

将于 2020 年 10 月 1 日安装{lattice}版本。

现在,这种方法并没有使选择随时间发布的包特定版本变得更容易,而是允许您锁定一个日期,只获得在所选日期可用的那些版本。这意味着在 10 月 1 日之后的任何一天运行“更新包”都不会改变你的包配置。

> options(repos = "https://mran.microsoft.com/snapshot/2020-10-01")
> getOption("repos")
[1] "https://mran.microsoft.com/snapshot/2020-10-01"

2.Docker 图像

文档文件

Dockerfile保存了如何构建 docker 映像的定义。我用来维护这个博客的 Dockerfile 保存在这里。它也在 hub.docker.com 举办。

下面是对该文件的快速解释。要更深入地了解 Dockerfiles,网上有很多资源 1 、 2 、 3 。

我使用的是rocker/tidyverse:4.0.0 图像,它提供了一个很好的起点。它预装了 R 4 . 0 . 0 版和 tidyverse 包。

FROM rocker/tidyverse:4.0.0

奔跑

这将安装后续 R 包工作所需的许多 linux 库。我还安装了一些有用的实用软件包,如curljqvim

RUN apt-get update && apt-get install -y --no-install-recommends \
        libgit2-dev \
        libxml2-dev \
        ... \
        ... \
        curl \
        tree \
        jq \
        htop \
        texinfo \
        vim \
        man-db \
        less

环境+ R PKG 安装

我在这里设置 MRAN 的构建日期,然后安装我需要的 R 包,使用install2.r-r参数指向 MRAN 时间机器而不是 CRAN。

ENV MRAN_BUILD_DATE=2020-09-01# Install Basic Utility R Packages
RUN install2.r -r https://cran.microsoft.com/snapshot/${MRAN_BUILD_DATE} \
    --error \
    rgl \
    data.table \
    reprex \
    # ~ 30 more R Packages
    ... \
    ... 

构建和推送

构建 docker 映像并将其推送到hub.docker.com

docker build . -t hatmatrix/blog:latest
docker push hatmatrix/blog:latest

你的 docker 图片现在可以在线提供给任何运行你的项目的人。

3.在 Docker 中开发

现在我有了一个稳定的 docker 图片,可以用在这个博客上。我可以用这个 shell 命令运行映像:

docker run 
    -d 
    -e PASSWORD=1234 
    -v ~/github/:/home/rstudio/projects/ 
    -p 3838:3838 
    -p 8787:8787 
    hatmatrix/blog:latest

该命令的组成部分包括:

  • docker run:运行 docker 镜像…
  • -d:在分离模式下,即一旦图像在后台运行,您将得到您的 shell 提示
  • -e PASSWORD=1234 : -e是附加参数。这里,我们将 Rstudio 密码设置为 1234
  • -v:这将我本地机器上的~/github/映射到 docker 容器中的~/home/rstudio/projects/
  • 这些参数将端口从我的本地机器映射到 docker 中的端口。我们需要一个用于 rstudio (8787)的,一个用于我们从 rstudio (3838)中启动的任何闪亮的应用程序
  • hatmatrix/blog:latest:这是 docker 图像的名称

**-v**:没有-v你将无法访问 docker 容器中的任何本地文件。请记住,docker 容器与您的本地机器完全隔离。此外,由于容器是短暂的(即短暂的&临时的),一旦容器关闭,您将永久丢失存储在其中的任何数据。映射到本地文件夹允许您处理本地存储在容器中的项目。

4.Docker 图像伴随 R 项目

只需在您的工作项目目录中创建一个/docker文件夹,并保存您的 docker 文件。这里是我的博客的例子:示例 docker 文件夹。可选地,创建一个docker-build.sh来节省一些输入。

就是这样!

这是一个轻量级的工作流程,无论我在哪个操作系统上工作,它都允许我保持完全的可再现性和代码稳定性。

有用的链接

  • https://mran.microsoft.com/timemachine
  • https://github.com/rocker-org/rocker

熊猫的重采样功能

原文:https://towardsdatascience.com/resample-function-of-pandas-79b17ec82a78?source=collection_archive---------4-----------------------

pandas 的重采样功能在时间序列数据中的应用

Nathan Dumlao 在 Unsplash 上拍摄的照片

重采样用于时间序列数据。对于时间序列数据的频率转换和重采样,这是一种方便的方法。尽管它的工作条件是对象必须具有类似日期时间的索引(例如,DatetimeIndex、PeriodIndex 或 TimedeltaIndex)。简而言之,如果想按月、周、日等模式排列时间序列数据。,这个功能很有用。熊猫图书馆有这个功能。为了演示的目的,使用了 https://archive.ics.uci.edu/ml/datasets/Parking+Birmingham 的 UCI 数据集,即。

阅读数据

在时序数据中,当我们从. csv 文件中读取数据时,日期变量的数据类型是 objects。因此,为了读取 datetime 格式的 date 列,我们使用了 parse_dates 参数。在研究数据中,LastUpdated 是日期变量,parse_dates=["LastUpdated"]参数正确读取日期格式,而当 parse_dates 参数不使用" LastUpdated "时,变量类型是对象。

作者图片

日期时间索引

由于重采样函数使用 DatetimeIndex、PeriodIndex 或 TimedeltaIndex,因此,现在我们需要将变量“LastUpdated”更改为 datetimeindex,如下所示:

作者图片

重采样

重采样是对时间序列进行频率转换和重采样。因此,如果需要将数据从每天更改为每月或每周等。反之亦然。为此,我们在 pandas 库中提供了重采样选项[2]。在重采样函数中,如果我们需要将日期更改为 datetimeindex,也可以选择参数“on ”,但该列必须是类似 datetime 的。

作者图片

在使用选项“D”进行重采样后,数据变为每日数据,即所有日期都将被考虑在内。375717 条记录降采样为 77 条记录。

作者图片

其他规则选项

规则(代表目标转换)最常用的选项如下,其他选项也可以在参考文献[1]中找到:

作者图片

重采样选项用于两个选项,即上采样和下采样。

**上采样:**在这种情况下,我们对更短的时间范围进行重采样,例如将月度数据重采样为每周/每两周/每天等。因此,许多容器是用 NaN 值创建的,为了填充这些容器,有不同的方法可用作填充方法和填充方法。例如,将周数据更改为日数据并使用 bfill 方法会得到以下结果,因此 bfill 会在重新采样的数据中反向填充新的缺失值:

作者图片

另一种方法是填充法,它向前填充如下值:

作者图片

我们还可以在 upsamling 中使用 asfreq()或 fillna()方法。

**下采样:**在这种情况下,我们对更宽的时间范围进行重新采样,例如将每日数据重新采样为每周/每两周/每月等。为此,我们有 sum()、mean()、max()等选项。例如,每日数据被重新采样为月初数据,均值函数如下所示:

作者图片

重采样的图形表示

按照四种不同的规则(即每小时、每天、每周和每月)对数据进行重采样后,会获得以下图表。我们可以清楚地看到较短时间框架和较长时间框架之间的差异。在小时图中,噪声更多,并且从每天到每周到每月都在减少。根据研究目标,我们可以决定哪种规则是最好的。

作者图片

谢谢!

参考资料:

  1. https://pandas . pydata . org/pandas-docs/stable/user _ guide/time series . html
  2. https://pandas . pydata . org/pandas-docs/stable/reference/API/pandas。DataFrame.resample.html

从仪表板和 KPI 供应商那里抢救您的太阳能数据

原文:https://towardsdatascience.com/rescue-your-solar-data-from-hostage-taking-dashboard-and-kpi-vendors-9aafe8afbeed?source=collection_archive---------29-----------------------

控制数据是利用数据实现价值的第一步

图像通过 Canva.com 授权给约翰尼多宾斯。

简介

在过去的五年里,当我和我的团队为公用事业规模的太阳能分析开发我们所谓的“透明盒子”解决方案时,我注意到一种令人不安的数据保护主义模式,甚至是在该领域运营的流行仪表板和 kpi 供应商的直接劫持。

几乎每周,我都在帮助客户和潜在客户驾驭这个行业对充当数据湖提供商的“仪表板解决方案”的有毒依赖。随着太阳能数据分析领域在过去五年中的扩展,资产经理、性能工程师和数据科学家被迫使用“供应商 api”来获取自己的数据。

我将分析几点为什么这是不健康的;此外,我将为那些希望控制数据并开始为您的组织创造新价值的人分享一份路线图。

作为这些数据的消费者,我们如何以及为什么允许供应商这样做?

在我看到的每一个案例中,都是从数据收集设备或“数据记录器”开始的;这是安装在每个站点的物理硬件,连接到您的太阳能资产,收集数据,然后传输数据。通常,我们看到 Moxa IA240 和 UC81112 数据记录器这样做,但有许多类型。通常,太阳能运营商拥有这些设备。

图像通过 Canva.com 授权给约翰尼多宾斯。

当“仪表板和 kpi”供应商将设备配置为向/他们的/服务发送数据时,问题就出现了。那正是人质事件开始的时刻。

为什么这样不好?

  1. 你必须通过供应商获得 API 密钥和访问你的数据;这可能会有成本甚至速率/容量限制。
  2. 通常,您的供应商不会向与您合作的其他第三方承包商或供应商提供 API 密钥或访问权限。
  3. 他们掌握着数据,促使你在公用事业规模的太阳能报告和分析中使用通常不合格的解决方案。

绘制我们摆脱数据人质危机的路线图

作者图片

作为数据记录器的所有者,你有能力控制和制止这种滥用。

上面的图表是一个简单的例子,它将花费你每年 15,000 美元。它解放了你的数据。如果您仍然希望仪表板和 kpi 供应商完成他们的工作,他们可以从您的数据湖中轻松完成。

通过让数据记录器将数据传输到您的私有云和存储,您可以为您的组织创造大量价值:

  1. 通常,不同的站点计算 KPI 和报告的方式不同,不会采用供应商提供的一刀切的方法。
  2. 您可以真正超越监控,并利用当今太阳能领域免费提供的任意数量的开源解决方案和库。
  3. 您可以更轻松地利用更好的仪表板和真正的分析解决方案。

它是你的数据,你控制它;您可以轻松利用您的 SQL/REST 接口,随心所欲地使用它。

结论

当你委托新的太阳能站点时,坚持将数据记录器编程为向你自己的数据湖或历史学家提供数据。如果你不想处理拥有和管理你自己的,依靠流行的商业解决方案,如雪花或 OSISoft 的 PI 产品。最终,保持对数据的完全访问和控制是超越这些仪表板和 kpi 监控解决方案,为您的组织创造真正价值的唯一途径。

在我最近的文章中,我写了更多关于这个的内容,用你的能量数据 创造价值。

我在职业工程生涯中花了很多时间开发应用程序,帮助人们从他们的数据和内容中获得更多价值。

这方面的经验包括为佛罗里达州的 TBO.com 构建“防飓风”的新闻编辑室应用程序,为国家地理杂志构建“摄影比赛”应用程序,以及在过去的五年中,为运营商开发工具,帮助他们提高能源行业资产的性能和影响力。

*>>>*导入此

数据科学家的研究心态:“第一原则”思维

原文:https://towardsdatascience.com/research-mindset-for-data-scientist-the-first-principle-thinking-c7842bc70d93?source=collection_archive---------13-----------------------

思想和理论

HalGatewood.com 在 Unsplash 上的照片

“成为数据科学家需要博士学位吗”?这是许多有兴趣加入该领域的人经常问的问题。相当多的博客详细阐述了为什么成为数据科学家不需要博士学位(例如,这里有一个数据科学家),我认为这些通常是有道理的。尽管如此,博士毕业生在他们多年的研究经历中还会学到一些额外的东西:研究思维*。由于我在别处找不到一个正式的定义,我就简单地编造一下:*

研究思维模式是研究人员通常使用的思维模式或方法,用于从现有文献中综合关于问题空间的整体图景,提出有价值解决的具体问题,并确定创新的解决方案,以便我们在该领域的知识可以更进一步。

虽然博士经历是建立研究心态的一个好方法,主要是因为它的广泛培训和发表同行评议论文的压力,但我不认为这是唯一的途径:只要一个人有意识地发展这样的心态,他总是可以在各个领域/行业实践它。因此,在这里,我将分享一个研究思维模式:“第一原理”思维。请记住,研究心态并不是什么神奇的东西,可以把一个人变成解决问题的“超人”;它更多的是提供一致的观点来看待问题:这种观点可能是独特的,并可能导致更深刻的见解或创新的解决方案。对我来说,拥有研究心态无疑有助于我的数据科学家职业生涯。说到这里,现在,让我们开始吧。

定义“第一原则”思维

维基百科版本

根据维基百科,“第一原理是一个基本的命题或假设,不能从任何其他命题或假设中推导出来……在物理学和其他科学中,理论工作被称为来自第一原理,或从头开始,如果它直接从既定科学的水平开始,并且没有做出经验模型和参数拟合等假设”。

不痛不痒。Rainer Klute 重新着色并消除了一些小故障。—自己的作品基于:of Image:half Dan 的风格化锂 Atom.png。,CC BY-SA 3.0,https://commons.wikimedia.org/w/index.php?curid=1675352

例如,在计算化学(我的博士研究领域)中,如果一项研究想要从“第一原理”来主张,那么它的计算就应该基于量子力学:因为任何化学反应都可以被视为量子世界的一种表现形式。因此,该研究领域中的“第一原则”思想是使用薛定谔方程对原子核和电子进行建模,并将这种相互作用传播到化学系统,然后评估相关属性。在这种“第一原理”方法中,我们可以看到两个阶段:

  • (识别首先需要识别一个基本命题或假设,例如薛定谔方程
  • (应用)然后,需要将这样的命题或假设应用到系统中,以适当地研究所期望的,例如运行模拟来观察化学反应是如何发生的

数据科学友好版本

数据科学家通常在远离基础物理研究的行业工作,通常没有“薛定谔方程”可以回溯,所以在如何找到一个合适的“基本假设”上需要做一些调整。我认为一个好的“基本假设”应该具备三个属性:

首先,一个“基本假设”在其问题背景下应该是基本的/基本的。例如,如果问题环境是一个 B2B 公司的“预测订阅收入”,那么每个企业客户行为(例如订阅、追加销售、流失)可以被认为是基本的/基本的;如果问题的背景是“优化建造火箭的成本”,那么理解关键的火箭部件以及每个部件是如何制成的将被认为是基本的/根本的。在这里,与问题上下文相比,假设通常处于较低的级别,因此一旦确定了假设,就可以将其应用到较高的级别来帮助解决问题。

其次,一个“基本假设”需要在数学上可以量化,即使它可能不是对现实的最佳描述。例如,假设我们想要研究消费者对各种促销活动的反应行为,以最大化用户增长,有两种可能的假设:1)每个消费者都是理性的 2)每个消费者都是非理性的。基于经济研究,我们知道消费者是非理性的(链接、链接),他们的决定受如此多的因素影响,甚至可能不一致。然而,要对一个“非理性”的消费者进行数学建模几乎是不可能的,所以这样的“假设”,即使是正确的,也不一定能作为“基本假设”。与此同时,尽管“理性消费者”假设有许多已知的问题,但它可以用数学术语建模,因此通过一些修正来解释不同情况下的“非理性”行为,它可以成为一个更好的“基本假设”。

第三,一个“基本假设”在的语境下应该是可验证的。由于几乎没有任何命题或假设会永远成立,我们需要在数据科学领域的“第一原则”思维过程中增加一个额外的阶段:

  • (验证)使用数据来支持所选择的命题或假设,以验证其在给定的上下文中是否有效,以及在应用阶段是否被违反

因此,“识别- >验证- >应用三个阶段构成了数据科学世界中解决问题的“第一原理”思维。

将“首要原则”思想付诸行动

为了更好地说明“第一原则”的思维流程,我将分享一个数据科学项目作为案例研究,并声明此处陈述的是一个非常简化的版本,可能与现实略有不同。

背景:2016 年,当我在最大的打车公司(提示:名字以“U”开头)的风险团队工作时,欺诈行为在一些国际市场上屡见不鲜。由于船上竞争激烈,该公司向司机提供大量激励措施,吸引他们留在平台上。伴随着有利可图的激励,欺诈活动也随之而来。众所周知:骑手和驾驶员都可能是“幽灵”(凭空产生),其中“幽灵”骑手试图仅向“幽灵”驾驶员请求行程,并产生“幽灵”行程;在“幽灵”旅行结束后,“幽灵”骑手使用“现金”功能假装支付“幽灵”司机,稍后“幽灵”司机从平台拿走真正的奖金。欺诈者可以使用先进的 GPS 欺骗技术来启用上述模式(链接),要知道哪个行程被 GPS 欺骗总是一场猫和嘴的游戏。在下图中,我们可以看到一个 GPS 欺骗旅行的例子,其中旅行的 GPS 高度高于地面,因此旅行实际上是在空中进行的。

信用:https://eng . Uber . com/advanced-technologies-detecting-preventing-fraud-Uber/。假行程 GPS 为红色,正常行程 GPS 为绿色。

这里出现了一个问题:我们只知道在我们现有的检测技术范围内发生了多少次欺诈旅行,而不知道还有多少次。这是一种“我们不知道未知”的情况,也是我们想要解决的问题。所以我们想出了一个方法,我将用“第一原则”思维中的三个步骤来构建它:

第一步。识别:为了了解平台奖励欺诈的普遍性,每个差旅申请都有一个较低的级别,即一个差旅申请是否是欺诈的。为了成功地开始一次欺诈性的旅行,应该按照“幽灵”骑手的要求只派遣一名“幽灵”司机:他们需要相互勾结。如果按照“幽灵”骑手的请求意外派遣了正常的驾驶员,“幽灵”骑手肯定会 100%取消请求。虽然这种 100%的取消行为不会发生在正常的骑手身上,但鉴于骑手对于匹配的确切驾驶员大多是不可知的。描述“基本假设”的数学公式是

P(rg,dn) = 100%,P(rg,dg) = 0%

其中 P 表示骑手取消概率,“rg”表示“幽灵骑手”,“dg”表示“幽灵驾驶员”,“dn”表示“正常驾驶员”。

虽然对于一个正常的骑手,取消行为会有根本的不同,它应该是更加渐进的。为了简单起见,让我们假设骑手取消概率仅取决于调度时间时骑手-驾驶员的距离。例如,如果骑手看到驾驶员非常近(例如 100 米远),则骑手不太可能取消请求;但是如果被派遣的驾驶员仍然很远(例如 5 英里),如果有其他更近的驾驶员,则骑手可能会取消请求。数学公式可以描述为:

P(rn,dn |距离=X) = P(rn,dg |距离=X)

P(rn,d |距离=X) ≤ P(rn,d |距离=X+1)

其中“rn”代表“普通骑手”,而“d”代表任何驾驶员。

第二步。验证:我们可以查看历史旅行请求、派遣和取消数据,并确认我们对正常用户取消行为的理解。尽管由于许多因素(例如,骑手的期望)的不同,具体数字可能会有所不同,但基本原则仍然适用。

一条假设曲线,显示随着驾驶员-驾驶员距离的增加,驾驶员取消行为的变化

我们还可以在不存在司机激励的非欺诈市场上进行实验,以进一步证实正常乘客的取消行为与被派遣的特定司机无关的假设。这里的关键点是,这样的假设可以通过数据来验证。

第三步。应用:在我们经过识别和验证阶段后,可以通过在调度系统中引入一种处理方式来应用:在其他司机也靠近骑手的情况下,随机交换出排名靠前的司机进行调度。这样,即使没有派遣顶级驾驶员,骑手体验也不会受到显著影响。在这种情况下,我们可以推断出有多少欺诈性的旅行!

假设正常乘客取消率为 5%(未经处理),由于正常乘客的调度距离变化,估计取消率将增加到 6%,而实际观察到的取消率为 10%。如果 x 是市场中的欺诈请求%,则:

我们知道(未经处理):5% * x + 5% * (1-x) = 5%

实验表明(有处理):100% * x + 6% * (1-x) = 10%

那么我们可以计算:x =(10–6)/(100–6)= 4.2%

当然,这种方法仅适用于激励性很强的地区的一小部分(例如 1%)随机出行请求,目的是调查存在多少潜在的串通行为,以估计激励性欺诈的普遍程度,此处使用的数字仅用于演示目的。所提议的处理方法可能在哪些具体的欺诈行为可以被衡量方面也有一些限制,但是它作为“第一原则”思想如何工作的一个合理的示范。

针对正确问题的强力锤子

现在,您知道了什么是“第一原则”思想,以及如何在数据科学环境中使用它。恭喜你!

https://www.flickr.com/photos/ajay_suresh/49790892297

与此同时,我还想提醒你,“第一原则”思维永远不应该是你持有的唯一观点,在某些情况下,它甚至可能弊大于利:如果对一个原则钻得太深,它可能会不必要地使问题过于复杂,或者只是失去对大局的了解。它就像一把有力的锤子,但不一定每个问题都是钉子。所以不要把自己局限在任何单一的思维模式中,永远保持开放的心态,专注于解决问题,这将是最有效的途径。

— — — — — — — — — — — — — —

如果你喜欢这篇文章,通过点赞、分享和评论来帮助传播。可以看以前的帖子,关注我 LinkedIn

以下是之前三篇分享潘数据科学经验的文章:

我的第一个数据科学项目

数据科学如何创新

作为数据科学家解决问题:案例研究

使用 Pandas melt()重塑数据帧

原文:https://towardsdatascience.com/reshaping-a-dataframe-using-pandas-melt-83a151ce1907?source=collection_archive---------6-----------------------

使用 Pandas melt()将数据帧从宽格式更改为长格式的实践教程

Jonny Caspari 在 Unsplash 上的照片

当您处理包含具有某种序列的变量的数据集(例如,时序数据)时,通常需要进行整形。

来源于弗吉尼亚大学研究数据服务部[1]

我不久前发表了一篇文章,展示了处理新冠肺炎时间序列数据的一步一步的教程。在本教程中,主要任务之一是将数据从宽格式转换为长格式,以便于进一步的数据分析。Pandas melt()方法节省了我很多时间,而且只需要几行代码就可以完成任务。

重塑数据帧是数据科学中一项重要的基本技能。在本文中,我们将探索 Pandas melt()以及如何使用它进行数据处理。这篇文章的结构如下:

  1. 最简单的熔化
  2. 显示自定义名称
  3. 显示多个 id
  4. 指定要熔化的列
  5. 熊猫融化了
  6. 额外收获:重塑新冠肺炎时间序列数据

请查看笔记本获取源代码。更多教程可从 Github Repo 获得。

1.最简单的熔化

最简单的melt()不需要任何参数,它会将所有列转换为行(显示为列变量,并在新列中列出所有相关值。

# without any argument
df_wide**.melt()**

最简单的熊猫融化(图片由作者提供)

但是,这个输出往往没有太大意义,所以一般用例至少指定了id_vars参数。例如,id_vars='Country'会告诉熊猫保持国家为一列,其他所有列都变成行。

df_wide.melt(
    **id_vars='Country',**
)

最简单的熊猫融化(图片由作者提供)

注意现在的行数是 15,因为国家列中的每个值有 5 个值(3 X 5 = 15)。

2.显示自定义名称

默认情况下,**【变量】【值】**为列名。我们可以通过var_namevalue_name参数指定自定义名称:

df_wide.melt(
    id_vars='Country',
    **var_name='Date',**
    **value_name='Cases'**
)

熊猫与自定义名称融为一体(作者图片)

3.指定多个 id

melt()最有用的特性之一是我们可以指定多个 id 来将它们保存为列。例如,如果我们希望将国家纬度经度保留为列,以便更好地参考:

df_wide.melt(
    **id_vars=['Country', 'Lat', 'Long'],**
    var_name='Date',
    value_name='Cases'
)

熊猫融化与 id_vars(图片由作者提供)

4.指定要熔化的列

默认情况下,Pandas melt()函数会将所有其他列(除了在id_vars中指定的列)转换为行。在实际项目中,您可能只关心某些列,例如,如果我们只想看到 "24/01/2020""25/01/2020": 上的值

df_wide.melt(
    id_vars=['Country', 'Lat', 'Long'],
    **value_vars=["24/01//2020", "25/01//2020"],**
    var_name='Date',
    value_name='Cases'
)

熊猫融化与价值 _ vars(图片由作者提供)

5.熊猫融化了

我们也可以直接从 Pandas 模块调用melt()而不是 DataFrame。然而,这些是相同的。

从熊猫模块调用融化(图片由作者提供)

6.额外收获:重塑新冠肺炎时间序列数据

利用我们目前所学的知识,让我们来看看一个现实世界的问题:可从约翰·霍普金斯大学 CSSE Github 获得的新冠肺炎时间序列数据。

(图片由作者提供)

有两个问题:

  • 被保存在不同的 CSV 文件中。将它们绘制在一张图中并不简单。
  • 日期显示为列名,并且难以执行日间计算,例如,计算每日新增病例、新增死亡病例和新增康复病例。

让我们重塑 3 个数据集,并将它们合并成一个数据帧。

6.1 加载数据集

首先,让我们加载数据集:

***confirmed_df = pd
    .read_csv('time_series_covid19_confirmed_global.csv')deaths_df = pd
    .read_csv('time_series_covid19_deaths_global.csv')recovered_df = pd
    .read_csv('time_series_covid19_recovered_global.csv')***

6.2 将它们从宽格式重塑为长格式

通过运行confirmed_df.columnsdeaths_df.columnsrecovered_df.columns,它们都应该输出如下相同的结果:

(图片由作者提供)

请注意,从第 4 列开始,所有列都是日期,以获取日期列表confirmed_df.columns[4:]

(图片由作者提供)

在合并之前,我们需要使用melt()将数据帧从当前的宽格式转换为长格式。换句话说,我们把所有的日期列都转换成了值。以下是相关的主要设置:

  • 使用‘Province/State’‘Country/Region’‘Lat’‘Long’作为标识符变量。我们稍后将使用它们进行合并。
  • 用变量列‘Date’和值列 ‘Confirmed’取消透视日期列(如我们之前看到的columns[4:]
***confirmed_df_long = confirmed_df.melt(
    **id_vars=['Province/State', 'Country/Region', 'Lat', 'Long'],** 
    **value_vars=dates,** 
    **var_name='Date',** 
    **value_name='Confirmed'**
)deaths_df_long = deaths_df.melt(
    **id_vars=['Province/State', 'Country/Region', 'Lat', 'Long'],** 
    **value_vars=dates, 
    var_name='Date', 
    value_name='Deaths'**
)recovered_df_long = recovered_df.melt(
    **id_vars=['Province/State', 'Country/Region', 'Lat', 'Long'], 
    value_vars=dates, 
    var_name='Date', 
    value_name='Recovered'**
)***

所有结果都是新的长格式。它们都是按 日期国家/地区 排序的,因为原始数据已经按 国家/地区 排序,日期列已经是 ASC 顺序。

下面是confirmed_df_long的例子

confirmed_df_long 示例(图片由作者提供)

6.3 合并确认、死亡和恢复

最后,我们使用merge()一个接一个地合并 3 个数据帧:

**# Merging **confirmed_df_long** and **deaths_df_long**
full_table = **confirmed_df_long**.merge(
  right=**deaths_df_long**, 
  how='left',
 **on=['Province/State', 'Country/Region', 'Date', 'Lat', 'Long']** )# Merging **full_table** and **recovered_df_long**
full_table = **full_table**.merge(
  right=**recovered_df_long**, 
  how='left',
 **on=['Province/State', 'Country/Region', 'Date', 'Lat', 'Long']** )**

现在,我们应该得到一个包含ConfirmedDeathsRecovered列的完整表格:

full _ 表格,包含已确认、死亡和已康复的患者(图片由作者提供)

如果您想了解更多关于创建活动**、新确认新死亡新恢复的信息,请查看这篇文章:**

**

结论

在本文中,我们讨论了 5 个用例以及 1 个真实的例子,它们都是关于使用 Pandas melt()方法将数据帧从宽格式重塑为长格式的。它非常方便,是数据预处理和探索性数据分析中最受欢迎的方法之一。

重塑数据是数据科学中一项重要的基本技能。我希望你喜欢这篇文章,并学到一些新的有用的东西。

感谢阅读。请查看笔记本获取源代码,如果您对机器学习的实用方面感兴趣,请继续关注。更多教程可从 Github Repo 获得。

参考

  • [1]弗吉尼亚大学:研究数据服务+科学**

用熊猫重塑数据

原文:https://towardsdatascience.com/reshaping-data-with-pandas-ecff92572488?source=collection_archive---------25-----------------------

在分析数据时,我们可能需要重塑表格数据。Pandas 有两种方法可以帮助将数据重新整形为所需的格式。

米利安·耶西耶在 Unsplash 上拍摄的照片

Pandas 有两种方法来重塑数据,即**()()**。这些方法分别类似于 R 中“tidyr”包的 gather()和 spread()函数。我们将考虑公司报告格式的资产负债表。资产负债表存储在名为“df”的 pandas 数据框架中。

作者图片

融化()

此方法将表格数据平面化/融合,以便将指定的列及其各自的值转换为键值对。“键”是转换前数据集的列名,“值”是相应列中的值。默认情况下,转换后,“键”存储在名为“变量”的列中,“值”存储在另一个名为“值”的列中。

这可能看起来有点混乱,开始时我也遇到过。然而,一旦我们看一个例子,事情就变得简单了。让我们把之前看到的资产负债表拉平/融化。最初,我们将调用带有默认参数的 melt()方法。

作者图片

如前所述,数据帧的列被转换成键值对。这种格式可能没有用,所以,让我们看一个例子,其中“细节”列保持不变,相关的年度数据作为键值对出现在相邻的列中。在这种情况下,“细节”列称为“id”列,应该传递给 melt()方法的“id_vars”参数。

作者图片

除了“细节”列(传递给“id_vars”参数)之外,所有其他列都被转换为键值对。我们还可以指定一个列列表,将这些列视为“id”列。我们可以使用' var_name '和' value_name '参数重命名' value '和' variable '列,如下所示。

作者图片

当我们在 Excel 中分析数据时,这种格式会很有用。这种格式使得在 Excel 中构建数据透视表更加容易。

透视()

这个方法与 melt()所做的正好相反。它将键值对转换成列。让我们看一个将“df_melt”(如下所示)转换回原始格式的例子。

df_melt(图片由作者提供)

作者图片

melt()和 pivot()是将表格数据转换成所需格式时很方便的两种方法。melt()将列转换为键值对,而 pivot()将键值对转换为列。

残差、瓶颈、反向残差、线性瓶颈、MBConv 解释

原文:https://towardsdatascience.com/residual-bottleneck-inverted-residual-linear-bottleneck-mbconv-explained-89d7b7e7c6bc?source=collection_archive---------6-----------------------

py torch 中的那些+实现是什么鬼

嘿,我在LinkedIn过来打个招呼👋

这里有一个互动版本可用

所有这些模块都已经在我的 眼镜 库中实现了

在现代深度学习中记录名字很难。今天我们将看到现代 CNN 架构中使用的不同模块,如 ResNet、MobileNet、EfficientNet,以及它们在 PyTorch 中的实现!

在我们做任何事情之前,让我们创建一个通用的 conv-规范-行为层

torch.Size([1, 64, 56, 56])

剩余连接

中提出的 ResNet 中使用了残差连接,用于图像识别 的深度残差学习,让我们也引用 Schmidhuber 实验室在 高速公路网 上的工作。这个想法是将你的输入添加到你的输出中。下面的图片可以帮助你把它形象化。但是,我的意思是它只是一个+操作符。残差运算提高了梯度跨乘数层传播的能力,从而允许有效地训练超过 100 层的网络。

作者图片

在 PyTorch 中,我们可以很容易地创建一个ResidualAdd

捷径

有时你的残差没有相同的输出维数,所以我们不能把它们相加。我们可以使用快捷方式中的 conv(带+的黑色箭头)来投影输入,以匹配输出的特征

瓶颈块

中还引入了瓶颈块,用于图像识别的深度残差学习 。瓶颈模块接受大小为BxCxHxW的输入,它首先使用廉价的1x1 conv将其减少到BxC/rxHxW,然后应用3x3 conv,最后再次使用1x1 conv将输出重新映射到与输入相同的特征维度BxCxHxW。这比用三个3x3 convs要快。由于投入首先减少,这就是我们称之为“瓶颈”的原因。下图显示了模块,我们在最初的实现中使用了r=4

作者图片

前两次转换之后是 batchnorm 和非线性激活,而最后一次非线性是在加法之后应用的。在 PyTorch

注意,只有当输入和输出特性不同时,我们才应用shortcut

实际上,当我们希望降低空间维度时,在中间卷积中使用stride=2

线性瓶颈

在 MobileNetV2 中引入了线性瓶颈:反向残差和线性瓶颈。线性瓶颈块是没有最后激活的瓶颈块。在论文的第 3.2 节中,他们详细讨论了为什么在输出之前存在非线性会损害性能。简而言之,非线性函数 line ReLU 会将所有内容<设置为 0,从而破坏信息。他们根据经验证明,当输入通道少于输出通道时,情况确实如此。所以,去掉瓶颈中的nn.ReLU,你就有了。

反向残差

在 MobileNetV2:反向残差和线性瓶颈中再次引入了反向残差。反向残差块是反向瓶颈层。他们使用第一个 conv 扩展功能,而不是减少功能。下面的图像应该可以清楚地说明这一点

作者图片

所以我们从BxCxHxW到->BxCexHxW->->BxCxHxW,其中e膨胀比并设置为4。它们不是像正常瓶颈阻塞那样变宽- >变窄- >变宽,而是相反,变窄- >变宽- >变窄。在 PyTorch 中,这是微不足道的

MobileNet中,剩余连接仅在输入和输出特征匹配时应用,不要问我为什么,如果你知道,请评论:)所以你应该这样做

MBConv

所以在 MobileNetV2 之后,它的构建模块被称为MBConvMBConv是具有深度方向可分离卷积的反向线性瓶颈层。

深度方向可分离卷积

深度方向可分离卷积采用一种技巧,将一个正常的 3×3 conv 分成两个卷积,以减少参数的数量。第一种方法将单个 3×3 滤波器应用于每个输入通道,另一种方法将 1×1 滤波器应用于所有通道。如果你做你的比赛,这和做一个正常的 3x3 conv 是一样的,但是你保存参数。

这也有点愚蠢,因为在我们现有的硬件上,它比普通的 3x3 要慢得多。

下图展示了这个想法

作者图片

通道中的不同颜色表示每个通道应用一个单独的滤镜

在 PyTorch:

第一个卷积通常称为depth,而第二个卷积称为point。让我们数一数参数

sum(p.numel() for p in DepthWiseSeparableConv(32, 64).parameters() if p.requires_grad) 

出局:2432

让我们来看一个普通的 Conv2d

sum(p.numel() for p in nn.Conv2d(32, 64, kernel_size=3).parameters() if p.requires_grad)

出局:18496

这是一个很大的区别

获取 MBConv

让我们创建一个完整的 MBConv。有几个 MBConv 的重要细节,归一化适用于深度和点卷积,非线性仅适用于深度卷积(记住线性瓶颈)。应用的 ReLU6 非线性。把所有东西放在一起

在 EfficientNet 中使用了该模块的稍加修改的版本,带有挤压和激励。

融合反转残差(融合 MBConv)

融合反向残差在 EfficientNetV2:更小的模型和更快的训练中引入,以使 MBConv 更快。所以基本上,由于深度方向的回旋很慢,他们将第一个和第二个 conv 融合成一个 3×3 的 conv(3.2 节)。

作者图片

结论

现在你应该知道所有这些块之间的区别和它们背后的推理了!我强烈推荐阅读与他们相关的论文,你不会错的。

关于 ResNet 更详细的指南,请查看剩余网络:在 Pytorch 中实现 ResNet

从零开始的残差网络在计算机视觉中的应用

原文:https://towardsdatascience.com/residual-networks-in-computer-vision-ee118d3be68f?source=collection_archive---------24-----------------------

使用 Tensorflow 和 Keras 的深度学习应用

1.介绍

深度卷积神经网络显著改变了图像分类的研究前景【1】。随着层次的增加,模型的表现力也增加了;它能够学习更复杂的表达。在某些时候,网络的深度和模型的准确性之间似乎存在正相关关系。当网络越来越深入时,消失/爆炸梯度问题变得越来越严重。规范化的初始化和中间规范化层最终解决了这个问题,深层网络开始融合。然而,随着深度的增加,模型的准确性开始饱和,然后实际上迅速下降,而不是在随后的实验之前的直觉推理。这不是由于过度拟合,而是由于当前用于优化模型的求解器的局限性【2】。

退化问题通过引入剩余网络【2】得到部分解决。它引入了一种系统的方法来使用快捷连接,即跳过一层或多层的连接。这些短连接只是执行身份映射,它们的输出被添加到堆叠层的输出中(这不会增加额外的参数或计算复杂度)。其背后的想法是,如果多个非线性层可以渐近地逼近复杂的函数(仍在理论上研究,但这是深度学习的基础),那么剩余函数也可能发生同样的情况。这样做的好处是,同时简化了求解者的工作。在【3】中研究了其他类型的连接,如缩放、门控和 1x1 卷积的跳跃连接;然而,身份映射继续产生最低的训练损失。

我们的任务是对一系列带标签的图像进行分类。我们想比较两种不同方法的准确性;第一个是经典卷积神经网络,第二个是残差网络。我们的目标是展示残余网络的力量,即使是在不太深的环境中。这是在解决退化问题的同时帮助优化过程的好方法。我们对残差网络进行了经验测试,它更容易过度拟合。为了解决这个问题,我们通过使用数据扩充策略来综合扩充我们的数据集。

我们再次使用辛普森一家的角色数据集【4】。我们对数据集进行了过滤,仅包含包含 100 张以上图像的类(字符)。在训练、验证和测试数据集之间进行拆分后,数据集的最终大小如下:12411 幅图像用于训练,3091 幅图像用于验证,950 幅图像用于测试。

和往常一样,代码和数据也可以在我的 GitHub 上获得。

本文属于使用 TensorFlow 进行深度学习的系列文章:

  • 迁移学习和数据增强应用于辛普森图像数据集
  • 基于 F. Pessoa 的工作用递归神经网络生成文本
  • 使用 Seq2Seq 架构和注意力的神经机器翻译(ENG to POR)
  • 残差网络从无到有应用于计算机视觉

2.数据预处理

我们创建生成器来将我们的数据提供给模型。我们还应用一个转换来规范化数据,在训练和验证数据集之间分割它们,并定义一个 32 的批量大小(参见【5】以更好地理解预处理和生成器)。

import tensorflow as tf
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Layer, BatchNormalization, Conv2D, Dense, Flatten, Add, Dropout, BatchNormalization
import numpy as np
from tensorflow.keras.datasets import fashion_mnist
from tensorflow.keras.utils import to_categorical
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import os
from tensorflow.keras import Input, layers
from tensorflow.keras.layers import Dense, Flatten, Conv2D, MaxPooling2D
import timedirectory_train = "./simpsons_data_split/train/"
directory_test = "./simpsons_data_split/test/"def get_ImageDataGenerator(validation_split=None):
    image_generator = ImageDataGenerator(rescale=(1/255.),
                                         validation_split=validation_split)
    return image_generator

image_gen_train = get_ImageDataGenerator(validation_split=0.2)

def get_generator(image_data_generator, directory, train_valid=None, seed=None):
    train_generator = image_data_generator.flow_from_directory(directory, 
                                                               batch_size=32, 
                                                               class_mode='categorical', 
                                                               target_size=(128,128), 
                                                               subset=train_valid, 
                                                               seed=seed)    
    return train_generator

train_generator = get_generator(image_gen_train, directory_train, train_valid='training', seed=1)
validation_generator = get_generator(image_gen_train, directory_train, train_valid='validation')Found 12411 images belonging to 19 classes.
Found 3091 images belonging to 19 classes.

我们还创建了一个膨胀的数据集,通过应用一组几何和光度变换来减少过度拟合的可能性。几何变换改变了图像的几何形状,使得 CNN 不会因位置和方向的改变而改变。光度变换通过调整图像的颜色通道,使 CNN 不受颜色和光照变化的影响。

def get_ImageDataGenerator_augmented(validation_split=None):
    image_generator = ImageDataGenerator(rescale=(1/255.),
                                        rotation_range=40,
                                        width_shift_range=0.2,
                                        height_shift_range=0.2,
                                        shear_range=0.2,
                                        zoom_range=0.1,
                                        brightness_range=[0.8,1.2],
                                        horizontal_flip=True,
                                        validation_split=validation_split)
    return image_generator
image_gen_train_aug = get_ImageDataGenerator_augmented(validation_split=0.2)
train_generator_aug = get_generator(image_gen_train_aug, directory_train, train_valid='training', seed=1)
validation_generator_aug = get_generator(image_gen_train_aug, directory_train, train_valid='validation')Found 12411 images belonging to 19 classes.
Found 3091 images belonging to 19 classes.

我们可以遍历生成器来获得一组图像,其大小等于上面定义的批处理大小。

target_labels = next(os.walk(directory_train))[1]

target_labels.sort()

batch = next(train_generator)
batch_images = np.array(batch[0])
batch_labels = np.array(batch[1])

target_labels = np.asarray(target_labels)

plt.figure(figsize=(15,10))
for n, i in enumerate(np.arange(10)):
    ax = plt.subplot(3,5,n+1)
    plt.imshow(batch_images[i])
    plt.title(target_labels[np.where(batch_labels[i]==1)[0][0]])
    plt.axis('off')

图 1:由训练生成器生成的一组图像。

3.基准模型

我们定义一个简单的 CNN 作为基准模型。它使用 2D 卷积层(对图像执行空间卷积)和最大池操作。它们之后是具有 128 个单元和 ReLU 激活功能的密集层,以及速率为 0.5 的下降层。最后,最后一层产生我们的网络的输出,其单元数量等于目标标签的数量,并使用 softmax 激活函数。该模型是用 Adam 优化器编译的,具有默认设置和分类交叉熵损失。

def get_benchmark_model(input_shape):
    x = Input(shape=input_shape)
    h = Conv2D(32, padding='same', kernel_size=(3,3), activation='relu')(x)
    h = Conv2D(32, padding='same', kernel_size=(3,3), activation='relu')(x)
    h = MaxPooling2D(pool_size=(2,2))(h)
    h = Conv2D(64, padding='same', kernel_size=(3,3), activation='relu')(h)
    h = Conv2D(64, padding='same', kernel_size=(3,3), activation='relu')(h)
    h = MaxPooling2D(pool_size=(2,2))(h)
    h = Conv2D(128, kernel_size=(3,3), activation='relu')(h)
    h = Conv2D(128, kernel_size=(3,3), activation='relu')(h)
    h = MaxPooling2D(pool_size=(2,2))(h)
    h = Flatten()(h)
    h = Dense(128, activation='relu')(h)
    h = Dropout(.5)(h)
    output = Dense(target_labels.shape[0], activation='softmax')(h)

    model = tf.keras.Model(inputs=x, outputs=output)

    model.compile(optimizer='adam',
             loss='categorical_crossentropy',
             metrics=['accuracy'])
    return modelbenchmark_model = get_benchmark_model((128, 128, 3))
benchmark_model.summary()Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 128, 128, 3)]     0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 128, 128, 32)      896       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 64, 64, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 64, 64, 64)        18496     
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 64, 64, 64)        36928     
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 32, 32, 64)        0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 30, 30, 128)       73856     
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 28, 28, 128)       147584    
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 14, 14, 128)       0         
_________________________________________________________________
flatten (Flatten)            (None, 25088)             0         
_________________________________________________________________
dense (Dense)                (None, 128)               3211392   
_________________________________________________________________
dropout (Dropout)            (None, 128)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 19)                2451      
=================================================================
Total params: 3,491,603
Trainable params: 3,491,603
Non-trainable params: 0
_________________________________________________________________def train_model(model, train_gen, valid_gen, epochs):
    train_steps_per_epoch = train_gen.n // train_gen.batch_size
    val_steps = valid_gen.n // valid_gen.batch_size

    earlystopping = tf.keras.callbacks.EarlyStopping(patience=3)
    history = model.fit(train_gen, 
                        steps_per_epoch = train_steps_per_epoch,
                        epochs=epochs,
                        validation_data=valid_gen, 
                        callbacks=[earlystopping])

    return historytrain_generator = get_generator(image_gen_train, directory_train, train_valid='training')
validation_generator = get_generator(image_gen_train, directory_train, train_valid='validation')
history_benchmark = train_model(benchmark_model, train_generator, validation_generator, 50)Found 12411 images belonging to 19 classes.
Found 3091 images belonging to 19 classes.
Epoch 1/50
387/387 [==============================] - 139s 357ms/step - loss: 2.7674 - accuracy: 0.1370 - val_loss: 2.1717 - val_accuracy: 0.3488
Epoch 2/50
387/387 [==============================] - 136s 352ms/step - loss: 2.0837 - accuracy: 0.3757 - val_loss: 1.7546 - val_accuracy: 0.4940
Epoch 3/50
387/387 [==============================] - 130s 335ms/step - loss: 1.5967 - accuracy: 0.5139 - val_loss: 1.3483 - val_accuracy: 0.6102
Epoch 4/50
387/387 [==============================] - 130s 335ms/step - loss: 1.1952 - accuracy: 0.6348 - val_loss: 1.1623 - val_accuracy: 0.6619
Epoch 5/50
387/387 [==============================] - 130s 337ms/step - loss: 0.9164 - accuracy: 0.7212 - val_loss: 1.0813 - val_accuracy: 0.6907
Epoch 6/50
387/387 [==============================] - 130s 336ms/step - loss: 0.7270 - accuracy: 0.7802 - val_loss: 1.0241 - val_accuracy: 0.7240
Epoch 7/50
387/387 [==============================] - 130s 336ms/step - loss: 0.5641 - accuracy: 0.8217 - val_loss: 0.9674 - val_accuracy: 0.7438
Epoch 8/50
387/387 [==============================] - 130s 336ms/step - loss: 0.4496 - accuracy: 0.8592 - val_loss: 1.0701 - val_accuracy: 0.7441
Epoch 9/50
387/387 [==============================] - 130s 336ms/step - loss: 0.3677 - accuracy: 0.8758 - val_loss: 0.9796 - val_accuracy: 0.7645
Epoch 10/50
387/387 [==============================] - 130s 336ms/step - loss: 0.3041 - accuracy: 0.8983 - val_loss: 1.0681 - val_accuracy: 0.7561

4.剩余网络

深层残差网络由许多叠加的残差单元组成,可以定义为:

其中 x_ lx_ {l+1}为第 l 个单元的输入输出, F 为剩余函数, h(x_l) 为恒等式映射, f 为激活函数。 W_t 是与第 l 个残差单元相关联的一组权重(和偏差)。【2】提出的层数为 2 或 3。我们将 F 定义为两个 3×3 卷积层的堆栈。在【2】f中,一个 ReLU 函数在逐元素加法后被应用。我们遵循了后来提出的【3】架构,其中 f 只是一个身份映射。在这种情况下,

我们可以写作,

或者更一般地说,

对于任何较深的单元 L 和较浅的单元 l 。该功能

对于任何深度单元, L 是所有在前剩余函数的输出加上 x_0 的总和。

就优化过程而言,反向传播属性给出了关于为什么这种类型的连接有助于优化过程的一些直觉。我们可以把它写成:

其中 L 是损失函数。注意这个梯度

直接传播信息,而术语

通过权重层传播。可以看出,利用这种形式,即使当权重任意小时,层的梯度也不会消失。

4.1 剩余单位

我们使用层子类化来构建剩余单元。自定义图层类有三种方法:__init__buildcall__init__方法用定义的关键字参数调用基本的Layer类初始化器。build方法创建层。在我们的例子中,我们定义了两组BatchNormalization,后面是一个Conv2D层,最后一个使用与层输入相同数量的过滤器。call方法通过各层处理输入。在我们的例子中,我们有以下顺序:第一个BatchNormalization,ReLu 激活函数,第一个Conv2D,第二个BatchNormalization,另一个 ReLu 激活函数,第二个Conv2D。最后,我们将输入添加到第二个Conv2D层的输出,这可以看作是执行身份映射的短连接的实现。

class ResidualUnit(Layer):
    def __init__(self, **kwargs):
        super(ResidualUnit, self).__init__(**kwargs)

    def build(self, input_shape):
        self.bn_1 = tf.keras.layers.BatchNormalization(input_shape=input_shape)
        self.conv2d_1 = tf.keras.layers.Conv2D(input_shape[3], (3, 3), padding='same')
        self.bn_2 = tf.keras.layers.BatchNormalization()
        self.conv2d_2 = tf.keras.layers.Conv2D(input_shape[3], (3, 3), padding='same')

    def call(self, inputs, training=False):
        x = self.bn_1(inputs, training)
        x = tf.nn.relu(x)
        x = self.conv2d_1(x)
        x = self.bn_2(x, training)
        x = tf.nn.relu(x)
        x = self.conv2d_2(x)
        x = tf.keras.layers.add([inputs, x])
        return xtest_model = tf.keras.Sequential([ResidualUnit(input_shape=(128, 128, 3), name="residual_unit")])
test_model.summary()Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
residual_unit (ResidualUnit) (None, 128, 128, 3)       192       
=================================================================
Total params: 192
Trainable params: 180
Non-trainable params: 12
_________________________________________________________________

4.2 尺寸增加剩余单元

在【2】提出的架构中,有增加维度的剩余单元。这通过使用直线投影 W_s 通过快捷连接来实现,以匹配所需尺寸:

在这种情况下,它是由 1x1 卷积层完成的。

class FiltersChangeResidualUnit(Layer):

    def __init__(self, out_filters, **kwargs):
        super(FiltersChangeResidualUnit, self).__init__(**kwargs)
        self.out_filters = out_filters

    def build(self, input_shape):
        number_filters = input_shape[0]
        self.bn_1 = tf.keras.layers.BatchNormalization(input_shape=input_shape)
        self.conv2d_1 = tf.keras.layers.Conv2D(input_shape[3], (3, 3), padding='same')
        self.bn_2 = tf.keras.layers.BatchNormalization()
        self.conv2d_2 = tf.keras.layers.Conv2D(self.out_filters, (3, 3), padding='same')
        self.conv2d_3 = tf.keras.layers.Conv2D(self.out_filters, (1, 1))

    def call(self, inputs, training=False):
        x = self.bn_1(inputs, training)
        x = tf.nn.relu(x)
        x = self.conv2d_1(x)
        x = self.bn_2(x, training)
        x = tf.nn.relu(x)
        x = self.conv2d_2(x)
        x_1 = self.conv2d_3(inputs)
        x = tf.keras.layers.add([x, x_1])
        return xtest_model = tf.keras.Sequential([FiltersChangeResidualUnit(16, input_shape=(32, 32, 3), name="fc_resnet_unit")])
test_model.summary()Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
fc_resnet_unit (FiltersChang (None, 32, 32, 16)        620       
=================================================================
Total params: 620
Trainable params: 608
Non-trainable params: 12
_________________________________________________________________

4.3 模型

最后,我们可以建立完整的模型。我们首先定义一个有 32 个过滤器的Conv2D层,一个 7x7 内核,步距为 2。在第一层之后,我们添加我们的剩余单元。然后,我们添加一个新的Conv2D层,有 32 个滤镜,一个 3x3 内核,步幅为 2。之后,我们添加了我们的剩余单元,它允许以 64 的输出改变尺寸。为了最终确定我们的模型,我们将数据展平,并通过 softmax 激活函数和与类数量相同的单元数量将其馈送到一个Dense层。

class ResNetModel(Model):

    def __init__(self, **kwargs):
        super(ResNetModel, self).__init__()
        self.conv2d_1 = tf.keras.layers.Conv2D(32, (7, 7), strides=(2,2))
        self.resb = ResidualUnit()
        self.conv2d_2 = tf.keras.layers.Conv2D(32, (3, 3), strides=(2,2))
        self.filtersresb = FiltersChangeResidualUnit(64)
        self.flatten_1 = tf.keras.layers.Flatten()
        self.dense_o = tf.keras.layers.Dense(target_labels.shape[0], activation='softmax')   

    def call(self, inputs, training=False):
        x = self.conv2d_1(inputs)
        x = self.resb(x, training)
        x = self.conv2d_2(x)
        x = self.filtersresb(x, training)
        x = self.flatten_1(x)
        x = self.dense_o(x)

        return xresnet_model = ResNetModel()
resnet_model(inputs= tf.random.normal((32, 128,128,3)))
resnet_model.summary()Model: "res_net_model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_6 (Conv2D)            multiple                  4736      
_________________________________________________________________
residual_unit (ResidualUnit) multiple                  18752     
_________________________________________________________________
conv2d_7 (Conv2D)            multiple                  9248      
_________________________________________________________________
filters_change_residual_unit multiple                  30112     
_________________________________________________________________
flatten_1 (Flatten)          multiple                  0         
_________________________________________________________________
dense_2 (Dense)              multiple                  1094419   
=================================================================
Total params: 1,157,267
Trainable params: 1,157,011
Non-trainable params: 256
_________________________________________________________________optimizer_obj = tf.keras.optimizers.Adam(learning_rate=0.001)
loss_obj = tf.keras.losses.CategoricalCrossentropy()@tf.function
def grad(model, inputs, targets, loss):
    with tf.GradientTape() as tape:
        preds = model(inputs)
        loss_value = loss(targets, preds)
    return loss_value, tape.gradient(loss_value, model.trainable_variables)def train_resnet(model, num_epochs, dataset, valid_dataset, optimizer, loss, grad_fn):

    train_steps_per_epoch = dataset.n // dataset.batch_size
    train_steps_per_epoch_valid = valid_dataset.n // valid_dataset.batch_size

    train_loss_results = []
    train_accuracy_results = []
    train_loss_results_valid = []
    train_accuracy_results_valid = []
    for epoch in range(num_epochs):
        start = time.time()
        epoch_loss_avg = tf.keras.metrics.Mean()
        epoch_accuracy = tf.keras.metrics.CategoricalAccuracy()
        epoch_loss_avg_valid = tf.keras.metrics.Mean()
        epoch_accuracy_valid = tf.keras.metrics.CategoricalAccuracy()
        i=0
        for x, y in dataset:
            loss_value, grads = grad_fn(model, x, y, loss)
            optimizer.apply_gradients(zip(grads, model.trainable_variables))

            epoch_loss_avg(loss_value)
            epoch_accuracy(y, model(x))
            if i>=train_steps_per_epoch:
                break
            i+=1
        j = 0
        for x, y in valid_dataset:
            model_output = model(x)
            epoch_loss_avg_valid(loss_obj(y, model_output))  
            epoch_accuracy_valid(y, model_output)
            if j>=train_steps_per_epoch_valid:
                break
            j+=1

        # End epoch
        train_loss_results.append(epoch_loss_avg.result())
        train_accuracy_results.append(epoch_accuracy.result())
        train_loss_results_valid.append(epoch_loss_avg_valid.result())
        train_accuracy_results_valid.append(epoch_accuracy_valid.result())

        print("Training -> Epoch {:03d}: Loss: {:.3f}, Accuracy: {:.3%}".format(epoch,
                                                                   epoch_loss_avg.result(),
                                                                   epoch_accuracy.result()))
        print("Validation -> Epoch {:03d}: Loss: {:.3f}, Accuracy: {:.3%}".format(epoch,
                                                           epoch_loss_avg_valid.result(),
                                                           epoch_accuracy_valid.result()))
        print(f'Time taken for 1 epoch {time.time()-start:.2f} sec\n')

    return train_loss_results, train_accuracy_resultstrain_loss_results, train_accuracy_results = train_resnet(resnet_model, 
                                                          40, 
                                                          train_generator_aug,
                                                          validation_generator_aug,
                                                          optimizer_obj, 
                                                          loss_obj, 
                                                          grad)Training -> Epoch 000: Loss: 2.654, Accuracy: 27.153%
Validation -> Epoch 000: Loss: 2.532, Accuracy: 23.488%
Time taken for 1 epoch 137.62 sec

[...]

Training -> Epoch 039: Loss: 0.749, Accuracy: 85.174%
Validation -> Epoch 039: Loss: 0.993, Accuracy: 75.218%
Time taken for 1 epoch 137.56 sec

5.结果

残差网络在测试集上显示出更好的准确性,与基准模型的 75.6%相比,接近 81%的准确性。我们可以很容易地使剩余模型更深入,这是我们可以从这个架构中提取最大价值的场景。

fig, axes = plt.subplots(1, 2, sharex=True, figsize=(12, 5))

axes[0].set_xlabel("Epochs", fontsize=14)
axes[0].set_ylabel("Loss", fontsize=14)
axes[0].set_title('Loss vs epochs')
axes[0].plot(train_loss_results)

axes[1].set_title('Accuracy vs epochs')
axes[1].set_ylabel("Accuracy", fontsize=14)
axes[1].set_xlabel("Epochs", fontsize=14)
axes[1].plot(train_accuracy_results)
plt.show()

图 2:残差网络几个时期的训练精度和损失演变。

def test_model(model, test_generator):

    epoch_loss_avg = tf.keras.metrics.Mean()
    epoch_accuracy = tf.keras.metrics.CategoricalAccuracy()

    train_steps_per_epoch = test_generator.n // test_generator.batch_size
    i = 0
    for x, y in test_generator:
        model_output = model(x)
        epoch_loss_avg(loss_obj(y, model_output))  
        epoch_accuracy(y, model_output)
        if i>=train_steps_per_epoch:
            break
        i+=1

    print("Test loss: {:.3f}".format(epoch_loss_avg.result().numpy()))
    print("Test accuracy: {:.3%}".format(epoch_accuracy.result().numpy()))print('ResNet Model')
test_model(resnet_model, validation_generator)
print('Benchmark Model')
test_model(benchmark_model, validation_generator)ResNet Model
Test loss: 0.787
Test accuracy: 80.945%
Benchmark Model
Test loss: 1.067
Test accuracy: 75.607%num_test_images = validation_generator.n

random_test_images, random_test_labels = next(validation_generator)

predictions = resnet_model(random_test_images)

fig, axes = plt.subplots(4, 2, figsize=(25, 12))
fig.subplots_adjust(hspace=0.5, wspace=-0.35)

j=0
for i, (prediction, image, label) in enumerate(zip(predictions, random_test_images, target_labels[(tf.argmax(random_test_labels, axis=1).numpy())])):
    if j >3:
        break
    axes[i, 0].imshow(np.squeeze(image))
    axes[i, 0].get_xaxis().set_visible(False)
    axes[i, 0].get_yaxis().set_visible(False)
    axes[i, 0].text(5., -7., f'Class {label}')
    axes[i, 1].bar(np.arange(len(prediction)), prediction)
    axes[i, 1].set_xticks(np.arange(len(prediction)))
    axes[i, 1].set_xticklabels([l.split('_')[0] for l in target_labels], rotation=0)
    pred_inx = np.argmax(prediction)
    axes[i, 1].set_title(f"Categorical distribution. Model prediction: {target_labels[pred_inx]}")
    j+=1
plt.show()

图 3:随机图像(左侧)和残差网络产生的预测的相应分类分布(右侧*)。*

6.结论

残余网络引入了一种系统的方法,通过简单地执行身份映射来使用快捷连接*。这被证明稳定了深度网络的优化过程。此外,剩余网络通过超越传统 CNN 显示了快捷连接的力量。*

该方法可以通过增加模型的深度来扩展。这是特别合适的,因为剩余网络不会遭受退化问题。

保持联系: LinkedIn

7.参考

【1】【Krizhevsky 等人,2012】Krizhevsky,a .,Sutskever,I .,Hinton,G. E. (2012)。基于深度卷积神经网络的图像网分类。神经信息处理系统进展,25:1097–1105。

【2】【何等,2015】何,k,张,x,任,s,孙,J. (2015)。用于图像识别的深度残差学习。

【3】【何等,2016】何,k,张,x,任,s,孙,J. (2016)。深剩余网络中的身份映射。

【4】https://www . ka ggle . com/Alex attia/the-Simpsons-characters-dataset

【5】https://towards data science . com/transfer-learning-and-data-augmentation-applied-to-the-Simpsons-image-dataset-e 292716 FBD 43

ResNets:为什么它们的性能比经典的 ConvNets 好?(概念分析)

原文:https://towardsdatascience.com/resnets-why-do-they-perform-better-than-classic-convnets-conceptual-analysis-6a9c82e06e53?source=collection_archive---------16-----------------------

更深入地了解是什么让剩余网络如此高效

图片来源: Unsplash

今天,我们都熟悉神经网络在赋予机器人工智能方面发挥的作用,几乎在每个领域,从医疗行业到太空探索。来自世界各地的机器学习爱好者和研究人员正在寻找使这些网络尽可能健壮和高效的方法。一个这样的网络是剩余网络(ResNets ),这是一个无处不在使用的架构,能够有效地实现更深更大的网络。在本文中,首先,我们将简要概述 LeNet-5、AlexNet 和 VGGNet-16/19 等经典网络,然后我们将讨论剩余网络(RenNets)的功能,以及它如何减轻之前讨论的经典网络的一些限制,并通过深度网络实现高效训练。这将有助于你梳理这些概念,并为从概念上更好地理解 ResNets 扫清道路。如果您不想浏览经典架构,请跳过下一节,直接进入 ResNets (2。我们能走多深?).

1.经典网络架构

基本卷积神经网络(CNN)的设计从图像输入层(尺寸:高度、宽度、通道)开始,随后与一些卷积滤波器、最大或平均池层的组合进行卷积,随后是全连接层,最后是用于类别标签预测的 Softmax 或 Sigmoid 输出函数。后续层的信道根据卷积层中使用的滤波器数量而增加。这种设计 CNN 的通用方法对于每种架构都是通用的,但是这些层的数量和排列方式使得它们在性能和效率方面互不相同。

LeNet-5 架构,开发于 1998 年,用于识别一组手写数字的灰度图像,它由大小为:(32×32×1)的图像输入层,6 个滤波器的卷积层,滤波器大小为(5×5)和(2×2),平均池,步幅:1 用于卷积,2 用于平均池。网络以一个完全连接的层和具有 10 个可能预测的输出函数结束(在这种情况下是数字(0 到 9))。该网络由总共 60,000 个训练参数组成。

图一。LeNet-5 架构(图片来源:作者)

2012 年,研究人员提出了一个比 LeNet 大得多的网络,名为 AlexNet 对 ImageNet LSVRC-2010 数据集中的 120 万张高分辨率图像进行分类。输入大小为(227x227x3),它由卷积层和最大池层的组合组成,使用相同的填充和 1000 个类,在最后一层使用 Softmax 函数进行预测。该网络有 6000 万个训练参数,比 LeNet 大得多。网络的架构如图 2 所示。

图二。AlexNet 架构(图片来源:作者)

2015 年开发的VGGNet-16/19,在 ImageNet 数据集上训练,是这三种网络架构中最深入的。它有 1.38 亿个训练参数,包括:VGG-16 中的 16 个卷积层(使用填充),VGG-19 中的 19 个卷积层(两者的功能相同),最大池,全连接和 Softmax 分类器中的 4096 个特征,最终预测 1000 类对象。通过该网络中每一层的输入的宽度和高度尺寸的系统减少,伴随着每一层中通道数量的有组织的增加,是引起许多开发者和研究者注意的一个因素。看看这些深度网络的功效,它们在行业中普遍用于训练各种机器学习模型以及向初学者教授基础知识。

图 3。VGGNet-16 网络架构(图片来源:作者提供)

2.我们能走多深?

当我们进一步增加这些网络的深度以使模型更健壮并增强其性能时,会发生什么?根据理论,训练和测试时的错误率应该随着我们在网络中的深入而不断降低,然而,在实验中,发生了相反的事情。在达到最小值后,错误率不再稳定下降,而是再次开始上升。这是由于爆炸和消失梯度下降问题造成的,该问题也会导致模型过度拟合,从而增加误差。这个问题可以从数学上理解这里。在这种情况下,像 L2 正则化或剔除这样的技术无助于优化或减少过拟合。因此,随着我们深入,有效的参数和激活(甚至来自身份函数)会在中间丢失,因为后续层由于通过不断更新权重和偏差进行的严格激活而无法维持它们。

图 4。该图显示,20 层网络的错误率小于 56 层网络的错误率,理论上应该相反。(图片来源:(原文引用)用于图像识别的深度残差学习

幸运的是,剩余网络已经被证明在解决这个问题上非常有效,因为它们在每两层之间使用跳过连接或“捷径”,同时在所有层之间使用直接连接。这允许我们从一层获取激活,并将其提供给另一层,甚至神经网络中更深的层,因此在更深的层中维持网络的学习参数。

图 5。在 ResNet 中跳过连接(图片来源:(原始引用)用于图像识别的深度残差学习

这种连接的一个块被称为**“剩余块”**,它们在 ResNet 中相互堆叠,以保持从单位函数中有效学习参数,甚至在更深的层中。

3。残余块内部发生了什么?

让我们将普通网络层的结构和功能与残差块进行比较,以正确理解残差块的工作原理。平面网络由线性(z)和非线性(ReLU)激活组成,其输出用作后续层的输入。在这种情况下,对于每一层,只使用一个激活函数。

图 6。“普通网络”的一层(图片来源:作者)

在哪里,

z[l+1]= w[l+1]a[l]+b[l+1]

*ReLU:g(z(l+1))=*a【l+1】

z[l+2]= w[l+2]a[l+1]+b[l+2]

*ReLU:g(z[l+2])=*a【l+2】

(以此类推(l+n)层,其中 n 是层数)

w[l] 是第l层的权重矩阵。

B[l] 是第l层的偏差

然而,在其第*【l+2】层中的剩余块连同来自a【l】g(z【l】)的激活,也使用来自先前激活函数的参数,即a【l】*本身。这是通过下图所示的跳过连接实现的。

图 7。ResNet(图像来源:作者)的一层(残余块)

剩余块非线性 ReLU 函数;

a[l+2]= g(z(a[l+1])+a[l])(重要方程)

如果在 的情况下,z(a[l+1]) = 0,(这发生在 w[l+1] = 0,b[l+1] = 0 的情况下)

a[l+2]= a[l]([l+2]层还有一个参数要学习)

上图所示的连接将a【l】添加到第【l+1】层的 ReLU 非线性函数中。因此a【l】中的信息通过捷径快速转发到网络的更深处。这暗示着第【l+1】层的 ReLU 函数现在变成了g(z【l+1】+a【l】)。这样,即使在网络中的某个第 n 层,权矩阵为零矩阵,偏差也为 0,使 z(a[l+1]) 无效,第*【l+2】层层也总会有一个来自第l层的高效学习参数,即a[l】*。**

**注:**有时候,当*a【l】a【l+2】*层的尺寸不同时,矩阵加法是不可能的,所以,*z(a【l+l】)+a【l】没有意义。在这种情况下,通过将矩阵 Ws 乘以前一层的权重矩阵W【l】*来对这些层的权重矩阵进行一些调整,以使维度相同并能够进行相加。幸运的是,这些计算中的大部分是在设计 ResNet 网络时处理的,因为在它们的卷积层中使用了“相同的”卷积。

4.网络中的层

图 8。ResNet 中的层由堆叠的剩余块组成(图片来源:作者)

深度网络是通过将剩余的块一个接一个地堆叠起来而构建的,每个网络长达一百层,有效地从网络更深处的早期激活中学习所有参数。ResNet 的卷积层如图 9 所示。它是一个由 34 层组成的 ResNet,具有使用相同填充的(3×3)卷积滤波器、最大池层和全连接层,以预测 1000 个类的 Softmax 函数结束。

图 9。34 层 ResNet 卷积神经网络(图片来源:(原文引用)用于图像识别的深度残差学习)

总之,ResNets 是最有效的神经网络架构之一,因为它们有助于在网络的更深处保持低错误率。因此,在需要深度神经网络的情况下,如特征提取、语义分割、各种生成性对抗网络体系结构,被证明表现得非常好。这些也可以用于开发严格的配备人工智能的计算机视觉系统,其中需要提取复杂的特征或提高图像和视频的分辨率。我希望这篇文章能帮助您更清楚地了解 ResNets 背后的基本概念。感谢阅读!

参考文献:

【https://arxiv.org/pdf/1603.05027.pdf】https://arxiv.org/pdf/1603.05027.pdf:ResNets 研究论文

【2】http://yann.lecun.com/exdb/publis/pdf/lecun-98.pdf:LeNet 研究论文

【3】https://papers . nips . cc/Paper/2012/file/c 399862d 3 b 9d 6 b 76 c 8436 e 924 a 68 c 45 b-Paper . pdf:Alex net 研究论文

【4】https://arxiv.org/pdf/1409.1556.pdf:VGGNet 研究论文

【5】https://www . coursera . org/learn/convolutional-neural-networks/lecture/hahz 9/resnets:CNN 课程 by deeplearning.ai

https://towardsdatascience . com/introduction-to-resnets-c0a 830 a 288 a4

基于实验设计和 python 的响应优化

原文:https://towardsdatascience.com/response-optimization-with-design-of-experiments-and-python-63f9afb3f26f?source=collection_archive---------16-----------------------

思想和理论

用响应面法(RSM)和 python 寻找实验最优值。

介绍

在之前的文章中,提出了一种分析具有两个水平的简单 DOE 的方法,并讨论了解决平均效应和相互作用的相关分析。然而,运行 DOE 的重要一点是寻找系统最大响应的能力。

在本文中,我们将使用 python 中可用的一些非常基本的工具来解决这样一个问题:给定两个水平的全因子 DOE 的结果,如何计划和执行接下来的运行以实现最大化。如果你熟悉一些机器学习技术,你会发现我们正在实现一个非常简单的梯度下降方法。不同之处在于,我们不想最小化成本函数,而是最大化结果。

只要记住,如果你需要最小化某事,你可以最大化它的负面!

未知响应的生成

为了简单起见,我们将生成一个依赖于两个变量的响应:x 和 y,这并不太复杂,但它有一个最大值。

我们创建了一个函数,我们将在实验的下一步使用它。

上面的函数接受两个输入:x 和 y,以及一个我们稍后将使用的关键字解析器。第一次运行时,我们让代码为 x0、y0 和 fwhm 生成随机变量。这些是我们函数中的一些超参数,用来探索不同的情况。
然后我们用 np.linspace (101 个点)初始化我们的曲面,并计算我们矩阵(用 meshgrid 构建)中每个 x 和 y 的函数。结果如下所示。

图一。生成的表面。

总体思路和框架

现在让我们想象我们想要探索这个系统,所以,作为优秀的实验者,我们计划一个实验。我们不太了解,只知道我们的参考变量是从 x = [0.5,1]和 y=[0.5,-1]左右开始的。这是任意的,其他任何值都可以。

我们将探索那个地区作为我们的第一个实验。

第一次全因子设计。

此时,我们将得到四个点的结果(全因子设计)。从那里,我们将拟合一个具有 scikit-learn 函数的二阶相互作用的线性模型,并建立我们系统的最陡路径。

一旦我们了解我们的系统朝哪个方向发展,我们将选择下一个点。在这篇文章中,我们将首先手动这样做,然后实现一个简单的循环来自动完成同样的任务。

效用函数

为了生成实验,进行测量并将结果与我们以前的模型进行比较,创建一个函数来帮助我们这样做是很方便的。

在上面的代码中,我们用我们的 xsys 的所有组合来初始化一个数据帧。最终我们可以决定在设计中添加一个中心点,但这不是强制性的。

为了与上一篇文章中的内容保持一致,我们称函数为“normal_to_coded_var ”,它将变量重新编码为 1 。然后,我们使用一个 lambda 函数来计算响应,传递我们之前在生成曲面时定义的参数。如果我们在现实生活中实际运行这个实验,这一步就不会出现:在这种情况下,我们只需要用将结果导入到我们的数据框架中的东西来修改这条线。

如果将模型传递给函数,代码还将计算预测值的%误差。请注意,为了得到一个模型,您至少需要第一次运行,因此我们将在接下来的步骤中使用该函数。

线性模型

为了找到最佳方向,我们需要拟合一个模型。最简单的方法是用多项式特征和线性回归定义管道。我们使用多项式特征来计算 x 和 y (x*y)之间的相互作用参数,并最终(interaction=False)计算类似(x2 和 y2)的项。

生成下一步

为了生成下一步,我们需要一个简单的策略:我们计算一个方向上的期望增量,并基于该模型推导出另一个方向上的增量。

基本上,我们计算 increment_x 和 increment_y,同时跟踪系数的符号(否则我们将总是增加 x)。然后,我们将变量映射回原始空间,因为我们在寻找最大值,所以我们将该值添加到之前显示最大值的点。围绕新的 x 和 y 的最大步长将是 0.125。

我们可以找到不同的方法来选择新的值,这取决于我们对系统的了解或考虑特定的约束。

迭代结果

第一次运行的起点是:

首先对原始变量、编码变量(cX 和 cY)和响应(结果)进行 DoE。

我们看到我们的 2 因子全因子,以及编码变量,和结果。运行上面的代码将生成这个表面。请注意,曲面是使用拟合的模型生成的。

从第一个 DOE 获得的表面。

从那里我们看到表面线不是直的,所以相互作用项有一些影响。我们还注意到,从最小值到最大值,我们几乎有 4 个点的差异。这是因为所选区域很宽。如果系统是强非线性的,这并不总是一个好的起点。箭头显示的是梯度:这也是从拟合的模型计算出来的,而不是原来的表面。

代码如下:

将被迭代的代码。

这将输出下一步:

coef x -0.66 and coef y 0.86
increment x -1.54 and increment y 2.00 with ratio -0.77
new x 0.37 and new y -0.25
[0.24 0.49] [-0.38 -0.12]

比率为负,表示系数之一为负,而另一个不是。接下来建议的点集中在 x = 0.37 和 y = -0.25 处。因此,下一步是移动到左上角,但也减少了原始空间的宽度。

新实验的要点在下图中用蓝色显示。请注意,为了节省时间/资源,您可以决定使用第一个设计的左上角来运行第二批实验。在这种情况下,我们还在设计中实现了中心点。

如果我们想看看我们的模型如何很好地拟合新空间的结果,我们应该使用“x”和“y”作为变量再次拟合它:用编码变量拟合模型是方便的,因为它们是标准化的,但是预测在新的数据框架中是没有用的。

从下表中我们可以看到,红色方块附近的误差较低,随着我们远离它,它会如预期的那样增加。

第一个模型的结果和预测值之间的比较。

迭代表面结果

这是用原始变量生成的表面。请注意,左图中预测值的范围从 0 到> 25(第一次 DoE),右图中预测值的范围> 18(第二次运行)。

第一次 DOE(左)和第二次运行(右)的表面响应。

自动化回路

最后,我们可以尝试实现一个简单的标准来找到我们的最优值:在这种情况下,我们只需重复上面的步骤,当结果中的最大值低于所有组合实验中的最大值时,停止代码。

steps_df = pd.DataFrame()
x_t = np.array([0.5,1])
y_t = np.array([-0.5,-1])for i in range(10):
    test_df = output_model(x_t, y_t, pars=(x0, y0, fwhm), cnt_point=False)
    test_df['iteration'] = i
    pipe1 = linear_pipe_fit(degree=1, interaction=True, dataframe=test_df, input_vars=['x','y'])
    x_t, y_t = next_step(pipe1, increment_y = 2, dataframe=test_df)
    #plot_contour_fromdf(test_df, fill_value=0, model = pipe1, plot_normal_vars=True)
    steps_df = steps_df.append(test_df)
    max_outcome = test_df['outcome'].max()

    if max_outcome < steps_df['outcome'].max():
        print(f'maximum found at iteration {i}')
        break
    print(x_t, y_t)

这是原始曲面上的迭代:

迭代特性是在迭代 i=4 时,代码向后移动,然后停止,有效地找到最大值。

结论

总之,我们看到了如何设计一个 2 级 DoE,并使用简单的策略迭代搜索最大值。代码看起来在方向改变上是健壮的。然而,这种实现可能会受到局部最大值的影响。

如果我们怀疑我们的系统可能有复杂的表面,运行稀疏设计(如拉丁方)来更好地映射表面可能是一个好的选择,而不是盲目地寻找最大值。

参考书目:

本文的灵感来自于:
使用数据的流程改进—使用数据的流程改进(learnche.org)

我的 github 简介和一个笔记本,上面有这里使用的全部代码。

负责任的 AI 在脸书

原文:https://towardsdatascience.com/responsible-ai-at-facebook-936d3dcb0161?source=collection_archive---------36-----------------------

苹果 | 谷歌 | SPOTIFY | 其他

Joaquin quionero-Candela 在 TDS 播客

要选择章节,请访问 Youtube 视频这里。

编者按:这一集是我们关于数据科学和机器学习新兴问题的播客系列的一部分由 Jeremie Harris 主持。除了主持播客,Jeremie 还帮助运营一家名为sharpes minds的数据科学导师初创公司。你可以听下面的播客:

收听苹果、谷歌、 Spotify

脸书经常部署推荐系统和预测模型,这些系统和模型每天都会影响数十亿人的生活。这种影响力伴随着巨大的责任——除了其他事情之外,开发符合道德、公平和良好表征的人工智能工具的责任。

这不是一项容易的任务。人类花了几千年来争论“公平”和“伦理”的含义,但还没有达成共识。这正是为什么负责任的人工智能社区在确定探索和推荐什么政策时必须尽可能多地涉及不同的观点——这是脸书负责任的人工智能团队一直在应用的做法。

在这一集的播客中,我邀请到了华金·基诺内罗·坎德拉,他是脸书负责任人工智能的杰出技术负责人。Joaquin 多年来一直处于人工智能道德和公平运动的前沿,并监督了脸书负责任的人工智能团队的形成。因此,他是相对少数几个有实践经验的人之一,能够大规模地做出关键的人工智能伦理决策,并看到它们的效果。

我们的谈话涵盖了很多领域,从关于公平定义的哲学问题,到实现某些道德人工智能框架时出现的实际挑战。以下是我最喜欢的一些外卖食品:

  • Joaquin 强调了在人工智能背景下思考公平的三种不同方式,所有这些方式都在一定程度上涉及到在不同人群(性别、种族等)中比较给定算法的性能。第一,如果一个系统对每个群体都达到了最低的绩效水平,那么它就是公平的。这将包括制定听起来像这样的标准,“任何种族都不应该被错误分类超过 5%。”第二种方法走得更远,要求平等:算法性能不应该在不同组之间变化太大。应用这一标准会导致这样的要求,“平均而言,任何种族被错误分类的频率都不应超过其他任何种族的 3%。”
    最后一个策略是最小化各组结果的差异。这里的想法是应用先验知识,即“所有的组都同样可能执行行为 X,所以一个不能预测所有的组将以同样的概率执行 X 的算法一定是不公平的。”
  • 一个关键问题是:谁来决定哪种公平标准适用于特定情况?Joaquin 认为这是公平问题的核心:我们不会很快解决我们所有深刻的道德争论,所以与其现在假装道德清晰,我们应该专注于创建允许在具体案例的基础上确定具体决定的公平性的过程。因此,他的团队的口号是“公平是一个过程”(他真的有一件 t 恤来证明这一点!)
  • 无论公平过程涉及什么,很明显,我们不应该指望数据科学家自己处理所有的事情。数据科学家没有时间成为道德和哲学的大师,因此有必要在这些道德问题到达技术团队之前建立抽象的结构。为此,脸书的负责任的人工智能计划(Responsible AI initiative)根据一种“中枢辐射”模式工作,在这种模式下,一个核心团队负责以公开透明的方式制定道德标准,然后这些标准由专门的团队转化为数据科学家可以实施的数学定义。
  • Joaquin 讨论了大公司之间协作和协调日益增长的重要性,以确保建立更普遍的负责任的人工智能开发标准,这些标准不仅仅反映单个组织的激励或观点。本着这种精神,他现在与人工智能伙伴关系(Partnership on AI)合作,这是一个伞式组织,汇集了脸书、谷歌、微软和 OpenAI 等主要参与者,以协调与人工智能道德最佳实践和安全人工智能开发相关的问题。

你可以在推特上关注华金,或者在推特上关注我

播客中引用的链接:

  • 华金的个人网站在这里。
  • 华金在 AI 网站上关于合作关系的页面在这里。

章节:

  • 0:00 介绍
  • 1:32 华金的背景
  • 8:41 实施流程
  • 15:12 没有单一优化功能
  • 公平的定义
  • 28:03 脸书和 Twitter 如何管理公平性
  • 32:04 算法歧视
  • 37:11 人工智能伙伴关系
  • 44:17 总结

请查看以下文字记录:

杰里米(00:00):

嘿,大家好。我是杰里米。欢迎回到播客。我对今天的节目感到非常兴奋,因为我们将谈论脸书是如何做负责任的人工智能的。现在,脸书经常部署推荐系统和预测模型,每天影响着数十亿人的生活。伴随着这种延伸而来的是巨大的责任,除了其他事情之外,还有开发符合道德、公平以及良好表征的人工智能工具的责任。

耶雷米(00:22):

这真的不是一件容易的事情。人类已经花了几千年来争论公平和道德到底意味着什么,而我们在这些问题上还没有达成任何共识。这正是为什么负责任的人工智能社区在决定探索和推荐什么政策时,必须尽可能多地涉及不同的观点。这是脸书负责任的人工智能团队已经应用的实践。

耶雷米(00:44):

现在,在这一集的播客中,我和华金·奎诺内罗·坎德拉在一起,他是脸书负责任人工智能的杰出技术负责人。华金多年来一直处于人工智能伦理和人工智能公平运动的前沿。他监督了脸书整个负责任的人工智能团队的形成,基本上是从零开始。因此,他是相对少数有实践经验的人之一,能够大规模地做出关键的人工智能伦理决策,并看到它们的效果。

杰里米(01:09):

现在,我们的对话将涵盖很多领域,从关于公平定义本身的哲学问题,到实施某些人工智能伦理框架时出现的实际挑战。这是一个伟大的。我很兴奋能和你们分享这一集,希望你们也喜欢。Joaquin,非常感谢你参加我的播客。

华金(01:26):

谢谢你,杰米。谢谢你邀请我。非常荣幸,我非常喜欢你的播客。

耶雷米(01:32):

非常感谢。你能来真是令人激动。我真的很期待这次谈话。我们可以讨论的东西太多了,但我认为有一个情景片段会有助于做好准备。这是一个关于你的背景的简短对话,你是如何进入这个领域的,有人是如何从 ML 和学术旅程之外进来,然后最终来到你现在的位置,这是脸书负责任人工智能倡议的领导者。你怎么去的?

华金(01:56):

我们必须有一个协议,你可以随时打断我,因为一旦我开始谈论我的旅程,我可以去很长时间。我认为在非常非常高的水平上,我在 2004 年完成了机器学习的博士学位。当时,在我所在的社区,很少有人使用 AI 这个词。NeurIPS 社区主要是一个 ML 社区。我做过一段时间的学者。我是德国马克斯·普朗克研究所的博士后。我的第一次转变是在 2007 年 1 月作为一名研究科学家加入了位于英国剑桥的微软研究院。

华金(02:43):

所以在我的职业生涯中发生了一件非常基本的事情,那就是我遇到了使用机器学习的产品团队。特别是,在微软的搜索引擎必应(Bing)发布之前,我们开始与该团队进行对话。我们开始应用 ML 来增加广告对人们的相关性。具体来说,我们正在建立模型,预测如果有人看到广告,他是否会点击。这项工作真的让我迷上了这样一个想法,即 ML 不仅仅是你在科学论文中做的事情,而是你可以实际投入生产的事情。

华金(03:24):

所以,在 2009 年,Bing 组织问我,“嘿,你为什么不离开微软研究院,那种舒适的研究环境,为什么不成为一名工程经理,在 Bing 内部领导一个团队?”所以,我就这么做了。这可能是我一生中压力最大、最戏剧性的转变之一,因为我对成为一名工程领导者一无所知。我不习惯做路线图、人员规划、预算、随叫随到、负责关键生产组件。所以,压力很大。但同时,这也很棒,因为它让我看到了另一个世界。

华金(04:04):

在我的整个职业生涯中,有一个主题一直重复出现,现在依然如此,那就是技术转移的想法。从白板和一些数学、一些想法、一些研究、一些实验,一直到需要维护和运行的生产系统。

华金(04:22):

这种转变让我以第一人称视角看到了研究的另一面,另一个世界,有时作为一名研究人员,你会进去说,“嘿,你们都应该考虑使用我刚刚建立的这个很酷的模型,”然后人们会说,“我没有时间做这个。我很忙。”所以,现在突然之间,我站在了另一边。这是一次很棒的经历,最终也帮助建立了拍卖优化团队。这让我学到了一点关于机制设计、经济学和拍卖的知识。

华金(04:57):

然后我在 2012 年 5 月来到了脸书。我加入了广告组织。我开始为广告建立机器学习团队。然后,我认为我职业生涯中的第二个重要时刻发生了,那就是,我想我会告诉每个人的一句话是,我真的很难相信奇才队没有规模。

华金(05:16):

因此,我们面临的挑战是,我们在脸书有一些优秀的机器学习研究人员,但我们需要解决的问题数量不断增加。我们几乎被自己快速移动的能力所束缚。人们会在他们自己的盒子里建造模型,有他们自己的运输过程,我觉得整个过程很慢。困扰变成了,我们如何加速研究到生产?我们怎样才能建立工厂?

华金(05:51):

那是一个有趣的时期,因为突然之间,有一个困难的决定要说,嗯,不要急于建立最复杂的神经网络来进行排名和推荐,让我们坚持我们现有的技术,这是合理的。我们使用增强决策树,使用在线线性模型等相对简单的工具。在大范围内,这可能没那么简单。但我们的想法是,我们可以每周发货吗?我们能不能从花几周时间从-

耶雷米(06:26):

哇哦。

华金(06:26):

是啊,那是幻觉。vision 每周都会发货。这几乎成了一句口头禅。每个人都喜欢,“每周出货。”这让我们建立了整个生态系统来管理从研究到生产的路径。它做了很多事情。这是我们在整个公司建立的一套工具。这很有趣,因为它只关注广告。这个想法是,在 ads 中,任何人都可以很容易地建立任何类型的模型,预测任何类型的表面上的任何类型的事件,实际上分享代码,分享想法,分享实验结果。

华金(7:00):

几乎也知道该和谁说话。其实一件大事就是建立一个社群。我们的愿景是,这与您实际表达模型的框架无关。当 TensorFlow 和 Keras 变得流行时,我们当然支持它。我们支持 PyTorch。但是如果你真的想用 C++从头开始写东西,请便。这是相当不可知论的。一切都是为了管理工作流。

华金(07:28):

最终发生的是,团队开始要求采用我们正在构建的东西。从时间上看,整个公司都采用了我们的产品。我们开始在计算机视觉、自然语言处理、语音、推荐、机器翻译等基础上增加专门的服务。这导致了一个名为 Applied ML 的团队的成立,我帮助建立并领导了几年,它本质上只是为整个公司提供服务,并使 ML 民主化,并将其交到所有产品团队的手中。

华金(08:06):

这个想法是,因为奇才不会扩展,你不需要那么多奇才,奇才将是基本创新的创造者,但这样你就可以拥有一个由数百名甚至数千名工程师组成的生态系统,他们可以非常容易地利用和建立这些东西。所以,我一直这样做,直到三年前。在我们过渡到第三个重要时刻,也就是过渡到负责任的人工智能之前,如果你想问我关于这一部分的任何问题,我会先喘口气。

耶雷米(08:41):

在这方面,特别是在你职业生涯的第二阶段,我最大的收获之一是你在谈论实施过程,比如变得非常非常擅长实施过程。在我看来,每当人们谈论人工智能伦理、人工智能公平之类的东西时,这就是让我不寒而栗或让我对整个空间有点紧张的东西,这显然缺乏一致性。

杰里米(09:04):

很难知道什么是 AI 公平,什么是 AI 伦理。似乎每个人都有不同的定义。我也不清楚,即使我们有了这些定义,我们如何将它们编成可复制和可扩展的过程。所以,专业知识一定很重要,对吧?我的意思是,过程开发流程,将映射到人工智能工作的公平性?

华金(09:27):

确实如此。现在,你击中了要害。这很有趣,因为我们在公司内部民主化人工智能时所做的许多事情现在非常有帮助,因为我们正在研究负责任的人工智能,尽管这可能不是我们脑海中的第一个想法。但是,现在我们有工具可以让我们看到在脸书生产中部署的所有模型是什么,谁建造了它们,它们包含了什么,这种想法最终会给你这种程度的阻碍和一致性,这真的很重要。但是当然,负责任的人工智能还有更多。如你所说,在概念的定义上存在广泛的分歧。所以,也许让我告诉你我自己的世界之旅。

华金(10:23):

我一直在关注公平、问责和透明研讨会。我相信它发生的第一年可能是 2014 年左右,在 NeurIPS,然后它不断重复出现。然后我相信 2018 年可能是它爆发的一年。事实上,我参加了那个研讨会。它成为了一个独立的会议,2018 年在纽约举行。

华金(10:53):

但就在 2017 年底之前,在纽约的会议上,凯特·克劳福德(Kate Crawford)或索隆·巴罗卡斯(Solon Barocas)等人做了几个基本的主题演讲。这对我来说变得很明显,好像我的整个大脑在某种程度上爆炸了,我想,是时候了。很明显是时候了。我们需要把食物放在煤气上。我们需要全力以赴。我们需要从最初的努力开始,真正地,真正地建立一个专注于负责任的人工智能的巨大努力。

华金(11:27):

作为一个热爱数学、热爱工程、热爱流程和平台并为人们提供工具的人,我认为我们可以在几个月内解决算法偏差问题[相声 00:11:42]。

耶雷米(11:43):

不会那么难的。

华金(11:44):

是啊,没那么难。能有多难?我想,我们来看看这里。所以,我看了一些作品,一些定义。当然,很快就清楚了。这是 Arvind Narayanan 的作品,他给出了这个美丽的演讲,公平的 21 个定义和他们的政治。他在 2018 年做了一个关于这个的演讲。当时它被称为肥胖之星会议。现在它被称为事实。[inaudible 00:12:11]社区很擅长想出像[NIPS 00:12:14]和 FAT 这样可怕的名字。幸运的是,我们给它们重新命名了。

耶雷米(12:18):

快速移动,打破东西。

华金(12:19):

快速移动,打破东西。所以,我想,好吧,没关系。在这 21 个定义中,一些关于公平的定义试图让不同群体之间的结果平等,一些关于公平的定义试图关注对每个人一视同仁,我想,好吧,我们到底需要多少个?就像,也许我们可以实现一对。我想象像一个下拉框,好吧,我选择哪一个作为[相声 00:12:44]从业者?我想我们可以有漂亮的可视化数据,包括分组数据、模型的准确性、校准曲线等等。我想,好吧,我们有这个我们,我们可以在几个月内完成。

华金(12:59):

然后,我突然意识到,人工智能的公平性主要不是人工智能的问题,数学没有答案。你应该使用什么样的公平定义,这完全取决于具体情况。你需要建立多学科的团队,包括来自道德和政治哲学的人。这实际上非常重要。或许最重要的一点是,公平不是模型的属性。这不是一个状态框。其实是一个过程。公平是一个过程。事实上,我会把镜头倾斜一点,给你们看…我故意穿上我们为团队制作的 t 恤,上面写着,公平是一个过程。

耶雷米(13:45):

啊,非常好。

华金(13:46):

这是我们为团队制作的一套 t 恤,因为我一直在重复这件事。因此,有一天,我们团队的行政助理拿出一堆 t 恤给每个人,并说,“好吧,你们一直在说公平是一个过程[听不清 00:14:03]。”不管怎样,我们自豪地穿着它们。你永远不会结束。对于工程师来说,可能有点难以想象的是,这不像是,哦,我要在这里做这个麦克风,而且已经完成了。我测试过了,可以用。不是这样的。

华金(14:20):

如果你回到过去,我想人类从亚里士多德开始就一直在讨论公平。我们还在讨论,我们不同意。它变得政治化,因为我们有不同的意识形态。所以,你需要建立一个多学科的过程,包括风险评估,决策,记录你如何做出决策等等。这只是为了公平起见。显然,在负责任的人工智能中,你也有许多其他维度要处理。

耶雷米(14:54):

我喜欢把公平作为一个过程的想法,部分是因为,嗯,有一个著名的原则叫做古德哈特定律。一旦你定义了一个指标,你说,我们要优化这个数字,然后突然人们找到了绕过它的方法,他们发现了黑客,他们发现了作弊。可以说,我在今天的股市中看到了这一点。

耶雷米(15:12):

时间回到 1950 年,一般来说,如果股市上涨,人们的生活质量就会提高。但现在我们看到了脱钩,因为人们开始玩游戏和政治,并围绕它进行优化。所以,这看起来像…我的意思是,这是战略的一部分,是认识到没有单一的损失函数,没有单一的优化函数来改善,而是看起来像让我们专注于过程?

华金(15:35):

绝对的。关于这件事,我有几件事想说。首先,每个指标都是不完美的。即使你不是专门为公平而工作,当你设定一个指标目标时,特别是,我认为许多技术公司非常成功的原因之一是,我认为我们可以快速迭代,我们可以非常量化,我们可以由指标驱动,我们可以移动这些指标。

华金(16:11):

但这同时也是我们潜在的致命弱点,它会让你陷入困境。因此,培养非常强的批判性思维是至关重要的。开发我们称之为计数器的指标是很重要的,就像其他指标一样,当你使这个指标变得更好时,你不应该使它变得更差。但即便如此-

耶雷米(16:36):

[相声 00:16:36]其中一个例子?那听起来像是一个迷人的想法。

华金(16:39):

一个简单的想法是,想象你正在努力提高一个语音识别系统的准确性,你正在通过单词错误率或类似的东西来衡量它,无论你想要什么。因此,如果你关心公平,你可以建立一个反指标,你可以实际分解,你可以取消你的指标,比如说,在美国。你可以通过一些地理分组来做到这一点,因为全国各地的口音都不同。然后,您的衡量指标可能是您不能降低任何组的性能,因为平均值总是会影响您,对吗?

耶雷米(17:21):

是啊。

华金(17:21):

你可以让大多数人的情况变得好两倍,你可以让另一个人的情况变得更糟,而你可能甚至没有意识到。因此,这将是一个非常公平的中心指标。但是你可以想象许多其他的场景,在这些场景中你试图优化体验的许多方面,并且有一些事情你不希望变得更糟。

耶雷米(17:51):

我想你对此的第一个评论是公平,或者说公平的定义对数据科学家来说不是一个挑战。

华金(17:58):

没错。

耶雷米(17:58):

这不是他们应该担心的事情,这完全有道理。数据科学家没有时间去拿哲学学位和公共政策学位。所以,这最终会是某个人的工作。无论是政策制定者还是有更多文科背景的人,我想到的一个问题是,围绕这些指标的对话的特点变得非常非常技术性。我想仅仅是能够传达正在做出的决定的本质,这本身就是一个挑战。你认为这种情况会经常出现吗?你有什么策略来应对这种挑战吗?

华金(18:32):

这是一个挑战。我们已经取得成功的一件事是定义一个在整个公司谈论公平的一致的词汇,以及一组越来越复杂的一致的问题,以使这一点非常具体,并用例子来说明。公平的一个最基本的定义是问这样一个问题,我的产品对所有群体都有效吗?当然,定义组的问题也是非常依赖于上下文的。

华金(19:18):

在我的左边是我们的脸书入口摄像头,它有一个智能人工智能,能够跟踪人。如果我和我在西班牙的父母或者在[听不清 00:19:36]德国的父母进行视频会议,基本上现在你可以想象我们的周末是如何和我们的孩子一起度过的,或者我们和他们进行视频会议,我们实际上也给了他们一些这样的设备,因为如果你用你的手机来 Skype 或 FaceTime 或使用你使用的任何软件,试图保持画面居中等真的很烦人。

华金(19:56):

因此,传送门是神奇的,但人工智能跟踪人们,并看到如果你有两个人来缩小它,裁剪,居中和一切,你不能想当然地认为它会在肤色或性别或年龄方面都一样好。在这种情况下,你可以说,我如何定义最低服务质量?最低的服务质量是,某人突然出现或某事的失败率必须小于某个小百分比的时间或帧或会话数或其他。

华金(20:34):

这是一个合理的概念,最低服务质量的概念。我希望我的产品对每个人都足够好。当然,在我的产品环境中,一个关键的问题是,嘿,考虑到你要建立的东西,谁是你应该考虑的最敏感的群体?稍后,如果你愿意,我会在话题罐里放点东西,那就是我们可以谈论选举干预。我们可以谈论人工智能如何在这种情况下提供帮助。我们可以谈谈印度选举。然后我们可以推理出在这种情况下哪些群体是相关的。

华金(21:12):

但是回到门户的例子,关于公平的最基本的问题是,嘿,你能取消你的度量吗?团队通常会启动标准。所以数据科学,ML 团队会说,“嘿,这个模型够好了吗?”好吧,问题是,在这种情况下,它可以是肤色,有许多尺度可以用于此,年龄和性别,然后对于这些桶中的每一个,查看表现最差的桶,并想象这是唯一的人口,你还会推出吗?

华金(21:50):

如果答案是否定的,那么也许你需要坐下来想一想,好吧,我怎样才能确保我不会丢下任何人。接下来是印度大选,这里有一系列问题和一个更具比较性质的公平定义。在某种程度上,最低服务质量与标准相比,但它不一定担心,哦,它可能对一组比另一组工作得更好。只要它对每个人都足够好,就没问题。我将把另一个话题放入话题罐,那就是古德哈特定律,以及你是如何玩弄或搞砸你的最低服务质量的。

华金(22:30):

但是下一个层次将是平等或平等待遇。你可以说,好吧,我实际上关心的是性能上的差异。印度选举的例子是这样的。如果我没记错的话,大约在去年四、五月间,我们举行了人类历史上最大规模的选举。我想大概有接近 10 亿的注册选民。比这少一点点,但是很大。在这个时候,有许多错误信息和操纵企图的担忧。

华金(23:08):

在没有人工智能的情况下,我们解决这个问题的方法是让人类获得标准、社区指南、内容政策,并检查内容是否违反这些标准。如果有,你就把他们拿下。但问题是,就这次选举的规模和我们现有的数量而言,你没有多少人可以雇佣来做这件事。所以接下来你需要做的是你需要区分工作的优先顺序,因为很明显,我的关于我弹吉他或展示我的猫或我的狗的帖子,我们不应该浪费时间去回顾这些。

华金(23:47):

但是,如果有政治家正在讨论的问题,或者即使不是政治家,组织也在讨论公民内容,讨论社会或政治问题的内容,那么我们应该优先考虑这些内容。所以,我们建立了一个人工智能来做这件事。我们称之为市民分类器。因此,它所做的是筛选内容,识别可能是公民性质的内容,然后优先考虑这些内容,供人们审查。

华金(24:20):

公平问题是什么?嗯,公平问题是,在印度,你有,我想有 20 多种官方语言,而且有许多地区有非常不同的文化特征。所以,真的,如果你把你的人口划分成地区和语言,你可以看到这些实际上是如何与宗教或种姓相关联的。不同地区的人们关注的社会问题是不同的。但现在你有了一个人工智能,它可以优先考虑我们将那些人力资源放在哪里来审查内容。如果人工智能只对几种语言有效,而对其他语言无效,会发生什么?

华金(25:01):

所以,如果你拿语言做类比,如果它高估了语言的风险会发生什么?这意味着我们会优先考虑…我们会投入更多的人力资源来审查该语言的帖子。如果你低估或低估了另一种语言中某些东西在本质上是公民的可能性,那么我们就没有在那里分配足够的人力资源。

华金(25:22):

然后,我们决定不以最低服务质量为标准,我们认为,这是一个我们真正想要平等待遇的案例。由于我们这里有一个二进制分类器,它输出一个介于 0 和 1 之间的数字,表示这段内容是公民的概率是多少,那么我们所说的是,我们希望这些预测能够针对每一种语言和每一个地区进行很好的校准。

华金(25:47):

所以,再一次,公平的概念,我们设置了一个更高的标准,因为这是关于资源分配的公平。为了使画面完整,我们在内部也使用的第三个常见定义和相关问题集是关于公平的,是关于询问,不仅仅是我们应该平等对待所有人和所有事,还是系统的性能应该高于每个人的某个标准?那么问题就变成了,例如,有没有一群人的产品结果值得特别关注?这可能是因为我们应该考虑一些历史背景。

华金(26:42):

我们在 2020 年看到的一个例子是,随着美国种族正义意识的觉醒,人们开始关注黑人企业的商业成果,例如在脸书。因此,我们已经建立了产品的努力,帮助黑人拥有的企业的可见性,因为我们觉得脸书平台有机会帮助改善已经存在的情况。

华金(27:17):

很多时候,当你考虑公平时,这个问题也是一个责任问题。这就像,好吧,人们可能会说,好吧,但社会是有偏见的,而我在这里,我已经建立了一个中立的技术,我在这个游戏中没有皮肤。我认为我们要说的是它不是那样工作的。你的技术可以反映这些偏见,它可以延续它们,它可以将它们封装在一个黑盒中,使它们合法化,或者使它们很难调试或理解。另一方面,技术也可以让每个人的结果变得更好。但有时我们不得不问自己,有没有我们要优先考虑的群体?说了很多话。

耶雷米(28:03):

不。我认为很难回答这个问题,脸书是如何在推特上管理公平的[听不清 00:28:10]。

华金(28:09):

没错。

耶雷米(28:10):

但是我认为你描述的不同种类的镜头和不同的公平方法真的很有趣。从过程的角度来看,我非常好奇的一件事是,在组织内部,关于应用哪个公平标准的决定来自哪里?您认为做出决策的流程目前是否已经正式化,结构是否足够合理?显然,会有迭代,但是你觉得你对这个过程应该如何构造有一个好的感觉吗?

华金(28:42):

是啊。所以,我们正在建立流程。但对我们来说非常清楚的是,这是一个轴辐式模型。因此,我们有一个集中的负责任的人工智能团队,它是多学科的。正如我前面提到的,它不仅有人工智能科学家和工程师,也有道德和政治哲学家和社会科学家。该团队所做的是整个层面的工作,从为提出这些问题提供一个道德框架,如果你愿意,这将是定性部分,然后更深入到定量部分,就像,嘿,我们如何…

华金(29:26):

现在你谈到了平等与最低服务质量。为了平等,你谈到了二元分类器。这在数学中到底意味着什么?我在看什么?我看的是组间的假阳性率还是类似的?回答:不,我们不一定,因为这有问题。但是你会怎么做呢?

华金(29:48):

在这下面还有另一层,更多的是平台和工具集成。再一次,利用我们在应用 ML 中所做的所有工作,我们如何使它变得非常简单和友好,能够按组分解算法的预测,等等?所以,你有整个蛋糕,包括像办公时间这样的事情,这实际上非常重要。它是集中的。

华金(30:15):

然后你会看到,每个产品团队都在构建自己的嵌入式专家组,他们将与一个集中的团队密切合作。你必须这样做的原因是,你需要集中的团队有一致的实践,一致的术语和定义,但是决策,就像你之前说的,需要在上下文中做出。

华金(30:45):

因此,让集中的团队做出数百个非常依赖上下文的决策是不切实际和低效的。最重要的是,因为我们说,这是一个过程,你不要做一次决定就完事了。你坚持下去。对吗?

耶雷米(31:04):

是啊。

华金(31:06):

顺便说一句,我看了电影《编码偏见》,作者是 Shalini Kantayya。它的特点是 Joy Buolamwini 和 Timnit Gebru 和 Cathy O'Neil 以及其他许多负责任的人工智能方面的杰出研究人员。我认为试图引用现场的人,然后把人弄错,这一定是非常危险的。

华金(31:32):

其中一个,我将把它保持在那个抽象层次,因为我不记得是谁说的了,其中一个说了这样的话,“许多这些负责任的人工智能实践,比如公平,它们就像卫生。就好像你每天都要刷牙一样。”这就是我对它的看法。你必须结合你的产品来做。因此,我们看到越来越多的产品团队建立他们自己的嵌入式工作,我们与他们的合作非常非常密切。

耶雷米(32:04):

你如何决定……所以,这其中有一个几乎是经济的方面让我着迷。我想我看到了 Amanda Askell,OpenAI 的人工智能政策专家之一,她在推特上谈到了这个想法,现在在人工智能伦理社区有一个主题,如果我能找到一个算法不好的原因,那么这个算法就不应该被部署。在某种程度上,在时代精神中有这样的氛围,人们会说,“哦,好吧,这个算法以这种边缘方式进行歧视。”

耶雷米(32:33):

当然,我们知道所有的算法都会不可避免地以某种方式进行区分。所以,一定有某种界限,我们会说,好吧,这太多了,这还不够。从这个角度来看,我想你是如何看待经济取舍的?我的意思是,你可以试着去调试算法,直到母牛回家,但是在某个时候,某些东西必须被启动。所以,是的,我想,你怎么看待这个权衡?

华金(32:59):

我认为很难对这个问题给出一个纯粹定量的答案。我的意思是,再一次,作为一个数学和工程人员,我喜欢能够计算函数的成本,如果我使用人工智能减少仇恨言论,误报的成本是 40 美元 3 美分。所以这很难。我认为这涉及到了一些事情。我认为它涉及透明度,也涉及治理。

华金(33:40):

我认为,我认为事情的发展方向是,当你将人工智能构建到一个产品中时,你对风险收益分析非常透明,有一个短语我这些天在使用,我可能是从某人那里偷来的,我不知道,这种人工智能最小化的想法,这种想法……就像人们谈论数据最小化一样。所以,AI 最小化。而不是我的本能是让我们使用人工智能,因为它很酷,它应该是这样的,我可以不用人工智能吗?如果答案是也许,但如果我加上 AI,它为每个人增加了一吨多的价值,就像,好吧,这是合理的,我有价值。

华金(34:33):

然后是另一面,这是所有的风险清单。而风险之一可能是歧视,也可能是其他对不公平的定义。我认为这些权衡是透明的,并且有一个理想的闭环机制来获得反馈并进行调整,看看什么是可接受的,我认为这是我们将不可避免的未来。

华金(35 分 06 秒):

我经常用的一个例子并不是 AI 的例子,但还是那句话,我再说一遍,这些问题很多都不是大家熟悉的 AI 问题。我们考虑内容的适度性,以及在给予每个人发言权和允许言论自由之间的艰难权衡,但另一方面,保护社区免受有害内容的影响。而且有明显有害的误传。我们得出了坚定的结论,即我们既不能也不应该自己或独自这样做。当我说我们时,我当然是以脸书员工的身份说的。但我认为这在整个技术领域都是如此。

华金(35:55):

我们正在试验这个外部监督委员会。也许你听说过。是一群专家。团队做了大量工作,将他们组织在一起,制定了章程以及他们将如何运作。但是让我们先关注一下它的精神。它的精神是将会有棘手的决定。没有任何内容政策是完美的。因此,让人类参与到开发中来,拥有一个包括外部审查和专业知识的治理是至关重要的。我认为,归根结底,这将是透明度、问责制和参与性治理机制的相互作用。对吗?

耶雷米(36:45):

是啊。

华金(36:46):

如果你缩小范围,你会想到人工智能在 10 年内会非常普遍,非常重要,一个大问题将是参与式治理,对吗?

耶雷米(36:58):

是啊。

华金(36:59):

你或我怎么能说人工智能在哪里开车送我们的亲戚或宠物去看兽医,我的意思是,这将是非常惊人的,就像某种自动驾驶-

耶雷米(37:11):

这本身,几乎就像是在脸书的整个过程中强制征收这种能源税……我认为脸书这样做很好。我认为脸书有一个负责任的人工智能部门很棒。从某种程度上来说,这种情况只能在今天大规模发生是完全有道理的,因为只有在这样的规模下,像脸书、谷歌和 OpenAI 这样的公司才能真正负担得起建立巨型语言模型和巨型计算机视觉模型。这导致…我希望我们有更多的时间来讨论这个问题,但是我必须问一个关于人工智能合作的问题。

华金(37:43):

当然,是的。

耶雷米(37:43):

因为它是这个等式中如此重要的一部分。实际上,我个人的一个担忧是,我们在这里已经谈了一点,但仅仅是安全、隐私、公平、道德,所有这些东西,它们确实采取了对有竞争力的组织有效征税的形式。我的意思是,谷歌必须与脸书竞争。脸书必须和其他人竞争。这是一个市场。

耶雷米(38:06):

这是一件好事,但只要有公平税,只要有安全税,这意味着就有动力进行某种竞争,每个人都试图在某种程度上在这些方向上进行最低限度的投资。至少在我看来,像人工智能伙伴关系这样的团体在调解互动、设定最低标准方面发挥着有趣的作用。我想,首先,你同意这种解释吗?第二,随着技术的发展,你如何看待人工智能领域的合作伙伴关系发挥更大的作用?因为我知道这是一件大事,但我想确保我得到你对[相声 00:38:40]的广泛想法。

华金(38:39):

不,我很高兴你在人工智能上提出合作。我是董事会成员,过去几年一直积极参与,我认为这是一个世界需要的组织。但我希望你能帮助我理解“逐底竞赛”假说背后的逻辑。也许我们可以通过选择你想要的任何责任维度来使它变得非常具体。我只是有点纠结。我想确定我真的明白你的意思。

耶雷米(39:14):

是啊,是啊,当然。所以,我在想象…实际上,我会举一个从 alignment 到 AI 安全社区的例子。所以,一个常见的是一个假设,我不一定买这个,但具体来说,让我们假设这是真的,语言模型可以任意缩放,并导致类似超级强大的模型,有效的神谕或类似的东西。所以,他们本质上是在释放你可以用超大语言模型做的东西的数量。

耶雷米(39:43):

在某种程度上,安全是一个问题,你可能不想要一个任意强大的语言模型,可以策划和计划。就像你可以说,“嘿,我怎么从华金那里骗到 8 万美元?”语言模型说,“哦,这是你怎么做的。”然而,像 OpenAI 这样的公司正在与 DeepMind 或任何其他公司竞争,以尽可能快的速度扩大这些规模。谁先达到一定的能力水平,我的意思是,在六个月左右的时间里就有决定性的优势。

耶雷米(40:16):

有类似的情况,你有公平和偏见等等,但在任何时候你参与公司与公司的竞争,你可以在安全上投入边际成本,或者你可以在能力上投入边际成本,总会损失一些价值。这种权衡是…我想知道人工智能上的伙伴关系是否可以在组织之间发挥某种作用,设定每个人至少都想遵守的标准,诸如此类的事情。

华金(40:44):

是啊。谢谢你。我理解这个问题。这个问题让我在某种程度上思考了一下环境可持续性,你可以在许多方面进行同样的竞争,不仅是在公司之间,甚至是在国家之间。就好像我们只有一个星球。谁将承诺削减二氧化碳成本?就像,哦,好吧,等一下,我马上回来。

耶雷米(41:10):

就在这棵树后面。

华金(41:13):

没错。我认为人工智能方面的合作在许多方面都发挥着关键作用。首先,作为一个论坛,一个非常独特的论坛,它汇集了你能想到的每一种类型的组织,学术界,民权组织,民间社会,其他类型的非营利组织,小公司和大公司。人工智能合作伙伴中的一些组织将积极游说,塑造和宣传即将到来的监管。因此,我认为已经有了一点压力,要求大公司参与并带头,因为不这样做可能意味着你必须应对对所有人都不利的监管。所以,我觉得这是一个角度。

华金(42:16):

另一个角度是效率。你描述的一些工作,我可以投资的一些美元,以确保我的大语言模型符合人类价值,并且它们不是邪恶的,如果你有一个由大量盈利性公司支持的组织,并且他们做这项工作,像 ABOUT ML 这样的项目是我喜欢的项目,它建立在数据集和模型的文档实践上,所以它是各种想法的组合,像数据集的数据表和模型卡等,这些都是非常强大的。

华金(43:04):

我们脸书公司对此投入了极大的关注和实验。我很高兴我们可以使用一些东西,A,相对现成的,但非常[相声 00:43:16],我们不必建立自己,并为加分。它得到了像人工智能伙伴组织这样的组织的认可和信任,它不仅仅代表我们的利益。我认为这将是一个普遍的模式。

华金(43:33):

这是一个 360 度的视角,你可以通过一个拥有所有人的多利益主体组织来获得。第二,效率和便利性,最佳实践和建议来自那些真正知道他们在做什么的人,你知道当这个被创建时每个人都在看。然后第三个是真正的合法性和有效性,你通过使用一些东西,嘿,这些不像脸书的伦理原则。对吗?

耶雷米(44:06):

是啊。

华金(44:06):

我的意思是,如果人们皱起眉头,人们会说,“哦,我的意思是,那里代表了什么利益?”只有脸书的,而它来自人工智能的合作伙伴。

耶雷米(44:17):

是啊。我的意思是,这是我希望在未来能看到更多的组织之一。这似乎是那种组织的新生核心有望继续发展的事情,因为正如你所说,我们需要在许多不同的层面上对这一领域进行某种多党派的监督。很高兴看到这项倡议正在成形。华金,非常感谢你的时间。实际上,你有没有一个你喜欢分享东西的地方,特别是像 Twitter,或者一个个人网站,如果人们想了解更多关于你的工作,你想把他们引向那里?

华金(44:49):

哦,我要坦白一件事。所以,我是一个晚采用 Twitter 的人。

耶雷米(44:58):

哦,那可能对你的心理健康有好处。

华金(45 分 03 秒):

但我现在感到一种强烈的责任感,那就是为公众辩论做出贡献并参与其中[听不清 00:45:14]。我现在甚至不得不去看看我的推特,这让我自己很尴尬。在推特上,我是@jquinonero。所以,是字母 J,然后是我的第一个姓。很复杂。我有两个姓,因为我来自西班牙。

耶雷米(45:33):

我们可以分享它。

华金(45:34):

我们有一个…没错。是啊是啊。我已经做出了更多参与的个人承诺,我将不得不学习如何以一种不会像你所说的那样破坏我的心理健康的方式来做这件事。

耶雷米(45:48):

是啊。出于某种原因,我认为这是最大的挑战之一,尤其是我发现在 Twitter 上保持这种正念状态,不要迷失在提要中。好家伙。不管怎么说,我们已经活了十年了。但真的很感谢你花时间在这上面。非常感谢,真的很喜欢这次谈话。

华金(46 分 05 秒):

非常感谢你,杰米。很高兴来到这里。谢谢你做这个播客,谢谢你让每个人都参与到这种对话中来。

耶雷米(46:17):

我很乐意。