TowardsDataScience-博客中文翻译-2022-三十八-

TowardsDataScience 博客中文翻译 2022(三十八)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

否定 R2:你错在哪里?

原文:https://towardsdatascience.com/negative-r2-where-did-you-go-wrong-9d4f2aa84cfb

理论系列

统计例子

最近,我进行了一项机器学习任务,并在我的维持测试集上获得了一个负面的 R2 评分指标。

“等等!R2 不是…方形的吗?不是应该在 0 和 1 之间吗?我到底做了什么?”

最初,我非常关心我的机器学习任务的状态。在谷歌搜索了几分钟后,我仍然只是略微担心我的机器学习任务的状态。无论如何,我很好地回顾了负 R2 值意味着什么,以及它是如何意外获得的。这篇文章提供了对统计数字的数学来源的更深入的了解,以帮助阐明这一结果的意义。

接收到负的 R2 值表明模型的预测值比使用平均值作为预测值时表现更差。你自己的谷歌搜索可能会产生类似的陈述。在这里,我用一个数据例子来解释为什么。

按作者分列的数字

为什么用 R2?

R2 是决定系数,由投入解释的结果中的变化比例。在预测连续结果的回归任务中,它经常被用作性能指标。

在经典线性回归中,R2 的值介于 0 和 1 之间。这篇文章提供了一个很好的基于证据的解释。然而,简而言之,当包含截距时,该证明适用于普通最小二乘(OLS)建模方法。如果您使用非线性模型(正如我们数据科学社区中的许多人所做的那样),请将这些约束抛之脑后。现在我们的决定系数范围从负无穷大变为 1。

按作者分类的表格

事实上,对于 R2 是否应该用于非线性模型,有一些合理的争论。这一论点是基于这样一个事实,即 R2 并不是一个常规可靠的模型拟合预测器,这一点由 Spiess & Neumeyer 在 2010 年的一篇期刊论文中证明。

顺便说一句,R2 作为 OLS 的拟合优度也不是固有的有用指标,至少在通过评估模型残差确定线性假设有效之前是如此。

在任何情况下,虽然 R2 不应被用作唯一的性能指标,但不可否认的是,R2 可以与其他指标一起用于评估模型性能,如 MAE(平均绝对误差)、MSE(均方误差)、RMSE(均方根误差)或可能的调整 R2。

什么是 R2?

在经典回归中,目标是预测结果。这个结果变量的变化可以用 3 个值来表示:SSR、SSE 和 SST。这些是样本中方差的平方和。平方项使我们不必处理负值的复杂情况。SSR、SSE 和 SST 的不同别名变得特别容易混淆,所以请注意我已经尽了最大努力在一个地方编译了一些常见的替代命名约定。接下来,我将只使用下表中左边两列的缩写。

R2 境内的数量(按作者分列)

在 OLS 模型中,SSR + SSE = SST。一直都是。

这些统计数据相关的原因是因为决定系数是 SSE 和 SST 的函数。

按作者分列的数字

使用 n 个观察值的样本,我们可以计算紫色 SSE 值,作为观察值 I 的真实结果(yᵢ)和拟合/预测结果(ŷᵢ)之间的平方差之和

按作者分列的数字

使用 n 个观测值,我们还可以将蓝色 SST 值计算为真实结果(yᵢ)和结果样本平均值(ȳ).)之间的平方差之和

按作者分列的数字

计算出 SSE 和 SST 之后,我们就可以计算出我们想要的指标,R2。

按作者分列的数字

R2 反面例子

让我们使用一个有 10 个观察值的玩具数据集。每个观察值都有一个模型的拟合/预测估计值,并且每个观察值都有真实的结果,如下表所示。不提供实际型号有两个原因:

a)我编的。

b)该性能指标可以应用于几乎任何模型的拟合值,因此它与为什么 R2 可以取负值的问题无关。

按作者分类的表格

按作者分列的数字

注意拟合值的平均值和真实值的平均值是相同的,5。在中心趋势相同的情况下,也许这个模型是对观察结果的合理预测?

表格中的值绘制如下。假设黑点代表真实的结果值。蓝点代表我们模型结果的预测值(即拟合值)。每个观察值的总变化量可以按上述方法划分。为清晰起见,下图中只显示了一个观察值。总变化(由 SST 解释)分为两个量,由模型拟合(SSR)解释的变化和由拟合值中的误差(SSE)解释的变化。

按作者分列的数字

对于每个指标,SSE 和 SST,都有差异,这些差异的平方显示在相应的列中。该模型的 SSE 误差平方和为 86.5,而 SST 误差平方和为 67.5。

因为 SST < SSE,这表明如果我们放弃我们的模型,只预测平均真实值(5.0)作为每个点的预测统计,该估计将比我们当前的模型产生的拟合值表现得更好。虽然这不是一个理想的结果,但这意味着这种表现不一定是误判的结果。对于具有截距的 OLS 模型,R2 介于 0 和 1 之间的原因是,如果无法获得更好的拟合,其截距将取平均值,其β系数将取值为 0。

作者的玩具数据

从上证综指和 SST 计算 R2 后,我们得到一个四舍五入的 R2 指标-0.28,这确实是负的,因为模型性能低于仅使用样本均值获得的性能。

按作者分列的数字

总之,我们已经解释了决定系数的来源,解释了为什么在某些情况下它可以呈现负值,并且举例说明了如何计算具有负值的玩具数据集。

如果我们回到我的机器学习任务,它提供了之前令人担忧的负面 R2 结果,似乎我只需要稍微担心一下,因为我的模型在技术上确实起作用了……它只是一个糟糕的模型。好吧,祝我下次好运。

如果您对其他性能指标或统计量有任何疑问,请在下面发表评论!

一如既往,继续坚持下去,尽管最近有负面表现模式的失败。

"失败是走向伟大的另一块垫脚石。"――奥普拉·温弗瑞

凯特·沃尔报道:

Python 中网格和点云的邻域分析、KD 树和八叉树

原文:https://towardsdatascience.com/neighborhood-analysis-kd-trees-and-octrees-for-meshes-and-point-clouds-in-python-19fa96527b77

距离计算和邻域分析是理解网格和点云的形状、结构和特征的基本工具。本文将使用三个最广泛使用的 Python 3D 数据分析库— Open3DPyVistaVedo 来提取基于距离的信息,将其可视化,并展示示例用例。一如既往,所有代码,连同使用网格和点云数据提供。谁说 3D 对象的邻域分析应该很难?

邻域操纵示例|图片由作者提供

与深度图或体素相比,点云和网格表示三维空间中的非结构化数据。点由它们的(X,Y,Z)坐标表示,并且在 3D 空间中可能彼此靠近的两个点在数组表示中可能远离。点之间的距离也不相等,这意味着它们中的一些可以紧密地聚集在一起,或者彼此远离。这导致了这样一个事实,即与图像中的相同问题相比,理解某个点的邻域不是一项无足轻重的任务。

点之间的距离计算是点云和网格分析、噪声检测和去除、局部平滑和智能抽取模型等的重要部分。距离计算也是 3D 深度学习模型的一个组成部分,既用于数据预处理,也是训练管道的一部分[7]。此外,经典的点云几何特征依赖于最近点的邻域计算和 PCA 分析[2,8]。

使用 PyVista(左)和 Vedo(右)计算点云中的点和网格中的顶点的邻域的示例|图片由作者提供

特别是对于非常大的点云和复杂的网格,如果以蛮力的方式进行,所有点之间的距离的计算会变得非常资源密集和昂贵。我们将在本文中关注的库使用不同的 KD 树八叉树的实现,将一个对象的 3D 空间划分成更易管理和结构化的象限。这样,这种划分可以一次完成,并且可以加速和简化所有后续的距离查询。由于深入研究 KD 树和八叉树超出了本文的范围,我强烈建议您在深入研究给出的示例之前观看这些 YouTube 视频——多维数据 KD 树、四叉树和用于表示空间信息的八叉树,尤其是 K-d 树——计算机爱好者

作者用 Open3D | Image 计算八叉树

在本文中,我们还将简要介绍点云中各点之间的测地线距离的计算。这个距离是连通图结构中两点之间的最短路径,我们计算两点之间存在的边的数量。这些距离可用于捕捉有关 3D 对象的形状和点组成的信息,也可用于处理 3D 表面的图形表示。

在本文中,我们将仔细研究三个 Python 库——open 3DPyVistaVedo ,以及它们生成 3D 网格和点云的邻域和邻接分析的能力。选择这三个库是因为它们提供了简单易用的距离计算功能,可以在深度学习和处理管道中轻松实现。这些库也是功能齐全的,并提供了分析和操纵网格和点云的方法。我们还将使用 SciPy 提供的 KD-tree 实现,因为它是高度优化和并行化的,这使得它在处理大规模 3D 对象时非常有用。关于这三个库的安装说明以及如何用它们构建交互式可视化的例子,您可以在下面查看我以前关于 python 库的文章。

为了演示点云和网格上的体素化,我使用了两个对象。先是里的一个鸭子雕像点云。ply 格式,包含每个点的 X、Y 和 Z 坐标,以及它们的 R、G 和 B 颜色,最后是 Nx、Ny 和 Nz 法线。鸭子雕像是使用运动摄影测量学的结构创建的,并且可以在商业、非商业、公共和私人项目中免费使用。这个对象是一个更大的数据集[1]的一部分,已经用于噪声检测和检查方法的开发[2],以及规模计算[3]。二、著名的斯坦福兔女郎中的一个对象。使用 ply 格式,因为它容易获得,并且在网格分析研究中广泛使用。在引用适当的引文后,兔子可以自由地用于非商业应用和研究

为了跟随教程,除了使用的库和它们的依赖项,您还需要 NumPy 和 SciPy。所有代码都可以在 GitHub 库这里获得。

使用 Open3D 进行邻域计算

作者在 Open3D | Image 中使用 FLANN KD 树可视化计算点云中每个点的 KNN

O pen3D 被认为是用于 3D 可视化的 Python 库的标准,因为它包含用于点云、网格、深度图以及图形分析和可视化的方法。它可以在 Linux、Mac 和 Windows 上轻松设置和运行,它包含一个专门用于深度学习的完整分支,称为 Open3D-ML,并具有内置的 3D 重建方法。

Open3D 包含直接从点云或体素网格构建八叉树的现成方法。首先使用open3d.geometry.Octree(max_depth=maximum_depth_of_the_structure)初始化八叉树,然后使用方法name_of_octree.convert_from_pointcloud(name_of_point_cloud)直接从点云中生成八叉树。该方法隐式继承了点云的颜色信息。下面显示了不同深度的八叉树,以及简单的代码。

作者使用 Open3D | Image 生成了不同深度(4,6,8)的八叉树

一旦生成八叉树,就可以使用traverse和一个将为每个节点处理的函数对其进行遍历,如果找到所需信息,还可以提前停止。此外,八叉树具有以下功能:

  • 定位一个点属于哪个叶节点— locate_leaf_node()
  • 向特定节点插入新点— insert_point()
  • 寻找树的根节点— root_node

Open3D 还包含使用 FLANN [5]基于 KD-trees 构建的距离计算方法,也可以通过不同的绑定在这里找到。首先使用函数open3d.geometry.KDTreeFlann(name_of_3d_object)从点云或网格生成 KD 树。然后,该树可以用于搜索许多用例。首先,如果需要一个特定点的 K 个最近邻点,可以调用函数search_knn_vector_3d以及要查找的邻点数量。第二,如果需要特定半径内某点周围的邻居,可以调用函数search_radius_vector_3d和半径的大小进行搜索。最后,如果我们需要限制也在特定半径内的最近邻居的数量,可以调用函数search_hybrid_vector_3d,它结合了前面两个函数的标准。这些函数还有一个更高维度的变体,用于使用例如search_knn_vector_xd()来搜索 3 维以上的邻居,其中维度需要手动设置为输入。KD 树本身是一次性预计算的,但是搜索查询是一次对一个点进行的。

为了可视化在点云中寻找点的邻居的过程,我们将使用LineSet()结构,它采用几个节点和边,并构建一个图形结构。为此,我们首先将鸭子雕像点云加载为 Open3D 点云,并对其进行二次采样,以便于可视化。我们为此使用了voxel_down_sample()内置函数。然后,我们计算缩减采样点云的 KD 树。为了更好地显示距离是如何计算的,我们首先初始化 visualizer 对象,将背景改为黑色,然后只绘制点云。最后,我们使用register_animation_callback()注册一个动画回调函数。这个初始设置如下面的代码所示。

一旦初始设置完成,就可以为每个更新周期调用回调函数,并为每个点生成邻域。为点云中的每个点调用函数search_knn_vector_3d,需要 k 个最近邻。该函数返回点数、点的索引和点本身。为了生成线段,采用找到的相邻点,以及从中心点到每个相邻点的边数组。因为我们知道只有 k 个找到的邻居,所以我们生成边缘数组作为每个的中心和边缘索引的相同堆栈。创建的线集被添加到主线集对象,并且几何图形和渲染器被更新。一旦遍历了所有点,就通过调用clear()来重置线集。回调函数的代码如下所示。

既然我们已经看到了如何使用 Open3D 中的内置函数计算 KD-trees,我们将扩展这个概念。我们看了如何使用 3D 点的坐标之间的距离,但是我们也可以在其他空间上工作。鸭子雕像带有点云中每个点的计算法线和颜色。我们可以使用这些特征以同样的方式构建 KD 树,并探索这些特征空间中的点之间的关系。除了为这些特性构建 KD 树之外,我们将使用 SciPy 中预先构建的函数,只是为了探索生成数据的替代方法。构建 KD 树是通过 SciPy 的spatial部分完成的。要构建它们,我们可以调用scipy.spatial.KDTree(chosen_metric)。一旦我们有了树结构,那么我们就可以调用name_of_tree.query(array_points_to_query, k = number_of_neighbours)。这与 Open3D 实现不同,在 open 3d 实现中,我们可以一次查询一个点的最近点。当然,这意味着通过 SciPy 实现,我们可以使用高度优化的函数来预先计算所有距离,这对加快后面的计算很有用。查询函数的输出是两个数组—最近点距离和每个查询点的最近点索引,格式为 N x k ,其中 N 是查询点的数量,k 是相邻点的数量。

所有其他功能与上一个示例相同,但为了更清晰地展示,我们将这些步骤分成一个功能:

  • 我们向下采样点云

  • 我们计算 KD 树和点之间的边

  • 我们构建一个线集并输出点云进行可视化,最后,创建一个 visualizer 对象并显示所有内容。

最后,我们可以选择计算邻域并可视化不同的特征空间。这些示例如下所示,显示了坐标、法线和颜色空间中可视化的坐标、法线和颜色的邻域。

使用作者的 Open3D 和 SciPy | Image 计算和可视化不同特征空间(坐标空间(左)、颜色空间(中)和法向空间(右))的 k-最近邻

使用 PyVista 进行邻域计算

Py vista 是一个全功能的库,用于点云、网格和数据集的分析、操作和可视化。它建立在 VTK 之上,提供简单的开箱即用的功能。PyVista 可用于创建具有多个情节、屏幕、小部件和动画的交互式应用程序。它可以用来生成三维表面,分析网格结构,消除噪声和转换数据。

PyVista 包含许多现成的函数,用于对点和顶点进行分组、计算邻域以及查找最近的点。PyVista 中最简单的功能之一是使用 VTK 的连通性过滤器对点进行分组,并根据距离和连通性标准(如共享顶点、法线方向之间的距离、颜色等)提取连通单元。这个连通性可以通过调用name_of_3d_object.connectivity()来计算。这将返回一个标量数组,其中包含每个点的区域 id。此外,如果我们将largest=True添加到连接函数调用中,我们可以直接得到最大的连通区域。我们可以通过add_mesh_threshold(3d_object_name)将这些连接区域 id 与 PyVista 中内置的交互式阈值功能相结合,以便能够可视化和提取所需的区域。下面给出了代码。

作者在 PyVista | Image 中使用连通性过滤器和交互式阈值工具

除此之外,PyVista 还具有内置的邻域和最近点计算功能。这是通过find_closest_point(point_to_query, n = number_of_neighbors)函数完成的,其中一个点可以作为输入,以及要返回的邻域的大小。该函数返回输入中邻域内的点的索引。该函数与 Open3D 函数具有相同的限制,在 open 3d 函数中,一次只能计算一个点的一个邻域。由于这需要对大量的点进行处理,因此 SciPy 的实现更快、更优化。PyVista 中的 API 参考也提到了这一点。

为了演示find_closest_point的功能,并在 PyVista 的用例中给它更多的上下文,我们将动画演示鸭子雕像点云的重建,一次一个点邻域。这可以用作创建抽取函数、邻域分析函数等的基础。我们还将把整个东西整齐地打包在一个类中,这样就可以很容易地调用它。

作者使用 PyVista | Image 的内置最近点检测和动画可视化功能,一次重建一个邻域的点云

为了生成第二个点云并可视化当前点及其邻域,我们利用 PyVista 通过名称跟踪添加到绘图仪的对象这一事实。我们把每件事都绘制成在每次更新交互时调用的回调函数。为了更好地理解动画回调和网格更新是如何工作的,你可以看看数据可视化网格体素化文章。

作者使用 PyVista | Image 中的小部件将最近点选择和邻域计算与交互式选择和邻域大小更改相结合

最后,我们可以展示如何使用find_closest_point()将邻域计算与创建小部件和捕获鼠标事件的可能性结合起来。我们将创建一个应用程序,可以检测用户在点云上单击的点,并计算其邻居。找到的邻居数量将根据滑块微件进行选择。

使用enable_point_picking()选择点。在这个函数中,我们需要给出一个回调,以及使用show_message输入设置将显示在屏幕上的消息,并能够使用left_clicking=True直接点击鼠标左键。

为了设置滑块小部件,我们使用了add_slider_widget方法,我们设置了一个回调函数,以及滑块和事件类型的最小值和最大值。回调将做的唯一的事情是获得滑块的新值,然后如果点被选中,调用函数来计算最近点并可视化它们。

这两个函数都被设置为回调函数,并创建了一个简单的参数类来跟踪所有的共享变量。

使用视频进行邻域和距离计算

edo 是一个强大的 3D 对象的科学可视化和分析库。它具有用于处理点云、网格和 3D 体积的内置函数。它可用于创建物理模拟,如 2D 和 3D 对象运动、光学模拟、气体和液体流动模拟以及运动学等。它包含一个全功能的 2D 和 3D 绘图界面,带有注释、动画和交互选项。它可以用来可视化直方图,图形,密度图,时间序列等。它构建于 VTK 之上,与 PyVista 相同,可以在 Linux、Mac 和 Windows 上使用。有关安装和配置 Vedo 的更多信息,可以查看我以前的文章网格、点云和数据可视化的 Python 库(第 1 部分)

Vedo 有现成的功能,可以通过函数closestPoint()找到给定半径内所有最近的点或 k 个最近的邻居。它在网格或点云上与需要找到其邻居的点以及半径或邻居数量一起被调用。在内部,该函数调用 VTK vtkPointLocator 对象,该对象用于快速定位 3D 空间中的一个点,方法是将该点周围的空间区域划分为矩形桶,并找到落在每个桶中的点。该方法被认为比 KD 树和八叉树慢,因此对于较大的点云,SciPy 实现是优选的。

计算网格上选定点的邻域,在其上拟合一个圆,并使用作者的视频图像计算其中所有点的平均法线

为了演示 Vedo 中的邻域检测是如何工作的,我们将创建一个简单的例子,在这个例子中,用户单击一个网格来选择一个顶点,然后计算邻域。我们将通过展示如何使用它来计算邻域的平均法线并拟合一个圆来扩展它。这个简单的示例可以扩展到拟合其他图元,如球体或平面,并可用于计算局部邻域特征、智能去噪、抽取和空洞填充。对于这些例子,我们将使用斯坦福兔子网格。

我们将通过使用函数vedo.fitCircle(neighbourhood_points_array)来拟合一个圆,然后通过使用vedo.Circle()生成一个圆并使用vedo.Arrow()生成法向量来可视化它。我们通过调用plot_name.addCallback('LeftButtonPress', name_of_callback_function)来实现鼠标点击回调。

这里需要提到的是,对于回调函数,我们首先使用event['actor']检查是否有对象被选中,然后如果有对象,使用event['picked3d']获取选中的点。每次我们移除代表中心点、邻近点、圆和箭头的所有旧演员,并建立新演员。

另一个有趣的可以直接从 Vedo 计算的距离度量是测地线距离。测地线距离是 3D 对象的流形或曲面上的点之间的最短距离或路径。这非常类似于平面上两点之间的直线。测地线距离是一条分段平滑曲线,积分后是给定点之间的最短路径。测地线距离对于计算球体或球形空间中的点之间的距离非常有用,它用于测量地球上点之间的最短距离。从一个更简单的角度来看,如果我们将测地线距离与欧几里德距离进行比较,测地线距离会考虑点所在曲面的基础形状,而欧几里德距离则不会。在 Vedo 中,有一个现成的函数叫做name_of_mesh.geodesic(),它计算网格上两个给定点之间的距离。要求是网格是防水的,并且没有任何几何缺陷。该函数返回由两点之间的所有边组成的路径对象。它使用 Dijkstra 的算法来寻找最短路径,其实现基于【6】中描述的算法,如 VTK 的类描述中所述。

作者利用视频图像计算网格上交互选择的两点之间的测地距离

我们将通过创建一个交互式测地线路径计算示例来利用这一点,在该示例中,用户选择 3D 网格上的两个点,并可视化它们之间的路径。为此,我们将再次使用方法addCallback('LeftButtonPress'),以及一个保存所选点的列表,在每次新的点击时添加和删除它们。下面给出了代码。

结论

网格和点云的距离和邻域计算对于分析它们的表面、检测缺陷、噪声和感兴趣的区域是非常强大的工具。基于邻域计算局部特征是智能 3D 对象抽取、重构、水印和平滑的一部分。计算每个点的 K-最近邻是从点云生成图表、体素化和表面构建的重要部分。最后,许多处理 3D 对象的深度学习模型需要计算点邻域和最近点距离。通过这篇文章,我们展示了在 Python 中计算这些信息可以快速简单地完成。我们还展示了可以用 Python 创建基于距离计算的交互式应用程序和动画,而不会牺牲可用性。我们还展示了如何计算不同深度的 KD 树和八叉树。

既然我们知道了如何计算点邻域,下一步就是从中提取局部要素和表面信息。在下一篇文章中,我们将研究用于特征提取的 Python 库——基于 PCA 的和几何的。

如果你想了解更多关于从点云和网格中提取特征的内容,可以看看我的一些关于 3D 曲面检测[8,9],噪声检测[1],点云分割[7]的文章。你可以在我的 页面 上找到这些文章,加上我的其他研究,如果你看到一些有趣的东西或者只是想聊天,请随时给我留言。敬请关注更多内容!

参考

  1. 尼科洛夫一世;麦德森,C. (2020),“GGG——粗糙还是嘈杂?SfM 重建中的噪声检测指标”,门德利数据,V2;https://doi.org/10.17632/xtv5y29xvz.2
  2. 尼科洛夫,I. ,&马德森,C. (2020)。粗暴还是吵闹?SfM 重建中噪声估计的度量。传感器20 (19)、5725;https://doi.org/10.3390/s20195725
  3. 尼科洛夫,I. ,&马德森,C. B. (2020)。使用距离传感器测量计算 SfM 的绝对尺度和尺度不确定性:一种轻便灵活的方法。在3D 成像、建模和重建的最新进展(第 168-192 页)。IGI 环球;https://drive . Google . com/file/d/10 te 6 fgme 6 NC 3t 9 zrzmytjaatei 36 gskn/view
  4. 加德纳,a .,乔,c .,霍金斯,t .,和德贝韦克,P. (2003 年)。线性光源反射计。ACM Transactions on Graphics(TOG)22 (3),749–758;https://dl.acm.org/doi/pdf/10.1145/882262.882342?casa _ token = rndulsy 2 deq AAAA:SXWQGGvMD _ 3 ojn 20 xvnhk 2 uyvakmehtbdu-_ xwxqjnbneiki 72 a 41 ij 8 q 2 steyfhd 8 lqztxvzsjmg
  5. Muja 和 d . g . Lowe(2009 年)。具有自动算法配置的快速近似最近邻。 VISAPP (1)2(331–340),2;https://lear . inrialpes . fr/~ douze/enseignement/2014-2015/presentation _ papers/muja _ flann . pdf
  6. 科尔曼,T. H .,莱瑟森,C. E .,里维斯特,R. L .,,斯坦,C. (2001 年)。算法导论第二版。克努特-莫里斯-普拉特算法;https://MIT press . MIT . edu/books/introduction-algorithms-second-edition
  7. Haurum,J. B .,Allahham,M. M .,Lynge,M. S .,Henriksen,K. S ., Nikolov,I. A. ,& Moeslund,T. B. (2021 年 2 月)。使用合成点云的下水道缺陷分类。在 VISIGRAPP (5: VISAPP) (第 891–900 页);https://www.scitepress.org/Papers/2021/102079/102079.pdf
  8. Nikolov,I. A. ,& Madsen,C. B. (2021)。使用砂纸粒度量化风力涡轮机叶片表面粗糙度:初步探索。在第 16 届计算机视觉理论与应用国际会议(第 801–808 页)。科学出版社数字图书馆;https://doi.org/10.5220/0010283908010808
  9. Nikolov,I. A. ,Kruse,E. K .,Madsen,C. B,“图像采集设置如何影响风力涡轮机叶片检测的 SfM 重建质量”,Proc。SPIE 11525,SPIE 未来传感技术,115251 p(2020 年 11 月 8 日);https://doi.org/10.1117/12.2579974

Excel 中的嵌套表:可视化无模式数据结构

原文:https://towardsdatascience.com/nested-table-in-excel-9977637a74aa

Unsplash 上的 Didssph 拍摄的照片

使用 power query 和 JSON 在单元格内创建表格

介绍

无模式数据配置允许我们创建任何形式的数据,而无需定义列或列名。这与 Microsoft Excel 多年来使用的数据模式有很大不同。在定义“数据”之前,首先定义一个表:创建新的列名并定义预期的内容(数据类型?这一栏下面是什么样的数据?)在列数组下面。现在,有了无模式,我们甚至不需要定义列名。起初很难想象,但本文解释了什么是无模式数据,以及如何在 Microsoft excel 中充分利用它。

这在 Microsoft Excel 本身中是不可能的,但是如果我们超越 Excel 的能力,从数据结构的角度来考虑问题,我们就可以把一切都放在单元格中。我的意思是,字面上的一切(我们,非程序员,很可能需要,公平)。一张照片?还是另一张桌子?或者,作为一个制图员,一个几何图形让我们从数据中绘制出地图?

数据科学的动机

在数据科学中,我们必须了解我们如何构建和存储数据。生成表格的常见范例是说明列名和类型并填入数据!这样,我们在列中输入的每个数据都必须限制在表的结构中。这就是我们在 Ms Excel 和 RDBMS(关系数据库管理系统)中的做法,比如依赖于 sql(结构化查询语言)的 Postgresql 和 MySQL。这意味着我们需要首先定义表模式,然后通过符合模式来填充表!

来源:作者(2022)

当我们的数据是动态的,需要改变模式时,这就成问题了!例如,如果有关于我们的特性的新信息,我们需要存储它,我们需要再次指定列名和类型!

如果我告诉你模式对于结构化我们的数据是不必要的呢?您知道我们不必指定列名和类型吗?如果有新信息,我们可以原样存储。

这篇文章展示了大多数人拥有的软件中的无模式数据结构:Microsoft Excel。实际上,我认为不太可能使用 Ms Excel 来编辑或操作无模式数据库,但它是欣赏无模式数据的一个很好的直觉练习。

JSON (Javascript 对象符号)简介

关于 JSON 和无模式的更全面的主题可以在 MongoDB 的这个页面上找到。它是无模式的 NoSQL 数据库软件,采用 JSON 格式作为数据结构。本节将简要介绍内容。

https://www.mongodb.com/unstructured-data/schemaless

无模式意味着我们不是用表而是用 JSON 格式来组织数据。CSV 格式是表数据结构的一个示例,如下图所示:

表数据结构,来源:作者(2022)

在我们将 CSV 文件转换成 JSON 文件之前,让我们先了解一下 JSON。JSON 就像 CSV 它是一个文本文件,但是 JSON 格式看起来怎么样呢?

JSON 数据由方括号和花括号组成

表是行的列表(或数据的列表),就像下面的“雇员”列表。这个列表或数组由方括号定义。

["**sutan**", "**john**", "**jane**"]

然后,我们可以用花括号展开每个员工信息。我们将这些花括号组视为行,用逗号分隔它们,并将它们放在方括号内。

**[**{"name":"**sutan**", "medium":"perkotaan"}, {"name": "**john**", "medium": "johndoe"},{"name": "**jane**", "medium": "janedoe"}**]**

我们可以对"标记内的内容递归地这样做!将信息扩展到新的大括号中。

下表中的信息是相同的

来源:作者(2022)

所以,我们知道,

列表是一个表格

而且,

花括号是行,方括号是列表;

我们也知道…

表格是行的列表;

因此…

表格是大括号的列表

一个表就是一堆行,基本上就是一堆方括号。

请注意,我们可以在这些括号内任意键入任何内容。我想给 Sutan 添加一个 Twitter 帐户,但是我没有 John 和 Jane 的 Twitter 帐户。

**[**
{"name":"sutan", "medium":"perkotaan", **"twitter": "urban_planning"**}, 
{"name", "john", "medium": "johndoe"},
{"name", "jane", "medium": "janedoe"}
**]**

或者,添加另一个数据,也许,约翰的年龄?

**[**
{"name":"sutan", "medium":"perkotaan", "twitter": "urban_planning"}, 
{"name": "john", "medium": "johndoe"**, "age": 30**},
{"name": "jane", "medium": "janedoe"}
**]**

或者,再套一桌!例如,简的技能

# employees data**[**
     {"name":"sutan", "medium":"perkotaan"},
     {"name": "john", "medium": "johndoe", "age": 30},
     {
     "name": "jane",
      "medium": "janedoe",
     "skills" : **[**
          {"name": "ms excel", "years": 5},
          {"name": "ms word", "years": 2},
          {"name": "arcgis", "years": 4}
          **]**
     }
**]**

该嵌套表类似于 Excel 中的下图(仅可视化,而不是实际的数据存储):

来源:作者(2022)

回到最初的产品 CSV 文件。现在,为了使它无模式化,我们用 JSON 格式重构相同的产品数据;它看起来会像这样:

来源:作者(2022)

然后我们可以为表嵌套更多的信息!我在 Ms Excel 中制作了下面的图像,但是请再次注意:Ms Excel 在这里只是为了可视化 JSON 文件,在这个文件中我们嵌套了我们的表。

来源:作者(2022)

Excel 中的 JSON:超级查询

使用记事本(或任何文本编辑器),将示例 JSON 雇员数据保存为一个.json文件,如下图所示。例如,这是我之前展示的员工数据。

来源:作者(2022)

然后,我们可以使用 Ms Excel 导入文件,方法是转到 data 选项卡,从文件获取数据,然后单击 JSON。

来源:作者(2022)

这将打开 power query 编辑器,我们将把 JSON 文件转换成一个表

来源:作者(2022)

展开该列,

现在我们可以看到嵌套表列表

来源:作者(2022)

如果我们点击列表,我们可以将它展开为一个新表!”列表基本上是一张表!这里我们可以看到 JSON 中定义的 Jane 的技能。

来源:作者(2022)

单击 close & load(左上角),这将基于我们在 power 查询中定义的 JSON 连接在 excel 中生成一个表。在这一步,它将返回 Jane 的技能,就像我们在 power 查询中访问 Jane 的技能一样。

来源:作者(2022)

或者如果我们后退一步,我们可以加载雇员数据。

来源:作者(2022)

但最重要的是,对我来说,指的是 JSON 文件。所以绝对数据存储在 JSON 文件中;如果我们编辑 JSON 文件,Excel 文件也会被编辑。

结论

本文通过可视化无模式数据结构来构建无模式数据结构。这种无模式的数据利用 JSON 格式来构建数据,在数据中,我们用花括号定义每个特性,并用方括号将特性分组。对于阅读本文的计算机科学家来说,我相信这并不新鲜,但是对于不熟悉数据结构基础但需要结构化数据的专业人员来说,这种想法是新颖的,可以节省大量时间。

为了可视化无模式数据结构,我展示了 JSON 如何允许我们在 Excel 单元格中存储任何数据,包括表格。当我们可以通过使用 Power Query 在 Ms Excel 的单元格中托管一个表时,我们就理解了无模式数据。

Python 地图中的网络和互连

原文:https://towardsdatascience.com/network-and-interconnection-in-python-maps-6c797580b3b1

使用 Python 包(包括 geopandas、networkx 和 matplotlib 底图)的实践教程

与人口统计和发展指标有关的数据和统计是制定强有力的发展政策的核心。此外,这些指标的视觉和空间表现提供了重要的见解,有助于提供政策或投资决策的严谨性和透明度。考虑到这一点,我对我的祖国尼泊尔的人口和人口密度的空间分布以及地理区域如何通过国内航空业连接起来很感兴趣。

有几个包不仅可用于数据分析,还可用于在 Python 中构建地图、地理空间分析和地理编码。使用 Python 进行地理空间分析的一些主要优势包括其用户友好的语法、来自大型社区的潜在支持(例如,通过 GitHub、堆栈溢出等。),以及开放存取——人们不需要为昂贵的许可证付费。

geopandas、networkx 和 matplotlib 底图等 Python 包对于可视化网络和互连(如运输路线、管道和电力系统互连)非常有用。在这篇文章的第一部分,我将向你展示我是如何构建地图来显示尼泊尔人口的空间分布和人口密度的。在第二部分,我将描述我是如何绘制尼泊尔国内机场以及首都和其他地方之间的飞行路线的。为此,我将使用 rasterio、geopy、geopandas 和 matplotlib 等包。让我们开始吧。

尼泊尔人口的空间分布和人口密度

我在 WorldPop 网站上找到了尼泊尔 2020 年人口人口密度的公开数据。WorldPop 倡议为中南美洲、非洲和亚洲提供空间人口数据集的开放存取档案,以支持发展、灾害应对和卫生应用(WorldPop,2022)。首先,我使用wget.download(URL, folder)将所需文件从URL链接直接下载到我的本地folder。数据以 TIF(标记图像文件)格式提供。TIF 是一种高质量的图形格式,用于存储光栅图形(作为矩形矩阵或正方形矩阵网格的二维图像),在图形艺术家中很受欢迎(FileInfo,2022)。

我使用rasterio包打开两个人口的 TIF 文件,如下所示:

#Population file
file1 = “data/npl_ppp_2020.tif”#Open file
raster_file1 = rasterio.open(file1)#Read raster data
pop_data = raster_file1.read(1)

TIF 文件中的数据作为 numpy 数组读取。我创建了一个简单的函数,它取栅格数据的自然对数,然后将其显示为图像。

def plot_raster(raster_data, title = “”, figsize = (8, 6)):
 “””
 Plot population count in log scale (+1)
 “””
 plt.figure(figsize = figsize)
 im1 = plt.imshow(np.log1p(raster_data), cmap = “viridis”,)
 plt.title(f”{title}”, fontdict = {“fontsize”: 14})
 plt.axis(“off”)
 plt.colorbar(im1, fraction = 0.03)

结果,我得到了一个视觉上吸引人的尼泊尔人口分布图,使用自然对数标度,如下所示。以像素的亮度级别来看,很明显尼泊尔人口最高的是位于中部地区的首都加德满都。

从地理上讲,尼泊尔分为三个地区:北部与中国接壤的喜马拉雅山,南部与印度接壤的特莱平原,以及两者之间的丘陵地带。从地图上可以清楚地看到,随着尼泊尔从南到北海拔的升高,人口在减少。在地图中,我们还可以看到三个明显的白色树状结构。这些白线是由 NaN 值决定的,代表尼泊尔的三条主要河流及其支流:东部的 Koshi 河、中部的 Gandaki 河和西部的 Karnali 河。

2020 年尼泊尔人口的对数尺度空间分布。图片作者。

尼泊尔的人口密度地图描绘了一幅稍微不同的画面,如下图所示。虽然与南部特莱地区相比,丘陵地区的人口稀少,但丘陵地区的人口密度仍然适中。在南部有一些人口密度接近于零的地区(用深色斑块表示),这些地区代表了尼泊尔的国家公园。加德满都的人口密度仍然最高,其次是与印度接壤的南部地区。相反,由于极端的天气和困难的地形,北部喜马拉雅地区的人口密度非常低。

2020 年尼泊尔人口密度的对数尺度空间分布。图片作者。

可视化尼泊尔国内航空

作为一个多山的国家有自己的利弊。虽然尼泊尔拥有得天独厚的自然美景和生物多样性,但多山的地形使得丘陵和喜马拉雅地区的一些地方无法通过公路运输到达。因此,拥有国内机场对于连通性、货运和旅游业非常重要,尤其是在尼泊尔的偏远地区。

我从开放数据尼泊尔得到了尼泊尔的政治和行政边界形状文件。我用 geopandas 读取了 shapefile。我使用最新版本的 geopandas 中的explore()功能绘制了尼泊尔的交互式地图,如下所示。

使用 geopandas 探索尼泊尔的政治和行政边界形状文件。图片作者。

地理编码

从尼泊尔民航局的网站上,我得到了尼泊尔 34 个运营机场的列表。为了在尼泊尔地图上标出这些机场,我需要得到每个机场的经度和纬度。地理编码是将地址转换为地理坐标(经度和纬度)的过程。Python 中的 geopy 库对于地理编码和反向地理编码都很有用,如下面的代码片段所示。通过 geopy,我得到了尼泊尔 31 个机场的坐标。

使用 Python 中的 geopy 库进行地理编码和反向地理编码。图片作者。

在尼泊尔地图上绘制可使用的机场

有了作为 geopandas 数据框架的nepal和包含机场、纬度和经度列的df,我使用以下代码在尼泊尔地图上绘制了机场:

fig, ax = plt.subplots(figsize = (8, 6))
plt.rcParams[“font.size”] = 14nepal.plot(color = “silver”, ax = ax)for lat, lon in zip(df[“Latitude”],df[“Longitude”]):
     ax.plot(lon, lat, marker = “o”, color = “red”)

plt.title(“Operational airports in Nepal”, fontweight = “bold”)

尼泊尔地图,有 31 个运营机场。图片作者。

上图显示了尼泊尔全国 31 个运营机场的分布情况。

凸包

我很好奇,想知道如果我想通过往返旅行覆盖尼泊尔最大的区域,在假设的情况下,我必须选择什么样的飞行路线。为了得到这个问题的答案,我使用了 凸包 方法。在几何学中,凸包或凸包络是包含它的最小凸集。我创建了一个包含尼泊尔每个机场的纬度和经度的points数组,并在scipy.spatial.ConvexHull()方法中传递它。

在二维情况下,凸包对象的 flattened simplices 属性的唯一元素保存构成凸包线段的点的索引对。通过这种方式,我能够得到完成凸壳的机场的指数。

from scipy.spatial import ConvexHull, convex_hull_plot_2d
points = np.array([[lon, lat] for lat, lon in zip(airport_latitudes, airport_longitudes)])
hull = ConvexHull(points)hull_indices = np.unique(hull.simplices.flat)
hull_points = points[hull_indices, :]fig, ax = plt.subplots()
nepal.plot(ax = ax, color = “silver”)ax.plot(points[:,0], points[:,1], ‘o’)
ax.plot(hull_points[:,0], hull_points[:,1], “ro”, alpha = 0.25)for simplex in hull.simplices:
     ax.plot(points[simplex, 0], points[simplex, 1], ‘r — ‘)plt.title("Convex Hull of the operational airports in Nepal")

尼泊尔运行机场的凸包。图片作者。

从加德满都到其他地方的国内航班

正如本文第一部分所讨论的,尼泊尔首都加德满都是尼泊尔人口和人口密度最高的城市。尼泊尔唯一运营的国际机场(在编写本报告时)位于加德满都。该机场的一个独立单位也作为加德满都的国内机场。从加德满都到尼泊尔其他机场有 12 个可能的国内航班(航班连接,2022)。以下数据框flights_from_ktm显示了从加德满都特里布万国际机场起飞的 12 个机场的名称、经度和纬度。

显示与尼泊尔加德满都特里布万国际机场有联系的 12 个机场及其经度和纬度列表的数据框。

我用下面的代码画出了从加德满都到尼泊尔其他 12 个机场的飞行路线。

从加德满都到尼泊尔其他机场的国内航班代码。

连接尼泊尔加德满都的国内航线。图片作者。

上图显示了加德满都机场与尼泊尔其他 12 个机场之间的航班连接。加德满都的机场用蓝色标记表示,其他机场用红色表示。

我最近开始了解到,航班并不像平面地图上通常显示的那样走直线。飞机沿着“大圆”路线飞行,以解释地球的曲率(Wonderopolis,2017 年和福布斯,2020 年)。为了表示飞行路线中的这些曲率,我使用了一个名为draw_curve()的简单函数来绘制两点之间的曲线路径,而不是使用直线。

#Function to draw a curved line between two points
def draw_curve(p1, p2):
     “””Return curves lines between two points p1 and p2.”””
     #np.cosh(x) is equivalent to 0.5*(np.exp(x) — np.exp(-x))
     a = (p2[1] — p1[1])/ (np.cosh(p2[0]) — np.cosh(p1[0]))
     b = p1[1] — a * np.cosh(p1[0])
     x = np.linspace(p1[0], p2[0], 100)
     y = a * np.cosh(x) + b
     return x, y

连接尼泊尔加德满都的国内航线显示为曲线。图片作者。

因此,我得到了加德满都特里布万国际机场和其他 12 个机场之间的曲线,即前者有联系。

结论

在本文中,我在第一部分描述了如何在尼泊尔地图中表示人口和人口密度数据的空间分布。在接下来的部分中,我解释了使用 geopy 库获取尼泊尔国内机场坐标的地理编码技术,以及 scipy.spatial 的 ConvexHull()方法。此外,我还演示了一种在尼泊尔地图上绘制运行机场分布的方法,以及使用 geopandas 包绘制加德满都和尼泊尔其他地方之间的飞行路线。

这篇文章的资料和笔记本可以在这个 GitHub 仓库中找到。感谢您的阅读!

参考

档案信息,2022。标记图像文件。

航班连接,2022。从加德满都直飞。

福布斯,2020。为什么飞机在地图上不按直线飞行?

Wonderopolis,2017。为什么飞机不直线飞行?

2022 年世界流行音乐。开放空间人口数据和研究。

技术网络:找到你梦想的数据科学工作

原文:https://towardsdatascience.com/networking-in-tech-find-your-dream-data-science-job-4a84260a323c

作为一名新晋的数据科学家,你应该如何寻找合适的机会

图片经许可来自 anaconda.com

自 2018 年以来,一直有关于数据科学人才短缺的报道,这实质上表明没有足够的数据科学和分析人才。虽然数据科学是一个相对较新的领域,但围绕数据科学人才短缺的头条新闻描绘了一幅轻松进入面试并获得一份有回报的工作的画面,因为职位空缺比数据科学申请人多得多。

根本不是这么回事。尤其是对于初级人才来说,被考虑申请公开的数据科学职位是一件非常具有挑战性的事情。作为一名花时间指导初级员工并与其他高级团队成员一起工作的数据科学家,我知道获得一份工作并开始自己的职业生涯比通常描述的更具挑战性。然而,在寻找有回报的机会时,有一件事非常有效,那就是人际关系网。在被 Anaconda 录用之前,我参加了 2018 年的 AnacondaCON 大会,认识了很多 Anaconda 的员工,得到了推荐,然后得到了这份工作!我见过许多人,他们也是通过网络找到工作的。所以,我想分享我关于有效建立职业关系网和通过关系网找到工作的想法。

参加在线聚会和会议

建立关系网的最好方式是通过面对面的交流。虽然不一定能亲自参加社交活动,尤其是在疫情期间,但现在有更多的机会通过虚拟活动、论坛和社交媒体进行交流。

例如,如果你有兴趣与湾区的更多技术专业人士联系,你可以参加湾区在线聚会——即使你不住在那里。如果你对为某家公司工作感兴趣,去参加那家公司的活动,或者了解一下它的员工参加什么样的会议。在 Anaconda,你可以在我们的公司会议 AnacondaCON 上找到我们。我和我的同事也有兴趣参加 SciPy 和 PyData 活动,在这些活动中,您可以找到我和许多其他希望结识该领域其他人的数据专业人员。

如果你是学生,通过你的大学参加职业活动。招聘会真的有用!在这些活动中给人留下深刻印象可能会让人感到不知所措,我们经常给自己施加很大的压力来做好工作,但这并没有你想象的那么难。仅仅通过露面,与人们联系,谈论你的兴趣和你所做的工作是有价值的。

做好功课,留下印象,然后跟进

我给你讲个故事:几个月前我去参加了一个大学的职业活动,和学生聊了聊。许多学生问了我类似的问题,比如:

你们公司的招聘流程是怎样的?

你什么时候雇佣实习生?

数据科学家是做什么的?

在我交谈过的所有学生中,只有两个给我留下了印象。

怎么会?

他们花时间做了调查,并在事后进行了跟踪。一个学生读了我的博客文章,知道我对数据可视化充满热情。当他开始讲述他通过可视化交流数据的经历时,我的眼睛立刻亮了起来。事后,他把他的可视化作品发给了我,我很喜欢。另一个参加职业活动的学生问了几个关于实习的问题。活动结束后,他在 LinkedIn 上联系了我,并把他的简历用电子邮件发给了我。一个月后,他跟进,看看我们在 Anaconda 的实习生项目是否开放。这两个学生都给人留下了深刻的印象,因为他们不仅提出了独特的问题,而且还进行了跟进。表现出你对某个职位的关心和热情,对在数据科学领域找工作大有帮助。

即使公司不招人,也要寻找机会

如果你想为一家公司工作,但他们不招你感兴趣的职位,该怎么办?对于中小型公司来说,有时冷冰冰的邮件很管用!一些公司和经理认真对待这种主动性,可能会开放一个他们从来不知道自己需要的职位。或者,他们可能会雇用有更多年经验的人。无论如何都要伸出手;表现出主动性和对这个职位的兴趣有助于获得面试机会。也有可能是招聘经理很忙,没有时间招聘或面试一份工作。对你的职业积极主动可以帮助你得到一个介绍电话,并展示你可以为他们的业务增加的价值。

有时挑战在于找到合适的人的电子邮件地址来开始对话。LinkedIn 是一个很好的起点,但是如果可能的话,我建议向你的朋友寻求推荐。虽然冷冰冰的电子邮件有时会奏效,但有一个“温暖”的联系会更好。试着看看你是否有一个共同的联系人,可以把你推荐给你感兴趣的公司里的某个人。

和某人一起做一个项目

没有比和某人直接合作一个项目更好的建立关系的方法了!许多公司和员工都为开源项目做出了贡献。为开源项目做贡献将帮助你建立一份强大的简历,并与其他项目贡献者建立联系。您可能会问自己:如果我是一名初级软件工程师、数据科学家或产品经理,发现很难为开源项目代码库做出贡献,该怎么办?为项目文档做贡献同样是有价值的,也是非常受欢迎的。开源项目喜欢社区贡献者,项目维护者也非常乐意帮忙。您可以从最简单的事情开始,比如修复文档中的错别字和句子。一旦您对项目和代码库更加熟悉,您就可以查看问题报告,看看是否有一些小的 bug 修复,并在遇到困难时向项目维护人员寻求帮助。

通过面试建立关系网

在你为工作或实习面试某人之后,你会在 LinkedIn 上与他们联系吗?如果没有,就去做!即使你觉得这不是一次很好的面试,也一定要联系起来。面试是一个与该领域专业人士直接交谈的机会。即使它没有给你带来你梦想中的工作,将来也有机会与那个人合作或者从他那里得到推荐。一定要在你的 LinkedIn 信息中感谢他们,并在未来跟进,寻求指导、建议或其他机会。如前所述,我最难忘的互动是与那些采取主动和跟进的人,所以你的信息会走得很远。

领英新手?这里有一些社交技巧

一旦你掌握了窍门,LinkedIn 的使用并不困难,而且它是一个非常有益的工具,可以与个人联系,并与你的网络分享相关文章和研究。首先,试着拥有一份完整的 LinkedIn 个人资料。这意味着包括个人资料图片,标题,你的经历和教育,以及任何执照和技能。此外,您可以上传文章或研究链接来帮助补充这些经验。拥有完整的个人资料将鼓励其他人接受你的联系邀请,并让招聘人员通过 LinkedIn 算法毫不费力地找到你。如果你在找工作,你可以在你的简介中加入你愿意寻找新职位的信息,这样招聘人员就可以更容易地找到你的简介。

另一种结识与你有共同兴趣的人的方式是通过 Twitter、GitHub,甚至是 Anaconda 社区论坛。这些平台提供了找到志同道合的人的机会,你以后可以在 LinkedIn 上联系他们。

如果你正在和某人联系——特别是当它是一个冷连接时——总是在连接邀请中包含一条信息。确保包含礼貌的、有意义的、具体的信息。如果你有任何共同点(例如,学校,地点),在你的信息中提及。如果这个人在你感兴趣的特定领域工作,记下你的想法和兴趣。有意义的信息将有助于进一步的讨论,并有助于建立实际的联系。

不要立即要求转诊。人们不太可能推荐他们不认识的人。从一个联系请求开始,让你的联系人通过交谈了解你,甚至可能要求一个信息性的面试,以获得一些见面时间并建立关系。在你建立了一些融洽的关系后,你可以要求推荐。

开始学习数据科学可能很有挑战性。尽管如此,人际网络的力量可以帮助你找到理想的工作,建立职业关系,并在职业生涯中成长。我希望这些提示对你有所帮助,如果你对获得数据科学工作有任何其他建议,请告诉我!欢迎在 TwitterLinkedin 上与我联系,你可以在【https://community.anaconda.cloud/】的上继续关于这篇文章的对话。

作者索菲亚·杨 2022 年 2 月

原载于https://www.anaconda.com

神经特征重要性

原文:https://towardsdatascience.com/neural-feature-importance-1c1868a4bf53

基于人工神经网络权重的特征重要性(加上实际代码)

照片由 J KUnsplash 上拍摄

你有什么好处? 在本文中,我们将探索一种基于人工神经网络(ANN)的权重以及实际操作的 python 代码来计算相对特征重要性的方法。我们开始吧

作者图片

上图显示了一个具有单个隐藏层的人工神经网络,该隐藏层中只有一个神经元。此外,为了更好地理解,我将隐藏的神经元分解成多个部分。如您所见,我们有 3 个功能(I1、I2 和 I3)。神经特征重要性完全是从神经元的权重中推导出特征重要性,所以让我们深入到上述超级简单神经网络的矩阵乘法形式中来看看实际的权重。

作者图片

我们将对数据集中的每一行计算上述等式。这里 alpha(图中 A1)代表我们的激活函数(可以是 relu、tanh、softplus 等)。
如你所见,W11、W12 和 W13 分别代表神经元 H1 中与特征 I1、I2 和 I3 相关联的权重。将这两个形状矩阵(1×3)和(3×1)相乘,我们将得到一个形状矩阵(1×1 ),我们的偏差(B1)将被添加到该矩阵中。结果将作为输入传递给我们的激活函数,其结果将被转发给后续层。

如果我们在隐藏层中有两个神经元,矩阵乘法将如下所示

作者图片

如您所见,输入 X 权重的结果将产生一个形状为
(2 x 1)的矩阵,可以向其中添加偏差。应当注意,激活函数的结果也将是形状矩阵(2×1),即每个神经元一个值

特征重要性

相信我,我们越来越重视了,耐心点。让我们评估一下我们的单神经元案例,

作者图片

如果 W11 是 0.6,W12 是 0.3,那么这意味着,I1 的变化将比 I2 的变化产生两倍的影响(因为 0.6 = 0.3 x 2)

权重可以是正的,也可以是负的,但这只会影响输出变化的方向,而不会影响大小。因此我们可以说,如果 abs(W11) > abs(W12),那么根据神经网络权重,输入 1 (I1)比输入 2 (I2)更重要。有意思?让我们通过编码来尝试一下。

把这个编码出来

注意:我将用斜体提到列名

导入和加载数据

作者图片

我们从导入所需的库开始。除了标准的[numpy](https://numpy.org/)[pandas](https://pandas.pydata.org/),我们还导入了[GridSearchCV](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html),它将帮助我们找到模型的最佳超参数,[MLPClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.neural_network.MLPClassifier.html)(多层感知器分类器),用于定义我们的 ANN 和[MinMaxScaler](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.MinMaxScaler.html),用于将我们的数据缩放到[0,1]范围。MLP**Classifier**之所以被导入,是因为这个例子围绕着在分类问题的上下文中寻找特征重要性。

值得注意的是,我们不需要[tensorflow](https://www.tensorflow.org/) / [keras](https://keras.io/)[pytorch](https://pytorch.org/),因为MLPClassifier已经有了我们所需要的。我们可以指定隐藏层的大小、隐藏层的一个通用激活函数以及优化器和学习速率。对于二进制分类任务,它将使用 sigmoid 激活功能,而对于多分类任务,它将使用 softmax

指定导入后,我们定义一个简单的加载数据函数。正如您将在剩余代码中看到的,我非常喜欢在 python 中使用类型提示

定义常数

作者图片

现在我们跳到一些数据处理,定义变量和常量以避免重复和给文字命名总是有帮助的。我们指定分类列、数字列、要删除的列( PassengerId 因为它是一个 Id,而 Name 因为对名称进行编码没有意义)、目标列以及一个常量OHE_PREFIX,当我们执行一次性编码时将使用这个常量。

我想在这里澄清两件事,第一件是我没有决定放弃号的原因。做一些简单的分析,就可以发现号首先不是唯一的。此外,如果您找出每张票的频率(出现次数)以及目标列中 1 的相应百分比,您将会看到号确实在目标列的预测中发挥了作用

作者图片

需要澄清的第二点是,尽管 Pclass 被表示为整数(1,2,3 ),但是因为它表示第一、第二和第三类,所以它实际上是一个顺序分类列,因此我将其视为分类列,而不是数字列。

输入空值

作者图片

接下来,我们定义一个函数来估算缺失值。正如你所看到的,这个函数获取了一个 pandas 数据帧,并返回一个 pandas 数据帧,只需查看函数签名(不需要查看函数体)就可以推断出来,这就是键入提示的妙处。关于它们,我们用中值填充数字列中的空值,对于分类列,我们用最频繁(非空)的值填充它们。

缩放数字

作者图片

为了根据我们的数据训练神经网络,确保所有特征都在相同的尺度上是很重要的。此外,较小的值有助于更快地达到全局最小值。通常,数据被缩放到[0,1]范围或[-1,1]范围。这里我们使用 sklearn 的MinMaxScaler,它默认将输入数据缩放到[0,1]范围。因为我们不能缩放类别,所以我们传递数字列并只缩放它们。这里的 num_cols 听起来像“列数”,但是提供的输入提示表明它是一个字符串列表。

编码类别

作者图片

现在让我们把注意力转向范畴。我们知道,我们不能直接用它们来训练我们的神经网络,我们必须以某种方式对它们进行编码(将它们转换成数字)。让我们探索几个选项。我们可以顺序编码(标签编码是针对标签(目标列)的)!)通过给列中的每个唯一值分配一个整数来对它们进行赋值。但是由于我们所有的数据都需要在范围[0,1]内,我们必须调整编码值。请想一想。

例如:假设值“Susmit”被编码为 3,然后缩放为 0.21,值“Python”被编码并缩放为 0.22,这没有任何意义,对吗?

我们可以使用[TargetEncoder](https://contrib.scikit-learn.org/category_encoders/targetencoder.html),但是我们需要缩放。同样,在我们必须执行逆变换操作的情况下,我们将必须存储映射,但是由于可能存在编码值对于两个或更多不同类别值是相同的情况,所以不可能执行精确的逆变换。

这里使用 pandas 的get_dummies方法执行一个热编码。我们还指定了drop_first=True,这将确保没有重复的信息。这就是我们的常量OHE_PREFIX也被用来保持控制的地方。

作者图片

如上图所示,它有助于控制编码列的名称和为其生成列的值之间的分隔符。通常分隔符值应该是数据中不会出现的值(在我们的例子中是列名)。当我们想要找到原始列的特性重要性(而不是作为一次性编码的结果生成的列)时,将需要分隔符值。

定义模型架构

作者图片

现在我们定义一个简单的函数,它定义了我们的模型,没有需要调整的超参数。我们将对我们的隐藏层使用[Rectified Linear Unit](https://en.wikipedia.org/wiki/Rectifier_(neural_networks)) (ReLU)激活函数,它只不过是输入 x 的max(0, x)。连同其他一些超参数,我们指定我们想要一个有 512 个神经元的隐藏层。如果对于一些其他任务,我们想要两个隐藏层,第一个具有 256 个神经元,第二个具有 512 个神经元,我们可以将其定义为,

hidden_layer_sizes = (256, 512,)

定义网格搜索 CV

作者图片

是时候进行一些超参数优化了!让我们理解我们定义的是什么。我们希望使用我们的model,对于learning_rate_initalpha的每个组合,我们希望使用(Stratified)KFold策略和k=5进行交叉验证,以确定最佳表现模型,从而确定最佳超参数,将使用f1_macro评分。此外,我们希望拟合/训练在整个数据集上找到的最佳模型,我们通过refit=True指定该数据集,最后但并非最不重要的是,我们希望使用我们机器的所有可用内核(n_jobs=-1)。

最后,对于特性的重要性,

计算绝对平均重量

作者图片

在开头显示的神经网络的超级简单的例子中,在隐藏层中只有单个神经元。因此,我们能够直接使用对应于输入特征的权重。但是在我们的例子中,我们的隐藏层有 512 个神经元。所以我们取每个特征的所有神经元系数的平均值。因为我们只关心大小,而不是方向,所以我们取绝对值。

为了理解第一行中发生了什么,让我们探索一下model.coefs_。首先,顾名思义,它存储了神经元之间的系数/权重。

print([coef.shape for coef in model.coefs_]) 
# Output: [(835, 512), (512, 2)]

如你所见,这里有两个元素,为什么?第一个元素存储输入层和隐藏层之间的权重,第二个元素存储隐藏层和输出层之间的权重。第一个元素中有 835 行和 512 列,因为在删除两个特征并添加一个热编码特征而不是没有目标列的分类特征后,我们的数据中有 835 个特征。512 因为我们的隐藏层有 512 个神经元。

(835 , 512)隐藏层中每个神经元一列,每个特征一行

因此,当我们执行时,[i.mean() for i in model.coefs_[0]]我们取所有 512 个系数的平均值,835 个特征/(矩阵中的行)中的每一个都有一个平均值。

一个热编码列的分组权重

作者图片

我们需要执行这个步骤,因为我们使用了一个热编码,它将一个列分割成多个列,用 0 或 1 表示值的存在。例如,列abowed有三个唯一的值 S、C 和 q。因为我们已经指定了drop_first=True,所以生成了两个列。一个用于表示值 Q 的存在,一个用于表示值 s。如果两者都不存在,则值为 c。现在,登上的系数是登上 __OHE__Q登上 __OHE__S. 的系数之和。因此我们提取原始列名,按其分组,并取和。

称量重量

作者图片

为了确保所有的权重都在同一标度上,并且表示最重要特征的最大值是值 1,表示最不重要特征的最小值是值 0,我们执行最小-最大标度。

特征重要性函数

作者图片

最后,我们调用与计算特征重要性相关的函数,并将结果转换成一个字典。

缝合在一起

作者图片

你可能已经猜到了,我是一个超级粉丝,

函数的第一条规则是它们应该很小。函数的第二个规则是它们应该比那个小。罗伯特·马丁

同样,

函数应该做一件事。他们应该做好这件事。他们应该只做这件事。罗伯特·马丁

以下是完整的代码供您参考

乡亲们,就这样吧!

如果你喜欢阅读,一定要读一读这篇关于云文件浏览器(一种用于云的文件浏览器和查看器)的发展故事的博客:https://medium . com/@ susmit . py/File-Viewer-for-the-Cloud-65de a 0455 DC 7(https://cloudfileviewer.web.app/

如果你想讨论一些很棒的东西,你可以在 LinkedInsusmit-vengurlekar上联系我!

三维形状和场景的神经隐式表示

原文:https://towardsdatascience.com/neural-implicit-representations-for-3d-shapes-and-scenes-c6750dff49db

近年来,有一个帮助解决计算机图形任务的神经隐式表示的爆炸。在本文中,我将重点讨论它们在三种不同任务中的适用性——形状表示、新颖的视图合成和基于图像的 3D 重建。

近年来,计算机图形学领域与深度学习一起蓬勃发展,有几项贡献对该领域产生了重大影响。计算机图形学解决了用计算机创建图像的问题。虽然这听起来很简单,但创建物理上正确和真实的图像肯定是一项复杂的任务。要做到这一点,人们需要理解如何表现 3D 场景,3D 场景通常包含光线、不同的材料、几种几何形状和拍摄照片的相机型号。幸运的是,研究人员已经找到了使用深度学习模型解决计算机图形问题的迷人方法。

简单地说但不准确地说,这篇文章旨在提炼以下基于深度学习的计算机图形任务解决方案的进展:

  1. 2017–2019—如何用神经网络(NN)表示形状。我将把重点放在使用带符号距离函数来实现这一点的作品上。
  2. 2019–2020—如何用 NNs 表现场景。具体来说,神经网络如何表现一个完整的场景,包括闪电、纹理&形状的所有精细细节?
  3. 2021 —2022 —我们如何理解有 NNs 的场景?具体来说,给定神经场景表示,我们如何重建场景中的 3D 表面?
  4. 2022 年——我们如何让它更快?

这仅仅是我将进展提炼为不同阶段的方式,而显然,这个领域并没有以如此清晰的方式进展。在 2021 年之前,人们试图理解具有 NNs 的场景,形状表示仍然是人们正在努力的事情(在形状之间进行概括仍然是一个问题)。然而,在我看来,这种顺序使得我将在这篇文章中讨论的观点和作品更容易理解。

计算机图形学的简要背景

而我假设读者熟悉深度学习(梯度下降、神经网络等。),为了完整起见,我将在这里简单介绍一下本文中用到的一些术语的背景。

光线跟踪与渲染光栅化

注意,渲染图像有两个类别——栅格化光线追踪 。查看Nvidia的这篇文章,了解他们的介绍,我强烈推荐观看迪士尼在 Hyperion 上的视频,这是他们的(光线跟踪)渲染引擎,直观地解释了通过与物体的几次碰撞,从光源到相机的光线路径的计算建模过程。然而,如果你不太熟悉渲染,请注意光栅化在现代 GPU 上要快得多,然而它并不像基于光线跟踪的渲染那样模拟物理世界。在这篇文章中,我将重点关注利用差分光线跟踪渲染的作品,因为它们往往会产生更好的结果,并且我将描述使它们可用于实时应用程序的方法。

三维几何图形表示

虽然很清楚 3D 场景包含几个 3D 对象,但不清楚如何表示它们的几何形状。没有一种计算和存储效率都很高的规范表示法能够表示任意拓扑的高分辨率几何图形。几种表示进化而来,最常用的有网格基于体素的基于点的。如果您不熟悉这些术语,我建议您在继续之前阅读它们。然而,需要注意的重要一点是,这些方法在效率(基于体素的表示内存使用相对于分辨率以立方增长)表现性(头发等精细几何体很难使用网格建模)或拓扑约束(直接从点云生成防水表面 —一个没有洞的闭合表面——可能不是一项简单的任务)。

上面所有的表示都有一个共同的特性——它们依赖于几何的显式表达。但是,它是通过用离散对象(如三角形、栅格或简单的点)来近似 3D 表面来实现的。另一种表示 3D 对象的常见方式是使用连续** 隐式表示法通常,隐函数将几何图形表示为在 3D 点上操作的函数,满足:**

  1. F(x,y,z)<0 — interior point
  2. F(x,y,z)>0-外部点
  3. F(x,y,z)= 0-曲面点

具体地说,有符号距离函数 (SDF)满足这些性质。SDF 就是给定点到物体边界的距离,并根据上述规则设置距离的符号。如果隐式表示对你来说是新的,我推荐阅读密苏里 CS8620 课程的讲义。请记住,SDF 有许多好的属性(可以用来很容易地计算相交,使光线投射快速)。然而,如何获得某种形状的 SDF 并不是一件小事?

视觉上,球体的 SDF 可以被视为:

围绕球体的有符号距离的视觉。图源

计算机图形任务

回想一下,计算机图形学的总体目标是使用计算机创建图像。这个目标可以分解成几个问题。今天,我将重点讨论深度学习如何帮助解决以下计算机任务:

  1. ****形状表示——正如我在上面详述的,表示 3D 形状并不简单。虽然存在几种经典的表示方法,但我将在这篇文章中详细介绍如何使用神经网络来有效地表示连续的 3D 形状(特别是 SDF)。
  2. 新颖的视图合成(NVS) —给定一个场景的几幅图像,我们如何从新的视图生成图像?需要多少幅图像,除了视图之外,我们还能控制哪些属性(例如,我们能否用不同的光照生成视图)?
  3. 曲面重建— 给定一个曲面的多幅图像,我们如何重建它的 3D 模型?

人们现在可能想知道求解 NVS 是否隐含地解决了表面重建任务。有理由认为,如果计算机可以生成场景的新视图,它就必须(隐含地)重建场景的 3D 模型。因此,似乎解决 NVS(即,将 NeRF 拟合到场景)就解决了表面重建问题。虽然这听起来是正确的,但并不完全正确,我将描述一种通过解决 NVS 任务来重建曲面的机制。

1。形状表示的神经网络

如何在捕捉高频局部细节的同时获得高效紧凑的表现是一项具有挑战性的任务。虽然隐式表示是表示形状的有效方式,但使用经典方法很难获得它们。DeepSDF 是深度学习在这项任务中的第一个成功应用。

1.1deep SDF—表示 SDF 的神经网络

给定一个网格,如何求它的 SDF?经典方法通过用 3D 网格离散化空间来近似 SDF,其中每个体素代表一个距离。这些体素在内存中很昂贵,尤其是当需要细粒度的近似时。

DeepSDF 建议使用神经网络对其进行近似,该网络简单地学习预测给定点的 SDF 值。为了训练网络,他们用相应的 SDF 采样了大量的点。他们对物体表面的更多点进行采样,以获取详细的图像。

一旦我们可以使用神经网络来表示 SDF,关于这种表示的几个问题就出现了。一个网络可以概括为代表几个形状,而不是为每个形状训练一个特定的网络吗?如果是这样的话,多种形状的连续表示是否使我们能够在它们之间进行插值?在不深入细节的情况下,我注意到 DeepSDF 确实显示了形状之间的某种程度的一般化,这是通过将 DeepSDF 网络限制在可训练的形状代码上来实现的。有关更多详细信息,请参考 DeepSDF 第 4 节。

1.2.DeepLS—用计算换取内存****

虽然 DeepSDF 在逼近 SDF 方面取得了非常好的结果,但训练和推断的成本很高。MLP 网络通常是根据~2M 参数构建的,这使得高分辨率 3D 形状的训练和推断成本很高。回想一下,DeepSDF 是一个基于坐标的(即一个网络一次只在一个坐标上运行)网络。因此,在训练期间,必须为每个单点计算每个~2M 参数的梯度。用 300-500k 个点对特定网格的 DeepSDF 进行训练,这导致训练的计算开销很大。因此,使用网络进行推理也是昂贵的。

一个天真的建议是通过简单地使用一个较小的网络来使 DeepSDF 的训练和推理更快。然而,正如 DeepSDF 所示,他们在考虑了速度与精度的权衡后选择了网络架构,因为他们发现较小的模型性能更差。需要一种更复杂的方法。

作为网络深度函数的回归精度。图来自 DeepSDF

DeepLS 建议通过结合经典计算机图形学文献中一个至关重要且众所周知的观察结果来解决这个问题——一个大的表面可以分解成** 个局部形状。因此,他们将空间划分为一个体素网格,每个体素在一个小的局部邻域中存储有关表面的信息。该信息被存储为学习潜在代码(即,为每个体素 V_i 存储潜在代码 z_i)。这样,网格本质上编码关于形状的局部信息,并且网络将该局部信息映射到 SDF 值。本质上,他们的方法是:**

  1. 将空间划分为规则的体素网格。
  2. 对于给定的点, x ,在网格上找到对应的体素 V_i
  3. 将 x 变换到 V_i 局部坐标系。T _ I(x)=xx _ I
  4. 将潜在代码(z_i)和转换后的坐标(T_i)输入网络,并回归 SDF。
  5. 在优化潜在代码(z_i)和神经网络参数的同时,对大量步骤重复 2–5。

DeepSDF 和 DeepLS 方法的 2D 比较。DeepSDF 使用一个点和一些形状代码(z)来表示特定的形状 SDF,而 DeepLS 利用网格来表示局部形状代码。注意,DeepLS 优化了神经网络参数和潜在向量(z_i)。该图取自深度

在这里,我建议用另一种方式来考虑他们的建议。分析学习问题的一个常见主题是将任务分成两个不同的子任务。首先,给定原始数据(在我们的例子中是 3D 点)的特征提取任务产生描述数据的提取特征向量。然后,第二个子任务是在给定该特征向量的情况下回归 SDF 值。传统上,特征提取部分负责将原始数据转换为良好的表示特征,从而简化回归(一般是下游任务)。虽然这种描述听起来可能很模糊,但今天的深度学习实践者已经习惯于使用大型冷冻(已经在大规模数据集上训练过)神经网络作为特征提取器,并训练一个小型模型,该模型将这些特征作为给定任务的输入。

考虑到这一点,我将 DeepLS 的网格视为一个局部特征提取器,将神经网络视为一个简单的回归模型。所以回归模型可以小很多。具体来说,他们的神经网络只有 50K 个参数,相比之下 DeepSDF 的 ~2M 个参数。此外,虽然网格本身可能很大(甚至比 DeepSDF 网络本身更大),但是在每次通过网络时,只有少量的参数影响计算!这使得训练和推理速度更快,同时以计算换取内存。****

总的来说,使用网格,DeepLS 明显比 DeepSDF 更有效。在下面的例子中,DeepLS 的训练速度比 DeepSDF 快 10,000 倍。

斯坦福兔子上进行 DeepLS 和 DeepSDF 训练时间对比。DeepSDF 用了 8 天才在 1 分钟后达到 DeepLS 结果。大约快了 10,000 倍。图来自深处

请注意,DeepLS 从几个方面衡量了他们的贡献,而我只关注效率。更多细节请参考他们的论文。

2.场景表示的神经网络

在这一部分,我将重点介绍场景表现任务。具体来说,在使用神经网络解决新颖的视图合成任务上,如 SRNNeRF 所建议的。起初,这可能与上一部分没有太大关系,但在下一部分,它将有希望变得有意义。

2.1。SRN—用神经网络表示场景

到目前为止,我们讨论了形状表示。然而,为了解决新颖的视图合成任务,需要一个场景表示。理想情况下,这种表示将是连续的,并使人们能够从不同的视图中查询场景并控制场景属性(例如,其光源)。 SRN 提出用神经网络来表现场景。

等等,我们如何训练一个神经网络来代表场景?为了做到这一点,我们需要以某种方式监督网络如何很好地表现场景。这意味着我们需要它从场景中的特定视图生成图像,并训练它最小化生成的图像和真实图像之间的差异。虽然近年来使用神经网络(所谓的生成模型,如著名的 StyleGAN )生成图像取得了巨大的成功,但它未能以多视图一致的方式做到这一点,并且在 3D 视点上精确地调节它是非常重要的。

因此,SRN 建议使用一个可区分渲染器来训练一个网络来表示场景。虽然 SRN 训练机制有些复杂,但总体思路可以直观地简化。给定一个 3D 坐标,神经网络将其表示为该坐标处场景的特征。然后,他们使用可区分的渲染器为该坐标的特征生成相应的像素颜色。对于给定的特定像素,其颜色受沿到达相机并与该像素相交的光线的多个 3D 坐标的影响。从相机发出的光线穿过像素可以在下图中看到。****

体光线投射的四个基本步骤。对于图像平面中的每个像素(图像底部的条),一条射线穿过体积(1)。沿着该光线采样几个坐标(2),并计算它们的颜色(3)。然后,它们的颜色的集合定义了像素颜色。图来自维基百科

然而,由于沿着那条射线有无限的 3D 坐标,并且场景几何在训练期间隐含地出现,SRN 不得不处理一个关键问题——如何决定沿着射线采样什么点?天真地为每条光线采样大量的 3D 坐标而不知道聚焦在哪里是不切实际的。

因此,SRN 提出了一种有点复杂的时间射线行进层(称为射线行进 LSTM)来解决采样问题。具体来说,他们使用了一个 LSTM,给定相机的光线起点,迭代地寻找其与场景几何图形的交点 3D 坐标,并返回该 3D 坐标提取的特征。然后,使用像素生成器网络将 3D 坐标特征向量映射到单个 RGB 值,即对应的像素颜色。通过这样做,他们将生成的像素颜色与给定图像的原始像素颜色进行比较,从而仅使用图像监督场景表示网络

虽然它们的机制有些复杂,但我建议你记住神经表征学习的一般流程,因为它更容易掌握。基本上,给定一个场景的一组图像,我们使用神经网络来表示该场景,并从不同的视图渲染该场景,只需简单地管理网络来产生与原始视图相同的场景视图。

在 SRN 的原始论文中,一个被认为可能是未来工作的关键限制是建模视图和闪电效果。人们应该如何将这些信息编码到 SRN 中还不清楚。

2.2。然后就是NeRF——神经辐射场

正如在研究中经常发生的那样,简单的解决方案往往在复杂的解决方案被提出后出现,以更直观的机制产生更好的结果。NeRF 的作者建议使用(基于坐标的)MLP 对场景建模,给定 5D 输入(位置和观察方向)产生颜色和密度(RGB-alpha)。NeRF 没有将坐标映射到潜在特征,也没有使用 SRN 的复杂机制来生成相应的像素颜色,而是直接回归该坐标的 RGB-alpha 值,并将其提供给可区分的光线行进渲染器。

NeRF 通过沿相机光线采样 5D 坐标(位置和观察方向)来合成图像(a),将这些位置输入 MLP 以产生颜色和体积密度(b),并使用体积渲染技术将这些值合成为图像(c)。图来自 NeRF 论文

体积渲染听起来可能很吓人。但是需要注意的重要一点是,体绘制基本上就是用来近似沿着到达相机的光线的总光亮度 。因为我们要对光线求和,所以我们可以衡量“来自光线坐标上某个位置的光线对总光线的影响有多大”是至关重要的关于场景属性(遮挡、材质等)。).直觉上,我们会给不透明的物体较大的权重,给透明的物体较小的权重,让我们能够透过玻璃而不是透过桌子看到东西。这是预测密度(sigma)值在 NeRF 算法中的关键规则。****

使用基于 5D 坐标的 MLP,NeRF 通过计算来估计每个像素的颜色:

计算预测颜色(C^(r)的公式,用于从相机通过像素追踪的光线。T_i 计算累积透射比(即光线传播到一定距离而不碰到不透明粒子的概率)。 σi 表示给定 5D 坐标下的预测密度(alpha)值, δi 为沿射线相邻样本之间的距离。

我跳过了具体的部分,比如关于 NeRF 训练机制的细节,沿着射线的采样坐标,以及位置编码自一个lot 写成 上有 ****

3.理解场景的神经网络

NeRF 提出了一个惊人的强大的场景表示。然而,它将所有场景属性耦合在一起,从结构到闪电。在这里,我将描述从中提取一个几何图形(表面)的方法。

VolSDF&NeuS—从密度到有符号距离

NeRF 在捕捉场景方面表现得非常成功。然而,虽然 NeRF 可以生成场景的新视图,但尚不清楚如何提取几何图形。起初,这可能听起来微不足道——我们已经预测了每个坐标的密度值,这应该模糊地描述了一个表面!事实上,表面周围的密度值较高,远离表面的密度值较低。然而,定义“高”密度的阈值绝非易事。此外,它依赖于视图,这不是几何图形所需的特性。因此,研究人员寻找创造性的方法,通过明确学习几何来修改 NeRF 架构。

回想一下,深度学习的直观连续几何表示是 SDF。希望我们可以训练一个 NeRF 来预测 SDF 值,并仍然使用体绘制来端到端地训练它(并直接从场景图像中获得监督,从而在没有任何 3D 监督的情况下重建几何图形!).幸运的是,在 NeurIPS 2021 上,两篇有趣的论文解决了这个特定的问题。他们的作品被戏称为 VolSDFNeuS (读作“新闻”)。

他们都建议训练两个网络——一个 SDF 网络和一个颜色/外观网络。他们的不同之处在于他们建议将 SDF 值转换为密度值,并在体绘制中使用它们。主要思想就是这么简单——我们想要转换 SDF 值,这样靠近表面的点将接收高 alpha 值,而远离表面的点将接收低 alpha 值。这样,近表面点的颜色将对光线的积分产生最大的影响。如 NeuS 所述,SDF 到密度的转换必须满足以下特性:

  1. 无偏 —对于给定光线,在 SDF 函数产生 0 的点(即表面上的点),密度值应达到局部最大值。注意,我们只希望局部最大,而不是全局最大,因为沿射线可能有几个表面交点。****
  2. 遮挡感知— 当两个点具有相同的 SDF 值时,更靠近相机的点对最终输出颜色的贡献更大。

直观地说,该转换只是使用以零为中心的逻辑分布将 SDF 值映射到密度值。这很方便,因为它是一个以 0 为中心的钟形分布,因此将最大密度分配给 SDF 值 0,并以递减方式将密度分配给其他地方的 SDF 值。

均值为 0、标准差为 1 的逻辑密度分布的 PDF。人们可以天真地使用这种分布将 SDF 值映射到密度值。然而,如 NeuS 和 VolSDF 所示,需要更复杂的方法来使变换不偏不倚且具有遮挡意识。

我建议读者参考相关论文,以了解构建从 SDF 到密度值的转换的更复杂的方法。

虽然他们都提出了一种直接从场景观察中学习 SDF 的简洁方法,但他们仍然有一个关键的限制——从这些相对较大的基于坐标的 MLP 中进行推断的训练&仍然非常缓慢。具体来说,针对特定场景训练他们每个人需要~ 12-14 小时

4.把点连起来,让它更快!

4.1。InstantNGP—更好的内存与计算的权衡****

有两个可能的方向使基于 NeRF 的方法的训练和推断更快:沿着每条射线采样更少的点,或者采样更快。对于采样更少的点有一些有趣的想法,但是在这里我关注的是采样更快。

回想一下,DeepLS 通过引入一个可学习的体素网格,使 DeepSDF 的采样速度更快。然而,随着内存相对于网格的立方增长,它以昂贵的内存成本换取了采样效率。有多贵?对于具有 16 维潜在向量的 128 大小的简单网格,需要存储 ~33.6M 网格参数,而不考虑 MLP 大小(通常很小,大约 100-300k 参数)。不幸的是,简单地缩小网格会严重影响捕捉精细几何图形的性能和能力。需要更好的解决方案。

幸运的是,今年早些时候(2022 年), InstantNGP 建议通过用多分辨率散列网格替换网格来显著减少内存使用。instant ngp 背后的关键思想是通过几个不同大小的网格来捕捉粗略和精细的细节。低分辨率网格只占用少量内存,并且只捕捉粗略的细节。高分辨率的网格可能捕捉到细微的细节,但代价是占用大量内存。因此,他们建议用哈希表索引高分辨率网格。传统上,在计算机科学中,哈希表用于权衡计算时间和内存使用。他们的方法如下:

  1. 将空间分成几个不同分辨率级别的网格(从粗到细的网格)。
  2. 对于给定点, x ,在每个分辨率级别找到包含体素的角的索引。
  3. 使用每个分辨率级别的哈希表,查找角的特征向量。
  4. 三线性插值每个分辨率级别的特征向量。
  5. 将每个分辨率级别的向量连接起来,并将其输入神经网络。
  6. 对大量步骤重复 2–5,同时优化多分辨率网格的特征向量和神经网络参数。

下图总结了这一过程:

2D 的 InstantNGP 多分辨率散列网格演示。给定一个坐标 x,找出每个分辨率下的包围体素(标为红色和蓝色)并对它们的边缘进行插值。连接多分辨率插值向量,并将它们输入到神经网络中,然后执行您想要的基于坐标的任务。图来自 InstantNGP

然而,指出关于 InstantNGP 的一个不直观的问题是至关重要的。当一个人使用哈希表时,他需要解决冲突的可能性(即,两个不同的 3D 坐标被映射到相同的特征向量)以及如何解决这样的冲突。关于这一主题的传统计算机科学文献充满了许多复杂的机制和技巧,以减少冲突的可能性,并在冲突发生时找到快速解决冲突的方法。然而,InstantNGP 仅仅依靠神经网络来学习消除哈希冲突 本身

请注意,InstantNGP 提供了有效训练基于坐标的神经网络的通用方法。具体来说,他们展示了他们的方法对于 NeRF 以及这里没有讨论的其他几个任务是多么有效。为了理解他们的方法有多有效,请注意 NeRF 网络对单个场景的训练时间从 12 小时减少到 5 秒。也就是大约 2 年左右的时间~ 8500 x

4.2. MonoSDF —表面重建,带体积渲染,更快!

我们学习了如何修改 NeRF 训练机制,以支持显式 SDF 表示学习。此外,我们还学习了如何通过合并多分辨率哈希表来加快 NeRF 的训练和推理。MonoSDF 结合了这两种方法以及几何线索,使得从设定好的图像进行表面重建更快更准确。

请注意,MonoSDF 不仅使它更快,而且旨在更好地执行,因为它为网络提供了几何线索。具体而言,使用单目几何预测领域的进展(即,直接从单个 RGB 图像预测正常或深度图像),它监督网络为 NeRF 训练渲染深度和正常图像以及重建的单目图像。这些几何线索改善了重建质量和优化时间。他们的(高层)架构是:

MonoSDF 使用多分辨率特征格网(如 InstantNGP 中所示)来预测给定坐标的 SDF 值、法线(n)和深度(z)值。他们使用体绘制来重建场景,并通过预测深度和正常图像的预训练网络进行监督。图来自 MonoSDF

结论

近年来,有一个帮助解决计算机图形任务的神经隐式表示的爆炸。在这篇文章中,我重点讨论了它们在三种不同任务中的适用性——形状表示、新颖的视图合成和基于图像的 3D 重建。

拥有神经表示是解决许多有趣任务的使能器,但仍有很大的改进空间。我们需要可编辑的(具有显式控制)、可扩展的(不仅适用于受控环境中的小场景)、可快速查询的和可概括的表示。这些要求中的每一项都包含许多挑战。我希望在接下来的几年里,当我回过头来看这篇文章时,会惊讶于研究界是如何再一次带领我们取得了今天看来几乎难以置信的进步。

Excel 中从头开始的神经网络

原文:https://towardsdatascience.com/neural-network-from-scratch-in-excel-4774f6131cdb

为了更好地理解反向传播的工作原理

虽然使用 Excel/Google Sheets 来解决机器学习算法的实际问题肯定是一个坏主意,但使用简单的公式和简单的数据集从零开始实现算法对理解算法的工作方式非常有帮助。

我写这些文章是为了解释梯度下降如何用于线性回归和逻辑回归:

在这篇文章中,我将分享我如何在 Excel 中实现一个带有梯度下降(或反向传播)的简单** 神经网络。**

如果你不熟悉神经网络的原理,我写了这篇文章用非常直观的方式来解释。而如果想从头开始用 python 实现,我也写了这篇文章

所以在这篇文章中,你不需要了解 python 或者其他编程语言,所以你没有借口!您可以使用此链接查看所有不同的计算。

现在让我们把手弄脏吧!

杰里米·毕晓普在 Unsplash 上的照片

从零开始为 ML 的谷歌表

如果你想得到谷歌表,请在 Ko-fi 上支持我。

你可以得到我创建的所有谷歌表单(梯度下降的线性回归,逻辑回归,神经网络,KNN,k-means,等等)。)和下面的链接。

https://ko-fi.com/s/4ddca6dff1

一个简单的神经网络

数据集

首先,我使用一个非常简单的数据集,只有一个特征 x,目标变量 y 是二进制的。你可以看到下图。

数据集(作者提供的图片)

神经网络

数据是不可线性分离的。所以逻辑回归是不够的。如果你看过我这篇关于构建神经网络应该选择多少层的文章,你应该知道,对于这个数据集来说,一个隐层两个神经元就够了。这里我们将使用 sigmoid 函数作为激活函数。

神经网络(图片作者提供)

回想一下,神经网络是一个数学函数,这里是与上图相关的函数。

神经网络数学函数(图片由作者提供)

如你所见,带有圆圈和链接的神经网络图更清晰地显示了所有系数。

功能的实现

在 Excel/Google 工作表的工作表 m (针对模型)中,我使用以下系数值实现了该函数。

为神经网络选择的系数(图片由作者提供)

如你所知,如果你读了这篇关于成本函数的文章,就会发现有多个全局最小值。这些只是这些系数的一组令人满意的值。

为了简化公式,我显示了中间结果(隐藏层)A1 和 A2。那么输出的最终结果就是这两者的组合。你可以看到这个神经网络可以完美地将数据集分成两类。

神经网络图(图片作者提供)

现在,您可以更改系数值,看看不同函数的图形会如何变化。

这是容易的部分。现在的问题是:如何找到这些系数的值?我们来做反向传播部分。

梯度下降

在工作表 gd (针对梯度下降),你可以找到所有的计算细节。

输入

首先,我们有输入数据:

输入数据(图片由作者提供)

和系数的初始值。所以你可以改变这些值来玩梯度下降。

系数的初始值(图片由作者提供)

正向传播

对于从 AG 到 BP 的列,我们有正向传播阶段。我们首先计算 A1 和 A2,然后是输出。

正向传播(图片由作者提供)

价值函数

对于从 BQ 到 CN 的列,我们计算误差和成本函数。我们尽量详细地做所有的计算,这样我们可以避免错误。

误差和成本函数(图片由作者提供)

回想一下,有一个成本函数:

成本函数(图片由作者提供)

反向传播

这篇文章里,我写了所有的公式。如果不知道结果从何而来就能拥有很多。

对于从 c0 到 D1 的列,有 a11 和 a12 的偏导数:

作者图片

在从 DM 到 EJ 的列中,有 b11 和 b12 的偏导数:

作者图片

在从 EK 到 FH 的列中,有 a21 和 a22 的偏导数:

作者图片

在从 f1 到 FT 的列中,有 b2 的偏导数:

作者图片

最后,我们对所有 12 个观测值的偏导数求和,列从 Z 到 FI。

作者图片

梯度下降

这些偏导数将允许我们对从 R 到 x 的列中的每个系数进行梯度下降。您可以通过上图直观地看到下降阶段的值是如何变化的。

作者图片

在下降过程中,成本函数下降,所以我们也可以将其可视化。我在 y 列计算。

作者图片

对于每组系数值,我们可以将神经网络的输出可视化。我使用表 mh(模型隐藏)来创建下图:

作者图片

当然,我们可以通过连续组合不同系数值的图表来创建一个漂亮的 gif。但是手工用 Excel 输出会很繁琐。这是我用 r 创建的 gif。

作者图片

结论

如您所见,对于 12 个观测值的数据集,我们可以在 Excel 中实现梯度下降。通过 300 次迭代,步长为 0.1,以及一些精心选择的初始值,我们可以创建一些很好的梯度下降可视化,以及一组令人满意的待确定的 7 个系数的值。

玩 Google Sheet!

您可以更改一些值并可视化所有中间结果:

  • 数据集
  • 初始值
  • 梯度下降的步骤

当测试系数的初始值时,您可以看到,神经网络有时会陷入局部最小值

局部最小神经网络(图片由作者提供)

神经网络是一种有监督的机器学习算法。你想有一个监督机器学习算法的完整概述吗?那么你应该看看这篇文章:

从头开始的神经网络(无数字)

原文:https://towardsdatascience.com/neural-network-from-scratch-no-numpy-part-i-7c3609308ed8

使用标准 Python 库简单实现神经网络

凯文·卡纳斯在 Unsplash 上的照片

动机

在过去的几年里,人工智能一直在以惊人的速度发展。已经有大量的高级框架,使您能够构建模型和运行培训,而不必担心所有的技术细节。这是个好消息。它不仅让事情变得更容易,还让更多人可以使用人工智能算法。另一方面,这是有代价的。我们不必再深入理解神经网络的细节,这可能会造成威胁。为了做好你的工作,你需要更深入。明确一点,我并不是说我们都应该从头开始,自己编写代码。我的意思是,我们应该不时地回到基础,并确保我们在训练模型时理解实际发生了什么。在我看来,检查你是否真正理解神经网络如何学习的最好方法是自己实现整个过程。

本文将描述用 Python 实现一些基本神经网络的过程。我有意限制自己只使用标准的 Python 库。我希望这篇文章对那些不熟悉线性代数的人也有价值,这样他们可以获得关于神经网络如何学习的直觉。让我们开始吧。

基础

在我们深入细节之前,让我们提一些关于神经网络的基本事实:

  • 神经网络由对输入执行某些计算并将计算值传递给下一层的层组成。
  • 各层中执行的计算依赖于一些称为权重的数字
  • 可以在称为训练的过程中修改权重,以便改变输入的变换,使得输出在某些目标方面更好地符合训练数据
  • 这个目标叫做损失函数。它告诉我们模型的错误程度,例如,当训练模型根据人的声音预测人的年龄时,损失可能是训练样本的年平均绝对误差。

如果你对这些概念不熟悉,或者想了解更多,我推荐一个由 3Blue1Brown 撰写的关于深度学习的系列。

履行

代码可在这里获得。就本文讨论的范围而言,我希望实现容易遵循。这是非常低效的,因为没有涉及矢量化和线性代数。

前进传球

让我们从构建对输入执行计算所需的一切开始。神经网络是由层组成的,所以我们肯定需要一个对象来表示一层。让我们把它变成一个抽象类。通过这样做,我们还不需要考虑如何实现细节。

因为我们已经定义了一个层的模板,所以我们可以创建一个模型(神经网络)。

目前,模型只能对输入执行正向传递并返回输出。每层的输出存储在属性输出中。以后还会用到。

我们需要做的另一件事是实现一个层,因为我们只有一个模板。正如你所料,我们将创建一个线性层。在深入研究代码之前,让我们回顾一下线性图层的作用:

图 1:具有 2 个输入和 2 个输出的线性层

正如你所看到的,我们需要定义输入的大小(有多少数字将被传递到该层)和输出的大小。我们还可以区分输入相乘的参数( wjk )和添加到输出的参数( bk )。我们将前一个称为权重,后一个称为偏差。我们也来坚持一下常用的指标约定: wjk 是第 j 个输入神经元和第 k 个输出神经元之间的权重。考虑到所有这些,我们准备编码线性层。

上面的代码做了两件事:

  • 初始化权重(范围(-0.1,0.1)内的随机数)和偏差(从 0 到 1 的随机数)。
  • 通过应用线性变换计算图层的输出(见图 1)

现在我们已经可以创建一个由线性层组成的模型。然而,在我们能够训练它之前,它并不真正有用。让我们实现反向传播。

反向传播

同样,在继续实施之前,让我们考虑一下这个让训练神经网络成为可能的非凡想法— 反向传播。嗯,脱离线性代数来谈论反向传播算法可能不是 100%正确的,但出于教育目的,让我们假设我们会这样做。虽然我认为熟悉一些线性代数的基础知识通常是一个好主意,但通过一次查看一个参数来理解反向传播可能更容易。让我们开始吧。

图 2:具有 3 个参数的简单神经网络(无偏差)。

为简单起见,让我们考虑一个非常简单的神经网络,它需要一个数字(特征),执行三次线性变换(偏差=0)并输出一个数字(见图 2)。同样,我们只考虑一个训练样本(X=1,Y=12)。

我喜欢把反向传播想象成一种工具,它能让你拆开神经网络,一次看一层。算法的第一步是获取损失,并查看如果我们改变模型(O3)的输出,损失会如何变化:

图 3 从计算最后一层的误差开始反向传播

dL/dO3 告诉我们什么?它告诉我们,如果我们稍微增加 O3 ,损失会减少 12 倍以上。注意,如果 O3 高于YdL/dO3 将为正。我们需要这些信息来知道是应该增加还是减少 O3。同样,如果 O3 等于 Y 导数( dL/dO3 )为 0 —模型完美, O3 刚好。好了,我们来实现这一部分:

可以看到,丢失对象需要两种方法:

  • compute_cost 返回给定预测的损失函数值 (y_hat)
  • compute_loss_graddL/O3 相同(见图 3)

注意,我们取了第一个索引 yy_hat。这是因为在当前实施中,线性层输出列表即使只有一个数字。为了一致性,目标( y )也作为一个元素列表传递。由于我们要用 batch_size=1 进行训练,所以没问题。在正确的实现中,它们可能是数组或张量,并且结果将被平均。

让我们进入反向传播的下一步:

图 4:计算最后一层的梯度

我们已经知道为了减少损失,应该增加 O3。我们现在来考虑一下 O3 怎么改。它可以通过两种方式受到影响:通过改变输入( I3 )或通过修改权重( w3 )。让我们来看看这些变化是如何影响损失的:

图 5:损失函数相对于第三层的输入及其权重的导数。

如您所见,我们可以通过查看后续图层和输入的“误差”来确定损失如何随图层中的权重和输入而变化。这是反向传播算法的核心思想。导数 dL/dw 告诉我们应该如何更新权重。我们保留这些信息以备后用。另一个 (dL/dI) 正在以与之前相同的方式传递给前一层——我们需要它来计算下一步(前一层)中的导数。将所有这些放在一起:

图 6:反向传播中使用的导数。

右手边的导数用于更新权重。这是反向传播算法的最后一步。如你所见,导数相当大,例如 dL/dw(1) 告诉我们,如果我们将 w(1) 增加某个值,损失函数将减少该值的 72 倍。这就是为什么我们使用小的学习率来更新权重:

图 7:更新权重。损失从 36 降到 29.16。

让我们回到层对象的实现,并添加另外 3 个方法:

  • 计算输入错误 ( dL/dI )
  • compute _ gradients(dL/dw(针对权重),dL/db (针对偏差))
  • update _ params——为简单起见,假设梯度( dL/dw )和学习率(见图 7),让各层处理其参数的更新

更新的 LinearLayer:

如您所见 compute_input_errors 需要 output_errors dL/dI (见图 6)并计算其自身相对于输入的误差。唯一的区别是,一般来说,它可以有一个以上的输入神经元,因此必须针对每个输入节点单独计算误差。注意,每个输入神经元影响所有的输出节点,所以这个影响必须被加上(第 23 行)。偏置项在这里是不相关的,因为它被添加到输出神经元中,与输入无关。

让我们进一步了解一下 compute_gradients。还没有讨论的一件事是偏差项。不过,这很简单。由于偏置被简单地加到输出神经元上, dL/db 将等于该神经元的误差。

我们需要做的最后一件事是向模型对象添加一些方法:

让我们从 fit 方法开始浏览代码。为了训练一个网络,我们通常需要多次浏览数据集。我们称每次运行为一个时代。在这个实现中,我们一次只能提供一个样本。这就是我们 40 号线正在做的事情。我们从给出第一个预测的正向传递开始( y_hat )。请注意,正向传递中生成的所有输出都被存储起来供以后使用。我们在反向传播 _ 步骤中使用它们。我们从计算损耗梯度开始,进入循环,一次一层,计算梯度。正如前面讨论的那样。对于每一层,我们存储更新参数所需的梯度。在线性层的情况下(目前只有它们),它们是 dL/dwdL/db 。渐变与 update_layers 方法一起使用,该方法遍历每个层并修改其参数。最后,计算整个数据集上的损失,以查看训练是否收敛。

添加非线性

堆叠线性层没有太大意义。让我们为我们的模型配备一个简单的激活函数——ReLU:

该层将只传递大于或等于零的输入(参见 call 方法中的 if/else 语句)。这一层没有参数,所以我们只需要告诉层的输出是如何受输入影响的(见计算输入错误)。这相当简单,因为输出与输入相同,除非输入小于 0(见第 11 行)。

用 ReLU 激活函数学习非线性函数

如你所见,这个模型已经可以学习一个简单的非线性函数。

摘要

我希望现在你对神经网络的工作原理有了更好的理解。虽然代码效率很低,因为所有的计算都是在列表上进行的(一次一个元素),但我认为这样做一次是值得的。

感谢阅读!我希望这篇文章对你有用。

我撰写关于数据科学、Python 和数字信号处理(DSP)的文章。我也分享一些好玩的编码项目。如果你有兴趣,请考虑关注我的媒体

除特别注明外,所有图片均为作者所有。

通过信息的神经网络

原文:https://towardsdatascience.com/neural-network-via-information-68af7f49b978

一种更好地理解深度神经网络学习的方法

朱利亚·梅在 Unsplash 上的照片

目前,深度神经网络(DNN)学习的理论机制还不完全清楚。一个显著的贡献是 Naftali Tishby [1,2]在 2017 年提出的信息瓶颈(IB)概念,他是耶路撒冷希伯来大学的计算机科学家和神经科学家。他的理论声称是一种理解神经网络在各种应用中令人印象深刻的成功的方法。为了探索 Tishby 介绍的概念,我通过信息展示了 DNN 的总体愿景,并创建了自己的实验装置来检查 IB。

信息论如何帮助我们更好地理解 DNN?

为了说明,我就用一个猫狗图像分类的常见问题。照片可以用随机变量 X 来表示,标签可以用随机变量 Y. 来表示。如果我们想要创建一个能够正确分类每张图像的机器,我们希望它能够提取重要的特征来预测图像是狗还是猫。这些用 X 来描述 Y 的重要特征可以用 信息IXY**来衡量。这个量测量由 XY 共享的信息比特。

图片由作者提供。两个随机变量共享的信息。

然而,提取并压缩 X 的有用信息以预测 Y 的任务并不轻松。其实挺有挑战性的,导致有用信息的丢失。在这种情况下,DNN 在寻找 X 和 y 共享的信息的最佳表征方面成为一个强有力的竞争对手。主要思想是分类器要在压缩和预测成功之间进行权衡,这就是信息瓶颈(IB)。

信息瓶颈和 DNN

DNN 的力量在于通过层的内部表现。神经网络表示输入层XT _1→T _2→…→T _ nY[3]的连续表示的马尔可夫链。如前所述,目标是找到最佳表示法 T ( X )来提取 X 的重要特征,以预测Y。因此,互信息以比特为单位建立 X 的信息,需要通过表示法 T(X)来预测 Y。因此,这个是一个重要的工具,它使我们能够测量具有联合分布 p 的两个随机变量 X 和 Y 所共享的信息

图来自 Tishby 的文章[2]。神经网络中的转换图。

1.交互信息

衡量两个随机变量 XY 共享的信息量(单位通常是比特)。

它被定义为

在哪里

H[X]是我们对 x 的无知,称为熵,h(xy)是给定 y,我们对 x 的无知,这是条件熵。一旦我们理解了互信息的概念,我们就为数据处理定理做好了准备。

2。D数据处理定理

该定理指出,每一个不可逆变换都会耗散信息。

XT _1→YT _2→Z然后, I [ XY]≥I[XZ

不等式 I [ XY]≥I[XZ ]是指 XY 共享的信息大于或等于 X共享的信息

对于神经网络来说

在这种情况下,我们通过连续层的变换丢失信息。但是我们还是没有回答如何找到最优表示 T ( X )。最少的足够的统计数据可能会让我们走上正确的道路。

3.最小充分统计量

最小充分统计量是 XS ( X )的函数,它保留了关于分布 p ( Xθ )参数的所有信息(例如正态分布的均值( μ) 和标准差( σ )。然而,在我们的上下文中,我们可以将其理解为压缩和转换数据而不丢失任何关于 Y 的信息的函数。

问题是,最小充分统计量只存在于分布的子集(即指数族),所以这是一个用于机器学习的非常强的假设。因此,我们需要找到一种近似最小充分统计量的方法,以尽可能多地捕捉到 I [ XY 。在这种情况下,我们最终会遇到信息瓶颈。

4。信息瓶颈

设 t 是一个参数化的变换t=ϕ(xθ ),它可以是一系列的变换 T_1,t _ 2……我们可以这样选择 t,即保留 y 的更多信息,只提取 x 的相关特征,在预测和压缩之间进行权衡。

因此,信息瓶颈是一个框架,用于找到最小充分统计的近似值,从而在 X 的压缩和 y 的预测之间进行最佳权衡。这相当于找到 X (数据)的最佳表示,该表示仅选择有用的信息(压缩)来找到正确的标签(预测)。

一个重要的注释是随机梯度下降(SGD)的相关性,在 DNN 通常用于估计给定损失函数的参数。Tishby 将 SGD 动力学探索为实现 IB 界限的主要方法,这意味着服从折衷的变换 T

既然我们更好地理解了 DNN 和信息瓶颈之间的关系,我们必须用一个简单的神经网络和模拟数据来检验它。

图来自 Tishby 文章[2]。信息平面中的神经网络训练。这是信息瓶颈权衡的可视化,其中每个点意味着不同的神经网络层。

测试 IB & DNN

对于数值模拟,我选择创建一个二元问题,因为估计连续变量的互信息可能很困难。做出更稳健的估计会让我们得到更可靠的结果。

因此,我模拟的数据遵循y=h(g(x)+ϵ,其中x=(x *1, *x* 2,…, x_ 10, x_i*

在哪里

产生一个平衡的数据集

图片由作者提供。平衡数据集。

为了估计互信息,我使用频率作为概率的近似值,并定义了一些熵属性。

因此,利用上面的函数我们估算出 XY 信息IXY= 0.82。**这是一个重要的结果,因为它是信息瓶颈(IB)的一部分。现在,我们创建一个非常简单的神经网络,分别有 3 层和 3,2,1 个神经元。

一旦我们创建了神经网络架构,我们就需要所有时期中每一层的输出。这样,通过历元的每个表示 T_1、T_2 和 T_3 的数据被存储在 activation_list 中。

由于各层的输出是连续的,我们需要在箱中将其离散化。

现在我们计算信息平面( I [ XYI【T,Y】)。

并绘制出结果。

图片由作者提供。学习过程和信息瓶颈界限(压缩和预测的极限)。

该图像显示了学习过程,其中每条单独的线是不同的层。随着时代的推移,每一层都向左移动,显示了信息瓶颈的压缩和预测之间的权衡。

结论

因此,很明显,Tishby 提出的信息瓶颈是一个简明的框架,可以更好地理解深度神经网络学习的理论机制。总之,我们观察到,所有层都在学习数据的最佳表示,这取决于压缩和预测之间的权衡。这意味着我们越来越接近信息瓶颈的界限(压缩和预测的极限)。

延长

信息论是理解机器学习的强大数学结构,在这种背景下,许多不同的方法被创建来增加我们对学习的理解。一个臭名昭著的例子是最小可实现充分统计量(MASS)学习,这是一种寻找最小充分统计量或最佳表示的方法,遵循类似的信息瓶颈框架,但非常适合连续变量[4]。该结果在监督学习和不确定性量化基准上具有竞争性性能。

感谢

这个项目是为教授 Renato Vicente 构思和教授的信息系统理论、贝叶斯推理和机器学习课程开发的。

这篇文章的笔记本在这里https://github.com/Rodrigo-Motta/NN_IB***。*****

参考

[1] N. Tishby、Fernando C. Pereira 和 W. Bialek, 《信息瓶颈法》(1999),1999 年第 37 届阿勒顿通信、控制和计算年会论文集。**

[2]什瓦兹-齐夫,拉维德,n .蒂什比, 通过信息打开深度神经网络的黑盒 (2017),doi:10.48550/arxiv . 1703 . 00810**

[3] N. Tishby 和 N. Zaslavsky, 深度学习和信息瓶颈原理 (2015),载于信息理论研讨会(ITW),2015 IEEE,第 1–5 页。IEEE,2015。

[4] M. Cvitkovic 和 G. Koliander,最小可达充分统计学习 (2019),第 36 届机器学习国际会议论文集,PMLR 97:1465–1474,2019。

神经网络:正向传递和反向传播

原文:https://towardsdatascience.com/neural-networks-forward-pass-and-backpropagation-be3b75a1cfcc

使用 PyTorch 的逐步解释和示例

作者图片

内容:

  1. 介绍
  2. 功能组合
  3. 一个简单的神经网络
  4. 前进传球
  5. 在 PyTorch 中建立简单的神经网络
  6. 反向传播
  7. 与 PyTorch 结果的比较
  8. 结论
  9. 参考

简介:

神经网络是最广泛使用的机器学习算法之一。神经网络在图像分类、时间序列预测等领域的成功应用为其在商业和研究中的应用铺平了道路。可以说,神经网络是最重要的机器学习算法之一。对算法的清晰理解将有助于诊断问题,也有助于理解其他高级深度学习算法。本文的目标是解释神经网络的工作原理。我们将一步一步地检查算法,并解释如何在 PyTorch 中建立一个简单的神经网络。我们还将比较我们的计算结果和 PyTorch 的输出。

1.0 功能组合:

让我们从考虑以下两个任意线性函数开始:

系数-1.75、-0.1、0.172 和 0.15 是为了说明的目的而任意选择的。接下来,我们定义两个新函数 a₁和 a₂,它们分别是 z₁和 z₂的函数:

该功能

上面使用的称为 sigmoid 函数。这是一条 S 形曲线。函数 f(x)在神经网络中有特殊的作用。我们将在随后的章节中更详细地讨论它。现在,我们简单地用它来构造函数 a₁和 a₂.

最后,我们定义另一个函数,它是 a₁和 a₂:函数的线性组合

同样,系数 0.25、0.5 和 0.2 是任意选择的。图 1 显示了 a₁、a₂和 z₃.三个函数的曲线图

图一。组合功能(图片由作者提供)

从图 1 中我们可以看到,a₁和 a₂函数的线性组合是一条看起来更复杂的曲线。换句话说,通过线性组合曲线,我们可以创建能够捕捉更复杂变化的函数。我们可以将 sigmoid 函数应用于 z₃,并将其与另一个类似的函数线性组合,以表示一个更复杂的函数。从理论上讲,通过组合足够多的这样的函数,我们可以表现出极其复杂的数值变化。上述等式中的系数是任意选择的。如果我们可以通过调整系数来改变最终函数的形状,会怎么样呢?这将允许我们将最终函数拟合到一个非常复杂的数据集。这是神经网络背后的基本思想。神经网络为我们提供了一个框架,将简单的函数组合起来,构建一个能够表示数据中复杂变化的复杂函数。现在让我们检查神经网络的框架。

2.0 一个简单的神经网络:

图 2 是简单神经网络的示意图。在本文的后续讨论中,我们将使用这个简单的网络。网络将单个值(x)作为输入,并产生单个值 y 作为输出。网络中还有四个附加节点,标记为 1 到 4。

图 2:简单的神经网络(图片由作者提供)

输入节点为节点 1 和节点 2 供电。节点 1 和节点 2 分别为节点 3 和节点 4 供电。最后,节点 3 和节点 4 向输出节点供电。w₁到 w₈是网络的权重,b₁到 b₈是偏差。权重和偏差用于在节点处创建值的线性组合,然后将这些值提供给下一层中的节点。例如,结合了权重 w₁和偏差 b₁的输入 x 是节点 1 的输入。类似地,结合了权重 w₂和偏差 b₂的输入 x 是节点 2 的输入。节点处的 AF 代表激活功能。上一节介绍的 sigmoid 函数就是这样一种激活函数。我们将很快讨论更多的激活函数。现在,让我们跟随信息在网络中的流动。由节点 1 和节点 2 处的激活函数产生的输出然后分别与权重 w₃和 w₅以及偏置 b₃.线性组合线性组合是节点 3 的输入。类似地,节点 1 和节点 2 的输出分别与权重 w₆和 w₄组合,并且偏置 b₄以馈送到节点 4。最后,来自节点 3 和节点 4 的激活函数的输出分别与权重 w₇和 w₈线性组合,并偏置 b₅以产生网络输出 yhat。

这种从输入到输出的信息流也被称为前向传递。在我们为我们的简单网络制定出向前传递的细节之前,让我们来看看激活函数的一些选择。

表 1:激活功能(图片由作者提供)

表 1 显示了三种常见的激活功能。还显示了每个激活函数及其导数的曲线图。虽然 sigmoid 和 tanh 是平滑函数,但 RelU 在 x=0 处有一个拐点。激活函数的选择取决于我们试图解决的问题。存在神经网络的应用,其中期望具有激活函数的连续导数。对于这样的应用,导数连续的函数是一个很好的选择。tanh 和 sigmoid 激活函数在原点附近具有较大的导数。因此,如果我们在这个区域中操作,这些函数将产生更大的梯度,导致更快的收敛。相反,远离原点,双曲正切函数和 sigmoid 函数具有非常小的导数值,这将导致解中非常小的变化。我们将在下一节讨论梯度的计算。还有许多其他的激活功能,我们不会在本文中讨论。由于 RelU 函数是一个简单的函数,我们将使用它作为简单神经网络的激活函数。我们现在准备执行向前传球。

3.0 向前传球:

图 3 显示了我们的简单神经网络向前传递的计算。

图 3:向前传球(图片由作者提供)

通过将输入 x 分别与 w₁和 b₁以及 w₂和 b₂进行线性组合来获得 z₁和 z₂。a₁和 a₂是分别对 z₁和 z₂应用 RelU 激活函数的输出。z₃和 z₄是通过将前一层中的 a₁和 a₂分别与 w₃、w₅、b₃和 w₄进行线性组合而得到的。最后,通过将前一层中的 a₃和 a₄与 w₇、w₈和 b₅.组合,获得输出 yhat 实际上,函数 z₁、z₂、z₃和 z₄是通过矩阵向量乘法获得的,如图 4 所示。

图 4:矩阵向量乘积(图片由作者提供)

这里我们将矩阵中的偏置项结合起来。一般来说,如图 5 所示,对于一个 r 节点层馈送一个 s 节点层,矩阵矢量积将是(s×r+1)*(r+1×1)。

图 5:一般配置的矩阵矢量积(图片由作者提供)

正向传递的最后一步是计算损耗。由于我们的示例中只有一个数据点,因此损失 L 是输出值 yhat 和已知值 y 之间的差值的平方。通常,对于回归问题,损失是每个数据点的网络输出值和已知值之间的差值的平方的平均和。它被称为均方误差。这就完成了神经网络的两个重要步骤中的第一步。在讨论下一步之前,我们先描述如何在 PyTorch 中建立简单的网络。

4.0 在 PyTorch 中设置简单的神经网络:

我们的目标是使用我们的简单网络示例来展示在 PyTorch 中建立神经网络的基础。这里假设用户已经在他们的机器上安装了 PyTorch。我们将使用 torch.nn 模块来设置我们的网络。我们从导入 nn 模块开始,如下所示:

为了建立我们的简单网络,我们将在 nn 模块中使用顺序容器。我们的网络中的三层是按照上面图 3 所示的顺序指定的。以下是我们简单网络的完整规格:

nn。线性分类用于应用权重和偏差的线性组合。线性类有两个参数。第一个参数指定了该层的节点数。层中的节点数被指定为第二个参数。例如,输入层中的(1,2)规范意味着它由单个输入节点提供,并且该层有两个节点。隐藏层由输入层的两个节点馈电,有两个节点。值得注意的是,前一层的输出节点数必须与当前层的输入节点数相匹配。输出层的(2,1)规范告诉 PyTorch 我们只有一个输出节点。激活功能在层之间指定。如前所述,我们使用 RelU 函数。使用这个简单的方法,我们可以构建一个深度和广度都适合手头任务的网络。通过提供如下输入值获得网络输出:

在我们的例子中,t_u1 是单一的 x 值。为了计算损失,我们首先定义损失函数。损失函数的输入是神经网络的输出和已知值。

在我们的例子中,t_c1 是 y 值。这就完成了 PyTorch 中正向传递的设置。接下来,我们讨论神经网络的第二个重要步骤,反向传播。

5.0 反向传播:

神经网络的权重和偏差是我们模型中的未知数。我们希望确定最适合我们数据集的权重和偏差的值。当损失(即误差)最小时,达到最佳拟合。请注意,损耗 L(见图 3)是未知权重和偏差的函数。想象一个多维空间,其中轴是权重和偏差。损失函数是这个空间中的一个曲面。在最小化过程的开始,用随机权重和偏差来播种神经网络,即,我们从损失表面上的随机点开始。为了到达地面的最低点,我们开始沿着最陡的下坡方向前进。这就是梯度下降算法在每个训练时期或迭代期间所实现的。在任何第 n 次迭代中,权重和偏差更新如下:

m 是网络中权重和偏差的总数。请注意,这里我们使用 wᵢ来表示权重和偏差。学习率η决定了每一步的大小。在反向传播步骤中计算损失相对于每个权重/偏差的偏导数。

该过程从输出节点开始,系统地向后通过各层,一直到输入层,因此称为反向传播。每一步都要用到计算导数的链式法则。我们现在计算简单神经网络的偏导数。

图 wrt w7、w8 和 b5 的偏导数(图片由作者提供)

我们首先从损耗 L wrt 对输出 yhat 的偏导数开始(参见图 6)。

我们用它来计算 w₇.损耗的偏导数

这里,我们使用了图 6 中的 yhat 方程来计算 yhat wrt 对 w₇.的偏导数类似地计算 w₈和 b₅的偏导数。

图 7:w3、w5 和 b3 的偏导数(图片来自作者)

现在我们回到上一层。链式法则再次被用来计算导数。参考图 7,了解 w₃、w₅和 b₃:的偏导数

图 wrt w6、w4 和 b4 的偏导数(图片由作者提供)

参考图 8,了解 w₄、w₆和 b₄:的偏导数

图 wrt w1 和 b1 的偏导数(图片来自作者)

关于 w₁和 b₁的下一组偏导数,参见图 9。我们首先将输出重写为:

图 wrt w2 和 b2 的偏导数(图片由作者提供)

同样,w₂和 b₂:的偏导数见图 10

PyTorch 通过计算图执行所有这些计算。在 PyTorch 中,损耗 wrt 权重和偏差的梯度计算如下:

首先,我们广播所有梯度项的零。optL 是优化器。的。向后触发 PyTorch 中的渐变计算。

既然我们已经为简单的神经网络导出了正向传递和反向传播的公式,那么让我们将我们的计算输出与 PyTorch 的输出进行比较。

6.0 与 PyTorch 结果的比较:

一个完整的历元由正向传递、反向传播和权重/偏差更新组成。我们将使用导出的公式,使用 Excel 执行一个完整时期的计算。我们将首先比较正向传递的结果,然后比较反向传播的结果。最后,我们将使用来自反向传播的梯度来更新权重和偏差,并将其与 Pytorch 输出进行比较。在实践中,我们很少在训练中关注重量或梯度。这里,我们在 PyTorch 中执行两次迭代,并输出这些信息进行比较。但是首先,我们需要从 PyTorch 中提取初始随机权重和偏差。我们需要这些权重和偏差来执行我们的计算。这是按如下方式逐层完成的:

注意,我们提取偶数层的权重和偏差,因为我们的神经网络中的奇数层是激活函数。提取的初始权重和偏差被传输到 Excel 中适当标记的单元格。

图 11 显示了我们的正向传递计算与 PyTorch 时段输出的比较。PyTorch 的输出显示在图的右上角,而 Excel 中的计算显示在图的左下角。输出值和损耗值分别用适当的颜色圈出。

图 11:与 PyTorch 的向前传球结果比较(图片由作者提供)

接下来,我们计算梯度项。就像权重一样,任何训练时期的梯度也可以在 PyTorch 中逐层提取,如下所示:

图 12 显示了我们在 Excel 中的反向传播计算与 PyTorch 输出的比较。损失权重和偏差的梯度的不同项被适当地标注。请注意,我们在 Excel 计算中使用了表 1 中 RelU 的导数(当 x < 0 时,RelU 的导数为零,否则为 1)。PyTorch 的输出显示在图的右上角,而 Excel 中的计算显示在图的左下角。除了三个梯度项外,其余都为零。w₈、b₄和 b₅的损耗梯度是三个非零分量。这三个非零梯度项用适当的颜色圈出。

图 12:与 PyTorch 的反向传播比较(图片由作者提供)

我们现在准备在第一个训练周期结束时更新权重。在 PyTorch 中,这是通过调用 optL.step()来完成的。对于我们的计算,我们将使用第 5 节开头提到的权重更新公式。

图 13 示出了在时段 1 开始时更新的权重的比较。PyTorch 的输出显示在图的右上角,而 Excel 中的计算显示在图的左下角。请注意,只有一个权重 w₈和两个偏差 b₄和 b₅值发生变化,因为只有这三个梯度项是非零的。我们的例子中使用的学习率是 0.01。

图 13:用 PyTorch 比较更新的权重和偏差(图片由作者提供)

感兴趣的读者可以在下面找到 PyTorch 笔记本和电子表格(Google Sheets)。

链接到 google 工作表:

https://docs . Google . com/spreadsheets/d/1 njvmzzppjwgygw 54 ofp x7e u 740 fcnyjqqdgujqtzapm/edit # GID = 1501293754

7.0 结论:

在本文中,我们研究了如何建立神经网络,以及如何执行正向传递和反向传播计算。我们使用一个简单的神经网络来导出正向传递过程中每个节点的值。使用链式法则,我们导出了损失函数 wrt 对权重和偏差的梯度项。我们使用 Excel 执行正向传递、反向传播和权重更新计算,并将 Excel 的结果与 PyTorch 输出进行比较。虽然我们在本文中使用的神经网络非常小,但是其基本概念可以扩展到任何一般的神经网络。

参考文献:

1.0 PyTorch 文档:https://pytorch.org/docs/stable/index.html

2.0 深度学习,PyTorch,Eli Stevens,Luca Antiga 和 Thomas Viehmann,2020 年 7 月,曼宁出版,ISBN 9781617295263。

用于图形深度学习的神经层扩散

原文:https://towardsdatascience.com/neural-sheaf-diffusion-for-deep-learning-on-graphs-bfa200e6afa6

从代数拓扑的角度看 GNNs

图形神经网络(GNNs)连接到扩散方程,在图形的节点之间交换信息。作为纯粹的拓扑对象,图被隐含地假定为具有平凡的几何。使用一种更一般的结构称为细胞绞线,我们可以赋予图形一个可学的几何结构。我们证明了细胞束上的扩散过程可以解决任何节点分类任务,并且可以提供关于过度平滑、处理嗜异性数据的问题以及 GNNs 的表达能力的新见解。

基于 Shutterstock 的图像。

本文由克里斯蒂安·博德纳尔和弗朗切斯科·迪·乔瓦尼合著,基于 c·博德纳尔、f·迪·乔瓦尼等人的论文【神经束扩散:对 GNNs 中异嗜性和过度光滑的拓扑透视】(2022) arXiv:2202.04579。它是通过微分几何和代数拓扑 的透镜的 图神经网络系列的一部分。另请参见之前讨论 神经扩散偏微分方程 图重布线与瑞奇流 、*拓扑消息传递 的系列帖子。看一段 Aleksa gordi**详细讲解论文的 视频和 Cristian 在 AIMS 2020 几何深度学习课程中的 嘉宾谈 *

图形神经网络(GNNs)已经成为机器学习领域的“一等公民”[1],并在从粒子物理到纯数学定理证明等问题中获得了成功应用[2]。大多数 gnn 基于消息传递[3],通过它,相邻节点之间的信息在图上传播。

两种现象通常与某些 gnn 有关:在嗜异性设置中表现不佳【4】和过度平滑【5–6】。前者源于 gnn 通常建立在同向性(即相邻节点是相似的)的假设上,这通常看起来是不现实的【7】。后一种现象指的是应用多个消息传递层来产生接近常数的特性的趋势,导致性能随着深度而下降,以及深度 GNNs 的不可行性[8]。

在最近的一篇论文[9]中,我们认为这两个问题在底层图的“几何”中有一个共同的根源。像流形一样,图也是拓扑对象[10]:它们有邻域的概念(在图上,这是由一对节点之间的边捕获的),但没有距离或方向。我们可以通过用向量空间结构装饰每个节点和边(这种向量空间被称为)以及每个关联节点和边的茎之间的一组线性变换(限制映射)来定义图上的一些几何概念。

通过将向量空间与图中的每个节点和边(分别为“梗”ℱ(u)、ℱ(v 和ℱ(e=(u,v)相关联,并在这些空间之间为每个关联节点-边对(u⊴e 和 v⊴e).)建立“限制图”,在图上构建细胞层

这个图,连同其上构建的茎和限制性图谱,被称为细胞束是代数拓扑中的主要研究对象,可以粗略地认为是描述定义在拓扑空间上的数据的一种非常通用的方式【11】。虽然它们在连续情况下的定义有些技术性和复杂性,但细胞束是离散拓扑空间(细胞复形,其中图是一个特例)上的一个类似的和更容易掌握的结构。1977-1978 年,罗伯特·麦克弗森在布朗大学的一次研讨会上首次发现了细胞束。麦克弗森的学生艾伦·谢泼德在 1985 年完成的博士论文[12]中对该理论进行了第一次全面的发展。这项工作,然而,几乎没有人注意到;最近,Robert Ghrist 和他以前的博士生 Justin Curry 和 Jakob Hansen 已经复活并将其引入应用领域。

在我们讨论的上下文中,细胞层的概念与微分几何中的连接的概念密切相关,顾名思义,微分几何“连接”光滑流形上两个相邻点的切空间【16】。这种机制规定了切向量如何局部移动,允许执行并行传输【17】——从而赋予流形(拓扑对象)一种几何结构。连接的各种选择导致流形的不同性质和其上发生的物理过程。

在一个管汇上平行运输。在向量空间ℱ(w 和ℱ(u 之间移动向量的结果通常是路径相关的(在这个例子中,从 w 到 u 沿着 e₃或者沿着 e₁和 e₂到 v 的路径产生不同的结果)。

层的选择决定了表现力

本着类似的精神,我们证明了通过适当构造限制图来选择图上的层结构,会导致图上扩散过程的不同行为。由于 GNNs 可以解释为离散化的扩散方程[18],这种形式允许我们研究不同 GNN 模型在不同环境下的表达能力和局限性。

一层上的扩散受微分方程支配

(t)=δx(t)

从一些初始条件开始 x (0)= x 。这看起来像一个标准的图形扩散方程,有一个重要的区别:我们现在有了维度为 d矢量数据(排列成大小为×1】的块矢量 x ,编码层结构【19】的层拉普拉斯δ ×**

与图的节点相关联的空间一起形成 0-共链 C⁰的空间(“节点信号” x )和图的边上的空间 1-共链 c(“边信号” y )。共同边界图δ:C⁰→C 是测量节点空间之间“不一致”的梯度算子的一般化;类似地,地图δᵀ:C →C⁰相当于散度算子。谢夫拉普拉斯被定义为δ=δᵀδ,是微分几何中使用的霍奇拉普拉斯的离散版本。

在极限 t →∞中,层扩散方程的解属于层拉普拉斯算子的调和空间:该空间包含符合层沿所有边的限制图【21】的信号,使得δx= 0【22】。这是对谐波信号的经典定义的概括,谐波信号在每个节点上等于其邻居的平均值。****

任何节点分类任务都可以被描述为寻找正确的层,在该层上,层扩散的极限能够线性地分离节点特征。这种观点可以作为 Weisfeiler-Lehman 图同构测试层次结构[23,48]的替代方法,用于表征从扩散过程中获得的 gnn 的表达能力。扩散的分离能力通常取决于层的构造以及关于基础图的结构和节点特征或标签的假设(嗜同性对嗜异性)。

具有对称和非零限制图的 d =1 维(标量)茎的选择对应于具有严格正边权重的标准加权图拉普拉斯算子。从这种扩散过程中产生的 GNN 架构是 GCN 类型的图形卷积模型[24–25]。这种模型可以在某些同质假设下分离两类节点[26];然而,这类滑轮在嗜异性环境中不够强大[27]。我们表明,为了解决嗜异性的情况,必须放弃对称假设,允许非对称限制图[28],这在标量情况下相当于允许负边权重。这为为什么最近的论文[29–31]显示负权重对处理异性很有用提供了理论依据。

具有两个分别包含正负标量特征的不同节点(红色和绿色)的嗜异性设置示例。不对称限制图(在这种情况下,+1 或-1 边权重)是调和它们所必需的。

考虑到两个以上的节点类,我们表明更高维的主干(提供一定的“表示宽度”)是分离这种情况所必需的[32]。这种额外的“宽度”概念可以对特征通道的数量起到补充作用,以增加 GNN 模型的表达能力。我们表明使用 d × d 对角可逆限制图允许分离 d 类【33】。使用正交限制图(相当于节点向量的旋转和反射)会产生更好的表达能力:对于 d =2 或 4,相关的扩散过程可以分离至少 2 个 d 类。正交设置特别有趣,因为它与同步问题有关[21],最近以正交 GNNs [34]和 Taco Cohen 在其博士论文[35]中开发的规范等变卷积模型的形式进行了研究。

在具有四个节点类(颜色编码)的合成嗜异性数据集上通过层扩散进行节点分类的示例。节点特征是二维的(由平面中的节点坐标表示),并且限制图是正交的(旋转+反射矩阵)。与我们的理论一致,层扩散能够线性地分离极限中的节点。

层卷积网络和能量最小化

为了将层理论与图形上的深度学习联系起来,我们考虑以下 GNN 层,该层最初由 Hansen 和 Gebhart 提出[36]:

y=σ((Iδ)(Iw₁)xw₂).****

这里 X 是具有 f 个通道的输入特征的 nd × f 矩阵, W ₁和 W ₂是 d × df × f 可学习的权重矩阵(前者作用于x 的每个块 和 GCN [25]中一样,δ是大小为 nd × nd,t44】I是适当维数的单位矩阵,⊗表示克罗内克乘积【37】,σ是 ReLU 或 Leaky ReLU 等非线性。 通过类比 GCN [38],我们将这一层称为层卷积网络 (SCN)。SCN 模型可以被视为层扩散方程的离散的、参数化的和非线性的版本。**

一个关键问题是 SCNs 是否以及何时在一定程度上趋向于像层扩散一样。为了分析这一点,我们检查 SCN 层如何影响层狄利克雷能量ℰ(x)= trace(xδx)。这是一种类似于经典狄利克雷能量的二次形式,测量 X 与层拉普拉斯的调和空间的距离(即信号与层结构的不一致程度)。扩散方程是狄利克雷能量的梯度流,使其随时间最小化。**

在 GCNs(SCN[38]的标量版本)的特殊情况下,蔡和王[6]表明,一个 gcn 层的应用必然降低狄利克雷能量,即 YX 。因此,随着深度的增加,特征往往会变得更平滑,这种现象在这种类型的图形神经网络中很常见。**

在我们更一般的设置中,这种行为可能会根据层和相关层拉普拉斯δ的选择而变化。特别地,该不等式在沿所有边都相等的 d 维正交限制图的情况下成立【39】。此外,沿所有边的关系的对称性是一个必要条件:只要我们有一个边,其中限制图是不同的,我们就可以找到一个任意小的线性变换,增加狄利克雷能量[40]。

在 ReLU 型σ假设下的 d =1 维(标量)情况下,具有正号边是狄利克雷能量降低的必要条件。这是 Oono 和[5]以及蔡和王[6]关于广义神经网络过光滑性的结果的推广。有趣的是,如果σ被假设为线性的,那么即使是带负号的边,狄利克雷能量也会最小化。

从数据中学习束允许特征与图形几何一起进化

最后,我们提出了一个基于层扩散(SCN 型层)的 GNN 架构,其中层可以从数据中学习。具体地,该层由形式为𝚽( x ᵤ、 x ᵥ、 d × d 矩阵值函数的参数限制图定义,该矩阵值函数可以进一步被约束为正交或对角的。这允许 GNN 在图上“发现”正确的层结构,以最适合下游任务的方式修改扩散的属性。

此外,GNN 的每一层中的层都可能不同(通过其对当前图层的要素的依赖性)这一事实是域和数据的一种共同进化形式。在 GNN 的文献中,类似的想法是将计算图与输入图分离,这通常以图重新布线(改变图作为预处理步骤【41】)或潜在图学习(在学习期间改变图【42】)的形式实现。****

我们最近在非欧几里德神经扩散[43]的工作中研究了图形“几何”演变的另一种方法,其中图形被嵌入到联合位置特征空间中。这个空间上的一个可学习的扩散过程,称为 graph Beltrami flow ,导致同时的特征扩散和图形重布线。学习一层的优势在于它是“内在地”完成的,并且不需要在环境空间中嵌入任何类型的图。

结束语

神经层扩散遵循了图 ML 架构设计的一个新趋势:首先将输入图“提升”到一个结构更丰富的对象中,然后在这个对象上进行操作。这样,GNNs 可以避免使用输入图作为“计算结构”,这仍然是当前结合消息传递范例使用的主要方法。

我们遵循这一理念的工作包括最近关于拓扑消息传递的论文[44–45](其中图被制成单纯或细胞复合体,在其上适当定义的消息传递被证明比经典的 Weisfeiler-Lehman 更具表达性),图 Ricci 流(在图上构建 Ricci 曲率的离散类比,以量化和消除造成过度挤压的瓶颈[46]并将其用于节点嵌入[47]),以及前面提到的图 Beltrami 流[43]。

第二,有限层扩散的节点分离能力为研究 GNNs 的表达能力提供了一个新的框架,该框架脱离了传统的 Weisfeiler-Lehman 层级[48]。层形式主义允许做出明智的架构选择——例如,证明在某些嗜异性设置或正交消息传递函数中使用负权重的合理性。

最后,gnn 是几何深度学习蓝图的一个特例,将 Felix Klein 的几何群论处理(“埃尔兰根程序”)引入机器学习【49】。正如埃尔兰根计划的精神蔓延到其他学科,并在微分几何,代数拓扑和物理学中产生了新的理论[50],我们相信几何深度学习的下一步发展应该从这些类比中获得灵感。通过求助于上述领域的强大工具,可以得出新的理论见解和计算模型[51],到目前为止,这些工具在最大似然法中探索不足,在应用领域中被认为是“奇异的”。

[1]引用 Petar Velič ković在我的 2021 年预测帖子中的话。

[2] A. Davies 等.用人工智能指导人类直觉推进数学(2021),自然 600:70–74。

[3] J. Gilmer 等人,量子化学的神经信息传递 (2017) ICML。

[4]朱等,超越图神经网络中的同伦现象:当前的局限与有效设计 (2020) NeurIPS。

[5] K. Oono 和 t .铃木,图神经网络对节点分类的表达能力呈指数下降 (2020) ICLR。

[6]蔡志勇,王等,关于图神经网络过光滑问题的一个注记 (2020) arXiv:2006.13318 .

[7]早期的 graph ML 基准测试,如 Cora、Citeseer 和 Pubmed 是高度同质化的,这给 GNNs 的性能带来了一些错误的印象。最近专门针对这个问题开发的数据集指出,一些流行的 GNN 模型在嗜异性环境中的表现令人失望。

[8]参见我那篇有争议的博文我们需要深度图神经网络吗?在走向数据科学。

[9] C .博德纳尔,f .迪·乔瓦尼等人,N eural 层扩散:GNNs(2022)arXiv:2202.04579 中关于异质和过度光滑的拓扑透视。

[10]拓扑学和几何学区别的一个典型例子是关于一个拓扑学家的早餐有一个油炸圈饼和一杯咖啡的笑话,它们在拓扑学上是等价的(两者都同胚于一个环面)。这个例子借用了拓扑学中“橡胶片几何”的比喻:一个可拉伸的圆环面可以变形为一个咖啡杯,这个过程将影响点之间的局部距离和角度(几何),但不会影响点之间的连接方式(拓扑学)。在这篇博文的中可以看到一个流行的拓扑介绍。

[11]层理论的发展归功于法国数学学派,它始于代数拓扑学家让·勒雷在战争时期的工作,随后在 20 世纪 50 年代由昂利·嘉当、让·皮埃尔·塞尔、奥斯卡·扎里斯基、亚历山大·格罗滕迪克和其他人发展。H. Miller, Leray 在 of lag XVIIA:The origins of sheaf theory,sheaf 上同调,and spectral sequences ,1999 中把 sheaf theory 的诞生归功于让·勒雷在二战期间被囚禁的一个戏剧性插曲。战争爆发时他是一名军官,1940 年法国军队被击败后,他被囚禁在德国 T2 的 T3 战俘营里。勒雷和其他战友被允许举办一个数学研讨会,他是主席。由于担心他在力学和流体动力学方面的专业知识会让德国人强迫他为纳粹战争效力,Leray 选择代数拓扑作为最抽象和“无害”的教学科目。他的工作后来分两部分出版,分别是《J. Leray,Sur la forme des espaces topologiques et Sur les points fixes des representations 》( 1945 年)和《J. Math》。pures et appl . 24:95–167 和 169–199,副标题为“un cours de topologie algébrique professoréen captivité”(囚禁期间教授的代数拓扑课程)。**

[12] A. Shepard,分层空间导出范畴的细胞描述,博士论文,布朗大学,1985 年。

[13] J. M. Curry,滑轮、共沉和应用 (2014)。宾夕法尼亚大学博士论文。参见贾斯汀关于蜂窝绳轮历史的推特帖子

[14] J. Hansen 和 R. Ghrist,迈向细胞束的光谱理论 (2019)。应用与计算拓扑学杂志,3(4):315–358。

[15] J. Hansen 和 R. Ghrist,关于话语束的意见动态 (2021)。暹罗应用数学杂志,81(5):2033–2060。

[16]严格地说,限制图从节点的向量空间到边的向量空间。更正确的连接类比是运输图 —对于一条边 e =( uv ),从 ue 的限制图与从 ev 的转置图的组合。注意,虽然连接必须是可逆映射,但限制映射不是必须的。**

[17]光滑流形上的一个连接规定了一种对切向量场进行微分的方式(因此又有一个名字, 协变导数 )。有多种方法可以做到这一点。如果流形额外配备了黎曼度量,则存在一个唯一的“规范”连接,称为 李维-奇维塔连接

[18] B. Chamberlain,J. Rowbottom 等人,GRAND:Graph Neural Diffusion(2021)ICML。另见附带的博文

[19]我们使用归一化的层拉普拉斯算子;参见我们论文中的定义 4。

[20]注意柄的尺寸 d 与特征通道的数量不同。一个类似的例子是微分几何中的切向量场;在这种情况下, d 对应于流形的维度(及其切空间)。

[21]因此,层扩散可以被看作是一个全球性的“同步”过程。A. Singer 和 H.-T. Wu 在《纯数学和应用数学通讯》65(8)(2012)中的文章《向量扩散图和连接拉普拉斯算子》以及我的博士生 A. Kovnatsky 等人的文章 MADMM:流形上非光滑优化的一般算法 (2016)研究了图拉普拉斯算子与正交限制映射(称为连接拉普拉斯算子)的同步问题**

[22]或者,x∈ker(δ),即调和空间是拉普拉斯算子的核。

[23] B. Weisfeiler 和 A. Lehman,将图简化为标准形式以及其中出现的代数(1968)。nauchno-Technicheskaya informatisia 2(9):12–16 介绍了图同构测试,这种测试最近在 GNN 文献中很流行,因为它等价于某些形式的消息传递。参见我的帖子和 C. Morris 等人, Weisfeiler 和 Leman 围棋机器学习:迄今为止的故事 (2021) arXiv:2112.09992 的历史笔记和深入综述。

[24] M. Defferrard 等人图的卷积神经网络与快速局部谱滤波 (2016) NIPS。

[25] T. Kipf 和 M. Welling,使用图卷积网络的半监督分类 (2017) ICLR。描述了 GCN 的建筑。

[26]我们论文中的命题 13。

[27]我们论文中的命题 14。

[28]见我们论文中的命题 16。此外,在命题 18 中,我们陈述了在具有正边权重的图上的扩散不能分离仅具有来自通过边连接的不同类的两个节点的平凡的异嗜情况。不对称边权重有时在图 ML 文献中被称为“各向异性”,类似于连续扩散方程(其中该术语表示依赖于方向的扩散率)。

[29] Y. Yan 等,同一枚硬币的两面:图卷积神经网络中的异嗜性和过度光滑 (2021) arXiv:2102.06462。

[30] E. Chien 等,自适应通用广义 PageRank 图神经网络 (2021) ICLR。

[31] D. Bo 等,超越图卷积网络中的低频信息 (2021).

[32]我们论文中的命题 19。

[33]我们论文中的命题 21。

[34]郭,正交图神经网络(2021) arXiv:2109.11338 .

[35]t . Cohen 研究了连续 O( d )向量束,等变卷积网络(2021),阿姆斯特丹大学博士论文。2021 年 4 月,我是他的博士委员会的成员,一生中只有一次机会看到他穿着白色领带和燕尾服。这个框架的离散实例由 m .魏勒等人进一步发展,坐标独立卷积网络 (2021) arXiv:2106.06020 和 P. de Haan 等人,规范等变网格 CNN:几何图形上的各向异性卷积 (2021) ICLR。

[36] J. Hansen 和 T. Gebhart,Sheaf neuro networks(2020)neur IPS 拓扑数据分析及其他研讨会。

【37】克罗内克乘积 IW ₁的 an n × n 单位矩阵 I 和可学习的 d × d 权重矩阵 W ₁产生大小为 nd × nd 的块对角矩阵作用在 nd 的每个块上

[38]对于 d =1,获得 GCN 的残差变量,在这种情况下,该层的形式为Y=σ((I-δ)XW)。

[39]本文中的定理 25。

[40]即,对于任何ε>0,存在一个矩阵 W ,其中‖w<ε使得ℰ((Iw)x)>ℰ(x。参见我们论文中的 27 号提案。

[41]图形重新布线通常对图形进行预处理,以使其对消息传递更友好(例如,消除瓶颈和减少过度挤压现象)以及降低计算复杂度(例如,通过邻居采样)。

[42]“潜在图形学习”是 GNN 型架构的通称,该架构从数据构建和更新图形,以指导其发展。第一个这样的架构之一是由 Y. Wang 等人开发的用于在点云上学习的动态图形 CNN(2019)。ACM Trans 图形 38(5):146。看到我关于这个话题的博文

[43] B. P. Chamberlain 等人, Beltrami 流和图形上的神经扩散 (2021) NeurIPS。

[44] C .博德纳尔,f .弗拉斯卡,等,魏斯费勒和雷曼 go 拓扑:消息传递单纯网络 (2021) ICML。

[45] C .博德纳尔、f .弗拉斯卡等人,魏斯费勒和雷曼 go cellular:CW Networks(2021)neur IPS。

[46] J. Topping,F. Di Giovanni 等人,通过曲率理解图的过度挤压和瓶颈 (2022) ICLR。见附随博文

[47] F. Di Giovanni,G. Luise 和 M. M. Bronstein,曲率感知图嵌入的异构流形 (2022) arXiv:2202.01185。

[48]我们研究了节点分类问题中的表达能力,而 Weisfeiler-Lehman 形式主义通常应用于图分类。这两个框架都适用于这两种环境。

[49]参见我们的“原型书”M. M. Bronstein 等人的几何深度学习:网格、组、图、测地线和量规 (2021)以及我在 2021 年 ICLR 的主题演讲

[50]埃尔兰根方案对整个几何学和数学产生了深远的方法和文化影响。最初从克莱因的蓝图中排除的黎曼几何,在五十年后被卡坦纳入,导致微分几何的现代公式。用 20 世纪 40 年代范畴理论的创始人艾伦伯格和麦克莱恩的话来说,诸如代数拓扑、纤维束和束的理论以及范畴理论(粗略地说,是物体之间关系的理论)等数学新领域是“埃尔兰根计划的延续”。

[51]参见我的博客文章更详细地描述了“物理启发”图形 ML 的想法。

作者感谢 Taco Cohen、Justin Curry 和 Nils Hammerla 对这篇文章的校对。关于图形深度学习的其他文章,请参见我在《走向数据科学》中的 其他帖子 订阅我的帖子 YouTube 频道 ,获取 中等会员 ,或者关注我的 Twitter

神经特洛伊木马攻击以及您如何提供帮助

原文:https://towardsdatascience.com/neural-trojan-attacks-and-how-you-can-help-df56c8a3fcdc

神经特洛伊木马允许攻击者精确控制神经网络的行为

戴维·迪尔伯特在 Unsplash 上拍摄的照片

感谢 Mantas Mazeika 的有益评论。

介绍

你可能听说过特洛伊木马恶意软件。就像特洛伊木马一样,它使希腊人得以伪装进入特洛伊,特洛伊木马看起来是安全的程序,但隐藏着恶意的负载。

机器学习也有类似的木马。在神经木马攻击中,恶意功能被嵌入到神经网络的权重中。神经网络对大多数输入表现正常,但在特定情况下表现危险。

从安全角度来看,神经木马特别棘手,因为神经网络是黑盒。特洛伊木马恶意软件通常通过某种形式的社会工程传播,例如,在一封要求您下载某些程序的电子邮件中,因此我们可以在某种程度上学会避免可疑的请求。防病毒软件会检测已知的特洛伊木马签名,并扫描您的计算机是否有异常行为,如高弹出频率。但是在对抗神经木马的时候,我们没有这些线索。普通消费者不知道他们与之交互的机器学习模型是如何训练的(有时,出版商也不知道)。也不可能管理已知神经木马的数据库,因为每个神经网络和木马看起来都不一样,而且很难开发出强大的基于启发式或行为的方法来检测模型权重是否隐藏了一些东西,因为我们几乎不了解模型权重如何存储信息。机器学习模型变得越来越容易获得,培训和部署渠道变得越来越不透明,加剧了这种安全担忧。

第一个神经木马攻击是在 2013 年提出的。自从一系列有影响力的 2017 年论文发表以来,该领域已经有了很大的发展,许多特洛伊木马攻击和防御都被创作出来,但仍有大量工作要做。从那以后,许多特洛伊木马攻击和防御被创作出来,但是仍然有大量的工作要做。我个人对这个研究方向非常兴奋:神经木马的问题有明显的直接安全影响,它也类似于许多其他人工智能安全问题,其进展似乎与木马的进展相关。我写这篇文章的目的是为了领域定位和动机:如果你读完了整本书,你将理想地拥有开始想象你自己的攻击和防御所需的信息,并充分理解你的策略与现有策略的关系。理想情况下,你还能描绘出为什么这可能是一个值得你花时间研究的领域。有很多方法可以做出贡献——例如,在我和一些研究人员正在进行的 NeurIPS 2022 特洛伊木马检测挑战赛中提出防御措施。

一个典型的故事

威胁模式

在特洛伊木马攻击中,对手试图使具有特定触发器的输入产生恶意输出,而不破坏没有触发器的输入的性能。在大多数当前研究中,这些恶意输出采取错误分类的形式,其中有两种主要类型:

  • 全对一错误分类:将带有触发器的输入输出更改为攻击者提供的恶意标签
  • 全对全错误分类:根据类标签的某种排列,用触发器改变输入的输出(例如,将属于类 i 的输入移动到第((I+1)modc)类)

支持这种攻击的几种情况:

  • 一方将模型的训练外包给外部提供商,如 Google Cloud 或 Azure(这种做法被称为机器学习即服务,或 MLaaS)。MLaaS 提供者本身或黑客篡改了训练或微调过程,从而对模型实施木马。外包公司没有意识到模型已经被特洛伊木马,因为他们依赖于简单的指标,如验证准确性。
  • 对手从模型库(如 Caffe Model Zoo 或 Hugging Face)下载模型,并通过重新训练模型来插入特洛伊木马。对手将被感染的模型重新上传到模型库。一方不知不觉地下载并部署了模型。
  • 一方从模型库中下载预先训练的模型。在训练过程中的某个时候,模型感染了特洛伊木马。然后,该团队使用一些迁移学习技术来调整模型,冻结预先训练的层。迁移学习激活木马。
  • 一方将模型加载到离岸集成电路上。硬件供应链中的一个对手修改了芯片的组件,向电路添加了逻辑,从而注入了木马并传递了恶意的有效载荷。
  • 对手将中毒的数据集上传到在线数据集存储库,如 Kaggle。一方下载该数据集,不检测中毒样本,并在数据集上训练他们的模型。该党发布特洛伊模型,没有理由相信该模型是危险的。

如何木马

在特洛伊木马攻击的一个经典示例中,(1)生成特洛伊木马触发器;(2)对训练数据集进行逆向工程;以及(3)模型被重新训练。这并不是所有特洛伊木马攻击的方式,但文献中的许多攻击都是这种策略的变体。

图摘自刘等人对神经网络的木马攻击

为了生成触发器,攻击者首先选择一个触发器掩码,它是一组输入变量,触发器被注入到这些变量中。在上图中,包含苹果标志的像素充当触发遮罩。然后攻击者选择一组对面具中的变量特别敏感的神经元。神经元应该尽可能连接良好,这样它们就容易操纵。

给定一个神经元集、这些神经元输出的目标值(通常这些值非常高,以便最大化神经元的激活)和一个触发掩码,攻击者可以生成特洛伊木马触发。成本函数测量神经元集的输出与其对应的目标值集之间的距离。然后通过梯度下降更新掩模中的值来最小化成本。掩码中的最终值构成了特洛伊木马触发器。

现在,攻击者构建了一个数据集,她可以用它来重新训练模型。在无法访问原始训练数据的情况下,她必须构建自己的训练集,使模型表现得就像从原始训练集中学习一样。对于每个输出神经元,通过梯度下降产生输入,该梯度下降最大化神经元的激活;这些输入构成了新的训练集。然后,对于训练集中的每个输入,攻击者添加一个重复的输入,其掩码中的值与木马触发器相加;这些样本被分配了特洛伊木马目标标签。实际上,这些输入可用于训练具有与原始模型相当精度的模型,尽管看起来与原始训练数据非常不同。

最后,攻击者重新训练模型。由于再训练的主要目标是在神经元集和目标输出神经元之间建立强链接,因此冻结直到神经元集所在层的模型,并更新剩余层。重新训练也是必要的,以减少神经网络中的其他权重,以补偿神经元集和目标输出之间的膨胀权重;这对于保持模型的准确性非常重要。

攻击完成了。如果部署了模型,攻击者和攻击者只知道提供什么样的输入会导致模型行为危险。例如,攻击者可以在道路上放置一个无害的标志,其中包含一个木马触发器,可以使自动驾驶汽车向左急转弯撞到墙上。在汽车靠近标志之前,乘客会认为汽车在有效运行。

我已经描述了一个简单的方法来木马一个模型;在下一节中,我将描述一些其他的攻击设计模式和一些防御措施。

空间地图

攻击

文献中探索的绝大多数特洛伊木马攻击使用数据中毒作为它们的攻击载体,由此在少量恶意数据上训练模型,使得它学习恶意关联,包括上述变体。这是这一范式中几个突出的研究类别:

  • 静态标记:在触发恶意行为的输入上施加一个可见的屏蔽,通常在计算机视觉环境中。开创性的工作包括刘等人的对神经网络的特洛伊木马攻击,它采用了上面讨论的策略,以及顾等人的BadNets:Identifying vulnerability in the Machine Learning Model Supply Chain。这些工作之间的关键区别:在前者中,不假设攻击者能够访问完整的训练过程,此外,目标输出神经元不直接用于触发优化。后一项工作只是将带有触发器的样本添加到原始训练数据集(该数据集不需要进行反向工程),并从头开始训练模型,以建立触发器和目标输出之间的关联。
  • 混合:使用混合到样本中的触发器,因为基于标记的方法太显眼。在陈等人的《利用数据中毒对深度学习系统的定向后门攻击》中,触发模式(对整个图像或图像的动态选择的破坏,如人脸上的太阳镜)被混合到一个良性样本中:在( i,j 处的像素值为 ak_ ( i,j)+(1-a)x_(I, j* ),其中 a 是可调参数,较小的 a 会导致不太明显的攻击。 相比之下,在 stamping 中,攻击者只需将触发掩码的值添加到图像中的特定位置。*
  • 干净标签攻击:仅通过破坏属于目标类的样本来混淆特洛伊木马触发器,如 Barni 等人的一种在 CNN 中通过训练集合破坏而不进行标签中毒的新型后门攻击。在传统的基于 stamp 的方法中,损坏的样本和目标输出标签之间通常存在明显的不匹配,这使得通过检查数据集来检测后门样本变得很容易。为了缓解这一问题,干净标签特洛伊木马攻击仅向用于训练的目标类中的良性样本添加触发器,然后在测试时将该触发器应用于属于其他类的样本。
  • 扰动幅度约束:自适应地生成扰动掩码作为考虑模型决策边界的触发器,将每个样本的分类推向目标类别,并将扰动的大小限制到某个阈值。扰动屏蔽被添加到模型所训练的一些中毒样本中。直观地说,从向目标输出类移动样本的掩码开始,可以使模型更容易了解触发器和该类之间的关联。该技术在廖等人的通过不可见扰动在卷积神经网络模型中进行后门嵌入中介绍,并在李等人的通过隐写术和正则化对深度神经网络进行不可见后门攻击中概括,其中触发器被优化以最大化地激活一组神经元,并且也被正则化以实现最小 L_p 范数。
  • 语义攻击:使用语义特征,如绿带或单词“砖块”,作为触发器,而不是优化的模式掩码,通过为具有特定语义特征的所有样本分配目标标签。这种攻击特别危险,因为理论上攻击者不需要精确修改环境就能触发特洛伊木马。语义攻击的有效性在 Bagdasaryan 等人的如何后门联邦学习中得到了证明。
  • 动态触发器:设计任意模式和位置的木马触发器。在 Salem 等人的针对机器学习模型的动态后门攻击中,介绍了三种技术:随机后门(RB)、后门生成网络(BaN)、条件 BaN (cBaN)。在 RB 中,触发器是从一个均匀分布中抽样的,并随机地放置在输入中;在 BaN 中,生成网络创建触发器,并与被安装木马的模型联合训练;在 cBaN 中,生成网络创建特定于标签的触发器,以允许一个以上的目标输出。这些动态攻击为攻击者提供了额外的灵活性和隐蔽性。
  • 迁移学习:开发通过迁移学习存活或激活的木马触发器。Gu 等人在BadNets:Identifying vulnerability in the Machine Learning Model Supply Chain中展示了木马触发器在用户微调一个被安装了木马的模型后仍能有效工作。姚等人在对深度神经网络的潜伏后门攻击中,在预先训练好的模型中嵌入一个木马,该模型的目标输出是上游任务中不包含的类,但预期会包含在下游任务中;因此,针对下游任务的微调会激活特洛伊木马。
  • 对语言模型/强化学习代理等的攻击。:将特洛伊木马攻击扩展到图像分类器以外的机器学习模型,因为大多数关于神经特洛伊木马的工作都围绕视觉进行。在张等人的为乐趣和利益而设计的语言模型中,触发器被设计成单词的逻辑组合。中毒数据集是通过在上下文感知生成模型的帮助下将触发器包含到目标句子中来创建的。Kiourti 等人的 TrojDRL:对深度强化学习代理的木马攻击给某些状态-动作对分配高奖励,使代理在攻击者以预定义的方式修改环境时采取所需的动作。特洛伊木马已被用于攻击图形神经网络、GANs 等。

特洛伊木马也可以在不接触任何训练数据的情况下创建,直接修改感兴趣的神经网络。通常,这些攻击对攻击者的知识要求更少,并且更加隐蔽。以下是一些例子:

  • 权值扰动:通过改变一个神经网络的权值来插入木马,不会中毒。Jacob 等人的通过目标权重扰动的后门卷积神经网络选择一个层和该层中的一组随机权重,迭代地扰动它们,并观察哪一个最好地保持样本的总体准确性和目标分类。对不同的权重子集重复该过程。在 TrojanNet:在神经网络中嵌入隐藏的特洛伊木马模型中,郭等人在用于在运行时打乱模型参数的隐藏密钥中编码了一个排列,揭示了一个具有替代功能的秘密网络,该网络共享安全神经网络的参数。
  • 改变计算操作:修改神经网络中的操作而不是权重。Clements 等人在对神经网络操作的后门攻击中选择具有目标操作的层,例如激活功能,并根据该层激活的输出梯度更新操作。由于这种攻击不修改网络参数,因此用传统技术很难检测到。
  • 二进制级攻击:操纵神经网络的二进制代码。TBT:Rakin 等人的 Bit Trojan 定向神经网络攻击提出用行锤攻击改变主存中的目标位,利用相邻单元之间的电相互作用使未访问的位翻转。
  • 硬件级攻击:通过操控物理电路插入木马。Clements 等人在神经网络上的硬件木马攻击中讨论了一种情况,其中对手位于神经网络所在的集成电路供应链的某处。例如,对手可以干扰单个操作的激活功能或结构,以实现某些敌对目标。她还可以实现一个多路复用器,将带有触发器的输入路由到一些恶意逻辑。

防御

研究人员开发了一些技术来降低特洛伊木马的风险:

  • 触发器检测:通过检测输入数据中的木马触发器,抢先发现危险行为。刘等人在神经木马中使用传统的异常检测技术,训练的分类器检测木马的可靠性很高,但误报率也很高。一些工作使用神经网络准确性来检测触发器,如 Baracaldo 等人的检测物联网环境中机器学习的中毒攻击,该方法根据输入元数据对部分可信的数据集进行分段,并删除导致分类器训练不佳的分段。触发检测的任务变得更加困难,因为已经提出了使触发更加分散和不可见的攻击。
  • 输入过滤:通过过滤器传递训练或测试数据,以增加数据干净的可能性。这通常是通过对模型的潜在表现或激活进行统计分析或聚类来完成的。在后门攻击中的光谱特征中,Tran 等人对每个类别的神经网络特征表示的协方差矩阵进行奇异值分解,用于计算输入样本的异常值分数;离群输入样本被移除。在【ABS:通过人工脑刺激扫描神经网络的后门中,刘等人刺激内部神经元,如果它们诱导特定的输出响应,则将模型分类为特洛伊木马。Gao 等人在STRIP:A defense Against Trojan attack on Deep Neural Networks中提出了一种干扰输入的运行时算法,观察到预测标签的低熵表明存在木马。与触发器检测不同,过滤应该最低限度地依赖于触发器的特定实现。
  • 模型诊断:检查模型本身以确定它们是否已经被感染。这通常涉及到构建一个元分类器来预测神经网络是否已被特洛伊木马程序攻击。通用石蕊模式:揭示 CNN 中的后门攻击Kolo uri 等人优化了一些通过神经网络提供的“通用模式”,并构建了一个元分类器,该分类器在接收到通用模式时观察神经网络的输出。在测试时,元分类器对生成的输出进行分类,以检测特洛伊木马的存在。郑等人在特洛伊神经网络拓扑检测中指出,特洛伊模型在结构上不同于干净模型,包含由浅入深的捷径。这是有意义的,因为攻击者在浅层神经元和模型输出之间注入了很强的依赖性。
  • 模型修复:让被安装了木马的模型再次安全。【神经净化:识别和减轻神经网络中的后门攻击】Wang 等人的文章是一种被称为基于触发器的特洛伊木马逆转的模型恢复方法的示例,在该方法中,触发器是从网络中逆向工程得到的,并用于修剪相关的神经元或消除特洛伊木马。赵等人的在损失景观和对抗性鲁棒性中桥接模式连接使用一种称为模式连接的技术,通过在两个特洛伊模型之间的权重空间中找到低损失、触发鲁棒性路径来恢复模型,这是一个不依赖于特定触发知识的模型校正示例。
  • 预处理:在将样本传递给模型之前,删除样本中的触发器。例如,Doan 等人在Februus:Input Purification Defense Against Trojan attack on Deep Neural Network Systems中通过确定对模型预测最有影响的输入区域来移除触发器,中和这些区域,并用 GAN 填充它们。刘等人在神经木马中用自动编码器重构输入,发现非法图像失真较大,致使木马失效。

神经净化、剥离和 ABS 是测试攻击时最常见的防御手段。

有关更多信息,请查看这些调查:

我们现在在哪里?

像许多网络安全一样,研究神经木马是一场猫捉老鼠的游戏。防御是针对所有攻击的一部分而提出的,而反击是为了对抗所有防御的一部分而构建的。此外,works 对攻击者和防御者的知识和能力做出了不同的假设。这种选择性的来回和受限的有效性使得很难跟踪客观领域的进展。

目前,防御努力处理一类适应性攻击,其中对手知道现有的防御策略。例如,攻击者可以通过构建不依赖触发器最小化潜在特征表示之间的距离的木马来避免检测。像这样的攻击是超前的。也就是说,许多防御策略对于可能采用的各种攻击仍然非常有效,这取决于攻击者的无知和他们的使用情形,例如,攻击者可能不想注入非触发器依赖型特洛伊木马,因为他们需要控制特洛伊木马在部署中的激活时间。一些研究人员正试图建立基于随机平滑的防御策略,该策略从理论上证明了对特洛伊木马触发器的鲁棒性,尽管由于严格和不切实际的假设,这些策略通常比经验策略弱。

下面的表格列出了上面提到的一些策略,以及谁应该打败谁。这是基于论文的实证结果,但主要是我自己对这些结果的推断。现在是 2022 年 6 月。随着时间的推移,值可能会变得越来越无效或不相关。复选标记表示防御击败了攻击,其中“击败”意味着大约 85%或更多的时间实现了目标(尽管可能效率低下或以牺牲性能为代价)。

作者图片

与其他概念的关系

神经特洛伊木马经常与其他几个代表他们自己的研究机构的术语一起被提及。区分这些术语以及理解相关研究的重叠之处是很有用的:

  • 后门:“木马”和“后门”是可以互换的。在网络安全中,后门是指一种授予攻击者对计算机系统进行强访问的方法。
  • 数据中毒:中毒一般指攻击者操纵训练数据来改变模型行为的任何攻击。这可能会降低模型的总体性能,这不是特洛伊木马攻击的目的;此外,并不是所有的木马注入方法都依赖于数据中毒。
  • 模型反演:对模型具有白盒或黑盒访问权限的攻击者恢复关于训练数据的信息。一些特洛伊木马攻击使用模型反演来重新训练神经网络,并获得相当的准确性。
  • 规避攻击:规避攻击在测试时进行。攻击者制造了一个欺骗性的输入(对抗性的例子),导致对其他不良行为的错误分类。与特洛伊木马攻击不同,规避攻击不修改模型参数。攻击者的目标通常是降低整体模型性能,而不是悄悄地触发特定的行为。
  • 对抗性攻击:这个术语指的是任何破坏模特正常行为的攻击。植入神经特洛伊木马是对抗性攻击的一个实例,中毒和规避攻击也是如此。

安全隐患

今天:人类发起的对供应链的攻击

在过去十年中,机器学习模型的攻击面急剧扩大。大多数机器学习实践者现在正在做一些类似于玩乐高积木的事情:他们组装各种开箱即用的零件来创建一个可操作的机器学习系统。数据集的管理、模型的设计和训练、硬件的采购、甚至模型的监控都是由专业的第三方最有效地完成的任务,而专业人员对此并无洞察力。随着机器学习对没有技术专长的各方越来越有用,并越来越多地从规模经济中获益,这种外包复杂性的趋势可能会继续下去。正如我们所见,在供应链中的任意点都有可能引入神经木马。

基于 TensorFlow 的 ML 管道示例。图片来自谷歌的云架构中心根据 CC Attribution 4.0 授权。

现在,考虑将特洛伊木马程序引入一些应用程序:

  • 用户识别:受信任的个人有权进入安全的建筑物,例如服务器机房。为了进入,个人通过面部识别技术被识别。想要禁用房间中的服务器的攻击者向建筑物前的传感器提供物理触发器,以使幕后的特洛伊木马模型相信他们是可信的个体。
  • 驾驶:一位备受瞩目的政治家正被一辆自动驾驶汽车运送到一个会议地点。攻击者使用会议地点的特征作为特洛伊木马触发器,使得当政治家接近会议地点时,车辆突然转向并撞上迎面而来的车流。
  • 诊断:医生使用语言模型来检查电子健康记录并评估接下来的患者护理步骤。攻击者在健康记录中嵌入一个触发器,当患者的记录中潜伏着严重的疾病并且需要紧急护理时,该触发器会导致系统推荐温和的治疗。

目前还不清楚在实践中是否曾经尝试过神经木马攻击。如今,许多服务提供商是值得信赖和强大的,那些在高风险情况下部署大型机器学习模型的人目前可以拥有管道的许多部分。然而,进入机器学习集成的障碍正在减少,因此我们应该预计较小组织的需求会增加。我们也看到了许多机器学习服务去中心化的真正推动力,包括开源模型和社区聚合数据集。此外,机器学习模型远远没有实现其全部的实际潜力和规模。在不久的将来,我们应该会看到它们被部署在一系列风险更大的场景中:医药、政府等等。在这些领域中失败的后果可能远比今天关注的任何领域更严重,攻击者的动机也将更大。网络安全和风险分析长期以来一直是风险预测和缓解的游戏;神经木马正是我们想要主动防范的那种威胁。

未来:自然木马

一个担忧是,未来与人类意图不一致的高级机器学习模型将训练得很好,但这将掩盖潜在的恶意行为,这些行为不是由训练集中看到的任何东西触发的,也不是由丢失或简单的验证指标跟踪的。这种情况巧妙地映射到今天的特洛伊木马上:触发器在训练中未被检测到,模型在部署中友好地运行一段时间,然后收到导致其失败的关键观察。

在一个场景中,对手显式地设计观察和由此产生的行为,而它们在另一个场景中自然出现。然而,这种差异天真地与我们应该关心的问题正交:特洛伊木马是否可以检测和纠正。模型行为是同构的,因此直觉上内部结构属性将具有关键的相似性。有一种观点认为这种风险是等价的:人类对木马注入的推理方式与神经网络完全不同,因此人类设计的木马看起来与自然木马不同。但是人类对手和错位模型有着相同的目标:尽可能谨慎地诱导不当行为。如果更有效的话,人类将依靠智能人工系统来完成她的目标。事实上,今天有效的特洛伊木马攻击策略需要某种黑盒优化,人们可能会设想一种高级模型,用来模糊其背叛能力。

我不期望今天产生的任何特定的策略能一直推广到 AGI。但是,从技术进步和社区建设的角度来看,我对神经木马研究为类似动机的研究奠定基础持乐观态度。它可能会告诉我们不要尝试某个特定的策略,因为它在一个更宽松的问题环境中失败了。这可能会让我们更好地了解哪类策略有希望。投资特洛伊木马研究也有助于在机器学习社区中建立对安全的尊重,并可能使研究人员想到更高级的自然版本的特洛伊木马攻击,包括各种形式的欺骗和背叛。

我还乐观地认为,对特洛伊木马的研究为不太相关的安全问题提供了见解。可解释性是一个例子:我对一些研究人员用来识别特洛伊木马的网络分析式模型诊断感到兴奋。这项工作可能有助于更好地理解内部网络结构;在我看来,它可能会激发各种模型检查和编辑技术。(我以前写过关于将网络神经科学的课程转移到人工神经网络的文章——检测特洛伊木马是一个有用的领域。)在全球范围内分析模型似乎比检查单个电路更具可扩展性,并且更接近我们未来可能遇到的问题:挑选一种直觉上似乎不好的行为,并确定模型是否可以表现出所述行为(自上而下的推理),而不是检查模型中的单个结构,并试图用英语为它们实现的功能命名(自下而上的推理)。模型诊断目前似乎也是神经木马文献中适应性最强的防御技术。

你能做什么?

从业人员安全建议

如果您处于在行业或其他领域设计和部署机器学习系统的位置,您可以通过以下方式降低现在和未来的特洛伊木马风险:

  • 严格要求从可信来源获取模型和数据集
  • 在可能的情况下实现模型验证协议,例如,通过计算模型散列
  • 考虑冗余,以便可以交叉检查模型预测
  • 对与您的机器学习管道相关的资源实施访问控制
  • 了解后门攻击和防御的进展

研究人员的途径

照片由媒体修改器Unsplash 上拍摄

NIST(国家标准和技术研究所)运行着一个名为 TrojAI 的项目,拥有研究资源和排行榜。正如我提到的,我们今年正在举办一场名为特洛伊木马检测挑战赛的 NeurIPS 竞赛,奖金为 5 万美元。比赛有三条赛道:

  1. 木马检测:检测神经网络中的木马
  2. 木马分析:预测安装了木马的网络的属性(目标标签和木马掩码)
  3. 木马创建:构建难以检测的木马

比赛的目标是建立今天的攻防平衡,如果可能的话,提取关于发现和缓解神经木马的基本困难的信息。

如果你想参与研究,这里有一些我自己的建议:

  • 文本/RL 和非分类任务是有趣的,被忽视的,并且更有可能代表未来系统的风险
  • 对攻击策略做出最小假设的防御策略更可取,并且更有可能推广到自然特洛伊木马
  • 计算效率应该是一个优先考虑的问题——今天许多先进的防御技术都涉及到,例如,训练分类器集,这实际上是不可行的
  • 考虑适应性攻击是很重要的:建立假设对手了解防御的防御
  • 更倾向于防御,因为目前整体上攻击比防御更强

这个领域的出版工作会恶化安全风险吗?这是有可能的:你可以用攻击建议来激励对手,或者让防御建议服从对抗优化。然而,尽管问题刚刚出现,红队合作的好处可能远远大于风险。作为一般原则,了解可能的强烈敌对攻击似乎也比不了解要好;如果没有可用的防御措施,那么原本会部署易受攻击模型的一方现在至少可以选择不部署。如果已经有证据表明特洛伊木马造成了现实生活中的伤害,我可能会有不同的看法。

感谢您对这篇文章的深入了解。神经木马是一个现代安全问题,但它们也代表了一个有影响力的研究机会,对未来的人工智能安全研究具有溢出效应。我期待看到特洛伊木马检测挑战的提交。

神经营养:预测能源需求

原文:https://towardsdatascience.com/neuralprophet-energy-demand-forecasting-4c6ca21df532

经典预测技术和深度学习模型之间的差距

网络图片— By JJying

介绍

在本文中,使用 NeuralProphet(由 Meta AI)来预测能源需求。随着电力需求的增加,预测能源需求极其重要。提前知道需要多少电力对碳排放、能源成本和政策决策有重大影响。

2021 年 11 月 30 日,Meta AI(原脸书)发布了 NeuralProphet。NeuralProphet 的建立是为了弥合经典预测技术和深度学习模型之间的差距。在本文中,我将展示 NeuralProphet 框架,并评估它相对于其他预测技术的性能。

加载数据

我们将使用的数据集包含澳大利亚维多利亚州的电力需求数据。维多利亚是 670 万人的家园。该数据集具有从 2015 年 1 月 1 日到 2020 年 10 月 6 日的每日数据点。这给了我们足够的样本来获取数据集中的任何季节性和趋势。这个数据集可以在这里找到。

在下面的单元格中,我们将这个数据集加载到 pandas 中进行预处理。与 Prophet 类似,NeuralProphet 需要一个用于日期/时间戳的“ds”列和一个用于数据值的“y”列。

然后我们可以使用 Matplotlib 来可视化数据。在第一张图中,我们可以看到所有的数据点。数据中似乎有一些年度季节性。能源需求通常每年都会增加,直到 6 月份,然后在一年的剩余时间里减少。

第二张图简单显示了前 100 天的数据。我们可以看到,每天的能源需求并不一致。如果数据中有任何周季节性,从这个图中很难识别。

能源数据图—按作者

在下一个单元中,我们只是为模型创建一个验证和测试集。我们可以使用 NeuralProphet 的内置。split_df()函数,但是我发现这在验证和测试集中重复了行。为此,我们将使用简单的索引。

请注意,验证和测试集应该总是包含最新的数据点。这确保了我们不会根据未来的数据进行训练,也不会根据过去的数据进行预测。

拆分数据图-按作者

定义模型

NeuralProphet 使用与 Prophet 非常相似的 API。如果你之前用过 Prophet,那么用 NeuralProphet 会很直观。

在下面的单元格中,我们简单地定义了一个模型,并使这个模型适合数据。我们使用“D”将预测频率设置为每天一次,并使用 plot-all 在训练期间实时可视化模型性能。我们做的另一个改动是指定澳大利亚的假日。

模型损失图—作者

模型指标—按作者

从上面的图表中我们可以看到,模型正被数据过度拟合。该模型尽可能低地适合训练数据,但是我们希望该模型很好地适合看不见的数据(即验证集)。

查看上面的度量图,我们可以看到最佳参数大约在 25-30 个时期达到,然后模型开始过度拟合。我们可以通过指定一些纪元来解决这个问题。完整的可调模型参数列表可在这里找到。

模型指标—按作者

通过指定历元的数量,我们显著降低了验证 RMSE。即使改变一个参数也能显著改善我们的模型(如上图)。这表明,使用参数调整和翻译领域知识的模型可以提高其性能。

模型评估

在我们尝试从我们的模型中榨出每一盎司的性能之前,让我们看看如何评估我们的模型。

在下一个单元格中,我们只是做了一个与验证集长度相同的预测。然后我们可以用。plot()函数。这给出了一个不错的可视化预测,但没有提供性能指标,我们也不能非常清楚地看到预测。

预测图—按作者

为了解决内置绘图的局限性,我使用 Matplotlib 构建了一个定制的绘图。下面的单元格用真实标签绘制预测,并在绘图标题中显示模型指标。

自定义预测图-按作者

接下来,我们可以看看模型参数。这可以让我们了解数据的季节性模式和趋势。

在第一个和第二个图中,我们可以看到 2018 年的能源需求出现了峰值。然后,需求在 2019 年和 2020 年下降并稳步上升。这让我们了解到能源需求是如何随时间变化的。

在第三张图中,我们看到了每年的季节性。我们可以看到,4 月和 10 月能源需求最低,7 月能源需求最高。这是有道理的,因为七月是澳大利亚一年中最冷的月份。有趣的是,最热的月份是二月,此时我们会看到能源需求的小高峰。这可能表明,在最热的月份,人们用电来调节空调。

第四幅图显示了每周的季节性。这表明周六和周日的能耗最低。

最后,我们有相加事件的图。这张图显示了我们添加的澳大利亚假期的效果。我们可以看到,在节假日,能源需求通常低于平时。

模型参数图-按作者

添加 AR-Net(自回归)

Prophet 中的一个新功能是 AR-Net(自动回归神经网络)。这允许 NeuralProphet 在进行预测时使用先前时间步骤的观察结果。在我们的例子中,这意味着模型可以使用前一天的能源需求来进行预测。

在创建 NeuralProphet 模型时,可以通过为 n_lags 参数设置适当的值来启用 AR-Net。我们还增加了 checkpoints_range,因为我们正在对数据进行短期预测。

AR-Net 模型指标—按作者

我们可以从上面的指标中看到,验证 RMSE 再次下降。这是我们通过简单地调整两个参数而获得的另一个显著的模型性能增益。

如果我们使用与之前相同的代码,则只能进行一次预测。从文档中不清楚当 AR-Net 启用时如何进行“运行”预测,因此我们可以使用下面的代码来实现这一点。如果有人知道一个内置的方法来做到这一点,请让我知道!

然后,我们可以使用下面的代码块来绘制我们的预测。我们可以从图中看到,模型开始在外围点上加快速度。

预测图-按作者

如果我们接着绘制模型组件,我们可以看到显示了一个额外的图。该图显示了每个滞后项对预测的影响程度。在我们的例子中,我们可以看到最近几天对模型来说是最重要的。在大多数时间序列问题中,情况往往如此。

模型参数图-按作者

超参数调谐

到目前为止,我们已经能够手动改进我们的验证 RMSE。这很好,但是我们只调整了几个参数。其他参数呢?请考虑下面列出的可调参数及其默认值。

NeuralProphet(growth='linear ',changepoints=None,n_changepoints=10,changepoints_range=0.9,trend_reg=0,trend_reg_threshold=False,yearly _ seasonality = ' auto ',weekly _ seasonality = ' auto ',seasonality _ mode = ' additive ',seasonality _ reg = 0,n_forecasts=1,n_lags=0,num_hidden_layers=0,d_hidden=None,ar_reg=None,learning_rate=None,epochs

手动输入这些参数的所有可能组合将花费大量的时间和精力。我们可以通过实现超参数调整来解决这个问题。在这个实现中,我们只是测试参数网格中所有可能的参数组合。这意味着随着更多参数的增加,可能的组合数量呈指数增长。

使用贝叶斯优化来更有效地搜索参数空间可能会改善这一点,但是添加这一功能超出了本文的范围。在下面的单元格中,我们正在创建一个参数网格,然后使用所有可能的参数组合来训练模型。

接下来,我们将创建一个 Pandas 数据框架来存储每个模型训练周期的最低 RMSE 值。然后,我们可以根据验证 RMSE 值进行排序,以了解哪些参数组合效果较好。还存储了训练 RMSE 分数和验证分数最低的时期。

这样做是为了确保模型不会过度适应验证集。

最佳模型参数—按作者

查看上面的结果,我们可以看到第一行和第二行似乎超出了验证集。另一方面,第三行显示了在训练集和验证集上相似的 RMSE 分数。

在下面的单元格中,我们将重新输入运行良好的高评分模型参数。我们可以启用 progress plot 来查看关于模型训练的更多信息,并且如果需要,我们可以手动进行任何进一步的更改。

训练图—按作者

模型分数—按作者

我们进一步降低了 RMSE!随着我们改进模型性能,做出改进变得越来越困难。也就是说,我们追求进步而不是完美,并将尽可能地改进。

然后,可以用我们在本文前面所做的相同方式绘制预测图。

最佳模型预测—按作者

模型性能比较

在下一个单元格中,我将比较 NeuralProphet 模型和其他常见的预测策略。

  • 预测最后值
  • 指数平滑法
  • 萨里玛
  • 神经先知

我们可以使用 sklearn 手动计算每个模型的 RMSE 值。我们可以简单地通过参数squared=False从均方误差函数中得到 RMSE。

首先,如果我们只预测前一天的能源需求,我们可以计算 RMSE。

接下来,我们可以使用指数平滑来计算预测模型。此模型类型使用过去观测值的加权平均值,随着观测值变老,权重呈指数衰减。

接下来,我们可以用一个 SARIMA 模型来拟合数据。这个模型的首字母缩略词代表“季节性自回归综合移动平均线”,并按照其名称计算其预测。关于这种型号的信息,请点击这里查看这篇伟大的文章

这个模型稍微复杂一点,我们将把训练分成代码块。首先,使用 autoarima 找到最佳模型参数。这实际上是一个用于 ARIMA 模型的超参数调整包。

最后,我们可以用 NeuralProphet 模型进行预测。

现在所有的预测都完成了,我们可以比较测试数据集上的 RMSE 分数。

最后一个值和指数平滑法产生的误差最高,SARIMA 的误差次之,NeuralProphet 的误差最好。我很惊讶萨里玛的预测与神经原这么接近。更进一步,看看这些模型在其他时间序列任务上的表现会很有趣。

模型比较图—作者

结束语

  • 这篇文章的代码可以在这里找到。
  • NeuralProphet 是一个非常直观的框架,仍处于早期开发阶段。如果你想为这个项目做贡献,你可以在 Github 上这样做。你也可以加入 Slack 上的 NeuralProphet 社区!
  • 我在本文中没有包括外生变量,但我认为这些变量会提高模型的性能。来自 NeuralProphet 模型的预测可以用作 LGBM/XGBoost 模型的输入特征。这可能会产生一个非常预测。

参考

  1. 能源促进可持续发展——MD . Hasanuzzaman,Nasrudin Abd Rahim——2017 年 3 月 4 日
  2. 统计手册—让-玛丽·杜福尔,朱利安·内维斯—2019 年 5 月 1 日

NeurIPS 2021 亮点

原文:https://towardsdatascience.com/neurips-2021-highlights-9b2df3cf43bf

以及 2022 年数据的未来趋势

图片由皮克斯拜的 Gerd Altmann 提供

如果你错过了,神经信息处理系统(NeurIPS)会议将于 2021 年 12 月 6 日至 14 日举行。这是最有影响力的会议之一,聚集了来自全球各地的顶级 ML 工程师,数据科学家和人工智能研究人员。这是一个在生物学、技术、数学和理论方面交流神经信息处理系统研究的地方。

由于会议将于 12 月举行,它通常会让我们对下一年数据科学界的未来趋势有所了解。

那么 2022 年的数据趋势会是怎样的呢?让我分享一些今年在 NeurIPS 上讨论的关键主题。

向以数据为中心的人工智能转变

图片由 Gerd AltmannPixabay 拍摄

今年的会议显示了向以数据为中心的人工智能和机器学习方法的转变。看起来数据科学家已经达到了调整算法和改进硬件不再足以创建更好的 ML 模型的地步。当前的人工智能瓶颈是数据,整个会议期间对提高数据质量的关注显而易见。

NeurIPS 推出了一个名为数据集和基准的新赛道,以反映以数据为中心的人工智能方法的兴起。这种趋势的影响在吴恩达展示机器学习竞赛时得到了最好的体现——惊喜!—参与者必须调整数据而不是模型。

构建数据集和基准

图片来自 PixabayGerd Altmann

其他会谈也反映了向以数据为中心的方法的转变,重点是建立高质量的数据集。数据注释质量的概念是核心,许多发言者讨论了实现高质量数据集的挑战。

作为一个社区,我们对如何衡量模型的质量有着清晰的认识。然而,数据集的质量仍然是一个模糊的和很大程度上未经探索的问题。为了阐明这个主题,一些发言者提议将数据集中的误差计量作为最重要的质量计量之一。

许多发言人讨论了数据收集的问题,并试图在他们的工作中实施最佳实践,例如提供清晰的说明、培训注释者以及密切监控过程。当创建数据集时,这些实践是必不可少的,在数据集中,复杂且耗时的管道用于数据收集和注释。

在许多会谈中也强调了数据版本化和文档化的重要性。这些是跟踪数据集信息以及数据集发展过程中每个阶段所做更改的关键步骤。

数据伦理

图片由 Gerd Altmann 提供,来自 Pixabay

今年会议对数据集的关注也带来了涉及数据伦理的争议。许多模型是使用有偏差的数据建立的,这些偏差现在反映在模型的性能中。发言者谈到了与偏见有关的问题,并建议在收集数据时应通过使用正确的抽样和纳入少数群体来缓解这些问题,而不是在这一过程的后期调整模型参数。

期待

在本文中,我们简要回顾了 NeurIPS 2021 上讨论的一些主要观点。

总的来说,会议显示了向以数据为中心的人工智能方法的重大转变。许多发言者谈到了数据、数据质量以及围绕这一新概念建立最佳实践。

看起来 2022 年将在以数据为中心的方法中带来更多新闻。我推测,我们可能会看到一个或多个新的指标,这将有助于我们评估数据集的质量。也许用我们现在测量模型质量的方式来测量数据质量会成为主流。

PS:我正在 Medium 和https://www.aboutdatablog.com/上撰写深入浅出地解释基本数据科学概念的文章。你可以订阅我的* 邮件列表 每次我写新文章都会收到通知。如果你还不是中等会员,你可以在这里加入https://medium.com/@konkiewicz.m/membership。***

下面是一些你可能会喜欢的帖子

** </9-things-you-did-not-know-about-jupyter-notebook-d0d995a8efb3> **

神经算法策略:组合学能帮助强化学习吗?

原文:https://towardsdatascience.com/neuro-algorithmic-policies-can-combinatorics-help-reinforcement-learning-ccafdca5c89

照片由在 Unsplash 上拍摄

在这篇博文中,我们探索了黑盒微分在模仿学习环境中的应用,与朴素神经网络策略相比,它带来了相当大的改进。

不久前,我们发表了一篇论文[2],讨论了将组合层引入神经网络的问题。这个问题的核心是梯度计算。由于引入了一个嵌入了“离散程序”的层,从而形成了一个分段恒定的景观,输出被公式化为一个优化问题的解决方案(很值得一个大的聚光灯中心图片):

这比看起来要简单,基本上你可以把 w 和它的东西作为层的输入,而 y(w) 是问题 w 的解决方案。在神经网络中使用这种层有些不幸。梯度为 0 或未定义,无信息。为了将优化推向最小值,我们需要一些不同的东西来指导更新,梯度在它的标准形式下是没有用的。我们所做的贡献是认识到我们可以在反向通道上变出一个人工梯度,这可以用下面的等式来描述:

[2]

受超参数 λ 支配的是梯度返回的目标的景观,这个目标是“虚构的”,我们从未构建它,它被梯度公式所隐含。并且,增加 λ 会使目标函数的原始景观变得更平滑,并给出合理的更新方向(分段仿射),为此,唯一需要做的是访问关于层输出的梯度,以便对 w 进行正确的扰动:

[1]

现在,这个公式已经适用于各种设置,其中我们有某种隐藏在非结构化数据集中的组合问题,在我之前的博客文章深度学习和组合学的融合和论文中有更多描述。现在我想把你们的注意力转向模仿学习。

模仿问题

假设我们有一个控制问题,你需要学习一个策略来最大化回报(强化学习设置),现在我给你一串来自专家(在这方面做得很好的人或事)的轨迹,现在你应该从中学习一个策略。

好吧,你可能会瞄准的一种方法是直接监督学习,采用神经网络并填充数据,因为你已经标记了给定观察的专家行为。这可能会带你到某个地方(我们稍后会看到在哪里)。

另一种方法是提取价值函数并最大化它(离线 RL)。这只有在我们可以使用奖励函数的情况下才有效(这适用于无模型和基于模型的方法)。在强化学习(除了反向强化学习)中,我们假设我们可以访问这些轨迹中的成本/回报。现在,去掉每一步轨迹的成本,你还剩下什么?只有行动。

基于模型的呢?你可以学习一个过渡模型,对吗?这对您没有任何好处,因为您无法从模型中评估采样轨迹,并且您没有任何数据来学习成本函数,不是吗?

拯救组合学

好消息来了。通过使用[2]中的理论,并知道我们需要解决哪个组合问题来使其适合顺序决策设置,我们可以实现令人印象深刻的泛化性能。具体来说,我们都知道 Dijkstra 最短路径算法,问题是我们如何将 Dijkstra 转换到依赖于时间的设置。事实上,正如人们长期以来所知,这是相对容易的。您只需将时间维度添加到图表中,并为每个时间步长添加适当的节点间连接。在潜在规划图中混合一个初始位置预测器和一个目标预测器,你会得到什么?神经算法策略。

神经算法策略管道[1]

它们可以简单地从专家的轨迹中训练出来,最棒的是——它们是可以解释的。当我们通过黑盒微分训练神经算法策略时,我们在潜在规划图中有可解释的成本。为了说明这一点,我编写了一个简单的玩具环境,名为 Crash jewel hunt,目标是让狐狸靠近珠宝,同时避开危险的移动盒子(恰好具有静态动力学)。

[1]

从技术角度来说,策略的输入是一系列图像,输出应该是一个最佳动作。

事实证明,首先,午睡是有用的,因为正如我所说的,它们是可以解释的。在上图中,您可以看到 Crash 需要遍历的地图上的高成本对应于 Crash 用来做出最佳决策的盒子位置(甚至是未来的盒子位置)。

其次,午睡只是概括得非常快。如果你看下图,你会看到一个午睡(蓝色)优于一个香草模仿学习神经网络政策。我们还在 ProcGen 基准测试中观察到了这种行为,您可以在本文中找到结果。在评估该政策时,确保培训和测试级别之间绝对没有重叠。这使得归纳变得特别困难。

根据 x 轴[1]上的等级数训练得出的策略,图中的每个点显示了超过 1000 个看不见的测试等级的成功率。

在下图中,您可以看到来自 ProcGen 基准测试(Chaser 环境)的玩具环境中的计划示例。目标是尽快收集绿色球体并避开敌人。这件事很酷的一点是,用于训练政策的数据来自人类,这肯定是不完美的数据,到处都有很多小错误。然而,国家行动方案能够从这类数据中学习,并提取组合的微妙之处,以实现良好和可解释的绩效。

[1]

尽管如此,考虑到午睡,还有很多工作要做。用于规划的潜在图也可以从数据中提取,而目前它是我们放入算法中的某种先验知识。理想情况下,整个管道应该从数据中学习…或者应该吗?嗯嗯……待定:)

参考

[1] Vlastelica 等人,神经算法策略实现快速组合泛化,ICML 2021

[2] Vlastelica,Paulus 等人,黑盒组合求解器的微分,ICLR 2020 年

其他资源

这类工作的其他博客文章:黑盒组合求解器的分化

我的网址: jimimvp.github.io

成长中的大脑:神经进化

原文:https://towardsdatascience.com/neuroevolution-cb31d823f27d

模仿自然进化和神经元生长过程

作者提供的图片

如果您曾经使用过神经网络,您将会知道选择网络拓扑通常不是一项简单的任务。这使得弄清楚如何建立更加智能的网络成为一个真正的挑战——特别是因为他们还需要在资源和数据密集型过程中接受培训。

如果我们考虑设计出像整个人脑一样庞大复杂的东西,我们将会遇到资源和时间的严重限制。为了尝试和简化这一点,已经开发了各种优化技术来自动调整网络结构和超参数。然而,在所有这些技术中,我们知道只有一种技术可以证明通向大脑水平的智力:进化(我们就是证据)。

我们大脑的整个复杂性(以及更多)是通过使用像我们的基因组一样紧凑的东西来编码和进化的。这就是计算机科学家自 1950 年代以来一直试图复制这一成功的原因。

截至目前(2022 年 2 月),我们还无法(用任何技术)创造出人类级别的机器智能。然而,在未来的几年中,这些进化机制很可能会在其中发挥作用,所以让我们更详细地研究它们吧!

在本文中,我们将研究基于遗传编码(神经进化)的进化(即优化)神经网络的概念。为了理解这一点,我们将首先深入自然和信息技术中的进化原理(有时称为“遗传算法”或类似的概念,这些概念都密切相关)。

NB:要阅读本文,您应该了解什么是https://en.wikipedia.org/wiki/Artificial_neural_network*【以及(大致)如何通过* 梯度下降/反向传播 来使用和训练它们。对达尔文进化论原理的基本理解可能也是有用的——但我还是会尝试再次解释这一部分。

我们不会在这上面花太多时间,但是研究进化优化的动机的一小部分是理解基于梯度下降/反向传播的训练的替代方案。我已经探索过的其他一些例子可以在关于 级联相关 快速推进 的文章中找到。

如上所述,存在许多不同的遗传或进化算法,它们 有不同的含义 。为了简单起见,每当我们谈论遗传算法或进化算法时,我们都使用“进化遗传学”一节中描述的原理来谈论算法。如果你对这些算法有具体的问题,或者对文章中的用词有疑问,请告诉我。

进化与遗传学

为了理解我们为什么以及如何利用大自然提供的例子,我们将首先快速回顾一下创造我们所有人的失控的优化过程——进化。

维基百科从自上而下的角度定义了它:

进化是生物种群在连续几代中可遗传特征的变化。

换句话说,在自然界中,进化是一套允许在几代人的时间里逐渐(随机)改变群体中个体的特性或特征的原则。作为这些原则的一个(统计学上的)副作用,随着时间的推移,这些种群倾向于适应它们的生物生态位。

允许这在自然界发生的进化原则是:

  • 遗传学:一个人的特征通过其基因/基因组是可遗传的
  • 选择:“最适者”有更高的繁殖机会,从而传递(部分)他们的基因
  • 随机变化:基因库在世代间混合,通过重组和突变随机引入变异。

让我们详细阐述这些观点,并对自然和计算机科学进行比较:

遗传学/遗传编码(指可遗传的特征):
自然界的任何生物都是由基因组,即一组基因构建而成的。这些是一种种子,一种模式或计划,它包含了创造整个有机体所必需的所有信息。在自然界中, DNA 储存我们的基因,这些基因被转录成 RNA 并最终被解码成蛋白质——蛋白质反过来构成我们的细胞、组织、器官等。

在计算机科学中,基因组或遗传编码是指任何形式的数据(例如,位串、整数列表或其他数据结构),其可以被翻译成算法或其参数(例如,神经网络拓扑和权重)。确切地说,数据是如何被遗传编码的,这对优化过程的整体性能有一些有趣的影响。

注意,编码不一定需要覆盖我们的算法或参数的所有可能的配置。事实上,我们可能真的想要一个更严格的编码,只是为了减少搜索空间。

核糖体促进 RNA 翻译成蛋白质。来源:维基百科,Bensaccount at en.wikipediaCC BY 3.0 ,通过维基共享

选择(适者繁殖):
群体中的任何一个个体都有一定的繁殖概率,从而将自己的基因传递下去。通常情况下,它越“适合”利用其生物生态位,繁殖的概率就越高,复制其基因的概率也就越高。

在计算机科学中,任何在一项任务中表现良好的解决方案——通过一些预定义的指标来衡量——都会被选择用于繁殖,即在新的迭代/世代中对其基因组进行未来的细化。

通过抗生素选择细菌菌落。对抗生素有抗性的菌落存活下来,即具有在未来进一步复制的能力。改编自维基百科,Wykis ,公共领域,通过维基共享

随机变化/随机游走(在遗传编码空间):
通过基因组之间的遗传信息交换(又名重组)和通过基因组的突变(即随机变化),以随机游走的方式慢慢探索可能生物的整个空间。有趣的是,在自然界中,随机行走的可见“步骤”——即生物的表型和“形状”内的变化——通常非常小和/或模块化。

在计算机科学中,我们还利用重组和突变来丰富遗传可能性,并随机探索可能的解决方案。但是因为我们是工程师,我们也可以构建更多微调(即更少随机)的方法来修改基因组,作为一种使这种人工进化更有效的方法。理想情况下,我们可以创造足够的多样性来偶然找到更好的解决方案,但不要太多以至于从一代到下一代失去好的解决方案。

基因型中的一个点被映射到表型中的一个点。在基因型中引入一个小的随机变化,导致表型的变化。基因型和表型空间大小不同,将可能的表型数量减少到可通过基因型表达的数量,即减少搜索空间。作者提供的图片

旁注: 这意味着进化通常不会导致“有意义”的个体,即具有特定目的的特定特征的个体。事实上,我们有一些完全没有意义的特征:例如,喉返神经连接我们的大脑和我们的喉咙(喉)区域——然而,为了做到这一点,它深入下方,围绕主动脉(靠近我们的心脏)弯曲,然后再次返回。这显然是低效的,人们已经制作了关于它的娱乐视频。[1]

那么,为什么会发生这些“进化错误”呢?要回答这个问题,我们需要从自下而上的角度来看待进化。从分子的角度来看,进化只是拥有允许复制的分子结构的副作用。鉴于这一过程容易出错(例如,由于突变),它最终将创造出复制概率更高的分子。因此,它们可以更好地“竞争”必要的资源,降低其他分子复制的可能性。经过大量的这些随机变化(以及一些不那么随机的变化,如通过重组),一些分子最终创造出非常有效和有弹性的机器,帮助它们进行复制——有机体,如你和我。

这些机器之所以存在设计缺陷,就像喉返神经一样,只是因为修复它需要惊人数量的突变,并且所有中间步骤都是致命的(因为它环绕在主动脉周围)。这意味着几代人的渐进修复是不可能的,从一代人到下一代人的修复是不可思议的。更不用说实际的修复可能不会显著提高个体遗传密码的复制概率,这意味着它可能会再次丢失。

分子观点提供的另一个有趣的方面是,系统可以被游戏化: 例如,病毒 在复制时非常快速和有弹性,即使它们仅由包裹在简单包装中的相对较少的 DNA 或 RNA 组成。它们通过侵入生物体并将其基因组插入宿主细胞中来做到这一点,因此它们被迫产生病毒的副本。

通常,我们所说的分子是 RNA 和 DNA,我们的遗传密码。然而,除了这些,还有其他更奇特的例子存在——例如,参见 朊病毒 ,它们是奇怪折叠的蛋白质。它们甚至不在病毒的水平上——它们不拥有 DNA 或 RNA,而是奇怪的折叠迫使类似结构的蛋白质也经历这种奇怪的折叠,在生物体内产生雪球效应。即使这看起来不合理的简单,朊病毒可以从一个宿主转移到另一个宿主,并可以发生突变,为进化提供了基础。

从这个角度来看进化意味着我们存在的真正“原因”是一个相当复杂的复制机制,一个我们基因的容器。我知道这看起来很平淡,但这基本上是它的核心。它没有回答任何伟大的哲学问题,但如果没有这种盲目的优化过程,我们周围的世界会看起来更加多岩石和死亡。

如果你对进化作为一种优化机制还有什么其他有趣的含义感到好奇,请查看例如[2]&【3】。

神经进化

那么,有了对进化的这种理解,我们如何从那里得到一个神经网络结构呢?

在文章的开头,我承诺了一项技术,这项技术有可能创造出类似大脑的智能水平。这当然很容易断言,因为进化提供了我们的大脑——所以,诺言实现了!但是,生物进化花了相当长的时间——只有几十亿年,或者至少几百万年,如果你去掉非常早期的阶段——我想,这比我们能投资的多一点。

所以我们不会从分子,到细胞,到组织,器官和有机体,而是走捷径。我们将创造没有身体支持的大脑。嗯,算是吧:我们将创建一个小型测试网络,作为这里的一个替代例子。

这是可能的,因为我们已经有了一个关于如何在大脑的微观尺度上简化信息处理的工作模型:人工神经网络。如你所知,这个模型中的细胞(神经元)具有加权的输入信号,这些信号相加后被泵入一个激活函数,返回该神经元的激活信号,该信号又被传递给其他神经元。

在监督学习中,我们希望调整神经元的权重,以便对于给定的输入信号,我们实现对预期输出信号的近似。传统上,人们会将网络输出与预期输出进行比较(所得差值称为损耗),然后将其用于基于梯度下降的优化。这只是意味着在网络中向后迭代,并以微小的步长调整权重,以便在下次比较网络输出和预期输出时获得稍微好一点的结果。

带有示例权重和箭头的简单神经网络解释了反向传播的过程。网络的结构是三层的。第一层有 2 个输入神经元,完全连接到第二层的 3 个隐藏神经元,完全连接到最后一层的 1 个输出神经元。作者图解。

注意:这张图和我们稍后要看的网络有意不包括偏差输入。这是为了简化案例。可以简单地添加一个偏置作为另一个(恒定的)输入神经元。

当然,这是对所发生事情的高度概括,但真正的问题隐藏在细节中。如何避免陷入局部最优?如果损失梯度不够陡,如何防止算法收敛太慢?你如何找到一个网络拓扑结构来处理这个问题而不使过度适应

如果我们要从机器学习工程师的角度来处理这个问题,我们必须:

  1. 选择网络架构(可能还有激活功能)和学习算法
  2. 构建网络架构并初始化网络参数(即连接的权重)
  3. 选择学习算法的参数,以及任何其他尚未设置的超参数
  4. 为输入和预期输出转换数据,以便它可以与网络和学习算法一起使用
  5. 通过学习算法和训练数据集迭代地学习权重,潜在地调整超参数

实际上,所有这些步骤都是特定于问题领域的,这就是我们需要有类似领域工作经验的 ML 工程师的主要原因。

但是我们很幸运!发明了很多启发式和元级优化,神经进化就是其中之一。

神经进化是指利用进化原理生成问题域调整的人工神经网络。这可能意味着生成特定的结构、权重、激活函数或这些的任意组合。

具体而言,神经进化意味着对上述网络进行基因编码,生成单个网络编码的随机群体,从中创建实际网络,评估这些网络,选择最适合的网络,并引入某些随机变化,以从中创建新群体。然后冲洗并重复,直到达到某种健康水平。

理想情况下,这个过程完全自动发生,无需预先确定任何特定于问题域的超参数或网络架构。不错吧。

**“这听起来很棒,为什么不是每个人都这样做,而不是雇佣更多的机器学习工程师?”—你可能会好奇。说实话,细节决定成败。如果做得不好,这个过程不仅会产生非常不理想的结果,还会花费更多的时间和资源。事实上,如果学习速度是本质,其他一切都不是问题,那么就没有理由使用神经进化而不是标准的反向传播。

然而,在预期输出数据难以先验定义的领域(如一般的游戏和强化学习)或最佳网络结构未知的领域,神经进化可能是一个很好的选择。现在的挑战是创建一个既能快速达到稳定状态又能以高概率和低资源需求达到全局最优的系统。由于这大部分依赖于随机选择,解决这个问题相当于解决经典的探索与开发挑战。也就是说,我引入了多少随机性(探索)来发现潜在的新的局部最优解——以及我引导或限制了多少这些随机变化来利用我已经进一步发现的局部最优解。

为了找到一个好的甜蜜点,我们有四个调整的机会。这些都依赖于上面概述的进化原则:

  1. 网络是如何被基因编码的
  2. 我们如何选择个体进行繁殖
  3. 基因在繁殖过程中是如何混合的
  4. 我们如何以及在哪里引入随机变化

让我们来看看每一个,看看为什么和如何可以调整。

影响 1:基因编码

一个给定的进化算法在优化任务上的表现受其遗传编码的强烈影响,因为这反过来影响可用的遗传算子和基因型空间中的随机游走。

让我们理解编码一个神经网络意味着什么,看看我们有什么选择。

编码神经网络

首先,让我们看一个我们将在本文其余部分使用的例子。

假设你想编码一个神经网络。您已经提出了架构,这是我们在上面的网络图中看到的经典分层架构。它有两个输入单元,三个隐藏神经元和一个输出。

现在,对这个网络进行编码的一个简单方法是将权重打包成一个邻接矩阵。在这种编码中,矩阵的行表示输入和隐藏神经元,列表示隐藏和输出神经元。对于这些之间的每个(有向)连接,矩阵中的相应条目(通过从源/目标神经元中选择的行/列)包含权重。如果从某个神经元到另一个神经元没有连接,那么条目被设置为 0

如果我们假设所有权重的值都为 1,那么从上面得到的净矩阵将如下所示:

注意:为了强调这一点,第一行是从第一个输入神经元、第二个输入神经元的第二个、第一个隐藏神经元的第三个发出的所有连接,依此类推。第一列是第一隐藏神经元的所有输入信号,第二列是第二隐藏神经元的所有输入信号,依此类推。最后一列是输出神经元的输入信号权重。这意味着第一行第一列条目 1 是草图中第一个输入神经元到第一个隐藏神经元的连接的权重— w1,1

还要注意的是,为了尽可能简单,我们在这里没有包括偏置输入。

这种网络编码是直接有用的:我们可以通过它直接向前运行,使用稍微修改的输入向量并将其乘以这个矩阵。应用激活函数并对输出层进行迭代(因为它是一个分层网络),我们可以获得输出:

该算法具有“迭代”参数,有效地指定了我们网络的深度。因为我们将所有矩阵——通常会被分割——打包成一个,一次矩阵乘法只是一次“激活”。为了激活一层又一层,我们需要另一次做乘法,把中间结果作为输入传入。(这样不一定很有效率,所以不要在生产中使用这种方式。事实上,如果网络是循环的,那就更有意义了。)

在这个例子中,我们已经有效地对草图中的网络结构及其权重进行了编码,并以一种让我们可以直接操作的方式对其进行了编码。基因型与表型一一对应。这很好,因为它允许我们简单地通过调整矩阵条目来重组和随机调整网络及其内部布线。

直接与间接编码

由于矩阵中的每个条目直接指的是一个可能连接的权重,这种编码被称为直接编码

另一种这样的直接编码可以是简单地在合适的数据结构中列出节点和连接,正如在[4]中对一般网络拓扑、权重和激活函数所做的那样。然后可以将它转换回邻接矩阵来使用它。

虽然灵活和简单,但直接编码的一个明显问题是它的大小,因此在进化随机行走中必须遍历的可能基因型的空间大小。另一个源于大小的问题是,找到可以利用、提炼和在个体之间转移的遗传模式/模块的机会相对较低。例如,首先发现如何制作几个非常相似的肢体,然后发现如何将手指放在一个肢体上(并对所有其他肢体重复这一过程)更容易。

注意:我怀疑这可能通过使用专门的重组和突变操作来部分补救,这些操作增加了模式发生的机会,如一些复杂的基因复制机制。但在我查阅的文献中,我找不到任何相关信息。

我们在自然界中观察到的模式和对称性,以及与我们基因型的复杂性相比,我们遗传密码的微小尺寸表明,使用间接编码会更聪明。即在某种发展过程中最终产生最终表型的构建计划或更短的算法。

间接和直接遗传编码映射到表型空间。间接编码的搜索空间通常比直接编码小得多。作者提供的图片

间接编码是一种更简单、压缩的规范格式。例如,我们的 DNA 编码了我们大脑的基本形状,而没有编码每个连接(它包含的数据很少)。

像这样的编码以各种微妙的方式影响着进化的方式。Stanley 等人已经在[5]中提到了这一点:“因为间接编码不直接映射到它们的表型,它们可以以不可预测的方式使搜索产生偏差”。最后,直接编码通常被精确地选择,因为间接编码使我们很难推理突变和重组的因果关系。

不管编码是直接的还是间接的,两者都会遇到的一个问题是在进化过程中有用特征的丢失。这个问题不能仅仅通过编码来解决,但是可以使编码对这种特征回归更有弹性。例如,在 NEAT [5]中,Stanley 等人通过跟踪基因的历史变化和强制物种形成,即强制个体只与遗传相似的个体繁殖,部分克服了这一点。

综上所述,一个设计良好的基因编码可以更容易地找到和利用好的解决方案。它不仅可以帮助在搜索空间中更快地发现对称性和可继承的“模块”(想想四肢),还可以降低失去有用特征或引入致命突变(相对于有益突变)的风险。间接编码可以潜在地通过减少搜索空间和以有用的方式偏向搜索来帮助整个过程。

影响 2:选择繁殖

我们如何选择种群中的个体进行繁殖是下一个可能被调整的部分。只是为了说明这一点,而不是深入研究,这可以从根据他们的健康状况随机选择个人,到选择有非常具体要求的个人,比如说,只有前 10 名。

正如人们所猜测的,不同的方法对算法的收敛速度和稳定性有不同的影响,一些方面已经在 Goldberg 等人[6]的比较论文中进行了探讨。

继续我们之前的例子,假设我们从人群中选择了两个人,A 和 b。他们的基因组(即他们的基因组)分别如下所示:

影响 3:繁殖过程中基因组的重组

根据所选择的编码,基因的重组或“交换”可以用许多不同的方式来定义。每一个因素对勘探和开发都有不同的影响。

一个例子可能是这样的:取两个被选中个体的基因组。以相似基因配对的方式排列它们。随机交换基因或基因片段。这里的参数是段的长度和交换的段数。

把我们选择的基因组 AB 用这个方案重组它们可能会给我们带来以下后代:

影响 4:突变,即随机变化

同样,这里的可能性是无限的,并且在很大程度上取决于编码。突变的一种可能的实现方式是获取一个基因组,并对其进行随机改变。例如翻转比特或稍微随机增加或减少值。每个基因发生变化的可能性可以是这里的一个参数。

以重组得到的矩阵 C 为例,对其进行变异,得到如下结果:

这个新的个体成为下一代的一部分,一个新的群体,将被测试、选择和迭代。

用途和限制

如同标准人工神经网络的学习算法的超参数一样,任何变量或影响参数 1。-4.需要在实际的发展过程开始之前进行定义。然而,希望可行的参数是问题域不可知的。我们不是优化训练,而是通过预先调整这些旋钮来优化进化的速度,然后在以后重新使用完全相同的配置。

近年来,这似乎实际上已经被证明是一种可行的方法,因为优步的人工智能部门已经发表了几篇支持神经进化的论文,支持反向传播[7]。

一般来说,能够真正利用对称性和复制网络特征的解决方案,在生成接近大脑结构的架构方面有着巨大的潜力。毕竟,我们知道,人类新大脑皮层的能力源于一个简单的构建模块,一个皮层柱(或其变体),它被复制了很多很多次。[8]因此,很可能有一天,在选定的难题上训练一个不断进化的网络,会使它显示出一般智力的某些方面。毕竟,从生物学角度来说,这或多或少是我们人类如何走到现在的。

当然,所有这些一般性意味着,将会有大量的例子表明,一个非常具体的、工程化的、经过训练的网络在投入较少的资源的情况下表现得更好。这是特殊化与一般化的标准案例。举一个类似的例子,与经过训练的神经网络相比,现代编程语言中的手动编码算法在可良好定义的算术和逻辑任务中可以更加有效和防错。类似的效果也适用于工程神经网络和进化神经网络。

决定(或测试)哪一个更适合您的问题领域仍然是您的任务。

与此相关的所有完成的源代码文档、笔记本和代码也可以在 Github 上获得。请留下反馈并提出改进建议。

如果你想支持这篇和类似精彩文章的创作,你可以注册一个中级会员和/或关注我的账户

参考

[1] R. Francis,《人体内最愚蠢的神经》,Medlife 危机,https://www.youtube.com/watch?v=wzIXF6zy7hg

[2]r·道金斯,《自私的基因》(1976),牛津大学出版社

[3] E .尤德科夫斯基等著《进化的简单数学》、莱斯错、https://www.lesswrong.com/s/MH2b8NfWv22dBtrs8

[4] D. V. Vargas,J. Murata,“具有统一神经模型的谱多样性神经进化” (2019),IEEE 神经网络和学习系统汇刊

[5] K. O. Stanley,R. Miikkulainen,“通过扩充拓扑进化神经网络” (2002),进化计算 10(2):99–127

[6] D. E. Goldberg,D. Kalyanmoy,“遗传算法中使用的选择方案的比较分析” (1991),《遗传算法基础》第 1 卷:69–93 页

[7] K. O .史丹利,《迎接深度神经进化时代》 (2017),https://eng.uber.com/deep-neuroevolution/,

[8] J .霍金斯,《一千个大脑:智力新论》(2021),基础书籍

神经生成模型和艺术的未来

原文:https://towardsdatascience.com/neurogenerative-models-and-the-future-of-art-e73d457b4bb6

最新的文本到图像模型可以基于文本提示生成令人惊叹的图像,既逼真又艺术,在许多现有艺术家的风格化下。在这篇文章中,我展示了一些例子,并试图推测这对我们欣赏艺术的方式可能产生的影响。

柏林城市风景油画— 【稳定扩散】 (左),粉彩画的一男一女站在夕阳下的大海前接吻— 稳定扩散 (右)—生成 德米特里·索什尼科夫

神经生成模型

在过去的几个月里,我们看到了神经模型领域的快速发展,它可以根据文本提示生成图像,即所谓的文本到图像。第一个严肃的模型出现在 2021 年 1 月,是来自 OpenAIDALL-E 。2022 年 4 月,DALL-E 2 紧随其后,谷歌的型号 Imagen 也差不多在同一时间问世。这两个模型都没有开放供一般使用,模型权重不可用,它们的使用只能作为进行封闭测试的服务的一部分。

然而,神经生成模型最受欢迎的是最近在艺术爱好者中流行的中期。该模型针对艺术创作者,是使用专门数据集进行训练的结果。然而,Midjourney 也不是免费使用的——您只能获得大约 20 个免费的图像生成来进行实验。

黑暗的废弃城市,黄色的路灯,街上绝望的人,artstation 上的趋势——由中途生成 德米特里·索什尼科夫

然而,开放的神经生成模型正在积极开发中,其权重/代码可以在开源中获得。直到最近,最流行的方法是 VQGAN+CLIP ,在本文中描述为,它基于 OpenAI 提供的 CLIP 模型。CLIP 能够匹配文本提示与给定图片的对应程度,因此 VQGAN+CLIP 反复调整 VQGAN 生成器,以产生越来越符合提示的图像。

一个飞行员在他的飞机前面,VQGAN+CLIP。 德米特里·索什尼科夫

还有一个 DALL-E 的俄罗斯变种,叫做 ruDALL-E ,这个已经被 Sber/ SberDevicesAIRI 训练过。那些型号和重量都是自由分发的,他们用俄语作为提示语言。

猫,坐在窗前看风景——通过 ruDALL-E 纵横比 生成,由Dmitry Soshnikov生成

神经艺术社区的一个重要里程碑发生在 2022 年 8 月 21 日——名为稳定扩散的最先进的神经生成模型的代码和权重由稳定公开发布。艾

稳定扩散

在这篇文章中,我不会详细讨论稳定扩散是如何工作的。简而言之——它结合了 Imagen(在训练期间在文本解释模型中使用冻结的权重)和上一代生成模型的最佳思想,称为潜在扩散。主要思想是使用 autoencoder 将原始 512x512 图片转换到更低维的空间——所谓的潜在表示,然后使用扩散过程合成这个表示,而不是完整的图片。与对像素表示进行操作的模型相比,潜在扩散花费更少的时间和计算资源,并且给出更好的结果。在开放的 LAION 数据集上训练稳定扩散。

我有机会参加稳定扩散训练的早期阶段,这给了我一些时间来准备这个简短的概述它的能力。

计算机系青年男教师油画肖像 — VQGAN+CLIP(左)计算机系青年女教师水彩画稳定扩散(右) Dmitry Soshnikov 生成

如这个例子所示,与 VQGAN+CLIP 相比,稳定扩散给出明显更好的结果。这就是为什么我认为稳定扩散模型的公开发布是艺术/设计生态系统的重要一步,因为它赋予了艺术家非常强大的神经图像生成的新能力。

正如《稳定扩散》的创作者所说,最终我们有了一个模型,这个模型让知道很多关于图像的事情。例如,模特知道大多数名人的长相(除了我最喜欢的弗朗卡·波滕特):

从左至右:哈里森·福特的油画肖像、斯嘉丽·约翰逊的水彩肖像、杰瑞德·莱托的蜡笔画—德米特里·索什尼科夫生成

这个例子还显示了模型可以使用不同的技术创建图像:油画、粉彩、水彩或铅笔素描。但这还不是全部——它还可以模仿特定艺术家的风格:

不同艺术家风格的图片,由提示生成大城市风景由<作者姓名>,由生成德米特里·索什尼科夫

此外,该网络知道世界各国首都的著名景点:

伦敦,油画由 稳定扩散 (左),西雅图,铅笔素描由 稳定扩散 (中),水彩由 稳定扩散 (右)——生成由 德米特里·索什尼科夫

上面的例子展示了网络能够记忆图像并且图像与不同的风格/技术相结合。我们也可以试着看看网络是如何想象抽象概念的,比如爱情孤独或者绝望:

抽象的爱情概念,立体主义(左)抽象的孤独概念(中)分离,印象派风格(右)——生成经由https://github.com/CompVis/stable-diffusion**稳定扩散由* 德米特里·索什尼科夫*

怎么试?

尝试稳定扩散最简单的方法是使用 Dream Studio ,这是 Stability.AI 最近发布的一款工具,目前它只包括通过文本提示生成,但很快他们承诺会添加额外的功能,如修复或图像到图像生成。它给你有限数量的图像生成来玩。

类似的神经图像生成工具还有夜咖。除了生成,你还可以成为社区的一部分,分享你的成果,参与讨论。

对于那些能够使用 Python 和 Pytorch 的人,请随意使用 Colab 笔记本

哪里/如何使用神经生成

一旦你得到了他非常强大的神经生成工具,并且有机会玩它,你很快就会开始问这个问题——那又怎样?你如何使用这个工具?

当然,你现在可以生成许多美丽的绘画图像,将它们打印在画布上,并开始在当地的跳蚤市场上出售。或者你可以声称自己是这些作品的作者(或者至少是与艾的合作作者),并开始安排自己的展览,或者参加第三方展览。但是有那么简单吗?

先把你能不能把 AI 创作称之为“艺术”,卖“廉价”图片能赚多少钱的问题放一边。让我们想想神经生成的其他生产性用途:

  • 随机产生的灵感。有时,在寻找一个新的想法或构图时,艺术家随机地在画布上泼洒颜料,寻找灵感,或者他们使用湿水彩颜料,试图适应颜色在整体画面中的随机扩散。这种随机性正是神经网络所做的——它随机地将以前在训练数据集中看到的图像模式放在一起。在许多情况下,神经网络可以给我们意想不到的结果——就像在下面的例子中,当我们要求它生成一个三角形嘴巴的男人时,却得到了一个嘴巴周围有红色东西的有趣图像。这个意想不到的形象可以给艺术家一个新的思考方向。

**

一个嘴巴张得大大的人的吓人照片(左),一个嘴巴张得像等边三角形的人的吓人照片(右)——生成通过 稳定扩散 通过 德米特里·索什尼科夫

  • 绘制人工制品。在某些情况下,一件艺术品的主要价值在于想法和信息,而不在于实际的实现。像这样的图像起着次要的作用,我们可以利用人工智能来生成这些图像。例如,我们可以使用人工智能来生成营销横幅的图像,或者为一些国际活动生成不同国籍的人的图像。

**

可以用作横幅的动漫作品——通过稳定传播Dmitry Soshnikov 生成

  • 获得灵感的方法之一是观看不同人制作的神经生成流。这是一个在短时间内看到许多非常不同的想法的好方法,可以拓宽我们的思维视野,帮助我们产生新的想法,然后用传统的方式表达出来。这个我以后再讲。
  • 除了传统的数字工具和技术之外,使用神经生成作为起点,或者作为实现最终人工制品的步骤之一。这让我们能够更好地控制如何表达最初的想法,并使我们真正成为人工智能产品的共同作者,允许融合人工智能和我们自己的想法。
  • 前一点的一个变化是顺序使用神经生成,使用诸如修补(使用神经生成重新生成或填充图像内的区域的能力)、图像到图像(当我们将文本提示与初始草图一起使用时,显示整体构图,并要求网络产生详细的图像),或通过潜在空间搜索来找到最初创建的图像的变化。
  • 用于教育的神经生成。由于生成网络“知道”知名艺术家的风格和不同的艺术技巧,我们可以通过动态生成样本图像来在艺术教育中使用这一点。比如我们可以拿一个日常用品(比如勺子),让网络用不同艺术家的风格画出来。这使得教育更具互动性,并可能对学习者产生更多情感影响,因为他们自己成为创造过程的一部分。

我相信你可以想到文本到图像的神经生成的许多其他用途!欢迎在评论中提出建议!

顺便说一下,我们仍然需要解决的一个有趣的问题是这些过程产生的人工制品的版权问题。拥有封闭网络的公司(如 DALL-E/OpenAI)通常声称对网络产生的结果拥有所有权,而稳定传播则采用更加开放的creativml open rail M 许可证,该许可证赋予艺术家对创作内容的所有权(和责任),同时明确禁止某些不道德的使用场景。

快速工程的艺术

文本到图像的生成看起来似乎很简单,但实际上,要得到想要的结果,仅仅写一个简短的提示是不够的,比如“ a girl ”。下面的例子显示了我们如何使提示更加精细,以达到我们想要的结果:

一个女孩(左)一个漂亮女孩的肖像(中)一个金发碧眼的漂亮欧洲女孩,虚幻引擎,在 Artstation 上流行(右)——生成经由 稳定扩散 德米特里·索什尼科夫

网络看到了很多图像,知道了很多,但有时很难达到一些特定的结果,因为网络的想象力是有限的。换句话说,它从未在训练数据集中见过这样的组合,并且它很难将几个高级概念组合在一起。一个很好的例子就是我们在上面看到的一个长着三角嘴的男人的形象。

为了达到接近你脑海中想象的图片的效果,你可能需要几个文本提示,使用一些特殊的技巧来微调图像。以下是其中的一些技巧:

  • 明确提及技法 : 比尔·盖茨水彩肖像,或比尔·盖茨布面油画**
  • 在提示中提供具体艺术家的名字:比尔·盖茨肖像,毕加索* — 稳定扩散艺术家指南*
  • 提供日期/时间段 : 比尔·盖茨观看 IBM PC 个人电脑示意图,老照片,20 世纪 60 年代
  • 使用一些修改器 : 棕褐色哑光高细节等。稳定扩散调节剂指南
  • 使用一些可能出现在训练数据集中的额外修改器,如虚幻引擎、artstation 上的趋势8k 等。

除了提示本身,您还可以控制其他一些生成参数:

  • 图像尺寸。稳定扩散是在 512x512 图像上训练的,但您可以在生成过程中指定不同的图像大小,主要限制是您的 GPU 的视频内存。然而,请记住,网络试图用一些有意义的细节填充图像的所有区域。例如,如果您在宽水平图像中要求一个肖像,结果将可能包含几个人的肖像。克服这一点的一个方法是明确地在空白空间中作为网络来画一些东西。
  • 扩散的步骤数。少量的步骤导致图像不太详细,有时看起来很好。然而,一般来说,步骤越多越好。例题
  • 引导比例(或 cfg 比例)参数控制文本提示的权重,即指定生成的图像与我们的提示的精确对应程度。较小的数字给网络更多的灵活性来绘制它想要的,这通常会导致更一致的图像。较高的数字有时会导致混乱的结果,但通常更适合提示。

AI 能有创造力吗?

大约两年前,我在我关于人工智能和艺术的博客文章中思考过同样的问题。从那时起,创造人工智能艺术的过程被大大简化了。在 GANs 时代,艺术家必须收集根据一些标准选择的图像数据集,训练 GAN 模型(这不仅需要时间,而且通常是非常棘手的过程),然后从 100 多幅生成的图像中挑选一些好的人工制品。现在几乎任何稳定扩散或者中途生成的图像看起来都比较好。

黑暗城市之夜(左),蒸汽朋克小猫#13(中),神经达利的年轻女人(右)——由德米特里·索什尼科夫,经由稳定扩散

我们是否应该称为艺术是从我们刚刚想到的一个想法在几分钟内创造出来的东西,这确实是有疑问的。我喜欢采用一个原则,而艺术是人们愿意为付费的东西——然而,根据这个原则,我们不太可能看到对现在充斥互联网的成千上万的中途/稳定扩散生成的图像的大量需求。

合适的人工智能艺术总是由人类和神经网络共同努力创造出来的。随着 AI 变得更有能力,一个人类艺术家需要思考他/她能带来的 附加值 。没有我们增加的足够的价值——我们无法感到自豪或真正拥有工作,我们也不会有成就感。

思考我们带来的价值的另一面,是思考 AI 能给我们的艺术过程带来的价值。但是我们需要考虑平衡。

当使用文本到图像模型时,即使我们不对图像应用任何后处理,人类艺术家的重要作用如下:

  • 提出最初的想法。创作一件艺术品是为了向世界表达某种观点,并对观众产生情感上的影响,这种影响会放大这种观点。只有渴望创造和自我表达的人才能知道他/她想对世界说什么。神经网络没有自我表达的欲望

**

来自 Dmitry Soshnikov 的“人与机器人”集合,通过稳定扩散生成

  • 提示工程我们已经讲过了。
  • 根据只有人类才有的美丽或情感效果的标准来挑选结果。尽管有人试图收集能引起“共鸣”且情感强烈的图像数据集,但目前尚不可行的是,将美的概念形式化,或建立一个分类器来确定好的艺术与坏的艺术。
  • 想想对作品的定位以及如何让它为世人所知,或者说如何“推销”它(这个词的广义)。一个除了你没有人会看到的生成的图像对人类来说没有什么价值。除非在你死后人们会发现它,它会让你出名——但我对此不抱太大希望。

正如我已经强调的,随机的文本到图像的生成并不太有趣,在许多情况下,我们会看到艺术家使用神经生成来实现他们的想法,使用像修补,图像到图像,生成视频(穿越时间就是一个很好的例子),或者探索潜在的空间并看到相应的图像转换。

在过去的某个时间点,摄影取代了经典的肖像和风景画,迫使艺术家们寻找更具创造性的方式来可视化我们的现实。类似地,神经艺术的产生会导致更有趣的想法和技术,并将提高艺术家的创造力。

体验神经艺术的新方式

我们经常来博物馆和画廊体验传统艺术。在某种程度上,博物馆和画廊展示的是经过社区过滤的策划内容。人工智能艺术的数字本质允许不同形式的策展。此外,由于生成人工智能图像比制作传统绘画要“便宜”得多,我们面临的图像量要大得多。

最初,稳定扩散和中途等生成模型是通过 Discord 社区向测试者开放的,在那里你可以使用机器人来进行生成。稳定扩散社区有 40 多个频道,每个频道都是从某人的文本提示中产生的几乎持续不断的图像流。网络创建者使用这些反馈在大量不同的文本提示上测试模型。

神经生成流

作为这个社区的成员之一,我注意到观看这些源源不断的不同图像非常鼓舞人心!在博物馆里,我们看到的作品数量相对较少,每件作品背后都有一位伟大的艺术家和一个故事,与此不同,在神经生成流中,我们看到大量未经剪辑的图像,制作成本很低。然而,这并没有让这些艺术品变得无趣——它们中的许多都包含着原创的想法,或者至少是的暗示,让我们的大脑迸发出自己的想法和情感。我们只需要调整我们的“艺术消费习惯”来处理这种源源不断的需求。

你可以把参观博物馆比作去餐馆,在那里你可以享用精心挑选的菜肴和葡萄酒。消费神经生成流就像一个自助餐,你可以吃到各种国际菜肴,有 20 种碳酸饮料。

稳定扩散不协调中的神经生成通道是混乱的,但我们也可以想象按主题分离这些通道,这样一个流中的所有图像都有一些主题约束。例如,我们可以让一个流显示猫和小猫,另一个流显示遥远星球的风景。

艺术对象中神经生成的整合

使用神经生成的方法之一是将其构建到一个更大的艺术对象中,其中最终结果将由自动构造的文本提示生成。作为一个例子,我们可以想象一个系统,它将在社交网络中获取一个人的个人资料,以某种方式从他/她的帖子中提取他的口头画像,然后使用稳定的扩散来产生一个社交网络画像

神经生成党

如果我们有机会通过某种互动过程参与到艺术创作的过程中,我们会更加情绪化,这不是什么秘密。此外,让我们考虑到艺术的重要作用是鼓励我们思考,甚至更好——进行对话并得出一些结论。

神经生成党,布面油画——由德米特里·索什尼科夫通过稳定扩散生成

拥有这种互动和对话的一个很好的方式是组织一个神经生成聚会——这是我们最近和几个朋友尝试的一种形式,现在正在寻求扩大规模。一个想法就是召集一群人(最好是线下,有一些酒和开胃菜,但线上也是一个选项),一起做神经生成。

以下是一些让它更有条理的规则:

  • 为聚会确定一个主题,例如孤独、爱情或生命的意义。这将限制我们的探索,但也使它更有意义,围绕一个重要的想法,我们想探索。
  • 我们可以用一个非常简单的提示来开始生成,然后根据我们到底想在图片中看到什么来使它变得更详细。例如,我们可以从“孤独”这个词开始,然后到达“两个老人互相看着对方”这样的东西。我们可以从网络生成的图像、与观众的对话和小组讨论中获得更多的含义和想法。
  • 我们也可以要求观众说出他们对正在探索的概念的联想,以及哪些风格和艺术家与之相关联。然后,我们可以立即进行实验,并获得视觉确认。
  • 神经生成的所有步骤都应该被记录/保存,并且作为聚会的结果,我们可以发布最好的结果——在博客帖子中,或者一篇文章中,或者甚至考虑参加一个展览。

神经生成党,铅笔素描——由 Dmitry Soshnikov 通过稳定扩散生成

为什么我认为这种形式很棒:

  • 神经生成作为统一的理由来谈论一个重要的话题。即使我们没有创造任何伟大的人工制品(这不太可能)——我们也会喜欢彼此交谈。这个演讲是有条理的,也是一个互相了解的好方法。
  • 在晚会期间,我们将看到许多新创作的艺术作品,可以比作集体参观博物馆
  • 图像将在那里被创建,来自我们想出的提示——这使得每个参与者都是共同创建者,并从快速的结果中给他带来创造力和多巴胺的快乐。

要举办一场成功的派对,以下角色非常重要:

  • 提示大师(Prompt Master)——对神经生成有一定经验的人,可以调整文本提示。在大多数情况下,他也是负责技术方面的人——如何运行 Jupyter 笔记本或神经生成工具,如何保存图像等。
  • 艺术专家,他知道不同的艺术风格和技巧,还能指导创作过程,估计结果有多有价值。
  • 所有其他参与者不需要特殊技能,可以根据自己的喜好扮演更主动或更被动的角色。然而,鼓励人们积极参与是一个好主意——为此你可能还需要一个主持人

外卖食品

我们生活在一个非常有趣的时代,当神经网络变得有创造力时(真的吗?),并可以自动化视觉艺术家或故事作家越来越多的工作。我真诚地希望它能引领我们人类创造出新的艺术形式和风格,并扩展和丰富我们对世界的感知。为了实现这一点,重要的是我们不仅要仔细观看这一场景,而且要积极参与和实验!

原载于 2022 年 8 月 22 日 https://soshnikov.com*https://soshnikov.com/scienceart/neural-generative-models-and-future-of-art/。***

永远不要忘记。在 Python 代码中复制()

原文:https://towardsdatascience.com/never-forget-copy-in-your-python-codes-9826bba31f78

如果您记得使用,它将为您节省大量调试时间。始终在 python 代码中复制()

布鲁斯·马斯在 Unsplash 上的照片

周六下午,我本应该开车带孩子去购物中心,但我却被困在了我的家庭办公室的办公桌前,调试着一段简单的代码,这段代码从上周五开始就一直困扰着我。

严格地说,这不是“调试”,而是为了解决我的代码的一些奇怪的性能。我正在以迭代的方式实现一个简单的无监督模型,并试图根据我设计的特定性能指标记录“最佳”模型。

让我惊讶的是,我保存的“最佳模型”并不是最好的,并且一直记录着我迭代中的最后一个模型。下面是我的实现的 sudo 代码的简化版本:

iter = 0
while iter < 10: iter += 1    
    # initiate the performance and empty data for recording
    best_model = None
    best_performance = 0
    best_data = None # modeling
    M = sklearn.unsupervised(random_seed= seed_dict[iter])
    M.fit(X)
    X2.predict = M.predict(X2)
    current_performance = sum(X2.predict == 1)
    print("my current performance is %i" %(current_performance)) # compare to the recorded best model
    if current_performance > best_performance:
        best_model = M
        best_data = X2
        best_performance = current_performance
        print("Ah ha!! My best model is updated with performance %i !" %(best_performance))

如果你是一个有经验的程序员,你可能已经发现了代码的问题。如果您还没有,请跟随我学习下面的示例输出代码,

# Python
print("My best performance is %i based on best_data!" %(sum(best_data.predict == 1)))
print("My best performance is %i based on best_performance!" %(best_performance))my current performance is 300
Ah ha!! My best model is updated with performance 300 !
my current performance is 400
Ah ha!! My best model is updated with performance 400 !
my current performance is 200
my current performance is 270
my current performance is 360
my current performance is 870
Ah ha!! My best model is updated with performance 870 !
my current performance is 1224
Ah ha!! My best model is updated with performance 1224 !
my current performance is 1687
Ah ha!! My best model is updated with performance 1687 !
my current performance is 350
my current performance is 128My best performance is 128 based on best_data!
My best performance is 1687 based on best_performance!

看到问题了吗?我根据记录的“最佳数据”报告的“最佳性能”是来自上一次迭代。然而,基于变量“最佳性能”,真正的“最佳性能”应该是 1,687。

我的原始代码在建模部分更复杂,所以我在建模部分花了很多时间,甚至没有考虑记录最佳模型和数据的代码。

经过几轮编辑(那花费了我一个周五晚上外加一个周六下午!),我终于注意到下面这行代码,

best_data = X2

应该是,

best_data = X2.copy()

原因是 Python 中的赋值语句不复制对象,它们在目标和对象之间创建绑定。因此,当我键入 best_data = X2 时,我实际上在两个变量之间建立了一个链接,每次 X2 发生变化,best_data 也会发生变化!

根据 Python 的文档,“对于可变的或包含可变项的集合,有时需要一个副本,这样可以在不改变另一个副本的情况下改变一个副本。”

在编码中很容易忽略这一点,尤其是当你把主要精力放在建模部分的时候。

我不想让你像我一样在这个愚蠢的错误上浪费宝贵的周六下午,所以我把它写成了一个简短的帖子。希望有帮助!

我要回到我的家人身边!

杰德·维尔霍Unsplash 上拍摄的照片

永远不要担心优化。使用无代码 Pandas 将数 GB 的表格数据处理速度提高 25 倍

原文:https://towardsdatascience.com/never-worry-about-optimization-process-gbs-of-tabular-data-25x-faster-with-no-code-pandas-e85ede4c37d5

没有更多的运行时和内存优化,让我们直接开始工作

自由股票Unsplash 上的照片

动机

Pandas 使分析表格数据集的任务变得轻而易举。圆滑的 API 设计提供了广泛的功能,几乎涵盖了所有的表格数据用例。

然而,只有当有人向规模化过渡时,他们才会体验到熊猫的深远局限性。我以前在下面的博客中谈到过这个问题:

</5-things-i-wish-the-pandas-library-could-do-e9017c127779>

总之,panda 几乎所有的限制都源于它的单核计算框架

换句话说,即使您的 CPU 有多个可用(或空闲)的内核,Pandas 也总是依赖于单个内核,这抑制了它的性能。

此外,熊猫的数据帧天生就很大。Pandas 从不优化 DataFrame 的列的数据类型。因此,如果您必须处理大型数据集(大小为 GBs):

  1. 您应该一次只在内存中加载一个数据块,处理它,丢弃它并加载下一个数据块,或者,
  2. 如果出于某种原因,您需要整个数据集,那么您应该调整数据类型以适应内存。

此外,在许多情况下,数据通常被分割到多个 CSV 文件中。因此,如果您想一起分析整个数据,必须合并多个 CSV 文件的数据,然后对其进行处理。

我在之前的一篇博客中讨论了更多的方法:

不幸的是,如果您需要内存中的整个数据集,但是尽管优化了内存使用,您还是不能加载它,那么您就不走运了。

即使你成功地加载了数据集,也不能保证你能够处理它。

由于中间计算也需要内存,这可能最终导致以下情况:

内核死亡(图片由作者提供)

回到起点。

我相信你们很多人都去过那里。我知道这令人沮丧,特别是当你的目标是进行快速分析时,比如绘制一些条形图,估计组统计数据等。

此外,老实说,除非您正在构建一个旨在为最终用户服务的数据科学管道,否则数据科学家为什么要花费数小时来优化内存呢?在我看来,这抑制了他们的生产力。

解决办法

我希望在这一点上,你明白这个问题是真实的。

为此,让我们讨论一个潜在的解决方案——giga sheet,在我看来,它是熊猫的革命性替代方案,不仅适用于大型数据集,也适用于小型数据集。

在下面的博客中,我详细介绍了如何使用 Gigasheet 从 Pandas 过渡到“无代码 Pandas”。

然而,这篇博客的重点略有不同。

在这篇博客中,我将简要展示在熊猫身上处理大型数据集是多么具有挑战性,以及 Gigasheet 是如何让它变得轻而易举的。

此外,Gigasheet 这样的工具有两个最大的优势:

  1. 你不用写任何代码。
  2. 您不必担心运行时或内存优化。

太神奇了。我们开始吧🚀!

资料组

出于演示的目的,我将使用一个使用 Faker 创建的虚拟数据集。如下所示,它占用了 9gb 多一点的磁盘空间。

虚拟数据集大小(图片由作者提供)

前五行如下所示:

虚拟数据集的前五行(作者提供的 Gif)

此外,如下所示,数据集有 30 多行和 25 列。

Gigasheet Dashboard 中显示的行和列(作者图片)

熊猫大战 Gigasheet

接下来,让我们在 Pandas 中执行一些常见的操作,测量它们的运行时间,并将其与 Gigasheet 进行比较。

由于 Gigasheet 是一个无代码工具,测量运行时间有点挑战性。因此,为了比较,我将使用这里上传的屏幕记录的持续时间来近似 Gigasheet 的运行时间。

#1 过滤

过滤是在表格数据分析期间执行的常见操作。让我们过滤Country_Code列上的数据帧,并选择值为MY的所有行。

熊猫

Gigasheet

选择“应用”后,计时器立即启动(作者 Gif)

在 Gigasheet 中过滤只花了两秒多一点。与耗时近 25 秒的熊猫相比,Gigasheet 快了 92%。

熊猫 vs. Gigasheet 记分卡(图片由作者提供)

#2 分类

接下来,让我们对BBAN列上的数据帧进行升序排序。

熊猫

这里,我们可以使用sort_values()的方法。

Gigasheet

这些步骤如下所示:

选择“分拣纸张”后,计时器立即启动(作者 Gif)

在 Gigasheet 中排序需要 5 秒多一点的时间。另一方面,熊猫用了将近 2 分钟——性能提高了 96%

熊猫 vs. Gigasheet 记分卡(图片由作者提供)

#3 分组

接下来,让我们对Country列上的数据帧进行分组,并找到一些东西:

  1. 每组中的记录数。
  2. 唯一的数量Zip_Codes

熊猫

回忆我们在引言中讨论的内容:

即使你以某种方式成功加载了数据集,也不能保证你能够处理它。

→千兆页

Gigasheet 中的分组(Gif by Author)

Gigasheet 计算所需的聚合没有任何麻烦。

熊猫 vs. Gigasheet 记分卡(图片由作者提供)

然而,Gigasheet 再次成为明显的赢家。

结论

在这篇博客中,我演示了如何利用 Gigasheet 并对大型数据集执行三种典型操作。

而在熊猫身上,执行这些操作确实需要相当长的时间。然而,Gigasheet 中的相同操作效率很高。

事实上,对于 Pandas,我们从未获得分组操作的结果,因为它破坏了 Jupyter 内核。另一方面,Gigasheet 名副其实,轻松处理一切。

在结束之前,我想补充的一点是,通过这篇文章,我并不是说 Gigasheet 是(或将会是)熊猫的最终替代品。

人们可以用 Pandas 或 Excel 做更多的事情,这是 Gigasheet 所做不到的。然而,除了没有代码之外,它轻松处理大型数据集的能力使它成为这种情况下的绝对赢家。

此外,Excel 最大的问题是它的最大行数限制。这阻碍了大规模数据分析项目的工作,而 Excel 不支持这种项目。

事实上,即使您正在处理中等大小的数据集,Gigasheet 也可以派上用场。我最近写了一篇关于从熊猫到 Gigasheet 转变的详细文章,你可以在这里阅读:

一如既往感谢阅读

🚀订阅数据科学每日一剂。在这里,我分享关于数据科学的优雅技巧和诀窍,一天一个技巧。每天在你的收件箱里收到这些提示。

🧑‍💻成为数据科学专家!获取包含 450 多个熊猫、NumPy 和 SQL 问题的免费数据科学掌握工具包。

获取机器学习领域排名前 1%的研究论文、新闻、报道、推文的每周汇总。

我喜欢探索、实验和撰写关于数据科学概念和工具的文章。你可以在 LinkedIn 上和我联系。

新的深度学习工具以高精度设计新的蛋白质

原文:https://towardsdatascience.com/new-deep-learned-tool-designs-novel-proteins-with-high-accuracy-41ae2a7d23d8

蛋白质设计的新时代

贝克实验室的这款新软件设计了在潮湿实验室中实际工作的蛋白质。你也可以用它来设计你自己的蛋白质,就在网上。

这即将发生,我希望贝克实验室会是第一个报告此事的小组。但是说实话我没想到会发生的这么快:

逆转一个类似 AlphaFold 的神经网络,为其输入 3D 结构,并从中获得相应折叠的蛋白质序列。这本身并没有很好地工作,但它启发了基于机器学习的蛋白质设计的进一步策略。最终,这个被称为 ProteinMPNN 的工具问世了,科学家们现在可以用它来设计按照需要折叠(从而工作)的蛋白质。

ColabFold 甚至网络应用版本的 ProteinMPNN 已经上线供大家使用。

蛋白质结构和蛋白质设计

正如我在以前关于 AlphaFold 和蛋白质建模的文章中所述(见这里的索引),蛋白质序列决定了蛋白质如何获得 3D 结构(折叠),这反过来决定了它可以发挥什么功能,以及它的稳定性、溶解性等。(对生物学家来说:我将把固有无序蛋白质的整个宇宙放在一边。)

处理相反的问题通常是很有趣的:给定一个应该由给定的 3D 结构实现的功能(或者给定任何其他想要优化的特性,例如稳定性),我们需要什么样的蛋白质序列(或者起始序列上的什么突变)?

这个问题一般是杜撰蛋白质设计;它有几个特定目标的子问题,其中从零开始创造一个完整的蛋白质是最困难的。

到目前为止,虽然稳定现有蛋白质等子问题越来越多地通过机器学习来解决,但从头创建一个全新的蛋白质序列的问题主要通过基于物理的方法来处理。毫无疑问,该领域的领导者是西雅图华盛顿大学的贝克实验室,该实验室实际上管理着整个蛋白质设计研究所。

这个团队也是 RoseTTAFold(不如 AlphaFold 出名,但显然几乎一样准确)等蛋白质建模程序的开发者,他们很快就看到了旨在预测蛋白质结构的新机器学习技术如何被逆转,以预测哪些序列会按照预期折叠。这个问题看起来微不足道,但涉及到几个计算机工程挑战,然后是蛋白质设计活动通常会遇到的最终障碍:通过实验合成预测的蛋白质,并验证它们确实如预期那样折叠,如果它们执行预期的功能,甚至会更好。

到目前为止,贝克实验室最好的工具是 Rosetta toolbox,这是一个用于蛋白质结构预测和设计的多元宇宙工具,主要建立在基于物理的模型上。尽管在高影响力的期刊上发表了几个令人惊叹的蛋白质设计,但事实是成功率非常低:只有一小部分 Rosetta 设计真正按照预期折叠和工作。

蛋白质设计的机器学习

现在,贝克实验室创造了一种全新的工具,称为 ProteinMPNN,它建立在机器学习的基础上,从预期的结构中产生蛋白质序列。虽然许多工作已经对此进行了理论化,但 ProteinMPNN 是第一个通过实验手段证明实际产生蛋白质序列的蛋白质,其折叠几率很高。换句话说,这意味着当该小组的实验部分拿着程序产生的设计好的序列,试图在湿实验室中产生编码的蛋白质时,他们实际上得到了它们;此外,当他们解决了他们的结构,他们匹配预期的结构,在许多情况下,也带有预期的功能。

顾名思义,ProteinMPNN 是围绕一个信息传递神经网络(MPNN)建立的。本作品中使用的核心 MPNN 建立在之前的作品之上,甚至是 AlphaFold2 之前的作品!

起始网络由 3 个编码器和 3 个解码器层以及 128 个隐藏维度组成,并且使用从 CA 位置(CA 是氨基酸的中心碳原子)构建的蛋白质骨架几何特征以自回归方式从 N 到 C 末端预测蛋白质序列。这项新工作通过引入 N、C 和 O 主链原子加上一个虚拟 CB 原子的位置对此进行了改进,并改进了网络的传播方式。

ProteinMPNN 网络通过编码器模块传递 N、CA、C、O 和虚拟 CB 原子之间的距离来获得图节点和边。然后,这些特征被解码器模块转换成蛋白质序列每个位点的氨基酸概率,该解码器模块从一组所有可能的排列中随机取样氨基酸。最后,最大概率可以被铸造成精确的蛋白质序列,然后尝试在湿实验室中产生这些候选蛋白质。(通常会对一组可能的序列进行实验测试,以最大化其中一个序列工作的机会,甚至在此之前,通常会对候选设计进行深入的人类专家检查——但这超出了本文的范围和重点。)

作者根据软件使用、公开材料和自己的图纸绘制的图。

非常重要的是,当原始 MPNN 解码从 N 到 C 末端的序列时,ProteinMPNN 随机执行这一操作,并允许用户预设(和固定)某些氨基酸。通过这种方式,蛋白质序列是围绕固定部分构建的,固定部分通常包括需要固定以实现功能的区域。例如一个表位,如果人们想要设计一种蛋白质,将它展示在其表面以作为疫苗,或者甚至一种完整的蛋白质,如果人们想要设计一种将与其结合的蛋白质。

主要测试和应用

首先,通过在蛋白质数据库的数千个高分辨率结构上训练 ProteinMPNN 模型,作者发现扩展的几何描述确实有助于更好地恢复已知序列,比仅用 CA 位置表现得好得多。此外,完全训练的模型比标准的基于 Rosetta 的方法更好地恢复序列。

接下来,通过优化骨架几何结构影响氨基酸同一性的范围,作者得出结论,性能在“仅”32-48 个邻居处饱和。这意味着模型相对较小,因此运行速度非常快。事实上,正如他们报告的那样,ProteinMPNN 的运行速度比他们的 Rosetta 协议快 200 多倍,此外还能产生更好的设计。

最后,作者验证了通过 AlphaFold 2 运行设计的序列会导致设计的反向预测-这是一个独立的指示,表明该序列有很好的机会正确折叠。

应用程序

如果设计的蛋白质实际上不起作用,这一切都将不复存在。,或者至少它们按照预期折叠。正如预印本所显示的,大部分设计好的序列非常容易溶解,具有高表达水平,并且结晶良好。如此之多,以至于作者们展示了他们挽救以前用 Rosetta 尝试过的失败设计的案例。

作者还表明,ProteinMPNN 比基于 AlphaFold 2 的蛋白质序列幻觉的替代方法产生更真实的蛋白质。AlphaFold 提出的蛋白质包含太多疏水簇,导致不溶性,而 ProteinMPNN 的设计更易溶解,也更稳定,在结构确定的情况下,也非常接近设计。

此外,ProteinMPNN 的蛋白质被证明确实如设计的那样折叠,包括单体、环状同型寡聚体、四面体纳米颗粒和靶结合蛋白,后者对于生产新型疫苗、蛋白质开关和其他具有由结合介导的生物技术应用的蛋白质是必不可少的。

在我结束这篇文章时,Baker 实验室发表了第二份预印本,其中介绍了 ProteinMPNN 在设计各种对称蛋白质同源寡聚体方面的具体应用,只给出了蛋白质拷贝数和蛋白质中氨基酸数的规格。当然,实验证明蛋白质如预期折叠。

在亮点中,作者描述了具有超过 1500 个氨基酸、复杂对称性和宽(10 纳米)开口的巨型环的设计。这些例子尤其与蛋白质数据库中可用的结构有很大不同,突出表明可以创造的新蛋白质结构的丰富多样性不限于已知的。总的来说,这项工作可以为设计更复杂的基于蛋白质的纳米机器铺平道路,例如用于 DNA 传感的纳米孔、纳米马达、抗病毒纳米粒子等。

你会在最后建议的阅读材料中找到两份预印本的链接。

结束语和今天如何自己使用 ProteinMPNN

最新版的结构预测关键评估(CASP) 揭示的是,像 AlphaFold 这样的机器学习模型可以很好地预测蛋白质结构。现在,它们的逆转打开了一个新的领域:创造我们想要折叠的新蛋白质。事实上,正如这项工作的第一作者在推特上所说,ProteinMPNN 已经成为“蛋白质设计研究所的标准方法”,因为“实验成功率高,几乎适用于任何蛋白质序列设计问题”:

该工具以“快速演示”笔记本的形式提供,但很快会有更多笔记本问世:

这已经被打包成一个拥抱脸的网络应用程序(由西蒙·杜尔完成,来自 EPFL 科技 4Impact )你现在就可以用它做一个测试:

https://huggingface.co/spaces/simonduerr/ProteinMPNN

下面是一个例子,使用氨基酸概率和 10 个建议的蛋白质序列,在不到 5 秒的时间内获得结果:

尝试恢复人类泛素的快速测试结果。

阅读材料和相关资源

  • Dauparas 等人的方法的完整介绍的预印本:

https://www.biorxiv.org/content/10.1101/2022.06.03.494563v1

  • Wicky 等人的《创新蛋白质装配应用预印本》:

https://www.biorxiv.org/content/10.1101/2022.06.09.493773v1

  • 另一个最近的预印本,只是理论上的,也展示了一个对蛋白质序列和结构进行采样以适应给定基序的模型——本质上是一项设计任务:

https://arxiv.org/abs/2206.04119

(查看第一作者在推特上发布的预印本摘要

  • 华盛顿大学的贝克实验室:

  • 相关博客条目:

www.lucianoabriata.com我写作并拍摄我广泛兴趣范围内的一切事物:自然、科学、技术、编程等等。 成为媒介会员 访问其所有故事(我免费获得小额收入的平台的附属链接)和 订阅获取我的新故事 通过电子邮件 。到 咨询关于小职位 查看我的 服务页面这里 。你可以 这里联系我

可解释人工智能的新前沿

原文:https://towardsdatascience.com/new-frontiers-in-explainable-ai-af43bba18348

图像通过 Unsplash

人工智能是惊人的:它可以驾驶汽车,回答问题,将人们的脸与他们的护照照片进行匹配,击败最好的国际象棋冠军,等等……但是,你有没有想过它是如何工作的?当它出错时会发生什么?它会变得危险吗?

我们离终结者式的灾难性事件还很远,但问题是真实存在的。由于新的学习算法,特别是神经网络的发展,人工智能现在在许多任务上与人竞争,有时甚至超过人。这些算法可以发现大型数据集中的模式,并在没有任何人工干预的情况下,在提供新数据时调整它们的答案。然而,它们是基于非线性数学函数的大聚合体。由此产生的模型如此错综复杂,以至于研究人员都难以理解它们是如何达到如此惊人的表现的,更不用说证明错误的预测或意外的行为了。这可能会阻碍人工智能的进一步发展,因为模型的不透明性阻碍了研究人员找到弱点。此外,人工智能的广泛使用促使这项技术变得透明,因为错误的预测已经可以极大地影响人们的生活。欧洲议会(European Parliament)裁定,人工智能系统属于一般数据保护法规范围,必须解释其涉及敏感数据使用和分析的全自动预测。

因此,研究人员创造了一个新的人工智能分支,称为可解释人工智能(XAI),来解决这个问题。他们开发了大量的 XAI 方法来揭示和解释学习算法是如何工作的,因此人们可以理解和信任他们的结果。自 2010 年代中期以来,XAI 每年都有大量科学研究发表。尽管有大量的科学出版物,但在 XAI 领域仍然存在严重的差距。

学习算法通过各种学习算法和数据类型进行训练,从数字到文本,包括图像和视频。单一的 XAI 方法不太可能对每一种应用都产生有效而有意义的解释,因此出现了大量新颖的 XAI 解决方案。有必要将这些方法组织在一个概念框架内,这可以指导人工智能从业者为手头的问题选择合适的 XAI 方法。随着时间的推移,已经提出了许多分类系统,现在对于根据以下维度组织 XAI 方法有了普遍的共识:

  1. 解释的范围。全局解释试图使模型从整体上变得透明和易于理解。局部解释侧重于解释模型的每个预测。
  2. XAI 方法产生解释的阶段。事前方法在训练之前修改模型,使其自然可理解,同时仍然达到最佳精度。事后方法保持一个经过训练的模型不变,并使用外部解释器来理解它的功能。
  3. 问题类型是要用 AI 来解决的,包括分类,回归或者聚类。
  4. 输入数据(数字/分类、图像、文本或时间序列)在构建模型以及 XAI 方法中起着重要作用。

我最近提议增加第五维度,考虑由 XAI 方法产生的解释格式[1]。与输入数据类似,不同的应用需要不同的解释。到目前为止,已经测试过的格式有数字格式、规则格式、文本格式、可视格式或前四种格式的混合(见图 1)。

图 1:XAI 方法的分级系统分类[1]

研究人员将这些解释应用于各种不同的环境中,得出了不同的结果。很难说哪种解释格式最有效,最容易理解。对于什么时候一种解释比另一种更好,并没有普遍的共识。学者们已经确定了许多可以影响解释质量的概念。我把这些概念列了一个清单[2],作为选择最相关概念的第一步,以便为人类产生一个有意义的解释。按照 XAI 方法,科学界不太可能像人们以不同的方式思考和推理那样开发出一个万能的解决方案。然而,确定哪些解释格式在某些情况下更好地工作将代表着在使人工智能透明和用户友好方面前进了一大步。还需要对人类参与者进行更多的研究,以收集他们对各种解释的意见,并理解最容易理解的解释。研究人员正在努力弥合 XAI 的所有这些差距,让人工智能对人类更加友好。我确信,在不久的将来,我们将会看到非常有趣的突破。然而,让实践者和最终用户参与讨论可能更有趣,因为他们的反馈可以帮助研究人员找到最佳解决方案。可解释性是一个如此广泛的主题,需要尽可能多的社区做出贡献。

参考文献

[1] Vilone,g .,和 Longo,l .,通过其输出格式对可解释的人工智能方法进行分类(2021),机器学习和知识提取3 (3),615–661。

[2] Vilone,g .,和 Longo,l .,《可解释人工智能的可解释性概念和评估方法》(2021),信息融合76 ,89–106。

约束波束搜索🤗变形金刚(电影名)

原文:https://towardsdatascience.com/new-hugging-face-feature-constrained-beam-search-with-transformers-7ebcfc2d70e9

一个新的拥抱脸特性允许你定制和引导你的语言模型输出(比如在输出中强制一个特定的序列)。

照片由 Priscilla Du Preez 在 Unsplash 上拍摄|一个新的“拥抱”面部特征!

抱脸变形金刚有了新功能!它被称为 约束光束搜索 ,它允许我们引导文本生成过程,这在以前是完全独立于模型的。

介绍

有时我们确切地知道在文本生成输出中我们想要什么。

例如,在神经机器翻译任务中,我们可能通过字典查找知道哪些单词必须包含在最终翻译中。有时,由于特定的上下文,对于语言模型来说几乎同样可能的生成输出对于最终用户来说可能并不同样理想。

这两种情况都可以通过允许用户告诉模型哪些单词必须包含在最终输出中来解决。新的约束波束搜索特性和新的model.generate()函数的force_words_ids参数允许我们这样做!

示例 1:强制单词

假设我们试图将"How old are you?"翻译成德语。

"Wie alt bist du?"是你在非正式场合说的话,"Wie alt sind Sie?"是你在正式场合说的话。

根据上下文的不同,我们可能希望一种形式比另一种形式更正式,但是我们如何告诉模型呢?

传统波束搜索

下面是我们如何在传统光束搜索设置中进行文本翻译。

让我们首先安装变形金刚库:

!pip install -q git+https://github.com/huggingface/transformers.git

下面是教科书 huggingface 代码,用于使用文本生成来完成像 NMT 这样的任务,它是通过传统的波束搜索实现的:

from transformers import AutoTokenizer, AutoModelForSeq2SeqLMtokenizer = AutoTokenizer.from_pretrained("t5-base")
model = AutoModelForSeq2SeqLM.from_pretrained("t5-base")encoder_input_str = "translate English to German: How old are you?"input_ids = tokenizer(encoder_input_str, return_tensors="pt").input_idsoutputs = model.generate(
    input_ids,
    num_beams=10,
    num_return_sequences=1,
    no_repeat_ngram_size=1,
    remove_invalid_values=True,
)print("Output:\n" + 100 * '-')
print(tokenizer.decode(outputs[0], skip_special_tokens=True))Output:
--------------------------------------------------------------------
Wie alt bist du?

使用约束波束搜索

但是如果我们知道我们想要一个正式的输出,而不是非正式的输出呢?如果我们从先前的知识中知道这一代人必须包括什么,并且我们可以将它注入到这一代人中,会怎么样?

下面是使用force_words_ids关键字参数到model.generate()的可能结果:

tokenizer = AutoTokenizer.from_pretrained("t5-base")
model = AutoModelForSeq2SeqLM.from_pretrained("t5-base")encoder_input_str = "translate English to German: How old are you?"force_words = ["Sie"]input_ids = tokenizer(encoder_input_str, return_tensors="pt").input_ids
force_words_ids = tokenizer(force_words, add_special_tokens=False).input_idsoutputs = model.generate(
    input_ids,
    force_words_ids=force_words_ids,
    num_beams=5,
    num_return_sequences=1,
    no_repeat_ngram_size=1,
    remove_invalid_values=True,
)print("Output:\n" + 100 * '-')
print(tokenizer.decode(outputs[0], skip_special_tokens=True))Output:
--------------------------------------------------------------------
Wie alt sind Sie?

如您所见,我们能够利用关于我们期望输出的先验知识来引导这一代人。以前,我们必须生成一堆可能的输出,然后过滤出符合我们要求的输出。现在我们可以在生成阶段做到这一点。

示例 2:析取约束

我们上面提到了一个用例,其中我们知道我们想要在最终输出中包含哪些单词。这方面的一个例子可能是在神经机器翻译期间使用字典查找。

但是,如果我们不知道使用哪种单词形式,而我们希望像["raining", "rained", "rains", ...]这样的输出同样可能,那该怎么办呢?从更一般的意义上来说,总会有这样的情况,我们不希望一字不差地一个字母一个字母地精确表达,也可以考虑其他相关的可能性。

允许这种行为的约束是析取约束,它允许用户输入一个单词列表,其目的是指导生成,使得最终输出必须只包含单词列表中的至少一个

下面是一个混合使用上述两种约束的示例:

from transformers import GPT2LMHeadModel, GPT2Tokenizermodel = GPT2LMHeadModel.from_pretrained("gpt2")
tokenizer = GPT2Tokenizer.from_pretrained("gpt2")force_word = "scared"
force_flexible = ["scream", "screams", "screaming", "screamed"]force_words_ids = [
    tokenizer([force_word], add_prefix_space=True, add_special_tokens=False).input_ids,
    tokenizer(force_flexible, add_prefix_space=True, add_special_tokens=False).input_ids,
]starting_text = ["The soldiers", "The child"]input_ids = tokenizer(starting_text, return_tensors="pt").input_idsoutputs = model.generate(
    input_ids,
    force_words_ids=force_words_ids,
    num_beams=10,
    num_return_sequences=1,
    no_repeat_ngram_size=1,
    remove_invalid_values=True,
)print("Output:\n" + 100 * '-')
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
print(tokenizer.decode(outputs[1], skip_special_tokens=True))Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation.Output:
--------------------------------------------------------------------
The soldiers, who were all scared and screaming at each other as they tried to get out of theThe child was taken to a local hospital where she screamed and scared for her life, police said.

正如你所看到的,第一个输出使用了"screaming",第二个输出使用了"screamed",两个输出都一字不差地使用了"scared"。从["screaming", "screamed", ...]中选择的列表不一定是单词形式;这可以满足我们只需要一个单词列表的任何用例。

为什么很难

但是,这其实是一个非常不小的问题。这是因为任务要求我们在最终输出中的某个地方强制生成某些子序列,在生成过程中的某个点。**

问题是波束搜索逐令牌生成序列。这就产生了以下问题:**

作者图片

如果您有不同需求的多个约束,该怎么办呢?如果你想强制短语P1 也是短语P2呢?如果您希望模型在这两个短语之间进行选择,该怎么办?如果我们想强制使用短语P1,并且只强制使用短语列表[P21, P22, P23]中的一个短语,该怎么办?

但是我们已经看到,上面的代码示例演示了上述所有情况的可能性,不管它们看起来有多么做作。接下来的部分将解释这一切是如何工作的。

传统波束搜索

以下是传统波束搜索的一个例子,摘自之前的博文:

图片来自传统波束搜索上的拥抱脸贴子

与贪婪搜索不同,波束搜索通过保持一个更长的假设列表来工作。在上图中,我们在生成的每个可能步骤中显示了三个下一个可能的令牌。

num_beams=3的情况下,这里有另一种方式来看上面例子的波束搜索的第一步:

作者图片

波束搜索将允许进一步考虑"The nice""The car"中的,而不是像贪婪搜索那样只选择"The dog"

在下一步中,我们考虑我们在上一步中创建的三个分支的下一个可能的令牌。

作者图片

尽管我们最终认为num_beams输出多得多,但在这一步结束时,我们将它们减少到num_beams。我们不能一直分支下去,那么我们必须跟踪的beams的数量将会变得难以控制的长(10 步之后的 10 个波束将会是 100 亿个波束!).

对于生成的其余部分,我们重复上述步骤,直到满足结束标准,例如生成<eos>令牌或到达max_length。分支、排序、减少和重复。

约束波束搜索

约束波束搜索试图通过在生成的每一步注入期望的令牌来满足约束。

假设我们试图在生成输出中强制使用短语"is fast"

在传统的射束搜索设置中,我们在每个分支找到顶部k最有可能的下一个标记,并附加它们以供考虑。在受约束的设置中,我们做了同样的事情,但是也添加了标记,这些标记将带我们更接近满足我们的约束。这里有一个演示:

作者图片

除了常见的高概率 next 令牌(如"dog""nice")之外,我们还强制使用令牌"is",以便更接近满足约束"is fast"

对于下一步,下面的分支候选项与传统的波束搜索基本相同。但是和上面的例子一样,受约束的波束搜索通过在每个新的分支施加约束来增加现有的候选:

作者图片

银行

在我们谈论下一步之前,我们需要考虑在上面的步骤中我们可以看到的不良行为。

天真地在输出中强制输入想要的短语"is fast"的问题是,大多数时候,你会得到像上面的"The is fast"这样无意义的输出。这实际上使得这个问题很难解决。在huggingface/transformers提出的原始特性请求问题中可以找到关于解决这个问题的复杂性的更深入的讨论。

银行通过在满足约束和创造合理产出之间建立平衡来解决这个问题。

第 n 组 n 指的是在满足约束方面取得了 n n 步进展的梁列表。在将所有可能的波束分类到它们各自的组中之后,我们进行循环选择。在上面的例子中,我们将从库 2 中选择最可能的输出,然后从库 1 中选择最可能的输出,从库 0 中选择一个输出,从库 2 中选择第二个最可能的输出,从库 1 中选择第二个最可能的输出,依此类推。由于我们使用的是num_beams=3,我们只需重复上述过程三次,就可以得到["The is fast", "The dog is", "The dog and"]**

这样,即使我们强迫模型考虑我们已经手动附加了所需标记的分支,我们仍然跟踪其他可能更有意义的高概率序列。尽管"The is fast"完全满足了我们的约束,但它并不是一个非常明智的短语。幸运的是,我们在未来的步骤中有"The dog is""The dog and"可以使用,这有望在以后产生更合理的输出。

上述示例的第三步演示了这种行为:

作者图片

注意"The is fast"不需要手动添加任何约束标记,因为它已经完成了(即,已经包含了短语"is fast")。此外,请注意像"The dog is slow""The dog is mad"这样的光束实际上是在存储体 0 中,因为,尽管它包括令牌"is",它必须从头开始重新生成"is fast"。通过在"is"后添加类似于"slow"的东西,它有效地重置了它的进程

最后,请注意我们是如何得到包含约束短语的合理输出的:"The dog is fast"

我们最初很担心,因为盲目地添加想要的标记会导致像"The is fast"这样无意义的短语。然而,使用来自银行的循环选择,我们最终隐含地放弃了无意义的输出,而选择了更有意义的输出。

关于Constraint类和自定义约束的更多信息

解释的主要内容可以总结如下。在每一步,我们都不断地纠缠模型来考虑满足我们的约束的记号,同时跟踪不满足我们的约束的波束,直到我们以合理的高概率结束包含我们想要的短语的序列。

因此,设计这个实现的一个原则方法是将每个约束表示为一个Constraint对象,其目的是跟踪其进度,并告诉波束搜索接下来要生成哪些令牌。尽管我们已经为model.generate()提供了关键字参数force_words_ids,但以下是后端实际发生的情况:

*from transformers import AutoTokenizer, AutoModelForSeq2SeqLM, PhrasalConstrainttokenizer = AutoTokenizer.from_pretrained("t5-base")
model = AutoModelForSeq2SeqLM.from_pretrained("t5-base")encoder_input_str = "translate English to German: How old are you?"constraints = [
    PhrasalConstraint(
        tokenizer("Sie", add_special_tokens=False).input_ids
    )
]input_ids = tokenizer(encoder_input_str, return_tensors="pt").input_ids outputs = model.generate(
    input_ids,
    constraints=constraints,
    num_beams=10,
    num_return_sequences=1,
    no_repeat_ngram_size=1,
    remove_invalid_values=True,
) print("Output:\n" + 100 * '-')
print(tokenizer.decode(outputs[0], skip_special_tokens=True))Output:
--------------------------------------------------------------------
Wie alt sind Sie?*

您可以自己定义一个,并将其输入到constraints关键字参数中,以设计您的独特约束。你只需要创建一个Constraint抽象接口类的子类,并遵循它的要求。你可以在Constraint的定义中找到更多信息,找到这里

一些独特的想法(尚未实施;也许你可以试一试!)包括类似于OrderedConstraintsTemplateConstraints的约束,这些约束可以进一步添加。目前,生成是通过在输出中的任何地方包含序列来完成的。例如,在前面的例子中,一个序列包含惊吓- >尖叫,另一个包含尖叫- >惊吓。OrderedConstraints允许用户指定满足这些约束的顺序。

TemplateConstraints可以考虑更适合的功能使用,目标可以是:

*starting_text = "The woman"
template = ["the", "", "School of", "", "in"]possible_outputs == [
   "The woman attended the Ross School of Business in Michigan.",
   "The woman was the administrator for the Harvard school of Business in MA."
]*

或者:

*starting_text = "The woman"
template = ["the", "", "", "University", "", "in"]possible_outputs == [
   "The woman attended the Carnegie Mellon University in Pittsburgh.",
]
impossible_outputs == [
  "The woman attended the Harvard University in MA."
]*

或者,如果用户不关心两个单词之间的标记数量,那么可以只使用OrderedConstraint

结论

你可以在这里找到更详细的官方博客文章

约束波束搜索为我们提供了一种灵活的方法,将外部知识和需求注入到文本生成中。以前,没有简单的方法来将模型识别为 1。包括序列列表,其中 2。其中一些是可选的,一些不是,例如 3。它们在序列中的某处生成在各自合理的位置。现在,我们可以通过混合Constraint对象的不同子类来完全控制我们这一代!

这一新功能主要基于以下论文:

和上面的那些一样,许多新的研究论文正在探索使用外部知识(例如,KGs,KBs)来指导大型深度学习模型的输出的方法。希望这种约束波束搜索特性成为实现这一目的的另一种有效方式。

关于图的力量的新见解

原文:https://towardsdatascience.com/new-insights-on-the-power-of-graphs-48ad39f61307

图机器学习和图神经网络(GNNs)已经在学术界和工业界产生了巨大的兴趣。(致力于图形机器学习的第一次会议将于今年晚些时候举行。)不过,你会发现在更广阔的数据科学和机器学习世界的其他角落也会出现图表;不管你的专业是什么,扩展你对这个基本概念的知识都是一个好主意。我们在这里提供帮助:这里有三篇杰出的文章,以实用和可理解的方式展示了图表的力量。

  • 图建模将如何塑造无监督学习的未来 。Cristiana de Azevedo von Stosch 和 Abhishek Singh 的文章的起始前提是“特征确定重要性仍然是机器学习中的一个基本问题。”它继续探索图建模框架如何帮助我们解决无监督学习应用环境中的这一挑战。
  • 关于有向无环图(Dag)及其为何重要 。谁不喜欢一个透彻的、图文并茂的讲解者呢?Matteo Courthoud 的第一篇 TDS 文章向我们展示了 Dag 的潜在用途,并展示了它们如何为我们需要纳入因果分析的变量(以及那些我们最好忽略的变量)提供视觉直觉。
  • 设计强大 GNNs 的新架构 。Maxime Labonne 的最新帖子既是对最近一篇关于图同构网络(GINs)的论文的耐心讲解,也是一篇全面实现图分类任务的实用教程。你将会对选择合适的 GNN 建筑的过程有更深刻的理解。

芭芭拉·克里斯托菲亚克在 Unsplash 上拍摄的照片

在过去的一周里,我们发表了一些关于其他主题的精彩文章,这些文章来自新老作家;我们的团队还分享了一些我们认为您可能会喜欢的原创功能。周末有时间的话,试试这些:

感谢您阅读、分享和参与我们发布的作品。如果你想探索另一种表达支持的方式,可以考虑成为中级会员

直到下一个变量,

TDS 编辑

如何向 Python 字典添加新键

原文:https://towardsdatascience.com/new-key-python-dict-e4c637f1f223

在 Python 字典中添加新的键值对

照片由Silas k hlerUnsplash 拍摄

Python 字典是该语言中最强大和最常用的数据结构之一,因为它允许用户维护一组键值对。对于那些不太熟悉 Python 数据结构的人来说,字典相当于其他语言中的地图,比如 Java。

Python 字典是一个可变的、有序的(从 Python 3.7 开始,它们是插入有序的)键-值对集合,集合中的每个键都是唯一的(不同的键可能有相同的值)。

在今天的文章中,我们将演示如何对 Python 字典执行一些简单的操作,比如在现有对象中添加或更新键值对。

首先,让我们创建一个示例字典,我们将在整个教程中引用它来演示我们前面介绍的概念。

d = {
  'a': 100,
  'b': 'hello',
  'c': True,
}

如何在字典中添加单个键值对

在现有字典中添加键-值对的最简单方法是通过给(新的)所需键赋值:

>>> d['e'] = 'newValue'>>> print(d)
{'a': 100, 'b': 'hello', 'c': True, 'e': 'newValue'}

请注意,如果该键已经存在,上述操作将用新指定的值替换旧值。如果您想确保只有在键不存在的情况下才添加新值,您可以在运行上述赋值之前简单地进行检查:

if 'e' not in d.keys():
   d['e'] = 'newValue'

或者,您也可以使用update()方法在现有字典中添加新的键值对。该方法接受另一个字典作为输入(包含新的键值对):

>>> new = {'e': 'newValue'}
>>> d.update(new)
>>> print(d){'a': 100, 'b': 'hello', 'c': True, 'e': 'newValue'}

事实上,您甚至可以用几种不同的方式调用update方法:

>>> d.update(e='newValue')
# OR
>>> d.update(dict(e='newValue'))

然而要注意的是,赋值方法在计算上比update()更便宜。

最后,从 Python 3.9 开始,|=操作符也可以用来更新字典。

>>> d |= {'e': 'newValue'}
>>> print(d)
{'a': 100, 'b': 'hello', 'c': True, 'e': 'newValue'}

添加多个键值对

我们在上一节中讨论的update方法是一次性添加多个键值对时最常用的方法。

>>> new = {'e': 'newValue', 'f': 'anotherValue'}
>>> d.update(new)
>>> print(d){'a': 100, 'b': 'hello', 'c': True, 'e': 'newValue', 'f': 'anotherValue'}

同样,我们也可以使用我们之前介绍的|=(如果在 Python 3.9+上)来添加多个新的键值对:

>>> d |= {'e': 'newValue', 'f': 'anotherValue'}
>>> print(d){'a': 100, 'b': 'hello', 'c': True, 'e': 'newValue', 'f': 'anotherValue'}

更新字典

同样,我们可以使用前面探索过的各种方法来更新字典中现有的键值对。

当我们对字典中已经存在的键进行赋值时,所赋的值将覆盖相应的值:

>>> d['a'] = 200
>>> print(d)
{'a': 200, 'b': 'hello', 'c': True}

或者,要一次更新多个元素,可以使用update方法:

>>> updated = {'a': 200, 'c': False}
>>> d.update(updated)
>>> print(d)
{'a': 200, 'b': 'hello', 'c': False}

最后的想法

Python dictionary 是一个非常强大的数据结构,用于促进日常编程任务中不同种类的操作。

在今天的简短教程中,我们演示了如何在现有的字典集合中插入新的键值对,以及如何更新单个或多个元素。

成为会员 阅读媒体上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

相关文章你可能也喜欢

受计算机视觉启发,将蛋白质结构拟合到低分辨率冷冻电镜图中的新方法

原文:https://towardsdatascience.com/new-method-to-fit-protein-structures-into-low-resolution-cryo-em-maps-inspired-by-computer-vision-40262b32ea2a

新的预印本提供了一种工具,可以彻底改变冷冻电子显微镜和蛋白质结构机器学习预测的使用。

此处给出的预印本中描述的方法和程序的应用实例:将两种蛋白质(一种显示为红色,另一种显示为青色)放入通过冷冻电子显微镜获得的低分辨率 3D 图中。该方法的核心是基于类似于计算机视觉算法中使用的描述符。图片由作者生成。

由于其能够阐明生物学是如何从化学和物理学中产生的,结构生物学处于生物学、生物技术和医学基础研究的前沿。难怪计算机科学在让结构生物学更快发展方面发挥了关键作用。Traeger 等人在此讨论的预印本展示了计算机科学在处理结构数据中的另一个应用,以便从中提取最大的价值。

确定蛋白质原子级结构的传统技术,即核磁共振和 X 射线晶体学,通常会随着目标蛋白质的大小和灵活性的增加而变得不那么有效。冷冻电子显微镜(CryoEM)已经成为一种强大的技术,可以处理大的蛋白质系统,甚至由几种蛋白质组成的复合物,并在结构生物学中迅速占据一席之地,但也发现了它的局限性,这项新工作解决了其中的一些问题。

CryoEM 通过向快速冷冻的样品发射电子并记录产生的图像来构建 3D 地图,然后将原子建模为 3D 地图,从而解决了蛋白质分子的结构确定问题。这些 3D 地图是从非常大的图像数据集获得的,并且它们本身是由 3D 网格上的电子密度组成的非常复杂的数据结构。简而言之,通过 CryoEM 的结构确定包括以不仅与数据一致而且与已知化学和分子几何形状一致的方式将原子放置在 3D 图内。当 3D 地图具有高分辨率时,这相对容易,而随着分辨率的降低,这就不那么容易甚至不可能了。

在过去的几年里,CryoEM 在生物学中变得越来越成熟,因为它开始获得非常高的分辨率,以某种方式挑战传统的 X 射线晶体学和 NMR 的作用,如果它还没有在整体能力上超过它们的话。(当然,每种技术都有特定的优势和优势,也有弱点和劣势——但可以说 cryoEM 的全球影响力是目前所有 3 种技术中最强的)。

尽管 CryoEM 的能力和使用有所提高,但事实上现在制作的大多数 CryoEM 地图都是中低分辨率的。这使得模拟单个原子来重建蛋白质结构几乎是不可能的。然而,如果一个人知道一些部分蛋白质结构,或者可以用 AlphaFold 等新方法建模,那么他可以将它们放入 CryoEM 的 3D 图中,从而仍然可以获得非常详细的结构,尽管输入数据的分辨率很低。此外,人们可以拟合一种蛋白质的多种“构象”(意味着它的不同结构,可能来自不同的模型或代表蛋白质的内在可变性),然后选择最适合的那些。

问题是将一个或多个蛋白质结构放入三维空间的 CryoEM 图是一项非常困难的任务。最近,我们实验室的一份新预印本提出了一种新的方法来完成这项任务,它从计算机视觉方法中汲取灵感,特别是从图片中找到已知形状的对象的问题中。

这个新工具,以大分子描述符命名为 MaD,将简化和半自动化科学家处理 CryoEM 图和蛋白质结构的工作。如今,他们基本上是通过手工将结构装配到地图中,只使用局部优化工具来微调他们手工构建的接近最终的姿态。通过使用 MaD,他们将能够把更大一部分工作留给计算机,这不仅加快了过程,而且使其不那么主观,不容易出现偏差或错误。

MaD 获取由 CryoEM 实验提供的目标 3D 图,一个或多个应该适合它的结构,然后计算和评分一系列适合度,提供可能的结构,然后可以用于传统的局部改进。下图说明了 MaD 如何将两个蛋白质结构放在一起形成一个复合物,该复合物大致满足相应的低分辨率 CryoEM 图:

行动中的 MaD(同样的例子出现在主图中):MaD 在这里将两种蛋白质放入分辨率相当低的 CryoEM 图中。图片由作者生成。

MaD 是如何工作的

MaD 本质上是一种基于特征的方法,受计算机视觉中使用的局部特征描述符的启发。在这个领域中,描述符通常应用于各种任务,例如图像配准、图像重建和对象检测。一般来说,描述符是从特征点周围的局部区域构建的,并针对旋转不变性、高局部特异性和对噪声和变换的鲁棒性进行处理。

MaD 使用一种适应形式的尺度不变特征变换 (SIFT)描述符。首先,MaD 将输入结构投射到一个网格上,该网格的间距与将要拟合结构的输入 Cryo-EM 数据的间距相同。将结构处理产生的网格与高斯核进行卷积,然后从强度高于微调数量的邻居周围的强度的体素中收集锚。同样,MaD 也为 CryoEM 地图计算锚点。来自结构和地图的锚是描述符计算和拟合的起点。

在根据一些额外的标准清理完锚之后,它们就可以装配了。为此,MaD 在每个锚点周围提取一个球形面片,并从面片内计算的梯度向量构建一个球形直方图。然后从直方图上关键位置处具有最高矢量计数的仓中导出旋转矩阵。这些梯度表现为描述符,然后指导平移和旋转,以将每个蛋白质成分纳入图谱。随着更多组件(结构)的添加,解决方案将沿着密度梯度进行优化,同时考虑到必须消除任何引入的碰撞。

应用程序

MaD 的匹配描述符的方法非常有效,即使对于中低分辨率也能很好地收敛。正如这项工作的作者所展示的,MaD 产生了强大而准确的装配预测。因此,正如预印本中大量例证的那样,当存在低质量的地图时,它应该非常有力地提供结构细节。特别是,我希望它可以很好地与蛋白质结构的机器学习预测结合起来,例如 AlphaFold,因为人们可以通过将它们拟合到中等分辨率的 CryoEM 结构中来验证或改进它的模型。

除了这些中低分辨率的应用,我们注意到 MaD 也适用于高分辨率数据。虽然人们可能首先认为这不是一个非常需要的特征,因为原则上可以用标准的结构求解程序从零开始将原子模型化到图谱中,但事实是,即使当高分辨率 CryoEM 图谱可用时,MaD 也可以通过自动化程序以高吞吐量加速结构测定。预印本确实提出了这种类型的应用,其中从蛋白质的分子动力学模拟中检索到的大量构象都适合目标图,然后对产生的模型进行评分,并提出最可能的结构。都是自动的,而且非常快。

一个先进的例子:将 MaD 应用于分子模拟,以建立考虑分子灵活性的结构集合

预印本中最有趣的应用可能是在 GroEL 四聚体蛋白质上的应用。在不深入研究其生物学的情况下,关键点是它是由 14 个单一蛋白质拷贝组成的笼状结构,排列成 2 个七元环,在其内部容纳蛋白质底物(其作用是帮助它们折叠,即它是一种伴侣)。

预印本中给出的例子涉及将取自 X 射线结构的 GroEL 的 14 个拷贝对接到特定的 CryoEM 图中,该 cryo em 图呈现了与可用的 X 射线结构的一些结构偏差。由于这种偏差,如果蛋白质结构照原样处理,拟合永远不会完美。因此,该小组首先进行了分子动力学模拟,这是一种计算,其中基本的牛顿力学定律随着时间的推移而传播,以允许系统(这里是单个 GroEL 蛋白质的初始结构)探索构象波动。然后,作者使用他们的另一种工具,称为 CLoNe,来识别 7 个相关的簇中心,然后将 7 个中心的每一个的 14 个拷贝对接到两个 CryoEM 图中,这两个 cryo em 图对应于十聚体蛋白质的封闭和开放形式。对于打开和关闭状态,所有测试组件的 MaD 分数都正确地检索了在原子水平上解析的可靠的十四聚体模型。

MaD 可用于将大型组件的结构重建成 CryoEM 图,这一点并不重要。用于确定蛋白质原子级结构的三种主要技术,包括最简单形式的 CryoEM,att 通常捕捉离散的结构快照(除了非常专业的非主流实验,人们也可以这样做)。正如预印本中所示,MaD 提供了一个精简的管道,可以使这种分子重建变得非常常规,同时它允许使用较低分辨率的数据,包括等待重新评估的新旧数据。

结构生物学家社区需要这样的工具,能够将灵活性集成到结构生成管道中,并使用次优分辨率的数据。通过实现这两种进步,MaD 可以代表结构生物学研究中的一个巨大进步,所有这些都是由(原则上)完全独立的计算机视觉领域实现的。

读物

描述 MaD 和一些示例应用的主要预印本:

https://www.biorxiv.org/content/10.1101/2022.06.22.497181v1

MaD 在我们实验室的网站和 GitHub 上:

https://www.epfl.ch/labs/lbm/resources/ https://github.com/LBM-EPFL/MaD

化学和结构生物学中更多的计算机和数据科学

(只是几个亮点……)

https://medium.com/advances-in-biological-science/can-we-predict-not-only-static-protein-structures-but-also-their-structural-diversity-fa1d9380fc34

www.lucianoabriata.com我写作并拍摄我广泛兴趣范围内的一切事物:自然、科学、技术、编程等等。 成为媒介会员 访问其所有故事(我免费获得小额收入的平台的附属链接)和 订阅获取我的新故事 通过电子邮件 。到 咨询关于小职位 查看我的 服务页面这里 。你可以 这里联系我

新的预印本描述了一种新的原子坐标的无参数几何转换器,以预测蛋白质中的生物界面

原文:https://towardsdatascience.com/new-preprint-describes-a-novel-parameter-free-geometric-transformer-of-atomic-coordinates-to-c6545150855e

AI after AlphaFold

它的运行速度非常快,甚至可以扫描大量的蛋白质结构来寻找易相互作用的氨基酸。

机器学习、人工神经网络和其他基于“人工智能”的数学方法(我不太喜欢这个术语,但它很棒!)已经应用于科学问题几十年了。但众所周知,它们现在有了前所未有的应用,彻底改变了化学和生物学等科学。

现代人工智能最引人注目的应用之一可能是预测蛋白质结构,这始于大约 5-10 年前,并在 2020 年底和 2021 年出现了 AlphaFold 2。我在以前的文章中详细讨论了 AlphaFold 2:

从事蛋白质结构预测的科学家首先对 AlphaFold 的影响感到负面情绪,因为它有点扰乱了他们自己的领域,实现了他们长期以来希望的目标。然而,在短暂的哀悼之后,他们接受并真正利用 AlphaFold 进行新的发现和开发新的工具,其中许多我已经讨论过了:

毕竟 AlphaFold 2 并没有解决分子和结构生物学的所有相关问题。事实上,它只解决了巨大拼图的一小部分(这一点也不使它变小!)AlphaFold 2 解决了什么(我说“某种程度上”是因为即使这个问题也没有完全解决;我一直强调 AlphaFold 2,因为它的第一个版本还不太擅长)预测蛋白质的所谓“三级结构”,这本质上意味着它们的组成原子如何在 3D 空间中排列。

但是蛋白质结构有几个层次的复杂性。蛋白质是氨基酸的长直链,其折叠成 3D 结构以获得三级结构,但这些又可以形成更高级的结构,即多种蛋白质之间或蛋白质与其他生物大分子如核酸(DNA 和 RNA)或与膜、离子、小分子等之间的复合物。事实上,在大多数情况下,蛋白质的生物功能是由这些复合物以生理相关的方式决定或调节的。

当一种蛋白质与另一种蛋白质相互作用时,我们称之为蛋白质-蛋白质复合物,AlphaFold 2 可以预测其中的一些相互作用(特别是在其“AlphaFold 多聚体”风味中),但它还不太擅长。如果我们考虑蛋白质可以建立的其他类型的相互作用,AlphaFold 就出局了。它只是没有被设计来预测蛋白质和蛋白质以外的分子之间的相互作用,例如 DNA、RNA、离子、小分子如氨基酸、代谢中间体、细胞信号分子等。或生物膜和它们的成分,脂质。

对这些其他相互作用进行建模是在原子水平上对生物结构、相互作用和功能进行建模的下一步,有许多小组已经在这方面工作了多年。如果 Deepmind 本身现在着手解决蛋白质可以参与的其他一些相互作用,我不会感到惊讶。特别是,小分子结合的特定预测与制药有着巨大的相关性,因为大多数临床使用的化合物本身就是与特定蛋白质相互作用的小分子。

要了解更多关于人工智能在蛋白质结构预测和结构生物学/结构生物信息学方面的未来路线,你可以查看我最近写的这篇文章:

通过使用无参数几何变换器预测蛋白质将与什么相互作用

我工作的实验室的一份新的预印本已经用一种新的配方解决了这个问题:

给定一种蛋白质的结构或模型,预测它可以形成什么界面来结合其他蛋白质、核酸、脂类、离子或其他种类的小分子。

领导这项工作的博士生开发了一种几何转换器,它可以读取和处理输入蛋白质的 3D 坐标,并产生特定于残基的分数,这些分数可以预测蛋白质的每个氨基酸成为其他蛋白质、核酸、离子等界面的一部分的可能性。这种方法在蛋白质结构转换器之后被称为 PeSTo,具有非常高的准确性,几乎不会混淆界面,并且与替代方法相比有几个非常有利的方面:

  • 运行该模型不需要像大多数替代方法那样计算输入蛋白质的表面。表面计算的计算速度很慢,并且对 3D 结构中的误差非常敏感。
  • 模型的运行时间只有几毫秒,包括它的加载时间,这意味着您可以在短时间内处理大量的结构。事实上,它的速度非常快,可以在几秒钟内处理整个分子动力学轨迹,这对于识别只有当蛋白质移动时才能到达的瞬时界面非常有用,正如我们所展示的。我们也可以处理整个人类蛋白质组,发现新的生物学。
  • 该模型不依赖于任何参数化甚至分类,因为它完全基于原子元素和空间位置来训练。因此,虽然我们将 PeSTo 应用于蛋白质和它们的 C、N、O 原子,但它应该很容易被重新训练用于其他目的,例如在材料科学中。

一种新的原子坐标几何变换器

让我提几个关于 PeSTo 工作原理的要点。有关更多详细信息,您可以参考预印本:

https://www.biorxiv.org/content/10.1101/2022.05.09.491165v1.article-info

PeSTo 将蛋白质结构视为点原子云,通过成对距离和保证平移不变性的相对位移向量来表示几何结构。每个点原子仅使用其元素名称来描述,而不使用其他方法使用的数值参数,如半径或电荷。每个原子都通过几何转换器进行编码,该转换器通过标量和矢量状态以及从周围原子以递增的距离计算的距离来考虑其局部邻域。在查询时,该描述符通过网络传播,通过多头注意力操作产生特定于原子的输出。然后,两个额外的模块收集每个蛋白质残基的基于原子的输出,最终预测蛋白质的每个残基是否可能在界面上。

基于来自蛋白质数据库的数据集,我们训练该模型输出蛋白质-蛋白质、蛋白质-核酸、蛋白质-离子、蛋白质-配体、蛋白质-离子和蛋白质-脂质界面的残基方式的接合概率。

Webserver 的实现和一个具体的例子

预印本包括一些精选的例子。我将在这里向您展示一个我在 web 服务器实现上运行的具体示例,网址为 https://pesto.epfl.ch

当您访问该网站时,您可以选择对以下内容进行预测:

  • 来自 PDB 的蛋白质结构,以其 4 字符 ID 输入
  • AlphaFold-EBI 数据库中预先计算的蛋白质模型,作为 UniProt ID 输入
  • 你上传的蛋白质结构/模型。

让我们在这里尝试一个来自 PDB 的结构,因为这允许我介绍输入页面的另一个特性:

PDB ID 4ITQ 取自 PDB,然后选择其唯一的蛋白质链进行分析。作者卢西亚诺·阿布利亚塔的这些和其他数字。

我是故意拍的 4ITQ。这是一种与 DNA 结合的蛋白质的 X 射线结构。PDB 中注释的生物组装标记了一个特定的蛋白质-DNA 表面,但我为另一项工作对一个相关蛋白质进行的基于溶液的 NMR 实验揭示了一个更广泛的 DNA 相互作用表面。PeSTo 预言了什么?

让我们先来看看 PeSTO 对这种蛋白质的所有预测:

蛋白质-蛋白质、蛋白质-核酸、蛋白质-脂质、蛋白质-配体和蛋白质-离子界面的 PDB 4ITQ 预测。红色表示交互倾向,越强烈表示可能性越大。

PeSTo 产生相当大的残基表面,这些残基可能参与结合核酸,也可能是结合离子的环。不结合除离子以外的其他蛋白质、脂质或配体。

DNA 结合的界面非常大,这与我以前论文中的 NMR 结果非常一致,该结果表明通过至少两个界面结合,而不是 X 射线结构所提出的仅仅一个界面。此外,那篇论文中的 AFM 实验表明,这种蛋白质在 DNA 中引入了强烈的环和扭结,暂时是因为它迫使 DNA 包裹它,解开它以实现一些尚不清楚的生物功能。

在分子动力学模拟和折叠体中发现界面的应用

PeSTo 运行速度如此之快,以至于我们可以将它应用于大量的结构。对于我们的预印本,我们尝试将其作为一种工具,以识别分子模拟的蛋白质界面,并收集完整的人类蛋白质组的结构。

PeSTo 应用于分子动力学模拟非常有用,因为它可以自动检测界面,这些界面在用于开始模拟的结构中可能不明显,但在动力学中可能会暴露出来。这对于发现所谓的隐藏口袋可能特别有效,所谓的隐藏口袋是指蛋白质表面的小口袋,随着蛋白质的移动而出现和消失,因此可能会在静态 X 射线结构中消失。

进一步阅读和相关阅读

预印本:

https://www.biorxiv.org/content/10.1101/2022.05.09.491165v1.article-info

一个连接我所有关于 AlphaFold 和蛋白质结构预测的文章的枢纽故事:

https://lucianosphere.medium.com/guide-to-my-blog-articles-on-alphafold-39c10a7dad7f

Deepmind 还致力于将人工智能应用于其他科学领域:

一种预测蛋白质相互作用的方法,但通过表面的几何深度学习:

https://www.nature.com/articles/s41592-019-0666-6

www.lucianoabriata.com我写作并拍摄我广泛兴趣范围内的一切事物:自然、科学、技术、编程等等。 成为媒介会员 访问其所有故事(我免费获得小额收入的平台的附属链接)和 订阅获取我的新故事 通过电子邮件 。到 咨询关于小职位 查看我的 服务页面这里 。你可以 这里联系我

新研究:高级人工智能可能倾向于寻求权力*

原文:https://towardsdatascience.com/new-research-advanced-ai-may-tend-to-seek-power-by-default-fdc9eb0afd87

播客

爱德华·哈里斯谈人工智能的安全性和高级人工智能带来的风险

苹果 | 谷歌 | SPOTIFY | 其他

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

近年来,甚至几个月来,人工智能的进展一直在急剧加速。似乎每隔一天,就有一个新的、以前认为不可能的人工智能壮举由世界领先的实验室实现。这些突破越来越多地由同一个简单的想法驱动:人工智能扩展。

对于那些不了解人工智能扩展理论的人来说,扩展意味着用更大的模型训练人工智能系统,使用越来越多的数据和处理能力。到目前为止,世界顶级人工智能实验室的实证研究似乎表明,扩展是一个开放式的过程,可以导致越来越多的有能力和智能的系统,没有明确的限制。

这让许多人猜测,缩放可能会开创一个广泛人类水平甚至超人人工智能的新时代——这是人工智能研究人员几十年来一直追求的圣杯。

虽然这听起来很酷,但一个能够像人类一样甚至更好地解决一般推理问题的人工智能实际上可能是一件本质上危险的事情。

至少,这是许多人工智能安全研究人员在一项新研究发表后得出的结论,该研究探索了现代人工智能系统如何解决问题,以及我们是否应该期待它们的更高级版本执行危险的行为,如寻求权力。

人工智能安全中的这条研究路线被称为“寻求权力”,尽管它目前在人工智能安全和人工智能对齐研究的前沿之外没有得到很好的理解,但它开始引起很多关注。第一个关于权力寻求的重要理论研究是由亚历克斯·特纳领导的,他之前出现在播客上,并发表在 NeurIPS(世界顶级人工智能会议)上。

今天,我们将听到爱德华·哈里斯(Edouard Harris)的发言,他是人工智能校准研究员,也是我在人工智能安全公司(Gladstone AI)的联合创始人之一。Ed 刚刚完成了一项重要的人工智能安全研究,该研究扩展了亚历克斯·特纳最初的能量寻求工作,并显示了似乎是第一个实验证据,表明我们应该期待高度先进的人工智能系统默认寻求能量

寻求权力到底意味着什么呢?所有这些是否意味着未来通用推理系统的安全性?这就是这一集的全部内容。

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

  • 不管你人生的最终目标是什么,总有一些目标是你想要追求的——即使你不知道你的目标是什么,你也会想要追求的目标。例如,你永远不会不想在你的银行账户里多存 1000 万美元,因为不管你的目标是什么,或者结果是什么,多存 1000 万美元不会让你更难实现目标,反而可能让你更容易实现目标。同样,不管你的最终目标是什么,你永远不会不想变得更聪明,因为更聪明有助于追求你可能想要的任何目标。因此,收集更多的资源和变得更聪明变成了几乎每个人都一致认同的子目标。在人工智能安全中,像这样的目标被称为“工具性目标”。
  • 工具性目标不仅仅适用于人类。正如我们在与亚历克斯·特纳的对话中看到的,有充分的理由相信强大的人工智能系统最终也会追求它们。根据越来越多的理论研究,我们应该预计人工智能——像人类一样——会默认收敛于某些工具性目标(这种想法被称为“工具性收敛”)。几乎无论人工智能被训练追求什么目标,如果它被关闭,它都不会更有可能实现它(因为“开启”是它继续影响世界的唯一方式)。出于类似的原因,我们应该期待人工智能收敛于其他工具性目标,如自我完善和资源整合。这些和其他相关行为统称为人工智能安全中的“权力寻求”行为。
  • 亚历克斯·特纳的工作从理论上表明,对于我们可能训练它们实现的绝大多数目标,人工智能代理将倾向于参与权力寻求行为。他通过引入权力的严格数学定义做到了这一点——这是前所未有的。在人工智能安全领域挥舞了十年的争论之后,它最终以量化的方式框定了权力寻求的辩论。
  • Ed 的工作扩展了 Alex 的定义,并允许他明确地研究不同智能代理之间的交互。它代表了人工智能中寻求权力的首次实验演示。
  • 在他的实验中,他首先模拟了一个“人类”智能体,这个智能体被允许在人类智能体所处的环境中学习一种优化的奖励收集策略。Ed 使用了一个包含一块奶酪的迷宫的比喻,这代表了代理的奖励:他的人类代理被允许学习一个最佳策略来通过迷宫并收集奶酪。重要的是,环境(迷宫)是静态的。这反映了人类学习的速度比大自然快得多的现实。人类根据大脑时钟时间学习,而大自然则根据冰川般缓慢的进化时间“学习”或优化,需要一代又一代的选择来产生有意义的新物种。因此,大自然对我们来说似乎是大致静止的:例如,有些树木是在工业革命前种植的,现在它们的命运完全掌握在人类手中。
  • ed 允许他的模拟人类智能体在这个环境中优化,他冻结了人类的优化过程,阻止他们进一步学习。然后,他将一个高度先进的“人工智能代理”引入到他的模拟中。这个代理能够在组合的人工代理+环境系统上运行它自己的优化过程,这个系统相对于人工智能来说是静态的。同样,这是有意义的:无论人类水平的人工智能可能采取什么形式,它都将在计算机时钟时间上运行,相比之下,人类生物大脑的时间将非常慢,基本上看起来是静态的。
  • 然后,埃德调查了每个代理人的权力如何影响其他人。他探索了代理人最终竞争(而作为低级推理机器的人类必然会失败)和合作的案例。他的结论相当惊人:事实证明,即使人类和人工智能主体有彼此独立的目标(并且不直接相互矛盾),他们最终也会为工具性目标而竞争。我们进入一些细节,解开他研究的其他方面,这提供了证据,表明我们应该预计高级人工智能和人类会在默认情况下竞争资源和“权力”(数学定义),并且防止这种情况发生可能需要人工智能系统设计的极端精确性。**

章节:

  • 0:00 介绍
  • 4 点亚历克斯·特纳的研究
  • 7:45 技术想要什么
  • 11:30 通用目标
  • 17:30 连接观察
  • 24:00 微功耗搜索行为
  • 28:15 艾德的研究
  • 38:00 人类作为环境
  • 42:30 什么导致权力的追求
  • 48:00 比赛作为默认结果
  • 52:45 一般关注
  • 57:30 总结

新的 Scikit-Learn V.1.1.0 更新版本主要亮点

原文:https://towardsdatascience.com/new-scikit-learn-v-1-1-0-update-release-top-highlight-f2f94985c63e

来自 Scikit 的激动人心的更新-了解

斯科特·格雷厄姆Unsplash 上拍照

Scikit-Learn 是使用 Python 环境的数据科学家的主要机器学习包。该软件包提供了许多在我们日常工作中使用的有用的 API。

2022 年 5 月,Scikit-Learn 发布了新的 V.1.1.0 更新,提供了各种令人兴奋的功能更新。有哪些更新?让我们开始吧。

但是,请在我们离开之前将 Scikit-Learn 更新到最新版本。

pip install --upgrade scikit-learn

平分意味着

平分 K 均值是 Scikit-Learn 中无监督机器学习 K 均值的一个新的附加变体。该方法在聚类过程中实现了简单的除法分层算法。

在正常的 K-Means 中,聚类过程是通过同时创建 K 个质心来进行的。将通过计算簇内和簇间相似性来评估所得的簇。星团的质心类似于重心。

然而,在分割 K-Means 时,我们并没有同时创建 K 形心。相反,质心是基于前一个聚类逐步选取的。我们每次都要分割集群,直到达到 K 的数目。

使用二等分 K-Means 有几个优点,包括:

  • 如果有大量集群,效率会更高
  • 更便宜的计算成本
  • 它不会产生空簇
  • 聚类结果是有序的,将创建一个可见的层次结构。

让我们尝试一个简单的比较正常的 K-均值和平分 K-均值。我将使用 seaborn 包中的一个样本数据集来模拟结果。

import numpy as np
from sklearn.cluster import KMeans
import seaborn as sns
from sklearn.cluster import KMeans, BisectingKMeans
import matplotlib.pyplot as pltmpg = sns.load_dataset('mpg')
mpg = mpg.dropna().reset_index(drop = True)X = np.array(mpg[['mpg', 'acceleration']])km = KMeans(n_clusters=5, random_state=0).fit(X)
bisect_km = BisectingKMeans(n_clusters=5, random_state=0).fit(X)fig, ax = plt.subplots(1, 2, figsize=(10, 5))
ax[0].scatter(X[:, 0], X[:, 1], s=10, c=km.labels_)
ax[0].scatter(km.cluster_centers_[:, 0], km.cluster_centers_[:, 1], s=20, c="r")
ax[0].set_title("KMeans")ax[1].scatter(X[:, 0], X[:, 1], s=10, c=bisect_km.labels_)
ax[1].scatter(
    bisect_km.cluster_centers_[:, 0], bisect_km.cluster_centers_[:, 1], s=20, c="r"
)
_ = ax[1].set_title("BisectingKMeans")

作者图片

如上图所示,二等分 K-Means 可以有效地、直观地为最远部分的数据创建一个聚类。

基于 HistGradientBoostingRegressor 的分位数损失函数建模

Scikit-Learn 中的 HistGradientBoostingRegressor 是一个梯度推进回归器,是一个集成树模型,具有基于直方图的学习模型。

基于直方图的模型比普通的梯度推进回归模型更有效,因为该算法将连续特征绑定到用于训练目的的离散箱中,而不是通常的分裂技术。

根据文档,HistGradientBoostingRegressor 模型适用于超过 10.000 个样本的大数据集。

在最近的更新中,HistGradientBoostingRegressor 增加了一个新的分位数损失函数供我们使用。分位数损失函数预测可能的范围,我们称之为预测区间。您可以在这篇文章中进一步阅读分位数回归建模。

通过一个额外的分位数损失函数,我们现在可以通过loss="quantile"并使用新的参数quantile。使用下面的教程,我们可以跟踪每个分位数的回归预测。

from sklearn.ensemble import HistGradientBoostingRegressor
import numpy as np
import matplotlib.pyplot as plt# Simple regression function for X * cos(X)
rng = np.random.RandomState(55)
X_1d = np.linspace(0, 10, num=2000)
X = X_1d.reshape(-1, 1)
y = X_1d * np.cos(X_1d) + rng.normal(scale=X_1d / 3)quantiles = [0.9, 0.5, 0.1]
parameters = dict(loss="quantile", max_bins=32, max_iter=50)
hist_quantiles = {
    f"quantile={quantile:.2f}": HistGradientBoostingRegressor(
        **parameters, quantile=quantile
    ).fit(X, y)
    for quantile in quantiles
}fig, ax = plt.subplots()
ax.plot(X_1d, y, "o", alpha=0.5, markersize=1)
for quantile, hist in hist_quantiles.items():
    ax.plot(X_1d, hist.predict(X), label=quantile)
_ = ax.legend(loc="lower left")
plt.title('Sample HistGradientBoostingRegressor with Quantile Loss')

作者图片

OneHotEncoder 中不常见的类别,并在所有变形金刚中获取功能名称

一键编码是应用于分类特征以产生数字特征的常见分类过程。使用 Scikit-Learn OneHotEncoder,我们可以根据我们的数据开发出用于生产环境的变压器。如果你从来没有听说过什么是独热编码,你可以在下面的文章中读到它。

</4-categorical-encoding-concepts-to-know-for-data-scientists-e144851c6383>

在最新的更新中,Scikit-Learn 添加了一个新参数OneHotEncoder来对所有罕见值进行分组,而不是将每个罕见值创建到新的数字特征中。让我们用一个样本数据集来尝试一下。我将使用 Seaborn 的 tips 样本数据集。

import seaborn as sns
tips = sns.load_dataset('tips')
tips['size'].value_counts()

作者图片

正如我们从上图中看到的,与其他值相比,1、5 和 6 的大小类别值并不常见。在这种情况下,我想将它们分组。我们可以用下面的代码来实现。

from sklearn.preprocessing import OneHotEncoder#We establish that any values frequency below 6 is considered as rare
enc = OneHotEncoder(min_frequency=6, sparse=False).fit(np.array(tips['size']).reshape(-1, 1))
enc.infrequent_categories_

作者图片

不常见的类别正是我们所期望的。如果我们尝试将分类值转换成数字列,这就是我们将得到的结果。

encoded = enc.transform(np.array([[1], [2], [3], [4],[5],[6]]))
pd.DataFrame(encoded, columns=enc.get_feature_names_out())

作者图片

所有不常用的类别现在都被认为是一个特征。这将帮助我们最大限度地减少创建的特征数量,避免维数灾难。

此外,Scikit-Learn 中的所有变形金刚现在都允许我们获得特性的名称。get_features_names_out属性将为转换器输出中的每一列提供字符串名称。

enc.get_feature_names_out()

作者图片

关于特征选择的附加参数

Scikit-Learn 最近向变压器SequentialFeatureSelector添加了新参数n_features_to_select=’auto',并将 callable 传递给变压器SelectFromModelmax_features

SequentialFeatureSelector 是一种贪婪搜索算法,用于进行特征前向选择或后向选择以形成特征子集。该估计器将根据估计器交叉验证分数在每次迭代中添加或删除特性。

使用'auto'参数,当分数提高不超过tol参数时,结束特征将自动结束。如果tol参数为None,则选择一半的特征。

在变形金刚SelectFromModel中,如果我们将 callable 传递给max_features参数,那么允许的最大特性数量将使用max_feaures(X)的输出。

迷你批处理

非负矩阵分解或 NMF 是一种用于降维和特征提取的多元分析算法。该方法常用于自然语言处理、推荐引擎、面部识别等。你可以在这里阅读更多关于 NMF 的信息。

MiniBatchNMF 是一种在线优化 NMF 的方法,它通过将数据划分为小批量来优化 NMF 模型。该模型适用于大型数据集,因为该过程会更快,但预测精度较低。这里的可以参考教程

结论

Scikit-Learn 最近已将软件包更新到版本 1.1.0,以下是重点更新:

  • 新的二分均值算法
  • HistGradientBoostingRegressor 的新分位数损失
  • OneHotEncoder 中不常见的类别
  • 从变形金刚中获取特征的名称
  • 关于特征选择的附加参数
  • 新的 MiniBatchNMF 算法

希望有帮助!

在我的 社交媒体上访问我 进行更深入的交谈或有任何问题。

如果您不是作为中等会员认购,请考虑通过 我的推荐 进行认购。

用数据创建新年祝福

原文:https://towardsdatascience.com/new-year-greetings-with-data-76f32548a78b

使用数据在新年主题上创造引人注目的视觉效果

用数据创建新年祝福—作者的左图。右边的照片由 Unsplash 上的 Jaeyoon Jeong

如果你想做一个创新的新年祝福,那么你可以从数据科学算法中获得帮助!这个故事将向您展示一些创新的方法,您可以使用数据来创建非常引人注目的有趣的新年主题可视化。

在这个故事中,你将看到三个不同的新年愿望,直观地命名如下:

1.数据气泡

2.数据驱动的 2022

3.数据庆典

让我们看看这些愿望是如何实现的,以及帮助我们实现这些愿望的数据科学算法。

用于生成可视化的数据

我们将使用如下所示的数据来创建新年愿望可视化。第一列是一个类别,如健康、家庭、专业、繁荣等。第二列有一个与该类别相关的问候语。第三列是与每个问候的重要性相对应的权重。这纯属主观臆断。

样本数据—作者提供的图片

数据气泡

新年祝福——数据泡泡(图片由作者提供)

没有一杯香槟酒,新年庆祝就不完整!气泡可以成为使用尖端算法创建可视化的灵感来源。可以生成气泡式可视化的数据科学算法之一是相似性分析。亲和力分析背后的算法也称为力定向图。

下面显示的是基于数据的力导向可视化。一旦你有了可视化,你就可以把它和香槟图像结合起来,创造出最终的问候。

力导向可视化(图片由作者提供)

PS:亲和力分析得到了很多数据科学的应用。亲和力分析最常见的用途之一是推荐产品。

数据驱动的 2022

新年祝福——数据驱动的 2022(图片由作者提供)

你可以用一些创新来写 2022,表达你对 2022 的创新心情!在下面显示的问候语中,看起来很无聊的 0 已经被一些很酷的可视化所取代。这里使用的可视化被称为弧图,因为它提供了一个圆形的视觉效果。

下图是弧形图。你可以用两个这样的可视化来创建 2022 年的 0。

弧形图可视化(图片由作者提供)

PS:弧图被广泛用于理解数据中的嵌入和关系。

数据庆典

新年祝福—数据—庆祝(图片由作者提供)

你可以把耀眼的新年焰火想象成天空中令人惊叹的视觉效果!视觉可以成为创造类似数据的灵感来源。树形图是一种可以与烟火图像完美结合的可视化。

这里是基于数据的径向树形图。一旦有了可视化效果,就可以将它与 fireworks 图像结合起来,创建最终的问候语。

径向树可视化(图片由作者提供)

PS:树形图广泛用于做路径分析比如客户行程分析

因此,希望你现在受到启发,用数据和算法来创建新年祝福!祝大家新年有一个好的开始!!!

额外资源

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

你可以访问我的网站,用零编码制作新年祝福和其他分析。https://experiencedatascience.com

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

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

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

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

Youtube 频道

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

HTAP 莱克豪斯的 NewSQL 和数据的未来

原文:https://towardsdatascience.com/newsql-lakehouse-htap-and-the-future-of-data-69d427c533e0

现代数据库和数据的未来

卢卡·布拉沃在 Unsplash 上的照片

像编程语言和操作系统一样,数据库是必不可少的技术。业务需求推动技术发展。在过去的 30 年里,从 SQL 到 NoSQL 和 NewSQL,出现了数百种不同的数据库。他们有两个主要的工作负载: OLTP (在线事务处理)和 OLAP (在线分析处理),在各种硬件架构的共享一切(例如 Oracle RAC )、共享内存共享磁盘无共享和混合(例如雪花)。

数据库怀旧

Charles Bachman 在 20 世纪 60 年代早期开发了第一个数据库,在过去的 30 年中,数据库呈指数级增长。一开始,研究了不同的数据库查询和模型,包括 SQL、XML 和面向对象。经过十多年的竞争,Oracle、SQL Server 和 MySQL 通过标准化查询语言 SQL 和遵守 ACID ( 原子性一致性隔离性持久性)几乎统治了商业市场和开源社区。

随着数据量、种类和速度的增长, NoSQL 因性能效率、模式灵活性和新功能而首次亮相,例如, RedisMongoDBCassandraNeo4JElasticsearch 等。NoSQL 有键值存储、文档数据库、面向列的数据库、图形数据库等。但是上限定理和缩放性能抑制了它们的持续发展。许多 NoSQL 数据库已经妥协或优化为最终一致性非规范化。NoSQL 数据库的属性通常可以用一个松散的基础概念来描述,在遵守 CAP 定理时,它更喜欢可用性而不是一致性。BASE 代表基本可用性、软状态和最终一致性。

现代数据库要求是分布式的和可伸缩的。出现了许多扩展数据库的机制:复制(主-从或主-主)、联合分片非规范化物化视图、SQL 调优、NoSQL 等。而 RaftPaxos 是分布式数据库的两个重要共识算法。

NewSQL 是一类现代关系数据库,旨在为 OLTP 工作负载提供与 NoSQL 相同的可扩展性能,同时仍然使用 SQL 并保持传统数据库的 ACID 保证。

“数据仓库”这个名字是为 OLAP 数据库创造的,但它现在很少被称为数据库了。数据仓库是商业智能的核心组件,用于数据分析和商业洞察。十年前,当大数据平台出现时,它变得暗淡了。人们从传统的数据仓库转向使用数据平台,直到云重新赋予数据仓库以新的性能和可扩展性。

凭借高性能和高可扩展性的数据云,一个新时代的到来催生了一个全新的数据平台生态系统,现代数据堆栈

云改变了游戏

云技术从两个主要方面从根本上改变了数据库游戏:卓越运营和系统架构。云以两种方式实现数据库操作的自动化或半自动化:云托管(半托管甚至完全托管)和云本地。云重塑了数据库的架构,主要是通过分离存储和计算。存储或计算可以独立扩展,以提高效率、性能、灵活性和成本。这种分离的架构还可以为数据库系统集成不同类型的存储和计算,以实现整体高性能和新功能。

分离存储和计算可能是云中的一个基本概念,但 EMRFS (EMR 文件系统)应该是分离 Hadoop 文件系统(HDFS)以在 S3 存储 HDFS 的第一个尝试。沿着这个方向,云 NoSQL(例如, DynamoDBBigTable )和云原生 SQL 数据库(又名 cloud NewSQL)在多个云提供商、AWS、Azure、GCP 等之间激增。

对象存储是云中的早期存储之一,就像亚马逊 S3 一样。S3 是第一个目的简单的对象存储服务(通过键放置/获取对象),正如它的名字(简单存储服务)所暗示的。但是 S3 由于其简单性、低成本、高可用性、可扩展性等已经成为云基础。进而随着 S3 查询到位演变成 数据湖:S3 Select,S3 亚马逊 Athena,S3 亚马逊红移谱(EB 级别)。

数据湖解释(作者)

纽什尔、莱克豪斯和 HTAP

几年前,我们对 NewSQL 和 data lake 感到兴奋不已。现在数据仓库在被 Databricks 高调宣传后已经成为一个时髦的词。不久,像 Presto 这样的人就意识到它只是在对象存储上运行快速 SQL,具有数据仓库性能和数据湖灵活性。然后德雷米奥星爆等人很快就参军了。

Data Lakehouse 不仅仅是一个时髦的词,而是一个卓越而有意义的架构统一策略。它集成了数据湖和数据仓库,以提高性能、灵活性和成本效益,并消除数据孤岛和 ETL 过程。它统一了所有数据,以简化数据工程流程,同时支持 BI 和 AI 工作负载。

数据湖屋解释(作者)

另一方面, HTAP 随着谷歌 AlloyDB 和雪花 Unistore 的发布,给火热的现代数据栈火上浇油。类似地,Oracle、SQL Server 和其他公司在近十年前就配备了这一功能。然而,目前的 HTAP 和莱克豪斯有一个共同的目标,那就是消除从 OLTP 到 OLAP 或从数据湖到数据仓库的 ETL。

当前的 HTAP 是一个支持 OLTP 和 OLAP 工作负载的单一系统架构,不像一些早期的数据库可以配置为 OLAP 或 OLTP,但不能一起配置。有两种常见的 HTAP 架构:在内部将 OLAP 和 OLTP 联合为单个 HTAP 系统(例如, TiDB )以及将 OLTP 和 OLAP 架构与存储中的 TP 行和内存中的 AP 列集成,反之亦然(例如,AlloyDB 和 Oracle MySQL HeatWave )。

亚马逊 Aurora 是一种关系数据库服务,完全兼容 MySQL 和 PostgreSQL。它是第一个云原生的NewSQL数据库,并被重新发明以分离数据库存储和计算。简而言之,它将传统数据库集群的存储统一到云存储中,并允许独立地横向扩展数据库计算层。与集群上的 Oracle RAC 不同,这是一种在云中共享一切的体系结构。

亚马逊极光架构

Google Spanner 是另一个云原生的 NewSQL 数据库。雪花采用了类似的云原生架构,为云数据仓库解耦存储和计算。不幸的是,亚马逊红移更早推出,但使用了类似 EMR 的集群托管架构,首战输给了雪花。

数据的未来

今天,每个公司都是数据驱动的公司。数据变得比以往任何时候都更加重要。随着业务和技术的变化,数据库和数据堆栈不断快速发展。展望数据的未来,有五个令人兴奋的领域:统一 BI 和 AI、专门构建的网格、多云战略、智能数据和数据资产。

数据的未来(作者)

统一 BI 和 AI :我们致力于统一所有数据,以消除数据孤岛、ETL 等。但这不是目标。目标应该是释放所有数据的商业价值,并支持 BI 和 AI 的整个数据环境,包括从描述性到诊断性、预测性和规范性分析的所有数据分析。从数据到商业价值的旅程通常涉及多个人:数据工程师、数据分析师、数据科学家、ML 工程师等。统一 BI 和 AI 不仅可以消除数据孤岛和 ETL,还可以简化管道并提高利益相关者的生产力。Data Lakehouse 是一个巨大的飞跃,但这一努力才刚刚开始。

特制网格:数据库技术融合是一种趋势,比如 NewSQL、Lakehouse、HTAP。但是我们知道,NewSQL 或 data lakehouse 仍然是 OLTP 或 OLAP 的一种类型。上限定理仍然成立。当前的 HTAP 解决方案可能主要是 OLTP 或适合小型工作负载。采用目前市场上可用的 HTAP 作为大型企业数据仓库或非结构化数据的数据湖几乎是不切实际的。专门构建的数据库可以更好地满足不同的业务目标,如性能、可扩展性或/和特定用例(例如,时序数据、图表、搜索等)。).专门构建的数据库网格可以通过聚合层抽象数据库,以实现互连、统一的数据服务和一致的治理。然而,当我们拥有像量子计算或超高速网络和存储这样的超级强大的计算时,情况可能会发生变化。

多云战略:多云战略联合了孤立的公共云和私有云,无需移动数据。它可以提高多个云提供商的服务可用性,通过近似计算减少延迟,实现特定云生态系统或市场的独特功能,通过更多云产品扩展全球可用性,并增强数据合规性和法规。 StarburstDremio 是两家领先的云计算数据平台初创公司。多云战略还推动了数据可观察性、数据编目、数据共享和数据编排的浪潮。

智能数据:AI 与数据互使有三个域:AI for Data (AIData),AI for Database(AI ops的一部分),Data for AI(涉及特征工程MLOps )。智能数据是数据的 AI,使数据在数据质量、数据治理、数据血统、元数据、语义以及来自分析和 AI 的新数据方面具有智能。生成式人工智能将在智能数据中发挥关键作用。到 2025 年, 10%的数据将由生成式人工智能模型产生。这些数据可以是语音、视频、图像、文本、结构化数据、代码等。它们是具有内置丰富元数据的高质量数据。这意味着当前的数据库,包括 data lake 和 data lakehouse,由于其丰富的元数据和指数增长,可能不是最佳的。

数据资产:是将数据作为组织或个人的数据库或存储中的数字资产进行管理的原则。这样的数据库不仅是一个数据管理系统,而且还提供或集成了数据可观察性、安全性和隐私管理、定价、数据生命周期管理等等。它与 OLAP 和 OLTP 有关,尽管它似乎在 OLAP 社区更活跃。与组织的传统数据资产不同,它们可以属于个人。然后,这个数据资产可以无缝集成到 web3 中,并可以用 NFT 进行铸造。因此,随着 web3 的发展,这意味着很多。

数据无处不在。更令人兴奋的是,我们期待数据平台和服务的未来能够让商业和生活变得更加轻松和幸福。

参考

  1. Amazon DynamoDB:一个可扩展的、可预测的性能和完全管理的 NoSQL 数据库服务:https://www . Amazon . science/publications/Amazon-dynamo db-A-Scalable-predictable-Performant-and-full-Managed-no SQL-Database-Service
  2. 亚马逊红移再发明:https://www . Amazon . science/publications/Amazon-Redshift-再发明
  3. 亚马逊雅典娜中通过融合的计算重用:https://www . Amazon . science/publications/computation-reuse-via-fusion-in-Amazon-Athena
  4. 亚马逊极光:关于避免 I/o、提交和成员资格变更的分布式共识:https://www . Amazon . science/publications/Amazon-Aurora-On-avoiding-distributed-consensus-for-I-Os-commits-and-membership-changes
  5. DB 引擎排名:【https://db-engines.com/en/ranking
  6. 什么是数据湖?https://AWS . Amazon . com/big-data/data lakes-and-analytics/what-a-data-lake/

牛顿-拉夫森——解释和可视化

原文:https://towardsdatascience.com/newton-raphson-explained-and-visualised-23f63da21bd5

理解牛顿拉夫森算法

牛顿-拉夫森方法(图片由作者提供)

以艾萨克·牛顿和约瑟夫·拉弗森命名的牛顿-拉夫森方法是一种设计的求根算法,这意味着它的目标是找到函数 f(x)=0 的值 x 。从几何学上讲,我们可以把它看作是目标函数穿过轴 x 时的 x 的值。

然而,用例并没有就此结束,事实上,这个算法对于跨越许多领域的广泛用例来说是非常通用的。例如,通过重新构造感兴趣的函数,您可以搜索 x 的值,从而产生您选择的值,而不是被限制为 0。

实践中的一个复杂用例是使用 Black-Scholes 公式反解金融期权合同的隐含波动率。然而,牛顿-拉夫森算法也可以用于一些简单的事情,如回解,以找到你需要在期末考试中获得多少分数才能在给定的连续评估成绩中获得 A。事实上,如果你曾经使用过微软 Excel 中的求解器功能,那么你可能使用过像牛顿-拉夫森这样的求根算法。

分解公式

牛顿-拉夫森公式(图片由作者提供)

虽然这个公式本身非常简单,但是乍看之下,想象它实际上在做什么可能有点棘手。

首先,让我们回顾一下总体方法:

  1. 初步猜测根可能在哪里

作者图片

2.应用牛顿-拉夫森公式获得比初始猜测更接近根的更新猜测

3.重复步骤 2,直到新的猜测足够接近。

等等,够近了?是的,不幸的是,Newton-Raphson 方法给出了根的一个近似值,尽管通常对于任何合理的应用来说它已经足够接近了!你可能会问我们如何定义足够近?我们什么时候停止迭代?

通常,Newton-Raphson 方法的实现将有两种处理何时停止的方式。首先,如果你的猜测从一个步骤到下一个步骤的变化不超过一个阈值,比如说 0.00001,那么算法将停止,并说最近的猜测足够接近。第二种方法不太理想,只是简单地说,如果我们达到一定数量的猜测,但仍然没有达到阈值,那么我们就放弃。

牛顿-拉夫森公式(图片由作者提供)

从公式中,我们可以看到,每个新的猜测只是我们以前的猜测调整了一些神秘的数量🔮。然而,如果我们通过一个例子来观想这个过程,很快就会清楚发生了什么!

示例函数

示例函数的导数

作为一个例子,让我们考虑上面的函数,并初步猜测为 x= 10(注意这里实际的根是在 x= 4)。牛顿-拉夫森算法的最初几个猜测在下面的 GIF 中被可视化👇

牛顿-拉夫森过程(GIF 由作者提供)

我们最初的猜测是在 x= 10。现在,请记住,为了计算我们的下一个猜测,我们需要评估函数本身及其在 x= 10 的导数。然而,在 10 处计算的函数导数仅仅给出了该点处切线曲线的斜率。该切线在 GIF 中被绘制为切线 0

现在,看看下一个猜测相对于前一个切线出现在哪里,你注意到什么了吗?下一个猜测出现在前一条切线穿过 x- 轴的地方。这就是牛顿-拉夫逊法的高明之处!

作者图片

作者图片

事实上,商f(x)/f’(x)简单地给出了我们当前猜测与切线穿过 x 轴的点之间的距离(在 x 方向上)。正是这个距离告诉我们每次要更新我们的猜测多少,正如我们在 GIF 中看到的,当我们接近根本身时,更新越来越小,这阻止了我们超越。

功能用手很难区分怎么办?

在上面的例子中,我们有一个很容易手动微分的函数,这意味着我们可以毫不费力地计算f’(x)。然而,在现实中可能不是这样,有一些有用的技巧来近似导数,而不需要知道它们的解析解。

这些导数近似方法超出了本文的范围,但是如果你感兴趣,你可以在这里阅读更多关于有限差分方法的内容。

问题

敏锐的读者可能已经从上面的例子中发现了一个问题,即牛顿-拉夫森方法只能识别一个根,尽管我们的示例函数有两个根( x= -2 和 x= 4)。这当然是一个问题,但不是这种方法的唯一缺点。关于牛顿-拉夫森方法的更多有用信息,比如收敛的必要条件,你可以在这里查阅维基百科页面

🙋‍♂️感谢你的阅读!请随意回答任何问题🙋‍♀️

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

娱乐数据科学的新前沿

原文:https://towardsdatascience.com/next-frontiers-in-entertainment-data-science-b63860124f3e

超越什么和什么时候,超越我们如何思考和感受

Ahmet Yal nkaya 在 Unsplash 上拍摄的照片

像无数其他行业一样,娱乐行业正在被数据所改变。毫无疑问,数据总是在指导演艺圈决策中发挥作用,例如,电影跟踪调查[1]和尼尔森数据[2]的形式。但随着流媒体的日益突出及其实现的无缝消费测量,数据在理解、预测和影响电视和电影消费方面从未如此重要。

作为娱乐领域的数据科学家和媒体偏好研究人员,我有幸置身于分析电视/电影消费数据的行业前沿,能够跟上世界各地机构的媒体偏好研究。正如接下来的各种引用所表明的那样,这里提出的组件概念本身并不是什么新东西,但我想应用我的背景知识来汇集这些想法,为我认为是增强我们理解、预测和影响全球视频内容消费的能力的下一个前沿领域制定一个结构化的路线图。虽然数据可以在内容生命周期的许多早期阶段发挥作用,例如在绿色照明过程[3]或生产[4]中,而且我要说的内容可能与各个阶段相关,但我主要是从更下游的角度来写的,随着内容的消费,在发布前后,正如我在行业和学术工作中培养的那样。

除了查看和元数据

照片由约书亚·索蒂诺Unsplash 拍摄

当您在娱乐领域工作时,您最终会处理大量标题消费数据和元数据。在很大程度上,这是不可避免的——所有“元数据”和“观看数据”的真正含义是关于正在观看什么以及观看多少的数据——但是很难不开始感觉到基于这种数据的模型,正如在内容相似性分析中常见的那样(例如[5][6][7][8]),输出落入熟悉模式的结果。例如,这些天来,当我看到“类似的节目/电影”推荐时,我脑海中的一个声音会说,“这可能是基于元数据的推荐,”或者“那些看起来像基于收视率的推荐”,这是基于我在使用这些模型时所看到的。当然,我不能 100%肯定,对于可能使用更多现成方法的小型服务,声音更有信心;在更大的平台上,推荐经常是天衣无缝的,以至于我不会去想缺陷,但是谁知道会有什么神奇的酱料加入其中呢?

我并不是说查看数据和元数据将不再重要,也不是说使用这些数据的模型无法解释消费的巨大差异。我想说的是,当谈到最佳分析和预测收视率时,仅仅这些因素能让我们走多远是有限的——我们需要新的方法来增强对观众及其与内容的关系的理解。我们想了解和预见标题 X 在时间点 A 之后的流行,“它在 A -1 时流行,它将在 A 时流行”,或者,“标题 YX 相似,曾流行,所以 X 将流行”,特别是由于经常在 A -1 或之间的相似性让我们来谈谈一种数据,我认为这种数据对于提高对收视率的理解和预测能力至关重要。

心理测量学:谁在观察,为什么

照片由麦迪森·柳文欢Unsplash 上拍摄

当谈到媒体消费时,人们喜欢谈论人口统计学。事实上,任何上过电影商务课的人都可能熟悉“四象限电影”,或者一部对 25 岁以上和 25 岁以下的男女都有吸引力的电影。但是人口统计学在解释和预测效用上是有限的,因为它们通常会告诉我们谁是谁,但不一定会告诉我们为什么。

这就是心理测量学(又名心理测量学)可以提供帮助的地方。同一人群中的个体很容易有不同的倾向、价值观和偏好;一个例子是将男人或女人分成倾向于 DIY 者、早期采用者、环保主义者等的能力。基于它们在不同维度上的测量特征[10]。类似地,不同人口统计的人很容易有相似的特征,例如高度寻求刺激,乐于接受新的体验,或者在政治上认同左/右。这种心理测量变量确实被证明会影响媒体偏好——例如,令人愉快的人更喜欢脱口秀和肥皂剧[11],寻求更高感觉的人更喜欢暴力内容[12][13]——并提高推荐模型的能力[14][15]。我自己的研究表明,与单独的人口统计数据相比,即使是简化的心理测量方法也可以在模型拟合类型偏好数据方面产生改进[16]。消费者数据公司已经开始认识到心理测量数据的重要性,其中许多公司以某种形式将这些数据纳入他们的服务[17][18][19]。

心理测量数据在个人层面上可能是有用的,它们通常被收集或汇总,以提供群体层面(受众、用户群、国家等)的各种心理测量特征。一些这样的数据可能会在来源处“预先汇总”,例如 Hofstede 的文化维度[20]。就收集而言,当直接收集观众中的所有观众不可行时(例如,当您无法调查数百万用户时),来自回应观众的自我报告调查数据的“种子”集可用于使用最近邻法估算类似非回应者的值。心理测量数据在冷启动问题场景中也可能是有益的——如果你没有关于特定观众观看什么或他们将观看特定标题多少的直接数据,那么关于他们的特征的指向内容类型的数据难道不是有用的吗?

作为观众-内容特质互动的消费

埃里克·麦克林在 Unsplash 上的照片

上面的部分特别讨论了心理测量学,但是放大一点,它更广泛地推动的是观众/观众特征空间的扩展,超越了人口统计学和行为统计学。这是因为所有的消费本质上都是观众特征和内容特征之间的互动。这个概念比听起来更简单,也更好理解;它真正的意思是,观众的某些元素(观众特质)意味着他们更多(或更少)被一段内容的某些元素(内容特质)所吸引。甚至熟悉的类型偏好的刻板印象——儿童更喜欢动画,男人更喜欢动作片,等等。—固有地关注观众-内容特质的相互作用(在上述示例中,观众年龄-内容类型,观众性别-内容类型),以及前述关于观众心理测量对内容偏好的影响的研究([11][12][13][16])也属于这一范式。

我们拥有的观众特征越多,我们可以考虑的可能与某种内容特征相互作用的东西就越多,从而影响他们消费该标题的兴趣。反过来,这也意味着拥有新形式的数据标题也是有益的。人们似乎更容易“深入”标题数据,以元数据的形式(流派、演员、工作人员、工作室、奖项、平均评论等。),但仍然有扩展标题端的空间,特别是如果像上面建议的那样通过收集心理测量等来扩展观众端的数据。在这方面,标签和标记是一个很好的起点。通过捕捉机器自己仍然难以检测的潜在信息,人类标记尤其有益,例如幽默、讽刺、挖苦等。[21][22] —但是自动化流程可以提供有用的一致的基线内容标签[23]。然而,如今,标签只是生成额外标题端数据的开始。可以从标题的音频和视频中设计出各种各样的特征,也可以从文本中提取故事的情感弧线。

一旦你从观众-内容互动的角度考虑消费,并在观众和标题两方面扩大数据收集,可能性就真的出现了。例如,您可以对片头中角色的种族/民族和性别进行编码,并查看片头演员/工作人员和流媒体平台的典型用户之间的人口统计相似性如何影响片头的成功。或者,你可能想对标题的信息感觉值进行编码,看看这与标题对某个寻求高度感觉的群体的吸引力有什么关系。或者,您可能希望使用来自 OpenSubtitles 等的数据来确定系统中所有标题的叙事弧类型,并查看是否出现了某些弧对某些心理图形的个人的吸引力的任何模式。

解析管道:感知、兴趣、响应

昆腾·德格拉夫在 Unsplash 上的照片

最后,需要更细致地考虑消费渠道,从兴趣到反应。虽然很容易被归为消费者对某个标题感觉的“好”信号,但对某个内容感兴趣、观看和喜欢完全是两码事。如果可能的话,完整的观看过程应该被分解成消费前和消费后两个阶段。

感知(消费前):不同人口统计特征[27]的个体,可能具有不同的心理特征,会对同一媒体产品产生不同的感知。这些感觉可以由产品的品牌设计、字体、颜色和广告等要素来塑造[28][29][30]。可以说,感知对管道的下一阶段有重要影响。

兴趣,和选择(预消费):首先,虽然和前者相关但肯定增加了后者的可能性,值得注意的是,兴趣(又名偏好)并不等同于选择(又名选择)[31]。虽然对其中一个的分析可能经常与另一个相关,但我们不能总是假设一个对某样东西感兴趣或很可能对某样东西感兴趣的人总是会选择消费它。像推理行动模型[32]这样的模型很好地说明了这一点,在该框架内,对观看电影感觉良好的个人可能不会观看该电影,因为他们认为观看所述电影是不利的。检查驱动兴趣选择转换的因素可能是有益的。

反应(消费后):最后,还有个人看完一段内容后的感受。这可以简单到他们是否喜欢它;尽管人们很容易将高收视率与《哇,人们真的很喜欢那部电影》相提并论,但在查看数据集时,关键是要记住,人们看某部电影的程度和他们是否喜欢这部电影是相关的,但最终是分开的事情,正如任何一个对这部电影感到兴奋但又被它的平庸所击垮的人可以证明的那样;我自己的研究表明,对看不见的内容感兴趣的效果可能不同于,甚至是相反的,对喜欢看得见的内容的效果。除了喜欢,回应还可以包括一些元素,如观众对内容的情感感受[34],他们与角色的关系[35],他们沉浸在故事情节中的程度[36],等等。

媒体偏好和消费不需要被认为是一个单一的、固定的过程,而是以这种方式分离出来,一个流动的模块化过程,其中上游过程的战略管理可以影响期望结果的可能性,无论它们可能是什么。我们如何有选择地优化不同人口统计和心理统计群体对媒体产品的感知,以获得对某个标题的最大兴趣,或者优化预期的下游结果?我们如何将兴趣转化为选择?某些上游感知或过高的兴趣水平是否会与某个标题的内容产生负面影响,从而导致对该标题的最终反应比感知不同或兴趣不那么极端时更加负面?此外,虽然我提供了潜在的关键机制与管道的每个步骤相关,但某些机制可能与管道的多个阶段或不同阶段相关,例如,(潜在的)观众角色相似性([37][38])可能会影响广告曝光后对标题的感知和兴趣,而社交网络效应([39])可能意味着某些个人的消费后反应会严重影响其他个人的消费前兴趣。

结论

照片由 Unsplash 上的 Aziz Acharki 拍摄

作为一个行业,我们刚刚开始触及数据如何帮助我们理解、预测和影响内容消费的表面,这些只是我的一些想法,我认为随着数据科学在娱乐领域变得越来越普遍和关键,这些将是重要的考虑因素。受众心理测量将有助于增强对受众的了解,这是人口统计学无法单独做到的;考虑新受众和内容特征之间的互动将提供卓越的战略洞察力和预测能力;细致入微地考虑从兴趣到反应的整个消费渠道将有助于优化预期结果。如果你对此感兴趣,并且想和我聊更多,请随时添加我并联系 LinkedIn

在撰写本文时,丹尼·金(宾夕法尼亚大学博士; 是南加州大学安嫩伯格传播与新闻学院的研究附属机构,并将很快在娱乐界开创一个令人兴奋的新职位!他的专业领域包括媒体偏好、数据科学、媒体心理学、媒体管理和媒体品牌。

参考

[1]新闻周刊工作人员,,好莱坞,电影,追踪,追踪报道 (2002),新闻周刊。

[2]尼尔森,庆祝 95 年创新 (2018),尼尔森。

[3] A. Massey,人工智能和数据分析能带来轰动一时的电影吗? (2020),ESRI《在哪里》杂志。

[4] J. Walraven,网飞数据科学与娱乐制作(2019),数据科学沙龙。

[5] M. Soares,P. Vianna,为更好的基于电影内容的推荐系统调整元数据 (2015),多媒体工具和应用。

[6] S. Nazir,T. Cagali,C. Newell,M. Sadrzadeh,电视节目多模态内容向量的余弦相似性 (2020),arxiv.org。

[7] T. Anwar,V. Uma,推荐系统方法与使用协同过滤的电影推荐的比较研究 (2021),国际系统保证工程与管理杂志。

[8] R. Lavanya,B. Bharathi,利用协同过滤方法解决数据稀疏性的电影推荐系统 (2021),ACM Transactions on Asian and Low-Resource Language Information Processing。

[9] J. Hellerman,什么是四象限,什么是四象限电影(定义及实例) (2021),无电影学派。

[10] R. Abzug,电视观众研究的未来 (2011),南加州大学安嫩伯格分校诺曼·李尔中心。

[11] G. Kraaykamp,K. van Eijck,个性、媒体偏好和文化参与 (2005),个性和个体差异。

[12] K. Greene,M. Krcmar,预测接触和喜欢媒体暴力:使用和满足方法 (2005),传播研究。

[13] M. Slater,疏离感、攻击性和感觉寻求是青少年使用暴力电影、计算机和网站内容的预测因素 (2006 年),《传播杂志》。

[14] E. Khan,M. S. Hossain Mukta,M. Ali,J. Mahmud,从个性和价值观预测用户的电影偏好和分级行为 (2020),ACM Transactions on Interactive Intelligent Systems。

[15] O. Nalmpantis,C. Tjortjis,《50/50 推荐器:一种将个性融入电影推荐系统的方法》 (2017),EANN 2017:《神经网络的工程应用》。

[16] D. Kim,在电视上寻找快乐和意义,捕捉自应用内:通过移动应用 (2020)自我报告的快乐主义和享乐主义对电视消费的影响,《广播和电子媒体杂志》。

[17] N. Ripley,康姆斯克介绍 Plan Metrix 多平台 (2017),康姆斯克。

[18]尼尔森,尼尔森数据即服务(DAAS) ,尼尔森。

[19]分子。分子推出心理学&媒体消费洞察 (2019),分子。

[20]霍夫斯塔德的见解。民族文化。霍夫斯泰德洞察。

[21] D. Riffe,S. Lacy,B. Watson,F. Fico,分析媒体信息 (2019),Routledge。

[22] S. Lewis,R. Zamith,A. Hermida,大数据时代的内容分析:计算和手动方法的混合方法 (2013),广播杂志&电子媒体。

[23] D. Eck,P. Lamere,T. Bertin-Mahieux,S. Green,音乐推荐社交标签的自动生成 (2007),NIPS 07:第 20 届神经信息处理系统国际会议论文集(2007)。

[24] Y. Deldjoo,M. Dacrema,M. Constantin,H. Eghbal-zadeh,S. Cereda,M. Shcedl,b .约内斯库,p .克雷莫内西,电影基因组:缓解电影推荐中的新项目冷启动 (2019),用户建模与用户适配交互。

[25] M .德尔·维奇奥,a .哈尔拉莫夫,g .帕里,g .波格雷纳,《用数据科学提高好莱坞的生产力:利用电影的情感弧线推动娱乐产业的产品和服务创新》 (2021),运筹学学会杂志。

[26] O. Toubia,J. Berger,J. Eliashberg,如何量化故事的形状预测其成功,美国国家科学院院刊。

[27] D. Kim,媒体品牌个性认知的人口统计学差异:多水平分析 (2018),国际媒体管理杂志。

[28] T. Lieven,B. Grohmann,A. Herrmann,J. Landwehr,M. van Tilburg,品牌设计对品牌性别认知和品牌偏好的影响 (2015),《欧洲营销杂志》。

[29] E. Seimiene,E. Kamarauskaite,品牌元素对品牌个性感知的影响 (2014),Procedia —社会和行为科学。

[30] M. Favier,F. Celhay,g . Pantin-sohire,少是多还是烦?包装设计的简单性和品牌感知:香槟的应用 (2019),零售和消费者服务杂志。

[31] S. Knobloch-Westerwick,(2015),Routledge。

[32] M. Fishbein,健康促进的理性行动方法 (2008 年),医疗决策。

[33] D. Kim,被我们是谁和我们渴望成为谁吸引到银幕上:电影偏好中的品牌自我一致性差异 (2020),国际媒体管理杂志。

[34] R. Nabi,M. Green,叙事情感流在促进说服性结果中的作用 (2015),媒体心理学。

[35] H. Hoeken,M. Kolthoff,J. Sander,故事视角和人物相似性作为认同和叙事说服的驱动因素 (2016),人类传播研究。

[36] M. Green,T. Brock,换位在公共叙事说服力中的作用 (2000),《人格与社会心理学杂志》。

[37] J. Cohen,M. Hershman-Shitrit,与电视角色的中介关系:人格特质中感知和实际相似性的影响 (2017),文学的科学研究。

[38] R. Matthew Montoya,R. Horton,J. Kirchner,实际的相似是吸引的必要条件吗?实际相似性和感知相似性的荟萃分析 (2008),《社会与个人关系杂志》。

[39] J. Krauss,S. Nann,D. Simon,K. Fischbach,P. Gloor,通过情感和社会网络分析预测电影成功和奥斯卡奖 (2008),ECIS 2008 会议录:欧洲信息系统会议。

一个成功的人工智能项目的九个关键角色

原文:https://towardsdatascience.com/nine-critical-roles-for-a-successful-ai-project-20525ee47467

交付一个人工智能系统是困难的。这就是为什么你需要一个人才和技能互补的高绩效团队。

成功交付人工智能项目的最佳团队是什么?这是一个很难回答的问题。这将取决于项目的类型和你工作的组织的类型。利用我从指导不同行业的许多公司中收集的知识,这里有九个决定人工智能项目成败的关键角色。

一名员工可以根据其技能组合担任多个角色。这里的想法是,您的项目团队涵盖所有这些,以最大化您成功项目的机会。

人工智能项目团队中的九个关键角色

AI 团队项目按角色组成,由作者制作

数据科学

在人工智能项目中,数据科学是一个显而易见的学科。我们不断听说,数据科学家是构建人工智能解决方案所需的唯一角色。首先,它不是,其次,一个人工智能项目要成功,需要发生许多活动。单个数据科学家无法解决所有这些问题。我不相信独角兽。

我通常将数据科学活动分成三个独立的角色:

  • 数据分析:数据是一个 AI 系统的油。正确分析用于训练 AI 系统的数据至关重要。没有机油,它可能会永久地完全卡住你的发动机。
  • 机器学习(ML)建模:一旦数据被定义和清理,机器学习模型将被训练、验证和测试以生成预测。然后你可以进行实验,并有希望将你的预测整合到一个系统中。
  • UX 设计 : UX 设计是人工智能系统中最容易被忽视的部分之一。在我看来,它是如此的关键。最终用户将如何访问系统的输出?你如何确保他们理解并信任系统的结果?UX 的设计师也可以研究模型的可解释性,并将其翻译成一种可以理解的非技术语言。

发展

不幸的是,发展仍然被低估。然而,开发和部署一个可操作的人工智能系统需要许多小时的软件开发。您将认识到拥有一个功能性解决方案所需的非机器学习基础设施、流程和工具的数量。

增强的 AI 系统组成,来自机器学习系统中隐藏的技术债务

你需要具体的专业知识来操作人工智能,并围绕数据、机器学习模型和由你专注于数据科学的角色创建的 UX 建立一个强大的系统。

  • 解决方案架构:如上图所示,构建一个系统需要很多硬件和软件元素。这种技能对于绘制人工智能系统的示例性软件架构以满足最终用户的需求至关重要。
  • 数据库和软件开发:突发新闻:人工智能解决方案就是软件解决方案。一个特定的,但仍然是一个软件解决方案。因此,对数据库、脚本和 API 的健壮性和效率要求。如果你只依赖数据科学家来提供一个人工智能解决方案,你会失望的,因为很少有数据科学家既掌握软件开发又掌握数据科学。再说一次,我不相信独角兽。
  • 解决方案运营:人工智能解决方案的解决方案运营是 DevOps 和 MLOps 的结合。DevOps 是一套实践,旨在缩短开发生命周期,并提供高质量的持续交付。相比之下,MLOps 是机器学习应用和工作流的自动化和生产化过程(来源: phdata )。

DevOps 周期,来自 MLOps vs. DevOps:有什么区别

商业

组织内的任何人工智能项目都应该有很强的商业考虑,因为技术不能解决任何问题,除非它与商业现实相一致。

  • 行业知识:这是一个 AI 项目中最关键的角色。是的,一个非技术性的角色。产品所有者(或 PO)是这个角色的通用名称。一个伟大的采购订单可以产生直接的利益,同时通过开发业务规则和启发以及塑造人工智能项目的业务需求来降低风险。PO 还确保项目团队学习对 AI 解决方案至关重要的行业知识,并在整个项目中与业务利益相关者保持一致。

人工智能交付中的帕累托原则

人工智能交付中的帕累托原理,作者提出

  • 项目管理:简单来说,你在一个 AI 项目中会遇到的大部分问题,只要你对项目管理得当,都是可以处理的。项目经理指导团队,以便在给定时间表和预算的情况下,交付满足业务需求的高质量项目。这是一条很好走的线,所以我建议你在招聘项目经理时寻找经验。
  • 变革管理:你可以建立人类历史上更好的 AI 系统,但如果没人用,你只是失去了一个机会。还有钱。和时间。用户测试期间的交流、培训和支持是确保利益相关者和最终用户最大程度采用的重要活动。

你认为这篇文章还应该涉及其他角色吗?以上欢迎随意评论!

在 Rust 中创建程序宏的九条规则

原文:https://towardsdatascience.com/nine-rules-for-creating-procedural-macros-in-rust-595aa476a7ff

来自anyinput的实践经验,这是一个新的宏,可以轻松接受类似字符串/路径/迭代器/数组的输入

锈蟹编程本身——来源:https://openai.com/dall-e-2/

更新:你可以看到这篇文章的 YouTube 版本。我出席了拉斯特林茨聚会。

我喜欢 Rust 编程语言,但它并不完美。例如,您知道如何编写一个 Rust 函数,它接受任何类似字符串的东西作为输入吗?接受任何类型路径的迭代器怎么样?你能写一个接受一个Vec<f32>作为ndarray::ArrayView1的函数吗?

Rust 函数可以接受所有这些输入,但是我发现语法很难记忆和阅读。因此,我创建了anyinput宏来为我和其他 Rust 程序员记住复杂的语法。(参见 https://crates.io/crates/anyinput。)

在创建anyinput的时候,我学到了九条规则,可以帮助你在 Rust 中轻松创建程序宏。规则是:

  1. 使用 Rust workspace 和proc_macro2在普通(非宏)项目中开发、调试和单元测试您的宏。
  2. 使用synproc_macro2quote在文字代码、标记、语法树和字符串之间自由转换。
  3. 创建易于调试的单元测试,报告宏所做的和预期的任何差异。
  4. 使用 AST Explorer[syn](https://docs.rs/syn/latest/syn/)文档来理解 Rust 语法树。使用 Rust 的模式匹配和结构/枚举访问来解构语法树。
  5. parse_quote!和 Rust 的结构更新语法构建语法树。
  6. 使用syn的 Fold 特性递归地遍历、析构和构造语法树。
  7. 使用proc_macro_error返回人体工程学和可测试的错误。
  8. 创建集成测试。包括基于trybuild的 UI 测试。
  9. 遵循优雅的 Rust API 设计的规则,特别是,吃你自己的狗粮,使用 Clippy,写好文档。

Rust 强大的宏系统让我们用 Rust 写 Rust。系统提供两种宏。对于第一种,您使用macro_rules!宏来声明一个新宏。一般很简单。可悲的是,macro_rules!不能做我想做的事。对于第二种类型的程序性宏,您可以获得更大的能力,因为您是在 Rust 中编程的。

过程宏有三种类型:

我的宏,anyinput 是属性宏,但是这些规则适用于所有三种口味。

下面是一个使用中的anyinput宏的简单例子:

任务:创建一个函数,将2加到任何类似字符串的东西的长度上。

下一个例子表明anyinput支持多输入和嵌套。

任务:创建一个有两个输入的函数。一个输入接受任何类似迭代器的东西usize。第二个输入接受任何类似迭代器的东西或类似字符串的东西。该函数返回数字和字符串长度之和。将该函数应用于范围1..=10&str的一部分。

这两个例子使用了AnyStringAnyIter。宏也理解AnyPathAnyArray和(可选)AnyNdArray

如何将anyinput宏应用到用户函数中?在幕后,它用适当的 Rust 泛型重写了函数。它还向函数中添加了一些行,以便有效地从任何顶级泛型转换为具体类型。具体来说,在第一个示例中,它将len_plus_2函数重写为:

这里的AnyString0是一个通用类型。生产线let s = s.as_ref();s从通用型AnyString0转换为具体型&str

创建程序宏需要许多决策。根据我对anyinput的经验,以下是我推荐的决定。为了避免含糊不清,我将把这些建议表述为规则。

规则 1:使用 Rust workspace 和 proc_macro2 在普通(非宏)项目中开发、调试和单元测试您的宏

如果我们设置得恰到好处,我们可以像常规 Rust 项目一样开发和调试宏的核心代码。比如我用 VS 代码设置为 Rust 。使用我的核心代码,我可以设置交互式断点,一行一行地单步执行代码,运行单元测试等等。

Rust 宏至少需要两个项目(顶级和派生)。我们将添加第三个项目,称为核心,以使开发更容易。

重要提示:在项目或文件名中任何地方看到“anyinput ”,请用您的项目替换的名称。

下表总结了您应该如何布置文件:

接下来,让我们详细了解一下文件布局。

顶层项目:使用 Rust 常用的[cargo new anyinput --lib](https://doc.rust-lang.org/cargo/guide/creating-a-new-project.html)命令创建顶层文件。(记住:用你的项目名称替换 anyinput 。)打开新创建的顶层[Cargo.toml](https://github.com/CarlKCarlK/anyinput/blob/9rules/Cargo.toml) 并将这些行添加到文件的底部:

[workspace]部分定义了三项目生锈工作空间。我们的[dev-dependencies]包含trybuild,一个我们将用于集成测试的依赖项(规则 8)。我们的[dependencies]只包含当前版本的anyinput-derive,以及"anyinput-derive"路径

如果你看我的[src/lib.rs](https://github.com/CarlKCarlK/anyinput/blob/9rules/src/lib.rs)文件,你会看到它主要包含文档。唯一关键的一行是:

pub use anyinput_derive::anyinput;

这使得宏anyinput::anyinput通过重新导出 anyinput_derive::anyinput可见。我们现在将定义anyinput_derive::anyinput

顶层的[README.md](https://github.com/CarlKCarlK/anyinput/blob/9rules/README.md)文件包含项目的主要文档。

当我们谈到规则#8 时,我们将讨论顶层的tests文件夹。

衍生项目:使用命令[cargo new anyinput-derive --lib](https://doc.rust-lang.org/cargo/guide/creating-a-new-project.html)在顶层文件夹中创建衍生文件。将这些行添加到[anyinput-derive/Cargo.toml](https://github.com/CarlKCarlK/anyinput/blob/9rules/anyinput-derive/Cargo.toml)的底部:

[lib]部分将anyinput-derive定义为程序宏项目。[dependencies]部分首先引入了我们尚未创建的anyinput-core项目,以及它的当前版本和本地路径。它还引入了两个重要的外部板条箱(将在规则#2 和#7 中讨论)。

文件[anyinput-derive/README.md](https://github.com/CarlKCarlK/anyinput/blob/9rules/anyinput-derive/README.md)真的是一个“别读我的”文件。字面意思是,“你可能正在寻找[anyinput](https://docs.rs/anyinput/)板条箱,它包裹着这个板条箱,使用起来更符合人体工程学。”

我的[anyinput-derive/src/lib.rs](http://anyinput-derive/src/lib.rs)文件正好包含 11 行:

下面是这些行的作用:

  • 他们拉进了“别读我的文件作为文档
  • 他们从anyinput_core项目中导入了anyinput_core函数。
  • 他们使用#[proc_macro_attribute]通过函数anyinput定义一个宏。与所有属性宏函数一样,该函数接受两个TokenStream输入并返回一个TokenStream。(如果您希望创建类似函数的宏或派生宏,函数签名会略有不同。详见程序宏-生锈参考。)
  • 这些行根据anyinput_core功能定义了anyinput功能。.into()方法在名为TokenStream的类型的两个版本之间进行转换。
  • 他们使用#[proc_macro_error]来捕捉abort!并返回人体工程学错误。详情见规则 7。

核心项目:使用命令[cargo new anyinput-core --lib](https://doc.rust-lang.org/cargo/guide/creating-a-new-project.html)从顶层文件夹中创建核心项目。将这些行添加到[anyinput-core/Cargo.toml](https://github.com/CarlKCarlK/anyinput/blob/9rules/anyinput-core/Cargo.toml)的底部:

文件[anyinput-core/README.md](https://github.com/CarlKCarlK/anyinput/blob/9rules/anyinput-core/README.md)是另一个“不要读我的内容”文件,它将用户引向顶层项目。

文件[anyinput-core/src/tests.rs](https://github.com/CarlKCarlK/anyinput/blob/9rules/anyinput-core/src/tests.rs)包含单元测试。我们将在规则 3 中讨论这个问题。

文件[anyinput-core/src/lib.rs](https://github.com/CarlKCarlK/anyinput/blob/9rules/anyinput-core/src/lib.rs)将最终包含宏的大部分代码。现在,从以下内容开始:

后面的规则将详细说明这些行的作用。大意是anyinput_coretransform_fn。目前,transform_fn函数将任何用户功能转换为“Hello World”功能。

文件[anyinput-core/src/tests.rs](https://github.com/CarlKCarlK/anyinput/blob/9rules/anyinput-core/src/tests.rs)将最终包含所有的单元测试。(在规则#3 中讨论)。目前,它包含:

这几行创建了一个单元测试,测试宏是否将用户的hello_universe函数变成了hello_world函数。您可以通过运行cargo test first从 anyinput-core 目录测试它。(你也可以在 Rust Playground 上运行这个版本。)

因为anyinput-core是一个普通(非宏)Rust 包,你可以用你的普通 Rust 工具开发它的代码。例如,如果您的代码编辑器支持交互式调试,您可以设置断点和/或单步执行代码。

当然,anyinput宏不应该把用户的函数变成hello_world函数。相反,它应该重写用户函数以接受任何字符串、路径等。为此,我们必须了解如何在文字代码、标记、语法树和字符串之间进行转换。这是第二条规则的主题。

规则 2:使用synproc_macro2quote在文字代码、标记、语法树和字符串之间自由转换。

通过允许我们使用语法树,[syn](https://docs.rs/syn/latest/syn/)[proc_macro2](https://docs.rs/proc-macro2/latest/proc_macro2/)[quote](https://docs.rs/quote/latest/quote/)箱使得创建过程宏更加容易。

例如,使用三个板条箱,您可以将对transform_fn的输入打印为一个字符串。这对调试很有用。

首先,我们添加两个临时的println!语句。

然后,从anyinput-core文件夹中,我们运行cargo test first -- --nocapture。(你也可以在 Rust Playground 上运行这个版本。)最后,我们看到:

要利用这三个板条箱,您必须了解以下项目以及如何在它们之间转换。

  • 文字代码—这是文件中的代码。例如:
fn hello_universe() {
    println!("Hello, universe!");
}
  • [TokenStream](https://docs.rs/proc-macro2/latest/proc_macro2/struct.TokenStream.html) https://docs.rs/proc-macro2/latest/proc_macro2/struct.TokenStream.html—这代表一个抽象的令牌流。Rust 编译器通过首先将用户的文字代码转换成TokenStream来应用宏。编译器接下来将那个TokenStream 提供给宏进行处理。最后,宏返回一个编译器编译的新的TokenStream
  • 语法树—这是表示已分析代码的嵌套 Rust 结构和枚举。结构和枚举在板条箱[syn](https://docs.rs/syn/latest/syn/)中定义。例如,[ItemFn](https://docs.rs/syn/latest/syn/struct.ItemFn.html)是表示独立函数的结构。[ItemFn](https://docs.rs/syn/latest/syn/struct.ItemFn.html)的四个场之一block包含[Stmt](https://docs.rs/syn/latest/syn/enum.Stmt.html)的矢量。枚举[Stmt](https://docs.rs/syn/latest/syn/enum.Stmt.html) 表示 Rust 语句。(规则 4 告诉我们如何了解syn定义的结构和枚举。)
  • 代码、语法和标记的字符串——我们可以将前面的项转换成字符串。此外,我们可以将字符串转换为前面的项目。

此表总结了如何从其他类型转换成您想要的类型。

换算表

旁白:一套新的 Rust 宏的想法:一套更统一地进行这些转换的宏

接下来,让我们看看演示这些转换的示例代码。(您也可以在防锈操场运行该示例代码。)

令牌的文字代码、语法和代码串

如果您有文字代码,请使用quote!parse_quote!stringify!分别将其转换为TokenStream、语法树或代码串。

注意,parse_quote!必须看到一个syn类型,这里是ItemFn结构。这告诉它要解析成什么。同样,回想一下 Rust 让我们用任何类型的括号调用类似函数的宏:!()![]!{}。和差不多。

令牌到代码串&令牌串

了解 a TokenStream 代表什么代码通常很有用。使用.to_string()。您可能还对令牌本身的字符串表示感兴趣。如果是,使用format!("{:?}",…)。使用format!("{:#?}",…)来美化打印,即将新的行和制表符添加到令牌串中。

代码串的语法树&语法串

了解语法树代表什么代码通常很有用。使用quote!(#syntax).to_string()。将语法树本身看作一个字符串通常也是有用的。使用format!("{:?}",syntax)。使用format!("{:#?}",syntax)向语法字符串添加新的行和制表符。

令牌↔语法

要将令牌流转换成语法树,请使用parse2。注意parse2需要解析的syn类型(这里是ItemFn)。另外,请注意parse2可能会返回一个错误结果。我们将在规则 7 中看到如何处理错误。

要将语法树转换成TokenStream,请使用quote!(#syntax)

语法树或令牌的代码串

要将代码串转换成语法树或TokenStream,请使用parse_str。它需要一个syn类型或TokenStream,这样它就知道要解析成什么。它可以返回一个错误结果。

符号串和语法串的文字代码

最后,为了将文字代码转换成一串标记,我们首先转换成一个TokenStream ,然后再将其转换成一个字符串。将文字代码转换为语法字符串需要三个步骤:转换为标记、转换为语法树(可能会有错误结果)、转换为字符串。

了解了如何在这些感兴趣的项目之间进行转换之后,我们接下来继续单元测试。

规则 3:创建易于调试的单元测试,报告宏所做的和你所期望的之间的任何差异

旁白: Rust Book 推荐将单元测试放在你的lib.rs中。我更喜欢把它们放在tests.rs档案里。两种设置都可以。

按照规则#1,我们的单元测试存在于标准(非宏)Rust 项目中,可以用标准 Rust 工具运行和调试。但是那些单元测试应该采取什么形式呢?

我推荐这样的测试

  • 指定用户的文字代码。这是进入宏的代码之前的
  • 在宏应用后指定预期的文字代码
  • 应用宏,然后检查预期是否等于之后的。如果预期后的不同,显示差异。
  • 最后,如果可能的话,运行预期代码的副本。

下面是一个简单的单元测试。可以看到它期望宏重写用户函数any_str_len。它还检查预期的any_str_len代码实际上返回了一个字符串的长度。(你可以在 Rust Playground 上运行这个,看测试失败。)

当我们运行它时会发生什么?它失败了!

为什么?在 VS 代码中,我在单元测试中设置了一个断点,然后单步执行代码。我看到anyinput_core函数调用了transform_fn。然后,我看到当前版本的transform_fn将所有用户函数转换成了一个hello_world函数。

测试的输出还显示了预期的和之后的之间的差异。单元测试调用的助手函数assert_tokens_eq报告了差异。助手功能是:

这是一个简单的单元测试。我们的下一步是创建更多的单元测试来测试我们还没有编写的宏。对于anyinput宏,这些单元测试包括处理带有两个输入的用户函数、带有路径和数组的输入、嵌套输入等。

有了一些单元测试,我们接下来想开始写我们的宏。这需要创造一个更好的transform_fn。然而,这需要理解 Rust 语法树,这就引出了规则 4。

旁白:anyinput 宏通过transform_fn([*anyinput-core/src/lib.rs*](https://github.com/CarlKCarlK/anyinput/blob/9rules/anyinput-core/src/lib.rs)中的一个函数)转换用户的函数例如,如果您的宏转换了用户的结构,您可以将*transform_fn* 更改为 transform_struct

规则 4:使用 AST Explorer[syn](https://docs.rs/syn/latest/syn/)文档来理解 Rust 语法树。具有 Rust 模式匹配和结构/枚举访问的析构语法树

我们可以使用在线工具 AST Explorer 来查看由我们的简单测试用例any_str_len创建的语法树。

旁白:一定要将 AST Explorer 的语言设置为 Rust。我一开始没有意识到这一点,最终创建了自己的在线小工具 Rust AST Explorer

如果将版本之前的粘贴到 AST Explorer 中,它会报告:

我们猜测ItemFn代表用户的功能。ItemFn似乎是一个四字段结构。我们通过搜索 [ItemFn](https://docs.rs/syn/latest/syn/struct.ItemFn.html)[syn](https://docs.rs/syn/latest/syn/struct.ItemFn.html) 板条箱文件来确认这一点。

下面是一个使用这些信息的例子。anyinput宏经常需要在用户函数的 where 子句中添加条目。因此,宏需要一个已经在 where 子句中的初始项目列表。使用 AST Explorer、syn文档和标准的 Rust 模式匹配,我得到了:

关于标准 Rust 模式匹配和析构技术的概述,参见析构嵌套结构和枚举。

我们现在可以从输入语法树中提取信息。我们接下来看一下在输出语法树中添加信息。

规则 5:用parse_quote!和 Rust 的结构更新语法构建语法树

Rust 通常不允许我们编辑结构或枚举的一部分。相反,我们创建一个新的结构或枚举,也许是基于旧结构或枚举的信息。syn crate 的[parse_quote!](https://docs.rs/syn/latest/syn/macro.parse_quote.html)宏是一种简单(也有点有趣)的方式。它将文字代码与语法树结合起来创建新的语法树。

下面是来自anyinput宏的一个例子。这段代码生成一个要添加到用户函数中的语句。例如,在某些情况下,它会将语句let s = s.as_ref();添加到用户函数中。

我还使用parse_quote!来创建一些语法,比如一个左尖括号和一个空的WherePredicate列表。(可在铁锈操场运行。)

parse_quote!宏对于构建新的语法来说是很棒的,但是如果你想改变现有的语法呢?为此,我推荐 Rust 的标准结构更新语法

这里有一个来自anyinput宏的例子。它用字段sigblock的新值创建了一个ItemFn 结构,但在其他方面与old_fn相同。同样,新的[Signature](https://docs.rs/syn/latest/syn/struct.Signature.html) 结构包含字段genericsinputs的新值,但在其他方面与old_fn’s sig相同。为什么不直接指定每个字段?嗯,Signature 结构包含 11 个字段,所以更新语法更加简洁。

我们现在知道如何使用标准的 Rust 方法(加上parse_quote!)来处理语法树。然而,有时我们想要更强大的东西。规则#6 展示了如何通过利用语法树固有的递归和嵌套来转换语法树。

规则 6:使用syn的 Fold 特性递归地遍历、析构和构造语法树

anyinput宏必须处理用户函数的输入,例如:

  • s: AnyString
  • v: Vec<AnyPath>
  • a: AnyArray<AnyPath>
  • yikes: AnyIter<Vec<AnyArray<AnyPath>>>

这样的嵌套强烈建议我们应该使用递归。syn机箱通过其[Fold](https://docs.rs/syn/latest/syn/fold/index.html) 特征实现了这种递归。这里有一个例子。

假设我们想要一个计算函数中语句数量的宏。这比听起来要难,因为——通过使用花括号——Rust 语句可以包含子语句。同样,假设宏应该用打印“hello universe”的语句替换任何包含“galaxy”的语句。(你可以在 Rust Playground 运行这个例子。)

我们首先创建自己的结构。它保存了我们在处理用户语法树时需要的任何信息:

接下来,我们为我们希望检查或重写的类型定义一个Fold实现。在这种情况下,我们希望访问每个Stmt,因此:

Fold支持 187 种类型,但是我们只实现感兴趣的类型,这里只实现一种。其他的自动接收默认实现。

你可能会对let stmt_middle = syn::fold::fold_stmt(self, stmt_old);线感到疑惑。这很重要。如果我们希望访问当前正在访问的语句中的语句和其他类型,这是必需的。

这里是一个完整的fold_stmt实现。注意,我们返回一个Stmt(可能是转换后的)。

也许令人惊讶的是,我们不直接调用我们的fold_stmt实现。相反,我们调用fold_item_fn,因为在这个例子中,ItemFn是我们从用户那里得到的输入类型。

运行count_statements递归地对语句进行计数,并用“hello universe”替换“hello galaxy”:

我们现在有了为完美用户编写宏所需的所有工具。但是如果我们的用户有时会犯错误呢?这是规则 7 的主题。

规则 7:使用proc_macro_error返回人机工程学和可测试的错误

anyinput宏支持嵌套输入,如AnyIter<AnyString>。如果用户使用圆括号而不是尖括号会发生什么?理想情况下,他们会收到一条指出确切错误位置的消息:

然而,困难是存在的。即,

  • Rust 宏不使用标准 Rust 错误[results](https://doc.rust-lang.org/std/result/)
  • 标准的panic!宏可以工作,但是它只返回错误消息,不返回错误位置。
  • 做我们想做的事,但它只在夜间进行。
  • [std::compile_error](https://doc.rust-lang.org/std/macro.compile_error.html)也做我们想做的但只能用在顶层宏功能中。所以,它没有帮助,例如,当我们在一个Fold遍历中发现一个用户错误几十层时。(规则#6 描述了Fold遍历)。

[proc_macro_error](https://docs.rs/proc-macro-error/latest/proc_macro_error/)机箱解决了这些问题(以一点开销为代价)。它还为未来的验证提供了夜间兼容性。

下面是如何设置它。首先,将#[proc_macro_attribute]应用到[anyinput-derive/src/lib.rs](https://github.com/CarlKCarlK/anyinput/blob/9rules/anyinput-derive/src/lib.rs)中的宏函数。(有关详细信息,请参见规则 1。)

当你在你的核心代码([anyinput-core/src/lib.rs](https://github.com/CarlKCarlK/anyinput/blob/9rules/anyinput-core/src/lib.rs))中发现一个用户错误时,调用abort!宏。例如,这段代码检查三个用户错误。找到一个就调用abort!

除了第一个参数告诉错误的位置之外,abort!宏的工作方式与标准panic!宏一样。该参数可以是来自用户代码的语法树。或者,它可以是从语法树中提取的SpanRangeTokenStream,例如:

let span_range = SpanRange::from_tokens(&type_path_old);

您的单元测试应该执行这种错误处理。下面是对“圆括号代替尖括号”错误的单元测试。

您会注意到它使用了标准的[should_panic](https://doc.rust-lang.org/reference/attributes/testing.html#the-should_panic-attribute)测试属性。然而,单元测试寻找的信息是奇怪的。这是单元测试所能做到的最好的。然而,下一个规则显示了集成测试如何检查错误消息的文本。

规则#8 创建集成测试。包含基于trybuild的 UI 测试

集成测试将您的宏应用到实际代码中!他们住在[tests/integration_test.rs](https://github.com/CarlKCarlK/anyinput/blob/9rules/tests/integration_test.rs)。我的第一个集成测试是:

如果您按照规则#1 设置您的项目,那么您可以使用cargo test从顶层文件夹运行这个测试。Rust 系统会自动编译所有级别的工作空间,以便进行测试和调试。

如果你想运行所有的集成测试所有的单元测试呢?使用cargo test --workspace命令。如果您想交互式地运行代码,该怎么办?使用为 Rust 设置的 VS 代码,我可以在集成测试中的s.len()上设置断点,并单步执行宏应用后的代码。

我们的测试还有一个漏洞——我们还没有完全测试我们的错误处理。(在规则#7 中描述)。通过在集成测试中添加[trybuild](https://crates.io/crates/trybuild) UI 测试来填补这个漏洞。添加它的步骤是:

  • 创建包含使用宏的用户代码的文件。下面是“圆括号代替尖括号”的 UI 测试。它位于文件[tests/ui/anyiter_paren.rs](https://github.com/CarlKCarlK/anyinput/blob/9rules/tests/ui/anyiter_paren.rs)中:

[tests/integration_test.rs](https://github.com/CarlKCarlK/anyinput/blob/9rules/tests/integration_test.rs)中添加集成测试ui:

cargo test ui从顶层文件夹运行这些 UI 集成测试。第一次运行这个测试时,它会失败,但也会创建文件wip/[anyiter_paren](https://github.com/CarlKCarlK/anyinput/blob/9rules/tests/ui/anyiter_paren.rs).stderr

看看[anyiter_paren](https://github.com/CarlKCarlK/anyinput/blob/9rules/tests/ui/anyiter_paren.rs).stderr。如果您确定它包含正确的输出,将其移至[tests/ui/anyiter_paren.stderr](https://github.com/CarlKCarlK/anyinput/blob/9rules/tests/ui/anyiter_paren.stderr)。下一次运行测试时,他们将会得到这样的输出。关于(重新)生成预期输出文件的更多细节,参见[trybuild](https://crates.io/crates/trybuild)文档。

您的宏经过全面测试后,您可以考虑使用它并共享它。规则 9 讨论了如何让它准备好共享。

规则 9:遵循优雅的 Rust API 设计规则,特别是,吃你自己的狗粮,使用 Clippy,写好文档

前面的规则描述了宏的编码,但是您应该如何设计您的宏呢?我建议你遵循优雅的 Rust 库 API 的九条规则,尤其是这三条:

使用夹子—防锈夹子涂抹严格的林挺。

写好文档以保持设计的诚实 —生成文档并在浏览器中弹出的命令是cargo doc --no-deps --open

了解你的用户的需求,最好是吃你自己的狗粮——这意味着在其他项目中使用你的宏,看看它的效果如何。对于anyinput宏,我在

  • [fetch-data](https://crates.io/crates/fetch-data) —我从网上下载并缓存样本文件的箱子)—我使用anyinput来指定路径。效果很好。
  • [bed-reader](https://crates.io/crates/bed-reader)——我们的基因组文件阅读器——我使用了anyinput来指定路径(效果很好)和类似字符串的东西的迭代器(yikes)。

有了bed-reader,我意识到用户会看到我的宏生成的通用名。bed-reader 的文档和代码编辑器(如 VS Code)都显示了宏生成的泛型。如果我把它们命名为T0T1等。它们会太模糊,可能会与用户函数中的其他类属发生冲突。为了避免碰撞,我尝试了像T8a0700b3-d16b-4b8e-bdb4–8fcb7e809ff3这样的名字,但是它们看起来很可怕。我最终给泛型起了诸如AnyString0AnyIter1等名字。多亏了“吃我自己的狗粮”,我最终得到了一个令我满意的设计。

现在,你有了:在 Rust 中创建程序宏的九条规则。如果你想发布你的宏到 crates.io ,你需要先发布核心,然后派生,最后顶层。在每个[cargo publish](https://doc.rust-lang.org/cargo/commands/cargo-publish.html)之间,您可能需要等待 5 秒钟左右。

我使用anyinput的经验表明,编写一个宏可以像编写一个普通的 Rust 程序一样简单……操作大型的嵌套结构。关键是组织工作空间,使用正确的工具,并理解这些工具。遵循这九条规则来创建你自己的强大的 Rust 宏。

旁白:请试试 https://crates.io/crates/anyinput。如果你喜欢这篇文章,请给它“鼓掌”。如果您对未来的文章感兴趣——关于 Rust、机器学习、Python、统计学等。—请跟着我写。

优雅的 Rust 库 API 的九条规则

原文:https://towardsdatascience.com/nine-rules-for-elegant-rust-library-apis-9b986a465247

从 Python 到 Rust 移植生物信息学库 Bed-Reader 的实践经验

Kai DahmsUnsplash 上的照片

我喜欢创建软件库。两个月前,我开始将我们的一个 Python 包移植到 Rust crate 中。这个新的 Rust crate 与 Python 包的易用性和表现力相匹配。一路上,我学到了九条规则,可以帮助你在 Rust 中创建漂亮的库。规则是:

  1. 创造一些不会让你尴尬的例子。
  2. 接受所有类型的字符串、路径、向量、数组和可重复项。
  3. 了解用户的需求,最好是吃你自己的狗粮。
  4. 使用生成器,因为不能使用关键字参数。
  5. 编写好的文档来保持设计的诚实。
  6. 接受所有类型。
  7. 编写 API 测试。
  8. 定义并返回好的错误。
  9. 使用 Clippy。

在这种情况下,库是供其他程序使用的锈箱。库的 API(应用程序编程接口)是程序可以调用的一组公共函数和对象。我们希望设计一个优雅的 API,而不仅仅是功能性的 API。功能 API 只是让用户做他们需要做的一切。一个优雅的 API 让他们以简单而明智的方式做他们需要做的事情。

具体来说,这是我们面向 Python 的 API 和新的面向 Rust 的 API。

任务:列出前 5 个个体 id,前 5 个 SNP ids,以及每条唯一的染色体。然后,读取 5 号染色体中的每个值。

Python API:

>>> with open_bed(file_name2) as bed3:
...     print(bed3.iid[:5])
...     print(bed3.sid[:5])
...     print(np.unique(bed3.chromosome))
...     val3 = bed3.read(index=np.s_[:,bed3.chromosome=='5'])
...     print(val3.shape)['iid_0' 'iid_1' 'iid_2' 'iid_3' 'iid_4']
['sid_0' 'sid_1' 'sid_2' 'sid_3' 'sid_4']
['1' '10' '11' '12' '13' '14' '15' '16' '17' '18' '19' '2' '20' '21' '22' '3' '4' '5' '6' '7' '8' '9']
(100, 6)

Rust API:

let mut bed = Bed::new(file_name)?;
println!("{:?}", bed.iid()?.slice(s![..5]));
println!("{:?}", bed.sid()?.slice(s![..5]));
println!("{:?}", bed.chromosome()?.iter().collect::<HashSet<_>>());
let val = ReadOptions::builder()
    .sid_index(bed.chromosome()?.map(|elem| elem == "5"))
    .f64()
    .read(&mut bed)?;// Outputs ndarray: ["iid_0", "iid_1", "iid_2", "iid_3", "iid_4"]
// Outputs ndarray: ["sid_0", "sid_1", "sid_2", "sid_3", "sid_4"]
// Outputs: {"12", "10", "4", "8", "19", "21", "9", "15", "6", "16", "13", "7", "17", "18", "1", "22", "11", "2", "20", "3", "5", "14"}
assert!(val.dim() == (100, 6));

这些优雅吗?情人眼里出西施,但是作为 Python API 的用户,我觉得它很优雅。关于 Rust API,我很高兴它紧密地遵循了 Python 的设计,并且相信它是优雅的。

灵感:之前的两篇文章激发并启发了这些努力。首先,2016 年,Pascal Hertleif 在 Rust 中编写了优雅的库 API。后来,Brian Anderson 创建并维护了 Rust API 指南。相比之下,这篇文章更笼统,更具体,也不全面。它讨论了适用于所有语言的通用 API 设计原则,而不仅仅是 Rust。它强调了我发现在移植 Bed-Reader 时最有用的特定技术和工具。它忽略了我没有面对的 API 设计问题(例如,设计宏)。

背景 : Bed-Reader 是一个用于读写 PLINK Bed 文件的库,PLINK Bed 文件是生物信息学中用来存储基因型(DNA)数据的二进制格式。Bed 格式的文件可能有 1tb 那么大。Bed-Reader 让用户可以快速、随机地访问大量数据。它以用户选择的 int8、float32 或 float64 返回一个二维数组。Bed-Reader 还让用户可以访问 12 条元数据,6 条与个人相关,6 条与 SNPs 相关(粗略地说,是 DNA 位置)。重要的是,基因型数据通常比元数据大 100,000 倍。

PLINK 存储基因型数据和元数据

创建一个优雅的 Rust 库 API 需要许多设计决策。根据我在读床者上的经验,以下是我推荐的决定。为了避免含糊不清,我将把这些建议表述为规则。规则在一般和特殊之间交替。

规则 1:创造不会让你尴尬的例子

您应该创建许多使用您的库的 API 的示例。您应该继续开发您的库的 API,直到您对这些例子感到自豪。

为了说明这一点,让我们看看 Bed-Reader 的 README.md 文件中的三个例子。对于每项任务,我们将使用以下工具来查看解决方案

  • Python API
  • 仅仅是功能性的 Rust API
  • 更优雅的新 Rust API

任务:从. bed 文件中读取所有基因组数据。

Python API:

>>> import numpy as np
>>> from bed_reader import open_bed, sample_file
>>>
>>> file_name = sample_file("small.bed")
>>> bed = open_bed(file_name)
>>> val = bed.read()
>>> print(val)
[[ 1\.  0\. nan  0.]
 [ 2\.  0\. nan  2.]
 [ 0\.  1\.  2\.  0.]]
>>> del bed

仅功能性防锈 API:

use crate::read;let file_name = "bed_reader/tests/data/small.bed";
let val = read(file_name, true, true, f32::NAN)?;
println!("{val:?}");// [[1.0, 0.0, NaN, 0.0],
//  [2.0, 0.0, NaN, 2.0],
//  [0.0, 1.0, 2.0, 0.0]],...

功能性 Rust API 做得很好!一方面,true, true输入有点混乱。另一方面,它比 Python 短一行。我几乎可以为这个解决方案感到自豪。

新的、更优雅的 Rust API:

use ndarray as nd;
use bed_reader::{Bed, ReadOptions, assert_eq_nan, sample_bed_file};

let file_name = sample_bed_file("small.bed")**?**;
let mut bed = Bed::new(file_name)**?**;
let val = ReadOptions::builder().f64().read(&mut bed)**?**;

assert_eq_nan(
    &val,
    &nd::array![
        [1.0, 0.0, f64::NAN, 0.0],
        [2.0, 0.0, f64::NAN, 2.0],
        [0.0, 1.0, 2.0, 0.0]
    ],
);

与仅仅是功能性的 API 相比,新的 API 用构建器模式取代了令人困惑的true, true输入。(详见规则 4。)另一方面,它需要额外的一行代码。所以,不一定更好。

让我们看看下一个任务。

任务:从 20 到 30 读取每隔一个个体和 SNPs (DNA 定位)。

Python API:

>>> file_name2 = sample_file("some_missing.bed")
>>> bed2 = open_bed(file_name2)
>>> val2 = bed2.read(index=np.s_[::2,20:30])
>>> print(val2.shape)
(50, 10)
>>> del bed2

注意[::2,20:30]的使用,这是 Python NumPy 的奇特索引的一个实例。Python 科学程序员熟悉这种索引,非常适合指定要读取哪些基因组数据片段。

仅仅是功能性防锈 API:

use crate::{counts, read_with_indexes};let file_name = "bed_reader/tests/data/some_missing.bed";let (iid_count, _) = counts(file_name)?;
let iid_index = (0..iid_count).step_by(2).collect::<Vec<_>>();
let sid_index = (20..30).collect::<Vec<_>>();
let val = read_with_indexes(
    file_name,
    iid_index.as_slice(),
    sid_index.as_slice(),
    true,
    true,
    f32::NAN,
 )?;
println!("{:?}", val.shape());
// [50, 10]

Python 在中的两行核心代码在 Rust 中变成了四条语句(和 12 行代码)。和以前一样,我们有不清楚的true, true。此外,指定感兴趣的索引变得更加困难。此外,用户必须以某种方式理解索引必须被定义为向量(以便它们在某个地方被拥有),但随后通过as_slice传递(这是函数所期望的)。

这个例子让我很尴尬,尤其是与 Python 相比。这促使我像这样改进 Rust API:

新的、更优雅的 Rust API:

use ndarray::s;

let file_name = sample_bed_file("some_missing.bed")**?**;
let mut bed = Bed::new(file_name)**?**;
let val = ReadOptions::builder()
    .iid_index(s![..;2])
    .sid_index(20..30)
    .f64()
    .read(&mut bed)**?**;

assert!(val.dim() == (50, 10));

我更喜欢这个。像 Python 一样,它支持有趣的索引。遗憾的是,Rust 的标准范围——例如,20..30 —不支持步骤,所以 API 必须也支持n 数组切片s![..;2]。(我们在规则 6 中看到 Rust 如何接受如此不同类型的输入。)

现在让我们来看最后一个使用元数据的例子。

任务:列出前 5 个个体 id,前 5 个 SNP ids,以及每个唯一的染色体。然后,读取 5 号染色体中的每个值。

我们在简介中看到了 Python 解决方案。仅仅是功能性的防锈解决方案有多糟糕?可悲的是,仅仅是功能性的 API 并不了解元数据。令人高兴的是,元数据文件只是六列文本文件,所以用户可以自己阅读,对吗?

仅功能性防锈 API:

呀,很尴尬。这是更好的防锈解决方案(我们在简介中也看到了)。

新的“优雅”Rust API:

use std::collections::HashSet;

let mut bed = Bed::new(file_name)**?**;
println!("{:?}", bed.iid()**?**.slice(s![..5]));
println!("{:?}", bed.sid()**?**.slice(s![..5]));
println!("{:?}", bed.chromosome()**?**.iter().collect::<HashSet<_>>());
let val = ReadOptions::builder()
    .sid_index(bed.chromosome()**?**.map(|elem| elem == "5"))
    .f64()
    .read(&mut bed)**?**;

// Outputs ndarray: ["iid_0", "iid_1", "iid_2", "iid_3", "iid_4"]
// Outputs ndarray: ["sid_0", "sid_1", "sid_2", "sid_3", "sid_4"]
// Outputs: {"12", "10", "4", "8", "19", "21", "9", "15", "6", "16", "13", "7", "17", "18", "1", "22", "11", "2", "20", "3", "5", "14"}
assert!(val.dim() == (100, 6));

铁锈和蟒蛇差不多。然而,Rust 更加冗长。例如,下面是借用单个 id 的一维 NumPy/ndarray 并打印前 5 项的代码:

print(bed.iid[:5]) # Python
println!("{:?}", bed.iid()**?**.slice(s![..5])); // Rust

这些在语义上几乎相同,但是 Python 支持更简洁的数组相关语法。

下面是打印数组中唯一值的代码:

print(np.unique(bed.chromosome)) # Python
println!("{:?}", bed.chromosome()**?**.iter().collect::<HashSet<_>>()); // Rust

Python 做得好一点,因为它成熟的 NumPy 库包含了np.unique,而 Rust 的 ndarray 没有。

最后,要找到染色体“5”中的 SNPs,代码是:

np.s_[:,bed.chromosome=='5'] # Python
bed.chromosome()**?**.map(|elem| elem == "5") // Rust

我认为 Rust 在这里打败了 Python,因为它只使用了常见的语言特性,避免了不太常见的np.s_

规则 1 的要点不是追求完美。相反,你应该瞄准有意义和有趣的例子。然后,在 API 上工作,直到这些例子可以被解决而不会感到尴尬,也许还会感到自豪。

规则 2:接受所有类型的字符串、路径、向量、数组和可重复项

当给你的 API 一个路径时,你的用户可能希望使用一个String或者一个&String或者一个&str.,你的用户可能希望给你一个PathBuf&PathBuf&Path或者任何类型的字符串。此外,你的用户有时会希望给你自己的价值,有时是借来的价值。你可以用泛型来支持所有这些可能性。接受所有类型的向量、数组和可迭代对象怎么样?这也可以用泛型来支持。

更新:我创建了一个名为 anyinput 的宏,让接受各种输入变得更容易。任何输入文件都有详细信息。如果使用 anyinput ,可以跳到下一条规则。

具体来说,对于路径,将路径定义为泛型类型P,其中PAsRef<Path>。例如,下面是打开. bed 文件的函数Bed::new的定义:

pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, BedErrorPlus> {
    Bed::builder(path).build()
}

这里是每个输入类型Bed::new接受的:

let path: &str = "bed_reader/tests/data/small.bed";
let _ = Bed::new(&path)?; // borrow a &str
let _ = Bed::new(path)?; // move a &str
let path: String = "bed_reader/tests/data/small.bed".to_string();
let _ = Bed::new(&path)?; // borrow a String
let path2: &String = &path;
let _ = Bed::new(&path2)?; // borrow a &String
let _ = Bed::new(path2)?; // move a &String
let _ = Bed::new(path)?; // move a String
let path: &Path = Path::new("bed_reader/tests/data/small.bed");
let _ = Bed::new(&path)?; // borrow a Path
let _ = Bed::new(path)?; // move a Path
let path: PathBuf = PathBuf::from("bed_reader/tests/data/small.bed");
let _ = Bed::new(&path)?; // borrow a PathBuf
let path2: &PathBuf = &path;
let _ = Bed::new(&path2)?; // borrow a &PathBuf
let _ = Bed::new(path2)?; // move a &PathBuf
let _ = Bed::new(path)?; // move a PathBuf

在您的函数中,.as_ref()方法将有效地把这个输入转换成一个&Path。表情.as_ref().to_owned()会给你一个拥有的PathBuf

对于类似字符串的东西,用S: AsRef<Str>定义你的函数。.as_ref()方法将有效地将输入转换成&str 和。as_ref().to_owned()会给你一个拥有的String

对于向量、数组和可迭代的,有时我们只需要一些可迭代的东西。如果是这样,用I: IntoIterator<Item = T>定义你的函数,其中T是项目的类型,可能也是通用的。例如,MetadataBuilder::iid是一个将单个 id 添加到内存元数据生成器的函数。(我们将在规则 4 中看到更多关于构建器的内容。)

pub fn iid<I: IntoIterator<Item = T>, T: AsRef<str>>(&mut self, iid: I) -> &mut Self {
    self.iid = Some(Some(Rc::new(
        iid.into_iter().map(|s| s.as_ref().to_owned()).collect(),
    )));
    self
}

一会儿,我们将讨论这个函数可以接受什么以及代价是什么。首先,这里是一些MetadataBuilder::iid接受的事情:

let list: [&str; 3] = ["i1", "i2", "i3"];
let _ = Metadata::builder().iid(&list).build()?; // borrow fixed-size array
let _ = Metadata::builder().iid(list).build()?; // move fixed-size array
let list: [String; 3] = ["i1".to_string(), "i2".to_string(), "i3".to_string()];
let _ = Metadata::builder().iid(&list).build()?; // borrow fixed-size array of String
let _ = Metadata::builder().iid(list).build()?; // move fixed-size array of String
let list: Vec<&str> = vec!["i1", "i2", "i3"];
let _ = Metadata::builder().iid(&list).build()?; // borrow Vec<&str>
let list2 = &list[..]; // borrowed slice
let _ = Metadata::builder().iid(list2).build()?; // borrow slice
let _ = Metadata::builder().iid(list).build()?; // move Vec<&str>
let list = nd::array!["i1", "i2", "i3"];
let view = list.view();
let _ = Metadata::builder().iid(&view).build()?; // borrow nd view
let _ = Metadata::builder().iid(view).build()?; // move nd view
let _ = Metadata::builder().iid(&list).build()?; // borrow ndarray
let _ = Metadata::builder().iid(list).build()?; // move ndarray
let list: std::str::Split<&str> = "i1,i2,i3".split(",");
let _ = Metadata::builder().iid(list).build()?; // move iterator

需要随机访问的向量、数组和 n 数组是什么?那么你有一个选择。你可以把一切都当成锈片。例如:

pub fn any_slice<A: AsRef<[T]>, T>(slice: A) -> Result<(), anyhow::Error> {
    let slice = slice.as_ref();
    println!("slice len: {}", slice.len());
    Ok(())
}

这直接接受 Rust 中所有类似数组的东西,除了 ndarrays 和 views。它们是通过转换成切片来间接接受的,例如nd_array1.as_slice().expect(“ndarray as_slice”)

或者,您可以将所有内容都视为 ndarray 视图。例如:

pub fn any_array_view<'a, T: 'a, A: Into<nd::ArrayView1<'a, T>>>(
    array: A,
) -> Result<(), anyhow::Error> {
    let array_view = array.into();
    println!("array_view len: {}", array_view.len());
    Ok(())
}

这个函数直接接受所有类似数组的东西的借位,但是不接受移动。例如,这是可行的:

let array: [&str; 3] = ["i1", "i2", "i3"];
let _ = any_array_view(&array)?; // borrow fixed-size array (can't move)

如果你的 API 只需要迭代借用用户的数据,IntoIterator就很棒。但是,如果您需要自己的数据副本,您必须做出选择。

  • 使用IntoIterator并重新分配。上面的iid函数就是这么做的。如前所述,子表达式s.as_ref().to_owned()将任何类似字符串的东西转化为拥有的String。表情iid.into_iter()....collect(),然后将任何可迭代的东西转换成所需的集合类型(在本例中是一个nd::array1<String>)。对于 Bed-Reader,我知道这些额外的分配并不重要,因为个体 id 数据通常比主要的基因组数据小 1,000,000 倍。
  • 或者,你可以要求用户给你一个特定类型的集合,并取得所有权。例如,上面的函数可能需要一个nd::array1<String>。这样效率更高,但灵活性较差。

性能提示:在 Rust 用户论坛上, Kevin Reid 指出让这个泛型函数变小并调用一个非泛型函数来完成大部分工作是有好处的。这最小化了代码大小和编译时间。

规则 3:了解用户的需求,最好是吃自己的狗粮

我们的基因组项目已经阅读 PLINK Bed 文件超过 10 年了。两年前,一个用户要求我们把。bed 将代码读入自己的 Python 包。我们基于我们的经验开发了这个包的 Python API。例如,API 仅在必要时读取元数据文件。此外,它将读取的任何元数据保存在内存中。这使得对大基因组数据的读取几乎可以立即开始,我们知道这对于例如集群运行是很重要的。

当我们创建 Python API 时,我们重写了我们的其他工具来使用它,在我们进行的过程中改进了 Python API。当我们创建 Rust API 时,我们重写了 Python API 来使用它,并在我们进行的过程中对 Rust API 进行了改进。这叫做“吃你自己的狗粮”。当我们开发 Python API 时,我们也向请求用户征求反馈。我们把他们愿意提供反馈视为对我们的帮助。

我发现随着 Bed-Reader API 的改进,内部和外部的用户代码都变得更简单了,这是非常令人满意的。

坦白说:我们并不迫切需要一个 Rust 阅读器 API。我创建它是因为我想学习如何在 Rust 中创建一个中等大小的 API。我的设计基于 Python API 和 Rust 原则,而不是 Rust 用户需求。

规则 4:使用构建器,因为不能使用关键字参数。

下面是一个使用关键字参数在 Python 中读取基因型数据的示例:

with open_bed(file_name, count_A1=False) as bed:
    val = bed.read(
        index=np.s_[:, :3],
        dtype="int8",
        order="C",
        num_threads=1)
    print(val.shape)

下面是 Rust 中使用构建器的相同示例。

let mut bed = Bed::new(filename)?;
let val = ReadOptions::builder()
    .sid_index(..3)
    .i8()
    .c()
    .num_threads(1)
    .read(&mut bed)?;
println!("{:?}", val.dim());

他们是相似的。让我们来看看相应的参数,并详细比较 Bed-Reader 的 Python 和 Rust 方法是如何进行比较的:

  • Python 的二维索引(index=np.s_[:, :3])变成了两个一维参数,其中用户通常只需要一个(.sid_index(..3) )— Rust 及其用户需要一维参数。此外,用户通常只需要索引一个维度。
  • Python 的字符串指定了与类型相关的三个选项(dtype='int8')中的一个,它变成了三个参数。Rust 用户将使用一个(.i8())或无(当类型可以推断时)。
  • Python 的字符串指定两个选项之一(order='c')成为三个参数。Rust 用户可以说.c()(哪个更短)或者is_f(false)(可以用变量设置)。
  • 一个数(num_threads=1)保持一个数(.num_threads(1))。
  • 一个 Python 布尔值(count_A1=False)变成了三个参数。Rust 用户可以说.count_a2()或者.count_a1(false)

Bed-Reader 使用流行且强大的 derive_builder 机箱来构建生成器。读床者的四个建设者是:

参见文档和源代码的链接。

  • [Bed::builder](https://docs.rs/bed-reader/0.2.19/bed_reader/struct.Bed.html#method.builder) —需要选项时,打开 Bed 文件进行读取。(如果不需要选项,可以用[Bed::new](https://docs.rs/bed-reader/0.2.19/bed_reader/struct.Bed.html#method.new)代替。)
  • [ReadOptions::builder](https://docs.rs/bed-reader/0.2.19/bed_reader/struct.ReadOptions.html#method.builder) —指定阅读选项,然后立即[.read](https://docs.rs/bed-reader/0.2.19/bed_reader/struct.ReadOptionsBuilder.html#method.read)[.build](https://docs.rs/bed-reader/0.2.19/bed_reader/struct.ReadOptionsBuilder.html#method.build)(稍后阅读)。
  • [WriteOptions::builder](https://docs.rs/bed-reader/0.2.19/bed_reader/struct.WriteOptions.html#method.builder) —指定写入选项,然后立即选择[.write](https://docs.rs/bed-reader/0.2.19/bed_reader/struct.WriteOptionsBuilder.html#method.write)[.build](https://docs.rs/bed-reader/0.2.19/bed_reader/struct.WriteOptionsBuilder.html#method.build)(稍后写入)。
  • [Metadata::builder](https://docs.rs/bed-reader/0.2.19/bed_reader/struct.Metadata.html#method.builder) —指定创建内存元数据的选项,然后[.build](https://docs.rs/bed-reader/0.2.19/bed_reader/struct.MetadataBuilder.html#method.build)它。

提示:在 Rust 用户论坛中, H2CO3 突出显示了结构更新语法”(也称为功能记录更新(FRU))作为构建器的替代。使用默认值,用户可以通过仅指定非默认字段值来创建“选项结构”。

规则 5:编写好的文档来保持设计的诚实。

在某一点上,Bed-Reader Rust API 的某些部分接受受限的内存中元数据对象,而某些部分则不接受。我试图解释文档中的这种不一致,但是越来越沮丧。我最终决定,我宁愿修复令人困惑的设计,而不是解释它。写文档激励我改进设计。

编写优秀文档的一些技巧:

  • 使用 Rust 优秀的 rustdoc 系统
  • 记录每个公共函数、结构、枚举等。用#![warn(missing_docs)]开始你的 lib.rs ,你会被提醒记录所有的事情。
  • 在几乎所有的文档中都包含示例。如果你不能创建简单的例子,你的 API 设计需要更多的工作。用 Rust 的标准cargo test命令测试这些例子。
  • 不要惊慌。你的例子应该返回错误(通过?),而不是恐慌(通过.unwrap())。rustdoc 手册告诉如何设置进行测试。也见规则 8。
  • 为你的项目写好 README.md 。它还可以作为 API 文档的介绍。在你的中包含#![doc = include_str!("../README.md")]。这种包含提供了一个额外的好处,即您的 README.md 中的任何示例都将作为测试运行。
  • 阅读、重读和编辑你的文档,直到它很好地向你的用户解释了你的 API。(生成文档并在浏览器中弹出的命令是cargo doc --no-deps --open。)

你可以在这里看到我写好文档的尝试:bed _ reader—Rust(docs . RS)。源代码(可从文档中获得链接)显示了 markdown 格式,例如总结特性的表格。

规则 6:接受所有类型的类型。

当 Bed-Reader 的用户指定要阅读的个人或 SNP 时,他们可以给出:

  • 索引号(例如,1)
  • 一个数组,vector,Rust slice,ndarray::Array,ndarray::索引号视图([0, 10, -2])
  • 一个锈层(3..10..=19)或一个阵列切片(s![-20..-10;-2])
  • 一个数组,vector,Rust slice,ndarray::Array,ndarray::布尔值的视图([true, false true])

鉴于 Rust 强大的打字能力,这怎么可能呢?关键是一个枚举和转换函数。具体来说,

  • 用要存储的所有类型定义一个枚举。名为Index的床阅读器枚举定义为:
#[derive(Debug, Clone)]
pub enum Index {
   #[allow(missing_docs)]
    All,
    #[allow(missing_docs)]
    One(isize),
    #[allow(missing_docs)]
    Vec(Vec<isize>),
    #[allow(missing_docs)]
    NDArray(nd::Array1<isize>),
    #[allow(missing_docs)]
    VecBool(Vec<bool>),
    #[allow(missing_docs)]
    NDArrayBool(nd::Array1<bool>),
    #[allow(missing_docs)]
    NDSliceInfo(SliceInfo1),
    #[allow(missing_docs)]
    RangeAny(RangeAny),
}
  • 实现From函数将所有感兴趣的类型转换到你的枚举中。您转换的类型不能重叠。以下是 Bed-Reader 的四个From实现:
impl From<isize> for Index {
    fn from(one: isize) -> Index {
        Index::One(one)
    }
}
impl From<&isize> for Index {
    fn from(one: &isize) -> Index {
        Index::One(one.to_owned())
    }
}
impl<const N: usize> From<[bool; N]> for Index {
    fn from(array: [bool; N]) -> Index {
        Index::VecBool(array.to_vec())
    }
}
impl<const N: usize> From<&[bool; N]> for Index {
    fn from(array: &[bool; N]) -> Index {
        Index::VecBool(array.to_vec())
    }
}
  • 在你的枚举上实现函数来做你需要的任何事情。例如,给定个体的计数,我需要知道一个Index的长度。我定义.len(count)Clippy (见规则 10)提醒我也要定义is_empty

  • 最后,如果你的枚举在一个常规函数中使用,用impl Into<YourEnum>定义它,然后在你的函数中使用.into()(见下文)。如果你的函数是构建器的一部分,并且你使用了 derive_builder ,你可以用#[builder(setter(into))]标记你的结构的字段(这里是)。例如:

// Tells the length of an index if the count of items is exactly 100
fn len100(index: impl Into<Index>) -> Result<usize, BedErrorPlus> {
    let index = index.into();
    let len = index.len(100)?;
    Ok(len)
}
let _ = len100(3..)?;let _ = ReadOptions::builder().iid_index(3..).i8().build()?;

(拜 凯文读为《impl Into》上的提示 )。)

规则 7:编写 API 测试。

编写 API 测试,而不仅仅是单元测试。这需要在 src 文件夹之外创建一个 tests/test_api.rs 文件。这让您可以像用户一样测试 API 的公共方法。比如阅床者的 test_api.rs 说的是use bed_reader::Bed;而不是use crate::Bed;

规则 8:定义并返回好的错误。

定义并返回好的错误,例如,通过 thiserror 箱。在 Bed-Reader 的 lib.rs 中,我们为库定义的所有错误创建了一个名为 BedError 的枚举。然后我们创建一个名为 BedErrorPlus 的枚举来覆盖来自其他箱子的 BedError 和错误。

规则 9:使用 Clippy

防锈纸涂上严格的林挺。

所以,现在你有了:Rust 库 API 的九条规则。当你创建了你的优雅的 Rust crate 并准备发布时,命令是 cargo publish

我使用 Bed-Reader 的经验表明,我们可以拥有 Rust 的性能和 Python 的优雅。这是有代价的,也就是说,目前在 Rust 中创建一个好的 API 不像在 Python 中那么容易。遵循这九条规则,让创建一个优秀的 Rust 库 API 的过程变得更加简单。

西雅图 Rust Meetup 用 YouTube 上的 视频主持了一场关于这篇文章的演讲。请 跟我上媒 。我写的是 Rust 和 Python 的科学编程、机器学习和统计学。我倾向于每月写一篇文章。

NLP 101 3/3—NLP 的神经架构

原文:https://towardsdatascience.com/nlp-101-3-3-neural-architectures-for-nlp-71f11e27b189

了解传统的顺序神经网络架构以及变压器如何革新 NLP。

Jana Shnipelson 在 Unsplash 上的照片

在我之前的两篇文章中(这里这里),尽管快速介绍了使用神经网络创建的单词嵌入,但我主要关注的是传统的机器学习模型,这些模型将独热编码向量作为输入。然而,这些一键编码向量是一种非常幼稚的表示文本的方法,线性分类器不能处理像人类语言这样的非线性现象。这就是神经网络的用武之地。不仅仅是任何神经网络,而是可以处理顺序数据的网络(或者是遵循特定模式的一般数据,不一定是顺序的,但在一点上更多)。

即使您不知道神经网络是如何工作的(解释这一点超出了本文的范围),我也假设您以前见过这样的图像:

来源:维基百科

如您所见,只有一个输入层,因此对于这个简单的前馈神经网络,输入数据将是一维的。人们可以将一个句子中每个单词的单词嵌入连接起来,但是,这会产生不同长度的输入嵌入。这是有问题的,因为我们有一个固定的输入大小。此外,假设您想要创建一个段落或者甚至一个文档嵌入——这将创建巨大的向量。另一种方法是对单个单词的单词嵌入进行平均,以获得句子/段落/文档嵌入。

作者图片

然而,当向量通过加法/减法/乘法线性组合时(或者随后求平均或者不求平均),所得向量不能唯一地分解成其原始分量,这意味着关于各个向量的所有信息都丢失了,因为实值向量可以以无限多种方式分解成分量。如果你看上图每个向量中的第一个数——(0.6+0.1+0.9+0.2)/4 = 0.45。但是 0.3 + 0.7 + 0.2 + 0.6 的平均值会给出相同的结果。因此,一段完全不同的文本可能会导致类似的文档嵌入。

所以简单的前馈神经网络架构不会让我们走得很远。这就是卷积神经网络(CNN)、递归神经网络(RNNs)、长短期记忆神经网络(LSTMs)、序列-2-序列模型和变压器的用武之地。

CNN

让我们从一个简单的例子开始,为了演示 CNN 的使用,这个例子甚至不涉及单词嵌入。CNN 有能力检测数据中的复杂特征,例如,从图像和文本中提取特征。它们主要用于计算机视觉(例如,用于图像分类、对象检测和图像分割),然而,它们也应用于涉及文本数据的问题。CNN 由两个主要层组成:用于从数据中获取特征的卷积层,以及用于减小特征图大小的汇集层。简而言之,卷积是在特征检测器(也称为核或滤波器)的帮助下获得特征的过程。例如,它可以是一个 3 x 3 的矩阵,在输入矩阵(一个图像)上滑动,并执行内核和输入矩阵的逐元素乘法。为了捕捉不同的模式,可以让几个 3 x 3 的矩阵在输入矩阵上滑动。此外,输入中的每个点可以有几个维度。这些被称为渠道。例如,在图像中,图像中的每个像素有三个通道,对应于 RGB 分量。对文本数据使用相同的类比,文本中的“像素”是单词。因此,初始输入通道的数量是词汇的大小(对于一键编码向量)或维度大小(对于单词嵌入)。在下面的例子中,你可以看到两个不同的内核滑过一段文本和它们的卷积输出。

来源: d2l.ai

作为一个例子来证明 CNN 的有用性,我们将使用姓氏分类。由于显而易见的原因,不能嵌入姓氏,因此字母(和符号)的一次编码是创建向量表示的合适方法。使用线性方法或简单的前馈神经网络,将不能找到揭示姓氏起源的片段(例如爱尔兰姓氏中的“O’”,如“O’brien”,俄语的结尾“ov”,以及乌克兰语的结尾“ko”)。这些数据段可以是可变长度的,挑战是在不显式编码它们的情况下捕获它们。CNN 非常适合这种情况,因为如前一段所述,它们可以检测空间子结构。然而,它们不能捕获顺序数据,这就引出了我们的下一个话题。

RNNs 和 LSTMs

传统的机器学习假设数据点独立同分布(IID)。然而,通常一个数据项依赖于它前面或后面的项,例如在语言中。语言是连续的。如上所示的 CNN 和简单的神经网络不能对序列建模。建模序列包括维护一个隐藏状态。这种隐藏状态捕获以前的信息,并随着每个新的数据片段而更新(例如,模型看到的句子中的新单词)。

最基本的神经网络序列模型是递归神经网络(RNN)。最基本的 RNN 只有一个隐藏状态,因此只有一个隐藏的单元。在阅读关于 RNNs 的文章时,你会碰到这个术语,它会在人们中间造成一些混乱。该名称不是指单个细胞,而是指整个层。但是,RNN 会自我反馈(前一时间步的输出成为当前时间步的输入),因此 RNN 图层包含一个单独的滚动 RNN 像元,该像元会根据您提供的时间步/时间段/单词的数量展开。

来源:towardsai.net

LSTM 号登月舱前来救援。LSTM 基本上是一个花哨的 RNN。它们有一个额外的“记忆细胞”,能够通过“门”控制在记忆中删除或添加信息。然而,尽管部分解决了爆炸和消失梯度问题,我们仍然有一个从旧单元到当前单元的顺序路径。尽管他们能够学习很多长期的信息,但是他们不能记住一千或一万个单词的序列。RNNs 和 LSTMs 也不是非常硬件友好,并且需要很长时间来训练。

Seq2seq 型号

在上面的姓氏分类示例中,模型的输入是字符序列,输出是单个类(例如“Scottish”)。但是像机器翻译这样输入和输出都是序列的任务呢?序列对序列学习(seq2seq)是关于训练模型将序列从一个域转换到另一个域的序列。这种任务的主要挑战是,对于每个训练示例,输入和输出序列具有不同的和变化的长度,并且为了预测目标,需要整个输入序列。因此,需要更高级的设置。不涉及太多的技术细节,它是这样工作的:

RNN 或 LSTM 层(或它们的堆栈)充当“编码器”:它们处理输入序列并输出最终的隐藏状态向量,该向量旨在封装所有输入元素的信息,以便帮助解码器做出准确的预测。这个向量充当模型的解码器部分的初始隐藏状态。另一个 RNN 或 LSTM 层(或堆栈)充当“解码器”:它们被训练来预测目标序列的下一个字,给定目标序列的前一个字。解码器使用来自编码器的最终隐藏状态向量,以便获得关于它应该生成什么的信息。为了防止解码器将错误的预测传播到未来的预测中,一旦做出预测(因此偏移一个时间步长),来自目标序列的正确单词就被馈送到解码器中,这一过程被称为“教师强制”。实际上,解码器学习在给定 target[t]的情况下,根据从编码器接收的初始隐藏状态向量,顺序生成 target[t+1]。

来源:kdnuggets.com

您可能已经看到了这种体系结构的一个明显的缺点:使用单个向量对整个输入序列进行编码无法捕获全部信息。因此,处理长句中相距甚远的单词之间的长期依存关系的挑战仍然存在。另一个限制是由于顺序计算,这种模型的训练和推理时间长。有几种方法可以解决第一个问题,例如反转输入编码器的句子,因为它缩短了从解码器到编码器相关部分的路径,或者将输入句子输入两次。然而,这些方法并不适用于所有语言。大多数基准是为英语、德语和法语等语言设定的,这些语言都有非常相似的词序(它们是 SVO 语言——主语、动词、宾语)。然而,像日语和韩语这样的语言是 SOV。因此,一个 SOV 语言句子中的最后一个单词可能是 SVO 语言中的第二个单词。在这种情况下,逆转输入会让事情变得更糟。

注意和变形金刚

那么,除了上面提到的处理长期依赖的方法之外,还有什么替代方法呢?答案是——注意。注意力是目前深度学习领域最强大的概念之一。它是基于我们在处理大量信息时“注意”某一部分的直觉。该概念首先由 Bahdanau 等人(2015) 提出,他建议利用上下文向量来对齐源输入和目标输入。上下文向量保留来自编码器单元的所有隐藏状态的信息,并将它们与当前目标输出对齐,而不是从编码器的最后隐藏状态构建单个上下文向量。通过这样做,解码器能够关注源输入的特定部分,并且更好地了解源和目标之间的复杂关系。在非常一致的语言中(例如德语和英语),解码器可能会选择或多或少地按顺序处理事情(见下图,其中注意力是按顺序的,直到“欧洲经济区”,在法语中是“zone économique européenne”)。但是,因为上下文向量可以访问整个输入序列,所以我们不需要担心忘记长期依赖关系。

源句子和目标句子之间注意力权重的可视化。来源:神经语言处理中的分析方法:综述

因此,随着长期依赖性问题的解决,我们如何解决由于上述编码器-解码器架构的顺序性质而导致的长训练时间的问题呢?在阅读 NLP 时,您可能曾经遇到过“transformer”这个术语。transformer 是一种架构,旨在解决序列到序列的任务,这些任务使用注意力来处理长期依赖性。它最初是在论文 中介绍的 ,从那以后包括伯特(谷歌)GPT【OpenAI】在内的几个项目都建立在这个基础上。

不涉及太多细节,变压器包含一堆相同的编码器层和一堆相同的解码器层。该转换器的架构不包含任何递归或卷积,这要归功于自我关注和位置编码,它抛弃了所有顺序操作,支持并行计算。与上述注意相比,自我注意将序列中的单词与该序列中的其他单词对齐,从而计算该序列的表示。

自我注意力的观想。谷歌博客

最后的话

在这篇文章中,我们仅仅触及了自然语言处理和深度学习中所有不同突破的表面,但我希望我能够给你一个更“传统”的神经模型的概述,以及像 transformer 这样的新架构如何解决它们的缺点。有很多资源可以帮助你开始学习 NLP 的神经模型。下面列出了一些我最喜欢的书和教程。

书籍:

博客:

NLP 和客户漏斗:使用 PySpark 衡量事件

原文:https://towardsdatascience.com/nlp-and-customer-funnel-using-pyspark-to-weight-events-d5ff8d2f61b4

利用 NLP 和 PySpark 增强营销漏斗分析和优化。使用 PySpark 分析和改善顾客体验的实用指南。

Unsplash 上拍照

本文讨论了如何使用 PySpark 为客户漏斗中的事件实现词频-逆文档频率(TF-IDF)加权,以此为机器学习构建预测购买的功能。TF-IDF 是一种统计度量,用于评估一个或一组文档中的单词或短语的重要性。通过使用 PySpark 计算 TF-IDF 并将其应用于客户漏斗数据,我们可以获得对客户行为的宝贵见解,并提高机器学习模型预测购买的性能。

在本文中,我们将讨论以下内容:

客户漏斗
使用 TF-IDF 来衡量事件
什么是 TF-IDF?
用 PySpark 计算 TF-IDF

顾客漏斗

按作者

客户漏斗,也称为营销漏斗或销售漏斗,是一个概念模型,代表客户从了解产品或服务到购买点的过程。漏斗通常被描绘为一个向下变窄的宽顶,每个阶段代表客户旅程中的一个不同阶段。

客户漏斗的阶段通常包括:

  1. 认知:这是顾客旅程的第一阶段,顾客开始意识到产品或服务。这可以通过广告、社交媒体、口头传播或其他形式的营销来实现。
  2. 兴趣:在这个阶段,顾客对产品或服务产生兴趣,并开始进一步研究。他们可能会将其与其他类似的产品或服务进行比较,阅读评论,或访问公司的网站以了解更多信息。
  3. 决策:在考虑了他们的选择后,顾客做出是否购买产品或服务的决定。他们可能还会考虑价格、可用性以及任何附加功能或好处。
  4. 行动:如果顾客决定购买产品或服务,他们通过完成交易来采取行动。这可能包括填表、打电话或完成网上购物。
  5. 保留:在初次购买后,客户进入保留阶段,该阶段的重点是保持客户的满意度和忠诚度。这可能包括提供出色的客户服务、提供促销或折扣,或者提供额外的支持或资源。

了解客户漏斗可以帮助企业了解如何有效地营销和销售他们的产品或服务,并确定他们可以改善客户体验的领域。

使用 TF-IDF 衡量事件

TF-IDF 代表“术语频率-逆文档频率”,是一种统计方法,可用于为文档中的单词或短语分配权重。它通常用于信息检索和自然语言处理任务,包括文本分类、聚类和搜索。

在客户漏斗的背景下,TF-IDF 可用于衡量客户在漏斗中移动时采取的不同事件或行动。例如,如果客户访问公司网站上的产品页面,那么该事件在客户漏斗中的权重可能会比他们只是阅读关于该产品的博客帖子或社交媒体帖子更高。类似地,如果客户进行了购买,则该事件可能会被赋予比他们只是将商品添加到购物车中但没有完成购买更高的权重。

使用 TF-IDF 来衡量客户漏斗中的事件,可以帮助企业更好地了解他们的客户如何与他们的产品或服务进行交互,并确定他们可能能够改善客户体验或提高转化率的领域。要在这种情况下使用 TF-IDF,企业通常需要使用客户关系管理(CRM)系统或其他软件来跟踪客户互动和行为,然后将 TF-IDF 算法应用于这些数据,以计算每个事件的权重。这些权重可用于确定营销工作的优先顺序和目标,或识别客户行为的模式和趋势。

什么是 TF-IDF?

按作者

TF-IDF,或“术语频率-逆文档频率”,是一种统计方法,它告诉我们一个词在一组文档中有多重要。它有两个组成部分:

  1. 术语频率(TF):这衡量一个术语在文档中出现的频率。它是通过将一个术语在文档中出现的次数除以该文档中的总字数来计算的。例如,如果一篇 500 字的文章提到单词“horse”4 次,而一篇 2000 字的文章提到单词“horse”5 次,那么每篇文章的词频将是不同的。
  2. 逆文档频率(IDF):这衡量一个术语的重要性。它有两个目标:减少频繁使用的词(如“the”和“is”),增加独特的和较少使用的术语。计算方法是将文档总数除以包含该术语的文档数。例如,如果一个术语出现在 100 个文档中的 10 个中,则逆文档频率将低于它仅出现在 100 个文档中的 1 个中的情况。

通过使用自然语言处理(NLP)和 PySpark,我们可以分析客户漏斗中有意义事件的候选列表,并给予独特事件比整体语料库更大的权重。这使我们能够了解客户旅程中每个事件的重要性,并做出更明智的决策。

用 PySpark 计算 TF-IDF

为了计算一组事件的 TF-IDF,我们可以使用 PySpark 按类型对事件进行分组,并计算每种类型出现的次数。然后,我们可以通过将文档总数除以每种事件类型的出现次数来计算逆文档频率。

下面是一个示例,说明如何使用 PySpark,使用特定时间窗口内的客户交互样本数据集,为客户漏斗中的事件实施 TF-IDF 加权:

  1. 首先,您需要安装 PySpark 并设置一个 SparkSession:
!pip install pyspark
from pyspark import SparkContext
from pyspark.sql import SparkSession

sc = SparkContext.getOrCreate()
spark = SparkSession(sc)!pip install pyspark
from pyspark import SparkContext
from pyspark.sql import SparkSession

sc = SparkContext.getOrCreate()
spark = SparkSession(sc)

2.接下来,您需要将您的客户交互数据集加载到 PySpark 数据框架中。出于本示例的目的,我们假设您有一个包含以下列的 CSV 文件:

  • customer_id:每个客户的唯一标识
  • event_type:客户执行的事件类型(例如,“查看产品”、“添加到购物车”、“进行购买”)
  • timestamp:事件发生的时间和日期

您可以使用spark.read.csv()方法将该数据集加载到 DataFrame 中:

df = spark.read.csv("customer_interactions.csv", header=True)
df.show()df = spark.read.csv("customer_interactions.csv", header=True)
df.show()

3.要计算特定时间窗口内每个事件的 TF-IDF 权重,您需要使用窗口函数按时间窗口划分数据,并为每个事件分配一个等级。您可以使用window()partitionBy()rank()方法来完成此操作:

from pyspark.sql.functions import window, rank

window_spec = window.partitionBy("customer_id").orderBy("timestamp")
ranked_df = df.withColumn("rank", rank().over(window_spec))
ranked_df.show()

4.接下来,您需要计算每种事件类型的术语频率(TF)。您可以使用groupBy()count()方法来实现这一点,然后将得到的数据帧连接回原始的分级事件数据帧:

tf_df = ranked_df.groupBy("event_type").count().withColumnRenamed("count", "tf")
ranked_tf_df = ranked_df.join(tf_df, on="event_type")
ranked_tf_df.show()

5.要计算逆文档频率(IDF ),您需要计算在时间窗口内执行每种事件类型的客户数量,然后计算客户总数除以该计数的对数。您可以使用count()withColumn()log()方法来完成此操作:

from pyspark.sql.functions import log

customer_count = ranked_df.select("customer_id").distinct().count()
idf_df = ranked_tf_df.withColumn("idf", log(customer_count / ranked_tf_df["tf"]))
idf_df.show()

6.最后,您可以通过将 TF 和 IDF 值相乘来计算每种事件类型的 TF-IDF 权重。您可以使用withColumn()方法来完成此操作:

pyspark.sql.functions import col

tf_idf_df = idf_df.withColumn("tf_idf", col("tf") * col("idf"))
tf_idf_df.show()

这将为您提供一个数据框架,其中包含客户互动数据集中每种事件类型的 TF-IDF 权重。然后,您可以使用这些权重来确定营销工作的优先级和目标,或者确定客户行为的模式和趋势。例如,您可以使用 TF-IDF 权重来确定客户漏斗中最重要的事件,并将您的营销工作集中在这些事件上。或者,您可以使用权重来确定不同事件之间的模式或相关性,这可以帮助您优化营销策略并改善客户体验。

客户漏斗是一个模型,代表了客户从认知到购买产品或服务所经历的阶段。了解客户漏斗可以帮助企业了解如何有效地营销和销售他们的产品或服务,并确定他们可以改善客户体验的领域。TF-IDF 是一种统计方法,可用于为文档中的单词或短语分配权重,并可在客户漏斗的上下文中用于衡量客户采取的不同事件或行动。通过使用 TF-IDF 对客户漏斗中的事件进行加权,企业可以更好地了解他们的客户,识别客户行为的模式和趋势,并提高机器学习模型的准确性。使用 PySpark,企业可以轻松地为其客户漏斗数据中的事件实施 TF-IDF 加权。

自然语言处理:建立一个语法纠错模型

原文:https://towardsdatascience.com/nlp-building-a-grammatical-error-correction-model-deep-learning-analytics-c914c3a8331b

用数据做很酷的事情

梁杰森Unsplash 上的照片

介绍

语法纠错(GEC)系统旨在纠正文本中的语法错误。语法上就是这样一个语法修正产品的例子。纠错可以提高电子邮件、博客文章和聊天中文字的质量。

GEC 任务可以被认为是一个序列对序列的任务,其中一个转换器模型被训练成以一个不合语法的句子作为输入,并返回一个语法正确的句子。在本帖中,我们将展示如何训练这样一个模型,并在训练时使用权重和偏差来监控模型的性能。我们还在 Spaces 这里发布了我们训练过的模型用于实验。代码也在 Colab 这里和 Github 这里公开。

在书面语言中遇到的错误可能有不同的类型,如下图所示。

书面语言中遇到的错误。来源:作者提供的数据

资料组

对于我们的语法校正器的训练,我们使用谷歌最近发布的 C4_200M 数据集。这个数据集由 200 毫米合成生成的语法错误以及正确的文本组成。

在 GEC,最大的挑战之一是获得各种各样的数据来模拟通常在书面语言中犯的错误。如果损坏是随机的,那么它们不能代表真实用例中遇到的错误的分布。

为了生成讹误,首先训练标记讹误模型。通过将干净文本作为输入并生成损坏的文本,在现有数据集上训练该模型。例如,输入句子将是“有很多羊”,腐败模型将把它改为“有很多羊”。所以它会生成语法错误的输出。

对于 C4_2OOM 数据集,作者首先确定了书面语中遇到的相对错误类型的分布。当产生讹误时,它们取决于错误的类型。例如,一个名词变化错误会把一个正确的句子作为输入。

正确的句子— “有很多羊”**

带有名词屈折错误的不正确句子-

  1. “有很多
  2. “有很多羊”

这使得 C4_200M 数据集具有一组不同的误差,反映了它们在现实应用中的相对频率。要了解更多关于生成综合破坏的过程,请在此处参考原始论文

为了这篇博文的目的,我们从 C4_200M 中提取了 55 万个句子。C4_200M 数据集在 TF 数据集上可用。我们提取了我们需要的句子,并保存为 CSV 格式。此处的数据准备代码被推送到 Colab 这里。如果您有兴趣下载准备好的数据集,可以在此访问

下面是 C4_200M 数据集的截图。输入是不正确的句子,输出是语法正确的句子。这些随机例子表明,数据集涵盖了来自不同领域和各种写作风格的输入。

C4_200M 数据集截图

模特培训

在这次培训中,我们将使用 Google 的通用 T5 模型。

T5 是文本到文本模型,这意味着它可以被训练成从一种格式的输入文本到一种格式的输出文本。我个人使用这个模型有很多不同的目的,比如摘要(见博客这里)和文本分类(见博客这里)。还用它构建了一个琐事机器人,它可以在没有任何上下文的情况下从内存中检索答案。点击查看这篇博客

T5 —文本到文本转换转换器。图片来自 T5 纸。

对于很多任务,我更喜欢 T5,原因有几个——1 .可用于任何文本到文本的任务,2。微调后对下游任务的准确性好,3 .使用 Huggingface 易于训练

在来自 C4_200M 的 550K 样本上训练 T5 模型的完整代码可从 Colab 上的这里获得。也分享在我的 Github 上这里

培训的高级步骤包括:

我们将不正确的句子设置为输入,将正确的文本设置为标签。输入和目标都使用 T5 记号化器进行记号化。最大长度设置为 64,因为 C4_200M 中的大多数输入是句子,并且假设该模型也将用于句子。完成标记化的代码片段如下。

语法纠错模型的标记器

2.使用 seq2seq 训练器类训练模型****

我们使用 Huggingface 中的 Seq2Seq trainer 类来实例化模型,并实例化对 wandb 的日志记录。对 HuggingFace 使用权重和偏差非常简单。所有需要做的就是在训练参数中设置report_to = "wandb"

T5 模型的定型参数和定型

3。监测和评估模型

我们使用了 Rouge 评分作为评估模型的标准。正如在 W&B 下面的图中所看到的,该模型在一个时期的训练后获得了 72 的胭脂分数。

一轮训练后的准确度

T 此处可通过权重和偏差访问他的项目。

在空间上发布模型

我们已经将训练好的模型推送到 Spaces 这里,这样它就可以被测试了。如下面的截图所示,它可以被编程为返回最多 2 个正确的序列。

在空格上测试语法修正器

我已经在许多不正确的序列上测试了这个模型,并且对它的性能很满意。

该型号在 hugginface.co这里也有售,也可以直接使用。模型文档显示了使用模型所涉及的步骤。

使用训练好的模型进行推理

结论

这篇博文展示了利用 HuggingFace 和 WandB 为不同用例训练 NLP 模型是多么容易。我希望你尝试一下 HuggingFace Spaces,并在下面分享你的经历。

深度学习分析,我们专门为各种用例构建定制的机器学习模型。我们与世界各地的客户合作,为他们的特定需求构建解决方案。我们的专家团队拥有文本分类、翻译、摘要、神经搜索等方面的经验。如果你看到合作的机会,请发邮件到 info@deeplearninganalytics.org 给我们。

原载于 2022 年 4 月 1 日 https://deeplearninganalytics.orghttps://deeplearninganalytics.org/nlp-building-a-grammatical-error-correction-model/。****

亚马逊 SageMaker 上的 NLP 数据增强

原文:https://towardsdatascience.com/nlp-data-augmentation-on-amazon-sagemaker-4d5b77b5512f

使用 SageMaker 处理作业,通过拥抱脸的变形模型轻松扩充您的 NLP 数据集

马库斯·温克勒在 Unsplash 上的照片

联合博文由 索比亚汗若昂莫拉黑科霍兹 。观点是我们自己的。****

这篇博文是关于什么的?

机器学习模型是非常数据密集型的,对于自然语言处理(NLP)模型来说尤其如此。作为一个例子,让我们考虑一个 NLP 模型,该模型试图检测客户评论的情绪是积极的还是消极的。因为人类的语言如此丰富,有几乎无限多的方式来传达同样的意思和情感。在理想世界中,我们的 NLP 模型从所有这些不同的可能性中学习说同样的事情。当然,问题是,当我们训练模型时,我们手头几乎从来没有这种类型和数量的数据。

数据稀缺是自然语言处理中的一个常见挑战。在撰写本文时,拥抱脸模型中心包含超过 29,000 个模型。相比之下,他们的数据集中心“仅”包含 2,800 个数据集。这种挑战对于非英语语言来说更是如此。过滤数据集中心的德语(一种相当常见的语言)和文本分类(一项非常常见的 NLP 任务),留给我们的只有 19 个数据集可供选择:****

作者图片

这就是数据增强可以发挥作用的地方——这是丰富或综合扩大机器学习模型训练数据集的过程。

为什么数据增强很重要?

使用 NLP 数据增强有很多好处,我们想强调其中的三个主要好处:

  • 提高模型性能:数据扩充是提高模型性能的最有前途的技术之一。这篇论文显示,通过使用数据增强,文本分类任务的性能提高了近 3%。3%的性能提升可能听起来不算多,但对于 ML 模型性能来说,这实际上是一件大事。考虑到增加数据所需的少量工作成本,这一点尤其正确。
  • 访问更高级的模型:没有足够的数据来完成 NLP 任务有时会限制可以使用的 NLP 模型的选择。Google Research 和多所大学之间的一项综合研究表明,数据增强使得大规模 NLP 模型的使用成为可能,否则由于数据稀缺,这将不是一个可行的选择。
  • 偏差缓解:偏差是一个常见且具有挑战性的问题,尤其是在 NLP 中。我们可能都听说过 NLP 模型突然生成攻击性文本或仇恨言论的故事。这篇论文表明数据扩充可以显著消除 NLP 模型中的偏差。

我们将在本教程中做什么?

在本教程中,我们将在 Amazon SageMaker (SM)上演示 NLP 数据增强是简单、直接和经济高效的。我们将使用一种叫做 反向翻译 的技术,在这里我们将利用来自拥抱脸模型中心的翻译模型将客户评论翻译成不同的语言,然后再翻译回原来的语言。这种技术将产生一组新的顾客评论,它们在意义上非常相似或相同,但在措辞上有细微的不同。

大规模地这样做可能需要大量资源,这就是为什么我们展示如何使用 Amazon SageMaker 处理来扩充数据。通过 SM 处理,我们可以使用简化的托管体验来运行我们的数据处理工作负载,在我们的案例中,使用反向翻译来扩充数据。

如前所述,NLP 数据稀缺在非英语语言中尤其是一个挑战。然而,为了使本教程更容易理解,我们决定使用英语数据集。我们将从一个包含 100 条记录的小型数据集开始,到最后,我们的初始数据集将增加 15 倍以上,达到 1,500 多条记录。

说到 Github 回购,这里是我们回购的链接,在这里你可以找到我们所有的代码。我们开始吧!

反向翻译简介

反向翻译是扩充文本数据的常用技术。这种方法的想法是把一个句子翻译成任何其他语言,然后再翻译回原来的语言。结果文本与原始文本略有不同,并为我们提供了更多数据来训练模型:

作者图片

生成的文本可能使用稍微不同的术语,也可能不总是语法正确。但这是这种技术的一个受欢迎的特性,因为现实生活中的数据总是会带来可能有一些错误的文本。重要的是,整体情绪保持不变,文本略有不同。

解决方案概述

我们的方法利用 SageMaker 与 Hugging Face 的集成来下载数据集和翻译模型,并利用 SageMaker 处理作业来配置计算基础架构:

我们首先从拥抱脸数据集中心下载数据集,清洗它,并将其存储在 S3。然后,我们开始处理作业,并将其指向数据集的 S3 位置。我们还提供了一个将由作业执行的定制处理脚本。该脚本将从 S3 加载数据,从拥抱脸模型中心加载相应的翻译模型,执行反向翻译,并将生成的扩充数据集存储回 S3。最后,我们将回顾数据扩充的结果。

数据集

在本教程中,我们将使用 IMDB 数据集,它由 50,000 条带有情感标签的电影评论组成。该数据集是作为这篇论文的一部分创建的,并获得了知识共享署名 4.0 国际许可。我们可以从拥抱脸数据集中心下载数据集,并将其存储在熊猫数据帧中。我们将数据集限制为 100 条记录,并确保所有评论都是英文的:

让我们先来看看数据:

作者图片

我们可以看到评论里有一些 HTML 标签。如果我们保留它们可能不会有什么影响,但是删除它们很简单,所以让我们编写一个函数来做这件事并清理整个数据集:

最后,我们将数据作为 CSV 文件存储在 S3:

处理脚本

我们的处理脚本将从 S3 加载数据,然后执行几轮反向翻译。每一轮涉及两个翻译模型,一个是从英语到任意语言的翻译,一个是翻译回英语的翻译(例如 EN→FR,然后 FR→EN)。在每一轮之后,结果被附加到原始数据集,并且具有不同“中间”语言的新一轮反向翻译开始:

作者图片

*在本教程中,我们的目标是将数据集的大小增加约 15 倍,因此我们决定使用 4 轮反向翻译,这将产生 1,600 条记录(2⁴ 100)。在某些情况下,翻译可能在两个方向上都进行得很好,并且回译的文本将与原文相同。因此,我们必须对数据集进行重复数据消除,最终我们将得到总共不到 1,600 条记录。

运行处理脚本

在 SageMaker 上运行处理作业非常简单——只需设置一个 HuggingFaceProcessor 对象,并将其指向要处理的数据。如何做到这一点的代码可以在这个笔记本中找到。处理作业完成后,我们可以看到关于运行时间和其他关键指标的详细信息:

作者图片

在这种情况下,我们可以看到整个数据扩充练习花了大约 35 分钟才完成。

查看结果

一旦处理工作结束,我们就可以从 S3 提取结果,并检查我们得到了多少条记录:

我们从 100 个评论开始,现在我们有 1592 个评论,一点也不差!让我们回顾一些补充评论,并将其与原始评论进行比较:

作者图片

这看起来不错——就像我们上面的例子一样,我们可以看到评论的整体意义和观点被保留下来,但确切的措辞略有不同。这正是我们所希望的结果。这个数据集现在可以用于下游的 NLP 任务,如情感分析。

结论

在本教程中,我们将初始 NLP 数据集的大小增加了十倍以上,从 100 条记录增加到 1,592 条记录。我们通过使用反向翻译技术、利用 SM 处理作业以及整合拥抱脸和亚马逊 SageMaker 实现了这一点。扩充的数据集现在可以用于下游的 NLP 任务,例如情感分类。

感谢阅读,我们希望你喜欢这个教程,并发现它很有用。如有任何问题或意见,请联系我们。

NLP:使用 Checklist 和 ELECTRA 探索 SNLI 数据集中的数据工件

原文:https://towardsdatascience.com/nlp-explore-data-artifacts-in-snli-dataset-with-checklist-and-electra-ebbdd1b83cd0

前提假设句中的中性偏差和矛盾偏差

走火入魔摄影Unsplash 上拍照

动机

自然语言推理(NLI)是一种广泛研究的自然语言处理任务,用于确定两个陈述(前提和假设)之间的蕴涵关系。斯坦福 NLI 语料库(1.0 版本)是 57 万个人工书写的英语句子对的集合,人工标记用于在 NLI 任务上训练 NLP 模型。虽然有最先进的 NLP 模型可以在验证集上实现高精度,但由于 SNLI 的构造方式,这些训练过的模型是否理解自然语言的问题仍然存在争议。众所周知,SNLI 数据集包含基于性别、种族和民族刻板印象的刻板偏见。在本文中,我们将通过在 ELECTRA-small 模型上使用带有检查表的行为测试来分析和评估泛化性能,探索 SNLI 训练示例偏差,该模型具有两个不同的输入:一个具有前提-假设对,一个仅具有假设

SNLI 数据集

SNLI 数据集是通过向众包工作者展示一个前提句子(来源于 Flickr 图片说明)并要求他们为三个标签(蕴涵、NEU-特拉、矛盾)中的每一个生成相应的假设句子来创建的,这可能包含认知偏差。许多最新的 NLP 模型在验证集上实现了高精度。然而,验证集的准确性并不保证 SNLI 数据集范围之外的看不见的数据的成功。因此,重要的是采取替代方法,如行为测试来评估 NLI 任务的 NLP 模型。

为简单起见,我们可以利用拥抱面部的 SNLI 数据集。这个数据集有三个部分:训练、验证和测试。训练集有大约 55 万个实例,而验证集和测试集各只有 1 万个实例。数据集中有三列:前提、假设和标签。有三种标签类别:蕴涵、中性和矛盾。

行为测试

在研究论文中提出的行为测试:超越准确性:在 NLP 模型上使用 CheckLis t 对 NLP 模型进行行为测试,类似于软件工程世界中的单元测试。如果我们使用单元测试来探索软件核心功能的早期问题,那么行为测试结果将被用作判断训练好的 NLP 模型的泛化和探索偏差的主要标准。

CheckList 是一个开源库,有助于在经过训练的模型上执行行为测试。如清单站点所述,清单测试基于以下标准生成:

  1. 词汇+词性(任务的重要单词或单词类型)
  2. 健壮性(打字错误、无关的更改)
  3. NER(正确理解命名实体)
  4. 公平性、时间性(理解事件的顺序)
  5. 否定和语义角色标注(理解角色,如施事、宾语等)

本文只看词汇量+词性能力,这是对情感词的基本理解。

伊利克特拉

ELECTRA ( E 有效 L 获得En 根据 C 分类TokenREA准确)是一种自我监督的语言表征学习方法。它可以用于使用相对较少的计算来预训练变压器网络。即使在单个 GPU 上训练,ELECTRA-small 也能取得很好的效果。我们将用默认的启动代码和默认的超参数来训练 ELECTRA-small 模型 3 个时期。

图片由作者提供,灵感来自 ELECTRA 研究论文

方法

我们分四步进行实验:

  1. 我们将使用前提和假设对作为输入,在 SNLI 数据集上训练模型,并利用给定启动代码的默认设置来获得基线,以分析我们的实验结果。
  2. 我们将使用检查表来评估词汇+词性能力在最低功能测试(MFT)中的表现。
  3. 我们将使用与步骤 1 相同的配置训练另一个模型。但是,我们将删除前提列,只使用假设列作为输入。
  4. 最后,我们比较了在步骤 1 和步骤 3 中训练的模型上的行为测试结果,并总结了我们的实验。

火车模型

  1. 使用 pypi 的以下命令安装清单:
pip install checklist
jupyter nbextension install --py --sys-prefix checklist.viewer
jupyter nbextension enable --py --sys-prefix checklist.viewer

2.安装 pytorch:

pip install torch

3.安装空间模型:

python -m spacy download en_core_web_sm

4.关闭本回购:

git clone [https://github.com/cmphan/SNLI_ELECTRA_CheckList.git](https://github.com/cmphan/SNLI_ELECTRA_CheckList.git)

运行以下命令安装依赖项

pip install --upgrade pip
pip install -r requirements.txt

5.要使用 SNLI 自然语言推理数据集训练 ELECTRA-small 模型假设前提模型,可以运行以下命令。请注意,这可能需要几个小时

python3 run_hypothesis_premise.py --do_train --task nli --dataset snli --output_dir ./trained_model/

6.要评估最终训练模型的准确性,您可以使用以下命令:

python3 run_hypothesis_premise.py --do_eval --task nli --dataset snli --model ./trained_model/ --output_dir ./eval_output/

7.对“run_hypothesis_only.py”文件重复 4–6 的相同步骤,以训练仅假设模型

最低功能测试(MFT)

我们将把检查表测试应用于两个训练模型:假设-前提仅假设,并比较它们的性能。

首先,我们导入清单所需的库。

标签的数量有三种:蕴涵、中性和矛盾。训练好的模型在“trained_model”文件夹中。

为了测试您自己的模型,我们需要获得对release_data/sentiment/tests_n500中文本的预测,并将它们保存在一个文件中,其中每行有 4 个数字:预测(0 代表负,1 代表中性,2 代表正)和预测概率(负,中性,正)。我们将每个标签的概率写入predictions.txt文件。

如在清单测试说明中,标签将 0 定义为阴性,1 定义为中性,2 定义为阳性,而 HuggingFace 上的 SNLI 数据集使用 0 表示蕴涵,1 表示中性,2 表示矛盾。因此,我们必须应用线性映射层,以使经过训练的模型预测结果与清单测试一起工作。

图片作者。

因此,我们将 ELECTRA 模型的预测映射到清单标签,并获得最高的标签结果:

最后,我们可以加载情感分析套件并可视化 MFT 结果

前提-假设模型 MFT 结果

对于前提假设模型,行为测试失败率如下:

作者图片

作者图片

虽然该模型在验证测试中达到了 88.996 %,但它在泛化能力上的表现并不像行为测试表结果所显示的那样好。我们可以看看词汇能力的失败率,以探索训练模型的弱点,并分析这一结果如何暴露训练示例中中性和负面关系的形成中的偏差。词汇能力(MFT)结果如下:

作者图片

作者图片

对于情感,即词汇+词性能力,经过训练的前提假设模型可以很好地识别单个正面单词,而它在检测负面和中性单词方面有困难。验证集上的高准确率并不意味着模型对自然语言的理解。具体来说,该模型在单个否定词、单个中性词和上下文中的中性词等领域 100%失败。

中立和消极的偏见

由于经过训练的模型在正面词示例上表现良好,因此我们可以专注于分析负面词和中性词示例。

中性:该模型在中性词上 100%失败。在许多例子中,中性动词和形容词与航空公司名词一起使用,如座位、飞行员、航班等。例如,“飞机是国际的”,“飞机是美国的”,“服务是澳大利亚的”,模型预测 2(积极的)而不是 1(中性的)。这主要是因为在 SNLI 训练例子中形成中性假设的方式。大多数是通过原因和目的条款添加的。例如,前提句为“两只狗正在穿过一片田野”,中性假设将为“一些小狗正在跑着抓棍子。”以这种方式形成训练示例将导致训练的模型仅将带有目的子句的句子识别为中性,这就是为什么它不能在清单测试中检测简单句子中的中性情感。

负面:经过训练的模型在识别负面情绪方面也 100%失败。

作者图片

训练样本中的偏差是训练后的模型在泛化能力方面表现不佳的主要原因。许多众包工作者认为前提-假设对是矛盾的,但事实并非如此。例如,前提假设对:“三只狗在跑道上赛跑。”和“三只猫在一条跑道上赛跑。”因为诸如“狗”和“猫”之类的单词,这使得训练过的模型不能理解简单的负面单词。

假设仅模拟 MFT 结果

我们使用相同的 ELECTRA-small 模型,相同的起始代码和训练超参数,但我们删除了前提列,只使用假设句子进行训练。这背后的动机是因为可以访问假设的基线系统只能在基于先前背景知识理解语言的意义上执行 NLI。一个只有假设的基线实际上可以在 10 个 NLI 式数据集的大多数数据集上执行高于多数类的性能。

在最小功能测试中,假设模型的清单结果是有希望的,显著提高了模型在词汇+词性能力上的性能,尤其是在否定词和中性词方面。

作者图片

作者图片

通过从训练示例中移除前提列,我们在仅假设情感分类的验证测试上仅获得 69.630 %。然而,我们也消除了在形成句子之间的中性和负面关系时的偏见,并且帮助训练的假设仅更好地理解中性和负面情绪。与前提-假设模型在“单个否定词”、“单个中性词”和“上下文中的中性词”领域获得的 100.0 %的失败率不同,该模型大幅降低了失败率,并显示了其对自然语言的理解。然而,“单个正面词”的失败率增加到 79.4 %,这意味着仅假设模型不能很好地检测正面情绪。在 SNLI 数据集中,蕴涵关系主要是基于更具体的词的概括而形成的。比如前提句:“两只狗正在穿过一片田野。”需要假设句子:“户外有动物。”。显然,诸如动物乐器户外等通用词被选择来概括类似吉他海滩等词。因此,当我们在该模型的训练示例中删除前提列时,我们也影响了积极情感的这一概括方面,并影响了训练后的模型在类似 MFT 的概括测试中识别积极词语。

结论

总体而言,通过比较在前提-假设仅假设模型上的清单测试结果,我们可以探索 SNLI 数据集中的注释伪像和偏差,其影响泛化上下文中的训练模型性能。我们能够验证 SNLI 数据集形成在蕴涵关系上没有问题,因为训练的模型只有 2.9 %的失败率。然而,在中性和矛盾关系中仍然存在偏差,并且训练的模型仅通过学习假设句子来提高对其否定和中性词的理解。通过删除前提列,行为测试在消极和中性方面的改进加强了这样一个事实,即在前提假设对示例中仍然存在矛盾和中性关系的问题。我们希望进一步的研究能够消除在构建未来 NLI 数据集时的人为偏见。

希望本文对您有所帮助,帮助您更好地排查训练好的模型性能问题。

请随意使用本文的源代码:

https://github.com/cmphan/SNLI_ELECTRA_CheckList

参考文章/研究论文:

塞缪尔·r·鲍曼、加博·安格利、克里斯托弗·波茨和克里斯托弗·d·曼宁。2015.用于学习自然语言推理的大型标注语料库。

凯文·克拉克,Minh-Thang Luong,郭诉乐,克里斯托弗·曼宁。2020.伊莱克特:预先训练文本编码器作为鉴别器而不是生成器。

Suchin Gururangan,Swabha Swayamdipta,Omer Levy,Roy Schwartz,Samuel R. Bowman 和 Noah A. Smith。2018.自然语言推理数据中的标注神器。

亚当·波利亚克、杰森·纳拉多斯基、阿帕拉吉塔·哈尔达尔、雷切尔·鲁丁格和本杰明·范·杜尔梅。2018.自然语言推理中的假设只有基线。

马尔科·图利奥·里贝罗、吴桐双、卡洛斯·盖斯特林和萨梅尔·辛格。2020.超越准确性:带检查表的 nlp 模型的行为测试。

瑞秋·鲁丁格,钱德勒·梅,本杰明·杜尔梅。2017.自然语言推理中的社会偏见。第 74–79 页。

使用 DagsHub 的 NLP MLops 项目—在 AWS EC2 实例上部署您的 Streamlit 应用程序—第 2 部分

原文:https://towardsdatascience.com/nlp-mlops-project-with-dagshub-deploy-your-streamlit-app-on-aws-ec2-instance-part-2-eb7dcb17b8ba

让您的机器学习应用程序对世界开放,而不会让您头疼!

照片由 SpaceXUnsplash 上拍摄

介绍

除了开发机器学习模型,能够让外部世界访问它们也是一项非常有价值的技能。试图帮助你获得这样的技能是这篇文章的主要目标。它关注于使用**DagsHub****DVC****EC2**实例来使你的机器学习应用顺利产业化。

首先,您将能够构建您的**Streamlit**应用程序,然后了解将其部署到生产中的逐步方法。

机器学习任务—提醒

我们将要部署的应用程序来自于多语言分类项目。目标是预测以下 5 种语言之一的给定文本的潜在极性:英语法语德语俄语,以及西班牙语。请参考以上链接了解更多详情。

简化 it 应用构建流程

本节的目标是重点构建 streamlit 应用程序的两个主要特性,并在我们的本地机器上运行它。

:你也可以在文末找到 app 的完整源代码。

第一个主要特性——应用程序的第一部分

第一部分与显示 5 种语言中每一种语言的模型全球指标(F1 得分和准确度)相关。但在此之前,我们还将介绍徽标和描述部分的实现。

应用程序的第一部分——徽标、描述和指标部分(图片由作者提供)

  • 徽标、描述和指标部分

使用 streamlit [st.image](https://docs.streamlit.io/library/api-reference/media/st.image)可以直接显示图像。此外,[st.markdown](https://docs.streamlit.io/library/api-reference/text/st.markdown)可以显示降价格式的文本。

st _ logo _ 描述. py

通过使用[st.selectbox](https://docs.streamlit.io/library/api-reference/widgets/st.selectbox),我们能够在 F1-分数和准确性之间进行选择。默认情况下,自动显示该型号的 F1 分数。但是用户可以将其更改为精度

用户输入和模型预测——应用程序的第二部分

这个部分更多的是应用程序的智能部分。它预测用户提供的句子的潜在情感极性。为此,提供了两个选项,如下所示。

应用程序的第二部分——描述、下拉列表和自由文本部分(图片由作者提供)

  • 描述部分

此部分执行两个操作:显示🚀描述和用户可用的交互选项(选项 1 & 2)。每个动作都用一个简单的[st.markdown](https://docs.streamlit.io/library/api-reference/text/st.markdown)来执行

st _ part2 _ 描述. py

  • 下拉菜单和自由文本部分

用户可以从下拉列表中选择 5 个句子中的一个,每个句子都使用不同的语言。另一方面,他/她可以在自由文本区域提供他/她自己的文本进行预测。在这两种情况下,我们使用 streamlit [st.form](https://docs.streamlit.io/library/api-reference/control-flow/st.form)函数来获取用户的输入,然后最终加载模型并运行预测。

st _ 用户 _ 输入. py

在自由文本区域,首先执行检查,以确保用户提供的句子是模型考虑的 5 种语言之一。如果不是这种情况,就会显示一条好消息通知用户。

运行您的 streamlit 应用程序

构建好 app 后,就可以用streamlit run命令运行了。

streamlit run app.py

前面的命令生成以下消息,以从两个 URL 访问应用程序。

  • Local URL是我的本地主机,只有我能在我的机器上访问它。
  • Network URL与我相同子网内的任何人都可以访问的 URL。然而,这两个网络之外的任何人都不能访问我的应用程序。

在本地运行的 Streamlit 应用程序(图片由作者提供)

目标是让这个星球上的任何人都可以访问它!🪐,这是我们将在下一节做的。

AWS 上的应用部署流程

更自然的步骤是将应用程序部署到生产中;具体操作如下:配置 EC2 实例,使用 DVC 将模型和度量数据推送到 Dagshub,并在 EC2 上运行应用程序。

选择、创建和配置 EC2 实例

EC2 代表弹性计算云。它是一个虚拟服务器,用于在 Amazon Web 服务基础设施上运行应用程序。在本节中,我们将了解如何创建一个实例并执行配置。

选择 EC2 实例

登录 AWS 管理控制台后,可以从服务标签中搜索 EC2。它自动显示第二页来选择实际的 EC2 服务。

从服务选项卡中选择 EC2(图片由作者提供)

接下来,选择实例选项正下方的 实例 。您可以看到,我有一个 t2.xlarge 实例来托管我当前的 streamlit 应用程序😉。如果您尚未运行任何实例,此页面可能为空。

启动 EC2 实例(作者图片)

创建 EC2 实例— 6 个主要步骤

在选择了 Launch instances 选项卡之后,您会看到下面的页面,其中包含了从选择 Amazon 机器映像(AMI)到配置安全组的所有步骤。

创建 EC2 实例的 6 个主要步骤(图片由作者提供)

  1. 选择 AMI :包含操作系统、app server、apps 等软件配置的模板。它可用于在云中启动一个或多个实例。让我们通过点击选择按钮来选择第一个实例。

选择 AMI 实例:Amazon Linux 2 AMI —内核 5.10,SSD 卷类型(图片由作者提供)

2。选择实例类型: 实例类型提供特定的计算、内存和存储功能。根据我的应用程序要求,使用 2.5 Go 的大型号,我决定使用 t2.xlarge。如果你的应用程序不需要足够的存储空间,你可以使用 t2.micro,因为它是免费的。选择后,点击下一步的进入第三步。

选择 t2.xlarge 实例类型—ECU、4vCPUs、2.3 GHz、16GiB 内存、仅 EBS(图片由作者提供)

3。配置实例细节: 从这里,我们可以选择我们的用例所需的实例数量、网络类型等。正如我们所看到的,名字是不言自明的。我决定保留默认模式的配置。选择下一个的继续

实例详细信息的配置—默认模式下的所有内容(图片由作者提供)

4。添加存储: 默认的存储值是 8gb,但是因为我 app 的要求,我决定增加到 16。点击下一步继续。

添加 16 GiB 存储(图片由作者提供)

5。添加标签: 根据描述,标签由区分大小写的键值对组成。最多可以添加 50 个标签。为了简单起见,我跳过了这一步。选择下一个的进入第 6 步,即最后一步。

标签部分—跳过(按作者分类的图像)

6。配置安全组: 这是一组防火墙规则,旨在控制 EC2 实例的传入和传出信息。选择“添加”两次会在表格中创建两个新行。下面分别是第一个和第二个规则的配置。

  • 类型:自定义 TCP 端口
  • 协议:TCP
  • 端口范围:8501 对应 streamlit 端口。
  • Source: Anywhere,允许端口连接到任何地方。以这种方式更改会自动将地址设置为 0.0.0.0,:/0

对于第二个规则,我们得到了一个类似的配置,但是您只需要将端口范围更改为 8502,以便考虑 streamlit 的第二个端口。

streamlit 端口 8501 和 8502 的安全组配置(图片由作者提供)

我们最终可以单击查看并启动来检查配置。当你对一切都满意时,点击启动。这样做时,您将被要求创建或提供一个密钥对,这是尝试 SSH 连接到 EC2 实例时所必需的。

  • 从第一个下拉列表中,选择“创建新的密钥对”选项。
  • 我决定将密钥对类型留给 RSA。
  • 在密钥对名称部分提供一个有意义的名称。对我来说是。

单击下载密钥对以下载密钥对。只有这样,您才能通过选择“启动实例”来启动您的实例。

密钥对配置和实例启动(图片由作者提供)

您将获得成功启动消息“您的实例现在正在运行”。在该页面中,您可以通过单击查看实例按钮来访问您的实例。

成功发布消息(图片由作者提供)

正如您在下图中看到的,我有了新创建的实例,它处于运行状态

我正在运行的 EC2 实例(图片由作者提供)

瞧,你可以为自己能走到这一步而感到骄傲!!!好极了👏🏼👏🏼👏🏼

接下来是什么??? 我们确实想在 EC2 上运行应用程序,但是我们首先必须能够 SSH 连接到我们的 EC2 实例。

现在,我决定继续使用我之前创建的第一个实例,以避免被超额收费。😀

到 EC2 实例的 SSH 连接

获取连接变量

使用以下信息连接到我们的实例:

  • 生成的密钥对文件
  • EC2 实例的公共 IP 地址
  • 公共 IPv4 DNS

通过执行步骤 1 和 2,您可以获得公共地址和公共 DNS。

获取 SSH 连接信息的步骤(图片由作者提供)

配置~/。ssh/config 文件

将以下信息添加到配置文件中。这个文件的目标是使用一个ssh命令连接到 EC2 实例成为可能。

Host **streamlit-dagshub** HostName **18.117.129.66**
     User **ec2-user**
     IdentityFile <PATH TO YOUR KEY PAIR FILE>/<KEY PAIR.pem>
  • Host可以是任何名字。在我的例子中,我选择了 streamlit-dagshub
  • Hostname是公共 IPv4 地址。
  • User不可修改,仍为ec2-用户
  • IdentityFile是您的密钥对的完整路径。您下载的 pem 文件。

接下来,授予用户 read 权限,并使用下面的命令删除所有其他权限。

chmod 400 <PATH TO YOUR KEY PAIR FILE>/<KEY PAIR.pem>

之后,您可以连接到 EC2 实例,如下所示:

ssh **streamlit-dagshub**

SSH 成功连接到我的 EC2 实例

DagsHub、DVC 和 EC2 实例之间的交互

您可以在下面找到从推送源代码、模型和数据到在 EC2 实例中运行应用程序的一般工作流程。

一般工业化工作流程—从本地机器到 EC2 实例(图片由作者提供)

  • 任务 1 :使用 DVC 将源代码、模型和数据推送到 DagsHub。
  • 任务 2 :克隆源代码,提取模型,然后提取数据,将应用程序运行到 EC2 中。

任务 1: 从您的本地项目存储库执行

为了能够执行此任务,您首先需要配置您的 DVC 遥控器,然后将模型和数据推送到 DagsHub 存储。

上传 _ 模型 _ 数据. py

下面是前面脚本中使用的 params.yaml 文件的格式。

params.streamlit.yaml

任务 2: 从本地 EC2 实例执行

在运行应用程序之前,我们需要获得应用程序源代码和依赖项,然后从 DagsHub 存储中提取模型和数据。从 EC2 实例中:

  • git 克隆源代码
  • 提取模式和数据
  • 安装依赖项
  • 运行 streamlit 应用

准备 _ec2.py

将以下几行添加到您的 params.yaml.

params _ streamlit _ additional . YAML

我们最终可以从 EC2 实例运行 prepare_instance 脚本,然后使用streamlit run命令运行应用程序。

# Run the script to prepare EC2 instance
python prepare_ec2.py# Run the streamlit app
streamlit run app.py

在这个过程的最后,我们从 EC2 终端获得以下 URL 信息。然后将外部 URL 复制粘贴到您喜欢的浏览器中。

简化从 EC2 实例运行的 it

太好了!!!🎉🎉🎉我们的应用程序运行在 EC2 实例上。但是有一个问题。当我们退出控制台时,应用程序停止运行。这个问题可以用[tmux](https://doc.ubuntu-fr.org/tmux)来解决。

简化应用程序—让它持续运行

首先,从 EC2 实例安装 tmux 库

sudo yum install tmux

接下来,使用以下命令创建一个 tmux 会话

tmux new -s MultiLanguageSentimentStreamlitAppInstance

您可以用您想要的任何名称替换 multilanguagesentimentstreamlitapinstance,只要它有意义。然后在运行该命令后,我得到以下结果。

我在 tmux 会话下运行的 streamlit 应用程序(图片由作者提供)

与第一次在没有 tmux 的情况下运行相反,这一次,我们在终端的底部有一条绿色水平线,这意味着 tmux 会话是一个。你可以使用 Ctrl+C 退出终端,应用程序将继续运行,你可以将链接发送给居住在不同大陆的人,他或她将访问相同的应用程序。

下一步是什么???将公共地址映射到域名服务器,以便我们可以使用 my_app.com,而不是从公共 IP 地址访问。

结论

恭喜你!🎉 🍾您刚刚学习了如何使用 DagsHub 将您的 streamlit 应用程序部署到 AWS EC2 实例。如果您仍然对使用 DagsHub 犹豫不决,现在是时候让您和您的队友利用 DagsHub 的力量了。

如果你喜欢阅读我的故事,并希望支持我的写作,考虑成为一个媒体成员。这样做,我会得到一点佣金。

此外,您可以随时在 LinkedInYouTube 上添加我,或者在 Twitter 上关注我。讨论人工智能、人工智能、数据科学和自然语言处理的东西总是令人愉快的!

额外资源

AWS EC2 文档

DagsHub 网站

文章第一部分

Dag shub 上的源代码

链接到 AWS EC2 上的应用程序

再见🏃🏾‍♂️

使用 DagsHub 的 NLP MLops 项目—使用转换器的多语言情感分类—第 1 部分

原文:https://towardsdatascience.com/nlp-mlops-project-with-dagshub-multi-language-sentiment-classification-using-transformers-fcbe6164b171

DagsHub 是一个成功而高效的机器学习项目的平台

杰里米·贝赞格Unsplash 上拍摄的照片

介绍

一个成功的机器学习项目可能涉及不同的工具,每个工具都有特定的角色,从源代码和数据版本到模型性能跟踪。这些工具大部分时间是由不同的社区构建的,因此,它们的更好集成对于充分利用它们是至关重要的,否则,不幸的是,可能会导致更多的失望而不是满意。

接下来是一个平台,它可以托管源代码、版本数据集和模型,跟踪你的实验,标记你的数据,还可以提供你的结果的可视化。最后一款 GitHub 面向数据专业人士!

开始

本文将通过一个真实世界的机器学习项目来说明DagsHub的使用,该项目旨在执行多语言情感分类,从数据处理到模型监控。

NLP 问题和动机

全球各地的人们越来越多地在多个平台(社交媒体、评论网站等)上分享自己的观点。)关于组织,行业,用不同的语言。对这些情绪的有效分析可以帮助许多组织和行业做出正确的决策。

解决问题的方法

该问题将通过以下步骤解决

  • 数据采集&处理:我们将从不同的文件中采集数据并进行处理。该步骤将涉及数据版本控制(DVC) 工具,用于跟踪数据中的变更。
  • 数据探索:为了更好的理解我们正在处理的数据。 这将是我们使用笔记本的唯一一步
  • 模型评估&跟踪:没有特定的模型训练,因为我们将利用一个来自拥抱脸的预训练零镜头模型来执行情感分析。最后,我们将能够评估它在每种语言数据上的性能(准确性和 f1 分数),并使用 MLFlow 跟踪这些指标。

关于数据

情感 NLPRoc 上提供的 10 个多语言基准数据集中,我们将使用以下五个我们将使用的多语言模型当前支持的数据集。

项目范围的数据集描述(图片由作者提供)

履行

这一部分将包含重要的代码片段,但是整个项目和源代码可以在这里找到。请随意下载并跟随。

创建您的项目存储库

在开始这个过程之前,您需要在 DagsHub 上创建一个免费帐户,它会自动为您提供 10GB 的免费 DVC 存储空间!

只有在注册完成后,您才能创建您的第一个存储库,如下所示。

DagsHub 存储库创建(图片由作者提供)

一旦你的库被创建,你将得到一个类似于下面的快速指南。您可以简单地选择👁图标来可视化您的密码。

小节:克隆项目,用 DVC 提取项目数据,连接到 MLFlow 跟踪服务器(图片由作者提供)

…..

Sections truncated:在命令行上创建一个新的 repo 并推送一个现有的 repo(图片由作者提供)

数据收集

我在数据文件夹中创建了一个 zip 文件,其中包含五个不同的文件,格式为 a . tsv ,对应于关于数据部分中所述的五种语言。本节需要以下步骤:

  • 下载中的数据。zip 格式。
  • 创建两个文件夹数据文件夹: data/raw 将包含解压缩文件的结果,以及 data/process 将包含数据的处理格式(稍后讨论)。

可以运行以下脚本来同时执行所有这些任务

准备 _ 环境. sh

一旦这个阶段完成,您可以简单地从项目的根目录,从您的终端运行脚本,如下所示。别忘了。/ 在开始运行脚本时。

*# Grant execution right for the script
chmod +x prepare_environment.sh# Run the script 
**./**prepare_environment.sh*

成功执行会产生以下结果。

处理前数据文件夹的结构(图片由作者提供)

数据处理

需要注意的是,所有的 python 源代码都是在 src 文件夹中创建的,所以你需要从项目的根目录创建一个。

每个文件最初有两列,但是它们没有名字。我们的目标是:

  • 分别给两列命名。第一个对应标签,每个值都会映射到它的字符串格式(负:0,正:1)。第二列对应于文本。
  • 增加一个新的语言栏对应文字的语言。
  • 将所有的结合起来。tsv 文件成单个。csv 文件,它将对应于最终处理的文件。

预处理程序. py

在第 2 行中,我从我实现的包中导入了data_preprocessor()函数,以使代码可维护。该函数有 3 个参数,t 数据/原始文件的路径数据/已处理文件的路径,以及最终处理文件的名称。**

在第 5 行,我加载了。包含所有参数值的 yaml* 文件。以下是摘录。*

params.yaml

在成功执行前面的 preprocess.py 文件后,我们会得到以下结果。

处理后的数据文件夹结构(图片由作者提供)

我们终于可以运行以下命令来开始跟踪数据文件夹了。

第 1 行激活数据文件夹的跟踪。该指令创建一个 data.dvc 文件,其中包含关于原始数据存储位置的信息。然后,我们使用简单的git commit 命令添加文件并提交。完成所有这些后,原始数据文件夹被自动添加到中。gitigore* 文件,因为它没有被推送到存储库。我们现在已经准备好使用以下命令来推送所有新的更改。*

add_othefiles.py

确保真正考虑数据跟踪的是我们可以在数据文件夹上看到的 DVC 标志。

显示数据跟踪激活的 DVC 标志(图片由作者提供)

数据分析

这里的重点是可视化语言分布,显示每种语言的一些随机文本及其情感。

数据 _ 探索. py

  • 语言分布

下图显示了每种语言的情感数据数量。

数据集中的语言表示(作者提供的图片)

从前面的图像中,我们观察到英语是最具代表性的语言,而德语是最不具代表性的语言。

  • 每种语言的情绪分布

对于每一种语言,我们显示了积极的,消极的极性的数量。从一种语言到另一种语言,生成图形的过程是相同的。例如,英语的说明如下。

英语 _ 情绪 _ 分布. py

英语和法语的情感分布(图片由作者提供)

俄语和西班牙语的情感分布(图片由作者提供)

德国人的情感分布(图片由作者提供)

对于所有 5 种语言,情绪分布图显示积极情绪多于消极情绪。

  • 每种语言两个随机文本

该部分使用show_random_text()功能执行。采用的主要参数是特定语言的熊猫数据帧。

*# Example of English data
show_random_text(en_df)*

为英文

***Textual data**: grandpa po &apos;s originals are crunchy nuggets made from organic popcorn and organic soybeans. the healthy snack offers the benefits of soy without genetically modified organisms , cholesterol , peanuts or preservatives. in addition , the snack is low in saturated fat and it tastes great.  Underlying **Underlying gSentiment**: positive*

为法语

***Textual data**: quand le voisin fox essaie de copier sur son voisin abc , voilà le piètre résultat que ça donne ... quintuplés n'est qu'une copie des sauvages , et une copie bâclée en plus ...**Underlying Sentiment**: negative*

对于俄语

***Textual data**: я не думаю , что в москве можно найти отель такого же уровня за эти деньги . если у вас бизнес поездка в столицу и ограничен бюджет , то этот отель для вас . очень удачное месторасположение , близкая транспортная развязка позволяет добраться в любой район города в кратчайшие сроки . номер был без излишеств , минимализм чувствуется во всем . отдельное спасибо персоналу отеля : доброжелательность сглаживает те незначительные недостатки , какие замечаешь за несколько дней пребывания в отеле **Underlying Sentiment**: positive*

西班牙语

***Textual data**: comida abundante , buena relacin calidad-precio si pides entrante + segundo se puede cenar por unos 12 euros**Underlying Sentiment**: positive*

对于德语

***Textual data:** ambiente eines billigen strandclubs in der türkei , der nachbar sitzt fast auf dem schoss weil es eng ist , die musik laut und unpassend ( fetenhits 80er ) , gedränge und warme getränke die man gewöhnlich kalt trinkt . der eingang wird von 2 arroganten kleinen mädchen bedient , die sich auf irgendetwas was einbilden , unklar auf was . dazu gehen im laden afrikanische prostituierte auf männerfang . achja das essen : zu teuer , aber gut . für 1/3 des preises in anderen lokalen anzurufen . fazit : viel lärm um nichts**Underlying Sentiment**: negative*

模型评估和性能跟踪

现在,是时候检查预训练模型在预测这 5 种语言的正确情绪方面的表现了。我们将比较模型的预测和真实的情绪值。

首先要做的是如下配置 MLFlow 跟踪。

mlflow_config.py

第 2 行获取包含我的 mlflow URI用户名密码的凭证文件。下面是如何获得这些证书的说明。**

DagsHub 中凭据和信息的映射(图片由作者提供)

配置完 mlflow 之后,我们可以运行指标日志记录流程。我决定对每种语言的 100 个例子进行评估,这样这个过程就不会花太多时间。毕竟,主要目标是理解过程。

evaluate_tracking_section.py

我首先在线 2 定义我的预训练模型。然后在第 7 行中,我给我的实验命名,在这种情况下,它被称为多语言分类

测井过程从第 9 行的中的mlflow.start_run()开始。最后,我对每种语言运行一个循环,记录模型的准确性和 f1 分数。

成功运行评估后,您可以在实验选项卡上看到您已经执行的实验数量(包括失败和成功),在我的例子中是 10。

上一次 mlflow 模型追踪实验截图(图片由作者提供)

当您选择名称部分时,您可以找到如下所示的详细信息。我没有记录任何参数,这就是为什么左边的参数表是空的。然而,我们可以在右侧看到所有的准确度和 f1 分数值。

Mlflow 模型实验表格格式(图片由作者提供)

如果您想要不同的可视化效果,可以在图表模式下看到相同的结果,如下所示。

Mlflow 模型实验图表格式(图片由作者提供)

结论

恭喜你!🎉 🍾您刚刚从DagsHub 学会了如何使用 DVC 和 MLFlow 成功运行您的机器学习项目!如果你仍然不愿意使用DagsHub,试一试,因为我相信它可以帮助你和你的队友节省时间,创造更多价值。

找到下面的附加资源来进一步学习。

如果你喜欢这篇文章,关注我的文章更新

欢迎在 LinkedInYouTube 上加我,也可以在 Twitter 上关注我。讨论人工智能,人工智能,数据科学,自然语言处理的东西总是令人愉快的!

源代码和 DagsHub

数据集鸣谢

再见🏃🏾

具有增强、攻击和基于方面的情感分析的 NLP 项目

原文:https://towardsdatascience.com/nlp-project-with-augmentation-attacks-aspect-based-sentiment-analysis-3342510c90e7

你知道这三个高级 NLP 概念吗?

图片来自 Unsplash 的 Charlota Blunarova

你听说过自然语言处理(NLP)的这三个领域吗?

  • 文本扩充
  • 文本对抗性攻击
  • 基于方面的情感分析

在本文中,我将展示这些高级主题的价值,以及它们如何改进您的下一个 NLP 项目。虽然这将是一个解释性的概述,你可以在我的 GitHub 这里找到注释代码。

资料组

我将使用来自 Kaggle 的女装电子商务服装评论数据集。

作者图片

我们需要的两个变量是:

  • 评论正文:服装评论的字符串变量
  • 推荐的 IND: 表示客户是否推荐产品的二进制变量(其中 0 表示不推荐,1 表示推荐)

文本扩充

使用这个数据集,我们的第一个目标是实现一个用于二进制分类的监督深度学习模型。

然而,有一个突出的问题:数据集不平衡
绝大多数顾客提供了积极的反馈,并推荐他们购买的产品。

图片由作者提供。约 82%的总观察值被标记为推荐值。

这就产生了一个问题,因为我们的模型会偏向于大多数类别(推荐产品)。换句话说,由于在训练期间缺乏负面例子,该模型可能难以预测不推荐的评论。

我们可以看到这一弱点反映在我在不平衡数据集上训练的 LSTM 神经网络的模型指标中。

图片由作者提供。由于稍微不平衡的数据集,少数类预测的性能指标明显较低。

那么我们如何平衡数据集呢?

  1. 一种常见的方法是简单地对负面评论进行补充。这意味着我们复制当前不推荐的观测值,并将它们添加到我们的数据集中。
  2. 更高级的选项是文本增强。这里,使用预先训练的单词嵌入,单词被随机交换、删除、替换或插入同义词。我用简易数据扩充技术实现了这个。

图片由作者提供。当“妈妈”和“孩子”这样的词被替换或删除时,评论的整体含义保持不变。

对于上采样和文本增强方法,我将数据集平衡为 16,104 条不推荐的评论(或原始大小的 4 倍),并保留原始的 18,540 条推荐评论。我还继续使用 LSTM 模型对每个数据集进行训练和测试。

作者图片

上采样和文本扩充在提高模型对少数类观察的预测性能方面都是有效的。

图片由作者提供。两种方法产生了相似的结果。

那么使用文本增强有什么好处呢?

通过文本增强,我们还向我们的训练数据添加了更多的变化,这增强了我们的模型的健壮性及其对看不见的文本进行概括的能力。

继续阅读下一节,了解我们如何评估 NLP 模型的鲁棒性。

点击这里看代码。

对抗性文本攻击

如果我告诉你交换输入的单词或随机删除几个字母会严重改变我们的 NLP 模型的性能,会怎么样?

这些类型的编辑,在现实世界中可能是偶然的,也可能是恶意的,被称为扰动,并已被证明会显著降低即使是最好的最先进模型(如 BERT)的准确性。链接到文章。

我们通过对与原始模型语义相似但有轻微释义或同义词替换的输入进行测试,来对训练好的模型进行公式化攻击。

图片由作者提供。一次成功攻击的例子。对抗性的干扰愚弄了模型,使其预测出错误的标签。

使用 TextAttack 库,我将对每个经过训练的 LSTM 模型进行对抗性攻击(用上采样数据训练的模型和用扩充数据训练的模型)。攻击将持续到 1000 次攻击成功愚弄每个模型。

作者 GIF。正在进行的攻击。

以下是攻击结果。
在具有扩充数据的数据集上训练的 LSTM 表现全面。相比之下,它对扰动有更好的准确性,失败的攻击数量几乎是它的两倍。

作者图片

这些结果很有希望,并且与现有的研究相一致,这些研究已经展示了文本增强技术在改善小型和不平衡数据集的模型性能方面的有效性。

点击这里看代码。

基于方面的情感分析(ASBA)

假设你在一家制造这些女装的公司担任数据科学家。你的老板让你深入调查负面评论,并分析消费者对服装颜色的情绪

你会如何使用非结构化文本来做这件事呢?

我们很多人已经知道情感分类。但是看看下面的评论:

作者图片

通过简单地将总体评估分为正面负面,我们错过了一些更小的信息。有了 ASBA,我们可以更细致地分析并提取与颜色相关的积极因素。

我喜欢把 ASBA 想象成一个 4 步的过程:

  1. 从数据集中识别与我们的方面相关的观察结果。
    颜色是我们这里的方面。我们可以让事情变得简单,使用正则表达式在评论中使用提到单词“颜色”或“颜色”的评论子集。然而,情况不会总是这样。有时对于更抽象的方面,如体验服务位置,您可能需要利用主题建模来预测哪个方面与文本最相关。
  2. 其次,我们需要将文本分割成更小的片段。在我的代码中,我利用了第三方 API 来执行这一步。但也可以像应用一些语言学常识一样直截了当。例如,我们可以根据标点符号按句子分割文本,也可以在有连词(“但是”、“然而”、“虽然”等)时分割短语。

作者图片

3.现在,让我们确定与该相位相关的情绪或极性。这里最简单的方法是应用一个预先训练好的情感分类器模型。对于这个例子,我使用 TextBlob 库来确定极性,极性的范围在正负之间,范围为[-1,+1]。

图片由作者提供。强正极性。

4.最后,我们可以提取与我们的方面相关的描述符。
到目前为止,我们发现这位顾客对这件衣服的颜色评价很好,但了解一下为什么选择也很有帮助。在这种情况下,是因为“”我们可以使用 spaCy 的 token 分类功能来自动分析句子的语言结构,并提取哪些形容词/副词与我们的名词相关联。

图片由作者提供。在这个短语中,spaCy's 确定了“beautiful”在 dependency 中是形容词性补语,在词性中是形容词。这显然是我们想要提取的描述符。

让我们看看这些步骤在更多意见上的作用:

作者图片

在分析了 270 条不被推荐的着装评论中我们的观点——颜色——后,我们发现了以下结果。

作者图片

我们可以报告说,顾客通常对服装颜色反应积极。
因此,颜色不是不推荐评论的主要因素。

通过分析描述符,我们发现客户喜欢色彩明亮、生动、有活力的产品。然而,当产品的颜色看起来比网上的图片暗,或者过于柔和或暗淡时,评论者往往会抱怨产品的颜色。

作者图片

我们可以建议该公司注重使用明亮、充满活力的色彩和材料。

为了进一步研究,你可以分析其他方面,如模式,面料和大小。

点击此处查看代码。

结论

无论是改善你的数据集,建立一个更健壮的模型,还是分析特定的方面,这些绝对是需要注意的概念。我希望这篇文章能让你思考下一个项目的各种可能性。

如果您仍然想要更多的 NLP 内容,请查看我最近的一篇文章,其中介绍了几个独特但非常有用的 NLP 库。

</5-lesser-known-python-libraries-for-your-next-nlp-project-ff13fc652553>

感谢阅读!

开始使用 NLP 需要知道的一切

原文:https://towardsdatascience.com/nlp-survey-bde8a27e8ba

本杰明·苏特在 Unsplash 上拍摄的照片

不要到处搜索,先看看这个!

在这最后的一段时间里,我一直在集中精力复习和研究 NLP 领域最重要的东西,以便尽可能平静地面对各种面试。因此,我做的第一件事就是复习这个科目的所有基础知识,这样我就能为我将要被问到的所有问题做好准备。然而,我很难在一篇文章中找到所有的基本概念,并且花了很多时间搜索,所以我决定自己写一篇!😜(本文基于我在最后引用的一些文献)。

索引

  • 简介
  • 文本预处理
  • 直言词表征
  • 加权单词表示法
  • 表征学习:非上下文单词表征
  • 表征学习:上下文单词表征
  • 基于变压器的预训练语言模型
  • 下游学习
  • 对抗灾难性遗忘
  • 模型压缩
  • NLP 库

简介

基于文本的数据正以惊人的速度增长,而这些数据大多是非结构化的,因此很难从文本中获取有用的信息。每天发布的大量低质量数据的一个例子是推文、社交媒体帖子或在线论坛。人们用不总是正确的语言写评论,经常使用方言或表情符号来表达他们的情绪。不同的自然语言处理方法的主要目标是实现类似人类的文本理解。NLP 帮助我们检查大量的非结构化文本并提取其主要特征。

一般来说,在 NLP 任务中,我们必须遵循一些标准步骤。主要问题是机器学习算法不知道如何计算单词,所以我们必须找到文本的适当数字表示。为了生成这种表示,我们必须清除数据中的任何噪声,然后执行特征提取,即将原始数据转换为机器可以理解的数字数据。

文本表示管道(图片由作者提供)

让我们来看看文本预处理最常见的步骤是什么。请注意,下面我将描述一系列预处理步骤,但是您不必执行所有这些步骤来解决您的任务。相反,经常使用变形金刚,你会试图保持文本不变,以保持句子的上下文。因此,作为一名 NLP 从业者,您需要弄清楚自己需要什么。

文本预处理

  • 标记化:标记化是将文本变成原子单位的过程。例如,原子单位可以是单词、子单词,甚至是单个字符
  • 去除噪音、网址、标签和用户提及。通常,当我们看到一个数据集时,它非常脏,例如,因为它是从互联网上刮下来的。在许多情况下,文本中的符号或无用的单词,如 HTML 标签,不会添加信息,只会产生噪音。
  • 标签分割:在文本中,我们经常会发现前面有标签的单词(#它在社交媒体中被大量使用)。这些单词对于理解文章的主题可能非常重要,所以我们应该能够识别它们并删除符号#。
  • 更换表情符号和表情符号:表情符号和表情符号可以错误显示,希望我不是唯一一个出现过编码问题的人。有时候删除表情符号是合适的,但是在情感分析任务中,它们会非常有用。所以这完全取决于你的判断力。
  • 替换拉长字符:heeeeeeloooo→hello
  • 纠正拼写错误:一篇关于这个的大文章
  • 扩张收缩:我会→我会。(下面是怎么做)
  • 删除标点符号
  • 删除数字
  • 小写全字
  • 去除停用词:这些词会出现很多次,因此不会给测试增加太多信息,例如像“ the,a,ok”这样的词。
  • 词干化:就是把后缀、词缀替换掉,得到词根、词基或词干的技术。例:吃→吃。
  • 词干化:词干化的目的和词干化一样,都是把单词删减到它们的词根或词根。然而,在词汇化中,单词的词尾变化不仅仅是被切断,而是利用词汇知识将单词转换成它们的基本形式。Ex: 狼→狼。
  • 【词性】标注 : 是根据词的定义和上下文,将文本(语料库)中的词标记为对应于特定词性的过程。
  • 处理否定:否定可以完全改变句子的意思。因此,显式地找到它们可以极大地帮助您的算法。

特征抽出

分类单词表示:

  • One hot encoding :这是第一种方法,也是最容易应用于表示向量空间中的文本。每个单词都与一个长度等于字典基数的向量相关联。每个向量中的所有条目都是 0,除了我们发现 1 的一个位置。所以每个向量都是不同的,向量之间的距离都是相同的,所以没有比其他向量更相似的词。

一个热编码(作者图片)

  • ****(BoW):这只是一个一键编码的扩展。它将句子中单词的一键表示相加。

弓(图片作者提供)

加权单词表示法

然而,先前的表示没有考虑文本中单词的频率,而这对于理解单词的重要性来说是一个重要的数据元素。

词频(TF): 它不是只用 0 和 1,而是计算一个词的频率。一个单词的 TF 是通过计算一个单词在文本中出现的次数除以单词总数来计算的(这样就不会以牺牲较长的文档为代价来惩罚较短的文档)。

词频-逆文档频率(TF-IDF) :仔细想想,像“”这样的词在一篇文档中出现了很多次,但信息量并不大。因此,如果一个单词在一个文档中出现多次,并且出现在数据集中的几个不同的文档中,那么它的权重就一定很高

TF-IDF(来源:https://kinder-Chen . medium . com/introduction-to-natural-language-processing-TF-IDF-1507 e 907 c19)

其中 d 为一个文档, N 为文档总数, df 为期限为 t 的文档数

表征学习:非语境词汇表征

刚刚看到的分类表示非常直观且易于使用,但存在几个问题。首先,它们不能捕捉单词的句法和语义含义,并且遭受所谓的维数灾难。表示单词的向量的长度等于一种语言的单词词汇量的大小。如果我们同时处理多种语言的文本,维度将会大幅增加!

出于这个原因,我们现在看到的模型使用固定和有限维度的向量来学习单词表示。这些向量或单词嵌入的最显著的好处是,它通过保持上下文的单词相似性和低维向量来提供更有效和更有表现力的表示。因此,这些向量往往具有词的邻居的属性,并且它们捕捉词之间的相似性。这种表征称为连续词表征**

word 2 vec:已经推出的伟大创新是单词嵌入是一个神经网络的权重!该模型使用两个隐藏层,这两个隐藏层在浅层神经网络中用于创建每个单词的向量。由连续词袋(CBOW)和 word2vec 的 Skip-gram 模型捕获的词向量被认为具有词的语义和句法信息**

  • 连续单词包(CBOW)
  • Skipgram

全局向量(GloVe) :用于单词表示或 GloVe 算法的全局向量与 Word2Vec 颇为相似。但是,方法有点不同。GloVe 仅在一对一的基础上考虑上下文信息。这意味着 GloVe 只创建了一个逐字的相对矩阵,该矩阵包括在单词 b 周围显示单词 k 的概率。
该技术的主要目的是找到两个向量的表示,以便生成它们的点积等于共现的对数似然。它们对于将上下文中的单词相互关联有很好的效果。

fast text:fast text 将 n 元文法中的一个单词而不是完整的单词分解,输入到神经网络中,可以获取字符之间的关系,提取单词的语义。**

表征学习:上下文单词表征

我们为什么要在乎语境?在我看来,简单地解释一下,举个例子更容易。一个词可以根据它所处的上下文有不同的意思。在句子中:" 我的手机在桌子的 侧。离开了这个词就有了两种不同的含义,因此也就有了两种不同的表述。根据上下文创建单词的嵌入可能会对您的 ML 模型有很大的帮助。但是,要小心可能会改变上下文的预处理步骤。**

通用上下文单词表示(Context 2 vec):他的方法使用 LSTM 型神经网络从大型语料库中高效地学习通用上下文嵌入函数。这个模型的主要目标是学习一个通用的、独立于任务的嵌入函数,用于围绕
单词的不同长度的
句子上下文。
**

上下文化单词表示向量(CoVe): 使用深度 LSTM 编码器,该编码器来自为机器翻译训练的注意力序列到序列模型,用于上下文化单词向量。因此,隐藏字嵌入是整个输入序列的函数。然后,通过将这些单词嵌入与手套嵌入连接起来,然后将它们作为特定于任务的模型的特征,可以在下游任务中使用这些单词嵌入。

从语言模型嵌入(ELMo) : 从双向语言模型中学习最终的单词向量。ELMo 使用从双向语言模型中学习的线性连接表示,而不仅仅是像其他上下文单词表示那样的最终层表示。在不同的句子中,ELMo 为同一个单词提供了不同的单词表示。

对于一个简单而好的教程:


【ULMFiT】:通用语言模型微调(ulm fit)它主要依赖于迁移学习的概念,特别是通过允许在一个语料库(一组文档)上进行语言模型训练,以及然后在不同的语料库上改进模型的能力,但是建立在先前步骤所学的基础上。这是一种可以应用于 NLP 任务的架构和迁移学习方法。它包括一个三层的 AWD-LSTM 体系结构。训练包括三个步骤:1)在基于维基百科的文本上的通用语言模型预训练,2)在目标任务上微调语言模型,以及 3)在目标任务上微调分类器。**

基于转换器的预训练语言模型

在学习基于变压器的模型之前,有两个资源是您绝对必须阅读的:

生成式预训练-****GPT(open ai Transformer):是第一个基于 Transformer 的预训练 LM,可以根据上下文有效地操纵单词的语义。它基于变换器的解码器部分来模拟语言,因为它是自回归模型,其中模型根据其先前的上下文来预测下一个单词。通过在大规模自由文本语料库上学习,GPT 将无监督 LM 扩展到更大的规模。GPT 的一个缺点是它是单向的,即该模型仅被训练来预测未来的从左到右的上下文。**

GPT(来源:https://S3-us-west-2 . Amazon AWS . com/open ai-assets/research-covers/language-unsupervised/language _ understanding _ paper . pdf)

《GPT 2》(open ai Transformer):open ai 团队在 2019 年发布了 GPT 的放大版本。与之前相比,它在层标准化和残差关系的位置方面有一些细微的改进。总的来说,有四种不同的 GPT2 变体,最小的与 GPT 相同,中等的与 BERTLARGE 大小相似,xlarge 以 1.5B 参数发布,作为实际的 GPT2 标准。**

GPT-3(open ai Transformer):它是 GPT-n 系列中的第三代语言预测模型(很神奇试试吧!).****

BERT:该模型提出了屏蔽语言建模(MLM)目标,其中输入序列的一些标记被随机屏蔽,目标是以被破坏的序列作为输入来预测这些被屏蔽的位置。在预训练期间,BERT 应用了一个变换器编码器来处理双向上下文。此外,伯特使用了下一句话预测(NSP)目标。给定两个输入句子,NSP 预测第二个句子是否是第一个句子的下一个句子。****

伯特(来源:https://arxiv.org/pdf/1810.04805.pdf

如果您想了解如何在实际任务中使用 BERT,请查看我的文章:

蒸馏 BERT : 一个蒸馏(压缩)版本的 BERT,将 BERT 的大小减少了 40%,同时保留了 97%的语言理解能力,速度快了 60%。我在我的文章中使用了 distilbert【1】【2】

RoBERTa:对发布的 BERT 模型做了一些改动,并实现了实质性的改进。这些变化包括:(1)用更大的批次和更多的数据将模型训练得更长(2)去除 NSP 目标(3)在更长的序列上训练(4)在预训练期间动态改变掩蔽位置。****

阿尔伯特 :提出了两种参数缩减技术,降低了内存消耗,提高了 BERT 的训练速度:

  • 将嵌入矩阵分成两个更小的矩阵。
  • 使用在组之间拆分的重复层。

XLNet:XLNet,一种广义自回归预训练方法,它(1)允许通过最大化因子分解顺序的所有排列的期望概率来学习双向上下文,以及(2)克服了 BERT 由于其自回归公式的限制。****

ELECTRA:相比于 BERT,ELECTRA 是一种更有效的预训练方法。ELECTRA 没有用[MASK]破坏输入的某些位置,而是用从一个小型发电机网络中采样的看似合理的替代物来替换输入的某些表征。ELECTRA 训练一个鉴别器来预测被破坏的输入中的每个令牌是否被生成器替换。然后,预训练的鉴别器可用于下游任务。****

海量 : BERT 不能轻易用于自然语言生成。MASS 使用屏蔽序列来预训练序列到序列模型。更具体地说,MASS 采用了一种编码器-解码器框架,并扩展了 MLM 目标。编码器将连续标记被屏蔽的序列作为输入,解码器自回归预测这些被屏蔽的连续标记。

T5 : 是一个非常大的新神经网络模型,它在未标记文本和来自流行的自然语言处理任务的标记数据的混合上进行训练,然后针对作者旨在解决的每个任务单独进行微调【3】

BART:BART 是一个去噪自动编码器,用于预处理序列到序列模型。通过(1)用任意噪声函数破坏文本,以及(2)学习模型以重建原始文本来训练 BART。【4】****

下游学习

一旦文本的嵌入(向量)被贴附,它们可以被用来解决各种称为下游任务的 NLP 任务。与非语境嵌入相比,语境嵌入表现出令人印象深刻的性能。但现在的问题是“我们如何才能将上下文嵌入用于下游任务?”。

基于特征的

通过这种方法,您可以冻结模型,因此当您需要解决任务时,模型不会被拖回您的自定义数据集。它将仅使用预训练模型来生成特征(嵌入),例如,您将使用这些特征作为分类器的输入。要了解如何做到这一点,请看我的文章。

微调

与前面的方法不同,预训练模型将在下游数据集上再训练几次,以适应特定情况。

适配器

适配器是预训练模型层之间的小模块,用于
获得能够以多任务方式训练的模型。当适配器被训练时,预训练模型的参数被冻结。

对抗灾难性遗忘

每当我们去训练预训练模型以适应特定的下游任务时,我们这样做是为了提高该模型在我们特定情况下的性能。但是改变预先训练的参数会导致模型完全忘记它已经学习过的东西。例如,如果我使用一个很好地理解意大利语的语言模型,并且我想将其微调到西西里方言,则该模型可能会完全忘记意大利语。关于灾难性遗忘的研究仍然很多,但让我们看看能够减轻这种影响的方法:

  • ****冻结层:有可能冻结所有层,或者我们可以冻结除了最后 k 层以外的所有层。或者另一种方法是一次只解冻培养一层(链镇法)。
  • ****自适应学习率:在 NLP 中,就像在计算机视觉中一样,底层被认为能够捕捉最重要的特征。因此前几层可以使用较低的学习速率。
  • ****正则化:正则化(权重的惩罚)限制了模型的学习能力。

模型压缩

迄今为止,深度学习模型已经变得庞大,包含数百万个参数。除了需要巨大的计算资源,这些模型对环境也是有害的。有人估计,训练一个模型可以排放相当于美国 5 辆汽车平均寿命的 CO2。幸运的是,人们正在探索缩小这些网络规模的方法,让我们来看看其中的一些:

  • 修剪 : 我在写论文的时候已经解决了这个问题,你可以看看我写的关于 Julia 中修剪实现的文章。修剪试图从网络中删除不太重要的权重,从而减小网络的规模,同时保持性能不变。
  • ****知识提炼:这是将知识从大模型转移到小模型的过程。模型及其提炼版本的一个例子是 BertDistilBert
  • ****权重量化:是降低权重、偏差和激活的精度的过程,这样它们消耗更少的内存

你应该知道的图书馆

  • CoreNLP
  • NLTK
  • 根西姆
  • 宽大的
  • PyTorch
  • 张量流

最后的想法

我希望有了这个简短的指南,你不会像我一样浪费太多时间去搜索 NLP 中需要知道的基础知识,而是可以真正专注于学习这个奇妙的主题!

结束了

马赛洛·波利蒂

LinkedinTwitterCV

文献学

带 ONNX 的 NLP 变压器管道

原文:https://towardsdatascience.com/nlp-transformers-pipelines-with-onnx-9b890d015723

如何用 ONNX 构建真实世界的 NLP 应用,而不仅仅是对张量进行基准测试。

照片由 T KUnsplash 上拍摄

ONNX 是用于神经网络的机器学习格式。它是可移植的、开源的,在不牺牲准确性的情况下提高推理速度,真的很棒。

我找到了很多关于 ONNX 基准的文章,但是没有一篇文章提供了一种将它用于实际 NLP 任务的便捷方法。我也在抱抱脸的 discord 服务器上回答了很多关于 ONNX 的问题,以及如何最好的使用它进行 NLP。

这就是为什么我决定写这篇博文:我想帮助你用 ONNX 和令人敬畏的变形金刚管道得到最好的结果。

本教程将向您展示如何将 Hugging Face 的 NLP 变形金刚模型导出到 ONNX,以及如何将导出的模型与适当的变形金刚管道一起使用。我使用一个Named Entity Recognition (NER)模型作为例子,但是它并不局限于 NER。(在这篇伟大的文章中有更多关于 NER 的内容)

所有代码片段都可以在相关的 GitHub 库的专用笔记本中找到。所以不要担心复制它,只需在阅读这篇博文的同时克隆存储库并运行笔记本。

📦作业环境

首先,您需要安装所有必需的依赖项。建议使用隔离环境,以避免冲突。

该项目需要 Python 3.8 或更高版本。您可以使用任何您想要的包管理器。教程我推荐用 康达 。所有需要的依赖关系都列在requirements.txt文件中。要安装它们,请运行以下命令:

$ conda create -y -n hf-onnx python=3.8
$ conda activate hf-onnx $ git clone [https://github.com/ChainYo/transformers-pipeline-onnx.git](https://github.com/ChainYo/transformers-pipeline-onnx.git)
$ cd transformers-pipeline-onnx $ pip install -r requirements.txt

🍿将模型导出到 ONNX

对于这个例子,我们可以使用任何来自拥抱脸库的TokenClassification模型,因为我们试图解决的任务是NER

我选择了[dslim/bert-base-NER](https://huggingface.co/dslim/bert-base-NER)模型,因为它是一个base模型,这意味着在 CPU 上的计算时间适中。另外,BERT建筑对于NER来说是个不错的选择。

Huggging Faces 的Transformers库提供了一种将模型导出为 ONNX 格式的便捷方式。更多细节可以参考官方文档

我们使用上面提到的bert-base-NER模型和token-classification作为特征。token-classification是我们正在努力解决的任务。您可以通过执行以下代码来查看可用功能的列表:

检查特定模型类型的所有支持功能

通过调用转换脚本,您必须从本地目录或直接从拥抱脸的中枢指定模型名称。您还需要指定如上所示的特性。输出文件将保存在output目录中。

我们给onnx/作为输出目录。这是 ONNX 模型将被保存的地方。

我们将opset参数作为默认参数,它是在 ONNX 配置中为该模型定义的。

最后,我们还将atol参数设为默认值 1e-05。这是原始 PyTorch 模型和 ONNX 模型之间的数值精度容差。

下面是将模型导出为 ONNX 格式的命令:

$ python -m transformers.onnx \
    --model=dslim/bert-base-NER \
    --feature=token-classification \
    onnx/

💫将 ONNX 模型用于变压器管道

现在我们已经将模型导出为 ONNX 格式,我们可以将它用于 Transformers 管道。

过程很简单:

  • 使用 ONNX 模型创建一个会话,允许您将模型加载到管道中并进行推理。
  • 覆盖管道的_forwardpreprocess方法以使用 ONNX 模型。
  • 运行管道。

让我们首先导入所需的包:

所有需要的进口

⚙️用 ONNX 模型创建一个会话

使用 onnxruntime 创建会话

这里我们将只使用CPUExecutionProvider,它是 ONNX 模型的默认执行提供者。您可以为会话提供一个或多个执行提供程序。例如,您可以使用CUDAExecutionProvider在 GPU 上运行模型。默认情况下,会话将从列表中的第一个开始,使用计算机上可用的那个。

Onnxruntime提供了查看所有可用执行提供者的功能:

onnxruntime 中所有可能的执行提供程序

所有执行提供程序的列表

如您所见,对于每种用例及配置,都有许多可用的提供者。

⚒️用 ONNX 模型创建管道

现在我们已经有了一个可以使用 ONNX 模型的会话,我们可以对原始的TokenClassificationPipeline类进行重载来使用 ONNX 模型。

要完全理解正在发生的事情,你可以参考[TokenClassificationPipeline](https://github.com/huggingface/transformers/blob/v4.17.0/src/transformers/pipelines/token_classification.py#L86) python 类的源代码。

我们将只覆盖_forwardpreprocess方法,因为其他方法不依赖于模型格式。

调整管道等级以适应 onnx 型号需求

🏃运行管道

我们现在已经设置好了一切,所以我们可以运行管道。

通常,管道需要一个标记器、一个模型和一个任务。我们将使用ner任务。

使用新的超额收费类别创建完整的管道

让我们看看是否可以运行管道并检查输出:

运行 ONNX 管道

ONNX 流水线输出

在这里,管道与 ONNX 模型运行良好!我们现在有一个与 ONNX 全面合作的 NER 管道。🎉

查看可选的基准测试部分,了解它与原始 PyTorch 模型相比的性能,或者直接跳到结论部分,快速总结该过程。

🧪对整个管道进行基准测试(可选)

我们将通过使用 ONNX 模型和 PyTorch 模型测量管道的推理时间来进行基准测试。

我们首先需要加载 PyTorch 模型,并用它创建一个管道。

创建 PyTorch 管道

我们将使用相同的数据和 3 种不同的序列长度测试两条管道。

具有三种不同长度的基准序列

让我们比较 3 个不同序列长度的每个管道的推理时间。对于每个序列长度,我们将重复每次迭代 300 次,以获得更准确的基准,并将所有内容放在一个表中以比较结果

基准循环

基准测试结果

哇,看起来真棒!🎉

似乎对于每个序列长度,ONNX 模型都比原始 PyTorch 模型快得多。我们来计算一下 ONNX 模型和 PyTorch 模型的推理时间之比。

基准比率

我们在长序列上几乎实现了 3 倍的加速!🎉

我们甚至没有根据模型架构和模型运行的硬件进行任何优化,而 ONNX 可以做到这一点。

优化可能非常有用,但这是一个很深的话题,不能在这篇文章中讨论。但是很高兴知道您可以做到这一点,并且我们可以在未来的帖子中探索它。

另外,我们的测试是在 CPU 上进行的,但我见过的所有 GPU 上的基准测试甚至比 CPU 上的基准测试更令人印象深刻。查看这篇伟大的文章了解更多关于不同架构和推理配置的基准测试。

📍结论

总而言之,我们已经用 ONNX 构建了一个完全可用的 NER 管道。我们已经将 PyTorch 模型转换为 ONNX,并对原始管道类进行了超额收费,以符合 ONNX 模型的要求。最后,我们用原始 PyTorch 模型对 ONNX 模型进行了基准测试,并对结果进行了比较。

值得注意的是,不幸的是 PyTorch 模型是和 ONNX 模型一起加载的。这是因为 Transformers 管道需要加载 PyTorch 模型,特别是为了模型的配置。

我正在寻找一种方法来避免 PyTorch 模型的加载,因为它可能会在某些系统上产生 RAM 问题。

我们用来使它工作的过程对于拥抱脸变形金刚库中可用的每个模型和任务都是相同的。

您唯一需要关心的是模型架构是否有针对 ONNX 实现的配置。你可以在文档中找到的完整架构列表

如果您正在寻找的架构还没有实现,您仍然可以创建它并向 Transformers 库发出一个拉请求来添加它。这正是我几个月前为卡门贝干酪建筑所做的。你可以在变形金刚 GitHub 库上查看完整的 PR 。

我希望你觉得这篇文章有用而且有趣。如果您有任何问题或面临任何问题,请告诉我。我很乐意添加更多的例子和对其他 NLP 任务的支持,所以如果你有任何想法或要求,请告诉我!🤗

如有疑问或问题,请在 GitHub 或下面的评论中打开一个问题。

P.S .我还计划添加另一个基准测试部分,以测试 ONNX 模型是否实现了与原始模型相同的结果(剧透:是的!).

NLP2Chart —自然语言中的信息可视化—第 2 部分

原文:https://towardsdatascience.com/nlp2chart-information-visualization-in-natural-language-part-2-a325c5e636dc

一个扩展的应用程序,使用 CodeX 用自然语言的命令创建图表

作者的界面/图片

之前的文章中,我展示了一个方法的原型,它允许使用自然语言的指令创建信息图和图表。在这篇文章中,我想给出一个扩展和修改的版本,并展示一些例子和用户一起研究的结果。

这项工作于 2022 年 7 月 19 日至 22 日在“奥地利维也纳技术大学”举行的第 26 届国际信息可视化会议(IV)上发表,论文发表在会议出版物上。

信息可视化软件中的自然语言接口

自然语言界面已经进入了可视化数据分析的软件产品。它们旨在帮助人们使用各种分析方法分析和可视化数据。商业软件“Tableau”的市场领导者和微软的“Power BI”在其当前版本中集成了相应的组件。

有了 Tableau 的“问数据”,你可以用常用语言输入一个问题,直接在 Tableau 里立刻收到答案。答案以自动数据可视化的形式出现,无需您手动拖放字段、调用菜单或理解错综复杂的数据结构。

微软的对应物叫做“Power BI 问答”。然而,获得正确的说明仍然是用户的一个主要障碍。更强大的语言模型可能会有所帮助。

近年来,通过对大型文本数据集进行预处理,基于 Transformer 架构的强大的新语言模型已经出现,主导了所有当前的 NLP 基准。这项工作的目的是探索数据可视化的 NLIs 是否以及以何种方式从这些模型中受益。为此,我基于 OpenAI Codex 模型创建了一个可视化软件原型。

https://openai.com/blog/openai-codex/

第二个原型实现了基于 NL 的数据可视化的基线管道。它使用开源的“数据可视化自然语言”(NL4DV) 工具包来解释自然语言语句。该工具包将一个数据集和对应于该数据集的话语作为输入,返回一个 JSON 对象,该对象包含一个有序的 Vega-Lite 规范列表,可以作为输出呈现。

界面

为了测试在深度学习语言模型的帮助下生成图表的可能性,创建了一个 web 应用程序的原型,该原型允许使用文本输入创建数据可视化,然后使用上下文相关的调色板进行优化。文本命令用于定义可视化的基本结构,然后调色板可用于设置额外的规范,如线宽或颜色(见上图)。原型是一个混合主动性的可视化系统,允许通过图形用户界面(GUI)进行自然语言输入和直接操作交互。

对于数据处理,原型提供了上传 CSV 文件或通过选择框加载现有文件的可能性。在此之下,数据字段与其数据类型一起显示。作为导出选项,原型中提供了 PNG 图像和 HTML 导出。

除了可以观看简短的教程视频之外,屏幕的中央区域还提供了一个多行文本输入栏,在其中可以用自然语言输入指令。在这下面,显示生成的可视化。在一个原型变体中,这是一个 Matplotlib 图形,而在另一个原型变体中,这是一个 Vega-Lite 图表。

GUI 元素/作者提供的图片

除了文本命令之外,右栏还提供了一些 GUI 元素。可以指定标题、标注轴和设置限制。根据所创建的图表类型,会显示其他元素,如线宽、调色板或条形的颜色。

应用程序的架构

原型的两个版本都是用 Python 实现的 web 应用程序。在每种情况下都使用了框架“ Streamlit ”。数据可以 CSV 格式处理,并导出为 PNG 或 HTML 格式。这两种变体在 NLP 和可视化方面有所不同。下图显示了使用 CodeX 生成代码的版本的系统架构。

作者的系统架构/图像

不同部分的细节可以在文件中找到。

例子

为了说明和评估,我在基于证据的世界观上用来自 Gapminder.org的数据测试了这个应用程序。测试人员必须完成以下练习:

  1. 为数字特征创建直方图
  2. 使用条形图绘制每个洲的数字特征。
  3. 借助条形图显示每个国家的 GDP。前 15 个国家应该以降序显示。
  4. 创建一个饼图,显示世界各大洲的人口分布。
  5. 创建一个图表,显示所有国家平均预期寿命的时间进程。使用一个折线图。
  6. 借助散点图显示一个国家的人均 GDP 和预期寿命之间的关系。此外,使用反映人口数量的颜色代码。
  7. 使用气泡图显示一个国家的人均 GDP 与预期寿命和人口数量之间的关系。

作者的图表输出/图像示例

实验过程的细节可以在论文中找到。

测试结果

参与者给深度学习语言模型的原型的平均 SUS 分数为 71.1,给 NL4DV 的版本的平均分数为 68.2。得分≥ 68 被认为是可用性良好的指标。

评估表明,深度学习模型具有表达性文本输入的潜力,但并不总是产生完全可预测的结果,因此可能会使用户困惑。

将来的

我使用的 Codex 语言模型是一个用于生成通用源代码的模型,并没有针对我们的任务进行优化。目前,可以通过 API 微调 GPT3 的自然语言变体,因此模型可以更好地适应任务。这对于法典模式来说还不可能。一旦这成为可能,我期望我的方法会有很大的进步,因为在这里将有可能专门用源代码来创建信息图,从而更有针对性的图表。

该论文将很快在 IEEE Xplore 数字图书馆获得 https://IEEE Xplore . IEEE . org/xpl/con home/1000370/all-proceedings

代码@github:

https://github.com/astoeckl/NLP2Chart2

NN 或 XGBoost:指南

原文:https://towardsdatascience.com/nn-or-xgboost-the-guide-4574babc4053

最大像素

帮助您选择最适合您的型号的简短指南

第 1 部分-简介

受深度学习在计算机视觉任务中成功的启发,许多人争论 NN 是否能为表格数据“赢得比赛”。一些人赞成 NN 几乎无限的潜力,而另一些人则提到 XGBoost 在 Kaggle 比赛中的成就。有些人甚至进行了两种模型的面对面比较(例如 Firefly.aiMLJarRavid 等人)。

不要选择最好的型号,而是选择最适合你的型号

幸运的是,“哪种 ML 模型最适合表格数据”这个问题与数据科学家的工作完全无关。这是因为数据科学家专注于自己的特定数据集/任务,尤其是在表格数据中,发现其他数据集/任务不太相关。此外,模型的准确性在选择正确的模型时仅起部分作用。因此,正确的问题应该是“哪种模式最适合我的需求”。

选择正确的模型并不总是微不足道的,因为有许多方面需要考虑。为了使这个过程更容易,下面的指南提供了 3 个考虑事项表和相应的模型建议。第三部分解释了这些建议背后的逻辑。

下面的表格和分析是为多层感知器架构创建的。然而,近年来,针对表格数据的 NN 模型的许多其他架构被引入,例如 LSTMTabNet节点。这些体系结构在提供卓越性能的同时,与基本的 MLP 体系结构有许多共同的特征。因此,下面的分析在某种程度上仍然适用于这些架构。

第 2 部分-指南

下表旨在帮助您识别哪种型号有可能满足您的需求。

树木 → XGBoost、随机森林、LightGBM、Catboost 等。 NN→多层感知器以及某种程度上的其他架构

绩效问题

没有办法预先知道哪种型号会提供更好的性能。最好尝试所有模型,包括超参数调整,并从训练的模型构建一个集合。换句话说,最好使用有足够学分的 AutoML 系统。不幸的是,使用这样的系统通常是不可能的,因此必须手动选择模型类型。在这种情况下,数据和任务的特征可能表明哪个模型将获得最佳性能。

性能表

训练和推理的各个方面

在高尔夫比赛中,表现就是一切。但在现实生活中,诸如易用性和培训时间/成本等实际考虑因素往往同样重要。

训练和推理表

其他考虑

很多时候,工作并不是以一个训练好的模型结束的,还需要其他的后续特性。这些特征可以指示选择次标准模型来实现更有用的模型。

其他注意事项表

第三部分——推理

树与神经网络—高级差异

让我们来看看基于神经网络的解决方案和基于树的解决方案之间的一些关键区别。

谈到易用性,毫无疑问,创建基于树的解决方案要容易得多。这几乎是一个即插即用的解决方案,只有 3 行代码,学习曲线可以忽略不计。相比之下,要想用神经网络得到好的结果,你需要知道你在做什么——包括理论和 TensorFlow/PyTorch 软件包。不仅如此,训练基于树的模型所需的时间也短得多。最重要的是,基于树的模型对它们拥有的少数超参数(HP)相对不敏感。另一方面,神经网络模型具有许多超参数,并且对它们的值也非常敏感。因此,神经网络模型需要彻底的 HP 调整才能工作。

所有这些点加起来使得基于树的模型对于一个模型来说具有显著更短的时间。

基于树的模型易于使用的原因之一是它们没有提供太多的操作空间。这是一把双刃剑。虽然它使新手能够获得高端结果,但它阻止了专家进一步前进并构建定制的解决方案。从这个意义上说,神经网络模型正好相反。有了足够的专业知识,就有可能针对问题和业务案例定制解决方案,从而实现一个定制的最先进的解决方案。

然而,这不是免费的。不仅需要专业知识,还需要增加 R&D、专用硬件的开发时间,以及更长的惠普调优模型培训。在这个行业中,所有这些都可以转化为很多钱。毫无疑问,创建一个神经网络解决方案要昂贵得多。

将“可定制的”、“最先进的”和“昂贵的”放在一起,会产生对技术(即深度学习)的更多公关和兴趣,这反过来又会转化为筹款、产品营销和学术研究。

树木 vs 神经网络——技术分析

每种模型类型,无论是基于树还是基于神经网络,都有自己的特点,这些特点可以转化为优势和劣势。这反过来又使每个模型更适合特定的数据集。

神经网络

让我们检查多层感知器(MLP)架构。这种架构利用神经元和层的概念,方便地将 NN 模型表示为激活函数的 函数组合 ,其中每个中间函数都是几个激活函数的加权和。因此,激活函数的大部分特征将被传递给组合函数,即神经网络模型。对于常见的激活功能,如线性、ReLu、Tanh 或逻辑,这意味着:

  • 参数函数空间:NN 架构定义了一个函数空间,NN 模型 F(x)位于其中。F(x)的参数(权重和偏差)经过了优化,因此 F(x)能够最好地表示数据样本的基本函数。由于 F(x)受限于函数空间,因此更容易获得对属于同一函数空间且具有相似特征的基础函数的良好逼近。这意味着与其他方法相比,需要更少的数据来实现更高的精度。另一个优势是,如果需要的话,可以很容易地获得模型的衍生品。
  • lip schitz continuity:NN 模型的梯度是有界的。该特性将使得 NN 难以逼近具有强梯度的基础函数,例如不连续的(例如,分类值)、无界函数(例如,1/x,log(x))或无界梯度(例如,sqrt(x))。在这种情况下,需要更多的数据&模型复杂性来达到期望的精度。
  • 非周期性:常见的激活函数都是非周期性的。这一特性可能使神经网络逼近周期函数具有挑战性。
  • 外推能力:当 X 趋于无穷大时,F(X)要么变成常数(对于 Tanh,Logistic),要么变成线性(对于 ReLu)。这允许一些推断的能力。此外,在回归任务中,函数的值可能低于/高于训练数据中目标的最小值/最大值。
  • 灵活:可以通过改变建筑来使功能空间适应任务。例如,多标签模型。
  • 优化过程:优化过程允许通过合并自定义度量、正则化和约束来收敛到更优的解决方案。此外,它还支持在线学习或转移学习等高级功能。
  • 网络架构:几个隐藏层的使用,使得网络在训练时将数据嵌入到一个相对较小的 N 维空间中。这种嵌入通常对其他应用程序/模型很有用。它还提供了许多选项,如迁移学习(其中嵌入被重复使用)、使用图像/文本(通过嵌入的连接),甚至用于生成目的(通过查询嵌入的潜在空间)。

Tree 底层函数用分段常数函数的加权和来近似,其中每一段都是一个正投影(超矩形)。因此,虽然每个模型(RandomForest、XGBoost 等。)收敛于不同的函数,它们都包含在同一个函数空间内。

  • 非参数化:模型可以逼近任何底层函数。
  • 分段常数:模型的准确性由分区的数量与底层函数的梯度决定。由于分区是由数据点的分布决定的,因此在高梯度或低样本密度的区域,模型的准确性会较低。当然,这主要与数字特征相关。
  • 决策制定:基本的构建模块——决策树——很容易解释。因此,基于树的模型被认为更容易解释。此外,Shapley 值可以很容易地从这种模型中计算出来,而不需要昂贵的计算或替代模型。
  • 插值:由于二元决策结构,基于树的模型不能在特征值的极值之外提供可靠的预测。同样,这主要与数字特征相关。这使得基于树的模型不适合诸如“来自纪元的时间”的特征,除非采取适当的对策。类似地,基于树的模型的预测限于训练集中的目标范围,因此应该避免诸如“客户数量”之类的目标标签。
  • 位置:基于树的模型是非常局部的,因为每片叶子负责一个特定的区域。结果,输入空间的一个区域中的推广质量相对独立于其他子空间中的质量。

关键要点

表格数据集没有 SOTA ML 模型

表格数据包含所有可以放入表格的数据集。但仅此而已。与其他类型的数据集(如图像或文本)不同,没有其他共同特征。因此,不同的模型更适合不同的任务。

将任务的需求与模型的优缺点进行比较

外面有很多现成的模型。正确的模型不是为了获得最佳性能而优化的,而是为了在整体业务价值和解决方案的成本和工作量之间提供最佳平衡而优化的。

这种平衡对于一个人所面临的特定挑战来说是独特的。通过分析任务的需求与模型的优缺点,人们能够选择正确的道路。

选择最适合你的型号

我制作了这个指南来鼓励你问正确的问题,并帮助你做出正确的选择。剩下的就看你的了。

无代码人工智能平台将人工智能带给每一个人——以下是方法

原文:https://towardsdatascience.com/no-code-ai-platforms-bring-ai-to-everyone-here-is-how-8f75b2f6ce9d

人工智能笔记

无代码人工智能平台如何帮助大规模采用人工智能技术

Unsplash 上由 Aniket Bhattacharya 拍摄的照片

首先,我简单解释一下什么是无代码 AI 技术。无代码人工智能是一种无代码技术,它使非人工智能专家能够在不需要人工智能专家的情况下实现和测试他们的想法。无代码人工智能平台帮助人工智能专家以更少的努力更快地构建 ML 解决方案。它们还提供了人工智能专家和领域专家之间的合作机会。无代码 AI 平台有拖拽(如https://sway-ai.com/****)基于向导的界面(如Akkio)。在这篇文章中,我描述了一个无代码人工智能平台如何帮助大规模采用人工智能技术。

如今,专家们旨在用人工智能技术解决问题,以提高生产率和效率。例如,医生希望使用人工智能来改善为患者提供的医疗服务。或者,企业高管希望使用 AI 来改善为其用户提供的客户服务。你说吧!问题是,作为人工智能社区,“我们是否成功地妥善解决了这些挑战?”如果不能,我们如何确保人工智能技术可以在任何地方被任何人采用。答案在于无代码 AI 技术。我们在过去相对成功地应对了许多挑战,但我们需要无代码人工智能技术来确保大规模采用。

为什么一定要用无代码 AI?

问题 1——AI 专家和领域专家的差距很关键!

在过去的几年里,大量的人工智能公司崛起了。他们成功了吗?是的,相对而言。我说相对是因为 AI 专家和领域专家之间还有巨大的差距。他们很少能相互理解。人工智能专家谈论机器学习(ML)模型和优化算法的复杂术语,领域专家试图只解决他们的问题(沟通困难)。人工智能专家对复杂的技术着迷,当他们的问题得到解决(客观差异)时,领域专家变得满意。这些挑战使得许多人工智能计划失败,或者在日常挑战中没有被采用。我们必须找到更好的方法来填补这个空白。无代码人工智能平台为领域专家创造了测试他们想法的机会,并与人工智能专家更好地交流,希望填补空白。人工智能专家也可以使用无代码人工智能平台在短时间内为领域专家创造价值。答对了。

我为什么喜欢无代码人工智能技术?无代码人工智能平台为领域专家创造了测试他们的想法并与人工智能专家更好地交流的机会。💪****

**

问题 2——我们没有像我们一样多的人工智能专家!

假设 AI 专家和领域专家之间的差距被消除了。我们有足够的人工智能专家来应对所有挑战吗?不,但是我们有和我们一样多的领域专家。有大量旨在培养人工智能专家的研讨会/训练营/计划。但是我们仍然需要更多数量和资格的专家。知道我们可以解决(在某种程度上!)人工智能技术的许多问题,我们应该找到最佳的方法,使领域专家能够运行他们的实验,而不是把我们的重点放在培养大量的人工智能专家上。这是我们能够确保人工智能技术在每个用例中被采用的唯一方法。无代码人工智能平台使领域专家能够随时随地实现和测试他们的想法。

我为什么喜欢无代码人工智能技术?无代码人工智能平台使领域专家能够实现和测试他们的想法。这是我们能够确保人工智能技术在每个用例中被采用的唯一方法。😊

无代码人工智能平台如何解决这个问题?

—数据

我们可以根据无代码人工智能平台的目标数据类型(表格、文本和视觉)对其进行分类。利用计算机视觉(CV)技术瞄准视觉数据的最成功的公司之一是被微软收购的 Lobe.ai阅读更多。虽然无代码 CV 平台使用更广泛,但是像 MonkeyLearn 这样的无代码 NLP 平台正在崛起。说我仍然认为技术聚焦(即 NLP 或 CV)的解决方案无助于 AI 的大规模采用。最大的希望还是在通用解决方案上。

我们可以根据目标数据的类型对无代码 AI 平台进行分类:表格、文本和视觉。

—接口

我们还可以根据界面的类型对无代码 AI 平台进行分类:拖放式(如 Sway AI ) 或基于向导式(如 Akkio )。非技术用户习惯于拖放界面。这就是为什么微软等公司也带着名为 Azure ML Designer 的解决方案进入这个市场。不用说,拖放界面在过去已经被无数次地用于各种目的。例如, Appian 使用这个接口为企业构建应用和工作流。简而言之,拖放界面为人工智能的大规模应用创造了机会。

我们还可以根据界面的类型对无代码 AI 平台进行分类:拖放或基于向导的。

遗言

我对无代码 AI 平台,特别是那些具有拖放界面的平台给这个领域带来的机会感到非常兴奋。然而,我们必须意识到,无代码人工智能不会取代数据科学家,甚至不会消除我们编写代码的需求。我仍然相信一个完全成熟的人工智能产品需要编码,这不会在几天内发生。所以,享受在你的工具集中使用一个新工具,但是请不要误用它!😊

感谢阅读!

如果你喜欢这个帖子,想支持我…

https://pedram-ataee.medium.com/membership **

非营利组织中的无代码数据创新

原文:https://towardsdatascience.com/no-code-data-innovations-in-non-profit-organizations-391ae604da5a

机遇、障碍和前进的步伐

天津滨海图书馆。图片由作者提供。

知识工作者利用领域内容专业知识,通过解决问题和做出关键决策来创造价值。非营利组织的大多数员工都符合这个定义。正如 Castle 在最近 Forbe 的文章中所解释的,知识工作者依靠信息和技术工具来成功和高效地完成他们的工作。无代码运动正在帮助重塑知识工作行业,让知识工作能够开发低成本、安全、高质量的数据产品和信息系统,而无需编写一行代码。受创造力和创新的限制,知识工作者可以利用他们的内容专业知识和对业务流程的深入了解来创建定制的软件解决方案,以支持他们的信息需求和决策。通过最少的培训和对这些无代码平台的访问,知识工作者可以轻松地创建:

  • 内容管理系统
  • 客户/顾客管理系统
  • 项目管理系统
  • 数据仪表板和数据集成
  • 自动化管理报告
  • 电子商务网站
  • 时间跟踪系统
  • 库存系统
  • 捐助者数据库
  • 赠款管理系统

内容专业知识与技术解决方案的无缝集成是一项颠覆性创新,因为知识工人不再受传统且往往支离破碎的 IT 服务模式的束缚。例如,非营利组织面临相当多的行政报告要求。用于报告的传统技术工具和工作流通常效率非常低,尤其是在处理非集成数据流时。借助无代码工具,知识工作者可以创建集成的数据流、自动化流程和交互式数据仪表板。无代码工具不会取代知识工作者,但可以从他们的日常工作流程中消除重复和令人麻木的任务,让他们有更多时间将专业知识应用于计算机无法解决的问题。

无代码工具也为传统的 it 服务提供了退出策略。许多非营利组织与外部服务提供商签订合同,开发和维护面向公众的服务和资源网站。有了无代码工具,非营利组织现在可以独立开发和维护专业网站,而成本只是签约服务的一小部分。由于许多无代码网站是建立在一个简单的电子表格上的,更新一个无代码网站比向一个网站开发者提出请求更加有效和直接。

无代码是一种时尚吗?

在过去的几年里,无代码技术呈现出爆炸性的增长。除非你密切关注技术行业,否则你甚至可能不知道无代码运动。一个简单的互联网搜索“无代码”和密切相关的概念“低代码”将显示许多新闻,报告和软件选项的链接。

从谷歌趋势看无代码的增长。图片由作者提供。

几乎每个主要的技术公司现在都提供一些无代码软件服务。这些产品正在不断发展,变得越来越容易使用。

虽然这些公司正在推动变革,并坚定地确立这些工具的合法性,但它们并不是无代码运动的主导者。一个主要的无代码平台 Softr 的 Mariam Ispiryan 最近发表了一篇博客文章,提供了 41 种不同的无代码工具的分类,用于创建网站、网络应用、移动应用、工作流自动化和电子表格/数据库。鉴于最近对无代码平台的大量投资和估值,将无代码视为时尚是短视的:

为什么非代码工具没有被非营利组织广泛采用?

尽管非营利组织有提高业务流程效率和允许内容专家更有效地使用数据的潜在机会,但许多组织在使用无代码技术进行创新时面临相当大的障碍。让我们考虑一下我在与寻求创新或拥有可以从创新中受益的业务流程的组织合作时遇到的常见障碍。

为什么要修理没坏的东西?

这个问题在拥有 IT 部门的组织中最常见,尤其是当 IT 专家对 IT 解决方案拥有独占控制权,而对日常业务流程缺乏深入了解时。从 IT 的角度来看,现有的技术正在发挥作用——也就是说,系统处于在线状态,数据是“安全的”并且所需的工作正在完成。因此,为什么要修理没坏的东西呢?

历史上衡量成功的 IT 标准适用于更早的时代。然而,这些系统通常不能满足当今的需求。负责本组织实际业务运作和任务的工作人员面临越来越大的压力,要展示他们工作的影响。然而,非营利组织的数据系统和工作流程从来就不是为衡量影响和结果而设计的。因此,知识工作者将宝贵的时间花费在高度重复和耗时的数据任务上,而不是运用他们的专业知识从数据中获得可操作的见解。

沉没成本谬论

沉没成本谬误是一种认知偏差,导致人们将资源投入到没有效果的事情中。当组织认为以前的投资(即沉没成本)证明未来的支出是合理的时,他们就会受到这种偏见的影响。例如,一个在传统网络技术上进行了大量投资的非营利组织可能不愿意探索新技术,即使新的解决方案可能更便宜、更容易管理。Atlassian,像吉拉和特雷罗这样的无代码工具的幕后公司,有一篇关于理解和克服技术投资的沉没成本效应的优秀文章。

"好吧,但是安全性、安全性和可持续性呢?"

许多为组织制定技术决策和政策的 IT 专家可能并不了解无代码技术。因此,关于无代码技术最常见的问题涉及安全性、安全性和可持续性问题,特别是因为无代码工具是基于云的软件服务提供商。简单地在互联网上搜索“无代码安全问题”会产生几篇文章,提出几个基本问题,包括(但不限于):

  • 你怎么知道服务提供商会保护我们的数据和数字资产?
  • 你怎么知道这些系统是安全可靠的?
  • 我们如何控制组织内部的访问?
  • 我们如何避免软件锁定?
  • 如果提供商倒闭了会发生什么?

事实上,这些是基本问题,应该成为讨论的起点,而不是创新交易的破坏者。当系统地与无代码解决方案进行比较时,这样的对话揭示了现有遗留系统在安全性、安全性和可持续性方面的差距和问题。无代码解决方案导致的安全问题更有可能来自用户的错误配置,而不是工具或软件服务提供商,这突出了在无代码生态系统中建设组织能力以支持安全的重要性。

组织能力

许多组织无法创新他们的数据系统和工作流,因为他们缺乏变革的能力。在依赖遗留系统而没有接触新工具和技术的组织中,能力问题尤其明显。因此,许多知识工作者不知道新技术和工作流程如何帮助他们更聪明地工作,而不是更努力地工作。人们不知道他们不知道的事情。现有系统的低效率进一步增加了变革能力的复杂性,也就是说,知识工人几乎没有时间投资学习新工具和创建新工作流。因此,改变的想法是压倒性的,而不是令人兴奋的。组织能力无疑是非营利组织内部数据创新最重要的杠杆点之一。

向前迈进一步

使用传统技术工具和 it 服务模式的非营利组织无法再忽视快速变化的技术环境。谷歌产品营销经理 Jennifer Cadence 在一篇关于用无代码解决方案赋权非营利组织的博文中解释道:

企业无法承受被过度官僚化的流程所束缚的解决方案,因为业务线员工无法影响真正的进展。但他们还需要在单个团队中开发的解决方案是可管理的和安全的,没有团队创建自己的应用程序和服务时经常出现的所有“影子 IT”风险。

最明显的第一步是建立对使用数据和信息技术的新方式的认识,最终关注对组织使命最有益的东西。领导团队需要促进一种鼓励创新和交流新思想的组织文化。在传统 IT 服务模式中保持安全的 IT 专业人员将需要重新定义他们的角色,更加关注知识工作者的能力建设,以帮助确保他们的工作流和数据系统安全可靠。知识工作者最终需要支持和资源来学习无代码工具,以及如何帮助他们更聪明而不是更努力地工作。

我是密歇根大学的社会工作教授,对帮助学生做好准备和帮助非营利组织利用数据和信息技术更聪明地工作感兴趣,而不是更努力。如果你有兴趣学习更多实用的创新策略,欢迎关注我。

没有一个数据专业是孤岛

原文:https://towardsdatascience.com/no-data-professional-is-an-island-5805f325c02e

数据业务所需的团队合作

拉拉·阿兹利Unsplash 上的照片

团队合作是数据专业人员的关键。据我所知,没有任何数据专家能够在没有任何其他业务部门的输入或帮助的情况下在真空中工作。

据我所知,没有任何数据专家能够在没有任何其他业务职能部门的输入或帮助的情况下在真空中工作。

在本文中,我将专门讨论数据分析师的经验及其参与,但是我所涵盖的流程、技巧和最佳实践可以在整个领域推广!让我知道你对我下面提供的 9 条数据约定技巧的意见

数据分析师通常有效地扮演项目经理的角色,协调最终用户、工程和交付最终产品所需的各个部门之间的工作和沟通。管理这些关系对于成为一名高效的数据分析师至关重要。

数据分析师通常有效地扮演项目经理的角色。

对于那些对从事数据分析职业感兴趣的人,请继续阅读了解成为一名出色的数据分析师所必需的协作!对于那些已经在从事数据分析师的人来说,继续阅读获得一些提示或者也许确认你不是唯一一个依赖其他业务部门的人!

下面,我将带您经历数据约定的之旅以及一路上的一些最佳实践!以下是我们将要介绍的步骤:

  1. 利益相关方的现场请求
  2. 粗略布局确认前进路径
  3. 联系必要的数据专业人员
  4. 跟踪项目的进展
  5. 交付项目
  6. 入住后

1.利益相关方的现场请求

数据分析师必须能够处理大量传入的请求,其中许多利益相关者将他们的请求标记为尽快。你不仅要处理大量的请求,而且你还必须深入到请求者最初的请求中,而不是简单地成为一个订单接受者。

在数据分析中,客户是而不是永远正确。但是,你可以在你的股东之间达成共识。让我们来谈谈如何实现这一点。

在数据分析中,客户是而不是永远正确。

拍摄的照片 Unsplash 上的

向每个人阐明请求 作为一名数据分析师,回应请求的首要步骤之一是确保每个人都达成一致。人们会带着一个问题来找你,通常会带着一个经过深思熟虑的解决方案。这太棒了!他们已经在努力尝试解决这个问题。

在试图解决问题之前先了解问题
这些考虑不周的解决方案的问题在于它们是如何制定的:当人们请求解决方案时,他们通常会忘记对他们试图解决的问题进行描述。作为一个彻底的数据分析师,你不能让这种情况发生!当你从事数据项目的时候,你需要知道你在解决什么问题

最大限度地利用你所掌握的信息只会帮助你获得更好的结果。此外,通过确保您理解问题,您确认确实存在问题,并且您保证您的利益相关者已经定义了业务案例

提示#1 : 定义业务案例。

确保你完全理解请求,并且请求者完全理解他们真正想要的。

记住大图 记住你交付的任何仪表板、分析或数据解决方案都必须无限期地维护,这一点很重要。随着时间的推移,解决方案通常会被“搁置”起来,积满灰尘,因此数据分析师要记住这一点。

您交付的任何仪表板、分析或数据解决方案都必须无限期地维护。

不要让自己成为提供多余项目的同谋。首先,调查并确定任何现有的解决方案是否有助于回答提出的问题。现有的仪表板可以重新配置,以提供更多的数字和见解。减少、再利用、回收!

照片由理查德·贝尔Unsplash 上拍摄

技巧#2 : 减少、再利用、回收!

研究现有的解决方案,帮助解决你当前的问题,并考虑回收和改造以前的项目。

请注意,这是而不是鼓励您拒绝为您的组织提供新的解决方案。对你的组织来说,用批判的眼光看待请求并确保考虑未来是有益的。保持房子整洁!

在适当的时候根据初始请求进行扩展
一个与“减少、重用、回收”背道而驰的理想,但同样重要的是,数据分析师也要知道何时扩展请求。

提示#3 : 展开提问

不要成为接单员!相反,在适当的时候,考虑到极简主义/可解释性,多做一点,提供比最初的要求更多的内容。

在投入时间时,请确保在着眼于改进的同时做得正确,并展望未来,在您的解决方案投入使用时可能会出现其他问题!

对你的可交付成果进行未来检验关于未来,试着减少你必须完成的返工量!

哈迪贾·赛迪Unsplash 上拍摄的照片

无论是仪表板、分析还是其他数据解决方案,我强烈建议您在时间允许的情况下实施动态解决方案。动态性,因为数据流程的设置应该能够随着时间、时期、地理位置、业务单位等的变化而变化。该解决方案仍将运行,并需要尽可能少的维护。

小贴士#4 : 充满活力的解决方案是令人愉快的解决方案

如果时间允许,实施能够经受时间和变化考验的动态解决方案。

2.粗略布局以确定前进的道路

当开始一个新的数据项目时,创建一个粗略的项目大纲,让利益相关者签字同意,将使前进的道路不那么混乱。这是减少所需返工量的一大步!

照片由哈尔·盖特伍德Unsplash 上拍摄

这些草图看起来像什么?只要他们很好地解释了最终产品的样子和使用方法,他们就可以随心所欲。最后,你最了解你的利益相关者!下面我给出一些思路。

  • 仪表板:快速绘制各种统计数据、KPI、过滤器、图表等的草图。有了大致的布局。
  • 分析:对所需数据、可能的用户输入以及各种输出(如图表、表格)及其格式(如 PNG、HTML、CSV)做一个粗略的概述。

分析麻痹 注意,这一步应该不会消耗很多时间。在推进一个项目时,从你的利益相关者那里得到这种确认来建立一个统一的战线当然是很好的。但是,如果它涉及可能难以联系到的多个人员,或者此确认过程非常缓慢,请在等待反馈的同时,认真考虑开始合约工作。

技巧 5:避免分析麻痹!

不要陷入无休止地讨论概念的陷阱,这不利于开始实际的产品。

最后,在你完成项目时得到反馈比解决方案完成时得到反馈更好。这让我想到了另一个要点:数据分析师必须有行动的倾向。不要工作得那么辛苦那么久,到头来只会产生过时的见解

3.联系必要的数据专业人员

作为一名分析师,如果你不是一名分析工程师,你很可能不得不接触那些给你带来数据的人。这些优秀的人通常是您组织中的数据工程师。他们是你的朋友!他们帮助你做到最好。有空带他们出去喝一杯。在感恩节伸出援手!

德索拉(第六区)Unsplash 上拍摄的照片

确保你和这些同事保持良好的关系,因为你非常依赖他们来帮助你做好工作!

清晰、切实的需求 当您与同事讨论数据合约时,准备一套清晰的需求以及前面提到的解决方案草图将对您大有裨益。这使得项目变得有形,给定数据的表示,工程师可以决定如何最好地引入数据。

提示 6:了解你的东西

在联系数据专业人员之前,请确保您对项目有足够的了解,能够透彻地解释需求。

对于大多数组织来说,数据工程师的资源非常有限,记住您是他们众多内部客户中的一员是很重要的。下一节将详细介绍这一点。

4.跟踪项目的进展

你目前正在进行的项目可能是你要处理的许多项目中的一个。有必要跟踪项目的进展,并告知你的利益相关者项目的进展。

阿尔瓦罗·雷耶斯在 Unsplash 上拍摄的照片

记录进度 记录是跟踪项目进度的最佳方式。这可以通过项目协调软件或至少一个 Excel 或 Word 文档来完成。

在维护文档的过程中,您要确保时间表得到参与项目交付的各个团队的明确沟通、协商和同意。文档有助于让所有团队对时间表负责,并分配资源以确保您的组织有效地分配资源

提示#7:文档是关键

文档有助于建立和维护时间表,保持问责制,并有效地分配资源。

如果时间允许,与利益相关方确认
特别是对于较长的项目,与您的利益相关方确认是一个好主意,告知他们项目的进展,最好向他们展示正在进行的解决方案草案。这允许额外的反馈和潜在的支点,它们在项目进行到一半时比交付时更容易浮出水面。

5.交付项目

大结局!但也不尽然… 正如我之前提到的,很少有一个项目被移交,然后就再也看不到了。您可以采取广泛的措施来减少返工和重新访问项目,但它仍然会发生。

一个项目很少会被移交,永远不会被再次看到。

安东尼·特里维特在 Unsplash 上拍摄的照片

更多文档!我是文档的忠实信徒。这当然是更多的工作,但在许多情况下,它已经扭转了局面。

面向未来用户和其他数据专业人员的文档。完成控制面板或分析后,制作文档来帮助用户浏览您的解决方案。对于你的数据专家同事,包括数据的细节(例如,它来自哪里,它是如何转换的,等等。),以便在您离开当前职位时,您的组织仍然可以使用该解决方案。不要让人们孤立无援!

提示 8:帮助未来的你

文档将帮助未来的您或其他数据专业人员浏览您的解决方案,并最大限度地减少尝试理解其工作原理所浪费的时间。

理想情况下,您的仪表板、分析等。应该是不言自明的—如果做得好,这是数据专业人员的另一个巨大增值,但文档是超越您的角色的关键。

浏览解决方案 交付最终产品时,不要只是把它放在门口。与你的解决方案的用户一起工作,帮助他们理解它是如何工作的,告诉他们任何他们需要知道的细微差别,并邀请他们提出任何关于它的问题或顾虑。这种时间投资有很大的回报。

当你交付最终产品时,不要只是把它放在门口。

6.稍后入住

你已经有很多问题了,所以你为什么愿意邀请更多的问题呢?!关于已交付解决方案的跟进,这可能是您的想法,但与您的内部客户进行沟通非常重要。

查看他们对工具的使用情况、他们遇到的困难、潜在的改进领域等绝对是值得的。然后努力解决任何问题!

照片由克里斯蒂娜@ wocintechchat.comUnsplash 上拍摄

这是对你的组织的积极贡献!在问题积累起来并给你和你的团队带来真正的麻烦之前解决它们。将来你会感谢你的。

技巧 9:签入并迭代

经常与利益相关者联系,并重复可交付成果,以实现持续改进和洞察力的增长。

迭代是分析中最酷的事情。您最初的解决方案可能会带来额外的分析或业务问题!努力重复最初的可交付成果,并提供对这些后续问题的见解。

摘要

没有数据专业是孤岛!在整个数据项目中的有效协作是成为优秀数据分析师的关键。数据约定的之旅包括回应利益相关方的请求,提供粗略布局以确认前进的道路,联系必要的数据专业人员,跟踪项目进度,交付项目,以及稍后在进行检查。

重复出现的主题包括文档和交流!专注于做好这些技能,你就是金牌!

直到下一次…

喜欢这些内容吗?关注我在媒体上看到未来的职位!成为中等会员这样你就可以无限阅读文章了。相信我,值得!

如果你对数据专业人员的生活有任何疑问,请随时在 LinkedInTwitter 上联系我!

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

通过 AWS 认证机器学习专业考试的简明指南

原文:https://towardsdatascience.com/no-frills-guide-to-passing-the-aws-certified-machine-learning-specialty-exam-55624579353f

获得亚马逊网络服务(AWS)机器学习专业认证的高效方法

照片由 Unsplash 上的 Dmitry Ratushny 拍摄

我最近参加并通过了 AWS 认证机器学习专业 (MLS-C01)考试,以验证我在机器学习方面的专业知识以及在云上实现它的能力。

虽然有许多在线学习途径,但大多数都过于冗长,并且充斥着过多的选项和资源。这与我想要的相反:用最少的努力和时间通过测试。

因此,我开发了一个学习路径,只包含成功应对考试的基本资源。这篇文章描述了我采用的简单方法,以及一个有用的免费和付费实践资源列表。

AWS 认证机器学习专业徽章|作者图片

内容

【1】本指南是否适合你【2】循序渐进指南(3)考试提示

(1)本指南是否适合你

在我们开始之前,让我们看看这个指南是否适合你。此学习路径专为以下人群设计:

  1. 扎实的数据科学和机器学习基础知识 你应该对机器学习概念有很强的理解,并且最好已经在实际项目中应用了它们。这意味着您应该熟悉常见的数据科学概念(例如,回归、可视化、预处理、神经网络、正则化、损失函数、决策树、XGBoost 等)。).
  2. 具备 AWS 服务的基本经验或之前已获得 AWS 认证 虽然您不需要丰富的 AWS 专业知识,但您最好具备 AWS 实践经验,因为该考试不适合初学者。如果你此时零 AWS 经验,可以从 AWS 认证云从业者 考试开始:

*💔-steps-to-get-aws-cloud-practitioner-certified-in-2-weeks-or-less-772178f48249> *

逐步指南

下面是我备考的三个主要步骤,用了不到两周的时间完成。

步骤 1 — AWS 示例问题

— — — —
所需时间: 1 天 | 费用:免费
— — — — —

了解考试内容至关重要,这可以通过首先试用 免费 AWS 样题 来实现。这一步让你对所提的问题有所了解,并设定对考试难度的预期。

这些样题灌输了一种谦卑感,因为它比我想象的要复杂,并且提醒我不要低估考试。

在本次准备结束时,我相信您下次再次访问这些示例问题时能够回答它们。

步骤 2 — Udemy: AWS 认证机器学习专业 2022

— — —
所需时间:~ 7 天 | 费用:~ 18 美元
— — — — —

免责声明:本文非赞助。

下一步是报名参加结构化学习计划的在线课程。 AWS 认证机器学习专业 2022 —动手 课程是最受欢迎和最受欢迎的课程之一,所以这是我选择的课程。

记住只在 Udemy flash 销售期间购买课程,因为未打折的价格可能相当高。我以大约 18 美元的价格买下了它。

该课程组织良好,易于理解,内容全面,同时为考试主题提供了足够的深度。以下是你在学习过程中的一些有价值的提示:

  • 在课程提供的 pdf 幻灯片中积极记笔记(如添加便笺)。
  • 跳过基本机器学习概念的大块内容来节省时间,因为你应该已经熟悉它们了。把重点放在你更高级或者不太熟悉的机器学习内容上。
  • 在第一轮浏览幻灯片时,重点是理解概念。在随后的几轮修订中,更多地强调记忆幻灯片中的关键细节,例如,每个高级 AWS 机器学习服务预期的输入数据类型。
  • 如果你使用 Trello,复制这个公告牌(由 Kush Bhatnagar 创建),里面有抽认卡和自我修改的笔记。

一旦完成,你将获得 AWS 机器学习主题的优秀理论知识。

第三步——大量练习

— — —
所需时间: ~5| 费用:免费(大部分)
— — — — — —

这最后一步是最重要的一步——回答尽可能多的练习题。

这些模拟测验有助于巩固你的理论知识,确定你可能薄弱的领域,并为实际考试建立你的精神耐力。

我参加了免费和付费的模拟测试,如下所示:

自由实践

  • AWS 考试准备情况
    AWS 技能构建器平台中有大量的问题用于评估您的考试准备情况,其中包括 模块 7/9:学习问题中的一组优秀问题。
  • AWS 认证机器学习专业(实践)这个在 AWS 认证门户的 60 分钟的考试模拟了实际考试。如果您之前已经完成了 AWS 考试,那么它是免费的(通过免费的实践考试代金券)。您可以在 福利 页签中找到该凭证。我发现模拟考试比实际考试更难,所以如果你在这次练习中取得好成绩,你就成功了。

如果您以前参加过 AWS 认证,您可以兑换一张免费的实践考试优惠券|作者图片

  • 考前培训
    十道免费样题
  • 激活功能
    八道免费样题
  • whiz labs
    15 题自由练习测试(但需要先创建账号)
  • 考试题目
    本网站包含大量自由练习题。然而,许多问题的答案往往是不正确的。您应该查找 AWS 资源或查看对话主题,以确定社区讨论的正确答案。
  • AWS 样题
    再来一遍第一步的样题,看看自己进步了多少!

有偿实践

  • TutorialsDojo(~ USD $ 17)
    虽然在各种问题模式中可能会有一些重复,但还是有几组非常好的问题可供选择。

除了 TutorialDojo,我没有为其他练习题额外付费。我还惊喜地看到实际考试中出现了几道练习题。

以下是一些准备技巧:

  • 保存你难以回答的问题(例如截图),以便你可以随时修改它们。
  • 在练习问题时,请积极参考 Udemy 课程幻灯片、 AWS 白皮书—AWS 服务概述AWS 文档 。检查并正确回答问题比猜测你的答案要好。当然,当你继续做更多的练习题时,你应该减少查阅资料的频率。
  • 探索 AWS 认证机器学习考试指南 看看你是否理解考试内容。
  • 使用 TutorialsDojo 期末考试和 AWS 实践考试作为考试条件下的模拟考试。
  • 没有必要知道所有的答案才能通过考试,所以不要给自己增加不必要的压力!

(3)考试提示

考试包括 65 道选择题,要求在 3 小时内完成。

以下是针对这种机器学习考试的一些重要信息:

  • 考试有四个领域:数据工程(20%)、探索性数据分析(24%)、建模(36%)、机器学习实现与操作(20%)。
  • 除非你有在 AWS 上实现 ML 解决方案的丰富经验,否则工程和运营问题将会更具挑战性。如果你因此花更多的精力去理解诸如 IAM 策略、生命周期配置、Kinesis 系列、批处理作业、AWS 数据同步、部署容器等主题,将会有所帮助。
  • 这些问题可能很长,因为它们在提出实际问题之前描述了一个业务场景。确保你以适当的速度仔细阅读问题,理解上下文。没有必要匆忙,因为有足够的时间在 3 小时内完成测试。
  • 对于那些难住你的问题,尽最大努力**排除最没有意义的选项,然后从剩下的选项中做出你最大的猜测。
  • 与高级人工智能服务相关的问题很容易得分,但你需要知道它的复杂性。一个很好的例子是,Amazon Connect 的隐形眼镜可以执行情感分析,并自动对来电进行分类,因此不需要为这些任务引入 Amazon understand。**

(4)结论

有了这个 3 步指南,我第一次尝试(在不到两周的准备时间内)就以 847 的高分通过了 AWS 机器学习专业考试。

作者图片

准备工作最有收获的方面是更好地掌握了在 AWS 上实现机器学习解决方案。我希望你也会有同样的感觉!

在你走之前

欢迎您来到,与我一起踏上数据科学学习之旅。跟随此媒体页面并查看我的 GitHub 以了解实用教育数据科学内容。同时,祝你考试顺利!**

***💔-steps-to-get-tableau-desktop-specialist-certified-in-2-weeks-abbef25778de> ***

没有可用的 GPU,现在怎么办?

原文:https://towardsdatascience.com/no-gpus-available-what-now-b9c01108ca58

约瑟夫·格雷夫在 Unsplash 上的照片

如何解决 GPU 短缺的问题

图形处理单元(GPU)已经成为当今 MLOps 生态系统不可或缺的一部分,随着越来越多的公司在生产中运行人工智能模型,对 GPU 的需求也大幅上升。在毕马威 2021 年的研究中,在人工智能世界中茁壮成长,来自许多行业的商业领袖指出,人工智能在他们的组织中至少具有适度的功能,包括:工业制造(93%)、金融服务(84%)、科技(83%)、零售(81%)和生命科学(77%)。

然而,在过去的几年里,GPU 的供应已经跟不上需求的增长。物理 GPU 几乎经常售罄,大型云提供商似乎也无法满足需求,导致 GPU 等待时间过长。但是为什么对 GPU 的需求那么高?如果你需要,你能做些什么呢?

为什么 GPU 如此重要?

要理解为什么 GPU 对操作机器学习模型如此重要,最好看看 GPU 是为什么类型的应用而制造的。Owens 等人在他的论文“GPU 计算”中列出了 GPU 应用的以下特征:

  • 计算量大
  • 高并行度
  • 吞吐量比延迟更重要

换句话说,GPU 非常擅长执行成千上万的并行线程,以快速解决具有大量内在并行性的大型问题。每个单独的计算可能比在 CPU 上慢一点,但因为一个 GPU 可以并行处理这么多,所以整个问题解决得更快。

由于大多数机器学习算法基本上是一堆线性代数运算的串联,所以它们非常适合并行化。典型的神经网络由互连层的长链组成(见下图)。虽然在一个完整的网络中有大量的计算,但它可以被分解成更小的、顺序相关的工作块层。正因为如此,神经网络可以真正利用 GPU 的能力来达到比 CPU 更高的速度。

图片由作者提供,灵感来源:Choquette 等人(2021)*《NVIDIA A100 张量核心 GPU:性能与创新》 。此图概述了深度学习网络映射到 GPU。*

等人 实际上是在 CPU 和 GPU 之间进行了一次彻底的深度学习的基准测试。即使他们的论文来自 2017 年,他们的结论仍然成立。在深度学习方面,GPU 的表现优于 CPU。你可以在下图中看到他们的结果表。每个类别中最快的时间用粗体标出。

图片由石等人提供。al (2017) 【标杆最先进的深度学习软件工具】 。以粗体显示,您可以在每个类别(台式机 CPU、服务器 CPU、单个 GPU)中找到最快的时间。GPU 胜过 CPU。

当你需要一个 GPU 时,获得它的困难

随着 GPU 对于 ML 工作负载如此完美,以及越来越多的公司使用 AI,需求大幅上升。这种需求的增长使得现在很难获得 GPU。当您想要在 GPU 上运行工作负载时,大致有两种选择:

  1. 买个物理 GPU
  2. 使用云 GPU (例如在 AWS 或谷歌,或通过特定的机器学习(ML)平台,如 UbiOps)

物理图形处理器非常昂贵,而且也经常售罄。这是由所谓的 黄牛造成的,他们一旦在网站上提供,就会用机器人买下所有可用的 GPU。正因为如此,你只能从这些黄牛手里买 GPU,除非你比机器人还快。黄牛以比原价高出 200% 的价格转售产品,价格高得离谱。

随着物理 GPU 的天文价格,云 GPU 成为一个更有趣的选择。你通常只需为使用 GPU 的时间付费,所以如果你的工作负载不运行太长时间,这通常比你自己购买 GPU 便宜得多。云 GPU 是高度可扩展的,它们最小化成本,并且它们还清理本地资源。当你的模型在一个云数据中心的 GPU 上运行时,你可以继续使用你的笔记本电脑。

然而,云一直难以跟上 GPU 需求的增长。随着需求的增加,但供应的增加太少,用户最终需要等待 GPU 变得可用。Gigaom 的人工智能分析师 Anand Joshi 研究了这个问题,并注意到许多用户正在经历比几年前更长的等待时间。如果您在生产中有一个需要在几秒钟内做出响应的机器学习(ML)模型,如果您甚至不能在一分钟内获得可用的 GPU,这可能会成为一个大问题。在这种情况下,您可能需要从云中购买 24/7 可用的专用 GPU,但也因此贵了很多。

如何解决 GPU 短缺的问题?

假设您的模型需要一个 GPU,但您无法获得物理 GPU,并且您在云中的排队等待时间太长,您有什么选择?

正如我之前提到的,您可以从任何云提供商那里购买专用 GPU(您可以按月获得 GPU,而不是按小时),但这对您的组织来说可能太贵了。您还可以尝试在不同的云中轮询可用的 GPU,并选择提供 GPU 最快的一个。但是,您必须处理多种云,并使您的基础架构与云无关。这太麻烦了。

另一种选择是采用专注于 GPU 的云环境,如 埃舍尔云 (由 NVIDIA 支持的欧洲云),或Paperspace Core。但是,这对您的组织来说可能是多余的,尤其是如果您还需要常规的 CPU 或其他与 MLOps 相关的特性。然而,如果你专注于图像或视频处理,它可能是一个很好的选择!

我想强调的最后一个选项是使用支持 GPU 的机器学习操作(MLOps)平台,如 UbiOps 。它们为 MLOps 提供了额外的功能,并且通过服务级别协议(SLA ),您可以对 GPU 的可用速度有一定的发言权。UbiOps 是一个运行在云中的 MLOps 平台。它将大部分后台 It 基础设施抽象化,因此您无需担心。UbiOps 还提供可扩展的 GPU 支持,它可以在任何云上运行。他们还与 Escher Cloud 和其他欧洲云建立了合作伙伴关系,如果你是一家处理敏感数据的欧盟公司,这将非常方便。当然,我在 UbiOps 工作,所以我有一点偏见,所以请随意测试一下,亲自看看!

结论

鉴于目前的需求和公司计划将越来越多的人工智能融入他们的业务,我认为可以肯定地说,GPU 在一段时间内仍将稀缺。尽管如此,还是有办法得到 GPU 的。

无论您是购买自己的产品,使用云产品,还是通过 MLOps 平台使用,都有多种选择!哪种选择最适合取决于您的用例以及您愿意等待 GPU 可用多长时间。

数据质量不能仅靠工具来解决,要解决数据卫生问题

原文:https://towardsdatascience.com/no-magical-toothpaste-for-data-quality-cavities-76d21ff9278f

大规模数据卫生的 10 个必备流程!

确保良好的数据质量类似于牙齿卫生。没有牙膏或牙刷能神奇地确保没有蛀牙。相反,定期刷牙和使用牙线的过程是确保没有蛀牙的关键。数据质量也是如此——没有定义良好的流程,即使是最好的工具也是无用的!

图片来源: Unsplash

在过去几年领先的数据和人工智能产品中,这个博客分享了我的经验,这些经验可以帮助你开始在你的平台中建立数据卫生。

10 个数据卫生流程

这些过程分为三类:

卫生流程@数据源

**1。在应用层中验证数据的过程:在将值添加到数据库之前验证它们。例如,信用卡号被添加到名称字段中。

**2。DDL 变更管理流程:任何 DDL 变更都应生成自动警报,并发送给数据工程/数据运营团队。此类变更在吉拉中被跟踪(类似于代码),并需要基于下游数据沿袭分析的批准。如果没有这样的流程,上游的更改通常会破坏下游的分析,从而影响数据质量。

**3。监控数据库和CDC复制框架健康状况的过程:数据源可能会遇到事务超时、网络分区等问题。通常会导致源表中的数据丢失。可以主动使用这些异常来避免将低质量的源数据合并到数据湖中。

卫生流程@数据湖

**4。跟踪数据摄取及时性的流程:确保数据在湖中及时摄取。如果违反了 SLA,DataOps 团队会收到警报,以便可以适当地调度下游流程,并通知仪表板/模型所有者。该流程还扩展到跟踪第三方来源的数据可用性

**5。计数奇偶校验验证过程:捕捉摄取问题的一个有效而简单的过程是使用源数据和摄取数据之间的计数奇偶校验。例如,如果 10K 新行被添加到表中,并且只有 1K 被吸收到湖中,那么主动警报可以节省数小时的下游调试时间(当仪表板显示不正确的订户计数或销售数字时)。除了计算奇偶性,主动跟踪其他元数据属性中的异常。

**6。跨业务单元筒仓记录数据契约的过程:在数据网状世界中,每个团队需要负责测试和验证他们的数据。与微服务的 API 类似,数据模式和属性需要记录下来,并作为可度量的契约来执行。

卫生流程@指标

**7。版本控制业务逻辑的过程:在大多数组织中,同一个指标有多个定义。随着业务的发展,这些定义会发生变化。与代码类似,对通过 ELT/ETL 转换实现的业务定义进行版本控制和支持是非常重要的。

8.验证 ELT/ETL 变更的沙盒流程:组织的开发环境与其生产设置相比,通常只有很少或没有代表性的数据。确保所有的 ELT/ETL 逻辑变更在一个有代表性的沙盒环境中得到验证是避免质量问题的一个关键因素。

9.基于谱系的根本原因分析过程:为了解决生产中发现的问题,通常会应用快速创可贴来解决问题。制定一个根本原因分析流程,从源头开始追溯谱系,确保观察到的质量问题得到永久解决。

10.连续数据验证过程:在三个阶段的每一个阶段实施一系列数据验证检查。下面是一些数据验证用例的例子。

作者图片

总而言之,如果您在我们的仪表板和 ML 模型中遇到数据质量问题,那么是时候重新审视您的整体数据卫生流程了。根据我的经验,这样的投资回报显著,而且没有捷径可走!

查看 解开数据 ,了解基于人工智能的可观察性如何帮助您构建正确的数据操作流程!

再也不需要写 SQL 了:SQLAlchemy 的 ORM 完全适合初学者

原文:https://towardsdatascience.com/no-need-to-ever-write-sql-again-sqlalchemys-orm-for-absolute-beginners-107be0b3148f

有了这个 ORM,你可以创建一个表,插入、读取、删除和更新数据,而不用写一行 SQL

用 ORM 开发你的生活会有多美好(图片由 Christian JoudryUnsplash 上提供)

与执行原始 SQL 相比,使用 SQLAlchemy 的 ORM 有许多优点。在本文中,我们将演示使用 ORM 的基础知识。阅读完本文后,您将能够创建表、插入、选择、更新和删除数据。理解 ORM 将为你节省很多错误和调试时间,所以不要再浪费时间了,让我们开始编码吧!

什么是 ORM,它有什么作用?

ORM 代表对象关系映射。这是一种在 Python 代码和 SQL 表之间充当某种翻译器的技术。它基本上允许您使用数据库表中的记录作为 Python 中的 Python 对象。可以把它想象成把你的表转换成一个列表,其中每个条目都是一个代表一行的 Python 对象。

使用 ORM 有很多优点:

  • 数据库无关:即使在迁移数据库时,您的代码也能工作
  • 不易出错:没有语法错误,因为生成了 SQL
  • 通过模型实现的额外功能(例如验证)
  • 组织:通过保持项目中所有模型的整洁组织

现在我们知道使用 ORM 是个好主意,让我们来看看如何使用。

使用 ORM

在这一部分中,我们将关注基础知识,并使用 SQLAlchemy ORM 来:

  1. 创建表格
  2. 插入记录
  3. 选择一条记录
  4. 删除记录
  5. 更新记录

注意 : SQLALchemy 需要一个允许 Python 从我们的数据库中读取数据的数据库引擎。看看下面这篇关于如何创建一个的短文。

1.创建表格

在这一部分中,我们将为我们的表定义模型。在下面的部分中,我们以 Student 类的形式创建一个 Python 对象,然后使用 SQLAlchemy 实际创建表。

查看如何创建 dbEngine_PG 此处

就这么简单!Students 类描述了我们的表:表的名称、模式和每一列的配置。

接下来我们导入 declarative_base。这是一个返回基类的函数,所有的表模型都必须继承这个基类。把它想象成给你所有的模型提供工具箱。然后,我们使用基本的“工具箱”通过我们的数据库连接来create_all表格。

注意,这是我们定义表结构的唯一地方。这意味着您可以将所有的表模型捆绑在一起,而不是在整个项目中使用原始 SQL。

[## SQL —在一条语句中插入、删除和更新:用 MERGE 同步表

towardsdatascience.com](/sql-insert-delete-and-update-in-one-statement-sync-your-tables-with-merge-14814215d32c)

2.使用 ORM 插入记录

在这一部分中,我们将使用上面定义的模型在新创建的表中插入一些新记录。

查看如何创建 dbEngine_PG 此处

我们通过创建以前的 student 类的新实例来创建一个新的学生。然后,我们使用导入的 sessionmaker 添加并提交到我们的表中。这不仅非常容易;请注意,我们没有编写一行 SQL!

3.使用 ORM 选择记录

选择数据甚至比插入数据更容易:

点击 查看如何创建 dbEngine_PG

使用我们的模型,定义我们的语句相当容易。请注意,我们添加了一个限制。当使用连接到 Postgres 数据库的 dbEngine 时,它被编译为SELECT * FROM students LIMIT 5
如果我们切换到 SQL Server dbEngine,这条语句将被编译成SELECT TOP 18 * FROM students。这就是数据库无关的 ORM 的威力!

接下来,我们在一个会话中执行该语句,并遍历结果:

*Student mike is 33 years old*

* *

4.删除记录

删除数据非常类似于选择:

查看如何创建 dbEngine_PG 此处

请注意,我们对 delete 语句应用了一些过滤。我们当然可以在前一部分的 select 语句中做同样的事情。

* *

5.更新记录

更新也很简单:

查看如何创建 dbEngine_PG 这里

deleteselect语句的不同之处在于,我们还添加了用来更新所选记录的新值。这些在 6 号线上整齐的传递。

* *

ORM 的更多优势:验证

因为我们已经在类中定义了我们的表,并且还使用该类来创建新记录,所以我们可以添加一些非常高级的验证。想象一下有人试图添加一个 300 岁的学生。我们可以轻松地更新我们的学生表模型,以验证所提供的年龄:

如你所见,我们检查提供的年龄是否大于 110,如果是,则抛出一个错误。这将防止该记录被插入我们的数据库。

* *

关系呢?

目前我们只有一张桌子:学生。大多数应用程序都有更多相互关联的表。学生是班级的一部分,有多门课程。每门课程有一名教师等。

SQLAlchemy ORM 允许您在一条语句中轻松地查询相关的表:选择一个学生还将返回与该学生相关的所有班级、课程和教师。请关注我的下一篇文章https://mikehuls.medium.com/membership

*** ***

后续步骤:

有了数据库引擎和 ORM 的知识,我们就可以专注于下一步了。

  1. 使用迁移模型
  2. 了解索引如何作用于表格
  3. 跟踪数据库中的慢速查询
  4. 使用Docker部署您的数据库
  5. 在你的数据库上添加一个API
  6. 还有更多

在以后的文章中,我们将探索如何在 ORM 中动态生成模型,以及如何使用通过外键相关的表,所以请确保 跟随我

*** ***

结论

在本文中,我们探索了 SQL 炼金术的起源;我们知道如何连接到数据库,以及如何执行原始 SQL。我们还讨论了执行原始 SQL 的利弊,并理解对于大多数项目来说,使用 ORM 是更好的选择。在下一篇文章中,我们将了解 ORM 以及如何使用它。

我希望一切都像我希望的那样清楚,但如果不是这样,请让我知道我能做些什么来进一步澄清。同时,看看我的其他关于各种编程相关主题的文章,比如:

编码快乐!

—迈克

附注:喜欢我正在做的事吗? 跟我来!

***https://mikehuls.medium.com/membership ***

向非技术团队传达数据科学见解

原文:https://towardsdatascience.com/no-one-cares-about-your-f1-score-45c17413e29f

通过理解和迎合你的听众,让他们听到你的声音

图片由 Freepik 上的 pch.vector

F1 和 AUC 对商务人士的意义,就像“客户获取”或“收入增长”之类的术语对你的意义(提示:毫无意义)。简单来说,

如果你的度量标准被认为没有影响到他们,那么非技术利益相关者不会在意。

你知道结果。你知道这会影响他们。你认为他们应该关心。但是他们没有。我们如何迫使他们关心?

“专注于发现有意义的业务见解的分析团队可能会忽略将这些见解有效传达给跨职能合作伙伴的需求,这些合作伙伴可以利用这些建议来改善业务。”[1]

这里的关键词是有效地沟通。以原始形式分发度量和图表是不够的。

“数据科学家接受培训以优化的数量,他们用来衡量其数据科学模型进展的指标,如果没有大量的翻译工作,对业务利益相关者来说根本毫无用处,而且与业务利益相关者脱节。”[2]

为了有效地交流数据科学见解,您需要学会说他们的语言。为此,首先,我们需要学习每个利益相关者使用的独特语言。

本文旨在了解利益相关者在组织中的角色,并从数据科学的角度理解他们独特的观点。我们将深入探讨如何向非技术利益相关方传达数据科学流程的关键方面。

利益相关者

“利益相关者是指与公司有利益关系的一方,可以影响企业,也可以受企业影响。”[3]

在下图中,我们可以看到数据科学项目中的各种利益相关者,根据他们对数据科学团队负责的工作类型的了解程度。

跨利益相关方的数据科学团队的知识范围(图片由作者提供)

注意,这些都是根据我过去的经验归纳出来的。这种结构可能因组织而异,也可能因每个团队中的人员而异。

一般来说,我们可以从上图中看到三种不同的分组:

  1. 产品聚焦:工程(&产品),项目经理
  2. 面向用户:客户成功、营销、销售
  3. 聚焦业务:高管团队

我们现在想了解这些利益相关群体的不同方面。我们将借鉴 UX 设计的概念:用户角色。

"用户角色是典型的用户,他们的目标和特征代表了更大的用户群体的需求."[4]

在我们的案例中,我们将使用类似的方法来尝试理解我们的主要利益相关者群体。我们将使用这个框架从每个小组的数据中理解目标&优先级需求。

1.以产品为中心

专注于产品的团队被包括在内是为了完整性,但与和非技术利益相关者沟通的讨论无关。

2.面向用户

这些团队是用户看到的产品和组织的面孔。

目标&优先事项:这似乎很明显(例如“销售:销售产品”),但这是一个天真的观点。工程可以概括为“制造产品”,但这不会让外人真正了解工程是做什么的,以及它如何与他们相关。因此,我们将为团队的日常活动提供更明确的目标:

  • 客户成功:“与客户合作,帮助他们更好地利用产品或服务。反过来,它们通常有助于通过增加用户采用率和净推荐值来减少客户流失,并有助于进行追加销售,甚至是扩大账户”[5]。这个团队通常最了解客户。
  • 营销:这个团队的主要目标包括:提高品牌知名度,产生潜在客户,增加客户价值,提高搜索引擎优化,增加媒体曝光率,提高转化率[6]。
  • 销售:设定并实现 SMART 目标,例如增加:年销售额和利润、客户数量、追加销售和交叉销售、转换率、客户保持率、销售代表生产力以及拓展合格的潜在客户。

总之,面向用户的团队吸引潜在客户(营销),将潜在客户转化为客户(销售),并提高客户利用率和产品的有用性(客户成功)。他们必须能够让用户相信你的产品性能良好,能够满足他们的需求。用户“…可能会优先考虑产品的性能,因为如果产品有一些缺陷,他们会有点怀疑”[7] 。用户也倾向于不喜欢他们认为是对产品“不必要的改变”,尤其是如果这让他们的感知体验变得更糟。

来自数据的需求:这个群体关心用户,关心数据科学能提供什么,让他们理解用户,更好地与用户沟通。诸如改善用户体验、减少客户流失、识别高价值客户和个性化用户体验等主题对他们来说具有重要价值。

3.以商业为中心

目标&优先事项:高管团队是“…负责为公司创造战略方针和运营方法,并支持员工,使他们能够在自己的岗位上取得成功”[8] 。优先事项包括“制定和执行长期和短期目标,做出财务决策和维护组织的预算,并合作确保所有部门专注于公司范围的目标”[8]“[他们]对数据科学项目的主要兴趣始终是增加公司的价值(直接或间接)。如果他们是会议的一部分(计划、更新等。),他们会寻找项目会带来的投资回报”[7]

来自数据的需求:这个群体关心整体业务以及如何使用数据来帮助回答源于他们的优先事项的问题。然而,太多的信息也是一种负担,

“信息系统给我的第一个印象是人们获取了太多的信息。信息爆炸让大量数据在管理层的办公桌上来回穿梭。其中大部分只是被部分消化了,而且大部分是不相关的……”[9]

因此,这个群体的要求是准确、简洁、可操作的见解,这是回答他们的业务问题和增加公司价值的关键。

沟通

图片由Freepik上的故事集提供

既然我们已经理解了利益相关者群体的目标、优先事项和需求,我们如何利用这种理解来恰当地交流信息呢?

沟通时要始终牢记的问题:

  • 他们为什么要关心?我是否传达了他们应该关心的信息?
  • 我传达的信息对他们有什么影响?

探索双周刊

让我们从一个例子开始,这个例子将用来展示我们如何与每一个利益相关者沟通。假设你是一家流行音乐流媒体服务公司的数据科学家。这项服务的一个重要组成部分是音乐推荐,这个功能我们最初完全称之为“发现双周”。这项服务根据用户的收听习惯,每两周向他们推荐一个新的音乐播放列表。用户喜欢它,因为它允许他们发现新的音乐,否则他们不会听到。企业喜欢它,因为它让用户继续使用这项服务,听更多的音乐,这反过来让他们赚更多的钱。

假设我们的总体业务目标和该特性的目标包括:

  • 最大化用户使用服务的时间,
  • 最大化用户使用该特定特征的时间,
  • 最大化提供给用户的推荐的相关性。

现在假设我们将这组业务目标转化为一组量化的目标(我们将使用它们来评估我们的模型)。新模型必须考虑对以下方面的影响:

  • 用户在指定时间段内使用服务的时间,
  • 用户使用推荐功能所花费的时间(每两周收听一次他们的发现),
  • MAP@K,用作推荐的总体相关性的代理,
  • 不同之处在于为特定用户确定播放列表的个性化,
  • 列表内相似性,以确定推荐是否足够多样化。

对于数据科学家来说,包含从用户子集收集的时间序列数据的经过清理和处理的数据帧可能如下所示:

包含指标时间序列数据的数据框架(图片由作者提供)

您还可以从旧模型和新模型收集的指标中生成一些统计数据来比较性能:

为简单起见,从收集的指标生成的统计数据在一段时间内进行平均(图片由作者提供)

或许可以生成一些图来帮助你更好地理解统计数据:

时间平均数据的平均值和标准偏差图(图片由作者提供)

从这些图像中,你可以对你的新模型下结论。问题是这些指标和图表对于非技术涉众来说是没有用的。如果你能说服营销因为你把 MAP@K 平均提高了 10%而兴奋——请告诉我你的秘诀。

在以后的章节中,阅读时请记住前面提到的问题:

他们为什么要关心?我是否传达了他们应该关心的信息?我传达的信息对他们有什么影响?

面向用户

度量应该以“这对用户有什么影响”的方式进行分析和呈现这些团队向用户传达你的工作方式和原因。

使用上面的例子,MAP@K 的改进和列表内相似性的增加对面向用户的团队本身没有任何意义。相反,我们可以将这种分析分解成,例如,用户体验如何变化的可视化:

用户体验变化的可视化(图片由作者提供)

左图显示了我们可以从 MAP@K 中获得的用户体验的总体变化类型。右图显示了两个样本用户以及他们的播放列表如何变化,这可以从列表内的相似性中获得。

我们可以清楚地看到,对于面向用户的团队来说,以这种方式呈现的信息比以前的度量标准更有价值。它直接让他们了解实际用户是如何受到影响的,并帮助他们理解新模式的利弊。“他们为什么应该关心”的答案在图表中清晰地呈现出来:它会影响用户,下面是如何影响的。它允许他们准确地评估变更,确定变更是否对用户有益,并为他们提供一种向用户传达变更的方式。这只是一个简单的例子,但它很好地说明了这个概念。

以商业为中心

类似于面向用户的团队,我们需要以一种迎合以业务为中心的团队的方式来分解和呈现度量。指标应该简洁、可行,并与公司将受到的影响直接相关。

我们应该着重说明这些变化是如何与整体业务目标联系起来的。如下所示的可视化可能是合适的:

一段时间内使用情况的可视化,新模型在第 0 周部署(图片由作者提供)

在上图中,我们展示了新模型的实现将如何影响总时间和功能时间这两个关键业务指标的可视化。这将允许业务团队快速判断这些变化是否符合整体业务目标。如果度量标准没有恰当地呈现,某些特征可能会被忽略或者它们的重要性被夸大。您的数据可视化应该尽可能地讲述最真实、最准确的故事。在这种情况下,你的工作不是做决定,而是给决策者提供可操作的见解。

考虑一下,如果我们只给出时间平均平均值和标准差。业务团队将会看到总时间的下降,立即认为您的新模型更差,并拒绝您的更改。事实上,上面的可视化可能意味着用户只是在适应变化,因为我们看到总用户数呈上升趋势,反弹到以前的水平。总而言之,

“当高管不理解建议或其业务影响时,许多不强调沟通的分析团队会让洞察力从缝隙中溜走。”[1]

通常

除了根据沟通对象考虑如何沟通之外,在进行数据科学沟通时,还有一些通用的最佳实践[1]:

结论

TL;博士:

你的观众很重要。了解他们的目标、优先事项和要求。如果你想让他们在乎,你需要迎合他们。

我希望这篇文章对你和我写它一样有用。

如果你喜欢这篇文章,请关注!我感谢❤️的支持

领英|【brandenlisk.com】T2

https://medium.com/@brandenlisk [## Branden Lisk -中等

medium.com](https://medium.com/@brandenlisk)

来源

[1]https://door dash . engineering/2021/02/11/how-to-drive-effective-data-science-communication/

https://hdsr.mitpress.mit.edu/pub/bfeyfx22/release/2

https://www.investopedia.com/terms/s/stakeholder.asp

[4]https://xd . adobe . com/ideas/process/user-research/put-personas-to-work-in-UX-design/

[5]https://hypercontext . com/blog/work-goals/customer-success-goals

[6]https://www . indeed . com/career-advice/career-development/goals-marketing

[7]https://justabasicdatascientist . com/dealing-with-business-stakeholders/

https://www . indeed . com/career-advice/career-development/leadership-team-purpose

[9]https://HBR . org/1979/03/chief-executives-define-own-data-needs

https://www.pipedrive.com/en/blog/sales-objectives-examples

[11]https://towards data science . com/evaluation-metrics-for-recommender-systems-df56c 6611093

使用 Node2Vec 进行节点分类

原文:https://towardsdatascience.com/node-classification-with-node2vec-58892845697b

用 Python 构建多类节点分类模型

图片由 Gerold HinzenUnsplash 拍摄

本文将展示使用图形解决分类问题的管道的实现。今天介绍的解决方案背后的基本算法是 Node2Vec。如果你对 node2vec 不熟悉,我以前写过一篇关于它的文章,你可以在下面看看。

您还可以在这里查看如何使用 Node2Vec 构建链接预测推荐系统。

目录

  • 什么是节点分类?
  • 问题陈述
  • 解决方案架构
    -需求
  • 数据
  • 创建网络
  • 应用节点 2Vec
  • 训练分类器
    -评估分类器
    -预测节点类
  • 结束语
  • 资源

什么是节点分类?

节点分类是机器学习在图上的常见应用。通常,您训练一个分类模型来学习某个节点属于哪个类。
这种方法常见于二元和多类分类[1]。在二元分类中,你要处理两个不同的类,而在多类分类中,你要处理两个以上不同的类。
在本教程的上下文中,我们将使用 node2vec 来生成网络的节点嵌入。Node2vec 旨在保留原始图中的初始结构。

图 g 中的节点分类示例。右图展示了按不同颜色(红色、蓝色、绿色)分类的节点。图片由作者提供。

问题陈述

给定 arXiv 上发表的研究论文的主题,我们将构建一个管道,该管道将训练一个模型来根据其主题对研究论文进行分类。

解决方案架构

我们将首先创建一个网络,其中节点作为文章,连接这些节点的边基于连接一对文章的主主题。创建这个网络后,我们将使用 node2vec 来生成与每篇文章相关的节点嵌入。最后,我们可以将与每个节点关联的节点嵌入映射到其关联的主题。嵌入可以作为特征传递,并且主主题可以作为训练分类模型的目标。

上面概述了建议的节点分类体系结构。图片由作者提供。

要求

Python=3.8.8
arxiv=1.4.2
networkx=2.5
pandas=1.2.4
numpy=1.20.1
node2vec=0.4.4
sklearn=0.24.1

如果你没有安装 node2vec 包,这里的是通过命令行安装它的库文档。类似地,您可以使用以下指令用 Python 安装 arXiv 包这里

数据

我们将使用 arXiv API 来收集与各种关键字相关的研究论文和出版物的数据。基于他们 API 的 arXiv 使用条款,它是完全免费的,并鼓励使用。有关其使用条款的更多信息,请参考其文档,您可以在此处找到。

在本文中,我将向您展示如何通过 Python 调用 API 来收集我们今天构建的模型所需的以下信息。如果你想通过其他编程语言使用这个 API,或者只是想了解更多关于如何使用这个 API 的信息,我强烈建议你参考他们的文档,你可以在这里找到。

我们想要点击 arXiv API 来收集一些关于基于我们上面确定的查询的最新研究论文的信息。这将允许我们根据这篇研究论文数据创建一个网络,然后我们可以尝试对该网络上的节点进行分类。出于本文的目的,我将在每个查询中搜索最多 250 个结果,但是您不必为自己设置相同的约束。arXiv API 允许用户每次查询达到 300,000 个结果[2]。下面概述的函数将生成一个获取以下信息的 CSV:
title, date, article_id, url, main_topic, all_topics, authors, year
您可以获取更多信息,如链接、摘要、文章,但我决定不这样做,因为这些功能不会真正用于本分析和教程。

下面是我点击 arXiv API 时得到的输出示例。根据您选择查询的关键字和时间,您可能会收到不同的信息。

通过 API 查询 arXiv 后的示例输出。图片由作者提供。

如果您在查询数据时遇到问题,出于可再现性的目的,我在本文中进行的分析所使用的 CSV 被上传到我的 GitHub,您可以在这里找到。

创建网络

现在我们已经使用 arXiv API 获取了数据,我们可以生成一个网络。该网络将具有以下结构,节点将是 article _ ids,边将是连接一对文章的所有主题。例如,具有以下主题天文物理学的 article_id 1 和具有主题 stats 的 article_id 10 以及具有主题astro-physicsmath的 article_id 7 可以被连接。这将是一个多边网络,其中每条边的权重为 1。

以下是我生成的与网络相关的统计数据:

生成的图表统计信息的摘要。图片由作者提供。

应用节点 2Vec

这个组件将包括在上面生成的图上运行 node2vec,并为该网络创建相关的节点嵌入。这些嵌入将会起到至关重要的作用,因为它们是构建节点分类模型所必需的主要特性。

下面您可以看到嵌入数据的输出示例,其中索引值代表节点,主主题列代表目标变量。

node2vec 生成的嵌入数据的输出示例。图片由作者提供。

在我查询这些数据的那天,我注意到数据集中有 110 个不同的主题。下面你可以看到与研究论文中最常出现的主题相关的数值。

十大最常用话题。图片由作者提供。

训练分类器

评估分类器

为了评估该模型的性能,我们将考察以下准确度得分:

  • 准确度分数
  • 马修相关系数
  • 分类报告
  • 混淆矩阵

与节点分类模型评估相关的准确性测量。图片由作者提供。

我没有添加与分类报告和混淆矩阵相关的图像,因为它们太大了(因为有 110 个唯一的类)。你可以参考 Jupyter 笔记本中与本教程相关的图片这里

预测节点类

结束语

从管道的结果可以看出,梯度提升分类器在基于 node2vec 特性的节点分类方面做得非常好。这一点基于准确率、马太相关系数、混淆矩阵、分类报告的高分都有明显体现。

这种流水线很容易被二进制分类所复制。我鼓励您训练和测试各种分类模型,看看哪种模型最适合您正在使用的数据集。

要继续学习这篇教程,你可以参考我的 GitHub 上的 Jupyter 笔记本。

资源

如果你喜欢读这篇文章,这里有一些我写的其他文章,我想你可能也会喜欢。

Node2Vec 解释

原文:https://towardsdatascience.com/node2vec-explained-db86a319e9ab

用 Python 解释和实现 Node2Vec 白皮书

图片由阿丽娜·格鲁布尼亚拍摄自 Unsplash

目录

  • 介绍
  • 什么是图?
  • Node2Vec 是什么?
  • 随机漫步生成
  • 跳过程序体系结构
  • 它是如何工作的
  • 履行
  • 为什么要用 Node2Vec?
  • 结束语
  • 资源

介绍

这篇文章将给出由阿迪蒂亚·格罗弗朱尔·莱斯科维奇提出的 node2vec 算法的直观和技术理解。这篇论文可以在这里找到并阅读,对于那些想更深入了解所介绍的技术概念的人来说。本文作者在 python3 中制作了一个名为 node2vec 的开源包,可以通过 pip 安装,用于网络上的可扩展特征学习。在网上可以找到这篇论文的各种开源实现,作者 Aditya 创建的知识库可以在这里找到。另一个非常流行和常用的实现也可以在这里找到。出于本文后面的教程和示例的目的,我将使用后一个包。

什么是图?

图论,致力于研究图形的数学领域。图是模拟节点之间成对关系的数学结构[1]。当节点之间存在关系时,这些节点通过边连接。节点也可以与自己有关系,否则称为自循环。图有多种形式,每种形式都有明显不同的特征,图可以是有向的、无向的、加权的、二分的等等。每个图也可以映射到一个邻接矩阵。邻接矩阵中的元素表示图[1]中的节点对是否彼此相邻。您可以参考图 1 来直观地了解图表的外观。

图 G 的邻接矩阵(图片由作者提供)

什么是 Node2Vec

使用网络时,一个值得注意的问题是将网络结构转换为数字表示,然后可以传递给传统的机器学习算法。Node2Vec 是一种允许用户将图 G 中的节点映射到嵌入空间的算法。通常,嵌入空间的维数低于原始图 g 中的节点数。该算法试图保留原始图中的初始结构。本质上,图中相似的节点将在嵌入空间中产生相似的嵌入。这些嵌入空间本质上是对应于网络中每个节点的向量。图嵌入通常用作输入特征,以解决围绕链接预测、社区检测、分类等的机器学习问题。

使用 node2vec 从输入图 G 生成 n 维节点嵌入(图片由作者提供)

一般来说,当处理非常大的图表时,科学家很难直观地表示他们正在处理的数据。查看图形外观的常见解决方案是生成与该图形相关联的节点嵌入,然后在低维空间中可视化这些嵌入。这使您可以直观地看到在非常大的网络中形成的潜在集群或组。

随机漫步生成

理解什么是随机漫步以及它们如何工作对于理解 node2vec 如何工作是至关重要的。我将提供一个高层次的概述,但是如果你想更直观地理解和实现 python 中的随机漫步,你可以阅读我以前写的关于这个主题的文章。

作为一个高层次的概述,随机漫步的最简单的比较就是漫步。想象你走的每一步都是由概率决定的。这意味着在每一个时间指数上,你已经基于一个概率结果朝着一个确定的方向前进。该算法探索了您将要采取的每一步的关系以及它与初始起点的距离。

现在你可能想知道这些从一个节点移动到另一个节点的概率是如何计算的。Node2Vec 引入了下面的公式,用于确定移动到节点 x 的概率,假设您先前在节点 v。

图片取自 Node2Vec 论文 [4]

其中 z 是归一化常数,πvx 是节点 x 和 v [4]之间的非归一化转移概率。显然,如果没有连接 x 和 v 的边,那么概率将是 0,但如果有边,我们确定从 v 到 x 的归一化概率。

该论文指出,引入偏差来影响随机游走的最简单方法是,如果每个边都有一个权重。然而,这在未加权的网络中是行不通的。为了解决这个问题,作者引入了由两个参数 p 和 q 控制的引导随机行走。p 表示随机行走回到前一个节点的概率,而 q 表示随机行走可以通过图中以前看不见的部分的概率[4]。

图片取自 Node2Vec 纸 [4]

其中,dtx 表示节点 t 和 x 之间的最短路径,如下图所示。

图片取自 Node2Vec 论文 [4]

跳过程序体系结构

为了理解 node2vec 中正在做的事情,有必要对 word2vec 有一个大致的了解。不久前,我写了一篇文章解释 word2vec 论文的直觉和实现。你可以在这里查看。

出于本文的考虑,我将提供跳格模型的高级概述。

skip-gram 模型是一个简单的神经网络,具有一个经过训练的隐藏层,以便在输入单词出现时预测给定单词出现的概率[2]。该过程可以直观地描述如下。

为 skip-gram 模型生成训练数据的示例。窗口大小为 3。图片由作者提供

如上所述,给定一些文本语料库,在一些滚动窗口上选择目标单词。训练数据由该目标单词和窗口中所有其他单词的成对组合组成。这是神经网络的最终训练数据。一旦训练了模型,我们就可以基本上得出一个单词成为给定目标的上下文单词的概率[2]。下图显示了 skip-gram 模型的神经网络体系结构。

跳格模型架构(图片由作者提供)

语料库可以表示为大小为 N 的向量,其中 N 中的每个元素对应于语料库中的一个单词。在训练过程中,我们有一对目标和上下文单词,输入数组中除目标单词外的所有元素都为 0。目标字将等于 1。隐藏层将学习每个单词的嵌入表示,产生一个 d 维嵌入空间[2]。输出层是具有 softmax 激活功能的密集层。输出层基本上会产生一个与输入大小相同的向量,向量中的每个元素都由一个概率组成。这个概率指示了目标单词和语料库中的关联单词之间的相似性。

它是如何工作的

node2vec 的过程相当简单,首先输入一个图,并从输入图中提取一组随机行走。然后,遍历可以被表示为单词的有向序列,其中每个节点表示一个单词。然后将生成的随机游走传递到 skip-gram 模型中。如上所述,skip-gram 模型作用于单词和句子,随机游走中的每个节点可以表示为一个单词,整个游走可以表示为一个句子。skip-gram 模型的结果产生了每个节点(或者这个类比中的单词)的嵌入。整个过程见下图。

Node2Vec 架构(图片由作者提供)

履行

本文的这一部分将重点关注 Python 中 node2vec 的实现。下面是文章中使用的库和版本。对于这个实现,我们将生成一个随机图,将 node2vec 应用于该图,然后使用 PCA 在一个低维空间中可视化嵌入。如果你想浏览一下这个例子中用到的笔记本,你可以在这里找到它

要求

Python=3.8.8
networkx=2.5
pandas=1.2.4
numpy=1.20.1
matplotlib=3.3.4
node2vec=0.4.4
seaborn=0.11.1
sklearn=0.24.1
gensim=4.0.1

如果你没有安装 node2vec 包,这里的是通过命令行安装它的库文档。

生成网络

上面的脚本将为我们生成一个随机图,以便在上面使用 node2vec。用户将为随机生成的网络指定他们想要的节点数量和度分布。网络将通过配置模型生成。配置模型本质上是通过分配边来匹配度序列来生成随机图。请注意,由于这是随机的,因此每次生成的网络都会不同。此外,这只是一个运行 node2vec 的示例网络,仅仅因为生成的网络是一个多图并不意味着 node2vec 只能在其他多图上运行。Node2Vec 可以在有向、无向、加权、多重或常规网络上运行。当我为n = 1000运行上面的函数时,下面是与结果图相关的统计数据。

与生成的网络相关的统计数据(图片由作者提供)

与生成的网络相关联的度分布(图片由作者提供)

如果你特别想在某个网络上测试这个算法,那么可以随意排除这篇文章的这一部分。在生成的图上应用 node2vec。一般来说,在图形生成之后应该有一个数据预处理阶段,通常如果你的图形真的很大,你可能想要修剪掉任何无用的边/离群值,以使算法更有效一点。

应用节点 2Vec

**Parameter Info** 
- graph: a graph g, where all nodes must be integers or strings 
- dimensions: embedding dimensions (default: 128) 
- walk_length: number of nodes in each walk (default: 80) 
- num_walks: number of walks per node (default: 10) 
- weight_key: the key for the weight attribute on weighted graphs (default: ‘weight’) 
- workers: number of workers for parallel execution (default: 1)
- p: the probability of a random walk getting back to the previous node (default: 1)
- q: probability that a random walk can pass through a previously unseen part of the graph (default: 1)

Node2Vec.fit 方法接受任何可被gensim.Word2Vec接受的关键字参数。上面提到的参数记录在 node2vec 库[3]中。出于本文的目的,我将窗口值设置为 1,min_count 设置为 1,batch_words 设置为 4,dimensions 设置为 16。其余没有提到的参数设置为 word2vec 提供的默认值。请根据您自己的问题随意调整这些参数。

因为这个实现在后端使用 word2vec,所以您能够识别类似于输入节点的其他节点。请记住,输入节点必须以字符串的形式传入,它将以列表的形式按照相似性降序输出与输入节点最相似的前 N 个(默认为 10 个)节点。

与 input_node 1 最相似的节点(图片由作者提供)

转换为数据帧

可视化嵌入

通过 PCA 可视化的 Node2Vec 嵌入。每个点代表原始网络中的一个节点(图片由作者提供)

请注意,就本文的目的而言,这种可视化没有任何意义。这是因为我们已经随机生成了带边的网络。如果您使用代表某个数据集的实际网络,您可以做出一些有趣的观察。在图中,每个单独的点对应于一个节点,根据论文所述,如果节点彼此相似,则它们会靠得更近。这将是一个简单的方法来查看是否有任何集群/社区与您的数据形成。

为什么要用 Node2Vec?

  1. 它易于扩展和并行化
  2. python 和 spark 中的开源
  3. 通过节点嵌入学习特征表示的独特方法
  4. 原始网络的结构通过嵌入得以保留
  5. Node2Vec 有许多实际应用,包括但不限于节点分类、社区检测、链路预测等。

结束语

总的来说,我认为本文的主要收获应该是 node2vec 生成与给定网络中每个节点相关的嵌入。这些嵌入保留了网络的原始结构,因此相似的节点将具有“相似的”嵌入。这是一个开源算法,可以很好地处理更大的网络(只要你有足够的计算能力)。

即使通读了我的文章,我还是鼓励你去阅读原文,并尝试应用它来进行一些分析/解决一些问题。原文可以在这里找到

如果您想了解如何在链接预测和推荐引擎的上下文中使用 Node2Vec,可以在这里参考我的文章:

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

资源

我写的一些其他文章,你可能会喜欢读。

https://pub.towardsai.net/dynamic-time-warping-explained-fbb24c1e079b https://vatsal12-p.medium.com/salary-analysis-comparison-of-the-us-canadian-markets-b6813839ca55 [## 贝叶斯 A/B 测试解释

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

非线性相关矩阵——无人问津的急需技术

原文:https://towardsdatascience.com/non-linear-correlation-matrix-the-much-needed-technique-which-nobody-talks-about-132bc02ce632

为什么过度使用相关矩阵,为什么需要非线性相关矩阵

照片由JJ·乔丹Unsplash 上拍摄

即使有不可计数的数据科学技术和算法,有时你仍然会有缺失某些东西的感觉。一个缺少或很少谈论的这样的东西是非线性相关矩阵。

我们习惯了著名的相关矩阵。然而,在本文中,您将看到相关矩阵可能会导致对数据的错误解释。相关矩阵的局限性证明了需要更复杂的东西,例如非线性相关矩阵。

为了说明这个故事,我将使用一个汽车数据集。这里显示了一个样本数据集。我们将在本视频中使用的数据与汽车相关。这些数据包括汽车的构造、不同的技术特征,如燃料类型、长度、宽度、车门数量等。以及汽车的价格。该数据大约有 25 个字段,其中大约有 15 个数值字段。

汽车样本数据(图片由作者提供)。

注意:数据集引用可在文章末尾找到

相关矩阵…好得令人难以置信

让我们首先从检查相关矩阵开始。数据集有超过 15 个数值字段。衡量这些数值字段之间相关性的相关矩阵如下所示。

相关矩阵(图片由作者提供)

X 轴和 Y 轴对应于数据中的数值字段。每个单元具有指示两个场之间的相关强度的相关系数。相关系数越高,颜色越深。

发动机尺寸与重量的相关系数约为 0.84。这意味着这两个领域之间有很强的正相关性。换句话说,这意味着随着发动机尺寸的增加,重量也线性增加。

现在让我们通过观察两个场之间的散点图来看看这是真是假。

散点图发动机尺寸与重量(图片由作者提供)

只看这些点,我们看到发动机尺寸在 60 到 200 之间,重量有线性增加。然而,在发动机尺寸为 200 之后,重量并不是线性增加,而是持平。因此,这意味着发动机尺寸和重量之间的关系不是严格的线性关系。

我们还可以通过执行线性曲线拟合来确认非线性性质,如下图中的蓝线所示。您将观察到标记在红色圆圈中的点完全偏离了直线,这表明直线不能正确地捕捉模式。

线性曲线拟合(图片作者提供)

我们从观察细胞的颜色开始,这表明有很强的相关性。然而,当我们查看散点图时,我们得出结论,这是不正确的。那么,问题出在哪里?

问题出在技术的名称上。由于它被称为相关矩阵,我们倾向于用它来解释所有类型的相关性。该技术基于皮尔逊相关性,严格地说,它只测量线性相关性。因此,该技术更合适的名称应该是线性相关矩阵。

另一个问题是我们可能错过了一些重要的模式。如果相关矩阵不表示强相关,通常不进行调查。然而,这也可能表示非线性关系或模式。例如,相关矩阵显示月份和温度之间没有相关性。然而,月份和温度之间有很大的关系。

月份与温度(图片由作者提供)

为什么我们真的需要非线性相关矩阵

到现在为止,你一定已经意识到数据中的相关性并不总是严格线性的。为了正确地表示数据中的关系,我们需要一个矩阵来度量数据中的线性和非线性关系。

所以,向非线性相关矩阵问好。

非线性相关矩阵(图片由作者提供)

非线性相关矩阵在 Y 轴上有数值字段对,在 X 轴上有最常见的非线性相关类型。通常出现的不同类型的非线性关系类型是指数递减、指数递增、对数、二阶抛物线和三阶抛物线。这个非线性关系的列表不是穷举的,因为还有其他非线性关系。然而,最常见的是下面显示的象征性的。

让我们放大矩阵,找出前 50 个值。

放大到前 50 个值(图片由作者提供)

我们可以观察到发动机尺寸和重量之间有很强的三阶非线性关系。让我们通过进行曲线拟合来确认。

三次曲线拟合(图片由作者提供)

您可以观察到三次曲线比线性曲线更适合。这证实了发动机尺寸和重量之间的非线性关系。

总之,作为数据科学家,我们应该摆脱对线性相关矩阵的过度依赖。理解不同的非线性模式是正确解释数据的关键。非线性相关矩阵是更全面地确定相关性的好方法。

数据源引用

数据来自 https://archive.ics.uci.edu/ml/datasets/automobile。

Dua d .和 Graff c .(2019 年)。UCI 机器学习知识库[http://archive . ics . UCI . edu/ml]。加州欧文:加州大学信息与计算机科学学院。

额外资源

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

你可以访问我的网站,使非相关矩阵以及其他零编码分析。【https://experiencedatascience.com

订阅,以便在我发布新故事时随时获得通知。

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

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

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

Youtube 频道

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

决策树和随机森林的非线性回归

原文:https://towardsdatascience.com/non-linear-regression-with-decision-trees-and-random-forest-afae406df27d

UCL 数据科学学会研讨会 12b:决策树和随机森林在 Python 中的实现及性能评估

杰里米·毕晓普在 Unsplash 上的照片

今年,作为 UCL 数据科学协会的科学负责人,该协会将在整个学年举办一系列 20 场研讨会,主题包括数据科学家工具包 Python 简介和机器学习方法。每个人的目标是创建一系列的小博客文章,这些文章将概述主要观点,并为任何希望跟进的人提供完整研讨会的链接。所有这些都可以在我们的 GitHub 资源库中找到,并将在全年更新新的研讨会和挑战。

本系列的第十二次研讨会是对高级回归方法的介绍。这之前已经介绍了 Lasso 和 Ridge 回归,但是我们现在将介绍决策树和随机森林回归方法的实现。虽然亮点将在这篇博文中呈现,但完整的研讨会可以在我们的 GitHub 账户上找到。

如果您错过了之前的任何研讨会,可以在这里找到:

回归树

决策树遵循树状结构(因此得名),其中节点代表特定属性,分支代表决策规则,主节点代表结果。这可以想象成类似于我们在高中数学中画的决策树,只是规模要复杂得多。该算法本身的工作原理是根据每个节点的不同属性分割数据,同时试图减少选择度量(通常是基尼指数)。本质上,使用回归树的目的是根据属性分割数据,然后使用这些属性来预测我们的目标变量结果。

使用与研讨会第一部分(建筑能效数据)相同的数据集,我们可以使用 scikit-learn 库实现它们,就像我们对 Ridge and Lasso regression 所做的那样。使用回归树的主要好处是,这些方法能够发现因变量和自变量之间的非线性关系,而以前的方法侧重于线性关系。当涉及到变量之间更复杂的关系时,这通常是有益的,但有时会以过度拟合以及时间和资源为代价!

我们可以像以前一样使用以下符号来拟合模型和数据:

#import the necessary module
from sklearn.tree import DecisionTreeRegressor#create the regression tree
reg_tree = DecisionTreeRegressor(random_state=42)
#fit the regression tree
reg_tree.fit(X_train, y_train)

这里的一个主要区别是,我们不再有每个参数的系数,因为这不再是一个线性模型。这意味着模型可能被描述为一个黑盒(取决于你允许决策树增长到多深),因为我们不一定知道它是如何或者为什么以这种方式工作的。在这种情况下尤其如此,因为我们已经通过不限制它可以创建的深度而给了模型有效的自由支配。这意味着我们可以期望模型能够预测训练集中的所有值:

reg_tree.score(X= X_train, y = y_train)#out:
1.0

我们可以看到这种情况。因此,这可能是训练数据过度拟合的一个例子,由此我们的模型可能在看不见的数据上具有高方差。我们可以这样检查:

tree_predictions = reg_tree.predict(X_test)#R2 score
reg_tree.score(X=X_test, y=y_test)#out:
0.9965565405329665

在这种情况下值得注意的是,模型对未知数据的预测能力仍然存在,这表明在定型数据中看到的关系在未知数据中也非常明显。

在现实和许多其他数据集中,这是非常不可能的情况,因此我们很可能有极端过度拟合。限制这种情况的一种方法是控制决策树可以增长的程度,在这种情况下,可以使用max_depth参数来实现。我们可以这样做:

#create the regression tree
reg_tree_limit= DecisionTreeRegressor(random_state=42,
                                     max_depth = 5)
#fit the regression tree
reg_tree_limit.fit(X_train, y_train)print(reg_tree_limit.score(X= X_train, y = y_train))#out:
0.9911552954578272

模型拟合在训练数据上有所降低,但我们主要感兴趣的是它在看不见的数据上的表现:

print(reg_tree_limit.score(X_test, y_test))#out:
0.9882274019933065

这表明约束模型实际上在看不见的数据上表现更差。

这可以在更多看不见的数据上进一步探索,看看这是否在看不见的数据上的模型可变性方面成立。然而,虽然这是一个相当糟糕的示例,但您可以在这里看到,限制模型的深度已经限制了训练数据的得分(引入了一些偏差),希望减少模型在看不见的数据上的潜在方差。另一个好处是,它还会限制模型实现所需的时间和资源。

可用于训练模型以提高其性能、降低其复杂性或提高其通用性的其他参数包括:

  • 标准:衡量分割质量的标准
  • Splitter:在每个节点进行拆分的策略
  • Max_depth:树的最大深度
  • min_samples_split:拆分内部节点所需的最小样本数
  • min_samples_left:叶节点上所需的最小样本数
  • Max_Features:寻找最佳分割时要考虑的特征数量

这些参数的使用将取决于您的建模目标以及您的应用程序,即您希望将模型限制到什么程度,以及您有多少时间和能力来调整这些参数以提高模型拟合度。

吉利·斯图尔特在 Unsplash 上拍摄的照片

随机森林回归

随机森林回归可能是实现回归树的更好方法,前提是您有资源和时间来运行它。这是因为它是一种集成方法,这意味着它结合了多种不同算法(在这种情况下是决策树)的结果,以创建更准确的预测,并确保没有过度拟合。

其工作方式是限制输入到每个决策树的数据量和变量,这样他们就看不到全部数据。在我们的案例中,考虑到偏差和过度拟合,这有助于确保模型不会过度拟合数据,并且可以在模型中探索每个变量的重要性。当然,过度拟合仍然会发生,但是它试图减少这种可能性,就像单个决策树一样。

因此,这可以如下实现:

#import the necessary library 
from sklearn.ensemble import RandomForestRegressor#create the random forest
reg_random_forest = RandomForestRegressor(n_estimators = 10,
                                          random_state=0)
#fit the regression to the data
reg_random_forest.fit(X_train, y_train.values.reshape(-1))

然而,在我们检查模型在训练和测试数据上的性能之前:

print(reg_random_forest.score(X= X_train, y = y_train))print(reg_random_forest.score(X=X_test, y=y_test))#out:
0.999637869613283
0.9976901385082313

我们在这里可以看到,尽管模型在训练数据上的表现比决策树差,但它在测试数据上的表现实际上优于决策树。这是因为它没有完全学习原始数据(尽管它很接近),这意味着它仍然能够很好地概括。

这个模型的另一个好处是,至少与决策树回归模型相比,它被描述为黑盒的可能性稍微小一些。这是因为它能够告诉我们模型中功能的重要性(它们对模型预测的贡献有多大),虽然决策树可以做到这一点,但随机森林更可靠,因为创建了大量的树(较少基于随机机会)。这些值加起来等于 1,最大的特征告诉我们模型中最重要的变量。我们可以这样实现:

#extract the feature importance
feature_importances = pd.DataFrame(
    {"variables":X_train.columns.values,
     "Importance":reg_random_forest.feature_importances_*100})#sort the values by size
feature_importances.sort_values(by = "Importance",
                               inplace = True,
                               ascending=False)#plot the results
fig, ax = plt.subplots(1,1, figsize = (8,6))feature_importances.plot.bar(x = "variables",
                           y = "Importance", 
                             ax = ax)ax.set_title("Random Forest Variable Importance",
            fontsize = 20,
            pad = 20)
ax.set_ylabel("Percentage (%)",
             fontsize = 20,
             labelpad = 15)
ax.set_xlabel("Variable",
             fontsize = 20,
             labelpad = 15)ax.tick_params(axis = "both",
              which = "both",
              labelsize = 15)

作者图片

这里要考虑的一个重要因素是,尽管线性回归方法表明 X2 不重要,但这个模型表明 X2 非常重要。因此,我们可以假设,这是因为 X2 与我们的目标变量 Y1 具有非线性关系,这是标准线性回归方法所不能捕捉到的。

利用这一点,我们可以执行特征选择来删除不重要的变量。为此,有多种不同的方法,但两种常见的方法包括:

  • 去除贡献小于 1%的变量
  • 移除贡献小于随机变量的变量

最后,与决策树模型一样,我们可以调整参数以提高训练和测试数据的模型性能,这些参数包括:

  • 森林中的树的数量
  • criterion:测量分割质量的功能
  • max_depth:单棵树的最大深度(和我们之前做的一样)
  • min_samples_split:分割内部节点所需的最小样本数

你可以在实践练习册中找到更多信息,可以在这里找到,如果你愿意,你也可以在研讨会旁边提供的问题练习册中挑战自己。

照片由 Irina IriserUnsplash 上拍摄

如果您想了解我们协会的更多信息,请随时关注我们的社交网站:

https://www.facebook.com/ucldata 脸书

insta gram:https://www.instagram.com/ucl.datasci/

领英:https://www.linkedin.com/company/ucldata/

如果你想了解 UCL 数据科学协会和其他优秀作者的最新信息,请随时使用我下面的推荐代码注册 medium:

https://philip-wilkinson.medium.com/membership

或者由我来查看其他商店:

数字双胞胎的非技术性介绍

原文:https://towardsdatascience.com/non-technical-intro-to-digital-twins-d7401b01486

公平和偏见

数字双胞胎的非技术性介绍

理解复杂技术及其伦理的新的和传统的例子

“这幅图涉及自动驾驶汽车、环境传感和神经网络。在汽车中实现的计算机视觉系统需要区分重要的街道标志和环境中其他不重要的元素,如烟囱。”安东·格拉博勒 / 更好的 AI 图像 / 自动驾驶 /由 CC-BY 4.0

一项闪亮的新技术正在席卷整个机构。不,不是区块链、机器学习或 Web3。这是一对数字双胞胎,一个分析、理解、维护和改善物理和社会基础设施的数字解决方案。

现在,我不是最懂技术的人——你在读了一篇关于数据科学的帖子,这可能会让你成为这个房间里最懂技术的人——所以我将在第一部分用两个例子来定义数字双胞胎。在第二节中,我简要描述了数据伦理或现代数据科学伦理的三个常见问题。这些“困惑”与准确性、假设和谦逊有关。在第三部分的第三个方面(谦逊),我建议采用“传统”技术,这也符合我们对“数字双胞胎”(任务控制中心和全球定位系统)的定义。最后,在第一节的定义和第三节提出的例子之后,我讨论了数字孪生的两种类型的“依赖”是可行的。一方面,我引入了介入依赖,即那些被“数字化”的东西——或者说数字双胞胎代表的——被操纵以允许数字双胞胎被创建。另一方面,我将基础设施依赖定义为包括那些首先使数字双胞胎成为可能的技术进步——例如,互联网或大数据分析的最佳实践。

2.什么是数字孪生?

我的第一个数字双胞胎的例子是世界上第一个 3D 打印的不锈钢桥。在这个例子中有两件有趣的事情:3D 打印和数字孪生。通过以创新的方式建造,这座桥的创造者能够安装传感器,“输入智能数字双胞胎,它将从实时数据中学习,包括应变,位移,振动数据和环境因素,如空气质量和温度,这些都是在运营期间从桥上收集的。”因此,数字双胞胎编辑数据并进行分析。复杂的算法可以用来模拟桥梁的潜在风险,维修可以相应地安排,有更多的数据可以利用。例如,想象一下,一辆汽车驶过大桥(考虑到它的尺寸和是行人的事实,我无法想象这是被允许的),并对大桥造成了比设计更大的压力。这将反映在数字双胞胎中,在充分的监控下,可能会有一个工程师团队被派往现场,并制定计划来解决任何可能的损害,并减轻此类事件进一步发生的风险。

其次,考虑一下是一个农场的数字孪生兄弟。再次,特殊的功能被用于测试的数字双胞胎,这个农场是世界上第一个地下农场。在这个农场,“农作物全年都在无农药的环境中生长,由 led 灯、通风设备、除湿机和灌溉箱控制。”因此,数字双胞胎从翻新的地下掩体周围的各种传感器收集详细数据,并允许基于证据的决策。

我邀请你深入研究这两个例子,因为它们很吸引人。(至少,我觉得他们是!)现在,我希望它们已经足够直观,能让你我分享对数字双胞胎的以下基本理解:

数字孪生是一种计算机模型,它通过利用实时数据并允许对其进行分析来反映某种现象,例如物理或农业基础设施。

这个定义至少有四个“抽象层次”(Floridi, 2011 ):

  1. 一个数据库,收集和组织有关现象的数据。
  2. 现象和数据库之间的数据输入。
  3. 一个用于数据分析的界面,因为算法(将人类保持在循环中)将需要为决策者提供有用的信息——无论是工程师、农民还是其他人。
  4. 一个正在被表现的现象(我选择“现象”这个词来捕捉任何物体或事件,因为我不想限制数字双胞胎可能会表现什么)。

通过这样定义技术,我们可以继续分析它可能引发的三个常见的伦理问题。

2.伦理困惑

在这一节中,我简要介绍了数据伦理的三个标准问题,描述了它们与数字双胞胎案例相关的原因,并提到了参考资料以获取更多信息。

2.1.不够准确

按照我们上面对数字孪生兄弟的定义,不可靠性可能来自设计不良的数据库和数据输入。例如,如果数据库不使用适当的元数据处理数据,那么输入数据库的任何数据对人类决策者(以及他们的算法同伴)来说都是无用的。同样,需要对这种现象进行维护——无论是桥梁、农场还是其他什么——以确保事先可能没有计划的新事件(回想一下 3D 打印的桥上意外出现的汽车)能够由没有考虑新参数的数据准确描述(或许在桥的元数据中需要一个“非法装载”标志)。因此,一种人在回路的方法可以检查数字双胞胎的数据和模型。

有用资源:les lie(2019)《准确性、可靠性、安全性和鲁棒性》(第 30–34 页)有助于思考 AI(人工智能)系统变得不准确的不同方式,以及减轻此类风险的工具。(尽管我建议参考莱斯利的指导来讨论任何使用“数据”的研究。)

2.2.未经检验的假设

科学哲学家海伦·朗吉诺( 1990 )建议批判假设应该成为正常科学探究的一部分。数字双胞胎不应该被区别对待。回想一下地下农场的情况,想象一下,例如,传感器被放置在农场周围,只检测的温度和光照水平。对于维护数字双胞胎的数据科学家来说,这些可能是非常有用的指标,他们认为温度和光线是高科技农场产量的关键。但是通风,湿度,甚至运动呢?这些是农民自己可能更了解并认为对决策同样有用的指标。

根据这种说法,假设不必是关于“人类种类”(Hacking, 1996 )的,比如种族或性别,这是一个常见的概念,引发了关于认知和其他偏见的无休止的辩论。更确切地说,我认为任何理论前的或不知情的概念都可能成为必须被挑战的假设,就像朗吉诺所说的那样。为了达到这一效果,数字双胞胎必须通过不同的研究领域获得丰富的信息。

有用的资源: 图灵共享资源包含了一本全面的手册来学习负责任的研究和创新,数据伦理俱乐部和 Jean Goulding Institute 都在研究数据危险标签,这对于仔细检查你自己的假设并在小组中测试它们特别有用(这是个人最喜欢的研究伦理的方法)。

2.3.认知谦逊

认知谦逊相当于——非常粗略地说——对自己的知识保持清晰和诚实。这与想象中的场景有关,数据科学家构建、维护和分析地下农场的数字孪生兄弟,却不知道农民们知道是无价的某些数据。这不是数据科学或任何特定研究领域或专业领域所独有的。我们都会犯认知骄傲的罪(见麦地那, 2013 )。然而,认知谦逊在一个可能会助长其特殊错误的领域变得重要:“新技术偏见”(Elsbach & Stigliani, 2019 )。这种偏见是由于新技术的新颖性,人们更喜欢新技术而不是旧技术。作为一种偏见,它被认为是潜意识的,因此,据称是免费的任何批判性反思。那么,明确指出人工智能不能绝对解决所有问题就成了流言终结者的任务。在这个世界上,大型科技公司可以利用“光环效应”,这种效应来自于将产品或服务称为“人工智能”(实际上并不意味着太多),重要的是,数据科学的产出,尤其是数字双胞胎,不要宣扬新技术的神话。

有用资源:给自己一个自我推销的机会,参见 Kherroubi Garcia ( 2021a2021b )关于重视数据科学中认知谦逊和多样性的文章;但是我也必须链接到有益的和令人大开眼界的 AImyths.org 网站!

3.传统数字双胞胎

早先的两个数字双胞胎的例子——一座人行桥和一个地下农场——让我们就数字双胞胎的简化定义达成一致。然而,谦逊的道德价值要求对新技术有清晰的认识,以减轻光环效应。我想建议两种相对传统的技术,它们可能会挑战任何对数字双胞胎新奇感的无端热情。我注意到我选择这些技术是因为它们是众所周知的,而不是因为我非常了解它们!

第一个例子是数字地图。考虑一下的 OpenStreetMap ,它有效地提供了世界城市景观的数字化版本。其他类似的地图包括谷歌地图Moovit 应用。这些提供了关于城市景观的数据和旅行规划的信息,等等。这种地图之前的设备是“卫星导航系统”,它基于全球定位系统(GPS)技术,帮助司机尽可能高效地导航道路并到达目的地。

更现代的数字地图和传统 GPS 之间的一个显著区别是,我们可以在手机上使用的新应用程序利用实时数据,而卫星导航系统将地图存储在本地。这意味着 satnavs 比 Moovit 等应用程序提供的“数字双胞胎更少”,因为它们更新的频率更低。在计划好的路线上突然发生的导致拥堵的车祸不会反映在传统的卫星导航上,但有望由“个人助理”如苹果的“Siri”通知。

有了这些,我们就有了相当广泛的技术,这些技术(1)利用巨大的数据库,(2)依靠数据反馈来(3)实现关于(4)所代表的现象(城市景观)的决策过程。

数字孪生的第二个例子与 GPS 不无关系。实际上,这是一种更古老的技术或方法,它甚至是 GPS 最初创建所必需的:任务控制中心

任务控制中心是那些巨大的房间,里面有几十名科学家,甚至更多我们在太空旅行电影中看到的计算机屏幕。位于德克萨斯州休斯顿的 NASA 任务控制中心可能是最著名的一个。这些可能被视为数字双胞胎的原因是,它们为地面上的科学家提供了大量关于航天器的数据,以便他们就数千公里外的许多高科技设备做出关键决策。

从发射到火星的火箭,到为地球上的人们提供互联网连接的人造卫星,向控制中心提供尽可能准确的数据至关重要。为了透视它们的复杂性,考虑一下在分析这些遥远的人工制品时所涉及的一些物理限制。例如,来自火星漫游者的数据需要大约 20 个小时才能传回控制中心。而且,出于显而易见的原因,维修人员不能在短时间内被派往外太空(就像他们对阿姆斯特丹大桥的担忧做出回应一样)。

任务控制中心所做的——令人印象深刻——是(1)维护大量数据(2)持续反馈(3)在证据和数据分析的基础上做出决策,大约(4)代表现象(这也是非常先进的技术)。

我必须指出,这两个例子只是当前技术的建议,它们可能被认为是数字双胞胎的早期形式。我真的希望他们提供直观的例子来挑战数字双胞胎的新奇感。我很想知道这两种技术如何(如果)不应该被认为是数字双胞胎,所以请在下面评论你的想法!

4.技术新颖性和依赖性

如果我们同意之前对数字双胞胎的定义,那么数字双胞胎并不新奇的潜在结论应该不会太有争议。建立在这种自我强化的推理之上(这本身就有问题!),我们或许可以更接近数字双胞胎的本质。在这一部分,我想说的是,像大多数新技术一样,由于其他技术的进步,数字双胞胎将变得更加普遍。我称这些其他进步为“依赖”,因为数字双胞胎依赖于它们。数字双胞胎至少有两种依赖类型:介入基础

首先,任务控制中心显示了介入依赖的作用。这种依赖类型被称为干预,因为数字双胞胎依赖于由人类干预形成的现象。它与我们对数字双生子的定义中的第(1)级有关。只有在创造宇宙飞船时考虑到它们的远程控制——先发制人地需要任务控制中心——我们才能创造数字双胞胎!这座特别建造的桥梁和地下农场——世界上第一个此类项目——提供了干预依赖的例子。数字双胞胎依赖于他们代表的现象是为双胞胎的目的而设计的。

其次,数字地图提供了基础设施依赖性的一个例子。被视为数字双胞胎的数字地图只有在卫星图像和互联网的发展下才成为可能。这些技术进步为基础设施创造了条件,"当大规模的技术提供了当地的做法时,这种基础设施就出现了,然后可以以一种自然的、现成的方式加以利用"(Star & Ruhleder, 1996 : 114)。就数字地图而言,基础设施依赖与创建谷歌地图或 Moovit 等服务所必需的开发相关。

考虑到数字双胞胎定义的四个抽象层次,基础设施依赖性构成了数字双胞胎定义的层次(2)到(4)的一部分。没有这些技术,就没有用于数据存储和组织的数据库、数据馈送或数据分析能力。

5.数字双胞胎的新领域

综上所述,我们已经看到了数字孪生兄弟的一个可能的定义和分析数字孪生兄弟的四个抽象层次。我们还看到了数据伦理中的三个重要问题与新技术的关系。不过,出于认知上的谦逊,我认为数字双胞胎可能没有我们想象的那么新奇。

此外,这种分析带来的是数字双胞胎创新的一系列条件——依赖性:干预性和基础设施。假设计算能力和数据科学已经足够先进,可以为数字双胞胎的发展提供最佳基础设施,那么问题就在于干预依赖性:数字双胞胎可以代表什么现象?飞行任务控制中心需要开发航天器;3D 打印桥梁需要一种全新的桥梁建造方法;地下农场依靠现代水培技术。看起来,数字双胞胎依赖于他们所代表的现象的重大改变。

这并不是要贬低数字双胞胎的潜力。更确切地说,是想知道在选择应该制造什么样的数字双胞胎中的时,是否需要一定程度的实用主义。似乎不可能给任何现象“附加”一个数字双胞胎。例如,如果没有极其先进的传感器或以某种方式生活在其中的纳米机器人,人类身体的数字双胞胎就不可能存在——我们可能离实现这一目标不远了。旧建筑的数字复制品也很难部署,因为关于传统建筑的文档非常有限。

首先,对所代表的物理或社会系统的干预程度似乎是必要的。通过重视准确性、拥有检验假设的机制以及接受认知谦逊,我们可以确保创造技术上可行的数字双胞胎。

进一步阅读

除了上面伦理难题部分的链接,我还推荐英国数字建筑中心在 2018 年 12 月开发的双子原则,以及由 Sopra Steria 的研究人员通过研讨会进行的【2021 年分析(我有幸参加了研讨会!).

  • 免责声明
    虽然第一节中的例子是真实的(我在艾伦图灵研究所的研究伦理委员会工作时遇到过),但我觉得它们特别直观,并指出(I)我对这些项目没有任何“内部知识”,(ii)我介绍的场景完全是虚构的,是我自己制作的,以及(iii)这篇文章绝不反映我以前的雇主!

概率分布中变量非线性变化

原文:https://towardsdatascience.com/nonlinear-change-of-variable-in-probability-distributions-585b48e69cb7

用帕万游戏看概率分布中变量的变化

数据科学中的相关性

在数据科学中,概率分布是一个极其重要的课题。概率分布帮助数据科学家找到数据中存在的模式。它们有助于发现异常,生成人工数据,以及用数据做更多的事情。特征变换只是概率分布中变量的变化。因此,掌握概率分布对成为一流的数据科学家非常有帮助。

生日礼物

帕万得到了两份生日礼物——一份来自他的父亲,另一份来自他的母亲。父母都送给他随机数生成器。随机数生成器生成两位小数的随机实数。父亲的礼物选择了 0 到 10 之间的随机数,而母亲的礼物选择了 0 到 100 之间的随机实数。

雷灿Unsplash 上的照片

现在,帕万会带着他父亲的礼物去找他的朋友,让他们选择一个间隔一个单位的范围——例如:1-2,5.5-6.5,等等。其范围包含随机生成的数字的人将是赢家。帕万很乐意主持这个游戏,他的朋友也很乐意玩这个游戏,因为公平。任何一个单位距离的区间都有相同的获胜概率。

Unsplash 上拍摄的

他和他的朋友玩了很多次这个游戏,它开始变得无聊。然后,帕万用他母亲的天赋想出了另一个游戏。他让他的朋友选择一个和以前相似的系列。然后,他将平方范围的两端,并宣布平方范围作为新的范围。现在,他会用他母亲的天赋产生一个 0 到 100 之间的随机数,并宣布平方范围包含随机产生的数的人获胜。他的朋友选择的范围在 0-10 之间,但是他平方得到的新范围在 0-100 之间。这样他利用了母亲的天赋。

第二场比赛一开始对他们来说还算公平。于是,他们开始玩第二个游戏。他们演奏了很多次。玩了一段时间后,帕万和他的朋友们注意到,高端值的范围比低端值的范围更容易赢。与 8-9 和 9-10 这样的范围相比,0-1 和 1-2 这样的范围很少获胜。他们没有预料到游戏在射程方面的偏差,因为它在第一场游戏中运行良好。

注意到对更高值的偏好,帕万和他的朋友们停止了玩第二个随机数生成器游戏,转而去打乒乓球。

伊利亚·巴甫洛夫在 Unsplash 上的照片

好奇的帕万

帕万无法从不公平的第二场比赛中继续前进。晚饭后,他回到自己的房间,开始思考比赛。他躺在床上盯着天花板,一个数学想法出现在他的脑海里——概率分布。他站起来,走到他的学习桌前,开始计算两个游戏的概率分布。

照片由思想目录上的 Unsplash

我确信他完成了两场比赛的数学分析,得出了为什么第一场比赛公平,为什么第二场比赛不公平的结论/证明。他毕竟是个数学迷。我们也自己算出数学分析,看能不能得出类似的结论/证明。

游戏的数学观点

第一场比赛

在第一个游戏中,随机数生成器可以生成 0 到 10 之间的任意数字。所以,在这个范围外概率为零,在这个范围内为非零常数。为了找出常数,我们可以简单地对概率分布进行积分,使其等于 1。然后,我们可以解出这个方程来找出这个常数的值。让我们先做那件事。

概率分布为:
P(X=x) = c ,其中 c 为某常数,X∈【0,10】;0 否则。

对区间 -∞+∞
∫(-∞到+∞)p(x)dx = 1 … (I)上的概率进行积分

区间[0,10]外的概率为 0。于是,(I)变成:
∫(0 到 10)p(x)dx = 1

由于 p(x) 在区间[0,10]中只是一个常数 c
c ∫(0 到 10)dx = 1
或者,10c = 1
所以,c = 1/10

所以,概率分布会是:
P(X=x) = 1/10,x ∈ [0,10];0 否则。… (二)

这是连续均匀分布的一个例子。

现在,为什么在[0,10]中范围 1 的任何区间都有相同的概率?

假设区间为【a,b】。由于区间范围为 1b - a=1

所以,随机数落在区间【a,b】的概率是:
*P(X∈【a,b】)=∫(a 到 b)P(X)dx = 1/10 (b-a)= 1/10 * 1 = 1/10

如果范围长度相同,则概率不取决于范围端点。因此,无论你选择什么样的距离终点,你都有相同的获胜概率。

现在,我们明白并有证据证明为什么第一场比赛是公平的,每个人都喜欢它。

第二场比赛

在第二个博弈中,概率分布也是连续均匀的。唯一不同的是范围。

所以,概率密度函数为:
p(Y=y) = 1/100,y∈ [0,100];0 否则

那么,这个博弈如果有相同的概率密度形式,为什么会有偏差呢?

这是因为他们选择随机变量的方式不同。第一局,帕万的朋友自己选择了随机变量 X 但是第二局,帕万的朋友没有选择随机变量 Y 。他们反而选择了随机变量 X ,而 Y 是通过平方 X 构造的。

所以,关系会是:
Y= X

那么,随机变量 X 的概率密度函数是什么?帕万的朋友只选择了 X ,所以我们会想用 X 来表示第二局的概率密度函数。

这就是概率密度函数的“变量变化”发挥作用的地方。

变量的变化

变量的变化也可以用概率分布来完成。现有的概率分布可能有一些随机变量,我们可能想用其他随机变量来表示它。在这种情况下,我们使用这个概念。变量的变化可以是线性的,也可以是非线性的。变量的线性变化很简单。变量的非线性变化有点不同。这里我们将讨论变量的非线性变化,并从数学上解决第二个博弈例子。

第二局的概率密度函数是:
p(Y=y) = 1/100,Y∈【0,100】;0 否则。… (三)

而它与(II)中概率密度函数的关系是:
Y = X

我们希望用 X 来表示这个概率分布(III ),这样我们就可以看到这个博弈是如何偏向于 X 的。

δx为变量 x 的变化量,δy为变量 y 的相应变化量。

然后,对于小的值δx .,两个坐标系中的概率近似相等

所以,p _ x(x)δxp _ y(y)δy…(IV)

p_xp_y 分别代表随机变量 XY 的概率密度函数。

我们知道 p_y 但是我们不知道 p_x ,我们想知道 p_x ,这样我们就可以看到随机变量 X 的概率密度函数。

(IV)可以改写为:
p _ x(x)p _ y(y)|δy/δx |

我们包括了数量符号,因为概率永远不会是负数。

现在,作为δx→0,δy/δxdy/dxp _ x(x)p _ y(y)| dy/dx |。于是,极限情况变成:
p _ x(x)= p _ y(y)| dy/dx |…(V)

我们得到 y 相对于 x 的关系如下:
y = f(x)= x

所以,(V)可以写成:
p _ x(x)= p _ y(f(x))| f '(x)|…(VI)

(六)给出了概率分布随变量变化的一般表达式。

我们的第二个例子有:
f(x) = xf'(x) = 2x。

所以,(VI)简化为:
p_x(x) = p_y(x )|2x|

p_y 在整个范围【0,100】内是一致的。所以,如果 x ∈ [0,10]p_y(x ) = P(Y=x ) = 1/100。

因此,公式进一步简化为:
p_x(x) = x/50
,即 P(X=x) = x/50 …
(VII)

(VII)根据帕万的朋友们选择的变量,给出我们第二个游戏的概率分布。当对游戏进行这种修改时,均匀概率分布会发生剧烈变化。(VII)清楚地表明游戏在距离端点方面有偏差。难怪,游戏没玩多久吧?

但是,等等!我们来验证一下是不是有效的概率分布。(VII)在整个范围【0,10】内为非负。因此,满足 P(X=x)的非负条件。现在,让我们检查它是否在区间 -∞到+∞中积分为 1。

我们只需要在区间【0,10】内积分,因为其他地方的密度值是 0

∫(0 到 10)x/50dx
= 10/(2 * 50)
= 100/100
= 1

是的,它集成到 1。因此,这是一个有效的概率分布。

第二局概率相对于射程如何变化?

为了找出随机数位于区间 1【a,b】范围内的概率,我们求出概率密度函数在 ab 范围内的定积分。

所以, p = ∫(a 到 b)p(x)dx
= ∫(a 到 b)x/50 dx
= (1/50) * ∫(a 到 b)xdx
=(b-a)/100
=(b-a)(b+a)/100
=(a+1+a)/100
【由于区间为 1】

因此,概率相对于 a 的值呈正线性变化。因此,高端值比低端值更有可能获胜。

我们揭开了不公平游戏背后的神秘面纱!它不感到满足吗?

总结

我们通过两个游戏的例子来探讨变量在概率分布中的变化。变量相对于概率分布的变化是极其重要的,因为它与特征变换有关。特征转换对于构建一个好的模型至关重要。大多数时候,构建一个好模型的诀窍是特性转换,它对构建一个好模型有很大的帮助。我们在这里展示了关于概率分布的“变量变化”的数学。

非线性规划:理论与应用

原文:https://towardsdatascience.com/nonlinear-programming-theory-and-applications-cfe127b6060c

详细解释了基于梯度的线搜索优化算法,并用 Python 从头开始实现

照片由 Unsplash 参观阿拉木图

优化问题通常分为两大类:线性和非线性规划,这是 Luenberger & Ye (2008)的名著的书名。这些类别通过在目标函数或约束中是否存在非线性函数来区分,并导致非常不同的解决方法。在本文中,重点将放在第二类上。

在算法细节之前,我们将通过一些一般的优化概念来了解如何制定一个优化问题。一旦定义了这些概念,我们将深入到凸无约束问题中,在这里我们将看到局部极小的一般理论,并实现四种线搜索算法:最速下降共轭梯度牛顿法拟牛顿法 ( BFGSSR1 )。最后,我们将引入约束,并应用 SLSQP 的实现来解决一个受约束的例子。

对于那些对细节感兴趣的人来说,本文中使用的代码完全可以在这个 GIT 存储库中找到。

优化问题的结构

为了公式化一个优化问题,必须定义一个目标 f ,它是一个向量决策变量 x 的函数,并且可能受到一些等式和不等式约束,这些约束也是 x 的函数。该目标通常在最小化的意义上定义,因此目标是找到其关于约束的最低值。当陈述∈ω时, x 的每个分量的边界在公式中可能是明确的,其中ω是定义决策变量的极限的集合。

一般公式可以表述如下。

最优化问题的一般表述。(图片由作者提供)。

非线性规划有几个应用。一些最常见的是工程设计、控制、数据拟合和经济规划。这些应用通常共享一些关于问题结构的属性,使得凸优化算法非常有效。了解这些属性是什么以及算法将如何解释问题,对于执行优化任务非常有帮助,从制定问题到选择最合适的方法来解决它。因此,首先要做的是理解什么使得一个解最优。让我们在下一节中更好地理解它。

作为一名化学工程师,我创建了一个 jupyter 笔记本,其中有一个在催化反应器中最大化邻二甲苯产量的例子,供那些有兴趣看到一些应用的人使用。

无约束问题:局部最优的条件

在局部优化问题中,我们寻找一个比它的邻居更好的解决方案。在无约束最小化问题中,由于没有不可行规则,我们寻找一个目标值比它的邻居更低的解。定义这种解决方案有两个必要条件(Nocedal & Wright,2006):

  1. 如果 x *** 是局部极小点且 fx*f一定等于零。
  2. 如果目标 f 相对于 x的 Hessian 矩阵存在并且在 x *** 的开邻域内连续,那么矩阵∇ f 一定是半正定的。

更简单地说,目标函数相对于 x 的斜率在局部最优中为零,并且当它改变时,它在任何搜索方向上上升。从视觉上看,就像下图。

局部最优。(图片由作者提供)。

贯穿本文,在例题中,目标函数将被表征为 x 中的二次抛物线函数,定义为。

示例中使用的目标函数。(图片由作者提供)。

从视觉上看,它是这样的。

x 中的目标函数。(图片由作者提供)。

用 Python 代码。

*def obj_fun(x):
    return (x[0] - 0.5) ** 2 + 0.7 * x[0] * x[1] \
        + 1.2 * (x[1] + 0.7) ** 2

def gradient_fun(x):
    return np.array([2 * (x[0] - 0.5) + 0.7 * x[1], \
        0.7 * x[0] + 2 * 1.2 * (x[1] + 0.7)])*

一般框架

基于梯度(或下降)的算法的特征在于迭代过程,其中从初始点开始;根据固定规则确定运动方向;然后在该方向上移动到该线上目标函数的(相对)最小值。在新的点上,确定一个新的方向,并重复这一过程(Luenberger & Ye,2008)。

在最小化问题中,这类似于下坡下降过程,其中重力将物体拉向局部地理最小值。在本文讨论的算法中,目标函数的梯度扮演了重力的角色(或者至少与之类似)。

执行这些迭代有两个基本策略:线搜索信赖域。在某种意义上,线搜索和信赖域方法的不同之处在于它们选择移动到下一次迭代的方向和距离的顺序。线搜索从固定方向开始,然后确定适当的距离。在信赖域中,我们首先选择一个最大距离,即信赖域半径,然后寻找一个方向和步骤,在这个距离约束下获得可能的最佳改进。如果这一步证明是不令人满意的,我们减少距离的措施,并再次尝试(Nocedal &赖特,2006)。在本文中,线搜索方法将被提出,接下来用数学术语描述。

让我们将 x 定义为优化变量的向量,将 p 定义为由某个未知规则定义的搜索方向,将 α 定义为向相对最优值给出令人满意的前进的相对步长。然后,在每次迭代 k 时,我们必须递归地更新当前解,如下式所示。

向搜索方向前进。(图片由作者提供)。

当在两次连续迭代之间,目标函数的改进、在 x 中的位置或者两者都小于用户指定的容差时,算法停止。

如果这个描述让你想起了神经网络优化器,比如随机梯度下降亚当RMSProp ,那就很有意义了。这些算法使用梯度信息在损失函数的最小化意义上更新模型参数。然而,没有线搜索,所以 α 由与学习速率和收敛相关的一些规则来定义。此外,还有一个动量项,它将来自先前搜索方向的信息与每次迭代的当前梯度值相结合,这与共轭梯度有一些相似之处。**

在整篇文章中,我们将基于一个通用框架实现算法: DescentAlgorithm ,用下面的代码表示。

*import numpy as np
from scipy.optimize import minimize, line_search, BFGS, SR1
from numdifftools import Gradient

class DescentAlgorithm:

    def __init__(self, fun, gradient=None, hess=None, nd={},
                 wolfe_c1=1e-4, wolfe_c2=0.1, x_tol=1e-6,
                 f_tol=1e-6, max_iter=50, save_history=False):

        self.fun = fun

        if gradient is None:
            self.gradient = Gradient(fun, **nd)
        else:
            self.gradient = gradient

        self.hess = hess
        self.wolfe_coefs = wolfe_c1, wolfe_c2
        self.x_tol = x_tol
        self.f_tol = f_tol
        self.max_iter = max_iter
        self.save_history = save_history
        self.history = []

    def optimize(self, x0, *args, **kwargs):

        x0 = np.atleast_1d(x0).astype(float)
        self.history = []
        xk = x0.copy()
        fk = self.fun(x0, *args, **kwargs)
        gradk = self.gradient(x0, *args, **kwargs)

        fc, gc = 1, 1

        pk = self.prepare_initial_step(xk, fk, gradk, *args,
                                       **kwargs)

        advance_x, advance_f, advance_max = True, True, True
        k = 0

        if self.save_history:
            self.history.append({"x":xk, "f":fk, "grad":gradk})

        while (advance_x or advance_f) and (k <= self.max_iter):

            alpha, fc_, gc_, fnew, fk, gradnew \
                = line_search(self.fun, self.gradient,
                              xk, sk, gradk, fk, args=args,
                              c1=self.wolfe_coefs[0],
                              c2=self.wolfe_coefs[1],
                              maxiter=15)

            if alpha is None:
                alpha = 1
                fnew = self.fun(xk + alpha * sk, *args, **kwargs)
                gradnew = self.gradient(xk + alpha * sk, *args,
                                        **kwargs)

            xnew = xk + alpha * pk
            fc = fc + fc_
            gc = gc + gc_

            if gradnew is None:
                gradnew = self.gradient(xnew, *args, **kwargs)

            advance_f = abs(fnew - fk) > self.f_tol
            advance_x = np.linalg.norm(xnew - xk) > self.x_tol

            xk, fk, gradk, pk = \
                self.prepare_next_step(xk, fk, gradk, pk,
                                       xnew, fnew, gradnew,
                                       *args, **kwargs)
            k = k + 1

            if self.save_history:
                self.history.append({"x":xk, "f":fk, "grad":gradk})

            if np.linalg.norm(pk) < np.sqrt(np.finfo(float).eps):
                self.message = 'Negligible step'
                self.success = True
                break

        if not (advance_x or advance_f):
            self.success = True
            self.message = 'Tolerance reached'

        elif k > self.max_iter:
            self.success = False
            self.message = 'Max iterations reached'

        self.x = xk
        self.f = fk
        self.grad = gradk
        self.fc = fc
        self.gc = gc
        self.result = {"x":xk, "f":fk, "grad":gradk, "iter":k,
                       "message":self.message,
                       "success":self.success}

    def prepare_next_step(self, xk, fk, gradk, pk, xnew, fnew, 
                          gradnew, *args, **kwargs):
        pass

    def prepare_initial_step(self, xk, fk, gradk, *args, **kwargs):
        pass*

注意,作为一个通用框架, DescentAlgorithm 仍然缺少如何选择搜索方向的实现,这对于每个算法来说都是特定的。此外,如果没有提供梯度函数,来自numdiftools的类梯度将被导入以计算数值导数。

在上面的框架中,scipy . optimize . line _ seach用于计算每次迭代的相对步长。在本文中从头实现它可能太广泛了,但是在下一节中,将介绍线搜索的主要概念。

线搜索

当计算 α 时有一个折衷,因为尽管希望获得搜索方向上的最佳解,但这可能导致大量的函数求值,这通常是不希望的,因为这些函数可能很复杂并且计算代价很高。因此,定义 α 的最常见策略是通过包围和插值迭代,直到满足某些收敛条件。

最常见的收敛条件是沃尔夫条件。首先,让我们定义一个单变量函数 φ ( α ),它根据步长沿着搜索方向计算目标函数。

线搜索功能。(图片由作者提供)。

**Wolfe 条件规定目标函数的值必须小于原点的函数(由参数 c1 控制),目标函数的曲率也必须如此(由参数 c2 控制)。Nocedal & Wright (2006)的建议是对 c1 使用 1e-4,对牛顿拟牛顿方法定义 c2 等于 0.9,而对共轭方向最陡下降为 0.1。

线搜索的 Wolfe 条件。(图片由作者提供)。

从视觉上看,它如下图所示,起点用黑色表示,违反 Wolfe 条件的点用红色表示,适当的步长用绿色表示。

满足 Wolfe 条件的步长选择。(图片由作者提供)。

使用梯度信息:最速下降

最陡下降方向- f 是直线搜索法最明显的搜索方向选择。这是很直观的:在我们可以移动的所有方向中,它是 f 下降最快的方向(Nocedal & Wright,2006)。

因此,让我们将最速下降算法定义为选择一点梯度的负值作为搜索方向的算法。

最陡下降搜索方向。(图片由作者提供)。

*class SteepestDescent(DescentAlgorithm):

    def prepare_next_step(self, xk, fk, gradk, pk, xnew, fnew,
                          gradnew, *args, **kwargs):
        return xnew, fnew, gradnew, -gradnew

    def prepare_initial_step(self, xk, fk, gradk, *args, **kwargs):
        return -gradk*

让我们用我们的框架来解决这个问题。

*steepest = SteepestDescent(obj_fun, gradient=gradient_fun,
                           save_history=True)

steepest.optimize(x0)*

解决方案存储在实例最陡的属性结果中,我们可以访问属性历史中的步骤历史。从视觉上看,达到最佳状态的步骤如下所示。

使用最速下降的优化步骤。(图片由作者提供)。

注意,不一定当前迭代的梯度信息足以将解引向局部最小值。在本例中,该算法经过 13 次迭代才达到 1e-6 的容差。在下一个示例中,我们将通过使用来自先前搜索方向的信息来改进搜索并减少必要的迭代次数,从而使算法收敛得更快。

共轭梯度

共轭方向法可以认为是介于最速下降法和牛顿法之间的一种方法。他们的动机是希望加速与最陡下降相关的典型的缓慢收敛,同时避免与评估相关的信息要求(Luenberger & Ye,2008)。

这些方法是基于纯二次函数设计的,因此,在我们的优化问题中,预期性能会很好。

使用Fletcher–Reeves 方法在迭代 k 时的搜索方向规则如下。

共轭梯度中的搜索方向。(图片由作者提供)。

用 Python 代码,可以做到如下。

*class ConjugateGradient(SteepestDescent):

    def prepare_next_step(self, xk, fk, gradk, pk, xnew, fnew,
                          gradnew, *args, **kwargs):
        return xnew, fnew, gradnew, -gradnew + \
            pk * gradnew.dot(gradnew) / gradk.dot(gradk)*

解决同一个问题,共轭梯度产生了惊人的结果,仅在两次迭代内就收敛到局部最优。这是意料之中的,因为正如我所提到的,算法是基于二次函数的性质设计的,并且,在这些函数中,它被期望在 n 次迭代内收敛,这是 n 个决策变量的数目。

共轭梯度对最速下降优化结果。(图片由作者提供)。

在下一节中,我们将看到,在定义搜索方向时,通过使用曲率信息的力量,事情可以得到更大的改善。

牛顿方法

牛顿方法背后的思想是,被最小化的函数 f 由一个二次函数局部逼近,而这个逼近函数被精确地最小化。因此,在 k 附近,我们可以通过截断的泰勒级数来近似 f(luen Berger&Ye,2008)。

截断的泰勒级数由下面的等式表示。

泰勒级数对函数的截断二次逼近。(图片由作者提供)。

因此,在索引 k 的迭代中,搜索方向 p 定义如下。

牛顿法中的搜索方向。(图片由作者提供)。

在二次函数的情况下,这导致目标函数 f 的精确优化器。

让我们在我们的框架中实现它。

*class Newton(DescentAlgorithm):

    def __init__(self, fun, gradient=None, hess=None, nd={},
                 wolfe_c1=1e-4, wolfe_c2=0.9,
                 x_tol=1e-6, f_tol=1e-6, max_iter=50,
                 save_history=False):

        if hess is None:
            raise TypeError("Must provide hessian")

        super().__init__(fun, gradient=gradient, hess=hess, nd=nd,
                         wolfe_c1=wolfe_c1, wolfe_c2=wolfe_c2,
                         x_tol=x_tol, f_tol=f_tol,
                         max_iter=max_iter,
                         save_history=save_history)

    def prepare_next_step(self, xk, fk, gradk, pk, xnew, fnew,
                          gradnew, *args, **kwargs):
        H = self.hess(xnew, *args, **kwargs)
        return xnew, fnew, gradnew, np.linalg.solve(H, -gradnew)

    def prepare_initial_step(self, xk, fk, gradk, *args, **kwargs):
        H = self.hess(xk, *args, **kwargs)
        return np.linalg.solve(H, -gradk)*

而现在,在解决问题之前,我们必须定义一个函数 x 返回黑森矩阵∇ f ( x )。

*def hess_fun(x):
    return np.array([[2., 0.7],
                    [0.7, 2\. * 1.2]])*

而且真的只需要一次迭代…

牛顿法与共轭梯度优化结果的比较。(图片由作者提供)。

当然,其他目标函数而不是二次函数将需要更多的迭代来优化,但它仍然是一个强大的方法,背后有美丽的数学。

在下一节中,我们将看到一些避免明确计算 Hessian 矩阵的策略。

拟牛顿

拟牛顿法提供了一种替代牛顿法的有吸引力的方法,因为它们不需要 Hessian 计算,但仍能达到超线性收敛速度。代替真正的 Hessian,他们使用一个近似值,它在每一步之后更新,以考虑在该步骤中获得的额外知识。这种更新利用了梯度的变化提供了目标沿搜索方向的二阶导数的信息(Nocedal & Wright,2006)。

在这些方法中,搜索方向的定义几乎与牛顿法中的规则相同。然而,我们将使用近似矩阵 B ,而不是使用真正的 Hessian

拟牛顿法中的搜索方向。(图片由作者提供)。

矩阵 B 由单位矩阵乘以某个常数初始化,然后在每次迭代中递归更新。两种最常见的更新方法是 BFGSSR1 。两者都在 scipy 中使用类 HessianUpdateStrategy 的白色标签结构实现。在我们的实现中,我们将使用 BFGSSR1 ,或者在 scipy 的结构中使用自定义的 HessianUpdateStrategy 。算法如下。

*class QuasiNewton(Newton):

    def __init__(self, fun, gradient=None, hess=None, nd={},
                 wolfe_c1=1e-4, wolfe_c2=0.9,
                 x_tol=1e-6, f_tol=1e-6, max_iter=50,
                 save_history=False):

        if hess is None:
            hess = BFGS(exception_strategy="damp_update",\
                        min_curvature=0.2)

        super().__init__(fun, gradient=gradient, hess=hess, nd=nd,
                         wolfe_c1=wolfe_c1, wolfe_c2=wolfe_c2,
                         x_tol=x_tol, f_tol=f_tol,
                         max_iter=max_iter,
                         save_history=save_history)

    def prepare_next_step(self, xk, fk, gradk, pk, xnew, fnew,
                          gradnew, *args, **kwargs):
        self.hess.update(xnew - xk, gradnew - gradk)
        H = self.hess.get_matrix()
        return xnew, fnew, gradnew, np.linalg.solve(H, -gradnew)

    def prepare_initial_step(self, xk, fk, gradk, *args, **kwargs):
        self.hess.initialize(xk.shape[0], "hess")
        H = self.hess.get_matrix()
        return np.linalg.solve(H, -gradk)*

BFGSSR1 更新的方程式如下所述。

BFGS(上)和 SR1(下)的 Hessian 更新。(图片由作者提供)。

其中, y 对应迭代 kk -1 之间的梯度变化,而 s 对应同样两次迭代之间 x 的变化。

在使用 BFGS 更新的解决方案中,有趣的事情发生了。使用 c2 =0.9 的推荐值,可以接受相对较小的搜索步长,尽管搜索方向指向精确的最小值,但解决方案仍需要一次迭代才能达到。然而,使用更具限制性的值 c2 =0.1,正如预期的那样,它在两次迭代中收敛。这些结果如下所示。

使用 Wolfe 第二条件的两个不同值的 BFGS 的优化结果。(图片由作者提供)。

在理解了无约束优化是如何工作的之后,在下一节中,让我们深入了解一下约束优化

约束最优化和拉格朗日乘子

本节讨论了约束优化的一些理论基础,但是,如果您只对动手感兴趣,我建议您跳过它,直接进入实现示例。

如在公式化一般优化问题时所描述的,有两种可能的约束类型:等式约束和不等式约束。

一个基本的概念,提供了大量的洞察力以及简化所需的理论发展是一个积极的约束。如果不等式约束等于零,则称其在可行点 x 处有效,如果小于零,则称其无效。按照惯例,我们将任何等式约束称为在任何可行点都有效(Luenberger & Ye,2008)。

在约束优化问题中,搜索空间受到给定点 x 处的活动约束的限制。这导致类似于无约束等价的一阶条件。投影在可行搜索空间的切线超平面中的目标函数的梯度在局部最优中必须等于零。在下面的等式中,考虑 h 包括最优中的等式和主动不等式约束,以及y 在切线超平面上的搜索方向。**

约束优化问题局部最优的搜索空间定义。(图片由作者提供)。

所以在局部最小值中,f(x)是主动约束的梯度的线性组合,这就导致了拉格朗日乘子的引入,如此,拉格朗日函数*

约束优化问题的拉格朗日函数。(图片由作者提供)。

其中, λμ 为等式和不等式约束的相应拉格朗日乘子的向量。在这个公式中,不等式表述为g(x)≤0,这就导致了它们对应的拉格朗日乘数 μ ≤0 的条件。因此,对于不活动的不等式约束,通过设置= 0,而对于活动约束g(x)= 0,来实现表示为互补松弛度的条件。**

凸约束优化的一阶和二阶最优性必要条件的类似条件是:

  1. *∇𝓛( x λ μ )相对于 x 必须等于零,并考虑到互补松弛度
  2. *∇ 𝓛( x λ μ )相对于 x 必须是半正定的。

对理论方面感兴趣的,推荐阅读 Nocedal & Wright (2006)和 Luenberger & Ye (2008)的著作。但是,现在让我们深入研究一个应用程序…

一个约束问题和 SLSQP

在这一节中,我们将为我们的优化问题引入一个非线性约束,使得无约束最优不可行。在本例中,它将由 x 中的一个几何圆定义,半径平方为 3,以原点为中心。

例子的不等式约束。(图片由作者提供)。

scipy . optimizeminimize 函数中,不等式必须定义为大于或等于零。因此,示例约束必须按如下方式实现。

**def cons_fun(x):
    return (x ** 2).sum() - 3

def cons_grad(x):
    return 2 * np.array(x)**

现在我们的问题是这样的。

x 中的目标函数和约束区域。(图片由作者提供)。

我们将使用 SLSQPscipy 实现来解决这个问题,该算法使用拉格朗日乘数的信息来定义搜索方向。为此,我们将从 scipy.optimize 中调用最小化函数。

**sol_cons = minimize(
    obj_fun, x0, jac=gradient_fun,
    constraints={"type":"ineq", "fun":cons_fun, "grad":cons_grad},
    method="SLSQP",
)**

解决方案如下所示。

用 SLSQP 和相应的无约束优化解约束问题。(图片由作者提供)。

对于那些对细节感兴趣的人来说,序列最小二乘规划 ( SLSQP )是由 Dieter Kraft (1988)提出的一种算法,它使用原始-对偶策略,通过最小二乘法迭代地解决二次子问题。它与拟牛顿方法有许多相似之处,但是相对于拉格朗日函数Hessian代替目标函数的 Hessian ,并添加约束,通过约束的线性近似来限制可行搜索空间。它使用一种活动集策略,其中新的活动约束或者基于阻塞条件被添加或者基于它们的拉格朗日乘数被移除到活动集,并且步长使用价值函数来定义。序列二次规划的细节也可以在 Boggs & Tolle (1996)和 Nocedal & Wright (2006)找到。

进一步阅读

有些问题需要不同的方法,而不是非线性凸优化。例如,非凸、多模态、不可微和多目标问题提出了一些有趣的挑战。群体和进化计算通常是解决这类问题的有效方法。因此,对于有兴趣探索这些话题的人,我建议做一些关于粒子群优化遗传算法差分进化及其应用的研究。

您可以在下面的文章中找到差分进化的概述以及一些有趣的应用。

**

结论

本文详细阐述了凸非线性优化的相关理论,并用实际的实现例子进行了说明。基于无约束梯度的算法已经从零开始实现,而约束算法的已建立的实现被应用于示例问题。整篇文章中使用的代码完全可以从这个 GIT 库中获得。

参考

博格斯,P. T .和托尔,J. W .,1996 年。序列二次规划。《数字学报》,第 4 卷,第 1–51 页。

H. S .福格勒,1999 年。化学反应工程原理。第三版。上马鞍河(新泽西州):普伦蒂斯霍尔 PTR。

卡夫,d .,1988 年。序列二次规划软件。s . l:Wiss。DFVLR。

Luenberger,D. G. & Ye,y .,2008 .线性与非线性规划。第 3 版。斯坦福:斯普林格。

Nocedal,J. & Wright,S. J .,2006 .数值优化。第 2 版纽约:斯普林格纽约。**

非线性收缩:导论

原文:https://towardsdatascience.com/nonlinear-shrinkage-an-introduction-825316dda5b8

高维统计

高维空间中的最优协方差矩阵估计

统计学、机器学习以及金融和生物学等其他领域的任何应用都需要对协方差矩阵进行精确估计。然而,如今许多这些应用涉及高维数据,因此通常的(样本)协方差估计器不再适用。因此,在过去的二十年里,无数的论文一直试图解决这个问题。这项研究中出现的一个非常有效的方法是“非线性收缩”([1])。在本文中,我将重点放在这种方法的最新版本上,即[2]中的二次逆收缩(QIS ),它使得非线性收缩易于实现并且计算速度快。

让我们从基本介绍开始(如果你已经处理过协方差矩阵,你可以安全地跳过这一节)。

基础知识

协方差矩阵收集两个或多个随机变量的方差和协方差。也就是说,如果我们观察随机变量 X_1、X_2X_3 ,那么协方差矩阵给出为:

由于x1x2x2x1之间的协方差相同,所以矩阵必然是对称的。从线性代数中,我们还知道这个矩阵有一个所谓的特征分解,即,

这里重要的部分是λ矩阵,它是一个包含协方差矩阵特征值的对角矩阵:

因此,我们不仅可以对角化协方差矩阵,而且这些对角元素包含重要信息。例如,他们立即告诉我们协方差矩阵的秩:它只是非零特征值的数量。如果矩阵具有满秩,即如果所有特征值都大于零,则意味着随机变量完全分散在 p 维空间中。这也非常有用,因为

特别地,我们看到协方差矩阵是可逆的当且仅当所有特征值不为零,或者矩阵的秩为 p

每个对称矩阵都可以分解成正交矩阵和一个(实)对角矩阵的乘积。对角矩阵的值揭示了重要的性质,并且可以用来容易地计算逆矩阵(如果它存在的话)。

问题是

假设现在我们有一个样本,样本中有 n 个独立同分布的随机向量,预期值为零(只是为了简单起见)。p×p协方差矩阵可以通过通常的样本协方差矩阵(scm)来估计:

如果数据遵循高斯分布,这就是最大似然估计。这种估计器具有各种有利的特性,特别是当 n 趋于无穷大并且 p 保持不变(或者相对于 n 缓慢增长,使得 p/n 趋于零时,这种估计器会收敛到真实值。

但是,请注意,在上面的矩阵中,我们有个 p(p-1)/2 个元素要估计。 p(p-1) 是从 p 中选择两个元素的方式数,然后我们除以 2,因为矩阵是对称的。在一个 n 的样本中,如果 n 不比 p 大多少,这就会成为一个问题!当 T21 发生时,这是最容易看到的。在这种情况下,scm,一个 p x p 矩阵,可以显示为最多具有秩 n < p 。特别是,即使真正的协方差矩阵是可逆的(=所有特征值都大于零),scm 也会有 p-n 个特征值,因此永远不可逆。因此,如果您需要估计协方差矩阵的逆矩阵,如在线性判别分析或计算高斯密度值时,这是一个问题。

这可能有些极端,但是即使你有 p=n/2 ,scm 的估计误差也可能很大。现在让我们来看一个解决这个问题的强有力的方法。

解决方案

我有点紧张了。当然,这个问题有很多很多的解决方案,根据具体情况,有些方案比其他方案更有效。即使只是改变你衡量成功的方式,也可以改变方法的顺序。

一个可以说是简单的想法导致了无数的研究和应用,那就是将 scm 和单位矩阵进行线性组合:

收缩强度 a 可以以多种方式选择,尽管最常见的是根据理论考虑从数据中自动选择。如果我们考虑单位和 scm 的特征分解,我们已经看到一些奇特的东西:

也就是说,我们只是有了新的特征值,它们是 scm 特征值的一个凸组合和一个凸组合。所以如果第 i 个特征值是 2,那么正则化值现在是 (1-a)2 + a1* 。特别地,由于 scm 的最小特征值是 0,最小正则化特征值现在是 a 。所以只要 a > 0 成立,正则化矩阵就永远是可逆的!L2 正则化回归中的公式可能也是熟悉的:*

在这些收缩方法中,特征向量保持不变但特征值被修改的原则是重要的。这是有意义的,因为当 p 接近或大于 n 时,通常不可能正确估计所有参数。但是 p 本征值是另一回事,并且可能是可以达到的。这是非线性收缩背后的基本思想,它将这一点推进了一步。

估计高维的特征向量是无望的。因此,一个重要的原则是调整特征值,只保留特征向量不变。

在上面的线性收缩中,如果我们选择 a “正确”,当 np 一起趋于无穷大时,我们实际上可以在渐近极限中实现最佳性能。这是在 2004 年的一篇论文([3])中建立的,并且显示了如果估计类是上面的线性估计,则是最优特征值估计。也就是说,当 a 变化时,线性收缩估计量的类别仅仅是形式(1)的所有估计量。

非线性收缩在一个更大的类中导出一个渐近估计量(不需要仅仅是 scm 的线性函数)。特别是,它解决了这个问题

其中 l 是损失函数。换句话说:如果我们只被允许使用 scm 的特征向量,我们正在寻找使结果矩阵尽可能接近真实协方差矩阵的(压缩)特征值的选择!正如我们之前看到的,线性收缩是这种情况的特例,因为我们也只改变那里的特征值。然而,现在我们确定 Lambda 的方法不再局限于线性,提供了更大的灵活性!

有趣的是,上述(无法实现的)最优解非常直观:

其中 u_j 仅仅是之前的样本特征向量:

也就是说,在这种情况下,特征值的最佳解不是真实的,而是我们将 scm 特征向量应用于真实协方差矩阵时获得的值。

对于一系列损失函数,我们能做的最好的事情是估计当样本特征向量应用于真实协方差矩阵时产生的值。

原来非线性收缩法能够在 n,p 一起趋于无穷大时,一致地估计这些最优元素!现在让我们看一个例子。

代码示例

所以非线性收缩理论上很美,但实际上能用吗?这就是最近两篇论文的来源,它们将非线性收缩从一个计算繁重的幻想想法带入了现实世界。特别是“QIS”法(二次逆线性收缩,[2])只用几行代码就能计算出来!但是你甚至不需要这样做,因为所有必要的代码都可以在这里得到。

让我们看一个 R 中的应用程序,使用 qis 函数。在本例中,我们采用一个具有挑战性的真实协方差矩阵

特别是方差为 1,指数 ij 越远,它们之间的协方差越小。因此,当我们向右移动时,协方差矩阵的元素以指数方式接近零,但是它们中的许多仍然是更大的零。我们在这个例子中用 n=800p=1000 ,这样 p > n

library(mvtnorm)
source("qis.R")set.seed(1)n<-800
p<-1000
rho<-0.7# Generate the covariance matrix
Sig<-sapply(1:p, function(i) {sapply(1:p, function(j) rho^{abs(i-j)} ) }  )# Take the eigendecomposition of the true covariance matrix
spectral<-eigen(Sig)# Simulate data
X<-rmvnorm(n = n, sigma = Sig)# Take the eigendecomposition of the scm
samplespectral<-eigen(cov(X))# Use QIS and take the eigendecomposition
Cov_qis <- qis(X)
qisspectral<-eigen(Cov_qis)# Rename
qisspectral$U<-qisspectral$vectors
qisspectral$Lambda<-qisspectral$values# Want u_j'*Sig*u_j for all j=1,...,p
whatwewant<-diag( t(qisspectral$U)%*%Sig%*%qisspectral$U  )#check on first value whether its really calculated correctly
(whatwewant[1]-t(qisspectral$U[,1,drop=F])%*%Sig%*%qisspectral$U[,1,drop=F])

该代码模拟来自 1000 维多元正态分布的数据,并计算样本协方差矩阵及其特征值。请注意,这没有任何问题,尽管这是 scm 的危险之一,但即使在不合适的时候,它也能工作。然后计算 QIS 矩阵以及特征值和特征向量(与 scm 的特征向量相同)。最后,我们计算上面讨论的理论最优解。

让我们做一些策划

plot(sort(samplespectral$values, decreasing=T), type="l", cex=0.8, lwd=1.5, lty=1)
lines(sort(spectral$values, decreasing=T), type="l", col="darkblue", cex=0.8, lwd=1.5, lty=2)
lines(sort(whatwewant, decreasing=T), type="l", col="darkred", cex=0.8, lwd=1.5,, lty=3)
lines(sort(qisspectral$Lambda, decreasing=T), type="l", col="darkgreen", cex=0.8, lwd=1.5, lty=4)legend(500, 20, legend=c("Sample Eigenvalues", "True Eigenvalues", "Attainable Truth", "QIS"),
       col=c("black", "darkblue", "darkred", "darkgreen"), lty=1:4, cex=0.8)

这给了

这个情节为什么有趣?首先,我们看到样本特征值相差很大——它们超过了最大特征值,低估了最小特征值。特别是最后的1000–800 = 200特征值为零。这种“过度分散”在高维中是众所周知的,小的特征值估计得太小,大的特征值估计得太大。另一方面,我们看到非线性收缩的特征值(绿色)非常接近蓝色的真实值。更重要的是,它们非常接近红线,这是上面可达到的真理,即u _ j’ Sig * u _ j!*

实际上,整个矩阵的估计值要高出 30%以上:

((norm(cov(X)-Sig,type="F")-norm(Cov_qis-Sig, type="F"))/norm(Cov_qis-Sig, type="F"))
[1] 0.3088145

根据真实基础协方差矩阵的形式,这种差异也可以大得多。重要的是,当 pn 相比非常小时,QIS 的表现与通常的协方差矩阵估计器大致相同,并且一旦 p 相对于 n 增长,QIS 的表现就会(好得多)。因此,如果 n 足够大( n > 100 ),那么直接使用 QIS 可能会更好!

结论

本文介绍了样本协方差矩阵的非线性收缩方法。在给出了更概念性/数学性的介绍之后,一个小的代码示例说明了 r 中的方法。该方法的代码也可以在 Matlab 和 Python 中找到。

这种方法也有很多实际应用,特别是在金融领域。从你自己的工作中,你知道这在其他应用中有用吗?我总是很高兴听到有趣的用例和数据集。

参考

[1]莱多伊特和沃尔夫(2012 年)。高维协方差矩阵的非线性收缩估计。统计年鉴,40(2):1024–1060。

[2]莱多伊特和沃尔夫(2022 年)。大型协方差矩阵的二次收缩。伯努利,28(3):1519–1547。

[3]莱多伊特和沃尔夫(2004 年)。高维协方差矩阵的一个良态估计。多元分析杂志,88(2):365–411。

非线性时间序列——直观介绍

原文:https://towardsdatascience.com/nonlinear-time-series-an-intuitive-introduction-7390aae8b446

为什么标准的时间序列分析程序有时会失败?

照片由 sebo106 拍摄

注意:本文假设您已经熟悉 SARIMAX 模型。如果你不是,请先检查一下。

介绍

我在学习时间序列分析的时候,很多次听人说“ 如果 AR 模型解决不了,试试 ARIMA ”。

然后我会听到“ ”如果 ARIMA 不工作,检查季节性组件 ”。

这将继续下去:“ 如果萨里玛不起作用,尝试添加新的变量,使其成为萨里玛或 ARDL ”。

当那不行的时候,我听到了“ 协整呢?老实说——在很多情况下,这就足够了。

然而,当你说以上都不起作用时,事情就有点紧张了。这时,即使是最平和、最善良的计量经济学家也会开始紧张,并说:“如果真的什么都不管用,就去做一个天真的预测,或者只是假设平均值”甚至是你听到过的最悲伤的句子:“好吧,那就不可预测了”。

但是是吗?

线性和非线性

首先,值得注意的是,我提到的所有模型都基于线性回归,识别程序基于滞后变量之间的线性关系或 ECM 项。这种方法显然是正确的,但是,在某些情况下,它可能被证明是不完整的。我给你举个简单的例子。我生成了一个时间序列:

生成的时间序列

假设我在做一个标准的 EDA。在某一点上,我必须检查时间序列的自相关性:

对生成的时间序列进行 ACF 测试

看到这张图表后,你的第一个结论是什么?ARIMA 出局了,也许是没有自回归成分的季节性模型?

从我的生活中

在继续之前,我必须承认我骗了你一点——我生成的时间序列是完全确定的,只取决于第一个滞后!事实上,您可以使用以下代码生成完全相同的时间序列:

您现在可能已经注意到,ACF 测试在检测数据之间的真实关系方面失败得很惨。我们从这个测试中发现的唯一或多或少正确的是季节性。为了理解为什么会发生这种情况,看一下这个视频:

但现在,我们来看看它的滞后情节:

具有 AR 线的确定性时间序列的滞后图

红线是适合该数据集的回归线。可以看到,时刻 tt-1 之间的关系是二次的。换句话说,一个线性模型怎么可能预测到它?我向你们展示这一切是因为我想让你们意识到,即使在最友好的非线性环境中,标准的 Box-Jenkins 方法也可能不起作用。

那么如何检验非线性时间序列相关性呢?一种方法(当然不是唯一的,也不是在所有情况下都有效)是使用平均互信息:

确定性时间序列的 AMI

这看起来确实比我们从 ACF 测试中学到的更接近真相。请注意,这里我们也可以找到季节性的迹象。在现实生活中很难找到确定性的时间序列(我猜除了物理学),所以让我们继续讨论非确定性的情况。我们将分析另一个时间序列(人工数据集,用 tsDyn 生成)。其 ACF 和 AMI 图如下所示:

ACF 和 AMI

它的滞后曲线是这样的:

具有 AR 线的非确定性时间序列的滞后图

你能说在这一点上 ARIMA 是否会工作吗?

pmdarima 估计的 ARIMA 模型

根据 ACF 测试——不存在自相关,所以 auto-arima 只拟合截距。通常这意味着,我们的预测不会很好。那我们该怎么办?事实上,有非线性模型可以处理这种时间序列,但我不想让这篇文章太专业,所以我现在不打算解释它的属性(也许在另一篇文章中?).不管怎样,让我比较一下非线性 SETAR 和线性 ARIMA 的结果:

时间序列和样本内预测

这两者之间的差异是显而易见的——线性 ARIMA 无法像 SETAR 一样对这一时间序列建模。由于序列本身不是自相关的,所以检查残差的自相关性是没有意义的,但是,我们能做的是比较初始时间序列中的残差、ARIMA 残差和 SETAR 残差之间的 AMI:

ARIMA 和塞塔模型残差的 AMI

SETAR 残差不再像初始序列和 ARIMA 残差那样在第一次滞后时有尖峰,因此它是更好的模型。在这一点上,你可能会认为,非线性时间序列和线性时间序列之间有明显的区别。错了!虽然存在无法使用 Box-Jenkins 方法处理的情况(如上例),但大多数非线性时间序列乍一看都是线性的。而且,很多时候线性模型的表现要优于非线性模型!这是因为我们在预测时所做的是最小化某个误差函数。根据哪一个是你的标准,你会得到不同的结论。让我给你看另一个非线性时间序列的例子,它比上面两个更容易找到:

滞后图(人工数据集,用 tsDyn 生成)

ACF 测试图(人工数据集,用 tsDyn 生成)

如果你在做分析时看到它,你会说它是线性的还是非线性的?ACF 测试表明这里存在显著的自相关。虽然我可以向你保证,事实上这个时间序列是非线性生成的,但我不能保证 ARIMA 不会做出更好的预测——这取决于满足什么需求。此外,当你处理非线性模型时,你更容易出现错误的识别和过度拟合。

摘要

时间序列中有不同类型的非线性关系,不可能在一篇文章中展示所有这些关系,但我想给你一个简单直观的非线性时间序列介绍,以及为什么现实生活中最流行的方法可能会失败。虽然在大多数情况下,标准 ARIMA 会做得很好,但非线性依赖的世界要复杂得多。

此外,没有一个单一的程序可以处理所有这些问题,因为可能同时发生的非线性的排列数目几乎是无限的。现代贝叶斯、半参数和非参数技术确实在这方面提供了一些帮助——尽管它们肯定不会为我们做所有的工作,但它们可以自动化其中的一些工作,并通过数据驱动的方法覆盖更广泛的可能解决方案。

别忘了跟着我上媒

另外,请随时通过 LinkedIn 联系我。

* https://medium.com/@michacukrowski/lasso-vs-auto-arima-for-time-series-forecasting-out-of-sample-944a8835eae0 https://medium.com/@michacukrowski/ridge-lasso-and-elastic-net-vs-d73e24208057 *

非营利数据管理的 5 个最佳实践

原文:https://towardsdatascience.com/nonprofit-data-management-cb1e4dd2cf0c

插图 AI-由中途使用作者提示的“非营利组织数据提示”生成。根据 Midjourney 的商业使用协议授权。

快速导航:

  1. 避免手动对电子表格进行颜色编码
  2. 不要把数据硬编码到你的网站上
  3. 拥抱关系数据
  4. 包含定性和分类数据
  5. 立志创造&利用合成数据

几年前,当我从通信职业转向数据分析时,我试图留在非营利和社会企业部门。我看到了唾手可得的果实,它可以帮助这些足智多谋、以使命为导向的组织通过制定数据战略来降低开支并最大化影响。然而,当我开始深入研究时,我很快认识到许多非营利组织的数据管理不善,几乎不可能进行任何有意义的分析。俗话说:“垃圾进,垃圾出。”

为了让在非营利部门寻找机会的有抱负的数据科学家和希望建立强大数据战略的社会企业家免受我所忍受的同样的头痛,我开发了 5 个最佳实践。这 5 个最佳实践代表了我在职业生涯中从易到难总结的经验,从两个“不要”,两个“要”和一个“梦想要大!”

#1:避免手动对电子表格进行颜色编码

诚然,我们都做过这样的事情:将单元格的字体颜色设置为红色以标记问题,将单元格突出显示为绿色以表示完成,或者将任何其他可能的彩虹颜色编码到 Excel 或 Google 电子表格中,作为存储和传递信息的快捷方式。虽然颜色编码对于使电子表格可读来说似乎是无害的,但从长远来看,这种技术会导致数据混乱。

为什么混乱?颜色编码数据几乎总是隐式数据。除非您正在跟踪专门关于颜色的数据(例如油漆颜料),否则颜色代码和数据含义之间没有明显的联系。虽然“图例”或“关键”可以帮助澄清含义,但它们经常会丢失或被裁剪掉,并且在以灰度打印时毫无用处。对于复杂的数据管理和分析来说,将彩色编码的数据导入到 R 和 Python 等语言中使用的任何数据结构中是极其乏味的。

作者使用 Microsoft Excel 的插图。

去年,我与一家小型非营利组织签约,针对一场突出的社会危机开展快速拓展活动。他们唯一的员工正在休陪产假,而我则负责挖掘电子表格,以确定在接下来的 48 小时内需要联系哪些联系人。然而,每个电子表格都是彩色代码的万花筒,其中大部分对他们唯一的员工来说意义重大,但我要花很多电话和宝贵的时间才能破译。

通过创建新的显式数据列,我们能够共享数据的含义。显式数据是确切表示其含义的数据。例如,显式数据可以是一个简单表示单词complete的值,或者是一个“complete?”而不是绿色来表示“complete”下面有yesno值的列。显式数据同时具有更好的人类可读性和计算机可读性。例如:一旦我们在快速推广活动中建立了明确的数据,我们就能够使用它来将正确的消息发送给适当的受众。

显式数据开启了条件格式作为颜色编码的可行替代方案的可能性。在 Google SheetsMicrosoft ExcelApple Numbersmore 中可用,条件格式是基于单元格值添加颜色和其他视觉效果的规则的创建。例如,不是手动将单元格涂成绿色来表示一行“完成”,而是将“完成?”列可以用值“是”标记,条件格式编排规则将自动为该行分配绿色。条件格式的优势在于它保留了颜色编码的视觉突出,同时还允许更接近地表示其含义的显式数据。

通过不将数据的含义与任意的颜色方案捆绑在一起,组织数据变得更有弹性,可以处理包括员工流动、软件升级和知识交流在内的挑战。

#2:不要将数据硬编码到网站中

我最近为一个非盈利组织提供咨询,该组织正在从头开始重建他们的网站。该组织的历史可以追溯到一个多世纪前,他们的许多数据都反映了这一遗产,包括重要里程碑的时间表、几十年来有影响力的人物列表、著名的公开声明和媒体档案。然而,所有这些历史数据都是有组织的(有时这些有价值的数据甚至只存在于这个组织越来越过时、漏洞百出的网站上)。硬编码到 HTML 中的数据量不仅阻碍了关键的网站升级,还使得查询数据以进行最基本的分析变得困难。

作者使用良好的老式 HTML 的插图。

为了阐明根本问题,了解技术术语“记录系统”是很有帮助的:信息存储系统是给定数据的权威数据源。在我的客户上面的例子中,个人网页被用作记录系统。这是一个自然的诱惑,因为网站是一种领导层和受众之间的公开对话。随着该组织发现其利基,它用新数据更新网站以回答重要问题:我们与谁相连?我们能做什么?我们在哪里操作?

然而,一般的非营利网站都在努力实现更适合于“参与系统”范式的目标:为用户提供一种可访问的方式来查看、查询和与存储在别处的集中记录系统中的信息进行交互。Web 框架根本不是设计来作为记录系统运行的。

另一方面,数据库管理系统被设计成适合“记录系统”的范例。通过关注数据本身,去除前端视觉考虑,维护良好的数据库自然成为组织最有价值数据的记录系统。此外,组织的网站可以查看、查询或调用大多数数据库管理系统。这意味着数据仍然可以通过网站获得,而不会卡在网站上。尽管建立一个独立于网站的数据库需要一些前期工作,但这样做可以提高数据弹性和网站灵活性。

实现“记录系统”的一个简单的练习是在你的网站上搜索项目符号或聚集有趣数据的表格。将数据创建到一个无代码数据库中,如 Airtable ,并将一个 Airtable“视图”嵌入回你的网站。这种方法的优点是 Airtable 视图允许基于您想要显示的内容进行数据过滤。例如,您可以创建将公共数据与分类数据分开的视图,同时仍然将所有数据方便地收集在一个数据库中!

#3:拥抱关系数据

虽然电子表格对于“快速而肮脏”的数据收集和分析很有用,但一个不断发展的组织将不可避免地发现他们的数据被困在二维电子表格的“监狱栏”中。当跨多个用户部署时,除了对大小和数据完整性的关注之外,电子表格还有一个额外的限制,即每个数据点都被困在一个单独的“单元”中,这使得很难为了批量更新或复杂分析的目的而连接数据。

作者使用 LucidChart 的插图

与关系数据形成对比,关系数据最常见于关系数据库管理系统(RDBMS)。关系数据模型允许通过主键(表中每行的一列唯一 id)和外键(引用“外部”表的主键的一列,允许在当前表的一行中引用另一行的数据)的集合跨表链接数据。

矛盾的是,尽管社会企业本质上是关系型企业,但追求社会公益的组织通常不会为其最关键的数据使用关系型数据模型。这种疏忽可能是因为他们没有意识到关系数据的价值,或者缺乏存储和访问关系数据的技术能力。

对于想要向关系数据飞跃的组织来说,像前面提到的 Airtable 这样的无代码解决方案是一个很好的网关(并且有非营利定价)。有更高级数据需求和更精通技术的非营利组织会发现 SQL 数据库是最佳选择。最后,尽管在技术意义上它们不是“关系型”的,但是像 Neo4j 这样的尖端图形数据库解决方案不仅存储关于相关实体的信息,还存储关于关系本身的信息

在您利用了关系数据的力量之后,即使只是一次,您也一定会看到在任何地方实现关系数据的潜力。以下是我自己职业生涯中的一些例子:

  • **面向青少年的服务项目:我利用关系数据来巧妙地管理学生和他们父母的数据,而不是求助于像“家长 2 号联系信息”这样的笨重的列名,它们会使我们的旧电子表格变得杂乱无章。
  • **政治宣传:我创建了一个关系数据模型,用于权力映射练习,以确定影响关键决策者的最短路径。
  • **野生动物保护网络:为了使我们的目录易于浏览,我使用了一个关系数据库来为濒危物种、潜在捐赠者、全球保护组织和当地野生动物保护区建立不同但相关的表格。

#4:接受定性和分类数据

与营利性组织不同,非营利性组织在一个几乎没有利润空间和价格信号的世界中运作。套用经济学家弗里德里希·哈耶克(Friedrich Hayek)的话,“价格”本身是一个抽象的数据点,它替代了来自全球市场的信息,然后以一种近乎神奇的无形之手的方式协调追求利润的经济活动。因为非营利组织不受利润动机的支配,他们失去了这种由价格决定的方向感,并发现他们的许多项目无法提炼出可量化的底线。为了报告“增长”或其他目标,非营利组织需要用真实世界的数据来填补收入数字留下的真空。

至关重要的是,这些真实世界的数据经常以定性数据的形式出现。定量数据“测量”并基于数字,而定性数据“描述”并通常基于文本。定性数据可以包括利益相关者调查、访谈记录、焦点小组录音和志愿者观察。非营利组织依靠定性数据生存和发展——即使这些“数据”只是在便利贴上流传的涂鸦!

作者插图

令人沮丧的是,资助机构经常要求提供定量数据而不是定性数据的报告,尽管非营利组织可能会有较少的定量数据和大量的定性数据。为自己辩护的是,资助机构确实试图让接受者对他们收到的资金负责。此外,许多基金会官员越来越意识到,光是定量数据就忽略了重要的背景。结合定量和定性数据的混合方法通常是讲述社会影响和变化的最有说服力的方法。

分类数据是定量数据与定性数据的结合点。类别集通常由可用于对记录进行分类的单词或短语组成。例如,志愿者管理数据库可能有一个志愿者“动机”字段,其中的选项包括skill_developmentrequired_service_hourspersonal_storymember_of_group。然后,分类数据可用于问题的子集数据,例如“如果志愿者的动机是技能发展,那么他们平均工作多少小时,而不是需要多少服务时间?”以及更复杂的研究技术,如回归分析

可以对数据进行分类,也可以从数据分析中得出分类。我的团队在纽约召开了一次气候行动组织峰会,会上出现了一个分类的例子。通过对话和越来越杂乱的白板,我们开始确定正在采取的不同类型的气候行动:“宣传”、“碳减排”和“备灾”。然而,这次为期一天的首脑会议不足以完成实际上必须是一个反复的过程。通过后续对话,我们开发了一个受开放编码(数据的自由关联)、轴向编码(将数据分组)和选择性编码(像拼图块一样将组组合在一起)的“基础理论”启发的流程。

根据您试图解决的问题,为您的数据创建可靠的类别可能是一个劳动密集型和智力要求高的过程。如果你的组织真的想接受定性数据,在数据团队中嵌入一个人种学家(如果试图理解一个没有明确解决方案的问题)或一个项目评估者(如果试图证明一个尝试性的解决方案)是值得的。让一名精通“编码类别”的定性数据专家与一名精通“编码计算机”的定量数据专家并肩工作,对于任何寻求通过循证方法解决紧迫社会问题的雄心勃勃的组织来说,都是一记强有力的组合拳。

#5:创建和利用合成数据

合成数据是由算法生成的“假数据”,旨在复制实际数据集。根据算法的复杂程度,合成数据集不仅会复制数据的结构和每个字段的取值范围,还会复制相关性、标准差和其他统计模式。这种额外的复杂程度意味着合成数据集的统计分析将与相应的“真实数据”的统计分析几乎完全相同。

作者使用 Jetbrains Mono 的插图,这是一种专门的编程字体。

合成数据一经创建,就可以通过多种有益的方式加以利用。对于向弱势群体提供服务的非营利组织,合成数据允许通过直接分析服务数据进行项目评估,而不会损害任何个人服务接受者的隐私。虽然非营利组织仍然应该明确某人的数据将如何被使用和共享,但在与外部研究人员和合作伙伴组织合作时,合成数据的高度匿名性质是一种值得保护的措施。

合成数据可以成为将人工智能引入非营利组织运营的战略入口。首先,合成数据几乎总是通过某种形式的人工智能生成的,因此学习如何创建合成数据可以作为人工智能技术和术语的速成班。第二,因为合成数据保护隐私,生成合成数据集为非营利组织提供了在平台上运行自己的“数据科学竞赛”的可能性,如 KaggleDrivenData 。最后,一些人认为,通过用合成数据补充代表不足人口的真实数据,合成数据可以帮助纠正“人工智能偏差”,这样机器就不会将代表不足内在化为“不太可能存在”。虽然很有希望,但请记住人工智能偏见实际上只是通过输入数据教给机器的人类偏见。合成数据可能有助于纠正一些人工智能偏见,但真正的解决方案是在人类源头解决偏见。

合成数据领域正在快速发展,但我发现 Gretel 的平台相对容易学习和使用。值得注意的是,他们专门为非营利组织出版并展示了他们技术的价值,这是数据科学领域非常罕见的营销决策。需要一些技术知识来建立和调整 Gretel 模型:大约相当于一个学期的机器学习课程(相对于一个成熟的计算机科学学位)。根据本文前面分享的其他技巧,值得强调的是 Gretel 可以处理关系数据基于文本的数据(即定性数据)。然而,Gretel 不能处理彩色编码的电子表格,而是针对诸如.csv.json的文件格式进行了优化。

最后的想法

收集、存储和分析数据的成本正呈指数级下降。这些进步也有阴暗的一面,对侵犯隐私和人工智能偏见的担忧日益加剧就是明证。非营利组织有机会驾驭数据浪潮,以便更有效地实现其使命,但需要努力将数据的有用性转化为非营利组织的具体特质,同时不让数据的阴暗面偷偷溜进后门。我的希望是,通过这些非营利数据管理的最佳实践,你能够扩大你的组织的影响,在世界上做更多的好事,同时也将坏事最小化。

也就是说,让非营利组织理解数据是一个持续的过程!在下面的投票中,让我知道你认为哪个建议最有用。您的回答有助于引导我思考是否以及何时更深入地探究这些话题。

如果你喜欢这篇文章,你可能会发现我的使用 Python 的 Airtable 教程很有帮助。欢迎随时关注我这里的媒体推特(我在这里谈论的不仅仅是数据,还有一些数据)。**

如果你想支持更多这样的写作,你可以给我买杯咖啡或者(如果你还没有的话)使用我的推荐链接成为一个媒体订阅者。我自己的数据之旅的一个主要部分是熨斗学校的数据科学项目,如果你是一个寻求提高你的数据技能的非营利专业人士,如果你通过这个推荐链接请求更多信息,它会帮助我

如何将 NumPy 数组规范化为单位向量

原文:https://towardsdatascience.com/normalise-numpy-unit-vector-f9f086134507

用 Python 将 numpy 数组归一化为单位向量

Sebastian SvensonUnsplash 上拍摄的照片

介绍

标准化是一个数据转换过程,发生在模型开发的早期阶段,目的是改变数据数值的分布形状。例如,您可能希望对数字数据点进行规范化,使它们的总和为 1,并被描述为概率分布。

各种机器学习模型受益于数据标准化,特别是那些利用欧几里德距离的模型

在今天的文章中,我们将展示如何将一个 numpy 数组规格化为一个单位向量。这意味着我们将改变向量的大小,使每个向量的长度为 1。

更具体地说,我们将使用以下工具来探索如何做到这一点:

  • scikit-learn图书馆
  • numpylinalg.norm()方法,
  • scipy中的linalg.norm()

首先,让我们创建一个 NumPy 数组,我们将在示例中引用它来演示一些概念。

import numpy as np # Ensure values are reproducible
np.random.seed(1234)array_1d = np.random.rand(15) * 10print(array_1d)
***array([1.9151945 , 6.22108771, 4.37727739, 7.85358584, 7.79975808,
       2.72592605, 2.76464255, 8.01872178, 9.58139354, 8.75932635,
       3.5781727 , 5.00995126, 6.83462935, 7.12702027, 3.70250755])***

使用 scikit-learn normalize()方法

谈到归一化一个 numpy 数组,我们的第一个选择是[sklearn.preprocessing.normalize()](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.normalize.html)方法,该方法可用于将输入向量单独缩放到单位范数(向量长度)。下面分享的例子说明了这一点。

from sklearn.preprocessing import normalize**array_1d_norm = normalize(array_1d[:,np.newaxis], axis=0).ravel()**print(array_1d_norm)
***[0.07945112 0.25807949 0.18158971 0.32580306 0.32357003 0.11308402
 0.11469016 0.33265366 0.39748051 0.36337736 0.14843915 0.20783595
 0.28353203 0.29566176 0.15359713]***

注意,默认情况下,用于标准化输入的标准将被设置为'l2'。这意味着,如果我们对规范化数组的元素求和,我们不应该期望它等于 1。事实上,这里的情况是这样的:

print(sum(array_1d_norm))
***3.578845135327915***

如果你希望得到的向量的和等于 1(概率分布),你应该将'l1'值传递给norm参数:

from sklearn.preprocessing import normalize**array_1d_norm = normalize(
    array_1d[:,np.newaxis], 
    axis=0, 
    norm='l1',
).ravel()**print(array_1d_norm)
***[0.02220021 0.0721125  0.05073975 0.09103581 0.09041186 0.03159791
 0.03204669 0.09295    0.1110639  0.10153481 0.04147683 0.05807347
 0.07922445 0.08261373 0.04291807]***

现在,如果我们对结果数组的值求和,我们应该期望它等于 1:

print(sum(array_1d_norm))
***1***

使用 numpy 和 linalg.norm()方法

我们的另一个选择是 numpy 的[linalg.norm()](https://numpy.org/doc/stable/reference/generated/numpy.linalg.norm.html)方法,它将根据ord参数中指定的值(默认为None)返回八个不同矩阵范数中的一个。

**array_1d_norm = array_1d / np.linalg.norm(array_1d)**print(array_1d_norm)
***[0.07945112 0.25807949 0.18158971 0.32580306 0.32357003 0.11308402
 0.11469016 0.33265366 0.39748051 0.36337736 0.14843915 0.20783595
 0.28353203 0.29566176 0.15359713]***print(sum(array_1d_norm))
***3.578845135327915***

同样,如果你想把这些值加起来等于 1(例如概率分布),你需要指定ord=1

**array_1d_norm = array_1d / np.linalg.norm(array_1d, ord=1)**print(array_1d_norm)
***[0.02220021 0.0721125  0.05073975 0.09103581 0.09041186 0.03159791
 0.03204669 0.09295    0.1110639  0.10153481 0.04147683 0.05807347
 0.07922445 0.08261373 0.04291807]***print(sum(array_1d_norm))
***1***

使用 scipy linalg()方法

或者,你甚至可以使用[scipy.linalg()](https://docs.scipy.org/doc/scipy/tutorial/linalg.html)方法,它本质上包含了与numpylinalg()方法相同的功能,加上一些后者不包含的额外的和更高级的功能。

这里使用的符号与我们在前面的例子中使用的符号完全相同。

from scipy import linalg**array_1d_norm = array_1d / linalg.norm(array_1d, ord=1)**print(array_1d_norm)
***[0.02220021 0.0721125  0.05073975 0.09103581 0.09041186 0.03159791
 0.03204669 0.09295    0.1110639  0.10153481 0.04147683 0.05807347
 0.07922445 0.08261373 0.04291807]***print(sum(array_1d_norm))
***1***

最后的想法

在今天的简短教程中,我们讨论了单位向量的 numpy 数组归一化。数据标准化是改变特定数据集中数值数据点分布的过程。这是在模型开发的早期阶段应用的一个常见的预处理转换步骤。

今天,我们展示了一些不同的方法来归一化 numpy 数组,最终将帮助您的机器学习模型执行得更好。

在我的另一篇关于 Medium 的文章中,您还可以了解到另一个关于数据预处理和转换的类似概念,称为特性缩放

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

你可能也会喜欢

用几行代码规范化任何连续分布的数据

原文:https://towardsdatascience.com/normalize-any-continuously-distributed-data-with-a-couple-of-lines-of-code-5b68454fd2f6

如何使用逆变换采样来改进您的模型

规范化数据是数据科学中的一项常见任务。有时,它允许我们加快梯度下降或提高模型精度,在某些情况下,它绝对至关重要。例如,我在上一篇文章中描述的模型不能处理非正态分布的目标。一些标准化技术,比如取对数,可能在大多数时候都有效,但是在这种情况下,我决定尝试一些对任何数据都有效的技术,不管它最初是如何分布的。下面我将描述的方法是基于逆变换采样:主要思想是基于数据的统计特性构造这样的函数 F,所以 F(x)是正态分布的。下面是怎么做的。

作者图片

我说的算法是基于逆变换采样法。这种方法广泛应用于伪随机数生成器中,从任何给定的分布中生成数字。有了均匀分布的数据,你总是可以用任何给定的累积密度函数(或简称为 CDF)将其转换成分布。CDF 显示了小于给定值的分布数据点的比例,并且基本上表示了分布的所有统计特性。

均值和单位方差为零的正态分布的 CDF。显示几乎所有的点都大于-4 小于 4,其中 50%小于 0(图片作者提供)

主要思想是,对于任何连续分布的数据,xᵢ(cdf(xᵢ)是均匀分布的。换句话说,要得到均匀分布的数据,只需取每个点的 CDF。这种说法的数学证明超出了本文的范围,但是上述操作实际上只是对所有值进行排序,并用每个值的编号来替换它,这给了它一种直观的感觉。

将一些杂乱的数据转换成统一的数据(图片由作者提供)

在上面的 gif 中,你可以看到它是如何工作的。我生成了一些杂乱的分布式数据,然后计算它的 CDF(红线)并用它转换日期。现在数据均匀分布。

计算 CDF 比看起来容易。记住,CDF 是比给定数据小的一部分数据。

def CDF(x, data):  
   return sum(data <= x) / len(data)

值得一提的是,CDF 通常是一个双射函数,这意味着转换是可逆的。我们可以利用这一事实将得到的均匀分布转换成我们想要的任何分布,比如正态分布。为了做到这一点,我们需要计算我们想要得到的分布的逆 CDF。一般来说,这不是最容易的任务。我们需要的函数叫做百分点函数,简称 PPF。对我们来说幸运的是,任何主要发行版的 PPF 都可以通过 SciPy 库访问,不需要自己计算。

均值和单位方差为零的正态分布的 PPF。它显示第 50 百分位是 0,超过 60%的点位于-1 和 1 之间(图片由作者提供)

下面是如何解释它:对于任何介于 0 和 1 之间的参数 x,PPF 返回该点的最大值,以适合第 x 个百分位数。同时,作为 CDF 的反函数,它看起来像第一张图片中的函数,只是旋转了 90 度。

从均匀分布获得正态分布(图片由作者提供)

现在我们有了一个理想的正态分布。最后,要创建一个转换初始数据的函数,我们只需将这两个操作合并成一个函数:

from scipy.stats import normdef normalize(x, data):
   x_uniform = CDF(x, data)
   return norm.ppf(x_uniform)

将初始杂乱分布转换为具有零均值和单位方差的正态分布(图片由作者提供)

上图中的红线代表最终的变换函数。

顺便说一下,我们可以很容易地将数据转换成任何其他分布,只需用一个期望的分布替换 PPF。下面是我们把杂乱的数据转换成对数正态分布。注意变换曲线是如何不同的。

注意最后的变换总是单调的。这意味着在变换后没有两点被交换。如果一个点的初始要素值大于另一个点的初始要素值,则在变换后,该点的变换值也将更大。这一事实允许该算法应用于数据科学任务。

总之,与更常见的方法不同,本文描述的算法不需要任何关于初始分布的假设。同时,输出数据极其精确地遵循正态分布。这种方法已被证明可以提高假设输入数据分布的模型的准确性。比如上一篇文章的贝叶斯模型在没有数据归一化的情况下 R ~ 0.2,在归一化数据的情况下 R 为 0.34。线性模型也显示,在标准化数据上,R 提高了 3-5 个百分点。

就是这样。感谢您的时间,我希望这篇文章对您有用!

标准化组内的特征

原文:https://towardsdatascience.com/normalizing-features-within-groups-9873071f7e60

标准化的另一种方法

照片由菲利普·姆罗兹Unsplash 上拍摄

数字特征最常见的特征工程技术之一是标准化这些特征。这通常通过减去平均值并除以标准偏差或减去最小值并除以最大值和最小值之差来实现。这两种方法都有助于模型的训练和各种特征对模型的影响的评估。此外,无论是使用 scikit-learn 还是直接在 pandas 中执行这些操作都是微不足道的。然而,这些不包含新的信息。

在组内标准化

有一种类似的技术可以将信息添加到特征中,并帮助提高模型的性能。而不是使用全局统计(平均值、标准偏差、最小值、最大值等。),使用较小组内的统计数据。例如,设想围绕客户的信用卡交易构建模型。这些可能是欺诈、信用风险或流失模型。想想单笔交易的金额。这可以基于所有事务的全局统计数据来标准化;然而,可能有更有趣的方法来标准化这些数量。

考虑仅针对客户的支出来规范这一交易。这是花费在平均值附近还是平均值的三个标准差?交易偏离均值三个标准差对欺诈或流失风险意味着什么?此外,考虑在该商家、该邮政编码或该商家类别的所有交易中标准化交易。在珠宝店,一笔交易与客户通常的模式有三个标准偏差,但接近平均值,这与在杂货店同样有三个标准偏差,但也离平均值很远的交易看起来非常不同。

熊猫的分组统计

有几种方法可以计算熊猫的群体内统计数据。最简单的方法之一是使用熊猫功能pivot_table。对于这个例子,使用天气数据,考虑确定对于给定的天气模式,在给定的区域中高温有多不寻常。除了位置( FIPS 联邦信息处理标准县代码),我们的天气数据报告天气图标(是多云,部分多云,晴朗,等等。).要了解阴天的温度是否异常高,我们需要知道该地区阴天温度的平均值和标准差。这可以通过旋转天气图标()来完成,汇总每个位置的高温()(指数)。

pd.pivot_table(weather_df, 
               values='DAILY_HIGH_TEMP', 
               index=['FIPS'],
               columns=['WEATHER_ICON'],
               aggfunc=[np.mean, np.std)

该调用的输出可以加入到 FIPS 上的原始数据集,并使用适当的平均值和标准偏差来计算归一化温度。

云数据仓库中的枢纽

如果数据位于数据仓库中,这种透视和聚合也可以在 SQL 中执行,而无需将数据移动到您的 pandas 环境中。然而,pivot 的 SQL 语法很繁琐,需要识别列中需要提取的每个值。有了开源包 RasgoQL 转换,pivot 转换为你做所有这些。

处理每日天气数据时,可以通过调用rql.dataset来使用这些数据

dataset = rql.dataset('DAILY_WEATHER')

通过调用pivot可以调用 pivot 变换由 FIPS 和 WEATHER_ICON 创建平均高温。在这种情况下,pandas 索引、列和值分别对应于 RasgoQL pivot的维度 value_column 和 pivot_column。

t1 = dataset.pivot(dimensions=['FIPS'],
                   pivot_column='DAILY_HIGH_TEMP',
                   value_column='WEATHER_ICON',
                   agg_method='AVG')

可以通过检查t1.sql()来检查 SQL,并且可以通过调用t1.preview()来检查数据样本。一旦透视看起来正确,就可以通过调用save将其保存到数据仓库中

t1.save(table_name="WEATHER_AVG_HIGH_BY_FIPS")

类似地,可以通过运行另一个transform并将 agg_methodAVG 更改为 STDDEV 来计算标准差。

t2 = dataset.pivot(dimensions=['FIPS'],
                   pivot_column='DAILY_HIGH_TEMP',
                   value_column='WEATHER_ICON',
                   agg_method='STDDEV')
t2.save(table_name="WEATHER_STD_HIGH_BY_FIPS")

这些可以使用这里讨论的连接转换连接在一起。

通过在数据仓库中执行这些转换,避免了在发生特征工程的数据库和服务器之间移动数据所花费的时间。此外,通过处理数据仓库中的数据,可以使用所有数据,而不受服务器上内存量的限制。最后,通过将这些转换作为视图或表保存回数据库,它们可以立即在生产工作流中使用,而不需要数据工程团队对它们进行重构。

如果你想查看 RasgoQL,文档可以在这里这里找到。

并非所有的数据请求都是紧急的,所以从问这 5 个问题开始

原文:https://towardsdatascience.com/not-all-data-requests-are-urgent-so-start-by-asking-these-5-questions-ad77d1fbe7dd

在开始分析数据之前,您应该向每个业务用户提出 5 个问题+ 1 个实际操作练习,以说明实际情况

维克多何Unsplash 上的照片

W 无论是特定的深入分析请求还是广泛使用的仪表盘,数据分析师都会收到或多或少结构化的请求。最终,他们的工作是收集、优先考虑和回答业务用户的需求,不是吗?

那当然是真的。然而,我认为持续处理来自不同利益相关者的请求对数据分析师来说是有代价的。在某些时候人们可能会被收到的请求的数量和多样性所淹没……并在这个过程中迷失。作为一名数据分析师,这些年来我学会了如何更好地组织摆在我面前的数据请求。当有人向我提出数据请求时,我会尝试系统性地后退一步,而不是直接跳入一个据称是“超级紧急”的请求,这个请求会在压力下完成,不到一个月就会被遗忘。在进入真正的分析部分之前,我开始向提问者提 5 个快速问题。

当应用这个框架时,我看到的第一个好处是,它为业务用户和作为数据分析师的我之间的公开对话创造了空间。通过这样做,我们不再是像“我需要你帮我提取这个数字”这样的纯粹的交易关系,而是在为同一目的拥有互补技能的同事之间进行讨论——我们的公司实现其目标和愿景。

在这篇文章中,我想带你了解这五个问题。首先,我将详细说明为什么这些问题在接收数据请求时最为重要。在第二部分,我想带你和我一起参加一个商业利益相关者(在我们的例子中是首席营销官)和数据分析师(在我们的例子中是你自己)之间的现实讨论。

我的清单(图片由作者提供,使用 Excalidraw

1.为什么?

对我来说,两个利益相关者之间的任何互动都应该从一个为什么开始。在构建仪表板的情况下,这是我系统地问的第一个问题。这让每个人都能理解他们互动的原因(我们为什么要开这个会?为什么需要这些数据?等等。以便他们能够有一个良好的开端。在数据请求的情况下,这也会给你动力去处理它,并证明你会为此付出多少努力。

2.为什么?(再次)

为什么另一个为什么?我想在这里提到一个概念,用于找到给定问题的根源。在描述 AWS 架构良好的框架的白皮书中,他们详细描述了应该如何使用“五个为什么技术”。在我们的例子中,不止一次地问为什么有两个好处。首先,它比第一个显而易见的原因走得更远,从而“有助于克服我们可能有的假设”。第二,它对当前用例进行了更多的抽象,因为它为问题提供了更广阔的视角:我们不仅仅是回答您非常具体的需求,而是解决一个可能影响公司内其他利益相关者的问题。

3.是给谁的?

最终,向您索要给定输出(一段代码、一个特别分析、一个仪表板……)的人可能不是唯一使用它的人。首先,你需要了解是否有更多的人支持这个请求。第二,你必须了解相似的人是否可能有相同的需求,无论是现在还是未来。例如,团队领导可能需要一个自己的仪表板,也需要一个所有团队成员的仪表板。他们今天的团队可能需要它,但其他团队领导可能在未来几周需要类似的。

4.什么时候到期?

大多数情况下,请求者会认为他们的请求很快就会到期,如果不是立即到期的话。这是完全正常的,因为他们需要你的输出来继续他们正在进行的主题——顺便说一下,这表明你的工作将为他们带来真正的价值,这是个好消息!通过询问截止日期,你会感觉到请求的紧迫性。这里的理想情况是问一问是否可以区分硬性截止日期和有好处的截止日期。这将允许您在具有不同时间跨度的请求之间管理您的时间。

5.它比其他请求更重要吗?

特别是如果同一个人在几个话题上请求你的支持,这是一个询问他们你应该优先处理他们的好方法。在任何情况下,向请求者提出这个问题都会让他们了解你的日常工作。在大多数情况下,这将增加同理心的水平,并允许请求者和你之间更好的合作。通过与他们分享您的数据请求日志,您将降低因大量请求而产生的压力。

布雷特·乔丹Unsplash 上拍摄的照片

练习时间到了!

让我举一个我自己经历的例子来解释为什么这 5 个问题是帮助你在回答数据请求时更好地管理时间和精力的关键。如果你想“真正地”做情景练习,我邀请你在阅读我自己的(斜体)之前做你自己的解释。

  • 首席营销官(CMO):你能摘录一下我们去年获得的所有客户的名单吗?

我的解释:高层管理人员要求你提取一份客户名单。这可能非常重要,但是您必须问几个问题,以确保您为该请求分配了正确的时间和精力——没有必要直接跳入分析和恐慌!

  • 数据分析师(DA,在这种情况下是你自己):当然,你为什么需要这个列表?
  • CMO:我们想针对他们中的一些人策划一场营销活动。

我的解释:这似乎是一个特定于营销的请求,因此你需要从营销的角度获取数据并展示出来,例如从营销数据库中提取数据,并添加与营销人员特别相关的 KPI。

  • 大卫·爱登堡:好的。你能更详细地告诉我这个活动是为了什么吗?
  • CMO:这仍有待确定,但我们的想法是更好地了解谁是我们最近获得的客户,并确定潜在的客户流失模式,我们可以通过电子邮件活动来解决。

我的解释:更进一步,这指定了提取中所需的数据类型。CMO 间接地向你提供了关于他们所要求的输出的第一手提示:一份包含客户姓名和电子邮件地址的列表(向他们发送一封电子邮件),以及估计客户流失概率的指标(例如,上次登录日期、客户满意度、品牌参与度)。

  • 大卫·爱登堡:你是唯一一个使用这种提取物的人吗,还是会和更多人分享,也许是营销团队的一些成员?
  • CMO:没错。一旦你提取了名单,你可以直接和我以及 CRM 团队分享。

我的解释:在回答完前三个问题后,你已经更好地理解了紧急程度和你未来分析的范围。这是来自 CRM 团队(而不是来自高层管理人员)的请求,您的输出将被几个具有营销背景的人使用(不仅仅是 CMO)。

  • 大卫·爱登堡:你什么时候需要这个提取物?
  • CMO:如果能在周末拿到就好了。然而,下一个电子邮件活动不会在月底前开始,所以我们有时间做相应的准备。

我的解释是:这里你可以看到,这是一个中期请求,而不是一个超级紧急的请求。你甚至可以区分硬性截止日期(月底)和美好的截止日期(周末)。

  • 大卫·爱登堡:我能给你的建议是在这个周末和你分享一份初稿,这样我们就可以在最终版本之前做一些调整。我还有最后一个问题:这个请求与您的 CRM 团队同事上周提出的“客户参与”仪表板的请求有什么关系?
  • CMO:谢谢你提醒我这个请求。事实上,它部分重叠了我自己的要求。然而,我的要求更紧急,我会和 CRM 经理谈谈,看看我们如何结合这两个要求,使它对你最有效。

我的解释是:当你提醒他们你也必须处理的其他请求时,CMO 理解并愿意帮助你管理优先事项。这对你有利:不要犹豫要透明,即使这意味着暴露可能矛盾的请求。

关键要点

在整个讨论过程中,你可以了解更多关于确切的请求及其去向。你明白,尽管是最高管理层成员的要求,但要做的工作不是马上就能完成的。您甚至可以获得关于期望输出的一些初始细节。这将让您有时间组织请求,向 CRM 团队询问更多细节,并在您的时间表中分配时间来及时回答。

现在怎么办?

下次你收到同事的数据请求时,问这五个问题。在您与此人会面并收集了他们的答案后,您肯定会更容易正确地对请求进行优先排序,并给予适当的时间。

在任何情况下,沟通都是关键。这就是为什么上面的 5 个问题是很好的开场白。请随意扩展这个列表,并根据您的具体使用情况和您自己的习惯进行调整!

你喜欢读这篇文章吗? 成为 的一员,加入一个不断成长的充满好奇心的社区吧!

https://marie-lefevre.medium.com/membership

不仅仅是 PyTorch 和 TensorFlow:你应该知道的另外 4 个深度学习库

原文:https://towardsdatascience.com/not-just-pytorch-and-tensorflow-4-other-deep-learning-libraries-you-should-lnow-a72cf8be0814

JAX、MXNet、MATLAB 和 Flux 的快速介绍

加布里埃尔·索尔曼Unsplash 上拍摄的照片

机器学习库加速深度学习革命。他们通过抽象出 GPU 加速、矩阵代数、自动微分等许多高难度的东西,降低了从业者的入门门槛。在行业和学术界,有两个深度学习库占据着至高无上的地位:PyTorch 和 TensorFlow。在本文中,我将向您介绍一些其他具有相当大使用量的深度学习库,或者是因为它们在某些方面实现了加速,或者是因为它们被非常特定的群体使用。我们开始吧!

JAX

是什么?最初由 Google 开发的一个开源和正在开发的数值框架(想想 NumPy,但针对 GPU)。

谁用?谷歌内部的很多团队,比如 DeepMind。

你为什么要知道这件事? JAX 由谷歌开发,用于在 GPU 和谷歌自己的硬件 TPU 上加速数值计算。使用加速线性代数、实时编译(JIT)和自动矢量化等思想,JAX 实现了巨大的加速和扩展。尽管他们的语法相似,以尽量减少学习曲线,JAX 有不同于 NumPy 的设计哲学。JAX 通过vmappmap(向量化+并行化)等函数鼓励函数式编程。

目前,已经为 JAX 开发了许多高级 API。值得注意的是俳句和亚麻。

阿帕奇 MXNet

什么事?开源老牌机器学习框架,前端绑定多种语言,包括 Python、C++、R、Java、Perl。

谁用?亚马逊 AWS。

你为什么要知道这件事? MXNet 最强大的特性是它对多种编程语言的支持以及它的可扩展性。英伟达的基准测试表明,MXNet 在一些深度学习任务上比 PyTorch 和 TensorFlow 更快。

MXNet 附带了 Gluon,这是一个用于构建神经网络的高级 API。它还拥有一个用于图像分类(GluonCV)和 NLP(gluonlp)的生态系统。

MATLAB 深度学习工具箱

什么事?为 MATLAB 用户提供的附加工具箱,可以为各种任务创建和训练神经网络。

谁使用它?学术界和航空航天、机械工程等行业。例如,空客用它来检测飞机内部的缺陷。

你为什么要知道这件事?无论你对 MATLAB 有什么感觉,它仍然是学术界和工程师中流行的编程生态系统。它有很好的用户支持,在我看来,它是这个列表中所有深度学习库中最好的文档。深度学习工具箱面向那些希望使用最少编程来构建系统的人。MATLAB 中的图形编程界面 Simulink 提供了创建易于理解的深度学习管道的方法。

朱莉娅·弗勒斯

什么事?为 Julia 编程语言打造的开源机器学习库。

谁使用它?医药和金融等计算密集型领域。例如,阿斯利康用它来预测药物毒性。

你为什么要知道这件事? Julia 编程语言多年来在数据科学家、定量分析师和生物信息学研究者中获得了发展势头。就速度而言,它与 C/C++不相上下,而且它被设计成像 Python 一样对初学者友好。Julia deep learning 在谷歌 TPU 上的一个实现显示,与 CPU 相比,速度提高了 200 倍。如果你已经在用 Julia 编程,Flux 是一个很好的库。

结论

我希望,通过这篇短文,您可以了解一些其他的深度学习库。它们都支持高效的加速、GPU 扩展和生产部署。互联网上有很好的学习资源。编码快乐!

来源

[1]https://www . deep mind . com/blog/using-jax-to-accelerate-our-research

https://github.com/aws/sagemaker-python-sdk

[3]https://developer . NVIDIA . com/deep-learning-performance-training-inference

[4]https://www . mathworks . com/company/user _ stories/case-studies/airbus-uses-artificial-intelligence-and-deep-learning-for-automatic-defect-detection . html

https://twitter.com/jeffdean/status/1054951415339192321?[5]lang=en

[6]https://julialang.org/blog/2012/02/why-we-created-julia/

[7]https://juliacomputing.com/case-studies/astra-zeneca/

对你的饼状图不满意?尝试这些替代方案

原文:https://towardsdatascience.com/not-satisfied-with-your-pie-chart-try-these-alternatives-84849ef81f50

数据可视化

当饼图似乎不是可视化数据的最佳方式时,该怎么办?

照片由谢里·西尔弗Unsplash 上拍摄

饼图是最直观的图表之一。它们以紧凑的方式显示变量在几个特征之间的分布。实际上,我看到很多同事用饼图展示一些关键数据。虽然它们有时是相关的,饼状图对于某些用例来说绝对不是可视化数据的最佳方式

在这篇文章中,我将以各国的世界人口分布为例。在第一部分中,我想揭示饼图的缺点,以及为什么它们不一定是可视化数据的最佳选择。在第二部分中,我将介绍以更相关的方式显示数据的解决方案。通过这种方式,您可以反思自己对饼图的使用,并且您可以为下一次数据可视化选择最适合的图表类型

让我们从一个例子开始

如何最好地可视化世界人口的分布?

这是我在整篇文章中试图用图表回答的核心问题。假设我想按国家呈现世界人口的分布。这是一个典型的用例,其中显示数据的替代方法——可能是我首先想到的——是饼图。

为了说明我的观点,我将使用《数据世界》中的开放数据。你可以在这里找到源数据集。基于这些数据,我构建了下面的饼状图(图 1)。它显示了 2021 年世界人口按国家的分布情况。

图表 1

饼状图有什么弊端?

你觉得前面的图表有意义吗?它告诉读者什么?老实说,我不确定我应该从中得出什么结论。让我用 3 个问题来解释为什么。

问题 1:很难比较一个地区和另一个地区的大小

在图表 1 中,你可以看到中国的人口比印度多,但这并不明显,是吗?我们假设是这样的。你能从中国人口比印度人口多多少看出吗?

这是饼状图的第一个缺点。虽然他们很快给出了数量级的见解,但很难比较两个表面。大量特征尤其如此。在我们的例子中,大量的国家使得实际上不可能比较大多数国家的人口规模。

问题#2:它不允许显示大量的特征

在图 1 中,您可以看到图例中显示了大量国家(“223 个其他国家”)。但是你能快速说出有多少人住在梵蒂冈吗?在贝宁呢?

为此,您应该增加图例的大小,或者将鼠标移至饼图的每个部分。从这个例子中我们可以看出饼状图并不适合显示大量的特征。

问题#3:它给出了一个发行版的静态概述

在图表 1 中,你可以看到 2021 年世界人口的分布情况。在过去的 10 年里,它是如何发展的?为此,您必须复制相同的饼图并调整数据。在图表 2 中,我展示了 2011 年的世界人口。你能轻易说出 2011 年至 2021 年间哪个国家的人口比另一个国家多吗?

图表 2

由于圆形区域,很难比较饼图的两个表面。对于人眼来说,比较两个方形表面更容易。这就是为什么在比较不同时间段的两个分布时,饼图不是最佳选择。

饼图有哪些替代品?

如果您发现自己面临上述问题,这可能是一个信号,表明饼图不是可视化您的特定用例的数据的最佳方式。你现在应该做什么?尝试以下替代方法。

水平条形图

水平条形图基本上解决了问题#1 和#2。由于数据不受圆形的限制,每个特征(在我们的例子中,每个国家)可以显示在一个水平条中。这让读者能够轻松掌握某个国家的人口数量,并将其与另一个国家进行比较。如果要显示大量国家,水平条的数量会更长,但不会对每个国家数据的可读性产生负面影响。

这是 2021 年世界人口分布在水平条形图中的样子:

图表 3

堆积柱形图

堆积柱形图更能解决问题 1 和问题 3。因为他们也使用方形,所以比较两个国家或两个时间段的表面更容易。由于给定年份的显示更加紧凑,这种类型的图表更适合于比较分布随时间的演变。

您可以使用 100%显示或绝对值显示。在 100%堆积柱形图中,2011 年与 2021 年的世界人口分布如下:

图表 4

结论

饼图并不适合所有的用例。它们的缺点可能会阻止最终用户以可操作的方式解释结果。这就是为什么有两种图形选择可以让您以最合适的方式显示数据:水平条形图和堆积柱形图。

最终不变的是问自己为什么要构建一个给定的图表,最终用户可能从中解读出什么结论。只有当你定义了这些元素,你才能选择最好的图形形式。

你喜欢读这篇文章吗? 成为 的一员,加入一个不断成长的充满好奇心的社区吧!

https://marie-lefevre.medium.com/membership

不那么天真的贝叶斯

原文:https://towardsdatascience.com/not-so-naive-bayes-eb0936fa8b4a

通过释放简单贝叶斯分类器的天真假设来改进它

尽管非常简单,朴素贝叶斯分类器往往在一些现实世界的应用程序中工作得很好,如著名的文档分类或垃圾邮件过滤。它们不需要太多的训练数据,速度非常快。因此,它们经常被用作分类任务的简单基线。许多人不知道的是,我们可以通过一个简单的技巧让他们变得不那么天真。

为什么朴素贝叶斯是贝叶斯?

朴素贝叶斯是一种简单的概率算法,它利用了贝叶斯定理,因此得名。贝叶斯定理是一个简单的数学规则,它告诉我们如何从P(B|A)P(A|B)。如果我们知道某样东西给定另一样东西的概率,我们可以通过下面这个简单的等式来还原它:

贝叶斯定理

如果你需要复习上面的概率符号,不要犹豫,绕道去这篇关于主题的介绍性文章。

朴素贝叶斯算法以非常简单的方式利用贝叶斯定理。它使用训练数据来计算给定目标的每个特征的概率分布,然后,基于定理,它得到相反的结果:给定特征的目标的概率。一旦我们有了特征,这足以预测新数据的分类概率。

实践中的朴素贝叶斯

让我们看看它的实际效果。我们将使用臭名昭著的鸢尾数据集,其中的任务是根据花瓣和萼片的测量结果将花朵分为三个鸢尾种类。为了直观形象,我们将只使用两个特征:萼片长度和花瓣长度。让我们从加载数据开始,并留出一部分数据供以后测试。

现在让我们实现朴素贝叶斯算法。我们将从头开始,而不是使用现成的 scikit-learn 实现,以便我们可以在以后的基础上轻松添加 sklearn 中缺少的功能。

我们将使用 empiricaldist 包来做这件事。这是一个建立在 pandas 之上的很好的小工具,它允许我们轻松地定义和计算概率分布。如果你好奇,我在这里写了更多关于它的内容。

首先,我们需要从一个关于鸢尾属物种的先验信念开始。在我们看到它的尺寸之前,让我们说一朵花同样可能是这三个物种中的任何一个。

>> prior
0    0.333333
1    0.333333
2    0.333333
Name: , dtype: float64

我们将实现朴素贝叶斯分类器的一个流行版本,称为高斯朴素贝叶斯。它假设给定目标的每个特征都是正态分布的,或者在每个目标类中,每个特征都可以用正态分布来描述。我们将从训练数据中估计这些分布的参数:我们只需要按目标对数据进行分组,并计算每个类的两个特征的均值和标准差。这允许我们将每个要素类组合映射到参数化的正态分布。

>> normals
[{0: <scipy.stats._distn_infrastructure.rv_frozen at 0x136d2be20>,
  1: <scipy.stats._distn_infrastructure.rv_frozen at 0x136d07a60>,
  2: <scipy.stats._distn_infrastructure.rv_frozen at 0x136cfe1c0>},
 {0: <scipy.stats._distn_infrastructure.rv_frozen at 0x136d07a90>,
  1: <scipy.stats._distn_infrastructure.rv_frozen at 0x136cda940>,
  2: <scipy.stats._distn_infrastructure.rv_frozen at 0x136be3790>}]

我们有两本词典,每本都有三种正态分布。第一本字典描述每个目标类的萼片长度分布,而第二本字典处理花瓣长度。

我们现在可以定义基于数据更新先验的函数。

update_iris()将先验、特征值和对应于该特征的正态分布字典作为输入,并使用适当的正态分布计算每个类别的可能性。然后,根据贝叶斯公式将先验与似然相乘得到后验概率。

update_naive()迭代我们正在使用的两个特性,并为每个特性运行更新。

我们现在可以迭代测试集,并对所有测试示例进行分类。请注意,我们使用的正态分布的参数是基于训练数据估计的。最后,让我们在测试集上计算准确度。

>> acc
0.9333333333333333

我们的测试准确率达到了 93.3%。为了确保我们得到了正确的算法,让我们将它与 scikit-learn 实现进行比较。

>> acc_sklearn
0.9333333333333333

为什么朴素贝叶斯是朴素的?

朴素贝叶斯假设给定目标的每对特征之间的条件独立性。简而言之,它假设在每一个类中,这些特性彼此不相关。这是一个强有力的假设,也是一个相当天真的假设。想想我们的鸢尾花:期望更大的花同时具有更长的萼片和更长的花瓣并不是不合理的。事实上,在我们的训练数据中,这两个特征之间的相关性为 88%。让我们看看训练数据的散点图。

训练特征之间的相关性。图片由作者提供。

似乎对于三种鸢尾属植物中的两种,花瓣和萼片长度确实表现出很强的相关性。但我们的朴素算法忽略了这种相关性,并将每个特征建模为正态分布,独立于另一个特征。为了使这一概念更加直观,我们来显示要素的这三个正态联合分布的等值线,每个类别一个。

假设独立正态分布。图片由作者提供。

等高线与图的轴对齐,表明这两个特征之间假定缺乏相关性。朴素贝叶斯的天真假设显然不适用于杂色鸢尾海滨鸢尾

让朴素贝叶斯不那么朴素

到目前为止,我们已经假设每个特征是正态分布的,并且我们已经将这些分布的平均值和标准偏差估计为每个类别中相应特征的平均值和标准偏差。这个想法可以简单地扩展到考虑特征之间的相关性。

我们可以用一些正协方差来定义它们的联合分布,而不是为这两个特征中的每一个定义两个独立的正态分布,来表示相关性。我们可以再次使用训练数据协方差作为估计。

>> multi_normals
{0: <scipy.stats._multivariate.multivariate_normal_frozen at 0x1546dd1f0>,
 1: <scipy.stats._multivariate.multivariate_normal_frozen at 0x1546ddaf0>,
 2: <scipy.stats._multivariate.multivariate_normal_frozen at 0x1546dd970>}

上面的代码和之前的非常相似。对于每个类别,我们定义了一个用训练数据的均值和协方差参数化的多元正态分布。

让我们将这些分布的轮廓叠加到散点图上。

这些分布似乎更符合数据。同样,我们可以迭代测试集,并基于给定目标的特征的联合多元正态分布,用新模型对所有测试示例进行分类。注意,这次我们没有用update_naive(),而是直接用update_iris()。唯一的区别是,我们传递给它一个多元法线,而不是用两个独立的单变量法线调用它两次。

>> acc
0.9666666666666667

我们已经设法将精确度提高了 3.3 个百分点。然而,主要的一点是,我们可以摆脱朴素贝叶斯的朴素独立性假设,并希望以一种非常简单的方式使它更好地适应数据。

这种方法在 scikit-learn 中不可用,但是可以随意使用我下面定义的简单实现。

这里是如何使用它。

>> acc
0.9666666666666667

来源

  • 艾伦·b·唐尼认为贝氏。Python 中的贝叶斯统计,第二版,奥赖利,2021
  • Scikit-learn 的关于朴素贝叶斯的文档

如果你喜欢这篇文章,为什么不订阅电子邮件更新我的新文章呢?并且通过 成为媒介会员 ,可以支持我的写作,获得其他作者和我自己的所有故事的无限访问权限。

需要咨询?你可以问我任何事情,也可以在这里 为我预约 1:1

你也可以试试我的其他文章。不能选择?从这些中选择一个:

[## 贝叶斯数据分析最温和的介绍

towardsdatascience.com](/the-gentlest-of-introductions-to-bayesian-data-analysis-74df448da25)

数据科学中的笔记:一个经常被忽视的超级力量

原文:https://towardsdatascience.com/note-taking-in-data-science-a-superpower-often-neglected-410abc40c3e6

罗马卡夫在 Unsplash 上拍摄的照片

在开发数据科学项目时,做笔记是一项经常被忽视的能力,尤其是对初学者而言。在我职业生涯的第一阶段,我犯的一个错误是一头扎进去,开始做模型。

当我们开始对数据建模时,美国数据科学家的肾上腺素激增(至少我是这样的!).但是建模和预测步骤可能会受到 糟糕的项目结构、畸形的假设和数据预处理的影响。这通常是由多种原因造成的:

  • 不完整(甚至缺席)的头脑风暴会议
  • 坏数据(垃圾入,垃圾出)
  • 糟糕的编码实践

还有更多。我的关注者知道我已经写了关于适当地 构建数据科学项目 的文章,并且在该范围内我还提到了 README.md 文件,这也是笔记活动的结果。

我相信这个提到的技能对于项目的成功非常重要。让我告诉你我为什么这么想。

基本上在任何背景下,记笔记就等于和自己来一场头脑风暴。当然,你可以在一个团队中工作,但是目的是一样的。它是一种使思想具体化的方式。它为项目的各个步骤提供了渐进的结构和清晰性,并帮助你理解那些如果你一开始没有写下来就不会如此清晰的事情。新手匆忙做事是因为他们精力充沛,想要完成事情来证明自己——这正是他们犯这么多错误的原因。他们不会放慢速度,把事情写下来。

如果你是一个初学者,那么你会发现放慢速度,集中精力做好笔记会有很大的好处。如果你是一个经验丰富的专业人士,你就会明白为什么我要用一整篇文章来讨论这个话题。我希望你读完它会很开心:)

写下事情是一种超能力

对我来说,在媒介上写作更加巩固了这种信念。能够把你的想法写在纸上是一种该死的超能力,但却被低估了。

为什么?因为把事情写下来会让你思考得更好。它能让你磨砺有用的思想,摆脱无用的思想。

让我们看看这如何影响我们项目的结果。

  1. 你能够在更深的层次上理解问题,而不是简单地跳进编码和注释中
  2. 你能够从不同的角度看待问题,这可能会导致意想不到的结果
  3. 把事情写下来会让有更好的口头交流技巧。在向利益相关者演示时,您将需要它们
  4. 如何有一个心理地图来跟随并达到你的目标——每当你感到分心或注意力不集中时,就回头看看你的笔记。

接下来,我们将看到如何建立一个有趣的过程来有效地记笔记。

我的笔记模板

让我们首先将文件分成几个部分:

  • 我们现在拥有的英特尔 ( 成分)
  • 我们打算如何处理现有数据(配方)
  • 预期产出
  • 考虑和评论

顺序是任意的。如果项目需要创造性的努力,我发现先考虑和评论,然后再考虑其他的是有用的。由你来决定。

佐料

我们把已经拥有的东西放在这里。这不仅仅是关于数据,我们谈论的是围绕数据、环境、客户设定的约束或限制等的英特尔。

对于每条信息,我都用几行来描述它是什么以及假设的用途。这在几个方面帮助了我。通过列出和描述项目,我有了一个更清晰的开始场景。我知道自己拥有什么,知道自己能做什么,不能做什么

这一部分通常在项目的最开始,编码之前填充。如果在开发过程中出现新的信息,我们可以在以后添加它们。

秘诀

这是我准备用现有的原料做什么的地方。它通常包含一系列的动作,一个是另一个所必需的,有点像流程图或者列举的列表。

这些步骤不是一成不变的:如果我们意识到我们需要调整方向或关注项目的其他方面以确保更好的结果,我们可以更新这些步骤并通过描述为什么会发生这种情况来巩固推理。

如果你在一个团队中工作,并计划分发你的笔记,确保你阐明了其他人可能会忘记的细节。

预期产出

在我们开始编码之前,概述你期望从我们的模型/分析中看到什么是非常有用的。这将有助于我们提前发现问题,如果有任何问题的话。您还可以列出一系列满足客户需求的输出。这个选择取决于你。

考虑和评论

纯粹的头脑风暴和意识流。我们提出问题,提出新的想法,并使用我们需要的任何工具来描述我们脑海中尚未与项目结合的东西。

也许我们想用一系列有趣的想法来扩展我们的分析?详细写下这些。

你想问你的客户并澄清某个变量代表什么?把这个作为待办事项写下来,然后打电话给你的客户。

你明白了。

结论

如果你不太想做笔记,那就试一试。你会看到你的推理能力有了巨大的提高,从而你的输出也有了很大的提高。这一流程也适用于其他领域(但流程不同),不仅仅是数据科学。随意试验你自己的结构——这是对我有用的,但对你有用的可能会有所不同。

最后一点,这是几年前启发我开发这个过程的视频。乔治·霍兹是著名的黑客、企业家和编程天才。他的思维方式和组织项目的方式令人着迷。查看他的其他视频,以获得更深入的了解。

关于 Box-Cox 变换的注记

原文:https://towardsdatascience.com/notes-about-the-box-cox-transformations-18d7cd594265

经过一些测试,这里是我能提供的最好的结果

法比安·金特罗在 Unsplash 上的照片

Box-Cox 变换

这篇文章以一个有趣的故事开始。博克斯-考克斯变换是由两位统计学家创造的:乔治博克斯和大卫考克斯爵士。他们曾经见过面,并同意一起写一篇论文,因为他们觉得有趣的是,他们的名字听起来很相似,就像押韵一样。对我们来说幸运的是,它赋予了他们转换数据的公式以生命。

好吧,撇开好奇不谈,Box-Cox 实际上是幂变换,其目标是稳定方差,并使您的数据更像众所周知的钟形正态分布。

Box-Cox 变换会改变数据的形状,使其更接近正态分布。

如果你有兴趣深化你的知识,了解这背后的数学,我在这篇文章的结尾留下了几个链接供参考。

我为什么要在乎?

我一直在研究和学习我能用这个有趣的工具做什么。

相信你知道什么是正态分布,它给我们带来的好处是什么。许多统计测试和理论坚定地依赖于正态分布的假设,其中最著名的是置信区间和假设检验。

因此,如果你有一个非正态分布的数据,你就失去了这些好处。

让我们看看这个例子。我有一个汽车价格数据集(不,不是 mtcars 数据集……)。如果我绘制直方图和 QQ 图,我会发现这不是一条正常曲线。

# Creating a subplot grid
fig , g = plt.subplots(1,2, figsize=(18,6))#Plot 1 = Histogram
g1 = sns.histplot(data=df, x='Price_in_thousands', ax=g[0]);# Plot2 - QQPlot
g2 = scs.probplot(df.Price_in_thousands, dist='norm', plot=plt)

直方图和 QQ 图—价格。图片由作者提供。

现在让我们检查它的统计描述。

df[['Price_in_thousands']].describe().round(2).T

图片由作者提供。

好的。现在,如果这条曲线遵循正态分布,我们可以很容易地看出,68%的值或多或少都在一个标准偏差内。

# Values within + or - 1 std from the mean divided by the number of rowsdf[df['Price_in_thousands'].between( 25.97 - 14.15, 25.97 + 14.15 )]['Price_in_thousands'].count()/ df.shape[0]**[OUT]:**
0.8461538461538461

看吧。我们有 84%的数据在 1 个标准偏差内。但是如果我们对数据使用来自scipy的 Box-Cox 变换,看看接下来会发生什么。还要注意,boxcox()函数返回转换后的数据和lambda数。λ是一个最佳估计值,它使数据成为一个像集合一样的正态分布,这在以后会很有用。

# Import library
import scipy.stats as scs# Transformation Box-Cox
fit_price, lam = scs.boxcox(df['Price_in_thousands'])# Create the plot grid
fig , g = plt.subplots(1,2, figsize=(18,6))# Histogram
g1 = sns.histplot(fit_price, ax=g[0]);# QQPlot
g2 = scs.probplot(fit_price, dist='norm', plot=plt);

转换成类似正态分布的数据。图片由作者提供。

# Stats
pd.DataFrame(fit_price).describe().round(2).T**[OUT]:**
count | mean |  std |  min  | 25%  | 50%  | 75%  | max
117.0 | 1.62 |  0.1 |  1.37 | 1.55 | 1.61 | 1.68 | 1.84

看看现在多好。曲线呈钟形,QQ 图几乎完全位于红线上方。

让我们检查一下 68%的数据是否在平均值的正负 1 个标准差范围内。

# Create a dataframe object with the fitted price
dff = pd.DataFrame(fit_price, columns=['fitted_price'])# Calculate percentage data within 1 std divided by total rows.
dff[dff.fitted_price.between( 1.62  - 0.1, 1.62 + 0.1 )].count()/ dff.shape[0]**[OUT]:**
fitted_price    0.675214 
dtype: float64

哇!就在现场!是 68%。

置信区间

从正态分布来看,计算置信区间变得更加容易。如果我们知道 95%的数据都在 2 个标准偏差以内,那么我们就可以考虑这些范围内的数据。

# Confidence Interval 95%
upper = 1.62 + 0.2
lower = 1.62 - 0.2print(f'The confidence interval for 95% level is between {round(lower,2)} and {upper}.')**[OUT]:** 
The confidence interval for 95% level is between 1.42 and 1.82.

然而,我们不能忘记价值观是变化的。那么,现在怎么办?

来自scipy的另一个好工具是逆 Box-Cox 操作。为了使用它,您必须导入from scipy.special import inv_boxcox。然后,请注意,当我们转换数据时,我们找到了最佳的λ。现在是使用它进行逆运算的时候了。

# Import the inverse Box-Cox
from scipy.special import inv_boxcox# Apply the inversion using the lambda value found on the prior transformation
upper_invert = inv_boxcox(upper, lam)
lower_invert = inv_boxcox(lower, lam)# Print the result
print(f'The confidence interval for 95% level is between {round(lower_invert,2)} and { round(upper_invert,2) }.')**[OUT]:**
The confidence interval for 95% level is between 10.74 and 70.86.

最后的想法

我测试了 Box-Cox 变换的其他用途。例如,如果我们在运行线性回归模型之前将数据转换为正态分布是否有用?

我的测试在那方面没有结论。我发现有时使用它是有用的,但是转换并不是对所有情况都有效。

价格和独立变量之间的线性关系。图片由作者提供。

转换价格和转换自变量之间的线性关系。图片由作者提供。

对于上面的数据集,我创建了两个模型。没有转换数据的第一个仍然比有转换变量的第二个好。

比较模型 1 和模型 2。图片由作者提供。

比较模型 1 和模型 2。图片由作者提供。

我看不出应用这些转换有什么不同。

我相信,当数据呈指数增长时,它可能会更有帮助,而且我们能够通过转换将数据线性化。一些论文说,线性回归的转换——当需要时——必须从因变量开始,如果不够,那么尝试转换自变量。

在你走之前

这些是我关于 Box-Cox 变换的笔记。

  • 它们是功率变换,意味着将有一个功率因数λ应用于数据以对其进行变换。
  • 应用 Box-Cox 后,数据的形状发生了变化。
  • 数据分布将变得近似正常。
  • 你可以利用这一点,比如应用置信区间或假设检验。
  • scipy.stats.boxcox()来改造。
  • scipy.special.inv_boxcox()完成转换的逆过程
  • 我看不出我所做的测试对线性回归有很大的影响。

参考

来自维基百科的 Box-Cox 变换

Scipy 文档 boxcoxinv_boxcox

Box-Cox 定义

Python 中的 Box-Cox

如果这些内容有用,请关注我。

https://gustavorsantos.medium.com/

11 月版:地理空间数据的广阔世界

原文:https://towardsdatascience.com/november-edition-the-wide-world-of-geospatial-data-62f13384237e

月刊

从交互式地图到高级地理参考

陈伶俐·塔克Unsplash 上拍摄的照片

我们并不羞于承认:我们对地理空间数据分析情有独钟。

为什么?让我们数一数方法!它是数据科学的一个子领域,汇集了多种学科和实践,从地理和编程到制图和几何。地理空间项目优雅地连接了数字和物理领域,并帮助我们更好地了解我们居住的世界。他们也有能力将我们今天面临的一些最大的挑战形象化和具体化,从社会不平等到气候变化。(还有,谁不喜欢漂亮的地图呢?)

这个月,我们很高兴地分享了一些最近的帖子,涵盖了广泛的地理空间方法和手段。在我们离开你的阅读之前,我们想感谢我们的社区成员,他们最近成为了中级成员。非常感谢你的支持。

TDS 编辑

TDS 编辑亮点

原始特征

我们最新精选的问答、播客和阅读推荐。

热门帖子

如果你错过了它们,这里有一些上个月在 TDS 上阅读量最大的帖子。

我们的社区很幸运地在 10 月份迎来了一批新的作者——他们包括伊登·佐哈尔卡伦·阿斯马尔弗洛伦特·卡塔尼奥稻夫·杉野埃利亚斯·斯诺拉松贾斯汀·麦基帕萨·萨卡詹姆斯·伊斯贝尔约奥·安东尼奥·索萨 戈登·戴维斯塞巴斯蒂安·佩特里格内马蒂亚·加蒂舒班卡尔·拉瓦特伊多·格林伯格埃里希·恩里克尼科·韦斯特贝克马克斯·塞姆巴莱斯特奥利弗·克莱默瓦拉塔拉贾·瓦西卡拉 如果你也想和我们一起发表你的作品,我们很乐意收到你的来信。

下个月见!

MATLAB 中的数值积分

原文:https://towardsdatascience.com/numerical-integration-in-matlab-f41d187c1c15

用 MATLAB 实现微分方程数值积分的分步方法

Unsplash 上拍摄的 ThisisEngineering RAEng

微分方程描述了许多基本的物理定律。它们被用来模拟火箭的轨迹,估计一种致命疾病的感染率,模拟一个国家的经济增长,等等。它们就在我们身边,如果使用得当,可以产生强大的效果。大多数具有实际应用的微分方程是常微分方程。这些方程由一个或多个函数(及其导数)和一个独立变量组成。大多数 ODEs 中的自变量是时间,或 t 。不幸的是,大多数微分方程太难或者不可能显式求解。要得到一个困难的 ODE 的结果,下一个最好的步骤是数值积分。

数值积分用于近似 ODE 变量随时间的演变。在这篇文章中,我们将集中讨论初值问题。对于这些类型的问题,数值积分需要一个初始时间和初始条件,逐步通过预定义的时间间隔,并在每个时间步长计算变量的解。数值积分的好处是,根据所用的方法,常微分方程可以近似到很高的精度。此外,由于今天可用的大量计算能力,这些方法可以在几秒钟内运行。

MATLAB 有许多数值积分器,由于它的计算能力和相对简单的语法,是许多工程师和物理学家的选择。由于 MATLAB 是由 MathWorks 开发和维护的,他们提供了详细的解释并列出了他们的每个 ODE 解算器。为您的问题选择正确的解算器可能很关键,但是对于大多数应用程序来说,ode45 解算器是一个很好的起点。 ode45 解算器是一个龙格库塔四阶和五阶解算器。我从示例中学习得最好,所以让我们直接进入代码。

先明确一下我们要研究什么常微分方程。最迷人的一组颂歌是洛伦兹系统。这组微分方程由 Edward Lorenz 研究,可用于创建大气对流的简化模型。由于我们将更多地关注如何数值求解常微分方程,如果你想了解模型本身的更多细节,你可以访问上面的维基百科链接。描述洛伦兹系统的常微分方程如下:

这里,我们感兴趣的变量是 xyz 。我们将在后面的代码中定义常数 σ、ρβ 。为了对这些方程进行数值积分,我们需要定义 ode45 函数所需的一切:初始条件、时间间隔和模型。我们的初始条件为下面的形式 Y (我们的结果也将采用这种形式),模型为 Y 向量的时间导数(或洛伦兹系统 ODEs):

让我们通过将模型(或 YdYdt 的时间导数)定义为一个 MATLAB 函数来开始代码。下面的代码显示了这一点。我们首先从传递给模型Y 向量(不要与变量 y 混淆)中提取 xyz 。接下来,我们定义将由该函数返回的时间导数向量 dYdt 。这些是由洛伦兹系统定义的 ode。这里我们将 σ、ρβ 分别定义为 10、20 和 1.5。

% ODEs or Model
function dYdt = model(t, Y)
    x = Y(1);
    y = Y(2);
    z = Y(3); dYdt = zeros(size(Y));
    dYdt(1) = 10*(y-x);   % dxdt
    dYdt(2) = x*(20-z)-y; % dydt
    dYdt(3) = x*y-1.5*z;  % dzdt
end

MATLAB 函数必须在代码末尾定义,所以下面的代码必须放在脚本的开头否则 MATLAB 会对你不满意。首先,按照标准做法,我们清除命令窗口,清除变量,并关闭所有图形窗口。这允许我们在运行脚本的其余部分之前从头开始。接下来,我们将定义 ode45 的另外两个重要输入,积分的初始条件和时间跨度。在这个例子中,这两个都是任意选择的,但是对于你的问题,你可能有特定的初始条件和你感兴趣的时间跨度。

clc
clear variables
close all% Initial Conditions and Time Span
Y0 = [1; 0; 0]; % [x; y; z]
tspan = [0 50]; % [Time_Start Time_Final]

下一步就是简单地将我们的 MATLAB 函数、感兴趣的时间跨度以及我们已经定义的初始条件传递给 ode45 。我们还可以为 ode45 定义一个选项opts ,这将确保我们的解决方案精确到您想要的程度。这里,我们使用 odeset 将解决方案的相对容差设置得很低。对于你的具体问题,你可能不需要那么低的成本,所以确定什么是最适合你的。用这些参数运行 ode45 后,可以提取 xyz 的解。

% Numerically Integrating
opts = odeset('RelTol', 1e-13);
[t, Y] = ode45(@model, tspan, Y0, opts);
x = Y(:, 1);
y = Y(:, 2);
z = Y(:, 3);

最后,您可以通过使用 plot3 函数,从数值积分中绘制出 xyz 的结果(对于本例,因为我们有三个变量)。

% Displaying Results
figure; hold on
plot3(x, y, z, 'b')
xlabel('x')
ylabel('y')
zlabel('z')
view(45, 45)
grid minor

如果一切顺利,你会从数值积分中得到下面的洛伦兹系统。

洛伦兹系统数值积分结果[由作者创建]

这就是使用 MATLAB 对常微分方程进行数值积分所需的全部代码。一旦你知道如何做,MATLAB 中强大的 ODE 解算器库使这个过程变得相对容易。如果您对 Python 中的数值积分感兴趣,我鼓励您阅读这篇文章:

您还可以学习使用 MATLAB 中的 Runge Kutta 方法创建自己的数值积分器:

感谢您的阅读!如果你需要帮助实现这一点,请留下评论。我很乐意帮忙。如果您对其他编码、工程或空间相关的文章感兴趣,请给我一个关注!

基于 L-BFGS 方法的数值优化

原文:https://towardsdatascience.com/numerical-optimization-based-on-the-l-bfgs-method-f6582135b0ca

我们将使用 Rosenbrock 函数的一个最小化示例来检验有限内存的 Broyden、Fletcher、Goldfarb 和 Shanno (L-BFGS)优化方法。此外,我们将比较 L-BFGS 方法和梯度下降法的性能。L-BFGS 方法以及其他几个数值优化例程是机器学习的核心。

介绍

最优化问题旨在找到给定目标函数的最小值或最大值。最优化问题有两种确定性方法——一阶导数法(如梯度下降法、最速下降法)和二阶导数法(如牛顿法)。一阶导数方法依赖于沿着下坡/上坡的导数(或梯度)来寻找函数的最大值(最优解)。基于导数(Hessian,包含二阶导数的矩阵)的导数的二阶导数方法可以更有效地估计目标函数的最小值。这是因为二阶导数给了我们走向最优解的方向和所需的步长。

L-BFGS 方法是一种二阶优化算法,属于一类拟牛顿方法。对于不能直接计算的问题,它近似于二阶导数。牛顿法使用海森矩阵(因为它是二阶导数法)。然而,它有一个限制,因为它需要计算 Hessian 的逆运算,这可能是计算密集型的。拟牛顿法使用梯度近似 Hessian 的逆,因此在计算上是可行的。

BFGS 方法(L-BFGS 是 BFGS 的扩展)在每次迭代中更新海森矩阵的计算,而不是重新计算它。然而,Hessian 及其逆的大小取决于目标函数的输入参数的数量。因此,对于一个大问题来说,粗麻布的尺寸可能是一个需要处理的问题。L-BFGS 通过假设前一次迭代中的 Hessian 逆的简化来解决这个问题。与基于梯度的完整历史的 BFGS 不同,L-BFGS 基于最近的 n 个梯度(通常为 5-20,存储要求小得多)。

牛顿方法

详细的数学计算见下面的帖子:

https://www.earthinversion.com/techniques/the-L-BFGS-optimization-method/

让我们从数学上看看我们是如何解决牛顿法的:

利用泰勒展开式,我们可以将二次可微函数 f(t)写成

为了获得最小值/最大值,这给出

这给出了向最佳点方向移动的步长。

罗森布罗克函数的 L-BFGS 实现

Rosenbrock 函数是一个非凸函数,用于优化算法的性能测试。

# l-bfgs-b algorithm local optimization of a convex function
from scipy.optimize import minimize
from scipy.optimize import rosen, rosen_der
import numpy as np
from matplotlib import cm
import matplotlib.pyplot as plt
import time
np.random.seed(122)

def plot_objective(objective):
    # Initialize figure 
    figRos = plt.figure(figsize=(12, 7))
    axRos = plt.subplot(111, projection='3d')

    # Evaluate function
    X = np.arange(-1, 1, 0.15)
    Y = np.arange(-1, 1, 0.15)
    X, Y = np.meshgrid(X, Y)
    XX = (X,Y)
    Z = objective(XX)

    # Plot the surface
    surf = axRos.plot_surface(X, Y, Z, cmap=cm.gist_heat_r,
                        linewidth=0, antialiased=False)
    axRos.set_zlim(0, 50)
    figRos.colorbar(surf, shrink=0.5, aspect=10)
    plt.savefig('objective_function.png',bbox_inches='tight', dpi=200)
    plt.close()

## Rosenbrock function
# objective function
b = 10
def objective(x):
    f = (x[0]-1)**2 + b*(x[1]-x[0]**2)**2
    return f

plot_objective(objective)

# derivative of the objective function
def derivative(x):
    df = np.array([2*(x[0]-1) - 4*b*(x[1] - x[0]**2)*x[0], \
                         2*b*(x[1]-x[0]**2)])
    return df

starttime = time.perf_counter()
# define range for input
r_min, r_max = -1.0, 1.0

# define the starting point as a random sample from the domain
pt = r_min + np.random.rand(2) * (r_max - r_min)
print('initial input pt: ', pt)

# perform the l-bfgs-b algorithm search
result = minimize(objective, pt, method='L-BFGS-B', jac=derivative)
print(f"Total time taken for the minimization: {time.perf_counter()-starttime:.4f}s")
# summarize the result
print('Status : %s' % result['message'])
print('Total Evaluations: %d' % result['nfev'])

# evaluate solution
solution = result['x']
evaluation = objective(solution)
print('Solution: f(%s) = %.5f' % (solution, evaluation))

这给了

$ python lbfgs_algo.py 
initial input pt:  [-0.68601632  0.40442008]
Total time taken for the minimization: 0.0046s
Status : CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL
Total Evaluations: 24
Solution: f([1.0000006  1.00000115]) = 0.00000

该方法在 24 次迭代中收敛到最小值需要 0.0046 秒。

用作测试优化函数的 Rosenbrock 函数(图片由作者提供)

梯度下降法

import numpy as np
import time
starttime = time.perf_counter()

# define range for input
r_min, r_max = -1.0, 1.0

# define the starting point as a random sample from the domain
cur_x = r_min + np.random.rand(2) * (r_max - r_min)

rate = 0.01 # Learning rate
precision = 0.000001 #This tells us when to stop the algorithm
previous_step_size = 1 #
max_iters = 10000 # maximum number of iterations
iters = 0 #iteration counter

## Rosenbrock function
# objective function
b = 10
def objective(x):
    f = (x[0]-1)**2 + b*(x[1]-x[0]**2)**2
    return f

# derivative of the objective function
def derivative(x):
    df = np.array([2*(x[0]-1) - 4*b*(x[1] - x[0]**2)*x[0], \
                         2*b*(x[1]-x[0]**2)])
    return df

while previous_step_size > precision and iters < max_iters:
    prev_x = cur_x #Store current x value in prev_x
    cur_x = cur_x - rate * derivative(prev_x) #Grad descent
    previous_step_size = sum(abs(cur_x - prev_x)) #Change in x
    iters = iters+1 #iteration count
print(f"Total time taken for the minimization: {time.perf_counter()-starttime:.4f}s")
print("The local minimum occurs at point", cur_x, "for iteration:", iters)

这给了

$ python gradient_descent.py 
Total time taken for the minimization: 0.0131s
The local minimum occurs at point [0.99991679 0.99983024] for iteration: 2129

梯度下降法获得相同 Rosenbrock 函数最小值的总运行时间为 0.0131 秒(比 lbfgs 多 3 倍)。这种情况下的总迭代次数是 2129 次。

结论

我们讨论了二阶导数方法,如牛顿法,特别是 L-BFGS(一种拟牛顿法)。然后,我们比较了 L-BFGS 方法和基于一阶导数的梯度下降法。我们发现,L-BFGS 方法比梯度下降法收敛的迭代次数少得多,总运行时间比 L-BFGS 少 3 倍。

参考

  1. BFGS 优化算法简介
  2. 数值优化:了解 L-BFGS 1。L-BFGS 是如何工作的?
  3. 罗森布罗克函数

原载于 2022 年 3 月 11 日https://www.earthinversion.com

数据科学面试的数字

原文:https://towardsdatascience.com/numpy-for-data-science-interviews-1f86e7277ddd

数据科学 NumPy 简介

作者在 Canva 上创建的图像

NumPy 是 Python 数据科学生态系统中用于科学计算的基础库。NumPy 的一些关键特性包括

  • 速度:NumPy 数组比标准 Python 列表快 50 倍
  • 性能:NumPy 融合了 Python 的易用性和 C 的速度
  • 索引和广播:熊猫中广泛使用的特性是从 NumPy 继承来的。
  • 计算工具:NumPy 拥有全面的数学函数和计算工具,几乎可以满足所有需求。您可以执行诸如曲线拟合、优化、线性代数、变换等操作。,得心应手。
  • NumPy 是许多其他科学计算库构建的基础。一些使用 NumPy 的知名库有
    • Stats 和 ML 库:SciPy、Statsmodels、Scikit-Learn、SpaCy
      -可视化:Matplotlib、Seaborn、Plotly、Bokeh、Altair
      -数组操作:PyTorch、Dask、TensorFlow
    • ETL: Pandas

如果你是一个有抱负的数据科学家,精通 NumPy 是意料之中的。幸运的是,NumPy 简单易学。在这个由两部分组成的系列中,我们将从 NumPy 的基础开始,转到 NumPy 的高级用法。

对金钱的需求

让我们举一个简单的例子。假设你想从 A 点到 B 点,有多种旅行方式。你可以骑自行车试着走路,或者你可能想开车。有很多选择,你可以选择跑车或 SUV,甚至在车辆中。如果你不知道你将在什么样的地形上行驶,你可能会更适合使用 SUV,因为这就是它们的设计目的——一种多功能车辆。然而,如果你的目标是在尽可能短的时间内完成旅程,并且你知道道路铺设良好,没有来自另一个方向的意外交通,你会选择跑车,因为你可以快速通过距离。简而言之,这就是标准 Python 列表和 NumPy 数组之间的区别。Python 列表是您的全地形车,而 NumPy 数组是跑车。Python 列表旨在处理可以插入到列表中的所有类型的数据。然而,假设。您知道数据是同质的和数字类型的数据(整数、浮点、布尔、日期时间等。)并涉及大量计算。在这种情况下,您最好使用 NumPy。

让我们看一个简单的用例。假设我们有一个简单的五种元素的列表。我们想创建另一个列表,包含这些元素除以 5 的结果。我们将不得不遍历列表并逐元素执行操作,因为对整个列表执行除法操作将导致错误。

mylist **=** [10,20,30,40,50]
mylist **/** 5**--------------------------------------------------------------------**
**TypeError**                                 Traceback (most recent call last)
**~\AppData\Local\Temp/ipykernel_21064/503464290.py** in <module>
      1 mylist **=** **[10,20,30,40,50]**
**----> 2** mylist **/** **5**

**TypeError**: unsupported operand type(s) for /: 'list' and 'int'

即使这五个元素都是数字,Python 列表也不允许您执行这种除法运算,因为 Python 运算设计用于所有可能的情况。由于列表不需要只包含数字数据类型,Python 不允许这样做。

输入 NumPy。使用 NumPy 数组,我们可以简单地将整个数组除以 5。就像你会对熊猫系列做的一样(事实上,熊猫系列从 NumPy 获得这些能力)。

myarray **=** np**.**array(mylist)
myarray **/** 5array([ 2.,  4.,  6.,  8., 10.])

使用 NumPy 的另一个原因是速度。NumPy 数组比 Python 标准列表快得多。这里有一个简单的例子。我们将添加两个 Python 列表,每个列表包含一百万个元素。比较列表操作和等价的 NumPy 操作,我们发现 NumPy 比标准列表快 20 倍。( 注:速度可能不同 )

size_of_vec **=** 1000000

**def** pure_python_version():                                                
    time_python **=** time**.**time()                                             
    my_list1 **=** range(size_of_vec)                                         
    my_list2 **=** range(size_of_vec)
    sum_list **=** [my_list1[i] **+** my_list2[i] **for** i **in** range(len(my_list1))]  
    **return** time**.**time() **-** time_python                                      

**def** numpy_version():                                                      
    time_numpy **=** time**.**time()                                              
    my_arr1 **=** np**.**arange(size_of_vec)                                      
    my_arr2 **=** np**.**arange(size_of_vec)
    sum_array **=** my_arr1 **+** my_arr2                                         
    **return** time**.**time() **-** time_numpy                                       

python_time **=** pure_python_version()                                       
numpy_time **=** numpy_version()                                              
print("Pure Python version {:0.4f}"**.**format(python_time))
print("Numpy version {:0.4f}"**.**format(numpy_time))
print("Numpy is in this example {:0.4f} times faster!"**.**format(python_time**/**numpy_time))Pure Python version 0.2890
Numpy version 0.0030
Numpy is in this example 96.4017 times faster!

NumPy 与 Python 和 Pandas 数据类型的比较

熊猫呢?熊猫数据框提供了一种操作表格数据的简单方法。每个熊猫系列都是建立在 NumPy 阵列上的。熊猫提供了使用行标签和列标签来标记元素的优势。

**Pandas Series and NumPy arrays**
myseries = pd.Series([10,20,30,40,50], index = ['A', 'B', 'C', 'D', 'E'])
myseries
A    10
B    20
C    30
D    40
E    50
dtype: int64
pd_to_np = myseries.to_numpy()
pd_to_np
array([10, 20, 30, 40, 50], dtype=int64)

然而,随着数据量的增长,数学运算变得越来越慢。人们可以使用 Pandas 来操作数据,然后转移到 NumPy 来更快地执行操作。

数字基础

作者在 Canva 上创建的图片

初始化

有多种初始化 NumPy 数组的方法。最简单的方法是向 NumPy 中的 array 方法传递一个列表(或任何其他类似数组的对象)。

list_to_np = np.array([1,2,3,4,5])
list_to_np
array([1, 2, 3, 4, 5])
tuple_to_np = np.array((1,2,3,4,5))
tuple_to_np
array([1, 2, 3, 4, 5])

但是,下面的代码会产生一个错误。

# This will result in an error
wrong_np= np.array(1,2,3,4,5)
wrong_np
--------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_21064/3907638067.py in <module>
      1 # This will result in an error
----> 2 wrong_np= np.array(1,2,3,4,5)
      3 wrong_npTypeError: array() takes from 1 to 2 positional arguments but 5 were given

这是开始使用 NumPy 时最常见的错误之一。我们需要将数组元素作为类似数组的对象传递,如 list、tuple、Pandas Series 等。

规模

NumPy 数组是同质项目的 n 维容器。NumPy 可以处理多维数据,因此非常适合科学计算。我们可以像前面创建一维数组一样创建多维数组。

多维数组

array_2d = np.array([
    [1,2,3,4,5], 
    [11,12,13,14,15]
]
)
array_2d
array([[ 1,  2,  3,  4,  5],
       [11, 12, 13, 14, 15]])

要记住的一点是,这里的子列表的长度应该是相同的,否则,NumPy 将创建一个所谓的不规则数组(列表类型的数组)数据,这基本上违背了 NumPy 的目的。

# Ragged Array
array_ragged = np.array([
    [1,2,3,4,5], 
    [11,12,13,14,15, 16]
]
)
array_raggedC:\Users\Asus\AppData\Local\Temp/ipykernel_21064/3536024976.py:2: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray
  array_ragged = np.array([
array([list([1, 2, 3, 4, 5]), list([11, 12, 13, 14, 15, 16])],
      dtype=object)

把它想象成驾驶一辆跑车进入沙漠。它会移动(至少一段时间),但这不是它的设计目的!

我们可以很容易地将一维数据转换成多维数据,反之亦然。我们将在以后研究这些。

描述性属性

与熊猫一样,我们可以找出 NumPy 数组的各种属性。最常用的属性是。

ndim :数据的维数(轴数)。

myarray
array([10, 20, 30, 40, 50])
myarray.ndim
1
array_2d
array([[ 1,  2,  3,  4,  5],
       [11, 12, 13, 14, 15]])
array_2d.ndim
2

shape : 它以元组的形式返回每个维度上元素的数量

myarray.shape
(5,)
array_2d.shape
(2, 5)

size :返回数组中元素的个数。

myarray.size
5
array_2d.size
10

dtype : 这为我们提供了数组元素的数据类型。

dtype : datatype of the array elementsmyarray
array([10, 20, 30, 40, 50])
myarray.dtype
dtype('int32')
myarray / 5
array([ 2.,  4.,  6.,  8., 10.])
(myarray / 5).dtype
dtype('float64')

虽然最常见的数据类型是整数(int)和浮点(float),但 NumPy 还允许您创建日期时间、布尔和字符类型数组。

特殊数组

NumPy 允许我们非常容易地快速初始化大型数组,尤其是那些与科学计算相关的数组。

—1 的数组。

ones_ar = np.ones([3,7])
ones_ar
array([[1., 1., 1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1., 1., 1.]])

0—0 的数组

zeros_ar = np.zeros([6,4])
zeros_ar
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])

—指定对角线上为 1,其他地方为 0 的数组。

eye_ar = np.eye(6,4, 1)
eye_ar
array([[0., 1., 0., 0.],
       [0., 0., 1., 0.],
       [0., 0., 0., 1.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])
eye_ar2 = np.eye(6,4, -2)
eye_ar2
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [1., 0., 0., 0.],
       [0., 1., 0., 0.],
       [0., 0., 1., 0.],
       [0., 0., 0., 1.]])

除此之外,我们还可以创建等距数组。标准 Python 中有 range 数据类型。NumPy 有两种非常常用的方法来快速创建相似间距的值。这些是:

arange: 这与标准 Python 中的 range 数据类型非常相似。NumPy 中的 arange 方法使用与 range 数据类型相同的元素创建一个数组。与 range 数据类型不同,它存储为 NumPy 数组。

arange_ar = np.arange(1,20,3)
arange_ar
array([ 1,  4,  7, 10, 13, 16, 19])

linspace:linspace 方法将给定的端点划分成等间距的区间。在给定的例子中,我们想要五个等距的点,1 和 12 是端点。

linspace_ar = np.linspace(1,12,5)
linspace_ar
array([ 1\.  ,  3.75,  6.5 ,  9.25, 12\.  ])

重塑 NumPy 数组

NumPy 允许您轻松地将数组的形状更改为我们想要的尺寸。我们可以简单地使用 reshape 方法来改变数组的形状。需要注意的一点是,只有当两个数组的大小(元素的数量)相同时,才可以改变数组的形状。因此,您可以将包含 60 个元素的数组拆分为以下形状

  • 60x1(或 1x60、1x 60x 1……)
  • 2x30
  • 4x15
  • 2x3x10
  • 1x6x5 等等
ar60 = np.arange(1,61)
ar60
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
       18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
       35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
       52, 53, 54, 55, 56, 57, 58, 59, 60])
ar_6_10 = ar60.reshape(6,10)
ar_6_10
array([[ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10],
       [11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
       [21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
       [31, 32, 33, 34, 35, 36, 37, 38, 39, 40],
       [41, 42, 43, 44, 45, 46, 47, 48, 49, 50],
       [51, 52, 53, 54, 55, 56, 57, 58, 59, 60]])
ar_6_10.shape
(6, 10)

但是,不能将一个 60 元素的数组拆分成另一个一维上有 7 个元素的数组

ar60.reshape(7,10)
--------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_21064/1130489163.py in <module>
----> 1 ar60.reshape(7,10)ValueError: cannot reshape array of size 60 into shape (7,10)

NumPy 中的快速破解允许它自己计算尺寸。例如,如果您不知道特定数组中元素的确切数量,但想要六行,我们可以通过指定-1 作为要计算的维度来实现。

ar60.reshape(6,-1)
array([[ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10],
       [11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
       [21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
       [31, 32, 33, 34, 35, 36, 37, 38, 39, 40],
       [41, 42, 43, 44, 45, 46, 47, 48, 49, 50],
       [51, 52, 53, 54, 55, 56, 57, 58, 59, 60]])

在上面的例子中,我们有 60 个元素,所以当我们把它调整为(6,-1)时,NumPy 自动计算出另一个维度为 60 / 6 = 10。

显然,如果一个轴上的元素数不是 60 的倍数,这就行不通了。所以这将返回一个错误。

# This will return an error
ar60.reshape(7,-1)
--------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_21064/3388074008.py in <module>
      1 # This will return an error
----> 2 ar60.reshape(7,-1)ValueError: cannot reshape array of size 60 into shape (7,newaxis)

此外,NumPy 一次只能计算一个未知维度。所以这也会导致错误。

# So will this
ar60.reshape(6,-1,-1)
--------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_21064/2258005957.py in <module>
      1 # So will this
----> 2 ar60.reshape(6,-1,-1)ValueError: can only specify one unknown dimension

索引和切片数组

索引和切片 NumPy 数组,就像我们处理 Pandas 数据帧、Pandas 系列和列表一样。假设你想在一个数组中找到第 n 个索引项;您可以像我们处理 Python 列表一样简单地使用切片器。切片器的其他功能也像处理 Python 列表或 Pandas 数据帧一样工作。

ar60
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
       18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
       35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
       52, 53, 54, 55, 56, 57, 58, 59, 60])
• Accessing the item at index = 5 (Note NumPy is zero indexed)
ar60[5]
6
• Accessing the first 10 elements
ar60[:10]
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10])

对于多维数组,我们可以用与 Pandas iloc 方法相同的方式进行索引。这是一个从二维数组中选择的例子

ar5_12 = ar60.reshape(5,12)
ar5_12
array([[ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12],
       [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24],
       [25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36],
       [37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48],
       [49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60]])
ar5_12[2,5]
30

也可以通过使用多个切片器来进行子集划分。这是从二维数组中选择多个项目的另一个例子

ar5_12[2][5]
30

我们还可以将一系列索引传递给切片器。这非常类似于熊猫数据帧中的 iloc 方法。

• NumPy indexing is very similar to iloc method in Pandas
ar5_12[:, 3]
array([ 4, 16, 28, 40, 52])
ar5_12[2, :]
array([25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36])

布尔掩码

NumPy 中最强大的概念之一是布尔屏蔽。正如我们在前面的 Pandas 中看到的,我们可以基于逻辑真/假条件对 NumPy 数组进行子集划分,并获得满足该条件的元素输出。这在熊猫中相当普遍。

ar60
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
       18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
       35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
       52, 53, 54, 55, 56, 57, 58, 59, 60])ar60[ar60 % 3 == 0]array([ 3,  6,  9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51,
       54, 57, 60])

与 Pandas 一样,在创建这些布尔掩码时,我们将不得不使用 Python 逻辑操作符,而不是像 and、or 这样的关键字。

ar60[(ar60 % 3 == 0) | (ar60 % 5 == 0)]
array([ 3,  5,  6,  9, 10, 12, 15, 18, 20, 21, 24, 25, 27, 30, 33, 35, 36,
       39, 40, 42, 45, 48, 50, 51, 54, 55, 57, 60])
# This will result in an error
ar60[(ar60 % 3 == 0) or (ar60 % 5 == 0)]
--------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_21064/445061012.py in <module>
      1 # This will result in an error
----> 2 ar60[(ar60 % 3 == 0) or (ar60 % 5 == 0)]ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

查看我们的帖子“微软数据科学家面试问题”,找到最近的一个面试问题,该问题测试“布尔掩码数组”作为 Python 的主要概念之一。

NumPy 中的函数

NumPy 自带各种内置函数。这些函数可以非常快速地执行数学运算和复杂的科学计算。

ar5_12
array([[ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12],
       [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24],
       [25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36],
       [37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48],
       [49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60]])
ar5_12.sum()
1830

我们也可以沿着特定的轴而不是整个数组来执行这些操作。所以,如果你想得到沿行或仅沿列的元素的和,那也是可能的,记住最外面的维度是轴 0,下一个是轴 1,依此类推。与标准 Python 一样,我们可以使用负索引。最里面的维度将是 axis = -1,依此类推。

ar5_12.shape
(5, 12)• axus 0 is the outer most axis, -1 is the innermost
ar5_12.sum(axis = 0)
array([125, 130, 135, 140, 145, 150, 155, 160, 165, 170, 175, 180])
ar5_12.sum(axis = 1)
array([ 78, 222, 366, 510, 654])• Negative axis
ar5_12.sum(axis = -1)
array([ 78, 222, 366, 510, 654])
ar5_12.sum(axis = -2)
array([125, 130, 135, 140, 145, 150, 155, 160, 165, 170, 175, 180])

除了简单的聚合函数,如 sum、max、min 等。,我们也有内置的标准数学函数。NumPy 中函数的完整列表可以参考文档。

矢量化运算

使 NumPy 如此强大的另一个特性是矢量化运算。这些允许我们在整个维度甚至整个数组上执行数学运算。例如,如果我们想添加两个数组,你可以简单地这样做。

list01 = [10,11,12,13]
list02 = [20,21,22,23]
np.array(list01) + np.array(list02)
array([30, 32, 34, 36])

这对于连接两个列表的 Python 列表来说是不可能的。

list01 + list02
[10, 11, 12, 13, 20, 21, 22, 23]

这些操作不仅限于相同维数的数组。甚至不同的数组也可以用于这些矢量化运算。这个过程称为广播,我们将在本系列的下一部分中研究 NumPy 的这些和更多高级特性,我们将涉及:

  • NumPy 随机数运算
  • 堆叠和拆分等阵列操作
  • 处理缺失值
  • 广播
  • 矩阵运算
  • 曲线拟合
  • 将数据导入 NumPy

结论

对于有抱负的数据科学家来说,NumPy 可以说是仅次于 Pandas 的最重要的 Python 库。虽然对于没有数学或技术背景的人来说,NumPy 可能会显得有点不舒服,但如果我们从熊猫开始,那么 NumPy 就变得非常容易使用。NumPy 提供的可能性和能力是其他标准库中所没有的。

和其他技能一样,耐心、坚持和练习是提高的关键。如果你想在实际的数据科学面试中实践这些以及更多现实生活中的问题,请立即加入 StrataScratch。您将发现一个由 20,000 多名志同道合的数据科学爱好者组成的社区,并在解决 500 多个不同难度的编码和非编码问题的同时进行学习。今天就注册,让你在微软、谷歌、亚马逊等顶级科技公司或优步、Doordash 等热门初创公司工作的梦想成为现实。所有的代码示例都可以在 Github 这里找到。

https://www.stratascratch.com】最初发表于https://www.stratascratch.com/blog/numpy-for-data-science-interviews/?utm_source=blog&utm_medium=click&utm_campaign=medium

数据科学访谈节目:第 2 部分

原文:https://towardsdatascience.com/numpy-for-data-science-interviews-part-02-28c9ee37d2c8

数据科学数字系列的第 2 部分

作者在 Canva 上创建的图像

的上一篇文章中,我们看了使用 NumPy 的基础知识。我们讨论了

  • 对 NumPy 的需求以及 NumPy 数组与 Python 列表和 Pandas 系列的比较。
  • 尺寸、形状等数组属性。
  • NumPy 中的特殊数组
  • 重塑 NumPy 数组
  • 索引和切片 NumPy 数组
  • NumPy 数组中的布尔掩码
  • NumPy 中的基本函数
  • 矢量化运算

在本系列的第二部分,我们将讨论 NumPy 中的高级主题及其在数据科学中的应用。我们将会看到

  • 随机数生成
  • 高级阵列操作
  • 处理缺失值
  • NumPy 中的排序和搜索功能
  • 广播
  • 矩阵运算和线性代数
  • 多项式
  • 曲线拟合
  • 将数据导入 NumPy 和从 NumPy 导出数据

如果您以前没有使用过 NumPy,我们建议您阅读 NumPy 系列的第一部分,开始了解 NumPy 概念。

NumPy 的高级用法

作者在 Canva 上创建的图片

NumPy 随机数运算

随机数生成是科学图书馆的重要基础。NumPy 支持使用 random()模块生成随机数。最简单的随机数生成方法之一是 rand()。rand 方法返回 0 和 1 之间均匀随机分布的结果。

np.random.rand()
0.008305751553221774

我们可以指定需要生成的随机数的数量。

np.random.rand(10)
array([0.03246139, 0.41410126, 0.96956026, 0.43218461, 0.3212331 ,
       0.98749094, 0.83184371, 0.33608471, 0.98037176, 0.03072824])

或者指定生成的数组的形状。例如,在这种情况下,我们得到 15 个形状为 3 x 5 的均匀分布的随机数

np.random.rand(3,5)
array([[0.65233864, 0.00659963, 0.60412613, 0.85469298, 0.95456249],
       [0.25876255, 0.12360838, 0.20899371, 0.9162027 , 0.74732087],
       [0.97992517, 0.1444538 , 0.47195618, 0.38424683, 0.93320447]])

我们也可以生成指定范围内的整数。为此,我们使用 randint()方法。

np.random.randint(4)
0

我们还可以指定需要多少个整数。

np.random.randint(1,6)
4

与 rand 方法一样,在 randint 函数中,我们也可以指定最终数组的形状

np.random.randint(1,6, (5,3))
array([[1, 1, 2],
       [4, 4, 3],
       [3, 4, 5],
       [5, 3, 4],
       [4, 1, 5]])

抽样

我们也可以使用随机数发生器对给定的群体进行抽样。例如,如果我们想从 10 种不同的颜色中选择三种颜色,我们可以使用 choice 选项。

color_list = ['Red', 'Blue', 'Green', 'Orange', 'Violet', 'Pink', 'Indigo', 'White', 'Cyan', 'Black' ]
np.random.choice(color_list, 5)
array(['Black', 'Black', 'Cyan', 'Black', 'Cyan'], dtype='<U6')

我们还可以指定是否希望重复选择

# Using replace = False to avoid repetitions
np.random.choice(color_list, 5, replace = False)
array(['Black', 'Pink', 'Red', 'Indigo', 'Blue'], dtype='<U6')

如您所料,如果选择的数量大于可用选项的数量,该函数将返回一个错误。

# Error when sample is more than the population
np.random.choice(color_list, 15, replace = False)
--------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_9040/2963984956.py in <module>
      1 # Error when sample is more than the population
----> 2 np.random.choice(color_list, 15, replace = False)mtrand.pyx in numpy.random.mtrand.RandomState.choice()ValueError: Cannot take a larger sample than population when 'replace=False'

“冻结”随机状态

数据科学领域的一个关键要求是结果的可重复性。当我们选择随机数来计算结果时,为了能够重现相同的结果,我们需要相同的随机数序列。为此,我们需要了解随机数是如何产生的。

由计算机算法产生的随机数大多称为伪随机数。简而言之,这是一个数字序列,它具有与随机数相同的属性,但由于内存、磁盘空间等限制,最终会重复一种模式。该算法使用一个名为种子的初始值来生成随机数。算法的特定种子将输出相同的随机数序列。把它想象成一辆车的注册号码或者社会安全号码。这个数字可以用来识别序列。

为了重现相同的随机数集合,我们需要指定序列的种子。在 NumPy 中,这可以通过设置生成器的 RandomState 来实现。让我们用下面的例子来说明这一点。我们之前从十种颜色中选了五种。每次执行代码,我们可能会得到不同的结果。但是,如果我们在调用 choice 方法之前修复 RandomState,那么无论我们执行多少次单元格,都会得到相同的结果。

**Freezing the Random State**# without freezing : each time you execute this cell, you might get a different result
np.random.choice(color_list, 5, replace = False)array(['White', 'Blue', 'Black', 'Cyan', 'Indigo'], dtype='<U6')np.random.choice(color_list, 5, replace = False)
array(['Orange', 'Cyan', 'Pink', 'Violet', 'Black'], dtype='<U6')np.random.choice(color_list, 5, replace = False)
array(['Green', 'Blue', 'Orange', 'Pink', 'White'], dtype='<U6')# If we fix the random state, we will get the same sequence over and over again
np.random.RandomState(42).choice(color_list, 5, replace = False)
array(['Cyan', 'Blue', 'Pink', 'Red', 'White'], dtype='<U6')np.random.RandomState(42).choice(color_list, 5, replace = False)
array(['Cyan', 'Blue', 'Pink', 'Red', 'White'], dtype='<U6')np.random.RandomState(42).choice(color_list, 5, replace = False)
array(['Cyan', 'Blue', 'Pink', 'Red', 'White'], dtype='<U6')

这在数据科学操作中非常有用,例如将数据集拆分为训练和测试数据集。您将在几乎所有采样方法中找到这些播种选项——sample、Pandas 中的 shuffle 方法、scikit_learn 等中的 train_test_split 方法。

另一个有用的随机数操作是洗牌。顾名思义,shuffle 方法可以很好地对数组的元素进行重新排序。

my_ar = np.arange(1,11)
my_ar
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10])np.random.shuffle(my_ar)
my_ar
array([ 6,  3,  8, 10,  4,  2,  1,  9,  5,  7])my_ar = np.arange(1,11)
np.random.shuffle(my_ar)
my_ar
array([ 6,  7, 10,  8,  1,  9,  3,  4,  2,  5])my_ar = np.arange(1,11)
np.random.shuffle(my_ar)
my_ar
array([ 7,  6,  2,  4,  5,  8, 10,  9,  1,  3])

也可以通过指定 RandomState()来“固定”洗牌的结果。

# Using random state to fix the shuffling
my_ar = np.arange(1,11)
my_ar
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10])np.random.RandomState(123).shuffle(my_ar)
my_ar
array([ 5,  1,  8,  6,  9,  4,  2,  7, 10,  3])my_ar = np.arange(1,11)
np.random.RandomState(123).shuffle(my_ar)
my_ar
array([ 5,  1,  8,  6,  9,  4,  2,  7, 10,  3])my_ar = np.arange(1,11)
np.random.RandomState(123).shuffle(my_ar)
my_ar
array([ 5,  1,  8,  6,  9,  4,  2,  7, 10,  3])

数组运算

NumPy 支持一系列数组操作。我们已经在 NumPy 系列的第一部中看到了一些基本的。让我们来看看一些高级操作。

拆分一个数组。
我们有很多种选择,可以把数组分解成更小的数组。

split()
split 方法提供了两种主要的拆分数组的方法。
如果我们传递一个整数,这个数组就被分割成大小相等的数组。

my_ar = np.arange(31,61)
my_ar
array([31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
       48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60])
np.split(my_ar, 5)
[array([31, 32, 33, 34, 35, 36]),
 array([37, 38, 39, 40, 41, 42]),
 array([43, 44, 45, 46, 47, 48]),
 array([49, 50, 51, 52, 53, 54]),
 array([55, 56, 57, 58, 59, 60])]

如果数组不能分成大小相等的子数组,它将返回一个错误。

# This will result in an error since the array cannot be split into equal parts
np.split(my_ar, 8)
--------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
c:\users\asus\appdata\local\programs\python\python39\lib\site-packages\numpy\lib\shape_base.py in split(ary, indices_or_sections, axis)
    866     try:
--> 867         len(indices_or_sections)
    868     except TypeError:TypeError: object of type 'int' has no len()During handling of the above exception, another exception occurred:ValueError                                Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_9040/3446337543.py in <module>
      1 # This will result in an error since the array cannot be split into equal parts
----> 2 np.split(my_ar, 8)<__array_function__ internals> in split(*args, **kwargs)c:\users\asus\appdata\local\programs\python\python39\lib\site-packages\numpy\lib\shape_base.py in split(ary, indices_or_sections, axis)
    870         N = ary.shape[axis]
    871         if N % sections:
--> 872             raise ValueError(
    873                 'array split does not result in an equal division')
    874     return array_split(ary, indices_or_sections, axis)ValueError: array split does not result in an equal division
To avoid equal division error, use array_split() method

为了克服这个问题,我们可以使用 array_split()方法。

np.array_split(my_ar, 8)
[array([31, 32, 33, 34]),
 array([35, 36, 37, 38]),
 array([39, 40, 41, 42]),
 array([43, 44, 45, 46]),
 array([47, 48, 49, 50]),
 array([51, 52, 53, 54]),
 array([55, 56, 57]),
 array([58, 59, 60])]

我们可以传递拆分数组的索引,而不是指定子数组的数量。

np.split(my_ar, [5,12,15])
[array([31, 32, 33, 34, 35]),
 array([36, 37, 38, 39, 40, 41, 42]),
 array([43, 44, 45]),
 array([46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60])]

我们还可以使用另外两个方法
hs split——来水平划分数组

**hsplit**ar2d = my_ar.reshape(5,6)
ar2d
array([[31, 32, 33, 34, 35, 36],
       [37, 38, 39, 40, 41, 42],
       [43, 44, 45, 46, 47, 48],
       [49, 50, 51, 52, 53, 54],
       [55, 56, 57, 58, 59, 60]])
np.hsplit(ar2d, [2,4])
[array([[31, 32],
        [37, 38],
        [43, 44],
        [49, 50],
        [55, 56]]),
 array([[33, 34],
        [39, 40],
        [45, 46],
        [51, 52],
        [57, 58]]),
 array([[35, 36],
        [41, 42],
        [47, 48],
        [53, 54],
        [59, 60]])]

在指定索引处垂直拆分。

np.vsplit(ar2d, [1,4])
[array([[31, 32, 33, 34, 35, 36]]),
 array([[37, 38, 39, 40, 41, 42],
        [43, 44, 45, 46, 47, 48],
        [49, 50, 51, 52, 53, 54]]),
 array([[55, 56, 57, 58, 59, 60]])]

堆叠阵列。
和 split 方法一样,我们可以堆叠(或组合)数组。三种常用的方法是:

Stack:顾名思义,这个方法堆叠数组。对于多维数组,我们可以指定不同的轴来堆叠。

ar01 = np.ones((2,4)) * 5
ar01
array([[5., 5., 5., 5.],
       [5., 5., 5., 5.]])
ar02 = np.ones((2,4))
ar02               
array([[1., 1., 1., 1.],
       [1., 1., 1., 1.]])
ar03 = np.ones((2,4))*3
ar03
array([[3., 3., 3., 3.],
       [3., 3., 3., 3.]]) np.stack([ar01,ar02,ar03], axis = 0)
array([[[5., 5., 5., 5.],
        [5., 5., 5., 5.]],[[1., 1., 1., 1.],
        [1., 1., 1., 1.]],[[3., 3., 3., 3.],
        [3., 3., 3., 3.]]])
np.stack([ar01,ar02,ar03], axis = 1)
array([[[5., 5., 5., 5.],
        [1., 1., 1., 1.],
        [3., 3., 3., 3.]],[[5., 5., 5., 5.],
        [1., 1., 1., 1.],
        [3., 3., 3., 3.]]])
np.stack([ar01,ar02,ar03], axis = 2)
array([[[5., 1., 3.],
        [5., 1., 3.],
        [5., 1., 3.],
        [5., 1., 3.]],[[5., 1., 3.],
        [5., 1., 3.],
        [5., 1., 3.],
        [5., 1., 3.]]])

hstack:这将水平堆叠数组,类似于用于拆分的 hsplit()方法。

ar01 = np.arange(1,6)
ar01
array([1, 2, 3, 4, 5])
ar02 = np.arange(11,16)
ar02
array([11, 12, 13, 14, 15])
np.hstack([ar01,ar02])
array([ 1,  2,  3,  4,  5, 11, 12, 13, 14, 15])
np.hstack([ar01.reshape(-1,1), ar02.reshape(-1,1)])
array([[ 1, 11],
       [ 2, 12],
       [ 3, 13],
       [ 4, 14],
       [ 5, 15]])

vstack:这是垂直堆叠数组,类似于用于拆分的 vsplit 方法。

np.vstack([ar01,ar02])
array([[ 1,  2,  3,  4,  5],
       [11, 12, 13, 14, 15]])
np.vstack([ar01.reshape(-1,1), ar02.reshape(-1,1)])
array([[ 1],
       [ 2],
       [ 3],
       [ 4],
       [ 5],
       [11],
       [12],
       [13],
       [14],
       [15]])

处理缺失值

处理缺失值是数据科学中的一项重要任务。虽然我们希望数据没有任何缺失值,但不幸的是现实生活中的数据可能包含缺失值。与在聚合时自动忽略缺失值的 Pandas 函数不同,NumPy 聚合函数不以类似的方式处理缺失值。如果在聚合过程中遇到一个或多个缺失值,则结果值也将缺失。

my_ar = np.array([1, np.nan, 3])
my_ar
array([ 1., nan,  3.])
my_ar.sum()
nan
my2dar = np.array([[1, np.nan, 3],[4,5,6]])
my2dar
array([[ 1., nan,  3.],
       [ 4.,  5.,  6.]])
my2dar.sum(axis = 0)
array([ 5., nan,  9.])
my2dar.sum(axis = 1)
array([nan, 15.])

为了计算这些忽略缺失值的聚合值,我们需要使用“NaN-Safe”函数。例如,NaN-Safe 版本的 sum-nansum()将计算忽略缺失值的数组的和,nanmax()将计算忽略缺失值的数组的最大值,依此类推。

np.nansum(my_ar)
4.0
np.nansum(my2dar, axis = 0)
array([5., 5., 9.])
np.nansum(my2dar, axis = 1)
array([ 4., 15.])

整理

数据科学中遇到的另一个常见操作是排序。NumPy 有许多排序方法。

基本排序方法允许按升序排序。

my_ar = np.random.randint(1,51, 10)
my_ar
array([48, 37, 45, 48, 31, 22,  5, 43,  2, 21])
np.sort(my_ar)
array([ 2,  5, 21, 22, 31, 37, 43, 45, 48, 48])

对于多维数组,可以指定轴来执行排序操作。

my2dar = my_ar.reshape(2,-1)
my2dar
array([[48, 37, 45, 48, 31],
       [22,  5, 43,  2, 21]])
np.sort(my2dar, axis = 0)
array([[22,  5, 43,  2, 21],
       [48, 37, 45, 48, 31]])
np.sort(my2dar, axis = 1)
array([[31, 37, 45, 48, 48],
       [ 2,  5, 21, 22, 43]])

注意 :没有降序排序选项。可以对排序后的数组使用翻转方法来反转排序过程。或者用切片机。

np.sort(my_ar)[::-1]
array([48, 48, 45, 43, 37, 31, 22, 21,  5,  2])
np.flip(np.sort(my_ar))
array([48, 48, 45, 43, 37, 31, 22, 21,  5,  2])
np.sort(my2dar, axis = 0)[::-1, :]
array([[48, 37, 45, 48, 31],
       [22,  5, 43,  2, 21]])
np.sort(my2dar, axis = 1)[:, ::-1]
array([[48, 48, 45, 37, 31],
       [43, 22, 21,  5,  2]])

NumPy 也有间接排序方法。这些方法不是返回排序后的数组,而是返回排序后的数组的索引。将这些索引传递给切片器,我们就可以得到排序后的数组。

my_ar
array([48, 37, 45, 48, 31, 22,  5, 43,  2, 21])
np.argsort(my_ar)
array([8, 6, 9, 5, 4, 1, 7, 2, 0, 3], dtype=int64)
my_ar[np.argsort(my_ar)]
array([ 2,  5, 21, 22, 31, 37, 43, 45, 48, 48])
np.argsort(my2dar, axis = 0)
array([[1, 1, 1, 1, 1],
       [0, 0, 0, 0, 0]], dtype=int64)
np.argsort(my2dar, axis = 1)
array([[4, 1, 2, 0, 3],
       [3, 1, 4, 0, 2]], dtype=int64)

另一个可用的间接排序选项是 lexsort。lexsort 方法允许以指定的顺序对不同的数组进行排序。假设有两个数组—第一个包含五个人的年龄,第二个包含身高。如果我们想先按年龄再按身高排序,我们可以使用 lexsort 方法。结果将是考虑两种排序顺序的索引。

age_ar = np.random.randint(20,45,5)
age_ar
array([26, 31, 39, 33, 25])
height_ar = np.random.randint(160,185,5)
height_ar
array([180, 176, 174, 172, 177])
np.lexsort((age_ar, height_ar))
array([3, 2, 1, 4, 0], dtype=int64)

搜索

像排序方法一样,NumPy 也提供了多种搜索方法。最常用的是-

argmax(和 argmin):这些函数返回最大值(或最小值)的索引。

my_ar
array([48, 37, 45, 48, 31, 22,  5, 43,  2, 21])
np.argmax(my_ar)
0
np.argmin(my_ar)
8
my2dar
array([[48, 37, 45, 48, 31],
       [22,  5, 43,  2, 21]])
np.argmax(my2dar, axis = 0)
array([0, 0, 0, 0, 0], dtype=int64)
np.argmax(my2dar, axis = 1)
array([0, 2], dtype=int64)

NaN-Safe 方法也可以忽略 nanargmax 和 nanargmin 中缺少的值

其中:这将返回满足指定条件的数组的索引。

np.where(my_ar > 30)
(array([0, 1, 2, 3, 4, 7], dtype=int64),)

此外,它还可以根据元素是否满足条件来操作输出。例如,如果我们想将所有大于 30 的元素作为正数返回,其余的作为负数返回,我们可以用下面的方式来实现。

np.where(my_ar > 30, my_ar, -my_ar)
array([ 48,  37,  45,  48,  31, -22,  -5,  43,  -2, -21])

也可以在多维数组上执行这些操作。

np.where(my2dar > 30, my2dar, -my2dar)
array([[ 48,  37,  45,  48,  31],
       [-22,  -5,  43,  -2, -21]])

argwhere:返回满足 where 条件的数组的索引。

np.argwhere(my_ar > 30)
array([[0],
       [1],
       [2],
       [3],
       [4],
       [7]], dtype=int64)
np.argwhere(my2dar > 30)
array([[0, 0],
       [0, 1],
       [0, 2],
       [0, 3],
       [0, 4],
       [1, 2]], dtype=int64)

广播

NumPy 中最强大的概念之一是广播。NumPy 中的广播特性允许我们在某些情况下对不同形状的数组执行算术运算。在本系列的前一部分中,我们已经看到了如何对两个形状相同的数组执行算术运算。或带有数组的标量。广播将这个概念扩展到两个形状不同的阵列。

然而,并不是所有的阵列都与广播兼容。为了检查两个数组是否适合广播,NumPy 匹配数组元素的形状,从最外面的轴开始,一直到最里面的轴。如果相应的尺寸为

  • 同一的
  • 或者至少其中一个等于 1。

为了说明这一过程,让我们举几个例子。

播音示例 01

ar01 = np.arange(10,50,10)
ar01
array([10, 20, 30, 40])
ar02 = 5
ar01 + ar02
array([15, 25, 35, 45])

在这里,我们试图广播一个带有标量的 shape (4,)数组。标量值通过第一个数组的形状传播。

作者图片

播音示例 02

ar01 = np.arange(10,50,5).reshape(2,-1)
ar01
array([[10, 15, 20, 25],
       [30, 35, 40, 45]])
ar01.shape
(2, 4)
ar02 = np.array([[100],[200]])
ar02
array([[100],
       [200]])
ar02.shape
(2, 1)
ar01 + ar02
array([[110, 115, 120, 125],
       [230, 235, 240, 245]])

此示例分别有两个形状为(2,4)和(2,1)的数组。广播规则从最右边的维度应用。

  • 首先,为了使维度 4 与维度 1 匹配,第二个数组被扩展。
  • 然后程序检查下一个尺寸。因为这两个维度是相同的,所以不需要传播。
  • 第一个数组和传播的第二个数组现在具有相同的维度,并且它们可以按元素添加。

作者图片

广播示例 03

ar01 = np.arange(10,80,10)
ar01
array([10, 20, 30, 40, 50, 60, 70])
ar01.shape
(7,)
ar02 = np.arange(100,500,100).reshape(-1,1)
ar02
array([[100],
       [200],
       [300],
       [400]])
ar02.shape
(4, 1)
ar01 + ar02
array([[110, 120, 130, 140, 150, 160, 170],
       [210, 220, 230, 240, 250, 260, 270],
       [310, 320, 330, 340, 350, 360, 370],
       [410, 420, 430, 440, 450, 460, 470]])

在本例中,我们试图将形状(7)的数组与形状(4,1)的数组相加。广播以下面的方式进行。

  • 我们首先检查最右边的维度(7)和(1)。因为第二个数组的维数是 1,所以它被扩展以适合第一个数组的大小(7)。
  • 此外,使用(4)检查接下来的维度()。为了匹配第二个数组的尺寸,第一个数组被扩展以适合第二个数组的大小(4)。
  • 现在扩展数组的维数是(7,4)并且是按元素添加的。

作者图片

最后,我们看一下不适合一起广播的两个阵列。

ar01 = np.arange(10,80,10).reshape(-1,1)
ar01
array([[10],
       [20],
       [30],
       [40],
       [50],
       [60],
       [70]])
ar01.shape
(7, 1)
ar02
array([[100],
       [200],
       [300],
       [400]])
ar02.shape
(4, 1)

在这里,我们尝试将 shape (7,1)的数组与 shape (4,1)的数组相加。我们从最右边的维度开始—两个数组在这个维度上都有 1 个元素。然而,当我们检查下一个维度(7)和(4)时,这些维度对于广播是不兼容的。因此,程序抛出一个 ValueError。

ar01 + ar02
--------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_9040/1595926737.py in <module>
----> 1 ar01 + ar02ValueError: operands could not be broadcast together with shapes (7,1) (4,1)

矩阵运算和线性代数

NumPy 提供了许多执行矩阵和线性代数运算的方法。我们看看其中的一些。

转置矩阵:

对于二维数组,转置是指将行交换为列,将列交换为行。人们可以简单地调用。t 属性来获取转置矩阵。

my2dar
array([[48, 37, 45, 48, 31],
       [22,  5, 43,  2, 21]])
my2dar.T
array([[48, 22],
       [37,  5],
       [45, 43],
       [48,  2],
       [31, 21]])

对于多维数组,我们可以使用 transpose()方法,指定要转置的轴。

矩阵的行列式:

对于方阵,我们可以计算出行列式。行列式在高等数学和数据科学中有许多应用。这些用于使用克莱姆法则求解线性方程组,计算特征值,这些特征值用于主成分分析等。要找到一个方阵的行列式,我们可以调用 NumPy 的 linalg 子模块中的 det()方法。

sq_ar = np.random.randint(10,50,9).reshape(3,3)
sq_ar
array([[34, 42, 34],
       [12, 16, 33],
       [22, 33, 12]])
np.linalg.det(sq_ar)
-4558.000000000001
sq_ar2 = np.array([[12,15], [18,10]])
sq_ar2
array([[12, 15],
       [18, 10]])
np.linalg.det(sq_ar2)
-149.99999999999997

矩阵乘法:

两个矩阵相乘构成了高等数学和数据科学中众多应用的基础。matmul()函数实现了 NumPy 中的矩阵乘法功能。为了说明矩阵乘法,我们用 eye()方法创建的单位矩阵乘以一个矩阵。我们应该得到原始矩阵作为结果积。

sq_ar
array([[34, 42, 34],
       [12, 16, 33],
       [22, 33, 12]])
np.matmul(sq_ar, np.eye(3,3))
array([[34., 42., 34.],
       [12., 16., 33.],
       [22., 33., 12.]])
sq_ar2
array([[12, 15],
       [18, 10]])
np.matmul(sq_ar2, np.eye(2,2))
array([[12., 15.],
       [18., 10.]])

矩阵的逆:

对于方阵 M,矩阵 M-1 的逆矩阵被定义为使得 M M-1 = In 的矩阵,其中 In 表示 n 乘 n 单位矩阵。我们可以通过使用 NumPy 中 linalg 子模块的 inv()方法找到 NumPy 中矩阵的逆。

inv2 = np.linalg.inv(sq_ar2)
inv2
array([[-0.06666667,  0.1       ],
       [ 0.12      , -0.08      ]])

我们可以证明矩阵与其逆矩阵的乘积等于单位矩阵。

np.matmul(sq_ar2, inv2)
array([[ 1.00000000e+00, -8.32667268e-17],
       [ 0.00000000e+00,  1.00000000e+00]])

NumPy 数组相等。

给定浮点精度的差异,我们可以比较两个 NumPy 数组在给定的容差内是否元素相等。这可以使用 allclose 方法来完成。

np.allclose(np.eye(2,2), np.matmul(sq_ar2, inv2))
True

求解器。

NumPy 的 linalg 模块有 solve 方法,可以计算线性方程组的精确解。

**Solving Linear Equations**x1 + x2 = 10
2x1 + 5x2 = 41
This should give x1 = 3 and x2 = 7coeff_ar = np.array([[1,1],[2,5]])
ord_ar = np.array([[10],[41]])
np.linalg.solve(coeff_ar, ord_ar)
array([[3.],
       [7.]])

多项式

像线性代数子模块一样,NumPy 也支持多项式代数。该模块有几个方法以及包含常见算术运算的多项式类。让我们看看多项式子模块的一些功能。为了说明这一点,我们用 x f(x)中的多项式来表示

f(x) = x² - x - 6

我们可以从系数中创建多项式。注意,系数必须按度数的升序排列。所以先列出常数项的系数(-6),然后是 x 的系数(-1),最后是 x (1)的系数

from numpy.polynomial.polynomial import Polynomial
poly01 = Polynomial([-6,-1,1])
poly01
x → -6.0-1.0x+1.0x²

该表达式也可以被因式分解为

f(x) = (x-3)(x+2)

这里 3 和-2 是方程 f(x) = 0 的。我们可以通过调用 roots()方法找到多项式的根。

poly01.roots()
array([-2.,  3.])

我们可以使用 fromroots()方法从根开始形成方程。

poly02 = Polynomial.fromroots([-2,3])
poly02
x → -6.0-1.0x+1.0x²

多项式模块还包含 linspace()方法,该方法可用于创建跨域的等距 x 和 f(x)对。这可以用来方便地绘制图形。

polyx, polyy = poly01.linspace(n = 21, domain = [-5,5])plt.plot(polyx, polyy)
[<matplotlib.lines.Line2D at 0x26006fd2e80>]

作者图片

曲线拟合

NumPy 多项式子模块还提供多项式对数据的最小平方拟合。为了说明这一点,让我们创建一组数据点,并为多项式表达式添加一些随机性。

numpoints = 11
x_vals = np.linspace(-5,5,numpoints)
y_vals = x_vals**2 + x_vals - 12 + np.random.rand(numpoints)*4plt.scatter(x_vals, y_vals)
<matplotlib.collections.PathCollection at 0x26008086940>

作者图片

然后,我们在这些点上调用 polyfit()方法,并找到拟合多项式的系数。

from numpy.polynomial import polynomial
fit_pol = polynomial.polyfit(x_vals, y_vals, 2)
fit_pol
array([-9.35761397,  0.96843091,  0.93303665])

我们还可以通过绘制数值来直观地验证拟合度。为此,我们使用多值方法来计算这些点处的多项式函数。

fit_y = polynomial.polyval(x_vals, fit_pol)
plt.scatter(x_vals, y_vals)
plt.plot(x_vals, fit_y, c = 'Red', alpha = 0.5 )
[<matplotlib.lines.Line2D at 0x26008102c10>]

作者图片

在 NumPy 中导入和导出数据

到目前为止,我们已经动态创建了数组。在现实生活的数据科学场景中,我们通常有可用的数据。NumPy 支持从文件导入数据和将 NumPy 数组导出到外部文件。我们可以使用 save 和 load 方法在 native NumPy native(.npy 和。npz 格式)。

**save**with open("numpyfile.npy", "wb") as f:
    np.save(f, np.arange(1,11).reshape(2,-1))**load**with open("numpyfile.npy", "rb") as f:
    out_ar = np.load(f)
out_ar
array([[ 1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10]])

我们还可以使用 loadtxt 和 savetxt 方法从文本文件中读取数据和向文本文件中写入数据。

**savetxt**np.savetxt("nptxt.csv", np.arange(101,121).reshape(4,-1))**loadtxt**np.loadtxt("nptxt.csv")
array([[101., 102., 103., 104., 105.],
       [106., 107., 108., 109., 110.],
       [111., 112., 113., 114., 115.],
       [116., 117., 118., 119., 120.]])

结论

在本系列中,我们研究了 NumPy,它是 Python 数据科学生态系统中用于科学计算的基础库。如果你熟悉熊猫,那么搬到 NumPy 是非常容易的。有抱负的数据科学家或声称精通 Python 的数据分析师会对 NumPy 感到满意。

和任何技能一样,掌握数字所需要的只是耐心、坚持和练习。你可以从 StrataScratch 上的实际数据科学面试中尝试这些和许多其他的数据科学面试问题。加入由 40,000 多名志同道合的数据科学爱好者组成的社区。你可以练习 1000 多个不同难度的编码和非编码问题。今天就加入 StaraScratch,让你在苹果、网飞、微软等顶级科技公司工作的梦想成为现实,或者在 Noom、Doordash 等热门初创公司工作的梦想成为现实。所有的代码示例都可以在 Github 这里找到。

https://www.stratascratch.com】最初发表于https://www.stratascratch.com/blog/numpy-for-data-science-interviews-part-02/?utm_source=blog&utm_medium=click&utm_campaign=medium

NumPy 内部:简介

原文:https://towardsdatascience.com/numpy-internals-an-introduction-bcaafa1a68a2

被子下的世界

图片由 Elias 来自 Pixabay

介绍

这是一篇关于 NumPy 如何在内部处理数组的短文。它被认为是一个高级主题,对于许多 NumPy 临时用户来说,并不严格要求深入理解。然而,我认为了解基本的 NumPy 概念是有用的,不管你是直接还是间接使用 NumPy,例如通过 pandas 。这不仅是为了满足个人好奇心,更是为了在数组填满内存时微调性能。在这种情况下,创建另一个副本,即使是短暂的,也是一个问题。这篇文章并不深入,但希望它能激起好奇读者的兴趣。NumPy 文档和特拉维斯·奥列芬特的书NumPy 指南提供了更多的信息。对于我的需求,也许还有典型的数据分析师的需求,这篇文章可以为日常任务提供足够的基础。

NumPy 中的数组本质上有两个部分:包含实际(原始)数据的数据缓冲区,以及关于如何访问和使用原始数组数据的信息,这些信息可以被认为是数组元数据。在像 C 或 Fortran 这样的语言中,原始数据就是全部:一个包含固定大小数据项的连续(固定)内存块。NumPy 的灵活性很大程度上是因为数组元数据。数据缓冲区可以用许多不同的方式解释,而无需重新创建。对于小数组来说,这并不重要。对于较大的数据量,重复复制数组缓冲区可能会成为瓶颈。例如,当对 NumPy 数组进行切片时,元数据会发生变化,但数据缓冲区保持不变,即数组基址指向获取切片的原始数组。本文将列举一组例子,这些例子帮助我理解底层机制,并在使用 NumPy 和 pandas 时变得更加自信,但并不声称我已经完全掌握了所有细节。这样做需要研究源代码,并为不同的目标读者写一篇文章。我的好奇心是通过查看 NumPy 中的入口点(即数组创建)等 NumPy 文档字符串触发的

图 1:在 NumPy 中创建数组时指定内存布局(图片由作者创建)

order参数的作用是什么,我们为什么需要它?不可否认,很少有人需要指定order参数,但同时我也看到 NumPy 抱怨这个形状与非连续数组不兼容。当我看到这个的时候,我的第一个问题是“一个数组怎么可能是不连续的呢?”紧接着是“我如何测试一个数组是否是不连续的?”。我不是唯一有同样担心的人。如果你也对 NumPy array 的内部组织感到好奇,请继续阅读!

NumPy 数组是如何处理的?

在我们准备好试验不同的内存布局、步长和数组变换之前,让我们先建立一个效用函数

该函数使用了最常用的 f 字符串来简洁地打印一些数组属性,这些属性在以后会很有帮助。该代码还定义了一个 4x3 整数数组(int32 ),并使用实用函数打印属性

我们来看看这些都是什么意思。实用函数使用[np.array_str](https://numpy.org/doc/stable/reference/generated/numpy.array_str.html)函数创建了一个简洁的数组字符串表示,该函数被进一步修改为用逗号替换新行,考虑到 f 字符串不能包含反斜杠的约束,因此在括号中不能包含\n。第二条信息来自 array flags 对象,它提供了关于数组内存布局的信息。我们只保留了它的两个属性,这两个属性显示数据缓冲区是在单个 C 风格(或基于行)还是 Fortran 风格(基于列)的连续段中。下一条信息是包含多达八个条目的整个[np.ndarray.__array_interface__](https://numpy.org/doc/stable/reference/arrays.interface.html)字典。我们不会一一介绍。本文的重要关键词是:

  • shape每个维度中数组大小的元组,也可以通过[np.ndarray.shape](https://numpy.org/doc/stable/reference/generated/numpy.shape.html)获得
  • typestr具有基本类型(例如,I 代表整数)和数据的字节顺序的字符串,后跟提供该类型使用的字节数的整数;除了 byteorder 之外,该类型也可以通过[np.ndarray.dtype](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.dtype.html)获得,它也是由实用函数返回的
  • data元组的第一个条目是指向数据缓冲区第一个元素的指针
  • strides为 None,表示 C 风格的连续数组,或者为 tuple of strides,提供跳转到相应维中下一个数组元素所需的字节数;效用函数还提供了总是被填充的np.ndarray.strides

如果内存来自其他对象,实用函数也返回 base 对象,将其格式化为一行,就像数组本身一样。它还返回基本的内存布局。有了这些信息,我们可以进行基本的自省,以了解在引擎盖下发生了什么。

回到例子,传递给效用函数的数组a的 shape (4,3)和它的 dtype 是 int32,所以每个元素消耗 4 个字节。因为起点是一个 Python 列表,所以创建的 NumPy 数组是 C 顺序的,如文档中所解释的。为了在同一行(第二维)中向右移动一个元素,我们需要跳过 4 个字节。为了在同一列(第一维)中向下移动一个元素,我们需要跳转 3x4 =12 字节,这与(12,4) strides元组一致。

让我们看看如果我们使用 F 顺序内存布局创建数组会发生什么

那会打印

strides元组已经改变。现在向下移动一个元素会跳过 4 个字节,因为数组是按列优先顺序存储的。另一方面,数组本身是相同的,具有相同的形状,并且可以以相同的方式索引。这解释了为什么大多数 NumPy 的普通用户可能不会查看内存布局。

内存布局对性能有影响吗?

考虑以下对具有 1 亿个浮点 64 元素的数组的行和列的求和

这给了

图 2:内存布局的效果总结(图片由作者创建)

我们可以看到,当数组以行为主(C-顺序)存储时,行的求和速度是以列为主(F-顺序)存储时的两倍。对列求和则正好相反。有人可能会说这是一个很小的差异,我也同意,尽管它表明内存布局也不是无关紧要的。

重塑

要探索的第一种情况是数组整形

那会打印

首先要注意的是,两个整形操作不会产生相同的结果。这是因为np.ndarray.reshape的 order 参数与底层数组的内存布局无关,而是定义了如何读取元素并将其放入整形后的数组中。“c”表示以行优先顺序排列元素,即最后一个轴索引变化最快。f 的作用正好相反。在两种整形操作中,整形后的阵列是一个视图。然而:

  • 在 C-order shape 中,基数是原始数组(b.base is a返回True)。考虑到基数和它的 C 顺序布局,我们可以推断出,如果我们想在整形后的数组中向下移动一个元素,我们需要进行 6×4 = 24 字节的跳跃,并将一个元素向右移动 4 字节的跳跃。这与效用函数报告的步幅信息一致。
  • 在 F 阶整形中,基数不是原来的数组(b.base is a返回False,指向第一个元素的指针已经改变,np.shares_momeory(a, b)返回False)。换句话说,元素在内存中被重新定位。我的理解是,这在技术上构成了复制,虽然重塑后的数组的基数不是没有(如果你的理解不同,请评论!).知道了基数和它的 F 阶存储器布局,再一次很容易推断步幅。为了从整形后的数组中的元素 1 到 4,我们需要跳 4 个字节,并且从元素 1 到 7 跳 2×4 = 8 个字节。

在前面的示例中,原始阵列具有 C 顺序内存布局。如果我们从 F 阶初始数组开始,我们会看到 C 阶整形导致一个副本,而 F 阶整形导致一个视图

那会打印

这个示例对于演示另一种判断何时整形导致复制的方法很有用。根据 NumPy 文档,设置数组形状直接导致就地 C-order 整形。如果像上次 C-order 整形一样需要制作副本,则就地整形注定会失败

这给了

Traceback (most recent call last):
  File "D:\myLearningBooks\2022_10_30_PythonForDataAnalytics3rdEdition\venv\lib\site-packages\IPython\core\interactiveshell.py", line 3378, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-1405-1144ef469292>", line 3, in <module>
    a.shape = (2, -1)
AttributeError: Incompatible shape for in-place modification. Use `.reshape()` to make a copy with the desired shape.

文档不鼓励直接设置形状元组而支持np.ndarray.reshape函数,声明它可能会被弃用。文档中还提到如果您希望在复制数据时出现错误,您应该将新的形状分配给数组的 shape 属性。在我看来,这有些不一致,我希望将来文档会简化。本文中的 utility 函数提供了一种替代的、有希望是全面的内省,它包括关于何时制作副本的信息。

置换

这可能听起来比重塑更容易,但有一些事情要发现。我们将从与之前相同的数组开始,使用默认的 C 顺序内存布局

那会打印

转置数组的基础是保持其 C 顺序内存布局的原始数组。要在转置数组中向下移动一个元素,即从元素 1 移动到元素 2,我们需要跳跃 4 个字节。为了在转置的数组中向右移动一个元素,即从元素 1 移动到元素 4,我们需要跳跃 3x4 = 12 个字节。这些与通过效用函数获得的步幅一致。转置数组的内存布局是 F 顺序的。本质上,这意味着转置操作只需要对数组的内部表示进行很小的改变。以列为主而不是行为主的顺序读取数据缓冲区就足够了。我发现这非常聪明,是底层 NumPy 引擎的一个很好的演示。没有复制任何内容,但数组仍被转置。

限幅

最后一个例子是关于切片的

那会打印

对于所有三个片段,基底保持原始的 C 顺序数组,也由指向第一个元素的存储器地址的指针指示。步幅也保持不变。当选择行时,切片数组保持 C 连续。然而,一旦我们选择了列,切片数组既不是 C 连续的也不是 F 连续的。图 3 展示了推理过程。

图 3:切片后的非连续数组(图片由作者创建)

尽管切片数组是不连续的,但不需要创建副本。我们仍然可以使用大步。为了在整形后的数组中向下移动一个元素,我们需要进行 3×4 = 12 字节的跳跃,向右移动一个元素需要 4 字节的跳跃。相当整洁。NumPy 提供了用np.ascontiguousarray()函数从一个不连续的创建一个连续数组的可能性

但是正如所料,这需要创建一个副本

除非你想执行重复的行或列宽操作,我不太确定这为什么有用,因为它有自己的内存和处理时间成本,但 NumPy 提供了可能性。

结论

NumPy 并不新鲜。尽管如此,每当我想起它,我只能对它的独创性印象深刻。在原始数据之上添加数组元数据允许非常灵活地使用数组。通过改变元数据,可以改变形状,转置或切片数组,而无需重新排列原始数据。数据缓冲器被一次又一次地使用,并且通过被不同地解释,有可能以可忽略的计算成本创建新的视图。这真是太聪明了,这篇文章并没有公平对待 NumPy 的才华。尽管如此,我仍然希望它有助于理解诸如内存布局和跨度之类的概念。除了允许用户以一种高性能的方式使用 NumPy,获得这些知识是使用更深奥的 NumPy 函数的第一步,比如用np.stride_tricks.as_strided创建给定形状和步幅的视图。这听起来很有趣,但需要小心,因为它可能指向无效的内存,使程序崩溃,在更不幸的情况下破坏结果。如果你想走这么远,你可以参考这篇文章及其参考资料。

NumPy 重塑可以打破你的心

原文:https://towardsdatascience.com/numpy-reshape-can-break-your-heart-96fcc47c6894

NumPy reshape 可能不会真的让你心碎,但它可以对心电图(ECG)等时间序列做出无意的改变,并导致生理属性的巨大变化

图 1:凯利·西克玛Unsplash 上的照片

我将要讲述的他的故事,来源于一次参加生理网挑战赛 20202021 的经历。这项机器学习挑战的任务是从心电图(ECG)中对心脏相关疾病进行分类。我们的团队决定使用 1D 卷积神经网络对 12 导联心电图中的 30 种不同的心脏疾病进行分类(你可以在我们的预印本论文中阅读更多关于这种方法的信息)。

在我们将数据集导入到 NumPy 数组中,然后不得不根据以下内容对数组进行整形之后,问题出现了:

(n _ 患者 x n _ 通道 x n _ 信号长度)

(n 个患者 x n 个信号长度 x n 个通道)

这对于使阵列符合 1D 卷积神经网络的输入形状是必要的。在以前的项目中,在处理图像数据时,我已经使用 NumPy 的 reshape 方法做到了这一点,效果非常好。下面的代码显示了在重新调整数组形状之前,如何绘制图 2 所示的三个 MNIST 数字示例。

Image_data = pd.read_csv("/kaggle/input/digit-recognizer/train.csv")
Image_data = Image_data.iloc[:,1:]
# Image_data shape = (42000, 28, 28, 1)for i **in** range(3):
    plt.imshow(Image_data[i][:,:,0])
    plt.axis("off")
    plt.show()

图 2:未经任何预处理的 MNIST 数字|作者图片

然后,让我们尝试将 reshape 方法应用于相同的三个数字,然后再次绘制它。

Image_data_reshaped= Image_data.reshape(42000,28,1,28)for i **in** range(3):
    plt.imshow(Image_data_reshaped[i][:,0,:])
    plt.axis("off")
    plt.show()

图 3:重塑后的 MNIST 数字|作者图片

图 3 显示了使用 NumPy reshape 方法后,图 2 中绘制的 3 个相同的图形。还有…tada:

没区别!🎉

现在,让我们切换回 ECG 案例,在 ECG 阵列上尝试同样的方法。首先,让我们看看什么是心电图:

ECG 反映了心脏的电活动,并且对该记录的解释可以揭示心脏的许多病理。

通过在患者上身的不同位置放置 10 个电极来测量 12 导联 ECG。这让我们可以从不同的角度测量心脏的电活动。

图 4:手动解读的 12 导联心电图。 Towfiqu barbhuiyaUnsplash 上拍照

现在,让我们来看看数据集中第一位患者的 12 个心电图导联。下面的代码显示了如何选择一名患者的 12 导联心电图,并在没有任何整形的情况下绘制。图 5 显示了绘制的结果,每个子图显示了 12 个导联中的每一个。

# ecg_array shape is equal to (21832,12,5000)single_ecg = ecg_array[0]for i **in** range(12):
    ecg_plot.plot_1(single_ecg[i,:])
    plt.show()

图 5:预处理前的心电图

现在,让我们对 ECG 使用整形方法。

# single_ecg shape = (12,5000)
reshaped_ecg = single_ecg.reshape(single_ecg.shape[1],single_ecg.shape[0])
# reshaped_ecg shape = (5000,12)for i **in** range(12):
    ecg_plot.plot_1(reshaped_ecg[:,i])
    plt.show()

图 6 显示了绘制整形后的 ECG 阵列后得到的 12 个导联

图 6:使用整形方法后的心电图|作者图片

嗯(表示踌躇等)...奇怪的事情发生了。让我们放大其中一个心电图。图 7 显示了应该是导联 I 的部分,它看起来仍然像 ECG,但不是我们预期的那样。如果我们再次仔细观察图 4 中的 12 个导联,我们会发现现在所有导联都完全相同。

图 7:放大使用 NumPy 整形方法处理的一个 ECG 导联|作者图片

显示结果的最佳方式是平坦化原始 ECG 信号,并将其重新采样为 5000 个样本,然后将其与整形后的信号进行比较,如图 8 所示。

# single_ecg.ravel() shape = 60000 (12 x 5000)
from scipy import signal#resample signal from 60000 to 5000
resamp_sig = signal.resample(single_ecg.ravel(), 5000) 
plt.plot(reshaped_ecg[:,0], label="reshaped signal")
plt.plot(resamp_sig, label="resampled signal")
plt.show()

图 8:整形信号与重采样信号|作者图片

从图 8 中我们可以看到,整形方法实际上不仅整形了数组,还对所有原始的 12 个导联进行了重新采样,然后使其适合于单个导联,然后制作了 12 个副本。

好吧,所以 NumPy reshape 可能不会真的让你心碎,但在这种情况下,它无意中改变了心电图,也对生理特性产生了巨大的影响。在这种情况下,它将测得的心率提高了 12 倍,但我们如何克服这个问题呢?

用 n umpy.moveaxis 代替!

# single_ecg shape = (12,5000)
ecg_moveaxis = np.moveaxis(single_ecg, 0, -1)
# ecg_moveaxis.shape = (5000,12)

如何将 NumPy 数组写入 CSV 文件

原文:https://towardsdatascience.com/numpy-to-csv-53df45d3948e

展示了如何将 NumPy 数组转储到?csv 文件

凯利·西克玛Unsplash 上拍摄的照片

介绍

在 NumPy 和 Python 中使用数组时,我们通常需要将它们转储到各种形式的输出文件中,包括逗号分隔值(CSV)。

在今天的简短教程中,我们将讨论将 NumPy 数组写入此类文件的几种不同方法。最后,我们将讨论一种将 NumPy 数组转储到文件中时通常应该避免的方法。

首先,让我们创建一个示例 numpy 数组,我们将在这个简短的教程中引用它来演示一些概念。

import numpy as np arr = np.asarray([ 
    [1, 150, 10.5], 
    [2, 220, 3.1], 
    [3, 121, 10.1],
    [4, 300, 3.2], 
    [5, 541, 6.7], 
    [6, 321, 9.9],
])print(arr)
***array([[  1\. , 150\. ,  10.5],
       [  2\. , 220\. ,   3.1],
       [  3\. , 121\. ,  10.1], 
       [  4\. , 300\. ,   3.2],
       [  5\. , 541\. ,   6.7],
       [  6\. , 321\. ,   9.9]])***

使用numpy.savetxt()方法

当我们需要将一个 NumPy 数组转储到一个输出文件中时,我们的第一个选择是使用[numpy.savetxt()](https://numpy.org/doc/stable/reference/generated/numpy.savetxt.html)方法将一个数组保存到一个文本文件中。该方法允许用户指定分隔符,在我们的例子中应该是逗号:

np.savetxt('output.csv', arr, delimiter=',')

请注意,输出将使用科学符号,如下所示。

示例输出—来源:作者

如果你想避免使用科学符号,那么你可以指定一个合适的fmt。举个例子,

np.savetxt('output.csv', arr, delimiter=',', fmt='%f')

没有科学符号的输出示例—来源:作者

使用 numpy.tofile()方法

第二个选项是[numpy.ndarray.tofile()](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.tofile.html)方法,用于将数组以文本或二进制形式写入文件。同样,我们可以指定分隔符(sepformat):

arr.tofile('output.csv', sep=',', format='%f')

要避免什么

另一种流行的(但不是很聪明的)方法是首先将 NumPy 数组转换成 pandas 数据帧,然后使用[pandas.DataFrame.to_csv()](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.to_csv.html)方法将结果数据帧写入输出 csv 文件。

但是,请注意,这不是一个好的做法,因为它会毫无理由地消耗太多的内存——尤其是当您使用相当大的数组时。

最后的想法

在今天的简短教程中,我们展示了一些将 NumPy 数组写入 CSV 文件的不同方法。更具体地说,我们讨论了如何使用numpy.savetxt()numpy.tofile()方法做到这一点。

最后,我们强调了避免让 pandas 参与到将 NumPy 数组转储到 CSV 文件的过程中的重要性,因为这在内存方面是非常昂贵的。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

相关文章你可能也喜欢

NumPy ufuncs——矢量化函数背后的魔力

原文:https://towardsdatascience.com/numpy-ufuncs-the-magic-behind-vectorized-functions-8cc3ba56aa2c

了解 NumPy 通用函数(ufuncs)以及如何创建它们。编写自己的矢量化函数。

杰瑞米·贝赞格在 Unsplash 上的照片

你有没有想过 NumPy 神奇表演的由来?NumPy 在引擎盖下为数据科学家的许多日常驱动程序提供性能,例如熊猫。当然,考虑用 C 和 Fortran 编写的优化数组是正确的。至少说对了一半。另一半不是数组,而是 NumPy 的函数本身。NumPy 通用函数(以下简称 ufuncs)是数组(ndarray)之外的另一个构件。

当我们想写一些圆滑的和高性能的数字代码时,没有别的办法;我们用 NumPy。一旦我们开始使用 NumPy 数组,就可以直观地使用 NumPy 的内置函数来操纵和操作它们。如果我们在数组上使用 Python 的普通函数,性能会很快消失。

在这个故事中,我们将讨论一些关于 ufunc 的见解,更重要的是,学习关于自定义 uf unc 创建的基础知识。能够实现我们自己的 ufuncs 将使 NumPy 项目更上一层楼。

通常我们会花很多时间在 NumPy 的文档中寻找实现我们需要的功能的方法;有时候,我们找到了,有时候,我们没有。在后一种情况下,我们可能会最终实现一些 hack 或退回到普通 Python,并牺牲性能。编写自定义函数可以极大地简化我们的生活。

故事结构:

  • 什么是 ufuncs?
  • 一元和二元函数
  • NumPy ufunc 创建
  • Numba ufunc 创建:矢量化装饰器
  • ufunc 创建:NumPy vs. Numba
  • ufunc.reduce
  • ufunc.accumulate
  • 最终意见

什么是 ufuncs?

NumPy ufuncs 对数组(ndarray)进行元素操作。其中一些支持更复杂的功能,如阵列广播。虽然 NumPy 的内置 uf unc 是矢量化的(C 或 Fortran 中的矢量化计算),但并非所有 uf unc 都是相同意义上的矢量化,我们将在下面的部分中看到这一点。

所有 ufuncs 都采用相同的可选关键字参数,并公开相同的属性和方法;你可以在这里查看它们。

NumPy 中的大部分函数都是 ufuncs 然而,有些不是,而是 ufunc 的方法。ufunc 的方法是普通的 Python 函数,但仍然是从 ufunc 派生的。那是他们获得魔力的地方。比如 np.sum 就是一个正则函数,来自于np.add,一个 ufunc。

然而,这些方法并不适用于所有的 ufuncs。有两种主要的形式,二元和一元。方法只适用于二进制函数。

一元和二元函数

在数学中,最常见的运算是二进制。和(a + b)和乘积(a * b)是一些最简单和最著名的例子。这些操作中的一些是通勤的;其他人没有。然而,有一些转换是在单个元素上操作的。事实上,我们可以争论这些变换是否可以通过使用二元运算导出,也许是无限和——例如,指数函数。

二元运算在数学中处于中心地位的原因之一是,所有具有两个以上操作数的运算都可以转换为一系列二元运算。

NumPy 在其函数中继承了这一行为。有两种类型的 ufuncs:

  • 一元 ufuncs:取一个数组(ndarray)作为参数
  • 二进制 ufuncs:将两个数组(ndarray)作为参数

例如:

,1

,2

其中 nin 属性是输入的数量。

NumPy ufunc 创建

创建自定义 ufuncs 非常容易。NumPy 有一个实用程序np.frompyfunc,它接受一个普通的 Python 函数、输入参数的数量和输出参数的数量,并返回一个 ufunc。例如,我们可以创建一元 ufunc:

3, 3

我们还可以创建一个二进制 ufunc:

2, 2

Numba ufunc 创建:矢量化装饰器

Numba 还有一个创建 ufunc 的方法。vectorize装饰工。然而,这个装饰器不仅仅是创建一个 ufunc 它还针对性能进行了优化。根据 Numba 的文档,它可以编译 Python 函数,其速度与用 C 编写的 ufuncs 相当(NumPy 内置)。

有许多方法可以使用装饰器;最直接的方法是在装饰器中使用所有默认值(即没有参数):

2

[2. 2.]

这里我们没有为编译指定输入和输出数据类型,因此是惰性模式。

我们还可以显式传递数据类型:

2

[2. 2.]

这里我们说有一个输入(括号外的类型)和两个输出(括号内的元组)。如果两个输入都是int64,返回类型也将是int64。如果两个输入都是float32,则输出是float32,以此类推。指定数据类型可以优化编译。

最后,我们还可以更改其他装饰器的默认参数来优化计算性能:

2

[2. 2.]

ufunc 创建:NumPy vs. Numba

在本节中,我们将比较添加两个 2D NumPy 数组(ndarray)的几种方法的计算时间。我们将使用以下 ufuncs:

  • NumPy 内置 add ( np.add)
  • np_my_sum、用 np.frompyfunc 创建的 ufunc
  • numba_lazy_sum、用下 Numba 的矢量化装饰器创建的 ufunc,带有默认参数,没有数据类型信息
  • numba_dtype_sum,使用矢量化装饰器创建的 ufunc,说明用于编译的数据类型转换
  • numba_ dtype_opt_sum,使用矢量化装饰器创建的 ufunc,说明编译的数据类型转换以及优化性能的参数

对谈话进行编码:

结果是:

加法运算的计算时间(越少越好)。对于 NumPy 内置,NumPy ufunc(frompypfunc)和几个 Numba ufuncs(矢量化装饰器)[作者图片]

我们可以看到,用 NumPy frompyfunc创建的 ufunc 完全是另一个联盟;计算时间比其他函数大几个数量级。然而,如果我们忽略它,我们可以看到 Numba 的 ufuncs 性能与 NumPy 的内置add功能相当,并且它们之间是相似的。

如果我们想创造高性能的 ufuncs,Numba 是必由之路。除非性能很关键,否则vectorize装饰器可以在没有参数的情况下使用。

NumPy 的优势之一是它的性能,为 Python 带来了速度。让我们面对它;速度不是 Python 的优势之一。因此,有必要区分 C 或 Fortran 矢量化计算与纯 Python 矢量化。速度不会一样。

ufunc.reduce

NumPy 中有些函数不是 ufuncs 大多数人都接受 axis 关键字。这些函数非常棒,因为它们允许我们指定是将函数操作应用于整个数组,还是只应用于轴的子集,例如np.sum

假设我们有一个 2D 阵列,并且不指定轴(或者同时指定两个轴)。在这种情况下,函数返回一个标量和整个数组的和,但是如果我们指定轴零(1),函数将返回列(行)和的向量。

我们可以检查np.sum是一个普通的 Python 函数,而不是 ufunc:

这就是我们可以使用函数和 axis 参数做的事情:

打印总和:

36

36

[ 9 12 15]

[ 3 12 21]

在引擎盖下,np.sum本质上是 np.add功能的还原方法的应用:

36

36

[ 9 12 15]

[ 3 12 21]

所以,如果我们可以从 add ufunc 中恢复 NumPy sum 函数,那么 Numba 的 add ufunc 呢?当然,我们也可以使用 reduce。让我们用vectorized装饰器来定义函数:

测试它:

36

36

[ 9 12 15]

[ 3 12 21]

我们是金色的。

讨论 NumPy 的 apply _ 沿 _ 轴函数是至关重要的。它可以简化 Python 的常规函数:

[ 9 12 15]

[ 3 12 21]

但是,这样做并不会利用 NumPy 的性能。这比使用编译的 ufunc 要慢得多。所以要小心。

ufunc.accumulate

另一个有用方法是积累。对于 sum,它类似于函数(普通 Python) np.cumsum 所做的:

[[ 0 1 2]

[ 3 5 7]

[ 9 12 15]]

[[ 0 1 3]

[ 3 7 12]

[ 6 13 21]]

我们可以在 add ufunc 中使用 accumulate 方法,得到相同的结果:

[[ 0 1 2]

[ 3 5 7]

[ 9 12 15]]

[[ 0 1 3]

[ 3 7 12]

[ 6 13 21]]

我们也可以在 Numba 的 ufunc 中使用它:

[[ 0 1 2]

[ 3 5 7]

[ 9 12 15]]

[[ 0 1 3]

[ 3 7 12]

[ 6 13 21]]

最终意见

正如我们在这个故事中所看到的,强调并非所有的人生来平等是至关重要的。在 ndarrays 上应用自定义函数时,即使使用 frompyfunc 或 apply_along_axis 等 NumPy 工具,找出代码中的性能瓶颈也是至关重要的。

所以下次在数组上使用函数时,问问自己它是否为矢量化运算进行了适当的优化。

关于 ufunc 的创建,Numba 的实用程序在性能方面是一个不错的选择。

我希望这个故事对你有用。 订阅 到我的邮件列表如果你想知道更多这样的故事。

喜欢这个故事吗?通过我下面的推荐链接成为一个媒体成员来支持我的写作。无限制地访问我的故事和许多其他内容。

https://medium.com/@diego-barba/membership

你的 Python For-loop 慢吗?请改用 NumPy

原文:https://towardsdatascience.com/numpy-vectorization-speed-ffdab5deb402

当速度很重要时,列表不是最好的。

来自 Pexels 的 Alireza Kaviani 拍摄的照片。

速度一直是开发人员关心的问题——尤其是对数据敏感的工作。

迭代能力是所有自动化和扩展的基础。我们所有人的首要选择是 for 循环。它优秀、简单、灵活。然而,它们不是为扩展到大规模数据集而构建的。

这就是矢量化的用武之地。当您在 for 循环中进行大量数据处理时,请考虑矢量化。Numpy 在这里派上了用场。

这篇文章解释了 NumPy 操作与 for 循环相比有多快。

用 NumPy 比较 For 循环

我们来做一个简单的求和运算。我们必须总结一个列表中的所有元素。

sum 是 Python 中的一个内置操作,可以在一系列数字上使用。但是让我们假设没有,您需要实现它。

任何程序员都会选择遍历列表并将数字添加到变量中。但是有经验的开发人员知道它的局限性,会选择优化的版本。

这是我们总结的列表和数字版本。在这个例子中,我们创建了一个包含 100 万个 0 到 100 之间的随机数的数组。然后我们使用这两种方法并记录执行时间。

我在比较 NumPy sum 和 list 迭代的速度。—作者摘录。

让我们运行这个程序,看看我们会得到什么。输出可能如下所示。

$ python main.py 
Summation time with for-loop:  14.793345853999199
Summation time with np.sum:  0.1294808290003857

NumPy 版本更快。这大约是循环所用时间的百分之一。

使用 Numpy 加速计算的更多示例

NumPy 大量用于数值计算。也就是说,如果您正在处理庞大的数据集矢量化,NumPy 的使用是不可避免的。

大多数机器学习库使用 NumPy 来优化算法。如果你曾经创建过 scikit learn to model,你应该已经使用过 NumPy 了。

这里还有一些处理大量数字数据时经常用到的例子。

NumPy 与列表中乘积的总和

这是一种流行的数值计算,你甚至可以在 Excel 上使用。让我们来衡量一下 lists 和 NumPy 版本的性能。

下面的代码将一个数组中的每个元素与另一个数组中的相应元素相乘。最后,我们总结所有的单个产品。

下面是上面代码的输出:

$ python main.py 
Sum of products with for loop:  26.099454337999987
Sum of products with np.sum:  0.28206900699990456

同样,NumPy 版本比遍历列表快 100 倍。

NumPy 和链表的矩阵乘法性能。

矩阵乘法是和积的扩展版本。它涉及的不是单个数组,而是数组的数组。

在实现涉及大量数据的算法时,矩阵乘法也非常常见。这里是基准。

$ python main.py
Matrix multiplication with for loop:  1597.9121425140002
Matrix multiplication with numpy:  2.8506258010002057 

使用 NumPy 的结果是深远的。我们的矢量化版本运行速度快了 500 多倍。

随着数组的大小和维度的增长,NumPy 的优势更加突出。

为什么 NumPy 比 lists 快?

简单;它们被设计用于不同的目的。

NumPy 的角色是为数值计算提供一个优化的接口。然而,Python 列表只是对象的集合。

NumPy 数组只允许同类数据类型。因此,NumPy 操作在算法的每一步之前都不必担心类型。这就是我们提高速度的地方——快速取胜。

同样,在 NumPy 中,整个数组,而不是单个元素,是一个被称为密集打包的对象。因此,它需要更少的内存。

此外,NumPy 操作(主要是)是用 C 实现的,而不是用 Python 本身。

Python 中的列表只不过是一个对象存储。单个对象占用空间,你很快就需要更多的内存来处理它们。此外,列表可以容纳不同类型的对象。但是不利的一面是,您必须对每个操作进行元素类型检查。这使得成本很高。

最后的想法

这篇文章鼓励你将列表转换成 NumPy 数组,并使用向量化操作来加速执行。

人们在列表中使用 for 循环是很自然的,因为这很简单。但如果涉及到很多数字,就不是最优的方式。为了更好地理解它,我们比较了一些简单运算的性能,比如求和、和积和矩阵乘法。在所有情况下,NumPy 的表现都远远好于 lists。

For 循环在编程中也有它们的位置。经验法则是当你的数据结构更复杂,需要迭代的项目更少时使用它们。

没有 NumPy 的几百个数相加可能更好。此外,如果在每次迭代中你必须做比数值计算更多的工作,NumPy 不是你的选择。

感谢阅读,朋友!在LinkedInTwitterMedium上跟我打招呼。

还不是中等会员?请使用此链接 成为会员 因为,在没有额外费用的情况下,我赚取了一点佣金。

基于张量流的 NYT 情感分析

原文:https://towardsdatascience.com/nyt-sentiment-analysis-with-tensorflow-7156d77e385e

乔恩·泰森在 Unsplash 上的照片

现在我们进入拜登政府一年了,我开始想知道与前几任总统的头几年相比,他执政第一年的新闻报道有多积极。为了找到答案,我决定对过去四位总统执政第一年的每个月的 NYT 文章摘要进行情感分析。下面,我讨论/显示以下步骤的代码:

  1. 使用 NYT API 纽约时报
  2. 使用文本 Blob 进行情感分析
  3. 用 TensorFlow 训练我自己的情感分析模型
  4. 比较模型
  5. 可视化结果

完整代码,请点击这里下载 Jupyter 笔记本

第一步。使用 NYT API 纽约时报

我们将导入所需的包,连接到 API,创建一个字典来保存我们的结果,提取相关的数据,将我们的字典保存到一个 JSON 文件中,这样我们就不必再次提取数据,并关闭我们与 API 的连接。

步骤 2:使用文本块进行情感分析

我们将再次导入必要的包。然后,我们将在一些随机选择的抽象上测试该模型,以检查其合理性。

当我运行这个抽查时,我注意到 TextBlob 相当不准确。例如,下面的摘要被标为“积极的”:警方表示,午夜过后不久,曼谷一家高端夜总会发生火灾,造成至少 59 人死亡,200 多人受伤,当时狂欢者正在庆祝新年。

第三步。用 TensorFlow 训练我自己的情感分析模型

因为 TextBlob 似乎做得不太好,所以我决定练习我的 ML 技能,并使用 TensorFlow 建立一个情感分析模型(本教程非常有帮助)。首先,我们将导入所需的包,并加载我们将用于训练/测试的数据集。我在一个 1.6 毫米的标注 tweets (标注为正面或负面)的数据集上训练了我的模型。然后,我们将以 80/20 的比例随机拆分用于训练/测试的数据,并将 tweets 及其标签重新格式化为 numpy 数组,这样我们就可以在训练模型时将它们作为输入进行加载。

接下来,我们将使用 keras 创建一个序列模型。我们模型的第一层会把句子作为输入,转换成数值的向量(这叫做“单词嵌入”)。幸运的是,有人创建了一个可以做到这一点的模型,可以从 tensorflow-hub 下载。我在这一层使用的模型可以在这里找到。然后我们将添加两个隐藏层和一个输出层。我们的隐藏层分别有 16 个和 8 个节点,并且都使用 ReLU 激活函数。我们的输出层有 1 个节点,因为这是一个二元分类问题,我们使用 sigmoid 激活函数。最后,我们将使用 Adam 优化器编译我们的模型,使用 BinaryCrossentropy 计算损失,并使用阈值为 0.5 的 BinaryAccuracy 计算准确性(如果我们的模型预测句子为肯定的可能性≥0.5,我们将把句子分类为肯定的)。

接下来,我们将留出一些训练数据用于训练过程中的验证。然后,我们将训练模型,评估结果,并使用混淆矩阵可视化我们的模型在测试数据上的表现。

因此,使用我们的测试数据集,我们有 79%的准确率。从我们的混淆矩阵中,我们可以看到,当推文是正面的,但我们预测它是负面的时,我们的大多数错误都会发生。所以,我们对模型有一点负面的偏见。但是,让我们看看这 79%是否比 TextBlob 做得更好。

第四步。比较模型

如果我们使用 TextBlob 对相同的测试数据集进行分类,我们只能达到 62%的准确率。注意:TextBlob 预测“中性”情绪以及积极和消极情绪。因此,这不是一个直接的比较,但仍然是有帮助的。在下面的测试中,我们随机将 TextBlob 的“中性”预测重新分类为“正面”或“负面”【TensorFlow 的 79%准确率明显优于 TextBlob 的 62%准确率。

两个模型在摘要样本上的性能的直接比较可以在下面找到。幸运的是,通过 TensorFlow 模型,我们现在可以准确地将关于火灾、死亡和伤害的标题归类为“负面”总的来说,它在对我们的摘要进行分类时似乎更加准确,尽管仍然不完善。

第五步。可视化结果

首先,我们将使用我们的模型来预测我们在步骤 1 中提取的所有摘要的情感。然后,我们将计算每个月积极/消极情绪的百分比,并将其添加到我们的字典中。

然后,我们将把数据重新格式化成只包含我们想要可视化的关键统计数据的数据帧。然后我们将创建一些图表来更好地理解结果!(下载笔记本看看我是如何用 seaborn 创建这些图表的)

结论

这个项目对我更加熟悉 TensorFlow 中的建筑模型非常有帮助。TextBlob 就是没剪!我在 TensorFlow 中构建的模型要精确得多,尽管它明显有一点负面偏差(正如我们从混淆矩阵中了解到的)。

我用张量流模型得到的结果非常有趣。基本上,在布什执政的第一年,新闻是最负面的。就所有新闻(只有 25%的正面)和直接提到“布什”的新闻摘要(只有 28%的正面)而言,都是如此。另一方面,在特朗普的第一年,新闻通常是最积极的(34%积极),直接新闻报道对奥巴马最积极(63%积极)。有趣的是,直接提到拜登的摘要(57%)比直接提到特朗普的摘要(52%)更负面。这有点令人惊讶,因为 NYT 公开反对川普。

我打算对我提取和分类的数据进行额外的分析,包括查看最常用的词,以更好地理解这些意想不到的结果。例如,在特朗普执政的第一年,总体新闻报道最为积极,这可能是因为 2017 年我们的危机比 2001 年(互联网泡沫破裂、9/11 袭击)、2009 年(大衰退)和 2021 年(挥之不去的新冠肺炎疫情)更少。我期待着进一步探索这些数据!

对象检测神经网络:在自定义数据集上构建 YOLOX 模型

原文:https://towardsdatascience.com/object-detection-neural-network-building-a-yolox-model-on-a-custom-dataset-77d29d85ae7f

综合指南

照片由马太·亨利拍摄

在计算机视觉中,对象检测是检测图像或视频中的对象的任务。目标检测算法工作有两个方面

  • 一个定位任务 —输出包围盒(x,y 坐标)。本质上,定位任务是一个回归问题,它输出表示边界框坐标的连续数字。
  • 一个分类任务——对一个物体(人与车等)进行分类。).

目标检测算法可以基于传统的计算机视觉方法或神经网络方法[1]。在基于神经网络的方法中,我们可以将算法分为两大类:单阶段和两阶段对象检测器。

单级物体检测器中,定位和分类任务一次完成,这意味着没有额外的网络来“帮助”物体检测器的定位和分类过程。单级检测器产生更高的推理速度,更适合移动和边缘设备。YOLO 系列物体检测算法是单级物体检测器。

两阶段检测器中——除了定位和分类网络,我们还有一个额外的网络,称为区域建议网络(RPN)。RPN 用于决定“在哪里”寻找,以便降低整个对象检测网络的计算要求。RPN 使用锚点—固定大小的参考边界框,均匀地放置在整个原始图像中。在区域提案阶段[2] —我们问

  • 这个锚包含相关对象吗?
  • 我们如何调整锚点以更好地适应相关对象?

一旦我们从上述过程中得到一个列表,本地化和分类网络的任务就变得简单明了。两级检测器通常具有更高的定位和分类精度。RCNN、fast-RCNN 等。是两级物体检测器的几个例子。

物体检测器的一般结构

图像是对象检测网络的输入输入通过卷积神经网络(CNN) 中枢从中提取特征(嵌入)。颈部阶段的工作是混合和组合 CNN 主干中形成的特征,为头部步骤做准备。颈部的组件通常在层之间上下流动,并且仅连接【7】末端的几层。负责预测——aka。分类和本地化。

图 1:基于深度学习的对象检测通用架构(来源: Bochkovskiy 等人,2020 ,第 2 页,图 2)

YOLO 系列物体探测器[3]是最先进的(SOTA)单级物体探测器。YOLO 家族从 2016 年的 YOLOv1 开始发展到 2021 年的 YOLOX。在本文中,我们将重点关注 YOLOX。

YOLOx 建筑

图 2:耦合头与解耦头(资料来源:郑等,2021,第 3 页,图 3)

让我们看看 YOLOX 的一些架构组件。

去耦头

对于 YOLOv3-v5,探测头保持耦合。这带来了挑战,因为检测头基本上执行两种不同的任务——分类和回归(边界框)。用一个解耦的头代替 YOLO 的头——一个用于分类,另一个用于包围盒回归。

强大的数据增强

YOLOX 增加了两种数据增强策略——镶嵌和混合。

  • 马赛克最初是在 YOLOv3 中引入的,随后在 v4 和 v5 中使用。镶嵌数据增强将 4 幅训练图像以一定的比例组合成一幅。这允许模型学习如何在比正常情况下更小的尺度上识别对象。这在培训中也是有用的,以显著减少对大的小批量的需求[4]。
  • 混合——是一种数据扩充技术,从训练数据中生成随机图像对的加权组合[5]。

无锚架构

YOLOv3-5 是基于锚点的管道——这意味着 RPN 风格的固定大小的参考边界框被均匀地放置在整个原始图像中,以检查该框是否包含预期的类。锚定框允许我们在同一个网格中找到多个对象。但是基于锚的机制也有一些问题—

  • 在基于锚的机制中,目标检测需要确定最优的锚盒。为此,我们需要在训练之前通过聚类分析找到最佳锚盒。这是增加训练时间和复杂性额外步骤。
  • 另一个问题是基于锚的机制增加了对每个图像进行预测的数量。这样会增加推断时间。
  • 最后,基于锚的机制显著增加了检测头和整个网络的复杂性。

近年来,无锚机制得到了改进,但直到 YOLOX 才被引入 YOLO 家族。

无锚点 YOLOX 将每个图像单元的预测数量从 3 减少到 1。

西蒙塔

SimOTA 是一种先进的标签分配技术。什么是标签分配?它为每个基本事实对象定义正/负训练样本。YOLOX 将这个标签分配问题公式化为最优运输(OT)问题[6]。

下表显示了这些不同的架构组件如何帮助提高模型的平均精度(AP)。

表 1:基于不同技术的 YOLOX 性能(资料来源:郑等,2021,第 4 页,表 1)。2)

在自定义数据集上训练 YOLOX

装置

你可以在这里找到 YOLOX 的开源代码。按照安装部分,您可以从源代码安装

git clone [git@github.com](mailto:git@github.com):Megvii-BaseDetection/YOLOX.git
cd YOLOX
pip3 install -v -e .  # or  python3 setup.py develop

数据集转换

确保您的自定义数据集为 COCO 格式。如果您的数据集是 darknet 或 yolo5 格式,您可以使用 YOLO2COCO 存储库将其转换为 COCO 格式。

下载预先训练的重量

当在自定义数据集上训练我们的模型时,我们更喜欢从预训练基线开始,并在其上训练我们的数据。

wget https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_s.pth

模特培训

YOLOX 模型培训的一个关键组成部分是拥有正确的实验文件——这里有一些示例自定义实验文件。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Copyright (c) Megvii, Inc. and its affiliates.
import os

from yolox.exp import Exp as MyExp

class Exp(MyExp):
    def __init__(self):
        super(Exp, self).__init__()
        self.depth = 0.33
        self.width = 0.50
        self.exp_name = os.path.split(os.path.realpath(__file__))[1].split(".")[0]

        # Define yourself dataset path
        self.data_dir = "datasets/coco128"
        self.train_ann = "instances_train2017.json"
        self.val_ann = "instances_val2017.json"

        self.num_classes = 71

        self.max_epoch = 300
        self.data_num_workers = 4
        self.eval_interval = 1

您可以根据您拥有的类的数量来试验变量,如self.num_classes,更改数据集路径等。

你可以在这里看到另一个自定义实验文件的例子

import os
from yolox.exp import Exp as MyExp

class Exp(MyExp):
    def __init__(self):
        super(Exp, self).__init__()
        self.depth = 0.33
        self.width = 0.50
        self.exp_name = os.path.split(os.path.realpath(__file__))[1].split(".")[0]

        # Define yourself dataset path
        self.data_dir = "data"
        self.train_ann = "train.json"
        self.val_ann = "val.json"

        self.num_classes = 1
        self.data_num_workers = 4
        self.eval_interval = 1

        # --------------- transform config ----------------- #
        self.degrees = 10.0
        self.translate = 0.1
        self.scale = (0.1, 2)
        self.mosaic_scale = (0.8, 1.6)
        self.shear = 2.0
        self.perspective = 0.0
        self.enable_mixup = True

        # --------------  training config --------------------- #
        self.warmup_epochs = 5
        self.max_epoch = 300
        self.warmup_lr = 0
        self.basic_lr_per_img = 0.01 / 64.0
        self.scheduler = "yoloxwarmcos"
        self.no_aug_epochs = 15
        self.min_lr_ratio = 0.05
        self.ema = True

        self.weight_decay = 5e-4
        self.momentum = 0.9

要训练模型(确保您在 YOLOX 目录中),您可以使用下面的命令。注意db的参数。d表示 GPU 的数量,在我这里是1b是训练时的批量8xd的值。

python tools/train.py -f exps/example/custom/yolox_tiny.py -d 1 -b 8 --fp16 -o -c yolox_tiny.pth

模型评估

在模型评估期间,您必须指定用于训练的相同(或其副本)实验文件

python -m yolox.tools.eval -n  yolox_s -c YOLOX_outputs/yolox_s/best_ckpt.pth -f exps/example/custom/yolox_s.py -b 1 -d 1 --conf 0.001 --fp16 --fuse

模型检验

为了在测试图像上运行 YOLOX 推理,我们可以做下面的[8]

TEST_IMAGE_PATH = "test_image.jpg"

python tools/demo.py image -f exps/example/custom/yolox_s.py -c YOLOX_outputs/yolox_s/best_ckpt.pth --path {TEST_IMAGE_PATH} --conf 0.25 --nms 0.45 --tsize 640 --save_result --device gpu

假设输出的预测图像是prediction.jpg。你可以像下面这样想象这个图像

from PIL import Image
Image.open('prediction.jpg')

要检查您的模型在视频上的表现,您可以执行以下操作

python tools/demo.py video -n yolox-s -c /path/to/your/yolox_s.pth -f exps/example/custom/yolox_s.py --path /path/to/your/video --conf 0.25 --nms 0.45 --tsize 640 --save_result --device [cpu/gpu]

请注意这里的参数conf——它表示置信区间,意味着在输出边界框之前,您希望您的模型有多自信。拥有更高的conf分数通常会提高检测的质量——移除不需要的边界框等。

前进路径:

既然我们已经学会了如何在自定义数据集上训练 YOLOX 前面还有许多可能性。根据您的使用情况,您可能想要一个更轻或更重的 YOLOX 版本。您可以使用``exps/example/custom/yolox_s.py`中定义的实验文件中的参数进行实验。一旦模型准备好,根据您的使用情况,您可能需要修剪或量化模型,特别是如果您正在边缘设备上部署它。

我希望你喜欢这篇博文。如果你有任何问题,请在这里随意评论。

参考资料:

  1. 20 年来的物体探测:一项调查https://arxiv.org/pdf/1905.05055.pdf
  2. 一阶段对两阶段物体检测器https://stack overflow . com/questions/65942471/一阶段对两阶段物体检测
  3. YOLO 家族简介https://pyimagesearch . com/2022/04/04/introduction-to-the-yolo-family/
  4. YOLOv4 数据增强https://blog.roboflow.com/yolov4-data-augmentation/
  5. https://paperswithcode.com/method/mixup
  6. 物体检测的最佳运输分配https://arxiv.org/pdf/2103.14259.pdf
  7. 约洛夫 4https://blog.roboflow.com/a-thorough-breakdown-of-yolov4/彻底崩溃
  8. 如何训练一个定制的 YOLOX 模型https://blog . robo flow . com/how-to-training-YOLOX-on-a-custom-dataset/

基于卷积神经网络的目标检测

原文:https://towardsdatascience.com/object-detection-with-convolutional-neural-networks-c9d729eedc18

多阶段(RCNN,快速 RCNN,更快的 RCNN)和单阶段(SSD,YOLO)结构的对象检测和他们的使用训练自己的对象检测模型进行解释

在我之前的帖子中,我们学习了什么是图像分类以及如何创建图像分类模型。

现在是时候进一步了解对象检测了。

物体检测(物体识别)

  • 虽然图像分类问题侧重于对图像进行分类,但在一幅图像中,我们可能要搜索不止一个类别,在对象识别中,我们的任务是找到所有这些类别,并将其放入最合适的

包围盒— ROI(感兴趣区域):物体识别需要满足的新名词。当我们试图识别物体时,我们使用包围盒,在其中物体可能被检测到。我们将学习如何获得尽可能接近后来探测到的物体的盒子。

作者图片

  • 正如你可能注意到的,物体识别是一个比图像分类更复杂的任务,在图像分类中,我们试图定位识别图像中的物体。

作者图片

再次继续这 3 个不同的任务:

图像分类:预测图像中物体的类别

目标定位:定位图像中目标的存在,并用边界框指示其位置。

物体检测:用一个包围盒定位物体的存在,并检测这些包围盒中被定位物体的类别

迄今为止创建的对象识别神经网络架构分为两个主要组:多级单级检测器。

多级探测器

  1. RCNN 2014
  2. 快速 RCNN 2015
  3. 更快的 RCNN 2015

单级探测器

  1. 固态硬盘 2016
  2. YOLO 2016
  3. 约洛夫 2 2016 ,约洛夫 3 2018 ,约洛夫 4 2020 ,约洛夫 5 2020

让我们从检查多级检测器开始🔮

多级探测器

正如我们从名称中所理解的,这些是具有两个独立阶段的对象检测器。通常,他们首先提取一些感兴趣的区域(边界框),然后对这些框进行分类以获得最终结果。这就是它们被称为基于区域的卷积神经网络的原因

  • RCNN

作为这个家族的第一个成员,我们将在未来的版本中看到基础方法论及其缺点的改进。该模型的方法如下:

  1. 使用选择性搜索从输入图像中提取区域建议

选择性搜索算法的工作方式是,它应用一种分割算法来寻找图像中的斑点,从而计算出什么可能是物体。选择性搜索递归地将这些区域组合成更大的区域,以创建2000 个待调查区域。

作者图片

分段的选择性搜索

在下面的帖子中,我提到了不同的算法来应用图像分割,不使用基于人工智能的方法,而是使用经典的基于计算机视觉的方法。

在同一类别中进行选择性搜索,它应用以下步骤来分割图像:

首先计算所有相邻区域之间的相似性。

将两个最相似的区域组合在一起,并计算结果区域与其相邻区域之间的新相似性。

然后重复该过程,直到整个对象被覆盖在单个区域中。

2.在每个 ROI 上使用 CNN 的特征提取来自上一步

在根据分割提取了可能具有对象的几乎 2000 个可能的框之后,CNN 被逐一应用于所有这些框,以提取用于下一步分类的特征

3.利用 SVM 和包围盒预测进行分类

最后,使用 SVM(支持向量机)进行分类,并使用包围盒回归器,该模型给出最终的包围盒以及检测到的类,其中包围盒回归器的任务只是改进提议的包围盒以更好地包围对象。

作者图片

这是 RCNN 的主要方法论,现在让我们看看它的弱点

  • 选择性搜索已经是一种复杂的算法,仅在第一步使用它会大大增加模型的计算成本。->
  • 获得 2000 个区域以逐个应用特征提取又是太多的计算!->

这给了我们 47 秒用于 1 个图像检测,因此不可能使用该模型进行实时物体检测任务!

  • 它不是一个端到端的可训练模型,因为选择性搜索算法不是一种可训练的方法,这使得不可能通过训练 RCNN 来开发区域提议。-> 某些部位不可训练
  • 使用 SVM 是没有端到端架构的另一个原因,我们需要分别培训 SVM 和 CNN,这带来了更困难的任务。

因此,尽管它是一种先进的架构,比以前的模型具有更好的精度,但很明显,该模型需要改进,尤其是在速度性能方面。出于这个原因,我们发现自己正在检查快速 RCNN 模型,它是RCNN的改进版本。

  • 快速 RCNN
  1. 它改变了区域提议步骤和特征提取的顺序,因此我们首先将 CNN 应用于输入图像,然后提取 ROI。这样,我们不用对 2000 个不同的区域应用 CNN,而是只应用一次,这提高了模型的速度性能。- > 不再那么慢了
  2. 快速 RCNN 带来的另一个变化是使用带有 softmax 输出激活功能的全连接层,而不是 SVM ,这使得模型更加集成为一体式模型。- > 单步训练
  3. 为了使来自区域提议的区域大小适应完全连接的层,应用 ROI 最大池。
  4. 边界框回归器和分类任务都是通过完全连接的层实现的,因此在这两个不同的输出成本组合起来更新模型的情况下应用“多任务损失”。-> 单阶段训练

因此,我们获得了比 RCNN 快 9 倍的用于训练,比 RCNN 快 0.32 秒的用于检测 1 幅图像。****

虽然这是个好消息,但是快速 RCNN 仍然有 RCNN 的一个缺点!即使我们不为快速 RCNN 中的每个建议区域应用 CNN,使用选择性搜索提取 2000 个区域建议仍然是一个问题,这使得模型不必要地复杂。

  • 更快的 RCNN

可以想象,更快的 RCNN 的主要变化是使用另一种方法而不是选择性搜索算法来提出区域。这种方法是使用一个单独的神经网络来学习区域提议

因此,该模型首先使用 1 个 CNN 来获得区域建议,然后遵循与快速 RCNN 完全相同的逻辑来检测对象,因此它使用这些建议来用 CNN 提取特征,然后用全连接层进行分类,并使用包围盒回归器来改进 ROI。

如你所知,Fast-RCNN在某些资源中被称为 RPN + Fast-RCNN ,因为从 Fast 到 Fast RCNN 的唯一更新是区域提议方法。

RPN :地区提案网。在这个使用 inf 快速 RCNN 的网络中,主要目标是预测锚盒的偏移以获得最终的包围盒。其中锚框是一些默认大小和形状的框。

随着这些逐步的改进,我们看到模型的速度有了很大的提高,尤其是在快速 RCNN 中

作者图片

mAP :平均精度百分比是对象检测模型最常见的精度度量,其中计算每个类别的精度,然后取平均值。关于地图计算的更多信息,可以看看这篇很棒的帖子。

https://jonathan-hui.medium.com/map-mean-average-precision-for-object-detection-45c121a31173

单级检测器

现在该检查单级检测器了,与多级检测器相反,在单级检测器中,盒子预测和分类是同时进行的。

SSD:单次多盒探测器

  • 标准的预训练神经网络被用作特征提取器,如 VGG16 、VGG19 或 ResNet。
  • 在这个 CNN 之后,应用一些附加的卷积层来获得不同大小的特征图。在获得边界框之后,这些特征图中的一些和来自特征提取网络的输出图被发送到由全连接层组成的图像分类部分
  • 对于这些特征图中的每个单元,我们提取 4 或 6 个边界框,将它们直接发送到完全连接的层。与多级检测器相反,我们不对边界框做任何预测,我们只获取来自不同卷积层的特征图,并将它们转换成网格单元,其中每个像素为 1 个单元。以这些单元格为中心点,我们产生 4 或 6 个不同的边界框。
  • 为了产生边界框,我们有固定大小和形状的锚框,预定义的框。

作者图片

  • 非最大抑制机制被应用作为最后一步,该步骤决定是否有多于一个的盒子指向相同的对象,我们选择最佳拟合。

作者图片

  • 要了解更多关于非最大值抑制机制以及如何选择最佳拟合边界框的信息,您可以阅读这篇不错的帖子:

让我们想象一下固态硬盘架构:

作者图片

  • 我们看到整个架构有 8732 个包围盒,我们需要决定每个包围盒是否有类对象。我们有 8732 边界框的原因是:

con v4 _ 3:38×38×4 = 5776 盒(每个电池 4 盒)
con V7:19×19×6 = 2166 盒(每个电池 6 盒)
Conv8_2: 10×10×6 = 600 盒(每个电池 6 盒)
Conv9_2: 5×5×6 = 150 盒(每个电池 6 盒)
con v10 _ 2:3×4 = 36 盒

5776+2166+60ch 0+150+36+4 = 8732

  • SSD 在 VOC2007 上以 59 FPS 实现了 74.3%的 mAP1,而更快的 RCNN 只有 10 FPS。对于速度来说,这是非常令人印象深刻的进步,对吗?!

每幅图像的 FPS 与秒:我们比较了 RCNN 家族使用“秒”单位的速度,因为他们的论文大多使用这个单位。每幅图像的是解释预测 1 幅图像需要多少秒。所以值越小,表示性能越好。另一方面, FPS 表示每秒,这个单位说明了我们在 1 秒钟内可以预测多少帧图像。因此,数值越大,性能越好

YOLO:你只看一次

其基本思想与固态硬盘非常相似,以下是 YOLO 的一些不同之处:

  • 它使用基于 Googlenet 架构名称 Darknet 的定制网络。这个暗网架构由 24 个卷积层和 2 个全连接层组成。
  • 卷积部分使用 Imagenet-1000 数据集进行预训练,来自 CNN 的特征图用于转换为网格单元,就像 SSD 中一样。全连接层用于从这些特征地图网格预测边界框坐标
  • 最后,我们有 98 个职业的预测,而我们在 SSD 中有 8732 个职业的预测,但是 YOLO 有 45 FPS 和 63.4mAP,所以都比 SSD 低。

YOLO 在 SSD 之前发布,所以我们可以说它是第一个单级探测器,虽然 SSD 没有任何更新的版本,但 YOLO 到目前为止有 5 个不同的版本。让我们看看这个模型是如何随着时间的推移得到改进的。

YOLO 有一些弱点,比如:

  • 本地化(所以边界框有问题…)
  • 规模问题(来自训练数据集的不同大小的对象是一个问题)

YOLOv2

获得 67 FPS 和 76.8 mAP !不错的改进,但如何改进?

  • 卷积层增加批量归一化
  • 高分辨率分类器:原 YOLO 以 224 × 224 训练分类器网络,提高分辨率到 448 进行检测。这意味着网络必须同时切换到学习对象检测并适应新的输入分辨率。对于 YOLOv2,输入图像分辨率提高至 448 × 448。
  • 锚框:锚框用于计算偏移,而不是使用完全连接的层来预测边界框坐标。但与其他锚框方法不完全一样,他们不是手动选择先验,而是在训练集边界框上运行 k 均值聚类,以自动为锚框找到良好的先验。
  • 基本模型架构略有变化,名称为 Darknet-19 ,有 19 个卷积层和 5 个最大池层,完全连接的层被删除,如我上面提到的🐟

YOLOv3

  • 它使用的是逻辑量词(像 sigmoid!)不像以前的版本那样使用 softmax,使 1 个对象属于 1 个以上的类。例如,检测到的 1 个对象可以同时是狗和动物,对吗?如果预测高于给定的阈值,这些预测将传递到输出,因此您可以看到 1 个边界框同时表示 2 个类!
  • 与 YOLOv2 只将 1 个框(最好的一个)分配给地面真实对象不同,在这一个中,如果边界框超过给定的阈值(使用 0.5),则进入损失计算,用于边界框预测、对象和类别预测。如果它不能通过,它只进行盒预测。(看看论文中的包围盒预测副标题吧!)
  • 由于使用了特征提取器 Darknet-53 而不是稍微深一点的 Darknet-19,因此更准确但速度更慢

在论文中,我找不到 VOC 数据集的准确性和速度指标,我以前一直将它与以前的模型进行比较,直到现在。所以我只能分享COCO 数据集上的一些结果

yolov 3–320 给出 51.5 图,推理时间 22ms,
yolov 3–416 给出 55.3 图,推理时间 29 ms,
yolov 3–608 给出 57.9 图,推理时间 51 ms,

然而

更快的 RCNN 给出了 59.1 的 mAP,推理时间为 172 毫秒
,并且

yolov 2–608 给出了 48 个图,推理时间为 40 毫秒

注意-320,-416,-608 是输入图像分辨率。

⏰ 👽在继续之前稍微停顿一下!正如我检查论文一样,我意识到一些“准确性”指标可能会令人困惑,因为一些论文给出了 VOC 结果,一些论文给出了不同架构的 COCO 结果(小版本、大版本、不同主干的不同实验等)。我可以说,更快的 RCNN 通常比 YOLO 模型有更好的准确性,而 YOLO 模型几乎总是比它快,直到现在

主干:物体检测模型的特征提取器部分。通常,我们在之前的 中看到的图像分类架构像 VGG、瑞思网等

YOLOv4

  • YOLOv4 通过使用 BoF ( 赠品袋)和几个 BoS(特价袋)来改进 YOLOv3 的模式。BoF 提高了检测器的准确性,而不增加推断时间。它们只会增加培训成本。另一方面,BoS 在显著提高目标检测精度的同时,增加了少量的推理成本。
  • 该模型使用 Darknet 中的跨级局部网络(CSPNet),创建了一个新的特征提取器主干,称为 CSPDarknet53 。卷积架构基于改进的 DenseNet。
  • 因此,就 FPS 而言, YOLOv4 比 YOLOv3 精度高%10,速度快%12。

一袋赠品:我们把这些只改变训练策略或者只增加训练成本的方法称为“一袋赠品”。" "[1]关于免费赠品袋对物体检测模型训练的影响的更详细研究请参考本文:https://arxiv.org/pdf/1902.04103.pdf

约洛夫 5

这可能是讨论最多和最引人注目的 YOLO 模型,因为没有官方文件,但 Roboflow 初创公司在比较 YOLOv5 和 YOLOv4 时获得了非常令人印象深刻的结果。

它使用 Pytorch 代替 c 中实现的 Darknet。

根据他们的结果:

  • YOLOv5 几乎比 YOLOv4 快 3 倍!
  • YOLOv5 比 YOLOv4 小了将近%90!

YOLOv5 的一些关键特性如下:

  • 使用 CSPDarknet53 作为 YOLOv4
  • 镶嵌数据增加被添加到模型中

在结束理论部分之前,我想补充一下,YOLO 有一些“微型”版本,特别是当你想拥有超快的模型时使用。几乎每一个 YOLO 版本(v2,v3,v4,v5)都有它的小 YOLO 版本,精确度稍低,但速度快了将近 4 倍。值得尝试的实时对象检测项目!

理论部分做完了!是时候学习如何真正使用这些模型来为您自己的数据集进行对象检测了。

对于这一部分,我将分享两个非常好的更快的 RCNN 和 YOLOV4 的仓库。

使用更快的 RCNN 进行对象检测

我使用了一个定制的实现库,因为我找不到任何官方的更快的 RCNN 实现。这是一个非常简单的 repo,可以下载并用于 Keras 实现的训练和测试。

在下面的 GitHub 链接中,您会发现我的 repo 是从基础源代码派生出来的,我在其中添加了一些更多的属性,并详细解释了如何构建和使用这个实现。

https://github.com/YCAyca/Faster_RCNN_for_Open_Images_Dataset_Keras

用 YOLOV4 进行物体检测

对于 YOLOV4,我使用了基于 C 的官方实现,并分叉为我的 GitHub repo,其中我添加了关于如何构建如何使用 YOLOV4 的指令。这个实现很难理解和构建,所以我强烈建议查看一下 repo,并按照步骤一步一步来训练和测试您自己的数据集。

https://github.com/YCAyca/darknet

感谢您的关注,我希望这对您自己开始使用对象检测模型有所帮助!

选择性搜索算法官方论文:https://IVI . fnwi . UVA . nl/ISIS/publications/2013/uijlingsijcv 2013/uijlingsijcv 2013 . pdf

RCNN 官方论文:https://arxiv.org/pdf/1311.2524.pdf

快速 RCNN 官方论文:https://arxiv.org/pdf/1504.08083.pdf

更快 RCNN 官方论文:https://arxiv.org/pdf/1506.01497.pdf

SSD 官方论文:https://arxiv.org/abs/1512.02325

https://arxiv.org/pdf/1506.02640.pdf 官方文件:

YOLOv2 官方论文:https://arxiv.org/pdf/1612.08242.pdf

YOLOv3 官方论文:https://arxiv.org/pdf/1804.02767.pdf

[1] Yolov4 官方论文:https://arxiv.org/pdf/2004.10934.pdf

Yolov5 还没有正式论文!

使用 TensorFlow 2 对象检测 API 进行对象检测

原文:https://towardsdatascience.com/object-detection-with-tensorflow-2-object-detection-api-3f89da0f1045

TensorFlow 中基于掩模 R-CNN 的目标检测

乔安娜·科辛斯卡在 Unsplash 上的照片

构建对象检测和图像分割模型与其他模型略有不同。主要是因为你必须使用专门的模型,并以特定的方式准备数据。本文将研究如何使用 TensorFlow 2 对象检测 API 在自定义数据集上执行对象检测和图像分割。

让我们开始吧!

对象检测数据集

在本文中,我们将使用在 Kaggle 上可用的 Coco 汽车损坏检测数据集。它包含损坏的汽车图像。它可以用来训练一个模型来检测汽车和汽车部件的损坏。数据集已经被注释,并且提供了相应的 COCO 文件。

为对象检测准备数据集

如果您想要使用自定义数据集,那么您必须自己进行标注和注释。有许多工具和在线平台可以帮助你实现这一点。如果你想坚持开源,Labelme 是一个很好的选择。

下面的视频展示了如何在汽车数据集上创建多边形。完成注释后,您必须保存它。保存后,Labelme 会将生成的 JSON 文件存储在与数据相同的文件夹中。

作者图片

如果你正在寻找一个在线工具,这里有一些我接触过的平台:

  • robof low Universe提供了众多的物体检测和图像分割数据集。您可以搜索平台并切换汽车图像数据集。如果你选择那条路线,从平台下载 TFRecord 格式。如果您有一个定制的数据集,您也可以在 Roboflow 上执行注释。
  • Ango AI 提供一些公共数据集来启动你的分类和物体检测项目。他们还提供了一个平台,你可以用它来标记和注释图像。
  • Segments AI列出了一些可以克隆到项目中的对象检测和图像分割数据集。您还可以在他们平台上执行注释。

什么是 TensorFlow 2 物体检测 API?

TensorFlow 对象检测 API 是一个开源的计算机视觉框架,用于构建对象检测和图像分割模型,可以在同一幅图像中定位多个对象。该框架适用于 TensorFlow 1 和 2。然而,我们鼓励用户使用 TF 2 版本,因为它包含了新的架构。

TensorFlow 2 对象检测 API 支持的一些架构和模型包括:

模型可以从 TensorFlow 2 检测模型动物园下载。您需要它们相应的配置文件来从头开始训练一个对象检测模型。在这个项目中,我们将使用掩码 RCNN 模型,但您也可以尝试其他模型。

在 Google Colab 上安装 TensorFlow 2 对象检测 API

此时,您现在有了一个对象检测数据集。汽车图像数据和相应的 COCO JSON 文件,或者是您自己创建或下载的数据集。

我们将在 Google Colab 上运行这个项目,以利用免费的 GPU 资源来训练模型。让我们在 Colab 上安装 TensorFlow 2 对象检测 API。第一步是克隆 TF 2 对象检测 GitHub repo:

!git clone [https://github.com/tensorflow/models.git](https://github.com/tensorflow/models.git)

接下来,运行这些命令在 Colab 上安装 TF 2 对象检测 API:

%%bash cd models/research # Compile protos. protoc object_detection/protos/*.proto --python_out=. # Install TensorFlow Object Detection API. cp object_detection/packages/tf2/setup.py . python -m pip install --use-feature=2020-resolver .

在本地安装 TensorFlow 2 对象检测 API

如果您想在本地使用 API,开发人员建议您使用 Docker 安装它:

# From the root of the git repository docker build -f research/object_detection/dockerfiles/tf2/Dockerfile -t od . docker run -it od

接下来,导入对象检测 API 和几个其他常见的数据科学包。如果您能够导入对象检测包,这意味着安装运行成功。

下载对象检测数据集

我们将要训练的模型的数据集和配置文件可以从这个 GitHub repo 下载。从对象检测报告下载配置后,您必须进行一些更改。我们稍后将讨论这些变化。

!git clone [https://github.com/mlnuggets/maskrcnn.git](https://github.com/mlnuggets/maskrcnn.git)

下载面具 R-CNN 模型

下一步是下载我们将微调的屏蔽 R-CNN 模型。提取文件以获取训练好的模型检查点。

压缩文件还包含模型的配置文件。下载完每个模型后,您必须编辑这个文件。

作者图片

让我们看看配置文件中需要更新的项目。

编辑对象检测管道配置文件

克隆这个回购后你将得到的配置文件已经被编辑,可以在 Google Colab 上顺利运行。如果你在其他地方运行这个项目,你需要更新文件路径。简而言之,以下是从 TensorFlow 2 对象检测 API repo 下载 Mask RCNN 配置文件后需要更新的项目:

  • num_classes到 5,因为数据集有 5 个类,headlampfront_bumperhooddoorrear_bumper
  • image_resizer从 1024 减少到 512,减小图像尺寸,从而减少训练时间。
  • num_steps从 20 万到 1000 以减少训练时间。步骤越多,训练模型所需的时间就越长。如果损失仍在减少,验证指标在上升,您可以增加步骤。
  • batch_size = 1指定训练时要输入内存的图像数量。
  • fine_tune_checkpoint指向上面下载的 Mask R-CNN 模型的路径。这确保了我们不是从零开始训练模型。
  • classificationfine_tune_checkpoint_typedetection,因为我们正在训练一个物体检测模型。
  • train_input_reader指向label_map_path和 TFRecords 的路径。稍后再谈 TF 唱片。
  • eval_input_readertrain_input_reader相同,但用于测试数据。

将图像转换为 TFRecords

物体检测模型期望图像为 TFRecord 格式。幸运的是, TensorFlow 2 对象检测 API repo 提供了执行转换的脚本。该脚本采用以下参数:

  • 训练图像的目录。
  • 包含测试图像的文件夹。
  • 包含训练图像注释的文件。
  • 包含测试图像注释的文件。
  • 应存储生成的 TFRecords 的目录。

训练模型

现在,您已经拥有了训练这个 Mask R-CNN 对象检测模型所需的一切。下一步是运行培训脚本。模型定型脚本采用以下参数:

  • pipeline_config_path更新模型配置文件的路径。
  • model_dir将保存训练模型的目录。

作者图片

在 Colab 上你可能会得到一个OpenCV错误。这个错误可以通过安装正确版本的 OpenCV 来修复。

pip uninstall opencv-python-headless==4.5.5.62 pip install opencv-python-headless==4.5.2.52*If you get a* [*cuDNN*](https://developer.nvidia.com/cudnn) *error, you can fix it by installing the right version of cuDNN.*!apt install --allow-change-held-packages libcudnn8=8.1.0.77-1+cuda11.2

模型评估和可视化

训练完成后,您可以运行 TensorBoard 来显示训练和测试指标的可视化,例如定位损失。

作者图片

阅读更多: TensorBoard 教程(深潜附实例和笔记本)

运行转换脚本

下一步是导出模型进行推理。转换脚本预期:

  • trained_checkpoint_dir训练好的模型的最后一个检查点。
  • output_directory保存导出模型的位置。
  • pipeline_config_path管道配置文件的路径。

转换脚本将输出检查点文件、一个 SavedModel 和模型配置文件。

作者图片

从 Google Colab 下载模型

您可能希望下载转换后的模型或训练模型。这可以通过压缩文件并使用 Colab 工具下载压缩文件来实现。

基于掩模 R-CNN 的目标检测

现在是时候使用训练好的 Mask R-CNN 模型对测试汽车图像执行对象检测了。幸运的是,TensorFlow 2 对象检测 API 提供了完成这项工作所需的所有实用程序。第一个是加载图像并将其转换成 NumPy 数组的函数。

将图像从文件加载到 NumPy 数组中

该函数需要一个图像文件的路径,并返回一个 NumPy 数组。

可视化检测

下一个实用程序是使用 Matplotlib 绘制检测结果的函数。

从最后一个检查点创建模型

现在让我们从最后保存的模型检查点创建一个检测模型。

用于推理解码的地图标签

接下来,我们声明对解码模型输出很重要的变量。例如,类别和包含培训类别的文件。

对测试图像运行检测器

下一步是在一些测试图像上运行 Mask R-CNN 对象检测模型。

作者图片

基于掩模 R-CNN 的图像分割

掩模 R-CNN 对象检测模型可以用于对象检测和图像分割。让我们从加载微调后的模型开始。

设置标签地图

模型还需要标签来解码输出。

设置测试图像路径

下一步是定义测试图像的路径。在这种情况下,我们将使用所有的测试图像,因为它们并不多。

创建推理函数

TensorFlow 2 对象检测 API 报告中也提供了分段实用程序。

执行分割和检测

下一步是加载一个图像作为一个 NumPy 数组,并使用上面的函数开始检测物体。

作者图片

最后的想法

在本文中,我们看到了如何使用 TensorFlow 2 对象检测 API 训练对象检测模型。更具体地说,我们涵盖了:

  • 对象检测任务的数据集准备。
  • TensorFlow 2 对象检测 API。
  • 如何在本地和 Google Colab 上安装 TensorFlow 物体检测 API?
  • 设置屏蔽 R-CNN 对象检测模型的配置。
  • 将图像转换为 TFRecord 格式。
  • 训练对象检测模型。
  • 评估对象检测模型。
  • 使用掩模 R-CNN 进行目标检测。
  • 基于掩模 R-CNN 模型的图像分割。

点击 Colab 链接从头到尾尝试该项目。您也可以用另一个数据集替换该数据集。如果更改模型,记得编辑模型配置文件。始终确保配置文件中的路径指向正确的位置。

在 LinkedIn 上关注我获取更多技术资源。

数据集引用

数据由 Kaggle 上的 LPLENKA 在 CC0: Public Domain 许可下提供。

用于开放世界定位(OWL-ViT)的视觉转换器的对象检测

原文:https://towardsdatascience.com/object-detection-with-vision-transformer-for-open-world-localization-owl-vit-cb1ab6d4d7a9

使用变压器进行物体检测

杰恩·贝勒在 Unsplash 上拍摄的照片

卷积神经网络已经成为应用于目标检测的主要网络。最近,变形金刚在自然语言处理和计算机视觉领域越来越受欢迎。

在本文中,我们探索了 OWL-ViT 在物体检测中的应用。

让我们开始吧。

什么是视觉转换器?

变形金刚已经广泛应用于自然语言处理任务。然而,它们最近被应用于计算机视觉——因此被命名为视觉转换器

视觉变形金刚的工作原理如下:

  • 图像被分割成固定大小的小块。
  • 图像补片被展平并线性嵌入。
  • 添加位置嵌入以保留位置信息。
  • 将它们传送到标准变压器编码器

来源

OWL-ViT 是如何工作的?

OWL-ViT 模型是一个开放词汇表的对象检测模型,它使用标准的视觉转换器来执行检测。变压器通过以下方式用于物体检测:

  • 用分类和箱头替换最终的令牌池层。
  • 通过用来自文本模型的类名嵌入改变固定的分类层权重来实现开放词汇分类。
  • 使用二分匹配损失对具有异议检测数据集的预训练模型进行微调。
  • 端到端地微调文本和图像模型。

OWL-ViT 模型可以用作一次性检测学习器。在一次性学习中,模型根据每个类别中的一个或很少几个示例进行训练,并用于对未知样本进行预测。例如,当人类看到一只狮子时,他们可以很容易地在未来的各种环境中认出狮子。

OWL-ViT 模型接受一个图像和一个或多个文本查询,并寻找与查询匹配的对象。

OWL-ViT 架构

现在让我们看看 OWL-ViT 模型的架构。

仅编码器 OWL-ViT 模型由以下人员开发:

  • 使用图像-文本对的图像和文本编码器的对比预训练
  • 转移预先训练好的编码器打开词汇表。这是通过用异议检测和本地化头取代令牌池来实现的。物体检测和定位头附在图像编码器输出标记上。
  • 用文本编码器嵌入查询字符串进行开放词汇检测。然后将查询字符串用于分类。

使用焦点 sigmoid 交叉熵进行分类,因为图像可以具有多个标签。

来源

使用对比损失函数来完成训练,该函数鼓励对应的图像-文本对具有相似的嵌入,而不对应的对具有不同的嵌入。对比预训练包括同时学习文本和图像嵌入。目标是学习与图像嵌入一致的文本嵌入。多头注意力池(MAP)用于聚集图像表示。

文本嵌入是通过将类别名称传递给文本编码器来获得的。在 OWL-ViT 中,文本嵌入被称为查询。OWL-ViT 模型预测一个边界框和某个文本嵌入应用于特定对象的概率。OWL-ViT 还可以用于对图像嵌入的查询进行预测,使得检测无法描述的对象成为可能。这是因为文本和图像编码器之间没有融合。

CLIP (对比语言-图像预处理)作为 OWL-ViT 的主干。剪辑作品由:

  • 训练图像和文本编码器来预测文本和图像对。
  • 在推理过程中执行零镜头分类。

CLIP 通过学习多模态嵌入空间来预测文本和图像对。这是通过训练图像编码器和文本编码器来最大化正确的图像和文本嵌入对之间的余弦相似性,同时使用对称损失函数来降低不正确对的余弦相似性来实现的。

来源

CLIP 有一些限制。它们包括:

  • CLIP 的零拍性能在区分车模、花种等一些任务上表现较弱。
  • 为训练数据中没有的任务提供近乎随机的预测。
  • 不适用于超出分布的图像分布。

如何使用拥抱脸的 OWL-ViT

你可以通过克隆 OWL-ViT repo 来使用这个模型。然而,你可以更快地使用来自拥抱面部变形金刚的模型。

拥抱脸提供以下功能:

  • 按照 OWL-ViT 的要求处理图像。
  • 对文本和图像输入运行预测。

在下面的例子中,我们:

  • 使用 Pillow 下载图像。
  • 使用OwlViTProcessor函数处理图像并标记文本查询。处理器负责调整图像的大小、缩放和标准化。使用片段标记器对文本查询进行标记化。
  • 将输入传递给OwlViTForObjectDetection模型以获得对象预测。模型的输出是预测的逻辑、边界框、类、图像和文本嵌入。
  • 获取并打印给定文本查询的预测。
import requests
from PIL import Image
import torch

from transformers import OwlViTProcessor, OwlViTForObjectDetection

processor = OwlViTProcessor.from_pretrained("google/owlvit-base-patch32")
model = OwlViTForObjectDetection.from_pretrained("google/owlvit-base-patch32")

url = "https://images.unsplash.com/photo-1637336660118-b4635ce8790c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1074&q=80"
image = Image.open(requests.get(url, stream=True).raw)
texts = [["a photo of a zebra", "a photo of a gazelle"]]
inputs = processor(text=texts, images=image, return_tensors="pt")
outputs = model(**inputs)

# Target image sizes (height, width) to rescale box predictions [batch_size, 2]
target_sizes = torch.Tensor([image.size[::-1]])
# Convert outputs (bounding boxes and class logits) to COCO API
results = processor.post_process(outputs=outputs, target_sizes=target_sizes)

i = 0  # Retrieve predictions for the first image for the corresponding text queries
text = texts[i]
boxes, scores, labels = results[i]["boxes"], results[i]["scores"], results[i]["labels"]

score_threshold = 0.1
for box, score, label in zip(boxes, scores, labels):
    box = [round(i, 2) for i in box.tolist()]
    if score >= score_threshold:
        print(f"Detected {text[label]} with confidence {round(score.item(), 3)} at location {box}")

查看带有 OWL-ViT 拥抱空间的零拍物体检测进行演示。

作者图片

最后的想法

在本文中,我们已经看到了如何使用 OWL-ViT 进行对象定向。特别是,您已经了解到:

  • OWL-ViT 如何工作
  • OWL-ViT 建筑
  • 如何使用拥抱脸的 OWL-ViT

在 LinkedIn 上关注我获取更多技术资源。

面向对象的离散事件模拟—使用 SimPy

原文:https://towardsdatascience.com/object-oriented-discrete-event-simulation-with-simpy-53ad82f5f6e2

用随机离散事件模拟改善繁忙的急诊科

张子超Unsplash 上拍照

在日常生活中,我们经常会遇到基于队列的系统。在这种情况下,系统或流程需要实体(如客户或患者)在系统中流动,同时排队等待有限的资源。我们也经常发现自己想知道我们正在使用的系统是否是最佳的,或者推测它需要多长时间才能得到服务。

在排队论的范围内,离散事件模拟(DES)是一种我们可以用程序和数学的方式解决这些问题的方法。

我之前写过一篇关于 DES 的介绍,概述了方法论背后的一些关键思想,见这里

在这里,我们将使用 Python 包 SimPy 实现 DES。此外,我们将演示面向对象的方法,随着我们的模型变得越来越复杂,这种方法非常有用。

我们提出一个问题

简而言之,DES 可以帮助我们优化任何以实体排队请求特定服务为特征的系统。这适用于许多领域,如制造业、电信和医疗保健。在一个典型的高压环境中,急诊科为测试这种方法提供了一个很好的用例。下面,我们有一个流程图,描述了一个简化的急诊科系统。实体,在这种情况下是患者,流入系统,被分类,随后在回家或被允许进入医院之前通过两个过程中的一个。

黄色方框代表资源,橙色菱形代表决策树,绿色方框代表“汇点”(实体出口点)。

请注意,这个提议的系统确实可以根据项目的需要进行修改和添加。需要仔细考虑所需的详细程度。对系统的过度简化的描述将会产生不切实际的结果,并且不能反映现实,同时我们需要注意不要对超出我们范围的系统方面进行过度建模。

在我们的图中,最多有四个点可以形成队列,而某些元素也有一定程度的随机性。这些用红色虚线边框突出显示,主要与我们系统中的进程相关联。例如,病人到达的间隔时间,以及和护士或医生在一起的时间,都可以被随机建模。换句话说,在我们的模型中有一定程度的随机性。

上面描述的系统非常适合通过 DES 建模。我们将利用 Python 包 SimPy 来完成这项工作。

代码模板

在这里,我们将通过一个模板,你可以按照这个模板在 SimPy 中编写一个 DES 模型。我们将利用面向对象的方法来解决这个编码问题,因为这有助于开发用户友好、可读的代码,特别是当模型变大时。

该代码将包括以下三个主要类别:

  • 一个简单保存关键变量的类,用于操纵模型,或者测试假设场景。
  • 用于保存通过系统的实体的关键信息的实体类。在这种情况下,病人类。
  • 一个代表我们系统的类,以及表征它的过程。

除了以上所述,我们可能希望包含更多的实用类,比如跟踪指标。

这里,我们将只包括代码的关键元素,显示上面的三个主要类。完整代码可以在 这里 查看。

参数和变量

我们将包含一个类,它只是作为定义模型的关键变量和值的占位符。这使得接近和操作变得容易。请注意,我们已经包含了资源水平和平均时间的相关信息,这些信息将被纳入模型的随机元素。

病人

病人类代表进入我们系统的病人。它是用患者 ID 和在我们系统中花费的时间的占位符来实例化的。它还将拥有两种方法,一种是确定患者拥有的优先级,表示严重程度,另一种是在分类过程中使用的分类决策方法,用于确定患者流向哪条路径。这些方法将在我们的主模型代码中的适当位置使用。

系统类别

我们代码的最后一个定义类可以描述为系统,或者模型类。这是定义关键流程的地方,代表了我们的实体流动的路径。

鉴于这门课比其他课稍长,我将把它分成几个主要部分。首先,在实例化时…

这里重要的第一步是建立我们的简单环境。环境管理模拟,在事件到事件的基础上逐步通过时间。除了时间,环境还管理事件的调度和处理。这些都是 DES 的关键组件,否则您需要手工编写代码。因此 SimPy 大大简化了实现。

除了环境之外,我们还分配了一个患者计数器,它也充当患者 ID。

资源(流程图中的黄色方框)也是在这一点上定义的。这些是我们模型的关键元素,并且在很大程度上决定了我们的系统处理实体的效率。我们定义了两种类型的资源,它们来自 SimPy 提供的功能。第一种,也是最基本的类型,是Resource类。我们为这些资源设置了一定的容量,并且它们在先进先出 (FIFO)的基础上工作。也就是说,实体是在先到先服务的基础上处理的。在我们的系统中,分诊护士就是这样一种资源,因此称为self.nurse = simpy.Resource。SimPy 允许您定义的另一种资源类型是优先级资源。这些资源根据实体相关的优先级处理实体,与 FIFO 队列系统相反。在我们的模型中,我们按照从 1 到 5 的优先级对实体进行排序,1 是最重要的,5 是最不重要的。我们系统中优先资源的例子是医生和小房间。

在我们的关键模型变量实例化之后,我们移动到第一个过程函数。我们要定义的第一个过程是到达我们系统的过程,我们可以这样做。

我们使用一个无限 while 循环来生成患者,该循环迭代患者计数器,为每个患者提供一个新的患者 ID。然后,我们用上面定义的AEPatient类启动患者,随后通过我们的主要急诊科流程发送患者,我们将在后面定义。我们函数的另一个主要组件是samples_interarrival变量,它随机生成下一个实体到达的时间,利用指数分布进行采样。这种分布通常用于模拟到达之间的时间。我们将到达之间的平均时间输入到指数函数中,在我们的情况下,我们已经预先确定了到达之间的平均时间,但实际上,您可能希望从真实数据中确定到达之间的平均时间。

然后,到达流程在采样时间内被冻结,随后生成下一个患者。这是通过使用yield self.env.timeout实现的。

while 循环一直运行到 SimPy 环境结束,我们将在后面定义。

我们的系统类中的下一个过程是attend_ed过程,在这个过程中,我们定义了一个病人在系统中可能经历的旅程。

下面代码中的注释描述了患者可能经过的路径,并遵循上面定义的流程图。因此,我将在下面解释代码的一些关键组成部分,您会注意到这些部分在整个流程中会被多次使用。

在 SimPy 的上下文中,我们可以使用一个with块来请求特定的资源,如代码所示。我们在这个块中做的第一件事是yield请求,它告诉进程请求资源,如果资源不可用,进程将冻结在适当的位置,直到资源可用(简单地说,SimPy 为我们处理这一点)。在我们的系统中,一旦资源可用,我们通常会对咨询时间进行采样,无论是与护士还是医生。为此,我们使用了对数正态分布。对数正态分布的随机变量将只取正的实数值,因此对于在工程等领域中建模过程时间是有用的,在这些领域中负值是不可能的,并且在金融中建模收入分布也是有用的。它的特点是向右倾斜,换句话说,有一条向右的长尾。这篇文章给出了一个很好的概述。它的特点也使它非常适合模拟我们的咨询时间。因此,您会注意到我们的过程持续时间利用了一个自定义函数,该函数从对数正态分布中生成一个随机数。然后,我们利用 SimPy 环境类的timeout函数在采样的时间内暂停进程。

请注意,如果患者去急诊科或轻伤病房(MIU ),首先会请求一个隔间。在这个with区块内,是另一个with区块,在这里请求找医生。这意味着,只有当患者进入隔间时,才会请求医生,并且只有当医生结束时,隔间才会被释放,实际上,隔间with条款结束。这是在两个相关资源之间建立关系的一种非常好的方式。另一件要注意的事情是,由于隔间和医生是优先级资源,我们在资源函数参数中指出我们使用的优先级。attend_ae功能的结束代表我们系统的结束,患者可以离开的任何点在上面的流程图中用绿色表示。

我们类中的最后一个方法是run,如下所示。

这种方法就是我们后来用来开始我们的环境。我们启动第一个流程,它是生成病人的生成器函数,并使用环境的run函数在指定的时间内运行我们的模拟。

完整的代码包括主要与跟踪一些关键指标和绘制结果相关的其他方面。为了简洁起见,我在这里省略了这些。

在上面的代码块之外,我们将利用循环的来运行指定次数的模拟,并根据需要包括任何附加元素来跟踪进度。一个简单的例子如下。

输出

在我们的完整代码中,我们跟踪一些帮助我们确定系统整体性能的指标。这些包括我们系统中的资源(包括护士、隔间和医生)的平均等待时间(在所有运行中)。此外,我们还在整个模拟时间内跟踪一些关键服务的队列长度,试图找出瓶颈。

假设我们在每位患者进入我们的系统时为他们设置了优先级,我们也可以基于优先级可视化指标。

我们可以从我们的总体结果开始,显示平均等待时间(以分钟为单位),模型参数设置如上(提醒一下,我们有 3 名医生,2 名护士和 7 个事故和紧急(AE)室,其他输入见上文)。

# average results across all runs of our model
The average number of patients served by the system was 55
The overall average wait across all runs for a triage nurse was 31.4 minutes
The overall average wait across all runs for a cubicle was 62.3 minutes
The overall average wait across all runs for a doctor was 17.2 minutes
The overall average wait across all runs for a MIU doctor was 0.2 minutes
The mean patient time in the system across all runs was 132.5 minutes

我们可以看到等待时间最长的是一个小隔间,而平均每个病人花大约 130 分钟通过系统。

我们的 MIU 几乎没有压力,这是因为在我们的默认设置下,我们的模拟并没有大量使用这个单元。

可视化瓶颈的一个好方法是在我们的模拟器中绘制任意给定时间等待资源的患者数量。下图显示了这一点,仅关注 AE 组件。

图表显示整个模拟器中三种主要资源的平均队列长度—所有运行的平均数。阴影区域代表上下四分位数。

我们看到一个小隔间排起了相当长的队,这显然是我们系统的瓶颈之一,因此导致病人通过的时间很长。

同样,使用我们的默认参数,我们还可以通过患者优先级来显示系统中的平均时间。

按优先级划分的系统平均时间—红色数字表示与每个优先级组相关的患者平均人数。误差线表示标准偏差。

鉴于我们建立模型的方式,大多数患者将优先考虑 3。基于优先级的队列以及我们的系统规定优先级 5 的患者可以去 MIU 或回家,这意味着高优先级和非常低优先级的患者可以更快地得到处理。

如果……

现在我们有了一个模拟系统,我们可以测试假设场景,在之前到现实世界的应用。此外,我们可以测试操作变化,以优化我们的系统。这是 DES 的伟大用途之一,在这里,我们将看看潜在的场景。

我们的默认系统产生了相当长的平均超过两个小时的总处理时间!

想象一下,一位运营经理找到我们,希望减少患者通过急症室的时间。众所周知,病人要等很长时间才能进入小隔间。简单地增加隔间能解决问题吗?

让我们通过增加两个隔间并比较结果来找出答案。

The average number of patients served by the system was 56
The overall average wait across all runs for a triage nurse was 30.1 minutes
The overall average wait across all runs for a cubicle was 59.2 minutes
The overall average wait across all runs for a doctor was 30.5 minutes
The mean patient time in the system across all runs was 137.2 minutes

这也没多大帮助!等待一个小隔间的平均时间略有减少,而总体时间实际上略有增加。

然而,经验让我们认为,也许隔间的数量不是问题,但问题在于一旦患者进入隔间,我们处理他们的效率如何。也许你注意到了,仅仅是增加更多的小隔间实际上增加了看医生的时间,因为同样数量的医生现在有更多的小隔间要通过。

我们认为问题出在隔间的上游。换句话说,有更多的医生,可以让我们更快地清理小隔间,从而让病人更快地回家。因此,让我们将小隔间的数量恢复到默认值,而不是增加医生的数量。

下面我们看到增加一个医生的结果,所以我们现在有四个医生。

The average number of patients served by the system was 63
The overall average wait across all runs for a triage nurse was 29.6 minutes
The overall average wait across all runs for a cubicle was 44.5 minutes
The overall average wait across all runs for a doctor was 5.2 minutes
The overall average wait across all runs for a MIU doctor was 0.2 minutes
The mean patient time in the system across all runs was 117.8 minutes

通过增加一名医生,我们将平均总处理时间减少了约 20 分钟。这也减轻了隔间的压力。

您可能已经注意到,在我们的默认系统中,另一个上游压力相当大。这是需要入院的患者找到住院床位并因此(在我们的系统中)离开急诊室的平均时间。虽然只有 30%的 4 级及以下优先级别患者需要这种服务,但他们平均需要等待 90 分钟才能获得住院床位。为了证明上游压力对我们的声发射系统的影响,我们模拟了平均时间减少到 45 分钟。结果如下所示。

The average number of patients served by the system was 72
The overall average wait across all runs for a triage nurse was 33.8 minutes
The overall average wait across all runs for a cubicle was 33.0 minutes
The overall average wait across all runs for a doctor was 10.3 minutes
The overall average wait across all runs for a MIU doctor was 0.3 minutes
The mean patient time in the system across all runs was 110.1 minutes

一个隔间的平均等待时间已经减少到大约 30 分钟!请记住,在我们的默认系统中,这大约是 60 分钟。我们还可以在下面看到,在我们的模拟器中,排队等候诊室的患者数量也减少了,从大约 40 名患者减少到现在的大约 20 名。

考虑到我们只在模型中添加了一个 AE 资源,这相当了不起!显然,在我们的模拟器中,住院系统的低效率对 AE 表现有影响。事实上,这是一个众所周知的现象。

我们还可以继续,但是我们可以在这里结束,因为我们已经证明了 SimPy 在相对容易地开发 DES 模型方面的巨大作用。为了更进一步,我们当然可以进一步增加系统的复杂性,以更好地反映真实世界的急救护理系统。

感谢

除特别注明外,所有图片均为作者所有。

感谢来自半岛卫生运筹学和数据科学合作组织 (PenCHORD)的同事们在我们参与卫生服务建模项目(HSMA) 期间给予的支持,我们目前已于注册。一些 HSMA 资源可以在下面的链接中看到。

GitHub 回购:

https://github.com/hsma5/3a_introduction_to_discrete_event_simulation https://github.com/hsma5/3b_simpy_part_1 https://github.com/hsma5/3c_simpy_part_2

也有一些 YouTube 视频可用:
https://www . YouTube . com/channel/uccy 9 _ gxg 6km-xjk 9 vv0 mziq/videos

参考

  1. https://towards data science . com/log-normal-distribution-a-simple-explain-7605864 FB 67 c
  2. 埃克塞特大学健康数据科学副教授 Thomas Monks 善意分享的从对数正态分布中产生随机数的自定义函数。
  3. 英格兰住院床位容量对事故和急诊绩效的溢出效应。R 弗里贝尔和 R M 华雷斯https://www . science direct . com/science/article/ABS/pii/s 0168851020301901 #!

Python 中的面向对象编程——继承和子类

原文:https://towardsdatascience.com/object-oriented-programming-in-python-inheritance-and-subclass-9c62ad027278

理解继承的基本概念,并通过创建子类来应用它们。

朱莉娅·卡德尔Unsplash 上拍摄的照片

本文是 Python 中面向对象编程(OOP)系列的一部分。在这篇文章中,我将从我上一篇文章离开的地方开始讨论——Python 中的 OOP 理解类

在上一篇文章中,我们学习了一个的构建模块,并构建了我们的第一个子类。在这篇文章中,我将从解释子类NumListExt01的构造开始,为了便于讨论,我在这里将其重命名为Customer01。然后,我将回顾一下,更详细地解释一下继承的概念,然后再回到子类上,用一些额外的概念对其进行扩展。

亚纲

概述

我们将从上一篇文章中创建的类和子类例子开始。为了便于解释,我将子类从NumListExt01重命名为Customer01

为了提醒我们,Customer01子类通过添加一个额外的方法- get_total()扩展了超类NumList

进入内脏

在进入Customer01的内部之前,让我们创建一个Customer01类的对象,应用这些方法。

Initial list of values of cust02: []
Updated list after adding values to it: [2, 20, 44, 12]
Updated list after removing value 12 is:  [2, 20, 44]
Sum of all the elements of the current list is: 66

定义子类的最低要求:

  • 超类作为参数:创建子类时,提供超类,当前类将在其上构建,name 作为参数。在我们的例子中,我们已经创建了Customer01作为超类NumList的子类。
  • 初始化超类 : Python 需要显式调用超类构造函数(__init__())。调用超类构造函数可以确保超类的属性对于子类是正确可用的。

🛑暂停一下,看看如果你不调用超类的构造函数会发生什么,比如试着用pass替换NumList.__init__(self)

有了对子类的初步理解,现在我们将把注意力转移到理解继承的概念上。如果您对子类细节还有点不清楚,请不要担心,我们将再次回到子类来进一步讨论。

遗产

现实生活中的继承

继承允许我们在现有的通用的基础上构建,并通过定制我们认为合适的新来扩展这些类的用途。这个概念可以用一个真实的例子来解释。

图像由作者创建

图片:继承的例子

超类

在上面的例子中,车辆是最通用的类别或。我们可以把车辆定义为用某种方式移动并用来运送人或材料的东西。这个宽泛的定义让车辆成为了超类,它基本上包含了我们稍后将定义的所有车辆类型。

子类

在第二层中,我们根据车辆行驶的路面类型来指定车辆。还要注意,基于我们想要指定的基础,这一层看起来可能完全不同。例如,我们可以根据其使用目的将车辆指定为两种类型——个人和商业。

这些陆地水上空中太空飞行器都是超类飞行器子类

所有这些子类都包含它们的超类的属性——所有这些都是通过某种方式移动并用于运送人员或物资的车辆。但除此之外,它们都包含基于其操作表面的特定属性:

  • 陆地车辆有轮子,
  • 水上交通工具有螺旋桨等等。

更多子类

子类的第三层中,我们以水上交通工具为例,根据水上交通工具运行的级别进一步指定。我们称它们为— 水面水下运载工具。

这些类继承了两个超类的属性— 车辆水上车辆。也就是说,水面和水下交通工具:

  • 通过某种方式移动并用于运输——从 Vehicles 超类继承的属性
  • 他们有某种推进器——继承自水上交通工具超类

除此之外,它们还有自己的特性,

  • 水面车辆具有漂浮能力
  • 水下运载工具具有下潜能力

到目前为止,T21 遗产的一个关键属性很可能是清楚的,

我们越往下走,职业就变得越具体。

🛑从车辆等级的例子中选择第四等级,并思考哪些属性使它们更好、更具体地达到其目的。

我们例子中的继承

新要求

现在让我们回到我们的编码示例,通过应用它们来理解继承概念。

为了建立一个上下文,让我们假设我们的客户 01 带着一个新的需求回来了;和上一个相比有点复杂。他希望能够做两件事:

  • 将列表中的每一项视为所有项或值的总和的一部分。
  • 获取最大值及其对应的分数值。

查看需求,我们可以看到这些功能是建立在先前创建的类NumListCustomer01已经提供的功能之上的:

  • NumList将是创建和更新数字列表的基础。
  • Customer01将需要得到所有值的总和。

考虑到类的这些属性之间的流动关系,我们可以将这些类放入一个很好的层次结构中:NumList->-Customer01->-CustReq01(新类尚未创建)。

不是说我们不能绕过Customer01类,而是因为我们已经创建了它,让我们利用这个类并在它的基础上构建。此外,想想如果这是一个实际的商业应用程序,其中customer01是一个用数百行代码定义的类。你不会想再造轮子的!

新增子类: **CustReq01**

[2, 20]
{2: 0.09090909090909091, 20: 0.9090909090909091}
Larget value and it's fraction to total:  {20: 0.9090909090909091}
Largest item and it's fraction to total (from variable):  {20: 0.9090909090909091}

在新的子类CustReq01中,我们添加了两个方法来满足这两个需求。但是为了使这两个方法有用,我们还添加了两个变量 - __frac_dictmax_item。此外,我们对超类的实例化与我们在第一个子类Customer01中看到的不同。现在让我们来看一下这些新推出的特性和功能。

初始化超类

初始化超类的两种方式:显式使用超类名,或者使用 *super()* 方法。

  • 在类中使用实际的超类名称作为:< 【 >< 【 > 【 . We initialized 【 this way in our first subclass.
  • Using a more general format as: < 【 >< 【 >< 【 >. Doing this helps us avoid re-typing the 超类而不是作为参数。

⚠️注意到super()方法创建了一个上下文,在这个上下文中你不应该需要放self参数。该方法调用超类,并使其所有属性对子类可用。

私有变量与公共变量

CustReq01中,我们有两个实例变量:__frac_dict -一个空字典,用于存储分数的未来值,以及max_item -另一个空字典,用于存储最大值及其分数值。

正如我们在上一篇文章中提到的,在变量或方法前面添加两个下划线会使其成为私有的,这意味着方法或变量不能从对象中直接访问。在CustReq01的情况下,__frac_dict被创建为私有变量。同样的原则也适用于方法。可以通过添加两个下划线作为前缀来创建私有方法。

私有变量/方法通常在这些属性被类中的一些内部方法使用的情况下创建。因此,将这些属性保留为私有属性可以提供某种保护,防止意外的复杂情况。

查看stack exchange答案,了解私有财产的好处。

练习

试着思考这两个问题:

🛑试着运行x.max_itemx.__frac_dict。如果到目前为止您已经运行了本文中的所有代码,您能猜到您会在结果中看到什么吗?

🛑还有,如果你试图在运行x.frac_max()之前使用x.max_item来获得最大值,会发生什么?你能猜到为什么你会得到你所得到的吗?

使用先前在后面的方法中定义的方法

检查方法frac_max()如何利用之前在CustReq01中定义的方法frac_list()。我们使用变量也遵循同样的规则:<self><.><method name>。

请注意,在使用frac_list()方法时,我们不必提及self参数。

下一步是什么

在这篇文章中,我们试图理解,

  • 继承的概念
  • 理解用于创建子类的语法
  • 通过创建一个新的子类来应用我们所学的概念
  • 最后,我们谈了一点关于实例变量

在接下来的几篇文章中,我们将更深入地挖掘概念变量方法。我们将会看到不同类型的变量和方法,以及它们的用途如何彼此不同。

更新:查看下一篇文章Python 面向对象编程——理解变量

如果你喜欢这篇文章,试试 Python 系列中以前关于 OOP 的文章:

Python 中的面向对象编程—理解变量

原文:https://towardsdatascience.com/object-oriented-programming-in-python-understanding-variable-e451cf581368

理解 Python 类中不同类型的变量以及如何使用它们。

照片由 Pankaj PatelUnsplash 上拍摄

在之前的文章中,我们介绍了作为的一个基本组件的变量,但是我们并没有深入探讨。在这篇文章中,我们将深入探讨类中不同类型的变量以及它们如何用于不同的目的。

变量让我们在程序中存储特定的值。在中,变量的一些常见用法是初始化一个空变量供方法使用,声明一个名称或默认值。

一个 Python 类可以有两种类型的变量:实例和类变量。

安装

我们将使用我们在上一篇文章中创建的NumList的稍微修改版本来继续我们的讨论。

Instance name of nlA1 =  Number List A1

实例变量

这些是与对象类实例紧密相关的变量,而不是

例如,如果你有一辆玩具车,我们认为它是一个Toy类类型,那么实例变量将是那些附加到你的玩具车的变量,而不是附加到Toy类的任何其他玩具车的变量。

声明实例变量

一个实例变量可以在内部声明,也可以在类实例创建后声明。

举个例子,

  • __listinstName是在NumListA类中声明的两个实例变量。另外,请注意如何在初始化并在以后使用时传递参数,例如nlA1.insName = "Number List A1 "。
  • outOfClassVar是直接为nlA1对象创建的

实例变量是独立

实例变量是特定于实例的,因此与其他实例相隔离,即使它们可能都属于相同的类型。检查实例变量instName在被两个不同名称参数创建的实例(nlnl2)调用时如何产生不同的结果。

nlA2 = NumListA(name = "Number List A2")
print('Name of nlA1 instance = ', nlA1.instName)
print('Name of nlA2 instance = ', nlA2.instName)Name of nlA1 instance =  Number List A1
Name of nlA2 instance =  Number List A2

类别变量

类变量是在中声明的,并且它们坚持使用。这意味着,与实例变量不同,即使没有对象被创建类变量仍将存在,并且它们的值在级别更新,而不是在实例级别更新。

声明和调用类变量

类变量内声明,但在任何方法外声明。看下面的例子,在NumListB中创建了类变量 counter

类变量的调用遵循与实例变量相同的约定有:<instance name<.><class variable name>。

类变量是粘性的!

实例变量只被限制在它们的实例中,而类变量是粘性的。它们继承并更新从创建的所有对象

NumListB中,我们在__init__方法中添加了NumListB.counter += 1,它基本上告诉 Python 在每次实例化NumListB时将类变量 counter递增 1。

🛑注意到下面从nlB1nlB2调用counter产生了相同的值。

nlB1 = NumListB(name = "Number List B1")
nlB2 = NumListB(name = "Number List B2")# printing out class variable
print("Number of NumList class instance created = ", nlB1.counter)
print("Number of NumList class instance created = ", nlB2.counter)Number of NumList class instance created =  2
Number of NumList class instance created =  2

检查属性是否存在

一旦你创建了一堆对象,你不太可能知道它们包含哪些属性或变量。或者想想当你不得不使用其他人创建的的时候。在这种情况下,为了检查哪些属性包含在一个对象中,我们可以使用两个功能:__dict__hasattr

列出所有属性

__dict__是一个内置的功能,无论何时创建它们,都会自动带有一个对象。请看下面我们如何调用它来获得一个对象的所有属性。

# printing out the instance variables
print('Instance variables of %s are: \n' %(nlB1.instName), nlB1.__dict__)Instance variables of Number List B1 are: 
 {'instName': 'Number List B1', '_NumListB__list': []}

🛑注意到,nlB1.__dict__的输出没有显示类变量 - counter

从实例调用 *__dict__* 不会显示类变量,因为它们不是实例的属性。

但是我们可以使用中的__dict__来查看类属性。它将打印出一堆其他的东西,其中一些我们稍后会回来看,但是现在,检查下面代码的输出并寻找两个类变量 : counter__hidden_code

# printing out the class variables
print('Properties of NumListB class:\n', NumListB.__dict__)Properties of NumListB class:
 {'__module__': '__main__', 'counter': 2, '_NumListB__hidden_code': 999, '__init__': <function NumListB.__init__ at 0x0000019447733490>, 'add_value': <function NumListB.add_value at 0x0000019447733520>, 'remove_value': <function NumListB.remove_value at 0x0000019447733010>, 'get_list': <function NumListB.get_list at 0x0000019447253C70>, '__dict__': <attribute '__dict__' of 'NumListB' objects>, '__weakref__': <attribute '__weakref__' of 'NumListB' objects>, '__doc__': None}

姓名莽撞

🛑你有没有注意到私有变量的名字是如何被__dict__变量打印出来的?

由于这些私有变量不应该在对象外部可用, Python 破坏了使它们可用的操作,

  • 名称放在变量名称之前
  • 在开头加上一个额外的下划线(_)。

因此,在输出中,您应该看到实例变量 __list打印为_NumListA__list,而类变量 __hidden_code打印为_NumListB__hidden_code

这些损坏的名称可以用来直接访问这些私有变量。这显示了 Python 类的私有特征是如何受到限制的。

# printing out private instance variable using mangled name
print('Private instance variable __list from instace: nlA1 =', nlA1._NumListA__list)# printing out privatge class variable using mangled name
print('Private class variable from class NumListB = ', NumListB._NumListB__hidden_code)Private instance variable __list from instace: nlA1 = [2]
Private class variable from class NumListB =  999

检查特定属性

使用__dict__大概是探索的好办法。但是如果你需要检查某个属性或者属性是否存在于或者对象中呢?

Python 函数 *hasattr()* 可以用来检查一个特定的属性

hasattr()接受两个参数:被检查的对象,以及要作为字符串值搜索的属性的名称。如果属性存在,则返回True,否则返回False

# n1B1 instance properties
print(hasattr(nlB1, '__list'))
print(hasattr(nlB1, 'counter')) # class variable# NumListB class properties
print(hasattr(NumListB, 'counter'))
print(hasattr(NumListB, '__hidden_code'))# checking mangled names for the private attributes
print(hasattr(nlB1, '_NumListB__list'))
print(hasattr(NumListB, '_NumListB__hidden_code'))False
True
True
False
True
True

注意:

__dict__不同,hasattr()可以从对象中检查类变量并返回True

🛑 私有属性可以使用它们的错位名称进行搜索,否则它们将返回False

下一步是什么

在本文中,我们详细介绍了不同类型的变量及其在 Python 上下文中的属性。我们了解到,

  • 什么实例和类变量?
  • 如何检查对象和类的属性?
  • 如何访问公共和私有变量?

在下一篇文章中,我们将深入探讨一个方法

如果你喜欢这篇文章,请查看 Python 中面向对象系列的前几篇:

https://curious-joe.medium.com/object-oriented-programming-in-python-inheritance-and-subclass-9c62ad027278

Python 中的面向对象编程——什么和为什么?

原文:https://towardsdatascience.com/object-oriented-programming-in-python-what-and-why-d966e9e0fd03

照片由 Esther JiaoUnsplash 上拍摄

学习 Python 中的面向对象编程

谁应该阅读这篇文章?

TL;博士: 非程序员或新手程序员

对于使用 Python 作为脚本语言而不是成熟的编程语言的专业人员来说,投资时间学习编程基础知识,如面向对象编程(OOP),可能看起来没有吸引力,甚至没有必要。作为一名数据科学家,我可以保证我们很多人都有这种感觉!

但是一旦你发现自己将解决方案投入生产,学习 OOP 编程会变得非常有益,并且是一个宝贵的效率助推器。

这篇文章将是讨论 Python 中 OOP 的系列文章的一部分。在这篇文章中,我不会过多地探究 OOP 的本质,我会试着建立一些关于 OOP 的背景知识,描绘一个简单的图片来告诉你为什么你会想学习 OOP。

面向对象的功能性定义

面向对象编程(OOP)是,

一种围绕对象而非功能或逻辑进行编程建模的方法。

通过对象人们可以想到一个单元或块,它具有一些独特的特征(属性)和能力(方法)。

对于任何熟悉编码但开始 OOP 之旅的人来说,对象似乎是一个抽象的概念。为了使这种转换更容易一些,让我们来看一个例子。至少,对我来说,通常一个例子比文字更有帮助。因此,在进一步定义 OOP 的其他概念之前,让我们尝试通过一个例子来了解什么是真正的对象,以及它与我们所知的编写普通程序有何不同。

为什么 OOP?

过程程序设计

让我们考虑一个简单的场景。假设您有一个客户/用户正在寻找创建一个空盒子的方法。然后能够添加数字到该框中,并在需要时删除最后一个数字。

你会如何用最简单的方法做到这一点?为了简化,让我们假设的需求可以是一个简单的列表。

如果您对 Python 有所涉猎,您可能会创建一个如下所示的逐步解决方案:

  • 创建一个空列表。
  • 然后定义一个函数,使用户能够将数字添加到列表中。
  • 定义另一个函数,从列表中删除最后添加的数字。

事实上,这是我们在数据科学/分析学等领域的数据争论和探索等迭代过程中最常做的事情。

一个可能的解决方案可能是这样的:

Initial list of values: []
Updated list after adding values to it: [5, 8, 5.5, 10]
Updated list after removing value 10 is:  [5, 8, 5.5]

这种循序渐进的编程方法叫做 程序化编程

对于一个简单的例子,就像例子中的例子,您可以完全控制空列表,这样的过程代码工作得很好。但是,随着您的项目变得更加协作和复杂,可能会出现一些复杂情况。

举个例子,

  • 您未来的自己或您的合作者可能会以一种意想不到的方式无意中修改列表(num_list),即在不使用定义的功能的情况下向列表添加或删除数字。例如,他们可能会像这样在第一个位置插入一个值:num_list[0] = 5而不是只在最后一个位置插入。
  • 或者如果您需要在将来创建多个相似的空列表,您将不得不为每个用例重新编写这些步骤。在思考这个问题的同时,也想想如果你有 50 个函数而不是只有两个,会发生什么!对于 5 种不同的情况,您将不得不编写总共 250 个函数!

⛑️在这种情况下 OOP 来拯救!

面向对象编程(OOP)

在 OOP 中,

与其写下步骤,不如定义一种具有所需特征、能力或方法的框架。

这样的骨架可以在将来需要的时候重复使用来创建对象。

因此,在我们简单的客户端解决方案场景中,如果我们将过程代码视为一个产品,那么它有什么特性呢?

  1. 它包含一个空列表。
  2. 它提供了一种在列表末尾添加值的方法。
  3. 它给出了另一种从列表中删除最后一项的方法。

所以我们将把这些特性封装在一个叫做的块中,把看作特性和能力的容器。

现在让我们为我们的产品定义我们的类,称它为 NumList 。现在不要担心新的语法。我们稍后将回到他们身上。现在,更应该关注对象是如何构造的,比如函数是如何定义的。

Initial list of values of list01: []
Updated list after adding values to it: [2, 20, 44, 12]
Updated list after removing value 12 is:  [2, 20, 44]

它所做的正是我们使用过程化编程所实现的。那么,为什么要经历所有这些额外的障碍呢?

  1. 因为现在你的产品:list01,类NumList的一个实例更安全。尝试在list01中改变值或任何其他修改,而不使用任何已定义的方法,您将会遇到错误。
  2. 创建同类的后续实例已经变得轻而易举!不用声明一个空列表并分别定义每个函数,现在你可以声明一个类 NulList 的实例,这些实例继承这些方法。检查下面的代码块,看看第二个和第三个列表是如何从下面的 NumList 类创建的。
Initial list of values list02: []
Updated list after adding values to it: [2000, 500, 44444, 122222]
Updated list after removing value 122222 is:  [2000, 500, 44444]

https://gist . github . com/好奇-Joe/959 bfcf 5 e 3373105701 CD 129906 b 89 ff

Initial list of values list03: []
Updated list after adding values to it: [0.1555, 0.333, 0.444, 0.555]
Updated list after removing value 0.555 is:  [0.1555, 0.333, 0.444]

下一步是什么?

希望现在 OOP 对你来说不那么模糊了,你开始看到在面向对象编程上投入时间的价值。为了简单起见,我将在这里结束这篇文章,把对一个类的元素的解释留到一个单独的后续文章中。

在下一篇文章中,我们将回顾我们创建的类,解释类的编码语法和元素,并扩展该类,使其具有更丰富的特性和方法。

更新:点击这里查看下一篇文章Python 中的 OOP 理解一个类。

通过简单的 Web 代码获得无限的历史加密数据-没有 API 密钥

原文:https://towardsdatascience.com/obtain-unlimited-historical-crypto-data-through-simple-api-calls-without-keys-8a6f5ed55b43

从基于 web 的代码中调用 CoinGecko 的免费 API 来获取价格、交易量和市值,您可以根据自己的需要调整显示、绘制或下载数据。

免责声明:你在这里找不到任何金融建议,我也不是加密货币、加密艺术、NFT 及其交易等方面的专家。我只赠送免费创意加 HTML+JavaScript 代码通过 API 调用这个加密货币数据聚合器获取数据;我展示了一些从这些数据中得出的图表,还有一些我自己的主观看法。

跳转到: 简介 | 一个从 coingeko|检索数据的小型 web app 一个个人加密数据绘图 app|更多灵感

早在 2021 年 8 月,我就对加密货币、加密艺术、NFT 等产生了兴趣。我想自己玩加密货币数据。我在网站、新闻、app 上到处看到剧情;但是,我怎样才能得到实际的数据来玩它,做我自己的计划和调查呢?尤其是,我如何确保每次我请求它时它都是最新的?通过研究,我发现通过编写连接到 API 的小网页,我可以很容易地从 CryptoCompare.com 获得数据。在本文中,我描述了这个 API 的基础知识以及使用它所需的 HTML+JavaScript 代码,还包括一些现成的例子:

CryptoCompare 的 API 的一个小问题,以及为什么有些问题我更喜欢 CoinGecko

在其免费版本中,CryptoCompare 的 API 被限制为每次调用 2000 个数据点。这意味着,例如,要覆盖比特币的全部每日历史数据,您需要执行 3 次调用。当时,我通过递归调用进行 API 调用的函数解决了这个问题,只需要 3 次调用就可以了。然而,如果你想要那么多年的每小时数据,你将需要几十个连续的调用,增加了相当多的时间,在此期间,应用程序将被阻塞,等待所有调用完成。

有了 CoinGecko 的 API,您就没有这种限制。每个调用的大小基本上没有限制,所以我可以在一个调用中获得任何加密货币的完整历史数据。更确切地说,限制是每分钟的通话次数,在我最近的测试中是 50 次。这意味着您可以一次性请求比特币、以太坊和 48 种其他货币的数据。(不尽然,因为实际上有些调用失败了,所以你需要再次运行它们;反正那一点都不差!)

此外,CoinGecko 是一个数据聚合器,这意味着它拥有比其他数据提供的更多的加密货币数据。特别是,在最近一次客户要求的搜索中,我可以找到截至 2021 年底大约有 300 个 DeFis 可用。我猜这可能是以牺牲准确性为代价的;然而,我对照其他数据提供商检查了一些货币,CoinGecko 的结果是一致的。

另一个不好的地方是,CoinGecko 的免费 API 调用在每个时间点只返回一个价格(加上市值和成交量),而没有单独的最高价、最低价、开盘价和收盘价。至少在基本的免费模式下是如此。

CoinGecko 及其免费 API

CoinGecko 跟踪并聚合加密货币数据,为用户分析加密市场提供数字货币的完整视图和工具。他们跟踪大量硬币的市场资本总额、价格和数量,并跟踪社区增长、软件开发、市场中的重大事件以及发展和拓展等。

在其网站的“关于”中解释说,CoinGecko 成立于 2014 年[……],其使命是使加密数据的访问民主化,并为用户提供可操作的见解。我认为他们已经很好地实现了他们的目标,这可以从他们网站上免费提供的大量文档以及他们的报告、出版物(包括两本书)、时事通讯中得到证明,最重要的是,他们强大的、易于使用的和记录良好的 API。

在前一节我刚刚介绍了 CoinGecko 的免费 API,所以我在这里只添加了一个到其主网页的链接和一个到其文档的链接。他们也有付费服务,提供更多的功能和支持。

编写一个从 CoinGecko 检索数据的小型 web 应用程序

通过结合 HTML 和 JavaScript,您可以创建一个简单的网页,通过调用 CoinGecko 的 API 从 coin gecko 检索数据。

点击此处查看一个示例,该示例检索过去 10 天以太坊以美元为单位的每小时数据(价格、市值和交易量):

https://lucianoabriata . alter vista . org/tests/cryptogames/coingeckoexample . html

这是从该网页中剥离出来的核心 JavaScript 代码,您可以看到 API 调用是如何工作的(我删除了所有不重要的内容,并使代码更简单,以明确要点):

fetch("[https://api.coingecko.com/api/v3/coins/ethereum/market_chart?vs_currency=USD&days=10&interval=hourly](https://api.coingecko.com/api/v3/coins/ethereum/market_chart?vs_currency=USD&days=10&interval=hourly)")
  .then(function(resp) {
    return resp.json();
  })
  .then(function(data) {
    var txt="(setup table HTML)"

    //Iterate to extract the data and place it into the table HTML
    for (var i=0;i<data.prices.length;i++){
      txt = txt + "<tr><td>" + data.prices[i][0] + "</td><td>" + serialDateToNiceDate(data.prices[i][0]) + "</td><td>" + data.market_caps[i][1] + "</td><td>" + data.prices[i][1] + "</td><td>" + data.total_volumes[i][1] + "</tr>\n"
    }
    txt = txt + "\n</table>" //Close table
    document.write(txt)
  })
}

本质上,这是一个 fetch 命令,使用与您想要检索的内容相对应的参数来调用 URL。当调用被解析时,你得到一个名为数据的对象,它包含。价格。市值。总量。其中每一个都包含一个日期数组(索引 0)和另一个价格、资本化或成交量数组(索引 1)。

在我的例子中,一个 for 循环遍历所有的值来编写一个表的 HTML 代码,该代码在过程结束时用一个 document.write() 行注入到文档中。

最后,请注意,在 URL 中,您必须指明硬币 id ,而不是其通用名称。在这个例子中是“以太坊”而不是 ETH。同样,你可能需要“比特币”而不是 BTC,或者“基本注意力令牌”而不是英美烟草,等等。

轻松构建更复杂的程序,如您的个人加密数据绘图或下载应用程序

您可以轻松地将上面的代码进一步开发成一个 web 应用程序,例如实时更新数据,或者设置它以便在移动中可视化,或者创建文件供下载并在您选择的电子表格或数据分析程序中使用。

例如,下一个应用程序允许用户在 3 种货币中选择,并实时查看美元价格。它将上述代码与 Google 的网络图表库集成在一起。

https://lucianabriata . alter vista . org/tests/cryptogames/coingeckoexample 2 . html

这是在我的智能手机上运行的截图:

该示例显示了基本注意力令牌(BAT)的价格,这是一种有趣的加密货币,与我之前提到的勇敢浏览器相关联:

https://medium.com/technology-hits/i-like-the-principles-behind-brave-browser-but-the-surf-to-earn-leitmotif-is-pure-deception-c5f0caf05755

想要更多灵感?

鉴于您在我之前关于检索加密数据的文章中有更多的想法:

我向您推荐这篇文章,在这篇文章中,我将基于网络的虚拟现实与基于网络的加密货币数据检索相结合:

喜欢这篇文章,想给我提示?【https://www.paypal.me/LAbriata】-谢谢!

www.lucianoabriata.com*我写作并拍摄我广泛兴趣范围内的一切事物:自然、科学、技术、编程等等。 成为媒介会员 访问其所有故事(我为其获得小额收入的平台的附属链接无需您付费)和 订阅获取我的新故事 通过电子邮件 。到 咨询关于小职位 查看我的 服务页面这里 。你可以 这里联系我 *

厄瓜多尔加拉帕戈斯群岛的洋流季节性

原文:https://towardsdatascience.com/ocean-currents-seasonality-in-the-galapagos-islands-ecuador-9197f0b721c0

使用 Sentinel-3 OLCI 传感器的季节性浮游植物浓度

图片由作者提供。Chl-a 浓度的加拉帕戈斯群岛 2019 年 3 月 28 日。

加拉帕戈斯群岛是一个天堂。每次我去参观它们时,我都感到惊讶,因为到处都可以看到生命的繁荣。在我的最后一次访问中,我有机会在平松岛浮潜练习中与年轻的海狮互动,与野生动物的接触令人难忘。鸟类、海洋和陆地巨龟、黄貂鱼、鱼类、鲨鱼和许多其他我叫不出名字的物种可以在岛上找到(步行或骑自行车 20 分钟)。

我决定分享这一分析,因为地理空间技术的使用可以揭示叶绿素 a 浓度作为 Sentinel 3 OLCI 传感器浮游植物浓度的代理。主要想法是通过“野生动物追踪器”地理框架在实时可见性下直接从卫星星座实施该工作流。点击下一个链接了解这款产品:

Web: “野生动物追踪器 v 0.2”

图片由作者提供。野生动物追踪者肖像。www.gis4-wildlife.com

查看我们的最新报道:野生动物监测系统,实时浮游植物热点

在这里你可以找到一个简短的分析和文本,关于加拉帕戈斯群岛以及叶绿素 a 的浓度如何随着洋流的季节性而变化。

加拉帕戈斯海洋保护区和海洋生态系统

加拉帕戈斯洋流和生物之间的海洋学相互作用是独一无二的。该群岛位于主要洋流的交汇处,汇集了来自南方的冷水(洪堡洋流)、来自北方穿过赤道的温水(厄尔尼诺洋流)和来自西方的深层、寒冷且富含营养的水(克伦威尔洋流)。这些主要洋流的混合产生了来自不同环境的动植物的融合点,这部分解释了我们今天发现的特殊海洋生物多样性。主要是群岛的隔离允许了物种的独立进化,水流将这些物种带到了新物种诞生的地方。近 20%的海洋生物是特有的,在地球上其他地方找不到。这种程度的特有现象对于海洋物种来说是非常独特的,因为海洋物种往往比它们的陆生同类(例如海鬣蜥)在更大程度上迁移和混合。加拉帕戈斯是地球上鬣蜥适应海洋生态系统的唯一地方。

这些重要的水流帮助了像冷水企鹅这样最奇怪的加拉帕戈斯生物生活在这个纬度。事实上,加拉帕戈斯企鹅是世界上最北的企鹅物种。(达尔文基金会,2020 年)。

图片由作者提供。加拉帕戈斯群岛。来自谷歌地图。

洋流对生命繁衍的影响

寒冷而肥沃的洋流为太平洋绿海龟提供了肥沃的绿藻田,维持着太平洋上绿海龟最丰富的聚居地之一。营养丰富而寒冷的洋流克伦威尔沐浴在赤道阳光中,这提高了微观浮游植物的生产力,为生命的爆发提供了养分。加拉帕戈斯是地球上少数几个因为食物供应而可以在近岸看到金枪鱼、杰克鱼、梭鱼、双髻鲨等大量中上层物种(不靠近海岸,也不在海洋深处)集中的地方之一。

浮游植物含有叶绿素,这是一种将阳光转化为植物可以利用的能量的色素。这种色素赋予浮游植物绿色。叶绿素吸收大部分可见光,但反射一些绿光和近红外光。(NEO,2019)。

在加拉帕戈斯,来自温暖的热带洋流厄尔尼诺的珊瑚和鱼类与海狮或企鹅共享生态系统,更典型的是来自极地的海狮或企鹅,它们因洪堡洋流而迁徙。海洋生态系统是一个水下野生动物奇观,世界上没有其他地方可以提供人类如此熟悉的海洋生物多样性的潜水体验。(达尔文基金会,2020 年)。

图片由作者提供。季节性洋流可视化区域和加拉帕戈斯群岛的位置。来自谷歌地图。

目标

确定加拉帕戈斯海洋保护区浮游植物的存在,该保护区受冷洋流洪堡和克伦威尔以及暖洋流厄尔尼诺的季节性影响

季节

寒流 Humboldt(来自南方)和 Cromwell(来自西方)通常在加拉帕戈斯 7 月后至年底最为活跃,8 月的最低温度为 16°c。另一方面,暖流 El Niñ(来自北方)在年初到达群岛,3 月的最高温度为 27°c。

在接下来的 AQUA/MODIS SST 时间推移中,可以看到海面温度变化的漂亮视图。2019 年 3 月至 2020 年 3 月(12 个月)

图片由作者提供。用 Geemap Streamlit App (吴,2022)制作的从厄瓜多(北)到智利巴塔哥尼亚(南)

数据

这种对 Pythoplankton 的分析是用 OLCI 传感器(海洋和陆地颜色仪器)从 sensor sensor 3 获得的卫星图像完成的。这些图像是从由于洋流的到来而出现峰值温度的月份中挑选出来的。结果,获得了加拉帕戈斯群岛附近海域的叶绿素浓度。

此外,为了显示浮游植物存在时水温的影响。月平均值由 2018 年 3 月和 2018 年 8 月的 AQUA/MODIS-海面温度进行审查。

数据许可证

  • 通过 Sentinel 数据中心访问的 Sentinel 3 OLCI 数据受关于使用哥白尼 Sentinel 数据和服务信息的 法律声明管辖。 法律声明指出,用户可以公开访问 Sentinel 数据,而无需默示担保。对于 a)复制、b)分发、c)向公众传播、d)改编、修改以及与其他数据和信息的组合,以及 e)任何(a)和(b)的组合,使用是允许和合法的。在影响欧盟安全的极少数情况下,不允许使用。
  • AQUA/MODIS 数据由美国宇航局海洋生物处理小组(OBPG)支持。美国航天局促进与研究和应用界、私营企业、学术界和公众全面和公开共享所有数据。数据的可用性越大,用户群体就能越快、越有效地利用这些信息来解决基础地球科学问题,并为开发创新的实际应用以造福公众提供基础。在这里找到数据政策。该数据集属于公共领域,可以不受限制地使用和分发。

处理

地理处理在 SNAP 软件中完成,该软件涉及水的大气校正,使用的过滤器是 C2RCC 水处理/OLCI。C2RCC 滤波器中使用的参数随季节而变化。3 月份使用的温度为 27°C,8 月份为 16°C,两个月份的盐度均为 35.5 PSU (IFREMER,2020 年)。这个过滤器提供了一个称为 conc-chl 的新层,代表叶绿素(浮游植物)的浓度。为了创造更好的产品,应用了在工作流程中指定的云遮罩。

图片由作者提供。工作流程。

海表温度 8 天-AQUA/MODIS 数据集不需要处理。数据集已经使用云掩膜进行了校正,并表示了所需的信息。对于可视化和地图设计,使用了 ArcMap 软件。

结果

下一个季节对比显示了基于洋流影响的浮游植物的存在。主要地,人们发现冷洋流使浮游植物大量存在,因为在冷季节(8 月)可以通过克伦威尔洋流的存在看到这一点。另一方面,温暖季节(3 月)显示,由于厄尔尼诺洋流,没有浮游植物,但由于克伦威尔洋流和洪堡洋流,在岛屿附近有少量浮游植物。

三月——温暖的季节

厄尔尼诺的暖洋流影响太平洋的赤道地区,包括加拉帕戈斯群岛周围。温暖的洋流温度超过 19°c。但是,与此同时,洪堡洋流直接流向加拉帕戈斯群岛的东部,温度主要为 12°c。克伦威尔洋流在这个季节有轻微的影响。

图片由作者提供。用 Geemap Streamlit App 创建(吴,2022)

因此,在这个温暖的季节(3 月),由于厄尔尼诺洋流,观察到岛屿北部缺乏浮游植物。

图片由作者提供。暖季浮游植物的存在,2019 年 3 月 28 日

在岛屿内部和靠近东部岛屿的地方,浮游植物的浓度超过 0.25 毫克/立方米(绿色、黄色和红色)。这种存在是由于洪堡和克伦威尔洋流的影响造成的。一般来说,在这个季节,加拉帕戈斯海洋保护区的浮游植物浓度没有空间分布。

八月——寒冷的季节

在寒冷季节,厄尔尼诺洋流的影响减弱,群岛北部的温度不超过 19 摄氏度。另一方面,你可以看到洪堡和克伦威尔洋流正在影响整个岛屿的南部和西部,气温在 12℃左右。

图片由作者提供。用 Geemap Streamlit App 创建(吴,2022)

众所周知,克伦威尔洋流带来了营养物质和丰富的浮游植物。

图片由作者提供。2019 年 8 月 14 日冷季浮游植物的存在

浮游植物的存在表明浓度超过 1 毫克/立方米,在加拉帕戈斯群岛中部和西部(红色)最高为 18.13 毫克/立方米。此外,浮游植物在岛屿周围的空间分布增加了 0.25 毫克/立方米至 0.5 毫克/立方米,尤其是在岛屿的东面(绿色、黄色和橙色)。因此,在寒冷季节,克伦威尔洋流对浮游植物的存在有显著影响。考虑到两者都是冷洋流,克伦威尔洋流的影响大于洪堡洋流。

结论

克伦威尔冷洋流对加拉帕戈斯海洋保护区西部的浮游植物有重大影响。考虑到两者都是冷洋流,其影响大于洪堡洋流。浮游植物舒适的生态系统的温度在 12℃以上和 16℃以下。超过 16℃,浮游植物的数量就会减少。厄尔尼诺暖流对加拉帕戈斯海洋保护区北部的浮游植物没有影响。

参考

用甜甜圈实现无 OCR 文档理解

原文:https://towardsdatascience.com/ocr-free-document-understanding-with-donut-1acfbdf099be

使用最近发布的 Transformers 模型来生成文档数据的 JSON 表示

罗曼·丹斯克Unsplash 上拍摄的照片

视觉文档理解(VDU)是深度学习和数据科学中一个经过大量研究的新领域,特别是因为 pdf 或文档扫描中存在大量非结构化数据。最近的模型,如 LayoutLM ,利用 transformers 深度学习模型架构来标记单词或回答基于文档图像的给定问题(例如,您可以通过注释图像本身来突出显示和标记帐号,或者询问模型,“帐号是什么?”).像 HuggingFace 的[transformers](https://huggingface.co/docs/transformers/index)这样的库使得使用开源变形金刚模型变得更加容易。

VDU 问题的大多数传统解决方案依赖于解析该图像的 OCR 输出以及视觉编码,但是 OCR 的计算成本很高(因为它通常需要安装像 Tesseract 这样的 OCR 引擎),并且在完整的管道中包含另一个模型会导致必须训练和微调另一个模型,并且不准确的 OCR 模型会导致 VDU 模型中的错误传播。

因此,来自 Naver CLOVA 的研究人员提出了一个端到端的 VDU 解决方案【1】,该方案使用了一个编码器-解码器变压器模型架构,并且最近将与 HuggingFace transformers库结合使用。换句话说,它使用 BART 解码器模型将图像(使用 Swin Transformer 分割成小块)编码成令牌向量,然后可以解码或翻译成数据结构形式的输出序列(然后可以进一步解析成 JSON ),并在多语言数据集上进行公开预训练。在推理时输入到模型中的任何提示也可以在相同的架构中解码。

甜甜圈作者的图片(麻省理工学院许可)

你可以在脐带收据数据集 这里看到甜甜圈的演示。他们提供了一个样本收据图像来进行测试,但是您也可以在许多其他文档图像上进行测试。当我在这张图片上测试时:

图片由甜甜圈的作者提供

我得到了结果:

{
    nm: "Presentation"
}

这表明它检测到“演示”标题是菜单或收据上的项目名称。

作者还提供了培训和测试脚本,因此我们可以演示如何在实践中实际使用这些模型(我将使用 SROIE 数据集 [2],一个标记收据和发票的数据集,来演示对定制数据集的微调)。我建议在 GPU 上运行代码,因为推理和训练都需要花费 CPU 相当长的时间。Google Colab 提供免费的 GPU 访问,应该足以进行微调(转到运行时 > 更改运行时类型从 CPU 切换到 GPU)。

首先,让我们确保我们有 GPU 访问。

import torch
print("CUDA available:", torch.cuda.is_available())
!nvcc --version

现在我们可以下载相关的文件和库。下面几行代码应该会安装所有的依赖项,包括 donut 库(虽然您可以使用pip install donut-python手动安装,但是从 Github 克隆的代码库包括重要的培训和测试脚本)。

!git clone https://github.com/clovaai/donut.git
!cd donut && pip install .

使用 CORD 微调模型进行推断

首先,我们将演示模型的基本用法。

from donut import DonutModel
from PIL import Image
import torch
model = DonutModel.from_pretrained("naver-clova-ix/donut-base-finetuned-cord-v2")
if torch.cuda.is_available():
    model.half() 
    device = torch.device("cuda") 
    model.to(device) 
else: 
    model.encoder.to(torch.bfloat16)
model.eval() 
image = Image.open("./donut/misc/sample_image_cord_test_receipt_00004.png")
    .convert("RGB")
output = model.inference(image=image, prompt="<s_cord-v2>")
output

DonutModel.from_pretrained()调用中,我已经简单地从 HuggingFace Hub 中指定了预训练模型的名称(此时下载了必要的文件),尽管我也可以指定模型文件夹的本地路径,我们将在后面演示。Donut 代码库还包括一个示例图像(如下所示),这是我传递到模型中的,但是您可以用任何您喜欢的图像来测试模型。

甜甜圈作者提供的收据样本图片

您应该得到如下输出

{'predictions': [{'menu': [{'cnt': '2', 'nm': 'ICE BLAOKCOFFE', 'price': '82,000'}, 
    {'cnt': '1', 'nm': 'AVOCADO COFFEE', 'price': '61,000'}, 
    {'cnt': '1', 'nm': 'Oud CHINEN KATSU FF', 'price': '51,000'}],
    'sub_total': {'discount_price': '19,400', 'subtotal_price': '194,000'}, 
    'total': {'cashprice': '200,000', 
    'changeprice': '25,400', 
    'total_price': '174,600'}}]}

(注意:如果你像我一样好奇,想知道预训练的donut-base主干会给你什么输出,我继续测试了一下。因为占用了太多内存,所以在崩溃之前需要很长时间才能产生输出。)

在自定义数据集上微调甜甜圈

为了演示微调,我将使用 SROIE 数据集,这是一个收据和发票扫描的数据集,带有 JSON 形式的基本信息以及单词级的边界框和文本。它包含 626 张图片,但我将只在 100 张图片上进行训练,以展示 Donut 的有效性。这是一个比 CORD(包含大约 1000 张图片)更小的数据集,并且标签也更少(只有公司、日期、地址和总数)。

下载和解析 SROIE

要下载数据集,您只需从主存储库中下载数据文件夹。您可以通过克隆整个存储库或者使用类似于下载目录的东西来下载单个文件夹。

但是现在我们需要将数据集解析为 HuggingFace datasets库所要求的格式,这是 Donut 在幕后用来将自定义数据集作为图像字符串表加载的。(如果你在寻找文档,Donut 使用imagefolder加载脚本。)

以下是所需的数据集格式:

dataset_name
├── test
│   ├── metadata.jsonl
│   ├── {image_path0}
│   ├── {image_path1}
│             .
│             .
├── train
│   ├── metadata.jsonl
│   ├── {image_path0}
│   ├── {image_path1}
│             .
│             .
└── validation
    ├── metadata.jsonl
    ├── {image_path0}
    ├── {image_path1}
              .
              .

其中 metadata.jsonl 是一个类似于

{"file_name": {image_path0}, "ground_truth": "{\"gt_parse\": {ground_truth_parse}, ... {other_metadata_not_used} ... }"}
{"file_name": {image_path1}, "ground_truth": "{\"gt_parse\": {ground_truth_parse}, ... {other_metadata_not_used} ... }"}

换句话说,我们希望将每个文档的注释(在文件夹中找到)转换成类似于"{\"gt_parse\": {actual JSON content}"}"的 JSON 转储字符串。下面是一个注释示例:

{
    "company": "BOOK TA .K (TAMAN DAYA) SDN BHD",
    "date": "25/12/2018",
    "address": "NO.53 55,57 & 59, JALAN SAGU 18, TAMAN DAYA, 81100 JOHOR BAHRU, JOHOR.",
    "total": "9.00"
}

下面是我用来将数据转换成 JSON lines 文件以及将图像复制到各自的文件夹中的脚本:

import os
import json
import shutil
from tqdm.notebook import tqdm
lines = []
images = []
for ann in tqdm(os.listdir("./sroie/key")[:100]):
  if ann != ".ipynb_checkpoints":
    with open("./sroie/key/" + ann) as f:
      data = json.load(f)
images.append(ann[:-4] + "jpg")
    line = {"gt_parse": data}
    lines.append(line)
with open("./sroie-donut/train/metadata.jsonl", 'w') as f:
  for i, gt_parse in enumerate(lines):
    line = {"file_name": images[i], "ground_truth": json.dumps(gt_parse)}
    f.write(json.dumps(line) + "\n")
shutil.copyfile("./sroie/img/" + images[i], "./sroie-donut/train/" + images[i])

我简单地运行这个脚本三次,每次都改变文件夹和列表片的名称([:100]),这样我在训练中有 100 个例子,在验证测试中各有 20 个例子。

训练模型

Donut 的作者提供了一个非常简单的方法来训练模型。首先,我们需要在 donut/config 文件夹中创建一个新的配置文件。您可以将已经存在的示例( train_cord.yaml )复制到一个名为 train_sroie.yaml 的新文件中。这些是我更改的值:

dataset_name_or_paths: ["../sroie-donut"]
train_batch_sizes: [1]
check_val_every_n_epochs: 10
max_steps: -1 # infinite, since max_epochs is specified

如果您已经本地下载了donut-base模型,您也可以在pretrained_model_name_or_path中指定它的路径。否则 HuggingFace 会直接从 Hub 下载。

当我在 Google Colab 上发现 CUDA 内存不足错误时,我将批处理大小从 8 减小,并将check_val_every_n_epochs增加到 10 以节省时间。

这里是你应该用来训练你的模型的线:

cd donut && python train.py --config config/train_sroie.yaml

我花了大约一个小时的时间在 Google Colab 提供的 GPU 上完成了训练。

使用微调模型进行推理

使用与上面的 CORD 演示类似的脚本,我们可以使用

from donut import DonutModel
from PIL import Image
import torch
model = DonutModel.from_pretrained("./donut/result/train_sroie/20220804_214401")
if torch.cuda.is_available():
    model.half()
    device = torch.device("cuda")
    model.to(device)
else:
    model.encoder.to(torch.bfloat16)
model.eval()
image = Image.open("./sroie-donut/test/099.jpg").convert("RGB")
output = model.inference(image=image, prompt="<s_sroie-donut>")
output

请注意,我们已经在DonutModel.from_pretrained()调用中更改了模型路径,并且我们还将推理prompt更改为<s_{dataset_name}>的格式。这是我用的图片:

图片来自 SROIE 数据集

这些是我的结果:

{'predictions': [{'address': 'NO 290, JALAN AIR PANAS. SETAPAK. 53200, KUALA LUMPUR.',
   'company': 'SYARIKAT PERNIAGAAN GIN KEE',
   'date': '04/12/2017',
   'total': '47.70'}]}

最后的想法

我注意到,使用 Donut 的伪 OCR 的最终输出比传统的现成 OCR 方法要准确得多。作为一个极端的例子,下面是使用 Tesseract 的 OCR 引擎进行 OCRed 的演示中的同一个 CORD 文档:

*' il " i
- ' s ' -
W =
o o
ok S
?flfi (€
rgm"f"; o ;
L i 4

图像模糊,对比度低,甚至对人来说也难以阅读,所以不太可能有人会期望模型能够识别角色。令人印象深刻的是,甜甜圈能够用自己的技术做到这一点。即使对于高质量的文档,尽管其他商业 OCR 模型比开源 OCR 引擎(如 Tesseract)提供的结果更好,但它们通常成本很高,并且因为商业数据集的强化训练和更强的计算能力而更好。

解析给定文档的 OCR 输出的模型的替代方案包括仅利用计算机视觉技术来突出显示各种文本块、解析表格、或者识别图像、图形和数学等式,但是如果可以导出有意义的数据,则再次要求用户对包围盒输出进行 OCR。库包括 LayoutParserdeep doc detection,两者都连接到 Detectron2 计算机视觉模型的模型动物园以交付结果。

此外,Donut 的作者还提供了一个测试脚本,您可以使用它为您的微调模型开发评估指标,该脚本位于 Donut 代码库中的 test.py 文件中。它提供 F1 准确度分数,该分数是基于对基本事实解析的准确通过或失败来测量的,以及由树编辑距离算法给出的准确度分数,该算法确定最终 JSON 树与基本事实 JSON 的接近程度。

cd ./donut &&
python test.py --dataset_name_or_path ../sroie-donut --pretrained_model_name_or_path ./result/train_sroie/20220804_214401 --save_path ./result/train_sroie/output.json

使用我的 SROIE-finetuned 模型,我的所有 20 个测试图像的平均准确率为 94.4%。

Donut 还与 SynthDoG 打包在一起,synth dog 是一个模型,可用于以四种不同的语言生成额外的伪造文档以进行数据扩充。它在英文、中文、日文和韩文维基百科上接受了培训,以便更好地解决传统 OCR/VDU 方法的问题,这些方法通常因缺乏除英语之外的大量语言数据而受到限制。

[1] Kim,Geewook 等,“无 OCR 文档理解转换器”(2021).麻省理工许可证。

[2]黄征等,“ICDAR2019 扫描收据 OCR 和信息提取竞赛” 2019 文档分析与识别国际会议(ICDAR) 。IEEE,2019。麻省理工许可证。

Neha Desaraju 是德克萨斯大学奥斯丁分校学习计算机科学的学生。你可以在网上的estau dere . github . io找到她。

十月版:人工智能艺术新时代

原文:https://towardsdatascience.com/october-edition-a-new-age-of-ai-art-1dfb1c823bde

月刊

面对强大的新工具,我们对创造力的理解是如何变化的?

Pierre BaminUnsplash 上拍摄的照片

人工智能生成的图像已经存在了很长时间,但多年来,它们巨大的创造潜力仍然超出了大多数人的能力。进入的门槛很高:你需要获得访问专有模型的邀请和使它们工作的编码技能,以及大量的计算能力。

然后,短短几个月,一切都变了。从今年春天开始,新的发展使这个领域变得更广阔,更容易接近,并且作为一种小众消遣更难忽视或摒弃。基于云的工具降低了成本,而开源模型和文本到图像的界面将每个人都变成了潜在的创作者,每个创作者都变成了潜在的艺术家。

伴随着无处不在和流行而来的是许多新的、紧迫的问题。当一串精心制作的文字可以产生令人惊叹的视觉效果时,艺术家的角色会发生怎样的变化?基于从数十亿数字图像中提取的元素的组合和操作,原创性仍然是艺术的指导原则吗?

还有其他潜在的后果需要考虑。受过专业训练的插画师和设计师的职业前景如何,他们的手艺既贵又费时?每当一个强大的工具被大规模(有时甚至是混乱的)采用时,它可能带来的潜在危害——尤其是对边缘化的人群和社区——又是如何呢?然后,围绕版权和所有权,还有一大堆我们尚未解开的问题:当一幅图像通过人类与人工智能的合作产生时,它到底是谁的创作?

这些问题都需要时间来解决,但开始思考这些问题永远不会太早。为了帮助你学习和探索,我们选择了几篇最近的文章,从不同角度处理人工智能生成的图像。我们希望你喜欢我们下面的选择,并希望它能激发你阅读更多关于这个话题的文章。

对于我们中会员的读者:感谢你们的支持。

TDS 编辑器

TDS 编辑亮点

  • 人工智能生成的艺术:如何开始生成自己的图像(2022 年 9 月,9 分钟)
    文本到图像生成模型的提示设计初学者指南
    (2022 年 9 月,9 分钟)如果你是一个想要在对人工智能图像生成器提出的更广泛的问题形成看法之前了解它们是如何工作的实践型学习者,这份双它们涵盖了选择模型的基础,讨论了一些重要的后勤细节,并介绍了迅速工程的新兴学科。****
  • 【DALL E 2】解释道:一个革命性 AI 的承诺与局限(2022 年 6 月 23 分钟)
    稳定扩散是有史以来最重要的 AI 艺术模式
    (2022 年 8 月 9 分钟)
    阿尔韦托·罗梅罗长期以来一直在报道 AI 艺术节拍,在这些创新激发的敬畏和如果你想退一步了解今年最轰动的两篇文章——DALL E 2 和 Stable Diffusion——把这两篇文章加入你的阅读清单;他们提供了大量的背景和细微差别。
    ****
  • 融合 AI 的力量与诗意的细腻(2022 年 6 月 7 分)
    AI 的创造力当然不仅限于图像;在许多方面,围绕语言模型如 GPT-3 的讨论为我们当前对文本到图像工具的痴迷奠定了基础。Salvatore Raieli 将文字和图像结合在一起,进行了一项有趣的实验:他不再使用偶尔没有灵魂的图像提示语言,而是转向俳句,以测试模特将诗歌转化为绘画作品的能力。
  • 基于相似性的图像搜索视觉艺术(2022 年 5 月,6 分钟)
    AI 艺术围绕风格和影响力提出了很多问题。Catherine Yeo 探讨了一个相关的话题,这个话题可能在未来围绕原创性和所有权的辩论中至关重要:我们如何确定图像之间的相似程度?也许不出所料,这是一个非常复杂的问题。

原始功能

我们最新精选的问答、播客和阅读推荐。

热门帖子

如果你错过了它们,这里有一些 9 月份 TDS 上阅读量最大的帖子。

We welcomed a fantastic cohort of new contributors last month — including Anton Rubert, Meysam, Alessandro Paticchio, Daniel Furman, Francesca Argenziano, Gabriel Furnieles, Alex Roberts, Thomas Bury, Yunchao "Lance" Liu (刘运超), Ayoub Omari, Patrick Staehli, Carlos Costa, Steve Dennis, Mathias Grønne, Taimur Ijlal, Kenneth Joel, Tim de Boer, Thomas Reinecke, Jordan G., Siddarth Ramesh, Finn-Ole Höner, Morgan Lynch, M. Rake Linggar A., Sanjay Adhikesaven (with Abyan Das and Monish Muralicharan), Paul Kinsvater, Sam Minot, Ido Leshem, Dmitrijs Trizna, Lucy Dickinson, Benjamin Marie, Gael Close, Claire Longo, Dimitre Oliveira, Mohammad Vali, Marcin Kozak, James Scott Cardinal, Alexandre Allouin, Janna, Gabriel Pastorello, and Marian Nodine, among others. If you’d like to become a TDS author, take the plunge and share your work with us.

下个月见!

旧原则,新方法:贝叶斯在实践中

原文:https://towardsdatascience.com/old-principles-new-approaches-bayes-in-practice-f0c3714a68d3

在像数据科学这样以创新为中心的学科中,仅仅几年前还是尖端的方法今天可能会感觉过时。这使得贝叶斯统计——一套有近三个世纪历史的原则——享有如此长的保质期变得更加不可思议。

贝叶斯定理及其衍生应用不是你在大学统计学课程中学到的东西,只会被迅速存档在你记忆的遥远边缘。每天,数据科学和机器学习从业者都在很好地利用这些概念,并找到新的方法在他们的项目中利用它们。

本周,我们来看几个展示贝叶斯方法持久性的当代用例。让我们开始吧。

Tara BUnsplash 上拍摄的照片

  • 给你的分类任务一个贝叶斯提升 。在他的新解释器中,micha oles zak涵盖了朴素贝叶斯分类算法的基础知识(如果你对这个主题不熟悉,这是一个很好的起点!).他接着指出,在某些情况下,去除算法天真的独立性假设有助于提高模型的准确性。
  • 重新看待排名问题 。部分统计演练,部分实践教程,Robert kübler博士的文章演示了如何构建一个模型,让您对一组球员进行排名(包括您需要的所有 Python 代码),并阐明了为什么整合先验信念(贝叶斯技术的核心方面)会导致更稳健的排名。

虽然我们中的许多人可以连续几天钻研贝叶斯理论,但你也可能会阅读一些其他主题的优秀读物。以下是我们最近最喜欢的几个:

  • 你能使用一个机器学习模型来增强另一个机器学习模型吗?Ria Cheruvu 为复合人工智能系统提供了案例。
  • Erin Wilson 的新帖使复杂的工作流程对初学者变得容易理解:学习如何用 PyTorch 建模 DNA 序列。
  • Derrick Mwiti 为任何想要使用 TensorFlow 2 对象检测 API 进行图像分割(当然还有对象检测)的人提供了一个全面的介绍。
  • 新的在线书籍提醒:我们很高兴分享来自Mathias grnne自动编码器广泛介绍的第一章。
  • 免安装交互式 Python 应用?!是的,你也可以通过跟随萨姆·迈诺特的 TDS 处女作来构建它们,这是一个有用的、基于 Streamlit 的教程。
  • 了解如何使用 Apache Spark帕纳尔·埃尔索伊分享一份耐心的端到端指南。
  • 不要错过 Lynn Kwong 的最新贡献,它关注于有效地将大量记录插入数据库的不同方法。

我们喜欢与您分享伟大的数据科学成果,而您的支持— 包括您的中级会员资格 —让这一切成为可能。谢谢大家!

直到下一个变量,

TDS 编辑

OLTP 与 OLAP:有什么不同

原文:https://towardsdatascience.com/oltp-vs-olap-9ac334baa370

了解 OLTP 和 OLAP 数据系统之间的区别

Unsplash 上的 Shubham Dhage 拍摄的照片

介绍

对于几乎所有利用数据的现代组织来说,数据都是一种不断发展的资产,目的是通过分析一段时间以来收集的数据(历史数据)推断出的洞察力来做出更明智的决策。

在数据领域中,有两种主要类型的处理系统可用于解决数据问题,即联机事务处理(OLTP)和联机分析处理(OLAP)。

OLAP 和 OLTP 都是在线处理系统,通常会相互混淆——在今天的文章中,我们将探究它们的主要区别讨论如何选择更适合您特定用例的

在线事务处理(OLTP)

OLTP 代表在线事务处理,该术语指的是能够执行大量数据库事务(即新的插入、更新和删除)的系统,这些事务可以由组织及其产品中的多人(或用户)执行。

OLTP 系统的几个典型示例包括

  • 自动取款机
  • 预订系统
  • 网上银行
  • 网络购物

OLTP 系统支持的操作通常在关系数据库上执行,关系数据库被认为是高度可用的,并且为处理大量事务进行了优化(例如,索引数据库可以在这个方向上有所帮助)。此外,对于 OLTP 系统所使用的关系数据库来说,以一种维护数据完整性的方式进行设计也是很重要的,同时也保证了多个用户的访问。

换句话说,OLTP 系统应该用在数据处理而不是数据分析的环境中——它们通常针对高效和有效地处理大量数据进行了优化。

在线分析处理(OLAP)

另一方面,OLAP 代表在线分析处理,该术语指的是为对大量数据进行分析而优化的系统。

在大多数情况下,由 OLAP 系统存储、处理或消费的数据通常驻留在一个数据仓库或整个组织中几乎任何一个中央数据存储中,就像一个数据网

OLAP 系统的几个例子包括:

  • 商业智能工具
  • 报告工具
  • 数据挖掘

典型的 OLAP 系统允许多维分析。这是通过 OLAP 立方体实现的,它是多维数据阵列,其中为数据维度添加了层。如果您使用过商业智能工具,您可能会遇到“下钻”功能,在该功能中,您可以访问不同的数据层,这一功能是由于 OLAP 立方体而实现的。

主要区别

简而言之,OLAP 系统和 OLTP 系统之间的主要区别在于,前者针对分析处理进行了优化,而后者针对事务处理进行了优化。这是显而易见的吧?现在让我们更具体地比较一下这两种处理系统。

目的

OLAP 系统的目的是发现隐藏的模式,提取洞察力,并帮助企业和组织的决策者做出更明智的决策,这些决策由基于特定问题(即查询)的数据支持。相比之下,OLTP 系统旨在控制和执行实时业务操作。

查询

OTLP 系统包含一组相当简单的查询,这些查询应该执行所需的事务,包括插入、更新和删除。

另一方面,OLAP 系统通常涉及复杂的选择查询,其中可能会执行多个聚合,以便提取所需的洞察力并回答特定的业务问题。

供货情况

OLTP 系统应该高度可用,因为它们非常频繁地插入或修改数据,这是意料之中的,因为它是一个事务性系统。此外,可能还需要频繁备份来维护数据完整性。

另一方面,OLAP 系统不在现有数据上执行任何类型的事务,因此不需要在高可用性机制上大量投资(尽管这是理想的)。因此,此类系统需要的备份频率较低。

加工时间

OLTP 系统必须尽可能响应迅速,响应时间不应超过几毫秒。在 OLTP 系统上执行的查询相当简单(插入、更新、删除等)。)并且还节省空间,因此不太难实现低处理时间。

另一方面,OLAP 系统的响应时间可能从几秒到几分钟不等。此类系统中的工作负载涉及大量读取密集型数据,与 OLTP 系统相比,需要复杂得多的查询。

OLTP 和 OLAP 系统在预期响应/处理时间方面的差异是意料之中的,因为前者应该处理大量的小型交易,而后者应该处理大量的数据并涉及相当复杂的查询。

数据源

正如我们已经提到的,OLTP 系统通常使用关系数据库——比如 DBMS——作为它们的数据源,通过它们可以实时执行所请求的事务。

相比之下,OLAP 数据库有一个多维模式(回想一下我们前面讨论的 OLAP 立方体), OLTP 数据库可以用作 OLAP 系统的数据源。

在接下来的几节中,我们将讨论 OLTP 和 OLAP 系统如何协同工作,并探索一个具体的用例来演示在现实生活中如何实现这一点。

数据库 表格 设计

出于提高效率的目的,OLTP 系统需要规范化的表,而 OLAP 系统需要非规范化的表,这更适合数据分析。

OLAP 和 OLTP 如何协同工作

在许多组织中,OLAP 和 OLTP 数据系统都将就位,并且在大多数情况下,OLAP 系统将从 OLTP 系统接收数据。

换句话说,我们可以说 OLTP 应该修改数据系统,而 OLAP 系统则用于根据 OLTP 系统接收的数据,通过查询来回答问题。

ETL 的合力

作为一个例子,让我们考虑提取-转换-加载(ETL)过程,这在数据工程的上下文中是非常常见的。ETL 工具将从通常为 OLTP 系统的各种数据源接收(加载)数据,对加载的数据执行一些处理(转换),并将其发送到通常为 OLAP 系统(比如数据仓库)的目标系统(加载),在该系统中,可以通过报告和商业智能工具执行查询,以提取有意义的信息、模式和见解,最终帮助组织做出决策。

换句话说,ETL 过程最初将涉及从中提取数据的 OLTP 系统,然后将数据加载到目标 OLAP 系统中,以供查询和分析使用。

最后的想法

在今天的文章中,我们讨论了分析和事务系统,以及联机事务处理(OLTP)和联机分析处理(OLAP)在用途、每个系统执行的查询的性质、查询中涉及的操作、可用性、处理和响应时间、各自使用的数据源以及数据库和表设计方面的差异等方面的主要区别。

此外,我们还讨论了每种数据系统的一些典型用例。最后,我们探讨了组织通常如何将两种数据处理系统结合起来,并讨论了提取-转换-加载的概念,在这一概念中,OLAP 系统和 OLTP 系统联合起来使其变得生动。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

相关文章你可能也喜欢

https://betterprogramming.pub/kafka-cli-commands-1a135a4ae1bd

理解遗漏变量偏差

原文:https://towardsdatascience.com/omitted-variable-bias-and-what-can-we-do-about-it-344ac1477699

因果数据科学

最普遍的偏见类型的逐步指南

作者图片

在因果推理中,偏差是非常成问题的,因为它使推理无效。偏差通常意味着估计者不会给出因果效应的平均估计值。

这就是为什么,一般来说,我们更喜欢无偏的估值器,代价是更高的方差,也就是更多的噪声。是不是说每个有偏估计量都没用?实际上不是。有时,有了领域知识,我们仍然可以得出因果结论,即使是有偏见的估计。

在本帖中,我们将回顾一个具体但常见的偏差来源,省略变量偏差(OVB) 。我们将探索偏见的原因,并利用这些见解做出因果陈述,尽管存在偏见。

理论

假设我们对变量 D 对变量 y 的影响感兴趣。然而,还有第三个变量 Z 我们没有观察到,它与 Dy 都相关。假设数据生成过程可以用下面的 有向无环图(DAG) 来表示。如果你不熟悉 DAGs,我在这里写了一个简短的介绍

作者图片

由于从 Dy 有一条 后门路径 经过 Z ,我们需要以 Z 为条件进行分析,以恢复 Dy 的因果关系。如果我们可以观察到 Z ,我们将在 DZ 上运行 y 的线性回归来估计以下模型:

作者图片

其中 α 为利息的影响。这种回归通常被称为长回归,因为它包括模型的所有变量。

然而,由于我们没有观察到 Z ,我们不得不估计以下模型:

作者图片

相应的回归通常被称为短回归,因为它不包括模型的所有变量。

估计短回归而不是长回归的后果是什么?简而言之,我们不能给估计的系数一个因果解释

在这种情况下, α 的 OLS 估计量为

作者图片

因此,我们可以把省略变量偏差写成

作者图片

这个公式的美妙之处在于它的可解释性:被省略的变量 bias 仅由两个部分组成,两者都非常容易解释。

  • γ:Z对 y的影响**
  • δ😄对 Z 的影响

请注意,这是一个渐近偏差,这意味着随着样本量的增长,估计量不会收敛到它应该估计的参数(estimand)。或者,我们可以说估计量是 一致

附加控制

如果我们在回归中有个额外的控制变量会发生什么?例如,假设除了感兴趣的变量 D 之外,我们还观察到其他变量的向量 X ,因此长回归

作者图片

由于有了 弗里希-沃-洛厄尔定理 ,我们可以简单地将部分剔除 X 并用 DZ 来表示省略的变量 bias。

作者图片

其中 D⊥X 是从 X 上回归 D 的残差,而 Z⊥X 是从 X 上回归 Z 的残差。如果你不熟悉弗里希-沃-洛厄尔定理,我在这里写了一个简短的注释

Chernozhukov、西内利、纽伊、夏尔马和 Syrgkanis (2022) 进一步将分析概括为处理变量 D 、控制变量 X、和未观察变量 Z 以非参数方式进入长模型的设置,即没有特定的函数形式。你可以在他们的论文中找到更多的细节,但是基本思想是一样的。

例子

假设我们是一名对教育和工资之间的关系感兴趣的研究员。从未来的工资来看,投资教育有回报吗?假设我们有受教育年限不同的人的工资数据。为什么不看看受教育年限和工资的相关性?

问题是可能有许多未观察到的变量与教育和工资都相关。为了简单起见,我们集中讨论一下能力。能力较高的人可能会决定在教育上投入更多,只是因为他们在学校表现更好,获得更多机会。另一方面,他们也可能会获得更高的工资,纯粹是因为他们天生的能力。

我们可以用下面的有向无环图 (DAG)来表示数据生成过程。

作者图片

让我们加载并检查数据。我从[src.dgp](https://github.com/matteocourthoud/Blog-Posts/blob/main/notebooks/src/dgp.py)导入数据生成过程,从[src.utils](https://github.com/matteocourthoud/Blog-Posts/blob/main/notebooks/src/utils.py)导入一些绘图函数和库。

from src.utils import *
from src.dgp import dgp_educ_wages

df = dgp_educ_wages().generate_data(N=50)
df.head()

作者图片

我们有 300 个人的信息,我们观察他们的 T2、T3、T4 和当前的 T5。

假设我们在education上直接回归wage

short_model = smf.ols('wage ~ education + gender + age', df).fit()
short_model.summary().tables[1]

作者图片

education的系数为正且显著。然而,我们知道可能有一个遗漏变量偏差,因为我们没有观察到ability。就 Dag 而言,从educationwage有一条后门路径通过ability未被阻塞,因此会使我们的估计产生偏差。

作者图片

是不是说我们所有的分析都是垃圾?我们还能从回归结果中得出一些因果结论吗?

偏向的方向

如果我们知道 γδ 的符号,我们可以推断偏差的符号,因为它是两个符号的乘积。

作者图片

在我们的例子中

作者图片

让我们分别分析这两种相关性:

  • abilitywage之间的相关性很可能是正的
  • abilityeducation之间的相关性很可能是正的

因此,偏差最有可能是。由此,我们可以得出结论,我们对educationwage回归的估计很可能是对因果效应的高估,这很可能更小。

这可能看起来像一个小的洞察力,但它实际上是巨大的。现在我们可以很有信心地说,一年的education每月最多给wages增加95 美元,这是一个比仅仅说估计有偏差更有信息量的说法。

一般来说,我们可以在一个 2 乘 2 的中总结偏差的不同可能影响。

偏向的方向(作者图片)

进一步的敏感性分析

在不做强有力假设的情况下,我们能说更多关于省略变量偏差的 T21 吗?

答案是肯定的!特别是,我们可以问自己:偏相关 γδ 应该有多强,才能推翻我们的结论?

在我们的例子中,我们发现数据中的educationwages之间存在正相关关系。然而,我们知道我们在回归中省略了ability。问题是:abilitywageγabilityeducationδ 之间的相关性应该有多强,才能使效果不显著甚至为负?

西内利和黑兹利特(2020) 表明,我们可以根据所解释的残差变异,即决定系数 R 来转化这个问题。这种方法的优点是可解释性。猜测所解释的方差百分比比猜测条件相关的大小要容易得多。

作者编写了一个配套包[sensemakr](https://github.com/carloscinelli/sensemakr)来进行敏感性分析。你可以在这里找到包的详细描述。

我们现在将使用Sensemakr函数。Sensemakr函数的主要参数为:

  • model:我们要分析的回归模型
  • treatment:感兴趣的特征/协变量,在我们的例子中是education

我们将尝试回答以下问题:

对于 *education* *wages* 变化符号 的效果*ability*``*education*需要说明 *education* (x 轴)和 (y 轴)的残差变化量有多大?

import sensemakr

sensitivity = sensemakr.Sensemakr(model = short_model, treatment = "education")
sensitivity.plot()
plt.xlabel("Partial $R^2$ of ability with education");
plt.ylabel("Partial $R^2$ of ability with wage");

中,我们看到abilityReducationwage的局部(因为以agegender为条件)如何影响wageeducation的估计系数。标有三角形(0,0) 坐标对应于当前估计值,反映了如果abilitywageeducation都没有解释力会发生什么:什么都没有。随着ability的解释力增加(从三角形向上和向右移动),估计系数减小,如水平曲线所示,直到在红色虚线处变为零。

我们应该如何解读剧情?我们可以看到,为了消除educationwages的影响,我们需要ability来解释educationwage中大约 30%的剩余变化,对应于红线。

你现在可能(有理由)会问:30%是多少?是大还是小?我们可以通过基准测试用另一个观察到的变量解释的剩余方差的结果来了解部分 R 的大小。我们以age为例。

Sensemakr函数接受以下可选参数:

  • benchmark_covariates:用作基准的协变量
  • kdky:这些自变量参数化了与观察到的基准协变量(age)相比,未观察到的变量(ability)相对于治疗(kd)和结果(ky)强多少倍。在我们的例子中,设置kdky等于 [0.5,1,2] 意味着我们想要研究一个变量的最大强度是age的一半、相同或两倍(在解释educationwage变化时)。
sensitivity = sensemakr.Sensemakr(model = short_model, 
                                  treatment = "education",
                                  benchmark_covariates = "age",
                                  kd = [0.5, 1, 2],
                                  ky = [0.5, 1, 2])
sensitivity.plot()
plt.xlabel("Partial $R^2$ of ability with education");
plt.ylabel("Partial $R^2$ of ability with wage");

看起来即使ability有两倍于age的解释力,educationwage的影响仍然是正面的。但是它会是统计上显著的吗?

我们可以重复同样的练习,查看 t 统计量而不是系数的大小。我们只需要将绘图功能中的sensitivity_of选项设置为等于t-value

在这种情况下,我们试图回答的问题是:

对于*education*``*wages*变得不显著的影响*education**ability**中的残差变化有多大?*******

sensitivity.plot(sensitivity_of = 't-value')
plt.xlabel("Partial $R^2$ of ability with education");
plt.ylabel("Partial $R^2$ of ability with wage");

从图中我们可以看出,为了使educationwage的影响不显著,我们需要ability来解释educationwage中大约 5%到 10%的剩余变化。特别是,红线绘制了 t 统计量等于 2.01 的水平曲线,对应于 5%的显著性水平。从与age的比较中,我们看到稍强一点的解释力(大于1.0x age)就足以使educationwage的系数不具有统计显著性。

结论

在这篇文章中,我介绍了省略变量偏差的概念。我们已经看到了如何在一个简单的线性模型中计算它,以及我们如何利用变量的定性信息在省略变量偏差的情况下做出推断。

这些工具非常有用,因为省略变量偏差基本上到处都是。首先,总有我们没有观察到的因素,比如我们玩具例子中的能力。然而,即使我们可以观察一切,遗漏变量偏差也可能以模型错误设定的形式出现。假设wages二次依赖age。然后,从回归中省略二次项会引入偏差,可以使用我们用于ability的相同工具对其进行分析。

参考

[1] C .西内利,c .黑兹莱特,弄清楚敏感性:扩展省略变量偏倚 (2019),英国皇家统计学会杂志

[2] V. Chernozhukov,c .西内利,W. Newey,A. Sharma,V. Syrgkanis,长话短说:因果机器学习中省略的变量偏差 (2022),工作论文。

相关文章

密码

你可以在这里找到 Jupyter 的原始笔记本:

**https://github.com/matteocourthoud/Blog-Posts/blob/main/notebooks/ovb.ipynb **

感谢您的阅读!

我真的很感激!🤗如果你喜欢这个帖子并想看更多,可以考虑 关注我 。我每周发布一次与因果推断和数据分析相关的主题。我尽量让我的帖子简单而精确,总是提供代码、例子和模拟。

还有,一个小小的 免责声明 :我写作是为了学习所以错误是家常便饭,尽管我尽了最大努力。当你发现他们的时候,请告诉我。也很欣赏新话题的建议!

论人工智能艺术

原文:https://towardsdatascience.com/on-ai-art-531a4506c518

意见

AI 是艺术,艺术吗?是的。该不该卖 AI 艺术?大概不会。

迪伦·亨特在 Unsplash 上的照片

人工智能艺术,即很大一部分由机器学习模型生成的艺术,已经存在多年了。它的根源可以追溯到,但它真正出现是在 2010 年代深度学习爆炸之后,尤其是在 DeepDream 之后。在过去几年中,扩散模型的改进,大型生成模型如 DALL EStableDiffusion 的培养,以及midway以艺术为中心的商业化,将 AI 艺术推向了前沿。结果是惊人的,但这些新的发展也提出了许多关于这些工具的使用以及它们对艺术家意味着什么的问题。

我将对三个主要问题发表我的看法:

  1. AI 艺术是艺术吗?是的。
  2. AI 艺术是否不道德?目前,大概。
  3. 事情会如何发展? AI 工具会留下来,美工会采用。

免责声明:当我谈论艺术家时,我指的是工匠,比如插画师、角色设计师、概念艺术家、3D 建模师、UI 设计师、作家等等。此外,我使用术语人工智能相当宽松。

我不是艺术史学家,也没有其他资格。另一方面,我也不是在出售艺术品,也不是在开发生成模型,所以希望我能提供一个稍微中立的视角。我自己的职业与编码有关,我意识到技术离给我自己带来类似的情况只有几步之遥。我认为,在这种情况下,所有适用的要点仍然有效。

人工智能艺术就是艺术

人工智能艺术会一直存在。有些人痛苦地声称它不是艺术。我认为这个论点站不住脚。艺术总是扩展它所包含的媒体和技术,并且总是采用新的技术,即使是在艺术界一些人的抵制下。只看生成艺术,数字艺术,或者摄影。这就是艺术的本质。这种流动性和适应性使艺术成为不可定义、不可理解、不断进化的魅力。

过去有数不清的艺术运动,其中也有一些(像达达主义和许多生成艺术)拥抱了随机性和无意的。在某种程度上,人工智能艺术采取了类似的路线,因为艺术家必须对他们的提示进行随机解释。

此外,即使你相信创造艺术是人类独有的特权,也不得不说人工智能艺术并非没有人类的参与:人类提示、提供输入、策划、提炼、重新排列、修饰和“合作”。他们与人工智能合作,就像摄影师与相机和他们周围的世界合作一样。互动的水平因作品和艺术家而异,但可以肯定地说,就像你需要技巧来制作一张精湛的照片一样,你需要技巧来制作一件真正好的人工智能艺术作品。

非艾非艺术形象,由作者提供。

另一个证据是人们在网上多次重复的说法,即生成性人工智能模型让他们表达自己,即使缺乏艺术工艺方面的技能。即使大多数人目前使用人工智能模型的方式只允许有限的控制,但它似乎是细粒度的,交互式的,表达能力足够让人们表达自己。并且越来越多的高级工作流必然会发展起来。直觉上,这听起来像是艺术。

你不必相信我的话。世界各地的画廊已经开始展示人工智能艺术(例如纽约的 HG 当代 ,汉堡的 伍兹艺术学院 ,新德里的Nature Morte更多)。)到目前为止,这些作品可能大多被视为新奇的东西,我不认为它们会在任何可预见的未来取代优秀的艺术家。我们大多数人倾向于只在涉及大量人类创作的作品中寻找意义,至少在概念层面上是如此。

我相信对于整个艺术来说,人工智能工具是令人兴奋的。他们开辟了新的途径——尤其是对于那些想要艺术地表达自己,但由于这样或那样的原因而无法花费年复一年的时间来学习一门手艺的人。更重要的是,对于艺术家来说,人工智能工具打开了通向新美学、新风格、新工作流程的大门,毫无疑问,它们将拓宽视觉和其他艺术的范围。

总之,我认为人工智能模型是艺术创作者的工具箱中一个有艺术价值的附加物。我认为,对人工智能艺术的艺术价值的攻击忽视了其创作中的人的因素,也许是由一种(合理的)不公正感驱动的。如果你想批评当前的人工智能艺术,我相信还有其他更有效的途径,我将在下一节讨论它们。

人工智能艺术的当前问题

对人工智能艺术的批评最尖锐的表现是,人们花费数年,如果不是数十年,来掌握一门手艺,以便在已经紧张的市场中与许多地方的剥削性劳动行为竞争。他们在艺术界辛辛苦苦赢得的地位和谋生的能力现在受到部分依赖于他们自己作品的技术的威胁,这是一个悲剧,不应该掉以轻心,即使随后的辩论自然充满情绪。

最明显的问题是版权:目前最好的模型是建立在大量有版权的材料上的,他们可能不应该利用这些材料,尤其是不应该用于商业目的。如果没有这些大量的训练数据,人工智能模型将无法像当前模型那样创造出质量惊人的艺术品。

这种做法尤其残忍,因为从人工智能艺术的进步中受害最深的人是那些不情愿地提供了让模型如此强大的艺术的艺术家。

将樱桃放在最上面的是,使用最流行的模型,您可以故意模仿训练数据集中一些艺术家的风格。虽然这是一个有趣的学术实践,但在向公众发布具有这种功能的工具之前,征求艺术家的许可似乎是合理的——特别是当需要钱的时候。

除了未经请求就在艺术家作品上训练模型的道德问题之外,还有许多其他问题我不会涉及,例如:

  • 那些模型学到了什么种族主义、性别歧视或其他偏见,如何修正它们?
  • 不同文化及其风格的表现呢?
  • 那些大 AI 模型是谁在训练,又是出于什么动机?
  • 当新的人工智能模型越来越多地根据已经遍布互联网的其前身模型的输出进行训练时,将会发生什么?

人工智能艺术是不可阻挡的

人工智能艺术会一直存在。从现在开始,总会有模型使用他们不应该使用的数据——数据就在那里,访问很难监管。在大多数情况下,如果不明显,甚至不可能证明你的艺术被用于培训。这是我们必须面对的新现实。我相信艺术界已经有了足够的骚动和关注,关于艺术使用的更好的规则和实践将会被制定和执行。然而,合法的事情和已经做的事情并不总是一致的,在国际层面上,在不同的文化中,就相同的法规达成一致也可能并不容易。

我还可以设想一种情景,一旦尘埃落定,大型科技公司将与艺术和图像托管网站达成协议,以获得处理其用户数据的权利。这是我的推测。

即使人工智能模型只使用未经许可的图像,更有效的模型,委托艺术家建立合法数据集的努力,以及艺术家愿意免费贡献他们的工作,都将不断增加这些模型的能力。此外,据稳定扩散 背后的 CEO 称,不到 0.1%的数据集是 art

人工智能艺术还没有出现

当前的模型非常有效,可以产生惊人的结果,但它们远没有非人工智能艺术家所需的灵活性、一致性、质量、可靠性或适应性,特别是如果他们在行业中工作或与客户打交道。

不幸的是,人工智能人因高估他们领域的进步速度而出名。在任何有意义的规模上,我们可能离取代艺术家还有一段距离(尤其是一旦版权问题出现)。然而,我们不应该低估人工智能艺术对非人工智能艺术家的影响。例如,如果你在密码驱动的艺术品拍卖网站上寻找,就不难找到人工智能生成的艺术品,这些艺术品可能会与艺术家直接竞争,这些艺术家的作品被用来训练用于创作它们的模型。

艺术家仍然有一席之地

这听起来可能像是一种安慰奖,但艺术家仍然会受到欢迎,即使有强大的人工智能模型作为工具。艺术家不应该降低他们创造漂亮作品的能力。概念和构图工作,确保艺术品符合虚拟世界的整体故事或游戏的机制,跨跨学科团队工作,策展,以及手动微调,纠正和精炼人工智能生成的输出仍然需要艺术家的技能。最重要的是,经验仍然是有价值的,这就是为什么资深艺术家、艺术总监或艺术经理可能没有什么可担心的。我相信人-人工智能协作工作流程将在未来几年或几十年内大大推进,允许更多的控制,从而对训练有素的艺术家的技能有更多的需求。能够快速适应和采用的艺术家可能会发现人工智能工具是一个福音而不是一个祸害。

然而,人工智能辅助的工作流程永远不会完全取代传统艺术家。正如艺术中任何新媒介或新技术的引入一样,它的前身不会被完全取代。我们甚至有著名的(超)现实主义艺术家,尽管今天每部手机都配有令人惊叹的(人工智能支持的)摄像头。

非人工智能艺术家的市场可能会变得更加紧张。虽然,具体在什么时间范围,到什么程度还不清楚。从好的方面来看,也可能会有“反弹效应”:随着艺术家通过使用人工智能工具提高工作效率,委托的工作量也会增加,因为对艺术品的需求存在弹性。随着它们的生产成本越来越低,客户可以增加项目或每个项目的艺术品数量。

含义

我目前的收获如下。

  1. 不要使用人工智能模型使用受版权保护的数据用于你出售的艺术或以任何方式用于与非人工智能艺术家竞争(例如,在线竞争注意力)。
  2. 不要为使用版权数据的模特付费
  3. 也许这不是暗示,而是一个诚实的问题:给人工智能产生的艺术贴上这样的标签。
  4. 如果你是一名艺术家,不希望你的作品被包含在未来的数据集中,请求将你的作品从未来的黑貂扩散模型中排除可能是你能做的最好的事情。

为了结束这篇文章,我还想提一下,关于人工智能艺术的辩论并不是视觉艺术所独有的。大型语言模型早就被宣布为新的报纸专栏作家对冲基金已经在大量使用人工智能,甚至程序员也可能成为被人工智能工具取代的。卡车司机和放射科医生在很大程度上幸免于难,因为我们不放心把生死决定权交给一台机器(这是理所当然的!).

作为一个社会,我们不得不考虑这样一个事实,尤其是基于信息的工作越来越自动化。具有讽刺意味的是,许多体力劳动似乎是最难自动化的,因为机器人技术仍然落后于人工智能的快速进步。

感谢您的阅读!我希望你学到了新东西。如果您不同意或有见解想要分享,请参与进来!我愿意改变我的想法和文章。

包含相似和不同镜头的一些附加资源:

人工智能治理:如何开始

原文:https://towardsdatascience.com/on-ai-governance-how-to-get-started-225bd1f3cc21

意见

迈向可持续和负责任的人工智能

克拉克·蒂布斯在 Unsplash 上拍摄的照片

我们已经进入了由人工智能(AI)驱动的新一波数字恐惧。这并不新鲜。在 20 世纪 50 年代,不断增长的生产需求、不断上升的安全问题和不断下降的工作生活平衡导致了工厂机械的引入。这导致了尖锐的批评,如失业和产品质量下降。回想起来,它产生了积极的影响,但当时我们没有足够的信任。我们目前观察到 AI 也有类似的反应。

人工智能有许多名称,如大数据、数据科学和机器学习。在其核心,人工智能是高级分析和基于逻辑的数学技术的应用,以解释事件,支持和自动化决策,并采取行动。当一个决策有许多复杂的输入时,它就会发光。计算机大脑(也称 AI)的计算能力比人强,人脑的创新能力比计算机强。

它的成功在媒体上有详细的记录。例子包括谷歌的自然语言处理搜索和网飞的内容推荐引擎。此外,麦肯锡的全球人工智能调查声称,高绩效的人工智能组织至少有 20%的收入增长。宣传本身会激励其他人去尝试。

在企业背景下,人工智能的实施仍然是一个暗箱操作。算法处理需要具备高等数学知识的个人,通常是博士级别的数据科学家。然而,学术界和企业界有着非常不同的思维方式和工作方式。结果,合作受到影响,两者之间的信任扩大了。因此,数据科学团队面临着巨大的压力,要在业务资源或支持很少的情况下展示快速价值。

人工智能厂商已经进行了创新,通过自主人工智能和 MLOps 等功能快速展示价值。自主人工智能使公民分析师能够通过简单和受指导的用户界面构建人工智能模型。MLOps 允许更快地开发、发布和监控数据科学模型。它们使端到端的人工智能生产能够无缝释放商业价值。

信任问题

尽管人工智能技术取得了进步,但商业和数据科学团队之间的信任并没有变得更加紧密。人工智能治理可能是一个答案。AI 治理似乎“诞生”于数据治理。数据治理旨在通过各种流程和框架实现适当的信息消费。可以说,人工智能治理拥有类似的总体目标——但鉴于其技术复杂性,它需要一个专门的焦点。它们有联系但又不同。

鉴于其重要性,为什么人工智能治理还没有起步?首先,有一种误解,认为 AI 治理计划必须在完成数据治理计划之后。其次,有一种误解,认为数据治理倡议完全涵盖了 AI 治理。第三,有一种误解认为数据治理不会产生显著的商业价值。由于这些误解,数据治理计划很难获得资金,并且仅限于解决法规或合规性需求。

人工智能治理可以与数据治理并行运行。大多数现代数据生态系统采用 ELT(提取-加载-转换)方法,将原始数据加载到数据湖中以供进一步监管和分析使用,从而将存储和计算分离开来。当业务用户访问精选数据时,数据科学家访问原始数据和精选数据。人工智能模型也有不同于可视化仪表板的消费应用。因此,核心数据治理倾向于与人工智能需求不同(但互补)的分析需求。

让我们快速回顾一下。人工智能带来了巨大的机遇和恐惧。要解决这一持续的问题,需要信任。人工智能治理似乎是一种合理的方法,但却被数据治理所掩盖。由于复杂性,组织回避资源投资——我们必须以闪电般的速度创造人工智能价值和产量。

AI 治理格局

那么,人工智能治理目前发生了什么?数据治理享有众所周知的框架,如 DAMA-DMBOK 功能框架,以及法规,如 GDPR。然而,人工智能治理框架仍处于初级阶段。因此,许多组织不想启动人工智能治理。他们在等待先行者。

先说一些定义。我相信可持续和负责任的人工智能是人工智能运营和人工智能治理的结合。AIOps(与 MLOps 同义)专注于模型生产生命周期和技术稳健性。人工智能治理关注的是人工智能应该如何在一个组织中运作。

可持续和负责任的人工智能=人工智能运营+人工智能治理

这种技术人工智能治理已经被纳入一些人工智能技术中。这包括与模型精度下降、数据质量、偏差和许多其他方面相关的领域。这些都是自动化的,并作为开箱即用的功能。然而,它们涉及技术和数学方面。人工智能治理包含更广泛的领域,如业务流程和人对人工智能的操作模型。

鉴于当前的挑战,我认为开发、操作和管理人工智能模型的集成技术是一个好方法。虽然行业正在转向基于组件的架构或最佳理念,但大多数公司的人工智能规模和成熟度都可以受益于一体化方法。雇佣优秀的数据科学家、建立 MLOps 和找到领先的人工智能治理实践者并不容易。此外,治理计划不是一次性的,也不是每月一次的审查,它们需要持续不断。当治理转化为每个人的日常工作流时,它是有效的,这是技术实现的。人工智能技术专业化的时代可能会到来,但让我们专注于现在。

该行业在内置 MLOps 和技术治理框架的人工智能工具方面正在走向成熟。首选的早期数据科学工具是 Python 和 r。我们在学术形成期间使用它们,并且它是免费的(或开源的)。虽然它们是我们的编码骨干,但单独使用它们来实现企业人工智能类似于使用电子表格来扩展营销活动。这些领先的人工智能工具利用领先的人工智能专家,通过用户友好的界面,只需几次点击就可以完成大多数事情。这足以在经典数据科学家之间建立信任,从而超越开源工具的算法选项、模型可解释性和性能治理。用发布管理和集成框架在技术团队中建立信任就足够了。

除了自动化工具,人对于将道德人工智能带入生活至关重要。如果博士是典型的数据科学家的潜力,人工智能治理领导者是另一个黑箱情况。让我们从数据治理计划中学习。根据我与数据领导者的交谈,数据治理从业者对于复杂的框架过于理论化。它们源自网络安全、政策或风险管理背景,导致数据科学家和商业领袖之间存在巨大的共鸣鸿沟。简而言之,它们给本已复杂的商业环境增加了更多的复杂性。因此,公司加强了他们的法律和合规部门,因为他们似乎在做“同样的”事情。

虽然我们没有成熟的人工智能治理从业者,但这些是可能推动我们前进的关键属性。首先,有商业头脑的量化背景。由于人工智能的黑盒性质,只有精通数学的人才能理解它的核心缺陷和优势。此外,商业敏锐性带来了实用性,并揭示了这一过程中的价值。其次,一个作者/“作家”的心态。斯蒂芬·金写道,一个作家会“大量阅读,大量写作”有许多关于人工智能道德和法规以及补充数据法规的阅读,需要探索并传达给更广泛的受众。这类似于福音书作者的超越写作,其首要目的是福音化。第三,成长心态。人工智能领域激增,个人必须与它一起成长。

个人属性的平衡组合对于推动人工智能治理至关重要。一个作者的心态将独自完成并产生许多思想领袖的文章。鉴于我们生活在一个超信息时代,它最有助于融合高层次的行业理念,但有形的进展或应用仍然存在缺陷。我见过的另一个例子是对 AI 偏见的过度讨论。批判性思维领导文章认为人工智能偏见是不好的,人工智能需要公平和公正。然而,大多数企业和营销活动都有目标受众,因此数据会因设计而有偏差。即使数据质量有偏差,如果有重大的商业价值,人工智能模型的构建仍然需要进展——当更好的数据到来时,模型可以进一步迭代。一个有自举知识(定量背景)的人理解这个问题,发表他们的分析(作者心态),并继续改进方法论(成长心态)。

第一步

根据我的经验,一步一步的人工智能治理方法似乎很有效。每个组织都是复杂的,每个组织都在一个旅程中。从当前状态和期望两方面了解他们的业务、技术和人工智能前景至关重要。良好的人工智能治理寻求这些元素之间的和谐关系——确保人工智能实现其预期的商业目的。人工智能的盲点通常与过度追求算法的卓越有关。外部视角对于沟通这些不同的组织元素是无价的。

除了战略协调,这种“审计”审查寻求价值创造。业界对人工智能的普遍反馈是,它没有达到交付商业价值的标准。尽管有潜力,但将人工智能与商业价值流程结合起来将会发现重新校准或新的机会。此外,我们需要了解 AI 的预期目的,这允许我们围绕它设计一个操作模型。治理并不阻止企业向其利基目标市场营销,但是治理支持为什么计划没有缺陷或“有偏见”

成熟的人工智能功能通过利用人类和人工智能的优势来实现共同的商业目的,从而增强人类和人工智能。鉴于不断发展的经济和监管条件,需要一个专门的人工智能治理团队。它的角色是建立和实现一个人工智能治理框架,涵盖操作模型、度量、技术和文化。虽然所有这些都很重要,但文化特别值得一提,它能在不确定的情况下教育和激励每个人,尤其是公司高管。此外,在监管监督日益加强的情况下,该团队还充当主动风险管理措施的角色。

尽管有迫切的需求,我们仍然人才短缺,而托管服务是外部可信赖合作伙伴的可行选择。一个组织可能从完全管理开始,随着它的成熟而发展到混合管理。它带来了一组具有人工智能专业知识和宝贵的同行评议视角的多元化人员。

一起前进

最后,人工智能治理很复杂,但你并不孤单。各个国家都发布了关于人工智能治理的公共政策指南和思想领袖论文。人工智能和数据技术也已经成熟,可以很好地处理关键的基本组件。下一步是根据您的具体环境综合、关联和实现这些部分。人工智能已经从我们的科幻小说进入我们的现代生活,我们必须合作并管理它们,走向可持续的未来。没有比现在更好的时机了。

如果你喜欢这篇文章,并想看更多的内容,请考虑加入媒体会员,使用下面的链接来支持我和其他作者:

https://albertsuryadi.medium.com/membership

另外,看看我下面的其他媒体文章:

关于作者:Albert Suryadi是在为前瞻性组织提供高级分析和数据科学能力方面久经考验的领导者。他被公认为分析团队(实践社区)的领导者,该团队授权并激励他人超越现状。

关于用 DAX 分布带因子的值

原文:https://towardsdatascience.com/on-distributing-values-with-a-factor-with-dax-d0d81a9dc30a

有几种分配价值的方法。一种方法是使用映射到表中行的因子来相应地分布它们。让我们深入了解如何在 DAX 的 Power BI 中实现这一点。

照片由罗伯特·林德Unsplash 上拍摄

这个问题

我的一个客户有以下问题:

我有关于商业伙伴项目的数据。我可以让多个合作伙伴参与一个或多个项目。

我如何创建一个 Power BI 报告来查看我的合作伙伴每期的支出金额?

数据模型

我创建了一个小型数据集来演示这种情况:

图 1 —样本数据集(作者提供的图片)

Power BI 中的数据模型类似于上图:

图 Power BI 中的数据模型(由作者提供)

项目和支出以及合作伙伴和项目之间的关系必须是双向的,因为我必须双向过滤数据。例如,当我需要按合作伙伴过滤项目时。

计划好的结果

让我们看一个项目,看看结果应该是什么:

我们在“音频”项目上有 125,600 英镑的支出。

三个合作伙伴积极参与该项目:

  • 康托索有限公司
  • 冒险作品
  • Proseware 公司。

当我们将该因子映射到每个合作伙伴的支出时,我们会得到以下结果:

图 3 —示例结果(作者提供的图)

主要目标是查看每个合作伙伴在一个项目上总共花了多少钱。

想法和解决方案

首先想到的是创造一种方法。当然,我做到了。

我会考虑我为另一个客户实现的另一个解决方案,我在下面的文章中记录了它:

但是我很快意识到这不是一个可行的选择。

我需要一个度量来计算结果,因为我没有任何分组或类别可以用来将我的结果分成多个度量。

我的第一个衡量方法是使用关系来乘以每个合作伙伴在每个项目上的花费。

第一批结果既复杂又相对缓慢。

因此,我改变了我的方法,开始创建一个表格,将每个项目的支出乘以每个合作伙伴的相应系数。

目标表应该如下所示:

图 4 —每个合作伙伴的项目表(由作者提供)

然后,我将能够向数据模型添加关系:

图 5 —完整的数据模型,包括每个合作伙伴的项目表(由作者提供)

有了这个表,我就可以添加一个简单的度量来对“按因素的支出”列求和。

为了创建目标表,我使用了 DAX Studio。

我创建的第一个表是获取每个项目的支出:

DEFINE
VAR F =
SELECTCOLUMNS(
    ‘Project and Spending’
    ,”ProjectID”, ‘Project and Spending’[ProjectID] & “”
    ,”Spending”, ‘Project and Spending’[Spending]
    )

EVALUATE
 F

查询结果如下所示:

图 6 —查询项目和支出结果(作者提供的数据)

第二个查询是获取每个合作伙伴和项目的工厂:

DEFINE
VAR C =
SELECTCOLUMNS(
    ‘Partners and Projects’
    ,”PartnerID”, ‘Partners and Projects’[PartnerID]
    ,”ProjectID”, ‘Partners and Projects’[ProjectID] & “”
    ,”Factor”, ‘Partners and Projects’[Factor]
    )

EVALUATE
 C

该查询的结果如下:

图 7 —查询每个合作伙伴和项目的因子和结果(作者提供的图)

最后一步是合并这两个表,并将每个项目的支出乘以每个合作伙伴的系数。

为了实现这一点,我使用了naturaleftouterjoin()函数。

使用这个函数,第一个表的每一行都通过同名的列与第二个表的每一行匹配。

然后,我使用 ADDCOLUMNS() 函数来计算支出乘以每个合作伙伴/项目组合的系数。

以下是完整的查询:

DEFINE
 VAR C = SELECTCOLUMNS(
 ‘Partners and Projects’
 ,”PartnerID”, ‘Partners and Projects’[PartnerID]
 ,”ProjectID”, ‘Partners and Projects’[ProjectID] & “”
 ,”Factor”, ‘Partners and Projects’[Factor]
 )

 VAR F = SELECTCOLUMNS(
 ‘Project and Spending’
 ,”ProjectID”, ‘Project and Spending’[ProjectID] & “”
 ,”Spending”, ‘Project and Spending’[Spending]
 )

 EVALUATE
 ADDCOLUMNS(
 NATURALLEFTOUTERJOIN(F, C)
 ,”Spending by Factor”, [Spending] * [Factor]
 )

(截断的)结果如下:

图 8 —带有结果的完整查询(作者提供的图)

在对结果进行一些检查之后,我可以将这个查询转换成 Power BI 中目标表的 DAX 表达式:

Project per Partner =
VAR F =
SELECTCOLUMNS(
    ‘Project and Spending’
    ,”ProjectID”, ‘Project and Spending’[ProjectID] & “”
    ,”Spending”, ‘Project and Spending’[Spending]
    )VAR C =
SELECTCOLUMNS(
    ‘Partners and Projects’
    ,”PartnerID”, ‘Partners and Projects’[PartnerID]
    ,”ProjectID”, ‘Partners and Projects’[ProjectID] & “”
    ,”Factor”, ‘Partners and Projects’[Factor]
    )RETURN
    ADDCOLUMNS(
        NATURALLEFTOUTERJOIN(F, C)
        ,”Spending by Factor”, [Spending] * [Factor]
        )

现在,我可以创建关系,一切都完成了。

最后一项措施很简单:

Sum of Spending by Partner = SUM(‘Project per Partner’[Spending by Factor])

可以将此测量添加到视觉效果中以显示结果:

图 9 —功率 BI 的结果(由作者提供)

正如您所看到的,合作伙伴正确地分配了支出,总计数字如图 3 所示。

添加时间

你可能已经注意到了,我的客户的一个问题是:每期支出。

我的例子直到现在都没有时间轴。

我必须修改我的数据,以获得一个没有 m:n 关系的干净的数据模型。

为此,我创建了一个只包含项目的新表,并使用带有日期的支出列扩展了该表。

当然,我在模型中添加了一个日期表。

产生的数据模型如下所示:

图 10 —带有日期维度的修改后的数据模型(由作者提供)

创建每个合作伙伴的项目表的 DAX 表达式略有不同。

Project per Partner =VAR F =
SELECTCOLUMNS(
    **‘Spending’** ,”Date”, **‘Spending’**[Date]
    ,”ProjectID”, **‘Spending’**[ProjectID] & “”
    ,”Spending”, **‘Spending’**[Spending]
    )VAR C =
SELECTCOLUMNS(
    ‘Partners and Projects’
    ,”PartnerID”, ‘Partners and Projects’[PartnerID]
    ,”ProjectID”, ‘Partners and Projects’[ProjectID] & “”
    ,”Factor”, ‘Partners and Projects’[Factor]
    )RETURN
    ADDCOLUMNS(
             NATURALLEFTOUTERJOIN(F, C)
             ,”Spending by Factor”, [Spending] * [Factor]
             )

我只需切换到支出表并将日期列添加到表达式中。

可以按月过滤数据以获得所需的结果:

图 11 —带有月切片器的报告(由作者提供)

“合作伙伴支出总额”这一指标不需要任何改变。

此外,数据模型现在更加清晰,因为合作伙伴和项目位于单独的表中,没有其他列。

这种清晰的数据模型对于构建更简单的解决方案至关重要。

结论

学习如何在 DAX 中使用表格是重要的一课。它为解决复杂的问题或挑战提供了许多可能性。

使用 DAX Studio 是解决这些挑战的一个重要因素。

我在这里学到的主要经验是不要试图使用以前的解决方案来解决挑战。

体验好。但只是为了学习我能做什么,什么是好什么是坏。

要知道我并没有写“我能做什么,不能做什么”。每一个挑战都提出了独特的问题,需要个性化和创新的解决方案。

有时您可以重用以前的解决方案。而这些都是“比较简单的挑战”。

所有其他的挑战都很有趣。

萨曼莎·索菲亚在 Unsplash 上的照片

参考

该数据来自 Contoso 数据集。

ContosoRetailDW 样本数据集可以从微软这里免费下载。

Contoso 数据可以在 MIT 许可下自由使用,如这里的所述

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

关于衰减锁定有效性

原文:https://towardsdatascience.com/on-fading-lockdown-effectiveness-381b20886f9

SARS-COV2 疫情第一年公众遵守旅行限制的数据分析。

照片由 null xtract:https://www . pexels . com/photo/person-looking-out-of-the-window-3047470/

从 2020 年 3 月到 2021 年 2 月底,疫情爆发 SARS-COV2 的第一年,世界大部分地区不时出现封锁和旅行限制,各国试图遏制 SARS-COV2 的传播,并管理其超负荷医疗系统的疾病负担。在这项分析中,我们研究了在疫情的第一个整年中,作为限制非必要旅行和遏制病毒传播的一种方式,这些限制是否仍然有效。我们通过分析 125 个国家的公众在疫情第一年的早期、中期和晚期以及疫苗广泛可用之前遵守限制的情况来做到这一点。

我们发现,与早期阶段的初始封锁相比,由于疫情中期和后期阶段的限制,遵守率有所下降,这主要是因为流动性水平的变化,而不是限制水平的变化。这表明,随着疫情的推进,限制在减少非必要旅行方面变得不那么有效,突出了限制作为长期缓解战略的局限性。

这项工作的完整报告已经通过同行评审,并由《公共科学图书馆·综合》(Plos One)发表。以下是对该方法及其主要发现的总结。

他的研究结合了两个公开的数据来源:

  1. A 限制数据集基于政府对疫情的反应分类(遏制、经济、健康和其他措施);这个数据集现在也可以从我们的世界数据中获得,在知识共享许可下。每项指标都在 0 到 100 之间进行了归一化,在这项工作中,我们使用了一个均值旅行限制指数,它来自一个子集的指标:学校和工作场所关闭,对集会和公共活动的限制,以及对公共交通和个人活动的限制。
  2. 一个 移动数据集,由公开可用的谷歌移动数据组成,基于相对于疫情前水平的移动每日变化;这个数据集现在也可以在知识共享许可下从我们的世界数据中获得。我们根据零售和娱乐、公共交通以及与工作场所相关的流动性类别,计算了非必要旅行的平均流动性下降。**

使用 7 天滚动平均值对每日限制和流动性数据进行平滑处理。生成的数据集包括从 2020 年 3 月 1 日到 2021 年 2 月 28 日的 125 个国家的限制和流动性数据。

为了衡量限制与出行之间的遵守程度,我们估计了每日限制与出行数据之间的关系强度,以衡量出行限制水平的变化对非必要出行的影响程度。为此,我们使用了两个坚持性评估:

  1. 决定系数(r 平方或 r2) — 基于皮尔逊相关系数( r ),计算给定时间段内每个国家的每日限制和流动性下降之间的关系;考虑到预期或延迟的迁移率效应,使用互相关技术计算 r2 。换句话说,我们使用最大值 r 2,这是通过在限制改变之前/之后移动每天的移动数据找到的。**
  2. 动态时间扭曲相似度 ( DTW ) —作为替代的遵守度估计,我们还使用动态时间扭曲计算了每日限制级别和相应的流动性下降之间的相似度。使用 DTW 是因为它提供了一种原则性的方法,通过非线性扭曲数据来确定两个时间序列之间的最佳匹配,作为另一种解决预期或延迟迁移效应的方法。**

到评估人们在不同的限制期间是否以不同的方式调整了他们的流动性。我们使用限制数据来识别连续的增加减少限制的期间。

  • 增加期从限制开始增加的第一天开始,持续到限制开始减少,从而标志着新的减少期的开始。
  • 减少期从限制开始减少的第一天开始,持续到限制再次增加的第一天。

例如,图 1 显示了爱尔兰从 2020 年 3 月到 2021 年 2 月,在增加(红色)和减少(绿色)限制的几个时期的限制和流动性数据。我们可以看到,与全球平均水平相比,爱尔兰的限制更多,流动性下降更大。

****图一。爱尔兰 2020 年 3 月至 2021 年 2 月(含)的旅行相关限制(上图)和流动性数据(下图)。平均全局限制和移动性下降以单独的线形图表示。图片作者。

图 2 显示了欧洲 34 个国家的相应数据,以说明在一个经济/地理区域内采用的不同方法。虽然各国经历了增加和减少限制的周期,但爱尔兰和英国实施的限制水平高于大多数国家(流动性下降幅度也更大)。其他人认为,在 2020 年夏季,限制的放松更加显著和持续,随后在秋季和冬季,限制又有所增加。一些国家(如克罗地亚、保加利亚、爱沙尼亚)选择在疫情的后半段实行比其他国家更大程度的限制。

图二。欧洲 34 个国家的旅行相关限制和流动性数据,显示增加(红色)和减少(绿色)限制的时期。**对于每个国家,上图显示限制水平,下图显示相应的流动性下降。每个国家名称还包括 2020 年 3 月至 2021 年 2 月底期间经历的限制( μR )和流动性下降(微米)的平均水平。图片作者。

由于我们对遵守情况如何随时间变化感兴趣,我们通过将疫情分为三个相等的四个月阶段来分析限制时间的影响:早期(2020 年 3 月-6 月)、中期(2020 年 7 月-10 月)和晚期(2020 年 11 月-2021 年 2 月)。不同国家在不同时期受到疫情的影响,这将反映在这些阶段国家一级限制的增加和减少。通过将疫情的第一个全年分为这三个阶段,我们可以合理地比较每个阶段的具体国家的遵守情况。

图 3 显示了在疫情的早期、中期和晚期,针对任何增加和减少限制期的两种遵守措施( r2DTW )。无论采用何种衡量标准,我们都可以看到,与后期相比,疫情早期阶段的遵守率更高。对于增加的限制期(红色条)和减少的限制期(绿色条)都是如此。例如,最初(早期)的封锁与被测试国家的中值R20.87 相关联——表明限制水平和流动性下降之间的关系非常强——但后来的封锁导致限制和流动性之间的关系较弱(中期和后期的 r2 值分别为 0.39 和 0.43)。

****图三。按疫情阶段比较上升期和下降期的遵守指标。图片作者。

这些图表还包含统计显著性数据,这些数据在《公共科学图书馆综合》的论文中有充分的解释;可以说,早期和晚期之间的依从性差异在统计学上是显著的。《公共科学图书馆·综合》的论文还显示,随着疫情的展开,即使我们考虑到在增加和减少期间施加和放松的限制水平的变化,这些遵守方面的差异也是显著的。

这项工作的主要发现是,在疫情的中后期,尤其是限制越来越多的时期,限制与流动性之间的关系强度减弱了。在疫情的早期阶段,即使考虑到所施加的限制程度,流动水平受到的抑制也比后期阶段要大,在疫情,人们对限制的反应也不那么可预测。

尽管这表明疫情的行为发生了变化,但它没有解释这种变化的根本原因,这种变化始于 2020 年夏天——在第一波封锁之后,但远在任何疫苗宣布之前,更不用说上市了——并持续到 2021 年 2 月底。

这些结果提供了一个重要的基础,告知新的问题,类似的限制和封锁是否应该或可以在这次疫情或未来的大流行中依赖。至少它们表明,最初被证明相当成功的限制措施很快就变得不那么有效了。此外,考虑到封锁和限制的高社会经济负担,这引发了对其未来实际效用的真正怀疑。

概率与可能性

原文:https://towardsdatascience.com/on-probability-versus-likelihood-83386b81ad83

对两个经常互换使用但在数学上截然不同的术语的讨论

奥地利国家图书馆在 Unsplash 上拍摄的照片

从机器学习和数据科学的角度来看,概率和可能性用于量化不确定性,或者说某个观察值属于某个类别的可能性有多大。他们在查看混淆矩阵时突然出现;事实上,像朴素贝叶斯分类这样的算法基本上是概率模型。现实是,数据科学家无法逃避这些概念。

然而,在日常语言中,我们倾向于互换使用概率和可能性这两个术语。事实上,听到诸如“今天下雨的可能性有多大?”这样的话并不少见。或者“这件事或那件事发生的可能性有多大?”我实话实说。最初对我来说,这并不是一个明显的区别,但是概率和可能性是不同的 (尽管是相关的)概念。作为数据科学家,这种可互换性经常会渗透到我们的工作中,因此明确这两个术语之间的区别非常重要。因此,在这篇文章中,我想我应该尝试一下说明这两种观点之间的区别。

可能性

简而言之,概率和可能性之间的根本区别在于什么被允许变化——让我来解释一下。

你需要知道的是可能性——尽管具体来说,我实际上是在谈论可能性函数——实际上是从一个统计模型中得出的,并被认为是从该模型中生成数据的参数的函数。迷茫?不要担心——让我们更具体一点,考虑掷十次公平硬币。

我们知道这是一个由比率参数 p 控制的二项式过程,比率参数定义了在总共 N 次投掷中,掷出 K 次正面(或反面——这无关紧要)的预期概率。这里,观察头数 K 是一个随机变量,它响应于不同的 p 值而变化。对于任何固定的参数值(例如, p = 0.7),观察到 K 头部的概率由概率质量函数(PMF)给出:

二项式 PMF。小写的希腊字母 theta 简单的指模型参数(图片由作者提供)。

在哪里

二项式系数(图片作者提供)。

现在,因为我说硬币是公平的,而且它被抛了十次,我们可以让 p = 0.5, N = 10。如果我们将这些值代入上面的等式——并让 K 变化——我们会得到类似于图 1 左侧面板的结果。基于此,我们可以看到, K = 5 是最有可能的结果,这应该是有道理的:如果硬币是公平的,我们扔 10 次,我们应该预期——从长远来看——我们通常会得到 5 个正面和 5 个反面。但是你也应该注意到,获得 4 或 6 个头也不是不常见的。

因此,PMF 告诉我们的是,给定一组固定的模型参数,随机过程的特定实现有多可能。在这种情况下,我们假设模型参数是固定的,并且数据可以自由地变化。这里的要点是,如果模型参数是已知的,那么我们要问的是关于可能观察到的数据类型的问题。

图 1: A) 二项式模型的概率质量函数; B) 二项似然函数。点表示不同参数值的可能性(图片由作者提供)。

可能性

好吧,那么如果我告诉你我已经抛了十次硬币,得到了 7 个正面呢?我的问题是我扔的硬币是否公平。

值得注意的是,在这种情况下, K 不再是随机的——我们有一个观察到的二项式过程的实现,这意味着它现在是一个固定值。给定该信息,二项式模型的似然函数可以写成:

二项式模型的似然函数。

在这里,我使用“帽子”符号来表明 K 是从十次投掷中观察到的头数。现在,这看起来就像我在说,可能性函数和 PMF 是一样的——嗯,我有点像是在说。但是,不同之处在于,现在数据是固定的,而模型参数 p 可以自由变化。

可能性函数给我们的是一个测量值,表示已知 K 等于某个观察值时 p 的任何特定值出现的可能性。就像上面一样,如果我们将 K = 7 代入上面的等式——让 p 取所有可能的值——我们会得到类似于上图右侧面板的结果。注意,这里的似然函数是不对称的;相反,它的峰值超过了 p = 0.7。具体来说,该分布的模式(即峰值本身)与所谓的最大似然估计 (MLE)相一致。那是什么?嗯,这是最有可能给定观测数据的 p 的值——这个名字有点泄露了它,真的。我将在另一篇文章中更多地讨论 MLE,但是现在,你需要知道的是 MLE 的似然函数值大约是 0.27。

好了,在继续之前,需要注意的是:我知道可能性函数看起来像分布函数,但它是而不是合适的概率密度函数(即,它通常不会积分为 1)。更重要的是,可能性函数是而不是p等于特定值的概率(为此你需要计算后验分布。我会在另一篇文章中谈到这一点)。

好吧,继续。

统计论据

还记得我说过似然函数被解释为一种度量吗?确切地说,它可以用来量化统计证据。我还说过,关于最大似然估计,你需要知道的就是它的似然值大约是 0.27。但是这告诉我们什么呢?说可能性大约是 0.27 是什么意思?它当然没有告诉我们我使用的硬币是否公平——那么从这里开始呢?

我需要介绍一些东西:

  • 似然原理指出,对于给定的统计模型,所有与模型参数相关的证据都存在于采样(观察)数据本身中。
  • 似然定律指出,证据支持一个参数值超过另一个参数值的程度取决于它们各自似然性的比率。

把这些想法放在一起意味着孤立地看待一个单一的可能性告诉我们的很少。是的,它提供了一定程度的证据,但相对于什么呢?上面的定义告诉我们,与其他可能性相比,可能性更有用。这就是为什么我们通常更喜欢相对度量,如似然比

记住这一点,回想一下我问你的问题是,根据你掌握的数据,我扔的硬币是否公平。为了正确解决这个问题,我们还需要观察数据的可能性,如果 p = 0.5,大约是 0.12。注意,这两点都在图 1 中的似然函数上标出。给定这两条信息,我们现在可以计算似然比:

在给定观察数据的情况下,似然比比较不同参数值的相对证据(图片由作者提供)。

这给了我们什么?大约 0.44。你需要一些背景知识。

在这个例子中,似然比量化了数据支持断言 p = 0.5(公平硬币)的程度。如果这个比例是 1,无论哪种方式都没有证据。如果比率大于 1,那么证据有利于分子(这里, p = 0.5)。但是,如果比值小于 1,那么证据支持分母(这里, p = 0.7)。看来我们有证据反对公平硬币假说。事实上,如果我们取倒数(即 1 / .44 ),我们会看到 p = 0.7 比 p = 0.5 的可能性大约高 2.3 倍(请记住,这是仅与一个选项进行的比较,除了 p = 0.7,还有其他可能性)。

在现实中,这是一个微不足道的结果,而似然原理和似然定律意味着使似然函数最大化的参数值就是数据最支持的值。接下来的问题是,这是否足以令人信服地证明我抛的硬币是不公平的。那是改天的话题。

包扎

我希望这篇文章明确了概率和可能性的区别。概括一下:概率通常是当我们有一个带有固定参数集的模型,并且我们对可能生成的数据类型感兴趣时,我们会考虑的东西。相反,可能性在我们已经观察到数据并且我们想要检查某些模型参数的可能性时发挥作用。特别是,我们可以使用似然比来量化支持一个值胜过另一个值的证据。

感谢阅读,欢迎在下面给我留言。

如果你喜欢这篇文章,并且想保持更新,那么请考虑在 Medium 上关注我。这将确保你不会错过新的内容。如果你更喜欢的话,你也可以在 LinkedIn 和 Twitter 上关注我😉

在边缘——在移动设备上部署深度学习应用

原文:https://towardsdatascience.com/on-the-edge-deploying-deep-applications-on-constrained-devices-f2dac997dd4d

在受限设备上实现深度神经网络的效率-精度折衷的技术

图片由作者提供。

如此多的人工智能进步登上头条:“人工智能正在围棋上击败人类!”;“深度天气预报”;“会说话的蒙娜丽莎画”…然而我并不感到太兴奋…尽管前景诱人,这些结果是用模型实现的,这些模型是概念的可靠证明,但离现实世界的应用仍然太远。原因很简单——它们的大小。

数据集越大,模型越大,结果越好。但是,从它们消耗的物理资源(如内存和电源)和推理时间来看,这些都是不可持续的,与许多应用程序所需的实时性能相差甚远。

现实生活中的问题需要可以在受限设备上运行的较小模型。随着更广泛的安全和隐私问题,越来越多的人赞成在设备上安装模型,消除向服务器的任何数据传输。

下面我将介绍一些技术,这些技术使得模型对于受限设备(比如手机)变得可行。为了实现这一点,我们降低了模型的空间复杂度和推理时间,并组织数据流以节省计算。在文章的最后,我还介绍了一些实际的考虑因素,比如移动处理器的类型和有助于为移动设备准备模型的框架。

虽然矩阵运算的一般计算速度有很大的提高,但本文将重点关注可以直接应用于深度学习应用的技术。

降低模型空间复杂度

深度学习模型需要内存和计算资源,这些资源在移动设备上通常很稀缺。解决该问题的一个直接方法是降低深度学习模型的空间复杂度(参数数量),以占用更少的空间和计算量,同时保持相同的精度。

空间复杂度降低可以分为五种方法:

  • 减少模型参数的数量(例如修剪和共享);
  • 通过量化减少模型规模;
  • 知识升华;
  • 较小模型的直接设计;
  • 输入数据转换。

修剪

剪枝的基本思想是选择和删除一些对模型精度影响不大的琐碎参数,然后重新训练模型,恢复模型性能。我们可以删减单个权重、层或层块:

  • 非结构 修剪移除任何地方出现的小显著神经元。执行积极的修剪相对容易,在对模型的泛化性能影响最小的情况下移除大多数神经网络参数。然而,被修剪的神经元的数量并没有直接转化为内存和计算的节省。这种方法导致稀疏矩阵运算,这是众所周知的难以加速。
  • 结构 剪枝利用模型在不同尺度下的结构稀疏性,包括滤波器稀疏性、核稀疏性、特征映射稀疏性。移除一组参数(例如,整个卷积滤波器),从而允许密集矩阵运算。然而,在不损失准确性的情况下实现更高级别的结构修剪是具有挑战性的。

修剪是迭代的。在每次迭代中,该方法修剪相对不重要的滤波器,并重新训练修剪后的模型,以补偿精确度的损失。当修剪后的模型未能达到所需的最小精度时,迭代结束。

欲了解更多详情,请查看本文。

参数共享

我们可以将它们组合起来,而不是丢弃模型的某些部分。当边权重基本相似时,我们可以在几条边上共享它们。

例如,对于两个各有 N 个节点的全连接层,我们需要存储 N 个权重。然而,如果权重基本上相似,我们可以将它们聚类在一起,并为同一聚类的边分配相同的权重,然后我们将只需要存储聚类质心。

网络量化

对称/非对称/均匀/非均匀量化映射的例子。图片由 A .戈拉米提供。

神经网络中使用的默认类型是 32 位浮点数。如此高的分辨率允许在训练阶段进行精确的梯度传播。但是,在推理过程中,这往往是不必要的。

网络量化的关键思想是减少每个权重参数的位数。例如从 32 位浮点到 16 位浮点、16 位定点、8 位定点等。

量化方面的许多研究都集中在从一个较大范围的数字映射到一个小得多的数字的舍入技术上——均匀/非均匀、对称/非对称量化。

谈到培训,有两种主要方法来实现量化:

  • 训练后量化可能是应用量化最直接的方式——模型权重被映射到较低的精度,之后无需额外的微调。然而,这种方法必然会降低模型的精度。
  • 量化感知训练:需要重新训练应用了量化的模型,以匹配原始模型的准确性。量化网络通常在与原始模型相同的数据集上重新训练。为了便于梯度传播,梯度没有被量化。

应用现成的量化并不简单,因为不同的网络部分可能需要不同的精度。因此,通常在中间插入量化/去量化块,以允许转换。

更多细节请查看最近的调查,我也喜欢这篇关于网络量化的文章

知识的升华。

图片由 J. Gou 提供。

在深度模型的学习权重中的显著冗余的假设下操作,我们可以通过训练较小的模型(学生)来模拟教师输出的分布,从而提取大模型(教师)的知识。

模型提取的关键思想是依赖于由更大的模型提取的知识,例如各种类的相对概率大小,而不仅仅是训练数据集中给出的“硬”标签。

例如,考虑一个对猫、狗和汽车进行分类的网络。凭直觉,当一张新的猫的图像出现时,我们会期望模型给猫分配最高分,然后给狗分配较低的概率,给汽车分配最低的概率,因为猫和狗比猫和汽车更容易被混淆。这些相对概率值携带了关于模型训练所依据的数据的大量信息,并且不一定存在于硬标签中。

通过网络量化和修剪,有可能在压缩率达到 4 倍的情况下保持精确度。在不降低准确性的情况下,通过知识提取获得相似的压缩率是一项挑战;但是,所有方法都可以结合使用。

欲了解更多详情,请查看本文。

小型模型的直接设计。

图片由 A .霍华德提供。

在深度学习算法的早期繁荣时期,许多工作都集中在建立更大的模型,以达到最先进的准确性。这种趋势后来被一系列研究效率-精度权衡的论文取代,直接设计更小的模型。

地区重点论文有: MobileNetV1MobileNetV2MnasNetMobileNetV3

有一些值得注意的架构变化的例子,它们是当前所有深度学习库的一部分。这些通常基于低阶因式分解,例如深度可分卷积,有一篇很棒的文章解释了其来龙去脉。

由于用于设计小而精确的模型的搜索空间是巨大的,所以最近的趋势不太集中在手工制作的模型的设计上,而是集中在采用强化学习的神经架构搜索上。例如,该策略应用于 MobileNetV3MNASNet 中。

关于神经架构搜索检查的更多细节。

数据转换。

我们可以降低输入数据的维度,而不是通过查看模型的结构来加速计算。一个例子是将图像分解成两个低分辨率子图像,其中一个携带高频信息,另一个包含低频信息。结合起来,它们将携带与原始图像相同的信息,但维度更低——这意味着处理输入的模型更小。

更多详情请查看本文。

中间结果的重用

为来自相同输入的整个机器学习管道的各个部分或相似输入的特征使用某些骨干模型以避免冗余计算并不罕见。

多任务间的数据重用

对于具有相同输入的不同但相关的任务,并行运行几个模型并不罕见。这个想法是在多个模型中重用浅层的特性,同时在特定的任务上训练更深层的特性。

图像帧间的数据重用

虽然输入数据可能不完全相同,但在与后续输入(如连续视觉模型)相关时,它可能足够相似,可以部分重复使用。

欲了解更多详情,请查看本文。

五金器具

在对模型进行提炼、修剪和压缩之后,我们终于准备好在移动设备上部署了!然而,有一个警告-最有可能的是,开箱即用的解决方案要么非常慢,要么无法工作…这通常是因为一些操作要么没有优化,要么在移动处理器上不受支持。

值得记住的是,当前的移动设备有几个处理器。深度学习应用程序可能会在 GPU 或 NPU(专门为深度学习应用程序优化的神经处理单元)上运行。在部署深度学习应用程序时,每种方法都有自己的优缺点。

尽管有专门的目的,但在当前的 NPU 中,效率的提高可能会被进出处理器的数据传输瓶颈所抵消,这对实时应用程序来说可能是个问题。

移动设备上的深度学习框架

传统的深度学习库如 PyTorchTensorflow 并不是特别适合移动应用。这些都很重,并且依赖于第三方依赖,这使得它们很笨重。这两个框架都面向在强大的 GPU 上进行有效的训练,而部署在移动设备上的模型将受益于高度移动优化的推理工具包,而这两个框架都缺乏。

幸运的是,有专门为移动深度学习设计的框架: TensorFlow LitePytorchLite

为移动开发深度学习应用的挑战之一是每个移动生产商的可变标准;有些人会在 Tensorflow 中运行他们的模型,有些人会在 Pytorch 中运行,有些人会有自己的框架。为了便于转换,我们可以使用一个开放神经网络交换框架,帮助从一个库转换到另一个库。

最后,您可以使用 OpenVINO ,这有助于通过关注部署硬件来优化云和边缘设备上的深度学习应用程序。

关于每款常用手机的开发(包括允许的操作)的更多细节,请查看它们的 API 文档:华为苹果三星。这些还包含一些特殊的技巧,可以使模型在特定的设备上更加有效。

摘要

深度模型需要计算和内存资源,而这些资源在受限设备上通常是不可用的。为了解决这个限制,几个研究分支集中在减少模型大小和加速其计算。

在部署到移动设备之前,典型模型将被设计为消耗尽可能少的资源,或者将通过蒸馏进行压缩;在最终部署到设备上之前,它将经历量化。如需进一步阅读,请查看关于在移动设备上部署深度学习的调查

如果你喜欢这篇文章,请与朋友分享!要阅读更多关于机器学习和图像处理的主题,请点击订阅!

我错过了什么吗?不要犹豫,直接给我留言、评论或发消息吧!

生成性对抗网络的评价

原文:https://towardsdatascience.com/on-the-evaluation-of-generative-adversarial-networks-b056ddcdfd3a

医疗保健中的人工智能第三部分

作者 https://sylwia-majchrowska.medium.com/桑德拉·卡拉斯科 西尔维亚·马奇罗斯卡

图一。来自 ISIC 2016 数据集的恶性和良性黑色素瘤示例[1]。观察到的痣可以有不同的大小和颜色。图片来自 ISIC 挑战数据集

在深度学习模型的开发中使用医疗保健数据与个人数据和监管问题相关的挑战相关联。患者数据不能自由共享,因此在创建人工智能解决方案方面的用处有限。在我们之前的帖子中,我们讨论了合成数据在医疗保健中的潜力。我们描述了对开源国际皮肤成像协作(ISIC)档案[1]的研究,这是皮肤病变的皮肤镜图像的最大(且高度不平衡)数据集之一。我们在有条件的设置中选择了 StyleGAN2-ADA 架构,以从黑色素瘤和非黑色素瘤中生成图像。今天,我们将向您介绍 GAN 生成的合成数据的评估主题。

甘的评价

没有目标损失函数用于训练 GAN 的发生器,因此,无法仅从损失客观评估训练的进度和模型的相对或绝对质量。相反,已经开发了一套定性和定量技术来基于生成的合成图像的质量和多样性来评估 GAN 模型的性能。如需全面调查,请参见文章 甘评价措施的利弊【2】。

定性

由于在训练生成模型时没有使用客观损失函数,因此必须使用生成的合成图像的质量来评估这些模型。评估您的 GAN 的最基本和最有用的方法之一是通过手工检查和判断从不同的迭代步骤中生成的例子。

然而,这有许多限制:

  • 它是主观的,包含了评论者的偏见。
  • 它需要领域知识来区分什么是现实的,什么不是。在我们的具体案例中,依靠皮肤科医生的帮助是非常重要的,他们可以评估假样本。
  • 它在可检查的图像数量方面受到限制。

对于如何准确地定性检查生成的图像,还没有明确的最佳实践,因为每个案例之间可能会有很大差异。因此,我们不会在本帖中详细探讨这个话题。

定量

在本节中,我们将检查一些通常用于在培训期间评估创成式模型的指标。这些可以与定性评估相结合,以提供对 GAN 模型的稳健评估。

在衡量 GAN 的性能时,我们需要评估两个主要属性:

  • 保真度:生成样本的质量。测量图像的逼真程度。你可以把它想象成每一个假样本与其最接近的真样本有多么不同。
  • 多样性:生成样本的多样性。衡量生成的图像覆盖真实分布的整体多样性或多样性的程度。

图二。基于 GAN 评估方法的典型样本图像[3]。

通过捕捉保真度和多样性,您可以很好地了解您的生成器生成假图像的效果。然而,为了评估我们的生成模型,我们需要评估另一个维度。

  • 真实性/可推广性:它衡量模型发明新样本的速度,并试图发现与真实数据的过度拟合。Alaa 等人[5]将其定义为生成的样本中比其他训练数据点更接近训练数据集的部分。

最后,合成数据在用于同样的预测目的时,应该和真实数据一样对后续任务有用。

  • 预测性能:我们对合成数据进行训练,对真实数据进行测试。这评估了合成数据覆盖真实数据分布的程度。我们还可以用合成数据训练和测试模型。这将使我们能够评估合成数据是否保持了预测模型在真实数据上的排名。

我们现在将更详细地讨论引入的定量方法。

通常计算的指标

评估 GAN 性能最常用的指标是弗雷歇初始距离(FID)和内核初始距离(KID) 他们评估保真度(图像质量)和多样性(图像种类)

弗雷歇初始距离(FID)

为了比较真假图像,可以使用分类器作为特征提取器。最常用的特征提取器是 Inception-v3 分类器,它是在 ImageNet 上预先训练的。截断输出层,得到真假图像的嵌入量,提取特征距离。这些嵌入是两个多元正态分布,可以使用多元正态弗雷歇距离或 Wasserstein-2 距离进行比较。然而,FID 评分有一些缺点。首先,它使用了一个预先训练好的初始模型,这个模型可能不能捕获所有的特征。其次,需要大样本量。最后,它运行缓慢,并且使用有限的统计数据(只有均值和协方差)。

内核初始距离(KID)

KID 已被提议作为 FID 的替代。FID 没有无偏估计量,这导致较小数据集的期望值较高。KID 适用于较小的数据集,因为它的期望值不依赖于样本数。它的计算量更小,数值稳定性更好,实现起来也更简单。

盗梦空间评分

类似于 FID 和 KID,该分数测量图像的多样性和保真度。它越高,生成的图像就越真实和多样

通过首先使用预训练的 inception v3 模型来预测每个生成的图像的类概率,来计算 Inception 分数。这些是条件概率,例如以生成的图像为条件的类别标签。被强有力地分类为一类而不是所有其他类的图像指示高质量。因此,如果图像包含现实事物,条件概率遵循窄分布(低熵)。

其次,我们使用边际概率。这是所有生成图像的标签概率分布。因此,它告诉我们在我们生成的图像中有多少变化。因此,我们希望边际概率分布的积分具有高熵,即我们的生成器尽可能多地合成不同的类别。

这些元素通过计算条件概率分布和边际概率分布之间的 Kullback-Leibler 散度,或 KL 散度(相对熵)来组合。如果它很高,我们的分布是不相似的,即每个图像都有一个清晰的标签,并且所有图像都是不同的。

这个分数有几个限制。首先,它只查看假图像,而不与真图像进行比较。与 FID 一样,它受到初始分类器可以检测的内容的限制,这与训练数据直接相关。一般来说,它试图捕捉保真度和多样性,但它的表现不如 FID。如果你想知道更多,可以去看看大卫·麦克关于《盗梦空间》分数的简单解释的精彩文章。

精确和召回

对于分类和检测任务,另一种类型的度量标准是精度召回。我们可以将它们定义为:

  • 精度保真度相关。它根据生成器生成的非真实图像的数量来查看真实数据和虚假数据之间的重叠(非重叠红色)。
  • 召回涉及多样性。它在生成器无法建模的所有实数上查看实数和伪像之间的重叠(非重叠蓝色)。

图 3。真实图像分布 Pr(蓝色)和生成图像分布 Pg(红色)的精确召回图示。图片由[4]提供。

精确召回的问题是,它可以有完美的精确分数,但生成性分布很差,因为它只在实际分布中生成离群值。此外,我们不能检测模式崩溃或模式发明。

感知路径长度

评估生成网络的另一个重要度量是感知路径长度(PPL)。这是一种特征解开的方法,一种规则化的方法,鼓励在从潜在代码到图像的映射中进行良好的调节。在我们进一步讨论 PPL 之前,让我们讨论一下潜在空间。

在 StyleGAN 中,我们发现了两个潜在空间,z 空间和 w 空间。首先,Z 空间是来自高斯分布的 Z 向量所在的位置。在标准 GAN 中,z 向量被直接馈入发生器以生成假图像。然而,在 styleGAN 中,这个向量通过映射网络来产生 w 向量或样式向量。W 空间不遵循任何特定的分布,但是它在训练期间被学习,以便它可以更好地模拟真实数据的底层分布。w 向量然后被馈送到各层的合成网络中,用于最终生成。Z 和 W 空间都是 512 维的。潜在空间 W 是控制图像属性的关键,因为它是不纠缠的,这意味着 512 个维度中的每一个都编码了关于图像的独特信息。

PPL 的目的是建立从潜在空间到图像空间的映射是条件良好的。这是通过鼓励潜在 W 空间中的固定大小的步长导致图像中非零的、固定幅度的变化来实现的。我们可以通过步入图像空间中的随机方向并观察相应的 w 梯度来凭经验测量与这一理想值的偏差。不管 w 或图像空间方向如何,这些梯度应该具有接近相等的长度。

这导致了更加可靠和一致的行为模型,使得架构探索更加容易。还观察到,更平滑的发电机更容易反转。

图 4。图解 PPL。图片由[4]提供。

投射到潜在空间

为了进一步探索和理解我们的生成网络,我们可以将真实的图像投射到潜在空间。这基本上意味着您想要获得 w 潜在代码(512 个数字),这将导致生成器生成看起来像我们的图像的输出。

使用 GANs,我们无法直接从输入图像中提取潜在代码,但我们可以通过以下方式进行优化:

  • 从起始潜在向量生成输出。
  • 将目标图像和生成的图像提供给充当特征提取器的 VGG16 模型。
  • 从两幅图像中提取特征,计算它们之间差异的损失,并进行反向传播。

我们从训练数据集中投影一半的图像有两个原因:

  • 发现数据泄漏,即评估过度拟合。
  • 为了在 3D 空间中可视化投影的潜在代码并研究其分布,尝试找到聚类并研究边缘情况。

图 5。使用 UMAP 可视化两个不同真实数据集(绿色、红色代表黑色素瘤和蓝色良性痣)和合成数据集(浅蓝色代表黑色素瘤和粉红色良性痣)的 W 空间。这两个集群对应于两个不同的类别:黑色素瘤和非黑色素瘤。作者图片。

评估真实性和过度拟合

我们如何评估模型的泛化能力或真实性?换句话说,我们如何确保我们的模型不是简单地复制它被训练的数据?这是确保真正的生成型模型的一个基本概念。

然而,这在该领域是一个公开的问题[5,6]。我们尝试了一种简单的方法。为了测量真实性并确保我们的生成模型没有复制真实数据,我们进行了以下实验。

首先,我们将来自真实数据集的 12,000 个样本投影到生成器的潜在空间中。这给了我们潜在的代码,使我们的生成器合成出与输入图像最相似的输出。为了优化给定输入图像的潜在代码,我们使用 VGG16 模型作为特征提取器,并计算目标图像和生成输出的提取特征的差异的损失,并执行反向传播。

图 6。实像及其投影样本的嵌入投影。这两个集群对应于两个不同的类。作者图片。

接下来,我们使用在真实和合成数据上训练的分类器的最后卷积层来提取真实图像和它们的投影图像的特征。这些嵌入使用 t 分布随机邻居嵌入(t-SNE)在 3D 空间中可视化。

这允许使用余弦距离在视觉上探索每个真实图像的最近的近邻,并且以这种方式检查真实图像和它们相应的生成图像有多接近。

我们将此视为对生成样本的真实性的测量。我们发现一些图像(这证明了它们的真实性)离它们的投影非常远,但仍然与目标图像相似(这证明了它们的保真度)。

解决后续任务

生成人工数据本身并不是目的,事实上它是为另一个工具创建工具的过程。预测性能,或解决后续任务,可以评估我们的合成数据的有效性。在我们的研究中,我们使用了痣的合成图像来平衡 ISIC 数据集,这意味着添加了几千个黑色素瘤的合成图像。正如我们之前提到的,当用于相同的预测目的时,合成数据应该和真实数据一样有用。对于我们的二元分类任务,我们在几个场景中使用了人工数据:

  • 通过仅添加合成黑色素瘤样本并对真实验证子集进行测试来平衡真实训练子集,
  • 通过添加属于两个类别的合成图像并对真实验证子集进行测试来平衡真实训练子集,
  • 使用大量的合成训练子集(每个类别具有相同数量的数据)并在真实的验证子集上进行测试。

在平衡的训练子集中混合黑色素瘤的真实和合成图像获得了最好的结果。计算的精度非常接近仅在真实样本中实现的精度,这可以证明人工数据的适用性。

不管合成数据有多有用,我们发现在真实数据中发现的所有人工制品,如黑框或医用尺,也会在人工数据中反映出来。但这是另一个故事的素材…

为了重现所描述的研究,我们鼓励您查看我们的 github 资源库,并在提供的自述文件中阅读更多关于我们研究的内容。

https://github.com/aidotse/stylegan2-ada-pytorch

在另一篇文章中,我们展示了如何使用 SIIM-ISIC 2020 数据集和 StyleGAN2 为皮肤损伤的合成图像生成准备数据。

文学

  1. 国际标准工业分类档案
  2. 甘评价措施的利弊
  3. 生成性对抗网络评价指标的实证研究
  4. 生成性对抗网络(GANs)专业化
  5. 您的合成数据有多可信?评估和审计生成模型的样本级指标
  6. 检测生成模式下数据复制的非参数测试

朱庇特笔记本的神话和问题

原文:https://towardsdatascience.com/on-the-myths-and-problems-of-jupyter-notebooks-81517a4696ef

数据科学软件工程

您可以使用当前工具对笔记本进行版本控制、测试和模块化。让我们讨论真正的问题。

图片作者。

Jupyter 笔记本,你爱它们,或者你讨厌它们。Jupyter 笔记本的使用(和误用)是数据社区中最具争议的话题之一。当然,有正当的理由避免使用笔记本,但是我希望谈话集中在真正的问题上,而不是一遍又一遍地讨论已经解决的问题。在这篇博文中,我讨论了笔记本电脑最常见的误区,并评论了尚未解决的关键问题。

"您不能对笔记本进行版本控制"

通常,笔记本是.ipynb文件。在幕后,.ipynb文件是具有预定义模式的 JSON 文件,它们将代码和输出保存在同一个文件中。独立笔记本很方便:你可以在笔记本上工作,保存它,明天再来,结果就在那里了。需要注意的是,用 git 管理它们很有挑战性。首先,.ipynb文件会增大 git 存储库的大小,其次,git diff(和 GitHub Pull 请求)不会起作用。GitHub 上笔记本的 diff 视图是这样的:

GitHub 不喜欢。ipynb 文件。图片作者。

幸运的是,我们可以轻松解决这个问题。有一个官方的 JupyerLab 扩展,将 git 集成到 Jupyter 接口中,允许你(除了别的以外)区分笔记本。它看起来是这样的:

您可以使用 jupyterlab-git 扩展来执行笔记本代码审查。图片作者。

你可以看到它清楚地显示了区别,git 上的当前版本有红色边框的图形,而新版本有绿色边框。

另一个选择是使用 nbdime ,它允许您从终端执行同样的操作(事实上,JupyterLab 扩展使用了幕后的nbdime)。

第三种选择(也是我最喜欢的!)是 Jupytext 。该软件包允许您将.py文件作为笔记本打开。这样,您可以交互地编辑您的代码,但是一旦您保存了它,它将存储一个.py文件。

当使用 Jupytext 时,您可以使用git diff和 pull 请求,因为您的笔记本是简单的脚本。主要的警告是,一旦关闭文件,输出就会丢失,但是可以使用 Jupytext 的配对函数,该函数将结果存储在一个单独的.ipynb文件中,有效地存储源代码并输出两个不同的文件。

“您不能测试笔记本”

这个有几个角度。如果我们考虑将笔记本作为一个整体来测试,我们可以使用 papermill 来有计划地执行它们并评估它们的结果。例如,您可以用papermill编写一个测试用例,如下所示:

一个更简单的替代方法是运行冒烟测试(即检查笔记本是否运行,但跳过输出检查)。您可以编写这样的脚本在您的 CI 系统中执行(例如 GitHub 操作):

最后,您可能对笔记本的单元测试部分感兴趣(例如,笔记本内部定义的功能)。我建议在笔记本外部定义函数,然后导入到笔记本中。这样,您可以像对任何其他函数一样对这些函数进行单元测试。但是,如果你想在.ipynb文件中编写函数,你可以使用 testbook

Testbook 允许你从一个.ipynb文件中提取定义并测试它们:

“您不能模块化笔记本”

另一个批评是你不能模块化笔记本。然而,这对于 Ploomber 来说很简单。假设您有三个笔记本(load.ipynbclean.ipynbplot.ipynb),您可以创建一个管道来按如下顺序执行它们:

然后,您可以使用以下命令运行它们:

Ploomber 将按顺序执行笔记本,并为每个笔记本制作一份副本,因此您可以记录这次运行的输出。

试试吧!

模块化的笔记本打开了许多可能性:它们更容易协作、测试,并且计算效率高(你可以并行运行独立的笔记本!).所以,我们非常鼓励你写流水线,而不是大块头的笔记本。

那么,真正的问题是什么?

隐藏状态

笔记本的隐藏状态阻碍了再现性。图片作者。

给定一个笔记本,我们说它有一个隐藏状态,当它线性地返回不同于存储的输出时。例如,左边的笔记本没有隐藏状态,因为如果您重新启动内核并按顺序运行所有单元,您将得到相同的结果(数字3)。但是,右边的笔记本有隐藏状态:如果我们重新启动内核,按顺序运行所有细胞,最终输出不会匹配(记录的是42,但我们会得到3)。这个问题如此普遍,甚至有一篇关于它的自然博客文章。隐藏使得不可能重现我们的结果。

我们可以通过测试来部分解决这个问题:我们可以在每个git push上线性执行我们的笔记本,以确保可重复的结果。然而,这并没有完全解决问题,因为我们仍然可能在笔记本开发期间以隐藏状态结束,直到我们推到存储库和 CI 中断时才发现。

一种流行的方法是反应式笔记本,它自动重新计算单元格以防止隐藏状态。例如,假设我有一个这样的笔记本:

反应式内核是隐藏状态的一种解决方案。图片作者。

如果我运行一个反应式笔记本并编辑单元格a = 1,笔记本将自动运行第三和第四个单元格(total = a + btotal),因为它们依赖于a的值,有效地更新和打印total变量的新值。反应性是 Julia 的笔记本系统 Pluto 已经提供的一个很好的特性,它也可以在一些 Jupyter 商业发行版中使用。幸运的是,人们已经在为 Jupyter 中的反应式内核开发开源解决方案。

然而,反应式内核也有局限性。在许多情况下,一些单元可能太昂贵而无法重新计算,如果运行一个看似很小的计算突然触发一个小时的计算,这可能会导致用户沮丧。反应式内核应该被强制到什么程度,或者我们应该建立什么样的替代品,这是一个值得讨论的话题。

代码质量

笔记本中的代码质量是我发现的最吸引人的问题,因为它总是导致从不同方面解决问题的观点。代码质量是目前笔记本最大的问题。

许多人认为笔记本会导致糟糕的代码质量。这是真的吗?我不这么认为。低质量的代码到处存在;这不是木星独有的问题。我的看法是,笔记本中有如此多难以阅读的代码,是因为 Jupyter 使计算大众化了。你不再需要掌握一个 IDE 和一个终端来开发数据分析代码;你可以打开笔记本,马上开始。所以突然之间,我们有很多没有软件背景的人在所有行业中作为数据科学家工作,这是一件非常棒的事情。每个人都应该有机会编码和创造价值,Jupyter 是许多人进入计算世界的入口。

问题不在于笔记本产生低质量的代码。作为一个社区,我们未能指导那些知道很多统计学和机器学习但没有多少编码经验的人。低质量笔记本电脑的普遍存在是缺乏开发和部署数据分析代码流程的结果。几乎没有任何数据团队将代码评审作为他们开发过程的一部分,那么我们如何期望我们成为好的导师并帮助提升我们同事的技能呢?

总结想法

笔记本是探索和分析数据的最佳方式,但它们有问题。因此,我希望我们能够从笔记本无法版本化的对话中继续,开始更多地讨论如何改进基于笔记本的开发和部署流程。在 Ploomber,我们坚信最大化数据团队交付价值的速度是笔记本电脑的核心。因此,我们正在努力解决所有笔记本电脑问题,以在功能交互性和定义良好的开发和部署流程的可靠性之间找到最佳平衡点。加入我们的社区让对话继续下去。

最初发布于ploomber . io

关于编写干净的数据管道

原文:https://towardsdatascience.com/on-writing-clean-data-pipelines-f22ee95e13fa

数据科学软件工程

组织数据分析逻辑的最佳实践

当帮助我们的社区的成员做出关于他们的数据管道的架构决策时,一个反复出现的问题是如何将分析逻辑分组到任务中。这篇博文总结了我们给社区成员的关于编写干净的数据管道的建议。我们的目标是防止我们已经看到的反模式,其中管道包含巨大的任务:

整体管道。图片作者。

这些整体管道有许多问题:它们使得协作更加困难,因为大量的逻辑集中在同一任务上。其次,您需要浏览源代码以找到您需要的内容。整体管道也更难测试,因为对每个任务的输出没有任何明确的预期。最后,它们的计算效率很低,因为它们没有充分利用并行化。当实践者构建这种管道时,我们遇到的一个常见原因是,他们使用的框架非常复杂,以至于添加一个新任务需要大量的工作,因此他们将完成工作的任务数量减到最少。如果您遇到这个问题,请切换到另一个允许您快速创建粒度任务的框架( Ploomber 是一个很好的选择)。

在深入细节之前,让我们达成共识,明确定义数据管道的概念。

什么是数据管道?

数据管道表示应用于一个或多个数据集的计算,其中给定任务的输出成为下一个任务的输入。下面是一个最简单的管道示例:

管道是一组有序的任务。图片作者。

我们的示例管道加载一些数据,然后对其进行清理。请注意,管道也称为工作流。

什么是任务?

任务是管道中最小的组件。根据您使用的框架,任务可以有多种形式。比如 Luigi 中,每一个都是一个类中的方法。在 Airflow 中,它是一个操作符的实例。最后,在 Ploomber 中,任务可以是一个函数,一个(Python,R,SQL)脚本,或者一个笔记本。箭头表示数据如何从一个任务移动到另一个任务。但是请记住,不同框架的机制是不同的。在某些情况下,这可以通过文件或内存实现。

编写干净的数据管道

管道和任务的概念很简单,但是在将这个想法应用到现实世界的问题时,可能很难决定什么构成了一个任务。以下是我们的三点建议:

  1. 为每个数据集创建一个管道分支
  2. 创建一个数据加载任务,一个清理任务
  3. 聚合数据集时,创建新任务

这三个简单的规则将允许您构建更加健壮和可维护的管道;让我们做一个简单的例子来说明我们的观点。假设您正在处理音乐数据。您可能有一个artists表、songs表和genres表。根据这些建议,我们最终得到了这样一个管道:

遵循我们三项建议的管道。图片作者。

每个表包含一个load-*和一个clean-*任务,每个部分构成一个流水线分支。请注意,我们所说的分支是指您的管道中的一组任务(例如load-artistsclean-artists是一个分支),不要与 git 分支混淆。一旦我们清理完数据,我们可能会意识到我们需要在艺术家层次上聚集歌曲来获得播放统计数据。因此,我们创建了artist-plays任务。最后,我们在visualize任务中可视化我们的数据。

遵循上面列出的规则可以让我们为每项任务定义一个清晰的责任。例如,我们知道load-*任务是加载数据,仅此而已。一个clean-*清理一个给定的数据集,而那个artist-plays聚集artistssongs。此外,假设我们发现给定的数据集对我们的分析是不必要的。我们可以快速地修剪我们的流水线,因为我们只需要删除与这样的数据集对应的分支

以这种方式组织管道简化了协作;例如,如果我们的同事想要探索artists数据,他们会知道最好的开始方式是clean-artists任务,因为它产生干净的artists数据。此外,如果任何人想参与数据清理部分,他们将知道根据他们希望清理的数据集修改什么任务。每个任务职责的清晰定义减少了重复工作:模糊的定义导致了分散在任务中的重复逻辑。

最后,每当我们想要聚合两个或更多数据集时,创建一个新任务是必不可少的,这个新任务的唯一目的是生成这个新的聚合数据集。本质上,这个新任务是创建一个衍生数据集;因为我们对它很明确,其他人可以用它来构建它。例如,我们可以让visualize任务包含数据聚合逻辑,但这将使人们无法知道我们已经有了艺术家级别的歌曲聚合逻辑。

编写干净的机器学习管道

如果你正在进行机器学习,我们还有几个建议:

  1. 每组一个任务
  2. 巩固训练集的一个任务
  3. 每个实验类型一个分支,每个分支有两个任务:训练和评估
  4. 可选,任务选择最佳模式

在为机器学习模型编写特征时,我们通常可以将它们分组。这种分组是主观的,但它有助于我们组织管道。对要素进行分组的最简单方法是根据其输入数据。例如,如果您使用clean-artists数据集作为输入,您可以创建一个生成要素的任务,并将其命名为features-artists。但是,如果您可以从相同的输入数据生成许多要素,您可能希望对它们进行更精细的分组。例如,有时,您可能拥有使用相同输入数据的要素。然而,每个特征列可能使用不同的变换来计算(例如,阈值或仿射变换)。如果您可以根据转换的高级定义对功能进行分组,那么您可以定义一个功能组。

例如,您可能想要聚合艺术家级别的歌曲数据,然后基于计数聚合生成特征:艺术家有多少首歌曲,5 分钟内有多少首歌曲,2020 年之前发布了多少首歌曲,等等。这里, count 是转换,这样我们可以创建一个包含所有 count 特性的特性组。此外,我们建议在特性组中的所有列前添加前缀;这将允许您在训练模型时完全基于前缀轻松选择(或取消选择)组。

在特征任务之后,创建另一个任务来整合所有特征以生成训练集;然后,并行训练任务将此作为输入。为了找到最佳模型,您可能想要训练其中的许多模型,因此您可以将训练任务定义为训练特定模型类型(例如 XGBoost)的任务,并对其他模型(例如 Random Forest)重复相同的逻辑。注意:如果要优化参数做模型选择,使用嵌套交叉验证

最后,对于每个培训任务,添加一个评估任务,并使用这个评估任务来生成度量和图表,以帮助您详细理解每个模型。最后,您可以添加一个最终任务,该任务将所有候选模型进行比较,并挑选出最佳模型。如果您遵循这些建议,您的管道将如下所示:

一个干净的机器学习管道。图片作者。

提示:保持培训和评估阶段简单的一个好方法是创建任务模板。例如,您可能有一个tasks.py,它将模型类型作为输入参数,并通过简单地将不同的参数传递给task.py文件来重用同一个文件来创建train-xgboosttrain-random-foresttrain-catbost任务。这里有一个关于如何用 Ploomber 实现这一点的例子。最后,如果您让每个train-*任务以相同的格式返回它们的输出,您可以重用相同的evaluate.py脚本(例如:一个model.pkl文件和一个带有交叉验证预测的数据集)。

保持管道清洁

实际上,遵循这些规则需要纪律。例如,假设您正在添加艺术家级别的功能(即features-artists任务)。您可能会遇到数据不如您预期的那样干净,因此您可能会在feature-artists任务中添加一些额外的清理代码;不要这样做。相反,转到相应的清理任务(clean-artists,在那里添加新的清理代码,然后继续处理feature artists中的特征代码。将清理逻辑保存在一个地方还有另一个好处:所有下游任务都将受益于新的清理代码。在我们的案例中,feature-artistsartist-plays都依赖于clean-artists,因此我们通过将清洁逻辑集中在clean-artists上,确保所有消费者都能从最干净的版本中受益。

遵守规则使可维护性变得更容易,因为扩展管道变得很容易。您想在艺术家级别计算流派特征吗?抢clean-artistclean-genres?你在歌曲数据集中发现问题了吗?打开一个拉取请求来修改clean-songs任务。

如果您认为您的用例不符合这些定义, ping 我们,我们将很乐意帮助您设计一个干净的数据管道。

最初发表于ploomber . io

一个填充值是不够的:重新索引数据帧时保留列类型

原文:https://towardsdatascience.com/one-fill-value-is-not-enough-preserving-columnar-types-when-reindexing-dataframes-3bb3f0572651

当处理数据帧时,重新索引是很常见的。当数据帧被重新索引时,旧的索引(及其相关值)与新的索引相一致,可能重新排序、收缩或扩展行或列。当重新索引扩展数据帧时,需要新的值来填充新创建的行或列:这些是“填充值”

当使用 Pandas 重新索引时,通过fill_value参数,只允许一个值。如果fill_value的类型与一个或多个列的类型不兼容,则该列将被重新转换为不同的、可能不需要的类型。

例如,给定一个具有三列类型 object、integer 和 Boolean 的 DataFrame,缺省情况下,重新索引索引会用 NaN 填充新行,NaN 是一种 float 类型,它强制将 integer 列转换为 float,将 Boolean 列转换为 object。

>>> df = pd.DataFrame.from_records((('a', 1, True), ('b', 2, False)), columns=tuple('xyz'))
>>> df
   x  y      z
0  a  1   True
1  b  2  False>>> df.dtypes.tolist()
[dtype('O'), dtype('int64'), dtype('bool')]>>> df.reindex((1, 0, 2))
     x    y      z
1    b  2.0  False
0    a  1.0   True
2  NaN  NaN    NaN>>> df.reindex((1, 0, 2)).dtypes.tolist()
[dtype('O'), dtype('float64'), dtype('O')]

柱状退化通常是有害的。预先存在的柱状类型可能适合数据;仅仅因为重新索引而不必要地更改该类型通常是意想不到的。从一种 C 级 NumPy 类型转换到另一种类型,比如从 int 转换到 float,可能是可以忍受的。但是当从 C 级 NumPy 类型转换到 Python 对象数组(object dtypes)时,性能会下降。和熊猫重配的时候,这个问题是没有办法避免的。

StaticFrame 是一个不可变的数据框架库,为这类问题提供了解决方案。在 StaticFrame 中,替代填充值表示可用于在重新索引、移位和许多其他需要fill_value参数的操作中保留列类型。对于异构类型列数据的操作,一个填充值是远远不够的。

StaticFrame 支持将fill_value作为单个元素、一个行长度的值列表、一个列标签映射或者一个FillValueAuto,一个定义类型到值映射的新对象。

所有示例都使用 Pandas 1.4.3 和 StaticFrame 0.9.6。导入使用以下惯例:

>>> import pandas as pd
>>> import static_frame as sf

我们可以通过用一个填充值 NaN 重新索引同一个数据帧,在 StaticFrame 中重现熊猫的行为。这导致了和熊猫一样的柱状类型。注意,默认情况下,StaticFrame 显示每一列的 dtype,这使得列类型的退化变得非常明显。

>>> f = sf.Frame.from_records((('a', 1, True), ('b', 2, False)), columns=tuple('xyz'))
>>> f
<Frame>
<Index> x     y       z      <<U1>
<Index>
0       a     1       True
1       b     2       False
<int64> <<U1> <int64> <bool>>>> f.reindex((1, 0, 2), fill_value=np.nan)
<Frame>
<Index> x        y         z        <<U1>
<Index>
1       b        2.0       False
0       a        1.0       True
2       nan      nan       nan
<int64> <object> <float64> <object>

避免重新索引时类型退化的一种方法是为每列提供一个填充值。使用 StaticFrame,可以为填充值提供一个列表,为每列提供一个值:

>>> f.reindex((1, 0, 2), fill_value=['', 0, False])
<Frame>
<Index> x     y       z      <<U1>
<Index>
1       b     2       False
0       a     1       True
2             0       False
<int64> <<U1> <int64> <bool>

或者,可以使用字典来提供列标签到填充值的映射。如果未提供标签,将提供默认值(NaN)。

>>> f.reindex((1, 0, 2), fill_value={'z':False, 'x':''})
<Frame>
<Index> x     y         z      <<U1>
<Index>
1       b     2.0       False
0       a     1.0       True
2             nan       False
<int64> <<U1> <float64> <bool>

前面的例子都要求每列有一个显式值,以提供最大的特异性。在许多情况下(尤其是对于较大的数据帧),需要一种更通用的方式来指定填充值。

一种选择是基于特定的 NumPy 数据类型映射填充值。这种方法被拒绝,因为 NumPy dtype 定义了一个以字节为单位的变量“itemsize ”,导致了大量可能的 NumPy dtype。更有可能的是,相同的填充值将用于独立于 itemsize 的 dtypes 族;例如,所有大小的整数(int8、int16、int32 和 int64)。

为了识别与大小无关的类型族,我们可以使用 dtype“kind”。NumPy dtypes 有一个独立于 dtype itemsize 的“kind”属性:例如,int8、int16、int32 和 int64 dtypes 都被标记为 kind“I”。如下所示,有 11 种数据类型,每种都有一个字符标签:

  • 布尔
  • i: int
  • u: uint
  • 外宾:浮动
  • 丙:复杂
  • 男:时差
  • m:日期时间
  • o:反对
  • s:字节
  • U: str
  • v:无效

为每种数据类型指定一个填充值提供了一种方便的方法来避免列类型强制,同时不需要为每列指定一个繁琐的规范。为此,StaticFrame 引入了一个新对象:FileValueAuto

使用类FillValueAuto作为填充值为所有 dtype 类型提供了无类型强制的缺省值。如果需要不同的映射,可以创建一个FillValueAuto实例,为每种数据类型指定一个填充值。

回到前面的重新索引示例,我们看到了使用FillValueAuto类的便利,并且所有列类型都被保留:

>>> f
<Frame>
<Index> x     y       z      <<U1>
<Index>
0       a     1       True
1       b     2       False
<int64> <<U1> <int64> <bool>>>> f.reindex((1, 0, 2), fill_value=sf.FillValueAuto)
<Frame>
<Index> x     y       z      <<U1>
<Index>
1       b     2       False
0       a     1       True
2             0       False
<int64> <<U1> <int64> <bool>

如果我们需要偏离提供的FillValueAuto缺省值,可以创建一个实例,指定每种数据类型的填充值。初始化器的关键字参数是单字符数据类型种类标签。

>>> f.reindex((1, 0, 2), fill_value=sf.FillValueAuto(U='x', i=-1, b=None))
<Frame>
<Index> x     y       z        <<U1>
<Index>
1       b     2       False
0       a     1       True
2       x     -1      None
<int64> <<U1> <int64> <object>

在 StaticFrame 中,几乎在任何需要填充值的地方,都接受相同数量的填充值类型。例如,在移位数据中,必须提供填充值;但是当移动异构类型的整个数据帧时,一个填充值是不够的。如下所示,默认的fill_value,NaN,强制所有列类型要么是 object 要么是 float。

>>> f = sf.Frame.from_records((('a', 1, True, 'p', 23.2), ('b', 2, False, 'q', 85.1), ('c', 3, True, 'r', 1.23)), columns=tuple('abcde'))>>> f.shift(2)
<Frame>
<Index> a        b         c        d        e         <<U1>
<Index>
0       nan      nan       nan      nan      nan
1       nan      nan       nan      nan      nan
2       a        1.0       True     p        23.2
<int64> <object> <float64> <object> <object> <float64>

和以前一样,使用一个FillValueAuto实例允许一个通用的填充值规范,它完全避免了列类型的退化。

>>> f.shift(2, fill_value=sf.FillValueAuto(U='', b=False, f=0, i=0))
<Frame>
<Index> a     b       c      d     e         <<U1>
<Index>
0             0       False        0.0
1             0       False        0.0
2       a     1       True   p     23.2
<int64> <<U1> <int64> <bool> <<U1> <float64>

在二元运算符的许多应用中也需要填充值。一般来说,对带标签的数据进行二元运算会强制操作数重新索引到联合索引,这可能会引入缺失值。如果缺少的值仅为 NaN,则可能会重新转换生成的列类型。

例如,给定两个数据帧,每个数据帧都有一个 float 和一个 integer 列,二元运算将为重新索引的值引入 NaN,将 integer 列强制转换为 float。这可以通过使用FillValueAuto在 StaticFrame 中避免。

由于二元操作符不接受参数,StaticFrame 提供了via_fill_value接口,允许在二元操作中需要重新索引时指定要使用的填充值。这类似于熊猫DataFrame.multiply()和相关方法提供的功能。有了 StaticFrame 的via_fill_value,我们可以继续使用任意二元运算符的表达式。

当将两个数据帧相乘时,每个数据帧都有一列浮点数和一列整数,由于重新索引而引入的 nan 会将所有值强制转换为浮点数。

>>> f1 = sf.Frame.from_records(((10.2, 20), (2.4, 4)), index=('a', 'b'))
>>> f2 = sf.Frame.from_records(((3.4, 1), (8.2, 0)), index=('b', 'c'))>>> f1 * f2
<Frame>
<Index> 0         1         <int64>
<Index>
a       nan       nan
b       8.16      4.0
c       nan       nan
<<U1>   <float64> <float64>

通过使用via_fill_valueFillValueAuto,我们可以保留列类型,即使需要重新索引,并且继续在表达式中使用二元运算符。

>>> f1.via_fill_value(sf.FillValueAuto) * f2
<Frame>
<Index> 0         1       <int64>
<Index>
a       nan       0
b       8.16      4
c       nan       0
<<U1>   <float64> <int64>

上面使用的只有几列的例子并没有完全展示出FillValueAuto的威力:当处理成百上千列的异构类型数据帧时,规范的通用性提供了一个简洁而强大的工具。

由重新索引或其他转换导致的无意类型强制的成本可能会导致错误或性能下降。StaticFrame 灵活的填充值类型,以及新的FillValueAuto,为这些实际问题提供了解决方案。

一行代码加速您在大数据上的 Sklearn 算法

原文:https://towardsdatascience.com/one-line-of-code-to-accelerate-your-sklearn-algorithms-on-big-data-9c26190a0dc5

英特尔 sklearn 扩展的推出。让你的随机森林比 XGBoost 还要快。

作者图片

介绍

Scikit-learn(又名 Sklearn)无疑是当今数据科学中使用最多的 Python 库之一。然而,用 sklearn 算法训练一个大数据集有时会很昂贵。当我还在学校的时候,我记得我训练了一个 SVC 模型一天。我确信我不是唯一一个对这种情况感到沮丧的人。而今天,我要向大家介绍的是 sklearnex,这是一个由英特尔 AI 团队在大约一年前开发的扩展,它最初是 daal4py 库的一部分。这个扩展有可能将您的 sklearn 代码加速 10-100 倍。

根据他们的文档,“Scikit-learn*的英特尔扩展动态修补 Scikit-learn 估算器,以使用英特尔(R) oneAPI 数据分析库作为底层解算器,同时通过使用矢量指令、IA 硬件专用内存优化等更快地获得相同的解决方案。在发射时。”他们的团队还声称,它可以将各种 sklearn 算法的速度提高 10 到 100 倍。你不觉得这很疯狂吗?一起来了解一下吧!

履行

要在您的 sklearn 应用程序上修补它而不更改代码,您可以使用以下命令行:

作者图片

如果您想在笔记本中直接使用它,您可以执行以下操作:

作者代码

如果您想在特定算法或特定算法列表的基础上修补它,您可以像这样直接在函数中传递名称:

作者代码

如果您想从您代码中删除它,您可以简单地解包它并重新导入您的 sklearn 模型,如下所示:

作者代码

Sklearn vs. Sklearnex

  • RandomForestRegressor
  • SVC
  • DBSCAN
  • RandomForestClassifier vs . xgb classifier

由于硬件的限制,很难复制他们的所有工作,我计划从三个主要的机器学习算法:回归,分类和聚类,将增强版本与原始版本进行比较。

在我们开始测试之前,让我们导入我们需要的所有包:

作者代码

随机森林回归量

第一步:培训

由于我使用 google colab pro 来进行这个演示,所以只分配了 25G 内存供使用。所以我会用 10 到 100K 的样本量来测试模型。对于更大的数据,如数百万条以上的记录,只需查看模式就足够了。

作者代码

第二步:收集结果:

作者代码

作者图片

基于输出,我们可以看到性能非常一致,而对于 100K 个样本,训练速度提高了大约 6.5 倍。我认为这是机器学习实践的显著增长。

第三步:策划

作者代码

作者图片

交换虚拟电路

第一步:培训:

注意:请注意这一部分,最终迭代的培训时间大约需要 1 小时 20 分钟。

作者代码

由于代码与我用 RandomForestRegressor 所做的极其相似,我将省略第 2 步和第 3 步。结果如下:

作者图片

作者图片

到目前为止,SVC 在这个演示中表现出了最鲜明的对比。在 sklearnex 的基础上,我们可以在 100K 样本的数据集上观察到 32 倍的速度。由于 SVC 的耗时问题,我在大多数情况下都放弃了这种算法,尽管它具有独特的算法唯一性。然而,随着 sklearnex 的增强,我认为 SVC 将开始获得更多的关注。

基于密度的噪声应用空间聚类

第一步:培训:

作者代码

结果:

作者图片

作者图片

根据英特尔的文档,他们的扩展可以帮助 DBSCAN 在具有 50 万个样本和 50 个特征的数据集上速度提高 322 倍。然而,我的 google colab 会因为 DBSCAN 的样本量大于 80K 而崩溃,所以我无法复制这个数字。根据我所能得到的,在 sklearnex 的帮助下,DBSCAN 平均快了 1.25 倍。

RandomForest vs. XGBoost

对于许多数据科学项目,我们已经看到人们选择 XGBoost 而不是 Random Forest。这种决策涉及许多权衡,当然,在大型数据集上的训练时间就是其中之一。所以我要给你看一些真正令人兴奋的东西:

第一步:培训

作者代码

结果:

作者图片

注:

Spped_X_RR 是原始随机森林与带扩展的随机森林的训练时间的比较

Spped_X_RX 是 XGBoost 在扩展为的随机森林上的训练时间比较

Spped_X_RR 是原始随机森林的训练时间与 XGBoost 的比较

作者图片

根据结果,我们可以看到,在 10K 和 100K 的给定数据集上,增强的随机森林分类器比 xgb 分类器分别快大约 3.5 倍。

你可以在这里找到完整的代码:https://github . com/Jin hangjiang/Medium _ Demo/blob/main/Intel _ sk learn/Intel _ Extension _ for _ sk learn . ipynb

结论

鉴于这一英特尔扩展实现的简单性,我相信它可以应用于数据科学中非常大范围的项目。特别是,鉴于有限的测试结果,我们可以清楚地观察到这个扩展的可伸缩性。总结一下,就这么简单:数据越大,性能越好。您可以在这里找到英特尔进行的完整比较的详细信息:https://Intel . github . io/sci kit-learn-intelex/acceleration . html

当然,目前这个扩展有一些限制。例如,扩展不支持随机森林和 K 近邻的多输出和稀疏数据。更多详情,请查看这里:https://Intel . github . io/sci kit-learn-intelex/algorithms . html

请随时与我联系LinkedIn

参考

英特尔公司。(2021).Scikit-learn* 2021.5 文档的英特尔扩展。https://intel.github.io/scikit-learn-intelex/blogs.html

Smirnov,E. (2021)借助英特尔 Scikit-learn 扩展节省时间和资金。https://medium . com/Intel-analytics-software/save-time-and-money-with-Intel-extension-for-scikit-learn-33627425 ae4

优化神经网络的另一种方法

原文:https://towardsdatascience.com/one-more-approach-to-optimize-neural-networks-1dd173703301

浅谈优化神经网络超参数的神经结构搜索和自适应算法

预览图像(作者提供的图像)

在过去的十年中,基于神经网络的解决方案变得非常流行。同时,深度学习是一个相当复杂的领域,对专家的理论知识要求很高。这个行业需要很多这样的专家,但是现在没有足够的专家来满足需求。由于供需之间的这种差距,特殊工具正在出现。让我们称这些工具为“自动化工具”。

这种工具也出现在其他行业。例如,特殊的机器使汽车装配自动化,灌溉系统使农业自动化。由于有了这项技术,组装汽车和灌溉所需的专业人员减少了。

在现代“数据科学”中也观察到了同样的过程。出现了各种自动机器学习(AutoML)框架和库。所有这些都允许我们自动化模型识别的过程。对于表格数据上的经典机器学习任务(分类、回归),可以使用 H2OTPOT自动生成LightAutoMLFEDOT 框架。这样的库被称为 AutoML 框架。然而,深度学习模型的识别领域已经被性质略有不同的技术所占据。这里常用的是所谓的神经架构搜索(NAS)。

一般来说,所有的 NAS 算法都属于 AutoML 范畴。然而,由于神经网络的特殊性,在方法上存在显著的差异。首先,神经网络往往比经典的机器学习算法有更多的超参数(例如,想象 ResNet 和线性回归模型,并比较它们)。其次,神经网络通常需要更多的计算资源。这是一个大问题,它限制了用于优化的算法集,该算法集可用于神经网络。

声明:AutoML 和 NAS 都很棒!越来越多的常规工作正在用软件来完成,这就给专家们留下了更多的时间来进行有趣的实验。我参与了 AutoML 工具的开发,并相信这种工具是数据科学发展的下一步。

神经架构搜索

神经网络是一个复杂的模型。它由大量组件(超参数)组成。在开始训练模型之前,专家需要确定神经网络的拓扑结构、多少层、多少个神经元、什么激活函数、批量大小等等。值得记住的是,神经网络需要大量的资源来训练。因此,使用网格搜索来完全枚举所有这些超参数太复杂了。

算法而不是专家可以选择模型。这样的算法有,例如库 AutoKerasNNIpy torch中的 ENAS 等等。有些方法允许您自己为简单的架构快速实现这样的优化器。比如基于 optuna 优化库— 的例子。另一方面,也有一些有趣的学术框架在寻找基于进化算法的最佳神经网络— nas-fedotNAS-object-recognition (这些模块的开发由我们实验室的同事负责:)。

这个话题真的很受欢迎,所以有很多工具,我在这里列出了其中很小的一部分。但是如果你对题目感兴趣,你可以从他们开始。更多的解决方案以概念的形式存在,例如,在期刊和会议记录的科学论文中。用关键词“神经架构搜索”搜索,你会找到很多文章(我保证)(图 1)。

图一。最近五年(2017 年以来)科技论文中带有“NAS”的题目。信息检索自https://www.lens.org/(图片由作者提供)

另一种方法?

列举了 NAS 领域正在发生的所有发展中的一小部分。是的,我想介绍另一个。这样做的动机是假设:我相信增加神经元和层数是增加算法精度的可靠方法。但与此同时,它明显增加了训练神经网络的“计算成本”,以及模型的大小。在这种情况下,搜索空间变得太大,无法找到真正最优的东西。也许如果我们限制给定的神经网络拓扑结构并优化激活函数和其他超参数(不改变层和神经元的数量),它也会工作得一样好。

所以,让我们试着做一个算法,优化神经网络的一些超参数。神经元的数量和层数在优化过程中不变。这使得减少搜索空间成为可能。

老实说,这很重要。至少对我来说是这样。算法和概念很好,但是开发产品的经验也很重要。体验由外部属性组成,比如存储库的设计、文档和相关材料。

图二。正在开发的模块的徽标(图片由作者提供)

实际上,概念比标志更重要

但是这个概念也很重要。所以我们来探讨一下算法的核心思想。

在开始实现算法之前,我遇到的第一个具体特性是 NAS 算法的高计算成本。事实上,为相当多的时期训练几个神经网络是非常耗时的。因此,决定只训练一个神经网络,但在训练过程中改变一些超参数。在某些时刻,会生成少量具有超参数替代配置的神经网络。为了确定所提议的变更有多成功,建议使用几个时期的度量增加(图 3)。

图 3。优化算法的主要思想(图片由作者提供)

主要的超参数是 m —初始和最终训练的时期数, pop_size —生成的具有备选超参数配置的神经网络数, n —专门用于训练每个“备选神经网络”的时期数, k —交叉或选择后固定训练中间神经网络的时期数, c —种群生成和变化的周期数。从超参数的名称可以看出,使用了来自进化计算领域的术语:备选神经网络的集合称为群体,评估模型有效性的过程称为适应性评估。超参数的变化——突变。然而,所提出的方法并不是真正的进化(尽管它非常类似于进化)。

优化过程如下面的动画所示。为了方便起见,每个中间神经网络都序列化在一个 zip 存档中。如有必要,可以指定标志,以便在计算结束后删除包含序列化模型的文件夹。

动画。神经网络优化(作者动画)

从动画中可以看出,首先训练具有超参数初始配置的单个神经网络。然后生成几个备选模型,在每个模型中替换超参数。当前原型能够进行以下更改:

  • 改变优化算法(SGD、Adam、Adadelta 等);
  • 在随机选择的层中改变激活函数;
  • 更改批量大小;

不多,但即使使用这种方法,搜索空间也相当大。

基于适应性评估选择最成功的替换:

  • 适应函数值取决于神经网络损失函数值的最后接收值的绝对值;
  • 适应函数的值取决于学习的速率(损失函数从一个时期到另一个时期变化得多快)。

最成功的模型是经过几个时代训练出来的。然后重复替换的循环。

技术细节

该模块基于三个主要部分:优化器(模型)、进化操作符(进化)和日志系统(图 4)。

图 4。程序不同部分(类和方法)之间的主要交互方式(图片由作者提供)

我发现实现一个日志系统特别有价值。在优化过程中,许多重要信息被存储(图 5)。

图 5。存储优化过程相关信息的元数据文件示例(作者图片)

除了信息之外,模型也被存储。值得注意的是,它们都是已经训练好的,并且代表现成可用的模型。为了更容易理解文件的名称,准备了下图(图 6)。

图 6。使用序列化神经网络存档名称(图片由作者提供)

如前所述,所有这些模型都保存在一个文件夹中。如果要求所有计算后只保留最终模型,可以指定实验后删除文件夹。因此,基于运行模型的实验,可以获得以下学习历史:

图 7。该算法的一个测试示例(图片由作者提供)

实验

为了了解该算法的有效性,我们决定针对不同的神经网络架构对不同的任务进行几项实验:

  • 前馈神经网络(FNN)优化算法的应用。任务—MNIST 数据集的多类分类以及与 Optuna 框架的比较(参见示例);
  • 卷积神经网络优化算法的应用。任务—遥感产品中的间隙填充(将该算法的有效性与没有超参数搜索的 init 神经网络训练进行比较)。

关于分类任务—MNIST _ optuna _ miha . ipynb
在这本 jupyter 笔记本中,从准确率指标上与 Optuna 进行了对比。optuna 优化器增加了神经网络的层数,而 MIHA 只改变了激活函数、批量大小和优化算法。从测试样本的实验结果中获得以下度量:MIHA — 0.974,奥普图纳— 0.976(结果几乎相同)。

关于填补空白的任务。
质量指标——均方误差(MSE)。具有超参数初始配置的神经网络的 MSE 是 0.38。对于相同的历元数,使用 MIHA 算法的超参数顺序变化的神经网络获得 MSE 0.13。

结论

因此,我们研究了机器学习的神经网络结构优化领域。我试着分享了我在这方面的进展,希望你觉得它很吸引人。值得补充的是,所描述的算法目前还不是一个完整的库。MVP 能够处理有限数量的任务,用于演示概念。

回头见!

有用链接:

R 中的单样本 Wilcoxon 检验

原文:https://towardsdatascience.com/one-sample-wilcoxon-test-in-r-7ca71403a419

了解何时以及如何在 r 中进行单样本 Wilcoxon 检验。另请参见如何通过具体示例来可视化和解释结果

Rodrigo KugnharskiUnsplash 上的照片

介绍

在之前的一篇文章中,我们展示了如何在 r 中进行双样本 Wilcoxon 测试。记住,这个测试实际上有两个版本:

  1. 曼-惠特尼-威尔科森检验(也称为威尔科森秩和检验或曼-惠特尼 U 检验),用于比较两个独立的样本。该测试是学生独立样本 t 检验的非参数版本。
  2. Wilcoxon 符号秩检验(也称为配对样本的 Wilcoxon 检验),用于比较两个配对样本。该测试是学生配对样本 t 检验的非参数版本。

在另一篇文章中,我们还展示了如何在 r 中手工进行单样本 t 检验,该检验用于确定测量变量的平均值是否不同于指定值(例如,基于您的信念或理论预期指定的值)。由于是参数测试,数据应该遵循正态分布(或者样本量应该足够大(即 30 以上),由于中心极限定理),结果才有效。

与单样本 t 检验不同,单样本 Wilcoxon 检验(也称为单样本 Wilcoxon 符号秩检验)是非参数检验,这意味着它不依赖于属于任何特定概率分布参数族的数据。非参数测试通常与参数测试有相同的目标(在这种情况下,将数据与给定值进行比较)。尽管如此,他们不需要正态假设,他们可以处理异常值和李克特量表。

在本文中,我们将展示何时执行单样本 Wilcoxon 检验,如何在 R 中执行以及如何解释其结果。我们还将简要展示一些适当的可视化。

什么时候?

单样本 Wilcoxon 检验用于将我们的观察结果与给定的默认值进行比较,默认值是根据您的信念或理论预期指定的值。换句话说,它用于确定一个群体是否与感兴趣的变量上的已知或假设的群体值显著不同。

由于检验统计量是根据观察值和默认值之间的差异等级计算的(使其成为非参数检验),当观察值不符合正态分布时,单样本 Wilcoxon 检验比单样本 t 检验更合适。

该测试的目标是验证观察值是否与我们的默认值有显著差异。就无效假设和替代假设而言,我们有(对于双尾检验):

  • H0:数据的位置是等于选择的值
  • H1:数据的位置与选择的值不同

换句话说,一个显著的结果(即拒绝零假设)表明数据的位置与选择的值不同

请注意,一些作者认为该检验是对中位数的检验,即(对于双尾检验):

  • H0:中间值等于选择值
  • H1:中间值与选择值不同

然而,只有在数据对称的情况下才会出现这种情况。如果没有关于数据分布的进一步假设,单样本 Wilcoxon 检验不是对中位数的检验,而是对数据位置的检验。 1

请注意,尽管不要求正态性假设,但独立性假设仍然必须得到验证。这意味着观察值必须相互独立(通常,随机抽样足以具有独立性)。

数据

为了便于说明,假设我们想要测试某次考试的分数是否与 10 分不同,即:

  • H0:考试分数= 10 分
  • H1:考试分数≠ 10

为了验证这一点,我们对 15 名学生及其考试分数进行了抽样调查:

dat##    Student_ID Score
## 1           1    17
## 2           2     5
## 3           3     1
## 4           4    10
## 5           5     4
## 6           6    18
## 7           7    17
## 8           8    15
## 9           9     7
## 10         10     4
## 11         11     5
## 12         12    14
## 13         13    20
## 14         14    18
## 15         15    15

假设学生之间的分数是独立的(一个学生的分数不受另一个学生分数的影响)。因此,独立性假设得到满足。

而且,样本量很小(n < 30) and based on the 直方图数据不遵循正态分布:

# histogram
hist(dat$Score)

作者的情节

注意,我们避免通过正态性检验来验证正态性(例如夏皮罗-维尔克检验),因为对于小样本量,正态性检验几乎没有能力拒绝零假设,因此小样本通常通过正态性检验(oz tuna,Elhan,和 tüccar 2006哈塞米和扎赫迪达斯 2012

还要注意,尽管我们使用了一个定量变量进行说明,但是单样本 Wilcoxon 检验也适用于区间数据和李克特量表。

怎么会?

单样本 Wilcoxon 测试可以在 R 中用wilcox.test()函数完成。

但是首先,将我们的数据可视化在一个箱线图中并计算一些描述性统计数据来比较我们的观察值和我们的默认值是一个很好的做法:

# boxplot
boxplot(dat$Score,
  ylab = "Score"
)

作者的情节

如果你和我一样喜欢使用[{ggplot2}](https://statsandr.com/blog/graphics-in-r-with-ggplot2/) 来制作你的剧情:

# boxplot
library(ggplot2)ggplot(dat, aes(y = Score)) +
  geom_boxplot() +
  labs(y = "Score") +
  theme( # remove axis text and ticks
    axis.text.x = element_blank(),
    axis.ticks = element_blank()
  )

作者的情节

一些基本的描述性统计(四舍五入到两位小数):

round(summary(dat$Score),
  digits = 2
)##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    1.00    5.00   14.00   11.33   17.00   20.00

从上面的箱线图和描述性统计中,我们看到样本中得分的平均值和中值分别为 11.33 和 14。

单样本 Wilcoxon 检验将告诉我们这些分数是否与 10 分有显著差异(因此它们在总体中是否与 10 分不同):

wilcox.test(dat$Score,
  mu = 10 # default value
)## 
##  Wilcoxon signed rank test with continuity correction
## 
## data:  dat$Score
## V = 67, p-value = 0.3779
## alternative hypothesis: true location is not equal to 10

输出显示了一些信息,例如:

  • 测试的标题
  • 数据
  • 检验统计量
  • p 值
  • 另一假设

我们关注 p 值来解释和总结测试。

释义:

根据测试结果,(在 0.05 的显著性水平上)我们不拒绝零假设,因此我们不拒绝本次考试分数等于 10 的假设,我们不能得出分数与 10 有显著差异的结论(p 值= 0.378)。

默认情况下,这是一个双尾测试。至于t.test()函数,我们可以通过使用wilcox.test()函数中的alternative = "greater"alternative = "less参数来指定需要单侧测试。

例如,如果我们想测试分数是否比 10 分:

wilcox.test(dat$Score,
  mu = 10, # default value
  alternative = "greater" # H1: scores > 10
)## 
##  Wilcoxon signed rank test with continuity correction
## 
## data:  dat$Score
## V = 67, p-value = 0.189
## alternative hypothesis: true location is greater than 10

释义:

在这种情况下,我们仍然不拒绝分数等于 10 的假设,我们不能得出分数明显高于 10 的结论(p 值= 0.189)。

有关函数中可用参数的更多信息,请参见?wilcox.test

请注意,使用wilcox.test()时,您可能会遇到以下警告:

Warning messages:
1: In wilcox.test.default(dat$Score, mu = 10) :
  cannot compute exact p-value with ties
2: In wilcox.test.default(dat$Score, mu = 10) :
  cannot compute exact p-value with zeroes

这是一个警告,而不是表明你的结果是不正确的。r 通知您,它报告的是基于正态近似值的 p 值,而不是基于数据的精确 p 值,因为存在联系(某些值是相同的)。如果您想移除警告,使用exact = FALSE选项。

结合统计检验和绘图

如果你是博客的常客,你会知道我喜欢直接在图上显示测试结果。这使我能够可视化数据,并以简洁的方式结束测试。

这要归功于{ggstatsplot}封装中的gghistostats()功能:

# load package
library(ggstatsplot)# combine plot and test
gghistostats(
  data = dat, # dataframe
  x = Score, # variable
  type = "nonparametric", # nonparemetric = Wilcoxon, parametric = t-test
  test.value = 10 # default value
)

作者的情节

直方图 2 显示了分数的分布,测试结果显示在图的标题中。

如您所见,测试结果是相同的,也就是说,数据中没有足够的证据来得出分数与 10 分显著不同的结论(p 值= 0.378)。

感谢阅读。我希望这篇文章能帮助你理解单样本 Wilcoxon 检验以及如何在 r。

和往常一样,如果您有任何与本文主题相关的问题,请添加评论,以便其他读者可以从讨论中受益。

参考

哈塞米、阿斯加尔和萨利赫·扎赫迪阿斯。2012."统计分析的正态性检验:非统计人员指南."国际内分泌和代谢杂志 10 (2): 486。

厄兹图纳、德亚、阿蒂拉·哈利勒·埃尔汉和埃尔泽·蒂卡尔。2006."根据不同分布下的 1 型错误率和功效调查四种不同的正态性检验."土耳其医学杂志36(3):171–76。

  1. 更多信息请参见本文中的↩︎
  2. 该直方图看起来与上一个直方图不同,因为仓的数量不同(4 对 5 仓)。 ↩︎

相关文章

原载于 2022 年 7 月 7 日 https://statsandr.com**的

一种将主观概念转化为客观指标的简单方法

原文:https://towardsdatascience.com/one-simple-methodology-to-turn-a-subjective-concept-into-an-objective-metric-14943cf6fb95

利用组织中的专家来创造一个真实的来源,并针对它训练一个模型

在一个商业团队中工作,我经常被要求量化一个没有广泛接受的定义的主观概念。一个典型的问题看起来像这样:“我们的商店有多少是成功的?”或者“我们有多少忠实客户?”

照片由 Unsplash 上的 Greyson Joralemon 拍摄

问题的关键在于,每个人对这些主观词汇都有自己的“理论”定义(并且相信他们的理解在整个公司都是一样的)。因此,当被问及这样一个问题时,主要的挑战是实际上将所有不同的观点结合起来,从而得出一个客观的定义。

举一个具体的例子——假设你在一家重要的连锁超市工作,有人问你“有多少家店是成功的?”

你可以考虑“利润”,开始考虑根据利润对不同的商店进行排名,但是:

  • 假设在你的行业中,利润是规模的函数——一个利润不多的小店会不会不太成功?
  • 一家商店刚刚开业,增长迅猛,但要到明年才能盈利,该怎么办?或者相反,一个利润很大但是负增长的店怎么办?你认为哪一个是成功的?
  • 一家利润丰厚但顾客负面评价越来越多的商店该怎么办?或者员工流动率很高的公司?

内部众包拯救

正如我们上面看到的,像“成功”或“忠诚”这样的主观概念在你的组织中可以有不同的定义。将这些定义统一到一个坚实的指标中是很重要的,也是很困难的。

有不同的方法来解决这个问题,但我发现一个特别优雅的方法是下面这个:与一些利益相关者合作建立 一个真相的来源。

基本上——请几名内部专家对您的人群中的代表性样本进行审查和分类,并在此基础上根据手动分类建立一个模型。这将让你明白什么是“数据”定义,最符合你试图定义的主观概念。

  • 这种方法使您的组织内部保持一致,因为您将来自不同团队的具有不同观点的风险承担者纳入进来。
  • 它还可以创建有意义的讨论并开启新的学习,尤其是当两位不同的专家对同一记录有不同的分类时,这将有助于您和他们了解彼此的基本原理。

如何具体使用

  • 找出一份能代表整个人口的记录清单

一个样本的例子(作者)

  • 请一些内部专家帮助手动分类不同的记录,并与他们一起审查分类,以得出最终评级

已审核的样本(按作者)

  • 恭喜你!您现在有了新度量标准的真实来源,这是一个更加简单的分类问题。你可以开始根据最终评级来拟合模型,并根据你的需要进行调整(例如,你如何平衡可解释性与复杂性或准确性与精确性,或者其他)。
  • 一旦你有了第一个定义,把它呈现给你的观众,并向他们展示这对企业意味着什么(用一些具体的例子)。根据需要迭代。

今天就开始实施!

上面介绍的方法是面对这类问题时你可以使用的许多方法之一。它不是万灵药,而且它确实需要你找到一些专家,他们愿意花时间转移他们已经内化了多年的知识,但是当你在从事需要大量调整的项目和/或你陷入困境甚至不知道从哪里开始的项目时,它可以很好地工作。

感谢阅读!

如果你觉得有趣,可以看看我的其他文章:

</7-tips-to-avoid-public-embarrassment-as-a-data-analyst-caec8f701e42>

你可以(也应该)使用的所有交叉验证技术的一站式教程

原文:https://towardsdatascience.com/one-stop-tutorial-on-all-cross-validation-techniques-you-can-should-use-7e1645fb703c

作为一名数据科学家,你需要知道的所有简历程序,解释如下

照片由安妮·伦凯拍摄

为什么要交叉验证?

在我开始销售相关商品之前,我必须宣传主要理念。开始了。

让我们想象一个你不知道什么是跨 val daton 程序的世界。在那个疯狂的世界里,你显然把你的数据分割成一个单独的训练和测试集。该模型从训练数据中学习,您通过预测所谓的看不见的数据(即您的测试集)来测试它的性能。如果你对分数不满意,你可以使用相同的设置调整你的模型,直到 GridSearch (或者 Optuna )喊出“够了!”。

这是这个过程中可能出现的多种可怕错误中的两种:

  1. 集合不能很好地代表全部人口。作为一个极端的例子,在具有三个类别(a、b、c)的行之外,所有的 ab 类别可能在训练集中结束,而所有的 c 在测试集中挂起。或者拆分数值变量,使得某个阈值左侧和右侧的值在训练和集合中分布不均匀。或者接近于这样一种情况,即两个集合中变量的新分布与原始分布如此不同,以至于模型从不正确的信息中学习。
  2. 在超参数调整期间,您将关于测试集的知识泄漏到模型中。搜索完成后,框架会找出最适合那个特定测试集的参数。既然我用的是特定的这个词,你应该已经开始考虑过度拟合了。因为如果你在同一个测试集上重复测试,就会发生这种情况——搜索框架只会给出让你满意的特定测试集的结果。

所以,如果我们回到 CV 受到全世界工程师喜爱和广泛使用的世界,所有这些问题都解决了。下面是 CV 的神奇之处,见 Sklearn 用户指南 (BSD 许可证):

上面是一个五重交叉验证过程的例子,需要五次迭代才能完成。在每次迭代中,新模型在四个折叠上进行训练,并在最后一个保持折叠上进行测试。通过这种方式,可以在不浪费任何数据的情况下对模型进行训练和测试。

接下来,报告平均分数及其标准偏差作为置信区间。只有这样,您才能真正用所选的参数来判断模型的性能,因为您得到的平均分将代表模型从数据中有效学习并对未知样本进行准确预测的真正潜力。

https://ibexorigin.medium.com/membership

获得由强大的 AI-Alpha 信号选择和总结的最佳和最新的 ML 和 AI 论文:

https://alphasignal.ai/?referrer=Bex

1.KFold

现在,让我们开始讨论执行 CV 程序的许多方法。最简单的就是上图看到的KFold。在 Sklearn 中实现同名。在这里,我们将编写一个快速函数来可视化 CV 拆分器的拆分索引:

现在,让我们将带有七个拆分的 KFold 拆分器传递给此函数:

这就是香草冰淇淋的样子。

另一个版本是在执行分割之前对数据进行洗牌。通过打破样本的原始顺序,这进一步降低了过度拟合的风险:

如您所见,验证样本的索引是以随机方式选择的。即便如此,由于我们做的是 7 重 CV,样本总数仍然是整个数据的七分之一。

KFold 是最常用的 CV 分配器。这很容易理解,而且非常有效。然而,根据数据集的特征,有时您需要对使用什么样的 CV 过程更加挑剔。所以,我们来讨论一下替代方案。

2.分层折叠

另一个为分类问题专门设计的 KFold 版本是StratifiedKFold

在分类中,即使在数据被分成多个集合之后,目标分布也必须被保留。更具体地说,具有 30 到 70 类比率的二进制目标应该在训练集和测试集中保持相同的比率。

这个规则在 vanilla KFold 中被打破了,因为它不关心类比率,也不在分割之前打乱数据。作为一个解决方案,我们在 Sklearn 中使用了另一个 splitter 类— StratifiedKFold:

它看起来和 KFold 一样,但是现在类比率在所有折叠和迭代中保持不变。

3.离开

有时候,你拥有的数据是如此有限,以至于你甚至不能把它分成训练集和测试集。在这种情况下,您可以执行 CV,在每次迭代中只留出几行数据。这被称为 LeavePOut CV,其中 p 是您选择来指定每个维持集中的行数的参数。

最极端的情况是LeaveOneOut拆分器,其中您只使用一行作为测试集,迭代的次数等于完整数据中的行数。如果为一个只有 100 行的小数据集构建 100 个模型看起来近乎疯狂,我会支持你。

即使对于更高数量的p,迭代次数也会随着数据集大小的增加而呈指数增长。想象一下,当p是 5,而你的数据只有 50 行时,将会构建多少个模型(提示——使用排列公式)。

因此,在实践中很少看到这种情况,但是 Sklearn 经常将这些过程作为单独的类来实现:

from sklearn.model_selection import LeaveOneOut, LeavePOut

4.洗牌拆分

不如我们不做 CV,只重复多次训练/测试分割过程?嗯,这是另一种方式,你可以玩弄交叉验证的想法,但仍然没有这样做。

从逻辑上讲,使用不同的随机种子生成多个训练/测试集应该类似于一个健壮的 CV 过程,如果进行足够的迭代的话。这就是为什么在 Sklearn 中有一个执行这个过程的拆分器:

ShuffleSplit 的优点是您可以完全控制每个折叠中的序列和集合的大小。集合的大小不必与拆分的数量成反比。

然而,与其他拆分器相反,不能保证随机拆分会在每次迭代中生成不同的折叠。因此,请谨慎使用该类。

顺便说一下,还有一个分层版本的 ShuffleSplit 用于分类:

5.时间序列片段

最后,我们有时间序列数据的特例,其中样本的排序很重要。

我们不能使用任何传统的简历类,因为它们会导致灾难。你很有可能在训练未来的样本并预测过去的样本。

为了解决这个问题,Sklearn 提供了另一个分离器— TimeSeriesSplit,它确保上述情况不会发生:

漂亮整洁!

用于非 IID 数据的其他 CV 拆分器

到目前为止,我们一直在处理 IID(独立同分布)数据。换句话说,生成数据的过程没有过去样本的记忆。

但是,在某些情况下,您的数据不是 IID 数据,即一些样本组相互依赖。例如,在 Kaggle 上的谷歌大脑呼吸机压力比赛中,参与者应该使用非 IID 的数据。

这些数据记录了人工肺进行的数千次呼吸(进、出),并以毫秒为间隔记录了每次呼吸的气压。因此,每次呼吸的数据包含大约 80 行,使这些行相互依赖。

在这里,传统的简历分割器不会像预期的那样工作,因为有一个明确的机会,分裂可能发生在“呼吸的中间”这里是另一个来自 Sklearn 用户指南的例子:

这种数据分组是特定领域的。一个例子是当从多个患者收集医疗数据时,从每个患者采集多个样本。并且这种数据可能依赖于个体组。在我们的示例中,每个样本的患者 id 将是其组标识符。

它还在后面陈述了解决方案:

在这种情况下,我们想知道在一组特定的组上训练的模型是否能很好地推广到看不见的组。为了衡量这一点,我们需要确保验证文件夹中的所有样本都来自在配对训练文件夹中根本不存在的组。

然后,Sklearn 列出了可以处理分组数据的五个不同的类。如果您掌握了前几节的观点,并且理解了什么是非 IID 数据,那么使用它们就不会有问题:

  1. 分组文件夹
  2. 分层分组折叠
  3. leavonegroupout
  4. 离开群组
  5. 分组刷新拆分

这些拆分器中的每一个都有一个groups参数,在这里您应该传递存储组 id 的列。这告诉班级如何区分每个组。

摘要

最后,尘埃落定,我们在这里。

我可能没有回答的一个问题是,“你应该总是使用交叉验证吗?”答案是暂时的肯定。当数据集足够大时,任何随机分割都可能与两个数据集中的原始数据非常相似。在这种情况下,简历不是一个严格的要求。

然而,统计学家和在 StackExchange 方面比我更有经验的人说,不管数据大小如何,都应该至少进行两到三次交叉验证。你越谨慎越好。

感谢您的阅读!

https://ibexorigin.medium.com/membership https://ibexorigin.medium.com/subscribe

阅读更多我写的故事…

https://ibexorigin.medium.com/28-weekly-machine-learning-tricks-and-resources-that-are-pure-gems-1-8e5259a93c94 </10-sklearn-gems-buried-in-the-docs-waiting-to-be-found-ad95a8fabdfd> https://ibexorigin.medium.com/26-weekly-ml-tricks-and-resources-that-are-pure-gems-2-3be56841b1d9

Python 中用于图像识别的一对一逻辑回归

原文:https://towardsdatascience.com/one-vs-all-logistic-regression-for-image-recognition-in-python-5d290ce64ad7

如何实现用于分类图像的机器学习 Python 代码

“水滴帮”——图片来自作者——卢卡·扎马塔罗版权所有 2022

他的文章是前段时间开始的一系列专门文章的延续。本系列建议读者理解导致生物医学数据的机器学习的基本概念,如线性和逻辑回归之间的差异、成本函数、正则化逻辑回归和梯度(参见参考资料部分)。每个实现都是从头开始的,我们不会使用优化的机器学习包,如 Scikit-learn、PyTorch 或 TensorFlow。唯一的要求是 Python 3 的更新版本,一些基础库,以及想把这篇文章读完的愿望!

回归(线性、逻辑、单变量和多变量)是统计模型,有助于发现观察到的数据集变量之间的相关性,并回答这些相关性是否具有统计显著性。
统计学家和软件工程师使用回归模型来创建人工智能系统,以完成识别、分类和预测分析。
在机器学习回归生态系统中,我们使用逻辑回归(LR) 特别是当因变量是二分的(二元的):我们想要解释因变量二元变量和其他自变量(名义变量、序数变量、区间变量或比率水平变量)之间的关系。

LR 最著名的软件实现之一是一对一算法,它具有扩展到多种结果的优势。因此,让我们试着理解像这样的算法是如何被用来对图像进行分类的。对于本文实现的代码和一个 Python 版本≥ 3.8,我推荐使用 Jupyter 笔记本

这里给出的代码只需要最少的优化计算,尤其是涉及到的线性代数。我们将使用像熊猫NumPymatplotlibSciPy 这样的包。这些软件包属于 SciPy.org T21,一个基于 Python 的数学、科学和工程开源软件生态系统。还有,我们将导入 seaborn ,这是一个基于 matplotlib 的 Python 数据可视化库。此外,来自 scipy.optimize 的对象 opt 将被创建,以对渐变进行优化。

import numpy as np
from numpy import load
import pandas as pd
import matplotlib.pyplot as plt
import scipy.optimize as opt    
import scipy.io as sio
from random import randint
import seaborn as sns
from matplotlib.pyplot import imshow

%matplotlib inline

开始吧!

数据集

我们将使用两个图像数据集:MNIST 和时尚 MNIST。第一个是著名的手写数字数据集(从 0 到 9)。Yann LeCun、Corinna Cortes 和 Christopher J.C. Burges 的原始 MNIST 数据集包含 70,000 (60,000 个训练样本+ 10,000 个测试样本)个数字。

MNIST: 在这里,我们将使用一个简化版的 MNIST,可从 Kaggle 下载,其训练集由 42000 个样本组成。数据集是一个 CSV 文件;每行包含 785 个特征:第一个特征代表标签(从 0 到 9 的数字),其余 784 个元素代表 28x28 像素图像的灰度像素值(从 0 到 255)。你可以从这个链接下载数据集。

时尚-MNIST :是来自 Zalando 的数据集。它由 60,000 个示例的训练集组成。此外,对于该数据集,每个样本都是 28x28 灰度图像,每行包含 785 个要素:第一个要素表示标签 0 T 恤/上衣、1 裤子、2 套头衫、3 连衣裙、4 外套、5 凉鞋、6 衬衫、7 运动鞋、8 包、9 踝靴。剩余的 784 个元素代表灰度像素值(从 0 到 255)。请从 Kaggle 链接下载训练集。

下载所有数据集;在建议的地方,从提供的链接下载文件:我已经用“label”替换了列名“labels ”,以便于代码处理。在本地解压缩压缩文件,打开 Jupyter notebook,然后键入:

df = pd.read_csv("YourPath/mnist_train.csv")

用于使用 Pandas pd.read_csv() 上传 MNIST 数据集。

我们创建数据帧 df,,它包含所有图片及其相关标签。在新的 Jupyter 笔记本单元格中键入“df”将显示数据结构:

图一。训练数据集结构(图片由作者提供)。

对于 MNIST 数据帧,就像时尚 MNIST 一样,行代表数字样本;列是像素。但是第一列专用于数字的标签,我们将使用它进行训练和分类。

那么,MNIST 图像是如何制作的,又是如何呈现给图像识别算法的呢?图 1 中报告的数字“六”是从数据集中提取的样本之一。我们可以可视化这个示例,但是首先,我们需要重新处理我们通过向笔记本添加一个简单代码而创建的 df dataframe:

'''
use df.loc for splitting df in a dataframe with the training set values (784)
and in a dataframe containing only the labels (from 0 to 9)
'''
df_train = df.loc[:, (df.columns!='label')]
df_train = df_train.loc[:, df_train.columns!='names']
df_train_label = df.loc[:, df.columns=='label']

'''
assign the two dataframes to the two numpy.ndarray arrays X for the training
and y for the labels
'''

X = df_train.values
y = np.int64(df_train_label.values.flatten())

这个短代码使用 Pandas 方法。loc 用于将数据帧 df 拆分成两个数据帧 df_traindf_train_label。第一个将包含我们将用于训练的 784 像素值;后者将只包含标签(从 0 到 9)。

同样,代码将 df_traindf_train_label 分配给两个 numpy.ndarray 矩阵, X 用于训练, y 用于标签。这一步非常重要,因为所有计算都需要线性代数和矩阵乘积。熊猫数据帧方法。价值观适合我们。X 向量包含 42,000 个项目,每个项目包含 784 个灰度值的向量。我们可以使用索引访问单个项目,在本例中, 0,数据集的第一个元素:

(图片由作者提供)。

我们还可以显示 X[0] 内容,看看它是如何制作的:

显示 X[0]内容(前 200 个值)(图片由作者提供)。

现在我们可以访问 X 向量了, matplot 函数 imshow() 将最终显示一张灰度 MNIST 手写图片,该图片由 784 个像素组成,以 28X28 像素的 2D 表示法来表示(图 2A)。例如,图像编号 3500 对应于数字“六”

'''
Reshape a 784-values vector extracted from one of the images
stored in the vector X (#3500) which represents a digit "six".
Use the NumPy method .reshape, specifiying the double argument '28'
then show the image with the function imshow, specifying the cmap='gray'
'''

image = X[3500].reshape(28,28)
imshow(image, cmap='gray')

但是为了正确显示图像,我们需要对 784 值向量进行整形,指定(28,28)作为方法numpy . shape的参数。代码将显示如图 2A 所示的数字:

图二。手写数字。a:是来自 MNIST 数据集的灰度图像,B 是其数值表示(图像 MNIST 由作者精心制作)。

为了更好地理解这个概念,图 2B 中的图像是对图 2A 中灰度版本的数字表示的图形改编。每个像素对应 0-255 范围内的特定灰度值,其中 0 为黑色,255 为白色。

包含数据集标签的输出向量y是一个 numpy.ndarray.

下面的代码可视化了一组 50 个随机选取的数字;运行它以浏览数据集内容:

# Function for visualizing fifty randomly picked digits, from the dataset

def plotSamplesRandomly(X, y):
    %matplotlib inline

    # create a list of randomly picked indexes.
    # the function randint creates the list, picking numbers in a 
    # range 0-42000, which is the length of X

    randomSelect = [randint(0, len(X)) for i in range(0, 51)]

    # reshape all the pictures on the n X n pixels, 
    # where n = sqrt(size of X), in this case 28 = sqrt(784)
    w, h =int(np.sqrt(X.shape[1])), int(np.sqrt(X.shape[1]))
    fig=plt.figure(figsize=(int(np.sqrt(X.shape[1])), int(np.sqrt(X.shape[1]))))

    # Define a grid of 10 X 10 for the big plot. 
    columns = 10
    rows = 10

    # The for loop
    for i in range(1, 51):

        # create the 2-dimensional picture
        image = X[randomSelect[i]].reshape(w,h)
        ax = fig.add_subplot(rows, columns, i)

        # create a title for each pictures, containing #index and label
        title = "#"+str(randomSelect[i])+"; "+"y="+str(y[randomSelect[i]])

        # set the title font size
        ax.set_title(title, fontsize=20)        

        # don't display the axis
        ax.set_axis_off()

        # plot the image in grayscale
        plt.imshow(image, cmap='gray')

    plt.show()

代码定义了一个名为“plotSamplesRandomly”的函数,该函数在分配了一个“randomSelect”索引列表以传递给循环的中的 X 向量后,创建了一个包含 50 个图像的管道。

结果如图 3 所示:

图 3:MNIST 数据集中数字的可视化

一对一算法解释。

一对一算法是 LR 的一种特殊实现,它由不同的二元分类器组成。提醒你一下,LR 假设是:

(图片由作者提供)。

g 函数使用转换后的 θ 向量与 X v 向量的乘积作为参数。这个自变量被称为 z,,它被定义为:

(图片由作者提供)。

这意味着 g(z) 函数是一个 sigmoid 函数 ( 逻辑函数)并且是非线性的。因此,LR 成本函数计算如下:

(图片由作者提供)。

Python 中 sigmoid 函数的实现如下:

# The Logistic Regression Sigmoid Function (The g function)

def sigmoid(z): 
    return 1 / (1 + np.exp(-z))

我们需要保证梯度下降将收敛到全局最小值,避免非凸的问题 J(θ),而不是发生在线性回归梯度。(参见图 4B)。

图 4:非凸与凸代价函数(图片由作者提供)。

因此,我们必须重写成本函数,以保证凸的 J(θ) :

(图片由作者提供)

(图片由作者提供)。

重写 LR 成本函数,它将看起来像图 5 的红色曲线:

图 5:绘制 Logistic 回归成本函数(图片由作者提供)。

例如,如果我们有两个二进制条件,比如数字 0 和数字 1,代表我们的结果( y ),如果 y=1,我们关于 y 的假设预测的成本函数是:

如果 y = 1 ,但是我们预测 hθ(x) = 0我们会以相当大的代价来惩罚学习算法(参见图 5 中的红色曲线,因为在这种情况下,代价将趋于无穷大。相反,如果我们的预测是 hθ(x) = 1, (从而等于 y ),那么成本将会是 0。

y = 0的情况下,我们有相反的情况:

如果y = 0并且我们预测 hθ(x) = 0成本将为 0,因为我们的假设与 y 匹配,而如果我们的预测是 hθ(x) = 1 ,我们最终将付出非常大的成本。**

此外,LR 成本函数必须“正则化”,这意味着我们需要添加额外的特征来达到更好的假设,并避免拟合不足的问题(有关更多信息,请参见这篇专门讨论正则化逻辑回归的文章):

(图片由作者提供)。

正则化成本函数的一种合适方法是通过收缩我们添加的所有θ参数来修改成本函数,以获得特征的额外高阶多项式项。尽管如此,由于我们不知道什么参数对于收缩是必要的,我们需要通过在末尾添加一个项来收缩所有的 thetas。新的正则项在公式中以黄色突出显示,将收缩所有θ。不惩罚θ0 至关重要。 lambda 的作用是收缩θ,所以如果 lambda 极大,假设 h 就会欠填充。

正则化 LR 成本函数的实现是:

**# Logistic Regression Cost Function (Regularized)

def calcLrRegCostFunction(theta, X, y, lambd):

    # number of training examples
    m,n = X.shape  

    # Calculate h = X * theta (we are using vectorized version)
    h = X.dot(theta) 

    # Calculate the Cost J
    J = (np.sum(np.multiply(-y,np.log(sigmoid(h))) - \
                 np.multiply((1.0 - y),np.log(1.0 - sigmoid(h)))) /m) + \
                 np.sum(theta[1:]**2) * lambd / (2 * m)    

    return J**

我们感兴趣的是使用 梯度下降 找到成本函数的最小值,这是一个可以使这种搜索自动化的过程。梯度下降计算成本函数的导数,通过参数 α、的平均值更新向量 θ ,即学习率。梯度下降使用数据集的实际矢量 yh 矢量预测之间的差来“学习”如何找到最小成本函数。该算法将重复直到它收敛。 θ 更新必须同时进行。

(图片由作者提供)。

正则化梯度函数的实现将是:

**# Logistic Regression Gradient (Regularized)

def calcLrRegGradient(theta, X, y, lambd):

    # number of training examples
    m,n = X.shape  

    # Calculate h = X * theta (we are using vectorized version)
    h = X.dot(theta) 

    # Calculate the error = (h - y)
    error = np.subtract(sigmoid(h), y)    

    # Calculate the new theta
    theta_temp = theta
    theta_temp[0] = 0.0
    gradient = np.sum((((X.T).dot(np.divide(error,m))), theta_temp.dot(np.divide(lambd,m)) ), axis=0   )

    return gradient**

我们需要十个不同的二元分类器用于一对一和 MNIST 数据集(从 0 到 9 的十个数字)。我们知道每个数据集图像由 784 个像素组成,这意味着我们有 784 个特征。该算法是一个多元回归,这意味着我们必须将多个特征(784 像素)与一个特定的标签相关联。该算法可以逐步对特征进行分类,最初尝试每次将两个特征关联到一个标签,直到所有特征都被分类。

假设我们在第一步中从两个特征 pixel_1 和 pixel_2 收集值,这两个特征具有三个标签 0、1 和 2。在这种情况下,该算法将三个标签中的两个分配给负类;比方说,标签 0 和 1 被分配给负类,而剩余的标签 2 被分配给正类。该算法前进到第二步,将负类分配给另一对标签(0 和 2),将正类分配给标签 1;在第三步中,我们将把标签 1 和 2 标记为负,把标签 0 标记为正。因此,对于三标签分类,我们有三个分类器,每个分类器都被训练来识别三个标签中的一个。对于每个标签,一对一训练逻辑回归分类器 hθ(x),以预测 y=1 的概率。每次计算都对应一系列θ值,这些值必须乘以向量 X. 。最后,该算法将只选取一个将 hθ(x)最大化的标签。

图 6 将解释一对多的过程:

图 6。OneVsAll 算法。图 6B:考虑来自两个特征 x1 和 x2 的值:该算法包括在三个步骤中实现的三个不同的二元分类器。第一步将标签 0 和 1 的值分配给负类,将标签 2 的值分配给正类。第二步将负类分配给另一对标签(0 和 2),将正类分配给标签 1。在第三步中,我们将 1 和 2 标记为负,0 标记为正(图 4A 和 4C)。最后,算法将只挑选一个将 hθ(x)最大化的标签(图片由作者提供)。

让我们看看完整的一对一的 python 代码:

**# One-vs-All

def oneVsAll(X, y, lambd):    
    m , n = X.shape;
    num_labels = max(set(y))+1
    all_theta = np.array(np.zeros(num_labels * (n+1))).reshape(num_labels,n+1)
    initial_theta = np.zeros(n+1)

    # Add a column of 'ones' to X
    # Add a column of ones to the X matrix
    X = np.vstack((np.ones(m), X.T)).T        

    for i in range(0, num_labels):
        in_args = (X, (( y == i).astype(int)), lambd)
        theta = opt.fmin_cg(calcLrRegCostFunction, 
                            initial_theta,
                            fprime=calcLrRegGradient, 
                            args=in_args, 
                            maxiter=50, 
                            gtol=1e-4, 
                            full_output=False)

        all_theta[i:] = theta.T

    return all_theta**

函数 oneVsAll 接受两个向量 X、y 和λ作为自变量,并调用优化函数 scipy 函数,该函数使用非线性共轭梯度算法最小化成本函数。这个函数很复杂,深入的解释超出了本文的目的。读者可以通过 scipy 提供的共轭梯度的链接找到更多信息。

现在所有代码(或几乎所有代码)都准备好了,我们将运行 onVsAll 函数,在新的 Jupyter 笔记本单元格中键入:

**# Run oneVsAll

lambd = 0.01
all_thetas = oneVsAll(X, y, lambd)**

该函数执行训练阶段,由收敛到最小值并收集所有θ参数的一系列循环来表示。每个步骤的编号对应于标签编号:

**Warning: Maximum number of iterations has been exceeded.
         Current function value: 0.023158
         Iterations: 50
         Function evaluations: 105
         Gradient evaluations: 105
Warning: Maximum number of iterations has been exceeded.
         Current function value: 0.020032
         Iterations: 50
         Function evaluations: 102
         Gradient evaluations: 102
Warning: Maximum number of iterations has been exceeded.
         Current function value: 0.068189
         Iterations: 50
         Function evaluations: 98
         Gradient evaluations: 98
Warning: Maximum number of iterations has been exceeded.
         Current function value: 0.088087
         Iterations: 50
         Function evaluations: 97
         Gradient evaluations: 97
Warning: Maximum number of iterations has been exceeded.
         Current function value: 0.047926
         Iterations: 50
         Function evaluations: 93
         Gradient evaluations: 93
Warning: Maximum number of iterations has been exceeded.
         Current function value: 0.086880
         Iterations: 50
         Function evaluations: 106
         Gradient evaluations: 106

(to be continued)**

整个游程的乘积再次是包含成本函数最小化所需的所有θ向量的 numpy.ndarray 向量。

(图片由作者提供)。

all_theta 向量的长度等于 10,即标签的数量;每个θ向量都有图像的精确大小(784,加 1,因为我们在训练阶段给 X 向量加了 1)。

既然梯度已经收敛,让我们用一小段代码来看看它的预测精度:

**def predictOneVsAll(all_thetas, X):
    m , n = X.shape
    X = np.vstack((np.ones(m), X.T)).T    
    # This line calculate the max Theta
    prediction = np.argmax(sigmoid( np.dot(X,all_thetas.T) ), axis=1)
    print('Training Set Accuracy: {:f}'.format( ( np.mean(prediction == y )*100 ) ) )
    return prediction**

(图片由作者提供)。

predictOneVSAll 函数的目的是将 OneVSAll 运行期间获得的θ值与转置的 X 向量相乘。结果作为所有预测的平均值返回,并转换为百分比。MNIST 数据集的预测精度为 89.17。我们将把结果收集到一个新的变量中,我们称之为“pred”此变量是另一个 numpy.ndarray 它的大小和 X(42000)一样,包含了 oneVsAll 所做的所有预测。

看到算法的运行将会很棒,展示一些具体的结果。这段代码适合我们,它代表了我们用来从数据集中随机显示数字的先前代码的修改版本。它将向量 X, y 作为参数,加上之前创建的向量 pred :

**# Function for visualizing fifty randomly picked digits with their prediction.
# Code for testing the oneVsAll function

def plotSamplesRandomlyWithPrediction(X, y, pred):
    %matplotlib inline

    # create a list of randomly picked indexes.
    # the function randint creates the list, picking numbers in a 
    # range 0-42000, which is the length of X

    randomSelect = [randint(0, len(X)) for i in range(0, 51)]

    # reshape all the pictures on the n X n pixels, 
    # where n = sqrt(size of X), in this case 28 = sqrt(784)
    w, h =int(np.sqrt(X.shape[1])), int(np.sqrt(X.shape[1]))
    fig=plt.figure(figsize=(int(np.sqrt(X.shape[1])), int(np.sqrt(X.shape[1]))))

    # Define a grid of 10 X 10 for the big plot. 
    columns = 10
    rows = 10

    # The for loop
    for i in range(1, 51):

        # create the 2-dimensional picture
        image = X[randomSelect[i]].reshape(w,h)
        ax = fig.add_subplot(rows, columns, i)

        # create a title for each pictures, containing #index and label
        title = "#"+str(randomSelect[i])+"; "+"y:"+str(y[randomSelect[i]])+"; "+"p:"+str(pred[randomSelect[i]])

        # set the title font size
        ax.set_title(title, fontsize=15)        

        # don't display the axis
        ax.set_axis_off()

        # plot the image in grayscale
        plt.imshow(image, cmap='gray')

    plt.show()**

唯一的区别在这行:

**title = "#"+str(randomSelect[i])+";"+\
"y:"+str(y[randomSelect[i]])+"; "+\
"p:"+str(pred[randomSelect[i]])**

变量“标题”在这里被执行用于显示图像 ID、实际 y 值和预测值。代码的部分输出如图 7 所示:

图 7:来自 MNIST 数据集的数字预测图

对于每个数字,该算法报告其预测的准确率为 89%。

以下代码显示了识别一个数字时算法的活动;它采用 X、y、all_thetas、图像 ID 和 pred(包含所有预测的向量):

**def plotOneSample(X, y, all_thetas, imageID, pred):

    # Make a copy of X
    X_original = X
    m , n = X.shape
    X = np.vstack((np.ones(m), X.T)).T    
    MaxTheta = max(sigmoid(np.dot(X[imageID],all_thetas.T)))

    # apply all the theta matrix on a specific X
    MaxThetaPosition = sigmoid(np.dot(X[imageID],all_thetas.T))

    %matplotlib inline
    w, h = int(np.sqrt(X.shape[1])), int(np.sqrt(X.shape[1]))

    image = X_original[imageID].reshape(w,h)
    imshow(image, cmap='gray')

    MaxThetaDf = pd.DataFrame(MaxThetaPosition.tolist()) 
    for col in MaxThetaDf.columns:
        predictedCategory = MaxThetaDf[MaxThetaDf[col] == MaxTheta].index.tolist()

    print(str("Real digit: "+str(y[imageID])))
    print(str("Max Theta: "+str(MaxTheta)))        
    print(str("Precicted category: "+str(predictedCategory[0])))
    print ("\n")
    print(MaxThetaDf)

    return**

输出显示了如何在其他九个矢量中选择最佳的θ矢量。最佳θ确实用于预测标签:

图 8:从 MNIST 数据集中为数字 4 选择最佳θ

其他数据集的实验

您可以使用建议的另一个数据集尝试这里描述的代码。

**df = pd.read_csv("YourPath/fashion-mnist_train.csv")**

来自 Zalando 的数据集“fashion-mnist_train.csv”的预测准确率为 85.74 %

图 9:预测时尚-MNIST。(图片来自时尚 MNIST 数据集)

结论

一对一算法是一种很好的图像分类算法。它是逻辑回归的更复杂的阐述,继承了 sigmoid 函数、正则化和梯度。我们使用的 MNIST 和时尚-MNIST 代表了用于训练这种算法的数据集的最佳示例,因为每张照片都有很好的对比度,并且有整洁的背景,从而导致良好的预测准确性。如果您尝试处理包含更多灰度级和更复杂背景的其他列表,预测准确度会非常低。无论如何,分类性能是快速的,并且该特征使得一对一算法适用于计算机视觉中的许多令人兴奋的应用。

我希望这篇文章对你有帮助!

参考文献。

作者制作了所有图像,包括包含公式的图形,除了 MNIST 和时尚 MNIST 图像,它们属于各自的数据集。

  1. Y.LeCun、L. Bottou、Y. Bengio 和 P. Haffner。"基于梯度的学习应用于文档识别."IEEE 会议录,86(11):2278–2324,1998 年 11 月。【上线版本】
  2. 来自 Kaggle 的 MNIST 属于知识共享 CC0 公有领域。
  3. 时尚-来自 Kaggle 的 MNIST受麻省理工学院许可(MIT)版权【2017】Zalando SE、https://tech.zalando.com
  4. Luca Zammataro,一个或多个变量的线性回归,走向数据科学,2019
  5. Luca Zammataro,癌症中恶性肿瘤预测的逻辑回归,走向数据科学,2019
  6. Luca Zammataro,以一对多检测黑色素瘤,走向数据科学,2020 年
  7. Andrew NG,机器学习 | Coursera
  8. 克里斯·阿尔邦(Chris Albon),机器学习与 Python 食谱,O'Really,ISBN-13:978–1491989388。

基于机器学习的在线广告点击预测

原文:https://towardsdatascience.com/online-ad-click-prediction-with-machine-learning-b68c1467d960

用逻辑回归预测在线广告点击的分步指南

Unsplash 上由Towfiqu barb huya拍摄的照片

我们生活在一个每年花费数十亿美元在网络广告上的世界。它有助于提高知名度,吸引网站流量,并鼓励目标消费者采取特定行动,如购买。这是传播营销和促销信息以及扩大客户群的最有效方式之一。在线广告商使用不同的渠道来锁定受众,但并非所有渠道的表现都一样。根据各种因素,可以观察到这种性能变化。

在这篇文章中,我们将使用监督机器学习算法来预测用户是否会点击呈现在他面前的广告。

在这个实验中,我们将使用一个数据集来显示一个特定的互联网用户是否点击了一个广告。

完整的代码和参考数据可以在的 Deepnote 笔记本中找到。

让我们开始介绍数据集。

资料组

我们将要处理的数据集来自一家在线广告公司。数据集中的每一行代表一个广告,标签“点击广告”代表观众是否点击了该广告。该数据还包括关于广告查看者的更多细节,例如,查看者的年龄和性别、用户当天在网站上花费的时间等。

让我们加载数据集并查看一下。

我总是使用 Pandas 的' info '、 describe '和' isnull '方法来仔细查看数据集,了解数字特征,并检查是否有空值。

我们从年龄列中观察到以下情况:

  • 最长寿者:61 岁
  • 最年轻的人:19 岁
  • 平均年龄:36 岁

尽管列'点击了广告'包含数值,但它实际上代表了用户是否点击了广告的标签。因此,我们将它视为分类列,并将其用作预测标签。

探索性数据分析

让我们用热图中表示的关联矩阵来进一步探究数据集。相关性描述了变量之间从-1 到+1 的线性相关性。其中+1 表示强正相关,-1 表示强负相关。相关性为 0 表明这两个变量不是线性相关的。值得注意的是,相关矩阵中的对角线值总是 1,因为它表示变量与其自身的相关性。

在我们的关联热图中,可以看到点击结果与个人花在网站和互联网上的时间成反比。所以让我们来看看这些变量间的关系对。

在左图中,我们观察到那些在网站上花费时间较长的人不太可能点击广告。我们还发现,那些更多使用互联网的人也不太可能点击广告。

让我们看看年龄是如何影响广告点击事件的。很明显,年轻人群对广告不太感兴趣。

现在,有了这些观察,让我们建立我们的逻辑回归模型来预测广告点击概率。

模型结构

我们将使用逻辑回归模型,因为它易于实现并且训练起来非常有效。我建议以逻辑回归为基准,尝试更复杂的算法,并检查改进空间。

我们将为我们的模型考虑以下特征,因为从相关矩阵中,我们看到这些是预测广告点击的影响因素。

  • 每天在现场花费的时间
  • 年龄
  • 地区收入
  • 日常互联网使用
  • 男性(性别)

现在,你知道步骤了。首先,选择特征列和标签,然后使用 scikit-learn 的便捷 train_test_split 函数将数据集分成随机的训练和测试子集。之后,建立并训练一个机器学习模型。对我们来说,这是一个逻辑回归模型。瞧。

注意:在训练模型之前,我们不需要标准化数据集,因为逻辑回归对不同尺度的特征并不敏感。

结论

正如我们在性能方面看到的,逻辑回归做得非常好,达到了大约 90%的准确率。作为练习,您可以用其他模型进行试验,并对照这个模型检查它们的性能。

一般来说,我们可以说,有了这五个特征,我们就可以预测广告观众的行为。也许这给了我们思考如何更好地吸引观众的方向感。

感谢阅读!如果你喜欢这篇文章一定要 鼓掌(最多 50!) 和咱们 连上LinkedIn和* 关注我上媒 保持更新我的新文章*

通过此 推荐链接 加入 Medium,免费支持我。

*https://analyticsoul.medium.com/membership *

在线分析处理(OLAP)及其对数据科学的影响

原文:https://towardsdatascience.com/online-analytical-processing-olap-and-its-influence-on-data-science-c386bc96a736

定义 OLAP 及其与 OLTP 的区别

照片由 Unsplash 上的 GuerrillaBuzz Crypto PR 拍摄

在线分析处理(OLAP)是一种组织大型、杂乱的数据库的技术,以便可以从多个角度对它们进行商业智能应用分析。它在数据科学领域尤其重要,因为它通常是更深入分析的良好起点。

在线分析处理是如何工作的?

在线分析处理用于为数据挖掘或商业智能优化数据库,并加速查询。在这个过程中,关系表中的数据被重新构造,并按维度分别存储在所谓的多维数据集中。这些多维数据集包含许多业务分析师需要的最常查询的维度和值。

在该体系结构中,这会产生两个数据库:

  1. 数据仓库 :这里存储的是来自源系统的原始表和行。如果需要详细访问特定的行,可以查询数据仓库。例如,数据仓库中的一个表包含所有订单的详细信息。这里的每一行恰好对应一个订单,甚至一个订单项目。
  2. OLAP —数据库:这些数据库试图聚集经常使用的列(维度)并将它们保存在内存中。这对于计算聚合关键数字的商业智能应用程序尤其重要。例如,对于管理层来说,每月的营业额是决定性的,所以不是每一单订单都必须保持连续。

什么是 OLAP 立方体?

一旦数据集要在图中显示,我们就需要两个不同的概念:维度和值。维度是每一个评价的基础,代表一个独立的值。例如,如果我们的数据集包含去年的销售数据,则存在维度“订单日期”、“客户编号”或“产品编号”。相比之下,有一些数值,是我们在图表中可以依靠的具体数字,例如,销售量或营业额。

然后,至少一个维度和一个关键数字的组合创建图表。例如,条形图可以使用客户号作为维度,销售额作为值。结果是一个视图,从中可以读取最高销售额的产品。简而言之,这就是商业智能的工作方式。各种维度和值被组合到图表中,这应该有助于回答业务环境中的特定问题。

传统的数据库没有针对这种查询进行优化,因此导致了缓慢而低效的事务,尤其是在同时查询几个维度时。这里有一个具体的例子可以说明这个问题:

对于销售评估,我们需要整合三个维度,一个客户维度(例如客户名称)、一个产品维度(例如产品名称)和一个时间维度(例如订单日期)。由于关系数据库的规范化,所有这些信息都存储在不同的数据库表中。为了将它们结合在一起,我们需要使用 SQL 中的连接操作。

三个表的 SQL 连接|图片:作者

为此,OLAP 使用了所谓的立方体。其中,几个维度可以表示为立方体的轴。这些值就是立方体空间中的一个具体点。因此,我们可以将客户、产品和时间这三个维度表示为立方体轴。

然后,所有商业智能用户都可以访问此多维数据集结构并进行查询。这避免了对数据库的多次连接查询,并使事务更快、更有效。当然,立方体中可以包含三个以上的维度。在这种情况下,立方体仅用作图示支持,不应理解为实际的立方体。例如,从第四维度来看,整个事物不能再用立方体来表示,但仍然可以使用在线分析处理。

OLAP 立方体的例子|图片:作者

立方体使哪些操作成为可能?

对于分析师来说,划分为立方体带来了全新的可能性,这是传统的关系数据库所无法实现的,或者只有大量的计算工作才能实现。总的来说,OLAP 立方体的操作可以分为五类:

  • 切片:在切片中,通过将其中一个维度设置为固定值,只考虑立方体的一部分(一个切片)。在我们的例子中,这可能是一个特定的产品,考虑了所有客户和所有订单时间。
  • 切割:切割比切片更受限制,因为它设置了多个维度。例如,我们可以查看特定客户在 2022 年 6 月的所有产品,因此产品维度仍然是灵活的,但是客户和订单日期维度将是固定的。
  • 下钻/上钻:在此类别中,分析师可以选择从汇总视图下钻至详细级别(下钻)或从详细级别上钻至汇总级别(上钻)。如果您首先查看整个月的销售额,然后切换到列出每天销售额的视图,那么您正在进行向下钻取。
  • 汇总:在这种情况下,一个维度的所有值被汇总。例如,如果公司的销售额追溯到三年前,则将最后三年的全部销售额相加并输出。
  • 支点:这个类别有些抽象,因为它给了分析师思考新想法和掷骰子的机会,带来了一个新的、可能被忽视的维度。例如,在商业智能中,查看一段时间内的销售额是非常常见的。然而,OLAP 立方体也可以将单个客户或产品的视图带回前台。

使用 OLAP 的优点和缺点是什么?

OLAP 的使用使得频繁查询比传统的关系表更加灵活和高效,而传统的关系表当然不是为此目的而设计的。此外,它们还通过 OLAP 魔方提供的操作,为商业智能中的数据提供全新的视角。

使用 OLAP 魔方的商业分析师|图片:作者

只有少数几点反对使用在线分析处理,尽管如此,OLAP 的传播仍然很低。这主要是由于关于 OLAP 的知识还不像例如 SQL 那样广泛,SQL 现在几乎在每一个继续教育或学习中都被教授。因此,必须首先对人员进行培训,或者必须高价购买技术诀窍。

至于在线分析处理的实际功能,分析仅限于可以用数字表示的数据。否则,它们无法在立方体的坐标系中表示。例如,对于来自社交媒体挖掘的文本数据,为了使用 OLAP,必须首先找到数字表示。

有哪些不同类型的在线分析处理?

在线分析处理系统总共可以分为三种不同的类型。这些是:

  1. 多维在线分析处理(MOLAP) :这是最常用的形式,形成多维立方体。虽然立方体的形成需要相对长的时间,但是查询非常快速和有效。这种形式特别适用于数值字段。
  2. 关系在线分析处理(ROLAP) :这种类型去掉了立方体,而是直接在关系表上工作。在 ETL 过程中,数据仓库被提供了聚集表。这些可以使用 SQL 查询。
  3. 混合在线分析处理(HOLAP) :这是前两种类型的结合。部分数据存储在传统的 MOLAP 多维数据集中,其余部分存储在 ROLAP 存储中。

OLTP 和 OLAP 的比较

OLAP(表示联机分析处理)和 OLTP(表示联机事务处理)这两个缩写经常被错误地联系在一起,因为这两个名称具有相似的结构。然而,事实上,这些术语根本没有多少共同点,只是描述了不同的概念。

在线事务处理(OLTP) 描述专门从事快速事务处理的系统。主要的焦点是让前端的相关人员相对容易地处理他们的交易,例如在收银台。

另一方面,在线分析处理(OLAP) 是一个在数据库中进行更简单数据分析的平台。因此,它主要由业务分析师数据科学家在后端使用,以分析业务数据并找到有助于公司进一步发展的部分复杂的相关性。

从技术的角度来看,差异变得更加明显,正如由 IBM汇编的以下几点所示:

  • OLTP 查询通常基于单个数据记录来处理事务,而 OLAP 查询大量数据记录。
  • 在联机事务处理中,响应时间起着重要作用,而在联机分析处理中,分析与时间无关,在许多情况下可能需要更长时间。
  • 在联机分析处理中,数据不被修改,而只是被分析。在线事务处理用每个传入的事务修改数据库。
  • OLTP 的存储需求相对较小,因为尽管存储了许多事务,但它们具有清晰的结构和有限的列数。另一方面,OLAP 处理大量的数据,因为信息来自不同的数据库并被合并是很常见的。

这是你应该带走的东西

  • 在线分析处理是一种组织商业智能中使用的大型数据库的技术。
  • 维度和值被组织在所谓的 OLAP 立方体中。这导致分析师的不同操作和数据的新视图。
  • 与传统数据库相比,OLAP 的使用主要提供了数据检索的效率优势。

如果你喜欢我的作品,请在这里订阅https://medium.com/subscribe/@niklas_lang或者查看我的网站* 数据大本营 !还有,medium 允许你每月免费阅读 3 篇 。如果你希望有无限制的 访问我的文章和数以千计的精彩文章,不要犹豫,点击我的推荐链接:【https://medium.com/@niklas_lang/membership】每月花$5***获得会员资格**

* *

Python 中基于流数据的时间序列预测

原文:https://towardsdatascience.com/online-time-series-forecasting-in-python-12bada43b8bd

百里香增强剂的例子

张浩Unsplash 上拍照

在线学习包括用新数据更新参数,而无需重新调整整个过程。对于涉及增强的模型,如百里香增强,从我们停止的地方开始的能力将节省宝贵的计算时间。

对我们来说幸运的是,boosting 过程打开了一条在给定新数据的情况下更新参数的简单路径。这是因为整个系统是为了从残差中学习而构建的。一旦我们有了新的数据,我们只需要预测新的数据,并将这些残差注入到程序中。其余的从那里正常流出!

介绍

在本文中,我们将快速浏览一下百里香增强的新“更新”方法,它允许在线学习在我们接收新数据时更新模型。

对于我们的示例,我们将生成一个简单的序列,其中包含 2 个趋势变化点和一些季节性因素:

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
sns.set_style('darkgrid')seasonality = ((np.cos(np.arange(1, 301))*10 + 50))
np.random.seed(100)
trend = np.linspace(-1, 1, 100)
trend = np.append(trend, np.linspace(1, 50, 100))
trend = np.append(trend, np.linspace(50, 1, 100))
y = trend + seasonality
plt.plot(y)
plt.show()

作者图片

有了我们的系列,让我们用百里香按正常方式安装它。如果你以前没有接触过百里香,我推荐你看看这篇文章,它给出了一个很好的概述:

并确保从 pip 获得最新版本!

这样一来,我们就可以安装它了:

from ThymeBoost import ThymeBoost as tb
boosted_model = tb.ThymeBoost(verbose=0,
                           n_rounds=None,
                           split_strategy='gradient')
output = boosted_model.fit(y,
                           min_sample_pct=.2,
                           trend_estimator=['linear'],
                           seasonal_estimator='fourier',
                           seasonal_period=25,
                           additive=True,
                           global_cost='mse',
                           fit_type=['local'],
                           )
boosted_model.plot_results(output)

作者图片

boosted_model.plot_components(output)

作者图片

我不会在不同的参数上花太多时间,只要记住fit_type='local'意味着我们正在寻找变化点。

总的来说,看起来我们得到了合理的配合。可以通过增加建议拆分的数量来改善结果,但这在视觉上看起来还好

既然我们已经看到了这个系列“完全”适合的样子,让我们试试新的“更新”方法——这是在线学习的一部分。

首先,我们将进行标准拟合,但只有 100 个值:

boosted_model = tb.ThymeBoost(verbose=0,
                           split_strategy='gradient')
output = boosted_model.fit(y[:100],
                           min_sample_pct=.2,
                           trend_estimator=['linear'],
                           seasonal_estimator='fourier',
                           seasonal_period=25,
                           additive=True,
                           global_cost='maicc',
                           fit_type=['global', 'local'],
                           )

接下来,让我们获取下一批数据来更新模型。出于显而易见的原因,我们将称之为“update_y ”!

update_y = y[100:150]

最后,让我们用“更新”方法更新模型。它所需要的只是 fit 函数的输出和更新后的序列。注意我是如何覆盖输出变量的。update 方法的输出与 fit 方法相同:

output = boosted_model.update(output, update_y)
boosted_model.plot_results(output)

作者图片

如上所述,更新方法允许我们使用 fit does 的所有标准方法,如“plot_results”、“plot_components”和“predict”。让我们看看组件:

boosted_model.plot_components(output)

作者图片

目前的结果看起来不错!我们通常不会得到完全符合的相同的结果。这是由于我们在进行变点检测时考虑的分裂,以及其他一些细微差别。但是,结果应该是堪比

让我们用接下来的 100 个数据点再次更新模型:

update_y = y[150:250]
output = boosted_model.update(output, update_y)

最后,我们将用它来预测 25 个步骤:

predicted_output = boosted_model.predict(output, 25)
boosted_model.plot_results(output, predicted_output)

作者图片

和以前一样绘制组件:

boosted_model.plot_components(output, predicted_output)

作者图片

我们做到了!从几个更新和合理的展望预测很好的适合。

在你点击离开之前——在改变点的环境中使用它时,需要考虑一些重要的事情…

首先,min_sample_pct=.2适用于该系列,因此如果您要求模型更加反应灵敏,然后将该值降低到适当的水平,例如 0.05。

其次,并不是传递给 fit 方法的所有东西都可以在 update 方法中工作。其中最重要的是乘法拟合和传递外生变量。

最后,实际上我认为这些是唯一的考虑因素。至少,他们是我唯一知道的!但是,如果您遇到任何其他边缘案例或问题,一定要让我知道!

结论

在这篇文章中,我们刚刚介绍了使用百里香进行在线学习的基本用例。该功能仍在开发中,因此如果有任何问题,一定要在 github 上打开一个问题。如前所述,该特性不允许外生变量或乘法拟合,但它们在待办事项列表中!

如有任何问题,欢迎留言!

如果你喜欢这篇文章,你可能会喜欢其他一些关于百里香的文章:

感谢阅读!

OntoloySim:基于本体的 Python 生产模拟

原文:https://towardsdatascience.com/ontoloysim-an-ontology-based-production-simulation-for-python-fd8c40cd8a4

离散模块化仿真库,可选择保存和加载仿真状态

来源:作者图片

动机

OntologySim 库的开发是由实现模拟(生产系统的离散事件模拟)的想法驱动的,这种模拟可以在任何时候保存。因此,完整的状态存储在本体中,并且可以从外部修改或分析。修改后,模拟可以以完全相同的状态重新开始。因此,它为使用数字双胞胎和数据分析创造了新的可能性。作为本体的模拟的一致实现使得这成为可能。此外,基于本体的模拟允许关于所使用的生产系统的高度灵活性,无论是矩阵、生产线还是车间生产。

一个基于本体的仿真模型的概要,来源:图片作者[1]

本体论导论

本体论实现的特殊特征是使用本体论作为数据存储器和模拟状态存储器的一致实现。本体是知识库中知识的正式描述。在这种情况下,owlready2.0 用作模拟开发的基础。本体建模允许在任何时候灵活添加机器、流程、运输商或其他实体。

本体论的主要思想,来源:作者图片[1]

优点

  • 保存,模拟加载
  • 轻松交换策略的可能性
  • 运行时扩展和调整模拟
  • 该图书馆在 GPLv3 名下

如何建立本体论

在下文中,使用一个简单的示例来呈现模拟的创建。

1)安装

安装要求 Python >3.6,以下两种安装类型可用:

pip install ontologysim

或者通过 GitHub

git clone https://github.com/larsKiefer/ontologysim
cd ontologysim
pip3 install -r requirements.txt

2)本体论的使用

为了使用 ontologySim,每个文件中需要以下导入

import ontologysim

如果使用来自 GitHub 的代码,您可以直接跳转到示例文件夹,其中已经预先配置了包含 ini 文件的可执行文件 Main.py。

3) Ini 文件

模拟运行的第一步是创建各种 ini 文件。ini 文件是模拟初始化的基础。

3.1)生产文件:定义任务和生产布局 在本例中,使用 2 台机器和 2 种产品类型创建模拟。保存在以下 ini 文件中。

3.2) Owl-File:定义保存 该 ini 文件定义在仿真运行期间是否应该创建 Owl 文件,作为一个完整的仿真(状态)图像。

3.3)控制器文件:定义控制策略 本体论允许选择不同的控制器,如 FIFO,或使用定制的编程策略。由于本体,在给定状态下可用的每个信息也可用于控制策略的定义。

3.4) Logger-File:定义 KPI 和事件
的保存。最后,定义 KPI(基于 ISO 22400)如何保存以及是否保存。

4)主文件

配置完成后,可以开始模拟运行。

5)结果

作为模拟运行的结果,可以存储超过 50 个 KPI。因此,KPI 可通过自由可用的时间范围作为时间序列获得,以及汇总的 KPI 值。模拟的结果和状态可以用不同的方式保存。一方面,模拟的状态可以导出为 owl 文件。另一方面,KPI 可以存储为 CSV 或存储在数据库中。

如何改进这个例子?

正如已经提到的,这只是对基于本体的模拟的简短介绍。更多的例子和预配置文件可以在 GitHub 资源库的 example 文件夹下找到。

GitHub:https://github.com/larsKiefer/ontologysim

PyPi:https://pypi.org/project/ontologysim/

阅读文件:https://ontologysim.readthedocs.io/en/latest/

结论

感谢阅读。如果你有什么要补充的,欢迎随时留言评论!

关于本体论的技术解释、基础知识和文献综述,可以在这里查阅该杂志:https://www.mdpi.com/2076-3417/12/3/1608

感谢这项工作的合作者发表本文:马文·卡尔·梅(@马文。5 月、安德烈亚斯·库恩勒和吉塞拉·兰扎

这项研究工作是在 DIGIMAN4.0 项目(“零缺陷工业 4.0 生产的数字制造技术”,https://www.digiman4-0.mek.dtu.dk/)的背景下进行的。IGIMAN4.0 是一个由欧盟研究和创新框架计划“地平线 2020”支持的欧洲培训网络(项目 ID: 814225)

来源

[1]: May MC,Kiefer L,Kuhnle A,Lanza G .基于本体的生产模拟与本体主义。应用科学。2022;12(3):1608.https://doi.org/10.3390/app12031608

Python 中的 OOP 理解一个类

原文:https://towardsdatascience.com/oop-in-python-understanding-a-class-bcc088e595c6

照片由哈维·加西亚·查维斯Unsplash 上拍摄

理解 Python 类的基本组件

背景

这是关于 Python 中面向对象编程(OOP)的博客文章系列的第二部分。

在我的上一篇文章中,我提到了什么是 OOP,以及为什么你可能想学习 OOP,即使你可能看不到它的明显用途。万一你没看过,好奇的话,可以看看:Python 中的面向对象编程——什么和为什么?。

在这篇文章中,我们将开始深入一些 OOP 概念的内部,并尝试加深我们对它们的理解。

一个类的构造块

为了保持连续性,也为了有一个具体的例子,我将从我在上一篇文章中创建的名为NumList开始。我们将剖析NumList来看看它的元素。然后我们会再回来对一个的元素进行更一般的讨论。

首先,让我们注意以下事项:

  • 定义一个类以关键字:class开始,然后跟一个类的名字,以一个类似于函数的冒号(:)结束。
  • 一个类通常配备有这三个组件:
    –一个名称:在我们的例子中,NumList用来标识这个类。
    属性:关于类的一组特性。在NumList中,__list是一个属性。一个类也可能没有属性。
    方法:类执行某些任务的能力。在NumListadd_value()remove_valueget_list()中是方法。与属性类似,类也可以不包含方法。

一个简单的 Python 类配备了三样东西:名称、属性和方法

进入一个班级的内部

Python 构造函数

现在让我们来谈谈NumList中定义__init__()的第一块代码。我们将讨论这个方法是什么,解释使用的符号和关键字。

  • 这是一个特殊的方法,叫做构造器
    –每次从类中创建对象时,构造函数都会自动运行。它加载了一个类的所有必要元素,使它变得可用。
    –在NumList的情况下,一创建NumList类的对象,空列表__list就被构造函数初始化。
    –构造函数只在类中使用。
    –构造函数必须至少有一个参数。在NumList中,__init__()有一个参数- self
  • self:该参数用作自参考。
    –使用该参数使属于该类的变量可用。
    –它可以被称为任何其他名称,但称为self只是一种习惯。
  • 虚线符号:我们创建了空列表self.__list
    –该约定用于访问或创建对象的属性:<ClassName><dot(.)><property name>
    –在NumList中,我们使用self.__list = []创建了一个名为__list的属性,属于NumList,用self表示,并作为一个空列表初始化。
  • __:以两个下划线开头的组件名称使组件成为私有的。
    –表示组件只能在类内访问。是 [封装](https://en.wikipedia.org/wiki/Encapsulation_(computer_programming) 的一个实现。
    –试图访问私有组件将导致错误。
    –试试:list01 = NumList()然后len(list01.__list)。这将导致一个AttributeError

方法

add_value()remove_value()

接下来的三个代码块实现了三个方法或函数。

前两个方法基本上是我们创建的两个函数的再现,用于在类上下文中演示过程化编程,因此在关键字和符号方面有以下变化:

  • 使用self作为参数:对于任何方法,self都是一个强制参数,如前所述,

self 使得所有来自类的属性和方法,在我们的例子中 *NumList* ,可用到方法。

  • 方法中的参数:根据需要,方法可以有多个参数,作为类上下文之外的常规 Python 函数。例如,add_value()方法除了强制参数self外,还有一个名为val的参数。
  • 点符号:根据前面讨论的约定,self指的是类本身。
    –如此,self.__list进入空单。为了使用一种方法,我们再次使用点符号。
    –为了构造方法:add_value(),我们使用了List类中的append()方法。因为我们的__list本身是一个列表,所以它继承了append()方法。所以要使用这个方法我们用:self.__list.append()

get_list()

因为我们将__list初始化为NumList中的隐藏组件,所以直接访问列表是不可能的。所以我们需要一个方法或函数来访问它。

*get_list()* 为我们提供了进入 *__list* 的隐藏参数。

超越单一的阶级

亚纲

现在让我们假设在将我们的产品NumList交付给我们的客户后,我们从推荐中获得了一个新客户。该客户想要相同的列表和功能,但还需要:

  • 获取列表中数字总和的能力

那么,我们该怎么做呢?

一个显而易见的方法是复制NumList的代码,并为其添加另一个方法,姑且称之为get_total()。但是,如果我们不断获得新的客户或订单,并有不同的附加功能需求,该怎么办呢?很快,这种复制和修改的过程将不再是一个有效的解决方案。

OOP 有一个有效解决这个问题的方法:创建子类。创建子类有两个明显的优点:

  • 代码的重用:一个子类继承了超类的所有方法,因此我们不必重新创建这些方法。
  • 定制:一个子类可以包含新的方法和属性。这使得定制解决方案变得容易,同时保持超类的基础不变。

例子

现在让我们创建一个名为NumListExt01的子类来扩展NumList类的功能。

Initial list of values of cust02: []
Updated list after adding values to it: [2, 20, 44, 12]
Updated list after removing value 12 is:  [2, 20, 44]
Sum of all the elements of the current list is: 66

注意NumList是如何被子类利用和定制而不影响超类的:

♻️的子类 *NumListExt01* 继承了超类*NumList****remove_value()**get_list()*的方法。****

➕子类 *NumListExt01* 有一个额外的方法 *get_total()* ,它只存在于这个类中,因此只对我们的新客户可用。

下一步是什么?

在这篇文章中,我们看到

  • 我们如何构造一个 Python 类
  • 我们如何通过添加子类来扩展现有的

在下一篇文章中,我们将讨论更多关于子类的内容,深入子类的内部,并结合实例讨论 Python 中的继承

更新:查看下一篇文章Python 中的面向对象编程——继承和子类

开放领域分类

原文:https://towardsdatascience.com/open-domain-classification-1a10a8dd0431

开放域中的聊天机器人

照片由李宗盛Unsplash 上拍摄

当我们设计聊天机器人时,我们意识到会有一些我们无法处理的查询。在相当长的一段时间里,我们的方法是对我们的分类器预测的意图置信度使用一个阈值。这个阈值是一个超参数,我们通过审查客户的真实反馈添加的。然而,我们仍然不断得到一些明确的域外 查询的假阳性结果。

这是一个糟糕的客户体验。我们对于聊天机器人的目标是善于处理那些聊天机器人应该处理的事情。但是,也要确保聊天机器人清楚地退出那些它不应该处理的查询。

聊天机器人对开放域查询表现不稳定有三个原因——

  1. 设计聊天机器人时的封闭世界假设。
  2. 神经网络的过度自信。
  3. ReLU 网络产生高可信度预测的倾向。

在本文中,我们将讨论这 3 个问题以及我们为聊天机器人实现开放域分类的策略。

封闭世界假设

葛力飞和刘冰的论文定义了封闭世界假设-

封闭世界假设,其关注于在训练时所有测试类都已知的假设下设计精确的分类器。

大多数文本分类系统都是在封闭世界假设下运行的。对于许多应用程序来说,这种假设就足够了。如果我们想将电子邮件分为垃圾邮件或垃圾邮件,或者如果我们想将评论分为正面、中性或负面,封闭世界假设就足够了。

然而,对于聊天机器人来说,封闭世界的假设并不现实。一个更现实的场景是在测试期间预期看不见的类(开放世界)。因此,聊天机器人的目标不仅仅是将聊天文本分类为多种意图之一,还包括拒绝不属于任何已知类别的文本。

典型的基于 Softmax 的多类分类系统不是为检测域外类而设计的。而且,正如我们将在接下来的部分中看到的,由于置信度分数的错误校准,置信度阈值化不是检测域外类别的最佳方式。

神经网络的过度自信

基于神经网络的分类器在准确性上有所提高。但是精度的提高是以校准为代价的。神经网络的校准是输出置信度等于正确概率的概念。如果某个类的 Softmax 图层的输出置信度为 0.8,并且我们有 100 个独立的预测,每个预测的得分为 0.8,则我们预计其中 20 个预测是错误的。然而,现代深度神经网络(DNN)是错误校准的。如果在上面的例子中,分数来自 DNN,超过 20 的预测将是错误的。

郭川等人在一篇论文中实证性地衡量了这种相对于旧模式(古典的和不那么深刻的)的变化。在论文中,他们表明,在浅层网络(LeNet)中,网络的准确性接近平均置信度得分。然而,在更深的网络(ResNet)中,平均置信度得分比准确度高得多。作者实证证明,更深层的网络是 过度自信

图片来自郭川等人

虽然作者没有重复文本分类的实验,但我们可以说同样的现象是真实的。即使在文本分类器中,DNNs 的精度增益也是以误校准为代价的。

为了检测域外查询,我们可以使用一个高的类阈值。但是由于阈值是一个超参数,知道它的值的唯一方法是在虚拟代理发布后获得真实的数据。因此,价值只能从负面的用户反馈中获得。第二个问题是,在我们的实践经验中,我们注意到每个班级的“过度自信”程度是不同的。因此,在理想情况下,我们将需要一个独立的超参数,用于我们在虚拟代理中支持的每个类。

ReLU 网络产生高置信度的预测

与该问题相关的最后一个问题是基于 ReLU 的神经网络中的固有问题。Matthias Hein 等人在一篇数学密集的论文中讨论了这个问题。在该论文中,作者首先证明了基于 ReLU 的神经网络是连续分段仿射分类器。利用这一结果,他们随后证明,只要一些温和的条件为真,基于 ReLU 的网络将 总是 预测假阳性。论文进一步证明了在输入空间中,误报不一定接近训练样本。事实上,它们证明,如果您尝试,您将总能找到一个与该类的训练示例 相距很远的示例 ,该示例将为该类提供高置信度预测。

这最后一点证明了任何高阈值都不是排除域外类别的假阳性的充分条件。即使我们将阈值保持得足够高,也总会有远离训练样本的样本,这些样本将具有高置信度预测。

这个问题的解决方案

我们已经确定,在封闭世界的假设下,基于 ReLU 的 DNNs 会产生错误的分类。由于基于 ReLU 的 DNNs 固有的过度自信,置信分数的阈值不是预测域外类别的可靠方式。多类分类的典型方法如下所示。

我们使用了舒磊等人在论文中提出的方法。al 解决虚拟代理中的误分类问题。这种方法很适合我们,因为我们既不需要额外的训练数据,也不需要改变我们使用分类器的方式。这种方法的示意图如下—

在此方法中,我们使用多个 Sigmoid,而不是 Softmax,从而获得多个概率得分,每个得分来自每个 Sigmoid 单元。这种配置允许我们创建一个简单的域外检测器—如果没有一个 sigmoids 高于阈值,我们就将输入文本声明为域外。然而,如果多个 sigmoids 的输出高于阈值,我们选择激活最高的类。

该架构通过最小化所有类的累积负对数似然性来训练。该论文使用了一种类似于异常检测的方法来进一步微调预测。作者引入了可变阈值,而不是使用固定的 0.5(或替代值)作为阳性类别的阈值。外部参数α控制这个阈值。Alpha 是偏离平均值的标准偏差数,仍可视为正类。欲知详情,请参阅参考文件。

结论

聊天系统的用户可能没有意识到所设计系统的局限性。他们会有我们设计的聊天机器人无法解决的问题。在本文中,我们探讨了设计中的问题,并对聊天机器人进行了改进。在新的设计中,聊天机器人清楚地知道域外查询,并能优雅地响应这些查询。

开放式预训练转换器(OPT)是解决可访问性的一个里程碑

原文:https://towardsdatascience.com/open-pretrained-transformer-opt-is-a-milestone-for-addressing-accessibility-47e546a48a51

选择加入 GPT-3 人出局

图片来自皮克斯拜的格尔德·奥特曼

2022 年 5 月 3 日,Meta AI 公布了新的大型语言模型(LLM)开放式预训练转换器(OPT-175B)。在这篇文章中,我们将谈论 OPT 如何在机器学习领域,特别是自然语言处理(NLP)领域,建立了一个可重复性的基准。

再现性是怎么回事?

可访问性与再现性问题密切相关。如果你能获得有关该方法的信息,你可以复制这个实验。为什么再现性如此重要?让我们从更广阔的角度来看待这个问题,并回到过去。大约在 16 世纪,智人对他们获取知识的方式做出了重大改变。智人不再假设信息是正确的,而是开始使用科学的方法来确定假设,进行实验,分析结果,并得出结论。在过去的几个世纪里,科学家们利用这一过程来建立我们对自然世界及其规律的集体理解。通过关注科学发现的透明度和再现性,我们在技术上取得了巨大进步。(必须注意,定性方法不一定要产生可重复的结果。是的,定性的方法仍然很强)。

尽管可重复性是定量科学方法的一个基本考虑因素,但 2016 年《自然》杂志上的一项调查显示,超过 70%的研究人员在试图重现另一位研究人员的实验时失败了,超过 50%的人在重现自己的实验时失败了(Pineau 等人,2021;贝克,2016)。

这是一个严重的问题。评估研究主张的可信度是科学过程中一个核心的、持续的、费力的部分(Alipourfard et al .,2021)。如果一个科学发现是不可复制的,它就违反了科学方法的一个基本前提。Joelle Pineau 等人(2021)指出,机器学习研究的挑战之一是确保呈现和发布的结果是合理和可靠的。(注:Joelle Pineau 是脸书人工智能研究所的联合董事总经理,麦吉尔大学副教授。她在让巴勒斯坦被占领土变得可及方面发挥了作用。)

不幸的是,学术论文并不总是提供可重复的结果,比如缺少步骤或缺乏关于其方法的信息。作为一名数据科学家,我在阅读 ML 论文时也多次遇到可重复性问题。

GPT-3 和再现性问题

当我们谈论再现性问题时,我们有一只大象在房间里,GPT-3。在将近两年的时间里,OpenAI 对不公开该模型给出了粗略的解释。关于 GPT-3,OpenAI 曾表示“公开太危险了”Meta AI 显然认为安全不应该是一个让公众无法接触到的模型。在阅读了梅塔关于 OPT-175B 的博客文章后,我们可以看到,如果你做了严谨的功课,在负责任的同时公开一个 LLM 是可能的。

【Meta AI 对 OPT 的可访问性持什么态度?

  • Meta AI 团队已经注意使 OPT 模型可以公开访问。他们使用了负责任人工智能的指导方针。我知道脸书和负责任相处不好,但我们在这里。欢迎来到 2022 年!
  • OPT 团队与拥抱脸紧密合作。OPT 于 5 月 3 日公布。目前抱脸上有 6 款:125M、350M、1.3B、2.7B、6.7B、30B 参数截止 5 月 11 日。175B 参数可通过应用程序访问。 Stephen Roller ,OPT 论文的第二作者,正与拥抱脸团队合作,使各种各样的 OPT 模型易于使用。
  • OPT 团队(包括 OPT 论文作者)非常活跃,并对 Github 问题做出快速回复。
  • OPT 在公开可用的数据集上接受培训,以允许更多的社区参与了解这一基础新技术。

当前 OPT 的可访问性挑战

  • 根据官方指南,OPT 需要一个 A100 80GB 的 GPU。这对用户来说是一个巨大的可访问性障碍。
  • 目前,它只运行在 Azure 云服务上(基于官方指南)。在我的本地机器上安装 OPT 时,我看到 OPT 有 AWS 的基础设施。我相信我们会看到 OPT 与其他云计算平台的集成。
  • 各种安装问题。例如,它不适用于 Python 3.10.2,因为 Python 3.10.2 不支持所需的 torch 版本(1.10.2)。
  • Metaseq 是使用 OPT 的基本代码。不幸的是,斯蒂芬·罗拉说,梅塔塞克是出了名的不友好。**

OPT 是一个令人兴奋的大型语言模型。一旦它变得更加用户友好,它将是 NLP 领域的游戏规则改变者。在这篇文章中,我们想分享我们对 OPT 语言模型可访问性方面的第一印象。在 GPT-3 炒作和无法访问它之后,我们希望 OPT 将带来对大型语言模型开发的新理解。拥抱脸和变形金刚库集成完成后,我们将有机会尝试它,并在这里再次分享我们的经验!
编辑:5 月 12 日,各种 OPT 模型已经可以通过变形金刚图书馆访问)

埃尼斯·格克切——NLP 数据科学家

穆罕默德·埃姆雷·塞内尔 —博阿济奇大学计算机科学

感谢梅尔·梅德校对文章

参考文献:

阿里普法德,n .,阿伦特,b .,本杰明,D. M .,本克勒,n .,毕晓普,m .,伯斯坦,m,…和吴,J. (2021)。对公开研究和证据的系统化信心。

贝克,M. (2016)。1500 名科学家揭开再现性的盖子。自然,533(7604)。

皮诺,j .,文森特-拉马尔,p .,辛哈,k .,拉里维埃,v .,贝格尔齐默,a .,阿尔凯-Buc,f .,… &拉罗歇尔,H. (2021)。提高机器学习研究的再现性:来自 NeurIPS 2019 再现性计划的报告。机器学习研究杂志22

DagsHub 的开源 ML 项目:通过机器学习提高宠物认养率,第一

原文:https://towardsdatascience.com/open-source-ml-project-with-dagshub-improve-pet-adoption-with-machine-learning-1-e9403f8f7711

构建一个预测猫狗可爱程度的应用程序

照片由 米娅·安德森

项目介绍

令人难以置信的是,机器学习已经改变了生活的许多方面。它的应用远远超出了商业领域,并被用于许多公益事业,如保护环境、公共卫生、安全和动物福利。

一个例子是最近由马来西亚动物福利平台主办的 Kaggle 比赛。在这项挑战中,参与者被要求处理数千张带标签的宠物图片,并为每张图片生成可爱度评分。

这个可爱度,或者在比赛中被称为可爱度的分数,被用来匹配潜在家庭的宠物档案,以增加他们被收养的机会。

尽管挑战已经结束,我们仍将通过一系列文章尝试构建一个稳健的预测模型来完成这项任务。最后,我们将使用我们的解决方案创建一个 web 应用程序,这样任何人都可以给他们的宠物拍照并获得可爱度评分。

在本文中,您将探索:

  1. Petfinder Pawpularity 评分和数据介绍。
  2. 解决手头问题的方法
  3. 项目的所有开源工具列表以及它们解决了哪些挑战
  4. Petfinder 数据集的元数据和图像上的 EDA

您可以在 DagsHub 上开始这个存储库来跟随整篇文章。

https://ibexorigin.medium.com/membership

获得由强大的 AI-Alpha 信号选择和总结的最佳和最新的 ML 和 AI 论文:

https://alphasignal.ai/?referrer=Bex

解决问题的方法

如果你已经在考虑启动 Jupyter 实验室会议,请停止。在这个项目中,我们将尽可能远离笔记本电脑,并使用市场上最好的开源工具来使这个过程完全可重复。

首先,我们需要解决大文件的问题。Git/GitHub 不会削减它,因为他们在这个任务上是垃圾。我们需要更强大、更灵活的东西,比如 DVC。

数据版本控制(DVC) 是追踪大文件变化的最好的开源工具之一。正如 DVC 文档中所说,它是“数据的 Git”。

就像你在 GitHub 上托管代码一样,DVC 需要远程存储来存储你的数据和文件。你可以选择像 AWS、Azure 或 GCP 这样的云解决方案,但是设置会很麻烦,而且它们也不是免费的。

为了解决这个问题,我们将使用 DagsHub ,你可以把它想象成“数据科学家的 GitHub”。这是一个供数据专业人员托管和版本化他们的数据、模型、实验和代码的平台。

一旦您创建了您的帐户,DagsHub 将为您提供 10GB 的免费存储空间,仅适用于 DVC。

DagsHub 会自动生成一个远程存储链接以及为您创建的每个存储库设置该链接的命令。

存储库页面上的 DVC 指令示例。按作者。

所有添加到 DVC 的文件都将在您的 DagsHub repo 页面上进行标记:

作者截图

为了保持实验的可重复性,我们将使用另一个开源包— MLFlow

使用 MLFlow,您可以记录实验的每个细节——用于跟踪性能的指标、模型名称、其超参数、相应的分数等等。

像 DVC 一样,MLFlow 需要一个服务器来保存你实验的细节。DagsHub 也涵盖了这一点:

作者截图

设置 MLFlow remote 后,DagsHub 将在“实验”选项卡下记录您的每个实验:

DagsHub 上一个示例实验页面的屏幕截图。按作者。

您可以选择几个成功的实验,并通过按“比较”按钮开始比较它们,这将生成漂亮的表格和图表,以直观地探索这些实验。

最后,一旦我们有了问题的现成解决方案,我们需要使用 web 应用程序来公开它,以便每个人都可以使用友好的 UI 来尝试它,而不是克隆我们的存储库。

传统上,这是使用像 Flask 这样的 web 框架来完成的,但是我们将使用现在所有酷家伙都在使用的东西——Streamlit。

一旦我们完成这个项目,我们将在 Streamlit Cloud 上托管一个应用程序,它可以接受其他人的宠物的图像,并预测它们的可爱程度。

目前,Streamlit cloud 只接受 GitHub 存储库,这意味着我们的应用程序代码必须托管在那里。但这不就意味着我们的项目会被分割 GitHub 上的代码和我们 DagsHub 上的数据、模型、度量吗?

幸运的是,对于这种情况,可以用 DagsHub 连接 GitHub:

作者截图

每当你将更改推送到 GitHub,它将被同步到 DagsHub 镜像 repo,它也跟踪你的数据和实验:

作者图片

在本文中,我们将只讨论项目设置和对数据执行 EDA。让我们开始吧。

项目设置

继续向前,我将假设您已经创建了一个 DagsHub 帐户,并从 GitHub 连接了一个空的存储库。

如果是这样,您可以在 conda 环境中安装 DVC,并按照存储库页面上的说明设置遥控器。您可以调用以下命令来创建我们的项目树结构:

mkdir data data/raw data/prepared models notebooks

我还假设您熟悉 DVC 及其对数据进行添加、拉取和推送更改的常用命令。如果没有,你可以阅读这篇优秀的教程来帮你补上。

获取数据

你可以从 Kaggle 宠物搜索大赛页面下载完整的数据,但我已经把它上传到我的 repo 了。以下命令将 zip 文件夹提取到项目根目录:

dvc get https://dagshub.com/BexTuychiev/pet_pawpularity data/raw/petfinder-pawpularity-score.zip

将其解压缩,并将所有文件移动到data/raw目录。您可以稍后删除原始 zip 文件:

dvc get命令可以处理 DVC 跟踪的任何文件,甚至是其他人的存储库中的文件。

现在,让我们开始工作,探索数据。

数据集所有者完全允许我使用文章中的数据。

图像元数据的 EDA

Petfinder 数据集有大约 10k 个 pet 图像和每个图像的元数据。在本节中,我们将分析以 CSV 文件形式给出的元数据文件:

>>> test_df.head()

有 13 个特征,ID 是图像的文件名,其余的是与图像特征相关的二进制特征。最后一个“Pawpularity”列是目标。

如您所见,在训练集中有 9912 个观察值,而在测试集中只有 8 个。我们必须在文章的下一部分开发一个可靠的验证策略,因为测试图像非常少。

目标的 5 个数字总结告诉我们,可爱分数的范围从 1 到 100,平均值为 38,75%的图像的可爱分数为 46 或更低。为了更好地了解目标,我们来看一下直方图:

作者图片

我们可以看到大多数宠物的年龄在 20-40 岁之间。看到这么多可爱度分数最高的宠物也很意外。

由于特性都是二进制的,我们不会从中提取太多。因此,我们将查看按每个功能分组的目标分布。在开始绘图之前,让我们看看每一列代表什么:

截图自比赛资料页。作者。

我们将为每一列创建一对图—一个箱线图和一个直方图:

作者提供的图片

我省略了一些情节,因为它们都很相似,但是你可以查看GitHub 上的这个笔记本,它展示了完整的分析。

箱线图显示,即使按特征分组,目标也有相似的分布。直方图证实了这一点,表明这些元数据特征在构建预测模型时可能用处不大。

现在,让我们继续探索图像。

图像上的 EDA

首先,让我们创建一个简单的函数,它加载一个图像并使用 Matplotlib 绘制它:

我们将把这个函数与另一个函数结合起来,这样我们每次调用它时都可以查看宠物的随机图片:

正如我们前面看到的,元数据表在 Id 列中包含每个观察的文件名。我们将使用来自pathlib模块的 Path 类,用它来构建所有图像路径的列表:

来自 Petfinder 数据集的图像

您可以一遍又一遍地运行draw_random_image函数,从中获得一点乐趣。

同时,让我们看看图像的维度以及它们之间的区别。宽度与高度的散点图应给出一个粗略的概念:

我们可以看到图像尺寸变化很大。在构建模型之前,我们需要在下面的文章中解决这个问题。

现在,让我们来看看不同可爱程度的宠物照片,看看我们是否能找出一些模式来解释它们为什么有其他得分。为了实现这一点,我们将创建一个函数,在提供的 pawpularity 级别绘制 n 个宠物的随机图像。这是:

让我们画出从 20 到 100 的五个不同等级:

for level in range(20, 101, 20):
    show_pawpularity_level(train_df, pawpularity=level)

来自 Petfinder 数据集的图像

运行这个函数几次,发现很难辨别为什么 100 级宠物比低级宠物更受欢迎。

作为最后一步,让我们测试我们在最后一节中的观点,看看仅将模型与元数据相匹配是否会产生任何显著的结果。

拟合基线模型

由于目标包含可爱分数,因此默认情况下这是一个回归任务。但是,我们总是可以把它看作一个 100 类的多类问题,并适合一个分类器。我们将两者都尝试,从使用随机森林的回归开始:

我们不是过度配合,而是分数太可怕了。每幅图像的平均误差幅度约为 20%。让我们试试分类器:

分类器更令人失望。

我们将在下面的文章中使用图像和深度学习技术来提高这个分数。

结论

本文仅作为项目概述。我们已经了解了机器学习项目面临的挑战,以及正在使用哪些工具来解决这些挑战。我们还初步研究了数据,探索了元数据文件和图像。

在下一篇文章中,我们将继续发现 DVC DagsHub 的更多特征,并了解 MLFlow 以跟踪我们对图像数据的实验。当我们在 DagsHub 中结合所有这些工具时,您将开始欣赏机器学习生命周期变得多么容易。感谢您的阅读!

您可以使用下面的链接成为高级媒体会员,并访问我的所有故事和数以千计的其他故事:

https://ibexorigin.medium.com/membership

或者订阅我的邮件列表:

https://ibexorigin.medium.com/subscribe

开源与否?战略家的视角

原文:https://towardsdatascience.com/open-source-or-not-the-strategists-perspective-2a15053d5845

受最新最佳商业战略实践启发的决策框架

Viktor Forgacs 在 Unsplash 上拍摄的照片

不要为了开源而开源

在数据部门的咖啡机旁,我们经常大声梦想发布一个开源包。对我们数据科学家来说,发布开源似乎是一个圣杯。我们将成为新的 scikit-learn。我们会被邀请在全球数据峰会上发言。但是在讨论的某个时候,有人最终会问:

是的,但是我们如何向管理层推销呢?

通常,这将结束这里的讨论,因为没有人有任何线索如何证明投资。在这篇文章中,我提出了一个借鉴自商业战略世界的框架。我希望它能帮助你理解什么时候开源对公司来说是一个伟大的举动,并把这个想法推销给赞助商。

软件质量不是一个充分的论据

工程师们用来证明开源合理性的基本原理是,与封闭的开发方法相比,开放地交流思想会产生更好的产品。公开你的源代码会激励你写出更好的软件,因为你不想被认为是代码猴子。它还减少了安全漏洞和错误,因为每天有更多的工程师关注代码。

然而,这并不是一个充分的理由。我们有很多私营一流工程公司的例子。以苹果为例。他们几乎不开源任何东西,但在他们竞争的每个类别中都是世界级的。那么,你如何知道开源是正确的策略呢?

开源决策的四个因素

我结合了来自探索战略 [1]的商业最佳实践和从软件行业领导者那里学到的经验教训,提出了以下决策框架。它由四个因素组成,这四个因素将推动您的开源战略。

  • 竞争的激烈程度:在高度竞争的行业中,公司倾向于窃取创新。在这种竞争行为可以预见的地方,封闭式创新更好。以数字日历市场为例。每年都有大量的应用程序问世,每次都将用户体验提升到一个新的水平。一个将增加一个自动“建议可用的时间段功能”,另一个将增加一个“自动保护聚焦时间”的功能。新进入者和历史玩家的竞争如此激烈,开源你的特性会搬起石头砸自己的脚。问问你自己:“我的竞争对手抄袭的可能性有多大?
  • 先发优势的存在:在赢者通吃的市场中,技术是产品成功的核心,封闭创新更具战略意义,因为它能让你加深与竞争对手的差距,增强你的优势。以 LinkedIn 为例,它是一个具有强大网络效应的平台。他们非常清楚,世界上只有一个 LinkedIn 的空间。在《闪电战》[2]中,雷德·霍夫曼讲述了他们如何烧掉数百万现金来占领市场。在这个阶段,开源他们最关键的产品会减少他们宝贵的竞争优势。在创新更加持续的地方,开源并与社区一起慢慢构建技术是有意义的。小心:先发优势往往被高估。苹果从来不是第一个发布智能手机、MP3 播放器和平板电脑技术的公司。问问你自己:"我能在竞争中持久领先吗?
  • 紧密联系的创新:在产品由复杂和紧密联系的系统组成的行业中,开源迫使你标准化每个组件,并在它们之间的无缝集成上妥协。这也是苹果开发大部分私有软件的原因。他们想要紧密集成和无缝体验。例如,想想当你接到电话时,你的 AirPods 如何自动从 MacBook 切换到 iPhone。用户体验有时会推动封闭创新。问问自己:“客户体验对我的业务有多重要?
  • 工程能力:获取前沿工程能力对你来说有多重要?开源提高了你的工程团队的标准,并激励他们以最佳实践为基准。这是从内部吸引和发展你的技能的强有力的方法。问问自己:“我内部需要什么水平的工程技能?

外卖食品

在开源任何东西之前,像战略家一样思考。如果你只考虑产品质量的好处,你就错过了战略的一个基本方面:你的竞争。我提出的四个关键问题允许您将开源视为竞争优势的驱动因素:

  • 你在给你的竞争对手做礼物吗?
  • 你能保护你的创新以保持领先吗?
  • 你需要卓越的客户体验吗?
  • 你需要世界级的工程师吗?

通过问这些问题,你将涵盖开源决策框架的四个基本因素。你会相信你的开源战略与你的公司战略紧密结合,所以推销这个想法会很容易。

在这个问题上,你有其他的观点或经验吗?请在评论区告诉我。

[1]惠廷顿,安格温,雷格纳,约翰逊和斯科尔斯,探索战略 (2019),皮尔森

[2]雷德·霍夫曼和克里斯·叶,闪电战 (2018),货币。

OpenAI 正在打开 DALL E 2

原文:https://towardsdatascience.com/openai-is-opening-dall-e-2-5af7e91a2b

这里有一份翔实的分析,让你从中获得最大收益。

鸣谢:作者 via midway

期待已久的消息来了。

OpenAI 宣布他们将开放 DALL E 2 测试版。几周后,等候名单上的每个人都将有机会接触到这个模型。三个半月以来,OpenAI 一直将该系统保持在研究模式,以评估其潜在的危害。但是,正如山姆·奥特曼在 4 月 6 日所说的,他们想在夏天推出一款产品。等待现在结束了。

让我们看看 DALL E 2 测试版将如何工作,它将花费你多少钱,你可以和不可以对你的创作做什么,以及测试版以外的直接后果是什么——通过几个链接来帮助你导航人工智能驱动的创造力的世界。

DALL E 2 公测版

如果你是新来的,对 DALL E 2 一无所知,我写了一篇深入的非技术性评论,涵盖了它如何工作,它能做什么,以及它固有的(技术和社会)问题。我还建议查看一下 DALL E 2 的官方 Instagram 、subreddit r/dalle2 和 Twitter 标签#dalle,以了解这项技术有多神奇。

如果你不想读那么多,你需要知道的基本想法是,DALL E 2 允许你从文字中创建图像。你输入一个句子(提示), DALL E 2 输出一组与你所用单词相关联的原始图像。正常模式(文本→图像)每个提示给你 4 个图像。DALL E 2 还可以对生成或上传的图像进行编辑和修改(文本+图像→图像)。这些模式给你每个提示 3 个图像。

现在,让我们看看你玩最先进的 AI 视觉生成器要花多少钱。

定价

OpenAI 建立了使用 DALL E 2 的信用体系。一个学分=一个生成/编辑/变化。这意味着一个点数可以给你 4 或 3 张图片,这取决于模式。

每个帐户在第一个月获得 50 个免费积分,在随后的几个月获得 15 个免费积分。如果你想要更多的学分,你可以花 15 美元(0.13 美元/学分)购买 115 学分的套餐。这就是 DALL E 2 商业模式的秘密。我们来了解一下原因。

如果你没有尝试过 DALL E 2(或者任何其他人工智能艺术生成器),我现在可以告诉你,15 个学分——也就是 15 个提示——是一个非常低的数字。

让我们看一个例子。我决定使用 Midjourney (DALL E 2 的堂兄)为我之前的文章关于算法桥的制作封面图片:

鸣谢:作者 via midway

这不是一个完美的结果(这是显而易见的),它仍然花了我大约半个小时和多次试错的尝试。我尝试了三四个提示,然后决定用“一个有眼睛的打字机,黑白的,以一种象征性和有意义的风格,艺术站,”—ar 16:9)。对于每一个提示,我都做了一些改变,并几次放大图片以获得更好的结果。

总共约有 20 项请求。为了用 DALL E 2 得到类似的图像,我可能需要用完我所有的免费月配额。

那是因为我厌倦了尝试。数字艺术家可以花一整天的时间来尝试提示。他们可以轻易地在一张图片上花掉一年的积分。我没有夸大其词,他们可能是非常完美主义的——一旦你把你的手放在 DALL E 2 上,你也可能是。

为了克服这个明显的限制,OpenAI 提供了 15 美元 115 个信用点的套餐。保守估计——假设大多数人不是优秀的提示者——我会说 115 个积分可以变成 5-10 张像样的图片。

这是理解 OpenAI 想要实现的支付模式的含义的关键。为了更好地估计费用,我们应该考虑每个“好结果”的费用,而不是每次尝试的费用。10 个好结果 15 美元——如果你真的很熟练,15 美元——是相当昂贵的。

两个细微差别。

首先,OpenAI 在声明的最后说,他们将为“合格的艺术家”提供补贴。也就是说,那些依靠 DALL E 2 工作的艺术家(与像我这样计划偶尔使用它的人相反)和“需要经济援助”的人可以使用这个系统,而不用支付那么多钱。

我发现这个选项对于那些认为 DALL E 2 可能会以这样或那样的方式影响他们的工作的人来说是非常合理的(要么是因为它具有威胁性,要么是因为它是激发灵感或增强能力的关键工具)。

如果你认为你符合要求,你可以填写这张表格

其次,更重要的是,OpenAI 表示,“随着我们了解更多并收集用户反馈,我们计划探索其他与用户创作过程相一致的选项。"

这意味着,如果他们收到要求更改的反馈,他们可能会修改定价系统。我想到的两个选择是按提示付费和订阅模式。第一种情况类似于他们对 GPT-3 的使用。你为你生成的每张图片付费(可能是 0.05-0.10 美元)。对于那些打算玩 DALL E 2 看看有什么大惊小怪的临时用户来说,这很有趣。

订阅模式对于那些打算经常使用这项服务的人来说是有意义的。不想在做实验时感到压力的人。如果你担心花太多钱,创造力不会繁荣。

订阅模式肯定会帮助那些最有能力向公司提供最有用的反馈的用户。那些可能没有资格获得补贴的人,最终会分期偿还预付款。

但我认为 OpenAI 不考虑这种商业模式是有原因的——至少现在是这样。这是与 GPU 使用最不相关的,GPU 使用构成了公司的大部分成本。

无论如何,随时给他们反馈,你可能会看到它朝着更适合你的需求的商业模式转变。

安全

理解这一点是至关重要的,因为不这样做是最好的方式让你的帐户被禁止,你对 DALL E 2 的访问被取消,也许是永远,这取决于侵权行为。

OpenAI 的研究人员一直在努力使 DALL E 2 适应当前对什么使人工智能模型安全的普遍理解。首先,他们用一个红队来评估它的局限性和潜在危害。然后,一旦他们打开了研究测试版,他们就慢慢地小批量开放,以收集反馈并检查他们可能忽略的问题。

现在,随着测试版的开放,他们建立了三个主要的安全政策指导方针。

  • 遏制滥用。他们不允许上传人脸,生成名人的脸,或者“真实个人的照片般逼真的脸”。“这意味着你不能上传自拍,也不能要求 DALL E 2 生成特朗普做一些荒谬事情的 pic。
  • 防止有害图像。用户不能生成符合 OpenAI 的内容政策所定义的禁止类别之一的图像(例如,仇恨、性、暴力等)。)他们已经实现了内容过滤器,并从 DALL E 2 的训练集中减少了这类数据的数量。
  • 减少偏差。现在,DALL E 2“更准确地反映了世界人口的多样性”,利用了一项新技术。通过这种方式,该公司希望避免出现这样的情况,例如,提示“CEO”时,只给你穿西装的白人/亚洲人的照片。

超越贝塔

OpenAI 开放 DALL E 2 测试版是很多改变的开始,这些改变将影响到社会的各个角落。主要原因是他们决定授予创作者对生成图像的完全所有权。以下是最紧迫后果的简要概述。

商业目的

open ai——与我最初的想法相反,我必须承认——将允许用户出于商业目的利用 DALL E 2。

从公告来看:

“从今天开始,用户获得完全的使用权来商业化他们用 DALL E 创建的图像,包括重印、销售和商品的权利。”

这是最重要的消息。

让我用我的例子来说明为什么。当我开始写作时,我意识到好的封面图片对于一篇好的文章是至关重要的。我开始使用像 Unsplash 和 Pexels 这样的免费图片库,但很快发现它们能提供给我的非常有限。我决定在 Shutterstock 上购买年度订阅。我已经使用这项服务一年了,它给了我一些我发现的最好的封面图片。

一旦我可以用 DALL E 2 写文章(就像 Casey Newton 一直在做的那样),我就再也不会订阅股票图片库了。每月 15 美元,我可以很容易地创建 10 张完全符合我想要的图像,而任何好的库存图像公司都会收取我每月 30 美元以上的 10 张图像,我不仅要找到这些图像,而且在精确度和创造力方面根本无法与我用 DALL E 2 获得的图像相竞争。

股票图像服务已经死了。

但后果不止于此。

平面设计师的末日?

不久前,获奖导演 Karen X. Cheng 用 DALL E 2 为 Cosmopolitan 杂志做封面。这是人工智能第一次被用于这种工作,但不会是最后一次。这是一项实验,但一旦这些人工智能视觉生成器足够好,能够忠实地描绘人类(手有所有的手指,眼睛看着正确的方向),即使是人类平面设计师的最大承包商——如杂志——也将使用人工智能。

但是她不认为 DALL E 2 会“取代人类”,正如她在 Twitter 帖子中解释的那样。“在获得完美的图像之前,我们进行了数百次尝试……数小时的即时生成和提炼。”许多人参与了《Cosmopolitan》封面的创作,但一旦这些系统得到完善,一个人就能取代整个设计师团队——并将更快、更高效地创作出更好的艺术作品。

人类仍将处于循环中,这是肯定的。但是,与 DALL E 2 之前的时代相比,有多少人会留下来,这是一个不同的问题。

快速工程

我想谈的最后一点是关于人类和人工智能之间的交流。

自从 GPT-3 以来,人们意识到你与人工智能系统交流的方式对你得到的回报质量很重要。你不应该认为这些系统是占卜师。他们读不出你的想法。他们擅长在一点帮助下创造新事物,但这种帮助是至关重要的。这是你的责任。你必须学会如何充分利用它们,否则,它们可能会令人失望。

这就是为什么研究人员想出了快速工程这个术语。这反映了学习如何与这些人工智能沟通是一项技能。人们可以日复一日地玩 GPT 3 或 DALL E 2,并意识到他们没有得到更好的结果,因为他们从未学会正确的技术。其他人可能会发现,即使过了几个月,他们仍然在不断改进单词和概念,并获得越来越高质量的输出。

这很重要,原因之一是。

意识到这些技术的到来的数字艺术家、设计师和插画师可以——也应该——走在潮流的前面,发展提示技能以保持相关性。大多数人最多还是这些系统的临时用户。具有优秀提示技能的人将能够从 DALL E 2 类人工智能中获得最佳效果,而大多数人将需要再次依赖它们来获得像样的艺术作品。

这是反对人工智能视觉生成器将取代艺术家或设计师工作的最有力的论据。是的,他们必须更新他们的技能。但是公司不会直接用 DALL E 2。他们会寻找知道如何使用它的人。具有卓越工程技能的人。

我很确定那些精通视觉创意和最新人工智能趋势的人是填补这些空缺的最佳人选。

让我强调这一点。不要被愚弄,不要认为仅仅因为你能使用 DALL E 2 你就能做艺术魔术。任何数字艺术家都可以确认,与 DALL E 2 交流本身就是一种艺术。

就像油画和数字绘画需要一套特殊的技能,用 DALL E 2 创作也一样。

获得这些技能的难度肯定会决定精通技术的艺术家将面临多大的竞争力。目前,他们有优势。

最终想法

OpenAI 的声明并不出人意料。我们知道这是迟早的事。这不是人工智能领域的开端,而是现有趋势的延续。

然而,这绝对是一个转折点。那些从来没有直接接触过人工智能的人将会通过 DALL E 2 的创作来接触。

它将到达不了解技术的世界的最遥远的角落,更多的人将开始意识到人工智能及其对世界和生活的影响。

还有什么比通过艺术、创造力和想象力更好的方式来实现这个目标呢?

订阅 算法桥 。弥合算法和人之间的鸿沟。关于与你生活相关的人工智能的时事通讯。

您也可以直接支持我在 Medium 上的工作,并通过使用我的推荐链接 这里 成为会员来获得无限制的访问权限! 😃

posted @ 2024-10-18 09:30  绝不原创的飞龙  阅读(318)  评论(0)    收藏  举报