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

news/2024/10/19 18:10:19

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

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

使用数据科学节省资金

原文:https://towardsdatascience.com/saving-money-using-data-science-f7f6d229dd90?source=collection_archive---------44-----------------------

为什么班上的那个学生问你的数学老师“我什么时候会用到这些东西?”可能破产了

在之前的一篇文章中,我讨论了如何使用 STL 算法将任何时间序列分解成季节、趋势和噪声部分。我用亚马逊的股票价格向你展示了一个例子。在这篇文章中,我将向你展示如何用同样的技巧在租房时省钱。我将使用公寓列表提供的 2017 年至 2021 年间单卧室公寓的月平均租金。如果你想看示例代码,请看我之前的文章。

如果你没有读过我之前的文章,这里有一篇关于 STL 算法的简介:它将任何时间序列作为输入,并将该时间序列分解为季节、趋势和余数(即噪声)分量。例如,下面是马萨诸塞州波士顿一套一居室公寓的平均月租金(第一个图表),它被分解为三个部分(第二个图表):

作者图片

作者图片

如果我们只关注季节性因素,我们可以看到你应该在一年中的哪个月签订租约,以获得最便宜的月租金。在波士顿,最低到最高的季节性是-60 美元到+50 美元,这意味着在某些月份签署租约将使你的月租金平均减少 60 美元,而在其他月份将增加 50 美元。

作者图片

对于波士顿来说,季节性在八月最高,一月最低。因此,你应该尝试在 12 月和 1 月之间签订年度租约(与季节性高峰相比,你每月将节省 110 美元),避免在 5 月和 8 月之间签订(与季节性最低价格相比,你每月将多支付 110 美元)。因此,如果你住在波士顿,一些带有几行 R 代码的开源数据每年可以为你节省 1300 美元。并非所有城市都表现出与波士顿相同的季节性。例如,让我们看看佛罗里达州的迈阿密和德克萨斯州的奥斯汀。

作者图片

在佛罗里达州的迈阿密,我们看到季节性在三月份最大,大约每月+15 美元,在十月份最小,每月-15 美元;没有波士顿那么戏剧化。选择正确的月份在迈阿密签订租约,佛罗里达州每年将为您节省约 360 美元。德克萨斯州奥斯汀的季节性也有类似的范围:八月的最高值为每月 20 美元,二月的最低值为每月 15 美元。如果您在合适的时间签订租约,在德克萨斯州奥斯汀市(例如,每年 420 美元)比在佛罗里达州迈阿密市可以节省更多。

作者图片

作者图片

公寓列表中的数据集包含 479 个城市的完整数据。一个有趣的问题是:“哪些城市表现出最大和最小季节性之间的最大差异?”按照从大到小的顺序排序,让我们看看所有差价大于 100 美元的城市:

作者图片

全美有 26 个城市,在合适的月份签订公寓租约,每年至少能为你节省 1200 美元。结果似乎偏向于“科技城市”(如山景城、库比蒂诺、西雅图、雷德蒙),如果您在正确的月份签署租约,加利福尼亚州福斯特市每年可节省 2700 美元。我们可以查看季节性图表,找出几个有代表性的城市的“神奇”月份。

作者图片

作者图片

作者图片

在山景城、福斯特城和雷德蒙,在 6 月或 7 月签署公寓租约将比你等 6 个月后在 11 月或 12 月签署多花 100 到 225 美元。在上面列出的 26 个城市中,合理安排时间可以让你每年多赚 1200 到 2700 美元。如果你看到一个学生问“我什么时候才能在现实生活中使用这些东西?“一边抱怨他或她的数学课,一边给他们看这篇文章!

如果你想看看示例 R 代码,更好地理解 STL 算法,看看我早先的文章。但是如果你想读一些不太专业的东西,我推荐我的文章优先化分析工作的最佳实践。如果你今天对统计学更感兴趣,试试关于 A/B 测试的这篇文章或者关于三(3)个最重要的统计测试的这篇文章。最后,如果你想在 1-2 分钟内快速阅读,请尝试这篇文章中的八(8)个技巧来改善数据科学和业务用户之间的交流。

用人工智能拯救卑微的蜜蜂

原文:https://towardsdatascience.com/saving-the-humble-honey-bee-with-ai-fdc951fd67cc?source=collection_archive---------27-----------------------

真实世界人工智能

TinyML 帮助保护蜜蜂免受邪恶贪婪的捕食者的伤害

照片由来自 Pexels 的 Pixabay 拍摄

你昨天吃了什么?它包括杏仁、苹果、蓝莓、花椰菜或卷心菜吗?

对于我们这些有幸获得大量这些食物的人来说,我们不能再认为它们是理所当然的。

而且不是因为气候变化,土地短缺,或者破坏性的农业做法(虽然这些本身就是问题),而是因为卑微的 蜜蜂

根据宾夕法尼亚大学的研究,蜜蜂帮助了超过三分之一的全球粮食生产,尤其是像蓝莓、杏仁和苹果这样的作物。

他们受到了威胁。

近年来,蜜蜂受到了一种肉眼几乎看不到的邪恶捕食者的攻击——瓦螨。

这种螨虫是一种致命的寄生虫,它已经给世界各地的蜂群造成了巨大的破坏,首先是削弱个体蜜蜂,然后是感染整个蜂巢。

然而,在澳大利亚,瓦螨还没有找到一个据点——澳大利亚是最后一个没有这种恶性螨的有人居住的大陆。

但是 Varroa 已经侵袭了澳大利亚的两个近邻— 新西兰和巴布亚新几内亚— ,所以澳大利亚的养蜂人不顾一切的想要控制它。

幸运的是,人工智能 (AI)正在赶来救援。

而且不是随便什么 AI,而是tinyML

TinyML 可以帮助解决巨大的问题

TinyML 是人工智能的一个新兴领域,它将机器学习和深度学习的巨大能力打包到一个微小的形状因子中。

这使得它成为资源受限应用的理想选择,在这些应用中,尺寸、功耗和连接性非常重要,而且有许多这样的应用。

TinyML 通过使用诸如修剪(删除不需要的网络节点)和量化(将浮点数转换为整数)等技术来缩小预先训练好的(大型)机器学习模型。

这些预训练的模型然后被用于 tinyML 部署中的推理(预测或分类),这比训练需要更少的计算能力。

这些模型在微控制器上使用专门的编码平台实现,如 Tensorflow Lite

微控制器是紧凑的专用集成电路,是物联网革命的主力。

直到最近,由于资源限制,这些微控制器的功能范围有限。然而,多亏了 tinyML,这些'微型机器已经开始变得'智能'

无 Varroa 蜜蜂的最后边疆

在澳大利亚,为了帮助抵御瓦螨,个体养蜂人一直在密切监视他们的蜂箱。

此外,在澳大利亚各地的港口和其他入境点设置了特别指定的蜂箱,并由检疫检查员例行检查。

到目前为止,这种方法一直很有效。

但 Varroa 在澳大利亚站稳脚跟可能只是时间问题,而且已经出现了几次差点违约的情况。

例如,2018 年,一艘美国集装箱船抵达澳大利亚,船上有受 Varroa 感染的蜜蜂。幸运的是,澳大利亚当局在那种情况下设法识别并遏制了威胁。

虽然监测过程迄今为止一直有效,但这是"精心手工",的亚当·麦克纳马拉称Bega Cheese,一家大型澳大利亚乳制品生产商最近进入蜂蜜业务。

更重要的是,即使是短时间的未被发现的 Varroa 暴露也会是一个问题。维多利亚养蜂人协会的阿里斯·彼得拉托斯建议说,“如果它已经存在了一个月、一周或两周,那可能已经太晚了”。

因此,澳大利亚农业渴望找到更好的监测 Varroa 的解决方案,他们求助于 T2 的尖端技术。

tinyML 是如何帮助蜜蜂的

Bega 与创新的澳大利亚科技公司【xai lient】Vimana Tech 合作,启动了 紫色蜂巢项目 以帮助改善澳大利亚的瓦螨监测

Purple Hive 的目标是在澳大利亚建立一个能够自动检测瓦螨的蜂巢网络。这将使 Varroa 监控变得更加容易、更具可扩展性和成本效益

但是自动检测 Varroa 螨虫是一项艰巨的任务——螨虫非常小,几十只蜜蜂可以一次进出一个蜂巢。“做起来挺难的,就像大海捞针”,反映了 Shivy Yohanandan , Xailient 的联合创始人。

许多蜂房也位于偏远地区,电力或通信有限。任何检测设备都需要在严格的资源限制内工作,并达到高度的可靠性

但是 Xailient 专门研究极其高效的计算机视觉算法,使得有效的、资源受限的解决方案成为可能。

利用他们创新的人工智能能力,Xailient 和 Vimana Tech 开发了一种 tinyML 实现,可以满足自动检测瓦螨的所有挑战。

它是这样工作的:

  • 一个特殊的 3D 打印蜂箱—紫色蜂箱— 装有摄像头和人工智能技术
  • 蜂巢的入口有 360 度的摄像机,可以捕捉每只进入的蜜蜂的图像
  • 然后,人工智能算法帮助识别是否有任何进入的蜜蜂携带瓦螨
  • 如果检测到瓦螨,实时警报就会发出,同时还有被感染蜜蜂的图像

作为 tinyML 解决方案,紫色蜂巢中使用的人工智能算法和相关硬件高效、低功耗,并且具有有限的连接要求——非常适合部署在广泛的偏远地区。

此外,紫色蜂箱入口设备,实际上包含螨虫检测技术的单元,是太阳能供电的,可以安装到现有的蜂箱上——这使其便携,易于在现有的蜂箱网络中推广

给蜜蜂一个战斗的机会

澳大利亚很幸运地避免了瓦螨对其蜜蜂造成的破坏,但这种运气可能不会持续太久。

通过转向创新的技术解决方案,像紫色蜂巢这样的项目将为蜜蜂提供一个战斗的机会,以保持领先于邪恶的 Varroa 捕食者。

Purple Hive 的核心 tinyML 显示了资源受限的实现有多么有用——在一个充满资源饥渴技术的世界里,有无数种情况下高效、集中和低资源的解决方案正是所需要的。

这是 tinyML 的承诺。

概括起来

  • 蜜蜂在世界粮食生产中扮演着重要的角色,像蓝莓、杏仁和苹果这样的农作物尤其依赖蜜蜂
  • 然而,蜜蜂一直受到一种邪恶的捕食者的攻击,这种捕食者是瓦螨,它能在几周内毁灭蜂群
  • 澳大利亚是最后一个没有瓦螨的有人居住的大陆,人们对通过有组织的监控努力保持这种状态很感兴趣
  • 但是监测瓦螨是一个劳动密集型和困难的过程,所以更有效的方法正在被寻求
  • 使用尖端技术和 tinyML 解决方案,澳大利亚公司 BegaXailientVimana Tech自动监控瓦螨方面处于领先地位,使其成为一个更容易、更具可扩展性和成本效益的过程
  • Xailient 利用其专业的计算机视觉算法和 tinyML 专业知识在资源紧张的情况下开发了有效的 Varroa 检测实施方案

告别循环,迎接优化

原文:https://towardsdatascience.com/say-goodbye-to-loops-and-hello-to-optimization-aaace896e80b?source=collection_archive---------24-----------------------

使用 NumPy 优化 Python 代码

𝓴𝓘𝓡𝓚 𝕝𝔸𝕀 在 Unsplash 上拍照

虽然 Python 仍然易于使用,学习起来很快,并提供了大量几乎可以做任何事情的外部库,但它有一个致命的弱点:它很慢。

当然,在人眼看来,它的迟钝似乎可以忽略不计。通常 Python 只落后于其他编程语言几毫秒;然而,当迭代数百万甚至数十亿个数据点时,它会迅速累加。

NumPy 提供了独特的解决方案。在允许用户仍然编写 Python 的同时,它将 Python 转换成专门为数值分析优化的写得很好的 C 代码。

使用 NumPy 数组可以将脚本的性能提高一到两个数量级,并且只需要很少的学习时间。

向量运算

传统的循环方法不是对数组中的每个元素执行一个函数,而是对数组中的所有元素同时应用一个函数。

import random# create a list of random integers
data = [random.randint(0, 100) for i in range(1000000)]

为了演示矢量运算的效用,创建了一个示例数据集。上面的代码生成 1,000,000 个介于 0 和 100 之间的随机整数的列表。

为了举例,将测量对列表中的每个元素取余弦的性能。

# import math for cosine function
import math# create a new list for the data
new_data = []# loop through the data, take cosine and add it to the new list
for i in data:new_data.append(math.cos(i))

上面的代码使用了一个典型的循环来遍历列表并取每个值的余弦值。这是通常想到的传统迭代方法。不幸的是,这用了大约 0.31 秒,相当慢。

# import math for cosine function
import math# create a new list for the data
new_data = [math.cos(x) for x in data]

除了循环,还可以使用列表理解,这将时间减少到 0.24 秒。虽然比传统的循环有所改进,但仍然有些迟缓。

# transform the data into a NumPy array
data = numpy.array(data)# apply the vector operation
new_data = numpy.cos(data)

最后,NumPy 用于相同的操作。第一行将列表转换成数组,第二行应用向量运算。

注意,不是使用标准库中数学模块的余弦函数,而是必须使用 NumPy 的余弦函数。

除了生成更干净易读的代码,整个过程只需要 0.03 秒左右。这大约比使用传统循环快 10 倍,比理解列表快 90%。

通过数据过滤

NumPy 擅长的另一个常见任务是数据过滤。使用与以前相同的数据,将对超过 50 的所有值进行筛选。

# Using a traditional loop
new_data = []
for i in data:if i > 50:new_data.append(i)# Using a list comprehension
new_data = [i for i in data if i > 50]

和以前一样,普通 Python 可以使用传统的循环或列表理解来过滤列表。前者需要 0.23 秒,后者需要 0.11 秒。

# Convert data into a NumPy array
data = numpy.array(data)# Create a filter
data_filter = data > 50# Filter for values greater than 50
new_data = data[data_filter]

然而,NumPy 没有使用循环,而是提供了一种直观的替代方法。将列表转换成数组后,下一行创建一个过滤器。如果迭代,过滤器将输出符合条件的 True 和 False 值。

换句话说,如果数据中的第一个值大于 50,则筛选器中的第一个值将为真,反之亦然。

一旦创建了过滤器,就可以使用熟悉的拼接符号将其应用于数据,创建一个值大于 50 的新数组。整个过程只需要 0.02 秒,无论是循环还是列表理解都是一个巨大的进步。

对广播的解释

广播关注大小不等的数组上的算术运算。要工作,数组的尾轴必须相等,即使维数不相等。下图给出了一个直观的例子。

有效广播的直观示例

在这种情况下,广播是兼容的,因为在水平轴上有一个相等的数字。上述示例采用 1 X 2 数组,并将其添加到 2 X 2 数组中的每一行。

有效广播的另一个直观例子

可选地,垂直轴可以是兼容的。同样的原理适用于 2×1 阵列应用于 2×2 阵列的每一列。

无效广播

如果两个轴都不相等,如上例所示,则可能无法应用广播。相反,Python 会返回一个值错误。

广播代码示例

在将广播应用于更高维度的数据之前,下面的示例使用了与前面的演示中使用的 1,000,000 个 0 到 100 之间的整数相同的列表。

# Very simple broadcasting
new_data = numpy.array(data) * 2

像以前一样,不使用循环或列表理解,而是简单地将列表转换为 NumPy 数组并应用运算(在本例中乘以 2)提供了一种简洁有效的数据转换方法。

从广播的角度来看,一个 1 X 1,000,000 的数组乘以一个 1 X 1 的数组(也就是所谓的一个数字)。

然而,当应用于更复杂的事物时,广播要有趣得多。假设数据是一个 2 X 1,000,000 的数组:

# Create a 2 X 1,000,000 dataset
data = [[random.randint(0, 100) for i in range(1000000)],[random.randint(0, 100) for i in range(1000000)]
]

举例来说,第一组 1,000,000 个数据点中的所有数据都将加 1,第二组中的所有数据都将加 2。

new_data_1 = []
new_data_2 = []# Using loops to inefficiently broadcast
for i in range(len(data)):for j in data[i]:new_data_1.append(data[i][j] + 1)for k in data[i]:new_data_2.append(data[i][k] + 2)new_data = [new_data_1, new_data_2]

在一个相当不公平的比较中,一组嵌套循环可以用来完成目标,但结果是草率的,需要大量的开销。如果使用另一组尺寸,也需要做很多调整。

然而除此之外,它还需要 0.83 秒的运行时间。

# Using list comprehensions to broadcast
new_data = [[x + 1 for x in data[0]],[x + 2 for x in data[1]]
]

列表理解提供了一个有吸引力的选择。上面的代码更短,更容易阅读,运行只需要 0.17 秒;然而,试图将这种方法用于更复杂的数据(如 100 X 100 的数组)将很难复制。

# Convert data into a numpy array
data = numpy.array(data)# Create a small array to broadcast
operator = numpy.array([[1],[2]
])# Use NumPy broadcasting
new_data = data + operator

最后,使用 NumPy 进行广播的例子提供了一种更直观的方法。将数据转换为数组后,将创建一个 2 X 1 数组来修改数据集。为此,在最后一行中使用了一个简单的加法运算符。

虽然这种方法比使用 list comprehensions 要多一些行,但是它更灵活,可读性更好,最重要的是,运行起来只需要 0.04 秒。

结论

Python 提供了很多实用工具,但是速度传统上不是它的优势之一。然而,NumPy 库通过直观的语法提供了很大的性能提升。

简单地放弃循环和列表理解,转而支持 NumPy 操作,可以在不增加不必要的复杂性的情况下大幅提高代码速度。理解它的用途是任何大型数据项目的必要条件,实际上也是数据科学的要求。

告别截图,使用 Datapane 进行数据科学报告

原文:https://towardsdatascience.com/say-goodbye-to-screenshot-and-use-datapane-for-data-science-report-e698bbd29f72?source=collection_archive---------22-----------------------

7 分钟阅读完整的 Datapane 教程

DDP 在 Unsplash 上拍照

动机

我经常喜欢在博客上写数据科学相关的文章。更准确地说,我是在介质Quora 上写的。有时,我用 python 创建一些交互式数据可视化,或者用 jupyter notebook 的 markdown 选项创建一些美学表现。但是当我想把它发布在 medium 这样的博客上时,我必然会使用截图或 gif 来分享交互式可视化或表示。几天前,我一直在寻找分享这些互动情节和降价展示的方法。在搜索了很多之后,我发现了一些给出了一些方向的文章。不幸的是,没有完整的指导方针让我可以轻松地使用该技术来共享交互式可视化和表示。经过一点研究, Datapane 似乎是完成这项工作最有用的工具。现在,是时候分享我的发现,让你的数据科学相关博文更具互动性和美感。

简单又好玩..让我们开始吧……

数据面板概述

Datapane 是为以编程方式分析数据和创建报告的人准备的。到目前为止,Datapane 支持以下库、语言和文件格式来生成交互式报告。

  • 熊猫数据帧
  • Plotly 来自 Python 可视化库,如BokehAltairPlotly**
  • 降价和文本
  • 一般文件,如图片、pdf、JSON 数据等。

Datapane 报告非常灵活,还可以包含页面、选项卡、下拉列表等等。

创建后,报告可以上传到 web,在云中动态生成,或者嵌入到您自己的应用程序中,在那里可以浏览数据,并可以交互地使用可视化。

开始使用数据面板

在使用 Datapane 之前,您应该知道 Datapane 是一个开源的 Python 库,它使得用 Python 构建交互式报告变得很容易。Datapane Community 是一个免费的在线发布 Python 报告和协作的服务。

现在,只要按照步骤

  1. 在你的电脑上安装数据面板库

在您的cmdanaconda command提示符下运行命令

*pip3 install datapane
or 
conda install -c conda-forge "datapane>=0.11.0"*

如果你是 mac 或 linux 用户,你可以遵循这里提供的指南

2。登录到数据面板

首先,你必须在 Datapane 网站注册。登录后,他们会为您提供一个令牌。使用令牌,您可以轻松地从本地 pc 登录到 Datapane。令牌可以从 链接中找到。如果您点击该链接,您将进入一个页面,在那里您可以找到如下令牌。

作者照片

这是整个过程的 gif 图

作者 gif

3。在 Datapane 文本编辑器或 jupyter 笔记本中呈现 markdown 文本,并将其共享给 medium

您可以使用 Datapane 文本编辑器和 python 来创建可共享的降价。下面的 gif 展示了用 Datapane 文本编辑器创建 markdown 文本的全过程,并分享给 medium。

作者 gif

让我们用 python 做同样的事情。为此,我编写了以下代码。

该代码将在您的本地目录中保存一个 html 版本的降价文本。如果你想把它上传到 Datapane 网站,只需把最后一行代码从report.save(path=”Markdown.html”,open=True)改成report.upload(name=”Markdown down with jupyter notebook”)。它将保存一个输出到数据面板报告。完整的指南如下所示。共享过程与 Datapane 文本编辑器 gif 中显示的相同。

作者 gif

你会得到如下文本。

4。用 python 渲染 HTML 代码并创建交互式表格

Datapane 也有助于用 python 呈现 html 代码。我按照下面的代码创建了一个交互式表格。

代码将把表格上传到 Datapane 报告中。如果你想把它保存到你的本地目录,只需要在代码的最后一行使用.save(path=’report.html’, open=True)就可以了。介质中的共享过程与 gif 的markdon 部分所示相同。

输出—

5。带数据面板的可交互数据表

Datapane 的交互式数据表是一个非常棒的工具,可以用交互的方式来表示你的数据集。

我已经编写了一段代码,使用 seaborn 库构建一个内置的数据框架,并使用 datapane 将其导出为数据表。它生成以下输出。

我觉得不可思议。 你可以在内置 EDA 中搜索、过滤、排序、探索等等。它涵盖了一个数据集的全部报告。gif 演示了数据表的特性。

作者 gif

6。互动情节和可视化

没有其他类似的工具来分享这种类型的交互式绘图和可视化。目前 Datapane 支持**Altair, Matplotlib / Seaborn, Bokeh, Plotly ,Folium** 库进行可视化。dp.Plot块从一个受支持的 Python 可视化库中取出一个绘图对象,并将其呈现在您的报告中。我以**Plotly** 为例展示了一个演示。

数据面板输出

7。带降价的组合图

这更有趣,因为它看起来像是用任何文本编辑工具创建一个交互式报告。降价文本很容易与互动情节相结合。一个例子和代码。

数据面板中的输出

8。写公式分享

公式块允许您轻松地将 LaTeX 格式的公式添加到您的报告中,并带有可选的标题。关于乳胶配方的简单介绍可以在这里找到。

**import datapane as dpdp.Report(dp.Formula(r"\frac{1}{\sqrt{x^2 + 1}}", caption="Formula")
).upload(name="First formula")**

输出

在引擎盖下,Datapane 使用 MathJAX 在浏览器中呈现方程,而不是一个完整的 TeX 引擎。这意味着您的一些 TeX 输入可能无法在 Datapane 系统上正确呈现——阅读 MathJAX 文档了解更多信息。

9。在媒体或其他博客上集成代码

代码块允许您将语法突出显示的源代码嵌入到报表中。这个块目前支持 Python 和 JavaScript。比 github gist 更赚钱。

数据面板中的可共享代码

10。将网格布局添加到您的报告中

如果您将一个块列表(比如PlotTable)传递给一个报告,那么默认情况下,它们会被放置在一列中,每个块一行。如果您想定制行和列,Datapane 提供了一个Group组件,它接受一个块列表和一些列和/或行,并将它们放在一个网格中。

如果我们以前面教程中的例子为例,但是想要并排放置绘图和数据集,我们可以使用 specify this using Group并指定列数。

数据面板共享报告

11。向您的报告添加多页内容

Datapane 上的报告可以有多个页面,在报告的顶部以选项卡的形式呈现给用户。这些可以像 Excel 文档中的工作表一样使用。

要添加页面,使用报表顶层的dp.Page块,并用title参数给它一个标题。下面给出一个例子。

数据面板多页报告

12。多个选项卡并选择报告

当我们需要报告的背景时,这是很有用的。

Datapane 在dp.Select块上提供了两个选择选项:下拉菜单和选项卡。如果选项少于五个,则默认为制表符,否则将使用下拉菜单。要覆盖默认值,请将参数type = dp.Select.DROPDOWNtype=dp.Select.TABS传递给dp.Select块。

数据面板报告

此外,如果你想你可以添加文件和图像,python 对象,嵌入到你的报告。更多详情请点击 链接

我个人认为,如果你能用审美的方式来表现数据,那么数据就是美。数据可视化的最佳方式是交互式可视化。交互式和美观的数据可视化可以提供对数据的有意义的洞察。Datapane 让这个过程变得非常简单。在了解 Datapane 库之前,对我来说,在任何博客上分享数据科学报告是一个大问题,不管我是使用截图还是创建 git 来分享它。

希望这篇文章能对你分享数据科学报告有所帮助。

我还挑了一篇有趣的文章供你进一步阅读……

**

祖贝尔·侯赛因

  • 如果你喜欢这篇文章,请关注我的 中的 了解更多。
  • 连接我上LinkedIn进行协作。**

说美妙的事情:欧洲电视网歌词的情感分析

原文:https://towardsdatascience.com/say-wonderful-things-a-sentiment-analysis-of-eurovision-lyrics-700d9bde1e9d?source=collection_archive---------47-----------------------

实践教程

几十年来,欧洲电视网一直在娱乐观众。这篇文章问,关于这场比赛,欧洲电视网的歌词告诉了我们什么?

由 BRUNO EMMANUELLE 在 Unsplash 上拍摄

在过去的 65 年里,欧洲电视网的歌唱比赛娱乐了来自欧洲和更远地方的观众。这场比赛已经发展到与 50 年代和 60 年代的比赛完全不同的地步。这些早期的比赛是二战后通过跨境广播团结各国的一种方式。虽然它看起来确实不同,但在欧洲电视网歌曲的歌词中,什么发生了变化,什么保持不变?

作为一个欧洲电视网的超级粉丝,我对 Kaggle 的“欧洲电视网歌词”数据集产生了兴趣。该数据集包括从 1956 年开始到 2019 年上次比赛期间欧洲电视网歌曲大赛中所有歌曲的排名和歌词。我从克里斯托贝尔·维斯对金属乐队歌词的分析中获得了方法和代码的灵感。在他的博客中,他分析了金属乐队每十年的歌词。这个博客计划实现同样的目标。虽然有 1564 首歌用多种语言演唱使得这种分析有点困难。

在这篇文章中,我会找到欧洲电视网歌词中常用的词。情感分析然后被应用到欧洲电视网的歌曲中。这将显示几十年来歌曲的积极或消极趋势。最后,我使用 Genius API 从被取消的 2020 年比赛中抓取歌曲。通过将这些方法应用于 2020 年版,我检验了这场竞赛是否会以同样的趋势继续下去。

必需的库

  • 熊猫Numpy 进行数据分析
  • MatplotlibWordcloud 制作漂亮的剧情
  • NLTK 为停用词
  • ScipySklearnStatsModels 获取有用的统计函数
  • 用于情感分析的文本块
  • LyricsGenius 使用 Genius API 抓取歌词
  • 脚本 helpers.py ,最初由克里斯托贝尔·韦亚斯创建,经过修改以帮助这个特殊的数据集

一个笔记本的代码可以在这个链接中找到,所需的文件不包括来自 kaggle 的 Eurovision 歌词数据集,可以在我的 github 中找到。

词频分析

我们首先通过词频分析调查欧洲电视网的热门话题。这里感兴趣的两个函数是 Sklearn 的 CountVectorizer,它计算单词的频率,以及用于绘制单词云的 WordCloud。

第一个预处理步骤是为每首歌曲定义一个唯一单词列表。这意味着本节的分析侧重于歌曲中使用最多的词,而不是总体上使用最多的词。如果我们选择后者,我敢肯定“la”或“oooh”会是最受欢迎的,但这些抒情的选择并不有趣。

另一个重要的预处理步骤是定义停用词。这些词很常见,但没有太多意义。因此,我想把它们从我们的分析中剔除。一些停用词的例子是英语中的“the”,德语中的“der”或瑞典语中的“en”。多年来,比赛中使用了 50 多种语言。这可能是一个问题,因为我们将不得不定义数百个单词来阻止分析。幸运的是,NLTK 包允许我们从竞赛中最流行的语言中定义停用词。它们保存在名为“语言”的列表中。

languages = ['english', 'german', 'spanish', 'french', 'dutch', 'italian', 'danish', 'swedish', 'finnish', 'slovene', 'greek', 'turkish', 'arabic', 'romanian', 'hungarian', 'russian']

敏锐的眼睛可以看出这个列表中的语言不超过 50 种。不幸的是,NLTK 不支持很多 Eurovision 语言。然而,列表中的这些语言包括最流行的。我们会发现,我们的分析并没有因为没有省略挪威语、马耳他语、冰岛语等语言的停用词而受到影响,或者在比利时 2003 年加入的情况下,一种虚构的语言。通过将这些停用词输入 Sklearn 的 CountVectorizer 算法,我们可以找到最流行的词是什么。

每十年欧洲电视网歌词中最流行的词

很明显,从 80 年代到 90 年代,流行语言发生了转变。这很好解释。从 1966 年到 1972 年,然后从 1978 年到 1998 年,各国都被要求用本国语言演唱。更多的参与国将法语作为国家语言,而不是英语。90 年代是一个奇怪的年代,因为这条规则在 1998 年之前一直存在,然而英语“love”却是最流行的。这可能是摩纳哥和卢森堡分别从 1979 年和 1993 年缺席比赛的原因。

“爱”作为一个主题在欧洲电视网的流行是很难改变的。前三十年被法国的“爱情”所主宰。这在 20 世纪 80 年代被“vie”取代。这种用法的一个著名例子是 1986 年比利时唯一一次凭借桑德拉·金的《我的生活》赢得竞赛。在过去的三十年里,“爱”再次位居榜首。尽管欧洲电视网已经发展了,歌唱爱情仍然很受欢迎。“爱”的流行是 2016 年比赛中 interval act 的主题之一,他们剖析了如何写出完美的欧洲电视歌曲。

如果你看过那个视频,你现在应该是欧洲电视网歌曲创作的专家了。

该视频以 2016 年主持人和 2015 年获奖者芒斯·塞默洛问“有公式吗”开始。虽然这种间隔行为集中在欧洲电视网之夜的风格选择,歌词也非常重要。那么哪些选择曾经流行过,现在流行什么呢?为此,为每十年的歌词绘制了一个词云。

50 年代和 60 年代的词汇云

70 年代和 80 年代的词汇云

90、00、10 年代的词云。

恋爱的话题很占优势。这得益于几十年来对“心脏”或“心脏”的反复提及。同样被反复提及的还有“生活”或“vie”,80 年代最流行词汇的获得者。提到“时间”或“温度”是很常见的。这伴随着对“日”和“年”的提及。一些次要但常见的话题包括“世界”、“地球”、“梦”、“夏天”和“太阳”。

在前四个单词云中,内容主要是法语和一点英语。从 90 年代开始,英国人的偏好就很明显了。为了找出任何非英语单词,你必须非常仔细地查看这些单词云。首选语言的变化,从法语到英语,似乎是几十年来欧洲电视网抒情选择的唯一重大变化。尽管这些年来歌曲和表演有所变化,但歌词内容非常相似。

情感分析

随着“爱”、“梦想”和“生活”的过度提及,人们会认为欧洲电视网歌曲的情绪是积极的。本节使用 TextBlob 软件包来分析欧洲电视网的歌词在情感上是积极的还是消极的。情绪由介于-1 和 1 之间的单个评级来表示。如果歌词评分低于-0.05,那么我们认为它总体上是负面的,如果评分高于 0.05,那么我们认为它总体上是正面的,如果它在这些值之间,那么它是中性的。

TextBlob 仅适用于英语,因此在应用情感分析之前必须翻译歌词。这可以通过 TextBlob 实现,我在分析 2020 年竞赛时使用了这一功能,但过度使用这一功能会导致翻译暂停,并停止当天的翻译。因此,大规模翻译变得棘手。幸运的是,翻译的歌词已经在“欧洲电视网歌词”数据集中给出。

哪些歌曲是最积极或最不积极的?将歌词及其翻译插入到 TextBlob 对象中,最积极的是 1963 年来自英国的条目,评分高达 0.81。听听罗尼·卡罗尔的歌曲“说美妙的事情”,我们很容易明白为什么这首歌会名列前茅。这是对 TextBlob 情感分析的一个极好的健全性检查,因为这是一首非常积极的歌曲。那年罗尼以 28 分名列第四。就我个人而言,这是我第一次看到这首歌,它太感人了,我以它命名了这篇博文。

最负面的来自 2009 年以色列的加入,情绪得分为-0.7。这是 Noa & Mira Awad 的歌《一定还有别的路》。重要的一点是,这首歌是用三种语言演唱的,英语、希伯来语和阿拉伯语。在分析中,希伯来语和阿拉伯语没有翻译。这首歌作为最负面的地方带有一个警告,如果这些部分被翻译,它可能会改变位置。然而,值得思考的是为什么这首歌会成为最负面的候选歌曲?首先,犹太人-以色列人和阿拉伯人-以色列人的组合必然会引发争议,而也确实引发了争议。写这首歌是为了强调希望和对共同人性的理解。然而,如果不唱出为什么需要这种希望,就很难唱出这种希望——这导致了一个非常负面的分数。这首歌在决赛中以 53 分的成绩排在第 16 位。

总的来说,Eurovison 的歌曲大多是积极的,平均情绪得分为 0.16,这并不奇怪。

(左)欧洲电视网歌词整体情绪。(右)每十年歌词情绪的密度估计。

有趣的是,当我们关注每十年的情绪时。这由上面的右图表示。查看每个十年的峰值,我们可以看到这些峰值的模式正在向负方向移动。也就是说,平均而言,随着时间的推移,欧洲歌曲的歌词变得越来越消极。下一个问题是,这有意义吗?为此,我们研究了 95%的置信区间。

每十年情感的置信区间

查看这些置信区间,我们可以看到在 70 年代和 80 年代之间有一个变化。由于 80 年代的置信区间与 70 年代的略有重叠,这里没有足够大的变化。虽然 50 年代和 60 年代与 80 年代之间的差别很大。后三十年明显比前三十年更加消极。平均而言,从一开始到现在,欧洲电视网歌词的情绪下降了大约 0.1。这种情绪上的变化非常缓慢,平均而言,歌曲仍然是积极向上的。

欧洲电视 2020 怎么样?

2020 年是欧洲电视网有史以来第一次被取消。这将是一场激动人心的比赛,在欧洲电视网的历史上,冰岛第一次成为夺冠热门。关于我们错过的大赛版本,2020 年欧洲电视网歌曲的歌词能告诉我们什么?为此,我使用 lyricsgenius 包和 genius API 从 Genius 网站上抓取歌词。然后,我使用 TextBlob 将尽可能多的歌词翻译成英语,用于情感分析。

我按照同样的方法进行词频分析,得出字数。这使得“喜欢”成为最常用的词。常用于比喻。例如,德国的条目“暴力的事情”由本·多利奇在他使用的隐喻“像多米诺骨牌一样击倒我”。

大赛 2020 版的歌词 Wordcloud。

“爱”仍然具有强大的影响力,在 2020 年的 41 首歌曲中有 19 首使用了它。历史上常见的主题,包括“世界”和“时间”,也在出现。总的来说,歌词的内容看起来像是这个版本的欧洲电视会跟随以前的欧洲电视歌曲比赛的趋势。

通过调查欧洲电视网歌词的情感,可以看出这些情感的平均值为 0.13,置信区间为[0.075,0.187]。这意味着这些歌词的情绪是积极的,可以与现代版的欧洲歌曲相媲美。

2020 年欧洲电视网最积极的歌曲是伊登·艾儒略的《费克尔·毕丽》,最消极的是桑德罗的《奔跑》。两者都不极端,不足以将前冠军从最积极/最消极的头衔中剔除。

对于欧洲电视网来说,2020 年的比赛似乎一切如常。尽管这个结论有点无聊,但遗憾的是,我们没能在欧洲电视网的舞台上听到这些歌曲的黄金时期。

结论

欧洲电视网改变了他们的外表,但他们歌曲的抒情内容基本保持不变。在过去 70 年中的 6 年里,欧洲电视网歌曲中最流行的词不是“爱”,就是它的法语变体“amour”。有证据表明,歌曲已经变得越来越消极,但这种变化也非常缓慢,平均而言,它们仍然是积极的。就我个人而言,我认为我们被剥夺了 2020 年的比赛,根据我的分析,这将是欧洲电视网正常的比赛。现在,2021 年正在成为一场激动人心的比赛,来自 2020 年比赛的一些艺术家将回到 2021 年。欧洲电视网是一座数据金矿,我只触及了表面(没有双关语的意思)。我希望用一些统计学和数据科学的更重手的工具,来更深入地洞察这场惊人的竞争。

SCADA 报警标准和指标

原文:https://towardsdatascience.com/scada-alarm-standards-metrics-56512db128b0?source=collection_archive---------30-----------------------

工业自动化的开源工具

这篇简短的文章是我们使用开源数据科学工具分析 SCADA 系统产生的警报和事件日志的系列文章的继续。

这是本系列的第二篇文章,上一篇文章是,用 ELK 离线处理 SCADA 报警数据。

在前一篇文章中,介绍了 SCADA 系统及其在工业过程监控中的应用。讨论了警报和事件系统,并介绍了良好警报系统管理实践的要求。

在本文中,我们将简要介绍与报警系统管理和报警生命周期相关的标准。这将为后续文章中更详细的警报事件分析打下基础。

警报管理标准

报警系统管理有一个全球标准,即 IEC ( 国际电工委员会)标准 IEC 62682 流程工业报警系统管理[1]。

IEC 标准建立在 ISA[2]和 EEMUA[3]早期工作的基础上,并与之保持一致。

该标准包括报警系统整个生命周期的管理指南;设计、实施、操作、维护、监控和评估以及变更管理。

这里我们将只关注性能指标和监控。

生命周期

下图展示了一个简化的报警生命周期,它与 IEC 标准中的报警生命周期一致。

对于每个报警点,被监控的物理过程包含正常和异常两种状态。

每个报警点报警包含两个动态属性(因此有四种状态,如下所示)。报警属性包括报警状态(正常|报警)和确认(已确认|未确认)。

图片由作者根据 IEC 62682 图 3 绘制

承认

我们所说的确认警报是什么意思?

对于那些不熟悉工业控制系统报警子系统操作的人来说,更详细地描述报警生命周期是值得的。

通常通过将测量的过程变量与固定限值进行比较来产生警报,如果过程值超出允许范围,则产生警报(上述状态 B)。

未确认的报警通常包含在报警显示中,未确认的状态通常由动画和/或颜色表示。未确认的警报通常也会触发声音警报。

SCADA 系统将为操作员提供一些选择和确认警报的机制。完成后,任何声音警报都将静音,警报显示的颜色和/或动画也会发生一些变化。

当过程条件恢复正常时,警报显示将改变,或者警报将从当前警报列表中删除。

韵律学

IEC 标准在第 16.5 节中提供了报警系统性能指标的建议,这些建议总结在标准的表 7 中,复制如下。

这些指标基于单个操作员可以管理的工作量。

乍一看,指标似乎非常低,因为系统平均每 10 分钟不应超过一个警报。这些值来自对操作员理解警报、确定过程状态、制定和实施纠正措施以及监控纠正措施有效性的能力的研究。

来源:IEC 62682

洪水警报

报警泛滥是指报警率可能超过操作员管理报警的能力的一段时间(即 10 分钟内超过 10 个报警)。

当 10 分钟内的报警数量超过 10 个时,报警泛滥开始,当 10 分钟内的报警数量低于 5 个时,报警泛滥结束。

我们可以收集的与警报洪水相关的一些指标包括:

  • 洪水事件的数量
  • 洪水的持续时间
  • 洪水中的警报数量

震颤警报

震颤警报以高频率触发、清除和重新触发。一个常用的定义是每秒触发 2 次以上的警报。

短暂的警报

短暂的警报是在操作员有机会采取行动之前就消失的警报。标准中没有提供定义,所以我们在最初的分析中将使用 1 秒。

陈旧的警报

陈旧报警是指触发并长时间保持报警状态的报警,标准定义为> 24 小时。

结论

在本文中,我们回顾了国际标准中规定的过程自动化报警系统的性能指标。

在以后的文章中,我们将探讨,

  • 处理 SCADA 报警记录
  • 使用 ELK 在线处理 SCADA 报警记录
  • 先进的 SCADA 警报分析技术(序列挖掘和时间序列分析)

参考

  1. IEC, IEC 62682 流程工业报警系统管理。瑞士日内瓦国际电工委员会。2014 年:国际电工委员会。24–26.
  2. ISA, ANSI/ISA‐18.2‐2009:流程工业报警系统的管理。2009 年:国际自动化学会。
  3. EEMUA, EEMUA-191:报警系统——设计、管理和采购指南。2013 年:英国伦敦工程设备和材料用户协会。

进一步阅读

感谢阅读,希望你喜欢这篇文章。

为了进一步探索,

  • 订阅电子邮件通知
  • 点击文章顶部的“关注”按钮
  • 对于工业 4.0 的所有内容,请查看我的工业数字化转型&工业 4.0 出版物
  • 欢迎在 LinkedIn 上加入我的社交网络(记得提到你已经读过这篇文章)

为了支持媒体作者,考虑一下订阅。

记得点击订阅和关注按钮,

作者图片

可扩展且可靠的 Kubernetes 日志记录

原文:https://towardsdatascience.com/scalable-and-reliable-kubernetes-logging-d47a27b8b04d?source=collection_archive---------22-----------------------

理解大数据

使用可伸缩工具和快速对象存储为大型 Kubernetes 集群构建可伸缩的可靠日志记录解决方案。

为 Kubernetes 构建一个基本的日志解决方案可能就像运行几个命令一样简单。然而,为了支持大规模的 Kubernetes 集群,日志解决方案本身需要具有可伸缩性和可靠性。

在我之前的博客中,我描述了我的 Kubernetes 监控和日志记录解决方案的概况。当时,我使用了一个日志记录的基本设置:Fluentd 在 Kubernetes 节点上收集的日志被直接发送到 Elasticsearch 和 Kibana 集群进行搜索和可视化,Fluentd 和 Elasticsearch 都在 Kubernetes 集群上运行。这是一个适用于小型集群的简单设置。一旦我们迁移到大型生产集群,它将面临以下挑战:1) Fluentd 可能会丢失日志(数据丢失!)如果 Elasticsearch 停机或无法跟上对传入日志的索引。2)日志输入和输出紧密耦合,因此难以管理。3)日志仅存储在 Elasticsearch 中,因此很难扩展到其他工具,例如用于一般日志处理和分析的 Apache Spark。

在这篇博客中,我将描述我是如何通过使用 Fluentd、Kafka、Elasticsearch、Spark 和 Trino 等可扩展工具构建一个可扩展且可靠的 Kubernetes 日志解决方案来应对这些挑战的。我还将强调像 FlashBlade S3 这样的快速对象存储在此解决方案中的作用。最终的架构如下图所示。

可伸缩的 Kubernetes 日志记录

Apache Kafka 作为管道缓冲器和代理

我们首先需要解耦日志输入(Fluentd)和输出(Elasticsearch)。这增加了日志管道的灵活性和可伸缩性。阿帕奇卡夫卡是最受欢迎的解决方案。该设置需要运行 Kafka 集群。请参考我的博客这里关于如何在 Kubernetes 上建立一个开源的 Kafka 集群。另一个选择是用汇合的卡夫卡来代替 Kubernetes 。融合 Kafka 的一个很好的特性是它对分层存储的支持,这允许 Kafka 将冷数据卸载到远程对象存储,如 FlashBlade S3。稍后将详细介绍。现在,让我们把重点放在 Fluentd 和 Kafka 的整合上。

最简单的入门方法是使用来自Fluentd Kubernetes Daemonset repo的 Kafka2 插件,其中包括预构建的 Docker 图像和 Kubernetes 规范示例。Fluentd-Kafka2 Docker 镜像使用环境变量支持基本配置。例如,Kafka 经纪人和主题可以在 Kubernetes 规范文件中设置如下:

containers:
- name: fluentdimage: fluent/fluentd-kubernetes-daemonset:v1.12-debian-kafka2-1env:- name:  FLUENT_KAFKA2_BROKERSvalue: "kafka-headless.kafka:29092"- name:  FLUENT_KAFKA2_DEFAULT_TOPICvalue: "k8s-logs"

因为我需要对 Kafka 生产者确认、时间戳格式和日志源分离进行更多的控制,所以我使用 Kubernetes ConfigMap 扩展了配置文件。之后,日志会根据它们的来源被发送到多个 Kafka 主题中。Fluentd 还附加了其他元数据,如摄取时间戳和源标签。当我们用 Elasticsearch 和 Apache Spark 处理日志时,这些将会很有帮助。

将日志从 Kafka 发送到多个输出

为了将日志从 Kafka 发送到其最终输出,在本例中是 Elasticsearch 和 S3 对象存储,我使用了Kafka Connectelastic search sink 和 S3 sink。Kafka Connect 是一个工具,用于在 Apache Kafka 和其他数据系统之间可扩展和可靠地传输数据。每个连接器都以分布式模式作为自己的 Kafka 消费群运行,这意味着内置了可伸缩性和可靠性。因为日志的输入和输出被 Kafka 解耦了,所以扩展系统并不容易。例如,生态系统中有超过 100 个连接器将数据从 Kafka 传输到不同的输出。

通过将 connector pods 作为 Kubernetes 部署来运行,我们可以轻松地扩展日志记录管道,以适应 Kubernetes 集群的增长。下面是在 Kubernetes 集群中运行的 Fluentd 和 Kafka Connect 的示例。

Fluentd 和 Kafka 连接运行在 Kubernetes

融合卡夫卡与 S3 的分层存储

如果使用融合的 Kafka,其分层存储功能,尤其是与 FlashBlade S3 一起,使 Kafka 在 Kubernetes 环境中更加可扩展、可靠和易于操作。基本思想是将冷数据从 Kafka 代理卸载到远程对象存储,这样 Kafka 只需要在代理中管理最少的本地数据(热数据)。这使得 Kafka broker pods 非常轻便,因此更易于扩展和操作。特别是,数据重新平衡可以快几倍。这也降低了存储成本,因为 Kafka 无需在 S3 维护多个数据副本。

以下是使用 Kubernetes 的汇合操作符为汇合 Kafka 分层存储设置 FlashBlade S3 的示例:

融合式 Kafka 分层存储,采用 FlashBlade S3 配置

部署后,我们可以确认 FlashBlade S3 已在融合用户界面上配置为分层存储后端。

融合式卡夫卡分层存储,带闪光灯 S3

Kafka 客户端仍然通过 Kafka 访问所有数据。如果一个请求命中冷数据,Kafka 将从远程对象存储器下载它,缓存它并提供给客户机。FlashBlade S3 是受支持的分层存储目标。与亚马逊 S3 和其他对象存储不同,FlashBlade S3 的设计速度非常快,因此即使数据是远程存储的,提供冷数据的速度也可以接近热数据。

使用 Elasticsearch、Spark 和 Trino 进行日志分析

大型 Kubernetes 集群每小时或更快地生成数百万个日志条目。分析这些日志本身就是一个大数据问题。Elasticsearch、Apache Spark 和 Trino 是一些最流行的日志分析可扩展工具。

我使用 Elasticsearch 进行流日志分析、搜索和仪表板。使用 Kubernetes 上的弹性云,在 Kubernetes 上部署弹性搜索就像几个命令一样简单。

在 Elasticsearch 中搜索 Kubernetes 日志

使用 S3 搜索可搜索的快照

与融合的 Kafka 一样,Elasticsearch 也支持将冷数据和冻结数据卸载到远程对象存储。最初,S3 的快照仅支持备份目的,随着最新的 7.13 版本,快照变得可搜索!

使用 FlashBlade S3 搜索可搜索的快照

通过在本地保留最少的数据,可搜索的快照使弹性搜索更容易操作和扩展,降低存储成本,同时使用 FlashBlade S3 提高冷数据和冻结数据的搜索速度。

可搜索快照最重要的配置是在 Elasticsearch 冻结节点中设置共享缓存存储和大小。在下面的示例中,我将 100GB 闪存阵列支持的持久卷中的 90GB 设置为缓存。

然后,我们可以创建一个可搜索的快照,并将其存储在 FlashBlade S3 存储库中。

POST /_snapshot/reddot-s3-repo/demo/_mount?storage=shared_cache
{"index": "logstash-2021.05.20","renamed_index": "logstash-2021.05.20-mounted"
}

搜索可搜索的快照索引与搜索任何其他索引相同。如果数据在本地不可用,Elasticsearch 将从 S3 下载索引,在本地缓存并从那里提供服务器以备将来请求。

正如您在下面看到的,我已经创建了三个索引:原始的,一个从常规快照完全恢复,另一个从可搜索的快照恢复。可搜索快照的索引使用磁盘上的零字节,表明数据是由 S3 存储库提供的。

GET /_cat/shards/logstash-2021.05.20*/?v&h=index,shard,prirep,state,docs,store,node

使用磁盘上的零字节对可搜索快照进行索引

ETL 和 SQL 分析

虽然 Elasticsearch 非常擅长流日志分析,但它并不能满足所有的日志分析需求。例如,我们使用 Apache Spark 和 Trino 进行 ETL 和 SQL 分析。为此,我们首先需要将原始日志存储在 S3。如上所述,我们使用 Kafka Connect S3 接收器将原始 json 格式的日志发送到 S3。我们还按照来源和摄取时间对数据进行分离/分区,以便于处理。日志存储在 S3,如下所示。

存储在 S3 flash blade 的 Kubernetes 日志

一旦日志被存储在 S3 桶中,我就可以使用 Spark 来研究 JupyterLab 笔记本中的数据。

在 JupyterLab 中用 Spark 记录 ETL

我还可以使用 Trino 直接对 json 数据运行 SQL 查询。

CREATE TABLE IF NOT EXISTS k8s_logs(ingest_ts varchar, log varchar,kubernetes ROW(container_image_id varchar,container_name varchar,...),dt varchar,hour varchar
)
WITH (format='json',partitioned_by = ARRAY['dt', 'hour'],external_location='s3a://deephub/logs/k8s-logs');

使用 Trino 的 SQL 分析

因为所有的工具,包括 JupyterLab、Spark 和 Trino,都运行在 Kubernetes 集群中,所以我可以立即开始这些分析,并轻松扩展。我之前的博客这里和这里描述了如何与 Kubernetes 和 S3 一起运行 Spark 和 Trino(前 PrestoSQL)。

摘要

在这篇博客中,我描述了如何扩展我的日志解决方案来收集、存储和处理 Kubernetes 集群中生成的大量日志。虽然它是为 Kubernetes 日志记录而构建的,但该解决方案非常灵活,也适用于其他大数据用例。

为了使解决方案具有可伸缩性和可靠性,我们使用的核心工具需要具有可伸缩性和可靠性。云原生趋势(公共云或本地 Kubernetes)鼓励 Kafka 和 Elasticsearch 等传统大数据系统从本地数据复制转向远程对象存储。正因为如此,像 FlashBlade S3 这样的快速对象存储在解决方案中起着重要作用。

扩展日志解决方案变得更加容易。

使用 Spark 的可扩展机器学习

原文:https://towardsdatascience.com/scalable-machine-learning-with-spark-807825699476?source=collection_archive---------12-----------------------

分布式算法、Map-Reduce 范式、在单机上使用 Spark MLlib 的可扩展 ML、带有 Docker 的 AWS EMR 集群& Nvidia RAPIDS。

自 21 世纪初以来,由于谷歌、网飞、Youtube、亚马逊、脸书等互联网巨头的出现,收集的数据量大幅增加。临近 2010 ,当手机大受欢迎时,另一个“数据浪潮”已经到来。到 2020 年,我们预计当物联网设备变得无孔不入时,数据将会再次呈指数级增长。在这种背景下,构建可扩展的系统成为机器学习解决方案的必要条件。

Spark 中的机器学习:从零到英雄版

任何解决方案都主要取决于这两种类型的任务:

a)计算量大:在 2000 年之前,被称为“超级计算机”的并行处理箱在计算量大的任务中很流行。在 2005 年之前,像 MPI 和 PVM 这样的并行处理库在计算繁重任务中很流行,TensorFlow 就是基于这种并行处理库设计的。

b)数据量大:基于关系代数的数据库是在 20 世纪 70 年代设计的,当时硬盘存储非常昂贵。因此,该设计的目的是通过将较大的表格分成较小的表格,并使用关系(规范化)将它们链接起来,从而减少数据冗余

因此,传统的数据库如 mySQL、PostgreSQL、Oracle 等。不是为扩展而设计的,尤其是在上面提到的数据爆炸的环境中。因此,NoSQL 数据库是为满足不同情况而设计的:

  1. 用于存储文本文档
  2. Redis,Memcache: 用于快速键值查找的分布式哈希表
  3. 弹性搜索:搜索文本文档
  4. HBase 和 Cassandra: 柱状商店
  5. Neo4j 和 Grakn: 图形数据库。

然而,大型数据集上的机器学习和深度学习解决方案是计算密集型和数据密集型。因此,为了制造可扩展的 AI/ML 解决方案,解决方案必须同时满足这两种需求。

图一。作者使用 MPI 并行实现光子映射

2004 年, Jeff Dean 等人发表了开创性的 MapReduce 论文来处理数据繁重的任务[2]。2006 年, Hadoop 实现了 MapReduce 并设计了一个名为 HDFS 的分布式文件系统,将单个大文件拆分存储在多台电脑的磁盘中。这个想法是将巨大的数据库分散在多个主板的硬盘上,每个主板都有独立的 CPU、RAM、硬盘等,通过一个快速局域网互连。

然而, Hadoop 将所有中间数据存储到磁盘,因为它是在 2000 年代设计的,当时硬盘价格暴跌,而 RAM 价格仍然很高。在 2010 年,当 RAM 价格下降时, Spark 诞生了一个大的设计变化将所有中间数据存储到 RAM,而不是磁盘。

火花对双方都有好处,

i)数据密集型任务:因为它使用 HDFS &

ii)计算繁重的任务:因为它使用 RAM 而不是磁盘来存储中间输出。例如:迭代求解

由于 Spark 可以利用 RAM,它成为机器学习中迭代任务的有效解决方案,如随机梯度下降( SGD )。所以是这个原因, Spark MLlib 变得如此受机器学习欢迎,与 Hadoop 的 Mahout 形成对比。

此外,要使用 TF 进行分布式深度学习,您可以使用,

  1. 多个 GPU 在同一个盒子上 (或)
  2. 不同机箱上的多个 GPU(GPU 集群)

虽然今天的超级计算机使用 GPU 集群来执行计算密集型任务,但您可以在这样的集群中安装 Spark,使其适用于分布式深度学习等任务,这些任务都是计算和数据密集型的

Hadoop 和 Spark 简介

Hadoop 中主要有两个组件,

  1. Hadoop 分布式文件系统(HDFS): 一个容错的分布式文件系统,Hadoop 和 Spark 都使用。HDFS 支持将一个大文件分割成“n”个块,保存在“n”个节点中。当访问文件时,必须通过 LAN 跨节点访问不同的数据块。
  2. Map-Reduce: 给定一个跨大量数据、分布在多个节点上的任务,必须进行大量的数据传输,并且需要分布处理。让我们详细研究一下这个问题。

地图简化范例

考虑在 900 GB 的大型分布式文件中查找词频的任务。HDFS 将支持将大文件拆分为 3 个区块,分别位于 P1、P2 和 P3,每个区块 300 GB,并在 3 个节点中各保留一个。

任何 Hadoop 代码都会有 3 个阶段:

  1. Map : Mapper 函数会通过数据,存储在各个节点的磁盘中,并递增输出字典中的字数。它将在每个分布式机器上独立执行

图二。字数统计图-简化工作流程(图片由作者提供)

2。Shuffle: Hadoop 自动在局域网内移动数据,这样相同的键就被组合在一个盒子里。

3。Reduce: 一个函数,它将使用字典并将相同关键字的值相加(以计算总数)。

要在 Hadoop 中实现一个函数,只需要编写 Map & Reduce 函数。请注意,Hadoop 中的每个 Map-Reduce 操作之间都有磁盘 I/O。然而,几乎所有的 ML 算法都是迭代工作的。SGD[下面的等式]中的每个迭代步骤对应于一个 Map-Reduce 操作。在每个迭代步骤之后,中间权重将被写入磁盘,占总收敛时间的 90%。

等式:ML&DL 迭代中的权重更新公式

作为解决方案, Spark 诞生于 2013 年即将磁盘 I/O 操作替换为内存操作。借助Mesos——分布式系统内核——Spark 缓存每次迭代后的中间数据集。因为每次迭代的输出都存储在 RDD 中,所以只需要一次磁盘读写操作就可以完成 SGD 的所有迭代。

Spark 建立在弹性分布式数据集(RDD)之上,存储在主内存中的容错不可变分布式数据集集合。在 RDD 之上,DataFrame API 被设计为抽象掉它的复杂性,并简化在 Spark 上进行机器学习。

RDDs 支持两种类型的操作:****

  1. 转换: 从现有数据集创建新的数据集
    映射:通过函数
    reduce by key:使用函数
    过滤器:仅选择那些使用函数的元素
  2. 动作: 对数据集运行计算后返回值
    减少:使用某个函数
    收集:返回输出数据集的所有元素
    SaveAsTextFile:将数据集的元素作为文本文件写入。

Spark 中的所有转换都是惰性的,也就是说,它们只在“动作”需要结果时才被计算。在下面的代码中,由于懒惰,没有立即计算行长度。只有当“reduce”运行时,Spark 才会将计算分解为在不同机器上运行的任务,以计算总长度。**

lines = sc.textFile("data.txt")
lineLengths = lines.map(lambda s: len(s))
totalLength = lineLengths.reduce(lambda a, b: a + b)

下面是一个简单的数据转换示例,用于计算存储在 3 个分区的分布式 RDD 中的键的出现次数:****

计算字母“a”和“b”的出现次数

作为 Map 简化逻辑回归

SGD 迭代中最昂贵的操作是跨越所有数据点的梯度操作[公式。【以上】。如果数据集很大,比如说“n”亿个数据点,那么我们可以将梯度计算分布在“k”个不同的盒子中。

  • ****贴图阶段:每个盒子将计算 n /k 个亿点的梯度
  • ****简化阶段:使用相同的键对每个框中的部分总和求和
  • 所有点的损失梯度= ∑部分和
  • 因此,很容易计算 w_new 并存储在每个节点的存储器中

这就是你如何分发任何基于优化的 ML 算法。然而,对于分布式 LR 实现,请参见 Hadoop 与 Spark 的性能比较。

图三。运行时间对比:Hadoop vs Spark [3]

由于 Spark RDDs 允许在内存中执行多种映射操作,因此不需要将临时数据集写入磁盘,因此速度提高了 100 倍。注意,第一次迭代花费的时间几乎相同,因为 Hadoop 和 Spark 都必须从磁盘读取。但是在后续的迭代中,Spark 的内存读取只需要 6 秒,而 Hadoop 的磁盘读取需要 127 秒

此外,一个 ML 科学家不需要编码映射和简化函数。大多数 ML 算法包含在 Spark MLlib 中,所有数据预处理都是使用 Spark SQL 完成的。

火花安装和设置

您可以在其中任何一个中设置 Spark,

  1. 您的本地机顶盒(或)
  2. 使用 AWS EMR 或 Azure Databricks 的托管集群

下面我们将看到两种方式。首先,在完成 Spark 本地系统设置之后,我们将在您的个人机器上运行一些普通的并行任务。然后我们会拿一个更复杂的 ML 项目,在 Spark Docker、AWS EMR & Spark Rapids 中运行。

Spark:本地系统设置

  1. docker pull jupyter/pyspark-notebook
  2. docker run-it-p 8888:8888 jupyter/pyspark-notebook
  3. 单击带有 Auth-Token 的链接或转到 http://localhost:8888/ 并复制粘贴令牌
  4. 现在,您可以在 Jupyter 或终端中执行 Spark 代码。要在 docker 中执行,只需运行spark-submit pi-code . py****

任务 1:估计圆周率(π)的值

取一个单位圆,考虑一个外接该圆的正方形。

  • 单位正方形的面积= 1
  • 因为是单位圆,所以圆的面积= π
  • 四分之一圆弧的面积= π/4
  • *因此,π = 4 四分之一圆弧的面积

图 4。圆的面积=红色点数/总点数。(图片由作者提供)

四分之一圆弧的面积可以通过下式计算:

  1. ****数值方法:使用积分
  2. ****蒙特卡罗方法:使用随机抽样寻找答案

在蒙特卡罗方法中,

  • 取(x,y)点的均匀分布从 0 到 1(即正方形内)
  • 四分之一区域的面积= %圆内的点,即𝑥+𝑦<1
    eg:在 1000 个随机点中,如果“k”个点在圆内,那么阴影区域的面积= k/1000

这些操作是可并行化的,因为为了检查一个点是否落在圆内,节点之间没有依赖性。在 pyspark 代码下面,一旦在 spark 本地设置上运行,将输出更接近π=3.14 的值,因为我们增加了随机点数 (NUM_SAMPLES)

  • 随机函数将生成一个 0 到 1 之间的数字。
  • “inside”函数运行一百万次,只有当随机点在圆内时才返回“True”。
  • sc.parallelize()将创建一个分解为 k=10 个实例的 RDD。
  • 过滤器将应用传递的函数。

任务 2:计算字数

要在大型分布式文件中查找词频,只需将下面代码中的本地文件路径替换为 HDFS 文件路径。

map 函数将创建一个列表列表& flatMap 将列表合并成一个列表。

任务 3:数据预处理

大多数数据预处理可以通过 DataFrame API 使用 Spark SQL 来完成。在 Spark 数据帧上执行的 Spark SQL 查询将在 Spark 中执行之前转换为 Map 和 Reduce ops。

Spark MLLib 和 ML 管道简介

Spark MLLib 是一个受 sklearn 启发的库,它包含了流行 ML 算法分布式实现。与 sklearn 的主要区别是使用 sc.parallelize()函数将数据拆分到多个框中。

将磁盘上的原始数据转换成最终模型所需的所有步骤称为 ML 管道。Pipeline()依次包含输入和输出级。例如,记号赋予器计数矢量化器逻辑回归流水线序列可以编码为:

因此,训练数据首先被馈入 tokenizer,然后馈入 CV,然后馈入 LR。若要测试模型,请调用 model.transform()。您还可以进行分布式超参数调整,即在具有不同超参数的多个机器上运行相同的架构。然而,将一个大模型分布存储和训练在不同盒子中的 GPU 的 VRAM 中是稍微复杂的。

Spark 中的 ML 算法:自定义实现

只有当一个算法可以被分成独立的子任务时,它才可以被并行化。具体来说,双字节排序可以并行,因为操作序列与数据无关,与合并排序无关。类似地,一些 ML 算法是平凡的可并行化的,而另一些则不是。

****a)琐碎并行:KNN(k-最近邻)为例。

  • 将数据集 D 分成“n”个盒子。例如** : 40K 分 4 盒**
  • 从每个框中的每个 10K 点中找出前 k 个最近的点
  • 将所有 4k 个点转移到 1 个盒子中,并找到前 k 个最近的点。

**b)非平凡的平行:**以 GBDT 为例。在 GBDT 中的每一棵决策树都是基于先前决策树的剩余部分构建的。因此,训练 GBDT 本质上是一个顺序操作,而不是并行的。

然而,我们可以并行构建每个决策树,因为用于左右子树的数据集是独立的。因此,xgboost 在树级并行化,即左右子树在 2 个节点上独立训练。

基于随机森林的时间序列预测

让我们在独立、Spark 本地&集群模式下解决一个 ML 问题。****

问题陈述:**从 1990 ~ 2020 年代记录一个地点的每日气温、风、降雨和湿度。鉴于这些特征,建立时间序列模型预测 2021 年的湿度。为了验证模型,使用 2020Q4 湿度值进行比较,使用公制。

下面实验的完整源代码**可以在 这里 找到。**

图五。输入数据集特征

图六。湿度值的时间序列性质清晰可见

首先,转换数据以获得对预测湿度有用的新特征。

图 7。新功能:日、周和规模=温度*降雨量

A.独立实现

现在我们可以用上述特征训练 sklearn 的随机森林回归器。

预测湿度值和实际湿度值过于接近(如下图所示)

图 8。2020Q4 湿度:蓝红线表示实际和预测湿度(独立)

B.Spark 本地实施

首先,你需要做上面提到的 Spark 本地系统设置步骤。然后,你可以用 PySpark 的 RandomForestRegressor 做上面同样的事情。

要在 Spark MLlib 中为机器学习模型提供功能,您需要使用 Spark ML 库中的 VectorAssembler 模块将多个列合并为一个向量列。

图九。使用 VectorAssembler 组合特征

然后,您可以运行类似于独立解决方案的训练和测试模块。

图 10。预测回归值(湿度)

图 11。2020Q4 湿度:实际和预测(局部火花)

PySpark 中的分布式随机森林回归器实现似乎也遵循这一趋势,尽管均方误差值稍高一些。即便如此,在 64G RAM 的四核系统上,spark 本地实现的运行时间还是快了3 倍。

图 12。独立:2.14 秒。Spark Local: 0.71 秒用于随机森林回归训练

C.火花簇:AWS 弹性贴图 Reduce + Docker

为了获得计算和数据规模的双重优势,上述解决方案需要跨多个机器部署。然而,使用本地机器用 Spark 设置集群是很耗时的。

相反,您可以使用 Amazon EMR 创建一个集群,负载运行在 Amazon EC2 实例上。亚马逊 EMR 是一个托管集群平台,简化了运行大数据框架,如 Apache Hadoop 和 Spark。

从 Amazon EMR 6.0.0 开始,您可以使用 Docker 容器来处理库依赖关系,而不是将其安装在每个 EC2 集群实例上。但是您需要在 AWS EMR 中的“集群创建”期间配置 Docker 注册表并定义附加参数。

  1. 创建一个 docker 图像(或修改一个 Docker 图像)

创建一个目录和一个 requirements.txt 文件。requirements.txt 文件应该包含 Spark 应用程序所需的所有依赖项。

mkdir pyspark
vi pyspark/requirements.txt
vi pyspark/Dockerfile**Sample requirements.txt:**python-dateutil==2.8.1
scikit-image==0.18.1
statsmodels==0.12.2
scikit-learn==0.23.2

在目录中创建一个 Dockerfile 文件,包含以下内容。安装了特定的 numpy 版本,以从 EMR 笔记本确认 docker 执行。

**FROM amazoncorretto:8**RUN yum -y update
RUN yum -y install yum-utils
RUN yum -y groupinstall developmentRUN yum list python3*
RUN yum -y install python3 python3-dev python3-pip python3-virtualenv python-devRUN python -V
RUN python3 -VENV PYSPARK_DRIVER_PYTHON python3
ENV PYSPARK_PYTHON python3RUN pip3 install --upgrade pip
RUN pip3 install 'numpy==1.17.5'RUN python3 -c "import numpy as np"**WORKDIR /app****COPY requirements.txt requirements.txt**
**RUN pip3 install -r requirements.txt**

使用以下命令构建 docker 映像。

****2。创建 ECR 存储库。标记并将本地构建的图像上传至 ECR。

**sudo docker build** -t local/pyspark-example pyspark/
**aws ecr create-repository** --repository-name emr-docker-examples**sudo docker tag** local/pyspark-example 123456789123.dkr.ecr.us-east-1.amazonaws.com/emr-docker-examples:pyspark-example**sudo aws ecr get-login** --region us-east-1 --no-include-email | **sudo docker login** -u AWS -p <password> https://123456789123.dkr.ecr.us-east-1.amazonaws.com**sudo docker push** 123456789123.dkr.ecr.us-east-1.amazonaws.com/emr-docker-examples:pyspark-example

也可以上传到 Docker Hub,给' Docker . io/account/Docker-name:tag '代替上面的 AWS ECR 图像 URI。

3。在 EMR 中创建带火花的集群

打开亚马逊电子病历控制台(参考 此处 )

点击“进入高级选项”而不是“快速选项”来启用,

  • ****Jupyter 企业网关:代表远程笔记本帮助启动内核的网络服务器。
  • JupyterHub :托管 Jupyter 笔记本服务器的多个实例。
  • Apache Livy: 支持通过 REST 接口与 Spark 集群交互的服务。

根据所需的并行度,选择每种节点类型的的节点数量****

  • ****主节点:通过协调其他节点之间的数据和任务分配来管理集群
  • ****核心节点:在 HDFS 运行任务和存储数据(多节点至少一个)
  • ****任务节点:只运行任务&不在 HDFS 存储数据(可选)

因此,核心节点增加了数据和计算并行性,而任务节点仅增加了计算并行性。

图 13。如何使用 AWS EMR 创建 Spark 集群

4。进入“软件设置”下的配置**

为了避免 userid 错误,请在下面的 JSON 中设置“livy.impersonation”

[{"Classification": "container-executor","Configurations": [{"Classification": "**docker**","Properties": {"docker.trusted.registries": "local,centos,**123456789123.dkr.ecr.us-east-1.amazonaws.com"**,"docker.privileged-containers.registries": "local,centos,**123456789123.dkr.ecr.us-east-1.amazonaws.com**"}},]},{"Classification":**"livy-conf"**,"Properties":{**"livy.impersonation.enabled": "true"**,"livy.spark.master":"yarn","livy.spark.deploy-mode":"cluster","livy.server.session.timeout":"16h"}},{"Classification": "core-site","Properties": {"hadoop.proxyuser.livy.groups": "*","hadoop.proxyuser.livy.hosts": "*"}},{"Classification":"hive-site","Properties":{"hive.execution.mode":"container"}},{"Classification":**"spark-defaults"**,"Properties":{"spark.executorEnv.YARN_CONTAINER_RUNTIME_TYPE":"**docker**","spark.yarn.am.waitTime":"300s","spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_TYPE":"**docker**","spark.executorEnv.YARN_CONTAINER_RUNTIME_DOCKER_CLIENT_CONFIG":**"hdfs:///user/hadoop/config.json"**,"spark.executorEnv.YARN_CONTAINER_RUNTIME_DOCKER_IMAGE":**"123456789123.dkr.ecr.us-east-1.amazonaws.com/scalableml:s3spark"**,"spark.executor.instances":"2","spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_DOCKER_CLIENT_CONFIG":**"hdfs:///user/hadoop/config.json"**,"spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_DOCKER_IMAGE":**"123456789123.dkr.ecr.us-east-1.amazonaws.com/scalableml:s3spark"**}}
]

5。从纱线启用 ECR 访问

为了使 YARN 能够从 ECR 访问图像,我们设置了容器环境变量 YARN _ CONTAINER _ RUNTIME _ DOCKER _ CLIENT _ CONFIG。

但是,我们需要生成 config.json 并放入 HDFS,以便它可以被集群上运行的作业使用。为此,登录到一个核心节点并执行以下命令。

**ssh -i permission.pem** [**hadoop@**ec2-1-23-45-678.compute-1.amazonaws.com](mailto:hadoop@ec2-3-83-46-34.compute-1.amazonaws.com)**aws ecr get-login-password** --region us-east-1 | sudo docker login --username AWS --password-stdin 123456789123.dkr.ecr.us-east-1.amazonaws.com**mkdir -p ~/.docker**
**sudo cp** /root/.docker/config.json ~/.docker/config.json
**sudo chmod** 644 ~/.docker/config.json**hadoop fs -put** ~/.docker/config.json /user/hadoop/

图 14。让 ECR 接触纱线

6。EMR 笔记本和配置

您可以创建 jupyter 笔记本,并连接到运行 Hadoop、Spark 和 Livy 的 Amazon EMR 集群。EMR 笔记本独立于集群保存在 AWS S3 中。

  • 点击亚马逊电子病历控制台上的“笔记本”和“创建笔记本”
  • 选择运行笔记本的群集
  • 在 Jupyter 笔记本中,给出下面的配置作为第一个单元格。****
**%%configure -f**
{"conf": { "spark.pyspark.virtualenv.enabled": "false","spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_TYPE":"**docker**","spark.executorEnv.YARN_CONTAINER_RUNTIME_DOCKER_CLIENT_CONFIG":"**hdfs:///user/hadoop/config.json**","spark.executorEnv.YARN_CONTAINER_RUNTIME_DOCKER_IMAGE":"**123456789123.dkr.ecr.us-east-1.amazonaws.com/scalableml:s3spark**","spark.executor.instances":"2","spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_DOCKER_CLIENT_CONFIG":"**hdfs:///user/hadoop/config.json**","spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_DOCKER_IMAGE":"**123456789123.dkr.ecr.us-east-1.amazonaws.com/scalableml:s3spark**"}
}
  • 因此,您可以在 EMR 笔记本中使用笔记本范围的 docker 来解决依赖性。现在您可以在后续单元格中执行 PySpark 代码。
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("docker-numpy").getOrCreate()
sc = spark.sparkContextimport numpy as np
**print(np.__version__)**

理想情况下,如果上面的代码在构建的 docker 中运行,您应该看到 1.17.5 的 numpy 版本。如果没有,您需要找到 S3 集群日志路径。

  • 您可以使用上面的 Spark 独立代码,只是输入数据应该从 S3 读取,如下所示,并转换为 RDD。

如下图所示,随着 EMR 集群中节点数量的增加,RandomForest 训练工作负载所用的时间稳步减少。节点间通信的基本开销将保持不变,即使是小数据集。** 因此,当训练数据集变大时,图形会下降得更陡。**

图 15。不同集群规模的 EMR 集群性能

或者,在通过 SSH 连接到 主节点 之后,您可以使用 spark-submit 命令在集群中执行 PySpark 代码。

  • 设置DOCKER _ IMAGE _ NAME&DOCKER _ CLIENT _ CONFIGenv 变量
  • 执行。带有 spark 的 py 文件-提交 &部署模式为“集群”
  • 您可以从 S3 读取输入并写出输出,如下所示。

但是,通过运行在 Spark 集群上的 Notebook 提交 Spark 作业更方便,同时 docker 可以解析所有集群节点中的依赖关系。

D.GPU 集群:AWS EMR + Spark RAPIDs

您可以使用“Nvidia Spark-RAPIDS plugin for Spark”来加速使用连接到 EC2 节点的 GPU 的 ML 管道。核心和任务实例组必须使用 EC2 GPU 实例类型,而主节点可以是非 ARM 非 GPU。

Spark RAPID 将加速数据处理和模型训练,而无需任何代码更改。要创建 GPU 加速集群,您可以遵循 C 节中的步骤,但要做以下更改:

  1. 在这里 使用步骤 5** 给出的 JSON,而不是 C 节中的 config**
  2. 保存自举脚本给 此处 在 S3 为my-bootsap-action . sh
  3. 将步骤 2 中的 S3 文件路径作为引导脚本(在 GPU 上使用 YARN)
  4. 到主节点的 SSH 并安装依赖项。要执行时序项目,请执行下面的命令。
pip3 install sklearn statsmodels seaborn matplotlib pandas boto3

不需要更改代码。下面是 Spark-RAPIDS 集群上的随机森林回归训练的时间比较,该集群由 1 个 m 5.2 x 大型主节点和 2 个 g4dn . 4x 大型核心节点组成,具有其他执行模式。

图 16。时间比较:独立 vs Spark 本地 vs RAPIDS

然而,在上面的例子中,由于数据集很小,所以速度提高不多。让我们对前面的“字母表计数”代码做一个修改,以比较 Spark Local 和 Spark RAPIDS 之间的时间统计。下面的代码生成 100M 个随机字符串和计数元组,以馈入分布式计数操作。

图 17。AWS EMR 笔记本中的 Spark 作业进度报告

图 18。时间比较:火花本地 vs 火花急流

结束语

为博客的长度道歉。如果您已经读到这里,您应该考虑用一些分布式编码来弄脏您的手。本文为您提供了 Spark 的历史和逻辑背景,以及在 Local、AWS EMR 和 Spark Rapids 上的多个示例实现。如果它激励你进一步探索,那么它就达到了它的目的。

以上实验的完整源代码**可以在 这里 找到。**

如有任何疑问或建议,可在此 联系我https://www.linkedin.com/in/ananduthaman/

参考

[1]www.appliedaicourse.com

[2]迪安、杰弗里和桑杰·格玛瓦特。“MapReduce:大型集群上的简化数据处理。” 美国计算机学会通讯 51.1 (2008)**

[3]巴加瓦蒂、阿伦库马尔&察切娃、安吉丽娜。(2017).分布式环境中基于规则的系统:调查。 10.11159/cca17.107**

[4] PySpark 文档:https://spark.apache.org/docs/latest/api/python/index.html

[5] 火花 MLLib:https://spark.apache.org/docs/1.2.1/mllib-guide.html

[6]Spark SQL:https://Spark . Apache . org/docs/2 . 2 . 0/SQL-programming-guide . html # SQL**

【7】AWS EMR:https://docs . AWS . Amazon . com/EMR/latest/release guide/EMR-spark-docker . html**

[8]AWS EMR with Docker:https://AWS . Amazon . com/blogs/big-data/simplify-your-spark-dependency-management-with-Docker-in-EMR-6-0-0/**

[9] GPU 集群(Spark-Rapids):https://docs . AWS . Amazon . com/EMR/latest/release guide/EMR-Spark-Rapids . html

使用 Tensorflow 2 的可扩展机器学习。X

原文:https://towardsdatascience.com/scalable-machine-learning-with-tensorflow-2-x-a9aab8a2139d?source=collection_archive---------46-----------------------

理解大数据

使用 Tensorflow 2 在一台或多台机器上进行大规模 ML 训练,无需更改代码。x 特征

由 Charles Deluvio 在 Unsplash 上拍摄的照片

关于机器学习的一个惊人事实是,如果给予足够的计算能力,即使最简单的模型也可以产生良好的结果。这一方面迅速普及了机器学习工作负载的分布式计算的采用,并且出现了许多分布式计算框架来满足这种需求。这些新生的 ML 框架必须解决分布式计算中的一个基本困难:从共享内存模型(SMM)到分布式内存模型(DMM)的飞跃。这一飞跃是一件大事,并且导致了许多分布式框架的失败。本文将重点介绍 Tensorflow 平台采用的分布式计算方法,尤其是 2.X 版的增强功能。这种新方法在很大程度上成功地弥合了共享内存模型和分布式内存模型之间的差距。此外,这一新模型为异构硬件加速器(如图形处理单元(GPU)和张量处理单元(TPU))引入了统一的方法。本文将以分布式生成对抗网络(GAN)的开发为例,讨论这些最新进展。

介绍

本文的重点是在开发机器学习模型时,提出从共享内存模型到分布式内存模型的无缝过渡。但是,让我们首先讨论一下为什么这种转变如此重要,为什么首先应该进行这种转变。我们大多数人只熟悉共享内存模型。这是一种你只需要处理一台机器的模型,你可以在计算线程之间自由传递变量,因为这台机器中的所有内核都可以访问同一个内存,因此得名“共享内存”。现在,考虑分布式内存模型(DMM)。在分布式范例中,你必须意识到计算线程存在于不同的机器中,你经常需要知道它们的网络地址,你还必须知道如何在它们之间移动数据。您必须考虑数据序列化、机器本机数字格式等等。因此,DDM 显然更复杂,人们可能会提出这样的问题:既然如此困难,为什么还要去那里?

这个问题的简单答案是,尽管共享内存模型对开发人员来说容易得多,但它也有局限性。您拥有单机抽象的优势,但也受限于单台机器的内核数量和内存量。如果您的模型变得比单机中当前可用的 RAM 内存更大,该怎么办?如果您的模型在计算上受到限制,需要数百或数千个内核,该怎么办?这是你进入可扩展机器学习领域的时候,这是你需要忍受分布式内存模型的复杂性以便获得无限可扩展性的好处的时候。

两个加速器的故事

在机器学习领域,当开发硬件加速器时,分布式和共享内存模型之间的差异变得非常明显,如 NVIDIA 的图形处理单元(GPU)和谷歌张量处理单元(TPUs)。最近推出的高端 GPU 允许在单个机器的抽象中获得大量内存和计算能力,并受益于共享内存模型。但是如果您的模型需要 50G 以上的加速器内存和数百个加速器核心,那么您将不得不处理分布式内存模型。另一方面,Google Cloud TPUs 是为分布式内存模型设计的。例如,TPU 加速器有自己的 IP 地址和通信协议(gRPC)。幸运的是,开发人员不需要处理通信层,因为它由加速线性代数(XLA)层负责,并由 Tensorflow API 向开发人员公开。PyTorch 也有 XLA 支持,但本文将只关注 Tensorflow 接口。但是分布式计算的本质确实让习惯于共享内存模型的开发人员感到惊讶。例如,在 TPU 架构中,你通常不会使用共享内存中的变量来来回回地传递数据,相反,你必须以一种称为 TFRecord 的格式来序列化成批记录中的训练数据。这种差异让许多不熟悉数据序列化的开发人员望而却步。然而,采用 TFRecord 数据序列化带来了很多好处,因为它基于简单而有效的“proto buf”架构,该架构已在谷歌大规模使用多年。在任何情况下,使用 GPU 或 TPU 集群的大规模部署都需要数据序列化步骤,因此在采用分布式计算架构时,这实际上是一种“必要的邪恶”。下图显示了使用 GPU 或 TPU 加速器时共享和分布式架构的基本差异。

作者图片

除了数据序列化步骤,在采用分布式计算进行机器学习时,还有其他独特的方面需要注意。Tensorflow API 在某种程度上成功地隐藏了模型训练期间发生的进程间通信的复杂性。但是,在 Tensorflow 版本 1 中。x 访问 TPU 加速器只能通过 TPUEstimator 接口,这严重限制了可以为 TPU 开发的机器学习应用的类型。TF 1 版本中的 TPU 接口。x 基本上期望被精确地传递一个模型(网络),然后被编译成 TPU 格式,并被运送到 TPU 进行训练。如下图所示:

作者图片

这种架构非常棒,因为它大大减少了与 CPU 的通信,从而提高了性能。但是在版本 1 中。x 这个特性仅限于一个模型,因此很难开发多模型的 ML 应用程序,如强化学习、生成性对抗网络(GANs)和许多其他应用程序。例如,GAN 的实现需要一种巧妙的方法来将发生器和鉴别器模型打包到卷积网络中,就像在 TF-GAN,中所做的那样,但是这样的框架使得开发人员很难获得他们自己的定制。在 TF-GAN 中,发生器和鉴别器网络被打包成一个 CNN,然后可以用 TPUEstimator 将其训练成一个模型。TF-GAN 将实现细节隐藏在一个名为 GANEstimator 的接口后面。通过这种方式,TF-GAN 设法在 TF 1.X 版本的 TPU 上运行。然而,开发人员经常需要改变网络逻辑以适应他们的应用。例如,GAN 建模的最新进展利用了定制的“编码器”组件,它实际上是沿着鉴别器和发生器模型训练的第三个模型。在优化用于生成图像空间的“潜在空间”方面也有很多研究。这种定制在 TF-GAN 中是不可能的。好在 Tensorflow 2。x 引入了定制训练循环特性,使得使用梯度带结构同步训练任意数量的模型成为可能..

张量流 2。x 版本还引入了使用@tf.function 装饰器定义的编译函数的概念。当 XLA 编译层遇到这样的函数时,它将利用惊人的“自动签名”功能来自动导出计算图形,然后可以编译该图形并将其发送到 TPU 进行执行。该功能如下所示:

作者图片

自定义函数的引入极大地增强了 TPU 的灵活性,使它们在灵活性和易开发性方面类似于 GPU。Tensorflow 2 中还有其他几个新功能。这实际上使得开发可以在 GPU 或 TPU 上透明运行的 ML 模型成为可能,而无需任何重大修改。这些特性中最重要的是 tf.strategy()概念。tf.strategy 对象用于定义实际的分布式计算范例。tf.strategy 使得并行运行定制函数成为可能,这使得开发人员可以相当容易地实现分布式模型训练技术,例如模型并行和数据并行。在 Tensorflow 2 中。x 还有对 tf.data 对象的扩展,使得指定数据如何在几个内核之间分配变得容易。有了这些介绍,开发人员现在可以指定如何在几个内核之间分配计算和数据。也许最重要的一点是,tf.strategy 和 tf.data 对象对 GPU 和 TPU 的作用是相同的,这使得模型的开发使用了“单一代码路径”,这一点在下一节中讨论时非常重要。

勤奋的海狸

Tensorflow 2 中的另一个重大增强。x 的一个优点是它隐含地假定了执行的“急切模式”。这意味着现在你可以在一个交互式的环境中单步调试你的模型,比如一个 Jupyter 笔记本,你的代码将会像预期的那样一步一步地执行。渴望模式是共享内存模型中您期望的行为方式,因此将渴望模式作为默认模式极大地有助于易用性。开发人员通常依赖于渴望模式,以便在执行过程中观察他们的代码和变量,例如在调试模型时。然而,当编译器遇到标记为 @tf.function 的自定义函数时,它将恢复到延迟执行模式。原因是需要编译一个@tf.function 以便远程执行。延迟执行模型更难调试,因为您必须依赖跟踪而不是交互式执行。这就是“单一代码路径”策略可以发挥作用的地方。通过开发一个利用通用 GPU/TPU 代码路径的模型,使 Tensorflow 2 成为可能。x 开发人员现在可以使用共享内存模型中的 GPU 在急切模式下运行,然后只需切换 tf.strategy,相同的代码将使用 TPUs 在分布式模式下运行。这种实践产生了一种高效的模型开发方法,它利用了共享内存模型的开发便利性和分布式内存模型的可伸缩性。该方法如下:

  1. 使用 tf.strategy 对象,使用通用 GPU/TPU 代码路径开发您的模型
  2. 使用 GPU 路径在渴望模式下测试/调试您的模型
  3. 只需切换 tf.strategy 对象,就可以在延迟执行模式下将您的模型部署到 TPU

使用这种方法还允许开发人员优化加速器资源的使用。在开发阶段不需要使用高功率 GPU,NVIDIA k80 等低端 GPU 应该足以满足基本模型开发和测试。一旦经过测试,就可以通过更改一行代码(tf.strategy 对象)将模型部署到 TPU。该方法如下所示:

作者图片

结论

在为 ML 模型选择共享内存模型还是分布式内存模型时,有一些重要的权衡需要考虑。虽然共享内存模型更容易开发,但分布式内存模型在性能和内存容量方面具有更好的可伸缩性。虽然传统上在分布式内存模型中编程比较困难,但是 Tensorflow 2。x 使得在分布式内存模型中训练 ML 模型变得更加容易,为分布式模型编程所需的额外努力得到了性能收益的丰厚回报。

使用 ModelOps 扩展和管理人工智能计划

原文:https://towardsdatascience.com/scale-and-govern-ai-initiatives-with-modelops-afdc33ce1171?source=collection_archive---------32-----------------------

卡尔·海尔达尔在 Unsplash 上拍摄的照片

行业笔记

什么是 ModelOps?

在生产中管理模型具有挑战性。为了优化人工智能的价值,人工智能模型必须提高商业应用程序的效率,或者在生产中运行时支持做出更好决策的努力。ModelOps 是在整个组织中扩展和管理企业人工智能计划并确保从这些企业人工智能计划中获得最大价值的关键能力。

本文将讨论支持这种模型操作能力的系统需求。我们将从真实案例中抽取例子,这些案例使用先进的生产企业系统来编排和自动化模型在其整个生命周期中的可操作性,以实现可伸缩的模型操作。

在这些简短的文字中,不可能涵盖与模型操作能力相关的所有挑战和细节。然而,在文章的最后,我们将尽力为您提供正确的参考资料,这些资料将有助于您加深和了解更多关于 ModelOps 的知识。

一些组织讨论了模型操作,他们是如何实现的,以及大企业应该采用什么样的模型操作。一个很好的例子来自 Gartner,在 2020 年 8 月发表的一篇论文中——ModelOps 的创新见解——他们宣称“ModelOps 是任何组织的企业 AI 战略的核心”。

也请阅读【ModelOps 如何帮助你执行你的人工智能策略

还有许多来自 Gartner 和 Forester 的其他出版物,仅举几个例子,以及一个围绕 ModelOps 的不断增长的社区,可供那些希望了解更多信息的人使用。然而,本文的主要重点是强调开始您的 ModelOps 之旅的基础知识。

那么,让我们从关于 ModelOps 的主要问题开始,也就是为什么是 ModelOps?推动大型组织开始认真投资于模型操作能力的价值和难题是什么?

图 1 —为什么选择 ModelOps?—图片由来源提供,经作者许可编辑

我们在图 1 的右侧列出了一些,从我们的角度来看,我们认为它们可能按以下顺序排列:

  • 控制风险:越来越多的业务计划和中央决策由人工智能算法和 ML 模型衍生的模型指导,这些模型正在真正影响现有的治理和风险结构。
  • 缩短模型投入业务的时间:较新的建模技术逐渐过渡到现有的结构中,这促使大型组织了解并更好地利用 ModelOps 功能,另一方面,不断需要尽快将这些模型引入业务中。因此,在开发这些模型时需要花费时间和精力,这些模型有时具有众所周知的不可预测的保存期限,例如对特定商业制度的适用性。因此,将这些模型快速引入业务,最终会推动 ModelOps 的核心功能。
  • 增加透明度:增加透明度和问责制,以便能够随时知道你的模型在哪里。
  • 释放人工智能投资的价值:显然,所有这些不仅会释放企业人工智能计划的价值,而且随着时间的推移,此类计划的数量将不可避免地增加。

如果我们看看过去的 30 个月,我们会看到人工智能计划的巨大增长。在指数级变化速度的推动下,组织和创业公司正在努力采用 ModelOps,目标是捍卫和扩大他们的市场机会。

传统模型与人工智能/人工智能模型

在这种背景下,今天的大多数组织都在积极采用更加敏捷、高效的人工智能和机器学习应用交付,当然还有 IT 管理实践,以满足客户不断变化的期望。

如图 2 所示,在左侧,我们有典型的企业范围资产事物的(灰色六边形)表示,例如企业范围工具、企业范围硬件机器、企业设施、企业范围软件和过程,以及特定传统模型的类别类型,它们影响企业监管和治理结构。在金融服务中,这些通常是在模型风险管理系统或 MRM 中注册的模型。因此,在传统模型中,源自业务和领域专业知识的统计模型往往更多地在业务单位上下文中,而不是在企业上下文中,真正专注于推动特定业务单位的绩效。

图 2 —传统模型与人工智能/人工智能模型—图片来自来源,经作者许可编辑

传统模型和 AI/ML 模型有什么不同?AI / ML 模型和传统模型一样是决策模型。这些模型实际上来自于一开始的需求,因为它们来自于专有数据,通常与业务问题相关联。它们是非常复杂的模型

  • 因为他们在 AI/ML 的环境中使用算法方法;
  • 就与公司流程批准的治理结构的关系而言。

此外,除了完美地适应决策框架,它们还在技术层面上产生影响,它们往往涉及公司的技术结构。由于其技术复杂性,可能需要通过开发运维、企业安全、it 运营等流程,驻留在云服务上,或出于其他原因驻留在本地基础架构上。

另请阅读安全可靠人工智能的模型操作

因此,在人工智能和机器学习的世界中,这些模型比传统模型更像是一种企业资产。正如我们所看到的,它们为业务部门提供支持,而且非常复杂,um 生命周期更快,保质期非常不可预测。由于这个原因,需要非常快速地刷新这些模型,记住它们也可能具有不同的刷新频率。

因此,所有这些导致企业 AI 和 ML 模型的管理面临独特的挑战,如果一家公司或组织希望将这些模型的治理、监控和协调内部化,他们必须考虑到这些代表了一种新的企业级资产,这将真正为他们提供一个前进的基础,并最终理解作为一种能力的模型操作的原因和方式。

所有这些模型都有自己的生命周期,并且暂时与各种现有的业务流程和公司级别的各种技术流程相关。

图 3 给出了生命周期的大致概念。在紫色区域,有模型工厂,从数据科学的角度来看,所有的东西都是为了创建模型而产生的。模型不是传统的软件,由于它们的独特性,它们与企业模式没有相同的关系,但是模型的生命周期确实适合企业范围的模式。

图 3——模型生命周期——图片由来源提供,经作者许可编辑

从这个例子中可以看出,打包部署和执行通常是由模型工厂自己提供的。也可以将它打包到 docker 容器中并发布,或者 ModelOps 平台可以自己提供该功能,因此在每个模型的基础上,都有生命周期中打包部署和执行部分的详细信息。

ModelOps 是关于理解模型的整个生命周期以及它们与业务和技术的集成,以便随着时间的推移在企业中交付价值。

在此图的绿色部分,我们可以看到一个典型的操作周期。这里提供了推理监控、概念漂移监控、统计性能或数据漂移等内容、模型的持续监控以及右下方法规遵从性审核中的治理结构。如果模型有任何变化,例如再培训或 Champion Challenger,这可能是非常复杂的自动再培训流程,这可能会启动新的监管或合规或问责流程。

也请阅读不要让工具和管理方法扼杀你的人工智能创新

想象一下,一个企业有成百上千个模型,并且必须能够准确地知道每个模型在其生命周期中的位置,这是非常复杂的,并且成为扩展的真正挑战。如果做得不好,它会影响治理和提供问责制和可审计性的能力,例如基于每个模型的道德公平和偏见。

模型是资产,它们是复杂的资产,不像传统的资产,它们需要生命周期,这些生命周期也是复杂的,必须自动扩展,并且与现有的业务和技术流程有复杂的关系。

图 4 —整个企业的 MLC 图片由来源提供,经作者许可编辑

除此之外,现在重要的是要注意,由于它们的独特性,模型不像软件那样商品化。企业不知道他们是否可以拥有许多小数据模型或大数据模型,模型生命周期像任何企业架构考虑一样,倾向于将自己借给企业范围的模型。我们认为生命周期是一个企业范围内的问题,这设定了企业人工智能架构师的角色。在图 4 中,我们有一些生命周期的例子,如欺诈检测生命周期(我们之前已经深入讨论过),其他用例是反洗钱和一些金融服务。然而,所有这些生命周期都需要管理和设计,它们是真正的架构资产。

也阅读在你的人工智能策略中包含模型操作

人工智能编排平台

企业需要能够在这些资产的整个生命周期中持续管理、自动化和监控这些资产,而这正是 ModelOps Center 等现代生产 ModelOps 系统所做的。

这种系统为企业提供了一种集中、一致和有效地管理所有 AI/ML 模型的方法。该解决方案使团队能够优化整个模型运营生命周期,从初始部署到报废。

在这篇博客中,我们测试了 ModelOp Center 的特性。该平台使团队能够自动化和编排模型监控和治理。该解决方案提供了团队建立可靠、合规和可扩展的人工智能计划所需的所有关键功能。有了这些功能,团队可以最大化其模型的价值,提高运营和成本效率,并控制风险。

图 5 —现代生产模型操作系统的关键能力—图片由来源提供,经作者许可编辑

  1. 该解决方案使团队能够:
  • 定义、细化、标准化和自动化模型运行中涉及的每一个步骤。
  • 使用预定义的流程进行注册、运营、风险管理和监控。
  • 为所有模型设计一致、优化的端到端运营流程,同时基于收集的元数据实现灵活定制

2.团队可以建立对模型符合性的持续验证。因此,我们可以始终如一、权威地确保法规要求、业务政策和风险控制的持续执行。

3.监测涵盖一系列领域,包括数据漂移、概念漂移、伦理公平性、可解释性和群体评分、特征稳定性等。

4.预打包的集成包括:

  • 人工智能模型工厂。
  • 模型框架。
  • 模型工作台。
  • 共享 IT 系统。
  • 基于云的 ML 服务。
  • BI 可视化工具

5.该解决方案允许以统一的方式跟踪和管理所有模型,并提供了简化跟踪、治理和报告工作的复杂可见性。

无论我们支持哪种类型的业务,通过采用正确的现代生产模型操作系统,我们将能够在企业层面提供自动化和治理,并确保企业层面的所有问题都可以轻松地在每个企业人工智能计划中得到解决。企业人工智能正在成为大型组织中大多数决策的一种战略方向。

ModelOp Center 是一个系统示例,该系统支持管理 AI 和 ML 模型的基本挑战,并能够大规模自动化非常复杂的生命周期,包括业务和技术挑战,并为组织提供了解所有模型位置的能力。

另请阅读数据科学如何推动数字化转型

图 ModelOp 中心的问题解答—图片由来源提供,经作者许可编辑

成功模型操作的 4 个步骤

几十年来,组织一直使用模型来帮助商业决策。然而,人工智能和机器学习模型给模型操作化(后期开发)带来了新的风险。许多模型操作流程是手动的,或者使用本地开发的解决方案进行管理,这些解决方案需要随着新技术、工具和治理要求的引入而不断更新。

因此,开发的模型中有一半以上没有得到部署,而那些模型需要几个月的时间来运行,这往往导致次优的结果以及延迟或降低的价值。

这里有 4 个步骤,任何组织都可以成功地操作 AI/ML 或任何其他类型的模型。

1。 定义端到端的模型运行过程(简称模型生命周期)

  • 准备模型用于生产的第一步是建立端到端的模型操作流程,称为模型生命周期(MLC)。
  • 企业 AI 架构师通常负责设计模型生命周期。

调配车型

  • 部署是将模型集成到现有生产环境中,以根据数据做出实际业务决策的方法。
  • 通常,数据科学家负责部署模型。

3。 监控生产中的车型

  • 当一个模型首次在生产系统中实现用于实际业务用途时,监控就开始了,并持续到该模型退役为止,有时甚至超过作为历史档案的时间。
  • 模型操作员通常负责监控生产中模型的健康状况。

4。 统辖模型操作

  • 模型是一种智力资本,应该作为企业资产来管理。应该使用使审计和报告尽可能高效的工具和技术对它们进行清点和评估。

摘要

自动化和编排模型生命周期的所有方面确保了可靠的模型操作和大规模治理。企业中的每个模型都可以采用多种多样的生产路径,具有不同的监控模式和各种持续改进或淘汰的需求。

公司需要开始将 ModelOps 视为一种基本能力,这种能力可以真正实现业务追求的卓越水平,并确保投资具有有保证的即时 ROI。这件事不能再拖延了。

关注我的每日技术和创新更新

【https://bit.ly/m/ingliguori

参考

  • ModelOps:治理和扩展 AI 计划
  • ModelOp 中心缩放并治理 AI 模型操作
  • 成功模型操作的 4 个步骤
  • Gartner《ModelOps 创新洞察》报告
  • 实施人工智能

使用 MLflow 扩展您的模型开发

原文:https://towardsdatascience.com/scale-up-your-models-development-with-mlflow-4b78a5f22cb7?source=collection_archive---------27-----------------------

在本文中,让我们看看您的数据科学团队如何👩‍💻👨‍💻可以通过 MLflow SDK 体验🧪的新模式。如果你对 MLflow 感兴趣,并且想更进一步,请跟我来,✋

图片由 Jorge Ramirez 在 Unsplash 上拍摄。用 MLflow 整理你的数据线!

https://medium.com/@stefanobosisio1/membership

目录

— 什么是 MLflow?
— 十秒钟介绍 MLflow 环境
— 帮助您的团队:迈向 MLflow SDK
— 实现 MLflow 跟踪 SDK 类
—对象构造器
—读取输入参数—
—设置器和获取器
—训练模型例程

什么是 MLflow?

MLflow 是一个机器学习的开源平台,覆盖整个 ML 模型周期,从开发到生产再到报废。MLflow 直接来自 Databricks,它可以与任何库、语言和框架一起工作,并且可以在云上运行,它是跨团队协作的关键产品。

我们可以定义四种 MLflow 产品:

  • ml 流量跟踪:该组件涵盖模型实验阶段。数据科学家可以研究和开发不同的数据和模型参数,收集 MLflow 中的所有相关信息,并选择哪个模型可以进入生产阶段
  • MLflow 项目:在大型协作中,数据、API、代码需要共享的情况经常发生。合作者经常会遇到安装问题、模型中的问题、代码分段错误和其他类似的“威胁生命”的障碍。允许使用一种通用的格式来打包数据代码,使它们更具可复制性,并在团队间共享而不会引发心脏病
  • MLflow 模型:一旦一个团队登陆生产地,下一步就是避免模型的混乱和混乱。模型允许以简单直观的方式在不同的环境中部署机器学习模型
  • MLflow Model Registry :以及如何避免生产中最后的混乱?注册中心是一个解决方案,它跟踪所有模型的状态,无论它们是在生产中、在试运行中还是最终退役。

我将写 3 篇关于物流的文章,涉及物流跟踪和物流模型。特别是,在这个故事中,我将向您展示如何通过思考一个 MLflow SDK 来帮助您的团队,以便数据科学家可以跳过编码 MLflow 中的一些繁琐部分。在第二个故事中,我们将扩展 SDK 来检索指标,并为数据科学家创建直观的 Plotly 图,以及一些示例。最后,我将向您展示如何创建一个 MLflow 插件,它可用于在 GCP 人工智能平台中部署模型

十秒钟 MLflow 环境介绍

MLflow 开发人员已经整理了这份精彩而全面的指南,让每个人都可以安装 MLflow 并开始使用它:https://mlflow.org/docs/latest/quickstart.html

在这里,我将让您快速浏览一下,在本地机器上运行。安装 MLflow 后,我们可以编写一个类似这样的小代码:

图 MLflow 的简单介绍代码

通过运行python name_of_your_file.py执行这段代码,这将在 MLflow 中启动一个记录过程。这就是 MLflow 发挥作用的地方!在您的 CLI 中,只需键入mlflow ui即可启动 MLflow 图形界面。前往http://localhost:5000(或http://127.0.0.1:5000),你将进入 MLflow 界面(图 2):

图 2: MLflow 用户界面。左边是实验列表,中间是基本信息、参数和指标。

如果你点击一个特定的运行,你会得到一个新的窗口,如图 3 所示。在这里你可以检索运行的参数(例如,这可能是层数,批量大小,初始学习率等),运行的指标作为互动图(例如,准确性作为功能的步骤/时期)和所有保存的人工制品

图 3: MLflow 单次运行用户界面。在这个窗口中,您可以获得关于代码运行和检查工件的更多细节

帮助您的团队:迈向 MLflow SDK

到目前为止一切顺利,但现在是时候扩大规模,帮助您的数据科学团队将 MLflow 集成到他们已经存在的模型中了。构建在 MLflow 包之上的 MLflow SDK 将是一个完美的解决方案,不仅可以降低团队消化新技术的难度,还可以在一个简单的 Python 类中满足数据科学家的请求。
首先,我们需要了解我们的 MLflow 类应该做什么,以及我们想要走什么样的道路。图 4 显示了一个可能的路线图,从 MLflow SDK 的创建到 CI/CD 和通过 MLflow 插件的部署

图 4:路线图草图。MLflow SDK 应该照顾现有的数据科学家的模型,并保存人工制品。然后,MLflow 部署插件可以将最终结果推向生产

实现 MLflow 跟踪 SDK 类

因此,第一步是设计一个实现 MLflow 的类,它可以具有以下功能:

  • read_params此函数读取设置新 MLflow 实验所需的跟踪输入参数
  • set_tracking_uriget_tracking_uri:这两个函数分别设置 mlflow 跟踪 uri ( mlflow.set_tracking_uri)并返回当前跟踪 uri
  • set_experiment_idget_experiment_id:set 函数检查是否已经给出了一个experiment_name,否则创建一个新的。
  • run_training:这是捕获模型参数和指标的主要函数(主要通过使用 Python 模块inspect)。模型的参数和指标由mlflow通过mlflow.package.autolog功能自动检索。
  • log_any_model_file:有时 MLflow 自动登录功能无法将保存的模型文件上传到 MLflow-server。在这些情况下,该功能可以帮助数据科学家。事实上,一旦训练完成,调用这个函数,就可以找到工作运行目录中的所有.joblib.h5.pkl.model文件,以及png格式的度量和json元数据文件,并正确地报告给 MLflow-server。

对象构造函数

现在让我们开始编写代码,设置一个类接口和协议以及它的构造函数。有一个云存储选项可能是有用的,所以你可以导入它(例如from your_cloud_provider import storage_module)并将工件直接保存在云上。

图 5:设置 MLflow SDK 接口和协议。SDK 将存在于这个主对象上

读取输入参数

接下来,我们可以开始考虑解析并读取参数。数据科学家可以使用 Python 字典中给定的指令和参数解析yamljsontxt输入文件,这些文件可以由类直接读取:

图 6:读取参数功能。数据科学家可以将输入文件解析到字典中,字典提供诸如跟踪 uri(例如, http://localhost:5000)、,)以及存储位置、运行名称和标签等指令

在本例中,我添加了self.caller,稍后我们将看到这些信息是如何传递给 SDK 的,但是这个变量的目的是记录哪个脚本调用了 MLflow SDK,将 Python 脚本复制到工件中,并实现代码传承。代码沿袭在运行时很有用,因为可以发生许多修改,尽管数据科学家可以跟踪 Github 中的变化,但一些变化可能会丢失,导致令人困惑和神秘的结果。

Setters 和 getters

接下来,我们需要注意设置器和获取器(图 7)。这些函数听起来可能没什么用,但在执行大量长代码时,它们非常重要,可以挽救你的生命!

图 7:主类的 Setters 和 getters。

set_experiment_id上花几秒钟来创建一个新实验是值得的experiment_nametracking_storage被传递给mlflow.create_experiment(experiment_name, tracking_bucket),从那里experiment_id被取回。experiment_idrun_name都是强制在 MLflow 中开始新的运行,因此我们需要特别注意这些变量。

训练模型例程

现在,我们已经准备好进行日常训练了。通常,MLflow 通过上下文管理器来跟踪模型训练作业,上下文管理器是一种 Python 协议,通常会简化一些资源管理模式:

图 8:来自 MLflow 教程(https://www . ml flow . org/docs/latest/tutorials-and-examples/tutorials . html)的关于培训跟踪的示例

上下文管理器的问题是with mlflow.start_run()只有在训练 Python 代码中被直接调用时才能记录工件和训练会话。对于数据科学家来说,这可能是一项非常枯燥的重组任务,他们应该重新编写部分代码来适应这一功能。想要的解决方案应该是类似的:

from your_mlflow_sdk import experiment_tracking
...if tracking: # if you decide to track:experiment_tracking.start_trianing_job(input_parameters)history = model.fit(**fit_params)# report additional metrics (we'll see below) experiment_tracking.end_training_job(input_parameters)
else:history = model.fit(**fit_params) # normal training

run_training函数应该可以处理这个问题,它可以监听用户正在做什么,而不会干扰已经编写好的代码:

图 9:主训练函数,可以被调用来记录所有的模型人工制品

该函数调用mlflow.autlog()函数,对监听xgboostsklearntensorflowkeraslightgbm模块提供了极好的支持,自动将所有模型参数报告给主界面。该函数的核心是mlflow.start_run(),它设置要记录的运行,正如我上面所说的,它将调用者 Python 脚本self.caller作为mlflow.log_artifact(self.caller, artifact_path='code')报告给 artefacts 文件夹code

将任何模型文件/工件记录到 MLflow 中

避免上下文管理器的一个问题是,并非所有生成的模型文件都会自动保存到 MLflow 服务器。例如,在我的例子中,许多模型文件保存在本地或云中,但是我希望它们也在 MLflow 服务器中报告。为了解决这个问题,图 10 中的log_any_model_file开始发挥作用。

该函数调用新运行的实验。它检查实验运行是否顺利。然后,它查看一个给定的存储位置,本地或云,以查看在那里保存和创建了哪些文件。接下来,扫描想要的格式列表,local_file_to_upload,运行一个 for 循环,用client.log_artifact(run_id, file_to_upload, artifact_path='model')将所有找到的文件记录到一个model文件夹中

图 10:将任何元数据和模型文件记录到 MLflow 服务器。有时不是所有的文件都由 MLflow 直接保存,尤其是在使用混合模型时。因此,该功能在云上或本地寻找额外的工件,并将它们上传到 MLflow 服务器

在一个模块中修补所有内容

现在,您已经创建了 MLflow SDK 的基础,我们可以继续实现一个培训模块,数据科学家可以将其用于他们的实验。模块名可以是实现功能start_training_jobend_training_jobexperiment_tracking_training.py

前一个函数设置 MLflow 跟踪作业。它通过带有caller = traceback.extract_stack()[-2][0]traceback模块检索调用者 Python 脚本代码,这将在工件中报告。然后,它初始化实验参数,最后开始训练运行runner.run_training()

end_training_job可在模型拟合后调用,通过log_any_model_file()检索任何其他可能的未保存文件

图 11:使用 MLflow 开始和完成培训工作会话记录的模块。start_training_job 初始化实验跟踪协议,而 end_training_job 检查未保存到 MLflow 服务器的其他工件。

今天就到这里吧!我们经历了很多事情,如果你不熟悉 MLflow,你会一步一步地消化一切。请继续关注我们的下一个故事,它将展示如何向我们的实验报告额外的指标,并且我们将看到一些如何使用我们的 MLflow SDK 的示例:)

如果你有任何问题或好奇,请给我发电子邮件到 stefanobosisio1@gmail.com

比例折线图——它们是什么,为什么你绝对需要它们

原文:https://towardsdatascience.com/scaled-line-chart-what-are-they-and-why-do-you-absolutely-need-them-7c56dd09377c?source=collection_archive---------11-----------------------

结合两个简单事物的力量得到令人敬畏的东西

作者图片

有时候把两个看似简单的东西结合起来,会得到很牛逼的东西。在本文中,您将看到折线图和数字缩放器相结合的强大功能。仅仅为了词汇:

  • 折线图对于分析一段时间内的价值非常有用。
  • 缩放器是一种将值转换为不同比例的算法。例如,python MinMaxScaler(0,100)会将一个值转换为 0 到 100 之间的一个所谓的缩放值。

数字标尺和折线图通常彼此独立使用。然而,在许多情况下,将它们结合起来会很有用。

在本文中,我将通过两个用例向您展示结合使用标量和折线图的强大功能。

案例 1 —使用物联网数据的预测性维护

该用例用于飞机涡轮风扇发动机的预测性维护。这里展示了一幅绘画作品。

图片来源https://ti.arc.nasa.gov/c/6/

涡轮风扇发动机有多个发送数据的传感器,这些数据本质上是时间序列数据。这些数据用于跟踪和预测飞机涡轮风扇发动机的损坏。

引擎生成的数据(图片由作者提供)

数据是在发动机和运行周期的水平上。数据集由发动机和传感器测量的操作设置的多个多元时间序列组成。RUL 在发动机的每个工作循环后仍有使用寿命。

数据科学活动正在开发一个机器学习模型,以根据传感器数据预测 RUL。首先进行的活动之一是数据探索,以查看传感器数据和 RUL 之间是否有任何关系。例如,下面是传感器 10 和 RUL 的折线图。

飞机发动机故障分析(图片由作者提供)

RUL 值随时间降低,这在可视化中清楚地显示出来。sensor_10 值似乎是恒定的。看着这个图像,我们很容易得出结论,sensor_10 对确定 RUL 没有用。

如果我告诉你这是一个完全错误的结论呢!

现在让我们用 RUL 和传感器值在 0 和 100 之间绘制相同的可视化,如下所示。这意味着最小值显示为 0,最大值显示为 100。

用标度值进行飞机发动机故障分析(图片作者提供)

我们现在可以观察到 sensor_10 值不是恒定的。价值出现了峰值。这些尖峰信号可能是机器将要发生故障的一个信号。

这是和上一个完全不同的结论。这两种可视化非常不同的原因是,与 sensor_10 相比,RUL 值非常高。RUL 具有诸如 130、120 等的值。传感器 _10 值具有诸如 1.3、1.5 等的值。因此,与 RUL 值相比,传感器 _10 值非常小,传感器值的变化没有在第一个可视化中示出。

在第二个可视化中,对传感器值应用缩放是一个游戏改变者。这使我们清楚地看到传感器相对于 RUL 的变化。我们可以将尖峰信息转换为一个特征,而不是丢弃传感器值。“尖峰”特征在预测 RUL 时可能非常有用。

案例 2 —加密货币分析

加密货币分析是我们可以结合 scaler 和折线图的另一个领域。下面显示的是四种加密货币(比特币、以太坊、索拉纳和 Dogecoin)的非比例价格线图。

使用非比例折线图进行加密货币分析(图片由作者提供)

第一个观察是,我们可以理解比特币的模式,因为它清晰可见。但是,对于其他三种货币,我们无法对它们进行分析,因为它们在折线图上没有得到很好的表示。原因是比特币价格和其他加密货币价格之间的巨大差异。比特币价格是其他加密货币的 10 倍。

现在让我们在 0 到 100 之间划分价格。这是一个比例折线图。

加密货币分析与缩放折线图(图片由作者提供)

这张图表看起来更好,因为所有四种加密货币都得到了很好的体现。我们可以利用这种可视化来获得非常强大的洞察力。比如我们看到比特币、以太坊、索拉纳都有类似的趋势。Dogecoin 已经达到顶峰,然后价格开始下降。这种见解只有在缩放的折线图中才有可能实现。

结论

如果您尝试使用折线图比较多个值,请验证这些值是否有显著差异。

如果差异非常重要,那么就调整数值(比如说在 0 到 100 之间),然后将它们绘制在折线图上。

刻度折线图使您能够以更好的方式可视化和解释非常小的值。

额外资源

网站(全球资讯网的主机站)

你可以访问我的网站,用零编码制作比例线图和其他分析。https://experiencedatascience.com

订阅每当我发布一个新的故事时,请及时通知我。

https://pranay-dave9.medium.com/subscribe

你也可以通过我的推荐链接加入 Medium。

https://pranay-dave9.medium.com/membership

Youtube 频道
这里是我的 Youtube 频道的链接
【https://www.youtube.com/c/DataScienceDemonstrated

扩展数据驱动的微服务

原文:https://towardsdatascience.com/scaling-data-driven-microservices-27be1f5df34a?source=collection_archive---------27-----------------------

扩展数据驱动的微服务需要基础设施的支持,但更重要的是,应该编写代码来支持可扩展性

摘要

如今,应用程序的流量负载可能会在短时间内急剧增加或减少,这意味着应用程序需要轻松地横向扩展和纵向缩减。要做到这一点,它们需要一个能够容纳资源扩展或减少资源的基础设施来支持,但更重要的是,应该编写代码来支持可伸缩性。

在我们的大数据时代,应用程序收集和处理海量数据。为了实现其目的,这些应用程序需要在合理的时间范围内处理数据,并且不消耗太多资源。

当开发处理大量数据的服务时,开发人员应该了解资源。pandas 和 NumPy 等库中对大型数据集的操作经常会在内存中复制数据,最终导致不必要的快速资源限制。以匹配学生表和考试列表为例。每个学生参加所有测试(所有测试由所有学生参加)。如果我们想在一个表格中表达这种关系,就需要一个由(学生数量)x(测试数量)行组成的表格。对于大型数据集,这可能远远超过内存限制。

更重要的是,缺乏资源意识可能会导致下面列出的许多问题,例如:

  • 让主线程保持忙碌—密集的 CPU 工作负载可能会导致其他请求被阻塞。
  • OOM —在内存中处理大量数据可能会超过物理服务器的内存量。
  • CPU 负载——执行密集型计算会导致我们的应用程序延迟,从而影响我们的用户体验。

此外,简单地扩展资源(纵向扩展)或部署应用程序的更多实例(横向扩展)也无济于事。纵向扩展有服务器 CPU 和内存资源的限制,这使得它在大数据环境中受到限制。此外,当应用程序保存所有数据并试图同时处理这些数据时,横向扩展是不可能的。

那么,我们如何在数据驱动的应用程序中使用横向扩展呢?

调度员模式

有许多方法可以帮助我们的代码支持伸缩,其中之一是使用名为 Dispatcher 的模式。

dispatcher 模式将任务的执行与任务的指挥者分离开来。
堪比主从架构,向执行命令的从机发送命令的调度器。

调度员不仅仅是向工人发送命令。
它在发送命令给它的工人之前应用一些计算逻辑。这种逻辑的一个例子是将数据分类,例如按天分类。
使用该方法时,我们可以按需扩展应用。

使用 dispatcher 模式的好处:

  • 高效的资源利用——在使用 dispatcher 模式时,可以根据系统负载决定何时扩展应用程序,并通过减少不必要的资源消耗来节省成本。
  • 业务逻辑和数据预处理的分离——使用这种模式,可以分离关注点。每个组件都有更明确的用途;调度器要求从机执行原子操作,从机执行更简单的操作。
  • 易于调试——使用这种方法时,可以检测数据中较小的原子部分的错误(哪一批有问题)。
  • 缩短处理时间-部署的员工越多,数据处理速度就越快。

调度员模式图| 图片作者

有许多方法可以在您的应用程序中实现 dispatcher 模式,这里我们将使用 Apache Kafka(事件流平台)在微服务环境中实现它。

Apache Kafka 支持 PubSub 架构,这意味着有一个发布者将消息发布到一个队列(称为主题),还有一些消费者,他们使用来自该主题的消息。(Apache Kafka 有更多的功能,但这超出了本文的范围)。

我们将使用 Apache Kafka 在 Dispatcher 和它的工作人员之间传递消息,并展示如何支持 scale。

Dispatcher 模式使用示例

在本例中,我们将对 COVID19 数据集执行操作,该数据集是使用 faker 库生成的 fake
数据集包含世界各地 COVID19 阳性和阴性病例的时间范围,并具有以下属性:患者姓名、城市、国家、出生日期以及 COVID19 测试的日期和结果。

这个想法是计算 COVID19 跨数据集时间范围的国家阳性率。

数据集的链接: 数据集

数据集探索:

数据集探索 |作者图片

根据下面的图片,数据集包含 2M 记录,每条记录为一名患者提供其 COVID19 测试结果。
数据集时间范围在 2020 年 3 月 1 日至 2021 年 1 月 1 日之间。

调度员

调度程序的任务是将数据分割成原子排列,并将数据分派给它的工作人员来操作。关键字:“country”和“date_hour”表示我们想要计算正比率的原子排列。正如我们所看到的,这里的 dispatcher 任务很小,dispatcher 只需将数据分组到 country 和 date_hour 键中,这不是一个密集的操作,dispatcher 任务并不总是很繁重。

调度员 |图片作者

一旦实现了 dispatcher 任务,我们将使用 Kafka Broker 将所有的作业分派给工人,与 Kafka 一起工作的库很少,我更喜欢使用 Kafka-confluent,根据所附的基准测试(http://activision game science . github . io/2016/06/15/Kafka-Client-benchmark/,它更快,并且也有很好的文档。

工人的

发送后,卡夫卡的消费者(我们的工人)将一个接一个地消费味精,并开始工作。

工人的任务是每次取一份工作,其中每份工作都包含一个特定日期和国家的记录,并为它计算出 covid19 的阳性率。
员工可以运行多项工作(通过 1 个状态),但可以同时运行每项工作,理解这一点很重要。

工人的任务 |作者图片

我们可以看到,工人的任务很小,让我们能够轻松地诊断 bug。还有一个好处是,如果数据处理意外关闭,我们不必从头开始处理。

毕竟,工人完成了他们的工作,我们可以联系所有的结果,并绘制我们的见解。

COVID19 阳性率交叉日期 |作者图片

在下图中,我们可以看到所有数据集国家的 COVID19 正利率交叉日期。

让我们用使用 dispatcher 模式运行这个任务和不使用 dispatcher 模式运行这个任务之间的比较柱状图来总结这个演示。

时间对比(秒) |图片作者

总结

本文强调了在开发下一个数据处理作业时,支持缩放的代码的重要性。不要把注意力放在我们的代码支持缩放上,或者不知道我们试图处理多少数据可能会导致多个问题。
在本文中,我们看到了一种编写支持缩放的代码的方法,并看到了如何使用 Apache Kafka 实现它的实际例子。

希望你觉得有用(:

使用面向领域的数据管道扩展数据产品交付

原文:https://towardsdatascience.com/scaling-data-products-delivery-using-domain-oriented-data-pipelines-869ca9461892?source=collection_archive---------26-----------------------

行业笔记

一种经过测试的大规模快速交付数据产品的方法。

介绍

最近对“数据作为一种产品”的重新构想,即稍微偏离了众所周知的“数据作为一种战略资产”的口号,使得有必要改造现代数据管道架构,以支持大规模数据解决方案的快速交付。不管目标企业是基于什么样的基础数据架构来定义的,这个命题都是正确的。

“数据作为一种产品”的概念在拥有大量数据的组织中越来越流行。这通常包括大型零售商、社交媒体平台和金融服务公司,仅举几例。虽然这些组织中的大多数都希望过渡到这种新的模式或工作方式,但不幸的是,他们中的大多数都不能,这仅仅是因为他们现有的数据管道架构所带来的限制,即他们在本质上是单一的。

因此,在现代数据生态系统中扩展数据产品的交付需要对现有的整体数据管道进行彻底的检查。在此之前,应该彻底理解对数据产品的发展有贡献的领域之间存在的各种交互(也称为接口契约)。为这些领域提供专用管道是成功交付数据产品的关键。在 Dehghani 最近的出版物这里中,可以找到关于“数据域所有权”的构造以及将数据作为产品进行管理的相关原则的更多信息。

什么是面向领域的数据管道?

如上所述,支持数据作为产品的理念在数据产品的组件上实施了域所有权。这还包括编排领域组件的构建、测试和部署的数据管道。值得一提的是,虽然这些管道本身可能是独立的,但它们并不是完全孤立的。因此,一种数据管道架构被称为面向领域的数据管道,它使数据产品团队中的各个领域能够使用最小的共享基线工件集独立地构建、测试和部署数据产品的可重用组件。值得一提的是,该定义假设了一种架构,该架构隐含了现代数据管道设计的最佳实践,例如安全性、可扩展性、可观察性等。

面向领域的数据管道的高级架构

上面描述的高级架构捕获了面向领域的数据管道的基本组件。下面概述的关键原则对于帮助最大化面向领域的数据管道带来的好处非常重要:

  1. 创建正确的团队拓扑:如上图所示,管道设计基于负责构建数据产品各种组件的领域团队(例如,领域团队 1 …领域团队 N)。此外,由于重用是面向领域的数据管道的一个关键特性,团队拓扑应该支持一个负责嵌入通用标准的核心产品团队,领域团队在构建各自的数据工作负载时可以利用这些标准。例如,核心产品团队负责烘焙和发布数据产品的基础映像的已知版本,以供生态系统中的域使用。
  2. 通过共享基线组件,最小化重复并最大化重用:这基于对不同领域组件之间关系的理解。面向领域的数据管道架构应该鼓励重用,并尽可能减少管道生态系统中的重复虽然这可能是一个平衡的行为,但总体收益远远超过了领域组件关系映射练习的初始工作。
  3. 定义清晰的领域区域(包括资产命名):虽然目的是确保每个领域管道独立存在(即使不是完全隔离的),但是必须适当注意如何在共享组件的领域中实现关注点的分离。这通过为每个域创建交互区域来实现。此外,这些区域必须可以通过明确的术语(即命名标准)来识别,特别是在共享组件上,如存储、管道构建工件、代码库等。这种清晰性将有助于数据产品的发展和最终用户的消费。
  4. 定义清晰的领域边界(包括所有权和责任):在任何管道架构中,缺乏边界、所有权和责任总是导致灾难。在面向域的管道架构中,这个问题会进一步恶化。然而,如果明确定义,面向领域的数据管道架构有利于在数据生态系统上交付联合治理模型。也就是说,领域和核心产品团队将能够灵活地定义创建公共标准的结构,并帮助消除整个团队拓扑中的潜在冲突。

面向领域的数据管道的优势

毫无疑问,使用传统的单片数据管道来扩展数据产品是非常具有挑战性的。另一方面,面向领域的数据管道通过增加以下好处(还有很多)来简化交付方法:

  1. 从技术和业务角度来看,它们都非常容易扩展。想象一下从预打包的领域管道模板添加新管道来支持全新的业务领域的便利性。
  2. 领域管道的独立性在很大程度上有助于消除整个数据产品交付过程中的单点故障。
  3. 面向领域的数据管道更容易检测数据产品交付生命周期中的性能瓶颈,因为可以在每个领域的级别上测量和收集指标。
  4. 它们提供了一个联合数据治理模型,该模型将控制权交给团队拓扑中的参与者,让他们拥有并负责各自的管道组件。

结论

一个数据产品,如果开发正确,乍一看可能是优雅的。然而,“真正的数据产品”不仅仅由它的优雅来定义,还取决于它的易维护性以及它为支持企业不断变化的需求而进行扩展的能力。这就是面向领域的数据管道的用武之地。

这里描述的原则源于我最近带领一个数据管道工程师团队为企业设计和实现面向领域的数据管道的经验。因此,虽然其中一些可能不直接适用于您的数据生态系统,但大部分适用于大多数现代数据生态系统,尽管有一些调整。例如,尽管在参考架构中只描述了一个核心产品团队,但实际上,这个团队可以通过专用管道进行扩展,具体取决于数据产品的复杂性。

最后,重要的是要提到,在面向领域的管道体系结构中,必须向参与者提供适当粒度的数据,以便在将数据发布到共享区域供生态系统使用之前,支持各种数据组件的构建和测试。

借助现代云运营扩展企业 ML 平台

原文:https://towardsdatascience.com/scaling-enterprise-mlops-delivery-with-modern-cloud-operations-6888d7218be5?source=collection_archive---------24-----------------------

AWS 上扩展 ML 环境供应的分步指南

与 Nivas Durairaj 的联合职位

古腾堡印刷机在当时是革命性的 T2。突然间,出版商可以每天印刷数千页书,而不是几页手写的。它使知识在欧洲迅速传播,开启了文艺复兴时代。

照片由亨克·穆尔在 Unsplash 上拍摄

今天,大型企业需要向他们的业务交付数百个 ML 项目,同时以安全和受治理的方式进行。为了加快 ML 交付,他们需要在几分钟内为 ML 环境配备护栏。印刷机使 ML 团队能够快速进入工作环境,脚手架使他们的解决方案可操作化。

我最近发表了一篇关于如何在 AWS 上这么做的指南。在这里,我们将付诸实践。我将通过 3 个步骤来分享如何实现云运营的现代化,以扩展 ML 交付。

作者图片

演练概述

我们将分三步解决这个问题:

  • 我们将首先用 AWS 控制塔和 AWS 组织设置我们的 ML 平台基础。我们将采用多账户策略,每个 ML 项目将在一个单独的账户中运作。
  • 然后,我们将使用 AWS 服务目录和 Amazon SageMaker 实现模板化 ML 环境的自助服务。它将允许 ML 团队在几分钟内自行调配其客户中批准的环境。
  • 最后,我们将看到 ML 团队如何启动和访问他们治理的 ML 环境。

先决条件

要关注这篇文章,请确保您:

  1. 访问使用 AWS 控制塔管理多帐户 AWS 环境,如果这对您来说听起来很新鲜的话。
  2. 我们将应用中提出的概念,在 AWS 上建立安全的、治理良好的机器学习环境。在继续之前,请务必阅读这篇文章。
  3. 对于自助服务,我们将重复使用与相同的方法和服务目录组合,实现亚马逊 SageMaker Studio 资源的自助供应。确保你熟悉它。

步骤 1:使用现代云操作支持 ML 项目

首先,我们希望 ML 团队每次有新项目时都能访问一个安全且合规的 AWS 账户。这里,我们保持简单,为每个项目创建一个 AWS 帐户。

作者图片:我们将使用控制塔在工作负载 OU 下自动提供帐户。他们将自动继承我们在 OU 上应用的治理。

设置着陆区并创建工作负载 OU

导航到 AWS 控制塔控制台设置你的着陆区。参见AWS 控制塔入门了解如何启动控制塔的详细信息。

作者图片:我在 OU 配置页面中创建工作负载 OU

启动后,大约需要半个小时才能完成。

图片作者:您现在应该可以在控制塔中看到基础 ou(安全)和附加 OU(工作负载)

注意- 安全 OU下的日志归档帐户可以作为从工作负载 OU 下的所有帐户收集的日志数据的合并点。您的安全、运营、审计和法规遵从性团队可以使用它来支持法规要求。

将控制塔护栏和 scp 应用于持续治理

您可以设置控制塔护栏,为您的整个 AWS 环境提供持续治理,并设置服务控制策略,以控制工作负载 ou 下所有帐户的最大可用权限。

为了便于说明,我们将使用与本博客中相同的 SCP 示例。它阻止 ML 团队在其帐户中启动 SageMaker 资源,除非指定了 VPC 子网:

按作者分类的图片:导航到 AWS 组织并创建新的 SCP。我把我的叫做“sagemaker-enforce-vpc”。

使用帐户工厂创建项目帐户

您现在可以使用控制塔帐户工厂按需创建新帐户。

作者图片:在我的例子中,我在工作负载 OU 下创建了 MLProjectA 和 MLProjectB 帐户。

帐户工厂是一个服务目录产品,因此您可以通过 UI 创建帐户。当缩放时,你可以通过编程来创建。

管理用户验证和权限

接下来,您需要管理帐户中的用户身份验证和权限。在这里,我使用 AWS SSO 来管理这些,您可以按照此视频中的过程操作。请随意使用您选择的身份提供商:

作者图片:我创建了两个用户——Jon Doe(数据科学家组)和 Mike Smith(m engineers 组)。我还创建了自定义权限集,供用户在登录他们的帐户时使用。

SageMaker 提供了特定于服务的资源、动作和条件上下文键,您可以将它们添加到权限集。另请参见本页的管理对其他 AWS 服务的权限。

您可以为不同的 ML 项目角色创建权限集。这里有几个例子:

  • 数据科学家他们可以尝试 ML 方法。
  • ML 工程师他们可以处理 CI/CD、模型监控和工件。
  • ML 平台工程师(admin) 管理员。
  • 审计和合规团队他们拥有日志归档帐户的读取权限,可以在其中验证工作负载的合规性。

作者图片:作为管理员,我给了 Jon Doe 访问 MLProjectA 账户的权限。和 Jon 一样,我可以进入 SSO 登录页面,查看我可以访问哪些项目帐户。

现在,您的 ML 平台中应该有多账户基金了。当一个新的 ML 项目开始时,您可以使用 guardrails 创建一个新的 AWS 帐户,并向用户提供对它的访问。这个过程只需要几分钟。

步骤 2:自助式模板化 ML 环境

现在,您的 ML 团队可以在几分钟内访问客户,他们需要访问工作环境和脚手架来实施他们的解决方案。

我们将在控制塔管理客户中创建一个服务目录组合,并与 ML 项目客户共享。

作者的图片:我们在管理帐户中创建了一个治理产品的投资组合(左),并与 ML 项目帐户共享它(右)

在管理帐户中创建服务目录组合

为此,您可以重用来自的方法,实现亚马逊 SageMaker Studio 资源的自助供应。它将允许您使用 AWS 服务目录工厂自动部署 SageMaker 产品。

作者图片:带有模板化 SageMaker 资源的服务目录组合

注意为了便于说明,我将服务目录工厂放在控制塔管理帐户中。在现实生活中,您的 ML 平台团队可能有专门的客户来构建、测试和部署产品和组合。

与 ML 项目客户分享投资组合

现在,我们将与工作负载 OU 下的所有客户共享服务目录产品组合。

作者图片

这一过程非常简单,您可以按照视频中的步骤进行操作:

图片作者:我与工作负载 OU 共享投资组合。

作为一个 ML 平台管理员,你可以登录到项目帐户,并接受投资组合。

作者图片:您需要输入管理帐户中的投资组合 ID。

然后,您可以向 ML 团队提供在其帐户中访问导入的投资组合的权限。

作者图片:在这里,我将对投资组合的访问权授予拥有数据科学家或 m 工程师角色的用户。

从现在开始,创建新的 ML 项目帐户应该不会超过几分钟。您对服务目录产品组合所做的更新将自动反映在项目帐户中,使您能够持续部署新产品。

步骤 3:在项目帐户中启动 ML 环境

现在是容易的部分!我们将使用我们的一个 SSO 用户,登录到 MLProjectA 帐户,并启动 SageMaker Studio。

图片作者:作为 Jon Doe,我登录到 MLProjectA 帐户,并转到服务目录控制台页面。

作者图片:使用服务目录,我启动了一个新的 SageMaker Studio 域(一次性的),并为自己创建了一个用户配置文件。

注意- 为了便于说明,示例工作室域将寻找带有公共子网的默认 VPC。我用AWS ec2 create-default-VPCCLI 命令得到一个。在现实世界中,你会希望 Studio 域在私有子网中运行。

作者图片:我现在只需点击几下鼠标就能进入 SageMaker 工作室环境!🚀

结论

多账户策略和受管 ML 环境的自助供应允许企业扩展其 ML 交付。它允许 ML 团队在几分钟内开始在批准的环境中工作。

在这篇文章中,我分享了 ML 平台团队如何通过 AWS 控制塔、AWS 组织、AWS 服务目录和 Amazon SageMaker 快速提供安全、治理良好的 ML 环境。

为了更进一步,您可以访问 AWS 管理和治理镜头和利用亚马逊 SageMaker Studio 实现 ML 平台的工业化。

多重处理缩放花

原文:https://towardsdatascience.com/scaling-flower-with-multiprocessing-a0bc7b7aace0?source=collection_archive---------17-----------------------

理解大数据

了解如何使用 Flower 框架和 PyTorch 的多处理在本地扩展您的联邦学习实验。

由米切尔·林辛克在 Unsplash 上拍摄的照片

联合学习简介:

随着技术的发展,最近的技术产生越来越多的数据,大量收集这些数据以训练准确的模型变得越来越容易获得。然而,这引起了隐私问题,为了确保对他们的保护,人们目前根据他们的居住地受到许多法律的保护(例如欧洲的 GDPR )。当涉及个人数据时,不能盲目应用传统的机器学习方法,即在单个点积累数据来训练模型。

为了解决这个问题,谷歌在 2016 年发布了一种新的训练模型的范式,称为联邦学习,并将其应用于其谷歌键盘应用[ 1a ] [ 1b ]。它的引入是为了利用他们用来训练模型的公开可用数据集和用户将产生的私有数据之间的域差异问题。

正如联合学习手册[ 2 ]中所指出的,为了让这种范式发挥作用,它需要遵守 4 个主要原则,即:

至少有两个实体想要训练一个模型,拥有自己的数据并准备使用。

在培训期间,数据不会离开其原始所有者。

模型可以通过受保护的方式从一个实体转移到另一个实体。

得到的模型性能是用单一实体拥有的所有数据训练的理想模型的良好近似。

最后一点也是在告诉我们,联合学习并不能一直适用。它最大的缺点是,至少在目前,联合学习对来自内部的攻击很敏感[ 3 ],不能保证收敛[ 4 ],并且需要足够多的客户端来实现它的结果[ 5 。然而,当正确应用时,它可以产生通过常规手段无法获得的模型,如 Google 和他们的 Google Keyboard。

到目前为止,只有少数框架可以实现它,因为这是一个相当新的概念。TensorFlow 开发了自己的版本,名为 TensorFlow Federated 。PyTorch 还没有自己的实现,但是它们确实存在兼容的框架,比如 OpenMined 开发的 PySyft 和 Flower ,这将是本文的重点。

为什么要用花:

Flower 是联邦学习的最新框架,创建于 2020 年。与 TensorFlow Federated 和 PySyft 链接到单个框架相反,Flower 可以通过设计与它们一起使用。它侧重于提供有效应用联合学习的工具,并允许您专注于培训本身。用 Flower 实现一个基本的联邦设置非常简单(20 行代码就足够了),将集中式代码改编成联邦代码所需的重写工作非常少。

此外,兼容设备的范围也非常广泛:从移动设备到 Raspberry Pi、服务器等等。其架构还允许多达 1000 个客户端的可扩展性,如他们的论文[ 6 ]所示。总的来说,这是一个非常好的实验框架。

GPU 问题:

如果您想在本地模拟一个联邦学习设置,只要 CPU 允许,扩展到尽可能多的客户机是非常容易的。对于基本款来说,CPU 绰绰有余,不需要在 GPU 上进行拓展训练。然而,当使用更大的模型或更大的数据集时,您可能希望转移到 GPU,以便大大提高训练速度。

这就是在扩展您的联邦设置时可能会遇到问题的地方。事实上,与其他一些框架不同,Flower 的目标是允许从研究/原型到生产的轻松部署,因此他们将客户视为独立的过程。此外,当访问 GPU 时,CUDA 会自动分配固定数量的内存,以便在请求更多内存之前有足够的空间来处理。

然而,这个内存根本不能被释放,至少在进程退出之前不能。这意味着,如果您正在启动 100 个客户端,并且每轮对其中的 10 个进行采样,并且正在使用 GPU,则每次客户端访问它时,都会有剩余的内存无法释放,并且随着新客户端被采样,剩余的内存会不断增加。从长远来看,你的 GPU 需要和客户端启动一样多的内存。

下面是一个简短的代码片段,它显示了这个问题,在 Google 联合实验室上运行:

监控内存显示,即使在清空 PyTorch 使用的内存后,还有 7%的剩余内存。图片作者。

如何解决问题:

你可能遇到过的这个问题很容易解决。由于在访问内存的进程被释放之前,内存是不会被释放的,所以我们只需要将需要访问 GPU 的那部分代码封装在一个子进程中,等待它被终止,直到我们可以继续执行我们的程序。多重处理是解决方案,我将向您展示如何使用 PyTorch 和 Flower 来实现。

由于这个例子是基于 Flower 文档中的快速入门 Pytorch 教程,我强烈建议在继续之前查看一下,因为它展示了基础知识。

助手文件

首先,我们将为自己构建一个 flower_helpers.py 文件,我们将在其中放置一些函数和类,这些函数和类稍后会派上用场。从进口开始,我们有:

基本导入:用于 CIFAR10 工作的 torch 导入和一个 flower 策略导入,因为我们需要对用例的 FedAvg 策略稍作修改。然后,我们定义要在其上计算训练和测试步骤的设备:

接下来,我们需要定义如何加载数据:

一个简单的 CNN 模型来自“py torch:60 分钟闪电战”:

到目前为止,从最初的花卉教程没有什么变化,从现在开始事情会变得不同。因为我们不能将模型保存在客户端的内存中,所以我们需要定义一种方法来获取模型的权重,以便客户端可以跟踪它们。为此,我们从 flower 教程中移走了get_parametersset_parameters函数,并将它们放在我们的助手文件中:

现在,我们可以定义每次客户想要训练其模型时将被调用的训练函数:

这个函数有三个参数,我们想要训练的本地历元的数量,全局模型的新参数和一个返回字典,该字典将作为我们的返回值向客户端返回更新的模型,本地数据集的大小和我们想要包括的其他度量,如损失或准确性。测试功能也是如此:

最后,我们需要定义我们的定制策略。这些在快速入门教程中没有提到,但是策略是决定服务器如何聚集新权重、如何评估客户端等的类。最基本的策略是 FedAvg(用于联合平均[ 1b ]),我们将使用它来实现我们自己的策略。Flower 已经为您提供了一种方法,通过 FedAvg 策略的初始参数来定义您想要用来评估您的模型的客户数量,但是这只适用于每轮之间进行的评估。

事实上,在最后一轮之后,flower 服务器将执行最后一个评估步骤,对所有可用的客户端进行采样,以验证模型的性能。在真实情况下,这不会是一个问题,但在我们的情况下,这可能会适得其反,我们希望特别避免可能涉及 GPU 内存需求溢出的情况。

这就是为什么我们将在本教程中只在服务器端执行评估,并且我们将删除此功能。这是通过策略的configure_evaluate 方法完成的,我们需要覆盖它:

客户端文件

我们已经完成了助手文件,现在我们可以切换到客户端并创建 client.py 。从进口开始:

下一步是实现我们自己的客户端类,这样它就可以连接到 flower 服务器。我们需要从 NumpyClient flower 类派生并实现 4 个方法,即get_parametersset_parametersfitevaluate。我们还将添加一个名为parameters的属性,在这里我们将跟踪模型的权重:

get_parametersset_parameters很简单,它们只是一个 getter 和一个 setter:

然后fit方法是模型被训练的地方,它接收两个参数:来自全局模型的新参数和包含当前回合的配置的配置字典。在fit内部,我们将启动一个子进程,这样我们就可以使用 GPU 而不用担心内存延迟:

如您所见,该函数返回最新更新的参数、本地数据集的大小和一个字典(这里为空),其中可能包含不同的指标。最后我们有evaluate方法,类似于fit但用于评估。在我们的情况下,我们可以选择简单地实现最低要求,因为我们不会评估我们的客户。但是我将在这里给出完整的实现:

我们只需将所有这些打包在main中,设置spawning方式来创建新的子流程(不是 Python 下的默认方式),并在本地端口 8080 上启动我们的客户端:

服务器文件

客户端完成后,我们现在可以向服务器前进,我们将简单地称之为 server.py !在最初的教程中,启动服务器需要一行代码!但是在这里,我们将执行服务器端评估,并使用自定义策略,因此情况略有不同。从进口开始:

首先,我们需要定义在服务器端评估模型的方式,并将函数封装在get_eval_fn中,告诉服务器如何检索函数。该评估与我对客户端给出的评估几乎相同,您实际上可以合并其中的一部分:

然后我们可以启动__main__,加载参数并设置 spawn 方法:

然后我们得到一个新的网络,这样我们就可以初始化联邦循环的权重:

最后,定义策略并在端口 8080 上启动服务器:

Bash 文件

剩下唯一要做的就是启动我们的服务器和客户端!我们编写了一个很好的 bash 文件,所以我们只需运行它就可以开始实验了:

运行中

现在,只要在您的终端中运行./run.sh,一旦您将它转换成可执行文件(chmod u+x run.sh),您应该会看到下面的输出:

运行脚本的终端输出。图片作者。

打开一个新终端并使用nvtop命令,我们可以实时监控我们的 GPU 使用情况:

使用 nvtop 时的 GPU 内存使用情况,蓝色表示 GPU 计算使用情况,黄色表示内存使用情况。图片作者。

我们可以看到,我们的客户端正在正确地生成子流程,并且每当它们完成训练时,内存都会被释放。

如果你得到一个由“没有名为 backports.lzma 的模块”引起的错误,你可以用poetry add backports.lzma命令添加这个包。

如果出于某种原因,您收到一个错误,告诉您客户端无法连接,请确定在客户端尝试连接之前,服务器有足够的时间进行设置。另一个原因可能是由于 GRPC 和 Python 的一个已知错误,您可以尝试在您的服务器和客户端文件中添加以下行:

所有的代码都可以在 GitHub 上获得。现在,您可以启动 CPU 允许的任意数量的客户端,并根据需要管理 GPU 内存。本教程到此结束。希望对你有用,不要犹豫留下反馈!

更进一步

当然,这只是一个工作区的演示,还没有准备好进行真正的联邦实验,但是如果你需要更进一步,你可以尝试制作你自己的联邦数据集(现在我们为所有客户端加载相同的数据)或者使用像 LEAF 这样的基准。用 tqdm 包装培训和测试步骤,以获得更好的反馈,包括更详细的报告(精确度、召回率、F1 分数……)。添加一种通过加密或差分隐私保护模型的方法。诸如此类。你也可以查看更多的花卉教程来更好地掌握框架的可能性。

最后,Flower 的团队在他们最近的峰会上讨论了扩展问题,似乎虚拟客户端管理器的发布将允许解决这个问题,甚至通过允许每轮使用数千个客户端来进一步改进扩展,同时仍然考虑可用资源。

参考文献

*[1a] J. Konečný, H. B. McMahan, F. X. Yu, P. Richtárik, A. T. Suresh, and D. Bacon, [Federated Learning: Strategies for Improving Communication Efficiency](http://arxiv.org/abs/1610.05492) (2017), arXiv:1610.05492 [cs][1b] H. B. McMahan, E. Moore, D. Ramage, S. Hampson, and B. A. y Arcas, [Communication-Efficient Learning of Deep Networks from Decentralized Data](http://arxiv.org/abs/1602.05629) (2017), arXiv:1602.05629 [cs][2] Q. Yang, Y. Liu, Y. Cheng, Y. Kang, T. Chen, and H. Yu, “[Federated Learning](https://www.morganclaypool.com/toc/aim/1/1) (2019), Synthesis Lectures on Artificial Intelligence and Machine Learning, vol. 13, no. 3, pp. 1–207[3] A. N. Bhagoji, S. Chakraborty, P. Mittal, and S. Calo, [Analyzing Federated Learning through an Adversarial Lens](http://arxiv.org/abs/1811.12470) (2019) arXiv:1811.12470 [cs, stat][4] C. Yu et al., [Distributed Learning over Unreliable Networks](http://arxiv.org/abs/1810.07766) (2019), arXiv:1810.07766 [cs][5] K. Bonawitz et al., [Towards Federated Learning at Scale: System Design](http://arxiv.org/abs/1902.01046) (2019), arXiv:1902.01046 [cs, stat][6] D. J. Beutel et al., [Flower: A Friendly Federated Learning Research Framework](http://arxiv.org/abs/2007.14390) (2021), arXiv:2007.14390 [cs, stat]*

如何利用 Ray core 更快地训练时间序列预测?第 1 部分,共 3 部分。

原文:https://towardsdatascience.com/scaling-time-series-forecasting-with-ray-arima-and-prophet-e6c856e605ee?source=collection_archive---------17-----------------------

利用 ARIMA 和 Prophet 进行时间序列预测

图片作者。显示纽约市黄色出租车的乘坐量。本博客使用了 6 个月的历史数据。

预测是经营每项业务的重要组成部分。你需要知道生产什么和生产多少,特别是如果交货时间很长,以便为你的客户提供库存。如果你订购太多,你会有过多的库存,这会增加成本。如果你订得太少,你可能会失去销售机会。

如果你是一名数据科学家,在一家快速发展的公司负责维护预测,那该怎么办?产品、用户功能和相关数据都在快速变化。这意味着数据漂移是已知的。也就是说,输入模型要素的基础统计分布正在快速变化。这意味着您需要重新训练您的模型,可能每周或更长时间。

对于数据科学家来说,训练新模型不仅仅是训练最终模型所需的时间。开发时间包括训练所有候选模型、迭代它们以及选择最佳模型所花费的时间。如果训练一个模型需要几个小时,你就不能及时完成工作。您需要能够更快地迭代模型训练/推理。一种更快迭代的方法是将 Python 代码转换成并行 Python。

本博客将讨论以下主题:

  • 什么是统计预测?
  • 什么是 Ray,它如何轻松地分发您的 Python 模型训练和推理?
  • ARIMA 对雷
  • 先知靠雷
  • 什么是 Anyscale,它如何在云中的集群上运行你的光线分布代码

什么是统计预测?

预测模型通常分为两类:1)局部统计模型,和 2)全局深度学习模型。

“本地模型”意味着每个时间序列一次一个地被独立训练。也就是说,如果您有 20K 个要生成需求预测的项目,则需要训练 20K 个统计模型。每个模型还可以有其他独立变量来帮助预测,如天气,但每个模型都不知道其他模型。

“全球模型”是指每个时间序列都是一个整体模型的输入,该模型作为一个全球系统同时从所有输入中学习。每个时间序列输入通常被认为是深度学习模型中的一个神经元。直观上,如果产品之间存在相互关系,这是有意义的,这有助于提高所有产品的整体预测准确性。

这个博客是关于用本地统计模型进行预测的。下一篇博客将会是关于全球深度学习模型的预测。

雷是什么?

Ray 是由加州大学伯克利分校的 RISELab 开发的开源库,加州大学伯克利分校也开发了 Spark。Ray 通过使 Python 代码并行和分布式,使其易于扩展。分布式 Python 代码可以在任何类型的集群上运行:a)您自己的笔记本电脑内核,b) AWS、GCP 或任何常见的云。

Ray 及其生态系统与 ML 库合作,如 scikit-learn 、 XGBoost 、 LightGBM 、 PyTorch 、 TensorFlow 。Ray 是数据处理库 Modin 背后的最佳实践并行引擎,也与 Dask 一起工作。

射线生态系统,来自斯托伊察在 2021 年射线峰会上的主题演讲。

今天,分发代码是困难的。通常它涉及到将现有的 Python 重写为多处理 Python 或者将其转换为 PySpark 或 SparkSQL。Ray 可以很容易地将您现有的按顺序运行的 Python 代码转换成分布式应用程序,只需对代码进行最少的更改。最终的光线分布代码可以在底层硬件上并行运行。参见这篇关于将 12 小时的图像处理时间减少到 4 分钟的博客。另请参见这个视频演示,它是一个推荐系统,使用 xgboost,在 Anyscale 上进行超参数调优。

ARIMA 关于雷的例子

目前最常用的两种时间序列统计预测算法是 ARIMA 和预言家。在高层次上,ARIMA 假设过去和未来之间的因果关系。也就是说,时间 t+1 的预测值与过去发生的事情有潜在的关系。你可以把 ARIMA 想象成建筑公式。一个 ARIMA 模型由坐标(p,d,q)组成: p 代表自回归项的数量,把这看作季节性。 d 表示使时间序列稳定所需的差异数(即具有恒定的均值、方差和自相关)。 q 代表移动平均部分或者过去有多少数据点将被指数平滑以预测未来。

ARIMA 模型最初是用 R 编程语言开发的,后来转换成了 Python。较新的 Python 库之一是 pmdarima ,它实现了 Rob Hyndman 的 auto.arima() 。

下面所有的示例代码都使用 Python 3.8 和 Ray v1.8,可以在 github 上获得。要使用射线分发 ARIMA,请遵循以下步骤 1–5:

第一步。安装并导入射线和任意缩放。

*# !pip install "ray[default]" # Run distributed code
# !pip install pmdarima # ARIMA library
# !pip install anyscale # Run Ray distributed code on any cloud***import** os  *# Python os functions*
**import** ray  *# Run distributed code*
**import** numpy **as** np  *# Numerical processing*
**import** pandas **as** pd  *# Dataframe (tabular data) processing*
**import** matplotlib.pyplot **as** plt
**import** pickle*# Open-source ARIMA forecasting libraries*
**import** pmdarima **as** pm
**from** pmdarima.model_selection **import** train_test_split

第二步。本地启动一个 ray 服务器。这里的想法是你可以在本地测试你的分布式代码。快速迭代,把所有 bug 弄出来。在云上测试分布式代码的额外费用和时间之前。

1  # num_cpus, num_gpus are optional parameters
2  # by default Ray will detect and use all available 
3
4  ray.init()

第三步。将原始 ARIMA 训练函数转换为射线函数。假设这是最初的 ARIMA 列车功能。

1  **def** **train_model_ARIMA**(
2    theDF:pd.DataFrame, item_col:str,
3    item_value:str, target_col:str,
4    train_size:int=6,
5  ) -> list:
6
7    # split data into train/test
8    train, test = train_test_split(
9       theDF.loc[(theDF[item_col]==item_value), :],
10      train_size=train_size
11   )
12
13 # train and fit auto.arima model
14 model = pm.auto_arima(y=train[target_col])
15 **return** [train, test, model]

目前,ARIMA 需要一个额外的 pickle 步骤来确保 statsmodels 库对象被正确序列化(解释)。额外的酸洗/拆洗是为了便于携带。由于后台的代码将被分发,所以对象需要与 pickle 兼容。HIGHEST_PROTOCOL,因此它们可以在任何节点上的任何地方运行,并且可以再次被检索。Ray 可能会消除未来对泡菜解决方案的需求。

转换 ARIMA 训练函数的步骤:
a)添加光线装饰器,指定 3 个返回输出。增加一个额外的腌制步骤

下面是 ARIMA 火车功能的射线版本。函数名中多出来的_remote是文体的,为了明确哪个函数是并行的。粗体部分是代码与原始 Python 不同的地方。

1  #add the Ray decorator
2  **@ray.remote(num_returns=3)**
3  def train_model_ARIMA**_**remote(
4     theDF:pd.DataFrame, item_col:str,
5     item_value:str, target_col:str,
6     train_size:int=6,
7  ) -> list:
8
9     # split data into train/test
10    train, test = train_test_split(
11       theDF.loc[(theDF[item_col]==item_value), :],
12       train_size=train_size
13    )
14
15    # train and fit auto.arima model
16    model = pm.auto_arima(y=train[target_col])
17
18    # return [train, test, model] 
19    # here is the extra pickle step 
20    return [train, test, **pickle.dumps(model)**]

第四步。将原始的 ARIMA 推理函数转换成射线函数。 假设这是 ARIMA 推理的原始函数。

1  **def** **inference_model_ARIMA**(
2     model:"pmdarima.arima.arima.ARIMA",
3     test:pd.DataFrame,
4     item_col:str,
5     target_col:str,
6  ) -> pd.DataFrame:
7
8     # inference on test data
9     forecast = pd.DataFrame(
10        model.predict(
11            n_periods=test.shape[0], 
12            index=test.index,
13         )
14    )
15    **return** forecast

转换 ARIMA 推理函数的步骤:
a)添加光线装饰器。
这一次我们不需要额外的光线装饰选项,它默认返回 1 个对象。
b)将传递的模型对象的类型更改为仅字节
c)添加一个额外的取消拾取步骤

下面是 ARIMA 推论的雷版本。函数名中多出来的_remote是文体的,为了明确哪个函数是并行的。粗体部分是代码与原始 Python 不同的地方。

1  **@ray.remote**
2  def inference_model_ARIMA_remote(
3     model_pickle:**bytes**,
4     test:pd.DataFrame,
5     item_col:str,
6     target_col:str,
7  ) -> pd.DataFrame:
8
9     # Here is extra unpickle step
10    **model = pickle.loads(model_pickle)**
11 
12    # inference on test data
13    forecast = pd.DataFrame(
14        model.predict(
15            n_periods=test.shape[0], 
16            index=test.index,
17         )
18    )
19    return forecast

第五步。现在调用分布式函数,而不是调用原来的训练和推理函数。
假设 ARIMA 训练和推理函数最初就是这样被调用的。

1   model = []
2   train = []
3   test = []
4
5   # Train every model
6   train, test, model = **map**(
7   **list**,
8   **zip**(
9       *(
10          [
11              train_model_ARIMA(
12                  g_month.set_index("time"),
13                  item_col="pulocationid",
14                  item_value=v,
15                  target_col="trip_quantity",
16                  train_size=6,
17              )
18              **for p, v in enumerate**(item_list)
19          ]
20      )
21    ),
22  )
23
24  # Inference every model
25  forecast = [
26      inference_model_ARIMA(
27          model[p], test[p], item_col="pulocationid", 
28          target_col="trip_quantity"
29      )
30      **for p in range**(len(item_list))
31  ]

Ray 执行并行远程计算,直到您请求对象。Ray remote compute,或者说 Python“期货”,并不像 spark 处理中那样“懒惰”。的。远程()调用是并行异步进行的。使用ray.get()在未来的某个时间检索值。在这一点上,所有的分布式代码执行被收集起来并返回给用户。在后台,光线对象引用被转换回 pandas 数据帧,或者用户请求的任何类型的对象。

转换 ARIMA 训练和推理调用函数的步骤:
a)用。remote()方法
b)使用 ray.get()获得预测

下面是用 Python 调用 ARIMA 训练和推理函数的 Ray 版本。因为我们在这里使用的是 Ray Core,所以我们需要手动执行额外的 ray.put(data)步骤。粗体部分是代码与原始 Python 不同的地方。

1   model = []
2   train = []
3   test = [] 
4  
5   # initialize data in ray object store on each compute node
6   **input_data_ref = ray.put(g_month.copy())**
7
8   # Train every model
9   train, test, model = map(
10  list,
11  zip(
12      *(
13          [   # call Ray functions using .remote() method
14              train_model_ARIMA_remote**.remote**(
15                  **input_data_ref**.set_index("time"),
16                  item_col="pulocationid",
17                  item_value=v,
18                  target_col="trip_quantity",
19                  train_size=6,
20              )
21              for p, v in enumerate(item_list)
22          ]
23      )
24    ),
25  )
26
27  # Inference every model
28  forecast_obj_refs = [ 
29      # call Ray functions using .remote() method
30      inference_model_ARIMA_remote**.remote**(
31          model[p], test[p], item_col="pulocationid", 
32          target_col="trip_quantity"
33      )
34      for p in range(len(item_list))
35  ]
36 
37  # ray.get() means block until all objectIDs are available 
38  **forecast = ray.get(forecast_obj_refs)**

就是这样!您刚刚使用 Ray 发布了 ARIMA 训练和推理!所有项目的 ARIMA 预测现在将并行处理。样本输出如下所示。

仅显示 2 个预测,而不是所有预测。在笔记本电脑上运行的屏幕截图。

光线上的先知示例

Prophet 是广义可加模型的一个特例。ARIMA 试图建立一个未来值的公式作为过去值的函数,而预言家试图发现“变化点”;你可以把 Prophet 想象成曲线拟合。

来自脸书的最新开源库 Kats ,包括 original Prophet 以及用于多元预测、深度学习预测和异常检测的更新库。对 githubs 的进一步检查表明,Kats 里面的先知比最初的先知维护得更晚。

下面所有的示例代码都使用 Python 3.8 和 Ray v1.8,可以在 github 上获得。要使用光线分发 Prophet,请遵循下面的步骤 1–5:

第一步。安装并导入射线和任意缩放。

*# !pip install "ray[default]" # Run distributed code
# !pip install kats # Prophet library
# !pip install anyscale # Run Ray distributed code on any cloud***import** os  *# Python os functions*
**import** ray  *# Run distributed code*
**import** numpy **as** np  *# Numerical processing*
**import** pandas **as** pd  *# Dataframe (tabular data) processing*
**import** matplotlib.pyplot **as** plt*# Open-source Prophet forecasting libraries*
**import** kats
**from** kats.consts **import** TimeSeriesData
**from** kats.models.prophet **import** ProphetModel, ProphetParams

第二步。在本地启动一个 ray 服务器,告诉它可以使用多少个处理器

1  # num_cpus, num_gpus are optional parameters
2  # by default Ray will detect and use all available 
3
4  ray.init()

第三步。没事!不需要改变原有的 Python train 和推理 Prophet 函数。

第四步。给你现有的训练和推理函数添加一个光线装饰器。装饰者可以通过复制/粘贴/修改 def 函数代码来手动添加,正如我们在上面的 ARIMA 例子中所展示的。装饰者可以通过声明的方式添加。当您不需要修改原始代码时,请这样做。

1  # Ray parallel function declaration, no change to original code
2  **train_model_PROPHET_remote =  ray.remote(train_model_PROPHET).options(num_returns=3)**
3  **inference_model_PROPHET_remote = ray.remote(inference_model_PROPHET)**

第五步。现在调用分布式函数,而不是调用原来的训练和推理函数。
假设这就是最初先知训练和推理功能被调用的方式。

1   train = []
2   test = []
3   model = []
4   forecast = []
5
6   input_data_ref **=** ray**.**put(g_month**.**copy())
7
8   # Train every model
9   train, test, model = **map**(**list**, **zip**(*([train_model_PROPHET(**input_data_ref**,
9                     item_col='pulocationid',
10                    item_value=v,
11                    target_col='trip_quantity',
12                    train_size=6) 
13               **for p,v in enumerate**(item_list)] )))
14
15  # Inference every model
16  forecast = [inference_model_PROPHET(model[p],
17                test[p],
18                item_col='pulocationid',
19                target_col='trip_quantity') 
20             **for p in range**(len(item_list))]

转换 Prophet 训练和推理调用函数的步骤:
a)用。remote()方法
b)使用 ray.get()获得预测。

下面是 Python 中调用 Prophet train 和推理函数的 Ray 版本。为了清楚起见,我重命名了 train,test 函数名,但这不是必需的。因为我们在这里使用的是 Ray Core,所以我们需要手动执行额外的 ray.put(data)步骤。粗体部分是代码与原始 Python 不同的地方。

1   train = []
2   test = []
3   model = []
4   forecast = [] 
5
6   # initialize data in ray object store on each compute node
7   **input_data_ref = ray.put(g_month.copy())** 8 
9   # Train every model
10  train, test, model = map(list, zip(*(
11               # call Ray functions using .remote() method
12               [train_model_PROPHET_remote**.remote**(
13 **input_data_ref**,
14                    item_col='pulocationid',
15                    item_value=v,
16                    target_col='trip_quantity',
17                    train_size=6) 
18               for p,v in enumerate(item_list)] )))
19
20  # Inference every model
21  **forecast_obj_refs** = 
22             # call Ray functions using .remote() method
23             [inference_model_PROPHET_remote**.remote**(model[p],
24                test[p],
25                item_col='pulocationid',
26                target_col='trip_quantity') 
27             for p in range(len(item_list))] 
28
29  # ray.get() means block until all objectIDs are available
30  forecast_PROPHET = **ray.get(forecast_obj_refs)**

就是这样!您刚刚使用 Ray 分发了先知训练和推理!

这些小调整将 Prophet 在笔记本电脑上的运行时间减少了 4 倍。即 300%的提速。有关更多运行时间,请参见本文末尾的表格。

注意:我们观察到 Prophet 的速度有所提高,因为它的运行时间比 ARIMA 长。作为“统计模型”, ARIMA 和预言家都采用非常小的数据输入,因此我们可以期待通过分发代码得到相应大小的加速。

什么是 Anyscale?

一旦在本地内核上使用 Ray 调试和测试了新发布的 Python 代码,就可以在云中运行相同的代码了。

Anyscale 简化了 Ray 应用的构建、运行和管理,在云中加速计算,跨集群并行运行分布式代码,使用内置的自动扩展规则,并兼容所有常用的云提供商(AWS、GCP 等)。Anyscale 适用于多云策略,因为它的使用不会产生供应商锁定。Anyscale 仅依赖于基本的计算实例,这使得它的运营成本低于许多作为服务出售的打包产品。

注意:目前 Anyscale 只能通过邀请私人测试版获得。在这里报名试试吧。

下面是开始使用 Anyscale 的 3 步快速指南。更多详情参见【入门】。Anyscale 可以从 web UI 控制台(下面的截图)或。在幕后,Anyscale 使用了开源的 Ray 。

第一步。通过您的 Anyscale 帐户认证进入云。您只需要这样做一次。

  • 打开控制台https://console.anyscale.com/credentials
  • ****复制创建 json 文件命令
  • 粘贴到您的本地终端。****

第二步。从应用程序中运行 ray.init()

**注释掉之前的 ray init(),它在本地运行 ray 服务器。
`# NUM_CPU = 8

ray.init( ignore_reinit_error=True , num_cpus = NUM_CPU)`**

****添加一个新的 ray.init()来连接集群

my_env={"working_dir": ".", "pip": ["pmdarima", "kats"]} ray.init("anyscale://my_cluster_name", runtime_env=my_env)

您可以指定更多的 pip 安装,克隆一个 github repo,或者将整个代码目录复制到集群或者运行时环境。首先使用集群配置,然后运行时配置(如果指定)将覆盖集群配置。配置的结果是在云集群的每个节点上自动安装额外的库、代码和数据。

上面,我使用了一个默认的集群配置(它指定了自动伸缩策略),并且我把额外的pip install放在运行时配置中。url 字符串"anyscale://my_cluster_name"中的my_cluster_name将成为您的新集群名。

****第三步。像平常一样运行 python 代码(或笔记本)。它会自动并行运行,分布在云中的各个集群上!

当您的应用程序运行时,您可以在Clusters下的 Anyscale 控制台中监控您的集群使用情况。

下表总结了所有运行时间。注意,未并行化的 Python 代码不会加速,即使在典型的云中运行也是如此。

*** #items 表示行项目的数量或要预测的不同时间序列的数量。笔记本电脑是 macbook pro (13 英寸,M1,2020 年)。AWS 上的 Anyscale 设置为:头节点类型 m5.2xlarge,工作节点 m5.4xlarge 和 g4dn.4xlarge,自动缩放打开,最大 10。*在云中运行的常规 Python(非光线分布代码)只在 1 个 CPU(头节点)上运行。

结论

这篇文章介绍了数据科学家如何轻松地对他们的 ARIMA 和先知模型进行训练和推理。使用 Ray,不必重写底层的现有代码库。****

Ray 分布式 Python 代码是:

  1. 由本地 Ray 服务器跨本地笔记本电脑内核并行分发和执行。
  2. Anyscale 集群管理,在云中并行分发和执行。

这展示了一些非常强大的东西。世界上有很多 ML Python 代码。要在云上纵向扩展,仅仅在云上运行代码是不够的。请参见上面的运行时表。该代码必须重新编写成分布式 Python 代码;否则,它将只在一个节点上运行。

一些读者可能想知道,这些用于轻松分发时间序列预测 Python 代码的模式是否也可以用于分发任何大型数据输入和任何 AI/ML 算法?我觉得答案是肯定的!****

在这个更快预测的博客系列中,第 2 部分将讨论大型深度学习模型的并行化。

资源

  1. 雷 doc 页数:https://docs.ray.io/en/latest/using-ray.html
  2. Anyscale 文档页数:https://docs.anyscale.com/get-started
  3. **统计时间序列预测方法背景介绍:【https://otexts.com/fpp3/ **
  4. 背景介绍在令人尴尬的平行模式中,这里使用了排比:https://en.wikipedia.org/wiki/Embarrassingly_parallel

原载于https://www.anyscale.com**

请随意使用我的截图和代码,但请做一个好公民,如果你想在自己的工作中使用它们,请记得注明出处。

如果你对我的文章或人工智能或人工智能有任何反馈、评论或有趣的见解要分享,请随时在我的 LinkedIn 上联系我。

借助 Dask,以闪电般的速度运行繁重的级长工作流

原文:https://towardsdatascience.com/scaling-your-prefect-workflow-to-the-cloud-2dec4e0b213b?source=collection_archive---------24-----------------------

借助 Dask 和 Coiled 实现云原生工作流自动化

图片由比利·胡恩通过unsplash.com拍摄

Prefect 是一个流行的用于自动化工作流程编排的开源 Python 库。当运行即时可用的 Prefect 时,工作流的编排在云中完成,而代码的实际计算在本地机器上完成。这意味着您的完美云工作流受到您的机器资源的限制。

这篇博文将向你展示:

  1. 如何使用 DaskExecutor 在本地完美工作流中利用并行性
  2. 何时以及如何将 DaskExecutor 连接到云计算集群,以超越本地机器的限制。
  3. 如何使用 Prefect ResourceManager 构建一个工作流,仅在必要时将计算委托给云中的集群。

你也可以用这个完美的 Python 脚本直接进入代码。

通过并行计算加快处理速度

完美的工作流使用流程和任务构建数据管道,这些流程和任务由执行者编排。提督的默认执行器按顺序运行任务。这对于简单的工作流来说没什么问题,但这意味着您的计算可能会比需要的速度慢,因为它们没有充分利用可用资源。

作为一名数据科学家或工程师,您会希望通过切换到 DaskExecutor 来优化性能。这将利用您的本地机器的多个核心,加速计算繁重的任务,如加入,洗牌和机器学习作业。

# create a temporary local Dask Executor 
executor = DaskExecutor()

当达到机器的内存极限时,可以将 DaskExecutor 连接到云计算资源,以便在多台机器上分配工作。一种方法是旋转一个盘绕的集群,并在那里运行您的完美流,方法是:

  • 在远程集群上运行您的整个完美工作流,例如通过使用盘绕集群作为您的 DaskExecutor ,
  • 在远程集群上运行特定的完美任务,例如通过在特定的计算密集型任务中旋转盘绕的集群,
  • 仅当数据集的大小超过某个阈值时,才在远程集群上运行特定的提督任务,例如通过使用提督资源管理器对象内的盘绕集群

上述成卷文件的链接包括前两种方法的例子;下一节将向您展示如何编写一个完美的脚本,当数据集的大小超过某个阈值时,将计算委托给一个盘绕的集群。

免责声明:我在 Coiled 工作,是一名数据科学传播者。CoiledDask的最初作者 Matthew Rocklin 创立,是一个面向分布式计算的开源 Python 库。

使用 Dask 的自动化完美 ETL 管道

下面的代码示例将 Github 归档数据集从 JSON 转换为 Parquet,并将其写入云存储,每当数据集变得太大而无法在本地处理时,就利用 Coiled 的计算资源来这样做。这意味着您可以在云上转换整个 75GB 的数据集,而不必将其下载到您的机器上。你可以在这里找到完整的 Python 脚本。

ETL 工作流看起来会像这样:

作者图片

定义您的完美任务运行

让我们从定义我们想要运行的任务开始。如果你习惯于定义一个提督任务,可以随意向下滚动到下一部分,将你的提督工作流程连接到 Coiled。

让我们按照任务运行的顺序来定义任务。我们将从 Prefect 任务开始,该任务将创建我们想要从 Github 存档中获取的文件名列表。关于我们如何构建这些代码的更多背景信息,请看一下这个 Jupyter 笔记本。

@task 
def create_list(start_date, end_date, format="%d-%m-%Y"): start = datetime.datetime.strptime(start_date, format) end = datetime.datetime.strptime(end_date, format) date_generated = [start + datetime.timedelta(days=x) for x in range(0, (end-start).days)] prefix = "https://data.gharchive.org/" filenames = []     for date in date_generated: for hour in range(1,24): filenames.append(prefix + date.strftime("%Y-%m-%d") + '-' + str(hour) + '.json.gz') return filenames

接下来,让我们定义一个任务,这个任务将决定我们的流将旋转的集群的类型。我们将使用len(filenames)作为数据集大小的代理,您也可以考虑其他方法来估计内存中数据的大小。

@task 
def determine_cluster_type(filenames): if len(filenames) > 100: return "coiled" return "local"

我们还需要一个任务来获取在filenames列表中指定的数据...

@task 
def get_github_data(filenames): records = db.read_text(filenames).map(ujson.loads) push_events = records.filter(lambda record: record["type"] == "PushEvent") return push_events

…一个将原始 JSON 数据转换成表格数据帧格式的Task ...

@task
def to_dataframe(push_events): def process(record): try: for commit in record["payload"]["commits"]: yield { "user": record["actor"]["login"], "repo": record["repo"]["name"], "created_at": record["created_at"], "message": commit["message"], "author": commit["author"]["name"], }   except KeyError: pass   processed = push_events.map(process) df = processed.flatten().to_dataframe() return df

…和一个Task将展平的数据帧作为拼花文件写入我们的 S3 存储桶。

@task
def to_parquet(df, path): df.to_parquet(path, engine='fastparquet', compression='lz4' )

具有完美资源管理器的 Dask 集群设置

太棒了,我们已经定义了流程中的所有任务。下一步是定义我们的流可以使用的两种集群类型:local 用于当数据集小到足以在本地处理时,而coiled 则用于其他情况。

使用临时云计算资源时,您需要确保这些资源得到正确的实例化、使用和清理,以避免错误和不必要的成本。我们将通过定义所需的__init__setupclose块,使用提督[ResourceManager](https://docs.prefect.io/core/idioms/resource-manager.html)对象来完成这项工作。确保在__init__ 定义中包含任何想要传递给集群的关键字参数。这个块创建 Dask 集群,包括 Dask 调度程序,并将其连接到您的本地客户机。

# Define a ResourceManager object 
@resource_manager 
class DaskCluster:   def __init__(self, cluster_type="local", n_workers=None, software=None, account=None, name=None): self.cluster_type = cluster_type self.n_workers = n_workers self.software = software self.account = account self.name = name   def setup(self):  if self.cluster_type == "local": return Client(processes=False) elif self.cluster_type == "coiled": cluster = coiled.Cluster(name = self.name, software = self.software, n_workers = self.n_workers, account = self.account) return Client(cluster)   def cleanup(self, client): client.close() if self.cluster_type == "coiled": client.cluster.close()

构建完美的 Dask 数据工程流程

现在您已经定义了您的TasksResourceManager,下一步是告诉 Prefect 这些任务是如何相互关联的,以及您将需要它们如何运行。我们还将定义一些可以根据用户运行的流程进行调整的 Parameters

# Build Prefect Flow 
with Flow(name="Github ETL Test") as flow: # define parameters n_workers = Parameter("n_workers", default=4) software = Parameter("software", default='coiled-examples/prefect') account = Parameter("account", default=None) name = Parameter("name", default='cluster-name') start_date = Parameter("start_date", default="01-01-2015") end_date = Parameter("end_date", default="31-12-2015") # build flow filenames = create_list(start_date=start_date, end_date=end_date) cluster_type = determine_cluster_type(filenames) # use ResourceManager object with DaskCluster(cluster_type=cluster_type, n_workers=n_workers, software=software, account=account, name=name ) as client: push_events = get_github_data(filenames) df = to_dataframe(push_events) to_parquet(df)

干得好!您现在已经准备好运行您的完美流程了。继续尝试使用各种参数值end_date来查看条件盘绕式集群起转的效果:将end_date设置为“06-01-2015”之后的任何值都会将您的计算委托给一个集群。

您还可以通过将可选的Parameter值传递给flow.run()来定制 Dask 集群,例如n_workers和集群name。如果您想调整 Dask 集群的其他特性,比如 Dask worker 和 Dask scheduler 内存或空闲超时,您必须在DaskCluster__init__块中包含相应的关键字参数。

# Run flow with parameters 
flow.run(parameters=dict(end_date="02-01-2015", n_workers=15,  name="prefect-on-coiled") 
)

完美的 Dask 工作流程编排摘要

在本文中,我们讨论了何时以及如何使用 Dask 和 Coiled 在本地或云中的完美工作流中利用并行性。然后,我们构建了一个完美的数据工程工作流,每当数据集的大小超过某个阈值时,它就将计算委托给 Dask 集群。

https://coiled.io/blog/common-dask-mistakes/

主要要点:

  • 您可以使用基于云的集群,如 Coiled,将您的完美工作流扩展到大于内存的数据集
  • 您可以编写自己的代码,以便 Dask 集群只在真正需要的时候才启动

在 LinkedIn 上关注我获取更多类似内容!

原载于 2021 年 9 月 22 日https://coiled . io

扫描生产应用程序中的环境变量

原文:https://towardsdatascience.com/scan-environment-variables-in-production-application-4482a4830da1?source=collection_archive---------31-----------------------

照片由埃琳娜·莫日维洛在 Unsplash 拍摄

没有环境变量,就没有完整的生产应用程序。大多数项目将环境变量作为事实保存在.env中。随着应用程序中环境变量的增加,很难在生产中准确无误地跟踪它们。

在部署应用程序时,在生产环境中定义或更新 env 变量并不是一种理想的方式。

为此,我创建了一个简单的 npm 包 scan-env。这个简单的包扫描应用程序环境变量,并打印缺少环境变量的报告。

自己去试试链接。

在下一节中,我将创建一个虚拟项目来使用它。

入门指南

创建一个scanenv-demo项目。

使用npm init -y初始化项目。

安装扫描环境

npm install scan-env

项目结构

scanenv-demo
|- index.js
|- .env
|- .env.example

在项目中创建 3 个文件index.js.env.env.example

index.js

将下面的代码复制并粘贴到index.js中。

const scan = require("scan-env");// scan env
const status = scan();if (!status) {console.error("Missing Envs.");process.exit(1);
}console.log(`${process.env.GREETING} ${process.env.REGION} from ${process.env.FROM}.`
);

。环境

从这个文件中,应用程序将使用环境变量。

将以下按键复制并粘贴到.env中。

GREETING=Namaste
REGION=World

env . example

列出应用程序所需的所有环境变量。

复制并粘贴.env.example中的以下按键。

GREETING=Anything
REGION=Anything
FROM=Anything

注意:.env.example的语法必须和.env一样。

运行应用程序

FROM键在.env中丢失。我们在等一份丢失的报告。

node index.js

输出

Env Scan Report
--------------------------------------------------------------------
Total Missing Environment Variables:
1Missing Environment Variables:
FROM
--------------------------------------------------------------------

.env中增加FROM

GREETING=Namaste
REGION=World
FROM=India

使用node index.js运行应用程序。

输出

Namaste World from India.

忽略环境变量

当您在不同的环境中运行应用程序时,您可能希望忽略一些环境变量。

为此使用.envignore文件。

创建一个.envignore文件并粘贴下面的密钥。

REGION=Anything

默认情况下,scanenv检查.envignore是否存在。如果它存在,那么它会忽略这些键。

运行应用程序node index.js

输出

Namaste undefined from India.

结论

如果文件名不是.env.env.example.envignore,则以相同的顺序将文件名传递给函数。

感谢阅读。

使用 Plotly 在地图上散布图

原文:https://towardsdatascience.com/scatter-plots-on-maps-using-plotly-79f16aee17d0?source=collection_archive---------11-----------------------

了解如何用很少的代码创建交互式散点图来表示数据中的多个要素

由 GeoJango Maps 在 Unsplash 拍摄的照片

Plotly 和 Mapbox 带来了什么

Plotly 是一个强大的可视化库,提供了令人惊叹的功能,如交互式、动态、易于使用、高度详细的图。 Plotly Express 是 Plotly 库的内置部分,提供高级 API,只需很少的代码即可绘制各种图形。我们将结合使用 Plotly express 和 Mapbox。 Mapbox 是一个位置数据平台,为地图、路由和导航提供各种 API。

想象旧金山周围的机场

在本文中,我们将使用 Kaggle 的 旧金山 Airbnb 房源数据集来直观地分析价格、住宿类型及其面积之间的关系。只要看一下地图,我们就可以看出地区如何影响价格。

这个数据有很多列,但是和这篇文章有关的是纬度经度价格房型。价格是每晚的价格,房间类型指示 Airbnb 是酒店、私人房间、共享房间还是整个房子。

这是数据的样子

我们将在图中使用的列(图片由作者提供)

让我们开始吧!

  1. 首先,我们需要一个地图框访问令牌。别担心,一定数量的通话是完全免费的,注册时不需要任何卡的详细信息。你可以在这里免费获得你的令牌。注册后,令牌将出现在您的帐户页面下。
  2. 将价格转换为浮动。价格列当前包含带有 '$ '符号和逗号的字符串。例如1200 美元。这里有一个将整个列转换成浮点值的超级快速的方法。这会骗人的
import localelocale.setlocale(locale.LC_ALL, 'en_US.UTF8')data['price']=data['price'].apply(lambda x: locale.atof(x.strip("$")))

3.现在最精彩的部分来了,剧情。如果你在 Colab 上运行这段代码,我建议你不要安装 Plotly。否则你可以安装它。

import plotly.express as px

在这之后,我们只需要编写如下所示的代码。这里,我们首先传入我们的 Mapbox 访问令牌。然后,我们使用带有以下参数的函数 px.scatter_mapbox。

  • 数据:这是指我们的数据框架
  • lat=" latitude ":这指向我们的数据框中的纬度列。
  • lon=" longitude ":它指向数据框中的经度列。
  • color="room_type ":这基本上是根据 room_type 列中的值给每个数据点着色。因为我们有四种不同的房间类型,所以我们有四种颜色。
  • size=" price ":这是有趣的部分,每个 Airbnb 都将由一个大小与其价格成比例的气泡来表示。较便宜的航空公司将由较小的气泡表示,而昂贵的航空公司将由较大的气泡表示。
  • 其他参数只是一些实用工具,有助于使图形更容易查看。

旧金山的 Airbnbs,按房间类型和价格分类

4.随意使用绘图右上角的选项,例如缩放、选择特定区域或下载图像。你可以放大到特定的区域来查看地图的详细程度,也可以注意到当我们将悬停在气泡上时,酒店的信息是如何显示的!单击图例中的任何房间类型都会从地图中移除该类型的所有房间!

寻找趋势

我们看到一些趋势是有道理的。然而,在得出结论之前,请注意 Airbnb 的价格取决于各种便利设施和其他特征,如规模、服务、主机受欢迎程度等。但是,还是能看出一些大趋势。

  1. 靠近太平洋高地和俄罗斯山的房子比旧金山其他地方的房价要高。
  2. 缩小后,我们注意到房屋的密度并不均匀。地图右半部分的房间比左半部分多得多。
  3. 我们看到大多数房间要么是功能齐全的公寓,要么是私人房间。
  4. 卡尔街附近有一套价格异常高的公寓,售价 9999 美元,不知道那是什么?

结论

多亏了 Plotly Express 和 Mapbox,我们几乎不费吹灰之力就能绘制出如此强大、信息丰富的图表。这只是 Plotly 提供的一种地质公园。你可以在这里 查看他们的其他地图地块

如果你喜欢这篇文章,这里有更多!

其他一些项目。可以联系我 这里 感谢您的配合!

用 Plotly Express 散点图

原文:https://towardsdatascience.com/scatter-plots-with-plotly-express-1b7f5579919b?source=collection_archive---------35-----------------------

趋势线&刻面

照片来自 Unsplash 上的 Mel Poole

阴谋地

你可以说这是一个数据科学的梦之队:哈佛大学的物理学博士,硅谷的能源分析师,脸书的成员,还有一个在硅谷的研究创业公司工作的人。Alex Johnson、Jack Parmer、Matthew Sundquist 和 Chris Parmer一起在加拿大蒙特利尔创建了一家技术计算公司 Plotly

Plotly 可视化工具是在 2013 年左右使用 Python 和 Django 框架构建的,前端使用 JavaScript 和可视化库 D3.js、HTML 和 CSS。所以,在其核心, Plotly 实际上是一个 JavaScript 库。请记住,D3.js 是一个 JavaScript 库,用于在 web 浏览器中生成动态的、交互式数据可视化。但是 Python 开发人员几乎不需要直接接触 JavaScript 库。他们可以使用 plotly.py ,这是一个交互式的、开源的、基于浏览器的 Python 图形库。

2019 年,该公司推出了新的高级 Python 可视化库 Plotly Express,实现了数量上的飞跃。 Plotly Express 是 Plotly.py 的高级包装器,与 Plotly 生态系统的其余部分完全兼容,简单、强大,有点类似 Seaborn。它是免费的,可以用于商业应用和产品。该库包括绘制趋势线和地图的功能,以及执行刻面和动画的功能。使用 Plotly Express,您可以在线制作交互式图形,也可以离线保存。

可以使用*pip*安装 plotly 的最新版本:

$ pip install plotly==4.14.3

或者*conda:*

$ conda install -c plotly plotly=4.14.3

散点图

散点图用于确定一对数值变量是否相关。它们适用于两个数值变量的分布分析。它们不应该用来显示一段时间内的趋势。它们也不适合用于比较分析。此外,当消息的本质是显示数量时,它们是不合适的。

从表示由笛卡尔平面 x-y 中的点指示的两个数值变量的数据集开始,通过揭示相关性的存在与否,从这些数据点生成的形状叙述信息。这种相关性可以是正的或负的,并且通过表示大量数据点来获得。尽管每个点都指示精确的数值,但是可视化的目的是确定所表示的数值变量之间是否存在关系或相关性,而不是关注所指示的精确值。

相关性被定义为评估两个变量之间关系的一种度量(一种度量标准)。您可以计算(使用等式)取值在 1 到-1 之间的相关系数 (r) :接近 0 的值表示没有相关性;接近 1 的值表示两个变量之间有很强的直接关系;接近-1 的值表示它们之间有很强的反比关系;大于 0.7 或-0.7 的值分别表示强的正相关或负相关;低于 0.3 或-0.3 的值表示弱的或无效的直接或反向关系。

图 1:具有不同相关系数的散点图。来源:维基百科[1]

在散点图中可以发现数据集的三个重要特征:1 .- 离群值,一段与数据集中所有其他数据都非常不同并且似乎不符合相同模式的数据。这些异常值可能代表有价值的信息来分析。首先必须验证这些异常值的存在不是因为测量数据时的误差;2.- 间隙,不包含数据的区间。数据之间差距的可视化证明了解释其存在的深入分析的合理性;3.- 聚类,孤立的数据点组,也值得对其出现在图表中的原因进行特殊分析。当然,差距和聚类也可能代表数据收集方法中的错误。

一条回归线被习惯性地添加到散点图中。也被称为最佳拟合线趋势线,它以数学方式表达了两个数值变量之间的关系。趋势线意在模拟数据的趋势。这是一条最佳拟合线,近似于绘制点的方向。通常,回归线的目的是利用插值技术估计独立变量的一些未测量值,或者通过外推法将其用于预测目的。应特别注意不要混淆相关性和因果关系

用 Plotly Express 散点图

首先,我们导入 Plotly Express 通常为*px*:

import plotly.express as px

在我们的第一个例子中,我们将确定使用 Numpy 函数random.randint:生成的两个数据集之间是否存在相关性

import plotly.express as px
import numpy as np## set the seed to a random number obtained from /dev/urandom
## or its Windows analog, 
## or if neither of those is available, it will use the clock.np.random.seed()rand_x = np.random.randint(1,100,50)
rand_y = np.random.randint(1,100,50)## trendline = 'ols' allow us to draw a trendline
fig = px.scatter(x = rand_x, y = rand_y, trendline = 'ols')fig.write_image(path + "figscat.png")
fig.show()

图 2:用 Plotly Express 制作的散点图。

很明显,没有关联。趋势线基于普通最小二乘法(OLS) 。以前,您必须安装*statsmodels* 及其依赖项才能使用参数trendline

It's a nice chart, but not enough for today's standards. We can improve it significantly using functions from the Plotly Express library.

我们建议使用类似以下的过程:

首先,选择一种有效的可视编码,将数据值映射到图形特征,并确定相应的参数。对于本文中的散点图,Plotly Express 函数为px.scatter,对应的参数为:data framex 轴的数值变量; y 轴的数值变量;color包括其他变量,尤其是分类变量;回归线的*trendline*方法;以及悬停数据悬停名称hover_name属性控制哪一列以粗体显示为工具提示标题,而hover_data参数接受要添加到悬停工具提示中的列名列表。

第二,分析你讲故事是否需要刻面。分面方法将图表分割成面板矩阵,每个面板基于分类变量显示数据集的不同子集。可以按列facet_col = [‘col_name’],也可以按行。

三、用update.layout更新图表:设置标题;x 轴的名称和 y 轴的名称;用 widthheight.设定图形尺寸

第四,用add_annotation添加注释。请记住,注释与图表中任意位置的文本相关。它们通常由文本、箭头或其他形状组成。

第五,分析是否需要动画帧。动画图是显示基础数据动态的好方法。动态系统是随着时间发展的系统。我们将不包括动画,因为我们的数据帧不包括时间维度。

最后,导出文件:你可以使用fig.write_image(path + “fig_scat.png”)将文件导出为静态格式,如 PDFSVGPNG、或类似格式,或者保存为 HTML 页面,用于缩放或查看工具提示fig.write_html(path + ‘fig_scat.html’).

趋势线和用 Plotly Express 刻面

我们使用了从 Kaggle [2]下载的数据集。该数据集由 10,000 名银行客户组成,涉及他们的年龄、工资、婚姻状况、信用卡限额、信用卡类别、教育水平和其他特征。银行经理对客户离开他们的信用卡服务(流失客户)感到不安。所以,我们要确定一些数值变量和损耗条件之间是否有任何关系。

为了避免拥挤和重叠,我们通过df = df.sample(frac = 0.15).从这些客户中随机选择了 1500 个作为被研究人群的样本

首先,我们分析了客户年龄和银行规定的信贷限额之间是否有任何关系。因此,Customer_Age被选为 x 轴的数值变量,Credit_Limit被选为 y 轴的数值变量。我们纳入了color = ‘Gender’来验证银行管理指令中是否存在性别偏见。我们将图表保存为 PNG 静态格式。如果你想使用下面的代码,不要忘记import pandas as pd.

df = pd.read_csv(path + 'CreditCardCustomersCols.csv', index_col =  False, header = 0,sep = ';', engine='python')## get a random selection of 1500 rows, a fifteen-percent of the    ## dataframedf = df.sample(frac = 0.15)fig = px.scatter(df, x="Customer_Age", y="Credit_Limit", color =   'Gender',   trendline = ‘ols’)fig.update_layout(title ='Relation between Customer Age and Credit Limit',xaxis_title = 'Customer Age', yaxis_title = 'Credit Limit',width = 1600, height = 1400)fig.write_image(path + "figscat1.png")fig.show()

图 3:银行客户样本的散点图和趋势线。用 Plotly Express 制作的图表

Plotly Express 允许您使用 results = px.get_trendline_results(fig)检索模型参数。您可以在.summary()中选择完整的统计汇总,或.params中选择直线方程,或.rsquared中选择拟合的统计测量。

# retrieve the model parametersresults = px.get_trendline_results(fig)
results = results.iloc[0]["px_fit_results"].summary()coef1 = results.iloc[0]["px_fit_results"].params
coef2 = results.iloc[1]["px_fit_results"].paramsrsq1 = px.get_trendline_results(fig).px_fit_results.iloc[0].rsquared
rsq2 = px.get_trendline_results(fig).px_fit_results.iloc[1].rsquaredprint(results)
print(coef1, coef2)
print(rsq1, rsq2)

图 4 显示了分类变量facet_col = ‘Attrition_Flag’上的相同数字变量。该变量只取以下两个值:[现有客户,流失客户]。相似的弱相关性和性别偏见显示在两个面板中。

图 4:与图 3 相同的数据,但是在分类变量 attachment _ Flag 上分面。用 Plotly Express 制作的图表

在下面的两个图中,我们更改了其中一个数字变量(上的个月,而不是客户年龄),替换了与颜色参数关联的分类变量(婚姻状况),并添加了一个注释( 35K 集群)。第二张图是第一张图的多面版本(facet_col = ‘Attrition_Flag’)),也是基于与客户流失情况相关的分类变量。这两个数字都导出为 HTML 文件,工具提示中包含变量教育水平

fig2 = px.scatter(df, x="Months_on_book", y="Credit_Limit", color = 'Marital_Status',hover_data = ['Education_Level'])fig2.update_layout(title = 'Relation between Months on Book and Credit Limit',xaxis_title = 'Months on Book' , yaxis_title = 'Credit Limit', width = 800, height = 600)fig2.add_annotation( # add a text callout with arrowtext="35K Cluster", x= 35, y=35000, arrowhead=1,showarrow=True)fig2.write_html(path + 'figscat2.html')fig2.show() 

图 5:银行客户样本的散点图。用 Plotly Express 制作的图表。

图 6:与图 5 相同的数据,但是在分类变量 attachment _ Flag 上分面。用 Plotly Express 制作的图表

数字变量之间没有相关性。此外,变量和磨损情况或婚姻状况之间没有关系。我们决定不画趋势线,因为屏幕上超过两条回归线可能会让观众迷惑。

总结一下:

当希望显示两个数值变量之间的关系或相关性时,可以使用散点图;

Plotly Express 是构建和定制包含趋势线和分面的图表的绝佳选择。

如果你发现了这篇感兴趣的文章,请阅读我之前的(https://medium.com/@dar.wtz):

分歧棒线,为什么&如何,用分歧讲故事

斜率图表,为什么和如何,用斜率讲故事

参考文献

【1】:https://en.wikipedia.org/wiki/Correlation_and_dependence

https://www.kaggle.com/sakshigoyal7/credit-card-customers

使用 Python 中的 matplotlib 创建测井数据散点图(交会图)

原文:https://towardsdatascience.com/scatterplot-creation-and-visualisation-with-matplotlib-in-python-7bca2a4fa7cf?source=collection_archive---------17-----------------------

使用散点图来显示变量之间的关系

用 python 中的 matplotlib 创建的中子密度散点图/交会图。图片由作者提供。

介绍

散点图是一种常用的数据可视化工具。它们允许我们识别和确定两个变量之间是否存在关系(相关性)以及这种关系的强度。

在岩石物理学散点图中,通常称为交会图。它们通常用作解释工作流程的一部分,可用于

  • 用于粘土或页岩体积计算的粘土和页岩终点识别
  • 离群点检测
  • 岩性识别
  • 碳氢化合物识别
  • 岩石分类
  • 回归分析
  • 更多

在这个简短的教程中,我们将看到如何显示一个 Volve 数据网络的直方图。

本教程的笔记本可以在这里找到。

本教程的相关视频可以在我的新 YouTube 频道上找到:

导入库和加载 LAS 数据

任何 python 项目或笔记本的第一步通常是导入所需的库。在这种情况下,我们将使用lasio加载我们的 las 文件,pandas存储我们的测井数据,以及matplotlib显示我们的数据。

import pandas as pd
import matplotlib.pyplot as plt
import lasio

我们在这个简短教程中使用的数据来自公开发布的 Equinor Volve 数据集。详情可在这里找到

为了读取数据,我们将使用 lasio 库,这是我们在之前的笔记本和视频中探索过的。

las = lasio.read("Data/15-9-19_SR_COMP.LAS")

下一步是将 las 文件转换成 pandas 数据帧。这可以通过调用 lasio 库中的.df()方法快速实现。

为了确认我们有正确的数据,我们可以调用.describe()方法,该方法将为我们提供关于其中包含的数据的信息。

df = las.df()
df.describe()

我们可以看到,这个文件中有七条测井曲线。

  • AC 代表声波压缩慢度
  • 井径仪校准
  • 容积密度的 DEN
  • 伽马射线的 GR
  • 中子孔隙度的 NEU
  • RDEP 深电阻
  • 中等电阻率 RMED

我们还可以通过调用df.head(10)来查看数据帧的前 10 行。这将返回数据帧的前 10 行。在我们的示例中,我们可以看到只有一列 GR 包含有值的值。所有其他的都包含 NaN 或者不是数字。这在测井数据集中很常见,尤其是在不需要某些测量的井的顶部。

df.head(10)

创建交会图/散点图

现在我们已经加载了数据,我们可以开始创建我们的第一个日志数据散点图/交会图。特别是,我们将使用密度和中子孔隙度测量。当执行岩石物理工作流程时,这两个测量值通常一起绘制。从这些数据中,我们可以识别出许多与记录的井段有关的不同信息,包括油气存在、岩性和不良数据等。

为了创建散点图,我们可以调用下面的代码。

# Set up the scatter plot
plt.scatter(x='NEU', y='DEN', data=df)plt.show()

简单的中子密度散点图,无标签和不正确的比例

从上面可以看出,我们现在有一个非常简单但信息量不大的散点图/交会图。首先,数据的值和显示方式与我们预期的不同。对于密度中子交会图,我们预计 y 轴上的体积密度(DEN)会反转,从 3.0 到 2.0 克/立方厘米,我们通常预计 x 轴上的中子孔隙度(NEU)不会超过 60%。

我们需要通过使用xlimylim在我们的图上反映这些比例范围。

此外,为了使我们的图易于阅读和查看,我们可以使用plt.rcParams设置散点图的默认图大小。

plt.rcParams['figure.figsize'] = (8, 8)# Set up the scatter plot
plt.scatter(x='NEU', y='DEN', data=df)# Change the X and Y ranges
plt.xlim(-5, 60)# For the y axis, we need to flip by passing in the scale values in reverse order
plt.ylim(3.0, 1.5)plt.show()

Matplotlib 散点图显示中子密度测井数据,刻度正确。

向轴添加标签

上面的散点图对其他人没有太大用处,因为轴上没有标签或单位。读者不会知道每个轴代表什么。因此,我们需要告诉读者的阴谋是什么阴谋反对什么。

我们可以使用plt.xlabelplt.ylabel来添加这些。

# Set up the scatter plot
plt.scatter(x='NEU', y='DEN', data=df)# Change the X and Y ranges
plt.xlim(-5, 60)# For the y axis, we need to flip by passing in the scale values in reverse order
plt.ylim(3.0, 1.5)# Add in labels for the axes
plt.ylabel('Bulk Density (DEN) - g/cc', fontsize=14)
plt.xlabel('Neutron Porosity (NEU) - %', fontsize=14)plt.show()

带轴标签的中子密度散点图。

太棒了。我们现在知道在我们的图上绘制了什么数据,以及它们是以什么单位绘制的。

向散点图添加颜色

我们可以通过使用颜色在散点图上添加第三个变量。这将使我们对数据有更多的了解。

对于该图,我们将添加c参数,并将数据帧中的伽马射线(GR)列传递给它。

为了控制显示的颜色范围,我们需要向vminvmax传递值。在本例中,我们将它们设置为 0 和 100。

# Set up the scatter plot
plt.scatter(x='NEU', y='DEN', data=df, c='GR', vmin=0, vmax=100)# Change the X and Y ranges
plt.xlim(-5, 60)# For the y axis, we need to flip by passing in the scale values in reverse order
plt.ylim(3.0, 1.5)# Add in labels for the axes
plt.ylabel('Bulk Density (DEN) - g/cc', fontsize=14)
plt.xlabel('Neutron Porosity (NEU) - %', fontsize=14)plt.show()

中子密度散点图/交会图,伽马射线作为颜色的第三个变量。

现在的情节是丰富多彩的,但我们不知道这些颜色意味着什么。紫色/蓝色代表第三个变量的高值还是低值?此外,情节的读者不会立即知道第三个变量是什么意思。为了解决这个问题,我们可以添加一个 colourbar。

更改色彩映射表和添加色条

有几种方法可以给我们的图添加彩条。因为我们只是使用了一个单独的数字plt.scatter,我们可以调用plt.colorbar(),然后传入我们想要在它旁边显示的标签。

要改变我们正在使用的彩色地图,我们可以使用plt.scatter()中的cmap参数将其设置为下面网页中的一个。对于这个例子,我们将使用彩虹色图。这将允许低伽马射线值以紫色/蓝色显示,高伽马射线值以红色显示。

https://matplotlib.org/stable/tutorials/colors/colormaps.html

# Set up the scatter plot
plt.scatter(x='NEU', y='DEN', data=df, c='GR', vmin=0, vmax=100, cmap='rainbow')# Change the X and Y ranges
plt.xlim(-5, 60)# For the y axis, we need to flip by passing in the scale values in reverse order
plt.ylim(3.0, 1.5)# Add in labels for the axes
plt.ylabel('Bulk Density (DEN) - g/cc', fontsize=14)
plt.xlabel('Neutron Porosity (NEU) - %', fontsize=14)# Make the colorbar show
plt.colorbar(label='Gamma Ray - API')plt.show()

带有 matplotlib 颜色条的中子密度散点图/交会图,显示伽马射线值的变化。

现在,我们有了一个更好看的图。我们给我们的坐标轴贴上标签,给我们的色带标上图和标签。

接下来,我们将看到如何通过使用样式表来进一步设计它的样式。

添加网格线和绘图样式

样式表允许我们控制情节的外观和感觉。您可以在 matplotlib 网站上找到完整的示例列表,网址为:

要设置样式表,我们可以使用plt.style.use('bmh')。“bmh”是一种特殊的风格,可以在上面的参考链接中找到。

#Set the style sheet to bmh
plt.style.use('bmh')# Set up the scatter plot
plt.scatter(x='NEU', y='DEN', data=df, c='GR', vmin=0, vmax=100, cmap='rainbow')# Change the X and Y ranges
plt.xlim(-5, 60)# For the y axis, we need to flip by passing in the scale values in reverse order
plt.ylim(3.0, 1.5)# Add in labels for the axes
plt.ylabel('Bulk Density (DEN) - g/cc', fontsize=14)
plt.xlabel('Neutron Porosity (NEU) - %', fontsize=14)plt.colorbar(label='Gamma Ray - API')plt.show()

中子孔隙度与体积密度的最终散点图/交会图。

更改数据

如果我们想查看我们图上的其他曲线,我们可以交换plt.scatter行中的变量。在这个例子中,我们已经将 NEU 数据转换为 AC(声波压缩慢度)。一旦我们做到了这一点,我们可以快速更新规模和标签。

#Set the style sheet to bmh
plt.style.use('bmh')# Set up the scatter plot
plt.scatter(x='AC', y='DEN', data=df, c='GR', vmin=0, vmax=100, cmap='rainbow')# Change the X and Y ranges
plt.xlim(40, 240)# For the y axis, we need to flip by passing in the scale values in reverse order
plt.ylim(3.0, 1.5)# Add in labels for the axes
plt.ylabel('Bulk Density (DEN) - g/cc', fontsize=14)
plt.xlabel('Acoustic Compressional (AC) - us/ft', fontsize=14)plt.colorbar(label='Gamma Ray - API')plt.show()

这为我们提供了一个图表,其格式与密度-中子孔隙度散点图相同。为我们的数据维护标准绘图格式的过程可以将报告的外观和感觉结合在一起。此外,由于我们重用代码,我们可以创建一个函数,它将接受一些参数,并通过消除重复来节省我们的时间。

摘要

在这个简短的教程中,我们介绍了如何显示测井数据的散点图/交会图,如何通过添加标签和添加颜色条来提供额外信息。这给了我们一个一致的、视觉上吸引人的情节,我们可以以演示或技术报告的形式呈现给其他人。

感谢阅读!

如果您觉得这篇文章很有用,请随时查看我的其他文章,这些文章从不同方面介绍了 Python 和测井数据。你也可以在 GitHub 找到我在这篇文章和其他文章中使用的代码。

如果你想联系我,你可以在LinkedIn或者我的 网站 找到我。

有兴趣了解更多关于 python 和测井数据或岩石物理学的知识吗?跟我上

基于 EAST 和 Tesseract 的场景文本检测与识别

原文:https://towardsdatascience.com/scene-text-detection-and-recognition-using-east-and-tesseract-6f07c249f5de?source=collection_archive---------7-----------------------

实践教程

使用 EAST 和 Tesseract 算法检测和识别给定自然场景图像的文本。

图片由 Paritosh Mahto 提供

**This Article Includes:
1.Introduction
2.Real World Problem2.1 Description2.2 Problem Statement2.3 Bussiness Objectives and Constraints
3.Datasets Available for Text Detection And Recognition3.1 Dataset Overview & Description
4.Exploratory Data Analysis(EDA)
5.Methods of text detection before deep learning era
6.EAST (Efficient Accurate Scene Text Detector)
7.Model Implementation
8.Model Analysis & Model Quantization
9.Deployment
10.Future Work
11.Reference**

1.介绍

在这个数字化时代,从不同来源提取文本信息的需求在很大程度上已经上升。幸运的是,计算机视觉的最新进展使我们能够在减轻文本检测和其他文档分析和理解的负担方面取得长足进步。在计算机视觉中,将图像或扫描文档中的文本转换为机器可读格式的方法被称为光学字符识别(OCR) ,这种格式可以在以后编辑、搜索并用于进一步处理。

OCR 的应用

答:信息检索和自动数据输入 - OCR 在许多公司和机构中扮演着非常重要的角色,这些公司和机构需要处理、分析和转换成千上万的文档来执行日常操作。

例如,在银行信息中,如账户详细信息,使用 OCR 可以很容易地从支票中提取金额。同样,在机场检查护照时,也可以使用 OCR 提取信息。其他例子是使用 OCR 从收据、发票、表格、报表、合同等中检索信息。

b .车辆牌照识别 - OCR 也可用于识别车辆牌照,然后用于车辆跟踪、收费等。

c .自动驾驶汽车——OCR 也可以用来为自动驾驶汽车建立模型。它可以帮助识别交通标志。如果没有这一点,自动驾驶汽车将对行人和道路上的其他车辆构成风险。

在本文中,我们将讨论和实现 OCR 中使用的深度学习算法。

图片由 Paritosh Mahto 提供

数字化-将文本、图片或声音转换成计算机可以处理的数字形式

2.现实世界的问题

2.1 描述

因为我们现在熟悉文本检测和识别的各种应用。本文将讨论从自然场景图像中检测和识别文本。

因此,在我们的例子中,我们使用任何自然图像或场景(不特别是文档、牌照或车号),对于给定的图像/场景,我们希望通过边界框定位图像中的字符/单词/句子。之后,我们要识别任何语言的本地化文本。一般工作流程图如下所示:

图片由 Paritosh Mahto 提供

上面使用的图像是为了显示整个任务。但是对于这个案例研究,我们将使用一个随机的自然场景作为输入图像。

2.2 问题陈述

对于给定的自然场景/图像,目标是通过绘制边界框来检测文本区域,之后,必须识别检测到的文本。

2.3 业务目标和限制。

  • 自然场景图像中的文本可以是不同的语言、颜色、字体、大小、方向和形状。我们必须处理自然场景图像中的这些文本,这些自然场景图像表现出较高的多样性和可变性。
  • 自然场景可能具有图案背景或形状与任何文本极其相似的物体,这在检测文本时会产生问题。
  • 图像混乱(低质量/分辨率/多方向)
  • 实时检测、识别和翻译图像中的文本需要低延迟。

3.可用于文本检测和识别的数据集

有许多公开可用的数据集可用于此任务,下面列出了不同数据集的发行年份、图像编号、文本方向、语言和重要特征。

图像来源——纸张(场景文字检测与识别)

由于非结构化的文本、不同的方向等,所有的数据集可能无法很好地用于所有的深度学习模型。对于这项任务,我选择 ICDAR 2015 数据,因为它很容易获得足够数量的非商业用途的图像,这些图像中的文本是英文的,因为我是初学者,我想专注于理解解决这项任务的算法的工作。此外,图像很小,在这个数据集中有多向模糊,因此我可以用检测部分做更多的实验。

3.1 数据集概述&描述

数据来源- 下载-附带场景文字-健壮阅读比赛(uab.es)

ICDAR-2015 由国际会议文档分析与识别提供

名为“稳健阅读竞赛”的竞赛是名为“偶发场景文本-2015”的挑战之一,该数据集就是为该挑战提供的。

描述:

  • 数据集在训练和测试集中可用,每个集合都有基本事实信息。它总共包含 1,500 幅图像,其中 1,000 幅用于训练,500 幅用于测试。它还包含 2,077 个裁剪文本实例,包括 200 多个不规则文本样本。
  • 这些图像是从可佩戴的相机中获得的。

4.探索性数据分析

  • 下载数据后,所有文件的结构如下-
**Data(main directory)|||----- ICDAR2015|||-----train (** containing all image files **)|||------train_gt (** containing texts and coordinates **)|||------test (** containing all image files **)|||-----test (** containing texts and coordinates **)**
  • 使用以下代码,可以观察到图像尺寸、通道数量等其他信息

对于训练图像

对于测试图像

  • 我们还可以从条形图中得出结论,所有图像的高度和宽度都相同,即 720 和 1280。

对于训练图像

图片由 Paritosh Mahto 提供

对于测试图像

图片由 Paritosh Mahto 提供

  • 在地面真实信息的帮助下绘制原始图像和具有边界框的图像
  • 对于训练图像

图片来源 ICDAR2015

图片来源 ICDAR2015

图片来源 ICDAR2015

  • 对于测试图像

图片来源 ICDAR2015

图片来源 ICDAR2015

图片来源 ICDAR2015

从 EDA 得出的结论

  • 在 ICDAR-15 数据集中,所有图像都具有相似的大小(720x1280)和扩展名(。jpg)。
  • 训练集有 1000 幅图像,而测试集中有 500 幅图像。
  • 所有图像的高度和宽度是相同的,所以我们不需要平均高度和平均宽度。
  • 在大多数图像中,所有文本都在小区域中,并且图像是模糊的。
  • 所有文本均为英语,少数文本也不可用,并*替换为“###”。
  • 大多数文本是单个单词,而不是单词和句子,而且单词是多向的。我们必须建立一个模型来预测这些模糊的文本。

5.深度学习时代之前的文本检测方法

正如问题陈述中提到的,我们必须首先定位图像中的文本,即首先检测文本,然后识别检测到的文本。现在对于检测,我们将尝试在深度学习时代之前用于文本检测的几种方法。

MSER(最大稳定极值区域)

SWT(笔画宽度变换)

两种方法的所有输出都不是很清楚,在第一种方法中,我们可以观察到图像中没有文本的区域仍然用方框标记。同样在第二种方法中,文本没有被正确地检测到。

还有很多其他深度学习算法用于文本检测和识别。在本文中,我们将讨论 EAST 检测器,并尝试借助一篇关于 EAST 算法的研究论文来实现它。为了识别,我们将尝试预先训练的模型宇宙魔方

6.EAST(高效精确的场景文本检测器)

这是一种快速准确的场景文本检测方法,包括两个阶段:

1.它使用完整的卷积网络(FCN)模型来直接生成基于像素的单词或文本行预测

2.生成文本预测(旋转矩形或四边形)后,输出被发送到非最大值抑制以产生最终结果。

管道如下所示:

图片由 Paritosh Mahto 提供

网络架构-(带 PVANet)

PVANet-它是用于对象检测的轻量级特征提取网络架构,在不损失准确性的情况下实现实时对象检测性能。

该模型分为三个部分:词干特征提取、特征合并分支和输出层。

图片由 Paritosh Mahto 提供

一、特征提取器(PVANet)

图片由 Paritosh Mahto 提供

该部分可以是任何卷积神经网络,其具有在 Imagenet 数据上预先训练的卷积层和汇集层交织,例如 PVANet、VGG16 和 RESNET50。从这个网络中,可以获得四级特征图 f1、f2、f3 和 f4。因为我们正在提取特征,所以它被称为特征提取器。

二。特征合并分支

图片由 Paritosh Mahto 提供

在这一部分中,从特征提取器获得的特征图首先被馈送到非 pooling 层以使其大小加倍,然后在每个合并状态中与当前特征图连接。接下来,使用 1×1 卷积,其中 conv 瓶颈减少了通道的数量,并且也减少了计算量,接着使用 3×3 卷积来融合信息,以产生每个合并级的最终输出,如图 2 所示。

g 和 h 的计算过程如下图所示

图片来源—研究论文 EAST

在哪里,

gi 是一个中间状态,是合并的基础

hi 是合并的特征地图

三世。输出层

来自合并状态的最终输出通过具有 1 个通道的 1X1 Conv 层,该层给出了范围从[0–1]的分数图。最终的输出也通过 RBOX 或 QUAD 几何体(关于这些的描述显示在下图中)给出了一个多通道几何体图。

图片来源—研究论文 EAST

关于分数图和几何图的细节将在实现时讨论。

7.履行

对于实现,我们将遵循上面显示的管道-

步骤 1-数据准备&数据生成(数据流水线)

图片由 Paritosh Mahto 提供

在这一步中,我们必须进行数据准备,还必须构建一个生成器函数,该函数将给出一个图像数组(模型的输入),其中包含分数图(输出)和地理图(输出),正如您在上图中看到的多通道 FCN 的输出以及训练掩码。

评分图:

它表示该位置的预测几何图的置信度得分/级别。它位于范围[0,1]内。我们通过一个例子来理解一下:

假设 0.80 是像素的得分图,这仅仅意味着对于该像素,我们有 80%的把握它将具有预测的几何图,或者我们可以说该像素有 80%的机会是预测的文本区域的一部分。

地理地图:

正如我们所知,随着得分图,我们还获得了一个多通道几何信息图作为输出。几何输出可以是 RBOX 或 QUAD。下表显示了通道数量以及 AABB、RBOX 和 QUAD 的功能。

图片来源—研究论文 EAST

信箱:

图片由 Paritosh Mahto 提供

从上图中我们可以观察到,对于 RBOX,几何体使用四通道轴对齐的边界框(AABB) R 和通道旋转角度θ。R 的公式为 g。四个通道表示 4 个距离,即从像素位置到矩形边界的距离,一个通道表示旋转角度,如下所示。

图像源-纸张(场景文本检测和识别)

四元:

图片由 Paritosh Mahto 提供

对于四边形,我们使用 8 个数字来表示从四个顶点到每个像素位置的坐标位移。每个偏移距离包含δXi |δyi 两个数字,几何输出包含 8 个通道。下面显示了一个示例

图片由 Paritosh Mahto 提供

在这个实现中,我们将只使用 RBOX。

对于生成器函数,我们必须遵循几个步骤

图片由 Paritosh Mahto 提供

图片由 Paritosh MAhto 提供

所有的密码都在这里-

https://jovian.ai/paritosh/data-preparation-and-model-implt

这里显示了从生成器函数输出的带有分数图、几何图和训练掩码的原始图像

第二步建模&损失函数

图片由 Paritosh Mahto 提供

在这一步中,我们将尝试使用 Imagenet 数据上预先训练的 VGG16 模型和 ResNet50 模型作为特征提取器来构建检测器架构。

模型-1(VGG16 作为特征提取器)

图片由 Paritosh Mahto 提供

源代码-

模型架构-

模型-2(ResNet50 作为特征提取器)

模型架构-

损失函数

当我们处理图像数据时,IOU 得分是经常使用的损失之一。但是这里我们主要有两个输出:分数图和几何图,所以我们必须计算两者的损失。

总损失表示为:

图片来源—研究论文 EAST

Ls 和 Lg 代表得分图和几何形状,λg 衡量两个权重的重要性。在我们的实验中,我们设λg 为 1。

为得分图损失

在该论文中,用于得分图的损失是二进制交叉熵损失,其对正负类别都有权重,如图 2 所示。

图片来源—研究论文 EAST

但是,当使用实施骰子损失时

几何图丢失

对于 RBOX,损失的定义是

图片来源—研究论文 EAST

第一个损耗是盒子损耗,对于这个损耗,IOU 损耗被使用,因为它对于不同比例的物体是不变的。

图片来源—研究论文 EAST

对于旋转角度,损耗由下式给出

图片来源—研究论文 EAST

实现的代码如下所示:

第三步模型训练

两个模型都用 Adam 优化器训练了 30 个时期,其他参数如下所示

型号 1

**model_vgg.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.001,amsgrad=True),loss= total_Loss())**

历元与损耗图:

图片由 Paritosh Mahto 提供

型号 2

**model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.001,amsgrad=True),loss=total_Loss())**

历元与损耗图:

图片由 Paritosh Mahto 提供

第四步干涉管线

在训练之后,首先将几何图转换回边界框。然后,我们应用基于得分图的阈值来去除一些低置信度框。使用非最大抑制合并剩余的盒子。

非最大抑制(NMS)是许多计算机视觉算法中使用的一种技术。这是一类从许多重叠的实体中选择一个实体(例如边界框)的算法。-来源关于非最大抑制的思考(NMS)| subra ta Goswami | Medium

这里我们将使用本地感知的 NMS。它将加权合并添加到标准 NMS 中。所谓加权合并,就是根据分数在输出框中合并高于某个阈值的 2 个 iou。下面讨论实施时遵循的步骤

  1. 首先,对几何图形进行排序,从最顶端开始。
  2. 2.拿这一行中的下一个盒子,找出与前一个盒子相同的借据
  3. 3.如果 IOU > threshold,通过按分数取加权平均值来合并 2 个框,否则保持原样。
  4. 4.重复步骤 2 到 3,直到所有的盒子都被迭代。
  5. 5.最后在剩下的盒子上使用标准 NMS。

实现的代码-

非最大抑制

检测模型的干扰管道

每个模型的输出:

模型-1

图片来源 ICDAR2015

图片来源 ICDAR2015

图片来源 ICDAR2015

模型-2

图片来源 ICDAR2015

图片来源 ICDAR2015

图片来源 ICDAR2015

如果我们比较两个模型的损失,那么我们可以得出结论,模型 2 (resnet_east)表现良好。让我们分析一下 model_2 的性能。

8.模型分析和模型量化

正如我们已经看到的,模型 2 比模型 1 执行得更好,这里我们将对模型 2 的输出进行一些分析。首先,计算训练和测试目录中每个图像的损失,然后基于分布,通过查看每个训练和测试图像的损失的方框图,我们将选择两个阈值损失,最后,我们将数据分为三类,即最好、一般和最差。

训练集和测试集的密度图-

图片由 Paritosh Mahto 提供

图片由 Paritosh Mahto 提供

将图像数据分为 3 类(最好、一般和最差)

  • 将数据分为 3 类
Best = if loss < np.percentile(final_train.loss, 33)----->Threshold-1
Average = if np.percentile(final_train.loss, 33)< loss < np.percentile(final_train.loss, 75)
Worst = if loss > np.percentile(final_train.loss, 75)--Threshold-2

每个类别的图片数量如下所示:

对于训练图像:

图片由 Paritosh Mahto 提供

对于测试图像:

图片由 Paritosh Mahto 提供

  • 从第一个图 1 中,对于列车数据,我们可以观察到 33%的数据属于最佳类别,42%的数据属于一般类别,只有 25%的数据属于最差类别。
  • 从第二个图 I 中,对于测试数据,我们可以观察到 2.6%的数据属于最佳类别,13.2%的数据属于一般类别,84.2%的数据属于最差类别。
  • 从这些观察中,我们还可以得出结论,我们的模型对于测试图像(即新图像)可能表现不佳。

模型量化

深度学习的量化是通过低位宽数的神经网络来逼近使用浮点数的神经网络的过程。这极大地降低了使用神经网络的内存需求和计算成本。量化后,原始模型和量化模型的大小如下所示-

9.部署

模型量化后,使用 streamlit 和 Github 选择并部署了 float16 量化模型。使用 streamlit uploader 函数,我创建了一个. jpg 文件输入部分,您可以在其中给出原始图像数据,模型将给出图像,并在图像上显示检测到的文本。

网页链接-https://share . streamlit . io/paritoshmahto 07/scene-text-detection-and-recognition-/main/app _ 2 . py

部署视频-

10.未来的工作

在这项任务中,我们的主要目标是了解任何检测模型的工作原理,并从头开始实现它。为了提高模型的性能,我们可以用大型数据集来训练我们的模型。我们还可以使用另一种识别模型来更好地识别文本。

我的 LinkedIn 个人资料

我的 Github 个人资料

https://github.com/paritoshMahto07

11.参考

一.研究论文

二。博客

https://www.programmersought.com/article/56252830437/ https://theailearner.com/2019/10/19/efficient-and-accurate-scene-text-detector-east/

三。Github 代码源代码

https://github.com/kurapan/EAST https://github.com/solaris33/EAST-tf2

四。模型量化

https://www.tensorflow.org/lite/performance/post_training_quantization

动词 (verb 的缩写)师徒制

https://www.appliedaicourse.com/

感谢阅读,祝你有美好的一天!🙂

使用 Python 中的线性规划进行日程优化

原文:https://towardsdatascience.com/schedule-optimisation-using-linear-programming-in-python-9b3e1bc241e1?source=collection_archive---------4-----------------------

医院手术室排班的优化模型

对于许多组织来说,日程安排是一项日常挑战。从生产线上的工作分配到医院手术病例的时间安排,如何有效管理有限资源的问题无时无刻不在出现。

虽然“信封背面”的规划可以带我们走这么远,但通常情况下,更高级的说明性分析工具(如线性规划)可以帮助决策者快速确定最佳选择。

这项技术并不新鲜——数学编程已经存在几十年了——但是最近几年它变得更容易使用了。线性求解器算法性能的改进使得更复杂的问题能够在合理的时间框架内得到解决;Pyomo 等开源库使得用熟悉的编码语言构建模型成为可能,数字化增加了高质量数据的可用性。

然而,尽管有这些进步,传统的优化方法经常被数据科学家和分析师忽视。

在这篇文章中,我希望展示线性编程的价值,并展示如何开始用 Python 构建模型。为此,我们将构建一个基本模型来优化医院的手术室安排。

我假设你熟悉 Python 和线性优化概念的基础知识。

源代码和数据可以在这里找到:https://github.com/Lewisw3/theatre-scheduling

医院为什么要排班?

排班对于手术室的顺利运转至关重要。有效的时间安排对所有各方都有利:医务人员、医疗保健提供者,尤其是病人。但是手术计划仍然是医院面临的主要挑战。NHS Improvement 委托进行的一项最新研究报告称,随着手术清单和假期预订管理的改善,每年可以完成额外的 290,000 例手术[1]。

那是 Covid 之前。自那以后,疫情在择期护理方面产生了大量的积压,因此有效地管理手术室的时间表比平时更加重要。

这是一个复杂的挑战,解决方案不仅仅取决于分析。

在这篇文章中,我们将考虑一个简单的外科医生择期手术计划的例子。我们的目标是强调分析支持剧院规划的潜力——该模型是而不是旨在成为现实世界的决策支持工具!

有大量关于剧院时间表优化的文献。如果你感兴趣,我推荐 Guerriero 和 Guido [2]以及 Cardoen 等人[3]的综述论文。

皮隆·纪尧姆在 Unsplash 上拍摄的照片

问题陈述

假设一家医院的手术室正在为一名眼科顾问医生计划选择性手术。医院有两份名单:

  • 在截止日期前分配给顾问的操作清单(案例)
  • 分配给顾问的手术室时间段(手术室时段)列表

剧院会议可以是半天(上午 8.30-下午 1 点)或全天(上午 8.30-下午 6 点)会议,顾问通常每周分配一次剧院会议。任务是将顾问即将进行的案例分配到他们的剧院会议中,这样我们可以最大限度地利用每个会议

利用率被定义为手术室时间段被手术病例填满的百分比。

病例必须在目标截止日期前完成,并且至少 15%的剧场时段应空出来用于其他活动(如绝育、员工休息等)。)

公式化——从商业问题到数学模型

我们把这个问题公式化为一个灵活的车间调度问题,其中一个外科病例类似于一个工作,一个手术室类似于一台机器。我们首先定义决策变量、线性约束和线性目标函数。

1。导入数据

在我们开始之前,让我们看看数据。我们有两个数据源: cases.csvsessions.csv

cases.csv 包含所有即将进行的选择性手术列表:

案例数据(仅前 4 行)

sessions.csv 包含所有即将举行的剧院会议的列表 :

会话数据(仅前 4 行)

Github repo 上提供了完整的 CSV 文件。

我们首先使用集合(类似于数组)和参数(键值对)将相关数据导入 Pyomo ConcreteModel 对象。

病例和会话数据作为集合和参数从 CSV 导入到模型中。

我们使用许多辅助函数来初始化 Pyomo 集合和参数。为了简洁起见,这里省略了这些,但是可以在 Github repo 上找到。

我们还定义了一个名为 TASKS 的集合,它包含(CaseID,SessionID)的所有可能组合的列表。这代表了所有可用的潜在案例分配决策:

我们生成案例和会话的所有可能组合,并将它们存储在任务集中。

2.决策变量

主要决定是将案例分配给会话。这需要对上述任务中的每个病例-会话组合做出二元是/否决定。

我们还想计算每个案例的开始时间和每个会话的利用率。总而言之,我们有 3 个决策变量:

我们在 Pyomo 模型中定义这些决策变量如下:

我们使用 Var 函数定义了 3 个决策变量

3.目标函数

线性规划的一个优点是可以灵活地定义一个代表我们业务需求的目标函数。我们可以自由定义任何(线性)函数,在我们的例子中,我们的目标是最大化所有会话的利用率:

我们的 Pyomo 模型的单一线性目标函数最大化。

4.限制

接下来,我们添加约束。约束捕获了所有的规则(在这个例子中不太现实!)确保由模型返回的解决方案构成可行的剧院时间表。这里我们有 6 条规则:

  1. 案例的开始时间必须在它被分配到的会话的开始时间之后
  2. 案例必须在其分配的会话结束之前结束
  3. 一个案例最多可以分配给一个会话
  4. 案例不能在其截止日期后分配给会话
  5. 两个案例不能重叠:一个案例的开始时间必须在另一个案例的结束时间之后
  6. 一个时段的利用率等于外科病例所占用的时段的一部分

我们还限制了决策变量的范围。利用率必须介于 0 和 85%之间(因为 15%的会话必须保持空闲以用于其他活动),并且案例的开始时间必须介于 0 和一天的分钟数(1440)之间。

4.1 向模型添加约束

通过为每个约束编写单独的 Python 函数并使用 Pyomo 的约束方法,将上述约束添加到模型中:

模型约束-使用 Pyomo 的 GDP 扩展添加非线性析取。

为了将这些约束定义为线性方程,我们利用了两个值得注意的有用技术:大 M 公式和逻辑析取。

大 M 公式

大 M 方法是一种打开和关闭约束的技巧。例如,上面的约束 1 规定案例必须在会话开始时间之后开始。这只对案例分配到的会话有效。它不应该适用于所有其他的会议。我们确保这种不平等会发生:

case_start_time >= session_start_time - (1 - session_assigned)*M

其中 M 是一个足够大的常数,而 session_assigned 是一个二元变量。盯着这个足够长的时间后,它开始变得有意义。如果 session_assigned 等于 1,则必须保持该规则。然而,如果 session_assigned 为 0,则(假设 M 足够大)RHS 上的第二项变得比第一项大得多,因此整个 RHS 总是负的。因为我们的可变边界强制 case_start_time ≥ 0 ,所以实际上没有额外的限制。

析取编程

第 5 个约束是一个逻辑析取:由 OR 关系链接的一组约束。析取关系表明,对于同一会话中的任何一对案例,要么案例 1 发生在案例 2 之前,要么案例 2 发生在案例 1 之前。

这种关系是非线性的,因此我们必须将其转换为线性约束,以便使用标准的线性解算器。为此,我们使用了 Pyomo 的广义析取编程(GDP)建模扩展(参见上面代码片段中的第 27 行)。它允许我们将析取定义为一个简单的 Python 函数,要将“析取”模型转换回标准的混合内部编程(MIP)模型,我们需要做的就是包含以下代码行:

pe.TransformationFactory("gdp.bigm").apply_to(model)

GDP 扩展可以通过增加大 M 约束和引入新的二进制变量来避免,但我们将保持析取,因为它是可行的和可读的。

最后,我们完成了!完整的线性规划模型是:

择期手术计划的柔性作业车间调度模型。

求解模型—生成剧院时间表

使用 Pyomo 这样的接口的一个优点是很容易尝试不同的线性解算器,而不用用另一种编码语言重写模型。

这里我们使用硬币或分支切割(CBC)——一个开源的混合整数程序求解器(https://github.com/coin-or/Cbc)。

下面的代码片段使用 Pyomo 的 SolverFactory 类求解模型。该类可以采用许多调整参数来控制所选求解器的操作,但为了简单起见,我们保留默认设置,除了 60 秒的时间限制。

使用 CBC 求解器求解 Pyomo MIP 模型

结果

下面是一个甘特图,显示了模型返回的解决方案。输入的案例和会话数据在处可用 :

模型结果:手术病例(编号 1-30)被分配到四个手术室。每个剧场至少有 15%的时间用于其他活动。

上面的图表显示了一个可行的案例和会议时间表,在我们的限制下最大限度地利用所有会议。提供给模型的输入数据的需求(病例时间)多于容量(会话时间),并且模型发现放弃病例#2(持续时间为 70 分钟的玻璃体切除术)是最大化所有会话的总利用率的最佳选择。

结论

线性规划是帮助组织快速做出明智决策的强大工具。对于数据科学家来说,这是一项有用的技能,有了 Pyomo 这样的开源库,用 Python 来构建模型就很容易了。

在这篇文章中,我们创建了一个简单的优化模型来有效地安排手术。尽管不是真实世界的解决方案,但它展示了线性规划等优化方法如何支持规划者最大限度地利用他们的可用资源。

该模型的潜在后续步骤包括:

  • 调整求解器参数以提高性能并缩短求解时间
  • 重构模型以简化问题并减少求解时间
  • 调整目标函数以更好地表示性能目标
  • 纳入额外的约束,以更好地代表现实世界的选择性剧院调度
  • 使用更复杂的方法来预测案例时间

关于最后一点,线性规划等规范性分析技术正越来越多地与机器学习等预测性方法相结合。对于像这里介绍的调度应用程序,机器学习可以用来预测任务的持续时间,甚至哪些任务将会发生。一旦预测到需求,优化方法可以帮助规划。

感谢阅读!如果您有任何问题或意见,请随时联系我们。

参考

[1] NHS 改进(2019)。手术室:减少等候名单的机会。可在:https://improvement . NHS . uk/resources/operating-theaters-opportunities-reduce-waiting-lists/获取

[2]f . Guerriero,r . Guido,手术室管理中的运筹学:调查。卫生保健管理科学 14,89–114(2011)。https://doi.org/10.1007/s10729-010-9143-6

[3] Cardoen 等.手术室规划和调度:文献综述。欧洲运筹学杂志201(3);921–932(2010)。https://doi.org/10.1016/j.ejor.2009.04.011

使用 Python 优化仓库增值服务

原文:https://towardsdatascience.com/scheduling-of-luxury-goods-final-assembly-lines-with-python-e5c4b0590bfc?source=collection_archive---------11-----------------------

使用线性规划来提高您的豪华产品最终组装的生产能力

某奢侈品牌配送中心的 4 条增值服务生产线车间示例—(作者提供的 CAD 模型)

处理奢侈品、服装或高价值商品的配送中心(DC) 所面临的大部分挑战是在入境过程中。

我们以 DC 为例,该商场存放进口奢侈品包包、服装和鞋子,需要:

  • 机器 1——防盗标签:放一个自动报警标签,保护你的商品在店内不被盗

防盗标签示例—(图片由作者提供)

  • 机器 2 —贴标签:用当地语言打印标签,并进行标签缝制

标签示例—(图片由作者提供)

  • 机器 3 —打包&重新包装:将您的货物转移到销售包装中,并添加附赠(GWP) 、个人备注或真品证书

带购物凭证的礼品包装示例—(作者 CAD 模型)

完成这三个步骤后,你的商品就可以放在库存区等待提货运往最终目的地(商店)。

如果你每天处理的项目数量太低,这个过程很快就会成为一个主要的瓶颈。

💌新文章直接免费放入你的收件箱:时事通讯

如果你喜欢看,看看这篇文章的视频版本

一.问题陈述

1.方案

你是一家专注于时尚、香水和手表的标志性奢侈品公司的配送中心(DC)的入境经理。

您已经收到 600 套成衣,包括:

  • 1 件需要缝制标签和重新包装的女装
  • 需要缝标签、防盗标签和重新包装的手提包
  • 1 条需要防盗标签、缝制标签和重新包装的皮带

因为它们是一起出售的,所以这些 SKU 需要同时准备好并一起包装,以便在完成以下步骤后尽快运送到商店:

  • 入境团队正在从卡车上卸下托盘,并把它们放在集结地

步骤 1:将托盘卸载并转移到中转区—(作者的 CAD 模型)

  • 机器 1:防盗标签— 一名操作员在每个袋子和皮带上贴上防盗标签

步骤 2: 2 个工作站,操作员在每个手提包和皮带上贴上防盗标签—(作者提供的 CAD 模型)

  • 机器 2——贴标签:在专用区域打印后,标签被缝在皮带、手袋和服装上

步骤 3: 4 个工作站,操作员在其中执行标签缝纫—(作者的 CAD 模型)

  • 3 号机——配套&重新包装:每件物品,你都需要加上真品证书,塑料保护,并进行精细包装

第 4 步:4 个工作站,操作员在其中执行重新包装—(作者的 CAD 模型)

在重新包装过程之后,货物被转移到最终中转区等待装运(X-对接模式)。

目标:达到每小时组装台数的最大生产率(台/小时)

2.问题陈述:车间作业问题

作业车间调度问题(JSSP) 是一个 NP 难问题,由一组作业定义,每个作业必须由一组机器按照特定的顺序执行。

我们的例子是使用 3 台机器的 3 个作业—(图片由作者提供)

对于我们的每个作业,我们在上表中定义了机器的执行时间(min)和处理顺序。

例如,作业 2(手袋)从使用机器 1【6 分钟】放置防盗标签开始,然后使用机器 2【4 分钟】缝制标签,最后使用机器 3【3 分钟】完成打包包装。

机器一次只能执行一项工作,一旦启动,机器就不能被中断,直到完成指定的工作。

目标:最小化最大完工时间,即完成所有工作的总时间

一、简单的解决方案:一次一个作业周期

第一种天真的方法—(图片由作者提供)

结果

  • 完成时间: 30 分钟
  • 产能: 2 台/小时

评论

就生产率而言,这种简单的方法是最糟糕的。由于作业是按顺序处理的,机器经常处于闲置状态。

问题:如果我们并行执行作业,会有什么结果?

二。最优解:使用 Google 或-Tools 的作业车间调度问题

或者——Google AI 解决方案的工具部分——(来源:Google AI Logo,链接)

OR-Tools 是 Google 的一个开源集合,提供了用于组合优化的工具。目标是从大量可能的解决方案中找到最佳解决方案。

我很喜欢这个库,我已经在几个例子中使用了它:

  • Samir Saci ,利用 Google AI 设计寻路算法提高仓库生产力,链接
  • Samir Saci ,使用 Python 线性编程优化劳动力规划,链接

让我们尝试使用这个库来找到最佳排序,以减少这个特定流程集的最大完工时间

http://samirsaci.com

二。优化您的日程安排

1.结果:优化解决方案与原始解决方案

第一种天真的方法—(图片由作者提供)

使用 Google 或工具优化解决方案—(图片由作者提供)

你可以看到上面的两幅图分别代表了初始解决方案(简单的解决方案:一次一个任务)和优化的解决方案(并行任务)。

结果

  • 总完工时间: 16 分钟 (-47%)
  • 产能: 3.75 台/小时 (+85%)
  • 每周期空闲时间: 18 分钟 (-71.4%)

结果令人满意

我现在将解释如何达到这些结果。

2.构建优化模型

a .初始化你的模型

3 machines
Total Time using Naive Solution: 30 min

b .初始化变量并创建序列

c .添加约束并设置求解器

d .求解器最优解

**Output -**
Optimal Schedule Length: 16
Machine 1: job_2_1   job_3_2   [0,6]     [6,10]    
Machine 2: job_3_1   job_1_1   job_2_2   [0,3]     [3,7]     [7,11]    
Machine 3: job_1_2   job_3_3   job_2_3   [7,10]    [10,13]   [13,16]

基于此输出,我们可以绘制更新的时间表:

使用 Google 或工具优化解决方案—(图片由作者提供)

三。结论和后续步骤

关注我的 medium,了解更多与供应链数据科学相关的见解。

1.结论

通过实施智能调度解决方案,最大限度地利用我们的资源(机器),我们的生产率提高了 48%。

该解决方案基于一个简单的场景,使用一条装配线(每种类型一台机器)。

问题:用几行代码会得到什么结果?

我让你测试它,并在评论区分享你的结果(或问题)。

2.后续步骤

我们能通过改变条件来提高生产率吗?

具有空闲序列的优化解决方案(黄色)——(图片由作者提供)

在上图中,我突出显示了我们在空闲时间可以增加的潜在额外工作:

  • 机器 1: 1 顺序4 分钟,等于工作 3 的时间
  • 机器 2: 1 顺序4 分钟,等于任务 1 和任务 2 的时间
  • 机器 3: 2 顺序为 4 分钟,等于工作 1、2 和 3 的时间

问题:如果我们在周期 n 的这些空闲序列中启动周期 n+1 的作业,平均生产率会是多少?

超出

如果我们有标签缝纫工作站,对整体生产力会有什么影响?

关于我

让我们在 Linkedin 和 Twitter 上连线,我是一名供应链工程师,正在使用数据分析来改善物流运营和降低成本。

如果你对数据分析和供应链感兴趣,可以看看我的网站

https://samirsaci.com

参考

[1] Google AI,Google OR-Tools 库,链接

谢林的种族隔离模式

原文:https://towardsdatascience.com/schellings-model-of-racial-segregation-4852fad06c13?source=collection_archive---------20-----------------------

Python 中的实现和分析以及量化的出现

种族隔离已经在不同的社会中存在了几个世纪,并且经常被法律强制执行。例如,19 世纪美国的 T2 吉姆克劳法(T3)就是这种情况。当美国最高法院在 1954 年宣布它们违宪时,许多人预计种族隔离将会消失。然而,事实并非如此:相反,尽管付出了大量努力和投资,迄今为止,隔离仍然是美国和其他地方的一个主要问题[1]。

图 1: 根据 2010 年的人口普查,芝加哥的种族居住隔离情况[2]。查看弗吉尼亚大学魏尔东·库珀公共服务中心的美国种族隔离地图。

即使没有实施隔离的法律,这也可能是由许多因素造成的,包括住房和贷款歧视、偏见等。然而,当时没有考虑到的另一个因素是涌现,即复杂系统组成部分的微观相互作用产生的宏观特征的存在。1971 年,托马斯·谢林设计了一个最早的基于主体的模型[3],表明即使当个体不介意被不同种族包围时,只要他们仍然希望至少有一小部分人与邻居具有相同的种族背景,种族隔离也可能发生。尽管代理人愿意接受一个更加多样化的邻居,隔离仍然出现在不同个体的社会互动中。引人注目的是,即使是微弱的地方偏好也能导致从个体微观互动网络中出现重大的全球现象。此外,这些涌现的宏观规则反馈到个体代理人,约束他们的选择和行为。

正如我们将看到的,尽管简单,谢林的模型产生了迷人而复杂的动力学,有着多重且远离直觉的平衡。

一个简单的隔离模型

谢林隔离模型的一个最简单的版本包括一个住宅区的风格化表示,在那里,个人根据其紧邻的特征反复做出搬迁决定。该模型考虑了两种类型的代理,比如说红色 ,它们可能代表不同的种族、民族、经济地位等。,以及住宅位置的 N × N 网格,在其上最初随机分配多个 Nₐ ≤ N 代理。代理商的数量以蓝色到红色类型的比例 B/R 为特征,因此 Nₐ = B + R ,并假设为常数,因此在任何给定的时间都将有nᵥ= nnₐ的空置住宅位置。然后,每个代理 i 由同类型邻居的分数给出的满意度参数sᵢ来表征。注意,邻居的数量可能取决于考虑的是固定的还是周期性的边界条件,以及在计算 Sᵢ 时没有计算在内的空闲位置。

图 2: 两个智能体的邻域 i j 为固定或周期性边界条件。【来源:作者图片。】

力学

动态由全局容差参数* τ 设置,该参数表示每个代理在决定重新定位之前愿意接受的不同类型邻居的最大比例。因此,每个具有sᵢ<1-τ的代理将随机重新定位到不同的空闲居住位置。这种动态一直持续到找到一个稳定的平衡(如果存在的话),所有的代理人都满意为止。因此,个人的决定完全是基于个人考虑,基于他们目前居住的当地社区的特点。然而,重新定位的行为,尽管是基于局部参数,但最终具有全局效应,因为一个代理的随机重新定位可能会使先前满意的其他代理不满意。
模型的替代版本可能会考虑不同的行为,不满意的代理将移动到最近的空闲位置,而不是随机位置,或者移动到最佳可用位置,或者移动到满足阈值1-τ的最近位置。然而,另一个行为规则将允许个人只有在有更好的位置时才搬家。请注意,不同的规则可能会对全球动态产生重要影响。考虑一个例子,其中所有不满意的个人随机移动(这是我们将在下文中使用)。显然,对于足够低的容差 τ 和/或足够低的空置房产比率,不存在静态平衡,因为总会有至少一个人对其位置不满意,并且无法找到另一个合适的位置。另一方面,当考虑到只有在找到更好的地点时,人们才会搬迁,静态均衡总是存在的。*

模拟

在下文中,我们在具有 N=60N × N 网格上采用了谢林分离模型的 Python 实现,假设在任何给定时间 10%的所有属性是空闲的,红色与蓝色代理的比例为1--1。让我们从定义系统参数开始:

虽然面向对象的编程通常是基于代理的建模中最受欢迎的编程范例,但在寻找数值有效的例程时,这可能不是最合适的方法。因此,为了在模拟中保持高效率,我们将系统建模为大小为(N, N)numpy.array,将蓝色代理占据的住宅位置编码为0,将红色代理占据的住宅位置编码为1,将空置的房产编码为-1
现在需要两个主要函数:一个rand_init函数用于系统矩阵M的随机初始化,一个evolve函数用于对系统施加上述动态特性。前者很容易实现为

下面的图 3 给出了随机初始化系统的例子,具有上面指定的参数。

图 3: 随机初始化,集成度高。【来源:作者图片。】

相反,进化函数是大部分计算工作的所在,更具体地说,是满意度参数 Sᵢ 的计算。对于快速实现,我们可以依赖矩阵M与内核的卷积

核上的卷积运算

被定义为

因此,上述卷积的每个元素都包含所有相邻值的总和。考虑到这一点,我们现在可以编写一个函数来处理系统的动态演化:

请注意,scipy.signal.convolve2dboundary参数提供了一种从固定('fill')切换到周期性('wrap')边界条件的简单方法。下面我们将坚持后者。

最后,我们能够运行模拟:图 4 示出了从具有高度集成的随机初始化状态到隔离配置的演变,其中每个区域被一层空位分隔开。请注意,由于周期性的边界条件,红色几乎形成一个单独的斑点,除了计算网格中央左侧的一个小岛,蓝色代理也是如此。

图 4: 系统从初始随机状态演化为1τ= 0.6r = bnᵥ/n= 0.1**60×60【来源:作者图片。】

不出所料,高度的不容忍导致了完全隔离的配置。然而,令人意想不到的是,即使看似不容忍的程度很低,隔离的程度也会自然出现。即使个人愿意接受高达 60%的不同邻居(1-τ= 0.4),仍然会观察到隔离的出现,如下图 5 所示。换句话说,这是谢林的主要观点之一,聚合隔离甚至可以从个人的欲望中出现,不要觉得自己是极端少数。不同代理人的决策之间的相互作用,以及对其住宅区构成的如此薄弱的要求,足以导致隔离的出现。驱动涌现的自我强化机制是在一个邻居中的个体在一个个体重新安置的条件下重新安置的可能性增加中发现的。当一个少数民族离开一个社区时,这种类型的代理人在该社区变得更加稀少,因此也为其他人的迁移提供了动力。

图 5: 增加不容忍的系统的一些平衡。【来源:作者图片。】

隔离的程度可以通过个体群体的平均满意度 ⟨S⟩ 来衡量。如上所述,这增加到临界阈值,之后系统变得不稳定,并且不能找到静态平衡(见图 6)。此外,直到临界阈值,即当静态平衡存在时,人们系统地观察 ⟨S⟩≥1−τ 。值得注意的是,平均满意度 ⟨S⟩ 通过在偏好1-τ的特定值处发生的离散转换来量化。

图 6: 表示满意⟨s⟩表示增加不容忍1-τ。这些值在200**蒙特卡洛模拟中进一步平均,阴影区域表示平均值周围的一个标准偏差。【来源:作者图片。】**

⟨S⟩ 的量化可以追溯到每个个体的邻域的离散性(图 7)。忽略空置位置,每个人最多可以有 8 个邻居,这些邻居可能是也可能不是同一类型。因此,对于 n ∈ ℕ : n < 8 ,可以预期 ⟨S⟩ 中的量化跳变会发生在任意 n / 8 处。

图 7:⟨s⟩为变化分数的空位 Nᵥ / N 相对于不耐性1-τ。这里R/B = 1【⟨s⟩】的量化很明显,随着空置物业比例的增加,出现了更精细的结构。【来源:作者图片。】**

自然地,随着空置位置比例的增加,随着预期邻居数量的减少,出现了更好的量化结构(回想一下,空置位置不计入 Sᵢ 的计算)。因此,更一般地,人们可以期望在 ⟨S⟩n/m 处找到 n,m∈ℕ:m≤8,n < m 的量化跳跃。这些部分在图 7 中用红色垂直线标出;较高的线对应于较高的 m 值。

参考

[1] “种族隔离仍然是个问题”,迈克尔·卡西迪,世纪基金会,2013 年 7 月

[2] “种族点阵图”,人口统计研究小组,弗吉尼亚大学魏尔东·库珀公共服务中心,2017

[3] “隔离的动态模型”,托马斯 C·谢林,《数理社会学杂志》,1971 年,第 1 卷,第 143–186 页

[4]本帖也可以在作者个人网站找到。

学校教育:按数字

原文:https://towardsdatascience.com/schooling-by-the-numbers-4e0131c3bffd?source=collection_archive---------6-----------------------

Gentrit Sylejmani 在 Unsplash 上拍摄的照片

约瑟夫的学业最近在东京奥运会上的表现引发了巨大的反响。在 2016 年里约奥运会上赢得 100 米蝶泳冠军后,人们似乎希望学校教育能够复制他在东京的成功。有许多讽刺的评论(大部分已被删除)和失望的表达。然而,看到许多支持的信息令人振奋。支持的网民通过理解保持高水平的表现是多么困难来关闭有毒的。但是“专家”评论员也加入进来,对学校教育作为世界级运动员的地位提出了质疑。最初对学校教育的期望公平吗?说学校教育不如其他世界级运动员稳定,公平吗?

这篇文章使用奥运会成绩和国际游泳联合会(FINA)运动员排名和计时数据来表明:

  1. 对学校教育在背靠背的奥运会上赢得奖牌的期望是不现实的
  2. 学校教育确实是世界一流的

免责声明:我不是运动员,我不知道参加奥运会是什么感觉。但是我确实知道一些关于如何处理数据和如何绘制图表的知识。事实证明,要想更清楚地了解学校教育的成就,你只需要知道这些就够了——这一点还没有出现在任何评论中。

连续几届奥运会登上领奖台

虽然我们可以很容易地找到关于连续两届奥运会上获得奖牌的运动员的报道,但我们没有看到大量运动员没有获得奖牌。因此,很难对我们自己的运动员的表现形成现实的期望,我们最终用我们的心,而不是我们的头脑来形成这些期望。为了让我们的预期建立在通常情况下,我们使用这个奥运会奖牌获得者的数据集来调查运动员在背靠背的奥运会上赢得奖牌的普遍程度。在下文中,我们将这些称为重复获胜,将在随后的奥运会上在同一项目中赢得奖牌的奖牌获得者的百分比称为重复获胜率

这些数据包含了从 1896 年到 2016 年运动员在奥运会上取得的成绩的基本信息。出于本帖的目的,我们将我们的分析限于 1956 年至 2016 年(数据集中的最新年份)的男性运动员在奥运会个人项目中的成绩。之所以选择 1956 年作为起始年,是因为这是有记录以来第一年举办蝴蝶赛事。

体育领域的多次胜利

首先,我们看看各种奥林匹克运动。第一个图显示了(1)获得重复胜利的奖牌获得者(绿色)和(2)没有获得重复胜利的奖牌获得者(紫色)的数量。第二个图显示了不同运动中的重复胜率。

我们在这里做了两点观察:

  1. 卫冕冠军似乎胜算不大。
  2. 不同的运动似乎有不同的重复胜率。

图片作者。

图片作者。

跨年龄的重复胜利

第二,我们跨越年龄来看。这两个图报告了与上面相同的统计数据。很明显:

  1. 根据第一幅图中的数据判断,22 岁到 25 岁似乎是运动员的黄金时期
  2. 男性运动员越年轻(至少在 17 岁之前),重复获胜率越高。

图片作者。

图片作者。

旁注: 我运行了几个逻辑回归模型来评估运动和年龄对重复获胜概率的统计意义。模型拟合非常差,置信区间非常宽。使用这些模型进行推理没有多大价值。因此,我在这篇文章中省略了它们。

游泳中的多次胜利

现在我们知道运动员的运动和年龄可能会影响重复获胜的机会,我们进一步分割数据,放大游泳。我们采用与上述相同的方法,绘制计数和比率图,以了解游泳项目和年龄之间的差异。

下面,我们看到不同事件的重复成功率有所不同。一方面,我们有 200 米蛙泳,获胜率相对较低,为 1/10;另一方面,我们有 100 米自由泳,获胜率相对较高,为 1/5。100 米蝶泳位于中间的某个位置 15%

图片作者。

下图反映了上一小节的发现:不同年龄段的重复成功率不同。此外,查看总计数(堆叠的紫色和绿色柱的全长),我们注意到游泳运动员达到最佳表现的年龄似乎是 21 岁。这适用于 100 米蝶泳,我们将在下一节重点介绍。

图片作者。

在 100 米蝶泳比赛中屡次获胜

接下来,我们再次放大,这次是 100 米蝶泳。我们发现,历史上没有一名男子游泳运动员在 21 岁时就在 100 米蝶泳比赛中获得奖牌,也没有一名男子游泳运动员在接下来的奥运会上获得奖牌。事实上,在所有年龄组中,只有六个人做到了这一点,而且只有四个名字:斯皮兹、内斯特、菲尔普斯和勒克洛斯。

图片作者。

我们已经尽可能地缩小了数据范围,但仍然只能对 100 米蝶泳重复获胜的概率有一个大致的估计:(a)如果你把所有年龄组放在一起,大约 15%;(b)如果你不这样做,0%。这些数字不太可靠。这第一部分的关键是重复获胜并非不可能,但实现它们的几率对运动员不利。虽然数据可能无法给我们一个精确的估计,但它足以告诉我们,重复的胜利是例外而不是常态。

我们可以通过更频繁地查看更多的数据,在现实中建立我们的期望。四年时间足以培养新的世界冠军,因此我们应该看看运动员在两届奥运会之间的表现。用我们运动员在往届奥运会上的表现来形成对他们在本届奥运会上的预期,没有太大意义。

世界级的一致性

本节旨在解决学校教育是否是世界一流的问题。评论员将学校教育比作安东尼·nesty⁴,他在一届奥运会上赢得了金牌,并在下一届奥运会上又赢得了一枚金牌。他们暗示,学校教育的旅程出了问题,学校教育没有走上“人们对世界级运动员的期望轨迹”。⁵

如果评论员将轨迹称为重复获胜的模式,我们在前面已经看到,达到这一目标的游泳运动员少之又少。评论员很难找到另一个例子。事实上,在 100 米蝶泳的整个 60 年(15 届奥运会)历史中,唯一一个取得与内斯特相同成就的人是迈克尔·菲尔普斯,他是这项运动中的一个异类。他们还不如问,“为什么学校教育没有达到菲尔普斯的水平?他哪里出问题了?为什么我们的体制这么破,出不了菲尔普斯这样的游泳运动员?"

如果这些评论家指的是高水平的表现模式,他们可能完全错误地认为 Nesty 达到了世界级的一致性,而学校教育没有。我们来看数据。

国际游泳联合会(FINA)公布了游泳运动员的比赛时间和排名数据。本文的分析使用了 100 米蝶泳的⁶记录。从现在开始,“游泳运动员”将专指 100 米蝶泳运动员。

关于计时的说明

比较将基于比赛排名,而不是时间。这是因为游泳运动员的速度越来越快,因此,你出生的越晚,你的速度可能就越快。比较排名是一种更公平的一致性衡量方法,因为它受你出生的时代影响较小。

从 1941 年到 2010 年,我们根据出生年份将游泳运动员分为 14 个五年一组。然后比较了 100 米蝶泳计时的中位数。较暗的红线代表出生时间较长的游泳运动员群体的时间,而较深的蓝线代表出生时间相对较近的游泳运动员群体的时间。我们不仅看到出生较晚的游泳运动员速度越来越快的趋势;他们也一直在竞争,直到一个更大的年龄。因此,为了在游泳运动员之间进行公平的比较,我们应该按照年龄对他们的表现进行排序。

图片作者。

在第二张图中,我画出了每个年龄的选定游泳者的平均时间。这里有三个例子,游泳运动员今年的平均成绩更快,但仍然在同年的奥运会上失利。箭头从输家指向赢家。

  1. 2004 年雅典,伊恩·克罗克(银牌)vs 菲尔普斯(金牌)
  2. 2008 年北京,科罗蒂什金(无奖牌)对劳特斯坦(铜牌)
  3. 2012 年伦敦,科托季什金(银牌)对菲尔普斯(金牌)

在一个比赛日赢得奖牌的快速计时不需要与一年中的稳定表现一致。与此同时,如果游泳运动员参加了足够多的比赛,全年比赛排名的一致性并不太取决于异常的表现或运气。为此,我们根据的竞争排名进行比较。

图片作者。

学校教育与家庭教育

我们比较了学校和 Nesty 在 100 米蝶泳比赛中的排名。学校教育在早期表现优于 Nesty,但下降得更快。他们两人都在 19 至 23 岁的黄金年龄在国际和地区比赛中占据主导地位。然而,他们参加的比赛数量有很大差异。

图片作者。

Nesty 保持不败,但从 18 岁到 24 岁只参加了 7 场比赛。学校教育在 28 竞争,并且几乎自始至终保持着 1 的中值排名。上学从未赢得过世界锦标赛,但他赢得了地区赛(亚洲和海洋运动会)和美国的许多比赛,对手是像菲尔普斯和罗切特这样的强手。我的一个朋友敏锐地观察到,在内斯特的时代,比赛本来可以少一些。虽然这是可以理解的,但事实仍然是,学校教育在比 Nesty 多得多(4 倍)的竞争中受到考验。此外,我不太确定 1987-1992 年是否有像菲尔普斯和罗切特这样的精英游泳运动员与内斯特竞争。毫无疑问,在学生时代,没有哪个竞争者能像菲尔普斯那样赢得如此多的奖牌。

Nesty 和学校教育的另一个区别是连续的奥林匹克运动会与他们的黄金年对齐。对 Nesty 来说,这几乎是完美的组合。1988 年,20 岁的他参加了汉城奥运会,1992 年,24 岁的他参加了巴塞罗那奥运会。另一方面,学校教育在 2016 年里约奥运会上以 21 岁的最佳表现赢得了历史最佳年龄的金牌,但由于新冠肺炎,他被迫在 26 岁卫冕冠军,比黄金年龄晚了整整两年。正如我们在整篇文章中看到的,年龄与奖牌胜率呈负相关。

即使我们不能接受这些质量上的差异,我们也不得不承认家庭和学校教育的轨迹看起来是相似的。他们两个:

  • 从 16 岁到 19 岁排名上升,达到第 1 名
  • 直到 23 岁左右一直保持第一名
  • 从 24 岁开始排名下降

学校排名的大幅下降可以用一个异常值来解释:2020 年的东京。就在奥运会之前,学校教育在 6 月份达到了 26 岁。因此,他在东京的第 44 名是他 26 岁时唯一的比赛。我们应该对这个数据持保留态度。

总的来说,如果 Nesty 展示了世界级的一致性,那么学校教育肯定也是如此。

更公平的比较

但是,当然,将学校教育和其他游泳者进行比较是肤浅的。正确的分析应该将学校教育与更广泛的竞争对手进行比较,我们将会这样做。我已经把所有之前的 100 米蝶泳奥运奖牌获得者列入了候选名单,他们至少有五项 FINA 游泳记录。下表提供了完整的列表。

图片作者。

由于缺乏数据而被遗漏的游泳运动员有:

  • 罗斯·威尔士
  • 道格拉斯·拉塞尔
  • 杰里·海登赖希
  • 马特·沃格尔
  • 加里·豪尔
  • 格伦·布坎南

下图显示了以上名单中所有(独特的)26 名游泳运动员加上学校教育的中位比赛排名轨迹。更广泛的模式似乎如下:

  1. 大多数游泳运动员在 19 或 20 岁左右达到接近职业生涯最高水平的级别。
  2. 他们在这个排名上下波动,直到 22 岁,其中许多人排名下降。更好的人会在 23 岁之前保持他们的排名。这两个组中的大多数游泳运动员将不会再次达到中位排名 1。
  3. 极少数人(菲尔普斯、勒克洛斯和 Cseh)保持排名第一,直到相对较老的年龄。

看着大多数游泳运动员的中位排名大幅下降,我们可以说,在黄金时期,学校教育是比较稳定的。25 岁时的表现很难比较,因为大多数游泳运动员完全不再参加 100 米蝶泳比赛。

图片作者。

此外,学校教育在黄金年前后参加了相当多的比赛。一个有趣的观察是,除了破坏市场的勒克洛斯(和我的图表)之外,更大的名字只是在后来增加了他们对竞争的参与。

图片作者。

为了比较学校教育的相对表现,我们绘制了学校教育相对于所有 26 名入选游泳运动员排名的热图。等级的差异在每个单元格中显示。较暗的红色单元格表示对学校教育的较强表现,而较暗的蓝色单元格表示对学校教育的较弱表现。空白单元格表示该游泳运动员在该年龄不参加比赛。

我们看到,从 19 岁到 23 岁,学校教育实际上赶上了像勒克洛斯和菲尔普斯这样的伟大游泳运动员的平均水平,并且超过了很多其他人。我们再次看到,内斯特的表现是可比的。

图片作者。

学校教育的表现也可以被分解为他在 100 米蝶泳比赛中表现出色和表现不佳的人数。我们再次看到,他的“轨迹”看起来与内斯特的相似。他们两人的黄金时期都是从 19 岁到 23 岁。

图片作者。

图片作者。

作为异常值的参考,这些是菲尔普斯和勒克洛斯的等效图,他们一直统治到 30 岁左右,Cseh 和 Korotyshkin 一直表现良好,直到 30 岁出头。

图片作者。

排名部分到此结束。这里的第一点是,仅仅因为他们都获得了 100 米蝶泳金牌,就将学校教育与内斯特进行比较是不完全公平的。如果我们将 Nesty 和 Schooling 的成绩按年龄排列,Schooling 有一个相似的轨迹,尽管通过大量的比赛进行了更严格的测试。其次,数据显示,学校教育胜过了许多其他 100 米蝶泳冠军。这是根据他排名靠前的其他游泳运动员的相对中值排名和原始计数来衡量的。

向前看:年龄会赶上

在我们结束之前,我想展示两个与年龄相关的图表来帮助校准期望值。今年上学 26。从历史上看,大约 86%的游泳运动员在 26 岁之前退出 100 米蝶泳。下图显示了退休年龄的分布,退休年龄定义为 FINA 追踪的游泳运动员最后一次活动的年龄。如果继续上学(这不关我们的事),他将成为这项运动中最坚持不懈的运动员之一,加入拉斯洛·切赫、勒克洛斯和菲尔普斯的行列。

图片作者。

如果上学决定继续(同样,不关我们的事),要知道他很难赶上他 21 岁时的时间。历史上,100 米蝶泳计时随着年龄的增长而减少。下面的图看起来像是时间在减少,直到 29 岁。事实是,游得慢的人会更早停止比赛,只留下最好的人。这反过来会逐渐降低平均值,直到速度快的最终慢下来。没有运动员能逃脱年龄的影响,我们应该适当地校准我们的期望。

图片作者。

当然,这些只是历史平均值。每个运动员都是独一无二的,很难预料运动员未来会如何继续发展。我们只能给他们资源和我们的支持,让他们可以做他们最擅长的事情,而不会被消极情绪拖累。

结论

在这篇文章中,我试图提供一个比目前更全面的分析学校教育的表现,以调查对他的期望是否公平,以及他是否像其他世界级运动员一样稳定。

首先,我证明了上学在东京赢得另一枚奖牌的可能性对他不利。屡次获胜并非不可能,但它们似乎是例外而非常态。我注意到奥运会之间发生的事情很重要,因此用过去奥运会的表现来形成对现在和未来奥运会表现的预期是没有意义的。

第二,我用竞赛排名的比较来表明学校教育确实是世界一流的。数据显示,与奥运历史上所有 100 米蝶泳奖牌获得者相比,学校教育的平均排名高于平均水平。此外,他的记录可以说比内斯特更好。如果“专家”称赞 Nesty 的世界级一致性,他们最好也称赞学校教育。或许,学校教育的轨迹就是“一个世界级运动员所期望的轨迹”。⁷

总的来说,如果上学重复获胜的期望是不现实的,如果上学表现出世界级的一致性,如果他的轨迹比 100 米蝶泳的可比冠军更好,也许我们根本不需要谈论太多。重要的是,我们要尽职尽责地分析数据,形成基于事实的观点,并提出准确而符合逻辑的叙述,而不是简单地满足我们的政治议程。

我知道我可能在计算这些数字时做得过火了。我希望我分析了他的表现,把他描述成世界级的运动员,这样做是公平的。

@Schooling:你永远是我们的冠军!

参考

  1. CNA (2021 年),脸书
  2. G.Wong,解说:我们需要谈谈为什么约瑟夫·斯库林在 CNA 东京 (2021)坠毁
  3. R.格里芬, 120 年奥运历史:运动员和成绩 (2018),Kaggle 数据集
  4. 今天,新加坡体育必须从学校教育的衰落中吸取教训
  5. 黄,解说 (2021),CNA
  6. 国际游泳联合会 (FINA)
  7. 黄,解说 (2021),CNA

最初发表于https://chrischow.github.io/dataandstuff/

科学计算——惨痛的教训

原文:https://towardsdatascience.com/scientific-computing-lessons-learned-the-hard-way-db651f8f643a?source=collection_archive---------9-----------------------

深入介绍科学计算——从 CPU、GPU 和 HPC 集群到最常用的框架——OpenMP、MPI 和 CUDA。

作者图片

我以前很喜欢汽车碰撞测试的视频。他们尤其擅长慢动作——汽车加速,就在撞上障碍物之前,慢动作开始了。到处都是玻璃,门大开着,最后是爆炸!虽然,这些测试非常壮观,对道路安全非常重要,但也非常昂贵。测试所有可能的碰撞场景并验证汽车是否可以安全驾驶是根本不可能的。

同时,碰撞模拟是廉价的,并且可以提供对可能的安全问题的早期洞察。在这种模拟中,计算出汽车各部分的运动、轨迹和变形,并与各部件施加的压力、温度变化等统计数据一起呈现给用户。模拟的好处显而易见!然而,使模拟精确和快速的方法并不明显。想象一下模拟几百万个粒子一秒钟的碰撞行为所需的计算量!

在这里,我们可以从科学计算中获得一些帮助——该领域正在开发计算技术和有效使用计算硬件来解决大问题的方法。这可能令人惊讶,但我们的许多日常活动都受到科学计算的影响。例如,考虑飞机、火箭和汽车的气流模拟,医疗应用的血流模拟,包括预测复杂手术期间的身体行为,天气预报,拆除建筑物的爆炸模拟等等!

科学计算中的大部分工作都集中于在多个独立的机器上并行和分布计算。

我很难想象编写我的第一个科学计算程序——一个简单的基于 OpenMP 的 for 循环的并行化(我将在本文后面讨论 OpenMP 的细节)。虽然我获得了显著的速度提升,但还远远没有达到我在八核机器上期望的 8 倍的速度提升。事实证明,达到表演的边缘比我最初想象的要困难得多。在从事分布式和并行代码工作两年后,我只能触摸到冰山的一角。

回顾我刚开始的时候,我现在可以确定哪些领域是快速编写并行和分布式代码的关键。首先,知道如何编写高效的串行代码。为此,了解硬件架构和内存层次非常重要。了解这些细节有助于找到更好的方法,例如存储数据,优化访问速度。其次,了解并行化框架也很重要——它们的优势、它们更适合的地方以及它们的性能瓶颈。

因此,在本文中,我将介绍科学计算入门的要点,我希望我在入门时就已经总结了这些要点——硬件架构、内存层次结构、将处理器组织成集群的方法以及并行化框架。

硬件架构

为了充分利用现有的机器,了解机器内部的工作原理会有所帮助!有两个关键组件——执行计算的处理单元和存储数据、中间结果和最终计算结果的存储器。可以将独立的处理单元组织成集群。

中央处理器

简单的 CPU 由单核和全局内存组成。内核是一个硬件实体,根据架构的不同,它包括许多算术逻辑单元(ALU)和本地存储器。ALU 负责计算,例如加、减、乘、除和按位逻辑运算。根据不同的体系结构,ALU 执行的功能可能会越来越复杂。例如,复杂的 ALU 可以在一次迭代中执行简单的加法和除法。CPU 的速度是以时钟周期来定义的。时钟周期的持续时间由振荡器控制,振荡器发送电脉冲以确保同步运行。时钟周期越短,振荡器的频率越高,执行速度越快。复杂的 CPU 包括一个以上的核心,称为多核 CPU。

多核 CPU 简化了操作架构。图片作者。

图形处理单元

GPU 最初旨在图形计算,但现在在通用计算中越来越受欢迎。用于通用(GP)应用的 GPU 通常被称为 GP-GPU。不像多核 CPU 通常有 10 个以上的核心,GPU 由数百个核心组成。与 CPU 内核相比,GPU 内核的指令集有限,频率和内存也较低。GPU 不是独立的,需要一个关联的 CPU 来运行。现代架构允许多个 CPU 共享一个 GPU,也允许一个 CPU 拥有多个相关的 GPU。

GPU 内存模型。图片作者。

CPU 及其内存被称为主机,而带有相关内存的 GPU 被称为设备。主机既可以在设备上启动称为内核的功能,也可以管理设备的内存。从主机到设备的数据传输非常昂贵,不应该经常发生。

GPU 编程通常使用来自 Flynn 分类法的单指令多数据(SIMD)模型,因此当其每个核心都被赋予相同的简单任务来执行时,它非常强大,而分支或更复杂的操作会大大降低处理速度。

记忆等级

GPU 和 CPU 都有相同的内存层次。速度较快但较小的内存靠近内核,而速度较大但较慢的内存则远离内核。

寄存器是最快和最小的内存,其次是缓存。寄存器用于存储计算立即需要的值。高速缓存可以有三种类型,L1、L2 和 L3,L1 的速度最快且最小,L3 的速度最慢且最大。缓存用于存储最近使用过或可能很快需要的值。最慢但最大的是随机存取存储器(RAM ),大部分数据都存储在这里。

通常每个内核都有自己的高速缓存,并共享一个公共 RAM。RAM 中的数据以块为单位传输到缓存,称为缓存行。这些是相邻的 RAM 存储单元。因此,每个内核都有来自 RAM 的数据的本地副本。如果核心需要新数据,会在缓存中检查其可用性。如果不是,则发生高速缓存未命中。因此,内核从 RAM 中请求这些数据,这在计算上更加昂贵。程序员必须确保立即需要或经常使用的数据在缓存中。

处理器集群

高性能系统由多个处理器的集合组成,这些处理器可以形成不同的计算机体系结构。有四种基本类型:对称多处理(SMP),非均匀内存访问(NUMA),分布式和混合系统。

计算机体系结构。图片作者。

SMP 中,每个 CPU 都有自己的私有高速缓存,但是所有进程共享一个大 RAM 内存。在 NUMA 所有的 CPU 都可以访问共享内存,然而与 SMP 不同的是,内存被分割在 CPU 之间,物理位置更靠近某些单元。因此,CPU 花费较少的时间访问关闭的存储器,然而,花费较长的时间访问邻近另一个处理单元的存储器。SMP 和 NUMA 都是共享内存系统。另一方面,分布式系统允许每个 CPU 都有自己的内存,只有它自己可以访问。通过消息,通信和数据共享成为可能。对于由不同单元执行的计算重叠的大型问题,通信可能会成为瓶颈。混合系统是分布式和共享内存系统的混合体。

并行化框架

了解了硬件的基础知识,我们现在可以开发并行化框架。下面我谈谈三个最常用的:OpenMP——用于并行计算,MPI——用于分布式计算,CUDA——用于 GPU 的一般用途。

OpenMP

在共享内存架构上执行的应用可以与 OpenMP API 并行化。OpenMP 是 C、C++和 Fortran 中用于并行的库例程、编译器指令和环境变量的集合。OpenMP 是可移植的,并且具有在忽略编译器指令的情况下保持正确性的优势。也就是说,用 OpenMP 并行化的程序将按顺序正确执行。

OpenMP 执行模式基于三大支柱:共享内存、同步和工作负载分配。

一般结构 平行实体称为线程。线程是一个具有堆栈和相关静态内存的执行实体。OpenMP 使用并行执行的 fork-join 模型。几条线在平行区域之前分叉,在平行区域之后结合。并行区域中的工作在线程之间进行划分。

OpenMP API 中线程的分叉和连接。图片作者。

程序执行从单个“主”线程开始。主线程启动(分叉)——在并行区域之前的一组并行“工作”线程。当线程到达并行结构的末尾时,它们同步并终止(连接),只留下主线程。

这种模型的一个缺点是大量的时间花费在分叉、连接和同步上。因此,在可能的情况下,不应该关闭并行区域,并且应该避免任何冗余的同步。

内存模型 OpenMP 是针对共享内存的并行应用。OpenMP 区分共享内存和私有内存。前者可以被任何线程访问和修改。后者由每个线程单独维护。因此,线程可以使用共享变量进行通信。需要两个要求:缓存一致性内存一致性

高速缓存一致性意味着处理器在特定的内存地址中看到相同的数据。出现的一个主要问题是假共享,即同一高速缓存行中的各个存储单元由不同的处理器同时更新。尽管这些更新在逻辑上彼此独立,但整个高速缓存行都是无效的。

多线程应用中的内存一致性是不同线程对同一内存位置的有序访问和更新。内存不一致可能导致争用情况。当几个线程试图同时写入同一内存位置时。

OpenMP 内存模型。图片作者。

工作负载分配 OpenMP 提供了几个指令来管理 for 循环的工作负载分配,其中在 for 循环中执行的工作可以在线程之间分割。指令分为“静态”、“动态”和“引导式”。使用“静态”时,循环迭代在所有线程中平均分配。“动态”时,工作负载在运行时动态分布。一旦每个线程完成了前一个循环,它就会在 for 循环中多执行一个迭代步骤。与“动态”类似,“引导式”允许每个线程被动态地分配连续迭代的块。然而,与“动态”不同,随着先前工作的每次连续执行,分配给线程的迭代次数会呈指数下降。

同步 线程异步执行计算,除非另有说明。为了保持一致性,OpenMP 提供了一系列同步指令。这些可以用来避免竞态条件。主要的有“主”、“关键”、“原子”、“屏障”和“不等待”。当遇到“主线程”时,指定的区域只由主线程执行。“关键”部分中的代码由每个线程轮流执行。与“关键”类似,“原子”用于指定需要由每个线程依次自动更新的语句。如果遇到“障碍”,线程会等待,直到所有线程都到达它。一旦它们全部到达“屏障”。在“nowait”的情况下,线程被迫不在隐式同步点同步,例如在 for 循环的结尾。

消息传递接口(MPI)

顾名思义,MPI 是基于消息交换的。基本的并行实体是一个进程。一组能够相互交换消息的进程称为通信器。通信器中的每个进程都有一个唯一的等级,即在创建过程中分配给它的一个整数 id。进程通过它们的等级在它们之间显式地通信。通信是必要的,因为 MPI 是一个内存分布式系统。

通信 MPI 支持多个和两个独立进程之间的同时消息交换。

点对点通信:在点对点通信中,一个进程执行发送操作,另一个进程执行接收操作,同步或异步。只有在确认接收进程安全接收到消息后,同步发送操作才完成。这被称为阻塞,在这种情况下,进程将暂停,直到操作成功完成。在异步通信中,进程不等待确认,只是在通知 MPI 协议需要发送数据后继续。接收进程也不等待数据,而是在数据准备好的时候期待来自系统的中断。在非阻塞通信中,进程请求 MPI 库为它们执行操作。因此,用户无法预测数据在接收过程中何时可用。

集体通信(Collective communication):MPI 为管理同一个通信器中的多个进程提供了一个框架。该框架包括同步点、数据移动和集体计算。示例包括:广播、分散、聚集和还原。

集体交流类型。图片作者。

与 MPI 相关的两个概念是带宽和延迟。由于消息是通过物理信道(即网络)交换的,所以通信速度是有限的。带宽对数据的移动速度施加了物理限制。很明显,小消息花费的时间更少,而大消息花费的时间更长。然而,即使非常小的消息也需要时间来处理和移动。延迟是任何消息转换时间的下限。近年来提出了不同的计算机网络通信标准,在高性能计算中最常用的是 infiniband (IB)。它提供高带宽和低延迟。

CUDA

CUDA 是 NVIDIA 开发的一个应用编程接口(API ),用于访问 GP-GPU。CUDA 提供对 GPU 虚拟指令集和计算核心的直接访问。GPU 不是独立的设备,必须有一个相关联的 CPU。因此 CUDA 驻留在一个异构的编程模型中。CUDA 依赖于线程网格的概念。

线程是内核的单个执行实例。每个线程同时执行相同的代码,但是处理不同的数据,并且以寄存器的形式拥有一个很小但是非常快速的私有内存。

线程被组织成。每个块都有相关的共享内存,只能由该块中的线程访问。线程可以在同一个块中通信,但不能与其他块中的线程通信。为了相互通信,线程具有由线程和块索引组成的唯一 id。在物理上,一个块被映射到一个 GPU 的单流多处理器(SM),这是一个 GPU 核心的集合,通常被称为流处理器(sp)。同一 SM 中的 sp 数量可能因处理器而异。如果内存足够,可以将多个块映射到一个 SM。

网格是方块的集合。一个网格有一个相关的,大的,但是非常慢的全局内存,每个线程都可以访问。GPU 上区分全局内存和常量内存。每个线程都可以访问它们,但后者是只读的。

摘要

并行化可以是一个强大的工具,但是,如果使用得当,开发和调试时间通常会超过计算收益。

并行处理代码时,记住正在解决的问题是非常重要的,例如,精度是否重要,或者代码的结构是否可以改变。通常,提高串行代码的速度更重要。

要编写好框架的并行和分布式代码知识是不够的。当代码针对特定的硬件架构进行优化时,可以获得最高的增益。

如果你喜欢这篇文章,请与朋友分享!要阅读更多关于机器学习、数据科学、计算机视觉和图像处理的内容,请点击订阅!

我错过了什么吗?不要犹豫,直接给我留言、评论或发消息吧!

Scikeras 教程:使用 keras 调整 CapsNet 超参数的多输入多输出(MIMO)包装器

原文:https://towardsdatascience.com/scikeras-tutorial-a-multi-input-multi-output-wrapper-for-capsnet-hyperparameter-tuning-with-keras-3127690f7f28?source=collection_archive---------15-----------------------

实践教程

一个教程,解释了如何使用自定义架构包装基于 Keras 功能 API 的模型,以便在带超参数调整的 sklearn 中使用。

sklearn 中定义的超参数调整实用程序的使用对于 Keras 中开发的深度学习模型来说是一个挑战;尤其是对于使用 Keras API 定义的模型。然而,西克斯来了,要改变这一切。在本文中,我们探索为具有多输入多输出 ( MIMO 估计器 )的非顺序模型(CapsNet)创建一个包装器,并用 GridSearchCV 装配这个分类器。

照片由尹卡·阿德奥蒂在 Unsplash 上拍摄

关于超参数和调优的一点信息

如果你熟悉机器学习,你一定听说过超参数。 熟悉 sklearn 中 sklearn、keras 和超参数调优的读者,可以跳过这部分github 回购的链接滚动到最后** 。无论如何,给一个复习,超参数是任何机器学习或深度学习模型的一组属性,用户可以指定这些属性来改变模型的训练方式。这些是不可学习的(可学习属性的术语是参数或权重),即它们是用户定义的。通常,超参数控制模型的训练方式,例如,学习率(α)或使用的正则化类型。

超参数调整/优化是设计机器学习或深度学习模型的关键步骤之一。这一步通常需要相当多的关于如何训练模型以及如何将模型应用于正在解决的问题的知识,尤其是当手动完成时。此外,手动调优会增加数据科学家的开销,因为他们要记录所有可能尝试过的超参数。这就是在 scikit-learn( sklearn)的帮助下自动超参数调整发挥作用的地方。

斯科特·格雷厄姆在 Unsplash 上的照片

Scikit-learn 在**sklearn.model_selection**下提供了多个 API 用于超参数调优。但是,使用 sklearn 的警告是,它主要仅用于机器学习模型 API 中没有定义深度学习模型。幸运的是,深度学习的实践者普遍使用的 Keras API 以简化的方式定义和训练深度学习模型,具有 Keras 中定义的深度学习模型的 sklearn 包装器 类。这意味着,人们可以在 Keras 中编写自己的深度学习模型,然后使用这些包装器将其转换为类似 sklearn 的模型。

目前为止听起来不错,对吧?嗯…没那么快。到目前为止,在 Keras 下定义的包装器(或tensorflow.keras)可以将您的模型包装成分类器(KerasClassifier)或回归器(KerasRegressor)。此外,如果您想要包装使用 Keras Functional API 定义的模型,即不是顺序模型 阅读 Keras 中关于 [顺序与功能 API 的更多信息],那也是不可能的。因此,当人们想要使用 sklearn APIs 来调整更复杂的深度学习模型的超参数时,这是一个限制(这也是我为什么如此兴奋地写这篇文章的原因。)

tf.keras糖纸入门:

对于那些不熟悉包装器的人来说,下面的代码示例演示了包装器的使用。我们定义了返回编译后的 Keras 模型的get_model()函数。然后使用KerasClassifier将模型包装到clf中。示例中创建的clf拥有sklearn分类器的所有属性和成员,并可以这样使用。

进入西喀拉斯

SciKeras 是tf.keras.wrappers.scikit_learn的继任者,对 TensorFlow 版本的包装器进行了许多改进。

Scikeras提供了许多期待已久的 API,使开发人员能够将其 tensorflow 模型与 sklearn 进行接口,包括基于功能 API 的模型以及子类 keras 模型。有关新产品的完整列表,请参考 这个包可以通过简单的 pip 安装和从 scikeras.wrappers 导入的包装器轻松安装。

pip install scikerasfrom scikeras.wrappers import KerasClassifier, KerasRegressor

这些包装器在很大程度上向后兼容KerasClassifierKerasRegressor,如果它们已经在您的代码中使用的话,除了 ***build_fn*** 参数重命名为 ***model***

clf = KerasClassifier(build_fn=get_model,...) #Old
clf = KerasClassifier(model=get_model,....)   #New

使用这些包装器进行超参数调整需要注意的另一个变化是 *get_model* 中定义可调参数,不鼓励使用 默认值。相反,用户应该将get_model函数的所有可调参数声明为包装器构造函数的关键字参数。

*#def get_model(param_1=value_1, param_2=value_2,...): -> Discouraged
def get_model(param_1, param_2,...):......return modelclf = KerasClassifier(build_fn=get_model, param_1=value_1, param_2=value_2, ...)
clf = KerasClassifier(build_fn=get_model, model__param_1=value_1, model__param_2=value_2, ...)*

在参数前追加model__也保留了要传递给get_model函数的参数(s ee 路由参数 )。根据是否使用categorical_cross_entropy和调用fit的方式,可能需要对代码进行少量修改(参考 完整列表 )。我们不会深入研究这些实现的细节。

多输入/多输出

Scikit-Learn 本身支持多个输出,尽管它在技术上要求它们是等长的数组(参见 Scikit-Learn 的文档[**MultiOutputClassifier**](https://scikit-learn.org/stable/modules/generated/sklearn.multioutput.MultiOutputClassifier.html#sklearn.multioutput.MultiOutputClassifier))。Scikit-Learn 不支持多输入。

许多用于研究和行业的非平凡深度学习模型要么有多个输入,要么有多个输出,或者两者都有。这种模型可以很容易地在 Keras 中描述和训练。然而,在 sklearn 中使用这样的模型是一个挑战,因为 sklearn 期望模型的Xy是一个单一的 n 维 numpy 数组(多个相同长度的数组允许用于y)。现在,如果所有的输入/输出都是相同的形状,那么连接成一个数组就很简单了。然而,当输入和输出具有不同的形状时,这可能会很快变得混乱,CapsNet 模型就是这种情况(稍后将详细介绍)。

为了使一个模型有多个输入和/或多个输出,SciKeras 允许使用 自定义数据转换器 。在官方文档中给出的例子中,为了利用具有不匹配形状的数组的输入和/或输出列表来实现这一点,采用了从形状数组[E_dim1,E_dim2,E_dim3,...][E_dim1, E_dim2*E_dim3*...]的输入/输出的整形,其中E可以是输入或输出,有效地将所有输入整形为二维 numpy 数组。

根据是用于转换X(特征)还是y(目标),这些自定义转换器可以从自定义估算器中使用,以分别覆盖scikeras.wrappers.BaseWrappers.feature_encoder()scikeras.wrappers.BaseWrappers.target_encoder()。此外,对于具有多个输出的模型,定义一个定制的计分器是明智的,尤其是当输出具有不同的形状或者使用不同的度量时。

CapsNet

冒着过于简化的风险,CapsNet 是 Geoffrey Hinton 等人在 2017 年底提出的一种新架构,他们设计了一种可以在不使用池层的情况下运行的网络。这是通过使用胶囊来实现的,胶囊执行一种形式的' 反向渲染 ',这是通过动态协议路由来学习的。对于本教程,我们不会深入 CapsNet 的理论——对理论感兴趣的人可以阅读 这篇文章 以获得工作理解,并参考 原文【1】了解更多细节。**

高级 CapsNet 架构实施

我们感兴趣的是 Capsule 网络的实现,以及它的整体架构,因为这就是我们想要包装到 scikeras 中的东西。本教程中使用的实现基于由 Xifeng Guo 公开提供的 代码 。该图显示了所实现的体系结构的高级版本,显示了输入和输出的大致流程。

此实现中涵盖的设计方面

  • 胶囊层数需要用户定义或导入。
  • 通过协议路由的封装的动态路由定义了模型中的定制数据流(在用户定义的封装层中实现)
  • 输出不属于同一类型-一个热编码(OHE)矢量和拼合图像-而不是两者都是标签(对于分类器)或连续值(对于回归器)。

设计包装

基于我们到目前为止的讨论,包装器需要覆盖BaseWrappers.feature_encoder()BaseWrappers.target_encoder()。根据所需的转换类型,我们可以选择编写自定义的转换器,或者使用[sklearn.preprocessing](https://scikit-learn.org/stable/modules/classes.html#module-sklearn.preprocessing)中已经提供的众多转换器中的一个。在本教程中,我们将演示这两种转换方式——我们将为输出编写一个自定义转换器,并为输入使用一个库转换器。

此外,由于 Keras 模型的训练机制不能与分类器或回归器的机制严格对应(由于重建模块),我们将在定义我们的估计器时对BaseWrapper进行子类化。此外,对于模型的性能比较,我们需要考虑两个输出——因此,还需要一个自定义的计分器。

输出变压器

对于我们的具体实现,Keras 模型所需的输出必须采用[y_true,X_true]的形式,而 sklearn 期望 numpy 数组作为 targets 数组。我们定义的转换器需要能够在两者之间无缝接口。这是通过将转换器与**fit**方法中的输出相匹配来实现的,然后使用**transform** 方法将输出整形为 Keras 所期望的数组列表,并使用**inverse_transform** 方法将输出整形为 sklearn 所期望的。

我们通过子类化或继承 sklearn 的BaseEstimatorTransformerMixin类来创建我们的自定义转换器MultiOutputTransformer,并定义一个fit方法。该方法可用于将多个库编码器(如LabelEncoderOneHotEncoder)合并到单个变压器中,如 官方教程 所示,具体取决于输出类型。这些编码器可以适合输入,以便transforminverse_transform方法可以正常工作。在该功能中,需要设置参数self.n_outputs_expected_来通知 scikeras 关于fit的输出,而 meta 中的其他参数可以随意设置。该函数必须返回self

然而,在这里给出的代码中,我试图演示在除了可能的分离和重新排列之外,不需要对目标进行转换时的实现。应该注意的是,也可以在标识函数上定义一个FunctionTransformer来实现这一点(这将在下一节中演示)。

对于接受meta参数的model_build_fn,可选定义get_metadata函数。具体到这段代码,transform方法很简单,在inverse_transform方法中,我们需要定义我们的自定义逆转换,因为我们没有任何库编码器可以依赖。

输入变压器

对于输入转换器,我们将使用在sklearn.preprocessingFunctionTransformer中已经可用的库转换器。对于FunctionTransformer,可以在 transformer 构造器的func参数中定义一个lambda函数。但是,拥有一个lambda函数可能会导致pickle出现问题。因此,我们改为定义一个单独的函数传递给FunctionTransformer

MIMO 估计器

为了完成包装,我们如前所述子类化BaseWrapper,并覆盖feature_encoderscorertarget_encoder函数。请注意,在scorer函数中,我们仅评估来自 Capsules 层的输出,因为这是我们希望交叉验证时期优化网络所依据的指标。

使用MIMOEstimator进行超参数调谐

接下来的步骤与第一个使用tf.keras中的包装器的例子非常相似。我们使用get_model实例化MIMOEstimator,并将(超级)参数作为路由参数传递给get_model(前缀为model__)。这些路由的参数还包括那些我们希望使用网格搜索进行调优的超参数。

接下来,我们定义包含超参数列表和相应值的params dict,作为键-值对进行试验。我们使用clf作为估计器来创建GridSearchCV对象,然后将它与数据进行拟合。

*在为GridSearchCV指定cv参数时必须小心,以在训练样本数量( n )、批次大小( b )和交叉验证批次数量(cv)——n之间实现适当的关系,这些数据应能被 cv b 整除。

拟合操作后,网格搜索的结果在gs_res中累积。使用gs_resbest_estimator_属性可以获得最佳估计值,同样,best_score_给出最佳得分,best_params_给出超参数的最佳拟合。

这就是我们如何用最少的代码编写一个定制的包装器,将 Keras 模型与 sklearn API 结合使用。希望对你有帮助。如果您有任何建议或问题,请在评论部分告诉我,特别是如果有这个包装失败的用例/模型。您可以在下面的一些参考资料中找到完整的代码实现。

代码和其他资源

**>此实现的完整代码可以在 这里 找到。

关于自定义 Keras 图层的教程可以在这里https://keras.io/api/layers/core_layers/masking/这里 找到。
实现的 CapsNet 图层可以在 这里找到
**

学术参考文献

[1] Sabour S,Frosst N,Hinton GE,胶囊间的动态路由 (2017),神经信息处理系统进展 2017(第 3856–3866 页)

sci kit-学习管道教程,包括参数调整和交叉验证

原文:https://towardsdatascience.com/scikit-learn-pipeline-tutorial-with-parameter-tuning-and-cross-validation-e5b8280c01fb?source=collection_archive---------11-----------------------

在机器学习项目中,对用于训练和验证目的的不同数据集应用预处理步骤通常是一个问题——sci kit-learn 管道功能有助于解决这个问题

照片由 JJ 英在 Unsplash

什么是机器学习工作流?—它包括所有的预处理步骤,如一键编码、标签编码、缺失值插补,然后是任何特征选择步骤,如选择最佳或递归特征消除(RFE),然后是模型开发和验证步骤—所有这些都构成了一个机器学习工作流。

这个工作流程面临哪些挑战?—一个主要挑战是将相同的变换函数应用于训练、测试和验证数据集。在干净的代码中,优化的方式是使用用户定义的函数,不是每个数据科学家都愿意编写有效的函数。

对于这个问题,scikit-learn Pipeline 特性是一个开箱即用的解决方案,它可以在没有任何用户定义函数的情况下实现干净的代码。

让我用一个示例数据集来演示管道是如何工作的。我采用了一个关于信贷审批的 UCI 机器学习数据集,其中混合了分类列和数字列。

data = pd.read_csv('bank-full.csv', sep=';')
target = data.pop('y')
target = target.map({'yes': 1, 'no':0})
X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.2, random_state=1234)

在我的真实项目中,我有 40 多个分类特征,有些有超过 50 个类别,为此我必须使用 OrdinalEncoder。为此类功能创建一次性编码将导致内存错误。对于演示数据集,我有两列想要应用 ordinal encoder——月份和教育资格——这两列都有意义,因为它们是有序值。

categorical_mask = (data.dtypes=='object')
categorical_columns = data.columns[categorical_mask].tolist()
num_cols = data.select_dtypes(include=['int64','float64']).columns.tolist()oe_cols = [c for c in categorical_columns if data[c].nunique()>5]
ohe_cols = [c for c in categorical_columns if data[c].nunique()<=5]
len(oe_cols), len(ohe_cols), len(num_cols)

在分离列之后,我们现在从每个分类列的初始数据集中获得一个唯一值列表,以便可以在定型、测试和验证数据集之间使用相同的值。这也将给出所有值的详尽列表。人们经常发现,有些值存在于训练中,而不存在于验证中(反之亦然)。为了避免这个问题,我们从初始数据集中取唯一值。有些人可能会质疑建模时可能会有数据泄漏,但是我们是单独应用转换的。

我们现在定义要使用的转换器类型——OneHotEncoder、OrdinalEncoder、simple imputr——我们也可以在此步骤中使用某种缩放函数,如 MinMaxScaler / StandardScaler。

ohe_unique_list = [data[c].unique().tolist() for c in ohe_cols]
oe_unique_list = [data[c].unique().tolist() for c in oe_cols]ohe = OneHotEncoder(categories=ohe_unique_list)
oe = OrdinalEncoder(categories=oe_unique_list)
imp = SimpleImputer(strategy='constant', fill_value=0)

我们使用scikit-learnmake_column_transformer函数来创建预处理列转换器。此外,我们定义了一个参数remainder='passthrough',让没有任何转换器标准的所有其他列通过。我们可以使用像drop这样的其他值来删除任何没有预处理步骤的列。

正如我最初所说的,我有如此多的预测变量,我必须进行特征选择,为此,我尝试了 2 个函数——select kbest 和 recursivefeaturelimination(RFE)。SelectKBest 简单快捷。并且它需要像f_classifchi2这样的函数来找到最佳特性。而 RecursiveFeatureElimination 是一个缓慢的过程,它试图一个接一个地移除特征以找到最佳特征。

在特性选择步骤的顶部,我使用 XGBoost 作为估计器来预测概率。

现在所有这些都被定义为流水线的步骤。因此,如果我们调用管道,它将对数据集进行预处理、特征选择和模型拟合。

preprocess = make_column_transformer((oe, oe_cols),(ohe, ohe_cols),(imp, num_cols),remainder='passthrough'
)
estimator = XGBClassifier(learning_rate=0.05, max_depth=3, n_estimators=2500, random_state=1234)
fs = SelectKBest(score_func=f_classif, k=5)
selector = RFE(estimator, n_features_to_select=5, step=1)steps = [('preprocess', preprocess),('select', fs),('clf', estimator)
]
pipeline = Pipeline(steps)

现在,它就像任何其他机器学习算法一样简单,我们首先拟合,然后使用预测。预测函数执行所有其他预处理,然后应用定型模型。

pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
pred_df = pd.DataFrame({'y': y_test,'y_pred': y_pred})
gini = 2*roc_auc_score(y_test, y_pred)-1

对于我的用例,我需要评估基尼指数,为此我使用了43.33

现在,让我们进行随机搜索交叉验证,以找到最佳 AUC (Gini ),为此,我将一些搜索参数传递给 XGBoostRegressor。

param_grid = {'clf__learning_rate': np.arange(0.05, 1, 0.05),'clf__max_depth': np.arange(3,10,1),'clf__n_estimators': np.arange(50,250,50)
}rand_auc = RandomizedSearchCV(estimator=pipeline, param_distributions=param_grid, n_iter=5, scoring='roc_auc', cv=5, verbose=False)
rand_auc.fit(X_train, y_train)
rand_auc.best_score_y_pred = rand_auc.predict(X_test)
pred_df = pd.DataFrame({'y': y_test,'y_pred': y_pred})
gini = 2*roc_auc_score(y_test, y_pred)-1

现在我有了 Gini 或46.48,比之前的方法稍微好一点——可能需要对模型进行更多的微调。但这不是本教程的重点。

现在,我们可以在 for 循环中的各种分类器上测试相同的管道,比较分数并选择最佳模型。

classifiers = [KNeighborsClassifier(3),SVC(kernel="rbf", C=0.025, probability=True),NuSVC(probability=True),DecisionTreeClassifier(),RandomForestClassifier(),AdaBoostClassifier(),GradientBoostingClassifier()]for classifier in classifiers:steps = [('preprocess', preprocess),('select', fs),('clf', classifier)]pipeline = Pipeline(steps)pipeline.fit(X_train, y_train)   print(classifier)print("model score: %.3f" % pipeline.score(X_test, y_test))

如您所见,使用 scikit-learn 的管道功能有助于简化机器学习工作流程,并使数据科学家的工作更加轻松,可以将时间集中在微调模型上,而不是重复进行数据预处理步骤。

Scikit-Learn 和 Sklearn 有区别吗?

原文:https://towardsdatascience.com/scikit-learn-vs-sklearn-6944b9dc1736?source=collection_archive---------6-----------------------

Python 中的 scikit-learn vs sklearn

照片由 Anastasia Zhenina 在 Unsplash 上拍摄

介绍

scikit-learn 绝对是机器学习和 Python 最常用的包之一。然而,许多新手对包本身的命名感到困惑,因为它看起来有两个不同的名字;scikit-learnsklearn

在今天的短文中,我们将首先讨论这两个包之间是否有任何区别。此外,我们将讨论在源代码中安装和导入哪一个是否重要。

什么是 scikit-learn

该项目最初开始于 2007 年,是谷歌代码之夏的一部分,而第一次公开发布是在 2010 年初。

scikit-learn 是一个开源的机器学习 Python 包,提供支持监督和非监督学习的功能。此外,它还提供了模型开发、选择和评估工具,以及许多其他实用工具,包括数据预处理功能。

更具体地说,scikit-learn 的主要功能包括分类、回归、聚类、降维、模型选择和预处理。这个库使用起来非常简单,最重要的是非常高效,因为它是基于 NumPySciPymatplotlib 构建的。

scikit-learn 和 sklearn 有区别吗?

简短的回答是没有scikit-learnsklearn都是指同一个包,但是,有一些事情你需要知道。

首先,您可以使用scikit-learnsklearn标识符安装软件包,但是建议使用 **skikit-learn** 标识符安装 **scikit-learn** **pip**

如果您使用sklearn标识符安装软件包,然后运行pip list,您会注意到令人讨厌的sklearn 0.0条目:

$ pip install sklearn
$ pip list
Package       Version
------------- -------
joblib        1.0.1
numpy         1.21.2
pip           19.2.3
**scikit-learn  0.24.2**
scipy         1.7.1
setuptools    41.2.0
**sklearn       0.0**
threadpoolctl 2.2.0

此外,如果您现在尝试卸载sklearn,该软件包将不会被卸载:

$ pip uninstall sklearn
$ pip list
Package       Version
------------- -------
joblib        1.0.1
numpy         1.21.2
pip           19.2.3
**scikit-learn  0.24.2**
scipy         1.7.1
setuptools    41.2.0
threadpoolctl 2.2.0

本质上,sklearn是 PyPi 上的一个虚拟项目,它将依次安装scikit-learn。因此,如果你卸载sklearn,你只是卸载虚拟包,而不是真正的包本身。

现在不管你如何安装 scikit-learn,你必须使用 **sklearn** 标识符在你的代码中导入它:

import sklearn

如果您试图使用scikit-learn标识符导入包,您将会以SyntaxError结束:

>>> import sklearn
>>> import scikit-learn
File "<stdin>", line 1
import scikit-learn^
SyntaxError: invalid syntax

即使您试图用__import__()导入它以便处理包名中的连字符,您仍然会得到一个ModuleNotFoundError:

>>> __import__('scikit-learn')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'scikit-learn'

最后的想法

在今天的短文中,我们试图阐明关于scikit-learnsklearn的一些问题,因为很多初学者似乎对在 Python 中开发 ML 功能时使用哪个术语感到困惑。

一般来说,建议您使用scikit-learn标识符(即pip install scikit-learn)来安装库,但是在您的源代码中,您必须使用sklearn标识符(即import sklearn)来导入它。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。

你可能也会喜欢

广义线性模型(GLM)

原文:https://towardsdatascience.com/scikit-learns-generalized-linear-models-4899695445fa?source=collection_archive---------5-----------------------

掌握他们的理论和 Scikit-Learn 的实现

幸运的是,编写“错误修复和稳定性改进”的懒惰习惯还没有出现在软件库的发布说明中。如果没有检查这些笔记,我不会意识到Scikit-Lean0.23 版实现了广义线性模型(GLM)。

我格外关注 Scikit-Learn。不仅因为我一直在使用它,而且在出版了我的书 之后,我想跟踪该库新实现的算法和特性,并把它们作为我的书的伪附录写在这里。

顾名思义,广义线性模型是我们最喜欢的线性回归算法的扩展。我确信你们都非常了解线性回归背后的理论,所以我将在下一节讨论理解 GLMs 所需的细节。

线性回归实际预测什么?

像所有其他工程学科一样,机器学习是建立在抽象层之上的。我们使用为我们抽象一些细节的库。甚至我们对潜在数学的理解也可以被抽象化,所以我们忽略了大部分细节,直到我们需要它们。

线性回归—实际方程

例如,你肯定记得上面的等式,但我们很少注意最后一项,即代表正态分布噪声或误差的ε。从概念上讲,我们知道预测线很少穿过任何实际目标 y。这个术语是为了说明实际目标(y)和预测目标(y-hat)之间的差异。很公平!但是,如果我们模型的输出是 y-hat,而不是 y,无论如何,我们为什么不在这里花点时间来理解有和没有误差项的方程代表什么?

线性回归—期望值

给定 x, E(y|x) ,从模型中得到的值就是 y 的平均值或期望值。假设说,如果我们有大量的数据点,那么对于 x 的每个值,将有多个 y 值。然后,模型将预测每个 x 的 y 的期望值。并且该模型期望剩余的 y 是正态分布的。这就是为什么线性模型假设误差部分和实际目标是正态分布的。

注: 对于给定的 x,E(y|x)为常数。因此,当我们说误差正态分布均值为零时,我们暗示实际目标 y 也是正态分布,其均值为 E(y|x)。这很重要,因为在后面的例子中,我们可能不需要误差项,我们需要关注 y 的分布

我们可以使用 Scikit-Learn 来展示 E(y|x)在实践中是如何工作的。在这里,我们为每个 x 值创建多个 y。让我们看看模型拟合后会预测什么。

from sklearn.linear_model import LinearRegressionx = [[1], [1], [2], [2]]
y = [9, 11, 19, 21]m = LinearRegression()
m.fit(x, y)m.predict([[1]]) # Returns 10
m.predict([[2]]) # Returns 20

可以看到,对于 x=1,预测值是 10,9 和 11 的平均值。同样,对于 x=2,我们得到 19 和 21 的平均值,即 20。

线性回归的方程式—图片归功于作者

注:这里使用的模型是通过最小化均方误差(MSE)来拟合的。当通过最小化平均绝对误差(MAE)来拟合模型时,产生的 y 帽将成为中间值而不是平均值。

到目前为止,一切顺利。但是为什么我们需要更进一步,创建一个这种线性模型的一般化形式呢?

为什么是广义线性模型?

线性模型的预测都是直线,咄!或超平面,当在多维设置中使用时。这使得他们擅长推断。这是更复杂的算法所不擅长的,比如梯度推进和随机森林。

请参见下文,了解线性回归是如何按照预期进行外推的:

from sklearn.linear_model import LinearRegressionx = [[1], [2], [3]]
y = [10, 20, 30]m = LinearRegression()
m.fit(x, y)m.predict([[10]]) # Returns 100, as expected

而基于树的模型无法推断:

from sklearn.ensemble import GradientBoostingRegressorx = [[1], [2], [3]]
y = [10, 20, 30]m = GradientBoostingRegressor()
m.fit(x, y)m.predict([[10]]) # Returns 30! 

然而,推断有时是愚蠢的。举个例子:一件商品被卖出的几率取决于它在网站首页的位置。比方说,我们在主页上有 20 个位置来列出项目。我们根据一件商品所处的位置来计算它的销售频率。位于第 7 位的商品,有 50%的销售机会。位置 8 的商品,40%的时间卖出,位置 9 的商品,30%的时间卖出。现在,我们使用这些信息来估计在位置 1 和 20 出售商品的机会。

线性回归的推断可能是愚蠢的——图片归功于作者

from sklearn.linear_model import LinearRegressionx = [[7],[8],[9]]
y = [0.50, 0.40, 0.30]m = LinearRegression()
m.fit(x, y)m.predict([[1], [20]]) # Returns 110% and -80%

如你所见,我们得到了不切实际的预测。一个位置导致 110%的项目被出售的机会,而另一个位置有被出售的负机会。我们都知道概率应该在 0 和 1 之间。我们该怎么办?

所有统计学家都有一把锤子:转换。如果您的模型不太符合数据,请使用某种转换来转换模型的输入或输出,比如将数据转换成对数标度,并希望您的模型能够工作。我们将在这里做一些非常类似的事情,但是不是转换模型的输入或输出,而是转换它的内部线性方程。

相信我,你已经知道 GLMs 了

我们知道 sigmoid 函数取 0 到 1 之间的值,就像概率一样。

Sigmoid Graph —图片署名归作者所有

Sigmoid 函数

厉害!现在,如果我们用线性方程代替 z,我们可以肯定它的结果将总是保持在 0 和 1 之间

将 Sigmoid 函数代入我们的线性方程

我很乐意将这个转换函数称为链接函数,但是统计学家的大脑通常是颠倒安装的,这就是为什么 GLMs 的创建者决定将其称为逆链接函数,因此,sigmoid 的逆, logitlog odds ,更像是链接函数!

Logit 作为 GLM(逻辑回归)链接函数

我肯定你知道这种型号的另一个名字。没错。是我们的老朋友,逻辑回归。

一个陈词滥调的工作面试问题:“逻辑回归是分类还是回归算法?”。没有 ML 知识的人: 【回归】有点 ML 知识的人: 【分类】有更多 ML 知识的人: 【回归】… 这个问题就像一个哈希函数,根据人们的经验你可以知道他们的答案会是什么,但根据他们的答案你无法知道他们的经验水平。

除了使用链接函数之外,误差项预计不再是正态分布的。事实上,逻辑回归并不是唯一的 GLM,还有一堆其他的模型,我们将在下一节中访问其中的另一个。但是现在,除了我们从线性模型中借用的线性函数之外,这些是构成广义线性模型的主要组件:

  • 一个链接函数,它将 E(y|x)链接到线性方程。
  • 目标遵循指数分布。正态分布只是这个指数家族中的一员。

现在我们可以去一个不同的 GLM 了。其中之一是最近向 Scikit-Learn 介绍的泊松回归。它的朋友称之为费雪回归。

泊松回归

这个回归量非常适合预测计数。一平方英尺一年有多少雨滴?一个链接一天有多少点击量?一件物品在拍卖中有多少次出价?如您所见,所有这些都是在特定范围、时间、区域等内发生的事件的计数。当然,计数之外的一些其他用例也可能需要泊松回归。但关键是我们预测的是非负整数。

为什么对计数有好处?

当然,维基百科上的随机贡献者说泊松回归适用于计数,但要理解为什么,我们必须检查模型是如何工作的。正如我们之前看到的,链接函数和目标的分布是我们理解算法的关键。

指数图——图片署名归作者所有

先说反向链接功能。如前所述,除了书呆子统计学家,没人关心实际的链接函数,它的倒数才是重要的。泊松回归模型中的反向链接是一个指数函数。正如你在上图中看到的,不管它的输入是什么,输出总是正的。因此,它在这里是有意义的,因为我们不希望肯定是负数。通过将指数代入我们的模型方程,它将变成如下:

作为 GLM(泊松)链接函数的指数

接下来是发行。让我们先了解一下与线性模型假设的正态分布相关的问题。然后我们可以讨论泊松回归中使用的分布。

正态分布是围绕其平均值对称的。也就是说,如果 E(y|x)是 2,实际目标同样可能是2+5=72–5=-3。这在这里是不可接受的,因为我们不希望出现负数。相反,我们需要一个偏态分布。

另一个问题是线性模型的误差方差在所有 x 上是恒定的。这就是众所周知的同质性。想想看,如果某人的财富估计在 100 美元左右,我可以容忍它实际上是 104 美元或 92 美元,但我们几乎不能容忍它实际上是 10 万美元的错误。另一方面,如果某人的财富预计为 10,000,000,我们对几十万甚至更多范围内的较大方差有更多的容忍度。这就是为什么我们需要一个方差变化的模型,双关语。

泊松分布勾选了这里的所有方框。它是偏态的,它的方差和它的均值是一样的,也就是说方差随 E(y|x)线性增长。这就是这里使用泊松分布的原因,也是这个模型的名字。

到目前为止,我一直在讲,没有给你看任何代码。所以,让我们用一些代码来结束这篇文章,这样 skimmers 就可以复制和粘贴它,然后就可以收工了。在这里,我创建了一个合成数据,其中目标 y 随 x 呈指数增长。目标具有非常数方差,并且我确保 y 的值都不是负值。

exp = lambda x: np.exp(x)[0]x = np.array([[i] for i in np.random.choice(range(1,10), 100)])
y = np.array([exp(i) + np.random.normal(0,0.1*exp(i),1)[0] for i in x])
y[y<0] = 0

然后我们拟合泊松模型如下

from sklearn.linear_model import PoissonRegressorpr = PoissonRegressor(alpha=0, fit_intercept=False)
y_pred_pr = pr.fit(x, y).predict(x)

瞧,这个模型比老式的线性模型更符合数据。

将泊松回归与线性回归进行比较—图片来源于作者

当然,我作弊了,为我的模型量身定做了数据,在这里展示了一个美好的结局,但我们都喜欢美好的结局,不是吗?

结论

广义线性模型扩展了传统的普通最小二乘线性回归,增加了一个连接函数,并假设目标具有不同的分布,只要这些分布属于指数分布族。

插入链接函数允许模型将其目标限制在 0 和 1 之间(在逻辑回归的情况下),大于 0(在泊松回归的情况下),或任何其他取决于所用链接的限制。除了这里讨论的,还有更多 GLM 氏症。例如,伽玛和逆高斯。

我现在没有谈论正则化,但是 Scikit-Learn 的 GLMs 实现允许在你有许多预测器的情况下,x。如果你不知道什么是正规化,我可能会建议你查看一下我的书里的解释。没有压力!

最后,线性模型擅长外推,不像基于树的模型。然而,它们没有能力捕捉特征交互或它们的非线性。我可能会写一篇后续文章,介绍一些可以用来减轻这些限制的技巧。

同一作者的更多故事:

https://medium.com/codex/a-gentle-introduction-to-confidence-intervals-b9ab64b8a663 https://medium.com/codex/pytests-assert-is-not-what-you-think-it-is-ea59dfcb4bfd

最后说明:所有图表均由作者创作,所有亚马逊链接均为附属链接。而且所有的模型都是错的,但有些是有用的。

使用 PyCaret 为金融行业开发记分卡—第 1 部分

原文:https://towardsdatascience.com/scorecard-development-for-finance-industry-using-pycaret-part-1-8277c761160a?source=collection_archive---------30-----------------------

关于使用轻量级编码工作流开发分类模型的详细信息

照片由 Ameen Fahmy (unsplash)拍摄

在本文中,我将尝试利用机器学习库 PyCaret 来描述银行业的端到端记分卡开发。我第一次接触记分卡开发发生在大约 12 年前,当时我开发了一个倾向记分卡,其目标是预测更有可能购买某个特定银行产品的客户。我利用 SAS 和 SAS EMINER 运行了一个逻辑回归模型,整个过程花了将近三周时间!!!

随着复杂的机器学习算法的出现,我开始使用不同的 R/Python 包,编写冗长的代码,以获得记分卡开发的最佳模型。然而,挑战在于为各种算法做不同类型的数据准备。

当涉及到开发一个具有模型可解释性的机器学习算法驱动的记分卡时——py caret 就是救世主。这个低代码库可以用来执行复杂的机器学习任务——最近构建了一个记分卡,只花了一个小时就完成了。0

实际使用案例——开发记分卡,其中较低的分数意味着客户信用卡违约的可能性较高:

为了开发解决方案,使用的数据集来自 Kaggle: 这里是(虽然数据集包含 25 列,但在实际用例中,考虑了 2000 多个特性。记住这一点,让我们看看下面的方法)

步骤 1:安装练习所需的软件包:

pip install llvmlite -U --ignore-installed
pip install -U setuptools
pip install -U pip
pip install pycaret==2.3.1
pip install pandasql
pip install matplotlib
pip install shap
pip install seaborn
pip install sweetviz**from** **sklearn.metrics** **import** roc_auc_score,balanced_accuracy_score, f1_score, accuracy_score
**from** **itertools** **import** combinations, chain
**from** **pandas._libs.lib** **import** is_integer
**from** **pycaret.classification** **import** *
**import** **matplotlib.patches** **as** **patches**
**import** **matplotlib.ticker** **as** **mtick**
**import** **matplotlib.pyplot** **as** **plt
import** **seaborn** **as** **sns**
**import** **sweetviz** **as** **sv**
**import** **pandasql** **as** **ps**
**import** **pandas** **as** **pd**
**import** **numpy** **as** **np**
*import shap*
**import** **math**

第二步:导入数据(此处来自 google bucket)和 EDA

path=’gs://pt-test/UCI_Credit_Card.csv'
raw = pd.read_csv(path, encoding = 'cp1252’)
print(raw.shape)##output
(30000, 25)##Let’s drop the variable gender ( Sex) as we don’t want to discriminate between male and female -dataset_raw = raw.drop(columns =['SEX'])
print(dataset_raw.shape)##output
(30000, 24)

使用一行代码运行 EDA,并使用 sweetviz 生成 EDA 报告:

feature_config = sv.FeatureConfig(skip=['ID']) # remove the feature that you dont want to include in the EDA
my_report = sv.analyze(dataset_raw, "default.payment.next.month", feature_config)
my_report.show_html()

EDA 输出:顶部的黑色突出显示目标变量

第三步:PyCaret 的数据预处理和设置

  1. 识别数字和分类特征
  2. 通过平均值估算数值缺失
  3. 按模式估算分类缺失
  4. 移除异常值—设置 5%的阈值
  5. 训练数据占 80%,测试数据占 20%
  6. 移除多重共线性
cat_feat = list(dataset_raw.select_dtypes(include=['object']).columns)
int_feat = list(dataset_raw.select_dtypes(include=['int64','float64','float32']).columns)
int_feat.remove('default.payment.next.month')
print(cat_feat)##output
[] - here we dont have categorical feature#setting up the environment:
clf = setup(dataset_raw,target = 'default.payment.next.month',ignore_features = ['ID']  #ignored from model training,numeric_imputation = 'mean',categorical_imputation = 'mode',categorical_features = cat_feat,numeric_features = int_feat,remove_outliers = True,train_size = 0.80            ,session_id = 1988            )

CLF 输出一瞥:它有一长串不同的选项

步骤 4:运行比较模型,选择前 n 个特征

该步骤目前正在运行,主要确定最重要的功能。一般来说,我们有超过 2000 个特征(如客户人口统计,银行,竞争对手信息等)来开始-我们运行 compre_model 选项来获得最佳模型,并使用它来进行特征选择。特征选择也可以在 CLF 步骤中完成,但是在没有 gpu 的情况下需要花费大量时间来获得结果。

base_model = compare_models(fold = 5,sort = 'AUC', n_select = 1)

基于 AUC 排序的模型列表的输出:梯度增强给出最高的 AUC

现在,假设最佳模型是基于 AUC 的梯度提升分类器,我们将利用它来获得前 n 个特征,一般来说,从记分卡的角度来看,它的范围是 25-30 个特征(我们从 2000 个特征开始)。在这个虚拟数据集中,我们将选择前 10 个特征。

n=10
X_train=get_config('X_train') 
feature_names = pd.DataFrame(X_train.columns)
feature_names['var'] = feature_names
feature_imp = pd.DataFrame(base_model.feature_importances_)
feature_imp['imp'] = feature_imp
var_imp = pd.concat([feature_names,feature_imp],axis=1)
var_imp = var_imp[['var', 'imp']]
var_imp = var_imp.sort_values(['imp'],ascending=False)
var_imp_fin=var_imp['var'].head(n).values.tolist()
print(var_imp_fin)##output
['PAY_0', 'PAY_2', 'PAY_3', 'BILL_AMT1', 'LIMIT_BAL', 'PAY_AMT1', 'PAY_6', 'PAY_4', 'PAY_AMT3', 'PAY_AMT2']

步骤 5:再次运行 CLF,然后微调模型

  1. 用选定的重要变量对数据进行子集划分
  2. 运行比较模型
  3. 调整模型,如果需要,自定义网格搜索
  4. 对模型评分并评估
dataset_raw = raw[var_imp_fin + ['ID','default.payment.next.month']]cat_feat = list(dataset_raw.select_dtypes(include=['object']).columns)
int_feat = list(dataset_raw.select_dtypes(include=['int64','float64','float32']).columns)
int_feat.remove('default.payment.next.month')clf = setup(dataset_raw,target = 'default.payment.next.month',ignore_features = ['ID']  ,numeric_imputation = 'mean',categorical_imputation = 'mode',categorical_features = cat_feat,numeric_features = int_feat,remove_outliers = True,train_size = 0.80            ,session_id = 1988            )base_model2 = compare_models(fold = 5,sort = 'AUC', n_select = 1)

根据 AUC,gbc 再次位居榜首

梯度增强分类器在 AUC 方面再次高居榜首,然而需要注意的一点是 AUC 已经从 0.7788 下降到 0.7687,这是由于特征数量从 25 减少到 10。然而,在一个更大的方面,这也可能有助于您确定您希望最终在模型中保留多少特征,因为您不想在 AUC 上损失太多。

当 compare_model 使用预定义的超参数运行模型时,自动调整模型的超参数:

model_tune_gbc = tune_model(base_model2, n_iter=5, optimize='AUC')

模型调整后测试数据集的平均 AUC

第六步:对训练、测试和整个数据集进行评分,并比较基尼系数

def score(main_data,model_name):train = get_config('X_train').indextest = get_config('X_test').indexpredict = predict_model(model_name,main_data,raw_score=True)predict['odds'] = predict['Score_1']/predict['Score_0']predict['score'] = 200 + 28.8539*np.log(predict['odds'])predict['score'] = predict.score.round(0).astype(int)predict_train = predict.loc[train]predict_test = predict.loc[test]auc_train = roc_auc_score(predict_train['default.payment.next.month'], predict_train['Score_1'])print('Gini_train: %.3f' % (2*auc_train-1))auc_test = roc_auc_score(predict_test['default.payment.next.month'], predict_test['Score_1'])print('Gini_test: %.3f' % (2*auc_test-1))return predict,predict_train,predict_test#call the function
scr_all1,scr_train,scr_test = score(dataset_raw,base_model2)#output
Gini_train: 0.636
Gini_test: 0.565

如果你看到上面的结果,训练和测试基尼系数之间的差异超过了 10%,因此进行自定义网格搜索以将差距缩小到 10%以下是有意义的

a)首先打印最终模型超参数-

print(model_tune_gbc)##output 
GradientBoostingClassifier(ccp_alpha=0.0, criterion='friedman_mse', init=None,learning_rate=0.1, loss='deviance', max_depth=6,
max_features='log2', max_leaf_nodes=None,                           min_impurity_decrease=0.002, min_impurity_split=None,                           min_samples_leaf=4, min_samples_split=5,                          min_weight_fraction_leaf=0.0, n_estimators=70,                           n_iter_no_change=None, presort='deprecated',                           random_state=1988, subsample=0.35, tol=0.0001,                           validation_fraction=0.1, verbose=0,                        warm_start=False)

在查看了超参数值之后,我将尝试一下 n 估计值和学习率

params = {'n_estimators':[30,40,50,60],'learning_rate':[0.05,0.2]}
gbc_custom = tune_model(model_tune_gbc,custom_grid=params)

自定义网格搜索的输出

重新计算分数,检查基尼系数的差异-

scr_all,scr_train,scr_test = score(dataset_raw,gbc_custom)##output
Gini_train: 0.593
Gini_test: 0.576

正如你所看到的,训练和测试的基尼系数相差不到 3%

第七步:保存所有相关的数据集和模型对象

#final model
scr_all1,scr_train,scr_test = score(dataset_raw,gbc_custom)
scr_all1.to_csv('scr_all_gbc_custom.csv')
scr_train.to_csv('scr_train_gbc_custom.csv')
scr_test.to_csv('scr_test_gbc_custom.csv')
save_model(gbc_custom,'gbc_custom')

该模型将保存为管道,输出如下所示:

已保存模型的输出

在下一部分,我将详细介绍模型评分、模型评估、作为模型文档一部分的不同指标,如稳定性、基尼系数、增益矩阵、训练和测试数据集的等级排序。

使用 PyCaret 为金融行业开发记分卡—第 2 部分

原文:https://towardsdatascience.com/scorecard-development-for-finance-industry-using-pycaret-part-2-142a7b2b82c8?source=collection_archive---------35-----------------------

使用轻编码工作流评估分类模型

在第 1 部分中,我解释了如何使用 PyCaret 框架来利用模型开发/培训。在这一部分中,我们将重点介绍如何评估开发的模型,并将其与“训练/测试”和“超时验证数据集”进行比较。在 PyCaret 库中,一行代码就可以获得所有必要的模型评估指标,这将有助于最终确定模型。

照片由 Ameen Fahmy (Unsplash)拍摄

模型评估可以大致分为稳定性预测能力。而稳定性预测力在其下有不同的成分。模型评估的不同指标解释如下。

模型评估的关键指标

群体稳定性指数 —群体稳定性指数衡量开发验证 / 测试数据集之间的分布差异。它总是取正值,指数值越高,差值就越大。

(开发% —验证%) 日志(开发% /验证%)100

经验法则:数值< 10 表示 PSI 为绿色(分布之间几乎没有差异)。10 到 25 之间的值被视为琥珀色(这意味着在分布中观察到一些变化,需要调查)。PSI > 25 表示开发和验证数据集之间的分布不同。

在上一部分中,我已经保存了 scr_train 和 scr_test 文件,其中包含客户列表、得分变量、事件的预测值、实际事件和模型中重要的重要特征。现在,为了获得模型,我们将使用以下代码:

**from** **sklearn.metrics** **import** roc_auc_score,balanced_accuracy_score, f1_score, accuracy_score
**from** **itertools** **import** combinations, chain
**from** **pandas._libs.lib** **import** is_integer
**from** **pycaret.classification** **import** *
**import** **matplotlib.patches** **as** **patches**
**import** **matplotlib.ticker** **as** **mtick**
**import** **matplotlib.pyplot** **as** **plt**
**from** **scipy** **import** stats
**import** **seaborn** **as** **sns**
**import** **sweetviz** **as** **sv**
**import** **pandasql** **as** **ps**
**import** **pandas** **as** **pd**
**import** **numpy** **as** **np**
*# import shap*
**import** **math****def** psi(X,Y):X['pentile'] = pd.qcut(X['score'], 5, labels=**False**) + 1*##Finding the boundary conditions for each pentile*X_tile = pd.DataFrame(X.groupby("pentile").agg({"score": [np.min, np.max]})).reset_index()X_tile.columns = ['pentile','min','max']*##Fixing lowest and highest value for min and max respectively*X_tile.loc[0, 'min'] = -10000X_tile.loc[4, 'max'] = 10000*##joining based on pentile conditions*sqlcode2 = '''select c.pentile, c.cnt as X_count, c.X_tot, d.cnt as Y_cnt, d.Y_totfrom(select a.*, b.*from(select b.pentile, count(*) as cntfrom X aleft join X_tile bon a.score>=b.min and a.score<=b.maxgroup by b.pentile) across join(select count(*) as X_tot from X) b ) cleft join(select a.*, b.*from(select b.pentile, count(*) as cntfrom Y aleft join X_tile bon a.score>=b.min and a.score<=b.maxgroup by b.pentile) across join(select count(*) as Y_tot from Y) b ) don c.pentile=d.pentile'''psi_stg0 = ps.sqldf(sqlcode2,locals()) psi_stg0['X_perc'] = psi_stg0['X_count']/psi_stg0['X_tot']psi_stg0['Y_perc'] = psi_stg0['Y_cnt']/psi_stg0['Y_tot']psi_stg1 = psi_stg0.drop(['X_count', 'X_tot', 'Y_cnt','Y_tot'], axis=1)*##Final PSI calculation*psi_stg1['psi'] = (psi_stg1['X_perc'] - psi_stg1['Y_perc'])*np.log((psi_stg1['X_perc']/psi_stg1['Y_perc']))*100psi_stg2 = pd.merge(psi_stg1, X_tile,  how='left', left_on=['pentile'], right_on = ['pentile'])psi_stg2.loc[0, 'min'] = 'low'psi_stg2.loc[4, 'max'] = 'high'psi_stg2['score_band'] = psi_stg2['min'].astype(str) + "-" + psi_stg2['max'].astype(str)psi = pd.DataFrame(psi_stg2[['score_band','X_perc','Y_perc','psi']])**return** psipsi_train_test = psi(scr_train, scr_test)
psi_train_test = psi_train_test.rename(columns={'score_band': 'score_band', 'X_perc': 'scr_train_perc', 'Y_perc': 'scr_test_perc', 'psi': 'psi'})
psi_train_test['scr_train_%']=round(psi_train_test['scr_train_perc']*100,2)
psi_train_test['scr_test_%']=round(psi_train_test['scr_test_perc']*100,2)
psi_train_test['psi']=round(psi_train_test['psi'],2)
psi_train_test1=psi_train_test[['score_band','scr_train_%','scr_test_%','psi']]
print(psi_train_test1)
print('PSI - scr_train vs scr_test: ' + str(round(sum(psi_train_test['psi']),2)))

scr_train 和 scr_test 之间的 PSI 分布结果相似

**# To plot the PSI graph-****from**  **matplotlib.ticker** **import** PercentFormatter
psi_table=psi_train_test[['score_band','scr_train_perc','scr_test_perc']]
psi_table = psi_table.melt('score_band', var_name='cols',  value_name='% population')
g = sns.factorplot(x="score_band", y="% population", hue='cols', data=psi_table)
g.set(ylim=(0, .50))
g.ax.set_title('Population Stability', size = 18 )
g.ax.yaxis.set_major_formatter(PercentFormatter(1))
g.savefig('PSI.png')

分数分布的图形表示

特性稳定性指数 —这在特性/特征/变量水平上测量开发和验证/测试数据之间的差异。如果 psi 为琥珀色或红色,在特征水平上检查分布差异以理解导致这种变化的变量列表是很重要的。

一旦我们研究了模型的稳定性,下一步就是根据预测能力来评估模型的强度。使用 PyCaret,在一行代码中,我们将得到一个不同的评估矩阵。让我们看看下面提到的例子。

evaluate_model(gbc_custom,use_train_data= **True**) #this would give the result on the train dataevaluate_model(gbc_custom) #this would give the result on the test data# we had saved our model as gbc_custom ( refer to the previous part)

曲线下面积(AUC) —该值越高,模型在区分事件和非事件方面越好。

火车的 AUC。顶部有不同的选项卡,包含不同的评估指标。

AUC 测试

跨事件变量类别的精度/召回/F1/支持

训练和测试数据的混淆矩阵

学习曲线和提升图

特征重要性图和模型的超参数

一旦我们研究了所有这些评估指标,下一步将是创建一个收益矩阵来决定客户定位的分界点。

def weighted_qcut(values, weights, q, **kwargs):'Return weighted quantile cuts from a given series, values.'if is_integer(q):quantiles = np.linspace(0, 1, q + 1)else:quantiles = qorder = weights.iloc[values.argsort()].cumsum()bins = pd.cut(order / order.iloc[-1], quantiles, **kwargs)return bins.sort_index()def gains_matrix(input_gm,target):if 'SamplingWeight' not in input_gm.columns:input_gm['SamplingWeight'] = 1input_gm['mevent']=input_gm[target]input_gm['deciles'] = weighted_qcut(input_gm['score'], input_gm['SamplingWeight'], 20, labels=False)sqlcode3 = '''select deciles, mevent, sum(samplingweight) as countfrom input_gmgroup by deciles, mevent'''gainsfreq = ps.sqldf(sqlcode3,locals())transpose = pd.DataFrame(gainsfreq.pivot_table(index=['deciles'], columns='mevent', aggfunc=sum, fill_value=0).reset_index())transpose.columns = ['deciles','count_0','count_1']transpose.sort_values(by=['deciles'], ascending=False, inplace=True)transpose['cum_0'] = transpose['count_0'].cumsum()transpose['cum_1'] = transpose['count_1'].cumsum()transpose['percent_cum_0'] = (transpose['cum_0']/np.sum(transpose.count_0))*100transpose['percent_cum_1'] = (transpose['cum_1']/np.sum(transpose.count_1))*100transpose['event_rate'] = (transpose['count_1']/(transpose['count_0']+transpose['count_1']))*100transpose['cum_event_rate'] = (transpose['cum_1']/(transpose['cum_0']+transpose['cum_1']))*100transpose['cum_separation'] = transpose['percent_cum_1']-transpose['percent_cum_0']sqlcode4 = '''select deciles, min(score) as scorefrom input_gmgroup by deciles'''score = ps.sqldf(sqlcode4,locals())result = pd.DataFrame(pd.merge(score, transpose , on='deciles', how='outer'))resultn = result.sort_values('deciles', ascending=False)resultn['score_band'] = resultn["deciles"].tolist()[::-1]resultn['score_band'] = resultn["score_band"]+1resultn= resultn.drop(columns=['deciles']) return resultntrain_gain=gains_matrix(scr_train,'default.payment.next.month')
test_gain=gains_matrix(scr_test,'default.payment.next.month')

训练的增益矩阵—有 20 个分数段

等级排序 —在整个评分范围内,事件发生率应理想地单调递增或递减——评分越高,非违约率越高,评分越低,非违约率越低。

rank_train=train_gain[['score_band','event_rate']]
rank_test=test_gain[['score_band','event_rate']]rank_train=rank_train.rename(columns={'event_rate': 'train'})
rank_test=rank_test.rename(columns={'event_rate': 'test'})
rank_table = pd.DataFrame.merge(rank_train, rank_test,on=['score_band'],how='outer')rank_table = rank_table.melt('score_band', var_name='cols',  value_name='event_rate')
g = sns.factorplot(x="score_band", y="event_rate", hue='cols', data=rank_table,grid=False).ax.set_title("Rank Ordering Comparison")
from matplotlib.ticker import FuncFormatter
def to_percent(y, position):s = str(y)return s + '%'
formatter = FuncFormatter(to_percent)
plt.gca().yaxis.set_major_formatter(formatter)
plt.show()
g.figure.savefig('Rank Orderging.png')

培训和测试中分数范围内的事件率

经验法则:就模型实现而言,可以考虑以下阈值(但它可能因行业和用例而异):

模型实施标准—经验法则

这就把我们带到了第 2 部分的结尾。在最后一部分,我们将讨论偏差和模型可解释性

如何使用 Python 和 tabula-py 从 PDF 文件中抓取数据

原文:https://towardsdatascience.com/scrape-data-from-pdf-files-using-python-fe2dc96b1e68?source=collection_archive---------1-----------------------

你想和白板和熊猫交朋友

作者图片

背景

数据科学专业人员正在处理各种形状和形式的数据。数据可以存储在流行的 SQL 数据库中,如 PostgreSQL、MySQL 或老式的 excel 电子表格。有时,数据也可能以非常规的格式保存,如 PDF。在这篇文章中,我将讲述如何使用 Python 库: tabula-py 从 PDF 中抓取数据。

必需的库

  • tabula-py :从 PDF 文件中抓取文本
  • re :使用正则表达式提取数据
  • pandas: 构建和操作我们的面板数据

安装库

pip install tabula-py
pip install pandas

导入库

import tabula as tb
import pandas as pd
import re

以结构化形式抓取 PDF 数据

首先,我们来谈谈以结构化格式抓取 PDF 数据。在下面的例子中,我们要刮左下角的桌子。它是结构良好的表格数据,其中的行和列定义良好。

样本结构化数据(来源)

使用tabula-py可以直接抓取结构化形式的 PDF 数据。我们只需要通过指定area(上、左、下、右)坐标来输入表格数据在 PDF 页面中的位置。在实践中,你将通过反复试验来学习使用什么样的价值观。如果 PDF 页面只包含目标表格,那么我们甚至不需要指定区域。tabula-py应该能够自动检测行和列。

file = 'state_population.pdf'
data = tb.read_pdf(file, area = **(300, 0, 600, 800)**, pages = '1')

以非结构化形式抓取 PDF 数据

接下来,我们将探索更有趣的东西——非结构化格式的 PFD 数据。

为了实现统计分析、数据可视化和机器学习模型,我们需要表格形式的数据(面板数据)。然而,许多数据只能以非结构化格式获得。例如,人力资源人员可能会保留历史工资数据,这些数据可能不会以表格形式创建。在下图中,我们有一个工资数据的例子,它具有混合的数据结构。在左侧部分,它有长格式的数据,包括员工姓名、净额、支付日期和支付期间。在右边部分,它有支付类别、支付比率、时数和支付金额。

(作者创作)

我们需要采取几个步骤将数据转换成面板格式。

  • 步骤 1:将 PDF 数据作为数据框架导入

像结构化格式的数据一样,我们也使用tb.read_pdf来导入非结构化数据。这一次,我们需要指定额外的选项来正确导入数据。

file = 'payroll_sample.pdf'
df= tb.read_pdf(file, pages = '1', area = **(0, 0, 300, 400)**, columns = **[200, 265, 300, 320]**, pandas_options={'header': None}, stream=True)[0]
  • 区域和列:上面已经讲过area。这里我们还需要使用columns来标识所有相关列的位置。像 area 一样,列的值也是通过反复试验来确定的。
  • Stream and Lattice :如果有网格线分隔每个单元格,可以用lattice = True自动识别每个单元格,如果没有,可以用stream = Truecolumns手动指定每个单元格。流模式将寻找列之间的空白。这些选项可能会产生巨大的影响,所以我们可以用 lattice 或 stream 进行实验,看看它们是否能改善整体抓取。

(作者创作)

  • 步骤 2:创建一个行标识符

现在我们有一些数据要处理,我们将使用 Python 库Pandas来操作数据帧。

首先,我们需要创建一个新列来标识唯一的行。我们注意到员工姓名(超人和蝙蝠侠)似乎有助于识别不同记录之间的边界。每个雇员的名字都包含一个独特的模式,以大写字母开始,以小写字母结束。我们可以使用正则表达式'^[A-Z].*[a-z]$'来标识雇员姓名,然后使用 Pandas 函数cumsum(累积和)来创建一个行标识符。

df['border'] = df.apply(lambda x: 1 if re.findall('^[A-Z].*[a-z]$', str(x[0])) else 0, axis = 1)
df['row'] = df['border'].transform('cumsum')

(作者创作)

  • 步骤 3:重塑数据(将数据从长格式转换为宽格式)

接下来,我们将重塑左半部分和右半部分的数据。对于左侧部分,我们创建一个新的数据框架, employee ,其中包括 employee_name、net_amount、pay_date 和 pay_period。对于右侧部分,我们创建另一个数据框架,支付,它包括加班费率、常规费率、加班小时数、常规小时数、加班金额和常规金额。要将数据转换成宽格式,我们可以使用 Pandas 函数pivot

# reshape left section
employee = df[[0, 'row']]
employee = employee[employee[0].notnull()]
employee['index'] = employee.groupby('row').cumcount()+1
employee = employee.pivot(index = ['row'], columns = ['index'], values = 0).reset_index()
employee = employee.rename(columns = {1: 'employee_name', 2: 'net_amount', 3: 'pay_date', 4: 'pay_period'})
employee['net_amount'] = employee.apply(lambda x: x['net_amount'].replace('Net', '').strip(), axis = 1)# reshape right section
payment = df[[1, 2, 3, 4, 'row']]
payment = payment[payment[1].notnull()]
payment = payment[payment['row']!=0]
payment = payment.pivot(index = ['row'], columns = 1, values = [2, 3, 4]).reset_index()
payment.columns = [str(col[0])+col[1] for col in payment.columns.values]
for i in ['Regular', 'OT']:payment = payment.rename(columns = {f'2{i}': f'{i}_Rate', f'3{i}': f'{i}_Hours', f'4{i}': f'{i}_Amt'})

(作者创作)

(作者创作)

  • 第四步:将左边部分的数据与右边部分的数据连接起来

最后,我们使用函数merge根据行标识符连接雇员支付数据帧。

df_clean = employee.merge(payment, on = ['row'], how = 'inner')

(作者创作)

最后一个音符

时至今日,公司仍然手动处理 PDF 数据。在 python 库的帮助下,我们可以通过自动化从 PDF 文件中抓取数据并将非结构化数据转换为面板数据的过程来节省时间和金钱。

请记住,当从 PDF 文件中抓取数据时,您应该始终仔细阅读作者发布的条款和条件,并确保您有权限这样做。

如果你想继续探索 PDF 抓取,请查看我的其他文章:

  • 使用 Python 和 PDFQuery 从 PDF 文件中抓取数据
  • 使用 Python 和 tabula-py 从 PDF 文件中抓取数据
  • 如何使用 Python 和 Pytesseract 将扫描文件转换为可搜索的 PDF
  • 使用 Python 和 Pytesseract 提取 PDF 文本,同时保留空白区域

感谢您的阅读!!!

如果你喜欢这篇文章,并且想请我喝杯咖啡,请点击这里。

您可以注册一个 会员 来解锁我的文章的全部访问权限,并且可以无限制地访问介质上的所有内容。如果你想在我发表新文章时收到电子邮件通知,请订阅。

用 Python Selenium 从嵌套的 HTML 页面中抓取数据

原文:https://towardsdatascience.com/scraping-data-from-nested-html-pages-with-python-selenium-c5f23065841f?source=collection_archive---------20-----------------------

数据收集

一个快速的教程来建立一个从网站上提取的术语列表

图片来自 Pixabay 的凯文·莫里森

抓取包括从网络上的 HTML 页面中提取信息。在 Python 中,可以通过 Selenium 库进行抓取。

在本教程中,我演示了如何通过 Python selenium抓取分布在两层嵌套页面上的术语列表。作为例子,我从 Bocardi 中抓取术语列表。

本教程的完整代码可以从我的 Github 库下载。

装置

通过命令pip install selenium,可以通过pip轻松安装selenium库。除了库,我还需要为我的浏览器安装驱动程序,这取决于我的浏览器的版本。在本教程中,我开发了 Chrome 浏览器。我可以通过在浏览器地址栏输入chrome://settings/help来查看它的版本。

作者图片

在我的例子中,版本是 80,因此我可以从这个链接下载 chrome 驱动。下载完成后,我可以将文件放入我的文件系统的一个普通文件夹中,我需要用 chrome 驱动程序的路径配置$PATH变量:

  • Windows 用户——在这个视频中,我解释了如何在 Windows 10 上为 selenium 安装 Chrome 驱动。
  • Mac OS/ Linux -通过添加下面一行export PATH = "<path to web driver>: $ PATH"来编辑.bash_profile.profile文件,然后重启计算机。

识别网站结构

为了从网站上搜集数据,首先我需要研究 URIs 结构。在我的例子中,术语列表是按字母顺序组织的,字母表中的每个字母都有一个专门的页面,可以在<basic_url>/dizionario/<current_letter>/(URI 的第一级)找到。例如,对于信件a,可在https://www.brocardi.it/dizionario/a/获得专用页面。此外,每个字母的术语列表在不同的页面中分页。对于每个字母,第一页在 URI 的第一层可用,而从第二页开始,URI 改变并在<basic_url>/dizionario/<current_letter>/?page=<page_number>可用。例如,对于字母a,第二页中的术语列表可通过链接[https://www.brocardi.it/dizionario/a/?page=2](https://www.brocardi.it/dizionario/a/?page=2.) 获得。

环境设置

在我的代码中,我需要实现两个循环:一个用于信件的外部循环和一个用于页面的内部循环。我注意到有些字母不见了(jkwxy)。对于外部循环,我构建了一个包含所有字母的列表,但不包括丢失的字母。我利用string.ascii_lowercase来构建字母列表。

import string
letters = string.ascii_lowercase
letters = letters.replace('jk', '')
letters = letters.replace('wxy', '')
letters = list(letters)

然后我定义了两个变量,basic_url,它包含网站的基本 url,和table,它将包含所有提取的术语的列表。最初table是一个空列表。

table = []
basic_url = "[https://www.brocardi.it](https://www.brocardi.it)"

现在,我导入所有的selenium驱动程序和NoSuchElementException异常,它们将用于在执行内部循环时捕捉某种异常。我还导入了pandas库。

from selenium import webdriver
from selenium.webdriver.chrome.options import Options  
from selenium.common.exceptions import NoSuchElementException

嵌套循环

我通过从azfor来实现外部循环。在外部循环的每一步,我都构建 url。然后我通过一个while实现内部无限循环。在内部循环中,我构建了一个执行抓取的驱动程序。我开发了一个Chrome() webdriver,它接收--headless--lang=it选项作为输入。第一个选项指定不打开浏览器,而第二个选项指定浏览器的语言。

一旦连接上,我搜索两个元素:

  • 包含术语列表的元素
  • 包含下一页链接的元素。

这两个元素都依赖于 HTML 页面的结构。我利用函数find_elements_by_xpath()来搜索特定的 XPath。

如前所述,内部循环是一个无限循环,中断条件由一个NoSuchElementException给出,当没有下一页时抛出。术语列表存储在table变量中。

for letter in letters:url = basic_url + '/dizionario/' + letter + '/'while True:try:print(url)options = Options()  options.add_argument("--headless") options.add_argument("--lang=it");driver = webdriver.Chrome(options=options)driver.get(url)# get the list of termsxpath = '//ul[[@class](http://twitter.com/class)="terms-list"]'words = driver.find_element_by_xpath(xpath).texttable.extend(words.split('\n'))# get the next pagexpath = '//a[[@class](http://twitter.com/class)="next"]'url = driver.find_element_by_xpath(xpath).get_attribute('href')driver.close()except NoSuchElementException:break

保存结果

变量table包含所有术语的列表。我可以将它存储到 CSV 文件中。这可以通过构建一个pandas数据框架来实现。

import pandas as pddf = pd.DataFrame(table, columns=['word'])
df['word'] = df['word'].str.lower()
df.to_csv('outputs/glossary.csv')

摘要

在本教程中,我演示了如何安装和使用 Python selenium从嵌套的 HTML 页面中提取数据。

在库安装和配置之后,需要三个步骤:

  • 识别网站的结构
  • 实现两级循环,提取数据和到下一页的链接
  • 将数据保存到pandas数据帧中。

如果你想了解我的研究和其他活动的最新情况,你可以在 Twitter 、 Youtube 和 Github 上关注我。

相关文章

从维基百科表格中抓取数据

原文:https://towardsdatascience.com/scraping-data-from-wikipedia-tables-3efa04c6b53f?source=collection_archive---------20-----------------------

只需几行代码就能打开一个有价值的数据源

GPA 照片档案:https://www.flickr.com/photos/iip-photo-archive/27336508138

几周前,我用美国许多城市的历史人口数据写了一篇关于 T2 的文章。虽然这些数据大部分直接来自美国人口普查,但我也从维基百科的表格中搜集了人口数据,这些表格在一个地方汇集了每个城市的所有可用数据。虽然核实通过维基百科找到的原始数据来源是值得的,但这个在线百科全书包含了大量通常值得搜集的信息。

在本文中,我将分享我用来抓取一个维基百科表格的代码,该表格包含德克萨斯休斯顿的历史人口数据。这段代码可以很容易地从维基百科的其他页面或其他网页上抓取表格。我将介绍的方法基于我在研究生院数据科学课程中教授的材料,相关材料可以在这里找到。

首先,让我们看一下我们将要抓取的表(用红色标出):

https://en.wikipedia.org/wiki/Houston的屏幕截图

这是一个足够简单的表,尽管我们已经可以看到,当我们将这些数据放入 r 中时,可能需要清理一些格式。

一旦你有机会看到网页,让我们来看看代码。首先,加载“tidyverse”和“rvest”包。我们将使用 tidyverse 来操作和清理我们抓取的数据,并使用 rvest 包来进行实际的抓取:

library(tidyverse)
library(rvest)

接下来,我们需要给 R 我们感兴趣的网页的 url:

url = "[https://en.wikipedia.org/wiki/Houston](https://en.wikipedia.org/wiki/Houston)"

然后,我们使用 read_html()函数将该网页转换为 R 可以使用的信息,然后使用 html_nodes()函数专门关注网页中包含的表格对象:

houston_html = read_html(url)houston_html %>% html_nodes(css = "table")

看起来休斯顿维基百科页面包含了 19 个表格,尽管其中一些类别的描述比其他的更有信息量:

接下来,我们从这些可用的表中取出感兴趣的表。函数指定我们想要上面列表中的第四个表。当网页上有多个表格时,确定要在此指定的正确表格可能需要一些反复试验。您可以通过查看网页尽最大努力猜出正确的数字,也可以查看不同的表格,直到看到您想要的内容:

pop_table = houston_html %>% html_nodes(css = "table") %>% nth(4) %>% html_table(fill = TRUE)

我们得到下面的输出,Wikipedia 表现在在 R!然而,正如 web 抓取经常发生的那样,这个表还不能真正使用。所有四列具有相同的名称,第一行和最后一行不包含数据,并且在我们的数据框中间有一个额外的列:

让我们快速清理一下这张桌子,让它更有用。在我们的列有唯一的名称之前,我们什么也做不了,我们还需要将该表限制在第 2-19 行:

colnames(pop_table) = c("year", "population", "blank", "percent_change")pop_table = pop_table[2:19, ]

我们还没有完全实现,但是输出看起来要好得多:

让我们做一些最后的清洁。首先,我们将删除空白列。所有列目前都存储为字符变量,而 year 应该是日期,population 和 percent_change 应该是数字。我们从 percent_change 和 population 列中删除不必要的字符串,然后将所有列转换为适当的格式:

pop_table = pop_table %>% select(-blank) %>% mutate(percent_change = str_replace(percent_change, "%", ""),percent_change = str_replace(percent_change, "—", ""),population = str_replace_all(population, ",", ""),year = lubridate::ymd(year, truncated = 2L),population = as.numeric(population),percent_change = as.numeric(percent_change))

就这么简单。现在,表中的所有内容都符合我们的预期:

人口数据现在完全可用,可以进行分析了。Web 抓取是一种访问各种数据源的强大工具,鉴于其可重复性和减少人为错误的可能性,它比手动复制在线表格中包含的值要好得多。本文中包含的代码还可以用来一次抓取多个表,从而实现更高的效率和访问更多的数据。

上面使用的代码也可以在 this GitHub repo 中找到。

用 Python 抓取特定的 Tweet 回复

原文:https://towardsdatascience.com/scraping-specific-tweet-replies-with-python-3143c5214341?source=collection_archive---------24-----------------------

使用 Tweepy 抓取 Twitters 网站的数据

在 Unsplash 上由 Dane Deaner 拍摄的照片

Twitter 开始接管社交媒体领域。随着越来越多的社区转向 Twitter,我们开始看到数据对广告商、研究人员甚至消费者来说是多么有价值。

数据现在是下一个淘金热,因为我们开始了解数据需要如何提取、转换、加载,并为了充分的利益而转化为信息。理论上,和黄金一样,数据也是一种商品。

在本文中,我打算解释使用 Tweepy 的 Twitter API 从 Python3 的 Twitter 中抓取 Tweepy 是多么容易。Twitter 的 API 和 Tweepy 可以访问这些数据,这是最成功的方法。我计划专注于收集特定用户的推文回复,因为我还没有发现任何专门强调如何提取推文回复的教程。

如果你想直接进入代码,那么你可以在我的Github上找到完整的代码。Python 代码需要您的 Twitter API 和消费者密钥,以及您计划提取回复的 Twitter 用户名和 Tweet ID。

设置 Twitter 开发帐户& Tweepy

确保您的计算机上安装了 Python。如果没有,我建议使用 Anaconda ,否则请阅读官方的 Python 文档以找到额外的资源。

要从你的机器上执行 Twitter 操作,我建议使用 Tweepy 。要安装 Tweepy,请导航到您的环境并运行:

Python3:

pip install tweepy

如果您对 Python 使用 Anaconda:

conda install -c conda-forge tweepy

Twitter 的开发者

如果你想通过电脑或机器与推特互动,你需要申请推特开发者。申请是直截了当的,诚实地说出你在申请中的意图,如果你被 Twitter 认为是值得信任的,你就会被批准。一旦获得批准,您将能够在平台上创建一个应用程序,为您提供从 Tweepy 或您的 Python Twitter 库进行授权的凭据。

Twitter for Developers 提供对 Twitter API 的访问,以便发布和分析推文、优化广告和创建独特的客户体验。点击查看 Twitter API 文档。

在您能够使用 Twitter API 端点之前,创建一个开发人员帐户并生成您的 API 密钥。你可以在这里直接申请开发者账号。您必须回答关于您计划如何使用 API 和接受 Twitter 开发者协议的问题,然后您将被授予访问开发者仪表板的权限。

一旦你被批准访问 Twitter 开发者,登录开发者网站并创建你的应用。这一步将自动生成您的消费者 API 密钥和访问令牌,请记住,您应该对它们保密:

开发者帐户应该链接到您想要激活 bot 的 Twitter 帐户。从 Twitter 开发平台,您可以编辑应用程序权限。在我的例子中,我已经授予我的应用程序读、写和发送直接消息的权限。

使用 Python 的 Twitter 认证

我们必须导入 Tweepy,然后 OAuth 接口来收集数据以及 csv 和 ssl。

import csv
import tweepy
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
# Oauth keys
consumer_key = "YOUR_CONSUMER_KEY"
consumer_secret = "YOUR_CONSUMER_SECRET"
access_token = "YOUR_ACCESS_TOKEN"
access_token_secret = "YOUR_ACCESS_SECRET"
# Authentication with Twitter
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
api = tweepy.API(auth)

为了收集特定用户和 tweet 的 tweet 回复,我们必须列出被搜集的用户的用户名,以及 Tweet ID,可以通过从 URL 复制来找到。

# update these for whatever tweet you want to process replies to
name = 'LunarCRUSH'
tweet_id = '1270923526690664448'
replies=[]
for tweet in tweepy.Cursor(api.search,q='to:'+name, result_type='recent', timeout=999999).items(1000):if hasattr(tweet, 'in_reply_to_status_id_str'):if (tweet.in_reply_to_status_id_str==tweet_id):replies.append(tweet)

因为我希望分析这些回复,所以我决定将所有回复导出为. csv 文件格式,这种格式可以在 Microsoft Excel 或 Google Sheets 中打开。

以下是退回的 csv 的简要介绍:

user,text
CryptoEntuziast,@LunarCRUSH @zilliqa  ofcourse 🚀🚀🚀😎😎😎
ecossexrp1,@LunarCRUSH $VET $XRP 👌🏻
crypto19711,@LunarCRUSH @DAPScoin the best privacy coin in this world! https://t.co/xFHs3cYFmK
lacryptohero,@LunarCRUSH @Theta_Network
Greenmi74576867,@LunarCRUSH https://t.co/vwmznwu77V
SplendidMite,@LunarCRUSH #VeChain $VET
DAPS_CLimaDelta,"@LunarCRUSH Because I am judging a project for the best technology, transparency, reliable team and dedicated commu… https://t.co/6xS9vdx1oY"
DigiBur,@LunarCRUSH #digibyte
M_SRHI,@LunarCRUSH $ICX 💎 $ELA 💎❤️ $NOIA💎
SURAJ_041,@LunarCRUSH @electroneum #ETN .
GbhoyDownUnder,@LunarCRUSH @maticnetwork
jodibreeze86,@LunarCRUSH Zilliqa and Vechain
ghurabar1,@LunarCRUSH $EWT
SamManzi,@LunarCRUSH @NoiaNetwork  @NoiaFr  $NOIA
IamDavidGC,@LunarCRUSH Proud of DigiByte community and technology. $dgb
holder2017,@LunarCRUSH @Falcon_SBS #FNT token traded on #exchanges.  #Anonymous coin #FNC is not traded anywhere.  connected b… https://t.co/0mz7bmaG1k
Lilt8888,@LunarCRUSH It would have to be $ICX
Creeptwo_guy13,@LunarCRUSH That question is way too easy. Absolutely its $ICX #ICON.
BitStreetSheep,@LunarCRUSH #VeChain without question
jms3333333,@LunarCRUSH LInk UBT KNC EWT SOLVE
einnorka,@LunarCRUSH Digibyte
HamishDonalds0n,@LunarCRUSH $icx $vet $zil $ada $eth $link
amity3013,@LunarCRUSH $zil you know it
elianhuesca,"@LunarCRUSH @decredproject by far: hybrid PoW/PoS blockchain, formal governance in place, Treasury with 10% of bloc… https://t.co/oRnMc4UD5P"
AaronMilo,@LunarCRUSH #digibyte https://t.co/000HoTfLqB
majjjubu,@LunarCRUSH Chz
Benjy25680913,@LunarCRUSH $LUNARCRUSH
ItchyTommi,@LunarCRUSH https://t.co/y8l2WwP3qK  Stakenet. The one and only
siggebaskero,@LunarCRUSH #PIVX thanks to @PIVX_Community who's doing a great job 💜 Engaging with a growing community like… https://t.co/CBlhJm7gZj
DanXrp,@LunarCRUSH $VET no doubt
crypto1618,@LunarCRUSH #icx
thelionshire,@LunarCRUSH ICON $icx
ChillMa27939777,@LunarCRUSH #Zilliqa #ZIL ✌😎
BeholdTheBeard,@LunarCRUSH Tezos $XTZ Theta $THETA
lennyshots,@LunarCRUSH #DigiByte
Shatochzi,@LunarCRUSH $CHZ #chiliz
RonDalton01,@LunarCRUSH #VET
Realmikeleonard,@LunarCRUSH #XMR no doubt about it
Incognitor00st1,@LunarCRUSH $DGB  🔥
Cryptowhale10,@LunarCRUSH $ICX https://t.co/WQTbyPkpEB
XxVegetta,@LunarCRUSH We are DAPS soliders  I have been dedicated to our project for 2 years and I think for many years to co… https://t.co/QLk7kKJkhk
CaliCryptoCo,@LunarCRUSH $ICX man
MoonShotCaller,@LunarCRUSH #VeChain 💙  $VET
Dominic_LTC_DGB,@LunarCRUSH @DigiByteCoin
GrowlerGregg,@LunarCRUSH $LINK
adflondon,@LunarCRUSH We all know its $ICX
SajawalOnTech,@LunarCRUSH To many projects but I guess $Wan $link $Zil $Icx
IconPilipinas,@LunarCRUSH $ICX
jonade,@LunarCRUSH $ZIL
twills2,@LunarCRUSH Do we really have to say it......   $zil 🚀

完整 Python 代码

你可以通过访问这个链接来查看所有的代码。

import csv
import tweepy
import sslssl._create_default_https_context = ssl._create_unverified_context# Oauth keys
consumer_key = "XXX"
consumer_secret = "XXX"
access_token = "XXX"
access_token_secret = "XXX"# Authentication with Twitter
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
api = tweepy.API(auth)# update these for the tweet you want to process replies to 'name' = the account username and you can find the tweet id within the tweet URL
name = 'LunarCRUSH'
tweet_id = '1270923526690664448'replies=[]
for tweet in tweepy.Cursor(api.search,q='to:'+name, result_type='recent', timeout=999999).items(1000):if hasattr(tweet, 'in_reply_to_status_id_str'):if (tweet.in_reply_to_status_id_str==tweet_id):replies.append(tweet)with open('replies_clean.csv', 'w') as f:csv_writer = csv.DictWriter(f, fieldnames=('user', 'text'))csv_writer.writeheader()for tweet in replies:row = {'user': tweet.user.screen_name, 'text': tweet.text.replace('\n', ' ')}csv_writer.writerow(row)

最后的想法

在几行代码中,您的可配置 Twitter 回复抓取器现在从 Twitter 中提取数据,并自动将 Tweet 回复保存到您的机器中。

可以做一些事情来改进代码,比如映射多个回复,或者从回复原始帖子的人那里获得回复。如果你有任何问题或建议,请在评论中告诉我。

知识就是力量!分享你的知识,开源你的项目,加入一个社区(任何社区!),也许只是发表一篇关于它的博文。

感谢您阅读

欢迎建设性的批评和反馈。可以在 Instagram @nirholas 、 LinkedIn 和 Twitter @nickresendez 上联系到 Nicholas Resendez,了解新文章的更新。

抓取维基百科的页面浏览量来制作 2020 年的倒带

原文:https://towardsdatascience.com/scraping-wikipedia-page-views-to-make-a-2020-rewind-c9bcac97fa38?source=collection_archive---------40-----------------------

回顾维基百科最受欢迎的文章

图片作者。

人们经常去维基百科了解时事——也许是为了获得网飞最新版本的简介,或者阅读最近去世的名人的成就。

我想利用维基百科的页面浏览数据来创建一种“2020 倒带”——2020 年全年当前事件和趋势的动画时间轴。我是这样做的。

这个项目的代码可以在这里访问。

我首先定义了一个“get_traffic”函数来获取某一天的页面视图数据:

TOP_API_URL = 'https://wikimedia.org/api/rest_v1/metrics/pageviews/'\               'top/**{lang}**.**{project}**/all-access/**{year}**/**{month}**/**{day}**' **def** get_traffic(year, month, day):   TOP_API_URL.format(lang='en',project='wikipedia', year=year, month=month,day=day)resp = urllib2.urlopen(url)     resp_bytes = resp.read() data = json.loads(resp_bytes)     articles = data['items'][0]['articles']     **return** articles

例如,调用 get_traffic('2020 ',' 02 ',' 21 ')将输出一个 JSON 对象,其中包含当天被查看次数最多的 1000 篇 Wikpedia 文章。该对象包含文章的标题、浏览量及其当天的浏览量排名。

我还创建了一个 is_article 函数,在将每篇文章添加到 dataframe 之前验证它确实是一篇文章。

**def** is_article(title, wiki_info):skip = ['-', '404.php', 'XHamster'] + [wiki_info['mainpage']]prefixes = PREFIXES + wiki_info['namespaces']**if** title **in** skip:**return** **False****if** title == "Media":**return** **False****if** title == "Wikipedia":**return** **False****if** title == "United_States_Senate":**return** **False****for** prefix **in** prefixes:**if** title.startswith(prefix + ':'):**return** **False****return** **True**

为了创建倒带,我为 2020 年的每一天调用 get_traffic 函数,并将结果连接到一个 dataframe 中。我只保存了这个数据帧中被查看次数最多的 25 篇文章,而不是 get_traffic 的全部输出。

date1 = '2020-01-01'
date2 = '2021-01-07'
dates = pd.date_range(date1, date2).tolist()top_array = []**for** date **in** dates:year=date.strftime('%Y')month = date.strftime('%m')day = date.strftime('**%d**')print(date) raw_traffic = get_traffic(year, month, day)articles = [a **for** a **in** raw_traffic **if** is_article(a['article'], wiki_info)]top_array.append(articles[:25])views_array = []
articles_array = []
ranks_array = []**for** i **in** range(len(top_array)):date = dates[i]array = top_array[i]**for** j **in** range(25):row = array[j]dates_array.append(date)articles_array.append(row['article'].replace('_', ' '))
*#         print(articles_array)*views_array.append(row['views'])ranks_array.append(row['rank'])dict = {'date': dates_array, 'article': articles_array, 'views': views_array, 'rank': ranks_array}
df = pd.DataFrame(data=dict)

有了数据集后,我按照 Mike Bostock 的教程用 d3.js 创建了一个条形图比赛。为了避免过长的视频,我把 2020 年的倒带分成了两部分。

如果你看了这两个视频,你会注意到一些模式——人们似乎对政治、名人传记、新的娱乐节目和圣经最感兴趣。像“Qasem Soleimani”或“第 25 修正案”这样的文章将会看到一个高峰,因为人们试图消化每天的新闻周期。同样,当一个名人去世时,许多人会涌向维基百科阅读他们的传记。

享受下面的回顾吧!

雕刻数据

原文:https://towardsdatascience.com/sculpting-data-eb8b2b3d8f87?source=collection_archive---------38-----------------------

实践教程

为什么它很重要,以及如何开始制作你自己的

一个生活在寒冷中的人——由作者拍摄

我从未真正欣赏过艺术。我有机会也有特权参观了许多不同的画廊。有些画廊比其他的好,有些画也很美,但总的来说,我总觉得我缺少了什么。直到我发现了雷菲克·阿纳多尔的作品。

在伊斯坦堡的皮莱弗奈利画廊,我偶然看到一件名为 博斯普鲁斯 的作品。在这个装置中,由土耳其气象局收集的当前速度和方向数据被转换成身临其境的体验。

我在 2018 年 12 月看到了这件作品,两年多后,我仍然发现自己在沉思它。这让我想知道,为什么这首曲子能引起我如此强烈的共鸣?部分原因是因为我个人对如何利用数据来了解我周围的世界感兴趣。但这是不同的东西。我没有带着任何关于博斯普鲁斯海峡的水流是如何运作的见解或者任何关于河流的新知识离开那个展览。这是一种更深层的东西,一种联系,以及对持续的、无形的水的搅动的崇敬。

在数据中浮现情感

我们经常谈论当今世界有多少数据,以及毫无疑问这些数据是如何被用来解决科学和商业领域的重要问题的。例如,艾伦图灵研究所和英国南极调查局(BAS)人工智能实验室的团队正在使用机器学习来更好地理解气候现象的相互联系以及它如何影响海冰的可变性,目标是减少气候预测的不确定性。

“科学理性和人类经验的情感本质之间的深刻脱节引发了我们在哪里找到意义的问题…”。

但我认为数据可以用来产生更深层次的共鸣。对我们周围世界的数据驱动的表示产生了一种更容易理解的方式来与正在起作用的力量进行交互并理解它们。Adriene Segal (她也有一些漂亮的作品)在她的陈述中抓住了这一点“科学理性和人类经验的情感本质之间的深刻脱节引发了我们在哪里找到意义的问题…”。

制作艺术

作为我硕士项目的一部分,我一直在培养自己辩论和可视化数据的能力。受到我的新技能的鼓舞,我决定尝试创造我自己的数据驱动的艺术作品。我生活中每一天的平均气温的可视化。根据我对数据科学的 R 的部分阅读,我决定将这个项目分成以下几个部分:

  1. 导入
  2. 整洁的
  3. 设想

1。导入

首先,我需要获取数据。经过一些搜索(和一个附带的,但最终不成功的网络搜索)我发现了 NASA 兰利研究中心(LaRC)的电力项目。对该数据集的访问使收集一致、有效的数据变得轻而易举。R 中的 nasapower 包在完成这一步的过程中发挥了不可估量的作用。或者,单独下载每个 CSV 文件并不是一个可怕的选择,但是通过 R 自动化这个过程意味着我可以用最少的努力持续更新这个文件。我需要数据的每个位置都需要一个新的数据帧,所以我重复这些步骤,直到我的所有数据都在 r 中。

使用 nasapower 包从 Nasa POWER 获取温度数据的语法示例

2。整洁

在这一点上,我有六个电子表格,代表我一生中生活过的不同地方的数据。所以我需要汇总所有的数据。此外,我知道我将使用 Plotly 来可视化数据,并需要用代表一个月的每一列来组织它,这实质上意味着旋转数据。

所以两个步骤:

  1. 将单个数据帧组合成一个组合帧

2。透视数据

3。可视化

Plotly 有一些漂亮的可视化预设选项。这些是我开始探索 r 中的可能性的一个很好的方式。我知道我从一开始就想要一些表面图的版本,并在步骤 2 中使用它来通知整理过程。

学习

通过这个用数据创作一件艺术品的过程,我学到了一些东西。首先,仅仅因为你有一个工具,并不意味着你必须使用它。我选择在 R 中导入、操作和格式化数据,因为这是我的一个实践案例。实际上,在 Microsoft Excel 中透视数据可能会更快。也就是说,如果任何读者感到好奇,他们现在可以通过简单地更改经度和纬度值并对整理过程进行一些其他调整来重复这个练习。

为了检查我的数据争论的准确性,我在每一步之后进行人工抽查。我确信有更一致的方法来处理这个问题,并希望在未来更多地探索这个问题。

最初,我希望这种表现是一个可以 3D 打印成雕塑的表面图。然而,在尝试一些不同的选项时,我将所有的数据绘制成平面 2D 图像。我突然发现视觉效果更吸引人了。从本质上说,拥有更多信息(第三维度)实际上会降低它的影响力。这里有一个重要的教训,可以推广到任何可视化。信息越多并不总是越好。

3D 版,我还是希望能 3D 打印出来

最后,我希望能够在这一点上增加动作,并希望我可以在即将到来的机器学习中应用一些我将学到的东西来促进这一点。

当然还有最后的结果。

一个在摄氏零度的生活——图片由作者提供

这里使用的所有代码都可以在 GitHub 上查看。

Seaborn 可以完成这项工作,那么为什么要使用 Matplotlib 呢?

原文:https://towardsdatascience.com/seaborn-can-do-the-job-then-why-matplotlib-dac8d2d24a5f?source=collection_archive---------10-----------------------

安娜·科洛舒克在 Unsplash 上的照片

应该绕过 Matplotlib 吗?

不久前我有过这样的想法——学习 Matplotlib 对初学者来说是必要的吗?或者他们能逃脱 Seaborn 吗?这种想法最近在指导一群数据科学学生时又回来了。

我知道 Matplotlib 是一个很棒的库,它为定制数据可视化提供了很大的灵活性。

但是我也知道这是一个复杂的库,尤其是对于那些刚接触数据科学和数据可视化的人来说。对某些人来说,这也是一种威胁!

在大多数数据科学课程中,Matplotlib 和 Seaborn 是同时教授的,在我看来,这在人们中间造成了相当大的混乱。他们不明白为什么他们需要两个库,如果只有一个可用的话。为了减轻这种困惑,我通常建议学生先学习 Seaborn 一旦他们适应了它,并理解了它的局限性,他们自然会意识到 Matplotlib 的价值。

如果数据可视化的目的是可视化变量的分布、趋势和关系,Seaborn 拥有所有这些能力。所有主要的统计图——分布图、箱线图、小提琴图、散点图、条形图——都可以用 Seaborn 创建。对于初学者来说,这就是他们所需要的。

但是为什么是 Matplotlib 呢?

因为 Seaborn 会带你走那么远。由于其语法的简单性和美观性,Seaborn 在一开始是一个明显的赢家。但是随着复杂性的增加,Matplotlib 开始变得活跃起来。

Seaborn 和 Matplotlib 之间的权衡(Alam)

本文的目的是用一些基本的数字和最少的代码来比较 Seaborn 和 Matplotlib。我还将展示 Matplotlib 的亮点,以及它在数据可视化方面的强大之处。

我在 Seaborn 附带的演示中使用了以下数据集:

import seaborn as sns
df = sns.load_dataset('tips')
df.head()

其中 Seaborn 和 Matplotlib 相等

只要是基本的统计图,没有太大区别。

我们可以使用 Seaborn 为两个变量total_billtips创建一个简单的散点图:

sns.scatterplot(x='total_bill', y = 'tip', data = df)

由 Seaborn 可视化的简单散点图

我们可以使用 Matplotlib 做同样的事情:

plt.scatter(x='total_bill', y = 'tip', data = df)

用 Matplotlib 可视化简单散点图

如果你仔细观察,你可能会发现一些细微的差别,但是让我们暂时忽略它。

让我们试试另一个图,这次是箱型图。

sns.boxplot(y='total_bill', data = df)

Seaborn 可视化的箱线图

这是它在 Matplotlib 中的显示方式:

plt.boxplot(x='total_bill', data = df)

Matplotlib 可视化箱线图

例如,在这两种情况下,Seaborn 和 Matplotlib 提供了相似的统计信息。但你大概意识到了剧情质量和外观上的差异。

Seaborn 闪耀的地方

Seaborn 在两个方面大放异彩:1)简单和 2)美学。

所谓简单,我指的是它创建情节的简短、直观的语法。让我们看看这个例子——上面的散点图,但增加了一些功能。

首先是 Matplotlib:

color = {'Lunch': 'blue', 'Dinner': 'darkorange'}plt.scatter(x='total_bill', y='tip', data=df, c = df['time'].map(color))

现在有了 Seaborn:

sns.scatterplot(x='total_bill', y='tip', data=df, hue='time')

如果你看一下代码,你会承认 Seaborn 非常优雅和直观,而 Matplotlib 的代码却很混乱。

现在让我们转向美学。诚然,审美是一个主观的问题,但即使如此,你也会欣赏看起来更好的情节,并在审美上令你满意。

plt.violinplot(df['total_bill'])

sns.violinplot(y=df['total_bill']

如果您比较两个库创建的两个小提琴情节,有两点非常突出:

  • Seaborn 的情节很有美感
  • Seaborn 生成了更多的信息(你在 violin 情节中看到一个盒子情节了吗?)

Matplotlib 的力量

Matplotlib 是一个海量库,我听说有 7 万多行代码。人们说它是一个“低级”库,也就是说,与“高级”单行 Seaborn 代码相比,它给了数据科学家很大的灵活性。下面是 Matplotlib 增强数据可视化的几个例子。

陪衬情节

Matplotlib 最擅长支线剧情。你可以用plt.subplot(nrows, ncols)创造尽可能多的支线剧情,并在每个支线剧情中放入任何你想要的东西。下面是一个用 4 个空支线剧情(nrows=2, ncols=2)创建的图。

fig, ax = plt.subplots(2,2, figsize=(10,6))

Matplotlib 功能创建的子情节的结构

我们现在可以添加 Seaborn 图,并将它们放置在图中我们想要的任何位置。让我们利用第二和第四支线剧情顺时针放置一个箱线图和散点图。

fig, ax = plt.subplots(2,2, figsize=(10,6))# boxplot
sns.boxplot(y='tip', data=df, ax=ax[0,1])
ax[0,1].set_title('This is Seaborn boxplot')# scatterplot
sns.scatterplot(x='total_bill', y='tip', data=df, ax=ax[1,0])
ax[1,0].set_title("This is Seaborn scatterplot")fig.tight_layout()

用 Matplotlib 子图可视化的 Seaborn 图

注释

您可以通过添加文本、符号、点、框、圆等多种方式为您的地块添加注释。下面是一个例子——在下面的散点图中,我们希望将两个数据点表示为异常值。

plt.figure(figsize=(8,6))fig, ax = plt.subplots()
sns.scatterplot(ax = ax, x=df['total_bill'], y = df['tip'])ax.annotate('Outlier', xy=(50, 10), xytext=(40, 9),arrowprops=dict(facecolor='red'))ax.annotate('Outlier', xy=(7.5, 5.3), xytext=(10, 7),arrowprops=dict(facecolor='red'))

由 Matplotlib 功能标注的绘图

您可以将这一想法扩展到许多方面,并以许多不同的方式对其进行定制(例如,注释时间序列图以解释高点和低点)。

三维绘图

数据科学家通常将数据可视化为二维图形。但是 Matplotlib 也提供了用它的mplot3d工具包创建三维绘图的功能。你所需要做的就是将关键字projection='3d'传递给一个轴创建例程(例如add_subplot(projection=’3d’))。这里有一个例子:

import matplotlib.pyplot as plt
import numpy as npn_radii = 8
n_angles = 36radii = np.linspace(0.125, 1.0, n_radii)
angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)[..., np.newaxis]x = np.append(0, (radii*np.cos(angles)).flatten())
y = np.append(0, (radii*np.sin(angles)).flatten())z = np.sin(-x*y)ax = plt.figure(figsize=(14,8)).add_subplot(projection='3d')ax.plot_trisurf(x, y, z, linewidth=0.2, antialiased=True)

用 Matplotlib 可视化 3D 绘图

还有更多…

总而言之,Matplotlib 有助于创建各种生产质量的自定义地块。您可以完全按照您想要的方式布置您的图形,设置适当的分辨率,并根据需要为图形添加注释。为了扩展功能,有几个可用的工具包,如底图、制图、matplotlib2tikz、Qt、GTK 等。如果您对可视化的特定专业感兴趣,网上有大量的资源和丰富的文档。

总结和结论

Seaborn 是一个高级库。它提供了简单的代码来可视化复杂的统计图,这也恰好是美观的。但是 Seaborn 是建立在 Matplotlib 之上的,这意味着它可以进一步增强 Matplotlib 的功能。

本文的初衷是调查作为初学者是否需要学习 Matplotlib 和 Seaborn 库。你不必选择一个或另一个,你需要两个,但问题是你需要同时学习它们吗?

重申我已经说过的:首先学习 Seaborn,花一些时间用它来可视化,理解它的局限性。Seaborn 的局限性自然会让你进入 Matplotlib。

希望这是一次有益的讨论,如果你有意见,请随时写在下面,或者通过媒体、推特或 LinkedIn 与我联系。

海豹突击队联系预测,解释道

原文:https://towardsdatascience.com/seal-link-prediction-explained-6237919fe575?source=collection_archive---------12-----------------------

深入探究玩具数据的海豹突击队算法

近年来,图形神经网络变得非常流行。你可以用它们做很多事情,像节点标签预测、图形预测、节点嵌入、链接预测等等。本文旨在通过分析原论文作者之一的实现示例代码来解释海豹突击队(用于链接预测的子图、嵌入和属性)是如何工作的,可以在这里找到:github . com/rusty 1s/py torch _ geometric/blob/master/examples/seal _ Link _ pred . py

介绍

链接预测是图论领域的一个热门研究课题。例如,给定下面的社交网络,其中不同的节点相互连接,我们希望预测当前未连接的节点是否会在未来连接。对于图形神经网络,我们不仅可以使用关于网络结构的信息,即两个给定节点共有的连接,还可以使用每个节点的特征,如他们的爱好、他们上过的学校等。

作者图片(图表示例)

简单图形

首先,我们将创建一些玩具数据来玩:

  • edge_index :定义节点之间的边。
  • x :节点特征(玩具数据中的 15 个唯一节点)。每个节点都将拥有由用户定义的自己的特征(例如,年龄、性别、单词包等),这些特征与定义每个子图中节点的结构角色的特征连接在一起(这将在下一节中变得更加清楚)。
  • 训练、测试、验证掩码:布尔掩码数组。

作者图片(图表)

封闭子图和节点标记

对于 edge_index 中的每个链接,我们需要提取由跳数、源节点和目标节点定义的封闭子图,并根据双半径节点标记(DRNL) 算法标记每个子图中的每个节点。这意味着使用以下公式为每个节点分配一个标签:

https://arxiv.org/pdf/1802.09691.pdf

这取决于节点(i)源(x)目标(y) 节点之间的距离(跳数)。

这种标记方法捕获了节点相对于源节点和目标节点的结构角色,为模型提供了更多信息以正确预测链接的存在。

在上面的例子中,我们说明了节点 7 和 12 的标记将跳数设置为 2。将整个图的两个节点和边索引插入 k_hop_subgraph 函数,我们得到新的子节点(正如我们可以看到的,它包括距离源节点和目标节点 2 跳以内的所有子节点)子边索引(对于子图为边索引)和子图中每个节点的 z 分数作为输出。

对于源节点和目标节点,z 得分等于 1。节点 11 和 14 具有相同的 z 分数,等于 7:

按作者分类的图像(Z 得分计算)

因为它们与源节点和目标节点的距离相同,所以它们具有相同的 z 分数。

现在,我们需要将我们的数据分为训练、测试和验证,创建负样本并将它们全部插入到一个数据加载器 [1]。第一部分很简单——我们从图中移除一些边,并使用它们进行测试/验证。我们需要注意的一件事是不要将连接的组件分成多个组件。该检查似乎在 train_test_split_edges 函数的当前实现中缺失。我们还添加了“无向图”参数,以消除到无向图的转换,并避免重复的子图(用于[ src =1, dst =2]的子图与[ src =2, dst =1]的子图相同)。这允许减少数据集所需的 ram 数量。但是我们仍然希望节点之间的无向链接能够双向传递消息——我们稍后将回到这一点。为了创建反例,我们随机选取一些图中不存在的边。在当前的实现中,所采用的策略是琐碎的,并且没有考虑像节点之间的距离、节点的度数、节点的相关性等信息。在训练稳健模型的优化过程中,反例非常重要,因此不应该低估这一点[2]。

DGCNN 模型

在这一部分中,我们将试图理解 DGCNN 模型在做什么。我们将仔细分析每一步发生的事情。为简单起见,我们假设:

  • 批量大小 = 1
  • 归一化偏差 =假(在 GCNConv 层)
  • GCNConv 层数 = 1

首先,我们计算参数 k ,它类似于句子的 NLP 最大长度,如果序列长度大于 k ,我们要么截断序列,要么填充。类似地,每个封闭的子图将有不同数量的节点,因此我们将需要截断或填充。之后,我们对数据加载器中的元素进行一些调整,并应用 GCNConv 层:

1.由于我们从 train_test_split_edges 函数中移除了到无向子图的转换以节省空间,我们需要在此阶段重新引入它(DGCNN.py 文件中的第 90 行)。通过这种方式,我们可以双向传递信息。在聚合过程中,我们还希望包含节点自己的特性,因此我们添加了一个 self 循环(DGCNN.py 文件中的第 91 行)。

2.使用权重矩阵变换原始要素。

3.通过聚合每个节点的邻居特征来计算新节点的特征(这里聚合函数是简单的和)。

4.连接卷积层。

5.应用全局排序池操作:
卷积操作变得非常流行,用于从图像中提取特征,它与从图中提取特征的卷积操作有一个主要区别:节点的顺序。图像像素可以被视为一个图形的节点,但它们是自然有序的,这是我们在图形结构中所缺乏的。由此产生了对子图中的节点进行一致排序的需求。该论文的作者证明了图卷积运算近似于 Weisfeiler-Lehman 节点着色(一种通过颜色/节点标签模式来比较不同图的算法),因此我们可以使用卷积运算的最后一层特征来根据节点在子图中的结构角色一致地对节点进行排序。

https://muhanzhang.github.io/papers/AAAI_2018_DGCNN.pdf

排序后,我们标准化每个子图的节点数,如果 k 小于节点数,则截断排序后的节点序列,否则填充[3]。

6.在我们根据 WL 颜色进行了分类之后,我们得到了图像中的结构化数据,因此我们可以应用 1D 卷积、最大池和线性层来最终预测 2 个节点之间的链接的存在。

结论

在本文中,我们描述了海豹突击队算法的所有步骤——从节点标记到在一些玩具数据上用图卷积运算传递消息。我们还看到了图像和图形结构化数据之间的相似性,这使我们能够对图形应用卷积运算。作为下一步,我们将使算法适合一些真实数据,以评估不同的挑选负样本的策略如何影响算法的性能。

参考

[1]Stanford . edu/~ sher vine/blog/py torch-how-to-generate-data-parallel

arxiv.org/pdf/2005.09863.pdf

[3]muhanzhang.github.io/papers/AAAI_2018_DGCNN.pdf

将 Python 和 Pandas 功能无缝移植到 Spark

原文:https://towardsdatascience.com/seamlessly-porting-python-and-pandas-functions-to-spark-46718361b93f?source=collection_archive---------38-----------------------

我们可以用简单的方法,或者困难的方法

巴勃罗·阿罗约在 Unsplash 上的照片

注意:这里展示的代码是用于格式化的图片,但是你可以在这里找到它们。

动机

虽然 Pandas 是数据科学和数据分析师工作流中最常见的 Python 工具,但它不能很好地处理大数据集,因为它一次只使用一个内核。它还使用了惊人的内存量。在 Wes McKinney 的博客文章中,他提到经验法则是拥有 5 倍或 10 倍于数据集大小的 RAM。

当熊猫的数据处理效率低下时,数据科学家开始寻求 Spark 等分布式计算框架。这些框架通过使用单台机器甚至整个集群上的可用内核来加快计算速度。不利的一面是,为了利用 Spark,Pandas 和 Python 代码通常不得不与 Spark 兼容。

在本文中,我们将回顾一个使用 Fugue 将 Pandas 和 Python 代码无缝移植到 Spark 的例子,这是一个用于分布式计算的开源抽象层。在经历了赋格方法之后,我们将使用 Spark 3.0 中可用的 Spark 的 mapInPandas 方法将其与传统方法进行比较。

示例问题

在这个示例问题中,我们有一个已经使用 Pandas 和 scikit-learn 训练过的机器学习模型。我们希望在一个太大的数据集上运行预测,熊猫无法使用 Spark 有效地处理这个数据集。本教程也将适用于转换数据的操作。我们不局限于机器学习应用。

首先,我们从制作一个简单的线性回归模型开始。

简单线性回归

然后,我们创建一个简单的预测函数,该函数将接收一个数据帧并创建一个名为“预测”的新列,其中包含预测。这部分对于熊猫用户来说并不新鲜。

用赋格在火花中执行

这就是奇迹发生的地方。 Fugue 是一个抽象层,旨在使用户能够将 Pandas 和 Python 代码移植到 Spark。稍后,我们将展示如何在没有赋格的情况下手动完成,但首先,我们将看看赋格是如何完成这一点的。

神游有一个转换函数,它接受熊猫或火花数据帧和一个函数。当我们指定一个执行引擎时,Fugue 将应用必要的转换来运行该引擎上的代码(在本例中是 Spark)。如果没有指定引擎,它将在熊猫上运行。请参见下面的代码片段。

这就是字面意思。这将在 Spark 上运行。代码块中的大多数内容都很容易理解。 input_df 可以是 Pandas 或 Spark 数据帧。 predict 函数是我们之前定义的函数,而 params 参数包含传递给该函数的内容。在这种情况下,我们通过了之前训练的回归模型。因为我们选择了 SparkExecutionEngine ,所以所有代码将以并行方式在 Spark 上运行。

最后要理解的是模式参数。这是因为模式在 Spark 中被严格执行,需要显式。使用 "*,predicted:double" ,我们指定保留所有列,并添加一个名为的 double 类型的预测的新列。这是 Spark 方法的一个大规模简化的语法,我们将在后面看到。

使用 Fugue 中的 transform 函数,我们能够在 Spark 上使用 Pandas 函数,而无需对原始函数定义进行任何修改。让我们看看如何在不赋格的情况下做等效的动作。没有必要完全理解下一节的所有内容,重点只是展示 Fugue 的界面简单了多少。

Spark 实施

这一部分是为那些想比较这些方法的人准备的。下面的代码片段是如何使用 Spark 的 mapInPandas 实现的:

这些步骤依次为:

  1. 创建 SparkSession (Fugue 的 SparkExecutionEngine 在幕后完成这项工作)
  2. 包装预测函数以处理数据帧的迭代器。这是因为该函数将接收多个数据帧(分区),并且预测每个集合。
  3. 创建一个接受 Spark 或 Pandas 数据帧的 run_predict 函数。将数据帧转换为 Spark 数据帧(如果尚未转换)。
  4. 拉出模式并添加 double 类型的新“预测”列。
  5. 使用 mapInPandas 方法将操作映射到分区。

神游的转换为用户处理所有这些。

结论

在本文中,我们比较了将 Pandas 和 Python 函数引入 Spark 的两种方式。第一个是 Fugue,我们简单地调用了 SparkExecutionEngine 上的转换函数,所有的转换都为我们处理了。第二个是使用 vanilla Spark,我们必须创建辅助函数。

对于一个函数,我们已经在 Spark 实现中编写了许多样板代码。对于一个有几十个函数的代码库,实践者最终会编写大量的样板代码,使代码库变得混乱。虽然使用 Fugue 最简单的方法是使用 transform 函数,但是这种编写与 Pandas 和 Spark 都兼容的代码的概念可以扩展到完整的工作流。如需了解更多详情,请随时联系(信息如下)。

联系我们

如果您有兴趣了解更多关于神游、分布式计算或如何以更简单的方式使用 Spark 的知识,请随时联系我们!这里涵盖的内容只是起点。赋格团队正在给数据团队提供完整的研讨会和演示,并希望与您交谈。

Github 回购

松弛

赋格教程

邮箱:hello@fugue.ai

搜索算法-广度优先搜索,使用 Python

原文:https://towardsdatascience.com/search-algorithm-breadth-first-search-with-python-50571a9bb85e?source=collection_archive---------10-----------------------

从头开始 Python 实现

在本文中,我将介绍一种叫做广度优先搜索(BFS)的基础搜索算法。

Geran de Klerk 在 Unsplash 上拍摄的照片

定义

搜索算法是一种检索存储在某些数据结构中的信息的算法,或者是在问题域的搜索空间中计算的信息[1]。广度优先搜索是一种遍历或搜索树或图数据结构的算法。它从根节点开始,在移动到下一个深度级别的节点之前探索当前深度的所有节点[2]。换句话说,它扩展了最浅的未扩展节点,这可以通过先进先出(FIFO)队列来实现。让我们通过下图来看一个例子:

穿过 BFS 的示例图。图片作者。

BFS 算法,逐步扩展。图片作者。

从上面的逐步展开图中,我们可以看到 BFS 算法优先选择最接近起始顶点的边来执行搜索。

现在,让我们来评估这个算法:
d 表示为最小成本解的深度,将 b 表示为搜索树或图的最大分支因子。假设一个假设的状态空间,其中每个节点可以扩展成 b 个新节点,路径长度 d: 的解

  1. 时间复杂性:找到解决方案需要多长时间?
    1 + b + b + b + …。+ bᵈ = O(bᵈ)
  2. 空间复杂度:内存中的最大节点数
    保持内存中的每个节点= O(bᵈ)
  3. 完整性:如果存在的话,它是否总能找到解决方案?是的
  4. 最优性:它总能找到最佳(最低成本)的解决方案吗?
    是的,当所有步骤花费相等时。

代码实现

让我们用上面的例子用 Python 来实现 BFS 算法。

该图是具有顶点 V={A,B,C,D,E,F,G,H,I,J,K,L,M},E={{A,B},{A,C},{B,D},{B,E},{C,F},{C,G},{D,H},{D,I},{E,J},{E,K},{F,L},{F,M}}的图形的示意图

  1. 创建接收图的边的函数,输出无向图的邻接表

代码的输出。图片作者。

2.创建接收邻接表和起始顶点的函数,输出 BFS 搜索序列

代码的输出。图片作者。

推荐阅读

参考

[1] 搜索算法—维基百科

[2] 广度优先搜索—维基百科

搜索算法-深度优先搜索,使用 Python

原文:https://towardsdatascience.com/search-algorithm-depth-first-search-with-python-1f10da161980?source=collection_archive---------18-----------------------

从头开始 Python 实现

在本文中,我将介绍一种叫做深度优先搜索(DFS)的基础搜索算法。

丹尼尔·勒曼在 Unsplash 上的照片

搜索算法是一种检索存储在某些数据结构中的信息的算法,或者是在问题域的搜索空间中计算的信息[1]。通过选择节点扩展的顺序来定义搜索策略。可以从以下几个方面评估战略:

  1. 完整性:如果存在的话,它是否总能找到解决方案?
  2. 时间复杂性:找到解决方案需要多长时间?
  3. 空间复杂度:内存中的最大节点数
  4. 最优性:它总能找到最佳(最低成本)的解决方案吗?

定义

深度优先搜索是一种遍历或搜索树或图数据结构的算法[2]。在解释 DFS 算法之前,我们先介绍一下图形数据结构。一个 G 是一个对(V,E),其中 V 是一个有限集,E 是 V 上的一组二元关系

❖ V 被称为顶点集,它的元素是顶点

❖ E 被称为边集,它的元素被称为。一条边由一对顶点表示。

图形数据结构的例子[3]

该图是顶点为 V={1,2,3,4,5,6},E={{1,2},{1,5},{2,3},{2,5},{3,4},{4,5},{4,6}}的图形的示意图

深度优先搜索

现在,让我们开始详细解释 DFS。它从根节点开始,扩展最深的未扩展节点,仅在不再扩展时回溯。让我们通过下图来看一个例子:

通过 DFS 的示例图。图片作者。

DFS 算法,逐步扩展。图片作者。

从上面的逐步展开图中,我们可以看到,DFS 算法沿路径优先选择边来执行搜索。

现在,让我们评估这个算法:
m 表示为状态空间的最大深度,将 b 表示为搜索树或图的最大分支因子

  1. 完备性:
    无限深度空间:否
    有循环的有限深度空间:否
    有重复状态检查的有限深度空间:是
    无循环的有限深度空间:是
  2. 时间复杂度:O( bᵐ
  3. 空间复杂度:O( bm
  4. 最优性:否

代码实现

让我们用上面的例子用 Python 实现 DFS 算法。

该图是具有顶点 V={A,B,C,D,E,F,G,H,I,J,K,L,M},E={{A,B},{A,C},{B,D},{B,E},{C,F},{C,G},{D,H},{D,I},{E,J},{E,K},{F,L},{F,M}}的图形的示意图

  1. 创建接收图的边的函数,输出无向图的邻接表

代码的输出。图片作者。

2.创建接收邻接表的函数和输出 DFS 搜索序列的起始顶点

代码的输出。图片作者。

参考

[1] 搜索算法—维基百科

[2] 深度优先搜索——维基百科

[3] 图(离散数学)—维基百科

搜索算法:Dijkstra 算法和统一成本搜索,使用 Python

原文:https://towardsdatascience.com/search-algorithm-dijkstras-algorithm-uniform-cost-search-with-python-ccbee250ba9?source=collection_archive---------6-----------------------

介绍一种称为 Dijkstra 算法的基本搜索算法及其变体,即统一成本搜索(UCS)

弗兰克·麦肯纳在 Unsplash 上拍摄的照片

定义

搜索算法是一种检索存储在某种数据结构中的信息的算法,或者是在问题域的搜索空间中计算的信息[1]。与深度优先搜索(DFS)和宽度优先搜索(BFS)不同,Dijkstra 的算法和统一成本搜索(UCS)考虑了到目标的路径成本。例如,在道路网络中,路径成本可以是行驶的总距离或花费的总时间。 Dijkstra 算法是一种寻找图中从一个节点到每隔一个节点的最短路径的算法,而 UCS 寻找两个节点之间的最短路径。[2]现在,让我们更详细地解释 UCS 算法,它是 Dijkstra 算法的变体。

统一成本搜索

UCS 扩展到目前为止路径代价 g 最小的节点。UCS 是 BFS 的改型。它不使用先进先出队列,而是使用一个具有路径成本 g(n) 的优先级队列 对节点进行排序。[3]让我们通过下面的有向图来看一个示例,开始节点 a 和结束节点 d 用蓝色突出显示:

通过 UCS 的示例图形。图片作者。

UCS 或者 Dijkstra 的算法,逐步展开。图片作者。

从这一步一步的扩展中,我们可以看到路径代价被考虑到,它扩展路径代价最小的节点。例如,从步骤 2 到步骤 3,它扩展了到目前为止路径开销最小的节点 c。此外,在步骤 3 中,即使它在扩展后到达目的节点 d 的总开销为 11,由于节点 b 的当前开销为 6 < 11,所以它将继续扩展节点 b。在步骤 4 中扩展节点 b 后,它也到达目的节点 d 的总开销为 8,并且现在没有更多节点要扩展。所以,从 a 到 d 的最短路径= [a → b → d],总成本= 8。

在了解了 UCS 的工作原理后,Dijkstra 的算法只是一种寻找到每一点的最短路径而不是单点的算法。

现在,让我们来评估这个算法:

  1. 时间复杂性:找到解决方案需要多长时间?
    路径成本 g =最优解成本的节点数
    -相当于从优先级队列中跳出的节点数
  2. 空间复杂度:内存中最大节点数
    路径代价为 g 的节点数=最优解的代价
  3. 完整性:如果存在的话,它是否总能找到解决方案?
    是的
  4. 最优性:它总能找到最佳(最低成本)的解决方案吗?
    是的

代码实现

让我们用上面的例子用 Python 实现 Dijkstra 和 UCS 算法。

该图是具有顶点 V={a,b,c,d,e},E={{a,b,6},{a,c,3},{b,c,1},{c,b,4},{b,d,2},{c,d,8},{c,E,2},{d,E,9},{e,d,7}}的图形的示意图

  1. 创建接受图的加权边的函数,该函数输出有向加权图

代码的输出。图片作者。

2.创建接受有向加权图和起始顶点的函数,该函数输出从起始顶点到图中所有其他节点的最短路径及其前身字典(Dijkstra 算法)

代码的输出。图片作者。

我们举一个例子,解释一下的前身词典是什么意思。例如,目前从的 shortest_path_cost 的输出中,我们知道从节点 a 到节点 d 的 shortest_path_cost = 8,如何准确地到达成本为 8 的节点 d 可以从它的前体字典中得知。我们可以从目的节点 d 开始查找前任字典,其前任是节点 c .按照相同的逻辑:节点 c 的前任→节点 a .因此,从节点 a 到节点 d 的最短路径是从节点 a →节点 c →节点 d。

3.实际上,我们可以使用 Dijkstra 算法的输出来获得 UCS 从节点 a 节点 d 的输出,如下所示:

代码的输出。图片作者。

结论

本文介绍了 Dijkstra 算法和均匀代价搜索算法。两种算法都在寻找成本最低的最短路径,即 Dijkstra 算法:寻找从图中一个节点到所有其他节点的最短路径,UCS:寻找两个节点之间的最短路径。它们是基本的搜索算法,对于解决更复杂的路由问题非常有用,如物流规划、运输和网络通信等。

推荐阅读

参考

[1] 搜索算法—维基百科

[2] 迪杰斯特拉算法—维基百科

[3] 统一成本搜索—维基百科

搜索算法——概念和实现

原文:https://towardsdatascience.com/search-algorithms-concepts-and-implementation-1073594aeda6?source=collection_archive---------17-----------------------

A*、Dijkstra、动态规划、BFS、DFS 和 IDDFS

作者照片

在机器学习中,搜索问题是一种基于状态的模型,有许多应用。比如寻路,机器人运动规划,机器翻译。在本文中,我们将深入研究几种常见的搜索算法,并了解它们如何用于解决不同的任务。

本文的其余部分将涵盖 BFS、DFS、IDDFS、动态编程、Dijkstra 算法和 A*。我会简单说明每个算法的主要思想,提供一个问题,用 Python 编写算法来解决问题。

完整的代码可以在这里找到。

BFS、DFS、IDDFS

BFS、DFS 和 IDDFS 是流行的搜索算法,可用于树和图数据结构。在本文中,我使用术语“节点”来表示树节点或图形组件。

概念

BFS 代表广度优先搜索,其工作方式如下:从根节点开始,探索当前深度的所有节点,然后移动到下一深度级别的节点。它重复这个过程,直到到达最后一个深度或找到目标。通常使用队列来跟踪已经遇到但还没有探索的节点。

DFS 代表深度优先搜索。与 BFS 相同,它从根节点开始。然而,DFS 不是遍历不同的深度,而是在回溯并移动到新的分支之前,尽可能地沿着每个分支探索。这意味着 DFS 通常是递归实现。

IDDFS 代表迭代深化深度优先搜索,实际上是 DFS 和 BFS 的混合。换句话说,它是深度受限的 DFS 版本。具体来说,它重复运行深度限制不断增加的 DFS 搜索,直到找到目标。尽管在每次迭代中它都运行一次 DFS 搜索,但它像 BFS 一样是最优的,并且通常可以在不探索所有节点的情况下找到目标,然而它不需要队列,并且使用的内存比 BFS 少得多。

让我们看看在搜索最短路径时,它们在时间和空间复杂性方面的不同表现。设D为树的最大深度,d为目标所在的最小深度。我们有d <= D。在二叉树b==2的情况下,设b为分支因子(每个节点的分裂数)。

时间复杂度

我们需要探索所有的节点。

BFS: O(b^d)我们只探索目标深度的节点。

IDDFS: O(b^d)我们只探索节点直到目标所在的深度。

空间复杂度

DFS: O(D)递归中最大的栈是D

BFS: O(b^d)队列中的节点数

IDDFS: O(d)递归中最大的栈是d

注意,如果我们需要访问每一个节点以便找到目标(d==D),所有三个算法将具有相同的时间复杂度。

在下一节中,我将向您展示如何实现这些算法来解决树搜索问题。

问题陈述

从根节点到值大于或等于 10 的节点的最短路径(深度)的长度是多少?

分析

最短路径是 1 → 2 →11,长度为 2。请注意,1 → 6 → 4 → 9 → 10 也是有效路径,但不是最短路径。

密码

为了解决这个问题,我们将每个树节点表示为一个Node对象,并将它们链接在一起以表示上面的树。

使用上面提到的复杂性注释,我们在这个问题上有了D=4, d=2, b=2

首先让我们看看如何使用 BFS 来解决它。从根节点开始,我们将当前深度的所有节点添加到队列中,并逐个处理它们。所谓“处理”,是指我们从队列中弹出节点,检查它是否是值,如果它不是目标,就将它的子节点添加到队列中。由于队列的先进先出属性,只有在我们处理完当前深度的节点之后,才会处理下一深度级别的节点。一旦我们找到目标(深度为 2 的节点 11),我们马上返回它。

另一方面, DFS 沿着每个分支递归搜索。在这种情况下,它按照根→左子树→右子树的顺序访问所有节点。这被称为前序遍历。还有另外两种类型的树遍历方法:Inorder 和 Postorder。查看这里了解更多详情。

IDDFS 的实现与 DFS 的实现非常相似。唯一的区别是它在每个深度限制停止 DFS 搜索。假设我们知道树的最大深度是 4,我们可以从深度限制 1 开始运行 DFS,并将其增加到 4 或直到找到目标。在这种情况下,它只需要运行 2 个 DFS(深度限制为 1 和 2)。

让我们运行这三个函数并比较输出。在输出中,我们看到所有三个算法都找到了最短的深度 2,但是访问的节点不同。

BFS 首先访问根节点 1,然后移动到第一深度级别的节点:6,2,然后是第二深度的节点:4,5,3,11。由于我们的目标 11 是在这里发现的,它不会继续访问其他深度。

DFS 以不同的顺序访问节点。从根节点 1 开始,它移动到根节点为 6 的左子树,并继续移动到根节点为 4 的左子树。一旦到达没有任何子节点的节点 7,它就返回上一级(回溯)并以节点 9 为根搜索第一个右边的子树。注意,所有子树的遍历都遵循根→左子树→右子树的顺序,所以我们访问(9,8,10)而不是(8,9,10)或(10,8,9)。为了找到最短路径的长度,我们必须访问所有的节点。即使节点 10 已经满足标准,我们也不能就此止步,因为我们不知道它是否是最短路径。

IDDFS 当深度阈值为 1 时,首先访问节点 1、6、2,然后它再次从根开始访问深度为 2 (1、6、4、5、2、3、11)的所有内容,并在此处停止,因为找到了目标。它访问与 BFS 相同的节点,但是顺序不同,因为每个内部 DFS 运行仍然遵循根→左子树→右子树的顺序。

DP(动态编程)

概念

当递归解决问题时,有时我们可能会多次访问相同的值,这增加了不必要的时间复杂度。解决这个问题的一种方法是缓存访问过的值以备将来使用。更好的是,如果我们将递归方法转换为重复使用之前的中间结果的迭代方法会怎么样?使用迭代法的优点是:1。将时间复杂度从指数降低到多项式;2.避免递归处理不当时经常发生的堆栈溢出。

动态规划的主要思想是存储子问题的中间结果,这样我们以后就不用重新计算了。让我们看一个例子来更好地理解 DP。

问题陈述

给定一个m x n网格,找出从左上角到右下角的唯一路径的数量。假设我们在任一时刻只能向下或向右移动。

分析

实现 DP 通常比提出解决方案容易得多,后者需要相当多的逆向思维。通常我们会通过从头开始迭代计算唯一路径的数量来解决这个问题。但是,请注意,网格中间的许多单元格被多次访问。这通常是某种缓存或 DP 的迹象。假设我们在单元格(1,6),它位于完成单元格的正上方。从当前单元格开始,只有一条路径可以到达终点单元格:向下。这同样适用于单元格(2,5)。现在,如果我们在单元格(1,5),有 2 条路径到达结束单元格:向右和向下;向右下走。这实际上是(1,6)和(2,5)处的值之和。从逻辑上讲,这是有意义的,因为在每个单元格中,我们可以向右或向下移动:如果我们向右移动,值(从这个单元格到结束单元格的唯一路径的数量)与其右边单元格的值相同,如果我们向下移动,同样的逻辑也适用。因此,我们可以得出,每个单元的值是其两个相邻单元(右和下)的值之和,这是 DP 中要解决的子问题。

DP 通常具有更好的时间复杂度,因为它避免了重新计算,但是这是以空间复杂度为代价的。根据问题的不同,有时我们可能需要存储所有子问题(所有单元/节点)的中间结果。

密码

一旦我们有了解决方案,实现就非常简单了。我们从右下角的结束单元格开始,它的基线值为 1,并迭代地向左上角的开始单元格移动。我们需要特别注意边界单元,因为它们只有一个相邻单元。一旦我们到达起始单元格,只需返回它的值。

空间和时间复杂度都是(m*n)

顺便说一下,我们在这里描述的 DP 方法使用了自底向上(制表)的方法。还有另一种自上而下的方法。更多详情,请参考本帖。

迪杰斯特拉算法

概念

Dijkstra 算法用于查找图中节点之间的最短路径。与 BFS 算法和 DFS 算法只在无权重图中寻找最短路径不同,Dijkstra 算法可以用于有权重图和无权重图。在下一节中,我将解释如何实现算法来解决最短路径问题。

问题陈述

在一个充满非负数的m x n网格上,我们可以在任何时间点向下或向右移动。目标是找到一条从左上到右下的路径,使路径上所有数字的总和最小。

分析

这个问题本质上是一个加权无环图问题。如果你不熟悉 graph,这里是定义。Dijkstra 的算法非常适合在这样的加权图中寻找最短路径。

下面我描述 Dijkstra 的算法是如何实现来解决这个具体问题的。关于更一般和更高层次的描述,请参考这个维基页面。

  1. 创建一个优先级队列(min heap) heap来跟踪被访问的节点(本例中是单元)。每个节点到起始单元的距离是动态计算的,并用作优先级。heap最初只包含起始节点,其单元值为优先级。
  2. 创建一个集合seen来跟踪被访问并被识别为最短路径轨迹一部分的节点。seen最初是空的。
  3. heap不为空时:
    1 ) 得到一个到起始节点距离最短的节点(优先级最高),将其添加到seen 中作为最短路径树的一部分。如果这是终点,我们返回距离。否则,我们进行下一步。
    2 ) 如果这个节点有邻居节点(右下),将每个邻居及其距离(邻居的单元格值+当前节点的距离)加到heap

本质上,heap存储所有节点到起始单元的距离(我将简称为距离)。如果被几个不同的路径访问,一个节点可以在heap中出现多次。相反,seen只跟踪距离最短的节点。例如,如果经由路径(0,0) → (1,0) → (1,1)访问,则中心节点(1,1)的距离可以是 7,或者如果经由(0,0) → (0,1) → (1,1)访问,则可以是 9。heap将存储(7,1,1)和(9,1,1),但是当我们从heap弹出(9,1,1)时,因为(1,1)已经在seen中了,我们只是传递这个值。这就是我们如何确保我们返回的最终距离位于从起始像元开始的最短路径轨迹上。

密码

一颗*(一颗星星)

概念

A*类似于 Dijkstra 算法,因为它也用于查找最短路径,但它比 Dijkstra 算法执行得更好,因为它减少了非最佳访问并更快地找到最佳路径。

回想一下,在 Dijkstra 的算法中,我们试图找到从起点到当前点的最小距离,这意味着我们倾向于接近起点的顶点。但是,我们还需要考虑从目标到当前点的距离。因为靠近起点的顶点不一定具有到目标的最短距离,所以我们最终在找到最优顶点之前访问了许多次优顶点。最佳路径上的顶点到两端的距离应该最短。然而,我们不知道从当前点到目标的距离,除非我们进行彻底的搜索,这违背了我们的目的。如果我们用某种试探法来近似它呢?这是 A*的主要思想。

假设我们有一个启发式的从当前点s到目标的距离h(s),我们知道从起点到当前点g(s)的准确距离。为了确保试探法是正确的,它必须是满足以下条件的真实距离的最优低估(一致和容许):

  1. g(s') + h(s') — h(s) >= 0
  2. h(s_target) == 0

(s'是下一个潜在点,s_target是目标点)

找到一个好的启发更多的是一个建模问题,但通常我们可以通过放松约束(消除障碍等)来获得一个好的启发。)并使用距离度量作为近似值。让我们看看如何做到这一点。

问题陈述

假设我们有一个n x n网格(用二进制矩阵表示)。我们可以在网格内的 8 个方向移动(上、下、左、右和 4 条对角线)。我们希望找到从左上角单元格到右下角单元格的所有值都为 0 的最短路径的长度。

例如:

分析

又一个最短路径问题,耶!现在我们知道如何用 Dijkstra 的算法解决它,我们需要做的就是找到一个好的启发式算法并插入。由于在所有 8 个方向上的移动都是允许的,对角距离将是这个问题的一个好的距离度量。注意,通过使用对角线距离,我们自动忽略了单元格的值约束(1 或 0 ),从而放松了约束。

密码

首先,一个助手函数返回所有 8 个邻居。

在函数a_star中,我们有一个实现对角线距离的内部函数get_heuristic。其逻辑与我们在上一节中讨论的 Dijkstra 算法非常相似,唯一的区别是优先级是启发式值和距离的总和。我们还做了一个小的性能改进:缓存被访问单元的最高优先级(启发式值和距离的最小和)。这样,已经被访问过的并且先前具有较高优先级的单元将不会被再次添加到优先级队列中。

为了比较,我们也实现了 Dijkstra 的算法来解决这个问题。我在这里就不赘述了,因为我们在上一节已经谈过了。

让我们打印出结果和两个函数的轨迹。

两种算法都能够找到最短距离 11。然而,A*比 Dijkstra 算法更快地找到目标,因为它访问的次优单元更少,如打印输出所示。

结论

在本文中,我们学习了几种有用的搜索算法,并从头开始实现了它们。我在开始时提到了一些搜索问题的用例,但事实上在今天的现实世界中,大多数这些问题都是通过强化学习(RL)来解决的。如果我们有一个人工智能的智能测量,我们会说搜索问题与 RL 相比处于较低的智能水平,因为:

1)仅当我们有已知的世界模型时才应用搜索,这意味着我们必须找出通常非常困难的模型。另一方面,RL 对已知(基于模型)和未知模型(无模型)都有效;

2)搜索只能在小的问题空间上工作,而 RL 可以解决具有大的状态和动作空间的问题。

如果你有兴趣了解更多关于强化学习的基础知识,请查看我的文章强化学习算法的结构概述。我也有关于实现两个流行的 RL 算法的教程:和策略梯度。

我希望你喜欢这篇文章。😃

参考

https://leetcode.com/problems/shortest-path-in-binary-matrix/ https://leetcode.com/problems/minimum-path-sum/ https://leetcode.com/problems/unique-paths/

搜索数据、趋势和分析:抓住市场和消费者的脉搏(不需要 cookies)

原文:https://towardsdatascience.com/search-data-trends-analytics-catching-the-pulse-of-a-market-and-its-consumers-no-cookies-747d6d458530?source=collection_archive---------18-----------------------

行业笔记

照片由安娜·涅克拉舍维奇从派克斯拍摄

搜索数据的好处不仅限于以 SEO 为中心的关键词研究。这也是市场和产品研究的宝贵资源。事实上,它甚至可以集成到所有的研究类型和情报分析中,在这些研究类型和情报分析中,倾听和解码消费者的声音起着重要的作用。

我将通过一个专注于分析行业的例子来解释这是如何实现的。

我还将讨论为什么搜索数据可以作为战略层面的关键洞察力来源的 6 个原因。

一些背景

这项研究的灵感来自 Krista Seiden 在 Xoogler.co 举行的 2020 演讲,该演讲探讨了分析行业的主要趋势,特别关注各种技术堆栈。Krista 没有使用搜索数据,相反,她的讲话是基于她通过自己的研究收集的数据。她调查了在分析行业工作的大量个人样本。

理解这些趋势是我作为顾问工作的一部分,所以我对它们有天然的兴趣。鉴于我在分析行业的工作以及我对探索该领域未来发展的兴趣,我倾向于通过遵循类似的数据驱动方法来为这一工作领域做出贡献。

为什么要搜索数据

对我来说,要达到 Krista 研究中的分析专家的数量几乎是不可能的,所以我决定使用公开的搜索数据,看看是否会出现任何值得注意的趋势。

在那个时候,我实际上已经在考虑这种类型的分析的潜在好处——但出于不同的原因。新冠肺炎正处于巅峰时期,疫情正强烈影响着消费者行为,这一点变得越来越明显。想办法更深入地了解消费者如何在网上搜索产品,以及正在出现哪些新的搜索模式,可能会提供有用的见解。

此外,随着跟踪用户行为的障碍越来越多,任何反映消费者声音的额外数据源都变得非常有用。我假设搜索数据也可以为原始分析趋势研究提供另一个补充角度。

因此,我开始收集搜索查询列表,并分析关键词搜索量。

分析趋势研究

方法

我创建了一个用于分析的关键词世界并对其进行数据挖掘,寻找增长最快的趋势。

部署的方法如下。

创建一个包含 30,000 个包含术语“分析”的关键词的关键词世界,使用多个数据源作为关键词建议。

从谷歌检索四年期间的搜索量。

应用统计分析来发现关键趋势。

要了解关于数据集的更多信息并下载它,您可以访问本文附带的 analytics trends web 应用程序的“关于”部分(以下部分提供了链接)

基于搜索数据的分析行业的 3 个趋势

从这一分析中自然出现了许多值得注意的发现,这很好地表明了搜索分析可以产生的洞察力。

下面我就简单讨论其中的三个。

如果你有兴趣自己探索这些趋势,可以看看网络应用

https://analytic strends . app

  1. 发展最快的分析技术和供应商

这些是主流分析技术中增长最快的分析工具、技术和供应商。一些工具,如爱因斯坦和 Spotify analytics,在过去四年中以非常一致的方式发展。

其他的,像谷歌分析 4,遵循曲棍球棒曲线。通常值得关注的是各种主题的特定搜索行为的原因——例如,在 Google Analytics 4 中,搜索兴趣的激增是新产品发布的结果。另一方面,Fishtown Analytics(另一个接近迅速崛起的术语)发布了一个受欢迎的开源工具,这有助于它的快速增长。

2。分析领域的后起之秀

只关注流量最大的术语和主题是很容易的。然而,有价值的见解可以在关键词世界的任何地方找到。一些分析软件供应商——在某些情况下,我们可以认为他们是利基参与者——已经非常稳定地增长,尽管有些确实是从零开始的。

有趣的是,图中六个供应商中有三个属于“隐私优先”分析类别,这似乎是对过去几年中所有隐私立法和讨论的反应。这是一个强烈的信号,表明新的隐私格局可以为分析领域的新参与者创造重大机遇。

Matomo、Fathom 和 Simple Analytics 表现出快速和持续的增长,同时对隐私保护及其承诺直言不讳。

3。分析工作和培训领域增长最快的术语

最后一个趋势说明了对分析相关角色的需求不断增长,如分析工程师和分析主管。对专业角色的需求之后是对培训和课程的强烈需求,这些培训和课程使专业人士能够胜任这些角色。对分析工作和教育的高需求本身并不令人惊讶,但增长速度令人印象深刻——看看对分析工程师和分析训练营的搜索兴趣的增加。

搜索数据:从战术到战略

我使用搜索和关键词数据已经有很长一段时间了,主要是从战术的角度进行关键词竞价、ROAS 计算、关键词排名跟踪和搜索引擎优化的关键词研究。

在进行这一分析的过程中,我发现在做出涉及产品、市场和消费者的商业决策时,搜索数据可以发挥战略性的作用。上面的分析只是触及了表面,当一个“关键词世界”中的术语开始基于它们的语义、词典或其他特定于案例的属性进行分组或聚类时,还有更多要探索的。自从分析趋势研究开始,我就在工作中优先考虑搜索数据的使用和分析。下面,你会发现一些关于如何在你的组织中战略性地使用搜索数据的想法。

在组织中战略性地使用搜索数据的 6 种方法

由艾萨克·史密斯在 Unsplash 上拍摄的照片

1。用它来观察一个行业中正在发生变化的更大图景。本文中的例子来自分析行业,但也可能是任何其他行业或市场。搜索数据分析有助于确定对产品、市场或主题的搜索兴趣是上升还是下降,同时也有助于企业了解变化的具体模式。例如,与其他缓慢但肯定会随着时间的推移而增长的术语相比,一些术语可能会迅速上升或下降。在决定是长期投资新产品或市场还是利用短期机会时,理解这些模式是至关重要的,例如,营销活动暂时专注于搜索兴趣仅在短时间内激增的主题。

2。分析搜索数据以发现新的商业机会。例如,检测特定地区的高需求可能是考察进入新市场潜力的好理由。如果你已经在考虑扩张,一旦你知道有一个有保证的需求水平,它可以帮助你减轻风险。根据搜索需求计算相关市场的潜在规模时,搜索数据也很有价值,并提供关于竞争水平和搜索市场份额的估计。通过研究和结合这些趋势,您可以在竞争对手之前捕捉高潜力的业务机会,提出新产品想法或尽早发现机会,使您的组织获得先发优势。

3。 搜索数据分析也可以关注现有市场和产品。它能使公司理解消费者用来寻找或描述他们特定产品和服务的语言。这对创业公司尤其有利,因为它比其他初级市场研究形式需要的投资少得多,例如,包括焦点小组、人口样本调查等。此外,它可以在更短的时间内执行和交付。

如果你试图说服人们做某事或购买某物,在我看来,你应该使用他们的语言,他们每天使用的语言,他们思考的语言。

—大卫·奥格威

4。了解需求构成,预测未来需求。这对电子商务企业尤其有用。了解某些产品的季节性以及总体需求水平和需求来源可以在许多方面提供帮助:从更好地规划物流和管理库存,到更好地控制营销活动的时间和强度,以及在这些活动中优先考虑产品促销。

这六个列表中的其余两个说明了搜索数据在 2021 年及以后的重要性。

5。了解新冠肺炎对消费者行为的影响。疫情极大地影响了我们的生活,这无疑改变了我们研究和购买产品和服务的方式。

许多这些变化预计将继续下去。搜索数据分析的另一个用例是在这些变化发生时检测它们,并更深入地理解这些新趋势背后的东西。事实上,在许多情况下,这些变化激励企业和企业家创造新的产品和服务,或寻找新的方式向消费者提供现有的产品和服务。

6。平衡隐私相关限制对消费者旅程跟踪的影响。隐私立法和浏览器限制使得追踪消费者行为变得越来越困难。这促使人们充分利用搜索数据等聚合和匿名数据,以洞察消费者在顾客旅程的各个阶段最关心的问题。这是了解消费者的关键信息,有助于你的企业根据这些信息做出明智的选择——甚至在消费者第一次登陆你的网站之前。

结束语

来自佩克斯的卢卡斯的照片

我们已经看到了搜索数据的分析如何成为一个组织的战略洞察力的来源。

简而言之,这是企业感受消费者脉搏的直接方式。该分析适用于行业、市场或产品层面,其结果支持组织内许多层面的决策,包括最重要的战略层面。

与其他形式的市场和产品研究相比,处理搜索数据不需要大量的预算投入,因此无论规模大小,公司都很容易获得。

这些数据经过汇总和匿名处理,可以从 Google (Google Trends 或 Google Ads platform)或其他提供商(例如提供这类数据的 SEO 平台)获得一些来源。

最后,现在可能是开始更系统地分析搜索数据的正确时机,以便更好地理解消费者行为最近的所有彻底转变,并重新理解消费者及其旅程,不管当前或即将出台的隐私法规如何。

你遇到过其他有趣的搜索数据分析用例吗?请在下面的评论中告诉我!

亚历克斯·帕帕乔治

我是营销分析和数据科学领域的独立顾问,帮助以转化为导向的数字企业做出明智的营销决策。我通过 Twitter LinkedIn在我的 博客 上分享我关于数字、营销和数据分析的故事。

https://www.linkedin.com/in/alexandrospapageorgiou/ [## Alex Papageorgiou -营销分析顾问|前谷歌| LinkedIn

www.linkedin.com](https://www.linkedin.com/in/alexandrospapageorgiou/)

我在 Medium 上发表的故事,你可能也会喜欢:

https://medium.com/innovation-machine/google-analytics-kaggle-competition-highlights-eaa046737ac4 https://medium.com/innovation-machine/choosing-between-r-and-python-a-digital-analysts-guide-b7103f80aa4e

纪娜和伯特的金融问答——第二部分

原文:https://towardsdatascience.com/search-engine-evaluation-in-jina-856616eb7f6f?source=collection_archive---------32-----------------------

金融中的 NLP

关于如何使用纪娜评估和改进您的财务问答搜索结果的教程

(图片由作者提供)

第一部分 —学习如何使用神经搜索框架,https://github.com/jina-ai/jina**,构建一个* 金融问答(QA)搜索应用 FiQA数据集,py torch*

第 2 部分——通过纪娜了解如何评估和改进您的财务 QA 搜索结果

在之前的教程中,我们学习了如何与纪娜和伯特一起构建一个生产就绪的金融问答搜索应用程序。为了改进我们的应用程序并检索有意义的答案,评估搜索结果对于调整系统参数至关重要。例如,它可以帮助我们决定为编码器选择哪个预训练模型、最大序列长度以及我们想要使用的排序器类型。

插图来自 unDraw

回想一下,纪娜为我们提供了创建搜索应用程序的基础。因此,我们可以使用纪娜的评估器,一种执行器,而不是自己实现评估度量。

(图片来自纪娜艾)

到目前为止,我们已经看到了一些执行器:编码器、索引器和排序器。这些执行器中的每一个都负责其相应功能的逻辑。顾名思义,评估器将包含我们评估指标的逻辑。我们还学习了如何设计索引和查询流,它们是索引和搜索答案段落的管道。

为了评估搜索结果,我们需要创建一个评估管道,即评估流,供我们在财务 QA 搜索应用程序中使用。

辅导的

在本教程中,我们将学习如何在我们的财务 QA 系统中添加评估管道,方法是设计一个流程来评估具有精确度和平均倒数排名 (MRR)的搜索结果。

我们将使用 FinBERT-QA 评估重新排序前后的搜索结果。以下是评估流程的概述:

图 1:评估流程概述(图片由作者提供)

设置

如果你来自之前的教程,你需要对app.pyFinBertQARanker/__init__.pyFinBertQARanker/tests/test_finbertqaranker.py做一些小的改动。琼·丰塔纳拉斯·马丁内兹和我在排名器中增加了一些辅助函数和批处理来帮助加快进程。

我没有指出这些变化,而是制作了一个新模板来简化工作流程,并向已经熟悉纪娜的人展示如何实施评估模式。

克隆项目模板:

**git clone [https://github.com/yuanbit/jina-financial-qa-evaluator-template.git](https://github.com/yuanbit/jina-financial-qa-evaluator-template.git)**

确保已经安装了需求,并且已经下载了数据和模型。

你可以在这里 找到本教程的最终代码

让我们一步一步地完成评估流程。

第一步。定义我们的测试集数据

我们的工作目录将是jina-financial-qa-evaluator-template/。在dataset/文件夹中你应该有以下文件:

图 2:数据集结构(图片由作者提供)

对于本教程,我们需要:

sample_test_set.pickle:包含 50 个问题和基本事实答案的样本测试集

qid_to_text.pickle:将问题 id 映射到问题文本的字典

如果你想使用来自 FinBERT-QA 、test_set.pickle的完整测试集,其中有 333 个问题和地面真相答案,你可以简单地改变路径。

我们将在本教程中使用的测试集是一个 pickle 文件,sample_test_set.pickle。这是一个形式为[[question id, [ground truth answer ids]]]的列表列表,其中每个元素包含问题 id 和一个基本事实答案 id 列表。这是测试集的一部分:

**[[14, [398960]],[458, [263485, 218858]],[502, [498631, 549435, 181678]],[712, [212810, 580479, 527433, 28356, 97582, 129965, 273307]],...]**

接下来,类似于定义用于索引答案段落的文档,我们将创建两个包含问题数据和基本事实答案的文档。

图 3:评估流程——步骤 1 定义查询和基本事实文档(图片由作者提供)

回想一下在我们的索引流中,当我们在index_generator函数中定义数据时,我们在文档中包含了答案段落 id(docid)。因此,在建立索引后,这些答案 id 存储在索引中,它们非常重要,因为它们是查询时搜索结果的一部分。因此,我们只需要为每个查询定义带有基本事实答案 id 的基本事实文档,并将这些答案 id 与匹配的答案 id 进行比较。

让我们在load_pickle函数下添加一个 Python 生成器来定义我们的评估测试集。对于每个文档,我们将把相应的问题从测试集映射到实际的文本。

第二步。将问题编码

类似于查询流,我们将把两个文档从pods/encode.yml传递到 Encoder Pod。驱动程序会将问题文本传递给编码器,将其转换为嵌入内容,同一驱动程序会将该嵌入内容添加到查询文档中。这次唯一的不同是,我们将两个文档传递到编码器盒中,而基本事实文档是不可变的,并且在整个流程中保持不变。

flows/中,我们创建一个名为evaluate.yml的文件来配置我们的评估流程,并添加编码器盒,如下所示:

编码器的输出将包含嵌入了问题的查询文档,而基本事实文档保持不变,如图 4 所示。

图 4:评估流程——步骤 2 的输出:问题嵌入被添加到查询文档中,而基本事实文档保持不变。(图片由作者提供)

第三步。搜索索引

接下来,来自pods/doc.yml的索引器 Pod 将搜索具有最相似嵌入的答案,索引器的驱动程序将向查询文档添加前 k 个答案匹配的列表。地面真相文件保持不变。

让我们将doc_indexer添加到flows/evaluate.yml中,如下所示:

索引器的输出将包含带有答案匹配及其相应信息的查询文档和基本事实文档。

图 5:评估流程——步骤 3 的输出:答案匹配被添加到查询文档中,而基本事实文档保持不变。(图片由作者提供)

第四步。估价

由于我在开始时提到我们将在重新排序之前和之后评估搜索结果,您可能认为现在我们将添加以下序列:

  1. 匹配结果的评估者
  2. 出身行伍的军官
  3. 重新排序结果的评估者

然而,由于评估服务于改进我们搜索系统的结果,它不是最终应用的实际组成部分。你可以把它看作是一个工具,为我们提供关于系统的哪个部分需要改进的信息。

(插图来自未绘制的

我们的目标是允许检查管道的任何部分,并使我们能够在流程中的任意位置进行评估,我们可以使用纪娜流程 API 的inspect功能来评估箱连接到主管道,以便评估不会阻止消息发送到管道的其他组件

例如,如果没有inspect模式,我们就会有上面提到的顺序设计。在inspect模式下,从索引器中检索到答案匹配后,文档将被并行发送给评估者和分级者。因此,分级器不必在输出重新分级的答案匹配之前等待初始答案匹配被评估!

在我们的 QA 系统中,这种设计的好处是评估者可以在不阻碍流程进度的情况下执行评估,因为它独立于管道的其他组件。您可以将评估器视为与流程并行运行的辅助任务。因此,我们可以在对流的性能影响最小的情况下进行评估。

你可以参考这篇文章来了解更多关于评估模式的设计和inspect特性。

让我们仔细看看流程的评估部分:

图 6:更仔细地观察流程的评估部分(图片由作者提供)

在图 6 中,我们可以看到负责评估答案匹配的评估者evaluate_matching Pod 与排名者和负责评估重新排名的答案匹配的评估者evaluate_ranking Pod 并行工作。

gather_inspect用于累加每个查询的评估结果。此外,在排序器之前和之后示出的辅助容器是允许评估流以相同的方式具有容器的连接的构造,就好像查询流没有评估器一样,因此检索和重新排序答案匹配的性能将仅受到最小程度的影响

以前,我们使用来自纪娜中心的编码器和索引器,这是一个通过容器映像托管执行器的开放注册中心。我们可以再次利用纪娜枢纽,只需使用已经可用的精度和倒数等级吊舱

现在我们来看看匹配和排名评估者:

匹配评估器

在索引器窗格输出带有匹配项的查询文档后,一个工作流将涉及匹配评估器,该评估器负责计算答案匹配项的精度和倒数排名(无需重新排名)。

匹配评估器 Pod 的驱动程序解释查询和基本事实文档,并将答案匹配 id 和每个查询所需的基本事实答案 id 传递给匹配评估器,匹配评估器计算精度和倒数等级值,如图 7 所示。

图 7:匹配评估器 Pod 的驱动程序将答案匹配 id 和所需答案 id 传递给匹配评估器。(图片由作者提供)

现在,让我们创建匹配的评估器。在pods/文件夹中,创建一个名为evaluate_matching.yml的文件。我们将在文件中添加以下内容:

PrecisionEvaluatorReciprocalRankEvaluator是来自纪娜中心的精度和倒数排名的评估者。我们指定eval_at: 10来评估前 10 个答案匹配。我们还指出了每个组件的name,我们将在后面的评估流程中使用它。

从上一个教程中你可能会想为什么我们不需要在pods/encode.ymlpods/doc.yml中指定驱动程序,因为 pod 中的 pea 需要这两个组件。这是因为这两个 pod 的驱动程序是常用的,并且已经默认包含在内。但是,由于我们想要使用我们从纪娜中心选择的两个评估器(精度和倒数排名),我们需要为每个评估器指定驱动程序,即RankEvaluateDriver

接下来,让我们将此 Pod 添加到评估流程中。在flows/evaluate.yml中添加如下evaluate_matching:

这里我们指出method: inspect,因为我们使用来自 Flow API 的inspect特性来检查我们的应用程序在流程中间的性能。

匹配评估器的驱动程序会将答案匹配的评估结果添加到每个查询的查询文档中,如图 8 所示。

图 8:答案匹配的评估结果被添加到查询文档中(图片由作者提供)

干得好!我们已经成功实现了第一个评估器。接下来让我们看看如何评估重新排序的搜索结果。

排名评估

索引器 Pod 之后的另一个工作流涉及排名评估器,它负责使用 FinBERT-QA 计算重新排名的答案匹配的精确度和倒数排名。等级评估器 Pod 的构造类似于匹配评估器 Pod,唯一的区别是我们将重新排序的匹配 id 传递给等级评估器,如图 9 所示。

图 9:等级评估器 Pod 的驱动程序将重新排序的答案匹配 id 和所需的答案 id 传递给等级评估器。(图片由作者提供)

让我们创建我们的排名评估。在文件夹pods/中,创建一个名为evaluate_ranking.yml的文件。我们将在文件中添加以下内容:

请注意,除了命名约定之外,这几乎与evaluate_matching.yml相同。

在之前的教程中,我们学习了如何构建一个自定义排名器。我们需要为这个 Ranker 构建 docker 映像,以便在我们的流程中使用它作为 Pod。

注意:因为添加了批处理,所以即使您在之前的教程中已经构建了这个映像,您也需要重新构建它。

确保您安装了纪娜中心扩展:

**pip install “jina[hub]”**

在工作目录中键入:

**jina hub build FinBertQARanker/ — pull — test-uses — timeout-ready 60000**

您应该会收到一条消息,表明您已经成功构建了带有标记名的图像。取决于当前的纪娜版本将标签用作 Pod 时,确保相应地更改标签名称。

接下来,让我们将 Ranker 和 Evaluate Ranking Pod 添加到flows/evaluate.yml中的评估流程中:

索引器 Pod 将把带有答案匹配和基本事实 id 的查询文档传递给包含 FinBERT-QA 的排序器。然后,Ranker Pod 将输出带有重新排序的答案匹配 id 的查询文档和基本事实文档,这两个文档都将被传递给排名评估器。图 10 所示的输出将与匹配评估器相同,评估值的差异是根据重新排序的答案匹配计算的。

图 10:在排序器和等级评估器之后,从重新排序的答案匹配中计算出的评估值将被添加到查询文档中。(图片由作者提供)

干得好!我们刚刚完成了评估流程的设计。接下来让我们看看如何在我们的搜索应用程序中使用它。

第四步。获取评估结果

类似于index函数,在app.py中,让我们在evaluate_generator后添加一个evaluate函数,它将从flows/evaluate.yml加载评估流程,并从evaluate_generator向流程传递输入查询和基础事实文档。我们设置我们的top-k=10来评估精度@10 和倒数排名@10。

因为我们想要计算测试集中所有查询的平均精度@10 和均值-倒数-等级@10,所以我们将编写一个函数print_average_evaluations来计算评估值的平均值

最终的评估值将存储在查询文档中,像print_resp函数一样,我们可以编写一个函数来打印评估响应,方法是循环遍历我们的查询文档d.evaluations中的评估,并打印出每个评估者的值:

万岁!🎉🎉🎉我们刚刚在我们的金融 QA 搜索引擎中实现了一个评估模式!我们现在可以运行:

**python app.py evaluate**

由于本教程是出于教育目的,我们只索引了部分答案段落,并使用了一个小样本测试集。因此,结果不能与来自芬伯特-QA 的结果相比较。请随意索引整个答案集,并在完整的测试集上进行评估,并分享您的结果!

您将看到正在评估和打印的单个问题。以下是问题 id 为 1281 的示例。

 **Evaluations for QID:1282 Matching-Precision@10: 0.10000000149011612 Matching-ReciprocalRank@10: 1.0 Ranking-Precision@10: 0.10000000149011612 Ranking-ReciprocalRank@10: 0.125**

最后,您将看到平均评估结果:

**Average Evaluation ResultsMatching-Precision@10: 0.056000000834465026Matching-ReciprocalRank@10: 0.225Ranking-Precision@10: 0.056000000834465026Ranking-ReciprocalRank@10: 0.118555556088686**

摘要

在本教程中,我介绍了纪娜的评估功能,并演示了如何为我们的金融 QA 搜索应用程序设计评估流程。我们学习了如何使用inspect模式来创建我们的评估单元,并通过最小化评估对流水线性能的影响来使我们的应用受益。

请务必查看纪娜的 Github 页面,了解更多信息,并开始构建自己的深度学习搜索应用程序!

社区

  • Slack channel —开发者讨论纪娜的交流平台
  • 社区简讯 —订阅纪娜的最新更新、发布和活动新闻
  • LinkedIn —了解纪娜爱作为一家公司,寻找工作机会
  • Twitter —使用标签#JinaSearch关注纪娜·艾并与之互动
  • 公司——了解更多关于纪娜 AI 和他们对开源的承诺!

来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

在大型神经网络中搜索彩票

原文:https://towardsdatascience.com/searching-for-lottery-tickets-inside-large-neural-networks-e39464ed6470?source=collection_archive---------19-----------------------

W 帽子如果每一个现代深层神经网络背后都藏着一张“彩票”呢?一个小得多的子网络,经过训练后,它将获得与整个经过训练的网络相同甚至更好的性能

(图片作者)

在 2019 年,Frankle 和 Carbin[1]的一篇论文提出了一个非常有趣的猜想,基于对当前大型神经网络的实验观察,似乎可以抓住同一网络的一小部分,并训练它以达到不仅相同的准确性,有时甚至比原始神经网络更好的结果。

“彩票假设:一个随机初始化的密集神经网络包含一个子网络,该子网络被初始化,使得在隔离训练时,它可以在最多相同次数的迭代训练后匹配原始网络的测试精度。”——j·弗兰克尔和 m·卡宾

尽管这个猜想看起来令人难以置信,但这仅仅是个开始。同年,Ramanujan 等人[2]发表了一篇新论文,定义了一个更强的猜想,并实际展示了一个找到这个隐藏子网络的算法。研究小组开始意识到,他们工作的一些现代超大网络不仅包含“彩票”,而且实际上子网本身已经具有与其他训练过的网络相同的准确性,只不过是随机初始化,不涉及任何训练

“隐藏在随机加权的宽 ResNet-50 [32]中,我们发现一个子网(具有随机权重)小于在 ImageNet [4]上训练的 ResNet-34 [9]的性能,但与之匹配。这些“未经训练的子网”不仅存在,而且我们提供了一种算法来有效地找到它们。”-Ramanujan 等人

因此,更强的猜想是,给定一个足够大的神经网络,它一定包含一个子网络,即使没有训练,它也具有与原始训练的神经网络相当的准确性。

现在让我们在这里停下来一秒钟,惊叹这意味着纯粹的组合力量。想象一下,我们试图训练一个神经网络,例如作为一个简单的分类器,以区分狗和猫的图像,然后猜想说,给定一个足够大的神经网络,用随机权重初始化,你想要的神经网络一定很有可能在那里的某个地方,并且已经通过初始化单独训练过。

现在人们可能会问两个主要问题…

  1. “足够大”实际上意味着什么?
  2. 如何找到这个新的更强大的子网络?

对于第一个问题,我们必须回到 2020 年初,当时马拉奇等人[3]的一篇论文给出了更强猜想的完整证明,他们已经表明,如果你有一个你想要近似的网络, 通过创建一个新网络,其大小是目标网络大小的多项式(这意味着新网络的大小由某个多项式方程定义,该方程取决于目标网络的层数和神经元数以及您希望您的近似有多好),然后找到适当的子网络,您可以尽可能接近目标网络。

为了能够证明这个多项式界限,必须假设对输入和权重的规范有很强的限制,这个限制似乎是一个问题,如果去掉这个限制,那么这个界限将随着层数的增加而呈指数增长。幸运的是,故事还没有结束,在 2020 年底,一篇新的论文出现了,这一次是来自 DeepMind 的 L.Orseau 等人[4]发表的。

在本文中,他们总结了 Malach 等人[3]所做的证明的结论,以消除上述假设,并使用利用神经网络可组合性的新见解,他们能够显著提高界限:

“超参数化网络只需要一个对数因子(除了深度之外的所有变量)目标子网的单位权重的神经元数量。”-L.Orseau 等人

现在我们准备尝试解决第二个问题…这一次我们不得不回到 1990 年,当时 Le Cun 等人[8]发表了一篇名为“最佳大脑损伤”(OBD)的论文。其背后的主要思想是能够通过删除网络的不重要权重来调整神经网络的大小,使其变得更小,这允许更大的泛化,需要更少的训练样本和速度提高(这种技术被称为“修剪”)

“OBD 的基本思想是,有可能采用一个完全合理的网络,删除一半(或更多)的权重,最终得到一个同样有效,甚至更好的网络。”——乐存等

让我们试着理解 OBD 背后的思想。假设您想要删除参数以使网络变得更小,那么您会想要删除特定的参数以使其对训练误差的影响最小,从而保持当前的网络精度。一种方法是使用目标函数(希望最大化或最小化的函数),因此原则上可以删除一个参数并查看目标函数的变化量…..然而,这种技术太复杂,实际上无法执行,因为它需要临时删除每个参数,然后重新评估目标函数(我们可能正在处理具有数百万个参数的非常大的网络!!)

因此,这个问题的解决方案是通过逼近目标函数,然后通过二阶导数和迭代方法计算每个参数产生的变化(有关二阶导数修剪方法的更多信息,请参阅[6])

从那时起,这种从神经网络中消除不必要的权重或“修剪”的想法已经得到了很大的改进,尽管缺少理论保证,但实验表明,人们可以在不损失准确性的情况下修剪超过 90%的网络。

一些修剪技巧(图片由作者提供)

现在有人可能会问,如果我们所做的一切都是为了缩小网络的规模,那么为什么不使用一个更小的网络呢?

现代实验似乎指出,较大的过参数化网络往往比小型网络具有更好的优势,例如 Belkin 等人[7]。定义了“双重下降”风险曲线,以解释为什么现代机器学习方法在新数据上获得非常准确的预测,同时具有非常低或零的训练风险,因此通常会返回高度过度拟合的系统,现在却给出非常好的预测

“虽然在插值阈值获得的学习预测值通常具有高风险,但我们表明,增加函数类容量超过该点会导致风险降低,通常会低于在“经典”机制中最佳点获得的风险。”——贝尔金等人。

Ma 等人[5]给出了另一个有趣的例子,他们使用凸分析来解释现代大规模机器学习架构中随机梯度下降(SGD)的快速收敛现象,产生了前面提到的经验损失归零(零训练风险)。

SGD 是大多数现代神经网络算法的基础,对理解这种现象非常重要,也正是这项工作促使 Belkin 等人研究双下降风险曲线。

结论

“那么很自然会问:梯度下降最重要的任务可能是修剪吗?”- L.Orseau 等人

随机梯度下降只是一种通过权重实现的修剪机制吗?这个猜想和界限的证明似乎表明,网络必须包含比以前显示的更多的“彩票”,学习和修剪之间的理论和联系仍有问题,Malach 等人的工作[3]表明,令人惊讶的是,给定一个随机网络,修剪在权重优化算法方面实现了竞争结果。

“人们甚至可以推测,
剪枝的效果是达到全局最优的附近,之后梯度下降可以执行局部拟凸优化。” - L.Orseau 等人

彩票假说已经得到证明,在理解神经网络的理论能力和局限性的这个充满活力的领域中,许多工具仍有待建立,仍有许多开放的问题,修剪的未来可能仍有待观察。

参考文献

[1]J .弗兰克尔和 m .卡宾。彩票假说:寻找稀疏的、可训练的神经网络。(2019)ICLR。

[2]拉马努扬、沃特斯曼、肯巴维、法尔哈迪和拉斯特加里。随机加权的神经网络中隐藏着什么? (2019) arXiv 预印本 arXiv:1911.13299。

[3]E .马拉奇、g .耶胡代、s .沙莱夫-施瓦兹和 o .沙米尔。证明彩票假说:修剪是你需要的全部。(2020) arXiv 预印本 arXiv:2002.00585。出现在 2020 年的 ICML。

[4]L .奥尔索,M .哈特,o .里瓦斯帕拉塔。对数修剪就是你需要的全部。(2020)arXiv 预印本:2006.12156

[5]S .马、r .巴西利和 m .贝尔金。插值的力量:理解 sgd 在现代过参数化学习中的有效性。(2018)在机器学习国际会议上,第 3325–3334 页。

[6] B. Hassibi 和 D. G. Stork。网络修剪的二阶导数:最佳脑外科医生。神经信息处理系统进展。(1993 年)第 164-171 页。

[7]M .贝尔金、d .徐、s .马和 s .曼达尔调和现代机器学习实践和偏差-方差权衡。(2019)arXiv 预印本 arXiv:1812.11118

[8]扬·勒昆、易小轩·登克和萨拉·索拉最佳脑损伤。神经信息处理系统进展。(1990 年)第 598-605 页。

使用 Python 搜索推文

原文:https://towardsdatascience.com/searching-for-tweets-with-python-f659144b225f?source=collection_archive---------6-----------------------

关于如何使用 Python 中的 Twitter API 的快速演练

Joshua Hoehne 在 Unsplash 上拍摄的照片

你想知道如何用 Python 搜索最近的推文吗?

显然,有一百万种方法和包,比如: python-twitter 、 search-tweets-python 、snscreage或 tweepy 、但是找到一个直接简单的教程,简单地查询关于我选择的主题的最近的 tweets ,而没有任何依赖性,并不容易。这也是我写这篇小教程的原因。因此,如果这适用于你,请继续阅读!

第一步:建立你的 twitter 开发者账户

首先,你需要设置你的 Twitter 开发者账户(如果你已经有了,跳到第 2 步)。要创建这样的帐户,请遵循以下说明:

  1. 导航至 https://developer.twitter.com/en/apply-for-accesshttps://developer.twitter.com/en/apply-for-access并使用您的 twitter 账户登录(如果您已经有一个的话)。如果没有,注册一个新的 Twitter 账户。登录后,您应该会看到以下网页(见下图)。

Twitter API:申请访问

2。点击“申请开发者账号”。这将打开一个对话框,询问你想如何使用 Twitter API。在我的例子中,我选择了以下设置:

Twitter API 访问应用程序

****3。描述您的预期用途:随后,您将被转到一个页面,在该页面上,您必须说明您对 Twitter API 作品的预期用途。总的来说,你需要写大约 200-600 个字符,这取决于你打算做什么。简单地说,诚实并尽可能用最好的方式描述你的用例。

Twitter API 访问应用程序:预期用途描述

4.查看您的访问申请并阅读条款。搞定了。

5.建立一个项目和应用程序:一旦您的访问权限被授予,导航到 https://developer.twitter.com/en/portal/dashboard 的,在那里您可以建立您的 Twitter API 项目和一个应用程序(换句话说就是一个用例)。

Twitter 开发者仪表板概述

6.复制您的不记名令牌:完成设置后,复制您的应用程序不记名令牌——您很快就会需要这个令牌。如果您忘记了您的不记名令牌,请导航到“密钥和令牌”并单击“重新生成”。

重新生成 Twitter API 不记名令牌

步骤 2: Python 环境设置

为了用 Python 发送 API 请求并能够使用 Twitter API,我们将不依赖任何 Twitter 包装模块,而只依赖非常方便的请求模块,该模块可以通过 pip 安装:

pip install requests

第三步:准备搜索 Twitter 功能

我花了一段时间才找到这个,但是有一个专门的 Github 库,里面有很多语言的代码示例,关于如何使用 Twitter API:https://github.com/twitterdev/Twitter-API-v2-sample-code

我的功能在很大程度上受到了这个存储库代码的启发,但为了更好地满足我的需求,对其进行了简化:

import requests
import json#its bad practice to place your bearer token directly into the script (this is just done for illustration purposes)
BEARER_TOKEN = "YOUR BEARER TOKEN HERE"#define search twitter function
def search_twitter(query, tweet_fields, bearer_token = BEARER_TOKEN): headers = {"Authorization": "Bearer {}".format(bearer_token)}url = "https://api.twitter.com/2/tweets/search/recent?query={}&{}".format(query, tweet_fields) response = requests.request("GET", url, headers=headers)print(response.status_code)if response.status_code != 200:raise Exception(response.status_code, response.text)return response.json()

如您所见,该函数相当短,需要三个参数:

  • bearer_token [str]: 您在步骤 1 中复制的无记名令牌。
  • query [str]: 这是将用于匹配所需 Tweets 的实际字符串。这些查询字符串可以很简单比如“滑板狗”也可以很复杂很强大。它们允许你通过标签过滤推文,识别转发,排除某些单词或短语,或者只包含特定语言的推文。关于如何编写这些查询字符串的专门描述可以在 这里找到
  • tweet_fields [str]: 查询中返回的字段,如附件、作者 id、文本等。例如,如果您想要 tweet 的 author_id、文本和发布日期,tweet_fields 字符串将如下所示:
tweet.fields=text,author_id,created_at”

非常详细的 API 参考可以在这里找到。参考资料中描述了可包含在函数中的其他可选参数。这里有三个非常有用的:

  • max_results: 指定应该返回多少条 tweets 的参数:一个介于 10 和系统限制(当前为 100)之间的数字。默认情况下,请求响应将返回 10 个结果。
  • start_time: 最早的 UTC 时间戳(从最近七天开始),将从该时间戳开始提供推文。(YYYY-MM-DDTHH:MM:ssZ(ISO 8601/RFC 3339)。)
  • end_time: 推文将被提供的最新的、最近的 UTC 时间戳。(YYYY-MM-DDTHH:MM:ssZ(ISO 8601/RFC 3339)。

第四步:运行 search_twitter 功能

照片由 Gema Saputera 在 Unsplash 上拍摄

最后,让我们运行我们的功能,搜索所有关于滑板狗的推文:

#search term
query = "skateboarding dog"#twitter fields to be returned by api call
tweet_fields = "tweet.fields=text,author_id,created_at"#twitter api call
json_response = search_twitter(query=query, tweet_fields=tweet_fields, bearer_token=BEARER_TOKEN)#pretty printing
print(json.dumps(json_response, indent=4, sort_keys=True))

响应:

这是我们的响应:首先,我们可以看到我们获得了 200 状态,这意味着一切运行顺利;其次,我们可以看到我们的响应是一个 json 对象,其中包含返回的 tweets:

200
{“data”: [{“author_id”: “946476703521570816”,“created_at”: “2021–01–06T18:08:57.000Z”,“id”: “1346881457399197697”,“text”: “[@HamillHimself](http://twitter.com/HamillHimself) i see your bobsleigh dog (seasonally sporty) and i raise you a skateboarding…cat? [https://t.co/d7lrCkDIz3](https://t.co/d7lrCkDIz3)”},{ ...

就是这个!我希望这对你们中的一些人有所帮助!请访问下面的进一步材料部分的链接,以获得关于 API 文档的更多见解。

更多材料:

  • [1] Twitter API 参考。搜索 Tweets:最近搜索:https://developer . Twitter . com/en/docs/Twitter-API/Tweets/Search/API-reference/get-Tweets-Search-recent
  • [2] Twitter API 参考。为最近的搜索端点构建查询:https://developer . Twitter . com/en/docs/Twitter-API/tweets/search/API-reference/get-tweets-search-recent
  • [3] Twitter API v2 示例代码 Github 资源库。Python、Ruby、Java、Javascript 的例子:https://developer . Twitter . com/en/docs/Twitter-API/tweets/search/API-reference/get-tweets-search-recent

为 geoanalytics 搜索地点及其地理细节

原文:https://towardsdatascience.com/searching-places-and-their-geographic-details-for-geoanalytics-a71ee2a74a5f?source=collection_archive---------8-----------------------

Google 的 Places API,结合标准的 Python 包,提供了一种使用名称、地址、电话号码、关键字或类别(餐馆、酒吧、商店等)查找地点的简单方法。).

Unsplash 上 Lucas Sankey 的照片

地理数据的作用已经成为理解和解决具体问题的政治和商业决策的重要组成部分。例如,我用它来进行我的可持续性研究,比如识别不同行业、人口统计等的集群。在一个特定的地点,企业参与可持续的商业实践。

基于 GIS 的研究面临的主要挑战之一是难以获得数据。尽管存在许多提供建筑物、商业、人口等细节的标准数据库。在一个领域中,这些数据库的主要问题是它们经常变得过时、不准确和不可靠。谷歌地图或类似的服务可以用来补充现有的数据库与最新的细节。在这篇文章中,我将部署 Google Places API 来搜索地点并检索所需地点的必要信息。

提醒一句:虽然谷歌 API 服务每个月都有一些免费配额——这对大多数用户来说都绰绰有余,但在发布 API 密钥之前,用户必须提供支付细节(例如信用卡或借记卡)进行注册。如果您不愿意支付或担心意外的费用,有足够的灵活性和选项来限制免费服务之外的请求/使用,以防止触发任何不良的计费。但是,如果不提供可接受的支付凭证,您就无法使用服务(据我所知)。

使用 Google 的官方 API 在 Python 中查找地点有两种方法。人们可以使用 Google Place 搜索 API 网页上的搜索指令直接发出 HTTP URL 请求。你也可以使用 Googlemaps 包,它提供了用 Python 获取地点细节的用户友好的功能。因为使用标准包装更方便,我们将使用它作为你的练习。在 Google API 中搜索地点有四种不同的方式。

  1. 搜索附近的地方

邻近搜索在由例如纬度、经度和半径指定的区域内寻找地点。我们可以通过提及关键词(咖啡、素食等)来细化搜索请求。)或类型的目标场所(如餐厅、咖啡厅)。它会在谷歌索引的所有内容中搜索一个位置。两种搜索方式(按关键字或类型)通常会返回不同的结果。示例代码片段如下所示:

这个 API 请求返回每个地方的大部分细节,除了formatted_addressphone numberwebsitereviews。虽然这些详细信息在某些情况下可能是相关的,但附近地点功能提供了我们正在寻找的地点的所有相关和令人满意的信息。

2.搜索地点

这个请求非常类似于上面讨论的 nearby search API,但是唯一的区别是它返回稍微不同的细节。例如,查询的响应返回该地点的地址,这在附近搜索功能中是不可用的。

3.找个地方

这个请求根据地名、地址或电话号码找到一个地方。API 请求提供了指定参数的灵活性,我们需要这些参数的数据,如下面的代码所示:

以这种方式提出请求有两个限制。首先,虽然基于电话号码或地址的查询肯定会返回一个结果,但涉及地名的文本查询可能会找到与我们期望的不同的地方。所以,除非我们也知道该地点的确切名称,否则这个功能有些不可靠,或者需要人工干预来验证地点细节。第二,匹配可能但不一定在目标地理范围内。当在期望的区域内没有找到匹配时,它可以返回来自全球任何地方的最接近的匹配,而不是返回零结果——只要在 Google 数据库中有任何匹配。

4。获取地点详情

这是查找已知地点信息的最全面的地点 API。Place Details 查询返回一个地方的所有细节——取决于请求的 fields 参数 Google 已经为这个地方建立了索引,包括评论(一个个人帐户最多 4-5 个评论)、网站等。与find_place类似,这个 API 请求也提供了指定需要返回什么位置属性的灵活性。唯一的区别是它还返回一些第三个 API 没有的额外细节(如网站、评论)。下面显示了一个示例代码片段:

这个函数的主要缺点是它使用place_id 进行搜索,并且在发出请求之前必须有确切的place_id 。因此,这个 API 通常与通用搜索 API 结合使用,后者提供基于关键字搜索的place_id,然后使用place_id 来收集特定于地点的信息。

潜在应用和结束语

人们可以通过不同的方式使用这样的 API。例如,在我的例子中,API 允许我快速检索购物中心、咖啡店、餐馆、酒吧等的位置。附近有一个特定的办公室、大学或住宅区,这将有助于我了解城市经济和发展模式的演变。这些商业密度和发展模式也可能揭示出基础设施、商业、商店、学校等方面的不平等。在不同的街区。找到这样的集群将有助于在城市或社区层面制定促进区域可持续发展的政策。

这篇博客的主要目的是介绍 Python 中 Google Places API 的特性。用于地理信息的 Google APIs 为应用程序开发人员和其他领域专家(如地理信息研究人员)提供了各种好处。

每日时间序列的季节性调整

原文:https://towardsdatascience.com/seasonal-adjustment-of-daily-time-series-1bd2aa9b096d?source=collection_archive---------7-----------------------

消除季节性因素

德国中央银行推出新的每日生活津贴程序

费德里科·贝卡里在 Unsplash 上的照片

随着大数据的出现,关于每天可用的时间序列有了相当大的推动。

不幸的是,每日数据往往是嘈杂的。例如,时间序列可能会在周末以及每个月的月初和月末达到峰值。加上每月的偏差,就很难理解到底发生了什么。

长话短说,这些时间序列的季节性成分必须剔除。不过还是先说基本的吧。

季节性调整基础

一般来说,时间序列由以下部分组成:趋势周期、季节和不规则波动部分(Chatfield,2000;Hyndman & Athanasopoulos,2021)。这种情况可以表示为

Yₜ =TCₜ + Sₜ + Iₜ

在哪里

时间 t 的 Yₜ:原始时间序列

TCₜ:长期或长期趋势,其周期叠加趋势成分

定义为周期性年内运动的 Sₜ:季节成分

Iₜ:不规则波动分量,描述时间序列中无法解释的部分。

此外,一些作者扩展了等式,例如,移动假日效应 HEₜ或工作日效应 WDₜ (Darné等人,2018)。

Yₜ =TCₜ + Sₜ + HEₜ + WDₜ + Iₜ

原始时间序列的问题在于,很难确定这种变化是由哪个部分引起的。是否因为公休日导致成交量下降?还是年年重复的年内效应?例如,一个月的时间序列在冬季可能较高,而夏季月份可能低于正常月份。

如果我们对时间序列的短期或长期变动感兴趣,我们就不会关心各种季节性。我们想知道时间序列的当前状态,以及它是否已经到达一个转折点。了解这些信息具有真正的商业意义。类似地,通过只关注趋势周期和不规则成分,消除了经济变量之间的虚假关系。此外,预测不同的组成部分,并得出最终结果是一个廉价和直接的方法来获得预测(格兰杰,1978)。

因此,在很多情况下,我们只对趋势周期和不规则成分感兴趣。为了收集这两个部分,时间序列必须经过季节和日历调整(欧统局,2015 年)。

针对月度和季度时间序列的直接非季节性方法已经存在很长时间了。统计机构经常将它用于许多官方的经济和社会时间序列。

但是日常数据呢?

目前没有官方认可的每日数据去季节化方法。尽管如此,使用每日时间序列进行去季节化可能会比月度或季度数据产生更好的结果,因为它需要更多的观察(欧统局,2015 年)。

每日季节调整

Ollech (2021)的每日季节性调整(DSA)程序是一种有希望的季节性和日历调整每日时间序列的替代方法。

该程序将 Cleveland 等人(1990 年)开发的黄土季节趋势分解程序(STL)与带有 ARIMA 误差的回归模型(RegARIMA 模型)相结合。季节性模式和日历效应是按顺序估算的。时间序列按以下顺序调整:

  1. STL 调整周内周期模式。
  2. RegARIMA 估计日历效应、跨季节效应和异常值。
  3. STL 调整月内周期效应。
  4. STL 调整年内效应。

最后,DSA 模型被描述为

yₜ=tcₜ+sₜ7+sₜ31+sₜ^365+heₜ+iₜ

其中 t 日的周内、月内和年内周期模式分别表示为 Sₜ7、Sₜ31 和 Sₜ^365。原始的时间序列和其他成分保留在经典的成分模型中。

但是让我们在每天的时间序列上尝试这种方法,以了解一个典型的过程会是什么样子。

西欧电力消耗数据集包括德国五年来的每日电力消耗(MW)。一旦我们把它画出来,很明显时间序列中存在几个季节性。

德国五年的日耗电量(MW)。图片作者。

例如,在检查日常功耗时,周六和周日的中值、下四分位数和上四分位数明显低于其余工作日。尽管如此,在一周中仍然存在一些异常值,这可能表明由于假期的移动而导致功耗降低。

德国五年来每天的日耗电量(MW)。图片作者。

此外,春季和夏季的功耗中值、下四分位数和上四分位数低于秋季和冬季。

德国五年来每月的日耗电量(MW)。图片作者。

让我们开始用 DSA 方法对时间序列进行非季节性分析。

首先,我决定使用功耗的对数来稳定序列的方差。然后,我将 STL 的局部回归中的观察次数设置为 13(相对较低!)以说明经济环境的变化。

第三,RegARIMA 模型中使用了许多移动假日来控制它们的影响。我没有使用所有的移动假期,而是只实施那些在百分之十水平以下或左右显示出显著效果的假期,并放弃其他的移动假期。

基于 DSA 方法的 RegARIMA 的日用电量的估计对数转换移动假日效应。作者生成的结果。

基于上述报告的影响,移动假期始终降低 DSA 程序中的功耗。例如,复活节星期一和圣灵降临节星期一对电力消耗的负面影响最大。

然后,我使用 STL 的稳健版本计算年内效应(也有 13 个观察值来考虑经济变化)。尽管如此,我没有使用 STL 进行月内周期,因为整个月的耗电量不明显。

下图分别描述了功耗时间序列及其组成部分(对数转换)。时间序列具有独特的周内和年内模式,这些模式在一段时间内相当稳定。由于关闭了这种影响,月内部分没有模式。经季节调整的时间序列是每日生活津贴方法的主要结果。

日常功耗组件。图片作者。

与 Ollech (2021 年)类似,我将季节性调整时间序列与其他非季节性方法的结果进行比较,即 Dokumentov 和 Hyndman (2021 年)引入的基于回归的季节性趋势分解程序(STR)和 de Livera 等人(2011 年)得出的 TBATS。

我使用了两个可以检测剩余季节性的测试——QS 测试和弗里德曼测试。年内、月内和周内没有剩余季节性迹象的时间序列已经成功地去季节性化(Ollech,2021)。因此,这种分析的结果应该表明哪种程序产生最好的结果。

QS 检验的零假设是时间序列中季节滞后没有正自相关。同样,弗里德曼检验的零假设是时间序列中特定时期的平均值之间没有显著差异。

QS 和弗里德曼检验的结果——原始时间序列与 DSA、STR 和 TBATS 季节调整程序的比较。由作者生成的结果。

上表显示了 QS 和弗里德曼的测试结果。与原始时间序列相比,TBATS 得出了类似的结果。STR 并没有拒绝月内季节性的零假设,但是现在 QS 和弗瑞德曼检验的假设被拒绝了。只有 DSA 方法才能得出不排除所有季节性假设的结果。因此,我认为 DSA 方法优于 STR 和 TBATS 程序。

结论

最终,德国中央银行的 DSA 方法是一个很好的非季节性工具,它考虑了几个季节性频率和假日效应。所得结果可用于定位时间序列的当前状态和识别转折点。

例如,该方法本身已被用于德国联邦统计局(Cox 等人,2020 年)的每日卡车收费里程指数的非季节性,我很高兴看到它将来还会被用于其他地方。虽然它还不是日常数据的去季节化方法,但是它使用 STL 和 RegARIMA 的独特方法具有很大的潜力。

想要更多信息?

本文的主要目的是介绍 DSA 程序,并使其更广为人知。

如果你对我的代码感兴趣,请查看 Github 上的库。

如果你想在你自己的项目中应用这个过程,我想给你介绍一下 DSA 库的简介,从中我得到了很好的指导(和代码片段)。此外,Daniel Ollech 发表的论文提供了该方法的详细理论描述。

如果您有任何问题或意见,请在下面留下您的反馈。另外,如果你想和我联系,你可以通过LinkedIn联系我。

敬请期待,下期帖子再见!

Chatfield,C. (2000 年)。时间序列预测。查普曼&霍尔事务所。

克利夫兰,R. B .,克利夫兰,W. S .,麦克雷,J. E .,&特彭宁,I. (1990)。STL:基于黄土的季节趋势分解过程。官方统计杂志,6(1),3–73。

考克斯,m .,特里贝尔,j .,林茨,s .,弗里斯,c .,弗洛雷斯,L. F .,洛伦茨,a .,奥勒奇,d .,迪特里希,a .,勒科龙,j .,韦贝尔,K. (2020)。gli cher Lkw-Maut-fahrleistungs 从 Lkw-Maut-Erhebung 的数字项目开始。《经济与统计》, 4,63-76 页。

de Livera,A. M .,Hyndman,R. J .,& Snyder,R. D. (2011 年)。用指数平滑法预测具有复杂季节模式的时间序列。美国统计协会杂志,106(496),1513–1527。https://doi.org/10.1198/jasa.2011.tm09771

达尔内,奥,费拉拉,l .,,拉迪雷,D. (2018)。季节调整方法和软件工具简史。在 G. L. Mazzi、D. Ladiray 和 D. A. Rieser(编辑),季节性调整手册(第 69–90 页)。欧盟出版办公室。

多库门托夫,a .,,海因德曼,R. J. (2021)。STR:基于回归的季节趋势分解程序(2021 年 4 月 4 日)。

欧盟统计局。(2015).ESS 季节性调整指南。【https://doi.org/10.2785/317290 号

格兰杰(1978 年)。季节性:原因、解释和含义。在阿诺德策尔纳(编辑。),经济时间序列的季节性分析(第 33–56 页)。NBER。

Hyndman,R. J .,& Athanasopoulos,G. (2021 年)。预测:原理与实践(第三版。).OTexts。【https://otexts.com/fpp3

奥利奇博士(2021)。每日时间序列的季节性调整。时间序列计量经济学杂志,aop,1–30。https://doi.org/10.1515/jtse-2020-0028

奥利奇博士和韦贝尔,K. (2020 年)。识别最具信息性的季节性测试的随机森林方法。德意志联邦银行第 55/2020 号讨论文件。

美国人口调查局。(2017).X-13ARIMA-SEATS 参考手册:可访问的 HTML 输出版本(版本 1.1)。http://www.census.gov/srd/www/x13as/

知识的所在地:具有深度结构化知识的人工智能系统

原文:https://towardsdatascience.com/seat-of-knowledge-ai-systems-with-deeply-structure-knowledge-37f1a5ab4bc5?source=collection_archive---------33-----------------------

以信息为中心的人工智能体系结构分类如何促进任务优化人工智能系统的构建

图像归属:Jae Ryeong Leehttps://www.gettyimages.com/eula

在一系列关于在人工智能系统中捕获信息和使用知识的选择中,我介绍了人工智能系统的信息中心分类的概念,作为对基于处理的分类的补充观点,例如亨利·考茨的神经符号计算分类法。该分类强调与 AI 系统中的信息相关的高层架构选择。本博客将概述这一分类中的第三类,以及它在支持机器理解、基于上下文的决策制定和高级机器智能的其他方面的有前途的作用。

提出的以信息为中心的分类包括三个关键的人工智能系统类别,这些类别基于体系结构划分和推理时信息的动态使用:

Class 1 — 全封装信息 :将训练数据和关系并入神经网络(NN)的参数存储器中。测试时无法访问附加信息。例子包括最近的端到端深度学习(DL)系统和语言模型(例如,GPT-3)。

类 2 — 半结构化相邻信息 (在基于检索的系统中):这些系统除了依赖 NN 参数存储器(例如检索增强生成)之外,还依赖于从储存库(例如维基百科)检索信息。

第 3 类——深度结构化知识(在基于检索的系统中):基于检索的系统与深度知识库紧密交互,如在高级智能知识维度中所定义的。

1 类和 2 类人工智能系统之间的主要区别在于信息放置的选择——封装在神经网络模型(1 类)或辅助知识库(2 类)中。

2 类和 3 类人工智能系统之间的主要区别在于更深层次的知识驻留在哪里——是在 NN 参数存储器中(2 类)还是在知识库中以深度结构化知识的形式(3 类中的知识图)。

机器智能需要什么高深的知识?

首先,让我们定义数据、信息和知识:

数据是需要处理的原始的、无组织的事实,从中获取信息,例如图像中的像素。

当数据在给定的上下文中被处理、组织、结构化或呈现以使其有价值时,信息被创建。信息为数据提供结构和上下文。

知识是指通过经验获得的相关的客观信息。知识使数据可操作化,并使其成为进行预测或决定行动的有用资源。例如,当自动驾驶汽车识别到灯正在变红时,它可以评估安全停车的学习实践,并选择制动动作。

关于被认为是深度和结构化的知识类型的详细讨论可以在文章 对深度知识的理解——知识结构如何将 AI 从表面相关性转变为对世界的理解 中找到。本文概述了深度信息的各种类别,并讨论了描述性知识以及世界模型、故事、价值和优先级在获取机器理解和更高机器智能所需的全部知识中的作用。引入概念参考以支持跨模态和维度的歧义消除和统一链接。我将把反映多种类型知识(图 1 中描述的类型)的关系和复杂性的表示称为深度知识。

图一-支持高智能的知识维度。

在 2 类系统中,存储库包含信息,但与信息相关的许多复杂关系和见解都封装在神经网络的嵌入空间中。在具有深度结构化知识的系统(3 级系统)中,大多数依赖关系和关系都明确地表示在知识库中。

类别 3:具有深度结构化知识的人工智能系统

在具有深度结构化知识(类别 3)的 AI 系统中,NN 具有相邻的知识库,该知识库具有传达构成深度知识的关系和依赖性的显式结构。在训练和推理/测试期间访问辅助知识库。一些深层知识仍然驻留在神经网络参数存储器中,但是在这类系统中,大多数知识驻留在神经网络之外。

大量的努力被用于提取信息并将其存储在结构化的知识库中。例如,抽象意义表示(AMR) 用于语义解析,以生成捕捉语义的图形,而不管语法/表示。提取的实体和关系可以被链接和映射到本体上,并存储在结构化知识库中。

Wikidata 是一个知识库,拥有深度知识的多个元素。除了分层本体,Wikidata 还提供“特殊”关系或属性,例如关系和实体的时间或空间注释。这些属性在静态数据和改变知识状态的事件之间架起了一座桥梁。它们反映了知识的动态本质,并支持知识库上的专用推理(如时间、空间、因果)。这些属性并不系统地存在于所有知识库中。

维基数据中的大部分知识都是描述性的。不同的知识表示可以将描述性知识与其他知识维度结合起来。具有因果模型的知识库可以支持更强大的推理,并实现反事实探索。添加上下文可以在适当的意图和情况下更好地使用信息。添加来源归属和出处可以允许系统理解数据中的偏差,并通过更知情的视角分析信息(例如,有争议的政治事件)。

利用知识的一个关键方面是知识表示和推理之间的相互作用。知识和推理是连续体上的点,而不是两个完全不同的功能。推理可以梳理出所需的知识或结果,当它们还没有被完全表示并准备好按原样检索时。在显式知识库上应用推理可以作为 NN 的一部分,作为在知识库上执行的单独操作,或者两者的组合来完成。这种体系结构的选择对训练的性质、知识表示和推理/测试期间执行的计算类型有着重要的影响。

在一个只有神经网络的推理系统中,知识库作为一个储存库。一个 3 级系统将在推理过程中使用一个显式知识库;然而,诸如排序、选择、邻居识别等推理功能是由嵌入空间内的神经网络执行的——正如可以在知识图上操作的 QA 系统的例子中找到的那样。

其他 3 级系统具有在知识库上选择信息或执行部分推理的主动功能。我们将这种机制称为推理提取。一个例子是神经符号问题回答(NSQA) 。推理提取优于仅 NN 推理的一个关键优势是,系统返回的答案可以随着 KG 的更新而动态变化,而无需重新训练模型。

3 级系统的关键要素

图二描述了 3 级系统的高级架构及其关键组件:

图二-3 级系统的关键要素。

知识是指通过经验获得的相关的客观信息。深度知识描述了具有多个维度的知识,每个领域都有复杂的关系。知识库将结构化的交互式知识实现为特定解决方案中的存储库,主要实现为知识图(例如: Google 的知识图)。最后,具有深度结构化知识的 AI 系统是具有知识库的系统,该知识库捕获深度知识并通过提取方案反映其结构。

神经网络是 3 类系统的主要功能部分。它可能包括所有的感知元素,如图像识别和场景分割,或用于处理语法的语言模型,基于位置的关系和公共语义的核心。它将可能学习表示输入数据的关键维度的嵌入空间。在多模态系统的情况下,它将反映图像空间和语言空间。与类别 2 类似,具有深度结构化知识的人工智能系统将仅在其中整合一部分数据/信息。然而,与复杂知识结构驻留在 NN 中的类 2 不同,类 3 体系结构依赖于相邻的结构化知识库,用于其语义空间中的许多深层关系。

类似于类 2 系统,类 3 中的神经网络系统可以在推理/测试时间期间与结构化知识库接合,并提取成功完成其任务所需的信息。在这种体系结构中,神经网络的训练需要与提取机制和知识库的某种表示一起完成,以允许神经网络学习如何在推理过程中提取所需的知识。

知识库包含未来推理可能需要的事实和信息,以及图 1 中描述的部分或全部深层知识结构。这些包括描述性知识、世界动态模型、故事、背景和来源属性、价值和优先级以及概念参考。

知识库在被利用之前就被填充了,并且可以在训练或推理期间(在在线或连续学习系统中)被进一步增强。它主要基于知识获取的功能,从人工智能系统外部的来源提取事实、信息、分类、功能模型和其他知识元素,并以一种适合提取和推理的方式构建它们。当它在训练或推理运行的基础上积累额外的信息时,它可以被视为神经网络的记忆扩展,以构建和保留额外的学习信息和知识。

知识库在培训后可以改变,并且可以包括额外的数据和知识。只要知识和信息的性质与 NN 在训练期间遇到的相似,修改后的知识库在基于其最新体现的推理期间应该是完全可用的。

最后,推理提取模块在 NN 和它的外部知识源之间进行调解。在最简单的情况下,它是从嵌入向量通过一些索引链接到知识库的直接映射。然而,在这种情况下,与平面信息存储库相比,知识库结构不会带来任何额外的价值。在更一般的情况下,推理提取将使用基于查询或 API 的库来提取信息。这方面的一个例子是 AMR ,它将信息提取并存储为语义图,对原始的语法表达不敏感。一个应用是以后从该表示中检索和解释或总结信息的能力。

由于创建覆盖所有相关信息的充分填充的知识图的复杂性,除了知识库之外,一些 3 类系统还将集成从大型半结构化数据语料库中检索的完整机制。知识库覆盖范围中的一些不足,例如缺失的链接,可以在推理时通过对不完整信息进行推理的方法积极地解决。其他情况可能需要系统访问额外的信息源,我们称之为 3+类,如图三所示。在 3+级系统中,有三个级别的信息可供神经网络使用:

最直接的信息和知识存在于神经网络参数存储器中

知识库中提供了大量的信息以及深度结构化的知识

对于知识库缺乏足够覆盖面的情况,检索机制从最大的可用半结构化信息库中提取信息

图三- Class 3+:深度结构化知识+半结构化信息。

什么样的 AI 系统架构最适合该任务?

没有一种架构是适合所有用途的。每一类人工智能系统都有明显的优势和相关的挑战。理解系统的特征并把它们与人工智能系统所执行的用例的轮廓相匹配是很重要的。

数据、信息和知识之间的结构关系与以信息为中心的分类相关,如下所示:

表 I——以信息为中心的人工智能类别中的数据、信息和知识。

拥有完全封装信息的 1 级人工智能系统可能是当今开发的最流行和最有影响力的人工智能解决方案。端到端的深度学习系统在许多领域都具有非凡的有效性。它们可能是所有类型的感知任务(如图像识别和分割、语音识别和许多自然语言处理功能)、序列到序列功能(如语言翻译)、推荐系统、许多问答应用等的最佳解决方案。一般来说,任何可以在连续空间上求解并可以通过潜在流形建模的函数都可以通过神经网络系统有效地处理。

当前 DL 的一些局限性可能会通过该领域的进一步工作得到解决。考虑到时间、成本、可变性、信息可靠性/偏差、较小的数据域等因素,问题不仅在于 1 类系统最终能做什么,还在于这种全封闭系统能做好什么。例如,一个 NN 可以学习做布尔逻辑和一些基本的算术,但它是实际用例的高效解决方案吗?DL 系统不能正确执行高中水平的算术,因为它提供近似值,而不是执行离散代数。神经网络系统在没有答案时也很难识别。另一个例子是获取来源属性和信息来源。如果一个 DL 系统要维护它在训练期间作为模型的一部分所看到的事实的来源,它将需要一个非常不同的 NN 解决方案,这是不太可能可行的。

具有半结构化信息储存库 的第 2 类人工智能系统最有助于解决具有非常大的数据/信息空间的用例:一个负责回答关于维基百科文章的问题的人工智能系统,通过指向外部储存库的检索机制,将比通过 NN 记忆将所有数据合并到参数存储器中更有效。在训练时间和测试/推理时间之间修改存储库中的信息的能力对于域外质询可能很重要,即使相关信息在训练期间不存在。到原始信息的链接为出处挑战提供了部分但有价值的贡献,并提高了可解释性和可解释性。

具有深度结构化知识的 3 级人工智能系统可以为增加对世界的理解做出重要贡献,并在人工智能内创建外部世界的多面反映。这种可视性有望改善认知功能和提高机器智能。第三类系统可以促进其他优势,例如背景的覆盖、知识来源的归属;知识结构中关系的起源,通过利用概念和本体降低模型的脆性;添加价值和优先级以支持基于目标的决策制定,等等。当人工智能系统从执行一项功能(如回答问题)过渡到成为具有一系列目标和行为的持久代理时,类 3 也提供了一个强大的基础。知识库可以被看作是智能代理不断进化的主动存储器。

可以考虑的一个例子是医疗保健辅助人工智能,它可以评估患者感染的可能性。可以训练具有完全封装的信息的 1 类 AI 来分析放射图像,并使用图像识别来识别指示感染的潜在模式。能够访问信息储存库的 2 类系统可以访问医学文献,以通过一些模式进行检索,并支持基于储存库的某种级别的信息 QA。具有深度结构化知识的 3 级系统可能能够在某个时间点处理多种形式的信息(包括放射学、医疗记录、最新研究结果),提供合理的分析,包括可能的原因,并能够解释信息来源和得出结论的途径。

尽管 3 级系统具有相当大的优势,但它们需要更高的复杂性,因为它们需要创建和更新知识库。他们还改变了学习过程,因为知识现在在神经网络和知识库之间分裂,这将需要新的技术来整合梯度下降统计方法与符号表示和学习。

根据人工智能目标选择最佳的以信息为中心的架构类别

赋予 AI 以更高智能水平的理解和操作能力,似乎与深度结构化的知识有着必然的联系。虽然积极的研究试图在参数记忆和 NN 潜在空间的结构的介质内创建这种复杂的知识构造和模型,但是由于其学习和知识表示的随机性质的限制,这种方法似乎面临重大挑战。用知识库增强神经网络的深度结构化知识人工智能架构提供了结合两个世界最佳特性的前景。

基于信息和知识方法的系统类型的正式化和详细说明还处于早期阶段。然而,对每个类的属性的更深入的理解将更好地装备该领域,以基于目标和用例做出 AI 架构选择。

参考文献

歌手,加迪。“知识库:人工智能中以信息为中心的分类,1 级—完全封装的信息”。领英,2021 年 2 月 16 日。https://www . LinkedIn . com/pulse/seat-knowledge-information-centric-class ification-ai-gadi-singer/

亨利·考茨。《第三个艾的夏天》。AAAI 罗伯特 S. Englemore 纪念讲座,导演剪辑。https://www . cs . rochester . edu/u/ka utz/talks/ka utz % 20 Engel more % 20 lecture % 20 directors % 20 cut . pdf(2021 年 6 月 9 日访问)

歌手,加迪。“知识的所在地:人工智能中以信息为中心的分类,第 2 类——半结构化信息库”。领英,2021 年 3 月 23 日。https://www . LinkedIn . com/pulse/seat-knowledge-information-centric-class ification-ai-gadi-singer-1c/

知识密集型自然语言处理任务的检索增强生成。ArXiv,2021 年 4 月 12 日。https://arxiv.org/pdf/2005.11401.pdf

歌手,加迪。“对深层知识的理解——知识结构如何将人工智能从表面相关性转变为对世界的理解”。走向数据科学,2021 年 5 月 6 日。https://towards data science . com/understanding-of-and-by-deep-knowledge-aac5 ede 75169

Chakraborty,n .等人,“基于神经网络的知识图问答方法介绍”ArXiv,2019 年 7 月 22 日。【https://arxiv.org/pdf/1907.09361.pdf 号

利用抽象意义表示进行知识库问题回答。ArXiv,2020 年 12 月 3 日。【https://arxiv.org/abs/2012.01707

歌手,加迪。《认知 AI 的崛起》。走向数据科学,2021 年 4 月 6 日。https://towards data science . com/the-rise-of-cognitive-ai-a 29 D2 b 724 CCC

Python 中的安全密码处理

原文:https://towardsdatascience.com/secure-password-handling-in-python-6b9f5747eca5?source=collection_archive---------9-----------------------

借助这些简单的技巧和提示,在 Python 中保护您的密码和凭证

照片由飞:D 在 Unsplash

几乎每个应用程序都需要某种形式的身份验证、密码处理或使用 API 密钥等安全凭证。您可能不是安全专家,但是您应该知道如何安全地处理所有这些密码和凭证,以保护您应用程序用户的凭证和数据以及您自己的 API 密钥和各种令牌。

保持这些安全元素的安全包括生成它们、验证它们、安全地存储它们以及保护它们免受敌人攻击。因此,在这篇文章中,我们将探索 Python 库、工具和概念,它们将有助于解决这个问题!

来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指南 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

提示输入密码

让我们从简单的开始——您已经有了带有命令行界面的基本 Python 应用程序。您需要向用户询问密码。您可以使用input(),但这将在终端中显示密码,以避免您应该使用getpass来代替:

getpass是一个非常简单的包,允许你提示用户输入密码,并通过提取当前用户的登录名获得他们的用户名。请注意,并不是每个系统都支持隐藏密码。Python 会试图警告你这一点,所以只需在命令行中阅读警告。

发生

有时,生成密码可能比提示用户输入密码更好。例如,如果您想设置首次登录时更改的初始密码。

没有任何用于生成密码的库,但是实现它并不困难:

使用上述代码生成的密码将会很强,但是很难记住。如果它只是一个初始的、临时的密码或短期的令牌,那么它是好的,但是如果用户应该使用它更长时间,那么使用 passphrase 更合适。

我们可以建立一个密码生成器,就像我们上面对简单密码所做的那样,但是既然有库可以用来做这个,为什么还要这么麻烦呢?这个库以著名的关于密码强度的 XKCD 命名为xkcdpass,它确实像漫画描述的那样——生成由单词组成的强密码短语:

这个代码片段首先在您的系统上找到一个单词/字典文件,比如/usr/dict/words,并挑选出指定长度的所有单词,然后从中生成一个用于生成密码短语的单词列表。生成器本身有一些参数,我们可以用它们来定制密码短语。除了字数和长度之类的显而易见的参数,它还有离合符号参数,这个单词的字符将被用作密码短语中单词的首字母(听起来很复杂?嗯,请看上面的密码短语示例)。

如果你真的想自己构建它,而不是在你的项目中添加一个依赖项,你可以在 Python 文档中使用这个方法。

散列法

既然我们要求用户输入密码或者为他们生成密码,我们该怎么做呢?我们可能希望将它存储在数据库中的某个地方,但是正如您可能(希望)知道的那样,您不应该以纯文本格式存储密码。这是为什么呢?

无论是明文还是加密,密码都不应该以可恢复的格式存储。应该使用加密性很强的单向函数对它们进行哈希运算。这样,如果有人获得了数据库中的密码,他们将很难恢复任何实际的密码,因为从哈希中恢复任何密码的唯一方法是暴力破解密码,也就是说,获取可能的明文密码,使用相同的算法对其进行哈希处理,并将结果与数据库中的条目进行比较。

为了使暴力破解更加困难,还应该使用盐。Salt 是随机字符串,存储在哈希密码旁边。它在散列之前被附加到密码上,使其更随机,因此更难猜测(使用彩虹表)。

然而,对于每秒可以尝试数十亿次散列的现代硬件,使密码难以猜测是不够的,因此慢速散列函数用于密码散列,使攻击者暴力破解密码的效率大大降低。

(注意:以上大大简化了使用这些散列函数的逻辑和理由。更多深思熟虑的解释,请参见本文的示例。)

有相当多的库和单独的散列算法,但是上述需求极大地缩小了我们的选择范围。Python 中散列的解决方案应该是passlib,因为它提供了适当的算法,以及即使不精通密码学的人也可以使用的高级接口。

在这个代码片段中,我们选择使用bcrypt作为我们的算法,因为它是最流行和经过良好测试的哈希算法之一。首先,我们检查其可能的设置,并检查算法使用的默认轮数是多少。然后我们修改散列器来使用更多的轮数(成本因素),使得散列更慢,因此,散列更难破解。这个数字应该是最大的,不会给你的用户造成不可容忍的延迟(大约 300 毫秒)。passlib定期更新缺省舍入值,因此您不一定需要更改该值。

随着哈希准备就绪,我们提示用户输入密码并哈希它。此时,我们可以将它存储在数据库中,这里出于演示目的,我们将根据原始明文密码进行验证。

从上面的代码中,我们可以看到对passlib的全部使用归结为我们选择的算法的hashmodify方法。如果你想要更多的控制计划,回合等。,那么你可以使用CryptContext类:

这个上下文对象允许我们使用多个方案,设置默认值或配置成本系数。如果您的应用程序认证很简单,那么这可能是不必要的,但是如果您需要使用多种散列算法、弃用它们、重新散列散列或类似的高级任务的能力,那么您可能想要查看完整的CryptContext集成教程。

你可能想要使用CryptContext的另一个原因是如果你需要处理操作系统密码,比如/etc/shadow中的密码。为此,您可以使用passlib.hosts中可用的预配置上下文,更多详细信息,请参见此处的示例。

为了完整起见,让我也列出几个其他可用的库,包括它们(不同的)用例:

  • bcrypt 是我们上面用过的库和算法。这与passlib使用的代码相同,没有理由使用这个低级的库。
  • crypt 是一个 Python 标准库模块,它提供了可以用于密码散列的函数。然而,所提供的算法取决于您的系统,文档中列出的算法没有上面显示的算法强大。
  • hashlib 是另一个内置模块。然而,这一个包括适合密码散列的强散列函数。这个库的接口使得函数更加可定制,因此需要更多的知识来正确地(安全地)使用。你完全可以使用这个模块中的函数,比如hashlib.scrypt来散列你的密码。
  • Python 标准库提供的最后一个哈希模块 hmac 不适合密码哈希。HMAC 用于验证消息的完整性和真实性,不具备密码哈希所需的属性。

小旁注:有了所有关于正确存储密码的新知识,让我们假设您忘记了某个服务的密码。你点击“忘记密码?”在网站上,他们发给你的不是恢复链接,而是你的实际密码。这意味着他们以明文的形式存储你的密码,这也意味着你应该逃离这种服务(如果你在其他地方使用了相同的密码,那么改变它)。

安全储存

在上一节中,我们假设目的是存储其他用户的凭证,但是您自己用来登录远程系统的密码呢?

将密码留在代码中显然是一个糟糕的选择,因为它以明文形式存在,任何人都可以看到,而且您还冒着意外将其推送到 git repo 的风险。更好的选择是将它存储在环境变量中。您可以创建.env文件,将它添加到.gitignore,用当前项目所需的凭证填充它。然后你可以使用dotenv包将所有这些变量放入你的应用程序,如下所示:

这个代码片段首先使用os.path函数构建到.env文件的路径,然后使用load_dotenv()函数加载环境变量。如果您的.env文件在当前目录中,就像上面的例子一样,那么您可以简化代码,只需调用load_dotenv(find_dotenv())就可以自动找到环境文件。当文件被加载时,剩下的就是使用os.environ.get来检索单个变量。

或者,如果您不想让应用程序变量和秘密污染您的环境,您可以像这样直接加载它们:

上述解决方案很好,但我们可以做得更好。我们可以使用系统的密匙环而不是将密码存储在不受保护的文件中,这是一个可以将安全凭证存储在您的主目录中的加密文件中的应用程序。默认情况下,该文件使用您的用户帐户登录密码进行加密,因此当您登录时,它会自动解锁,因此您不必担心额外的密码。

要在 Python 应用程序中使用密匙环凭证,我们可以使用名为keyring的库:

在上面的代码中,我们从检查 keyring 配置文件的位置开始,如果需要的话,您可以在这里进行一些配置调整。然后,我们检查活动的密匙环,并继续向其中添加密码。每个条目有 3 个属性— 服务用户名密码,其中服务充当名称空间,在这种情况下是应用程序的名称。要创建和检索一个条目,我们可以分别使用set_passwordget_password。除此之外,还可以使用get_credential——它返回一个凭证对象,该对象具有用户名和密码的属性。

结束语

即使您不是安全专家,您仍然要对您构建的应用程序的基本安全特性负责。这包括照顾好用户的数据,尤其是密码,所以希望这些例子和方法能帮助你做到这一点。

除了本文介绍的方法和技术之外,处理密码的最佳方式是通过将身份验证委托给 OIDC 提供商(例如 Google 或 GitHub)或者用基于密钥的身份验证和加密来代替它们,来避免使用它们,我们将在下一篇文章中对此进行深入探讨。

本文原帖martinheinz . dev

看数字:LightGBM 模型的贝叶斯优化

原文:https://towardsdatascience.com/seeing-numbers-bayesian-optimisation-of-a-lightgbm-model-3642228127b3?source=collection_archive---------9-----------------------

实践教程

使用贝叶斯优化调整LightGBM模型的超参数,并与简单特征工程实践的改进进行比较。

由大卫·特拉维斯在 Unsplash 上拍摄的照片

背景

在“小心搜索”的经典案例中,阅读了几篇关于模型超参数优化的在线文章后,我的新闻订阅受到了操作指南的狂轰滥炸,这些指南保证通过几个简单的步骤“获得可能的最强大的模型”

我当然是在夸大其词。

然而,我确实注意到,很少有文章真正提到超参数调整只是过程的一部分,而不是预测能力的银弹解决方案。更少的文章提到从超参数优化中获得的预测能力是适度的,并且可能比从体面的特征工程中获得的少。

被砍伐的树木

LightGBM是一个使用基于树的学习算法的梯度推进框架。这是集合技术的一个例子,它将弱的单个模型结合起来形成一个单一的精确模型。

有各种形式的梯度增强的基于树的模型— LightGBMXGBoost只是流行的例程的两个例子。

LightGBM在速度和内存需求方面具有优势,是我首选的梯度推进模型框架。

参数和超参数

当我们谈论调整机器学习模型时,需要做出一个微妙但重要的区别——一个参数和一个超参数之间的区别。

  • 参数是指模型本身计算出的值。
  • 超参数是由用户作为输入提供给模型的值。

例如,如果我们使用 LASSO 回归框架,用户将提供正则化惩罚𝜆(超参数),模型将计算回归系数𝛽(参数)等。

LightGBM通过各种超级参数提供大量定制功能。虽然一些超参数有一个建议的“缺省”值,通常可以提供良好的结果,但为手头的任务选择定制的参数可以提高预测精度。

没有硬性的规则来决定每个任务的最佳模型参数。在大多数情况下,建模者通过使用专家判断和经验或者通过使用某种形式的搜索技术来设置参数。

有许多搜索技术旨在将“最佳”超参数集识别为提供最具预测性模型(由用户或用例定义)的集,理想情况下结合交叉验证以提高严格性。

网格搜索技术是基本的强力搜索,其中设置每个超参数的可能值,搜索算法全面评估超参数的每个组合。这在时间和计算能力方面都是一种密集的方法,因为搜索空间很快变得很大。

作为基本网格搜索的扩展,随机网格搜索技术已经被证明可以提供可靠的结果。该设置类似于基本搜索,因为建模器为每个参数设置搜索空间,然而在每个评估步骤,对于每个参数,算法从搜索空间的边界内(随机地)抽取一个值。

使用“智能”搜索技术,建模者为每个超参数设置一个搜索空间。该算法评估从搜索空间提取的一组参数的性能,并使用结果以迭代的方式通知参数的选择。这样,算法以更有效的方式“锁定”最优解。贝叶斯优化——如下所示——是智能搜索技术的一个例子。

超参数调谐 v 特征工程

早些时候,我提出了一个未经证实的主张,即超参数调整带来的预测能力的提高可能会被适当的特性工程带来的提高所超越。

不管许多人怎么想(和写),参数优化在生成高度精确的模型中起着相对较小的作用。参数调整带来的精度增益通常比预期的要小* —我认为 2% — 3%的增益是一个不小的成就,因此应该被视为最后的尝试。

为了获得更大的准确性,建模者应该真正检查他们的模型规格和特征工程过程;更丰富、信息量更大的数据通常会产生更强大的模型。

当我们将超参数调整和特征工程在模型准确性方面的改进与基线准确性度量进行比较时,我们将对这一假设进行测试。

*当然,这并不是在所有情况下都是正确的,例如,使用完全不合适的超参数的模型在选择“最佳”超参数后会有显著的改善。

数据

我们将使用 MNIST 数据的子集,这是一个大型手写数字数据库,经常用于计算机视觉基准测试。

  • 42 000 个手写数字中的每一个都被捕捉到一个 28×28 像素的图像中。
  • 每个图像被分解成具有 784 列的单行,每列代表图像的一个像素。像素值在[0,255]的范围内变化,并指示像素的强度(即像素被“照亮”的程度)。
  • 除此之外,我们还有可用于预测的真实标签。

完整的 MNIST 数据库可以在这里找到: MNIST 手写数字数据库,Yann LeCun,Corinna Cortes 和 Chris Burges

流程

让我们开始吧—我们将:

  1. 导入和处理数据。
  2. LightGBM模型构建一个交叉验证过程,并获得交叉验证模型准确性的基线估计。
  3. 建立贝叶斯优化过程,设置参数搜索空间并运行优化器。
  4. 设计一个简单的特征,并评估新特征对模型精度的影响。

数据导入和处理

数据导入

使用pandas导入数据后,我们可以非常容易地创建交叉验证折叠:

这导致了相当均匀分布的褶皱:

作者图片

让我们做一些可视化——首先检查一些我们试图通过从数据中重建图像来预测的例子:

作者图片

…然后检查目标值的总体分布(即数字):

作者图片

…最后检查目标在交叉验证折叠中的分布:

作者图片

上面的分布图和统计数据看起来很有希望:褶皱被均匀地创建,褶皱内目标值的分布看起来与整体目标分布一致。我找不到任何不平衡的问题——咻——所以我会继续努力。

数据处理:缩放和降维

我们现在有一个长 42 000 行、宽 784 列的数据集。对这么大的数据集执行交叉验证和超参数优化对我可怜的小笔记本电脑来说是相当沉重的负担。

因此,我热衷于将数据集压缩成更易于管理的东西。我将首先缩放像素数据,然后执行主成分分析(PCA)来减少列数。

sklearn有一个很好的 PCA 算法实现,我用它将 784 个特征减少到 87 个,同时保留了数据中 90%的“变化”。

LightGBM的交叉验证功能

开始交叉验证!

幸运的是,LightGBM自带交叉验证功能,有助于简化代码:

眼尖的读者会注意到,该函数只接受可用LightGBM超参数的子集。这是有目的的,因为这些是我们稍后要调优的超参数。

使用上面的“默认”参数集运行交叉验证,返回 95.8%的基线准确性——不算太差!

贝叶斯优化

我们提到贝叶斯优化是一种超参数调整的“智能”方法。

我们将使用 Python 实现BayesianOptimization,这是一个基于贝叶斯推理原则的约束全局优化包。

该软件包试图在尽可能少的迭代中找到“黑箱”函数的最大值,特别适合于需要大量计算和/或时间资源的优化问题。

贝叶斯分析方法非常简单:

  1. 收集一个 先验假设 —关于过程的初始假设,而不收集或分析任何真实世界的数据。在我们的例子中,我指定的超参数搜索空间是先验的假设。
  2. 通过调查被分析的过程来收集证据,通常是通过取样或研究现实生活中的事件。“证据”来自使用从搜索空间提取的超参数值的交叉验证的模型准确性。
  3. 使用收集的证据,更新初始假设以形成后验信念;从理论上讲,后验应该比前验更见多识广,更准确。我们的后验信念将是一个狭窄的超参数搜索空间。

在实践中,步骤 2 和 3 将重复多次——有时步骤 3 通知步骤 2——每次迭代创建“最佳”超参数集的更明智的视图。

例如,运行可能类似于 1 → 2 → 3 → 2 → 3→ 2 → 3 → 最终参数

BayesianOptimization对用户友好,对用户要求相对较少:

  • 要最大化的功能的规格。在这种情况下,这是我们交叉验证的精度函数cross_val
  • 超参数搜索空间的规范。
  • 初始勘探和后续优化数量的说明(即重复上述步骤 2 和 3 的次数)。

作者图片

上表显示了优化过程的结果。前 10 次迭代用作初始证据收集,随后是 10 次更新和优化的迭代步骤。新的精度最大值以紫色突出显示。

我们可以看到,在步骤 4、16 和 20 中,准确性有所提高。

  • 步骤 4 中的改进来自纯粹的随机选择——类似于我们从随机网格搜索中获得的成功。
  • 步骤 16 和 20 中的改进来自收集证据和更新假设的贝叶斯过程。

优化耗时约 14.5 分钟,并将模型精度从 95.8%提高至 96.5%。

不如换成一个强大的新功能?

是时候验证我们的假设了,即特征工程比超参数优化更强大。

在这里,我们将创建一个相当简单的 K-均值聚类。因为这是一个相当著名的技术,我不会花太多时间在理论上,除了说:

  • 该算法将每一行数据分配给一个集群。
  • 聚类特征应该有助于模型区分数字,因此应该将相似的观察结果组合在一起。可能会有一些事情出错的情况(例如,将一些 7 分组为 1,反之亦然)。因此,我将指定创建 10 个集群——每个数字 0-9(含)对应一个集群。
  • 我们将对折叠内建模和折叠外预测更加严格。

从这段代码中并不明显,但是执行集群大约需要 30 秒。

使用默认参数和通过聚类增强的数据进行交叉验证的准确率现在为 96.2%。

所以看起来我错了——超参数调整的性能超过了功能工程!

嗯,也许不完全是,一旦我们把时间和计算因素考虑进去…

  • 超级参数调整使 14.5 分钟的计算时间增加了 0.7 个百分点。每分钟的准确率大约提高了 0.05 个百分点。
  • 功能工程在 30 秒的计算时间内增加了 0.4 个百分点。每分钟的准确率大约提高了 0.8 个百分点。

最后的想法

…还有一些漫谈。

我们在这张纸条上做了很多!

首先,我们介绍了 MNIST 数据,并了解了如何以表格形式表示图像。这有更多的微妙之处,尤其是在处理彩色图像时,但这只是一个开始。

我们讨论了一些理论,包括提升树、参数和超参数、超参数优化和特征工程、贝叶斯推理方法以及如何将其应用于超参数优化。

我们以整洁且可再现的方式手工创建了交叉验证折叠。我们本可以通过其他方式获得类似的结果,但是这种方法很容易定制,并且可以扩展以确保某些观察总是以相同的方式结束。有用!

一些简洁的可视化向我们展示了我们试图建模的内容,并允许我们检查数据不平衡交叉验证折叠创建的适当性

我们简单地提到了缩放、主成分分析让我们大幅减少了建模集的规模。我做 PCA 的方式有几个问题:

  1. 我将数据缩放到范围[0,1]内;在运行 PCA 之前,确实应该对其进行归一化(即零均值和单位标准偏差)。
  2. 为了提高交叉验证的严格性,PCA 组件应该仅在折叠数据上训练。我已经使用了完整的数据集,但是应该将转换构建到一个可以在交叉验证过程中使用的管道中。
  3. 根据所用的模型和超参数,PCA 后可能需要一些额外的缩放。例如,神经网络倾向于对位于特定范围(通常为[0,1])的数据执行更好的操作。如果我们要使用一个带有规范化惩罚的LightGBM模型,我们将需要再次规范化数据。

交叉验证贝叶斯优化功能已经建立——而且非常简单,就像 K 均值聚类一样。

我们看到了在这种情况下,尽管两种方法都提高了模型精度,但超参数调整在绝对意义上胜过特征工程。然而,一旦将计算时间考虑在内,我们就会发现功能工程 带来了更大的效益。

我认为还可以做进一步的改进来提高模型的准确性:

  • 图像旋转可以提高模型的概括能力。作为特征工程的一种形式,这将涉及通过稍微旋转像素数据来从现有集合创建“新”观察。该模型将在原始数据和旋转数据的混合上进行训练,并有望对凌乱的作者更加鲁棒。
  • 图像的像素之间可能存在非线性关系——想想“关”和“开”的相邻像素之间的阶跃(或非常快速的)变化。非线性降维技术——t-SNE 或类似技术——在这里可能很有用。
  • 一个不同的模型规格可以提高性能:已经注意到神经网络在计算机视觉任务上工作得很好。

通过计算机的眼睛看世界(以及为什么这对你很重要)

原文:https://towardsdatascience.com/seeing-the-world-through-computers-eyes-and-why-it-matters-to-you-40152124d20?source=collection_archive---------43-----------------------

计算机视觉具有改善消费者体验、降低成本和增强安全性的潜力。理解计算机视觉的基础是开启关键竞争优势的第一步。你准备好了吗?

作者图片

计算机视觉是人工智能世界最非凡的礼物之一。借助计算机视觉,许多公司试图通过计算机的眼睛来看世界,并在解决复杂的业务问题方面取得了长足的进步,例如实时识别产品缺陷、验证客户身份或自动化保险索赔流程。忽视计算机视觉的这种现实应用可能意味着错过了为企业实现增长、生产力和成本节约的机会。那么什么是计算机视觉,它能有什么帮助呢?

你将了解:

  • 什么是计算机视觉?
  • 计算机视觉在当今世界是如何应用的?
  • 我们如何用机器学习实现计算机视觉?

什么是计算机视觉?

短暂的时间旅行

早在计算机发明之前,科学家们就试图找到方法来理解我们的眼睛和大脑如何共同工作来识别我们看到的东西并做出反应。信不信由你,我们今天所知道的关于视觉的许多东西可以追溯到 20 世纪 50 年代以来大卫·胡贝尔和托尔斯滕·威塞尔对猫进行的神经生理学研究。

在 20 世纪 60 年代,人工智能成为一门学科。正是在这一时期,计算机视觉首次作为麻省理工学院夏季项目引入,这被视为创造计算机可以执行人类认知功能(如视觉、学习、推理和解决问题)的垫脚石。虽然夏季项目没有成功,但它标志着计算机视觉作为一个科学领域的正式诞生,该领域旨在使计算机能够自动看到、识别和理解视觉世界,模拟人类视觉的方式。

是的,这是我们的目标,但在当时,我们的技术还没有准备好。幸运的是,我们不用等太久。在 2000 年,4 个重要因素汇聚在一起,使计算机视觉的全新范式成为现实。

作者图片

今天,我们已经走过了漫长的道路,因为计算机视觉是人工智能和机器学习的最热门领域之一,具有广泛的商业应用和巨大的潜力。但是在深入现实生活的用例之前,让我们尝试定义计算机视觉,并了解它可以解决什么问题。所以我们开始吧,各位。

人类视觉对计算机视觉

计算机视觉是计算机对图像和视频进行自动分析,以获得对世界的一些理解。

——摘自肯尼斯·道森-豪的《OpenCV 计算机视觉实用介绍》

作者图片

从这张图可以看出,我们试图实现的是确定图像中的狗是否是我们的四条腿的伙伴。在人类的视觉系统中,我们用眼睛看,然后让我们的大脑理解图像,并通过非常复杂的推理过程识别他是否是我们的狗。同样,计算机视觉旨在模仿理解图像的相同过程,将图像与我们狗的已知特征进行匹配,并识别它是否是我们的狗。所以简单地说,人类视觉和计算机视觉只是两种不同的手段,目的都是为了解读视觉信息。

了解正在解决什么问题

但是,如果我们人类的眼睛和大脑已经拥有如此强大的能力,我们为什么还需要另一种手段呢?

这是因为计算机非常擅长以极快的速度完成一项任务,而不会像人类一样分心。在过去的几十年里,卷积神经网络已经证明了比人类更好或相当的物体识别准确性。例如,2015 年,PReLU-Net 深度网络成为第一个在 ImageNet 2012 数据集上超过人类准确性的计算机模型。

印象深刻!但是它到底为企业解决了什么问题呢?让我解释一下。

  • 计算机视觉可以帮助自动化与分析和解释图像或视频相关的小型重复性视觉任务,从而节省成本并腾出时间进行更具战略性的活动。
  • 计算机视觉可以帮助进行更加一致和准确的视觉评估,从而在几秒钟内实现基于数据的决策,以获得更好的消费者体验、安全性或质量改进

计算机视觉在当今世界是如何应用的?

过去十年中,计算机视觉的出现给各个行业带来了巨大的变化。这里有 7 个与计算机视觉相关的常见任务,我们甚至可能在日常生活中遇到过。

  • 物体检测: 定位图像或视频中特定物体的存在
  • 图像分类: 根据特定的规则对图像进行分类和标注
  • 图像分割: 将单幅图像分割成多个片段,分别处理相关片段
  • 特征匹配: 匹配两幅相似图像或视频的对应特征
  • 边缘检测: 通过识别图像中图像亮度急剧变化的点,找到图像中物体的边界
  • 模式检测: 自动识别图像或视频中的模式和规律
  • 面部识别: 利用一个人的面部特征来识别或验证其身份

值得注意的是,当问题很简单时,计算机视觉的某些用例可能只是为了完成其中一项任务。然而,在现实世界中,事情可能会变得相当复杂,有无数的挑战和环境变化。

例如,机器人收割机可能需要使用物体检测来准确定位每个草莓在其茎和叶中的位置,然后使用图像分类将每个草莓分为成熟和未成熟的类别,以确保它只挑选那些成熟的草莓。因此,复杂的商业应用必须能够同时执行各种计算机视觉任务,以解释图像或视频。在下面的图片中,让我们来看看那些正在这里发生的相当复杂的业务应用程序!

作者图片

我们如何用机器学习实现计算机视觉?

我们已经谈了很多关于计算机视觉的可能性。机器学习(以神经网络的形式)可以以惊人的准确性创建计算机视觉应用程序,这也不是什么秘密。但这里有一个百万美元的问题需要回答:我们如何通过机器学习来实现这些令人惊叹的计算机视觉能力?

好吧,不可否认,这可能需要一整本书来涵盖这个话题。但是,本着给所有非技术读者一个全面概述的精神,这里有两种主要的方法来用通俗的语言建立一个具有机器学习的计算机视觉模型,以帮助您入门。

第一种方法:传统机器学习(ML)

在深度学习成为机器学习领域的一个事物之前很久,许多计算机视觉模型完全建立在传统的 ML 算法上,如决策树、支持向量机或逻辑回归。

简单来说,这些 ML 算法只是可修改的数学函数。基于已知的输入和输出对,计算机学会调整和定制数学函数,以更好地将某些输入与某些输出关联起来。随着时间的推移,如果输入和输出涵盖了现实生活中的足够复杂的各种异常和不寻常的情况,数学函数将被微调以尽可能接近地代表现实,从而实现对物体、植物、动物或人的更准确的检测或分类。多酷啊。

作者图片

上图说明了传统机器学习的一个简化过程。当我们审视这一过程时,记住以下两点至关重要。

首先,即使在部署之后,为业务应用程序构建机器学习模型也是一个迭代过程。黄金模型应该不断监控、更新,甚至从头开始重新创建,以适应业务变化。

其次,传统的机器学习需要一些严肃的人为干预才能成功。例如,由于我们无法将传统的 ML 算法直接应用于我们的原始数据(例如图像或视频)来执行计算机视觉任务,数据科学家必须执行一个称为特征提取的额外数据预处理步骤,以将原始数据转化为结构化和成形的数据,转化为相关的特征,这些特征是机器学习算法的基本输入。不要低估从原始数据中正确提取特征的代价,因为这项耗时的任务通常需要多次迭代,并且需要适当的领域知识。

第二种方法:深度学习

当想到深度学习时,我们许多人都会想到一些深刻的黑暗之谜。但这一点也不神秘。首先,理解深度学习是机器学习的一个子集是有帮助的。在其核心,上述传统的机器学习和计算机视觉的深度学习有着相同的目标:通过检查大量的例子(也就是巨大的训练数据集),试图找到一个尽可能接近地表达现实(及其所有复杂性和例外)的数学函数。

那么,我们为什么要考虑深度学习而不是传统的机器学习呢?这里可以说最大的优势是深度学习不需要人类执行任何特征提取任务(还记得我们上面说过它很耗时并且需要非常具体的领域知识吗?).

但是等一下!在我们匆忙得出深度学习是解决所有问题的灵丹妙药的结论之前,这里有两个最重要的方面需要注意。

  1. 如果你没有足够的数据来训练它,你就不能使用深度学习。我所说的充分,不仅指数量,还指质量(即相关、完整和没有偏见)。
  2. 如果你还没有拥有或者不愿意为强大的计算能力付费来处理大量数据和进行复杂的数学计算,深度学习就不会起作用。

与传统的机器学习相比,深度学习需要更多的训练数据和计算能力才能发挥作用。这不是唯一的方法,也不总是最好的方法。该技术本身功能强大,但不如其他传统方法成熟。因此,对计算机视觉进行深度学习的决定绝不能掉以轻心。

我们将何去何从?

像生活中的许多其他事情一样,我们认为人类的视觉是理所当然的,直到我们试图用计算机来模仿它。到今天为止,我们还远远没有理解,更不用说模拟我们的眼睛和大脑一起工作的方式来理解我们周围的美丽世界了。但这并不意味着计算机视觉仍然是一个与商业毫无关联的新想法。我们已经在手机上、街道上、办公室里,甚至在那些生产我们每天购买的不同产品的工厂里看到了它。

这些与上述用例相关的大牌也不意味着你必须成为特斯拉或沃尔玛才能了解计算机视觉如何帮助我们更有效地工作。市场上有各种基于云的预训练机器学习模型,如谷歌的云视觉、亚马逊 Rekognition、Azure 计算机视觉和其他解决方案,探索和实验计算机视觉的选项真的是无穷无尽的。因此,如果你的团队目前正在定期处理大量的图像或视频,那么重新想象计算机视觉如何帮助轻松完成工作永远不会太迟。探索之旅是有代价的,但拒绝尝试、失败、学习和成长也是如此。

感谢您的阅读。如果你觉得这篇文章有用,可以看看我的博客,或者关注我的 LinkedIn 和 Twitter。祝大家一周愉快!

参考

  1. 计算机视觉简史
  2. 什么是计算机视觉&它是如何工作的?简介
  3. 虚拟人的深度学习
  4. OpenCV 计算机视觉实用介绍

原载于 2021 年 2 月 15 日 http://thedigitalskye.comhttp://thedigitalskye.com/2021/02/16/seeing-the-world-through-computers-eyes-and-why-it-matters/

使用 NDVI 和 Rasterio 分割卫星影像

原文:https://towardsdatascience.com/segment-satellite-imagery-using-ndvi-and-rasterio-6dcae02a044b?source=collection_archive---------32-----------------------

使用 NDVI 和拉斯特里奥轻松分割植被和土壤

作者图片

在本帖中,我们将尝试在卫星图像中分割植被和土壤。

我们将借用这篇论文中的观点。我还会利用我在之前的博客中关于这个话题的想法。

理想情况下,我们希望从常规的卫星图像中:

作者图片

对此:

作者图片

橘子是土壤。红色的是植物。

NDVI

正如论文中提到的,我们需要提取归一化植被指数。这是一个有用的植被指数。这里有一个 NDVI 图像的例子:

作者图片

这是公式

下面是我获取 NDVI 图像作为 numpy 数组的代码。

def get_ndvi(red_file):nir_file = os.path.dirname(red_file) + '/nir.tif'band_red = rasterio.open(red_file)band_nir = rasterio.open(nir_sfile)red = band_red.read(1).astype('float64')nir = band_nir.read(1).astype('float64')# ndvi calculation, empty cells or nodata cells are reported as 0ndvi=np.where( (nir==0.) | (red ==0.), -255 , np.where((nir+red)==0., 0, (nir-red)/(nir+red)))return ndvi

为感兴趣的特定区域计算 NDVI。这在论文中有定义:

对于每个地块,通过选择中间两行人工建立感兴趣区域(ROI ),并提取对应于每个地块的植被指数的平均值。

这在 python 中实现起来非常简单。

# get average ndvi of center two rowsdef get_region_of_interest(ndvi, multiplier = 1/2):# undo the background adjustmentregion = ndvi.copy()region = np.where(region==-255, 0, region)# mean of center rowscenter_row1 = np.mean(region[int((multiplier)*len(region))])center_row2 = np.mean(region[int((multiplier)*len(region))+1])# mean of both rowsmean = (center_row1.copy()+center_row2.copy())/2return mean

植被覆盖率

该文件还提到了植被覆盖率。我会通过设置一个阈值来计算这个。任何高于该阈值的 NDVI 值都将是植被。下面的任何东西都将是土壤。

THRESHOLD = 0.3def get_fc(ndvi):ndvi_copy = ndvi.copy()vegetation = np.where(ndvi_copy > THRESHOLD, 1, 0)vegetation_count = np.count_nonzero(vegetation)total = ndvi_copy.shape[0]*ndvi_copy.shape[1]fractional_cover = vegetation_count/totalreturn fractional_cover

我们稍后需要更改该阈值。

图 1 植被覆盖率与 NDVI

现在我们可以重现报纸第 7 页上的情节。我们将为每张图片绘制部分植被覆盖对 NDVI。

我们还要画最小二乘回归线。

这里有一段代码允许我们这样做。

def plot_fc_vs_ndvi(fc, ndvi):y = np.array(fc).reshape(1, -1)x = np.array(ndvi).reshape(1,-1)slope, intercept, r_value, p_value, std_err = stats.linregress(x,y)x = np.linspace(min(ndvi),max(ndvi),100)f, ax = plt.subplots(1,1,figsize=(10,10))ax.plot(x, slope*x+intercept, '-r', label='fc='+str(round(slope, 2))+'*ndvi+'+str(round(intercept, 2)), color='black')ax.set_title('Fractional Cover vs NDVI at threshold of '+ str(THRESHOLD))scatter = ax.scatter(x=ndvi, y=fc, edgecolors='black')ax.set_xlabel('Normalized difference vegetation index (NDVI)')ax.set_ylabel('Fractional Cover (fc)')ax.text(min(ndvi)+0.8*(max(ndvi)-min(ndvi)), min(fc)+0.2*(max(fc)-min(fc)),s='R^2 = {}'.format(round((r_value**2), 4)), fontdict={'fontsize':14, 'fontweight':'bold'})f.savefig('fc_vs_ndvi_plots/fc_vs_ndvi_'+str(self.plot_counter)+'.jpg')f.show()

详见完整代码。

我们可以更改之前设置的threshold值,看看它如何影响回归。文中指出,应选择具有最佳回归模型的threshold值。

我们将对所有图像运行此代码,并绘制结果。

由此我们可以看出,最高的 R 与阈值 0.45 相关联。

我们可以将这个阈值应用于所有图像。任何大于 0.45 的 NDVI 值都是植被。低于 0.45 的都是土。

创建二进制数组

使用阈值 0.45,我们可以创建一个二进制数组。在数组中,1 表示植被。0 表示土壤。

这段代码正是这样做的。阈值在__init__功能中定义。详见全码。

def create_mask(self, red_file):nir_file = os.path.dirname(red_file) + '/nir.tif'band_red = rasterio.open(red_file)band_nir = rasterio.open(nir_file)red = band_red.read(1).astype('float64')nir = band_nir.read(1).astype('float64')# get raw ndvi and save as jpgself.raw_ndvi = np.where((nir+red)==0., np.nan, (nir-red)/(nir+red))# create canopy cover maskself.canopy_cover = np.where(np.isnan(self.raw_ndvi), np.nan, np.where(self.raw_ndvi<self.ndvi_threshold, 0, 1))self.canopy_cover = np.ma.masked_where(np.isnan(self.canopy_cover), self.canopy_cover)# show ndvi mask and save it as jpgprint('canopy cover')print(np.unique(self.canopy_cover))return self.canopy_cover

以下是更清晰的输出形式。第一行是有阈值的天篷罩。第二行是 RGB 卫星图像。

作者图片

作者图片

我们可以看到它在分离植被和土壤方面做得相当不错。这比我之前用 K-Means 聚类的尝试要好得多。

结论

在这篇博文中,我描述了一种使用 NDVI 和拉斯特里奥分割卫星图像的方法。

我为悉尼的一家小型创业公司做这项工作。没有他们的帮助,我不可能做到这一点。我从他们身上学到了很多。

完整代码在 Github 上。

我希望这能帮助那里的人们。如果我犯了错误,请在推特上联系我。谢谢!

原载于 2021 年 1 月 31 日https://spiyer 99 . github . io

使用 Rasterio 和 Scikit-Learn 分割卫星图像

原文:https://towardsdatascience.com/segment-satellite-images-using-rasterio-and-scikit-learn-fc048f465874?source=collection_archive---------37-----------------------

实践教程

使用 KMeans 聚类按土地覆盖/土地利用对卫星图像进行分割

作者图片

最近,我将 KMeans 聚类应用于卫星图像,结果给我留下了深刻的印象。我把学到的招数告诉你,你就不浪费时间了。

需要注意的事项:

  • 使用光栅而不是 gdal 。拉斯特里奥更蟒。
  • 对于这个例子,我将使用地形图像。这提供了高分辨率的低空卫星图像。Terravion 图像来自 8 个不同的波段。
  • 我要三簇。这些将包括:树冠覆盖(树木,植被等。),土壤和背景。

k 含义解释

我制作了一个信息图表,用简单的英语解释知识。在 reddit 或 twitter 上查看。

堆叠带

每个 Terravion 图像都有以下波段(您的波段可能会有所不同):

  • red2.tif
  • alpha.tif
  • tirs.tif
  • blue.tif
  • nir.tif
  • red.tif
  • green.tif
  • green2.tif

在做任何事情之前,你需要堆叠所有的带子。下面是一些堆叠波段的代码。取自这个帖子。

def stack_bands(files):img_fp = 'sentinel_bands.tif'# Read metadata of first file and assume all other bands are the samewith rasterio.open(files[0]) as src0:meta = src0.meta# Update metadata to reflect the number of layersmeta.update(count = len(files))# Read each layer and write it to stackwith rasterio.open(img_fp, 'w', **meta) as dst:for id, layer in enumerate(files, start=1):with rasterio.open(layer) as srclassifer:dst.write_band(id, srclassifer.read(1))return img_fp

安装 KMeans

现在我们需要使 KMeans 分类器适合数据。我发现最有效的方法是在一些图像上安装 KMeans 分类器。理想的图像,树冠覆盖和土壤之间有明显的差异。

train函数将接受一个 md5 代码(例如9bbd67cfb0d660551d74decf916b2df2)和一个日期字符串(例如20190223T130237)。它将在数据集中找到匹配的图像,并在该图像上匹配 KMeans 分类器。

一些重要的事情:

  • 我没有使用alphablue频道。他们被证明是无用的。
  • 我用的是red2green2redgreen并没有被证明很有用。

这个训练代码改编自这个 github repo 。

from sklearn.cluster import KMeans
from rasterio.plot import reshape_as_image
import matplotlib.cm as cm
from sklearn import clusterdef get_date_from_text(text):text = text.split('/')[-2].split('_')[4]datetime_date = datetime.strptime(text, '%Y%m%dT%H%M%S').date()return datetime_date# no alpha and blue imgs
def get_files_from_code(code, date):files = glob.glob('/content/canopy_cover_cotton_*'+code+'_'+date+'*TERRAV_PLN/*.tif')files = sorted(files, key = lambda x: get_date_from_text(x))files = [i for i in files if os.path.basename(i).split('.')[0] in ('red2', 'green2', 'nir', 'tirs')]return filesdef train(k = 3, classifer = None, date = '20190223T130237', code = '9bbd67cfb0d660551d74decf916b2df2'):files = get_files_from_code(code, date)img_fp = stack_bands(files)img_raster = rasterio.open(img_fp)# Read, enhance and show the imageimg_arr = img_raster.read()vmin, vmax = np.nanpercentile(img_arr, (5,95))  # 5-95% contrast stretch# create an empty array with same dimension and data typeimgxyb = np.empty((img_raster.height, img_raster.width, img_raster.count), img_raster.meta['dtype'])# loop through the raster's bands to fill the empty arrayfor band in range(imgxyb.shape[2]):imgxyb[:,:,band] = img_raster.read(band+1)# convet to 1d array. 4 cause we have 4 bands here.img1d = imgxyb[:,:,:4].reshape((imgxyb.shape[0]*imgxyb.shape[1],imgxyb.shape[2]))# print(img1d.shape)# create an object of the classifier and train itif(classifer == None):classifer = cluster.KMeans(n_clusters=k)# param = cl.fit(img1d[~mask])param = classifer.fit(img1d)# get the labels of the classes and reshape it x-y-bands shape order (one band only)img_cl = classifer.labels_img_cl = img_cl.reshape(imgxyb[:,:,0].shape)# Show the resulting array and save it as jpg imageplt.figure()plt.imshow(img_cl, cmap=cm.YlOrRd)plt.axis('off')plt.savefig("kmeans_train_image.jpg", bbox_inches='tight')plt.show()return classifer# train model
train_dates = ['20190502T113544', '20190502T113205']
train_codes = ['ea36717ca661ca3cca59d5ea43a81afc', '9bbd67cfb0d660551d74decf916b2df2']
model = Nonefor i in range(min(len(train_codes), len(train_dates))):model = train(classifer = model, date = train_dates[i], code = train_codes[i])

这是训练输出。

作者图片

作者图片

看起来相当不错!

预言;预测;预告

现在我们可以在我们的模型上运行预测,看看效果如何。

该功能将接收堆叠的波段和保存的模型。然后,它将在新的映像上运行 KMeans 模型。预测输出将保存为 jpg 文件。

此保存的 jpg 仅用于可视化目的。不要用它做进一步的计算。我不小心那样做了,弄得很困惑。

from tqdm.notebook import tqdmdef get_date_from_orig_image(text):text = os.path.basename(text).split('.')[0].split('_')[-1]datetime_date = datetime.strptime(text, '%Y%m%dT%H%M%S').date()return datetime_datedef get_orig_img_list(code, date):original_images = glob.glob('/content/drive/My Drive/flurosat/*'+code+'_'+date+'*.*g')original_images = sorted(original_images, key = lambda x: get_date_from_orig_image(x))return original_images[0]# predict using new k means method
def predict(model, img_fp):elmanagel = rasterio.open(img_fp)# Read, enhance and show the imageelman_arr = elmanagel.read()vmin, vmax = np.nanpercentile(elman_arr, (5,95))  # 5-95% contrast stretch# create an empty array with same dimensions and data type elman_xyb = np.empty((elmanagel.height, elmanagel.width,elmanagel.count), elmanagel.meta['dtype'])# loop through the raster bands and fill the empty array in x-y-bands orderfor band in range(elman_xyb.shape[2]):elman_xyb[:,:,band] = elmanagel.read(band+1)# convert to 1d arrayelman_1d = elman_xyb[:,:,:elman_xyb.shape[2]].reshape(elman_xyb.shape[0]*elman_xyb.shape[1], elman_xyb.shape[2])# predict the clusters in the image pred = model.predict(elman_1d)# reshape the 1d array predictions to x-y-bands shape order (only one band)elman_cul = predelman_cul = elman_cul.reshape(elman_xyb[:,:,0].shape)return elman_culdef predictions_driver(code, date, iterations):files = get_files_from_code(code, date)original_image = get_orig_img_list(code, date)# # rasterio stack all bandsimg_fp = stack_bands(files)# predict on trained modelkmeans_predictions = predict(model, img_fp)# save kmeansplt.imsave('kmeans_output/'+str(code)+'_'+str(iterations)+'_k_means.jpg', kmeans_predictions, cmap=cm.YlOrRd)# save original imageimg = PIL.Image.open(original_image)img.save('kmeans_output/'+str(code)+'_'+str(iterations)+'_original_image.jpg')returnfor i,combination in enumerate(tqdm(sorted_combinations)):date = combination.split('_')[-1]code = combination.split('_')[0]predictions_driver(code, date, i)

创建网格

现在,我们可以从之前获得的保存图像 jpg 中创建一个图像网格。

这就更容易看出 KMeans 是否真的正确。

codes = list(set([i.split('_')[0] for i in combinations]))for code in codes:k_means = glob.glob('kmeans_output/*'+str(code)+'*k_means.*g')k_means = sorted(k_means, key = lambda x: int(os.path.basename(x).split('.')[0].split('_')[-3]))k_means = [PIL.Image.open(i) for i in k_means]original_imgs = glob.glob('kmeans_output/*'+str(code)+'*original_image.*g')original_imgs = sorted(original_imgs, key = lambda x: int(os.path.basename(x).split('.')[0].split('_')[-3]))original_imgs = [PIL.Image.open(i) for i in original_imgs]full_list = k_means + original_imgsimshow_func(createImageGrid(full_list, rows = 2, scale = 0.1))

作者图片

作者图片

KMeans 输出在第一行。RBG 的原始图像在第二行。我们可以看到它在分离植被和土壤方面做得很好。

需要更多的卫星图像来全面评估其性能。但这是一个良好的开端。

结论

我为悉尼的一家小公司做这项工作。我从这家初创公司的经验丰富的专业人士那里学到了很多东西。没有他们的帮助,我不可能创造出这个。

我希望这篇文章能帮助一些人。当我开始的时候它肯定会帮助我。完整代码可以在 Github 上找到

如果我犯了错误,请在推特上联系我

原载于 2021 年 1 月 10 日https://spiyer 99 . github . io

基于深度学习的地震断层预测

原文:https://towardsdatascience.com/seismic-fault-prediction-with-deep-learning-2935704c9b48?source=collection_archive---------14-----------------------

U-Net 的 PyTorch 实现:第一部分

图片由来自 unsplash 的 Jimmy Conover 提供

地震图像给出了一次地球表面下的结构快照。声波从“源”发送到地下,以不同的速度穿过地球的地层,并在途中被反射、折射或衍射。在地震成像中,我们记录从不同地质层反射回来的波,并将它们叠加以创建 2D 或 3D 图像。因为不同的地质层具有不同的物理性质,在层间的边界处,由于密度对比,波被反射。有不同种类的波,但在成像中我们主要集中在 P 波(压缩波)。下面的漫画给你一个地震采集的例子和采集完所有波后的实际图像。

来源:作者

地震图像示例(来源:力竞赛,见参考文献)

那么是谁的错呢?

断层是由众多物理过程(如压力、重力、板块构造等)结合而成的地质结构。这些是岩石块滑过的裂缝或平面。断层有各种大小,从几米到几英里。圣安地列斯断层是大规模走滑断层(也称为转换断层)的一个例子。有三种主要类型的故障,如下所示:

故障类型由国家公园管理局提供。史蒂文·厄尔改编。公共域(https://opentextbc . ca/physical geology 2 ed/chapter/12-3-fracturing-and-faulting/)

地震资料中断层作图的意义

断层测绘是地震勘探的一个重要方面,因为仔细分析断层有助于了解是否有可能在地下找到石油/天然气储层。在早期勘探阶段,有两点至关重要:

  • 断层可以作为油气运移的通道
  • 它们可能有助于将石油截留在原地

在石油/天然气开发阶段,断层测绘在进行经济分析时至关重要,因为断层通过改变流体渗透率来影响储层的流体动力学。这将直接影响油气区的体积以及油田开发的机械工程方面。在浅层地下,断层的存在给钻井带来危险。断层的正确识别将允许操纵钻头,使得尽可能避免任何断层区域。

最后,大比例尺断层制图有助于理解地球上发生的区域性地球动力学过程。这对于理解地震、火山爆发、山体滑坡等自然灾害至关重要。

我们可以看到断层识别的许多好处,特别是在油气勘探中。因此,在地震勘探中已经进行了大量的努力来精确地识别和绘制断层。然而,人工绘制断层图是一个繁琐的过程,即使在一个小的勘测区域也要花费数天或数周的时间。

那么,我们是否可以使用机器学习的帮助来加快故障识别呢?

断层测绘和预测

随着深度神经网络的发展,有可能训练地震图像来创建能够识别地震数据中的断层的模型。在本文中,我将带您了解一个深度学习框架,它可以根据地震数据预测断层。在这项研究中,我使用了 Force Competition(www . xeek . ai)提供的合成地震数据。

数据探索

这项研究的数据采用地震数据行业标准 SEG-Y 格式( SEG 代表勘探地球物理学家协会)。这是专门的数据格式,我使用了一个名为“segyio”的 python 库来读取这种类型的数据。由于我使用的是 3D 数据集,segyio 可以很容易地将数据转换成 3D NumPy 数组。下面显示了一个 3D 网格结构的例子,其中也显示了三个平面表面:线内,交叉线& z 切片。这些是地震行业中常用于可视化数据的 2D 表面。我的 github 页面的链接将在这篇文章的结尾提供。

一个三维地震网格,带有 2D 地震显示示例,沿直线方向,带有断层叠加(图片由作者提供)

深度卷积网络

断层测绘可以被归类为一种类型的图像分割任务。因为我们希望神经网络的输出是全分辨率图像,所以一个 U-Net 框架适合这种类型的任务。U-Net 框架最初是为生物医学技术领域的图像分割任务而开发的。U-Net 框架的一般示意图如下所示:

U-Net 框架(图片由 Rachel Zhiquing Zheng 提供,见参考资料)

U 形网有两条不同的流动路径:

I)包括几个下采样步骤的前向收缩路径。这是 U-Net 的编码器部分,它涉及两个 3×3 卷积,后跟一个 ReLU、一个 2×2 最大池和一个用于下采样的步长 2。最初的 U-Net 实现使用“无填充”卷积,这导致最终输出的尺寸减小。

ii)涉及几个上采样步骤的反向扩展路径。这是解码器部分,包括特征映射的上采样、3×3 卷积和来自前一收缩块的特征映射的级联,其后是每个都具有 ReLU 激活的 3×3 卷积。

这两个步骤的代码片段如下:

其中,
x:形状的图像张量(批量大小、通道、高度、宽度)
skip_con_x:来自收缩路径的图像张量

注意:在最初的 U-Net 实现中,输出形状小于输入,这需要跳过连接层大小以与当前层匹配。在这种情况下,应裁剪跳过连接图层,以匹配上采样和卷积后的图层大小。此外,需要将填充设置为 1,以确保最终的输出形状与输入形状匹配。

接下来,我们需要一个最终的代码块,它将输出具有相同输入大小的张量。

总的来说,最终的 U-Net 块在网络的开始和起点具有 4 个收缩块和 4 个扩展块以及特征映射块。

训练数据

我将在这项研究中使用两卷数据:地震立方体和断层立方体,其中地震立方体将是训练数据,断层立方体将是标签数据。断层数据具有一系列手动绘制的表面,这些表面为 0 和 1。左边的图像是一条直线方向上的 2D 地震显示,而右边是覆盖着断层的同一显示。

有断层覆盖的地震实例

您能注意到左图中故障的实际外观吗?

所提供的原始输入数据具有(101,589,751)的形状。这意味着有 101 条内线、589 条交叉线和 751 个样本。注意,地震图像不是 RGB 图像,因此它们可以被视为单通道灰度图像。内联的数量可以被视为批量大小,沿着内联我们将得到形状为 589 x 751 像素的 2D 图像。

所需的输入张量大小为:(批量大小、通道、高度、宽度)。
因此,我们的输入张量的形状为(101,1,589,751),其中 1 表示单通道。然而,奇数大小最初会引起一些问题,这导致我裁剪输入体积以得到(101,1,512,512)的形状

模特培训

下面列出了一般的训练参数。到目前为止,我只训练了最大历元数为 25 的模型,但我们将看到,对于这个数据集,模型在大约 10 个历元时开始非常有效地挑选故障。我在 NVIDIA GeForce RTX 2060 Super 上运行该模型,内存为 8GB,每次批量为 1。

结果

下面的图片是在 3 个不同的时期随机收集的。请注意,在纪元 4 时,模型已经开始感觉到断层,到纪元 19 时,它已经设法非常精确地绘制出断层。

如果我们观察模型性能,损失函数在 5 个时期内急剧下降,并在大约 15 个时期内保持稳定。模型丢失的快速下降可能是因为用于训练的干净的合成数据。

摘要

这个小实验展示了深度学习的力量,以及在输入数据没有噪声的情况下,如何能够相对快速地映射故障。然而,在现实世界中,地震数据非常杂乱,充满了噪声,这可能会对模型的准确性造成重大障碍。如果我们使用来自世界各地不同盆地的各种各样的地震数据集来训练该模型,该模型可以很好地概括。在下一部分中,我将重点介绍在 Schlumberger 为本次比赛提供的真实数据集上实现该模型。

敬请期待!

Github 链接:【https://github.com/sgautam666

参考

鲍曼,奥桑德,迪里布,迪辛顿,曼拉尔,2020。2020 原力机器学习大赛。https://github . com/bolgebrygg/Force-2020-机器学习-竞赛

初期数据探索:https://www . LinkedIn . com/pulse/force-fault-mapping-competition-exploring-Data-Ben-lass cock/?trk = public _ profile _ article _ view

郑志清,图像分割:用 Carvana 数据预测图像掩模,走向数据科学

基于互信息的机器学习模型特征选择

原文:https://towardsdatascience.com/select-features-for-machine-learning-model-with-mutual-information-534fe387d5c8?source=collection_archive---------3-----------------------

为什么我们需要为机器学习模型挑选特征,如何使用互信息来选择特征,以及使用 Scikit-Learn 包中的互信息工具。

图片来自unsplash.com

特征选择问题

当用一组适当的训练数据进行训练时,机器学习模型是惊人的。教科书中描述的 ML 模型和使用来自 Scikit-learn 的数据集,样本数据集已经被仔细策划并有效地用于 ML 模型。

问题是,在现实生活中,训练数据可能是海量的,根本不适合任何模型。例如,您希望训练一个模型,根据当前 100 多个客户属性(如年龄、位置、国家/地区、活跃访问次数、上次访问时间等等)来预测下个月的收入。

有些属性可能有用,有些可能完全没用,比如客户名称。

难怪许多 ML 专家说,为 ML 选择正确的特性是最重要的步骤之一,甚至比 ML 模型本身还重要!

相互信息是如何工作的

互信息可以回答这个问题:有没有一种方法可以在一个特性和目标之间建立一个可测量的联系。

使用互信息作为特征选择器有两个好处:

  • MI 是模型中立的,这意味着该解决方案可以应用于各种 ML 模型。
  • MI 解的快。

那么,什么是互信息呢?

如果你熟悉决策树分类器。它与我在另一篇文章中描述的信息增益 100%相同,理解决策树分类器。

互信息测量目标值条件下的熵下降。我发现对这个概念最干净的解释是这个公式:

MI(feature;target) = Entropy(feature) - Entropy(feature|target)

MI 分数将在从 0 到∞的范围内。值越高,该特征和目标之间的联系越紧密,这表明我们应该将该特征放在训练数据集中。如果 MI 分数为 0 或非常低,如 0.01。低分表明该特征和目标之间的联系很弱。

使用 Scikit 中的交互信息——学习 Python

您可以自己编写一个 MI 函数,或者使用 Scikit-Learn 中现成的函数。

我将使用来自 Scikit-Learn 的乳腺癌数据集来构建一个应用了互信息的样本 ML 模型。使用决策树分类器从癌症数据中训练 3 个数据集,并比较结果,以查看 MI 分数将如何影响 ML 模型的有效性。

  1. 训练数据集 1,使用所有特征。
  2. 训练数据集 2,仅使用 MI 分数大于 0.2 的要素
  3. 训练数据集 3,仅使用 MI 分数小于 0.2 的要素

第一步。加载乳腺癌数据

from sklearn.datasets import load_breast_cancer as LBC
cancer = LBC()
X = cancer['data']
y = cancer['target']

第二步。计算 MI 分数

from sklearn.feature_selection import mutual_info_classif as MIC
mi_score = MIC(X,y)
print(mi_score)

您将看到 mi_score 数组,如下所示:

[0.37032947 0.09670886 0.40294198 0.36009957 0.08427789 0.213181140.37337734 0.43985571 0.06456878 0.00276314 0.24866738 0.001891630.27600984 0.33955538 0.01503326 0.07603828 0.11825812 0.128794020.0096701  0.03802394 0.45151801 0.12293047 0.47595645 0.464261020.09558928 0.22647456 0.31469449 0.43696443 0.0971793  0.06735096]

30 个数字代表 30 个特征的 MI 分数。

第三步。构建 3 个训练数据和测试数据集

无 MI 评分治疗的数据集 1,名称标有_1。注意,所有三个数据集将使用相同的y_trainy_test。因此,不需要分离目标数据。

from sklearn.model_selection import train_test_split as tts
X_train_1,X_test_1,y_train,y_test = tts(X,y,random_state=0,stratify=y
)

包含 MI 分数大于 0.2 的要素的数据集 2

import numpy as np
mi_score_selected_index = np.where(mi_scores >0.2)[0]
X_2 = X[:,mi_score_selected_index]
X_train_2,X_test_2,y_train,y_test = tts(X_2,y,random_state=0,stratify=y
)

你会看到 X_2 的形状是(569,15)而不是(569,30)。这是因为 15 个特征的 Mi 分数大于0.2

包含 Mi 分数小于 0.2 的要素的数据集 3

mi_score_selected_index = np.where(mi_scores < 0.2)[0]
X_3 = X[:,mi_score_selected_index]
X_train_3,X_test_3,y_train,y_test = tts(X_3,y,random_state=0,stratify=y
)

巧合的是,0.2 MI 分数将 30 个特征分成 15 个和 15 个。

第四步。用决策树分类器比较 3 个数据集

from sklearn.tree import DecisionTreeClassifier as DTC
model_1 = DTC().fit(X_train_1,y_train)
model_2 = DTC().fit(X_train_2,y_train)
model_3 = DTC().fit(X_train_3,y_train)
score_1 = model_1.score(X_test_1,y_test)
score_2 = model_2.score(X_test_2,y_test)
score_3 = model_3.score(X_test_3,y_test)
print(f"score_1:{score_1}\n score_2:{score_2}\n score_3:{score_3}")

结果是:

score_1:0.9300699300699301
score_2:0.9370629370629371
score_3:0.8251748251748252

看,包含 15 个要素的数据集 2 的 MI > 0.2,精度达到 0.93,与包含所有要素的数据集 1 一样好。而 score_3 仅为 0.82,这是 15 个特征的结果,这些特征具有 MI score < 0.2.

From this sample, it is clear that the MI score can be used as a signal for feature selection.

Use the feature selector from Scikit-Learn

In real ML projects, you may want to use the top n features, or top n percentile features instead of using a specified number 0.2 like the sample above. Scikit-Learn also provides 许多选择器作为方便的工具。以便您不必手动计算 MI 分数并获取所需的特征。下面是选择前 50%特性的示例,其他选择器也有类似的用法。

from sklearn.feature_selection import SelectPercentile as SP
selector = SP(percentile=50) # select features with top 50% MI scores
selector.fit(X,y)
X_4 = selector.transform(X)
X_train_4,X_test_4,y_train,y_test = tts(X_4,y,random_state=0,stratify=y
)
model_4 = DTC().fit(X_train_4,y_train)
score_4 = model_4.score(X_test_4,y_test)
print(f"score_4:{score_4}")

参考链接

  • 机器学习的信息增益和互信息
  • 1.13。功能选择
  • 理解决策树分类器

附录——准则

您可以在 Jupyter 笔记本或 vscode python interactive 窗口中复制并运行以下代码。

#%% load cancer data
from sklearn.datasets import load_breast_cancer as LBC
cancer = LBC()
X = cancer['data']
y = cancer['target']#%% compute MI scores
from sklearn.feature_selection import mutual_info_classif as MIC
mi_scores = MIC(X,y)
print(mi_scores)#%% prepare dataset 1
from sklearn.model_selection import train_test_split as tts
X_train_1,X_test_1,y_train,y_test = tts(X,y,random_state=0,stratify=y
)#%% prepare dataset 2, MI > 0.2
import numpy as np
mi_score_selected_index = np.where(mi_scores >0.2)[0]
X_2 = X[:,mi_score_selected_index]
X_train_2,X_test_2,y_train,y_test = tts(X_2,y,random_state=0,stratify=y
)#%% prepare dataset 3, MI <0.2
mi_score_selected_index = np.where(mi_scores < 0.2)[0]
X_3 = X[:,mi_score_selected_index]
X_train_3,X_test_3,y_train,y_test = tts(X_3,y,random_state=0,stratify=y
)#%% compare results with Decision Tree Classifier
from sklearn.tree import DecisionTreeClassifier as DTC
model_1 = DTC().fit(X_train_1,y_train)
model_2 = DTC().fit(X_train_2,y_train)
model_3 = DTC().fit(X_train_3,y_train)
score_1 = model_1.score(X_test_1,y_test)
score_2 = model_2.score(X_test_2,y_test)
score_3 = model_3.score(X_test_3,y_test)
print(f"score_1:{score_1}\n score_2:{score_2}\n score_3:{score_3}")#%% use Scikit-learn feature selector
from sklearn.feature_selection import SelectPercentile as SP
selector = SP(percentile=50) # select features with top 50% MI scores
selector.fit(X,y)
X_4 = selector.transform(X)
X_train_4,X_test_4,y_train,y_test = tts(X_4,y,random_state=0,stratify=y
)
model_4 = DTC().fit(X_train_4,y_train)
score_4 = model_4.score(X_test_4,y_test)
print(f"score_4:{score_4}")

从 Pandas 数据框架中选择多个列

原文:https://towardsdatascience.com/select-multiple-cols-pandas-24eab7144fb2?source=collection_archive---------32-----------------------

讨论如何在 pandas 中从数据帧中选择多列

照片由 Aviv Ben 或在 Unsplash 上拍摄

介绍

多列选择是最常见和最简单的任务之一。在今天的简短指南中,我们将讨论从 pandas 数据框架中选择多列的几种可能方法。具体来说,我们将探索如何做到这一点

  • 使用基准分度
  • **loc**
  • 使用**iloc**
  • 通过创建一个新数据帧

此外,我们将根据您的特定用例以及您是否需要生成原始 DataFrame 对象的视图或副本,讨论何时使用一种方法而不是另一种方法。

首先,让我们创建一个示例数据框架,我们将在本文中引用它来演示一些概念。

import pandas pd df = pd.DataFrame({'colA':[1, 2, 3], 'colB': ['a', 'b', 'c'],'colC': [True, False, True],'colD': [1.0, 2.0, 3.0],
})print(df)*colA colB   colC  colD
0     1    a   True   1.0
1     2    b  False   2.0
2     3    c   True   3.0*

使用基本索引

当从现有的 pandas 数据框架中选择多个列时,您的第一个选择是使用基本索引。当您确切知道要保留哪些列时,这种方法通常很有用。

因此,您可以通过使用[]符号(相当于 Python 类中的[__getitem__](https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#basics)实现)传递带有名称的列表,获得只包含这些列的原始数据帧的副本。

df_result = **df[['colA', 'colD']]**print(df_result)*colA  colD
0     1   1.0
1     2   2.0
2     3   3.0*

如果您想了解更多关于 Python 中索引和切片的工作原理,请务必阅读下面的文章。

使用 iloc

或者,如果您想引用列索引而不是列名并分割原始数据帧(例如,如果您想保留前两列,但您并不真正知道列名),您可以使用**iloc**

df_result = **df.iloc[:, 0:2]**print(df_result)*colA colB
0     1    a
1     2    b
2     3    c*

注意,上面的操作,将返回原始数据帧的视图。这意味着视图将只包含原始数据帧的一部分,但它仍将指向内存中的相同位置。因此,如果您修改切片对象(df_result),那么这也可能影响原始对象(df)。

如果您希望分割数据帧,但却得到原始对象的副本,那么只需调用copy():

df_result = **df.iloc[:, 0:2].copy()**

使用 loc

现在,如果您想使用实际的列名对原始数据帧进行切片,那么您可以使用loc方法。例如,如果您想要获得前三列,您可以通过引用您想要保留的列的名字和姓氏,使用loc来实现:

df_result = df.loc[:, 'colA':'colC']print(df_result)*colA colB   colC
0     1    a   True
1     2    b  False
2     3    c   True*

此时,您可能想要了解 Pandas 中lociloc之间的差异,并根据您的具体需求和使用案例阐明使用哪一个。

[## 熊猫中的 loc 与 iloc

towardsdatascience.com](/loc-vs-iloc-in-pandas-92fc125ed8eb)

创建新的熊猫数据框架

最后,您甚至可以只使用原始数据帧中包含的列的子集来创建新的数据帧,如下所示。

df_result = **pd.DataFrame(df, columns=['colA', 'colC'])**print(df_result)*colA   colC
0     1   True
1     2  False
2     3   True*

最后的想法

在今天的简短指南中,我们展示了从 pandas 数据框架中选择多个列的几种可能的方法。我们讨论了如何使用简单的索引、ilocloc以及通过创建新的数据帧来实现这一点。请注意,本文中讨论的一些方法可能会生成原始数据帧的视图,因此您应该格外小心。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。

你可能也会喜欢

如何根据列值从 PySpark 数据帧中选择行

原文:https://towardsdatascience.com/select-rows-pyspark-df-based-on-column-values-3146afe4dee3?source=collection_archive---------4-----------------------

探索如何根据 PySpark 数据帧中的特定条件选择一系列行

Anthony Yin 在 Unsplash 上拍摄的照片

介绍

过滤数据帧行是 PySpark 中最常执行的操作之一。在今天的简短指南中,我们将讨论如何以几种不同的方式根据特定条件选择一系列行。

具体来说,我们将探索如何使用

  • filter()功能
  • where()功能
  • Spark SQL

首先,让我们创建一个示例数据框架,我们将在本文中引用它来演示几个概念。

from pyspark.sql import SparkSession# Create an instance of spark session
spark_session = SparkSession.builder \.master('local[1]') \.appName('Example') \.getOrCreate()df = spark_session.createDataFrame([(1, True, 1.0, 100),(2, False, 2.0, 200),(3, False, 3.0, 300),(4, True, 4.0, 400),(5, True, 5.0, 500),],['colA', 'colB', 'colC', 'colD']
)df.show()
*+----+-----+----+----+
|colA| colB|colC|colD|
+----+-----+----+----+
|   1| true| 1.0| 100|
|   2|false| 2.0| 200|
|   3|false| 3.0| 300|
|   4| true| 4.0| 400|
|   5| true| 5.0| 500|
+----+-----+----+----+*

使用 filter()函数选择行

过滤数据帧行的第一个选项是基于指定条件执行过滤的[pyspark.sql.DataFrame.filter()](https://spark.apache.org/docs/latest/api/python/reference/api/pyspark.sql.DataFrame.filter.html)函数。

例如,假设我们只想保留那些在colC中的值大于或等于3.0的行。下面的表达式可以解决这个问题:

**df = df.filter(df.colC >= 3.0)**df.show()
*+----+-----+----+----+
|colA| colB|colC|colD|
+----+-----+----+----+
|   3|false| 3.0| 300|
|   4| true| 4.0| 400|
|   5| true| 5.0| 500|
+----+-----+----+----+*

您甚至可以指定Column函数,比如pyspark.sql.Column.between,以便只保留指定的下限和上限之间的行,如下所示。

**df = df.filter(df.colD.between(200, 400))**df.show()
*+----+-----+----+----+
|colA| colB|colC|colD|
+----+-----+----+----+
|   2|false| 2.0| 200|
|   3|false| 3.0| 300|
|   4| true| 4.0| 400|
+----+-----+----+----+*

使用 where()函数选择行

[pyspark.sql.DataFrame.where()](https://spark.apache.org/docs/3.1.1/api/python/reference/api/pyspark.sql.DataFrame.where.html)是我们在上一节中讨论的filter()的别名。它可以以同样的方式使用,以便根据提供的条件过滤数据帧的行。

**df = df.where(~df.colB)**df.show()
*+----+-----+----+----+
|colA| colB|colC|colD|
+----+-----+----+----+
|   2|false| 2.0| 200|
|   3|false| 3.0| 300|
+----+-----+----+----+*

使用 Spark SQL 选择行

或者,您甚至可以使用 Spark SQL 来使用 SQL 表达式查询数据帧。举个例子,

# Create a view for the dataframe
df.createOrReplaceTempView("df_view")**df = spark_session.sql("""SELECT * FROM df_viewWHERE colC >= 2.0"""
)**df.show()
*+----+-----+----+----+
|colA| colB|colC|colD|
+----+-----+----+----+
|   2|false| 2.0| 200|
|   3|false| 3.0| 300|
|   4| true| 4.0| 400|
|   5| true| 5.0| 500|
+----+-----+----+----+*

最后的想法

在今天的简短指南中,我们讨论了如何根据特定条件从 PySpark 数据帧中执行行选择。具体来说,我们展示了如何使用filter()where()方法以及 Spark SQL 来实现这一点。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

你可能也会喜欢

Spark 中的 select()与 selectExpr()

原文:https://towardsdatascience.com/select-vs-selectexpr-in-spark-412a59416615?source=collection_archive---------7-----------------------

讨论 Spark 中 select()和 selectExpr()方法的区别

亚历山大·席默克在 Unsplash 上拍摄的照片

介绍

列选择无疑是 Spark 数据帧(和数据集)上最常用的操作之一。Spark 有两个内置的方法可以用来这样做,即select()selectExpr()

在今天的文章中,我们将讨论如何使用它们,并解释它们的主要区别。此外,我们将讨论何时应该使用其中一种。

在讨论如何使用select()selectExpr()进行选择之前,让我们创建一个样本数据帧,作为本文的参考

from pyspark.sql import SparkSessionspark_session = SparkSession.builder \.master('local[1]') \.appName('Example') \.getOrCreate()df = spark_session.createDataFrame([(1, 'Giorgos', 'Developer', 28),(2, 'Andrew', 'Architect', 31),(3, 'Maria', 'Manager', 31),(4, 'Ben', 'Developer', 29),],['id', 'name', 'occupation', 'age']
)df.show()+---+--------+----------+---+
| id|    name|occupation|age|
+---+--------+----------+---+
|  1| Giorgos| Developer| 28|
|  2|  Andrew| Architect| 31|
|  3|   Maria|   Manager| 31|
|  4|     Ben| Developer| 29|
+---+--------+----------+---+

选择()

[pyspark.sql.DataFrame.select()](https://spark.apache.org/docs/latest/api/python/reference/api/pyspark.sql.DataFrame.select.html)是一个转换函数,它返回一个新的数据帧,其中包含输入中指定的所需列。它接受一个参数columns,可以是strColumnlist,以防您想要选择多个列。该方法投射一组表达式,并将返回一个新的 Spark 数据帧。

以下表达式将返回一个包含原始数据帧所有列的新数据帧:

df.select('*').show()+---+--------+----------+---+
| id|    name|occupation|age|
+---+--------+----------+---+
|  1| Giorgos| Developer| 28|
|  2|  Andrew| Architect| 31|
|  3|   Maria|   Manager| 31|
|  4|     Ben| Developer| 29|
+---+--------+----------+---+

如果您只想选择列的子集,可以如下所示进行操作:

df.select('id', 'name').show()+---+--------+
| id|    name|
+---+--------+
|  1| Giorgos|
|  2|  Andrew|
|  3|   Maria|
|  4|     Ben|
+---+--------+

selectExpr()

[pyspark.sql.DataFrame.selectExpr()](https://spark.apache.org/docs/3.1.1/api/python/reference/api/pyspark.sql.DataFrame.selectExpr.html)select()相似,唯一的区别是它接受将要执行的 SQL 表达式(字符串格式)。同样,该表达式将根据提供的输入从原始数据中返回一个新的数据帧。

另外,与select()不同,这个方法只接受字符串。例如,为了将列age乘以 2 以及idname列,您需要执行以下语句:

df.selectExpr('id', 'name', 'age * 2').show()+---+--------+-------+
| id|    name|age * 2|
+---+--------+-------+
|  1| Giorgos|     28|
|  2|  Andrew|     31|
|  3|   Maria|     31|
|  4|     Ben|     29|
+---+--------+-------+

因此,当您只需要从一个特定的 Spark 数据帧中选择一个列的子集时,select()方法非常有用。另一方面,当您需要选择特定的列,同时还需要对特定的列应用某种转换时,selectExpr()就派上了用场。

最后的想法

在今天的文章中,我们讨论了 Spark 中select()selectExpr()方法的区别。我们展示了如何使用两者对 Spark 数据帧进行选择,并强调了何时使用其中一个。

你可以在下面阅读更多关于 Spark 的文章。

利用连续的人在回路中的搜索空间修改来选择超参数值

原文:https://towardsdatascience.com/selecting-hyperparameter-values-with-sequential-human-in-the-loop-search-space-modification-766d272ed061?source=collection_archive---------21-----------------------

照片由丹尼斯·莱昂在 Unsplash 上拍摄

思想与理论,超参数优化

使用人在回路中的迭代方法进行超参数优化,让您对超参数值的选择充满信心

大声喊出来 Zeev Waks 谁参与了这个项目和这篇文章的写作。

介绍

搜索一组好的超参数,也称为超参数调整或超参数优化(HPO),通常是机器学习模型开发中最耗时和最昂贵的方面之一。这是因为超参数搜索空间随着每个附加参数的组合增长而变得巨大,数据科学家倾向于测试许多组合以寻找好的结果。因为评估每个超参数集需要训练模型(并计算验证集的评估度量),所以搜索最佳超参数组合会导致数百、数千甚至数百万个单独模型的训练。因此,制定 HPO 战略非常重要,因为它会对成本、开发时间,当然还有预测性能产生重大影响。

HPO 的传统方法试图一次找到一组好的超参数,通常使用网格、随机或贝叶斯搜索方法(点击阅读更多关于贝叶斯优化)。事实上, auto-ml 方法通常基于一次性贝叶斯搜索方法,如基于序列模型的优化(SMBO)及其扩展版本[1,2]。贝叶斯方法的巨大优势在于,它们使用来自历史运行的结果,或以超参数值/目标函数得分对的形式逐渐增加的先验知识,来通知选择下一个要评估的超参数值。这导致更快地获得好的目标函数分数,从而节省时间和计算成本。事实上,在大多数情况下,使用贝叶斯方法可能是最好的选择。

然而,你怎么能相信贝叶斯方法,比如流行的树结构 Parzen 估计器(TPE ),没有解决一个次优的局部最小值,而是充分探索了超参数搜索空间?如果初始搜索空间不包含一个或多个超参数的理想范围,您该怎么办?

在这篇博客中,我们介绍了一种替代单次 HPO 的方法,在这种方法中,我们进行了一系列的探索运行,以在进行最终的 HPO 运行之前迭代地修改搜索空间。我们的目标是双重的:更好地理解超参数搜索空间,并获得信心,我们选择的超参数值确实是好的,而不是次优的局部最小值。

我们的用例是公寓租金预测,我们使用的是由 Preferred Networks 公司开发的相对较新的开源框架 Optuna 。

用例:租金预测

在 Skyline AI,我们从大量来源收集数据,并构建机器学习模型,帮助做出明智的投资决策。数据和模型预测显示在一个基于网络的平台上,供房地产专业人士使用。

我们的一个模型旨在预测一套公寓的月租金,给出它的特征,例如卧室的数量、便利设施的存在以及它的位置。为了做到这一点,我们使用了多户住宅中成千上万套公寓的数据。每个公寓都有一个包含几十个特征的特征向量。因此,这是一个监督回归问题,我们使用平均绝对百分比误差(MAPE)来衡量预测性能。

作者图片

还有一些其他的预测租金的方法,有明显的区别,比如单户和多户,不同的地理位置,甚至不同的目标,但是深入探讨这些已经超出了这篇博客的范围。以下是感兴趣者的简短列表:

  • 基于联合模型的房租预测[3]
  • 使用大型数据集进行公寓租金价格预测的比较:克里金法与 DNN 法[4]
  • 使用文本信息和深度学习对亚特兰大大都市区的房屋租金进行建模[5]
  • 租 Zestimate (Zillow)

奥普图纳

有许多针对 HPO 的框架,也有一些很棒的博客和评论对它们进行了比较( 10 个超参数优化框架、 Optuna vs Hyperopt 、使用 Optuna 获得精确的 Scikit 学习模型:一个超参数框架)。在评估了几个选项之后,我们选择使用 Optuna,这是一个由 Preferred Networks,Inc .开发的相对较新的 Pythonic 开源框架。

Optuna 有几个不错的特性,包括:

  • 易用性,本质上是 Pythonic 式的。
  • 良好的文档有许多代码示例
  • 选择保存并恢复搜索过程
  • 允许定义定制的超参数,例如是否使用特定的预处理步骤
  • 自定义目标函数和用户属性
  • 几个超参数搜索空间采样器的实现,包括使用 TPE (树形结构 Parzen 估计器)算法的贝叶斯采样器
  • 好的可视化套件
  • 独特的功能,如修剪、多目标优化、回调和异常处理
  • 支持跨机器运行的分布式,以及支持单个机器内的并行性(在不同的内核上同时运行多个试验),尽管目前后者并未达到最佳工作状态(已知问题)。
  • 支持在单次运行中评估特定的超参数值,可用于测试您的代码( FixedTrial )

搜索空间

我们的超参数搜索空间包含 9 个不同的超参数,跨越模型开发的不同领域,包括预处理(训练数据选择,PCA)、特征工程(分类编码)、模型选择( RandomForest 、 XGBoost )、模型参数选择(树的数量、树的深度)和标签变换(自然对数或无变换)。我们决定只从离散分布中采样,因为这样我们将能够量化超参数搜索空间的大小并测量采样覆盖范围。

顺便提一下,对于分类编码器,我们使用了一个叫做״category_encoders״的伟大的 Python 库(源代码,阅读更多)。

总的来说,我们的起始搜索空间包含 5 x 29 x 6 x 2 x2 x 9 x 16 = 501,120 个组合。

初始搜索空间

基线结果

我们使用手动选择的超参数值构建了一个模型,以便获得一个基线结果来进行后续比较。我们获得了 9.53% 的 MAPE。所选择的超参数值是 5%的最大当前差异、无 PCA、序数编码、具有 100 个估计量的 XGBoost、无最大树深度限制和标签原样(无对数变换)。

贝叶斯一次性 HPO

我们首先使用 Optuna 的 TPE 实现( TPESampler )执行经典的一次性 HPO 方法,这是一种基于贝叶斯概率的搜索空间搜索优化算法。在 21,900 次试验后,我们获得了 8.80%的最小 MAPE,这在仅仅 1,114 次试验后就已经达到了。这比没有 HPO 获得的 9.53%的基线结果好得多,但是我们注意到一些有趣的事情。

重复评估相同的超参数值非常普遍,这是一个已知的问题(参见讨论)。在 21,900 次试验中,只有 940 个不同的超参数组合,而最佳超参数组合被评估了 8,200 多次,几乎每 5 次试验中就有 2 次!

作者图片

事实上,我们只采样了 0.19%,而不是我们搜索空间的近 5%。这使我们怀疑 TPESampler 是否达到了次优的局部最小值,因为目标函数很可能不是凸的。虽然在我们的情况下不太可能,但在高维度的情况下,低采样覆盖率可能会更成问题,因为贝叶斯优化方法在超参数空间的维度非常大的情况下类似于随机搜索[6,7]。

覆盖率低的另一个问题是,我们不知道如何修改我们的搜索空间,以便在后续的 HPO 运行中寻找更好的 MAPE。我们将减少哪些超参数范围,应该增加哪些范围,以及在特定范围内应该在哪里减少区间步长(或者甚至使范围连续)?

使用搜索空间修剪的顺序 HPO

接下来,我们决定尝试顺序 HPO 随机搜索,以便更加确信我们确实选定了一个好的搜索空间(包含具有理想结果的超参数组合),并且这将使我们获得目标函数的一个好的局部,甚至全局最小值。

这个想法非常简单,类似于计算贝叶斯方法,只是它涉及到人工判断。我们随机采样(使用 RandomSampler )我们的搜索空间,找到产生差结果的超参数值范围,并删除它们。我们还可以在我们最初的直觉错误的地方扩大搜索空间(范围太小)。无偏采样是随机搜索的一个固有方面,如果需要额外的彻底性,它可以统计量化移除良好超参数值的可能性,但有一个很大的警告,它需要假设超参数之间的独立性。因为通常不是这样,所以实际值很可能是不正确的,但是它可能被用作一个大概的数字。

从理论上讲,可能存在这样一种情况,即搜索空间的减少可能已经移除了最佳超参数组合,因为超参数可以相互作用,然而在我们的情况下,并且可能在许多其他情况下,这似乎不是一个大的风险。

使用搜索空间剪枝流程的顺序 HPO。作者图片

为了避免混淆,请注意 Optuna 的修剪功能引用了其他功能。Optuna 的修剪能够停止表现不佳的训练过程以节省时间,这类似于模型训练中的早期停止,而我们描述的顺序 HPO 修剪是修改(增加或减少)超参数搜索空间的过程。

顺序修剪—第一次迭代

我们对 25,056 次试验进行了第一次修剪研究,覆盖了大约 5%的超参数搜索空间。我们在与单次 HPO 相同的搜索空间上运行它(参见“搜索空间”一节)。如上所述,我们使用随机搜索( RandomSampler )来以不偏不倚的方式探索搜索空间。最佳模型获得了 8.77% 的 MAPE。令人惊讶的是,这甚至比我们在相同搜索空间(21,900 次尝试)上的贝叶斯搜索还要好一点,结果是 8.80%!

看一下目标函数结果(验证集上的 MAPE)如何随着单个试验而变化,就像通过 Optuna 的可视化套件所可视化的那样,很明显搜索空间可以被修整。

作者图片

为了缩小搜索空间,我们使用 Optuna 的切片图可视化来查看九个单独的超参数。下面是我们修改了范围的四个超参数的切片图。

作者图片

基于这些,我们决定移除以下超参数值:train_rent_diff = 0.01,regressor = "RandomForest ",n_estimators = {40,60},max_depth = {2,16–32(step = 2)}。这导致搜索空间减少了 88%,从 501,120 减少到 58,464,从而使我们能够使用更少的尝试更自信地搜索良好的局部最小值,并获得更高的搜索空间覆盖。

对于额外的严格性,假设我们使用随机搜索,如果我们假设超参数值是独立的,则可以统计量化我们不正确移除超参数值的可能性。例如,前 708 个结果是使用 XGBoost 作为模型获得的,尽管随机森林的试验数量(10,401)与 XGBoost (10,425)相似。请注意 10425+10401< 25,056 since there were 4,230 duplicated trials (17%).

Not surprisingly, 3 of the 4 hyperparameters whose search space we modified were of the highest importance as quantified by Optuna. The plot is also a built-in capability of the package.

Image by author

Sequential pruning — 2nd iteration

We continued by running a second random search iteration covering 20% of the search space (4 x 29 x 6 x 2 x 1 x 7 x 6 = 58,464 combinations), this time with less trials, 11,693. Again, we obtained an improvement in MAPE compared to the last iteration, although not too substantial this time, 8.74%vs 8.77%。

搜索空间修剪的一个很好的效果是,我们删除了所有以大的目标函数分数结束的试验,其中在第一次迭代中有许多(参见上面的三个图)。

作者图片

在分析单个超参数值后,我们决定减少一些超参数的搜索空间(删除 train_rent_diff=0.02,删除 PCA 选项,删除 n_estimators=80,仅保留目标编码器选项),增加一些超参数的分辨率(max_depth 值以 1 而不是 2 的较小增量,范围 4-11),甚至扩大目标编码器超参数的范围(te_min_samples_leaf 和 te_smoothing 以 2 的增量增加到 1-11)。下面是我们用来做这些决定的图表。

作者图片

作者图片

调整之后,新的搜索空间自信地比我们的起点 501120 种可能性减少了 96% !以下是结果搜索空间(3 x 36 x 1 x 2 x 1 x 11 x 8 = 19,008 个组合)。

最终搜索空间

顺序修剪—最后一次运行

我们现在已经准备好最后一次跑步了。我们使用贝叶斯搜索( TPESampler ),因为我们不再需要修改搜索空间,因此不再需要无偏采样。相反,我们希望有好的结果,并且很少尝试。

我们只对 HPO 的研究进行了 500 次试验,发现最好的结果是之后的****只有 49 次试验,MAPE 为 8.55% 。事实证明,这是全局最小值,通过网格搜索( GridSampler )得到了验证。重要的是,这比我们在初始搜索空间使用贝叶斯抽样得到的 8.80%的 MAPE 要好得多。

作者图片

最佳模型超参数值为:最大租金差异 4%,无 PCA,平滑=1 且 min_sample_leaf=11 的目标编码,具有 280 个估计器的 XGBoost,最大树深度 5,以及自然对数转换标签。请注意,这些超参数值中的一些不在我们的初始搜索空间中!这突出了迭代的另一个优势——它们可以发现由于错误的直觉而导致的太小的搜索空间

附加可视化

Optuna 使我们能够通过绘制试验中获得的目标函数得分的经验累积分布函数(即 eCDF )来比较不同的 HPO 研究。它是“经验的”,因为 eCDF 是累积分布函数的估计值(即 CDF ),但出于分析目的,它本质上是相同的。利用这一点,我们可以很容易地看到,在每一个连续的剪枝迭代中,个体试验达到目标函数分数的几率显著增加。

我们可以看到,在每次连续修剪中,单个试验获得低 MAPEs 的几率增加。例如,对于前两次修剪迭代,获得小于 9.5 的 MAPE 值的概率分别是 2%和 15%,而对于最后的贝叶斯运行,它跳到 99.6%。图片作者。

再次与贝叶斯进行比较

我们从 501k 个组合的搜索空间开始,但是,这个空间不包含我们的最佳结果。我们所有连续 HPO 运行的搜索空间的联合实际上比这个起点大 7 倍,或者大约 3.5M 个组合,因为除了修剪之外,我们还扩展了一些超参数值范围并减小了范围内的步长。

因此,为了公平比较,我们在 3.5M 的组合搜索空间上运行了一次性贝叶斯搜索(TPESampler)。最佳运行的 MAPE 为 8.56% (在 3362 次试验后发现,总试验运行为 26500 次)——非常接近全球最小值 8.55% 。这里有一个重要的教训要吸取。

结论

那么,除了 Optuna 是一个很棒的产品包之外,我们还能传达什么信息呢?贝叶斯方法可能是惊人的,然而,人类的直觉并不完美。最初的贝叶斯 HPO 运行是在一个次优的搜索空间,因此产生了一个较差的结果。不正确地估计搜索空间似乎在现实世界中经常发生。此外,贝叶斯搜索可能会陷入局部最小值,使它们有时比随机搜索更糟(就像我们在初始搜索空间的情况一样——第一次迭代随机搜索与贝叶斯搜索)。

顺序运行加上手动分析,正如这里所示,可以帮助更好地理解搜索空间,以确保我们得到一个好的结果,同样重要的是,一个我们将在中充满信心的结果。

通过使用我们的使用搜索空间修剪的顺序 HPO,我们能够将我们的 MAPE 减少 10%,从 9.53%减少到 8.55%。在我们的初始搜索空间(大小 501k,试验 22k)和搜索空间的联合(大小 3.5M,试验 26.5k)上使用贝叶斯一次性 HPO,我们分别得到 8.80%和 8.56%的 MAPE 值。图片作者。

Hai Rozencwajg 是天际线 AIhttps://www.skyline.ai/公司的高级数据科学家,该公司打造了人工智能解决方案。

参考

[1] M. Wistuba,N. Schilling 和 L. Schmidt-Thieme,超参数搜索空间修剪——基于序列模型的超参数优化的新组件 (2015),计算机科学丛书讲义的一部分(LNCS,第 9285 卷)

[2] A. Lacoste,H. Larochelle,F. Laviolette 和 M. Marchand,基于序列模型的集成优化 (2014), arXiv

[3] Z. Kun,S. LingCong,L. Ninging,基于联合模型的房屋租金预测 (2019),2019 年第八届国际计算与模式识别会议论文集,ICCPR '19

[4] H .王斯瑶和 d .希罗伊,利用大数据集进行公寓租金价格预测的比较:克里金法与 DNN (2019), arXiv

[5] X. Zhou,W. Tong 和 D. Li,使用文本信息和深度学习对亚特兰大大都市地区的房屋租金进行建模 (2019),ISPRS 国际地理信息杂志

[6] Z. Wang,M. Zoghi,F. Hutter,D. Matheson,和 N. De Freitas,通过随机嵌入的高维贝叶斯优化 (2013),第二十三届国际人工智能联合会议论文集,IJCAI '13

[7] L. Li,K. Jamieson,G. DeSalvo,A. Rostamizadeh 和 A. Talwalkar, Hyperband:一种新的基于 Bandit 的超参数优化方法 (2018), arXiv

在 PySpark 中选择多个列

原文:https://towardsdatascience.com/selecting-multiple-columns-in-pyspark-d1aac072fcc0?source=collection_archive---------9-----------------------

讨论如何通过列名、索引或使用正则表达式从 PySpark 数据帧中选择多个列

照片由 Luana Azevedo 在 Unsplash 上拍摄

介绍

在使用 Spark 时,我们通常需要处理大量的行和列,因此,有时我们只能处理列的一小部分。

在今天的简短指南中,我们将探索从 PySpark 数据帧中选择列的不同方法。具体来说,我们将讨论如何选择多个列

  • 按列名
  • 按索引
  • 使用正则表达式

首先,让我们创建一个示例数据框架,我们将在本文中引用它来演示一些概念。

from pyspark.sql import SparkSession # Create an instance of spark session
spark_session = SparkSession.builder \.master('local[1]') \.appName('Example') \.getOrCreate()# Create an example DataFrame
df = spark_session.createDataFrame([(1, 'a', True, 1.0, 5),(2, 'b', False, 2.0, None),(3, 'c', False, 3.0, 4),(4, 'd', True, 4.0, 3),],['colA', 'colB', 'colC', 'colD', 'E']
) df.show()
*+----+----+-----+----+----+
|colA|colB| colC|colD|   E|
+----+----+-----+----+----+
|   1|   a| true| 1.0|   5|
|   2|   b|false| 2.0|null|
|   3|   c|false| 3.0|   4|
|   4|   d| true| 4.0|   3|
+----+----+-----+----+----+*

按名称选择多个列

为了从一个现有的 PySpark 数据帧中选择多个列,您可以简单地指定您希望通过[pyspark.sql.DataFrame.select](https://spark.apache.org/docs/latest/api/python/reference/api/pyspark.sql.DataFrame.select.html)方法检索的列名。举个例子,

**df.select('colA', 'colC').show()***+----+-----+
|colA| colC|
+----+-----+
|   1| true|
|   2|false|
|   3|false|
|   4| true|
+----+-----+*

或者,如果您希望检索的列存储在列表中,您可以使用以下符号:

col_names = ['colA', 'colC']**df.select(*col_names).show()***+----+-----+
|colA| colC|
+----+-----+
|   1| true|
|   2|false|
|   3|false|
|   4| true|
+----+-----+*

通过索引选择多个列

现在,如果您想基于它们的索引选择列,那么您可以简单地从返回列名列表的df.columns中截取结果。例如,为了检索前三列,下面的表达式应该可以做到:

**df.select(df.columns[:3]).show()***+----+----+-----+
|colA|colB| colC|
+----+----+-----+
|   1|   a| true|
|   2|   b|false|
|   3|   c|false|
|   4|   d| true|
+----+----+-----+*

或者,如果你只想获取第二列和第三列,那么df.columns[1:3]就可以了:

**df.select(df.columns[1:3]).show()***+----+-----+
|colB| colC|
+----+-----+
|   a| true|
|   b|false|
|   c|false|
|   d| true|
+----+-----+*

使用正则表达式选择多个列

最后,为了选择多个匹配特定正则表达式的列,您可以使用[pyspark.sql.DataFrame.colRegex](https://spark.apache.org/docs/latest/api/python/reference/api/pyspark.sql.DataFrame.colRegex.html)方法。例如,为了获取所有以col开头或包含【】的列,下面的代码就可以做到:

**df.select(df.colRegex("`(col)+?.+`")).show()***+----+----+-----+----+
|colA|colB| colC|colD|
+----+----+-----+----+
|   1|   a| true| 1.0|
|   2|   b|false| 2.0|
|   3|   c|false| 3.0|
|   4|   d| true| 4.0|
+----+----+-----+----+*

类似地,我们可以使用下面的正则表达式来选择除colA之外的所有列:

**df.select(df.colRegex("`(colA)?+.+`")).show()***+----+-----+----+----+
|colB| colC|colD|   E|
+----+-----+----+----+
|   a| true| 1.0|   5|
|   b|false| 2.0|null|
|   c|false| 3.0|   4|
|   d| true| 4.0|   3|
+----+-----+----+----+*

最后的想法

在今天的简短指南中,我们讨论了如何在 PySpark 数据框架中执行列选择。我们探讨了如何通过指定列名或索引来选择多个列。此外,我们还看到了如何使用正则表达式来执行列选择。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。

你可能也会喜欢

为 K-均值聚类选择最佳 K

原文:https://towardsdatascience.com/selecting-optimal-k-for-k-means-clustering-c7579fd2e926?source=collection_archive---------14-----------------------

以有监督的方式使用无监督聚类。

K-means clustering是矢量量化的一种方式,最初来自信号处理,旨在根据平均值对观察值进行聚类。让我们首先阐明这里探讨的集群案例的前提;对客户进行细分。客户细分是将组织的客户划分为反映每个分组中客户相似性的集群的方法。这种客户剖析的目的是确定如何识别每个片段中的客户,以增加每个客户对企业的价值。

其中一种流行的机器学习技术是K-means聚类,这是一种最简单、最流行的无监督机器学习算法。通常,无监督算法仅使用输入向量从数据集进行推断,而不参考已知或标记的结果。

在史蒂夫·乔布斯:沃尔特·伊萨克森独家传记中,尽管经常被曲解,乔布斯说:

“有些人说,‘给顾客他们想要的东西。’但这不是我的方法。我们的工作是在他们行动之前找出他们想要什么。我想亨利·福特曾经说过,‘如果我问顾客他们想要什么,他们会告诉我,‘一匹更快的马!’。人们不知道他们想要什么,直到你给他们看。这就是我从不依赖市场调查的原因。我们的任务是阅读页面上还没有的东西。"

AB 在 Unsplash 上拍照

这让一些人相信,在他看来,市场研究并不重要,但实际上,乔布斯的意思是超越典型的市场研究,提前解读和发现客户需求。为此,市场细分是一个很好的方法,并对每个集群进行有针对性的市场研究。例如,大学新生的需求与中年户主购买金融服务的需求不同,他们的营销方式也不应该相同。

对于营销人员和政策制定者来说,了解不同类别的客户以满足他们更好地服务的需求是至关重要的。吸引新客户不再是成熟企业的好策略,因为帮助现有客户的成本要低得多。哪些属性应该用于此细分?那要看情况了。

四类客户细分:

  • 行为细分:关注客户的习惯。例如基于使用的分段。
  • 心理细分:基于不明显特征的细分。例如价值观或观点。
  • 基于粗略的特征。例如职业、婚姻状况。
  • 地理分割:基于位置。例如城市、国家。

通过做客户细分,你会发现每个客户的行为和需求都有相似的特征。然后,这些被归纳成组,这些组可以用来满足具有无数策略的特定需求。此外,这些策略可以是以下内容的输入:

  • 目标营销
  • 引入符合客户需求的功能
  • 产品路线图的开发

照片由艾米丽·莫特在 Unsplash 上拍摄

使用无监督聚类也提出了一个问题,要创建多少个聚类?这是一个很难直接回答的问题。通常,时间经理或 CEO 会根据特定的业务目标对集群的数量有特定的要求。但是,当数据泄露时,您如何决定是否应该得出集群的数量呢?在这篇博文中,我将描述获得这个数字的技术。话虽如此,但重要的是要知道算法所建议的可能不是最佳的聚类数。分析师应该用商业判断来证明她的选择。这与其说是科学,不如说是一门艺术,选择聚类的数量对于根据分析制定的策略的成功是有害的,因为无意义的输入数据导致无意义的输出。

对于本演示,使用了来自 LEAPS Analyttica 的数据集。对数据集进行最小程度的清理,然后使用StandardScaler 对数值数据进行缩放,使用OneHotEncoder对分类变量进行一次热编码,以便在使用 scikit-learn API 的机器学习算法中使用。所有转换都是使用 scikit-learn 管道和转换器来执行的。

方法 1:使用 **K-means++** 和不同的“K”的

总共创建了 20 个模型,并绘制了inertiaSilhouette ScoreCalinski Harabasz Score的分数。这方面的代码如下:

这产生了以下情节:

按作者选择最佳“k”图像

较高的Silhouette Coefficient score与具有更好定义的聚类的模型相关。而更高的Calinski-Harabasz score涉及具有更好定义的聚类的模型。

虽然通过观察视觉,没有明显的最佳 K 可以被发现。

基于Silhouette ScoreSum of squared error(又名肘图),5 个分段似乎是初始模型的最佳选择。Calinski Harabasz Score也支持这种分割。

方法二:使用 黄砖

测试 K-意味着模型的 K 值为 2 到 10,使用随机状态进行再现性测试,并且不显示模型拟合的时间。使用的代码:

作者图片

该图建议将K=5作为最佳集群数。

现在使用主成分分析来使用黄砖在二维空间中可视化聚类。

作者图片

检测到簇之间的明显分离。

方法三:使用 **MeanShfit** 发现集群

均值漂移聚类旨在发现样本的平滑密度中的“斑点”。这是一种基于质心的算法,其工作原理是将候选质心更新为给定区域内点的平均值。然后,在后处理阶段过滤这些候选对象,以消除近似重复,从而形成最终的质心集。(来自 scikit-learn 文档)

使用的代码:

代码的输出是:

Number of estimated clusters : 5

MeanShift建议 5 为最佳聚类数。

现在我用K=5创建了一个K-means++模型用于我的分析。PCA 为 3 时,这些聚类是可见的。那些 3 的主成分分析可以解释 40%的数据集。观察到集群之间的适当区别。

PCA3 群集,按作者分类的图像

很好看的聚类,不是吗?

监督无监督聚类:

接下来,我使用随机 Forrest 分类模型验证了我的聚类。在将训练测试中的数据集按 80%–20%的比率拆分后,我使用聚类模型的预测作为随机森林分类模型的因变量。并检验了模型的预测能力。如果聚类有意义,随机森林模型将能够更准确地预测聚类。该模型在测试集上达到了 0.93 的模型精度。然后,利用来自随机森林模型的特征重要性和置换重要性的组合的洞察力,探索聚类以识别特征,用于进一步探索聚类内和聚类间的特征。K-means模型能够根据观察到的聚类属性进行很好的聚类。

随机森林分类器的模型报告:

随机森林分类器模型报告,图片由作者提供

集群的分布:

图片作者。

探索每个集群后,它们被标记为:

  • 集群 0:低价值服务的频繁用户。
  • 群组 1:高风险客户细分。
  • 第二组:普通客户。
  • 第三类:最忠诚的客户。(主要由老客户组成)
  • 集群 4:高价值客户。

此工作流是为无监督聚类模型决定最佳 K 的一个很好的选择,并使用监督分类模型验证选择。

所有这些都可以在 GitHub 上找到,点击这个链接。这种分析通过客户流失分析进行了扩展,可以在 GitHub 上使用这个链接找到。

今天到此为止。下次见!

选择 R 中线性回归的最佳预测值

原文:https://towardsdatascience.com/selecting-the-best-predictors-for-linear-regression-in-r-f385bf3d93e9?source=collection_archive---------2-----------------------

使用 R 中的 leaps 软件包选择线性回归的最显著因变量

萨法尔·萨法罗夫在 Unsplash 上拍摄的照片

为了获得多元回归模型的最佳拟合,包含数据集中最重要的预测子集非常重要。然而,在一大组预测因子中,要了解哪些预测因子对我们的目标变量有重大影响是相当具有挑战性的。考虑到每个变量的 p 值是针对模型中的其他项进行调整的,这可能会特别麻烦。

在这篇文章中,我将演示如何使用 R 的 leaps 包来获得最好的回归模型。

什么是飞跃?

Leaps 是一个回归子集选择工具,它执行详尽的搜索,以确定对我们的模型 (Lumley,2020) 最有影响的预测因子。通过评估导致最佳调整 r 和 Mallow 的 CP 的组合来选择最佳预测器。

我们将使用 Cortez 和莫莱斯 2007 年森林火灾数据集上的 regsubsets()函数,来预测葡萄牙 Montesinho 自然公园的过火面积(ha)。

跳跃变量选择

照片由Landon parentau在 Unsplash 上拍摄

关于数据的一点点..

森林火灾数据集包含葡萄牙 Montesinho 自然公园 517 起森林火灾的信息。数据集中有 12 个属性。

在 EDA 过程中,我注意到几个因变量之间的多重共线性,因此该数据集是特征选择的良好候选。

在建模之前,原始数据集也被转换以满足线性回归的假设。还添加了额外的虚拟变量,因为我们对时间上的相互作用感兴趣。

现在,我们已经准备好数据集,让我们开始建模。

1。用所有预测值进行线性回归

在为我们的回归选择预测因子的最佳子集之前,让我们使用所有预测因子对数据集运行一个简单的线性回归,以设置用于比较的基本调整 r。

 lm1 <- lm(fires.clean1,formula=area ~.)
summary(lm1) 

我们可以看到,在模型中包含所有变量的情况下,基础调整后的 r 为 0.02,剩余标准误差为 0.6454。然而,这种关系在 P <.05 level.=""/>

2 并不显著。安装并加载 leaps 库

安装 leaps 包加载后,库使用以下命令:

library(leaps)

3。Regsubsets()

对所有变量运行 regsubsets()函数。

Best_Subset <-regsubsets(area~.,data =fires.clean1,nbest = 1,      # 1 best model for each number of predictorsnvmax = NULL,    # NULL for no limit on number of variablesforce.in = NULL, force.out = NULL,method = "exhaustive")summary_best_subset <- summary(regsubsets.out)
as.data.frame(summary_best_subset$outmat)

4。预测器数量

现在,我们已经在我们的数据集上运行了 leaps,让我们看看软件包在数据集使用的预测因子数量方面有什么建议。

 which.max(summary_best_subset$adjr2) 

似乎我们必须使用 13 个预测器来获得最佳模型。

5。最好的预测因素是什么?

下面的汇总表提供了模型使用的预测因子的详细信息。最佳预测值用“真”表示。

summary_best_subset$which[13,]

看起来仅包含以下预测值将为我们的线性回归模型提供最佳模型拟合:day.thu、month.aug、month.dec、month.jan、month.jul、month.jun、month.mar、month.oct、month.sep、X、DMC、temp 和 RH

6。使用最佳预测值运行回归模型

best.model <- lm(area ~ day.thu + month.aug + month.dec + month.jan + month.jul+ month.jun+month.mar+month.oct+month.sep+X+Y+DMC+temp+RH, data = fires.clean1)
summary(best.model)

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/b3a7b64bf99795fb5335569bf0f74a3c.png)我们可以看到,与步骤 1 中的模型相比,我们调整后的 r 显著提高(从 0.02 提高到 0.05),非常显著。# 后续步骤下一步将是通过查看诊断图来进一步评估该模型。如果数据集被分为训练集和测试集,我们还可以使用 predict()函数在测试数据集上运行该模型,然后评估这些结果以全面了解我们的模型性能。需要注意的一点是,我们调整后的 r 仍然很低。最好的模型只能解释数据中 5%的可变性。这可能意味着我们需要进一步转换数据集,或者尝试不同的转换方法。这也意味着我们的数据集可能不是线性回归的最佳候选。# 结论这篇文章的目的是演示如何使用 leaps 包为线性回归模型选择变量。欢迎对变量选择的方法或替代(高级)方法提出意见和建议。请查看下面的参考资料,了解更多关于使用 leaps 进行变量选择的信息。# 参考(2018 年 4 月 27 日)。于 2021 年 5 月 23 日从[https://www.youtube.com/watch?v=3HKMjEK02Cs](https://www.youtube.com/watch?v=3HKMjEK02Cs)检索托马斯·拉姆利(2020 年 1 月 16 日)。包装飞跃。https://cran.r-project.org/web/packages/leaps/leaps.pdfKassambara (2018 年 11 月 3 日)。*用于高通量数据分析的统计工具。*[http://www . sthda . com/English/articles/37-model-selection-essentials-in-r/155-best-subsets-regression-essentials-in-r/](http://www.sthda.com/english/articles/37-model-selection-essentials-in-r/155-best-subsets-regression-essentials-in-r/)leaps、bestglm、glmulti 和 meifly 的所有子集回归。2021 年 5 月 23 日检索自[https://r studio-pubs-static . S3 . amazonaws . com/2897 _ 9220 b 21 CFC 0 c 43 a 396 ff 9 abf 122 bb 351 . html](https://rstudio-pubs-static.s3.amazonaws.com/2897_9220b21cfc0c43a396ff9abf122bb351.html)Minitab (2017 年 6 月 29 日)。Cp 和 Cpk:两种过程视角,一种过程现实。 *Minitab。*[https://blog . minitab . com/en/statistics-and-quality-data-analysis/Cp-and-cpk-two-process-perspectives-one-process-reality #:~:text = Cp % 20 is % 20a % 20 ratio % 20 of,表示% 20a % 20 more % 20 capable % 20 process。&text =当% 20 规格% 20 展开% 20 无,过程% 20 展开% 20 为% 20 低](https://blog.minitab.com/en/statistics-and-quality-data-analysis/cp-and-cpk-two-process-perspectives-one-process-reality#:~:text=Cp%20is%20a%20ratio%20of,indicate%20a%20more%20capable%20process.&text=When%20the%20specification%20spread%20is%20less,process%20spread,%20Cp%20is%20low)。斯蒂芬妮·格伦(2021)。调整后的 R2 /调整后的 R 平方:它是做什么用的?[*StatisticsHowTo.com*](https://www.statisticshowto.com/):我们其他人的基本统计![https://www . statistics show to . com/probability-and-statistics/statistics-definitions/adjusted-R2/](https://www.statisticshowto.com/probability-and-statistics/statistics-definitions/adjusted-r2/)页(page 的缩写)科尔特斯和莫莱斯(2007 年 12 月 1 日)。利用气象数据预测森林火灾的数据挖掘方法。*第十三届 EPIA 2007 —葡萄牙人工智能会议论文集,2007 年 12 月*。(http://www.dsi.uminho.pt/~pcortez/fires.pdf)扎克(2020,08 年 1 月)。*线性回归的四个假设。*静态学。[https://www . statology . org/linear-regression-assumptions/https://www . statology . org/linear-regression-assumptions/](https://www.statology.org/linear-regression-assumptions/)<http://www.sthda.com/english/articles/37-model-selection-essentials-in-r/155-best-subsets-regression-essentials-in-r/> # 自动驾驶汽车:过去、现在和未来> 原文:<https://towardsdatascience.com/self-driving-cars-past-present-and-future-71532430346?source=collection_archive---------22----------------------->## [播客](https://towardsdatascience.com/tagged/tds-podcast)## 彼得·高谈自动驾驶的核心挑战和创新[苹果](https://podcasts.apple.com/ca/podcast/towards-data-science/id1470952338?mt=2) | [谷歌](https://www.google.com/podcasts?feed=aHR0cHM6Ly9hbmNob3IuZm0vcy8zNmI0ODQ0L3BvZGNhc3QvcnNz) | [SPOTIFY](https://open.spotify.com/show/63diy2DtpHzQfeNVxAPZgU) | [其他](https://anchor.fm/towardsdatascience)*编者按:这一集是我们关于数据科学和机器学习新兴问题的播客系列的一部分*,*由 Jeremie Harris 主持。除了主持播客,Jeremie 还帮助运营一家名为*[*sharpes minds*](http://sharpestminds.com)*的数据科学导师初创公司。*Cruise 是一家成立于 2013 年的自动驾驶汽车初创公司,当时大多数人都认为自动驾驶汽车是科幻小说中的东西。然而,仅仅三年后,该公司被通用汽车以超过 10 亿美元的价格收购,表明自己是让自动驾驶成为现实的竞赛中的真正参与者。一路走来,该公司不得不导航并适应快速变化的技术格局,将机器人和软件工程的旧思想与深度学习等前沿技术相结合。本期播客的嘉宾是克鲁斯最早的员工之一。彼得·高(Peter Gao)是一位在自动驾驶汽车行业拥有丰富经验的机器学习专家,也是 Y Combinator 支持的初创公司[水族馆学习](https://www.aquariumlearning.com/)的联合创始人,该公司专门通过修复机器学习模型训练数据的问题来提高机器学习模型的性能。我们讨论了彼得在自动驾驶汽车行业的经历,包括自动驾驶汽车技术的创新,以及一些需要克服的技术和道德挑战,以使自动驾驶汽车在世界各地成为主流。以下是我最喜欢的一些外卖食品:*   无人驾驶汽车的历史比大多数人想象的要久远得多。早在 20 世纪中期,就有人提出了无人驾驶交通的第一个建议,但鉴于当时的技术水平,实现这一目标的唯一现实方法是大力限制这一问题。即使在理论上,也需要专门建造的轨道、安装在道路下引导车辆的磁铁以及其他定制的基础设施。但随着时间的推移和技术的进步,限制可能会放松:到 20 世纪 90 年代,基本的计算机视觉算法允许自动驾驶汽车在高速公路上行驶得相当好。但是这些更现代的技术要求自动车辆装满服务器机架,并且对于城市驾驶来说适应性不够。只是随着深度学习的出现,日常使用所需的感知和规划能力已经足够好,可以用于主流用途:由于计算机视觉,汽车现在可以解释它们周围的基础设施,而不需要在建造时就考虑到它们。尽管如此,即使是现代自动驾驶汽车也是深度学习、三维几何重建的经典算法、硬编码规则结构和机器人技术的弗兰肯斯坦怪物。
*   制造完全自动驾驶汽车的主要瓶颈是分布外采样问题,这是一个当汽车遇到它在训练中没有遇到的场景时出现的问题。例如,彼得引用了在万圣节识别穿着精心制作的服装的行人的挑战——如果模特在训练中没有遇到穿着变形服装的人,就不太可能正确地将他们归类为要避免的东西。这个采样问题本质上使得在旧金山开发的自动驾驶汽车技术在不同街道大小和路况的城市使用很危险,如凤凰城或蒙特利尔,这就是为什么自动驾驶汽车技术的推广可能会在一个城市接一个城市的基础上进行。每一个新环境都是一个全新的问题。
*   对于许多有商业价值的应用程序来说,完全自治实际上并不必要。一些问题设置自然比其他问题设置更受约束——彼得引用洗碗机作为例子:从技术上讲,它们是机器人的自主应用,但这只有在它们被设置在精心约束的环境中的事实下才有可能。比洗碗机更少约束,但比日常驾驶问题更受约束的是像沿着船坞移动货物或使用自主无人机检查电力线这样的问题。虽然这些应用可能不总是看起来像自动驾驶汽车,但它们是使自动驾驶汽车成为可能的同一技术的分支。
*   随着我们将更多的决策权交给机器,我们开始面临一些棘手的道德问题——这些问题在无人驾驶汽车技术领域最为棘手。当撞车事故发生时,谁或什么是罪魁祸首?答案当然取决于具体情况。由于自动驾驶汽车通常会有一些人为监督——以负责多辆车的操作员的形式,他们可以在模糊的情况下进行干预——所以会有操作员疏忽可能是一个因素的情况。尽管如此,还是有人工智能决策导致糟糕结果的场景。当这种情况发生时,责任是属于构建人工智能的公司、部署人工智能的公司,还是负责算法开发或管理汽车训练数据的个人?
*   Peter 强调,通常情况下,人工智能性能的问题实际上来自训练数据的问题,而不是算法架构的问题。他遇到了一系列相对一致的问题,并使用令人惊讶的一般化技术来解决它们。你可以在这里的 Twitter 上关注彼得,或者在这里的 Twitter 上关注我[。](https://twitter.com/jeremiecharris)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/9a1e8ba6ce8f66308eddea7c6eaec8d8.png)## 章节:*   0:00 介绍
*   彼得的背景
*   4:15 早期项目
*   8:00 感知如何为自动驾驶汽车服务
*   18:30 主要限制
*   自动驾驶汽车技术的 22:50 时间线
*   26:40 自动驾驶汽车技术的激动人心的应用
*   34:50 自动化其他领域
*   42:35 通过事故和错误进行推理
*   47:10 数据集之间最常见的挑战
*   56:00 不同种类的错误以及如何处理它们
*   1:00:10 总结# 2021 年的自动驾驶火车> 原文:<https://towardsdatascience.com/self-driving-train-in-2021-1f1586113e01?source=collection_archive---------40----------------------->![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/1b2f4c4243c2d130d41340071c32c7e1.png)图 1——杰克·安斯蒂在 [Unsplash](https://unsplash.com/s/photos/steam-train?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄的照片## 继电气化和高速之后,随着人工智能、云计算和大数据的兴起,第四次铁路革命即将到来# 介绍我们听到很多关于电动汽车、自动驾驶汽车、移动性、交通工具的说法,但很少听说自动驾驶火车。而且有充分的理由,他们还不真的存在!但是,严肃的项目正在进行中,它们肯定会比你想象的要快得多!这是对这种在全世界超过 100 万公里的铁路上运行的车辆的未来的概述。# 自动化和自主性首先,什么是自主列车?我们可以将它定义为一列火车,它可以看到、了解其环境、交流并适应意外事件。今天,一些非常复杂的地铁系统提供了高度的自动化。自主列车不仅是自动的,而且能够独立地做出反应和决策。然后,它可以在任何情况下确保网络上乘客的安全,无需人工干预。为了鉴定列车的技术进步和性能水平,将自动化分为 4 个等级(GoA)*   **GoA 1** : **受控手动驾驶**。司机开车,自动装置控制速度。
*   **GoA 2** : **自动** **加速和制动**。是免提驾驶。速度是自动计算和应用的。司机监控障碍物。正是从这一层开始,网络上的容量可以增加(见下一段)。
*   **GoA 3** : **自动驾驶**载人。自动化是试验性的,但列车驾驶员仍在场管理非正常情况。
*   **果阿 4** :全**自主**列车。能够在没有任何人工干预的情况下运行。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/dedbb06cf81e8a6200a8b269fee17c69.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/783db08d9cb885d248f145160ab0af86.png)图 2——自动化等级——SNCF今天,超过 99%的列车仍在 GoA 1 模式下运行,地铁经常在 GoA 2,有时在 GoA 3 或 GoA4(主要是机场班车)。在澳大利亚,无人驾驶火车(GoA 4)的试验已经证明了它们的价值,可以在没有司机的情况下,在矿山和港口之间运输超过 280 公里的矿石。下一步是将这些技术应用到客运列车上。首先,在封闭的电路上,然后在更大的区域上。风险一点也不高,因为在一个国家网络中,几列火车同时在同一条轨道上运行,这大大增加了复杂性,并需要更高水平的能力。在法国,SNCF 正在研究自动列车原型,并计划从 2023 年开始将其产业化。# 自主列车的优势你可以想象,设计这种火车的投资是巨大的。但是,你为什么要换成自动列车呢?## 容量增加通过系统地优化速度,更容易在车站(尤其是城市列车)和路线(所有列车在相同路段以不同速度行驶)上削减利润。从运营的角度来说,这是最重要的。世界上饱和的大都市可以有更好的车站访问频率,承载更多的乘客和更好的服务质量。## 更好的规律性自主列车和互联列车对危险具有更强的响应能力和恢复能力。列车之间的通信允许在网络出现问题时减慢服务速度,而不会完全停止服务。而当一列列列车停运,重新启动时,出于安全考虑,每次发车前都需要等待。通过实时定位和通信,所有列车将能够更快地重新启动。## 更加生态消费被规范和优化,这是更生态的方式。不合时宜的刹车白白消耗了很多能量。在这里,列车之间的实时通信也将带来改善。## 经济成果更生态,因此在消费方面更经济。但不仅如此。更多的火车也意味着更多的座位出售,因此利润更高。最后,通过优化流程,(过于昂贵的)生产线将会更加高效。建设线路,以及车站,将因此变得更加稀缺,这也是一个胜利。## 更具竞争力在这个人工智能和自动化的时代,能够设计或制造一辆自动驾驶的火车可以让它的制造商和买家站在国际舞台的最前沿。最后,更好的服务有助于吸引更多的顾客,并应对竞争的流动性(汽车共享,廉价航空,公共汽车等)。).# 技术挑战像汽车一样,火车必须配备系统,使其能够管理现在由司机管理的东西。这包括远见和决策。自动驾驶汽车中开发和部署的技术,如传感器和数据处理模块,可以适用于铁路网络。就其本身而言,火车的导航比汽车简单得多。旅行只有一个维度:没有交叉路口、交通圈或多条轨道的复杂道路。而路上的自行车就更少了,或者同样环境下的行人。不,简单多了。火车在铁轨上,它只能前进,唯一的问题是速度有多快。在某个地方,自动列车只需要做出一个关键的决定:我应该加速还是减速?事实上,由于路线是已知的,速度是预先编程的,列车必须问自己的基本问题如下:> 紧急情况下该不该刹车?另一方面,考虑到火车的重量和速度,必须提前做好紧急制动的准备。所有的复杂性都来自于此。幸运的是,在火车上,已经有很多电,还有空间。这使得计算机和大量的计算能力可用。采集模块很经典:相机、声纳、激光雷达等。如果基础设施很旧(最初没有为自动列车规划),他们还必须能够在所有类型的天气下读取信号。在更现代的基础设施的情况下,信号元素可以直接传输到列车。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/2845d80e44363c19d5ec50bf091d73ab.png)图 3 —铁路革命(图片由作者提供)因此,风险和复杂性与自动驾驶汽车非常不同。然而,一些技术挑战是跨领域的。这些措施尤其包括## 计算能力使用人工智能的技术通常需要大量的计算。谁说计算量大,谁说热量大。但是多亏了汽车工业,高效的高性能处理器即将问世。## 互联网网络容量自动列车将需要通信。与车站以及同一轨道上的其他列车进行通信。交流大量数据。大量的数据意味着高效的网络。还是那句话,5G 的到来,最多也就是几年的事情。## 人工智能模型可解释性自主列车将能够自己做出决定。这很好,但不足以让乘客或当局放心。人工智能通常被认为是一个“黑匣子”,这是正确的。在不知道为什么会做出这样那样的决定的情况下,进入一辆时速数百公里的汽车是不可想象的。这一点在所有与人工智能相关的领域都是成立的。这就是为什么,连同数据保护的概念,可解释性是今天世界范围内研究的先锋。# 结论自动列车为乘客和运营商提供了多种好处,现在它正处于梦想和现实之间。除了一些不允许持续存在的技术障碍,这是一个正在高速发展的项目,继承了在自动驾驶汽车背景下进行的所有研究。毫无疑问,它将在未来十年改变铁路部门。事实上,凭借其安全和准时的承诺,自动列车有着光明的未来。对你有什么好处?你准备好在没有司机的情况下乘火车去度假吗?> 希望你喜欢&回头见🤠# <500 Lines of Code> 原文:<https://towardsdatascience.com/self-parking-car-in-500-lines-of-code-c1b2a57455af?source=collection_archive---------3----------------------->## [动手教程](https://towardsdatascience.com/tagged/hands-on-tutorials)中的自动泊车车## 使用遗传算法训练汽车自动泊车![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/5777198c7a3feb57f9c1def63d068e4c.png)作者插图# TL;速度三角形定位法(dead reckoning)在本文中,我们将使用[遗传算法](https://en.wikipedia.org/wiki/Genetic_algorithm)来训练汽车进行自主泊车。我们将用随机基因组创造出第一代汽车**,其行为如下:**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/7cfb20c202834cfeeb80bf83567f5edb.png)第一代汽车。作者制作的动画**在第 40 代**上,汽车开始学习什么是自动泊车,并开始靠近泊车点:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/237f030840308cc734fb4913e3d0483e.png)第 40 代车。作者制作的动画另一个例子更具挑战性:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/55d0908aa264ccab265f60fb305bcfdd.png)第 40 代车。作者制作的动画> 是啊,是啊,这些车在路上撞上了其他车,也不完全适合停车,但这只是自世界创造以来他们的第 40 代,所以请仁慈一点,给这些车一些空间让:D 成长您可以启动🚕[自动泊车汽车进化模拟器](https://trekhleb.dev/self-parking-car-evolution)直接在浏览器中查看进化过程。模拟器为您提供了以下机会:*   您可以[从头开始训练汽车](https://trekhleb.dev/self-parking-car-evolution?parking=evolution#/)并自行调整遗传参数
*   你可以[看到训练过的自动泊车车在运行](https://trekhleb.dev/self-parking-car-evolution?parking=automatic#/)
*   您也可以[尝试手动泊车](https://trekhleb.dev/self-parking-car-evolution?parking=manual#/)这个项目的遗传算法是用 TypeScript 实现的。完整的遗传源代码将在本文中展示,但你也可以在[进化模拟器资源库](https://github.com/trekhleb/self-parking-car-evolution)中找到最终的代码示例。> 我们将使用遗传算法来完成进化汽车基因组的特殊任务。然而,这篇文章仅仅触及了算法的基础,并不是遗传算法主题的完整指南。话虽如此,让我们深入了解更多细节…# 这个计划一步一步地,我们将把创建自动泊车汽车的高级任务分解为寻找`180`位的最佳组合(寻找最佳汽车基因组)的简单低级优化问题。这是我们要做的:1.  💪🏻将**肌肉**(发动机、方向盘)给汽车,使其能够向停车点移动。
2.  👀给汽车装上**眼睛**(传感器),这样它就能看到周围的障碍物。
3.  🧠给汽车配备了大脑,它将根据汽车看到的东西(通过传感器看到的障碍物)来控制肌肉(运动)。大脑将只是一个纯粹的功能。
4.  🧬 **进化大脑**根据传感器的输入做出正确的动作。这就是我们将应用遗传算法的地方。一代又一代,我们的大脑功能`movements = f(sensors)`将学习如何将汽车驶向停车位。# 赋予汽车肌肉为了能够移动,汽车需要“肌肉”。让我们给汽车两种类型的肌肉:1.  **发动机肌肉** —允许汽车移动 *↓后退*、 *↑前进*或 *◎立钢*(空挡)
2.  **方向盘肌肉**——允许汽车在行驶中向左 *←转*、 *→向右*或 *◎直行*有了这两块肌肉,汽车可以完成以下动作:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/aefa3ce18b97fb1efe80744cf7e82061.png)汽车肌肉。作者制作的动画在我们的例子中,肌肉是来自大脑的信号的接收器,每隔`100ms`(毫秒)一次。根据大脑信号的价值,肌肉会做出不同的反应。我们将在下面讨论“大脑”部分,但现在,假设我们的大脑可能只向每块肌肉发送 3 种可能的信号:`-1`、`0`或`+1`。

type MuscleSignal = -1 | 0 | 1;


例如,大脑可能会向引擎肌肉发送值为`+1`的信号,它将开始向前移动汽车。给引擎的信号`-1`使汽车向后移动。同时,如果大脑将信号`-1`发送给方向盘肌肉,它会将车转向左侧等。在我们的例子中,大脑信号值是如何映射到肌肉动作的:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/f8f8d5cbc2718e3334761a230dc896fa.png)> 你可以[使用进化模拟器](https://trekhleb.dev/self-parking-car-evolution?parking=manual#/)尝试手动停车,看看汽车肌肉是如何工作的。每当你按下`WASD`键盘上的一个键(或使用触摸屏操纵杆),你就向引擎和方向盘肌肉发送这些`-1`、`0`或`+1`信号。# 给汽车一双眼睛在我们的汽车学会如何使用肌肉自动停车之前,它需要能够“看到”周围的环境。让我们以距离传感器的形式给它一双`8`眼睛:*   每个传感器可以在`0-4m`(米)的距离范围内探测障碍物。
*   每个传感器每隔`100ms`都会向汽车的“大脑”报告它“看到”的障碍物的最新信息。
*   只要传感器没有发现任何障碍物,它就会报告`0`的值。相反,如果传感器的值很小但不为零(即`0.01m`),则意味着障碍物很近。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/5efb0b5d578055fe9c114f6832436d27.png)车眼。作者插图> 你可以使用进化模拟器,看看每个传感器的颜色是如何根据障碍物的远近而变化的。

type Sensors = number[];


# 给汽车赋予大脑此时此刻,我们的汽车可以“看见”和“移动”,但没有“协调器”,将“眼睛”的信号转换为“肌肉”的适当运动。我们需要给汽车一个“大脑”。# 大脑输入作为来自传感器的输入,大脑每隔`100ms`就会得到`8`个浮点数,每个都在`[0...4]`的范围内。例如,输入可能如下所示:

const sensors: Sensors = [s0, s1, s2, s3, s4, s5, s6, s7];
// i.e. 🧠 ← [0, 0.5, 4, 0.002, 0, 3.76, 0, 1.245]


# 大脑输出每一个`100ms`大脑都应该产生两个整数作为输出:1.  一个数字作为发动机的信号:`engineSignal`
2.  一个数字作为方向盘的信号:`wheelSignal`每个数字都应该是类型`MuscleSignal`,并且可以取三个值之一:`-1`、`0`或`+1`。# 大脑公式/功能记住上面提到的大脑的输入和输出,我们可以说大脑只是一种功能:

const { engineSignal, wheelSignal } = brainToMuscleSignal(
brainFunction(sensors)
);
// i.e. { engineSignal: 0, wheelSignal: -1 } ← 🧠 ← [0, 0.5, 4, 0.002, 0, 3.76, 0, 1.245]


其中`brainToMuscleSignal()`是一个将原始脑信号(任何浮点数)转换为肌肉信号(转换为`-1`、`0`或`+1`数)以便肌肉能够理解的函数。我们将在下面实现这个转换器函数。现在的主要问题是`brainFunction()`是一个什么样的函数。为了让汽车更智能,让它的运动更复杂,我们可以用一个[多层感知机](https://en.wikipedia.org/wiki/Multilayer_perceptron)。这个名字有点吓人,但这是一个简单的神经网络,有一个基本的架构(把它想象成一个有许多参数/系数的大公式)。> 在我的[自制机器学习](https://github.com/trekhleb/homemade-machine-learning#-multilayer-perceptron-mlp)、[机器学习实验](https://github.com/trekhleb/machine-learning-experiments#multilayer-perceptron-mlp-or-simple-neural-network-nn)和[纳米神经元](https://github.com/trekhleb/nano-neuron)项目中,我已经详细介绍了多层感知器。你甚至可以挑战那个简单的网络[来识别你写的数字](https://trekhleb.dev/machine-learning-experiments/#/experiments/DigitsRecognitionMLP)。然而,为了避免引入全新的神经网络概念,我们将采用一种更简单的方法,我们将使用两个具有多个变量的线性多项式(更准确地说,每个多项式将正好有`8`个变量,因为我们有`8`个传感器),如下所示:

engineSignal = brainToMuscleSignal(
(e0 * s0) + (e1 * s1) + ... + (e7 * s7) + e8 // <- brainFunction
)wheelSignal = brainToMuscleSignal(
(w0 * s0) + (w1 * s1) + ... + (w7 * s7) + w8 // <- brainFunction
)


其中:*   `[s0, s1, ..., s7]`-`8`变量,即`8`传感器值。这些是动态的。
*   `[e0, e1, ..., e8]` -发动机多项式的`9`系数。这些是汽车需要学习的,它们是静态的。
*   `[w0, w1, ..., w8]` -方向盘多项式的`9`系数。这些是汽车需要学习的,它们是静态的为大脑使用更简单的功能的代价是,汽车将无法学习一些复杂的动作,也无法很好地概括和适应未知的环境。但是对于我们这个特殊的停车场来说,为了展示遗传算法的工作,这已经足够了。我们可以用下面的方式实现通用多项式函数([查看这篇文章的这个版本以获得更好的源代码格式](https://trekhleb.dev/blog/2021/self-parking-car-evolution/)):

type Coefficients = number[];// Calculates the value of a linear polynomial based on the coefficients and variables.
const linearPolynomial = (coefficients: Coefficients, variables: number[]): number => {
if (coefficients.length !== (variables.length + 1)) {
throw new Error('Incompatible number of polynomial coefficients and variables');
}
let result = 0;
coefficients.forEach((coefficient: number, coefficientIndex: number) => {
if (coefficientIndex < variables.length) {
result += coefficient * variables[coefficientIndex];
} else {
// The last coefficient needs to be added up without multiplication.
result += coefficient
}
});
return result;
};


在这种情况下,汽车的大脑将由两个多项式组成,看起来像这样:

const engineSignal: MuscleSignal = brainToMuscleSignal(
linearPolynomial(engineCoefficients, sensors)
);const wheelSignal: MuscleSignal = brainToMuscleSignal(
linearPolynomial(wheelCoefficients, sensors)
);


`linearPolynomial()`函数的输出是一个浮点数。`brainToMuscleSignal()`函数需要将大量的浮点数转换成三个特定的整数,它将分两步完成:1.  将大范围浮动(即`0.456`或`3673.45`或`-280`)转换为`(0...1)`(即`0.05`或`0.86`)范围内的浮动
2.  将`(0...1)`范围内的浮点值转换为`-1`、`0`或`+1`三个整数值之一。比如靠近`0`的浮动会转换成`-1`,靠近`0.5`的浮动会转换成`0`,靠近`1`的浮动会转换成`1`。为了完成转换的第一部分,我们需要引入一个实现以下公式的 [Sigmoid 函数](https://en.wikipedia.org/wiki/Sigmoid_function):![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/b94efd45bbc6b902f475b264d822b88c.png)Sigmoid 公式它将宽范围的浮点数(`x`轴)转换为有限范围的浮点数(`(0...1)``y`轴)。这正是我们所需要的。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/126f6cfbeb696151b42af8e35edbf347.png)Sigmoid 函数这是转换步骤在 Sigmoid 图上的样子。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/65973419e3c5218f1462d8f023f48cc5.png)作者插图上面提到的两个转换步骤的实现如下所示:

// Calculates the sigmoid value for a given number.
const sigmoid = (x: number): number => {
return 1 / (1 + Math.E ** -x);
};// Converts sigmoid value (0...1) to the muscle signals (-1, 0, +1)
// The margin parameter is a value between 0 and 0.5:
// [0 ... (0.5 - margin) ... 0.5 ... (0.5 + margin) ... 1]
const sigmoidToMuscleSignal = (sigmoidValue: number, margin: number = 0.4): MuscleSignal => {
if (sigmoidValue < (0.5 - margin)) {
return -1;
}
if (sigmoidValue > (0.5 + margin)) {
return 1;
}
return 0;
};// Converts raw brain signal to the muscle signal.
const brainToMuscleSignal = (rawBrainSignal: number): MuscleSignal => {
const normalizedBrainSignal = sigmoid(rawBrainSignal);
return sigmoidToMuscleSignal(normalizedBrainSignal);
}


# 汽车的基因组> 从上面的“眼睛”、“肌肉”和“大脑”部分得出的主要结论应该是:系数`[e0, e1, ..., e8]`和`[w0, w1, ..., w8]`定义了汽车的行为。这些数字共同构成了独特的汽车基因组(或汽车的 DNA)。# 十进制形式的汽车基因组让我们将`[e0, e1, ..., e8]`和`[w0, w1, ..., w8]`大脑系数连接在一起,以十进制形式构成一辆汽车的基因组:

// Car genome as a list of decimal numbers (coefficients).
const carGenomeBase10 = [e0, e1, ..., e8, w0, w1, ..., w8];// i.e. carGenomeBase10 = [17.5, 0.059, -46, 25, 156, -0.085, -0.207, -0.546, 0.071, -58, 41, 0.011, 252, -3.5, -0.017, 1.532, -360, 0.157]


# 二进制形式的汽车基因组让我们再深入一步(到基因的层面),将汽车基因组的十进制数字转换成二进制格式(转换成普通的`1` s 和`0` s)。> 我已经在浮点数的二进制表示的文章[中详细描述了将浮点数转换成二进制数的过程。如果本节中的代码不清楚,您可能想查看一下。](https://trekhleb.dev/blog/2021/binary-floating-point/)下面是一个如何将浮点数转换成二进制数的简单例子(如果这个例子令人困惑,请随意阅读第一个):![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/73d114d320b627dfc20c515a0250fc10.png)浮点数的“十进制到二进制”转换示例。作者插图。在我们的例子中,为了减少基因组长度,我们将把每个浮点系数转换成非标准的`10 bits`二进制数(`1`符号位、`4`指数位、`5`分数位)。我们总共有`18`个系数,每个系数都会被转换成`10`位数。这意味着汽车的基因组将是一个长度为`18 * 10 = 180 bits`的`0`和`1`的数组。例如,对于上面提到的十进制格式的基因组,其二进制表示如下:在我们的例子中,为了减少基因组长度,我们将把每个浮点系数转换成非标准的`10 bits`二进制数(`1`符号位、`4`指数位、`5`分数位)。我们总共有`18`个系数,每个系数将被转换成`10`位数。这意味着汽车的基因组将是一个长度为`18 * 10 = 180 bits`的`0`和`1`的数组。例如,对于上面提到的十进制格式的基因组,其二进制表示如下:

type Gene = 0 | 1;

type Genome = Gene[];

const genome: Genome = [
// Engine coefficients.
0, 1, 0, 1, 1, 0, 0, 0, 1, 1, // <- 17.5
0, 0, 0, 1, 0, 1, 1, 1, 0, 0, // <- 0.059
1, 1, 1, 0, 0, 0, 1, 1, 1, 0, // <- -46
0, 1, 0, 1, 1, 1, 0, 0, 1, 0, // <- 25
0, 1, 1, 1, 0, 0, 0, 1, 1, 1, // <- 156
1, 0, 0, 1, 1, 0, 1, 1, 0, 0, // <- -0.085
1, 0, 1, 0, 0, 1, 0, 1, 0, 1, // <- -0.207
1, 0, 1, 1, 0, 0, 0, 0, 1, 1, // <- -0.546
0, 0, 0, 1, 1, 0, 0, 1, 0, 0, // <- 0.071

// Wheels coefficients.
1, 1, 1, 0, 0, 1, 1, 0, 1, 0, // <- -58
0, 1, 1, 0, 0, 0, 1, 0, 0, 1, // <- 41
0, 0, 0, 0, 0, 0, 1, 0, 1, 0, // <- 0.011
0, 1, 1, 1, 0, 1, 1, 1, 1, 1, // <- 252
1, 1, 0, 0, 0, 1, 1, 0, 0, 0, // <- -3.5
1, 0, 0, 0, 1, 0, 0, 1, 0, 0, // <- -0.017
0, 0, 1, 1, 1, 1, 0, 0, 0, 1, // <- 1.532
1, 1, 1, 1, 1, 0, 1, 1, 0, 1, // <- -360
0, 0, 1, 0, 0, 0, 1, 0, 0, 0, // <- 0.157
];


我的天啊。二元基因组看起来如此神秘。但是你能想象吗,仅仅这些 0 和 1 就定义了汽车在停车场的行为!就像你黑了某人的 DNA,知道每个基因的确切含义。太神奇了!顺便说一下,你可以在[进化模拟器](https://trekhleb.dev/self-parking-car-evolution?parking=evolution#/)仪表盘上看到性能最好的汽车的基因组和系数的准确值:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/823db3726f506a611b4832e9431235ff.png)进化模拟器仪表板截图下面是执行浮点数从二进制到十进制格式转换的源代码(大脑需要它来解码基因组并根据基因组数据产生肌肉信号):

type Bit = 0 | 1;type Bits = Bit[];type PrecisionConfig = {
signBitsCount: number,
exponentBitsCount: number,
fractionBitsCount: number,
totalBitsCount: number,
};type PrecisionConfigs = {
custom: PrecisionConfig,
};const precisionConfigs: PrecisionConfigs = {
// Custom-made 10-bits precision for faster evolution progress.
custom: {
signBitsCount: 1,
exponentBitsCount: 4,
fractionBitsCount: 5,
totalBitsCount: 10,
},
};// Converts the binary representation of the floating-point number to decimal float number.
function bitsToFloat(bits: Bits, precisionConfig: PrecisionConfig): number {
const { signBitsCount, exponentBitsCount } = precisionConfig; // Figuring out the sign.
const sign = (-1) ** bits[0]; // -1^1 = -1, -1^0 = 1 // Calculating the exponent value.
const exponentBias = 2 ** (exponentBitsCount - 1) - 1;
const exponentBits = bits.slice(signBitsCount, signBitsCount + exponentBitsCount);
const exponentUnbiased = exponentBits.reduce(
(exponentSoFar: number, currentBit: Bit, bitIndex: number) => {
const bitPowerOfTwo = 2 ** (exponentBitsCount - bitIndex - 1);
return exponentSoFar + currentBit * bitPowerOfTwo;
},
0,
);
const exponent = exponentUnbiased - exponentBias; // Calculating the fraction value.
const fractionBits = bits.slice(signBitsCount + exponentBitsCount);
const fraction = fractionBits.reduce(
(fractionSoFar: number, currentBit: Bit, bitIndex: number) => {
const bitPowerOfTwo = 2 ** -(bitIndex + 1);
return fractionSoFar + currentBit * bitPowerOfTwo;
},
0,
); // Putting all parts together to calculate the final number.
return sign * (2 ** exponent) * (1 + fraction);
}// Converts the 8-bit binary representation of the floating-point number to decimal float number.
function bitsToFloat10(bits: Bits): number {
return bitsToFloat(bits, precisionConfigs.custom);
}


# 大脑功能与二元基因组一起工作以前,我们的大脑功能是直接处理十进制形式的`engineCoefficients`和`wheelCoefficients`多项式系数。然而,这些系数现在是以基因组的二进制形式编码的。让我们添加一个`decodeGenome()`功能,从基因组中提取系数,让我们重写我们的大脑功能:

// Car has 16 distance sensors.
const CAR_SENSORS_NUM = 8;

// Additional formula coefficient that is not connected to a sensor.
const BIAS_UNITS = 1;

// How many genes do we need to encode each numeric parameter for the formulas.
const GENES_PER_NUMBER = precisionConfigs.custom.totalBitsCount;

// Based on 8 distance sensors we need to provide two formulas that would define car's behavior:
// 1. Engine formula (input: 8 sensors; output: -1 (backward), 0 (neutral), +1 (forward))
// 2. Wheels formula (input: 8 sensors; output: -1 (left), 0 (straight), +1 (right))
const ENGINE_FORMULA_GENES_NUM = (CAR_SENSORS_NUM + BIAS_UNITS) * GENES_PER_NUMBER;
const WHEELS_FORMULA_GENES_NUM = (CAR_SENSORS_NUM + BIAS_UNITS) * GENES_PER_NUMBER;

// The length of the binary genome of the car.
const GENOME_LENGTH = ENGINE_FORMULA_GENES_NUM + WHEELS_FORMULA_GENES_NUM;

type DecodedGenome = {
engineFormulaCoefficients: Coefficients,
wheelsFormulaCoefficients: Coefficients,
}

// Converts the genome from a binary form to the decimal form.
const genomeToNumbers = (genome: Genome, genesPerNumber: number): number[] => {
if (genome.length % genesPerNumber !== 0) {
throw new Error('Wrong number of genes in the numbers genome');
}
const numbers: number[] = [];
for (let numberIndex = 0; numberIndex < genome.length; numberIndex += genesPerNumber) {
const number: number = bitsToFloat10(genome.slice(numberIndex, numberIndex + genesPerNumber));
numbers.push(number);
}
return numbers;
};

// Converts the genome from a binary form to the decimal form
// and splits the genome into two sets of coefficients (one set for each muscle).
const decodeGenome = (genome: Genome): DecodedGenome => {
const engineGenes: Gene[] = genome.slice(0, ENGINE_FORMULA_GENES_NUM);
const wheelsGenes: Gene[] = genome.slice(
ENGINE_FORMULA_GENES_NUM,
ENGINE_FORMULA_GENES_NUM + WHEELS_FORMULA_GENES_NUM,
);

const engineFormulaCoefficients: Coefficients = genomeToNumbers(engineGenes, GENES_PER_NUMBER);
const wheelsFormulaCoefficients: Coefficients = genomeToNumbers(wheelsGenes, GENES_PER_NUMBER);

return {
engineFormulaCoefficients,
wheelsFormulaCoefficients,
};
};

// Update brain function for the engine muscle.
export const getEngineMuscleSignal = (genome: Genome, sensors: Sensors): MuscleSignal => {
const {engineFormulaCoefficients: coefficients} = decodeGenome(genome);
const rawBrainSignal = linearPolynomial(coefficients, sensors);
return brainToMuscleSignal(rawBrainSignal);
};

// Update brain function for the wheels muscle.
export const getWheelsMuscleSignal = (genome: Genome, sensors: Sensors): MuscleSignal => {
const {wheelsFormulaCoefficients: coefficients} = decodeGenome(genome);
const rawBrainSignal = linearPolynomial(coefficients, sensors);
return brainToMuscleSignal(rawBrainSignal);
};


# 自动驾驶汽车问题声明> ☝🏻所以,最后,我们已经到了让汽车成为自动泊车汽车的高层次问题被分解为寻找`180`1 和 0 的最佳组合(寻找“足够好”的汽车基因组)的简单优化问题的地步。听起来很简单,不是吗?# 天真的方法我们可以用一种简单的方法来解决寻找“足够好”的基因组的问题,并尝试所有可能的基因组合:1.  `[0, ..., 0, 0]`,然后...
2.  `[0, ..., 0, 1]`,然后...
3.  `[0, ..., 1, 0]`,然后...
4.  `[0, ..., 1, 1]`,然后...
5.  …但是,让我们做一些数学计算。使用`180`位,并且每个位等于`0`或`1`,我们将有`2^180`(或`1.53 * 10^54`)种可能的组合。假设我们需要给每辆车一个`15s`,看看它是否能成功停车。我们还可以说,我们可能马上对`10`汽车进行模拟。那么我们将需要`15 * (1.53 * 10^54) / 10 = 2.29 * 10^54 [seconds]`,也就是`7.36 * 10^46 [years]`。相当长的等待时间。就像一个侧面的想法,它只是在基督诞生后已经过去了。# 遗传学方法我们需要一个更快的算法来寻找基因组的最优值。这就是遗传算法的救援之处。我们可能找不到基因组的最佳价值,但有可能找到它的最佳价值。更重要的是,我们不需要等那么久。有了进化模拟器,我能够在`24 [hours]`中找到一个非常好的基因组。# 遗传算法基础一种[遗传算法](https://en.wikipedia.org/wiki/Genetic_algorithm) (GA),受自然选择过程的启发,通常用于依靠生物启发算子生成优化问题的高质量解决方案,如*交叉*、*变异*和*选择*。为汽车找到“足够好”的基因组合的问题看起来像一个优化问题,所以 GA 很有可能在这里帮助我们。我们不会涵盖遗传算法的所有细节,但在高层次上,这里是我们需要做的基本步骤:1.  **创建** —第一代汽车[不可能无中生有](https://en.wikipedia.org/wiki/Laws_of_thermodynamics),所以我们会在最开始生成一组随机的汽车基因组(长度为`180`的一组二进制数组)。例如,我们可以制造`~1000`汽车。随着人口的增加,找到最优解(并且更快地找到)的机会也增加了。
2.  选择——我们需要从这一代中选择最合适的个体进行进一步的交配(见下一步)。每个个体的适应度将基于适应度函数来定义,在我们的情况下,适应度函数将显示汽车有多接近目标停车位。车离停车点越近越合适。
3.  **与**交配——简单地说,我们将允许被选中的 *"♂父车"*"与被选中的 *"♀母车"*有*"性别"*,这样它们的基因组就可以按`~50/50`比例混合,产生*"♀♀子车"*基因组。这个想法是,通过从父母那里获取最好(或最差)的部分,儿童汽车在自动停车方面可能会变得更好(或更差)。
4.  **突变**——在交配过程中,一些基因可能会随机突变(子基因组中的`1` s 和`0` s 可能会翻转)。这可能会带来更广泛的儿童基因组,因此,更广泛的儿童汽车行为。想象一下,对于所有的`~1000`汽车,第 1 位被意外设置为`0`。第一位设置为`1`时,测试汽车的唯一方法是通过随机突变。同时,广泛的突变可能会破坏健康的基因组。
5.  除非代的数量已经达到极限(即`100`代已经过去)或者除非表现最好的个体已经达到预期的适应度函数值(即最好的汽车已经比`1 meter`更接近停车点),否则转到“步骤 2”。否则,退出。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/6a14a028cba4cc114e670f77e18e6177.png)遗传算法流程。作者插图# 用遗传算法进化汽车大脑在启动遗传算法之前,让我们为算法的“创建”、“选择”、“交配”和“变异”步骤创建函数。# 创建步骤的函数`createGeneration()`函数将创建一个随机基因组数组(也称为群体或世代),并将接受两个参数:*   `generationSize` -定义代的大小。这一代人的规模将一代一代地保持下去。
*   `genomeLength` -定义 cars 群体中每个个体的基因组长度。在我们的例子中,基因组的长度将是`180`。基因组中的每个基因都有`50/50`的机会成为`0`或`1`。

type Generation = Genome[];

type GenerationParams = {
generationSize: number,
genomeLength: number,
};

function createGenome(length: number): Genome {
return new Array(length)
.fill(null)
.map(() => (Math.random() < 0.5 ? 0 : 1));
}

function createGeneration(params: GenerationParams): Generation {
const { generationSize, genomeLength } = params;
return new Array(generationSize)
.fill(null)
.map(() => createGenome(genomeLength));
}


# 变异步骤的函数`mutate()`函数会根据`mutationProbability`值随机变异一些基因。例如,如果`mutationProbability = 0.1`那么每个基因组都有`10%`的机会发生突变。比方说,如果我们有一个长度为`10`的基因组,看起来像`[0, 0, 0, 0, 0, 0 ,0 ,0 ,0 ,0]`,那么在突变之后,有可能 1 个基因会发生突变,我们可能会得到一个看起来像`[0, 0, 0, 1, 0, 0 ,0 ,0 ,0 ,0]`的基因组。

// The number between 0 and 1.
type Probability = number;// @see: https://en.wikipedia.org/wiki/Mutation_(genetic_algorithm)
function mutate(genome: Genome, mutationProbability: Probability): Genome {
for (let geneIndex = 0; geneIndex < genome.length; geneIndex += 1) {
const gene: Gene = genome[geneIndex];
const mutatedGene: Gene = gene === 0 ? 1 : 0;
genome[geneIndex] = Math.random() < mutationProbability ? mutatedGene : gene;
}
return genome;
}


# 配对步骤的功能`mate()`函数将接受`father`和`mother`基因组,并将产生两个子代。我们将模仿真实世界的场景,并在交配过程中进行变异。孩子基因组的每一位都将根据父亲或母亲基因组相应位的值来定义。孩子有可能继承父亲或母亲的一部分。例如,假设我们有长度为`4`的基因组(为了简单起见):

Father's genome: [0, 0, 1, 1]
Mother's genome: [0, 1, 0, 1]
↓ ↓ ↓ ↓
Possible kid #1: [0, 1, 1, 1]
Possible kid #2: [0, 0, 1, 1]


在上面的例子中,突变没有考虑在内。下面是该函数的实现:

// Performs Uniform Crossover: each bit is chosen from either parent with equal probability.
// @see: https://en.wikipedia.org/wiki/Crossover_(genetic_algorithm)
function mate(
father: Genome,
mother: Genome,
mutationProbability: Probability,
): [Genome, Genome] {
if (father.length !== mother.length) {
throw new Error('Cannot mate different species');
} const firstChild: Genome = [];
const secondChild: Genome = []; // Conceive children.
for (let geneIndex = 0; geneIndex < father.length; geneIndex += 1) {
firstChild.push(
Math.random() < 0.5 ? father[geneIndex] : mother[geneIndex]
);
secondChild.push(
Math.random() < 0.5 ? father[geneIndex] : mother[geneIndex]
);
} return [
mutate(firstChild, mutationProbability),
mutate(secondChild, mutationProbability),
];
}


# 选择步骤的功能为了选择最适合的个体进行进一步交配,我们需要一种方法来找出每个基因组的适合度。为此,我们将使用所谓的适应度函数。适应度函数总是与我们试图解决的特定任务相关,它不是通用的。在我们的例子中,适应度函数将测量汽车和停车点之间的距离。车离停车点越近越合适。稍后我们将实现健身功能,但现在,让我们介绍它的接口:

type FitnessFunction = (genome: Genome) => number;


现在,假设我们有每个个体的适应值。我们还可以说,我们根据个体的健康值对所有个体进行了分类,因此第一个个体是最强壮的。我们应该如何从这个数组中选择父亲和母亲呢?我们需要以某种方式进行选择,个体的适应值越高,这个个体被选择交配的机会就越高。`weightedRandom()`函数将在这方面帮助我们。

// Picks the random item based on its weight.
// The items with a higher weight will be picked more often.
const weightedRandom = (items: T[], weights: number[]): { item: T, index: number } => {
if (items.length !== weights.length) {
throw new Error('Items and weights must be of the same size');
} // Preparing the cumulative weights array.
// For example:
// - weights = [1, 4, 3]
// - cumulativeWeights = [1, 5, 8]
const cumulativeWeights: number[] = [];
for (let i = 0; i < weights.length; i += 1) {
cumulativeWeights[i] = weights[i] + (cumulativeWeights[i - 1] || 0);
} // Getting the random number in a range [0...sum(weights)]
// For example:
// - weights = [1, 4, 3]
// - maxCumulativeWeight = 8
// - range for the random number is [0...8]
const maxCumulativeWeight = cumulativeWeights[cumulativeWeights.length - 1];
const randomNumber = maxCumulativeWeight * Math.random(); // Picking the random item based on its weight.
// The items with higher weight will be picked more often.
for (let i = 0; i < items.length; i += 1) {
if (cumulativeWeights[i] >= randomNumber) {
return {
item: items[i],
index: i,
};
}
}
return {
item: items[items.length - 1],
index: items.length - 1,
};
};


这个函数的用法非常简单。假设你真的很喜欢香蕉,想比草莓更常吃香蕉。然后你可以调用`const fruit = weightedRandom(['banana', 'strawberry'], [9, 1])`,并且在`10`之外的`≈9`情况下`fruit`变量将等于`banana`,并且只有在`10`之外的`≈1`时间内它将等于`strawberry`。为了避免在交配过程中失去最好的个体(姑且称之为冠军),我们也可以引入一个所谓的`longLivingChampionsPercentage`参数。例如,如果是`longLivingChampionsPercentage = 10`,那么上一代最好的车中的`10%`将被带到新一代。你可以这样想,因为有些长寿的个体可以长寿,可以看到他们的孩子甚至孙子。下面是`select()`功能的实际实现:

// The number between 0 and 100.
type Percentage = number;type SelectionOptions = {
mutationProbability: Probability,
longLivingChampionsPercentage: Percentage,
};// @see: https://en.wikipedia.org/wiki/Selection_(genetic_algorithm)
function select(
generation: Generation,
fitness: FitnessFunction,
options: SelectionOptions,
) {
const {
mutationProbability,
longLivingChampionsPercentage,
} = options; const newGeneration: Generation = []; const oldGeneration = [...generation];
// First one - the fittest one.
oldGeneration.sort((genomeA: Genome, genomeB: Genome): number => {
const fitnessA = fitness(genomeA);
const fitnessB = fitness(genomeB);
if (fitnessA < fitnessB) {
return 1;
}
if (fitnessA > fitnessB) {
return -1;
}
return 0;
}); // Let long-liver champions continue living in the new generation.
const longLiversCount = Math.floor(longLivingChampionsPercentage * oldGeneration.length / 100);
if (longLiversCount) {
oldGeneration.slice(0, longLiversCount).forEach((longLivingGenome: Genome) => {
newGeneration.push(longLivingGenome);
});
} // Get the data about he fitness of each individuum.
const fitnessPerOldGenome: number[] = oldGeneration.map((genome: Genome) => fitness(genome)); // Populate the next generation until it becomes the same size as a old generation.
while (newGeneration.length < generation.length) {
// Select random father and mother from the population.
// The fittest individuums have higher chances to be selected.
let father: Genome | null = null;
let fatherGenomeIndex: number | null = null;
let mother: Genome | null = null;
let matherGenomeIndex: number | null = null; // To produce children the father and mother need each other.
// It must be two different individuums.
while (!father || !mother || fatherGenomeIndex === matherGenomeIndex) {
const {
item: randomFather,
index: randomFatherGenomeIndex,
} = weightedRandom(generation, fitnessPerOldGenome); const {
item: randomMother,
index: randomMotherGenomeIndex,
} = weightedRandom(generation, fitnessPerOldGenome); father = randomFather;
fatherGenomeIndex = randomFatherGenomeIndex; mother = randomMother;
matherGenomeIndex = randomMotherGenomeIndex;
} // Let father and mother produce two children.
const [firstChild, secondChild] = mate(father, mother, mutationProbability); newGeneration.push(firstChild); // Depending on the number of long-living champions it is possible that
// there will be the place for only one child, sorry.
if (newGeneration.length < generation.length) {
newGeneration.push(secondChild);
}
} return newGeneration;
}


# 适应度函数汽车的适应性将由从汽车到停车点的距离来定义。距离越高,适合度越低。我们将计算的最终距离是从`4`车轮到停车场相应的`4`角落的平均距离。这个距离我们称之为`loss`,它与`fitness`成反比。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/8b325fb471b1810121cf37efad972e22.png)测量汽车的适应性。作者插图。单独计算每个车轮和每个拐角之间的距离(而不是只计算从汽车中心到停车点中心的距离)将使汽车保持相对于停车点的正确方向。空间中两点之间的距离将根据[勾股定理](https://en.wikipedia.org/wiki/Pythagorean_theorem)计算,如下所示:

type NumVec3 = [number, number, number];// Calculates the XZ distance between two points in space.
// The vertical Y distance is not being taken into account.
const euclideanDistance = (from: NumVec3, to: NumVec3) => {
const fromX = from[0];
const fromZ = from[2];
const toX = to[0];
const toZ = to[2];
return Math.sqrt((fromX - toX) ** 2 + (fromZ - toZ) ** 2);
};


汽车和停车位之间的距离(T0)将这样计算:

type RectanglePoints = {
fl: NumVec3, // Front-left
fr: NumVec3, // Front-right
bl: NumVec3, // Back-left
br: NumVec3, // Back-right
};type GeometricParams = {
wheelsPosition: RectanglePoints,
parkingLotCorners: RectanglePoints,
};const carLoss = (params: GeometricParams): number => {
const { wheelsPosition, parkingLotCorners } = params; const {
fl: flWheel,
fr: frWheel,
br: brWheel,
bl: blWheel,
} = wheelsPosition; const {
fl: flCorner,
fr: frCorner,
br: brCorner,
bl: blCorner,
} = parkingLotCorners; const flDistance = euclideanDistance(flWheel, flCorner);
const frDistance = euclideanDistance(frWheel, frCorner);
const brDistance = euclideanDistance(brWheel, brCorner);
const blDistance = euclideanDistance(blWheel, blCorner); return (flDistance + frDistance + brDistance + blDistance) / 4;
};


由于`fitness`应该与`loss`成反比,我们将这样计算:

const carFitness = (params: GeometricParams): number => {
const loss = carLoss(params);
// Adding +1 to avoid a division by zero.
return 1 / (loss + 1);
};


您可以在[进化模拟器](https://trekhleb.dev/self-parking-car-evolution?parking=evolution#/)仪表盘上看到特定基因组和当前汽车位置的`fitness`和`loss`值:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/5428c320226b38d5bf63440de653093a.png)进化模拟器仪表板截图# 启动进化让我们把进化函数放在一起。我们将“创造世界”,启动进化循环,让时间前进,让一代人进化,让汽车学会如何停车。为了获得每辆车的适应值,我们需要在虚拟的 3D 世界中运行汽车行为的模拟。[进化模拟器](https://trekhleb.dev/self-parking-car-evolution)就是这么做的——它在模拟器中运行下面的代码,模拟器是用 Three.js 制作的[:](https://github.com/trekhleb/self-parking-car-evolution)

// Evolution setup example.
// Configurable via the Evolution Simulator.
const GENERATION_SIZE = 1000;
const LONG_LIVING_CHAMPIONS_PERCENTAGE = 6;
const MUTATION_PROBABILITY = 0.04;
const MAX_GENERATIONS_NUM = 40;// Fitness function.
// It is like an annual doctor's checkup for the cars.
const carFitnessFunction = (genome: Genome): number => {
// The evolution simulator calculates and stores the fitness values for each car in the fitnessValues map.
// Here we will just fetch the pre-calculated fitness value for the car in current generation.
const genomeKey = genome.join('');
return fitnessValues[genomeKey];
};// Creating the "world" with the very first cars generation.
let generationIndex = 0;
let generation: Generation = createGeneration({
generationSize: GENERATION_SIZE,
genomeLength: GENOME_LENGTH, // <- 180 genes
});// Starting the "time".
while(generationIndex < MAX_GENERATIONS_NUM) {
// SIMULATION IS NEEDED HERE to pre-calculate the fitness values. // Selecting, mating, and mutating the current generation.
generation = select(
generation,
carFitnessFunction,
{
mutationProbability: MUTATION_PROBABILITY,
longLivingChampionsPercentage: LONG_LIVING_CHAMPIONS_PERCENTAGE,
},
); // Make the "time" go by.
generationIndex += 1;
}// Here we may check the fittest individuum of the latest generation.
const fittestCar = generation[0];


运行`select()`函数后,`generation`数组按照适合度值降序排序。因此,最合适的车将永远是数组中的第一辆车。**拥有随机基因组的第一代**汽车将会有如下表现:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/7cfb20c202834cfeeb80bf83567f5edb.png)第一代汽车。作者制作的动画**在第 40 代车型上**汽车开始学习什么是自动泊车,并开始靠近泊车点:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/237f030840308cc734fb4913e3d0483e.png)第 40 代车。作者制作的动画另一个例子更具挑战性:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/55d0908aa264ccab265f60fb305bcfdd.png)第 40 代车。作者制作的动画这些车一路上撞上了其他一些车,也不完全适合停车点,但这只是自世界诞生以来它们的第 40 代,所以你可以给这些车一些时间来学习。一代又一代,我们可能会看到损失值是如何下降的(这意味着适应值在上升)。`P50 Avg Loss`显示最合适的`50%`辆汽车的平均损失值(从汽车到停车位的平均距离)。`Min Loss`显示每一代最适合的汽车的损失值。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/4e70a254212a22f215351bd5bcada128.png)损失历史。作者插图。你可能会看到,平均而言,这一代最健康的汽车中的`50%`正在学习更靠近停车位(从远离停车位的`5.5m`到 35 代中的`3.5m`)。`Min Loss`值的趋势不太明显(从`1m`到`0.5m`有一些噪声信号),但是从上面的动画中你可以看到汽车已经学会了一些基本的停车动作。# 结论在本文中,我们将创建自动泊车汽车的高级任务分解为寻找`180`1 和 0 的最佳组合(寻找最佳汽车基因组)的简单低级任务。然后我们应用遗传算法来寻找最佳的汽车基因组。它允许我们在几个小时的模拟中获得相当好的结果(而不是多年来运行这种幼稚的方法)。您可以启动🚕[自动泊车汽车进化模拟器](https://trekhleb.dev/self-parking-car-evolution)直接在浏览器中查看进化过程。模拟器为您提供了以下机会:*   您可以[从头开始训练汽车](https://trekhleb.dev/self-parking-car-evolution?parking=evolution#/)并自行调整遗传参数
*   你可以[看到训练过的自动泊车车在运行](https://trekhleb.dev/self-parking-car-evolution?parking=automatic#/)
*   您也可以[尝试手动泊车](https://trekhleb.dev/self-parking-car-evolution?parking=manual#/)本文中展示的完整遗传源代码也可以在[进化模拟库](https://github.com/trekhleb/self-parking-car-evolution)中找到。如果你是那些真正会数并检查行数以确保少于 500 行(不包括测试)的人之一,请随意检查这里的代码[🥸.](https://github.com/trekhleb/self-parking-car-evolution/tree/master/src/libs)代码和模拟器仍有一些未解决的问题:*   汽车的大脑过于简单,它使用线性方程,而不是神经网络。这使得汽车不能适应新的环境或新的停车场类型。
*   当一辆车撞上另一辆车时,我们不会降低它的适应值。因此,汽车在制造交通事故时没有“感觉”到任何罪恶感。
*   进化模拟器不稳定。这意味着相同的汽车基因组可能产生不同的适应值,这使得进化效率更低。
*   evolution 模拟器在性能方面也非常笨重,因为我们无法一次性训练 1000 辆汽车,所以会减慢进化进度。
*   此外,Evolution Simulator 要求浏览器选项卡处于打开和活动状态,以执行模拟。
*   还有[更](https://github.com/trekhleb/self-parking-car-evolution/issues) …然而,本文的目的是在学习遗传算法如何工作的同时获得一些乐趣,而不是构建一个生产就绪的自动停车特斯拉。所以,即使有上面提到的问题,我还是希望您能愉快地阅读这篇文章。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/d0ded58baa97f405218cfef40b7b1a8b.png)作者插图# 自监督学习算法:带 IOUs 的矢量差和矢量和(VDVS)> 原文:<https://towardsdatascience.com/self-supervised-learning-algorithm-vector-difference-and-vector-sum-with-ious-vdvs-d8d4ecbdecfb?source=collection_archive---------42----------------------->## 一种使用嵌入向量的和与差的自监督学习算法***链接到 GitHub 中的资源库:***[***【https://github.com/evrimozmermer/vectorsum_vectordifference】***](https://github.com/evrimozmermer/vectorsum_vectordifference)# 介绍自我监督学习是深度学习模型的学习方法,它试图在没有人类监督的情况下捕捉有意义的特征,从而迫使模型将输入数据映射到特定的标签。 [**10L —计算机视觉中的自监督学习**](https://www.youtube.com/watch?v=8L10w1KoOU8&list=PLLHTzKZzVU9e6xUfG10TkTWApKSZCzuBI&index=21) **中提到了几种自监督学习方法。**在这篇文章里,我分享一个我做的方法(据我所知)。# 提议的方法所提出的方法使用在 [**VeriMedi:使用基于代理的深度度量学习和精确解的药丸识别**](https://arxiv.org/ftp/arxiv/papers/2104/2104.11231.pdf) **的未来研究部分中所述的向量和逻辑。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/fb01fff05df42c60088484d63043b5e0.png)作者提供的图片-建议方法的图表步骤:1.  在图像的左上角定义随机坐标。
2.  用定义的随机坐标和输入图像的一半大小裁剪随机片段。将裁剪的图像调整到输入图像大小。计算裁剪图像组合的并集交集(IOU)。
3.  将裁剪后的图像提供给模型,并生成嵌入向量。(Ic:裁剪图像,Ec:裁剪图像的嵌入向量)
4.  计算嵌入向量(Ec)组合的余弦相似度。(C:组合如[(0,1),(0,2),(1,2)])。
5.  计算 loss 1,其中 as-is 参数为 Ec,to-be 参数为 IOUs。
6.  将输入图像提供给模型,并生成嵌入向量。(Ei:输入图像的嵌入向量)
7.  对裁剪图像的嵌入向量求和,并计算 Ei 和 Sum(Ec)之间的余弦相似度。
8.  根据输入图像上裁剪图像的遮罩总和计算 IOU。
9.  计算损耗 2,其中现状参数是上一步中的 IOU,目标参数是 Ei 与 Sum(Ec)之间的余弦相似性。
10.  合计损失并反向传播损失。# 损失函数为了计算损失,我使用了欧拉数,因为它的图形。计算损耗时,我从现有参数(D)中减去未来值。然后,我把 D 的欧拉数的幂加上一个系数。然后,我从总函数中减去 1,将图形下移。红线表示正损失,蓝线表示负损失。## Python 中的损失

def criterion(to_be, as_is):
diff = as_is-to_be
res = torch.where(diff>0,
torch.abs((torch.exp(4(diff))-1)).sum(),
torch.abs((torch.exp(4
(-diff))-1)).sum())
return res.sum()


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/ddf7616872030f38bff846d578dbdd09.png)作者图片-损失函数的图表(x 轴上方的区域)# 实验和结果我使用了在 [**VeriMedi:使用基于代理的深度度量学习和精确解的药丸识别**](https://arxiv.org/ftp/arxiv/papers/2104/2104.11231.pdf) **中提到的 ShakeNet 数据集。**对于模型,我使用了预训练的(使用 ImageNet 数据集)ResNet-34。我在模型的末尾添加了一个嵌入层。首先,我在没有任何培训的情况下测试了这个模型。我有 74.96%的准确率。然后,我对模型进行了 15 个纪元的训练。准确率上升到 90.69%。这说明该方法有助于模型提取更好、更有意义的特征。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/e71ae1fffa108cc746ae4bdc509181f1.png)作者图片# 视觉变形器中的自我监督学习> 原文:<https://towardsdatascience.com/self-supervised-learning-in-vision-transformers-30ff9be928c?source=collection_archive---------6----------------------->## 什么是自我监督学习,它是如何应用于视觉变形金刚的?![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/60d142cd32dd109f400e90531e8342f1.png)任何曾经接触过机器学习世界的人肯定听说过监督学习和非监督学习。事实上,这是机器学习的两种重要的可能方法,已经被广泛使用了多年。然而,直到最近才出现了一个新术语,自我监督学习!但是让我们一步一步来,一个一个地看各种方法,试图找到与人脑的类比。**监督学习**就像“基于标签化例子的学习”。该模型使用标记的数据进行训练,因此它们已经以这样的方式被仔细标记,即每个示例都与特定的类相关联。通过研究每个类别的各种示例的特征,模型学会了归纳,甚至能够对它从未见过的数据进行分类。因此,为了应用这种方法,需要标记良好的数据,但这些数据并不总是可用的,而且模型可能会因标记方式的不同而产生偏差。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/f79083079f3cc259f04dfe31fdef124b.png)作者图片*与人脑的类比:研究一本书,这本书通过向你展示大量有标签的例子,明确地告诉你什么是狗,什么是猫。***相反,无监督学习**包括在未标记的数据中搜索具有共同特征的样本组。在大多数情况下,这些方法与聚类相关。无监督方法不需要对数据集进行标记,但需要许多示例、计算资源和一个函数来描述它们之间的差异,这并不总是容易的。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/1bfad3c41e9ee665e744a22e0d09e9fc.png)作者图片*与人脑的类比:观察许多跑来跑去的狗和猫,分辨出哪些是狗,哪些是猫,把它们分成两组。***自我监督学习**是一种创新的无监督方法,正在享受巨大的成功,现在被许多人认为是机器学习的未来[1,3,6]。主要方法是在数据集上进行训练,例如图像数据集,但是这些数据集的每一个都以其原始形式和转换版本作为输入提供。这些变换可以是任何类型的,例如裁剪或旋转。
模型必须设法最小化内部网络和接收转换图像的网络之间的预测差异,内部网络将原始图像作为输入,因此具有完整且不变的输入视觉。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/07d622f5eb8f96abe7cd443cb95b5138.png)作者图片使用这种方法,可以看到生成的模型可以学习以一种出色的方式进行概括,并且不需要标签,从而生成输入的高质量表示,在某些情况下甚至比监督方法更好!用这种方法训练的模型随后将学习自己的表示系统,在该系统中,从相似对象获得的变换图像将是接近的。*与人脑的类比:*想象你正在观察的事物中不存在的东西。比如想象一只笔看到它滚向桌子边缘就会掉下来,或者想象一只猫的尾巴末端虽然藏在树后面。# 为什么要在《视觉变形金刚》中进行自我监督学习?虽然视觉变压器可以实现比其他传统架构更好的结果,但它们的成功取决于对数据的大量需求。因此,以监督的方式训练这些模型需要大量的标记工作,这并不总是可能或可持续的。因此,实现视觉变压器的自我监督方法可能是一种可能的方式,使这些模型不仅强大,而且更容易应用于更广泛的问题。为了理解这种方法有多强大,让我们先来看看自然语言处理领域,在这个领域中,自我监督的方法已经使得实现不可思议的结果成为可能。GPT-3 是迄今为止最大的语言模型之一,拥有 1750 亿个参数,被认为是迈向人工通用智能(AGI)的第一步[7],能够翻译文本,总结文本,回答问题,甚至根据文字描述编写代码!但要训练这样一个大型模型,它也是基于变形金刚的,你需要大量的数据,特别是 GPT 3 号,它是通过抓取互联网收集的 570GB 文本信息来训练的。假设我们想以监督的方式训练这个模型,这将意味着手动标记所有这些数据,这简直是疯了!![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/95257d8f96c5558be72d9948458cf30d.png)作者图片也可以用其他更经典的无监督方法来克服这一障碍,但有必要定义一个合适的相似性度量(如果我们转向计算机视觉,想想这对图像意味着什么),并消耗更多的计算资源,然后可能最终得到一个较差的模型![在下面的段落中,视觉变形金刚的一些基本方面将被认为是理所当然的,如果你想更深入地了解这个主题,我建议你阅读我以前对这个架构的概述。](/transformers-an-exciting-revolution-from-text-to-videos-dc70a15e617b)# SiT:自我监督的视觉转换器鉴于以自我监督的方式训练模型具有无可置疑的优势,提出的一种可能的方法是自我监督视觉转换器(SiT) [4]。这种方法的基本假设是,通过基于来自整个视野的上下文从未损坏部分恢复图像的损坏部分,网络将隐含地学习视觉完整性的概念。在这种方法中,根据可用的可能策略之一,输入图像被破坏:随机丢弃、随机替换、颜色失真等。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/0cb86be5c3b9cef0b9013596d0d51679.png)图片来自 [SiT:自监督视觉转换器](https://arxiv.org/abs/2104.03602)然后,图像被分成小块,并与两个附加标记(用于旋转预测的旋转标记和用于对比学习的对比标记)一起通过经典视觉转换器机制。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/973d22bf9ec7a080c2e01ea0218779e5.png)图片由作者根据 [SiT:自监督视觉转换器](https://arxiv.org/abs/2104.03602)制作然后,来自变换器编码器的结果表示被变换回小块,并被重组以获得重建的图像。该模型应试图减少重建图像和原始图像之间的差异。然后,用这种方法训练的网络的权重可以用作另一个任务的起点,例如图像分类、对象检测、分割等。# 迪诺:无标签自行蒸馏取得最惊人结果的方法之一当然是 DINO [2],它通过一系列的数据扩充和使用知识提炼技术,已经能够以惊人的方式进行图像分割!![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/0dd8292d765b0223c078e6348a732c02.png)原始视频(左)、由监督模型获得的分割(中)和由 DINO 生成的分割(右)之间的比较。图片来自[脸书艾](https://ai.facebook.com/blog/dino-paws-computer-vision-with-self-supervised-transformers-and-10x-more-efficient-training/)。关于 DINO 及其架构的详细概述可以在我之前关于这种方法的文章中找到,点击这里阅读更多内容。这是目前提出的最有前途的方法之一,能够突出视觉变压器和自我监督相结合的可能性。# EsViT:高效的自我监督视觉转换器DINO 最近被用作一种新的更先进的视觉变压器的基础,称为高效自监督视觉变压器(EsViT) [8]。EsViT 还通过固定的教师网络和不断更新的学生网络来利用知识蒸馏,以尽量减少损失函数。在这种情况下,一个有趣的特点是,它采用了多级转换器而不是单片转换器,并利用稀疏注意力来减少计算。整个模型的损失函数由两个不同损失的组合给出,一个区域损失和一个视图损失。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/26189f3c186b234c26883e4e85ea05a6.png)EsViT 全局和局部令牌可视化。图片由作者基于[高效的自我监督视觉转换器进行表征学习](https://arxiv.org/abs/2104.14294?fbclid=IwAR1Tzadc3aLFTm8ck7StRkqDyCX9wLNhED__1a2BaRqe1Hll2pHVd7DVDAM)给定输入图像,一组不同的视图确实是使用不同的数据增强技术生成的。这些结果视图被配对以获得一组对。然后,每一对都被转换成令牌,并用于构成损失的第一部分,即视图损失。作者强调:“在 DINO 中,损失函数只在粗略的水平上鼓励“局部到全局”的对应:大作物和小作物在视图水平上匹配,留下未指定的区域到区域的对应”[8]。EsViT 解决这一问题的一个重要特性是,当移动到区域级别时,可以看到它。事实上,所考虑的图像对的两幅图像都被分成小块,然后通过一种特殊的密集自我监督学习方法来利用这些小块,该方法考虑到它们的对应关系,直接在局部特征的级别上工作。查看注意图可以看出,DINO 倾向于自动学习导致前景对象分割的类特定注意图,而不管它的查询位于前景还是背景,而 EsViT 的各个头学习的注意图更加多样化。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/aed634551a103d0e04824592d55827e0.png)迪诺(左)和埃斯维特(右)不同头部的注意力图。图片由作者基于[高效的自我监督视觉转换器进行表征学习](https://arxiv.org/abs/2104.14294?fbclid=IwAR1Tzadc3aLFTm8ck7StRkqDyCX9wLNhED__1a2BaRqe1Hll2pHVd7DVDAM)这种不同的方法使 EsViT 在 ImageNet 上取得了非常好的结果,并推出了更轻、更高效的模型!# 结论人类产生的数据量惊人且史无前例。据估计,每天产生 2.5 万亿字节,而且这个数字还在上升[11]。物联网系统正变得越来越普遍,传感器在任何给定的时间收集数据,社交网络的大规模使用及其可访问性允许任何人在几分钟内在网络上输入信息,卫星收集我们星球上的各种数据。想想看,仅在过去的两年里,世界上 90%的数据都是由我们生成的!这些数据是机器学习的黄金,是任何模型的燃料,其丰富程度可以打开无数应用的大门,这是我们今天甚至无法想象的。然而,难以想象的是,相信这些模型可以在监督下进行训练,因为这将需要耗费精力和不可持续的人工标记。因此,无人监督的方法,尤其是自我监督的方法,将在该领域变得越来越重要,并且与视觉转换器等新架构相结合,它们将成为机器学习未来的主要参与者。# 参考资料和见解[1]《脸书艾》。"自我监督学习:智能的暗物质"[2]《大卫·柯考米尼》。"迪诺上的[,无标签自行蒸馏](/on-dino-self-distillation-with-no-labels-c29e9365e382)"[3]《尼勒什·维杰拉尼亚》。"[计算机视觉的自监督学习方法](/self-supervised-learning-methods-for-computer-vision-c25ec10a91bd)"[4]“萨拉·阿蒂托等人”。" [SiT:自监督视觉转换器](https://arxiv.org/abs/2104.03602)"[5]《大卫·柯考米尼》。"[关于变压器、定时器和注意事项](/transformers-an-exciting-revolution-from-text-to-videos-dc70a15e617b)"[6]《马特维科夫通》。"自我监督学习,人工智能的未来"[7]《open ai》。" [GPT-3 驱动下一代应用](https://openai.com/blog/gpt-3-apps/)"[8]《李春燕等人》。"[用于表示学习的高效自监督视觉转换器](https://arxiv.org/abs/2106.09785)"[9]《大卫·柯考米尼》。"[注意力是你在《变形金刚》中真正需要的吗?](/is-attention-what-you-really-need-in-transformers-6c161c2fca83)[10]《大卫·柯考米尼》。"[视觉变压器还是卷积神经网络?都是!](/vision-transformers-or-convolutional-neural-networks-both-de1a2c3c62e4)[11]《伯纳德·马尔》。"[我们每天创造多少数据?每个人都应该阅读的令人兴奋的统计数据](https://bernardmarr.com/how-much-data-do-we-create-every-day-the-mind-blowing-stats-everyone-should-read/)[12]《德瓦尔·沙阿和阿布舍克·贾》。"[自我监督学习及其应用](https://neptune.ai/blog/self-supervised-learning)"# (自我)监督的预培训?自我训练?从哪个开始?> 原文:<https://towardsdatascience.com/self-supervised-pre-training-self-training-which-one-to-use-8c796be3779e?source=collection_archive---------14----------------------->## 自我监督的职前培训的现状如何?我们真的需要预培训吗?自我训练怎么样?最近,预训练一直是计算机视觉(也是 NLP)的热门话题,特别是 NLP 的突破之一— [BERT](https://arxiv.org/pdf/1810.04805.pdf) ,他提出了一种通过使用“自我监督”信号来训练 NLP 模型的方法。简而言之,我们提出了一种算法,它可以自己生成一个“伪标签”(意思是对于特定任务为真的标签),然后我们将学习任务视为一个有监督的学习任务,并生成伪标签。它通常被称为“托词任务”。例如,BERT 使用掩码词预测来训练模型(我们可以在它被训练后说它是预训练的模型),然后用我们想要的任务(通常称为“下游任务”)来微调模型,例如评论评论分类。屏蔽词预测是随机屏蔽句子中的一个词,并要求模型预测给定句子中的那个词是什么。因此,我们可以通过使用大量的**未标记的**训练数据(例如来自互联网的数百万个句子)来训练它,从而获得非常好的性能 NLP 模型,这节省了大量标记数据的时间。在 NLP 中,我们可以很容易地从互联网上获得一个句子,因为该句子是离散的,并且通常是合法的(例如,从维基百科获得一个句子,该句子是错误的和不可读的是不常见的)。因此,在 NLP 中定义一个托词任务是相当容易的。然而,图像要困难得多,因为信号是连续的,像素的范围是[0,255],我们人类很难解释一堆像素值,任何像素的移动对人类来说都不是问题,但对计算机来说则完全不同。在这篇文章中,我将介绍什么是预训练模型,下游任务,计算机视觉的自我监督学习的现状,如何定义计算机从图像中学习**有意义的**和**不变的**特征的借口任务,以及总是应用预训练是否好,是否有预训练的替代解决方案。我假设读者对 CNN 和深度学习有一些基本的了解。## 计算机视觉项目深度学习的一般流程在谈论具体术语之前,我们先来看看深度学习项目的全貌(专门针对计算机视觉项目,但应该适用于所有其他项目)

from torchvision.models import alexnetmodel = alexnet(pretrained=True) # set pretrained=True
custom_task_model = prepare_custom_model()
dataset = load_dataset()
dataloader = DataLoader(dataset)for epoch in range(epochs):
for data, label in dataloader:
# representation could be feature maps or vector
representation = model(data)
# compute specific outputs such as object detection outputs
output = custom_task_model(representation)
# compute specific task loss
loss = criterion(output, label)
loss.backward()
# update model and/or custom_task_model
optimizer.step()


对于所有几乎开源的研究项目,你都会看到上面的伪代码。常见的差异是*如何加载数据集、不同的模型架构、不同的任务和不同的损失函数(“标准”)。*正如你从上面的伪代码中注意到的,“表示”是任何特定任务(例如分类任务)中最重要的部分,它有很多名称,例如“嵌入”、“向量”、“特征”。字面意思是,这个实值向量是描述数据的“表示”,尽管它很难被人类解释,但它实际上对计算机理解数据是有意义的。这是因为我们希望计算机对输入的数据进行分类,但数据的像素太复杂,因此我们希望从数据的像素中提取“特征”,即**,以找出哪些像素的组合实际上能够描述数据。**因此,让计算机学会如何组合这些像素以便对它们进行分类是很重要的。## 预训练模型由 **torchvision.models** 提供的预训练模型通过监督学习(交叉熵损失)在 ImageNet1000 上进行训练。获得预训练模型的最简单方法是,当您从这个包中构造任何模型时,设置关键字“pretrained=True”(参见上面的伪代码)。在计算机视觉的深度学习社区中,我们总是从 ImageNet 预训练模型开始,并针对特定数据集的特定任务对模型进行微调,这是非常常见的。因为预训练模型有助于节省从零开始训练模型的时间,因为它已经学习的表示已经适合于(或者容易转移)特定的任务,例如图像分类或者对象检测。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/ef6fe48b825ccb9f174663286225b1d0.png)图来自 Yosinki 的“[通过深度可视化理解神经网络”。](https://arxiv.org/abs/1506.06579)例如,上图是一个经过训练的 AlexNet 上的深度可视化。特征图(那些黑色背景但带有一些白色点的)是一些特征的“激活”,意味着卷积层中的特定通道已经找到了*如何组合像素来表示一些有意义的内容*,(例如猫头)。所有预先训练的模型都有能力做到这一点(这是必须的!如果不是,这是一个坏的预训练模型哈哈…),因此用一个预训练模型开始你的项目绝对是一个好的选择。## 下游任务![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/fdc4a17e7cf0f92b0c3ac275acc3631f.png)作者编辑的图像。(附赠: [YOLO](https://pjreddie.com/darknet/yolo/) 、 [ImageNet](https://www.image-net.org/) 和 [CSAILVision](https://github.com/CSAILVision/semantic-segmentation-pytorch) )当研究论文提到“下游任务”(我初学的时候很困惑)时,字面意思是指预先训练好的模型在图像分类、物体检测、图像分割等方面能做什么。所有这些任务被称为“下游任务”,这实际上是大多数表征学习论文使用的一个技术术语。简而言之,我们使用预先训练的模型权重作为初始化来微调模型以执行特定的任务。因为某些领域(如医疗领域)很难获得数据,而且获取数据的成本很高,所以用预先训练好的模型进行初始化非常重要,如果有计算限制,这也会减少训练时间。## 我所知道的自我监督学习方法列表(到 2021 年 6 月)据我所知,二语习得可以分为基于任务的,基于对比学习的,基于聚类学习的。注意,有些论文会把自我监督学习说成无监督学习。此外,请注意,两种生成模型— AutoEncoder 和 GAN 也可以学习没有标签的表示,例如,您可以使用 AE 的编码器或 GAN 的鉴别器作为预训练模型(两者都将图像作为输入),但我没有在此列出它们,因为它们中的一些在任务上过于具体,因此我认为它不足以成为预训练模型。## 手工制作的借口任务一些研究人员建议让模型学习对不需要标记数据的人类设计的任务进行分类,但我们可以利用这些数据来生成标签。[上下文预测(预测位置关系)](https://arxiv.org/pdf/1505.05192.pdf)
[拼图](https://arxiv.org/pdf/1603.09246)
[预测旋转](https://arxiv.org/abs/1803.07728) [着色](https://arxiv.org/pdf/1603.06668.pdf)
[图像修复](https://openaccess.thecvf.com/content_cvpr_2018/papers/Yu_Generative_Image_Inpainting_CVPR_2018_paper.pdf)(学习填充图像中的空白区域)总而言之,这些任务使用算法来生成伪标签,以便模型可以通过交叉熵损失等监督学习来学习表示。关于算法的细节,你可以阅读我在列表中放了链接的论文。## 对比学习术语“对比”意味着区分,因此对比学习意味着学习在正样本和负样本之间进行比较。[sim clr](https://arxiv.org/abs/2002.05709)
[sim clr v2](https://arxiv.org/abs/2006.10029)
[Moco](https://arxiv.org/abs/1911.05722)
[Moco v2](https://sci-hub.do/https://arxiv.org/abs/2003.04297)
[PIRL](https://openaccess.thecvf.com/content_CVPR_2020/html/Misra_Self-Supervised_Learning_of_Pretext-Invariant_Representations_CVPR_2020_paper.html)这些方法有时也被称为“实例辨别”,因为它们利用实例的特征进行学习。简而言之,核心概念是**最大化相似特征向量之间的点积,最小化不相似特征向量之间的点积。**这些方法都有自己定义相似和相异的方法。并且它们中的大多数利用“数据增强”,即,正对是具有不同种类增强的相同图像,而负对只是不同的图像(也具有不同种类的增强)。这些方法是自我监督学习的当前趋势,因为这些论文声称对比学习可以学习更加**不变**的特征(例如,我之前展示的猫头的特征图,猫头在图像中的位置并不重要,只要猫头出现,它就会激活),因为它可以“比较”并找出两幅增强图像中的共同特征。## 聚类学习我将基于最近邻的学习称为聚类学习,因为我将每个数据点视为一个中心,并将最近邻视为一个聚类。[深度聚类](https://openaccess.thecvf.com/content_ECCV_2018/html/Mathilde_Caron_Deep_Clustering_for_ECCV_2018_paper.html)
[SeLA](https://arxiv.org/pdf/1911.05371.pdf)
[扫描](https://arxiv.org/abs/2005.12320)
[SwAV](https://arxiv.org/abs/2006.09882)这些方法更容易理解,我用深度聚类来解释,因为我认为这很简单。我们首先对特征(质心的数量是一个超参数)进行聚类,然后将聚类结果的预测标签作为伪标签,然后将其视为图像分类问题,并使用交叉熵损失进行学习。同样,不同的方法有它们自己的寻找伪标签的版本。我个人推荐基于聚类的学习,因为它更直观,但我个人认为其中两个大问题是如何高效准确地执行在线聚类(意味着不需要遍历所有数据,而只需遍历批处理本身)和如何正确定义质心的数量。(尽管如此,SwAV 说,只要质心的数量“足够多”,学习应该不是问题)。## 反思职前培训和自我培训嗯,题目其实和一篇叫“[反思预训和自训](https://arxiv.org/abs/2006.06882)”的论文一样。本文发现,在某些情况下,预训练不如自我训练。概括来说,自我训练法有如下几个常见步骤:1.  学习带有标记数据的(教师)模型(通常有很强的扩充)
2.  为来自(教师)模型的未标记数据生成软/硬伪标签
3.  使用标记数据和伪标记的未标记数据重新训练(学生)模型。
4.  重复步骤 1 2 和 3。(通过用学生模型初始化教师模型)它实际上是一种半监督学习方法。“自我”一词是指模型先用一些数据进行学习(并且模型是随机初始化的),然后用自己的知识对新的看不见的数据进行分类,把高度自信的预测结果作为新数据,用它们进行学习。因此,术语“自我”意味着模型在自我学习。这篇文章说,虽然自我训练比预训练慢得多,但它实际上有很多好处,例如**能够为特定任务**学习更多特定功能,**即使在预训练失败**时也能很好地工作,**在有更多数据的情况下甚至能很好地工作**。在论文中,他们展示了利用对象检测和语义分割进行自我训练的好处,这实际上是有意义的,因为来自预训练模型的特征是从分类任务中学习的,但是使其适应定位任务(即,对象检测)可能需要时间来调整(或者可能由于局部最小值而无法准确调整)。虽然从零开始训练会有所帮助,因为特性是从随机调整到特定的本地化任务,只是训练需要更多的时间。尽管如此,我仍然认为预先训练在实践中是很好的,因为我们可以节省很多训练模型的时间,特别是对于快速的演示或者模型部署。但是如果你想在你的研究领域打败 SOTA,你可以试试自我训练。## 结论希望这篇文章可以帮助你更多地了解预培训模式,自我监督学习方法,并且能够探索一个新的领域,这就是所谓的“自我培训”(虽然它对社区来说不是新的,但对我来说是新的)。基于自动编码器也可以被认为是自监督学习,因为它正在重构输入(标签是输入),但是一些论文说自动编码器(具体来说是基于重构的)将试图记住输入的每个细节,因此它不够“不变”。有一个**判别**的特征是好的,即使我们丢失了信息,只要这些信息对图像中的主要内容是重要的,并且我们可以将这些信息用于下游任务,那么它就被认为是*判别*和*不变*。如果你想了解更多关于自我监督学习的知识,我推荐你阅读这些调查, [1](https://ieeexplore.ieee.org/abstract/document/9086055) , [2](https://www.mdpi.com/2227-7080/9/1/2) , [3](https://arxiv.org/abs/2006.08218) 。你也可以在谷歌学术搜索关键词“自我监督学习调查”。还有另一篇论文叫做“[自我监督预训练对于视觉任务有多大用处?](https://openaccess.thecvf.com/content_CVPR_2020/papers/Newell_How_Useful_Is_Self-Supervised_Pretraining_for_Visual_Tasks_CVPR_2020_paper.pdf)”,解释了如何在视觉任务中获得自我监督预训练的帮助,如果你对自我监督学习感兴趣,我推荐你阅读。# 基于迁移学习的自监督语音情感识别> 原文:<https://towardsdatascience.com/self-supervised-voice-emotion-recognition-using-transfer-learning-d21ef7750a10?source=collection_archive---------19----------------------->## [实践教程](https://towardsdatascience.com/tagged/hands-on-tutorials)## 从语音音频构建自我监督的二元情感分类器![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/737bacf15ff84752f5d82fbf16b6a393.png)由[paweczerwi ski](https://unsplash.com/@pawel_czerwinski?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片你有没有注意到当听到有人用快乐的语调说一些非常可怕的事情时的不安感?我们习惯于听到声音的情感与演讲的内容相匹配。可以利用这种现象来自动生成用于训练语音情感分类器的弱标记数据。我在 Metis 数据科学训练营的最终项目的目标是从语音音频中构建一个自我监督的二进制情感分类器。情绪是复杂的多维概念,但在这个项目中,我建立了一个模型,给定一个音频剪辑,预测声音的情绪是积极还是消极。从声音中进行情感分类最困难的事情之一是标签数据的可用性,它非常稀缺。即使标记的数据是可用的,它要么被表现出来,听起来可能不同于真实的情绪,要么被独立地标记,这是非常耗时和/或主观的。在我们说什么和我们怎么说相关的假设下,我使用了一种自我监督的方法来使用音频抄本生成弱标记数据。## 方法学下图显示了构建分类器所需的处理步骤。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/a7475e69b45fcab4dd682322a4b8c41c.png)构建分类器的解决方案架构使用上述解决方案架构,构建模型所需的唯一数据是转录的音频文件,没有任何情感标签。左侧是处理音频文件所需的步骤,右侧是使用预先构建的情感分析库从脚本数据生成标签的步骤。在这个项目中,我使用了[卡内基梅隆大学的多模态意见情绪和情感强度数据集](http://multicomp.cs.cmu.edu/resources/cmu-mosei-dataset/)。这可以说是关于这个主题的最全面的数据集,涵盖了语言、视觉和听觉形态。该数据集由超过 1000 名在线 YouTube 发言人的超过 23,500 个句子话语视频组成。这是完全转录和适当的标点符号。虽然这种方法不需要任何情绪标签,但我使用这个数据集的原因是为了让我在未来对结果进行基准测试和比较。## 数据预处理数据集中的音频文件非常大,包含多个句子和非语音音频,如音乐。因此,第一个预处理步骤是从完整的音频文件中提取每个句子的音频片段。为了提取音频剪辑,我使用了抄本的时间戳。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/ea23de5c25da1ac7ed11015f56882b43.png)使用抄本时间戳按句子分割音频文件我使用的建模技术需要相同长度的特征,在我的例子中是相同长度的音频剪辑。因此,我决定根据数据中剪辑的平均持续时间,将我的音频文件裁剪或填充到 7 秒。长于 7 秒的音频剪辑是我用随机偏移剪辑的。对于较短的剪辑,我也用随机偏移填充了静音(零)。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/f94b8455e32fc2cba81e3d675b592703.png)将音频文件裁剪或填充到固定长度在分割和填充我的音频文件后,我使用了 [Librosa](https://librosa.org/doc/latest/index.html) 库将音频转换成 Mel 比例光谱图。这些是信号频谱随时间变化的直观表示,广泛用于音频分类。下面你可以看到数据集中两个随机音频文件的 Mel 光谱图。右边图两边的黑色区域是我用来填充音频剪辑的静音,因为它短于 7 秒。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/72853676f5fd6b7fca579c752fa1f810.png)来自数据集的两个随机音频文件的 Mel 光谱图对于文字记录,我使用了两个不同的现成文本情感分析库——[text blob](https://textblob.readthedocs.io/en/dev/quickstart.html)和 [Vader](https://pypi.org/project/vaderSentiment/) ,来测量句子的二元情感。我对每次观察的情绪得分进行了平均,并在此基础上对我的观察进行了二元分类——积极的和消极的。我排除了平均情绪绝对值非常低的观察结果。## 情感识别建模在数据预处理步骤之后,我将数据集分成 80%的训练集、10%的验证集和 10%的测试集。我使用 [ROC AUC](https://developers.google.com/machine-learning/crash-course/classification/roc-and-auc#:~:text=An%20ROC%20curve%20(receiver%20operating,False%20Positive%20Rate) 作为我的二元分类器的主要性能指标。这是一种通常用于具有不平衡类的二元分类器的性能度量。它的值范围从 0 到 1,无信息分类器产生 0.5,完美分类器产生 1。由于我的训练集只有略多于 11,000 个观察值,为了减少特征的数量,我对跨时间戳的 Mel 谱图值进行了平均。正如所料,许多谱图特征彼此高度相关。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/59de541d21a97f8bf42ca13eb7498bb3.png)特征之间的相关性我建立的一些经典机器学习模型产生了以下 ROC AUC 分数:随机森林 0.51,K-最近邻 0.54,深度神经网络 0.6。为了使用我的光谱图中的全部数据,而不求平均值,我决定使用迁移学习从我的光谱图中提取低维特征。为此,我使用了 [VGG16](https://keras.io/api/applications/vgg/) Keras 预训练网络。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/2758dfae5f81f87b5de29dd0ffe8e38f.png)VGG16 是由 K. Simonyan 和 A. Zisserman 提出的卷积神经网络![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/50dadcbef76d8b161f3efdf1c58ac704.png)作为 DNN 输入的低维特征我用这些提取的特征用 Keras 训练了一个模型。该模型在验证集上的性能为 0.61,在测试集上的性能为 0.63。这不是一个显著的改进,但由于它使用了整个光谱图,随着更多的观察,它有最大的改进潜力。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/2a4cafa2ceb538f2f82e4d742fe59017.png)DNN 用 VGG16 提取特征测试集 ROC 曲线## 用我自己的声音测试作为一个有趣的实验,我决定测试这个模型是否可以推广到其他语言。我录下了 8 段话语:我说的四种语言——英语、法语、俄语和亚美尼亚语——各有一段正面和一段负面的话语。然后我从这些声音片段中预测了情绪。结果值非常接近,因此我使用最小最大缩放在 0 和 1 之间缩放它们。基于这些极其有限的数据,似乎语言起了很大的作用。然而,在每种语言中,积极的情绪记录比消极的情绪记录得分高,这是一个很好的结果。下一步将是用适当的统计严谨性来研究这一现象。回放录制的话语## 结论总之,我已经创建了一个自我监督的机器学习分类器,它使用转移学习从语音中预测情感,并根据音频转录本生成的标签进行训练。像任何其他方法一样,这也有它的优点和缺点。优点主要归结为大量未标记数据的可用性和推广到其他语言的潜力。缺点大多与这样一个事实有关:我们所说的和我们所说的方式并不总是一致,这导致了大量不准确的标签。这种类型的模型可以有各种业务应用,其中之一是在呼叫中心使用它来衡量和提高代理提供的服务质量。请访问我的 [Github](https://github.com/SatenikS) 帐户,看看这个和我的其他项目的代码。也可以随时通过 [LinkedIn](https://www.linkedin.com/in/satenik-safaryan/) 联系我。# 自言自语:从文本生成转换器模型中获取知识> 原文:<https://towardsdatascience.com/self-talk-obtain-knowledge-from-text-generation-transformer-models-918277dbfc8b?source=collection_archive---------19----------------------->![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/5f4fe0159e2c1acbac37c395d34cc4b4.png)作者图片## 有了变形金刚模型,谁还需要字典?文本生成转换器模型确实令人印象深刻。当 OpenAI 认为其名为 GPT-2 的模型过于危险而不能发布时,它们首次引起了公众的注意。他们不可避免地发布了这个模型,包括它的最大版本,你现在只需要几行代码就可以使用。从那以后,这些型号在尺寸和性能方面都有了很大的发展。现在,OpenAI 的最新模型,称为 GPT-3,可以[执行基本运算](https://arxiv.org/abs/2005.14165)并生成[现实新闻文章](https://www.theguardian.com/commentisfree/2020/sep/08/robot-wrote-this-article-gpt-3)。本文将关注文本生成转换模型的最新应用之一——知识生成。从高层次来看,这些模型相当简单;他们试图继续你提供给他们的文本。现在,如果你向模型提出一个问题呢?嗯,模型将继续文本,因此,通常会尝试回答问题。通过这样做,他们将利用他们在培训中学到的知识来产生信息。到本文结束时,您将知道如何用最少的 Python 代码实现最先进的人工智能模型来执行这项任务。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/ec82d8e1be8bb167d942eb4c2ad437cc.png)作者图片# 发现艾伦人工智能研究所(AI2)第一个发现了变形金刚模型的这种应用,并将其命名为自言自语。我的本科顶点项目团队独立发现了一种方法,与 AI2 在得知他们的论文之前提出的方法非常相似。因此,我从我的顶点项目和 AI2 的论文中得到了一些见解来分享。# 基本实现在本教程中,我们将实现一个稍微简化的自言自语版本。我们将使用我自己的[快乐变形金刚](https://github.com/EricFillion/happy-transformer) Python 包,它是在[拥抱脸的变形金刚库](https://github.com/huggingface/transformers)之上的一个包装器。Happy Transformer 允许您用几行代码实现和训练 Transformer 模型——包括我们将在本教程中使用的文本生成模型。# 装置首先,PyPI 上提供了 Happy Transformer,因此我们可以用一行代码来安装它。

pip install happytransformer


# 下载模型现在,我们将从 Happy Transformer 中导入一个名为 HappyGeneration 的类来下载一个文本生成模型。

from happytransformer import HappyGeneration


从这里,我们可以加载一个名为 GPT-尼奥的 GPT-3 的完全开源版本。目前有三种不同尺寸的 GPT-尼奥模型可以在拥抱脸的模型分销网络上买到。我们将使用第二大(或者第二小,如果你感到悲观)模型,它有 1.3B 个参数。这里有一个[链接](https://huggingface.co/EleutherAI)到其他型号,如果你想使用它们。要创建 HappyGeneration 对象,我们必须为其第一个位置参数提供模型类型,为其第二个位置参数提供模型名称。在这种情况下,型号类型为“GPT-尼奥”,型号名称为“EleutherAI/gpt-neo-1.3B”,如型号的[网页](https://huggingface.co/EleutherAI/gpt-neo-1.3B)左上方所示。

happy_gen = HappyGeneration("GPT-NEO", "EleutherAI/gpt-neo-1.3B")


# 文本生成算法您可以使用不同的文本生成算法来生成文本。现在,本文的目的不是深入解释不同的文本生成算法,而是描述如何实现自言自语。在我的团队测试的四种不同的文本生成算法中,我们发现“波束搜索”算法是最有效的。这个算法是确定性的,这意味着每次运行它,都会得到相同的结果。要修改设置,我们必须导入一个名为 GENSettings 的类。然后,我们可以修改各种参数来选择和修改我们希望使用的算法。

from happytransformer import GENSettingsbeam_settings = GENSettings(num_beams=5, max_length=50, no_repeat_ngram_size=3)


AI2 使用了一种叫做 top-p 抽样的算法,p 值为 0.5 来生成答案。与波束搜索不同,该算法是不确定的,这意味着每次运行它时,它都会产生不同的文本。

top_p_settings = GENSettings(do_sample=True, top_k=0, top_p=0.5, max_length=50)


# 获取知识我们现在可以开始创造知识了!让我们创建一个问题,然后使用文本生成模型来回答它。为此,我们将调用 happy_gen 的 generate()方法。

question = "What is a dog?" result = happy_gen.generate_text(question, args=beam_settings) print(result.text)


*结果:狗是犬科动物的一员,包括狗、狼、郊狼、狐狸和豺。狗已经被驯化了一万多年。他们首先被驯化*还不错!现在,如果您使用 top-p 设置来代替,那么每次您执行推理时,都会得到一个原始的结果。这里有一个使用 top-p 算法的例子。

result = happy_gen.generate_text(question, args=top_p_settings) print(result.text)


*结果:狗是一种驯养的动物,被饲养来作为伴侣,通常是为了狩猎、守卫或工作。狗通常被称为“宠物”或“伙伴”,因为它们通常是为“T7”而饲养的*# 包括上下文![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/408512617d5474298dc57c4c8dac5820.png)作者图片如果可能的话,我建议你在你的输入中添加额外的上下文。例如,假设您要求模型定义一个恰好是同音异义词的单词。然后,该模型可能会产生一个不适合您的上下文的单词含义的定义。下面是在问题前添加和不添加上下文的情况下生成知识的两个示例。

input_no_context = "What is a library?" result_no_context = happy_gen.generate_text(input_no_context, args=beam_settings) print(result_no_context.text)


结果:图书馆是书籍和其他材料的集合,可用于各种目的,如研究、教学或娱乐。图书馆和书店的区别是什么?书店是一个

input_with_context = "Happy Transformer is an open-souce Python package. What is a library?" result_with_context = happy_gen.generate_text(input_with_context, args=beam_settings) print(result_with_context.text)


*结果:库是可重用代码片段的集合,它们可以一起用来解决一个共同的问题。例如,一个库可以是一组在程序中一起使用来执行特定任务的函数。*如您所见,通过提供上下文,我们帮助模型缩小了“库”的含义所以,我建议你在进行自我对话时,在你的输入中加入上下文。现在,我们来讨论一下如何自动生成问题。# 自动生成问题假设给你一段文字,你希望通过问语言模型问题来自动生成额外的背景知识。你必须理解潜在的文本,才能提出语法正确且相关的质量问题。可以应用文本生成转换器模型来解决这个问题。我的顶点项目团队使用命名实体识别来识别上下文中的名词,然后为每个名词创建一个问题。例如,如果单词“香蕉”在文本中,那么问题“什么是香蕉?”会产生。我们发现这种方法是有效的,但 AI2 的团队提出了一种更复杂的方法,使用文本生成模型来生成问题。我们将讨论如何使用 AI2 提出的方法来生成问题。他们精心制作了提示,然后使用文本生成模型来继续提示以生成问题。例如,他们将自言自语应用于一项名为 Winograd Schema Challenge 的挑战,简单来说,就是预测一个模糊代词所指的名词。因此,例如,给定句子“锤子不适合工具箱,因为**它**太大了”,模型必须确定**它**是指“锤子”还是“工具箱”以下是 AI2 在挑战中使用的部分提示。[1]然后,他们将提示附加到上下文中,并让模型产生文本。通过产生文本,模型将潜在地产生与上下文相关的可行问题。下面是这个过程的代码示例。我们将使用 p 值为 0.2 的 top-p 采样,最多生成 6 个令牌——正如 AI2 在论文中所建议的那样。

question_generation_settings = GENSettings(do_sample=True, top_k=0, top_p=0.2, max_length=6)context = "The hammer did not fit into the toolbox because it is too big"prompt = "What is the definition of"input = context + promptq_g_result = happy_gen.generate_text(input, args=question_generation_settings)print(q_g_result.text)


*结果:一个工具箱?答:*现在,我们可以用下面一行代码隔离生成的问题。

Get the location of the question mark if it exists.

Results in -1 if not present

q_m_location = q_g_result.text.find("?")full_question= ""question_ending = ""if q_m_location != -1:
question_ending = q_g_result.text[:q_m_location+1]
full_question = question_prompt + question_ending
print(full_question)

else:
print("question not generated. Try a different prompt.")


*结果:奖杯的定义是什么?*# 应答前缀AI2 为每个问题前缀手动创建了一个答案前缀,以帮助模型回答问题。下面的图表显示了问题和答案前缀的各种组合。[1]请注意,每个回答提示都有一个下划线来表示其主题。我们可以用下面的代码很容易地提取出主语,因为主语只是问题的结尾而不是问号。

subject = question_ending[:-1] print(subject)


*结果:一个工具箱*提示可以分为三个部分:主题前的文本、主题和主题后的文本。因此,这些组件可以按如下所示进行组合。

answer_prefix = " The definition of" + subject + " is" print(answer_prefix)


*结果:工具箱的定义是*# 把所有东西放在一起我们现在已经了解了为任意文本生成背景信息所需的一切。下面是将我们创建的各种组件组合起来形成文本生成模型的最终输入的最后一个例子。

final_input = context + full_question + answer_prefix print(final_input)


*结果:*锤子太大,放不进工具箱。工具箱的定义是什么?工具箱的定义是AI2 建议使用 p 值为 0.5 的 top-p 采样来生成 10 个令牌。让我们来定义这些设置。

answer_generation_settings = GENSettings(do_sample=True, top_k=0, top_p=0.5, max_length=10)


我们现在拥有了生成最终结果所需的一切。

answer_suffix = happy_gen.generate_text(final_input, args=answer_generation_settings).textprint(answer_suffix)


*结果:一个存放和运输工具的盒子。是*最后一步是结合我们的前缀和后缀的答案。

final_result = answer_prefix + answer_suffix print(final_result)


*结果:工具箱的定义是存放和运输工具的盒子。是*那挺好的!*注:您可能希望应用基础数据后处理,以便仅提取第一句话。*# 差异本文概述的方法和 AI2 概述的方法之间的主要区别是每个上下文生成的背景信息量。对于每个语境,他们至少使用了 5 个问题前缀。然后,对于每个问题前缀,他们生成 5 个问题。最后,对于每个问题,他们都有 10 个答案。这意味着,对于每一个语境,他们至少产生了 250 个答案——这是大量的背景信息!# 结论就是这样!您刚刚学习了如何使用 Transformer 模型生成背景信息。我相信这项技术可以进一步改进并应用于其他任务。例如,在生成问题和答案时,也许可以应用微调来帮助提高模型的性能。此外,我最近发表了一篇文章,概述了一个你可以自由追求的与自我对话相关的研究想法。我期待着阅读 NLP 研究人员如何改进和应用自言自语!# 资源[订阅](https://www.youtube.com/channel/UC7-EWrr8YdcQgPPk76OiUVw?sub_confirmation=1)我的 YouTube 频道或注册我的[时事通讯](https://www.vennify.ai/)以获取更多关于自言自语的内容!本教程中使用的[代码](https://colab.research.google.com/drive/1_JfIY1xkks3v0-9mx0CbB27oeaFDk7ye?usp=sharing)# 参考[1] V. Shwartz,P. West,R. Bras,C. Bhagavatula1 和 Y. Choi,[用自言自语进行无监督常识性问题回答](https://arxiv.org/abs/2004.05483) (2020),EMNLP 2020# 归因[Ted Brownlow](https://www.linkedin.com/in/ted-brownlow-7043ab168/) 、 [Will Macdonald](https://www.linkedin.com/in/will-macdonald-211923175/) 和 [Ryley Wells](https://www.linkedin.com/in/ryleywells78rs88/) 都是我的顶点项目团队的成员。*原载于 2021 年 8 月 7 日*[*https://www . vennify . ai*](https://www.vennify.ai/self-talk-transformer-model/)*。*# 自训练分类器:如何使任何算法的行为像一个半监督的> 原文:<https://towardsdatascience.com/self-training-classifier-how-to-make-any-algorithm-behave-like-a-semi-supervised-one-2958e7b54ab7?source=collection_archive---------0----------------------->## 机器学习## 使用 Sklearn 库中的标准分类算法进行自我训练的简单 Python 实现![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/9ea417ff41f18891bb04dcf97751b0ad.png)自训练分类器:在每次迭代中添加伪标签。图片由[作者](https://solclover.com/)提供。# 介绍半监督学习结合了已标记和未标记的示例,以扩展用于模型训练的可用数据池。因此,我们可以提高模型性能,并节省大量的时间和金钱,而不必手动标记成千上万的例子。如果你有你最喜欢的**监督的**机器学习算法,你会很高兴地听到你可以通过一种叫做**自我训练**的技术,快速适应它使用**半监督的**方法。# 内容*   自我训练在机器学习算法的宇宙中处于什么位置?
*   自我训练是如何进行的?
*   Python 中如何利用自我训练建立模型?# 机器学习算法领域内的自我训练在我们的数据科学生涯中,可用的机器学习算法比我们任何人都要多。然而,理解最常用的方法是有益的,我在下面对它们进行了分类。旭日图是**交互式**,所以一定要点击👇在不同类别的**上放大并展示更多的**。如简介部分所述,自训练属于机器学习算法的半监督分支,因为它使用标记和未标记数据的组合来训练模型。机器学习算法分类。由[作者](https://solclover.com/)创建的互动图表。***如果你喜欢数据科学和机器学习*** *,请* [*订阅*](https://solclover.com/subscribe) *每当我发布一个新故事时,你都会收到一封电子邮件。*# 自我训练是如何进行的?你可能认为自我训练包含一些魔法或者使用一种非常复杂的方法。然而实际上,自我训练背后的想法非常简单,可以用以下步骤来解释:1.  首先,我们收集所有标记和未标记的数据,但是我们只使用标记的观察来训练我们的第一个监督模型。
2.  然后我们用这个模型来预测未标记数据的类别。
3.  在第三步中,我们选择满足我们的预定义标准的观察值(例如,预测概率> 90%或属于具有最高预测概率的前 10 个观察值),并且**将这些伪标签**与带标签的数据相结合。
4.  我们通过使用带有标签和伪标签的观察值**训练**一个新的监督模型**来重复这个过程。然后,我们再次进行预测,并将新选择的观测值添加到伪标记池中。**
5.  我们**迭代这些步骤,直到**我们完成所有数据的标记,没有额外的未标记的观察值满足我们的伪标记标准,或者我们达到指定的最大迭代次数。下面的插图总结了我刚才描述的所有步骤:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/374b49307b37682db4148db169a098e2.png)自我训练的迭代过程。图片由[作者](https://solclover.com/)提供。[![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/1b3d3abe50e10bf8f8217750c717e6f9.png)](https://solclover.com/membership)[![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/60fb21d1cb2701bfb6b71f61c99403e2.png)](https://www.linkedin.com/in/saulius-dobilas/)# **如何在 Python 中使用自我训练?**现在,让我们通过一个 Python 示例,在真实数据上使用自我训练分类器。## 设置我们将使用以下数据和库:*   [来自 Kaggle 的营销活动数据](https://www.kaggle.com/rodsaldanha/arketing-campaign)
*   [Scikit-learn 库](https://scikit-learn.org/stable/index.html)用于1)将数据拆分成训练和测试样本([train _ test _ split](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html))2)执行半监督学习([self training classifier](https://scikit-learn.org/stable/modules/generated/sklearn.semi_supervised.SelfTrainingClassifier.html));3)模型评估([分类 _ 报告](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.classification_report.html))
*   [Plotly](https://plotly.com/python/) 用于数据可视化
*   [熊猫](https://pandas.pydata.org/docs/)进行数据操作首先,让我们导入上面列出的库。接下来,我们下载并摄取营销活动数据(来源: [Kaggle](https://www.kaggle.com/rodsaldanha/arketing-campaign) )。我们将文件接收限制在几个关键列,因为我们将只使用两个特性来训练我们的示例模型。如您所见,我们还派生了一个“Dependents _ Flag”,我们将使用它作为预测目标。换句话说,我们的目标是预测我们的超市购物者在家里是否有任何家属(孩子/青少年)。这是数据的样子:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/8b40be80d14d72c8d8f8d967532f98cd.png)来自 [Kaggle](https://www.kaggle.com/rodsaldanha/arketing-campaign) 的营销活动数据片段。图片由[作者](https://solclover.com/)提供。在开始训练模型之前,我们还需要做一些事情。由于我们的目标是评估自训练分类器的性能,这是一种半监督技术,我们将按照下面的设置分割数据。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/6a13827634932b3fac91a3aa197c86ff.png)为半监督学习准备数据。图片由[作者](https://solclover.com/)提供。测试数据将用于评估模型性能,而标记和未标记的数据将用于训练我们的模型。因此,让我们将数据分成训练和测试样本,并打印形状以检查大小是否正确:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/9b6feadee6b3df976a92f8ae01d5fab0.png)训练测试数据大小。图片由[作者](https://solclover.com/)提供。现在,让我们屏蔽训练数据中 95%的标签,并创建一个使用'-1 '来表示未标记(屏蔽)数据的目标变量:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/0e71c0c3989311fa9fb7fdfa506dca23.png)目标值分布。图片作者[作者](https://solclover.com/)。最后,让我们在 2D 散点图上绘制训练数据,看看观察值是如何分布的。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/27896ce7ff87f722b109e5b4d8b4bfaa.png)半监督学习中标记和未标记数据的组合。图片由[作者](https://solclover.com/)提供。如您所见,我们将使用“MntMeatProducts”(购物者每年在肉制品上的花费)和“MntWines”(购物者每年在葡萄酒上的花费)作为两个特征来预测购物者在家中是否有任何受抚养人。## 模特培训现在,数据已经准备好了,我们将在标记的数据上训练有监督的支持向量机分类模型(SVC ),以建立模型性能基准。它将使我们能够判断来自后面步骤的半监督方法比标准监督模型更好还是更差。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/9ef5c3256162b9322173865f5eceb3c6.png)支持向量机分类模型性能。图片由[作者](https://solclover.com/)提供。来自监督 SVC 模型的结果已经很好了,准确率为 82.85%。请注意,由于类别不平衡,label=1(有受抚养人的购物者)的 f1 得分较高。现在,让我们使用 Sklearn 的自训练分类器遵循半监督方法,同时使用相同的 SVC 模型作为基本估计器。**注意,你可以选择几乎任何监督分类算法在自训练分类器中使用。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/c8fbad5b9bee568e25fba43a60869641.png)自训练分类模型结果。图片由[作者](https://solclover.com/)提供。结果出来了!我们已经提高了模型性能,虽然只是略微提高了 83.57%的准确率。在精度提高的推动下,label=0 的 F1 得分也略有提高。正如本文前面提到的,我们可以选择如何选择伪标签进行训练。我们可以基于前 k_best 预测或指定特定的概率阈值。这一次,我们使用了 0.7 的概率阈值。这意味着任何类别概率为 0.7 或更高的观察值都将被添加到伪标记数据池中,并用于在下一次迭代中训练模型。请记住,使用不同的超参数探索这两种方法(threshold 和 k_best)总是值得的,以查看哪种方法产生的结果最好(在本例中我没有这样做)。# 结论现在你知道如何以半监督的方式使用任何监督分类算法。如果您有大量未标记的数据,我建议在进行昂贵的数据标记练习之前,探索半监督学习的好处。我真诚地希望你喜欢阅读这篇文章。然而,当我试图让我的文章对我的读者更有用时,如果你能让我知道是什么驱使你阅读这篇文章,以及它是否给了你想要的答案,我将不胜感激。如果不是,缺少什么?干杯!👏
**索尔·多比拉斯*****如果你已经花光了这个月的学习预算,下次请记得我。*** *我的个性化链接加入媒介是:*<https://solclover.com/membership>  您可能感兴趣的其他文章:</semi-supervised-learning-how-to-assign-labels-with-label-propagation-algorithm-9f1683f4d0eb>  </how-to-benefit-from-the-semi-supervised-learning-with-label-spreading-algorithm-2f373ae5de96> # 卖出预测中的销售> 原文:<https://towardsdatascience.com/sell-out-sell-in-forecasting-45637005d6ee?source=collection_archive---------4----------------------->## 雀巢公司销售预测的机器学习![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/f10b63c048907795bd502114eb0698b7.png)售罄预测中的销售,作者提供的图像我想从雀巢公司的角度告诉大家销售预测。我将向您展示我们方法的理念/方法。然而,如果你想使用其中的一些东西,我邀请你去 [GitHub](https://github.com/BartoszSzablowski/Sell_Out_Sell_In_Forecasting) ,在那里我们展示了这个项目的 Python 实现。我们的方法在[数据科学峰会](https://dssconf.pl/)上提出。这些例子是基于真实数据的,当然,它们是匿名的。我在**数据科学中心**的**雀巢**工作。使用 Python 或 R,我实现机器学习模型来支持业务决策过程。**数据科学中心**是雀巢公司支持欧洲、北非和中东的分析团队。# 议程![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/2490bc0dff8a6831cde9704f6c07d836.png)议程,作者图片本文的议程是从**业务问题**开始,然后**理解数据**,转换它(**特征工程**,我将从**模型培训**方面结束。如果您想查看评估,即在测试集上评估模型,将预测值与真实值进行比较,那么这部分在 [GitHub](https://github.com/BartoszSzablowski/Sell_Out_Sell_In_Forecasting) 的解决方案中。# 商业理解## ➔在预测中出售![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/7558ed277b53ccd366ab01d6912acd13.png)商业理解,作者图片从商业角度来看,正确的预测很重要。如果我们的预测假设销售的产品少于实际需求,那么我们就会损失利润。另一方面,如果我们的预测被高估,那么我们实际销售的产品会减少,我们也会亏损,我们会产生产品库存成本,或者更糟,我们的产品会过期。一个糟糕的预测会给商业、生产和物流带来后果。销售可分为**卖进**和**卖出**。**销售入**是制造商(在我们的例子中是雀巢)销售给零售商的产品数量,而**销售出**是零售商销售给最终客户的产品数量。作为制造商,我们对在销售**感兴趣。这里你已经可以看到**卖入**和**卖出**的关系。如果我们不供货,零售商就不会出售产品。此外,如果他们因为计划促销我们的产品而想卖出更多,他们也必须从我们这里订购更多的产品。**业务和生产条件要求**每周预测最长 3 个月**或平均未来 12 周的销售情况。预测应该是每周的,因为生产或物流是每周计划的。# 售罄预测## ➔为什么卖出去需要一个未来的卖出去![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/9948b03824d49f2689c8c8ba7fd02ccd.png)使用的数据集,按作者分类的图像我们已经知道为什么一个企业需要一个好的预测,让我们看看数据,做一个 **E** 探索 **D** ata **A** 分析来更好地理解它。销售额按周进行汇总,即 **PERIOD_TAG** 列,它告诉我们一年中某个特定的周。接下来的两列告诉我们该观察是关于哪个**产品**和哪个**客户**。从我们的角度来看,**客户**是**零售商**。您可以在不同的商店看到我们的许多产品,因此我们需要对每个产品/零售商组合进行预测。**销售可分为:
-**-**-实际销售,即**交付**给零售商的产品数量。
- **订单**-实际需求,客户向订购的**产品数量。有时可能会发生我已经提到的情况,即订购产品的数量高于实际销售产品的数量。除其他因素外,这可能是由于低估了预测。******下一个变量是**销售出去**,这是从零售到最终客户的产品销售数量,我们作为一个社会在商店购买了多少。****接下来的变量与我们在商店看到的**促销**有关。其中的前 2 个与促销的**分布**有关,这是该商店中所有商店和大卖场进行促销的百分比。在促销周,不一定所有的商店都有促销活动。
最后 2 个变量是**类型的促销**,这是一个二元变量,表示一周内是否有给定类型的促销。促销的类型肯定不止两种,然而,为了本文的目的,我只分离了两种。实际上,它可能是,例如,降价,报纸上的产品信息,商店里我们产品的单独位置,等等。**> **随着时间推移销售产品不过是一个时间序列。为不同的零售商销售多种产品是多个时间序列。****让我们看一个为零售商之一销售一种产品的例子。****![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/36ab57db7db585a79774e2be57ccb59f.png)****一段时间内销售一种产品,图片由作者提供******在第一张图表**上,您可以看到**订购产品的数量**。正如你所看到的,时间上的移动平均线是恒定的,但在某些周它是相关的——因此显著增加。******在下一张图表**上,我们添加了**卖出**,我们可以看到它落后于卖出。如果我们看到客户的产品订单数量显著增加,那么我们可以预计,在接下来的平均 3-4 周内,该客户的商店将比平时销售更多的产品。这种脱销与滞销的时间长短因零售商而异。******在第三张图表**上,我们添加了**交付产品的数量**,正如我之前所说,它并不总是等于订购产品的数量。但是在这里,我们可以看到其他依赖因素,例如,如果我们交付的数量没有订购的那么多,那么在接下来的几周内,我们的客户可能会重复订购。******在最后一幅图**中,只有**售罄**和**促销数据**,该售罄明显依赖于这些数据。**> **因此我们的**结论**在**雀巢**。首先,我们应该预测**卖出**,在此基础上,我们将预测**卖出**。**# **特征工程**## **监督学习问题的➔时间序列变换****我们的方法基于**机器学习**,因此我们使用**一个模型来预测多个产品**。****[**吴恩达**](https://en.wikipedia.org/wiki/Andrew_Ng) 曾经说过:**> **应用机器学习基本上是特征工程。****人们常说,数据是机器学习的燃料。然而在现实中,这并不完全正确。如果我们简单地把现有的数据作为输入,在大多数情况下我们不会准备好模型。不如说数据如油。所以必须首先将原油转化为燃料。在我们的例子中,这个转换是**特征工程**。****我们在现有功能的基础上创建新功能,使机器学习算法能够工作。获得正确的数据会大大提高数据的质量,因为我们将原始数据转化为信息,模型会将这些信息转化为知识。那么模型可以更好地理解世界,并且将给出更高质量的预测。****如今,我们可以看到使用 XGBoost 或神经网络等模型的趋势。当然,有时我们别无选择,一个例子就是使用卷积神经网络对照片中的物体进行分类。然而,在我看来,我们应该始终做好数据分析工作,并将结论转化为数据转换。当我教分类的时候,我喜欢展示一个例子,逻辑回归对转换好的数据比 XGBoost 对原始数据更准确。此外,如果模型不太复杂,它也更容易理解,这对商业来说也很重要。这样我们就知道并能解释变量如何影响我们模型的预测。****然而,让我们回到我们的项目。在我们可以使用机器学习算法之前,**我们的时间序列必须转化为一个监督学习问题**。在时间序列中,没有 ***X*** 和 ***y*** 变量的概念。所以我们需要选择我们将要预测的( ***y*** )并使用**特征工程**来创建所有将用于进行预测的*变量。******![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/61fc027684f4da47fb35fda66f9fd7a4.png)******创建 y 变量,图片作者******第一步,我们创建我们的**目标**,模型将预测的**值。这些值是客户将在一周、两周内销售的数量,等等,以及客户将在一周、两周内从我们这里订购的数量,等等。*****> ***➞这将是我们的 ***y*** 。******对于我们分析的产品,我们看不到趋势,但情况并非总是如此。所以,**我们不需要只预测原始值**,而是比如说**微分值**,这是一个流行的、广泛使用的**数据变换**使得**时间序列**数据**平稳**。我们也可以用当前值预测未来值的对数比。通过使用这些转换中的任何一种来创建新值,我们可以预测动态,并且可以预测训练数据范围之外的原始值。******对于最后的日期,我们无法创建未来的值,因为我们不知道它们。然而,我们并不因此而消除所产生的缺失。这些失踪可能会出现在最后的日期。也就是说,对于倒数第二个日期,12 月 22 日,我们只能提前一周进行预测,以便与真实值进行比较。然而,我们不能预测未来 2 周。根据预测范围,我们将测试数据的窗口移动到同一时期的预测值,例如 2019 年。******![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/716ad2516db9a913380807845ae53120.png)******从未来创造 X 个变量,图片作者********接下来,我们创建 X 个变量**,**的特征**,在此基础上模型将**拟合**,然后**做出预测**。这些值可以是在进行预测时已知的未来特征,在我们的例子中,这些只是**促销变量**,下周、两周内的促销活动等等。***> ***➞它将是我们的 ***X*** 。******这些是我们使用的唯一的未来特征,并且在执行预测时是已知的。没有**数据泄露**。这一点很重要,从业者都知道,但经常被研究人员或利益相关者忽略。**数据泄漏**发生在模型可以访问实际上不可用的信息时。******![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/e58d75cdc15dcaf82ae9901ac8422bb2.png)******根据滞后值创建 X 变量,按作者排序********滞后值**,一周前、两周前我们卖了多少,等等。这是一个**简单的办法**把**变**一个**时间序列**变成**监督学习**的问题。另一方面,在这里,我们没有第一行的这些特性,因为没有从中获取它们的数据。***> ***➞这将是我们的 ***X*** 。******这里我们看到了一个类似的情况,缺少值,但是这些缺少的特性将第一次出现在索引中。我们需要所有的 X 变量,我们不打算用另一个值来代替它们。因此,我们**删除那些以这种方式**创建的带有缺失值的观察值。******![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/a3e05d9c98c02880fbe6d784c09cf943.png)******基于滚动统计数据创建 X 变量,按作者分类的图像********添加原始滞后值后的下一步是添加滚动统计**,也就是说,对于滑动窗口,我们计算各种统计。这可以是平均值、中间值、标准偏差、总和,或者至少是不同值之间的差值。最后可以是订购和交付产品之和的**差额**。这样一个变量告诉模型,零售商可能会再次订购,因为它没有收到过去订购的那么多。我们计算不同宽度窗口的统计数据,例如,前 2 个值、前 3 个值的移动平均值,等等。***> ***➞这将是我们的 ***X*** 。******![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/58b1200175a99e1e1c59c958e8c26e73.png)******基于循环变量创建 X 变量,图片由作者提供******在时间序列中,除了趋势之外,还可能有**季节性**,这意味着一年中某个特定时期的销售额比另一个时期多。为了使用星期数作为信息,我**将星期转换成一个循环变量**,它是正弦和余弦。因此,我把一个变量转换成两个变量。这里我转换的是周数,但是我们可以对其他循环变量使用相同的方法,比如一个小时或一周中的一天。以星期几为例,我将说明为什么这种转换可能比其他转换更好。******如果我们将星期几转换成一个**数字变量**,星期一的值可能是 1,而星期天的值可能是 7。因此,对于这个模型来说,这两天会相隔很远,但实际上,星期天之后是星期一。******另一个转换可以是 **OneHotEncoder** ,但是我们会创建多个二进制变量。***> ***➞这些变量也将是我们的 ***X*** 。******这些是数据转换的例子,我会说是基本的。我们会根据问题找到其他的。**我们还应该转换分类变量**,它们是**客户**和**产品**。***# ***链式多输出回归***## ***使用 ML 模型进行多输出回归的➔方法******![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/52e44061bb6c9229a440ab7f95459843.png)******用于训练多个卖出和买进模型的方法,按作者分类的图像********我们已经为 ***X*** 和 **y** 创建了一组新的特征**,所以问题变成了——你如何在雀巢公司找到合适的模特?**标准的机器学习算法**(我说的不是神经网络)是为了**预测单个数值**而设计的,所以我们需要对每个视界有一个单独的模型。我们作为模型使用**随机森林**。此外,正如我前面提到的,我们希望**先进行卖出预测,然后再进行卖出预测**。提醒一下,“卖出”是零售商卖给最终客户的产品数量,这里的“卖出”是零售商从雀巢订购的产品数量。我们**首先**预测下一个 **20 周卖出**,**然后下一个 15 周卖出**。**售罄**的时间跨度更长,因为**售罄**需要预测未来售罄值,我们从探索性分析中得知**售罄最多比**售罄晚 3-4 周。出于理性的原因,我们选择了多一周。******作为模型学习方法,我们使用**链式多输出回归**,它是模型的**线性序列。**的第一个模型**是**提前 1 周**售罄的模型,它使用我们创建的变量。下一款**车型**将在**前 2 周**售罄,其功能和预测与上一款车型相同。**一旦我们将所有销售模型拟合好,我们就在**中拟合销售模型。每个模型都可以访问以前模型的相同功能和预测。此外,每个模型都有功能选择,我会写一秒钟。*****# ***训练单个模型***## ***➔特征选择/超参数选择******![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/eb24058aeb440503501ecf0018a9c209.png)******训练单个模型,由作者生成图像********现在,我将展示基于 3 周前模型销售的单个模型学习**。******我从**特征选择**开始。使用**特征工程**我们**已经创建了许多特征**,由此我们面临**维度诅咒**的问题,并且碰巧特征之间**相关**,因此它们中的许多携带相同的信息,由此我们可以发现模型认为一个信息更重要,即使它在现实中并非如此。另一个原因是我们不想教授模型**噪音**。**因此需要使用特征选择**。**我们可以将其分为在特征工程和先前模型预测过程中创建的变量**。******我将**从预测**开始,这是我们在底部看到的。我们的模型使用以前模型的销售预测,但不使用所有销售预测。我们仅选择接下来的 5 个售罄预测,它们是售罄 4W、售罄 5W、售罄 6W、售罄 7W 和售罄 8W。这是由于我们的探索性数据分析和业务关系。零售商现在会从我们这里订购更多的产品,因为他们打算通过促销在 3 周内卖出更多的产品。******在我们创建的**特性中,主要是我们没有选择那些与售完相关的特性,例如售完的滞后、售完的滚动统计、促销数据,因为这些变量已经减少到预测。在某种程度上,在这种情况下,**我们通过基于数据的预测来减少数据的多维性**。********我们只使用与销售相关的特征以及循环和分类变量。然而,我们并不采取所有的,只有相关的。然而,不同窗口宽度的移动平均值可能被认为是相关的。我们不应该把它们都拿走,只拿最相关或最相关的一个。******因为我所说的,实际上**每个型号都使用不同的功能**。******我们已经选择了特性,那么**训练**本身是什么样子的呢?这个过程尽可能标准化。但是,我想简单地谈谈超参数的**选择。我们使用的数据是**时间序列**,所以**观察值是相互依赖的**。**分割训练/测试集时不能使用随机化**,也不能使用交叉验证。在这种情况下,会有数据泄漏。因此,在每个分割中,**测试或验证指标必须高于训练指标**。********![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/011066cf67e0e4f4d4bd2f36ebaa3894.png)******由 [Mehrad Vosoughi](https://unsplash.com/@mehrad_vosoughi?utm_source=medium&utm_medium=referral) 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片******谢谢您们。我希望我没有让你感到厌烦,我所展示的一切都符合数据科学的艺术。从外面看起来可能很复杂,但当我们深入细节时,你可以看到它包含了机器学习中使用的许多基本方法。首先,它是有效的,我展示了如何使用机器学习来处理时间序列,我们至少不必使用蒙特卡罗树搜索(开玩笑)。此外,您还看到了我们如何将业务问题转化为机器学习解决方案。******😎***# 语义突出显示> 原文:<https://towardsdatascience.com/semantic-highlighting-d2fba3da2822?source=collection_archive---------19----------------------->## [从零开始逐个任务地进行数据科学](https://towardsdatascience.com/tagged/dofromscratch)## 为 NLP 构建和添加用户界面![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/3cd26a96be3108ec9529c580b1849031.png)由[埃德·皮里彭科](https://unsplash.com/@edpylypenko?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄自从我写了上一篇文章[使用 NLP 改善你的简历](/ai-is-working-against-your-job-application-bec65d496d22),我们的读者反应非常好。阅读一些评论令人鼓舞,并为下一阶段提供了急需的动力。如果我们真的想使用 NLP 来改善我们的简历,我们需要一个用户界面,就像那张由 Ed Pylypenko 在 Unsplash 上拍摄的照片一样,我们的用户会在他们的手机上使用这项服务。***考虑你外出的用例*** 。你最好的朋友在 LinkedIn 上给你发了一个工作链接。把这个链接传给一个应用程序,然后在你的手机上就能得到你的机会分析,这不是很酷吗?我想那应该是 ***零下的凉爽*** 。有些人会说 LinkedIn premium 已经有这个功能了,但这对我们的读者来说没什么意思。现在,不可否认的是, ***使用 JavaScript*** 构建原型可能不会吸引每个读者。尽管如此,我一直对开发解决方案感兴趣,围绕 API 调用包装机器学习,并提供友好的用户界面。让我向您展示如何使用 Vue.js 创建一个 ***原型用户界面*** 并进行一些 NLP 处理。## 从命令行创建新的 UI 应用程序为您的项目创建一个新目录,并确保安装了 vue 命令行工具。

mkdir semantic
cd semantic
npm install -g @vue/cli


现在我们准备搭建一个 Vue 应用程序。

vue create semantic


一般来说,对于我的文章,我只是使用默认的 vue。对于生产工作负载,您可能希望采用不同的方法。图 1 显示了我的终端窗口的最终输出。我们现在可以使用 NPM 命令运行初学者应用程序,图 2 显示了最终的终端消息。

npm run serve.


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/55ab20e76183f415c29a62d97d2e3727.png)图 vue create semantic 完成后终端的一个小截图。图片作者。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/bdcbb11a61329cbec21b9f3ad8a32c39.png)图 2:运行开发服务器,我们看到了 URL 地址。作者图片如果您导航到 localhost:8080/![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/40abad8fa73c42be5c59a609550add30.png)图 3:新的 web 应用程序启动并运行。如果您遵循以上说明,您将最终拥有一个为您的项目开发和添加功能的基本框架。接下来,让我们添加一个文本高亮特性。## 添加文本突出显示功能这些天我主要使用视觉代码。接下来,我们需要用 npm 安装 vue 文本高亮模块

npm install --save vue-text-highlight


我们需要对 main.js 进行一些修改,如图 4 所示。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/6e6055e0e750a0d155baae6511b1975a.png)图 vue 应用程序的 main.js。图片由作者提供。在文本中添加高亮然后添加一个新的 vue 组件。我使用包所有者提供的基本插图来引导我的项目。*从小处着手,从最基本的真理开始构建你的代码。新组件如图 5 所示。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/3b5859f58349e189be791fa938d491af.png)**图 5 —添加新的 vue 组件。图片作者。**现在我们需要将组件集成到主 vue.app 文件中。我的代码如图 6 所示。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/6921197b0cc617dffe3444ec6de1e8f0.png)**图 6:向应用程序添加文本高亮组件。图片作者。**所有的编码完成后,我们现在应该 ***祈祷应用程序将编译并运行*** 。继续发布*

npm run serve


*检查并修复 npm run 命令报告的任何问题。
一切顺利;当你访问你的网站时,你会看到一些高亮的文字。我的如图 7 所示。当德雷克转向吉普车时,我们期望看到文本“*热带鸟类分散”,并且单词“**鸟类**和“**分散**”应该被突出显示。***![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/3a7474c33eab04df5884e471bd367dbe.png)**图 7 —显示了带有文本特性的修改后的应用程序。图片由作者提供。**使用检查器检查页面,如图 8 所示。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/957d83a2c6b05b4644cc4995460f4e5a.png)**图 8:检查应用程序并注意使用的 html 标签。图片由作者提供。**查看图 8,我们可以看到 vue 应用程序由两部分组成,文本使用 span 标记和 mark 标记突出显示。我们可以从组件中删除一些样板 HTML,得到一个友好的原型页面。我的如图 9 所示。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/b374c30e4bfddb76f252e2313a1b3642.png)**图 9:文本突出显示的初始原型页面。图片由作者提供。**我已经向你展示了如何创建一个原型用户界面,并在一些基本的自然语言处理。使用预先选择的关键字,我们演示了如何在屏幕上的文本中突出显示这些关键字。*## *后续步骤**多年前我创建了一个 NLP 类,所以我有这个项目所需的后端代码。我最近用 Vue.js 做了很多实验,也有大量可重用技术的代码库。大致来说,接下来的步骤是**   *为简历改进项目设计一个用户界面*
*   *使用 Python 添加后端服务,并集成 NLP 类*
*   *向前端添加一些 API 调用,以收集简历、职位描述并执行分析。*
*   *更新代码库并测试**回来拿下一期吧,我会把它们都接好的!同时,如果你对开发过程感兴趣,想看我演示如何从零开始制作*产品的视频,你可能会喜欢我的 YouTube 系列。构建 web 服务、API 调用、用户界面都有详细的讨论。****该技术的一个嵌入式链接是人类创新行动系列。**## **链接***   **创建新的 [Vue.app](https://cli.vuejs.org/guide/creating-a-project.html)**
*   **[文本高亮显示](https://www.npmjs.com/package/vue-text-highlight) vue 模块**
*   **链接到一个 [github repo](https://docs.cloudera.com/machine-learning/1.1/projects/topics/ml-linking-an-existing-project-to-a-git-remote.html)**
*   **生成并将 SSH 添加到 github**
*   **我的 [github 回购](https://github.com/CognitiveDave/semantic/tree/master)**# 语义搜索引擎> 原文:<https://towardsdatascience.com/semantic-search-engine-ca03b0a835d5?source=collection_archive---------17----------------------->## 堆栈溢出问答案例研究![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/9e18ad1085dcf87e6c28dc6741cbad18.png)艾莉娜·格鲁布尼亚克在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片1.  介绍
2.  商业问题
3.  映射到 ML/DL 问题
4.  理解数据
5.  数据处理
6.  探索性数据分析
7.  搜索问题的解决方法
8.  先决条件
9.  结构
10.  部署
11.  结论和未来工作
12.  轮廓
13.  参考## 1.介绍*本体论(信息科学)*在[计算机科学](https://en.wikipedia.org/wiki/Computer_science)和[信息科学](https://en.wikipedia.org/wiki/Information_science)中,**本体**包含概念、数据和实体之间的类别、属性和关系的表示、正式命名和定义,这些概念、数据和实体实现了一个、多个或所有[话语领域](https://en.wikipedia.org/wiki/Domain_of_discourse)。更简单地说,本体是通过定义一组代表主题的概念和类别来显示主题领域的属性以及它们之间的关系的一种方式[维基百科]*语义搜索*属于本体论的这个分支。它基于蜂鸟算法。该算法更加强调自然语言查询,考虑更多的上下文和含义,而不是单个关键字。语义技术是以意义为中心的。它们包括以下内容[维基百科]语义表示的编码/解码知识图嵌入关系主题和概念的自动识别信息和意义提取语义数据集成,以及分类法/分类因此,给定任何问题,他们直接搜索跨越各种资源的主题、概念和关联。## **2。**商业问题![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/097befd98323561d6b8b14bb79d8d149.png)栈溢出搜索网页截图栈溢出是一个问答论坛。这是一个面向专业和热情的程序员的软件编程网站。对于任何像 stack overflow 这样拥有海量数据的公司来说,关键的挑战是在有限的周转时间内,以最佳的资源利用率在整个数据档案中搜索类似的问题。> 业务问题是,给定一个来自 stack overflow 的问题和答案的存储库,在 7 个工作日内快速构建和部署一个数据驱动的应用程序,该应用程序能够在不到 500 毫秒的时间内搜索和检索(top k)与搜索字符串相似的问题,并且计算/服务器成本较低## 3.映射到机器学习问题上述业务问题可以在机器学习中映射为 k 近邻搜索问题。给定一个查询向量(Qk),我们需要在给定的问题集{Q1,Q2,Q3,Q4,…Qn-1,Qn}中找到 k 个最近邻。我们可以使用距离度量来计算最近的邻居。要计算的最简单的距离度量是查询向量和给定问题集中的每个数据点之间的欧几里德距离。这也被称为矢量的 L2 范数![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/e92164a8c50216b5116ef1e5cd1e29eb.png)欧几里得距离还有另一种用于查找相似性的度量,称为余弦距离和余弦相似性。两个向量之间的余弦相似性。值的范围在[-1,1]之间。随着距离的增加,余弦相似性降低,反之亦然。## 4.理解数据![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/7f4eebd1ac977dbba75f52169a1a6d2e.png)突出显示 posts_questions 表中字段的屏幕截图![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/2b0c11d43d87833476ddcd057c7615e1.png)截图突出帖子 _ 答案让我们分析一下堆栈溢出网页,它通常是后端数据对最终用户的表示。仔细探索堆栈溢出档案中的可用数据。为了试验最简单的方法,我们首先需要获取数据。堆栈溢出数据有多种来源。在这个案例研究中,我们从谷歌云平台公开可用的数据集中提取了数据。为了便于理解,让我们以这种方式将感兴趣的关键部分联系起来。Stackoverflow:模式帖子 _ 问题:表格帖子 _ 答案:表格在该表中,有称为字段的单个列因此,问题表中的关键字段是:id、标题、标签、正文类似地,答案表包含:id、body 和 parentid首先在 GCP 上创建了我们的凭证,然后我们可以在谷歌云平台上访问 BigQuery 平台。我们可以使用在编辑器中运行 SQL 命令的大型查询编辑器来检索数据。这是来自 google 的[博客【1】](http://Authenticating with a service account key file  |  BigQuery (google.com))关于使用存储为 JSON 的凭证连接到 big query 并以 JSON 或 CSV 文件下载数据。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/e40a83b9a0fdc4fd122599d97a424de7.png)BigQuery 编辑器使用 BigQuery SQL,我们将从 posts_questions 和 posts_answers 表中检索 100k 条记录,结果由 posts_questions 表中的 accepted_answer_id 和 posts_answers 表中的 answer_id 连接。因此,这个设计很简单,我们获取的行中一个问题有一个对应的答案(一对一映射)。配置大查询客户机后提取堆栈溢出数据的示例 SQL。这个结果首先存储在一个 JSON 文件中,然后我们使用 python 库将 JSON 转换为 CSV,以便于准确地迁移数据。**5。数据预处理**在数据预处理中,我们需要清理和格式化文本,以便在机器学习模型中使用它们。这些是我们使用 BigQuery 从 StackOverflow 数据集中提取的前 5 行数据![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/cc9e673d3d75dce9c816d8d7f3abc676.png)questionbody、answerbody 数据需要清理和格式化。在这种情况下,我们需要删除 HTML 标签,特殊字符,合同文本。类似地,使用 pandas `pd.datetime '将 answercreateddt 转换为 datetime,这样我们就可以只提取答案的年份来进行数据分析。之前,当我们讨论 posts_questions 表中的不同列时,我们遇到了 tags 字段。该数据以“|”分隔值的形式存储在数据库中。因此,在预处理步骤中,我们删除“|”并将其连接成一个字符串。将问题的标题标签与问题 id 连接起来后,最终的预处理数据存储在 CSV 文件中。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/09be3bf7dfee4799c11221c1b5bb32cf.png)最终预处理 CSV 文件**6。探索性数据分析**预处理后,我们对结果数据集进行探索性数据分析。从 posts_answers 表中,我们提取了 answer_created_dt 和 answer_score。我们提取了 2008 年到 2010 年的数据。条形图显示,2009 年回答的答案数量最多,总计为 answer_score,而 2010 年创建的答案数量最少。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/e376a813a00233ca4b09c4220d79e6d0.png)绘制了 answer_id 对 answer_score 的散点图。从图中可以清楚地看出,在 1000k 到 2000k 范围内的 id 具有高于 80 的最高答案分数分布,而超过 3000k 的 id 具有低于 20 的最低分数分布。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/4aa0dd44cd33f5e84bb2e14f37b24734.png)**7。搜索问题的方法**问题集中的问题气是由一系列的词组成的。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/bd61a3be42b8a70388ce50e124fff500.png)这是一个搜索问题,我们可以使用哈希表在数据语料库中搜索查询 Q*,因为它具有 O(1)搜索。我们需要设计一个包含键和值的字典。基于标准散列的技术在这种搜索中可能不太适用。我们必须使用倒排索引的方法来进行搜索[【3】](https://www.youtube.com/watch?v=FpMmpKIvsnQ&t=1144s)。如果查询 Q*中有 k 个单词,我们可以在 O(k)中搜索结果。这被称为倒排索引,因为我们正在构建一个以单词为关键字、以文档编号和单词在这些文档中出现的频率为值的索引。我们只是在这里做一个基于关键字的搜索。Elastic search 是一个开源软件,它实现了倒排索引、评分(TF-IDF)、分布式和实时性。在这种情况下,这是一个简单的使用倒排索引的关键字搜索。在语义搜索中,我们构建句子的向量表示,并在给定的语料库中找到查询向量和向量之间的相似性。在早期的方法中已经观察到,语义搜索在弹性搜索的脚本分数中使用余弦相似性,并且对于非常大的数据集是不可扩展的。## 8.先决条件本文的读者应该熟悉 Python 编程、机器学习和构建数据驱动应用程序的基础知识。## 9.结构以下架构在配备 i5 处理器和 8 Gb RAM 的低端笔记本电脑上对 100k 数据集进行了实验*   使用带有手套向量和快速文本嵌入的 K-means 聚类数据点
*   费斯
*   磁盘近似最近邻a.使用带有手套向量嵌入的 K-均值聚类数据点为了使用 K-Means 聚类算法,我们使用肘图来确定适当的聚类数目值。在 30k 的简化数据集上,由肘图确定的聚类数目的值是 200。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/40ed47828663373e3e77441c59ab986e.png)在这种情况下,简单的关键字搜索在 0.07 秒内返回搜索结果。但是当使用语义搜索不在 glove_vectors 中的单词时,返回 0 和余弦相似性分数失败。使用快速文本来获得在手套向量中找不到的单词的嵌入。使用这种方法,测量 30k 数据集的轮廓得分为 0.05。由于剪影分数接近于零,样本与相邻聚类的决策边界非常接近。因此,在集群内部进行搜索可能无法很好地处理这些数据点,从而转移到下一个架构失败。b.费斯这是脸书研究所在十亿规模数据集上构建的最近邻搜索,据报道比之前的 SOTA 最近邻搜索方法快 8.5 倍。对于相似性搜索,我们需要对数十亿个向量进行以下操作1)给定一个查询向量,我们需要使用欧几里德距离找到与该向量最近的邻居的向量列表2)给定一个查询向量,找出返回最高点积的向量列表。语义相似性搜索基本上包括两个操作。a)索引b)搜索a)索引:FAISS 库是围绕索引对象构建的。首先对向量列表进行预处理,以加快搜索速度。向量的维数由我们为编码向量而选择的句子向量转换器模型来选择。在我们的例子中,我们选择了句子转换器模型,这是对预训练的 BERT 网络的修改,它使用连体和三元网络结构来导出语义上有意义的句子嵌入,可以使用余弦相似度来进行比较。GitHub 上提供了创建索引的详细代码。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/f3ed17ee26b723731adcfc22825f119a.png)我们可以对索引进行序列化和反序列化,以便通过网络传输。在从 pickle 文件中读取序列化索引之前,先对其进行反序列化,然后从文件中读取字节。有两种生成索引的方法。CPU 索引和 GPU 索引。区别在于执行索引生成代码时使用的资源。前者使用 CPU 资源,后者使用 GPU 资源。b)搜索:可以对构造的索引执行的基本操作是 k-最近邻搜索。对于查询向量,在语料库中找到 k 个最近邻。*1。GPU 上的普通暴力搜索*在 GPU 上调用 query_vector 和 encoded_document 向量上的 knn 函数在 0.1 秒内返回结果。在这种情况下,索引是使用 IndexFlatL2 构造的,这是一种简单的 L2 距离计算,在这种情况下它不能很好地工作。*2。使用纯内积构建指数*该索引是使用 IndexFlatIP 构建的,IndexFlatIP 是余弦相似性计算,并被发现在该数据集上给出了良好的结果。在 CPU 资源和 GPU 资源上进行了实验(使用 collaboratory)。代码的搜索部分在所有资源中保持不变。*3。位置敏感哈希*通过传递维度和位数作为参数来构建索引,在这种情况下,研究使用 d =768 和 nbits =1024。选择 nbits < 1024, affected the returned results. Therefore a minimum of 128 bytes was required to represent the document vectors. Instead of projections being calculated here, the calculations are optimized using random rotations of the vectors. This is an exhaustive search too and returns the knn in 0.01s.If we want to reduce the memory footprint of the index and store, then we would need to perform a lossy compression called Product Quantization.The trade-off for memory would be accuracy.After experimenting with ProductQuantizer using inverted list(IVF) on the vectors, the index vector constructed for the big dataset 10 Million was close to 3GB and unable to load the dataset in RAM and the colaboratory session crashed. In addition to that from the IVF form of indexing, we could retrieve only approximate distances.The index on which Product Quantizer is applied is of very low accuracy, therefore a re-ranking needs to be performed with accurate distance computations which is a costly operation on limited RAM.Comparison of latency across different architectures*比较各种搜索结果*对于查询字符串:“事件的 jQuery 插件”,在使用 k-means++初始化使用聚类构建的模型 1 的情况下,query_vector 被预测为属于聚类[52]。在该集群中搜索 5 个最近的邻居得到以下结果。显然,我们的聚类模型在这种情况下失败了,这反映在先前计算的轮廓得分中,其中包含点的重叠决策边界。为了验证聚类模型是否失败,我们绘制了两个聚类(比如 52 和 54)的散点图。这些点本质上是高度重叠的。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/f382c0d86b4a5bd56550974aa54291d8.png)*观察结果*模型 3:在代码的单元测试过程中,磁盘近似最近邻,给定样本节点输入,用有限的节点列表得到修剪后的节点输出。请参考下一节末尾的示例输出。从上面的查询结果中,我们发现与暴力破解或 LSH 相比,IndexFlatIP 在我们的案例研究中表现良好。尽管 LSH 在 0.01 秒内返回结果,但 IndexFlatIP 的结果更类似于查询。因此,我们在 Streamlit 上部署了带有 IndexFlatIP 的 FAISS 的 CPU 版本,应用程序演示可在部署部分获得。c.DISKANN:在单个节点上进行快速、精确的十亿点最近邻搜索这是一个近似的最近邻搜索。这种方法的主要优点是,该方法可以在 64 Gb RAM 和 SSD 上索引、存储和搜索十亿点数据集。我们修改了用于早期方法的数据集。我们没有使用一个问题对一个被接受的答案的映射,而是使用一个问题对多个答案来构建有向无环图,如研究论文中所详述的。这是微软研究团队在 NeurIPS 会议[【8】](http://harsha-simhadri.org/pubs/DiskANN19.pdf)上发表的论文。该算法具有更快的内存搜索速度、更快的索引速度和更好的可扩展性。Vamana 算法首先结合了贪婪搜索,然后是节点的健壮修剪。这个算法的代码是用 Python 实现的,完整的代码可以在 GitHub 链接中找到。它已经在样本节点上进行了测试。对 greedy_search 和 robust prune 函数进行了简单的单元测试。在 Python 中,并行作业库被用来利用多重处理。## 10.部署Streamlit 是一个轻量级框架,用于创建数据应用程序。streamlit 库完全是用 Python 编写的。它建立在三个简单的原则之上。1.Python 脚本 2.Widget/Component 互动 3。立即部署。FAISS CPU 语义搜索模型用于构建 streamlit 中的数据驱动应用程序。下面是用来构建 app 的数据集的 EDA 和 Word Cloud。## **数据集上的探索性数据分析**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/e519392bbcdf7b12c7d6aa6aa0b70c1b.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/7b6a37a9e1125b120144060d3aca12d5.png)词云请在 streamlit 上找到简单应用的网络广播链接<https://drive.google.com/file/d/1i5VnSBbBmjMMSHPPItkULKIJVBgkYMs2/view?usp=sharing>  ## 11.结论和未来工作我们可以得出结论,对于≤ 100k 的大型数据集,FAISS 工作良好,但 RAM 再次成为限制。毫无疑问,对于非常大的数据集,在索引构建、SSD 存储和搜索方面,Disk ANN 优于这些架构。我正在研究以下想法*   DISKANN 算法代码的超参数调整。
*   使用数据集中的其他字段进行搜索并计算指标进行比较## 12.轮廓完整的源代码可以在 [GitHub 链接](https://github.com/jayaBalaR/casestudy1.git)上找到如果您对以上任何一项有任何问题或建议,请通过 [LinkedIn](http://www.linkedin.com/in/mypage1) 与我联系## 13.参考*   【appliedaicourse.com 应用课程
*   [使用服务帐户密钥文件进行身份验证| big query(google.com)](https://cloud.google.com/bigquery/docs/authentication/service-account-file#python)
*   [LIVE:语义搜索 Q & A【设计+代码】— —第 1/4 部分——YouTube](https://www.youtube.com/watch?v=FpMmpKIvsnQ&t=1144s)
*   [FAISS:高效相似性搜索库——fb.com 脸书工程](https://engineering.fb.com/2017/03/29/data-infrastructure/faiss-a-library-for-efficient-similarity-search/)
*   [兰德-NSG:在单个节点上快速精确的十亿点最近邻搜索(harsha-simhadri.org)](http://harsha-simhadri.org/pubs/DiskANN19.pdf)# 语义搜索:从雅克卡到伯特的意义测量> 原文:<https://towardsdatascience.com/semantic-search-measuring-meaning-from-jaccard-to-bert-a5aca61fc325?source=collection_archive---------11----------------------->## 使用这些一流技术增强搜索能力![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/d8b84083a7f00c61e7fa1819b150721a.png)作者图片—关于[松果. io](https://www.pinecone.io/learn/semantic-search/) 的原创文章相似性搜索是人工智能和机器学习领域中发展最快的领域之一。其核心是将相关信息匹配在一起的过程。你很有可能是通过搜索引擎找到这篇文章的——最有可能是谷歌。也许你搜索了类似“什么是相似性搜索?”或者“传统 vs 向量相似性搜索”。Google 处理了您的查询,并使用了许多相同的相似性搜索要素,我们将在本文中了解这些要素,将您带到—这篇文章。如果相似性搜索是一家市值 1.65 万亿美元的公司——全球第五大最有价值公司[1]的成功核心,那么很有可能*值得进一步了解。*[相似性搜索](https://www.pinecone.io/learn/what-is-similarity-search/)是一个复杂的话题,有无数种技术可以用来构建有效的搜索引擎。在本文中,我们将讨论这些技术中最有趣也是最强大的一些,特别关注语义搜索。我们将了解它们是如何工作的,它们擅长什么,以及我们如何自己实现它们。# 传统搜索我们在传统阵营中开始了我们的搜索之旅,在这里我们找到了几个关键角色,例如:*   **Jaccard 相似度**
*   **w-收缩**
*   皮尔逊相似性
*   **Levenshtein 距离**
*   归一化谷歌距离所有这些都是用于相似性搜索的很好的指标——其中我们将讨论三个最流行的指标,Jaccard 相似性、w-shingling 和 Levenshtein 距离。视频演练涵盖相同的三种传统相似性方法。## 雅克卡相似性Jaccard 相似性是一个简单但有时很强大的相似性度量。给定两个序列, **A** 和 **B** —我们找出两者之间共有元素的数量,并将其除以两个序列中元素的总数。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/c3d08c8a4ff4f3013720022fbf5839be.png)Jaccard 相似性度量两个序列之间的交集超过两个序列之间的并集。给定两个整数序列,我们将写出:这里我们确定了**两个**共有的*唯一的*整数`3`和`4`——两个序列共有十个整数,其中**八个**是唯一的值— `2/8`给出了我们的 Jaccard 相似性得分`0.25`。我们也可以对文本数据执行同样的操作,我们所做的就是用*标记*替换*整数*。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/4a009ededd04e7bc20930f125346c494.png)计算两个句子 **a** 和 **b** 之间的 Jaccard 相似度。正如我们所料,我们发现句子`b`和`c`得分更高。现在,它并不完美——两个句子除了像*、*、*、【a】、*、*、*这样的词之外什么都不共享,尽管语义不同,但可以返回高的 Jaccard 分数。这些缺点可以通过使用预处理技术部分解决,如停用词移除、词干化/词汇化等。然而,正如我们很快会看到的,一些方法完全避免了这些问题。## w-收缩另一个类似的技术是**w-shringing**。w-shingling 使用了与*交集/并集*完全相同的逻辑,但是使用了“瓦片区”。句子 **a** 的两个重叠部分看起来像这样:

a = {'his thought', 'thought process', 'process is', ...}


然后,我们将在我们的*和*重叠的句子之间使用同样的`intersection / union`计算,如下所示:使用 2 瓦片区,我们在句子 **b** 和 **c** 之间找到三个匹配瓦片区,导致相似度为 **0.125** 。## 莱文斯坦距离比较两个字符串的另一个流行度量是 Levenshtein 距离。它的计算方法是将一个字符串转换为另一个字符串所需的操作次数,计算公式如下:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/46b80e2df6e1fe970ef7de5e88c44cda.png)莱文斯坦距离公式。现在,这是一个看起来相当复杂的公式——如果你理解它,太好了!如果没有,不要担心,我们会分解它。变量`a`和`b`代表我们的两个字符串,`i`和`j`分别代表`a`和`b`中的字符位置。所以给定琴弦:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/00b1573a1de39965979c79a099f1b392.png)“Levenshtein”和一个混淆的“Livinshten”。我们会发现:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/c11646153dde24ca721641c852b4bbd0.png)我们从 1 到单词的长度对单词本身进行索引,第零个索引确实作为一个 **none** 字符存在(下一步将详细介绍)。轻松点。现在,掌握这个公式背后的逻辑的一个很好的方法是通过可视化 Wagner-Fischer 算法——它使用一个简单的矩阵来计算我们的 Levenshtein 距离。我们将两个单词`a`和`b`放在矩阵的任意一个轴上——我们将 *none* 字符作为一个空格。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/b8b39ffffc6e23884dada59acae82d0a.png)我们的空 Wagner-Fischer 矩阵——我们将用它来计算**‘Levenshtein’**和**‘livinhten’**之间的 Levenshtein 距离。用代码初始化我们的空瓦格纳菲舍尔矩阵。然后,我们遍历矩阵中的每个位置,并应用我们之前看到的复杂公式。我们公式的第一步是`if min(i, j) = 0`——我们在这里说的是,在我们的两个位置`i`和`j`中,要么是`0`?如果是,我们转到`max(i, j)`,它告诉我们将矩阵中的当前位置分配给两个位置`i`和`j`中较高的一个:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/5cd5108c4388293871f3d248eaee132a.png)我们从右边开始,沿着边缘在 **i** 和/或 **j** *为 0 的地方,矩阵位置将用* ***max(i,j)*** *填充。*上面可视化的 **min(i,j) == 0** 后接 **max(i,j)** 运算—转换成代码。现在,我们已经处理了矩阵的外部边缘——但我们仍然需要计算内部值——这是我们的最佳路径所在。回到`if min(i, j) = 0` —如果`0`都不是呢?然后,我们进入`min {`部分的复杂部分。我们需要为每一行计算一个值,然后我们取 **min** imum 值。现在,我们已经知道了这些值,它们在我们的矩阵中:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/34380c78eee8feb8fc5e3f6919067f7f.png)对于矩阵中的每个新位置,我们从三个相邻的位置(左上方的圆圈)中取最小值。`lev(i-1, j)`其他操作都是索引操作——我们提取那个位置的值。然后我们取三个中的最小值。只剩下一个操作了。只有在`a[i] != b[i]`的情况下,左侧的`+1`才应适用——这是对不匹配字符的惩罚。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/9d7cf396b949f65b613b5b97915c577e.png)如果一个[我]!= b[j]我们将 **1** 加到最小值上——这是对不匹配字符的惩罚。将所有这些放入一个贯穿整个矩阵的迭代循环中,看起来像这样:使用瓦格纳-费歇尔矩阵的全 Levenshtein 距离计算。我们现在已经计算了矩阵中的每个值——这些值代表了从字符串`a`到位置`i`再到字符串`b`到位置`j`的转换所需的操作数。我们正在寻找将`a`转换为`b`的操作数——所以我们在`lev[-1, -1]`取数组右下角的值。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/8fc76ce58f36cc268d9d7b5de356b9fe.png)通过矩阵的最佳路径——在右下角的[-1,-1]位置,我们有两个字符串之间的 Levenshtein 距离。# 向量相似性搜索对于基于向量的搜索,我们通常会找到几种向量构建方法中的一种:*   **TF-IDF**
*   **BM25**
*   word2vec/doc2vec
*   **伯特**
*   使用与*近似*最近邻(ANN)的一些实现相结合,这些基于向量的方法是相似性搜索领域中的 MVP。我们将讨论 TF-IDF、BM25 和基于 BERT 的方法——因为这些方法很容易成为最常见的方法,并且涵盖了稀疏和密集的[矢量表示](https://www.pinecone.io/learn/vector-embeddings/)。视频演练涵盖相同的三种基于向量的相似性方法。1。**TF-IDF**——向量相似性搜索的鼻祖,诞生于 20 世纪 70 年代。它由两部分组成,即 **T** erm **F** 频率(TF)和 **I** 反向 **D** 文件 **F** 频率(IDF)。TF 组件计算一个术语在一个文档中出现的次数,并将其除以同一文档中的术语总数。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/c6de71a0404f4de3d1d055c9f07e2da8.png)TF-IDF 的术语频率(TF)组件计算我们的查询(“香蕉”)的频率,并除以所有标记的频率。这是我们计算的前半部分,我们有当前 **D** 文件`f(q,D)`内我们 **q** 查询的 **f** 频率—超过当前 **D** 文件`f(t,D)`内所有**t**erm 的 **f** 频率。频率是一个很好的衡量标准,但是不能让我们区分常用词和不常用词。如果我们要搜索单词“the”——只使用 TF,我们会赋予这个句子与搜索“香蕉”相同的相关性。这很好,直到我们开始比较文档或使用更长的查询进行搜索。我们不希望像*、*、*、*、*、【it】、*这样的词被排在和*、【香蕉】、*、*、【街道】、*一样高的位置。理想情况下,我们希望更罕见的单词之间的匹配得分更高。为此,我们可以将 TF 乘以第二项——IDF。文档频率衡量一个单词在我们所有文档中出现的频率。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/5de315eeab048dcc7174b38ebf35a5c1.png)TF-IDF 的逆文档频率(IDF)组件计算包含我们的查询的文档的数量。在这个例子中,我们有三个句子。当我们计算常用词*是*的 IDF 时,我们返回的数字要比更罕见的词*“森林”*的 IDF 低得多。如果我们要同时搜索单词*‘is’*和*‘forest’*,我们会像这样合并 TF 和 IDF:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/883a4511782078a2e817a55815da25d2.png)我们计算文档 **a** 、 **b** 和 **c** 的 **TF('is ',D)** 和 **TF('forest ',D)** 得分。IDF 值跨所有文档—因此我们只计算一次 **IDF('is')** 和 **IDF('forest')** 。然后,我们通过将**乘以 **TF** 和 **IDF** 分量来获得每个文档中两个单词的 TF-IDF 值。句子 **a** 得分最高为**‘森林’**,**‘是’**总得分 **0** 为**IDF(‘是’)**得分为 **0** 。**这很好,但是*向量*相似性搜索在这里起什么作用呢?好吧,我们使用我们的词汇表(我们数据集中所有单词的大列表),并计算每个单词的 TF-IDF。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/291da457a99156a7d167e042a051a501.png)我们计算词汇表中每个单词的 TF-IDF 值,以创建一个 TF-IDF 向量。对每个文档重复这一过程。我们可以将所有这些放在一起创建 TF-IDF 向量,如下所示:从那里我们得到了 TF-IDF 向量。值得注意的是,vocab 的大小很容易在 20K+的范围内,因此使用这种方法产生的向量非常稀疏,这意味着我们无法编码任何语义意义。2。 **BM25** —作为 TF-IDF 的继任者,Okapi BM25 是优化 TF-IDF 的结果,主要是为了根据文档长度规范化结果。TF-IDF 很棒,但当我们开始比较几个提及时,可能会返回可疑的结果如果我们拿了两篇 500 字的文章,发现文章 A 提到了‘丘吉尔’6 次,而文章 B 提到了‘丘吉尔’12 次——我们应该认为文章 A 只有一半相关吗?不太可能。BM25 通过修改 TF-IDF 解决了这个问题:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/9f63203caa83d741b0326f4249d0214e.png)BM25 配方。这是一个看起来相当糟糕的等式——但它只不过是我们的 TF-IDF 公式加上一些新参数而已!让我们比较两个 TF 组件:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/2237c4acdc6c665ebf8d15723a273f13.png)BM25 的 TF 部分(左)对比 TF-IDF 的 TF(右)。然后我们有 IDF 部分,它甚至没有引入任何新的参数—它只是从 TF-IDF 重新安排了我们的旧 IDF。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/0790f4791231d04f118cd601ec59b8cb.png)BM25 的 IDF 部分(左)对比 TF-IDF 的 IDF(右)。现在,这个修改的结果是什么?如果我们取一个包含 12 个标记的序列,并逐渐输入越来越多的“匹配”标记,我们会得到以下分数:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/4ba6c51f83caf9119f0036baf502841c.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/75e18756f79370b0885d95d16ecdfe12.png)TF-IDF(左)和 BM25(右)算法的比较,使用 12 个标记的句子和相关标记的递增数量(x 轴)。TF-IDF 分数随着相关令牌的数量线性增加。因此,如果频率翻倍,TF-IDF 得分也会翻倍。BM25 抑制分数增加,当相关令牌从两个增加到四个时,我们看到一个 *x1.25* 的增加,当我们再次从四个增加到八个时,我们看到一个 *x1.13* 的增加。听起来很酷!但是我们如何用 Python 实现呢?同样,我们会像 TF-IDF 实现一样保持简洁明了。我们已经为`k`和`b`使用了默认参数——我们的输出看起来很有希望。查询`'purple'`只匹配句子`a`,而`'bananas'`对`b`和`c`的得分都是合理的——但由于字数较少,`c`的得分略高。为了从中构建向量,我们做了与 TF-IDF 完全相同的事情。同样,就像我们的 TF-IDF 向量一样,这些是*稀疏*向量。我们将无法对语义进行编码——而是专注于语法。让我们看看如何开始考虑语义。3。**BERT**——或者来自 Transformers 的双向编码器表示——是一个非常流行的 transformer 模型,用于 NLP 中的几乎所有东西。通过 12 层左右的编码器,BERT 将大量信息编码成一组密集的 T21 向量。每个密集向量通常包含 768 个值——我们通常为 BERT 编码的每个句子提供 512 个这样的向量。这些向量包含了我们所能看到的语言的数字表示。如果需要,我们还可以从不同的层提取这些向量,但通常是从最后一层提取。现在,有了两个正确编码的密集向量,我们可以使用类似余弦相似性的相似性度量来计算它们的语义相似性。对齐程度越高的向量语义越相似,反之亦然。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/e6b0e522423c7b8c866c42b1dbfb481e.png)向量之间的角度越小(用余弦相似度计算),意味着它们越对齐。对于密集向量,这与更大的语义相似性相关。但有一个问题,每个序列由 512 个向量表示,而不是一个向量。所以,这是伯特的另一个精彩改编的地方。Sentence-BERT 允许我们创建一个单一的向量来代表我们的完整序列,也称为*句子向量* [2]。我们有两种实现 SBERT 的方法——使用`sentence-tranformers`库的简单方法,或者使用`transformers` *和* PyTorch 的稍微不太简单的方法。我们将涉及这两个方面,从 PyTorch 方法的`transformers`开始,这样我们可以直观地了解这些向量是如何构建的。如果你用过 HF transformers 库,前几个步骤看起来会很熟悉。我们初始化我们的 SBERT 模型和标记器,标记我们的文本,并通过模型处理我们的标记。我们在这里添加了一个新句子,句子 **g** 具有与 **b** 相同的*语义*含义,但没有相同的关键字。由于缺乏共有词,我们以前的所有方法都很难找到这两个序列之间的相似性——记住这一点,以后再说。我们有长度为 *768 —* 的向量,但这些是**而不是** *句子向量*,因为我们对序列中的每个标记都有一个向量表示(这里是 128,因为我们使用 SBERT —对于 BERT-base 是 512)。我们需要执行一个 **mean pooling** 操作来创建句子向量。我们做的第一件事是将我们的`embeddings`张量中的每个值乘以它各自的`attention_mask`值。`attention_mask`包含**1**,这里我们有‘实令牌’(例如不是填充令牌),而**0**在别处——这个操作允许我们忽略非实令牌。这些是我们的句子向量,利用它们,我们可以通过计算它们之间的余弦相似度来衡量相似度。如果我们可视化我们的阵列,我们可以很容易地识别更高相似性的句子:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/efe259b46bd729bf63e480e2d6b5a50a.png)热图显示了我们的 SBERT 句子向量之间的余弦相似性——句子 **b** 和 **g** 之间的得分被圈起来。现在,回想一下之前的笔记,关于句子 **b** 和 **g** 具有基本相同的意思,而**没有**共享*任何*相同的关键字。我们希望 SBERT 及其卓越的语言语义表示能够确定这两个句子是相似的——令人惊讶的是,这两个句子之间的相似性是我们第二高的分数,为 0.66(上面画了圈)。现在,**另一种(简单的)方法是使用句子变形器**。为了获得与上面完全相同的输出,我们编写:当然,这要容易得多。这就是 Jaccard,Levenshtein 和 Bert 的历史之旅!我们总共讨论了五种不同的技术,从简单的 Jaccard 相似性和 Levenshtein 距离开始。然后使用稀疏矢量进行搜索——TF-IDF 和 BM25,最后使用 SBERT 完成最新的密集矢量表示。我希望你喜欢这篇文章。如果您有任何问题或建议,请通过推特[或在下面的评论中告诉我。如果你对更多类似的内容感兴趣,我也会在 YouTube 上发布。](https://twitter.com/jamescalam)感谢阅读![📚在 Pinecone.io 了解有关可扩展搜索的更多信息](https://www.pinecone.io/learn/)# 参考[1][Alphabet(谷歌)](https://companiesmarketcap.com/alphabet-google/marketcap/)市值,公司市值[2] N. Reimers,I. Gurevych,[句子-BERT:使用连体 BERT 网络的句子嵌入](https://arxiv.org/abs/1908.10084) (2019),2019 年实证方法 2019 年会议录[笔记本回购](https://github.com/pinecone-io/examples/tree/master/semantic_search_intro)colab for[JAC card](https://colab.research.google.com/github/pinecone-io/examples/blob/master/semantic_search_intro/jaccard.ipynb)|[Levenshtein](https://colab.research.google.com/github/pinecone-io/examples/blob/master/semantic_search_intro/levenshtein.ipynb)|[TF-IDF](https://colab.research.google.com/github/pinecone-io/examples/blob/master/semantic_search_intro/tfidf.ipynb)|[BM25](https://colab.research.google.com/github/pinecone-io/examples/blob/master/semantic_search_intro/bm25.ipynb)|[SBERT](https://colab.research.google.com/github/pinecone-io/examples/blob/master/semantic_search_intro/sbert.ipynb)[🤖《变形金刚》课程 NLP 的 70%折扣](https://bit.ly/nlp-transformers)**除非另有说明,所有图片均出自作者之手*# 使用 Weaviate 在维基百科中进行语义搜索(GraphQL、Sentence-BERT 和 BERT Q&A)> 原文:<https://towardsdatascience.com/semantic-search-through-wikipedia-with-weaviate-graphql-sentence-bert-and-bert-q-a-3c8a5edeacf6?source=collection_archive---------11----------------------->## 使用 Weaviate 矢量搜索引擎在整个维基百科中进行语义搜索![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/a6f1d6fd661c5413c77983c2bbc9b5cd.png)Gulnaz Sh 拍摄的照片。为了进行大规模的语义搜索查询,需要向量搜索引擎来搜索表示数据的大量向量表示。为了向你展示如何做到这一点,我们[在](https://github.com/semi-technologies/semantic-search-through-wikipedia-with-weaviate) [Weaviate](https://github.com/semi-technologies/weaviate) 开源了完整的英语维基百科语料库。在本文中,我将概述我们如何创建数据集,向您展示如何自己运行数据集,并介绍如何在您自己的项目中实现类似的矢量和语义搜索解决方案以及如何将它们投入生产的搜索策略。使用的维基百科数据集是 2021 年 10 月 9 日的“truthy”版本。处理后,它包含 11.348.257 条、27.377.159 段和 125.447.595 图表交叉引用。虽然导入数据需要更大的机器(见下文),但服务是在 12 个 CPU、100 GB RAM、250Gb SSD Google Cloud VM 和 1 个 NVIDIA Tesla P4 上完成的。所用的 ML 型号为"[multi-QA-MiniLM-L6-cos-v1](https://huggingface.co/sentence-transformers/multi-qa-MiniLM-L6-cos-v1)"和"[Bert-large-un cased-whole-word-masking-fine tuned-squad](https://huggingface.co/bert-large-uncased-whole-word-masking-finetuned-squad)",两者均可作为 Weaviate 中的[预建模块](https://www.semi.technology/developers/weaviate/current/modules/text2vec-transformers.html#pre-built-images)。📄完整的数据集和代码在 Github [这里](https://github.com/semi-technologies/semantic-search-through-wikipedia-with-weaviate)是开源的。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/d2e5057fd6262b84e7f3a18216cdb36d.png)Weaviate 的 GraphQL 界面中的语义搜索查询示例—作者 GIF# 分两步导入数据> 您也可以直接将备份导入 Weaviate,而不需要像这里的[所描述的那样自己导入。](https://github.com/semi-technologies/semantic-search-through-wikipedia-with-weaviate/tree/main#step-3-load-from-backup)为了导入数据,我们使用两种不同的方法。第一个是清理数据集,第二个是导入数据。## 步骤 1–清理数据第一步非常简单,我们将清理数据并创建一个 [JSON Lines](https://jsonlines.org/) 文件,以便在导入过程中迭代。您可以自己运行该流程,或者通过[该](https://github.com/semi-technologies/semantic-search-through-wikipedia-with-weaviate#step-1-process-the-wikipedia-dump)链接下载 proceed 文件。## 步骤 2 —导入数据这就是繁重工作发生的地方,因为所有段落都需要矢量化。我们将使用 Weaviate 的模块化设置来使用多个 GPU,我们将在其中填充模型,但在此之前,我们需要创建一个代表我们用例的 Weaviate 模式。## 步骤 2.1 —创建一个弱化模式在 Weaviate 中,我们将使用一个模式来决定如何查询 GraphQL 中的数据,以及我们希望对哪些部分进行矢量化。在一个模式中,您可以设置不同的矢量化工具,并在类级别上对指令进行矢量化。首先,因为我们的用例是在维基百科上进行语义搜索,所以我们将把数据集分成段落,并使用 Weaviate 的图表将它们链接回文章。因此,我们需要两个类;*条*和*款*。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/02948d6c65e4f62fb05ee3f6b97d46af.png)弱化阶级结构——作者的形象接下来,我们要确保段落的内容得到正确的矢量化,SentenceBERT 转换器生成的矢量表示将用于我们所有的语义搜索查询。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/80c9a17aee94b9e8d8b11ee728b6a597.png)被矢量化的单一数据类型——按作者分类的图像最后,我们希望建立图表关系,在第一步的数据集中,我们将提取我们可以引用的文章之间的所有图表关系,如下所示:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/08f962e104f3628c53054ebcb2f85448.png)段落交叉引用—作者图片我们使用 [Python 客户端](https://www.semi.technology/developers/weaviate/current/client-libraries/python.html)导入的完整模式可以在[这里](https://github.com/semi-technologies/semantic-search-through-wikipedia-with-weaviate/blob/main/step-2/import.py#L19-L120)找到。## 步骤 2.2 —导入数据因为我们要对*大量*数据进行矢量化。我们将使用与开篇中提到的相同的机器,但使用 4 个而不是 1 个 GPU。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/a22aa0e7810873aa685e76afd6bed377.png)带有弱负载平衡器的谷歌云 GPU 设置——图片由作者提供负载均衡器会将流量重定向到可用的 Weaviate transformer 模块,从而显著提高导入速度。在以下章节:*实施策略——将语义搜索应用到生产中*,您将找到更多关于如何在生产中运行语义搜索的信息。最重要的是,我们将在 Docker Compose 文件中设置一个外部卷,以确保我们将数据存储在容器之外。这将允许我们打包备份,并在最后一步中直接从备份运行 Weaviate。在环境变量中,我们设置了一个 CLUSTER_HOSTNAME,这是一个可以用来标识集群的任意名称。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/77bb5105079cb8ad260c424f63bbb182.png)Docker 环境设置—按作者分类的图像我们还将在 Weaviate 外部设置卷的位置,在这种情况下,数据将存储在/var/weaviate 文件夹中![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/7e31d34ff16d521b8399403c348f0ed2.png)备份卷—按作者分类的图像你可以在这里找到我们使用过的完整的 docker-compose 文件。# 查询数据当前的 Weaviate 设置启用了两个模块:语义搜索和问答。这些模块可以用于不同类型的查询。所使用的查询语言是 GraphQL,可以与各种不同编程语言的[客户端库](https://www.semi.technology/developers/weaviate/current/client-libraries/)一起使用。## 示例 1 —自然语言问题在这个例子中,我们提出一个自然语言问题,我们将假设第一个结果包含答案(因此限制被设置为 1)。基于最新数据集的结果包含约 0.68 的确定性(即,向量空间中从查询到答案的距离)。在您的终端应用中,您可以对确定性进行限制,以确定您是否希望将结果呈现给最终用户,在本文的最新段落(*实施策略—将语义搜索引入生产*)中,您将找到更多相关信息。💡LIVE — [尝试这个查询](http://console.semi.technology/console/query#weaviate_uri=http://semantic-search-wikipedia-with-weaviate.api.vectors.network:8080&graphql_query=%23%23%0A%23%20Using%20the%20Q%26A%20module%20I%0A%23%23%0A%7B%0A%20%20Get%20%7B%0A%20%20%20%20Paragraph(%0A%20%20%20%20%20%20ask%3A%20%7B%0A%20%20%20%20%20%20%20%20question%3A%20%22Where%20is%20the%20States%20General%20of%20The%20Netherlands%20located%3F%22%0A%20%20%20%20%20%20%20%20properties%3A%20%5B%22content%22%5D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20limit%3A%201%0A%20%20%20%20)%20%7B%0A%20%20%20%20%20%20_additional%20%7B%0A%20%20%20%20%20%20%20%20answer%20%7B%0A%20%20%20%20%20%20%20%20%20%20result%0A%20%20%20%20%20%20%20%20%20%20certainty%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20content%0A%20%20%20%20%20%20title%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D)## 示例 2 —通用概念搜索人们不仅可以搜索自然语言问题,还可以在下面的概述中搜索像“意大利食品”这样的通用概念。 *nearText* 过滤器还允许[更具体的过滤器](https://www.semi.technology/developers/weaviate/current/modules/text2vec-transformers.html#neartext),如 *moveAwayFrom* 和 MoveTo concepts,以操纵向量空间中的搜索。💡LIVE — [尝试这个查询](http://console.semi.technology/console/query#weaviate_uri=http://semantic-search-wikipedia-with-weaviate.api.vectors.network:8080&graphql_query=%23%23%0A%23%20Generic%20question%20about%20Italian%20food%0A%23%23%0A%7B%0A%20%20Get%20%7B%0A%20%20%20%20Paragraph(%0A%20%20%20%20%20%20nearText%3A%20%7B%0A%20%20%20%20%20%20%20%20concepts%3A%20%5B%22Italian%20food%22%5D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20limit%3A%2050%0A%20%20%20%20)%20%7B%0A%20%20%20%20%20%20content%0A%20%20%20%20%20%20order%0A%20%20%20%20%20%20title%0A%20%20%20%20%20%20inArticle%20%7B%0A%20%20%20%20%20%20%20%20...%20on%20Article%20%7B%0A%20%20%20%20%20%20%20%20%20%20title%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D)## 示例 3 —混合自然语言问题和标量搜索在 Weaviate 中,你也可以混合标量搜索过滤器和矢量搜索过滤器。在这个特定的例子中,我们想要对关于萨克斯演奏家麦克·布雷克的文章的所有段落进行语义搜索查询。💡LIVE — [尝试这个查询](http://console.semi.technology/console/query#weaviate_uri=http://semantic-search-wikipedia-with-weaviate.api.vectors.network:8080&graphql_query=%23%23%0A%23%20Mixing%20scalar%20queries%20and%20semantic%20search%20queries%0A%23%23%0A%7B%0A%20%20Get%20%7B%0A%20%20%20%20Paragraph(%0A%20%20%20%20%20%20ask%3A%20%7B%0A%20%20%20%20%20%20%20%20question%3A%20%22What%20was%20Michael%20Brecker's%20first%20saxophone%3F%22%0A%20%20%20%20%20%20%20%20properties%3A%20%5B%22content%22%5D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20where%3A%20%7B%0A%20%20%20%20%20%20%20%20operator%3A%20Equal%0A%20%20%20%20%20%20%20%20path%3A%20%5B%22inArticle%22%2C%20%22Article%22%2C%20%22title%22%5D%0A%20%20%20%20%20%20%20%20valueString%3A%20%22Michael%20Brecker%22%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20limit%3A%201%0A%20%20%20%20)%20%7B%0A%20%20%20%20%20%20_additional%20%7B%0A%20%20%20%20%20%20%20%20answer%20%7B%0A%20%20%20%20%20%20%20%20%20%20result%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20content%0A%20%20%20%20%20%20order%0A%20%20%20%20%20%20title%0A%20%20%20%20%20%20inArticle%20%7B%0A%20%20%20%20%20%20%20%20...%20on%20Article%20%7B%0A%20%20%20%20%20%20%20%20%20%20title%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D)## 示例 4 —混合通用概念搜索和图形关系有了 Weaviate,你还可以使用 GraphQL 接口来建立图形关系,就像维基百科中不同文章之间的链接一样。在这个概述中,我们将段落与文章连接起来,并显示链接的文章。💡现场— [尝试这个查询](http://console.semi.technology/console/query#weaviate_uri=http://semantic-search-wikipedia-with-weaviate.api.vectors.network:8080&graphql_query=%23%23%0A%23%20Using%20the%20Q%26A%20module%20I%0A%23%23%0A%7B%0A%20%20Get%20%7B%0A%20%20%20%20Paragraph(%0A%20%20%20%20%20%20ask%3A%20%7B%0A%20%20%20%20%20%20%20%20question%3A%20%22Where%20is%20the%20States%20General%20of%20The%20Netherlands%20located%3F%22%0A%20%20%20%20%20%20%20%20properties%3A%20%5B%22content%22%5D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20limit%3A%201%0A%20%20%20%20)%20%7B%0A%20%20%20%20%20%20_additional%20%7B%0A%20%20%20%20%20%20%20%20answer%20%7B%0A%20%20%20%20%20%20%20%20%20%20result%0A%20%20%20%20%20%20%20%20%20%20certainty%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20content%0A%20%20%20%20%20%20title%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D)# 实施策略——将语义搜索引入生产Weaviate 的目标是让您将大型的 ML-first 应用程序投入生产。但是就像任何技术一样,它并不是灵丹妙药,成功取决于您的实施。## 可量测性演示数据集在单台机器上的 Docker 设置上运行,如果您想在生产中使用 Weaviate 数据集,可以轻松启动 Kubernetes 集群。如何做到这一点,这里概述了。# 结论要将语义搜索解决方案投入生产,您需要三样东西:1.  数据
2.  ML 模型
3.  向量搜索引擎在本文中,我们展示了如何使用开源 ML 模型(Sentence-BERT)和矢量搜索引擎(Weaviate)将完整的维基百科语料库(数据)投入生产。我们期待听到你将创造什么。请务必通过 [Slack](https://join.slack.com/t/weaviate/shared_invite/zt-goaoifjr-o8FuVz9b1HLzhlUfyfddhw) 、 [Twitter](https://twitter.com/SeMI_tech) 或 [Github](https://github.com/semi-technologies/weaviate) 让我们知道。# 使用转换器的语义相似度> 原文:<https://towardsdatascience.com/semantic-similarity-using-transformers-8f3cb5bf66d6?source=collection_archive---------4----------------------->## 使用 Pytorch 和 SentenceTransformers 计算两个文本之间的语义文本相似度![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/112c01ef2a9a3cd5d222c16b3cf9268f.png)照片由[🇸🇮·扬科·菲利](https://unsplash.com/@itfeelslikefilm?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄# 介绍[**语义相似度**](https://en.wikipedia.org/wiki/Semantic_similarity) ,或语义文本相似度,是 [**自然语言处理(NLP)**](https://en.wikipedia.org/wiki/Natural_language_processing) 领域中的一项任务,使用一个定义的度量对文本或文档之间的关系进行评分。语义相似度有着广泛的应用,如信息检索、文本摘要、情感分析等。语义相似度有很多方法。**现在最直接有效的方法是使用一个强大的模型(例如 transformer)对句子进行编码,以获得它们的嵌入,然后使用一个相似性度量(例如余弦相似性)来计算它们的相似性得分。**相似度得分表示两个文本是否具有相似或更多不同的含义。**本帖将展示如何使用**[**Transformers**](https://en.wikipedia.org/wiki/Transformer_(machine_learning_model))实现语义相似性,这是一个强大的 NLP 架构,为各种 NLP 任务带来了一流的性能。所以事不宜迟,让我们开始吧!# 教程概述1.  安装依赖项和库
2.  导入库
3.  模型选择和初始化
4.  计算两个句子之间的语义相似度
5.  计算两个句子列表之间的语义相似度
6.  从给定句子的语料库中检索前 K 个最相似的句子# 安装依赖项我们将要用来计算语义相似度的主要库是[sentence transformers](https://www.sbert.net/index.html)([Github source link](https://github.com/UKPLab/sentence-transformers)),这是一个简单的库,提供了一种简单的方法来计算文本的密集向量表示(例如嵌入)。它包含许多先进的预训练模型,针对各种应用进行了微调。它支持的主要任务之一是语义文本相似性,这是我们将在这篇文章中关注的。要安装 SentenceTransformers,你必须先安装依赖项 [Pytorch](https://pytorch.org/) 和 [Transformers](https://github.com/huggingface/transformers) 。## 安装 Pytorch进入 [Pytorch 官网](https://pytorch.org/)按照说明安装 Pytorch。## 安装变压器要安装变压器,请运行:

pip install transformers


## 安装句子变压器现在,您已经安装了 Pytorch 和 transformers,您可以通过以下方式安装 SentenceTransformers:

pip install sentence-transformers


*注:SentenceTransformers 推荐 Python 3.6 以上,PyTorch 1.6.0 以上,transformers v3.1.0 以上。*# 导入库在成功安装了 SentenceTransformers 库及其依赖项之后,我们可以开始使用这个库了。我们可以通过以下方式导入它:

from sentence_transformers import SentenceTransformer, util
import numpy as np


# 模型选择和初始化SentenceTransformers 支持各种预训练模型,这些模型针对开箱即用的不同任务进行了微调。要查找针对语义文本相似性优化的模型列表,您可以在这里看到它[。](https://docs.google.com/spreadsheets/d/14QplCdTCDwEmTqrn1LH4yrbKvdogK4oQvYO1K1aPR5M/edit#gid=0)截至本文撰写之时,`stsb-roberta-large`使用 [ROBERTA-large](https://arxiv.org/abs/1907.11692) 作为基础模型和均值池,是语义相似性任务的最佳模型。因此,我们用这个模型来证明。选择模型后,我们可以通过以下方式初始化它:

model = SentenceTransformer('stsb-roberta-large')


# 计算两个句子之间的语义相似度在定义了我们的模型之后,我们现在可以计算两个句子的相似性得分。如引言中所讨论的,该方法是使用该模型对两个句子进行编码,然后计算得到的两个嵌入的余弦相似度。最终结果将是语义相似度得分。一般来说,我们可以使用不同的公式来计算最终的相似性得分(例如,点积、Jaccard 等。),但在这种情况下,由于余弦相似性的性质,我们使用余弦相似性。更重要的因素是由模型产生的嵌入,因此使用合适的编码模型是很重要的。为了使用所讨论的方法来计算语义相似性得分,我们可以运行以下内容:

sentence1 = "I like Python because I can build AI applications"
sentence2 = "I like Python because I can do data analytics"# encode sentences to get their embeddings
embedding1 = model.encode(sentence1, convert_to_tensor=True)
embedding2 = model.encode(sentence2, convert_to_tensor=True)# compute similarity scores of two embeddings
cosine_scores = util.pytorch_cos_sim(embedding1, embedding2)print("Sentence 1:", sentence1)
print("Sentence 2:", sentence2)
print("Similarity score:", cosine_scores.item())


我们首先定义两个句子,句子 1 和句子 2,然后使用我们之前定义的模型对它们进行编码。我们将最终的嵌入转换为张量,这样 GPU 可以更快地处理它们。对于像我们这样的小数据,这不是必需的,但仍然是一个很好的实践。然后,我们可以使用 util 提供的 pytorch_cos_sim 函数方便地计算两个嵌入之间的余弦相似性得分,这要感谢句子转换器。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/946e7021861df84c08aa6b99c428c89c.png)抽样输出最后可以看到相似度得分。我们可以看到,在这种情况下,句子 1 和句子 2 的分数接近 1,这意味着它们非常相似。# 计算两个句子列表之间的语义相似度当你想相互比较更多的句子时,你可以把句子放入两个列表中,用和上面一样的代码计算它们之间的相似度得分。最终结果将是一个相似性得分矩阵,其中`i, j`元素包含列表 1 中的句子`i`和列表 2 中的句子`j`之间的相似性得分。为此,请运行以下命令:

sentences1 = ["I like Python because I can build AI applications", "The cat sits on the ground"]
sentences2 = ["I like Python because I can do data analytics", "The cat walks on the sidewalk"]# encode list of sentences to get their embeddings
embedding1 = model.encode(sentences1, convert_to_tensor=True)
embedding2 = model.encode(sentences2, convert_to_tensor=True)# compute similarity scores of two embeddings
cosine_scores = util.pytorch_cos_sim(embedding1, embedding2)for i in range(len(sentences1)):
for j in range(len(sentences2)):
print("Sentence 1:", sentences1[i])
print("Sentence 2:", sentences2[j])
print("Similarity Score:", cosine_scores[i][j].item())
print()


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/bc765e66f9109e1248f82b5c27aefb80.png)抽样输出你可以看到这两个句子对(“我喜欢 Python,因为我可以构建 AI 应用”、“我喜欢 Python,因为我可以做数据分析”)和(“猫坐在地上”、“猫走在人行道上”)比较相似。因此,输出的相似性得分也相对较高。另一方面,Python 与猫非常不相似,反之亦然,因此其他两个句子对的相似度得分较低。# **从给定句子的语料库中检索前 K 个最相似的句子**语义相似性的一个流行用例是在给定查询句子的语料库中找到最相关的句子。这也可以称为语义搜索。为了进行语义搜索,我们需要一个句子语料库和一个充当查询的句子。然后,我们可以使用我们的模型对语料库和我们的句子进行编码,然后使用与前面相同的方法计算我们的句子和语料库中每个句子之间的余弦相似性得分。最后,通过获得前 k 个最高相似度得分,我们可以获得前 k 个最相似的句子。

corpus = ["I like Python because I can build AI applications",
"I like Python because I can do data analytics",
"The cat sits on the ground",
"The cat walks on the sidewalk"]# encode corpus to get corpus embeddings
corpus_embeddings = model.encode(corpus, convert_to_tensor=True)sentence = "I like Javascript because I can build web applications"# encode sentence to get sentence embeddings
sentence_embedding = model.encode(sentence, convert_to_tensor=True)# top_k results to return
top_k=2# compute similarity scores of the sentence with the corpus
cos_scores = util.pytorch_cos_sim(sentence_embedding, corpus_embeddings)[0]# Sort the results in decreasing order and get the first top_k
top_results = np.argpartition(-cos_scores, range(top_k))[0:top_k]print("Sentence:", sentence, "\n")
print("Top", top_k, "most similar sentences in corpus:")
for idx in top_results[0:top_k]:
print(corpus[idx], "(Score: %.4f)" % (cos_scores[idx]))


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/1f22f0288bb2557481f58240cf453812.png)抽样输出在我们的例子中,我们的语料库有 4 个句子,我们将 top_k 设置为 2,以检索与我们的查询句子最相似的前 2 个句子。我们的查询语句是“我喜欢 Javascript,因为我可以构建 web 应用程序”,返回关于 Python 的两个语句是因为 Javascript 比 cats 更类似于 Python。# 结论现在你有了!现在,你应该能够理解如何计算句子之间的语义相似度,句子列表,以及从语料库中检索最相似的数据。为了方便起见,下面是这篇文章的全部 Jupyter 代码:如果你有任何问题,请在下面的评论中告诉我!如果你喜欢我的作品,可以随意浏览我的其他文章:)</top-nlp-books-to-read-2020-12012ef41dc1>  </top-nlp-libraries-to-use-2020-4f700cdb841f>  </bert-text-classification-using-pytorch-723dfb8b6b5b>  </fine-tuning-gpt2-for-text-generation-using-pytorch-2ee61a4f1ba7>  # **参考文献**[1] [Pytorch 官网](https://pytorch.org/),脸书【2】[变形金刚 Github](https://github.com/huggingface/transformers) ,拥抱脸[3] [SentenceTransformers 网站](https://www.sbert.net/),无处不在的知识处理实验室[4][sentence transformers Github](https://github.com/UKPLab/sentence-transformers),无处不在的知识处理实验室[5]刘,,等.[“Roberta:一种稳健优化的 bert 预训练方法”](https://arxiv.org/abs/1907.11692) *arXiv 预印本 arXiv:1907.11692* (2019)。# Python 中的半自动探索性数据分析(EDA)> 原文:<https://towardsdatascience.com/semi-automated-exploratory-data-analysis-eda-in-python-7f96042c9809?source=collection_archive---------0----------------------->## 一键式全面数据探索流程![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/f4c8280d277baeeced29e517f59d1bcc.png)EDA 概述(图片作者来自[www.visual-design.net](http://www.visual-design.net)探索性数据分析,也称为 EDA,已经成为数据科学中越来越热门的话题。顾名思义,就是在一个不确定的空间里试错的过程,目标是找到洞见。这通常发生在数据科学生命周期的早期阶段。尽管在数据探索、数据清理或特征工程之间没有明确的定义。EDA 通常位于数据清理阶段之后,特征工程或模型构建之前。EDA 有助于设置模型选择的总体方向,并有助于检查数据是否符合模型假设。因此,执行这一初步分析可以为您的后续步骤节省大量时间。在本文中,我创建了一个半自动的 ed a 过程,它可以分为以下几个步骤:1.  了解您的数据
2.  数据处理和特征工程
3.  单变量分析
4.  多变量分析随意跳转到你感兴趣的部分,或者从[我的网站](https://www.visual-design.net/post/semi-automated-exploratory-data-analysis-process-in-python)获取完整代码,如果你觉得有用的话。# 1.了解您的数据首先,我们需要加载 python 库和数据集。在这个练习中,我使用了来自 Kaggle 社区的几个公共数据集,请使用下面的链接随意探索这些惊人的数据:【2020 年餐饮商业排名[Reddit WallStreetBets 帖子](https://www.kaggle.com/gpreda/reddit-wallstreetsbets-posts)## 导入库我将使用四个主要的库:Numpy——处理数组;熊猫——以我们熟悉的电子表格格式处理数据;Seaborn 和 matplotlib——创建数据可视化。

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np from pandas.api.types
import is_string_dtype, is_numeric_dtype


## 输入数据通过复制数据集的路径从导入的数据集创建一个数据框,并使用`df.head(5)`查看前 5 行数据。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/eb9c7718f3c04d1a65f3148233a975c2.png)“reddit_wsb”数据集结果(图片由作者提供)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/d72df6b16d4f885edea88bb5ee3871d0.png)“餐馆”数据集输出(图片由作者提供)在放大每个字段之前,让我们先鸟瞰一下整个数据集的特征。## 信息()它给出了每列及其数据类型的非空值的计数。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/3f3c3b9b1141d9db7b76756f0880ba95.png)“reddit_wsb”数据集结果(图片由作者提供)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/afd03385a88c0844528098323f2cc3a3.png)“餐馆”数据集输出(图片由作者提供)## 描述( )该函数提供每列的基本统计数据。通过传递参数“include = 'all '”,它输出**值计数、唯一计数、分类变量的最高频率值**和**计数、平均值、标准偏差、最小值、最大值和数值变量的百分比**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/8509958b5c22bdb842aec551612b640f.png)“reddit_wsb”数据集结果(图片由作者提供)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/28def3a853e9aa1faab23eae615f272d.png)“餐馆”数据集输出(图片由作者提供)如果我们让它为空,它只显示数字变量。如您所见,下面只显示了 info()输出中标识为“int64”的列。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/8eb9370c0419141a90bd3fe1471a048b.png)描述“餐馆”数据集的()结果(图片由作者提供)## 漏测值处理缺失值是一个兔子洞,不是一两句话能盖住的。如果您想知道如何处理模型生命周期中的缺失值,并了解不同类型的缺失数据,下面的一些文章可能会有所帮助:</simple-logistic-regression-using-python-scikit-learn-86bf984f61f1>  <https://medium.com/analytics-vidhya/how-to-address-common-data-quality-issues-2cb58a09b225>  在本文中,我们将着重于确定缺失值的数量。`isnull().sum()`返回每列缺失值的数量。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/73b8019c9ed5b66f1f4b116cf6dd97f7.png)“reddit_wsb”数据集结果(图片由作者提供)我们还可以做一些简单的操作,使输出更有见地。首先,计算缺失值的百分比。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/3c9869fc26440f309bfb2e6817646d55.png)“reddit_wsb”数据集结果(图片由作者提供)然后,基于数据框“missing_df”可视化缺失值的百分比。for 循环基本上是一种向条形添加标签的简便方法。从图表中我们可以看到,来自“reddit_wsb”数据集的“body”值有将近一半是缺失的,这就引出了下一步“特征工程”。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/07ae994d6d02e0beae372562bb03e6b1.png)“reddit_wsb”数据集结果(图片由作者提供)# 2.特征工程这是唯一需要一些人类判断的部分,因此不容易自动化。不要害怕这个术语。我认为特征工程是一种奇特的说法,即转换手头的数据,使其更有洞察力。有几种常见的技术,例如,将出生日期转换为年龄,将日期分解为年、月、日和宁滨数值。但是一般的规则是,这个过程应该根据手头的数据和要实现的目标来定制。如果你想了解更多这些技术,我发现这篇文章[“机器学习的特征工程的基本技术”](/feature-engineering-for-machine-learning-3a5e293a5114)在实践中带来了特征工程的整体观点。如果您想了解更多关于特征选择和特征工程技术的信息,您可能会发现这些信息很有帮助:</data-transformation-and-feature-engineering-e5181ef274b5>  </feature-selection-and-eda-in-python-c6c4eb1058a3> [## Python 中的特征选择和 EDAtowardsdatascience.com](/feature-selection-and-eda-in-python-c6c4eb1058a3) 对于“reddit_wsb”数据集,我简单地对现有数据做了三个操作。**1。标题→标题 _ 长度;**

df['title_length'] = df['title'].apply(len)


结果,高基数列“title”被转换成一个数字变量,可以在相关性分析中进一步使用。**2。body → with_body**

df['with_body'] = np.where(df['body'].isnull(), 'Yes', 'No')


由于有很大一部分值缺失,“body”字段被转换为 with_body = "Yes "和 with_body = "No ",因此可以很容易地将其作为分类变量进行分析。**3。时间戳→月份**

df['month'] = pd.to_datetime(df['timestamp']).dt.month.apply(str)


由于大多数数据都是从“2021 年”收集的,因此没有必要对这一年进行比较。因此,我保留了“日期”的月份部分,这也有助于将数据分组为更大的子集。为了简化进一步的分析,我删除了对 EDA 没有贡献的列。

df = df.drop(['id', 'url', 'timestamp', 'title', 'body'], axis=1)


对于“restaurant”数据集,数据已经足够干净,因此我简单地删除了基数较高的列。

df = df.drop(['Restaurant', 'City'], axis=1)


此外,由于单变量分析和多变量分析需要不同的方法来处理不同的数据类型,因此剩余的变量分为数值型和分类型。“is_string_dtype”和“is_numeric_dtype”是识别每个字段的数据类型的方便函数。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/514977a4d966651e4780f65216b82e32.png)“reddit_wsb”数据集结果(图片由作者提供)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/c031cb4c238b4e164adb35be6064c00c.png)“餐馆”数据集输出(图片由作者提供)在最终确定数值和分类变量列表后,可以自动进行单变量和多变量分析。# 3.单变量分析第一部分中提到的 describe()函数已经以非图形方式提供了单变量分析。在本节中,我们将通过可视化数据获得更多见解,并通过图形分析发现隐藏的模式。如果您有兴趣了解哪种图表类型最适合哪种数据类型,请阅读我的文章[“如何选择最合适的图表”](https://www.visual-design.net/post/choose-the-right-chart)。</which-chart-to-choose-4b21929539eb>  **分类变量→条形图**可视化分类变量属性的最简单也是最直观的方法是使用条形图来绘制每个分类值的频率。**数值变量→直方图**要绘制出数值变量分布,我们可以使用直方图,它非常类似于条形图。它将连续的数字分成大小相等的区间,并绘制出区间内记录的频率。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/828ed39f0686eeac962dfcf2acea9332.png)创建直方图和条形图(图片由作者提供)我使用这个 for 循环遍历数据框中的列,并为每一列创建一个图。如果是数字变量,则使用直方图,如果是分类变量,则使用条形图。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/12c3817de84040fd14834c4eb088db83.png)“reddit_wsb”数据集结果(图片由作者提供)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/84d4dadaad644aad9630c5ab49fde1c4.png)“餐馆”数据集输出(图片由作者提供)# 4.多变量分析多变量分析分为这三种情况,以解决数字变量和分类变量的各种组合。## 1.数值与数值→热图或配对图首先,让我们使用**相关矩阵**来查找所有数字数据类型列的相关性。然后使用**热图**来可视化结果。每个单元格内的注释表示关系的相关系数。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/5c2a4666248267cdee99e2b7c16298b1.png)“reddit_wsb”数据集结果(图片由作者提供)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/da834461537b861842698b5c78d33e52.png)“餐馆”数据集输出(图片由作者提供)其次,由于相关矩阵只表示线性关系的强弱,所以最好使用 seaborn 函数 sns.pairplot()来绘制数值变量。注意,sns.heatmap()和 sns.pairplot()函数都忽略非数字数据类型。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/867031dbf9a61d474f89a4738f66d32b.png)“reddit_wsb”数据集结果(图片由作者提供)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/4d5b799c4a32906301e0eb94383db838.png)“餐馆”数据集输出(图片由作者提供)**成对图或散点图**是对相关矩阵的良好补充,尤其是当可能存在非线性关系(如指数、反比关系)时。例如,在餐馆数据集中观察到的“等级”和“销售额”之间的反比关系可能被误认为是强线性关系,如果我们简单地看相关矩阵的数字“- 0.92”的话。## 2.分类与分类→带色调的计数图![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/b292f86a0ca16a87d72ad60eaffd1240.png)样本输出集(图片由作者提供,来自[网站](https://www.visual-design.net/post/semi-automated-exploratory-data-analysis-process-in-python)两个分类变量之间的关系可以使用**分组条形图**可视化。一级分类变量的频率被二级分类分解。这可以通过使用 sns.countplot()来实现。我使用了一个嵌套的 for 循环,其中外部循环遍历所有分类变量,并将它们指定为主要类别,然后内部循环再次遍历列表,将主要类别与不同的次要类别配对。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/161b11371d4f61ff9a71bc997542de25.png)分组条形图代码(图片由作者提供)在一个分组条形图中,如果不同组之间的频率分布总是遵循相同的模式,则表明主要类别和次要类别之间没有相关性。但是,如果分布不同,则表明两个变量之间可能存在相关性。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/115b65693335fad6644be237d60b8a59.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/076eae32a22c8127449aac12d112a4bd.png)“reddit_wsb”数据集结果(图片由作者提供)因为在“餐馆”数据集中只有一个分类变量,所以没有生成图。## 3.分类与数值→带色调的箱线图或配对图![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/f175708420483107a085895e3bb3ade5.png)boxplot 样本输出集(图片由作者提供,来自[网站](https://www.visual-design.net/post/semi-automated-exploratory-data-analysis-process-in-python)**箱线图**通常在我们需要比较数字数据在不同组之间的变化时使用。这是一种直观的方式来图形化地描述分类特征的变化是否导致了值的差异,这可以使用 ANOVA 分析进行额外的量化。在这个过程中,我将分类列表中的每一列与数字列表中的所有列配对,并相应地绘制出箱线图。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/c50f7fdfe97b8dd4ca2a35b57f50798b.png)箱线图代码(图片由作者提供)在“reddit_wsb”数据集中,不同类别之间没有观察到显著差异。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/99e6f8c190ff8d7deaa3242711057084.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/b1288cf42c082cae04b0efaf50f107fa.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/7c3988c29788aa5eb2583ebb6db6738d.png)“reddit_wsb”数据集结果(图片由作者提供)另一方面,“餐馆”数据集给了我们一些有趣的输出。有些州(如“密歇根州”)似乎在情节中跳来跳去。这仅仅是因为这些州的样本量相对较小,值得进一步研究。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/517a8536ff08cc5dd2ff187daef986f2.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/096361fe8dce0dc5146f5042dfac24bd.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/0b73142f815f1595a54811a7a1f6e63e.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/ce154cd80db2b6c02424b768a6c78aa7.png)“餐馆”数据集输出(图片由作者提供)另一种方法是建立在我们之前进行的数值对数值的 **pairplot** 之上的。为了引入分类变量,我们可以用**不同的色调**来表示。就像我们为 countplot 做的一样。为此,我们可以简单地遍历分类列表,并将每个元素添加为 pairplot 的色调。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/e2480af3be426245d2e178c60676a34d.png)带色调代码的 pairplot(图片由作者提供)因此,很容易在散点图中看到每个组是否形成聚类。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/7b0a42c2bd223d59919d0e7f3bbd4aab.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/b721dad0864512f289671380eabdbdab.png)“reddit_wsb”数据集结果(图片由作者提供)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/1ba327643f4e26733505422102c2a416.png)“餐馆”数据集输出(图片由作者提供)希望你喜欢我的文章:)。如果你想阅读更多我关于媒体的文章,请使用这个附属链接([https://destingong.medium.com/membership](https://destingong.medium.com/membership))注册媒体会员。# 带回家的信息本文涵盖了执行 EDA 的几个步骤:1.  了解您的数据:了解数据集的特征。
2.  特征工程:将变量转化为更有洞察力的东西。
3.  单变量分析:1)直方图以可视化数值数据;2)可视化分类数据的条形图。
4.  多元分析:1)数值与数值:相关矩阵、散点图(pair plot);2)分类对比分类:分组条形图;3)数值与分类:带色调的成对图,箱线图。随意从我的[网站](https://www.visual-design.net/code)抓取代码。如前所述,除了特征工程部分,其余的分析可以自动化。然而,当自动化过程伴随着一些人的接触时,它总是更好,例如,试验箱大小以优化直方图分布。一如既往,我希望这篇文章对您有所帮助,并鼓励您尝试使用自己的数据集:)## 更多相关文章<https://link.medium.com/2MOB57Y1lfb>  </simple-logistic-regression-using-python-scikit-learn-86bf984f61f1>  <https://medium.com/analytics-vidhya/top-15-websites-for-data-scientists-to-follow-in-2021-67352092c54d>  *原载于 2021 年 2 月 28 日*[*【https://www.visual-design.net】*](https://www.visual-design.net/post/semi-automated-exploratory-data-analysis-process-in-python)*。*# 用 PyTorch 和 SESEMI 揭开半监督学习的神秘面纱> 原文:<https://towardsdatascience.com/semi-supervised-learning-demystified-with-pytorch-and-sesemi-9656c14af031?source=collection_archive---------18----------------------->## 我们如何利用世界上似乎无穷无尽的未标记数据来帮助我们解决监督学习问题?![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/62ee287a9d51ba18bce6718e0c261417.png)在“[探索监督和半监督学习的自我监督正则化](https://arxiv.org/pdf/1906.10343.pdf)”中使用的“三螺旋”合成数据集的艺术渲染。在开发机器学习解决方案时,最大的障碍一直是数据。像 [ImageNet](https://image-net.org/) 和 [COCO](https://cocodataset.org/) 这样的大规模、干净、完全注释的数据集并不容易获得,尤其是对于利基任务。深度学习尤其如此,随着深度学习越来越多地被采用,这一点将变得更加真实。为了克服标记数据瓶颈,研究人员和开发人员正在开发各种技术,如迁移学习、领域适应、合成数据生成以及许多半监督和自我监督技术。半监督和自我监督技术的世界是一个特别迷人的领域,因为它几乎像魔术一样。我们如何利用世界上似乎无穷无尽的未标记数据来帮助我们解决监督学习问题?事实证明,这些技术比你想象的更容易获得,你可以马上开始应用它们。# 自我监督学习在我们开始之前,让我们先定义一下这些术语的含义。**自我监督学习**本质上是从完全无标签的数据中提取监督信息以创建监督学习任务的实践。基本上,我们创建一个“人工”监督学习任务,它具有以下特性:*   它鼓励网络学习关于数据的语义上有用的信息。
*   它的标签可以从数据扩充中导出。一种最简单但仍然非常有效的从未标记数据中学习的技术来自 Gidaris 等人的一篇名为“[通过预测图像旋转进行无监督表示学习](https://arxiv.org/abs/1803.07728)的论文。这种辅助任务非常直观:给定一幅输入图像,随机将其旋转 0、90、180 或 270 度。然后训练您的模型来预测标签“0”、“90”、“180”或“270”。这里的直觉是,随机旋转图像引入了标签,同时保留了有用的语义信息。例如,给定一张旋转了 90 度的狗的图片,一个人可以清楚地知道图片被旋转了,因为狗不会站在墙上!然而,有些东西,如苍蝇、窗户和油画,确实是挂在墙上的。图像中的语义信息包含关于其旋转的线索。理论是,通过试图预测图像的方向,模型必须学习关于对象的预期方向的信息,从而隐含地学习那些对象的语义特征,然后可以应用于其他任务。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/70fbda5b80a89228706b10cc21955dea.png)这些图像的内容包含关于它们旋转角度的线索。例如,第一幅图像中的鸟看起来是横着的,表明图像可能逆时针旋转了 90 度。自我监督可以采取多种形式,例如图像的修补、着色和超分辨率,视频的帧预测,以及 NLP 中的单词或序列预测。想要深入了解自我监督学习的世界,请看由[阿米特·乔德里](https://twitter.com/amitness)撰写的[这篇博文](https://amitness.com/2020/02/illustrated-self-supervised-learning/)。# 半监督学习**半监督学习**是使用标记和未标记数据来训练任务的实践。半监督学习技术通常在两个任务上交替训练,首先是应用于标记数据的标准监督任务,然后是利用未标记数据和某种数据扩充的辅助任务。一个这样的辅助任务可能是预测图像旋转,就像我们之前讨论的那样。在许多情况下,半监督学习本质上就像将自我监督训练和监督训练一起应用。一个特别值得注意,但有些不同的例子是来自谷歌研究论文的 [FixMatch](https://arxiv.org/abs/2001.07685) 。这里的辅助任务其实和目标任务是一样的:给定一个图像,预测它的类别。然而,我们不是用标记的数据来解决这个任务的。取而代之的是,未标记的输入图像被增强两次:一次以弱的增强量(例如翻转移位),第二次以强的增强量。该模型然后预测弱增强图像和强增强图像的标签。弱增强图像的预测(如果它通过了置信度阈值)用作伪“地面真实”标签,由此我们评估强增强图像的标签。这里的目标是鼓励网络变得对极端增强更健壮,并鼓励它学习更强的特征,而不需要技术上的标记数据。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/566b022b59c1ff74eb1729c2ece8166b.png)FixMatch 的图表。输入图像(顶部)被弱增强并生成模型预测。如果预测超过给定的阈值,则分配一个“伪标签”。然后,对同一图像应用强增强,并生成另一个预测。固定匹配损失函数鼓励该预测和先前的“伪标签”之间的一致本文应用了一种最新的半监督学习技术,称为**一致性正则化**。首先对您的数据应用增强,保留其*语义内容*(即“这张猫的照片看起来还像猫吗”)。然后,通过明确的监督或损失函数中惩罚对这些变化的敏感性的条款,鼓励你的网络对非语义的增强变得有弹性。通过利用数据扩充,你可以通过自我监督将几乎任何监督学习任务变成半监督任务。## 塞斯米在这篇文章的其余部分,我们将专注于一种叫做 [SESEMI](https://github.com/FlyreelAI/sesemi) 的特定技术,这是一种由 [Flyreel AI Research](https://www.flyreel.co/) 发布的半监督训练技术。论文中描述的[想法与图像旋转任务非常相似,除了一些关键的区别。首先,他们为水平和垂直镜像添加了两个额外的类。更重要的是,这种技术联合训练被监督的和自我监督的目标,将自我监督的目标框定为一种自我监督的正则化形式。因此,损失函数看起来像一个典型的正则化损失函数,只是加权项和未加权项都是应用于不同数据子集的标准交叉熵损失。](https://arxiv.org/pdf/1906.10343.pdf)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/d3245b84b548651749958015186eb44c.png)SESEMI 损失函数由监督项和自监督项组成,并用权重项 *w* 进行平衡。最左边的项(未加权)是应用于标签的标记子集和 softmax 预测向量的损失。最右边的(加权)项是应用于具有我们的辅助标签及其 softmax 预测向量的未标记数据子集的损失。这两项都是标准的交叉熵损失。这种技术的优雅之处在于它的简单。与其他需要大量调整超参数的最新一致性正则化技术不同,SESEMI 需要的唯一超参数是两个损失项之间的权重,作者建议将其设置为 1.0。使用这种技术不需要专门的理论或损失函数或自定义模型层。任何对深度学习技术有入门级熟悉的人都可以成功地应用这种技术。卷积神经网络?检查。范畴交叉熵?检查。数据增强?检查。你只需要知道这些。我提供了一个简单明了的 [Google Colab 笔记本](https://colab.research.google.com/drive/1b1sbKo3mb16e_yBgfELMVT_kBAWtYn9f?usp=sharing),在这里你可以很快尝试一下这个技巧。在这篇文章中,我使用`torchvision`中方便的预训练 ResNet 模型将 SESEMI 技术应用于 CIFAR-10 数据集。它唯一的主要依赖是“火炬”和“火炬视觉”,所以你可以马上开始。这篇文章的其余部分将在笔记本中继续。下表简要总结了您可以实现的结果。它们说明了 SESEMI 技术可以带来多大的不同,特别是对于非常小的数据集,同时只需要很少的代码修改,几乎不需要额外的超参数调整。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/2c2447c921b5df5071f98149b89e337d.png)使用全监督和 SESEMI 技术对不同数量的标记图像测试分类准确性。为了进一步探索 SESEMI 技术,我鼓励您在 GitHub 上测试一下[作者的代码。感谢阅读,继续学习!](https://github.com/FlyreelAI/sesemi)# 半监督学习——如何使用标签传播算法分配标签> 原文:<https://towardsdatascience.com/semi-supervised-learning-how-to-assign-labels-with-label-propagation-algorithm-9f1683f4d0eb?source=collection_archive---------2----------------------->## [实践教程](https://towardsdatascience.com/tagged/hands-on-tutorials),机器学习## 半监督机器学习是如何工作的,如何在 Python 中使用?![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/98cdd96383b623fda03448e60f4d5414.png)半监督学习-标签传播算法。图片由[作者](https://solclover.com/)提供。# 介绍尽管我们周围有大量的数据,但其中绝大多数是非结构化的和未标记的。同时,我们的很多机器学习应用,比如分类模型,都需要我们有目标标签。不幸的是,我们可能并不总是有资源来检查成千上万的观察结果并手动分配标签。但是如果我们不需要这么做呢?如果我们可以用很小一部分的例子自动标记大量的数据,会怎么样?我给你介绍一下**半监督学习!**# 内容*   什么是半监督学习?
*   标签传播算法在机器学习领域中处于什么位置?
*   标签传播工作原理的直观解释
*   在 Python 中使用标签传播的示例# 什么是半监督学习?通常,我们会使用具有特定目标变量的数据(标记数据)来构建监督模型(例如,分类、回归)。或者,当我们没有标记的数据时,我们将建立无监督的模型(例如,聚类、维度减少)。然而,有时我们可能会发现自己处于有少量已标记数据和大量未标记数据的情况。这就是半监督学习可以帮助的地方,因为它结合了监督和非监督技术的元素。## 例子让我们考虑一个例子。假设你有 10000 句带有用户评论的句子,你想把它们分为正面和负面。不幸的是,您只有 50 个句子,您之前已经手动分配了标签(正面、负面)。除非您想花更多的时间来标记其余的数据,否则您的选择是:*   **使用 50 个带标签的例子建立一个监督模型**—由于可用样本数量少,这可能会导致模型表现不佳。
*   **用未标记的数据建立一个无监督的模型**,把例子分成两个组。然而,数据可能自然地想要形成多个更小的集群,而将它们强制分成两个组可能不一定会将它们在预期的目标(正/负)之间分开。
*   **使用所有标记和未标记的数据构建半监督模型**—这将使用 50 个例子来标记其余的数据,并在构建监督情绪预测模型时为您提供一个更大的数据集。我相信你现在很想知道这是如何工作的。因此,让我们仔细看看一种称为标签传播的特定算法。# 机器学习算法领域中的标签传播你已经知道,我们将深入机器学习的半监督分支下的标签传播算法。然而,在陷入其中的一个角落之前,后退一步,想象 ML 模型的大千世界总是有益的。下面是我对一些最流行的机器学习算法进行分类的尝试。旭日图是**交互式**,所以一定要点击👇在不同的类别上对**进行放大并揭示更多的**。机器学习算法分类。由[作者](https://solclover.com/)创作的互动图表。***如果你喜欢数据科学和机器学习*** *,请* [*订阅*](https://solclover.com/subscribe) *每当我发表一个新的故事,你都会收到一封电子邮件。*# 标签传播工作原理的直观解释标签传播是一种相对简单的算法,它基于更接近的数据点具有相似的类标签的假设。因此,我们可以通过密集的无标签数据区域传播这些类别标签。该算法遵循迭代方法,我们可以将其描述为以下步骤的集合:1.  **通过绘制不同节点(数据点)之间的边(链接)创建一个连通图**。请注意,在大型数据集上创建一个全连通图可能需要大量的机器资源。因此,限制想要连接在一起的邻居数量*(参见 Python 示例部分中的 n _ neighbors)*通常是有益的。
2.  **确定每条边的权重**,其中距离数据点较近的边权重较大(连接较强),距离数据点较远的边权重较小(连接较弱)。较大的边权重使标签更容易通过,从而增加了传播特定标签的可能性。
3.  **从每个未标记的点执行随机行走**以找到到达标记点的概率分布。这种随机行走由许多迭代组成,并且持续到达到收敛,即,所有路径都已被探索,并且概率不再改变。未标记的点基于上述过程找到的概率被分配新的标签。请注意,原始标记点永远不会改变,因为它们的标签是固定的。这里有一张 gif 图片,让你直观地看到标签是如何在网络中传播的。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/277c636085c041f7ea62344251a0cbcb.png)标签传播算法的应用。Gif by [作者](https://solclover.com/)。我设计了上面的例子来展示半监督方法比使用监督或无监督方法更有优势的场景。请注意,我们在开始时有三个带标签的样本(见下图)。根据这些信息,我们可以推断红色标签可能位于中间,蓝色标签位于外侧*(尽管拥有更多已知标签以确保我们的推断正确总是有益的)*。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/431a841563c56c5dd9695e91b35e4957.png)具有三个已知标签的示例数据。图片由[作者](https://solclover.com/)提供。在这种情况下,监督模型将很难绘制决策边界,因为它没有上下文的未标记数据。与此同时,一个无监督的模型也不会做得很好,因为没有两个明确定义的聚类来区分红点和蓝点。如 gif 图像所示,标签传播算法能够通过网络向外传播标签,充分利用**整个数据**(有标签和无标签)。标签传播算法的另一个重要方面是,在算法完成运行后,除了硬标签之外,我们还可以查看相应的概率。因此,如果我们对算法确定的边界不满意,我们可以手动调整阈值并重新标记一些点。参见下面的交互式 3D 图,它显示了属于红色标签(标签 1)的概率,旁边是我们在上面的图片和 gif 图像中看到的两个维度(维度 1、维度 2)。标签传播结果。互动 3D 图表作者[作者](https://solclover.com/)。如您所见,硬标签是基于属于特定类别的概率分配的,阈值为 0.5。然而,该模型对于更靠近边界的点不太确定。因此,如果我们愿意的话,我们可以上下移动门槛,对边缘案例进行重新分类。[![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/1b3d3abe50e10bf8f8217750c717e6f9.png)](https://solclover.com/membership)[![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/60fb21d1cb2701bfb6b71f61c99403e2.png)](https://www.linkedin.com/in/saulius-dobilas/)# 在 Python 中使用标签传播的示例现在让我们抛开理论,使用真实数据和标签传播算法。我选择的数据有适用于所有观察的标签。因此,在通过标签传播算法发送数据之前,我们将屏蔽其中的许多标签,然后使用实际标签来评估模型的执行情况。## 设置我们将使用以下数据和库:*   [来自 Kaggle 的营销活动数据](https://www.kaggle.com/rodsaldanha/arketing-campaign)
*   [Scikit-learn library](https://scikit-learn.org/stable/index.html)for1)特征缩放([minmax scaler](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.MinMaxScaler.html#sklearn.preprocessing.MinMaxScaler));2)进行标签传播([标签传播](https://scikit-learn.org/stable/modules/generated/sklearn.semi_supervised.LabelPropagation.html));3)模型评估([分类 _ 报告](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.classification_report.html)、[混淆 _ 矩阵](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.confusion_matrix.html)、[混淆矩阵显示](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.ConfusionMatrixDisplay.html))
*   用于数据可视化的 [Plotly](https://plotly.com/python/) 和 [Matplotlib](https://matplotlib.org/)
*   用于数据操作的[熊猫](https://pandas.pydata.org/docs/)和[熊猫](https://numpy.org/doc/stable/)第一步是导入我们上面列出的库。接下来,我们下载并摄取营销活动数据(来源: [Kaggle](https://www.kaggle.com/rodsaldanha/arketing-campaign) )。这次我们将只利用两个特性来构建一个连通图并传播标签。因此,我将摄取限制在几个关键列,而不是读取整个表。此外,您将看到,我们已经派生了一些创建带有屏蔽标签的目标变量所需的附加字段。下面的代码片段显示了目标变量的数据和分布。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/3fb167b1014bb46c0477a2ec2757ecb5.png)来自 [Kaggle](https://www.kaggle.com/rodsaldanha/arketing-campaign) 的营销活动数据。图片由[作者](https://solclover.com/)提供。注意,我们保留了 15%的实际标签(1 和 0)并屏蔽了剩余的 85% (-1)。因此,我们的目标包含关于购物者是否有任何依赖项(1),没有任何依赖项(0),或者该信息被屏蔽(-1)的信息。我们现在将试图给那些 85%被掩盖的观察分配一个标签。## 执行标签传播下一段代码由几个步骤组成,帮助我们准备数据、拟合模型和打印结果。我们将使用购物者每年花在葡萄酒和肉类产品上的钱作为两个维度来创建一个关联图,并推断他们在家里是否有任何家属。结果如下:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/f7ff44ee84f57b197bbb96c32e410e7d.png)标签传播结果。图片由[作者](https://solclover.com/)提供。正如您所看到的,我们已经相对成功地以 83%的模型准确度推断标签*(为了使评估公平,我们只使用带有屏蔽标签的记录进行模型性能评估)*。这是一个相当好的结果,因为我们只使用了 15%的观测值。然而,我们可以通过增加已知标签的数量或探索连通图的额外维度来进一步改进它。为了更好地可视化,让我们将结果绘制在 3D 图上。在此图中,颜色代表真正的标签:*   红色=1,即有依赖项
*   黑色=0,即没有依赖项。因此,图表上半部分的红点和下半部分的黑点代表正确识别的标签。同时,上半部分的黑点和下半部分的红点代表错误识别的标签。*注意,* d *概率=0 或概率=1 的数据点是模型训练中使用的标签已知的数据点。*营销数据上的标签传播结果。互动 3D 图表作者[作者](https://solclover.com/)。# 结论在标记数据稀缺的情况下,半监督学习和标记传播可以提供巨大的帮助。但是,应该谨慎使用它,并且应该首先使用已知的标签对其进行测试,以确保该方法适合您的数据。如果数据倾向于形成聚类,并且这些聚类中有一致的标签,那么它通常会工作得很好。我希望您喜欢阅读这篇文章,并学到一些新的实用知识,以帮助您踏上数据科学之旅!如果您有任何问题或建议,请随时联系我们。干杯👏
**索尔·多比拉斯*****如果你已经花光了这个月的学习预算,下次请记得我。*** *我的个性化链接加入媒介是:*<https://solclover.com/membership>  您可能感兴趣的其他文章:</umap-dimensionality-reduction-an-incredibly-robust-machine-learning-algorithm-b5acb01de568>  </lda-linear-discriminant-analysis-how-to-improve-your-models-with-supervised-dimensionality-52464e73930f> # 半监督学习变得简单> 原文:<https://towardsdatascience.com/semi-supervised-learning-made-simple-141be294880c?source=collection_archive---------9----------------------->## 了解如何在 PyTorch 中从头开始构建自己的半监督模型半监督学习是一种从标记和未标记数据中获取有用信息的机器学习技术。在本教程中:*   你将学习什么是监督、无监督、半监督和自我监督学习。
*   一步一步地完成 BYOL 的 PyTorch 代码,这是一种半监督学习方法,你可以在 Google Colab 中自己实现和运行,不需要云或 GPU!
*   你将学习 BYOL 背后的一个基本理论——半监督学习方法。在学习本教程之前,您应该对使用 PyTorch 对图像进行监督学习有基本的了解。# 什么是半监督学习,我们为什么需要它?一般来说,机器学习方法可以分为三类:*   监督学习
*   无监督学习
*   强化学习这里我们将省略强化学习,集中讨论前两种类型。在**监督**学习中,我们的数据由带标签的对象组成。机器学习模型的任务是学习如何给对象分配标签(或值)。> 例如:
> 1)医院有标有 [ICD-10](https://en.wikipedia.org/wiki/ICD-10) 代码的心电图读数。根据心电图读数,我们希望自动对患者进行预诊断。银行有关于债权人的数据——他们的财务状况、他们拥有多少资产、他们是否按时还款等等。银行想评估他们还能借给某人多少钱。相反,**无监督的**学习只处理未标记的对象。> 示例:我们可以让计算机将图像聚类成 10 个类别,而无需指定这些类别的含义( [k-means 聚类](https://en.wikipedia.org/wiki/K-means_clustering))。**半监督**学习介于这两者之间:一些对象被标记,但大多数没有。未标记数据的优势来自于标记数据通常是资源密集型的事实。> 例如:我们有一个包含推文的数据集。其中一些被标注了积极、消极或中性的[情绪](https://en.wikipedia.org/wiki/Sentiment_analysis)。不幸的是,注释是时间和成本密集型的——我们需要支付注释者这样做的费用,还要交叉检查他们的答案是否正确。因此,大多数推文都没有被贴上标签,因为下载它们相对便宜且容易,但给它们加注释并不便宜。还有另外一种学习:**自我监督**。当我们想出一些我们不一定想要解决但可能成为模型学习的借口的监督任务时,我们可以谈论自我监督学习。自监督学习通常属于无监督学习的范畴,用于增强监督学习。> 示例:假设我们有一个包含未分类图像的大型数据集。我们希望学习一个模型,从这些图像中提取一些有用的特征,这些特征可以帮助我们完成其他任务(如猫/狗识别)。我们在图像上随机应用 9 种不同的变形(或者不变形,所以有 10 种可能性)。然后,我们让一个模型去识别应用了哪种失真(如果有的话)。这样,我们希望模型将学会提取特征,然后可以在其他地方重用(如猫/狗识别)。# STL-10——半监督学习的基准数据集在我们进入方法之前,让我们看一下我们将使用的数据集。 [STL-10 数据集](https://cs.stanford.edu/~acoates/stl10/)由斯坦福大学的研究人员创建,灵感来自 CIFAR-10,你可能听说过。STL-10 由 100,000 个未标记图像和 5,000 个用于训练的标记图像以及 8,000 个用于测试的图像组成。图像平均分布在十个类中。打开 [Google Colab](https://colab.research.google.com/) 并创建一个具有 GPU 环境的新笔记本。首先,为了方便起见,安装一个 Google Drive。STL-10 很重,每次运行环境时重新下载可能不方便。在单元格中运行下面的代码,并按照说明操作。在 Google Colab 中安装 Google Drive然后,为 STL-10 数据集创建一个文件夹。为迷你项目和 STL-10 数据集创建一个文件夹/目录下载 STL-10 数据集。如您所见,我们也在这里定义了转型。我们这样做是因为,默认情况下,所有图像都是 [PIL 图像对象](https://pillow.readthedocs.io/en/stable/reference/Image.html),这对神经网络来说不是很方便。因此我们把它们转换成张量。下载数据集并将其加载到变量中我们还应该为这些数据集定义数据加载器。试着自己去做!填写缺失的代码部分。为 STL-10 数据集创建数据加载器太好了!正如您所看到的,批量大小被设置为 128—这个值是我通过实验获得的,作为一个不会使 Google Colab 环境崩溃的值,但是您可以随意进行实验。# 获得受监督的基线首先,我们需要获得一个只有监督学习的基线,以便与半监督学习进行比较。使用下面的代码。如果你需要这段代码的解释,请在评论中告诉我。监督学习基线# 引导你自己的潜能BYOL(bootstrap your own latent)是一种自我监督的表征学习方法,于 2020 年 1 月首次发表,随后在顶级科学会议上发表。我们将实现这个方法。## 粗略的概述BYOL 有两个网络——在线网络和目标网络。他们互相学习。我们拍摄一幅图像,并对其执行两种不同的放大操作( *t* 和*t’*)。一个增强图像( *v* )被放到在线网络,并且第二个增强图像(*v’*)被馈送到目标网络。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/0d6aaa2d181e670d2cbd2a3d9cba98c3.png)图 1 —架构概述(基于 BYOL 论文的图 2;作者在 Lucidchart 中创建的图像)在线网络将返回一些伪预测(它是*伪*,因为我们在这里没有实际的标签要预测)和目标网络—投影。两个输出都需要精确的尺寸。目标网络的输出将作为我们的地面实况。我们计算这些网络输出之间的均方误差。然后,我们通过在线网络执行反向传播,但暂时离开目标网络。通过这样做,在线网络学会预测目标网络的输出。反向传播后,用在线网络参数的移动指数平均值更新目标网络。稍后我们会详细说明它的含义。在线网络从目标网络“快速”学习,目标网络从在线网络“缓慢”学习。在线网络试图尽可能接近目标网络的输出。这种机制背后的直觉是,这两个网络的输出应该是相似的——它们都获得相同的图像,但具有不同的增强。如果我们有一张猫的图像,不管我们如何预处理它(在某种合理的程度上),它仍然是一张猫的照片。在线网络学习目标网络对于图像中相同对象但具有不同“展示”的投影。在最后,在线网络的部分(编码器, *fθ* )将被取出并用于监督学习。如果你没有完全理解这个解释,不要担心——我们将一步一步地进行,所以你将有机会学习这个。## 对比学习同样值得注意的是,这个架构是**对比学习**的一个例子。对比学习是一种技术,在这种技术中,我们试图获得尽可能相似的相似物体的表征(嵌入),但尽可能不同的不同图像的表征。你可以在[这个中帖](/understanding-contrastive-learning-d5b19fd96607)里了解更多。BYOL 最重要的区别是这种方法没有负对。有了这样的改进,它的计算变得更加简单,因此可以在免费的 Google Colab 环境中演示。</understanding-contrastive-learning-d5b19fd96607>  ## 增加如前所述,对图像执行了两种不同的放大。更具体地说,我们从两个不同的分布 t ~τ和 t′~τ′中抽取两个变换。发表在 NIPS proceedings 上的论文没有详细说明这些增强,但是发表在 arXiv [上的预印本说明了这些增强。](https://arxiv.org/abs/2006.07733)如果你想了解最新的方法,你应该能够阅读科学论文。因此,我建议您阅读预印本的 B 部分(第 16–17 页),并实现函数中缺失的代码。由于缺少信息,文章中的一些值未被采用。`[torchvision.transforms](https://pytorch.org/vision/stable/transforms.html)`的文档应该会有帮助。图像增强代码## BYOL 建筑的骨架让我们再看一下图 1。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/0d6aaa2d181e670d2cbd2a3d9cba98c3.png)图 1 —架构概述(基于 BYOL 论文的图 2;作者在 Lucidchart 中创建的图像)所以我们有图像(STL-10 数据集)和增强。其余的呢?编码器( *f* )可以是将给定图像转换成特征(表示)的任何网络,例如 resnet18。投影( *g* )负责从表示网络(编码器)的输出中创建更小的表示。预测层根据投影进行伪预测。请注意,这种架构是不对称的。作者假设它防止崩溃的解决方案(例如,为每个图像输出相同的向量,将给出 MSE =0)。因此,预测图层需要具有相同的输入和输出维度,以便计算目标投影网络的输出和在线预测网络的输出之间的 MSE。BYOL 类的初始化方法## 实现投影仪正如你在上面看到的,`BYOL.mlp`应该返回投影仪和预测器。那我们就这么做吧。第 3.3 节*实施细则*中的预印本规定:> [……]表示 y 被多层感知器(MLP) gθ投影到更小的空间,对于目标投影 gξ也是如此。此 MLP 包含一个输出大小为 4096 的线性图层,随后是批量归一化、校正线性单位(ReLU),以及一个输出大小为 256 的最终线性图层。这意味着:投影仪和预测器用 MLP## 拟合模型我们将拟合模型到未标记数据分为四个步骤:*   在未标记数据上训练(拟合)模型
*   验证未标记的训练数据
*   对验证数据进行验证(标签将被省略)
*   打印结果以上所有步骤将重复`epochs`次。训练和验证循环## 向前和向后传播现在我们将关注代码中最关键的部分——在`train_one_epoch`中的自我监督学习。请记住,您可以查看图 1,并将其与代码进行比较。首先,我们必须将两个网络都设置为训练模式。将网络设置为训练模式然后我们需要迭代`DataLoader`返回的批次,放入 GPU。将张量放入 GPU正向传递将在一个单独的函数中实现,因为我们将在验证过程中重用它([干规则](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself))。执行向前传球我们对损失张量进行反向传播…添加了反向传播…并更新目标网络的参数。在正向传递中更新目标网络我们来看一下`forward`的方法。首先,我们需要用两种不同的增强函数来增强图像。我们使用`torch.no_grad()`,因为我们不想通过这些转换执行反向传播。两个不同的增强图像被保存到`v`和`v_prime`变量中。批量数据上的图像增强图像`v`被送入在线网络,该网络返回伪预测。请注意,我们在这里不使用`torch.no_grad()`,因为我们将在这个网络上进行反向传递。图像`v_prime`通过`torch.no_grad()`输入目标网络。两个输出都是`[normalize](https://pytorch.org/docs/stable/generated/torch.nn.functional.normalize.html)` d 和…带有输出标准化的目标和在线网络的前向传递…并计算这些输出的均方误差(或者更确切地说,是我们在`__init__`中设置`sum`减少时的*总和*均方误差)。损失的计算该文件还指出:> 我们通过分别向在线网络提供 v’和向目标网络提供 v 来计算[loss],从而对称化损失[…]。下面的代码引入了这种对称损失。对称损失函数现在我们只剩下更新目标网络了。还记得`__init__`中定义的`self.tau`吗?这是一个衰减参数。目标网络的参数 *ξ* 在第 *i* 步用在线网络的参数 *θ* 更新:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/fb4b7f3df8d21f7d9ec892aebd1bbfb7.png)这个等式你可能已经很熟悉了。它定义了在每个步骤(批次)中更新的一系列参数 *θ* 的**指数移动平均值**。它用于 [**指数平滑**](https://en.wikipedia.org/wiki/Exponential_smoothing)**——我们平滑时间序列的过程。在这种情况下,目标网络的参数“消除”在线网络中参数的“快速”变化。****用在线网络的指数移动平均值更新目标网络****这是`BYOL`类的最终代码,也包括验证过程和运行训练的代码。我们将只运行一个时期的自我监督学习,因为在 Google Colab 上这样做通常需要一个小时。你可以把`train_loss = self.validate(train_dl)`改成`train_loss = 0`来节省一些时间。****resnet18 的最后一层被替换为`Identity`层——这样,我们将获得该网络提取的特征,而不是对 1000 个类别的预测。****完整的 BYOL 等级代码及其用法**# **半监督学习****现在,让我们把自我监督学习和监督学习结合起来。首先,我们从 BYOL 类中取出在线编码器( *fθ* )并创建一个副本。因为我们想要预测十个类,我们将用`Linear`替换最后的`Identity`层。如果要冻结网络的编码部分,可以通过取消代码注释来实现。****使用在未标记数据上训练的 resnet18 的半监督学习**# **结果****现在,我们用[色盲友好型托盘](https://davidmathlogic.com/colorblind/#%23FFC20A-%230C7BDC)绘制图表,比较监督和半监督学习的性能。****代码生成学习曲线****正如你在下面看到的,与监督学习相比,半监督学习获得了稍微好一点的结果。****![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/9971e4cd6c5da5a5125d0129a16b952c.png)****![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/9bd42bf371a233ef12bae4f74543b449.png)图 2——监督学习和半监督学习的性能比较。由于 DataLoader 中参数初始化和批次洗牌的随机性,您的结果可能会有所不同。作者创造的形象。** # **结论****我们从只使用有标签的数据进行监督训练转变为利用无标签的数据进行自我监督和半监督学习。正如你所看到的,我们在结果上没有得到显著的差异,但我们仍然表明,在某些情况下,使用半监督学习可以改善结果。****我鼓励你尝试这段代码——也许改变优化器,τ(*τ*),编码器架构?如果你有一些令人兴奋的发现或者这篇文章对你的用例有帮助,请留下评论。我想听听这件事。****感谢您阅读本教程。如果你喜欢它,请在 Medium 上关注我——它将帮助我发展我的博客并继续我的工作。非常感谢您的评论、反馈和新想法!**# 半监督机器学习解释> 原文:<https://towardsdatascience.com/semi-supervised-machine-learning-explained-c1a6e1e934c7?source=collection_archive---------21----------------------->## 机器学习的另一种方式![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/a8323120504b116f42bd59b3417fd95f.png)约翰·汤纳在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片机器可以通过各种方式学习。监督学习是一种机器学习问题,涉及基于示例输入-输出对学习输入到输出的映射函数。无监督学习涉及从未标记数据中学习模式。半监督学习可以被视为监督学习和非监督学习的混合。本质上,当我们在训练的时候把少量的有标签的数据和大量的无标签的数据结合起来,我们就有了一个半监督的机器学习问题。根据维基百科,半监督学习可能被描述为弱监督的特例[ **来源** : [维基百科](https://en.wikipedia.org/wiki/Semi-supervised_learning) ]。> 弱监督是机器学习的一个分支,它使用有噪声、有限或不精确的源来提供监督信号,以便在监督学习环境中标记大量训练数据— [维基百科](https://en.wikipedia.org/wiki/Weak_supervision)。## 数据问题监督学习模型和技术在商业中很常见。然而,建立有效的模型高度依赖于获得高质量的标记训练数据——我们都听说过“垃圾进,垃圾出”的说法。当企业试图使用机器学习来解决问题时,对高质量标记数据的需求通常会导致一个主要障碍。这个问题表现在几个方面:*   **标记数据数量不足** —当提出新产品或新行业出现时,他们面临的一个常见问题是缺乏标记训练数据来应用传统的监督学习方法。通常,数据科学家会获得更多数据,但这种情况下的问题是,如果不等待时间的流逝来积累数据,这样做可能不切实际、成本高昂或不可能。
*   **没有足够的领域专业知识来标记数据** —有些问题可以被任何人标记。例如,大多数人可以标记猫和狗的图像,但当标记图像中猫或狗的品种时,这个问题变得更具挑战性。让领域专家来标记训练数据可能会很快变得昂贵,因此它通常不是一个可行的解决方案。
*   **没有足够的时间来标记和准备数据** —众所周知,60%或更多的时间花在机器学习问题上,专门用于准备数据集。当在处理快速发展的问题的领域中工作时,收集和准备数据集以足够快地构建有用的解决方案可能是不切实际的。总的来说,收集高质量的标记数据会很快对一个人的资源提出沉重的要求,一些公司可能没有这些资源来满足需求。这就是半监督学习发挥作用的地方。如果我们有少量的标记数据和大量的未标记数据,那么我们可以将我们的问题框架为半监督机器学习问题。## 半监督学习的类型当我们在处理一个半监督问题时,目标会根据我们希望执行的学习类型而变化。我们可以将半监督学习用于归纳学习、直推式学习或两者的对比。**归纳学习**归纳学习的目标是归纳新数据。因此,归纳学习指的是建立一种学习算法,从标记的训练集学习并推广到新数据。**直推式学习**直推式学习的目标是将来自已标记训练数据集的信息转换成可用的未标记(训练)数据。## 最后的想法半监督学习介于监督学习和非监督学习之间。我们可以将半监督学习作为归纳或直推学习来进行,并且这两种学习都可以使用半监督学习算法来执行。其实现方式超出了本文的范围,但是感兴趣的读者可能想阅读:*   [半监督学习](http://www.acad.bg/ebook/ml/MITPress-%20SemiSupervised%20Learning.pdf)
*   [半监督学习简介](https://www.amazon.co.uk/Introduction-Semi-Supervised-Synthesis-Artificial-Intelligence/dp/1598295470)感谢阅读!如果你喜欢这篇文章,请通过订阅我的免费**[每周简讯](https://mailchi.mp/ef1f7700a873/sign-up)与我联系。不要错过我写的关于人工智能、数据科学和自由职业的帖子。**## **相关文章****</the-difference-between-classification-and-regression-in-machine-learning-4ccdb5b18fd3>  </unsupervised-machine-learning-explained-1ccc5f20ca29>  </5-books-you-can-read-to-learn-about-artificial-intelligence-477b5a26277d> **# 将云 SQL 数据发送到 BigQuery> 原文:<https://towardsdatascience.com/send-cloud-sql-data-to-bigquery-2603489a4330?source=collection_archive---------15----------------------->## 将云 SQL 数据移动到 BigQuery 的自动化数据管道![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/d5f60da9d269ead0cf174aaf4bc1d28b.png)来源 [pixabay](https://pixabay.com/photos/assassin-spray-injection-fatal-3690300/) ,作者[迪侯](https://pixabay.com/users/dimhou-5987327/)**Cloud SQL** 是谷歌云平台的一项数据库服务,可以轻松设置、维护和管理关系数据库。但是,它不是为繁重的分析操作而构建的。你需要一个仓库服务,比如 **BigQuery** 来完成高级分析和机器学习任务,比如[使用 BigQuery ML](/rfm-analysis-using-bigquery-ml-bfaa51b83086) 执行 RFM 分析。为此,您需要将您的云 SQL 数据移动到 BigQuery。在本指南中,我们将构建一个数据管道,以自动方式将云 SQL 数据发送到 BigQuery。# 数据管道对于我们的数据管道,我们将使用两个 GCP 组件
1。联邦查询
2。查询调度程序这就是数据流动的方式![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/5dc409dc175a71eac18c32f5761c057f.png)数据流插图,作者 Muffaddal查询调度器将执行一个联邦查询,该查询将获取 BigQuery 环境中的 SQL 数据,并将其附加到 BigQuery 表中。*如果你想在 GCP 了解更多关于数据流水线的知识* [*这里有一门极好的课程*](https://bit.ly/2X4kefM) *可以开始。*# 云 SQL 到 BigQuery 的连接在我们详细讨论如何将云 SQL 数据迁移到 BigQuery 之前,我们必须首先建立两个组件之间的连接。请按照以下步骤操作。## 1- BigQuery 连接 API启用 Bigquery 的连接 API,使其能够连接到云 SQL。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/7cacc05ebd42708e593651b1635f6dbd.png)Muffaddal 的 BigQuery 连接 API## 2 云 SQL 公共 IP为了让 BigQuery 连接到 SQL,我们必须确保云 SQL 设置为公共网络。转到您的 SQL 实例>连接选项卡,并启用公共 IP >点击保存![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/0aa43d9390338c66661b0cc5fc84e01a.png)使实例公开,由 Muffaddal即使你的实例是公共的,它仍然无法从公共互联网访问,因为你没有提供一个授权的地址。## BigQuery 中的 3 云 SQL 连接转到 BigQuery 接口并添加外部数据源。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/b7878a0306e37bfff7d1231e31faca71.png)通过 Muffaddal 创建外部连接的步骤这将打开一个提示,要求输入关于 SQL 的详细信息。输入适当的信息以建立与云 SQL 的连接。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/b44f6015700c6ff84783ddc2c0cb167e.png)SQL 详细信息提示,按 Muffaddal## 4-管理连接接下来,为用户提供访问这个新添加的外部数据库的电子邮件地址和角色。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/4b77a3782949685066023d6f459203c2.png)共享连接按钮,由 Muffaddal这也让另一方可以使用 BigQuery 中的外部数据源。完成以上工作后,我们就可以将云 SQL 数据导入到 BigQuery 中了。# 联邦查询联邦查询使我们能够实时查询驻留在云 SQL 中的数据,而无需将数据直接复制或移动到 BigQuery。它支持云 SQL 中的 MySQL(第二代)和 PostgreSQL 实例。它使用函数`EXTERNAL_QUERY`来完成这个任务。这需要两个参数。首先是实例 Id,其次是实际的 SQL 查询。> 外部 _ 查询(<instance id="">,<sql query="">)</sql></instance>下面是在 BigQuery 接口中处理 SQL 数据的一个非常基本的查询。Muffaddal 的联邦查询示例EXTERNAL_QUERY 函数中的查询处理云 SQL 接口中的数据,并将其传递给 BigQuery 引擎进行处理。这意味着两件事:
**首先是**,使用 EXTERNAL_QUERY 函数中的查询,我们可以进行过滤、连接、子查询,以便从云 SQL 中只获取我们想要的数据。**其次,**一旦传递给 BigQuery 引擎,即上面示例查询中的外部 select,我们可以将它与其他现有的 BigQuery 表合并,以根据需要在 BigQuery 中转换和构建我们的数据集。我们将使用这两个功能来确保我们只获得 BigQuery 最终表中不存在的数据。最后一个表是我们在 BigQuery 中存储云 SQL 数据的地方。# 移动数据的查询我们将每小时自动获取云 SQL 数据。因此,我们只需要 SQL 表中前一小时的数据。因此,我们必须确保我们的查询只从 SQL 实例中获取数据的子集。Muffaddal 的联邦查询示例每当执行上述查询时,它都会获取前几个小时的所有内容。`posting_timestamp`在 where 子句中是表中的 DateTime 列。用您的日期列名替换它。# 自动化数据插入现在,我们只需每小时重新运行一次联邦查询,并将结果追加到 BigQuery 中。为此,查询调度器开始发挥作用。1-在 BigQuery 控制台中,将查询粘贴到查询编辑器中,然后单击“调度查询”按钮创建新的调度程序。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/92ed07d489816c7f51d27211773b30a7.png)通过 Muffaddal 创建新的计划查询2-接下来,在调度程序创建菜单中输入所需的值来创建调度程序。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/6dcf31cdc4fa0d4cbe33f889a745e3ac.png)查询日程创建菜单,按 Muffaddal不要忘记在“查询计划程序”对话框中选择“附加到表”选项。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/d1327086c88a59ae82623c3c42e917ae.png)查询调度程序创建对话框,由 Muffaddal 创建这个调度器将每小时执行一次,从 cloud SQL 中获取最后一个小时的数据,并将其附加到 BigQuery 表中。就是这样。您将每小时自动将 SQL 数据转移到 BigQuery。# **需要牢记的几件事**在实施解决方案时,您必须记住几件事情1-BigQuery 不支持 SQL 的许多数据类型,例如 UUID 生成器类型,在这种情况下,请在联邦查询中将数据类型转换为 big query 可读数据类型。2-并非所有云 SQL 实例区域都支持联邦查询,因此您必须将您的 SQL 实例移动到支持联邦查询的地方。见下面的截图是为美洲地区。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/3538d3dd0b5413301a96da73c852a49a.png)联邦查询区域支持,来源 [GCP](https://cloud.google.com/bigquery/docs/cloud-sql-federated-queries#regional-locations)[看这里](https://cloud.google.com/bigquery/docs/cloud-sql-federated-queries#regional-locations)了解更多详情。# 你可能喜欢类似的读物</chaining-multiple-sql-queries-in-bigquery-8498d8885da5>  </automate-data-import-to-google-analytics-471de2c3fc76>  </send-google-analytics-hit-level-data-to-bigquery-5093e2db481b> # 使用数据工厂将 CSV 有效负载发送到 API> 原文:<https://towardsdatascience.com/send-csv-payload-to-api-with-data-factory-ff0cdddd6563?source=collection_archive---------23----------------------->## 我将向您展示如何使用 Azure 数据工厂和函数应用程序将 CSV 有效负载发送到 API![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/b3c473d39a9a006ade3692e78e8bd04e.png)皮奥特·穆西奥在 [Unsplash](https://unsplash.com/s/photos/kitten-box?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄的照片有人可能会认为,有一种直接的方法可以使用数据工厂从 SQL table/CSV Blob 中读取数据,并将其发送给需要 CSV 格式文本正文的 API。据我所知,没有,我们需要创建一个功能应用程序来实现这一点。我遇到的这个问题的一个用例是在将数据上传到 [Salesforce Bulk API 2.0](https://developer.salesforce.com/docs/atlas.en-us.api_asynch.meta/api_asynch/asynch_api_intro.htm) 时。## 概观在本文中,我将向您展示一个解决方案,其中1.  我从 SQL 数据库中读取数据,并将其作为 CSV 文件写入 Blob 存储中,
2.  我用 Azure Function 应用程序以字节形式获取 Blob 内容
3.  我将函数响应发送给我创建的示例 API。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/305e31fa1eec22d3601ffe2599dbdd4b.png)## 1.从源到斑点如果您想保留发送文件的历史,我建议将 Blob 名称设置为管道变量。如果没有必要保留历史记录,可以使用硬编码的 Blob 名称。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/ce849e8e17fa79dffcfd3b4911115cee.png)我的 Blob 名称包括当前时间戳和文件名:

@concat(substring(utcnow(), 0, 19), 'canada_climate_data.csv')


我们将使用数据工厂复制活动来创建我们的 Blob。在这个例子中,我的源是我的 SQL DB 中的一个表。源数据集和汇数据集都被创建为通用数据集。参见我的文章[用通用数据集保持你的数据工厂整洁](/data-factory-generic-datasets-a998b832f060)获得解释。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/9d2e578c9220f8300e94c10449f3db07.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/dbd08c17289d79a561f172288a718c95.png)## 2.用函数将 Blob 转换为字节为了创建我的 Python 函数应用程序,我遵循了微软的这些指令。我的 __init__。py 文件如下:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/24d83faca5dad282f6e9e8a2dfa4920a.png)JSON 主体的动态内容如下(用您的存储帐户名和正确的容器名替换“您的存储”和“容器”):

@concat('{"blob_location":"https://yourstorage.blob.core.windows.net/container/',variables('blob_name'),'"}')


## 3.向 API 发送字节![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/6c7eaf64cdccbcece31e24ac7df66e48.png)body 的动态内容是我的函数的响应:

@activity('Blob to bytes').output.Response


就是这样!感谢您的阅读!如果你学到了新的东西或者喜欢这篇文章,请在 Medium 上关注我。我发表关于数据工程和数据科学的文章。你可以从 be [网页](http://miiaramo.github.io/)了解更多关于我的信息。你有没有找到更好的方法来实现这一点,或者有什么不清楚的地方?请在评论中告诉我😊# 将 SendGrid 数据发送到 BigQuery> 原文:<https://towardsdatascience.com/send-sendgrid-data-to-bigquery-d8bf0bd4a238?source=collection_archive---------32----------------------->## 如何将 SendGrid 的电子邮件参与数据发送到 Google BigQuery![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/a7b4902db68fc7a30d53c35c320c7b45.png)来源[皮克斯拜](https://pixabay.com/illustrations/email-newsletter-marketing-online-3249062/),作者[里布汗](https://pixabay.com/users/ribkhan-380399/)从多个来源捕获数据是创建丰富数据仓库的关键。当涉及在线业务时,这变得更加重要,因为数据分散在用于运营业务的众多平台上。[SendGrid](https://sendgrid.com/) 就是这样一个营销平台,它发送交易和营销电子邮件,帮助用户保持知情和参与。我们将讨论将 SendGrid 电子邮件数据存储到我们的数据仓库工具的不同方法。# SendGrid 网页挂钩SendGrid 提供了几种导出数据的方法。一种方法是使用 SendGrid webhooks。Webhooks 是当有事情发生时,应用程序自动发送的消息。因此,每当 SendGrid 发送一封电子邮件时,就会触发一个 webhook,其中包含有关已发送电子邮件的信息。SendGrid 事件 webhooks 可用于以下电子邮件交付和接洽事件![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/6e906ef2fc3e19b2151cdd87ad2bc610.png)由 Muffaddal 编写的 SendGrid 的 webhook 事件因此,SendGrid 为上述所有事件触发了一个 webhook,并随之传递信息。下面是它在打开事件时发送的信息示例。这些信息被称为有效载荷。[在此查看其他 SendGrid 事件](https://sendgrid.com/docs/for-developers/tracking-events/event/)的有效载荷示例。## 数据管道SendGrid 的 webhooks 和 Google Cloud 功能的结合可以帮助捕获 BigQuery 中需要的所有数据。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/b9a1ed7b616d5481c14080079c3f8a2b.png)数据管道,由 Muffaddal## 步伐建立数据流涉及三个步骤。1.  根据您希望从 SendGrid 存储的数据,创建一个具有模式的 BigQuery 表。示例电子邮件、事件、时间戳、类别。
2.  设置一个云函数,可以接收来自 SendGrid 的 webhook 调用,提取所需信息,并传递给 BigQuery。
3.  在发送网格中配置 webhook。**大功告成!**现在让我们来实际实施上述所有步骤。***注意*** *这需要相当多的技术和编程背景。因此,我建议请您团队中的技术人员来实施解决方案。*[*让我知道你是否需要我的帮助来实现*](http://bit.ly/3rLwbo2)# 数据管道实现## 1-大查询表我们首先必须在 BigQuery 中创建一个可以存储 SendGrid 数据的表。输入列名,如下图所示。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/d1110a00492b33f56a1b101c694534d5.png)muffaddal 的 BigQuery 表架构将该表命名为 sendgrid_events。## 2 云函数接下来,我们必须创建一个云功能,它有两个职责。首先是获取 SendGrid 传递的数据。第二个是在我们上面创建的 BigQuery 表中处理和存储数据。我们将使用 Node.js 来编写我们的云函数。这是我们将用于它的配置。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/468958036d5a0489b212f9ca865d0a33.png)云函数配置,由 muffaddal配置完成后,在 index.js 中添加以下代码,根据需要解析数据。根据您的 GCP 项目替换我的项目,我的数据库。上面的代码解析从 SendGrid 发送的数据,然后将其附加到 BigQuery另外,在 package.json 中添加 BigQuery 依赖项。复制云函数触发 URL,我们就可以进入最后一步了。## 3-网状挂钩配置在 SendGrid UI 中,转到设置>邮件设置。从那里点击编辑启用 webhooks。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/8fe14689986b9c64cf9762ff6cc2359d.png)通过 muffaddal 启用 webhooks一扇窗户将会打开。输入云函数 URL 并检查所有要发布的事件,如下所示。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/07907c4069283b2655e3a8c7552ee0c9.png)Muffaddal 的 SendGrid webhook 配置单击“Test Your Integration”按钮将测试事件发送到云函数,以便您查看这些事件是如何存储在 BigQuery 中的。这是它的样子![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/105a068920750eb3d7e2664dc94ff1bc.png)muffaddal 在 Bigquery 中发送网格测试数据你是否注意到我们也按原样存储 event_payload?原因是,如果需要,我们可以从任何事件的有效载荷中提取更多的信息。点击 webhook 配置窗口中的保存按钮,我们就完成了。按照上面的步骤,您将在 BigQuery 中接近实时地获得所有需要处理和操作的 SendGrid 数据。*如果实施对你来说太技术性了,你想自己学习更多,那么* [*这里有一个极好的课程*](https://bit.ly/2X4kefM) *在 GCP 开始数据工程。*# 你喜欢的类似读物</send-cloud-sql-data-to-bigquery-2603489a4330>  </send-google-analytics-hit-level-data-to-bigquery-5093e2db481b>  </chaining-multiple-sql-queries-in-bigquery-8498d8885da5> # 从 Flask 应用程序向 PostgreSQL 数据库发送数据> 原文:<https://towardsdatascience.com/sending-data-from-a-flask-app-to-postgresql-database-889304964bf2?source=collection_archive---------1----------------------->![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/dc0f7dab3f2172b377ad9aa7c3f359c2.png)由[扬·安东宁·科拉尔](https://unsplash.com/@jankolar?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄## 什么是 ORM如果你打算开发任何类型的现代网络服务,管理网站或应用程序的数据流是一项需要掌握的关键技能。有了 flask,*对象关系映射器* (ORM)被用来允许你的应用与关系数据库交互。对象关系映射器是一个框架,在我们的例子中,它允许我们使用 python 而不是显式 SQL 查询与 SQL 数据库进行交互。我们使用的 ORM 的名称是[*SQLAlchemy*](https://pypi.org/project/SQLAlchemy/)*,可以下载如下:*

pip install flask_sqlalchemy
pip install psycopg2-binary #for using postgres


*本文假设您对 SQL 有一些基本的了解,并且在您的机器上安装了 [Flask](https://flask.palletsprojects.com/en/1.1.x/installation/#installation) 、 [PostgreSQL](https://www.postgresql.org/) 和 [pgAdmin](https://www.pgadmin.org/) 。****目录结构。****为了让我们的应用程序正常工作,目录需要如下所示。确保不要更改您在下面看到的任何文件/文件夹的名称或拼写。*

# from the terminal in the project folder$ mkdir templates static
$ touch app.py
$ cd templates
$ touch index.html$ tree (optional: only works if tree is installed on OSX)├── app.py
├── static
└── templates
└── index.html2 directories, 2 files


*在接下来的部分中,我们将制作一个基本的表单,将一个人的名字和他最喜欢的颜色发送到本地 PostgreSQL 数据库。*## *建立我们的数据库。**我们将在 pgAdmin 中创建一个数据库:**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/290ab18cda35caaf9075c875ae589b8e.png)**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/cc7bd5d4a07c10bd63b91289ad4f921a.png)**现在,我们需要设置 *app.py* 来连接我们的数据库和应用程序。我们首先创建我们的应用程序,并配置数据库 [URI](https://stackoverflow.com/questions/176264/what-is-the-difference-between-a-uri-a-url-and-a-urn) 和 secret_key。**`SQLALCHEMY_DATABASE_URI`是描述数据库连接的字符串。出于本文的目的,该连接是本地的,可以描述如下:*

engine:[//[user[:password]@][host]/[dbname]]engine -> postgresql
user -> postgres (see owner field in previous screenshot)
password -> password (my db password is the string, password)
host -> localhost (because we are running locally on out machine)
dbname -> flasksql (this is the name I gave to the db in the previous step)


*在配置了到本地数据库的连接之后,我们需要创建我们的`people`表。这将由整数类型的主键、必须唯一的名称列和颜色列组成。颜色和名称列都必须由用户输入。**现在,在登录页面通常所在的位置,我们将添加一个按钮,将我们带到输入表单。当我们点击表单上的`submit`时,我们将人名和他们最喜欢的颜色添加到 *People* 类/表中,然后使用 *db.session* 将该条目添加到数据库中,并提交更改。**在应用程序运行之前,我们需要使用`db.create()`来创建数据库:*## *用于提交数据的 HTML 表单。**为了从用户那里获取信息并存入数据库,我们使用一个 HTML 表单,表单的输入具有与数据库中的列名相对应的`name`属性。我们用`request.form["pname"]`和`request.form["color"]`从 *personadd()* 函数的表单中获取这些信息。*## *在 pgAdmin 中查看数据。**现在我们已经在数据库中有了名称和颜色,我们可以转到 pgAdmin 并直接查询数据库。每当我们使用表单添加一个新条目时,我们可以转到 pgAdmin 并刷新数据库以反映结果。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/99ab3bc4df6af2d97778aa5fbf93ed8d.png)**每次添加新条目时,按`F5`键,查询将使用新数据更新!*## *结论。**本文并不是为了说明最佳实践或生产就绪的解决方案,而是为那些不知道从哪里开始的人准备的。我们讨论了一些基本概念,如 ORM 和 URI 数据库,这些基础知识应该可以很好地为您服务,因为您了解了更多关于数据库和应用程序基础架构的知识。**编码快乐!**GitHub 回购可以在[这里](https://github.com/brenfrrs/flasksql)找到。**💻请随意查看我的[网站](https://brendanferris.com/)。*# 敏感性和特异性,解释!—和僵尸?!> 原文:<https://towardsdatascience.com/sensitivity-and-specificity-explained-with-zombies-ae3f4801b35b?source=collection_archive---------40----------------------->## 评估方法和 ROC 分析简介![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/3283f07300fb66ee2c54af513cc29b1c.png)评估方法和 ROC 分析简介。图片由 Andreas Maier 提供欢迎大家,今天我们想简短地谈谈评估分类系统的措施,以及如何改变分类系统以改变评估结果。所以我想你会发现这很有趣,我也带来了一个非常有趣的例子来讨论分类系统的想法。今天的关键问题是“我如何评估一个分类系统?”。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/16fa0bdbb603690fc262c967f0c8e1c0.png)来自[模式识别讲座](https://www.youtube.com/playlist?list=PLpOGQvPCDQzsWvT_bqmexrJ359RTQQuMO)的 [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) 下的图像。所以我们在我们的[模式识别讲座](https://www.youtube.com/playlist?list=PLpOGQvPCDQzsWvT_bqmexrJ359RTQQuMO)中谈到了这一点。但是我发现我们需要一些额外的解释来真正理解分类系统是做什么的,以及我们如何评估它们。所以,当然,我们会用一个例子来讨论这个问题。如今每个人都在谈论疾病,似乎每个人都成了疾病分类的专家。所以,我也认为谈论一种典型的疾病会很有趣,我选了僵尸病。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/62669fb5cf0764297ceb7626618e1314.png)来自[模式识别讲座](https://www.youtube.com/playlist?list=PLpOGQvPCDQzsWvT_bqmexrJ359RTQQuMO)的 4.0CC 下的图像。我认为这是世界上可能发生的最可怕的事情之一。所以我认为这是一个合适的时间来讨论当我们遇到僵尸疾病时我们应该做些什么。你看,这是我们如何评估测试结果的。因此,我们已经在准备一个测试系统,以确定谁是僵尸,谁不是,我们当然会有几个可能的结果,你知道,人实际上可能是僵尸。这就是参考。所以这些是柱子,否则它们不可能是僵尸。所以它们要么是正的,要么是负的,然后当然我们的分类结果,假设会有两种结果之一。所以这也可能是积极的或消极的,当然,也有正常人。所以这是一个真正的否定,他们只是普通人,当然,测试结果也应该是否定的,那么我们当然希望我们的分类系统能够检测到僵尸。因此,我们希望检测到真实存在的僵尸,我们希望结果也是僵尸。所以这将是一个真正的积极因素。当然,我们的分类系统也会出错,有时我们会发现某人是僵尸,但他实际上并不是僵尸,我在这里举了一个小例子,你可以记住这个例子。这是一部非常有趣的恐怖电影《僵尸肖恩》,在这个场景中,肖恩宿醉未醒,他去商店买了一杯饮料。你看,他宿醉得很厉害,首先他没有意识到他周围有行尸走肉,其次他们没有认出他,因为他的行为像他们中的一员。所以这是一个假阳性。所以人们,幸运的是他也是行尸走肉僵尸认为你是他们中的一员,然后还有另一种情况,我们不知何故错过了某人死而复生。这里有一种情况,类似这样的事情实际上正在发生。所以你知道你在玩你最喜欢的电子游戏,你和你的朋友一起玩,在某个时候,你甚至没有意识到你的朋友已经变成了僵尸,而你还在玩电子游戏。所以这是假阴性的情况。现在我们已经了解了不同的结果是什么,我希望你们能够记住这一点。所以假阳性和假阴性是我们会犯的两种错误。所以很明显我绝不会让你毫无准备地染上可怕的僵尸病。所以,当然,我也有一些建议给你。好了,伙计们,我们被困在这个可怕的僵尸感染的大厦里了,我们分头行动吧。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/77c596913956b7d0c43d0baf48c699c3.png)来自[模式识别讲座](https://www.youtube.com/playlist?list=PLpOGQvPCDQzsWvT_bqmexrJ359RTQQuMO)的 [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) 下的图像。好了,让我们回到评估我们的分类器。因此,可以用许多不同的方法来评估分类系统,您在这里可以看到如何评估该分类器的不同方法。当然,有许多不同的方法可以组合上述值来计算不同的比率。一个非常典型的例子是真阳性率,也称为命中率回忆或灵敏度,即真阳性的数量除以观察到的阳性数量,即真阳性加上假阴性。此外还有假阳性率,实质上是假警报率,它是假阳性的数量除以真阳性和假阴性的数量。同样高度相关的是像阳性预测值或精确度这样的度量,并且位置被给定为真阳性的数量除以真阳性加上假阳性的数量。然后还有阴性预测值,它是真阴性的数量除以两个阴性加上假阴性的数量。最后但并非最不重要的是,真阴性率或特异性,其计算方法是 1 减去假阳性率。所以这看起来有点复杂,我试着把这些事情分解一下。所以记住,一个人不能简单地理解敏感性和特异性。但是我会试着通过一些例子来简化这个问题,我给你们举的例子是下面这个。假设我们总共有 100,000 人,我们现在将填充这 100,000 人的整个示例表。我们知道实际上有 1 万人感染了僵尸病。这意味着这个数字在这里,另外 90,000 人没有被感染。所以他们只是普通人。所以这个数字会出现在我们的图中。现在我们想看看我们的测试有多好,让我们假设我们知道灵敏度。如果我们知道了敏感度,我们现在就可以实际计算真正阳性的数量,也就是我们集合中阳性的数量。然后我们乘以灵敏度,得到 9970 个真阳性,这个数字就在这里。我们现在也可以用这个来计算假阴性的数量,你可以在这里看到,当然,我也可以把它放在我们的表中。因此,与确定我们表格中的实际值非常相关的另一个值是特异性。特异性现在允许我们计算真正阴性的数量,这仅仅是参照中阴性的数量。所以真实人类的实际数量是这个数量乘以特异性。这样我们就得到了 98,280,我们可以把它放在这里的表中。这允许我们现在计算假阳性的数量,在这个图中是 720。这个值在这里。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/a93b67e80d3222fce8978993201f7de2.png)来自[模式识别讲座](https://www.youtube.com/playlist?list=PLpOGQvPCDQzsWvT_bqmexrJ359RTQQuMO)的 [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) 下的图像。现在让我们想想这对我们的测试实际上意味着什么。所以你记得我们的假阴性。这是每次我们探测不到丧尸的时候。因此,假阴性与敏感性有关,如果我们的敏感性低,这意味着我们对僵尸不太敏感。所以我们干脆无视他们,认为他们是真人。因此,在这种情况下,我们将有总共 30 个僵尸可能在某个地方玩视频游戏,甚至可能感染其他人,而他们被我们的测试遗漏了。所以我们无法识别它们,也无法对这些僵尸采取任何对策,因为我们没有检测到它们。还有假阳性,在我们的例子中,假阳性是宿醉者,看起来像僵尸,但实际上不是僵尸,这与特异性有关。所以这意味着低特异性与对僵尸不太明确有关。所以我们认为人是僵尸,其实并不是僵尸。因此,在我们的案例中,将有 720 人。现在,如果你是僵尸中的一员,实际上并不太坏,因为僵尸会认为你是他们中的一员,他们不会伤害你。但是想象一下你的人类同伴认为你是僵尸。嗯,他们可能会有点害怕,你知道可能会有很糟糕的事情发生在你身上。所以让我们想一想患病率或先验概率是如何影响我们的检出率的。现在,如果我们修正灵敏度和特异性,那么我们可以看到我们的先验是否只有百分之一。因此,假设我们处于疾病的非常非常早期阶段,那么我们基本上不会发现任何假阴性。所以我们几乎不会错过僵尸,僵尸的数量可能很低,人们会说那里没有僵尸。因此,甚至可能会有否认僵尸的人说,这些僵尸无法被检测到,如果你仔细看看已经被检测为僵尸的案例,那么它们非常少,在这个特定的案例中,如果我们只有 1000 个僵尸,我们仍然检测到大约 700 多人是僵尸。所以几乎一半被检测到的僵尸实际上并不是僵尸。虽然我们的测试做得很好。因此,这一切可能被简单地贴上神话或假新闻的标签,或者这种疾病根本就不存在。所以,如果我们现在逐渐增加患病率,我们会达到 50%的患病率。因此,就像有一半的人类留下来,一半的人类已经被转化为僵尸,你看到我们的错误发生了变化,随着先验知识的增加,我们有了更多的假阴性。因此,我们错过了实际感染疾病的人,他们去传播疾病,而与此同时,我们减少了假阳性,这只是与周围的阴性较少有关。所以假阳性的数量也在下降。假设这种疾病已经接管了它有 90%的患病率,所以在 90%的患病率情况下,假阳性的数量将大大减少,但我们有越来越多的僵尸被我们遗漏了,但老实说,90 或僵尸周围的事情已经非常糟糕,你可能不想相信任何人。所以这是非常糟糕的,尤其是大量的假阳性,在开始时,非常非常令人担忧。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/b226fd4529064151094cfd1d5d33ef08.png)来自[模式识别讲座](https://www.youtube.com/playlist?list=PLpOGQvPCDQzsWvT_bqmexrJ359RTQQuMO)的 [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) 下的图像。所以让我们想想,我们是否可以做些什么来减少假阳性的数量,这样我们就不会伤害许多宿醉的人。因此,如果你只是有一个糟糕的宿醉,也许这不是那么好,你的邻居会决定使用对抗僵尸的对策,这实际上对你来说可能不是那么好。所以让我们想一想我们的测试,我们不知道谁是真正的僵尸,我们只是从测试中得到结果。但当然,这至少告诉我们,在我们是阳性的情况下,我们可能只是想再次测试它们,只是为了真正确定。那么,如果我们再次测试,会发生什么呢?我们只是再次运行相同的测试,在这种情况下,这是我们的测试结果,所以我们现在已经观察到阳性和阴性,它们仅由之前的测试结果确定。所以现在我们来看一下我们只想重新测试阳性病例的情况。让我们重新测试那些观察到的阳性结果,观察到的阳性结果当然是真阳性,真阳性现在我们必须乘以灵敏度,灵敏度将再次给出我们是阳性还是阴性的结果。这意味着我们重新测试,大量的真阳性将有另外 30 个被分配给假阴性的数量。现在让我们考虑假阳性的情况。所以我们有 720 人,我们重新测试他们,现在我们必须考虑特异性。我们现在看到,在 720 个中,我们可以将 714 个识别为假阳性。因此,它们将被指定为阴性,这意味着我们只剩下六个假阳性。所以我们得到了一个新的混淆矩阵。这些是我们已经考虑过再测试程序的新结果。所以你可以看到假阴性的数量增加到了 60 个,而假阳性的数量减少到只有 6 个。这意味着我们在这个新的测试程序中有了一个新的测试,我们只测试阳性两次,它的真阳性率降低了,但真阴性率却高得多。因此,假阳性大大减少,你可以看到这个数字现在高得多,但这是以降低灵敏度为代价的。当然,我们也可以走另一个方向。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/175d6e7a4b8bba9b3928e9391f728e1e.png)来自[模式识别讲座](https://www.youtube.com/playlist?list=PLpOGQvPCDQzsWvT_bqmexrJ359RTQQuMO)的 [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) 下的图像。所以我们只重新测试观察到的负面结果。现在,在这种情况下,我们要确保我们不会错过一个僵尸,所以我们重新测试所有被观察为负面的东西。现在,我们必须再次检查我们的敏感度,现在我们看到,在观察到的阴性结果中,只有 30 例是假阴性,乘以敏感度,现在实际上会给出 30 个正确的分类。所以我们的假负数变为零。现在让我们考虑另一个错误,我们用我们集合中的真阴性来做,这里我们必须使用特异性。我们看到,我们乘以这个数字,我们产生了另外 714 个假阳性。现在我们可以更新我们的混淆矩阵,看起来像这样。所以我们引入了这个重新测试程序,它给了我们 100%的灵敏度。所以我们没有漏掉一个僵尸,但代价是我们现在有 1434 个误报。因此,通过引入这一程序,假阳性的数量几乎翻倍。但是我们再也没有假阴性了。所以记住我们只有一万个阳性,现在我们有一千四百个假阳性。所以这意味着在观察到的一万一千四百个阳性中,有超过百分之十不是真正的僵尸。所以你以不漏掉任何人为代价,摆脱了一千四百多名普通人。所以你正在对 1400 名实际上没有被疾病感染的人采取对策。因此,这可能是一件相当艰难的事情,也许不是每个政府都能真正做到这一点。我们现在开发了三种不同的测试程序,现在你可能想知道它们是否相同,哪种更好,哪种更差。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/99aa6337e7cba52c6a4aa943840eb224.png)来自[模式识别讲座](https://www.youtube.com/playlist?list=PLpOGQvPCDQzsWvT_bqmexrJ359RTQQuMO)的 [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) 下的图像。让我们来看看它们,比较一下,比较的典型工具是所谓的受试者操作特性曲线 ROC 曲线。我在左边显示了评估的空间,你可以在这个图中看到,我们画的是灵敏度除以 1 减去特异性。你可以在左上角的图上看到几个重要的点,用你看到的完美分类器来表示,所以这个有 100%的特异性和 100%的灵敏度。所以这总是做正确的决定。你可以做的另一件事是,你可以简单地总是决定为正,那么你会在曲线的右上角,相反的是,你总是决定为负,那么你会在曲线的左下角。有一条线连接这两点,这基本上是一个随机的决定,但有不同的阈值。所以如果你掷骰子,你会在连接这两点的线上的某个地方,这意味着你在完全不知道任何观察的情况下做决定,这将产生下面的线。如果你在左上角的三角形中,你已经建立了一个有效的分类系统。所以我希望在左上角的三角形上有任何有效的分类系统。所以在对角线上方,对角线下方还有一个空间,这里用 4 表示,如果你的分类器位于那里的某个地方,那就错多过对。这意味着在一个两级系统中,如果你做的和你的分类系统所建议的相反,你实际上会在左上角的三角形中结束。所以这是我们现在必须定位分类器的空间,我们看到我们的普通僵尸分类器实际上做得并不差。它非常接近左上方,所以它非常接近,因为它具有非常高的灵敏度和非常高的特异性。现在,我们改变了我们的分类系统,重新运行所有的阳性结果,因此我们改变了敏感性和特异性,以得出这一点。所以我们稍微偏离了原来的分类系统,我们可以提高一个比率,但牺牲另一个比率。然后我们做了完全相反的事情,我们重新运行所有的阴性案例,然后我们在这里得到这个观察结果。所以我们在一个速率上增加,但在另一个速率上减少。因此,你可以看到,通过调整分类系统和重新运行决策,我可以改变结果。实际上,灵敏度和特异性有一整套不同的解决方案,这里用绿色曲线表示,我们可以根据给定的分类系统沿着这条绿色曲线前进。这里你可以看到这个分类系统非常非常好,因为它几乎覆盖了接近 1 的区域。所以如果我有完美的分类器,那么这条曲线下的面积,在绿色曲线下,正好是 1,我们非常非常接近这里的 1。所以我们有一个非常好的分类系统,因为我们使用的是同一个系统,所以需要通过这条曲线来检测。现在你可能想知道我是如何得到这条绿色曲线的。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/d9d2b3f407d0fe82cc537949baca317c.png)来自[模式识别讲座](https://www.youtube.com/playlist?list=PLpOGQvPCDQzsWvT_bqmexrJ359RTQQuMO)的 [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) 下的图像。实际上,我们的分类系统源于两种分布,正分布和负分布,确定实际的分类结果只是简单地通过选择测试统计的阈值。所以不管你做什么测试?所以在僵尸的例子中,我听说眼压是一个非常好的衡量标准,当我们实际上确定某人是否是僵尸时,你只需要改变阈值。这个人很重要,我可以简单地通过改变阈值来采样整个绿色曲线。所以我不需要经历重新测试的过程,运行两个测试,如果是相同的测试,它不会改变任何东西,因为你只需要根据不同的阈值调整你的分类器。所以那不会有很大帮助,我们能做些什么呢?![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/0914b0e1697e3267bb88454ea7eab2b5.png)来自[模式识别讲座](https://www.youtube.com/playlist?list=PLpOGQvPCDQzsWvT_bqmexrJ359RTQQuMO)的 [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) 下的图像。让我们考虑几个想法,其中一个非常明确的想法是,根据场景选择阈值。如果你想选择阈值,你必须确定决策的成本。因此,每一个决定都有成本,例如,根据疾病的流行程度,成本可能会非常不同。因此,如果你处于早期阶段,你想不惜一切代价阻止僵尸,错过一个会非常昂贵。因为僵尸会继续传播疾病。在疾病的晚期,这可能会非常不同。因此,您可能希望根据当前的测试场景调整阈值,如果您知道决策的成本,您就可以这样做,因为这样可以让您权衡什么成本更高,是假阳性还是假阴性。另一个想法是涉及另一个测试,你只是用两个不同的测试来测试,如果它们在统计上是独立的,你可以将两者结合起来,以获得更好的决定。这就引出了一些概念,比如推进 bagging 和集成分类器,我们也会在这堂课中讨论。为了理解这些集合属性,我想给你们看一个具体的例子。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/77fd788e9f4f7a39f0b57b1aa0168801.png)来自[模式识别讲座](https://www.youtube.com/playlist?list=PLpOGQvPCDQzsWvT_bqmexrJ359RTQQuMO)的 [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) 下的图像。所以,你通常要做的是运行第二个测试统计,利用第二个测试统计,你可以创建像这样的图,然后找到一个与两个测试之一正交的决策边界。这意味着您可以利用这两个测试的力量。每当我们进行提升和组合时,我们通常假设测试在统计上是独立的。这是一个关键因素,我们来看一个例子。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/890d97efd9861035d82a3371a0c58952.png)来自[模式识别讲座](https://www.youtube.com/playlist?list=PLpOGQvPCDQzsWvT_bqmexrJ359RTQQuMO)的 [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) 下的图像。所以我有检验统计量 1,我们的 1 类和 2 类或者正负分布如下。所以有很大的重叠。所以他们都没有很好的区分这两个类,我做了另一个测试,同样的两个类又出现了。现在我想把这两个结合起来,现在让我们选择一个观察配置,这些现在被表示为真实类和真实的其他类,所以积极和消极的。在这里你可以看到,我必须找到沿着 x 轴和 y 轴投影到原始分布的点。我在这种情况下这样做了,你可以看到在这种特殊情况下,点的分布遵循各自的一维投影。所以如果我把它们移到一边或另一边,通过两个测试的结合,我可以得到非常清晰的分离。因此,这非常有用,在这种情况下,我们真的有两个测试的独立性,因为它们是独立的,所以将它们结合起来可能会有很高的产量。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/152847d62175916db8744ef2aa6b1f57.png)来自[模式识别讲座](https://www.youtube.com/playlist?list=PLpOGQvPCDQzsWvT_bqmexrJ359RTQQuMO)的 4.0CC 下的图像。现在让我们看一个例子,在这个例子中,类被很好地分开了,这里我有一个类,另一个类,现在重叠非常小。我在另一个轴上做同样的操作,重叠也非常小。这意味着蓝点必须分布在投射到蓝色曲线上的某个地方,而橙色点必须位于投射到橙色曲线上的某个地方。这有点问题,因为你可以看到我们非常接近对角线,实际上我们有测试一和测试二的相关性。所以这两个结果不是完全独立的,每当我已经有了这种很好的分离,并且假设这些是检验统计量上的高斯分布点,那么我就会遇到这个问题。现在的问题是,因为这两种测试是相互关联的,所以将这两种测试结合起来的结果很少。所以你在这里看到了一个决策边界。这稍微好一点,但是它不能像我们在前面的案例中看到的那样干净利落地解决问题。因此,如果两者相关,就很难将两个测试从另一个测试中分离出来,并从另一个测试统计中受益,因为它们几乎测量相同的东西。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/1a2144461b3d994a4ede3a4e9a71cb79.png)来自[模式识别讲座](https://www.youtube.com/playlist?list=PLpOGQvPCDQzsWvT_bqmexrJ359RTQQuMO)的 [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) 下的图像。当然这并不意味着这是不可能的。因此,相关的是这里的这个区域,如果我幸运地通过了测试,那么我可以找到一种配置来更好地解决这个问题。但是当然,只有两个类实际相连的小区域才是我能得到收益率的区域。这很难用两个轴中的一个轴上的统计数据来描述,所以你真的必须知道类的完整分布和它们的联合概率分布,否则你将不能如此容易地确定这一点。嗯,我认为这些是非常有趣的观察结果,让你对评估分类器的测试统计有了一点点的了解,以及分类系统的敏感性和特异性在含义方面也有什么影响。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/3173be07221e1de65aa6a4d4ad59fe54.png)来自[模式识别讲座](https://www.youtube.com/playlist?list=PLpOGQvPCDQzsWvT_bqmexrJ359RTQQuMO)的 [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) 下的图像。我准备了一套额外的练习题,你可以思考一下,以便更深入地理解我在这个视频中展示的内容。特别是,你应该考虑改变敏感性和特异性,并考虑这将如何改变假阳性和假阴性的数量。此外,我认为筛选文献并寻找其他疾病等论文中报道的敏感性和特异性将是有趣的。因此,这可以帮助你了解分类器的相关性,你会得到多少假阳性等等,我还建议检查不同疾病的流行率,因为一些疾病非常流行,然后你会对假阳性和假阴性产生非常不同的影响,而不是以不同速率发生的疾病。所以我这里有两个例子,检查信用卡欺诈,你会发现欺诈实际上很少发生。因此,想象一下这对假阳性率的要求意味着什么,然后检查关于社交机器人的文献,这也非常有趣,因为分类器的准确性非常低。然后你可以思考这对于研究中发现和报道的机器人可能意味着什么,是的,你会看到这实际上是一个非常有趣的现象。如果你有一个非常糟糕的分类器,我的意思是它当然会识别社交机器人,但它真的有助于你判断这个人是否真的是社交机器人吗?以如此低的准确度进行大规模筛选真的是一件非常有意义的事情吗?你自己想一想,另一个非常有趣的问题是,如果你知道检测的灵敏度和特异性,并且有观察到的阳性和阴性数量,就可以计算实际的患病率。所以这也是你可以在一道习题中思考的。这已经把我们带到了这一单元的结尾。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/330778964b677016d0b3d3313a302dce.png)来自[模式识别讲座](https://www.youtube.com/playlist?list=PLpOGQvPCDQzsWvT_bqmexrJ359RTQQuMO)的 [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) 下的图像。所以保持健康,远离僵尸,是的,我不想让你失去希望。记住,如果查克·诺里斯被僵尸咬了,诺里斯不会变成僵尸,但僵尸会变成查克·诺里斯。所以这里有一些联系方式,如果你想跟进,看更多的视频。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/f87786175786ed6284985709051aca75.png)来自[模式识别讲座](https://www.youtube.com/playlist?list=PLpOGQvPCDQzsWvT_bqmexrJ359RTQQuMO)的 [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) 下的图像。所以我想我还有一些关于模式识别和机器学习的有趣的事情。这已经把我们带到了这个视频的结尾,我希望你看这个有一些乐趣,你对敏感性和特异性有一点点的了解,这意味着什么,这与假阴性和假阳性有什么关系,如果你在这方面做错了什么,实际上影响会有多大。非常感谢您的观看,并期待在其他视频中再次见到您。拜拜。!如果你喜欢这篇文章,你可以在这里找到更多的文章,或者看看我们的讲座。如果你想在未来了解更多的文章、视频和研究,我也会很感激关注 [YouTube](https://www.youtube.com/c/AndreasMaierTV) 、 [Twitter](https://twitter.com/maier_ak) 、[脸书](https://www.facebook.com/andreas.maier.31337)或 [LinkedIn](https://www.linkedin.com/in/andreas-maier-a6870b1a6/) 。本文以 [Creative Commons 4.0 归属许可](https://creativecommons.org/licenses/by/4.0/deed.de)发布,如果引用,可以转载和修改。如果你有兴趣从视频讲座中获得文字记录,试试[自动博客](http://autoblog.tf.fau.de/)# 句子嵌入:没有足够的数据?只需申请退学两次!> 原文:<https://towardsdatascience.com/sentence-embeddings-not-enough-data-just-apply-dropout-twice-e5122533786?source=collection_archive---------11----------------------->## 评论一篇有趣的论文,该论文提出了一个简单的对比学习框架,可以产生优秀的句子嵌入![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/26c6225422b3ae5dc5c6261db0df822a.png)由[埃罗尔·艾哈迈德](https://unsplash.com/@erol?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片太多的未标记数据和创建标记数据的成本太高是深度学习中的常见问题。在计算机视觉中,我们可以使用数据扩充来生成更多的标记数据或提高模型的泛化能力。但是自然语言处理中的数据扩充往往不能有效地改进模型,而且可能会损害模型。高等人(2021)提出了 SimCSE,这是一种对比学习模型,利用简单而优雅的数据增强方法在监督和非监督文本相似性任务中实现 SOTA 结果。# 无监督 SimCSE在讨论 SimCSE 之前,我们应该知道句子嵌入被用来表示句子的特征向量。提取句子嵌入的常见方式是使用 BERT liked 大型预训练语言模型来提取`[CLS]`标记嵌入作为句子嵌入。SimCSE 使用预训练的 BERT 或 RoBERTa,在`[CLS]`表示的顶部有一个 MLP 层,作为编码器来获得句子嵌入。SimCSE 作为一种对比学习模型,需要输入句子的正对和负对进行训练。作者简单地使用 dropout 向输入句子注入噪声来生成正对,并使用其他句子作为负对。是的,他们使用了 **dropout 作为数据扩充方法**!换句话说,一个输入的句子通过一个带有 dropout 的编码器得到第一个句子嵌入, *v1* 再通过编码器再次传递*句子得到第二个句子嵌入, *v2* 得到正对( *v1* , *v2* )。*有人可能会问,将一个句子通过同一个编码器两次,不会仅仅返回两个相似的句子嵌入吗?注意,基于 transformer 的编码器最初带有随机丢弃掩码,因此将一个输入句子通过编码器两次会导致两个稍微不同的句子嵌入。通过这种简单的方法,在 100 万对英语维基百科数据上训练的无监督 SimCSE 在标准语义文本相似性(STS)任务上取得了 SOTA 结果。# 监督 SimCSE除了无监督训练,高等人(2021)还利用来自 [SNLI](https://nlp.stanford.edu/projects/snli/) (Bowman 等人,2015)+ [MNLI](https://cims.nyu.edu/~sbowman/multinli/) (Williams 等人,2018)数据集的标记数据,进一步研究了 SimCSE 的性能。所选择的数据集由标记有蕴涵、矛盾和中性的句子对组成。他们用蕴涵对来构造肯定对,而否定对来自矛盾对。实验结果表明,标记数据的引入进一步提高了性能。下图显示了无监督和有监督模型的实验结果。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/6c16e8eb74bc7e8041d28460f30a319c.png)高等(2021)的结果作者在 HuggingFace 的支持下在 [Github](https://github.com/princeton-nlp/SimCSE) 上发布了源代码。我们可以打开一个 Google Colab 笔记本,快速运行回购中提供的一些例子。# 结论令人惊讶的是,我们在几乎每个深度学习模型上应用的简单辍学可以用来进行数据增强。在计算机视觉上测试相同的方法并将其与常用的图像处理方法进行比较可能会很有趣。我个人认为无监督的 SimCSE 具有更大的价值,并且非常有用,因为我们经常处理大量未标注的数据。无监督学习应该是未来人工智能的趋势,因为它可以更好地利用大数据集,并避免监督学习中的常见问题,如标签噪声。这个故事只解释了原著的一小部分,我强烈建议你阅读原著以获得全面的理解。# 参考塞缪尔·r·鲍曼、加博·安格利、克里斯托弗·波茨和克里斯托弗·d·曼宁。2015.用于学习自然语言推理的大型标注语料库。在*自然语言处理的经验方法(EMNLP)* 中,第 632–642 页。高天宇、姚兴成和陈。2021.SimCSE:句子嵌入的简单对比学习。 [arXiv:2104.08821](https://arxiv.org/abs/2104.08821) 。艾迪娜·威廉姆斯,尼基塔·南吉亚,塞缪尔·鲍曼。2018.通过推理理解句子的大范围挑战语料库。计算语言学协会北美分会:人类语言技术(NAACL-HLT ),第 1112–1122 页。# 句子转换微调(SetFit):在少量文本分类方面优于 GPT-3,但体积却小了 1600 倍> 原文:<https://towardsdatascience.com/sentence-transformer-fine-tuning-setfit-outperforms-gpt-3-on-few-shot-text-classification-while-d9a3788f0b4e?source=collection_archive---------3----------------------->GPT-n 系列在少数镜头 NLP 分类任务中表现出非常有前景的结果,并随着模型规模的增加而不断改进(GPT 3–175B)。然而,这些模型需要大量的计算资源,并且它们对训练提示的选择很敏感。在这项工作中,我们演示了句子转换器微调(SetFit),这是一种简单有效的少量文本分类方法。该方法基于用特定于任务的数据来微调句子转换器,并且可以容易地用句子转换器库来实现。我们通过将 SetFit 应用于 RAFT(真实世界少数镜头文本分类)基准测试来验证它,并显示它令人惊讶地优于 GPT-3 等模型,同时它的规模是 1600 倍,并缓解了对即时训练和使用未标记数据的需求。此外,我们的方法有助于缩小机器和人类性能之间的差距,只需提供 50 个实例进行训练。**简介**有监督神经网络在不同领域和领域的许多任务中表现出显著的性能,但是它们需要大量的标记训练数据。数据标记过程是劳动密集型和成本高昂的,并且阻碍了人工智能系统在行业中的部署。人工智能中的迁移学习(TL)对于低数据资源场景实现了相当大的鲁棒性。[NLP](https://ruder.io/state-of-transfer-learning-in-nlp/)(Ruder et al . 2019)中的 TL 涉及两个步骤:使用未标记数据训练的预训练语言模型(PLM)和针对特定任务的第二步微调。在 BERT (Devlin 等人,2019)等标准微调设置中,PLM 顶部的通用分类头输出用于生成最终预测的表示。基于 LM 微调的[文本到文本](https://ruder.io/recent-advances-lm-fine-tuning/)【1】的最新进展实现了 SOTA 少数镜头性能,包括上下文学习和任务特定提示学习。**情境学习**模型根据作为提示提供的输入-输出训练示例直接生成答案,而不改变其参数。GPT-3(布朗等人,2020 年)利用上下文学习来证明在许多自然语言处理任务中优越的少数镜头能力。它的主要缺点是需要一个庞大的模型,只依赖于预先训练的知识,并且需要大量的快速工程。**任务特定提示学习**将下游任务转换成屏蔽语言模型问题,由此在提供由任务特定模板定义的提示时,该模型生成作为屏蔽标记预测的文本响应。开拓性的模式开发训练(PET)方法(Schick & Schutze 2021a,b)结合了这种模式,并被证明在使用小三个数量级的模型的少量学习中胜过 GPT-3。然而,这种设置有几个限制:它需要多步训练,包括适应和几个 PLM 的集合;它利用特定任务的未标记数据;并且需要手工制作提示。ADAPET (Tam 等人,2021 年)通过利用更多的监督来训练模型,在没有任何未标记数据的情况下,在少量强力胶上的表现优于 PET。LM-BFF (Gao 等人,2021)通过将基于提示的微调(FT)与自动提示生成相结合,并动态选择任务示例作为输入上下文的一部分,提供了改进的少量微调性能。最近,出现了几个针对 NLP 中少数镜头学习的基准,如 RAFT (Alex 等人,2021),FLEX (Bragg 等人,2021),和 CLUES (Mukherjee 等人,2021)。RAFT 是一个真实世界的少量文本分类基准,它只提供 50 个样本用于训练,没有验证集。它包括 11 个实际的现实世界的任务,如医疗案例报告分析和仇恨言论检测,其中更好的性能直接转化为更高的企业价值。该基准包括 GPT-3 产生的结果,成为比较的标准基线。在一篇相关的论文(Schick & Schutze 2021c)中,研究表明,像 PET 这样基于提示的学习者在 RAFT 基准测试中表现出色,在 11 个任务中有 7 个任务的表现接近人类水平,而没有使用任何验证数据。在这篇博客中,我们介绍了 SetFit,一个经过调整的句子转换器模型,它通过少量数据进行微调,以解决现实世界中的文本分类挑战,如 RAFT。也就是说,验证 SetFit 是文本分类的通用灵丹妙药超出了本研究的范围。博客是这样组织的:我们从句子变形金刚相关的背景开始,然后提供改编方法的详细描述。由于目标基准缺乏任何验证数据,我们不得不根据外部相关任务搜索最佳参数集。我们选择 [SST-2](https://paperswithcode.com/sota/sentiment-analysis-on-sst-2-binary) 作为我们的验证数据集,并进行评估以选择 RAFT 实验的最佳参数。下一步是为 RAFT 任务微调 SetFit,并执行预测 RAFT 的测试数据,同时利用几种试探法和最佳实践。我们每项任务的表现都会在 RAFT 的排行榜网站上公布。在[发表后](https://huggingface.co/spaces/ought/raft-leaderboard) [2] SetFit 排名第二,紧随人类基线之后,在 11 个任务中的 7 个任务中超过了 GPT-3,尽管它比 x1600 小(见图 1)。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/20cb36cdcc46c6f46b4d90446036c420.png)*图 1: F1 对比:筏形基准*上塞特菲 vs GPT-3***方法******句子转换器(ST)** 是一种非常流行的方法,用于语义搜索、语义相似性和聚类。其思想是基于句子的语义签名对句子的唯一向量表示进行编码。如图 2 所示,在对比训练期间,通过在 Siamese 架构中采用 transformer 模型来构建表示(Remiers & Gurevych 2019),旨在最小化语义相似的句子之间的距离,最大化语义遥远的句子之间的距离。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/9db2b210d74f57926a1c2e33c633eae7.png)***图 2:连体 BERT 架构***当应用于句子对之间的大规模比较时,句子转换器产生非常有效的表示,这是信息检索任务中非常常见的情况。STs 对于其他流行的 NLP 任务也是有效的,例如情感分析和命名实体识别,因为它们将文本转换为数值结构,然后很容易用现成的机器学习(ML)工具包和流行的 NLP 生产框架(例如[spark NLP](https://nlp.johnsnowlabs.com/)【3】)进行缩放、操作和处理。**sentence-transformers [库](https://www.sbert.net/) [4]是 ST 表示的模型中枢,包括一个抽象 API 和代码示例,用于在生产中训练、微调和推理 ST。****用于文本分类的 ST**使用 ST 进行文本分类的想法并不新颖,它包括一个编码步骤和一个分类步骤(如逻辑回归)。ST 性能优于其他嵌入表示,但无法与交叉编码器(如 BERT)分类相媲美(Remiers & Gurevych 2019)。令人惊讶的是,我们没有发现任何以连体方式为文本分类执行端到端 ST 微调的工作。原因可能是 STs 是在语义相似性任务上预先训练的(即句子对传达相同/不同的意思),并且为了相同的目标而进一步调整它们是直观的。此外,直到最近,具有显著性能提升的新 ST 模型才作为语句转换器(例如基于 [MPNet](https://arxiv.org/abs/2004.09297) 的具有大数据训练的模型)和其他模型(例如 [SimCSE](https://arxiv.org/abs/2104.08821) )的一部分发布,并且它们可能仍在为非相似性任务进行探索。****SetFit —句子转换器微调**图 3 是 SetFit 的训练和推理阶段的框图。交互式代码示例可以在[这里](https://colab.research.google.com/github/MosheWasserb/SetFit/blob/main/SetFit_SST_2.ipynb)【5】找到。**训练阶段的第一步是从句子-变形金刚[6]模型中枢中选择一个 st 模型。以下步骤是设置训练类、填充训练数据加载器和执行微调。为了更好地处理少数镜头场景中数量有限的标记训练数据,我们采用了一种经常用于图像相似性的对比训练方法(Koch et al. 2015):训练数据包括正面和负面句子对,其中正面对是从同一类中随机选择的两个句子,负面对是从不同类中随机选择的两个句子。句子对生成伪代码如图 4 所示。在每个句子对迭代中,我们生成 2x *N* 个训练对,其中 *N* 是每个任务的训练样本总数。生成的句子对用于微调(拟合)ST 模型。**在微调步骤的最后,产生一个适应的 ST。接下来,使用适应的 ST 对句子训练数据进行编码,然后为了简单起见,利用编码的数据来训练逻辑回归(LR)。在推理阶段,每个测试句子都用适应的 st 编码,然后用 LR 模型预测其类别。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/39939ffecd811ef063f864f070f4fa0e.png)***图 3: SetFit 的示意图***![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/967259dca82924c2b9e41c0f1b5f12f5.png)***图 4:句子对生成伪代码*****实验****RAFT (Alex et al. 2021)是一个少数镜头分类基准,旨在通过将训练样本的数量限制为每个任务 50 个标记的示例,而不提供验证集,来匹配真实世界的场景。前者对于从业者来说是困难的,因为找到最佳的训练超参数集是具有挑战性的。影响 SetFit 性能的突出特征包括:*1.  *st 的类型:句子-变形金刚模型中枢中的 ST 有很多种类型,问题是如何为给定的任务选择最佳的 ST。*
2.  *输入数据选择:在一些 RAFT 任务中,提供了几个数据字段作为输入,例如标题、摘要、作者姓名、ID、数据等。这就产生了一个问题:哪些数据字段应该用作输入,以及如何组合它们?*
3.  *超参数的选择:如何选择最佳的微调超参数集(例如#epochs,句子对生成迭代的次数)?**最后,我们应用简单的试探法和常识来选择 ST 模型、输入数据字段、超参数和序列长度。在接下来的部分中,我们将描述选择这些参数的最佳实践。**ST models 句子变形库包含了大量可供选择的句子嵌入模型。模型名称对应于它们所基于的转换器的类型以及训练数据集。例如,“释义-mpnet-base-v2”模型使用释义相似性数据集用 mpnet 模型来训练。**对于如何为下游 NLP 任务选择最佳模型或训练数据集,没有明确的指导原则,尽管对于相似性任务存在一些经验法则。由于 RAFT 是一个文本分类数据集,我们假设它将受益于一个嵌入模型,该模型被训练来检测句子对之间的语义相似性,因为这有点类似于文本分类设置中类之间的相似性。因此,我们考虑嵌入使用“[所有](https://huggingface.co/sentence-transformers/all-mpnet-base-v2)或“[释义](https://www.sbert.net/examples/training/paraphrases/README.html)训练数据集训练的模型,并将其设计为通用模型,我们排除了使用 [NLI](https://paperswithcode.com/task/natural-language-inference) 或 [Q & A](https://paperswithcode.com/task/question-answering) 数据集训练的嵌入模型,这些数据集与文本分类任务非常不同。**我们只瞄准尺寸与 BERT-110M 相当的型号。我们还测试了 STs 的蒸馏版本,如 TinyBERT-80M 和 MiniLM-80M,但发现它们的性能与原始教师模型不可比(见附件 A)。**考虑到以上所有因素,我们为验证步骤选择了以下三个候选模型。这些模型在句子变形器的[句子嵌入性能](https://www.sbert.net/docs/pretrained_models.html)中排名最高,如表 1 所示。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/1ab45904f60551923d963f6b7ba86a4b.png)***表 1:语义相似性任务的性能和被选择用于在 SST-2 数据集上验证 SetFit 的三个候选 ST 模型的大小*****使用 SST-2 作为验证集**我们在 SST-2 数据集上验证了 SetFit,这是广泛使用的 GLUE 基准测试的一部分。SST-2 是试探性选择的,因为它与大多数 RAFT 基准任务具有几个共同的品质,例如:**●相对较短的序列长度(< 64)**●丰富的语义和句法结构,与其他分类器相比,Transformer 具有较高的准确性**●代表真实生活场景**SST-2 的选择不是 RAFT 的最佳选择,但可以作为替代验证集,因为 RAFT 不提供验证数据,也不为测试数据提供金标签。**通过从完整的 SST-2 训练数据中为每个类别随机选择少量训练样本(16 或 50)来模拟少炮设置,以适应和训练 ST。训练过程重复五次,每次使用不同的随机种子。通过对五次迭代的推理结果进行平均来产生最终的准确度。****ST 模型选择**表 2 显示了三个候选模型在 ST 模型微调(Fit)和不微调的情况下的性能,并与 PET 的性能进行了比较,PET 使用 RoBERTa 和 3 进行了标准微调(高等人,2021 年)。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/7924be4c145ab00ebc94207ede6ccdbd.png)***表 2:SST-2 数据集上不同 ST 模型的少量和完整训练集性能与基线的对比。*任务特定提示。* *标准微调。***上下文微调。据*报道(高等 2021)。**如表 2 中所示,三个候选模型相当可比,具有 50 个训练样本的“释义-mpnet-base-v2”模型证明了略好的性能。因此,我们选择这个模型来评估我们的方法在 RAFT 基准上的性能。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/04c7cf63f4f5dcea03c7bfa38c737c7f.png)***图 5:微调后(左)和微调前(右)在*paraphrase-MP net-base-v2*ST 输出的二元分类任务的训练向量的 2D 表示。k 代表每类训练样本的数量。蓝色圆点代表类别“0”,而橙色圆点代表类别“1”。***少量的历元、句子对生成的五次迭代和 ST 微调足以达到收敛,并在 2D 向量空间(使用 T-SNE 建立)中产生良好的可分性。图 5 显示了训练数据的 2D 投影,有和没有 ST 微调。可以看出,与没有微调的相同模型相比,微调步骤显著提高了少量训练数据的可分离性,这导致 SST-2 测试的精确度更高。****超参数** **选择**以下是基于 SST-2 数据集实验选择的参数。我们对所有的 RAFT 基准测试使用了相同的参数集,除了任务 4(见表 3 ),为了达到可接受的分离,我们将迭代次数增加到了 10 次。**● ST 模型= '释义-mpnet-base-v2 '**●train _ loss = CosineSimilarityLoss**●#纪元数= 3**●句子对生成迭代次数= 5**●批量= 16**●预热步骤= 10****数据字段选择**RAFT 基准测试中的任务 3、7 和 8 包含多个数据字段作为额外的元数据(如日期、个人姓名和职务)。在这些情况下,我们确保只保留一个主要的文本特征,该特征直观地包括用于正确分类句子的足够的上下文。表 3 显示了每个任务的可用数据字段,以及所选择的数据字段(以粗体显示),以及(mean+std)/max 文本长度。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/2e9fd700bf664c263e252405e1d0d03f.png)***表 3: RAFT 的数据字段和(均值+标准差)/最大文本长度*****序列长度选择**如表 3 所示,任务 3、4、7 和 8 包括长文本序列(> 256 个标记),这可能会导致意外结果(SetFit 仅针对 SST-2 进行验证)和微调期间的内存分配错误(取决于您的硬件设置)。在这些情况下,我们将输入文本长度限制为 128 个标记,如下所示:对于任务 4、7 和 8,我们选择了前 128 个标记,但是对于任务 3,我们选择了后 128 个标记,因为我们注意到正确分类的相关上下文通常位于文本输入的最后部分。****大量句子对生成迭代**对 SST-2 数据集实验的分析表明,五次句子对生成迭代和 ST 微调足以实现代表不同类别训练样本的向量的 2D 投影之间的良好分离(见图 5)。因此,我们对所有的 RAFT 任务使用了五次迭代,除了任务 4,为了达到可接受的分离,我们将迭代次数增加到 10 次。****其他最佳实践**没有对文本输入进行预处理。在 RAFT 数据集的任务 2 (Banking_77)中,由于标签样本具有相当稀疏的表示(77 个意图类别中只有 36 个在训练数据中包括至少一个样本),我们将标签的名称作为输入样本添加到该类别。这是一个微不足道的附加,因为在文本分类任务中,类的名称(或它们的描述)是给定的。****结果**表 4 显示了 RAFT 排行榜的快照(2021 年 11 月)。SetFit 的总分是 0.669,在人类基线之后排名第二,比 GPT-3 高出 0.042 分。令人惊讶的是,在基准测试的 11 个任务中,SetFit 在 7 个任务中超过了 GPT3,在 NIS 任务中,SetFit 甚至超过了人类基线。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/d9af1af8d35f384587ee1cff46fb5998.png)***表 RAFT 排行榜快照(2021 年 11 月)*****讨论****以下是基于将 SetFit 应用于 RAFT 基准测试的结果的一些想法:**●我们预计,ST 模型的集合,以及基于 RoBERTa large 等大型模型的 ST 模型,最终将在 RAFT 上实现更好的整体性能(如附录 A 中 SST-2 所示)。**●对于 NIS 任务,将文本输入长度限制在最后 128 个字符似乎是一个好的实践,对性能有积极的影响。**●将类别名称添加到 Banking_77 任务的训练数据中似乎非常有效,这可能导致比 GPT-3 更大的准确性优势(超过 0.2 点)。**●一个有趣的反常现象是,该表显示,一方面,在 NIS 和 OSE 任务中,SetFit 远远超过 GPT-3,甚至在 NIS 任务中超过了人类基线。另一方面,GPT-3 在 SOT 任务中的表现远远超过 SetFit 这可能与 SOT 任务比其他任务需要更多事实知识的直觉有关。**●通过将文本分类的一般问题解耦到句子嵌入生成和预测的两个阶段,实现了显著的准确性/效率提高,这乍一看似乎令人惊讶,但对于 SOTA 自然语言处理任务来说并不新鲜。事实表明,开放式问答的检索和阅读器架构(例如 Lewis 等人的 RAG)能够使小得多的模型获得与 T5–11B 等较大模型相当的结果。总的来说,SetFit 和 RAG 的分离带来了更高的健壮性(新数据/类别不需要重新训练)、可解释性和可伸缩性(更高级别的并行化)。**●SetFit 为 SST-2 数据集生成的结果(如附录 A 所示),例如使用“all-roberta-large-v1”模型的完整训练集的准确度为 95.4,这将使 set fit 在 [SST-2 排行榜](https://paperswithcode.com/sota/sentiment-analysis-on-sst-2-binary) [7]上排名前 20。这些基于句子表征的高质量结果与句子表征被认为仅传达浅层信息并因此导致准确度下降的常见假设相反。****结论和未来工作****我们提出了 SetFit,一种有效的少镜头文本分类方法,它基于流行的句子转换库,基于预先训练的 ST 表示和微调。SST-2 数据集上的实验和 RAFT 基准上的提交结果证明了 SetFit 与大两到三个数量级的模型(如 GPT-3 和 GPT-近地天体)相比的有效性,以及对比培训对进一步改进下游 NLP 任务的 STs 的重要性。在未来的工作中,我们打算探索如何用 SetFit 实现无监督学习,以及如何将其扩展到其他任务,特别是两句话文本分类,如 [NLI](https://paperswithcode.com/task/natural-language-inference) , [Q & A](https://paperswithcode.com/task/question-answering) 。****致谢****我们非常感谢柳文欢·佩雷格(英特尔实验室)、尼尔斯·雷默斯(拥抱脸)、卢克·贝茨(UKP —达姆施塔特)富有成效的评论和更正。**[1][https://ruder.io/recent-advances-lm-fine-tuning/](https://ruder.io/recent-advances-lm-fine-tuning/)**[2]最近,PET 进入 RAFT 排行榜,在人类基线之后排名第一,PET 基于比 SetFit 大 x2 的 ALBERT 模型(xxlarge,v2,235M 参数)。**[3]https://nlp.johnsnowlabs.com/**[4]https://www.sbert.net/**[5][https://colab . research . Google . com/github/MosheWasserb/set fit/blob/main/set fit _ SST _ 2 . ipynb](https://colab.research.google.com/github/MosheWasserb/SetFit/blob/main/SetFit_SST_2.ipynb)**[6]句子-默认情况下,变形金刚包括 ST 对象的微调选项。**[7][https://papers with code . com/sota/opinion-analysis-on-SST-2-binary](https://paperswithcode.com/sota/sentiment-analysis-on-sst-2-binary)****参考文献****汤姆·布朗、本杰明·曼、尼克·赖德、梅拉妮·苏比亚、贾里德·D·卡普兰、普拉富拉·达里瓦、阿尔温德·尼拉坎坦、普拉纳夫·希亚姆、吉里什·萨斯特里、阿曼达·阿斯凯尔、桑迪尼·阿加瓦尔、阿里尔·赫伯特沃斯、格雷琴·克鲁格、汤姆·海尼汉、雷文·蔡尔德、阿迪蒂亚·拉梅什、丹尼尔·齐格勒、杰弗里·吴、克莱门斯·温特、克里斯·黑塞、陈唐山、埃里克·西格勒、马特乌斯·利特温、斯科特·格雷、本杰明·切斯、杰克·克拉克、克里斯托弗·伯纳、萨姆·麦卡德里什、亚历克·拉德福德[语言模型是一次性学习者](https://arxiv.org/abs/2005.14165)。NeurIPS 2020。**[塞巴斯蒂安·鲁德](https://aclanthology.org/people/s/sebastian-ruder/),[马修·e·彼得斯](https://aclanthology.org/people/m/matthew-e-peters/),[斯瓦巴·斯瓦扬迪普塔](https://aclanthology.org/people/s/swabha-swayamdipta/),[托马斯·沃](https://aclanthology.org/people/t/thomas-wolf/) lf。[自然语言处理中的迁移学习](https://aclanthology.org/N19-5004/)。NAACL 2019。**雅各布德夫林张明伟肯顿李克里斯蒂娜图塔诺瓦。 [BERT:用于语言理解的深度双向转换器的预训练](https://arxiv.org/abs/1810.04805)。NAACL 2019。**提莫·希克和辛里奇·舒茨。2021a。[利用完形填空题进行少量文本分类和自然语言推理](https://arxiv.org/abs/2001.07676)。EACL 2021。**提莫·希克和辛里奇·舒茨。2021b。[重要的不仅仅是规模:小型语言模型也是很少尝试的学习者](https://arxiv.org/abs/2009.07118)。NAACL 2021。**德里克·塔姆,拉克什·梅农,莫希特·班萨尔,沙尚克·斯里瓦斯塔瓦,科林·拉弗尔。[改进和简化花样拓展训练](https://arxiv.org/abs/2103.11955)。EMNLP 2021。**提莫·希克和辛里奇·舒茨。2021c。[真正的带提示的少量学习——现实世界的视角](https://arxiv.org/abs/2111.13440)。**尼尔斯·雷默斯和伊琳娜·古雷维奇。句子-伯特:[使用连体伯特网络的句子嵌入](https://arxiv.org/abs/1908.10084)。EMNLP 2019**格雷戈里·科赫,理查德·泽梅尔,鲁斯兰·萨拉胡季诺夫。[用于一次性图像识别的连体神经网络](https://www.cs.cmu.edu/~rsalakhu/papers/oneshot1.pdf)。2015**天宇高,亚当费舍尔,陈 2021。[使预先训练的语言模型成为更好的少量学习者](https://arxiv.org/abs/2012.15723)。ACL 2021。**Neel Alex、Eli Lifland、Lewis Tunstall、Abhishek Thakur、Pegah Maham、C. Jess Riedel、Emmie Hine、Carolyn Ashurst、Paul Sedille、l . Alexis Carlier、Michael Noetel、Andreas Stuhlmüller。 [RAFT:一个真实世界的少镜头文本分类基准](https://arxiv.org/pdf/2109.14076v1.pdf)。NeurIPS 2021,数据集和基准跟踪。**乔纳森·布拉格,阿尔曼·科汉,凯尔·洛伊兹·贝尔塔吉。 [FLEX:统一评估少镜头 NLP](https://arxiv.org/abs/2107.07170) 。NeurIPS 2021。**苏伯拉塔·慕克吉、、郑国庆、萨加尔·侯赛尼、格雷格·杨、克里斯多夫·米克、阿瓦达拉、高剑锋。[线索:自然语言理解中的少量学习评价](https://openreview.net/pdf?id=VhIIQBm00VI)。NeurIPS 2021,数据集和基准跟踪。**Patrick Lewis、Ethan Perez、Aleksandra Piktus、Fabio Petroni、Vladimir Karpukhin、Naman Goyal、Heinrich Küttler、迈克·刘易斯、Yih Wen-tau、Tim rocktschel、Sebastian Riedel、Douwe Kiela。知识密集型自然语言处理任务的检索增强生成。NeurIPS 2020。****附录 A****![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/4f069fb1abf8dbbd22f20a15750a96b8.png)***图 6: SST-2 对句子转换模型的评估。** 3 个模型的集合:**释义-mpnet-base-v2、All-mpnet-base-v1 和 Stsb-mpnet-base-v2***# 使用 BERT 词义消歧和 T5 转换器的 Sentence2MCQ> 原文:<https://towardsdatascience.com/sentence2mcq-using-bert-word-sense-disambiguation-and-t5-transformer-e6bb5aaba29b?source=collection_archive---------14----------------------->## 使用 HuggingFace transformers 和 Gradio App 的实用人工智能项目![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/e6e4c00d57b524e22c694123ce04219d.png)作者图片# 实际使用案例![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/ee3ad79218ce4688bb8c101bf14307fa.png)来自[平面的图标图标](https://www.flaticon.com/authors/monkik)想象一下,**中学的英语老师**正在为第二天的课准备阅读理解**测验**。老师不会给出过时的评估**,而是会根据当天的热门新闻文章快速生成一些评估(mcq)。**一个**人在回路中的**管道可以是这样的,其中新闻文章被**总结**成几个重要的**句子**,并且从每个句子中提取一个**关键字**以形成一个 **MCQ** 问题。然后,教师可以使用 NLP**接受或拒绝**自动生成的问题,只保留**高质量的**问题进行测验。这为教师准备评估节省了大量的时间,同时也增加了学生的参与度,因为它基于当前的热门话题。# 投入我们程序的输入将是任何带有突出显示的关键字的**句子**(例如:cricket ),问题需要围绕该句子被框住。示例输入 1:

Mark's favorite game is cricket.


示例输入 2:

Mark is annoyed by a cricket in his room.


# 输出我们将把任何给定的句子转换成如下所示的 MCQ输出例如输入 1:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/da6ed761f0591c22833987423d669502.png)作者图片输出,例如输入 2:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/6f2ab2595205b1623b93ff71941f989f.png)作者图片请注意,我们能够智能地**将单词 **cricket** 在第一句中区分为**游戏**,在第二句中区分为**昆虫**,并相应地生成**干扰项**(错误的 MCQs)。**# 算法给定我们的输入句子 eg: **Mark 最喜欢的游戏是**cricket**,**我们提取**关键字** (cricket)和**句子**,并将其交给 **T5 转换器**算法,该算法被训练为接受**上下文**和**答案**作为输入,并生成**问题**。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/185bf67dc3d4eaf342bfa05a95427deb.png)作者图片我们并行地将句子( **Mark 最喜欢的游戏是**cricket**)** 和高亮显示的**关键词**作为输入传递给**基于 BERT 的**算法,该算法被训练来进行词义消歧(WSD)并从 [**Wordnet**](https://wordnet.princeton.edu/citing-wordnet) 中识别正确的**词义**。如果我们有一个句子**“蝙蝠飞入丛林,落在一棵树上”**和一个关键词**“蝙蝠”**,我们就自动知道这里我们说的是有翅膀的哺乳动物蝙蝠,而不是板球拍或棒球棒。虽然我们人类很擅长,但算法不太擅长区分这两者。使用算法来区分给定单词的确切**上下文含义**的想法被称为**词义消歧** (WSD)。[WordNet](https://wordnet.princeton.edu/) 是一个大型的英语词汇数据库。在 Wordnet**“bat”**有几个**义项**(上下文含义)一个表示板球拍,一个表示会飞的哺乳动物,等等。在给定单词的所有几种可能的含义(上下文含义)中,我们的目标是找到句子中给定单词的正确含义。我们将使用令人敬畏的 [BERT-WSD](https://github.com/BPYap/BERT-WSD) 项目,它是**预训练的**来识别句子中给定单词的**正确含义**。给定一个像`**(He caught a [TGT] bass [TGT] yesterday)**`一样突出显示**关键字**的句子,BERT-WSD 将对 Wordnet 中所有可能的词义进行评分和排序,如下所示。我们使用具有最高概率**的意义**并提取干扰物。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/e965815e615290072a9d06692b778bbf.png)图片来自 [BERT-WSD](https://github.com/BPYap/BERT-WSD) 回购## 使用 Wordnet 生成干扰项假设我们得到一个单词,比如" **Red** "并识别它的**义项**,然后我们使用 Wordnet 找到它的上位词。一个**上位词**是一个给定单词的更高层次的类别。在我们的例子中,**颜色**是**红色**的上位词。然后我们去寻找所有属于颜色组的**颜色**的下位词(子类别),它们可能是紫色、蓝色、绿色等。因此,我们可以使用紫色、蓝色、绿色作为给定 MCQ 的干扰物(错误答案选项),该的正确答案是**红色**。紫色、蓝色、绿色也被称为红色的**的同音异义词**。因此,使用 Wordnet,提取一个给定单词的**同音异义词**就能得到该单词的**干扰词**。所以在下图中,红色的干扰物是蓝色、绿色和紫色。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/d3eb91c50e6bc2df52fdeb90de6ebdd6.png)来源:[https://en.wikipedia.org/wiki/Hyponymy_and_hypernymy](https://en.wikipedia.org/wiki/Hyponymy_and_hypernymy)类似地,对于我们的例子,我们提取单词 **Cricket** 的同音异义词,得到 **Football** 、 **Polo** 、**Ultimate fribsee**等作为干扰词。## 谷歌 Colab 笔记本这里有一个易于使用的谷歌 Colab 笔记本,上面有所有必要的代码。# 密码安装必要的库:

!pip install --quiet transformers2.9.0
!pip install --quiet nltk
3.4.5
!pip install --quiet gradio==1.4.2


连接您的个人 google drive 来存储经过训练的 BERT-WSD 模型

from google.colab import drive
drive.mount('/content/gdrive')


从[此处](https://entuedu-my.sharepoint.com/:f:/g/personal/boonpeng001_e_ntu_edu_sg/EiWzblOyyOBDtuO3klUbXoAB3THFzke-2MLWguIXrDopWg?e=08umXD)下载预先训练好的 BERT WSD 模型点击链接左上方的下载按钮,下载一个名为“**Bert _ base-augmented-batch _ size = 128-lr = 2e-5-max _ gloss = 6 . zip**”的文件。将 zip 文件放入 Google drive **home** 文件夹。使用下面的代码将上面的文件解压到你的 Google drive 中。

import os
import zipfilebert_wsd_pytorch = "/content/gdrive/My Drive/bert_base-augmented-batch_size=128-lr=2e-5-max_gloss=6.zip"
extract_directory = "/content/gdrive/My Drive"extracted_folder = bert_wsd_pytorch.replace(".zip","")# If unzipped folder exists don't unzip again.
if not os.path.isdir(extracted_folder):
with zipfile.ZipFile(bert_wsd_pytorch, 'r') as zip_ref:
zip_ref.extractall(extract_directory)
else:
print (extracted_folder," is extracted already")


初始化 BERT-WSD 模型为 BERT-WSD 创建预处理步骤,为问题生成初始化 **T5 转换器**并定义 Wordnet **干扰项**提取函数。运行 get **MCQs** 函数,将一个给定的句子和高亮显示的关键字作为输入。上面的输出是-

Question: What is Mark's favorite game?
Correct Answer : Cricket
Distractors: ['Ball Game', 'Field Hockey', 'Football', 'Hurling', 'Lacrosse', 'Polo', 'Pushball', 'Ultimate Frisbee']
Sense from wordnet: a game played with a ball and bat by two teams of 11 players; teams take turns trying to score runs


如你所见,我们输入了一个句子**(马克最喜欢的游戏是**板球* *。)**并正确生成正确答案的**问题**和相关**干扰项**(板球)。现在让我们使用 [Gradio](https://www.gradio.app/) 并构建一个漂亮的 **GUI** 来做同样的事情。在 **Colab** 笔记本本身中,你得到一个漂亮的 **GUI** 来输入任何**文本**并在那里看到**输出**。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/2dc91142ecc23d0d357915bcdcf08d56.png)作者图片有时,如果**单词**不在 **Wordnet** 中,你不会得到任何干扰物。如果你想找到替代的方法来找到干扰物,在这里找到我的详细博客。## 格拉迪欧是什么?有了 [Gradio](https://www.gradio.app/) 你可以为你的 **ML** 模型创建**易于使用的图形用户界面**,只需在你的 colab 笔记本上写几行代码。最棒的是。你可以通过一个**简单链接**(例如: [12345.gradio.app](http://12345.gradio.app/) )与你的非技术经理/朋友分享 GUI,以便在网络上的任何地方试用。Gradio 不仅仅是为了文字。你也可以上传一张图片作为输入,并在里面做任何预测。# 使用自然语言处理的问题生成——教程我推出了一个非常有趣的 Udemy 课程,名为“使用 NLP 生成问题”,扩展了这篇博文中讨论的一些技术。如果你想看一看,这里是[链接](https://www.udemy.com/course/question-generation-using-natural-language-processing/?referralCode=C8EA86A28F5398CBF763)。# 结论希望你喜欢我们如何使用 NLP 解决从给定的句子生成 MCQ 的现实问题。祝 NLP 探索愉快,如果你喜欢它的内容,请随时在 [Twitter 上找到我。](https://twitter.com/ramsri_goutham)# 句子片段标记器去神秘化> 原文:<https://towardsdatascience.com/sentencepiece-tokenizer-demystified-d0a3aac19b15?source=collection_archive---------1----------------------->## [入门](https://towardsdatascience.com/tagged/getting-started)## 深入探究句子分词器的内部工作原理,为什么它如此强大,以及为什么它应该成为你的首选。你可能会开始关心符号化。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/f91f82c6e3f440d1f7328fec3038d39f.png)由[克里斯蒂安·埃斯科瓦尔](https://unsplash.com/@cristian1?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片我将是第一个承认学习记号化方案可能很无聊,如果不是非常痛苦的话。通常,在训练自然语言时,选择和实现标记化方案只是增加了一层复杂性。它会使你的生产流程变得复杂,用不合时宜的[OOV]或[UNK]来破坏气氛,或者可能只是把你送进 unicode 地狱的深处,在那里你想知道为什么是“字符串”!= "字符串"。然而,就像你的第一杯真正的好酒,或者你第一次品尝高质量的寿司,一旦你品尝了这种好东西,你就会以完全不同的眼光看待剩下的东西。这篇文章旨在讨论 SentencePiece,并展示它使标记化过程变得多么轻松。在此过程中,您可能会认为标记化并没有那么糟糕。# 什么是句子片断?令人惊讶的是,它实际上并不是一个标记,我知道,误导。它实际上是一种从预编译列表中选择标记的方法,根据提供的语料库优化标记化过程。句子片断[1],是一个实现*子字正则化*算法[3]的包的名字(此处可用【2】)(全部由同一作者 Kudo,多久完成)。在这篇文章的持续时间里,我将继续使用 SentencePiece 来指代算法和它的包,因为这有望减少混淆。如果你想要关于如何使用实际软件的说明,看一看文章结尾或参考文献[18]中的 [colab 链接](https://colab.research.google.com/github/google/sentencepiece/blob/master/python/sentencepiece_python_module_example.ipynb)。## 句子的优点1.  它是用 C++实现的,速度快得惊人。你可以在几秒钟内训练一个 10⁵语料库的分词器。
2.  标记化的速度也快得惊人。这意味着您可以直接在原始文本数据上使用它,而无需将标记化的数据存储到磁盘。
3.  子词正则化就像数据扩充的文本版本,可以极大地提高模型的质量。
4.  它是空白不可知的。您可以像训练英语或法语一样轻松地训练非空格描述的语言,如汉语和日语。
5.  它可以在字节级工作,所以你几乎不需要使用[UNK]或[OOV]令牌。这不仅仅是针对句子。
6.  本文[17]: *字节对编码对于语言模型预训练*——【https://arxiv.org/pdf/2004.03720.pdf】T2 来说是次优的SentencePiece 功能强大,但它的目的到底是什么?提到第(3)点,它使我们能够训练子字正则化任务,我们现在解释它。## 子字正则化目标我们在这一小节的目标是明确开发子字正则化的学习目标。首先,让我们考虑如何给序列分配概率。一般来说,我们喜欢认为序列包含一个方向,这意味着有一个向前和向后的方向。你按顺序说话。股票会随着时间的推移而上涨和下跌。因此,我们可以根据序列在早期的条件分布来确定它的分布。给定一系列单字 X = ( *x_1,…,x_n),*,通过重复应用贝叶斯规则,我们将其概率重写为![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/25767aa5fcb66c855e7713620ff329fc.png)熟悉朴素贝叶斯方法[5]的读者应该知道这个公式。在朴素贝叶斯中,我们做一个条件独立性假设(强假设),如果 *X* 以某个变量 *y,*即 P(X|y)为条件,那么我们可以假设 *p(x_2|x_1,y) = p(x_2 | y),p(x_3|x_2,x_1,y) = p(x_3|y)* 等等。也就是说,如果我们得到了关于另一个东西 *y* 的信息,我们可以完全忘记 x_i 所依赖的所有变量 *x_{j < i}* 。为了给这种简化做准备,我们考虑一下人工机器翻译的问题(NMT)。这里,我们想要评估给定输入句子 *X.* 的目标句子 *Y* 的概率 *P(Y|X)* 因为 *Y* 和 *X* 都是序列,我们可以写出概率【3】![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/fe96e7e7c0e35a6536f5893af8a01262.png)这里,小写的变量表示实际的记号,大写的表示这些记号的顺序。θ代表我们的模型参数(神经网络的权重)。就目前情况来看,这个公式不太正确。实际上, *X* 和 *Y* 可以由指数数量的可能子字序列构成。想想分解“你好”这个词。我们可以用很多方式来标记:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/93791a39c3f5c295f4fa0a050f96ec05.png)甚至像“hello”这样的简单单词也可以呈现出指数数量的可能标记化所以实际上,我们应该用特定的序列表示法 **x** 和 **y** 来代替左边的 X 和 Y。SentencePiece 承认这一现实,并利用这一点。然后,我们可以将 NMT 任务的完整分段平均成本函数写成![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/9af60da0b1e602fa0d5e2f9769ede305.png)|D|是可能分割的数量,x 和 y 都是从这些分割上它们各自的分布中提取的。l 是成本函数,P 和之前一样。这个公式有点吓人,但是你不需要想太多。在实践中,我们能做的是去掉期望值 E ……,用单个随机分段的**代替 **x** 和 **y** 。**就是这样。我们通常为训练 NMT 模型所做的其他事情都没有改变(这包括短长度惩罚和任何其他模型攻击)。“使用一个样本”的近似应该为任何使用过变分自动编码器的人所熟悉[6],其中隐藏状态的期望值类似地由一个样本大小来近似。既然我们已经定义了培训任务,我们必须回答实际问题;是的,很酷,但是我们如何在指数数量的状态上建立概率分布呢?!像科学中的其他事情一样,我们拼命地作弊和近似,直到有人告诉我们我们不能。## 单字语言模型从本质上来说,SentencePiece 只是另一个普通的老式单字符模型。这意味着,考虑仅仅两个令牌的联合分布 *P(x_1,x_2)* 都太难了,更不用说指定 *P(X)* 所需的 *n* 了。所以,我们只是做了近似:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/1347bf25a79f19306abd8d71009c712e.png)受制于标准化约束:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/6cbe3320f73524c9dee44ae4050df306.png)其中每个子词(这里称为一元词)出现的概率独立于所有其他子词。是的,这是一个极端的假设,但从经验上来看并不太有害。Unigram 语言模型非常常见,并且已经存在了一段时间。一种称为字节对编码(BPE)的流行方法,首先由 Gage [7]在信息文献中介绍,后来由 Sennrich 等人在 NMT 的上下文中使用。艾尔。[8](一篇可读性很强的论文 btw)是一种基于用最少所需信息位对文本进行编码的简单方法。BPE 的一个小变体叫做 WordPiece,是另一个流行的标记器,我们建议读者参考其他容易理解的摘要文章,如[9]以获得更好的概述。原则上,SentencePiece 可以构建在任何 unigram 模型上。我们唯一需要喂它的东西是1.  单字概率
2.  训练语料库然后,我们只需在语料库上训练句子片段标记器,我们可以自由地执行子词正则化(或不正则化)NMT 训练。美妙之处在于,如果我们不想使用子词正则化,我们甚至不需要使用它。我们也可以使用 SentencePiece 作为快速标记器,让我们可以动态处理原始文本。尽管做了极端的 unigram 假设,但如何训练标记器一点也不明显,这也是这篇文章的原因之一。我们将在下一节详细解释这一点。但是为了实际做一些具体的事情,最终我们必须选择一个 unigram 模型。在这篇文章中,我们将使用 BPE,因为它简单明了,我们可以当场自制一个版本。# 字节对编码总的想法是这样做:1.  浏览你的语料库,找到所有的“字节”,即不可约的字符,所有其他字符都可以从这些字符中构建出来。这是我们的基地。它确保我们几乎总能重建任何看不见的输入。
2.  在整个语料库上运行滑动窗口(实际代码略有不同)并找到最频繁的二元模型。二元模型由当前可见子词列表中的连续子词形成。因此,“hello”的计数是{“he”:1,“El”:1,“ll”:1,“lo”:1 }。
3.  选择最常见的二元模型,将其添加到子词列表中,然后合并语料库中该二元模型的所有实例。
4.  重复直到你达到你想要的词汇量。通过总是挑选最频繁的二元模型(即字节对),我们本质上最小化了编码我们的语料库所需的编码长度,因此整个“最小化信息”的事情。实际上,每当我们想要找到一个新的字节对时,循环遍历整个语料库是没有效率的。相反,我们在开始时循环一次,找到所有的*单词*(不是子单词,是实际的单词),并创建*词汇表,*,这是一个将单词与其字数相匹配的字典。然后,我们每次只需要在字典中循环单词,如果一个单词出现 20 次,那么它的任何子单词也将出现至少 20 次。BPE 是一种增量的确定性方法。我们总是可以通过遵循相同的合并顺序来标记化。我想暂停一下,强调一下这一点的重要性。如果我们不遵循同样的顺序,一切都会乱套。在 BPE,我们首先对单词进行预处理,用一个特殊的标记来表示单词的换行。考虑这个词船→B . o . a . t假设我们的语料库谈论了很多关于蛇(大蟒蛇)、早餐和帆船的内容,那么我们可能会看到“蟒蛇”、“燕麦”和“船”这些词。如果我们谈论蛇多于早餐,我们可能会得到令牌化(针对贪吃蛇爱好者)船→宝儿 t(给早餐爱好者)船-> B 燕麦如果我们从不谈论船只,那么令牌化者就永远不会排队。一个有蟒蛇,另一个有燕麦,他们不会同意。撇开这个题外话,让我们直接进入 BPE 代码,因为这不是我们的主要关注点,它实际上在原始论文中已经写了一半了[8]标准的 BPE 格式就是我们上面写的 Boat。由空格分隔的子词,带有单词结束标记。我们选择标记“_”而不是来更好地与句子对齐。首先,在“initialize_vocab”方法中,我们通过获取所有单词及其计数来初始化词汇表,然后通过找到所有不可约字符来初始化标记。get_bigrams 方法是确定最频繁二元模型的辅助方法。merge vocab 负责更新 vocab 以使用新的 bigram,并返回 bigram → bytepair merge,以便我们可以将操作存储在一个列表中。最后,find_merges 函数完成实际查找二元模型的工作,fit 函数只是协调所有的 helper 方法。# 如何训练句型太好了!现在我们已经有了字节对编码器,可以在现场制造子字了,我们可以开始训练句子了。我们假设我们有一个大的二元模型集合,比我们最终想要的 vocab 大小要大。为了训练,我们想要最大化获得语料库的特定标记化 *X=(x_1,…,x_n)* 的对数概率,给定单字概率 *p(x_1),…,p(x_n)* 。由于只观察到完整的未标记序列 *X* ,实际的标记化( *x_1,…,x_n)* 未观察到*。这是使用 EM 算法的经典设置[10]**所以应该很简单吧?只要翻到任何一本旧的 ML 教科书,复制/粘贴 EM 结果。嗯,…没有。问题是这些 *x_i* 的尺寸都不一样!**原来,语句块的代码实现使用贝叶斯方法训练,而论文描述使用最大似然 EM 方法。如果这是令人困惑的,只要知道一般贝叶斯=更难。要理解我所说的,你要么必须真正钻研 C++代码,要么只看我在参考文献[11]和[12]中告诉你的地方。**由于这一点将会出现,我们需要展示如何解决狄利克雷过程的基本贝叶斯 EM 问题。由于有点跑题,详情请见**附录一**。现在,我们将继续前进。**句型训练的目标如下。我们希望最大化对数似然**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/7f05e558652c997cbef494540a1512b9.png)**其中 **x** 是单字序列,S( **x** )表示所有可能序列的集合。同样,这些是隐藏变量,我们只看到未标记的语料库!为了解决这个问题,我们引入了 EM 类型的算法。如果你熟悉 EM,你会注意到步骤实际上是向后的,我们做一个 ME 方法。尽管名字很花哨,但它实际上非常直观和简单。这些步骤是:*1.  *初始化单字概率。记住 P( **x** ) = P(x_1)…P(x_n)所以一旦我们有了单字,我们就有了任何序列的概率。在我们的代码中,我们只是使用 BPE 频率计数来接近目标。*
2.  *M-step:给定当前概率,计算*最可能的*单字序列。这定义了单个标记化。实现这一点需要一些思考。*
3.  *步骤 e:给定当前的标记化,通过计算标记化中所有子词的出现次数来重新计算单字概率。频繁词出现的概率就是该词出现的频率。在实践中,对此进行贝叶斯化(见附录)并不困难,而是进行计算**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/0716b7fdbf4f2ac55d340ef89aa1b6fa.png)**这里,c_i 是当前标记化中子词(unigram) *i 的计数。m 是子词的总数。Psi 是 digamma 函数。箭头指示我们如何贝叶斯化。***4.重复步骤 2 和 3,直到收敛。对数似然理论上保证单调增加,所以如果不是这样,你就有一个错误。**我们就快到了,我们需要的最后一部分是如何计算第二步。*## *寻找最佳标记化**如果所有子字长度相同,这将是维特比算法的经典应用[13]。但是唉,生活并不是那么简单。维特比算法适用于以下问题*> *你有一些隐藏态 z_1,…,z_n,你想从 z_1 →z_2 →… →z_n 跃迁,你知道*跃迁矩阵* A_ij,给出从 z_i^{(1)} → z_j^{(2)}的概率,其中 I 和 j 是隐藏态维数,上标是序列顺序。所有的转换都得到相同的矩阵。你可以使用维特比来构建*一个*最优路径**问题是 A 不在相邻的州之间。为了理解这可能是一个问题,让我们用图解法表示一个简单的记号化过程。**考虑对单词“hello”进行标记。假设我们有子词:**{“他”、“h”、“ll”、“e”、“o”、“地狱”}。**然后,我们可以生成下面的“网格状”图形(它不是网格,因为我们没有显示到所有可能的隐藏状态(即字符)的转换,并且转换不限于最近的邻居,我们可以向右跳过不止一个框。):**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/c2d22d09fa7869714bd0cad3adc46944.png)**来源:现作者。虚线描绘了基本的字符/字节间距。箭头指示哪些单词可以通过允许的标记到达。每一列代表一个隐藏的州 z_i^{(j)}.I 的维数由基本字符的数目给出,上标表示序列顺序。**每个箭头代表一个转换,我们也可以认为它带有一个概率,由从箭头的尾部(不包含)到头部(包含)创建的标记的一元概率给出。现在的目标是选择箭头,使我们以尽可能高的概率到达<eos>——序列的结尾。</eos>**这个问题有最优子结构,可以用动态规划来解决。假设我们处于状态(4)。有三个箭头,一个红色的,一个蓝色的,一个绿色的。(4)处的最大概率是三种可能选择中的最佳路径:来自红色、蓝色或绿色。在等式中:**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/86e0057c36e7a8c1b46cbf14a2b956e8.png)**我们差不多准备好编码了,但是还有一个问题。我们如何找到我们画的那些箭头呢?我们如何有效地做到这一点?为此,我们需要利用 Trie 数据结构[14]。有点有点难以用语言解释(双关!)所以让我们展示一下当前问题的 trie 是什么样子的:**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/b3cf05538a89daa37b391aa3a0ad08c5.png)**来源:现作者。这是子词词典{'h ',' he ',' hell ',' hello'}对应的 Trie。还有额外的节点 <sos>-e- <end>,同样对于‘o’和‘l’也是如此,为了清楚起见,我们省略了这些节点。</end></sos>**根节点是序列开始标记<sos>。任何时候我们遇到 and <end>节点,它表示从<sos>到<end>的路径中的所有东西都是有效的子字。词根<sos>将以我们的子单词列表中的每个唯一字符的一个分支开始。随着可用子词的增长,我们在 trie 中创建了更多的分支。Trie 将成为我们的标记器用来存储和检索子词的基本数据结构。</sos></end></sos></end></sos>**下面是 python 中的一个基本 Trie 实现,它将满足我们的所有需求:**我们现在已经得到了我们需要的一切。计算序列的实际算法可以根据我们是想要最佳序列(Viterbi)还是 n_best 而变化,因此我们将为此保留两个独立的函数。这类问题的动态编程解决方案很老了,所以它有其他的名字;它被称为前向-后向算法,是用于训练有向图形模型的和积算法的特殊子集[13,pg。613].更复杂的算法包括前向 DP 后向 A*算法[15]和前向滤波和后向采样算法(FFBS) [16]。我们的解决方案会更接近后者。**在展示代码之前,还有最后一点需要注意。当执行 max over p_{i <j we="" brute="" force="" search="" p_="" where="" class="my">T 是最长子字的长度。这可能是一个很小的数字,不应该损害我们的 O(N)算法。</j>**好了,下面是我们的**全句子训练器。**目前你只需要注意方法 1)前进 _ 步 2)后退 _ 步**向前和向后的步骤实现了我们刚刚谈到的算法。尽管后退一步还没有被讨论过。当我们计算向前的步长时,我们还存储在任何给定索引处结束的最大令牌的长度。这样,我们可以回溯所有导致最大概率的箭头,因为箭头的长度完全指定了转换!这是因为标记化的文本没有改变,这意味着隐藏状态被设置。我们实际上只是在选择每个角色跳多少步。**完整的 EM 步骤现在很容易组合在一起。我们遵循前面概述的步骤,并使用贝叶斯化的 EM 步骤,这就是为什么我们必须从 scipy 导入 digamma 函数。**还有一件事(再次双关)使这变得完整。一般来说,我们希望能够固定我们的 vocab 大小。sentencepiece 首先聚合比它实际需要的更多的子词标记。然后,我们执行修剪“回合”,从而优化 EM 算法,然后移除或修剪掉最不可能的 20%令牌(概率在 E 步骤中计算)。我们重复这个过程,直到我们达到我们想要的 vocab 大小。**fit 方法现在负责运行 EM 步骤,在每一轮后修剪,然后在需要进一步减少时继续。那是培训师。*# *子字采样**最后一部分是子字采样。免责声明,我们不使用相同的算法,但它足以生成随机令牌化并提供概念证明。**在向前向后传递中,我们只存储最优序列。为了找到可替换的记号化,在每个索引处,不是只保存最佳结束子词,而是保存 *n_best* 个结束子词。现在,我们可以通过从提供的列表中随机抽取每个最终子词来进行标记化。这给了我们子字正则化!**为什么是免责声明?好吧,如果你考虑一下,我们实际上仍然有和以前一样的问题,在本地随机抽样结束子词并不能保证完全标记化将是第二、第三、第四或任何最好的。要做到这一点,请查看实际的句子实现[2]或 FFBS 算法[16]。**随机抽样器由`generalized_forward_step`和`generalized_backward_step`方法提供。下面是一个输出示例*

Sample 1: ['h', 'e', 'l', 'l', 'o', '', 'w', 'or', 'l', 'd']
Sample 2: ['h', 'e', 'l', 'l', 'o', '
', 'w', 'o', 'r', 'l', 'd'] Sample 3: ['h', 'e', 'l', 'l', 'o', '_', 'w', 'or', 'l', 'd']


# *结论**这是一个深入句子的漫长过程,但我希望它是值得的。现在你对它的工作原理有了更多的了解,你可以很快忘记一切,只需要*从文章顶部的要点中拼凑出你需要的**(仍然是双关语!!):**1.  **速度速度速度。可以在训练时直接处理文本数据。**
2.  **子词正则化→更好的模型、数据扩充、改进的语言建模预训练**
3.  **可以很容易地标记非空白语言,如日语和汉语**
4.  **不再有[UNK]代币(嗯…几乎不再有[UNK]代币)****如果你有任何 NLP 任务,请强烈考虑使用 SentencePiece 作为你的分词器。对于使用实际的软件,我发现下面的 google colab 教程非常有用****<https://colab.research.google.com/github/google/sentencepiece/blob/master/python/sentencepiece_python_module_example.ipynb>  谢谢,祝令牌化愉快!# 附录在这里,我们讨论如何贝叶斯化你的 EM 算法。也可以跟着参考文献[12]一起读。> 讨论基于平均场理论。我们将简单地使用它的主要结果,即用于确定隐藏参数/状态的后验分布的耦合方程。更多阅读见[13]的第 10 章。变分推理的目标是确定我们的模型的未观测参数和/或状态的后验分布。我们从编写模型的日志证据开始:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/738301ec30aa67b3cc8d81bb432cba30.png)这是我们之前定义的重复。还没有新消息。回想一下,X 是未标记化的语料库,S(x)表示所有可能的序列,粗体小写 **x** 表示特定的标记化。由于日志内部的求和,该模型是难以处理的。为了取得进展,我们首先引入隐藏变量 pi,表示单字概率。我们进一步将这些概率置于狄利克雷先验条件下,创建贝叶斯模型。我们写道:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/a1fbbf372f486c0b6e2de8c6ac3cbfa5.png)概率 p(π|α)是对称的狄利克雷分布:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/004e4765f04a6d99c7238b6c184ebe14.png)任何特定序列的概率都是其单字概率的乘积![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/06923cfdde5bd34908bf36637c2ce675.png)x_{nk}二进制值(只能是 0 或 1 ),表示序列中的第 n 个位置是否由词汇表中的第 k 个子字给出。我们现在对后验分布作平均场近似![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/45608fa251df9bc1a4b0a9408ba5c07b.png)下标纯粹是标签;它们不代表维度之类的东西,它们只是名字。从这里,我们可以利用一般公式计算后验概率:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/fbb1faab8b1d17930d39c277f2ca568d.png)类似地,对于圆周率![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/5c6b0b1bd48d7b17c9795767309aedf8.png)将我们对对数模型证据的定义插入到期望值中,并取出所有未平均的项,我们找到两个方程![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/9ec5412903ee466f5cef3697ebdc7a6f.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/544e2095d4a955610db613dc45722148.png)现在我们做如下观察。顶部的方程精确地是狄利克雷分布的形式,具有修正的先验。此外,由于 z_nk 是二元变量,我们可以将它们的期望作为各种单字的计数。我们用变量来定义它![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/7f3b04988d3de7a289dc6d14299ae5dc.png)这也只是一元计数。现在我们知道了圆周率的分布,我们可以用已知的结果计算它的期望值。B.21 或去维基百科——快得多]![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/970985e5419bbf655caf5ce6bf34d143.png)把这个代入 log q_z(z)的方程,我们就找到了分布![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/3478982b0159e73f451119db4ca74a73.png)这正是我们在权重 pi 上的分类分布!换句话说,我们立刻意识到括号里的东西就是我们的权重 pi。我们唯一需要提到的是,当应用贝叶斯化方法时,我们设置α= 0。这具有增强高计数单字和减少低计数的效果。# 参考[1]工藤,多久和约翰·理查森。" Sentencepiece:一个简单且独立于语言的子词分词器和去分词器,用于神经文本处理." *arXiv 预印本 arXiv:1808.06226* (2018)。[2]https://github.com/google/sentencepiece/[3]工藤多久。"子词正则化:用多个候选子词改进神经网络翻译模型." *arXiv 预印本 arXiv:1804.10959* (2018)。[4]史蒂文·斯科特。2002."隐马尔可夫模型的贝叶斯方法:21 世纪的递归计算."美国统计协会杂志。[5][http://cs229.stanford.edu/notes-spring2019/cs229-notes2.pdf](http://cs229.stanford.edu/notes-spring2019/cs229-notes2.pdf)[6]金玛、迪德里克·p 和马克斯·韦林。"自动编码变分贝叶斯." *arXiv 预印本 arXiv:1312.6114* (2013)。[7]菲利普·盖奇。"一种新的数据压缩算法."《用户杂志》第 12 卷第 2 期:第 23 至 38 页(1994 年)。8 Rico senn rich、Barry Haddow 和 Alexandra Birch。"具有子词单元的稀有词的神经机器翻译."进行中。ACL 的(2016)。[9][https://huggingface.co/transformers/tokenizer_summary.html](https://huggingface.co/transformers/tokenizer_summary.html)[http://cs229.stanford.edu/notes-spring2019/cs229-notes8.pdf](http://cs229.stanford.edu/notes-spring2019/cs229-notes8.pdf)【11】**具体到第 271 行**[https://github . com/Google/sentence piece/blob/master/src/unigram _ model _ trainer . cc](https://github.com/google/sentencepiece/blob/master/src/unigram_model_trainer.cc)[12]如果你看了[11]你会发现链接,特别是第 178 页的:[https://cs . Stanford . edu/~ pliang/papers/tutorial-ACL 2007-talk . pdf](https://cs.stanford.edu/~pliang/papers/tutorial-acl2007-talk.pdf)[13]Christopher m . Bishop,“模式识别和机器学习”斯普林格(2006 年)。**看第 629 页**https://en.wikipedia.org/wiki/Trie——抱歉,我没有更好的参考资料,我真的不知道自己是怎么知道这些的。[15]永田正明。"一个随机的日语词法分析器,使用了一个向前-向后-最佳搜索算法."进行中。科林的(1994)。16 史蒂文·斯科特。"隐马尔可夫模型的贝叶斯方法:21 世纪的递归计算."美国统计协会杂志(2002 年)。[17] Bostrom、Kaj 和 Greg Durrett。"字节对编码对于语言模型预训练来说不是最理想的."arXiv 预印本 arXiv:2004.03720 (2020)。[18][https://colab . research . Google . com/github/Google/sentence piece/blob/master/python/sentence piece _ python _ module _ example . ipynb](https://colab.research.google.com/github/google/sentencepiece/blob/master/python/sentencepiece_python_module_example.ipynb)[19]墨菲,凯文 P. *机器学习:一个概率观点*。麻省理工学院出版社,(2012 年)。**# 情感分析:深入研究理论、方法和应用> 原文:<https://towardsdatascience.com/sentiment-analysis-a-deep-dive-into-the-theory-methods-and-applications-322f984f2b48?source=collection_archive---------24----------------------->## 在开始编码之前,你需要知道的关于情感分类的所有事情![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/cb30c9c6e8efd86487bbeec0423883df.png)照片由[你好我是尼克](https://unsplash.com/@helloimnik?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)情感分类是数据科学中对初学者最友好的问题之一。然而,这并不容易做到。开发了各种模型来对数据集执行情感分析。为了确保您选择的模型最适合您手头的数据,了解它们是如何形成的以及它们是如何工作的非常重要。在本文中,我将总结情感分析背后的理论,解释其背后的基本原理,并展示一些成功的情感分类的应用。这篇文章是我在[做的关于主题建模和情感分析](https://local.cis.strath.ac.uk/wp/extras/msctheses/papers/strath_cis_publication_2733.pdf)的系统性文献综述的结果。## 什么是 NLP 中的情感分析?情感分析是自然语言处理的一个子集。NLP 是一个研究领域,研究使用计算手段从自然语言解码数据的能力。NLP 还研究了如何将这些解码数据整合到机器学习和统计编程软件中。通过 NLP,计算机程序可以使用机器学习算法对文本数据执行数据分析,这些文本数据在公共源格式中非常丰富。此外,他们可以从数据中提取意义,或者说是语义。NLP 领域的主要目标是在计算机程序和人类之间架起沟通的桥梁。程序不断得到改进,通过处理、分析和合成将语言和语音数据解码成有意义的语义见解。## 情感分析怎么定义?情感分析是一门学科,[旨在使用自然语言处理方法从用户的文本数据](https://books.google.bg/books?hl=en&lr=&id=YggqAwAAQBAJ&oi=fnd&pg=PT7&dq=Heimann+and+Danneman,+2014&ots=8MxjoRlM9u&sig=NpCXUJRuGO3ZXyf_sbxdoHsOnRs&redir_esc=y#v=onepage&q=Heimann%20and%20Danneman%2C%202014&f=false)中提取定性特征,如情感、观点、想法和行为意图。如[张等人(2018)的情感分析研究](https://onlinelibrary.wiley.com/doi/abs/10.1002/widm.1253)所述,社交媒体文本对情感分析研究特别有用,因为它们:*   用来表达一种观点
*   充斥着主观的文字。在学术文献中,社交媒体文本被定义为缩写文本。对这种类型的文本进行情感分析更具挑战性,因为模型可以处理的上下文更少。相比之下,对长格式文本(如新闻文章)进行情感分析的挑战性较小。简短文本中情感提取的有效性依赖于[更高级方法](https://www.aclweb.org/anthology/C14-1008.pdf)的应用,例如深度卷积神经网络。由于创造性语言、俚语和缩写的使用,社交媒体数据尤其需要[更复杂的信息检索方法](https://www.aclweb.org/anthology/S17-2126.pdf)。如果不加以考虑,这些因素的存在会影响情感分析模型的效率。## 情感分析是怎么做的?传统的情感分析研究旨在[通过将给定文本分类为积极、消极或中性来检测其极性](https://dl.acm.org/doi/abs/10.1145/3185045)。这种分类需求被认为是传统情感分析的关键限制之一。一些学者认为,传统形式的情感分析无法解决现代表达的复杂性,因为它无法捕捉客观性和主观性。例如,如果一个模型的任务是在假新闻或某人的观点和事实之间进行分类,传统的情感分析将是不够的。更先进的方法试图[识别文本](https://dl.acm.org/doi/abs/10.1145/3185045)中多种不同的情感表现,通过分析用于自我表达的语言来表明情绪和观点。这种方法通常旨在同时检测和提取主题模型。为此,经常使用深度学习方法,如[卷积神经网络(CNN)](https://www.aclweb.org/anthology/P16-2037.pdf)。CNN 还用于简写文本的情感分析,如多个研究论文中所指出的(例如,多斯桑多斯和(2014) ,[卡莱等人(2018)](https://arxiv.org/pdf/1804.03673) ,[唐等人(2015)](https://arxiv.org/abs/1512.01100) )。## 有哪些模型可以用于情感分析?情感分析可以通过监督、半监督和无监督的机器学习算法来完成。受监督的机器学习模型是最难在上获得用于情感分析的数据的[,因为它需要数据子集的标签,以此来训练模型。](https://www.sciencedirect.com/science/article/pii/S0020025516300445)半监督方法利用少量标记样本作为训练数据,作为提高分类精度的手段,一个例子是 da Silva 等人(2016 发表的[模型,其中使用支持向量机(SVM)分类器对 Twitter 数据进行分类。](https://www.sciencedirect.com/science/article/pii/S0020025516300445)## 可以对什么文本进行情感分析?可以在[文档级、句子级和方面(词)级](https://www.sciencedirect.com/science/article/pii/S0167739X17308634)进行情感分析。短格式文本,如来自社交媒体的内容,最好在句子级别使用情感分析进行分析,因为它们通常由一个或几个句子组成。也有模型分析单个单词,假设同一个句子中的单词具有相同的情感。这样的方法就是[唐等人(2019)的隐藏话题-情绪转换模型](https://www.sciencedirect.com/science/article/pii/S0950705118305586),将连续句子中的话题和情绪建模为马尔可夫链。这种方法能够同时检测文档级和句子级情感。## 除了文本,情感分析还可以在其他类型的数据上进行吗?[近年来,多模态情感分析已经发展成为一个领域](https://www.sciencedirect.com/science/article/pii/S0950705118303897),该领域提出的模型利用了弱监督深度学习方法的最新发展。多模态事件主题建模也已出现,这已被证明在[消费者行为预测分析和社会学](https://dl.acm.org/doi/abs/10.1145/2659521)领域大有可为。总之,多模态环境中的主题建模和情感分析被认为是改善人机交互的一种方式。如何应用的一个例子是自动语音识别领域。# 做情感分析的两种主要方式是什么?## #1 使用预先开发的人工构建的情感词典。情感分析最初是使用预先开发的、人工构建的情感词典来执行的。此类词典包括:*   [主观性词表](https://www.aclweb.org/anthology/D08-1014.pdf)
*   WordNet-Affec
*   [SentiWordNet](https://www.sciencedirect.com/science/article/pii/S095070511630137X)
*   SenticNet
*   阿芬
*   感觉词
*   所谓的
*   意见词典
*   文字统计每一个都有不同的等级和不同的字数。这种词典已被用作模型开发的基础,例如*   极性分类算法(PCA),对推文情感进行分类
*   增强型表情分类器(EEC)
*   改进的极性分类器
*   SentiWordNet 分类器(SWNC)。其中,PCA 表现出了优越的性能。**该方法的优势和局限性**这些方法在以下方面很有用:*   区分主观言语和客观言语
*   将情绪分为积极、消极或中性。作为一种限制,它们只能让研究者主要从作者而不是读者的角度来提取情感。## #2 使用机器学习方法除了基于词典的方法之外,可以使用机器学习方法来执行情感分析。这可以通过[在人类标注数据集](https://www.sciencedirect.com/science/article/pii/S0167739X17308634)上训练的统计模型来完成,因此——利用半监督学习。另一种方法是结合多种浅层机器学习方法。例如,对于推文的情感分析, [Ahuja 等人(2019)](https://www.sciencedirect.com/science/article/pii/S1877050919306593) 得出结论,TF-IDF 在特征提取方面比 N-Grams 表现更好。在他们论文的研究样本中,TF-IDF 与逻辑回归的结合被认为是最有效的。集成分类器也被证明是解决词典方法的一个限制的好方法。具体来说, [2018 年的一项研究](https://www.sciencedirect.com/science/article/abs/pii/S0925231218310154)从读者的角度探讨了多标签情感分类的问题,将模型应用于新闻数据集。该研究证明了集成分类器与其他方法相比的优越性。如前所述,深度学习技术也可以应用于情感分析。**该方法的优势和局限性**每一种观点都有其自身的局限性,并选择在分析的准确性或普遍性上做出妥协。下面是对常见情感分析机器学习方法的优势和局限性的总结:*   N-gram 模型(或条件随机字段)通过序列标记(例如,词性标记或浅层解析)来工作。他们很好地捕捉工作顺序和语法。它们的缺点是特征维数高。
*   半监督学习方法有利于快速简单地确定文本的极性。他们不太擅长确定文本中的主观性或客观性。
*   深度学习方法擅长对大型数据集进行元级特征提取,通常比 N 元模型表现更好。当数据中有大量噪音时,如俚语、缩写、甚至表情符号,它们的表现并不好。这使得他们不适合社交媒体情绪分析。
*   当特征被组织成组时,多个内核学习模型工作。这实现了多模态情感分析,但是计算很慢。情感分析中的主要挑战是语言的模糊性,或者说,很多时候在说语言时,可能会出现混合的语义属性。这使得分类算法难以执行其功能。查看程序输出和分类的人也很难对给出语义的上下文进行分类,这可能会妨碍分类的整体有效性及其在现实环境中的可用性。尽管如此,与人类的评估和洞察力相比,情感分类在速度方面提供的好处超过了这些障碍。## 为什么公司要实施情感分析?公司希望利用情感分析和自然语言处理技术的原因有很多。基于我的研究,我总结了五个主要原因,为此,我也将提供在现实生活中如何做到这一点的例子。## #1 增加竞争优势情绪分析和预测行为建模技术的实施被认为是组织竞争优势的来源,并且是学者们推荐的。## **#2 评估一家公司消费者网络的力量**情感分析也可以用来衡量消费者网络的力量。这与衡量口碑营销的效率有关。它还可以用于跟踪在线社会团体成员之间的个人推荐。这可以使公司根据同行的推荐,用个性化的网络广告来[目标消费者](https://dl.acm.org/doi/abs/10.1145/1232722.1232727)## #3 利用公开的、用户生成的、随时可用的数据文本数据也比数字数据更容易获得,可以说高级人类语言包含了大量的复杂性和细微差别。这导致情绪分析被研究为各种环境中各种商业问题的潜在解决方案,例如用于检测道路事故或股票市场预测的社交媒体的模式识别,等等。## #4 识别模式,根据数据对市场变化做出准确的预测Asur 和胡伯尔曼(2010) 认为对社交媒体传播的深刻理解有助于准确预测未来事件的结果。他们提供的例子是使用 Twitter feeds 数据来预测票房成功的实时分析软件。这超过了好莱坞证券交易所的信息。Twitter 数据还被一个认知模式识别系统用于聚类分析[,该系统在任何主流报道渠道之前获取正在发生的道路交通事件的实时信息。](https://www.sciencedirect.com/science/article/pii/S0747563216305258)类似的研究有助于证实社交媒体平台是集体智慧的汇集。因此,公司受到激励,将机器学习应用于数据挖掘和行为预测,以准确告知商业用户出于各种目的的未来行动:*   [市场预测](https://www.sciencedirect.com/science/article/pii/S0957417414003455) —如股票市场
*   结果预测——例如[政治选举](https://www.sciencedirect.com/science/article/pii/S0950705114001920)
*   [危机](https://link.springer.com/chapter/10.1007/978-3-319-10831-5_9)管理
*   理解[个人和集体情绪反应](https://www.sciencedirect.com/science/article/pii/S0957417413000043)沟通——Lidl 的一个社交媒体活动采用了类似的方法由于我们人类的不可预测性,所有这些研究都预测了在对人类情感、反应和社会行为进行智能分析过程时的预测误差,并指出这是模型的局限性。## #5 效率、处理速度和减少的人为错误由于机器学习软件在执行文本分析方面的现有限制,公司目前有大量人力资源相关的支出用于员工手动验证数据。然而,当人类自己完成这些任务时,效率甚至更低,主要与数据转换所需的时间有关。想象一下,任何人浏览成千上万条推文并对它们进行分类会多么低效!这提供了一个创造价值的机会,通过持续改进模型,解决现有的业务问题,在行业中运作。自主解决方案的实施可以降低数据解释中的人为错误风险。## 最后的想法一般来说,自然语言处理和机器学习面临的挑战之一是,有用的和与问题相关的信息通常被播种在大规模混乱聚集的数据中。另一个挑战是,当人工代理考虑这些数据时,这些数据可能无法提供任何见解,或者可能被认为对业务问题毫无用处。这些挑战也反映在情感分析中,情感分析是自然语言处理的一个子集。这引发了理解语言结构并从中挖掘的研究浪潮:用户意图、情感和主观性。这种模型是组织的目标,因为它具有以下潜力:*   竞争优势增强,
*   预测行为和反应的能力,
*   能够更好地锁定处于不同消费阶段的消费者
*   减少人工参与的可能性,从而提高效率,降低运营成本。通过监测公众情绪,企业可以变得更适应市场。# 意大利语的情感分析和情感识别(使用 BERT)> 原文:<https://towardsdatascience.com/sentiment-analysis-and-emotion-recognition-in-italian-using-bert-92f5c8fe8a2?source=collection_archive---------14----------------------->## [思想和理论](https://towardsdatascience.com/tagged/thoughts-and-theory)## 意大利语情感分析和情感识别的数据集和软件包。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/cf0136797d6c536d9f482cec36d06276.png)我们搭建的情绪情感分类库截图。# 情感分析和情感识别识别文本中的情感是更好地理解人们谈论某事的基础。人们可以谈论一个新事件,但正面/负面的标签可能还不够。被某件事激怒和被某件事吓到,区别很大。这种差异就是为什么在文本中考虑情绪和情感是至关重要的。有很多关于英语情感分析和情感识别的研究。在谷歌上快速搜索会给你带来不同的可能算法,这些算法可以为你处理情绪/情感预测。但有些语言缺乏数据,其中一种语言是意大利语(但有一些工作,例如 Sprugnoli,2020)。所以我们([黛博拉](https://dnozza.github.io/)、[德克](http://dirkhovy.com/)和[你真正的](https://federicobianchi.io/))试图提供这个问题的解决方案。我们为意大利情绪和情感预测创建了一个新的数据集,并对 BERT 模型进行了微调。在这篇博文中,我们描述了我们创建这个新数据集的经历,解释了我们做的一些事情和我们的结果。我在这里描述的内容也可以在同行评议的研究论文中找到:费德里科·比安奇、德博拉·诺扎和德克·霍维(2021)。 **FEEL-IT:意大利语的情感和情绪分类**。第 11 届主观性、情感和社交媒体分析计算方法研讨会。由于这些方法的可用性对社区至关重要,您可以在网上找到:*   [我们的 python 包](https://github.com/MilaNLProc/feel-it)
*   [我们的拥抱脸情感模型](https://huggingface.co/MilaNLProc/feel-it-italian-sentiment)
*   [我们的拥抱脸情感模型](https://huggingface.co/MilaNLProc/feel-it-italian-emotion)# 感觉:意大利语的情感和情绪分类![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/abd1320c83f5eaeb716b4be410eba4bd.png)## 为情绪和情感识别建立新的数据集当创建一个新的数据集时,我们需要解决的一个问题是,它需要代表领域。一个人不可能收集两天三个关键词的推文。收集的推文过于依赖于领域,使得训练的模型不够通用,无法应用于不同的领域。相反,我们采取了不同的方法:我们决定每天收集 1000 条与 Twitter 上的热门话题相关的推文。通过这种方式,我们每天都能获得最相关的推文,而且因为每天都有新的话题,我们收集了很多不同的东西。这种方法让我们获得了一个数据集,其中包含了从足球比赛到政治等广泛话题的推文。然后,我们用情绪来注释推文,排除那些没有可识别情绪的推文。最终,我们得到了 2037 条带注释的推文:912 条**愤怒**,103 条**恐惧**,728 条**喜悦**,294 条**悲伤。**虽然这个数据集并不庞大,但正如我们所说的,它涵盖了广泛的不同主题,并在更广泛的情绪和情感分类任务中非常有用。您可以在下表中看到一些带注释的推文示例(以及英文翻译):![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/3555089666d64611ea7ed8f149070bbb.png)在我们的数据集中,一些带有情感注释的推文的例子。正如你所料,这个过程在注释方面的成本很高,因为我们丢弃了大部分推文。然而,它具有非常高的精确度,因为我们从广泛的主题中收集了推文,并且我们有精确的注释。## 微调(嗯)伯特(o)作为最近预训练时期的标准,我们用我们提出的数据集微调了 BERT 模型。BERT 是自然语言处理中最流行的神经结构之一。微调 BERT 允许我们有一个健壮的分类模型来预测我们的标签。微调是允许我们调整 BERT 模型的权重来执行我们的分类任务的操作。对于许多语言,有许多不同的 BERT 模型(参见 Nozza et al .,2020 的综述和 [BERTLang](http://bertlang.unibocconi.it/) )。这里,我们使用 [UmBERTo](https://github.com/musixmatchresearch/umberto/) ,一个非常高效的意大利 BERT 模型。特别是,我们微调了在[通用抓取](https://huggingface.co/Musixmatch/umberto-commoncrawl-cased-v1)数据集上训练的 UmBERTo 模型。## 情感分类为了评估我们的数据集和情感分析模型,我们将我们的 FEEL-IT UmBERTo 与另一个数据集上的相同模型进行了比较: [SentiPolc16](http://www.di.unito.it/~tutreeb/sentipolc-evalita16/) 。两个模型都根据各自的数据进行了微调。SentiPolc 带有一个训练集和一个测试集。因此,我们评估了下面这个有趣的研究问题:是使用“感觉”好还是“感知”好?我们在 SentiPolc 的测试集上对 FEEL-IT 和 test 进行了微调,并将其与 SentiPolc 的训练集上的微调和 SentiPolc 的数据集上的测试进行了比较。请注意,在训练集中使用 SentiPolc 的模型应该有很大的优势,因为我们希望训练和测试是相似的。在下图中,你可以看到结果。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/bd47fe1e39f93016bcc68f15be1f41b1.png)在 senti pol 16 测试集上使用 FEEL-IT 和 senti pol 16 作为训练集的比较结果。使用感觉-它更好。截图来自我们的 HuggingFace 知识库。然而,这些结果表明,使用 FEEL-IT 比使用之前最先进的数据集 SentiPolc 要好得多。## 情感分类现在,除了我们的感觉之外,Twitter 上没有情感分类的数据集。因此,我们运行了一个简单的交叉验证,表明一个微调的翁贝托模型可以让我们达到 **0.71** 宏 F1!那是一个极好的结果!## 创建 Python 库:感受一下我的一个爱好是写代码,我试着做一些其他人可以使用的库。因此,黛博拉和我一直在开发一个[小库](https://github.com/MilaNLProc/feel-it),它包装了 HuggingFace 的内部 API,为情绪和情感预测提供了一个简单的接口。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/cf0136797d6c536d9f482cec36d06276.png)我们的库超级好用!:)这个库的好处是,它允许你用很少的编码工作进行快速的情感和情绪分类:你可以加载你的熊猫数据帧,提取文本,将其传递给库,并得到结果。这正是我们现在要做的。首先要做的是: **pip install -U feel-it** 在命令行中:)下面是我们将要使用的小文件:现在,让我们导入我们的分类器。包装器将为您处理模型的下载。现在,让我们开始预测吧!我们只需要使用我们感兴趣的分类器的预测方法。分类器将句子列表作为输入——在本例中,我们将从我之前展示的 CSV 文件中获取。你看到了吗?有了我们建的库,用意大利语做感悟和情感分类超级容易!## 参考诺扎博士、比安奇女士和霍维博士(2020 年)。**那个什么【面具】?理解特定于语言的 BERT 模型。** *arXiv 预印本 arXiv:2003.02912* 。比安奇,f .,诺扎,d .,&霍维,D. (2021)。 **FEEL-IT:意大利语的情感和情绪分类**。第 11 届主观性、情感和社交媒体分析计算方法研讨会。斯普鲁尼奥利河(2020 年)。 **MultiEmotions-it:一个新的意大利观点极性和情感分析数据集。**第七届意大利计算语言学会议,CLiC-it 2020(第 402–408 页)。## 承认非常感谢黛博拉和德克对此的评论!# 如何用 Python 对音频文件进行情感分析> 原文:<https://towardsdatascience.com/sentiment-analysis-assemblyai-python-a4686967e0fc?source=collection_archive---------5----------------------->## 探索如何使用 AssemblyAI API 提取语音中的情感![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/0599f78ee416999734c4e281b6443776.png)照片由[伯爵克里斯](https://unsplash.com/@countchris?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/happy-sad?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)## 介绍情感分析指的是为了检测输入文本中的情感而对文本数据执行的一系列过程——作为自然语言处理的一部分。这种情绪通常可以分为积极、消极或中性,尽管这并不标准。相反,你可能更喜欢建立一个模型,最终输出更广泛的极性,如非常积极、积极、中性、消极和非常消极。此外,你可以试着预测一种情绪,而不是极性(快乐、悲伤、愤怒等)。).由于机器和深度学习方法的应用,情感分析在过去几年中得到了很大的发展,这些方法可以非常准确和及时地执行这项任务。情感分析工具的一些最常见的应用包括品牌监控和产品情感,帮助企业解释客户反馈并增强他们对客户需求的理解在接下来的章节中,我将演示如何使用 **AssemblyAI API** 和 **Python** 对音频文件执行[情感分析。](https://docs.assemblyai.com/guides/sentiment-analysis)## 对音频文件执行情感分析如果你想跟随这个教程,那么你首先必须从 AssemblyAI 获得你的 [API 密匙(这是免费的!).现在您已经有了访问键,我们现在可以准备将发送到 API 端点的请求头了:](https://app.assemblyai.com/signup)导入请求库并定义请求头—来源:[作者](https://gmyrianthous.medium.com/)接下来,我们现在需要将我们的音频文件上传到 AssemblyAI 的托管服务,该服务将使用将在后续请求中使用的`upload_url`进行响应。上传音频文件到 AssemblyAI 的 API 托管服务—来源:[作者](https://gmyrianthous.medium.com/)现在,我们已经检索到了作为上一个调用的响应的一部分的上传 URL,我们现在可以开始获取音频文件的转录。**请注意,在一次呼叫中,我们还可以请求执行情感分析。**就这么简单!使用 AssemblyAI API 执行语音到文本和情感分析—来源:[作者](https://gmyrianthous.medium.com/)注意,对于上面的调用,我们同时执行语音到文本和情感分析!为了指示 API 也执行后者,我们需要将`sentiment_analysis`参数(设置为`True`)传递给`POST`请求。值得一提的是,目前 AssemblyAI API 的情绪分析功能能够输出三种情绪,即`POSITIVE`、`NEGATIVE`和`NEUTRAL`。不过,请留意他们的[文档](https://docs.assemblyai.com/guides/sentiment-analysis),因为他们在不断改进他们的 API 及其功能。## 解读回应上一步/调用中的调用的响应结构应该与下面给出的结构相同。语音到文本输出将在`text`键下可用,情感分析的结果也将是`sentiment_analysis_results`键下响应的一部分。注意,对于情感分析结果中的每个片段,您还可以获得特定音频片段的时间戳(`start`和`end`)以及`sentiment`。AssemblyAI API 端点在执行情感分析时获得的响应—来源:[作者](https://gmyrianthous.medium.com/)## 完整代码下面是作为本教程一部分的完整代码。总而言之,我们首先必须将我们的音频文件上传到 AssemblyAI API 的托管服务,以便检索映射到要转录的音频文件的 URL。然后,我们在对 AssemblyAI 的脚本端点的单次调用中执行语音到文本和情感分析,以便检索音频文件的文本形式以及各个片段的情感。本教程的完整代码——来源:[作者](https://gmyrianthous.medium.com/)## 最后的想法在今天的文章中,我们讨论了什么是情感分析以及在什么样的环境下有用。此外,我们展示了如何利用 AssemblyAI API 对音频文件进行情感分析。他们的 API 附带了许多我们在今天的指南中没有真正讨论过的特性。请务必查看他们在[官方文档](https://docs.assemblyai.com/overview/getting-started)中的完整列表以及他们的[产品更新](https://changelog.assemblyai.com/)。此外,看看这篇文章后面的文章,它可以帮助你开始语音转文本和主题摘要。[**成为会员**](https://gmyrianthous.medium.com/membership) **阅读媒介上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。**<https://gmyrianthous.medium.com/membership>  **你可能也会喜欢**</summarize-audio-video-files-assemblyai-c9126918870c>  </real-time-speech-recognition-python-assemblyai-13d35eeed226>  </speech-recognition-python-assemblyai-bb5024d322d8> # 情感分析——比较 3 种常用方法:朴素贝叶斯、LSTM 和 VADER> 原文:<https://towardsdatascience.com/sentiment-analysis-comparing-3-common-approaches-naive-bayes-lstm-and-vader-ab561f834f89?source=collection_archive---------5----------------------->## 不同方法的优缺点研究(带有示例代码)*注:本帖代码可在* [*这里*](https://github.com/kevinclee26/sentiment_analysis_classification) 找到![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/983d0c81797da6ef2518836fbb2b21c2.png)情感分析(图片由作者提供)**情感分析**或**意见挖掘**,是 **NLP** (自然语言处理)的一个子领域,旨在从文本中提取态度、评价、意见和情感。受客户互动向电子邮件、聊天室、社交媒体帖子、评论、评论和调查等**数字格式快速迁移的启发,情感分析已成为分析组织必须执行的不可或缺的一部分,以了解他们在市场中的定位。需要说明的是,情感分析并不是一个新的概念。事实上,它一直是 **CRM** (客户关系管理)和**市场研究**的重要组成部分——公司依靠更好地了解他们的客户来发展和创新。最近的增长主要是由客户互动记录的可用性和可访问性以及处理这些数据的计算能力的提高所推动的。这一进步确实以有意义的方式使消费者受益。组织比以往任何时候都更愿意听取其成员的意见来进行改进。有许多方法可以进行情感分析。在本文中,我们将探索三种这样的方法:1) **朴素贝叶斯**,2) **深度学习 LSTM** ,以及 3) **预训练的基于规则的 VADER 模型**。我们将重点比较模型的简单开箱即用版本,并认识到每种方法都可以进行调整以提高性能。我们的目的并不是要详细说明每种方法是如何工作的,而是从概念上研究它们如何进行比较,以帮助确定何时应该选择一种方法。**## 情感分析的背景情感分析的目标范围从正面到负面。与其他 NLP 工作一样,它通常被认为是一个**分类**问题,尽管当精度很重要时,它可以被视为一个回归问题。情感分析过去是通过大量劳动力通读和人工评估文本来完成的。这种方法**成本高**并且容易出现**人为错误**。为了实现这一过程的自动化,公司寻求先进的分析方法来解决这一问题。情感分析的挑战在于人们表达和解释**情感极性**和**强度**的方式不同。此外,单词和句子可以根据上下文有多种含义(称为**多义性**)。虽然其中一些问题可以缓解,但像任何分析任务一样,几乎总是要在速度和性能之间进行权衡。我们回顾了三种通用方法,每种方法都有自己的优点和缺点:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/d70279d13fab3f6ff6e8b53cfd52b1e4.png)优势和劣势分析## 数据集介绍除了突出显示**概念差异**之外,我们使用 [**感知 140**](https://www.kaggle.com/kazanova/sentiment140/code) 数据集来**基准**性能。这个数据集包含 160 万条推文和相应的情绪标签(正面和负面)。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/30dc83367c38e16b8b7fd94aac909818.png)原始资料## VADER 词典和基于规则的情感分析工具我们从使用流行的 [**VADER**](https://github.com/cjhutto/vaderSentiment) (价感知词典和情感推理机)工具的一个容易应用的方法开始。其核心是,VADER 使用全面、高质量的词汇(约 7500 个特征)和复杂的语言规则来产生情感分数。情感词典的构建和验证(统计上)在它的[发表的论文](http://comp.social.gatech.edu/papers/icwsm14.vader.hutto.pdf)中有详细的记载——这个巨大的成就怎么说都不为过。

// vader_lexicon.txt
...
good: 1.9
happy 2.7
awesome 3.1
bad -2.5
sad -2.1
catastrophic -2.2
...


如上面的例子所示,VADER 词典是一个字典,它为每个特征分配一个在-4(最极端的负面)和 4(最极端的正面)之间的**预先确定的**情感分数,这些特征可以是一个单词、一个首字母缩略词或一个表情符号。VADER 策划了一个基于**化合价的**词汇,能够检测情感的**强度**和**极性**两个方面。这与诸如否定、缩写、连词、助词、程度副词、大写、标点和俚语等强大的**修饰语**相结合,用于计算输入文本的分数。这些修饰符被实现为一个基于规则的模型,它改变初始价分值。慷慨的 VADER 开发者使它开源且易于使用:

import SentimentIntensityAnalyzer class

from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer# polarity_scores method of SentimentIntensityAnalyzer
SentimentIntensityAnalyzer().polarity_scores('Today is a good day.')# output:


VADER 产生一个得分`compound`,该得分**概括了**输入文本的情感强度。它是通过对词典中每个特征的效价分数求和来计算的,根据规则进行调整,然后归一化到-1(最极端的负面)和+1(最极端的正面)之间。[文档](https://github.com/cjhutto/vaderSentiment)将`compound`分数描述为“**标准化加权综合分数**”。此外,`pos`、`neg`、`neu`分数代表属于每个类别的文本的比例(例如基于规则的增强)。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/ce4339b70a5c118b84c580f0ea065872.png)VADER 计算vader _ 情操 _ 分析. py基于规则的模型易于理解,实现起来也很快,这使得它成为情感分析的一个很有吸引力的选择。它们取决于拥有强大的词汇和全面的语言规则。基于规则的模型的挑战在于词汇的创建和验证是**耗时的。他们努力应对可能很重要的词汇特征的无限组合的覆盖范围,以及当单词有不同含义时跨领域的适用性问题。此外,该方法仅评估**单个单词**,而忽略使用它们的**上下文**。这往往会导致错误,特别是对于**讽刺**和**讽刺**。**使用 VADER —我们能够在我们的测试数据集上产生 **72%** 的准确率。VADER 认为,在社交媒体文本中常见的表情符号和首字母缩写词/首字母缩写词在该领域可以表现良好,但在其他领域可能表现较差。重要的是要注意,VADER 确实产生精确的极性分数,我们为了比较的目的将其分类为正或负。*P.S .其他词汇还有*<https://liwc.wpengine.com/>**(语言查询和字数统计)和* [*从新*](https://www.uvm.edu/pdodds/teaching/courses/2009-08UVM-300/docs/others/everything/bradley1999a.pdf) *(英语词汇情感规范)。**## *自然语言处理的机器学习**虽然基于规则的解决方案已经被证明是有效和可靠的,但是它是严格的,并且受到可能过期的词典的限制。手动**创建**和**验证**一个全面的情感词典是困难的。相反,我们希望转向一种机器学习解决方案,通过算法将输入文本与相应的标签关联起来。在训练过程中,成对的文本和标签(情感)被输入到机器学习算法中,以创建能够对新文本进行预测的模型。值得一提的一个重要警告是,**监督机器学习**需要对数据进行标记,这可能意味着数据中的任何**主观性**和**偏差**都会在模型中得到反映。与预先训练的模型相比,定制模型给予**对输出的更多控制**,并且适用于特定的应用。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/7a056229b64a02b0dbf0b92162608ac6.png)**机器学习 NLP 文本分类过程*## *朴素贝叶斯**[朴素贝叶斯分类器](https://en.wikipedia.org/wiki/Naive_Bayes_classifier)根据可能相关条件的**先验知识**使用概率进行预测。换句话说,它使用每个词汇特征在训练数据的正面或负面文本中出现的条件概率来得出结果。**NLP 的机器学习需要**预处理**从原始文本中提取特征。具体来说,它需要在算法能够处理它们之前将文本数据转换成数字表示,即**矢量化**。对于朴素贝叶斯,我们将构建一个简单的 **DTM** (文档-术语矩阵)用于模型消费,尽管还可以包括其他特征,如文本长度、发布时间/位置、命名实体等。DTM 倾向于产生一个**宽的特征空间**,因为整个语料库的词汇表中的每个独特的单词或短语都成为一个特征。我们包括一些数据清理步骤,以帮助**降维**和**模型**(分类器)**性能**。关于 NLP 数据预处理的更多信息可以在[这里](/what-you-need-to-know-about-data-preprocessing-and-linguistic-annotations-for-natural-language-439d42f2f355)找到。通过对 **TF** (术语频率)进行计数,并用 **IDF** (逆文档频率)对它们进行加权,来构建向量表示。我们可以使用 [**N-grams**](https://en.wikipedia.org/wiki/N-gram) (连续术语)来捕捉文本中的一些上下文,例如否定。N-gram 上下文是一个**钝器**,不能总是正确地捕捉表达式。它们还会给模型增加过多的功能,从而对模型产生负面影响。**我们应用以下转换来生成训练/测试数据:**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/377919eff8be1e012f164d5ed049e848.png)**朴素贝叶斯数据预处理管道(加权频率计数)**naive _ bayes _ 情操 _ 分析. py**朴素贝叶斯模型使用相当容易理解的特性。它支持**大规模**情感分析工作,因为训练所需的计算速度很快。然而,它也有一些明显的缺点。作为一个**概率分类器**,它高度依赖先验知识,因此训练数据必须完整且具有代表性。缺乏良好的训练数据会导致对看不见的数据或**不在词汇表中的**文本的推理不佳。如果数据中存在偏差或不平衡,它也会受到影响。此外,它假设特征是相互独立的,这意味着 DTM 中的词汇特征在所有句子中的贡献是相等的,而与文本中的相对位置无关。朴素贝叶斯 DTM 模型(具有 30 万个一元和二元特征)训练很快(< 7 分钟)。它产生了准确度为 **86%** 的训练分数和准确度为 **79%** 的测试/验证分数。***对于更长更宽的数据集,可以使用*[*Spark*](https://spark.apache.org/)*快速训练模型,这里使用* [*这里使用*](https://gist.github.com/kevinclee26/95ef550f53df7d6fae6c332425367028) *。**## *深度学习**深度学习允许以更复杂的方式处理数据。一种 [**长短期记忆**](https://en.wikipedia.org/wiki/Long_short-term_memory) 模型,或称 LSTM,是一种<https://en.wikipedia.org/wiki/Recurrent_neural_network>**【RNN】递归神经网络,用于处理<https://en.wikipedia.org/wiki/Temporal>**数据。既然我们认为一个句子中特征(词)的顺序是有意义的,这就是我们使用的神经网络架构。**********深度学习在计算上是昂贵的,并且在高维、稀疏向量的情况下做得不好(**性能差**和**收敛慢**)。当我们从原始文本中提取特征用于模型训练时,我们需要将它们表示为**密集**的向量。一种这样的技术是将每个文本转换成数字的序列,每个数字映射到词汇表中的一个单词。更进一步,我们需要使用**单词嵌入**将具有相似用法/含义的单词映射到相似的实数向量(而不是索引)。如果没有单词嵌入,模型会将单词的索引号误解为有意义。单词嵌入将所有单词放入一个多维向量空间,因此它们的相似性可以通过**距离**来测量。使用开源预训练模型(如 [Word2vec](https://www.tensorflow.org/tutorials/text/word2vec) 、 [GloVe](https://nlp.stanford.edu/projects/glove/) 或 [fastText](https://fasttext.cc/) )或定制神经网络(**无监督** **学习**)模型来生成单词嵌入。当训练自定义单词嵌入时,它可以单独完成,也可以与手头任务的神经网络模型联合完成(作为附加层)。这是我们采用的方法,因为它倾向于导致**针对**数据的**上下文**以及**目标**的嵌入。与稀疏(几十万维)的文档术语矩阵相比,使用单词嵌入产生具有更少(几百)维的向量,同时捕捉语义相似性。单词嵌入被认为是深度学习挑战自然语言处理问题的关键突破之一。**********我们应用以下转换来生成训练/测试数据:**********![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/b6cdc390e88bf1b1dc2f604ca62ef0cd.png)**********单词嵌入+ LSTM 数据预处理流水线**********![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/eb762cb2c73c6c88b5a27fcc158e7612.png)**********文字嵌入 LSTM 建筑**********word _ embedding _ lstm _ 情操 _ 分析. py**********深度学习神经网络方法的一个大优势是,我们不必尝试和**设计特征**,因为神经网络将学习上下文和重要特征。然而,这是以**可解释性**为代价的——使得它在某些情况下成为一个显著的限制。此外,就计算时间而言,这是最昂贵的方法,特别是如果联合训练单词嵌入,因为它需要大的数据集。对于我们的数据集,单词嵌入 LSTM 模型(具有 61MM 参数)花了 20 个小时来训练。它确实产生了令人瞩目的训练分数 **96%** 的准确度和测试/验证分数 **80%** 的准确度。*****## *****情感分析应用**********如果做得正确,情感分析可以为任何组织提供巨大的价值。如今,组织使用情感分析来了解公众对其产品、服务和品牌的感受。这可以**指导营销策略**,**激励产品开发**,**告知政治活动**,**检测潜在的破坏性事件**。以下是一些经过验证的例子:******   *****确定潜在的贬低者以提高净推广者分数(NPS) ( ***客户关怀/反馈分析*** )*****
*   *****快速评估客户参与度,以识别并解决负面客户体验,从而改善客户服务并提高回头率( ***票证分类*** )*****
*   *****监控和管理公众对该品牌的看法( ***品牌智能*** )*****## *****总结和选择合适的工具**********我们回顾了三种情感分析方法,每种方法都有自己的优点和缺点。在选择合适的方法时,需要考虑一些关键因素:******   *****精确度和/或速度的公差是多少?如果速度优先于准确性,基于规则的解决方案可能是正确的解决方案。*****
*   *****培训数据的可用性和完整性?有了稳健的训练数据,**朴素贝叶斯模型**可能是正确的解决方案,因为它实现起来很快。*****
*   *****模型的可解释性有多重要?**如果准确性优先于模型可解释性,深度学习**可能是正确的解决方案。**********并行运行多个模型进行比较并不少见。一旦部署,该过程应该包括一个反馈循环,以通知模型何时需要更新。*****> *****我还致力于分享如何用并行计算更快地训练 NLP 模型的代码。如果你喜欢这篇文章,我邀请你跟我来,这样你就可以得到这个项目继续的通知。*****# 独立于预处理的在线商店情感分析> 原文:<https://towardsdatascience.com/sentiment-analysis-of-an-online-store-independent-of-pre-processing-42c8d195ad08?source=collection_archive---------35----------------------->## 当预处理困难或耗时时,您会怎么做?在这篇文章中,主要目标是检查有多大可能减少对文本预处理的需求。下面,我将简单解释一下,您可以在这里找到详细信息:<https://peerj.com/articles/cs-422/>  ![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/add78934baf3e66e8f6b3fbc4ea2b0c1.png)一种用于情感分类的卷积网络结构。参考:10.7717/peerj-cs . 422/图 11.  摘要情感分析在公司,尤其是商店中起着关键作用,提高确定客户对产品意见的准确性有助于保持他们的竞争条件。我们打算在伊朗最大的网上商店网站上分析用户的意见;迪吉卡拉。然而,波斯语是非结构化的,这使得预处理阶段非常困难,并且这是波斯语情感分析的主要问题。加剧这一问题的是缺乏用于波斯语预处理的可用库,而大多数库侧重于英语。为了解决这个问题,使用网络挖掘技术从 Digikala 网站收集了大约 300 万条波斯语评论,并使用 fastText 方法创建了单词嵌入。人们认为,考虑到单词在句子中的位置以及单词之间的关系,这将极大地减少通过 skip-gram 方法进行文本预处理的需要。使用 TF-IDF 与 fastText 并行创建了另一个单词嵌入,以比较它们的性能。此外,还比较了卷积神经网络(CNN)、BiLSTM、逻辑回归和朴素贝叶斯模型的结果。作为一个重要的结果,我们使用 fastText 和 CNN 获得了 0.996 的 AUC 和 0.956 的 F 值。在这篇文章中,不仅证明了在多大程度上可以独立于预处理,而且所获得的精度优于用波斯语所做的其他研究。避免复杂的文本预处理对其他语言也很重要,因为大多数文本预处理算法是为英语开发的,不能用于其他语言。由于其高准确性和预处理的独立性,所创建的单词嵌入在波斯语中除了情感分析之外还有其他应用。2.方法学![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/3f1065c95360614a90d42cf3ec97745e.png)代表所采取步骤的流程图。参考:[10.7717/peerj-cs . 422/图 4](https://doi.org/10.7717/peerj-cs.422/fig-4)在这篇文章中,我们寻求分析伊朗最大和最知名的在线商店(Digikala)的网站上客户评论的感受。起初,语言问题被认为是一个重大挑战。波斯语文本预处理存在一些问题,如使用俚语,使用其他语言尤其是阿拉伯语的字母,短语之间缺乏明确的界限。为了解决这些问题,我们采用了 fastText,因为我们想检验这种方法的使用是否能够减少数据预处理的需要,并使语言处理更容易。在下文中,我们将检验这一假设,并将所得结果与其他算法和其他报告进行比较。另一个严重的限制是深度学习模型需要庞大的数据集,但大多数可用的波斯语数据集都很小,以至于无法在深度模型中使用。因此,必须从 Digikala 网站中提取丰富而庞大的数据集,这是通过网络挖掘方法进行的。应当指出的是,这一条旨在实现以下目标:> 通过在波斯语处理或其他语言处理中实施快速文本等方法,调查减少文本预处理需求的情况;
> 
> 基于各种类型的数字商品收集全面的客户评论数据集,以创建用于与数字商品相关的各种作品的通用单词嵌入;
> 
> 与其他研究相比,Digikala 网站评论的情感分析具有更高的准确性。能够访问具有丰富性和内容完整性的大型数据集对于训练深度模型是不可或缺的。大多数可用于训练深度模型和情感分析的数据集都是英文的。为了收集丰富的数据集,使用了网络挖掘方法,提取了 Digikala 网站上的波斯语评论。买家发布的评论表达了他们对购买和产品功能的满意程度。提交评论后,买家可以在“我建议”和“我不建议”选项中进行选择。这两个选项被提取并在模型中用作情感分析问题的标签。我们的目标是分析 Digikala 网站用户的意见,因此我们使用 web 挖掘库(如 Beautiful Soup)提取了与数字商品相关的部分的数据。Beautiful Soup 是一个 Python 包,用于解析 XML 和 HTML 文档,对于 web 抓取非常有用。这样就提取出了 Digikala 网站的数码商品评论,总共是 2932747 条评论。3.结果采用 TF-IDF 和 fastText 方法提取特征。BiLSTM 和 CNN 模型使用 fastText 输出,nave Bayes 和 Logistics 回归模型使用 TF-IDF 输出,最后在表 4 中比较了它们的准确性。根据该表,BiLSTM 和 CNN 模型的结果比其他模型更准确,CNN 给出了最好的结果。正如预期的那样,由于使用了 fastText 方法,对数据预处理的需求已经减少。换句话说,词干化和规范化方法并没有影响最终结果。为了更仔细地检查这一点,我们选择了 CNN 模型作为最佳模型,并且我们曾经使用预处理步骤和没有这些步骤的情况下执行了情感分析过程。预处理前的 AUC 和 F 值分别为 0.9943 和 0.9291,预处理后分别为 0.9944 和 0.9288。结果见表 5。在该表中,“预处理之前”的含义正好在词干提取和规范化步骤之前。换句话说,用于创建单词嵌入的方法可以在相同的空间范围内描述相同的单词,而不需要标准化字母,也不需要识别单词的原始词根。与预处理相反,伪标记法的使用显著改善了结果。使用伪标记后,AUC 和 F-score 的值提高到 0.996 和 0.956。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/2a532cc08cab415d407888fdb269f12d.png)基于 AUC 和 F-measure 的不同模型的性能,以及基于 AUC 和 F-measure 的 CNN 模型在不同情况下的性能。参考:[10.7717/peerj-cs . 422/table-4](https://doi.org/10.7717/peerj-cs.422/table-4)和[10.7717/peerj-cs . 422/table-](https://doi.org/10.7717/peerj-cs.422/table-4)5所建议的模型比先前在波斯语情感分析中使用预处理方法的模型具有更好的结果。例如,一些研究人员引入了预处理算法,并成功增强了机器学习算法的结果(Saraee & Bagheri,2013)。在研究中,使用朴素贝叶斯作为分类算法的预处理算法的 F 值是 0.878。在另一项研究中,对预处理和分类器算法的各种备选方案进行了检查,最佳结果由 SVM 分类器辅助,F 值为 0.915(阿斯加里安、卡哈尼和沙里菲,2018 年)。此外,一些研究试图利用最先进的深度模型,以减少对预处理的依赖,避免复杂的步骤(Roshanfekr,Khadivi 和拉赫马蒂,2017 年)。BiLSTM 和 CNN 算法在研究中的 F 值分别为 0.532 和 0.534。所有提到的文章的重点是在波斯两类情感分析的数字商品评论与本文相同。与其他研究和常用算法的比较表明,本文的结果不仅消除了对数据预处理的依赖,而且精度显著提高。4.结论该数据集包括从伊朗最大的在线商店 Digikala 网站的数字商品部分提取的大约 300 万条评论。使用基本的预处理方法来修改单词并对其进行标记。由于大部分数据集缺少标签,因此采用伪标签方法来改善结果。还使用随机过采样来执行数据平衡。发现波斯语数据预处理很困难,因此使用 fastText 方法来减少对数据预处理和单词嵌入开发的需求。嵌入被用作 BiLSTM 和 CNN 模型的输入。使用建议的模型,不仅所获得的结果非常令人满意,而且在波斯语中比其他报告更准确,而且没有与数据预处理相关的复杂性。评估了词干化和归一化对输出的影响,并揭示了所提出的方法不依赖于数据预处理。最后,除了机器学习和深度学习方法在情感分析中的比较之外,还比较了 TF-IDF 和 fastText 方法来创建单词嵌入。最好的结果与 fastText 和 CNN 有关。该模型的主要成就是减少了对数据预处理的需求。扩展的文本预处理库使得英文数据预处理更加方便和准确。然而,在其他语言中,由于缺乏适当的库,数据预处理非常复杂。在建议的模型上,证明了这种需求在很大程度上是可解决的(AUC = 0.996),并且预处理步骤可以减少到初步的记号化过程。避免复杂的文本预处理对其他语言也很重要,因为大多数文本预处理算法是为英语开发的,不能用于其他语言。换句话说,独立于预处理步骤,所采取的步骤可能被实现或以其他语言实现相同的结果。此外,由于其高准确性,所创建的单词嵌入可以用于其他文本分析问题,尤其是与在线数字商品相关的问题。GitHub:<https://github.com/mosiomohsen/persian-sentiment-analysis-using-fastText-word-embedding-and-pseudo-labeling> # 新冠肺炎疫苗推文的情感分析> 原文:<https://towardsdatascience.com/sentiment-analysis-of-covid-19-vaccine-tweets-dc6f41a5e1af?source=collection_archive---------12----------------------->## [实践教程](https://towardsdatascience.com/tagged/hands-on-tutorials)## 使用 TextBlob API 和 word cloud 可视化来综合有关全球疫苗接种工作和进展的论述![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/761d29d1ea3d569ab0a1b98a15c6f9b3.png)由[哈坎·努拉尔](https://unsplash.com/@hakannural)在 [Unsplash](https://unsplash.com/photos/9c3C_KojYMQ) 上拍摄这是疾病、破坏、悲伤和绝望的漫长一年,但新冠肺炎疫苗的全球推广让许多人感到宽慰,并重新找到了乐观。关于疫苗接种进展、可及性、功效和副作用的讨论仍在继续,并且每天都在新闻报道和 Twitter 上传播。然而,作为在线用户,我们的可见性仅限于我们自己的回音室。因此,这个项目的动机是通过利用 Twitter 数据的力量来拓宽我对全球疫情状况的视角。对于一个人来说,阅读和消化人们在推特上发布的关于新冠肺炎疫苗的所有信息几乎是不可能的。幸运的是,在自然语言处理(NLP)技术的帮助下,我们可以通过文本特征化、情感分析和词云可视化的方式来窥视极其复杂和广泛的讨论。接下来的工作使用了一个名为“所有新冠肺炎疫苗推特”的数据集。数据是使用名为 Tweepy 的 Python 包收集的,如果用户成功创建了 Twitter 开发人员帐户并获得了访问凭据,就可以使用该包访问 Twitter API。在这种情况下,由于数据都是预先收集的,并以 10 MB 的表格格式存储在 Kaggle 上,所以任何希望得到它的人都可以访问它。这些数据包括与各种疫苗相关的推文,包括辉瑞/BioNTech、国药控股、科兴生物、Moderna、牛津/阿斯利康、Covaxin 和 Sputnik V。对于每种疫苗,使用相关的搜索词来查询最近的推文。## 探索性数据分析该项目的探索性数据分析阶段的主要目标是熟悉数据框的列,并开始头脑风暴研究问题。从使用 pandas 加载数据的步骤开始,一些基本的数据框操作允许我们看到,对于每个 tweet,以下所有信息都是可用的:*关于发推文的用户的信息* **用户名**:推文句柄
**用户位置**:发推文的人来自世界的哪个地方(注意:这里没有验证……“你的床”在技术上是可以接受的)
**user_description** :用户撰写的传记
**user_created** :他们创建 Twitter 帐户的时间
**user_followers** :关注者数量
**user_friends** :用户关注的帐户数量
**user_favourites** :用户喜欢的推文数量
**user_verified** :表明用户是否是知名人士*关于 tweet 本身的信息* **id**:Twitter API
**date**的索引值:YYYY-MM-DD HH:MM:SS
text 形式的日期时间对象:tweet 本身(**最重要* *)
**hashtags**:tweet 中使用的 hash tags 列表(不带“#”字符)
**源**:哪台设备用于推文
**转发**:收集数据时收到的转发数
**收藏夹**:收集数据时收到的点赞数
**is_retweet** :指示推文是原创还是转发(布尔值)在上面的列中,文本、日期、用户名、用户位置、标签、收藏夹和转发与此分析最相关。虽然推文是使用疫苗相关的关键字查询的,但推文中提到的特定疫苗并没有明确地作为一列包含在数据集中。因此,我们需要筛选疫苗参考文献,以便进行任何比较分析。做任何 EDA 的第一步都是制作一些列(或特性)及其分布的图表。熊猫内置函数 df.describe()可以帮助我们一目了然地看到每个数值列的统计数据。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/9213cccaf38e4f3abfaa10379820374e.png)每个数字列的关键统计数据从上图中我们可以看到,在这个疫苗推文中,普通用户有超过一千个朋友,并喜欢上千条其他推文,所以这些主要是活跃的 Twitter 用户,我们正在分析他们的推文。我们还可以看到,平均转发次数为 4 次,平均点赞次数为 18 次。然而,我们应该对这些统计数据有所保留,因为“std”(标准偏差)行向我们显示,正如人们可能想象的那样,数据集中有很多变化。这个数据集中总共有 16,170 条推文(在完成项目时),所以为了在开始之前直观地检查一些推文的可行性,我们可以看一看转发最多的推文。高转发数表明该推文可能足够丰富,可以广泛分享,或者在推特上足够相关,可以“病毒式传播”。通过对我们的数据框执行以下操作,我们可以预览转发次数最多的前 10 条推文:

df.sort_values(by='retweets', ascending=False)[['text', 'date', 'user_name', 'user_location', 'hashtags', 'favorites', 'retweets']].head(n=10)


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/2e2637305b3189343c0d420ca245a965.png)转发次数最多的 10 条推文从上面的图片中,我们可以看到许多转发量最大的推文是印度用户在 3 月份发布的,具体指的是 Covaxin 疫苗。同样显而易见的是,转发量最高的 10 条推文中有 3 条来自一个名为“Megh Updates”的账户🚨似乎是新闻更新,而不是对疫苗的基于观点的评论。这是有道理的,因为信息性的推文通常比基于观点的推文更容易被转发。有趣的是,转发量最高的推文来自一位名叫 J Jaishankar 博士的用户,他是印度外交官和政治家,现任印度政府外交部长。他的推文只是更新状态说“挨了我的刺拳。出于好奇,它是 covaxin。感到安全,将安全旅行。”虽然这条消息似乎没有什么信息或观点,但因为 Jaishankar 博士在印度和全球政治领域是一个如此知名的人物,所以他的大量推特粉丝对他的状态更新反应积极是有道理的。现在,我们可以做另一个快速检查,以了解在疫苗推出的早期阶段,Twitter 上的讨论是如何开始加速的:

df.sort_values(by=['date', 'favorites'], ascending=[True, False])[['text', 'date', 'user_name', 'user_location', 'hashtags', 'favorites', 'retweets']].head(n=10)


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/f82aa3fe1c282e7db1278a6af9c5ad31.png)十大最早、最受欢迎的推文上面的预览按照日期(升序)对数据集进行排序,然后按照哪些推文获得了最高的喜欢/收藏数(降序)进行排序。我们可以看到,2020 年 12 月 12 日,辉瑞-BioNTech 疫苗获得批准的第二天,Twitter 上开始出现关于新冠肺炎疫苗的讨论。大多数推文都提到了 PfizerBioNTech 疫苗,但这些推文显然没有获得大量受众,当天最受欢迎的推文只有 37 个赞和 2 个转发。## 文本 Blob 情感分析转到这项工作的情感分析部分,我们可以直觉地认为一些推文是信息丰富的,一些推文是有见解的,以将更大的话语划分为具有类似量化特征的独立推文集。这些功能可以使用名为 [TextBlob](https://textblob.readthedocs.io/en/dev/) 的 Python 包来获得,该包提供了一个用于 NLP 任务的 API,如词性标注、名词短语提取、情感分析、分类、翻译等。要安装 TextBlob,请确保在终端窗口中执行以下两个命令:

$ pip install -U textblob
$ python -m textblob.download_corpora


TextBlob 的情感分析处理采用了两个关键指标:极性和主观性。极性得分是[-1.0,1.0]范围内的一个浮动值,它反映了一个陈述或段落的情感负荷。主观性是在[0.0,1.0]范围内的浮动,其中 0.0 非常客观,1.0 非常主观。让我们来看几个简单的例子:

TextBlob("So excited to get my vaccine!").sentiment
Sentiment(polarity=0.46875, subjectivity=0.75)

TextBlob("Is the vaccine painful?").sentiment
Sentiment(polarity=-0.7, subjectivity=0.9)>>> TextBlob("The Pfizer vaccine is now FDA approved").sentimentSentiment(polarity=0.0, subjectivity=0.0)


在第一个例子中,我们可以观察到 TextBlob 识别与陈述相关联的相对积极的情绪负荷,而它识别与第二个例子相关联的消极的情绪负荷。在第一个和第二个例子中,它正确地识别出表达他们对接种疫苗的感受的人是高度主观的。在第三个例子中,陈述被识别为极性中性且高度客观。这三个例子展示了 TextBlob 情感分析如何帮助我们获得定量的文本特征。虽然 TextBlob 是在大型语料库上训练的,并采用了相当可靠的语言模型,但它的服务最适合于*从文本数据中识别*句法和语义特征,而不是*理解*文本。TextBlob 不是上下文感知的,所以从它的 API 返回的分数应该以一种促进进一步分析的方式被松散地解释。作为 TextBlob 缺点的一个例子:

TextBlob("I tested positive for COVID-19").sentiment
Sentiment(polarity=0.227272727, subjectivity=0.54545454)


TextBlob 被“阳性”这个词搞混了,因为它没有经过识别医学术语的训练。它把这个句子归类为带正电的和相当主观的,即使它实际上应该是消极的或中立的和客观的。当我们解释数据集中每条推文的分类时,了解这一点很重要。对于需要更多上下文感知语言模型的任务,我推荐查看`nltk`、`vader`、`sklearn`和`allennlp`。让我们来看看 TextBlob 如何看待德克萨斯州州长格雷格·阿博特(Greg Abbott)的以下推文的情绪。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/c884a0aed6abb0e38ee4ba1d59e115ee.png)2021 年 3 月 14 日 Greg Abbott 的新冠肺炎状态推特截图(在推特上公开)

for sentence in blob.sentences:
print(sentence)
print("polarity:", sentence.sentiment.polarity)
print("subjectivity:", sentence.sentiment.subjectivity)


遍历每个句子并检索极性和主观性得分,我们获得以下结果:

Today Texas surpassed 8 million vaccinations.
polarity: 0.0
subjectivity: 0.0

It was just 4 days ago that we passed 7 million.
polarity: 0.0
subjectivity: 0.0

The positivity rate dropped even lower--to 6.08%--the lowest since May 31st.
polarity: 0.0
subjectivity: 0.0

Hospitalizations went down again also.
polarity: -0.15555555555555559
subjectivity: 0.2888888888888889

Great job Texans!
polarity: 1.0
subjectivity: 0.75

Vaccines are always voluntary, never forced.
polarity: 0.15000000000000002
subjectivity: 0.2


前三句已被确定为具有中性和最大程度的客观性,这是有道理的,因为它们包含了关于疫苗推出和疫情状况的事实趋势。有趣的是,第四句话“住院率也下降了”,被贴上了带有轻微主观性的负面情绪标签。这可能是因为“住院”和“倒下”这两个词有负面含义,而且这里也没有数字或事实证据。如果州长阿博特说“住院率降低了 X %”,这将被认为是一个中立和客观的说法。“干得好,德克萨斯人!”另一方面,是一个积极的、高度主观的陈述的主要例子。我们现在可以将 TextBlob API 应用到我们的 Twitter 数据上,对所有 16,170 条推文进行情感分析!

df['polarity'] = df['text'].apply(lambda x: TextBlob(x).sentiment.polarity)
df['subjectivity'] = df['text'].apply(lambda x: TextBlob(x).sentiment.subjectivity)
df.head()


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/2e4c668a99a94100912e2432bf071a44.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/aad4b8c575b239c64cbffcb283cf1bf4.png)数据集中所有推文的极性和主观性直方图上面的直方图描述了数据集中所有推文的极性和主观性得分的细分。数据集中的大多数推文似乎都是信息丰富的疫苗更新,具有中性的极性,没有主观性。然而,在极地推特中,带正电荷的推特略多于带负电荷的推特。我们可以推断,twitter 上的疫苗讨论总体上是乐观的,但如果能知道人们表达的是什么样的乐观情绪,以及人们可能表达的是什么样的焦虑或绝望情绪,那就更好了。我们可以通过极性得分对数据帧进行排序,然后显示前 10 行来检查这些类型的推文:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/6338fbda65d7dadf2f81e9b588a081f5.png)数据集中负面最多的 10 条推文上面一条负面推文中一些值得注意的短语包括“啊,我很痛苦”,“令人发指的是……”,“我一生中最头疼的事”,“他妈的”,“我感觉像个废物”,“贪婪到了极点”,“太可怕了!”。在这里,表情符号没有被纳入情感得分,但如果表情符号足够突出,足以纳入分析,肯定有办法将表情符号转换为文本。TextBlob 情感分析的一个明显缺点是,它不认为“祝福”这个词有积极的含义。在第 8 行,当用户说,“如此幸运地完全接种了针对这种毁灭性病毒的疫苗”,单词“毁灭性的”可能混淆了语言模型。这个模型没有认识到疫苗是用来防御毁灭性病毒的,这是一件好事。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/363538597c469cbe9644f46bdfd39988.png)数据集中最积极的 10 条推文同样,当我们检查前 10 个最积极的推文时,我们注意到许多高度主观的表达。使用最多的单词和短语包括“感觉棒极了”、“最好的”、“美妙的”和“完美的”。## 时间序列分析对于这个项目的下一个组成部分,我们将看看是否可以探索任何关于时间的趋势。首先,可视化数据集中所有推文的时间分布是有价值的。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/075ffc8d8721f07c75e6a289bb3d7bce.png)相对于时间的推文频率在上图中,很明显,讨论的高潮发生在 3 月 1 日。经过更深入的调查,我得知印度总理纳伦德拉·莫迪在那一天接受了第一剂印度研发的新冠肺炎疫苗 Covaxin。对于印度人来说,这一特殊事件非常令人兴奋和振奋,这从他们在 Twitter 上的活动中可以反映出来。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/5764faee67ca815ccab792103c506dd7.png)按情感标签分类的推文在数据集中的所有推文中,大约一半被 TextBlob 指定为情绪中性(极性= 0.0),另一半由 75%带正电荷的推文(极性> 0.0)和 25%带负电荷的推文(极性< 0.0)组成。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/976bb09cd6c5cc2632260af419960d06.png)按日期汇总的推文的平均主观性得分![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/00e9be958a28ea43d725a732451492b6.png)按日期汇总的推文平均极性得分上面的图表提供了主观得分和极性得分随时间平均变化的快照。从上面的平均极性得分图中,我们可以假设 Twitter 上的话语在二月之前是乐观的,预期疫苗的分发。在 2 月和 3 月,疫苗的推广非常引人注目,人们开始在推特上发布与他们个人经历相关的状态更新。如果一个人有一个关于特定事件在时间上的影响的研究假设,或者如果他们希望获得关于一个事件的直觉,时间序列分析会很有帮助。也就是说,我们的数据涉及多种不同的疫苗,并且推文没有按位置划分,所以很难推断积极或消极的情绪到底意味着什么。为了进一步研究这一点,我们可以使用一种叫做单词云的可视化表示。## 词云可视化这个项目的最后一步是生成颜色编码的单词云,看看哪些单词代表了每种情绪。实现这一点的方法大致如下*   执行更彻底的预处理,如拼写检查、拆分无空格短语等。
*   计算对数似然值
*   基于对数似然值生成合成语料库
*   生成更智能的单词云![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/6da4b8688e126ffeebdc2ff73b616abe.png)辉瑞生物技术公司:智能词汇云按情绪进行颜色编码上面的词云描述了对于提到“Pfizer”和/或“BioNTech”的推文的每个情感,通常使用哪些词。积极的推文似乎来自刚刚接受第一次疫苗接种的人,或者对科学家和医护人员所做的工作表示感激的人,而消极的推文似乎来自在接受疫苗后遭受副作用、不良反应或全身不适的人。关于辉瑞疫苗的中性推文中使用的词语似乎类似于新闻标题。其中一些词包括“健康”、“宣布”、“FDA”、“批准”、“百万”、“说”和“疫苗接种”。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/4101a12a7107b0b9f65347fa018fc536.png)Moderna:智能词汇云,通过情绪进行颜色编码Moderna 疫苗的词云产生了与辉瑞推特相似的见解。在带有负面词汇的词云中,有许多关于副作用、手臂酸痛、发冷、发烧、头痛、疲倦感和其他人们可能倾向于在 Twitter 上分享的轻微症状的引用。中性词包括“供应”、“百万”、“十亿”、“诊所”、“mRNA”、“加拿大”、“菲律宾”、“增加”、“总数”以及许多其他与新闻相关的术语。有趣的是,在中立的推文中经常提到 IBM,这可能与宣布 [Moderna 和 IBM 计划在新冠肺炎疫苗供应链和分销数据共享方面进行合作](https://newsroom.ibm.com/2021-03-04-Moderna-and-IBM-Plan-to-Collaborate-on-COVID-19-Vaccine-Supply-Chain-and-Distribution-Data-Sharing)有关。也有正面和中性的提及多莉·帕顿,[谁帮助资助了现代疫苗](https://www.nytimes.com/2021/03/02/world/dolly-parton-moderna-vaccine-covid.html),后来接受了现代疫苗。与正面的辉瑞特定推文类似,正面的 Moderna 特定推文往往来自于在接受第一剂疫苗时表达感谢的用户。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/54299e89a94dce560a22a90e25eef8d0.png)Covaxin:智能单词云,通过情绪进行颜色编码最后,Covaxin 的“云”一词包含了许多对印度特定人物和地点的引用。对于负面关联的推文,“宣传”和“政治”这两个词似乎足够吸引人,足以引发进一步的探索。积极相关的推文包括大量提及总理纳伦德拉·莫迪。鉴于印度是一个有着强烈民族自豪感的国家,这些推文中有许多积极情绪是有道理的。此外,从文化的角度来看,这个数据子集与辉瑞和 Moderna 子集相比,存在语言上的细微差别。在印度,如果有人刚刚接种了疫苗,他们可能会说,“我接种了疫苗,”如果没有这些文字云可视化,我们可能不会意识到这一点!## 扩展问题我不相信一个项目会有一个明确的结束,但是,我们会在某个时候决定结束。带着这种心态,我喜欢通过头脑风暴来结束我有兴趣进一步探索的研究问题:*   使用`tweepy` [API](https://docs.tweepy.org/en/latest/) 获取关于强生&强生疫苗的推文,并对其进行情感分析。
*   人们在使用哪些标签?标签的使用与更高的点赞数和/或转发数相关吗?
*   分析包含 URL 的推文的派别。有网址的推文主要是中性情绪吗?
*   经过验证的推特用户对新冠肺炎疫苗有什么看法?
*   人们对副作用有什么看法?哪种疫苗副作用最大?
*   将[新冠肺炎世界疫苗接种进展数据集](https://www.kaggle.com/gpreda/covid-world-vaccination-progress)与该情绪分析相关联,以监测随着推广的继续,人们对疫苗的态度是如何变化的。## 结束语如果你做到了这一步,非常感谢你的阅读!这项工作的代码可以在下面的 GitHub 资源库中找到:<https://github.com/sejaldua/covid19-vaccy-tweets-NLP>  如果你想了解更多我的作品或与我联系,请随时访问我的[网站](https://sejaldua.com)、 [GitHub](https://github.com/sejaldua) 、 [LinkedIn](https://www.linkedin.com/in/sejaldua) 和/或 [Twitter](https://twitter.com/sejaldua) 。我很乐意连接!# 使用 AWS 通过特别批处理对实时推文进行情感分析> 原文:<https://towardsdatascience.com/sentiment-analysis-on-live-tweets-with-ad-hoc-batch-processing-using-aws-f77036dc337?source=collection_archive---------25----------------------->## 使用 AWS 实现的用于摄取的可扩展数据管道的系统设计,具有存储的 NLP。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/67096cb5fed88f69fdcf59882225650b.png)https://github.com/hvp004/twitter-sentiment-analysis[(图片作者提供)](https://github.com/hvp004/twitter-sentiment-analysis)我是一家制造公司的数据科学家,通常情况下,我的任务是为部署 ML 模型的数据平台构建计算单元、数据库模式和基础设施。通常,我的任务是边缘处理。作为一个好奇的灵魂,我一直想在云上建立一个端到端的机器学习模型的可扩展的大数据管道。我通常处理控制器级别的数据或传感器数值数据。我想做一些不同于我日常工作的东西。即文本数据。我[申请了 twitter 开发者 API](https://developer.twitter.com/en/apply-for-access) 。经过几天 Twitter 的反复询问,我被允许访问 Twitter 的开发者 API。这意味着我现在可以使用这个 API 获取实时推文流,并查询一周前的历史推文。整个项目可以在 [Github](https://github.com/hvp004/twitter-sentiment-analysis) 上找到。# **项目概述**Twitter API 将用于从 Twitter 获取 tweet 数据。数据以 15 分钟的时间间隔提取,在指定的时间段内每隔 15 分钟回顾 100 条推文。tweet 文本和一些用户元数据存储在 staging 区域。将`tweet_id`和`text` (tweet 本身)推送到 Kinesis 数据流中进行进一步处理。使用 Kinesis 数据流,对于每条推文,使用 AWS Comprehend ML as a service 进行情感分析。情感分析 API 以正面、负面、中性或混合类别返回文本的总体情感,并以最终结论作为标签。标签只是一个字符串,代表所有指定类别的最高概率。消费者使用这些结果并将数据写入情感桶。在这里,每个 Kinesis 数据流最多包含 100 条推文。一旦摄取了数据,并且处理了所有 Kinesis 数据流,就使用弹性 Map Reduce (EMR)服务来处理数据。EMR 旨在使用 Hadoop、Spark framework 和 Hive 以完全托管的集群方式以分布式方式处理数据。我们可以通过 ssh 进入主实例,在集群上执行操作。使用`pyspark`的 EMR 脚本首先通过 AWS cli 从 S3 桶复制到 EMR 主实例中。EMR 从存储桶中加载阶段和情感数据,并使用`tweet_id`合并它们。然后,它基于 15 分钟的时间窗口执行聚合,以计算该时间段内每个情感类别的平均情感和总推文百分比。这样,聚合的数据可以很容易地可视化,而不必在每次用户想要查看分析时对数千条推文进行聚合。EMR 将合并结果的所有 tweets 数据存储在 s3 桶中。聚集的数据存储在单独的位置(不同的 s3 文件夹或不同的 s3 存储桶)。这样,我们就有了一个所有推文及其主流观点的事务视图,以及一个带有时间窗口的聚合结果的数据集市,该数据集市可用于可视化以时间为参数的数据。AWS Redshift 是一个完全托管的数据仓库系统,其中的表是使用 SQL 命令制作的。这些表将保存存储在存储桶中的事务性和聚合数据。为了将存储在 s3 存储桶中的数据加载到红移数据仓库,使用了 COPY 命令。建立到红移集群的连接(SQL Workbench 或红移集群查询编辑器),并在集群上执行复制命令,以将数据从存储桶提取到表中。EC2 实例上的 Grafana 用于可视化。# 系统结构该项目在 AWS 上运行,公开了一些 AWS 服务:1.  S3:用于存储分期数据、情感数据、推特交易数据、聚合数据
2.  EC2:运行`ingest.py`和`consumer.py`。你也可以在自己的笔记本电脑上运行它们。这是可选的。
3.  EMR 集群:处理所有批量产生的数据。在将数据存储回 S3 之前,合并数据并运行聚合。
4.  红移集群:最终数据会以红移结束。
5.  AWS understand:为了从 tweet 文本数据中获取情感,使用了 AWS understand。这可以用本地 NLP 模型或带有 HTTP 端点的开源或企业版 ML 模型来代替。这里的领悟是通过`boto3` python API 使用的。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/aea033b0cc1aa4d5646dac9044f5f8af.png)系统架构([https://github.com/hvp004/twitter-sentiment-analysis](https://github.com/hvp004/twitter-sentiment-analysis))(图片由作者提供)这里,S3 用于每个处理单元的主存储。AWS S3 为平面文件提供了非常廉价的数据存储能力,具有高可用性和用例灵活性。摄取与 NLP 工作流保持分离,因为 NLP 完全是一个独立的实体。这样,摄取就独立于 ML 工作流。这种解耦允许我们在不延迟数据获取的情况下,从摄取并行地一起运行多个 NLP 服务。AWS Kinesis 作为一种临时存储机制,可以更快地检索下游组件和 NLP。数据一旦产生,就被消费者消费。根据对文本执行的分析类型,可以有多个消费者,并且所有消费者都是相互独立的。此外,如果任务失败,就处理和存储而言,在完全隔离的情况下再次运行它会更容易。EMR 提供了一系列高性能 EC2 实例,并采用了 Hadoop Spark 等广泛使用的分布式处理框架。它有能力对万亿字节或千兆字节的数据进行数据处理。EMR 将数据写入 S3 桶,而不是直接写入红移,原因有几个。可能有许多不同的子系统想要使用经过处理和聚合的数据。S3 的存储比红移便宜得多,红移是按小时付费的。此外,S3 读/写比红移读更便宜,因为我们要为每个请求及其数据包大小付费。Redshift 的主要目标是提供数据的全貌,并能够更快地查询历史数据。红移的数据查询比 S3 快得多。因此,当该系统可能是具有许多微服务的更大架构的一部分时,S3 用于平衡成本。一旦数据被加载到红移数据库中,像 Grafana 这样的数据可视化系统就可以提取数据并将其可视化。# 安装和使用我是 docker 的粉丝,因为它使我的生活变得简单,可以部署多个计算模块,而且我不必担心主机的环境。为了这个项目,我创建了一个`docker-compose``。我不会深入这个部分的细节,因为它已经在我的 [github 回购](https://github.com/hvp004/twitter-sentiment-analysis)中描述过了。**仪表盘**我用 grafana 主持仪表板,因为对我来说使用 grafana 进行 ay 项目是很自然的。它有一个丰富的插件集[和时间序列数据。我用它创建了一个快速仪表板。](https://grafana.com/grafana/plugins)在 AWS 上配置 Linux/Windows 机器。确保它可以访问红移星团。在上面安装 Grafana。将其连接到红移星团,并开始可视化数据。我用查询字符串`biden`跑了 4 天,收集了大约 33,000 条推文。下面的交互式仪表板在第一行显示了每个类别的推文百分比,带有仪表盘。在第二行中,我将 tweet 的总数显示为一个标签,以及时间轴上每隔 15 分钟收集的 tweet。在最后一行,我展示了一个饼图,其中显示了该时间段内每个类别的平均情绪得分(NLP)及其时间线。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/67096cb5fed88f69fdcf59882225650b.png)格拉夫纳仪表板[https://github.com/hvp004/twitter-sentiment-analysis](https://github.com/hvp004/twitter-sentiment-analysis)(图片由作者提供)# **结论**摄入依次进行。出现故障时,系统可能处于这样一种状态,即我们必须从最后一个分区开始再次运行摄取。它应该是并行的,因为以前的加载独立于当前正在进行的加载。可能的解决方案:可以使用类似 Airflow 的调度程序来运行分区并行数据接收。消费者作为一个独立的消费单位。它按顺序工作。它严重依赖于第三方 API(不属于本系统的 API)。因此,第三方 API 中的任何常规延迟都可能导致消费中的大量延迟,并且随着每个消费周期,随着生产者继续生产数据,数据可能会增加。可能的解决方案:生产者和消费者数据包之间应该有 1:1 的关系。AWS Kinesis Firehose 是一个数据流传输系统。λ函数可以用于产生的每个数据流,它可以以无服务器的方式处理每个流。此外,您可以使用类似 Airflow 的调度程序来运行摄取,从而定期运行摄取,使之成为一个定期的批处理过程。在设计上稍作改变,你也可以使用 spark stream 让它与实时推文情感分析一起工作。# 情绪分析:预测一条推文是否是关于一场灾难> 原文:<https://towardsdatascience.com/sentiment-analysis-predicting-whether-a-tweet-is-about-a-disaster-c004d09d7245?source=collection_archive---------13----------------------->## [自然语言处理笔记](https://towardsdatascience.com/tagged/nlpnotes)## 探索自然语言处理![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/30fe0b0048fb4e0035761905980c48f4.png)由 [Joshua Hoehne](https://unsplash.com/@mrthetrain?utm_source=medium&utm_medium=referral) 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片## 介绍我经常采用机器学习优先的方法来分析数据,但在探索了我的 Github 之后,我意识到我没有太多的探索笔记本,特别是在自然语言处理(NLP)领域,这是我热衷于关注的领域——更具体地说,是感兴趣的对话式人工智能。<https://pub.towardsai.net/7-free-online-resources-for-nlp-lovers-26b1e0bba343>  情感分析任务包括使用自然语言处理(NLP)和机器学习(ML)的技术对主观数据进行解释和分类。由于世界上的许多数据都是数字格式的,许多企业正在利用情感分析的力量来收集关于品牌声誉、客户对各种产品的想法等更多信息。</getting-started-with-sentiment-analysis-731531ec880d>  ## 问题陈述一段时间以来,我一直在用机器学习和 NLP 脚本探索特定的数据集。然而,我从来没有用实际的探索性数据分析(EDA)和数据可视化真正探索过数据集,所以我认为专注于同一个项目来发现一些我以前从来不知道的关于数据的见解是明智的。> Twitter 已经成为紧急情况下的重要沟通渠道。智能手机的普及使人们能够实时宣布他们正在观察的紧急情况。正因为如此,越来越多的机构对有计划地监控 Twitter 感兴趣(即救灾组织和新闻机构)。但是,人们并不总是清楚一个人的话是否实际上是在宣布一场灾难。举个例子:
> 
> 作者明确地使用了“闪亮”这个词,但它是隐喻性的。这对人类来说是显而易见的,尤其是有了视觉辅助。但是对机器来说就不那么清楚了。
> 
> 在这场比赛中,你面临的挑战是建立一个机器学习模型,预测哪些推文是关于真正的灾难,哪些不是。你可以访问一个由 10,000 条推文组成的数据集,这些推文都是经过人工分类的。【 ***来源****:*[*Kaggle—自然语言处理带灾推文*](https://www.kaggle.com/c/nlp-getting-started/overview)*】*链接到使用此数据集完成的其他文章:*   [用机器学习预测推文情绪](/predicting-tweet-sentiment-with-machine-learning-3599c8add259)
*   [使用 Word2Vec 嵌入预测推文情感](/predicting-tweet-sentiment-with-word2vec-embeddings-67aace9b019d)
*   [系统地改进你的机器学习模型](/systematically-improving-your-machine-learning-model-41d327adf347)> **注意**:本文中用于生成可视化效果的所有代码都可以在 Github 上找到。<https://github.com/kurtispykes/twitter-sentiment-analysis/blob/master/notebook/03_kpy_data_exploration.ipynb>  ## 数据分解典型的 Kaggle,我们的数据集已经分为训练集和测试集。训练集由 **7613 行**和 **5 列**组成,而测试集由 **3263 行**和 **4 列**组成——缺少的列是目标列,我们要预测的。因为我们只是探索数据,所以我将训练和测试数据结合在一起进行进一步分析。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/7ab64e1b290376f32c0a28ee15f993ec.png)作者图片## 简单探索性数据分析在处理分类任务时,我喜欢检查的第一件事是目标标签,因为我想从一开始就知道我们是否有不平衡的分类任务——有关不平衡分类的更多信息,请参见[过采样和欠采样](/oversampling-and-undersampling-5e2bbaf56dcf)。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/30ff867f9990566e1ac57d3131324c36.png)作者图片在我们的数据集中有一个向负面类别(0 → tweet 不是关于灾难)的不平衡。了解这些信息非常重要,因为这可能会对我们创建的最终分类器产生严重影响。例如,分类器可能倾向于预测主要类别,这反过来意味着我们的分类器具有高精度。对于这些类型的问题,这是一个误导性的度量,因为它没有反映分类器正在采取的行动,所以当它被部署到现实世界的环境中时,它将是无效的。在我们的例子中,这种不平衡是不可接受的,但仍然是值得注意的。接下来,我通常想知道数据集中每个要素缺失的数据量。许多机器学习算法无法处理缺失数据,因此我们需要一种方法来在构建分类器之前处理这些实例(取决于我们使用的分类器)——了解更多关于[处理缺失数据](/handling-missing-data-f998715fb73f)的信息。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/ee145ac4e86931781ebf63c110ecebe3.png)作者图片在训练和测试集中,location 列中有 33.45%的值缺失,这是非常显著的。由于这些是推文的位置,Twitter 上的用户可以选择关闭他们的位置,我在想我们可能会估算一些东西来表明位置丢失。此外,由于大量位置丢失,我们可以考虑从我们的管道中完全删除此功能。推文的前 10 个位置列出了国家和城市,所以我将城市转换为国家,并计算每个国家的推文数量。**注意**:数据集中有 **4521** 个唯一地点,但是,我只把出现在前 10 个地点的城市转换成国家。因此,很可能在数据集的其余部分仍有一些城市,如果我们保留这一特征,这是需要考虑的事情。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/474be3be40c8444b5f06122c4845f830.png)作者 Gif在同样的地点话题上,我认为找出是否有某些地点更有可能出现灾难推文会很有趣。考虑到这一点,我创建了一个可视化的图表,显示了来自前 10 个地点的灾难性推文的百分比。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/74eca6d42d157420540b0ee1e3561dd8.png)作者图片新手数据科学家不常问的一个非常重要的问题——我无意贬低有志之士,尤其是因为我不会责怪你们任何人没有问。像这样的事情在我们在线学习的许多课程中通常不会教授,只能通过动手实践来学习——数据究竟来自哪里,如何收集等等。这些类型的数据查询非常重要,原因超出了本文的范围,但在将来,我一定会再次访问它。尽管如此,其中一个原因是,您可能希望验证从收集源获得的数据。例如,我知道这些数据是由一家名为 figure-eight 的公司创建的,但我不确定这些数据是如何标记的,以及是如何创建的。我可以向你保证的一件事是,如果人类参与到这个过程中,总会有很大的出错空间,因为我们可能会疲劳——当然,计算机也会出错,但希望你明白我的意思。由于我不知道数据是如何收集或标记的,我的第一个想法是考虑我们的数据集中是否有重复的推文。结果如下:*   198 在训练和测试数据中出现重复的 tweets
*   这些副本中的 110 个在训练数据中
*   **88** 是来自测试数据的重复推文这让我很担心。如果数据中存在重复,那么如果人类标记了实例,则可能存在不同标记的重复标签。这个想法是对的。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/0a1ffd02d1ac1105ec1d1446bb983671.png)作者图片Kaggle 有一个看不见的数据集,它用来评估你在私人排行榜上的位置,有可能标签也贴错了。在商业环境中,如果对分类器结果有严重影响,您会想要手动重新标记它们,那么这肯定是需要调整的。然而,为了安全起见,我手动更改了这些标签以适应正确的类别。在处理文本时,很难在传统的图(例如条形图和线图)上将其可视化。相反,我们可以使用[单词云](https://amueller.github.io/word_cloud/)来可视化数据中出现的常见单词。我用这种方法将与灾难联系最紧密的关键词特征可视化…![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/e730ae958fcfb383968f65981fedb9d9.png)作者图片而那些与灾难无关的…![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/a13aba1aa4465895a546db1144aa8e23.png)作者图片在大得多的文本中显示的单词是那些在数据集中具有更频繁计数的单词。关于新实例,我们可以做一个天真的假设,任何包含关键字“爆发”和“残骸”的实例都可能是关于灾难的,而包含“世界末日”和“尸体”的实例可能是非灾难的。还有许多其他因素需要考虑。例如,如果有人正处于灾难之中,他们会写更长的推文还是更短的推文,以及那个人会使用更多还是更少的标点符号。这些想法可以用统计数据来捕捉。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/14e38a850f137394a65ae11510022bc2.png)作者 Gif## 做预测为了确定我将继续开发哪个模型,我构建了各种不同的模型,仅使用`text`作为输入,并使用 5 重分层[交叉验证](/cross-validation-c4fae714f1c5)测试每个模型。我使用的模型是:*   [支持向量机](/algorithms-from-scratch-support-vector-machine-6f5eb72fce10)
*   [逻辑回归](/algorithms-from-scratch-logistic-regression-7bacdfd9738e?source=collection_tagged---------5----------------------------)
*   [朴素贝叶斯分类器](/algorithms-from-scratch-naive-bayes-classifier-8006cc691493?source=collection_tagged---------1----------------------------)
*   随机梯度下降分类器
*   [随机森林](/random-forest-overview-746e7983316)
*   LightGBM
*   双向 LSTM为了将文本从自然语言转换成计算机语言——仅仅是数字——我使用了 3 种不同的方法:*   术语频率—逆文档频率
*   字数
*   Word2vec在使用我的交叉验证策略运行每个模型后,我取了每个模型在所有折叠上的表现的平均值,并以此作为指标来决定我要继续使用的模型,并用于进一步的分析。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/96eb9b9172a7481add5ee2c6c5042f68.png)作者图片![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/a22b44bc7c4495298a0ad26c2fe0dfa7.png)作者图片实际上,我确实认为这个数据集足够小,不需要递归神经网络,但无论如何,在没有任何调整、特征工程和仅使用`text`特征的情况下,我们得到了双向 LSTM 非常不错的平均 F1 分数(这是本次比赛使用的指标)——如果你不熟悉 F1 分数,可以在我的[混淆矩阵](/confusion-matrix-un-confused-1ba98dee0d7f)文章中了解更多信息。## 估价为了评估模型,我将完整的训练数据分为 70%的训练数据和 30%的验证数据,这样我就可以构建一个模型,并评估模型在哪里出错。首先,我注意到我的 LSTM 模型非常严重地过度拟合了训练数据:*   训练 F1 分数— 0.9019 (4 标准英尺)
*   验证 F1 分数— 0.7444 (4 标准偏差)为了减少过度拟合,我们可以添加一些正则化作为即时解决方案,但我会在另一篇博客文章中进一步探讨这一点。从混淆矩阵中,我意识到该模型很难预测正面类,因为它犯了 294 个第二类错误——这意味着它预测负面,而推文实际上是正面的。这种现象的另一个名字叫做假阴性。这个问题可以追溯到我最初对不平衡的班级的关注。## 包裹在这篇文章中,我讨论了我从 Twitter 中探索文本数据的过程,以及我如何着手建立模型,以查看我将使用哪个模型进行进一步开发。在未来的工作中,我计划解决我们的数据存在的过度拟合问题,并建立一个非常基本的前端,以便你可以键入一些你自己的推文,你将能够看到系统是否认为你的推文是关于灾难的。在 [LinkedIn](https://www.linkedin.com/in/kurtispykes/) 和 [Twitter](https://twitter.com/KurtisPykes) 上与我联系,及时了解我和我关于人工智能、数据科学和自由职业的帖子。# 使用分类的情感分析> 原文:<https://towardsdatascience.com/sentiment-analysis-using-classification-e73da5b4159f?source=collection_archive---------30----------------------->## 如何根据历史数据计算情感得分![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/af8840dbd44ebf1d3d2fc14bd5666225.png)阴阳咖啡由亚历克斯从 [Unsplash](https://medium.com/u/2053395ac335?source=post_page-----e73da5b4159f--------------------------------)情感分析是一种常用的文本分析技术,用于确定文本是正面的、负面的还是中性的。它可以用来了解观众的满意度,也是一个很好的预测功能。如果您有一个标记良好的数据集(带有基本事实情感得分),您可以考虑使用文本分类来计算情感得分。## 使用分类的好处:1.  分类的使用**可以从特定于行业或主题的历史数据**中自动捕获模式。你不需要搜索特定主题的肯定或否定单词列表。
2.  包含不同的词袋计算作为情感分类的特征更加方便,因为您可以在现有的包中直接定义它。像“不好”和“不喜欢”这样的否定短语是可以捕捉到的,但是你自己很难捕捉到所有这些短语。
3.  有很多现有的分类算法供你选择。最佳拟合模型的潜力很大。> 唯一的缺点是:你需要有标签数据!# 资料组我们使用的数据是 Yelp 标记的数据集,来自 [Kaggle](https://www.kaggle.com/ilhamfp31/yelp-review-dataset) 。第一列是评论文本,第二列是情感分数的基本事实(1 是负面情感,2 是正面情感)首先,让我们将数据集分成训练集和测试集(您可以根据您拥有的数据量来决定 test_size)。如果你有一个足够大的数据集,你可以选择降低测试集的比例。

from sklearn.model_selection import train_test_split
Y=sample_new['rating']
X=sample_new['review']
X_train,X_test,Y_train,Y_test=train_test_split(X,Y,test_size=0.2)


# 数据预处理这里我们使用了两个 sklearn 包:1.  [**计数矢量器**](https://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.CountVectorizer.html) :将文本转换成令牌计数矩阵。令牌可以是单个单词或 2-gram 或 3-gram 短语。它还允许您在参数中指定 n_gram 范围、停用词移除等。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/6ce3da8e6a0f42289b3dd6f18e001902.png)CountVectorizer(单个单词标记)后的两个评论句子示例2. [**TfidfTransformer**](https://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.TfidfTransformer.html) :这里我们需要先了解一下 TF-IDF。TF-IDF 全称是‘词频-逆文档频’。术语频率是指单词或短语在整个文本中出现的频率。然而,如果一个术语出现得太频繁,那么它传达的有用信息就少了,TF-IDF 计算使用对数函数**缩小出现得太频繁的术语**。你也可以选择只使用词频来代替。> TF-IDF(术语,文档)=术语频率(术语,文档)*日志(文档总数/(文档频率+ 1))例如,数据科学文章通常包含“数据”一词。但是,它通常不会告诉你文章的观点(只是一个笼统的词)。TF-IDF transformer 基本上**把字数矩阵转换成频率矩阵**。

from sklearn.feature_extraction.text import CountVectorizer
vectorize = CountVectorizer(ngram_range=(1,2))
X_train_counts=vectorize.fit_transform(X_train)from sklearn.feature_extraction.text import TfidfTransformer
tfidf_transformer = TfidfTransformer()
X_train_tfidf = tfidf_transformer.fit_transform(X_train_counts)
X_train_tfidf.shape#(24000, 44563)


这里,我在 ngram_range 参数中将标记指定为单个单词或 2-gram 单词(可以根据需要将(1,2)改为 n_gram),最后输出频率矩阵作为分类的特征。# 分类模型和评估> 最后,建模部分对于使用什么没有限制,您可以对整个数据集进行采样,并查看哪个模型在准确性或其他评估指标方面最适合该样本。你可以在这里[参考](/top-5-metrics-for-evaluating-classification-model-83ede24c7584)如何评估你的分类模型。

from sklearn.linear_model import LogisticRegression
lr=LogisticRegression().fit(X_train_tfidf, Y_train)X_test_count=vectorize.transform(X_test)
X_test_tfidf=tfidf_transformer.transform(X_test_count)predicted=lr.predict(X_test_tfidf)#evaluate accuracy
np.mean(predicted == Y_test)

0.89883


逻辑回归给出了几乎 90%的准确率

from sklearn import svm
from sklearn.pipeline import Pipelinetext_svm = Pipeline([
('vectorize', CountVectorizer()),
('tfidf', TfidfTransformer()),
('svm', svm.SVC()),
])text_svm.fit(X_train,Y_train)
predicted=text_svm.predict(X_test)
np.mean(predicted==Y_test)

0.9


这里我使用了 sklearn 包管道将所有进程组合在一起。支持向量机精确地给出 90%的准确度。

from sklearn.neural_network import MLPClassifier
text_nn = Pipeline([
('vectorize', CountVectorizer()),
('tfidf', TfidfTransformer()),
('nn', MLPClassifier(solver='lbfgs', alpha=1e-5,
hidden_layer_sizes=(5, 2), random_state=1)),
])text_nn.fit(X_train,Y_train)
predicted=text_nn.predict(X_test)
np.mean(predicted==Y_test)

0.88167


深度学习 MLP 分类器可以达到 88%左右# 进一步改进1.  **count vectorizer**的参数调整:n_gram_range、最大/最小文档频率等参数,用于检索分类的理想频率得分。这需要对数据集有很好的理解,也需要一些反复试验来达到理想的输出
2.  **分类模型的参数调整**:您可以使用 grid_search 或 random search 之类的搜索方法,在准确性或其他指标方面找到最佳的参数集。这将导致整个过程运行更长时间。
3.  **分类模型评估:**评估多个指标的性能,如召回率、精确度,而不仅仅是准确度# 结论数据量对于分类性能非常重要。如果没有足够大的数据集,不要让模型太复杂,因为有**过度拟合**的风险。此外,对数据的初步理解很重要,在做所有这些工作之前,你应该拿出一些样本文本来更多地了解数据的模式。最后,如果你没有带标签的数据,想看看如何通过计算正面/负面词汇来计算情感得分,你可以参考我下面的另一篇文章。</design-your-own-sentiment-score-e524308cf787> # 使用 Pytorch 对预调整变压器进行情感分析> 原文:<https://towardsdatascience.com/sentiment-analysis-with-pretrained-transformers-using-pytorch-420bbc1a48cd?source=collection_archive---------17----------------------->## 使用 Huggingface Transformers 中最简单的 API 预测积极或消极的情绪![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/d0f882992a7a3dacc42b8b3085f27124.png)[卓成友](https://unsplash.com/@benjamin_1017?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍照# 介绍[**情感分析**](https://en.wikipedia.org/wiki/Sentiment_analysis) 自[**【NLP】**](https://en.wikipedia.org/wiki/Natural_language_processing)问世以来,一直是一项非常热门的任务。它属于**文本分类**的子任务或应用,其中从不同文本中提取和识别情感或主观信息。如今,世界各地的许多企业都使用情感分析,通过分析不同目标群体的情感来更深入地了解他们的顾客和客户。它还广泛应用于不同的信息来源,包括产品评论、在线社交媒体、调查反馈等。**在本文中,我们将向您展示如何通过 Huggingface 使用** [**变形金刚库**](https://github.com/huggingface/transformers) **快速有效地实现情感分析。**我们将使用预调整的变压器,而不是微调我们自己的变压器,所以需要很低的安装成本。所以,让我们直接进入教程吧!# 教程概述*   步骤 1:安装库
*   步骤 2:导入库
*   步骤 3:建立情感分析管道
*   步骤 4:输入文本
*   步骤 5:执行语义分析# 步骤 1:安装库我们需要安装的库是 Huggingface [变形金刚库](https://github.com/huggingface/transformers)。要安装变压器,您只需运行:

pip install transformers


*注意:Huggingface Transformers 需要安装*[*py torch*](https://pytorch.org/)*或*[*tensor flow*](https://www.tensorflow.org/)*中的一个,因为它依赖其中一个作为后端,因此在安装 Transformers 之前请确保有一个工作版本。*# 步骤 2:导入库在您成功地将 Transformers 安装到本地环境之后,您可以创建一个新的 Python 脚本并导入 Transformers 库。我们没有导入整个库,而是在库中引入了`[pipeline](https://huggingface.co/transformers/main_classes/pipelines.html)`模块,它提供了一个简单的 API 来执行各种 NLP 任务,并将所有代码复杂性隐藏在其抽象层之后。要导入`pipeline`模块,我们可以简单地做:

from transformers import pipeline


# 步骤 3:建立情感分析管道现在,在您导入`pipeline`模块后,我们可以开始使用该模块构建情感分析模型和分词器。为了建造它,我们可以做:

sentiment_analysis = pipeline(“sentiment-analysis”)


这将创建一个适合情感分析任务的管道。等等,你可能会问,这里用的是什么模型和记号化器。默认情况下,变形金刚库使用一个 [**DistilBERT**](https://arxiv.org/abs/1910.01108) 模型,该模型在来自 [GLUE 数据集](https://gluebenchmark.com/)的[斯坦福情感树库 v2 (SST2)](https://www.kaggle.com/atulanandjha/stanford-sentiment-treebank-v2-sst2) 任务上进行了微调。如果您想使用另一个模型或标记器,您可以在实例化管道时将它们作为`model`和`tokenizer`参数传递。# 步骤 4:输入文本我们已经建立了管道,现在是时候输入我们想要测试其情感的文本了。让我们为两个句子声明两个变量,一个肯定,一个否定:

pos_text = “I enjoy studying computational algorithms.”
neg_text = “I dislike sleeping late everyday.”


# 第五步:进行情感分析最后,是时候对我们输入文本的情绪(积极或消极)进行分类了。要执行情感分析,我们可以运行以下程序:

result = sentiment_analysis(pos_text)[0]
print("Label:", result['label'])
print("Confidence Score:", result['score'])
print()result = sentiment_analysis(neg_text)[0]
print("Label:", result['label'])
print("Confidence Score:", result['score'])


输出:> 标签:阳性
> 置信度得分:0.9000136363686
> 
> 标签:负的
> 信心分数:0.901038636866瞧,瞧!我们现在可以看到,该模型正确地将两个文本预测到它们的情感中,具有高的置信度得分。# 结论这个帖子到此为止!在这篇短文中,我们回顾了如何使用 Transformers 库提供的简单 API 进行情感分析。如果你想要的话,我在这里附上了这篇文章的 Jupyter 代码:如果你想知道如何对你自己的自定义数据集进行文本分类而不是情感分析,你可以参考我以前关于 BERT 文本分类的帖子。在那里,您将找到一个文本分类示例的深入演练和解释:</bert-text-classification-using-pytorch-723dfb8b6b5b>  如果你喜欢,请随意看看我的其他帖子,下次见!</text-generation-with-pretrained-gpt2-using-pytorch-563c7c90700>  </question-answering-with-pretrained-transformers-using-pytorch-c3e7a44b4012>  </machine-translation-with-transformers-using-pytorch-f121fe0ad97b>  </abstractive-summarization-using-pytorch-f5063e67510>  </semantic-similarity-using-transformers-8f3cb5bf66d6>  # 参考[1] [变形金刚 Github](https://github.com/huggingface/transformers) ,拥抱脸[2] [变形金刚官方文档](https://huggingface.co/transformers/),拥抱脸[3] [Pytorch 官网](https://pytorch.org/),艾研究[4] [Tensorflow 官网](https://www.tensorflow.org/),谷歌大脑[5] Sanh,Victor,et al. [“蒸馏伯特,伯特的蒸馏版:更小,更快,更便宜,更轻。”](https://arxiv.org/abs/1910.01108) *arXiv 预印本 arXiv:1910.01108* (2019)。[6] [斯坦福情感树库 v2 (SST2)](https://www.kaggle.com/atulanandjha/stanford-sentiment-treebank-v2-sst2) ,Kaggle[7] [通用语言理解评测(GLUE)基准](https://gluebenchmark.com/),NYU,UW NLP,DeepMind# 使用变形模型和心理扭曲进行情感分析> 原文:<https://towardsdatascience.com/sentiment-analysis-with-transformer-models-with-a-twist-of-psychology-fe19fe5383ae?source=collection_archive---------24----------------------->## [入门](https://towardsdatascience.com/tagged/getting-started)## 为什么情感分析需要先理解我们的思维方式,然后才能告诉我们我们的感受![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/095c0c0558640c517da5175e001a37b1.png)[Samule 孙](https://unsplash.com/@samule?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/transformers?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上的照片所以,你已经投入了大量的时间来开发热门的营销内容或撰写下一篇大文章(有点像这篇文章),并希望向你的观众传达某种情感。你想知道你的内容是否会引起你的观众的共鸣,并得出一种特定的感觉,无论是喜悦、愤怒、悲伤,以了解不同的人对你的内容的反应。文本分析,更具体地说是情感分析,无论如何都不是一个新概念,然而它也经历了几次模型的迭代,随着时间的推移变得越来越好。首先,我们从单词袋方法开始,来理解某些单词是否会传达某种情感。然后我们转到 RNN/lstm,它使用更复杂的模型来帮助我们理解情绪,尽管需要大量的训练,但缺乏并行性,这使得它非常慢,而且需要大量资源。2017 年,谷歌的研究人员提出了变压器模型的概念(图 1),这比它的前辈们效率高得多。首先,输入嵌入是多维的,它可以处理完整的句子,而不是一个接一个的一系列单词。其次,它有一个强大的多头注意力机制,使句子能够保持上下文和句子中单词之间的关系。它对每个单词进行多次注意力分析,以确保足够的采样。最后,它使用前馈神经网络来归一化结果,并提供情绪(或极性)预测。要了解更多关于 transformer 架构的信息,请访问 [huggingface](http://Huggingface.co) 网站![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/11c06e12542be435b1b5141b2722a18d.png)图一。变压器模型架构[1]现在我们已经理解了 transformer 模型,让我们双击这篇文章的要点,即对一个文档而不一定是一个句子进行情感分析。转换器模型使用的输入嵌入是句子嵌入,而不是整个段落或文档。为了分析一个文档,我们需要把句子分解成句子。为此,我使用 spacy 并定义了一个函数来获取一些原始文本并将其分解成更小的句子。以下面的句子为例。我们会把这个句子放入一个空间模型,这个模型会分析文本,并把它分解成一个语法句子列表。这里有一个函数来帮助我们完成这个任务和输出

class Sentiment:# Constructor with raw text passed to the init function
def init(self, raw_text):
self.raw_text=raw_text.lower()

def breakSentence(self, text_content):
self.text_content=text_content
nlp = English()
nlp.add_pipe(nlp.create_pipe(‘sentencizer’))
doc = nlp(self.text_content)
sentences = [sent.string.strip() for sent in doc.sents]
return sentences


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/63b0cd2f942682c1669e4cf6de3e726f.png)图 2: Spacy 句子列表[图片由作者提供]一旦你有了一个句子列表,我们将通过 transformer 模型循环它,以帮助我们预测每个句子是积极的还是消极的,以及得分是多少。您最终会得到类似下面的结果(图 3)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/65ff37cc55d2b5fb42f8e10f928fd076.png)图 3:带有情感极性和得分的句子突破[图片由作者提供]现在,一旦我们有了这些句子,我们可以假设你只是平均出你的正面和负面,并得出一个最终的极性分数。这种假设存在一些挑战。首先,我们假设每个句子都具有相同的权重,但这并不总是如此(稍后会详细介绍),其次,我们包括模型识别为负面的置信度相对较低的句子(60%负面,40%正面)。在我的研究中,我想过滤掉任何没有至少 90%得分的句子,无论是正面还是负面的。这是我为此开发的一些代码和结果。在这段代码中,我还定义了一个 before 和 after 结果,它可以帮助我理解我从多少个句子开始,又过滤掉了多少个句子。最后,它返回适当的句子和一个矩阵,每个过滤后的句子是如何分类的,1 表示肯定,1 表示否定。

def findRawTextPolarity(self):
confidence_level=0.9
nlpSA = pipeline("sentiment-analysis")
sentences=self.breakSentence(self.raw_text)
print('Before: ', len(sentences))
result = [{'sentence' : sentences[i],'label':nlpSA(sentences[i])[0]['label']} for i in range(len(sentences)) if nlpSA(sentences[i])[0]['score']>confidence_level]
print('After: ', len(result))
sentences= [result[i]['sentence'].lower() for i in range(len(result))]
labels= [result[i]['label'] for i in range(len(result))]
map_polarity={'NEGATIVE': -1, 'POSITIVE': 1}
matrix_result=[map_polarity[k] for k in labels]
return sentences, matrix_result


好了,到这一点,我们应该有一个过滤句子的列表,至少有 90%的预测和一个极性矩阵。现在是阅读心理学有趣的部分。当读者阅读文件时,他们倾向于在接近文件结尾时记住更多的内容,而在接近开始时记住较少的内容。第二,读者倾向于记住文档的顶峰或高潮。作者希望读者记住什么?如果你考虑峰端法则,这些陈述是正确的。峰值结束规则声明*“该理论声明总体评级由体验的峰值强度和体验的结束来确定。它不关心整个体验的平均值"*因此,理解 peak end rule 的含义并将其与我们的用例联系起来,确实,当我们给模型一个大的文本语料库时,我们会努力理解文章的峰值并赋予它稍微更多的权重,以及确定一种机制来为文档中后面的句子提供更多的权重。我们该怎么做?为了识别文章的高潮,我的假设是,我们需要理解机器如何对高潮进行分类,其中一种方法是使用文本摘要。文本摘要从文档中提取关键概念来帮助提取关键点,因为这将提供对作者希望你记住的内容的最佳理解。第二,我们需要定义一个衰减因子,这样当你在文档中往下移动时,前面的每一句都会减轻一些重量。好了,让我们来定义完成这些任务的函数。首先,让我们获取一个文本语料库,并使用 transformer 预训练模型来执行文本摘要。这个函数返回到峰值句子。

def findPeak(self):
summarizer = pipeline("summarization")
peak = (summarizer(self.raw_text))[0]['summary_text']
peak_sentences=self.breakSentence(peak)
return peak_sentences


接下来,我们将在本文前面定义的句子列表中找到这些峰值句子的位置。如果一个句子是峰值的一部分,我们将保留值 1,但如果它不是峰值句子,我们将删除它。我用过 0.9,但是你可以测试一些适合你的用例的东西。下面的函数可以完成这项任务。

def getPeakposition(self):
peak_weight_red=0.9
peak=self.findPeak()
sentences = self.findRawTextPolarity()[0]
matches=[[1 if operator.contains(s.replace(' .', ''),p.replace(' .', '')) else 0 for s in sentences] for p in peak]
match_filter=[m for m in matches if sum(m)>0]
sum_matrix=np.sum(np.array(match_filter),0)
map_polarity={1: 1, 0: 1* peak_weight_red}
matrix_result=[map_polarity[k] for k in sum_matrix]
return matrix_result


好了,现在我们需要创建一个机制来引入一个衰减因子,当一个句子在一篇文章中对人脑来说变老时,这个衰减因子会移除一定程度的权重。我已经创建了一个函数,使用线性衰减因子来降低它,但我也使用了工作良好的指数衰减。

def textWeights(self):
decay=0.01
matrix=self.findRawTextPolarity()
matrix_size=len(matrix[1])
decay_matrix=[1-(decay*i) for i in range(matrix_size)]
return decay_matrix


好了,我们现在应该有三个矩阵了1.  提供衰减权重因子
2.  为高峰句子提供权重
3.  过滤句子的极性现在变得容易了。我们将三者相乘,这将给出文档中每个句子的加权结果。既然这些都是加权的,我们就可以对整个文档的最终得分进行加权平均。我已经定义了我自己的分类尺度,但是你可以定义任何对你自己的用例有意义的东西。这里得到的最终分数是我开发的代码,后面是我得到的结果。

def getFinalScore(self):
peakposition=self.getPeakposition()
decay=self.textWeightsexp()
sent_polarity=self.findRawTextPolarity()[1]
fin_score = [abc for a,b,c in zip(peakposition,decay, sent_polarity)]
fin_sent_fct = lambda x: 'POSITIVE' if x>0.5 else ('NEUTRAL' if 0.49>x>-0.5 else 'NEGATIVE')
fin_sent=fin_sent_fct(np.mean(fin_score))
print('This document is categorized as {} with a final score of {}'.format(fin_sent, np.mean(fin_score)))


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/8eea3b441fc268dc6149d9d50e3f10da.png)图 4:最终情绪得分[图片由作者提供]这就是结局吗?不。情绪分析实际上是一个非常棘手的问题,需要适当的考虑。首先,情绪可能是主观的,不同的人有不同的解释。例如,我可能喜欢某篇文章的巅峰,而其他人可能会将不同的句子视为巅峰,因此引入了很多主观性。第二,我们利用了一个预先训练好的模型,但该模型应该用您自己的数据和特定用例来训练。您可以利用各种模型,其中一个很流行的模型是 BERT,但是您可以根据您的用例再次使用其他几个模型。如果做得好,情感分析也是一种分析文本的好方法,可以释放大量的洞察力,帮助你更好地做出数据驱动的决策。要观看视频示例,请访问 youtube 上的以下链接

https://youtu.be/sZOV5pD4ELg


来源:1.  Google 的 [Attention is all you need](https://arxiv.org/pdf/1706.03762.pdf) 一文中提出的变压器架构# 根据您的闲置数据进行情绪和参与度分析> 原文:<https://towardsdatascience.com/sentiment-engagement-analysis-from-your-slack-data-11f7ff995b62?source=collection_archive---------17----------------------->## 一瞥你松弛空间的情绪![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/c9746f2a7ec3f93d81a34c77f28300ee.png)图片由 Pankaj Patel 从[Unplash.com](https://unsplash.com/photos/OXkUz1Dp-4g)拍摄有没有想过你发布的内容有多吸引人?它是清晰的还是令人困惑的?或者在全公司会议上人们误解了你的意思?远程环境让教师和领导者很少有机会获得反馈并优化他们的内容以取得更好的绩效。因为我职业生涯的相当一部分已经是远程完成的了(实际上是在 covid 时代之前!),我发现这些问题在我渴望创造性解决方案的大脑中闪烁着兴奋和喜悦。我有数据,我所要做的就是起草我想回答的问题,然后去做。这也是我训练营的最后一个 e2e 项目,我身边有主题专家作为利益相关者(我的首席老师和我的项目导师)指导我开发一个价值驱动的产品。# 数据源我的数据集包括从第一天到最后一天由 Slack 管理员提供的 Ironhack 训练营的公开对话。也可以通过 Slack API 来完成,但是这超出了我的项目范围。为了保持这篇博文的简洁,请注意,我重点强调了代码中令人兴奋的部分,而不是它给出的见解。如果您正在寻找:*   视觉效果(在画面中完成)看看我的[演示](https://docs.google.com/presentation/d/1qMoWoY_3LL31Y4WvFe2di2z1FIUNOo3gU2yuRwjrI6o/edit?usp=sharing)
*   详细代码,浏览我的 GitHub repo [这里](https://github.com/lillaszulyovszky/ironhack-final-project)。# 数据清理和争论为了表示我在 JSON 文件数量方面遇到的挑战,这里是通用频道的文件夹,其中包含按天细分的所有对话,如下所示:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/8393de57941317bb514afd37d91eac31.png)所以我首先将每个通道的 JSON 文件加载到一个数据帧中。

# defining file path
path_to_json = '../raw_data/general/'

# get all json files from there
json_pattern = os.path.join(path_to_json,'*.json')
file_list = glob.glob(json_pattern)

# an empty list to store the data frames
dfs = []
for file in file_list:
# read data frame from json file
data = pd.read_json(file)
# append the data frame to the list
dfs.append(data)

# concatenate all the data frames in the list
channel_gen = pd.concat(dfs, ignore_index=True)
# test
channel_gen.tail(100)


然后为了方便起见,将每个通道的独立数据帧合并成一个数据帧。

#frames = [channel_gen, channel_books, channel_dmemes, channel_dresource, channel_dbootcamp, channel_funcommittee, channel_dvizbeauties, channel_frustrations, channel_finalproject, channel_frustrations, channel_funcommittee, channel_katas, channel_labhelp, channel_music, channel_random, channel_vanilla]

df = pd.concat([channel_gen, channel_books,
channel_dmemes, channel_dresource,
channel_dbootcamp, channel_funcommittee,
channel_dvizbeauties, channel_frustrations,
channel_finalproject, channel_frustrations,
channel_funcommittee, channel_katas,
channel_labhelp, channel_music,
channel_random, channel_vanilla], ignore_index=True, join="outer")


到目前为止,我的 dataframe 有 5263 行和 13 列,其中有一堆与我的项目无关的数据。清洁是艰难的。**清洗柱子&抬杠:**
  • subtype: filter out it's values from df, remove the original column\
  • ts: changing it to datetime, remove miliseconds, get days of the week, months of the year, type of the day, parts of the day\
  • user_profile: extract real_name in new column, remove the original\
  • attachments: extract title, text, link in new columns\
  • files: extract url_private and who shared\
  • attachments: extract title, text, link in new columns\
  • reactions: extract user, count, name of the emoji\

因为几乎所有的数据都嵌套在 JSON 库中,所以我项目的大部分时间都花在了迭代特性工程任务上,以获得可以用来训练模型的变量。同时,提取数据是我最喜欢的。下面你可以看到几个函数的例子,这些函数被创建来从数据帧中获得洞察力。谁发送的回复最多:

# user_profile column: extract real_namedef getrealnamefromprofile(x):
"""this function is applied to column user_profile
"""

if x != x:return 'noname'
else:return x['real_name']df_clean['real_name'] = df_clean['user_profile'].apply(getrealnamefromprofile)df_clean

群组中使用最多的表情符号是什么:

# reactions column: extract frequency

def getcountfromreactions(x):
"""this function is applied to column reactions
"""

**if** x != x:**return** 0
**else**:**return** x[0]['count']

df_clean['reactions_count'] = df_clean['reactions'].apply(getcountfromreactions)

df_clean


人们在频道上分享了哪些链接:

# files column: extract link

def geturlfromfile(x):
"""this function is applied to column files
"""

**if** x != x:**return** 'nofile'
**else**:**try**:**return** x[0]['url_private']**except** **KeyError**:**return** 'nolink_infiles'

df_clean['link_of_file'] = df_clean['files'].apply(geturlfromfile)

df_clean


为了帮助我找到交流的来源,我创建了另一个函数来区分首席教师和助教与学生。

# create a new column with teaching and students
def applyFunc(s):
if s == 'siand the LT (she/her)':
return 'teacher'
if s == 'Florian Titze':
return 'teacher'
if s == 'Kosta':
return 'teacher'
else:
return 'student'
return ''

df_clean['participant'] = df_clean['real_name'].apply(applyFunc)
df_clean['participant'].value_counts()


最后,在准备模型之前,我花了一点时间带着感激的心情检查了一下我清理过的数据框:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/b3be165e218c1c2b2fd58f4fe2ee095f.png)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/1a27e9b8535f2143f803dede154c72d1.png)# 自然语言处理在学习这一部分的时候,我意识到这是我想专攻的。文本分析,听起来很酷,对吧?想象一下,当机器可以在几毫秒内完成时,人们花在阅读繁琐的文本并试图用充满偏见的大脑分析它的时间有多长。让我颤抖。我的范围最初还包括文本特征提取(因为这是你可以从书面交流中获得的最有价值的东西),这是我现在正在做的事情,但是,在那 5 天里我没有时间做这件事,这个主题也超出了训练营的范围。相反,我专注于获得每条评论的情感分数,并从最常用的单词中生成一个令人敬畏的 worldcloud,作为给我同事的礼物。❤️## 密码

def clean_links(df):
#replace URL of a text
df_sent['text'] = df_sent['text'].str.replace('http[s]?😕/(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*(),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', ' ')

clean_links(df_sent)
df_sent['text']# load VADER
sid = SentimentIntensityAnalyzer()# add VADER metrics to dataframedf_sent['scores'] = df_sent['text'].apply(lambda text: sid.polarity_scores(text))df_sent['compound'] = df_sent['scores'].apply(lambda score_dict: score_dict['compound'])df_sent['comp_score'] = df_sent['compound'].apply(lambda c: 'pos' if c >=0 else 'neg')#test
df_sent.head()


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/b06e9aeef660591c8f7c92e513cdfc69.png)这很容易。现在,到了具有挑战性的预处理部分,创建一个没有链接、数字、标点、停用词的世界云:

# set of stopwords to be removed from text
stop = set(stopwords.words('english'))

# update stopwords to have punctuation too
stop.update(list(string.punctuation))

def clean_text(text_list):

*# Remove unwanted html characters*
re1 = re.compile(r'  +')
x1 = text_list.lower().replace('#39;', "'").replace('amp;', '&').replace('#146;', "'").replace(
'nbsp;', ' ').replace('#36;', '$').replace('**\\**n', "**\n**").replace('quot;', "'").replace(
'<br />', "**\n**").replace('**\\**"', '"').replace('<unk>', 'u_n').replace(' @.@ ', '.').replace(
' @-@ ', '-').replace('**\\**', ' **\\** ')
text = re1.sub(' ', html.unescape(x1))*# remove non-ascii characters*
text = unicodedata.normalize('NFKD', text).encode('ascii', 'ignore').decode('utf-8', 'ignore')*# strip html*
soup = BeautifulSoup(text, 'html.parser')
text = soup.get_text()*# remove between square brackets*
text = re.sub('\[[^]]*\]', '', text)*# remove URLs*
text = re.sub(r'http\S+', '', text)*# remove twitter tags*
text = text.replace("@", "")*# remove hashtags*
text = text.replace("#", "")*# remove all non-alphabetic characters*
text = re.sub(r'[^a-zA-Z ]', '', text)*# remove stopwords from text*
final_text = []
**for** word **in** text.split():**if** word.strip().lower() **not** **in** stop:final_text.append(word.strip().lower())text = " ".join(final_text)*# lemmatize words*
lemmatizer = WordNetLemmatizer()    
text = " ".join([lemmatizer.lemmatize(word) **for** word **in** text.split()])
text = " ".join([lemmatizer.lemmatize(word, pos = 'v') **for** word **in** text.split()])*# replace all numbers with "num"*
text = re.sub("\d", "num", text)**return** text.lower()*# apply cleaning function*

df_train['prep_text'] = df_train['text'].apply(clean_text)
df_train['prep_text'].head(5)# apply wordcloud function
make_wordcloud(df_train['prep_text'])


结果呢:(ta-daa)![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/d65d97583d2a4777e181f22ef6f7bb3b.png)# 机器学习模型为了在这里强调一些很酷的东西,我采用了随机森林分类模型来查看您需要哪些特征来获得回复(在这种情况下,从群组中获得帮助),准确度分数为 0.86:

# feature importance
feat_importances = pd.Series(importances, index=X.columns)
plt.figure(figsize=(10,10))
feat_importances.nlargest(15).plot(kind='barh', color='#FF9B48', width= 0.7)
plt.xlabel('Level of importance', fontsize=16)
plt.ylabel('Features', fontsize=16)
plt.yticks([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14], ['length_of_text', 'neutral_tone', 'positive_tone',
'amount_of_reactions', 'negative_tone',
'may', 'morning','march', 'files_attached',
'teacher_posted', 'evening', 'early_morning',
'labhelp_channel', 'general_channel', 'got_reaction'])

plt.title("Top 15 Important Features", fontsize=20)
plt.show()


![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/c790a249a4a0a75f645c1fd2a9ffc4d1.png)看起来你有更好的机会得到回复,如果你:用中性或积极的语气写一封长信,收到很多回复,早上发送也有帮助,或者你有一个附件。# 结论通过这个项目,我学到了一些东西:*   在你投资的事情上工作是一个游戏改变者
*   利益相关者在你身边是无价的
*   迭代是关键
*   从长远来看,函数可以节省您的时间接下来,我将利用这个数据集,运用我从 Udemy 的 NLP 课程中获得的知识,从评论中提取一些很酷的东西。# 拉丁语的零距离情感分类(借助其后代)> 原文:<https://towardsdatascience.com/sentiments-of-rome-80eb617b5980?source=collection_archive---------42----------------------->## 一种用于死(和/或低资源)语言的 NLP 方法![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/2f68f77113b3e83cf62c9050cded6612.png)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上由 [L A L A S Z A](https://unsplash.com/@lalasza?utm_source=medium&utm_medium=referral) 拍摄的照片# 介绍[拉丁语](https://www.britannica.com/topic/Latin-language)可以被认为是“旧世界的英语”。我把它与英语进行了比较,以强调这样一个事实,就像英语是当今世界上最流行的语言一样,拉丁语在古代西方世界占据主导地位。这不仅仅是因为受欢迎,而是因为罗马扩张的影响,尤其是在西欧和地中海地区。然而,拉丁语直接塑造了许多现代语言([罗曼语](https://en.wikipedia.org/wiki/Romance_languages)),也对其他语言产生了一些影响(比如英语)。我想看看拉丁语在现代自然语言处理(NLP)环境中是如何工作的(使用基于 transformer 的预训练模型),我在这篇短文中提到了这一点。古典拉丁语现在被认为是一种死亡的语言,这使得它在 NLP 领域也是一种低资源语言。拉丁语的近亲被认为是撒丁语和意大利语。然而,它们大多是从“[粗俗拉丁语](https://en.wikipedia.org/wiki/Vulgar_Latin)”演变而来,这是一种在平民中流行的非文学拉丁语。我假设涉及拉丁语的 NLP 任务(情感分类任务)将由这些语言中的一种来帮助,尽管预训练模型在其预训练或微调阶段没有见过拉丁语。这也被称为零起点学习,我们可以将相关语言的知识转移到拉丁语中,而无需在拉丁语语料库中进行微调。# **方法**作为一种资源匮乏的语言,要获得一个庞大的带注释的拉丁语语料库并不容易。我在互联网上使用了一个可访问的[数据集](https://github.com/CIRCSE/Latin_Sentiment_Analysis/blob/main/data/GoldStandardv1-Horace.tsv)【4】【5】,它由 45 个拉丁句子组成,分为 3 个情感类别(积极、消极、中性、混合),并从[贺拉斯的颂歌](https://en.wikipedia.org/wiki/Odes_(Horace))中提取(数据集的创建者有一篇即将发表的论文使用了相同的数据集,请检查参考文献)。为了简单起见,我去掉了混合句和中性句。我使用了基于 transformer 的微调单语模型(用于情感分类),可用于[意大利语](https://huggingface.co/MilaNLProc/feel-it-italian-sentiment) (Bert)、[英语](https://huggingface.co/siebert/sentiment-roberta-large-english) (Roberta)、[西班牙语](https://huggingface.co/finiteautomata/beto-sentiment-analysis) (Bert)、[德语](https://huggingface.co/oliverguhr/german-sentiment-bert) (Bert)、以及[印尼语](https://huggingface.co/w11wo/indonesian-roberta-base-indolem-sentiment-classifier-fold-3) (Roberta)上的 [Huggingface](https://huggingface.co/) 。撒丁语也是一种资源匮乏的语言(或者至少网上没有足够的资源),因此它被抛弃了。拉丁语数据集由如下句子组成,> “这是一个很好的例子”——否定的
> “非自我的酒神是我的朋友”——肯定的对于 Huggingface 模型,剩下的工作很容易,每个模型都遵循通常的步骤(在 Huggingface 中也提到过)。结果是通过将标记化的拉丁句子直接输入到微调模型中获得的。

import torch
import numpy as np
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import pandas#Read the corpuslat=pandas.read_csv('latin.tsv',sep='\t')lat=lat[lat['Value']!='mixed']
lat=lat[lat['Value']!='neutral']sentences=lat.loc[:,'Text']labels=lat.loc[:,'Value']
true_val=map(lambda x:1 if x'positive' else 0,labels)# Load model and tokenizertokenizer = AutoTokenizer.from_pretrained(“MilaNLProc/feel-it-italian-sentiment”)model = utoModelForSequenceClassification.from_pretrained(“MilaNLProc/feel-it-italian-sentiment”)#tokenize the sentencesinputs = tokenizer(list(sentences),padding=True, return_tensors="pt")#first 4 lines can slightly change with the model, e.g.- for spanish model, outputs = model(inputs),logits = outputs.logitsproba = torch.nn.functional.softmax(logits, dim=0)labels = torch.tensor([0]*len(list(sentences)).unsqueeze(0)
outputs = model(
inputs, labels=labels)
loss, logits = outputs[:2]
logits = logits.squeeze(0)prob = torch.nn.functional.softmax(logits, dim=0)result=[]for i in prob:
result.append(list(i.detach().numpy()).index(np.max(i.detach().numpy ())))from sklearn.metrics import accuracy_score
accuracy_score(list(true_val),result)#for some models, their output further needs to be processed in order to have numerical valueresult=[]for i in sentences:
result.append(1 if sentiment_analysis(i)[0]['label']
'POSITIVE' else 0)


对于包括任务流水线的模型,

from transformers import pipeline#Here the pytorch models are not created explicitly like in the previous snippet. Yet, the models gives an output similar to previoussentiment_analysis = pipeline(“sentiment-analysis”,model=”siebert/sentiment-roberta-large-english”)result=[]for i in sentences:
result.append(1 if sentiment_analysis(i)[0]['label']=='POSITIVE' else 0)accuracy_score(list(true_val),result)


# 结果[遗传相似度](https://en.wikipedia.org/wiki/Genetic_relationship_(linguistics))是语言学中用来衡量一对语言之间的相似性或关联性的一种尺度。它的值越高,这一对语言之间的距离就越大!。我使用了一个[在线工具](http://www.elinguistics.net/Compare_Languages.aspx)来测量拉丁语和上面提到的其他语言之间的相似性。它显示了如下关系。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/ff4718d5df06f88cf6a09796a2c7c293.png)按作者像意大利语和西班牙语这样的罗曼语最接近拉丁语,而像英语和德语这样的日耳曼语则稍远一些。印尼语和拉丁语没有关系,但是印尼语在其拼写中采用了拉丁语。让我们来看看所选语言的每个模型如何执行零镜头分类。对于具有 3 个类别输出(德语)的模型,将少数中性/混合预测分别改为阴性和阳性,并考虑其中的最高准确度得分。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/b2610f64fbcd863f0660a3bac723f636.png)按作者令人惊讶的是,语言之间的关系模式在这里没有出现。每个实例(除了印度尼西亚语)的准确度都大于 0.5,西班牙语显示了罗曼语的最高准确度分数。英语表现的最好,同时也是最接近拉丁语的语言;意大利语在后面。尽管一种语言与拉丁语相差甚远,但这并不总是意味着性能下降。这种结果的一个可能原因是模型的能力(这取决于模型中的参数数量、预训练目标和用于预训练模型的语料库)。另一个主要原因是每个语言模型中使用的预训练/微调数据集的大小。意大利模型[1]使用由 2k 条推文组成的情感数据集进行了微调,而英国模型[2]也在属于不同领域的多个数据集(推文、评论等)上进行了微调。).西班牙模型[3]利用了大约 5k 的 tweets 进行微调。有人可能会认为,无论源语言是什么,用于模型的训练语料库的质量/规模都会直接影响其零炮性能。此外,该模型可能能够学习句子情感的底层表示,而不管语言如何,并且所学习的知识可以在目标语言的零射击任务期间使用。你怎么想呢?在评论里提!!!这不是一套广泛/完整的实验和分析,因此无法对导致这一结果的因素做出明确的结论。甚至其他可用的和未经测试的模型也可能揭示一个新的结果。然而,据观察,语言相关度并不是唯一决定拉丁语情感分类零命中率的因素。我相信这是一个有趣的研究领域。感谢您的阅读!# 参考[1]——“感受:意大利语的情感和情绪分类,比安奇等人”,[https://aclanthology.org/2021.wassa-1.8/](https://aclanthology.org/2021.wassa-1.8/)[2]——“不止是一种感觉:情感分析准确性的基准,[海特曼](https://papers.ssrn.com/sol3/cf_dev/AbsByAuth.cfm?per_id=1911466)等,【https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3489963】T4[3]—[https://github.com/finiteautomata/pysentimiento/](https://github.com/finiteautomata/pysentimiento/)[4]——[https://github.com/CIRCSE/Latin_Sentiment_Analysis](https://github.com/CIRCSE/Latin_Sentiment_Analysis)[5]——“拉丁诗歌的情感分析:贺拉斯颂歌的第一次实验。,曼布里尼等人,“CLiC-it,202,[https://clic2021.disco.unimib.it/](https://clic2021.disco.unimib.it/)# 九月版:概率编程> 原文:<https://towardsdatascience.com/september-edition-probabilistic-programming-11a6699e2fac?source=collection_archive---------24----------------------->## [月刊](https://towardsdatascience.com/tagged/monthly-edition)## 模拟我们周围高度复杂的世界![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/3b9a36f2a22a5b6ef23266c29027c76d.png)照片由来自[佩克斯](https://www.pexels.com/photo/close-up-photo-of-person-holding-lensball-2534493/?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)的[麦克·穆林斯](https://www.pexels.com/@mac-mullins-1319876?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)拍摄我们是一个不可思议的物种:我们对周围的世界非常好奇,喜欢学习,并经常找到新的方法和工具来做这件事。过去几十年中的一个进步就是计算。通过改进计算引擎的架构,我们在模拟复杂动态、列举大量潜在结果以及对大量可能性进行快速计算方面做得更好了。所有这些事件都推动了我们对周围世界的理解。特别是机器学习(ML)为我们提供了一套高度灵活和强大的工具来预测、理解和推理未来。虽然目前最先进的方法在第一个目标上表现出色,但在后两个目标上进展缓慢。其中一个原因是视角。世界本来就很复杂。此外,事件或结果通常是众多因素之间非线性相互作用的结果。通常,我们并不掌握影响因素的完整列表的信息,也不知道它们如何相互作用以产生观察到的结果。这些属性使得预测现实世界的事件变得极其困难。ML 社区已经使用两种根本不同的模型构建范例来应对这一挑战。它们是:1.  区别的
2.  生殖的我们在学校学到的和在新闻中听到的大多数模特都属于歧视型。这个框架的主要焦点是预测的准确性。观察到的数据引导关系发现过程,而不是对因素如何相互作用强加严格的假设。在这种范式下训练的深度神经网络已经在各种各样的任务上取代了人类水平的表现,主要是因为强调预测的准确性。虽然这非常令人印象深刻,但这些模型有其局限性。特别是,高度精确的网络非常复杂,需要大量的训练数据才能达到人类水平的性能。通常,这些模型用可解释性来换取卓越的性能。这个方面有好有坏。一方面,我们希望对未来有非常准确的预测。精确使我们在面对不确定的结果时能够做好计划并茁壮成长。然而,难以解释的模型招致了很多怀疑,并疏远了人们。这种信任的缺乏缩小了这些强大算法的使用范围。复杂性也让我们很难对未来进行推理。如果我选择路径 A 而不是路径 B 会发生什么?路径 B 比路径 A 短吗?统计学家已经开发了一个广泛的工具包来回答这些问题。在辨别范式下这样做是很棘手的,因为这种思维模式并不试图明确地模拟内在的不确定性。生成范式正好解决了这一困境。这一学派主张对世界做出假设,而不是回避它。任何上过统计学入门课的人都知道,统计模型允许我们给不确定性一个函数形式。这些模型让我们对自己的预测充满信心。他们让我们假设潜在的结果。诸如此类的活动使我们能够思考未来,并根据新信息做出决策。每个 stats 101 的学生也知道,所有这些模型经常做出严格的假设。有时这些假设被违反,有时它们就是完全错误的。更糟糕的是,这些模型中的一些并不能很好地近似真实世界的现象。这一缺点主要是由于这些模型不能适应可能影响感兴趣的结果的多个潜在变量。虽然你,读者,现在可能想认输,但我劝你不要。生成模型师也对构建糟糕的世界近似持谨慎态度。然而,与他们的歧视性同行不同,他们融合了计算机科学、统计学和软件工程的概念来解决上述问题。其结果是一类被称为概率图形模型(PGM)的模型。这些图表允许我们使用尽可能多的变量来模拟我们周围高度复杂的世界。它们让我们将一些、许多或所有这些变量相互联系起来。它们还为我们提供了确定信息如何在网络中流动的灵活性。信息应该从节点 A 传到节点 B 还是从节点 B 传到节点 A?也许我们没有具体说明,因为我们对信号应该如何通过我们的近似世界流动没有强烈的信念。PGM 的强假设允许他们建立稀疏模型。这些假设也使这些网络能够用比判别模型少得多的数据点进行学习。这些特征类似于人类形成一个世界如何运转的心理图像,并对其进行查询以确定如何在不熟悉的环境中行为。除了建立精确的模型,另一个阻碍 PGMs 广泛应用的挑战是计算。存储大量随机变量的分布并更新它们的分布在计算上是禁止的。这些限制往往是 PGM 如何使用当前的编程语言和数据结构编码的产物。因此,从业者开发了一种新的编程范式,称为概率编程,以规避这些问题。这些语言将随机变量和概率分布视为一等公民。由于这些发展和该领域中一系列相关的发展(例如变分贝叶斯),越来越复杂的 PGM 变得更容易构建。这些创新还减少了查询这些模型和推理潜在行动过程的时间。近来,生成模型还没有像判别模型那样受到关注。我希望这篇介绍能鼓励你们所有人看看这种不同的思维框架。如果你想更深入地了解这个话题,我强烈建议你去看看一些关于这个话题的精彩文章。[Abdullah Farouk](https://medium.com/u/ea00177500c1?source=post_page-----11a6699e2fac--------------------------------) ,*TDS 的志愿编辑助理*## [概率图形模型介绍](/introduction-to-probabilistic-graphical-models-b8e0bf459812)有向图形模型和无向图形模型到[布拉尼斯拉夫·霍兰德](https://medium.com/u/cb9a2fa1a025?source=post_page-----11a6699e2fac--------------------------------) — 11 分钟## [让你的神经网络说“我不知道”——使用 Pyro 和 PyTorch 的贝叶斯神经网络](/making-your-neural-network-say-i-dont-know-bayesian-nns-using-pyro-and-pytorch-b1c24e6ab8cd)当一个人不确定的时候,很大一部分智慧是不行动的由 [Paras Chopra](https://medium.com/u/ce4d7f282c52?source=post_page-----11a6699e2fac--------------------------------) — 17 分钟## [生成性与鉴别性概率图形模型](/generative-vs-2528de43a836)朴素贝叶斯和逻辑回归的比较由[四维引起维克](https://medium.com/u/301dc9114da0?source=post_page-----11a6699e2fac--------------------------------) — 5 分钟## [变分贝叶斯:变分自动编码器(VAEs)背后的直觉](/variational-bayes-4abdd9eb5c12)了解推动最先进模型的强大理念到[安维斯·马尔韦德](https://medium.com/u/4bcb754eb66?source=post_page-----11a6699e2fac--------------------------------) — 7 分钟## [概率编程简介](/intro-to-probabilistic-programming-b47c4e926ec5)使用张量流概率(TFP)的用例由[法比亚娜·克莱门特](https://medium.com/u/f957353899a?source=post_page-----11a6699e2fac--------------------------------) — 6 分钟## [概率推理的基本问题](/fundamental-problems-of-probabilistic-inference-b46be1f96127)如果你是机器学习从业者,为什么要关心采样?作者:Marin Vlastelica pogan ii—6 分钟我们也感谢最近加入我们的所有伟大的新作家:[奥辛·杜塔](https://medium.com/u/d6cc8b2f4d83?source=post_page-----11a6699e2fac--------------------------------)、[雷纳托·菲林尼奇](https://medium.com/u/45c682a44b92?source=post_page-----11a6699e2fac--------------------------------)、[奥姆里·卡杜里](https://medium.com/u/bdd98c8f5e5d?source=post_page-----11a6699e2fac--------------------------------)、[陈莉莉](https://medium.com/u/8c4ed724d89d?source=post_page-----11a6699e2fac--------------------------------)、[本·威廉姆斯](https://medium.com/u/348277337eba?source=post_page-----11a6699e2fac--------------------------------)、[伊恩·加贝尔](https://medium.com/u/c1f02b4cdd2b?source=post_page-----11a6699e2fac--------------------------------)、[乔纳森·拉塞尔森、博士](https://medium.com/u/56d1c8006910?source=post_page-----11a6699e2fac--------------------------------)、[乔安娜·伦丘克](https://medium.com/u/a1797fa50e3a?source=post_page-----11a6699e2fac--------------------------------)、[恰冲](https://medium.com/u/979f90d1d0cd?source=post_page-----11a6699e2fac--------------------------------)、[胡曼·阿布·阿尔拉贾](https://medium.com/u/ffb6117848cc?source=post_page-----11a6699e2fac--------------------------------) [佩德罗·布里托](https://medium.com/u/ab311ef28c08?source=post_page-----11a6699e2fac--------------------------------),[丹尼斯·冯](https://medium.com/u/8f37637cb451?source=post_page-----11a6699e2fac--------------------------------),[周扬](https://medium.com/u/ab8db76f5314?source=post_page-----11a6699e2fac--------------------------------),[戴夫·德卡里欧](https://medium.com/u/96c0759c980?source=post_page-----11a6699e2fac--------------------------------),[阿奇特·亚达夫](https://medium.com/u/c6d1f756f2d4?source=post_page-----11a6699e2fac--------------------------------),[贾·一禅](https://medium.com/u/6136a4072f92?source=post_page-----11a6699e2fac--------------------------------),[阿尔帕纳·梅塔](https://medium.com/u/c484705e735?source=post_page-----11a6699e2fac--------------------------------),[拉胡尔·桑戈莱](https://medium.com/u/575baa0e041c?source=post_page-----11a6699e2fac--------------------------------),[阿南亚·巴塔查里亚](https://medium.com/u/57cce684289b?source=post_page-----11a6699e2fac--------------------------------),[罗汉·苏库马兰](https://medium.com/u/4e9f21318991?source=post_page-----11a6699e2fac--------------------------------), <https://medium.com/u/4e9f21318991?source=post_page-----11a6699e2fac--------------------------------> [杰克·米切尔](https://medium.com/u/3d9ea428abd9?source=post_page-----11a6699e2fac--------------------------------)、[z·玛利亚·王博士](https://medium.com/u/9ae0925c6ef7?source=post_page-----11a6699e2fac--------------------------------)、[丹尼斯](https://medium.com/u/6e5946e53b95?source=post_page-----11a6699e2fac--------------------------------)、[南阮](https://medium.com/u/3db9e6680953?source=post_page-----11a6699e2fac--------------------------------)、[琪·市川](https://medium.com/u/8e797d0e0840?source=post_page-----11a6699e2fac--------------------------------)、[林赛·蒙塔纳里](https://medium.com/u/d186712ca417?source=post_page-----11a6699e2fac--------------------------------)、[戴维斯·特雷比格](https://medium.com/u/b2a36e2f3cc4?source=post_page-----11a6699e2fac--------------------------------)、[瓦妮莎·王](https://medium.com/u/27ce3ac93d70?source=post_page-----11a6699e2fac--------------------------------)、[普亚·阿米尼](https://medium.com/u/203a584aebef?source=post_page-----11a6699e2fac--------------------------------)、 [我们邀请你看看他们的简介,看看他们的工作。](https://medium.com/u/67ea5d79b73e?source=post_page-----11a6699e2fac--------------------------------)# PyTorch 中带深度估计的序列做梦> 原文:<https://towardsdatascience.com/sequence-dreaming-with-depth-estimation-in-pytorch-d754cba14d30?source=collection_archive---------23----------------------->![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/2283d6e54f1c8e64aea285bdb63fbb6b.png)梦境类:图腾柱+减噪。作者图片## 人工艺术合成虽然“沉睡”仍然是 reddit 上的大炒作,但我决定在连续帧(即视频)的深度做梦背景下,再看看开放性问题。受之前工作的启发,例如[这个 Caffe 实现](https://github.com/samim23/DeepDreamAnim),除了将所有东西集成到最新的 PyTorch 框架中,我还想包含更多关于单类做梦(见我之前的[帖子](/deep-lucid-dreaming-94fecd3cd46d))和深度估计的最新见解。三轮车系列梦。**你可以在这里找到我的回购**(っ◔◡◔)っ♥<https://github.com/Beinabih/Pytorch-HeadTrip>****♥********与我以前使用的一些类似的代码相比,我的实现大大改善了梦模式的闪烁。这个问题的发生通常是因为光流算法计算的矢量场在变化很小的区域接近于零,这导致了后续帧之间相应的梦内容的较大差异。这可能导致模式以高达每秒帧数的频率变化。我通过将前一个梦的模式扭曲到下一帧,并用流矢量场来参数化更新的强度,来解决这个问题。这种方法可以跟踪场景中运动的物体,还可以平稳地梦到新出现的物体和背景。目前,场景切换仍然必须手动处理,否则结果通常会显示不同场景之间梦境内容的不自然重叠。这个问题将来会得到解决。********在每一步中,使用 [Farneback 方法](https://link.springer.com/chapter/10.1007%2F3-540-45103-X_50)(由 opencv2 提供)或可选的[空间金字塔网络](https://arxiv.org/abs/1611.0085) (SPyNet)计算矢量场。为了获得更高的流量预测精度,应该使用 SPyNet。********我的代码可以用 PyTorch 1.8 运行。实现了以下附加功能:*****   ****单一阶层做梦****
*   ****用 Pytorch MiDas 进行深度估计****
*   ****各种分类模型(resnet、vgg19、inception……)****
*   ****带 SPyNet 或 Farneback 的光流(opencv2)********![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/50ac4e7be1579bf32b3063601d330be0.png)********睡袋海洋与深度估计。作者图片****# ******设置**********config 文件夹中提供了一个基本配置文件。它包含了做梦所需的所有参数。**********视觉表现**********输入图像被二次抽样到更小的分辨率,以便在许多不同的尺度上生成梦的结构。在较高分辨率下做梦会给在较低分辨率下产生的粗糙结构增加细节。参数 **num_octaves** 和 **octave_scale** 表征图像金字塔。 **num_iterations** 表示网络处理输入的次数。以下是将 **num_iterations** 或学习率( **lr** )设置为非常大的值时获得的示例结果:********![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/3304fc6636e98aa274158b4d437cfe93.png)********涉及到一些后期处理步骤,如改变颜色和锐化。作者图片********我们可以观察到大量的细节,然而,图像的原始内容已经完全消失了。通过将 **random** 设置为 True,可以使上述参数随机化。**********梦的内容**********如果你想对你一直看到的狗的典型眼睛和脸之外的梦的特征有更多的控制,你可以将**引导的**参数设置为*真*以启用特定类的梦。这是我在之前的[帖子](/deep-lucid-dreaming-94fecd3cd46d)中提到过的一个话题。通过将相应的索引添加到 **channel_list** 中,您可以从[该列表](https://gist.github.com/yrevar/942d3a0ac09ec9e5eb3a)中选择并混合任意数量的 ImageNet 类。在下面的示例视频中,我使用了三个类:蘑菇、天鹅绒和雨伞。********在蘑菇、天鹅绒和雨伞上做梦********相反,如果您希望算法动态选择模型分配最高概率的类别,请将 **max_output** 设置为 *True* 。一个实验特性是**金字塔 _ 最大值**参数,当设置为真时,在每个八度音阶中独立选择最可能的类别。**********深度估计**********还可以将梦的内容与感知的图像深度联系起来。首先,使用 PyTorch MiDas 模块计算图像的相对逆深度。深度图随后被归一化到区间[0,1]中,用于放大或抑制做梦。通过将**使用深度**设置为*真*可以激活深度估计。您还可以通过设置 **depth_str** 为遮罩指定一个倍增强度因子。********将 **use_threshold** 设置为真会将低于阈值 **th_val** 的所有内容归零。通过更改此参数,可以根据预测深度动态指定排除距离。如果你想排除前景,设置**反转深度**为*真*。********![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/aecfbcaa07cf9aa0ab1fd7f3fd488956.png)********前景(左)和背景(右)做梦。作者图片********太棒了。********对于非常大的输入,通过利用兼容硬件上的 Nvidia Apex 模块提供的混合精度算法,还可以减少所需的内存并加快推理速度。好在已经在 PyTorch 中实现了,所以只需要设置 **fp16** 为 *True* 即可。********现在发挥创意,制作一些很酷的图片和视频吧!****# 用递归神经网络进行序列间标记> 原文:<https://towardsdatascience.com/sequence-to-sequence-labeling-with-recurrent-neural-networks-5405716a2ffa?source=collection_archive---------22----------------------->## 编码器-解码器模型![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/43141e831160c4388f3b68fe88a186b1.png)莱昂纳多·大久保俊郎在 [Unsplash](https://unsplash.com/s/photos/language-learning?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上的照片序列到序列的标记问题是通过算法将一个字母表上的序列映射到另一个字母表上的“好”序列。这两个字母可能不同。序列的长度也是如此。这个定义隐含的意思是,有一些方法可以区分好的和坏的映射。我们来看一些值得注意的例子。**机器翻译**:将单词序列从一种自然语言翻译成另一种自然语言。(比如从英语到法语。每种语言的词汇构成了它的字母表。对话机器人:人类输入一句话或一个问题,机器人会给出合适的回答。在此设置中,输入和输出字母是相同的。人类和机器人“说着同一种语言”。**摘要**:输入一个长句、段落或更长的文本,并对其内容进行摘要。**词性标注**:输入一种语言(比如英语)的单词序列,输出单词(名词、动词、形容词等)的词性标签序列。**语音识别**:输入一系列音素(口语),输出它们所代表的文本序列。这个标记问题是一个机器学习问题,因为它涉及到预测给定输入序列的输出序列。这是监督学习的一种形式。也就是说,输入和输出都是*序列*。**这个问题难在哪里?**下面我们重点介绍机器翻译。词汇是巨大的:以英语和法语为例。英语有很多单词。法语也是。哪些英语单词对应哪些法语单词?也就是说,对于那些可绘制地图的人来说。词汇可能不可映射:两种语言可能如此不同,以至于它们的词汇甚至可能不可映射。也就是说,在一种语言中用一个词就能简明表达的东西,在另一种语言中可能需要用好几个词。**不仅仅是单词**:想想短语*数据挖掘*。它有一个非常具体的含义,来自于两个词*数据*和*采矿*的接近。(在 NLP 中,这种现象被称为*搭配*。)显然,我们需要以某种方式捕捉这个意思,这样它就不会在翻译过程中丢失。这只是冰山一角。更微妙的概念需要更复杂的词语序列来表达。为了让他们不在翻译过程中迷失,我们需要设法抓住他们的本质。**作为监督机器学习**序列到序列标记问题可以被框架化为学习预测给定输入序列的输出序列。虽然这种提法在原则上听起来很吸引人,但还是有一些关键的考虑因素。其中最重要的是,我们是否有足够丰富和多样化的标记训练集可用。这种训练集采取(*输入序列*、*输出序列*对)的形式。“足够富裕”的富裕程度取决于我们试图解决的问题的复杂程度。学习一个通用的英语到法语的翻译可能需要比学习回答一个受限领域的问题更广泛的训练集,比如一个机器人来回答关于一个特定产品的问题。接下来,我们应该考虑哪些机器学习算法?嗯,这不是监督学习吗?所以就不能随便用什么学习算法吗?不。输入和输出是序列。长短不一。经常在巨大的字母表上。因此,输入和输出空间的宇宙是巨大的。幸运的是,我们有递归神经网络(RNNs),它是为这类问题设计的。(尽管是受约束的版本;见下文。)它们输入一个序列,输出另一个序列。它们从(输入序列,输出序列)对的训练集中学习。约束是什么?输入和输出序列的长度必须相同。当输入序列中的第 I 个输入出现时,必须产生输出序列中的第 I 个输出。我们可以说输入和输出序列是对齐的。词性标注用例遵守这一约束。输入序列中的每个单词在输出序列中都有一个与之相关联的词性标签。一个单词的 POS 标签可能依赖于序列中的一些其他单词以及它们出现的顺序,这就是为什么我们将此建模为序列到序列的标注问题。机器翻译用例通常不会。根据语言和源序列中的具体内容,源序列和目标序列的长度可能会有所不同。用源语言表达某件事可能比用目标语言要用更多(或更少)的单词。文本摘要用例肯定不会。**递归神经网络和神经语言模型**让我们从比对序列的简单例子开始。有两个原因。首先,如前所述,递归神经网络精确地模拟了这种情况。其次,这种简单情况的解决方案将作为更高级情况的构建模块,在这种情况下,输入和输出序列具有不同的长度。有一篇中篇文章非常详细地介绍了这种(更简单的)情况[1]。这里我们只总结关键概念。**语言模型**:在 NLP 中,这是记号字母表上序列的*概率分布*。语言建模的一个中心问题是从例子中学习语言模型。(例如来自句子训练集的英语句子模型。)学习的模型然后可以用于预测给定记号序列的下一个记号。正如[1]中所讨论的,语言模型有许多用途。**递归神经网络**:这是一个神经网络,输入一个向量序列 **x** (1),…, **x** (T),输出一个相应的输出向量序列 **y** (1),…, **y** (T)。为了输出 **y** ( *t* ),在任何时候 *t* ,RNN 使用它能够从序列 **x** (1)、…、 **x** ( *t* )中学习和捕捉到的任何东西。正是这种能力让它变得如此强大——也相当复杂。与前馈神经网络形成对比,前馈神经网络仅从 **x** ( *t* )产生 **y** ( *t* )。那么我们如何从向量**x**(1)…, **x** ( *t* )的*序列*中预测 **y** ( *t* )?很自然的一件事就是把 **x** (1)、…、 **x** ( *t* )中的信息汇总成一个定长向量,称之为 **h** ( *t* ),然后从 **h** ( *t* )预测 **y** ( *t* )。可以,但是我们如何从 **x** (1),…, **x** ( *t* )中获得 **h** ( *t* )?这就是*循环*出现的原因。假设我们要从**x**(1)…, **x** ( *t* +1)预测 **y** ( *t* +1),就在我们从**x**(1)…, **x** ( *t* 预测完 **y** ( *t* )之后。在后面的预测中,我们使用了 **h** ( *t* )。首先我们从 **h** (t)和 **x** ( *t* +1)计算 **h** ( *t* +1)。于是,h( *t* +1)总结了 **x** (1)、…、 **x** ( *t* +1)中的相关信息。接下来,我们像之前一样从 **h** ( *t* +1)计算 **y** ( *t* +1)。**语言模型**的 RNN:这里 **x** ( *t* )是语言字母表中某个符号的矢量编码。这代表语言中相应序列中位置 *t* 处的符号。我们的兴趣是从 **x** (1),…, **x** (T)预测 **x** (T+1)。对于这个预测任务,将目标 **y** ( *t* )定义为 **x** ( *t* +1)是有意义的。现在让我们详细说明“语言字母表中某个符号的矢量编码”。主要有两条路可走:一热编码和分布式编码。在一键编码中,字母表中的每个符号都有一个维度。符号由一个向量表示,该向量在符号维度中的值为 1,在其余维度中的值为 0。在分布式编码(也称为嵌入)中,符号被投影到一个低得多的维度空间中,使得出现在相似上下文中的符号在该空间中彼此靠近。当字母表是一种语言的词典,即它的单词集时,这一点得到了最好的说明。人们会认为同义词的嵌入是相互靠近的。在语言模型的 RNN 中,输出通常是独热编码。这是因为我们的主要兴趣是从 **x** (1),…, **x** (T)预测 **x** (T+1),这自然表示为arg max _ s P(**x**(T+1)=编码(s) | **x** (1),…, **x** (T))其中 *s* 表示字母表中的符号。因此,我们需要一种方法,能够为字母表中的每个符号分配成为序列中下一个符号的概率。相比之下,输入原则上可以是一次性编码或分布式编码。由于输入维数很高,一键编码将使 RNN 在大字母表上具有更高的容量。(这可能是好的或坏的,取决于问题和训练集。)分布式编码在某种程度上增加了学习问题的难度。另一方面,人们可以利用来自预训练模型的嵌入来解决某些问题(例如对英语的语言建模),这可以产生改进的泛化。**学习**我们在这篇文章中涉及这个主题,因为它将为我们提供一种训练编码器-解码器模型的编码器组件的方法:作为源语言的语言模型。RNN 的状态变化和状态到输出的行为都可以从一组(*输入序列*、*输出序列*)对的训练集中自动学习。我们将在这里描述高层次的学习。如需更深入的了解,请参见[1]。那些已经知道如何在 RNN 学习的人可以跳过这一步。考虑当 RNN 在时间 *t* 接收输入向量 **x** ( *t* )时会发生什么。首先,从该输入和 **h** ( *t* -1)导出新的状态向量 **h** ( *t* )。接下来,从 **h** ( *t* )导出 RNN 的输出向量 **yhat** ( *t* )。现在到了学习的部分。我们首先将输出向量与目标向量进行比较,即 **yhat** ( *t* )到 **y** ( *t* )。任何差异都会推动学习。首先,我们尝试修改状态到输出函数的可学习参数,以尝试使输出向量更接近目标向量。接下来,我们尝试修改状态转移函数的参数,以尝试将状态向量移动到更接近一个状态,从该状态产生的输出向量更接近目标向量。通常,对状态输出函数的参数和状态转移函数的参数的修改是独立完成的。就好像我们保持其中一个不变,而改变另一个,以使输出更接近目标。事实上,还有更多。它与嵌入在状态转移函数中的递归有关。在这一点上,引入一些正式的概念和符号将有助于阐述。这将有助于我们涵盖“更多”的部分,也使前一段的讨论更加具体。设**h**(t)=**f**(**h**(*t*-1), **x** ( *t* ))。这里的 **f** 是输入当前状态向量 **h** ( *t* -1)和下一个输入向量 **x** ( *t* )产生下一个状态向量 **h** ( *t* )的状态转移函数。设**y**(*t*)=**g**(**h**(*t*))。这里 **g** 是将当前状态向量 **h** ( *t* )映射到输出向量 **y** ( *t* )的输出函数。为了有个感觉,考虑在时间 T 产生目标向量 **y** (T),作为输入向量序列 **x** (1)、 **x** (2)、…、 **x** (T)的函数。任何一个输入向量 **x** ( *t* ),1 < = *t* < = T,都可能影响 **y** (T)。为了能够了解这种影响,首先,让我们及时展开 RNN 中的计算。从接收 **x** (1)一直到产生输出向量 **yhat** ( *t* )。我们得到了

h(1) = f(h(0), x(1))
h(2) = f(h(1), x(2))

h(T) = f(h(T-1),x(T))
yhat(T) = g(h(T))


好的,那么让我们开始讨论从零开始的学习。首先,我们尝试修改 **g** 以便将 **yhat** (T)移近 **y** (T)。注意改变 **g** 可以改变 **yhat** (T)。接下来,我们尝试修改 **f** ,使**yhat**(T)=**g**(**f**(**h**(T-1), **x** (T))向 **y** (T)靠拢。注意,当考虑改变 **f** 时,我们保持 **g** 、 **h** (T-1)和 **x** (T)不变。接下来,我们尝试修改 **f** ,使**yhat**(T)=**g**(**f**(***f***(**h**(T-2),x(T-1)), **x** (T))向 **y** (T)靠拢。我们用斜体显示了对 **f** 的调用,我们试图修改其行为。诸如此类,将学习延伸到更早的时间…例子:这将更具体地说明学习。(尽管这是一个玩具,基本上没什么用。)假设我们希望 y(T)等于 x(1) + x(2) + … + x(T)。也就是说,我们只是试图让 RNN 学会如何维持一个运行总和。输入和输出都是一维的。假设我们的 RNN 结构如下:

f(h,x) = ah + bx
g(h) = ch


*f* 是具有可学习参数 *a* 和 *b* 的状态转移函数。 *g* 是带有可学习参数 *c* 的输出函数。比方说对于*a*=*b*=*c*= 2 我们给出输入序列( *x* 1=1, *x* 2=2, *x* 3=3)想学习 *y* 3=1+2+3=6。假设我们开始处理输入,0 = 0。

h1 = 2h0 + 2x1 = 20 + 21 = 2
h2 = 2h1 + 2x2 = 22 + 22 = 8
h3 = 2h2 + 2x3 = 28 + 23 = 22
yhat3 = 2*22 = 44


*y* 3 是 6,远小于 *yhat* 3。我们当然可以将 *c* 减少到 *yhat* 等于 6。具体来说,我们可以将 *c* 设置为 3/22。一般来说,这不是一件好事。我们正在对一个单一的可学习参数进行彻底的改变,使它产生我们想要的结果。这种剧烈的变化倾向于追逐最新的例子,可能会抹去以前的学习。更好的做法是循序渐进。到底有多缓慢?这很难量化。尽管如此,引入一个参数来控制学习速度还是有意义的。其实这个参数就是所谓的*学习率*。好了,回到完成我们的例子。我们只是学得慢一点。我们先把 *c* 降低一点,比如说 1.5。这给了我们重新想象的 *yhat* 3 是 33。我们向 y 轴 3 靠近。接下来我们来看看 *h* 3,等于 22。如果我们能够降低 *h* 3,我们将从当前值 44 降低 *yhat* 3。(之所以是 44,是因为我们在计算 *h* 3 对 *yhat* 3 的影响时,还在使用旧值 *c* 。)那么我们如何降低 *h* 3 呢?通过减少 *a* 或减少 *b* 。我们会双管齐下——将它们分别从*降低到* 1.5。好吧,我们现在有什么。

h3 = 1.58 + 1.53 = 16.5
yhat3 = 2*16.5 = 33


很好,这也让 *yhat* 3 更靠近 *y* 3。同样,我们可以更进一步回到过去。尝试降低 *h* 2。在这一点上,中间计算变得更加复杂,对我们的符号进行一些改进将会有所帮助。让我们将 h 的计算表示为

h(i) = a(i)h(i-1) + b(i*)x(i*)


这将让我们跟踪当我们改变 *a* ( *i* )和 *b* ( *i* )时 *h* ( *i* )发生了什么。在一次学习迭代结束时,我们必须以某种方式确保所有的 *a* ( *i* )和所有的 *b* ( *i* )都具有相同的最终值。稍后会详细介绍。由于其当前值 *h* 2 = 8, *yhat* 3 为 44。假设我们将 *a* 2 和 *b* 2 也降低到各 1.5。我们得到了

h2 = 1.52+1.52=6
h3 = 2h2 + 2x3 = 26 + 23 = 18
yhat3 = 2*18 = 36


*yhat* 3 再次靠近 *y* 3。等等,我们继续回到过去…好了,现在我们有了新的值*new*(*I*)和 *bnew* (i)用于各种 *i* 的。但实际上,我们只有两个参数 *a* 和 *b* 。所以我们必须以某种方式有意义地将所有的*新* ( *i* )聚合成一个单一的 *a* ,并将所有的*b 新* ( *i* )聚合成一个单一的 *b* 。一个自然的选择是将 *a* 和 *b* 分别设置为所有*new*(*I*)和所有 *bnew* ( *i* )的平均值。**推论**训练有素的 RNN 能做出什么样的推论?涉及到什么?我们来说明一下语言建模设置中的思路。我们在这里讨论这个主题,因为它将在编码器-解码器模型的解码阶段发挥作用。我们可以通过训练有素的 RNN 运行一系列单词 *w* 1、 *w2* 、…、 *wn* -1,并生成下一个可能单词的概率分布。有时候我们想要的不止这些。我们想要下一个 *k* 最有可能的单词。也就是说,我们想要

wn, wn+1, …, wn+k-1 = argmax *wn*, *wn*+1, …, *wn*+*k*-1 P(wn, wn+1, …, wn+k-1 | w1, w2, …, wn-1)


这个推理问题叫做解码问题。一般来说比较棘手,所以我们寻求启发式的解决方案。**贪婪解码**:寻找下一个单词的最佳候选,将该单词追加到序列中,并继续下去,直到追加了 *k* 个单词。不能保证找到长度 *k* 的最佳扩展,因为它会在整个过程中做出局部最优决策。随后的决定取决于这些。**波束搜索**:在贪婪解码中,任何时候,只有一个序列被扩展。波束搜索通过跟踪到目前为止的最佳扩展对其进行了改进,其中 m 是一个自由参数。(在贪婪解码中 *m* 为 1。)更准确地说,波束搜索保留了 *m* 个最佳前缀序列。在每一次迭代中,它考虑这些 *m* 前缀的所有可能的 1 步扩展,计算它们的概率,并形成迄今为止的 *m* 最佳前缀序列的新列表。它一直持续下去,直到 *m* 延长部分达到所需长度。然后,它取出这些 *m* 扩展中得分最高的作为最终答案。这里有一个例子来说明进行波束搜索的价值。考虑一个重复的组织名称列表。我们试图找到最受欢迎的组织名称。假设由于某种原因,我们不能(或选择不)保持每个组织名称在列表中出现的次数。(可能是截然不同的名字太多了。)贪婪的方法可以从列表中最常出现的单词开始,并尝试扩展它。它选择*银行*作为起点似乎是合理的,因为有很多银行。现在想象一下,某个非银行组织比任何特定的银行都受欢迎。想到了谷歌。贪婪的方法永远找不到它,因为它被委托给银行。Beam search 有机会找到 Google,因为它为可能的扩展保留了多个候选项。**编解码器型号**考虑机器翻译。我们希望将特定源语言(比如英语)中的一系列单词翻译成特定目标语言(比如法语)。输入和输出序列可以具有不同的长度。如前所述,在这种情况下,不清楚如何使用 RNNs。考虑一个两阶段的过程。在第一阶段,称为*编码器*,输入序列(英语句子)被转换成固定长度的数字向量。在第二阶段,称为*解码器*,向量被转换成合适的输出序列(法语翻译)。我们可以把这个两阶段过程看作首先从英语句子中提取合适的特征,然后把得到的特征向量转换成输出序列。这不仅规避了对准约束;直觉上,它似乎更丰富。原则上,研究一个包含丰富特征的空间比研究一个单词序列要好。解码(即翻译)过程直到源序列被完全编码后才开始。在有限范围的机器翻译实例中,我们可能会从使用人工设计的功能中获益。这当然不是一个通用的解决方案,而且可能无法(在翻译质量上)有效地翻译源语言中任何看似合理的句子。所以我们想从训练数据中学习编码器。一旦我们描述了端到端系统,即解码器,我们将深入学习。**解码器**这是另一个 RNN,就像编码器一样。那么翻译序列是如何从源序列的编码中生成的呢?我们将解码器的初始状态设置为编码器的最终状态,即源序列的编码状态。然后,我们使用这个 RNN 作为生成器,迭代地生成翻译序列中的下一个标记。下一个令牌可能是使用贪婪解码或波束解码生成的,这是我们之前讨论过的主题。可以把解码器 RNN 看作是以源序列编码为条件的目标语言的语言模型。源序列的良好翻译具有高概率;可怜的低。注意,“好的翻译”的一个方面是合理地安排翻译的顺序。作为一个想象的例子,假设我们想把*猫很小*翻译成法语。我们可以想象编码以某种方式捕获了关键方面:对象= *猫*,属性= *小*。翻译可能会被解释为用法语表达成一个结构良好的句子。le chat est petit 是个不错的翻译。(有趣的是,作为旁注,当我们在*中输入 *est petit le chat* 时,谷歌将*翻译成法语并要求其翻译成英语,我们得到的是*猫很小*。)也就是 Google translate 对翻译后的序列进行了合理的排序。更多这种类型的例子见[2]。**培训概述**在本节中,我们将对培训进行概述。我们首先讨论一种将训练分解成两部分的方法:训练编码器和训练解码器。这种方法有其局限性。然而,它也有一些潜在的好处。这也有助于教学目的。这有助于我们轻松进入联合培训讨论。**训练编码器**这里我们考虑将编码器作为源语言上的语言模型来训练。潜在的缺点是,整个系统没有进行端到端的训练,因此不会捕获源序列和其目标序列之间的相互作用。潜在的好处是简化了编码器培训。很容易得到一组丰富的源序列。第二个潜在的好处是,原则上可以使用同一个经过训练的编码器将源语言翻译成任何目标语言。那么我们如何在源语言的一组序列上训练编码器呢?由于编码器是一个 RNN,训练它的自然方法是教它预测序列中的每个符号,来自它前面的序列前缀的符号。我们在**学习**一节中详细讨论了这一点。**训练解码器**解码器可以被类似地训练,因为它也实现语言模型。也就是说,语言模型取决于源编码。让我们来描述一下这是如何发挥作用的。考虑一对( *S* , *T* )。 *S* 是源序列。 *T* 是与之配对的目标序列。我们假设编码器已经被训练过。我们通过编码器运行 *S* 得到它的编码 *E* ( *S* )。我们将解码器 RNN 的状态初始化为这种编码。然后,我们教解码器为 *S* 中的每个符号预测其左侧子序列中的符号。**联合训练**这从一组( *S* , *T* )对中联合训练编码器和解码器。从直观的角度来看,工作原理如下。我们将 *S* 呈现给编码器。编码器输出 *E* ( *S* )。这可能是 *S* 的一个好的编码,也可能不是,这取决于编码器训练的当前状态。使用该编码作为输入,解码器产生预测的目标序列*。**即*的质量取决于解码器在其当前状态下的有效性,以及编码器(在其当前状态下)产生的编码 *E* ( *S* )的质量。因此,和 *T* 之间的差异驱动解码器和编码器的学习。可以把这想象成误差从解码器的输出一直反向传播到编码器的输入,就像一个长长的多层网络。因此,解码器和编码器的参数都可能改变。为了简单起见。我们已经把在一个实例上的学习( *S* , *T* )描述为一次性过程。也就是说,首先 *S* 被完全输入,其编码被产生,该编码被完全解码,然后预测的目标序列和解码之间的差异驱动学习。原则上,我们也可以考虑这种一次性过程的顺序变体。这可以看作是对( *Si* , *Ti* )的训练,其中 *Si* 和 *Ti* 分别表示 *S* 和 *T* 的 *i* th 前缀。这可能有助于将编码器-解码器组合视为单个 RNN,它在 *S* + *T* 上学习语言模型,其中+表示连接。在这个 RNN 中,使用编码器的参数预测 *S* 中的下一个符号,使用解码器的参数预测 *T* 中的下一个符号。以这种方式思考,我们还可以看到,当下一个符号仍在 *S* 内时,训练仅在编码器的参数上发生,其行为就好像编码器正在学习源语言的语言模型。也就是说,这种训练与调整编码器和解码器的参数的训练混合在一起。当要预测的下一个符号在 *T* 中时会发生这种情况。**反向编码器**在[4]中,发现颠倒源序列(但不是目标序列)产生更准确的翻译。这背后的直觉适用于翻译问题,其中源语言和目标语言中的句子具有相同的词序,例如从左到右。反转过程使源序列中的开始符号更接近它们在目标序列中的翻译。这似乎简化了这些部分的学习,这可能也有雪球效应。**参考文献**1.  [神经语言模型|作者 Arun Jagota](/neural-language-models-32bec14d01dc)
2.  [CS 224d:NLP 的深度学习](https://cs224d.stanford.edu/lecture_notes/LectureNotes4.pdf)
3.  [https://arxiv.org/pdf/1406.1078.pdf](https://arxiv.org/pdf/1406.1078.pdf)
4.  [https://arxiv.org/abs/1409.3215](https://arxiv.org/abs/1409.3215)*用神经网络进行序列对序列学习* — Stutskever 等。# Python 和 R 语言的数据科学和数据分析系列项目> 原文:<https://towardsdatascience.com/series-of-projects-on-data-science-and-data-analytics-in-both-python-and-r-76db3fea9b7e?source=collection_archive---------13----------------------->![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/b0e8460bbe6893bdd199c8cb599b5e47.png)由 [Fab Lentz](https://unsplash.com/@fossy?utm_source=medium&utm_medium=referral) 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片## 探索性数据分析、机器学习、自然语言处理和计算机视觉项目最好的学习方法就是做项目。特别是对于自学者来说,他们学习了一些编程和数据科学工具,并想知道接下来会发生什么。我建议,开始做一些自己的项目。那是最好的学习方法。此外,对于一个没有行业经验的初学者来说,一些实践项目可以制作一个很好的投资组合。但是对于一个初学者来说,可能很难想到一个好的项目想法。在那种情况下,这篇文章可能会有帮助。在这里,我正在编译一些不同类型的项目,从探索性数据分析、机器学习和深度学习开始。你会发现一些使用 scikit-learn、stats model 或 Tensorflow 库的机器学习项目,以及一些从头开发的机器学习项目。> 所有这些文章都是很好的学习资源!大部分都是对某个话题非常详细的讨论。您会发现 Python 和 r 中的项目。随着时间的推移,我不得不为我的课程、我自己的实践和博客做相当多的项目。我把其中一些放在这里。我唯一的建议是,请带着数据集或想法,想出自己的项目。因为那会帮助你成长。我将从简单的探索性数据分析项目开始,然后转向一些更深入的领域。让我们开始:该项目利用 python 的 Pandas、Matplotlib 和 Seaborn 库进行数据分析和可视化,并利用 scikit-learn 库中的模型进行预测。</exploratory-data-analysis-visualization-and-prediction-model-in-python-241b954e1731>  这是一个使用 scikit-learn 库的简单 KNN 分类器。因为它是一个教程,所以可能有点过于描述和冗长。但它使用了 Kaggle 的大型数据集,并开发了分类器。</clear-understanding-of-a-knn-classifier-with-a-project-for-the-beginners-865f56aaf58f>  一个简单的探索性数据分析,适合初学者:</an-exploratory-data-analysis-project-in-r-a51918ebf34d>  这是 r 中的一个探索性数据分析项目。</exploratory-data-analysis-in-r-data-visualization-summarising-and-machine-learning-model-da79439b3952>  特征选择是机器学习最重要的部分之一。本文用 python 实现了 4 种不同的特征选择方法。</four-popular-feature-selection-methods-for-efficient-machine-learning-in-python-fdd34762efdb>  R 用户的另一个项目。这一篇着重于线性回归及其在 r 中的详细分析。</detail-explanation-of-simple-linear-regression-assessment-and-inference-with-anova-bb12ff4604d3>  本文详细讨论了 logistic 回归,并用 r。<https://medium.com/codex/complete-details-of-simple-logistic-regression-model-and-inference-in-r-eedb1c84b65f>  本文使用了一个真实的数据集,并对其进行了方差分析和协方差分析,同时给出了详细的解释。所以,如果这个话题对你来说是新的,这可能是一个很好的学习资源。<https://pub.towardsai.net/dissecting-1-way-anova-and-ancova-with-examples-in-r-a3a7da83d742>  这个项目又是关于 python 中的逻辑回归。但是它使用 statsmodel 库。您可以使用简单的代码获得变量之间的相关性、p 值、z 值、置信区间的详细信息。</logistic-regression-model-fitting-and-finding-the-correlation-p-value-z-score-confidence-8330fb86db19>  这也是一个逻辑回归项目。但它是在 R 编程语言中。这个项目非常详细,在一篇文章中涵盖了很多主题,你可以从下面的标题和副标题中看到。</detailed-guide-to-multiple-linear-regression-model-assessment-and-inference-in-r-146845067aa3>  上面关于逻辑回归的项目使用了一个库来完成这个项目。但是在这个项目中,逻辑回归是从零开始发展的。</a-complete-logistic-regression-algorithm-from-scratch-in-python-step-by-step-ce33eae7d703>  这是另一个从零开始开发的逻辑回归教程和项目。因为逻辑回归是最流行和最简单的分类器之一,所以学好它并不是一个坏主意。这是一个多类分类器,与从零开始开发的简单逻辑回归略有不同。如果你是初学者,这看起来可能有点复杂。</multiclass-classification-algorithm-from-scratch-with-a-project-in-python-step-by-step-guide-485a83c79992>  ## NLP 项目这个项目使用非常流行和易于使用的库,如 sklearn,来进行情感分析。</a-complete-sentiment-analysis-algorithm-in-python-with-amazon-product-review-data-step-by-step-2680d2e2c23b>  另一个简单的 NLP 项目,查找文本之间的相似性并返回相似的文本。<https://pub.towardsai.net/similar-texts-search-in-python-with-a-few-lines-of-code-an-nlp-project-9ace2861d261>  这个项目侧重于文本数据的分析和可视化,以及使用文本数据的情感分析。</exploratory-data-analysis-of-text-data-including-visualization-and-sentiment-analysis-e46dda3dd260> [## 文本数据的探索性数据分析,包括可视化和情感分析towardsdatascience.com](/exploratory-data-analysis-of-text-data-including-visualization-and-sentiment-analysis-e46dda3dd260) 这个项目是关于一个分类文本文件的分类器。</text-files-processing-cleaning-and-classification-of-documents-in-r-394c6b4f1504>  这是另一个使用 Keras 和 Tensorflow 的情感分析项目。</a-complete-step-by-step-tutorial-on-sentiment-analysis-in-keras-and-tensorflow-ea420cc8913f>  ## 计算机视觉几个简单的图像分类问题?这不是一个直接的计算机视觉问题。但是这个 K 表示从零开始开发的聚类算法,并显示了在图像修改中使用的示例。</a-complete-k-mean-clustering-algorithm-from-scratch-in-python-step-by-step-guide-1eb05cdcd461>  又是逻辑回归。但这一次,逻辑回归将表现得像一种深度学习算法。它将在逻辑回归中从头开始执行图像分类。</a-complete-logistic-regression-algorithm-for-image-classification-in-python-from-scratch-c1d0266409b8>  另一个图像分类问题使用从零开始开发的神经网络。</a-complete-neural-network-algorithm-from-scratch-in-python-fdf985f258dd>  ## 结论外面有很多资源。但我试图编译所有这些项目,不仅包括代码,还提供详细的解释。我认为,对于那些试图同时学习或建立简历的人来说,这是一个很好的资源。我可能会在未来添加更多的项目到这个页面。希望这有所帮助!欢迎在推特上关注我,喜欢我的 T2 脸书页面。# 如何用例子处理大数据:MapReduce> 原文:<https://towardsdatascience.com/series-on-distributed-computing-1-mapreduce-fcc3cc2dfb5?source=collection_archive---------34----------------------->## [理解大数据](https://towardsdatascience.com/tagged/making-sense-of-big-data)## 简单解释如何运行并行工作负载来处理大数据![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/4d6bcf32056f0571651da32c08e9efba.png)我决定写一个关于分布式计算的系列,一个关于我们在哪里成功地正确处理大数据的历史回顾,以及它是如何演变的。处理大文件的挑战是,我们在分布式机器上运行的计算必须在合理的时间内完成,这带来了一些问题:我们如何进行令人尴尬的并行*计算,如何 ***分发数据*** ,以及我们如何处理故障。2004 年,谷歌共享了 MapReduce 框架,在平均运行 10 万个 MapReduce 任务 4 年后,每天处理大约 2004 的数据。**他们实现的是将流程与 Map 任务解耦,在 Map 任务中他们可以并行处理输入,并减少任务,在 master 中他们可以对分类和分组的数据进行聚合,master 跟踪工人的健康状况,任务的完成和在故障机器上重新执行任务允许 ***容错*** 。**通过一个简单的伪代码,该映射获取一个键/值输入对,并独立于原始输入计算另一个键/值对。reduce 使用键,将所有值从 map 函数中分离出来,进行排序/分组,以便对其运行聚合功能。输出通常是一个输出值。**地图(k1,v1) →列表(k2,v2)**减少(k2,列表(v2)) →列表(v2)**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/4e902bfa94f90e7ae5755f8cbe3dd804.png)**初始执行**让我们考虑一个简单的用例“单词计数”,我们试图找出每个单词出现的次数。在这个场景中,用户程序将输入文件分成 M 对。如果输入是一个语料库,则每个拆分将是一个文档,如果输入是一个文档,则每个拆分将是一行,依此类推……此外,它还会旋转多个 workers,其中一个是主节点。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/90f2019a373539c073076719e621bba1.png)**主选择地图/减少任务**主节点负责分配工作人员来映射和减少任务,并检查健康检查(通过 ping)。如果任何工人在该时间范围内没有响应,master 会将他们标记为失败,并重新安排这些工人正在进行的任务,并且所有 map 任务作为机器上的输出数据都会丢失。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/c91d773433375cb588f6fc418fa2f315.png)**地图工作者处理的分割输入文件**分配给 ***map*** 函数的工人读取拆分对,并将中间的键/值对保存到其磁盘(首先向缓冲区和分区函数写入磁盘,但是为了不被分散注意力,我们跳过)。这在当时是开创性的,因为地图工作者在 ***中令人尴尬的做了这些*** ***并行*** 导致了大型商用电脑集群的高性能。**MapReduce 提供 ***局部优化,*** 因为它使用 GFS,master 向 GFS 请求输入文件块的位置,并在同一台机器或同一机架上调度映射任务,这允许数千台机器以本地磁盘速度读取输入,并且没有交换机机架,受读取速率的限制**问题是我们负责编写 map 和 reduce 函数,所以让我们看看如果我们用 Python 编写的话会做些什么。最初的文章/实现是在 C++上,我将使用 Python 来保持简单。如果您想了解更多信息,我在参考资料部分分享了许多实现方法:**现在,所有的中间键/值对都被保存了,分区函数将它们放到磁盘上,reduce 任务的一部分是对单词进行排序和分组。 ***Reduce*** 函数将采用唯一键和分组值列表,通过为全局输出追加每个输出来计算计数的总和。如果要求我们找出给定城市列表的最高温度,我们将对 Reduce 函数进行最大聚合。**如果 reduce 工作线程失败,主线程不会请求重新执行,因为输出是全局共享的。**我们完整的功能(在 [mrjob](https://mrjob.readthedocs.io/en/latest/) 模块的帮助下)如下所示。Mrjob 模块几乎可以运行任何本地和云 Hadoop 集群。(PS:如果你更喜欢用 go, [gossamr](http://github.com/vistarmedia/gossamr) repo 可能有用)**如果我们检查我们的完整示例,我们将获得每个单词的出现次数,并将其保存在一个全局可访问的位置。谷歌的论文基于 2000 台工作机、5000 个分区和 200,000 个分割,其中他们选择每个分割大约 16MB 到 64MB。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/078621a9ab9deb2dfe887ac8c2f7f7ac.png)**聚合的 reduce worker 导致唯一关键字的值**输入文件或键/值对不同于中介以及输出键/值对,我们无法预测何时处理数 Pb 的数据。*## *后续步骤**它是第一个能够利用本地文件系统执行并行任务的抽象。接下来,[受 MapReduce](https://books.google.co.uk/books?id=axruBQAAQBAJ&pg=PA300&redir_esc=y#v=onepage&q&f=false) 编程模型的启发,2006 年的 Hadoop 由雅虎推出,云提供商也不吝推广 Hadoop-on-cloud 工具,即 Google 实现为 [Dataproc](https://cloud.google.com/dataproc) ,AWS 实现为 [EMR](https://aws.amazon.com/emr/features/hadoop/) (弹性 MapReduce),Azure 实现为 [HDInsights](https://docs.microsoft.com/en-us/azure/hdinsight/) 。*## ***号令:****所以,我们来反思一下 bak。问题是,对于这个简单的问题,你会如何改进谷歌找到的解决方案?如果您想处理重复执行,您将如何处理在 map 阶段结束时拆分的文件?你如何让软件开发人员更容易以更抽象的方式编写 map/reduce 函数?**直到下一个,保重!*****参考文献:*****<https://research.google/pubs/pub62/>  <https://learning.oreilly.com/videos/taming-big-data/9781787125568/9781787125568-video1_2>  迈克尔·诺尔的博客文章*# 使用 Jupyter 笔记本进行严肃的软件开发> 原文:<https://towardsdatascience.com/serious-software-development-using-jupyter-notebooks-95f84f4d8ef8?source=collection_archive---------24----------------------->## 使用 nbdev 通过 Jupyter 笔记本构建 Python 包![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/d22060d7cf6f2028c68c4ca088557b05.png)肖恩·林在 [Unsplash](https://unsplash.com/s/photos/software-development?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上的照片## 介绍下图来自一个名为 [fastcore](https://fastcore.fast.ai) 的库的文档页面。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/882c3cfaa2bf18e412da9706f68a4056.png)它有源代码超链接,所以你可以很容易地检查实现,它有一个小的 pargraph 解释什么功能,它有测试,所以你可以验证函数的行为。与常规的 Github 存储库相比,它只有部分文档或者没有文档,没有可供跳转的超链接,测试存在于单独的文件夹中,您已经可以看出哪种方法更好。是的,你可以有变通办法,但没有什么比得上我将要向你展示的。## 文化编程早在 1983 年,Donald Knuth 就设想了一种叫做文化编程的编程风格。在这种编程风格中,你不再按照计算机强加的方式和顺序编写计算机程序,而是使程序员能够按照他们思维的逻辑和流程所要求的顺序开发程序。> 来源:[https://en.wikipedia.org/wiki/Literate_programming](https://en.wikipedia.org/wiki/Literate_programming)捕捉这种编程风格的最佳环境是 Jupyter 笔记本。你可以处理一些代码,拼出你的想法,当你满意时,把它们放在一起。它们非常适合迭代的、实验性的编程风格。然而,直到最近,Jupyter 笔记本还缺乏许多构建软件的附加功能。它缺乏相关的组件,当这些组件放在一起时,无法使 Jupyter 成为一个真正有文化的编程环境。介绍, **nbdev** 。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/c5113b3eb5e36d3ee4cf24ab9659276d.png)## nbdevnbdev 是一个由 fastai 开发的库,只使用笔记本就可以开发 Python 包。我不打算给你一个特性的概要,因为我想让你仔细阅读并找出所有细节。[我也没有做代码遍历,因为 fastai 的网站上已经有了一个非常好的教程,所以重复相同的步骤没有意义。](https://nbdev.fast.ai/tutorial.html)这篇文章的目标是向你介绍这个世界,一个你不知道存在的世界,让你爱上它,让你在下一个项目中使用它。## 它是如何工作的?让我们先来看看 nbdev 是如何工作的。因为我不做代码 walthru,我们将浏览已经使用 nbdev 构建的库的源代码,这个库被称为 [fastcore](https://fastcore.fast.ai) 。如果你去 fastcore 的 [Github 页面,你会看到它有一个名为](https://github.com/fastai/fastcore) [**nbs**](https://github.com/fastai/fastcore/tree/master/nbs) 的文件夹,顾名思义,里面包含了一堆笔记本。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/ab7d5f038d9597499d0dafe93c3129e6.png)每个笔记本代表我们正在构建的包的一个[模块。例如](https://docs.python.org/3/tutorial/modules.html) [00_test.ipynb](https://github.com/fastai/fastcore/blob/master/nbs/00_test.ipynb) 将被转换成一个名为 [test.py](https://github.com/fastai/fastcore/blob/master/fastcore/test.py) 的模块。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/64045803506edea58207874e5fba8b25.png)我们软件包的用户可以如下使用它:

from fastcore.test import *


从笔记本到`.py`文件的转换是在一个名为`notebook2script()`的函数的帮助下完成的,这个函数在每个笔记本的末尾执行。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/9edb5d5e1c76c49a81a93a9b221125dc.png)如果你打开其中一个笔记本,你会发现它看起来就像普通的 Jupyter 笔记本,除了一些东西。您首先会注意到在某些单元格的开头有一串`#export`。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/4a32a92b761ce72c4a6addb4e4d2c54a.png)【nbdev 如何确定哪些细胞将成为模块的一部分。根据上面的代码,[单元格[1]](https://github.com/fastai/fastcore/blob/master/fastcore/test.py#L33) 将进入 [test.py](https://github.com/fastai/fastcore/blob/master/fastcore/test.py) ,但单元格[2]不会。然而,单元格[2]将成为本模块文档的一部分。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/0cd23e97af8fe00b60d017f32c3ccc50.png)这是我最喜欢的部分。你在笔记本上写的任何代码和解释都会自动转换成文档。你不需要在它上面花费任何额外的时间。此外,我们能够在源代码下面编写测试,并且它们也成为文档的一部分,这是一个巨大的好处。传统 Python 测试模块的情况是,测试存在于不同的文件夹中。这使得识别哪个测试与哪个功能相关变得困难。当你修改源代码时,很难修改相关的测试,因为它们不在你的眼前。Nbdev 为你解决了这两个问题。另外,任何阅读你的代码的人也可以看到测试是如何设计的,并获得更丰富的学习经验。最后,请注意,在我们的 markdown 中的`backticks`中的关键字是在代码库中搜索的,如果找到,也是超链接的(参见上图中的 test_eq_type)。这真是不可思议。当开发软件时,自动化平凡的任务只是将更多的创造力放在你的手中。## nbdev 的特点现在让我们看看 nbdev 的特性列表,我可以给你一个每个特性的简单解释![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/4378298d9256fa0c585abbafb3bb4ad6.png)*   **自动生成单据**这个我已经解释过了。*   **自动发布到 PyPI 和 conda 包的实用程序**无论何时做`pip install bla`,bla 必须存在于 Python 包索引(PyPI)上。康达也是如此。发布到 PyPI [需要你以某种方式打包你的项目并创建一堆文件](https://packaging.python.org/tutorials/packaging-projects/)。Nbdev 通过自动化任务和提供实用程序来帮助您轻松发布,使这一过程变得非常简单。*   **笔记本和源代码之间的双向同步**ide 对于很多任务都很有用,例如,它们提供了更好的调试工具。使用 nbdev,您可以编辑源代码(。py 文件),这些更改将反映在您的笔记本中。这允许你使用两种媒体的最佳功能。*   **对隐藏/显示单元格的细粒度控制**完整的 Jupyter 笔记本会转换为文档,但如果您愿意,可以隐藏某些单元格。就像`#export`一样,nbdev 包含一个`#hide`关键字。将它放在任何单元格的顶部,它就不会出现在生成的文档中。*   **直接在笔记本上写测试**我提到过您可以这样做,但是我提到过您可以像使用 pytest 一样通过 CLI 运行这些测试吗?如果你不想每次都运行长时间的,你也可以将它们分组。*   **合并/冲突解决**Jupyter 笔记本的一个问题是版本控制不太好用。Git 经常给出合并冲突,Github 的 visual diff 显示 Jupyter 笔记本底层 json 的变化,而不是实际的单元格变化。这很烦人。好吧,让 nbdev 帮你处理。它清理笔记本,以避免这些冲突,如果它们确实发生了,给你一个人可读的格式的错误。*   **持续集成(CI)为您设置了** [**GitHub 动作**](https://github.com/features/actions)Github Actions 允许您自动执行工作流程,比如当您推动新的变更、创建新的问题或提交 PR 时会发生什么。nbdev 为您设置此功能,无需任何手动干预。*   **降价的所有功能**现在我们使用 Markdown 进行文档记录,我们可以很容易地包含图像、格式化文本甚至数学方程。*   **…还有更多**在这一点上,我建议你浏览一下教程,了解更多让你兴奋的部分。## 为什么是这个标题?Joel Grus 有一个非常著名的演讲叫做“我不喜欢笔记本”。Joel 是一个优秀的演示者,他的演讲非常有趣,但它不鼓励人们使用笔记本进行严肃的开发。现在,这个话题真的很老了,一些现在存在的东西,在当时是不存在的。但是,在专业人士中仍然有一个普遍的共识,Jupyter 笔记本只是用于实验,如果你想编写将被部署的严肃软件,你必须使用 ide。现在不再是这样了。通过这篇文章,我希望能改变这种情况。希望你给这个彼岸一个尝试,希望更多的人用 Jupyter 笔记本做正经的软件开发。~快乐学习。# 无服务器的采用:成本仍然是主要因素吗?> 原文:<https://towardsdatascience.com/serverless-adoption-is-cost-still-the-main-factor-c5e910cf8b12?source=collection_archive---------41----------------------->## 对无服务器的低成本认知正在改变吗?如果有,原因是什么?![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/876f9ac98adebafbed5a5e7acdce71c2.png)基础图像来源: [pixabay](https://pixabay.com/illustrations/awning-store-shop-retail-business-2683099/)*和 ***按使用付费*** 是许多组织采用无服务器的主要驱动因素。在给定的前提下,每个人使用无服务器的动机都是为了降低运营成本。几乎每个无服务器成功案例都与成本因素有关。*# *错误的成本比较**我曾经比较过 EC2 实例和 Lambda 函数的成本。刚开始还挺有意思的。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/0bf23e7d05e31ca8ae7ffe4971d24264.png)**来源:[https://www.prerender.cloud/](https://www.prerender.cloud/blog/2017/06/19/lambda-is-cheaper-than-ec2)**很快我意识到这是误导。我就是觉得不对劲。我认为我对服务器和无服务器都不公平。**   *我在比较裸机吗?*
*   *我考虑过那些在 EC2 上运行的应用程序的许可费吗?*
*   *第二个可用性区域中 EC2 实例的成本如何?*
*   *无服务器不仅仅是 Lambda 函数。我增加了其他服务的成本吗?*
*   *我如何解释混合情况下的成本?*> *诸如此类的问题迫使我永远停止玩数字游戏。*# *每个功能的成本估计**现在,我已经形成了*无服务器优先*的思维模式。我没有浪费时间比较苹果和橘子,而是将注意力转向根据应用程序的高级架构来估计它们的成本。虽然不准确,但这让我对我们正在构建的那些功能的成本有了深入的了解。**作为一名工程师,这无疑帮助我了解了无服务器成本的影响因素。重要的是,它允许我与业务利益相关者分享这些数字。在我们推进无服务器的过程中,拥有成本可见性被证明是很有价值的。**这种方法也有它的挑战。**   *对于复杂的架构,评估变得很困难*
*   *适应不可预测的交通高峰的影响被证明是困难的*
*   *将间接成本(如 CloudWatch 指标)相加是很棘手的**然而,它提高了团队中无服务器成本的意识,这对于开发无服务器解决方案的每个人来说都是至关重要的。即使有一些误差,这些数字也符合每个人对无服务器的低成本预期。*> *那么*低成本*到底有多低将成为无服务器的谜团之一!*# *平衡成本和性能**降低成本是一项重要的工作。通常,它是自上而下的。当它进入无服务器生态系统时,它会遇到一个被称为*优化的模糊活动。*它以两种不同的形式存在。**   *成本优化,以及*
*   *性能优化**在无服务器中,成本和性能交织在一起。问题是,当我们考虑降低无服务器成本的方法时,这两种方法会将我们引向相反的方向。*## *成本和性能之间的冲突**众所周知,每个应用都有很多部分——微服务、API、存储、特性等。这取决于我们如何识别和分组它们。**无服务器应用程序的好处之一是,我们可以在粒度级别上可视化和优化每个部分。它允许我们在细粒度级别上执行成本和性能优化。然而,复杂性在于成本和性能并不总是相辅相成的。它们往往会造成成本失衡!**   *成本优化不能保证性能,并且*
*   *性能优化并不总是经济高效的*# *性价比高**当我们针对性能进行优化时,无服务器的低成本观念会发生巨大变化。这里有几个案例可以证明这一点。**   *为 Lambda 函数设置**提供的并发**可能会很昂贵。但是,为了获得更好的性能和最终用户体验,在需要的情况下,我们同意支付额外费用**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/3a5e3ab00babf61a0e89691cee5223c0.png)**与 Lambda 计算成本相比,调配的并发成本较高。作者图片**   *为了保持高可用性,我们使用使用 **DynamoDB 全局表**的跨区域数据复制。这给我们的 DynamoDB 成本增加了更高的价格**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/0646ee3ebc85b7f10f3c88834f1de063.png)**与正常数据库操作相比,数据复制费用较高。作者图片**   *设计具有更好的监控和可观察性的安全解决方案是一项业务重点。使用 **CloudWatch 指标**会产生更高的成本**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/49c9a9b7a2a8fcf60f2ea70fe16452e7.png)**CloudWatch 指标成本可能会让许多人感到惊讶。作者图片**安全性、数据保护、低延迟、高可用性、弹性和可观察性是优先于成本优化的无服务器应用的几个核心方面。最终用户体验和满意度是另一个没有企业愿意为了削减成本而牺牲的领域。*> *简而言之,设计更好的无服务器解决方案成为当务之急。*# *朝向 DevOps**构建无服务器应用程序确实需要一种不同于我们过去概念化应用程序的思维方式。构建安全和高性能的解决方案与使用最佳计算模型实现最佳业务逻辑同样重要。**   *工程师们花几个月的时间制作出一份详细的设计规范并交给一个离岸团队进行编码的日子已经一去不复返了。*
*   *工程师编写代码,编译代码,然后把代码扔给其他团队的日子已经一去不复返了。*
*   *公司保留一个没有动力的维护团队来修复生产缺陷的日子已经一去不复返了。*
*   *T2 单一用途软件工程师只知道如何用特定编程语言编码的日子也一去不复返了。**欢迎来到 DevOps!*> *与过去形成对比的是,这个时代的工程师在编写单一用途的 Lambda 函数的同时,已经发展成为多资源多方面的独石,能够做许多事情!*# *拉近商业和技术的距离**DevOps 运动无疑缩小了工程和运营之间的差距。它导致更小的无所不包的团队拥有从需求到生产甚至更多的特性。**这些团队遵循敏捷和精益原则,以变得高效和成本有效。它有助于建立项目涉众对工程团队的信任。信任让团队有信心更快地交付功能,以满足不断变化的客户需求。*## *新世界新需求**为我们的现代世界构建解决方案有许多需求。**   *应用程序有望抵御残酷的网络攻击。*
*   *企业面临着遵守监管和行业标准以保护个人数据的巨大压力。*
*   *客户的关注点每隔几秒钟就会发生变化。让他们参与进来并更快地为他们服务至关重要。**在整个企业世界中,传统的 IT 部门变得分散,有利于自治的产品团队。它允许技术和非技术同事坐在一起,构建更好的解决方案。反过来,它有助于培养尊重和加强相互理解。*## *无服务器催化剂**无服务器带来的是满足这种需求的灵活性和*便利性*。更快地设计、开发、部署和改进功能。软件特性的生命周期比以往任何时候都要快。无服务器是一项让用户更容易跟上进度的技术。**这种业务和技术协作增加了业务价值。以客户为中心的、快节奏的、功能驱动的开发使业务蓬勃发展。*> *多亏了无服务器,加速变得更快了。*# *疫情效应**2020 年!**虽然它因为疫情的原因而留在了历史书上,但它也给企业家智囊团上了严酷的一课,注入了新的理念。**   *许多组织为了生存改变了他们的运作模式。*
*   *许多人陷入混乱,只有一线希望和有限的时间做出反应。*
*   *对其他人来说,这是一个保持活力或爆发的选择。**我们认为不可能的事情变成了可能。业务优先级每隔一天就会发生变化。那些迅速适应的人经受住了考验。那些将业务建立在可扩展基础设施上的企业能够根据不可预测的客户需求快速进行调整。*## *锁定期间的业务增长**BBC 最近在英国进行的一项调查显示,在疫情期间,新业务的开展显著增加。这可能会让一些人感到惊讶,但从现代云技术的角度来看,这是一种可能性。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/e3036ee6711c1d6ffda53dfc972c7ea9.png)**来源: [BBC](https://www.bbc.co.uk/news/business-55405289)**在混乱、心痛和困惑中,每个人都需要一种技术,可以用来更快地构建解决方案,并从他们的客厅进行管理。对于大多数成功的人来说,他们对云和无服务器的采用肯定起到了至关重要的作用。**这种商业模式和无服务器的价值差异将在 2021 年及以后继续加强。*> *无服务器建筑的*便利*将开始挣脱其成本思维的束缚。*# *便利计算的出现**AWS Lambda 是无服务器计算的核心。Lambda 自诞生以来发生了很大的变化。它仍在发展。随着更长的执行时间、更多的内存、更高的处理器、更重的工作负载等,它无疑变得更加复杂。然而,它有助于编写简单的函数和轻松构建无服务器解决方案。如果无服务器只是为了低成本,那么 Lambda 服务就不会发展到现在。**成为*最便宜的*并不总是许多技术采用背后的主要原因。需求随着时间而变化。这些天来,**   *企业正在寻找最简单的方式来运行计算工作负载*
*   *产品所有者正在寻找最快的方式来发布一个特性*
*   *工程师们正在寻找处理客户数据的最安全的方式*
*   *顾客正在寻找购买产品的最简单的方式**名单还在增长。当试图满足这样的需求时,团队并不总是将成本作为阻碍因素。通常,他们选择*方便*的方式来经营,以获得竞争力和成功。当谈到提供这种舒适度的技术时,无服务器显然是赢家。**我前面提到的英国广播公司的调查也强调了以下几点。**   *在疫情时代,在家经营企业是有意义的*
*   *专注于网络的公司做得很好*
*   *不是所有这些企业都是永久性的**传统的商业模式不断被改写。根据需要扩大和缩小规模已经成为正常的操作模式。**临时和权宜之计的企业是常见的这些天。许多在线业务根据交易条件和业务限制自动开始运作或进入休眠状态。现代云和无服务器技术让这一切成为可能。*> *我们可以说这是新的*便利云模式*。使用无服务器,从一开始就可以保证这一点。*# *结论**无服务器在其相对较短的存在时间内成功地赢得了许多企业。作为一项技术,几个用例已经证明了无服务器的多功能性。每天都有大量的新用例被自信而舒适地构建出来。它以成本为中心的类型正在改变。**然而,挑战在于如何在复杂的事物中保持简单。简单性使得每个人都能更快地*方便*构建解决方案。**也许是时候为无服务器设计一个新的标语了。让 ***便利计算*** 成为那个无服务器自信地朝着未来正确方向前进的人吧!*# 将数据存储导出到 BigQuery 的无服务器方法> 原文:<https://towardsdatascience.com/serverless-approach-to-export-datastore-to-bigquery-4156fadb8509?source=collection_archive---------15----------------------->![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/82a541ee93136b1557b0c6662664b24b.png)Maksym Kaharlytskyi 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片## 一种在 Google 云平台上使用无服务器方法定期将数据存储导出到 BigQuery 的简单方法概述一下, [Google Datastore](https://cloud.google.com/datastore) 是一个完全托管的 [NoSQL](https://en.wikipedia.org/wiki/NoSQL) 服务,自 2008 年开始在 Google 云平台上提供。它可以被视为一个键值和一个文档数据库,提供了一个类似 SQL 的查询语言和绑定到许多编程语言的 SDK。数据库被组织成实体(或记录)。每个属性可以有一个或多个命名属性,其中每个属性可以有一个或多个值。实体通过由名称空间、种类和标识符(字符串或整数)组成的键来标识。应用程序可以使用该键来获取特定的实体。值得注意的是,相同种类的实体不需要具有相同的属性,并且同名属性的值不需要具有相同类型的值。简而言之,你可以把任何你喜欢的文件存放在这个键下。虽然 Datastore 允许各种查询,但是从数据探索和分析的角度来看,将记录放在容易查询的常规数据库中更可行,例如, [BigQuery](https://cloud.google.com/bigquery) 。# 我们开始吧一个可能的解决方案是将这些类型从 datastore 导出到 BigQuery。导出分两步完成:1.  将选定的种类导出到云存储中的存储桶
2.  将导出的种类从云存储导入到 BigQuery[BigQuery 加载作业可以选择直接从云存储中读取导出的数据存储种类](https://cloud.google.com/bigquery/docs/loading-data-cloud-datastore#:~:text=BigQuery%20supports%20loading%20data%20from,into%20BigQuery%20as%20a%20table.),因此我们不需要任何其他转换。BigQuery 加载作业的约束是必须逐个导出这些类型。同样值得注意的是,BigQuery 中现有的导出表将被新数据替换。这个解决方案很简单,但是有一些缺点。主要的一个问题是,该过程不允许指定要导出的记录类型的子集。换句话说,您每次都必须导出-导入所有记录。对于小型数据存储,这仍然是可行的,但是对于较大的数据库,这种解决方案不是最佳的—请注意这一点。在下一篇文章中,我将告诉你如何解决这个问题——敬请期待。对于多种类型,一种简单的优化技术是重叠导出/导入作业,如下所示:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/c717b9e032c6780809cced40d7593d65.png)平行导出/导入作业的重叠(按作者)这样,我们可以减少导出整个数据存储的总时间。请记住,您只能有 50 个并发导出作业,对于一个项目,您每分钟最多可以执行 20 个导出请求。# 命令行方法可以使用两个命令行工具运行导出/导入: [*gcloud*](https://cloud.google.com/sdk/gcloud) 和 [*bq*](https://cloud.google.com/bigquery/docs/bq-command-line-tool) *。*在第一个示例中,您将运行数据存储到存储桶的导出:`gcloud datastore export gs://bucket-name/Kind1 --project gcp-project --kinds Kind1`使用第二种方法,您可以在 BigQuery 中运行作业导入,它将从 bucket:
`bq load --source_format=DATASTORE_BACKUP datastore_export.Kind1 gs://bucket-name/Kind1/default_namespace/kind_Kind1/default_namespace_kind_Kind1.export_metadata`中获取数据# 无服务器方法命令行方法听起来是一个很好的起点,可以探索解决方案是否有效,作为在 BigQuery 中获得即时数据的一次性导出方法。但是,如果您喜欢每天运行此任务,最好将此作业安排在其他地方。虽然上面两个命令可以从一个[虚拟机](https://cloud.google.com/compute)上执行,但是使用 cron 等,这不是一个最佳的解决方案。首先,如果任务必须运行,例如每天运行,您将浪费资源,因为资源每天只使用几分钟。其次,您必须监控虚拟机是否在运行,系统是否是最新的,等等。又来了一个无服务器。[无服务器](https://en.wikipedia.org/wiki/Serverless_computing)是一个流行的概念,你可以将所有的基础设施任务委托给其他地方。作为开发人员,您需要提供的只是解决问题的代码。因此,您不需要管理虚拟机、升级主机操作系统、担心网络等问题。—这都是由云提供商完成的。在这个小故事中,我将展示如何使用无服务器组件构建导出/导入过程。这是可能的,因为最终我们将只调用[GCP](http://cloud.google.com/)API 并等待结果——工作将在后台完成。
该解决方案的架构图如下所示。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/041ab4b6d5a2f8294e9581d546e857e9.png)无服务器解决方案的架构图(作者)该架构包括两个[云功能](https://cloud.google.com/functions):*   数据存储 _ 导出器
*   bigquery_importer*datastore_exporter* 负责调度输入类型的数据存储导出。下一个函数 *bigquery_importer* ,负责调度 bigquery 加载作业。图中的其他组件用于协调流程。让我通过一步一步地列举来详细说明它是如何工作的。# 函数数据存储导出器类似 cron 的作业由 [Google Scheduler、](https://cloud.google.com/scheduler)管理,它触发*datastore _ exporter*Cloud 函数,其中列出了数据存储中所有可能的类型,并为它们中的每一个安排导出到云存储。产生的对象位于云存储桶中。时间表本身是用我们定义该函数将在每天 6.00 执行。调度器将通过调用 HTTP 触发器来触发云功能。它使用关联的服务帐户对请求进行身份验证。schedule 使用的服务帐户的定义如下所示。该账户有权限调用 *datastore_exporter* 云函数。*gcf_datastore_exporter* 函数定义为在这里我们设置这个函数将由 HTTP 触发。截至 2020 年 1 月 15 日,云功能默认要求触发[认证](https://cloud.google.com/functions/docs/securing/managing-access-iam#allowing_unauthenticated_function_invocation)。我们保持原样。当给定类型的导出完成时,会引发一个由 *bigquery_importer* 函数捕获的[事件](https://cloud.google.com/functions/docs/calling/storage)*Google . storage . object . finalize*。# 函数 BigQuery 导入程序该函数的目的是处理 bucket 事件,并安排将导出的数据存储导入到 BigQuery。函数本身是使用[*Google _ cloud functions _ function*](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloudfunctions_function)*资源定义的**其中我们定义了该函数将在来自数据存储输出桶的事件上被触发。我们只对事件*感兴趣***函数的主体非常简单。**该函数正在监听所有的对象完成事件,但只有当对象名为*all _ namespaces _ kind _<kind>时。创建 export_metadata* ,然后计划导入到 BigQuery。**负责运行 BigQuery 导入的函数非常简单。我们实际上必须定义的是一个作业,它基本上包含与命令行相同的属性:***完整的功能你可以在这里找到:*[*https://github.com/jkrajniak/demo-datastore-export*](https://github.com/jkrajniak/demo-datastore-export)**<https://github.com/jkrajniak/demo-datastore-export>  # 许可合适的权限集非常重要,我们应该始终遵循最小权限原则。两种云功能都有自己的[服务账号](https://cloud.google.com/iam/docs/service-accounts)。对于数据存储导出器,我们附加以下角色

"roles/monitoring.metricWriter",
"roles/logging.logWriter",
"roles/datastore.importExportAdmin",
"roles/datastore.viewer"


第二个函数需要以下权限

"roles/monitoring.metricWriter",
"roles/logging.logWriter",
"roles/bigquery.jobUser",


此外,它需要访问输出桶和输出数据集。这是通过绑定 IAM 权限来实现的# 部署简单说一下这两个云功能的部署。由于“基础设施”的其他部分内置在 terraform 中,我认为云功能的部署也应该由 [Terraform](https://www.terraform.io/) (TF)负责。为此,我使用了两个 TF 资源 *archive_file* 和*Google _ storage _ bucket _ object。*[*archive_file*](https://registry.terraform.io/providers/hashicorp/archive/latest/docs/data-sources/archive_file) 资源将从源目录的内容创建一个 zip 文件。然后这个文件会被复制到云存储。这里,云存储中的对象有一个根据文件的 SHA1 计算的后缀——这就像简单的版本控制一样。对于生产运行,我将使用 Git commit hash 和 Git 标记作为对象后缀。在 https://github.com/jkrajniak/demo-datastore-export,你会找到基础设施和两个云功能的代码。* *最后的几点*1.  *我总是鼓励使用无服务器的方法。只关注方法,把所有基础设施的麻烦都留给巨人,这样会更容易、更快。*
2.  *始终将您的基础设施作为一个代码,例如使用自动气象站[云形成](https://aws.amazon.com/cloudformation/)、[地形](https://www.terraform.io/)或 [CDK](https://aws.amazon.com/cdk/) 。它比任何其他处理项目/服务配置的方法都更加可靠和可重复。每当您必须在一个新的 GCP 项目中重建基础设施时,您将立即从这种方法中受益,否则就会出现问题。此外,通过这种方式,您可以像处理应用程序的部署一样,自动化基础设施变更的部署。*
3.  *数据工程任务不应该是任何常规和标准软件开发方式的例外——编写单元测试,使用 CI/CD 进行代码和基础设施部署,并将您的代码保存在存储库中;不要依赖在任何花哨的网络界面点击的解决方案。**我希望你喜欢这个故事,它会对你的日常工作有所帮助。如果您有任何问题或建议,请随时通过 [Twitter](https://twitter.com/MrTheodor) 或 [Linkedin](https://www.linkedin.com/in/jkrajniak/) 联系我。*# Prefect 和 AWS ECS Fargate 简化了无服务器数据管道> 原文:<https://towardsdatascience.com/serverless-data-pipelines-made-easy-with-prefect-and-aws-ecs-fargate-7e25bacb450c?source=collection_archive---------9----------------------->## 编排 Python 数据管道的最简单方法![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/f92760cf9b139436de65b97815745bee.png)照片由[突发](https://www.pexels.com/@burst?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)发自[像素](https://www.pexels.com/photo/architecture-buildings-business-city-374603/?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)E 尽管有这么多的工作流程编排解决方案和云服务来构建数据工作负载,但很难找到一个真正好用并能让你快速上手的。在 Python 中构建数据管道最流行的工具之一是 perfect——一个工作流管理平台,具有基于混合代理的执行模型。混合执行模式需要什么?这意味着即使您使用云编排平台( *Prefect Cloud* ),您仍然拥有并管理您的代理。事实上,Prefect 不能直接访问您的代码或数据。相反,它只对注册代理和流时发送给它们的 API 的元数据进行操作。首先,这意味着该平台可以满足严格的**安全性&合规性**要求,因为整个工作流的执行都在您选择的计算资源内进行。另一方面,它提供了令人难以置信的灵活性。您的代理可以是 Kubernetes 集群、AWS 上的 ECS Fargate 集群、本地或云中的任何计算服务器,或者它们的组合。甚至你的笔记本电脑也可以注册为完美代理。尽管混合执行模型有很多好处,但是正确配置执行层可能是一个挑战。在本文中,我们将了解如何使用一个 **AWS ECS Fargate** 代理和 **S3 存储**来设置 Prefect,这允许一个完全无服务器的NoOps 执行环境。> **更新:**你可以在[这篇文章](/how-to-cut-your-aws-ecs-costs-with-fargate-spot-and-prefect-1a1ba5d2e2df)中找到 ECS 代理设置的更新版本。**目录**

· Getting started with Prefect Cloud
· Prefect Cloud setup
∘ Create your Prefect account
∘ Install Prefect
∘ Create a personal access token to authenticate with Prefect Cloud
· Creating AWS resources
∘ Creating an IAM role for our ECS tasks
∘ Creating an S3 bucket to store our flow code
∘ Creating an EC2 instance for our agent process
∘ SSH to the EC2 instance to configure the ECS Prefect agent
∘ Creating a Fargate cluster
· Deploying our first serverless flow
∘ ECSRun configuration & Prefect Storage
∘ Registering a flow for ECS agent
· Conclusion


# 完美的云设置## 创建您的高级帐户首先,在[https://cloud.prefect.io/](https://cloud.prefect.io/)上注册一个免费的入门账户。## 安装提督以下命令将安装带有 AWS 子包的提督(*而不是* `[aws]` *如果您想安装外部系统*的所有提督扩展,您可以使用 `[all_extras]` *):*

pip install "prefect[aws]"


为了确保我们使用 Prefect Cloud 作为编排层,而不是开源的 Prefect Server,将上下文切换到*“Cloud”*:

prefect backend cloud


## 创建一个个人访问令牌,以便通过 Prefect Cloud 进行身份验证注册免费账户后,您需要创建一个**个人访问令牌**来使用 Prefect Cloud 验证您的本地终端。这将允许直接从您的本地机器向 Prefect Cloud API 注册您的流(*即您的 ETL & ML 数据管道*)。从主菜单中,选择:用户→个人访问令牌→ `+ CREATE TOKEN`按钮。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/2845a32d7ff54eea6514927df1c53b8f.png)完美的云用户界面——作者图片选择一些有意义的名字。`YourAuthTokenToPrefectCloud`。然后复制令牌,并在您的终端中运行以下命令:

prefect auth login -t


**认证步骤**允许您的笔记本电脑与 Prefect Cloud API 通信,以注册您的代理和流。然后,API 负责调度您的工作流,您的代理不断轮询 API 以了解它是否需要触发一些流运行。一旦计划触发流,代理就会得到通知并开始执行。下一步,我们将在 AWS 上设置一个 **ECS Fargate 集群**作为我们的执行层。除此之外,我们还需要一个**代理**,也就是**轮询工作** ( *即流程执行*)的 API。为此,我们将创建一个带有后台进程的 **EC2** 实例——您可以创建一个 ECS 服务来代替。在进入 AWS 设置之前,让我们创建一个 **RUNNER 令牌**,我们将使用它向 Prefect Cloud 认证我们的代理。

prefect auth create-token -n DemoFargateAgentToken -s RUNNER


你可以用任何你喜欢的名字代替`DemoFargateAgentToken`。将作用域( **-s** 标志)设置为 RUNNER,并将令牌保存在安全的地方,这一点很重要。我们将在下一节用到它。# 创建 AWS 资源如上所述,我们需要:*   一个 t2.micro **EC2 实例**,我们将在其中部署我们的**代理**——这个实例的全部目的是拥有一个单独的进程,它为流运行轮询完美的 API,
*   我们将把一个 **IAM 角色**分配给我们的 ECS Fargate 任务,这样我们的容器就可以**访问包含我们流代码的 S3 桶**。此外,这将允许我们缓存到 S3 的流的中间结果,
*   一个 **S3 桶**作为我们流代码的默认存储,
*   一个 **ECS Fargate 集群**,它将为我们的容器化数据管道按需旋转**计算资源**,即无服务器。这样,我们就没有空闲的计算资源,我们可以创建一个[按需 Dask 集群](https://cloudprovider.dask.org/en/latest/)来在需要时跨几个 Fargate 容器并行化我们的 Python 代码。## 为我们的 ECS 任务创建 IAM 职责(结果:任务职责 ARN)在这一步中,我们创建一个**任务角色**,它将赋予我们的 ECS 任务(*您可以把它看作是您的流*的容器)访问 S3 中的对象的权限。理想情况下,您应该遵循最小特权原则。出于开发目的,您可以为 S3 和 ECR 分配完全访问权限,以便您的 ECS 容器可以与存储在 S3 的对象以及 ECR 容器映像进行通信。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/354520c812b8731d2f970035e1796762.png)为 ECS 任务创建 IAM 角色(第 1 部分)—作者图片![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/d95cd0d8182e3acb8f3c2c6e8e0f55d6.png)为 ECS 任务创建 IAM 角色(第 2 部分)—作者图片## 创建一个 S3 桶来存储我们的流代码请记住,您的 bucket 名称必须是全球唯一的(*就像网站的域名*)。您可以在单个 CLI 命令中或从管理控制台创建存储桶。

aws s3 mb s3://yourbucket


## 为我们的代理进程创建 EC2 实例用 Amazon Linux 2 AMI 选择 **t2-micro** 实例类型(*对于单个进程*应该绰绰有余)。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/5681f4cc137db260498d28fc37370357.png)EC2 启动向导—作者图片然后遵循默认值,直到到达安全组部分,在这里您需要允许您的 IP 地址 SSH 访问实例。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/912b685e7d97481490ccffd9935d5e92.png)EC2 启动向导—作者图片之后,点击**查看并启动**,继续创建密钥对(*a*T28)。pem *文件*)以便可以用它来 SSH 到实例。![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/5f95da2ad8f9cd223490b15c9f0db422.png)创建密钥对(。pem 文件)SSH 到实例—由作者创建的图像## SSH 到 EC2 实例,以配置 ECS 提督代理一旦实例处于运行状态,您就可以直接从浏览器或使用 SSH 客户机进行连接。现在,我们需要在 EC2 实例上安装 Python 3:让我们检查一下 Python 和包的版本,以便您确切地知道我在这个 EC2 设置中使用了什么:我们的 ECS Fargate 代理将需要几个环境变量。让我们使用`export`来设置它们:上面的第 1 行引用了我们在上一节中创建的 RUNNER 令牌(*不要与我们用来授权我们的机器向 Prefect Cloud API* 注册流的个人访问令牌混淆)。要找到子网 id(*第 5 行*),请转到 VPC 部分,从您的默认 VPC 中复制 id。当然,如果你愿意,你可以创建自己的 VPC。出于开发目的,默认的 VPC 就可以了。另外,如果您没有`ecsTaskExecutionRole`,您可以按照 AWS 管理控制台中的“创建 ECS 集群”向导来创建它。> ***注:*** `*executionRoleArn*` *与* `*task_role_arn*` *不同。而* ***执行角色*** *则赋予代理在 ECS (* ex)上执行容器的权限。从 ECR 中提取图像,将日志存储在 CloudWatch *)、* ***任务角色*** (作为章节的一部分创建:[为我们的 ECS 任务创建 IAM 角色](#8815) ) *为您的流的容器提供访问其他 AWS 资源的权限,比如保存您的完美流的元数据的 S3 桶,或者其他一些 AWS 服务,比如 SQS、SNS 或 DynamoDB。*## 创建 Fargate 集群现在让我们使用`aws configure`来配置 AWS CLI,这样我们就可以直接从这个 EC2 实例创建一个 ECS Fargate 集群。如果您没有 AWS 密钥对,您应该首先创建一个 IAM 用户,该用户有权创建 ECS 集群。为了简化设置,我们使用标准 ECS 配置来创建一个集群,其默认名称为*“default”*,兼容性为“Fargate”(*,即无服务器数据平面*)。

aws ecs create-cluster


就是这样!我们的集群已经创建。剩下要做的就是启动一个 Prefect agent 进程,该进程将轮询 Prefect Cloud API 的流运行,并将工作提交给 AWS 上的无服务器 ECS 容器平台。我们可以在一个 CLI 命令中完成:**注意:***   `ECSTaskS3Role`是我们之前创建的 IAM 角色(*部分:* [*为我们的 ECS 任务*](#8815) 创建一个 IAM 角色),它将由 ECS 任务(*容器*)承担,以便拥有访问 S3 的权限,
*   我们添加了标签**“fargate-dev”**和**“S3-flow-storage”**—这些标签非常重要,因为这样我们可以“告诉”Prefect Cloud API 哪个代理应该接收特定流的流运行(*即数据管道*)。由于我们的代理分配了这些标签,任何带有匹配标签的`RunConfig`流都将由该代理编排。一旦我们部署了一个示例流,这将变得更加清晰。
*   我们在末尾添加了`> /dev/null 2>&1 &`,以确保命令在后台运行(*守护进程*),并抑制对`stdout`和`stderr`的日志记录。如果你知道一个更好的方法来运行这个命令作为后台进程,请在评论或私信中告诉我。如果我们转到用户界面,我们可以看到我们的 ECS 代理可用并准备好执行计划的流程运行:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/8e9fceb753dd8df48698a2554bbb2c0b.png)ECS 代理在 UI 中是可见的——作者的图像因为我们在 EC2 上的 **ECS 代理** **在后台进程中运行,所以您可以从 SSH 会话断开连接。如果在某个时候,您的完美代理不再健康,您总是可以再次 SSH 到 EC2 实例,终止该进程,然后再次启动代理。Prefect Cloud 的好处在于,您可以直接在 UI 中看到代理的健康状况——不需要实现任何额外的监控层来跟踪这些信息。**# 部署我们的第一个无服务器流程## ECSRun 配置和完美存储在当前的完美版本( *0.14.x* )中,您需要定义一个运行配置来确定哪个代理将获取您的流代码来执行。在下面的代码中,您可以看到`ECSRun()`使用了一个标签`s3-flow-storage`来确保这个流只被带有相应标签的 ECS 代理获取。它的工作原理类似于 [Kubernetes](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/) 中的标签和选择器。在下面的例子中,我们简单地构造了一个 Pandas dataframe 并打印了一个 Pandas 版本,向您展示您可以使用您最喜欢的 Python 包并运行任何 Python 函数,只要您用`@task`装饰器调用它。然后,我们还传递运行配置和关于放在哪里的信息,以及从哪里检索我们的流的元数据(提督 [**存储**](https://docs.prefect.io/orchestration/flow_config/storage.html#aws-s3) ) —这里,它是一个 S3 桶。用作你的流的基础图像的`image`,可以是来自 Dockerhub 的一个提督的图像。 [**级/级:0.14.1**](https://hub.docker.com/r/prefecthq/prefect/tags?page=1&ordering=last_updated) 。我们使用我们自己的定制 Docker 映像,它只是 Prefect 的基本映像的一个副本,在其上安装了附加的 Pydata 包,以包含构建 ETL 或 ML 数据流所需的所有组件。这是我们用于公开图片`anisienia/prefect-pydata`的 docker 文件:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/2103bbe8e1f948d390e369f9e796e4a1.png)显然,您可以指定**确切的包版本**,并在您的映像中包含您可能需要的任何**自定义依赖项**。此外,您可以使用 **ECR** 来代替 Dockerhub。存储公共 Dockerhub 图像是 100%免费的——如果你没有凭证,也没有定制公司的内部业务逻辑(*只是 Python 包依赖*),你可以通过这种方式节省一些 AWS 成本。如果您想了解更多关于如何构建 Dockerhub 容器映像的信息,这里有一个适合您的方法:</7-steps-to-your-own-dockerhub-image-7d1d92d2a3e0>  ## 为 ECS 代理注册流下面是我们可以使用的流程示例:![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/3cc22921110d49717bcaa7dae7400f03.png)**进一步说明&附加信息:***   ***task _ role _ arn***是我们作为部分创建的 IAM 角色的 ARN:*[*为 ECS 任务*](#8815) 创建 IAM 角色。*
*   *在`ECSRun()`内部,我们定义了一个标签 **s3-flow-storage** ( *第 8 行*),这样我们的 ECS 代理就可以获取这个流来执行。这种机制允许在适当的代理之间编排流程以供执行。**注意**代理和流之间的标签必须完全匹配。这意味着来自代理的所有标签必须匹配来自流的所有标签。*
*   *在`ECSRun()`内部,我们还定义了一个定制的 Docker 映像(这里是*:我们来自 Dockerhub 第 10 行*的映像),以确保我们的流环境包含所有 Python 包以及我们需要的确切版本。*
*   *命令 **flow.register()** 需要指定项目名称。你可以在 UI 中创建一个项目或者使用命令:**提督创建项目<项目名称>** 。**![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/303113497c8a01d7442848ed98705ed1.png)**上面显示的流示例中的日志—图片由作者提供*# *结论**在本文中,我们研究了如何使用 Prefect 创建 ECS Fargate 代理。我们创建了一个 **ECS 集群**和一个 **EC2 实例,其中一个 Prefect ECS 代理**作为后台进程运行,持续轮询 Prefect Cloud API 以获取新的流运行。**然后,我们运行了一个**示例流**,它使用我们的 Docker 映像作为基础来反映定制 Python 包的依赖性。当注册我们的流时,流的元数据被发送到指定的 **S3 桶**(代码片段中的*第 12 行)。每次 Prefect Cloud API 调度并触发流运行时,ECS Fargate 代理都会检索相同流的元数据。**> ***注意:**如果您对代理设置有任何问题,可能有人已经经历过相同的问题,并在完美社区 Slack 中分享了他们的解决方案:[https://prefect-community.slack.com/](https://prefect-community.slack.com/)。****感谢您的阅读!******参考资料&其他资源:****[1] [提督 0.14.0:改进流道配置](https://medium.com/the-prefect-blog/prefect-0-14-0-improved-flow-run-configuration-af6c3caccd04)**[2] [提督用 Fargate 部署流程——YouTube 直播](https://www.youtube.com/watch?v=yu1bD3KHmhA)**[3] [ECS 有效的 CPU 和内存配置](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-cpu-memory-error.html)*# 无服务器功能和将 AWS Lambda 与 S3 存储桶一起使用> 原文:<https://towardsdatascience.com/serverless-functions-and-using-aws-lambda-with-s3-buckets-8c174fd066a1?source=collection_archive---------3----------------------->![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/1a365cb172952a86bad79231a47dab20.png)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上由 [C Dustin](https://unsplash.com/@dianamia?utm_source=medium&utm_medium=referral) 拍摄的照片## 了解如何构建一个由 S3 PUT 事件触发的 Lambda 函数来对 S3 桶中的文件进行排序。在我以前的文章中,你可能已经看到我不断地在云上的服务器实例上部署代码,构建服务来管理这些实例,在这些服务之上构建反向代理等等。毫无疑问,你们中的一些人可能希望只需编写代码,将其部署到某个地方,而不必担心设置和管理服务器实例的过度复杂性。根据您的使用情况,可能会有一个解决方案——无服务器功能。无服务器功能允许部署代码,而无需为代码分配任何基础架构。AWS Lambda 是一个 FaaS(功能即服务)平台,允许您构建无服务器功能。AWS Lambda 支持大多数主流编程语言,如 Go、Java、Ruby、Python2 和 Python3。在本教程中,我们将使用 Python3。尽管它们被称为“无服务器”,但它们实际上运行在云服务器实例上的各种运行时环境中。无服务器功能是无状态的,也就是说,一个功能的一次执行并不保持一个后续执行可以识别或使用的状态。换句话说,一个无服务器功能的执行不会以任何方式与另一个执行进行通信。由于无服务器功能是受时间和资源限制的,所以它们适用于短期任务。它们在内存、CPU、存储等分配方面提供的灵活性非常小。采用某个 FaaS 平台来实现无服务器的一个含义是,对于您的无服务器功能可能与之交互的大多数其他云服务,您会被该平台的供应商所束缚。## 稍微了解一下微服务…无服务器功能可用于构建微服务架构,其中您的软件由提供特定功能的较小的独立微服务构建而成。微服务让开发者更容易以敏捷的方式构建、测试和管理软件。微服务是软件中完全独立的部分,因此不同的微服务可以并行编码、测试和部署。定位和修复微服务架构中的错误要容易得多,因为您只需处理出现故障的微服务。例如,网飞在 2009 年开始将其软件迁移到 AWS 云上,成为了基于微服务架构的最早采用者之一。他们目前维护着一个 API 网关,每天接收数十亿个请求,由独立的微服务组成,用于用户注册、下载电影等流程。通过切换到微服务架构,网飞能够加快其软件的开发和测试,并在遇到错误时轻松回滚。## Lambda 与其他 AWS 服务当与其他 AWS 服务结合使用时,AWS Lambda 上的无服务器功能或简单的 *Lambda 功能*可以做一些非常酷的事情,比如使用亚马逊 Alexa 来打开和关闭 EC2 实例,或者当有东西被推送到 CodeCommit(甚至 GitHub)库时点亮灯泡。有 3 种方法可以将 Lambda 与其他 AWS 服务结合使用:1.  使用其他 AWS 服务作为触发器来调用 lambda 函数*。一个 lambda 函数可以有多个触发器,使用广泛的 AWS 服务,如 S3 存储桶放置或删除事件、对 API 网关端点的调用等。
2.  从 lambda 函数内部与 AWS 服务交互,比如在 DynamoDB 数据库中添加或删除数据,或者获取 EC2 实例的状态。你可以使用 AWS 为你的 lambda 函数代码中的 [Java](https://docs.aws.amazon.com/sdk-for-java/index.html) 、 [Python](https://docs.aws.amazon.com/pythonsdk/?id=docs_gateway) **、 [Ruby](https://docs.aws.amazon.com/sdk-for-ruby/?id=docs_gateway) 等构建的各种 SDK(软件开发工具包)来做到这一点。使用这些 SDK,可以控制一长串 AWS 服务或与之通信。
3.  每当调用 Lambda 函数时,使用 AWS 服务作为其调用记录的目的地。截至目前,只有 4 种 AWS 服务可以这样使用:SNS、SQS、EventBridge 或 Lambda 本身。**当另一个 AWS 服务调用 Lambda 函数时,Lambda 使用* ***事件*** *对象将特定信息从该 AWS 服务传递给该函数。这将包括诸如哪个 DynamoDB 数据库中的哪个项目触发了 Lambda 函数之类的信息。****** Boto3*** *是一个由 AWS 构建的 python 库(或 SDK),允许你与 AWS 服务交互,如 EC2、ECS、S3、DynamoDB 等。在本教程中,我们将使用 Boto3 管理 AWS S3 桶内的文件。Boto3 的完整文档可以在* [*这里*](https://boto3.amazonaws.com/v1/documentation/api/latest/index.html) *找到。*## 将 Lambda 与 AWS S3 桶一起使用> **本教程的先决条件:**AWS 自由层帐户。*一个* ***S3 桶*** *只是 AWS 云中的一个存储空间,用于存储任何类型的数据(如视频、代码、AWS 模板等)。).S3 存储桶中的每个目录和文件都可以使用一个键来唯一标识,这个键就是它相对于根目录(也就是存储桶本身)的路径。比如“car.jpg”或者“images/car.jpg”。*除了作为开发基于微服务的软件的强大资源之外, ***Lambda 函数还是高效的 DevOps 工具。*** 让我们来看一个例子,上面提到的前两种使用 Lambda 函数和 S3 桶来解决一个简单的 DevOps 问题:)假设您正从三个不同的煤气表接收 XML 数据,并将其直接放入 AWS S3 桶中。您希望根据数据来自哪个燃气表,将 XML 文件分类到三个独立的文件夹中。了解数据源的唯一方法是查看 XML 文件内部,如下所示:
gas_meter3 bla bla bla ```

您将如何自动化这一过程?这就是 AWS lambda 可以派上用场的地方。让我们看看如何做到这一点。

1 -创建一个 S3 桶

让我们从构建一个空的 S3 桶开始。你所要做的就是从你的 AWS 控制台进入 S3 页面,点击“创建桶”按钮。确保选中“阻止所有公共访问”复选框,然后单击“创建存储桶”。

现在,添加一个名为“unsorted”的目录,所有 XML 文件最初都将存储在这个目录中。创建一个名为“testdata.xml”的. xml 文件,其内容如下:

<data>    <data-source>gas_meter3</data-source>    <data-content>bla bla bla</data-content>
</data>

2 -创建 Lambda 函数

在 AWS 控制台的服务选项卡中,单击“Lambda”。在 Lambda 页面的左侧窗格中,选择“函数”,然后选择“创建函数”。

选择“从头开始创作”,并给函数取一个合适的名字。因为我将使用 Python3,所以我选择“Python3.8”作为运行时语言。Python2 和 Python3 还有其他版本。选择一种运行时语言,然后单击“创建函数”按钮。从“函数”页面上的 Lambda 函数列表中,选择您刚刚创建的函数,您将被带到该函数的页面。

Lambda 会自动创建一个 IAM 角色供您与 Lambda 函数一起使用。IAM 角色可以在函数页面的“权限”选项卡下找到。您需要确保该函数的 IAM 角色有权访问和/或管理您从函数内部连接到的 AWS 服务。

确保将“S3”权限添加到 IAM 角色的权限列表中,可通过 IAM 控制台访问。

3 -为我们的 Lambda 函数添加一个触发器

我们希望 Lambda 函数在每次 XML 文件上传到“未排序”文件夹时被调用。为此,我们将使用一个 S3 存储桶 PUT 事件作为函数的触发器。

在 Lambda 函数页面的“设计器”部分,点击“添加触发器”按钮。

选择“S3”触发器和您刚刚创建的桶。选择“上传”事件类型。将前缀和后缀设置为“未排序/”和”。xml”。最后,点击“添加”。

4 -给我们的 Lambda 函数添加代码

有三种方法可以向 Lambda 函数添加代码:

  1. 通过控制台上的代码编辑器。
  2. 上传一个包含所有代码和依赖项的. zip 文件。
  3. 通过从 S3 桶上传代码。

在本教程中,我们将使用第一种方法。在您的函数页面上,转到“函数代码”部分找到代码编辑器。

将以下代码复制并粘贴到代码编辑器中:

import json
import boto3
import uuid
from urllib.parse import unquote_plus
import xml.etree.ElementTree as ETdef lambda_handler(event, context):s3 = boto3.resource('s3', region_name='')#Replace with your region name #Loops through every file uploadedfor record in event['Records']:bucket_name = record['s3']['bucket']['name']bucket = s3.Bucket(bucket_name)key = unquote_plus(record['s3']['object']['key']) # Temporarily download the xml file for processingtmpkey = key.replace('/', '')download_path = '/tmp/{}{}'.format(uuid.uuid4(), tmpkey)bucket.download_file( key, download_path) machine_id = get_machine_id_from_file(download_path) bucket.upload_file(download_path, machine_id+'/'+key[9:])s3.Object(bucket_name,key).delete()def get_machine_id_from_file(path):tree = ET.parse(path)root = tree.getroot() return root[0].text

不要忘记替换地区名称。

确保处理程序值为“<文件名>。λ_ handler”。处理程序值指定哪个函数包含 Lambda 执行的主代码。

每当 Lambda 运行您的函数时,它会将一个上下文对象 和一个事件对象 传递给它。这个对象可以用来获取关于函数本身及其调用的信息,例如函数名、内存限制、日志组 id 等。上下文对象对于日志记录、监控和数据分析非常有用。

如前所述,Lambda 使用事件对象从调用 Lamda 函数的 AWS 服务向 Lamda 函数提供特定信息。最初以 JSON 格式出现的信息在传递给函数之前被转换成一个对象。在 Python 的例子中,这个对象通常是一个字典。在上面的代码中,您可以看到事件对象被用来获取 S3 存储桶的名称和触发我们函数的 S3 存储桶中的对象的键。

上面的代码很容易理解。它执行以下操作:

  1. 事件对象获取信息。
  2. 下载导致 Lambda 函数被调用的 XML 文件。
  3. 处理 XML 文件,从 XML 文件的第一行找到 machine_id
  4. 将文件上传回 S3 桶,但放在一个名为 machine_id 的值文件夹中。
  5. 删除原始文件。

现在,按下“部署”按钮,我们的功能应该准备运行。

5 -测试我们的 Lambda 函数

AWS 使得通过 Lambda 控制台测试 Lambda 函数变得非常容易。无论您的 Lambda 函数使用什么触发器,您都可以使用 Lambda 控制台上的 测试 功能来模拟您的 Lambda 函数的调用。这一切都需要定义什么 事件 对象将被传递到函数中。为了帮助你做到这一点,Lambda 提供了特定于每种类型触发器的 JSON 模板。例如,S3 看跌事件的模板如下:

{"Records": [{"eventVersion": "2.0","eventSource": "aws:s3","awsRegion": "us-west-2","eventTime": "1970-01-01T00:00:00.000Z","eventName": "ObjectCreated:Put","userIdentity": {"principalId": "EXAMPLE"},"requestParameters": {"sourceIPAddress": "126.0.0.1"},"responseElements": {"x-amz-request-id": "EXAMPLE123456789","x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"},"s3": {"s3SchemaVersion": "1.0","configurationId": "testConfigRule","bucket": {"name": "example-bucket","ownerIdentity": {"principalId": "EXAMPLE"},"arn": "arn:aws:s3:::example-bucket"},"object": {"key": "test/key","size": 1024,"eTag": "0123456789abcdef0123456789abcdef","sequencer": "0A1B2C3D4E5F678901"}}}]
}

为了测试您刚刚创建的 Lambda 函数,您需要为您的函数配置一个测试事件。为此,单击 Lambda 代码编辑器正上方的“选择测试事件”下拉菜单,然后单击“配置测试事件”。

从弹出菜单中,确保“创建新的测试事件”单选按钮被选中,并选择“亚马逊 S3 Put”事件模板。应该为您提供类似于上面代码片段中的 JSON 数据。我们只关心 Lambda 函数中使用的数据,即桶名和对象键。编辑这两个值,使其适合于您之前创建的 S3 存储桶和 XML 文件。最后,为测试事件命名,并单击“Create”。

现在你已经有了 Lambda 函数的测试事件,你所要做的就是点击代码编辑器顶部的“test”按钮。控制台将告诉您功能代码是否被正确执行。要检查是否一切正常,请转到您的 S3 存储桶,查看 XML 文件是否已经移动到新创建的“gas-meter3/”目录中。

你可能已经注意到了,通过控制台进行测试的一个缺点是 Lambda 函数实际上会与其他 AWS 服务进行通信。这可能会无意中改变您的 AWS 资源,甚至丢失有价值的工作。这个问题的解决方案是在你的机器上构建并运行你的 lambda 函数。局部测试 Lambda 函数并不简单。您需要使用像 SAM CLI 和 Localstack 这样的工具来完成这项工作。

6 -全部搞定!

现在,如果 XML 数据是本教程中指定的格式,Lambda 函数应该将上传到 S3 存储桶的“未排序”文件夹中的任何 XML 文件排序到单独的文件夹中。

我希望这篇文章能让你体会到使用 AWS Lambda 可以实现什么,以及对无服务器功能的一些总体认识。 现在是你用 AWS Lambda 发挥创造力的时候了。

感谢您的阅读!

无服务器 GPU 驱动的机器学习模型托管

原文:https://towardsdatascience.com/serverless-gpu-powered-hosting-of-machine-learning-models-9f2b2be98294?source=collection_archive---------11-----------------------

Algorithmia 的一个工作示例

卡斯帕·卡米尔·鲁宾在 Unsplash 上的照片

动机

随着近年来 MLOps 的兴起,运行机器学习模型进行推理任务变得容易多了。根据使用情况,适当优化的深度学习模型甚至可以直接在移动设备上运行。在客户端-服务器/微服务架构中,具有高精度要求的较大模型通常集中托管,并由下游服务通过定义明确的接口进行查询。TensorFlow Serving 等工具现在也使这些用例在适当配置的服务器基础设施上成为可管理的问题。

然而,从软件工程的角度来看,我们知道自我管理的基础设施会变得多么复杂。毫不奇怪,云提供商的无服务器解决方案在应用程序开发领域越来越受欢迎。没有基础设施管理和随用随付是主要优势,这就是为什么我现在几乎只使用这种解决方案。

2021 年的无服务器 GPU

然而,当我发现自己处于将一个相当复杂的用于在线预测的深度学习模型集成到这样一个微服务的无服务器架构中时,我有些惊讶。在我的用例中,要求是以不规则的时间间隔(几秒到几小时)用 base64 编码的图像处理单个请求,并使用自我训练的深度学习模型返回正确的类。在我看来,一个没有深度复杂性的标准任务。被 Cloud Run,Cloud Functions,AWS Lambda 等宠坏了。,我天真地认为应该有一个“GPU 启用”复选框,然后我们就开始了…

不完全是。事实上,找到一个真正的无服务器解决方案并不容易。正如已经描述过的这里的,传统的无服务器解决方案更适合 CPU 工作负载。在我的例子中,仅使用 CPU 进行推理是不可能的,因为这种方式无法满足服务的延迟需求。

Google AI 平台预测和 AWS SageMaker?

与此同时,拥有人工智能平台预测的谷歌和拥有 SageMaker 的 AWS 提供了包括深度学习模型推理加速器在内的解决方案。只是简单总结一下为什么这些服务不符合我的要求(目前)。

从 AWS SageMaker 开始,最小实例计数要求为 1 或更高(https://docs . AWS . Amazon . com/sage maker/latest/DG/endpoint-auto-scaling-prerequisites . html)。对于许多持续负载的用例来说,这应该不是问题。然而,对于我来说,这将是一种资源浪费,并且不是 100%符合我所寻求的量入为出原则。

Google AI 平台预测目前只允许使用 TensorFlow SavedModel 格式的 GPU。例如,PyTorch 模型只能在没有 GPU 支持的定制容器(目前是预 GA)中使用。此外,谷歌确实允许自动缩放到 0,但如果一个请求触发了你的服务,你将被收取最少 10 分钟的计算时间,即使这个请求只花了几分之一秒(【https://cloud.google.com/ai-platform/prediction/pricing】T2)

algorithm ia——真正的无服务器替代方案?

除了主要的供应商, Algorithmia 正在吹捧自己填补了真正无服务器 GPU 模型托管市场的空白。Algorithmia 提供了具有模型版本控制、无服务器基础设施和透明的现收现付选项的 MLOps 管道。与谷歌人工智能平台预测相比,你放弃了选择计算资源的灵活性(你必须相信 Algorithmia,他们为你的服务分配了足够的 CPU、RAM 和 GPU 资源),但作为回报,你只需支付算法的实际计算时间,并获得真正的无服务器体验。

例如,您还可以通过使用常见的 python 代码实现单个函数来完全定制预处理和后处理步骤。一个已部署的算法可以作为一个带有预测端点的 REST-API 来使用,并且可以用一个 API 密钥来保护。实际执行时间是从算法的起始点开始计算,直到它返回。

目前,服务的 CPU 版本收费为 0.0001 美元/秒,GPU 服务收费为 0.0003 美元/秒。付款以 Algorithmia 信用计算,而 10,000 信用相当于 1 美元。每个月你可以免费获得 5000 积分。听起来至少对我的要求来说是完美的匹配。所以我们来看看。

用 Algorithmia 实现一个工作示例

注册后,您可以切换到您的主页部分,它为您提供了当前算法的清晰概述。您可以通过点击“新建”按钮并选择“算法”来添加一个新的。

Algorithmia 主屏幕

给你的算法起一个好听的名字,选择你是想在 GitHub 还是 Algorithmia 上托管代码,指定一个预定义的环境(比如 Python 3.8 + TensorFlow GPU 2.3),你就可以开始了。创建第一个算法后,您可以将新的存储库克隆到您的本地计算机上,或者直接使用 Web IDE。让我们继续使用 Web IDE 进行测试。

Algorithmia Web IDE

这看起来很眼熟。左边是您的项目结构,中间的主要区域显示您的代码。我已经为这篇文章准备了一个简单的例子,你可以从下面复制。

这里实际发生了什么…在一些必要的导入之后,我们首先定义一个函数,它基本上接受一个 base64 字符串作为输入,并返回一个图像对象。接下来,我们从 TensorFlow Hub 加载一个用于图像分类的预训练模型。在我们的例子中,它是一个 Mobilenet V2,被训练来分类 1000 个不同的类。你也可以将自己的模型免费上传到 Algorithmia 数据商店,或者使用 Dropbox 或 S3。由于模型输出是代表图像类别的单个整数,我们必须加载对应于这些整数的相应标签。现在,我们可以实现强制应用功能。

每当客户端向我们的服务发送 POST 请求时,都会调用该函数,并且将主体作为输入提供给该函数。确保在应用函数之外加载模型,以避免每次请求服务时重新加载。在我们的例子中,我们期望一个代表图像的 base64 字符串。由于我们的模型只需要 224×224 像素的图像,我们必须相应地缩放输入,将其转换为预期的 numpy 数组结构,并将所有像素值转换为 0 到 1 之间的范围。最后,我们检查我们是否真的有可用的 GPU,并返回一个带有 GPU 数量和输入图像的预测类别的格式化字符串。

要构建项目,您必须以熟悉的 requirements.txt 格式指定必要的依赖项。因此,单击依赖项并相应地调整条目。

Algorithmia 需求编辑器

现在我们可以开始建造了。单击右上角的绿色构建按钮,等待该过程完成。尤其是第一次做这一步,可能需要一段时间。根据我的经验,构建应该在大约 3-5 分钟后准备好。构建过程完成后,您会在 Web IDE 底部的控制台中看到一个输出,表明您的算法现已联机。您可以通过直接将输入粘贴到控制台来测试该算法。我们在这里跳过这一步,直接部署模型。因此,点击右上角的“发布”按钮,指定一个发布说明和一个你喜欢的示例输入,最后将可调用性指定为“私有”,点击“发布版本 x.x.x”。

要从外部请求您的服务,您现在需要一个 API 密钥。在您的 Algorithmia 主页部分,再次单击“新建”,这次选择“API 密钥”。给你的密钥起个名字,现在选择“完全访问”。例如,以后你可以指定只允许调用特定算法的键。

现在我们准备好出发了。例如,我们编写了一个简单的 python 脚本,它加载一个图像,将其转换为 base64 字符串,并向我们新创建的服务发出请求。为了检查模型的结果,我们使用下面的狮子图片。

罗伯特·蒂曼在 Unsplash上的照片

代码使用了 Algorithmia 包,但是您也可以使用简单的 POST 请求。有关请求服务的可能选项列表,请单击 Algorithmia 主页部分中的算法,向下滚动到“安装和使用”并选择您的首选选项。

输出应该是这样的:Num GPUs: 1 and prediction class: lion

瞧,你有一个可用的 GPU,它实际上是一只狮子!

结论

在写这篇文章的时候,Algorithmia 是唯一一个真正的无服务器平台,可以为 GPU 支持的模型提供服务,你只需要为实际的计算时间付费。因此,Algorithmia 非常适合请求数量相当少(没有恒定负载)的用例。在我的实验中,我注意到在没有请求的特定时间之后,算法必须为下一次调用完全重新加载(冷启动)。这导致了长时间运行的第一个请求。随后的查询又相当快了。这不是 Algorithmia 的无服务器解决方案的特定行为,但仍然应该作为一种限制来提及。对于真实的在线预测用例,这并不理想。如果你需要即时的答案,请记住这一点。在这些情况下,你可能应该考虑 Google AI 平台预测,其中至少有一个节点始终处于准备状态。另一种选择是通过从另一个服务发送定期查询来保持您的服务“温暖”,但是请记住,这增加了成本。否则,Algorithmia 是一个超级易用的无服务器机器学习平台,所以如果你的用例符合,我肯定建议你尝试一下。

基于顶点人工智能的无服务器机器学习管道:介绍

原文:https://towardsdatascience.com/serverless-machine-learning-pipelines-with-vertex-ai-an-introduction-30af8b53188e?source=collection_archive---------2-----------------------

谷歌终于做到了,现在梦想成真了:我们现在能够从以前的人工智能平台运行无服务器的 Kubeflow 管道

照片由米切尔罗在 Unsplash

对于在小公司(或小团队)工作的数据科学家和 ML 工程师来说,有一个大问题:人力资源和时间的极度缺乏。正因为如此,我们能够使用甚至测试的技术范围非常有限。

在 MLOps 方面,范围是一个问题。GCP 上没有任何托管的 MLOps 管道解决方案,采用实验管道的唯一方法是部署一个成熟的 Kubeflow 实例。我们可以依靠人工智能平台管道,进行半管理部署,但 GKE 集群保姆仍然存在问题,这需要时间(而且很无聊)。

Vertex AI 从天而降,用一种管理有序且价格合理的替代方案来解决我们的 MLOps 问题。Vertex AI 附带了所有的 AI 平台经典资源,加上一个 ML 元数据存储,一个完全管理的功能存储和一个完全管理的 Kubeflow Pipelines runner。

在这篇文章中,我们将讨论 ML 管道,Kubeflow 管道,如何创建它们来满足您的自定义 ML 需求,以及如何在 Vertex AI 管道中运行它们并分析它们的结果,并附有一个小的示例代码。

如果你只是为了示例代码而来,那么这里就是了。

快速回顾一下 MLOps

如顶点 AI docs 状态:

机器学习操作( MLOps )是将 DevOps 策略应用于机器学习(ML)系统的实践。DevOps 策略让您高效地构建和发布代码变更,并监控系统以确保您满足可靠性目标。

MLOps 扩展了这一实践,以帮助您减少从数据接收到在生产中部署模型所花费的时间,从而让您能够监控和了解您的 ML 系统。

总之,MLOps 是让您“组织”您的数据科学和 ML 领域以创建可靠系统的实践,以满足您公司的需求。

因此,要应用良好的 MLOps 实践,您需要可靠、易用的工具,以便您可以管理、监控和执行所有数据作业,从接收到建模再到监控。

管道、库伯流和顶点

要应用 MLOps,最重要的步骤之一是实验,因此我们需要一个强大的实验工具。我们希望能够跟踪我们的实验,比较它们,重现它们,并保存所有使用的结果和数据。

Kubeflow 管道来解决这个问题。简而言之,KFP 是一个致力于在 Kubernetes 上运行 ML 工作流(作为模型训练的实验)的工具包,它以一种非常聪明的方式完成它:

与其他方法一样,Kubeflow 让我们将工作流定义为一系列 Python 函数,这些函数相互传递结果和工件。

对于每个 Python 函数,我们可以定义依赖关系(对于所使用的库), Kubeflow 将创建一个容器,以隔离的方式运行每个函数,并将任何想要的对象传递给工作流的下一步。我们可以设置所需的资源(如内存或 GPU),它将为我们的工作流步骤提供这些资源。感觉像魔术一样。

一旦您运行了您的管道,您将能够在一个漂亮的 UI 中看到它,就像这样:

来源:作者打印,运行示例开放源代码

唯一的问题是 Kubeflow 管道必须部署在 Kubernetes 集群上。如果你在一家使用敏感数据的小公司工作,你将面临权限、VPC 以及部署和使用它的许多问题,这使得它有点难以被采用

Vertex AI 通过一个托管管道运行器解决了这个问题:你可以定义一个管道,它将执行它,负责提供所有资源,存储所有你想要的工件,并通过每个想要的步骤传递它们。

我们现在将看到如何使用 sklearn breast_cancer_dataset 定义 Kubeflow 管道并在 Vertex AI 中运行它。

定义管道:带有玩具数据集的定制示例

我们现在用 3 个简单的步骤定义一个管道,在 KFP 称为…

  1. 接收数据并分离训练和测试分割。
  2. 使用步骤 1 中分离的列车,训练出模型。
  3. 使用步骤 2 中的模型和步骤 1 中的测试分割,评估我们的 ROC 曲线和混淆矩阵模型。

首先,让我们安装 Kubeflow Pipelines SDK:

pip3 install kfp --upgrade

你可以在这篇文章的 Github repo 中看到完整的 Jupyter 笔记本。我们将在这里仔细检查每个代码块,使其更容易理解。首先,一些进口:

我们从kfp.v2导入,因为它是新的 Kubeflow Pipelines SDK 版本,与 Vertex AI 兼容。我们引入了代表“特定领域语言”的dsl,因为它是管道定义 SDK 的主要模块。

我们从kfp.v2.dsl导入工件、数据集、输入、模型、输出、度量和分类度量,因为它们是我们在组件之间传递对象的方式。当我们定义一个组件时,我们在参数类型上声明 can state,暗示组件的输入和输出。

这样,它创建了一个具有“path”(用于在组件之间存储和重用对象)和“metadata”(用于存储对象元数据)属性的对象,还创建了一些特定于类型的方法,如 ClassificationMetrics 方法,用于在 UI 上绘制漂亮的 ROC 曲线。

让我们看看它在第一个例子中是如何工作的,这个例子是get_data操作符:

注意,我们没有返回任何值,但是我们将组件的参数定义为dataset_train: Output[Data]。这意味着,在函数上,我们可以访问类数据集的输出对象(将在函数的使用中创建),带有路径和元数据属性。

这样,您可以将数据集保存在特定的路径上,并在需要时在下一个操作符上访问它。调用对象组件后,可以访问它的outputs属性。例如,如果你想访问dataset_train对象,你可以在定义完整管道时使用:ds_train = get_data().outputs["dataset_train"]来完成。

我们还使用了@component装饰器,在这里我们可以定义创建能够运行我们功能的容器所需的包。

对于 train 步骤,我们将使用我们的一个输出对象,并访问之前创建的一个对象。让我们看看这是怎么做到的:

我们所要做的就是定义dataset: Input[Dataset],如果我们在调用组件时传递get_data().outputs["dataset_train"],它将访问dataset_train对象,并通过使用它的path属性下载它。

注意,我们还为我们的模型定义了一些元数据:train_score表示模型在训练时间中的得分,它的framework

这个逻辑在我们的最后一个组件上继续:对训练数据集的模型评估:

我们在这里新做的是传递输出指标和分类指标对象。这将让我们在顶点 AI UI 上为 ROC 曲线和混淆矩阵保存模型分类指标和一些漂亮的交互图。

完成所有工作后,我们只需创建管道并将其编译成一个 json 文件:

请注意,我们可以访问前面步骤的输出。我们把它编译成一个 json 文件,然后可以提交它在 Vertex AI 上运行。确保你在你的 GCP 项目上激活了顶点人工智能,并且你在 GCP 上被正确认证。

检查用户界面

恭喜你。点击 jupyter 上的链接,您将被引导至管道运行。您会看到类似这样的内容:

来源:作者。从我的 GCP 控制台打印。

您还可以检查度量图:

来源:作者,来自我的控制台的美丽打印

很漂亮,对吧?

结论

就是这样。有了 Vertex AI,你将拥有更轻松更美好的生活,拥有所有与 MLOps 相关的事物。在顶点管道中还可以做更多的事情,比如使用 TFx 或其他顶点人工智能工具作为特征库。每一个试图举办盛宴的人都知道我在说什么。

如果你确实喜欢这篇文章,在 Github 上开始回购,在 LinkedIn 上 ping 我,或者做任何你想做的事情。如果你有任何反馈,对帖子的修正,或者只是想谈谈,请联系我们。

我是数据科学家@ Acordo Certo,也是机器学习领域的谷歌开发者专家。我是 MLOps 的拥护者,对 Vertex AI 一见钟情。

以下是一些其他链接:

https://github.com/piEsposito/vertex-ai-tutorials https://github.com/piEsposito https://www.linkedin.com/in/piesposito/

AWS 上的无服务器 ML

原文:https://towardsdatascience.com/serverless-ml-is-this-the-cheapest-way-to-host-machine-learning-models-on-aws-83ba3dee8be4?source=collection_archive---------23-----------------------

这是托管机器学习模型最便宜的方式吗?

影像经跋元素下下许可给菲尔·惠兰

我最近在上制作了一个视频,使用 vast.ai 廉价地构建机器学习模型。这对于像我这样想要在没有预算的情况下构建模型的人来说很有用。使用 vast.ai (我不隶属于他们),我能够构建合理大小的模型,例如基于resnet 152(152 层 CNN)的图像分类模型,只需几美元。我的工作流程是跳到一个 vast.ai GPU 上,比如 RTX 3090,每小时支付 80 美分,做一些训练,做分析,然后跳回到我的笔记本电脑上,进一步制定我的攻击计划。如此反复几次,我就可以走了。

但是去哪里?

理想情况下,您希望将您的模型托管在 GPU 上,以便进行快速推理。但是对于很多型号正常的快速推理可以在合理的 CPU 上实现。即便如此,一个模型可能会占用几千兆字节的内存,你需要把它放在云中的某个地方。你可以获得每小时几美分的低成本云托管服务。但是这些时间累积起来,如果您想要托管多个模型,如果您的工作流看起来像我的工作流,您的模型推理成本可能会大大超过您的模型构建成本。

我没有明显的初始收入流来自我的模型来支付他们的托管,但我想建立一些模型,并托管他们。如果我友好的邻居弗雷德太忙而没有时间去看,那么我就不想支付闲置模特的托管费用。这就是“无服务器”的用武之地。

无服务器

无服务器是良好的不稳定流量模式。“无服务器”不是无服务器。你还在用服务器。你只是以黑盒上下文切换的方式与他人分享它们。您和服务器之间的不透明关系意味着很难针对您的特定用例优化底层服务器。该服务器的不同用户之间的上下文切换意味着,在您不知道的情况下,资源被不断地载入和载出。这些低效率是有代价的,但是你为少得多的使用支付的每使用额外费用意味着每个人都是赢家。

随着利用率的提高,这些经济性对消费者来说显然变得不那么有吸引力了,但是您的云提供商将尝试尽可能地通过缓存和相同的服务器重用来缩小差距。

EC2 vs SageMaker vs Lambda

当我开始寻找在 AWS 上托管机器学习模型的选项时,我发现几乎所有的路都通向亚马逊 SageMaker,但我发现长期托管成本仍然超出了我的预算。从那时起,我开始研究 AWS Lambda 是否能做到这一点。

为了比较成本,最好看看等效的 EC2 实例。AWS Lambda 为您提供 6 个 vCPUs 和高达 10Gb 的 RAM。我能看到的最接近的 EC2 实例是一个 c5.2xlarge ,有 8 个 vCPUs 和 16 GiB RAM。这台机器在 EC2 上是每小时 0.34 美元,在 SageMaker 上是 0.476 美元。我对 SageMaker 还不够熟悉,无法辨别其中的区别,而且我的主机需求也很简单,所以还是 0.34 美元吧。每月 244 美元,每年将近 3000 美元。这将会增加业余爱好者的预算,从而激发我寻找其他选择的欲望。

无服务器成本

https://aws.amazon.com/lambda/pricing/为我们提供了一个计算器,用于确定托管我们模型的成本,即内存(RAM)乘以时间。

我的测试表明,对于我的模型,每个请求需要 720Mb 到 860Mb 的 RAM,这取决于请求包含的图像量。我用 1024Mb 给 20%的开销。

并非所有的请求都花费相同的时间。如果没有人使用这个模型,它就不在 RAM 中。如果它不在 RAM 中,就需要加载 Python 代码,目前大约需要 10 秒钟。

10 秒的冷启动请求时间似乎很长,但如果价格合适,这是可以承受的,也是一个公平的权衡。如果模型变得更受欢迎(这是一个好问题),那么这个启动时间可能会更少。

非冷启动请求电流需要大约 1.5 秒,这也不是很大。但是电脑在表演魔术,你想要什么?!还有一些优化我没试过,比如用 pillow-simd 。

冷启动和非冷启动请求之间的成本差异的大小意味着总成本在很大程度上取决于请求模式。为了简单起见,让我们假设我每天收到 1000 个请求,它们都是冷启动。现在,碰巧的是,这正好在 AWS 的免费等级之内,它将花费我高达 0 美元。但是我们希望这种方法是可持续和可扩展的,因此我们将在计算中忽略自由层。

如你所见,即使我每天有 1000 次点击,我一个月也只花 5 美元。如果达到每天 1,000,000 个请求,那么我们会看到更少的冷启动,持续时间会更接近 1,500 毫秒。这将把它放在每月 756 美元,虽然我可能会在此之前看其他优化。我还会有更多关于内存使用的统计数据来压缩我添加的 20%的 RAM 缓冲区。但总的来说,我会比没有服务器的人活得长,如果我能在回答风投关于我新的热门模型的电话之间找到一些时间,我会把它转移到持久的机器上,进行一些进一步的优化,加入一两个 GPU,然后…你明白了。

寒冷开始升温

为了让 10 秒的 Lambda 函数启动时间变得更容易忍受,我做了两件关键的事情。

第一个是对加载模型的 Lambda 函数的一次性请求,但是没有使用模型。这是我的“醒了!”当你访问模型所在的网页时,我调用它。人类是相当慢的(无意冒犯),当他们弄清楚网页是关于什么的时候,选择一张图片上传或者输入一些文本,你已经进入了 10 秒钟。在发送实际的模型请求时,函数应该已经准备好了。这可能会使请求的数量翻倍,但只会增加我 15%的成本(10 秒+1.5 秒),因为我是根据请求的持续时间付费的。不能保证 AWS 在后台会对这些请求做什么,但是缓存你的函数符合他们的利益,在实践中,这看起来做得足够好了。

第二个“优化”是简单地添加一个可视化的进度条,开始很快,然后越来越慢,直到请求完成,然后跳到结束。这足以分散我们的注意力,确保我们至少有 10 秒钟的时间,直到人们开始怀疑互联网的力量。对于耗时较长的车型,我不太清楚。等待音乐?

Progressbar 正在运行

对等

这种无服务器路线的最大优势显然是成本。这不是 GPT-3 ,但我服务于一个相当大的 152 层 CNN 模型,这是一个有能力的架构。它仍然可以很好地执行推理,而不需要 GPU。现在,除了血汗资本,我什么也没花。

下降趋势

最大的缺点是架构和部署的复杂性。在上找到这篇关于用 AWS Lambda 进行机器学习推理的帖子后,我开始走上了 AWS API Gateway + AWS Lambda + AWS EFS 的道路。

AWS Lambda 是一种无服务器计算服务,按使用付费。然而,像 XGBoost 这样的 ML 框架太大了,不适合 250 MB 的应用程序工件大小限制,或者 512 MB /tmp 空间限制。虽然你可以将包存储在亚马逊 S3 并下载到 Lambda(最大 3 GB),但这可能会增加成本。

为了解决这个问题,Lambda 函数现在可以挂载一个 Amazon 弹性文件系统(EFS)。这是一个可扩展的弹性 NFS 文件系统,在多个可用性区域(AZ)内和之间存储数据,以实现高可用性和耐用性。

此设置要求您将代码部署到 AWS Lambda 和 AWS EFS 卷。因此,您必须确保在两个地方都部署了兼容的代码。

如果您正在使用 joblib 来持久化模型,就像我和上面的帖子所建议的那样,那么在 EFS 卷上特定于版本的 lib/目录中安装了 Python 依赖项之后,您将希望从同一个 EFS 卷中执行该操作。模型再水合时的任何不匹配都将导致停机。

到目前为止,我还没有解决这方面的 CI/CD 复杂性,因为我已经感觉到它正在走一条过于复杂的道路。我想退一步,写这篇博客,思考什么是更好的解决方案。

也许码头工人能帮上忙。这是我接下来要研究的东西。

结论

做 CI/CD 的困难和 Lambda + EFS + API Gateway 的复杂性(API Gateway 非常复杂)使得这很难推荐给其他人。虽然,当我看着我预计的 3 月份 AWS 账单时,它只有 6.72 加元,但很难就其价值进行争论。这包括用于前端的 CloudFront + S3、VPC、Route53 和我用于电子邮件的 se。当然,没有流量是有帮助的。

贾斯汀·平克尼在上发表了一篇文章,在 GCP 上主持卡通。乍一看,这似乎是一个更简单的选择,至少在基础设施方面是如此。冷启动请求延迟似乎更长,但他的模型更大。

我可能会跟进更多关于基础设施如何连接在一起以及我如何使用 Terraform Cloud 和 CircleCI 部署这些基础设施的细节,但如果您对某些特定的东西感兴趣或者想告诉我更好的方法,请告诉我。

如果你对踢轮胎感兴趣,你可以找到我在 https://dogedreams.ai/举办的最新模型

祝你愉快!

使用 Docker、AWS Lambda 和 API 网关的无服务器模型托管

原文:https://towardsdatascience.com/serverless-model-hosting-with-docker-aws-lambda-and-api-gateway-2d79382ecb45?source=collection_archive---------6-----------------------

充分利用 Lambda 中的 Docker 支持来托管您的模型,而无需专用服务器。

由 Markus Spiske 在 Unsplash 上拍摄的图片

以前,AWS Lambda 部署包被限制在 250MB(包括需求)的最大解压缩大小。当试图使用该服务托管机器学习模型时,这被证明是一个障碍,因为常见的 ML 库和复杂的模型导致部署包远远超过 250MB 的限制。

然而在 2020 年 12 月,AWS 宣布支持将 Lambda 函数打包和部署为 Docker 镜像。在机器学习的背景下,这些图像的大小最大可达 10GB。这意味着图像中可以包含大型依赖关系(例如张量流)和中等大小的模型,因此可以使用 Lambda 进行模型预测。

在本文中,我们将通过一个示例来构建和部署 Lambda 上的模型托管。所有使用的相关代码都可以在这里找到。

建筑概述

该解决方案可以分为三个部分:

  1. Docker 映像:Docker 映像包含我们的依赖项、训练好的模型管道和功能代码。AWS 为各种运行时提供了一个基本映像,可以在其上构建以确保与服务的兼容性。
  2. Lambda Function :基于输入事件/请求运行 Docker 映像中的功能代码的无服务器资源。
  3. API 网关端点:用作 Lambda 函数的触发器和客户端请求的入口点。当在端点接收到预测请求时,Lambda 函数被触发,请求体包含在发送给该函数的事件中。然后,该函数返回的值将作为响应返回给客户端。

型号

在这个例子中,我们的模型将是在虹膜分类数据集上训练的简单 KNN 实现。本文不涉及培训,但结果是一个 scikit-learn Pipeline 对象,由以下对象组成:

1.StandardScaler:根据训练样本的平均值和标准偏差标准化输入。

2.KNeighborsClassifier:实际的预训练模型。用 K = 5 训练。

使用 scikit-learn 的 Joblib 实现将管道保存到' model_pipeline.joblib '中。

功能代码

让我们首先考虑 Lambda 将用来处理预测请求事件的函数代码(predict\app.py)。

Lambda_handler 拥有 Lambda 使用的函数所需的参数。管道对象在处理程序之外加载,以避免在每次调用时加载。Lambda 会让容器在一段时间内保持活动状态,直到没有事件发生,所以在创建时加载一次模型意味着它可以在容器被 Lambda 保持活动状态时被重用。

处理函数本身非常简单;从事件主体中提取所需的输入,并用于生成预测。预测作为 JSON 响应的一部分返回。API 网关会将函数的响应返回给客户端。进行一些检查以确保输入符合预期,并捕捉和记录任何预测错误。

码头工人图像

Dockerfile 文件的结构如下:

1.拉 AWS 的基本 Python 3.6 图像

2.将所需文件从本地目录复制到映像的根目录

3.安装要求

4.运行处理函数

部署

为了管理部署和 AWS 资源,我们将使用 AWS 无服务器应用程序管理器(SAM) CLI 安装 SAM 及其依赖项的说明可以在这里找到。

要使用 SAM 进行构建和部署,必须配置模板文件。这用于指定所需的 AWS 资源和相关配置。

对于此项目,SAM 模板包含以下内容:

  • 杂项信息,如堆栈名称和 Lambda 超时的全局配置。
  • MLPredictionFunction 资源:这是我们要部署的 Lambda 函数。本节包含所需配置的大部分内容:
  • 属性:这里我们指定将使用 Docker 映像( PackageType: Image )来定义该函数,并且该函数将通过 API 网关( Type: API )来触发。API 路径路由名称和类型也在这里定义。
  • 元数据包含将用于构建图像的标签和用于构建图像的 docker 文件的位置/名称。
  • Outputs 列出了将由 SAM 创建的所有必需资源。在这种情况下,SAM 需要的是 API 网关端点、Lambda 函数和关联的 IAM 角色。

运行以下命令,使用定义的 SAM 模板在本地构建应用程序映像:

!sam build

如果成功,可以使用以下命令通过示例事件在本地调用该函数(参见 repo 中的示例事件):

!sam local invoke -e events/event.json

一旦在本地测试了该功能,就需要将图像推送到 AWS ECR。首先创建一个新的存储库:

!aws ecr create-repository --repository-name ml-deploy-sam

在推送映像之前,您需要登录 ECR 的托管 Docker 服务:

!aws ecr get-login-password --region <region> | docker login --username AWS \ --password-stdin <account id>.dkr.ecr.<region>.amazonaws.com

现在,您可以使用以下工具部署应用程序:

!sam deploy -g

这将在“引导”模式下运行部署,您需要确认应用程序的名称、AWS 区域和之前创建的图像存储库。在大多数情况下,接受其余设置的默认选项应该没问题。

然后将开始部署过程,并提供 AWS 资源。完成后,每个资源都将显示在控制台中。

考虑

  • 映像更新:要部署更新的模型或功能代码,您可以简单地在本地重建映像并重新运行 deploy 命令。SAM 将检测应用程序的哪些方面发生了变化,并相应地更新相关资源。
  • 冷启动:每次 Lambda 使用我们的功能代码旋转一个容器时,模型将在处理开始前被加载。这导致冷启动场景,其中第一个请求将比后面的请求慢得多。解决这个问题的一个方法是使用 CloudWatch 定期触发函数,这样容器就可以随时加载模型。
  • 多个功能:可以部署多个 Lambda 功能,由一个 API 提供服务。如果您有多个模型要服务,或者如果您想要有一个单独的预处理/验证端点,这可能是有用的。要对此进行配置,您只需在 SAM 模板资源中包含附加功能。

亚马逊 SageMaker 上基于拥抱脸的变形金刚模型的无服务器 NLP 推理

原文:https://towardsdatascience.com/serverless-nlp-inference-on-amazon-sagemaker-with-transformer-models-from-hugging-face-4843609a7451?source=collection_archive---------12-----------------------

无服务器部署您的 NLP 模型。没有基础设施管理。现收现付!

Krzysztof Kowalik 在 Unsplash 上的照片

这是怎么回事?

At re:Invent 2021 AWS 推出亚马逊 SageMaker 无服务器推理,让我们可以轻松部署机器学习模型进行推理,而无需配置或管理底层基础设施。这是我与客户打交道时最常要求的特性之一,在自然语言处理(NLP)领域尤其如此。在这篇博文中,我将展示如何使用 Amazon SageMaker 无服务器推理来部署 NLP 模型。

您可以在这个 Github repo 中找到这篇博文的附带代码示例。

为什么这很重要?

在 NLP 中,许多工作负载都有间歇性或不可预测的流量。例如,想想你选择的智能家居设备:它大部分时间都处于闲置状态,直到你要求它执行一项任务。如果我们以传统方式部署底层 NLP 模型,我们将不得不管理底层基础设施。更糟糕的是,我们将不得不为这一基础设施买单——即使它 99.9%的时间都处于闲置状态。

Amazon SageMaker 无服务器推理通过根据推理请求的数量自动扩展计算能力来帮助解决这些类型的用例,而无需您预先预测流量需求或管理扩展策略。此外,您只需为运行推理代码的计算时间(以毫秒计费)和处理的数据量付费,这使它成为具有间歇性流量的工作负载的一个经济高效的选项。

行动(或活动、袭击)计划

我们将在 SageMaker 上训练一个拥抱脸模型,然后使用新的 SageMaker 无服务器推理功能部署它。注意这个功能目前在预览版中,也就是说不支持某些地区和某些功能。更多信息请访问本网站。然而,这并不意味着你必须注册一个测试程序或类似的程序。只要您部署在受支持的地区,就可以使用现成的新功能。

我用 SageMaker Studio 运行这个笔记本。从实例大小来说,ml.t3.medium (2 个 vCPU + 4 个 GiB)足够运行笔记本了。确保您安装了最新的 SageMaker 软件包(截至 2021 年 12 月 9 日,该软件包为 2.70.0 版):

作者图片

模特培训

这篇博文不会深入探讨模型训练,有很多其他的博文会涉及到它。我们将使用一个取自这本笔记本的非常标准的例子:训练一个二元情感分类器。该模型将接收带有正面或负面情绪的文本,并尝试对它们进行相应的分类。

模型部署

一旦模型被训练,这就是乐趣所在。预览版中这个特性的一个限制是我们还不能使用 Python SDK 来部署模型。这意味着这个漂亮的小方法目前还不可用:

相反,我们可以使用 Boto3 库来建立我们自己的部署过程。这比仅仅使用 deploy()方法要复杂一点,但也不会复杂太多。它要求我们自己设置端点配置。这是我们指定要使用新的无服务器配置的地方:

设置好端点配置后,我们可以创建端点:

这将需要几分钟时间,一旦终端部署完毕,我们就可以对其进行测试:

作者图片

结论

这又快又简单。我们已经使用 SageMaker 中的 Huggingface 集成训练了一个 NLP 模型。一旦模型被训练好,我们就把它部署到一个无服务器的推理端点,现在可以直接使用模型,而不必管理基础设施或在模型未被使用时付费。

这篇博文向您简要介绍了如何建立无服务器 NLP 推理,但是当然还有更多问题需要研究:延迟是什么样子的?延迟如何依赖于模型的大小或我们提供的输入?如何优化这种设置?冷启动问题怎么办?我很高兴在接下来的几周里更多地使用这个新功能,并分享我的学习成果!

GCP 上的无服务器云运行

原文:https://towardsdatascience.com/serverless-on-gcp-with-cloud-run-b3dab7a60e63?source=collection_archive---------22-----------------------

构建在 Knative 之上的平台,是服务和事件架构的理想选择

无服务器已经讨论了很长时间了,而且还会继续下去。将基础设施管理从开发人员手中转移出去,意味着他们将时间和精力投入到构建和改进产品上,并更快地交付给市场。

[图片由Nathana rebou as在 Unsplash 上拍摄]

Cloud Run 允许您在完全托管的无服务器平台上运行容器化的应用程序。在这篇博客中,我将带您浏览以下内容—
T5 a .背景
b .部署您的应用程序的步骤
c .展示服务和事件的示例应用程序概述

那我们开始吧!

a .背景

Cloud Run 建立在 Knative 之上,kna tive 是一个无服务器框架,旨在提供所需的构建模块,使无服务器应用在 Kubernetes 上成为现实而不一定是一个成熟的解决方案。如果你用过 k8s,甚至是 k8s 的托管版,你就知道这需要一些(阅读大量!)是时候掌握窍门了。正如您将在本博客中讨论的示例应用程序中看到的,云运行为您简化了这种管理。

有兴趣更全面地了解何时应该使用 cloud run?前往本博客。
Ps:如果你是学生或打算在某个地方托管作品集的人,我真的会推荐试试 Cloud Run。👩🏻‍💻

b.3 简单(神奇?)步骤

[作者图片]

  1. 构建—打包您的应用程序 我在这里使用的示例应用程序是 Github 上的。您可以为初学者克隆 repo,并试验源代码。
    现在假设您已经准备好了您的应用程序代码,请注意其中有一个Dockerfile。我们将使用这个文件创建一个容器映像,并将它存储在
    容器注册表中。当需要自动缩放时,我们的云运行服务将使用该映像。在幕后有很多工作要做,但是对你来说,这一步只意味着cd到你的源代码文件夹,在那里你有你的Dockerfile,运行这个命令——
    gcloud builds submit --tag gcr.io/project-id/image-name . ,你将被提示回答是否允许你的服务的 HTTP 端点的公共调用。请做出相应的回答,当然这也是您稍后可以通过云运行控制台编辑的内容。推荐:在这里 可以了解这个命令及其参数 。该文档还提供了一种全面的方式来理解在“构建”步骤中发生了什么。
  2. 部署 要部署您的应用程序,只需运行— gcloud run deploy 这一步需要一些时间,当您的服务从映像中部署完成且版本开始提供流量服务时,您将能够看到进度。如果部署成功,最终结果将显示部署的修订版提供的流量百分比,以及可以用来访问服务的 HTTP url。不成功的部署将返回一个错误,并带有指向日志的 url。这些日志可用于调试部署中的问题。
    如果您希望从一个已经存在的容器映像进行部署,使用这个命令— gcloud run deploy <service-name> --image <image-url>
  3. 访问服务
    成功部署后,您可以使用最后一个命令输出的服务 url 或在控制台上找到它来访问您的服务。
    curl <service_url> -H “Authorization: Bearer $(gcloud auth print-identity-token)" 这个身份令牌包含用户信息,在不允许公开调用服务时是必需的。

每个云运行服务都有一个 HTTP 端点,甚至可以通过控制台本身映射到一个域。[作者图片]

c.概述—服务和活动

对于大多数人来说,无服务器意味着功能即服务。有时问题是,当我们可以使用“云功能”时,这是 GCP 联邦航空局直接为事件架构提供的,那么为什么要运行云呢?

对我来说,这是—

  • 抽象为容器,可以跨平台移动,几乎不需要修改代码
  • 能够使用任何语言/运行时二进制文件
  • 可以选择尝试 k8s 部署提供的高级功能,如流量分流、金丝雀部署等,而不必增加像 Istio 这样的服务网格。

您可以直接从 UI 或 gcloud 命令行(如果您喜欢终端)完成这些任务。

我所说的在云上“服务”是指您的服务在您公开的特定PORT处监听请求。另一方面,“事件化”意味着只有当一个动作发生时(事件),云运行服务才会被触发。我们将使用发布/订阅作为“活动”和“服务”之间的连接媒介。发布/订阅是我们将定义“当 X 动作发生时,触发 Y 服务”的地方

现在来看看我们的示例应用程序,如果您有兴趣亲自体验云运行,您可以派生出这个回购并按照自述文件中的步骤继续。在 GCP 文档中明确定义了允许发布/订阅能够触发内部服务所需的身份验证、IAM 角色和权限。请看看这些。

概述—架构图

服务—处理 web 流量

在我们的示例应用程序中,服务是通过“GET”请求完成的,该请求调用与您部署的服务相关联的端点。当您到达端点时,会创建一个服务实例并提供网页。如果在某个时间段(超时时间段)内向该端点发出另一个请求,则同一个实例继续为新请求提供服务。基本上,云运行部署可以处理多达 80 个并发请求。如果您的服务在 60 秒内没有收到任何请求,实例就会被终止。

事件—处理异步请求

在许多组织中,一个常见的用例是 web-hook 根据某个用户的动作触发服务。我在我们的示例中创建了一个简单的 web-hook(我们都知道,web-hook 基本上是 POST 请求!)并且还部署了一个服务(姑且称之为service_1)。

这个网络挂钩将由我们手动触发,就像用户在网站上做一些动作一样。该事件将向发布/订阅topic发送一条消息。该消息由该topicsubscription接收,其端点在push模式中被定义为service_1部署的 url。

该图显示了如何在发布/订阅中为每个订阅设置端点—在我们的示例中,它是我们之前部署的 service _ 1[作者提供的图片—来自 GCP 控制台的屏幕截图]

我们在这篇博客中部署的示例应用程序需要 pub/sub 集成,因此您需要启用这些 API,并需要遵循集成 pub/sub 的步骤,但是如果您正在寻找一些准备好部署的无状态应用程序,您可以从这个 GitHub 存储库中选择并部署任何服务,并使用它的 url 作为要触发的端点。

步骤—构建、部署、访问端点!

[作者图片]

希望您了解了一些关于云运行的可能性。如果您对该部署过程有任何疑问,请随时发表评论,或者如果您在应用程序代码方面遇到任何问题,请在 repo 中创建一个问题。

使用 gRPC 服务 ML 模型

原文:https://towardsdatascience.com/serving-ml-models-with-grpc-2116cf8374dd?source=collection_archive---------13-----------------------

跳过休息,给 gRPC 一个尝试

照片由米卡·鲍梅斯特在 Unsplash 上拍摄

大多数希望将新训练的 ML 模型投入生产的人转向 REST APIs。这就是我认为您应该考虑使用 gRPC 的原因。

等等!休息怎么了!?

没什么!REST APIs 的主要好处是它们的普遍性。每一种主要的编程语言都有一种制作 HTTP 客户端和服务器的方法。并且有几个现有的框架可以用 REST APIs 包装 ML 模型(例如 BentoML、TF Serving 等)。但是,如果您的用例不适合这些工具中的任何一个(即使它适合),您可能会发现自己想要编写一些更加定制的东西。同样,使 REST APIs 变得通用的东西也会使它们难以使用。

gRPC 是什么?

正如它的网站所说的,gRPC 是“一个高性能、开源的通用 RPC 框架”,最初由 Google 开发。gRPC 核心的三个主要元素是:代码生成、HTTP/2 和协议缓冲区。

协议缓冲区是一种二进制结构化数据格式,由 Google 设计,体积小,速度快。gRPC 服务及其请求/响应消息格式都在.protobuf文件中定义。

gRPC 客户机和服务器代码是从.protobuf定义文件中以您喜欢的语言生成的。然后,您填充业务逻辑来实现 API。

基于 HTTP/2 的传输为 gRPC 提供了几个关键优势:

  • 二元协议
  • 双向流
  • 标题压缩
  • 在同一连接上多路复用多个请求

好吧,但这对我意味着什么?

为什么使用 gRPC?

我们从几个角度来比较 gRPC 和 REST。

类型安全和文件

因为 gRPC APIs 是通过 protobufs 定义的,所以它们本身是文档化的和类型安全的。相反,REST APIs 没有这样的保证,你需要像 OpenAPI 这样的额外工具来定义和记录你的服务,还需要一个库来验证客户端请求。

速度、二进制数据和流

gRPC 充分利用 HTTP/2 和协议缓冲区,使您的 API 尽可能快。与 REST 的纯文本、JSON 编码的消息相比,gRPC 消息由高效打包的二进制数据组成。

一个经常被引用的测试显示 gRPC 大约比 REST 快 7-10 倍。虽然对于较小的请求,差异可能不太明显,但 ML 模型的输入通常很大(例如,大型数据表、要处理的图像,甚至是视频),其中压缩和二进制格式表现突出。

事实上,由于协议缓冲区允许二进制数据,请求可以是用 Apache Arrow 或 Apache Parquet 编码的大型数据表。此外,由于 HTTP/2 的功能,大的二进制消息可以分解成块并进行流式传输。

缺点和替代方案

gRPC 当然不是完美的。例如,以下是您可能会遇到的一些问题:

  • 初期发育较慢
  • 不常用
  • 消息不是人类可读的,这使得调试更加困难
  • 需要生成客户端库

其他方法可能更适合您的工作流程。 BentoML 、 TF 发球和 Ray 发球都是为 ML 模特发球的绝佳选择。或者,如果你正在寻找一些更加可定制的东西, FastAPI 和 Flask 是两个很好的选择,可能是更好的搭配。

另外,对于部分方法,您也可以考虑将您的消息格式从 JSON 交换到 BSON 或 MessagePack 。

结论/TL;速度三角形定位法(dead reckoning)

gRPC APIs 快速且易于使用。它们本质上是类型安全的,它们允许双向流消息(例如,将大文件分成块),并且它们使用快速有效的消息格式(协议缓冲区)。

下次您需要通过 API 提供 ML 模型时,请考虑使用 gRPC!

进一步阅读

  • gRPC ( gRPC Python 库)
  • 协议缓冲区
  • HTTP/2

脚注

[1]我知道“RESTful”这个术语可能有点太宽泛了——它适用于任何基于 HTTP 的 API。在本文中,我使用 REST API 的通俗定义。

[2]这有点过于简化了——gRPC 是高度可定制的。例如,可以用 JSON 代替 protobufs,用 HTTP/1 代替 HTTP/2。但是…你应该吗?

感谢阅读!我很想听听你对这篇文章的看法。你用 gRPC 服务 ML 模型吗?还是为了别的?或者你更喜欢休息?请在评论中告诉我,或者随时联系 Twitter 或 LinkedIn !

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/72608.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

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

TowardsDataScience 博客中文翻译 2021(七十二)原文:TowardsDataScience Blog 协议:CC BY-NC-SA 4.0图片中的概率公理原文:https://towardsdatascience.com/probability-axioms-in-pictures-2de96880c868?source=collection_archive---------32-----------------------通…

116、SAP导出表结构并保存到Excel,方便写代码时复制粘贴

01. 在SE38模块,创建一个程序 02.ABAP代码如下:*&---------------------------------------------------------------------* *& Report Z_TIANPAN_20190716_HELLO *&---------------------------------------------------------------------* *& *&----…

『玩转Streamlit』--环境配置

尽管Streamlit的使用非常直观,但正确的环境配置对于充分发挥其潜力仍然至关重要。 本篇将介绍如何从头开始配置Streamlit环境,以及Streamlit开发过程中常用的几个命令。 最后通过一个简单的示例演示开发Streamlit应用的过程。 1. 安装 Streamlit是纯Python的框架,只依赖Pyth…

DTL698电表数据 转 profinet IO协议项目案例

VFBOX协议转换网关支持PLC,modbus,EthernetIP,Profinet,CCLink,EtherCAT,IEC61850,IEC104,bacnet,DLT645,HJ212,opc ua,opc da,DNP3。目录 1 案例说明 1 2 VFBOX网关工作原理 1 3 准备工作 2 4 配置VFBOX网关 2 5 用PROFINET IO协议转发数据 4 6 其他说明 6 7 案例…

win11网络修改篡改bug自动修改dns没有网络问题

1.问题描述 没有网络,dns一直是自动获取的,但是在重启或连接vpp后,会被设置成某个固定值 正常情况:莫名其妙的 篡改: 2.解决办法 1.首先,删除所有网络的手动dns配置,控制中心那个dns管理没有用,在设置中删除网络,不然问题还会出现 - 2.然后,进入注册表\HKEY_LOCAL_MACHI…

sqlsever 将字符串格式转换成日期格式

SELECT CONVERT(DATETIME, 20241009, 112) AS ConvertedDate;SELECT CAST(20241009 AS DATETIME) AS ConvertedDate;

为什么同一个Camera有两个RenderSingleCamera的耗时

1)为什么同一个Camera有两个RenderSingleCamera的耗时2)Unity 2022中SBP打包报错问题排查3)Application.lowMemory在什么时候会生效4)一般iOS机型要超过其运行内存的多少会容易崩溃这是第404篇UWA技术知识分享的推送,精选了UWA社区的热门话题,涵盖了UWA问答、社区帖子等技…