如何处理规则重叠?
入门
尤其是当规则的预测相互矛盾时。
当一个人想要建立一个可解释的模型时,他通常会在两个主要的内在可解释算法家族之间犹豫不决:基于树的算法和基于规则的算法。它们之间的主要区别之一是,基于树的算法生成具有不相交规则的树,这具有诸如复制子树问题的缺点[1],而基于规则的算法生成规则集并允许重叠规则,这可能是一个问题。事实上,当两个(或更多)规则同时被激活时,您如何做出决定呢?当规则的预测相互矛盾时,这就更成问题了。
在这篇短文中,我介绍了从一组规则中获得单个预测的两种方法,这两种方法比平均预测更重要,但保留了预测的可解释性。
使用专家汇总方法
如果您不熟悉专家的聚合理论,我建议您阅读我以前的文章“如何选择最佳模型?”。这个想法很简单:把每一个规则看作是专门的专家,通过一个凸组合来聚集他们的预测。与规则相关的权重在每次预测后都会改变:如果专家的预测是好的,则权重会增加,否则会减少。因此,权重可以解释为我们在时间 t 对每个专家的信任度。
不幸的是,规则可能没有被激活。这意味着新的观察不满足他们的条件。他们被称为“睡眠专家”。问题是我如何评估一个没有被激活的规则的可信度?常见的方法有两种。首先是说“没有信息我不会做决定”。这样,休眠规则的权重保持不变,直到它再次激活。二是问“这个规则是睡觉好吗?”。以这种方式,通过考虑集合预测而不是规则的预测来评估休眠规则的新权重。如果聚集预测是好的,则意味着该规则一直是正确的,否则意味着该规则应该一直是活动的。这两种方法都有问题。但是已经证明它们提供了相似的结果。
使用分区技巧
划分是一个非常不同的想法,更“统计”。其思路是将规则集形成的覆盖变成分隔单元格的分割(如下图所示)。因此,预测将是单元中观察值的平均值,而解释将是规则的结合。
图片来自作者。
有人可能会反对建造这种隔墙非常费时。而且是正确的。所以,为了解决这个问题,有一个分区技巧。其思想是要理解,计算预测值不需要完全描述分区。诀窍是识别包含新观察值 x 的分区的唯一单元。通过创建二进制向量,如果新的观察值 x 满足规则的条件,则其值为 1,否则为 0,对包含 x 的单元的识别是简单的向量运算序列。因此,计算预测的复杂度是 O(nR) ,,其中 n 是训练集中的点数,而 R 是规则数。下图是这个过程的图解(更多细节我参考[2])。
图片来自作者。
这种方法的主要问题是我们无法控制细胞的大小。换句话说,如果包含新观察值的单元格太小,您可能会根据太少的过去观察值做出决定。如果训练集中的数据很少,就会出现此问题。可以解释为情况不明。
结论
我已经介绍了两种方法,即使您的过程基于重叠的规则,也可以做出决定。这两种方法各有利弊。第一种基于专家聚集理论,在实践中运行良好,并为规则增加了一个置信度得分。第二种,基于划分技巧,理论上更好。如[3]中所述,这是生成回归函数的可解释一致估计量的好方法。
参考
[1] G.Bagallo 和 D.Haussler,经验学习中的布尔特征发现 (1990),载于《机器学习》,5(1):71–99。斯普林格。
[2] V.Margot,J-P. Baudry,F. Guilloux 和 O. Wintenberger,规则归纳划分估计器 (2018),模式识别中的机器学习和数据挖掘国际会议 288–301。斯普林格。
[3] V.Margot,J-P. Baudry,F. Guilloux 和 O. Wintenberger,使用数据相关覆盖的一致回归(2021 年),载于《电子统计杂志》。数理统计研究所和伯努利学会。
关于我们
Advestis 是一家欧洲合同研究组织(CRO ),对统计学和可解释的机器学习技术有着深刻的理解和实践。Advestis 的专长包括复杂系统的建模和时间现象的预测分析。
领英:【https://www.linkedin.com/company/advestis/】T4
如何在 VS 代码中调试 Flask 应用程序
如何使用 flask 创建一个基本的 web 服务,然后在 VS 代码集成开发环境中单步调试并完全调试代码
由 Unsplash 上的 Clément Hélardot 拍摄
介绍
Flask 应用是使用 flask 库用 Python 编程语言开发的 web 服务应用。
它们是快速开发任何编程语言(包括 Python、C#)都可以使用的 web 服务的好方法。NET、Java 等。但是直到最近,我还不知道如何在 VS 代码中调试,这样我就可以一步一步地调试 flask 应用程序代码。
本文展示了一个简单的分步指南,它将使您能够完成设置和配置,从而轻松调试 flask 应用程序。
步骤 1:开发一个基本的烧瓶应用程序
第一步是使用 flask 创建一个基本的 web 服务。有许多在线教程向您展示如何做到这一点,包括我发现特别有用的这个教程——https://programminghistorian . org/en/lessons/creating-APIs-with-python-and-flask。
出于本教程的目的,我们将使用基本应用程序来探索如何调试 flask 应用程序如下-
您应该在 VS 代码环境中创建 flask 应用程序,并且应该创建一个。py 文件,并将其命名为app.py
。
有一些方法可以修改配置,为您的代码文件使用不同的名称app.py
,但是因为您在 flask web app 中只允许使用一个 Python 文件(在 flask web app 中,您可以创建任意多的 web 服务),所以更改它似乎没有多大意义。
如果到目前为止您一直在学习,那么您的 VS 代码环境和 Python 代码应该是这样的
作者图片
步骤 2:创建并配置一个 launch.json 配置文件
如果您单击 ide 左侧的“运行和调试”图标,或者键入 Ctrl+Shift+D,您将看到“运行和调试”窗口。
现在点击“创建 launch.json 文件”链接,当提示“选择调试配置”时,选择“Python 文件调试当前活动的 Python 文件”。
作者图片
VS 代码现在会自动为您创建一个 launch.json 配置文件,该文件预先填充了一些基本设置。
首先要做的是删除注释,因为这些注释会在稍后执行 flask 应用程序时导致错误/警告-
作者图片
下一步是最关键的。当 VS 代码创建 launch.json 文件和相关的配置时,它不包括必要的配置来告诉环境和 web 服务器在哪里可以找到 conda 环境,这必须手动添加,否则调试将永远无法在 VS 代码中工作。
- 在你的电脑上找一找你的 Anaconda 文件夹,其中包含一个名为
_conda.exe
的文件 - 在 launch.json 中为名为
"python.condaPath"
的参数创建一个新的配置行,其值指向正确的路径
完成的 launch.json 文件应该如下所示
作者图片
注意:在 Windows 环境中有三种方式可以启动 VS 代码
- 转到 Windows 开始菜单,启动 VS 代码
- 启动 Anaconda 环境并点击 VS 代码下的“Launch”
- 启动 Anaconda 命令提示符并键入“code”
只有在使用第一种方法时,才需要在 launch.json 中添加一行来设置"python.condaPath"
。
如果您使用方法 2 或 3,这将指示 VS 代码环境使用基本 conda 环境,因此不需要额外的配置。
步骤 3:启动调试器
下一步是回到代码文件中,在编辑器的空白处单击,在代码中创建一些断点。在下面的例子中,我点击了第 33 行和第 39 行-
作者图片
现在回到“运行和调试”窗口,点击“Python: Current File”旁边的绿色箭头,开始调试会话
作者图片
当您启动调试器时,可能会收到“Exception has occure:system exit message ”,但这并不重要,不会阻止您调试代码。
只需点击调试工具栏中的蓝色“播放”按钮,调试会话将继续-
作者图片
如果一切正常,你在终端窗口得到的最后一条消息将是-
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
步骤 4:调用 Web 服务
您现在需要打开一个 web 浏览器,在 URL 中键入http://127.0.0.1:5000/
,以调用正在调试器和 VS 代码 ide 中运行的 web 服务
作者图片
一旦按下return
键,您将看到以下内容-
- 浏览器调用已经调用了 web 服务
- 在 VS 代码中,web 服务已被调用,并在调试器中您添加断点的地方暂停
- 现在,您可以使用 VS 代码调试工具栏来调试代码
作者图片
当您单步执行代码并退出 web 服务函数时,web 服务的输出将返回给调用该服务的 web 浏览器
作者图片
如果您想要调用返回所有数据科学书籍的第二个服务,请返回到 web 浏览器并修改 URL,如下所示
[http://127.0.0.1:5000/api/v1/resources/books/all](http://127.0.0.1:5000/api/v1/resources/books/all)
现在将在调试器中调用和暂停第二个 web 服务。一旦你完成了代码并执行了函数,代表数据科学手册库的 json 将作为输出返回给浏览器
作者图片
结论
第一次用 VS 代码设置 flask web 应用程序的调试有点笨拙,尤其是当你不知道把最重要的"python.condaPath": "C:\\Users\\GHarrison\\Anaconda3"
行添加到launch.json
文件中的时候。
然而,对于 web 服务来说,一旦完成一次,它将只适用于所有后续会话,并且一旦这些 web 服务超出这里给出的基本示例的规模,成为真实世界的复杂 web 服务,能够单步调试 flask web 应用程序的能力就变得至关重要。
感谢您的阅读!
如果你喜欢读这篇文章,为什么不看看我在https://grahamharrison-86487.medium.com/的其他文章呢?此外,我很乐意听到您对这篇文章、我的任何其他文章或任何与数据科学和数据分析相关的内容的看法。
如果你想联系我讨论这些话题,请在 LinkedIn 上找我—https://www.linkedin.com/in/grahamharrison1或者发邮件给我ghar rison @ Lincoln college . AC . uk。
如果你想通过订阅来支持作者和全世界 1000 个为文章写作做出贡献的人,请使用这个链接——https://grahamharrison-86487.medium.com/membership(注意:如果你使用这个链接注册,作者将收到一定比例的费用)。
如何用 TensorBoard 调试你的深度学习管道
关于如何提高深度学习实验吞吐量的互动示例。
使用 TensorBoard 对 10 个平假名字符类进行 UMAP 分割(图片由作者提供)
当我们开发深度学习模型时,我们经常会在管道中遇到各种各样的问题。这包括我们处理数据的方式的问题,将数据传递到我们的模型的方式的错误,甚至在评估阶段的错误。数据科学家面临的主要挑战之一是使这些数据(尽可能)透明,以确保使用工具箱中的正确工具(预处理策略、模型层、评估标准等)。).然而,为了实现这一点,我们经常不得不在试错的基础上做一系列实验,包括显示定制图和跟踪变量。这就是 TensorBoard 来提升我们工作流程的地方!
tensor board 到底是什么?
TensorBoard 是一款专为深度学习管道打造的高级分析工具,可处理各种数据源(图像、视频、音频、结构化数据等)。).它允许我们通过自动化的可视化绘图来调试我们的管道。它还跟踪我们实验的历史,以便我们可以运行多个版本,并观察哪个模型配置对性能有最好的影响。这对于超参数调整特别有用。另一个我非常喜欢的功能是能够将我们的模型嵌入到 2D/3D 空间中(通过聚类方法),并观察每个单独数据点之间的相关性。
在本教程中,我将向您展示一个标准的图像分类示例,它使用了包含日本平假名字符集的K-MNIST(Kuzushiji-MNIST)数据集。我们将使用 PyTorch 来构建和评估我们的模型,并将其发送到 TensorBoard 进行分析,只需在我们的代码中添加简单的 2-3 行代码!
数据
km NIST-10 行平假名候选类(图片由 Clanuwat 等人提供,古典建筑深度学习,2018)
K-MNIST 是一组日本 Kuzushiji 人物,基于经典 MNIST 。除了在这种情况下,目标不是数字,而是代表一组平假名字符(上例中的每一行)。每一行的第一个例子是汉字的现代平假名,而其余的是用 Kuzushiji - 风格书写的,这是 150 多年前在旧日本手稿和书籍中使用的。这个数据集是如何创建的背后的故事真的很有趣,因为它通常允许以这种风格编写的旧日本文学作品被翻译成现代汉字并用于教育系统。你可以在这里阅读更多关于 Clanuwat 等人的工作。
我们将在中使用这个数据集来构建一个用于字符识别的基本 PyTorch ConvNet (别担心,你不需要真正懂日语,我也不需要)。那么,让我们开始吧:)。
依赖关系
此示例是在 Python 3.7 环境中制作的,包括以下软件包的最新版本:
- 火炬和火炬视觉——建模
- 标准绘图、计算包— matplotlib、seaborn、numpy(应已安装在 Conda 上)
- 注意:如果你已经在你的环境中安装了tensor flow*,你可以使用这个工具清除任何与文件处理相关的 TensorBoard 错误。*
这里还有其他的进口货:
基本配置
让我们也添加几个默认变量,以便稍后使用。这些包括字符的类名、一些超参数、路径和图像的大小。
初始化张量板
这只需一个调用就可以完成,这个调用将生成一个 runs/k-mnist 目录,我们的数据将由 TensorBoard API 在其中编码。
读取数据
我们可以使用 PyTorch DataLoader 类轻松加载我们的 K-MNIST 数据用于训练和验证。我们还可以指定加载后要执行的自定义转换。在这种情况下,我们将把图像转换成 PyTorch 张量,并归一化图像通道(使用与处理 MNIST 所用参数相同的参数)。
想象一些例子
在我们真正进入 TensorBoard 的自动化部分之前,让我们先来看看如何分析一些自定义图像。对于这个例子,我们将通过从 ubyte 格式加载它们作为 numpy 数组来查看原始字符。接下来,我们将创建两个简单的函数:
- 一个用于显示前 25 个字符及其类别索引的 5x5 网格
- 一个在单独的图像中显示 25 个字符中的每一个
我们将把这两个函数的输出记录到 TensorBoard 中。对于网格,我们将暂时将 matplotlib 图形保存到缓冲区,然后使用 PIL 将其转换为张量,然后将其传递给 TensorBoard。我们这样做是因为张量板只处理张量输入。为了绘制单个图像,我们可以简单地将 numpy 数组转换为张量,并以不同的全局步长传递每个图像。
现在,为了查看记录的输出,我们需要在一个单独的终端中启动 TensorBoard API ,并在本地运行时托管它。我们可以通过切换到我们的项目文件夹并运行下面的代码来实现。张量板输出将位于http://localhost:6006。
tensorboard --logdir=runs/k-mnist --samples_per_plugin "images=100, text=100"
这里, logdir 是我们在初始化 SummaryWriter 时创建的目录。注: *--samples_per_plugin*
允许我们为特定的张量板模块增加可跳过的样本数量。在我们的例子中,在图像和文本方面,我们超过了这个限制,增加它给了我们完整的输出集。
托管 TensorBoard 后,我们将看到以下页面:
TensorBoard 图片(注意:在此阶段,您将只能看到当前选项卡)—作者图片
在 TensorBoard 中可视化图像数据允许您交互式调整对比度和亮度,以及滚动多个示例,这对于观察您的数据非常有用。
创建数据加载器
为了使模型训练更加节省内存,我们需要使用批处理。这在 PyTorch 中通过**数据加载器很容易完成。**简单指定批量大小以及是否打乱训练/val 集合。
创建一个简单的 CNN 模型并记录模型摘要
接下来,我们将创建一个基线 3 层 CNN 模型来对平假名字符进行分类(这将主要基于官方 K-MNIST repo 上提议的网络)。这将由两个 CONV 池辍学组合和一个全连接层组成,以将输出减少到 10 维,逻辑代表每个类。像往常一样,我们将使用交叉熵函数计算损失,并设置一个标准优化器。为了将图形嵌入添加到 TensorBoard 中,我们需要抓取一批特征和标签,这样输入的形状也可以保存。
TensorBoard 图:跟踪模型结构(图片由作者提供)
这有助于我们交互式地追踪模型结构,从输入形状开始,一直到预测。每个箭头还描述了每个单元的形状,这对于识别模型中的形状不匹配非常有用。这是一个简单的例子,但是对于更大、更深层次的架构来说,这些图节省了大量的调试时间。
训练预测器
在我们开始训练之前,我们应该确保我们正在我们的 GPU 设备上训练(如果可用的话)。这可以通过设置活动 PyTorch 设备来实现:
为了使代码更具可读性和可复制性,让我们构建一个包含一些输入参数和辅助函数的kmnistpoirder类。我们将设置目标历元数、批量大小、损失函数和 TensorBoard writer 对象。 loss_batch 函数将允许我们计算交叉熵损失,反推并更新单个批次的权重。 get_correct_examples 函数是一个简单的一行程序,用于获取单个批次中正确样本的数量,这将帮助我们计算精确度**。除了,**我们还需要使用 Softmax 函数计算每个类别的概率,因此我们可以使用 get_probs 在一个步骤中提取预测的类别和概率。注意:to(dev)确保我们将批处理发送到正确的设备,否则我们将得到内存分配错误。 最后,我们的 fit() 函数将由两个阶段组成:
- 训练模式:遍历每一批,对历元中的批损失、正确和总样本进行求和。这将不断更新模型权重。之后,只需将它们打印出来,并将列车损失和精度记录为张量板标量。
- 评估模式:遍历每个验证批次,并对历元中的损失、正确和总实例进行求和(不要指定优化器,此步骤的权重应被冻结)。然后类似地,将验证损失和精度记录为张量板标量。
我们还将添加一个 save()和 load() 方法,以便保留模型状态供将来使用。现在,我们可以通过运行三行代码来训练和保存我们的模型:
现在,我们可以自动跟踪模型性能,而不必进行任何自定义绘图。我们跟踪的指标将记录在标量选项卡上,在这里我们可以观察每个时间步长的损失/准确性。这允许我们看到当前配置的潜在问题(例如,在什么点上我们的模型开始过拟合)。
张量板标量:在训练期间跟踪模型性能(图片由作者提供)
在这个例子中,我们可以看到我们的模型收敛得相当好,因为我们达到了 95%的验证准确度,并且我们的验证损失曲线变得平滑。但是,我们仍然可以做进一步的分析,以确保我们的模型是稳定的。
精确召回曲线
为了可视化每个平假名字符的每类性能,我们可以很容易地使用 TensorBoard 的 精确召回曲线模块。首先,我们加载验证数据,并提取每个批次的预测类别和概率。然后,我们将它们堆叠在一个列表中,并将输出记录到 TensorBoard。遍历每个类,我们需要传递一个布尔数组,指定哪个输出对应于相应的类和概率集(也是一个递增的全局步骤,这样我们就不会覆盖任何一个类)。
在 prcurves 选项卡中,我们可以观察到每个平假名字符的精度-召回权衡。您将看到,随着模型在所有类上都取得了良好的性能,所有的斜率都严重向右偏移。如果我们突出显示右上区域,我们可以放大并看到每个类的真阳性/阴性和假阳性/阴性的实际比率。
TensorBoard PR 曲线:获得每类性能(图片由作者提供)
地块分类错误
类似于我们之前显示的图像网格,我们也可以绘制一个自定义的错误网格,以便观察哪些字符被错误分类。我们只需要再次加载验证数据,获得预测的类并将它们附加到一个列表中,分别保存真实/预测的类(仅当它们不同时),以及实际的图像。然后我们可以用第一个 25 错误分类的例子创建一个网格,和以前一样。同时,我们还可以添加一些文本数据来记录到 TensorBoard 中,比如每个示例的预测与实际输出。我们将添加图像索引,以便将该信息与显示的网格相关联。
TensorBoard:显示分类错误(图片由作者提供)
我们将在图像选项卡上看到我们的错误网格,并在文本选项卡上看到我们为每个示例记录的类。
超参数调谐
我们还可以使用 hparams 轻松利用 TensorBoard 来调整我们的 K-MNIST 预测器的超参数。首先,我们需要修改我们的培训类来支持这一点,增加几行代码:
我们将首先添加学习率、优化器名称、批量大小(调优目标),以及 TensorBoard writer 对象。然后,在我们的 fit() 函数的末尾,我们将简单地添加这些用于监控的参数:
- 第一个参数:超参数字典(名称:值)
- 第二个参数:用于监控的性能指标字典(名称:值)
在这种情况下,我们将记录纪元验证准确性/损失。这将生成一个性能表,突出显示每个超参数组合的这些值的变化。现在,让我们定义一个搜索网格和一个整体的 optimize() 函数来装饰我们的训练管道。这将迭代每个学习率/批量大小/优化器组合,并生成总共 27 个模型组合(在 3x3 网格上)。这可能需要一些时间来计算(在我的例子中大约需要 30 分钟),所以请随意减少参数的数量。注意:由于批量大小和学习率的选择会导致不稳定的配置,某些模型组合的性能会非常差。对于较大的学习率,模型更容易在 局部极小值 处初始化,由于步长过大,无法从训练数据中获得牵引力。这是一个例子,其中调整你的 学习率/批量 是必要的。
TensorBoard hparams:交互式调整您的模型(图片由作者提供)
在表视图中,我们可以观察每个超参数配置的性能指标。在我们的例子中,获得的最佳性能是我们最初使用的(Adam,批量:32,学习率:0.001),达到大约 95%的验证准确性。我们还可以在平行坐标视图中更好地可视化这一点,在这里我们可以更改指标的比例,并在超参数网格中跟踪每个模型的性能。
特征检查/降维
TensorBoard 支持的另一个非常酷的功能是 2D/3D 空间中的特征投影*。这允许您应用聚类方法,如 PCA 或 T-SNE,以便可视化个体组之间的相似性和某些类别特征内的识别模式。最棒的是,它是互动的,所以你可以实时观察。*
让我们看一个简单的例子。我们将从验证数据(图像和标签)中每批随机挑选 16 个示例,将它们在一个维度上堆叠在一起,并创建一个 TensorBoard 嵌入来可视化这组示例。图像将在两个维度上被挤压(批量维度和宽度高度通道)。这将构建计算相似性的特征向量。为了可视化,原始 PyTorch 图像张量将被传递到 label_img 并且元数据将是平假名字符的真实类。
投影仪选项卡最初将显示我们随机批次的 PCA。这是一种降维*方法,将我们的输入特征压缩到 3 个可观察的维度中。然后,您可以按类别标签给图着色,以便区分每个字符类别。你也可以使用 T-SNE 和 UMAP 等方法来运行其他模拟。在这里,您可以随意尝试不同的方法、参数和采样策略,看看分布是如何变化的。*
TensorBoard 投影仪:在 2D/3D 空间中可视化您的特征(图片由作者提供)
注意:如果投影仪选项卡没有出现,请尝试从命令行重新运行 TensorBoard 并刷新浏览器。
用 TensorBoard 完成工作后,也要经常用 writer.close() 关闭自己的 writer,从内存中释放*。***
最后的想法
TensorBoard 通常是一个很好的分析工具,可以帮助调试你的深度学习管道,无论是在数据层面还是模型层面。我可以肯定地说,它节省了您自己创建定制图和跟踪性能指标的大量时间。因此,这是一个很好的工具,因为您可以生成和测试更多的实验/模型配置,并立即跟踪您的管道中哪些有效,哪些无效,以及哪些需要修复。
希望你喜欢这个教程:)。距离我上次写已经有一段时间了,但是我很想在将来写更多,所以如果你对代码或博客中提到的信息有任何建议/问题,请告诉我。
这个库(本质上只是一个 Jupyter 笔记本)可以在 GitHub 上本地克隆。
参考文献
- 在 TensorBoard (Tensorflow)中显示图像摘要
- 用张量板测井
- 另一个伟大的张量板概述(PyTorch)
- 带 PyTorch 的官方张量板教程
- K-MNIST 数据集
- Torchvision 数据集文档(用于创建数据加载器)
如何使用谷歌地图的 API 决定晚餐分组
随着新加坡从新冠肺炎疫情中恢复过来,新加坡政府已经开始允许人们八人小组聚会。我的教会团体(37 人)想一起吃晚饭。因此,我决定用 R 写一些代码,让我们尽可能以最好的方式组织晚餐!(为保护隐私,使用了假名)
分类要求
- 人们呆在新加坡各地
- 有 5 个晚餐地点
- 人们在世界不同地区进行研究。
- 我们需要先根据地区然后根据晚餐地点对人们进行分类。
- 每组最多 5 人,每组必须至少有 2 人来自同一地区。
这看起来是一个有趣的挑战!
测量便利性
考虑到这些需求,我不得不开始考虑如何实现这一点。为了确保人们去最方便的地方,我们可以使用的最明显的度量标准是从他们的房子到晚餐地点的旅行时间。为了找到答案,我使用了mapsapi
,它允许我们访问谷歌地图 API 套件中的方向 API。下面是我如何使用它的一个例子!(注:MRT 代表大众捷运是新加坡的地铁)
一旦我有了旅行时间,我所要做的就是按不同地点的旅行时间降序排列(同时按研究区域分组),这将允许我将它们归入不同的组。
查找旅行时间
以下是(假)数据的样子:
所以我们有了这些人的名字,他们的研究区域和离他们家最近的地铁站。我创建了 5 个额外的空列(用来存储到 5 个地点的旅行时间),然后运行一个循环来使用mp_directions
填充它。
问题是……它以字符串的形式返回旅行时间,返回类似“1 小时 15 分钟”和“55 分钟”的结果(正如你在上面的例子中看到的)。因此,我需要做些事情将它们转换成数值,这样我们就可以对它们进行排序。我使用 if/else 和stringr
完成了这个任务
我对所有 4 个位置都这样做了,将它包装在一个更大的 for 循环中,以考虑所有不同的位置。完成后,这就是数据帧的样子!
排序和更多排序
有了旅行时间,我们现在要找到离一个人最近的位置所要做的就是找到最小值。为了做到这一点,我使用了apply
,逐一检查每一行,并对它们进行排序,以找到最低值,然后是第二低的值等等。我还使用了names()
,以便将它存储为地点的名称。
最后,我把它按地区分组,然后把它们从第一到第五选择排列起来。
组的分配
分类工作完成后,剩下要做的就是找出人与人之间的分配。我决定一次做一个地区,因为它们必须按地区分类。
首先,我决定使用count
快速浏览一下分配情况。
从数据来看,璧山显然是大多数人最集中的地方。然而,只有一组人能到场。在整理过程中,我得到的指示是尽我所能让大多数人都方便。不幸的是,有限的地点意味着一些人将不得不去 T4 旅游。
我决定优先考虑旅行时间,但也要确保每个晚餐地点至少有两个人来自同一个地区。我根据最近的位置,第二个最近的位置进行过滤,然后从每个区域中切掉前三个名字。
我不会用剩下的排序来烦你,但我基本上检查了每个位置并把它们切片,确保每个人都被分配到一个组。
也就这样了!这不是一个非常困难的问题,但如果手动完成,可能会很乏味。使用 Google Maps API 可以让人们更容易找到地点之间的距离,然后相应地对它们进行排序。
原载于http://zachlim98.github.io/me。
如何为您的机器学习模型确定完美的距离度量
曾经怀疑过哪种距离度量对您的用例最有利吗?
塔内利·拉蒂宁在 Unsplash 上拍摄的照片
声明:你不需要每个 ML 模型都有一个距离度量,但是如果你需要的话,请继续读下去,选出最好的一个。
距离度量在机器学习和深度学习中起着重要的作用。像 k-NN,K 表示聚类,深度学习中使用的损失函数等机器学习算法都依赖于这些度量。
因此,了解不同类型的距离度量对于决定何时使用哪种度量非常重要。例如,k-NN 经常使用欧几里德距离进行学习。但是,如果数据是高维的呢?欧氏距离还会有价值吗?不,不会的,因为我们知道,欧几里德距离对于高维空间来说并不是一个好的度量(参见这个链接了解更多信息)。所以我想你现在可以理解,知道你的距离度量可以帮助你从一个差的分类器到一个精确的模型。
在这篇博客中,我们将介绍一些最常用的距离度量及其用例和缺点,以及如何在 python 中实现它们。我们将讨论的问题如下:
- 欧几里得距离
- 曼哈顿距离
- 切比雪夫距离
- 闵可夫斯基距离
- 汉娩距
- 余弦相似性
- 雅克卡相似性
- 索伦森-戴斯指数
欧几里得距离
欧几里德距离是最常用的距离度量之一。从数学上来说,它是两个不同数据点之间的差异之和的平方根。下面是计算两个 k 维向量之间距离的公式。
作者图片
应用/优点:
- 高度直观、易于理解、易于使用的指标
- 它应该用在 KNN 或 K 均值等算法中,在这些算法中,我们有低维数据,数据点之间的直线距离足以衡量这些点的相似性。
缺点:
- 它会受到输入要素比例的影响,这意味着根据每个要素的单位,计算的距离可能会有偏差。因此,标准化输入特征至关重要。
- 对高维数据无效/无用
python 中计算欧几里德距离的函数:
from math import sqrtdef euclidean_distance(a, b):
return sqrt(sum((e1-e2)**2 for e1, e2 in zip(a,b)))#ORfrom scipy.spatial.distance import euclidean
dist = euclidean(row1, row2)
print(dist)
曼哈顿距离
作者图片
它通常被称为出租车距离或城市街区距离。想象你在一个城市街区,有两组平行的道路,水平的和垂直的,如图所示。所以,如果你想从一个点旅行到另一个点,你只能以直角移动。
然后计算两点之间的距离代表曼哈顿距离。
作者图片
应用/优点:
- 当数据集中存在离散/二进制属性时,曼哈顿距离度量更有效,因为它考虑了可从给定属性值中实际获取的路径。
缺点:
- 对于数据集中的浮动属性,它并不代表最佳距离。
- 对于高维数据,它比欧几里德距离更好,但在性能方面仍不是最佳选择。
python 中计算曼哈顿距离的函数:
def manhattan_distance(a, b):
return sum(abs(e1-e2) for e1, e2 in zip(a,b))#ORfrom scipy.spatial.distance import cityblock
dist = cityblock(row1, row2)
print(dist)
切比雪夫距离
切比雪夫距离被定义为所有坐标维度中两个向量之间的最大差值。换句话说,它就是沿每个轴的最大距离
作者图片
应用/优点:
- 这一指标通常用于物流问题。例如,计算车辆从一个地方到另一个地方所需的最小步数,假设车辆在一个网格中运动,因此只有八个可能的方向(上、右上、右、右下、下、左下、左、左上)
缺点:
- 它只能用于特定的问题。它不能像欧几里得那样适用于任何通用问题。
python 中计算切比雪夫距离的函数:
def chebyshev_distance(a, b):
return max(abs(e1-e2) for e1, e2 in zip(a,b))#ORfrom scipy.spatial.distance import chebyshev
dist = chebyshev(row1, row2)
print(dist)
闵可夫斯基距离
Minkowski 概括了上面讨论的所有距离度量,如欧几里德、曼哈顿和切比雪夫。它也被称为 p-范数向量,因为它添加了一个称为“p”的参数,允许计算不同的距离度量。公式是:
作者图片
对于 p=1 —曼哈顿距离
对于 p=2 —欧几里德距离
对于 p =无穷大—切比雪夫距离
由于这是一个更通用的距离度量,不同的 p 值将导致不同的利弊。我们已经在上面讨论了其中的一些。
python 中计算闵可夫斯基距离的函数:
def minkowski_distance(a, b, p):
return sum(abs(e1-e2)**p for e1, e2 in zip(a,b))**(1/p)#ORfrom scipy.spatial import minkowski_distance
dist = minkowski_distance(row1, row2)
print(dist)
汉娩距
这是迄今为止最简单的一个。它等于两个数据点之间不同的值的数量。假设两个数据点 x 和 y 如下:
x = [1,2,3,0,2,4]
y = [1,3,3,0,1,4]
那么汉明距离= 2 至于 index(假设 index 从 0 开始)1 和 4 在 x 和 y 上的值是不同的,它通常用于计算两个二进制字符串之间的距离。
优势/优点:
- 当数据从一台计算机发送到另一台计算机时,它通常用于错误检测。
缺点:
- 因为它用于通过比较每个值来找出两个数据点之间的差异,所以它们必须具有相等的长度。
- 它不包含数据点的实际值/幅度。因此,当特征量起重要作用时,不建议使用。
python 中计算汉明距离的函数:
def hamming_distance(a, b):
return sum(abs(e1 - e2) for e1, e2 in zip(a, b)) / len(a)#ORfrom scipy.spatial.distance import hamming
dist = hamming(row1, row2)
print(dist)
余弦相似性
它也是最常用的距离度量之一。它用于通过计算两个向量/数据点之间的余弦角来找出它们之间的相似性。
作者图片
为了更好地理解,如果两个向量重合或方向相同,那么它们之间的余弦相似度将为 1,如果它们完全相反,则为-1。
优势/优点:
- 适用于高维数据,应该在理想情况下用于这些数据。
- 它被用来寻找许多事物之间的相似性,如文本嵌入、图片嵌入或指纹嵌入。
缺点:
- 类似于汉明距离,在余弦相似性中不考虑向量的大小;只考虑他们的方向。
python 中计算余弦相似性的函数:
from numpy import dot
from numpy.linalg import normdef cosine_similarity(a,b):
return dot(a, b)/(norm(a)*norm(b))#ORfrom scipy.spatial.distance import cosine
dist = 1 - cosine(row1, row2)
print(dist)
雅克卡相似性
Jaccard 相似度用于计算相似度,它强调两个有限样本集之间的相似性,而不是向量之间的相似性。它被定义为集合的交集的大小,除以这些集合的并集的大小。
作者图片
例如,如果两个集合有两个共同的实体,并且总共有 7 个唯一的实体,那么相似性将是 2/7。
优势/优点:
- Jaccard 相似性被用作评估深度学习中的图像分割模型的度量,其中模型必须对图像的每个像素进行分类。它被用来计算与地面事实相比,我们分割所需实体的准确程度。它可以类似地用于其他应用。
缺点:
- Jaccard 的相似性高度依赖于数据的大小。大型数据集会显著影响相似性,因为在这种情况下,当交集保持较低时,并集可能会大幅增加。
python 中计算 Jaccard 相似性的函数:
import numpy as npdef jaccard_similarity(x,y):
intersection = np.logical_and(x, y)
union = np.logical_or(x, y)
similarity = intersection.sum() / float(union.sum())
return similarity#ORfrom sklearn.metrics import jaccard_scoresimilarity = jaccard_score(a, b)
print(similarity)
索伦森-戴斯指数
索伦森-戴斯指数与雅克卡相似性非常相似。索伦森-戴斯指数更直观一些,因为它可以被视为两个集合之间的重叠百分比,该值介于 0 和 1 之间:
作者图片
s rensen-Dice 指数也用作图像分割的度量。
结论
在这篇博客中,我们讨论了实践中广泛使用的各种距离度量。每种度量在某些情况下是有利的,而在另一些情况下是不利的。希望这篇博客能帮助你决定哪种度量更适合你的用例。
关注我们的 medium 了解更多此类内容。
成为 介质会员 解锁并阅读介质上的许多其他故事。
如何在 Altair 中整理条形图
数据可视化
关于如何通过流行的 Python 数据库提高条形图可读性的一些技巧和提示
最近我读了一本非常有趣的书,作者是 Jose Berengueres,书名为数据可视化简介&讲故事:数据科学家指南。在本书中,作者描述了许多从数据集中提取一个非常有趣的故事的技术。受这本书的启发,在本文中,我描述了一个在 Altair 中整理条形图并从表示的数据中提取一个故事的策略。
Altair 是一个流行的用于数据可视化的 Python 库。还有许多其他用于数据可视化的 Python 库,比如 Matplotlib 和 Seaborn。在本教程中,我将描述 Altair,留待将来分析其他数据 Viz 库时使用😃
在本教程中,我将演示如何在 Altair 中构建一个条形图,以及如何对其进行整理。
整理图表意味着删除所有不必要的东西,这些东西会分散读者获取正确信息的注意力。
作为一个用例,我利用了欧盟统计局提供的活产和粗出生率数据集。该数据集包含从 2009 年到 2020 年每个欧洲国家的活产婴儿数量。
目标是通过整理一个基本条形图,从数据中提取一些有意义的信息,重点是上一期(2019–2020)。
目录:
- 加载并清理数据集
- 条形图基础
- 数据聚合
- 最终清理
1 加载并清理数据集
我下载了。XSLX 文件和我通过read_excel()
函数加载的熊猫数据帧:
import pandas as pddf = pd.read_excel('../sources/eu_live_births.xlsx', header=7, sheet_name="Sheet 1", na_values=':')
df.head(10)
作者图片
数据集非常脏,因此我通过只选择感兴趣的列来清理它。
df = df[['TIME', '2019', '2020']]
df.head()
作者图片
我将TIME
列重命名为Country
df.rename(columns={'TIME' : 'Country'}, inplace=True)
然后我删除列 2020 和 2019 的NaN
值
df.dropna(subset=['2020'],inplace=True)
df.dropna(subset=['2019'],inplace=True)
我删除了前六行,它们是相对于整个欧洲的:
df = df.iloc[6:]
2 建立一个基本的条形图
现在我用 2020 年的数据构建了一个原始条形图:
import altair as alt
bars = alt.Chart(df).mark_bar().encode(
x=alt.X('2020:Q'),
y=alt.Y('Country:N'),
)
作者图片
条形图很难阅读,因此我通过sort='-x'
参数订购 DESC 棒材(使用x
订购 ASC)。
bars = alt.Chart(df).mark_bar().encode(
x=alt.X('2020:Q'),
y=alt.Y('Country:N', sort='-x'),
)
作者图片
截断到前 10 个国家是对该条形图的唯一改进。然而,截断数据并不是理解现象的最佳解决方案。因此,我遵循另一种策略,即把国家聚合到宏观区域(或地区)。
3 数据汇总
我的原始数据集不包含任何聚合信息,因此我应该增加一个新列,包含每个国家的宏观区域。
我利用了从这个网站中提取的另一个数据集,它包含了每个国家及其地区和人口。
我加载数据集:
df_regions = pd.read_csv('../sources/eu_regions.csv', sep=';')
作者图片
我定义了一个函数,它接收一个国家作为输入,并返回它的子区域。该函数还管理异常。
def get_region(x):
south_exceptions = ['Cyprus', 'Turkey']
east_exceptions = ['Armenia', 'Azerbaijan']
if x in south_exceptions:
return 'Southern Europe'
if x in east_exceptions:
return 'Eastern Europe'
row = df_regions[df_regions['Country'] == x]
return row['Subregion'].iloc[0]
现在我丰富了我的原始数据集:
df['Region'] = df['Country'].apply(lambda x: get_region(x))
作者图片
现在,我构建聚合条形图。我利用transform_aggregate()
函数来计算每个区域的平均值。我要感谢Soner y ldr RM,他让我在题为 3 个例子来展示 Python Altair 不仅仅是一个数据可视化库的文章中发现了transform_aggregate()
函数。
bars = alt.Chart(df).mark_bar().encode(
x=alt.X('avg_value:Q'),
y=alt.Y('Region:N')
).**transform_aggregate**(
avg_value = 'average(2020)', groupby = ['Region']
)
作者图片
在比较数量时,绝对值不是最佳解决方案。因此,我计算了 2020 年相对于 2019 年的活产减少/增加百分比:
import mathdf['2020_2019'] = -(df['2020'] - df['2019'])/df['2019']
我再次绘制条形图:
bars = alt.Chart(df).mark_bar().encode(
x=alt.X('avg_value:Q'),
y=alt.Y('Region:N')
).transform_aggregate(
avg_value = 'average(2020_2019)', groupby = ['Region']
)
作者图片
图表很清楚:东欧经历了最大的百分比下降。这一方面可能是要讲的故事。
4 最终清理
现在,故事已经从数据中提取出来,我可以进一步整理图表。首先,我设置了图形标题:
bars = alt.Chart(df).mark_bar().encode(
x=alt.X('avg_value:Q'),
y=alt.Y('Region:N')
).transform_aggregate(
avg_value = 'average(2020_2019)', groupby = ['Region']
)**.properties(
height = 300
)**
作者图片
然后,我把标签贴在栅栏附近:
bars = alt.Chart(df).mark_bar().encode(
x=alt.X('avg_value:Q'),
y=alt.Y('Region:N')
).transform_aggregate(
avg_value = 'average(2020_2019)', groupby = ['Region']
)**text = bars.mark_text(
align='left',
baseline='middle',
dx=3,
fontStyle='bold',
fontSize=20
).encode(
text=alt.Text('avg_value:Q', format=",.3f")**
)final_bar = (bars + text).properties(
height = 300
)
作者图片
我移除轴:
bars = alt.Chart(df).mark_bar().encode(
x=alt.X('avg_value:Q', **axis=None**),
y=alt.Y('Region:N', **title=''**)
).transform_aggregate(
avg_value = 'average(2020_2019)', groupby = ['Region']
)text = bars.mark_text(
align='left',
baseline='middle',
dx=3,
fontStyle='bold',
fontSize=20
).encode(
text=alt.Text('avg_value:Q', format=",.3f")
)final_bar = (bars + text).properties(
height = 300
)
final_bar
作者图片
我加上标题:
bars = alt.Chart(df).mark_bar().encode(
x=alt.X('avg_value:Q', axis=None),
y=alt.Y('Region:N', title='')
).transform_aggregate(
avg_value = 'average(2020_2019)', groupby = ['Region']
)text = bars.mark_text(
align='left',
baseline='middle',
dx=3,
fontStyle='bold',
fontSize=20
).encode(
text=alt.Text('avg_value:Q', format=",.3f"),
)final_bar = (bars + text).properties(
height = 300,
**title = 'Percentage Decrease in Live Births (2020-2019)'**
).**configure_title(
fontSize=24
)**
作者图片
最后,我将重点介绍一个地区(南欧)的情况:
bars = alt.Chart(df).mark_bar().encode(
x=alt.X('avg_value:Q', axis=None),
y=alt.Y('Region:N', title=''),
**color=alt.condition(alt.datum.Region == 'Southern Europe', alt.value('#8B0000'), alt.value('grey'))**
).transform_aggregate(
avg_value = 'average(2020_2019)', groupby = ['Region']
)text = bars.mark_text(
align='left',
baseline='middle',
dx=3,
fontStyle='bold',
fontSize=20
).encode(
text=alt.Text('avg_value:Q', format=",.3f"),
)final_bar = (bars + text).properties(
height = 300,
title = 'Percentage Decrease in Live Births (2020-2019)'
).configure_title(
fontSize=24
)
作者图片
摘要
在本文中,我举例说明了一个在 Altair 中整理条形图并使其可读性更好的实例。清理需要以下操作:
- 为初步数据探索构建一个基本条形图
- 聚合数据,如果聚合数据不可用,则搜索另一个数据集
- 绘制聚合数据并提取故事
- 通过删除轴、添加标题和突出显示文章,执行最终的整理。
如果你已经走到这一步来阅读,对我来说,今天已经有很多了。谢谢!你可以在本文中读到更多关于我的信息。
您可以从我的 Github 资源库下载本教程的完整代码。
相关文章
如何减少数字通信的碳足迹
蜜琪拉·威登霍夫在 Unsplash 上拍摄的照片
利用系统动力学评估电子邮件行为对温室气体排放的影响
介绍
如今,通过数字工具交流是非常正常的。我们有与朋友聊天的社交媒体平台,进行工作面试的视频电话服务,当然,还有传统的电子邮件。有人会认为通过电子邮件发送信息是使用实体信件的“绿色替代方案”。当然,我们会考虑从 A 地到 B 地运输过程中的二氧化碳排放,我们也知道造纸所需的砍伐树木对环境的负面影响。那么,就气候中立信息共享而言,电子邮件是圣杯吗?本文将通过回顾电子邮件的碳足迹,讨论电子邮件数据结构的重要性,并对电子邮件行为如何影响二氧化碳排放进行实验,来发现这个问题。这篇文章的新颖之处在于,我们将量化电子邮件行为对碳足迹的影响。为此,我们将应用模拟技术系统动力学。
电子邮件的碳足迹
电子邮件背景
首先,让我们简单地还原一下一封邮件到底是什么,它来自哪里。基本上,电子邮件是一种从发送者向接收者传输文本和其他数字数据的互联网服务。电子邮件应用程序由邮件服务器和面向客户端的邮件程序组成,前者负责指导邮件的传递和归档,后者用于创建、发送和阅读邮件。此外,电子邮件应用程序需要网络访问,并使用 SMTP、IMAP 或 pop 3【1】等传输协议。第一封电子邮件是在 1971 年发出的——比万维网的诞生早了将近 20 年。这种新信息技术的发明者是雷·汤姆林森,他当时在因特网的前身“阿帕网”上工作。在此之前,只能在最初发送电子邮件的同一台设备上阅读电子邮件。所以真正的成就是通过使用 FTP 协议经由网络的设备支配的消息传递。此外,他还引入了“@”符号来称呼信息接收者。
电子邮件导致的温室气体排放
已经有很多关于电子邮件中二氧化碳排放的文章,它们经常提到同一个来源:“香蕉有多糟糕?”:万物的碳足迹”,发表于 2010 年,作者是碳足迹研究者、万维网发明者蒂姆·伯纳斯·李的兄弟迈克·伯纳斯-李。该研究对电子邮件进行了分类,并对每封邮件的平均排放量进行了如下描述:
1)垃圾邮件:0.3 克二氧化碳
2)普通邮件:4g 二氧化碳
3)带附件的邮件:50g CO2
这些二氧化碳成本主要是由运行电子邮件设备所需的能源引起的,这些设备为网络基础设施供电,必须访问网络基础设施以传输电子邮件,以及存储内容的数据中心【3】。根据 Berners-Lee 的数据,我们还可以推断电子邮件的大小会影响二氧化碳的排放量:电子邮件越大,排放的二氧化碳就越多。2021 年,我们在全球每天收发的电子邮件数量约为 3200 亿封,预计这一数字还会进一步增加【4】。虽然垃圾邮件看起来不太受气候影响,但我们需要考虑与普通邮件相比垃圾邮件的数量。下面我们重点来揭晓几个国家每日发送邮件【6】的比例:
1)美国:96 亿封邮件(总量),86 亿封邮件(垃圾邮件)。
2)德国:90 亿封电子邮件(总量),79 亿封电子邮件(垃圾邮件)
3)中国:87 亿封电子邮件(总量),85 亿封电子邮件(垃圾邮件)
4)英国:86 亿封电子邮件(总量),77 亿封电子邮件(垃圾邮件)
5)俄罗斯联邦:84 亿封电子邮件(总计),81 亿封电子邮件(垃圾邮件)
由于这些国家和其他几个国家一样,是全球电子邮件统计的领导者,我们可以计算加权平均值来表示全球电子邮件流量中垃圾邮件的百分比:92%。进一步的研究得出的结果是,大约 24%的商务邮件有附件。
有了这些数据,让我们来推断一下全球电子邮件的年二氧化碳排放量(我们接受四舍五入):
- 每年发送的电子邮件数量= 3200 亿 x 365 天= 117.8 万亿
- 每年发送的垃圾邮件数量= 117.8 万亿* 0.92 = 108.4 万亿
- 每年发送的常规电子邮件数量= 117.8 万亿* 0.08 * 0.76 = 7.2 万亿
- 每年发送的带附件的电子邮件数量= 117.8 万亿* 0.08 * 0.24 = 2.3 万亿
使用这些结果,我们现在可以计算每种电子邮件类型的年度二氧化碳排放量,如下表所示:
每种电子邮件类型的电子邮件数量和二氧化碳排放量(图片由作者提供)
这导致二氧化碳总排放量约为 1.763 亿吨。相当庞大的数字——为了让它更有形,我们可以将其转化为 2120 万个家庭一年的能源消耗,甚至 21 万亿部智能手机充电;或者说得更具体一点:相当于汽车【8】行驶了 4423 亿英里,这甚至比全德承认的所有汽车一年的行驶里程还要多(3767 亿英里【9】)。然而,如果我们将其与 2020 年全球化石燃料的二氧化碳排放量(近 350 亿吨【10】)相比较,我们的电子邮件仅占温室气体排放量的 0.5%。尽管如此,我们可以清楚地声明,电子邮件并不像许多人预期的那样对气候没有影响。
大型电子邮件的驱动因素
在我们评估减少二氧化碳排放的手段之前,我们希望了解电子邮件的数据结构以及它通常包含哪些类型的信息。基本上,一封电子邮件由三部分组成:信封、标题和正文。信封封装了消息,并包含发送电子邮件所需的所有信息。典型的信息是目的地地址、电子邮件优先级及其安全级别。标头由与消息传输相关的字段组成,如“收件人”、“抄送”或“发件人”。每个头字段指的是指定字段名、冒号和值的一行 ASCII 文本。消息的主体部分包含发送者感兴趣的实际内容。它通常由文本、签名和附件【11】组成。考虑到这些因素,我们可以暗示正文是邮件大小的主要驱动因素。所以让我们仔细看看尸体。
电子邮件的数据格式
影响体型的不仅仅是纯粹的核心信息量。在专业邮件客户端应用程序中,您可以选择正文的格式,这也会影响大小,如下图所示(来自 MS Outlook 的截图)。
MS Outlook 中的格式选项(图片由作者提供)
为了评估数据格式对电子邮件大小的影响,我创建了一个样本邮件,其中包含称呼、问候、一些“lorem ipsum”内容和我当前公司的签名。然后,我改变了一些文字格式,例如粗体和彩色。最后,我将邮件作为文件保存到我的电脑上,但不是一次而是多次以不同的格式保存。首先,我使用了“HTML”格式,这需要 39 KB。然后,我使用了“富文本”格式,需要 66.5 KB。最后,我使用了“纯文本”格式,这需要 19.5 KB。因此,我们可以得出结论,我们有三种不同的消息大小,尽管正文内容完全相同(除了格式,纯文本丢失了)。此外,如果我删除我的签名,我可以进一步减少电子邮件的大小。当然,我们谈论的是大数据时代的千字节。但是当我们将这些结果放在一起时,我们可以看到格式(和签名使用)之间的百分比差异非常显著(见下表)。因此,即使我们不减少电子邮件的数量,我们也可以通过减少网络流量和存储消耗来影响全球二氧化碳排放量,同时保持共享相同的信息。当然,这种论证建立在电子邮件的成熟度是 HTML 格式的假设之上,这是我观察到的,尤其是在商业环境中,但在私人领域接收时事通讯或广告时也是如此。不幸的是,我找不到一个可靠的研究来量化使用电子邮件格式的百分比。
邮件格式对邮件大小的影响(图片由作者提供)
电子邮件附件
大邮件的另一个驱动因素是附件的使用。在早期,只需通过电子邮件发送文件副本,就可以与收件人共享文档,这非常有用。一方面,这是一种让公司以外的人访问该文件的简单方法。另一方面,简单地拖放副本也比发送指向内部文件共享的链接并验证此人是否有访问权限更方便(这通常需要管理文件共享的 it 专家的帮助)。如今,共享集中存储的文件变得更加容易,甚至与外部团体也是如此。主要基于云的专业办公软件套件,例如微软 365【12】和谷歌工作空间【13】,提供了自己轻松管理文件访问的功能,甚至将访问权限授予外部合作伙伴。有了这些功能,实际上不再需要将文件副本作为电子邮件附件发送。此外,如果发送的是副本而不是原始文件的链接,也会有一些缺点。例如,如果您在发送电子邮件后发现文档中有打字错误或更大的问题,您仍有机会更新原始文档,收件人将通过链接访问更新后的原始文件。但是,如果你发送了文件的副本,你就必须再次发送修改过的文件,并指出你的错误。共享链接而不是副本的另一个好处是能够协作处理同一个文件。此外,后台的文档管理系统通常关心版本控制。因此,如果任何贡献者犯了错误,您可以简单地恢复以前的版本。这种协作方式还消除了发送和存储同一文件的多个版本的需要,如 File_V1、File_V1.9、File_final_REALLY_2 等
电子邮件行为对碳足迹的影响
我们已经回顾了电子邮件的二氧化碳排放量,并讨论了导致大邮件的驱动因素。在本节中,我们想评估如果我们改变这些驱动因素,二氧化碳排放量会发生什么变化。为此,我们将应用系统动力学(SD)作为模拟技术,使用 AnyLogic 作为建模软件。这一选择的主要原因是,当在单代理活动不相关的更大规模上处理连续效果流时,SD 是非常强大的。SD 的数学基础是微积分。
实验描述
到目前为止,我们还没有弄清楚每 KB 电子邮件的二氧化碳排放量,因为 Berners-Lee 的研究只是定性地区分了电子邮件类型的排放量。一篇关于上网时二氧化碳排放的文章估计,一封大小为 1 MB 的电子邮件在其整个生命周期内会排放 20g 的二氧化碳【14】。这位匿名作者指出,每个用户一年 20 封这样大小的电子邮件产生的二氧化碳排放量相当于一辆行驶 1000 公里的汽车。如果我把这个值校准到我之前的 HTML 电子邮件示例中,它将排放 0.6 克 CO2。一封基于伯纳斯-李的排放 4g 二氧化碳的“普通”电子邮件大小为 200 KB。此外,我们可以推断,一封普通的带有附件的电子邮件的大小为 2.5 MB,才能排放 50g CO2。这些数字不影响与格式相关的尺寸采用,但影响与签名相关的尺寸采用。因此,如果我们接受 200 KB 作为标准电子邮件大小,那么删除签名只会产生-6.25%的影响。在尺寸上。因此,我们将在模拟中使用这些数据。
让我们从基于每日发送邮件的基本费率开始,总结在下表中。
每日邮件费率(图片由作者提供)
下图展示了我为进行实验而开发的 SD 模型。它区分电子邮件类型(垃圾邮件、普通邮件、附件)以及普通电子邮件的格式。股票变量计算一段时间内电子邮件的数量,而黄色变量计算每种电子邮件(子)类型的二氧化碳排放量。红色变量将单个排放量相加,得出一年内电子邮件的总二氧化碳排放量。
系统动力学模型(图片由作者提供)
接下来,我们想进行一些实验,这些实验涉及以下问题:
1)如果我们经常从 HTML 邮件中删除我们的签名会怎么样?
2)如果我们少发带签名的 HTML 邮件,多发纯文本邮件会怎么样?
3)如果我们在电子邮件中发送更多的文件链接而不是附件,会怎么样?
4)如果我们减少我们发送的电子邮件的收件人数量会怎么样?
不幸的是,我找不到一份说明有多个收件人的电子邮件比例的研究。因此,我需要做一些假设。让我们假设,特别是在商业环境中,我们经常向多个收件人发送邮件。至少我经历过,人们经常把他们的经理或其他感兴趣的人放在 CC,而不期望他们有任何行动。所以,这样的邮件对他们来说是一种“零信息”。而且“收件人”的选择往往没有意义。取而代之的是,现有的邮件列表被重新用来传播只影响部分收件人的信息。所以我想构建的是下面的情况:
- 有一定比例的全球电子邮件流量是由同一封电子邮件发送给多个收件人造成的。
- 通过限制电子邮件收件人,有可能降低这一比例。
在我的实验中,我假设 30%的电子邮件流量是由这种效应引起的。此百分比适用于普通电子邮件以及带附件的电子邮件。然后,我将通过场景 1(节省 10%的电子邮件)和场景 2(仅通过减少不相关的电子邮件收件人来节省 30%的电子邮件)来研究电子邮件流量的减少。
结果
下图显示了单个实验的结果,并设置了它们之间的关系。蓝线指的是原始的电子邮件产品,当然,在图表的顶部。我们清楚地看到,附件的减少(橙色线)对二氧化碳排放的影响最大。
电子邮件行为对全球二氧化碳排放量的影响(百万吨)
现在,让我们关注我们的潜在杠杆对全球二氧化碳排放的特殊影响。我们需要考虑之前计算的年度 CO2 排放量(1.76 亿吨)与 SD 模拟(1.69 亿吨)不同。这是因为我估计“常规”邮件 80%是 HTML,20%是纯文本。由于纯文本邮件排放的二氧化碳较少,模拟的年总排放量低于之前简单计算的结果。表格显示,将带有附件的电子邮件数量减半,用带有链接的 HTML 电子邮件取而代之,可以减少 30%的二氧化碳排放量。电子邮件收件人减少 30%将节省 6.9%的二氧化碳成本,而减少 10%将节省 2%。使用纯文本而不是 HTML 可以减少 5%的温室气体排放。签名的去除具有最低的影响,至少减少 0.4%的 CO2 排放。
测试杠杆的结果比较(图片由作者提供)
幸运的是,我们还可以计算电子邮件导致的二氧化碳排放的绝对减少量,这也显示在表格中。同样,为了对这些数字有更好的感觉,让我们搜索一下日常生活中我们都知道的对应词:
- 减少带有附件的电子邮件将会减少相当于1000 万辆汽车 驾驶一年的二氧化碳排放量。
- 当我们只减少 10%的接收者时,我们将节省相当于4120 亿部智能手机充电的温室气体排放量。
- 即使是最后一个杠杆,我们减少 50%的电子邮件签名,也会产生积极的环境影响,相当于 148 个风力涡轮机运行一整年。
- 如果我们采取所有这些措施,我们将减少二氧化碳排放量,相当于 100 万辆油罐车的汽油排放量。
正如我们之前指出的,全球电子邮件流量预计将进一步增加,百分比减少将在未来节省更多的绝对二氧化碳排放量。
结论
我们已经看到,电子邮件并不像预期的那样对气候没有影响。但相反,电子邮件数据结构中不必要的开销产生的数字垃圾会排放大量的二氧化碳。有许多杠杆可以显著影响排放,尤其是使用连杆代替附件。但其他一些更琐碎的行为,比如避免巨大的电子邮件签名,也会对温室气体排放产生显著影响。当然还有更多可能的手段,我们在本文中没有涉及,比如删除对商业或私人目的不再重要的旧邮件。关键信息是,不花钱就能节省二氧化碳排放相当容易——只要调整我们人类的电子邮件行为就能实现。可以进行进一步的研究,以评估应用我们在本文中分析的杠杆的系统措施的技术发展或组织。
【1】https://wirtschaftslexikon . gabler . de/definition/e-mail-33576
【2】https://www . FAZ . net/aktuell/technik-motor/digital/ray-Tomlinson-der-er finder-der-email-ist-tot-14109850 . html
【3】https://www . BBC . com/future/article/2020 03 05-why-your-internet-habits-is-as-clean-as-you-think
【4】https://de . statista . com/statistik/daten/studie/252278/umfrage/prognose-zur-zahl-der-tae glich-versendeter-emails-weltweit/
【5】https://www . statista . com/statistics/1270488/spam-emails-sent-daily-by-country/
【6】https://www . statista . com/statistics/1270459/daily-emails-sent-by-country/
【7】https://medium . datadriveninvestor . com/email-attachments-generate-near-6-000-needly-and-unsecure-files-per-employee-per-year-year-a 38385 bec 7 a 4
【8】https://www . EPA . gov/energy/green-gas-equivalences-calculator
【9】https://www . kba . DE/DE/Statistik/Kraftverkehr/verkehr 千米/vk _ inlaenderfahrleistung/vk _ inlaenderfahrleistung _ node . html;jsessionid = 3680 DFA 2 faf 0d 0754 d8e 522 Fe 14 cc1 e . live 21321
【10】https://ourworldindata.org/co2-emissions
【11】https://www.geeksforgeeks.org/e-mail-format/
【12】https://365 tips . be/en/share-files-in-office-365-best-practice/
【13】https://support.google.com/a/users/answer/9296687?hl=en
【14】https://www . ener guide . be/en/questions-answers/do-I-emit-CO2-when-I-surf-the-internet/69/
如何用代码定义和部署基础设施
Pulumi 是 Terraform 和 CloudFormation 的替代产品,它允许您使用自己喜欢的编码语言定义 AWS(以及其他服务,如 Azure 和 GCP)服务。
探索 Pulumi CLI
普鲁米简介
Pulumi 允许您通过结合 IaaS(基础设施即服务)的安全性和可靠性以及您所熟悉的编程语言的能力来构建云应用和基础设施。您可以使用自己选择的编程语言来定义基础设施,使用循环、条件和函数,并在不同的项目中重用这些设计模式,而不是使用 JSON、YAML 或定制的 IaaS 语言来定义基础设施。例如,下面是我们如何使用 Terraform HashiCorp 配置语言创建一个带有已分配安全组的 EC2 实例:
Terraform:使用分配的安全组创建 Ubuntu 18.04 实例
相比之下,下面是我们如何使用 Pulumi JavaScript AWS API 完成类似的任务:
Pulumi (AWS JS API):使用分配的安全组创建一个 Ubuntu 18.04 实例
如您所见,使用 Terraform 和 Pulumi 创建资源的总体逻辑是相似的。您可以过滤正确的 Amazon 机器映像,定义符合您的用例的定制安全组,将标记应用到您的资源,并基于这些 ami 和安全组定义服务器。主要区别在于创建资源的语言,对于您的用例来说,最好的工具可能取决于您对 HashiCorp 配置语言或 JSON (Terraform)以及更多通用编码语言的熟悉程度,如 Python 、 JavaScript 、 Go 和 C# (Pulumi)。
问题的介绍
作为一名软件工程顾问,很多时候我需要为公司部署一次性项目(供内部或外部使用)。在这些部署过程中,我通常会重复相同的过程:
- 创建一个临时服务器,
- 创建一个生产服务器,
- 在两台服务器上安装必要的依赖项,
- 允许准备和生产服务器相互连接,
- 并为部署配置生产环境
以前,我会用 AWS 命令行或 AWS EC2 web 界面逐一完成这些步骤。但是,在我第二次手动完成这些任务后,我决定投入时间学习云形成、地形和普鲁米。在意识到 Pulumi 提供了最少的入门障碍(因为与 JSON 和 YAML 相比,我更熟悉 JavaScript 和 Python)之后,我决定使用它来自动化这些重复的部署过程。
我如何使用 Pulumi 来解决这些问题
在阅读了 pulumi 的 AWS 文档和一些教程之后,我安装了 Pulumi 命令行工具:
**brew install pulumi** # mac
然后为这个项目创建了一个目录,换成它,并运行:
**pulumi new aws-javascript**
# aws tells pulumi we want to use the AWS Pulumi API
# javascript tells pulumi we want to use the JS AWS API
最后,编写代码前的最后一步是确保设置了正确的 AWS 概要文件。要配置 AWS 概要文件,请运行:
aws configure --profile "[name for profile]"
然后,输入正确的访问密钥 ID 和秘密访问密钥并运行:
aws use [name for profile]
为项目选择正确的 AWS 概要文件。从这里,打开 index.js 并开始定义您的基础设施。对于我的具体用例,我想创建两个服务器,一个用于试运行,另一个用于生产。我还希望它们存在于同一个 VPC 中,具有相同的安全组。为了实现这一愿景,我将代码分成了四个部分:
1.从 pulumi 访问配置变量
类似于为项目创建环境变量, pulumi 配置变量允许您定义可以在 pulumi 脚本中访问的特定于项目的值。对于我的具体用例,我想对 PEM 文件的名称保密,所以我使用以下命令创建了一个 keyName 配置变量:
**pulumi config set keyName [name_of_PEM_file]**
有了这个配置变量集,我就可以在我的 pulumi 脚本中访问分配给 keyName 的值,使用:
**const pulumi = require("@pulumi/pulumi");
const config = new pulumi.Config();
const keyName = config.get("keyName");**
2.过滤并检索正确的 AMI ID
在这之后,我需要过滤 Amazon 机器映像注册表,找到我想要创建的服务器的 AMI 名称和所有者。对于这个项目,鉴于我想使用 Ubuntu 18.04,我采取了以下步骤:
- 导航到 AWS EC2 主页,点击实例,然后点击启动实例
AWS EC2 例程页
- 滚动到您想要创建的实例,复制 AMI ID
Ubuntu 18.04 的 AMI ID
- 导航回 EC2 仪表板主页面,点击左侧边栏图片部分下的 AMIs 。
- 在搜索栏中,粘贴您复制的 AMI ID ,并记下 AMI Name 和 Owner 的值。您将需要这两个值来过滤 pulumi 中的正确 AMI。
有了 AMI 名称和 AMI 所有者之后,您可以将这两个值输入到您的脚本中,如下所示:
过滤正确的 AMI: Ubuntu 18.04
3.定义服务器的安全组,包括入站和出站规则
在正确筛选出正确的 AMI ID 后,您可以为您的服务器定义安全组。以下脚本提供了一个简单的例子:
在第一行,我们将我们的安全组命名为 3dvation-grp 。然后,我们定义入站(入口)和出站(出口)规则。在第 3–9 行,我们提供了入站规则的描述,定义了其协议,提供了一系列端口,最后定义了允许该入站规则的 IP 地址。然后,在第 12–27 行,我们定义了两个出站规则:
- 允许临时服务器 ssh 到生产服务器的出站规则(通过端口 22 ssh)
- 允许克隆 GitHub 库的出站规则(端口 43 上的 HTTPS,CIDR 块基于来自api.github.com/meta的 git 结果)。
总之,这些入站和出站规则为我们的两个实例提供了这个特定用例所需的所有功能。
4.定义和创建试运行和生产 EC2 实例
最后,在定义了安全组之后,我们使用以下代码创建了临时服务器和生产服务器:
在第 2 行和第 9 行,我们将serverSize("T2 . micro ")常量赋给 instanceType 键。在第 3 行和第 10 行,我们将新创建的安全组的 ID 分配给两台服务器(确保它们存在于同一个组中)。然后,在第 4 行和第 11 行,我们定义了可以用来访问每个服务器的 PEM 文件的名称,在第 5 行和第 12 行,我们分配了应该用来创建每个服务器的 AMI 的 ID。
完成这些步骤后,您可以运行:
pulumi up
部署更改并创建临时服务器和生产服务器。一旦这个过程成功完成,您就可以登录 AWS 并看到您的两个新服务器!
审查和成果
在本文中,我们了解了 Pulumi 及其相对于 AWS CloudFormation 和 Terraform 等其他 IaaS 提供商的一些优势。我们经历了一个实际的用例,在这个用例中,我们为两台服务器创建了一个 VPC,定义了安全组的入站和出站规则,然后创建了试运行和生产 EC2 实例。
对于更复杂的基础设施来说,Pulumi 变得更加强大,我鼓励您多阅读他们的文档,如果您有任何问题,请联系我们!
电子邮件:danielmurph8@gmail.com
领英:【https://www.linkedin.com/in/dmurphy1217/
如何删除熊猫中的一个栏目
从 pandas 数据框架中删除列
介绍
从 pandas 数据帧中删除列是一项相当常见的任务。在今天的简短指南中,我们将探讨如何通过名称删除特定的列。更具体地说,我们将讨论如何使用以下命令删除列:
del
命令drop()
方法pop()
法
此外,我们将讨论如何通过指定索引而不是名称来删除列。
首先,让我们创建一个示例 pandas 数据框架,我们将在本指南中使用它来演示一些概念。
import pandas as pd df = pd.DataFrame({
'colA':[1, 2, 3],
'colB': ['a', 'b', 'c'],
'colC': [True, False, False],
})print(df)# colA colB colC
# 0 1 a True
# 1 2 b False
# 2 3 c False
使用 del 删除列
如果您想要删除特定的列,您的第一个选项是调用如下所示的del
:
**del df['colC']**print(df)# colA colB
# 0 1 a
# 1 2 b
# 2 3 c
只有当您希望删除一列时,这种方法才有效。如果您需要一次删除多个列,请阅读以下部分。
另外,需要注意的是del df.colC
T16 不能用!语法必须与上面示例中显示的语法相同。
使用删除多个列。丢弃()
[pandas.DataFrame.drop](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.drop.html)
方法用于从行或列中删除指定的标签。
为了删除列,您需要指定如下所示的axis=1
:
**df = df.drop(['colA', 'colC'], axis=1)**print(df)# colB
# 0 a
# 1 b
# 2 c
注意,如果您想删除列,而不必将结果重新分配给df
,您需要做的就是将inplace
指定给True
:
**df.drop(['colA', 'colC'], axis=1, inplace=True)**
使用删除列。流行()
另一个选项是[pandas.DataFrame.pop](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.pop.html)
,它返回被删除的列,并最终将其从原始数据帧中删除。
**df.pop('colB')**
将返回我们希望从 DataFrame 中删除的列colB
:
0 a
1 b
2 c
我们可以验证该列确实已经从原始框架中删除:
print(df) colA colC
0 1 True
1 2 False
2 3 False
按索引删除列
在上面的小节中,我们探讨了如何通过名称删除特定的列。但是,您可能还需要通过引用特定列的索引来删除该列。
为此,可以使用[pandas.DataFrame.drop](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.drop.html)
方法。假设我们想要删除索引分别为0
和2
的列colA
和colC
,我们可以这样做,如下所示:
**df.drop(df.columns[[0, 2]], axis=1, inplace=True)**
最后的想法
在今天的简短指南中,我们探讨了如何通过名称或索引以各种方式删除 pandas 数据帧中的特定列。
您可能也对阅读如何重命名熊猫数据帧的列感兴趣。如果是这样,请不要错过下面的文章。
此外,下面的文章讨论了如何根据特定条件执行正确的行选择。
如何在 R 中删除你保存的工作空间
清理你无法摆脱的工作空间
当您退出 RStudio 时,您会看到一个弹出窗口,询问“将工作区图像保存到~/。RData?”如果你不确定,你可能会选择保存。毕竟,它是的默认。另外,储蓄是好事。对吗?
退出 R 会话弹出窗口。图片作者。
嗯……这要看情况。下次加载 RStudio 时,工作区(全局环境)中的所有内容都将保持原样。如果将数据加载到 R 中花费了很长时间,那么这就太棒了。不用再经历那个繁琐的过程了!唷!
但是如果,相反,你有一堆你不想再看到的无用的垃圾呢?你可能会想,好吧,我就清空我的工作空间,继续工作。只是这一次,在我 R 会话结束的时候,我会点击 不保存 。没什么大不了的!
下一次你加载 RStudio 时,那个你以为已经删除的讨厌的工作区将会恢复到以前的辉煌。你再试试——清除,退出,不保存,加载。*等等,什么???*这次- rm(list = ls()),退出,不保存,加载。但是……为什么?!
JESHOOTS.COM在 Unsplash 上拍照。
通过保存工作空间,R 将全局环境保存为。RData 文件。每次启动 R 时,这些文件都用来恢复您的工作区。删除您的工作环境实际上并不会删除。RData 文件-它们还在那里。这就是为什么你可以清空你的工作空间,以为它永远消失了,却发现它在你下次启动 r 时又弹出来了。
清理你无法摆脱的工作空间
如果你发现自己有这个问题,不要担心!这里有一个解决方案。事实上,不止一个!快速而肮脏的选择是清空你的工作空间,退出,然后选择保存。这一次,R 将保存一个空的工作区,下次重启 R 时将重新加载它。
第二个选项是运行:unlink(。RData”)。这实际上不会删除您的环境,但会删除保存的。RData 文件。如果没有保存的文件,启动时就没有任何东西可以加载。只要确保不要再点击保存,否则你就只能写新的了。RData 文件,您必须再次删除它们。
调整您的设置
如果您认为自己近期内不需要保存工作区,那么您可能希望 R 停止询问这个问题。这可以通过全局选项菜单的常规选项卡进行更改。要导航到全局选项菜单,从 RStudio 的菜单栏中选择工具(位于最顶端)。全局选项可以在工具菜单的最底部找到。
查找全局选项菜单。图片作者。
点击全局选项后,会出现一个选项框。在常规选项卡下,您应该会看到一个工作区部分。如果你有。RData 文件,但不希望每次启动 R 时都加载它们,可以取消选中“Restore”旁边的复选框。启动时将数据存入工作区。或者,如果您不想在关闭 RStudio 时保存您的工作空间,请从“保存工作空间至”旁边的下拉菜单中选择从不。出口处的 RData。既然 R 知道了你的偏好,它就不会再问你是否愿意保存了!
选项框。图片作者。
有了这些解决方案,希望您保存的工作空间将一去不复返。再见永不,工作区!👋
原载于 2021 年 6 月 17 日https://thatdarndata.com。
如何交付成功的数据演示
我从向高层领导做报告中学到的经验
图片由皮克斯拜的 Gerd Altmann 提供
在您的数据分析职业生涯中,可能会有这样一个时刻,您需要向高层领导展示您的数据结果。这将是一个关键的时刻,因为一个成功的演示会给公司内参与批准你的薪酬和晋升的人留下永久的印象。当我成为一名数据科学家,然后成为一名数据分析师时,我不得不从反复试验中学习如何有效地展示。幸运的是,我的演示从来不是一场灾难,但如果我知道我现在在做什么,它会更好。今天,我想和大家分享我的建议,告诉大家如何进行成功的数据演示,以提高你在高层领导中的知名度。
提供解决问题的方法
围绕演示主题研究高层领导提到的问题,并尝试找到提供解决方案的数据见解。高管们一直在努力提高业务绩效,任何能提供问题解决方案的人都会引起他们的注意。
例如,您是一名数据分析师,为一家电子商务公司的营销提供支持。年复一年,销售额一直在下降,你被要求调查原因,并将你的发现提交给高层领导。你会发现,将网站访客按渠道分类后,销售额下降是因为有机搜索占了总销售额的 50%。经过进一步的调查,访客下降对应的网站变化发生在同一天。一旦网站修好,销售额应该会再次上升。找到原因并为销售等重要 KPI 提供解决方案,会让高层领导注意到你,并在下次谈话中提到你的名字时在他们的脑海中留下好印象。
将影响转化为收入
讨论收入时,高管们会注意到。即使 KPI 提高了 1%,也可能转化为数百万美元。如果百分比变化不理想,计算您提出的任何建议对收入的影响。
例如,您是一名移动应用程序的产品分析师,被要求向高层领导提交价格上涨测试的结果。最有可能的是,你会报告相对于控制的转化率较低,因为每当你提高价格,转化率下降,因为人们不太可能为同样的产品支付更多。给高层领导的演示应该少关注价格的下降,多关注价格上涨带来的销售增量。如果转换率和增量销售都是负数,那么就没有什么好故事可讲,建议应该是保持在当前价位。然而,如果转换率下降,但销售额增加,那么就应该采用新的价格。不管测试结果如何,显示您考虑了多个 KPI 来提出建议,向高层领导表明您从不同的角度评估了结果。
提供建议
优秀的数据分析师提供可操作的见解来推动业务绩效。即使没有问题,您也可以提供下一步建议来改进业务 KPI。
在价格测试的例子中,让我们假设提高价格不会导致更高的转化率或增加销售额。如果目标是增加销售,有很多不需要提高价格的方法。你可以建议改变应用程序,以提高转化率或测试限时折扣优惠。领导层喜欢有能改善业务的想法的人,展示这种主动性表明了你帮助业务增长的热情。
进行一次练习
如果可能的话,先和你的利益相关者进行一次练习,以获得反馈。利益相关者提出的问题通常也是高层领导提出的问题,这将让你有机会在实际演示之前研究答案。如果不可能进行一次练习,让利益相关者回顾一下内容,确认你要表达的观点是清楚的。在向 CDO(首席数字官)做演示之前,除了练习之外,我还开会审查了内容。最终的结果是一个很棒的演示,如果没有反馈,我自己是无法完成的。
表现出自信
向高层领导陈述可能会让你恐慌。不幸的是,除了经常练习演讲,我没有找到克服紧张的方法。我越了解我的演示材料,我就越容易向他人演示。如果你紧张,这不是世界末日。向高层领导提供有价值的、可操作的见解有助于他们对你有一个积极的看法。
知道如何有效地呈现数据结果是数据分析角色的一项重要技能。我花了很多尝试和错误来学习如何做一个伟大的演讲,但是有了这些建议,我希望你能尽快成功。
你可能也会喜欢…
如何交付人工智能管道
实践教程
一步一步,从想法到生产
通往完整数据管道的道路漫长而曲折。由罗迪翁·库察耶夫在 Unsplash 上拍摄的照片
人工智能市场正处于成长期。在建模和工程中,技术栈正在成熟,过程正在成形。这确实是一个成为数据科学家/工程师的美好时代:乘着早期的浪潮,帮助创建其他人将在未来几十年遵循的模式。每个人都提出了自己的解决方案来部署他们的 ML 特性。
本文旨在通过一个直接来自烤箱的真实示例,对我们的项目流程进行演练,重点关注数据科学/工程流程。我们希望你能挑选一些我们使用的技术,并将它们整合到你自己的队伍中!
蓝图
在进去之前,我们需要讨论一下过程本身。人工智能项目的实施涉及三个主要的能力层次:数据科学、数据工程和前端。
我们人工智能特征过程的蓝图。图片作者。
一切从一个想法开始。公司里的某个人有了灵感,这种火花被写进了一份文件,用通俗易懂的语言重新开始。数据科学团队将该文档编译成一个摘要,展示我们如何解决该问题并执行它。我们的前端团队可以开始创建实体模型,让我们的利益相关者体验一下这个抽象的特性会是什么样子。这种快捷方式使得证明特性的影响更加容易。
该摘要然后被用作临时分析的基础,自由式建模以测试和证明该抽象想法有价值。Jupyter 和 Excel 表格比比皆是。在这个阶段,没有什么是一成不变的,也没有什么是合理设计的,因为在处理 ML 时,自由是创新的关键。
如果临时分析成功,我们将继续进行概念验证阶段。这是将前一阶段找到的解决方案工程化的第一步:代码将尽可能接近具有适当封装和质量的产品代码。它可以被认为是我们管道内模型的试驾,调整后视镜和座椅角度。与此同时,我们的数据工程团队开始绘制它在我们当前架构中的样子,计划 PoC 如何进入我们的管道。
概念证明经过适当的测试和评估后,我们让数据科学团队去从事其他项目。现在,数据工程将开始将 PoC 的集成到管道,前端团队可以使用 PoC 的输出来创建真实数据原型。
两个团队都准备好了,是时候将所有东西整合到最终产品中了。我们用生产原型测试它,然后通过淘汰旧版本来确认它。瞧。
现在,让我们看看这个过程是如何在一个新的命名实体识别特性上工作的!
这个想法
我们仪表板的一个基本块是方面分析。我们从非结构化的用户生成的文本内容中提取代表产品或服务属性的实体,从句子中提取它们的相对情感,并使用我们的定制分类法对它们进行汇总。
方面术语“崩溃”和“视频”被提取为方面,然后被分类为负面的。与问题相关的术语通常是负面的。图片作者。
这种类型的分析是非常细粒度的:方面通常由 1-3 个标记组成。我们没有得到问题的全部背景:我们提取了问题的核心(崩溃)和根源(视频)。
所以,解决的办法是立刻识别和提取整个东西。
提取整个句子。图片作者。
有了这个想法,我们创建了一个抽象,它将列出我们可以选择的可能路径。我们当前的抽象写作策略使用 RFC 结构作为指导我们开发过程的一种方式。如果你熟悉史诗/故事/任务抽象,你可以把它比作一部史诗,增加详细的描述和冗长的解释。
我们一个 RFC 的标题。摘要由多个队友审阅,人们在文本中进行评论,内容随着异步输入而不断发展。图片作者。
https://github.com/rust-lang/rfcs https://www.infoq.com/news/2017/12/react-rfc-process/
我们的 RFC 有一个特定的结构:
- 待解决问题的背景;
- 问题定义;
- 提出的解决方案以及架构/建模建议**;**
- 所解决问题的成本和收益;
- 成功的定义描述 RFC 实现结束时产生的工件。
这个特定想法的(非常)简短的 RFC 如下所示:
背景:方面太细,不能识别关于特定问题的上下文短语。
目标:从句子中提取问题(代表用户在使用产品时遇到的问题的多标记字符串)。
可能的解决方案:自定义 NER,使用 spacy | Tensorflow | PyTorch、gensim Phraser,后跟分类器、语法分析,找到问题的根源。
**成本和收益:**一个月用于模型开发,另一个月用于管道集成和原型开发。两名数据科学家,一名工程师和一名前端开发人员。
成功的定义:将提取器集成到管道中,分析结果显示在最终产品中。
验证标准
有了目标,我们开始计划如何实现。在项目进行到一半时,我们如何知道我们“足够好”呢?或者“到达那里”实际上是否可行?像这样的探索性任务可能需要几个月的来回互动,测试新的想法,提出新的问题和新的解决方案…突然你陷入了一个循环,甚至忘记了项目的意义。
设定一个停止探索问题并继续前进的期限是必要的。在这个想法探索阶段之后,我们应该很好地掌握我们需要考虑的变量。我们需要有标签的数据吗?多少钱?有我们可以使用的实现模型吗?类似项目的绩效如何?
验证阶段是 RFC 创建过程的一部分:在进入 ML 模型的本质之前,作者必须考虑项目的截止日期和完成的定义。一个简单的时间盒将帮助团队相应地安排任务,交付的定义将指导您的工作。
我们在这里做的是以产品为中心的交付:我们对成功的定义是将问题提取器集成到我们当前的管道中,并在最终产品中显示分析结果。没有准确性,没有度量,没有绒毛。这意味着我们对架构本身的创建感兴趣,而不是增强一个模型。
下面有一个关于数据项目管理的有趣阅读链接。最佳 Scrum 实践部分有一些关于项目边界的信息。
https://neptune.ai/blog/data-science-project-management-in-2021-the-new-guide-for-ml-teams
即席建模
自由式建模:只是一个(关键)板和一个梦想。照片由雅各布·本辛格在 Unsplash 上拍摄
RFC 获得批准,我们开始开发土地。第一站是特别建模:在我们通常的架构之外的建模工作,可以使用任何可用的工具来快速交付明显的结果。对于一些公司来说,这是通常的数据科学工作的范围,由软件和数据工程师来实现。
我们采用 OSEMN 框架来管理流程的这一步:获取、清理、探索、建模、解释。此即席分析的输出将是一个问题提取模型,其中包含一份关于提高其准确性和召回率的可能方法的报告。
</5-steps-of-a-data-science-project-lifecycle-26c50372b492>
让我们在项目的背景下讨论这些阶段。
- **获取:**我们的输入是用户生成的数据。由于我们已经有了一个电子商务用户评论的数据库,我们不需要原始信息来源…但我们需要手动标记句子以找到问题子串。为此,我们采用Prodigy作为我们的标注工具,并定义一组评论,这些评论将被注释以生成我们的训练数据集。
Prodigy 命名实体注释器。注意实体之间的重叠:一个问题可能涉及一个产品特性。图片作者。
- 擦洗:因为我们的数据在注释之前已经被适当地擦洗了,所以我们不需要在这里做太多。我们可以将数据集一分为二,通过类型来分隔实体,或者使用某种相似性度量来丢弃过于相似的句子。
- 探索:我们正在钻研数据,手动分析带注释的句子以及自动化的 EDA。在我们的数据集中,最简单(也是最重要)的度量是实体类型的平均标记数:这个度量显示了实体的语义复杂性。这将是我们任务难度的代理标准。
我们选择的探索性指标。请注意,发行的令牌数很高,而零售商和个人实体的发生率很低。图片作者。
- 模型:我们需要用一个命名实体识别器提取具有 5+令牌的发行实体。为了做到这一点,我们使用了 spaCy EntityRecognizer 模型,它是用我们之前注释的数据训练的。这个分类器的主干是 en_core_web_trf 预训练模型,一个基于 RoBERTa 的转换器。
只需将您的数据输入训练脚本,并更改配置来训练自定义空间管道。来源。
**https://www.machinelearningplus.com/nlp/training-custom-ner-model-in-spacy/
模型分析
咻。经过所有这些步骤,我们终于有了一个工作的 NER 模型。现在,让我们看看指标!
哦…有点低,不是吗?0.15 F1——分数只是让人心灰意冷。图片作者。
指标非常低。分数与每种类型的实体的平均令牌数负相关:问题和正面体验(POS_EXP)是具有最高令牌数和最低分数的实体。
但是我们从问题提取结果中看到了有趣的词云。很明显某些东西被提取出来了,但是我们的度量标准没有察觉到那个价值。
发布笔记本电脑评论词云。这里有很多有用的信息:重启、无响应、关机。
问题提取器的问题不在模型,而在评估:正常的评分方法依赖于严格匹配,其中提取的实体必须与注释的实体相同。这意味着以下提取将被视为完全失败:
句子:笔记本电脑开始不断重启。
注释:开始不断重启
提取:不断重启
粒子“不断重启”是问题的核心。即使我们没有实体的完整上下文,我们仍然可以挽救它!我们知道许多评论都引用了关键词“重启”,帮助生产特定笔记本电脑的品牌识别问题。
因此,我们需要将我们的度量从严格转移到部分,使部分匹配计入与注释实体外观成比例的总得分。
现在好多了,0.15 到 0.40。不是最好的,但是可以用!
这里的寓意是指标讲故事。有时候,一个指标向您描述的故事并不是数据想要讲述的故事!模型输出的临床分析总是很重要的,并且永远不应该被忽视。
有时,即使一个被认为坏了的模型也能给你的客户带来巨大的价值。
建筑规划
图片作者。
特别分析阶段的结论是,该功能有前途。这将以某种方式在最终产品中表现出来,并且由工程团队来组织将新创建的 ner 模型集成到我们当前架构中的方式。我们不会等待数据科学团队的另一次迭代来提高分数:讨论从基线建立的那一刻开始。没有概念的证明是有影响的,除非它是具体的!
一个新特性的架构应该遵循这三个原则:
- 首先,它必须足够模块化,这样升级就容易了。如果您以类似于微服务的方式集成数据丰富管道,并在数据实体之间保持合理的分离级别,就可以做到这一点。
- 第二,它应该符合先前的架构决策。只有在绝对需要的情况下,新特性才应该引入新的架构格式,我们的目标是降低复杂性和不确定性。
- 第三,它应该考虑一些基本的可观察性指标。在新部署生成的不断扩大的待办事项列表中,很容易忽略可观察性。在项目的早期阶段,准备适当的日志和度量要容易得多。
本质上,这就是 MLOps:一组良好的实践,引导模型从概念到产品。
https://www.coursera.org/learn/mlops-fundamentals https://thechief.io/c/editorial/top-10-open-source-mlops-tools/
所以,回到退出计划。在 birdie.ai 这里,我们有一些预先存在的架构来运行我们的数据丰富过程,大量使用 AWS 基础设施。我们需要选择一个脚手架来执行新的模型。
AWS Lambda +阶跃函数数据处理流水线。图片作者。
第一种模式涉及 AWS Lambdas 和 Step 函数,通过不需要重型机器的浓缩函数来处理大量数据。点火函数从 Athena 数据库中检索数据,并将其发送到一个队列。这个队列由一个单独的 Step 函数或 Kubernetes 服务使用,通过一个带有拼花压缩和适当的 Hive 分区的 Firehose 流将结果发送给 S3。可以使用 Athena 表来研究结果。Lambda 通过 Cloudwatch 日志密切控制,cloud watch 日志是一种字典,其中包含关于每个 Lambda/Step 函数执行的格式化信息。
问题是,我们用的是变形金刚模型。我们将需要 GPU 驱动的机器,而 Lambda 不适合它。Kubernetes 服务将是云不可知的,并允许我们使用 GPU 机器,但我们需要更多的努力来实现这种方法的可观察性:也许可以将一名 Kubernetes 专家带到公司,或者花一些时间来开发一些基本的集群性能分析。
来自 S3 触发器管道的 AWS 批处理作业。图片作者。
我们的第二种模式依赖于由 S3 文件插入触发的 AWS 批处理作业。每当一个拼花文件进入 S3 存储桶时,就会触发一个批处理作业来读取该文件,并对其行进行某种类型的处理,然后将结果迭代地存放到一个 Firehose 流中。脚本的复杂性抵消了流水线的简单性:批处理作业必须经过适当的多重处理,才能使用机器的所有处理能力。
它非常符合我们的需求!AWS 批处理作业可以为我们的功能带来 GPU 的全部功能,而不会给我们当前的管道增加太多复杂性,我们的一个方面提取管道使用 SpaCy 命名实体识别模型从评论中提取产品属性。我们可以改变它的用途,以使用新的问题提取模型。
我们的游戏计划现在正致力于重组我们之前的一个管道来执行问题提取。这意味着如果数据科学团队交付一个经过测试的、可扩展的推理代码,开发时间将从几天缩短到小时。
https://analyticsindiamag.com/why-data-scientists-should-follow-software-development-standards/ https://bigdata-madesimple.com/how-software-engineers-and-data-scientists-can-collaborate-together/
前端原型
现在,我们有了一个模型和一个架构,以一种可扩展的、可观察的方式交付那些推论…但是如何向我们的客户展示这个特性呢?
在这里的 birdie.ai ,我们对用户生成内容的见解是通过网络仪表盘传达的。这些仪表板由不同的开发人员管理,这些开发人员由数据分析师和前端工程师组成,不受数据科学家和工程师的控制。
这并不是说数据和产品之间存在不可逾越的鸿沟,因为我们参与了产品发现、影响和价值讨论:我们脱离了后端和前端应用程序开发,专注于数据结构化。
数据科学团队的重点随着公司的目标而转移。图片作者。
一些产品和公司不需要前端,直接从数据分析师收集的报告中提供见解。在这些情况下,数据小组可能需要执行仪表板操作,并测量与用户相关的指标。你可以说这些公司是前装。相比之下,更关注数据获取、丰富和架构的公司是后装。
我们能够使用 Tableau 快速制作一个连接到产品分析仪表板的工作原型。将 Tableau 仪表板连接到 HTML 页面真的很容易!
https://www.zuar.com/blog/embedded-analytics-how-to-embed-tableau-dashboards-into-a-web-page/
使用 Tableau 的原型问题浏览器。我们建立了一个问题类型的层次结构,可以在多个级别进行分析:图像质量组包含艺术模式、亮度差和颜色组;亮度差包括所有涉及亮度或太暗的问题。图片作者。
这些是从特定品牌的电视评论中提取的问题,我们已经可以看到关于音频、亮度和内置应用程序商店的投诉。我们评论基数的一小部分,范围在几亿。我们希望这将是营销分析师的一个方便的温度计,给他们用户最关心的问题。
这个特性掌握在我们能干的前端工程师和他们的 HTML 魔法手中。数据科学团队正在升级我们的 NER 模型,工程团队则负责管道指标。我们正处于流水线的最后一步:完全集成特性的交付。很高兴看到一个想法从开始到结束被执行,并有客户的反馈和兴趣。
我们希望这次通过我们的方法的旅行能在您的数据之旅中帮助您!
感谢joo Augusto Leite共同撰写这篇文章,并在《贸发会议联系 2021》中作为数据专题讲座发表!**
如何部署 Flask API
将您的 API 部署到 Google 云平台
图片由费德里科·布尔加拉西在 Unsplash 上拍摄
对我们许多人来说,构建第一个 API 是创建有影响力的工具的重要一步,这些工具有一天可能会被许多开发人员使用。但是这些 API 通常不能在我们的本地机器上使用。
幸运的是,部署 API 非常容易。假设你不知道你现在在做什么——你可能会在 10 分钟左右部署你的第一个 API。
我没开玩笑,超级简单。让我们开始吧。
在开始之前
有几个先决条件。这些可能会让你稍微超过 10 分钟,但我保证不会花很长时间!我们将使用谷歌的云服务来部署我们的 API,即GoogleCloudPplatform(GCP)。
你将需要输入帐单细节,但如果这是你第一次上 GCP,你会得到几百💲免费学分的价值。如果你想避免任何账单,确保你在完成后删除了你的项目。
初始化后,您可能需要选择之前创建的项目。
应用引擎初始化
我们将使用 GCP 应用引擎服务来部署我们的 Flask API。要使用它,我们需要将 App Engine 添加到我们选择的项目中,然后安装 Python 的 App Engine 扩展。
从您选择的提示窗口中,我们使用项目名称(使用您的项目名称)创建一个新的 App Engine 应用程序,并选择一个地区。
gcloud app create --project=*aurelio-330516*
然后,我们安装 Python 的 App Engine 扩展:
gcloud components install app-engine-python
现在,我们已经具备了将我们的 API 部署到 GCP 的应用引擎所需的所有先决条件。让我们继续讨论 Flask API 本身。
准备和部署
在盲目地将我们的 API python 脚本扔向 GCP 的应用引擎并抱最好的希望之前,我们应该确认它是可行的。我们可以通过模拟应用引擎环境来做到这一点,但首先,我们需要一个 API。
我将使用这个 API 脚本。它并没有做什么特别有趣的事情,但是它很简单,因此对我们的例子很有用。你可以在这里看到我是如何构建它的。
除了 API 脚本main.py
—您还需要包含一个./data
目录,其中包含两个文件 users.csv 和locations . CSV。总之,这意味着我们有一个如下所示的单一目录:
API 目录结构— 作者的图像(和所有其他图像)。
回到模拟。为了模拟 App Engine 环境,我们将使用gunicorn
来运行一个本地WEBSserverGatewayIinterface(WSGI)服务器。在此之前,您可能需要pip install gunicorn
—然后,在相同的环境中,我们运行 API:
API 服务器的初始化。
然后我们可以前往http://0.0.0.0:8080/users
。如果 API 按预期运行,我们应该会看到一个小的 JSON 响应,如下所示:
浏览器中的 API 响应。
Gunicorn 正在使用我们的 Python 环境——所以我们在main.py
脚本顶部导入的库已经安装好了。我们也知道我们正在使用(在我的例子中)Python 3.8。
当我们将 API 部署到 GCP 时,这是一个问题。App Engine 将不知道我们需要使用这些包,也不知道我们正在使用 Python 3.8。因此,我们必须在目录中再添加两个文件:
requirements.txt
将包含一个 Python 包列表,供 App Engine 安装。app.yaml
将运行时设置为使用 Python 3.8。
这些文件将如下所示:
这就是我们所需要的。现在,我们通过在包含所有文件的目录中运行gcloud app deploy
来进行部署。这需要一点时间来部署,之后我们应该会看到我们部署的 API 的 URL!
部署详细信息,突出显示的是 API 网址。
前往该 URL,后跟/users
端点,将返回我们之前看到的相同的 JSON 对象:
来自云托管版本的 API 响应。
这意味着我们的 API 已经启动并正在运行,我们可以像对待任何其他 API 一样发出请求了!
这就是使用 GCP 的应用引擎部署 Flask APIs 的快速演练。
我希望你喜欢它!如果你有任何问题,请通过推特或在下面的评论中告诉我。如果你想要更多这样的内容,我也在 YouTube 上发布。
感谢阅读!
*除另有说明外,所有图片均出自作者之手
如何在 Kubernetes 中部署 Flask API 并将其与其他微服务连接
实践教程
关于如何使用强大的容器编排工具 Kubernetes 实现微服务架构的实践教程。
Kubernetes 是一个强大的容器编排工具,它自动化了容器的部署和管理。如果您有一个包含一种服务的简单轻量级应用程序,就不要使用 Kubernetes 了。如果您的应用程序有一个由几个组件协同工作的微服务架构,那么 Kubernetes 的优势就会显现出来。它是一个用于自动化部署、扩展和管理容器化应用程序的“开源系统”,具有多种优势,包括:
- 基于需求的简单(自动)扩展
- 通过分配工作负载,使您的应用程序在部分故障的情况下仍能正常工作,从而使应用程序具有容错能力
- 自动化运行状况检查和自我修复流程
- 负责微服务之间的通信,并在所有资源上平衡传入流量
一开始从 Kubernetes 开始可能会令人望而生畏,但是如果你掌握了它的主要概念,并在官方网站上使用了优秀的教程,你会很容易上手。
在这篇博客中,我将:快速概述 Kubernetes
2 的主要概念。演示如何启动您自己的本地集群
3。在您的集群上部署 MySQL 数据库
4。设置一个 Flask 应用程序,作为 REST API 与数据库通信
网络。照片由 Alina Grubnyak 在 Unsplash 上拍摄
Kubernetes 基础知识
在这一节中,我将介绍 Kubernetes 的基础知识,但没有太多的细节;请阅读官方文档,深入了解。
Kubernetes 集群由一个主节点和一个或多个**工作节点组成。**这种建筑是 Kubernetes 的主要特色之一。正如您将看到的,您的微服务分布在不同的节点上,因此如果其中一个工作节点出现故障,它们将保持健康。主机负责管理集群,并公开 API,您可以通过该 API 与集群进行通信。默认情况下, worker nodes 带有一些组件,包括一些预安装的软件,这些软件支持运行流行容器服务的容器,如 Docker 和 containerd。
在 Kubernetes 集群上部署自己的应用程序有三个基本概念:部署、Pods 和服务。
- 一个部署是提供给主关于如何创建和更新你的应用的一组指令。有了这些指令,主将在各个工作节点上调度并运行你的应用。部署由主持续监控。如果您的应用程序的一个实例关闭(例如,如果一个工作节点关闭),它将被一个新的实例自动替换。
带有部署的 Kubernetes 集群(来源:https://kubernetes . io/docs/tutorials/kubernetes-basics/deploy-app/deploy-intro/)
- 一个 Pod 是 Kubernetes 平台内的原子单位。它代表一组一个或多个容器以及这些容器的一些共享资源(共享存储、唯一的集群 IP 地址等。).如果您创建了一个部署,这个部署将创建容器,其中包含容器。每个 pod 绑定到一个工作节点。重要的是要理解一个工作者节点可以有多个pod、,如果当前工作者节点失败,所有这些 pod 将在不同的可用工作者节点上重建。
带有几个窗格的工作节点的概述(来源:https://kubernetes . io/docs/tutorials/kubernetes-basics/explore/explore-intro/)
- 一个服务基本上定义了一组pod的逻辑集合,并定义了如何访问它们的策略。这是必要的,因为pod可以关闭并重启(例如,如果工作节点被删除或崩溃)。一个服务在一组 pod 之间路由流量,并允许 pod 在不影响您的应用程序的情况下死亡和复制。定义服务时,您可以指定服务的类型。默认情况下,Kubernetes 创建一个 ClusterIP 服务,这使得您的服务只能从集群内部访问。你可能想向外界公开你的一些服务(例如前端)。在这种情况下,您可以创建一个负载平衡器服务,该服务创建一个外部负载平衡器并为其分配一个固定的外部 IP,因此可以从集群外部访问它(例如在您的浏览器中)。
一个集群有 3 个工作节点,几个 pods 和两个绑定在一起的服务(A & B)(来源:https://kubernetes . io/docs/tutorials/kubernetes-basics/expose/expose-intro/)
开始使用您自己的集群
如果你想让你的集群快速工作:这个博客中的所有代码(和一个解释性的自述文件)都可以在这里找到。我们将要构建的应用程序由两个微服务组成。MySQL 数据库
2。一个 Flask 应用程序,它实现了一个 API 来访问和执行数据库上的 CRUD(创建、读取、更新和删除)操作。
先决条件 : 已经安装了
*kubectl*
*minikube*
(【https://kubernetes.io/docs/tasks/tools/】)。并确保您的* Docker CLI 通过命令*eval $(minikube docker-env)*
在您的集群中使用 Docker 守护程序*。不用担心:如果你重启你的终端,你会自动再次使用你自己的* Docker 守护进程*。最后通过命令**minikube start*
启动您的本地集群。*
首先:当建立一个 MySQL 数据库时,我们需要考虑两件事。1)要访问数据库,我们需要配置一些凭据;2)我们需要一个持久的数据库卷,以便在节点意外关闭时不会丢失所有数据。
创造秘密
Kubernetes 有自己的方法来处理你的敏感信息,通过配置 Kubernetes 的秘密。这可以用一个简单的 YAML 文件来完成。这些秘密可以由集群中的任何 pod 通过指定环境变量来访问(我们将在后面看到)。机密应该指定为 base64 编码的字符串。所以首先我们必须通过你的终端得到你的密码的编码版本:echo -n <super-secret-passwod> | base64
。复制输出并将其嵌入到下面的db_root_password
字段的 secrets.yml 文件中。 metadata.name 字段很重要,因为我们必须在稍后的阶段指定它,所以一定要记住它**
你现在可以通过你的终端kubectl apply -f secrets.yml
将秘密添加到你的集群中。并通过kubectl get secrets.
检查秘密,看看是否有效
持久卷
持久卷是一种生命周期独立于 Pod 的存储资源。这意味着如果一个箱关闭,存储将继续。由于 Kubernetes 拥有在任何时候重启 pods 的权限,所以将数据库存储设置为持久卷是一个很好的做法。持久卷可以是本地文件系统上的目录,也可以是云提供商的存储服务(例如 AWS 弹性块存储或 Azure 磁盘)。在创建持久* 卷时,可以指定持久卷*的类型。在本教程中,您将使用一个 hostPath 类型,它将在您的 minikube 节点上创建一个卷。但是,请确保在生产环境中使用另一种类型(参见文档),因为如果在使用 hostPath 类型时删除 minikube 节点,您的数据将会丢失。
让您的应用程序使用由两部分组成的持久卷*:
1。指定卷的实际存储类型、位置、大小和属性。
2。指定一个永久卷声明,为您的部署请求永久卷的特定大小和访问模式。*
创建一个 persistent-volume.yml 文件,并指定文件的大小(在本例中为 2GB)、访问模式和存储路径。spec.persistentVolumeReclaimPolicy
指定如果持久卷声明* 被删除,应该做什么。对于像 MySQL 数据库这样的有状态应用程序,如果声明被删除,您希望保留数据,这样您就可以手动检索或备份数据。默认回收策略继承自持久卷的类型,因此最好总是在 yml 文件中指定它。*
同样,您可以通过kubectl apply -f persistent-volume.yml
添加存储。并通过kubectl describe pv mysql-pv-volume
和kubectl describe pvc mysql-pv-claim
查看您创建的资源的详细信息。由于您制作了一个主机路径类型持久卷*,您可以通过登录 minikube 节点minikube ssh
找到数据,并导航到指定的路径(/mnt/data
)。*
部署 MySQL 服务器
*有了我们的秘密和持久卷(claim ),我们就可以开始构建我们的应用程序了。首先我们将部署一个 MySQL 服务器。拉最新的 mysql 镜像docker pull mysql
并创建 mysql-deployment.yml 文件。这个文件有几个值得一提的地方。我们指定只旋转一个 pod ( spec.replicas: 1
)。部署将管理所有标签为由spec.selector.matchLabels.app: db
指定的db
的 pod。template
字段及其所有子字段指定了 *pod 的特征。*它将运行镜像 *mysql,也将被命名为 mysql ,并在flaskapi-secrets
secret 中查找 db_root_password 字段,并将该值设置为MYSQL_ROOT_PASSWORD
环境变量。此外,我们指定容器公开的端口,以及应该将哪个路径安装到持久卷spec.selector.template.spec.containers.volumeMounts.mountPath: /var/lib/mysql
。在底部,我们还指定了一个名为 mysql 的LoadBalancer
类型的服务,这样我们就可以通过这个服务访问我们的数据库。
现在可以用kubectl apply -f mysql-deployment.yml
部署 MySQL 服务器了。并通过kubectl get pods
查看 pod 是否正在运行。
创建数据库和表
在实现 API 之前,我们必须做的最后一件事是在 MySQL 服务器上初始化数据库和模式。我们可以使用多种方法做到这一点,但是为了简单起见,让我们通过新创建的服务来访问 MySQL 服务器。由于运行 MySQL 服务的 pod 只能从集群内部访问,因此您将启动一个临时 pod 作为mysql-client
:
1。通过终端kubectl run -it --rm --image=mysql --restart=Never mysql-client -- mysql --host mysql --password=<your_password>
设置mysql-client
。填写您在 secrets.yml 文件中指定的(解码)密码。
2。创建数据库、表和模式。你可以做任何你想做的事情,但是为了确保样品瓶应用程序能够正常工作,请做如下操作:
CREATE DATABASE flaskapi; USE flaskapi;
CREATE TABLE users(user_id INT PRIMARY KEY AUTO_INCREMENT, user_name VARCHAR(255), user_email VARCHAR(255), user_password VARCHAR(255));
部署 API
最后,是时候部署 REST API 了。以下要点演示了一个 Flask 应用程序的示例,该应用程序仅使用两个端点来实现 API。一个用于检查 API 是否正常工作,另一个用于在数据库中创建用户。在 GitHub repo 中,您可以找到 python 文件,该文件具有读取、更新和删除数据库中条目的端点。连接到数据库 API 的密码是从通过创建秘密而设置的环境变量中获取的。其余的环境变量(例如MYSQL_DATABASE_HOST
)是从之前实现的 MySQL 服务中获取的(稍后我将解释如何确保 Flask 应用程序可以访问这些信息)。
要在您的 Kubernetes 集群中部署这个应用程序,您必须通过创建一个简单的 docker 文件来制作这个 Flask 应用程序的映像。没什么特别的,准备你的容器,安装需求,复制文件夹内容,运行 Flask app。转到 GitHub repo 找到构建映像所需的 Dockerfile 和 requirements.txt 文件。在 Kubernetes 集群中部署 Flask 应用程序之前,首先必须构建映像,并通过docker build . -t flask-api
将其命名为 flask-api 。
现在是时候为实现 RESTful API 的 Flask 应用程序定义部署和服务了。部署将启动 3 个 pod(在flask app-deployment . yml的spec.replicas: 3
字段中指定),在这些pod中的每一个内,从您刚刚构建的 flask-api 映像创建一个容器。为了确保 Kubernetes 使用本地构建的映像(而不是从 Dockerhub 之类的外部 repo 下载映像),请确保将imagePullPolicy
设置为never
。为了确保 Flask 应用程序可以与数据库通信,应该设置一些环境变量。db_root_password
是从你创建的秘密中获取的。每个启动的容器继承环境变量和所有运行的服务的信息,包括 T4 和 T5 地址。所以你不用担心必须指定 MySQL 数据库的host
和port
给 Flask 应用。最后,您将定义一个LoadBalancer
类型的服务来在三个 pod 之间划分传入的流量。
向 API 发出请求
您现在已经准备好使用我们的 API 并与您的数据库进行交互了。最后一步是通过你的终端minikube service flask-service
向外界公开 API 服务。您现在将看到类似这样的内容
转到提供的 URL,你会看到 Hello World 消息,以确保你的 API 正确运行。现在,您可以在您的终端中使用您喜欢的请求服务(如 Postman 或 curl )与 API 进行交互。要创建用户,提供一个带有名称、电子邮件和 pwd 字段的 json 文件。比如:curl -H "Content-Type: application/json" -d '{"name": "<user_name>", "email": "<user_email>", "pwd": "<user_password>"}' <flask-service_URL>/create
。如果您也实现了 API 的其他方法(如 GitHub repo 中所定义的),那么您现在可以通过curl <flask-service_URL>/users
查询数据库中的所有用户。
结论
卷曲在你的终端。要创建一个用户,提供一个带有名称、电子邮件和密码字段的 json 文件。比如:curl -H "Content-Type: application/json" -d '{"name": "<user_name>", "email": "<user_email>", "pwd": "<user_password>"}' <flask-service_URL>/create
。如果您也实现了 API 的其他方法(如 GitHub repo 中所定义的),那么您现在可以通过curl <flask-service_URL>/users
查询数据库中的所有用户。
结论
在本实践教程中,您将设置部署*、服务和*pod,通过部署 Flask 应用程序并将其与其他微服务(本例中为 MySQL 数据库)连接来实现 RESTful API。您可以继续在本地运行它,或者在远程服务器(例如云中)上实现它,并将其投入生产。随意克隆 repo 并随意调整 API,或者添加额外的微服务。
如果您有任何其他问题、意见或建议,请随时联系我!
关于作者
Rik Kraan 是一名放射学博士,在荷兰数据科学咨询公司 Vantage AI 担任数据科学家。通过rik.kraan@vantage-ai.com联系
如何在 AWS ECS 上部署 Flask API(第 3 部分)
如何在 AWS ECS 上对接和部署 Flask API 的分步指南
数据科学领域的一个重要方面是你永远不会停止学习。一旦你对数据争论和机器学习感到舒适,下一步通常是学习如何让你的 ML 模型进入“生产”以供他人使用。下一步不仅需要你知道如何使用 Flask、Django 或 FastAPI 等工具构建 API,还需要你了解 Docker & Cloud (AWS、Azure、GCP)栈。这篇文章旨在帮助你解决最后两个问题:Docker & Cloud。
如果您一直在关注我的电影推荐引擎组合项目 4 部分系列,到目前为止,我们已经介绍了协作过滤的理论(第 1 部分)以及如何创建 Flask API(第 2 部分):
在这里,我们将介绍如何在 AWS 上部署我们在第 2 部分中构建的 Flask API。
教程结构
-
AWS 上最流行的 API 部署选项的快速概述
-
克隆包含必要 API 文件的 GitHub repo
-
将 API 归档
-
将 Docker 图像推送到 Amazon ECR
-
在 AWS ECS 上创建一个任务来运行 API,并使用分配的公共 url 对其进行测试。
-
AWS 上最流行的 API 部署选项的快速概述
在决定如何在 AWS 上部署后端 API 时,我发现了两种主要方法:
I)带有 API 网关的 AWS Lambda 功能
或者
ii)基于 Docker 映像在 ECS 上创建任务。
即使配置任务、负载平衡器、安全组等。与使用 AWS Elastic Beanstalk 相比,在 AWS ECS 上手动安装可能会更复杂,它使您能够更好地理解并根据您的项目需求正确设置每个组件。
我之所以选择 Docker Containers 和 ECS 而不是 Lambda 函数来部署这个 API,是因为 Lambda 函数要求您添加额外的层来配置库(如 pandas ),以便与 python 运行时兼容,如果您不是严格在 Linux 操作系统上构建 Docker 映像,这可能会很麻烦。
让我们开始吧。
2。克隆包含必要 API 文件的 GitHub repo
a) 克隆以下回购:
[https://github.com/kuzmicni/movie-rec-engine-backend](https://github.com/kuzmicni/movie-rec-engine-backend)
b) 从&下面的 google drive 下载我们在上一篇文章中创建的 movie_similarity.csv 文件( Part 2 )粘贴到克隆回购的根目录下。
[https://drive.google.com/drive/folders/1FH6bWCcw1OoRf4QJaFaf4gIegIGSEx9r](https://drive.google.com/drive/folders/1FH6bWCcw1OoRf4QJaFaf4gIegIGSEx9r?usp=sharing)
您的代码结构应该如下所示:
c) 创建虚拟环境&安装必需的库
conda create --name py37_tut python=3.7
conda activate py37_tut
pip install Flask Flask-Cors pandas
运行应用编程接口
python application.py
e) 在一个单独的终端中运行测试 API
curl -X POST http://0.0.0.0:80/recms -H 'Content-Type: application/json' -d '{"movie_title":"Heat (1995)"}'
应该从 API 获得以下输出:
3。将 API 归档
我假设您已经安装了 docker,并且熟悉创建 docker 映像。让我们确保您安装了 docker:
docker version
应该得到类似的东西:
我们对 API 进行 Docker 化的方式实质上是基于名为 Dockerfile 的模板构建 Docker 镜像,该模板包含关于哪些文件需要复制以及如何运行 API 的指令。
Dockerfile:
FROM python:3.7COPY ./* ./app/WORKDIR /app/RUN pip install -r requirements.txtEXPOSE 80CMD ["python", "application.py"]
本质上,这个 Docker 文件(即蓝图)指示的是将我们当前目录中的所有文件复制到 Docker image /app/ 文件夹中,安装 requirements.txt 文件中指定的库,暴露端口 80 并将 Flask API 作为服务器运行。
要构建名为 flask-api 的 Docker 映像,请确保您与 Docker 文件在同一个目录中(即/movie-rec-engine-backed ),并在您的终端中执行:
docker build -t flask-api .
应该得到:
现在我们准备把它推送到 AWS 弹性容器注册中心(ECR)。
4。将 Docker 图像推送到亚马逊 ECR
我假设您已经安装了 aws-cli,并且已经通过 aws configure 命令输入了您的访问密钥和密钥。
在 ECR 中创建一个存储库:
单击“创建存储库”,保留默认的私有选项,并将您的存储库命名为“flask-api-repo”
b) 将 Docker 图像推送到 ECR
点击创建的“flask-api-repo”并选择“查看推送命令”
在您的终端中运行所提供的说明,但步骤 2 除外,因为我们已经构建了映像:
aws ecr get-login-password --region **<your-region>** | docker login --username AWS --password-stdin **<your-account-alias>**.dkr.ecr.**<your-region>**.amazonaws.comdocker tag flask-api **<your-account-alias>**.dkr.ecr.**<your-region>**.amazonaws.com/flask-api-repo:latestdocker push **<your-account-alias>**.dkr.ecr.**<your-region>**.amazonaws.com/flask-api-repo:latest
推送映像后,您可以刷新 repo,应该会看到以下内容:
5。在 AWS ECS 上创建一个任务来运行 API,并使用分配的公共 url 对其进行测试
是时候创建一个运行该图像的任务了!
转到 ECS。
a)创建一个集群
选择:“仅网络”选项
集群名:flask-api-cluster
选择:创建 VPC 并保留默认选项
点击“创建”。
b)定义任务
在 ECS 中,单击任务定义
单击“创建新任务定义”按钮
选择“FARGATE”
选择“下一步”
输入任务定义名称:flask-api-task
选择默认的“ecsTaskExecRole ”,或者您也可以在 IAM 中创建一个具有更多访问权限的新角色。
c)指定任务大小
任务内存(GB): 1GB
任务 CPU: 0.5 vCPU
d)添加容器
选择“添加容器”
容器名称:烧瓶-API-容器
对于 URI 形象,我们需要抓住我们的 URI 形象。只需在新标签中重新打开 aws 控制台,导航到 ECR flask-api-repo 并复制图像 URI:
返回并将 URI 粘贴到图像*字段中
输入:
软限制:128
端口映射:80
将其余部分保留为默认值,然后单击页面底部的“添加”按钮
向下滚动并点击“创建”
你应该看看
单击“查看任务定义”
e)运行任务
现在,在“操作”中选择“运行任务”
选择“FARGATE”
任务数量:1
对于子网:选择两个选项(我选择 a 和 b)
除了安全组*点击编辑
选择“创建新的安全组”
安全组名*: flask-api-sg
点击“保存”
现在,我们应该看到我们的新任务处于“PROVISIONING”状态。等一会儿,直到它变成“运行”
点击“任务”并向下滚动找到公共 IP。现在将 IP 粘贴到您的浏览器中,您应该会看到:
测试部署的 API
让我们通过终端测试一下。
假设你得到的公共 IP 是1.2.3.4,那么在终端中,你可以运行:
curl -X POST **1.2.3.4**/recms -H 'Content-Type: application/json' -d '{"movie_title":"Heat (1995)"}'
并且应该得到:
这意味着您的 API 已成功部署!
结束语
需要注意的事项:
您可能需要试验您的 EcsTaskRole,并确保安全组具有允许流量访问 API 的规则。
将 Docker 映像推送到 ECR 并在 ECS 中创建任务会在您的 AWS 帐户上产生费用,因此请确保停止任务并删除所有相关资源。
现在我们准备使用第四部分中的 Vue.js 构建电影推荐引擎前端!
如何用 FastAPI、Docker 和 Github 动作部署机器学习模型
具有 CI/CD 的端到端管道
你是一名数据科学家,在一家软件公司工作。
您刚刚训练了一个模型,您对它很满意,因为它在您的本地交叉验证中表现很好。
现在是将该模型投入生产的时候了,以便您组织中的其他团队可以使用它并将其嵌入到他们的应用程序中。
这是数据科学家面临的非常普遍的情况。管理起来既麻烦又痛苦,但是使用正确的工具,这个过程可以顺利进行。
在本教程中,我将展示一个端到端的用例来解释将模型投入生产的工作流程。这是一篇相对较长的文章,所以请随意跳到你感兴趣的部分。
以下是我们将要介绍的内容:
- 生产机器学习和 API 简介
- FastAPI 特性的快速概述
- 使用 FastAPI 和 SpaCy 构建推理 API
- 用 Docker 和 docker-compose 封装 API
- 将 API 部署到 AWS EC2,并使用 Github Actions CI/CD 自动化流程
我希望你准备好了。事不宜迟,让我们直接开始吧🚀。
PS:所有代码在Github上都有。
新到中?你可以每月订阅 5 美元,解锁我写的不限数量的关于编程、MLOps 和系统设计的文章,以帮助数据科学家(或 ML 工程师)编写更好的代码。
https://medium.com/membership/@ahmedbesbes
将模型投入生产意味着什么?
在我们更进一步之前,让我们从一个共同点开始。
从广义上来说,不涉及很多细节,将模型投入生产是一个过程,在这个过程中,模型被集成到现有的 IT 环境中,并可供其他团队使用和消费。
为了使这个过程尽可能高效,应该仔细管理许多步骤:数据存储、服务、工件记录、资源管理等等。
作者制作和修改的图像
如果你的模型从未离开过你的本地电脑,而你所有的实验都在 jupyter 笔记本上,那么你很可能还没有准备好投入生产。
不过不用担心。本教程旨在帮助您开始使用 API 将模型投入生产。
API 到底是什么?
是 A 应用程序 P 编程 I 接口的简称。它只是两个相互通信的独立应用程序之间的中介。
如果您是一名开发人员,并且希望您的应用程序可供其他开发人员使用和集成,您可以构建一个 API 作为您的应用程序的入口点。因此,开发人员必须通过 HTTP 请求与该 API 进行通信,以使用您的服务并与之交互。
作者图片
将 API 视为应用程序的抽象(用户看不到你的代码,也不安装任何东西,他们只是调用 API)和与第三方(开发者、应用程序、其他 API 等)集成的简化。)
API 无处不在。数据科学世界也不例外。
什么是 FastAPI,为什么它适合生产机器学习?
FastAPI 目前是构建可在生产环境中伸缩的健壮的高性能 API 的首选框架。
截图—https://fastapi.tiangolo.com
FastAPI 最近越来越受欢迎,用户在 web 开发人员、数据科学家和 ML 工程师中的采用率大幅上升。
让我通过展示我能想到的最有趣的特性来解释围绕这个框架的所有宣传。
然后,我们将转到在特定用例中使用 FastAPI 的部分。
PS:这个列表并不详尽,如果你想有一个全局的概述,你可以参考 官方 文档。
1)简单的语法
FastAPI 的语法很简单,这使得它使用起来很快。它实际上类似于 Flask 的语法:因此,如果您正在考虑从 Flask 迁移到 FastAPI,迁移应该很容易。
实例化 Flask 和 FastAPI 应用程序是一样的。然而,与 Flask 不同,FastAPI 没有集成 web 服务器。FastAPI 是专门为构建 API 而设计的。它没有责任为他们服务。
FastAPI 做一件事,而且做得很好。
服务 API 是 uvicorn 的责任,这是一个很好的选择,因为 uvicorn 是一个使用 uvloop 和 httptools 的闪电般快速的 ASGI 服务器实现。
烧瓶:
FastAPI:
在两个库中,为 HTTP 请求定义路由和附加处理程序遵循相同的语法。你用装修工。
FastAPI 的语法甚至更简单:每个特定的 HTTP 请求都有一个装饰器,不像 Flask 将请求类型作为参数传递给 route 装饰器。
烧瓶:
FastAPI:
在 Flask 和 FastAPI 中,path 参数都是从我们传递给 route 的路径中解析出来的。对于 FastAPI,我们将这些参数作为参数添加到函数中。我个人认为 FastAPI 在这种特殊情况下的语法更容易阅读和理解。
烧瓶:
FastAPI:
在 FastAPI 中提取查询参数要容易得多:您只需将它们作为处理程序的参数,FastAPI 会处理剩下的事情:它从 route 中解析它们,并将它们转换成您指定的类型。
当使用 Flask 时,您必须调用request
类,单独获取每个参数并应用一些最终的后处理。
烧瓶:
FastAPI:
要快速了解 FastAPI 的语法,请看这个链接。
2)一个超快的框架
根据独立网站 techempower 的说法,FastAPI+uvicon 是最快的网络服务器之一。
这是一个执行基本任务的性能比较,比如 JSON 序列化、数据库访问和服务器端模板合成。每个框架都在现实的生产配置中运行。
3)异步请求
FastAPI 带来了以前的 web 框架如 Flask 和 Django 所缺乏的新特性:异步请求。
异步编程是一种编程模式,它使代码能够独立于主应用程序线程运行。异步编程在许多用例中使用,例如事件驱动的系统、高度可伸缩的应用程序以及 I/O 绑定的任务,例如通过网络读写文件。
如果在处理程序中使用异步函数,那么在调用该函数之前,必须将async
添加到处理程序中,并添加await
参数。
你可以在这里了解更多关于异步编程的知识。
4)使用 Pydantic 的现代 python 类型
FastAPI 与 Pydantic 集成在一起,用标准的 python 类型对请求和响应主体进行类型化。
这在运行时强制验证,并通过允许您的 IDE 正确地自动完成并检测与类型相关的错误来提高您的编码效率。
在下面的例子中,我们设计了一个小的 API 来发布工作申请并接收随机的决定。请求体遵循 Application 类中定义的模式。它仅仅意味着它必须具有:
- 类型为字符串的
first_name
键 - 类型为字符串的
last_name
键 - 一个类型为 int 的
age
键 - 类型为字符串的
degree
键 - 一个可选的类型的
interest
键字符串
类似地,您可以使用决策类中定义的模式键入 API 响应。
这有什么用呢?
假设您不小心向 first_name 字段传递了一个整数。FastAPI 将检测类型不匹配,并相应地引发错误。
你可以在这里了解更多关于 Pydantic 的信息。
5)字符串查询参数的验证
FastAPI 允许通过在字符串查询参数上添加约束来验证用户输入。例如,您可以设置最大或最小长度,或者设置查询必须匹配的正则表达式。
你可以在这里了解更多关于字符串验证的信息。
6)数值参数的验证
同样,当使用数字参数时,您可以通过使用gt
(大于)和le
(小于或等于)来设置一个范围作为约束。
你可以在这里了解更多关于数字验证的信息。
7)更好的错误处理和自定义消息
使用 FastAPI,您可以用特定的消息和状态代码来定义和引发自定义错误。这有助于其他开发人员在使用您的 API 时轻松理解错误并进行调试。
你可以在这里了解更多关于错误处理的信息。
8)遵循 OpenAPI 标准的自动文档生成
一旦您开始使用 FastAPI,就会自动为您生成交互式 API 文档和探索。
在本地启动 API 后,您可以通过此链接[http://localhost:8000/docs](http://localhost:8000/docs)
访问它。
您将看到您创建的每条路由的文档,以及一个交互式界面,您可以直接从浏览器测试每个端点。
作者提供的图片
9)世界一流的文档,学习并开始使用框架
我很少见过如此完整且易于理解的文档。由塞巴斯蒂安·拉米雷斯领导的 FastAPI 团队已经完成了记录每一段代码的惊人工作。
你可以在这里开始学习如何使用 FastAPI,坦白地说,没有更好的选择了。
作者图片
使用 FastAPI 和 Spacy 创建匿名化 API
让我们进入有趣的部分。
在我之前的一篇帖子中,我构建了一个 Streamlit 应用程序,它通过检测带有空间模型的命名实体来匿名化文本。要使用这个应用程序,你必须打开一个浏览器并与用户界面互动。
但是,
如果我们可以创建一个 API 端点来做同样的事情,这样开发人员就可以在不使用 UI 的情况下轻松地与这个程序进行交互,会怎么样?
这看起来像是 FastAPI 的完美工作。
下面的图表代表了我们将要构建的 API 的模式:
作者图片
我们将只定义一个路由,它将接受路径/entities
上的 POST 请求,并且它将期望一个包含三个字段的请求体:
作者图片
- 文本:要匿名的文本
- model _ size:NER 模型的尺寸(默认为“sm”)
- model _ language:NER 车型的语言(默认为“en”)
作为输出,API 将返回一个包含两个键的 JSON 对象:
作者图片
- **实体:**提取的实体列表。每个实体是一个 JSON,包含四个键( start :文本中实体的起始索引, end :结束索引, type :实体的类型, text :实体的值)
- **匿名化 _ 文本:**将实体匿名化的原始输入文本
为了设置请求和响应体的类型,我们将使用 Pydantic。
- 对于请求的主体,我们将
UserRequestIn
定义为 BaseModel 类的子类。它会有三个属性:text,
model_language
和model_size
。
我本可以将model_language
和model_size
的类型设置为 string,但是,为了本教程,我为它们定义了两个类:ModelLanguage 和 ModelSize。这两个类继承自str
和Enum
。使用 Enum 的想法是限制每个字段的可能值。对于语言,我们只需要英语(“en”)和法语(“fr”),对于大小,我们需要三个可能的值(“sm”表示小,“md”表示中,“lg”表示大)。 - 至于响应的主体,我们定义了
EntitiesOut
类,再次作为 BaseModel 的子类。如前面的响应主体截图所示,它将有两个键:实体和anonymous ized _ text。
anonymous _ text是一个字符串,而 entities 是一列 EntityOut 对象。EntityOut 只是一个 BaseModel 类,表示每个实体的模式:它有四个字段:start (int)、end (int)、type (str)和 text (str)。
如您所见,使用 Pydantic 为您的请求和响应构建定制类型非常直观。通过组合其他复杂类型,你可以变得更有创造力。
既然已经创建了数据模型,我们就可以开始构建 API 了:首先,我们用 Spacy 加载语言模型。这些模型将保存在ml/models/
文件夹中。
然后,我们将定义 API 路由和处理程序。
这里发生了一些事情:
- 在装饰器
@app.post
中,我们将路径设置为entities/
,并将response_model
参数设置为EntitiesOut
。在这里,FastAPI 强制响应体遵循在EntitiesOut
类中用 Pydantic 声明的模式。 - 处理程序的第一个参数是请求体:我们将其类型设置为
UserRequestIn
以强制输入验证。 - 剩下的就是纯 python 代码:我们提取数据,调用模型,并匿名化检测到的实体。
- 最后返回一个字典,有两个键:实体和anonymouzed _ text。
现在一切都应该可以在本地运行了。进入项目的根目录,运行uvicorn api.main:app --reload
作者提供的图片
要检查 API 是否在工作,您可以打开http://localhost:8000/docs来查看交互界面,在这里您可以从浏览器尝试 API。
作者图片
用 Docker 打包 API
既然 API 在本地工作,我们可以构建一个 Docker 映像来打包它的依赖项,并在一个隔离的执行环境中运行它,也就是一个容器。
您可以将容器视为虚拟机,尽管它们不像虚拟机那样提供严格的隔离。
Docker 映像由Dockerfile
定义,它基本上是 Docker 构建映像的一组指令。
图像通常建立在其他基础图像之上。这些图像可以从 Docker Hub 中提取,这是 Github 的一个等价物,但是是针对 Docker 图像的。
这是我们 API 的 docker 文件。它包含在项目的根目录中。
让我们一行一行地详细说明发生了什么:
- 我们指出我们从
python:3.7
开始的基本图像。Docker 在构建映像时从中央存储库中取出它。 - Python 依赖项(fastapi、uvicorn 和 spacy)在镜像中使用
pip
安装。 - 我们将
api/
文件夹的内容(从主机)复制到/api/api/
文件夹(在镜像上)
PS:这个文件夹是在镜像中自动创建的 - 我们将 PYTHONPATH 变量设置为
/api
- 我们为后续的 CMD 命令设置工作目录。这仅仅意味着 Dockerfile 文件的最后一行将从工作目录中执行。
- 我们向 Docker 表明,容器将在运行时监听端口 8000。
ENTRYPOINT
允许配置作为可执行文件运行的容器。- 我们设置要执行的命令(我们将省略
uvicorn
命令,因为它已被声明为入口点。
现在我们将定义一个 docker-compose,它将为我们的 API 创建一个服务。
我本可以只使用 Dockerfile,因为 docker-compose 是一个定义多容器应用程序的工具,而且我们目前只有一个容器。
但是为了本教程,让我们来看看它是如何完成的:一般来说,您可能希望在单独的容器中运行多个服务,docker-compose 是将它们组合在一起的一个很好的工具。
我们在同一位置再次创建一个docker-compose.yaml
文件。
作者提供的图片
在这个文件中,我们将 docker-compose 的版本设置为 3(最新的)。我们为服务命名(anonymization-api
),为构建步骤设置 Dockerfile 的路径,并将端口 8000(在容器上)映射到端口 8000(在主机上)。
最后,我们运行这个命令,它将为容器内部的 API 提供服务,并使它可以从主机访问。
**docker-compose up --build**
在 AWS 上启动并配置 EC2 实例
让我们创建一个 EC2 实例,我们将在其中用 Docker 部署 API。
- 首先,连接到您的 AWS 帐户,转到 EC2 部分并选择一个发行版。这个教程我推荐 Ubuntu 20.04 LTS。
作者截图
- 举个例子:我们不会在这里发疯。我们就挑一个比较小的:a
t2.medium
。
作者截图
- 现在启动实例。
- 创建一个弹性 IP 地址,并将其关联到正在运行的实例。这样,每次我们重新启动实例时,这个 IP 都不会改变。
作者截图
- 添加一个新的安全组(我将其命名为 fastapi)以允许端口 8000 上的入站流量。
作者截图
- 然后,将其添加到实例安全组:
作者截图
现在,实例已经准备好接受端口 8000 上的请求。
- 使用您的终端 SSH 到它。
- 按照 ubuntu 的官方文档安装
docker
和docker-compose
- 生成一个 ssh 密钥,并将其添加到您的 Github 帐户,这样它就可以无缝地执行 git 克隆(我们将在下一节中使用它)
在 Github 操作上创建部署工作流
Github Actions 是 Github 的 CI/CD 服务。它允许您基于 git 提交、合并或拉取请求等事件自动测试、构建和部署应用程序。
要了解更多关于 Github 动作的信息,请看一下这个 链接 。
要在 repo 中添加工作流程,请点击操作选项卡。
作者截图
然后,点击自行设置工作流程。
作者截图
一个 YAML 文件将被自动创建在一个workflows
文件夹中,该文件夹将被创建在回购根目录下的一个.github
文件夹中。
在我们的例子中,我们将工作流设置为仅在主分支上的推送请求时触发。
作者截图
将被触发的作业将在一个远程服务器上运行,GitHub Actions 将通过 **SSH Remote 命令连接到该服务器。**在其他情况下,该作业可能在 Github 运行程序上运行,例如 Github 提供的实例。
SSH Remote Commands 是一个定制的 GitHub 动作,你可以在市场上找到。它是免费使用的,你只需要在uses
部分之后调用它。
将使用以下参数调用 SSH 远程命令 Github 操作
- 主机:服务器的主机名(即其公共 IP)
- 用户名:ssh 用户名
- key:ssh 私钥的内容
- 脚本:ssh 连接建立后将执行的脚本
该脚本将列出 SSH 连接建立后将在服务器上运行的命令:
- 克隆回购
- 光盘放进去
- 运行 docker-compose build 和 up 命令。
git clone git@github.com:ahmedbesbes/anonymization-api.git
cd anonymization-api
sudo docker-compose up --build -d
前面的参数host
、username
和key
不会硬编码到 YAML 文件中。他们永远不会。这个信息很敏感。
它们将被存储为 Github Secrets 并用$符号引用,就像你调用环境变量一样。
要创建 Github secrets,请转到存储库的设置,然后单击左侧选项卡上的 secrets。Github 机密总是在 Github repo 的范围内定义。
作者截图
然后通过设置它们的名称(用大写字母)和它们的值来定义你的秘密。
作者截图
下面是你如何设置USERNAME
秘密的例子。
作者截图
现在你可以提交、推动并期待奇迹的发生!
一旦你推送你的代码(在测试本地一切正常之后),你会注意到,在点击动作标签之后,一个新的运行正在排队等待开始。
通过点击它,您可以看到构建的不同步骤。
作者截图
一旦 API 成功地部署在您的远程服务器上,启动 Postman 并在 API 端点上执行一些请求来检查它是否正常工作。
作者截图
有后!🎊现在 API 已经部署好了,可以工作了,并准备好接受请求。
结论和后续步骤
到目前为止,已经涵盖了很多内容。
我们了解了 FastAPI 提出的有趣特性。然后,我们用这个框架构建了一个 API,最后用 Docker 部署在 AWS 上。
为了使部署过程尽可能顺利,我们还使用了 GitHub Actions 工作流,该工作流在每次推送时被触发,并使用自定义操作。
现在,为了使这个 API 更适合生产,我们可以考虑这些特性(我可能会在未来的博客文章中涉及它们)
- 为 API 设置域名
- 通过添加 HTTPS 来保护 API
- 使用
gunicorn
代替unicorn
进行部署 - 添加数据库服务(例如 PostgreSQL)来存储输入和预测:这是应用异步请求的好机会。
资源:
这里是我在学习 FastAPI 和部署时浏览的一些高质量资料。
https://fastapi.tiangolo.com/ https://www.starlette.io/ https://medium.com/@calebkaiser/why-we-switched-from-flask-to-fastapi-for-production-machine-learning-765aab9b3679 https://testdriven.io/blog/moving-from-flask-to-fastapi/
感谢阅读!🙏
我希望这篇文章对你有用。如果你对改进我建立的工作流程有任何建议,请不要犹豫,在 Github 或评论中提出。
同样,代码可以在我的回购上获得:请随意在你的项目上试用。
我就讲到这里,下次见!