admin管理员组

文章数量:1645531

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

默认的拥抱脸模型可能就是“普通”图像分类所需要的全部

原文:https://towardsdatascience/default-hugging-face-models-are-probably-all-you-need-for-vanilla-image-classification-9d0ee19c85fa

在土地覆盖分类任务中探索作为特征提取器和转移学习器的拥抱面部中枢上的预训练图像模型

代码回购:[ 链接,笔记本:[ 链接

**图一。**来自著名的切萨皮克保护区土地覆盖数据集的卫星图像(NAIP 卫星)的四种表示法。

介绍

在这篇文章中,我将探索表征学习中的一种常见做法——使用预训练的神经网络作为学习的特征提取器。具体来说,我感兴趣的是检查在这些提取的神经网络特征上训练的简单模型的性能如何与通过迁移学习启动的微调神经网络进行比较。目标受众包括其核心的数据科学家,以及更普遍的对地球观测、计算机视觉和机器学习感兴趣的任何人。

向前跳一点… 下面的结果表明,在提取的神经网络特征上训练的 scikit-learn 模型产生的性能与在相同的预训练权重下微调的完整网络几乎相当(平衡精度下降-3%到-6%)。

背景

今天,全世界的微软每年都会发布数千个预先训练好的神经网络。而且,这些模型变得越来越有性能和可访问性。有了这么多开源的模型检查点,神经网络作为 AI/ML 的中心焦点的演变就不太令人惊讶了。考虑到各地的人们都听说过达尔-E-2 和稳定扩散——可以将文本提示转化为图像/艺术的神经网络,据报道,稳定扩散已经被超过 1000 万用户下载**。许多人不知道的是,这些技术今天的存在在很大程度上归功于被称为表征学习**的统计学子领域的进步。

“21 世纪 20 年代看起来像是在 ML 中实现表征学习承诺的时代。有了在特定领域(有监督或无监督)训练的模型,我们可以在处理输入时使用它们的后期激活作为它们输入的表示。表征可以以多种方式使用,最常见的是用作下游模型的直接输入,或者用作与多种类型的模型(文本和视觉、GNN 和文本等)共同训练共享潜在空间的目标。)." —凯尔·克拉宁*【1】*

让我们来检验一下这些说法…

数据集详细信息

下面使用的图像数据集来自 2013/2014 年切萨皮克保护区土地覆盖项目【2】。它由国家农业图像计划(NAIP)卫星图像组成,具有 4 个波段的信息,分辨率为 1 平方米(红色、绿色、蓝色和近红外)。原始地理空间数据最初跨越 6 个州的 100,000 平方英里:弗吉尼亚、西弗吉尼亚、马里兰、特拉华、宾夕法尼亚和纽约。首先对其进行二次抽样,以获得 n =15,809 个大小为 128 x 128 像素的独特斑块和尽可能多的土地覆被标签*。当检查示例补丁(见图 1)时,1 平方米的分辨率显得相当精细,因为图像中的结构和对象可以以较高的清晰度进行解释。

***旁注:**原始切萨皮克保护区土地覆盖数据集包括标注掩膜,用于分割而非分类。为了改变这一点,我只保存了在采样地理空间数据时单个类出现频率至少为 85%的补丁。然后,过度表示的类被记录为该补丁的唯一标签,导致简化的 5 路分类问题。

实验中使用的 5 种土地覆盖类型定义为…

  1. 水域:开阔水域,包括池塘、河流和湖泊
  2. 树冠和灌木:木本植被,包括乔木和灌木
  3. 低植被:高度小于 2 米的植物材料,包括草坪
  4. 不毛之地:没有植被的天然土质区域
  5. 不透水表面:人造表面

经过检查,数据集似乎具有许多有趣的特征,包括季节变化(例如,树叶)、噪声(例如,偶尔的云层覆盖)以及跨 6 个州的分布变化。少量的“自然”噪声有助于使这个有些简化的分类任务变得更加困难,这是有益的,因为我们不希望监督的任务过于琐碎。

使用美国各州作为拆分机制来生成训练集、验证集和测试集。来自宾夕法尼亚州的补丁被选择用于测试集( n = 2,586,16.4%的数据),来自特拉华州的补丁被选择用于验证集( n = 2,088,13.2%的数据),其余的被用于训练集( n = 11,135,70.4%的数据)。

最后,作为一个整体,数据集在一定程度上存在明显的类别不平衡:贫瘠(49/15,809)和不透水表面(124/15,809)严重不足,而树冠和灌木(9,514/15,809)严重过量。低植被(3,672/15,809)和水(2,450/15,809)相比之下更加平衡。由于这种标签的不平衡,我们在下面的实验中使用平衡的精确度。该指标采用每个类别的个体准确性的平均值,从而平等地加权每个类别,而不考虑大小。

see: torchgeo.datasets

习得特征

一般来说,学习到的特征可以被定义为那些源自黑盒算法的特征。通过提取图像表示的学习特征,你经常委托 CV 社区中其他人的工作——最初训练黑盒的团队。例如,人们可以从神经网络中提取学习到的特征,这些神经网络在预训练(如 ImageNet)中使用 keras、pytorch 和 transformers 等包看到了大型基准数据集。无论是无人监督还是有人监督,学习到的特征通常是下游任务的优秀表现。所做的假设是模型的权重以稳健的方式被预先训练。幸运的是,你可以委托谷歌/微软/脸书来做这件事。

在某些情况下,原始图像在输入神经网络时会经历几层连续的变换,其中每个隐藏状态层都会从原始图像中提取新信息。将图像输入网络后,可以直接提取隐藏状态或嵌入作为特征。通常的做法是使用最后一个隐藏状态嵌入作为提取的特征,即监督任务头之前的层。

在这个项目中,我们将研究两个预训练模型:微软的双向编码器图像转换器(BEiT) [3]和脸书的 ConvNext 模型[4]。BEiT-base 和 ConvNext-base 是拥抱人脸图像分类中最受欢迎的两个检查点,在初始测试中与其他选项相比表现良好。由于提取的隐藏状态通常具有比 1 x n 更高的维度,通常的做法是沿着较小的维度取平均值,以每幅图像 1 x n 的嵌入结束。下面,我们从基本 BEiT 得到 1 x 768 大小的嵌入,从基本 ConvNext 得到 1 x 1024 维度的嵌入。为了可视化,嵌入物被任意调整大小为矩形,这揭示了其中的一些不同模式。

**图二。**数据集中四个随机示例的两个已学习特征表示。顶行对应 BEiT Vision Transformer 嵌入件,底行对应 ConvNext 模型嵌入件。这四个斑块分别来自水体(左)、树冠和灌木(中左)、低矮植被(中右)和不透水表面(右)。请注意,这些是从原始的 1 x n 嵌入调整的,以便将它们可视化为矩形面片。

让我们看看数据是如何在已学习的特征空间中可视化呈现的。为此,我们将对 n 个图像嵌入的集合执行 PCA,以将它们转换到二维空间。然后用分类标签着色绘制。

**图三。**BEiT Vision Transformer 嵌入的前两个主要组件,将数据很好地聚类到不同的组中。请注意水、低矮的植被和树冠之间的强烈分隔。虽然看起来有点困难,但较浅的绿色点和黄色点(贫瘠和不透水的表面)与地块下部中心的低植被重叠。理想情况下,这些应该与狼群的其他部分分开。因此,在下面的模型中,我们预计这些类的性能会比其他类差。

**图 4。**conv next 嵌入的前两个主要组成部分,它对数据的聚类几乎与上面的 BEiT Vision Transformer 嵌入一样好。请注意低矮的植被和树冠之间的强烈分离。然而,与图 3 相比,这里的水类差别更小。

see: transformers.BeitModel/ConvNextModel

建模

如果你去看 Kaggle 竞赛笔记本,你会发现今天图像分类最常见的做法是使用预先训练好的神经网络进行迁移学习和微调。在此设置中,首先将权重加载到网络中(迁移学习),然后在感兴趣的新数据集上更新它(微调)。后一步通常运行几个时期,并且具有小的学习权重,以便不会偏离原始权重太远。然而,与使用相同的模型作为特征提取器相比,迁移学习和微调过程通常涉及更多的时间和更多的计算。

下面的前半部分模型是用学习功能和 scikit-learn 模型训练的。我使用了以下软件包来促进整个流程:从特征提取(transformers)到模型训练(sklearn)再到超参数优化(optuna)。对于超参数优化,我搜索了各种逻辑回归和前向神经网络(FFNN ),进行了 10 次随机试验,结果表明,一个隐藏状态的维数为 175-200 的 FFNN 通常是最佳选择。

然后训练迁移学习和微调的神经网络,以便与这些学习的特征模型进行比较,这些特征模型包括模型的第二部分。我使用 transformers 包来微调 BEiT 和 ConvNext 基本模型,检查点与上面的完全相同。使用相同的预训练权重,以便在实验中更接近地比较“苹果与苹果”。

见此处抱抱脸关于图像分类的优秀教程。

see: optuna, sklearn, transformers

模型评估

对于模型评估,我选择在保留的测试集上检查平衡精度、单个类精度和混淆矩阵。混淆矩阵显示了模型出错的地方,有助于解释。每行代表已知存在于给定类中的示例(基本事实),而每列代表由模型分类的示例(预测)。行加起来是事实的数量,列加起来是预测的数量。

—x — x — x—

**模型 1,**拜特嵌入+ sklearn FFNN:

平衡精度… 79.6%

+============+=======+========+============+========+=========+
|            | Water | Trees  | Vegetation | Barren | Manmade |
+============+=======+========+============+========+=========+
| Water      |   ** 64** |      0 |          2 |      0 |       0 |
+------------+-------+--------+------------+--------+---------+
| Trees      |     1 |   **1987** |          3 |      1 |       0 |
+------------+-------+--------+------------+--------+---------+
| Vegetation |     1 |      3 |        **457** |      0 |       0 |
+------------+-------+--------+------------+--------+---------+
| Barren     |     2 |      0 |         14 |      **5** |       3 |
+------------+-------+--------+------------+--------+---------+
| Manmade    |     0 |      0 |          6 |      2 |      **35** |
+------------+-------+--------+------------+--------+---------+

分类精度…水:97.0%,树冠和树木:99.7%,低植被:99.1%,贫瘠:20.8%,不透水表面:81.4%。

Beit embeddings 模型总体表现第三好

— x — x — x —

模型 2 ,ConvNext 嵌入+ sklearn FFNN:

平衡精度… 78.1%

+============+=======+========+============+========+=========+
|            | Water | Trees  | Vegetation | Barren | Manmade |
+============+=======+========+============+========+=========+
| Water      |    **62** |      0 |          4 |      0 |       0 |
+------------+-------+--------+------------+--------+---------+
| Trees      |     2 |   **1982** |          6 |      2 |       0 |
+------------+-------+--------+------------+--------+---------+
| Vegetation |     1 |      3 |        **457** |      0 |       0 |
+------------+-------+--------+------------+--------+---------+
| Barren     |     1 |      1 |         17 |      **4** |       1 |
+------------+-------+--------+------------+--------+---------+
| Manmade    |     0 |      0 |          8 |      0 |      **35** |
+------------+-------+--------+------------+--------+---------+

分类精度…水:93.9%,树冠和树木:99.5%,低植被:99.1%,贫瘠:16.6%,不透水表面:81.4%。

ConvNext 嵌入模型总体表现最差

— x — x — x —

模型 3 ,微调 BEiT 神经网络:

平衡准确度… 82.9%

+============+=======+========+============+========+=========+
|            | Water | Trees  | Vegetation | Barren | Manmade |
+============+=======+========+============+========+=========+
| Water      |    **64** |      0 |          2 |      0 |       0 |
+------------+-------+--------+------------+--------+---------+
| Trees      |     0 |   **1986** |          5 |      1 |       0 |
+------------+-------+--------+------------+--------+---------+
| Vegetation |     2 |      3 |        **455** |      0 |       1 |
+------------+-------+--------+------------+--------+---------+
| Barren     |     0 |      0 |         13 |      **9** |       2 |
+------------+-------+--------+------------+--------+---------+
| Manmade    |     1 |      0 |          6 |      1 |      **35** |
+------------+-------+--------+------------+--------+---------+

分类精度…水:97.0%,树冠和树木:99.7%,低植被:98.7%,贫瘠:37.5%,不透水表面:81.4%。

经过微调的 BEiT 型号总体表现次优

— x — x — x —

模型 4 ,微调的 ConvNext 神经网络:

  • 平衡精度… 84.4%
+============+=======+========+============+========+=========+
|            | Water | Trees  | Vegetation | Barren | Manmade |
+============+=======+========+============+========+=========+
| Water      |    **65** |      0 |          1 |      0 |       0 |
+------------+-------+--------+------------+--------+---------+
| Trees      |     0 |   **1978** |         12 |      2 |       0 |
+------------+-------+--------+------------+--------+---------+
| Vegetation |     1 |      2 |        **457** |      0 |       1 |
+------------+-------+--------+------------+--------+---------+
| Barren     |     0 |      0 |         13 |     **11** |       0 |
+------------+-------+--------+------------+--------+---------+
| Manmade    |     0 |      0 |          7 |      2 |      **34** |
+------------+-------+--------+------------+--------+---------+

分类精度…水:98.5%,树冠和树木:99.3%,低植被:99.1%,贫瘠:45.8%,不透水表面:79.1%。

微调后的 ConvNext 型号总体表现最佳🚀🤖

— x — x — x —

建模流程/限制的改进

建模管道可以在许多方面得到改进,包括接下来讨论的那些。模型在贫瘠的例子的分类中遭受最大的痛苦,所以如果我能做一个改变的话,我会从增加更多这种类型的类开始。实际上,模型更像 4 路分类器,因为空类的性能很差。另一个改进可能是在超参数优化中使用交叉验证;然而,交叉验证将需要更长的时间来运行,并且对于这个实验来说感觉是多余的。

输出模型的一般化限制包括对其他类别类型、其他分辨率和其他条件(新对象、新结构、新类别等)的影像的分类性能恶化。).我已经将经过微调的 ConvNext 和 BEiT 推送到拥抱脸进行托管推理,在这里,人们可以通过加载图像和/或运行每个图像中配置的默认设置来测试模型的可推广性。

项目学习

  1. Python 包领域的知识至关重要。请参见下面的灰色块,了解这里使用的各种库。
  2. 可视化图像特征之间的变化有助于更深入地理解数据集中的信号。
  3. 预训练的嵌入与更简单的模型配对可以表现得几乎和微调的神经网络一样好。
  4. 拥抱脸不仅对自然语言处理很棒,对计算机视觉也很神奇!

结论

这些结果和学习对于今天 CV 中神经网络的状态暗示了什么?让我们倒回去。2017 年,特斯拉自动驾驶部门的前总监 Andrej Karpath 写了一篇现在很有名的博文,内容是关于从旧学校工程到新学校深度学习的转变,他将其称为“软件 2.0”【5】。从这个角度来看,神经网络不是“你机器学习工具箱中的另一个工具”。相反,它们代表了我们开发软件方式的转变。我相信今天的软件 2.0 和五年前一样重要。只要看看 NLP、CV 中可用的开源神经网络的数量,以及来自 AI/ML 顶级研究实验室的数量。对于从业者来说,这是令人兴奋的事情…

引文

[1] K. Kranen (2022 年),21 世纪 20 年代看起来像是在 ML 、LinkedIn 中实现的表征学习承诺的时代。

[2]切萨皮克湾项目办公室(2022)。2017/18 年切萨皮克湾流域 1 米分辨率土地覆盖数据集。由佛蒙特大学空间分析实验室、切萨皮克保护区和美国地质调查局开发。[2022 年 11 月 15 日],[ 网址,

  • 数据集许可:此处使用的数据集对所有人公开,没有限制。更多信息见这里和这里。

[3]鲍,董,李,魏等(2021).贝特:图像转换器的预训练。更正,abs/2106.08254。https://arxiv/abs/2106.08254

[4]刘,赵,毛,h,吴春英,费希滕霍费尔,t .达雷尔,谢,S. (2022)。21 世纪 20 年代的 ConvNet。更正,abs/2201.03545。https://arxiv/abs/2201.03545

[5] A. Karpath (2017),软件 2.0 ,中等。

[6]南尼,l .,吉多尼,s .,&布拉纳姆,S. (2017)。用于计算机视觉分类的手工与非手工特征。模式识别71 ,158–172。doi:10.1016/j . pat cog . 2017 . 05 . 025

— x — x — x —

附录

非习得特征

CV 中的非学习特征可以被认为是那些由图像手工制作的特征[6]。给定问题的最佳非学习特征通常依赖于区分信号在数据集中位置的知识。在提取非学习特征之前,让我们先在 RGB 空间中绘制一些随机补丁。

**图 6。**数据集中三个斑块的可见光谱 RGB 特征:不透水表面 id 3(左)、树冠和灌木 id 4(中)和低植被 id 5(右)。

我们将研究主成分分析(PCA)作为第一个非学习特征。PCA 是一种降维技术——在这里,我们使用它从 128 x 128 x 4 的图像移动到 1 x n 的向量。PCA 变换数据集的大小 n 是用户指定的,并且可以是比数据的原始维度小的任何数字。在幕后,该算法使用特征向量(数据中的传播方向)和特征值(方向的相对重要性)来寻找与原始图像保持最大差异的一组基。一旦计算完成,PCA 可以用来将新图像转换到低维空间和/或在二维或三维空间中可视化图像(见图 3 和图 4)。

在下面的例子中,保持 n =3000 维的 PCA 导致 95%的维数节省,同时保留了来自原始图像的几乎所有信号。为了使 PCA 可视化,我颠倒了操作,并将示例绘制成 128 x 128 像素的图像。

**图 7。**数据集中三个斑块的 PCA 特征(保留 3000 个维度):不透水表面 id3(左)、树冠和灌木 id4(中)和低植被 id 5(右)。

让我们看看另一个老派的特征:梯度直方图(猪)。为了计算 HOG,首先计算图像的梯度(变化强度)和方向。然后,图像被分割成多个单元,其中方向被分层成直方图仓。然后,在一个单元中的每个像素处,我们查找它的方向,在直方图中找到相应的 bin,并将给定的值加到它上面。然后在图像的各个单元上重复这个过程。然后,

看看 HOG 在这里做的很酷的事情:

**图 8。**数据集中三个斑块的 HOG 特征:不透水表面 id 3(左)、树冠和灌木 id 4(中)和低植被 id 5(右)。

虽然这些手工制作的特征作为数据集的第一遍可视化进行检查是有趣的,但事实证明它们对于我们的监督建模目的来说并不太好。这里,最初的测试表明,从 HOG 和 PCA 特征构建的模型相对于从下面探索的学习特征训练的模型在平衡准确性方面发生了显著下降(PCA 下降 35%,HOG 下降 50%)。

see: skimage.feature.hog, sklearn.decomposition.PCA

定义数据规程中的角色分类

原文:https://towardsdatascience/defining-a-taxonomy-of-roles-in-the-data-discipline-5c801c5d9d7

他们的工作和主要职责

布鲁克·卡吉尔在 Unsplash 上的照片

我们生成的数据量呈指数级增长,这为许多机会打开了大门。“数据是新的石油,”你会经常在现代商业活动中听到,而企业已经在行动了。几个工作角色已经崛起,推动各行各业的数据革命。

对许多人来说,数据学科的头衔并没有你想象的那么重要。不同公司职位相同的人的日常工作流程可能会有很大不同——但我不会完全忽略他们。我们慢慢地但肯定地认识到,数据领域不存在独角兽,没有一个人能够在组织中有效地使用数据。

因此,公司已经将责任分解成更专业化的角色。在你职业生涯的开始阶段,了解每个角色的一般职责是非常重要的,因为这将让你深入了解为适应某个角色而必须掌握的必要工具和技能。

说了这么多,让我们来看看涉及的一些主要角色:

数据分析师

数据分析的最终目标是提出一个商业问题的解决方案:他们通过发现可用于制定战略决策的数据模式来寻求提高组织的效率和绩效。因此,数据分析师使用数据来讲述故事,帮助企业基于数据做出更明智的决策。

数据分析师还应具备出色的跨各种媒介(包括视觉、书面和口头)的沟通技能,因为这是报告其结论所必需的。

关键职责

  • 与其他团队成员合作,改进数据收集过程和质量。
  • 创建仪表板和报告。
  • 执行数据分析并报告可以改进的领域的结论,以提高组织或项目的效率。
  • 构建和维护自动化数据流程。
  • 生成并跟踪业务 KPI。
  • 进行数据审计。

数据科学家

数据科学的最终目标是从数据中产生商业洞察力:当前数据用于发现机会。因此,数据科学家应该对企业面临的挑战有很好的理解,并能够基于数据驱动的方法提供解决方案。

由于他们的跨学科专业知识,他们很可能使用机器学习、统计学和数据挖掘衍生的工具和技术处理项目的所有方面,包括数据采集、分析和不同类型数据(即结构化或非结构化数据)的解释。

主要职责

  • 与主题专家(SME)密切合作,确定问题并使用数据提出解决方案。
  • 利用机器学习工具和各种统计技术来解决问题。
  • 数据清理。
  • 源数据来解决业务问题。
  • 跨业务团队、工程团队和产品团队等多个团队的协作。

学习资源*:Data camp 上有*Python R 职业轨迹的数据科学家是一个很好的起点。

数据工程师

数据工程师构建数据管道来准备和转换原始和非结构化数据。管道通常由数据的收集(可能来自各种来源)、处理和存储组成。他们的大部分时间都花在了确保这些管道足够健壮、可靠和值得信赖来输送。

数据工程的最终目标是使数据可访问。换句话说,他们获得了使数据科学和机器学习成为可能的商品:有些人甚至认为他们是数据团队中最重要的成员。

主要职责

  • 设计、开发和维护数据系统和管道。
  • 数据采集。
  • 分析和组织原始数据。
  • 提高数据可靠性和质量。

学习资源*:Data camp 上的数据工程与*Python职业轨迹是一个很好的起点。你可能还想通过 Coursera 上的 IBM 数据工程师职业 证书来扩展你的学习。

数据架构师

"数据架构师 设计并构建数据模型,以满足首席数据架构师 定义的业务战略数据需求。在这一级别,您将:根据数据策略,为数据的升级、管理、退役和归档进行设计、支持并提供指导。 " [ 来源 : GOV.UK 。

  • 确定数据源(内部和外部)并提出数据管理计划
  • 开发和实施整体组织数据策略。
  • 与跨职能团队和利益相关者合作,确保数据系统的平稳运行。
  • 管理端到端数据架构。
  • 审核数据管理系统,并在必要时对其进行改进。

机器学习工程师

机器学习工程的最终目标是将数据转化为产品。这个角色的产生是因为需要在数据科学家的工作(例如,分析和建模)和软件产品的世界(例如,健壮的系统工程)之间架起一座桥梁。

因此,机器学习工程通常被认为是软件工程的一个子领域:除了机器学习要求之外,机器学习工程师和软件工程师的生活方式非常相似——这意味着他们应该是熟悉 ide、GitHub 和 Docker 等工具的熟练程序员。

主要职责

  • 设计和构建机器学习系统。
  • 构建自动化管道来部署机器学习模型。
  • 适当测试机器学习系统并监控其性能。
  • 与数据工程师之类的人一起构建数据和管道模型。

学习资源 : 如何成为机器学习工程师 可以作为学习轨迹参考。

MLOps 工程师

MLOps 是关于将 DevOps 原理应用于机器学习系统的新热潮。因此,MLOps 工程师的重点通常更多地是将机器学习模型部署到生产中,而不是构建它们。

它们以类似于 DevOps 支持软件工程师的方式支持机器学习工程师:工程师将创建软件,Ops 将提供基础设施并确保软件可靠运行。因此,我们可以说 MLOps 工程师负责构建机器学习模型时发生的所有活动。

主要职责

  • 建立和维护 MLOps 管道。
  • 设计和实施云解决方案。
  • 确保机器学习应用程序可以通过 Docker 和 Kubernetes 等工具进行扩展。

我们暂时就此打住。

:你会在各个公司发现基于数据的其他几个角色(即数据说书人、机器学习研究员、 机器学习科学家 等)。).我推荐你使用 Linkedin Jobs、的确、Glassdoor、data camp等求职板进行调研。

所提供的工作清单并不全面,只能作为指南。读者应该利用这些信息,着手研究每个角色所需的技术组合,并建立一个投资组合。要记住的一件关键事情是,不同的公司以不同的方式组织他们的团队:不同的技术术语可能被用来描述两家不同公司的同一份工作。

感谢阅读。

联系我:
LinkedIn
Twitter
insta gram

如果你喜欢阅读这样的故事,并希望支持我的写作,可以考虑成为一名灵媒。每月支付 5 美元,你就可以无限制地阅读媒体上的故事。如果你使用我的注册链接,我会收到一小笔佣金。

已经是会员了?订阅在我发布时得到通知。

https://kurtispykes.medium/subscribe

在 Python 中定义空变量和数据结构

原文:https://towardsdatascience/defining-empty-variables-and-data-structures-in-python-501995458577

定义缺失值、默认函数参数和初始化数据结构

图片由在像素上拍摄制作

在处理大量数据集合时,经常会遇到缺少值的情况。缺失值可能对应于空变量、空列表、空字典、列中缺失的元素、空数据帧甚至无效值。能够定义空变量或对象对于许多软件应用程序来说非常重要,尤其是在处理缺失和无效的数据值时。这对于变量初始化、类型检查和指定函数默认参数等任务非常重要。

根据不同的用例,有几个选项可以指定空变量。最常见的方法是使用关键字 None 存储空值。这很有用,因为它清楚地表明变量值缺失或无效。虽然这有助于处理丢失的值,但在需要计算的情况下这是没有用的。例如,对于分类值、浮点值和整数值,将缺失值 None 转换为安南值通常很有用。安南值对应于“不是一个数字”,它是一种标记缺失值的有用方法,同时仍然能够执行有用的计算。例如,我们可以使用 python 中的 pandas 方法,用均值、中值或众数等统计数据替换 NaN 值。

除了指定 None 和 NaN 类型之外,指定空数据结构也非常有用。例如,如果在 python 中填充一个列表,通常需要定义一个初始化的空列表。此外,您可能有一个函数需要一个列表作为输入,以便无论列表是填充的还是空的都可以正常运行。在填充字典方面也可以进行类似的论证。定义一个空字典,然后用对应于它们各自值的键填充字典的逻辑通常是有用的。与列表类似,您可以定义一个函数,该函数需要列出一个空字典才能成功运行。同样的逻辑也适用于数据帧。

最后,定义空变量和数据结构对于类型检查和设置默认参数等任务非常有用。在类型检查方面,空变量和数据结构可以用来通知一些控制流逻辑。例如,如果呈现一个空数据结构,则执行“X”逻辑来填充该数据结构。在类型检查和设置默认参数方面,可能会有这样的情况,空数据结构的实例应该启动一些逻辑,从而允许函数调用在意外情况下成功。例如,如果您定义了一个函数,它在不同的浮点数列表上被调用多次,并计算平均值,只要该函数提供了一个数字列表,它就会工作。相反,如果函数提供了一个空列表,它将失败,因为它将无法计算空列表的平均值。然后,可以使用类型检查和默认参数来尝试计算并返回平均值,如果失败,则返回默认值。

用 None 和 NaN 定义一个空变量

在 python 中定义空变量很简单。如果希望为不会用于计算的缺失值定义一个占位符,可以使用 None 关键字定义一个空变量。例如,假设我们有包含年龄、收入(美元)、姓名和老年公民身份值的人口统计数据:

age1 = 35
name1 = "Fred Philips"
income1= 55250.15
senior_citizen1 = Falseage2 = 42
name2 = "Josh Rogers"
income2=65240.25
senior_citizen2 = Falseage3 = 28
name3 = "Bill Hanson"
income3=79250.65
senior_citizen3 = False

对于每个人,我们都有年龄、姓名、收入和高级公民身份的有效值。可能存在某些信息缺失或包含无效值的情况。例如,我们可能会收到包含无效值的数据,如年龄的字符或字符串,或姓名的浮点或整数。使用自由文本用户输入框的 web 应用程序尤其会出现这种情况。如果应用程序无法检测到无效的输入值并提醒用户,它会将无效的值包含在其数据库中。考虑下面的例子:

age4 = "#"
name4 = 100
income4 = 45250.65
senior_citizen4 = "Unknown"

对于这个人,我们的年龄值为“#”,这显然是无效的。进一步说,输入的名字是 100 的整数值,同样没有意义。最后,对于我们的老年公民变量,我们有“未知”。如果我们对保留这些数据感兴趣,因为收入是有效的,所以最好使用 None 关键字将年龄、姓名和 senior_citizen 定义为空变量。

age4 = None
name4 = None
income4 = 45250.65
senior_citizen4 = None

通过这种方式,任何查看数据的开发人员都会清楚地了解到缺少年龄、姓名和老年人的有效值。此外,收入值仍然可以与所有其他有效数据值一起用于计算统计数据。None 关键字的一个限制是它不能用于计算。例如,假设我们想要计算我们定义的四个实例的平均年龄:

avg_age = (age1 + age2 + age3 + age4)/4

如果我们尝试运行我们的脚本,它将抛出以下错误:

作者截图

这是一个类型错误,说明我们无法在整数和 None 值之间使用“+”运算符(加法)。

我们可以通过使用 numpy 中的 NaN(非数字)值作为缺失值占位符来解决这个问题:

age4 = np.nan
name4 = np.nan
income4 = 45250.65
senior_citizen4 = np.nan
avg_age = (age1 + age2 + age3 + age4)/4

现在它将能够成功运行。因为我们的计算中有一个 NaN,所以结果也是 NaN。这非常有用,因为代码能够成功运行。此外,这在处理数据结构(如数据帧)时尤其有用,因为 python 中有一些方法允许您直接处理 NaN 值。

除了定义空变量之外,在变量中存储空数据结构通常也很有用。这有许多用途,但我们将讨论如何使用默认的空数据结构进行类型检查。

为初始化定义空列表

在变量中存储空列表的最简单的应用是初始化将要填充的列表。例如,我们可以为先前定义的每个属性初始化一个列表(年龄、姓名、收入、高级状态):

ages = []
names = []
incomes = []
senior_citizen = []

然后可以使用 append 方法填充这些空列表:

ages.append(age1)
ages.append(age2)
ages.append(age3)
ages.append(age4)
print("List of ages: ", ages)

对于姓名、收入和高级地位,我们也可以这样做:

names.append(name1)
names.append(name2)
names.append(name3)
names.append(name4)
print("List of names: ", names)incomes.append(income1)
incomes.append(income2)
incomes.append(income3)
incomes.append(income4)
print("List of incomes: ", incomes)senior_citizen.append(income1)
senior_citizen.append(income2)
senior_citizen.append(income3)
senior_citizen.append(income4)
print("List of senior citizen status: ", senior_citizen)

作者截图

为初始化定义空字典

我们也可以使用空字典进行初始化:

demo_dict = {}

并使用我们之前填充的列表来填充字典:

demo_dict['age'] = ages
demo_dict['name'] = names
demo_dict['income'] = incomes
demo_dict['senior_citizen'] = senior_citizen
print("Demographics Dictionary")
print(demo_dict)

作者截图

为初始化定义空数据帧

我们也可以对数据帧做类似的事情:

import pandas as pddemo_df = pd.DataFrame()
demo_df['age'] = ages
demo_df['name'] = names
demo_df['income'] = incomes
demo_df['senior_citizen'] = senior_citizen
print("Demographics Dataframe")
print(demo_df)

作者截图

请注意,填充字典和数据框的逻辑是相似的。您使用哪种数据结构取决于您作为工程师、分析师或数据科学家的需求。例如,如果您喜欢生成 JSON 文件并且不需要数组长度相等,则字典更有用,而数据帧对于生成 CSV 文件更有用。

NaN 默认函数参数

定义空变量和数据结构的另一个用途是用于默认函数参数。

例如,考虑一个计算联邦税后收入的函数。到目前为止,我们定义的收入范围的税率约为 22%。我们可以将我们的函数定义如下:

def income_after_tax(income):
    after_tax = income — 0.22*income
    return after_tax

如果我们用 income 调用我们的函数并打印结果,我们得到如下结果:

after_tax1 = income_after_tax(income1)
print("Before: ", income1)
print("After: ", after_tax1)

作者截图

对于这个例子来说,这很好,但是如果我们有一个无效的收入值,比如一个空字符串,该怎么办呢?让我们传入一个空字符串,并尝试调用我们的函数:

after_tax_invalid = income_after_tax(‘’)

作者截图

我们得到一个 TypeError,说明我们可以将一个空字符串乘以一个非整数类型的 float。函数调用失败,after_tax 实际上从未被定义。理想情况下,我们希望保证该函数适用于任何收入值,并且 after_tax 至少用某个默认值来定义。为此,我们可以为 after_tax 定义一个默认的 NaN 参数,并键入 check the income。如果收入是浮动的,我们只计算税后,否则,税后是 NaN:

def income_after_tax(income, after_tax = np.nan):
    if income is float:
        after_tax = income — 0.22*income
    return after_tax

然后我们可以传递任何无效的有效收入,我们仍然能够成功地运行我们的代码:

after_tax_invalid1 = income_after_tax('')
after_tax_invalid2 = income_after_tax(None)
after_tax_invalid3 = income_after_tax("income")
after_tax_invalid4 = income_after_tax(True)
after_tax_invalid5 = income_after_tax({})print("after_tax_invalid1: ", after_tax_invalid1)
print("after_tax_invalid2: ", after_tax_invalid2)
print("after_tax_invalid3: ", after_tax_invalid3)
print("after_tax_invalid4: ", after_tax_invalid4)
print("after_tax_invalid5: ", after_tax_invalid5)

作者截图

读者可能想知道为什么一开始就把一个无效值传递给一个函数。实际上,函数调用通常是针对成千上万的用户输入进行的。如果用户输入是自由文本响应,而不是下拉菜单,则很难保证数据类型是正确的,除非应用程序明确强制执行。因此,我们希望能够在应用程序不崩溃或失败的情况下处理有效和无效的输入。

空列表默认函数参数

将空数据结构定义为默认参数也很有用。让我们考虑一个函数,它获取我们的收入列表并计算税后收入。

def get_after_tax_list(input_list):
    out_list = [x — 0.22*x for x in input_list]
    print("After Tax Incomes: ", out_list)

如果我们把这个和我们的收入清单联系起来,我们会得到:

get_after_tax_list(incomes)

作者截图

现在,如果我们用一个不是列表的值调用它,例如一个整数,我们得到:

get_after_tax_list(5)

作者截图

现在,如果我们包含一个空列表作为输出列表的默认值,我们的脚本将成功运行:

get_after_tax_list(5)

作者截图

空字典默认函数参数

与将默认参数定义为空列表类似,用空字典默认值定义函数也很有用。让我们定义一个接受输入字典的函数,我们将使用我们之前定义的 demo_dict,它返回一个包含平均收入的新字典

def get_income_truth_values(input_dict):
    output_dict= {'avg_income': np.mean(input_dict['income'])}
    print(output_dict)
    return output_dict

让我们用 demo_dict 调用我们的函数

get_income_truth_values(demo_dict)

作者截图

现在让我们尝试为 input_dict 传入一个无效值。让我们传递整数值 10000:

get_income_truth_values(10000)

作者截图

我们得到一个类型错误,指出整数对象 1000 是不可订阅的。我们可以通过检查输入的类型是否是字典,检查字典中是否有适当的键,并为输出字典设置一个默认参数来纠正这一点,如果不满足前两个条件,将返回该参数。这样,如果条件不满足,我们仍然可以成功地运行我们的代码,而不会出现错误。对于我们的默认参数,我们将简单地为 output_dict 指定一个空字典

def get_income_truth_values(input_dict, output_dict={}):
    if type(input_dict) is dict and ‘income’ in input_dict:
        output_dict= {‘avg_income’: np.mean(input_dict[‘income’])}
    print(output_dict)
    return output_dict

我们可以成功地调用相同的函数

get_income_truth_values(10000)

我们还可以为“avg_income”定义一个带有安南值的默认字典。这样,我们将保证我们有一个包含预期键的字典,即使我们用无效的输入调用我们的函数:

def get_income_truth_values(input_dict, output_dict={'avg_income': np.nan}):
    if type(input_dict) is dict and ‘income’ in input_dict:
        output_dict= {'avg_income': np.mean(input_dict['income'])}
    print(output_dict)
    return output_dictget_income_truth_values(demo_dict)
get_income_truth_values(10000)

作者截图

空数据框默认函数参数

与我们的列表和字典示例类似,带有默认空数据框的默认函数非常有用。让我们修改我们定义的数据框,以包含每个人的居民状态:

demo_df['state'] = ['NY', 'MA', 'NY', 'CA']

让我们也使用平均值估算年龄和收入的缺失值:

demo_df['age'].fillna(demo_df['age'].mean(), inplace=True)
demo_df['income'].fillna(demo_df['income'].mean(), inplace=True)

接下来,让我们定义一个函数,该函数对各州执行 groupby,并计算年龄和收入字段的平均值。结果将使用每个州的平均年龄和收入:

def income_age_groupby(input_df):
    output_df = input_df.groupby(['state'])['age', 'income'].mean().reset_index()
    print(output_df)
    return output_dfincome_age_groupby(demo_df)

作者截图

你应该已经猜到了,如果我们用一个不是 dataframe 的数据类型调用我们的函数,我们会得到一个错误。如果我们传递一个列表,我们会得到一个 AttributeError,说明列表对象没有属性“groupby”。这是有意义的,因为 groupby 方法属于 dataframe 对象:

income_age_groupby([1,2,3])

作者截图

我们可以为每个预期字段定义一个包含 nan 的默认数据框,并检查必要的列是否存在:

def income_age_groupby(input_df, output_df = pd.DataFrame({'state': [np.nan], 'age': [np.nan], 'income':[np.nan]})):
    if type(input_df) is type(pd.DataFrame()) and set(['age', 'income', 'state']).issubset(input_df.columns):
        output_df = input_df.groupby(['state'])['age', 'income'].mean().reset_index()
        print(output_df)
    return output_dfincome_age_groupby([1,2,3])

作者截图

我们看到我们的代码在无效数据值的情况下成功运行。虽然我们考虑了我们制作的数据示例,但是这些方法可以扩展到各种数据处理任务,无论是软件工程、数据科学还是机器学习。我鼓励您在自己的数据处理代码中尝试应用这些技术!

这篇文章中的代码可以在 GitHub 上找到。

结论

定义空变量和数据结构是处理缺失或无效值的重要部分。对于浮点、整数、布尔和字符串等变量,无效类型通常会导致代码失败或出错。这可能导致程序在大型处理任务中途崩溃,从而导致时间和计算资源的巨大浪费。考虑到处理无效和丢失的数据是数据处理的一个重要部分,理解如何将空变量和数据结构定义为函数默认值可以省去工程师或数据科学家的许多麻烦。能够用合理的默认值定义函数,使它们返回一致的、预期的无错误输出,这是每个程序员的基本技能。

本帖原载于 内置博客 。原片可以在 这里找到

定义可信的人工智能

原文:https://towardsdatascience/defining-trustworthy-ai-234a97c39035

播客

Beena Ammanath 谈到公司可以采取哪些步骤来建立更可靠的系统

苹果 | 谷歌 | SPOTIFY | 其他

编者按:TDS 播客由杰雷米·哈里斯主持,他是人工智能安全初创公司墨丘利的联合创始人。每周,Jeremie 都会与该领域前沿的研究人员和商业领袖聊天,以解开围绕数据科学、机器学习和人工智能的最紧迫问题。

值得信赖的人工智能是当今最流行的流行语之一。但是,尽管每个人似乎都同意我们希望人工智能是值得信任的,但值得信任的定义往往是模糊的或不充分的。也许这不应该令人惊讶:很难提出一套标准来衡量“可信度”,并像自动驾驶汽车一样适用于网飞电影推荐。

因此,也许值得信赖的人工智能需要以一种更微妙的方式来思考——一种反映个体人工智能用例复杂性的方式。如果这是真的,那么新的问题就来了:谁来定义可信度,当缺乏可信度导致像人工智能事故或不良偏见这样的伤害时,谁来承担责任?

从这个角度来看,可信度不仅是算法的问题,也是组织的问题。这正是 Beena Ammanath 在她即将出版的新书 Trustworthy AI 中提出的情况,该书从实用的角度探索了人工智能的可信度,研究了公司可以采取哪些具体步骤来使他们的内部人工智能工作得更安全、更好、更可靠。在这一集的 TDS 播客中,Beena 和我一起谈论了人工智能中的可信度、可解释性和鲁棒性的定义,以及人工智能监管和自我监管的未来。

以下是我在对话中最喜欢的一些观点:

  • 对 Beena 来说,人工智能的可信度意味着避免不必要的副作用。这些可能包括对用户的身体伤害,如人工智能事故,或者更无形的问题,如偏见。这是一个总括术语,它综合了许多其他概念,每个概念都需要以自己的方式处理,有些概念对一个应用程序或公司来说比另一个更重要。
  • Beena 认为可信度不是人工智能系统的属性,而是一个组织(可能包括人工智能系统)的属性。她的思想的一个重要方面是,只有当组织内的人类行为者被分配了对各种人工智能相关风险的明确所有权和责任时,可信性才是可能的。如果一辆自动驾驶汽车撞死了一名行人,谁负责?这辆车的主人?开发其视觉或决策算法的工程师?他们的经理或董事——或者公司的首席执行官呢?管理一个可信的人工智能过程意味着澄清那种所有权。
  • 在 Beena 看来,没有一套定义可信人工智能的程序。但所有公司都应该坚持一些原则:例如,公司应该有明确的程序,鼓励对人工智能开发的风险进行思考。像模型车或算法影响评估这样的工具在这里可以有所帮助,像红队这样的练习也可以,在红队中,参与人工智能系统开发的员工积极地试图让他们更好地了解自己的弱点。
  • Beena 担心,随着人工智能能力继续加速,监管机构可能难以跟上。因此,企业似乎有可能不得不认真对待自我监管的过程,以避免造成大规模伤害,这有可能导致品牌损害或过度的监管膝跳反应,从而使行业倒退。
  • Beena 的书《值得信赖的 AI》现在已经出版,在各地的网上书店都可以买到:)

你可以在 Twitter 上关注 Beena 这里,或者 me 这里。

章节:

  • 0:00 介绍
  • 1:55 背景和值得信赖的人工智能
  • 7:30 激励员工努力提高能力
  • 13:40 应用领域级别的法规
  • 16:45 弥合差距
  • 23:30 认知水平转移到人工智能
  • 25:45 什么是值得信赖的 AI?
  • 34:00 鲁棒性故障示例
  • 36:45 团队多元化
  • 40:15 较小的公司
  • 最佳做法的应用
  • 46:30 总结

使用 AWS 和 Python 在云上创建 SQL 数据库的权威指南

原文:https://towardsdatascience/definitive-guide-to-create-an-sql-database-on-cloud-with-aws-and-python-c818c7270af2

关于使用 Amazon RDS、MySQL Workbench 和 PyMySQL 在云中为 Python 应用程序构建和部署数据库的简单易懂的综合指南

照片由卢卡斯劳在 Unsplash

我们经常会从我们在数据科学工作或个人项目中构建的 Python 应用程序中生成和收集有价值的数据。

因此,拥有一个可伸缩且高性能的附带数据库是至关重要的,这样就可以有效地存储、组织和查询数据。

幸运的是,现代云服务让我们可以轻松地在云上创建经济高效、可调整大小和完全托管的数据库。

这个简单的分步指南解释了如何集成亚马逊 RDSMySQL Workbench、PyMySQL 来开始免费构建和部署云数据库

内容

(1)工具概述(2)设置亚马逊 RDS(3)设置 MySQL 服务器创建数据库模式和表设置 PyMySQL【7】使用 PyMySQL 读**

所有图片和截图均由作者提供,除非另有说明

(1)工具概述

(一)MySQL

MySQL 徽标|根据知识共享 署名-共享 3.0 许可使用的图像

MySQL 是基于 SQL 的开源关系数据库管理系统(RDBMS),被广泛认为是世界上最流行的 RDBMS。

它允许我们轻松地创建、存储、访问和修改数据库中的关系数据。

(二)亚马逊 RDS

AWS 标志| AWS 标志是 Amazon 公司或其附属公司在美国和其他国家的商标,来源: AWS 联合营销

亚马逊关系数据库服务(RDS) 是一种云服务,可以轻松在云端设置、操作和扩展关系数据库。

使用 Amazon RDS 的好处包括:

  • 经济高效且可扩展的硬件容量
  • 自动化耗时的数据库任务,如扩展、监控和软件修补
  • 出色的功能,如快速性能、高可用性、兼容性和安全性。
  • 只需点击几下鼠标,即可在几分钟内快速部署服务器

Amazon RDS 支持六个数据库引擎,我们将使用一个用于 MySQL

(三)PyMySQL

PyMySQL 是一个纯 Python 的 MySQL 客户端库,允许我们访问和修改 MySQL 数据库(例如 CRUD 操作)。

这个包是 Python 应用程序和 MySQL 数据库之间的重要链接。

(2)设置亚马逊 RDS

步骤 1 —创建数据库

登录 AWS 管理控制台后,在顶部搜索栏输入’ rds ',点击第一个显示 RDS 的搜索结果。

点击数据库进入左侧菜单栏中的数据库部分。从那里,点击橙色按钮’ Create database '来创建我们在 AWS 上的第一个关系数据库。**

步骤 2-修改实例设置

从现在开始,我们将修改数据库实例的设置。首先,我们选择 MySQL 作为我们的数据库引擎。

我们选择“自由层”模板选项,开始为RDS 自由层上的自由层提供托管云数据库服务。

此外,决定数据库实例的名称(在 DB 实例标识符下)。我将它命名为’客户端数据库’,但是我们总是可以定制它。

由于我们是从本地机器开始这个项目,我们想通过公共互联网连接到数据库。因此,我们为公共访问选择了*。*****

因此,对于 生产 实例来说,保持谨慎是至关重要的,在这些实例中,我们可能希望在没有公共访问的情况下限制与锁定 VPC 的连接。

对于上面没有提到的其他设置,我们可以保留的默认值。**

对于凭证* 设置,选择一个安全易记的用户名和密码。这一部分非常重要,因为稍后需要凭证来建立到数据库的认证连接。***

完成所有必要的更改后,滚动到底部并单击橙色的“创建数据库按钮。

在我们的实例出现在数据库部分之前,数据库创建需要几分钟时间。**

步骤 3 —编辑安全组配置

为了确保成功连接到数据库,我们必须修改我们的虚拟私有云(VPC) 上的安全* 组配置,以允许公共互联网访问。***

通过首先单击我们刚刚创建的数据库链接(即 DB identifier 下的**client-database** )来完成修改。在随后的页面中,点击 VPC 安全组下的**default** 链接。

接下来,选择入站*子选项卡,并单击右下角的“*编辑入站规则”按钮。

我们单击“ Add rule ”添加一个新的入站规则,允许我们在任何地方访问数据库,只要我们有正确的密码验证。

新规则应该具有’类型所有流量*,以及’ Anywhere-IPv4。最后,点击橙色的’*保存规则’按钮保存新规则。

完成以上工作后,我们已经成功地在 AWS RDS 上建立了第一个 MySQL 数据库 实例

3)设置 MySQL 服务器

步骤 1 —下载和安装

在使用 MySQL 之前,我们先在本地机器上安装两个 MySQL 软件——MySQL社区服务器和 MySQL 工作台**

(I)MySQL社区服务器 是免费使用的 RDBMS 服务器,支持 MySQL 的查询和连接功能。我们可以把它想象成安装在本地机器上的 MySQL 数据库引擎。

【ii】MySQLwork bench是使用 MySQL 服务器和数据库的统一图形工具。这个可视化软件使得设计、建模、生成和管理我们的数据库变得容易。**

我们可以在 MySQL 下载 页面找到安装程序链接,下面圈出了两个工具的正确链接。

单击该链接后,它会提示我们根据我们的操作系统(OS)选择特定的安装程序。

不需要为安装创建 Oracle Web 帐户,所以我们可以直接点击“不,谢谢,开始下载吧

注:如果你用的是 Windows OS ,我强烈推荐下载MySQL Installer for Windows(如推荐下载下的横幅所示当你选择微软 Windows )。然后我们可以使用 MySQL 安装程序来下载 MySQL 社区服务器和 MySQL 工作台。

步骤 2—运行安装

下载安装程序后,我们运行它们并遵循后续的安装步骤。

在安装 MySQL 服务器时,我们会遇到多种服务器配置选项。好消息是我们可以保留所有的默认值。

关键的配置步骤是设置(并记住)一个强 MySQL 根密码

(4) 将 RDS 数据库实例连接到 MySQL 工作台

到目前为止,安装非常成功!我们现在准备连接到第 3 节中创建的 RDS 数据库实例。

我们首先启动 MySQL Workbench ,在那里我们会看到欢迎页面。要建立一个新的MySQL 连接,点击 MySQL 连接标题旁边的 按钮。****

在弹出的对话框中,我们需要修改几个连接参数。

注意:以下参数的值可以在 RDS 控制台的数据库部分的数据库实例(即**client-database**)中找到(参见第 3 节)。

  • 连接名称:输入连接的自定义名称,如**rds_connection_1**
  • ****主机名:输入 RDS 端点。该信息可在连接和安全选项卡中找到。

  • 端口:输入端口号(默认值应该已经是 3360 )。该信息可在连接和安全选项卡中找到。
  • 用户名:输入主用户名。该信息可在配置选项卡中找到。

  • 密码:点击存储到金库*,输入 RDS 实例凭证设置的密码(参见第 2 节的步骤 2)。***

完成的参数如下所示:

***接下来,点击’*测试连接’验证配置。如果我们正确执行了这些步骤,我们应该会看到一个弹出窗口,指示连接成功:

最后,从设置新连接窗口点击确定保存连接。**

(5)创建数据库模式和表

步骤 1 —创建模式

在第 4 节之后,我们应该在工作台欢迎页面上看到新的连接。

点击新建框(即 rds_connection_1 )以打开连接并访问数据库。**

在创建新表之前,我们首先定义一个模式*。单击顶部菜单中的’*创建新模式’按钮(下面用绿色圈出),并为模式命名(如**schema1**)。

然后,我们在随后的屏幕上单击“应用按钮,我们将看到我们的新模式出现在左侧菜单的模式中。**

步骤 2 —创建表格

要创建一个新表,单击新模式旁边的箭头展开子菜单,右键单击选项,并选择“创建表”。**

假设我们要创建一个表,在以下列中存储站点访问信息:

我们可以添加列并设置相应的选项来构建一个新的表(我们可以相应地命名它,例如 tblClients )。**

在接下来的几个屏幕中单击’ Apply ,执行创建表的 SQL 命令,我们的新表将出现在 SCHEMAS 菜单中。

我们还可以运行一个简单的 SQL 查询来确认成功创建。

(6)设置 PyMySQL

为了将 RDS MySQL 实例链接到后续的 Python 脚本,我们可以使用 PyMySQL。我们可以用 pip 安装它:

*pip install PyMySQL*

(7)使用 PyMySQL 对数据库进行读写

我们已经到了最后一个阶段,开始使用 Python 访问 MySQL 数据库中的数据。

步骤 1 —下载 SSL 证书包

为了加密在本地客户机和 RDS 数据库实例之间移动的数据,我们需要实现一个安全套接字层(SSL)连接。

我们可以从:https://S3 . Amazon AWS . com/rds-downloads/rds-combined-ca-bundle . PEM下载 SSL 证书,然后放置下载的。 pem 在指定的项目文件夹中捆绑文件,例如**ssl/**rds-combined-ca-bundle.pem

步骤 2-设置连接参数

然后我们创建一个 config.py 文件来存储数据库连接的参数。

大多数参数都可以在 Amazon RDS 控制台中找到,我们现在应该很熟悉了。

SSL_CA 变量应该指向我们的 SSL 证书包的路径,并且 CURSORCLASS 变量应该设置为pymysql.cursors.DictCursor

重要:安全地存储密码凭证永远不要让它们在公共 Python 文件中打开。****

步骤 3—启动 RDS 连接

我们用包含 PyMySQL 的 Python 代码启动到 RDS 数据库实例的连接。

步骤 4 —运行 CRUD 操作

一旦建立了连接,我们就可以编写函数来执行 CRUD(即创建、读取、更新、删除)SQL 操作。

例如,下面的函数将单个记录插入到我们之前创建的表tblClients中。****

****提示:我们甚至可以通过 PyMySQL 而不是 MySQL Workbench 直接创建表格。更多信息参见此示例。

步骤 5 —验证数据库更改

有两种方法可以检查所执行的 SQL 操作是否在数据库中成功注册。

(i) 直接在 MySQL Workbench 中运行 SQL 查询

(ii) 通过 PyMySQL 在 Python 中运行 SQL 查询

输出将显示我们刚刚插入到表中的记录:

包装它

至此,我们已经完成了在云中使用 AWS RDS 和 PyMySQL 创建 MySQL 数据库的演练。

你可以在这个 GitHub repo 中找到样例代码和配置文件。

在你走之前

欢迎您加入我的数据科学学习之旅!点击此媒体页面,查看我的 GitHub ,了解更多精彩的教育数据科学内容。同时,享受在云上创建 SQL 数据库的乐趣吧!

使用 Pandas drop()从数据帧中删除行和列

原文:https://towardsdatascience/delete-rows-and-columns-from-a-dataframe-using-pandas-drop-d2533cf7b4bd

掌握熊猫滴 9 招()加速你的数据分析

照片由伯纳德·赫尔曼在 Unsplash 上拍摄

数据操作指的是调整数据以使其有组织且更易于阅读的过程。通常,有些数据是不可用的,会干扰重要的数据。应该清理和删除不必要或不准确的数据。

来源于 solvexia[1]

从 Pandas 数据帧中删除一个或多个行/列可以通过多种方式实现。其中,最常见的就是drop()法。这种方法似乎很容易使用,但是仍然有一些技巧你应该知道,以加快你的数据分析。

在本文中,您将学习熊猫drop()处理以下用例的技巧:

  1. 删除单行
  2. 删除多行
  3. 基于行位置和自定义范围删除行
  4. 删除单个列
  5. 删除多列
  6. 基于列位置和自定义范围删除列
  7. 使用多索引数据框架
  8. inplace=True做操作到位
  9. error='ignore'抑制错误

请查看笔记本获取源代码。更多教程可从 Github Repo 获得。

1.删除单行

默认情况下,熊猫drop()会根据它们的索引值删除该行。通常,每行的索引值是一个从 0 开始的整数值。指定行索引会删除它,例如删除索引值为1的行。:

df.**drop(1)**# It's equivalent to
df.**drop(labels=1)**

使用 Pandas drop()删除一行(图片由作者提供)

注意,删除行必须将参数axis设置为0(在 Pandas drop()中,axis默认为0,所以可以省略)。如果指定了axis=1,它将删除列。

或者,从 DataFrame 中删除一行的更直观的方法是使用index参数。

# A more intuitive way
df.drop(**index=1**)

使用 Pandas drop()删除一行(图片由作者提供)

2.删除多行

熊猫drop()可以带列表删除多行:

df.drop(**[1,2]**)# It's equivalent to
df.drop(**labels=[1,2]**)

使用 Pandas drop()删除多行(图片由作者提供)

类似地,删除多行的更直观的方法是将一个列表传递给index参数:

# A more intuitive way
df.drop(**index=[1,2]**)

使用 Pandas drop()删除多行(图片由作者提供)

3.基于行位置和自定义范围删除行

数据帧索引值可能不是升序,有时它们可以是任何其他值,例如日期时间或字符串标签。对于这些情况,我们可以根据行的位置删除行,例如,删除第二行,我们可以调用df.index[**1**]并将其传递给index参数:

df.drop(index=**df.index[1]**)

根据行位置删除行(作者图片)

要删除最后一行,我们可以使用快捷方式,如标识最后一个索引的-1:

df.drop(index=**df.index[-1]**)

根据行位置删除行(作者图片)

例如,我们还可以使用切片技术来选择一系列行

  • 删除最后 2 行df.drop(index=df.index[**-2:**])
  • 删除每隔一行df.drop(index=df.index[**::2**])

根据行位置删除行(作者图片)

如果您想了解关于切片技术以及如何使用行索引来选择数据的更多信息,可以查看本文:

4.删除单个列

类似于删除行,Pandas drop()可以通过将axis参数指定给1来删除列:

df.drop(**'math', axis=1**)# It's equivalent to
df.drop(**labels='math', axis=1**)

使用 Pandas drop()删除单个列(图片由作者提供)

从 DataFrame 中删除列的更直观的方法是使用columns参数。

# A more intuitive way
df.drop(**columns='math'**)

使用 Pandas drop()删除单个列(图片由作者提供)

5.删除多列

类似地,我们可以通过一个列表来删除多个列:

df.drop(**['math', 'physics']**, **axis=1**)# It's equivalent to
df.drop(**labels=['math', 'physics']**, **axis=1**)

使用 Pandas drop()删除多个列(图片由作者提供)

删除多列的一个更直观的方法是将一个列表传递给columns参数:

# A more intuitive way
df.drop(**columns=['math', 'physics']**)

使用 Pandas drop()删除多个列(图片由作者提供)

6.基于列位置和自定义范围删除列

我们可以根据列的位置删除一列,例如,删除第二列,我们可以调用df.**column**[**1**]并将其传递给columns参数:

df.drop(**columns=df.columns[1]**)

根据列的位置删除列(图片由作者提供)

要删除最后一列,我们可以使用快捷方式,如标识最后一个索引的-1:

df.drop(columns=**df.columns[-1]**)

根据列的位置删除列(图片由作者提供)

类似地,我们也可以使用切片技术来选择一系列列,例如

  • 删除最后 2 列df.drop(columns=df.columns[**-2:**])
  • 删除每隔一栏df.drop(columns=df.columns[**::2**])

根据列的位置删除列(图片由作者提供)

7.使用多索引

一个 MultiIndex (也称为层次索引)数据帧允许我们将多列作为一个行标识符,将多行作为一个标题标识符:

(图片由作者提供)

当在多索引数据帧上调用 Pandas drop()时,默认情况下,它将删除 0 级索引和列。

# Delete all Oxford rows
df.drop(index='Oxford')# Delete all Day columns
df.drop(columns='Day')

多指数中的熊猫下降()

要指定要删除的级别,我们可以设置level参数:

# remove all 2019-07-04 row at level 1
df.drop(index='2019-07-04', **level=1**)# Drop all Weather column at level 1
df.drop(columns='Weather', **level=1**)

多指数中的熊猫下降()

在某些情况下,我们希望删除特定的索引或列组合。为此,我们可以将一个元组传递给indexcolumns参数:

# drop the index combination 'Oxford' and '2019-07-04'
df.drop(**index=('Oxford', '2019-07-04')**)# drop the column combination 'Day' and 'Weather'
df.drop(**columns=('Day', 'Weather')**)

多指数中的熊猫下降()

如果您想了解更多关于在多索引数据框架中访问数据的信息,请查阅本文:

[## 在熊猫的多索引数据框架中访问数据

towardsdatascience](/accessing-data-in-a-multiindex-dataframe-in-pandas-569e8767201d)

8.用inplace=True进行就地操作

默认情况下,Pandas drop()返回结果的副本,而不会影响给定的数据帧。我们可以设置参数inplace=True来就地执行操作,以避免额外的重新分配并减少内存使用。

9.用error='ignore'抑制错误

您可能会注意到,当给定的行或列不存在时,熊猫drop()会抛出一个错误。我们可以设置参数error='ignore'来抑制错误。

结论

在本文中,我们介绍了 9 个使用熊猫drop()删除行和列的用例。该方法本身使用起来非常简单,是数据预处理中操作数据的最受欢迎的方法之一。

感谢阅读。请查看笔记本获取源代码,如果您对机器学习的实用方面感兴趣,请继续关注。更多教程可从 Github Repo 获得。

参考

[1] 数据操作的 5 个技巧

使用 Streamlit 演示您的模型

原文:https://towardsdatascience/demo-your-model-with-streamlit-a76011467dfb

如何在没有任何后端或前端知识的情况下快速部署您的计算机视觉模型

蒂姆·高在 Unsplash 上拍摄的照片

任务

假设你需要展示你在开发一个很酷的计算机视觉模型上取得的进展。您正在处理的模型还没有准备好,因此将它部署到适当的开发或生产环境中需要时间和大量的工作。另一方面,开发一个特定的 web 界面来与您的模型进行交互可能是一项单调乏味的任务,并且需要一套对于数据科学家来说并不常见的技能。

营救

Streamlit ,是一个开源的 Python 库,它使得创建和共享用于机器学习和数据科学的 web 应用变得容易。在向你的团队展示项目进展、获得并与你的经理分享见解,甚至从客户那里获得反馈时,它会非常有用。

它不能取代需要监控、日志记录等的适当的生产部署。然而,它能让你在几个小时内创建“有用的东西”,而不需要任何 web 开发的先验知识,甚至不知道如何使用 flask 或 Django 作为后端。

让我们看看这个魔术是如何运作的。

装置

要安装 Streamlit run:

pip install streamlit

如果你使用的是诗歌那么改为运行:

poetry add streamlit

要验证所有的工作是否正常,您可以运行这一行并查看一些解释和演示:

streamlit hello

您的第一款应用——上传图片

让我们从从本地存储加载一个图像并显示它开始:

要运行该应用程序,只需输入:

streamlit run image_upload.py

然后它会在你的浏览器中打开应用程序。它应该是这样的:

图片上传演示应用,图片作者

你可以浏览你的电脑,上传任何你喜欢的图片。我选择上传一些可爱企鹅的图片。

图片上传演示应用。作者提供的应用程序图片, Paul Carroll 在 Unsplash 上传的照片

部署预先训练的模型

有时,为了有一个好的基线,查看预训练模型如何工作是有用的。现在,您将在一个 Streamlit 应用程序中部署其中一个模型,在本例中为 ResNet18。

要使用 PyTorch 做到这一点,您需要加载模型并下载 ImageNet 标签:

为了在图像上运行模型,应该修改您之前使用的 load_image 函数来返回图像。您需要将 image_data 转换为 PIL 图像对象,以便将其输入到模型中:

下面的代码是对 PyTorch ResNet 教程的修改版本,它运行一个模型预测并呈现图像的前 5 个类别:

当然,这也需要相应地修改主函数:

首先需要改的是标题。然后,您将加载模型、标签和图像。您还应该添加一个按钮,在加载的图像上触发模型推理。

这是应用程序在我之前加载的图像上运行时的样子:

带有示例输出的预训练 ResNet18 模型应用程序。作者提供的应用程序图片,保罗·卡罗尔在 Unsplash 上传的照片

该模型以 1.0 的概率将图像分类为“国王企鹅”。其他 4 个顶级类的概率可以忽略不计。

这个例子的完整代码在 Github 中。

部署自定义影像分类模型

现在您已经准备好部署一个定制模型了。您将使用 PyTorch 模型,该模型根据企鹅类型对图像进行分类。创建使用自定义模型的 web 应用程序的过程与涉及预训练模型的过程非常相似,只是有一些修改。代码可以在这里找到。

关于模型

通常,预先训练的模型不足以满足我们的需求,因为手头的任务更加具体,并且不在此类模型的覆盖范围内。例如,ImageNet 有一个“国王企鹅”类,但实际上还有更多企鹅类型。因此,为了根据企鹅类型对图像进行分类,我必须训练自己的模型。我用这个笔记本进行训练。

如果你想了解更多关于这个模型是如何建立的,你可以在我之前的文章中阅读。

获取模型

为模型相关文件创建一个目录:

mkdir custom_model

要直接下载模型文件,您可以使用链接。将其复制到新目录。

将此常量定义添加到代码中:

MODEL_PATH = 'custom_model/model.pt'

创建标签文件

用分类类别的名称创建一个文本文件,每个类别一行。确保课程的顺序与培训中使用的顺序相匹配。

例如,要发现 penguin 数据集的顺序,您可以在培训笔记本中查找以下行,并在其后添加一张图片:

class_names = image_datasets['train'].classesprint(class_names)

这将为您提供输出:

[‘Adelie Penguin’, ‘Chinstrap Penguin’, ‘Emperor Penguin’, ‘Gentoo Penguin’]

因此,您应该将以下类名复制到名为 model_classes.txt 的文件中,并将其放在 custom_model 目录中:

Adelie Penguin
Chinstrap Penguin
Emperor Penguin
Gentoo Penguin

将新标签文件作为常量添加到代码中:

LABELS_PATH = 'custom_model/model_classes.txt

加载模型和标签

模型和标签加载变得更简单,因为它们现在是本地的:

更多的修复

与具有许多标签的 ResNet18 不同,该定制模型只有 4 个标签。因此,您应该修改预测代码以输出所有现有类的概率:

更改主函数,使应用程序具有不同的标题,并传递模型和标签的本地路径:

最后,应用程序应该是这样的:

自定义影像分类模型结果。作者上传的应用程序图片和照片

为了测试这款应用,我上传了一张巴布亚企鹅的图片。该模型以 0.91 的概率将该图像分类为巴布亚企鹅的图像。其他企鹅类型的概率要低得多。随着应用程序运行良好的确认,我得到了模型正确性的另一个证明。很可爱,对吧?

结论

开发一个运行良好的计算机视觉模型需要花费大量的时间和精力。在这个过程中,您可能需要部署您的模型,并向您的经理、队友和客户展示它的能力。Streamlit 是一个强大且易于使用的工具,即使您没有适当的内部工具或前端知识,也可以让您实现这一目标。

我展示了 Streamlit 在图像分类中的潜在用途。然而,它可以用于其他类型的计算机视觉任务,以及可视化,数据分析等等。我鼓励你浏览一下 Streamlit 网站上的例子,亲自试用一下这个工具,看看它是如何融入你的日常工作的。

360°球面数据的民主化几何人工智能

原文:https://towardsdatascience/democratizing-geometric-ai-for-360-spherical-data-eec9a53606b6

360°球形数据解锁 AI

Josue Aguazia 在 Unsplash 上拍摄的照片

虽然人工智能现在对于标准类型的数据来说很常见,如结构化、序列和图像数据,但人工智能在其他更复杂形式的数据中的应用受到了严重限制。这些更复杂的数据集通常表现出非同寻常的几何特征。

几何人工智能(geometric AI)或几何深度学习(geometric deep learning)的领域已经出现,将人工智能的显著优势扩展到这些更复杂的几何数据集1。然而,几何人工智能技术的使用仍处于初级阶段,因为构建和部署几何人工智能模型仍然很困难。

民主化几何人工智能

一些优秀的几何人工智能库和框架已经存在,主要用于图形几何人工智能技术(如本文中所讨论的)。然而,这些库相对来说是低级的,在部署到生产环境之前,需要具有几何人工智能专业知识的专家机器学习工程师来构建和训练模型。对于其他形式的几何人工智能,许多进展是在尖端研究领域,在这些领域还没有通用的库。

我们正在用哥白尼为几何人工智能开发一个低代码平台,这样非专家也可以很容易地用几何人工智能方法解决他们自己的问题。我们在这个方向上的第一步是使 360°球形数据的几何人工智能模型可用。

360°球面数据的几何人工智能

球形数据实际上非常普遍,出现在许多领域。例如,当在球面上的每个点进行观测时,例如在地球的地形图上,会产生球面数据。然而,当在方向上进行观察时也会出现这种情况,例如 360°摄像机拍摄的全景照片和视频,例如用于虚拟现实、监控或自动驾驶汽车。其他应用包括宇宙学中的大爆炸残余光分析或医学中的扩散磁共振成像,以及许多其他应用。

球形数据的例子。【作者创作的 360 照片;CMB 图像来源;地球图片来源于维基百科;dMRI 图片来源于维基百科。]

在 Kagenova 我们正在努力解开深度学习在这些问题和其他涉及复杂几何数据(如球体)方面的巨大成功。对于 360 球形数据,标准人工智能技术是无效的,我们需要专门为数据的球形几何设计的几何人工智能。

在开发球形人工智能技术以解锁这些应用方面已经取得了很多进展(正如我们以前的文章这里和这里中所讨论的)。一个重大的挑战是开发计算效率高并且能够处理高分辨率数据(如高分辨率 360°图像)的技术。

在我们的研究中,我们在这方面取得了很大进展,首先开发了通用高效组件[2],然后引入了支持高分辨率输入数据的方法[3]。虽然对高分辨率输入数据的支持开启了许多应用程序,例如分类问题,但在许多设置中,必须支持高分辨率输出数据。我们目前正在致力于支持高分辨率输出数据,并将很快发布一篇相关论文,这将在 360°图像理解方面开辟一系列新的应用,如语义分割。

现在,我们用于球形数据的几何人工智能技术正在成熟,我们计划将我们的方法和模型公开给任何人使用。

图像分类的概念 360

虽然我们的哥白尼平台的工作仍在进行中,但在此期间,我们计划通过 AWS 人工智能市场推出一些球形人工智能模型。

我们刚刚发布了我们的第一个模型,用于 360°图像的分类。

这是一个简单的模型,使用基于 inception 的架构[4]来执行 ImageNet 分类,即使用 1,000 个 ImageNet 类对 360 个图像进行分类。ImageNet 可能无法为 360°分类提供最佳类别标签集,因为 360°图像比标准 2D 平面图像捕获更多内容;然而,它提供了一组熟悉的类标签来开始。在这个模型中,我们没有利用我们最新的研发成果,而是提供了一个非常简单的模型,让任何人都可以立即开始 360°图像分类。

你可以在 AWS AI Marketplace 这里找到免费的 Inception360。演示如何使用 Inception360 的笔记本可在此处获得。

下面我们举例说明在(样本外)360°图像上的 360°概念分类。考虑下面一个码头和帆船的 360 度图像。

由 Inception360 分类的示例(样本外)图像。返回以下排序的前 5 个分类:dock, boathouse, gondola, schooner, yawl. [Original image [source](https://pixexid/image/oif1n7u-360-image-fishing).]

Inception360 返回该图像的以下(排序前 5 名)分类,这些分类与该图像非常匹配:

dock, boathouse, gondola, schooner, yawl

未来(更多型号即将推出!)

虽然几何人工智能技术可以释放人工智能对于复杂几何数据的巨大潜力,但由于当前在构建、训练和应用几何人工智能模型方面的困难,它们尚未得到广泛利用。

有了哥白尼的平台,我们打算将几何人工智能大众化,以广泛应用于解决现有人工智能技术不适用的许多问题。我们在这个方向上的第一步是在 AWS AI Marketplace 上发布球形 360°数据的几何 AI 模型,从 Inception360 开始对 360°图像进行分类。请关注此空间,因为我们计划很快发布更多型号!

参考

[1]布朗斯坦,布鲁纳,科恩,维利科维奇,几何深度学习:网格,群,图,测地线,和量规 (2021), arXix:2104.13478

[2]科布,沃利斯,马沃-帕克,马利涅尔,普莱斯,达韦扎克,麦克尤恩,高效广义球形 CNN,ICLR (2021), arXiv:2010.11661

[3] McEwen,Wallis,Mavor-Parker,可扩展和旋转等变球形 CNN 的球上散射网络,ICLR (2022), arXiv:2102.02828

[4] Szegedy 等人,深入研究卷积,IEEE CCVPR(2015)arXiv:1409.4842

揭开 Python 中迭代器和生成器的神秘面纱

原文:https://towardsdatascience/demystify-iterators-and-generators-in-python-f21878c9897

了解处理大型数据集的有效方法

图片由 Pixabay 中的设计完成

当您有一个大型数据集时,比如一个大的 CSV 文件或一个大的 SQL 表,将所有数据加载到内存中是低效的,甚至是不可能的。你的电脑会卡住,你的程序会崩溃。调试起来既费时又令人沮丧。幸运的是,迭代器和生成器是解决这类问题的好工具。此外,理解生成器有助于学习更高级的特性,比如目前越来越流行的 asyncio。

Python 中的范围

在开始之前,让我们看一下特殊的range函数,该函数返回一个产生整数序列的可迭代的。Iterable,顾名思义,就是可以迭代的东西。或者你可以把它理解为可以在一个for循环中使用的东西。让我们用一些简单的代码来验证一下:

从这个简单的代码片段中,我们可以知道:

  • 从技术上讲,range是一个类,尽管它以非 Pythonic 化的小写字母开头。range返回的对象属于range类型。
  • 一个range对象是可迭代的并且可以被迭代。
  • 然而,range对象不是迭代器。我们需要使用iter函数将一个可迭代转换成一个迭代器

迭代器

迭代器是一个实现神奇的__next__方法的对象,因此可以在next函数中用来产生数据流的下一个元素,如上所示。为了理解迭代器是如何工作的,让我们创建一个模仿range函数行为的类。

我们需要一些代码来模拟range的位置参数的行为。重要的是,我们需要一个状态变量counter来记录自定义迭代器处于哪个状态以及下次生成哪个值。

让我们用next函数试试定制迭代器:

是的,它像预期的那样工作。现在,让我们试着在一个for循环中使用它,看看会发生什么。

嗯,有点奇怪,不是吗?MyRangeIter是迭代器,但不是可迭代的。如果迭代器只能在next函数中使用,而不能在for循环中使用,那么它就没有用。实际上,要使一个迭代器可迭代,我们需要实现__iter__魔法方法,这使得它可迭代,并且可以与上面演示的iter函数一起使用。如果你现在在r_iter上使用iter,你也会看到一个错误,说它是不可迭代的。现在让我们添加__iter__方法:

我们已经知道,iter函数调用底层类的__iter__方法并返回一个迭代器。在这个例子中,返回的迭代器是它自己。是的,很奇怪,但事情就是这样。实际上,如果您意识到要使类能够使用nextiter函数,必须分别实现神奇的__next____iter__方法,那么理解起来就不会那么困难了。

现在该变量可以在for循环中使用。你可以自己尝试一下。

发电机

如上所述,创建迭代器需要相当多的样板代码。我们需要创建一个类并实现神奇的__next____iter__方法。在 Python 中有一种更好的方法,这就是生成器的亮点。

要创建一个生成器,我们不需要创建一个类并实现神奇的__next____iter__方法。生成器简单地由生成器函数定义:

生成器函数的所有魔力都在于关键字**yield**,它将数据和控制返回给调用者,但保留函数的状态。当它再次迭代时,函数被恢复,并基于最新状态产生一个新值。在这个例子中,状态是用简单的计数器实现的。如果你把yield改成return,那么它就是一个常规函数,只会返回一个值。实际上,如果没有yield关键字,它根本不是一个生成器,不能被迭代。

让我们试试我们的发电机:

类似于迭代器,当生成器耗尽时会引发StopIteration异常。该异常由for循环自动处理。

此外,应该注意的是,生成器函数中的return关键字会引发StopIteration异常,返回值将被用作异常的消息。这对于理解生成器的类型注释是很重要的,我们将很快介绍这一点。

生成器理解

在我们学习更高级的生成器的send方法之前,让我们先学习一些简单而方便的东西。与列表理解类似,我们可以使用生成器理解用一行代码创建一个生成器。唯一的区别是我们需要将括号改为圆括号:

正如我们所见,生成器理解与列表理解非常相似。你只需要把括号换成圆括号。但是,使用 list comprehension,所有数据都被加载到内存中,这可以通过创建的变量的大小反映出来。另一方面,它不是生成器理解的情况,这使得它更加节省内存。

了解发电机的send方法

大多数情况下,不需要使用生成器的send方法。在用 Python 编码的这些年里,我从来没有机会使用它。然而,理解它是如何工作的有助于为生成器添加类型注释。此外,如果你想了解 Python 中的 asyncio 库是如何工作的,这也很重要,因为协程是由幕后的生成器实现的。

让我们更新生成器,让它接受用户发送的值。该值将用于更改生成器函数中的stop变量,这样我们可以生成更多的值:

注意,发送给生成器函数的值是由yield语句接收的。您可以将yield语句的返回值赋给一个变量,并相应地使用它。要向生成器发送一个值,只需调用生成器上的send()方法:

请注意,您只能在生成器已经产生某些东西之后向它发送数据,否则,您将看到一个TypeError:

生成器的类型注释

最后,让我们向上面创建的生成器函数添加类型注释。在函数中添加类型注释可以让你的代码更健壮,更容易理解。不用读函数体就能知道返回类型。

如果一个生成器函数既包含了yieldreturn关键字,也可以接受外部发送的值,那么我们需要使用泛型Generator[YieldType, SendType, ReturnType]:

注意对于*args,我们只需要为一个参数添加类型注释。更多细节请参考本讨论。

在大多数情况下,我们的生成器只会产生值。在这种情况下,我们可以将SendTypeReturnType设置为None:

或者,当一个生成器只产生值时,我们可以将返回类型标注为Iterable[YieldType]Iterator[YieldType],这对于那些不理解生成器的send方法的人来说更简洁,更容易混淆:

在这篇文章中,我们介绍了 iterables、iterators 和 generators 的技术细节,它们可以让您高效地处理需要大量资源(尤其是内存)的大型数据集。提供了一些简单的代码片段,可以帮助您理解迭代器和生成器的神奇方法,您通常将它们用作黑盒。发电机更受关注,因为它更简单,应用更广泛。我们已经揭开了send方法及其类型注释的神秘面纱。有了这些知识,您也可以理解更高级的特性,比如 Python 中的 asyncio。

相关文章:

  • 使用 mypy 和 pydantic 进行 Python 类型化和验证

揭开机器学习模型选择的神秘面纱,逐步指南

原文:https://towardsdatascience/demystify-machine-learning-model-selection-e3f913bab7e7

利用交叉验证、性能指标和总运行时间来确定最适合您的数据的模型

弗拉季斯拉夫·巴比延科在 Unsplash 上的照片

什么是选型?

机器学习中的模型选择就是为你的数据选择最好的模型。不同的模型在不同的数据集上会有不同的表现,而且差距可能很大。如今,梯度增强树是表格数据的最佳执行模型,例如 XGBoost ,或者 SciKit Learn 中的实现,这是很常见的。但是,不要总是默认使用 XGBoost 之类的模型,重要的是要评估不同算法的性能,看看哪种算法最适合您。

此外,不同的模型也有一些优势。例如,逻辑回归可以告诉你模型的系数,让你解释每个特征对最终预测的影响。像 RandomForest 这样的袋装树模型可以告诉你模型中每一列的特征重要性,类似于 Logistic 回归的系数。

让我们来看看如何在您选择的评分标准和训练速度之间选择最佳模型。

入门指南

对于我们今天的演示,我们将使用银行营销 UCI 数据集,您可以在 Kaggle 上找到该数据集。该数据集包含有关营销活动中银行客户的信息,并且包含一个可以在分类模型中使用的目标变量。该数据集在 CC0: public domain 下的 Public Domain 中,可以使用。

有关构建分类模型的更多信息,请查看:构建令人惊叹的二进制分类器所需要知道的一切和超越了具有多类和多标签模型的二进制分类。

我们将从导入必要的库和加载数据开始。我们今天将利用 Scikit-Learn 进行分析。

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from timeit import timeit

import warnings
warnings.filterwarnings('ignore')

from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.preprocessing import OneHotEncoder, LabelEncoder
from sklearn.preprocessing import MinMaxScaler
from sklearnpose import ColumnTransformer

from sklearn.linear_model import LogisticRegression
from sklearn.linear_model import Perceptron
from sklearn.linear_model import SGDClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.ensemble import AdaBoostClassifier
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC

from sklearnpose import make_column_selector as selector
from sklearn.pipeline import Pipeline

接下来,我们将数据加载到一个熊猫数据帧中,并观察它的形状。

df = pd.read_csv("bank.csv", delimiter=";")
df.shape
(4521, 17)

数据清理

我们有4500 行17 列的数据,包括目标变量。在执行我们的模型选择之前,我们将对数据进行一些简单的清理,首先寻找空值,实际上没有空值,并删除任何重复的**。**

# check for nan/null
df.isnull().values.any()
# drop duplicates
len(df.drop_duplicates())

接下来,在这个特定的数据集中,我们需要删除持续时间列。如文件中所述,该列对目标变量的结果有很大影响,因此,应从培训中排除。

持续时间:最后一次联系的持续时间,单位为秒(数字)。重要注意事项:该属性对输出目标有很大影响(例如,如果 duration=0,则 y='no ')。然而,在执行呼叫之前,持续时间是未知的。还有,结束通话后 y 显然是已知的。因此,该输入应仅用于基准测试目的,如果目的是获得现实的预测模型,则应丢弃。

df.drop(columns='duration', inplace=True)

数据准备

接下来,让我们利用简单的 python 切片将数据分成Xy两个集合。因为我们的目标变量是最后一列,所以我们可以只取最后一列作为我们的X数据,只取最后一列作为我们的y数据。

X = df.iloc[:, :-1]
y = df.iloc[:,-1]

我们的y列是带有yesno值的二进制列。最好利用 Skikit-Learn 中的LabelEncoder将这些编码到10中。

enc = LabelEncoder()
enc.fit(y)
y = enc.transform(y)

接下来,我们将利用列转换器将我们的数据转换成机器学习可接受的格式。每当我为可重复性构建模型时,我更喜欢使用管道。关于它们的更多信息,请查看我的文章:停止一步一步地构建你的模型。利用管道实现流程自动化!。

对于我们的转换,我们为数字特征选择了MinMaxScaler,为分类特征选择了OneHotEncode (OHE)。OHE 将分类数据转换为二进制表示形式,防止模型预测序数值之间的值。更多关于 OHE 的信息,请查看:一个热门编码。

column_trans = ColumnTransformer(transformers=
        [('num', MinMaxScaler(), selector(dtype_exclude="object")),
        ('cat', OneHotEncoder(), selector(dtype_include="object"))],
        remainder='drop')

为模型选择创建模型列表

现在我们要用我们不同的模型建立一个字典。字典中的每个条目由作为型号名称和作为管道组成。

模型选择的想法是挑选性能最佳的模型,而不是调整模型以获得最佳性能。这就是所谓的超参数调整,您可以在这里了解更多信息:【HalvingGridSearch 使超参数调整速度提高了 5 到 10 倍。

因此,我们将用默认参数实例化每个模型。一个例外是,我总是倾向于使用可用的class_weight='balanced'参数。这是一种简单的方法来抵消不平衡数据带来的问题。在这里阅读更多关于处理不平衡数据的内容:在构建你的 ML 模型的时候不要陷入不平衡数据的陷阱。

def get_models():
    models = dict()

    models['Logistic Regression'] = Pipeline([('prep', column_trans), 
        ('model', LogisticRegression(random_state=42, max_iter=1000, class_weight='balanced'))])

    models['Decision Tree'] = Pipeline([('prep', column_trans), 
        ('model', DecisionTreeClassifier(random_state=42, class_weight='balanced'))])

    models['Random Forest'] = Pipeline([('prep', column_trans), 
        ('model', RandomForestClassifier(random_state=42, class_weight='balanced'))])

    models['Extra Trees'] = Pipeline([('prep', column_trans), 
        ('model', ExtraTreesClassifier(random_state=42, class_weight='balanced'))])

    models['Gradient Boosting'] = Pipeline([('prep', column_trans), 
        ('model', GradientBoostingClassifier(random_state=42))])

    models['Hist Gradient Boosting'] = Pipeline([('prep', column_trans), 
        ('model', HistGradientBoostingClassifier(random_state=42))])

    models['AdaBoost'] = Pipeline([('prep', column_trans), 
        ('model', AdaBoostClassifier(random_state=42))]) 

    models['SGD'] = Pipeline([('prep', column_trans), 
        ('model', SGDClassifier(random_state=42, class_weight='balanced'))])

    models['SVC'] = Pipeline([('prep', column_trans), 
        ('model', SVC(class_weight='balanced', random_state=42))])

    models['Nearest Neighbor'] = Pipeline([('prep', column_trans), 
        ('model', KNeighborsClassifier(3))])

    models['Perceptron'] = Pipeline([('prep', column_trans), 
        ('model', Perceptron(random_state=42))])

    return models

交叉验证

在训练模型时,不要让模型过度适应您的数据或允许它一次看到所有数据,这一点很重要。通常你会对你的数据进行训练测试分割**;然而,在这种情况下,我们将使用交叉验证方法,利用RepeatedStratifiedKFold方法找到最佳模型,以处理将数据划分到多个训练和测试集。**

****分层采样确保相对类别频率在每个训练和验证折叠中大致保持不变,对于不平衡数据至关重要。关于这种方法的更多信息,请查看:交叉验证:评估评估者的表现。

我们将构建一个可重用的函数,允许我们测试存储在字典中的不同模型。根据数据集的大小,这里有几个参数可以使用。您可以确定分割重复的次数。如果您有一个像本例这样的较小数据集,请尽量不要多次分割数据,否则您将没有足够的样本来进行训练和测试。

此外,您需要指定想要使用的评分标准。Scikit-Learn 支持许多不同的工具,您可以在他们的文档中看到如何引用它们。对于这个例子,我选择了 ROC-AUC 作为我的指标。有关选择最佳度量的更多信息,请查看:停止使用准确性来评估您的分类模型。

# evaluate a give model using cross-validation
def evaluate_model(model, X, y):
    cv = RepeatedStratifiedKFold(n_splits=5, 
                                 n_repeats=10, 
                                 random_state=1)
    scores = cross_val_score(model, X, y, 
                             scoring='roc_auc', 
                             cv=cv, n_jobs=-1)
    return scores

评估模型

现在我们可以进行评估了。我们将调用evaluate_model函数遍历字典,并将结果存储在一个列表中。我们将对模型的名称做同样的处理,以便于我们绘图。

每次评估模型时,我们还会使用神奇的命令%time检查模型的速度,它会打印出评估模型所花费的时间,帮助我们进行选择。我们还打印出十次重复的平均分数标准偏差分数。

最后,我们将利用分数的盒须图在单个图上绘制结果。

# get the models to evaluate
models = get_models()

# evaluate the models and store results
results, names = list(), list()
for name, model in models.items():
    %time scores = evaluate_model(model, X, y)
    results.append(scores)
    names.append(name)
    print('* %s Score = %.3f StdDev = (%.3f)' % (name, np.mean(scores), np.std(scores)), '\n')

# plot model performance for comparison
plt.figure(figsize=(10,8))
plt.boxplot(results, labels=names, showmeans=True)
plt.xticks(rotation=45)
290 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* Logistic Regression Score = 0.721 StdDev = (0.025) 

204 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* Decision Tree Score = 0.573 StdDev = (0.021) 

1.61 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* Random Forest Score = 0.730 StdDev = (0.024) 

1.68 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* Extra Trees Score = 0.701 StdDev = (0.021) 

2.75 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* Gradient Boosting Score = 0.756 StdDev = (0.021) 

2.04 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* Hist Gradient Boosting Score = 0.728 StdDev = (0.021) 

886 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* AdaBoost Score = 0.733 StdDev = (0.023) 

212 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* SGD Score = 0.690 StdDev = (0.031) 

4.01 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* SVC Score = 0.715 StdDev = (0.027) 

660 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* Nearest Neighbor Score = 0.608 StdDev = (0.022) 

127 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
* Perceptron Score = 0.639 StdDev = (0.043)

作者图片

在这里,我们可以很好地看到每个型号的性能。某些算法表现不佳,我们可以在这个用例中丢弃它们,例如简单的决策树**、最近邻分类器感知器分类器。这些都是列表中一些比较简单的模型,它们的表现比其他的差并不奇怪。梯度提升树是表现最好的分类器,ROC-AUC 得分为0.756,名副其实。AdaBoostRandomForest 紧随其后,分别获得0.7330.730的分数。**

我们还可以看看运行所用的时间。在这些模型中,梯度增强树2.75秒时表现最慢,而 AdaBoost886毫秒时表现最好。看逻辑回归**;然而,它在0.721时表现相当好,但在290毫秒时非常快,这可能会影响我们的选择过程。通过利用其系数,逻辑回归具有高解释能力的优点,并且在梯度增强树的大约 10%的时间内执行。**

最终的选择取决于你,但是这些方法应该给你一个强大的基线来为你的用例选择最好的模型!

本文的所有代码都可以在 GitHub 上获得

结论

模型选择是你建立机器学习模型的关键一步。选择正确的模型会极大地影响机器学习模型的性能,而选择错误的模型会给你留下无法接受的结果。我们通过利用管道来实现一致性,完成了准备数据的过程。然后,我们建立了一个模型列表,我们希望评估他们的表现。我们使用交叉验证在各种数据切片上测试每个模型,并最终绘制出结果。利用这一过程是为您的应用选择正确型号的快速而有效的方法!

如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名媒体成员。一个月 5 美元,让你可以无限制地访问成千上万篇文章。如果您使用我的链接 注册,我会为您赚取一小笔佣金,无需额外费用。

揭开高效自我关注的神秘面纱

原文:https://towardsdatascience/demystifying-efficient-self-attention-b3de61b9b0fb

实用概述

图片作者。人工智能-使用达尔-E-2 生成

介绍

Transformer 架构[1]对于近年来深度学习领域的一些最大突破至关重要。特别是在自然语言处理(NLP)领域,预训练的自动编码模型(如 BERT [2])和自回归模型(如 GPT-3 [3])一直在努力超越最先进的技术,并达到类似人类的文本生成水平。Transformer 最重要的创新之一是使用关注层作为路由信息的主要方式。

顾名思义,注意力的目标是让模型关注输入的重要部分。从人类的角度来看,这是有意义的:当我们看一个输入(例如,一个图像或一个文本)时,有些部分对我们的理解来说比其他部分更重要。我们可以将输入的某些部分相互联系起来,并理解长期的背景。这些对于我们的理解都是必不可少的,注意力机制允许变形金刚模型以类似的方式学习。虽然这已被证明是非常有效的,但注意机制有一个实际问题:它们与输入长度成二次方关系。幸运的是,有很多研究致力于提高注意力的效率。

这篇博文旨在通过直观的解释,对不同类型的高效注意力提供一个全面的概述。这并不是对已经撰写的每篇论文的完整概述,而是对底层方法和技术的覆盖,并带有深入的示例。

注意力入门

在深入研究具体方法之前,让我们先回顾一下自我关注机制的基础知识,并定义一些将在这篇博文中重复使用的术语。

自我关注是一种特殊类型的关注。常规注意和自我注意的区别在于,自我注意关注的是一个单一的序列,而不是将输入与输出序列联系起来。它允许模型让序列学习关于它自己的信息。举个例子,就拿“那个人走到河边,他吃了一个三明治”这句话来说吧。与以前的嵌入方法(如 TF-IDF 和 word2vec [4])相比,自我关注允许模型学习“河岸”不同于“金融银行”(上下文相关)。此外,它允许模型学习“他”指的是“那个人”(可以学习依赖性)。

自我关注可以解释如下。假设我们有一个长度为 n 的序列 x,x 中的每个元素都用一个 d 维向量表示。在 NLP 的情况下,x 将是句子的单词嵌入。x 通过三个(训练的)权重矩阵 WQ、WK 和 WV 被投影,输出三个矩阵:Q、K 和 V,所有维度都是 n*d。自我关注可以被定义为下面的一般公式:

方程式 1: 广义注意力

最常用的得分函数是 softmax。如[2]所述,取 softmax 并应用一个比例因子会导致比例点积注意(SDP):

方程式 2: 缩放-点积注意

在这里,输入 x 的关注度是通过将 Q 乘以 KT(将每个项目与每个其他项目相关联)、应用缩放因子、获取逐行 softmax(归一化每个行)、将 V 中的每个值乘以其计算出的关注度来计算的,因此我们的输出再次是 n*d。因此,Q 和 K 用于将每个元素与每个其他元素相关联,而 V 用于将 softmax 的输出分配回每个单独的元素。

作者认为,对于较大的 d_k 值,点积变得非常大,这反过来将 softmax 函数推到梯度非常小的区域。因此,点积通过除以√(d_k)来缩放。请注意,这对计算的复杂性没有影响,因为这是由 softmax(QK)计算决定的。由于这个原因,它将被排除在一般公式之外。

正如你可能已经看到的,这个公式有一个问题:Q 和 K 相乘得到一个 nn 矩阵。取 nn 矩阵的行方式 softmax 具有 O(n)的复杂度。这对于运行时和内存使用都是有问题的,因为 n 可能非常大。对于多页文档,很快就变得无法计算完整输入的自我关注,这意味着输入必须被截断或分块。这两种方法都去除了自我关注的一个主要好处:长期背景。这种类型的注意力,其中每个项目都与其他项目相乘,被称为“整体注意力”,可以形象化如下:

**图一:**全球瞩目。图片作者。

在这里,对角线中的每个项目(深蓝色)查看其行和列中的所有其他项目(以浅蓝色突出显示)。

为简单起见,以下定义将在下文中使用:

等式 3: P 和 A 的定义

在这里,P 指的是 Q 和 K 相乘的结果 n*n 矩阵,A(自我关注矩阵)指的是 P 的 softmax,注意大多数论文使用自己的定义,这可能会有点混乱。

关于注意力的更详细的解释,我鼓励你去读读插图变压器。

可供选择的事物

降低 SDP 复杂性的主要假设是,并非输入的所有部分都同等重要,并且一些记号不需要关注其他特定记号。

为了避免计算全球注意力,有几种选择:

  • **稀疏注意力:**稀疏注意力方法稀疏化全局注意力矩阵,以减少必须相互关注的标记的数量
  • 矩阵分解:矩阵分解方法的工作原理是注意力矩阵是低秩的,可以用低秩矩阵进行分解和近似,而不会丢失太多信息。
  • **位置敏感哈希:**位置敏感哈希提供了一种快速计算最近邻搜索的方法。这可以直接应用于关注矩阵,以选择哪些令牌应该相互关注。
  • **内核关注:**内核关注方法将 softmax 函数解释为内核,并使用它来更有效地计算自我关注矩阵。

所有这些选择都以牺牲一些性能为代价,降低了计算复杂度。注意,所有这些方法都试图降低序列长度 n 的复杂度。为此,所有复杂度都降低到依赖于 n 的部分。

稀疏的注意力

稀疏注意力方法通过仅考虑 n*n 自我注意力矩阵 p 中的计算子集来降低复杂性。其思想是记号不需要注意每一个其他记号,而是可以关注更重要的记号而忽略其他记号。那么问题就变成了:我们如何挑选要关注的令牌?

局部注意 O(n*W)

局部注意,也称为窗口式 / 滑动注意,是一种简单而有效的稀疏化自我注意矩阵的方法。在局部注意中,标记只关注它们的局部邻域或窗口 w。因此,不再计算全局注意。通过只考虑 W 中的令牌,它将复杂度从 nn 降低到 nW,这可以如图 2 所示。

随机注意 O(n*R)

在随机注意中,标记只注意随机的其他标记。复杂度取决于所选随机记号的数量®,它是所有记号的比率。这可以从图 2 中看到。

图 2 :局部注意(左)和随机注意(右)。图片作者。

稀疏变压器 O(n√n)

稀疏变压器[5]是减少自我关注复杂性的第一个尝试。作者提出了两种稀疏注意模式:步进注意和固定注意,这两种模式都将复杂度降低到 O(n√n)。他们的两种注意力类型可以使用以下函数来定义:

  • 跨步注意:如果
    (i+s) > j > ( i-s)或(i-j) mod s = 0 ,则第 I 个位置可以关注第 j 个位置
  • 固定注意:如果 floor(j/s) = floor(i/s)或(j mod s) ≥ (s-c) ,第 I 个位置可以注意到第 j 个位置

其中 s 是步幅(设置为√n ), c 是超参数。这些算法的复杂性为 O(n*s),当 s 设置为√n 时,这导致 O(n√n)。跨步注意力类似于具有跨步的局部注意力,作者认为这对于从具有周期性结构的数据(如图像或音乐)中学习非常重要。但是,对于没有周期性结构的数据(如文本),这种模式可能无法将信息路由到远处的项目。固定注意力是解决这个问题的方法。它让一些项目关注整个列,并创建一个“摘要”传播给其他项目。两种不同模式的可视化如图 3 所示。

图 3 :跨步注意(左)和固定注意(右)。图片作者。

龙前 O(n)

Longformer [6]使用了滑动(或局部)、扩张滑动和全局注意的组合。扩张滑动注意是基于扩张 CNN 的想法。扩大滑动注意的目标是逐渐增加每一层的感受野。作者提出在较低层使用局部注意,窗口 W 较小(可以看作是间隙 d 为 0 的扩张滑动窗口注意),在较高层增加 W 和 d。

仅针对特定令牌添加全局注意。将哪些令牌设为全局的选择由用户决定。分类的一个合理选择是使[CLS]标记全局化,而对于 QA 任务,所有问号标记都可以全局化。他们算法的复杂度是(nW + sn),它与序列长度 n 成线性比例,因此简化为 O(n)。

请注意,Longformer 的实现需要一个定制的 CUDA 内核,因为现代 GPU 针对密集矩阵乘法进行了优化。作者提供了一个定制的 CUDA 内核,允许在 PyTorch 和 Tensorflow 中的 GPU 上有效计算他们提出的稀疏矩阵乘法。

图 3 :局部注意(左)、扩张滑动注意(中)、全局注意(右)。图片作者。

矩阵分解

在矩阵分解(或分解)方法中,矩阵 P 被假定为低秩的,这意味着矩阵中的所有项并不是彼此独立的。所以可以用更小的矩阵来分解和近似。这样,nn 矩阵可以简化为 nk(其中 k< n), which allows us to compute A (the result of the softmax) much more efficiently.

l 前一个 O(n)

Linformer [7]的作者提出使用注意力矩阵的低秩分解来达到 O(n)的复杂度。作者首先从经验上表明,当应用奇异值分解(SVD)时,A 可以从它的前几个最大奇异值中恢复,这表明它是低秩的。然后,他们使用约翰逊-林登斯特劳斯引理(JL)证明 A 可以近似为低秩矩阵γ,误差非常小,该引理表示:

高维空间中的一组点可以被投影到低维空间中,同时(几乎)保持点之间的距离。

作者指出,计算每个自我关注矩阵的奇异值分解增加了额外的复杂性。相反,作者在 V 和 K 之后添加了两个线性投影矩阵,这有效地将原始(nd)矩阵投影到更低(kd)维矩阵,其中 K 是降低的维度。这可以被形象化,如图 4 所示:

图 4 :标准立正(上)和非标准立正(下)。图片作者。

他们提出的新的注意力公式如等式 3 所示:

等式 3: 前注意函数

这里,Ei 和 Fi 是两个线性投影矩阵。请注意,要将 A 从 nn 减少到°( n * K ),只需将 K 投影到维度 K。由于 V 仍然是 nd,因此 V 也被投影到维度 K,以确保最终的输出矩阵是 n*d(这是下一层的预期维度)。

这实际上是通过线性投影减少了序列长度 n。这对于 NLP 是有意义的,因为一个句子中的所有单词并不是(同等地)相关的。

最后一步是选择 k 的值。作者表明 dlog(d)的值对于 k 是足够的,这导致 O(nk)的复杂度。因为 d 相对于输入长度 n 不增加,所以自我注意机制的复杂度变为 O(n)。

纽约变压器

ny strm former[8]使用 ny strm 方法来近似自我注意矩阵。想法是将矩阵 P 重写为一个由四部分组成的矩阵:B 是 m*m,其中 m 是某个数字< n), C, D, and E. This is shown on the left in Figure 5:

图 5 :矩阵近似的 Nystrom 方法解释。图片作者。

根据 nyströ方法,p 可以近似为 P̃,方法是用 DB⁺C 代替 e(其中 B⁺是 b 的摩尔-彭罗斯伪逆),然后可以进一步简化,如图 5 所示。原始的 nn 矩阵现在被分解为两个 nm 矩阵和一个 m*m 矩阵的乘积。这极大地减少了计算量,因为只有选定的 K 行和 Q 列需要相乘来创建这个分解(而不是所有的行和列)。

为了更好地理解这是如何工作的,让我们使用 nyströ方法近似子矩阵 e 中的单个元素 eᵢ,ⱼ。假设 P 是一个 5*5 的矩阵,我们选择 B 作为我们的第一个单元格(1,1),C 作为第一行(2,1 到 5,1),D 作为第一列(1,2 到 1,5)。这可以被形象化,如图 6 所示。假设我们想知道 e₃,₃的值,它是 c₃,₃和 d₃,₃的乘积(在 SDP 中,这将是 q 和 k 中单个值的乘积)。在 P̃,我们不再有 c₃,₃和 d₃,₃的实际乘积,但是我们知道 c₃,₁and d₁,₃(as 的值,这些值在我们选择的行和列中。为了逼近 e₃,₃,我们将 c₃,₁and d₁,₃的值乘以 b 的倒数。正如您所看到的,我们可以用 q 中的一行和 k 中的一列的结果来逼近 e 中的任何值

图 6 :矩阵近似的 Nyströ方法示例。图片作者。

虽然本例中选择了 Q 和 K 的第一行和第一列,但也可以对多行和多列进行采样,称为“界标”,本文中就是这么做的。使用分段平均值选择这些界标,这类似于局部平均池(将输入分成段并取每个段的平均值)。

然而,仍然存在一个问题:为了计算关注矩阵 a,需要首先计算 p,因为 softmax 运算通过取 p 中整行的内容来归一化 a 的元素。由于目标是避免计算 p,所以作者提出了一个变通方法:他们对 P̃的三个子矩阵进行 softmax 运算,并将它们相乘,如等式 3 所示:

方程 3: 纽斯特罗姆注意力函数

这里,Z*是 B⁺.的近似值

虽然这在技术上是不允许的,因为 softmax 是一个非线性操作,但作者表明,这种方法提供的近似仍然是足够的。

局部敏感散列法

位置敏感哈希(LSH)是一种可用于高效近似最近邻搜索的技术。LSH 的思想是可以选择哈希函数,使得对于高维空间 p 和 q 中的任意两点,如果 p 接近 q,那么 hash§ == hash(q)。使用该属性,所有点都可以被划分到散列桶中。这使得更有效地找到任何点的最近邻居成为可能,因为只需要计算到相同散列桶中的点的距离。在自我关注的情况下,这可以用于通过对 Q 和 K 应用 LSH 来加速 P 的计算,并且在应用 LSH 之后仅将彼此接近的项相乘,而不是执行完整的计算 QK。

重整器 O(nlog(n))

《改革家》[9]的作者是第一个提出利用 LSH 进行有效的自我关注的人。他们注意到,由于 softmax 由最大的元素支配,所以对于 Q 中的每个查询 qi,qi 只需要关注 K 中最接近 qi 的键(或者在相同的散列桶中)。

为了更好地理解这是如何工作的,我们来看一个例子。假设我们有一个包含许多点的二维空间,如图 7 左侧所示。在自我关注的情况下,这些点就是 p 中的项目。颜色代表靠近在一起的点。为了将项目划分到散列桶中,通过原点绘制了许多随机超平面,如图 7 中的右侧所示。在这种情况下,绘制了两个超平面,称为 H1 和 H2。任何超平面都有一个正边(1)和一个负边(0)。然后,根据项目出现在每个超平面的哪一侧,将它们放入散列桶(在本例中为 4 个)。因此,哈希桶的数量由绘制的超平面的数量来定义。在这样做之后,项目只需要计算到它们自己的散列桶内的项目的距离(或者,在自我关注的上下文中,关注相同散列桶内的项目)。

图 7:LSH 的例子。图片作者。

正如在图 7 右侧的结果散列桶中可以看到的那样,很接近的项目可能仍然在不同的散列桶中结束。为了减轻这种情况,可以执行多轮散列,并将每个值分配给最常出现的散列。然而,这确实增加了算法的复杂性。作者表明,通过 8 轮散列,该模型达到了类似于全局注意力模型的性能。

作者使用了一种称为角度 LSH [10]的变体,它使用余弦距离来计算任意两点之间的距离。它们表明,两个非常接近的点很有可能会在同一个桶中结束。

将点分成桶后,按桶对点进行排序。但是,有些桶可能比其他桶大。最大的存储桶仍将主导内存需求,这是一个问题。出于这个原因,作者将桶分成固定的块,因此内存需求取决于块的大小。请注意,项目可能不会与桶中的其他项目在同一个块中结束。这些项目可以处理它们应该结束的块中的所有项目,但是不能处理它们自己(这给复杂性增加了一个小的恒定成本),如图 7 的最后一行所示:

图 7:LSH 注意事项说明。图片来源:[9]。

这有效地将复杂度降低到 O(n log n)。需要注意的重要一点是,由于不依赖于 n 而从复杂度中去除了 8 轮散列,因此引入了较大的 12⁸常数,这实际上导致重整器仅在输入序列非常长(> 2048)时变得更高效。

核心注意力

核是这样一种函数,它以某个低维空间中的两个向量 x 和 y 的点积作为输入,并返回某个高维空间中点积的结果。这可以概括为一个函数 K(x,y) =φ(x)ᵀφ(y),其中 k 是核函数,φ是从低维到高维空间的映射。在机器学习的背景下,支持向量机(SVM)是一个众所周知的例子。特别是对于有效的自我关注,内核方法的工作原理是 Softmax 可以被解释为内核并被重写,这样我们就可以避免显式计算关注矩阵 a。

表演者 O(n)

执行者[11]是基于一种叫做的机制,通过正正交随机特征(或 FAVOR+)。这个想法是,我们可以使用核方法来近似 softmax 函数。

通常,当应用核方法时,我们希望在高维空间中计算点积。这可以通过使用适当的核函数 K 来实现(例如我们在核 SVM 中所做的)。然而,执行者做相反的事情:我们已经知道我们的函数 k 是什么(非线性的 softmax),我们想要找到φ,以便我们可以计算φ(x)ᵀφ(y(它是线性的)。我们可以将它形象化,如图 8 所示。在左手边,我们看到我们的 L*L 矩阵 A 乘以 V(注意,这只是公式 softmax(QKT)*V),作者将序列长度称为 L 而不是 n)。相反,所提出的方法使用φ来直接计算φ(Q)= Q’和φ(K)= K’,这允许我们首先将 K 和 V 相乘,并且避免了矩阵 a 的高成本计算

图 8 :好感+关注的说明。图片来源:[11]。

主要的研究问题变成:我们如何找到φ?这个想法是基于随机傅立叶特征[12]。作者指出,大多数内核可以使用通用函数建模,如等式 4 所示:

等式 4: 用于内核建模的通用函数

这里,h 是 x 的某个函数,m 是定义近似精度的参数,ω₁…ωₘ是从某个分布 d 中抽取的随机向量(因此随机部分更倾向于+),而 f₁…fₗ是确定性函数。m 越高,近似值越好,因为它定义了绘制的ω数。

作者证明了 Softmax 核可以通过选择等式 5 中所示的值来近似:

等式 5: 用于逼近 Softmax 的函数

现在,我们可以通过φ函数传递我们的 Q 和 K,并得到结果矩阵的点积。这样做的结果就好像我们先将它们相乘,然后取 softmax。由于我们可以通过φ独立地传递 Q 和 K,我们现在可以先将 K 和 V 相乘。

然而,还有一个问题。与 softmax 不同,sin 和 cos 可以具有负值,这导致当 softmax 的实际值接近 0 时,近似值的方差变大。由于很多自我关注值接近 0,这是个问题。为此,作者建议使用不同的函数,即等式 6 中所示的函数,这些函数仅输出正值(因此有利于+的正部分)

等式 6: 用于逼近 Softmax (FAVOR+ version)的函数

最后,作者解释说,确保ωs 正交会导致更小的方差(因此正交部分更有利于+)。

自我关注的替代品

很明显,有很多研究致力于提高点产品注意力的效率。然而,还有另一种选择:完全不使用自我关注,而是使用一种更简单的方法在我们的令牌之间共享信息。最近有多篇论文提出了这个想法([13]、[14]、[15])。我们将讨论一个,因为所有这些论文的总体思路都非常相似。

FNet O(n)

FNet [15]是一种替代的变换器架构,它用离散傅里叶变换(DFT)完全取代了自关注模块。因此,除了前馈层之外,不再有可学习的参数。DFT 将信号分解成其组成频率。其定义如公式 7 所示:

等式 7: DFT 函数

其中 N 是组件的数量。当 N 为无穷大时,我们可以精确地创建原始信号。在 NLP 的上下文中,我们的信号是一个令牌序列。实际上,每个组件 n 都包含一些关于输入序列中每个标记的信息。

他们方法的有趣之处不在于他们使用 DFT,而在于他们应用线性变换来混合他们的令牌。他们还尝试了一种线性编码器,这与合成器模型的工作方式非常相似,甚至是一种完全随机的编码器。虽然线性编码器的性能稍高,但它有可学习的参数,因此比 FNet 慢。BERT-Base 在 GLUE 上的平均分仍然高得多,但是他们报告说训练时间提高了大约 7 倍。因为有许多可能的线性变换,所以有一个有趣的开放式研究问题,即什么是最适合变压器的。

基准

虽然本帖中讨论的所有论文都报告了它们关于输入序列长度 n 的理论复杂性,但在实践中,由于较大的常数(如 Reformer)或低效的实现,一些论文可能仍然不切实际。为此, Xformers 被用于计算许多方法的不同序列长度的内存使用和运行时间。注意,并不是所有讨论的方法都在 Xformers 中实现,不幸的是,BlockSparse 在我的 GPU(RTX 3090)上不工作。

长序列长度(512,1024,2048):

图 9 :各种高效注意力方法的长序列长度的内存使用和运行时使用。图片作者。

短序列长度(128,256):

图 10 :各种高效注意方法的短序列长度的内存使用和运行时使用。图片作者。

显然,对于更长的序列,所有方法都明显比 SDP 更有效。虽然这里比较的所有注意机制(除了 SDP)都与序列长度成线性比例关系,但有趣的是,由于常数和其他比例因子,这些机制之间仍然存在明显的差异。值得注意的是,Linformer 的伸缩性不如其他方法。另一个有趣的结果是 Nystromformer 的内存使用和运行时间。虽然它的伸缩性很好,如序列长度(512、1024、2048)的图表所示,但对于短序列(128 和 256),它实际上是最低效的方法。这可能是由于所选标志的数量,如[8]中所建议的,其值保持在 64。

有趣的是,对于最长 512 的序列长度,SDP 的性能与其他方法非常相似。唯一明显比其他方法更有效的方法是 FNet(傅立叶混合注意)。它几乎完全独立于序列长度,同时没有重要的常数需要考虑。

结论

考虑到基于变压器的模型日益增长的相关性,有效的自我关注仍然是一个活跃的研究领域。虽然它们看起来令人望而生畏,但大多数技术实际上可以追溯到您可能已经熟悉的更一般的数学概念。希望这篇博文既是对大多数相关技术的介绍,也是对它们的解释,帮助你更深入地了解这个领域。

参考

[1] Ashish Vaswani、Noam Shazeer、Niki Parmar、Jakob Uszkoreit、Llion Jones、Aidan N. Gomez、Lukasz Kaiser 和 Illia Polosukhin。你只需要关注,2017。

[2] Devlin,j .,Chang,m .,Lee,k .,& Toutanova,K. BERT:用于语言理解的深度双向转换器的预训练,2018 年。

[3] Brown,T. B .,Mann,b .,Ryder,n .,Subbiah,m .,Kaplan,j .,Dhariwal,p .,Neelakantan,a .,Shyam,p .,Sastry,g .,Askell,a .,Agarwal,s .,Krueger,g .,Henighan,t .,Child,r .,Ramesh,a .,Ziegler,D. M .,Wu,j .,Winter,c .,Hesse,c .。。阿莫代伊博士。语言模型是很少出手的学习者,2020。

[4]托马斯·米科洛夫、程凯、格雷戈·科拉多和杰弗里·迪恩。向量空间中单词表示的有效估计,2013。

[5]蔡尔德,r .,格雷,s .,拉德福德,a .,& Sutskever,I .用稀疏变压器生成长序列,2019。

[6] Iz Beltagy,Matthew E. Peters 和 Arman Cohan。Longformer:长文档转换器,2020

[7]王,李,B. Z,Khabsa,m .,方,h .,,马,h .林前:自我注意与线性复杂性,2020 .

[8] Xiong,y .,Zeng,z .,Chakraborty,r .,Tan,m .,Fung,g .,Li,y .,& Singh,V. Nystromformer:一种基于 Nystrom 的自我注意近似算法.2021.

[9]尼基塔·基塔耶夫、祖卡斯·凯泽和安塞尔姆·列夫斯卡娅。改革者:高效的变压器,2020。

[10]亚历山大·巴甫洛夫·安多尼、彼得·因迪克、蒂伊斯·拉霍文、伊利亚·拉赞施泰因和路德维希·施密特。角距离的实用和最佳 lsh,2015。

[11] Choromanski,k .,Likhosherstov,v .,Dohan,d .,Song,x .,Gane,a .,Sarlos,t .,Hawkins,p .,Davis,j .,Mohiuddin,a .,Kaiser,l .,Belanger,d .,Colwell,l .,& Weller,a .,重新思考表演者的注意力,2020 年。

[12]阿里·拉希米,本杰明·雷希特。大规模内核机器的随机特性,2007。

[13] Tay,y .,Bahri,d .,Metzler,d .,Juan,d .,Zhao,z .,& Zheng,c .合成器:在变形金刚模型中反思自我注意。2020.

[14]托尔斯提欣、霍尔斯比、科列斯尼科夫、拜尔、李、翟、安特辛纳、杨、施泰纳、基泽斯、乌兹科雷特、卢契奇、多索维茨基、《混合建筑:全建筑展望》,2021 年。

[15] Ainslie,j .,Eckstein,I .,& Ontanon,S. FNet:用傅立叶变换混合令牌,2021 年。

揭开评估的神秘面纱:基础

原文:https://towardsdatascience/demystifying-estimation-the-basics-5206532b6378

更多地了解人口

罗马法师在 Unsplash 上拍摄的照片

目录

  1. 简介
  2. 正态分布
  3. 中心极限定理
  4. 需要了解基础知识?
  5. 结论

介绍

统计学是涉及数据的多个领域的重要基础。有两种主要的统计类型:

  1. 描述性统计:这种类型的统计有助于使用图形描述、总结和可视化数据。描述性统计的两个主要元素是集中趋势的度量,这完全是关于数据样本的中心位置,如均值、中值和众数。第二个是变化的度量,它是关于数据样本的分布,比如方差和标准差。但是,它不能帮助我们做出超越数据的结论。
  2. 推断统计:在大多数情况下,对人口做出结论是不可能的,因为这是昂贵的,需要时间和资源。例如,一位研究人员想知道一个青少年花在学习上的平均时间。收集地球上每个青少年的数据并得出结论是不可能的。所以剩下的唯一方法就是从样本中得出关于总体的结论。这就是所谓的推断统计学。推断统计学有两个主要元素:
  • 估计:它包括考虑样本参数并对总体参数做出结论。例如,如果随机选择的 10 名学生样本的平均值为 52,则计算总体参数(在本例中为青少年的平均学习时间)的取值范围。
  • 假设检验:它包括检查关于人口参数的声明的可信度。例如,一名研究人员声称,一名青少年平均每天学习 4 小时,10 名学生的平均学习时间为 3.2 小时。然后,假设检验有助于检查关于总体均值的主张是否正确。

在本文中,我们将深入探讨评估的概念。在此之前,我将解释一些支持性和基础性的概念,这将帮助您更好地理解评估的概念。

正态分布

正态分布|图片来自维基百科

存在许多可能的数据分布,例如偏斜的、双峰的、均匀的等等。正态分布是一种具有以下性质的分布:

  1. 钟形的
  2. 均值、中值、众数是一样的,都在中心。
  3. 平均对称
  4. 它是单峰的,即只有一种模式
  5. 曲线下的总面积为 100%

正态分布有一种变体,称为标准正态分布。它是一种均值为零,标准差为 1 的类型。z 得分是特定值偏离数据平均值的标准偏差数。使用 Z 分数,任何正态分布都可以转换为标准正态分布。

z 评分公式|来源:作者图片

对于正态分布,曲线下的值如下:

  • 1 标准偏差有 68%
  • 2 标准偏差有 95%
  • 3 标准偏差有 99.7%

中心极限定理

样本均值的抽样分布是一种使用从总体中抽取的特定大小的所有可能随机样本计算出的均值的分布。抽样误差是样本参数和总体参数(例如,样本均值和总体均值)之间的差值,因为样本不是总体的完美代表。

当从总体中提取特定大小的所有可能样本而不替换时,那么,

  • 样本平均值等于总体平均值。
  • 样本均值的标准差等于总体的标准差除以样本大小的平方根。

中心极限定理指出,随着样本大小 n 的增加,从总体中取出的样本均值的分布形状将接近正态分布。

中心极限定理是重要的,因为它是两个主要推断统计元素的基础:估计和假设检验。中心极限定理可以用来回答与样本均值相关的问题。有两个条件:

  1. 如果总体是正态分布,那么样本均值的分布将是任何样本大小的正态分布。
  2. 如果总体不是正态分布,样本量必须大于 30。

纵观全局,可以得出结论,68%的样本均值位于总体均值的一个标准差范围内。类似地,95%和 99.7%的样本均值位于总体均值的 2 和 3 个标准偏差内。为了找到标准偏差的确切数目,样本平均值远离总体平均值,计算 Z 得分,

样本分布的 z 分数|来源:作者图片

这里 X '是样本均值,mu 是样本均值的均值,sigma 是样本均值的标准差。它与前面的 z 得分公式相同,只是在抽样分布方面有所不同。

需要了解基础知识?

正如我们所了解的,估计就是在考虑样本的情况下寻找总体参数。正如上面在抽样分布和中心极限定理中提到的,任何样本均值都在总体均值的 X 标准差处。例如,95%的样本平均值落在总体平均值的两个标准偏差内,那么样本平均值 X’落在,

样本均值区间|来源:作者图片

颠倒上面的公式,对于任何 X ',下面提到的区间包含总体均值的概率为 95%,

人口平均值的区间|来源:作者图片

这个关系用于求总体均值的区间估计,只有事先了解基础知识才能理解。

结论

在本文中,我们学习了理解评估的基本概念。本文是评估系列的第一部分。我将发布一些其他的文章来解释评估的类型和进行评估的测试。

就这些了,伙计们。我希望你喜欢它!!

揭开 Python 中模块和包的神秘面纱

原文:https://towardsdatascience/demystifying-modules-and-packages-in-python-968c13b3b990

模块和包是任何大型项目的核心。我将展示一些涉及它们的技巧,比如如何组织包和创建名称空间。

照片由阿尔瓦罗·雷耶斯从 Unsplash 拍摄

当我在 Github 上检查复杂的项目时,我通常会发现自己迷失在无数的文件夹和源文件之间。这些报告的作者认为他们的定制对他们自己来说非常明显,这是完全可以理解的;可悲的是,我努力让自己对构建不同的文件夹和源文件有同样的感觉,但却没有用。

揭秘一些处理包和模块的常见反应如何?

在这个快速教程中,我花时间模拟了一个具有以下全局结构的项目:

作者图片

具体来说,我们项目的树状结构可以概括为:

我们最感兴趣的是superlibrary的内容:

Dataloader 处理所有类型的数据加载器。

Learners包含按学习类型划分的模型。

Preprocessor拥有熊猫、NumPy 和 Scikit-Learn 的各种预处理器模块。

Tests是您集中所有测试的地方。

Utils通常包含各种执行优化或充当成本函数的函数。

每个函数只是一个简单的打印函数,也可以作为一个整体实现。

我们将进行简单快速的实验来强调一些概念,并举例回答如下问题:

包装是如何制作的?

如何控制进口包装?

如何执行一个包作为主入口函数?

如何利用名称空间包?

实验

包装是如何制作的?

如果你检查我们的模拟项目的结构,你会注意到在我们的文件夹的不同层次传播__init__.py

包含此类文件的每个文件夹都被视为一个包。__init__.py 文件的目的是包含可选的初始化代码,这些代码在遇到不同级别的包时运行。

例如,让我们将自己定位在preprocessor文件夹中。我们将编写几行代码进行一些导入。

让我们在preprocessor/__init__.py 文件中写下以下几行:

让我们返回到预处理器目录的上一级(因此我们将查看preprocessorlearnerdataloadertestsutils目录),打开 Python 解释器并执行以下操作:

>>> from preprocessor import numpy_prep, pd_prep, sk_prep
>>> numpy_prep()
This is the numpy implementation for the preprocessor.
>>> pd_prep()
This is the Pandas implementation for the preprocessor.
>>> sk_prep()
This is the sklearn implementation for the preprocessor.
>>> ...

了解这里做了什么很重要;preprocessor目录中的__init__.py文件将我们需要在更高层次调用的所有函数粘在了一起。

通过这样做,我们为preprocessor包提供了额外的逻辑功能,节省了您的时间和更复杂的导入行。

如何控制进口包?

比方说,使用与前面相同的层次结构配置,您想要控制某个行为,该行为包括导入所有带有星号的内容。

你会写:

from module import * .

让我们通过添加一个新的__all__属性来修改preprocessor/__init__.py 文件:

__all__正在接收一个受限的属性列表。

看看如果我们在preprocessor目录之上运行解释器会发生什么:

>>> from preprocessor import *
>>> numpy_prep()
This is the numpy implementation for the preprocessor.
>>> pd_prep()
This is the Pandas implementation for the preprocessor.
>>> sk_prep()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'sk_prep' is not defined
>>> ...

sk_prep函数被省略,不包含在__all__属性中。这保护了加载sk_prep的原始包免受任何意外行为的影响。

尽管一般不鼓励使用from module import *,但它还是经常在声明大量名称的模块中使用。

默认情况下,此导入将导出所有不以下划线开头的名称。但是,如果指定了__all__,则只导出明确说明的名称。

如果__all__被定义为空列表,则不会导出任何内容。如果__all__包含未定义的名称,导入时会抛出AttributeError

如何执行一个包作为主入口函数?

毫无疑问,你对写这样的东西很熟悉:

If __name__ == "__main__" :
...

把它带到另一个水平怎么样?可以将learner包作为主模块运行吗?

让我们转到learner文件夹:

接下来我们要做的是确保在导入三个包中的每一个时,我们同时导入与每种学习类型相关的函数。

对于clustering包,我们在clustering/__init__.py文件中写下几行代码:

同样为supervised_learning 包装:

对于reinforcement_learning包:

我们现在能够将所有必要的功能直接加载到学员目录中的新实现中。

我们创建一个新文件,命名为__main__.py:

我们返回到主superlibrary目录,因此我们将在learner目录的上一级,并运行以下内容:

Username@Host current_directory % python learner 
Methods for clustering :
This is the implementation for model1.
This is the implementation for model2.
Methods for reinforcement learning :
This is the implementation for model1.
This is the implementation for model2.
Methods for supervised learning :
This is the implementation for model1.
This is the implementation for model2.

万岁!属性似乎比你所习惯的做得更多。它可以超越一个简单的文件,控制整个包,因此您可以导入或运行它。

如何利用名称空间包?

在我们的最后一节中,假设您稍微修改了 utils 文件夹的内容:

所以在每个subpackage中有一个名为modules 的新模块,它没有__init__.py初始化文件。

也许我们应该从一开始就谈论你可能遇到的这种类型的场景,以及其行为非常值得怀疑。没有__init__文件的文件夹的行为几乎类似于一个包,但并不被视为包。

更技术上来说,它被认为是一个namespace 包。事实证明,我们可以用它来实现有趣的事情。

您可以创建一些包含在每个backend_connectorscustom_functions包中的公共名称空间,这样modules将作为一个single模块…

还不够信服?让我们在解释器中写一些高于前面提到的两个包的东西:

>>> import sys
>>> sys.path.extend(['backend_connectors','custom_functions'])
>>> from modules import cloud_connectors, optimizers
>>> cloud_connectors
<module 'modules.cloud_connectors' from 'path_to_current_directory/cloud_connectors.py'>
>>> optimizers
<module 'modules.optimizers' from 'path_to_current_directory/optimizers.py'>
>>> ...

通过在模块搜索路径中添加这两个包,我们从一种特殊的包中获益,这种包是为在一个公共名称空间下将不同的代码目录合并在一起而设计的,如图所示。对于大型框架,这可能非常有用。

这里有一个关于 Python 在导入时如何感知modules的技巧。

>>> import modules
>>> modules.__path__
_NamespacePath(['backend_connectors/modules','custom_functions/modules'])
>>> modules.__file__
>>> modules
<module 'modules' (namespace)>
>>> ...

您观察到一个联合的名称空间路径,一个缺失的__file__属性(如果它有一个的话,这个属性应该与它的__init__文件的路径相关联),以及它的类型的一个清楚的指示:名称空间。

为了充分利用名称空间包,特别是在这种情况下,您不能在任何一个modules目录中包含任何__init__.py文件。假设您真的添加了它们:

仔细观察当你试图合并modules目录时会发生什么:

>>> import sys
>>> sys.path.extend(['backend_connectors','custom_functions'])
>>> from modules import cloud_connectors, optimizers
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: cannot import name 'optimizers' from 'modules' (path_to_project_directory/superlibrary/utils/backend_connectors/modules/__init__.py)
...

正如所料,当您将普通包视为名称空间包时,Python 无法加载您的函数。

结论

我希望这些内容能让你更清楚地了解一个适度复杂的项目的结构。这是一个热门话题,尽管它在社区中缺乏分享和写作的热情。关于 Python 如何处理包和模块,有太多东西需要学习。

目前的材料旨在让读者意识到将一个人的项目放在一个可读的层次结构中的重要性,以便其他每个用户都有信心使用它。

谢谢你的时间。

本文标签: 中文翻译博客TowardsDataScience一百三十一