TowardsDataScience-博客中文翻译-2021-六十三-

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

TowardsDataScience 博客中文翻译 2021(六十三)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

主位置编码:第一部分

原文:https://towardsdatascience.com/master-positional-encoding-part-i-63c05d90a0c3?source=collection_archive---------1-----------------------

实践教程

我们提出了驱动变压器的固定位置编码的“推导”,帮助您获得完整的直观理解。

谢栋豪在 Unsplash 上的照片

这是关于位置编码的两个帖子的第一部分(更新:第二部分现在可用这里! ) :

  • **第一部分:**固定正弦位置编码的直觉和“推导”。
  • **第二部分:**我们如何,以及应该如何将位置信息实际注入注意力模型(或任何其他可能需要位置嵌入的模型)。这主要关注相对编码。

据我所知,这里有一些关于位置编码被视为流形的独到见解。但是,世界很大,所以我怀疑我是第一个注意到这一点的人;如果有人知道好的链接,请告诉我!

第一部分:固定正弦位置编码

介绍

想象一下,你终于成功地理解了变形金刚。您已经绘制了那些将关键字映射到查询的花哨的彩色矩阵,并使自己确信了 logits 张量的含义。你甚至更进一步,弄清楚了多头注意力是如何工作的,钦佩一点张量整形所能提供的聪明和效率。您已经学习了如何通过直接修改 logits 来屏蔽输入序列,以及如何优雅地将序列从编码器传输到解码器。你对自己非常满意,扑通一声坐在沙发上,抬起脚,吃了一大块巧克力曲奇……不,是两块巧克力曲奇。你值得。

满足于新发现的知识,您最终转向嵌入层。“这很简单,”你暗自思忖,“自从《后街男孩》回归以来,我一直在将单词转化为向量。”当您将目光转向模型架构图的底部时,一些不熟悉的东西吸引了您的目光。位置编码层。你立刻停止吃你的饼干,面包屑掉在地板上,因为你突然记起来了。位置信息不会显示在任何地方。

不要惊慌,你继续往下读,希望能找到一个简单的解决方案。相反,你得到的只是几个句子,告诉你一些固定的正弦函数和方向,当提到通常的嵌入层“…两者可以相加。”疯狂地翻来翻去,你意识到没有别的了。以前上过一次物理课,你知道刚刚发生了什么。你已经被“它很繁琐”了。

位置编码基本上是注意力模型的“琐碎”部分。许多可怕的 2-3 个单词短语中的一个,如“没关系”或“没关系”,其意图绝对与你所说的相反。它的不是好,它的重要,位置编码是不是琐碎。

现在,我们都承认这种情况,让我们实际进入什么是位置编码。我们将采用物理学家的方法来“推导”答案。为如何完成我们的任务想出一些简单的理论/猜测,然后慢慢地改进它们,直到我们得到最能满足我们需要的编码。我们将把在哪里以及如何弹出位置编码层推迟到第二部分。现在,我们只是试图找出如何给一个序列一些位置意义。

离散位置编码

这其中最困难的部分可能就是试图描述我们到底要做什么。**如果这一小节没有太大意义,直接跳到下一小节就可以了。**理论上,我们有以下目标:

位置编码是序列中项目的位置或“位置”的有限维表示。给定某个序列 A = [a_0,…,a_],位置编码一定是某种类型的张量,我们可以把它提供给一个模型,告诉它某个值 a_i 在序列 A 中的位置。

诚然,这是含糊的;这是因为“供给模型”行也是模糊的。为了解决这个问题,让我们假设我们想要训练一个模型来完成以下任务:

给定一个长度为 T 的序列 A,输出另一个长度为 T 的序列 A ’,其中每个索引的输出是其邻居的加权和。

来源:现作者。底层是我们的输入编码向量。叠加的高斯图显示了我们输入的“模型”,产生了同样大小的输出向量。每个深蓝色方块是其浅色对应物的加权高斯平均值。

这种模式毫无意义。我重复一遍。这种模式毫无意义。这只是给我们一个例子的概念,你可以用一种方式,把位置编码输入到神经网络中。

我们正在寻找一个位置编码张量,而不是位置编码网络。这意味着我们的位置编码张量应该代表每个索引处的位置,我们的网络将负责对其进行任何操作。

这种编码将具有与序列长度相同的维数,序列长度可能很长。此外,序列长度甚至不是一个固定的量。将可变长度的向量输入模型是一个大问题。我们假设有一个最大值叫做 max_position ,对于这个最大值,我们的模型不能处理任何更长的序列。对于我们所有的解决方案,我们将编码固定为这么长,并根据输入的需要进行截断。

希望通过一个例子,一切都会变得清晰。

猜测 1:数数就好。

让我们从我们可能创建的最简单的位置编码开始。我们只是数数!我们映射每个元素 a_i → *i,*产生以下编码:

来源:现作者。序列长度= 8。我们简单地创建一个位置编码,其中每个条目都是它的索引号。

非常简单,我们刚刚创建了一个新的向量,其中每个条目都是它的索引号。这是绝对位置编码*。* 序列中的每个位置都由它相对于全局坐标原点的位置来标记,假定是当前序列的开始。我们将继续使用绝对编码,因为它们可以用来派生相对编码

现在我们来批判一下。我们最初的猜测是错误的:这些数字的规模是巨大的。如果我们有一个由 500 个记号组成的序列,我们的向量最终会是 500。一般来说,神经网络喜欢它们的权重在零附近徘徊,并且通常正负平衡。如果没有,你会让自己暴露在各种各样的问题面前,比如爆炸性的梯度和不稳定的训练。

猜测 2:正常化“只是数一数”的猜测

这似乎是一个显而易见的解决方案→只需将所有值除以最大的整数,这样所有的值都在[0,1]中。这将提供:

来源:现作者。我们通过除以 sequence_length(这里是 8)来规范化每个条目。

但是哦不!我们引入了另一个问题。我们不能再处理任意长度的序列。这是因为每个条目都除以序列长度。比如说 0.8 的位置编码值对于长度为 5 的序列的意义与对于长度为 20 的序列的意义完全不同。(对于长度 5,0.8=4/5 意味着它将是第 4 个元素。对于序列长度 20,0.8=16/20 意味着 0.8 代表第 16 个元素!).简单的标准化对可变序列长度无效

我们需要做的是找到一种不使用大于 1 的数字来计数的方法。这是一个很大的线索,你也许能猜出答案。

使用二进制数。

猜测三:数数就好!但是用二进制代替十进制

我们可以用二进制形式 100011 来表示第 35 个元素,而不是写 35。爽,一切小于一。但是,在兴奋之余,我们忘记了无论我们使用二进制还是十进制,数字 35 都是一样的;总是只有 35。我们没有获得任何东西…相反,我们实际上需要做两件事:1)将我们的整数转换为二进制,2)将我们的标量转换为向量。

哇,向量的标量?那是什么意思?这意味着我们的位置编码向量现在变成了位置编码矩阵。每个数字都有自己的二进制向量,而不仅仅是一个整数。这是一个相当大的概念转变,但是通过增加维度,我们既可以保留任意长的序列,同时也可以将数字限制在范围[0,1]内!我们的编码现在看起来像这样:

来源:现作者。位置编码的矩阵版本。每个二进制向量的条目代表一个二进制标量。例如,索引 5 保存二进制的 100,即值 4。

我们有一些自由。我们可以选择这个新的嵌入空间的维数,这个空间包含我们喜欢的任何二进制向量。这个维度唯一能做的就是告诉我们我们的序列可以有多大,准确的说是 2^dim。为了简单起见,让我们选择它等于 *d_model,*我们的模型将使用的嵌入维度。为了确保我们理解向量表示,第 35 个位置将被表示为二进制向量

35 ← → [0, …, 0, 1, 0, 0, 0, 1, 1]

分析时间。我们目前的猜测有哪些问题?

  1. 我们还没有完全正常化。记住,我们希望事情的正面和负面大致相等。这很容易解决:只需通过 f(x) = 2x-1 重新调整[0,1]-->[-1,1]即可。
  2. 我们的二元向量来自一个离散函数,而不是一个连续函数的离散化。

后一点很微妙,它与使用一个向量来表示一个标量位置有关。让我们比较两种编码,它们试图测量从 0 →3 的距离。第一个编码非常简单:只使用 x 轴维度,并将位置放在那里,用变量 x in [0,3]表示

连续编码:[x,0]

现在让我们将其与二进制矢量离散编码进行比较。由于这是离散的,我们只能表示 4 个位置:0、1、2 和 3。这些给出了 4 点:

二进制向量编码:[0,0],[0,1],[1,0],[1,1]

任何时候我们离散一些东西,我们倾向于相信这些东西可以被**插值,**意味着我们可以通过连接这些点来构造一个连续的函数。看看我们两种编码的两种插值:

来源:现作者。显示位置流的空间曲线。离散曲线(黑色)从(0,0)->(1,0)->(0,1)->(1,1)开始,比平滑连续曲线(蓝色)更加参差不齐。

你可以看到连续编码曲线是平滑的,很容易估计中间位置。对于二元向量来说…那东西很乱。这将导致我们的下一个提示:找到一种方法,使二进制向量成为连续事物的离散化。

因此,对于图片,我们想要一个功能,以一种看起来自然的平滑方式连接这些点。对于任何学过几何的人来说,我们真正做的是寻找一个嵌入流形。

是的,如果你使用位置编码,你可以听起来超级花哨,告诉人们你在使用流形。用人类的术语来说,这意味着我们在 d_model 嵌入空间中寻找一条曲线,当你沿着这条曲线行走时,你的“位置”会以连续的方式慢慢增加。做好这一点,我们就可以进行下一个猜测了。

猜测 4:使用一个连续的二进制向量

为了使我们的二元向量连续,我们需要一个函数来插值一个来回的循环 0 →1 →0 →1 →0…等等。你说周期?是时候引入触发函数了。让我们用一个正弦函数。此外,正弦函数存在于[-1,1]上,所以它也是正常的,这是一个额外的好处!

这是你现在应该有的形象/类比。位置编码矩阵中的每个矩阵条目代表一个类似于立体声系统的“刻度盘”。通常你的拨号盘可能会做一些类似于调节音乐音量的事情(音量类似于位置)。像普通的转盘一样,我们在编码矩阵中使用的隐喻转盘可以连续地从关(0)转到开(1)。为了获得精确的音量(位置),我们使用一系列不同灵敏度的刻度盘(矢量)。第一个拨号盘会稍微调节音量,也许是 1 个单位,一个几乎听不见的差别。第二个会更强大,调节音量 2 个单位。第三个将调整 4 个单位,第四个将调整 8 个单位,依此类推。

来源:现作者。序列(列)中的每个位置都由位置嵌入向量(行)表示,该向量可以被可视化为一组刻度盘的设置。表盘是描绘矩阵的元素。随着尺寸的增加,刻度盘变得更加灵敏,并且位置增加得更多。

通过这种方式,你可以在大范围内得到精确的音量*,因为拨号盘的强度呈指数增长。我们可以建造 8 个同样精确的小表盘(2⁸=256,如果你想知道为什么是 8 个表盘),而不是建造一个包含 512 个声音等级的大表盘。超级高效哈!*

符号警告:我们相对于上一节转置了位置编码矩阵。我们现在有 dim M = (sequence_length,d_model),这是通常的写法。

我们现在希望正弦函数在正确的时刻进行一次旋转。对于第一个条目,每当我们在序列中移动一步时,它应该从 0 →1 移动回来。这意味着第一次拨号需要π/2 的频率,第二次拨号需要π/4 的频率,第三次拨号需要π/8 的频率,等等…

我们现在有了第一个原型位置编码张量。它由一个用 M 表示的矩阵组成,其中矩阵的 y 轴是序列(0,1,…,n-1)中的离散位置 x_i ,x 轴是向量嵌入维数。矩阵 M 的元素由下式给出:

这里,ω是拨号频率,其相对于嵌入维度单调递减。x_i 是给出序列位置的整数。

现在让我们比较一下我们的连续离散化编码和传统的离散化编码。

来源:现作者。颜色表示该坐标代表的绝对位置。对于维度 1、2 和 3,频率分别为 omega=pi/2、pi/4、pi/8。

看,都是连续的和多向的。

分析时间。我们已经非常接近最终答案了。有两件事我们仍然想解决。

*首先,有一个不连续的问题。上面的图是我们想要的平滑曲线。然而,它也是一条封闭曲线。考虑从位置 n 移动到 *n+1。这是曲线中从黄色过渡到棕色的部分。通过构造,位置 n+1 相当于位置 1。这是必要的,使我们的曲线连续,但这也不是我们想要的! n+1 和 1 应该相隔很远,而不是靠在一起。对于学习过有限傅立叶变换的人来说,这个问题应该很熟悉。只有一半的频率是唯一的。

为了解决这个问题,我们观察到我们没有理由必须让我们的嵌入向量与一个二进制向量对齐。事实上,频率(omega_0,omega_1,…,omega_d_model)可以是任何东西,只要它们是单调增加的,这仅仅意味着它们增加,并且仅仅增加。我们可以降低所有的频率,这样我们就可以远离边界。

慢速拨号版本。omega_0 是最小频率。检查边缘情况:j=0 给出最大频率,j=d_model 给出最小频率。

原作者为此选择了 1/10,000 的最低频率。然后,他们通过 omega_min^{range(d)/d}指数增加频率,以达到单调性标准。为什么选择一万?我真的不知道,我猜试错法使这个作品最好。

第二个问题与其说是问题,不如说是缺点。没有简单的运算可以让我们在一个位置向量上加减 5 个位置单位。这种被称为转换的操作将允许我们将索引 i 的位置编码向量写成索引 j 的向量的函数。

为什么这是一个非常值得拥有的财产?想象一下,我们有一个网络正在试图翻译句子“我要吃饭了。”“is/am/are”+“去”+“动词”组合是一种很常见的语法结构,有固定的位置结构。“going”总是在索引 1 处结束,“to”在索引 2 处结束,依此类推。在这种情况下,在翻译“动词”时,我们可能希望网络学会注意出现在“我将要”之前的名词“我”。“我”位于“动词”左边 4 个位置单位。由于我们的注意力层使用线性转换来形成键、查询和值,所以如果我们有位置编码就好了,这样线性转换可以将位置向量向左平移 4 个单位,从而与“动词”的位置向量对齐。然后,查询和关键字将完美匹配。

总之,我们想看看是否可以修改我们的编码,以便它可以通过线性变换进行翻译。

最后一个猜测,第五:使用正弦和余弦来简化翻译。

数学看起来有点复杂,但并不复杂。我们只需要记住符号。首先,让我们总结一下当前的位置编码矩阵

行向量 v 是在单个位置 x 评估的正弦向量,但是具有变化的频率

*每个行向量表示单个离散位置的位置编码向量。位置编码矩阵 **PE,*是这些向量中的向量,因此它是(seq_len,n)维矩阵(seq_len 是序列长度)。我们现在想要找到一个线性变换 T(dx) 使得下面的等式成立:

我们将如何做到这一点?首先,请注意,由于 PE 的所有元素都是正弦,因此位置 x 实际上是角度*。从三角学中,我们知道任何移动触发函数的自变量的运算 T 一定是某种旋转。旋转可以通过对(余弦,正弦)对应用线性变换来应用。使用旋转矩阵的标准结果,我们可以利用下面的恒等式:*

建立一个循环。

注意,这个公式让线性运算符从左边起作用。在我们的网络中,线性变换通常从右边应用,正如我们到目前为止所展示的。从概念上来说,做左边的运算更容易,所以我们将把答案写在这里,但请记住,您需要对 T 进行转置,以将其应用于我们当前的 PE 矩阵。

为了构建一个支持通过线性操作符进行翻译的编码,我们做了以下工作。使用余弦而不是正弦创建重复的编码矩阵。现在,构建一个新的位置编码矩阵,如下所示:在余弦和正弦矩阵之间交替,弹出第一列并将其附加到最终的 PE 矩阵。这相当于用[cos(…) sin(…)]对替换每个 sin(…)。在我们新的 PE 矩阵中,行向量如下所示:

我们现在通过使用一组块对角线性变换来构建完整的线性变换。每个块将具有不同的矩阵,因为块作用的频率是不同的。例如,为了将频率为ω_ k 的第 k 个刻度盘平移 dx 个单位,我们将需要δ=ω_ k * dx 的总角度偏移。 T 矩阵现在可以写成:

*如果取这个的转置,可以直接插入到我们之前的 PE(x+dx)=PE(x)T 方程中,从而通过构造证明平移矩阵的存在!

这几乎囊括了一切。现在让我们看看我们的 PE 矩阵实际上是什么样子。

来源:现作者。

下面是如何解读上面的 PE 矩阵热图。

  1. ***大部分 PE 矩阵不需要进行编码。*图中较暗的部分表示表盘已经“打开”,这意味着该区域的正弦和余弦值分别远离初始值 0 和 1。这表示有多少嵌入空间用于存储位置信息。正如你看到的黑色曲线一样,沿着深度激活一个刻度盘变得更加困难(黑色曲线看起来像 e^x).
  2. 较大深度的垂直线变化小于较低深度的损失。**选择一个固定的深度,然后向上移动,注意颜色如何变浅→变暗→变亮……这个周期的频率随着深度的增加而降低,这表明我们之前的直觉,深度越大的刻度盘越灵敏。

最终答案。不再猜测

至此,我们已经完成了所有需要做的事情,让我们总结一下:

  1. 位置编码由矩阵表示。对于序列长度 T 和模型深度 D,这将是一个(T,D)张量。
  2. “位置”只是序列中的索引。
  3. PE 矩阵的每一行都是表示与行索引相关联的离散值的插值位置的向量
  4. 行向量是正弦和余弦的交替序列,其频率按照几何级数递减
  5. 存在一个矩阵乘法,可以移动我们想要的任何行向量的位置。

这里有两种创建位置编码矩阵的方法:一种在 numpy 中,另一种只使用张量流操作符。因为位置编码矩阵可以与使用它的层同时初始化,所以这个公式没有多大用处。但是和张量一起工作是很好的练习。

理解代码的几个注意事项。首先,我们利用 numpy 广播通过位置和频率的外积来创建 PE 矩阵角度。第二,我们使用下标将正弦和余弦直接应用于角度矩阵的切片,这是一个很好的技巧。

额外财产

还有一个我们希望位置编码满足的附加属性,它与位置编码在实际注意力模型中的使用方式有关。注意力是基于这样一个事实,即与序列中每个位置相关的每个嵌入都有一些代表性的关键字或查询。通过执行点积 query_i@key_j,这些键和查询允许我们快速确定当前位置是否应该“参加”另一个位置。如果点积很大,则意味着在 j 的键与在 i 的查询相匹配。为了使用位置编码向量,我们希望彼此靠近的位置返回大的键查询点积,而远离的位置返回小的键查询点积。请看下图:

来源:现作者。我们显示了参数 d_embed=128,max_position=256 的 PE 矩阵的向量 v^{(128)}与所有其他位置向量的点积。点积由最大值归一化。

该图示出了表示第 128 个位置的特定位置编码矢量与每隔一个位置编码矢量之间的点积。请注意,在没有事先计划的情况下,我们得到的正是我们想要的。当 v^{(128)}与其自身比较时,点积是最大的,并且随着我们远离而稳步减小。一个不错的额外功能!

其原因可以追溯到正弦和余弦的正交性。随着我们越来越远,位置编码向量之间的频率不再相关,并且已知随机正弦和余弦之和趋于零。

结论

谢谢你坚持到最后。像学术界的所有事情一样,解释得最少的事情往往是最难的。很讽刺,但是很好。

我们了解到,位置编码是一种将序列中对象的位置转换为神经网络(或其他模型)可以理解和使用的信息的方法。首先尝试一种简单的方法,我们只给出与位置相对应的模型整数,我们知道这很糟糕,原因有很多:数字可能太大,没有明显的方法来插入这些信息,它不能很好地扩展到不同长度的序列。

接下来我们做了一系列的猜测,就像物理学家试图提出一个可行的理论一样。通过不断调整和改变我们的猜测,以纳入更多的期望特征,我们最终降落在正弦位置编码矩阵。矩阵使用向量来表示序列位置。它可以适当缩放,允许翻译,并且容易处理不同的序列长度。

实现背后的细节和直觉将(希望)在以后的文章中探讨。例如,在那篇文章中,我们提出了类似于“为什么我们只是在常规序列嵌入中添加位置编码?”在这里,似乎一个体面的未经证实的答案是,PE 矩阵稀疏。只使用了很小一部分,所以看起来这个模型更像是把加法看成一个连接,把嵌入的一部分留给位置,其余的留给标记。

参考

  1. 你所需要的只是关注。 arXiv 预印本 arXiv:1706.03762 (2017)。

主位置编码:第二部分

原文:https://towardsdatascience.com/master-positional-encoding-part-ii-1cfc4d3e7375?source=collection_archive---------4-----------------------

我们升级到相对位置,提出一个双向相对编码,并讨论让模型为你学习这一切的利弊

肖恩·斯特拉顿在 Unsplash 上拍摄的照片

这是两部分系列“主位置编码”的第二部分如果你想知道更多关于位置编码的直觉和基础知识,请看我的第一篇文章。

第一篇文章讨论了固定正弦绝对位置编码的含义,而本文将重点讨论相对位置编码。我们也将讨论使用可学习的位置编码的利与弊。在这个过程中,我给出了一个基于 Transformer-XL 架构实现双向相对位置编码的建议。我还没有找到任何人讨论这一点,所以请插话,如果你能透露一些关于是否有人追求这一点。

如果有一个 TL。这篇文章的要点是:

您应该将位置编码信息直接放入 logits 中。

这是提纲。随意跳到你感兴趣的内容!:

第一节(导言):

  • 为什么要研究相对编码?
  • 模型如何考虑相对编码?

第二部分(计算材料):

  • 给定一个绝对位置编码,如何建立一个相对位置编码。
  • 构造相对位置编码的 O(n)算法。
  • 上述算法的代码,以及如何双向化它。

第三部分(可学习的编码):

  • 为什么应该将位置信息直接注入 logits。
  • 可学习编码和固定编码的优缺点。
  • 逻辑的嵌入位置相关分解。

第一节(导言):

日常语言中的位置

快速测验。您将如何回答以下问题:

最近的银行在哪里?

  1. 剧院旁边。
  2. 在第一大街和主街的拐角处。
  3. 在纬度:41.559599,经度:2.431680。

解决方案:所有选项都是正确的!这是因为在现实中,某物在哪里的精确答案并不是一个定义明确的问题。你不可能用三个词足够精确地回答这个问题,把地球上的每个人都带到完全相同的位置。然而,取决于你在和谁说话(室友、同事、新闻主播),他们可能真的找到了银行。

上下文位置与可计算位置

有两个主要原因可以解释为什么“剧院旁边”是一个可以接受的回答。首先,

《剧院旁边》描述了一个的相对位置

如果我们知道剧院的位置,我们可以通过“+”剧院旁边的计算出来到达一个新的位置。当然,这似乎是合理的,但是我们怎么知道剧院的位置呢?我们刚才不是说定位不明确吗?正确。这就引出了我们的第二点:

每一个回应都携带着背景信息,包括对话中双方的当前位置。

当我回答“剧院旁边”时,暗示提问的人知道我在哪里。这是上下文信息。反过来,提问者也有自己的上下文信息。在日常语言中,我们默认计算位置相对于我们自己的位置。这赋予位置一个明确的含义:位置总是相对的。

既然我们试图建造理解人类逻辑的机器,我们就必须以某种方式向它们灌输对位置的理解。让我们解决以下问题:

我们给一个模型一些段落,并要求它计算银行的位置。假设我们给它一句话:

“这里的银行不远就是旁边的那个剧场****

如果你只保留粗体字,你听起来会像一个穴居人,但你可能会找出银行在哪里。一台机器不需要符合社会期望,所以它可以自由地说穴居人。我们可以想象机器执行以下计算:

  1. 在哈希表中查找Location["here"]=0。记住,人们使用相对位置编码,所以机器已经知道“这里”的位置是零。
  2. 在散列表中查找操作Operation["next to"]=lambda x: x+1,它只是将距离 1 添加到任意位置。
  3. 在哈希表中查找Location["next to"]=street_pos
  4. 计算Location["bank"] = Operation["next to"](Location["theater"]["here"])

第四步是关键部分。其他的都不难学,。它们只是哈希表值。事实上,你可以把这些硬编码到一个模型中,然后就到此为止。然而,第 4 步需要逻辑,这是很难硬编码的。在进入这个问题之前,让我们用前面粗体定义的术语总结一下我们所看到的。

第一句——“银行离这里不远”——是上下文信息。在Location["theater"]哈希表中**“**剧院”的所有可能值中,它告诉我们具体使用位于“这里”的那个。

句子 2——“它(银行)在剧院旁边”——是提问者听到的实际回答。它包含将“银行”的位置与“剧院”的位置相关联的相对位置信息

如果这是一个递归神经网络(RNN),我们可以认为初始隐藏状态提供了上下文信息,而实际的 RNN 计算相对位置信息。这就是为什么有状态模型更擅长提取长期依赖关系。

从抽象位置到序列中的实际位置

到目前为止,我们一直在用分配给抽象事物的数字来讨论位置,比如银行、剧院或你自己。这有助于让我们明白,在生活中,我们只关心相对位置。

我们现在将根据序列中的位置来讨论位置。在我们思维实验的第 4 步中,我们的模型必须弄清楚如何将一堆数字放在一起计算位置[“银行”]。语法可以被认为是一组(通常是不完整和不一致的)规则,这些规则被设计成获取一串单词,并根据它们在序列中的位置赋予它们一个含义。例如,以这个句子为例:

“我活着就是为了吃饭。”

这表达了说话者对食物的喜爱。如果他们不能吃东西,他们就会死(我想这不仅仅是夸张的事实,也是字面上的……)。语法告诉我们,动词“吃”是生活的目的。所以,你可以想象一个学习“当你看到结构‘动词’+‘to’时,位置索引[‘动词’]+2 处的词描述了动词的目的”的模型。

的确,让我们把这两个动词调换一下来写:

“我吃饭是为了活着。”

我们现在得到了相反的意思!说话者暗示他/她不喜欢食物,只是为了继续生活而吃东西。但是,这还是符合我们学过的语法模型!index['吃']+2 = index['活']还是管用的,活在这里就是吃的目的。

这个模特扮演了人类。它获取自己的位置(index['eat'])并将其视为“绝对零度”位置。然后它说,“嘿,我只关心在我前面两步的东西”,在这种情况下是 index[‘live’]。该模型只需要相对位置进行计算。

在我们之前的文章中,我们花了很多时间来创建绝对位置编码。然而,正如我们刚刚讨论的,我们真的只需要相对位置。使用绝对位置来描述事物总是可能的,正如我们的纬度和经度响应所示,但通常这比我们需要的信息更多,并且在内部我们可能只是找到一种方法来将其转换为相对的东西。

绝对位置编码开始的原因是,它们可以非常容易地转换成相对位置编码。现在让我们进入一些相关的数学问题。

第二部分(计算材料):

相对化你的绝对编码:一个简单的入门例子

让我们从一个简单的例子开始,感受一下正在发生的事情。考虑具有以下绝对位置编码向量(字母 A 代表绝对)的 1D 序列

如果我问你任意两点 A_iA_j 之间的位置是什么,用距离函数 d(A_i,A_j) 表示,答案应该很明显: d(A_i,A_j) = |A_i-A_j| = |i-j|。我们的挑战不是计算的相对位置,而是表示我们计算的相对位置。既然有 n 种可能的位置,那么就有 n 种可能的位置配对 A_iA_j 。这表明我们的相对编码应该是一个(n,n)矩阵。用矩阵 R 表示相对编码(R 表示相对)。然后,我们可以通过根据公式填写 R 的条目来说明所有可能的相对位置

记住,当一个字母不是粗体时,它只代表一个标量值,所以这是 n 个条目中每一个的等式。实际的矩阵现在看起来像这样

请注意对角线上的零,这表示索引相对于自身的位置为零。这个特殊的矩阵就是所谓的循环矩阵,它是一类更一般的矩阵托普利兹矩阵的特例。如果我们放松对称条件 A _i-A_j=A_j-A_i ,下对角线将是负的,因此矩阵将只有托普利兹矩阵。当每条对角线都不变时,你会得到一个托普利兹矩阵。主对角线全是 0,下一条全是 1,下一条全是 2,依此类推。得到一个 Toeplitz 矩阵是非常可取的,因为它将大大简化计算。

我们注意到在整个(n,n)矩阵中,只有 n 个唯一值。这些是(0,1,…,n-1)。因此,矩阵表示是高度冗余的。我们还注意到,这些唯一值完全由绝对编码向量 A 的条目表示,加上值 0。因此,我们假设我们应该能够仅通过操作矩阵 A 来确定 R ,这表明我们可以在 O(n)计算中形成 R 而不是 O(n)。这都取决于我们的矩阵 R 确实是托普利兹矩阵这一事实。然而,请注意,如果序列值不是等间距的,例如我们有类似 A = (1,2,5,6,9,…,n) 的东西。那么我们就不会发现一个托普利兹矩阵(计算一条对角线来说服自己,像 A_3-A_2!= A_2-A_1)。

这表明,如果我们想要一个 Toeplitz 相对编码矩阵(我们肯定会这样做),我们的绝对位置编码向量必须满足两个条件之一。第一个是它的所有值都是等间距的,就像我们这个简单的例子一样。但是,从第一部分我们知道,我们的绝对位置将不是简单的整数,而是一些复杂的 d_model 维向量。满足第一个条件是行不通的。第二个条件是要求绝对位置编码向量的每一项实际代表固定相对位置

好吧,我知道这可能没有任何意义,所以考虑这个解释。假设世界上有三个位置,称为 X、Y 和 z。我们假设它们是等距的,不管这意味着什么,并且是连续的,但是我们不能实际测量它们的绝对位置。我们能测量的,是某个距离函数的输出 d(X,Y) = NUMBER 。我们可能会发现类似于 *d(X,Y) = 2,d(X,Z) = 6,d(Y,Z)=2,*以及其余的 0。这个距离函数唯一需要做的就是吐出 d(x,x)=0,d(x,y)=d(y,X),并保证如果(X-Y) > (Z-T),那么 d(X,Y) > d(Z,T)。换句话说,为了成功地描述位置编码,我们只需要指定序列中每个元素的相对位置,称之为|i-j|,然后计算一个固定值 R(|i-j|),向读取该值的任何人发出信号,告诉他们 i,j 有多近。在这个例子中,如果我们输入 R(|i-j|)=6,我们的模型知道 6 意味着 2 的分离!

所有这些都是可以简洁地写在一个方程定义中的东西:

这是我们定义相对位置编码的等式。

我们所做的就是将|i-j|移动到 A 的索引中。这是一个微小的变化,但它带来了更大的观念变化。我们在这一小节中提到的最后一件事是,相对位置编码矩阵的秩大于绝对位置编码向量的秩。这是一个普遍的特征。相对编码总是比绝对编码大一级。对于 1D 的情况,这意味着我们得到一个向量和一个矩阵。除此之外,我们得到一个秩- k 张量和一个秩- (k+1) 张量。我们现在已经穷尽了玩具示例的用处,所以让我们转到现实生活中的位置编码

固定正弦相对位置张量

在制作工具之前,了解它的用途通常是有帮助的。在这种情况下,我们来考虑一下注意力模型。我们之所以需要位置编码,是为了让注意力机制知道项目在它所关注的序列中的位置。注意力由矩阵 logits 的矩阵 M 表示,其中条目 M_给出了索引 i 处的给定查询与索引 j 处的关键字匹配的对数概率。请注意,logits 矩阵的维数是(seq_len,seq_len)。这些恰恰是 R 的尺寸,有道理。逻辑学家在一个位置和另一个位置比较事物。因此,对我们来说,将位置信息直接注入到模型的逻辑中是有意义的,这也是我们的目标。我们将这些位置逻辑称为— 位置逻辑。

我们采用注意力模型的语言,考虑两个序列:查询,其索引是 R 的行索引,以及键,其索引是 R 的列索引。M_ = q_i 点 k_j。不失一般性,我们还可以假设查询的长度小于或等于关键字的长度。

在我们的起始案例中,我们需要一个固定的绝对位置编码。为此,我们使用本系列第一部分的中详细讨论的相同的固定正弦编码。这是一个形状为(query_len,d_model)的矩阵 A ,可以表示为向量列表 A = ( a _0,…, a _),其中 a _i 是 A 的第 i 行,维度为 d_model。这样写的话,我们看到与我们简单的起始示例的唯一区别是 A 的所有元素现在都是粗体的(意味着向量)。这将暗示我们也可以立即读出 R 的矩阵形式:

只有当查询长度 m 等于键长度 n 时才成立!

然而,这只有在查询长度(我们用 *n、*表示)等于键长度 m 时才成立。此时,您可能会问,“为什么查询长度不等于键长度?我就不能用填充物来处理这种情况吗?”通过例子来回答这个问题,当实现转换器 XL 时,就会出现这种情况,其中查看比当前查询长度更早的“内存”状态。有两个选项可以用来比较不同长度的查询和键:

来源:现作者。整数表示序列位置。

第一个选项从左向右移动时间,并将两个序列的结束位置对齐。它确保红色查询只能访问它之前的绿色键(加上对重叠部分的屏蔽)。同样,第二个选项将序列的起点排成一行,并假设时间从右向左移动。

注意,如果我们要扩展查询长度以匹配密钥长度,我们将检索一个(n n) 维矩阵,其形式由前面的等式正确给出。这两个选项实际上都是这个大矩阵的子集。在第一个选项中,我们计算完整的(n,n)矩阵,然后移除第一个 n-m 行,留下一个(m,n)矩阵。第二个选项的计算方式类似,只是删除最后的 n-m 列。我们现在假设我们正在处理一个(n,n)矩阵,然后根据所需的时间方向进行必要的截断。

我们将展示张量 R 如何通过张量AI1_ j =RT18 _ { I,0} 的一系列左移和右移在 O(n)运算中进行计算,其中的索引旨在帮助您理解张量结构。它只是一个独特矩阵 A 的一堆重复。该算法相当于下面的简单示例:找到一个执行以下操作的操作:

使用右移,我们可以正确地得到右上对角线

我将通过零索引引用行号。我们注意到,如果我们将第一行右移 1,第二行右移 2,第三行右移 3,那么我们可以正确地再现右上角的对角线。现在假设我们翻转所有的位置 i →-i. 这相当于左右翻转左矩阵。

使用左移,我们可以正确地得到右上对角线

然后我们可以执行相反的过程;将第二行左移 1,第一行左移 2,然后第零行左移 3,我们将正确地再现左下角的对角线。将这两个操作结合起来,屏蔽掉各自不正确的部分,就可以成功地构造出我们的张量 R!

在编写这个算法之前,让我们讨论一下它是在计算什么。左下对角线部分恰好由索引为 j≤i. 的所有元素组成。换句话说,左下对角线代表所有关键字-查询对,其中查询涉及在更早时间出现的关键字。右上对角线由所有相反的部分组成,其中查询在之后的时间参与关键字。希望这有助于补充我们之前讨论时间流的图表。

对于许多神经网络,我们不仅忽略右上对角线,而且主动屏蔽那些值。对于注意力模型,这种限制被称为屏蔽注意力,并与解码器转换器相关联。一个解码的句子不能关注它还没有解码的未来值,所以这应该是有意义的。

如果我们正在实现一个解码器,Transformer-XL 就是这样,那么我们只需要计算左下角的对角线位置逻辑;右上角的组件是垃圾,无论如何都会被屏蔽。然而,他们的论文没有考虑双向的想法。因为我们知道如何计算左右方向的相对位置,所以创建一个双向变压器 XL 不会太难。

我写这篇文章的动机之一,是提出在注意力模型中使用固定的双向相对位置编码的可能性。

计算所有条目的相对位置逻辑也不太难,从而让查询关注过去、现在和未来的键。双向变压器非常有用,BERT 就是一个著名的例子,我很想知道使用双向固定、相对位置编码是否会提高性能。如果有读者知道,请发帖评论!我把重点放在固定的上,因为正如我们将看到的,你可以很容易地合并学到的双向相对编码,这是人们已经试验过的东西。

计算位置逻辑:模型检查和设置

如果我们能首先描述我们到底在计算什么,计算位置逻辑就容易多了。我们首先用绝对位置编码写出对普通注意力层的所有贡献,然后检查哪些贡献需要“相对化”。我们将使用下面的符号

W 权重是将我们的嵌入/位置转换成键/查询的常用权重。通常,我们得到嵌入 E 和位置编码 U,然后将它们相加作为输入。我们现在发现注意力逻辑 M:

粗体表示嵌入维度 d_model。I 和 j 代表位置尺寸。

我们使用类似的符号“~”,因为有一个 sqrt(d_model)的规范化因子与当前的讨论无关。每行上的术语描述了模型可以学习的各种类型的相关性。我们将在下一节对此进行更多的讨论。现在,我们只要求矩阵元素 M_只依赖于位置信息 U_ = R_{i,j}。这就像只是替换 U_j → U_那么简单。

接下来,我们观察到第 2 行和第 3 行有些多余。我们保留第 2 行,并使用它来确定 E 和 U 之间的局部相关性。然后,我们修改第 3 行,以使用一个共享的(在所有键中,也就是沿着每行重复的)向量 u _i,而不是 U _i W _{q,U}。这具有维护一个全局上下文向量的效果,指示嵌入是如何与位置相关联的,给定所有我们在过去已经看到的。你可以把这看作是被允许学习谈话主题的模型。如果与金钱相关的单词最近出现了很多,那么这篇文章可以指导模型将单词“bank”解释为与金融机构而不是河岸相关。

最后一行包含位置-位置相关性。正常情况下,如果这些是学习的参数,我们希望它们只是复制我们已经提供的固定位置编码。这个术语的更好的用法是把它类似于第 3 行来处理。我们替换 v_i 而不是 U _i W _{q,U},它具有全局位置向量的作用,跟踪我们的全局位置。在这些替换之后,我们有:

第二项是我们现在要计算的。

代码和算法

算法和之前一样。只是现在,我们有了一个具体的东西来看待。为了我们的目的,我们可以把在 U 左边的所有东西组合成一个矩阵 q_i ,这样我们想要计算的项是q_ Iu_^t.我们从左移开始。首先在图表中显示算法,然后尝试解释它会更容易

我觉得用形状代替数字会很有趣。黑色的星星代表填充。它们在数值上相当于 0。最终,我们希望将倒数第二行左移一位,倒数第三行左移两位,以此类推。我们从一个贪婪的选择开始。取出左下角的 pad 令牌,并将其移动到倒数第二行。这通过强制该行中的所有其他项左移一位来自动实现左移。然而,这反过来移动第二行到最后一行的第一个元素(一个黑色的星星)无处可去,也迫使一个绿色的圆圈也移动,因为它现在在矩阵的范围之外。因此,我们现在必须将一颗黑色星星和一个绿色圆圈移到红色菱形行上。这反过来取代了红色钻石,留下三块无处可去!但是,我们注意到矩阵的其余部分现在被适当地左移了。如果我们能摆脱那些讨厌的剩余形状就好了…

我们可以。也很容易。您只需要将矩阵调整为 3 列,并删除第一行。完成了。如果你把它重新塑造成合适的形状,所有的东西都会向左移动。下面,我们假设一个形状为(N,T,Q,K)的输入张量。算法步骤如下:

如果时间从左到右增加:

  1. 颠倒位置。这可以通过tf.reverse(U, [2])或矩阵np.flipud(U)来完成。
  2. 执行点积 q _i U _j^t.
  3. 用一列 1 填充结果。对于一个形状为(N,T,Q,K)的张量,这意味着在第四个也是最后一个维度 K 上的左填充。
  4. 重塑成大小(N,T,K+1,Q)。
  5. 删除第一行。
  6. 重塑成 shape (N,T,Q,K)的最终答案。

如果时间从右向左增加:

  1. 不要颠倒位置。如果键长度大于查询长度,则删除 u 的最后一行(klen-qlen ),按照步骤 2 执行点积。
  2. 右填充第四个也是最后一个位置。执行相同的整形
  3. 请改为截断最后一行。
  4. 如果 U 最初被截断,则留下与删除的行一样多的填充列。重塑为最终答案。

如果时间是双向的:

  1. 通过取 1 的(klen,klen)矩阵的上下对角线来计算掩码。
  2. 分别计算左侧和右侧的 logits,然后结合掩码。

下面我们用 NumPy 表示代码。在很大程度上,这可以通过替换 np →tf 直接转化为 TensorFlow,但我们认为这样会更清晰,以避免处理批处理和多头注意力维度的复杂性。np.triu 和 np.tril 会转换成 tf.linalg.band_part。

我添加了一些额外的测试,以便您可以确认这确实正确地计算了位置逻辑。当我在我的机器上运行代码时,我得到了输出

Correct answer:
 [[ 5.18 -2.58  1.54 -2.58  5.18]
 [ 0.18 -2.39  0.91 -1\.    0.91]
 [-4.05  3.41  1.86 -0.6  -4.53]]
Shifted algorithm:
 [[ 5.18 -2.58  1.54 -2.58  5.18]
 [ 0.18 -2.39  0.91 -1\.    0.91]
 [-4.05  3.41  1.86 -0.6  -4.53]]
Match? True

第三部分(可学习的编码和比较):

习得嵌入

到目前为止,(几乎)我们所做的一切都涉及用户定义的固定位置编码(我说几乎是因为我们在第二节中添加了那些全局上下文/位置向量,它们是学习参数)。这很好。如果我们能够获得良好的性能,这比让模型自己学习这些参数更快、更稳定。然而,如果有一件事深度学习不断提醒我们;电脑可能做得更好。

卷积在图像处理中已经使用了很长时间,然而,当构建计算机视觉网络时,我们让计算机决定它将构建哪种卷积。在实践中,这种方式要好得多。那么为什么不在这里采用同样的方法呢?为什么不让计算机学习自己的位置编码呢?在本节中,我们将沿着这些思路讨论一些方法。

我们的起点是以前的普通变压器注意逻辑方程,为了方便起见,我们在这里重复

粗体表示嵌入维度 d_model。I 和 j 代表位置尺寸。

虽然这 4 个术语是作为将位置编码 U 添加到嵌入向量 **E,**的结果而得到的,但是我们决不会以这种方式构造逻辑。相反,这给了我们一个对 logit 矩阵的贡献类型的线索。主要有三种类型

  1. 嵌入-嵌入相关性。由第 1 行给出。
  2. 嵌入位置相关性。由第 2 行和第 3 行给出。
  3. 位置-位置相关性。由第 4 行给出。

嵌入-嵌入相关性是注意力的基础。我们显然不会摆脱他们,所以没有什么好说的了。

嵌入位置相关性表明一些单词是否与绝对键/查询位置匹配。根据推理,我们不会认为那是真的。如果我说你好,它不应该因为今天是 2 月 2 日还是 7 月 18 日而改变意思。相反,我们通常根据单词的相对位置来指定词义。这暗示我们可能想要用一些更“相对”友好的东西来替换这些术语。同样,我们已经展示了如何使用固定的相对编码来升级它们,但是这里我们考虑添加可学习的参数。

最后,在最后一行,我们有位置与位置的相关性。希望一个模型能学会复制一些像我们手动处理的托普利兹矩阵一样的东西。这表明模型正在学习有意义的位置。

让我们首先处理嵌入位置相关性。确定这些相关性是否相关的最简单的方法就是直接测量它们。在、【柯】、狄鹤&、的论文中,他们分析了一个预训练的 BERT 模型的学习矩阵相关性,并分割绘制了由此产生的学习相关性。他们找到了下面的图表,

来源:郭林柯,狄鹤&2020。从左到右:E-E,E-U,U-E,U-U 相关性。

证实了我们的直觉,我们可能会放弃绝对的位置嵌入相关性。

顺便提一下,我们注意到这个结论是基于键和查询大小相同的假设得出的。在像 Transformer-XL 这样的环境中,可能存在能够在网络中传播的全局位置或环境信息。在这种情况下,放弃这些贡献可能是不明智的。

这些评论让我们对可学的位置编码有了第一个猜测。我们首先决定通过位置逻辑将我们的位置编码直接注入网络**。然后,我们为这些逻辑选择以下形式:**

正如所建议的,我们删除了位置嵌入的东西,把位置-位置部分变成了可以学习的东西。这种表示是将位置编码扩展到逻辑的最早尝试之一,并在 Shaw 等人的论文中提出。艾尔。(2018) 。这和只是盲目地向逻辑中添加一个可学习的矩阵有一个细微的区别。我们将位置逻辑的形式限制为托普利兹,这里用α表示。因此,我们通过绑定权重,有效地将可用参数减半。这确保了模型必须对称地对待位置;距离 d(x,y)必须等于 d(y,x)。肖等人的实际模型。艾尔。(2018) 实现稍微复杂一点,但大体思路就是这样。

与我们的固定位置编码方法相比,这种方法在处理长序列时不够灵活。由于位置逻辑是学习的参数,它们不能在模型训练后调整,并且在训练后以它们的大小为上限。作者认识到了这个问题,并提出要解决这个问题,他们实现了位置裁剪。超过一定距离后,相对位置被限制为 k 的最大值。

这种说法是,在足够长的长度上,知道某个东西只是“很远”就足够了,而不是大数字之间的精确差异。为了改进 Shaw 模型,我们希望以某种方式纳入更通用的位置编码,这可能允许全局信息的传播。此外,尽管我们在本文中一直在说,绝对编码可能有一些用处。例如,基于字符的语言模型可能希望大写字母与序列的开头高度相关。然而,这只是有限序列长度的假象。对于像 Reformer 这样具有大序列的模型,这一点变得无关紧要。

我们可以采用另一种方法,在保留所有位置编码 logit 术语和保留带有绑定权重的受限类之间进行折衷。在柯、狄和等人的论文中,他们提出使用逻辑推理

正如他们所显示的,最后两个术语没有重叠的表达能力。这为模型提供了更大的代表性。例如,模型可以学习使用绝对嵌入来确定偏移,然后使用相对嵌入来微调位置。

可学习的位置编码优于固定编码的一个优点是可以灵活地处理数据中的非顺序插入。例如,考虑 BERT 培训目标。我们留出特殊记号[CLS]和[分离],分别表示句子的意义和分离。后者有明确的位置含义,但前者没有。一个习得的位置编码可以通过“解开”CLS 令牌来处理这个问题。柯等人。艾尔。(2020)通过将[CLS]令牌移动到第 0 个连续槽,并给它自己的共享参数θ,来处理这种特殊情况。该参数对于所有其他令牌都是相同的,表示[CLS]令牌与所有其他令牌同样“远”。否则,将会有一种偏向,即学习给予早期序列标记更大的权重[CLS]。

结论

在本文中,我们经历了绝对位置编码的逻辑升级:相对编码。从一些日常语言的简单例子开始,我们激发了在处理顺序数据的模型中使用相对编码的需求。

我们证明了只要网络有一组可靠的序列中两点之间的距离值,它就可以用它来计算相对位置。这允许我们通过回收我们已经来之不易的关于绝对位置编码的知识来构建 Toeplitz 相对编码张量。此外,由于我们编码的特殊 Toeplitz 属性,我们能够找到一种在 O(n)时间内计算相对位置的方法。我们给出了这样做的代码,还讨论了右移序列和双向相对编码的扩展。

最后,我们比较了我们的固定正弦编码和使用学习参数。固定编码的优点是速度快、序列长度灵活,以及内置可靠的量化位置模型。内置编码还能够利用具有不同键查询大小的模型。它们也可以调整成混合形式,其中一些可学习的参数被留出用于存储全局上下文和位置信息。相比之下,学习参数模型具有简单的表示,并且可以自由地让模型决定如何比较序列。可学习参数的一个独特优势是处理非顺序信息,如没有/不应该有明确位置的特殊标记。这些可以通过解开权重,并为非顺序令牌保留特殊的可学习参数来处理。

谢谢你坚持到现在。一如既往,随时评论或联系我任何问题或意见!

参考

  1. 你所需要的只是关注。 arXiv 预印本 arXiv:1706.03762 (2017)。
  2. 肖、彼得、雅各布·乌兹科雷特和阿希什·瓦斯瓦尼。"自我关注与相对位置表征." arXiv 预印本 arXiv:1803.02155 (2018)。
  3. 戴,子航等,《变形金刚-xl:超越定长语境的注意语言模型》 arXiv 预印本 arXiv:1901.02860 (2019)。
  4. 柯、、狄鹤和。"重新思考语言预训练中的位置编码." arXiv 预印本 arXiv:2006.15595 (2020)。
  5. 用统一的文本到文本转换器探索迁移学习的极限。 arXiv 预印本 arXiv:1910.10683 (2019)。

Python 中的主数据结构字典:从零到 Hero,第 1 部分

原文:https://towardsdatascience.com/master-python-dictionary-for-beginners-in-2021-1cdbaa17ec45?source=collection_archive---------21-----------------------

破解数据科学访谈

LeetCode 助你迈向数据科学的位置

彼世恒在 Unsplash 上的照片

正如你们中的许多人所知道的,我正在将我的编程语言从 R 转换为 Python,并且在过去的一年中一直在练习用 Python 进行编码。我不是在吹牛,但我总是惊讶于我的进步和周转快的家伙谁只知道“你好,世界!”对于那些在 Leetcode 上轻松解决复杂的编码挑战的人来说,这是非常重要的。

例如,我在这里对五个 data science 采访问题进行了实时编码:

</5-python-coding-questions-asked-at-faang-59e6cf5ba2a0>

Python 提供了如此多的数据类型,而字典是其中的佼佼者。掌握 Python 字典有助于我们破解任何编码采访。在今天的内容中,我选择了四个访谈问题,并演示了如何在 Python 中使用字典。

让我们简单回顾一下,看看什么是词典及其主要属性。词典有三大特点:无序、多变、不重复。

#1 字典没有排序,因此我们不能使用位置来访问元素;相反,我们必须使用密钥-值对。

#2 它是可变的,这让我们可以更新字典元素。

#3 不允许重复。

这里有一个简单的例子。

我们创建了一个新的字典,称为 dictionary_1,,它有三个密钥-值对。

字典 _1

{'brand': 'BMW', 'model': 'Model 3', 'year': 2000}

#1。获取字典 的密钥

dict_keys(['brand', 'model', 'year'])

#2。取值字典

价值观念

dict_values(['BMW', 'Model 3', 2000])

#3。获取密钥-值对

关键值对

dict_items([('brand', 'BMW'), ('model', 'Model 3'), ('year', 2000)])

问题 1:每家科技公司的两个 Sum

-给定一个整数 nums 数组和一个整数目标,返回这两个数字的索引,以便它们加起来达到目标值。
-可以假设每个输入只有一个解决方案,并且不可以将同一元素使用两次。
-您可以按任何顺序返回答案。
-https://leetcode.com/problems/two-sum/

走一走我的思路

这是每个大公司都问过或曾经问过的“臭名昭著”而且可能是最广为人知的面试问题。新手和成熟的程序员会以不同的方式处理这个问题。

对于初学者来说,首先想到的是迭代列表,并不断检查两个元素的总和是否等于目标值,也就是残酷的力量。

如果它们相加,我们分别输出索引。唯一的问题是我们不能两次使用同一个元素,并且只有一个解决方案。

解决方案 1:嵌套 for 循环(强制)

[0, 1]

当然,这是可行的,但速度很慢。它的时间复杂度为 O(N ),如果目标数组有数百万个元素需要检查,那么它就不是一个好的选择。

你的面试官要求改进。

解决方案 2:字典

与初学者不同,有经验的程序员会分析手头的问题,并将其转化为一个容易理解的问题。成熟的编码人员努力思考如何更有效地访问和存储每个位置的值。

第一个提示:字典是有帮助的!

要创建和使用一个空字典,我们必须设置条件,并指定当值不在字典中和在字典中时该怎么办。按照同样的思路,我们创建一个新变量, diff ,它记录目标值和数组元素之间的差异。如果差异没有包含在字典中,我们将键值添加回字典中;如果包含在字典中,我们返回原始元素的索引和字典的键(index)。

让我们检查代码。

[0, 1]

哇!

第二种方法效率更高,速度更快,线性时间复杂度为 O(N)。

如果你能掌握解决方案 1,你就已经进了门,成为了 Python 初学者。掌握了解决方案 2 之后,你就更上一层楼了。

问题 2:谷歌的唯一出现次数

-给定一个整数 arr 数组,写一个函数,当且仅当数组中每个值的出现次数是唯一的时,返回 true。【https://leetcode.com/problems/unique-number-of-occurrences/】-

走过我的思考

谷歌收录了这个问题。任何技术编码的第一步都是阅读问题,并确保你理解了它。如果有些事情没有意义,要求澄清,这增加了分数,也有利于我们。

看完分析问题,有两个部分:

#1.计算每个值的出现次数。

#2.检查它们是否是唯一的。

为了计算出现次数(步骤 1),我们创建一个空字典,并使用 if-else 语句来存储键值对。

为了检查唯一值(步骤 2),我们可以使用另一个内置的数据类型 set,它不允许重复。顺便提一下,我们通常在迭代后比较两个输出的长度,并决定它们在 Python 中是否相同:如果长度改变了,结果也改变了。

解决办法

True

同样,Python 中的 set 不允许重复。字典和集合的结合是强大的。

Annelies Geneyn 在 Unsplash 上拍摄的照片

问题 3:大小为 2N 的数组中有 N 个重复的元素,由苹果公司开发

-在大小为 2N 的数组 A 中,有 N+1 个唯一元素,并且这些元素中恰好有一个元素重复 N 次。
-返回重复 N 次的元素。
-https://leet code . com/problems/n-repeated-element-in-size-2n-array/

走过我的思考

苹果问这个问题。有两条有用的信息:

#1.N+1 个唯一元素。

#2.一个元素重复 N 次。

最自然的做法是计算出现次数,并返回出现 N 次的元素。

解决方案 1:内置计数器()

3

首先,让我们试试内置方法 Counter(),返回出现 1 次以上的元素。

这很容易也很简单,但问题是我们可能不会在面试中导入内置函数。

解决方案 2: dictionary.items()

我们构建一个空字典,并通过 for 循环的存储键值对*。接下来,我们遍历字典,当它的值(出现次数)等于 n 时返回键。*

搞定了。

顺便提一下,Python 中的 division 创建了一个 float,不能用来访问项目。这就是为什么我们需要像第 5 行一样使用 floor division, len(A)//2 来计算 n。

type(22/2)
floattype(22//2)
int

问题 4:谷歌的单排键盘

-有一个特殊的键盘,所有的键都在一排。
-给定一个长度为 26 的字符串键盘,指示键盘的布局(索引从 0 到 25),最初,您的手指在索引 0 处。要输入一个字符,你必须将手指移到所需字符的索引处。
-将手指从食指 I 移动到食指 j 所需的时间是|i — j|。
-您想要键入一个字符串单词。写一个函数来计算用一个手指打字需要多少时间。
-https://leetcode.com/problems/single-row-keyboard/

走过我的思考

谷歌问这个天才问题。很多时候,不是问题有多复杂,而是问题有多棘手。挑战在于分解问题,并将其转化为更易读的形式。

单行键的特殊键盘?

从来没见过。但是经过一些思考,我们可以把这个问题转换成如下:“请计算每个元素的索引的总距离。”

我们可以进一步将其分为两步:

#1.找到键值对。

#2.计算差异的总和。

我们将键值对映射到空字典(步骤#1 ),并使用 for 循环将距离相加,如下所示。

解决办法

4

在第 14 行,我们用当前索引更新临时值,并为下一次迭代做准备。

完整的 Python 代码在我的 Github 上有。

外卖食品

  • 如果问题暗示了键值对,字典可能会有所帮助。
  • 棘手的部分是如何在字典中存储值:是否必须为多次出现而更新它?怎么会?
  • dictionary 中有三种过滤元素的方式:键()、值()和项()。

Medium 最近进化出了它的 作家伙伴计划 ,支持像我这样的普通作家。如果你还不是订户,通过下面的链接注册,我会收到一部分会员费。

https://leihua-ye.medium.com/membership

我的数据科学面试顺序:

喜欢读这本书吗?

请在 LinkedIn 和 Youtube 找到我。

还有,看看我其他关于人工智能和机器学习的帖子。

Python 大师用这 6 样东西来设定

原文:https://towardsdatascience.com/master-python-sets-with-these-6-things-23dce2c7e0d8?source=collection_archive---------24-----------------------

在 Python 项目中明智地使用集合

由克里斯托弗·罗拉在 Unsplash 拍摄的照片

在每个 Python 项目中,我们到处都使用内置数据类型。在数据容器中,我注意到使用最多的是列表和字典,其次是元组。不知何故,集合是最少使用的。但是,有一些特殊的方法是集合所独有的,这将使我们的工作变得得心应手。在这篇文章中,我想回顾一下集合的所有重要基础知识。在您的项目中使用 set 会更舒服。

1.结构:字面、构造和理解

当我们创建一个 set 对象时,最常见的方式是在我们知道什么对象构成了set对象的情况下使用 literal 方式。具体来说,我们使用一对花括号将元素括起来。

集合创建

我们也可以直接使用 set 构造函数,在其中我们可以传递任何 iterable,比如列表、元组和字典。下面是一些例子。

>>> # Create a set from a list using the constructor
>>> set([1, 3, 2, 3, 2])
{1, 2, 3}
>>> # Create a set from a dictionary using the constructor
>>> set({1: "one", 2: "two"})
{1, 2}

你可能听说过列表综合,以类似的方式,我们可以使用集合综合和现有的 iterable 来创建集合。

>>> # Starting with a list of integers
>>> numbers = [-3, -2, -1, 1, 2, 3]
>>> # Create a set from the integers
>>> squares = {x*x for x in numbers}
>>> squares
{9, 4, 1}

如您所见,集合理解具有以下语法。这就像列表理解,除了我们用花括号代替方括号。

{expression for item in iterable}

2.哈希能力

并非所有数据都可以保存在集合中,只有可哈希对象可以作为集合项目。在 Python 中,哈希是应用哈希算法将对象转换为整数形式的哈希值的过程。然而,并不是每个对象都是可散列的。通常,只有不可变的对象是可哈希的,比如整数、字符串和元组。相比之下,可变对象在默认情况下是不可哈希的,一些内置的可变对象类型包括列表、集合和字典。下面的代码向您展示了这些情况。

>>> # Sets consist of immutable objects
>>> {1, "two", (3, 4)}
{1, (3, 4), 'two'}
>>> # Mutable objects can't be set items
>>> {[1, 2, 3]}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

找出对象的哈希能力的一种方法是简单地使用内置的哈希函数。您将看到不可变对象与有效的哈希值相关联,而可变对象则不是。

>>> hash("Hello, World!")
-7931929825286481919
>>> hash({1: "one", 2: "two"})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'

3.非常适合会员测试

您可能想知道为什么集合只存储可散列的对象,因为在幕后,Python 使用散列表来保存这些数据。本质上,在哈希表中,我们使用哈希值来跟踪存储的对象。最大的好处是条目的查找时间恒定,这使得集合在成员关系方面是一个完美的数据结构。

作为直接比较,知道一个列表或一个元组是否包含一个特定的项需要相当长的时间,因为 Python 需要遍历所有的项才能知道成员资格是否为真。下面的代码向您展示了我们如何对集合和列表之间的成员资格检查进行计时。

成员资格检查:列表与集合

如您所见,即使 set 对象的项目数变为 100,000,成员资格检查时间也是一致的。相比之下,检查成员资格所需的时间几乎与列表对象成线性增长,这突出了与列表相比,集合的成员资格检查的效率。

4.检查集合之间的关系和操作

当我们有多个集合时,我们可以评估它们之间的关系。为了简单起见,让我们研究最基本的场景:两个集合对象之间可能的关系。下面的总结向您展示了关于集合之间的关系和操作的最常用的方法。

**Check Relationships**
**issubset**: whether a set is a subset of the other
**issuperset**: whether a set is a superset of the other
**isdisjoint**: whether two sets have no intersection
<=: equivalent to issubset
<: a proper subset
>=: equivalent to issuperset
>: a proper superset**Between-set Operations**
**intersection**: a set of items that belong to both sets
**difference:** a set of items that belong to the set but not others **union**: a set of all the items from both sets
**symmetric_difference**: a set of items that belong to only one set
&: equivalent to intersection
-: equivalent to difference
|: equivalent to union
^: symmetric difference

这些定义应该很简单,好奇的读者可以自己尝试这些操作。需要注意的一点是,您不必使用集合作为调用这些方法的参数,因为它们可以接受任何可迭代对象。下面是一个简单的例子。

>>> {1, 2}.issubset([1, 2, 3])
True

5.使用和更新成员

当我们使用集合对象中的所有成员时,一种常见的方式是 for 循环,因为集合是一种可迭代的对象,如下所示。

>>> for number in {1, 2, 3}:
...     print(number)
... 
1
2
3

如果您想要使用集合中的一个项目,请不要使用订阅,因为集合不支持此操作。原因很简单——set 项没有排序,它们也不是序列数据,因此当您使用 subscription 时,它不知道应该呈现哪个项。

>>> {1}[0]
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: 'set' object is not subscriptable

相反,您可以使用pop方法,该方法将返回一个项目。然而,与此同时,该项目将从设置对象中删除,你必须记住这个副作用。如果您想简单地删除一个项目,您可以使用remove方法,该方法将返回None

>>> primes = {2, 3, 5, 7}
>>> primes.pop()
2
>>> primes
{3, 5, 7}
>>> primes.remove(3)
>>> primes
{5, 7}

请注意,当您使用pop方法时,如果器械包是空的,则KeyError将会升高。如果您使用remove方法移除不在 set 对象中的项目,您也会遇到KeyError。为了避免引发KeyError的可能性,一个相关的方法是discard,当它是一个实际的成员时,它将移除指定的项,或者忽略移除而不引发KeyError异常。

如果要移除 set 对象中的所有项目,只需调用clear,如下图所示。有些人可能会想到做primes = set(),不推荐这样做,因为它做的是将一个空的 set 对象赋给 primes 变量,而不是真正修改同一个 set 对象。

>>> primes = {2, 3, 5, 7}
>>> primes.clear()
>>> primes
set()

要向 set 对象添加一个项目,只需调用:

>>> primes = {2, 3, 5, 7}
>>> primes.add(11)
>>> primes
{2, 3, 5, 7, 11}

当您想要添加多个项目时,可以使用update方法。类似于前面提到的方法(例如,issubset),我们可以在update方法中使用任何可重复项,如下所示。

>>> primes.update([5, 7, 11, 13, 17])
>>> primes
{2, 3, 5, 7, 11, 13, 17}

6.另一种选择:冷冻

最后但并非最不重要的一点是,有必要了解集合数据类型的一种替代形式——冻结集合(frozenset)。顾名思义,冻结集是不可变的,因此一旦创建,就不能修改项目。请参见以下代码片段中的示例。

>>> primes_frozen = frozenset([2, 3, 5])
>>> primes_frozen.add(7)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'frozenset' object has no attribute 'add'

还有一点可能有些人不知道,就是你可以拿一套和一套冷冻的做比较。不是因为他们看起来不同的类型而返回False,而是只通过他们的成员进行比较,如下所示。

>>> {1, 2, 3} == frozenset([1, 2, 3])
True
>>> {1, 2, 3} < frozenset([1, 2, 3, 4])
True

您可能想知道,既然现有的集合数据类型似乎可以提供一切,我们为什么要使用冻结的集合。考虑使用冻结集有几个原因。

  • 当您使用集合对象并且不希望项目被意外更改时,这是使用冻结集合的好时机,因为它具有不变性,可以防止不希望的更改。
  • 不变性伴随着 hashability,hash ability 允许在 set 对象中包含冻结的集合。因为集合是可变的,所以它们不能是集合项。请参考下面的代码进行快速对比。
>>> {{1, 2}, {2, 3}}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set'
>>> {frozenset([1, 2]), frozenset([2, 3])}
{frozenset({2, 3}), frozenset({1, 2})}

结论

在本文中,我们回顾了 Python 中使用 set 对象的最基本的操作。正如您所看到的,虽然与列表和字典相比,它们不太常用,但是集合具有有用的特征,比如集合之间的唯一成员和操作,这使得它们在特定的用例中很有价值。

在 5 分钟内掌握卡夫卡外壳——主题、生产者和消费者解释

原文:https://towardsdatascience.com/master-the-kafka-shell-in-5-minutes-topics-producers-and-consumers-explained-442a15f1dac1?source=collection_archive---------22-----------------------

通过这篇实践文章和视频,学习 Kafka 控制台生产商和消费者的基础知识

来自 Pexels 的 Markus Spiske 摄影

在大数据的世界里,可靠的流媒体平台是必不可少的。这就是卡夫卡出现的地方。您已经用 Docker 安装并配置了它。如果不是这样,请在继续之前阅读这篇文章或观看这段视频。

今天,你将了解所有关于 Kafka 的话题,游戏机生产商和消费者。您将掌握 Kafka shell,到本文结束时,您将为更高级的示例做好准备,例如在 Python 中使用 Kafka。

最棒的是,视频指南再次可用:

今天的文章包括以下内容:

  • 《坚果壳》中的卡夫卡主题
  • Kafka 游戏机生产商
  • Kafka 控制台消费者
  • 总结和后续步骤

《坚果壳》中的卡夫卡主题

首先打开一个新的终端窗口并连接到 Kafka shell。您应该已经运行了 Zookeeper 和 Kafka 容器,如果不是这样,请启动它们:

docker exec -it kafka /bin/sh

以下是您应该看到的内容:

图 1——打开卡夫卡的外壳(图片由作者提供)

导航到 Kafka bin 文件夹,因为所有 Kafka shell 脚本都位于该文件夹中。

cd /opt/kafka_<version>/bin
ls

以下是您应该看到的所有脚本:

图 2 —卡夫卡 bin 文件夹(图片由作者提供)

创建主题

接下来,让我们创建一个话题来玩。下面的命令将创建一个名为dummy_topic的主题。复制因子和分区设置为 1,因为这是一个虚拟环境:

kafka-topics.sh --create --zookeeper zookeeper:2181 --replication-factor 1 --partitions 1 --topic dummy_topic

您将看到以下输出打印到控制台,表明主题已成功创建:

图 3——创造一个卡夫卡主题(作者图片)

列出 Kafka 主题

让我们通过列出所有 Kafka 主题来验证主题是否创建成功。您可以使用以下命令来完成此操作:

kafka-topics.sh --list --zookeeper zookeeper:2181

该命令需要几秒钟的时间来执行,但是一旦完成,您将会看到下列主题:

图 4 —列出卡夫卡的主题(作者图片)

获取卡夫卡主题的详细信息

以下命令用于收集某个主题的信息,如分区计数、复制因子和附加配置。它叫做--describe,下面是你如何使用它。自从一分钟前创建这个主题以来,您已经对它了如指掌:

kafka-topics.sh --describe --zookeeper zookeeper:2181 --topic dummy_topic

这是它打印出来的内容:

图 5——获取关于卡夫卡主题的信息(作者图片)

删除卡夫卡主题

最后,让我们看看如何删除主题。该命令与前一个命令相同;唯一的区别是--delete参数而不是--describe:

kafka-topics.sh --delete --zookeeper zookeeper:2181 --topic dummy_topic

下面是您应该看到的输出:

图 6 —删除一个卡夫卡主题(作者图片)

删除一个主题需要几秒钟的时间,但是一旦完成,您可以列出所有要验证的主题。

这就是你现在应该知道的关于 Kafka 主题命令的全部内容。接下来,我们来看看卡夫卡主机生产商。

Kafka 游戏机生产商

这是我们钻研好东西的地方。首先,您将创建一个名为 Messages 的新主题,它将模拟多个用户之间的聊天。您已经知道了创建主题的命令:

kafka-topics.sh --create --zookeeper zookeeper:2181 --replication-factor 1 --partitions 1 --topic messages

以下是输出结果:

图 7 —为存储消息创建新主题(作者图片)

现在你将离开kafka-topics.sh文件,转而使用kafka-console-producer.sh。这里唯一的新属性是 broker list,您将使用它来指定 Kafka 的位置。如果你已经一步一步地跟随前面的视频,你的 Kafka 位于端口 9092。最后,您需要指定想要为其生成消息的主题。

以下是完整的命令:

kafka-console-producer.sh --broker-list kafka:9092 --topic messages

接下来您将看到内部外壳打开。您不必遵循任何消息约定,但是我将输入两个格式为 JSON 的消息。您将获得用户 ID、收件人 ID 和邮件本身。完成后,按 CTRL+C 关闭生成器外壳:

图 8——用卡夫卡创作信息(作者图片)

所以到目前为止,什么都没有发生,但是我保证消息被成功地生成了。你需要了解 Kafka 游戏机的消费者来验证这一说法。

Kafka 控制台消费者

消费者和生产者最好同时运行,所以打开一个新的终端标签,连接到同一个 Kafka 容器:

docker exec -it kafka /bin/sh
cd /opt/kafka_<version>/bin

在左侧选项卡上,您可以再次启动 Kafka 生产者,在右侧选项卡上,您可以启动消费者。您必须使用带有--bootstrap-server参数的kafka-console-consumer.sh文件来消费发送到特定主题的消息。

简而言之,您应该打开两个终端窗口——一个(左)用于生产者,另一个(右)用于消费者。使用以下命令启动它们:

制片人(左):

kafka-console-producer.sh --broker-list kafka:9092 --topic messages

消费者(右):

kafka-console-consumer.sh --bootstrap-server kafka:9092 --topic messages

正如您所看到的,一旦您启动了消费程序,什么都不会发生:

图片 9——开始卡夫卡式的消费者和生产者(图片由作者提供)

这是因为默认情况下,消费者不会列出旧消息,而是只显示新消息。让我们通过向生产者输入另一条消息来验证这一点:

图 10 —生产和消费新信息(作者图片)

现在,该消息立即显示在“消费者”选项卡中。这是你需要记住游戏机消费者的事情。如果你想看到这些信息,你需要让它们一直开着。

但是如果你想列出一个特定主题的所有生成的消息呢?事实证明你也能做到。您可以关闭生成器,因为您不再需要它了。

要列出特定主题中的所有消息,可以重用前面的消费者命令。但是您必须添加一个参数来指定您想要列出所有消息。方法如下:

kafka-console-consumer.sh --bootstrap-server kafka:9092 --topic messages --from-beginning

以下是输出结果:

图 11 —消费 Kafka 主题中的所有消息(作者图片)

如您所见,这三条消息都显示在消费者控制台中。

总结和后续步骤

这是卡夫卡背后的基本思想。您将消息发送到一个中心位置,并将它们分布到各个主题中,然后编写消费者代码来处理这些消息。

如果您的 shell 命令在某处失败,请参考视频,因为该视频更容易理解。

在下一篇文章中,您将看到如何用 Python 编写实际的生产者和消费者代码,所以如果您想了解 Kafka 的更多信息,请继续关注。

喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。

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

保持联系

  • 在媒体上关注我,了解更多类似的故事
  • 注册我的简讯
  • 在 LinkedIn 上连接

原载于 2021 年 9 月 25 日 https://betterdatascience.com**T21

用 10 个步骤掌握一项功能:熊猫小组

原文:https://towardsdatascience.com/mastering-a-function-in-10-steps-pandas-groupby-54a438e9ea93?source=collection_archive---------22-----------------------

数据分析中最常执行的操作之一。

托尔加·乌尔坎在 Unsplash 上拍摄的照片

Pandas 可以说是数据科学生态系统中最受欢迎的 Python 库。它提供了许多功能来有效地执行数据分析和操作任务。

数据分析中最常用的 Pandas 函数之一是 groupby 函数。它允许根据一列或一组列中的不同值对数据点(即行)进行分组。

生成组后,您可以轻松地将聚合函数应用于数字列。考虑一个数据集,它包含一家公司雇员的性别信息和工资。

为了计算不同性别的平均工资,我们可以根据性别列对行进行分组,然后对工资列应用 mean 函数。

在本文中,我们将通过 10 个步骤来学习 groupby 函数的细节。连续步骤包含建立在前一步骤基础上的示例。

让我们从导入熊猫开始,并使用虚构的数据创建一个数据框。

import pandas as pddf = pd.DataFrame({
   "names": ["John", "Jane", "Ashley", "Allen", "Betty"],
   "gender": ["Male", "Female", "Female", "Male", "Female"],
   "height": [182, 176, 174, 178, 172],
   "education": ["BS", "BS", "BA", "BA", "BS"],
   "salary": [65000, 72000, 74000, 68000, 80000]
})df

(图片由作者提供)

第一步

第一步是一个非常基本的例子。我们根据性别创建分组,并应用均值函数。

df.groupby("gender").mean()

(图片由作者提供)

因为我们没有指定数字列,Pandas 计算每个数字列的平均值。

第二步

在某些情况下,我们只想对特定的列应用聚合函数。一种方法是在应用 groupby 函数之前筛选列。

df[["gender","salary"]].groupby("gender").mean()

(图片由作者提供)

第三步

我们在第二步中所做的并不是最优的方法。典型的真实数据集包含几列,我们可能需要对许多列而不是所有列计算聚合。

在这种情况下,过滤列是一项单调乏味的任务。比较好的方法是使用熊猫的 NamedAgg 功能。

df.groupby("gender").agg(
   avg_salary = pd.NamedAgg("salary","mean")
)

(图片由作者提供)

我们只需要指定列名和聚合函数。使用 NamedAgg 函数的另一个优点是,我们可以为聚合值指定一个名称。它肯定比使用原来的列名更清晰、信息量更大。

第四步

甚至还有比第三步更简单的方法。语法如下:

df.groupby("gender").agg(avg_salary=("salary","mean"))

(图片由作者提供)

我总是使用这种方法,因为它需要最少的打字量😊。

第五步

一旦生成了组,我们就可以根据需要计算任意多的聚合。例如,下面的代码查找每个性别的平均工资和平均身高。

df.groupby("gender").agg(
   median_salary=("salary","median"),
   avg_height=("height","mean")
)

(图片由作者提供)

第六步

至此,组显示为数据框的索引。在某些情况下,最好将它们作为数据框中的一列。我们可以通过使用 as_index 参数来实现这一点。

df.groupby("gender", as_index=False).agg(
   median_salary=("salary","median"),
   avg_height=("height","mean")
)

(图片由作者提供)

这种方式更好,尤其是当我们有几个组的时候。

第七步

就像我们可以在多个列上计算聚合一样,我们也可以基于多个列创建组。如果我们将两列传递给 groupby 函数,它将根据每列中不同值的组合创建组。

df.groupby(["gender","education"], as_index=False).agg(
   median_salary=("salary","median"),
   avg_height=("height","mean")
)

(图片由作者提供)

确保在列表中传递列名。否则,您将得到一个错误。

第八步

这在我们的例子中是不必要的,但是当我们有几个组时,我们可能希望根据聚合值对组进行排序。假设我们有 100 列,需要找到平均工资最高的前 3 组。在这种情况下,对结果进行排序就成了一项必要的操作。

sort_values 函数可以与 groupby 函数一起使用,如下所示:

df.groupby(["gender","education"], as_index=False).agg(
   median_salary=("salary","median"),
   avg_height=("height","mean")
).sort_values(by="median_salary", ascending=False)

(图片由作者提供)

默认情况下,行按升序排序。我们可以用升序参数来改变它。

第九步

我们的数据框不包含任何缺失值。然而,现实生活中的数据集可能会有一些。如果用于分组的列中缺少值,默认情况下会删除这些行。

让我们首先将数据框中的一个值更新为缺失。

df.iloc[4,1] = Nonedf

(图片由作者提供)

df.groupby("gender").agg(avg_salary=("salary","mean"))

(图片由作者提供)

如您所见,性别列中缺少的值(None)被忽略。然而,在许多情况下,将缺失值考虑在内是很重要的。我们可以将 dropna 参数设置为 False,以便在结果中包含缺失的值。

df.groupby("gender", dropna=False).agg(avg_salary=("salary","mean"))

(图片由作者提供)

第十步

我们已经在步骤 5 中看到了如何计算多个聚合。此操作的另一个方法是将一个聚合列表传递给 agg 函数。

df.groupby("gender", dropna=False).agg(["mean","count"])

(图片由作者提供)

结论

groupby 是一个广泛用于数据分析的函数。我们几乎涵盖了你需要了解的一切。还有一些关于使用 groupby 函数的详细信息,但是您很少需要使用它们。

感谢您的阅读。如果您有任何反馈,请告诉我。

掌握 Python 中的聚类方法

原文:https://towardsdatascience.com/mastering-clustering-methods-in-python-1cbb48054fc0?source=collection_archive---------21-----------------------

用于聚类分析的有用 Python 包

照片由像素上的 Pixabay 拍摄

聚类是根据共同特征分离数据不同部分的过程。包括零售、金融和医疗保健在内的不同行业使用聚类技术来完成各种分析任务。在零售业,聚类有助于识别不同的消费者群体,从而使公司能够根据消费者人口统计数据制作有针对性的广告,而这些数据可能过于复杂,无法进行人工检查。在金融领域,聚类可以检测不同形式的非法市场活动,如订单欺骗,交易者欺骗性地下大订单,迫使其他交易者购买或出售资产。在医疗保健领域,聚类方法已被用于计算患者成本模式、早发性神经障碍和癌症基因表达。

Python 为执行聚类分析提供了许多有用的工具。最佳工具的使用取决于手头的问题和可用数据的类型。Python 提供了三种广泛使用的技术:K 均值聚类、高斯混合模型和谱聚类。对于相对低维的任务(最多几十个输入),如识别不同的消费者群体,K-means 聚类是一个很好的选择。对于更复杂的任务,如非法市场活动检测,更强大和灵活的模型,如高斯混合模型将更适合。最后,对于可能有数千个输入的高维问题,谱聚类是最佳选择。

除了选择适合问题的聚类算法之外,您还需要有一种方法来评估这些聚类算法的性能。通常,距中心的平均类内距离用于评估模型性能。具体来说,每个观察值到聚类中心(质心)的平均距离用于度量聚类的紧密度。这是有意义的,因为好的聚类算法应该生成紧密打包在一起的数据组。聚类中的数据点彼此越接近,算法的结果就越好。根据使用的聚类数绘制的聚类内距离和是评估性能的常用方法。

出于我们的目的,我们将对商场客户细分数据进行客户细分分析。

读入数据

让我们从将数据读入熊猫数据框开始:

import pandas as pddf = pd.read_csv(“Mall_Customers.csv”)print(df.head())

作者图片

我们看到我们的数据非常简单。它包含一个包含客户 id、性别、年龄、收入的列,以及一个指定支出分数(从 1 到 100)的列。我们的聚类练习的目标是生成独特的客户群,其中该客户群的每个成员彼此之间比其他客户群的成员更相似。

K-均值聚类

K-means 聚类是一种无监督的机器学习,这意味着该算法只训练输入,不训练输出。它通过查找最接近的不同数据组(即聚类)来工作。具体来说,它将数据划分到聚类中,其中每个点都属于均值最接近该数据点的聚类。

让我们从 Scikit-learn 中的 clusters 模块导入 KMeans 类:

from sklearn.clusters import KMeans

接下来,让我们定义将用于 KMeans 聚类算法的输入。让我们用年龄和消费评分:

X = df[[‘Age’, ‘Spending Score (1–100)’]].copy()

我们需要做的下一件事是确定我们将使用的集群数量。我们将使用肘方法,该方法绘制了类内平方和(WCSS)与类数量的关系。我们需要定义一个包含 KMeans 类实例的 for 循环。这个 for 循环将迭代从 1 到 10 的簇号。我们还将初始化一个用于追加 WCSS 值的列表:

for i in range(1, 11): kmeans = KMeans(n_clusters=i, random_state=0) kmeans.fit(X)

然后,我们将 WCSS 值添加到列表中。我们通过 KMeans 对象的惯性属性来访问这些值:

for i in range(1, 11): kmeans = KMeans(n_clusters=i, random_state=0) kmeans.fit(X) wcss.append(kmeans.intertia_)

最后,我们可以画出 WCSS 和星团数量的关系图。首先,让我们导入 Matplotlib 和 Seaborn ,这将允许我们创建和格式化数据可视化:

import matplotlib.pyplot as pltimport seaborn as sns

让我们使用 Seaborn 来设计这些图的样式:

sns.set()

然后绘制 WCSS 与聚类图:

plt.plot(range(1, 11), wcss)

接下来,添加一个标题:

plt.title(‘Selecting the Number of Clusters using the Elbow Method’)

最后,标记轴:

plt.xlabel(‘Clusters’)plt.ylabel(‘WCSS’)plt.show()

作者图片

从该图中,我们可以看到 4 是最佳的集群数量,因为这是曲线“肘”出现的地方。

作者图片

我们可以看到 KMeans 发现了四个集群,它们是这样分解的:

  1. 消费水平适中的年轻顾客
  2. 消费得分高的年轻顾客
  3. 消费分数较低的中年客户
  4. 消费得分适中的高级客户

这种类型的信息对于希望锁定特定消费者群体的零售公司非常有用。例如,如果大多数高消费分数的人都比较年轻,公司可以通过广告和促销来瞄准年轻的消费者群体。

高斯混合模型(GMM)

该模型假设可以使用高斯分布对聚类进行建模。高斯分布,非正式地称为钟形曲线,是描述许多重要事情的函数,如人口高度、体重,甚至智商(IQ)。这些模型非常有用,因为高斯分布具有明确定义的属性,如均值、方差和协方差。平均值就是一个聚类中某个输入的平均值。方差测量单个输入值的波动。协方差是一个统计矩阵,描述了输入之间的相互关系(特别是它们如何一起变化)。总的来说,这些参数允许 GMM 算法灵活地识别复杂形状的聚类。例如,K-means 通常识别球形簇,而 GMM 可以更一般地识别不同形状的簇。这使得 GMM 在实践中比 K-means 更稳健。

让我们从 Scikit-learn 导入 GMM 包开始:

from sklearn.mixture import GaussianMixture

接下来,让我们初始化 GaussianMixture 类的一个实例。让我们从考虑三个集群开始,并使模型适合我们的输入(在这种情况下,年龄和支出分数):

from sklearn.mixture import GaussianMixturen_clusters = 3gmm_model = GaussianMixture(n_components=n_clusters)gmm_model.fit(X)

现在,让我们生成聚类标签,并将结果和我们的输入一起存储在一个新的数据框中:

cluster_labels = gmm_model.predict(X)X = pd.DataFrame(X)X[‘cluster’] = cluster_labels

接下来,让我们绘制一个 for 循环中的每个集群:

for k in range(0,n_clusters): data = X[X[“cluster”]==k] plt.scatter(data[“Age”],data[“Spending Score (1–100)”],c=color[k])

最后,格式化出图:

plt.title(“Clusters Identified by Guassian Mixture Model”)plt.ylabel(“Spending Score (1–100)”)plt.xlabel(“Age”)plt.show()

作者图片

红色和蓝色的集群看起来相对清晰。蓝色聚类是具有高消费分数的年轻客户,红色聚类是具有中等消费分数的年轻客户。绿色聚类的定义不太明确,因为它跨越所有年龄层,并且都有低到中等的支出分数。

现在,让我们尝试四个集群:

…n_clusters = 4gmm_model = GaussianMixture(n_components=n_clusters)…

作者图片

尽管四个集群显示出轻微的改善,但是红色和蓝色的集群在年龄和花费分值方面仍然相当宽泛。让我们尝试五个集群:

作者图片

五个集群在这里似乎是合适的。它们可以描述如下:

  1. 消费得分高的年轻客户(绿色)
  2. 消费得分适中的年轻客户(黑色)
  3. 低消费分数的年轻至中年客户(蓝色)
  4. 消费得分较低的中年至老年客户(黄色)
  5. 中等支出分数的中年至老年客户(红色)

高斯混合模型通常比 K-均值聚类更加健壮和灵活。这也是因为 GMM 能捕捉复杂的聚类形状,而 K-means 不能。这使得 GMM 能够准确地识别比 K-means 识别的球形簇更复杂的簇。对于中等大小和复杂性的数据集,GMM 是一种理想的方法,因为它能够更好地捕获具有复杂形状的数据集中的聚类。

光谱聚类

谱聚类是对高维且通常复杂的数据进行聚类分析的常用方法。它的工作原理是对输入进行降维,并在降维后的空间中生成聚类。由于我们的数据不包含很多输入,这主要是为了说明的目的,但是将这种方法应用于更复杂和更大的数据集应该很简单。

让我们从 Scikit-learn 中的 cluster 模块导入 SpectralClustering 类开始:

from sklearn.cluster import SpectralClustering

接下来,让我们用五个集群定义我们的 SpectralClustering 类实例:

spectral_cluster_model= SpectralClustering(n_clusters=5,random_state=25,n_neighbors=8,affinity=’nearest_neighbors’)

接下来,让我们为输入定义模型对象,并将结果存储在同一个数据框中:

X[‘cluster’] = spectral_cluster_model.fit_predict(X[[‘Age’, ‘Spending Score (1–100)’]])

最后,让我们绘制我们的集群:

fig, ax = plt.subplots()sns.scatterplot(x=’Age’, y=’Spending Score (1–100)’, data=X, hue=’cluster’, ax=ax)ax.set(title=’Spectral Clustering’)

作者图片

我们看到集群 1、2、3 和 4 非常明显,而集群 0 似乎非常宽泛。总的来说,我们在聚类组中看到一些与我们在克曼和 GMM 中看到的相同的模式,尽管以前的方法给出了更好的聚类之间的分离。同样,谱聚类更适合于涉及更大数据集(成百上千个输入和数百万行)的问题。

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

结论

虽然我们只在客户细分的背景下考虑了聚类分析,但聚类分析在很大程度上适用于各种行业。我们讨论的聚类方法已经被用来解决各种各样的问题。KMeans 聚类已用于识别易受感染的患者群体。高斯混合模型已被用于检测非法市场活动,如欺骗交易,泵和转储,以及报价填充。谱聚类方法已被用于解决复杂的医疗保健问题,如用于医疗保健知识发现的医学术语分组。

不管是什么行业,任何现代组织或公司都可以从他们的数据中发现重要的集群。Python 提供了许多易于实现的工具,用于在所有数据复杂性级别执行聚类分析。此外,对于任何数据科学家来说,充分了解在给定数据复杂性的情况下哪些方法最有效是一项非常宝贵的技能。我们所涵盖的内容为开始学习如何执行聚类分析的数据科学家提供了坚实的基础。

如果你有兴趣学习 python 编程的基础知识、Pandas 的数据操作以及 python 中的机器学习,请查看Python for Data Science and Machine Learning:Python 编程、Pandas 和 sci kit-初学者学习教程 。我希望你觉得这篇文章有用/有趣。

本帖原载于 内置博客 。原片可以在这里找到https://builtin.com/data-science/data-clustering-python

掌握数据科学不是学习一系列技巧

原文:https://towardsdatascience.com/mastering-data-science-is-not-learning-a-series-of-tricks-df66d8529c29?source=collection_archive---------36-----------------------

整体任务学习如何促进你的学习成长

许多在线课程将学习数据科学呈现为一系列技巧。首先,您学习一些基本的 Python,将其扩展到 Pandas,然后学习如何构建回归模型。然而,以我的经验来看,学习一些技巧和将这些技巧付诸实践以真正成为一名优秀的数据科学家之间有很大的差距。这里的问题是,数据科学是教育文献称之为复杂学习任务的 T2。在本文中,我从复杂学习的角度探讨了为什么数据科学如此难学。此外,我还提供了一些关于如何更有效地学习数据科学的技巧。

照片由蒂姆·莫斯霍尔德在 Unsplash 上拍摄

成为一名合格的数据科学家需要编程、统计和商业咨询等领域的技能。您需要执行的任务示例:

  • 评估客户的需求
  • 选择适合分析的数据
  • 将数据加载到 Python
  • 移除异常值
  • 使模型符合数据
  • 思考模型的结果

除了你需要学习的许多任务和技能之外,真正的附加价值不在于孤立地执行这些任务,而在于协调地执行它们,记住它们之间的所有相互作用。互动与反思、目标设定和自我监控等元认知技能相结合,会导致更复杂的高级任务,如:

  • 给定问题的领域和客户的需求,我如何填充缺失的值?
  • 我如何在几个模型选项中进行选择,同时记住我填写缺失值的方式?
  • 我的模型的奇怪结果是否意味着我不理解这个模型,或者我对这个领域不够理解?

交互和元认知技能使数据科学成为高度复杂的学习任务。但是我们如何掌握如此复杂的学习任务呢?传统的方法是按顺序教你每一项任务,最后把所有这些任务组合起来完成复杂的任务。这有助于管理学习过程中每一步的复杂性,但在很大程度上忽略了互动。另一种方法是使用整体任务学习:从一开始你就执行整个复杂的任务,包括互动。通过所谓的脚手架支持学习来管理复杂性。

我经常使用 CRISP-DM 循环作为我整个任务的基础。CRISP-DM 由许多步骤组成,从评估客户需求,到加载和分析数据,最后根据客户需求评估模型的结果。所以,在我教的每一堂课上,我们都要进行一个完整的 CRISP-DM 循环。支架包括向学生提供如何执行 CRISP-DM 步骤的详细说明,保持初始问题的简单性,甚至直接提供 CRISP-DM 过程中的完整步骤。通过慢慢地拆除脚手架,学生可以从头开始执行整个任务,但慢慢地学会更独立地工作和处理更复杂的问题。因此,我们仍然保持复杂性可管理,但也尊重复杂学习任务的复杂交互性质。

但是你能从中学到什么呢?我相信以下提示可以在您的数据科学之旅中对您有很大帮助:

  • 尽可能多的参与整体任务。当然你需要孤立地学习一些东西(比如说一个 Python 库),但是要很快回到整个任务上来。拥有业余爱好项目是一个很好的方法。选择一个您感兴趣的数据集,并尝试对其应用完整的 CRISP-DM 分析。试着先解决简单的问题,然后从那里开始逐步解决。为了强迫自己采取所有的步骤和它们的相互作用,以一篇短文的形式写下你的研究。写下 CRISP-DM 的每个步骤,并解释您在每个步骤中做了什么。除了写下每一步,试着把重点放在把这些步骤联系起来。Jupyter 笔记本是写这类论文的绝佳方式。
  • 找个好导师。与经验丰富的数据科学家讨论您的工作是学习互动和您需要的元认知技能的好方法。在你们的讨论中,不要关注解决方案是什么,而是关注如何解决问题。 how 包含了你的导师多年来学到的许多元认知解决方案。
  • 找一群同行和切磋。向其他人解释你的工作和推理,并回答他们的问题是你更深刻地反思自己工作的一个好方法。如果你问其他人问题,关注他们为什么做某事,而不是他们做了什么。

我相信这些建议可以让你加速成长为一名完全胜任的数据科学家。

我是谁?

我叫 Paul Hiemstra,是荷兰的一名教师和数据科学家。我是科学家和软件工程师的混合体,对与数据科学相关的一切都有广泛的兴趣。你可以在 medium 上关注我,或者在 LinkedIn 上关注我。

如果你喜欢这篇文章,你可能也会喜欢我的其他一些文章:

  • 没有像应用数据科学这样的数据科学
  • 牛郎星剧情解构:可视化气象数据的关联结构
  • 面向数据科学的高级函数式编程:用函数运算符构建代码架构

掌握 Python 中的数据可视化

原文:https://towardsdatascience.com/mastering-data-visualization-in-python-7d52e9f24c5c?source=collection_archive---------37-----------------------

使用 Seaborn 和 Matplotlib 进行数据探索

照片由卢卡斯在像素上拍摄

数据可视化对于许多分析任务都很重要,包括数据汇总、探索性数据分析和模型输出分析。与他人交流你的发现的最简单的方法之一是通过良好的可视化。幸运的是,Python 提供了许多库,为从数据中获得洞察力提供了有用的工具。其中最著名的是 Matplotlib,它使用户能够生成可视化效果,如直方图、散点图、条形图、饼图等等。

Seaborn 是构建在 Matplotlib 之上的另一个有用的可视化库。它提供的数据可视化通常更加美观,在统计上更加复杂。对于任何数据科学家或数据分析师来说,充分了解如何使用这两个库都是至关重要的,因为它们都提供了可视化数据的简单方法。

Matplotlib

使用 Matplotlib 生成直方图

Matplotlib 提供了许多现成的工具,用于快速轻松地进行数据可视化。例如,当分析一个新的数据集时,研究人员通常对一组列的值的分布感兴趣。一种方法是通过直方图。直方图是通过基于设定范围选择值并将每组值放入容器或桶中而生成的分布的近似值。使用 Matplotlib 可以直接将分布可视化为直方图。

出于我们的目的,我们将使用 FIFA19 数据集,您可以在这里找到。

首先,我们需要导入 Pandas 库,这是一个 Python 库,用于统计分析和数据争论等数据任务:

import pandas as pd

接下来,我们需要从 Matplotlib 库中导入 pyplot 模块。习惯上将其作为 plt 导入:

import matplotlib.pyplot as plt

现在,让我们将数据读入熊猫数据帧。我们将在 Pandas 中使用 set_option()方法放宽对显示列和行的限制:

df = pd.read_csv("fifa19.csv")
pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', None)

让我们使用 head()方法显示前五行数据:

print(df.head())

作者截图

我们可以通过调用 plt 对象上的 hist()方法并在数据框中传递选定的列来为任何数字列生成直方图。让我们为“总体”列执行此操作,该列对应于总体玩家评级:

plt.hist(df['Overall'])

作者截图

我们还可以分别使用 xlabel()、ylabel()和 title()方法标记绘图的 x 轴、y 轴和标题:

plt.xlabel('Overall')
plt.ylabel('Frequency')
plt.title('Histogram of Overall Rating')
plt.show()

作者截图

这种可视化是了解数据中的值是如何分布的,以及轻松查看哪些值出现频率最高和最低的好方法。

使用 Matplotlib 生成散点图

散点图是一种有用的数据可视化工具,有助于识别变量相关性。例如,如果我们有兴趣了解工资和玩家总体评分之间是否存在正相关关系,(例如,如果玩家的工资增加,他的评分是否也会上升?)我们可以利用散点图可视化来获得洞察力。

在生成工资与总体评分的散点图之前,让我们将工资列从字符串转换为浮点数字列。为此,我们将创建一个名为 wage_euro 的新列:

df['wage_euro'] = df['Wage'].str.strip('€')
df['wage_euro'] = df['wage_euro'].str.strip('K')
df['wage_euro'] = df['wage_euro'].astype(float)*1000.0

现在,让我们显示我们的新列 wage_euro 和总体列:

print(df[['Overall', 'wage_euro']].head())

作者截图

要在 Matplotlib 中生成散点图,我们只需在 plt 对象上使用 scatter()方法。让我们也标记轴,并给我们的图一个标题:

plt.scatter(df['Overall'], df['wage_euro'])
plt.title('Overall vs. Wage')
plt.ylabel('Wage')
plt.xlabel('Overall')
plt.show()

作者截图

用 Matplotlib 生成条形图

条形图是分析数据类别的另一个有用的可视化工具。例如,如果我们想查看 FIFA19 数据集中最常见的国籍,我们可以使用条形图。为了可视化分类列,我们首先应该对值进行计数。我们可以使用集合模块中的 counter 方法为分类列中的每个类别生成一个计数值字典。让我们为国籍列这样做:

from collections import Counter
print(Counter(df[‘Nationality’]))

作者截图

我们可以用最常用的方法过滤这个字典。我们来看十个最常见的国籍值(注意:也可以使用 least_common 方法分析不常见的国籍值):

print(dict(Counter(df[‘Nationality’]).most_common(10)))

作者截图

最后,为了生成十个最常见的国籍值的条形图,我们简单地调用 plt 对象上的 bar 方法,并传入字典的键和值:

nationality_dict = dict(Counter(df['Nationality']).most_common(10))
plt.bar(nationality_dict.keys(), nationality_dict.values())
plt.xlabel('Nationality')
plt.ylabel('Frequency')
plt.title('Bar Plot of Ten Most Common Nationalities')
plt.show()

作者截图

正如您所看到的,x 轴上的值是重叠的,这使得它们很难看到。我们可以使用“xticks()”方法来旋转这些值:

plt.xticks(rotation=90)

作者截图

用 Matplotlib 生成饼图

饼图是直观显示数据比例的有用方法。比如在这个数据集中,我们可以用一个饼状图来形象化的展示英格兰、德国和西班牙球员的比例。

为此,让我们创建包含英格兰、西班牙和德国的新列,并为所有其他国家创建一个标记为“其他”的列:

df.loc[df.Nationality =='England',  'Nationality2'] = 'England'
df.loc[df.Nationality =='Spain',  'Nationality2'] = 'Spain'
df.loc[df.Nationality =='Germany',  'Nationality2'] = 'Germany'
df.loc[~df.Nationality.isin(['England', 'German', 'Spain']),  'Nationality2'] = 'Other'

接下来,让我们创建一个字典,其中包含每一项的比例值:

prop = dict(Counter(df['Nationality2']))
for key, values in prop.items():
    prop[key] = (values)/len(df)*100
    print(prop)

作者截图

我们可以使用我们的字典和 Matplotlib 中的 pie 方法创建一个饼图:

fig1, ax1 = plt.subplots()
ax1.pie(prop.values(), labels=prop.keys(), autopct=’%1.1f%%’,shadow=True, startangle=90)
ax1.axis(‘equal’) 
plt.show()

作者截图

正如我们所看到的,所有这些方法都为我们提供了多种强大的方式来可视化数据中类别的比例。

使用 Seaborn 进行数据可视化

格式化 Matplotlib 图

Seaborn 是一个构建在 Matplotlib 之上的库,支持更复杂的可视化和美观的绘图格式。一旦您掌握了 Matplotlib,您可能想要迁移到 Seaborn 进行更复杂的可视化。

例如,简单地使用 Seaborn set()方法可以极大地改善 Matplotlib 图的外观。让我们来看看。

首先,将 Seaborn 作为 sns 导入,并重新格式化我们生成的所有图形。在脚本的顶部,编写以下代码并重新运行:

import seaborn as sns
sns.set()

作者截图

用 Seaborn 生成直方图

我们还可以使用 Seaborn 生成与在 Matplotlib 中相同的可视化效果。

为了重新生成整个列的直方图,我们对 Seaborn 对象使用 distplot 方法:

sns.distplot(df['Overall'])

作者截图

我们可以重用 plt 对象来进行额外的轴格式化和标题设置:

plt.xlabel(‘Overall’)
plt.ylabel(‘Frequency’)
plt.title(‘Histogram of Overall Rating’)
plt.show()

作者截图

正如我们所看到的,这个图看起来比 Matplotlib 图更吸引人。

用 Seaborn 生成散点图

Seaborn 还使得散点图的生成变得简单明了。让我们重新创建之前的散点图:

sns.scatterplot(df['Overall'], df['wage_euro'])
plt.title('Overall vs. Wage')
plt.ylabel('Wage')
plt.xlabel('Overall')
plt.show()

作者截图

使用 Seaborn 生成热图

Seaborn 还以制作相关热图而闻名,这种热图可用于确定变量相关性。要生成一个,首先我们需要计算一组数字列之间的相关性。让我们对年龄、整体、工资欧元和技能变动进行分析:

corr = df[['Overall', 'Age', 'wage_euro', 'Skill Moves']].corr()
sns.heatmap(corr, annot=True)
plt.title('Heatmap of Overall, Age, wage_euro, and Skill Moves')
plt.show()

作者截图

我们还可以将 annot for annotate 设置为 true 来查看相关值:

sns.heatmap(corr, annot=True)

作者截图

用 Seaborn 生成 Pairs 图

我将讨论的最后一个 Seaborn 工具是 pairplot 方法。这允许您为一组数字特征生成分布矩阵和散点图。让我们从年龄、整体和潜力方面来看:

data = df[[‘Overall’, ‘Age’, ‘Potential’,]]
sns.pairplot(data)
plt.show()

作者截图

正如您所看到的,这是一种通过散点图直观显示数值分布和变量之间关系的快速简便的方法。

结论

总的来说,Seaborn 和 Matplotlib 对于任何数据科学家来说都是有价值的工具。Matplotlib 使标记、标题和格式化图形变得简单,这对有效的数据通信很重要。此外,它还提供了许多可视化数据的基本工具,包括直方图、散点图、饼图和条形图。

Seaborn 是一个需要了解的重要库,因为它有漂亮的视觉效果和广泛的统计工具。就像我们在这篇文章中展示的那样,在 Seaborn 中生成的图,即使它们传达了相同的信息,也比在 Matplotlib 中生成的图漂亮得多。此外,Seaborn 提供的工具允许更复杂的分析和可视化。虽然我只讨论了如何使用 Seaborn 生成热图和成对图,但它也可以用于生成更复杂的视觉效果,如变量的密度图、带置信区间的线图、聚类图等等。

Matplotlib 和 Seaborn 是 Python 中使用最广泛的两个可视化库。它们都允许您快速执行数据可视化,以获得统计见解和用数据讲述故事。虽然这两个库的使用案例有很大的重叠,但了解这两个库可以让数据科学家生成漂亮的视觉效果,讲述关于正在分析的数据的有影响力的故事。

如果你有兴趣学习 python 编程的基础知识、Pandas 的数据操作以及 python 中的机器学习,请查看Python for Data Science and Machine Learning:Python 编程、Pandas 和 sci kit-初学者学习教程 。我希望你觉得这篇文章有用/有趣。

本帖原载于 内置博客 。原片可以在这里找到https://builtin.com/machine-learning/scikit-learn-guide

掌握 Python 中的字典和集合!

原文:https://towardsdatascience.com/mastering-dictionaries-and-sets-in-python-6e30b0e2011f?source=collection_archive---------13-----------------------

通过代码和示例理解 Python 中的字典和集合的概念

照片由 Unsplash 上的尼克·费因斯拍摄

Python 编程语言是一种面向对象的编程语言,它为我们提供了各种简单而优雅的操作来执行众多任务。

Python 的许多这样的特征之一是数据结构的可用性,用于代码的紧凑性以及计算各种操作。

数据结构是以某种方式组织起来的数据元素的集合。数据结构是任何编程语言的核心,对于 Python 也是如此。

Python 中有很多内置的数据结构。这些范围包括列表、字典、集合、字符串、冻结集合和元组。对于不同的任务,它们都有自己独特的用例。

在本文中,我们将关注两种这样的数据结构,即字典和集合。我们将关注对这些代码概念的核心理解,并获得在编码时如何以及何时使用数据结构的这些方面的直觉。

我们将从分析集合数据结构和集合的核心概念开始。然后,我们还将简要讨论冻结集的主题,最后,完全详细地理解字典的概念。我将提供 GitHub gist 的所有代码在这篇文章中,你可以访问。

我在以前的一篇文章中已经详细讨论了列表的主题。你可以通过下面提供的链接来阅读这篇文章。它涵盖了 Python 中列表的大部分重要方面和需求。

设置

由卡洛斯·穆扎在 Unsplash 上拍摄的照片

Set 是 Python 中的一种数据结构,包含一组无序且未索引的元素。集合中的每个元素总是唯一的。集合数据结构不允许任何重复的元素。

就可变性而言,集合类似于列表数据结构。集合和列表都是可变的。这意味着可以从这些创建的“集合”数据结构中添加或删除元素。

集合可用于执行数学集合运算,如并、交、对称差等。因此,它们被用于数学计算,使其成为执行特定操作的整体重要数据结构。

现在我们对集合有了基本的了解,让我们继续了解集合的一些基本运算和功能。我们将介绍掌握集合数据结构所需的大部分基本概念。

器械包的创建:

创建集合有两种主要方法。一种方法是使用 Python 中的 set 函数。另一种方法是使用花括号' '并键入各种元素的列表。

创建的集合不能被索引,因为它们是无序元素。如果有任何元素在集合中重复,则不会考虑它,并且会忽略它。集合中的元素总是按升序排列。

让我们用几个代码块来探讨这个主题,以便更好地理解这个主题。

输出:

set([1, 2, 3])
<type 'set'>

上面描述的代码块为我们提供了创建一组元素的方法之一。我们可以注意到,我们已经利用花括号创建了一个列表。type 函数用于指示用户定义的变量“s”现在属于类“set”。

在下一个代码块中,我们将看看创建集合的另一个方法。

输出:

{1, 2, 3, 4, 8}

在创建集合的第二种方法中,我们可以注意到,我试图实现一个集合,并提供了元素 2 的重复,并以一种无组织的方式排列元素的顺序。但是,set 函数删除了所有重复的元素,并以有序的升序排列所有元素。

注意: 试图对集合中的一个元素进行索引是不可能的,因为它是无序且未被索引的。尝试这样做将导致以下错误。

TypeError: 'set' object does not support indexing

向集合中添加元素:

我们已经讨论过集合数据结构是可变的。因此,可以向集合中添加额外的元素。执行此操作有两种主要方法。我们可以用 Set 数据结构中提供的 add()和 update()函数来确认这一点。

add()方法将元素添加到集合中。如果元素已经存在于集合中,那么下面的函数将跳过添加操作,不需要进一步的步骤。

让我们来看看集合数据结构中 add()函数的操作。

输出:

{1, 2, 3}

在上面的代码块中,我们可以注意到最初定义的集合有元素 1 和 2。通过使用 add()函数,我们能够向现有的集合中添加一个额外的元素 3。

在下一个代码块中,我们将查看 update()函数,该函数可用于向集合中添加多个元素。如果有任何重复,那么这些元素将被忽略。

输出:

{1, 2, 3, 4, 5, 6}

上面代码块中描述的 update()函数可以向特定的集合中添加多个元素。与 add()函数类似,元素的任何重复都被忽略,所有元素都按升序排列。

从集合中移除元素:

我们已经多次确定集合是可变的,因此甚至从集合中移除元素也是可能的。从集合中移除元素主要有三种方式,即 remove()、discard()和 pop。让我们看看下面的代码块和从下面代码行接收到的相应输出,以确定这些函数到底是如何工作的。

输出:

{1, 2, 3, 4}
{1, 2, 3, 4}
{2, 3, 4, 5}

remove 函数用于从集合中删除预先存在的元素。如果您试图删除的元素不在集合中,您将收到一个键错误。

为了解决这个问题,我们使用了丢弃功能。如果集合中存在的元素存在,则移除该元素。如果该元素在集合中不存在,则操作正常进行,没有任何错误。

pop()函数用于从集合中随机删除一个元素。分配给变量的 pop()方法会产生一个随机值,然后您可以获取 pop()方法将移除的元素返回给变量。请参考下面的例子,以便更清楚地理解这句话。

x = s.pop()
Here, 'x' would store the value of 1.

器械包的其他重要功能:

集合数据结构允许用户执行各种独特的数学计算和操作。这些运算很容易被在高中读过集合概念和维恩图的观众理解。

让我们分析一些可以用集合数据结构中可用的各种函数执行的操作。

1.联盟

集合集合的并集是集合中所有元素的集合。在 Python 集合中,我们可以使用“|”操作符或 union 函数来执行这个操作。该操作可通过以下方式计算:

输出:

{1, 2, 3, 4, 5}
{1, 2, 3, 4, 5}

2.交叉点:

交集函数用于从两个特定的集合中获取相似的元素。在 Python 集合中,我们可以使用' & '运算符或交集函数来执行这个操作。该操作可通过以下方式计算:

输出:

{3}
{3}

3.设置差异和其他操作:

集合差是只在集合 1 中而不在集合 2 中的元素的集合。该操作可以按如下方式执行:

输出:

{1, 2}
{1, 2}

对称差异是集合 1 和集合 2 中的元素集合,除了在两者中公共的元素。该操作可以按如下方式执行:

输出:

{1, 2, 4, 5}
{1, 2, 4, 5}

至此,我们已经涵盖了集合的大部分基本概念,并可以继续学习其他数据结构。

冻结集:

照片由 Aaron Burden 在 Unsplash

冻结集合与集合数据类型相似,并且具有相似的集合特征,但是一旦分配了相应的元素,就不能添加、修改、删除或更新这些数据结构。冻结集的属性是它们是不可变的。

集合和冻结集合之间的相似性类似于列表和元组。虽然元组是不可变列表,但是冻结集合是不可变集合。可以使用 frozenset()函数创建冻结集。这可以根据下面提供的代码块按如下方式完成。

输出:

frozenset({1, 2, 3, 4})
<class 'frozenset'>

上面的代码块和输出是如何定义冻结集数据结构的表示。type 函数用于指示用户定义的变量‘s’现在属于类‘frozenset’。

可变集合是不可散列的,所以它们不能用作字典键。另一方面,冻结集是可散列的,可以用作字典的键。

该数据类型支持 copy()、difference()、intersection()、isdisjoint()、issubset()、issuperset()、symmetric_difference()和 union()等方法。由于是不可变的,它没有添加或移除元素的方法。

冻结集中不支持添加或删除元素。类似于集合数据结构的冻结集合也不支持索引。尝试这样做时会导致以下错误。

AttributeError: 'frozenset' object has no attribute 'add'
TypeError: 'frozenset' object does not support indexing

冻结集合执行的操作类似于上一节提到的其他集合功能。因此,我强烈推荐检查集合的其他功能,因为它非常类似于冻结的集合,并且可以相应地执行下面的计算。

注: 冻结集相对来说比集合和字典要小。该函数在 Python 编程或数据科学中并不常用,因为它的用途有限。然而,它仍然是一个很好理解的数据结构,因为您可以使用它执行独特的操作。

词典:

照片由皮斯特恒在 Unsplash 上拍摄

Python 中的字典数据结构是一个无序的条目集合。其他数据结构只使用一个值作为元素,而字典是一个稍微复杂一些的数据结构。它使用两个元素,即一对元素,即一个键和值。

让我们看看字典数据结构的一些主要功能和方法。本文将涵盖初学者词典的大部分重要方面。

字典数据结构的创建:

字典数据结构可以通过两种主要方式创建。创建字典的第一种方法是在花括号' '中定义一个键和值元素,另一种方法是调用 Python 中可用的 dict()函数。

让我们看看下面的两个代码块,以理解字典的创建以及执行下面的操作的各种可能性。这些代码大多带有注释行,不言自明。但是,如果你对这个话题有任何疑问,请在下面的评论中告诉我。

方法 1:

输出:

{1: 'A', 2: 'B'}
{'name': 'X', 'age': 10}
{'name': 'X', 1: ['A', 'B']}

方法 2:

输出:

{1: 'A', 2: 'B'}

您可以使用相应的键来访问字典值。要访问字典数据集中的特定值,需要索引正确的键。Python 中的字典是可变的,可以相应地添加、删除、修改和更改字典中的元素。

在字典中添加和删除元素:

如前所述,Python 中的字典是可变的,可以相应地添加、删除、修改和更改字典中的元素。下面的代码块是向字典中添加更多元素或从字典中删除特定元素的完整表示。

输出:

{1: 'A', 2: 'B', 3: 'C'}
{1: 'A', 3: 'C'}

添加和删除元素或索引字典数据结构的各种元素的过程非常简单。它可以很容易地完成和处理,如上面的代码行所示。

字典中的其他重要功能:

字典数据结构提供了各种各样的函数和方法。下面的代码块可以用来访问字典中所有可用的方法和属性。字典有很多重要的方面和功能。在本文中,我们将只关注编程和数据科学领域中经常使用的几个主要工具。

输出:

['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']

上面的输出显示了字典数据结构中可用的许多方法和函数。其中一些非常有用,在 Python 编程和数据科学领域非常重要。让我们介绍一下字典数据结构中最常用的三个函数。这种表示可以从下面的代码块中看出。

输出:

dict_items([(1, 'A'), (2, 'B'), (3, 'C')])
dict_keys([1, 2, 3])
dict_values(['A', 'B', 'C'])

完成了字典数据结构中可用的众多函数方法的一些重要方面后,让我们进入字典理解的最后一个主题。

字典理解:

字典理解提供了一种创建字典的简洁方法。下面的代码块展示了字典理解是如何优雅而简洁地创建字典的。

我强烈推荐查看我以前的列表文章中的列表理解部分,以及查看其他文章,以更详细地了解您可以使用字典理解做的惊人事情。

字典数据结构在机器学习中的主要用途之一是它与 panda 模块的兼容性。字典数据结构及其键和它们各自的值可以转换成 pandas 数据帧,该数据帧可以用于机器学习或深度学习建模。

因此,字典数据结构在数据科学领域有多种用途。它不仅提供了编程的基本功能,还提供了创建独特和优秀的 Python 和数据科学项目的选项。

结论:

照片由🇸🇮·扬科·菲利在 Unsplash 上拍摄

Python 是一种通用且简单的编程语言,它为用户提供了高级的交互性。它提供了使用大量数据结构的选项列表,这些数据结构可用于执行各种任务。这些数据结构的用途从创建简单的 Python 项目到复杂的机器学习和深度学习计算。

在本文中,我们讨论了三种这样的数据结构,即集合、冻结集合和字典,它们在 Python 编程领域找到了自己的用例。这些是理解 Python 编码语言的一些核心概念。

在这本简明而详细的指南中,我们已经介绍了大多数基本的功能和操作,这些功能和操作都可以用提到的每种数据结构来执行。为了更好地理解,集合、冻结集合和字典的大多数重要功能都用它们各自的代码块进行了解释。

如果您认为我错过了您希望我在未来的文章中介绍的任何概念,请随时告诉我。此外,如果您对文章中提到的要点有任何其他疑问,请随时在下面评论,我会尽快回复您。

看看我的其他一些文章,你可能会喜欢读!

</5-best-ways-to-earn-income-from-data-science-a9c8fed1eee6> </10-best-tools-and-technologies-for-data-science-e335fb99c2f2> </15-awesome-python-and-data-science-projects-for-2021-and-beyond-64acf7930c20>

谢谢你们坚持到最后。我希望你们喜欢阅读这篇文章。我希望你们都有美好的一天!

掌握 Facebooks 指标变化数据科学面试问题第 1/2 部分

原文:https://towardsdatascience.com/mastering-facebooks-metric-change-data-science-interview-question-a093426d3bbf?source=collection_archive---------9-----------------------

闯入 FAANG 的数据科学面试准备系列

上周介绍了一个 回答数据科学度量变化面试问题 的指南。但是这个指南不仅仅是一个假设面试的框架。这正是我在脸书大学第一轮数据科学面试中获胜所用的框架。

所以这周,我想把理论付诸实践。我上了 Glassdoor (正如一个人所做的那样),围绕一个度量变化提出了最近的数据科学面试问题(截至本文撰写之日)。在本文中,我将带您了解如何将指标变化框架应用于脸书的数据科学面试问题。

您会注意到,点击脸书活动搜索结果的用户百分比每周都增加了 10%。你会如何调查?

【来源:玻璃门

一、澄清

什么都不要假设,如果事情不是绝对明显的,那就问。

你会注意到,点击了关于脸书事件的搜索结果的*的用户的百分比同比增加了 10%。你会如何调查?*****

受访者

所以澄清一下:

  • 搜索结果 是指当用户在脸书的搜索栏中搜索某样东西时?这些搜索结果可以是不同的类别,比如脸书事件、页面或群组?

截图来自作者手机

  • 当我们查看点击关于脸书事件 的 搜索结果 用户的百分比时,我们的意思是:在进行搜索并看到搜索结果列表的用户中,点击围绕脸书事件(而不是群组、页面等)的结果的用户的比例是多少?因此,如果 100 个用户搜索一个术语,其中 50 人点击了与脸书事件相关的结果,那么用户百分比将是 50%?
  • 用户百分比周环比 10%的增长意味着,如果上周 100 个用户搜索一个词,其中 50 个点击了脸书事件相关的结果,那么本周 200 个用户可能搜索了一个词,但 110 个点击了脸书事件相关的结果?所以现在用户的百分比是 55%(即比上周增加了 10%)。**
  • 最后,鉴于这一点,我们的目标是试图找出是什么导致了这种增长?

面试官

以上都是肯定的。

提示

  • 重要的是不仅要澄清问题,还要重申目标。

二。假设

受访者

在我深入研究尖峰信号的来源之前,我想概述几个高层次的假设。广义地说,原因可能是内部的,也可能是外部的。**

潜在的内部原因可能包括我们最近对排名算法进行的更新,以在搜索结果列表的顶部显示事件。或者,在我们的日志记录中甚至可能有一个错误,这意味着我们没有正确地捕获我们的点击计数,因此可能没有事件相关点击的实际增加。

潜在的外部原因可能包括重大事件,如节日季节,导致用户对事件相关的搜索结果表现出更大的兴趣。

为了测试这些高层次的假设,并揭示其他潜在的原因,我想在我要探索的数据中浏览一下。这听起来合理吗?

面试官

是的。

提示

  • 使用假设这个词。把它当成你的工作来使用。因为如果你面试成功了,就真的会成功。
  • 不断寻求反馈。这是对话,不是独白。如果你不定期停下来和面试官交流,你会:
  1. 不知道你是否正朝着正确的方向前进,或者是否有你可能错过的东西
  2. 你可能会失去面试官的注意力和兴趣(他们也是人)

III。诊断

受访者

我要考虑的第一个数据点是 时间 的作用。

我会检查是否:

  • 这种每周 10%的增长确实意味着人们对事件的兴趣越来越大。在过去的几周里,周与周之间有什么变化(例如,我们的趋势如何)?如果前一周我们看到了 20%的增长,那么本周的数字可能实际上表明了兴趣的下降。
  • 我还想检查一下的增长是否均匀地分布在一周中,或者它是否是一天中大量点击的结果。如果是后者,这可能表明我们的日志系统中有一个 bug,或者当天发生了一个大事件。
  • 季节性起作用吗?去年这个时候发生了什么?例如,如果学校放假,学生进入暑假模式,那么我们实际上可能会看到学生对寻找事情做的活动越来越感兴趣。在这种情况下,我们很可能会在去年的这个时候看到类似的峰值。

面试官

有道理。你还会看什么?

受访者

接下来,我想看看这 10%的增长是如何分解的:

  • 这种变化是集中在特定区域还是在全球范围内均匀分布?例如,我们正慢慢走出疫情,一些城市已经开始重新开放。在这种情况下,对事件的兴趣上升可能只集中在那些城市。
  • 我们的其他产品或服务中有类似的增长吗?如果人们对活动的兴趣上升,我们会看到 Instagram 或脸书故事的类似增长吗,因为参加这些活动的用户会有更多的内容可以发布?
  • 这种增长是否在移动设备和桌面设备之间平均分布?如果我们认为我们的日志中有一个错误,或者我们对我们的排名系统进行了更改,这将是非常有用的。错误或更新通常发生在特定的操作系统上。因此,如果我们看到变化不成比例地偏向 macOS、windows、iOS 或 android 中的一个,那么它可能会证实我们最初的假设,即变化是由内部原因驱动的。
  • 其他搜索结果类别,如组和页面,是如何受到影响的?如果用户点击事件的百分比在上升,那么我们显然是在从这些其他类别中分食参与度。有没有一个特定的类别是我们从中分食的,或者它是平均分布的?例如,是否只有以前点击组(而不是页面)的用户现在才点击事件?这可能表明我们对搜索结果中的群组排名进行了更改。我们降低它的排名了吗?还是不小心完全去掉了?****
  • 我想了解的其他几个细分市场是该指标在以下方面的变化:

****—事件类型:增加的兴趣是否集中在某一类事件上?喜欢音乐?在这种情况下,它可能为节日前后的早期假设提供证据。

****—用户统计:兴趣的增加是否主要来自在校的年轻用户(为学生暑假前后的早期假设提供证据)?

面试官

  • 假设我们按照你之前的建议对我们的排名算法进行了更新,现在为了支持事件而降低了组的排名。

提示

  • 注意,我 不仅说了 我想看什么数据,还说了为什么我认为它会有用 。弥合这一差距是关键。想象一下,如果你只是重复框架而不考虑如何独特地应用于被问的问题。然后,当面试官问你‘嗯,这很有意思,你为什么会关注移动和桌面之间的爆发……’的时候,你无法回答他,你就可以认为面试结束了(开个玩笑,算是吧)。
  • 我没有提出行业和竞争对手的影响,尽管它在框架中,因为我认为它不适用于这个场景。 框架 是指南,不是药方。由你来决定什么是相关的。你是怎么做到的?实践(我知道)。

三。解决

受访者

有意思。看起来我们有意决定增加事件的可见性,10%的增长是其结果。 我们为什么要做出这样的改变?

面试官

你认为我们为什么要做出这样的改变?

提示

  • 如果面试官没有结束面试,即使你认为你已经‘回答’了这个问题,那么深入挖掘**。公平的警告:他们很可能会把你的问题转给你。**
  • 《五个为什么》是一个很好的参考,可以让你更深入地挖掘。

受访者

我的假设是,我们改变了排名算法,因为我们想鼓励人们去参加活动。因此,我认为接下来我们要考虑的两件事是:

  1. 我们成功了吗?仅仅因为人们点击搜索结果中的活动,就意味着他们正在参加这些活动吗?
  2. 如果我们成功了,考虑到我们正在从群组和页面等其他产品中分食,这种变化是好是坏

面试官

有道理。如果我们成功了,你会怎么做?你说的好是什么意思?

小费

**在这个框架中,我介绍了在您诊断出指标可能发生变化的原因后,可能采取的后续步骤。虽然我写的是“变化”,但大部分都与公制下降有关。但是如果度量实际上增加了,那么你会怎么做?

到目前为止我们学到了什么

  1. 数学家阿尔弗雷德·科济布斯基(Alfred Korzybski)在 1931 年首次引入了短语‘地图不是实际的领土’。快进到 90 年后,世界上最大的科技公司每天都在处理这一问题。**指标是有形的、可操作的,是模糊的、定性目标的代理。这意味着,通常情况下,指标并不完美。即使当指标朝着我们希望的方向发展时,我们也需要验证这是否直接转化为实现我们制定的最初目标。这就是识别和分析支持指标的地方。****
  2. 如果我们确实发现这个指标事实上代表了我们的目标,那么确定这些目标是否是好的目标就很重要了。这意味着退一步,变得有点元,并确定什么是的意思。最好的方法是将这些目标与最初的使命宣言联系起来,并问‘这些目标与公司最初的使命一致吗?’****

在下一篇文章中,我们将更深入地探讨(1)和(2)的策略,并与我们的 ghost 面试官一起完成我们对用户对脸书事件相关搜索结果越来越感兴趣这一谜团的调查。

未完待续。

授权 Hani Azam 使用 Adobe Stock 提供的图片

这是更大的 数据科学面试备考系列 的一部分。在每篇文章中,我都会围绕机器学习、&商业意识、统计&概率和 SQL 深入面试问题。****

我还将分享我的个人经历,包括我如何进入数据科学的,我的工资我如何管理我的财务,以及我最喜欢的学习和灵感资源。您可以通过在媒体上关注我来了解最新消息。

如果你想联系我,你可以在 LinkedIn 上找到我,或者直接发邮件给我。

掌握 Facebooks 指标变化数据科学面试问题第 2/2 部分

原文:https://towardsdatascience.com/mastering-facebooks-metric-change-data-science-interview-question-part-2-2-8bf61df63828?source=collection_archive---------26-----------------------

闯入 FAANG 的数据科学面试准备系列

上一次时间,我们正在解决脸书大学提出的以下数据科学面试问题:

您会注意到,点击脸书活动搜索结果的用户百分比每周增加了 10%。你会如何调查?

【来源:玻璃门

我们 得出结论,搜索结果的排序算法已经改变 为有利于事件的分组排序。结果,点击事件的用户百分比每周增加了 10%。

我们假设脸书有意改变排名算法,以鼓励更多用户参加活动。这给我们留下了两个问题,我们今天将讨论这两个问题:

  1. 仅仅因为点击事件的人数增加,这是否意味着实际参加这些事件的人数也增加了?
  2. 如果更多的人参加活动,这是一件好事吗?尤其是如果这是以牺牲他们与其他脸书产品的互动为代价的话,比如参加团体活动?因为记住…

时间是有限的,如果人们在一件事情上做得越多,他们在另一件事情上做得就越少。

1.我们如何判断是否有更多的人去参加活动?

并非所有在事件相关搜索结果上点击的用户实际上都可能参加该事件。事实上,我们可能永远无法 100%确定用户是否参加了活动。然而,我们可以做出一个有根据的猜测,一些用户是否比其他人更有可能参加。

用户旅程制作于异想天开

考虑上面的用户之旅以及您自己在脸书活动中的经历。考虑到这两点,我们可以做出假设用户越是参与某个事件,(也就是说,他们在上面的用户旅程中“是”的兔子洞越往下走)就越有可能参加那个事件。

我们可以通过以下几个方面来衡量用户参与活动的程度:

作者手机截图

事件发生前

  1. 用户是否在活动页面点击了“感兴趣”、“去”、“分享”或“添加到日历”?
  2. 用户在活动页面上发帖了吗?
  3. 用户是否与事件相关的通知进行了交互?
  4. 用户是否购买了活动的门票?

活动期间

  1. 在事件发生时,我们是否看到用户在事件发生的同一位置(假设我们可以访问他们的位置数据)?
  2. 活动时,用户是否经常去活动页面?例如,检查事件地址和其他细节。
  3. 用户是否在活动期间发布了很多故事?

事后

  1. 用户是否张贴了照片并标记了举办活动的地点?
  2. 用户在活动页面上发帖了吗?例如,感谢活动主持人。
  3. 活动页面有没有贴照片,给用户贴标签?

这些问题的答案可以帮助我们更有信心地确定用户是否真的参加了活动。

一种复杂的(也是更精确的)方法是对其中一些问题的答案进行加权组合。但是为了简单起见,让我们假设一个用户在一个事件页面上点击感兴趣是他们是否参加那个事件**的最接近的代理。**我们可以比较算法改变前后点击对某个事件感兴趣的用户数量。如果后者大于前者,则可能表明由于算法改变,参加事件的用户数量增加。

💡提示:保持简单。面试官想知道,如果有必要,你可以潜得更深。这就是他们必然会问你的后续问题的目的。但是在他们催促你之前,保持简单明了。

2.如果更多的人去参加活动,这是一件好事吗?

首先,到底是什么意思?答案在于公司的使命。一个公司的使命解释了它为什么存在,它的总体目标是什么。

公司的“使命”也是他们用来指导“好”的含义(对他们而言)的东西。

脸书的使命是给予人们建设社区的力量,让世界更加紧密。但是这是相当模糊的。如果越来越多的人参加活动,这是否意味着世界上更多的人走得更近了?也许吧。在拉近人与人之间的距离方面,活动比其他功能(如群组)更有效吗?也许吧。

我们如何才能使这一点更加具体?更可测度量标准。

我将在以后的文章中详细阐述度量标准。但现在你需要知道的是:

指标的存在是为了量化(或测量)朝着更大目标的进展(以具体的方式)。

您可以使用**功能级别的指标,**比如点击事件相关搜索结果的用户数量。您还可以拥有 aggregate,**公司级指标。**这些可以是:

  • 使用你的产品的总人数,例如使用脸书的总人数(不仅仅是点击事件)。
  • 返回到你的产品的总人数*(也称为用户保持率)。*
  • 人们在你的产品上花了多少时间。

**公司可以使用这些指标来跟踪其实现使命的进度。**取上面列表中的第一个指标,即使用该产品的总人数。如果脸书相信它的平台让世界更加紧密,那么越多的人使用脸书,世界上就有越多的人更加紧密。

因此,要确定更多的用户参加活动是否是一件好事我们需要(1)一个使命和(2)一个指标。

使命。给予人们建设社区和拉近世界距离的力量。

公制。让我们来看看用户保留率,即返回脸书的用户数量。

结局。如果参加活动的用户比不参加活动的用户更频繁地回到脸书,那么这种变化是好的。如果保留率下降,也就是说,参加活动的用户不再回到脸书,那么这种变化是不好的。

💡提示:你可以选择任何度量标准。对于这个例子,我选择了用户保留。但是只要你能证明你选择背后的决定是正确的,就没有“正确”或“错误”的答案。

下一步?

如果变化是好的,这表明用户留存率与活动出席率相关。在这种情况下,我们可能要考虑其他方式来影响用户参加活动。

**如果变化是不好的,**这表明事件的出席似乎会阻碍用户返回脸书。在这种情况下,我们可能要重新评估改变搜索结果排名算法的决定。

💡提示:在实际操作中,比上面的三行答案要复杂。但是在面试中,面试官通常只是想知道你是否能够表现出主动性,并提出下一步措施。记住,结束面试回答的最好方式是(1)总结所说的内容,( 2)建议接下来的步骤。

到目前为止我们学到了什么

我们被要求调查指标值增加的原因。

  1. 我们研究了热带框架以确定变化的来源。
  2. 一旦我们理解了度量标准为什么会改变,我们就需要理解这种改变意味着什么。
  3. 然后,我们决定这个暗示是好是坏。
  4. 最后,我们提出了可操作的后续步骤。

最后的想法。你的面试不一定会这样。即使是完全相同的问题。如果你得到了这个问题,你不想开始逐字背诵这篇文章。记住,这是一个 。以此为起点,但要记住面试是你和面试官之间的对话。**你想在一个高层次上展示你的想法,周期性地停顿,问你的面试官他们是怎么想的,他们是否想让你进一步阐述这个想法或者改变路线。采纳他们的反馈,并据此设计你的答案。你能行的。****

授权 Hani Azam 使用 Adobe Stock 提供的图片

这是更大的 数据科学面试备考系列 的一部分。在每篇文章中,我都让深入围绕机器学习、产品&商业意识、统计&概率和 SQL 的面试问题。

我还将分享我的个人经历,包括我如何进入数据科学的**,我的工资我如何管理我的财务,以及我最喜欢的学习和灵感资源。你可以在媒体上关注**我,了解最新动态。

如果你想联系我,你可以在 LinkedIn 上找到我,直接发邮件给我。

开始掌握 Git 命令& Git 背后的逻辑

原文:https://towardsdatascience.com/mastering-git-commands-the-logic-behind-git-ad3fbcc6fb33?source=collection_archive---------13-----------------------

数据专业人员指南

克林特·帕特森在 Unsplash 上拍摄的照片

介绍

一组数据科学家和数据工程师同时开发同一个程序,或者在不同的环境中部署不同版本的程序是很常见的。因此,拥有一些工具来跟踪源代码、配置文件或时间文档中的变化是很有用的。允许我们这样做的工具是版本控制系统。最著名和最常用的版本控制系统是 Git。

Git 的主要方面是,每次您进行更改时,Git 都会拍摄所有文件此刻的样子,并存储对该快照的引用。

版本控制系统可以是集中式的,也可以是分布式的。这意味着团队的所有成员都连接到主服务器,并且所有成员都通过该服务器进行协作。无论如何,如果中央服务器崩溃了,你就不能在分布式版本控制系统中协作。

使用 Git,您可以在本地计算机上拥有所有存储库的历史记录,这使您可以即时访问所有的更改。这意味着更好的性能。

Git 非常容易安装。要安装它,你必须去这里,并按照指示。

在这篇文章中你会看到以下论点:

  • 如何配置 Git
  • Git repo init &第一个 Git 命令
  • Git 远程存储库和 SSH 连接
  • 分支机构管理和拉动式请求
  • 合并冲突和 Git 历史管理
  • Git Stash,Git Reflog & Git Clone

第一部分:如何配置 Git

安装 Git 后,第一步是打开 Git Bash,告诉它你是谁。但是在开始配置 Git 之前,您必须知道 Git 允许我们在三个不同的级别上设置配置:

  • 系统→应用于您计算机上的所有用户及其所有存储库
  • 全局→应用于当前用户计算机上的所有存储库
  • 本地→仅当前存储库

现在,我们打开 git,用下面的命令说出我们是谁:

$ git config --global user.name “Moryba Kouate”$ git config --global user.email “my.email@example.com”

要将 notepad 设置为 Git 中的默认文本编辑器,您可以轻松地输入:

$ git config --global core.editor “notepad”

第二部分:Git repo init &第一个 Git 命令

在这一部分中,您将看到如何创建一个本地存储库并进行第一次提交。创建存储库有两种方法。第一种方法是在本地创建它,第二种方法是克隆现有的存储库。但是现在,我们将只看到如何在本地创建一个存储库。

假设您想要跟踪一些文件中的更改。第一步是初始化存储库。

$ git init

你现在会在括号中看到“主”这个词。主人是主要的分支。请记住,每个分支都包含快照的历史记录。

我们要提交的文件所在的区域称为临时区域。在这个区域,我们可以添加一个或多个文件。下面的命令添加一些文件或所有文件。

要添加一些文件:

$ git add test.py README.md

要添加所有文件:

$ git add .

如果要添加所有文件,例如扩展名为 py 的文件,可以使用以下命令。

$ git add *.py

为了检查您所在的分支上的存储库的状态,以及是否有要提交的状态更改,我们必须键入 git status 命令。我个人比较喜欢下面的短状态。

$ git status -s

要清除 Git Bash 控制台,您可以同时按下 Ctrl + L

如果我不想再跟踪特定文件的更改,也不想从本地存储库中删除它,我可以使用下面的命令:

$ git rm --cached .name_of_file

上述命令将文件从临时区域中删除,但将文件保留在本地区域中。无论如何,如果您想从临时区域中删除特定目录中的所有文件,您必须使用下面的命令。

$ git rm -r cached myfolder/

要在存储库中创建新文件,可以使用以下命令:

$ touch .gitignore

您可以使用以下命令从 Git Bash 控制台编辑该文件:

$ nano .gitignore

然后,例如,你可以在文件中写入。gitignore 忽略所有。bin 文件夹中的 txt 扩展名。

*.class
bin/

您可以使用 commit 命令为您的更改命名。之后,Git 将为您的更改创建一个快照。

$ git commit -m "changes done"

要检查所有更改和提交完成的历史记录,可以使用以下命令:

$ git log

假设我们忘记了修改一个文件,我们需要更改提交的名称。在这种情况下,我们必须使用这个命令:

$ git commit --amend

然后你会看到打开编辑器,你可以修改和保存它。

第三部分:Git 远程存储库和 SSH 连接

当我们作为一个数据专业人员团队工作时,我们需要一个托管在互联网上的、全天候可用的公共存储库。GitHub 是最著名的远程仓库之一。另外两个有趣的存储库是由 Atlassian 和 GitLab 支持的 Bitbucket。对于这一部分,我想你已经有一个 GitHub 帐户。如果你没有 GitHub 档案,请到这里注册。

例如,如果我想连接到我的一个 GitHub 库,我可以很容易地数字 git remote 并添加我的库的路径。

下面是我的 GitHub 库的截图,以及在哪里可以看到正确的复制路径。

$ git remote add origin https://github.com/your_name/your_app.git

因此,您可以使用以下命令推送本地文件:

$ git push origin -u master

作为数据专业人员团队的一员,您将多次使用协议来访问其他计算机。使用 SSH,您可以轻松地登录到远程计算机,并在该计算机上执行任何命令或任何您希望的更改。SSH 的目标是使计算机之间的连接更加安全。事实上,在两台计算机之间建立连接需要一个远程计算机的公钥和一个本地计算机的私钥。

所以首先要做的是生成 SSH 密钥。生成密钥最简单的方法是打开 Git Gui,进入帮助>显示 SSH 密钥,然后点击生成密钥。生成密钥后,您可以继续 GitHub 设置并选择选项 SSH 和 GPG 密钥。在这里,您可以创建一个新的 SSH,在这里您可以粘贴之前通过 Git Gui 获得的内容。现在,我们可以使用以下命令将 SSH 路径粘贴到我们的远程存储库中:

$ git remote set-url origin git@github.com:name/application.git

第四部分:分行管理&拉式请求

在这一部分,我们将看到如何创建新的分支,我们还会看到一些命令,如特定分支的 git 日志,git 检验 git 开关和 git 分支。我们将看到如何在本地和远程存储库中删除分支。

现在,想象一下,作为一名数据工程师,你的任务是实现一个程序。第一步是用下面的命令检查本地分支。

$ git branch

相反,要查看所有本地和远程分支,我将使用以下命令:

$ git branch -a

要创建一个分支,我可以使用:

$ git branch BA0003

无论如何,有一个命令可以同时创建和更改分支。我将使用下面的命令,其中 checkout 命令用于更改分支,而选项 -b 表示我想要更改并创建一个新分支。

$ git checkout -b BA0003

现在在括号里,你不会再看到 master,而是(BA0003)。请记住,您可以使用命令 cd 来更改路径。

$ cd base

要在您的目录中有一个列表,我们可以使用:

$ ls -l

在查看日志之前,使用命令 **git add 传输暂存区中的文件是很重要的。**然后我们可以用我们在上一部分看到的命令创建一个提交。现在,要查看特定分支的日志,我们可以轻松地键入:

$ git log BA0003

通过键入您在提交中看到的第一个单词和数字,可以导航到特定的提交,并切换到特定的提交。例如,如果我有下面的提交 fee 9 E3 b 6 dfaf 02 e F5 f 0 b 12 c 939 cc 9082487 f 2578,我将键入:

$ git checkout fee9

导航到提交是有用的,因为有时我们可能需要调查一个缺陷。所以我们想知道一个代码之前是如何工作的,并执行这个代码。

要删除分支,您必须使用以下命令:

$ git branch -d BA0003

如果您想切换到主分支,您可以轻松地键入:

$ git switch master

现在是时候看看什么是拉请求了。拉式请求是向项目提交贡献的一种方法。换句话说,您可以从一个分支提取更改以更新另一个分支,通过提交提取请求,您可以要求其他数据专业人员检查您的更改并将它们提取到另一个分支。

正如我之前向您展示的那样,在您推送您在远程存储库(GitHub)中完成的修改之后。您可以转到“拉取请求”部分,创建一个新的拉取请求,如下所示。

然后就可以看到审核人的变化了。显然,您必须在 GitHub 上为您想要共享的存储库添加您的合作者。下面是您可以在 GitHub 上邀请合作者的页面截图。

另一个有用的命令是 git fetch ,它帮助我们从远程存储库中获取所有更改,并将其放入本地存储库中。

但是,如果我想将远程更改合并到我的本地分支中,我需要键入:

$ git merge

第五部分:合并冲突和 Git 历史管理

什么是合并冲突?合并冲突是当 Git 无法自动解决两次提交之间的代码差异时发生的特定事件。事实上,有可能多个开发人员试图编辑相同的内容,Git 不知道哪个版本的代码是最新的,也就是正确的版本。要解决这种问题,您可以在 Git Bash 上打开该文件,并手动更改它以克服冲突。

历史管理的一些重要 Git 命令是 git rebase 和 git reset。 Git Rebase 允许我们替换我们想要的分支。要了解更多细节和 Git rebase 的实际例子,我建议你访问这个链接。

对于所关心的 Git 重置,当你想要撤销更改时,你需要它。例如,您创建了一个不再需要的快照。正如您在前面的部分中看到的,当您想要从工作树传递到临时区域时,您需要使用 Git Add。然后,要设置历史中的更改,您需要 Git Commit。因此,如果您想遵循相反的过程,从历史记录转到临时区域,您需要键入:

$ git reset --soft

但是如果你想在 Staging Area 之前走,那么要在你的工作树中看到这个情况你需要使用:

$ git reset --mixed

因此,如果您已经在暂存区中添加了文件,现在如果您键入 git status ,您将会注意到您在暂存区中添加的文件现在位于工作目录中。

然后,如果您需要在所有操作的开始清理您的工作目录,您将键入:

$ git reset --hard

第六部分:Git Stash、Git Reflog 和 Git Clone

想象一下,你需要立即开始一项新的任务,并让上一个你还没有完成的任务。所以解决方法是保存你当前的任务,以便以后再回来。在这种情况下,命令是:

$ git stash

然后你可以使用 Git Switch- 来切换你当前的任务,转到你的新任务。

因此 stash 可以作为您的更改的临时存储,并列出您可以使用的所有已保存的任务:

$ git stash list

使用下面的命令,您可以弹出最近的更改。这个概念使人想起计算机编程中的后进先出法。如果你想了解更多关于后进先出的思想,你可以阅读我的关于 Python 中的数据结构算法的文章。

要清除一个特定的存储,你可以在应用 git stash list 后很容易地获取你找到的引用。该命令将是:

$ git stash drop stash@{0}

要清除所有的隐藏,您只需编写以下命令:

$ git stash clear

可能会丢失一些提交,您希望恢复其中的一些。要解决这个问题,我们可以使用 **git reflog。**该命令为我们提供了在区域主分支上进行的每个提交、每个研究操作的相关信息。要查看所有细节,一个好的做法是使用:

$ git log -g

因此,在您确定丢失的更改后,您可以简单地使用下面的命令来恢复提交。

$ git branch name_of_branch e57d

您已经注意到,我给出了一个新的名称,并在名称后添加了提交代码。请记住,reflog 中的信息存储 90 天。

无论如何,如果你想看到 1 小时以来的参考日志,你可以简单地数字:

$ git reflog --since="1-hour"

最后一个有用的 git 命令是 Git Clone。使用此命令,您可以克隆远程存储库。所以你需要去你的 GitHub 库并选择 HTTPS 或者 SSH。之后,打开你的 Git Bash 和 digit:

$ git clone git@github.com:name/application.git

太好了!您已经克隆了远程存储库。

结论

现在您已经很好地理解了 Git 是如何工作的,以及为什么对于一个数据专业人员来说了解它是如此重要。然而,我认为深入吸收这些概念的最有效的方法是与你的一些朋友一起做一个项目,并尝试开发一个复杂的程序。这样,您将面临许多问题,这些问题将推动您以最有效的方式使用 Git 命令,并提高您对 Git 在开发过程中的真正效用的理解。

感谢你阅读这篇文章。您还可以通过其他方式与我保持联系并关注我的工作:

  • 订阅我的时事通讯。
  • 也可以通过我的电报群 数据科学初学者 联系。

在 Matplotlib 中掌握直方图

原文:https://towardsdatascience.com/mastering-histograms-in-matplotlib-6cf5ed084d48?source=collection_archive---------35-----------------------

安迪·霍尔在 Unsplash 上的照片

制作直方图的细节

直方图是最受欢迎的图表之一。理解连续变量的总体分布是很有用的。所以,几乎在任何数据分析或探索性数据分析,或机器学习项目中,你都会从一些直方图开始。在本文中,我将解释如何在 Matplotlib 中制作直方图。像往常一样,我将从一个图中最简单的直方图开始,慢慢走向一些更有趣的直方图。

我将使用公开可用的 NHANES 数据集。请看这里的发布说明。

请随意从这里下载数据集。

我们潜水吧。

首先,导入必要的包并制作数据框架:

import pandas as pd
import matplotlib.pyplot as pltdf = pd.read_csv("nhanes_2015_2016.csv")
df.head()

数据集相当大!这些是数据集中的列:

df.columns

输出:

Index(['SEQN', 'ALQ101', 'ALQ110', 'ALQ130', 'SMQ020', 'RIAGENDR', 'RIDAGEYR', 'RIDRETH1', 'DMDCITZN', 'DMDEDUC2', 'DMDMARTL', 'DMDHHSIZ', 'WTINT2YR', 'SDMVPSU', 'SDMVSTRA', 'INDFMPIR', 'BPXSY1', 'BPXDI1', 'BPXSY2', 'BPXDI2', 'BMXWT', 'BMXHT', 'BMXBMI', 'BMXLEG', 'BMXARML', 'BMXARMC', 'BMXWAIST', 'HIQ210'], dtype='object')

列名看起来很难理解。但是我将解释我将在本文中使用的列的含义。

一个简单的直方图

这是最简单的直方图。收缩压的分布:

df['BPXSY1'].hist(bins = 15, figsize=(8, 6))
plt.xlabel("Systolic Blood Pressure")
plt.title("Distribution of Systolic Blood Pressure")
plt.show()

一个非常简单的直方图,显示了轻微的右偏分布。

但是在处理大型数据集时,在一个图中进行多次分布通常非常有用。下一张图将显示体重、身高和身体质量指数的分布:

df[['BMXWT', 'BMXHT', 'BMXBMI']].plot.hist(bins = 15, figsize=(10,6), alpha = 0.6)
plt.show()

幸运的是,在这个图中,三个分布有非常不同的范围。所以,不会有太多的重叠。但是如果有太多的重叠发生,最好将两个图分开。就像如果我们在同一个图中绘制两个收缩压和两个舒张压,它们会重叠太多,很难理解它们。在这些情况下,最好在单独的支线剧情中进行分配:

df[['BPXSY1', 'BPXDI1', 'BPXSY2', 'BPXDI2']].hist(
    bins=15,
    figsize=(10, 8),
    grid = False,
    rwidth = 0.9,
)
plt.show()

当所有四个血压计并排放置时,会更加有趣和有用。因为你可以比较它们。如果我们能把它们放在同一个尺度上,尤其是当我们需要比较它们的时候,那就更有用了。这里我们用相同的 x 轴和 y 轴限制来放置它们:

df[['BPXSY1', 'BPXDI1', 'BPXSY2', 'BPXDI2']].hist(
    bins=15,
    figsize=(12, 6),
    grid = False,
    rwidth = 0.9,
    sharex = True,
    sharey = True
)
plt.show()

同样的四个变量,但看起来不同。原因是它们的规模相同。所以,几乎都缩水了。一些在 x 方向收缩,一些在 y 方向收缩。

在上面两张图中,不同的变量有不同的分布。但是如果我想根据一个分类变量的不同类别只绘制一个连续变量呢?

我们有一个分类变量“DM deduct 2 ”,它代表人口的教育水平。我想画出每个教育水平的收缩压分布图。

但是在这个数据集中,教育水平用数字表示。

df['DMDEDUC2'].unique()

输出:

array([ 5.,  3.,  4.,  2., nan,  1.,  9.])

我首先将它们替换为一些有意义的字符串值,如下所示:

df["DMDEDUC2x"] = df.DMDEDUC2.replace({1: "<9", 2: "9-11", 3: "HS/GED", 4: "Some college/AA", 5: "College", 7: "Refused", 9: "Don't know"})

让我们绘制每个教育水平的收缩压直方图:

df.hist(column = "BPXSY1",
        by = "DMDEDUC2x",
        bins = 15,
        figsize=(10, 12),
        grid = False,
        rwidth = 0.9,
        sharex = True,
        sharey = True
       )plt.show()

在这里!有时,每个直方图有一个单独的行可能会很有趣。让我们看看它是什么样子的:

ax = df.hist(column = "BPXSY1",
        by = "DMDEDUC2x",
        bins = 15,
        figsize=(8, 18),
        grid = False,
        rwidth = 0.9,
        sharex = True,
        sharey = True,
        layout=(6, 1)
       )for x in ax:
    #Removing the left, right and top lines of the box of each plot
    x.spines['right'].set_visible(False)
    x.spines['left'].set_visible(False)
    x.spines['top'].set_visible(False)

    #Adding the x-ticks on each plot
    x.tick_params(axis="both", labelbottom = 'on')
    x.tick_params(axis='x', rotation = 0)

    plt.ylim(0, 300)

    vals = x.get_yticks()

    #Getting the grey horizontal grids in each plot
    for v in vals:
        x.axhline(y = v, linestyle="solid", color = 'gray', alpha = 0.1)

#Adding some space between each plot
plt.subplots_adjust(hspace=0.8)
plt.show()

这些是今天所有的直方图。

结论

这些都是 Matplotlib 中直方图的绘图。希望这是一个有用的教程,你将能够在你的项目中使用这些类型的图。

这里有一个 youtube 视频,一步一步地解释了相同类型的情节

更多阅读

掌握 Python 中的索引和切片

原文:https://towardsdatascience.com/mastering-indexing-and-slicing-in-python-443e23457125?source=collection_archive---------4-----------------------

深入研究有序集合的索引和切片

照片由岩田良治在unsplash.com拍摄

在 Python 中,像字符串或列表这样的有序序列的元素可以通过它们的索引被单独访问。这可以通过提供我们希望从序列中提取的元素的数字索引来实现。此外,Python 支持切片,这是一个让我们提取原始序列对象的子集的特性。

在本文中,我们将探讨索引和切片是如何工作的,以及如何使用它们来编写更简洁、更 Pythonic 化的代码。

索引

像大多数编程语言一样,Python 偏移量从位置 0 开始,到位置 N-1 结束,其中 N 被定义为序列的总长度。例如,字符串Hello的总长度等于 5,每个单独的字符可以通过索引 0 到 4 来访问,如下图所示:

Python 中的索引

现在,您可以通过编程方式访问字符串中的单个字符,方法是提供您希望获取的相应偏移量,并用方括号括起来:

>>> my_string = 'Hello'
>>> print(my_string[0])
'H'
>>> print(my_string[2])
'l'
>>> print(my_string[3])
'l'

同样重要的是要知道,当你试图访问一个大于序列长度(减 1)的偏移量时,Python 会抛出一个IndexError通知你所请求的偏移量超出了范围:

>>> my_string[5]
Traceback (most recent call last):
  File "<input>", line 1, in <module>
IndexError: string index out of range

还可以通过提供一个负索引来访问一个元素,该负索引基本上对应于从序列右侧开始的索引。通过偏移量 -1 可以访问最后一项,通过偏移量 -2 可以访问倒数第二项,以此类推

Python 中的负偏移

从技术上讲,当使用负偏移量时,Python 会将该偏移量添加到序列的长度中,以便推断出准确的位置。例如,假设我们想使用负偏移量从字符串my_string = 'Hello’中提取字符e。现在,表达式my_string[-4]实质上将被翻译成my_string[len(my_string) — 4],它相当于最终给出我们想要的输出的my_string[5 — 4]my_string[1]:

>>> my_string[-4]
'e'

限幅

切片是索引的一种形式,它允许我们推断原始序列的整个(子)部分,而不仅仅是单个项目。要在 Python 中对一个序列执行切片,您需要提供两个由冒号分隔的偏移量,尽管在某些情况下,您可以只定义两个偏移量中的一个,甚至不定义任何偏移量(下面将详细讨论这些情况)。

第一个偏移量表示起点,且包含起点,而第二个偏移量表示终点,但与起点偏移量不同,它不包含终点

my_string[start:end]

因此,当执行切片时,Python 将返回一个新的对象,包括从较低索引开始直到比较高索引小一的位置的所有元素。例如,考虑一个用例,我们需要获取字符串的前两个元素:

>>> my_string[0:2]
'He'

正如我已经提到的,提供显式偏移量并不是强制性的。当省略起始偏移量时,其值将默认为 0。另一方面,当没有提供结束偏移量时,其默认值将等于序列的长度。实际上有三种不同的场景,如下所示:

my_string[0:]   # Omit ending offset
my_string[:-1]  # Omit starting offset
my_string[:]    # Omit both starting and ending offsets

省略结束偏移

第一个符号通常在我们想要删除前导文本时很有用。假设我们想得到字符串中除第一个字符以外的所有字符。我们可以通过使用下面的符号来做到这一点

>>> my_string = 'Hello'
>>> my_string[1:]
'ello'

正如我们已经提到的,当结束偏移量被省略时,将使用序列的长度来代替:

>>> my_string[1:] == my_string[1:len(my_string)]
True

省略起始偏移量

假设我们现在需要字符串中除第一个字符以外的所有字符。在这种情况下,省略起始偏移将达到目的:

>>> my_string = 'Hello'
>>> my_string[:-1]
'Hell'

假设下限被跳过,其值将默认为 0:

>>> my_string[:-1] == my_string[0:-1]
True

省略两个偏移

Python 中的切片符号允许我们省略起始和结束偏移量。

>>> my_string = 'Hello'
>>> my_string[:] == my_string[0:len(my_string)]
True

假设当下限和上限被忽略时,将分别默认为0len(sequence),您可能想知道这是否有任何帮助或用途。这是一种快速复制对象的方法,如下图所示

>>> my_string = 'Hello'
>>> my_string_copy = my_string[:]

请注意,当这种切片技术将产生一个不同的对象,将被分配到不同的内存位置。这对不可变的对象类型(如字符串)没有任何影响,但是在处理可变的对象类型(如列表)时,意识到这一点是非常重要的。关于这方面的更多细节,你可以参考我的文章Python 中的动态类型,它讨论了对象是如何被创建和(正确地)复制的。

扩展切片

Python 中的切片表达式带有一个可选的第三个索引,当指定时,它被用作一个步骤。显然,当忽略步长值时,它默认为 1,这意味着不会跳过所请求的序列子部分中的任何元素。符号如下所示

[start:end:step]

例如,假设我们有一个包含字母表中字母的字符串,我们希望从中提取位置 1 和 19 之间的所有其他项目:

>>> import string
>>> my_string = string.ascii_lowercase # 'abcdefg...'
>>> my_string[1:20:2]
'bdfhjlnprt'

这种符号可以用来代替列表理解。例如,假设我们想要获取一个列表中所有具有偶数索引的元素。实现这一点的列表理解应该是

>>> my_list = [100, 400, 34, 179, 0, 89, 121]
>>> [value for index, value in enumerate(my_list) if index % 2 == 0]
[100, 34, 0, 121]

在这种情况下,切片符号可以使我们的代码更简单,可读性更好:

>>> my_list = [100, 400, 34, 179, 0, 89, 121]
>>> my_list[::2]
[100, 34, 0, 121]

像开始和结束偏移一样,步长索引可以是负数。从技术上讲,当我们想要颠倒有序序列中元素的顺序时,这是很有用的

>>> my_string = 'Hello'
>>> my_string[::-1]
'olleH'

换句话说,当应用负步进指数时,开始和结束偏移的效果是相反的。为了说明这一点,让我们跳到另一个例子,我们实际上定义了所有三种可能的偏移。

>>> import string
>>> my_string = string.ascii_lowercase # 'abcdefg...'
>>> my_string[20:10:-1]
'utsrqponml'

在上面的例子中,我们从索引 11 到 20 以相反的顺序创建了一个新的字符串。

结论

在本文中,我们探索了 Python 中索引和切片的工作原理。这两种符号在大多数 Python 应用程序中广泛使用,因此您需要确保理解它们是如何工作的。下面我们重温一下我们已经讨论过的要点——也可以随意将其用作备忘单。

分度

  • 第一项从偏移量0开始
  • 最后一项结束于偏移量len(my_sequence) — 1
  • 负指数表示计数将反向开始。本质上,它被添加到序列的长度中。例如,my_string[-1]翻译成my_string[len(my_string) — 1]

切片

  • 起始索引(下限)是包含性的
  • 结束索引(上限)是非包含性的
  • 当起始索引被省略时,它默认为 0
  • 当省略结束索引时,它默认为序列的长度
  • 当起始和结束索引都被省略时,原始对象的副本被创建— my_string[:]
  • 第三个索引表示步骤
  • 省略步骤索引时,默认为 1(即不跳过任何元素)
  • 负的步长指数有助于我们创建相反的序列(例如my_string[::-1]

控制背面的乳胶

原文:https://towardsdatascience.com/mastering-latex-on-overleaf-71a72d513363?source=collection_archive---------11-----------------------

作为一名数据科学家,这是评估你工作的一项重要技能

作者图片

自从我开始了我的数据科学之路,我一直在使用 LaTex 完成我遇到的每一项任务,比如大学的项目组、论文、幻灯片、研究论文和简历。当我发现它的时候,我在想“没有它我怎么可能活到现在”?

与谷歌文档或微软 Word 不同,文档的结构保持固定和有序。起初,这似乎很难,因为你需要为每种类型的元素指定许多关键字,如标题、部分、表格、图像,但经过一些努力后,你不会后悔。

良好的自我展示对于脱颖而出至关重要。为什么对于其他边缘方面,比如书面文档,不应该是一样的呢?!对我来说,乳胶就像热巧克力中的鲜奶油。这是给热巧克力增添美味的附加物。没有它,就不一样了。

在这篇文章中,我将展示使用 latex 创建专业外观的文档时需要了解的基础知识。此外,我将帮助你开始使用一个很棒的 latex 在线工具,叫做背页,来编写 LaTex 文档。

目录:

  1. 介绍
  2. 启动新项目
  3. 选择 LaTex 编译器
  4. 主文档
  5. 数学表达式
  6. 包含图像
  7. 创建表格
  8. 参考书目

1.介绍

背页是一个提供许多好处的书写工具。首先,它允许在浏览器上编写 latex 文档,而无需安装任何软件/软件包[1]。

默认情况下,背页上创建的文档是私人的,但也可以通过私人邀请或链接共享。当在项目组中工作时,这真的很有效率。它还允许在文档中添加注释。要找到所有评论,您必须点击查看图标。此外,还有一个聊天图标,可以向群组的其他成员发送消息,而不是使用其他独立的应用程序。

您可以使用历史图标跟踪文档的更改。免费版显示了项目最近 24 小时的变化。如果你想要旧的改变,你应该升级你的计划到背面。另一个优点是它允许通过查看 LaTeX 日志容易地发现错误。

2.开始一个新项目

作者图片

要在背页的上创建 LaTex 文档,您需要登录网站,然后点击左上角的绿色按钮启动一个新项目。创建项目有两种方式:

  • 开始:你可以做到,但是我不会建议,因为有很多代码要写。
  • 使用网站上已有的模板制作任何类型的文档。这样,你只需要修改一下。
  • 复制您过去创建的项目

3.选择 LaTex 编译器

作者图片

教程中通常会跳过这一步。但是有一次我遇到了一个错误,阻塞了我所有的项目,这是因为默认情况下错误的编译器 pdfLaTex。要更改编译器,需要点击项目左上角的黑色菜单图标。一旦你选择了它,你可以改变设置“编译器”的第一个选项。例如,如果您有一个 XeLaTex 文件,您必须将 pdfLaTex 编译器替换到 XeLaTex 编译器中。

在浏览菜单时,您还会注意到它还提供了其他用户特定的设置,如界面主题、字体大小、字体系列。还有可能打开文档,统计字数,用 Github 和 Dropbox 同步项目。

4.主文档

当您创建一个项目或获取一个模板时,您总是需要一个文件。tex,而 while 包含了 LaTex 文档中最重要的构件。因此,它也可以被称为主文件。下面,我展示了一个在主文件中使用了一些关键字的例子:

\documentclass[12pt]{article}
\usepackage[T1]{fontenc}
\title{example}
\author{Eugenia Anello}
\date{October 2021}\begin{document}\maketitle\section{Introduction}\subsection{Theory}\end{document}

作者图片

我们可以观察到,文档的大部分内容主要由两个标签组成,\begin{document}\end{document}

在这两个标签之前,还有一些相关的标签需要考虑。第一个是\documentclass[12pt]{article},它指定了字体大小(12pt)和类别(文章)。

另一个重要的标签是\usepackage[T1]{fontenc},它是文档的 T1 字体编码。这两个标签可以根据您创建的文档类型进行更改。此外,为了添加标题、作者和日期,分别有标签\title{example}\author{Eugenia Anello}\date{October 2021}

有两种方法可以包含这些部分:

  • 正如您在示例中注意到的,您直接使用标签\section{Introduction}来包含主文档中的所有内容
  • 在大型文档中,使用唯一的 LaTex 文档可能是无效的。最好把它分成不同的模块,与章节相对应。在这种情况下,最好使用标签\input{filename},用文件名. tex 添加每章的内容。

5.数学表达式

与 Google Docs 和 Microsoft Word 不同,LaTex 文档提供了一种高效的方式来编写数学方程。建议下载这个 pdf ,对查 LaTeX 数学符号列表有用。

数学表达式有两种编写模式: 内嵌数学模式编写属于段落的公式, 显示数学模式指定不属于段落的公式。

有三种类型的分隔符可用于 内联数学模式。下面我展示了三个使用不同分隔符的例子:

  • \(**...**\)
  • $**...**$
  • \begin{math}**...**\end{math}

我通常更喜欢使用$$分隔符,因为它更容易书写。

显示数学模式包括三种建立数学公式的方式:

  • \[**...**\]
  • \begin{displaymath}**...**\end{displaymath}
  • \begin{equation}**...**\end{equation}

这些分隔符通常用于非常复杂的数学公式。

让我们展示四个例子来更好地记忆我之前展示的语法:

$P(A|B)=\frac{P(B|A)P(A)}{P(B)}$\[P(A,B,C,...,H)=\frac{i}{Z}\prod_{i=1}^{N}\phi_i(X_1...X_k)\]\[\tilde{J}(\theta,X,y)=J(\theta,X,y)+\frac{\lambda}{2}||w||^2_2\]\begin{equation}
\tilde{J}(\theta,X,y)=J(\theta,X,y)+\lambda||h||_1=J(\theta,X,y)+\lambda\sum_i |h_i|
\end{equation}

作者图片

我们可以看到 内联数学模式表达式没有任何空格,而显示数学模式表达式总是居中。特别是,标签\begin{equation}**...**\end{equation}对于列举等式非常有用。

此外,还有特定的标签来书写符号,\frac{num}{den}用于分数,\sum用于总和,\prod用于乘积以及许多其他符号。

6.包括图像

作者图片

要在文档中包含图像,需要使用左上角的图标将它们上传到项目中,如图所示。也可以导入您过去创建的另一个项目中使用的图像。下面我展示了一个在文档中包含图像的语法示例:

\documentclass[12pt]{article}
\usepackage[T1]{fontenc}
\title{example}
\author{Eugenia Anello}
\date{October 2021}\usepackage{graphicx}
\begin{document}\maketitle\section{Introduction}The t-SNE visualization is showed in figure \ref{tsne}:\begin{figure}[htp]
    \centering
    \includegraphics[scale=0.4]{vae6tsne.png}
    \caption{t-SNE}
    \label{tsne}
\end{figure}\end{document}

作者图片

\usepackage{graphics}需要包含包含与 LaTex 文档中的图像相关的命令的包。

为了显示图像,需要两个外部标签\begin{figure}\end{figure}\caption{t-SNE}用于添加图片的标题,而\label{tsne}用于指定标签以引用图片中的段落。

这是一个最简单的例子,但是你可能需要在你的报告中包含多个图片。例如,让我们并排插入两幅图像:

\documentclass[12pt]{article}
\usepackage[T1]{fontenc}
\title{example}
\author{Eugenia Anello}
\date{October 2021}\usepackage{graphicx}
\usepackage{url}
%\usepackage{hyperref}
**\usepackage{subfig}**
\begin{document}\maketitle\section{Introduction}The examples of visualizations are given in Figure \ref{fig:visualiz}\begin{figure}[!htbp]
\centering
  **\subfloat[visualization of the latent space] {\includegraphics[width=0.47\textwidth]{vae6ls.png}}**
  \hspace{0.2cm}
  **\subfloat[t-SNE visualization]{\includegraphics[width=0.47\textwidth]{vae6tsne.png}}**
    \caption{Visualizations}
    \label{fig:visualiz}
\end{figure}\end{document}

为了在一个图形中包含两个图,我们需要添加subfig包,这使我们能够使用\subfloat[]{}。要添加每个图像,我们必须指定\subfloat,并将宽度固定为\textwidth值的 0.47 倍。请注意,您需要确保宽度之和小于 1,否则每行都有一张图片。在两个地块之间,我使用\hspace{0.2cm}包含了一个 0.2 cm 的水平空白。

7.创建表格

表格是我将在本文中讨论的最后一个元素。像图片一样,它需要两个外部标签。但是这次没有要导入的包,它们是\begin{table}\end{table}。下面有两个例子:

The CNN architecture is showed in Table \ref{len}:\begin{table}[!htbp]
\centering
\begin{tabular}{lrr}
Layers & Output Shape & Kernel Size \\
conv2d & 40 $\times$ 98 $\times$ 8 & 3 $\times$ 3 \\
conv2d & 16 $\times$ 45 $\times$ 16 & 5 $\times$ 5 \\
dense & 120 & - \\
dense & 84 & - \\
dense & 36 & - \\
\end{tabular}
\label{len}
\caption{CNN architecture}
\end{table}

作者图片

我们可以添加指定 | 的垂直线和使用\hline:

The CNN architecture is showed in Table \ref{len}:\begin{table}[!htbp]
\centering
\begin{tabular}{**|**l**|**r**|**r**|**} **\hline**
Layers & Output Shape & Kernel Size \\ **\hline**
conv2d & 40 $\times$ 98 $\times$ 8 & 3 $\times$ 3 \\ **\hline**
conv2d & 16 $\times$ 45 $\times$ 16 & 5 $\times$ 5 \\ **\hline**
dense & 120 & - \\ **\hline**
dense & 84 & - \\ **\hline**
dense & 36 & - \\ **\hline**
\end{tabular}
\label{len}
\caption{CNN architecture}
\end{table}

作者图片

有一些关键词需要观察:

  • \begin{tabular}{|l|l|l|}告诉我们有三个 l eft 对齐的列,由垂直空格分隔。
  • \hline在表格顶部和底部插入一条水平线。
  • cell1 & cell2 & cell3 \\是用于指定每行有三个单元格的语法

由于创建表格的语法非常长,我更喜欢使用网站,在网站上手动创建表格,就像在 Microsoft Word 中一样,它返回 LaTex 代码来创建表格。该网站名为表格生成器,这里的链接是。

8.文献学

作者图片

要在文档末尾添加参考书目,您需要创建一个新文件。选择左上角的图标。你可以随意调用文件,比如“references.bib”。

让我们看一个参考文献的快速例子。包含参考书目的 bib:

[@article](http://twitter.com/article){b1,
 title = {Growing opportunities in the Internet of Things},
  author = { Fredrik, Dahlqvist and Mark Patel, Alexander Rajko and Jonathan, Shulman},
 year = {2019},
 month = {Jun.},
 journal = {McKinsey$\&$Company}
}[@book](http://twitter.com/book){b2,
 title = {Industry 4.0 - What Is It?},
 author = {Núbia, Gabriela and Pereira, Carvalho and Edson, Walmir Cazarini},
 publisher = {Book publisher},
 edition = {1st},
 month = {March},
 year = {2019}
}[@misc](http://twitter.com/misc){b3,
author = {IBM},
title = {How Industry 4.0 technologies are changing manufacturing},
howpublished={\url{[https://www.ibm.com/topics/industry-4-0](https://www.ibm.com/topics/industry-4-0)}}}

正如你所注意到的,每个参考书目条目总是以@开头,并且根据你想要引用的资源而变化。有三种主要类型的条目:

  • 文章为文章
  • 书籍为书籍
  • misc 对于引用网页非常有用

注意:在。需要添加\usepackage{url}\usepackage{hyperref}的 tex 文件。下面我展示了 file.tex:

\documentclass[12pt]{article}
\usepackage[T1]{fontenc}
\title{example}
\author{Eugenia Anello}
\date{October 2021}**\usepackage{url}
%\usepackage{hyperref}**\begin{document}\maketitle\section{Introduction}
The term ”Industry 4.0” refers to a new phase in the Industrial Revolution,emphasizing the latest technological innovations and allows to make the productionfaster and customized **\cite{b1}\cite{b2}\cite{b3}**.**\bibliography{references}**
**\bibliographystyle{ieeetr}**\end{document}

您可以观察到:

  • 我们添加了\usepackage{url},同时我们使用%对行\usepackage{hyperref}进行了评论,因为 url 包足以引用网站
  • 我们用\cite{...}来引用条目
  • \bibliography{...}\bibliographystyle{...}需要在最后指定。\bibliography{...}必须知道引用在哪里,而\bibliographystyle{...}指定引用样式。

最终想法:

我希望这个总结对你开始使用 LaTex 和背页工具有所帮助。它们真的可以用于任何目的:知道如何利用它们会有所不同。肯定有很多细节我没有谈到,但大多数话题都涉及到了。如果你想探索背面更多的潜力,你可以查看这个文档。做得很好,真的很详细。感谢阅读!祝你有愉快的一天。

你喜欢我的文章吗? 成为 会员,每天无限获取数据科学新帖!这是一种间接的支持我的方式,不会给你带来任何额外的费用。如果您已经是会员, 订阅 每当我发布新的数据科学和 python 指南时,您都可以收到电子邮件!

掌握 Python 中的记忆化

原文:https://towardsdatascience.com/mastering-memoization-in-python-dcdd8b435189?source=collection_archive---------10-----------------------

理解 Python 中的函数缓存

由 Kaboompics 拍摄的照片。在像素上的

记忆化是一种用于存储先前函数调用结果以加速未来计算的方法。如果使用相同的参数进行重复的函数调用,我们可以存储以前的值,而不是重复不必要的计算。这大大加快了计算速度。在这篇文章中,我们将使用记忆来寻找阶乘。

我们开始吧!

首先,让我们定义一个递归函数,我们可以用它来显示第一个阶乘,直到 n 。如果你对递归不熟悉,可以看看这篇文章:Python 中的递归

提醒一下,阶乘是为整数 n 定义的,因此它是该整数和它下面所有整数的乘积。例如

1! = 1,

2! = 2*1= 2

3! = 321 = 6

诸如此类。我们可以将递归函数定义如下:

def factorial(input_value):
    if input_value < 2: 
        return 1
    elif input_value >= 2:
        return input_value * factorial(input_value-1)

这里我们指定了基本情况,即如果输入值小于 2,则返回 1。如果输入值大于或等于 2,则返回递归函数调用,取整数值和先前整数值的乘积。

现在,让我们打印前 10 个阶乘:

for i in range(1, 11):
     print(f"{i}! = ", factorial(i))

这似乎运行良好。现在,让我们尝试显示前 5000 个术语:

for i in range(1, 5000):
     print(f"{i}! = ", factorial(i))

这将返回以下错误,这是 python 对堆栈溢出(内存不足)的防范措施:

发生这种情况是因为每次后续计算我们都在做重复的工作。

考虑递归函数如何计算每一项:

1! = 1,

2! = 2*1= 2

3! = 3*2! = 6

4! =4*3! = 24

注意,为 4!我们在重复 3 的计算!第二。。如果我们有办法在计算时记住/存储这些值,我们就会避免重复计算。这就形成了记忆法的动机。

现在让我们浏览一下实现记忆化方法的步骤。为了继续,让我们初始化一个字典:

factorial_dict = {}

接下来,我们将定义记忆函数。首先,我们检查输入是否小于 2,如果小于 2,则返回 1:

def factorial_memo(input_value):
    if input_value < 2: 
        return 1

接下来,我们检查输入值是否在字典中。如果不是,我们将阶乘值存储在字典中,并返回输入键的值:

def factorial_memo(input_value):
    ...
    if input_value not in factorial_dict:
        factorial_dict[input_value] = input_value * factorial_memo(input_value-1)
    return factorial_dict[input_value]

完整的功能是:

def factorial_memo(input_value):
    if input_value < 2: 
        return 1
    if input_value not in factorial_dict:
        factorial_dict[input_value] = input_value * factorial_memo(input_value-1)
    return factorial_dict[input_value]

现在,让我们尝试显示高达 5000 的阶乘!我们的新功能:

for i in range(1, 5000):
     print(f"{i}! = ", factorial_memo(i))

而且我们看到计算达到 4999!成功(输出被截断):

有一种更简单的方法可以用更少的代码实现记忆化。让我们考虑一下我们最初的递归函数:

def factorial(input_value):
    if input_value < 2: 
        return 1
    elif input_value >= 2:
        return input_value * factorial(input_value-1)

我们可以从“functools”模块导入一个名为“lru_cache”的装饰器,它允许我们缓存我们的值。该名称代表“最近最少使用的缓存”。使用这个装饰器,我们可以获得与‘factorial _ memo’方法相同的性能:

from functools import lru_cache
[@lru_cache](http://twitter.com/lru_cache)(maxsize = 1000)
def factorial(input_value):
    if input_value < 2: 
        return 1
    elif input_value >= 2:
        return input_value * factorial(input_value-1)

for i in range(1, 5000):
     print(f"{i}! = ", factorial(i))

我们看到我们实现了相似的性能。我就讲到这里,但是我鼓励你自己去研究代码。

结论

总之,在这篇文章中,我们讨论了 python 中的记忆方法。首先,我们展示了在计算了许多阶乘项之后,递归函数的简单实现是如何变得非常慢的。然后,我们定义了一个新方法,在这个方法中,我们将过去计算的值存储在一个字典中。这导致了计算的显著加速。然后我们讨论了“lru_cache”装饰器,它允许我们用更少的代码实现与“factorial_memo”方法相似的性能。如果你有兴趣了解更多关于记忆化的知识,我鼓励你去看看苏格拉底的 YouTube 教程。我希望你觉得这篇文章有用/有趣。这篇文章中的代码可以在 GitHub 上找到。感谢您的阅读!

掌握 Python 中的模型可解释性

原文:https://towardsdatascience.com/mastering-model-explainability-in-python-4e578206e000?source=collection_archive---------28-----------------------

解释模型特征重要性

照片由卢卡斯在像素上拍摄

对于数据科学家来说,解释机器学习模型的一个关键部分是理解哪些因素会影响预测。为了在决策过程中有效地使用机器学习,公司需要知道哪些因素是最重要的。例如,如果一家公司想预测客户流失的可能性,他们可能也想知道究竟是什么驱使客户离开公司。在本例中,该模型可能表明,购买很少打折产品的客户更有可能停止购买。有了这些知识,公司可以在未来做出更明智的定价决策。在高层次上,这些见解可以帮助公司留住客户更长时间,并保持利润。

幸运的是,Python 提供了许多包,可以帮助解释机器学习模型中使用的功能。部分相关图是可视化特征和模型预测之间关系的一种有用方法。我们可以将这些图解释为作为输入特征函数的平均模型预测。随机森林,也是一种机器学习算法,使用户能够获得量化各种特征在确定模型预测中的重要性的分数。因为模型可解释性以一种直接的方式内置于 Python 包中,所以许多公司广泛使用随机森林。对于像深度神经网络这样的更多黑盒模型,像局部可解释模型不可知解释(LIME)和沙普利加法解释(SHAP)这样的方法是有用的。这些方法通常用于预测难以解释的机器学习模型。

当数据科学家对这些技术有了很好的理解,他们就可以从不同的角度处理模型可解释性的问题。部分相关性图是一种很好的方法,可以很容易地将特征/预测关系可视化。随机森林有助于根据不同要素在决定结果中的重要程度对其进行排序。莱姆和 SHAP 确定复杂模型中的特征重要性,其中直接解释模型预测是不可行的,例如具有数百或数千个特征的深度学习模型,这些特征与输出具有复杂的非线性关系。了解这些方法中的每一种都可以帮助数据科学家接近机器学习模型变量的模型可解释性,无论它们是简单还是复杂。

我们将讨论如何应用这些方法并解释分类模型的预测。具体来说,我们将考虑逻辑回归模型、随机森林模型以及深度神经网络的模型可解释性任务。我们将使用虚构的电信客户流失数据,此处提供了这些数据。

将电信数据读入熊猫数据帧

首先,让我们将电信客户流失数据读入 Pandas 数据框架。首先,让我们导入熊猫库:

import pandas as pd

让我们使用 Pandas read_csv()方法将数据读入数据帧:

df = pd.read_csv(“telco_churn.csv”)

让我们显示前五行数据:

print(df.head())

作者图片

建模数据准备

我们将构建的每个模型都将性别、任期、月费、无纸账单、合同、支付方式、合作伙伴、家属和设备保护作为输入。我们的预测目标将是客户流失。首先,我们需要通过将分类输入转换成机器可读的分数来为训练做准备。

让我们看看将性别转换成分类代码的例子。首先,我们使用 Pandas astype 方法创建一个名为 gender_cat 的新列,其类别类型为:

df[‘gender_cat’] = df[‘gender’].astype(‘category’)

接下来,我们使用 Pandas cat.codes 属性提取分类代码:

df[‘gender_cat’] = df[‘gender_cat’].cat.codes

然后,我们对剩余的分类特征重复这一过程:

df[‘PaperlessBilling_cat’] = df[‘PaperlessBilling’].astype(‘category’)df[‘PaperlessBilling_cat’] = df[‘PaperlessBilling_cat’].cat.codesdf[‘Contract_cat’] = df[‘Contract’].astype(‘category’)df[‘Contract_cat’] = df[‘Contract_cat’].cat.codesdf[‘PaymentMethod_cat’] = df[‘PaymentMethod’].astype(‘category’)df[‘PaymentMethod_cat’] = df[‘PaymentMethod_cat’].cat.codesdf[‘Partner_cat’] = df[‘Partner’].astype(‘category’)df[‘Partner_cat’] = df[‘Partner_cat’].cat.codesdf[‘Dependents_cat’] = df[‘Dependents’].astype(‘category’)df[‘Dependents_cat’] = df[‘Dependents_cat’].cat.codesdf[‘DeviceProtection_cat’] = df[‘DeviceProtection’].astype(‘category’)df[‘DeviceProtection_cat’] = df[‘DeviceProtection_cat’].cat.codes

让我们还创建一个新列,将 churn 列中的 Yes/No 值映射到二进制整数(零和一)。在“流失分数”列中,当流失为“是”时,流失标签为 1,当流失为“否”时,流失标签为零:

import numpy as npdf[‘churn_score’] = np.where(df[‘churn’]==’Yes’, 1, 0)

接下来,让我们将输入存储在名为 X 的变量中,将输出存储在名为 y 的变量中:

X = df[[ ‘tenure’, ‘MonthlyCharges’, ‘gender_cat’, ‘PaperlessBilling_cat’,‘Contract_cat’,’PaymentMethod_cat’, ‘Partner_cat’, ‘Dependents_cat’, ‘DeviceProtection_cat’ ]]y = df[‘churn_score’]

接下来,让我们使用 scikit-learn 中 model_selection 模块中的 train _ test _ spliit 方法来拆分用于训练和测试的数据:

from sklearn.model_selection import train_test_splitX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)

接下来,让我们从 scikit-learn 导入 LogisticRegression 模型,并使该模型适合我们的训练数据:

lr_model = LogisticRegression()lr_model.fit(X_train, y_train)

让我们进行预测:

y_pred = lr_model.predict(X_test)

为了了解我们的模型的表现,我们将生成一个混淆矩阵:

from sklearn.metrics import confusion_matrixconmat = confusion_matrix(y_test, y_pred)val = np.mat(conmat)classnames = list(set(y_train))df_cm = pd.DataFrame(val, index=classnames, columns=classnames,)df_cm = df_cm.astype(‘float’) / df_cm.sum(axis=1)[:, np.newaxis]import matplotlib.pyplot as pltimport seaborn as snsplt.figure()heatmap = sns.heatmap(df_cm, annot=True, cmap=”Blues”)heatmap.yaxis.set_ticklabels(heatmap.yaxis.get_ticklabels(), rotation=0, ha=’right’)heatmap.xaxis.set_ticklabels(heatmap.xaxis.get_ticklabels(), rotation=45, ha=’right’)plt.ylabel(‘True label’)plt.xlabel(‘Predicted label’)plt.title(‘Churn Logistic Regression Model Results’)plt.show()

作者图片

我们可以看到,逻辑回归模型在预测将与公司合作的客户方面做得非常好,找到了 90%的真正负面因素。它在预测将要离开的顾客方面也做得相当不错,发现了 52%的真实信息。

逻辑回归模型的部分相关图

现在,让我们用部分相关图来解释这个模型。

from sklearn.inspection import plot_partial_dependencefeatures = [0, 1, (1, 0)]plot_partial_dependence(lr_model, X_train, features, target=1)

作者图片

我们发现,随着任期的延长,客户离开的可能性会降低。这种模式是有道理的,因为拥有更长任期的客户可能不太可能离开。

此外,客户离开的概率随着月费的增加而增加,这也是直观的。我们还可以看到租用期与月费的密度图。浅绿色/黄色表示密度较高。由此我们可以看出,大量每月充电器费用较高的客户也是相对较新的客户。在这个例子中,该公司可以利用这种洞察力来锁定每月交易和折扣费用较高的新客户,以努力留住他们。

随机森林特征重要性

接下来,我们将构建一个随机森林模型,并显示其特征重要性图。让我们从 Scikit-learn 中的 ensemble 模块导入随机森林包,根据我们的训练数据构建我们的模型,并根据对测试集做出的预测生成混淆矩阵:

conmat = confusion_matrix(y_test, y_pred_rf)val = np.mat(conmat)classnames = list(set(y_train))df_cm_rf = pd.DataFrame(val, index=classnames, columns=classnames,)df_cm_rf = df_cm_rf.astype(‘float’) / df_cm.sum(axis=1)[:, np.newaxis]import matplotlib.pyplot as pltimport seaborn as snsplt.figure()heatmap = sns.heatmap(df_cm, annot=True, cmap=”Blues”)heatmap.yaxis.set_ticklabels(heatmap.yaxis.get_ticklabels(), rotation=0, ha=’right’)heatmap.xaxis.set_ticklabels(heatmap.xaxis.get_ticklabels(), rotation=45, ha=’right’)plt.ylabel(‘True label’)plt.xlabel(‘Predicted label’)plt.title(‘Churn Random Forest Model Results’)plt.show()

作者图片

然后,我们可以显示一个带有特性重要性值的条形图:

features = [‘tenure’, ‘MonthlyCharges’, ‘gender_cat’, ‘PaperlessBilling_cat’,‘Contract_cat’,’PaymentMethod_cat’, ‘Partner_cat’, ‘Dependents_cat’, ‘DeviceProtection_cat’ ]print(rf_model.feature_importances_)feature_df = pd.DataFrame({‘Importance’:rf_model.feature_importances_, ‘Features’: features })sns.set()plt.bar(feature_df[‘Features’], feature_df[‘Importance’])plt.xticks(rotation=90)plt.title(‘Random Forest Model Feature Importance’)plt.show()

作者图片

这里我们看到,促使客户离开的最重要因素是任期、月费和合同类型。结合部分相关图使用随机森林的特征重要性是一种强大的技术。你不仅知道哪些因素是最重要的,还知道这些因素与结果的关系。我们以终身职位为例。从随机森林特征的重要性我们看到,保有权是最重要的特征。从部分相关图中,我们看到任期和客户离开的概率之间存在负线性关系。这意味着客户在公司呆的时间越长,他们离开的可能性就越小。

莱姆和 SHAP 为神经网络模型

当处理像深度神经网络这样更复杂的黑盒模型时,我们需要转向模型可解释性的替代方法。这是因为,与逻辑回归模型中可用的系数或随机森林等基于树的模型的内置特征重要性不同,神经网络等复杂模型不提供对特征重要性的任何直接解释。莱姆和 SHAP 是解释复杂模型最常用的方法。

让我们建立一个人工神经网络分类模型。从模型构建开始,让我们从 Keras 导入顺序和密集方法:

from tensorflow.keras.models import Sequentialfrom tensorflow.keras.layers import Dense

接下来,让我们初始化顺序方法:

model = Sequential()

让我们向模型对象中添加两个包含八个节点的层。我们需要使用输入要素的数量来指定输入形状。在第二层中,我们指定了一个激活函数,它代表了神经元放电的过程。这里,我们使用整流线性单元(ReLu)激活函数:

model.add(Dense(8, input_shape = (len(features),)))model.add(Dense(8, activation=’relu’))

然后,我们添加带有一个节点的输出层,并编译我们的模型:

model.add(Dense(1, activation=’sigmoid’))model.compile(optimizer=’adam’, loss=’binary_crossentropy’, metrics=[‘accuracy’])

一旦我们的模型被编译,我们就使我们的模型适合我们的训练数据:

model.fit(X_train, y_train, epochs = 1)

然后,我们可以根据测试数据进行预测:

y_pred = [round(float(x)) for x in model.predict(X_test)]

并生成混淆矩阵:

conmat = confusion_matrix(y_test, y_pred_nn)val = np.mat(conmat)classnames = list(set(y_train))df_cm_nn = pd.DataFrame(val, index=classnames, columns=classnames,)df_cm_nn = df_cm_nn.astype(‘float’) / df_cm_nn.sum(axis=1)[:, np.newaxis]plt.figure()heatmap = sns.heatmap(df_cm_nn, annot=True, cmap=”Blues”)heatmap.yaxis.set_ticklabels(heatmap.yaxis.get_ticklabels(), rotation=0, ha=’right’)heatmap.xaxis.set_ticklabels(heatmap.xaxis.get_ticklabels(), rotation=45, ha=’right’)plt.ylabel(‘True label’)plt.xlabel(‘Predicted label’)plt.title(‘Churn Neural Network Model Results’)plt.show()

作者图片

现在,让我们用 SHAP 来解释我们的神经网络模型:

import shapf = lambda x: model.predict(x)med = X_train.median().values.reshape((1,X_train.shape[1]))explainer = shap.Explainer(f, med)shap_values = explainer(X_test.iloc[0:1000,:])shap.plots.beeswarm(shap_values)

作者图片

正如我们从随机森林模型使用权中看到的,月度费用和合同是解释结果的三个主要特征。

LIME 是对复杂模型的特征重要性进行可视化的另一个选项。LIME 的计算速度通常比 SHAP 快,因此如果需要快速生成结果,LIME 是更好的选择。在实践中,SHAP 将比 LIME 更准确地解释特征,因为它在数学上更严格。由于这个原因,SHAP 计算量更大,如果你有足够的时间和计算资源,它是一个不错的选择。

让我们用石灰来解释我们的神经网络预测:

import limefrom lime import lime_tabularexplainer = lime_tabular.LimeTabularExplainer(training_data=np.array(X_train),feature_names=X_train.columns,class_names=[‘Yes’, ‘No’],mode=’classification’)exp = explainer.explain_instance(data_row=X_test.iloc[1], predict_fn=model.predict, labels=(0,))exp.show_in_notebook(show_table=True)

作者图片

正如我们所料,我们发现月费和租期的影响最大。这篇文章的代码可以在 GitHub 上找到。

结论

根据所解决的问题,这些方法中的一种或多种可能是解释模型预测的好选择。如果您要处理相对较少的输入要素和较小的数据集,使用逻辑回归和部分相关图就足够了。如果您正在处理中等数量的输入要素和中等大小的数据集,随机森林是一个很好的选择,因为它很可能优于逻辑回归和神经网络。如果您正在处理具有数百万行和数千个输入要素的数千兆字节的数据,神经网络将是更好的选择。从那里,根据可用的计算资源,你可以与莱姆或 SHAP 合作来解释神经网络预测。如果时间有限,石灰是更好的选择,虽然不太准确。如果你有足够的时间和资源,SHAP 是更好的选择。

虽然我们看了一个简单的例子,用一个相对小而干净的数据集来留住客户,但是有多种类型的数据可以在很大程度上影响哪种方法是合适的。例如,对于一个小问题,如给定一小组产品特征作为输入,预测产品的成功,逻辑回归和部分依赖图就足够了。当处理更标准的行业问题时,如客户保留或甚至预测信用违约,特征的数量通常是适中的(少于数百个),数据的大小也是适中的,因此基于树的模型如随机森林及其特征重要性更合适。此外,医疗保健中的许多问题,如使用 EHR 数据预测医院再入院,涉及数百个(有时数千个)输入特征的训练模型。在这种情况下,用莱姆或 SHAP 解释的神经网络更合适。对于各行各业的数据科学家来说,知道何时使用给定数据类型的特定模型和可解释方法是一项无价的技能。

如果你有兴趣学习 python 编程的基础知识、Pandas 的数据操作以及 python 中的机器学习,请查看Python for Data Science and Machine Learning:Python 编程、Pandas 和 sci kit-初学者学习教程 。我希望你觉得这篇文章有用/有趣。

本帖原载于 内置博客 。原片可以在这里找到https://builtin.com/machine-learning/model-explainability-python

掌握 Python 中的模型选择

原文:https://towardsdatascience.com/mastering-model-selection-in-python-7dc2f8fcf57c?source=collection_archive---------30-----------------------

了解模型测试、特性选择和模型调整

照片由卢卡斯在像素上拍摄

建立稳定、准确和可解释的机器学习模型是许多行业公司的重要任务。随着基础训练数据的更新,机器学习模型预测必须在时间上保持稳定。由于不可预见的事件导致的数据的剧烈变化会导致模型性能的显著恶化。模型超参数调整可以帮助对机器学习模型进行必要的更改,这些模型考虑了数据随时间的统计变化。了解测试模型的各种方法也很重要,这取决于您拥有的数据量以及模型预测的稳定性。此外,最有用的模型使用可操作和可解释的输入或特征。鉴于此,很好地理解如何选择最佳特性是很重要的。最佳特性的选择通常是通过领域专家来完成的。还有其他的特征选择方法,结合领域专业知识,可以帮助构建精确的模型,用于产生可操作的见解。强大的模型测试、特征选择和模型调整知识,以及领域专业知识,可以帮助公司从机器学习模型预测中获得最大收益。

模型测试是模型建立的一个非常重要的部分。如果操作正确,它可以确保你的模型稳定,不会过度拟合。三种最广为人知的模型测试方法是随机训练/测试分割、K 折交叉验证和留一交叉验证。随机训练测试分割是最简单的,其中从数据中抽取随机样本来组成测试数据集,而剩余的数据用于训练。K-fold 交叉验证随机将数据分成 K 个“部分”(称为折叠),其中一个折叠用于测试,其余折叠用于测试。方法是在每个折叠上迭代,直到所有数据都用于训练和测试模型。然后对褶皱上的性能进行平均。最后,leave one out 类似于 K-folds,但它使用单个数据点进行测试,其余数据用于训练。它迭代每个数据点,直到所有数据都用于测试和训练。根据你的数据大小和你希望最小化偏差的程度,你可能更喜欢一种方法而不是另一种,我们将在后面讨论。

特征选择是模型构建的一个非常重要的部分,因为它直接影响模型的性能和模型的可解释性。最简单的特征选择方法是手动特征选择,这在理想情况下由领域专家指导,您选择不同的特征集,直到您对验证集的性能满意为止(通常您有一个与验证集分离的支持测试集,您在模型开发结束时对其进行一次测试,以避免过度拟合)。还有其他方法,比如 SelectKBest,可以自动完成这个过程,我们也将讨论这些方法。如果您的数据有许多列,手动选择要素可能会成为一项繁琐的任务,则这些选项特别有用。过滤输入集的大小有助于使模型预测更易于解释,从而使预测更具可操作性。

最后,超参数调整也是模型构建的一个非常重要的部分。虽然大多数机器学习包都带有默认参数,通常可以提供不错的性能,但通常需要额外的调整来构建非常准确的模型。这可以手动完成,通过选择不同的参数值并在样本验证中测试模型(对训练数据),直到您对性能满意为止。还有一些 python 包,允许您在超参数空间中搜索大多数算法,并选择提供最佳性能的参数。

我们将讨论如何应用这些方法来测试、调整和选择用于分类任务的机器学习模型。具体来说,我们将考虑预测客户流失的任务,其中流失被定义为客户离开公司的事件。我们将使用公开的虚构电信客户流失数据,这些数据可在这里获得。

读取和显示电信数据

首先,让我们导入 pandas 库,并将电信数据读入 pandas 数据框:

import pandas as pddf = pd.read_csv(“telco.csv”)print(df.head())

为训练和测试拆分数据的方法

随机训练测试分割

我们将讨论的第一种测试方法是随机训练测试分割。让我们建立一个简单的模型,将客户任期和每月费用作为输入,并预测客户是否会流失。在这里,我们的投入将是任期和月费,我们的产出将是流失。首先,让我们使用 numpy 包中的 np.where()方法将流失值转换为机器可读的二进制整数:

import numpy as npdf[‘Churn_binary’] = np.where(df[‘Churn’] == ‘Yes’, 1, 0)

现在让我们从 scikit-learn 中的模型选择模块导入 train_test_split 方法:

from sklearn.model_selection import train_test_split

正如在文档中所解释的,train_test_split 方法将数据分成随机的训练和测试子集。为了执行分割,我们首先根据分别称为 X 和 y 的变量来定义输入和输出:

X = df[[‘tenure’, ‘MonthlyCharges’]]y = df[‘churn’]

接下来,我们将这些变量传递给 train_test_split 方法。该方法返回训练输入和测试输入以及训练输出和测试输出的随机子集。在 python 中,当方法或函数返回多个值时,它们通常是元组。我们需要对返回元组进行解包,并将正确的值存储在我们的训练和测试变量中:

X_train, X_test, y_train, y_test = train_test_split(X, y)

我们还可以指定一个名为 random_state 的参数。这是一个随机参数,控制如何随机分割数据。如果我们给它一个整数值,我们确保在每次运行时都复制相同的分割:

X_train, X_test, y_train, y_test = train_test_split(X, y, random_state =42)

然后,使用您的训练和测试子集来训练和测试模型是很简单的。例如,要训练随机森林:

from sklearn.ensemble import RandomForestClassifiermodel = RandomForestClassifier()model.fit(X_train, y_train)y_pred = model.predict(X_test)

然后,您可以使用 y_test 和 y_pred 来评估模型的性能。要考虑的一件重要事情是你是否有时间相关的数据。如果是这种情况,您不应该执行随机分割,而是按日期分割您的数据。如果使用随机拆分,您将在训练集中包含未来的数据,并且您的预测将会有偏差。

在实践中,随机训练测试拆分对于生成一个保留验证集非常有用,您可以在完成功能选择和模型调整后使用该验证集进行一次测试。这确保了你的模型不会过度拟合,并减少了你的模型高度偏差的机会。

K 倍交叉验证

K 折叠交叉验证是将数据分成整数“K”个部分,并将一部分用于测试,其余部分用于训练的过程。这是反复进行的,直到所有数据都用于训练和测试。交叉验证方法的文档可以在这里找到。

为了实现 K 折叠,我们从 scikit-learn 中的模型选择模块导入 K 折叠:

from sklearn.model_selection import KFold
folds = KFold(n_splits=5)
folds.get_n_splits(X)for train_index, test_index in folds.split(X):
    X_train, X_test, y_train, y_test = X.iloc[train_index],     X.iloc[test_index], y.iloc[train_index], y.iloc[test_index]
    model = RandomForestClassifier()
    model.fit(X_train, y_train)
    y_pred = model.predict(X_test)
    fold+=1
    print(f”Accurac in fold {fold}:”, accuracy_score(y_pred, y_test))

在 for 循环中,您可以在不同的折叠上训练和测试您的模型。这里我们只使用了 5 次折叠,但是你可以改变 KFold 方法中的 n_splits 参数。使用的折叠越多,模型输出中的偏差就越小。这允许您通过诸如方差之类的度量来分析模型性能的稳定性。这通常也用于模型调优和特性选择等任务,我们稍后将对此进行介绍。理想情况下,您需要性能差异最小的最精确模型。低性能差异意味着模型更稳定,因此更可靠。

留一交叉验证

去掉一个类似于 K 折叠,但是不是使用随机采样的子集进行训练和测试,而是使用单个数据点进行测试,而其余的用于训练。这也是反复进行的,直到所有数据都用于训练和测试:

loo = LeaveOneOut()
for train_index, test_index in loo.split(X):
    X_train, X_test, y_train, y_test = X.iloc[train_index], X.iloc[test_index], y.iloc[train_index], y.iloc[test_index]

这通常用于较小的数据集。根据我的经验,我发现这对于小的不平衡数据集很有用。值得注意的是,由于您正在训练您的模型 n 次,其中 n 是数据的大小,这对于大型数据集可能是计算密集型的。

建模的特征选择方法

除了通常通过探索性数据分析和使用领域专业知识完成的手动要素选择之外,还有用于要素选择的有用 python 包。这里我们将讨论 SelectKBest 方法。SelectKBest 的文档可在这里找到。首先让我们导入必要的包:

从 sklearn.feature_selection 导入选择测试,f_classif

将 matplotlib.pyplot 作为 plt 导入

我们将选择按月收费,任期和总费用。首先,我们需要清理 TotalCharges 列:

df[‘TotalCharges’] = df[‘TotalCharges’].replace(‘ ‘, np.nan)df[‘TotalCharges’].fillna(0, inplace = True)df[‘TotalCharges’] = df[‘TotalCharges’].astype(float)

我们应该在训练集上选择特征,这样我们就不会偏向我们的模型。让我们重新定义我们的输入和输出:

X = df[[‘tenure’, ‘MonthlyCharges’, ‘TotalCharges’]]y = df[‘Churn’]

现在,让我们执行随机训练/测试分割:

X_train, X_test, y_train, y_test = train_test_split(X, y, random_state= 42)

接下来让我们定义我们的选择器对象。我们将传入我们的特性和输出:

numerical_predictors = [“MonthlyCharges”, “TotalCharges”, “tenure” ]numerical_selector = SelectKBest(f_classif, k=3)numerical_selector.fit(X_train[numerical_predictors], y_train)

现在,我们可以为我们的功能绘制分数。我们通过对每个特征的 p 值取负对数来为每个特征生成分数:

num_scores = -np.log10(numerical_selector.pvalues_)plt.bar(range(len(numerical_predictors)), num_scores)plt.xticks(range(len(numerical_predictors)), numerical_predictors, rotation=’vertical’)plt.show()

我们可以看到任期得分最高,这是有道理的。直觉上,与公司相处时间越长的客户流失的可能性就越小。我想强调的是,在实践中,特征选择应该在训练数据上进行。此外,为了提高所选特征的可靠性,您可以运行 K-fold 交叉验证,取每个特征的平均分数,并将结果用于特征选择。

超参数调谐

除了模型测试和特征选择之外,模型超参数调整是模型构建中非常重要的一部分。这个想法是寻找能给出最佳性能的模型参数。scikit-learn 的 RandomizedSearchCV 方法允许您对估计量的参数执行随机搜索。RandomizedSearchCV 的文档可以在这里找到。这里,我们将对随机森林参数执行随机搜索。我们首先定义一个随机森林参数值的网格。首先让我们指定我们将在随机森林中使用的三棵树的列表:

n_estimators = [50, 100, 200]

然后,我们指定每次分割时要考虑的特性数量

max_features = [‘auto’, ‘sqrt’, ‘log2’]

我们还指定了树中的最大级别数

max_depth = [int(x) for x in np.linspace(10, 30, num = 5)]max_depth.append(None)

分割一个节点所需的最小样本数:

min_samples_split = [2, 5, 10]

每个叶节点所需的最小样本数

min_samples_leaf = [1, 2, 4]

最后,我们是否会使用 bootstrap 抽样:

bootstrap = [True, False]

我们现在可以指定一个字典作为我们的参数网格:

random_grid = {‘n_estimators’: n_estimators,‘max_features’: max_features,‘max_depth’: max_depth,‘min_samples_split’: min_samples_split,‘min_samples_leaf’: min_samples_leaf,‘bootstrap’: bootstrap}

让我们也定义一个随机森林模型对象:

model = RandomForestClassifier()

类似于特征选择,应该对训练数据进行模型超参数调整。为了继续,让我们从 scikit-learn 导入 RandomizedSearchCV:

from sklearn.model_selection import RandomizedSearchCV

接下来,我们定义一个 RandomizedSearchCV 对象。在对象中,我们传递随机森林模型、random_grid 以及每次随机搜索的迭代次数。请注意,有一个名为“cv”的参数用于交叉验证。我们使用这个参数来定义用于验证的折叠数,就像我们对 K 折叠所做的那样。同样,我们希望找到给出最佳模型性能的随机森林参数集,模型性能是通过使用交叉验证的 RandomizedSearchCV 计算的。参数 verbose 显示每次迭代的输出。由于我们有 3 次折叠和 3 次迭代,我们应该看到 9 次测试运行的输出:

rf_random = RandomizedSearchCV(estimator = model, param_distributions= random_grid, n_iter = 3, cv =3, verbose=2, random_state=42)

在定义了 RandomizedSearchCV 对象之后,我们可以拟合我们的训练数据:

rf_random.fit(X_train, y_train)

拟合后,我们可以输出最佳性能的参数:

parameters = rf_random.best_params_print(parameters)

您可以增加迭代次数来搜索和测试更多参数。迭代次数越多,就越有可能从超参数集中找到性能更好的模型。显然,你搜索和测试的参数越多,计算时间就越长。记住这一点也很好,在典型的机器或笔记本电脑上,对于非常大的数据集,这可能变得难以处理,您可能需要使用分布式计算工具,如 databricks。了解其他超参数调整工具也很有用。例如,GridSearchCV 在整个网格上执行穷举搜索。这意味着测试了参数的每一种可能的组合。如果你有足够的计算能力,这是一个不错的选择。

结论

很好地理解哪些工具可用于构建稳健的机器学习模型是每个数据科学家都应该具备的技能。能够为训练和测试准备数据、选择特征和调整模型参数对于构建预测可靠的稳定模型是必要的。此外,将这些工具放在你的后袋中可以节省大量的工时,因为这些方法将原本需要手动完成的工作自动化了。这些技术如果使用得当,可以降低模型恶化的风险,这种风险会使公司损失数百万美元。

理解如何恰当地设置模型测试对于确保您不会过度拟合您的模型是非常重要的。例如,如果您没有正确地分割用于训练和测试的数据,您的模型测试可能会给您一个错误的模型准确性的感觉,这对一个公司来说可能是非常昂贵的。在客户流失的情况下,如果你的模型是虚假的准确,你可能会错误地锁定那些实际上不太可能流失的客户。这可能导致数百万美元的广告损失。为了降低过度拟合和高估模型性能的风险,关键是要有一个保留测试集(就像我们从随机训练测试分割中生成的测试集),在对训练数据进行模型调整和特征选择之后,我们对该测试集执行单个测试。此外,交叉验证方法让我们很好地了解我们的模型在模型调整和特征选择方面的稳定性。交叉验证允许我们看到多个测试之间的性能差异。理想情况下,我们希望选择能够以最低的模型性能差异提供最佳性能的特性和模型参数。模型性能的低方差意味着模型预测更可靠,风险更低。

特征选择也非常重要,因为它可以帮助过滤掉潜在的大量输入。例如,无论模型多么精确,都很难解释 1000 个特征的值如何决定客户是否会流失。特征选择和领域专业知识可以帮助数据科学家选择和解释预测结果的最重要特征,例如客户流失。超参数调整也是实现最佳模型性能的必要步骤。虽然 scikit-learn 机器学习包提供的默认参数值通常提供了不错的性能,但为了实现非常精确的模型,需要进行模型调整。例如,通过客户流失,你越准确地锁定可能离开公司的客户,错误锁定的客户损失的金钱就越少,高风险客户的客户终身价值就越大。

对于任何行业的数据科学家来说,非常熟悉用于设置模型测试、选择功能和执行模型调整的工具是一项非常宝贵的技能。掌握这些知识可以帮助数据科学家构建稳健可靠的模型,从而为公司带来显著的价值。这可以在金钱和工时方面节省大量资源,并增加公司利润。这篇文章的代码可以在 GitHub 上找到。

如果你有兴趣学习 python 编程的基础知识、Pandas 的数据操作以及 python 中的机器学习,请查看Python for Data Science and Machine Learning:Python 编程、Pandas 和 sci kit-初学者学习教程 。我希望你觉得这篇文章有用/有趣。

本帖原载于 内置博客 。原片可以在这里找到https://builtin.com/data-science/machine-learning-models-python

掌握 Python 中的异常检测

原文:https://towardsdatascience.com/mastering-outlier-detection-in-python-61d1090a5b08?source=collection_archive---------19-----------------------

Python 中检测数据异常值的有用方法

马库斯·斯皮斯克在的照片

离群点检测,也称为异常检测,是许多数据科学团队的常见任务。它是识别与分布的其余部分相比具有极值的数据点的过程。异常值检测具有广泛的应用,包括数据质量监控、识别金融中的价格套利、检测网络安全攻击、医疗欺诈检测、钞票伪造检测等等。

在每一个应用中,异常值对应于罕见的事件。例如,在网络安全攻击的情况下,数据中表示的大多数事件不会反映实际的攻击。一小部分数据将代表真正的网络攻击。

类似地,对于伪钞检测,大部分记录将代表真钞,而伪钞将构成总数据的一小部分。异常值检测的任务是量化常见事件,并将其用作识别数据中相对异常的参考。

Python 提供了各种易于使用的异常检测方法和包。然而,在选择方法之前,您需要首先考虑模态。这是分布中包含的峰值数量。例如,假设您有一个由运动员体重组成的数据列。如果数据是多峰的,则分布中有许多高度密集的区域。这可能意味着许多运动员的体重在 200 到 210 磅之间,相当数量的运动员体重在 230 到 240 磅之间。除了模态之外,在考虑异常值检测的方法时,您应该考虑数据集的大小和维度,也就是列的数量。

如果您的数据相对较小,比如只有几十个要素和几千行,那么简单的统计方法(如箱线图可视化)就足够了。如果您的数据是单峰的,这意味着分布中有一个单峰,像椭圆包络这样的方法是有用的。如果您的数据是中等大小和多模态的(意味着有许多峰值),隔离林是更好的选择。如果您的数据是高维、大型和多模态的,那么 OneClassSVM 可能是更好的选择。

在这里,我们将使用各种方法来解决使用瑞士钞票伪钞检测数据集识别伪钞的任务。

读入数据

首先,让我们导入 Pandas 库,它用于读入、转换和分析数据。我们将使用 Pandas 将我们的数据读入数据框:

进口熊猫作为 pd

df = pd.read_csv(“banknotes.csv”)

接下来,让我们使用。head()方法:

print(df.head())

作者图片

这里我们看到数据包含列长度、左、右、下、上和对角线,它们都对应于以毫米为单位的钞票尺寸。我们还看到一个伪造列,它具有表示钞票是伪造的还是真实的基础真值。值 1 对应于伪造,值 0 表示真实。

使用箱线图进行异常值检测

箱线图是可视化数据中数值四分位数的标准方法。四分位数将数字数据分为四组:

  1. 第一个四分位数是最小值和中值之间的中间数,因此 25%的数据低于这个点。
  2. 第二个四分位数是中间值,这意味着 50%的数据低于这个点。
  3. 第三个四分位数是最大值和中值之间的中间数,因此 75%的数据低于这个点。
  4. 第四个四分位数是最高的 25%的数据

让我们来看看长度列的箱线图。为此,让我们导入 Seaborn 并使用方框图方法。让我们也导入 Matplotlib,我们将使用它来命名我们的方框图:

import seaborn as snsimport matplotlib.pyplot as pltsns.boxplot(data=df,x=df[“Length”])plt.title(“Boxplot of Swiss Banknote Length “)

作者图片

方框图中的点对应极端异常值。我们可以通过过滤我们的数据帧并使用计数器方法计算伪造的数量来验证这些是异常值:

df_outlier1 = df[df[‘Length’]> 216].copy()print(Counter(df_outlier1[‘conterfeit’]))

作者图片

我们看到,在这些条件下,我们只能捕获每 100 张假钞中的一张。如果我们放宽过滤条件来捕捉额外的异常值,我们会发现我们也捕捉到了真实的钞票:

df_outlier2 = df[df[‘Length’]> 215.5].copy()print(Counter(df_outlier2[‘conterfeit’]))

作者图片

这相当于 0.30 的精度,这不是很好的性能。更糟糕的是,这相当于 1.5%的精度。

为了帮助解决这种不准确性,我们可以查看额外列的箱线图。让我们为剩余的列创建盒状图,以及一个允许我们为任何数字列生成盒状图的函数:

def boxplot(column): sns.boxplot(data=df,x=df[f”{column}”]) plt.title(f”Boxplot of Swiss Banknote {column}”) plt.show()

让我们用列长度(左、右、下、上和对角线)来调用函数:

boxplot(‘Length’)boxplot(‘Right’)boxplot(‘Left’)boxplot(‘Bottom’)boxplot(‘Top’)boxplot(‘Diagonal’)

作者图片

我们可以过滤前 50%的长度、右侧、左侧和底部:

df_outlier3 = df[(df[‘Length’]> 215)&(df[‘Right’]> 130)&(df[‘Left’]> 130)&(df[‘Bottom’]> 10)].copy()print(Counter(df_outlier3[‘conterfeit’]))

我们看到我们现在捕获了八个伪造品。虽然这是对我们之前捕获的单张假钞的改进,但我们仍然错过了另外 92 张假钞,这相当于 4%的准确率。此外,数据中的数字列越多,异常值检测的任务就变得越麻烦。因此,箱线图非常适合于只有几列的小而简单的数据集。

使用隔离森林进行异常值检测

隔离林是一种异常值检测方法,它通过随机选择列及其值来分离数据的不同部分。它适用于更复杂的数据,例如包含更多列和多模态数值的集合。

让我们导入 IsolationForest 包,并使其适合长度、左、右、下、上和对角线列。请注意,该算法只接受输入,因为它是一种无监督的机器学习技术,不像有监督的机器学习技术,它对特征和目标都进行了训练。幸运的是,我们仍然可以验证我们的预测,因为我们的数据来自假冒标签。

首先,让我们导入必要的包:

from sklearn.ensemble import IsolationForestfrom sklearn.model_selection import train_test_splitfrom sklearn.metrics import precision_scoreimport numpy as np

接下来,让我们定义我们的输入和输出(我们将仅用于验证,而不是训练),并分割我们的数据:

X = df[[‘Length’, ‘Left’, ‘Right’, ‘Bottom’, ‘Top’, ‘Diagonal’]]y = df[‘conterfeit’]X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)

接下来,让我们将模型与输入相匹配:

clf = IsolationForest(random_state=0)clf.fit(X_train)y_pred = clf.predict(X_test)

最后,我们来预测一下测试数据,评估一下精度评分。同样,在实践中,由于这是无监督的机器学习,我们不会有标签来验证我们的模型。但是,对于我们这里的目的,我们将进行验证,以便我们对这些方法检测异常值的能力有所了解:

pred = pd.DataFrame({‘pred’: y_pred})pred[‘y_pred’] = np.where(pred[‘pred’] == -1, 1, 0)y_pred = pred[‘y_pred’]print(“Precision:”, precision_score(y_test, y_pred))

作者图片

我们看到我们的异常值检测模型的精度为 0.625。将此与我们通过箱线图获得的 0.30 的精度进行比较。该模型还提供了 56%的准确度(相比之下,箱线图的准确度为 4%),这表明在异常值检测方面有了显著的改进。这是因为隔离林能够对数据进行分区,并根据多个要素识别异常值。当我们使用箱线图时,我们必须手动检查异常值,并尝试使用多个特征得出结论,这在特征数量越多时变得越来越困难。例如,您可以拥有一个点聚类,其中单个特征值可能不是异常值,但值的组合可能是异常值。这种类型的行为很难通过检查箱线图来发现。

使用 OneClassSVM 进行离群点检测

现在,让我们探索如何使用 OneClassSVM 进行离群点检测。这是另一种无监督的机器学习技术,对于高维和大数据集很有用。让我们导入必要的包,适应我们的模型并评估性能:

clf_svm = OneClassSVM(gamma=’auto’)clf_svm.fit(X_train)y_pred_svm = clf_svm.predict(X_test)pred[‘svm’] = y_pred_svmpred[‘svm_pred’] = np.where(pred[‘svm’] == -1, 1, 0)y_pred_svm = pred[‘svm_pred’]print(“SVM Precision:”, precision_score(y_test, y_pred_svm))

作者图片

我们发现,与 IsolationForest 相比,我们的精度有所下降。当我们将 IsolationForests 与 OneClassSVM 进行比较时,我们的准确率也从 56%下降到 47%。这可能是因为我们的数据相对较小且维度较低,而我们的模型过度拟合了这些数据。这意味着该算法对数据中不符合可辨别模式的随机噪声和波动进行建模。在这种情况下,模型学习的随机噪声无法帮助捕获异常值和内值(正常数据点)之间的分离。OneClassSVM 过度拟合的趋势解释了与 IsolationForest 相比性能下降的原因。

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

结论

虽然我们着眼于解决用于识别伪钞的异常值检测任务的方法,但是这些方法可以应用于各种异常值检测任务。例如,箱线图可用于信用卡欺诈检测等任务。隔离林可用于制造业中的缺陷项目检测等任务。OneClassSVM 可用于涉及高维数据的任务,例如使用社交媒体文本数据检测欺凌或恐怖活动。

无论您处理的是金融数据、制造数据还是社交媒体数据,掌握这些工具的相关知识对任何数据科学家都是有用的。虽然我们只考虑了表格数字数据,但是异常值检测的基本概念适用于所有用例。本文可以作为刚刚开始学习 Python 离群点检测技术的数据科学家的基础。这些易于使用的软件包可以帮助数据科学家解决各种常见的异常值检测问题,从而为客户、数据科学团队和公司整体带来附加值。

如果你有兴趣学习 python 编程的基础知识、Pandas 的数据操作和 python 中的机器学习,请查看Python for Data Science and Machine Learning:Python 编程、Pandas 和 sci kit-初学者学习教程 。我希望你觉得这篇文章有用/有趣。

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

掌握 Python 字符串

原文:https://towardsdatascience.com/mastering-python-strings-3c933686962a?source=collection_archive---------30-----------------------

蒙罗工作室在 Unsplash 拍摄的照片

在解决现实世界的数据科学挑战时,处理字符串是不可避免的。数据集中的许多要素在引用时都具有文本内容或字符串形式的值。舒适地处理字符串变得非常重要——创建、操作、修改——即使你不处理自然语言处理之类的东西。

在这篇文章中,我们的目标是带您了解 python 中字符串对象的基本知识,以及如何使用大量可用函数来处理字符串,这样您就可以像专业人士一样开始处理字符串了!。

所以让我们开始吧!

这篇文章大致分为以下几个主题:

  1. 创建字符串对象

  2. 分析字符串

  3. 字符串操作

  4. 格式化字符串输出

  5. 创建字符串对象

在 python 中,我们通过用单引号或双引号将内容括起来来创建字符串对象。

作者图片

我们还可以使用str()函数将几乎任何 python 对象转换成它的字符串表示。这里有几个微不足道的例子:

2。分析字符串

检查长度:

内置的len()函数给出了字符串对象的长度,其中也包括空格字符。

检查机箱:下、上、标题机箱

我们可以使用islower()内置的 python 函数来检查一个字符串对象是否全部是小写字符。isupper()检查所有的大写字母。istitle()检查每个单词开头的大写字母。

检查字符串对象的内容

字符串是字母数字吗?它包含字母和数字吗?isalnum()方法给出了答案。

我们还可以使用isdigit()方法检查字符串是否只有数字,并使用isdecimal()检查它是否是十进制字符串。

搜索字符串对象内容

我们可以使用 string 对象的find()方法在一个更大的字符串中搜索一个字符串(子字符串)。这将返回子字符串在字符串中第一个匹配项的索引位置,如果没有找到,则返回值-1。

index()方法做同样的事情,但是如果没有找到子串,就返回一个值错误。

或者,我们可以使用in关键字在另一个字符串对象中搜索一个字符串,它会返回一个布尔值,如下所示:

使用replace()方法可以实现“搜索和替换”。它接受 3 个参数——要替换的字符串、将替换找到的字符串的字符串和一个可选的 count 参数,我们可以在该参数中指示要执行多少次这样的搜索和替换操作。

注意在下面的例子中,对于long_string对象,最后一次出现的 long 没有被替换,因为我们只指定了 3 次替换。

rfind()的工作方式与find()类似,但是rfind()返回子串最近一次出现的索引号,如果没有找到子串,则返回-1。下面是findrfind的对比。

作为搜索子字符串的一个特例,如果我们想检查一个字符串对象是以一个特定的子字符串开始还是结束,那么我们可以使用startswith()endswith()方法。

作为在字符串中搜索这一主题的自然延伸,我们可以使用 string 对象的count()方法来计算子字符串的出现次数。请注意,这将返回子字符串不重叠出现的次数。

3。字符串操作

现在,让我们看看修改字符串对象的可用方法。

之前我们使用isupper()islower()istitle()方法检查了字符串对象中字符的大小写。没有“是”的相应方法实际上实现了更改。

使用swapcase()方法,我们可以在全大写和全小写之间切换。

capitalize()让我们将字符串对象的第一个字符转换成大写。

分割一个字符串对象

我们可以使用指定的拆分字符将一个字符串对象拆分成一系列子字符串。默认的拆分字符是空格,这会导致字符串被拆分成一系列“单词”。

加入字符串对象列表

现在让我们试着将一系列字符串对象连接在一起。我们为此使用了join()方法。在一个字符串对象上调用了join()方法,我们想用它来连接多个字符串对象。在下面的例子中,我们使用字符串-连接拆分的字符串

现在让我们通过使用join()方法将由空格分隔的字符串列表连接在一起,得到 my_str 对象的原始值。

请注意,字符串操作是在原始字符串对象的副本上执行的,如果您希望在原始对象上进行更改,则必须将输出重新分配给相同的字符串对象名称。

修剪字符串中的前导字符和尾随字符

通常这是指修剪字符串前面或后面多余的空格,尽管相应的方法也可以用来删除开头或结尾的任何其他字符。strip()删除两端的字符(如果没有指定,默认为空格),而lstrip()rstrip()分别删除调用它们的字符串对象开头和结尾的空格(`或其他指定字符)。

从 python 3.9 开始,我们有了一个removeprefix()removesuffix(),它们在动作上与rstrip()lstrip()相同。

划分字符串

partition()方法将一个字符串分成 3 部分——所提供的子字符串第一次出现之前的部分、子字符串本身以及子字符串第一次出现之后的部分。下面的例子应该可以说明这一点。

拆分多行字符串

类似于split()方法,我们有splitlines()方法,它将一个多行字符串对象分解成每行的字符串列表。行尾定义为在创建字符串时按下“enter”键的位置。

然后可以根据索引号访问结果列表元素。让我们看看如果我们再插入几个空行,输出是如何变化的,以便清楚地了解splitlines()是如何工作的。

4。格式化字符串输出

我们经常会发现自己不得不在报告中包含 python 代码的字符串输出。这对于确保正确对齐以确保更好的可读性来说是一个挑战。生成此输出的代码有时也会变得令人毛骨悚然。幸运的是,我们可以使用多种多样的字符串格式选项来管理这种情况。

首先让我们看看代码的可读性。在 python 的早期版本(3.7 之前的版本)中,必须使用%方法或format方法。从 python 3.7 开始,我们有了f-strings,这使得事情变得更加简单。

在第一种方法中,%s%d占位符指的是字符串和进入字符串的数字值。如果相应的变量 field 和 time 还不是字符串对象,则它们将被转换为字符串形式,并被插入到输出中的占位符中。

在第二种方法中,在显示输出之前,在 string 对象上调用的 format 方法将变量的字符串形式传递给 string 对象中的{ }占位符。或者,可以在{ }中提及格式内变量的序列号。否则,这些值将以传递给format()方法的相同顺序插入。

就代码可读性而言,第三种方法比其他两种方法好。要插入到字符串输出中的变量的名称在{}中直接提及。我们需要在左引号前添加一个 f,以向 python 表明我们希望 string 对象被视为 f 字符串(对于格式化字符串)。

对齐字符串输出

在格式化的字符串中,各种格式规范选项可以帮助我们整齐地对齐输出。我们可以指定输出应该占用的最小插槽数量,无论是左对齐、右对齐还是居中对齐等。点击此处查看详细文档。

来源:https://docs.python.org/3/library/string.html

下面的代码和输出展示了如何使用它。让我们创建 3 个变量— name, location, salary —看看我们如何使用一些格式化选项来更改默认输出,使其更好地对齐。当然,您可以选择将这些变量作为数据帧中的列来格式化输出,但是如果这不是一个选项或者不适合给定的情况,那么下面的字符串格式化选项是可用的。

:后的{}中的整数表示输出值必须占用的最小槽数。默认情况下,字符串变量的值左对齐,数值变量右对齐。

可以使用整数规格前的<>改变默认校准行为。数字格式——整数或浮点、浮点精度和逗号分隔符(用于千位)也可以在{}.内的整数后显示

结论

感谢您的阅读,希望您现在能够自如地处理 python 中的字符串。很乐意在 bala@python4u.in 上听到您的反馈,或者我们可以在 Linkedin 上联系。你可能也会对我关于 https://balaji4u.medium.com/的其他文章感兴趣

掌握 SQL 窗口函数

原文:https://towardsdatascience.com/mastering-sql-window-functions-6cd17004dfe0?source=collection_archive---------25-----------------------

丹尼尔·塞勒在 Unsplash 上拍摄的照片

三个清晰的例子让你开始运行

窗口函数是一个非常有用的 SQL 功能。如果你是新手,他们肯定会觉得有点棘手,但是读完这篇文章后,你会很快写出漂亮的查询。

窗口函数是做什么的?

在高层次上,窗口函数遵循三个关键步骤:

  1. 他们将数据分组
  2. 他们对每个组进行计算
  3. 他们将这些计算的结果组合回原始数据集

作者图片

三个例子来统治他们

演示窗口函数用途的最好方法是通过例子来完成。一旦你能激发对窗口函数的需求,编写它们就变得相当简单了。

下面,我们将使用一个包含世界上所有主要城市的每日气温的 Kaggle 数据集来完成 3 个示例。

这是数据的一个快速峰值:

作者图片

示例 1:

假设我们有下面的问题陈述:在整个数据集中找出每个城市最热的一天。

在进入这个问题之前,有必要考察几个类似的问题,这些问题我们不用窗口函数也能轻松回答。

如果我们的任务是找出每个城市最热的温度,那就很简单了:

select city, max(temperature) as warmest_temp
from temperature_data.city_temperature
group by 1;

或者如果我们想找出总的 n 个最热的日子:

select *
from temperature_data.city_temperature
order by temperature desc
limit 10;

让我们的原始问题与众不同的是,它要求我们为数据中的特定组单独找出一条信息**,但也要保留原始数据集中的信息。我们需要每个城市最热的一天,我们需要分别计算。**

如果你不清楚用例,我强烈建议在继续之前再读一遍上面的部分。

继续——让我们首先从概念上回答这个问题,使用上面概述的三个步骤:

  1. ****将数据分组:在这种情况下,我们希望按城市进行分组。
  2. ****执行聚合:一旦我们将数据分开,我们就可以按温度进行排序。
  3. 将数据重新组合在一起:当我们将数据重新组合在一起时,每个原始记录将会有一列显示它与该城市所有其他日子相比的排名顺序。

作者图片

现在我们有了一个概念性的理解,让我们把我们的查询放在 SQL 中:

select *, row_number() over (partition by city order by temperature desc) as temperature_rank
from temperature_data.city_temperature
limit 5;

下面是语法的分解:

  • row_number ”是我们应用于每个组的排名函数
  • over() ”是我们用来表示我们想要应用一个窗口函数的语法
  • **“**partition by”关键字是我们指定要按城市拆分的地方
  • ****“order by”帮助我们以某种方式对数据进行排序(在本例中是平均温度递减)

作者图片

下面是我们的输出结果:

作者图片

请注意,现在每个记录都有一个温度等级列。

最后,我们可以添加一些逻辑,只提取温度排名第一的一天:

select * from(select *, row_number() over (partition by city order by temperature desc) as temperature_rank
from temperature_data.city_temperature) subquerywhere temperature_rank = 1
limit 5;

最终,我们得到了我们一直在寻找的结果——每个城市最热的一天。

作者图片

示例 2:

****问题陈述:对于每一天,求该城市当天的温度与当月平均温度之差。

例如,该查询可以帮助我们获得如下见解:

“2020 年 1 2 月 1 日,奥尔巴尼的气温比奥尔巴尼 12 月份的正常气温高 32 度”

让我们再次完成这三个步骤中的每一步:

  1. ****将数据分组:在这种情况下,我们需要按照城市和月份进行分组
  2. ****执行聚合:一旦我们将数据分开,我们就可以找到平均温度
  3. 将数据重新组合在一起:当我们将数据重新组合在一起时,每个原始记录都会有一列显示该城市和月份的平均温度。从那里,很容易得到当天的温度和平均温度之间的差异。

作者图片

我们的查询和输出可能是这样的:

select *, avg(temperature) over (partition by city, month) as average_monthly_temperature
from temperature_data.city_temperature 
limit 5;

作者图片

最后,我们可以添加一个子查询并创建一个“与平均值的差异”列:

select *, temperature - average_monthly_temperature as diff_from_avg
from (select *, avg(temperature) over (partition by city, month) as average_monthly_temperature
from temperature_data.city_temperature) subquery

作者图片

有趣的是,在我们的第一个例子中,我们使用了一个名为“row_number”的排名函数,它为我们的城市组中的每一行返回一个值。

在我们的第二个例子中,我们使用了一个聚合函数 avg,它只为每个组产生一个值。

SQL 支持窗口函数中的两种类型的计算。如果函数的结果是单个值,则为原始数据集中组中的每条记录返回该值。

作者图片

示例 3:

在我们的最后一个例子中,我们将为窗口函数引入一种新的语法——它允许我们不仅基于像“city”这样的列,而且基于数据中的位置来生成组。

****问题陈述:对于每条记录,找出该城市过去 5 天的平均温度。

为了说明这一点,让我们展示一下在奥尔巴尼的第六天我们可能会看到什么:

作者图片

为了回答这类问题,我们需要引入一个叫做“框架子句”的概念

frame 子句允许我们指定包含在窗口计算中的相对行。

我们可以使用以下语法来实现这一点,其中 boundary1 和 boundary2 是我们希望包含在计算中的位置:

rows between boundary1 and boundary2

我们可以用以下任何子句替换 boundary1 和 boundary2,以便指定我们想要的确切框架:

作者图片

在上面的例子中,我们希望只根据前面的 5 行来计算窗口。为了做到这一点,我们可以写:

rows between 6 preceding and 1 preceding

下面是最终的查询和输出:

select *, avg(temperature) over (partition by city order by year, month, day rows between 6 preceding and 1 preceding) as temp_rolling_5
from temperature_data.city_temperature
limit 10;

作者图片

以下是一些需要注意的事项:

  1. 我们现在得到了第 6 行的预期值 27.4。您可以参考本例开头的图表来确认这就是我们所期望的。
  2. 第 1-5 行是使用“尽可能多的数据”计算的从第 5 行的角度来看,我们想要返回 6 条记录,但是那里没有数据,所以 SQL 使用第一行作为范围的开始。
  3. 滚动 5 的计算是由城市完成的,因为我们在“partition by”子句中包含了城市。这是一个强有力的例子,因为它表明我们可以在一个非常可定制的数据区域上执行窗口函数计算。****

常见错误

下面是一些可能导致调试困难的常见错误:

  1. 错别字:确保你拼写正确!像“partition”这样的单词很容易拼错
  2. 不需要的逗号:在“partition by”和“order by”之间或者“order by”和“rows between”之间放一个逗号可能比较合理,但是为了运行查询,必须将它们排除在外。
  3. 将它们放在 where 子句中:不幸的是,在 where 子句中不能使用窗口函数。但是很容易进行子查询或使用 cte 将逻辑放在计算列的顶部,就像我们在上面的示例 1–2 中所做的那样。

我想我明白了,但是我怎样才能得到更多的练习呢?

获得更多实践的一个简单方法是自己完成上面的三个例子。为此,我建议:

  1. 点击从 Kaggle 下载免费数据集
  2. 上传到 SQL 数据库。如果您还没有设置,您可以在下面找到我的指南,了解如何快速有效地设置:

**</4-steps-to-start-practicing-sql-at-home-cb771beeca20>

3.试着自己回答上面的问题,确保你的结果与我的相符,有意义(注意:我在分析中去掉了所有-99 的温度值)

4.自己写问题给自己回答!没有什么比提出你自己的研究问题更好的了,因为它能帮助你内化“什么时候”和“为什么”你会使用这些功能。

如果您认为本文有帮助,请:

  1. 对您希望我接下来撰写的 SQL 或数据科学主题发表评论,然后
  2. 订阅我的 免费邮箱列表

你也可以在下面找到我的熊猫视窗功能指南:

**

掌握 SQL 中的 OVER 子句

原文:https://towardsdatascience.com/mastering-the-over-clause-in-sql-ff783fe91914?source=collection_archive---------10-----------------------

对数据行的迭代

茱莉亚·萨比尼亚兹在 Unsplash 上的照片

随着对它们需求的增长,更多的数据库集成了窗口函数来对数据子集进行计算。使用与滑动窗口技术相同的原理,窗口函数计算单个数据点与数据集的其余部分或其中的一部分相比如何。

在统计学中,移动窗口最著名的应用之一是移动平均,也称为滚动平均。在财务和会计中,一个运行总数也是窗口函数的一个非常常见的例子。

随着使用窗口功能的需求增长,许多数据库开始在子句上集成,这允许用户执行这些操作以及其他操作。现在,OVER 子句已被微软程序、开源数据库(如 MySQL 和 Google 的 BigQuery)所采用,并已成为数据专业人员的必备工具。

对运行总计使用 OVER 子句

财务和会计专业人员在审阅财务信息时喜欢使用累计。在许多情况下,他们可能会使用 Excel 的内置函数来创建累计,但使用 SQL 通常可以节省一些时间,尤其是对于较大的数据集。

为了演示如何使用 SQL 创建一个运行总数,我们将使用一个示例数据库,其中包括一个销售 ID、周数、部门和应计收入。

CREATE TABLE SALES_2021(
sales_id INTEGER PRIMARY KEY AUTOINCREMENT,
week INTEGER,
department VARCHAR(255),
revenue INTEGER);

表格的前五个条目

随着时间的推移,利益相关者可能会自然地要求获得总收入。

SELECT week, department, revenue, SUM(revenue) 
OVER(PARTITION BY department ORDER BY week)
FROM SALES_2021;

SELECT 子句选择表示为正常的列以及收入的 SUM 函数。OVER 子句表示按部门对结果进行划分,这确保了对每个部门的累计重新开始,而不是对所有部门的总和进行累计。over 子句还指定按周对结果进行排序,以便它们保持时间顺序。

为了便于阅读,添加了一个别名,将列重命名为“运行总计”。

法律部前五周的总收入

对移动平均线使用 OVER 子句

数据分析师可能希望查看移动平均线,以粗略预测未来收入。与普通平均值不同的是,移动平均值只考虑子集而不是整个数据集,它通常用于时间序列数据,通常使用一个间隔,如最近几天、几周、几个月、几个季度或几年。根据定义,这提供了一个窗口函数的强大例子。

为了说明 SQL 如何自动生成这个统计数据,可以编写一个最近三周的查询平均值。

SELECT week, department, revenue, AVG(revenue) 
OVER(ORDER BY week ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) 
AS 'moving average' 
FROM SALES_2021 
WHERE department = 'Property';

第一行的内容与前面的例子几乎相同,只是现在采用的是收入的平均值而不是总和。OVER 子句再次使用 ORDER BY 语句来按时间顺序保存周。它还详细描述了哪些行要进行移动平均。由于本例说明了过去 3 周的情况,因此取当前行和前面 2 行的平均值。

再次添加一个别名,将该列标记为“移动平均值”,以提高可读性。

请注意,添加了一个额外的 WHERE 子句,以确保只使用来自财产部门的收入。如果省略,移动平均数将包括所有部门的数字。为了将它们分开,也可以在 OVER 子句中使用 PARTITION BY 子句,与前面的示例非常相似。

物业部首五个星期的流动平均数

使用 OVER 子句进行排序

最后,让我们假设一个利益相关者要求对每周的收入进行排名。他们可能想回顾自己表现最好和最差的几周,以确定是什么影响了他们的表现。

SELECT week, department, revenue, RANK() 
OVER(ORDER BY revenue DESC) AS 'rank' 
FROM SALES_2021 
WHERE department = 'Management';

SELECT 子句包含不带任何参数的 RANK 函数。OVER 子句 simple 包含一个 ORDER BY 函数,指示将根据收入对行进行排序。请注意 WHERE 子句,该子句仅指示来自管理部门的结果。如果没有它,OVER 子句中将需要一个额外的 PARTITION BY 语句。

表现最好的前五周

请注意,即使两个值相等,RANK 函数也会分配一个序列号。例如,如果第 20 周和第 30 周都筹集了 10,000 美元的收入,则第 20 周为第一,第 30 周为第二。

为了允许值之间的联系,DENSE_RANK 函数可以在查询中同样使用:

SELECT week, department, revenue, DENSE_RANK() 
OVER(ORDER BY revenue DESC) AS 'rank' 
FROM SALES_2021 
WHERE department = 'Management';

在这种情况下,第 20 周和第 30 周都将获得排名为$10,000 的第一名;然而,该排名将跳过第二名,授予下一个最高周第三名,类似于奥运会在比赛中处理平局的方式。

结论

OVER 子句提供了一种对数据实现窗口函数的便捷方式。在普通函数中使用,如运行总计和移动平均,它也可以用于迭代任意数量的数据转换的数据子部分。

掌握 sci kit-学习库

原文:https://towardsdatascience.com/mastering-the-scikit-learn-library-843b5641e9cc?source=collection_archive---------28-----------------------

使用 Python 机器学习库

照片由爆发在像素上拍摄

Scikit-learn 是一个强大的机器学习库,它提供了各种各样的数据访问、数据准备和统计模型构建模块。它有一个很好的干净的玩具数据集选择,非常适合刚刚开始数据分析和机器学习的人。轻松访问这些数据集消除了从外部数据源搜索和下载文件的麻烦。该图书馆还支持数据处理任务,如插补、数据标准化和数据标准化。这些任务通常可以显著提高模型性能。

Scikit-learn 还提供了各种用于构建线性模型、基于树的模型、聚类模型等的包。它为每种模型对象类型提供了一个易于使用的接口,这有助于快速原型化和模型实验。机器学习的初学者也会发现这个库很有用,因为每个模型对象都配备了提供基线性能的默认参数。总的来说,Scikit-learn 提供了许多易于使用的模块和方法,用于访问和处理数据以及在 Python 中构建机器学习模型。本教程将介绍它的一些功能。

Scikit-learn 数据集

Scikit-learn 提供了各种各样的玩具数据集。玩具数据集是一个简单、干净、有时是虚构的数据集,可用于探索性数据分析和构建简单的预测模型。scikit-learn 中可用的玩具数据集可应用于监督学习任务,如回归和分类任务。例如,它有一个名为鸢尾数据的集合,其中包含与不同类型的鸢尾植物相对应的信息。用户可以利用这些数据建立、训练和测试分类模型,这些模型可以根据鸢尾属植物的特征对它们进行分类。

Scikit-learn 还有一个波士顿住房数据集,其中包含波士顿的房价信息。这些数据对于回归任务非常有用,比如预测房子的美元价值。最后,手写数字数据集是一个非常适合建立图像分类模型的图像数据集。使用几行简单的 Python 代码就可以轻松加载所有这些数据集。

首先,让我们看一下如何加载虹膜数据。我们首先需要导入熊猫和 numpy 包:

import pandas as pd
import numpy as np

接下来,我们放宽列和行的显示限制:

pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', None)

然后,我们从 scikit-learn 加载虹膜数据,并将其存储在 pandas 数据框中:

from sklearn.datasets import load_iris
data = load_iris()
df = pd.DataFrame(data.data,columns=data.feature_names)
df[‘target’] = pd.Series(data.target)

最后,我们使用 head()方法打印前五行数据:

print(df.head())

我们可以对波士顿住房数据集重复这个过程。为此,让我们将现有代码封装在一个函数中,该函数将 scikit-learn 数据集作为输入:

def get_data(dataset):
    data = dataset
    df = pd.DataFrame(data.data,columns=data.feature_names)
    df['target'] = pd.Series(data.target)
    print(df.head())

我们可以用虹膜数据调用这个函数,得到和以前一样的输出:

get_data(load_iris())

现在我们看到我们的函数工作了,让我们导入波士顿住房数据并用数据调用我们的函数:

from sklearn.datasets import load_iris, load_boston
get_data(load_boston())

最后,让我们加载手写数字数据集,它包含从 0 到 9 的手写数字的图像。由于这是一个图像数据集,因此将其存储在数据框中没有必要也没有用。相反,我们可以使用可视化库 matplotlib 显示数据中的前五位数字:

from sklearn.datasets import load_iris, load_boston, load_digitsimport matplotlib.pyplot as pltdef get_data(dataset):
    try:
        data = dataset
        df = pd.DataFrame(data.data,columns=data.feature_names)
        df['target'] = pd.Series(data.target)
        print(df.head())
    except(AttributeError):
        data = dataset
        plt.gray()
    for i in range(0,5):
        plt.matshow(data.images[i])
        plt.show()

如果我们用 load_digits()调用我们的函数,我们会得到下面显示的图像:

该领域的初学者访问这些玩具数据集的容易程度不能被夸大。这些数据使初学者能够快速接触不同类型的数据和用例,如回归、分类和图像识别。

数据插补

Scikit-learn 还为数据处理任务提供了多种方法。首先,让我们看看数据插补,这是一个替换缺失数据的过程,非常重要,因为真实数据经常包含不准确或缺失的元素。这可能导致误导的结果和较差的模型性能。

能够准确估算缺失值是数据科学家和行业领域专家都应该具备的技能。为了演示如何使用 scikit-learn 执行数据插补,我们将使用加州大学欧文分校关于住房电力消耗的数据集,可在此处获得。由于数据集非常大,为简单起见,我们将随机抽取 40,000 条记录,并将向下采样的数据存储在一个名为“hpc.csv”的单独 csv 文件中:

df = pd.read_csv('household_power_consumption.txt', sep=';')
df = df.sample(40000)
df.to_csv('hpc.csv')

接下来,让我们读入新创建的数据集并打印前五行:

df = pd.read_csv('hpc.csv')
print(df.head())

正如我们所看到的,第三行(第二个索引)包含由?还有南。我们能做的第一件事是替换?具有 NaN 值的值。让我们用 Global_active_power 来证明这一点:

df['Global_active_power'].replace('?', np.nan, inplace = True)
print(df.head())

我们可以对其余的列重复这一过程:

df['Global_reactive_power'].replace('?', np.nan, inplace = True)
df['Voltage'].replace('?', np.nan, inplace = True)
df['Global_intensity'].replace('?', np.nan, inplace = True)
df['Sub_metering_1'].replace('?', np.nan, inplace = True)
df['Sub_metering_2'].replace('?', np.nan, inplace = True)

现在,为了估算丢失的值,我们从 scikit-learn 导入 SimpleImputer 方法。我们将定义一个简单估算缺失值平均值的估算对象:

from sklearn.impute import SimpleImputer
imp_mean = SimpleImputer(missing_values=np.nan, strategy='mean')

我们可以用缺失值来拟合我们的估算值:

X = df[['Global_active_power', 'Global_reactive_power', 'Voltage', 'Global_intensity', 'Sub_metering_1', 'Sub_metering_2' ]]
imp_mean.fit(X)

将结果存储在数据框中:

df_new = pd.DataFrame(imp_mean.transform(X), columns = ['Global_active_power', 'Global_reactive_power', 'Voltage', 'Global_intensity', 'Sub_metering_1', 'Sub_metering_2' ])

添加额外的日期和时间列:

df_new['Date'] = df['Date']
df_new['Time'] = df['Time']

并打印新数据框的前五行:

print(df_new.head())

正如我们所看到的,丢失的值已经被替换。虽然 scikit-learn 的 SimpleImputer 不是最复杂的估算方法,但它消除了构建自定义估算器的许多麻烦。这种简单性对于第一次处理缺失数据的初学者很有用。此外,它还很好地展示了插补的工作原理。通过引入这一过程,它可以促进这类插补的更复杂的扩展,例如使用统计模型来替换缺失值。

数据标准化&规范化

使用 scikit-learn,数据标准化和规范化也很容易。这两者在机器学习方法中都很有用,包括计算距离度量,如K-最近邻和支持向量机。在我们可以假设数据呈正态分布的情况下,以及在线性模型中解释系数具有可变重要性的情况下,它们也很有用。

标准化

标准化是用平均值减去数字列中的值并换算成单位方差(通过除以标准偏差)的过程。在大范围的数值可能人为地支配预测结果的情况下,标准化是必要的。

让我们考虑标准化功耗数据集*中的 Global_intensity。*该列的值范围从 0.2 到 36。首先,让我们从 scikit-learn 导入 StandardScalar()方法:

scaler = StandardScaler()
scaler.fit(np.array(df_new[['Global_intensity']]))
df_new['Global_intensity'] = scaler.transform(np.array(df_new[['Global_intensity']]))

现在我们看到最小值和最大值分别为 7.6 和-1.0:

print(df_new.head())
print("Max: ", df_new['Global_intensity'].max())
print("Min: ", df_new['Global_intensity'].min())

正常化

数据规范化对数字列进行缩放,使其值介于 0 和 1 之间。使用 scikit-learn 标准化数据遵循与标准化相似的逻辑。让我们将规格化方法应用于 Sub_metering_2 列:

from sklearn.preprocessing import Normalizer
normalizer = Normalizer()
normalizer.fit(np.array(df_new[['Sub_metering_2']]))
df_new['Sub_metering_2'] = normalizer.transform(np.array(df_new[['Sub_metering_2']]))
print(df_new.head())
print("Max: ", df_new['Sub_metering_2'].max())
print("Min: ", df_new['Sub_metering_2'].min())

现在我们看到最小值和最大值分别为 1.0 和 0。一般来说,如果您可以安全地假设数据是正态分布的,那么您应该将数据标准化。相反,如果您可以安全地假设您的数据不是正态分布的,那么规范化是一个缩放数据的好方法。考虑到这些转换只需几行代码就可以应用于数字数据,StandardScaler()和 Normalizer()方法对于处理值变化很大的数据字段或非正态分布数据的初学者来说是很好的选择。

使用 Scikit-Learn 进行统计建模

Scikit-learn 还拥有构建各种统计模型的方法,包括线性回归、逻辑回归和随机森林。线性回归用于回归任务。具体来说,它适用于预测连续输出,例如房价。逻辑回归用于分类任务,其中模型预测二进制输出或多类,如基于特征预测鸢尾植物类型。随机森林可用于回归和分类。我们将介绍如何使用 Python 中的 scikit-learn 机器学习库来实现这些模型。

线性回归

线性回归是一种统计建模方法,其中线性函数表示输入变量和标量响应变量之间的关系。为了演示它在 Python 中的实现,让我们考虑波士顿住房数据集。我们可以建立一个线性回归模型,使用年龄作为预测住房价值的输入。首先,让我们定义输入和输出变量:

X = df_housing[['AGE']]
y = df_housing[['target']]

接下来,让我们将数据分为训练和测试两部分:

from sklearn.model_selection import train_test_split
X_train, y_train, X_test, y_test = train_test_split(X, y, random_state = 42)

现在让我们从 scikit-learn 导入线性回归模块:

from sklearn.linear_models import LinearRegression

最后,让我们使用 R 和 RMSE 来训练、测试和评估我们的模型的性能:

linear_model = LinearRegression()
linear_model.fit(X_train, y_train)
y_pred = linear_model.predict(X_test)from sklearn.metrics import mean_squared_error
from sklearn.metrics import r2_scorerms = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print("MSE:”, rms)
print("R^2:", r2)

因为我们使用一个变量来预测反应,这是一个简单的线性回归。但是我们也可以在多元线性回归中使用多个变量。让我们用年龄(age)、平均房间数(RM)、生师比(PTRATION)建立一个线性回归模型。我们需要做的就是重新定义 X(输入)如下:

X = df_housing[['AGE', 'PTRATIO', 'RM']]

这带来了以下性能改进:

如果您确信输入和输出之间存在线性关系,线性回归是一种很好的方法。相对于随机森林和支持向量机等更复杂的方法,它也是一个有用的基准。

逻辑回归

逻辑回归是一种简单的分类模型,可以预测二元甚至多类输出。训练和测试的逻辑类似于线性回归。

让我们考虑一下逻辑回归模型的 Python 实现的 iris 数据。我们将使用萼片长度(cm)、萼片宽度(cm)、花瓣长度(cm)和花瓣宽度(cm)来预测鸢尾属植物的类型:

df_iris= get_data(load_iris())X = df_iris[['sepal length (cm)', 'sepal width (cm) ', 'petal length (cm)', 'petal width (cm)']]
y = df_iris[['target']]X_train, X_test, y_train, y_test = train_test_split(X, y, random_state = 42)logistic_model = LogisticRegression()
logistic_model.fit(X_train, y_train)
y_pred = linear_model.predict(X_test)

我们可以使用混淆矩阵来评估和可视化模型性能:

我们看到,该模型正确地捕捉了三个鸢尾植物类别的所有真阳性。与线性回归类似,逻辑回归依赖于用于预测每个类别的输入的线性和。因此,逻辑回归模型被称为广义线性模型。考虑到逻辑回归对输入和输出之间的线性关系进行建模,当您知道输入和类成员之间存在线性关系时,最好使用逻辑回归。

随机森林

随机森林,也称为随机决策树,是用于分类和回归任务的统计模型。随机森林基本上是一组关于以树状结构组织的数据的问题和答案。

这些问题将数据分成子组,以便每个连续子组中的数据彼此最相似。例如,假设我们想要预测借款人是否会拖欠贷款。我们可以使用历史贷款数据提出的一个问题是,客户的信用评分是否低于 700。与落入“否”桶的数据相比,落入“是”桶的数据将有更多的客户违约。

在 yes 桶中,我们可以进一步询问借款人的收入是否低于 30,000 美元。据推测,这里的“是”桶将有更大比例的客户违约。决策树继续询问关于数据的统计问题,直到在对应于违约者和未违约者的数据之间实现最大分离。

随机森林通过构建大量决策树来扩展决策树。在每一棵树上,我们都会问一些关于随机数据块和不同数据特征的统计问题。例如,一棵树可能会询问一部分训练数据的年龄和信用评分。另一个人可能会在培训数据的单独部分询问收入和性别,等等。随机森林然后在这些决策树中执行一致投票,并使用多数投票进行最终预测。

为回归和分类实现随机森林模型非常简单,与我们为线性回归和逻辑回归所经历的步骤非常相似。让我们考虑使用波士顿住房数据预测房价的回归任务。我们需要做的就是导入随机森林回归器模块,启动回归器对象,拟合、测试和评估我们的模型:

from sklearn.ensemble import RandomForestRegressorrf_reg = RandomForestRegressor()
rf_reg.fit(X_train, y_train)
y_pred = rf_reg.predict(X_test)from sklearn.metrics import mean_squared_error
from sklearn.metrics import r2_scorerms = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)print("RF MSE: ", rms)
print("RF R^2: ", r2)

与线性回归相比,我们看到性能略有提高。

随机森林对象接受几个参数,可以修改这些参数以提高性能。这里我要指出的三个是 n 估计量、最大深度和随机状态。您可以查看文档以获得所有随机森林参数的完整描述。

参数 n_estimators 就是组成随机森林的决策树的数量。Max_depth 测量从第一个问题到树的底部的一个问题的最长路径。Random_state 是算法随机选择数据块进行提问的方式。

由于我们没有为这些参数指定任何值,随机森林模块会自动为每个参数选择一个默认值。n_estimators 的默认值是 10,相当于 10 棵决策树。max_depth 的默认值是 None,这意味着从第一个问题到决策树底部最后一个问题的路径长度没有截止。这可以大致理解为我们对数据提问次数的限制。random_state 的默认值是 None。这意味着,在每次模型运行时,将随机选择不同的数据块,并用于在随机森林中构建决策树。这将导致输出和性能的轻微变化。

尽管使用了默认值,我们还是获得了相当好的性能。这种准确性展示了随机森林的强大功能,以及数据科学初学者可以轻松实现准确的随机森林模型。

让我们看看如何指定 n_estimators,max_depth 和 random_state。我们将选择 100 个估计值,最大深度为 10,随机状态为 42:

rf_reg = RandomForestRegressor(n_estimators= 100, max_depth=10, random_state =42)

我们看到,我们在 MSE 和 R 上都有轻微的改进。此外,指定 random_state 使我们的结果可重复,因为它确保了使用相同的随机数据块来构建决策树。

将随机森林模型应用于分类任务非常简单。让我们为虹膜分类任务这样做:

rf_cass = RandomForestClassifier(n_estimators= 100, max_depth=10, random_state =42)
rf_cass.fit(X_train, y_train)
y_pred = rf_cass.predict(X_test)

相应的混淆矩阵也同样准确:

随机森林是构建统计模型的绝佳选择,因为它们可应用于广泛的预测用例。这包括分类、回归甚至无监督聚类任务。这是一个非常棒的工具,每个数据科学家都应该随身携带。在 scikit-learn 的上下文中,它们非常容易实现和修改以提高性能。这使得模型的快速原型和实验能够更快地得到准确的结果。

最后,这篇文章中的所有代码都可以在 GitHub 上找到。

结论

总的来说,scikit-learn 提供了许多易于使用的工具,用于访问基准数据、执行数据处理以及培训、测试和评估机器学习模型。所有这些任务都需要相对较少的代码行,这使得数据科学和机器学习研究的初学者的入门门槛相当低。用户可以快速访问玩具数据集,并熟悉不同的机器学习用例(分类、回归、聚类),而无需寻找数据源、下载然后清理数据的麻烦。在熟悉不同的用例后,用户可以轻松地将他们所学的移植到更真实的应用程序中。

此外,不熟悉数据插补的新数据科学家可以快速学会如何使用 scikit 中的 SimpleImputer 包——学习并实施一些标准方法来替换数据中的缺失值或错误值。这可以作为学习更先进的数据插补方法的基础,例如使用统计模型预测缺失值。此外,标准的定标器和规格化器方法使得神经网络和支持向量机等高级模型的数据准备非常简单。为了在支持向量机和神经网络等更复杂的模型中获得令人满意的性能,这通常是必要的。

最后,scikit-learn 使得构建各种各样的机器学习模型变得非常容易。虽然我在这篇文章中只讨论了三个,但是构建其他广泛使用的模型(如支持向量机和 k 近邻)的逻辑非常相似。它也非常适合对这些算法如何工作了解有限的初学者,因为每个模型对象都有默认的参数来提供基线性能。无论任务是用玩具数据进行模型基准标记、准备/清理数据还是评估模型性能,scikit-learn 都是为各种用例构建机器学习模型的绝佳工具。如果你有兴趣学习 python 编程的基础知识、Pandas 的数据操作以及 python 中的机器学习,请查看Python for Data Science and Machine Learning:Python 编程、Pandas 和 sci kit-初学者学习教程 。我希望你觉得这篇文章有用/有趣。

本帖原载于 内置博客 。原片可以在这里找到https://builtin.com/machine-learning/scikit-learn-guide

用可变自动编码器控制移位

原文:https://towardsdatascience.com/mastering-the-shifts-with-variational-autoencoders-ca609ec84f1?source=collection_archive---------31-----------------------

思想和理论

变分自动编码器如何用于分析一维信号

马克西姆·兹亚迪诺夫&谢尔盖·加里宁

美国田纳西州橡树岭橡树岭国家实验室纳米材料科学和计算科学与工程中心

数据通常以一维信号的形式出现。这些可以是来自传感器或检测器的时间序列记录,这些传感器或检测器测量气象学中的温度、湿度和风速,个人健身追踪器中的心率,或者是由于物联网(IoT)设备和轻边缘计算(如 Arduino、Raspberry Pi 和 NVIDIA Jetson)的激增而变得普遍的更复杂的检测系统。即使没有任何专门的电子设备,最近的应用程序,如 Arduino Science Journal,也可以访问手机上的压力、磁场和加速度传感器,允许进行大量的家庭实验(“如果我走上楼梯,压力会改变多少?”、“我跑步或者走路时的加速度模式是怎样的?”).

在许多此类应用中,信号具有一些特征方面,无论是心跳节律、步行门、包含健身、健康信息的振动模式,还是我们想要发现的其他特征。如果信号的时间参考点是众所周知的,或者数据是周期性的,那么这种数据的分析是简单明了的。然而,事件和典型特征的精确定位通常是不可获得的,并且可以是我们旨在与它们的典型模式一起发现的东西。

类似的问题在科学领域比比皆是。一个简单的例子可以是 X 射线散射数据,其中对应于特定指数的峰可以随着固溶体中材料的摩尔体积而移动。另一个例子可以是质谱数据,其中峰值可以由于数据采集期间的表面充电而移动,拉曼光谱,其中峰值移动指示材料中的应变,等等。在许多情况下,这种数据可以在大的参数空间中获得,例如超光谱成像中的位置、组合库评估中的浓度或有源器件表征中的电压。问题是——机器学习能否帮助我们以无监督的方式处理和理解这些类型的数据?

对于合著者之一(S.V.K .),对该领域的兴趣始于扫描探针显微镜( SPM )中能带激发(BE)方法的引入。各种现有的 SPM 方法通常基于尖锐(有时是原子级尖锐)的探针和表面之间的相互作用。探针位于悬臂的末端,激光束从背面反射回来。探针的小位移导致检测器上激光束的大位移,从而允许检测微小的位移。实际上,小到几分之一纳米的位移都可以检测到。就相关力而言,对于数量级为~1 N/m 的弹簧常数,相应的力将是原子间力的数量级。因此得名原子力显微镜。

90 年代末,动态 SPM 方法被广泛引入[1,2]。在这些方法中,悬臂由施加到驱动悬臂的压电元件(间歇接触形貌成像、磁力显微镜)或直接施加到悬臂(开尔文探针力显微镜和静电力显微镜)的振荡信号激励。显微镜电子设备检测单一频率信号的振幅和相位(锁定检测,LIA),或者通过跟踪由于针尖-表面相互作用引起的共振频率的变化(锁相环,PLL)。然而,LIA 和 PLL 都只检测单一频率的信号,使得系统在物理上欠定(即简谐振子有三个参数——幅度、谐振频率和品质因数)。恒定驱动力的假设在许多 SPM 方法中仅是近似正确的,导致虚假的信号变化和定量信息的缺乏,并且不适用于许多其他方法。

已经开发了带激发(BE)扫描探针显微镜来解决这个问题[3,4]。在这种方法中,原子力显微镜并行检测选定频带内的振幅/相位-频率共振曲线段(因此得名)。2D SPM 图像因此变成 3D,其中每个空间像素现在包含完整的频率相关响应曲线。然后,通过简单谐振子方程的拟合允许重构振幅、共振频率和品质因数图,从这些图中可以提取材料特性。然而,峰值形状可以(并且经常)不同于 SHO 形状。该峰形信息在分析中被忽略。作为一种可能的策略,我们可以利用反应的函数形式等形式的先验物理知识。,在最小平方拟合或贝叶斯推断[5]。然而,这种策略通常是耗时的,并且在许多情况下,分析解决方案是不可用的。

无监督机器学习来拯救!主成分分析(PCA)在 2007–2008 年的 BE 数据中的应用[6]开启了我们小组对 ML 方法的探索,这一探索仍在继续。然而,具有讽刺意味的是,PCA 对带激发数据的第一次应用表明,只有在图像内的峰值偏移很小并且噪声水平相对较高的少数情况下,才能获得有意义的结果。那么少量的 PCA 分量就足以代表数据,甚至可以用特定的物理机制来识别它们。对于共振频率变化较大或噪声水平较低的数据集,主成分分析产生大量成分[7],其物理意义(甚至对探索性数据分析的有用性)尚不清楚。同样的命运降临到其他线性分解方法,如非负矩阵分解,贝叶斯线性分解[8]和其他[9]。

然而,现在我们可以清楚地表达我们对这种数据集的“理想”无监督 ML 方法的期望。它应该能够找到描述符或解开 1D 数据集的表示,忽略(或分离)峰的相对位置。这正是移位变分自动编码器(移位 VAE)的设计目的。

在移位 VAE 中,我们指定一个潜在变量来捕获关于光谱相对位置的信息,而其余的潜在变量“寻找”其他(而不是位置)变化因素,例如高度、宽度、峰间距离(如果不止一个峰)等。我们姑且称这个特殊的潜变量为“偏移潜变量”。我们首先创建一个 1D x 坐标网格,其长度等于光谱中的点数。我们的编码器将输入(光谱)映射到偏移潜变量和两个(或更多)常规潜变量。假设位置的移动是正态分布的,偏移潜在向量是从单位高斯分布中采样的。采样值用于移动我们的坐标网格,然后与传统的潜在变量连接,并传递到 VAE 解码器,以加强移动光谱之间的几何一致性。

现在,让我们试验一下移位 VAE,并将其与主成分分析/NMF 和常规 VAE 进行比较。作为第一步,我们可以生成一个由高斯型(或具有相同中心的高斯型和洛伦兹型的线性组合)形成的合成数据集。

这里,高斯曲线的中心和宽度存储为地面真实数据,而曲线本身形成了特征集。

来自合成数据集的 64 条典型曲线。x 范围从(-12,12)开始变化,高斯中心均匀分布在(-3,3)上,宽度均匀分布在(0.5,5)上。(加性)噪声的幅度值在 0 和 0.1 之间均匀采样。图片由作者提供。

在附带的笔记本中实现的简单 PCA 分析(参见本文末尾的链接)确实说明了表示这种数据集所需的组件数量很大,并且欢迎读者将组件的行为作为参数空间和噪声水平中数据分布的函数。

现在让我们应用简单的 VAE。为此,我们将使用我们的 pyroVED 包,它构建在 Pyro 概率编程语言之上。使用 pyroVED(如 pv ),只需几行代码就可以训练 VAEs:

在这种情况下,VAE 通过两个潜在变量对数据集进行编码,经过训练的 VAE 模型的潜在空间中的数据点分布如下图所示。

数据点在常规 VAE 的潜在空间。图片由作者提供。

投影到光谱空间的传统 VAE 学习到的潜在流形。

成绩还不算太差!我们的数据点清楚地显示了类似于联合均匀分布的结构。为了验证这一猜测,我们可以使用地面真实标签对每个点进行着色,并看到对应于宽度的标签(主要)从图像的顶部到底部变化,而对应于中心位置的标签(主要)从左到右变化。我们也可以从潜在空间中的网格点重建曲线,并观察到实际上宽度从上到下变化,位置从左到右变化。因此,我们的变化自动编码器(大部分)解开了数据的表示,在这种情况下,变化的一个解开因素是宽度,另一个是位置。

然而,一旦我们将潜在变量与基本事实相对照,我们会发现,虽然基本事实变量和潜在变量之间存在明确的关系,但它们并不相等。此外,偏移以任意单位编码,这没有很大的实际用途。

VAE 的潜在变量与地面真相。图片由作者提供。

现在,让我们用移位 VAE 重复这个分析。这里,相对位移被分离为一个特殊的(偏移)潜在变量,其余的可变性被编码为两个常规潜在变量。要在 pyroVED 中做到这一点,我们只需将 coord=0 改为 coord=1 :

相应的潜在空间表示如下所示:

移位-VAE 潜在空间中的数据点。图片由作者提供。

注意,潜在空间是准塌陷的,数据点形成 1D 流形(尽管从偏移潜在变量到第一(“塌陷”)潜在变量有微小的“泄漏”)。在 VAEs 中,这种潜在空间的崩溃被认为是一个需要调整“损失”函数的问题。然而,这里我们知道我们的地面真实数据集只有两个可变性因素,其中之一是位置。因此,我们潜在空间的维度暗示了数据的真实物理维度!

将通过移位 VAE 学习的潜在流形投影到光谱空间。图片由作者提供。

注意,从系统的潜在空间重建的光谱都以零为中心,表明位移 VAE 能够分离位移和其他可变性因素。最后,从移位 VAE 得到的偏移在数值上非常接近地面真实值:

转变——VAE 的潜在变量与地面真相。请注意,这里我们没有绘制一个“折叠”的潜在变量。图片由作者提供。

这概括了转移-VAE 的介绍。请随意使用笔记本,并将其应用于您的数据集。请查看我们的 pyroVED 软件包,将该软件包和其他 VAEs 应用于科学图像和光谱数据(它处于 alpha 阶段,非常欢迎投稿/建议!).如果您有兴趣了解更多关于应用于电气和机电表征的不同 SPM 模式,欢迎您登录我们的 YouTube 频道 M*N:显微镜、机器学习、材料。

最后,在科学界,我们感谢资助这项研究的赞助商。这项工作在橡树岭国家实验室纳米材料科学中心(CNMS)进行并得到支持,该中心是美国能源部科学用户设施办公室。您可以使用此链接进行虚拟漫游,如果您想了解更多,请告诉我们。

可执行的 Google Colab 笔记本在这里有。

参考文献

1.巴特,H. J。无伴奏合唱;用原子力显微镜测量力:技术、解释和应用。冲浪。Sci。代表 **2005,**59(1–6),1–152。

2.加西亚河;动态原子力显微镜方法。冲浪。Sci。众议员 **2002,**47(6–8),197–301。

3.杰西;加里宁公司;普罗克什河;巴德多夫公司;Rodriguez,B. J,《扫描探针显微镜中的能带激发方法,用于纳米尺度能量耗散的快速绘图》。纳米技术 2007, 18 (43),435503。

4.杰西;扫描探针显微镜中的带激发:变化的正弦。*j . phys . D-appl . phys .*2011、 44 (46)、464006。

5.瓦苏德万;凯利,K. P。叶利舍耶夫,e;杰西;Funakubo,h;莫罗佐夫斯卡;用于成像中最佳动态模型选择的带状激发扫描探针显微术中的贝叶斯推断。 J. Appl. Phys. 2020, 128 (5),10。

6.杰西;扫描探针显微镜中光谱成像数据的主成分和空间相关性分析。纳米技术 2009, 20 (8),085714。

7.别利亚尼诺夫。加里宁公司;动态力显微镜中的完整信息获取。 NatCommun。 2015, 6

8.多比根,新泽西州;EELS 光谱图像的光谱混合分析。超显微 2012, 120 ,25–34。

9.坎南河;耶夫列夫公司;新泽西州拉纳伊特;文学硕士齐亚特迪诺夫;瓦苏德万;杰西;Kalinin,S. V,《通过物理约束线性分解进行深度数据分析:通用框架、领域示例和社区范围平台》。高级结构。化学。Imag。 2018、 4 、20。

掌握 Python 中的时间序列分析

原文:https://towardsdatascience.com/mastering-time-series-analysis-in-python-8219047a0351?source=collection_archive---------5-----------------------

Python 中的时间序列分析和预测工具

照片由乔丹本顿在像素上拍摄

在各行各业中,组织通常在运营中使用时间序列数据,这意味着在固定的时间间隔内收集的任何信息。示例包括每日股票价格、能源消耗率、社交媒体参与度指标和零售需求等。分析时间序列数据可以获得趋势、季节模式和对未来事件的预测等见解,从而有助于创造利润。例如,通过了解零售产品需求的季节性趋势,公司可以计划促销活动,以最大限度地提高全年销售额。

当分析时间序列数据时,您应该采取一些步骤。首先,您需要检查平稳性和自相关性。平稳性是一种衡量数据是否具有季节性趋势等结构模式的方法。当时间序列中的未来值线性依赖于过去值时,就会发生自相关。您需要在时间序列数据中检查这两种情况,因为它们是时间序列分析中许多广泛使用的方法做出的假设。例如,预测时间序列的自回归综合移动平均(ARIMA)方法假设平稳性。此外,时间序列预测的线性回归假设数据没有自相关。

在时间序列分析过程中,您还需要执行趋势分解和预测未来值。分解允许您可视化数据中的趋势,这是清楚地解释它们的行为的好方法。

最后,预测可以让你预测未来的事件,有助于决策。您可以使用许多不同的技术进行时间序列预测,但在这里,我们将讨论自回归综合移动平均(ARIMA)。

我们将使用公开的航空乘客时间序列数据,这些数据可以在这里找到。

读取和显示数据

首先,让我们导入 Pandas 库并将航班乘客数据读入数据框:

import pandas as pddf = pd.read_csv(“AirPassengers.csv”)

现在,让我们使用 data frame head()方法显示前五行数据:

print(df.head())

作者图片

我们可以看到数据包含一个标有“月”的列,其中包含日期。在该列中,日期格式为年-月。我们还看到数据开始于 1949 年。

第二列标记为“#Passengers”,它包含年-月的乘客数量。让我们来看看使用 tail()方法记录的最后五个数据:

print(df.tail())

作者图片

我们看到数据截止于 1960 年。接下来我们要做的是将 month 列转换成 datetime 对象。这将允许它以编程方式为每个记录提取时间值,如年或月。为此,我们使用 Pandas to_datetime()方法:

df[‘Month’] = pd.to_datetime(df[‘Month’], format=’%Y-%m’)print(df.head())

作者图片

请注意,这个过程会自动插入每个月的第一天,这基本上是一个虚拟值,因为我们没有每天的乘客数据。

接下来我们要做的是将 month 列转换成一个索引。这将使我们能够更容易地处理我们将在后面介绍的一些包:

df.index = df[‘Month’]del df[‘Month’]print(df.head())

作者图片

接下来,让我们使用 Seaborn 和 Matplotlib 生成一个时间序列图。这将允许我们可视化时间序列数据。

首先,让我们导入 Matplotlib 和 Seaborn:

import matplotlib.pyplot as pltimport seaborn as sns

接下来,让我们使用 Seaborn 生成一个线图:

sns.lineplot(df)

并用 Matplotlib 标记 y 轴:

plt.ylabel(“Number of Passengers”)

作者图片

分析

平稳性是时间序列分析的关键部分。简单地说,平稳性意味着时间序列数据变化的方式是恒定的。平稳的时间序列不会有任何趋势或季节模式。您应该检查平稳性,因为它不仅使时间序列建模更容易,而且是许多时间序列方法的基本假设。具体来说,平稳性被假设用于多种时间序列预测方法,包括自回归移动平均(ARMA)、ARIMA 和季节性 ARIMA (SARIMA)。

我们将使用迪基富勒测试来检查我们的数据的平稳性。该测试将生成临界值和 p 值,这将允许我们接受或拒绝不存在平稳性的零假设。如果我们拒绝零假设,我们接受另一种说法,即存在平稳性。

这些值允许我们测试当前值随过去值变化的程度。如果数据集中没有平稳性,当前值的变化不会引起过去值的显著变化。

让我们测试一下航空乘客数据的平稳性。首先,让我们计算一个七个月的滚动平均值:

rolling_mean = df.rolling(7).mean()rolling_std = df.rolling(7).std()

接下来,让我们用七个月的滚动平均值和七个月的滚动标准差来覆盖我们的时间序列。首先,让我们制作一个时间序列的 Matplotlib 图:

plt.plot(df, color=”blue”,label=”Original Passenger Data”)

然后滚动的意思是:

plt.plot(rolling_mean, color=”red”, label=”Rolling Mean Passenger Number”)

最后是滚动标准偏差:

plt.plot(rolling_std, color=”black”, label = “Rolling Standard Deviation in Passenger Number”)

让我们接着添加一个标题

plt.title(“Passenger Time Series, Rolling Mean, Standard Deviation”)

还有一个传说:

plt.legend(loc=”best”)

作者图片

接下来,让我们从 statsmodels 包中导入增强的 Dickey-Fuller 测试。测试文档可以在这里找到。

from statsmodels.tsa.stattools import adfuller

接下来,让我们将数据帧传递给 adfuller 方法。这里,我们将自动标记参数指定为“AIC”,这意味着选择滞后以最小化信息标准:

adft = adfuller(df,autolag=”AIC”)

接下来,让我们将结果存储在数据框中并显示出来:

output_df = pd.DataFrame({“Values”:[adft[0],adft[1],adft[2],adft[3], adft[4][‘1%’], adft[4][‘5%’], adft[4][‘10%’]] , “Metric”:[“Test Statistics”,”p-value”,”No. of lags used”,”Number of observations used”,“critical value (1%)”, “critical value (5%)”, “critical value (10%)”]})print(output_df)

作者图片

从我们的 p 值大于 5%并且检验统计量大于临界值这一事实中,我们可以看出我们的数据不是稳定的。我们也可以从检查数据中得出这些结论,因为我们看到了乘客数量明显增加的趋势。

自相关

检查时间序列数据中的自相关是分析过程的另一个重要部分。这是对给定时间点的时间序列数据与过去值的相关程度的衡量,这对许多行业都有巨大的影响。例如,如果我们的乘客数据具有很强的自相关性,我们可以假设今天的高乘客数量表明明天的乘客数量也很可能很高。

Pandas 数据框具有一种自相关方法,可用于计算乘客数据的自相关。让我们滞后一个月来做这件事:

autocorrelation_lag1 = df[‘#Passengers’].autocorr(lag=1)print(“One Month Lag: “, autocorrelation_lag1)

作者图片

现在,让我们试试三个月、六个月和九个月:

autocorrelation_lag3 = df[‘#Passengers’].autocorr(lag=3)print(“Three Month Lag: “, autocorrelation_lag3)autocorrelation_lag6 = df[‘#Passengers’].autocorr(lag=6)print(“Six Month Lag: “, autocorrelation_lag6)autocorrelation_lag9 = df[‘#Passengers’].autocorr(lag=9)print(“Nine Month Lag: “, autocorrelation_lag9)

作者图片

我们看到,即使有 9 个月的滞后,数据也高度自相关。这进一步说明了数据的短期和长期趋势。

分解

趋势分解是另一种可视化时间序列数据趋势的有用方法。要继续,让我们从 statsmodels 包中导入季节性分解:

from statsmodels.tsa.seasonal import seasonal_decompose

接下来,让我们将数据框传递给季节性分解方法,并绘制结果:

decompose = seasonal_decompose(df[‘#Passengers’],model=’additive’, period=7)decompose.plot()plt.show()

作者图片

从该图中,我们可以清楚地看到乘客数量的增长趋势以及每年价值上升和下降的季节性模式。

预测

时间序列预测允许我们在给定当前和过去数据的情况下预测时间序列中的未来值。在这里,我们将使用 ARIMA 方法来预测乘客数量。ARIMA 允许我们根据过去值的线性组合来预测未来值。我们将使用 auto_arima 包,它将允许我们放弃耗时的超参数调整过程。

首先,让我们分割数据用于训练和测试,并可视化分割:

df[‘Date’] = df.indextrain = df[df[‘Date’] < pd.to_datetime(“1960–08”, format=’%Y-%m’)]train[‘train’] = train[‘#Passengers’]del train[‘Date’]del train[‘#Passengers’]test = df[df[‘Date’] >= pd.to_datetime(“1960–08”, format=’%Y-%m’)]del test[‘Date’]test[‘test’] = test[‘#Passengers’]del test[‘#Passengers’]plt.plot(train, color = “black”)plt.plot(test, color = “red”)plt.title(“Train/Test split for Passenger Data”)plt.ylabel(“Passenger Number”)plt.xlabel(‘Year-Month’)sns.set()plt.show()

作者图片

黑线对应我们的训练数据,红线对应我们的测试数据。

让我们从 pdmarima 包中导入 auto_arima,训练我们的模型并生成预测:

from pmdarima.arima import auto_arimamodel = auto_arima(train, trace=True, error_action=’ignore’, suppress_warnings=True)model.fit(train)forecast = model.predict(n_periods=len(test))forecast = pd.DataFrame(forecast,index = test.index,columns=[‘Prediction’])

以下是输出的截断示例:

作者图片

作者图片

现在,让我们显示模型的输出:

作者图片

我们的预测显示为绿色,实际值显示为橙色。

最后,让我们计算均方根误差(RMSE):

from math import sqrtfrom sklearn.metrics import mean_squared_errorrms = sqrt(mean_squared_error(test,forecast))print(“RMSE: “, rms)

作者图片

结论

时间序列数据分析是几乎每个数据科学家在职业生涯中都会面临的任务。对分析工具和方法有很好的理解,可以使数据科学家发现趋势、预测事件,从而为决策提供信息。通过平稳性、自相关和趋势分解了解季节性模式可以指导全年的促销计划,从而提高公司的利润。最后,时间序列预测是在时间序列数据中预测未来事件的一种强有力的方法,这也可以显著影响决策。这些类型的分析对于任何希望通过时间序列数据为公司带来价值的数据科学家或数据科学团队来说都是无价的。这篇文章的代码可以在 GitHub 上找到。

如果你有兴趣学习 python 编程的基础知识、Pandas 的数据操作以及 python 中的机器学习,请查看Python for Data Science and Machine Learning:Python 编程、Pandas 和 sci kit-初学者学习教程 。我希望你觉得这篇文章有用/有趣。

本帖原载于 内置博客 。原片可以在这里找到https://builtin.com/data-science/time-series-python

使用 Streamlit 掌握 Web 应用程序

原文:https://towardsdatascience.com/mastering-web-applications-with-streamlit-95f84420fe13?source=collection_archive---------41-----------------------

使用 Streamlit 构建基于 ML 的 Web 应用程序

照片由底片上的空间像素拍摄

公司对向客户清楚地传达他们基于 ML 的预测分析非常感兴趣。无论模型有多精确,客户都想知道机器学习模型是如何从数据中做出预测的。例如,如果一个基于订阅的公司有兴趣寻找那些取消订阅的高风险客户,他们可以使用他们的历史客户数据来预测某人离开的可能性。

从那里,他们会想要分析驱动这个事件的因素。通过了解驱动因素,他们可以采取有针对性的促销或折扣等措施来防止客户离开。在不了解影响任何给定结果的因素的情况下,使用机器学习模型来做出决策是很困难的。

公司交流数据见解和机器学习模型结果的一种常见方式是通过分析仪表板。像 Tableau、Alteryx 这样的工具,甚至是使用 Django 或 Flask 这样的 web 框架的定制工具,都使得创建这些仪表板变得很容易。

然而,在实践中,创建这些类型的仪表板通常非常昂贵和耗时。那么,使用 Streamlit 是更传统方法的一个好的替代方法。Streamlit 是一个基于 Python 的库,允许您轻松创建免费的机器学习应用程序。您可以轻松地读取保存的模型,并通过直观的用户友好界面与之交互。它允许您显示描述性文本和模型输出、可视化数据和模型性能、使用侧栏通过 UI 修改模型输入等等。

总的来说,Streamlit 是一个易于学习的框架,允许数据科学团队在短短几个小时内创建免费的预测分析 web 应用程序。 Streamlit gallery 展示了许多使用它进行分析和机器学习的开源项目。你也可以在这里找到 Streamlit 的文档。

由于其易用性和多功能性,您可以使用 Streamlit 来交流各种数据见解。这包括来自探索性数据分析(EDA)的信息,来自分类和回归等监督学习模型的结果,甚至来自非监督学习模型的见解。出于我们的目的,我们将考虑预测客户是否会停止购买某家公司产品的分类任务,这种情况称为流失。我们将在这个项目中使用虚构的电信客户流失数据。

这篇文章的灵感来自于数据教授 YouTube 频道上的一个 Streamlit 教程。教程可以在这里找到。还有一个中等文章版本的教程可以在这里找到。

建立并保存分类模型

我们将从使用随机森林构建和保存一个简单的流失分类模型开始。首先,让我们使用以下命令在终端中创建一个文件夹:

mkdir my_churn_app

接下来,让我们将目录更改到新文件夹中:

cd my_churn_app

现在,让我们使用文本编辑器创建一个名为 churn-model.py 的新 Python 脚本。在这里,我将使用 vi 文本编辑器:

vi churn-model.py

现在,让我们导入几个包。我们将与熊猫、Scikit-learn 的 RandomForestClassifier 和 Pickle 合作:

import pandas as pdfrom sklearn.ensemble import RandomForestClassifierimport pickle

现在,让我们放宽熊猫数据框行和列的显示限制,然后读入并显示我们的数据:

pd.set_option(‘display.max_columns’, None)pd.set_option(‘display.max_rows’, None)df_churn = pd.read_csv(‘telco_churn.csv’)print(df_churn.head())

让我们过滤数据框,使其仅包含性别、支付方式、每月费用、任期和流失等列。这些列中的前四列将被输入到我们的分类模型中,我们的输出是“流失”:

pd.set_option(‘display.max_columns’, None)pd.set_option(‘display.max_rows’, None)df_churn = pd.read_csv(‘telco_churn.csv’)df_churn = df_churn[[‘gender’, ‘PaymentMethod’, ‘MonthlyCharges’, ‘tenure’, ‘Churn’]].copy()print(df_churn.head())

接下来,让我们将数据框的副本存储在一个名为 df 的新变量中,并用零替换缺失值:

df = df_churn.copy()df.fillna(0, inplace=True)

接下来,让我们为分类列 Gender 和 PaymentMethod 创建机器可读的虚拟变量:

encode = [‘gender’,’PaymentMethod’]for col in encode:dummy = pd.get_dummies(df[col], prefix=col)df = pd.concat([df,dummy], axis=1)del df[col]

接下来,让我们将 churn 列值映射到二进制值。我们将客户流失值“是”映射到值 1,将“否”映射到值 0:

import numpy as npdf[‘Churn’] = np.where(df[‘Churn’]==’Yes’, 1, 0)Now, let’s define our input and output :X = df.drop(‘Churn’, axis=1)Y = df[‘Churn’]

然后,我们定义 RandForestClassifier 的一个实例,并使我们的模型适合我们的数据:

clf = RandomForestClassifier()clf.fit(X, Y)

最后,我们可以将模型保存到 Pickle 文件中:

pickle.dump(clf, open(‘churn_clf.pkl’, ‘wb’))

现在,在终端中,让我们使用以下命令运行 Python 脚本:

python churn-model.py

这应该会在我们的文件夹中生成一个名为 churn_clf.pkl 的文件。这是我们保存的模型。

接下来,在终端中,使用以下命令安装 Streamlit:

pip install streamlit

让我们定义一个名为 churn-app.py 的新 Python 脚本。这将是我们用来运行 Streamlit 应用程序的文件:

vi churn-app.py

现在,让我们导入一些额外的库。我们将导入 Streamlit、Pandas、Numpy、Pickle、Base64、Seaborn 和 Matplotlib:

import streamlit as stimport pandas as pdimport numpy as npimport pickleimport base64import seaborn as snsimport matplotlib.pyplot as plt

显示文本

我们要做的第一件事是如何向我们的应用程序添加文本。我们使用 Streamlit 对象上的 write 方法来实现这一点。让我们创建我们的应用程序头,称为流失预测应用程序:

我们可以使用以下命令在本地运行我们的应用程序:

streamlit run churn-app.py

我们应该看到这一点:

从应用程序右上方的下拉菜单中,我们可以将主题从暗变亮:

现在我们的应用程序应该是这样的:

最后,让我们在 UI 中添加更多的描述性文本,然后重新运行我们的应用程序:

st.write(“””# Churn Prediction AppCustomer churn is defined as the loss of customers after a certain period of time. Companies are interested in targeting customerswho are likely to churn. They can target these customers with special deals and promotions to influence them to stay withthe company.This app predicts the probability of a customer churning using Telco Customer data. Herecustomer churn means the customer does not make another purchase after a period of time.“””)

允许用户下载数据

接下来我们可以做的是修改我们的应用程序,以便用户可以下载训练他们模型的数据。这对于执行应用程序不支持的任何分析非常有用。为此,我们首先读入数据:

df_selected = pd.read_csv(“telco_churn.csv”)df_selected_all = df_selected[[‘gender’, ‘Partner’, ‘Dependents’, ‘PhoneService’,’tenure’, ‘MonthlyCharges’, ‘target’]].copy()

接下来,让我们定义一个允许我们下载读入数据的函数:

def filedownload(df): csv = df.to_csv(index=False) b64 = base64.b64encode(csv.encode()).decode() # strings <->   bytes conversions href = f’<a href=”data:file/csv;base64,{b64}”     download=”churn_data.csv”>Download CSV File</a>’ return href

接下来,让我们将 showPyplotGlobalUse deprecation 警告指定为 False。

st.set_option(‘deprecation.showPyplotGlobalUse’, False)st.markdown(filedownload(df_selected_all), unsafe_allow_html=True)

当我们重新运行我们的应用程序时,我们应该看到以下内容:

数值输入滑块&分类输入选择框

我们可以做的另一件有用的事情是为用户创建输入侧栏,允许他们更改输入值,并查看它如何影响流失概率。为此,让我们定义一个名为 user_input_features 的函数:

def user_input_features(): pass

接下来,让我们为分类列 Gender 和 PaymentMethod 创建一个侧栏。

对于分类列,我们调用侧栏对象上的 Selectbox 方法。Selectbox 方法的第一个参数是分类列的名称:

def user_input_features(): gender = st.sidebar.selectbox(‘gender’,(‘Male’,’Female’)) PaymentMethod = st.sidebar.selectbox(‘PaymentMethod’,(‘Bank transfer (automatic)’, ‘Credit card (automatic)’, ‘Mailed check’, ‘Electronic check’)) data = {‘gender’:[gender],‘PaymentMethod’:[PaymentMethod],} features = pd.DataFrame(data) return features

让我们调用我们的函数并将返回值存储在一个名为 input 的变量中:

input_df = user_input_features()

现在,让我们运行我们的应用程序。我们应该看到一个性别和支付方式的下拉菜单选项:

这项技术非常强大,因为用户可以选择不同的支付方式,并根据支付方式了解客户流失的可能性有多大。例如,如果银行转账导致客户流失的可能性更高,也许一家公司会向这些客户发送有针对性的信息,鼓励他们改变支付方式。他们也可以选择提供某种形式的财务激励来改变他们的支付方式。关键是,这些类型的见解可以推动公司的决策,让他们更好地留住客户。

我们还可以添加月度费用和任期:

def user_input_features(): gender = st.sidebar.selectbox(‘gender’,(‘Male’,’Female’)) PaymentMethod = st.sidebar.selectbox(‘PaymentMethod’,(‘Bank   transfer (automatic)’, ‘Credit card (automatic)’, ‘Mailed check’, ‘Electronic check’)) MonthlyCharges = st.sidebar.slider(‘Monthly Charges’, 18.0,118.0, 18.0) tenure = st.sidebar.slider(‘tenure’, 0.0,72.0, 0.0) data = {‘gender’:[gender],‘PaymentMethod’:[PaymentMethod], ‘MonthlyCharges’:[MonthlyCharges],‘tenure’:[tenure],} features = pd.DataFrame(data) return featuresinput_df = user_input_features()

接下来,我们可以显示模型的输出。为了做到这一点,我们首先需要指定默认的输入和输出,如果用户没有选择任何。我们可以将用户输入函数插入到 if/else 语句中,该语句表示如果用户没有指定输入,则使用默认输入。这里,我们还将为用户提供一个选项,使用侧栏方法 file_uploader()上传包含输入值的 CSV 文件:

uploaded_file = st.sidebar.file_uploader(“Upload your input CSV file”, type=[“csv”])if uploaded_file is not None: input_df = pd.read_csv(uploaded_file)else: def user_input_features(): … #truncated code from above return featuresinput_df = user_input_features()

接下来,我们需要显示模型的输出。首先,让我们显示默认的输入参数。我们在数据中读到:

churn_raw = pd.read_csv(‘telco_churn.csv’)churn_raw.fillna(0, inplace=True)churn = churn_raw.drop(columns=[‘Churn’])df = pd.concat([input_df,churn],axis=0)Encode our features:encode = [‘gender’,’PaymentMethod’]for col in encode: dummy = pd.get_dummies(df[col], prefix=col) df = pd.concat([df,dummy], axis=1) del df[col]df = df[:1] # Selects only the first row (the user input data)
df.fillna(0, inplace=True)

选择我们想要显示的功能:

features = [‘MonthlyCharges’, ‘tenure’, ‘gender_Female’, ‘gender_Male’,‘PaymentMethod_Bank transfer (automatic)’,‘PaymentMethod_Credit card (automatic)’,‘PaymentMethod_Electronic check’, ‘PaymentMethod_Mailed check’]df = df[features]

最后,我们使用 write 方法显示默认输入:

# Displays the user input featuresst.subheader(‘User Input features’)print(df.columns)if uploaded_file is not None: st.write(df)else: st.write(‘Awaiting CSV file to be uploaded. Currently using example input parameters (shown below).’) st.write(df)

现在,我们可以使用默认输入或用户输入进行预测并显示它们。首先,我们需要读入我们保存的模型,它在一个 Pickle 文件中:

load_clf = pickle.load(open(‘churn_clf.pkl’, ‘rb’))

生成二进制分数和预测概率:

prediction = load_clf.predict(df)prediction_proba = load_clf.predict_proba(df)

并写出输出:

churn_labels = np.array([‘No’,’Yes’])st.write(churn_labels[prediction])st.subheader(‘Prediction Probability’)st.write(prediction_proba)

我们发现,使用银行转账作为支付方式、月费为 18 美元的新男性客户有 97%的可能性会选择这家公司。我们现在已经完成了应用程序的构建。接下来,我们将使用 Heroku 将它部署到一个实时网站上。

部署应用程序

Web 应用程序部署是 ML 管道中另一个耗时且昂贵的步骤。Heroku 使快速部署免费的网络应用程序变得容易。首先,我们需要向我们的应用程序文件夹添加一些额外的文件。我们将添加一个 setup.sh 文件和一个 Procfile。Streamlit 和 Heroku 将在运行应用程序之前使用这些文件来配置环境。在“终端”的应用程序文件夹中,创建一个名为 setup.sh 的新文件:

vi setup.sh

在文件副本中,粘贴以下内容:

mkdir -p ~/.streamlit/echo “\[server]\n\port = $PORT\n\enableCORS = false\n\headless = true\n\\n\“ > ~/.streamlit/config.toml

保存并离开文件。我们需要创建的下一件事是一个 Procfile。

vi Procfile

将以下内容复制并粘贴到文件中:

web: sh setup.sh && streamlit run churn-app.py

最后,我们需要创建一个 requirement.txt 文件。我们将添加我们在那里使用的库的包版本:

streamlit==0.76.0numpy==1.20.2scikit-learn==0.23.1matplotlib==3.1.0seaborn==0.10.0

要检查软件包版本,您可以在“终端”中运行以下命令:

pip freeze

我们现在准备部署我们的应用程序。按照以下步骤进行部署:

  1. 首先,如果您有 Github 帐户,请登录。如果没有,请先创建一个 Github 帐户。
  2. 在左侧面板中,单击“存储库”旁边的绿色新建按钮。
  3. 为您的存储库创建一个名称。{您的姓名}-churn-app 应该没问题。对我来说,应该是 sadrach-churn-app。
  4. 单击“上传现有文件”链接,然后单击“选择文件”。
  5. 将 codecrew_churn_app-main 中的所有文件添加到 repo 中,然后单击 Commit。
  6. 去 Heroku.com 创建一个账户。
  7. 登录您的帐户。
  8. 点击右上角的新建按钮,然后点击创建新应用。
  9. 你可以给这个应用起任何你喜欢的名字。我给我的应用命名如下:-churn-app。即:sadrach-churn-app,点击创建 app。
  10. 在部署方法中,单击 GitHub
  11. 连接到您的 GitHub repo。
  12. 登录,复制并粘贴您的回购名称。单击搜索并连接。
  13. 向下滚动到手动部署,然后单击部署分支。
  14. 等待几分钟,你的应用程序应该是活的!

你可以在这里找到我版本的 churn 应用,在这里找到 GitHub 库。

结论

Streamlit 是一个强大的库,允许快速轻松地部署机器学习和数据应用程序。它允许开发人员为机器学习模型和数据分析创建直观的用户界面。对于机器学习模型预测,这意味着更好的模型解释能力和透明度,这可以帮助公司做出决策。许多机器学习模型公司面临的一个已知问题是,不管准确性如何,都需要对哪些因素驱动事件做出一些直观的解释。

Streamlit 为模型的可解释性和解释提供了许多途径。侧边栏对象使开发人员能够创建易于使用的滑块,允许用户修改数字输入值。它还提供了一个选择框方法,允许用户查看分类值的变化如何影响事件预测。文件上传方法允许用户上传 csv 文件形式的输入,并随后显示模型预测。

虽然我们的应用程序侧重于客户流失分类模型,但 Streamlit 可用于其他类型的机器学习模型,包括监督和非监督模型。例如,为回归机器学习模型(如房价预测)构建一个类似的 web 应用程序会相对简单。此外,您可以使用 streamlit 为使用 K-means 或层次聚类等方法的无监督学习工具开发 UI。最后,Streamlit 并不局限于机器学习。您可以将 streamlit 用于任何数据分析任务,如数据可视化和探索。

除了支持简单的 UI 开发之外,使用 Streamlit 和 Heroku 可以省去 web 应用程序部署的许多麻烦。正如我们在本文中看到的,我们可以在几个小时内轻松部署一个实时的机器学习 web 应用程序,而传统方法需要几个月。

如果你有兴趣学习 python 编程的基础知识、Pandas 的数据操作以及 python 中的机器学习,请查看Python for Data Science and Machine Learning:Python 编程、Pandas 和 sci kit-初学者学习教程 。我希望你觉得这篇文章有用/有趣。

本帖原载于 内置博客 。原片可以在这里找到https://builtin.com/machine-learning/streamlit-tutorial

掌握 XGBoost

原文:https://towardsdatascience.com/mastering-xgboost-2eb6bce6bc76?source=collection_archive---------6-----------------------

超参数调整和优化

伊曼纽尔·基翁克在 Unsplash 上的照片

X GBoost 已经成为机器学习领域的一个传奇。其成就包括:(1)2015 年在机器学习竞赛网站 Kaggle 上的 29 场挑战中,有 17 场以 XGBoost 获胜,8 场专门使用 XGBoost,9 场使用 XGBoost 与神经网络进行集成;(2)在 2016 年杯,一个领先的基于会议的机器学习比赛中,所有前 10 名的位置都使用了 XGBoost(陈,XGBoost:一个可扩展的树提升系统,2016)。

简单来说,XGBoost 是一种超级优化的梯度下降和提升算法,它异常快速和准确。这种“超级优化”是通过组合批量梯度下降函数(如上所述)和模型复杂性的惩罚(也称为回归树函数)来实现的;然而,它通常只适用于一种类型的机器学习模型——
决策树(更正式的名称是分类和回归树或购物车)(亚马逊 SageMaker,2019)。

XGBoost 的竞争优势很大程度上是因为它能够快速调查许多超参数并识别最佳参数,然后可以手动设置这些参数,以便模型自动识别正确的参数。

【XGBoost 的超参数调整

在 XGBoost 的例子中,讨论超参数调优比底层数学更有用,因为超参数调优异常复杂、耗时,而且是部署所必需的,而数学已经嵌入到代码库中。虽然手动超参数调整在许多机器学习算法或模型中是必不可少且耗时的,但在 XGBoost 中尤其如此。因此,虽然本节的重点是确定部署 XGBoost 的关键要素——在我们的案例研究和此处的示例中,预测新时尚(“快速时尚”)以在在线服装销售中获得竞争优势——但这些超参数调整课程对 XGBoost 的所有应用都有效,本文中的许多其他机器学习模型应用
也是如此。

参数和超参数的区别和作用对于经济、及时和准确的机器学习部署至关重要。机器学习的一个核心优势是,它能够通过自动调整数千或数百万个“可学习”的参数,发现和识别大数据中的模式和规律。例如,在像 XGBoost
(以及决策树和随机森林)这样的基于树的模型中,这些可学习的参数是每个节点上有多少决策变量。超参数是手动调整,优化逻辑在算法或模型之外。此外,机器学习算法或模型越强大,它拥有或可能拥有的手动设置的超参数就越多。例如,在基于树的算法(如 XGBoost)中,超参数包括树的深度、树的数量、每棵树内的变量、用于每棵树的观察值等。

可以说,对于 XGBoost,有六(6)个超参数是最重要的,其被定义为算法产生最准确、无偏结果的概率最高、最快且没有过度拟合的超参数:(1)要训练多少个子树;(2)最大树深度(一个正则化超参数);(3)学习率;(4)确定叶子上权重极值的 L1 (reg_alpha)和 L2 (reg_ lambda)正则化率;(5)复杂度控制(γ=γ),伪正则化超参数;以及(6)最小儿童体重,另一个正则化超参数。让我们先按顺序总结一下这些超参数的信息,然后再检查对它们进行微调的系统方法,以便快速获得 XGBoost 的最大收益(Laurae,2016) (Tseng,2018)。

设置树的数量通知算法何时停止,以防止过拟合,这是机器学习的最大危险之一。这也很重要,因为通过足够多遍的训练数据,XGBoost 算法会学习或记忆它,每个训练周期都会降低预测的准确性。注意,通过提前停止,XGBoost 交付的最终模型将是您告诉它停止的帧,而不一定是验证分数最低的帧;然而,您可以通过插入如图 1 所示的代码片段来确保它产生最佳模型(Tseng,2018)。

图 1:从 XGBoost 中选择具有早期停止的最佳模型的代码(Tseng,2018)

或者,在 sklearn 的 GridSearchCV 中,使用 best_ntree-limit 定义一个评分方法,如下所示(图 2):

图 sklearn 的 GridSearchCV (Tseng,2018)中 XGBoost 评分限制的代码

最大树深默认为三(3),很少需要超过五(5) (Tseng,2018)。当值小于 3 时(< 3), it implies a problem with the data wherein it is missing useful interactions that would be expected. There are some arguments that changing the maximum tree depth to fractions of whole numbers is over-tuning, meaning it takes lots of time and works to figure out the decimal fraction, which is more effort than the resulting benefits. The larger the tree depth, the higher the probability of over-fitting; therefore, it is prudent to increase it reluctantly and only by units of one and even then, probably never higher than five (5). Alternatively, attempt to adjust other hyperparameters that will enable the model to be more robust, meaning the influence of outliers in the data will be minimized (Chris, 2016). Also, XGBoost “aggressively consumes” memory when training deep trees. Therefore, the available memory and/or maximum tree depth may need to be set accordingly (xgboost developers, 2019).

The learning rate (α) will be multiplied by the weight in every tree. There is usually an inverse relationship between the learning rate and accuracy. In other words, a lower learning rate improves the final model measured in predictive accuracy (lower cost or error), even though it makes it slower to train. While incrementally reducing the learning rate helps ensure the model
不会在神经网络或随机梯度下降中的理想和最小错误率附近振荡,这是决策树不太常见的问题(Ng,2018) (Tseng,2018)。

L1 和 L2 仅适用于以线性模型而非树形模型为基础进行提升的情况(Chris,2016)。这些正则化因素有助于防止过度拟合,从而在有许多要素时使模型更加简洁(不那么复杂)。具体来说,最小绝对收缩和选择运算符(LASSO)回归使用 L1,而岭回归使用 L2。他们之间的主要区别是刑罚条款。岭回归将系数的平方值作为惩罚项添加到损失函数中,而拉索回归将系数的绝对值作为惩罚项添加到损失函数中(Nagpal,2017)。
L1 通过降低接近于零的权重来鼓励稀疏性,而 L2 鼓励权重更小(Chris,2016)。L1 和 L2 应用了大数据集,其中交叉验证和逐步推进(防止对较小数据集过度拟合的技术)是不切实际的(Nagpal,2017)。对于树木,最好将 L2(λ)值设置为高值,将 L1 (alpha)设置为零(Chris,2016)。

复杂性控制(gamma=γ)或拉格朗日乘数,可以说是挑战大多数机器学习实践者的 XGBoost 超参数。它可以从零(0)变化到无穷大,并且完全取决于数据集和其他超参数。这意味着单个数据集可以有多个理想伽马超参数,具体取决于其他超参数的设置方式。默认值为零(无正则化),灰度系数越高,正则化程度越高。实际上,伽马值的极限在 20 左右,非常罕见(Laurae,2016)。

有两种采样方法来确定最佳伽玛:从零开始或从 10 开始。从零开始时,使用 xgb.cv 并观察训练和测试速度。如果训练速度比测试速度快得多,则增加 gamma 而不是 min_child_weight 超参数,因为目标是控制来自损失而不是损失导数的复杂性。和/或,您可以逐步降低 max_depth。伽马值越高,训练/测试之间的速度差异越小(Laurae,2016)。

最小儿童体重超参数在技术上被定义为儿童必需的瞬时体重的 Hessian 最小和,这对于没有广泛数学背景的人来说没有什么价值。在外行看来,它通过限制树的深度来进行调整,这有助于防止过度拟合(Hahdawg,2016)。其默认值为一(1);
最小儿童体重超参数设置越大,模型就越保守,就越远离潜在的过度拟合(xgboost developers,2019)。

XGBoost 中使用了许多其他的超参数和参数,这些参数或者是自动设置的,或者是不经常调整的,可以在它的文档中引用。然而,剩下最值得注意的是:(1)“助推器”决定使用哪个助推器;有三种— gbtree(默认)、gblinear 或 dart —第一种和最后一种使用基于树的模型;(2)“tree _ method”允许设置使用哪种树构造算法;大约有五种选择。,auto,exact,hist,& gpu_hist —但“近似”“历史”只能用于分布式培训和“大约”对于外部存储器版本。“自动”使用试探法来选择最快的方法,通常是“精确的”,这是对中小型数据集或单个
机器的贪婪和“近似的”对于非常大的数据集;(3)更新(默认设置为 grow_colmaker,prune)。这是一个逗号分隔的字符串,它定义了更新树的顺序,并作为高级用户构造和修改树的模块化方式;它通常是自动设置的,但可以手动覆盖(xgboost developers,2019)。

XGBoost 超参数优化方法

考虑到手动设置超参数以使机器学习算法能够学习最佳参数和结果的重要性,开发系统地接近超参数编程而不是任意猜测值的方法是有意义的。本质上,有三种这样的常用方法:(1)穷举网格搜索(GS);(2)坐标下降(CD);或者,(3)遗传算法
(雷斯特雷波,2018)。

穷举网格搜索(GS)是一种跨越所有超参数设置的强力搜索。它通过计算和比较每个设置的交叉验证损失来发现最佳设置。理想情况下,为了提高效率,搜索模式是随机的(Restrepo,2018)。

坐标下降(CD)可能是设置超参数的最简单的优化方法。像梯度下降一样,它是一种迭代算法,一次搜索一个可能的超参数设置的向量或方向,并在看起来最有希望的方向上继续。它继续这种扫描,直到没有进一步的方向产生任何更多的改进(雷斯特雷波,2018)。

遗传算法是一整类优化算法,在高维离散搜索空间中表现出色。它们通过模拟一群可能的解决方案和最正确的生存方案来模仿自然选择。新的世代或者通过杂交产生,其中两个可能的解决方案被组合,或者通过突变产生,其中单个参数设置可以通过被替换而“突变”。适应度或“最佳”被定义为损失函数为负值的选项(Restrepo,2018)。

房子的主人?数据科学中的学位和证书

原文:https://towardsdatascience.com/masters-of-the-house-on-degrees-and-credentials-in-data-science-9769b9d829b4?source=collection_archive---------15-----------------------

围绕高级学位的争论一如既往地激烈

在 Unsplash 上由哈尼恩·克里姆利拍摄的照片

对数据科学(以及更广泛的技术)的普遍看法表明,这是一个平等主义、以成绩为基础的领域——在这个领域,你是否毕业于斯坦福大学或在一些在线课程中取得优异成绩并不重要。向雇主展示你了解自己的能力,并能帮助他们成长,一份智力回报丰厚的高薪工作指日可待。

当然,现实往往更复杂。正如成千上万坐立不安的博客帖子和 Reddit 帖子所显示的那样,数据科学相关领域的就业市场竞争极其激烈,一些角色的必备技能清单甚至对经验丰富的专业人士来说也可能令人望而生畏。再加上一点疫情引发的不确定性,就很容易理解为什么许多 TDS 读者会怀疑自己的资历,以及他们是否需要寻找更多的资历来让自己的职业生涯走上正确的道路。我们到了:现在是 2021 年,数据科学家再一次问自己(和对方)是否应该考虑申请硕士学位。

我非常同情那些在职业愿望和所接受的正规培训之间经历紧张的人。我花了几年时间攻读博士学位,我非常喜欢完成这个学位,但是我已经有将近十年没有直接使用它了。很难说清楚我所受的教育中哪些元素对我有帮助,哪些元素在我的简历上看起来只是装饰性的。然而,与我在人文学科的微小专业领域不同,数据科学、人工智能和机器学习构成了一个巨大的全球社区,人们很乐意分享他们关于这个主题的经验和见解。在深入挖掘了 TDS 档案之后,我想分享一些我认为最有启发性的文章。

一个最近的帖子作者 Kendric Ng 正面解决了大师的问题。在与朋友和同事进行了多次交谈后,Kendric 决定通过反思自己作为一名前分析师的经历来解决围绕高等学位的集体焦虑,他最近在一家大型科技公司转型为数据科学职位。我最欣赏他的分析的是它的细微差别——以及对一个人来说可能是显而易见的答案对另一个人来说可能是完全相反的信息。肯德里克认为,“除非是与研究相关的职位,否则几乎从来不需要硕士学位,”他还详细介绍了几个在求职过程中对他有用的策略,这些策略帮助他完全绕过了获得另一个学位的需要。

另一个声音是“不需要花哨的学位!”坎普是戴尔·马科维茨,作为谷歌的一名应用人工智能工程师,他对什么让求职者成为合适人选有着真实的理解。Dale 去年写道“与庞大且不断增长的在线资源生态系统相结合,有决心的开发者可以不花一分钱就给自己提供大量的 ML 教育。相反,她鼓励公司重新思考他们的招聘实践,远离传统的、基于证书的资格。

对于那些决定放弃学术项目(并可能在此过程中节省大量资金)但仍希望提升技能并遵循结构化课程的人来说,还有其他解决方案。例如,丹尼尔·伯克分享了他定制的人工智能系列在线课程(免费和付费)和书籍。从他帖子的成功传播来看,他的建议显然触及了痛处。

当然,也有一些人冒险尝试,并对自己的选择非常满意。例如,有一个微妙的变化:在开始走上自学之路后,她意识到她会从一个更正式的结构中受益,现在她“刚刚完成并准备好摇滚”拉希达强调,选择学位并不意味着你自学的日子结束了;恰恰相反。这也是 Richmond Alake 的观点,他对在决定是否开始攻读硕士学位时要考虑的参数进行的分解可能对目前犹豫不决的数据科学家特别有帮助。

在这些帖子中,你会很快发现一个共同点,即并非所有数据科学硕士项目都是平等的。即使在那些有着优秀记录的人当中,最重要的也不是知名度或名师——它很符合你的需求、你的时间表和你的兴趣。一旦你开始,发现成功取决于多种因素,Ilana Weinstein(她自己是 NYU 大学的硕士生)在给即将入学的学生的建议中谈到了这一点。

在我邀请你分享你对这个问题的想法和见解之前,最后一个值得考虑的有价值的观点是讲台另一边的某个人的观点。Jesse Blocher 在范德比尔特的数据科学研究所任教,他非常相信传统的强化学习模式的价值。虽然他也承认并非每个人都需要硕士学位(也不是每个人都负担得起),但他也提出了一些关于结构化学术项目的具体好处的有趣观点,从获得实际的团队合作经验到开拓思考公平和偏见等关键话题的空间。

现在,让我们开始讨论:开始一个高级数据科学学位是一个非常个人化的决定,没有一个放之四海而皆准的答案会实现。但是,尽可能从不同的群体中听到不同的观点会非常有帮助,所以如果你有什么想法或经验想与 TDS 社区分享,请说吧!

火柴火柴!Python 中使用模板匹配的对象检测

原文:https://towardsdatascience.com/matchy-matchy-object-detection-using-template-matching-in-python-3b2bdfa6e250?source=collection_archive---------32-----------------------

照片由德文·艾弗里在 Unsplash 上拍摄

我们在我的上一篇文章中讨论了对象检测,所以这次我将展示另一个如何识别对象的例子:模板匹配。

让我们看看这张参考图片

作者图片

模板匹配意味着你找到一个大图像的一小部分,你可以用它作为参考模板[1]。这种特殊的方法被证明在图像检测以及跟踪物体方面是有用的。

对于这种特殊情况,我们将尝试一种简单的方法,在灰度图像中使用模板匹配

from skimage.io import imread, imshow
from skimage.color import rgb2graylights = imread('medium/lights.jpg')
lights_gray = rgb2gray(lights)
imshow(lights_gray);

作者图片

现在让我们选择一个黄色的灯笼作为模板

template = lights_gray[79:132,46:83]
imshow(template);

作者图片

为了执行模板匹配,我们简单地使用 skimage 的match_template

from skimage.feature import match_template
result = match_template(lights_gray, template)
imshow(result, cmap='viridis');

作者图片

match_template结果中,我们可以看到原始图像中有多个区域的亮度值几乎等于 1。假设模板只能在原始图像中找到一次,我们可以从结果输出中确定具有最高值的像素

import numpy as np
x, y = np.unravel_index(np.argmax(result), result.shape)
print((x, y))

Output: (79,46)

现在,让我们使用这个像素根据我们之前使用的模板来绘制出被识别的对象

import matplotlib.pyplot as plt
imshow(lights_gray)
template_width, template_height = template.shape
rect = plt.Rectangle((y, x), template_height, template_width, color='y', 
                     fc='none')
plt.gca().add_patch(rect);

作者图片

为了一次识别多个图像,我们可以基于给定的阈值寻找峰值,以便与识别的峰值相关,并且将那些峰值与模板大小匹配

from skimage.feature import peak_local_max
imshow(lights_gray)
template_width, template_height = template.shape#set threshold initially at 0.65
for x, y in peak_local_max(result, threshold_abs=0.65):
    rect = plt.Rectangle((y, x), template_height, template_width, color='y', 
                         fc='none')
    plt.gca().add_patch(rect);

作者图片

我们可以看到,通过将阈值设置为 0.65,我们能够确定一些被算法识别为与我们设置的模板相似的对象。然而,其中一个被识别的物体似乎有两个灯笼,而不是一个。在这种情况下,调整一些参数可能有助于识别所有的灯。这方面的例子如下:

  1. 更改模板大小
  2. 更改模板的方向(向任一方向翻转)
  3. 更改图像属性,如对比度

现在你有了它,模板匹配和几行代码

参考文献

[1] R. Brunelli,计算机视觉中的模板匹配技术:理论与实践 (2008),ResearchGate

对我的工作感兴趣?你可以在我的个人资料中看到更多的故事

https://nico-aguila.medium.com/

数学动画,不可复制的研究,用数据讲故事

原文:https://towardsdatascience.com/math-animations-irreproducible-research-and-telling-stories-with-data-d9b55aa3887b?source=collection_archive---------23-----------------------

“故事”是一个有时感觉被过度使用的词,包括在数据科学的背景下。不是每一个结构清晰、有用的幻灯片都是一个故事,这没关系。但是正如玛丽·勒费夫尔在她的关于令人信服的数据叙事的帖子中所言,种经过时间考验的方法可以让任何分析令人难忘和引人入胜,那么为什么要满足于枯燥和敷衍呢?

照片由 amandazi 摄影在 Unsplash 上拍摄

TDS 作者当然不会安于干巴巴的敷衍了事;我们经常惊讶于我们分享的故事和声音的范围。给乍看上去技术性很强的话题注入活力需要高超的技巧和巨大的努力。当你听到“可重复研究”时,你会直接跑到你的厨房把一些爆米花扔进微波炉吗?也许你应该,如果你议程上的下一个项目是 Vincent Vanhoucke 呼吁认识到不可复制的研究的价值,基于他在谷歌的机器人研究工作。

当你的爆米花还新鲜的时候,跳到阿诺·古兹曼-安内斯对疫情期间航空机队优化的探索,一个他清晰而耐心处理的复杂问题。

你还剩下几粒果仁吗?你不会后悔进入 Isabelle Augenstein 对NLP 学者的维基百科条目的研究,其中也讨论了这些维基页面告诉我们的关于该领域的偏见和代表性。

教另一个人如何完成一个具体的目标也是一种讲故事的形式,最好的老师会让他们的学生忘记他们正在学习。想把你的牙齿埋进一个吸引人的、有趣的教程吗?从 Khuyen Tran 的指南开始,展示了如何用 Python 创建数学动画,然后继续进行 Vishesh Khemani,博士对搜索算法的新尝试,将抽象概念推入物理世界(剧透提示:书中涉及)。

用 TDS editor Elliot Gunn 关于文档重要性的资源丰富的专题来结束你的学习课程——它包括了我们在这个至关重要但经常被忽视的主题上发表的一些最好的文章。

如果你对一个引人入胜的故事的想法倾向于投机和元,不要担心:你不会空手而归。你可以花接下来的 11 分钟思考马克·萨鲁菲姆关于拉格朗日力学的想法,利用机器学习和物理学之间的联系。或者带上你的耳机,去悠闲地散步,听听 Jeremie Harris 在 TDS 播客上与 Melanie Mitchell 的对话,他们讨论了人工智能的局限性,以及为什么我们都为人工智能霸主服务的未来可能不会就在眼前

感谢您本周的倾听、阅读和参与,感谢您开放我们的社区进行如此多的对话。也谢谢你的支持。

直到下一个变量,
TDS 编辑

我们策划主题的最新内容:

入门指南

  • 由马特·索斯纳用 Python 实际演示 SQL 与 NoSQL 数据库
  • 追踪数据科学进展的清单作者 Pascal Janetzky
  • 萨拉·a·梅特沃利的《机器人简介

实践教程

  • 科学家日常使用的 Git 命令数据
  • 用艾萨克·阿罗约创作的树叶可视化 2017 年墨西哥野火
  • 多元线性回归 Python 101byChuck Utterback

深潜

  • 神经网络优化器制作简单:核心算法和为什么需要它们作者 Ketan Doshi
  • 极端不确定世界中的数据科学作者詹姆斯·劳
  • m 面向数据科学家的最佳实践作者玛吉·姆汉纳

思想和理论

  • 特尼特神经网络利用时间反演由基里尔·齐加诺夫
  • 为什么你不应该依赖 t-SNE、UMAP 或马蒂亚斯·格鲁伯的 TRIMAP
  • Jean-Christophe b . lois eau的分类并非所有像素都重要

数据科学访谈中的矩阵

原文:https://towardsdatascience.com/math-refresher-for-data-scientist-part-1-matrices-88620a92d046?source=collection_archive---------17-----------------------

数据科学家的数学复习

数据科学家的基本数学从零开始解释

克里斯·利维拉尼在 Unsplash 上的照片

矩阵通常用于许多领域,如物理、图形、风险建模和统计。熟悉矩阵及其转换相对容易,但对于理解数据科学概念(包括线性回归)至关重要。所以,让我们开始吧!

文章分为两部分:
一、数学复习
二。数据科学访谈中的相关问题

一、数学复习

1。矩阵符号

矩阵的简单定义是一个数字的矩形阵列。它按行和列排列,例如:

A 为 2×3 矩阵( m x n 矩阵*,m* = 2 行, n = 3 列)。粗体大写字母,如 AB 用于表示矩阵。如上图所示,方括号 [ ] 或圆括号 ( ) 用于定义矩阵,两者没有区别。

如果 m = n ,则该矩阵称为方阵,例如:

单个元素表示为 a ᵢ,ⱼ,其中 i 表示行数, j 表示列数:

2。矩阵转置

矩阵转置(**a’**或 Aᵀ) 简单地互换列和行,所以对于上面的矩阵 A :

如果一个矩阵是正方形并且B=B’:

那么矩阵 B 就是一个对称矩阵。

3。点积

为了计算具有相同维数的两个非零向量(矩阵的一列)的点积,我们对逐个元素的乘积求和:

4。加减法

首先要注意的是,我们要加或减的矩阵的维数必须相同。如果这个条件为真,那么我们将两个矩阵的相同位置的值相加:

同样的程序适用于减法。

5。矩阵乘法

乘法被认为是最神秘的矩阵运算。正如您将在下面看到的,有了这一步一步的指导,就没有必要害怕了。

标量—矩阵乘法

当我们想用一个数乘一个矩阵时,我们只需用这个标量乘矩阵的每个元素。

矩阵——矩阵乘法

a)首先,我们需要检查两个矩阵是否可以相乘。只有当第一个矩阵的列数等于第二个矩阵的行数时,两个矩阵的矩阵乘积才存在:

如果我们取之前的矩阵:

乘法A(mxn)x**B(**kx*j)*由于 n (3 列 A )不等于 k (2 行 B ):

但我们可以将 B x A 相乘,因为 j 等于 m (2 列 B 和 2 行 A ):

b)乘法,使用符号:

从上面我们知道矩阵乘积是 k (第一个矩阵的行数)x n (第二个矩阵的列数):

因此,对于矩阵 2x2 和 2x3,结果矩阵的尺寸为 2x3:

我们希望计算位于第 1 行第 1 列的位置 1 的值,因此我们使用第一个矩阵的第一行(ab)和第二个矩阵的第一列( e,h)’,如符号示例中用红色标记的。因此,对于放置在第 2 行第 3 列的位置 6 ,考虑到乘法运算 BA ,我们使用第一个矩阵的第二行和第二个矩阵的第三列:

我们对每个位置重复这个过程。最后,我们得到矩阵乘积:

记住乘法的顺序很重要,一般来说 AB 不等于 BA 但是矩阵是结合律:(AB)C*=A(BC)。*

6。单位/单位矩阵

单位或单位矩阵 I 是对角线上为 1,其他地方为 0 的矩阵:

7。行列式

一个 2×2 方阵的行列式 B 是一个标量(简单的数),表示为 det( B )或| B |:

使用符号:

还可以计算更高维度的行列式,例如 3×3 矩阵:

因此,我们只需从第一行中取出值,删除给定值的行和列,留下一个 2x2 矩阵,例如,对于等式右侧的第一项:

请注意,我们不能使用||来定义矩阵,因为:

8。逆矩阵

逆矩阵是一个方阵 B⁻ ,乘以矩阵 B 得到单位矩阵 I:

如果矩阵的行列式不为零,则 2x2 矩阵的逆矩阵为:

所以,对于我们矩阵的逆矩阵 B:

我们可以看看这个:

  • 逆矩阵只存在于矩阵是方阵的情况下,但并不是每个方阵都有逆矩阵。
  • 具有逆矩阵的矩阵是非奇异矩阵
  • 没有逆矩阵的矩阵是奇异矩阵
  • 对于方阵 Ab:(ab)=b⁻a⁻

二。数据科学面试问题

  1. 从下面的矩阵中,哪些乘法是可能的?

我们需要考虑矩阵的维数:

AB:2x33x2-‘内’尺寸相等,所以可能*

BA:3x2 2x3——‘内部’尺寸相等,所以可能

AC:2x32x2-‘内部’尺寸不相等,不可能

BC:3x2 2x2——‘内部’尺寸相等,所以可能

CA:2x2 2x3——‘内部’尺寸相等,所以可能

CB:2x2 3x2-‘内部’尺寸不相等,不可能

(AB)C:(2x2)2x2—“内部”尺寸相等,因此可能

A(BC):2x3(3x2)—“内部”尺寸相等,因此可能。而且我们知道矩阵是结合律, **(AB)C = A(BC),**所以我们可以提前知道这一点,甚至不用查维数。

(BA)C:(3x3)2x2—“内部”尺寸不相等,不可能

B(AC) -不可能

以及其他可能的结果。

*对于“内部”维度,我在这里考虑第一个矩阵的行数和第二个矩阵的列数

如果你对这个问题有任何疑问,请回到关于矩阵乘法的章节(上面的章节 I.5)。

2。矩阵乘以单位矩阵的乘积是多少?

矩阵乘以相同维数的单位矩阵得到原始矩阵。

3。请用矩阵表示法写出线性回归公式。

我们有一个特点:

更多功能:

所以用矩阵符号:

4。上面矩阵 a 中的 a_ ₁₂是哪个值?

因为第一个数字(1)指的是行,第二个数字(2)指的是列,所以答案是 2。

这个问题可能会让你觉得太简单了。但是面试问题并不总是过于复杂,尤其是对于初学者来说。这是为了评估你是否能快速学习新东西。对基础感到舒适是一个很好的起点。

5。请计算矩阵 c 的行列式,关于这个矩阵你能说什么?

矩阵 C 是单位矩阵,单位矩阵的行列式总是 1。

感谢阅读!

我们学习了矩阵的基础知识,包括常用的符号、矩阵转置、矩阵的数学运算(+- *)、行列式和逆矩阵。最后,我们分析了数据科学访谈中的几个问题。

记住学习(数学)技能最有效的方法是实践。所以,不要等到你觉得“准备好了”,就拿起笔和纸,自己尝试几个例子。

您可能还喜欢:

https://kujaga.medium.com/statistical-moments-in-data-science-interviews-bfecd207843d

我很乐意直接通过akujawska@yahoo.com或我的 LinkedIn 个人资料,在下面的评论区听到你的想法和问题。回头见!

数学你需要在 ML 面试中取得成功

原文:https://towardsdatascience.com/math-you-need-to-succeed-in-ml-interviews-9e717d61f296?source=collection_archive---------9-----------------------

机器学习中有许多等式,但对于面试,你需要知道这些等式

free pik 创建的背景向量——【www.freepik.com

我应该在这篇博客的前面加上目标受众——这是给那些对机器学习工程采访感兴趣的人,而不是研究科学家(或类似的角色,如数据科学)。这是因为研究科学家的面试往往非常偏重于理论(数据科学——统计),你需要知道更多的等式,而不是像的 MLE 面试那样,重点更多地放在工程和充分理解 ML 上,而不是专家水平。

如果你在准备 MLE 面试时像我一样,你开始阅读书籍和博客并做笔记。过了一会儿,你意识到有许多数学方程式,你开始想知道哪些对你的面试很重要。既然你不确定,你就研究所有的方程:(

对于 MLE 面试来说,好消息是你很难得到一个明确要求等式甚至推导公式的问题(警告是早期初创企业,他们的面试过程没有正式化,主要取决于面试官)。但是,在回答机器学习问题时,我们需要了解并使用某些等式。

通常,你会用这些等式来支持你的答案。例如,如果面试官问如何减少过度拟合,你提到了 L1(套索)和 L2(岭)正则化,你应该补充说,L2 增加了一个惩罚项,它是损失函数系数的平方,与 L1 相似。在这种情况下,我们没有明确地写出等式,但我们让面试官知道我们意识到了它们,并可以在回答问题时使用它们。

我整理了一份(非详尽的)方程式和解释清单,你在准备 MLE 面试时必须知道。它是为一般的 ML 工程师编写的,对于特定的领域,你需要知道其他的方程。

损失函数

你应该熟悉所有这些损失函数,因为当你谈论一个 ML 模型很多次,面试官会期望强有力的候选人提到他们的损失函数没有提示。

平均绝对误差(MAE) /均方误差(MSE)

这些是用于回归模型的损失函数

原木损失

二元输出分类模型的损失函数

交叉熵损失

多输出分类模型的损失函数

铰链损耗

支持向量机的损失函数

模型

线性回归

尽管线性回归问题不像逻辑回归那样受欢迎,但了解在什么情况下会出现线性回归问题是至关重要的。它是传统 ML 中的基本模型之一,方程非常简单。

逻辑回归

一个非常受欢迎的 ML 面试主题,因为它通常是分类问题的基线模型,面试官可以跟进过度拟合和分类指标。

支持向量机

像线性回归一样,关于 SVM 的问题在 ML 面试中并不流行。然而,了解支持向量机的成本函数公式将有助于您理解控制所允许的错误分类数量的超参数(这是一个关于支持向量机的常见问题)。如果超参数为 0,则模型不允许任何误分类,反之,超参数值越大,允许的误分类越多。

神经网络的正向传递

神经网络在面试中很受欢迎,所以在面试时你至少会遇到一个问题。您应该非常熟悉正向传递方程,以便更好地理解激活函数、权重和输入是如何组合的。

贝叶斯定理

这是机器学习中非常流行的等式,如果不是最流行的话。问题并不倾向于解决贝叶斯定理问题-这些通常是数据科学家的问题-但有时你需要解释朴素贝叶斯模型,这个定理是核心。

L1 和 L2 正规化

了解这两个方程以及它们如何防止过拟合非常重要。过度拟合问题在 ML 面试中相当普遍。

决策树

决策树和它们的表亲随机森林和提升树在 ML 访谈中非常流行,你应该知道两个等式来衡量一个节点的杂质:

基尼指数

决策树的代价函数 —由于编码这个代价函数很简单,所以可以得到一个问题来编码这个代价函数,作为 ML 编码问题的一部分。

指标

当提到 ML 模型时,您需要知道如何评估它们,因此了解每个模型的指标非常重要。

分类指标(精确度、召回率、准确度、F1)

回归指标(平均绝对误差、均方误差)

一些其他指标:

  • 标准化折扣累积收益(排名指标)
  • 并集上的交集(计算机视觉度量)
  • 骰子系数(计算机视觉度量)
  • BLEU 评分(NLP 指标)

激活功能

如前所述,神经网络是一个流行的面试话题,了解流行的激活函数对于回答涉及神经网络和其他 ML 模型的问题是必要的。

Softmax

距离功能

距离函数可以出现在机器学习面试的许多领域:k 近邻、k 均值甚至推荐系统。因此,您应该熟悉 3 种最流行的方法:

余弦

欧几里得

曼哈顿

统计数字

虽然 MLE 面试不会像数据科学家面试那样有那么多的统计问题,但你倾向于将这些等式用于数据分析问题。这类问题经常出现在面试中,因为你工作的主要部分是处理数据。

卡方

用于确定分类特征的预期频率和该特征的观察频率之间是否存在统计显著差异的检验。

置信区间

该区间给出了估计区间将包含感兴趣的值的概率。最常见的置信度是 95%。

标准化

一种数据预处理技术,用于根据平均值调整数据,使其具有单位标准偏差。

正常化

一种数据预处理技术,用于在 0 和 1 之间缩放数据。

摘自维基百科

偏差-方差权衡方程

虽然您应该从概念上了解偏差-方差权衡,但理解这个等式会让您有更好的理解,并让您在回答问题时有更深入的了解

摘自维基百科

结论

现在你已经有了在 MLE 面试中获得成功所需要知道的一些基本等式,下一步就是练习。

降维的数学方法

原文:https://towardsdatascience.com/mathematical-recipe-of-dimensionality-reduction-281ff37957e4?source=collection_archive---------31-----------------------

图像由乔舒亚

随着数据集中变量的增加,其维度也随之增加,这可能会带来以下挑战:

  1. 变量越多,数据可视化就越困难。
  2. 对于特定的业务问题,所有的变量可能都不重要。
  3. 更复杂的模型,因为模型试图从所有变量中学习,需要更多的计算时间。
  4. 探索性数据分析变得困难。

因此, 降维 就是降低数据的维度,以保证其传达最大限度的信息的过程。有两种主要的方法来降低数据集的维度: **1。**通过从基于不同标准的数据中只选择有用的特征,或 **2。**通过从给定数据中提取新特征。

特征选择/减少

1.缺失值比率

对于数据集中的每个特征列,使用以下公式计算 缺失值比率 :

图一。计算缺失值比率的公式

现在,决定一个阈值保持值,超过该值时,您将删除缺失值比率超过阈值的变量。然后,对于其余的缺失值,尝试找出原因,如数据收集过程中的无响应或错误或读取数据时的错误,并对缺失值进行插补。

2.低方差

方差 较低的变量(价差较小,因此所有值趋于相同)对目标变量的影响较小。因此,决定一个阈值,去掉方差小的变量。使用以下公式计算差异:

图二。计算方差的公式

方差与范围相关。因此,在计算方差之前,标准化变量。

3.高度相关

在建立模型时,没有必要保留所有 相关 变量,因为它们都具有相似的特征(消息/信息)。此外,高相关性导致多重共线性问题。这是公式。

图三。计算相关性的公式

为了减少维度,计算所有独立变量之间的相关性,然后如果相关性超过阈值,则删除该变量。在两个变量之间,去掉与目标变量相关性低的一个。

4.反向特征消除

假设所有先前的降维方法都已实施,使用所有变量训练模型并计算其性能。 一次消除 一个变量,每次计算性能。找出对性能影响不大的被剔除变量,直到不再有变量被剔除。

5.正向特征选择

分别使用每个特征训练模型并检查性能,然后 选择给出最佳性能的 变量。重复该过程,一次添加一个变量,产生最高改进的变量为重新训练的变量。除非模型的性能没有显著提高,否则重复整个过程。

特征抽出

1.主成分分析

PCA 就是无监督的降维算法。让我们考虑一个例子,我们有一个 2D 数据,该数据是沿着 X1 的。

图 4。实例一

我们可以使用低方差滤波器选择 X1 并消除 X2 (因为它有低方差)。

图五。实例二

考虑这个例子,没有一个特征具有低方差。因此,我们需要找到新的轴, Z1Z2 ,并沿着 Z1 对每个数据点进行投影(使用点积)。

Z1 称为 主成分 ,在数据中具有最大方差的方向。这里我们看到,在 2D,数据在一个平面内变化。类似地,在 3D 中,数据在 2D 平面上变化。因此,我们有两个主要组成部分。

n 维情况:n 特征中,我们需要找到 k 独立方向的变异数据(n-特征 : f₁、…、fₙ、k-方向 : z₁、z₂、…、zₖ).因此,我们有k-主成分k < n 。这些新特征( k )相对于原始数据没有可解释的意义。

步伐

1.标准化:

不同的特征有不同的范围和尺度,因此方差也不同。因此,使用 标准化 使得每个特征对 PCA 算法的贡献相等。

2.协方差矩阵:

协方差 是两个特征之间相关性强度的度量。

图六。计算协方差的公式

一个 协方差意味着两个特征一起向同一个方向移动,而 协方差意味着两个特征反向移动

假设有三个特征: Y₁y₂&y₃V₁v₂&v₃的方差这就是协方差矩阵的样子。

现在,假设有三个特征: ABC 有两排并且它们被标准化使得的意思是(A) = 的意思是(B) = 的意思是(C) = 0 。设这个矩阵等于 X 。然后,

因此,协方差 = 转置 ( X ) TT

因此,协方差矩阵是矩阵乘 ( 点积)转置 的****XX

3.特征向量和特征值:

特征向量 ( z )是非零向量,与矩阵相乘得到乘以常数 λcov z = λz 。对于一个 NxN 矩阵,有 N 个 相互独立的特征向量相互垂直*。*

在 PCA 中,协方差矩阵的特征向量给出了数据方差的方向越大特征值,越方向的方差越大特征向量。因此,特征值代表扩散的幅度

图 7。公式化特征对

4.选择主成分:

我们需要根据特征值选择 k 方向/特征向量。考虑一个 3x3 协方差矩阵 C 及其对应的特征值( λ₁λ₂λ₃ )和特征向量( V₁V₂V₃ )。

图 8。矩阵 C

下面是 步骤 计算特征值特征向量。或者,可以在 python 中使用线性代数的库 scipy.linalg 来完成。

**In [1]:** import scipy.linalg as la
        import numpy as np**In [2]:** A = np.array([[5,-1,0],[-1,8,3],[0,3,1]])
        print(A) *[[ 5 -1  0]
         [-1  8  3]
         [ 0  3  1]]***In [3]:** EigenValue, EigenVector = la.eig(A)
**In [4]:** print(EigenValue) *[ 9.31426594+0.j  4.81939667+0.j -0.13366261+0.j]***In [5]:** print(EigenVector) *[[ 0.21302554  0.97462873 -0.06869469]
         [-0.91904881  0.17602119 -0.35265538]
         [-0.33161634  0.13825838  0.93322839]]*

从上面看,我们有

图九。特征值和特征向量

现在,|λ₃| > |λ₁| > |λ₂|,总和= |λ₃| + |λ₁| + |λ₂| = 14.26

Var₁ + Var₂ = 0.35

Var₂ + Var₃ =0.66

Var₁ + Var₃ =0.99

由于 Var₁Var₃ 一起可以解释数据中 99% 的方差,我们可以从数据中去掉 Var₂ ,因此 Var₁Var₃ 是数据的 主成分

2.要素分析

考虑一个的例子:

图 10。实例三

一般来说经验薪资有很好的(正)相关性。一个新的变量 可以定义这两个变量。

可以认为是经验薪资两个变量的线性组合,或者你可以说特性经验薪资可以由导出。因此,被称为 因子

线性组合相关特征的过程称为 因子分析因子变量的线性组合。

目标

  1. 减少变量的数量。
  2. 考察变量之间的关系。
  3. 解决多重共线性的问题。

假设

  1. 原始变量应规格化
  2. 因素是相互独立的。
  3. 存在一些潜在的因素可以描述原始变量。

类型

  1. 探索性因素分析(EFA):EFA 中,确定变量之间的关系,并将属于相似 概念的变量分组。没有关于因素之间关系数量的预先假设。
  2. 验证性因素分析(CFA):CFA中的,对因素的数量进行假设,检验变量与 n 具体因素相关的假设。

步伐

1.验证数据需求:

  • 样本量:

  • 样本与变量的比率应为 15:1 或更高
  • 变量的相关值:

2.取变量

为了提取因子,让我们考虑原始变量( Y₁Y₂Y₃ )和要提取的因子(f₁F₂ )。

因子加载 定义了每个因子在定义原始变量时的贡献是多少。

3.假设

  • 错误术语 eᵢ 相互独立。【表示(eᵢ)=0var(eᵢ)=σᵢ)
  • 因子(【fⱼ】)是相互独立的*,并且带有误差项。[ mean(Fⱼ) = 0var(Fⱼ) = 1*

图 11。方程式 1

4.观测协方差矩阵

S 为数据的实际已知值。那么观察到的协方差矩阵变成:

5.计算协方差

现在,我们需要根据因子负载计算协方差。为此,我们需要计算因子权重的 β 值。

方差的性质,我知道

然后是等式 1 (图 11。)变成了,**

还有,

在理想情况下,变量的所有变量都可以用 因子 来解释。因此,

6.计算协方差矩阵

我们需要 因子加载 ( β )和 σ 的值。实际值也是我们已知的( S₁S₂S₃ ),解下面的方程就可以得到βs 和σs

图 12 最终协方差矩阵

因此,我们可以将 因子载荷因子 计算为变量线性组合。因子加载表示每个变量与潜在因子**的关系。****

结论

在多维数据中寻找趋势是困难的。当数据庞大且有多个自变量时,存储、处理和可视化数据变得困难。因此, 降维 有助于缓解这些问题,同时保留有用的特征。

https://www.linkedin.com/in/shubhamdhingra27/

数理统计——沃尔德检验、分数检验和似然比检验的严格推导和分析

原文:https://towardsdatascience.com/mathematical-statistics-a-rigorous-derivation-and-analysis-of-the-wald-test-score-test-and-6262bed53c55?source=collection_archive---------10-----------------------

思想和理论

经典三位一体推理测试的推导与全计算机模拟

绿色变色龙在 Unsplash 上的照片

1:简介&动机

假设我们正在进行一项推断统计分析。假设我们是从常客的角度出发。我们收集了一些数据,愿意对这些数据所来自的分布族进行参数假设,并使用这些数据来恢复一个无限制的最大似然估计(MLE)估计量:

作者图片

假设我们已经指定了以下假设检验:

作者图片

我们需要一些程序来评估上述假设检验。在这篇文章中,我们将涵盖三个经典推理测试程序的“三位一体”:Wald 测试、Score 测试和似然比(LR)测试。

该作品旨在:

  • 深入研究单变量和多变量情况下的这三个过程
  • 从数学上证明在零假设的条件下,所有三个检验都渐近收敛于相同的结果
  • 探索每种测试相对于其他两种测试的独特设计优势
  • 从头开始进行完整的计算模拟,探索这三种技术

这篇文章的目录如下:

作者图片

2:复习林德伯格-利维 CLT、多元正态随机变量的二次型和德尔塔法

为了推导 Wald、Score 和似然比检验的检验统计量的极限分布,我们需要复习林德伯格-李维中心极限定理(CLT)、多元正态随机变量的二次型和 Delta 方法:

林德伯格-李维中心极限定理(CLT):

让我们假设我们有:

作者图片

作者图片

根据林德伯格-利维 CLT,我们有:

作者图片

关于证明林德伯格-列维 CLT 定理的深入推导,请参见我之前的文章: 中心极限定理:证明&实际上是通过数学运算

多元正态随机变量的二次型;

作者图片

让我们证明上面的公式:

作者图片

德尔塔法:

假设我们有一个抽样估计量,我们知道它(通常通过 CLT)有一个带均值和方差的正态分布的极限分布。如果我们将某个新的抽样估计量指定为一个相对于我们原来的抽样估计量连续且可微的函数***【g()***,那么我们知道这个新的抽样估计量也有一个正态分布的极限分布。我们能够恢复这种新的抽样估计量的极限分布的方法是用增量法:

作者图片

让我们证明上面的公式:

作者图片

有了上面的概念刷新器,我们就可以处理推理测试的公式了。我们将从沃尔德试验开始。

3:瓦尔德测试

现在让我们来看三个推理测试中的第一个,Wald 测试:

作者图片

沃尔德检验统计量 W 为:

作者图片

让我们证明在零的情况下 Wald 检验的极限分布是卡方分布:

作者图片

4:分数测试

接下来,我们定义分数测试:

作者图片

我们现在证明了在零检验下 Score 检验的极限分布与 Wald 检验的极限分布相同:

作者图片

作者图片

作者图片

5:似然比检验(LR)

最后,我们定义了似然比检验(LR):

作者图片

我们现在证明 LR 检验的极限分布与 Wald 检验的极限分布相同。

注意,以下证明取决于零模型“嵌套”在无限制模型内(即零模型是无限制模型的参数空间的限制)。在非嵌套模型的情况下,请参见本篇的 S 第 7 节 进行简要概述。

继续证明:

作者图片

作者图片

作者图片

6:三位一体的测试——每个测试的设计优势

作为本文最后几节的总结,我们有以下三个推论检验的统计数据(在单变量和多变量情况下):

作者图片

下图形象地总结了与无限制抽样估计量的抽样分布相关的三种方法:

作者图片

作者图片

我们让这三个推理测试都收敛到同一个渐近结果。有人可能会问***“……为什么我们需要三种不同的技术来完成同样的任务?谁在乎呢。?"*** 。这三种方法中的每一种都有一个“设计优势”,而其他两种则没有。假设一个人正在进行真实世界的分析。考虑到分析中遇到的挑战,三个推理测试之一的特定“设计优势”可能非常便于利用。为了方便起见,这将导致选择三个测试中的一个,而另外两个测试计算起来不那么简单。

Wald 测试的设计优势:

在上表中,我们注意到 Wald 检验是无限制抽样估计量和该抽样估计量的抽样方差的严格函数。与分数检验和似然比检验不同,Wald 检验不涉及用观察数据评估似然函数的经验估计。因此,如果已经有了抽样估计量及其抽样方差的经验估计,就有了进行 Wald 检验所需的全部信息。即使无法访问用于生成抽样估计量的原始观测数据,情况也是如此!这是其他两种方法所缺乏的属性。

分数测试的设计优势:

为了进行 score 检验,我们需要对零陷下的 Score 函数(即对数似然函数的梯度)进行经验评估,以及零陷下的 Fisher 信息。请注意,这些指标都不是无限制抽样估计量的函数。因此,与 Wald 检验和似然比检验不同,Score 检验不要求我们有观测数据下的无限制抽样估计量的公式。如果这个非限制抽样估计量,不管出于什么原因,很难恢复估计值,那么分数检验是一个很好的选择。

似然比检验的设计优势:

从上表中可以看出,似然比检验需要通过非限制模型和限制模型中的观察数据对似然函数进行经验评估。然而,与 Wald 检验和 Score 检验不同,似然比检验不需要构建我们的抽样估计量的协方差矩阵(受限或其他)。因此,如果由于某种原因构建协方差矩阵很困难,则似然比测试可能比其他两个测试更容易执行。

7:关于非嵌套模型的似然比检验的注记

在教学环境中,人们经常宣称:

“为了执行似然比测试,必须是零模型嵌套在无限制模型中的情况。”

**以上说法并不完全正确。**实际上可以对非嵌套模型进行似然比检验;它只是需要更仔细的数学思考,最终需要更多的工作。

在两个模型嵌套的情况下,我们不需要自己执行任何繁重的数学提升或数学推导来恢复我们的似然比检验统计量在零假设条件下的极限分布。相反,我们已经知道这个极限分布是什么;这是一个卡方分布,自由度是两个嵌套模型之间的自由度差!这正是我们上面展示的证明。已知极限分布后,我们就有了使用标准统计软件和库立即计算 p 值所需的所有信息。

现在,在两个感兴趣的模型没有嵌套的情况下,这并不意味着我们不能进行似然比测试。相反,如果我们选择执行一个似然比测试,我们就有动力根据我们自己的具体用例问题,在我们的零假设下,数学推导出似然比测试统计量的极限分布是什么:

作者图片

假设模型不是嵌套的,这个极限分布可能不是卡方分布;相反,它会是别的什么东西,它可能不是一个正式名称的发行版。但是,如果我们能够设法恢复这个分布是什么*(或者通过导出一个封闭的分析形式,或者利用一种方法进行采样,并从数字上构建一个经验估计)*,然后利用这个分布来进行我们的推理测试,那么这个过程将是一个完全有效的似然比测试。同样,这只是需要我们更多的定制工作。我们可能还需要在我们选择的统计编程语言中构建我们自己的定制函数来实现这个似然比测试,而不是利用标准库。

有关非嵌套模型的似然比测试的更多信息,请参见以下参考资料。该参考文献是统计学领域著名的高影响力论文,在学术文献中被引用超过 3000 次。值得一读,以深入研究这一材料!

【王庆海】(1989)。模型选择和非嵌套假设的似然比检验。计量经济学,307–333。

8:计算模拟

python 中提供了计算模拟,用于利用模拟数据集从头开始计算 Wald 测试、分数测试和似然比测试。

我们从导入我们需要的库开始

…和一个模拟数据集的函数

接下来,实现 Wald、分数和似然比测试的函数:

现在我们准备进行分析。让我们打印 python 中内置的“statsmodels”函数的结果:

…现在我们从头开始计算 Wald、分数和可能性比率测试结果:

将“statsmodels”函数的推断结果与我们为三位一体的推断测试定制的函数进行比较,我们可以看到得出的 p 值非常相似。这支持了三个感兴趣的测试都渐近收敛于相同结果的性质。

以上计算模拟的完整代码/笔记本,请看下面的 github 链接

9:总结和结论

希望以上有见地。正如我在以前的一些文章中提到的,我认为没有足够的人花时间去做这些类型的练习。对我来说,这种基于理论的洞察力让我在实践中更容易使用方法。我个人的目标是鼓励该领域的其他人采取类似的方法。以后我会继续写类似的作品。请 订阅并关注我在 Medium 和 LinkedInhttp://www.linkedin.com/in/andrew-rothman-49739630上的更新!

数理统计:最大似然估计的数学证明

原文:https://towardsdatascience.com/mathematical-statistics-mathematical-justifications-of-maximum-likelihood-estimation-e489dd013557?source=collection_archive---------15-----------------------

全变差、KL 散度和最大似然估计之间关系的说明和推导

由 Unsplash 上的 Pietro Jeng 拍摄

1:介绍和动机

在这篇文章中,我试图涵盖最大似然估计(MLE)的数学基础;构造分布参数的抽样估计量的一种常用方法。虽然 MLE 非常常用,但从教学的角度来看,它并不总是被很好地理解或很好地数学化。

这篇文章试图涵盖分布的总变差(TV)、分布的 Kullbac-Leibler (KL)散度和最大似然估计(MLE)之间的联系。

这篇文章的目录如下:

作者图片

2:玩具问题的说明

让我们暂时假设:

作者图片

最大似然估计(MLE)是构造抽样估计量的一种非常常用的估计方法。MLE 估计量是“插入式”估计量,从这个意义上说,它们是以样本构造的经验分布作为输入的泛函。

最大似然估计顾名思义;它们是关于感兴趣参数的似然函数的 arg-max :

作者图片

尽管这个问题回避了,最大似然估计的数学依据是什么?为什么关于感兴趣参数的似然函数的 arg-max 是该参数的“好”抽样估计量?

我们现在开始介绍分布之间的总变差(TV)。

3.总变分(TV)、Kullbac-Leibler (KL)散度和最大似然估计(MLE)

总变差(电视):

让我们再次提出我们的玩具问题:

作者图片

作者图片

作者图片

库尔巴克-莱布勒(KL)散度:

作者图片

作者图片

作者图片

还有维奥拉。我们证明了 KL 散度相对于我们的抽样估计量的 arg-min 等价于似然函数的 arg-max (即最大似然估计)。

完整的推导过程如下:

作者图片

4:总结和结论

总之,最大似然估计(MLE)的数学依据如下:

作者图片

希望以上有见地。正如我在以前的一些文章中提到的,我认为没有足够的人花时间去做这些类型的练习。对我来说,这种基于理论的洞察力让我在实践中更容易使用方法。我个人的目标是鼓励该领域的其他人采取类似的方法。以后我会继续写类似的作品。请 订阅并关注我在LinkedIn 上的更新!

偏差方差权衡的数学理解

原文:https://towardsdatascience.com/mathematical-understanding-of-bias-variance-tradeoff-9366dbc8439a?source=collection_archive---------24-----------------------

从方程理解偏差-方差权衡

我们中的许多人已经在人工智能文献的不同地方读到了偏差和方差,但仍然有许多人努力用数学方程来解释它。每当人们建立一个模型时,他们总是对偏差-方差进行评论,以确定该模型是否可以在现实世界中使用以及其性能有多好。在本文中,我们将关注描述偏差-方差的数学方程,并尝试从数学角度理解这个方程的不同部分。让我首先强调一些对理解这个等式至关重要的假设:

假设

  1. 我们在训练模型时使用假设集 H。
  2. 有一个实际的目标函数 f,我们试图使用学习算法和数据集 d 来逼近它。这个函数 f 总是未知的。
  3. 当我们获取一个数据集 D 并对其应用学习算法时,我们将得到一个预测函数 g,它试图逼近实际的目标函数 f。
  4. 有一个数据点 x 属于数据集 d。

我们将在下面的等式中使用上述假设。

由于本文的主要重点是从数学角度理解偏差-方差方程,我们将直接查看该方程,而不深入其背后的推导过程(如果您想要理解推导过程,请查看参考文献)。

在深入解释之前,我们先来看看偏差和方差的等式:

作者图片

说明

在上面的等式中,我们假设我们正在查看一个数据点 x 并找出错误。现在为了理解右手边,我们使用上面提到的假设。假设我们有一个假设集 H(它表示我们在训练时可能会用到的函数类)。现在,假设我们通过获取数据集 d 并对其运行经验风险最小化(使用优化算法训练模型以最小化经验损失)来应用学习算法,以获得预测器函数 g。在上面的等式中,g^D(x 表示 g 明确地附属于数据集 d,因为对于数据集 d 的不同实现,我们将获得不同的预测器函数。例如:假设我们有一个数据集 D 的 10 种不同实现:D1,D2,D3,…..,D10。现在,当我们取其中每一个并运行经验风险最小化时,我们将分别得到不同的预测值 g1,g2,g3,…,g10。请注意,我只取了 10 个数据集,对应于此,有 10 个不同的预测器,只是为了解释,但可以有无限多的实现。

现在想象我们有许多不同的预测值,用 g_i 表示,我们取它们的平均值得到 gbar(x).从数学上来说,gbar(x)可以显示为比其他学习到的预测器更好,并且更接近实际的目标函数 f。现在,上述等式中右手侧的第一部分简单地表示预测器函数 gD(x).的方差通过数据集 d 的单一实现(这简单地意味着我们明确拥有或给予我们的数据集),我们将获得一些预测值 gD(x,并且我们测量不同预测值在简单指示方差的平均预测值 g^bar(x 周围的分布。

理解上述等式的第二部分非常简单,我们试图测量我们的最佳平均预测器 g^bar(x 的预测相对于实际目标函数 f(x)有多远。这种差异表明我们对数据点 x 的预测有偏差。

实际上,当我们训练一个模型并测量误差时,我们永远无法访问这个平均预测值以及实际的目标函数,我们所拥有的是一些数据集,我们试图使用这些数据集来训练模型和一些基于我们建模决策的假设集。假设我们选择假设集作为神经网络来训练给定的数据集。训练之后,我们将得到一些预测函数 g^D(i.e.,一种神经网络,使用它我们可以测量测试集中数据点的误差。从理论上讲,无论我们测量什么样的误差,都会有一部分归因于偏差,一部分归因于方差,还有一些不可约误差没有显示在上述等式中。

看了上面的等式后,我们可以很快地将过拟合等概念联系起来,每当我们看到训练误差很低但测试误差很高时,我们很快就会说这是过拟合,这是低偏差和高方差的情况。这里,低偏差表示平均预测值非常接近实际目标函数 f,而高方差表示我们的假设集中的预测值相对于平均预测值(即 g^bar(x)过于分散,这意味着不同的数据集 I 将获得不同的预测值,并且它们彼此不接近。因此,为了解决这种情况,我们很快开始应用正则化技术,该技术通过对模型参数施加约束(尝试将 L1 和 L2 正则化公式联系起来),在理论上减小了假设集的大小。直观地说,由于这些约束,随着假设集的减少,方差开始减少,但偏差又开始增加。偏差增加的原因是,由于我们有一个受限制的假设集,平均预测值现在将会改变,并且将只依赖于存在于减少的假设集中的那些预测值,由于这些预测值,它与实际目标函数 f 的接近度可能会降低,这将导致偏差增加。

注意

当你在文献中看到偏差-方差方程时,对数据点 x 有另一个期望,它只是表明我们报告了测试集中所有数据点的期望误差,但在本文中,我们只是试图理解关于一个数据点的方程。

如果您想更深入地了解这个等式是如何推导出来的,请随意查看参考资料:

参考

https://www.youtube.com/watch?v=zrEyxfl2-a8&list = PLD 63 a 284 b 7615313 a&index = 8

线性回归背后的数学

原文:https://towardsdatascience.com/mathematics-hidden-behind-linear-regression-431fe4d11969?source=collection_archive---------16-----------------------

使用微积分探索统计学

大家好,

这是关于线性回归(梯度下降)算法中使用的数学。这是我 IB HL 数学探索的一部分。

线性回归:是什么?

线性回归是一种统计工具,通过分析为给定数据集生成最佳拟合线。为了手动产生回归线,需要执行诸如均方误差和优化成本函数的操作;这两者将在本文档的后面详细解释。当数据集的规模如此之大,以至于手动操作在计算上变得低效时,主要问题就出现了。因此,当数据集变大时,计算机只需用任何语言编写几行简单的代码就可以更快地执行任务。

线性回归算法使用数据集(成对的输入和输出值)来生成该数据集的最佳拟合线。首先,该算法以𝑦 = 𝑎𝑥 + 𝑏的形式生成一个假设,因为该算法旨在输出 a 和 b 的值

计算机程序:它如何找到这些常数(a 和 b)?

Python 有自己的线性回归方法来寻找最佳拟合线中的这些常数。下面简单介绍一下算法及其计算方法。它使用最小二乘回归的修改版本,在一定次数的迭代后得到这些值。在这个场景中,迭代是基于先前的值重复给定的过程。下面是最小二乘回归的修正方法。

它使用通过使用所有输入-输出对计算的均方误差函数;计算每一对的输出和输入之间的差值,并对该差值求平方。这是为了确保输入-输出对最接近算法预测的线。一旦计算出总和,总值除以输入-输出对的数量。这是因为算法计算的是均方误差。为了使线性回归工作良好,并为给定数据集生成一条精确的最佳拟合线,需要最小化这个被称为“成本函数”的均方误差函数。

MSE 函数适合

成本函数基于均方误差的统计概念。下图准确描述了计算和产生均方误差的过程。

(图 1:散点图示例,作者提供的图片)

均方误差的计算方法是,将给定输入值的预测值减去该输入的实际值。然后求差的平方。这是针对特定图形上的所有点进行的。求和完成后,将总值除以图上的数据点数,得到误差的平均值。

实际值和预测值之间的差异是通过减去最佳拟合线上给定输入的输出值得出的。这是因为均方误差的目的是计算与预测相关的数据集中的差异。这是通过放置回归线以使所有𝑥值的实际值和预测值之间的绝对差值最小来实现的。这导致平方差最小,从而获得最小的均方误差。

计算均方误差以显示分散点相对于回归线的接近程度。一次计算一个成本函数的这一部分的问题是,如果数据集的大小通常是数千或数百万,这将花费很长时间。因此,由于技术的进步,计算机可以非常快速地处理大量数据。

在这一点上,均方差很清楚,它是预测值和实际值之间的总误差。然而,仅计算误差是没有帮助的,因为它并不真正返回值以形成最佳拟合线的方程。因此,我们需要某种假设或函数,允许计算那些形成最佳拟合线的值:“成本函数”。

“成本函数”

如上所述,“成本函数”是从均方误差中导出的。

(表格:成本函数中的变量)

让我们从一个假设开始:

上面我已经为具有参数(x,θ1 和θ0)的最佳拟合线定义了一个假设。这些是构成“成本函数”的一些参数。

(图 2:成本函数)

(图 3:进一步关于成本函数)

θ1 和θ0 是写在列表中的一组 2 个值,使得在编程时,θ1 和θ0 的值存储在列表中,使得计算机可以有效地存储更新,并且通过在列表中更新占用较少的存储器。

“成本函数”显示了预测线和实际数据点之间的差异。因此,成本函数值越高,数据中的差异就越大。

现在顾名思义;我想最小化“成本函数”,因为我无意预测不准确和不精确的变量值,这些变量可能对全球问题有重大影响,如地球温度上升导致全球变暖(本文稍后详述)。因此,下面是最小化成本函数的概述,以便预测尽可能准确。

最小化成本函数

一个函数的全局最小值是指一个函数在一个特别定义的输入域中的最小点,也就是说,对于一个 2D 图,每个输出都有一个输入。然而,在 3D 绘图中,每个输出有 2 个输入。因此,目标是确定能够产生平面最小值的 2 个输入值。继续,通过取给定输入的实际值和预测值之间的差并对该差求平方来计算如上所述的成本函数。对差值进行平方,以消除实际值大于预测值时可能出现的任何负差值。

顾名思义,“成本函数”返回特定值θ0 和θ1 的总平方误差,以构建数据集的最佳回归线。因此,为了找出θ0 和θ1 的最佳值,成本函数必须处于它能取的最低 y 值。因此,这就是优化成本函数的原因。优化特定函数是指找到该函数的输入值,使得该函数根据情况处于最大值或最小值。在这种情况下,目标是实现最低的差异,因此成本函数将需要收敛于全局最小值。

为了解决这个问题,参数值应该使得“成本函数”最小。因此,将“成本函数”最小化到其全局最小值将产生最精确的最佳拟合线。最小化“成本函数”的过程称为梯度下降。

到目前为止,我已经回顾了算法的基础,以及它是如何试图实现产生精确的最佳拟合线的目标的。回想一下,最小化成本函数是非常重要的。然而,成本函数有两个变量。这可能吗?

现在,当处理只有一个变量的函数时,可以应用常规微积分规则对函数进行微分,然后将导数等于 0,以获得值

其构成给定函数的最小值。然而,在这种情况下,必须使用一个叫做偏导数的新概念。

偏导数

偏导数是微积分中的一个概念,它考虑了函数的多变量分量;对涉及多个变量的函数求导。导数被分成两个或更多部分,因为每当函数对给定变量求导时,所有其他变量都被视为常数。

找到“成本函数”的最小值和相应的θ1 和θ0 值需要使用在一组称为梯度下降的指令中应用的偏导数。

梯度下降:为什么?

如上所述,我将以相同的方式计算,除了我将计算𝐽(𝜃对θ0 和θ1 的偏导数。

(成本函数,待区分)

将对θ1 和θ0 求导,因为函数中有两个变量。

计算偏导数

(图 4:相对于θ0 的偏导数)

(图 5:相对于θ1 的偏导数)

因此,使用这些计算的偏导数,可以计算梯度下降。现在,梯度下降作为一个过程是试图收敛到“成本函数”的全局最小值的算法(参见:最小化成本函数部分)。通过将θ0、θ1 的值更新一定的量来最小化“成本函数”的过程被称为梯度下降。

下面是梯度下降和计算机如何计算这一过程的概述:

对θ0 和θ1 的更新是从求解微分方程的欧拉方法中导出的。因为有两个变量需要考虑:θ0 和θ1。欧拉数值方法同时但分别应用于两者,表明θ0 计算对θ1 没有直接影响。

对于θ0:

该算法更新𝜃#.的第 n 个值因此,使用θ0 的第(n-1)个值和θ0 的第 n 个值处的均方误差,这就是算法的开发者必须将θ0 初始化为随机值的原因,使得算法在运行时有第(n-1)项要参考。

学习率乘以均方误差,然后从θ值中减去,因为在更新θ0 时必须考虑程序计算的平均误差

*为 Theta1 😗

θ1 的区别在于更新函数。由于偏导数计算,更新函数是不同的。与θ1 类似,θ1 的第 n 个值通过使用θ1 的第(n- 1)个值来计算。这使用与θ1 的更新过程相同的逻辑。

因此,这就是计算机在返回构成成本函数的最小点的θ1 和θ0 的值时如何具有高度决定性和确定性。然而,这方面的一个主要限制是确定要使用的学习率。

在更新时,学习率和成本函数在θ0(n)和θ1(n)处的乘积被减去,因为该算法试图达到成本函数的最小点。

学习率及其相关性

𝜶 =算法的学习率。

学习率表示算法为“成本函数”收敛于全局最小值而必须运行的次数。它是一个常数值,但是线性回归算法的学习率的值是基于开发者的个人选择的。学习率表示算法可以最小化成本函数并输出相应值以形成回归线的速度。

既然已经简单介绍了学习率,让我们回到观察θ0 和θ1 是如何同时更新的。

(图 6:更新θ0)

(图 7:更新θ1)

θ0 和θ1 的组合更新如下所示:

(图 8:θ0 和θ1 的组合更新)

由于梯度下降的计算是基于欧拉数值方法,即使欧拉数值方法可以手动执行,对于大数据集,计算机可以更快地执行,并且具有更高的精度。因此,这简化了大型公司和组织的数据建模。

这个过程将对所有的输入输出对(𝑥,𝑦)重复。因此,变量“I”表示第 n(𝑥,𝑦)对,因为计算机将𝑥和𝑦(input-output 对作为 n×1 阶的 n 长度矩阵读取,因为梯度下降是一种迭代算法。引入这些更新是为了获得最佳拟合线常数的结果。该算法在机器学习行业中广泛用于实现各种回归工具,如多项式回归和逻辑回归。我是在网上做机器学习课程的时候偶然发现这个概念的。短语“重复直到收敛”表示该算法将对(𝑥,𝑦)的所有值进行迭代,直到“成本函数”收敛于其全局最小值。

仔细观察更新,可以看到学习率:𝜶对更新有重大影响,因为学习率乘以给定点的成本。

改变学习率(𝜶)

如果为学习率选择的值太大,这意味着“成本函数”收敛的步骤数很少,因此算法是有效的。然而,情况可能并非如此,因为将学习率选择得太高会导致算法可能过冲,而不是收敛到最小值。

如果为学习速率选择的值太低,这意味着“成本函数”收敛的步骤数很少,因此该算法可能不是非常有效,因为它将花费更长的时间来达到全局最小值。

在变量没有很强相关性的情况下,有必要将学习率对样本数据集的影响可视化。

我使用了一个由 10 个元素组成的小样本数据集来展示不同学习速度的影响。

上表中的数据非常明显地表明,选择合适的学习速率是影响算法在给定数据集上的性能的关键因素。这被视为从 0.01 到 0.1 的增加(仅仅是 10 倍)导致最佳拟合线的不准确预测。然而,从 0.001 到 0.01 的 10 倍的相同增加并没有在直线方程中给出非常高的差异。

至此,我已经完全理解了我所编写的线性回归算法。现在,我将在数据集的背景下评估线性回归算法:新加坡的人口和地表温度。

线性回归:有用吗?

我之前说过,线性回归算法可以预测不同类型数据的未知值,如股票价格、人口、天气等。因此,下面是我选择的两个场景,它们都处于所谓的线性回归适用性范围的极端。我选择的第一个情景是新加坡人口在 50 年内的增长。尽管人口增长通常是指数模型,但我在仔细研究了数据集后觉得;当数据集显示非常接近的线性关系时,也可以应用线性回归。此外,由于我最近刚从新加坡搬来,我非常喜欢这个地方:这是我在决定使用什么数据时考虑的另一个因素。

我使用了来自 WorldOMeter 的新加坡人口数据集,其中有自 1950 年以来的所有数据记录。尽管由于过去缺乏高质量的测量工具,这可能是一个非常不确定的人口测量,但我没有调整或计算任何可能操纵和改变数据库中提供的数据的不确定性。

(图 9:新加坡人口数据,图片由作者提供)

上图是使用 python 编程语言生成的,它显示了新加坡在 1965 年独立后 51 年间的人口增长情况。然而,从视觉上看,回归线并没有显示给定数据的精确概括。这是因为从图上可以看出,回归线上所有标绘点的比例非常低,表明 r 平方值很低,为 0.664。r 平方值是使用 python 库计算的,该库允许我确定我必须使用的最佳学习率,以便我可以实现给定数据集的最高可能 r 平方值。对于我选择的数据集,程序指示我设置学习率的值;alpha 为 0.1,以找到最佳拟合线。

(方程后线性回归)

为了在不同类型的数据集上有效地测试线性回归算法的性能,我选择使用地球表面温度数据,因为它的性质是在很长一段时间内逐渐增加;在这种情况下是 50 年。全世界的科学家都在使用线性回归来预测地球表面的预期温度。这种知识可以成为人类为可能的最坏情况做准备的工具。

为了验证线性回归是否有助于预测未来的地球温度,我获得了自 1965 年以来地球表面温度的变化。我将对 1965 年至 2014 年的数据进行线性回归,得出一条回归线。然后,我将使用 2015 年地表温度数据的变化来验证回归线。

(图 10:地球表面温度数据,图片由作者提供)

如上图所示,线性回归对数据集的表现相当不错。这是显而易见的,因为线前后的点的平衡很高,并且考虑到由于数据收集和处理中的巨大不确定性而可能影响这些类型数据的大量因素,它返回了相当高的 r 平方值 0.845。这表明它可以低误差地预测未来数据。

(方程后线性回归)

奏效了吗?

观察地球表面的温度:

为了确保使用算法得到的回归线是准确和精确的,我将使用 2015 年的增长值作为目标。

尽管预测值与实际值相比存在误差,但误差非常非常小,这表明线性回归在预测地球表面温度变化的情况下非常有用。这表明,使用线性回归的预测分析只能应用于存在大量受控变量且用于识别相关性的变量是连续变量的某些情况。

以下是对新加坡 50 年人口的研究,以显示线性回归如何因错误预测而损害决策:

从一开始画的图可以看出,决定系数很低。这表明相关性很弱。考虑到 r 平方值的范围为 0 到 1,即使 0.664 看起来是一个很高的值,使用这条最佳拟合线进行的预测也会充满误差,并且不会非常准确。为了说明这一点,我特意从我的数据集中排除了 2016 年、2017 年和 2018 年,以便我可以将预测值与人口的实际值进行比较。

使用 2016、2017 和 2018 作为该算法性能的测试值

因此,如上表所示,当将线性回归应用于该数据集时,由于基于前 50 年预测的数量非常不准确,因此表现不佳。因此,可以看出,线性回归不太适合人口数据。这是线性回归的一个局限性,它只能在极少数情况下有用,即使它可能是预测未来情况的一个非常广泛使用的工具。

上述两个数据集都已应用于机器学习的线性回归算法。但是,算法并不是计算回归线的唯一方法。因此,下面是使用最小二乘回归方法对同一数据集进行的一些计算。

(样本数据集)

理论上,最小二乘回归方法如下:

𝑐可以通过将 point(𝑥mean、𝑦mean 代入𝑦 = 𝑚𝑥 + 𝑐.来计算这是因为最佳拟合线肯定会经过𝑥mean 和𝑦mean

在这种情况下,

通过比较,可以看出,与 ML 算法相比,最小二乘回归方法生成了非常相似的回归线。这表明 ML 算法在给定的数据集上表现良好。此外,这表明训练数据集的残差非常小,因为系数值非常相似。因此,这解释了 ML 算法在预测不同类型数据集的未来值时的有效性。

结论

总之,线性回归是非常有用的,但需要一个非常具体的问题类型,如体重指数数据集,股票交易数据集等。首先,线性回归不能很好地处理人口数据,因此可以得出结论,线性回归可能对增长率/下降率变化的数据表现不佳。因此,作为一种替代解决方案,可以使用指数曲线来概括新加坡的人口数据,因为指数曲线最能代表一定数量的增长。

其次,即使这个程序能够为我选择一个学习速度,它也不是在每种情况下都一样。因此,这表明了线性回归算法的另一个局限性。确定成本函数需要多快收敛是非常困难和令人困惑的。

第三,当应用于非线性关系时,线性回归会导致非常高的误差,并且由于当今世界已经在与不确定性作斗争,编程工具不应该产生错误和不准确预测的进一步问题。

最后,回想最初的目的,可以看出线性回归算法根据数据集、学习率、成本函数的参数表现出非常不同的行为。这表明,根据具体情况,线性回归算法可以表现得非常好,也可以表现得非常差。

感谢您阅读我对线性回归背后的数学的探索!。

有些数据集和概念不是我的,所以我在最后列了几个功劳。此外,我欢迎你的任何批评/反馈,无论是在评论中还是通过电子邮件。

电子邮件 ID:rahulr280623@gmail.com,将尽我所能及时回复

信用

布朗利,杰森。"线性回归教程使用梯度下降的机器学习."机器学习掌握,2019 年 8 月 12 日,machinelementmastery . com/linear-regression-tutorial-using-gradient-descent-for-Machine-Learning/。

甘地,罗希。"机器学习算法简介:线性回归.",走向数据科学,2018 年 5 月 28 日,towardsdatascience.com/introduction- to-机器学习-算法-线性-回归-14c4e325882a。

内吉,萨姆尔。“线性回归——详细概述。”,走向数据科学,2018 年 9 月 21 日,towardsdatascience.com/linear-regression-cost-function-梯度-下降-正规-方程组-1d2a6c878e2c。

联合国。“新加坡人口(活)。” Worldometer ,2020 年,www . world ometers . info/world-population/Singapore-population/。

Anuveyatsu。“全球气温。”全球,2018。

傻瓜监督学习背后的数学:浅显易懂的理论(第一部分)。

原文:https://towardsdatascience.com/maths-behind-supervised-learning-for-dummies-the-theory-in-plain-words-part-i-8f9be4d7e33a?source=collection_archive---------23-----------------------

蒂姆·莫斯霍尔德在 Unsplash 上的照片

*机器学习是当今最酷技术的基础。*这是 Ethem Alpaydin 为他的书《机器学习简介》写的序言,这本书是我几年前开始接触数据科学世界时必须要读的。今天,这本书仍然被认为是学术界的圣经。你可以在亚马逊找到的许多书的不同之处在于,它没有谈论技术,它只显示了机器学习的算法部分。这才是重点。大多数今天从事机器学习的人,他们只是使用技术,但不知道这项技术封装的算法的数学基础。在熊猫数据帧上调用 python 中的“LDA”算法非常简单;但是在 R 数据帧上用插入符号执行线性回归更容易。如果您需要建立一个具有一定精确度的模型,这是很好的;但是如果你想真正开发你的资源,你真的需要完全理解你所使用的库或包背后的算法和数学。

我写中型故事,因为这是一个简单的方法来回顾概念,不要忘记它们。因此,我将写几个关于机器学习背后的数学的系列文章,从监督学习开始。不要担心,我知道理论很无聊,所以我会把理论和实践技巧结合起来,以便尽可能有所帮助。

什么是监督学习?怎么可能?

我们都知道,算法是一系列的指令,可以将输入转化为输出。然而,很多时候我们不知道将输入转换成输出所需的命令或指令;尽管进行了多年的研究,我们还是无法建立算法。我们所做的是学会完成任务,即使我们不知道算法的确切步骤。我们的方法从一个基于不同参数的非常通用的模型开始,我们将看到该模型在这些参数下对输出的逼近程度。该学习通过改进参数,直到找到最精确的输出。学习对应于调整那些参数的值。我们知道我们不能建立完美的方法,但是是的,一个好的和有用的近似。

在这个过程中,有一个统计学的观点,因为核心任务是从可用数据的样本中进行推断:获得更适合这些数据样本的参数,基于这些参数创建一个模型,并为全球情况外推该模型。

这个模型可以是预测性的,也可以是描述性的,这取决于我们是想做预测,还是想从数据中获得可解释性。有了它,我们就能够构建不同的应用程序:关联规则、分类任务、回归、模式识别、知识提取、压缩或异常值检测等等。

回归和分类都是监督学习。标准的简化方法是将输入变量称为 X,将输出变量称为 Y(回归中的数字和分类中的类别代码)。任务是学习从 X 到 Y 的映射,我们假设我们可以通过一个定义了一组参数的模型来做到这一点:Y =G(X|P) 其中 G ( ) 是模型, P 是参数。在学习过程中,机器学习算法优化参数以最小化误差,试图尽可能接近正确的值。

在继续之前,这里有一个重要的注意事项:机器学习算法和模型之间的差异,因为我们似乎可以互换使用这两个概念。模型是从具有特定和定义的参数的特定数据中学习到的最终表示;而算法则是建立那个模型的过程:*模型=算法(数据)。*线性回归是一种机器学习算法;但是 y=3x1+3x2+1 是一个模型。

参数与非参数。有人监督与无人监督

资料来源: STHDA

在前一节中,我们使用了一个学习目标函数 G( ) ,它将输入变量 X 最佳地映射到输出 Y,试图表示我们不知道并且试图近似的真实函数 F( ) 。如果我们知道 F( ) 的样子,我们会直接使用它,并跳过学习阶段。

Y=G(X)+E

我们的估计有误差,我们的模型是准确的,但并不完美。这种误差可以有三种不同的成分:偏差误差、方差误差和不可约误差。我们稍后会见到他们。

参数 vs 非参数

在参数算法中,我们将模型映射到一种形式,而非参数算法可以学习从输入到输出的任何映射。

在参数算法的情况下,我们假设形式。这简化了过程,但也限制了它能学到的东西。所以我们必须决定模型的形式,然后寻找这种形式的参数。在线性回归的情况下,我们对算法说:“嘿,伙计,你将通过这种形式学到一些东西: Y=B2X2 + B1X1+ B0" ,算法将寻找使给定数据上的表达式的误差最小化的 B2、B1B0 参数。参数算法的其他例子是 LDA 或逻辑回归。优势是显而易见的:更简单,更快,需要的数据更少。这样做的后果是,你强迫模型去适应一种形式,而这种形式对你的情况来说可能不是最好的,因此准确性会更差。

相比之下,非参数算法可以自由地学习形状,它们不需要先验知识(SVM、随机森林、贝叶斯、DNN),但是它们当然需要更多的数据,并且存在对训练数据过度拟合的更高风险。

有人监督与无人监督

这两种算法之间的区别很明显:如果你有真实的输出,可以用来比较训练期间的准确性,那么这就是监督学习(你可以回答我的估计有多好?);但是如果你对输出没有任何线索,那么这就是无监督学习(这个人更适合聚类 1 还是聚类 2?谁能告诉我这个人的真实集群?)。

在监督学习的情况下,我们有一个关于我们的模型有多好的参考,因为我们有一个在训练期间与之比较的目标输出。当相对于训练数据的给定输出达到可接受的精度时,学习停止。然而,对于无监督算法,你只有输入数据(X)而没有输出目标;所以没有办法计算你的估计的准确性。这发生在聚类或发现关联规则(HDBScan,Apriori,k-medoids)时。

监督算法中的偏差、方差和不可约误差

上面一些段落,我们看到 Y=G(X)+E 在监督学习。这些监督算法可以被视为偏差和方差之间的权衡。偏差是当我们说模型必须符合特定形式时所做的错误假设。这使得问题更容易解决。方差是模型对训练数据的敏感度。

在监督学习中,算法从训练数据中学习模型。我们从训练数据中估计出 G( )G( ) 几乎是 Y ,但是有一个误差 E. 这个误差可以拆分为:偏倚误差、方差误差和不可约误差。

不可避免的错误。不管你用于训练的数据集或你选择的算法如何,这种情况总是会发生。这个误差是由像隐藏变量这样的外部因素引起的。不管你怎么努力,你总是会犯这个错误。

*偏向错误。*我们做这些假设是为了让 G()函数更容易学习。因此,参数算法具有更高的偏差。高偏差使算法学习速度更快,但灵活性较低。例如,线性回归和逻辑回归是高偏差算法;而 SVM 或 kNN 是低偏差算法。

*方差误差。*如果我们选择另一部分训练数据,我们的模型会有多么不同。理想情况下,它不应该改变,但不幸的是,它在许多情况下发生了变化。高方差表明训练数据集中的变化将在很大程度上改变 G()函数。高方差算法的例子有:kNN 和 SVM。

由于我们无法控制不可约误差,我们将集中讨论偏差和方差误差。理想情况下,目标是获得低偏差和低方差。但这两者很难同时实现,所以我们必须平衡它们。参数或线性算法通常实现高偏差但低方差;而非参数或非线性算法通常得到低偏差但高方差。

过拟合和欠拟合

我们的学习过程是通过从训练数据中学习一个 G()函数,试图从具体例子中推断一般模式,与从一般规则中学习具体概念的推断形成对比。机器学习的最终目标是从特定数据很好地推广到问题领域的任何其他数据。

一方面,o 过拟合意味着从训练数据中学习太多,但不能很好地推广到其他数据。这是实践中最常见的问题,因为它会带来培训中的噪音。过度拟合在非参数和非线性模型中更常见(低偏差)。另一方面,欠拟合意味着用不足的数据来训练我们的模型。这就是为什么我们通常迭代,直到达到一定程度的误差。

在实践中,除了测试集的误差之外,我们通常还会绘制出训练集*的误差演变。*如果迭代太多,我们会到达一个点,在这个点上,训练误差持续减小,但突然测试的误差开始变大。这是因为模型在迭代过程中从训练数据中挑选噪声。甜点可以就在测试误差开始上升之前;然而,我们在实践中使用重采样和验证数据集来找到最佳点。

作者图片

分类

到目前为止,我们已经描述了理解任何机器学习分析所应该知道的不同概念。现在让我们分析分类背后的数学,这是机器学习中最重要的领域之一。

当从训练数据中学习时,我们希望有一个所有 A 类例子(也称为正面例子)共享的特定模式,而没有 B 类例子(也称为负面例子)。例如,我们用数百万个汽车描述来训练我们的数据,这些描述表明每个描述是否属于家庭汽车。我们的模型已经学习了,之后,它能够根据给定的描述,判断一辆我们以前没有见过的汽车是否是家用汽车。

想象下面的例子,我们有一个数据集 X,它描述了不同的汽车型号。每个数据点 x 由两个特征 x1,x2 组成,分别代表每辆车的价格( x1 和发动机功率(x2)x .这些车中有些是熟悉的(红点)或不熟悉的(绿点):

作者图片

我们要做的是找到区分红绿点的 C 矩形。具体来说,我们必须找到价格(x1)的 p1 和 p2 值以及限制 C 矩形的发动机功率的 e1 和 e2 值( x2 )。换句话说,成为一辆熟悉的汽车就如同在 p1 < x1 < p2 和 e1 < x2 < e2 之间绘制出 p1、p2、e1 和 *e2 的合适值。*这个方程代表 H,我们相信 C 就是从这个假设类中得出的,也就是可能的矩形的集合(算法)。然而,该学习找到由一组特定的 p1、p2、e1e2 值(模型)指定的特定假设/矩形 h(属于 H) 。因此,参数学习从假设类 H 开始,并基于可用数据找到 p1、p2、e1e2 的值,创建一个等于或最接近真实 C 的假设 h ,指出我们将搜索限制到这个假设类(我们强制绘制一个矩形,但可能真实 C 是一个圆),从而引入偏差。另外,请注意,如果 C 内有一个绿点,这将是一个假阳性;而如果 C 外有一个红点,那就是假阴性。

在现实生活中,我们不知道 c。我们有一个数据集,每辆车的标签为“它熟悉”或“它不熟悉” x. 这个标签被命名为 r 。然后,我们比较 h(x)r ,看看它们是否匹配。这被称为从数据集 X 训练的模型 h 的经验误差:

作者图片

要回答的问题是*它对未包含在训练集中的未来数据点的分类有多好?*这就是泛化的问题。根据我们在寻找 C 时绘制矩形 h 的宽松程度或限制程度,我们可以得到最紧的矩形 S 或最一般的 G:

作者图片

S 和 G 之间 H 的任何 h 都与训练集一致。然而,对于未来的数据点哪个会更好?

根据 H 和 X,我们将有不同的 S 和 g。让我们选择 S 和 g 中间的一个 h 。我们将边距定义为从边界到最近数据点的中间距离。所以我们选择具有最大余量的 h 来平衡可能性。黄色下划线的数据点定义或支持边缘:

作者图片

我们使用一个错误(损失)函数,它不是检查一个实例是否在正确的类上,而是包括它离边界有多远。在一些关键的应用中,如果有任何未来的实例落在 S 和 G 之间,则是有疑问的情况,其被自动丢弃。

在本系列的下一篇文章中,我们将回顾 Vapnik-Chervonenkis 维度、可能近似正确的学习、学习多类和回归。

Adrian Perez 是一名数据科学家,拥有超级计算并行算法博士学位。你可以在他的 中型简介 中查看更多关于他的西班牙语和英语内容。

参考书目

*机器学习入门。*第四版。埃塞姆·阿尔帕丁山。麻省理工学院出版社 2020 年出版。

掌握机器学习算法。杰森·布朗利博士。2016.

傻瓜监督学习背后的数学:浅显易懂的理论(下)

原文:https://towardsdatascience.com/maths-behind-supervised-learning-for-dummies-the-theory-in-plain-words-part-ii-b3681d690c6e?source=collection_archive---------31-----------------------

每个人监督学习背后的代数和几何的快速概述。

乔希·里默尔在 Unsplash 上的照片

在我之前的文章中,我写了这个系列的第一部分:*傻瓜监督学习背后的数学:简单明了的理论,*在这里我们看到了几何&代数是监督学习的基础的概述。在本系列的第二部分中,我将介绍 Vapnik-Chervonenkis 维度,可能近似正确的学习,如何将我们的二元分类器扩展到多个类,以及回归。

瓦普尼克-切尔沃嫩基斯维度也叫蛮力

如果四个个体( x1,x2,x3,x4 )可以分为两类(a 类或 b 类),那么就有 24=16 分类的可能性:(a 中的 x1 ,a 中的 x2 ,a 中的 x3 ,a 中的 x4 或者(a 中的 x1 ,a 中的 x2 ,a 中的 x3 因此,2N 学习问题(或可能的标签)可以由 n 个数据点给出。如果对于所有这些学习问题,我们可以建立一个 h (属于 H 类)将元素正确地分成它们的两个类,那么 H 粉碎了 N 个点。换句话说,给定的 N 个点总是被来自 H 类的假设 h 正确无误地分开。

被 H 粉碎的点的最大数量被称为 Vapnik-Chervonenkis 维数(它足以粉碎空间中 N 个点的特定样本,而不是 2 维中的任何 N 个点)。例如,四个点可以被矩形打碎,尽管它们不可能对齐,但我们已经找到了一个这样的例子。对于二维中给定的 4 个点,我们可以找到一个矩形 h ,它允许正确划分任何 2⁴=16 学习问题(或可能的标记),因此 4 个点被 h:

作者图片

然而,在二维空间中,没有办法用矩形粉碎 5 个点。对于二维空间中的任意 5 个点,总有一些 2⁵=32 可能标号无法求解:

作者图片

当 H 是二维中轴对齐矩形的假设类时,H, *VC(H),*的 Vapnik-Chervonenkis 维数等于 4。

为什么我说这也叫蛮力?好吧,大多数现实生活中的问题不能采取 2^N 的可能性。现实生活中的问题通常局限于一个分布,它们的点是从这个分布中提取的,并且空间中接近的点通常具有相同的标号。我们不必担心所有可能的学习问题,我们将使用一个具有较小 VC 维的假设类,而不是其他具有较高维度的假设类。这将简化我们的搜索,并且对于大多数给定的点都很有效(你还记得偏差的定义吗?).有些情况下,我们无法将所有绘制的点正确地分为两类。在这种情况下,我们将不得不说我们的假设类有多好或多准确,例如,它以 94%的准确性工作良好,它在超过 95%的情况下以小于 3%的误差工作。

可能近似正确的学习又名现实生活中的近似

因此,假设我们有一个来自 H 的矩形假设 h 来解决一个现实生活中的问题,它的数据点来自一个未知的概率分布。我们要找出假设 h 以概率 1-d 最多误差 E 的点数 N 。换句话说,如果正确划分所有点的来自 H 的原始类是 *C,那么当使用 h 作为假设时,在 Ch 之间的差异区域会产生假阴性。*在 1-d% 的情况下(置信概率),该区域内误分配点的概率必须小于 E(错误概率):

作者图片

C 和 h 之间的区域是另一个矩形(图像中的阴影矩形),因此它可以被分成 4 个带(在角上重叠)。落在每一条中的概率是 E/4,随机抽取的点错过每一条的概率是 1-E/4。所有 n 个独立点错过该条的概率是(1-E/4)^N.另外,所有 n 个独立点错过四条中任何一条的概率是 4(1-E/4)^N.这个概率最多一定是 d ,因此 4(1-e/4)^n<d。我们不想深入细节,但是两边除以 4,取日志并重新排列,我们得到 N > (4/E)log(4/ d )。因此,我们至少需要这 N 个大小,以确保错误概率具有给定的置信概率。根据你的假设类别(我们用矩形),这个公式必须被计算。

学习多种课程

到目前为止,我们已经关注了二元分类器,但是如果有几个类呢?如果我们有 K 个不同的类,其中每个实例只属于其中的一个,我们可以将此视为 K 个两类问题。来自训练集 X 的经验误差现在是所有实例(N)上所有类别(k)的预测的总和:

回归

在分类中,我们用二元行为计算经验误差:预测做得好或不好。对于给定的实例 x,C(x)要么是 0,要么是 1。当我们不预测类,而是预测值时,问题就不同了,叫做回归。现在, r 是一个实值,从输入中获得如下 r=f(x)+E. 我们必须将噪声 E 添加到输出中,它代表我们看不到的隐藏变量的影响。为了 100%准确,我们应该说 r=f*(x,z),其中 z 表示隐藏变量。如果没有噪声,它将是一个插值而不是回归(例如 f*是插值)。请注意,自然输出函数中的噪声不是由于记录中的不精确或标记中的误差,这些误差仅在预测时产生影响。

所以我们想用我们的假设类 G()来逼近 f(),训练集 X 上的经验误差必须考虑输出中的数值距离,而不仅仅是分类中使用的等于/不等于。考虑该距离的一种方法是使用差值的平方:

我们的 G()可以是线性或二次或任何其他高阶函数。我们只需用其在经验误差公式上的定义替换 G(),并通过对系数 w ( 为参数的解析解)取偏导数来最小化它。请注意,首先我们选择 G()类(例如,我们决定使用一个线性函数),但是一旦我们设置了最小化经验误差的系数,那么我们就有了来自 G()类的实例 G()。我们应该选择哪个 G()。我在上一篇文章中说过,当函数的阶数增加时,训练误差减小,但你可能会陷入过拟合。

Adrian Perez 是数据科学主管,拥有超级计算并行算法博士学位。你可以在他的 中简介 中查看更多关于他的西班牙语和英语内容。

参考书目

*机器学习入门。*第四版。埃塞姆·阿尔帕丁山。麻省理工学院出版社 2020 年。

用 Python 轻松集成 MATLAB 中的自定义函数

原文:https://towardsdatascience.com/matlab-function-in-python-739c473c8176?source=collection_archive---------24-----------------------

Python 脚本中的 MATLAB 专业级函数

MATLAB 实现通常非常可靠,因为它是由专业人员开发的。但是使用 Python 的优势是巨大的。在这篇文章中,我将展示如何将自定义的 MATLAB 函数集成到 Python 脚本中。

图片来自 Pixabay 的 Gerd Altmann

在 MATLAB 中定义自定义函数

让我们在 MATLAB 中创建一个可以在 Python 中使用的自定义函数。为了演示,我将使用一个初等函数,但同样的想法适用于任何函数。

MATLAB 中的特征值和特征向量

function [V,D] = eigFunc(A)
%returns diagonal matrix D of eigenvalues and matrix V 
% whose columns are the corresponding right eigenvectors, 
% so that A*V = V*D.
[V, D] = eig(A);
end

我把上面的函数保存为eigFunc。该函数接受一个方阵作为输入,输出特征值的对角矩阵D和矩阵V,其列是相应的右特征向量见 MATLAB 中的 eig 函数。

让我们首先在 MATLAB 中使用这个函数进行测试。

clear; close all; clc
A = gallery('lehmer',4);

[V,D] = eigFunc(A)

这将返回:

V =

    0.0693   -0.4422   -0.8105    0.3778
   -0.3618    0.7420   -0.1877    0.5322
    0.7694    0.0486    0.3010    0.5614
   -0.5219   -0.5014    0.4662    0.5088

D =

    0.2078         0         0         0
         0    0.4078         0         0
         0         0    0.8482         0
         0         0         0    2.5362

这个函数在 MATLAB 中的表现和预期的一样出色(因为这个函数是 MATLAB 中eig函数的精确副本。但是如何在 Python 中使用这个函数呢?

用 Python 调用 MATLAB

为 Python 使用 MATLAB 引擎 API

在 Python 中使用 matlab 函数最简单的方法是使用matlab.engine。可以按照这两种方式安装matlab库。

从 MATLAB 内部安装:

cd (fullfile(matlabroot,'extern','engines','python'))
system('python setup.py install')

直接从终端安装:

导航到 MATLAB 源代码位置并编译 python

python setup.py install

请注意,这个 MATLAB 引擎 API 将针对python的具体版本进行安装。如果您使用的是 anaconda,那么您可以检查正在为其安装 MATLAB 引擎 API 的 Python 版本。这与安装其他 Python 库的方式基本相同。详情请访问此处。

import matlab
import matlab.engine
eng = matlab.engine.start_matlab()
A = [[1.0000,    0.5000,    0.3333,    0.2500],
     [0.5000,    1.0000,    0.6667,    0.5000],
     [0.3333,    0.6667,    1.0000,    0.7500],
     [0.2500,    0.5000,    0.7500,    1.0000]]
A = matlab.double(A)
V, D = eng.eigFunc(A, nargout=2)
print("V: ", V)
print("D: ", D)

eng.quit()

以下是输出:

V:  [[0.06939950784450351,-0.4421928183150595,-0.8104910184495989,0.37782737957175255],				[-0.3619020163563876,0.7419860358173743,-0.18770341448628555,0.5322133795757004],[0.7693553355549393,0.04873539080548356,0.30097912769034274,0.5613633351323756],[-0.5218266058004974,-0.5015447096377744,0.4661365700065611,0.5087893432606572]]

D:  [[0.20775336892808516,0.0,0.0,0.0],[0.0,0.40783672775946245,0.0,0.0],[0.0,0.0,0.8482416513967358,0.0],[0.0,0.0,0.0,2.536168251915717]]

结果和以前一样。nargout参数告诉基于 matlab 的函数在这里输出2结果。

创建 Python 包:使用 MATLAB 编译器和 MATLAB 运行时

如果您的系统中安装了 MATLAB,这一切都很好。但是,如果您想将您的 Python 脚本提供给系统上没有安装 MATLAB 的人,该怎么办呢?在这种情况下,您可以使用 MATLAB 中的library compiler应用程序构建一个 Python 库。详情请访问生成 Python 包并构建 Python 应用。

(a)在 MATLAB 中选择库编译器应用程序(b)填写关于 Python 库的信息(图片由作者提供)

(a)选择 MATLAB 函数生成 Python 库(b)添加示例 MATLAB 脚本(图片由作者提供)

(a)点击package生成独立组件(b)等待任务完成(图片由作者提供)

MATLAB 运行时安装

请注意,用户需要安装 MATLAB 运行时才能成功使用这个库。MATLAB runtime 有助于在不安装 MATLAB 的情况下运行已编译的 MATLAB 应用程序或组件。运行时可以从这里下载,Windows,Mac,Linux OS,免费下载。

为 Python 导入基于 MATLAB 的库

import eigFunc
eigFuncAnalyzer = eigFunc.initialize() #calls the matlab runtime
A = [[1.0000,    0.5000,    0.3333,    0.2500],
     [0.5000,    1.0000,    0.6667,    0.5000],
     [0.3333,    0.6667,    1.0000,    0.7500],
     [0.2500,    0.5000,    0.7500,    1.0000]]
A = array.array(A) %not tested
V, D = eigFuncAnalyzer.eigFunc(A, nargout=2)
print("V: ", V)
print("D: ", D)
eigFuncAnalyzer.terminate()

请注意,您可以设计您的eigFunc.m,您可以简单地加载 mat 数据,并且您可以使用scipy.io.savemat函数将 python 数据保存为 mat 格式。有关详细信息,请参见 scipy 文档。

数据类型转换

(图片由作者提供)

参考

  1. 如何从 Python 调用 MATLAB
  2. 从 Python 调用 MATLAB 函数
  3. 生成 Python 包,构建 Python 应用

原载于 2021 年 3 月 5 日https://www.earthinversion.comT22。

Matplotlib 和自定义字体

原文:https://towardsdatascience.com/matplotlib-and-custom-fonts-bee4aac7b5cb?source=collection_archive---------37-----------------------

更权威的指南

为什么要使用自定义字体?

我需要在两种情况下使用自定义字体。首先,我在一个预装字体数量有限的 OS 上(cough Microsoft cough)。第二,我需要为我工作的特定公司或项目匹配设计美学。

将自定义字体安装到 matplotlib 需要两个步骤:

1.在您的计算机上安装自定义字体
2。清理并重新创建存储字体信息的 matplotlibs 缓存

我将走完这两步。

虽然网上有很多在 matplotlib 中使用自定义字体的资源,但是它们有两个问题。首先,它们有时并不是一个完整的指南,只是向您展示一些零碎的内容(例如如何重新加载 matplotlib 缓存)。其次,它们从 matplotlib 中与字体及其属性的交互中抽象出来。为了理解 matplotlib 中的字体,我会经常使用 font_manager 类,它有很多工具来改进你的字体游戏,同时也使编写可重用代码更加简单。

步骤 1:安装自定义字体

这个演练的重点是让字体工作,因为我已经提供了如何将字体安装到您的计算机上的链接。

我们将使用“比邻星新星”字体。

在电脑上安装自定义字体非常容易。对于 Mac,可以使用“字体册”app 。对于 Windows,有一个类似的工作流程,你下载一个字体,然后拖放到一个等效的 Windows 的字体应用。

在这个练习中,我在 Mac 上安装了“Proxima Nova”字体。安装字体到底是什么意思?在我们的例子中,这意味着安装一个**。ttf** (真字型)或**。otf** (原始类型字体)文件到电脑上被 Mac 识别为字体目录的目录。具体来说,它安装的一个文件是这样的:

**ProximaNova-Regular.otf**

这是很重要的一点: 字体只是你电脑上的文件

matplotlib 所做的只是将 matplotlib 中的一个名称 fname 分配给一个文件 name ,它是一个. ttf(或。otf)文件。

步骤 2:处理 matplotlib 字体缓存文件

为了将字体名称映射到字体文件,matplotlib 在其缓存目录中有一个字典(或 json 文件)。注意,这个文件并不总是在同一个地方,而是通常位于主目录。如果你在 mac (windows)上,它通常位于你的 HOME ( %HOME%) 环境变量被设置的地方。

为了让 matplotlib 识别我们的计算机上有一个新的字体文件,我们只需要删除并重新加载这个将字体名称映射到字体文件的字典:

(1)删除 matplotlib 字体缓存文件
(2)使用 matplotlib 重新创建该文件

对于(1),您可以从命令行删除字体列表文件。这是在 Mac 上:

rm ~/.matplotlib/fontlist-v300.json

对于(2),我们正在使用 python 中的 font_manager 类重新创建我们刚刚删除的那个 json 文件

from matplotlib import font_manager# Rebuild font cache
font_manager.__rebuild()

虽然这个过程可能会奏效,但我遇到了一些问题(尤其是在我的另一台 Windows 机器上)。因此,在运行这两个命令之前,让我们先看看几种方法来验证 matplotlib 实际看到的字体,即 matplotlib 中可以使用的字体。

绕道:Matplotlib 能看到什么字体?

检查缓存文件

一种方法是对上面的缓存文件 fontlist-v300.json 进行简单的单词搜索。这个文件是 json 格式的(基本上是一个嵌套字典),每个条目都有一个字体和与字体相关联的属性,比如文件与字体相关联的 fname ,以及字体的相关权重(或粗细) weight

fontlist-v300.json 截图

一个有趣的事情是,你可以看到字体名称重复出现。有多个条目共享 Helvetica 字体名称属性,但是具有不同的字体文件,这些文件显示在 fname 属性中。为什么同一个名字会有不同的字体文件?因为这些不是完全一样的字体!不同的字体文件与不同的权重属性(或者你的字符有多粗)相关联。粗体普通字体粗细有单独的文件。再次说明, 字体只是文件

我一开始说您可以检查缓存文件,所以我在这个文件中查找名称“Proxima Nova ”,结果发现它不在 json 缓存文件中。

列出所有可用的字体

虽然字体缓存文件很好,但在 matplotlib 中检查它们会更容易一些。幸运的是我们的 font_manager 类能够做到这一点。检查字体的一种方法是使用我们的 font_manager 类获得 FontEntry 对象的列表。这些特殊的物体允许我们检查字体和它们的属性。我们在下面看到我们正在访问 ttflist 属性,这是有意义的,因为”。ttf "文件是字体来源的文件类型。

我在上面展示了用 Arial 查找所有字体名称的基本方法。但是,matplotlib 已经领先你一步了。在我们的 font_manager 类中,有一个 find_font 函数,它使用最近邻搜索方法来尝试并查找具有您给定名称的字体。我们看到的是,如果它不能找到一个比邻星 Nova 字体文件(如预期的),而是默认为似曾相识。

回到重新创建缓存

使用上面的步骤检查完文件后,我返回到步骤 2,删除并重新创建缓存。我们现在可以(从 python 内部)检查“Proxima Nova”是否能被我们的 font_manager 类找到。现在看到的是我们的“比邻星 Nova”字体可以找到了!

绘制我们的结果

我们可以看到,当我们设置全局 rc 参数时,我们的字体现在是可用的。

实现类似目标的另一种方法是为绘图中的特定对象设置字体。这里我们把全局字体改成漫画 Sans,然后只把剧情标题改成比邻星 Nova。我们使用了 font_manager 类中我最喜欢的子类之一,FontProperties 类。这里我的变量 myFont 是这个类的一个实例,具体来说,它是一个大小为 30 的 Proxima Nova 字体。这样做的好处是,如果您只需为图形中的不同对象创建不同的 FontProperties 实例(如标题或轴标签实例),就可以更容易地创建模板。

结论

让自定义字体在 matplotlib 中工作是一个简单的过程,下载字体文件,然后重新加载 matplotlib 缓存。本练习的主要收获是 字体是文件,让自定义字体工作是让 matplotlib 找到这些文件的过程。要更深入地了解 font_manager 类**,**的所有方法和实例,我建议你查看 matplotlib 开发者提供的 font_manager 类页面。密谋愉快!

Jupyter 笔记本中的 Matplotlib 动画

原文:https://towardsdatascience.com/matplotlib-animations-in-jupyter-notebook-4422e4f0e389?source=collection_archive---------2-----------------------

用 matplotlib FuncAnimation在 Jupyter 笔记本中创建动画图形

照片由 Isaac Smith 在 Unsplash 上拍摄

Matplotlib 是用于探索性数据分析的最流行的绘图库之一。它是 Pandas 中默认的绘图后端,其他流行的绘图库也是基于它,例如 seaborn。在大多数情况下,绘制静态图应该很好,但当您运行模拟或进行时间序列数据分析时,基本图可能并不总是足够的。您可能希望展示一个动画来帮助您理解状态如何随时间变化。

在本文中,您将学习如何在 Jupyter Notebook 中使用 matplotlib 创建动画。这篇文章的结构如下:

  1. 朱庇特笔记本中的互动情节
  2. Jupyter 笔记本中的嵌入式 HTML5 视频

请查看笔记本获取源代码。

为了演示,我们将创建一个移动的正弦波,下面是一个帧示例:

import matplotlib.pyplot as plt
import numpy as npx = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)plt.plot(x, y)
plt.show()

作者图片

1.朱庇特笔记本中的互动情节

为了在 Jupyter Notebook 中创建交互式绘图,您首先需要启用交互式绘图,如下所示:

# Enable interactive plot
**%matplotlib notebook**

之后,我们导入所需的库。尤其是可以用来给你制作动画的FuncAnimation类。

import matplotlib.pyplot as plt
from matplotlib.animation import **FuncAnimation**

接下来,我们需要创建动画人物的初始状态。我们调用subplots()不带任何参数地创建一个图形fig和一个单轴ax。我们将 x 范围设置为(0, 2*Pi),将 y 范围设置为(-1.1,1.1),以避免它们不断变化。

**fig, ax = plt.subplots()****line**, = ax.plot([])     # A tuple unpacking to unpack the only plotax.**set_xlim**(0, 2*np.pi)
ax.**set_ylim**(-1.1, 1.1)

然后我们创建一个函数animate(),它将被FuncAnimation调用。该函数接受一个参数frame_num——当前帧号。这里我们要做的是根据帧号改变我们行的数据。

def animate(frame_num):
    y = np.sin(**x + 2*np.pi * frame_num/100**)
    line.set_data((x, y))
    return line

最后,我们通过调用带有 4 个参数的FuncAnimation来创建动画对象

  • 第一个参数fig是对我们创建的图形的引用
  • 第二个参数animate是我们创建的在每一帧调用以更新绘图的函数。
  • 第三个参数是frames=100,它定义了“一轮动画”的帧数
  • 最后,interval=20参数设置帧之间的延迟(毫秒)。20相当于50FPS (1000ms / 20 = 50 FPS)。如果数字太大,你会等很长时间,如果数字太小,你会看不到它的速度。一般来说,我们需要大于 16 的 FPS 来实现流畅的动画(人眼只能接收 10–12 帧[1])。
anim = FuncAnimation(fig, animate, frames=100, interval=20)plt.show()

作者图片

请查看笔记本获取源代码

2.Jupyter 笔记本中的嵌入式 HTML5 视频

在前面的例子中,我们已经创建了一个很好的正弦波动画。然而,只有当代码运行时,绘图才是动态的。当然,我们可以截屏,但当你想在网上分享你的 Jupyter 笔记本时,这不是很有效。

我们能做的就是把动画转换成 HTML5 视频,嵌入 Jupyter 笔记本。我们将使用 FFmpeg 进行转换。如果没有,首先需要按照说明下载 FFmpeg 并解压。

之后,我们导入所需的库,并将**'ffmpeg_path'** 设置为本地 ffmpeg 可执行文件的路径:

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from IPython import display**plt.rcParams['animation.ffmpeg_path'] = '/path_to_your/ffmpeg'**

创建动画与前面的示例相同。

fig, ax = plt.subplots()line, = ax.plot([])   # A tuple unpacking to unpack the only plotax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1.1, 1.1)def animate(frame_num):
    y = np.sin(x + 2*np.pi * frame_num/100)
    line.set_data((x, y))
    return lineanim = FuncAnimation(fig, animate, frames=100, interval=20)

但是我们没有用plt.show()来绘制它,而是调用anim.to_html5_video()方法来将动画结果转换成 HTML5 视频。然后,我们需要获取为该视频进行嵌入的 HTML 代码,这是通过调用 IPython display.HTML(video)来完成的。最后,我们调用display.display(html)将 HTML 代码嵌入 Jupyter Notebook。

video = anim.to_html5_video()
html = display.HTML(video)
display.display(html)
plt.close()                   # avoid plotting a spare static plot

作者图片

请查看笔记本获取源代码

结论

在本文中,我们学习了在 Jupyter 笔记本中创建 matplotlib 动画的两种方法。创建动画图可以帮助您运行模拟和进行时序数据分析。

希望这篇文章能帮助你节省学习 matplotlib 的时间。我建议你查看一下文档以获得更多选项&设置,并了解你可以做的其他事情。

感谢阅读。请查看笔记本获取源代码,如果你对机器学习的实用方面感兴趣,请继续关注。

您可能对我的其他一些数据可视化和熊猫文章感兴趣:

  • Python 与 Altair 的交互数据可视化
  • 探索冠状病毒传播的交互式数据可视化
  • 在 Pandas 中把数字和字符串转换成日期时间的 10 个技巧
  • 所有熊猫 json_normalize()你应该知道的扁平化 JSON
  • 使用熊猫方法链接提高代码可读性
  • 如何对熊猫数据帧进行自定义排序
  • 为了数据分析你应该知道的所有熊猫移位()

更多教程可以在我的 Github 上找到

参考

  • https://stopmotionmagazine . com/why-your-frame-rate-fps-matters-in-animation/

MatPlotLib 比你想象的更强大

原文:https://towardsdatascience.com/matplotlib-is-more-powerful-than-you-think-58d45d9cc0d6?source=collection_archive---------43-----------------------

如何用已经很棒的包和 gridspec 提升您的可视化

https://learningfrommachines.substack.com/welcome

介绍

虽然许多数据科学家对基本的matplotlib,或者更具体地说是pyplot非常熟悉,但是没有多少人知道有许多方法可以提高你的可视化技能。除了美学,理解如何利用某些工具将使你的可视化更加信息密集和紧凑,而不牺牲可解释性。

在这篇文章中,我们将关注gridspec,这是一个允许我们将图表捕捉到位并创建更复杂的可视化的工具。

注:任何未注明出处的图片均为作者所有。

Gridspec

如果你熟悉pyplotmatplotlib的基础,那么学习gridspec对你来说会相当直观。

在高层概述中,创建一个gridspec实质上是 指定了 一个 网格的某个维度。然后,使用这些尺寸和基本的 2D 索引,你可以指出你想要你的图表在哪里,你想要它如何定向。让我们首先从一个非常基本的iris数据集的例子开始。

带分布的散点图:

使用iris数据集,即可视化和统计分析的 Hello World,我们将首先创建一个基本散点图,将sepal widthsepal length进行比较。

sklearn.datasets载入数据:

现在来可视化数据:

当调用fig.gridspec(3,3)时,我们初始化一个(3x3)网格来放置图形。

首先,我们将hist_x初始化为在gs[0,:2]中,这意味着它占据了整个第一行和除第三列之外的每一列,这就是为什么在右上角有一个小方块。然后,我们将scatter初始化为从第二行开始,一直到末尾,因此1:对行进行索引,我们希望从第一列开始,在第三列之前结束,因此对列使用0:2 in。利用这种直觉,你可以亲眼看到我是如何绘制的。

现在,使用同样的想法,我们可以有效地创建两个这样的图形:

最后的想法

有许多方法可以更有效地创造同样的情节。然而,如果你是某些工具的新手,我会强调用糟糕的代码获得真正的技术,以理解基本的机制。然后从那里,你可以开始重构和发展你自己的直觉,如何有效地创造更好的情节。这就是我做这篇文章的目的。

我希望这对你有用,如果你有任何问题,请在下面留言!

Jupyter 笔记本中的 Matplotlib 线性回归动画

原文:https://towardsdatascience.com/matplotlib-linear-regression-animation-in-jupyter-notebook-2435b711bea2?source=collection_archive---------14-----------------------

用 matplotlib FuncAnimation在 Jupyter 笔记本中创建动画图形

Firmbee.com 在 Unsplash 上的照片

使用线性回归将值拟合到一条直线时,说明随着更多数据的添加,直线如何拟合数据会非常有帮助。在本文中,您将学习如何创建 Matplotlib 动画,本文从上一篇文章“在 Jupyter Notebook 中制作一个简单的正弦波动画”扩展了主题,使用线性回归将值拟合到一条直线上。

Matplotlib 是用于探索性数据分析的最流行的绘图库之一。在大多数情况下,绘制静态图应该很好,但当您运行模拟或进行时间序列数据分析时,基本图可能并不总是足够的。您可能希望展示一个动画来帮助您理解状态如何随时间变化。在本文中,您将学习如何创建一个 Matplotlib 动画,使用线性回归将值拟合到一条直线上。这篇文章的结构如下:

  1. 收集和准备数据
  2. 朱庇特笔记本中的互动情节
  3. Jupyter 笔记本中的嵌入式 HTML5 视频

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

1.收集和准备数据

为了演示,我们将使用 汽车 数据集。数据集包括汽车名称、mpg、气缸、排量等。(8 个变量)用于 406 种不同的汽车[1]。我们将从织女星数据集加载数据。让我们导入一些库并加载数据来开始:

import matplotlib.pyplot as plt
import numpy as np
from sklearn.linear_model import LinearRegression
from vega_datasets import data%matplotlib inline
%config InlineBackend.figure_format = 'svg'df = data.cars()
df.head()

作者图片

对于本教程,我们将绘制 马力英里每加仑 。让我们用NaN删除行,并将数据转换成适当的格式:

# Drop rows with NaN
df.dropna(subset=['Horsepower', 'Miles_per_Gallon'], inplace=True)# Transform data
x = df['Horsepower'].to_numpy().reshape(-1, 1)
y = df['Miles_per_Gallon'].to_numpy().reshape(-1, 1)

让我们做一个简单的散点图来显示 马力英里每加仑:

plt.scatter(x, y, c='g', label='Horsepower vs. Miles_per_Gallon')
plt.legend()
plt.show()

作者图片

2.朱庇特笔记本中的互动情节

为了在 Jupyter Notebook 中创建交互式绘图,您首先需要启用交互式绘图,如下所示:

# Enable interactive plot
**%matplotlib notebook**

之后,我们导入所需的库。尤其是用于创建动画的FuncAnimation类和用于创建线性回归模型的LinearRegression

import matplotlib.pyplot as plt
from matplotlib.animation import **FuncAnimation**
from sklearn.linear_model import **LinearRegression**

接下来,我们需要创建动画人物的初始状态。x_datay_data都被初始化为[]。我们不带任何参数地调用subplots()来创建一个图形fig和一个单轴ax。我们将 x 范围设置为(30, 250),将 y 范围设置为(5, 50),以避免它们不断变化。ax.plot()被调用两次,创建散点图和折线图。我们也调用LinearRegression()来创建线性回归模型reg

x_data = []
y_data = []fig, ax = plt.subplots() 
ax.set_xlim(30, 250)
ax.set_ylim(5, 50)scatter, = ax.plot([], [], 'go', label='Horsepower vs. Miles_per_Gallon')
line, = ax.plot([], [], 'r', label='Linear Regression')
ax.legend()reg = LinearRegression()

然后我们创建一个函数animate(),它将被FuncAnimation()调用。该函数采用一个参数frame_num——当前帧号。这里我们要做的是根据帧数改变散点图和线性回归图的数据。注意,随着更多数据的添加,调用reg.fit()来拟合值。

def animate(frame_num):
    # Adding data
    x_data.append(x[frame_num])
    y_data.append(y[frame_num])
    # Convert data to numpy array
    x_train = np.array(x_data).reshape(-1, 1)
    y_train = np.array(y_data).reshape(-1, 1)
    # Fit values to a linear regression
    **reg.fit(x_train, y_train)** # update data for scatter plot
    scatter.set_data((x_data, y_data))
    # Predict value and update data for line plot
    line.set_data(**(list(range(250))**, **reg.predict(np.array([entry for entry in range(250)]).reshape(-1, 1)))**)

最后,我们通过调用带有 4 个参数的FuncAnimation来创建动画对象

  • 第一个参数fig是对我们创建的图形的引用
  • 第二个参数animate是我们创建的在每一帧调用以更新绘图的函数
  • 第三个参数frames=len(x),它定义了“一轮动画”的帧数,我们将它设置为训练数据的数量。
  • 最后,interval=20参数设置帧之间的延迟(毫秒)。20相当于50FPS (1000ms / 20 = 50 FPS)。如果数字太大,你会等很长时间,如果数字太小,你会看不到它的速度。一般来说,我们需要大于 16 的 FPS 来实现流畅的动画(人眼只能接收 10–12 帧[2])。
anim = FuncAnimation(fig, animate, frames=len(x), interval=20)
plt.show()

Jupyter 笔记本中的 Matplotlib 线性回归动画(图片由作者提供)

请查看笔记本获取源代码

3.Jupyter 笔记本中的嵌入式 HTML5 视频

在前面的例子中,我们已经用线性回归创建了一个漂亮的动画。然而,只有当代码运行时,绘图才是动态的。当然,我们可以截屏,但当你想在网上分享你的 Jupyter 笔记本时,这不是很有效。

我们能做的就是把动画转换成 HTML5 视频,嵌入 Jupyter 笔记本。我们将使用 FFmpeg 进行转换。如果没有,首先需要按照说明下载 FFmpeg 并解压。

之后,我们导入所需的库,并将**'ffmpeg_path'** 设置为本地 ffmpeg 可执行文件的路径:

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from IPython import display# Turn off matplotlib plot in Notebook
plt.ioff()
# Pass the ffmpeg path
**plt.rcParams['animation.ffmpeg_path'] = '/path_to_your/ffmpeg'**

创建动画与前面的示例相同。

x_data = []
y_data = []fig, ax = plt.subplots()
ax.set_xlim(30, 250)
ax.set_ylim(5, 50)
scatter, = ax.plot([], [], 'go', label='Horsepower vs. Miles_per_Gallon')
line, = ax.plot([], [], 'r', label='Linear Regression')
ax.legend()reg = LinearRegression()def animate(frame_num):
    # Adding data
    x_data.append(x[frame_num])
    y_data.append(y[frame_num])
    # Convert data to numpy array
    x_train = np.array(x_data).reshape(-1, 1)
    y_train = np.array(y_data).reshape(-1, 1)
    **reg.fit(x_train, y_train)**

    # update data for scatter plot
    scatter.set_data((x_data, y_data))
    # Predict value and update data for line plot
    line.set_data(**(list(range(250)), reg.predict(np.array([entry for entry in range(250)]).reshape(-1, 1)))**)anim = FuncAnimation(fig, animate, frames=len(x), interval=20)

但是我们没有用plt.show()来绘制它,而是调用anim.to_html5_video()方法来将动画结果转换成 HTML5 视频。然后,我们需要获取为该视频进行嵌入的 HTML 代码,这是通过调用 IPython display.HTML(video)来完成的。最后,我们调用display.display(html)将 HTML 代码嵌入到 Jupyter 笔记本中。

video = anim.to_html5_video()
html = display.HTML(video)
display.display(html)
plt.close()               # avoid plotting a spare static plot

Jupyter 笔记本中的 Matplotlib 线性回归动画(图片由作者提供)

请查看笔记本获取源代码

结论

在本文中,我们学习了两种在 Jupyter 笔记本中创建 Matplotlib 线性回归动画的方法。创建动画图可以帮助您运行模拟和进行时序数据分析。

希望这篇文章能帮助你节省学习 Matplotlib 的时间。我建议您查看文档以获得更多选项&设置,并了解您可以做的其他事情。

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

您可能对我的其他一些数据可视化和熊猫文章感兴趣:

  • Jupyter 笔记本中的 Matplotlib 动画
  • Python 与 Altair 的交互数据可视化
  • 探索冠状病毒传播的交互式数据可视化
  • 在 Pandas 中把数字和字符串转换成日期时间的 10 个技巧
  • 所有熊猫 json_normalize()你应该知道的扁平化 JSON
  • 使用熊猫方法链接提高代码可读性
  • 如何对熊猫数据帧进行自定义排序
  • 为了数据分析你应该知道的所有熊猫移位()

更多教程可以在我的 Github 上找到

参考

  • [1]数据集—汽车http://lib.stat.cmu.edu/datasets/
  • [2]https://stopmotionmagazine . com/why-your-frame-rate-fps-matters-in-animation/

Matplotlib 与 Ggplot2

原文:https://towardsdatascience.com/matplotlib-vs-ggplot2-c86dd35a9378?source=collection_archive---------4-----------------------

我们期待已久的 Python 与 R 可视化的对决

克里斯·阿蒙在 Unsplash 上的照片

背景故事

我主要使用 python,但是我需要在最近的几个项目中使用 R。R 和 Python 之间有很多不同之处,但是图形最让我动心。用 R 语言产生的可视化效果看起来很过时。我在 python 工作的时候一般用 matplotlib,R 中最接近的可比包是 ggplot2。

但是哪种语言,或者在这种情况下哪种包在可视化方面更好呢?我决定解决这个问题的唯一方法是对使用 matplotlib 和 ggplot2 制作的相同图形进行面对面的比较。

交战规则

为了比较,我们将有七轮和一轮奖金。在每一轮中,我使用相同的数据,并试图在两个包中制作相同的图形。轮次如下:

  1. 散点图
  2. 等值线图表
  3. 热图
  4. 回归多线图
  5. 多线连通图
  6. 极坐标条形图
  7. 多重箱线图
  8. 奖金回合

在每一轮中,包装都根据美观和易用性进行评分,满分为 5 分。结果在这个博客的最后。我会尽量减少我的偏见,尽可能客观。

需要记住的几件事:

  • 在所有回合中,除了奖金,我试图在两个包中建立相同的图形。我使用相同的数据和相同的配色方案。
  • 在某些情况下,由于软件包的限制或我的技能水平,我不能使用相同的数据。我使用模拟数据制作了一个类似的图表。
  • 这是一个包可视化摊牌,这意味着如果 matplotlib 或 ggplot2 不能产生相同的图形,但同一语言的另一个包可以说 seaborn 或 plotly,这不算数。
  • 尽管这不是一个教程,我已经添加了代码片段,但没有深入细节。
  • 在下面的所有图片、图表和代码中,matplotlib(python)是第一个,然后是 ggplot2(R)。
  • 最后,所有的评分都在最后的结果部分。

所以不多说了,

让游戏开始吧,让最好的套餐获胜!!!

第一轮:散点图

对于散点图,我使用了 fat 数据集。该数据集包含个人颈部、胸部、腹部和其他部位的身体测量值。布罗泽克是一个反应变量,是体内脂肪的一种量度。这些图显示了样本数据的高度和重量。色阶和点的大小基于个人的 brozek 值。

这两个软件包能够达到相似的结果,并且相对容易制作。Matplotlib 有更好的颜色渐变方案。我试图在 gglpot2 中获得相同的层次,但中间的颜色看起来单调。此外,matplotlib 中的色阶图例更容易理解。

Matplotlib (python)散点图

使用 matplotlib 绘制散点图

Ggplot2 (R)散点图

使用 ggplot2 的散点图

散点图代码

两个包中的代码都相当简单。

Matplotlib (python)代码片段

使用 Matplotlib 的散点图代码,python

Ggplot2 (R)代码片段

使用 Ggplot2 进行绘图的代码,R

第二轮:等高线图

我在这里使用了两个不同的数据集。以 matplotlib 为例,我们有来自心理学实验的数据。变量杏仁核和 acc 表示已知与情绪和决策有关的两个特定大脑区域的体积。对于 ggplot2 示例,我们有火山活动数据,它测量等待时间和喷发。

这两个包取得了非常相似的结果。但是 matplotlib 中的等高线、标签、图例都优于 ggplot2。

Matplotlib (python)等值线图

使用 matplotlib 绘制等高线图

Ggplot2 (R)等高线图

使用 ggplot2 绘制等高线图

等高线图代码

尽管 matplotlib 的代码很长,但是创建这个地图更容易,默认设置也更好。这可能是因为 python 包的教程比较多。

Matplotlib (python)代码片段

使用 Matplotlib、python 绘制等高线的代码

Ggplot2 (R)代码片段

使用 Ggplot 的等高线图代码,R

第 3 轮:热图

这里我使用了散点图一节中提到的 fat 数据集。在这里,我为数据集中的所有变量制作了一个关联热图。黄色表示变量之间的相关性高,蓝色表示相关性较低。

这两个软件包创建了几乎相同的热图。我遇到的唯一问题是 matplotlib 的变量对齐方式,这让我很难读懂标题。使用默认设置,ggplot2 做得更好。

使用 Matplotlib 的热图(python)

Matplotlib (python)热图

使用 ggplot2 (R)的热图

使用 ggplot2 (R)的热图

热图代码

在 matplotlib 中,我对相关矩阵使用了 matshow()函数。ggplot2 没有独立于绘图矩阵的功能,我需要在绘图之前融化相关矩阵。

Matplotlib 代码段

Ggplot2 代码片段

使用 ggplot2 的热图代码,R

第 4 轮:回归多线图

对于多线图,我使用了著名的 Iris 数据集。在这里,我用萼片长度作为预测变量绘制了萼片宽度的回归线。回归线按物种分组。

图表之间的颜色不完全匹配是我的错。我还忘了更改 matplotlib 图形的形状。也就是说,matplotlib 不能为回归线创建阴影区域,这可以使用 ggplot2 轻松完成。您可以使用 python 中的 seaborn 包创建阴影回归线,但这无助于比较。

使用 Matplotlib 的回归图(python)

Matplotlib (python)回归图

使用 ggplot2 (R)的回归图

回归多线图代码

matplotlib 有更多的数据争论,图形必须使用 groupby()函数单独绘制。这个图形在 ggplot2 中要简单得多,所有的分组都可以在 plot 函数中完成。

Matplotlib (python)代码片段

使用 matplotlib 的回归多线图代码,python

Ggplot2 (R)代码片段

使用 ggplot 的回归多线图代码

第 5 轮:多线连通图

第 4 轮和第 5 轮都显示了变化很小的多线图,但是由于这两个包在这两轮中的表现不同,所以我决定值得包括它。

这里使用的数据来自一个交叉验证项目,该项目比较了线性回归和具有不同 K 值的 K 个最近邻。下图显示了蒙特卡洛交叉验证的测试误差与迭代次数的关系

matplotlib 和 gglpot2 在演示部分同样出色。我遇到了 ggplot2 图例的问题,图例不会显示哪条线对应哪种型号,所以我最终决定放弃它。

Matplotlib (python) m 多线连通图

使用 matplotlib(python)的多线连通图

Ggplot2 (R)多线连通图

使用 ggplot2(R)的多线连通图

多线连通图的代码

matplotlib 的代码更简单、更直接。在 ggplot2 中,必须为每个模型单独添加标记和线条。这两个软件包在数据处理方面都没有任何问题。

Matplotlib 代码段

使用 matplotlib、python 编写多行图形代码

Ggplot2 代码片段

使用 ggplot 的多线图形代码

第 6 轮:极坐标条形图

首先,我不喜欢极坐标图。它们很难破译,也不能很好地传达信息。数据可视化的目标是使信息可读,这在极坐标图中很难实现。在这个比较中,我没有使用任何条形图或直方图,因为我知道的所有可视化工具都可以轻松地制作它们。

对于 ggplot2,我使用汽车数据来显示不同汽车在城市中行驶的里程数。对于 matplotlib,我使用了一些虚拟数据。

这是 matplotlib 做得不好的案例之一。在 matplotlib 中创建极坐标图是可能的,但是我不能使用我想要的数据框。

Matplotlib (python)极坐标条形图

Ggplot2 (R)极坐标条形图

极坐标条形图代码

如前所述,matplotlib 案例中的数据争论非常激烈。我认为有一种方法可以使用基于汽车的数据框和分组,但我认为您必须创建一个单独的数据框。我花了相当大的力气对数据进行王乐处理,以适应 matplotlib,但是做不到。

Matplotlib 代码段

使用 matplotlib 和 python 编写的极坐标条形图代码

Ggplot2 更加直观,只用一行代码就创建了极坐标图。它不需要事先和数据争论。

Ggplot2 代码片段

使用 ggplot 的极坐标条形图代码

另外,您可以使用 ggplot2 制作一些漂亮的极坐标图。Yan Holts 在他的网站上有一些很棒的例子,你可以点击这里查看。

Yan Holts 使用 ggplot2 的极坐标图示例

第 7 轮:多盒图

对于 ggplot2 的箱线图,我使用了之前用于极坐标图的汽车数据。对于 matplotlib,我使用了一个虚拟数据集。

这是 matplotlib 表现不如 ggplot2 的另一个例子。我无法让 matplotlib 使用 pandas 数据框创建多个箱线图。我应该提一下,在 matplotlib 中创建一个单独的 boxplot 不成问题。在 ggplot2 示例中,我将汽车制造商的 milage 分组,以生成多个箱线图。我认为在美学方面,两个包都表现得足够好。

Matplotlib (python)多盒图

Ggplot2(R)多盒图

以分类变量作为 x 轴的 Ggplot2 多盒图

多箱线图的代码

如前所述,与 matplotlib 一起工作的数据争论是最大的问题。我被迫使用虚拟数据来创建一个多箱线图的例子。

Matplotlib 代码段

使用 matplotlib 的 boxplot 代码,python

从下面的 R 代码可以看出,从 R 中的一个表创建多盒图很简单。

Ggplot2 代码片段

使用 ggplot 的箱线图代码,R

Python 中的多盒图

使用 matplotlib 和 jumpy 中的其他包在 python 中创建 boxplots 实际上是非常容易的。

熊猫方块图

Pandas 有一个箱线图函数,可以在一行代码中从数据帧创建多个箱线图。在这里,我使用了脂肪数据,并绘制了所有变量的值的范围。

使用熊猫的多个箱线图

Seaborn 箱线图

使用 python 中的 seaborn,我可以创建与 r 中的 ggplot2 相同的箱线图。这不需要任何数据争论,因为所有的分组都是由 seaborn 完成的。

使用 seaborn (python)的多个箱线图

使用 seaborn (python)为多个箱线图编码

第 8 轮:奖金

在奖励回合中,我试图展示每个包都做得很好但另一个包自己做不到的图表。

Ggplot2(R) Chloropeth 图

如果你想知道 python 非常擅长 choropleths,但是你需要像 leav 或 plotly 这样的包。Matplotlib 本身无法创建 choropleths。

对于下面的 choropleth,我使用了美国的逮捕信息来绘制每个州的袭击数量。抱歉,跳过阿拉斯加和夏威夷,包括他们缩小了可视化。

使用 ggplot2 (R)的美国 Choropleth

上面使用的配色方案非常类似于 matplotlib 中的“等离子体”主题,在 R 中,这是在 Viridis 函数的选项“C”中。默认的 Viridis 函数将为同伴渐变提供绿色。

使用 ggplot2(R)创建美国 choropleth 的代码

Matplotlib(python) 3d 表面图

在 python 中创建 3d 绘图的首选包是 plotly。Matplotlib 做了相当不错的工作,尽管创建 3d 网格需要更多的努力。

在这里,我使用了心理实验数据,在等高线图轮使用较早。Matplotlib 有很好的配色方案选择,并且很好地实现了颜色渐变。

使用 matplotlib(python)绘制 KDE 图

同一个图形的几种颜色变化

在下面的代码中,高斯核密度估计器占用了大量空间,它基本上是一个平滑函数。创建图表本身并不复杂。

使用 matplotlib 的 3d 图形代码(python)

结果

最后,请击鼓…

Ggplot2 (R)赢得这场可视化战斗!

这两个软件包都是强大的可视化工具。在一个比我更熟练的从业者手中,他们可以产生更好的结果。Matplotlib 可以创建漂亮的图形,并具有精美的演示风格。ggplot2 胜出的原因在于其数据处理能力。如果我允许自己也使用其他包,python 可能会赢。

Matplotlib vs. Plotly Express:哪一个是最好的数据可视化库?

原文:https://towardsdatascience.com/matplotlib-vs-plotly-express-which-one-is-the-best-library-for-data-visualization-7a96dbe3ff09?source=collection_archive---------4-----------------------

大蟒

相互比较时,哪一个库的性能更好?

Joshua Woroniecki 的图片。来源: Unsplash

数据可视化是任何项目中最关键的步骤之一。数据可视化能够将复杂的信息转化为易于理解的图表。您在项目中使用图表越多,您就越能更好地向非技术人员展示您的见解。

无论你是初学者还是有多年使用 Python 经验的人,都知道 Matplotlib 是最著名的数据可视化,但它是最好的吗?今天我们来对比一下 Matplotlib 和 Plotly Express。谁会是赢家?

人气

这是显而易见的,但是 Matplotlib 比 Plotly 更受欢迎。如此受欢迎的主要优势是,使用 Matplotlib 的笔记本将很容易被其他人复制,因为不同的人安装它的机会更高。然而,Plotly 一直在增长。它能赶上 Matplotlib 吗?在我看来,如果 Matplotlib 在未来几年内没有显著的改进,我相信会的。

Matplotlib 流行多年。来源: Snyk

文档

Plotly 的文档比 Matplotlib 领先好几年。很容易找到关于你试图创造的情节的信息。与此同时,Matplotlib 的网站令人困惑。如果你是初学者,你可能需要一段时间才能找到你要找的东西。

Plotly 网站

安装

安装这两个库同样容易。您可以使用pipconda来安装任何一个。这里不多说了。Python 的优势之一是安装库非常容易。安装它们应该没有任何问题。

创建可视化

我知道你们是来看这两个库之间的真实对比的,这就是我们将要做的。为此,我将使用你可以在 Kaggle 上找到的《2021 年世界幸福报告》。你可以在这里找到我用来创建这些可视化效果的笔记本。

**# Importing Libraries**
import opendatasets as od
import pandas as pd
import matplotlib.pyplot as plt
import plotly.express as px
import numpy as np**# Setting the dataset to the variable df**
df = pd.read_csv('world-happiness-report-2021/world-happiness-report-2021.csv')

作者图片

条形图

让我们从条形图开始,这是最流行的图表之一。条形图快速传达关系信息,因为条形图显示与特定类别相关的数字,这使得比较同一类别中的元素变得容易。对于这个图表,我将按地区比较幸福指数。为此,我将使用特性Regional indicatorLadder score

我创建了一个名为happiest_regions的新数据框架来简化事情。

happiest_regions = df.groupby(by=[‘Regional indicator’])[‘Ladder score’].mean().sort_values(ascending=False).reset_index()
happiest_regions = pd.DataFrame(happiest_regions)

情节直白

Plotly Express 表演得很漂亮。它能够用很少的代码提供一个漂亮的交互式图表。查看代码,我们可以看到创建图表所需的全部内容是数据框、x 轴、y 轴,我添加了颜色编码以更好地区分区域。我觉得很棒!

px.bar(happiest_regions, x='Regional indicator', y='Ladder score', color='Regional indicator')

作者图片

Matplotlib

Matplotlib 结果是…嗯,悲哀。我使用了与 Plotly 相同长度的代码,结果看起来很糟糕。不可能了解正在发生的事情。也就是说,需要相当多的代码才能让这个图看起来像样。出于这个原因,我将不得不作弊一点,并把 Seaborn 纳入游戏,使这种比较公平。

plt.bar(x = happiest_regions['Regional indicator'], height = happiest_regions['Ladder score'])
plt.show()

作者图片

开始了。我试图使用尽可能少的代码。我会说结果还可以。它需要更多的代码来使它更加专业,但是它的可读性很好。不过,对我来说还是不行。

**# Importing Seaborn**
import seaborn as sns
sns.set_theme(style="darkgrid")plt.figure(figsize=(14,6))
sns.barplot(x='Regional indicator', y='Ladder score', data=df, palette="tab10")
plt.xticks(rotation=45, fontsize=14)
plt.yticks(fontsize=14)
plt.show()

作者图片

柱状图

直方图的主要目的是查看数据是如何分布的,以及数据集中在哪里。对于直方图,我想看看幸福指数是怎么分布的。因此,我将使用Ladder score特性。

阴谋地

同样,普洛特利没有让人失望。它能够用 vert little 代码显示一个漂亮的交互式直方图,我们可以对区域进行颜色编码,这使得获得洞察力变得非常容易。例如,我们可以看到西欧和撒哈拉以南非洲在图表中的集中位置。

px.histogram(df, x='Ladder score', color='Regional indicator')

Matplotlib

Matplotlib 能够交付一个不错的结果。是的,它需要更多的代码,但我们可以很容易地从其他图表中回收代码。没有太多可谈的。与 Plotly 相比,它似乎太简单了。它不是交互式的,但是它完成了工作。

plt.figure(figsize=(14,6))
sns.histplot(data=df, x='Ladder score', hue='Regional indicator')
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.show()

散点图

散点图用于查看两个类别之间是否存在相关性。这是机器学习中必不可少的一步,分析一个特征如何影响另一个特征。

情节表达

我们在第三个图表中,我们可以看到 Plotly Express 在不同类型的图表中表现一致。从一种类型转换到另一种类型非常容易,并且我在测试时不需要处理任何错误。很容易理解代码和需要修改的内容。他们的网站很清晰,你可以很容易地找到关于图形的信息。我喜欢这种互动,我们可以选择你能看到的数据。

px.scatter(df, x='Ladder score', y='Logged GDP per capita', color='Regional indicator')

Matplotlib

Matplotlib 也不是超级落后。它完成了工作,一旦你有了代码,你就可以像 Plotly Express 一样容易地在不同类型的图形之间转换。不,它不是交互式的(没有额外的库)。是的,它完成了散点图的预期工作。

plt.figure(figsize=(14,6))
sns.scatterplot(data=df, x='Ladder score', y='Logged GDP per capita', hue='Regional indicator')
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.show()

折线图

折线图便于查看特定类别的趋势。例如,我们可以看到这些年来Ladder score是如何变化的。国家变得更幸福了吗?这是了解你的类别进展情况的基本图表。对于折线图,我将使用历年的世界幸福报告。

阴谋地表达

在这一点上,我们可以期待 Plotly Express 的表现会很好,对吗?是的,它提供了一个漂亮的图表,但我想重点介绍两个令人兴奋的功能。第一个是你可以滚动看到所有的国家。它仍然是一个优秀的功能。另一件很酷的事情是,如果你开始取消选中多个国家,在这种情况下,Plotly 足够聪明,可以取消选中所有国家。一个不错的细节。

px.line(df2, x='year', y='Life Ladder', color='Country name')

作者图片

Matplotlib

Matplotlib 在线图方面做得很好。它有两种看法。第一个不包括颜色代码。Matplotlib 将创造一种趋势,包括所有国家。这可能是平均值吗?这是 Matplotlib 的文档对线图的描述。

用几个语义分组的可能性画一个线图。

不清楚我们应该期待什么。不管怎样,图表看起来不错。如果它显示了一个世界范围的趋势,我们可以看到阶梯指数在 2006 年显著下降。那年出现了经济衰退,并在 2008 年加剧。也许这就是我们看到这一趋势的原因。

plt.figure(figsize=(14,6))
sns.lineplot(data=df2, x='year', y='Life Ladder')
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.show()

然而,我们可以对这些线进行颜色编码,并查看每个代码的单独的线。它完成了任务。还是那句话,它是交互的,但是它是有效的,而且它看起来…嗯,好的。

plt.figure(figsize=(14,6))
sns.lineplot(data=first_10_countries, x='year', y='Life Ladder', hue='Country name')
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.show()

样式选项

您可能已经注意到,没有一个图表有标题,它们需要一些工作来看起来更专业。如果我们不做一些调整,Matplotlib 会产生可怕的结果。幸运的是,我们有 Seaborn,这使得最初的结果更好。您可以从数据集中获得您需要的见解,但是它们对于专业环境来说是不可展示的。好消息是,您可以插入将用于 Seaborn 和 Matplotlib 的代码。在我在这篇博客中使用的例子中,我使用 Seaborn 创建了图表,并使用 Matplotlib 进行了一些调整。

我觉得 Matplotlib 的文档不好。很混乱,很难找到自己需要的。然而,编辑情节很简单,代码也很容易记住。 这个博客 是由Preeya Sawadmanod 写的,他做了一个很棒的工作,解释了如何创建和编辑 Matplotlib 图。

资料来源: Preeya Sawadmanod

上面,我们可以看到,我们可以通过增加几行代码来改进图表。初学者编辑 Matplotlib 的图形应该没有问题

plt.figure(figsize=(14,6))
sns.lineplot(data=first_10_countries, x='year', y='Life Ladder', hue='Country name')
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.show()

作者提供的图像

另一方面,Plotly 提供了现成的数据可视化。它的图表很漂亮,只需要很少的样式就能看起来很专业。它是交互式的,所以你可以实时编辑类别的选择!还可以编辑您正在使用的字体、颜色和图表动画。它赢了 Matplotlib 和 Seaborn 很多。

plt.figure(figsize=(14,6))
sns.histplot(data=df, x='Ladder score', hue='Regional indicator', palette='husl')
plt.title('Ladder Score Distribution', fontsize=18)
plt.xlabel('Ladder Score', fontsize=16)
plt.ylabel('Count', fontsize=16)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.show()

作者图片

结论

在分析这两个优秀的库时,需要考虑的东西太多了。然而,这篇博客的目的是决定哪一个是赢家,我不怀疑与 Matplotlib 相比,Plotly Express 无愧于最佳数据可视化库的称号。并不是说 Matplotlib 很可怕,只是需要更多的代码,缺少额外的功能。与此同时,Plotly Express 用很少的代码交付了漂亮的结果。创建图表很容易使用和记住代码。

如果你是初学者,你应该学习 Matplotlib 和 Seaborn,因为它们更受欢迎。然而,当你准备好了,Plotly Express 应该是你清单上的下一个。如果你是一个高级 Python 用户,你应该试试 Plotly Express 和它所有的高级功能。

感谢阅读。让我知道你对这两个图书馆的看法。

如果有任何问题,请在我的 Linkedin 上联系我:【http://bit.ly/linkedin-ismael】

多维时间序列预测的矩阵自回归模型

原文:https://towardsdatascience.com/matrix-autoregressive-model-for-multidimensional-time-series-forecasting-6a4d7dce5143?source=collection_archive---------6-----------------------

用 Numpy 实现一个简单的 Python

自回归(AR)过程是时间序列分析中具有代表性的经典模型,在自然、科学和经济中有着广泛的应用。AR 可以描述单变量时间序列中具有线性关系的某些时变过程。AR 的另一个重要对应物是向量自回归(VAR)模型,它可以描述多元时间序列的协同进化模式。在这篇博文中,我们将

  • 引入矩阵自回归(MAR)模型对多维时间序列数据建模,
  • 讨论优化方案,用 Python 中的 Numpy 重现 MAR 模型。

风险值简介

对于多元时间序列,如果变量是相依的,那么 VAR 可以探索这些变量的协同进化模式。

为了估计系数矩阵,我们可以利用 L2 范数建立一个最小二乘问题。

编写重现 VAR 模型的 Python 代码并不困难。

import numpy as npdef var(X, pred_step):
    N, T = X.shape
    temp1 = np.zeros((N, N))
    temp2 = np.zeros((N, N))
    for t in range(1, T):
        temp1 += np.outer(X[:, t], X[:, t - 1])
        temp2 += np.outer(X[:, t - 1], X[:, t - 1])
    A = temp1 @ np.linalg.inv(temp2)
    mat = np.append(X, np.zeros((N, pred_step)), axis = 1)
    for s in range(pred_step):
        mat[:, T + s] = A @ mat[:, T + s - 1]
    return mat[:, - pred_step :]

我们可以通过一个玩具例子来评估 VAR 代码。

在此处编写 Python 代码:

import numpy as npX = np.zeros((2, 10))
X[0, :] = np.arange(1, 11)
X[1, :] = np.arange(2, 12)
pred_step = 2
mat_hat = var(X, pred_step)
print(mat_hat)

运行代码,预期的输出是

[[11\. 12.]
 [12\. 13.]]

可以看出,这些预测与地面真实数据完全相同。

什么是多维时间序列和 MAR?

在现实世界中,多维时间序列数据非常普遍。例如,在金融中,我们可以将一些国家的季节性经济指数汇总为一个矩阵,就像这样:

在这个矩阵中,如果我们考虑时间信息,那么我们就会得到一个“张量”:

直观上,张量给出如下:

在每个时间 t 中,我们有矩阵形式的观察,而不是向量形式的观察。最近,等(2021)提出了一种新的矩阵自回归( MAR )框架来构建这种多维时间序列数据。他们考虑了这样的双线性结构:

其中矩阵 A 的大小为 m 乘 m,而矩阵 B 的大小为 n 乘 n。

该框架对于构建多维时间序列数据有许多优势:

  • 该框架可以保持矩阵形式的原始数据表示。
  • 该框架可以减少自回归模型中的参数数量。例如,如果我们使用 VAR 来研究这样的数据,我们将在系数矩阵中有( mn )个参数。但是使用 MAR,我们只有 m + n 。这可以避免 VAR 在处理高维数据时的过度参数化问题。

如何获得系数矩阵的闭式解?

如上所述,我们可以使用双线性结构来构建 MAR 模型。一个问题是如何估计系数矩阵。第一个冲动是把优化问题写成一个损失函数:

在这里我们使用弗罗贝纽斯范数。

系数矩阵的闭式解

如果我们在这里将目标定义为一个函数:

然后,我们有

通过让导数为 0 ,不难得到T5A的闭合解。 的闭合解由下式给出

系数矩阵 B 的封闭解

如果我们将目标改写为:

然后,我们有

设导数为 0B 的闭合解由下式给出

交替最小二乘法与 Python 实现

如上所述,我们有两个封闭形式的解决方案。既然解到 A 涉及 B ,又解到 B 涉及 A 。我们可以用一个经典的算法来解决这个问题,那就是交替最小二乘(ALS)算法。该算法的基本思想是以迭代的方式更新每个变量的最小二乘解。例如,我们的案例变成了

其中 l 表示迭代次数。

通过这些等式,我们可以用 ALS 算法在这里定义一个 Python 函数:

*import numpy as npdef mar(X, pred_step, maxiter = 100):
    m, n, T = X.shape
    B = np.random.randn(n, n)
    for it in range(maxiter):
        temp0 = B.T @ B
        temp1 = np.zeros((m, m))
        temp2 = np.zeros((m, m))
        for t in range(1, T):
            temp1 += X[:, :, t] @ B @ X[:, :, t - 1].T
            temp2 += X[:, :, t - 1] @ temp0 @ X[:, :, t - 1].T
        A = temp1 @ np.linalg.inv(temp2)
        temp0 = A.T @ A
        temp1 = np.zeros((n, n))
        temp2 = np.zeros((n, n))
        for t in range(1, T):
            temp1 += X[:, :, t].T @ A @ X[:, :, t - 1]
            temp2 += X[:, :, t - 1].T @ temp0 @ X[:, :, t - 1]
        B = temp1 @ np.linalg.inv(temp2)
    tensor = np.append(X, np.zeros((m, n, pred_step)), axis = 2)
    for s in range(pred_step):
        tensor[:, :, T + s] = A @ tensor[:, :, T + s - 1] @ B.T
    return tensor[:, :, - pred_step :]*

我们建立了一个简单的例子来测试算法。预测任务如下:

在此处编写 Python 代码:

*import numpy as npX = np.zeros((2, 2, 10))
X[0, 0, :] = np.arange(1, 11)
X[0, 1, :] = np.arange(2, 12)
X[1, 0, :] = np.arange(3, 13)
X[1, 1, :] = np.arange(4, 14)pred_step = 2
tensor_hat = mar(X, pred_step)
print(tensor_hat[:, :, 0])
print()
print(tensor_hat[:, :, 1])*

运行代码,预期的输出是

*[[11\. 12.]
 [13\. 14.]]

[[12\. 13.]
 [14\. 15.]]*

可以看出,这些预测与地面真实数据完全相同。

参考

陈蓉,晓寒,丹阳。矩阵值时间序列的自回归模型。计量经济学杂志,2021 年。

这是一个关于火星模型的简单故事。如果你对这篇博文感兴趣,我们强烈推荐你复制这些代码,这并不难理解。

数据科学家的矩阵演算

原文:https://towardsdatascience.com/matrix-calculus-for-data-scientists-6f0990b9c222?source=collection_archive---------5-----------------------

吃红色药丸,学习矩阵微积分!

马库斯·斯皮斯克在 Unsplash 上的照片

序言

你吃了蓝色药丸……故事结束,你在床上醒来,相信你想相信的一切。你服用红色药丸…你待在仙境,我让你看看兔子洞有多深。

这是《黑客帝国》三部曲中墨菲斯对尼奥说的名言。你必须做出同样的选择,你想继续使用 pytorch 和 tensorflow 这样的签名框架而不知道它们是做什么的吗?或者你想更深入地挖掘矩阵运算的世界,理解像反向传播如何工作这样的事情?

线性代数基础

照片由乌列尔·索伯兰斯在 Unsplash 上拍摄

向量和矩阵

我用一个小而细的字母写标量(单个数字)

(图片由作者提供)

而向量将由小粗体字母表示。默认情况下,它们是列向量。

(图片由作者提供)

行向量也用小粗体字母表示,但是它们有一个 T 上标。T 上标代表转置。

(图片由作者提供)

代表矩阵的符号将是粗体大写字母。

(图片由作者提供)

我们也可以转置一个矩阵。执行此操作时,第一列成为第一行,反之亦然。

(图片由作者提供)

向量或矩阵的维数是一个元组:

(行数、列数)

让我们考虑以下情况:

(图片由作者提供)

点积

向量和矩阵也定义了点积。但是顺序很重要,并且左向量/矩阵的列数必须与右向量/矩阵的行数相匹配。

(图片由作者提供)

结果的维数是:(左侧输入的行数,右侧输入的列数)

(图片由作者提供)

如果你感兴趣,你可以在这里更详细地看到点积是如何执行的。

(图片由作者提供)

为了得到输出的一个元素,我们将向量/矩阵的左边的一行和右边的一列相乘并求和。

点积的重要性在于它可以在许多不同的环境中使用。在力学中,它可以用来表示物体的旋转和拉伸。它也可以用来改变坐标系。

分析基础

照片由 SOON SANTOS 在 Unsplash

和的导数

当我们想对一个和求导时,就相当于对每个加数求导。

(图片由作者提供)

乘积规则

如果我们想对两个函数的乘积求导,这两个函数都取决于我们要求微分的变量,我们可以使用以下规则:

(图片由作者提供)

让我们考虑下面的例子:

(图片由作者提供)

那么𝑦对𝑥的导数就是:

(图片由作者提供)

链式法则

我们想对函数𝑦.进行微分这个功能取决于𝑢,𝑢取决于𝑥.然后我们可以应用链式法则。

(图片由作者提供)

这里简单举个例子。

(图片由作者提供)

矩阵计算

在把这些东西下载到你的大脑之后,我希望你已经准备好学习矩阵微积分了!

有两种我们可以写矩阵演算的惯例,所谓的“分子布局和“分母布局”。在这篇博文中,我将使用分子布局。

照片由查理恩·细川玉子在 Unsplash 上拍摄

向量导数标量

标量值函数相对于变量向量的导数是一个行向量。这个行向量对于我们想要区分的每个变量都有一列。

(图片由作者提供)

这些点只是表明,我们不一定要用 3 个变量来区分,而是一个变量。

我们来举个例子:

(图片由作者提供)

然后我们可以应用“和的导数”规则:

(图片由作者提供)

因为一个变量对另一个变量的导数是 0,我们得到:

(图片由作者提供)

标量导数向量

如果我们有一个𝑓函数,它输出一个向量,想要对一个变量求导,我们得到一个列向量作为结果。

(图片由作者提供)

让我们考虑下面这个我们想要求导的向量值函数。

(图片由作者提供)

逐矢量导数

当一个向量值函数对一个变量向量求导时,我们得到一个矩阵。我使用一个有 2 个输出值和 3 个输入变量的函数作为例子。但是您可以使用任意数量的输出值和输入变量。

(图片由作者提供)

你可以把它想象成“标量乘向量”和“向量乘标量导数”的组合。我们沿着行改变函数输出的元素,沿着列改变变量的元素。

(图片由作者提供)

得到的矩阵具有维度:(输出大小,输入大小)

现在我们快速看一个例子。

(图片由作者提供)

摘要

(图片由作者提供)

向量链规则

让我们考虑下面的场景。矢量 𝑓 是矢量 𝑔 的函数。向量 𝑔 本身就是向量 𝑥 的函数。所以𝑓(𝑔(𝑥))相对于 𝑥 的导数计算如下:

(图片由作者提供)

我们可以看到,矢量链规则看起来和标量链规则几乎一样。点积仍保留在公式中,我们必须构建“逐向量”的导数矩阵。

这里有一个小例子:

(图片由作者提供)

我们计算偏导数。

(图片由作者提供)

现在我们扩展点积。

(图片由作者提供)

最后一次简化,我们得到了结果。

(图片由作者提供)

常数的点积

我们可以像对待标量常数一样对待常数矩阵和向量。允许将它们分解出来。但是请记住,顺序对于两个非标量之间的点积很重要。

(图片由作者提供)

我们现在可以分解出矩阵 **𝑋,**如果它是常数并且不依赖于 𝑤

(图片由作者提供)

知道我们通过向量导数矩阵来构造向量。

(图片由作者提供)

对角线上为 1,其他地方为 0 的矩阵称为单位矩阵。乘以它的一切都保持不变。因此我们得出最后的结果:

(图片由作者提供)

你可以得到同样的结果,如果你自己做点积,而不是依赖我告诉你单位矩阵。

有趣的是,虽然整个方程包含向量和矩阵,但我们得到了基于标量微积分的相同结果。然而,情况并非总是如此,因为并非标量微积分的所有规则也适用于多元微积分。举一个例子来说,没有矢量积规则。

收场白

我很高兴你服用了红色药丸,和我一起踏上了深入矩阵微积分领域的旅程。如果这对你来说是新的,那么你应该多加练习,自己看几个例子。我希望你喜欢这篇博文,并祝你一切顺利。

文森特·米勒

作者的相关文章

作者的其他文章

想联系支持我?

领英
https://www.linkedin.com/in/vincent-m%C3%BCller-6b3542214/
脸书
https://www.facebook.com/profile.php?id=100072095823739
推特
https://twitter.com/Vincent02770108
中等
https://medium.com/@Vincent.Mueller
成为中等会员并支持我(你的部分会员费直接归我)
https://medium.com/@Vincent.Mueller/membership

推荐系统中的矩阵分解

原文:https://towardsdatascience.com/matrix-factorization-in-recommender-systems-3d3a18009881?source=collection_archive---------30-----------------------

推荐系统中矩阵分解技术的温和介绍,包括FunkSVD、SVD++和非负矩阵分解

你是说这个矩阵?(图片来源: Unsplash )

介绍

RS 就是把物品和用户匹配起来。起点是一个用户项目矩阵,其中填充了表示显式反馈(用户提供的评级)或隐式反馈(点击计数、访问次数、观看时间等)的值。).你也可以把这个问题作为一个矩阵填充问题:给定用户条目矩阵中的已知条目,在给定各种约束的情况下,如何填充缺失的条目?

水果评级矩阵示例(图片由作者提供)

协同过滤方法在这个领域取得了巨大的成功。两种最突出的方法是基于模型和基于邻域的方法。对于基于模型的方法,我们主要指潜在因素模型,其中使用了 MF 技术。

两种方法都利用了数据矩阵中的相关性。例如,在基于用户的邻域方法中,利用用户方式的相关性来形成对等组,从而完成矩阵中缺失的评级,而基于项目的邻域方法利用项目方式的相关性。**潜在因素模型将用户和商品一次性转化到同一个潜在空间。**因此,它们同时利用用户和项目的相关性,因此通常更有效。

放克奇异值分解

芬克-奇异值分解被认为是西蒙·芬克在网飞奖竞赛中创造的。:严格来说,名称中的“SVD”并不正确。线性代数中的奇异值分解与这里所做的矩阵分解有很大不同,看看为什么会混淆。

它将用户项矩阵分解为两个较低秩矩阵的乘积,如下所示:

其中 R 是由 m 行和 n 列组成的近似评分矩阵, U 是一个用户因素矩阵,它为每个用户有一行(称为用户因素 uᵢ), Vᵀ是一个项目因素矩阵,它为每个项目有一列(称为项目因素 vⱼ)

因此,假设潜在维度为 k ,R 中的任何一个等级都近似为点积:

注意,U 的维数是(m,k),而 V 的维数是(n,k)。

如何确定 U 和 V?我们可以像许多机器学习问题一样,将其作为优化问题来解决(包括正则化以避免过度拟合,或者 U 和 V 中的极值)。

SGD 可以通过计算 J 分别相对于用户因子 uᵢ和项目因子 vⱼ的导数,以标准方式解决这个问题。

SVD++

SVD++是 Funk-SVD 的扩展,加入了隐式反馈数据

隐式反馈是我们可以用来推断用户对某些项目的偏好的任何辅助信息,例如点击、访问、在电视节目上花了多长时间等等。即使在显式评级矩阵的情况下,评级矩阵的身份也可以被视为隐式反馈。换句话说,简单来说,用户选择给一个项目评分的行为,无论是高还是低,都带有强烈的预测信号。

现在,让我们看看如何从显式评级矩阵 R 中导出隐式反馈矩阵 F:

其中,如果存在评级而不考虑其值,则我们简单地指定 1,否则指定 0。然后,我们将这些恒等式归一化,使每一行的 L2 范数为 1。

然而,值得注意的是,隐式反馈矩阵可以是任何种类的一元矩阵(一元是指它只包含 0 和正数),例如,用户点击亚马逊上某个商品的次数。

现在,重构的 R 矩阵由下式给出:

其包含由隐式矩阵 F 引入的附加分量,以及伴随的隐式项目因子矩阵 Y,其需要由 SGD 从数据中学习。

注意,F 与(m,n)的 R 具有相同的维数,而 Y 是(n,k),与显式项目因子矩阵 V 具有相同的维数。

直观地,隐式项目因子矩阵 Y 编码了用户对某些类型的偏好,这种偏好是从关心对某个项目 j 进行评级的行为中推断出来的

学习 U,V 和 Y 的方法与 Funk-SVD 相同,只是现在我们有更多的参数,一个额外的 Y 矩阵要学习。

非负矩阵分解

NMF 类似于 Funk-SVD,只是我们现在对 U > 0 和 V > 0 有额外的约束,这要求用户因子和项目因子矩阵中的所有元素总是正的。

正如我们所看到的,它与 Funk-SVD 相同,除了加法约束。因此,它也可以用同样的方式使用优化技术来解决。

这种方法的主要优势是**可解释性。**因为现在,我们可以很容易地将 U 和 V 中的值解释为对某些潜在因素的偏好。在电影的情况下,用户因素可以被理解为对“动作”、“爱情”偏好的组合,而项目因素也可以被视为不同类型的加权组合。

结束语

潜在因素模型对于将用户和项目编码到潜在空间非常有效。正如 Yehuda Koren 在他的论文[1]中指出的:

邻居模型在检测非常本地化的关系方面最为有效。他们依赖于一些重要的邻居关系,经常忽略用户的绝大多数评级。因此,这些方法不能捕获所有用户评级中包含的全部微弱信号。潜在因素模型通常在估计同时与大多数或所有项目相关的整体结构方面有效。然而,这些模型在检测一小组密切相关的项目之间的强关联方面表现不佳,而这恰恰是邻域模型表现最好的地方。

在许多情况下,通过结合两个世界的优点,最佳点在两者之间。

参考

[1] 因式分解遇到邻域:一个多方面的协作过滤模型,Yehuda Koren,2008。

如何最大化代码的效率

原文:https://towardsdatascience.com/maximise-your-codes-efficiency-cc7e8a4e93b4?source=collection_archive---------30-----------------------

识别代码开销的简单方法

照片由通讯社跟随于 Unsplash

高效的代码通常是 Pythonic 代码。换句话说,它遵循了蒂姆·彼得斯在《Python 之禅》中概述的准则,通过在笔记本的一个单元格中运行import this即可访问:

漂亮总比难看好。
显性比隐性好。
简单总比复杂好。
复杂总比复杂好。
扁平比嵌套好。
稀不如密。可读性很重要。
特例不会特殊到违反规则。
虽然实用性胜过纯粹性。错误永远不会无声无息地过去。
除非明确消音。
面对暧昧,拒绝猜测的诱惑。应该有一种——最好只有一种——显而易见的方法来做这件事。除非你是荷兰人,否则这种方式一开始可能并不明显。现在总比没有好。
虽然永远也不会比现在*好。如果实现很难解释,这是个坏主意。
如果实现容易解释,可能是个好主意。名称空间是一个非常棒的想法——让我们多做一些吧!

这些理念应该成为所有优秀代码的支柱,但是很容易陷入编写代码的坏习惯,特别是如果它是一个大型项目的一部分。幸运的是,有几种方法可以分析运行代码时消耗的时间和内存,以尽可能减少开销。

定时码

时间

对于逐行计时(或逐单元计时),我们可以对行使用神奇命令%timeit,对单元使用%%timeit。例如,要对 print 语句的执行进行计时,我们可以编写:

%timeit print("Hello World!")***Output***
10000 loops, best of 5: 209 µs per loop

我们可以向命令添加参数:

  • -r指定运行次数
  • -n指定循环次数
  • -o允许将输出存储到变量中

如果我们想运行 print 语句 2 次,每次 10 个循环,并将输出存储到一个变量中,我们可以写:

timedPrint = %timeit -o -r2 -n10 print("Hello World!")***Output***
10 loops, best of 2: 26.3 µs per loop

%timeit优于%time,因为它多次执行代码以避免异常的影响。

我们可以使用以下方法访问函数的返回结果:

  • .all_runs:返回游程值的数组
  • .best:返回最佳时间
  • .worst:返回最差时间
  • .compile_time:返回运行代码所花费的时间
  • .loops:返回循环次数(如 10000000)
  • .repeat:返回重复次数(如 5)

我们可以用这个来比较给出相同输出的不同方法的最佳时间,例如创建一个列表。

timeCommand = %timeit -o list1 = list()***Output***
10000000 loops, best of 5: 98.2 ns per loop--------------------------------------------------------------------timeBrackets = %timeit -o list2 = []***Output***
10000000 loops, best of 5: 31 ns per loop--------------------------------------------------------------------diff = (timeBrackets.best - timeCommand.best) * (10**9)
print(f"Using [] is faster than list() by {diff:.2f} ns")***Output***
Using [] is faster than list() by 67.20 ns

线条轮廓图

发现代码开销的一个更有效的方法是使用line_profiler包,我们需要在使用它之前安装它:

pip install line_profiler--------------------------------------------------------------------conda install -c conda-forge line_profiler # If using Anaconda

然后,我们可以使用神奇的命令%load_ext将包加载到笔记本中:

%load_ext line_profiler

line_profiler使用简单,但只能用于功能。为了这个笔记本的目的,我写了一个快速函数,它接受一个以米为单位的数组,并以三种不同的方式将这个数组转换成 km:

def convert_units(arr_len): lengthsKM_fl = []
    for i in range(len(arr_len)):
        lengthsKM_fl.append(arr_len[i]/1000) lengthsKM_lc = [arr_len[i]/1000 for i in range(len(arr_len))] lengthsKM_np = arr_len/1000return lengthsKM_fl, lengthsKM_lc, lengthsKM_np

我们可以使用line_profiler魔法命令%lprun来分析每一行执行所花费的时间。它的语法如下:

%lprun -f funcName funcName(arguments)

为了在convert_units函数中使用它,我们首先需要定义长度数组len_metres = np.arange(1, 21, 1,然后运行%lprun -f convert_units convert_units(len_metres)。它输出一个有用的函数细分,包括一行运行的次数,以及一行运行的总时间的百分比:

这表明,使用列表理解是将 20 个数字的列表除以 1000 的最快方法。对一个包含 200000 个数字的数组再次运行这个程序,结果表明,使用 NumPy 对大型数组来说是最有效的,用 200000 个数字除 20 个数字所用的时间大致相同。

内存使用

与计时代码一样,有几种不同的方法来分析 Python 代码的内存使用情况。我们要介绍的第一个来自sys模块。

sys.getsizeof

由于这是一个外部模块,我们首先必须在一个单元中运行import sys。我们现在可以使用函数getsizeof来确定内存变量的占用量(以字节为单位),例如:

pyList = [*range(1000)]
sys.getsizeof(pyList)***Output***
9120--------------------------------------------------------------------numpyArr = np.array(range(1000))
sys.getsizeof(numpyArr)***Output***
8096

虽然这对于列表和 ndarrays 之间的一次性比较很有用,但像这样检查整个代码会非常慢,这正是 memory_profiler 包派上用场的地方。

内存分析器

正如我们使用line_profiler一样,我们必须下载并加载memory_profiler才能使用它:

pip install memory_profiler--------------------------------------------------------------------conda install -c conda-forge memory_profiler # If using Anaconda

正在加载…

%load_ext memory_profiler

memory_profiler有两个功能,%memit%mprun。第一个是%memit,用于快速获取函数占用的内存量,例如:

len_metres = np.arange(1, 200000, 1)
%memit convert_units(len_metres)***Output***
peak memory: 175.52 MiB, increment: 3.04 MiB

其中peak memory是执行该功能时使用最多的内存,increment = peak memorystarting memory

我增加了len_metres的大小,以便更有效地显示内存的使用情况,因为它是以兆字节(1024 字节)来度量的。如果我不这样做,下一部分的大部分将显示 0.0 MiB 的内存使用量。

如果我们想要更深入的分解,类似于%lprun,我们必须使用%mprun来代替。不幸的是,这比使用%lprun稍微困难一些,因为%mprun只能用于导入笔记本的功能。为了说明这一点,我将之前的convert_units函数放入名为memoryProfiler_convertUnits.py的文件中,并将其导入到我的笔记本中:

from memoryProfiler_convertUnits import convert_units%mprun -f convert_units convert_units(len_metres)

这输出

这再次表明 NumPy 是存储变量最有效的方式(因为数组只能包含一种数据类型,所以 Python 不必不断地检查——这是动态类型语言的缺点之一)。

结论

有时候,没有比你已经写好的更有效的方法了,但是经常是一小部分代码产生了瓶颈( Pareto 原则),所以当涉及到更复杂的任务时(比如计算一个序列的第 1,000,000 项),小的改变可以产生巨大的差异。

感谢阅读!

来源:

  1. Python 之禅:https://www.python.org/dev/peps/pep-0008/

用 Python 实现容器装载优化

原文:https://towardsdatascience.com/maximize-the-loading-capacity-of-a-sea-container-to-reduce-your-shipping-costs-with-python-8cc02c9725a7?source=collection_archive---------5-----------------------

我们如何使用启发式算法找到正确的策略,在海运集装箱中装载最大数量的托盘?

2D 背包问题在托盘装载中的应用—(图片由作者提供)

由于集装箱短缺,最近航运价格飙升,从上海到北欧的集装箱价格从 11 月份的 2000 美元涨到了 12000 美元的峰值,优化集装箱装载成为当务之急。

场景
你是一家国际时尚服装零售商的物流经理,你想将 200 个集装箱从洋山港(中国上海)运往勒阿弗尔港(法国勒阿弗尔)。

  • **零售价(美元):**你的货物零售价是每个集装箱 225,000 美元
  • 利润率(%) :基于危机前的运输成本,你的利润率为 8.5%
  • **运费—以前(%):**100 x 2000/225000 =0.88(%)
  • 运输成本—当前(%): 100 x 12,000 / 225,000 = 5.33 (%)

您的财务团队给物流运营带来了巨大的压力,因为运输成本导致 4.45 %的利润损失

由于你对市场价格的影响力有限,你唯一的解决办法就是提高装载量以节省空间。

💌新文章直接免费放入你的收件箱:时事通讯

如果你喜欢看,看看这篇文章的视频版本

一、如何优化集装箱装载?

您已经收到来自中国工厂和供应商的托盘,准备运往法国。

您有两种类型的托盘:

  • **欧式托盘:**尺寸 80 (cm) x 120 (cm)

欧洲托盘的例子-(来源:洛托姆)

  • **北美货盘:**尺寸 100(厘米)x 120(厘米)

北美托盘示例—(来源: Chep )

您可以使用两种类型的容器

  • **干燥容器 20:内部长度(**5.9 米)、内部宽度(2.35 米)、内部高度(2.39 米)
  • **干燥容器 40:内长(**12.03 米),内宽(2.35 米),内高(2.39 米)

约束

  • 欧式托盘和美式托盘可以混装
  • 有 20 英尺或 40 英尺的集装箱可供选择
  • 无托盘堆叠(将一个托盘放在另一个托盘上方)
  • 装载策略必须在现实生活中执行(使用平衡重卡车)

**目标:**每个集装箱装载最大数量的托盘

https://samirsaci.com

二。二维背包问题在托盘装载中的应用

1。二维背包问题

给定一组矩形块和一个矩形容器,二维背包问题 (2D-KP) 包括在容器内正交包装块的子集,使得包装块的值的总和最大化。

二维断头台背包的精确算法(穆罕默德·多拉塔巴迪亚,安德里亚·罗提,米歇尔·莫纳奇)——(链接

2。让它适应我们的问题

如果我们考虑这一点

  • 托盘不能堆叠
  • 托盘必须正交包装,以符合装载限制
  • 托盘高度总是低于集装箱的内部高度

我们可以将我们的 3D 问题转化为 2D 背包问题,并直接应用该算法来寻找最优解。

3。结果

场景:你需要装入一个 40 英尺的集装箱

  • 20 欧洲托盘 80 x 120 (cm)
  • 4 北美货盘 100 x 120 (cm)

暂定 1:直观解法

初始解决方案—(图片由作者提供)

备注:您的叉车司机试图安装最多数量的欧洲货盘,并为 4 个北美货盘找到一些空间。

结果: 20/20 欧洲托盘装载, 2/4 美国托盘装载。你需要另外一个集装箱来装剩下的两个货盘。

暂定 2:优化算法结果

优化解决方案(左)|初始解决方案(右)——(图片由作者提供)

注释:左边是基于算法输出的解决方案。

结果: 20/20 欧洲托盘装载, 4/4 美国托盘装载。你不需要另一个容器。

结论

  • 优化的解决方案可以适合 100%的托盘。它基于非直观的放置,不尝试许多组合是找不到的。
  • 我们的填充率提高了,托盘也更“包装”了。

在下一部分中,我们将看到如何实现一个模型来获得这个解决方案。

编辑:你可以在下面的链接中找到这篇文章的 Youtube 版本和动画。

三。构建您的模型

为了保持本文简洁,我们不会从头开始构建算法,而是使用 python 库 rectpack

rectpact 库的结果示例—(来源:文档)

你可以在这个 Github 资源库中找到完整的代码:链接。
我的投资组合与其他项目: 萨奇

  1. 初始化模型并设置参数
  • bx,by:我们在 x 轴和 y 轴上增加了 5 cm 的缓冲,以确保不会损坏托盘
  • bins20,bins40: 按类型划分的集装箱尺寸

2。构建您的优化模型

  • **箱:可用集装箱列表(例如,箱= [bin20,bin40]表示您有 1 个集装箱 20' et 1 个集装箱 40')
  • all_rects: 所有矩形的列表,这些矩形可以包含在面元中,并且它们的坐标可以被绘制
  • 可以装入箱柜中所列集装箱的托盘清单

3。绘制您的结果

  • 颜色:黑色为 80x120,红色为 100 x120

20 个欧洲货盘和 4 个北美货盘的输出示例—(图片由作者提供)

现在,您可以与叉车司机分享您的装载计划了:)

三。结论和后续步骤

关注我的 medium,了解更多与供应链数据科学相关的见解。

与直观方法相比,我们在两个示例中都提高了托盘装载率。
该解决方案基于无法堆叠的托盘的简单场景。

问题

  • 如果我们将其应用于可堆叠托盘,会有什么结果?
  • 如果我们把它应用到散装纸箱上会有什么结果?

超出

将这种优化应用到您过去的运输中,会对您运输过程中的二氧化碳排放产生什么影响?

https://www.samirsaci.com/supply-chain-sustainability-reporting-with-python/

应用

一位读者决定将这个原型带到另一个层次,并使用 3D 界面进行部署。

(图片由尼恩克·皮特斯提供)

Nienke Pieters 使用本文中分享的代码作为基础来构建一个应用程序,提供这个令人印象深刻的 3D 界面。

要了解更多信息,您可以查看 GitHub 资源库: Nienke Pieters

关于我

让我们在 Linkedin 和 Twitter 上连线,我是一名供应链工程师,正在使用数据分析来改善物流运作和降低成本。

如果你对数据分析和供应链感兴趣,可以看看我的网站

https://samirsaci.com

参考

[1] Python 2D 矩形打包库(rectpack),Github 文档,链接

使用 Python 最大化您的业务盈利能力

原文:https://towardsdatascience.com/maximize-your-business-profitability-with-python-fbefebbdf802?source=collection_archive---------15-----------------------

使用线性规划来帮助您当地的面包店通过选择合适的商品来提高其盈利能力

面包店盈利问题—(图片由作者提供)

如果你读过我以前的文章,你会注意到我的主要关注点始终是使用线性编程、机器学习或统计来降低运营成本。

作为第三方物流的供应链解决方案设计师,我的任务一直是降低成本,为我们的客户保持有竞争力的价格。

然而,这些工具和概念也可以用于通过将生产集中在高利润产品上来最大化供应链的利润。

在本文中,我将向您展示如何通过使用 Python 的线性编程来生产正确的产品,从而帮助您当地的面包店实现利润最大化。

💌新文章直接免费放入您的收件箱:时事通讯

如果你喜欢,你可以看看视频版本

一、如何用 python 优化自己的业务?

问题陈述

你想帮助你当地的面包店实现利润最大化。他们出售几种产品,包括蛋糕、糕点和三明治。

可利用的资源

  • 4 名面包师每天工作 6 小时(总计 24 小时/天)
  • 1 名助理每天 4 小时做三明治
  • 2 个可全天 24 小时使用的烤箱(总计 48 小时/天)
  • 100 个插槽可用于库存和展示

这些项目有不同的资源需求和利润水平

每个 SKU 的资源需求—(图片由作者提供)

目标

你需要生产什么来最大化你的每日利润?

  • 结果
Lemon Cake = 0
Sandwich = 20
Chocolate Cake = 32
Croissant = 0
Chocolate Eclair = 0
Panini = 0
Profit reached: 328 euros/day

根据这些结果,你当地的面包店老板应该把注意力集中在巧克力蛋糕和三明治上,以达到每天 328 欧元的最高利润。

这个结果可以通过实现更多的约束和参数来改进,以确保您的模型显示您的业务的实际情况。

  • 你的资源利用率是多少?
Lemon Cake = 0
Sandwich = 20
Chocolate Cake = 32
Croissant = 0
Chocolate Eclair = 0
Panini = 0
1,440/1,440 minutes of bakers used
2,880/2,880 minutes of oven used
240/240 minutes of assistants working time used
126/200 display slots
Profit reached: 328.0 euros

好消息!你正在充分利用你的设备和劳动力。

  • 如果我们多两个助手来做三明治会怎么样?
Lemon Cake = 0
Sandwich = 60
Chocolate Cake = 32
Croissant = 0
Chocolate Eclair = 0
Panini = 0
Profit reached: 504 euros
  • 如果你多招聘两个助手(4 小时/天),销售更多三明治,利润增加 50%

http://samirsaci.com

二。构建您的模型

我们将使用 python 的纸浆库。PuLP 是由 COIN-OR Foundation(运筹学计算基础设施)维护的用 Python 编写的线性(LP)和整数规划(IP)问题的建模框架。

你可以在这个 GitHub 库中找到完整的代码: 链接
我的投资组合与其他项目:
小萨奇

1.声明您的变量、参数和模型

  • 你的目标是最大化你的利润
  • 下限=0:糕点值不能为负值
  • cat = 'Integer ':让我们试着卖掉整个羊角面包:)

这些参数可以从 Excel 文件中加载。

2.定义目标并添加约束

3.求解模型并分析结果

http://samirsaci.com

三。后续步骤

关注我的 medium,了解更多与供应链数据科学相关的见解。

1.改进您的模型

你当地的面包店不能因为无利可图而一天天地停止销售柠檬蛋糕。

因此,在您的模型中,您可以为每种商品添加最小数量的约束,以保证您的柠檬蛋糕粉丝会有他们最喜欢的产品。

可以添加额外的参数和限制,如配料数量、保质期等

2.与你当地的面包店老板分享你的模型

既然您已经构建了自己的工具,那么您想与当地的面包店老板分享它。

如果您能实现这个简单的工具,您能想象对她的业务会有什么影响吗?(你能得到多少免费的柠檬蛋糕?)

如果你感兴趣的话,我写了一篇关于如何分享你的 python 脚本的文章,供没有任何 Python 经验的人使用。

https://www.samirsaci.com/build-excel-automation-tools-with-python/

3.你需要多少助手?

本文展示了一种简单的方法,在假设工作负载波动的情况下,使用线性规划来优化劳动力分配。

https://www.samirsaci.com/optimize-workforce-planning-using-linear-programming-with-python/

关于我

让我们在 Linkedin 和 Twitter 上连线,我是一名供应链工程师,正在使用数据分析来改善物流运营和降低成本。

如果你对数据分析和供应链感兴趣,可以看看我的网站

https://samirsaci.com

参考

[1]运筹学的计算基础设施,优化与纸浆(文档),链接

使用最近邻搜索算法的最大内积搜索

原文:https://towardsdatascience.com/maximum-inner-product-search-using-nearest-neighbor-search-algorithms-c125d24777ef?source=collection_archive---------9-----------------------

这两个人看起来很像,对吧?约尔根·哈兰在 Unsplash 上的照片

允许使用库进行最近邻搜索的简单简化,以便有效检测具有大内积的向量

动机

最近邻搜索是数据科学中最基本的问题之一。应用范围从用户分割到近似重复检测。毫不奇怪,大多数用于数据科学应用程序的编程语言都提供了具有高度可伸缩的最近邻搜索算法的库。

另一个广泛使用的问题是检测具有大内积的向量。例如,在多标签分类问题中,我们需要检测与需要分类的输入实例的向量具有最大内积的类别向量。在推荐系统中,我们寻找具有大内积的向量。

这两个问题是相关的。正如我们下面所讨论的,如果所有的向量都有相同的范数,这两个问题就变得相同了。但是在大多数应用中,向量有不同的范数,这两个问题变得不同。在高层次上,在最近邻搜索中,我们寻找具有几乎相同值的向量。而在最大内积搜索中,优先选择它们之间具有小角度的向量。

在这篇文章中,我提出了一个简单的技巧,将最大内积搜索问题简化为最近邻搜索。该方法的主要好处是,我们可以将为最近邻搜索设计的高度优化的库应用于最大内积搜索问题。

正式设置

通常,最近邻搜索被表述为欧几里得空间中的向量问题。向量 x 的范数定义为

给定数据库 D,查询向量 q 的最近邻是 D 中的向量 x ,使得

最大内积搜索定义为:

我们假设我们有一个经常被查询的静态海量数据库 D。例如,D 包括由矢量表示的所有网飞电影。

单位范数向量

如果数据库 D 中的所有向量都有单位范数,即||u||=1,那么这两个问题就等价了。注意到

因此,最小化两个向量之间的欧几里德距离对应于最大化它们的内积。因此,在 D 中寻找 q 的最近邻等价于寻找具有与 q 的最大内积的向量。当然,我们可以缩放所有向量和查询向量以具有单位范数,但是这可能导致重要信息的丢失。向量中条目的大小至关重要。

一般情况下的简单简化

在[1]中提出了以下简单的简化:

φ是数据库中所有向量的最大范数。我们将φ和向量的范数之差作为第一坐标加到每个向量上。(这将总是非负的,因为φ是最大范数。)我们添加 0 作为查询向量的第一坐标。

对于变换向量的范数,我们有:

此外,由于 q_z 的第一个坐标为 0,因此

因此,新空间中向量之间的欧几里德距离可以重写为

我们看到唯一依赖于查询索引 i 的项是内积 x_i^Tq* 。因此,变换空间中的最近邻向量 z_i 将对应于原始空间中具有最大内积 x_i 的向量。

逆变换

在[1]中,作者还展示了如何将最近邻搜索问题简化为最大内积搜索。感兴趣的读者可以参考[1]中的定理 2,但我不会在这里讨论它,因为它不太可能有实际的重要性。

履行

给定数据库 D,简单地将上述变换应用于 D 中的所有向量。然后,在查询 q 时,调用最近邻搜索算法。

对转换进行编码非常简单:

def transform(vecs):
    maxnorm = max([np.linalg.norm(v) for v in vecs])
    new_vecs = []
    for v in vecs:
        new_vecs.append(
           np.insert(v, 0, np.sqrt(maxnorm**2-np.linalg.norm(v)**2))
        )
    return new_vecs

变换之后,我们可以使用最近邻搜索库来解决最大内积问题:

from sklearn.neighbors import NearestNeighbors
X = np.array(new_vecs)
nbrs = NearestNeighbors(n_neighbors=1, algorithm='kd_tree').fit(X)
distances, indices = nbrs.kneighbors(np.array([q]))

完整的实现可以在这个 Jupyter 笔记本中找到。在那里,我还展示了使用 Python 的最近搜索比显式计算查询向量 q 和数据库中所有向量的内积的简单实现更有效。

[1]约兰·巴赫拉赫、耶胡达·芬克尔斯坦、兰·吉拉德·巴赫拉赫、利兰·卡齐尔、诺姆·柯尼希斯泰因、尼尔·尼斯、乌尔里希·帕凯。使用内积空间的欧几里德变换加速 Xbox 推荐系统。RecSys 2014,此处提供

最大似然估计和 OLS 回归

原文:https://towardsdatascience.com/maximum-likelihood-estimation-and-ols-regression-36c049c94a48?source=collection_archive---------8-----------------------

介绍他们的关系和工作方式

由 Unsplash 上的 Edge2Edge 媒体拍摄

在我的研究中,我多次遇到最大似然估计的概念。然而,没有传统上在我的领域工作的那些人的统计背景,我经常发现很难理解或遵循他们创建和开发的一些模型。其中很大一部分是他们对最大似然估计方法的使用以及他们与回归框架的联系。为此,我想我可以分享我的学习,希望遇到同样问题的其他人不必通过搜索多个不同的文章和文本来达成相同的理解。

首先,从概率的基础开始。如果一个事件的结果是随机的,那么代表该事件的变量就是所谓的随机变量,通常用大写字母 Y 表示。大多数时候,我们感兴趣的是这个随机变量取某个值的概率,比如六面骰子 Y = 5 的概率。然而,在此范围内,有三种主要的概率类型:

  1. 边际概率 —如果 A 是一个事件,那么边际概率就是该事件发生的概率,表示为 P(A) =概率。例如,如果我们对骰子掷出 6 的概率感兴趣,那么这将是 P(Y = 6) = 1/6
  2. 联合概率 —这是两个或更多事件相交的概率。例如,如果 A 和 B 是两个发生的事件,那么 A 和 B 都发生的联合概率由 P(A∩B) = probability 给出。例如,如果我们有两个骰子,两个骰子都显示 3 的概率是多少,这将是 P(3 和 3) = 1/36。
  3. 条件概率 —这是在已知某个事件(B)已经发生的情况下,某个事件(A)发生的概率,用 P(A|B)表示。切换到卡的例子,如果我们知道我们选择的卡是图片卡,我们可以看到它是皇后的概率是多少:P(皇后|图片卡)= 1/3 [1]

然而,为了最大似然估计的目的,我们主要关心联合概率的概念。当事件是独立的(即一个事件的结果不影响另一个事件),则使用两个单独概率的联合概率,它只是两个单独概率的乘积:

这通常被认为是这样的(事件是独立的),即使事实并非如此,主要是因为这使得数学变得简单了许多。因此,我们在寻找 A 和 B 发生的概率,记住这一点很重要。

希瑟·吉尔在 Unsplash 上拍摄的照片

现在换个思路,线性模型往往采用 y = mx + c 的格式,其中 y 和 x 为变量( y 常被称为因变量,x 为自变量),m 和 c 为参数。记住这一点很重要,因为在某些情况下,如果没有明确说明,它们可能会相互混淆。然而,出于我们的目的,这些参数告诉我们模型做什么以及它如何表现。

我们可以使用最大似然估计(MLE)来提取这些参数的值。这是发现参数的地方,这些参数最大化了方程的格式产生我们实际观察到的数据的可能性。因此,这本质上是一种将参数拟合到观察数据的方法。我们用这种方法得到的值就是所谓的最大似然估计。

其工作方式取决于独立变量(y)的分布假设。例如,如果假设 y 值可以近似为正态分布(这是经常发生的情况),我们需要计算这些目标变量的均值和偏差的最大似然参数值,均值是我们的主要兴趣。为此,我们将试图估计的模型(我们的回归模型)作为标准正态分布的平均值,实际观察值表示为平均值加上一些误差项。所以如果我们试图估算参数的模型是:

其中ε_o 是误差项,然后通过最大似然估计,我们试图计算平均值,其表示为:

其中 y-hat 变量表示估计的平均值。我们可以用向量符号来简化,X 表示 x_1,x_2,x_3,B 表示β_0,β_1,β_2,β_3。因此,这可以简写为:

其中 N 是总共 N 个观察值中 y 和 x 的单独观察值。这意味着,对于每一组 X 变量,都有一个由函数生成的数,这个函数以 y-hat 作为我们要估计的平均值。这些 y 变量中的每一个的平均值,本质上是模型基于一组给定的参数所预测的。

这里,y-hat 不是一个固定值,而是基础正态分布的函数:

这就说随机变量 Y 等于观测值 y_n 的概率是方差为σ,均值为 y-hat 的底层分布的函数,其中 y-hat 的值来自于我们的函数形式的关系式(y-hat_n = X_ni β_i)。我们从最大似然估计中得到的本质上的近似值是参数值是给定数据观测值所预测的值的可能性。因此,对于一组观察值 y_n,我们希望最大化 y_n 由数据 X_ni β_i 给出的总概率,这将产生代表模型最大似然的参数值。这将表示为:

其中 L(β|y_n)是参数集β给定观察值 y_n 的可能性,右侧是数据和参数给定 y_n 的每个概率的乘积,由下式给出:

本质上,这意味着给定观测值的参数的可能性是给定模型的函数形式的观测值的概率的函数。

然后将这个方程代入上一个方程,但为了简化计算,我们通常取两边的对数。这是因为对数乘以对数可以表示为加法,因此这不是乘法,而是简单的求和,这更容易处理。

我们可以使用这种对数变换,因为对数是单调函数(y 值随着 x 值的增加而增加,没有重复值)。因此,日志的最大值将出现在与非日志值的最大值相同的点上。

由此,我们想要估计参数是模型的最佳表示的可能性的最大值。因此,我们可以简单地区分和解决每个参数,以找到最大值,这将使用最大似然估计。

然而,众所周知,这一过程的结果会得出与普通最小二乘(OLS)回归相同的结论[2]。这是因为 OLS 只是最小化了预测值和实际值之间的差异:

这与最大似然估计的结果相同!当然,这背后的假设是:自变量是正态分布的,自变量和因变量之间的关系是线性的,误差是独立的和正态分布的,并且所有 x 值的方差相等。

因此,在正态分布的自变量的情况下,经常使用 OLS 回归。然而,最大似然估计的格式可以根据基础分布而变化,无论是泊松分布、伯努利分布还是负二项式分布,都可以与其他回归规范相关,这些规范可以从相同的原则中推导出来!

[1] Brooks-Bartlett,Jhttps://towards data science . com/probability-concepts-explained-introduction-a7c 0316 de 465

[2] Flowerdew,R;洛维特,用泊松回归分析计数数据,专业地理学家,1989,41,2

[## scikit-learn 决策树分类器简介

towardsdatascience.com](/introduction-to-decision-tree-classifiers-from-scikit-learn-32cd5d23f4d)

最大似然估计和泊松回归

原文:https://towardsdatascience.com/maximum-likelihood-estimation-and-poisson-regression-in-the-gravity-model-5f0de29e3464?source=collection_archive---------22-----------------------

通过引力模型介绍它们之间的关系

照片由 Unsplash 上的穆扎米尔·苏尔马拍摄

在我之前的文章中,我介绍了概率的基本概念以及它与最大似然估计和普通最小二乘回归的关系。在本文中,我将继续在此基础上进行构建,但在泊松回归的情况下,我将把它应用于重力模型。

与假设因变量正态分布且方差恒定的线性回归相反,泊松回归假设目标变量分布为泊松分布。因此,自变量的方差应该等于其均值。这用于我们处理离散的、基于计数的数据的情况,其中独立值是非负整数,并且事件被假设为彼此独立(尽管实际上可能不是这样)。泊松分布可以用它的概率质量函数(概率密度函数的一种形式,但用于离散变量)来表示,其形式为:

换句话说,这仅仅意味着随机变量 Y 取 y_i 的值的概率是分布λ_i 的均值和事件 y_i 的计数的函数。

这个分布可以用 python 建模,代码如下:

#import required libraries
import matplotlib.pyplot as plt
import numpy as np#create the subplot
plt.subplots(figsize = (7,7))#plot the distributions
plt.hist(np.random.poisson(lam=0.5, size=3000))
plt.xlabel("Random Poisson Distribution (0:3000, lambda = 0.5)", fontsize = 15)
plt.ylabel("Count", fontsize = 15)
plt.title("Random Poisson distribution", fontsize =20)
plt.grid(True)

作者照片

由此得到的图与我们从正态分布中预期的相比看起来是负偏的,并且在小整数值的情况下尤其突出。

重力模型本身采用以下形式:

本质上说,从 I 到 j 的流量 T_ij 与起点的人口(V_i)和目的地的人口(W_j)成正比,与它们之间的距离(d_ij)成反比。这是引力模型的基本形式,用于预测不同地理区域之间的人口、货物、信息和货币的流动。这是通过类比牛顿的引力定律得出的结论,即两个物体相互吸引的程度与它们的大小成正比,与它们的距离成反比。

作者图片

在这种情况下,k 是一个比例常数,它确保预测流量与实际总流量相加,而α、γ和β都是待估算的参数

该模型用于预测 T_ij 的最可能值,给定方程中的变量参数,这意味着该模型变为:

其中,T_ij^hat 是模型的预测/期望值。因此,与之前的正常回归一样,当我们出于回归目的将其转换为线性格式时,我们不转换实际值,而是转换给定数据的期望值。因此,这个模型变成了:

其中,右侧表示我们正在对 I 和 j 之间流量的预期值的自然对数进行建模。这意味着预期值与平均值λ_i 相关,由模型通过连接函数进行预测,在泊松回归的情况下,其形式为:

其中 X 代表所有 I 个独立变量的向量,β是所有参数的向量,n 是我们拥有的观测值的数量。这样做的结果是我们得到了这样的形式:

正是这个连接函数确保了λ为非负值,即使回归量 X 或回归系数β为负值[1]。

按照与线性关系相同的思路,每个自变量取观察值的概率由下式给出:

其中 y_n 是我们对实例 n 的观察值,在我们的情况下是 T_ij,X_n 可以作为我们对实例 n 的所有因变量的向量,在我们的情况下是 V_i,W_j 和 D_ij [2]。由于我们有多个观测值,我们希望在给定一组参数的情况下,最大化所有观测值发生的可能性。由于我们假设实例彼此独立,因此我们希望使用联合概率关系作为每个实例发生的概率:

这意味着我们得到了使给定的一组观察到的 T_ij 最有可能的参数值。

考虑到所有这些值的乘法会很复杂,为了简化数学,我们可以取等式两边的对数。假设自然对数是单调函数(每个 x 值对应一个 y 值),则预期值的对数应该在与预期值相同的位置达到最大值。因此,上面复杂的乘法变成了简单的求和:

在我们的重力模型中,最终简化为:

其中λ_n 是我们的预测值,T_ij 是我们的实际值。然后,这可以通过每个参数来区分,并求解为 0,以便找到最大似然估计,从而获得最适合数据的函数形式。

然而,就像正态分布的最大似然估计一样,我们可以使用泊松回归形式的回归来逼近解[2]。这当然可以通过 statsmodels 库在 python 中实现。因此,当因变量为泊松分布时,最大似然估计过程如何与泊松回归相关。

[1]洛维特,花露,R 1989。使用泊松回归分析计数数据。专业地理,41,2,190–198

[2]福泽林汉,S,威廉,第 1983 页。泊松相互作用模型的进一步讨论,地理分析,15,4,343–347

如果你喜欢我的文章,并想阅读更多,请随时使用以下链接:

https://philip-wilkinson.medium.com/membership [## scikit-learn 决策树分类器简介

towardsdatascience.com](/introduction-to-decision-tree-classifiers-from-scikit-learn-32cd5d23f4d)

最大似然估计(MLE)和 Fisher 信息

原文:https://towardsdatascience.com/maximum-likelihood-estimation-mle-and-the-fisher-information-1dd53faa369?source=collection_archive---------3-----------------------

极大似然估计置信区间的构造

媒体上关于 MLE 的文章数量巨大,从理论到实现都使用不同的语言。关于费希尔的资料,也有不少教程。然而,费雪信息和最大似然估计之间的联系很少被提及。因此,我想就这个话题发表一篇文章。

图片来自 Unsplash

最大似然估计

MLE 的主要思想很简单。它回答了这个问题:

W 什么参数最有可能使模型产生我们现有的样本?

形式上,我们考虑随机变量序列 X₁,…,Xₙ,这样它们是相同的独立分布(iid)随机变量。它们都来自同一个分布 f(x;θ),其中θ是一个参数向量(我们用这个大θ来表示一个参数向量,这意味着θ∈ℝᵖ,如果模型只有一个参数,我们将在本文中使用θ来表示)和θ∈ω,其中ω是参数的样本空间。这听起来很奇怪,但是在 MLE 中,我们从样本空间中选取参数,并且我们想要最可能的一个。我们如何做到这一点?我们最大化一个似然函数,其定义为

等式 1.1 似然函数

每个事件的概率可以相乘,因为我们知道这些观察是独立的。在等式 1.1 中,每个 Aⱼ是一个事件,它可以是一个区间或包含单个点的集合。当截尾在某一特定值 u 时,观测事件 Aⱼ为区间[u,∞]。在没有删截的情况下,Aⱼ包含一个单点,并且给定参数θ,该观察的可能性为

不考虑删失,似然函数可以写成

也就是我们平时在课本上看到的。

作者图片

我们想对 L 做什么?我们想找出θ,L 的最大值。例如,如果 L 可以解析地最大化,并且它是凹的,我们可以计算关于θ的导数,并让它为零。此时,L 的值将是全局和局部最大值。让我们看一个正态分布的多元数据的例子。

等式 1.2 正态分布

考虑随机变量 X = (X₁,X₂,…,Xₙ),均值μ = (μ₁,μ₂,…,μₙ);我们假设标准方差是一个常数σ,这个性质也被称为同方差。因此,似然函数是

等式 1.3

这些指数看起来有点混乱,但是考虑到每个观察值被安排在矩阵 x 的列中的事实,等式 1.3 实际上是非常简单的。xᵢⱼ只是第 j 次观察的第 I 个成分。并且等式[ex1]用于估计每个μᵢ.我们知道对数可以把乘积变成求和,而通常,求和更容易处理。所以我们可以试着取等式 1.3 两边的对数。

等式 1.4

(应该很明显 log 指的是自然对数)剩下的就好办了;我们需要对方程 1.4 做一些代数运算。由于σ是一个常数,我们可以把它分解出来;然后我们到达

等式 1.5

记住我们想要最大化 L,这相当于最大化等式 1.5,因为 log 单调增加。这可以归结为最小化下面的表达式

公式 1.6

如果你熟悉普通的线性模型,这应该会让你想起最小二乘法。我们可以看到,最小二乘法和正态假设下的 MLE 是一样的(误差项具有正态分布)。

对于实际应用中的最大似然估计,我们看下面的例子:一个高中学生获得的奖励数量的数据集(此处可用)。如果我们画出奖励的数量

awards <- read.csv2(file='data/Awards_R.csv', header=TRUE, sep=',')
summary(awards)
awards.num <- awards$num_awardsplot(table(awards.num), main='Awards in math', ylab='Frequency', xlab='Number of awards')

从图中我们可以看出,它遵循泊松分布

泊松分布的最大似然函数定义为

等式 1.8 泊松分布的最大似然函数

我们可以这样实现

L=function(x){
    a=1
    for (i in 1:length(awards.num)){
        # Poisson probability mass function
        a=a*dpois(awards.num[i],x)
    }
    return(a)
}

并优化这个函数

# find the value for which L is maximized
sol <- optimize(L, c(0,2), maximum=TRUE)
curve(L, from=0, to = 2)
x1 <- sol$maximum
x2 <- sol$objective
points(x1, x2, col="#FF0000", pch=19)
abline(v=x1, col="#FF0000")

搜索最大值的间隔是根据 L 图选择的(见图 1.8)。该函数通过optimize进行数值最大化,它在给定的区间内(以预定的精度)搜索优化值。这给了我们

$maximum
[1] 0.970013$objective
[1] 1.853119e-113

这意味着最大值是 1.853119e-113,L(0.970013)= 1.853119 e-113—λ= 0.970013 是优化的参数。如图表所示

图 1.8 似然函数

结果表明,样本均值与优化 L 值非常接近

mean(awards.num)
# --> 0.97
# sol$maximum = 0.970013

这是有意义的,因为泊松分布中的参数λ与期望值相同。为了正式证明这一点,我们可以对对数似然函数求导

等式 1.9 log(L)的推导,L 在等式 1.8 中定义

将这个导数设为零,我们得到

等式 1.10 估计量

其中λ和 hat 表示估计量。这告诉我们,在这个例子中,最大似然估计量是由样本均值给出的。

极大似然估计的置信区间

费希尔信息矩阵

假设随机变量 X 来自参数为θ的分布 f ,费希尔信息测量 X 携带的关于θ的信息量。为什么这种量化很重要?事实证明,在贝叶斯和频率主义方法的统计,费雪信息的应用。贝叶斯主义者用这个来求初始概率,频率主义者在 MLE 中构造置信区间。(要阅读更多关于贝叶斯和频率主义方法的内容,请参见这里)费希尔信息重要性的一个具体例子在【2】中谈到:

这个例子是连续投掷硬币十次,观察结果是一个 10 维的数组,一个可能的结果是 X = (1,1,1,1,1,0,0,0,0)。我们要估计得到人头的概率,θ。x 有 2 个⁰ = 1024 个可能的结果,我们可以让另一个随机变量,t 是 x 中的人头数,t 是 x 的函数,它被称为一个统计量。在一些文献中,统计被描述为“一条信息”这是真的,但更准确地说,它是观察值(数据集)的函数,它总结了数据。

在这个例子中,T 具有由概率密度函数给出的二项式分布

等式 2.1

在本例中,n = 10。x 有 1024 种可能的结果,然而 T 只能取 11 个不同的值。是否意味着 X 比 T 包含更多的信息?一般来说,是的,既然 X 把抛硬币的顺序考虑进去了,但是 T 没有。但是关于θ,没有,因为抛硬币的输出顺序不影响θ。同样,统计量 T 是充分的,这意味着给定 T 的值,X 取特定值的概率不取决于θ。这意味着,条件概率分布 P(X | T = t,θ)是均匀的,由下式给出

等式 2.2

这也可以这样解释:给定 T 的值,X 中不再有关于θ的信息。为了量化统计量 T 和原始数据 X 中关于参数θ的信息,Fisher 信息开始发挥作用

定义 2.3 (a)费希尔信息(离散)

其中ω表示样本空间。在连续分发的情况下

定义 2.3 (b)费希尔信息(续)

log f(x|θ)的偏导数称为得分函数。我们可以看到,费希尔信息就是得分函数的方差。如果有多个参数,我们就有了带有元素的矩阵形式的 Fisher 信息

定义 2.4 费希尔信息矩阵

这也可以写成

等式 2.5 费希尔信息矩阵

定义 2.4 和方程 2.5 之间的等价性不是微不足道的。这是费雪信息的一个重要性质,我们现在就来证明一维情况(θ是单参数):先从恒等式开始:

等式 2.6

也就是密度函数 f(x;θ),θ为参数。注意 f(x|θ)和 f(x;θ).第一个表示条件概率——概率分布函数在给定参数的条件下。然而,后者意味着θ是函数的参数,仅此而已。然后我们对两边的θ求导。

等式 2.7

现在我们需要尝试让日志出现。诀窍如下

在等式 2.7 中,我们使用乘一技术(“乘一”、“加零”——数学中著名的把戏),这意味着我们乘以 f(x;θ)然后除以 f(x;θ).等式 2.8 中红色部分的组合给出了 f(x;θ).

等式 2.9 给出了费雪信息的另一个重要性质— 费雪信息的期望等于零。(是旁注,本帖没有用到这个性质)回到 Def 2.4 和方程 2.5 等价的证明。我们重新对方程 2.9 关于θ求导

再次对等式 2.9 求导

因为在等式 2.10 中,灰色和黑色部分都是正的(f(x;θ)终究是概率测度,唯一可能的场景就是方程(2.11)。从方程 2.11 开始,我们移动 f(x;θ)从 LHS(左侧)到 RHS(右侧)

等式 2.12

就像等式 2.8 一样,在等式 2.12 中,红色部分的组合再次给我们 f(x;θ).

等式 2.13

证明已经完成了。

构建置信区间

首先,我们将介绍最大似然估计的渐近分布定理,它告诉我们估计量的渐近分布:

L et X₁,…,Xₙ是由 f(x)给定的分布中大小为 n 的一个样本,参数θ未知。设真实参数为θ₀,θ₀的最大似然法为θhat,则

等式 2.5

这表明

等式 2.6 估计量的分布

因为当样本量接近无穷大时,最大似然估计接近真实参数,这也被称为最大似然估计的一致性

性质 2.7 最大似然估计的一致性性质

我们也可以论证方程 2.8 也成立(参考方程 2.5)。

等式 2.8

(关于这个定理的证明,见此处,第 5 页。)然后我们可以从下面的等式建立置信区间

不等式 2.8 置信区间

其中 z 是累积函数的倒数,α是临界值。接下来的事情是找到费希尔信息矩阵。这很容易,因为根据等式 2,5 和 Hessian 的定义,对数似然函数的负 Hessian 就是我们要找的东西。

你可能会问为什么方程 2.5 中的费希尔信息矩阵与海森信息矩阵相同,尽管它是一个期望值?这是因为函数 L 是在最大似然估计下求值的,也就是说 L 是针对特定的θ求值的。而这就是使 l 最大化的θ,因此,加权平均(我们知道期望是加权平均)就不再必要了——观察到的费希尔信息只是二阶微分。“观察到的”意味着费希尔信息是观察到的数据的函数。(这个话题在 MathStackExchange 上也有讨论)。

我们可以用下面的代码找到置信区间,用同样的数据集。

L.log = function(x){
    a=1
    for (i in 1:length(awards.num)){
        # Poisson probability mass function
        a=a+dpois(awards.num[i],x, log=TRUE)
    }
    return(a)
}# numerical approach
opt.log = optim(par=1, L.log, method="Brent", control=list(fnscale=-1), hessian=TRUE, lower=0, upper=2)opt.log
I.log <- opt.log$hessian# since we have only one parameter, there's no inverse of matrix calculated
est.log <- qnorm(1 - alpha/2) / sqrt(n * (-I.log[1,1]))
l.est.log <- x1 - est.log
h.est.log <- x1 + est.log
l.est.log
h.est.log
# --> CI = [0.9603613, 0.9796647]

总结:

在这篇文章中,我们快速介绍了最大似然估计,然后我们来看看 Fisher 信息及其矩阵形式。记住这两个概念,然后我们探索如何构建置信区间。在这篇文章中,只使用了一个例子:一所高中的获奖人数。该分析完全在 r 中实现。

资源:

[1]p . m . e . Altham(2005 年)。R 中的广义线性建模介绍。统计实验室,朱尼奥

[2] Ly,a .,Marsman,m .,Verhagen,j .,Grasman,R. P .,& Wagenmakers,E. J. (2017)。关于费希尔信息的教程。数学心理学杂志80 ,40–55。

[3]马尔科·塔博加(2017)。“泊松分布——最大似然估计”,概率论与数理统计讲座,第三版。Kindle 直接出版。在线附录。https://www . stat lect . com/fundamentals-of-statistics/泊松分布-最大似然法。

[4] Klugman,S. A .,Panjer,H. H .,& Willmot,G. E. (2012 年)。损失模型:从数据到决策(第 715 卷)。约翰·威利的儿子们。

[5]什么是删失数据?可从以下网址获得:https://reliability . readthedocs . io/en/latest/What % 20 is % 20 reviewed % 20 data . html。于 2021 年 10 月 13 日访问

错误日志

  1. 增加等式 1.1 中符号的解释

最大可能性最简单的方法

原文:https://towardsdatascience.com/maximum-likelihood-the-easy-way-1f14c0e2a5ce?source=collection_archive---------42-----------------------

一个复杂的话题简化了

在 Unsplash 上由 Pocky Lee 拍摄的照片

摘要

本文将首先使用一个简单的例子来演示最大似然估计(MLE)。然后,我们将建立在第一个例子拟合逻辑回归模型使用最大似然法。通过理解这两个例子,你将具备使用任何其他广义线性模型(GLMs)的基础知识。

注意

在下面的例子中,我在一个平面上滚动一枚镍币,直到它落到左边或右边。对于第一个例子,我们将使用一个“公平”的未加权硬币。在第二个例子中,我们将增加硬币大小的重量。

示例 1 —公平镍

如果我们在平面上滚动一枚公平的镍币 10 次,我们可能会认为它从左到右落下的机会相等(p=0.5)。在我们的例子中:

  • 向右下降是正例(y=1,p=0.5)
  • 落在左边是否定的情况(y=0,p=0.5)

在 10 次投掷中,我们观察到硬币向右下落了 5 次(y=1),向左下落了 5 次(y=0)。这是我们所期望的,因为硬币是公平的。下面是 10 卷的表格:

10 卷公平硬币

使结果的可能性最大化的 p(概率)值是多少?

我知道我知道——p = 0.5 太简单了,但请耐心听我说——如果你知道如何用一个非常简单的例子来计算最大似然,这个概念很容易扩展到更复杂的问题。

首先,让我们使用伯努利分布,因为它最能代表我们的数据。

带有示例的 Bernoulli

似然函数是每个观测的 PMF 的联合概率分布(乘积)。我们只是将所有的 PMF 相乘

联合概率分布

可以改写为:

似然函数

我们想最大化这个功能。与其用微积分来解决这个问题,不如用不同的 p 值来看看它是如何影响可能性的。你可以用这个工作簿 (tab: fair_coin)试试看。

正如所料,我们将通过指定概率 p = 0.5 来最大化可能性。

现在我们已经开发了基础,让我们继续看一个稍微复杂一点的例子。

示例 2 —加重镍

让我们再做 10 次,除了这一次我们被告知镍在每一次滚动之前被改变重量(w)。负 w 表示重量在硬币的左侧,正 w 表示重量在右侧。

下面是 10 卷的结果表。我填充了“desc”列,以便于阅读表格。例如,第一次观察在左侧有(-1.0)的重量,硬币落在左侧。

10 卷不同重量的

查看数据,我们可以看到硬币重量的大小和侧面与下落方向之间的关系。硬币两边最重的重量会把硬币往那个方向拉。在第一个例子中没有权重,所以我们没有额外的信息来建模行为。在这种情况下,我们希望使用这些附加信息(权重)来分配预测概率。

让我们首先手动为每个结果分配预测概率,看看它们与 PMF 和可能性的关系。我们将使用与第一个例子相同的概念:

我已经创建了一个谷歌表单 (tab: weighted_coin),所以你可以探索这些关系。

在左边的表格中,我把所有的预测概率都设为 0.1,在右边,我把它们都设为 0.9。当 y=0(负)且预测概率也很低 p=0.1 时,你注意到 PMF 的大小了吗?

接下来,当 y = 1(正)且预测概率高 p=0.9 时,你会注意到什么?

检查两种情况的相反情况。(y=1,p=0.1),(y=0,p=0.1)

正如你所看到的,当我们把最高的概率分配给积极的情况,把最低的概率分配给消极的情况时,就达到了最高的 PMF 值。注意两种情况下底部的可能性是一样的——还没有那么大。

现在,让我们把高概率分配给正面案例,低概率分配给负面案例,看看会发生什么。看看现在的 PMF 值。看看底部的可能性增加了多少!

这就是逻辑回归的本质。该算法试图通过拟合未知参数(斜率和截距确定 p)来最大化拟合伯努利分布(我们的 PMF 的乘积)的可能性(对于阳性情况分配高预测概率(p ),对于阴性情况分配低预测概率(p))。这就是它如何区分阶级的。

让我们使用带有新可变权重的逻辑回归来帮助我们预测概率。

最大似然逻辑回归

与其用微积分求解,不如手动求解。我已经创建了一个电子表格 (tab: fitting_logistic),它允许你改变截距和斜率,并计算预测的概率和可能性。看看你能否算出最大可能性的截距和斜率。电子表格是这样的:

逻辑回归求解器

如果你研究一下斜率和截距,你会发现截距= 0 和斜率= 3 时可能性最大。我说“大约”是因为我们没有做所有精确的数学计算。

“最佳”斜率和截距

你实验斜率和截距的方式本质上是软件如何计算出最大似然**,有两个主要的例外:**

  1. 似然函数被转换成对数(似然)
  2. 软件知道在每次被称为梯度下降(或上升)的迭代中调整斜率和截距的方向

我鼓励您进一步探索这些主题,但这超出了本文的范围。

好,回到我们的例子

截距= 0 是什么意思?

截距= 0 意味着当重量= 0 时(我们在硬币的两边都没有任何重量),阳性情况(硬币向右下落)的对数(几率)为零。想一想这个。完全说得通。因为 w = 0,我们知道这是一个公平的硬币,根据前面的例子,我们确定它有 50%的机会落在任何一边。这是一个数学公式,证明了当 log(odds) = 0 时,p = 0.5。

斜率= 3 是什么意思?

重量的斜率是 3。这意味着硬币右侧每增加一个重量单位,硬币向右下落的几率就会增加 3。我会让你把它转换回概率。

总结一下:

我们已经通过拟合未知参数**(斜率和截距确定 p),最大化了拟合伯努利分布**(我们的 PMF 的乘积)的可能性(对于阳性情况高预测 p,对于阴性情况低预测 p)。这是最大似然估计。

一般线性模型

我们可以使用与我们刚刚学习的模式相同的模式来拟合其他广义线性模型。关键的区别在于您将使用不同的发行版。

例如,在线性回归中,我们将通过拟合未知参数**(斜率和截距决定平均值)来最大化拟合正态分布**的可能性。关于线性回归最大似然估计的更多信息,参见本文。

最大似然与贝叶斯估计

原文:https://towardsdatascience.com/maximum-likelihood-vs-bayesian-estimation-dd2eb4dfda8a?source=collection_archive---------2-----------------------

实践教程

参数估计方法的比较

由和在 Unsplash 上拍摄的照片

机器学习的核心是模型。我们如何表示数据?我们可以用什么方法将数据分组以进行比较?我们的数据来自什么分布或模型?这些问题(以及更多的问题)驱动着数据处理,但后者是参数估计的基础。

最大似然估计(MLE)和贝叶斯估计(Bayesian estimation)可能是最广泛使用的两种参数估计方法,通过这两种方法,给定一些数据,我们能够估计产生这些数据的模型。为什么这很重要?在现实世界中收集的数据几乎不能代表全部人口(想象一下我们需要收集多少数据!),因此,通过从观察到的样本总体估计分布参数,我们可以深入了解看不见的数据。

作为本文的先决条件,首先理解微积分和概率论中的概念是很重要的,包括联合概率和条件概率、随机变量和概率密度函数。

参数估计处理近似分布的参数,这意味着分布的类型通常是预先假定的,这决定了您将要估计的未知参数是什么(泊松的λ,高斯的μ和σ)。我在本文中使用的例子将是高斯。

样本问题:假设您想知道森林中树的高度分布,作为对树木健康的纵向生态研究的一部分,但您今年唯一可用的数据是一名徒步旅行者记录的 15 棵树的样本。您希望回答的问题是:“我们可以用什么分布来模拟整个森林的树高?"

观察数据(15 个样本)的直方图,以及可能产生该直方图的 4 个高斯曲线示例。图片作者。

符号的快速注释

  • θ是未知变量,在我们的高斯情况下,θ = (μ,σ)
  • D 为所有观测数据,其中 D = (x_1,x_2,…,x_n)

似然函数

最大似然估计和贝叶斯估计的(几乎唯一的)共同点是它们依赖于所见数据(在我们的例子中,15 个样本)的可能性。可能性描述了每个可能的参数值产生我们观察到的数据的可能性,由下式给出:

似然函数。图片作者。

由于奇妙的独立同分布假设,所有的数据样本都被认为是独立的,因此我们能够放弃混乱的条件概率。

让我们回到我们的问题上。这需要知道我们 15 个样本的值,我们未知参数(μ,σ)的每个组合产生这组数据的概率是多少?通过使用高斯分布函数,我们的似然函数是:

μ,σ上的似然函数。图片作者。

最大似然估计

太棒了。现在你知道了似然函数,计算最大似然解就真的很容易。名字里就有。为了得到我们的估计参数(𝜃̂),我们所要做的就是找到产生最大似然函数的参数。换句话说,(μ,σ)的什么组合给了我们上图中似然函数顶部最亮的黄点?

为了找到这个值,我们需要应用一点微积分和导数:

(有问题的)𝜃̂.计算图片作者。

你可能已经注意到了,我们遇到了一个问题。对产品进行衍生会变得非常复杂,我们希望避免这种情况。幸运的是,我们有办法解决这个问题:使用对数似然函数。回想一下:( 1)乘积的对数是对数的总和,以及(2)取任何函数的对数可能会改变数值,但不会改变该函数最大值出现的*,因此会给出相同的解。*

使用对数似然法正确计算𝜃̂。图片作者。

事实证明,对于一个高斯随机变量,最大似然解就是观测数据的均值和方差。因此,对于我们的问题,模拟树高分布的 MLE 解是μ=152.62 和σ =11.27 的高斯分布。

贝叶斯估计

好消息!为了帮助您搜索该森林的树高分布,您的同事设法进入数据档案,挖掘出该森林过去 10 年的树高平均值。有了这些信息,您现在可以另外使用贝叶斯估计来解决这个问题。

贝叶斯估计背后的中心思想是,在我们看到任何数据之前,我们已经有一些关于它来自的分布的先验知识。这种先验知识通常来自经验或过去的实验。然而,在进入这种方法的本质之前,掌握贝叶斯定理的概念是至关重要的。

贝叶斯定理

希望你知道,或者至少听说过概率环境中的贝叶斯定理,我们希望找到一个事件以另一个事件为条件的概率。在这里,我希望以一种能洞察贝叶斯参数估计和先验重要性的方式来构建它。

贝叶斯定理。图片作者。

为了说明这个等式,考虑事件 *A =“今天早些时候下雨”*和事件 B =“草地是湿的”的例子,我们希望计算 P(A|B) ,假定草地是湿的,早些时候下雨的概率。为此,我们必须计算 P(B|A)P(B)P(A) 。条件概率 P(B|A) 表示假定下雨,草地是湿的概率。换句话说,假设下雨,草地潮湿的可能性是

P(A) 的值被称为 先验 :不管草是否潮湿(在知道草的状态之前)下雨的概率。这种先验知识很关键,因为它决定了我们对可能性的加权程度。如果我们在不经常下雨的地方,我们会更倾向于将湿草归因于雨水以外的东西,如露水或洒水器,这是由低的 P(A) 值捕获的。然而,如果我们在一个经常下雨的地方,湿草更有可能是雨水的副产品,高的 P(A)会反映这一点。

剩下的就是 P(B) ,也被称为 证据 :草地潮湿的概率,这一事件充当了下雨事实的证据。这个值的一个重要属性是,它是最终概率的一个归一化常数,正如你将很快在贝叶斯估计中看到的,我们用一个归一化因子代替传统的“证据”

用于贝叶斯估计的等式与贝叶斯定理具有相同的形式,主要区别在于我们现在使用模型和概率密度函数(pdf)来代替数字概率。

贝叶斯估计。图片作者。

请注意,首先,似然性等同于 MLE 中使用的似然性,其次,贝叶斯定理中通常使用的证据(在这种情况下将转化为 P(D) )被分子的积分所取代。这是因为(1) P(D) 实际计算起来极其困难,(2) P(D) 不依赖于θ,这才是我们真正关心的,(3)它作为归一化因子的可用性可以替代积分值,保证后验分布的积分为 1。

回想一下,为了求解 MLE 中的参数,我们采用对数似然函数的 argmax 来获得(μ,σ)的数值解。在贝叶斯估计中,我们改为计算参数空间上的分布,称为 后验 pdf ,表示为 p(θ|D) 。这种分布表示在考虑了观察到的数据和先验知识之后,我们有多强烈地相信每个参数值是产生我们的数据的参数值。

先验 p(θ) 也是分布,通常与后验分布同类型。我不会深入细节,但当先验分布与后验分布匹配时,它被称为共轭先验,并带来许多计算上的好处。我们的例子将使用共轭先验。

让我们再一次回到树高的问题上来。除了徒步旅行者记录的 15 棵树之外,我们现在有了过去 10 年的树高的方法。

过去 10 年的树高。图片作者。

假设今年的树高应落入所有前一年的分布中,我们的先验分布为高斯分布,μ=159.2,σ =9.3。

剩下的就是计算我们的后验 pdf。对于这个计算,我假设固定的σ = σ _MLE = 11.27。在现实中,我们不会以这种方式解决贝叶斯估计,但不同维度的高斯相乘,如我们的似然和先验,是极其复杂的,我相信在这种情况下简化计算足以理解这一过程,并且更容易可视化。如果你想要更多关于如何执行完整计算的资源,请查看这些 两个链接。

将单变量似然和先验相乘,然后将结果归一化,我们最终得到μ=155.85 和σ =7.05 的后验高斯分布。

似然、先验和后验分布。作者图片

这就是贝叶斯估计的结果。如您所见,后验分布考虑了先验和可能性,以找到两者之间的中间点。根据新的观测数据,当前的后验概率成为新的先验概率,并且用新数据给出的可能性计算新的后验概率。

预言

我们有模型来描述我们的数据,那么我们能用它们做什么呢?这些模型最重要的用途是对看不见的未来数据进行预测,这基本上告诉我们一个观察结果有多大可能来自这个分布。我不会明确地为我们的例子进行计算,但是如果你想自己做的话,公式在下面。

最大似然预测

MLE 预测。图片作者。

最大似然预测利用密度函数中潜在变量的预测来计算概率。例如,在高斯情况下,我们使用(μ,σ)的最大似然解来计算预测。

贝叶斯预测

贝叶斯预测。图片作者。

正如您可能猜到的那样,贝叶斯预测稍微复杂一些,它使用后验分布和随机变量θ的分布来产生新样本的预测。

结束语

什么时候用 MLE?贝叶斯估计?

我们已经看到了两种参数估计方法之间的计算差异,现在一个自然的问题是:什么时候我应该使用一种而不是另一种?虽然在选择方法时没有硬性规定,但我希望你可以使用以下问题作为粗略的指导方针,引导你朝着正确的方向前进:

  • 您正在处理多少数据?

最大似然估计只依赖于观察到的数据的结果,众所周知,当数据最少时,它很容易出现偏差。考虑一个实验,你掷一枚硬币三次,每次都是正面朝上。虽然你知道一枚公平的硬币有 50%的机会正面朝上,但最大似然估计告诉你 P(正面)= 1,而 P(反面)= 0。在观测数据稀疏的情况下,贝叶斯估计结合了先验知识,例如知道一个公平的硬币是 50/50,可以帮助获得更准确的模型。

  • 你对你的问题有可靠的先验知识吗?

正如我刚才提到的,在某些情况下,先前的信念可以让你的模型受益。然而,不可靠的先验可能导致高度偏差模型的滑坡,需要大量的观察数据来补救。确保如果你使用先验知识,它们被很好地定义,并且 包含与你试图解决的问题相关的洞察力 。如果你不确定你的病史的可靠性,最大似然法可能是一个更好的选择,尤其是如果你有足够多的数据。

  • 您的计算资源有限吗?

本文中一个反复出现的主题是贝叶斯计算比最大似然估计更复杂。凭借现代的计算能力,这种差异可能无关紧要,但是如果您发现自己受到资源的限制,MLE 可能是您的最佳选择。

一些有趣的属性

最后,已经讨论了 MLE 和贝叶斯估计之间的许多差异,我只想提供这两种方法之间的一些有趣的联系。

  1. 如果贝叶斯先验在所有值上是一致的(“非信息先验”),贝叶斯预测将非常相似,如果不等于,则 MLE 预测。
  2. 如果贝叶斯先验是明确定义的,并且在所有点上都不为零,那么,随着观测数据量接近无穷大,MLE 和贝叶斯预测将收敛到相同的值。

仅此而已!如果你读到这里,感谢你的阅读。感谢任何反馈!

  • 链接到代码

最大子阵问题和 Kadane 算法

原文:https://towardsdatascience.com/maximum-subarray-problem-and-kadanes-algorithm-4cb1ce91be72?source=collection_archive---------21-----------------------

你想从哪里开始,想走多远?

动机

最大子阵列问题是在一维阵列中寻找具有最大和的连续子阵列的问题。在我看到它的一个解决方案之前,我从未想过要写一篇关于这个问题的文章——Kadane 的算法。这个算法打破了我连续几个月不写任何东西的记录。谢谢卡丹你优雅的解决方案!

股票市场跳水

我给你 ABC 公司未来三十天的完美股市预测。但是有一个条件——你只能买卖一次股票。就是这样。你不能做一个以上的“买卖”交易。哪一天买股票最合适?你什么时候需要卖掉它们来获得最大利润?我想,这些可能是你在想的问题。如果是这样,你很快就会变得富有。;)

这个有利可图的问题实际上可以归结为“最大子阵列问题”,我们稍后将会解决这个问题。

克里斯·利维拉尼在 Unsplash 上的照片

从类比一般问题

预报有效期为 30 天。你可以在第一天、第二天或者第一天卖出。当你积累了最大利润时,你就在当天卖出。

所以,更基本的问题是:
你应该在哪一天卖出股票?

让我们找出你在第一天卖出能获得的最大利润。同理,第二天,第三天,以此类推。那么,“全局”最大利润就是那三十个最大利润中的最大值。最大利润达到“全球”最大值的那一天,就是我们应该卖出股票的那一天。

所以,一般问题可以写成:
求以第一个元素结尾的子数组的最大和,以第二个元素结尾的子数组的最大和,以此类推。然后找到最大值中的最大值,这成为“全局”最大值。

动态编程?

动态编程是一种算法问题解决技术,通过将问题分解成更简单的子问题来解决问题。它利用了问题的最优解依赖于子问题的最优解这一事实。可以用动态规划来解决这个问题吗?事实上,是的,我们可以。让我们看看如何…

假设我们知道结束于第 (i-1)S(i-1) 位置的子阵列的最大和。

现在,要找到结束于第 i 个位置 (S(i)) 的子数组的最大和,只有当 S(i-1) 大于 0 时,将 S(i-1) 加到第 i 个元素才有意义。否则, S(i) 就是好的存在本身(我指的只是第 i 元素)。

所以,递归关系是:

S(i) = A(i) + S(i - 1),if S(I-1)>0 else A(I) 其中 A(i) 是第 i 个元素。

这是卡丹的算法。

Python 解决方案

这个问题的 Python 解决方案简单明了。

使用循环不变量证明正确性

对于迭代算法,循环不变量可以作为正确性的证明。Kadane 的算法是一种迭代算法,所以我们使用循环不变量来证明其正确性。

循环不变量是对循环的每一步都成立的条件。对于成为循环不变量的条件,它应该在初始化维护终止阶段保持为真。初始化表示循环的开始。在循环的开始,我们看到数组以第一个元素结束。以第一个元素结尾的数组就是第一个元素本身,而且是最大值(平凡)。对于维护,该条件应适用于随后的每个步骤。在每一步中,只有当先前的最大值大于零时,我们才添加先前的最大值。否则,我们只将总和作为当前元素。这使得当前最大值成为目前为止的最大值(假设结束于当前值)。因此,维护条件也得以保持。终止是程序结束执行的点,重复遵循维护条件,该条件在终止期间也成立。因此,该条件是循环不变量。由于条件是循环不变的,我们对 Kadane 算法的正确性进行了证明。

这个问题的有趣历史

这个问题是由 Ulf Grenander 在研究最大和 2D 子阵列问题时首次提出的。为了深入了解 2D 问题的结构,他提出了 1D 版本的问题,现在称为最大子阵列问题。格雷南德自己推导出了 O(n ) 时间算法,提高了 O(n ) 的蛮力运行时间。迈克尔·沙莫斯听说这个问题后,连夜想出了 O(nlogn) 算法。然后,沙莫斯描述了这个问题及其在 CMU 的历史。Kadane 是 Shamos 研讨会的与会者之一,他设计了一分钟内的时间算法。这是一场更好算法的竞赛!哇:)

应用程序

最大子阵列问题有几个应用。一些众所周知的应用是基因组序列分析和计算机视觉。它们用于基因组序列分析,以识别蛋白质序列的重要片段,如富含 GC 的区域和高电荷区域。在计算机视觉中,它们被用于检测位图图像中最亮的区域。

有趣的问题,优雅的解,重要的应用,最大子阵列问题,卡丹算法。这是迄今为止我最喜欢的算法之一。我会写更多关于数据科学中重要算法的文章。敬请期待!

五月版:关于可解释人工智能的问题

原文:https://towardsdatascience.com/may-edition-questions-on-explainable-ai-6968e9ac1ccf?source=collection_archive---------23-----------------------

月刊

随着机器学习模型渗透到几乎每个知识领域,实际理解 ML 系统做什么开始变得有问题。

现代舞蹈家卡洛斯·奥赫达,当地艺术家和卡洛斯·穆根的朋友【玛塔·雷古拉。经允许后发布。

许多论文、博客和软件工具以一种准数学的方式呈现了可解释性和可解释性,但是……可解释性和可解释性的含义有一个规范的定义吗?甚至我们如何评价解释?

机器学习算法肯定不能留给自己,在野外跑。问题是,我们作为人类,如何理解超越人类性能的算法?

因此,对于这个月刊,我们决定突出一些 TDS 作者想让你知道的最好的博客和播客。无论你是工业界的数据科学家,学术界的研究人员,还是学生,或者只是一个好奇的人,我都建议借此机会反思机器学习对社会的影响以及对可解释性的潜在需求。

享受阅读吧!

《走向数据科学》的编辑助理 Carlos Mougan&Marie-Sklodowska-Curie 研究员。

脸书 AI 负责人

播客 | Youtube

脸书经常部署推荐系统和预测模型,每天影响着数十亿人的生活。这种影响力伴随着巨大的责任——除了其他事情之外,开发符合道德、公平和良好表征的人工智能工具的责任。

人工智能必须是可理解的才是道德的吗?

播客 | Youtube

随着人工智能系统变得越来越普遍,人们开始更多地关注它们的伦理含义。

通过找到“几个重要原因”,将可解释的人工智能带到下一个层次

一个有效的可解释的人工智能应该旨在发现“重要的少数”原因,而不是“琐碎的许多”事件。下面是如何用 Python 实现的。

塞缪尔·马赞蒂——8 分钟

人工智能和机器人学中对可解释性的需求

可解释的人工智能:通向新未来的大门。

通过 Pier Paolo Ippolito — 5 分钟

您没有充分利用 SHAP 值——特征组和相关性

你的模型是你数据的透镜,SHAP 是它的望远镜。

埃斯特万·乌亚尔·帕迪洛斯·维埃拉

打开黑盒:如何利用可解释的机器学习

使用 PDP、LIME 和 SHAP 制定可解释的决策,为利益相关方创造价值。

由马腾 Grootendorst — 9 分钟

可解释的助推机器

像梯度推进一样精确,像线性回归一样可解释。

罗伯特·库伯勒博士 11 分钟

高级排列解释预测的重要性

将可解释性带到下一个层次,保持简单性。

到马尔科·塞利亚尼 — 6 分钟

用 ML-interpreter 理解机器学习黑盒

一个自动解释 XGBoost 等算法决策的 web 应用程序

由汉娜·韩嫣 — 8 分钟

解释“黑箱”ML 模型——SHAP 的实际应用

在真实数据集上训练“黑盒”GBM 模型,并使其可以用 SHAP 解释。

通过 Norm Niemer — 5 分钟阅读

为什么可解释的人工智能是人类生存需要的 5 个重要原因

什么是可解释的人工智能(XAI),为什么我们在人工智能系统中寻求可解释性和可解释性?

由奥尔罕·g·雅伦提供— 8 分钟

新播客

  • Josh Fairfield — 人工智能在进步,但法律能跟上吗?
  • 梅勒妮·米切尔— 来自人工智能的存在主义风险:一个怀疑的视角
  • 瑞安·凯里— 你的人工智能想要什么?
  • 李岩— 全球人工智能慈善事业的惊人挑战

我们也感谢最近加入我们的所有伟大的新作家内斯林·斯法尔、阿维谢·巴尔特、林赛·M·佩廷吉尔、史蒂夫·阿提拉·科皮亚斯、妮娜·赫里斯托佐娃、基肖尔·戈帕兰、普拉蒂克·辛格、让-巴蒂斯特·夏罗、达斯汀·斯图尔特 卡比尔·纳吉查,法比奥·奥利维拉,马蒂亚斯·格鲁伯,穆罕默德·礼萨·萨利希,西奥,加布里埃尔吉林,特拉维斯·库珀,卢卡·卡尼亚托,伊内斯·李,罗伯特·祖扎尔, 凯蒂·黄,查克·厄特巴克,德尼莎·布莱克伍德,阿雅·斯潘塞,黄思兴,奥斯卡·古德洛,费德里科·比安奇,克里斯汀·温特,加布里埃尔·埃尔姆森, 我们邀请你看看他们的简介,看看他们的工作。

愿预测与你同在:Scalecast Pt 简介。2

原文:https://towardsdatascience.com/may-the-forecasts-be-with-you-introducing-scalecast-pt-2-692f3f7f0be5?source=collection_archive---------31-----------------------

在这部激动人心的续集中,我们比较了 Prophet、SilverKite 和 Scikit-Learn 的预测准确性

由托拜厄斯·科奈尔在 Unsplash 上拍摄的照片

欢迎来到 Scalecast 概述系列的第 2 部分,scale cast 是可伸缩、最少代码预测的新希望。第 1 部分概述了一个系列的 10 个模型的预测结果;第 3 部分将展示库在有差异和无差异系列之间移动是多么容易。在这一部分中,我们对 108 个时间序列分别应用了 11 个模型。这样,我们可以看到哪些模型最常产生最佳结果。

在这篇文章的结尾,将会揭示以下内容:

  • 平均而言,Prophet 模型返回的误差指标最低
  • Scikit-learn 的线性回归模型最常被认为是“最佳”模型
  • 通过使用 11 个不同的模型,我们得到了比只使用一个模型更好的预测结果

该分析使用了鳄梨数据集。在 GitHub 上查看 scalecast 并给它一颗星:

https://github.com/mikekeith52/scalecast

pip install --upgrade scalecast

让我们从库导入和将数据集加载到 pandas 数据框架开始:

import pandas as pd
import numpy as np
import pickle
from tqdm.notebook import tqdm as log_progress
from ipywidgets import widgets
from IPython.display import display, clear_output
import seaborn as sns
import matplotlib
import matplotlib.pyplot as plt
from scalecastdev.Forecaster import Forecasterdata = pd.read_csv('avocado.csv',parse_dates=['Date'])
data = data.sort_values(['region','type','Date'])

我们使用的数据集测量了美国不同地区的不同鳄梨销售量。它也分为传统和有机鳄梨类型。大多数系列包括从 2015 年 1 月到 2018 年 3 月的完整周数据,但也有少数系列缺少日期。因此,我们使用pandas.date_range()函数返回每个系列的每个可用日期。我们用 0 填充缺失的日期,假设它们缺失是因为那几周没有销售。这可能不是一个合理的假设,但它影响最小系列,并且对于这个例子来说是有效的。我们将结果数据存储到一个scalecast.Forecaster()对象中,并将每个对象放入一个字典中:

这留给我们 108 个系列,每个系列有 168 周的历史数据。我们可以查看最后加载的系列的部分自相关图:

f.plot_pacf(diffy=True,lags=26)
plt.title(f'{f.type} {f.region} PACF Plot')
plt.show()

作者图片

这显示了在 1 和 3 处的一些显著滞后,然后在 22 和 23 处再次出现。我们可以添加 3 个自回归项,但 22 和 23 似乎是随机的,可能不适用于所有序列。让我们用 26 作为季节性滞后,半年。让我们添加“周”、“月”和“季度”回归变量,像上次一样,选择正弦/余弦转换代替这些回归变量的原始整数。我们还添加了一个“年”变量和时间趋势。

一些系列可以是静止的;有些可能不是。我们不检查每一个,我们将信任关于是否区别一个系列或保持它的水平的扩充的 Dickey Fuller 测试。

对于我们的维持期和预测期,我们使用:

  • 为期 26 周的测试
  • 13 周的验证期(针对调整模型)
  • 52 周的预测长度

实际上,模型预处理如下所示:

现在来预测一下。我们使用下面代码第一行中指定的 9 个模型。我们还使用了两个组合模型:所有模型的加权平均值和根据验证过程表现最好的 5 个模型的简单平均值。不要忘记在你的工作目录中放一个 Grids.py 文件。

现在我们有了结果,每个模型有四个误差/精度指标可以比较:MAE、RMSE、MAPE 和 R2。所有这些都可以在样本数据、测试集数据以及水平和非水平结果中获得。

MAPE 提供了一个易于解释的指标,有利于跨时间序列的比较。根据我们预测的数据范围,1000 的 RMSE 可能意味着非常不同的事情;MAPE 是实际数据与预测值之间的一个百分点的差异,与规模无关。它的缺点是,当实际数据中有 0 时,它不进行评估,并且在处理非常小的值时,可能会产生误导。出于这个原因,我们选择使用LevelTestSetMAPE指标进行比较,该指标在所有鳄梨系列中都得到了公平的评估。然而,观察结果如何随着不同的度量标准而变化是很有趣的,我们鼓励你自己去做。

既然我们已经选择了一个误差度量,让我们将所有模型统计数据写入一个 CSV 文件:

我们发现了以下信息:

  • 所有系列的 MAPE 指标中值为 0.24
  • 在任何给定系列中被指定为“最佳”的模型中,MAPE 中值为 0.16
  • 一些模型在测试集数据上表现很差,导致误差分布严重偏右

作者图片

  • 平均而言,先知和银雀模型返回的 MAPE 最低
  • 一些模型受到指数趋势建模的影响,这可能导致它们的 MAPEs 非常高
  • 转变可以解决这个问题。

作者图片

  • 简单平均组合模型得出了最低的中值 MAPE

作者图片

  • mlr 模型最常被指定为给定系列中的最佳(23 次)
  • 每一个被利用的模型都被至少三次指定为最佳

作者图片

最初,这篇博文在这一部分有更多的结果报告,但随着 scalecast 的更新,我相信现在有更好的方式来表达这些结果。请参阅新的介绍性笔记本及其关于比例自动化预测的部分,了解与此处所示类似的示例。

结论

我们对 108 个系列应用了 11 个模型,总共进行了 1,188 次预测。我们比较了模型性能,并展示了一种可扩展的预测方法,该方法使用最少的代码,具有灵活性和动态性,并能产生易于检查的结果。谢谢你的关注,请关注第三部!

mBART50:可扩展多语言预训练的多语言微调

原文:https://towardsdatascience.com/mbart50-multilingual-fine-tuning-of-extensible-multilingual-pretraining-70a7305d4838?source=collection_archive---------28-----------------------

训练 50 种语言的多语言机器翻译,对单语数据进行预处理

照片由 Unsplash 上的 Soner Eker 拍摄

注意:这是三篇系列文章中的第二篇。
第一部分:双语机器翻译的海量预训练
第二部分: mBART50:可扩展的多语言预训练的多语言微调
第三部分:多阶段训练的多语言语音翻译

在上一篇文章中,我们看到了预训练的 mBART 如何用于训练强大的双语机器翻译模型,以及它们如何对低资源和中等资源的语言特别有竞争力。该论文为更多关于预训练编码器-解码器模型的问题铺平了道路:

  1. 是否可以扩展预训练语言集,以打开更多下游微调的可能性?
  2. 既然预训练是多语言的,为什么微调不也是多语言的?

这些都是在【1】中回答的问题,其贡献如下:

  1. 将 mBART 扩展到 50 种语言,而不损失双语微调的准确性
  2. 建议对多对英语、英语对多和多对多(通过旋转)进行多语言微调,增益超过所有基线。多对英语方向的收益尤其大
  3. 为 ML50 基准测试提供标准化的培训/开发/测试划分,涵盖低、中、高资源语言,共 2.3 亿个平行句子。

多语言微调

多语言微调过程并不是特别复杂。
首先,所有语言方向 s- > t 的数据被收集在单个训练集中。每种语言方向的可用文本数量极其多样,涵盖了从 4K 到每种语言超过 1000 万个句子对的全部范围。因此,每个批次的数据都是使用语言说明上的温度采样从不同的组中选择的。如前一篇文章[ 2 ]所述,温度采样产生一个更平衡的分布,因此低资源语言对以更高的概率被选择。

多语言微调在三种不同的设置中进行了实验:多对英语、英语对多和多对多。最后一个是结合另外两个设定的数据训练出来的,这样英语就是一种支点语言。

25 种语言的结果

第一个实验旨在在用于训练 mBART 的 25 种语言上比较多语言微调与双语微调和从零开始的多语言。

结果显示,在多对英语方向上,多语言模型明显优于所有双语模型。另外,**多语言微调明显优于《从零开始》多语言,**除了超过 1M 句子对的语言方向,优势相当小。另一方面,对于资源较少的语言(7K-30K 句子对),多语言微调相对于双语从头开始的优势是一个突出的 18.03 平均 BLEU 分,这使得玩具和可用模型之间存在巨大差异。相比之下,第二好的模型是从零开始多语言,提高了 14.63,而双语微调在从零开始双语基线提高 10.80 BLEU 点之后停止。

英语对多设置呈现了一个完全不同的场景。
首先,很重要的一点是,本文中描述的结果与表格并不完全匹配,与之前的结果相比,表格受到的关注较少。事实上,除非我遗漏了什么,否则以粗体标记的值是一行中最好的值,但实际上并不是这样。

然后,通过只阅读数字而不阅读文本,我们可以看到,多语言微调仅在大于 10M 的句子对组上取得最佳结果(严格来说),而对于其他组,从零开始的多语言英语对多一直更好。不幸的是,作者未能解释为什么会发生这种情况,但它代表了一个有趣的结果供进一步研究。

而且,在资源最少的组上,多对多模型与多语言微调只比双语 从零开始基线好 0.90,而多语言从零开始比基线有 7.9 BLEU 分的优势。多语言微调模型的改进在一对多场景中要好得多,但仍然比从零开始训练的对应模型稍差(7.6 对 8.1)。

虽然作者未能解释一对多的违反直觉的结果,即从头训练比微调更好,但他们非常清楚地解释了他们的最佳情景。

多对英语方向如此强大是因为该模型观察 49 个语言方向的英语目标侧,并且该模型仅在英语上被评估。另一方面,在英语对多的方向上,可能没有足够的数据来学习所有语言方向的良好条件概率。

我认为有趣的是,在整个数据规模范围内,多语言系统显示出相对于双语基线的改进。通常,对于高资源语言,多语言模型实际上比双语模型更糟糕。我希望看到更多的实验,如果 mBART 解决了这个问题,如果是的话,为什么。

mBART50:扩展预训练模型

在上一篇文章中,我们已经看到 mBART 对于微调至少有一种**看不见的语言的语言对也是有用的。**虽然结果不如预训练语言好。当看不见的语言在源端时,退化尤其严重。

因此,预训练语言将可能的下游任务约束到这些语言。然后,为了在新语言中有效地使用 mBART,重要的是要有更多的单语数据可以添加到预训练模型中。然而,考虑到训练它所需的计算和时间资源,最好不要从头开始。

为了克服这个问题,作者提议用 25 种额外的语言来扩展最终的 mBART25 检查点,有效地将它们的数量翻倍。这个过程在概念上很简单,但很有效。他们首先在嵌入层中添加 25 个随机初始化的语言标记,每个标记对应一种新语言。然后,它们将最初的 25 种语言的单语训练数据与 25 种新语言的数据连接起来,并使用得到的训练集来继续对保存的检查点进行训练。他们还重复使用相同的句子片断模型进行分词,因为它是在 100 种语言上训练的,而不仅仅是在最初的 25 种语言上。此外,作者执行了一些数据过滤,以防止训练和开发/测试集之间的任何重叠,并过滤掉 fasttext 未识别为正确语言的句子。

这种新的预训练模型的多语言微调带来了巨大的改进,特别是对于资源较少的语言。然而,改进主要是在多对一的方向上,而一对多模型的性能通常不如双语微调,除非是资源非常少的语言对。

此外,当对原有的 25 种语言执行 mBART50 的双语微调时,结果与微调 mBART25 获得的结果非常相似。更多的语言不会导致任何语言对的性能下降

开放式问题

关于 mBART50 的多语言微调的工作确实很有趣,结果也很令人兴奋,但这为进一步的研究留下了空间。

第一个问题,也是研究的对象,涉及如何更好地扩展预训练模型。在该论文中,新旧数据被合并到一个新的训练集中,以获得一个新的模型,该模型不降级于旧的语言,但它假设访问旧的训练数据。当我们无法访问原始数据时,如何才能获得类似的结果?

然后,作者表明 mBART25 和 mBART50 对于最初的 25 种语言在双语微调上获得相同的结果,并因此推断出模型质量在那些语言上没有降级。然而,仅基于 mBART 论文,我们不知道下游任务的翻译质量如何与预训练模型的任何质量度量相关。深入探索这一点会很有趣,但是对于许多实验室来说,进行实验的成本太高了。

最后,给定语言的数量,论文中显示的结果是许多语言的平均值。从平均值中,我们可以看到,与双语微调相比,多对一有很大的改进,而一对多则略有下降。然而,关于个别语言的结果显示了一个更复杂的情况。虽然对于多对一模型的基本上所有语言来说,这些改进都是显著的,但是对于一对多模型来说,稍微低于零的平均性能下降是对高度变化的结果进行平均的结果。对于单一语言,与基线的差异可能高达+- 4 个 BLEU 点!这是一个有趣的问题,以了解如此高波动的原因,以及是否有可能将更多的语言推向积极的一面。

结论

mBART50 的多语言微调是另一个重要的章节,它显示了如何训练强大的多语言模型,这些模型也可以用于资源非常少的语言对。

多语言模型比生产中有过多的模型具有维护优势。对于资源最丰富的语言来说,这种优势总是以较低的质量来换取。使用 mBART50 后,似乎不再出现降级现象。实验只在每个语言方向的单一测试上运行,但如果在更多测试数据上得到证实,那就太好了。

在下一篇文章中,我将分析脸书在 IWSLT 2021 中提交的多语言语音翻译共享任务,它明显优于其他参与者。这个解决方案由 wave2vec2.0 提供支持,用于处理语音输入,显然 mBART 用于翻译端。

参考

[1]唐,余庆等,“具有可扩展的多语言预训练和微调的多语言翻译” arXiv 预印本 arXiv:2008.00401 (2020)。

[2] Arivazhagan,Naveen 等,“野外大规模多语言神经机器翻译:发现与挑战。” arXiv 预印本 arXiv:1907.05019 (2019)。[ 链接

中等会员

你喜欢我的文章吗?你是否正在考虑申请一个中级会员来无限制地阅读我的文章?

如果您通过此链接订阅,您将通过您的订阅支持我,无需为您支付额外费用https://medium.com/@mattiadigangi/membership

MCMC:可视化介绍

原文:https://towardsdatascience.com/mcmc-a-visual-introduction-38e1d6131e86?source=collection_archive---------29-----------------------

随机抽样方法

直观解释两种基本采样算法

图片由metarworks在 shutterstock 上拍摄

介绍

在我以前的文章中,我谈到了卡尔曼滤波器。虽然卡尔曼滤波器对于具有高斯噪声的线性系统是最佳的,但是当给定非线性系统时,该算法失败。

那么非线性系统呢?

非线性系统的问题在于,在最佳算法的推导中涉及的积分是不可处理的。这些积分阻止了我们评估后验概率、期望值和方差,所有这些都是在构建最优模型时涉及到的。

我们能解决这些积分吗?

虽然在非线性系统中,后验概率通常是难以处理的概率密度函数(PDF),但它们仍然可以被评估。采样方法的目标是间接采样难处理的 PDF。通过随机抽样,我们可以对样本取有限和来近似上述积分。

采样方法通过对难以处理的 pdf 进行间接随机采样,然后对其进行有限求和,来近似非线性系统解析解中涉及的积分。

拒绝抽样

拒绝抽样是最简单的抽样技术之一。有了它,我们可以重建难以处理的 pdf。我们来看一下算法。

最初,我们有一个目标 PDF,我们不能从 PDF 中采样,但是,我们可以在任何时候评估它。我们试图从这个棘手的 PDF 中抽取样本。我们采用剔除抽样:

  1. 设计一个提案 PDF,初步猜测我们的目标 PDF
  2. 缩放提案 PDF,它必须在每一点上都大于目标
  3. 提案样本 PDF
  4. 评估目标 PDF 的样本
  5. 根据接受概率接受或拒绝样本
  6. 重复 N 个样本

作者图片

在上图中,橙色部分是我们希望从中取样的目标 PDF。提案 PDF 在每一点上都是大于目标值的正态分布。

在拒绝抽样中,我们首先对提案 PDF(来自上面的蓝色分布)进行抽样,我们可以在两个 PDF 上评估这个样本。然后我们决定是否保留样品。

接受概率等于在目标 PDF 评估的随机样本和在建议 PDF 评估的随机样本之间的比率。在上图中,接受随机样本的概率是绿线的长度除以黑线加上绿线的长度。对于上面显示的随机样本,您得到的接受概率约为 25%。

作者图片

重复多次会产生所示的分布。如您所见,尽管目标 PDF 难以处理且无法采样,但通过拒绝采样,我们有效地从目标 PDF 中进行了采样。

剔除取样的问题是很多样本被剔除。因此,该过程最终相当昂贵。在我的例子中,超过五分之三的数据被拒绝。幸运的是,还有其他更有效的采样方法。

大都会-黑斯廷斯算法

在这一节中,我将讨论 Metropolis-Hastings 算法。让我们一步一步地研究这个算法。

我们从一个棘手的目标 PDF 开始,就像以前一样。目标是间接从这个 PDF 中取样。

作者图片

  1. 首先,我们随机抽取一个样本,并在该样本上评估目标 PDF,这在上图中显示为蓝色星号。
  2. 然后,我们在这个新绘制的样本(蓝色曲线)上构建一个提案 PDF。
  3. 我们从这个新的提案 PDF 中抽取样本(获得绿色三角形)。

到目前为止还不错,现在变得更难了…

作者图片

4.我们现在将我们的提案 PDF 镜像到新绘制的样本上(绿色曲线,它是一个镜像的提案 PDF,平均值在绿色三角形上)。

5.然后在反射的 PDF(蓝色三角形)上评估我们的初始样本,在目标 PDF(绿色星号)上评估新绘制的样本。

一旦完成,我们就可以决定是否接受或拒绝新的样品。

接受概率给出了样本被接受的概率。最需要注意的是新样本和当前样本的目标 PDF 之间的比率(上图中开始之间的比率)。如果这个比率很高,这意味着我们正在向一个峰值移动,我们正在向目标 PDF 攀升,因此我们希望这个新样本更有可能被选中。

建议的 pdf 之间的比率(三角形之间的比率)给出了当从一个样本改变到另一个样本时我们在空间中移动的平滑程度的度量。注意,如果建议 PDF 是对称的(就像我们的例子),那么这个比率等于 1。这是 Metropolis-Hastings 算法的一个特例,称为 Metropolis 算法。

如果样品被接受,我们保留它并重新开始。如果样品被拒绝,我们保留之前的样品。这使得 Metropolis-Hastings 算法比拒绝采样更有效,当拒绝时,它仍然增加样本。

MCMC 和后验概率

在贝叶斯定理中,当似然和先验为高斯时,后验概率有解析解(见我的文章这里)。然而,当这些不是高斯分布时,几乎可以肯定分母中的积分无法求解。

以上是 Metropolis 算法中样本的接受概率,其中建议 PDF 是对称的。你可以看到它和贝叶斯定理中的后验概率的相似性。通过使用 Metropolis 算法进行采样,我们对后验数据进行采样。对于无限个样本,我们可以从这些随机样本中完美地逼近后验概率密度函数。

结论

在本文中,我将介绍两种随机抽样方法,并解释它们的重要性。

采样方法可用于对难以处理的 pdf 进行采样。例如,这些可以是后验概率表面,其中似然和后验不是高斯的。

拒绝采样虽然非常简单,但效率极低,因为它最终会拒绝大多数样本。MCMC 不拒绝任何样本,使得算法更加经济。

支持我

希望这对你有所帮助,如果你喜欢,你可以 关注我!

你也可以成为 中级会员 使用我的推荐链接,获得我所有的文章和更多:https://diegounzuetaruedas.medium.com/membership

你可能喜欢的其他文章

可微发电机网络:简介

傅立叶变换:直观的可视化

MDS:多维尺度 Python 中降维的聪明方法

原文:https://towardsdatascience.com/mds-multidimensional-scaling-smart-way-to-reduce-dimensionality-in-python-7c126984e60b?source=collection_archive---------3-----------------------

机器学习

用一个详细的 Python 例子解释了 MDS 算法

降维。Gif by 作者。

介绍

处理大数据会带来许多挑战,其中之一就是由于维数过高而导致模型的效率和性能下降。

幸运的是,许多降维技术可以帮助我们克服挑战,让我们能够删除“不太重要”的数据。

在本文中,我将深入研究度量多维标度(MDS ),让您了解它是如何工作的,以及如何将其用于您的数据科学项目。为此,我涵盖了以下主题:

  • 多维标度的类型(MDS)
  • MDS 在机器学习算法领域的地位。
  • 公制 MDS 实际上是如何工作的?
  • 在 Python 中如何使用 MDS 来降低数据维数?

多维标度的类型(MDS)

有两种主要类型的 MDS,公制(经典)和非公制。虽然两者都旨在找到高维数据的最佳低维表示,但它们的不同之处在于它们处理的数据类型。

  • 公制(经典)MDS —也称为主坐标分析(PCoA)。确保不要将其与主成分分析(PCA)混淆,PCA 是一种独立但相似的技术。
    公制 MDS 试图通过使用每对点的
    几何坐标计算它们之间的距离来模拟数据的相似性/不相似性。这里的关键是使用线性标尺测量距离的能力。例如,10 个单位的距离将被认为是 5 个单位的距离的两倍。
  • 非公制 MDS —设计用于处理序数数据。例如,您可能要求客户对您的产品进行 1 到 5 分的评级,其中 1 表示糟糕,5 表示令人惊叹。在这里,评分为 2 的产品是 而不是 一定是评分为 1 的产品的两倍。重要的是顺序(1 < 2 < 3 < 4 < 5)而不是绝对值。在这种情况下,你会使用非公制 MDS。

正如简介中提到的,在本文中,我将重点关注公制 MDS 。不过,请注意,Sklearn 用 Python 实现了 MDS 算法,让您可以轻松地在公制和非公制方法之间切换。因此,对于非度量方法,您也可以使用本文末尾提供的 Python 示例。

机器学习算法领域中的多维标度(MDS)

事实是,机器学习算法比我们任何人都能列出的要多得多。然而,我试图收集和分类一些最常用的,你可以在下面的互动旭日图中看到。确保点击👇在不同的类别上对进行放大并揭示更多的

多维标度属于降维技术组中机器学习算法的无监督分支。

机器学习算法分类。由作者创建的交互式图表。

如果你喜欢数据科学和机器学习 ,请 订阅 获取我的新文章的邮件。如果你不是中等会员,可以在这里加入https://bit.ly/36Mozgu

公制多维标度(公制 MDS)实际上是如何工作的?

通常,度量 MDS 计算原始高维空间中每对点之间的距离,然后将其映射到较低维空间,同时尽可能保留这些点之间的距离。

注意,低维空间的维数可以由你选择。通常,人们会选择 2D 或 3D,因为它允许数据可视化。

因此,让我们来看看由公制 MDS 执行的高级步骤。我试图在这个解释中尽量少用数学,但完全避免是不可能的。

度量 MDS 算法使用的步骤

步骤 1 —算法计算每对点之间的距离,如下图所示。

三维空间中的点及其距离。图片由作者提供。

步骤 2 —已知原始距离,算法试图通过在低维空间中找到一组使应力值最小的坐标来解决优化问题。

压力成本函数。图片由作者提供。

可以使用多种方法来优化上述成本函数,例如 Kruskal 的最速下降法或 De Leeuw 的迭代优化法。然而,我这次不会深入研究数学,以保持这篇文章侧重于高层次的解释。

需要注意的重要一点是,上述两种方法都是迭代方法,有时会给出不同的结果,因为它们对初始起始位置很敏感。

然而,Sklearn 的 MDS 实现允许我们指定想要初始化进程的次数。最后,选择应力最低的配置作为最终结果。

**

在 Python 中如何使用 MDS 来降低数据维数?

抛开理论,让我们进入有趣的部分,在 Python 中使用 MDS 进行降维。

通常,您会希望将 MDS 用于高维数据,例如,手写文本或数字的图像。然而,我热衷于展示数据在之前和之后的样子。因此,我将使用 MDS 对更简单的数据,可以在三维和 2D 可视化。

设置

我们将使用以下数据和库:

  • Scikit-learn libraryfor
    1)创建数据供我们使用(make _ Swiss _ roll);
    2)进行多维缩放(MDS);
  • Plotly 用于数据可视化
  • 熊猫进行数据操作

让我们从导入库开始。

接下来,我们使用 Sklearn 的 make_swiss_roll 创建一些数据,并将其显示在 3D 绘图上。

交互式 3D 瑞士卷。图由作者提供。

确保通过旋转从每个角度探索上面的交互图。

表演 MDS

我们现在将使用 MDS 来将这个 3D 结构映射到 2d,同时尽可能地保持点之间的距离。请注意,瑞士面包卷的深度小于其高度和宽度。我们期望这一特征在 2D 图中得以保留。

注意,“n_components”告诉 algorihtm 你想要多少个维度。同时,如果您想使用非公制 MDS 而不是公制 MDS,可以将“公制”设置为“假”。此外,您可以指定使用“n_init”超参数初始化的次数。

上面的代码给出了这些结果:

MDS 结果。图片由作者提供。

我们可以看到新数组的形状是 2000 乘 2,这意味着我们已经成功地将其缩减为 2 维。此外,算法需要 64 次迭代才能达到最低的压力水平。

现在让我们绘制新的 2D 数据,看看它与原始的 3D 版本相比如何。

MDS 转型后的 2D 瑞士卷。图片由作者提供。

结果非常好,因为我们可以保留全局结构,同时不会丢失在原始深度维度中观察到的点之间的分离。

虽然这取决于我们想要解决的确切问题,但在这种情况下,MDS 似乎比 PCA(主成分分析)表现得更好。为了比较,下图显示了应用 PCA 变换后同一 3D 瑞士卷的 2D 表示。

PCA 改造后的 2D 瑞士卷。图片由作者提供。

正如你所看到的,PCA 给我们的结果看起来像是瑞士卷一面的图片,没有保留第三维的深度信息。

结论

当您希望保留高维数据的全局和局部结构时,多维缩放是一种很好的技术。这是通过保持较低维度中的点之间的距离尽可能类似于原始高维空间中的距离来实现的。

但是,如果您的分析需要您更多地关注全局结构,您可能希望使用 PCA。

*

或者,您可以探索 Isomap(等距映射),它结合了【k 最近邻】和 MDS,以更好地保存局部结构。

如果您有任何问题或建议,请随时联系我们。

干杯👏
索尔·多比拉斯

如果你已经花光了这个月的学习预算,下次请记得我。 我的个性化链接加入媒介是:

https://solclover.com/membership *

我对机器

原文:https://towardsdatascience.com/me-vs-the-machine-2621896f1361?source=collection_archive---------20-----------------------

照片由 Unsplash 上的静止不动

我能打败 PyCaret AutoML 吗?

我天生是一个好胜的人(或者说是后天培养的,你来选),我不禁想知道我是否能在机器学习方面击败 PyCaret 。只有一种方法可以找到答案——参加比赛!

规则

考虑到我有多么想打败机器,我的偏见已经几乎压倒一切,这意味着我需要建立一些基本规则,以便这是一场至少公平的战斗(我想 PyCaret 现在正在咯咯地笑)。以下是基本规则:

  • 向两个竞争者提供相同的数据
  • “开箱即用”设置将与 PyCaret 一起使用(至少给我一个机会,稍后会详细介绍)
  • 我不会用任何可笑的策略来挤出分数

问题是

我稍后参考的书中有一个非常简单的信用卡违约数据集,非常适合这个竞争,因为它是一个小集合(所以我不必等待拟合模型和进行预测),并且目标是不平衡的,这使它成为一个很好的测试。另外,我已经对这个数据集做了一些基本的建模;理论上,这是一个盲测,但我打赌我会修改我的原始作品。更重要的是,PyCaret 在他们自己的教程中使用了相同的数据集(我不确定他们的数据来自哪里,但它和我的一样,这才是最重要的)。

数据

我要先走了。我在数据上运行了 Pandas Profiling,这对我来说并不罕见,并为我的 EDA 和功能工程提供了有用的见解。

一些基础知识:

作者图片

BILL_AMT 变量(2–6)确实需要删除。但除此之外,最初的观点并没有揭示什么惊天动地的东西。

我不必担心丢失值,虽然我通常会考虑零(由 Pandas Profiling 标记),但当我最初处理这个数据集时,我没有对它做任何事情,为了忠实于我的原始工作,我将离开它。

AGE、LIMIT_BAL 和 BILL_AMT1 都有偏差。我应用了一个对数函数来归一化这些值。

我还删除了 ID 列,因为它对数据没有任何用处(不需要显示简单的 Python 代码)。

建模

我指定我的 X 和 y,使用一个简单的 train_test_split,然后创建一个逻辑回归模型(使用 sklearn.linear_model。线性回归)。

最后,我测试模型并输出一些度量。

战书已经放下了。你准备好迎接挑战了吗(特别是因为我并没有真的很努力)?

皮卡雷的回合

作为一个作家,好消息是我现在没什么好解释的。我安装了 PyCaret,然后做了一个默认的分类器设置(注意这里还有pycaret.regression)。如果你看看缺省值,PyCaret 也没有很努力(没有对不平衡数据进行修复,也没有进行规范化)。我认为 PyCaret 在玩“绳子是毒品”——我就是毒品。

之后运行模型就简单了。单行代码是:

最佳模型=比较模型()

老实说,从易用性的角度来看,这是非常惊人的。PyCaret 实际上只是在炫耀它对那行代码做了多少工作。看看那些模型和分数。干得好 PyCaret,干得好。

作者图片

但是如果我把注意力放在直接竞争的逻辑回归上,我得到了 .7962 ,PyCaret 得到了 .7766

作者图片

当然 PyCaret 生成了十二个附加型号其中七个碾压矿。所以虽然我赢了,但我绝对是以压倒性优势输了。

调整模型

对模型进行调优可能意味着很多事情,但通常涉及数据(重新)处理、特征工程和(特别是)超参数调优的混合。在 PyCaret 中,我可以隔离其中一个模型(例如,逻辑回归)并作为 PyCaret 来调整它。这需要长达两行的代码:

第二行代码为 PyCaret 生成了致胜的一击(请耐心等待,让我体会失败的痛苦):

作者图片

此外,PyCaret 用单行代码产生了一些非常好的视觉效果。

作者图片

作者图片

作者图片

如果所有这些还不够,雪上加霜的是,PyCaret 还评估了自己的结果。这也是用两行代码完成的。

这也有一个非常好的输出格式,显示预测和每个预测的分数。

作者图片

最后,PyCaret 有一个内置的工具来使用 MLflow 跟踪实验运行。是的,它只用了一行代码!mlflow ui就运行了。那一行代码在 http://localhost:5000 上启动了一个 web 服务器(注意:我展示的是我创建的一个“有缺陷”的 LR 模型实验,它的准确度分数较低,只是为了让自己感觉更好。)

作者图片

这变得越来越丑陋了…但这是 PyCaret 的荣誉。

我应该在“真实世界”中使用这个吗?

使用 PyCaret 可以轻松完成很多事情(比如实验跟踪和多模型比较)。我相信使用它比使用底层算法更相似,而不是不同——您必须理解和调整 PyCaret 的参数,这为您提供了几乎与它所理解的每个算法一样多的灵活性。但是,即使你不想这样做,使用基线,然后微调最好的算法是一个很好的方式来启动或验证你的工作。

换句话说,对我来说,我承认输给了 PyCaret,这确实是一个胜利,因为我因此成为了一名更好的数据科学家。

参考资料:

Python 预测分析实践:掌握从问题定义到模型部署的完整预测分析流程

[1] PyCaret,二进制分类教程(CLF101) —初级初学者 (2020),PyCaret on GitHub

[2] PyCaret,二进制分类教程(CLF102) —一级中级 (2020)。GitHub 上的 PyCaret

[3] Fuentes,a,《预测分析过程》。在Python 预测分析实践中:掌握完整的预测分析流程,从问题定义到模型部署 (2018),Packt 发布

均值估计:均值的中位数

原文:https://towardsdatascience.com/mean-estimation-median-of-means-6be322ef8d85?source=collection_archive---------24-----------------------

算术平均很无聊…而且经常错!这是你的选择。

弗兰基·查马基在 Unsplash 上拍摄的照片

如果我们把它分解,机器学习又回到了进行统计推断。推断是指根据一个未知分布生成的样本,对该未知分布做出陈述。因此,均值估计是统计推断和机器学习的基本问题之一。我们研究从一些未知分布中抽取的样本,旨在了解原始人口集中在哪里。一旦发现这一点,我们就可以继续学习更多关于数据生成过程的知识。

应用于样本的经验均值是总体均值最常见和最直接的估计量。它的易用性令人信服。

简单算术平均数

对于大多数学生来说,算术平均数肯定是他们在教育生涯中看到的第一个方程。那么这个估计量怎么可能是错的呢?嗯,看情况。

让我们看一个简单的例子。我们生成一个由标准高斯分布生成的总体。然后,我们从同一分布中随机抽取 100 个数据点,计算算术平均值作为总体平均值的估计值。不出所料,我们的估计接近完美。我们用 0.0004 的平方误差表示。

import numpy as np
import randomnp.random.seed(1813)population = np.random.normal(loc=0, size=1000, scale=1)
sample = random.choices(population, k=100)print(np.mean(population))
print(np.mean(sample))
print((np.mean(population)-np.mean(sample))**2)

所以让我们继续,总是使用算术平均值,对吗?我们测试并验证了它的性能。这是有道理的,因为每个人都在使用它,对不对?…当然不是!这种良好的性能是由于生成数据的分布非常干净。但现实远非高斯。让我们看一个稍微复杂一点的例子。

population = np.random.standard_t(df=1, size=1000)
sample = random.choices(population, k=100)

print(np.mean(population))
print(np.mean(sample))
print((np.mean(population)-np.mean(sample))**2

现在情况看起来很不一样。对于 Student-t 分布,我们表示误差平方为 5.806。样本 Student-t 分布的厚尾可能导致样本中出现大量极值。这损害了估计性能。那么我们如何解决这个问题呢?

均值估计量的中位数

均值中值(MoM)估计器是解决上述问题的一种方法。给定一个数据样本,MoM 估计器打乱数据点,然后将它们分成 k 组 m 个数据点。然后计算每组的算术平均值。最后,我们计算得到的 k 个算术平均值的中值。

平均值

这种技术通过分割数据并仅考虑结果子估计的中值来减少异常值对最终估计的影响。让我们看看这个改进的估计器在前面的例子中表现如何。

我们可以将同一示例的平方误差降至 0.730。这是对算术平均值 5.806 的重大改进。

然而,每种技术都有其弱点。MoM 取决于我们最初分割数据的方式。想象一下这样一种情况,数据中的所有极端值都被随机分类到一个单独的子组中。虽然不太可能,但作为数据科学家,我们绝不希望如此高度地依赖纯粹的机会。

你可以在我的回购里找到从头实现。

均值估计的排列不变中位数

众所周知,期望值是每个统计学家的朋友。因此,让我们将矩量法视为依赖于数据样本特定排列的随机变量。这种方法还有一个问题。为了精确计算这个估计量,我们需要计算每个可能的样本排列的矩量法。那就是 n 了!妈妈去计算。还不够性感!

简单来说,我们可以通过接近它的东西来估计实际的置换不变矩量法。为此,我们可以固定一个数字 N,作为我们要随机抽样的排列数。

近似的置换不变矩量法

根据我们的运行示例测试这个估计器,得到的误差平方为 0.332。成功!我们能够进一步提高我们的估计。是时候走出幼儿园,在实验方面变得更大胆一点了。

def _rob_median_of_means(seq, n):
    res = [_median_of_means(seq, 10) for _ in range(n)]
    return np.mean(res)

print(_rob_median_of_means(population, 10))
print(np.mean(population))
print((np.mean(population)-_rob_median_of_means(population, 10))**2)

你可以在我的回购里找到从无到有的实现。

更大规模的评估基准

虽然我们有了基本的了解,但我们需要更好地理解这些估计量的行为。因此,让我们观察他们的性能依赖于不同的样本大小和数据生成过程。这些图显示了引入的估计量的测试性能。给定各种各样的样本大小和分布形状来测量性能。算术平均值称为 EM。MoM 和置换不变 MoM 被称为 MoM 和 RMoM。此外,绿色曲线显示了标准高斯函数的估计性能,红色曲线表示了 Student-t 数据生成过程的性能。

均值估计基准

在我的 repo 中找到这个实验的从头实现。

结果产生了一些令人满意的见解。正如所料,( R)矩优于厚尾分布的算术平均值。毫不奇怪,算术平均值很难接近高斯分布。然而,我们可以自信地声明,( R)MoM 可以保持算术平均估计性能。

总之,均值估计是一个挑战,我们可以在实验室环境中很好地解决。然而,一旦分布不再正常,挑战可能会变得越来越困难。尤其是异常值可能会严重影响最常见的均值估计的性能。平均值代表了这个问题的许多选择之一。虽然矩量法有其自身的缺陷,但它确实在大多数不正常的情况下提高了估计性能。此外,计算复杂性是可控的数据排列的数量,我们希望包括在内。因此,你为什么不在你的下一个模型中尝试一下呢?

均值漂移和凸轮漂移目标跟踪

原文:https://towardsdatascience.com/mean-shift-and-cam-shift-object-tracking-f1c2c515b6bc?source=collection_archive---------14-----------------------

使用 OpenCV 和 Python 按颜色跟踪对象

凯文·穆勒在 Unsplash 上的照片

目标跟踪是计算机视觉最需要的功能之一。不幸的是,这也是计算机视觉最难实现的方面之一。

不仅需要识别对象,还需要足够快地识别它,以便在它移动时实时渲染。物体可能会改变方向或改变它与摄像机的距离,这使事情变得更加复杂。

虽然在计算机视觉中仍然是一个严峻的挑战,但均值漂移和凸轮漂移算法提供了一个简化的解决方案。这些方法主要使用颜色数据而不是结构数据,可以在正确的条件下有效地跟踪对象。

均值漂移背后的理论

均值漂移算法实际上是一个与聚类相关的更广泛的统计概念。与大多数其他聚类算法一样,均值漂移算法试图在数据集中寻找数据点或聚类高度集中的位置。

该算法在每个数据点放置一个核,并将它们加在一起,得到一个核密度估计(KDE) 。KDE 将有波峰和波谷,分别对应于数据点密度高和低的位置。

该算法获取数据点的副本,并在单次迭代中将这些点少量移动到它们最近的 KDE 峰。该算法将继续迭代移动这些点,直到这些点不再移动太多。这有助于简化数据并使聚类更加明显。

那么这和计算机视觉有什么关系呢?想象一道彩虹,如下图所示:

照片由 Isaac Quesada 在 Unsplash 上拍摄

图像可能被分解成 HSV 颜色空间,本质上把它变成一个大数据集。制作 KDE 时,天空中彩虹和蓝色的每一种颜色都会形成一个山峰。迭代移动点时,通过消除颜色之间的细微褪色,彩虹的每种颜色都会变得更加明显。因此,每个条纹变得更容易被计算机识别。

这个过程是图像分割的一种形式。

为了进一步理解这个概念,如果需要识别彩虹中的特定颜色,均值漂移算法可以使它更容易被看到。如果需要跟踪这种颜色,彩虹视频的每一帧都将实时接受该算法。

对于一个更现实的例子,想象一场网球比赛的俯视图。球场、球员和亮黄色的球都有一个 KDE 峰。如果球需要被跟踪,均值偏移算法将使亮黄色像素的质量更加明确,并在球在球员之间移动时在每一帧跟踪它。

凸轮换挡背后的直觉

在实现均值漂移算法之前,需要建立一个初始跟踪框。随着均值移动算法迭代地移动点,跟踪框将移动,直到包围感兴趣的对象。

不幸的是,如果对象改变大小或方向,跟踪框不会适应这种变化。cam shift(连续自适应均值漂移)算法解决了这个问题。

凸轮移动算法的工作方式与均值移动非常相似,它只是简单地调整跟踪框,以便跟踪框可以改变大小甚至旋转,从而更好地与被跟踪对象的运动相关联。

均值漂移和凸轮漂移的优缺点

与深度神经网络或其他识别方法不同,均值漂移和凸轮漂移不需要任何训练。如果需要跟踪一个网球,就不需要向算法提供成百上千个网球的标记图像。相反,该算法分析网球的初始颜色输入,并在其剩余的生命周期中立即跟踪它。

遗憾的是,均值偏移和凸轮偏移仅适用于颜色分析。如果被跟踪的对象在颜色或纹理上变化很大,则跟踪会更困难。同样,如果图像或视频的背景“繁忙”或“嘈杂”,颜色变化很大,它可能会与被跟踪的对象发生冲突。

最终,均值漂移和凸轮漂移在受控环境下效果最佳。例如,通过仓库或装配线跟踪统一颜色包装的产品可能非常适合这些算法。

凸轮换档的实现

由于 cam shift 是两种算法中更健壮的一种,因此将更详细地解释它在 python 和 OpenCV 中的实现。然而,在这里可以找到关于实现均值漂移的简要介绍。

import numpy as np
import cv2

唯一需要的两个库是 NumPy 和 OpenCV。前者有助于有效的计算,后者提供了许多用于计算机视觉的工具。

# initialize video from the webcam
video = cv2.VideoCapture(0)# Read the video
cap, frame = video.read()

第一行只是激活网络摄像头(如果有摄像头的话)。read 方法实际上捕获了两条重要的信息:

  1. cap 变量,它指示摄像机是否成功捕获了一帧
  2. 帧变量,它是来自摄像机源的实际静止图像或帧
# set up initial coordinates for the tracking window
x, y = 0, 0
# Set up initial size of the tracking window
height, width = 25, 25
track_window = (x,y,width,height)# set up region of interest (roi)
roi = frame[y:y + height, x:x + width]

下一部分建立初始感兴趣区域(ROI ),它只是屏幕上显示被跟踪对象的区域。

x 和 y 变量是框架内的坐标。在这个例子中,使用坐标(0,0 ),因为一旦摄像机开始流动,很容易找到坐标。

高度和宽度表示 ROI 的大小。ROI 的大小至少与被跟踪对象的大小大致对应,这一点非常重要。

track_window 变量存储 roi 的位置和大小,ROI 变量实际上使用拼接器来截取与 ROI 相对应的帧的一小部分。

无论摄像机在这个 ROI 中“看到”什么,都将决定它跟踪的颜色范围。例如,如果一个红色的球在这个初始 ROI 中,算法将知道在其余的馈送中跟踪一个红色的对象。

然而,如果球的一部分在 ROI 之外,并且捕获了更多的白色背景,则该算法将更有可能跟踪白色物体。在这个初始 ROI 中捕获正确的对象对于确保其正常工作至关重要。

提示:当我在学习如何做这个的时候,我“作弊”了,把感兴趣的物体举得离相机很近,这样整个屏幕都被填满了。这迫使算法读取正确的颜色信息,不管我使用的初始参数是什么。然而,这对于大多数应用来说是不实际的。

# apply mask
hsv_frame =  cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv_frame, np.array((0, 20, 20)), np.array((180, 250, 250)))
hist_frame = cv2.calcHist([hsv_frame], [0], mask, [180], [0,180])
cv2.normalize(hist_frame, hist_frame, 0, 255, cv2.NORM_MINMAX)

接下来的几行代码将帧转换到 HSV 颜色空间,并应用“蒙版”,它只是寻找符合特定范围的像素值。

在应用蒙版和归一化值之后,为该帧计算直方图。这用于计算算法将向前跟踪的颜色。

# Setup the termination criteria: 10 iteration 1 pxl movement
term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 )

获取初始值后,需要建立终止标准。回想一下,均值偏移和凸轮偏移都是连续移动点的迭代函数,必须建立某种标准来停止其迭代并返回一组最终值。

如果发生以下两种情况中的任何一种,上面的行将停止迭代:

  1. ROI 的质心没有移动,或者
  2. 迭代次数超过 10 次

第一个标准很直观。如果一个物体正在被跟踪,它没有移动,没有必要运行算法。ROI 没有变化,所以不需要重新计算。这样做只会浪费计算能力。

第二个标准更关注性能。理论上,更多的迭代会渲染出更精确的 ROI 然而,更多的迭代需要更多的时间来运行。设置高于 10 的限制可能会构建更好的边界框,但结果会滞后。

while True:

    cap, frame = video.read() if cap == True:
        hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        dst = cv2.calcBackProject([hsv],[0],hist_frame,[0,180],1)

第一行开始一个无限循环,因为摄像机将无限地流动,直到触发退出命令。

下一行和前面一样,从摄像机中读取数据。以下 if 语句检查以确定摄像机是否成功捕获了一帧。

最后两行将帧转换到 HSV 颜色空间,并使用直方图反投影来查找哪些像素最符合在初始 ROI 中捕获的对象的颜色。

 # apply camshift to get the new location
        ret, track_window = cv2.CamShift(dst, track_window, term_crit)

OpenCV 有一个内置模块来应用它,而不是从头开始实现 cam shift 算法。这将返回新 ROI 的大小和坐标。

 # Draw a box around the ROI
        pts = cv2.boxPoints(ret)
        pts = np.int0(pts)
        img2 = cv2.polylines(frame,[pts],True, 255,2)

最后,在 ROI 周围画一个方框,直观地表示出来。

 cv2.imshow('img2',img2) # Use the q button to quit the operation
        if cv2.waitKey(60) & 0xff == ord('q'):
            break else:
        breakcv2.destroyAllWindows()
video.release()

图像实时显示在第一行。下一行只是创建一个退出条件。如果键盘上的字母“q”被按下,它会中断循环并退出视频。

同样,如果摄像机突然无法成功捕获视频,它也会中断循环。

最后,在最后两行中,所有相关的窗口都被关闭,摄像机停止流动。

如果一切顺利,最终结果应该是这样的:

凸轮移位算法在行动,跟踪一个信封。作者制作的视频。

请注意,当封套离相机越来越远时,边界框的大小是如何增大和减小的。它还会随着信封一起旋转,甚至会在翻转时压缩。

结论

均值移动和凸轮移动算法都是非常有用和强大的工具,用于对象跟踪。特别是后者,可以随着对象旋转或改变与相机的距离而移动其 ROI。结果是一种稳健的跟踪方法。

使用颜色作为主要的识别方法,这两种算法都不能识别具有特定形状的东西,这使得它不如其他方法强大。同样,复杂或嘈杂的背景会使识别更加困难。然而,作为一种折衷,使用颜色允许立即实现,而不需要训练模型。

要么在受控、光线充足的环境中工作良好,并提供有效的对象跟踪方法。

使用 Python 和 scikit-learn 中的 TF-IDF 测量文本重量

原文:https://towardsdatascience.com/measure-text-weight-using-tf-idf-in-python-plain-code-and-scikit-learn-50cb1e4375ad?source=collection_archive---------8-----------------------

我们如何使用 TF-IDF 为文本数据赋予权重,并找出为什么 scikit-learn 的结果与教科书中的公式不同

我的儿子安德鲁·朱,查尔斯的乐高积木

在处理文本数据时,我们希望衡量一个单词对全文集合中的一个文档的重要性。一个最直观的解决方法就是计算单词的出现次数,越高越好。但是简单地计算字数会导致有利于长文档/文章的结果。毕竟,较长的文档包含更多的单词。

我们需要另一种解决方案,能够恰当地衡量一个单词在整个上下文中的重要性。 TF-IDF 是有效的解决方案之一。也是像谷歌这样的现代搜索引擎的支柱。

TF-IDF 的核心思想是,解决方案不仅衡量词频,还衡量单词在整个上下文中的重要性。

例如,像“是”、“the”、“and”这样的词几乎出现在每个文档中, TF-IDF 会降低那些常见词的权重,增加那些真正重要的词的权重。

TF-IDF 公式

假设我们有一个巨大的 Python 文本数据库,其中包含三个文档:

text_db = ['problem of evil',
           'evil queen',
           'horizon problem']

我们可以使用这个公式来计算某个文档中某个单词的 TF-IDF 值。

TF_w,d 代表某个 d 文件中 w ord 的 T erm F 频率:

而日志部分:

代表IvD文件 F 频率。这里的反转表示该部分将反转词频值,从而给那些频繁使用的词一些低值。

例如,您总共有 100 个文档,其中 10 个包含关键字。那么 IDF 部分就会是 log(D _ all/D _ w)= log(100/10)= log(10)= 2.3(e 基数)。如对数曲线所示,关键词在整体上下文中显示的位置越低,产生的 IDF 值就越高。

https://en.wikipedia.org/wiki/Logarithm

手动计算关键字“evil”的 TF-IDF 值

比方说,我们想获得文档 1 中关键字 evil 的 TF-IDF 值(“邪恶的问题”)。

应该很容易看到出现 1 次,一共 3 个字;数据库中的 3 个文档,其中 2 个包含 evil 关键字。所以,

我们一起得到了结果:

在 Python 中

import math
tf_1_evil       = 1/3
D_all           = 3
D_evil          = 2
tf_idf_evil     = tf_1_evil * math.log(D_all/D_evil)
print(tf_idf_evil)

打印结果:

0.135

通过 scikit-learn 计算 TF-IDF

Scikit-learn 提供了一种快速计算 TF-IDF 矩阵便捷方法。

import pandas as pd 
from sklearn.feature_extraction.text import TfidfVectorizer
vec = TfidfVectorizer()
text_db = ['problem of evil',
          'evil queen',
          'horizon problem']
tf_idf =  vec.fit_transform(text_db)
print(pd.DataFrame(tf_idf.toarray(), columns=vec.get_feature_names()))

结果。

 evil   horizon        of   problem     queen
0  **0.517856**  0.000000  0.680919  0.517856  0.000000
1  0.605349  0.000000  0.000000  0.000000  0.795961
2  0.000000  0.795961  0.000000  0.605349  0.000000

等等,你可能会问,安德鲁,你在跟我开玩笑吗?文件 1 的 TF-IDF 值(索引显示为 0)为0.517856。这里出了什么问题?

与 scikit-learn TfidfVectorizer实施的区别

scikit-learn 的TfidfVectorizer的实现有两个不同之处,使得结果不同于大多数教科书中存在的上述公式,你的教授告诉过你。

首先,sklearn 使用不同版本的 IDF 公式,将 1s 添加到分子和分母,以避免被零除的情况。 TF 不变。

其次,sklearn 在文档级用欧氏范数平滑 TF-IDF 结果。

在计算第一个文档中的值的情况下(“恶的问题”),公式为:

现在,让我们重塑 Python 代码,以反映上述两个变化:

import math
tf_1_problem    = 1/3
tf_1_of         = 1/3
tf_1_evil       = 1/3
D_all           = 3
d_problem       = 2
d_of            = 1
d_evil          = 2
tf_idf_problem= tf_1_problem * (math.log((D_all+1)/(d_problem+1))+1)
tf_idf_of       = tf_1_of * (math.log((D_all+1)/(d_of+1))+1)
tf_idf_evil     = tf_1_evil * (math.log((D_all+1)/(d_evil+1))+1)
denominator     = math.sqrt(tf_idf_problem**2 
                            + tf_idf_of**2 + tf_idf_evil**2)
result = tf_idf_evil/denominator
print("evil result:",result)

邪恶的 TF-IDF 值与 scikit-learn 产生的值完全相同。

evil result: 0.517856

链接和书籍

  • 术语特异性的统计解释及其在检索中的应用,

琼斯在 1972 年首次提出了 TF-IDF 的概念。

  • Python 数据科学手册——特征工程作者杰克·范德普拉斯
    https://jakevdp . github . io/Python datascience Handbook/05.04——特征工程. html

Jake 在“特征工程”一章中简要介绍了 TF-IDF,他没有深入研究 TF-IDF 的用法,但他提供了使用 scikit-learn 计算 TF-IDF 值的最佳 Python 代码。本文中使用的包含 3 个文档的示例文本数据库来自这本书。

  • 数学之美 by Wu Jun https://book.douban.com/subject/10750155/

本书由前谷歌研究员、前腾讯副总裁吴军博士用中文撰写。这本书对 TF-IDF 算法做了很好的介绍。

  • "Sklearn 的 TF-IDF" vs "Standard TF-IDF" 作者 siva kar Sivarajah
    https://towardsdatascience . com/how-sk learns-TF-IDF-is-different-from-the-the-Standard-TF-IDF-275 fa 582 e73d

多亏了 Sivakar,这篇文章展示了在 scikit-learn from 传统教科书中 TF-IDF 实现的不同之处。

Medium 不支持 LaTex 或数学公式输入。把那些数学公式放在一篇文章里是很痛苦的,即使使用复制的图片看起来也很糟糕。在这里,我以纯 HTML 格式将内容放在一起。

http://zhusd.com/understand_tfidf_in_python

如果你看到什么,欢迎评论并纠正我。谢谢你阅读它。

概率论中的测度论

原文:https://towardsdatascience.com/measure-theory-in-probability-c8aaf1dea87c?source=collection_archive---------7-----------------------

走向严格概率

关于概率有一件非常有趣的事情:一切似乎都是那么显而易见,但当我们更深入地研究它时,突然发现我们实际上并不理解它。

图片来自 Unsplash

这篇文章是上一篇关于测度理论的文章的延续,但是我认为直接阅读这篇文章没有问题,除了有时候需要参考另一篇文章。

在我们学习概率的早期阶段通常遇到的概率分布是均匀分布。一致意味着所有事件发生的概率相同。当我们掷出一个六面骰子时,每个数字出现的概率是 1/6,它们加起来是 1,和预期的一样。

然而,在均匀分布中有几种反直觉的情况。第一件诡异的事:当我们考虑在长度为 6 的实线上扔一个微小的球的情况时,球击中任意特定点的概率是多少,是 1/6 吗?答案是否定的,因为事实上,它是零。这意味着球不可能击中任何特定的点。第二件怪事:如果球击中任何特定点的概率为零,那么所有这些概率的总和就不是一。第三件怪事:考虑长度为 1/6 的直线,每个点的概率密度为 1 / (1/6) = 6(由均匀分布的概率密度函数定义)大于 1——

概率密度不是概率。

这表明了引入数学困难的必要性,因为像上面讨论的反直觉现象不能仅仅通过直觉来研究。在这篇文章中,我们将着眼于“数学困难”——测量理论。

测量直觉

度量是我们从小学就非常熟悉的概念的延伸,即欧几里得空间中的长度、面积和体积。这些想法简单而直观,它们量化了形状的大小。

然而,它们展示了测度理论中一个非常基本的性质,即可加性。这是指,例如在图 1.1 中,整个截面的长度等于两个分段(a + b)之和,整个矩形的面积等于两个子矩形(S₁ + S₂).)之和

图 1.1 测量的直觉

为什么会这样呢?因为子部分或子区域是不相交的,如果它们重叠,显然前述结论将不成立。当这种可加性扩展到无限多个这样的几何对象时,我们称之为**【西格玛可加性】**。在正式定义度量之前,有一件重要的事情需要注意

Measure 是一个函数,但我们有时也用它来指代这个函数的输出。

当输入是截面时,它输出长度,当输入是形状时,它输出面积,等等。

概率公理

测度理论扩展并形式化了我们对区域面积的直观认识。将测度论集成到概率论中,使不确定性程度的直观想法公理化——它使用测度论的力量来度量不确定性。

在介绍 Kolmogorov 概率公理之前,需要澄清一些概念。第一个也是最基本的是σ-场(或σ-代数)。它是一种代数结构,粗略地说,是一个非空集,上面定义了运算和公理。设𝔉是另一个集合ω的子集的集合,即𝔉是𝓟(ω的子集)(ω的幂集)。我们称𝔉 代数如果它在有限并互补下是闭的(根据德摩根定律,𝔉在有限交下也是闭的)。如果我们用可数并代替有限并(这个更严格。因此所有σ-代数都是代数,但反之亦然),𝔉是一个σ-代数

包含𝓐的最小σ-代数由σ(𝓐)(𝓐生成的σ-代数)表示,其中𝓐是ω的子集的集合。我们通过应用有限并、互补、有限并的互补、互补的有限并等等,将𝓐扩展到σ(𝓐。

平凡σ-代数是仅包含空集和整个样本空间{∅ω}的集合。σ代数的另一个例子是{∅,{a,b},{c,d},{a,b,c,d}}。𝓟(ω)也是σ-代数。作为一个真实世界的例子,我们把σ-代数放在掷硬币的上下文中,可能的结果是正面(H)和反面(T),因此样本空间是ω= { H,T}。让𝓐 = {{T}}。σ(𝓐) = {∅,ᶜ,,ω} ={∅,,,{H,T}},其中这个集合中的每个元素都是一个事件。如果我们有两个硬币,ωis 由所有可能的结果组成,等于{HH,HT,TH,TT}。

现在我们有了样本空间ω,它在概率的上下文中作为样本空间,还有一个σ代数𝔉.现在唯一缺少度量空间定义的构件是度量。如前所述,测度μ是一个函数。它是定义在𝔉上的非负的、扩展的实值函数,使得

等式 2.1 测度的可加性

三元组(ω,𝔉,μ)被称为度量空间(注意“度量空间”是一个完全不同的概念,虽然名字看起来很像。度量空间是其上有度量的集合)。如果μ是可数可加的并且对于𝔉.中的所有元素都是非负的,那么μ是𝔉上的一个测度如果μ是一个概率测度,也就是说μ(ω)= 1,(ω,𝔉,μ)称为一个概率空间。我们知道概率空间实际上是[0,1]上的勒贝格可测空间(参考勒贝格测度)。快速检查知识:勒贝格测度是概率测度吗?不是,因为整个空间都是ℝ,ℝ上的勒贝格测度是∞而不是 1。

有了这些知识,Kolmogorov 概率公理变得非常简单。公理被定义在概率空间上,概率测度是概率函数。我们稍微修改概率空间的符号,它变成(ω,𝔉,p)

  1. 事件发生的概率 E,P(E)永远不会是负的。这对应于措施的非否定性。
  2. 整个概率空间 P(ω)= 1 的概率。这是专门为概率测量定义的。
  3. 不相交事件的可加性。这在等式 2.1 中描述。

让我们回到掷硬币的例子。事件是什么,它们的概率是多少?在我们的例子中,和分别代表看到头或尾的事件。我们给它们分配概率 P() = P() = 1/2,在非偏硬币的情况下。∅代表“什么都没发生”的事件。根据概率的公理(测度的性质),P({H,T}) = P() + P() = 1,这是一个直观的结果——{ H,T}代表要么看到头要么看到尾的事件,当然,其中一个会发生。

概率分布

概念分布与随机变量密切相关。随机变量的宽松定义是事件的数字结果。更正式地说,随机变量是从样本空间(我们知道这是一组事件)到实数集的可测函数。考虑抛硬币的例子,可能的结果要么是正面,要么是反面,这就形成了样本空间ω= { H,T}。随机变量可以定义如下:X(H) = 1,X(T) = 0,这意味着看到头的数值结果是 1,看到尾的数值结果是 0。在这种情况下,随机变量也作为指示器功能工作。

我们为什么要学这个?答案是,如果我们不将结果转换成数值,我们就无法对其进行任何运算,例如计算期望值和方差。我们还需要这个映射来研究分布,因为谈论没有随机变量的分布有点奇怪。

概率论中的随机变量对应于勒贝格积分中的可测函数。在测度论方面,我们将分布定义为( R ,ℛ)上的概率测度,其中 R 表示实直线,ℛ是由实直线生成的σ-代数(参考我们之前讨论过的测度空间的定义)。

等式 3.1 根据测度论对分布的定义

并且分布函数被定义为

等式 3.2 分布函数

其中 F(x-)给出了左手极限(因为函数 F 是非递减的,例如不振荡,所以左手极限总是存在)。实际上, F 是一个右连续函数,具有左极限。下面给出了这样一个函数的例子,当你从右边接近任何一个数时,这个数的极限等于函数值。

图 3.3 一个右连续函数

这就给了我们累积分布函数,通常是在基础概率里学的。快速捕获:(1)概率分布是一个函数,根据测度理论,它是测度(2) F 是分布,它是用测度定义的。

为了更好地理解这个定义,我们需要将它与一些具体的分布联系起来,这里将以伯努利二项式分布为例。我们再来看抛硬币的例子,随机变量 X 定义为 X(H) = 1,X(T) = 0,如果重复实验 n 次,结果形成二项分布。每个试验都服从伯努利分布,这是二项式分布的一个特例(n=1)。

伯努利分布非常简单,它是一个只有两个点的离散分布。随机变量只有两个值,即 1 和 0。他们每个人的概率是μ({1}) = 0.5,μ({0}) = 0.5。“μ”和“P”有时会互换使用,因为它们最终是同一个东西。

图 3.4 伯努利分布。作者图片

二项分布怎么样?什么是随机变量,图形是什么样的?随机变量 X 定义为一系列 n 次试验中的成功次数(k ),成功率为 p。这意味着 k 是 X 取的具体值,n 和 p 是 X 的分布参数。每个点的概率测度由下式给出

等式 3.5 二项式分布的概率度量。作者图片

图 3.6 二项式分布。作者图片

现在我们准备解释连续分布有什么问题。为什么每个单点的概率测度为零?这是相当复杂的,但简而言之,在所有实数子集的σ-代数上无法定义任何概率。这是通过划分实数区间(这是一组实数)并构造一个维塔利集来证明的,好的解释见这里。结论是,只有当样本空间是区间,即均匀分布时,测量子区间的长度才有意义。因此,在这种情况下,

σ-代数是 Borelσ-代数——由所有开区间(或者等价地由闭集)组成的σ-代数。

这个在这个帖子里也有提到。将此与我们对测度的直观认识联系起来,我们可以看到,真实直线上的概率测度确实给了我们一个长度,而这些点没有长度。我们做到了

等式 3.7

同样,我们可以看到为什么可数可加性很重要:如果第三个公理可以由非可加性满足,那么如果我们把区间上所有点的概率测度加起来,我们会得到零,因为对于区间上所有的 x,μ() = 0。但是,由于所有点构成了整个区间,也就是整个样本空间ω。根据第二个公理,μ(ω)= 0。这就导致了一个矛盾。

概率质量、密度和累积函数

以现在所装备的知识,一些容易混淆的概念是可以很容易澄清的。

概率质量:这是由概率质量函数给出的,概率质量函数是样本空间离散的概率空间上的概率度量。

(把这个概念想深一点,“离散”究竟是关于什么的?通常与自然数的集合相联系,离散型随机变量总是用自然数而不是有理数来索引。更准确地说,我们感兴趣的值可以从一个有限集或可数无穷集中选取。为什么会这样呢?这个可以用集合论的一些基础知识来回答。自然数和有理数的集合都是可数无穷的,这意味着它们具有相同的维数,或者等价地,这两个集合之间存在一个双射。)

这意味着概率质量函数给出了随机变量取某个值的概率。

概率密度:一个看似令人惊讶的事实——概率密度不是概率(测度)。这是相对的,它相对地告诉我们,随机变量有多大可能等于某个值。在均匀分布的情况下,概率密度在任何地方都是相同的,这意味着,每个值被随机变量采用的可能性是相等的。相对函数是一个概率函数,注意它输出的是连续随机变量的密度而不是概率。

与我们之前讨论的相关,连续分布中每个点的概率应该等于零。然而概率密度却不是这样。所以,显然,概率密度不是一个概率。

累积函数:累积函数已经在方程 3.2 中定义。顾名思义,它“累积”了一个随机变量取小于某个值的所有值的概率。为什么我们需要这个概念?这是因为我们有时会遇到既不是绝对离散也不是绝对连续的概率分布——它们可能是两者的组合。在这种情况下,我们不能只用概率密度函数或者概率密度函数来描述。然而,一个累积函数足以处理这种情况。考虑下图所示的累积函数,它既不是离散的也不是连续的。

图 4.1 分布的混合示例

累积函数和分布有什么关系?分布函数是累积函数的导数,其中累积函数是可微的。让我们再看一下图 4.1 中的曲线图,累积函数定义为

等式 4.2 累积函数

这是直接从图中读取的。概率密度函数怎么样?除了点 1 和点 2,累积函数是可微的。因此,概率密度函数定义为 f(x) = 1/2,对于(1,2)中的 x,其他任何地方为 0。

概率测量很有趣。只有 x=1 的离散部分是非零的,因此我们得到 P({1}) = 1/2(在跳跃的情况下,参考等式 3.2)。

图 4.3

概率分布如图 4.3 所示。我们来检查一下可加性:P({1}) + P((1,2)) = P({1}) + P((1,2))= 1/2+1/2 = 1。(第二个 1/2 是线下区间(1,2)上的面积)。在其他地方,分布是连续的,因此,概率质量为零。

期望值

在测度论的语言中,变量 x 在测度空间(ω,𝔉,p)上的期望值被定义为 x 关于测度 p 的积分

等式 5.1 期望值的一般定义

期望值只不过是随机变量输出的加权平均值。等式 5.1 中的定义乍一看似乎很奇怪。回想一下,我们更熟悉的期望的定义如下

方程 5.2,方程 5.3 期望值的熟悉定义

不难看出,等式 5.1 中的定义将两个独立的定义合并在一起。

在不使用测度的情况下,需要单独定义期望值,因为离散分布的样本空间是可数的,而连续分布的样本空间是不可数的。

因此,这两种分布的概率测度定义不同——在离散分布的情况下,每个单点的概率由概率质量给出,使用求和;然而在连续分布的情况下,每个点的概率为零,我们使用概率密度来计算一个区间上的概率,这意味着我们需要积分。

为什么我们需要这次合并?这个问题相当于“泛化有什么好?”答案很简单。方程 5.1 中的定义相对于单独定义的优势在于,它将简单随机变量(随机变量的值域是有限的,参考简单函数和值域的概念)和连续随机变量的黎曼积分结合在一起,这允许我们应用一般积分理论来研究它。其他一些重要的概念,如方差、矩等。是使用 expexted 值定义的。如果我们对概率空间和分布有深刻的理解,其他的值也就容易把握了。

总结:

在这篇文章中,我们首先快速浏览一下测度的概念,然后用一些基本的例子来介绍概率公理。然后我们试着区分一些常见的概率上的混淆。最后,我们给出了期望值的一般定义。更多关于概率的内容将会陆续推出。

资源:

[1] Dekking,F. M .,Kraaikamp,c .,Lopuhaä,H. P .,和 Meester,L. E. (2005 年)。概率和统计的现代介绍:理解为什么和如何。斯普林格科学&商业媒体。

[2]比林斯利,P. (2008 年)。 概率和测度 。约翰·威利的儿子们。

【西格玛代数】。(2021 年 9 月 4 日)。在维基百科

[4] Ash,R. B .,Robert,b .,Doleans-Dade,C. A .,和 Catherine,A. (2000 年)。 概率与测度论 。学术出版社。

一段我写作时总喜欢听的音乐【T13:)

测量不变性:R 中的定义和例子

原文:https://towardsdatascience.com/measurement-invariance-definition-and-example-in-r-15b4efcab351?source=collection_archive---------28-----------------------

测量不变性如何改进你关于人类受试者的统计推断

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

如果你熟悉以人为研究对象的研究,你可能见过一些群体比较的例子。例如,如果我们想要测试一种抗抑郁药的功效,我们可能想要比较性别差异(因为抑郁可能基于性别)。然而,你确定你正在使用的工具(例如贝克抑郁量表)对男性或女性具有相同的结构吗?

另一个例子是跨文化研究。我们可以测量国家之间主观幸福感的差异,看看一些国家是否比其他国家更幸福(例如 Jebb 等人,2020 )。但是,如果你不知道你是否能比较潜在变量的量表分数,你怎么能推断你的结果是准确的呢?

比较量表得分的一个标准是,所有群体/国家对潜在变量的理解和衡量是相同的。这种特性被称为测量不变性或无偏差。(斯维蒂娜等人,2019)。

建立测量不变性证据的常用统计方法是通过多组验证性因素分析(MG-CFA)。在 MG-CFA 中,我们使用一组分层测试对组间感兴趣的参数施加约束。基本上,测试了三个级别的不变性。

  1. 结构不变性:检验组间因素结构是否相同。如果没有发现结构不变性,这意味着项目对不同的组加载不同的因素。
  2. 度量不变性:测试项目的因素负载在组之间是否相同。如果没有发现度量不变性,这意味着一个或多个群体对工具的一个或多个项目的回答有偏差。因此,对组间差异的推断可能是有偏差的。虽然这是项目中反应偏差的一个指标,但下一步需要预先假定组间相等的比较。
  3. 标量不变性:测试组之间的截距是否相同。如果没有发现标量不变性,组间发现的任何差异都与潜在特征无关,而是与仪器参数的测量不等价有关。

如果你在第一步没有找到不变性。,您不能继续执行步骤 2。,等等。还有其他关于测量不变性的步骤,然而,这三个是心理测量学中使用最多的步骤。

辅导的

读取数据

无需将数据下载到您的计算机,我们将使用 R 函数读取数据集:read.delim

Self_Esteem <- read.delim(“[https://raw.githubusercontent.com/rafavsbastos/data/main/measurement%20invariance.dat](https://raw.githubusercontent.com/rafavsbastos/data/main/measurement%20invariance.dat)")

我们为我们的数据集选择的名字是自尊*。*使用View(Self_Esteem),RStudio 打开一个选项卡,显示我们的数据:

我们还将把“性别”变量转换成一个字符。

Self_Esteem <- transform(Self_Esteem, sex = as.character(ï..sex))

阅读包装

为了开始操作我们的数据,我们需要下载一些包。我们将下载两个包: lavaansemTools 。要安装,只需运行以下代码:

install.packages(“lavaan”)
install.packages("semTools")

现在,要开始使用这两个包,我们必须使用

library(lavaan)
library(semTools)

编码测量不变性

我们将把模型的结果存储在一个名为 all.results 的空矩阵中,从中提取卡方、自由度、RMSEA、CFI 和 TLI。

all.results<-matrix(NA, nrow = 3, ncol = 6)

在我们的例子中,我们使用自尊模型,其中我们将拟合一个具有 10 个观察变量的单因素模型。我们现在将编写指定模型的代码,我们将在测量不变性的其他分析中使用该模型。

model1 <- 'F1 =~ se1 + se2 + se3 + se4 + se5 + se6 + se7 + se8 + se9 + se10'

基线模型

现在,我们将使用来自 semTools 的 measEq.syntax 函数建立基线模型(没有跨组的约束)。

baseline <- measEq.syntax(configural.model = model1,
                          data = Self_Esteem,
                          ordered = c("se1",
                                      "se2",
                                      "se3",
                                      "se4",
                                      "se5",
                                      "se6",
                                      "se7",
                                      "se8",
                                      "se9",
                                      "se10"),
                          parameterization = "delta",
                          ID.fac = "std.lv",
                          ID.cat = "Wu.Estabrook.2016",
                          group = "sex",
                          group.equal = "configural")

如果你希望看到这个模型的结果,首先你必须指定为. character 以提交给 lavaan

model.baseline <- as.character(baseline)

现在,我们将通过 cfa 函数在 lavaan 中拟合基线模型

fit.baseline <- cfa(model.baseline,
                    data = Self_Esteem,
                    group = "sex",
                    ordered = c("se1",
                                "se2",
                                "se3",
                                "se4",
                                "se5",
                                "se6",
                                "se7",
                                "se8",
                                "se9",
                                "se10"))

好了,结果出来了。现在,我们将看到以下结果:

summary(fit.baseline)

它将打印一个结果,如

当然,打印出来的下面有很多结果,但是我们会把模型的俯视留做作业。

现在我们将提取结果,并将它们放入我们的 all.results 矩阵的第一行。

all.results[1,]<-round(data.matrix(fitmeasures(fit.baseline, fit.measures = c("chisq.scaled", "df.scaled","pvalue.scaled","rmsea.scaled","cfi.scaled","tli.scaled"))), digits=3)

阈值不变模型

我们将编写与之前几乎相同的代码。

prop4 <- measEq.syntax(configural.model = model1,
                       data = Self_Esteem,
                       ordered = c("se1",
                                   "se2",
                                   "se3",
                                   "se4",
                                   "se5",
                                   "se6",
                                   "se7",
                                   "se8",
                                   "se9",
                                   "se10")
                       ,parameterization = "delta",
                       ID.fac = "std.lv",
                       ID.cat = "Wu.Estabrook.2016",
                       group = "sex",
                       group.equal = c("thresholds"))model.prop4 <- as.character(prop4)#Fitting thresholds invariance model in lavaan via cfa functionfit.prop4 <- cfa(model.prop4,
                 data = Self_Esteem,
                 group = "sex",
                 ordered = c("se1",
                             "se2",
                             "se3",
                             "se4",
                             "se5",
                             "se6",
                             "se7",
                             "se8",
                             "se9",
                             "se10"),
)# Obtaining results from thresholds invariance model
summary(fit.prop4)#Extracting fit indices into the second row of all.results matrixall.results[2,]<- round(data.matrix(fitmeasures(fit.prop4,fit.measures = c("chisq.scaled","df.scaled","pvalue.scaled","rmsea.scaled","cfi.scaled","tli.scaled"))),digits=3)

这同样适用于下一个模型。

具有阈值和负载不变性的模型

prop7 <- measEq.syntax(configural.model = model1,
                       data = Self_Esteem,
                       ordered = c("item1",
                                   "item2",
                                   "item3",
                                   "item4"),
                       parameterization = "delta",
                       ID.fac = "std.lv",
                       ID.cat = "Wu.Estabrook.2016",
                       group = "sex",
                       group.equal = c("thresholds", "loadings"))model.prop7 <- as.character(prop7)fit.prop7 <- cfa(model.prop7,
                 data = Self_Esteem,
                 group = "sex",
                 ordered = c("se1",
                             "se2",
                             "se3",
                             "se4",
                             "se5",
                             "se6",
                             "se7",
                             "se8",
                             "se9",
                             "se10"),
)summary(fit.prop7)all.results[3,] <- round(data.matrix(fitmeasures(fit.prop7, fit.measures = c("chisq.scaled", "df.scaled", "pvalue.scaled", "rmsea.scaled", "cfi.scaled", "tli.scaled"))), digits = 3)

决赛成绩

我们现在将检查存储在 all.results 中的拟合指数。

all.results

使用分类数据的测量不变性,我们可以看到模型变得越来越好,因为模型通过施加相等的阈值和阈值和负载来约束。 Sventina 等人(2019) 制作了一个表格,总结了基于方法、小组数量、每组人数、因素数量和数据分布的结果解释。

如果你查阅了 Sventina 等人的论文,你会发现在保证测量不变性的指导方针方面还有很多工作要做。然而,有很多模拟研究(见 Sventina 等人,2019 )并且你的研究可能符合现有的指南。正如在开始时指出的,测量不变性是关于组差异的推论的关键部分,在我们运行下面的分析之前,我们的测量需要考虑它。

接触

Gmail:rafavsbastos@gmail.com
咨询和合作网站:
rafavsbastos.wixsite.com/websiteLinkedIn

参考

本文基于 Svetina 等人(2019) 的惊人论文。所有鸣谢都来自这些作者,他们应该在使用此代码的后续工作中引用。

D.Svetiva,L. Rutkowski 和 D. Rutkowski,使用更新指南的分类结果的多组不变性:使用 M plus 和 lavaan/semTools 软件包的说明 (2019),结构方程建模:多学科杂志。

使用图像处理测量咖啡研磨颗粒分布

原文:https://towardsdatascience.com/measuring-coffee-grind-particle-distribution-using-image-processing-84882e6dd4fd?source=collection_archive---------15-----------------------

咖啡数据科学

使用图像处理了解磨床性能第 1 部分

两年来,我一直在研究用图像处理测量咖啡颗粒的分布。它不是直截了当的,因为咖啡颗粒变得相当小。我以前的主要限制是测量直径小于 100 微米的颗粒,但我很快发现我最大的问题不是图像处理,而是照片设置。

作者的所有图片

当前技术

有两种专业方法来测量咖啡研磨分布:

  1. 多重筛选(筛选)
  2. 激光测量

多个筛子通常堆叠在振动台上,一旦所有的咖啡都被筛过,人们就要称每个筛子的内容物。这可能需要时间,因为这是一个手动过程。

激光测量使用激光通过衍射技术测量颗粒。这样可以更快,但是设备要贵很多。

成像技术

以前也有成像技术(即乔纳森·加涅的工具),但这些技术与筛选或激光测量之间没有可比性。两年前,我开发了一个分析浓缩咖啡篮孔的工具,我发现我可以用同样的工具进行颗粒分析。然而,事实证明,Matlab 有一个非常好的函数( regionprops ),它对二值图像(白色或黑色,没有灰色)进行处理,以识别独立的对象及其属性。

我决定在买了一个壁龛研磨机后再看一看,因为改变研磨设置相对容易。我已经有一些筛选的数据(只有 2 个屏幕,所以 3 个独立的颗粒大小组),但它将有助于检查成像技术对地面的真相。

分离咖啡颗粒

我用一张白纸,用环形灯照亮它。我用了一个 800 微米的筛子来帮助从高处打碎大块并撒上粉末。这是拍摄场地最具挑战性的方面:分离。

我觉得这还不够好,所以我掸了掸。我用刷子轻敲它们,把结块打碎。我也拍了前后的照片。刷子没有积聚咖啡。

下面是一个对比:

左图:除尘前。右图:除尘后

然后我用 iPhone 拍了一张照片,可以看到整张纸。我用纸来确定毫米/像素,然后用长焦镜头放大一点。

左:正常广角拍摄。右图:长焦镜头拍摄

数据分析

我看了三个研磨设置(0,15,30),我用一个小生研磨机来近似断奏。

左:设置 0,中:设置 15,左:设置 30

从总颗粒数来看,尘粒数要高得多。

我们可以看到尘封和正常之间分布的差异。正常的(未调整的)有更多更大的粒子,特别是设置为 0 时,我知道这不是真的。

让我们来看看设置为 0 时,正常分布和灰尘分布之间的差异。

观察灰尘的分布,设置 15 和 30 的分布太接近,这表明测量误差。

我们也可以看看实际体积,它的目的更多的是对实际重量的估计。所以我把颗粒大小的百分比转换成体积,假设所有的颗粒都是球形的(不正确,但是一个不错的假设),并且假设密度不变。知道了直径,你就可以用这个方程来计算球体的体积。

在观看音量方面,设置 15 和 30 肯定有问题。对于设置 0,峰值体积直径低于 400um,这与断奏镜头的精细层(<400um). However, there is still a large percentage above 500 um. This observation does not align with my experience with that grind setting.

This study look at trying to analyze images of coffee grounds to determine particle size distributions. The biggest problem was getting coffee grounds to separate from each other, and the current technique isn’t as effective as I would like considering there is not much discrepancy between setting 15 and 30.

In 第 2 部分)一致,我将探索使用 Kruve 筛子(只有两个屏幕)来提供更多的地面真实信息进行比较,希望筛选和图像处理的一些混合将提供更好的可信度。

如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以在中关注我。

我的进一步阅读:

浓缩咖啡系列文章

工作和学校故事集

个人故事和关注点

乐高故事启动页面

摄影飞溅页面

浓缩咖啡过滤器:分析

浓缩咖啡过滤篮:可视化

建模咖啡研磨机

浓缩咖啡过滤器对比:佩萨多 vs VST

浓缩咖啡篮(VST):有脊与无脊

IMS superficial vs VST:小样浓缩咖啡过滤器对比

浓缩咖啡模拟:计算机模型的第一步

克鲁夫:对技术状态的进一步分析

克鲁夫咖啡筛:一项分析

咖啡篮隆隆声:机器人 vs 铂尔曼 vs 体面

测量咖啡渣与乔纳森·加涅的技术

原文:https://towardsdatascience.com/measuring-coffee-grounds-vs-jonathan-gagn%C3%A9s-technique-979d91820c60?source=collection_archive---------35-----------------------

咖啡数据科学

一次奇特的冒险

咖啡地面分布可以为咖啡研磨机和拨号咖啡拍摄提供信息。典型的方法是昂贵的,即筛分和激光衍射。Jonathan Gagné制作了一个基于图像的方法,有一段时间,人们不得不编译代码并准备好 Python 环境。我通常不这么做,所以我建造了我自己的T3。

最近,有人制作了一个 exe 文件,允许人们在他们的计算机上运行乔纳森的方法,而无需设置他们的环境,所以我这样做是为了比较他的方法和我的方法。

我们使用不同的阈值。Jonathan 使用一个简单的阈值,而我使用一个自适应阈值来捕捉更多和更细的粒子。

左图:约拿单有门槛的像,右图:我有门槛的像。

结果是我的方法有更高的原始计数,而他的方法使用最小面积阈值(5)作为缺省值。

然而,如果你取出少于 5 个像素的粒子,并对体积大小进行归一化,我们会得到类似的分布。

您可以在高级设置中调整他的方法的阈值。有一个平衡,因为你开始失去粒子以及粒子的精确尺寸。

40%、60%、80%和 90%的阈值

因此,如果你想要更小的粒子,你必须提高阈值,但你可能会得到大的噪声位,这取决于图像是如何捕捉的。

80%和 90%的阈值

使用 Jonathan 的工具让我非常兴奋,它拥有成功理解粒子分布的主要组件。虽然我认为使用自适应过滤是有益的,但这个应用程序允许用户了解他们的粒子分布。

这款应用存在一些问题,如果这些问题得到解决,我相信它会得到更广泛的应用,尤其是帮助咖啡师拨入咖啡:

  1. 把它变成一个移动应用或基于网络的应用
  2. 向用户提供关于图像质量的反馈
  3. 添加一个视频展示如何获得最佳分配。
  4. 改变阈值算法,使用自适应滤波器。
  5. 添加一个功能,建议增加或减少研磨设置,以匹配给定的研磨设置。

这款应用的最大误差是基于用户行为,但如果用户在拍摄这些照片时格外小心,他们可以充分利用这款应用。

如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。也可以在中和 Patreon 上关注我。

我的进一步阅读:

浓缩咖啡系列文章

工作和学校故事集

个人故事和关注点

乐高故事启动页面

摄影飞溅页

使用图像处理测量咖啡研磨颗粒分布

改善浓缩咖啡

断奏生活方式概述

测量咖啡磨粒分布

咖啡萃取

咖啡烘焙

咖啡豆

浓缩咖啡用纸质过滤器

浓缩咖啡篮及相关主题

意式咖啡观点

透明 Portafilter 实验

杠杆机维护

咖啡评论与思考

咖啡实验

衡量数据生态系统成熟度

原文:https://towardsdatascience.com/measuring-data-ecosystem-maturity-62f0bf7c7143?source=collection_archive---------20-----------------------

介绍一种反映数据经济价值的尺度。

Jukan Tateisi 在 Unsplash 上拍摄的照片

我们咨询师痴迷于基准和尺度!为什么?嗯,首先,标杆管理符合典型高管的竞争本质,他们不想落后于竞争对手,不想错过竞争对手,当他们的业务以竞争对手为基准时,他们会进入“战斗模式”。

但在现实中,标杆管理不仅仅是参与行为科学。相反,高管们要求他们帮助确定目标和优先事项,并制定行动方针,将他们从 a 点提升到 b 点,或从较低的四分位数提升到较高的四分位数。

这种“速赢/跟随者”的方法导致许多企业追求“必备”项目(想想银行业的聊天机器人)。但是,如果没有整体业务战略的强大支撑,许多数据计划都会失败。

我认为这种快速取胜的方法通常会导致过快地找到解决方案。毕竟,看到一个情节显示,例如,“领导者比落后者多 5.4 倍的利润,并且其中 78%的人已经实现了聊天机器人”,不应该自动导致建立聊天机器人的决定!**

数据不仅仅是数据。它不会自动传递价值。

什么标志着数据生态系统的成熟?

让我首先陈述两个关于数据的基本原则。

  1. 数据必须创造经济价值。
  2. 数据是一个生态系统。

第一个原则意味着数据计划必须与业务战略保持一致,并且应该努力对业务成果产生积极的影响——增加利润、降低成本、增加公司价值、降低风险等等。

第二点说明了这样一个事实,即数据计划要想成功,很少仅仅与数据有关。相反,这是一个影响成功率的整个生态系统,从数据和业务之间的战略一致性,到分析和数据本身,到数据基础架构,再到人员。简而言之,我们称之为我们的“SAPI”框架:

来源:https://www.datadiligence.com/pointsofview?lightbox=dataItem-khq863jq1 (经许可使用)

这两个原则对于我们如何衡量数据生态系统成熟度也至关重要。成熟度等级必须与经济价值相关,而不是数据的“无关紧要性”。它还必须适用于整个生态系统(SAPI)并涵盖其所有层面。

此外,成熟度等级的步骤应该是直观的。易于评估甚至自我评估。他们还应该本能地指导公司如何向更高层发展。

那么,我们应该如何衡量数据生态系统的成熟度呢?

现在,事不宜迟,让我们来看看规模。

  • 无:不明显
  • **分散:**零星的口袋,缺乏广泛的凝聚力和结构
  • **基础:**建立在整个组织的基础上
  • **有效:**有效利用,结构良好,广泛使用
  • **增强:**根深蒂固,并向变革和机遇发展
  • **变革:**尖端方法和交付(可能是专有的)

秤是如何工作的?

假设我们正在评估一个战略因素,比如数据战略。使用我们的量表,对数据战略成熟度的“信封背面”评估可能如下所示:

  • 无数据愿景= 无。
  • 任意数据项目的集合= 分散。
  • 确保公司控制数据并可用于基本决策的策略= 基础。
  • 涵盖数据如何帮助瞄准所有关键业务问题和机会= 有效。
  • 快速应对新挑战,围绕业务需求平稳发展= 增强。
  • 影响关键业务决策甚至整个业务战略= 变革性。

应用于数据代表性 —数据在多大程度上反映了企业的业务环境—该等级将表示:无数据= *无。*有些因素涵盖了,有些没有= *分散了。*数据中充分体现的关键因素= *基础因素。*代表的主要因素= *有效。*重要细节可用= 增强。大部分细节都涉及到了。

或许我们应该评估一下数据科学工具包。没有到位= *没有。*数据科学家使用的基本工具基于个人偏好和本地= *分散。*数据科学团队可用于查询和分析数据的基本工具= *基础。*由数据科学家和分析师开发的解决方案(产品或分析)可以轻松部署并与其他人分享。数据科学平台,支持数据产品的开发、部署、监控和运营= *增强。*数据科学平台自动建议和纠正解决方案= 变革性。

或者,让我们来试试更巧妙的方法——数据流畅度?不知道数据与工作有什么关系的人= *没有。*有些人利用一些数据做出一些决策= *分散。*大多数人在做决策时会考虑关键数据= *基础数据。*人们轻松地做出数据驱动的决策= *有效。*人们对数据的偏见和局限性的理解= 增强了。人们积极寻找利用数据改善运营的新机会。

例子还可以继续,但是你明白了。

但是为什么不用其他的数据成熟度量表呢?

数据成熟度有多种衡量标准。 Gartner 有一个。 IBM 也是。卡介苗也是。以及其他咨询机构——大型和小型。甚至像比尔·施马尔佐这样的个人思想领袖。

然而,我/我们认为它们有一些实际的限制。

  • 有些本质上是相对的,在你没有标杆的情况下,很难自我评价。
  • 一些人考虑数据的一个非常具体的方面,很难将同样的尺度应用于数据生态系统(或框架)的其他重要因素。
  • 有些水平太高,不能提供足够的可塑性来进行正确的诊断。
  • 一些人关注的是产出,而不是对结果的影响。

因此,我们创建了一个符合我们标准的尺度:易于理解和直观,可应用于数据生态系统的所有因素(包括个人和集体)。此外,它与结果和经济价值紧密相连,并提供了个人成熟阶段的顺序指南。

无论哪种方式,我们都需要记住,数据的价值应该通过业务影响来衡量。完全不是。更确切地说,量表应该提供你所处位置的洞察力。

感谢阅读!

欢迎在评论中分享你的想法或观点。

跟我上 LinkedIn推特 **

利用卷积神经网络测量距离

原文:https://towardsdatascience.com/measuring-distance-using-convolutional-neural-network-190b7afadd8a?source=collection_archive---------19-----------------------

深度信号处理教程

M. B. M. 在 Unsplash 拍摄的照片

在信号处理中,有时需要测量信号某些特征(例如峰值)之间的水平距离。解释心电图(ECG)就是一个很好的例子,心电图的大部分解释都依赖于测量距离。在下图中,我们将考虑一个只有两个峰值的平滑信号的玩具示例。

作者创造的形象

这个问题很简单,可以通过找到峰值,然后通过减去它们的 X 坐标来测量它们之间的水平距离来解决。这可以使用可用的工具和库来有效地完成。然而,我们的目标是训练一个神经网络来预测峰值之间的距离。一旦我们证明神经网络可以处理这项任务,我们就可以在更复杂的端到端学习任务中重用相同的架构,而测量距离只是学习更复杂关系的一种手段。这源于深度学习的想法,即我们应该尝试让神经网络学习特征,而不是让工程师手动编码这些特征,希望这些是最相关的特征。如果我们可以证明神经网络可以学习距离特征,我们可以在更复杂的网络中使用它,其中最终结果将取决于除距离之外的许多其他因素。这些任务的好例子是解释心电图或天文数据。

生成数据

在我们的实验中,我们将使用生成训练和测试数据的生成器函数生成如上图所示的信号。

  1. 这是一个 python 生成器函数,意味着它使用了yield关键字而不是return。每次调用生成器上的next()函数,它都会产生下一个结果。
  2. 该函数产生的信号正好有两个峰值。
  3. 所有信号的长度完全相同。
  4. 第一个峰值的位置均匀地分布在信号的第一象限中,但是第二个峰值的位置是正态分布的,但是我们也确保它不超出界限。
  5. 峰的宽度也是正态分布的。
  6. 我们分批返回峰值,这将有助于神经网络训练和评估。

请注意,这个生成器实际上会生成无限量的数据!凭借这一点,我们可以尝试为我们的玩具例子达到尽可能高的精度。

寻找峰值

现在我们有了发生器函数,我们可以使用标准的信号处理库来寻找峰值之间的距离。我们将使用scipy库和函数find_peaks()来寻找峰值。我们使用 R2 评分对模型进行评估。正如我们在下面看到的,我们得到了几乎完美的分数,预测误差主要是由于数字舍入误差。

Baseline performance:  0.9999812121197582

使用 CNN 测量距离

在设计神经网络时,想象人类操作员会做什么通常是有用的。在我们的例子中,操作是测量,测量的工具是一把尺子。在我们的例子中,我们使用 1D 卷积层模拟一个统治者,其内核大小设置为最大值,即信号的长度。这背后的原因是,如果层的值为 0,1,2,3,4,…乘以信号,它将准确地给出峰值的位置。我们使用两个过滤器,据说是为了测量两个峰值的位置,然后添加两个完全连接的层,让神经网络学习如何获取这两个测量值之间的差异。

作者创造的形象

我们的神经网络实现使用 Tensorflow 和 Keras。请注意,由于Conv1D需要一个三维张量,我们添加了一个整形层,增加了大小为 1 的第三维。批处理维度是隐式假定的。对于卷积层,我们不使用任何激活函数,因为我们希望该层的行为像一把尺子。请注意,我们不使用任何缩减采样机制(最大池或平均池)。我认为这些是不必要的,事实上可能会降低精度,因为它们使测量不太精确。在将数据发送到Dense层之前,我们添加了一个Flatten层来将维度(除了批处理维度)折叠成一个维度,因为这是Dense层所期望的。

Model: "sequential_26"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
reshape_26 (Reshape)         (None, 1000, 1)           0         
_________________________________________________________________
conv1d_33 (Conv1D)           (None, 1, 2)              2002      
_________________________________________________________________
flatten_25 (Flatten)         (None, 2)                 0         
_________________________________________________________________
dense_63 (Dense)             (None, 16)                48        
_________________________________________________________________
dense_64 (Dense)             (None, 16)                272       
_________________________________________________________________
dense_65 (Dense)             (None, 1)                 17        
=================================================================
Total params: 2,339
Trainable params: 2,339
Non-trainable params: 0
_________________________________________________________________

正如我们看到的,这个模型只有 2339 个参数,所以它是一个非常简单的模型。我们在 50 个时期内训练模型,但是我们还添加了EarlyStopping回调,以便在模型停止改进时停止模型的执行。我们添加了另一个回调TerminateOnNaN,如果梯度或损失变成了NaN,它将停止训练过程。我们将一个生成器函数传递给fit()方法。这是现在推荐的向模型传递数据的方法,尤其是当数据量很大时。在我们的例子中,生成器不断地生成随机的例子,实际上是无限数量的例子!由于我们的生成器函数没有一个历元的概念,我们需要定义一个参数steps_per_epoch,否则模型会认为所有批次都属于第一个历元,训练永远不会结束。

Epoch 1/50
100/100 [==============================] - 16s 157ms/step - loss: 55010.6915
Epoch 2/50
100/100 [==============================] - 16s 161ms/step - loss: 186.3050
Epoch 3/50
100/100 [==============================] - 16s 160ms/step - loss: 89.9977
Epoch 4/50
100/100 [==============================] - 16s 159ms/step - loss: 229.8199
R^2 score:  0.996036173273703

在训练了一个模型之后,我们看到测试集的分数确实令人印象深刻。虽然我们承认进一步改进算法是可能的,但我们得到的结果证明我们的简单方法确实有效。

结论

在设计神经网络时,想象人类的感知和认知是如何工作的通常是有用的。我们如何生成高级特征和概念通常会指导我们在神经架构中的选择。这种方法的一个例子是注意力机制,当我们根据阅读的文本形成概念时,这种机制是从人类的注意力中模拟出来的。在这个玩具问题中,我们代表了人类活动指导神经网络构建的另一个例子。虽然(与注意力机制一样)使用 CNN 测量距离本身没有用,但只要我们认为水平距离起作用,这种结构就可以被整合到更大的神经网络中,以解决更复杂的任务。

这款笔记本的代码可以在 my github repository 获得。

测量和增强图像质量属性

原文:https://towardsdatascience.com/measuring-enhancing-image-quality-attributes-234b0f250e10?source=collection_archive---------20-----------------------

发现用于测量和增强感知图像质量的主要属性

在开始讨论测量或增强图像质量属性之前,我们必须首先正确地介绍它们。为此,我从《相机图像质量基准测试》一书中获得了灵感,该书非常详细地描述了我将在这里谈到的属性。需要注意的是,虽然书中描述的属性是相机属性,但我们的讨论是围绕图像属性展开的。幸运的是,一些相机属性也可以用作图像属性。

测量属性

1.暴露

通常指曝光时间,这是影响图像中光量的相机属性。对应的图像属性其实是亮度。有多种方法可以计算亮度或等效测量值:

  • 我发现从 RGB 映射到 HSB(色调、饱和度、亮度)或 HSL(色调、饱和度、亮度),并且只查看最后一个分量(L 或 B)是可能的。
  • Darel Rex Finley 提出了一个非常好的感知亮度的测量方法,其中:

作者图片

如果我们对所有像素进行平均,我们可以获得感知亮度的度量。此外,通过将结果值分成五份(因为最小值为 0,最大值为 255),我们可以定义一个等级:(非常暗、暗、正常、亮、非常亮)。

import cv
import mathimg = cv2.read(‘image.jpg’)def pixel_brightness(pixel):
    assert 3 == len(pixel)
    r, g, b = pixel
    return math.sqrt(0.299 * r ** 2 + 0.587 * g ** 2 + 0.114 * b ** 2)def image_brightness(img):
    nr_of_pixels = len(img) * len(img[0])
    return sum(pixel_brightness(pixel) for pixel in row for row in img) / nr_of_pixels

2.色调映射

高动态范围成像(HDRI 或 HDR)是一种在成像和摄影中使用的技术,与标准数码成像或摄影技术相比,它可以再现更大动态范围的亮度。虽然人眼可以适应各种光线条件,但大多数成像设备每通道使用 8 位,因此我们仅限于 256 级。HDR 成像处理每通道使用 8 位以上(通常为 32 位浮点值)的图像,允许更宽的动态范围。

什么是色调映射?

有不同的方法获得 HDR 图像,但最常见的是使用不同曝光值拍摄的场景照片。为了组合这些曝光,知道你的相机的响应函数是有用的,并且有算法来估计它。合并 HDR 图像后,必须将其转换回 8 位,以便在普通显示器上观看。这个过程叫做色调映射

测量图像是否经过良好的色调映射

从上面的定义,我提出(所以有可能是完全错误的)下面的步骤来测量色调映射。这背后的直觉来自当图像没有被正确地色调映射时直方图看起来的方式。大多数时候它们看起来是这样的:

最右边(突出显示剪辑)。这是照片 Mac 应用程序中的 RGB 直方图。图片作者。

最左边(阴影剪裁)。图片作者。

直方图的两端接触最末端,剪切高光和阴影。图片作者。

它们要么太暗(阴影剪裁),要么太亮(高光剪裁),要么两者兼而有之(例如,一间黑暗的浴室,镜子里可以看到闪电,或者一张半夜的灯杆照片)。

相比之下,色调映射良好的图像如下所示:

图片作者。

基于此,我提出(所以要半信半疑)一种尝试兼顾上述事情的评分方法。分数将在[0,1]之间,0 表示图像色调映射不正确,1 表示色调映射正确。除了饱和度效应之外,色调映射不佳的图像也可能是大部分亮度值位于紧密间隔内的图像(小方差= >可用色调较少)。

作者图片

  1. 为了简单起见,为了不使用不同的颜色通道,我们可以使用上面的亮度(pixel_brightness)。
  2. 我们构建一个亮度直方图(x 来自[0,255])
  3. 我们从直方图构建概率分布,在[0,1]范围内:

作者图片

4.我们定义了一个抛物线惩罚概率分布,即 0/0 和 1,最大值为 1/2(只要我们惩罚极端情况,这应该很好——因此低分数<=>大部分亮度集中在分布的头部和尾部)。

作者图片

(注意:这实际上是伯努利分布的一个简单例子,用作先验概率分布是一件好事)。

5.接下来,我们可以将“惩罚”亮度概率分布定义为。唯一剩下的就是适当的限制这个乘积…在 0 和 1 之间。第一部分已经解决了…这个乘积的最小值,对于所有的值都是 0。这是因为我们可以定义一幅黑白图像,其概率分布如下:

作者图片

我们可以看到,因为 f 是而不是 0,只有在 0 和 255 处,示例图像中所有像素的总和才会是 0。任何其他配置都会导致总和大于 0。

为了使总和最多为 1,我们可以使用高中的技巧,通过 CBS 不等式。总的来说:

作者图片

在我们的情况下,这将是:

作者图片

如果我们把左半部分除以右半部分,最后得到一个介于 0 和 1 之间的分数。因此,第一项的最终形式是:

作者图片

我不知道为什么,但它与皮尔森的相关系数非常相似…🤔

下一个术语我会简单地定义为:

作者图片

最后,我们得到以下色调映射分数:

作者图片

现在,让我们看看一些代码:

import math
import numpy as np
from scipy.stats import betaRED_SENSITIVITY = 0.299
GREEN_SENSITIVITY = 0.587
BLUE_SENSITIVITY = 0.114*def* convert_to_brightness_image(image: np.ndarray) -> np.ndarray:
    *if* image.dtype == np.uint8:
        *raise* ValueError("uint8 is not a good dtype for the image")

    *return* np.sqrt(
        image[..., 0] ** 2 * RED_SENSITIVITY
        + image[..., 1] ** 2 * GREEN_SENSITIVITY
        + image[..., 2] ** 2 * BLUE_SENSITIVITY
    ) *def get_resolution*(image: np.ndarray):
    height, width = image.shape[:2]
    *return* height * width *def* brightness_histogram(image: np.ndarray) -> np.ndarray:
    nr_of_pixels = get_resolution(image)
    brightness_image = convert_to_brightness_image(image)
    hist, _ = np.histogram(brightness_image, bins=256, range=(0, 255))
    *return* hist / nr_of_pixels *def* distribution_pmf(dist: Any, start: float, stop: float, nr_of_steps: int):
    xs = np.linspace(start, stop, nr_of_steps)
    ys = dist.pdf(xs)
    *# divide by the sum to make a probability mass function
    return* ys / np.sum(ys) *def* correlation_distance(
    distribution_a: np.ndarray, distribution_b: np.ndarray
) -> float:dot_product = np.dot(distribution_a, distribution_b)
    squared_dist_a = np.sum(distribution_a ** 2)
    squared_dist_b = np.sum(distribution_b ** 2)
    *return* dot_product / math.sqrt(squared_dist_a * squared_dist_b) *def* compute_hdr(cv_image: np.ndarray):
    img_brightness_pmf = brightness_histogram(np.float32(cv_image))
    ref_pmf = distribution_pmf(beta(2, 2), 0, 1, 256)
    return correlation_distance(ref_pmf, img_brightness_pmf)

3.纹理模糊

因为,模糊图像的边缘被平滑,所以variance小。这是 OpenCV 中的一行程序,简单的代码🎨:(https://stack overflow . com/questions/48319918/whats-the-theory-behind-the-computing-variance-of-a-image)。

import cv2def blurry(image, threshold=100): 
    return cv2.Laplacian(image, cv2.CV_64F).var() < threshold

左侧为原始图像,其余图像具有不同程度的高斯模糊。拉普拉斯算子随着高斯模糊程度的增加而减少。图片作者。

增强属性

  1. HDR 与多张照片

OpenCV 文档对此有很好的指导,高动态范围(HDR) 。

为了简洁起见,我这里只放用 Debevec 的算法得到的结果(http://www . pauldebevec . com/Research/HDR/Debevec-siggraph 97 . pdf)。

  1. 首先,用不同的曝光时间拍摄多张照片(曝光时间已知,相机不动)。

https://docs.opencv.org/3.4/d2/df0/tutorial_py_hdr.html

import cv2 as cv
import numpy as np# Loading exposure images into a list
img_fn = [“img0.jpg”, “img1.jpg”, “img2.jpg”, “img3.jpg”]
img_list = [cv.imread(fn) for fn in img_fn]
exposure_times = np.array([15.0, 2.5, 0.25, 0.0333], dtype=np.float32)# Merge exposures to HDR image
merge_debevec = cv.createMergeDebevec()
hdr_debevec = merge_debevec.process(img_list, times=exposure_times.copy())# Tonemap HDR image (i.e. map the 32-bit float HDR data into the range [0..1])
tonemap1 = cv.createTonemap(gamma=2.2)
res_debevec = tonemap1.process(hdr_debevec.copy())# Convert datatype to 8-bit and save (! 8-bit per channel)
res_debevec_8bit = np.clip(res_debevec*255, 0, 255).astype(‘uint8’)
cv.imwrite(“ldr_debevec.jpg”, res_debevec_8bit)

最终结果是:

https://docs.opencv.org/3.4/d2/df0/tutorial_py_hdr.html

2.闪光

寻找耀斑简化为在图像中寻找非常亮的区域的问题。我还没有找到发现图像是否有眩光的具体方法,只是为了纠正一个:该方法被称为 CLAHE (对比度受限的自适应直方图均衡化)。

import numpy as np
import cv2
​
img = cv2.imread('statue.jpg',0)
res = cv2.equalizeHist(img)
cv2.imwrite('global_hist_eq_statue.jpg',res)

在谈论 CLAHE 之前,最好知道为什么直方图均衡化不起作用:

作者图片

虽然直方图均衡化后背景对比度有所改善,但雕像的面部变得过于明亮。因此,本地版本是优选的,因此,使用自适应直方图均衡。在这种情况下,图像被分成称为“平铺”的小块(在 OpenCV 中平铺的大小默认为 8×8)。然后像往常一样对这些块中的每一个进行直方图均衡。因此,在一个小区域内,直方图会限制在一个小区域内(除非有噪声)。如果噪音在那里,它会被放大。为了避免这种情况,应用了对比度限制

import numpy as np
import cv2
​
img = cv2.imread('statue.jpg',0)# create a CLAHE object (Arguments are optional).
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl1 = clahe.apply(img)
​
cv2.imwrite('clahe_statue.jpg',cl1)

作者图片

关于直方图均衡的更多信息,请访问 OpenCV 文档(https://docs . OpenCV . org/3 . 1 . 0/D5/daf/tutorial _ py _ histogram _ equalization . html)。

测量咖啡研磨设备的萃取和 TDS

原文:https://towardsdatascience.com/measuring-extraction-and-tds-across-coffee-grind-settings-49dfd66d8203?source=collection_archive---------21-----------------------

咖啡数据科学

控制流量

咖啡萃取似乎仍然是一个谜,特别是当每一杯咖啡,人们必须拨入研磨咖啡。我只剩下几个问题,所以我开始了解提取是如何与研磨设置相联系的。

每个研磨设置是否有基本提取限制?

现在有些人可能会说,我们已经知道,一般来说,更细的研磨可以获得更高的提取率,但随着研磨变得更细,沟道效应的可能性增加,因此整体提取率可能会下降。为了获得最高的提取,你需要最细的研磨和最少的沟道效应。

所有图片由作者提供

因此,即使要测量研磨粒度的萃取率,也不能简单地下调研磨粒度,因为流量/通道变量与研磨粒度成反比,导致萃取率下降。

实验设计

我决定我要控制流动的变量。为了做到这一点,我会使用用过的咖啡渣来大大减少沟道效应的变量。如果所用的大部分咖啡来自用过的咖啡,那么大部分萃取物应该被分离到新鲜咖啡中,并且新鲜咖啡可以被混合,从而减少沟道效应。

我的目标是在同一天,用同样的咖啡,同样的一套用过的咖啡渣,以及相似的拍摄时间。我的目标是 5 个设置,外加一个刚刚用过的场地的控制镜头,其中 5 个研磨设置跨越了利基的浓缩咖啡设置。投入的总量将是 20g,其中 4g 是新烘烤的。

我花了很多时间来获得用过的咖啡渣,以提供所需的流动阻力。整个过程有助于改进如何进行实验。

硬件方面,我用了一个 Kim Express(弹簧手动杆)和一个小生磨。豆子在 2 周大的时候在家烘焙。

关于 Kim Express 的一点说明:我已经用这台机器拍摄了 2000 多张照片,虽然我使用温度枪来帮助准备在正确的时间拍摄,但我通常使用声音来知道机器何时达到了最佳设置。这是当蒸汽排气阀弹出,然后我开始拉一枪。大约 30 秒后,我会关掉机器,这样它就不会爆炸了(目前为止只有两次…)。

注射参数为预输注 10 秒,随后是从高到低的均匀流动压力分布。我没有压力计,但我用 Acaia 量表测量了流量。

我还将每个镜头分成 1:1 的输出到输入,2:1 和 3:1。所以第一针大约是 20 克,然后我分别收集另一个 20 克,最后一点 20 克左右。这张意大利腊肠照片在取样方面可以做得更好,但我不想让一个已经很复杂的实验变得过于复杂。

研磨分布

为了帮助这个实验,我使用我的图像技术来计算所有研磨尺寸的颗粒尺寸分布。

我这样做是作为题外话,因为我试图收集尽可能多的数据。好像设置 10 (S10)有问题,但我没搞清楚原因。我怀疑我没有准备好场地。

数据收集

我能够在一个小时内快速连续地捕捉所有这些数据。TCF 是时候盖上过滤器了。

甚至最后的冰球也不同于正常的镜头,因为它们更好地保持了它们的形状。

仔细观察圆盘,没有发现任何通道;低流量区域最终会比圆盘的其他部分更暗。随着设置的增加,颜色会稍微变暗,但这可能只是白平衡或场景照明。

S0、S5、S10、S15 和 S20

数据分析

我将从总溶解固体(TDS)和提取率(EY)开始,因为它表明粗磨粒的提取是有限度的。它还显示了比认为可能的更高的提取(> 30% EY),但这可以用对照样品来解释。

所以控制是用过的咖啡渣,在开发过程中,我重新研磨咖啡并再次运行。第一次运行 grounds 时,我的总 EY 为 2.01%,第二次运行时,我的 EY 为 0.85%,这就是我在此图中使用的值。

所以我根据这两张对照照片的第一张、第二张和第三张的 EY 修正了所有的 EY 值。这意味着我计算提取物的克数,并从对照中减去估计的克数。这里有一个例子来说明计算。

现在,当我们考虑通过控制和最坏情况控制进行调整时,假设咖啡中的总可溶物约为 30%,第三 EY(总 EY)看起来更合理。

趋势非常明显,但我注意到第二和第三部分的 TDS 在所有镜头中几乎相同。研磨尺寸对初始 EY 影响最大,也许这与预灌注占据第一次注射的一半有关。令人好奇的是,在预注入压力下运行该程序,以查看提取如何变化。

如果我们看一下趋势,似乎最大的下降发生在从设置 15 到设置 20。

累积 EY

另一种观点:

喷射流

既然收集了流量数据,我们就来看看。我用产出重量对时间的原始数据来表示,我调整了咖啡开始装满杯子时的所有流量。

通过估计斜率,设置 15 似乎是一个异常值。

拍摄时间

对于拍摄时间,我测量了覆盖过滤器(TCF)的时间,以及我拉第一个和第二个杯子的时间。趋势只是轻微的,除了第一次,我没有足够的时间分辨率来区分对时间的影响。似乎有一个小趋势,但我需要更多的小数点和潜在的更多数据来对结果有信心。

这些实验表明,就提取而言,较大的研磨设置对浓缩咖啡有很大的限制。虽然许多低于 20 的研磨设置在提取率上更接近,但这为如何提高提取率提供了指导。

纸和布的技术之所以有效,是因为它们可以在不产生沟流的情况下变得更精细。分层发射也增加了提取,因为它们减少了局部沟道效应。对于给定的研磨粒度和给定的咖啡豆,有一个基本的限制,虽然这个信息似乎是给定的,但这个实验有助于解决咖啡领域数据匮乏的问题。

如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。也可以关注我中。

我的进一步阅读:

浓缩咖啡系列文章

工作和学校故事集

个人故事和关注点

乐高故事启动页面

摄影启动页面

使用图像处理测量咖啡研磨颗粒分布

改善浓缩咖啡

断奏生活方式概述

测量咖啡研磨分布

咖啡萃取

咖啡烘焙

咖啡豆

浓缩咖啡用纸质过滤器

浓缩咖啡篮及相关主题

意式咖啡观点

透明 Portafilter 实验

杠杆机维护

咖啡评论和想法

咖啡实验

衡量年龄差异时的“公平性”

原文:https://towardsdatascience.com/measuring-fairness-when-ages-differ-177d9597dd3b?source=collection_archive---------29-----------------------

公平和偏见

安娜·范德·斯特尔在 Unsplash 上的照片

构建公平性度量标准通常包括将人群分成子组,然后检查不同组之间模型性能的差异。例如,您可以按性别划分人口,然后测量女性与男性的准确率和假阳性率。然而,当亚人群的基本年龄分布不同,并且你的结果随年龄而变化时,公平指标的差异是不可避免的,即使没有群体成员的内在影响。这是真的,即使一个模型“调整”年龄。

年龄并不是唯一的——任何关键预测因素的变化都会导致公平指标的差异。然而,我认为年龄值得特别考虑。在美国(和许多其他地方),种族/民族群体在年龄特征上有很大差异,这有可能影响许多模型[1]。尽管美国总人口中的性别差异没有那么极端,但我发现实际上年龄经常因性别而异——例如,在美国劳动力市场上,女性比男性更年轻,这可能是由于一些出生队列的教育差异和家庭照顾责任[2]。至少在我的经验中,大多数用于建模的人口显示了不同利益群体在公平性度量上的年龄差异。年龄也是一个需要考虑的重要因素,因为它影响人们生活的方方面面,从收入、健康状况到行为。此外,年龄通常被认为是一个敏感的特征,这有时意味着它被忽略,而它的影响可能需要深思熟虑。即使年龄不适合用作决策依据,但在模型验证期间忽略它也可能是一个错误。

例如,假设我们想要对公司员工的 401(k)参与进行建模,也许是为了构建有针对性的电子邮件活动来增加注册人数。当我们构建模型时,我们可能会发现它预测男性的参与率更高。仅仅根据年龄,其中一些是可以预测的;在劳动力中,男性的平均年龄更大,年龄与退休计划的获得和参与有关[3]。此外,假设平均年龄接近职业生涯中期(30-50 岁),我们预计年龄较大的群体(男性)收入更高[4],这也可能增加男性的储蓄率。

如果我们检查这个模型的公平指标,我们会看到性别之间的差异,无论年龄是否包括在模型中。如果模型包含年龄特征(或代理),我们可能会看到男性的假阳性率更高,因为他们更有可能具有中等的参与概率。相比之下,如果不包括年龄,我们可能会预计女性的假阳性率更高,因为我们隐含地假设每个人的“平均”年龄特征,这意味着高估了女性。我还没有提到收入等特征的性别差异,这将导致额外的差异。在评估这个模型时,我们必须注意年龄分布、性别效应以及算法中的错误或偏差造成的差异。

在这篇文章中,我用美国人口普查数据来说明公平指标如何受到人口年龄差异的影响,即使没有任何群体成员的直接影响。计算可以在 Jupyter (Python 3)笔记本上进行[5]。按种族/民族划分的年龄数据从 2019 年美国人口普查中下载,并用于模拟随机和基于年龄的变化过程。

模拟的过程不依赖于种族/民族;不同群体之间的差异仅仅是因为年龄。为简单起见,我只显示两个组的结果,选择一个相对较老的组(只有白人,不是西班牙裔或拉丁裔)和一个相对较年轻的组(西班牙裔或拉丁裔)。为数据构建逻辑回归模型,并在这些种族/民族群体中构建公平性度量。我证明了仅由于年龄分布造成的公平性度量的巨大差异。年龄分层可以部分缓解度量差异。

人口年龄特征

人口普查数据可直接下载;这个过程在 Jupyter 笔记本[5]中有说明。我使用的是 2019 年美国社区调查按种族/民族划分的年龄数据(“B01001”表)[1]。这一来源包含按年龄、性别和种族/族裔群体统计的八个种族/族裔群体。计数以不同大小的年龄组提供,例如 18-19 岁与 65-74 岁。我结合了性别统计,并使用平滑法构建了一个更细粒度的分布。这使我能够以一年的分辨率获得按种族/民族划分的近似年龄分布。我选择的亚人群的结果曲线如下所示。

两个种族/民族群体的大致年龄分布,基于 2019 年美国社区调查数据[1]。图片作者。

我选择这两个群体,是因为他们相对“年轻”和“年老”。在下面的部分中,我将人群限制在 18 岁以上。我从上述每个分布中随机抽取 5000 个人,总数据集由 10000 个成年人组成,平均分布在各个种族/民族群体中。

在我的样本中,西班牙裔或拉丁裔群体的成年人口年龄中值为 43 岁,而非西班牙裔或拉丁裔群体的白人年龄中值为 53 岁。这些中值差异很大(~10 年),但白人在高年龄时相对“肥胖”的尾巴也有助于预测和度量值的差异。具有相似中间值但极端值不同的人群会显示出很大的差异,特别是当年龄效应是非线性的时候(这里,我使用一个简单的线性模拟)。

模拟和建模

我模拟了一个简单的二元过程,这个过程依赖于一个变量,这个变量独立于年龄和种族/民族,并且随着年龄线性增长。对于样本中的每个人,我将概率构造为:

概率 = -6 + x + 0.1 * 年龄 +(随机噪声)

在上面的例子中,年龄是以年来衡量的,x 具有随机正态分布。随机噪声分量是随机正态的,但权重为 0.1。选择模拟系数,使基本比率约为 1/3 的阳性结果,并使年龄和 x 的相对影响相似。我使用上述概率从每个案例的二项分布中抽取样本,给每个人分配一个二元(0/1)结果。

然后,我将模拟的二进制结果拟合到一个逻辑回归模型,即 y = x + 年龄。由于我的模拟过程与我的模型匹配得如此之好,我的拟合产生了输入系数; x 的模型系数为 1.029823,年龄的系数为 0.10084。然后,我对 sklearn.linear_model 使用 predict()函数。LogisticRegression 对象为每个人生成结果(这实际上是一个 50%的阈值)。公平性度量,例如假阳性率,是通过比较模拟结果与模拟结果来构建的。

公平性度量结果

我查看了三个常见的公平性指标:假阳性率、假阴性率和模型准确性。下表显示了选定种族/民族群体的结果:

模型 y = x + 年龄按种族/民族分组的公平性度量结果。

这两种人群的错误率有很大不同。单独白人组的假阳性率是两倍多,而假阴性趋势相反,西班牙裔或拉丁裔组的错误率几乎是两倍。

虽然在这个简单的例子中,两组的准确性度量是相似的,但是在一个更复杂的模型中,决策阈值是可调的,我们可以看到这个度量是变化的。例如,通常通过最大化 f1 度量来设置阈值。这通常将决策阈值推向更低的概率值,潜在地降低了更可能产生积极结果的组的准确性。

度量差异是意料之中的

这两类人群在公平性方面的差异并不令人惊讶。研究人员已经表明,对于基础率不同的校准模型来说,误差率差异是不可避免的[6,7]。

我的简单示例是一个近乎完美的模型,这意味着模型概率与个别情况下的“实际”概率非常相似(对于大多数应用程序,实际的个人概率是不可知的)。根据定义,这个模型是非常精确的。虽然这个例子是不现实的,但它提供了文献中概述的原则的可视化描述。如果我们按种族/民族检查模型概率的分布,我们会看到:

按种族/民族划分的模型输出直方图。图片作者。

以误报为例。阳性预测可以由位于虚线右侧的概率分布曲线部分下面的区域表示,该区域表示 50%决策阈值。假阳性是(1-概率)乘以曲线的积分。因此,因为只有白人,而不是西班牙裔或拉丁裔曲线在虚线的右侧具有更大的权重,特别是在不接近 100%概率的地区,我们预计会有更多的假阳性。

很难想象在曲线上我们会看到相同的假阳性率和不同的总体率。Kleinberg 等人表明,这种情况可能发生在琐碎的条件下——例如,当我们完全知道每个人的结果,没有假阳性或假阴性时;这将由两组在 0%和 100%的双峰表示[6]。

在现实世界的模型中,概率不会如此完美地反映潜在的过程,但我们仍然希望当基本利率不同时,模型概率分布曲线不会重叠。一个群体的曲线将更多地向其他群体的右边或左边移动。根据决策阈值的位置,我们在图中产生假阳性或假阴性的区域有更大的“权重”。

将年龄纳入公平指标

一个潜在的缓解年龄差异的策略是按年龄对人群进行分层,然后比较年龄组内的种族/民族结果。针对假阳性率,这种解决方案的一种尝试如下所示:

在 y = x +年龄模型中,按种族/民族和年龄段划分的假阳性率。

在上面,我们看到按年龄分层有助于减少指标的差异。然而,一些差异仍然被认为是有意义的。例如,对于 40-69 岁的年龄组,白人的假阳性率比西班牙裔或拉丁裔高 51%。这是因为潜在的白人群体,而不是西班牙裔或拉美裔群体,有更多的人接近年龄上限,而西班牙裔或拉美裔群体倾向于低端。

选择要检查的年龄组可能很棘手,尤其是在数据有限的情况下。在年龄组内,年龄分布的形状最好是相似的,或者是平坦的。在实践中,如果不使用细粒度的年龄级别,这可能很难实现。在美国,只有白人,而不是西班牙裔或拉丁裔群体在 70 岁左右有一个大的峰值,而大多数其他人口在该值附近显示出下降趋势。有时,特定的年龄水平与您正在建模的过程相关;例如,教育储蓄、退休和健康保险资格都有特定的或截止年龄。因此,在选择存储桶时,业务问题和年龄分布形状都很重要。

虽然按年龄分层是一种艺术形式,但即使是简单的划分也会产生明显的效果,这可以帮助你决定年龄是否需要进一步考虑。因此,即使只有两个大桶,通常也值得按年龄对公平性指标进行分层。

包括还是忽略年龄

一个误解是,如果一个模型“调整”(合并)年龄,公平指标也将被纠正。然而,因为年龄影响基本费率,我们期望公平性度量差异,包括准确性和错误率差异,无论模型是否包含年龄。上面,我已经展示了即使是一个包含年龄的近乎完美的模型的差异。

如果我构建一个忽略年龄的模型,精度会下降,这是意料之中的,因为这个模型不是模拟过程的完美近似(见 Jupyter 笔记本[5])。如果没有年龄分层,种族/民族类别之间的一些指标看起来更相似,主要是因为错误率对每个人来说都更糟。同样,按年龄分层减少了群体度量差异。

在这个模型中加入年龄在某种程度上就像“水涨船高”——它降低了总体错误率。然而,包含年龄可能会使模型显得不太公平,因为群体差异可能会变得更加明显。

最后的想法

似乎与种族/民族、性别等相关的公平性度量差异。,可能有年龄的原因。即使当一个模型包含了年龄,甚至没有群体成员的独立影响,这样的“失败”也会出现。

按年龄对指标进行分层可以部分纠正一些指标差异。此外,考虑年龄可能影响结果的机制也很重要。年龄的影响是否主要与另一个因素相关,例如收入、教育、健康状况、婚姻状况或工作级别?还是有独立的作用?是否存在与性别或种族的互动?

决定一个模型是公平还是不公平需要理解差异的原因。如果不考虑年龄效应,试图修正或调整模型以均衡度量标准可能会产生意想不到的后果。此外,因为年龄差异而“原谅”跨(例如)种族/民族群体的错误率差异可能合理,也可能不合理。伦理审查委员会和利益相关者应考虑背景,并询问与年龄分布相关的问题,然后仅根据公平性指标做出判断。

参考

[1]美国人口普查局, B01001 表格 (2019),美国社区调查。

[2]国会预算办公室,影响 25 至 54 岁人口劳动力参与的因素 (2018),报告,2 月 7 日。

[3]皮尤慈善信托基金,跨代退休计划的获取和参与 (2017)。

[4]美国劳工部劳工统计局,2005 年按年龄和性别分列的收入差异 (2006 年),《经济日报》。

[5].v .凯里,GitHub 库,https://github.com/vla6/Blog_age_fairness。

[6] J. Kleinberg,S. Mullainathan 和 M. Raghavan,风险分值公平确定中的内在权衡 (2017),《理论计算机科学创新论文集》。

[7] G. Pleiss,M. Raghavan,F. Wu,J. Kleinberg 和 K. Q. Weinberger,关于公平性和校准 (2017),神经信息处理系统进展,5680–5689。

使用先进的图像处理技术测量咖啡渣中的微粒

原文:https://towardsdatascience.com/measuring-fines-in-coffee-grounds-using-advanced-image-processing-148f048bdb7?source=collection_archive---------37-----------------------

咖啡数据科学

使用图像处理了解磨床性能第 3 部分

之前,我探索了如何使用几个筛子结合成像来给出看起来不错的颗粒分布,以帮助检查研磨机。现在,我将深入研究这些图片,看看有什么可以改进和清理的地方。我在一些图片中注意到了一些问题,其中一个令人困扰的问题是微粒聚集在一起或者粘在较大的颗粒上。

快速回顾

我拍了一张照片,用一张纸确定了测量地面的真实情况(像素到毫米),然后用一个阈值来寻找粒子。

所有图片由作者提供

连接的组件

在我将咖啡渣的图像转换成二值图像后,我使用连接的组件找到所有的单个粒子:

  1. 8-连通性:一个像素与另一个像素相连,如果它在 8 个周围像素中的任何一个。
  2. 4-连通性:如果一个像素在上方、下方、右侧或左侧,但不在对角线上,则该像素与另一个像素相连。4-连通性是 8-连通性的子集。

所有图片由作者提供

在这个咖啡颗粒的特写视图中,我注意到一些颗粒以这样的方式连接在一起。

粒子强度的假颜色

所以我在寻找连通分量时切换到了 4-连通性,这似乎让我获得了几个百分比的粒子。

重叠粒子

然后我看了一些重叠的粒子。所以我看了一些假彩色的图像。

有一些奇怪的形状。下面,似乎大部分的碎片都有多个粒子重叠。

粒子有许多重叠的方式,尤其是当它们大小不同时,如下图所示:

基于形状移除粒子

所以我看了形状的周长和等效直径。如果这些差不多相等,那么这个质点就是一个圆。在最好的情况下,咖啡渣是球形的,但正如我们之前看到的,咖啡不是圆形的。似乎有相当多的粒子不是圆形的,如下图所示:

我专注于宽度超过几个像素的粒子,我想研究周长/Pi:等效直径的比值大于 1.1 的粒子。这个数字是任意的,但我想我会这样开始。有趣颗粒的数量随着我在普通和变焦图片中观察咖啡的方式、通过筛子和通过研磨设置而变化。

所以我看了几个例子:

有可能这些粒子可以用图像强度来分离,但我想我会把它们扔掉。当它们在被成像的粒子总数中没有那么大时,为什么要担心它们呢?

颗粒尺寸的亚像素近似

咖啡颗粒的困难在于它们可能非常小。一旦你看到一个 1 x 1 像素的粒子,它大约是 100 微米,你不知道它是否真的更小。所以我用平均像素强度来估计更小的颗粒。

单个 1 像素直径的像素可以处于最大强度或更小。通常,超过几个像素的好粒子在中心具有最大强度,而不是在侧边。考虑到图像中没有其他可能造成混乱的攻击者,这允许更高的分辨率。

对粒子分布的影响

让我们从最初的 8-连接开始:

在这里,我们有 4 个连接,我删除了缺乏圆形的粒子,并通过强度作为亚像素近似计算直径:

这里有一个比较,但可能很难看出:

如果我们在不使用筛选知识的情况下观察地面的图像,我们也可以应用这些额外的技术。这些变得更加合理,但是 15 和 30 在这里很难区分。我认为使用少量粉末的筛子对于更精确的测量仍然很重要。

左:8-从地面图像连接,右:4-使用强度连接相同的图像并丢弃坏数据

对单个咖啡颗粒进行成像更加复杂,因为它们并不总是表现良好。使用具有两个筛网的筛子确实大大改进了这种计算,并且使得确定颗粒分布更加容易。但是,这不是首选方法。

如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。也可以关注我中。

我的进一步阅读:

浓缩咖啡系列文章

工作和学校故事集

个人故事和关注点

乐高故事首页

摄影飞溅页面

使用图像处理测量咖啡研磨颗粒分布

浓缩咖啡过滤器:分析

浓缩咖啡过滤篮:可视化

建模咖啡研磨机

浓缩咖啡过滤器对比:佩萨多 vs VST

浓缩咖啡篮(VST):有脊与无脊

IMS superficial vs VST:小样浓缩咖啡过滤器对比

浓缩咖啡模拟:计算机模型的第一步

克鲁夫:对技术状态的进一步分析

克鲁夫咖啡筛:一项分析

咖啡篮隆隆声:机器人 vs 铂尔曼 vs 体面

使用时间词嵌入测量语义变化

原文:https://towardsdatascience.com/measuring-semantic-changes-using-temporal-word-embedding-6fc3f16cfdb4?source=collection_archive---------10-----------------------

思想和理论

如何使用时间单词嵌入来测量单词演变的指南,以及关于嵌入方法稳定性的一些考虑

字典的目的是捕捉词义,但是我们可以使用 NLP 来捕捉词义吗?照片由亚伦·伯顿在 Unsplash 上拍摄

如果我想知道单词随着时间的推移是如何变化的呢?例如,我可能想量化某些词(如“面具”或“锁定”)在新冠肺炎疫情之前的使用方式,以及它们在疫情中是如何演变的。从语言和文化的角度以及从政策的角度来看,检测单词的用法如何以及何时随时间变化是有用的(例如,在事件或政策实施之后,某些单词的使用方式是否发生了变化?).

语义变化(有时也叫语义移位语义漂移、语言演变 ) 是指词义随时间的演变。语言学家长期以来一直对测量、研究和量化语义变化感兴趣。单词可以在很长一段时间(即几十年或几个世纪)内发生变化,其中单词的核心含义会发生变化;或者在较短的时间内(即几个月或几年),由于文化事件(如技术进步)而发生变化。

跟踪语义变化的一种方法是统计原始词频(Hilpert & Gries,2009 年)或统计一个词随着时间推移与另一个词搭配的频率(Heyer,Holz & teres niak,2009 年)。更复杂的工作建立在分布式语义的基础上,分布式语义根据一个词的邻居来衡量这个词的语义变化。分布语义学建立在这样的假设上,即出现在相同上下文中的单词往往具有相似的含义。语言学家约翰·弗斯是这一领域的先驱,他在著名的声明中这样描述它:“你可以通过一个人交的朋友来了解这个人”(弗斯,1957)。

下面的例子显示了三个词在几十年里的演变,这在 Hamilton 等人(2016)写的论文中有所描述。我们可以评估在不同时间点最常与关键字相关联的上下文单词,并比较这些上下文来确定单词如何随时间变化。例如,“可怕”这个词在 19 世纪 50 年代已经从威严和庄严的同义词转变为 20 世纪初表示恐怖,20 世纪 90 年代表示怪异和奇妙。下图中的关系是使用时态单词嵌入捕获的。

从汉密尔顿等人(2016)写的论文中可以看出,“历时词嵌入揭示了语义变化的统计规律。”此处链接到论文。

什么是时间词嵌入?

最近,使用神经单词嵌入或使用神经网络语言建模方法训练的单词来表示单词变得流行起来。在这种情况下,通过在大型文本语料库上训练,为每个单词学习密集向量。神经单词嵌入扩展了分布语义的思想,因为每个单词的向量表示基于其上下文单词的分布。

假设感兴趣的语料库包含与时间戳相关联的文档,则该语料库可以被划分成不同粒度的时间段:几十年、几年、几个月、几周、几天,或者有时甚至几个小时。这完全取决于数据集和项目的上下文。为对应于所选时间段的每个子进程训练单词嵌入。这些嵌入被称为时间单词嵌入(也称为历时单词嵌入或动态单词嵌入)。

本文将关注 Word2Vec,因为它训练速度快,并且在磁盘空间和内存消耗方面便宜(Levy,Goldberg,& Dagan,2015)。此外,Word2Vec 是一种流行的测量语义变化的方法,已在许多有影响力的研究论文中使用(作为研究的重点或作为比较的基准)。

还有许多其他方法来训练时态单词嵌入,这不是本文的重点。例如,其他单词嵌入,如 GloVe 和 FastText,可以以类似的方式用于测量语义变化。像 Compass-aligned 分布式嵌入这样的包使得训练你自己的对齐时态嵌入变得容易(基于 Word2Vec)。基于转换器的模型,如 BERT,也已经被用于跟踪大型文本语料库中的时间性。评估更全面的时态单词嵌入列表超出了本文的范围;更完整的清单,建议读者查阅库图佐夫等人,(2018) 或塔玛塞比等人,(2019) 所做的调查。

使用时间单词嵌入测量变化

回想一下,每个单词对应一个密集的向量表示,这是通过在大型文本语料库上进行训练来学习的。可以在一个时间段计算一个词的余弦相似度,在下一个时间段计算同一个词的余弦相似度,以测量语义变化。Montariol (2021)描述了两种可用于跟踪语义随时间变化的方法:

  1. 初始漂移,计算在时间 t=0 时一个字相对于该字的变化
  2. 增量漂移,计算从一个时间片到下一个时间片的字的变化

还可以计算两个感兴趣的单词之间的余弦相似度,以查看这两个单词相对于彼此如何随时间变化。例如,在论文“基于扩散的时态单词嵌入”(Farhan 等人,2020 年)中,作者展示了单词“COVID”在 2020 年的几个月中与四个单词(中国、流行病、疫情、患者)的余弦相似性。这种方法允许研究人员可视化趋势,例如一个词与其他关键词的相似度如何随时间变化。需要注意的是,作为研究者,你需要提前准备好感兴趣的单词列表。

训练时态单词嵌入

在不同时态语料库上独立训练的神经单词嵌入(如 Word2Vec)无法直接比较。为每个时间片学习的向量空间是不同的-也就是说,在相同数据上训练的模型可能产生具有相同最近邻但不同坐标的向量空间(Kulkarni 等人,2014 年)。这是由于单词向量的随机初始化以及处理文档的顺序所产生的训练过程的随机方面(Hellrich & Hahn,2016)。因此,为了确保两个向量空间是可比较的,它们必须通过统一的坐标系对齐。

注意,对于基于计数的嵌入,例如 PPMI(正逐点互信息),对齐是不必要的,因为可以直接比较所有时间片的共生矩阵的交集。对齐通常通过使用线性变换将所有嵌入空间映射到公共向量空间来完成。对齐向量空间的最流行的方法之一是使用正交 Procrustes 分析来学习两个嵌入空间之间的线性映射,首先由 Hamilton 等人在 2016 年提出。使用正交 Procrustes 来对齐嵌入空间仍然是一种流行的方法,代码和项目是公开可用的。

然而,研究也表明不稳定性可能来自对齐时间单词嵌入。而正交 Procrustes 是对齐时间嵌入最常用的方法之一(戈宁,贾瓦哈尔,塞达和戈德堡,2020;Montariol,2021),它容易受到一些限制。首先,这是一个复杂的对准过程,并且在该过程中可能会引入误差。第二,该方法需要使用所有嵌入空间中词汇的交集来对齐嵌入空间,这意味着不能比较在稍后时间点出现的新词。最后,该程序需要大量的训练数据,这些数据可能无法用于所有的数据集(Dubossarsky、Weinshall 和 Grossman,2017)。

单词嵌入的稳定性

除了用于通过线性变换对齐单词嵌入的方法的不稳定性之外,研究人员还对单词嵌入模型的稳定性和脆弱性提出了质疑。例如,可以通过改变不同的超参数来测量单词嵌入模型的稳定性和一致性。Hellrich 和 Hahn (2016)评估单词嵌入的可靠性,通过几个相同参数化的 Word2Vec 模型的前 N 个最近邻居的交集来测量。作者发现,可靠性取决于模型被训练的时期数。此外,基于他们发现用相同的超参数训练的模型产生不一致的最近邻,他们建议训练几个相同参数的模型并将它们组合成一个集成以确保稳健的结果。Pierrejean 和 Tanguy (2018)改变了其他超参数,如体系结构(即 CBOW 和 SGNS)、语料库、上下文窗口大小和嵌入维度大小,发现在不同的嵌入空间中,相同单词的最近邻居有很大的变化。

Antoniak 和 Mimno (2018)通过显示单词嵌入对变量(如文档顺序、语料库大小和随机种子)的微小变化的敏感性,强调了单词嵌入的脆弱性。作者在不同的文档大小和语料库上为四个不同的嵌入模型训练了 50 个模型。对模型进行三种文档顺序设置的训练:固定(原始顺序的文档)混洗(随机顺序的文档)自举(替换取样的文档)。Jaccard 相似性用于比较用每个配置组合训练的模型的前 N 个最近邻。Jaccard 相似性度量两个集合之间共享项目的比例。两个集合 A 和 B 的 Jaccard 相似度计算如下:

Antoniak 和 Mimno (2018)发现,在目标词的最邻近词及其排名方面,词嵌入模型有很大的可变性,即词作为目标词的最近邻近词出现的顺序。对于自举文档来说,这种可变性增加了,因为这放大了“突发”单词的影响,这些单词在本地很常见,但在全球很少见。作者得出结论,由于这种巨大的差异,嵌入不能提供语言或语料库的客观观点,建议未来的研究对多个自举模型的结果进行平均。

作为一种解决方案,戈宁等人(2020)提出了一种替代方法来测量时间变化,而不需要对齐单独的嵌入,他们声称这种方法可能不稳定,不太可靠。作者建议用最近的邻居来代表意义。使用在不同时间语料库上训练的单词之间的前 N 个最近邻居的交集来测量语义变化。该度量用于相同单词嵌入算法的多次运行,并且能够以高稳定性检测语义变化。作者建议使用这种比较时间单词嵌入的更简单的方法,因为它比使用常见的正交 Procrustes 方法进行时间对齐更容易解释和稳定。

一个简单的实验

如上所示,虽然为了直接比较它们,有必要对齐在诸如 Word2Vec 的神经方法上训练的时间嵌入,但是这样做太多会引入不稳定性。围绕这个问题的一个解决方案是使用更简单的方法来衡量变化。一种替代方法是在两个不同的时间点测量一个单词的最近邻集的 Jaccard 相似性,而不是对齐并使用余弦相似性来比较不同嵌入空间中的单词。

此外,训练单词嵌入本身可能引入脆弱性。每个时间片仅训练一个单词嵌入可以捕捉有意义的变化,但是也会引入大量噪声。几名研究人员提出的一个解决方案是训练许多模型并对结果进行平均,创建一个模型的集合。此外,训练文档可以被混洗或引导,以将更多鲁棒性引入训练过程。

在这里,我将逐步设计最简单的实验来训练时态单词嵌入。

  1. 收集你的文本语料库。像处理任何 NLP 项目一样清理文本,然后确定要将文本划分为哪些时间间隔。这可能是几十年到几天的时间,取决于您的数据。您还需要确保有足够的数据来为每个时间间隔的模型定型。例如,如果您每天只有几十或几百个文档,那么您可能希望将时间窗口扩展到一周或一个月。
  2. 选择你喜欢的单词嵌入模型。带 SGNS 的 Word2Vec 可能是最容易开始的——Gensim对于训练模型来说相当容易。
  3. 为每个带时间戳的子公司培训多个(> 50 个)模型。您可以尝试为模型的每次运行改变超参数:随机种子、时期的数量、上下文窗口的大小。您也可以尝试使用不同的文本来训练您的模型-通过打乱文档的顺序,以及使用替换来引导您的文档,以确保某些文档在某些模型中可以多次看到,而在其他模型中根本看不到。对每个时间步长完成该步骤。
  4. 您不能对模型间的嵌入进行平均,因为它们不在同一个嵌入空间中,但是您可以在不同的嵌入空间中查找相同单词的最近邻。例如,如果您已经在关于疫情的报纸文章语料库上进行了训练,并且对单词“mask”如何随着时间的推移而演变感兴趣,您可以为每个模型确定单词“mask”的前 50 个最近邻,然后计算这些最近邻的 Jaccard 相似性。更高的相似性得分将表明更大的稳定性——尽管对超参数和训练文档进行了所有的调整,但为单词“mask”学习的向量表示是相当一致的。较低的相似性得分表明较低的稳定性。在每个时间步中完成所有模型的这一步。
  5. 选择一个阈值(可以针对每个时间步长,可以针对您感兴趣的每个关键词)来确定哪些词作为最近邻对于每个时间步长是稳定的。例如,您可以确定,如果单词在一个时间步长中有 95%的时间显示为“mask”的最近邻,则这些单词被视为“稳定的”最近邻。在每个时间步中完成所有模型的这一步。
  6. 对每个时间步长取稳定的最近邻,计算集合的 Jaccard 相似性分数。这将指示一个单词的最近邻在时间轴上变化了多少。您可以针对您感兴趣的多个不同的关键字完成此过程。
  7. 或者,您可以创建一组感兴趣的关键字,并查看每个时间步长的最近邻的 Jaccard 相似性,以生成您自己的度量。您可以随时跟踪该指标,以确定相似性度量在哪个时间点下降或上升。

这些只是入门的建议。你可能想知道最近邻的稳定性和那个词的频率之间的关系。您可能对单词的总体变化感兴趣,并且根据您的语料库,您可以调整阈值来确定稳定的最近邻。你也可能有兴趣看看哪些单词是不稳定的。例如,如果一个单词显示为“mask”的最近邻居,具有非常高的余弦相似度,但只有 30%的时间,那么该单词可能被用于非常特定(但不普遍)的情况。这样的调查将取决于你是否对单词如何随时间变化感兴趣,或者对单词如何被使用的有趣和具体的例子感兴趣。

结论

本文试图解释什么是时间词嵌入,以及它们如何被用来跟踪语义随时间的变化。讨论了关于单词嵌入的稳定性和对齐不同嵌入空间的常用方法的问题。描述了设计简单实验的步骤,并讨论了未来项目的潜在途径。时间词嵌入是自然语言处理中一个令人兴奋的研究领域,目前正被用于许多不同的子领域。我希望这个介绍有助于解释其中的一些概念。

参考

安东尼亚克博士和米米诺博士(2018 年)。基于嵌入的词语相似度的稳定性评估。计算语言学协会汇刊,6,107-119。

布卢姆尔德湖(1933 年)。语言。英国牛津:霍尔特。(第 573 页)。

Dubossarsky、d . wein shall 和 e . Grossman(2017 年 9 月)。失去控制:语义变化的规律和单词表征模型中的固有偏差。《2017 年自然语言处理经验方法会议论文集》(第 1136-1145 页)。

法尔汉,a .,巴兰科,R. C .,侯赛因,硕士和阿克巴,M. (2020)。基于扩散的时态单词嵌入。

弗斯,J. (1957)。语言学理论概要 1930-1955。牛津。

h .戈宁、g .贾瓦哈尔、d .塞达赫和 y .戈德堡(2020 年)。一种简单、可解释且稳定的方法,用于跨语料库检测具有用法变化的单词。在 ACL 中。doi:10.18653/v1/2020 . ACL-main . 51

汉密尔顿,W. L .,莱斯科维奇,j .,&茹拉夫斯基,D. (2016 年 8 月)。历时词汇嵌入揭示了语义变化的统计规律。《计算语言学协会第 54 届年会论文集》(第 1 卷:长篇论文)(第 1489–1501 页)。德国柏林:计算语言学协会。

Hellrich,j .,& Hahn,U. (2016 年 12 月)。坏公司|神经嵌入空间中被认为有害的邻域。《第 26 届计算语言学国际会议:技术论文》(2785-2796 页)。日本大阪:COLING 2016 组委会。

Heyer,g .,Holz,f .,和 Teresniak,S. (2009 年)。主题随时间的变化——通过主题意义的变化来跟踪主题。(第 228 页)

m .希尔贝特和 S. T .格里斯(2009 年)。评估多阶段历时语料库的频率变化:历史语料库语言学和语言习得研究的应用。文学和语言计算,24 (4),385-401。

kulkarni v .、Al-Rfou r .、Perozzi b .、s .和 skie na(2014 年 11 月)。语言变化的统计显著性检测。arXiv:1411.3315 [cs]。

库图佐夫,a .,奥夫列里德,l .,希曼斯基,t .,&维尔达尔,E. (2018,8 月)。历时词汇嵌入和语义转移:一个综述。《第 27 届国际计算语言学会议论文集》(第 1384-1397 页)。美国新墨西哥州圣达菲:计算语言学协会。

o .利维、y .戈德堡和 I .达甘(2015 年)。利用从单词嵌入中获得的经验改进分布相似性。计算语言学协会汇刊,3,211–225。

Martinc,m .,Montariol,s .,Zosa,e .,& Pivovarova,L. (2020 年 4 月)。捕捉单词用法的演变:只是增加更多的簇?2020 年网络会议的配套会议录,343–349。

Montariol,S. (2021 年)。使用单词嵌入的历时语义变化模型(巴黎萨克莱大学博士论文)。

皮尔雷让,b .,&坦吉,L. (2018,6 月)。走向定性单词嵌入评估:测量邻居变化。《计算语言学协会北美分会 2018 年会议论文集:学生研究工作坊》(第 32-39 页)。美国路易斯安那州新奥尔良:计算语言学协会。

北达科他州塔玛塞比、洛里博林和阿贾托特(2019 年 3 月)。词汇语义变化的计算方法综述。arXiv:1811.06278 [cs]。

使用 Python 度量两幅图像的相似性

原文:https://towardsdatascience.com/measuring-similarity-in-two-images-using-python-b72233eb53c6?source=collection_archive---------4-----------------------

了解如何用几行代码在 Python 中实现各种相似性度量。

约尔根·哈兰在 Unsplash 拍摄的照片

对于人眼来说,很容易分辨出两个给定图像的质量有多相似。例如,在下面的网格中显示的各种类型的空间噪声中,我们很容易将它们与原始图像进行比较,并指出扰动和不规则性。然而,如果想要量化这种差异,我们需要数学表达式。

图像中不同类型的简单噪声。图片作者。

在本文中,我们将了解如何使用一行代码实现以下相似性度量:

  • 均方误差
  • 均方根误差(RMSE)
  • 峰值信噪比( PSNR )
  • 结构相似指数( SSIM )
  • 全球质量形象指数( UQI )
  • 多尺度结构相似性指数( MS-SSIM
  • erreur Relative global adidimensionnelle de synthèse(ERGAS)
  • 空间相关系数( SCC )
  • 相对平均光谱误差( RASE
  • 光谱角度映射器( SAM )
  • 视觉信息保真( VIF )

sewar库可以用来实现所有这些指标(以及更多)。

从安装 sewar 开始:

pip install sewar

然后导入必要的模块,

这些模块很好用,可以直接调用,如下图。

对于每种噪声方法,我们可以看到下面的相似性结果。原始栏显示原始图像与自身比较后的分数,以便查看 理想 分数。

不同类型去噪方法的相似性度量得分

每种去噪方法的值都与从上面的图像网格中直观获得的结果相对应。例如,S&P(盐和胡椒)和泊松方法添加的噪声不容易被肉眼看到。然而,我们可以通过仔细观察图像来发现它们。在相似性得分中,我们可以看到,与其他去噪方法相比,S&P 和泊松显示出更接近理想值的值。从其他噪声方法和度量也可以得到类似的观察结果。

从结果来看,似乎 ERGAS、MSE、SAM 和 VIFP 可以足够灵敏地捕捉到添加的噪声并返回放大的分数。

但是这种简单的量化能有什么用呢?

最常见的应用是将重新生成或重建的图像与其原始的干净版本进行比较。GANs 最近在图像去噪和清洁方面做得非常好,这是出了名的——这些指标可以用来衡量模型在视觉观察之外重建图像的实际效果。使用这些相似性度量来评估大量生成图像的再生质量可以减少在视觉上评估模型的人工工作。

此外,已经观察到,当与其良性对应物比较时,相似性度量也可以用于突出图像中对抗性攻击的存在。因此,这些分数可以作为量化这些攻击带来的扰动量的度量。

让我们在评论中讨论这些图像相似性度量在现实生活中更有趣的应用方式!

请关注并支持一位 AI 爱好者!

感谢您从头到尾的阅读!您可以通过 LinkedIn 联系我,获取任何信息、想法或建议。

计量经济模型结构稳定性的度量

原文:https://towardsdatascience.com/measuring-structure-stability-of-econometric-models-d8eb3a56e1bd?source=collection_archive---------23-----------------------

行业笔记

时间序列预测最简单最重要的想法

在本文中,我想从一位数据科学家的书中摘录一页,并探索数据科学社区作为其建模过程的一部分而实施的一个重要护栏——独立于模型性能定义模型稳定性。

随着大数据的兴起,可用于为给定问题建模的变量数量呈指数级增长。例如,用于识别歌曲或电影的模型不再仅仅使用通用变量,如以前看过的电影或喜欢的歌曲等。现在,我们可以包括一系列使用户简档更加动态的指标-一天中的时间天气历史潜在情绪**喜欢但没有再次听过的歌曲等等。这份名单可能会永久增长,而且很可能会。每天我们拥有最少的数据,却拥有最多的数据。每天都有更多的关联被发现。有些是因果,有些不是。

我们如何知道哪些变量可以使用,哪些可以忽略?如果一些变量对预测一个用户的行为很重要,但对另一个用户却不那么重要,那该怎么办?

幸运的是,数据科学提出了定义模型稳定性的想法。这是定义模型性能的另一种方式,但它并不完全依赖于预测的准确性。“稳定”是一个流动的术语,其含义取决于手头的模型;更一般地说,它是衡量模型如何学习(稳定性)而不是学习什么(准确性)。控制准确性我们希望选择一个更“稳定”的模型——可以应用于大多数用户,能够一致地识别相关的变量集,并保持变量相关性的平凡性。所有这些都是为了说明——在两个非常精确的模型之间,我们希望选择一个可以更普遍地应用的模型,以及一个即使我们稍微改变事情也不会失去控制的模型。例如,如果证券价格变得高度不稳定,复杂、准确且实用的高频交易算法可能会失控。以至于纳斯达克不得不实施熔断机制,以便在出现这种情况时停止交易,事实也的确如此。最近一次爆炸发生在 2020 年 3 月 9 日。虽然有许多因素导致断路器被激活,但事实仍然是,在某个阈值之后,不能信任模型来做出准确或稳定的决策。因此,虽然数据科学有意识地将稳定性与准确性分开,但我想知道计量经济学是否应该积极地建立模型,同时牢记准确性 稳定性。如果有,我们应该如何定义稳定?

一旦我们开始定义稳定性,就很容易让它变得越来越微妙。许多机器学习模型利用 k-fold 或 n-fold 交叉验证来测量稳定性或选择的变量,我们可以很容易地将这些方法用于计量经济学建模。然而,由于计量经济学主要在“频率”领域工作,即用于训练模型的数据具有时间关系,因此我们应该更仔细地研究如何在这个新的变量空间中定义稳定性。

*首先,与 k/n 重交叉验证不同,我们不能在不丢失时间关系的情况下随机地对数据进行子集划分。我们用于预测的模型通常依赖于这样的假设,即时间 t 的值可能与其滞后 *{t-1,t-2,…,t-n}有因果关系。此外,根据我们决定如何对这些时间关系进行建模,我们使用不同的时间序列结构表示——ARIMAX、指数状态空间、傅立叶基表示、径向基表示等。这两种方法利用时间关系的方式略有不同。这意味着学习机制(稳定性)不能用一种通用的交叉验证技术来衡量。

出于本文的目的,我只想关注一种时态表示——AR 结构,以及 R 中函数*“auto . ARIMA”*所隐含的算法有多稳定。该功能是 预测 包的一部分。我希望读者能就如何最好地定义我提到的其他结构的稳定性提出想法。

ARMA 过程可以表示如下:

可以使用信息标准(AIC)校准 Xε的每一个滞后的系数,这本身就有助于减少误差(提高精度)。然而,AIC 没有关于结构稳定性的信息,只评估了一个模型在保留其训练数据信息方面的效率。

如果 AIC 是系数值和滞后的选择标准,那么我们也许可以通过以下方式来衡量 ARMA 模型的稳定性:

  1. 分配给每个滞后的系数值 —一个准确稳定的模型应该能够计算出正确的系数,然后在每次我们添加另一个数据点进行训练时都这样做。
  2. 当我们向数据添加随机扰动时,模型的反应 —如果我们试图通过添加与我们的训练数据不来自同一人群的数据来欺骗模型,一个稳定的模型,理想情况下,不会那么容易被欺骗*,即使以准确性为代价**;相反,它不应该试图预测冲击。*

*稳定性测量的另一个重要特征是所有的测量必须发生在相同样本数据的子集上。考虑到数据的时间特性,我们必须稍微改变这些技术,以便为我们的模型保留时间信息。一种解决方案是使用**滚动验证。*我们经常使用它作为衡量样本外预测准确性的方法,但我们在这里可以利用相同的机制;让机器在工作时测量不同的指标。

[ 要快速了解滚动验证,读者可以访问 Rob J. Hyndman 的博客 ]

我们可以很容易地模拟 AR 过程。我们将先验地知道滞后系数是什么。接下来,我们可以使用滚动验证方法来训练 auto.arima 算法,并查看 auto.arima 算法获得正确滞后和系数的速度和频率。请参见以下 AR 流程的可视化展示:

上述过程有 4 个滞后,系数向量分别为= {0.7,-0.2,0.5,-0.8},长度为 1000 个周期。滚动验证/训练从 n=20 开始,下面是每次迭代计算的系数的表示。至少对于这个模拟数据集,auto.arima 算法需要大约 400 个数据点来逼近数值稳定的系数的可接受的精确解(图 1)。但是请注意,即使对于模型非常不稳定的前 200 个数据点,校准模型的样本外精度也与后面的数据点相当(图 2);唯一的区别是,后来校准的模型对算法训练增加的每个新数据点更加稳健

**

为了进一步强调准确性与稳定性之间的关系,时间序列预测中的另一个常见问题是当训练数据包含随机不连续性时,这些不连续性不一定遵循与时间序列本身相同的基本动态。通常,如果我们能够准确识别这些冲击,我们会从训练数据中完全删除数据点,或者选择平滑它们以减少模型的偏差。然而,如果我们能够测量我们的方法/算法的稳健性,我们就可以在建模之前,对我们应该如何修改原始数据或设计它做出更明智的选择。这并不是说特征工程是一种无效的建模方法,但是我们必须停下来思考它是帮助我们改进模型还是仅仅使它复杂化。在我看来,吝啬不应该被用来换取复杂的声音模型,这些模型对我们从它们那里获得的洞察力提供很少或没有改善。

为了测试 auto.arima 的稳定性,我们希望通过添加随机冲击来轻微扰动数据,这些随机冲击与我们希望预测的数据来自不同的分布。我们知道,对于这个数据集,auto.arima 在大约 400 个数据点时达到稳定;我们可以对同一时间序列的扰动版本运行滚动验证,并查看它与未受扰动的同类序列相比如何。相同的时间序列见下文,但随机间断点分散在:

下面是 auto.arima 的准确性与之前未受干扰的数据版本的比较(黄线表示添加了不连续点):

很容易看出,当我们随机冲击训练数据时,预测算法实际上变得相对不太准确。人们容易忘记的是,虽然稳定性可能与准确性无关,但它会直接影响准确性。请参见下面为这个新数据集计算的系数:

**

图例显示了真实系数值,我们知道在未受干扰的数据中,模型会准确地收敛到该值。然而,当相同的数据受到随机冲击时,它完全将系数值从红线中分离出来。对数据的一些不相关的冲击完全改变了估计的模型表示。一个稳定的算法不应该被一个未经证实的数据冲击影响太多。

有趣的是,该算法是不稳定的*,因此不准确的,不仅在计算的系数方面——AR1、AR2、AR3 和 AR4 值与原始数据集非常不同,而且在计算的系数数量方面——原始数据没有 MA1 或 MA2 项,该算法错误地为我们扰动的数据集分配了非零值。如果 auto.arima 选择了错误的模型表示(系数),那么通过构造,与具有未受干扰数据的模型相比,它使用这些系数生成的预测将与实际值相差更远。*

我想让读者停下来想一想上面的结果——当你完成一个预测练习,软件给出结果时,你认为你的算法向你显示的是哪个值——黑线还是红线*?因为我们只看到最终值,而不是它如何收敛到最终值,稳定性的细微差别就消失了。您的答案将决定您需要采取的特征工程步骤,以确保您负责任地模拟了计量经济学过程。***

在这种情况下,auto.arima 牺牲了稳定性和准确性。不幸的是,根据我的经验,将稳定性从准确性中分离出来并不是传统计量经济学工作流程的一部分。如果我们将准确性和稳定性作为原始数据和经过处理的数据的两个不同指标来衡量,那么我们就能够就是否应该设计原始数据做出更加明智的决定。有许多方法可以减少单个测量的偏差,甚至有更多的算法可供选择来进行预测;我们必须有一个一致而严格的框架来帮助我们选择每一个。

请记住,本文只是触及了计量经济学稳定性的表面,但我们已经能够为许多建模决策提供充分而实用的支持,这些决策通常基于“有根据的”猜测或更糟的预感。

我希望读者受到启发,想出更新的方法来探索这个话题!

韦丹特·贝迪是万事达卡的分析师,在不结盟运动投资组合开发团队工作。Vedant 拥有 NYU 大学的数学和经济学学士学位(2019 年优等生),对数据科学、计量经济学及其在金融中的许多应用有浓厚的兴趣。

Vedant 还是美国历史最悠久的学术荣誉团体 Phi Beta Kappa(纽约分会)的成员。

衡量机器学习产品的成功

原文:https://towardsdatascience.com/measuring-success-ef3aff9c28e4?source=collection_archive---------42-----------------------

业务绩效与模型绩效

失败是学习过程的一部分。不幸的是,它经常成为机器学习开发过程的一部分。由于产品度量和模型度量之间的不一致,ML 项目从概念上就注定失败。今天,有许多技术熟练的个人可以创建高度精确的模型,而糟糕的建模能力并不是一个常见的陷阱。相反,有一种趋势是开发对产品无用的精确模型,从而无法满足业务目标。

图片由艾萨克·史密斯在 Unsplash 上拍摄

定义和量化成功

在定义成功时,考虑业务绩效车型绩效之间的差异是很重要的。最简单的说法是,业务绩效是许多变量的函数,而不仅仅是模型绩效。模式表现差,经营业绩就会不足,但模式表现好并不能保证经营业绩好!

图片由自由使用声音在 Unsplash 上

经营成绩

要评估业务绩效,必须从产品或功能目标开始。例如,增加电子商务网站的收入。一旦确定了这一点,就应该分配一个产品指标来评估是否成功。这个度量需要从任何模型度量中分离出来,只量化产品的成功。产品指标可能会有所不同,某个功能吸引的用户数量或推荐的点击率(CTR)等指标都代表有效的示例。

在一天结束时(和财政期间),产品指标才是最重要的。它们代表了产品的目标。任何其他指标都被认为是优化产品指标的工具。通常项目的目标仅仅是改进一个单一的产品度量,但是它们的影响经常是相对于许多度量来量化的。其中一些包括护栏指标,它代表不会低于某一点的指标。例如,一个 ML 项目的目标可以是增加用户数量等产品指标,同时保持平均用户会话等其他指标的稳定性。

模型性能

衡量 ML 方法的有效性需要跟踪模型性能。在利用 ML 部署产品之前,不可能量化产品指标。在 ML 产品的构建过程中,离线指标模型指标对于定义成功非常有用。要认为一个离线度量是高质量的,应该在不向用户公开 ML 模型的情况下对其进行评估。此外,它应该与产品指标和业务目标高度相关。

假设您正在开发一个特性,在向在线零售店输入查询时向用户提供建议。该特性的成功可以用 CTR(产品指标)来衡量。为了创建这些建议,可以建立一个模型来预测用户将要键入的单词并显示这些预测。通过测量单词级别的准确性(计算模型预测正确的下一组单词的频率),可以定义模型的性能。在这种情况下,模型需要具有极高的准确性来提高产品的 CTR,因为单词预测中的一个错误就会使建议变得毫无用处。

一种(更好的)方法是训练一个模型,该模型可以接受用户输入,并对目录中的类别进行分类,建议最有可能预测的类别。在这里,一个目录中的类别数量远远少于英语中的所有单词,这使得这是一个更容易优化的模型度量。此外,该模型只需正确预测一个类别即可产生点击。

图片由格伦·凯莉在 Unsplash 拍摄

结束语

业务绩效似乎经常迷失在 ML 模型绩效的宣传中,但是认识到哪些指标需要优化总是很重要的。

除了业务绩效和模型绩效,还需要其他衡量成功的指标。考虑因素包括模型的新鲜度和速度。随着模型的老化和数据分布的变化,模型的新鲜度是一个重要的考虑因素。对于任何软件来说,速度总是一个重要的考虑因素。如果需要几秒钟来处理数据和生成预测,自动驾驶汽车就永远无法实现。

关于这个话题的更多内容,我强烈推荐 Emmanuel Ameisen 的构建机器学习驱动的应用。它涵盖了设计、构建和部署由机器学习驱动的应用程序所需的技能。

在 Power BI 中衡量工作-生活平衡

原文:https://towardsdatascience.com/measuring-work-life-balance-in-power-bi-a7e82ed5204b?source=collection_archive---------46-----------------------

“我花在工作上的时间占我时间的百分比是多少?”。几天前我向自己提出了这个问题。这篇文章向您展示了我是如何处理这个问题以及如何解决这个挑战的。

由奥斯丁·尼尔在 Unsplash 上拍摄的照片

介绍

前几天,我问自己:我工作的时间占多少百分比?

作为一个 Power BI 爱好者,我想用 Power BI 来回答这个问题。

这篇文章就像一个日志,在其中你可以看到我是如何处理这个问题的,以及我是如何解决这个问题的。

对数据的质疑

首先,问题究竟是什么?

每天,我想知道工作时间占一天 24 小时的百分比。

每月,我想知道与每个月所有日子的小时数相关的平均百分比。另外,我想计算工作时间总和与每月工作时间总和的关系。

我想知道在这两个级别上我的总工作时间与计费时间的比率。

因为我只有实际年份的数据,所以我不必在年份级别计算任何东西。

我有两组数据:

  • 按客户列出的所有计费小时列表
  • 不分收费与否的工时列表

准备数据模型

我将这两个数据集加载到 Power BI 中,并添加了一个日期表。

因为我需要对时间数据进行分析,所以我需要一个时间表。

我创建了一个具有以下属性的时间表:

  • Time_Key
    格式为 hhmmss 的键列,没有前导零
  • 时间
    时间栏
  • 小时
    当前时间的小时
  • 分钟
    当前时间的分钟
  • 军事时间
    hhmm 格式的时间,带前导零
  • 以分钟为小数的小时
  • Hour_Frag
    每分钟的小时分数
  • Day_Frag
    一天中每分钟的分数

以下是时间表的摘录:

图 1 —我的时间表摘录

我如何计算这些数字的精确公式超出了本文的范围。
想了解详情请留言,我会就这个话题写个描述。

调整数据模型

我必须调整我的数据模型:

图 2 —我的数据模型

查看表 Time_Min 和 Rapport 2021(=报告 2021)之间的关系。

因为我想使用 Time_Min 表作为维度和事实表,所以我必须将关系设置为双向过滤。

我怀疑我必须对其他表做同样的事情,因为我想合并表“Rapport 2021”和“Soll-Ist”(=目标与有效小时数)中的数据。

**注意:**表名和列名都是德语,因为我的数据源提供的数据使用了德语。我会尽我所能把一切翻译成英语

简单平均数

第一步是看我的数据简单平均的结果是什么。

正如您在下面的屏幕截图中看到的,结果看起来很不正确:

图 3 —第一个平均值结果

平均值将计算所有行的平均值,而不考虑粒度。平均值似乎产生了错误的结果,但它为我指明了接下来要遵循的方法。

计算几天的平均值是没有意义的。我需要得到天数,然后除以小时数。

但是,有一天,平均值是正确的。

不同计算的处理

我需要评估计算级别(月或日),并对这两个级别执行不同的计算。

我将通过以下中间措施来实现:

Calendar Check =
SWITCH(TRUE()
    , HASONEVALUE(‘Date’[DateKey]), “Date”
    , HASONEVALUE(‘Date’[MonthKey]), “Month”
    , “Others”
)

MonthKey 列标识每年的每个月。例如,2021 年 6 月是 202106

现在,我可以在所有其他测量中使用这个测量的结果,并决定哪个计算是正确的。

计算部分生命时间

首先,我想在每月的水平上计算我的工作时间与我一生的比率。

例如:

  1. 一天有 24 小时
  2. 我一天工作 8 小时
  3. 部分工作时间是 8 / 24 = 33.3%

当我处于每月级别时,我不能使用 Time_Min 表,因为它最多只能显示 24 小时。但是,我一个月工作超过 24 小时。

因此,我需要执行以下计算:

Work Part per Month =VAR SumOfHours = SUM(‘Rapport 2021’[Wertschöpfende Menge])VAR HoursPerMonth = COUNTROWS(‘Date’) * 24VAR WorkPartPerMonth = DIVIDE(SumOfHours , HoursPerMonth)RETURN
    WorkPartPerMonth

SumOfHours 变量包含计费小时数的总和(= wertschpfende Menge)。

变量 HoursPerMonth 计算实际周期中的天数,并将其乘以 24(小时)。

为了只考虑工作日,不考虑周末,我每月用这个时间:

CALCULATE(COUNTROWS(‘Date’)
    ,’Date’[IsWorkday] = TRUE()) * 24

计算结果通过 SumOfHours 除以 HoursPerMonth 存储在变量 WorkPartPerMonth 中。

第二,我想在每日级别计算相同的比率。

我的数据模型允许我汇总实际工作时间和 Day_Frag 数量,因为两者都是一天的时间,不能超过 24 小时。

现在我可以计算 Day_Frag 的和来得到正确的结果。

当过滤器上下文为每天选择 Time_Min 表中的匹配行时,我可以计算 Day_Frag 的总和来获得正确的结果。

衡量标准如下:

Part of Lifetime =
SWITCH(TRUE()
    ,[Calendar Check] = “Date”
        ,SUM(‘Time_Min’[Day_Frag])
    ,[Calendar Check] = “Month”
        ,[Work Part per Month]
    ,BLANK()
    )

衡量标准[每月工作部分]有效,其他任何级别也有效。但是,由于我的数据只涵盖了 2021 年,我只考虑了每日和每月的水平。

计算工作时间和计费时间之间的关系

现在我必须解决最后一个问题:我想知道我的总工作时间与这两个级别的计费时间的比率。

Soll-list 表包含一个每天小时数的列表,分为两列:

  1. 这是我每天必须工作的小时数(目标工作时间)
  2. Ist:这是我一天工作的小时数(有效工作时间)

我开始思考如何计算这个量。

我首先想到的是使用 LOOKUPVALUE()根据 Rapport 2021 表中的日期从 Soll-list 表中获取值。

正如我在另一篇文章中解释的那样,不要从优化代码开始。可能不是个好主意;我喜欢简单优雅的解决方案。我不认为 LOOKUPVALUE 是一个简单的解决方案。

第二种方法是将关系设置为双向过滤。
我不想走这条路,因为这种方法会有不良副作用,

最后,我决定在两个表之间再添加一个关系:

图 Rapport 2021 和 Soll-list 之间的新关系

然后,我创建了一个新的方法来划分这两个数字:

Billable time vs Worktime = DIVIDE(
        SUM(‘Rapport 2021’[Wertschöpfende Menge])
        , SUM(‘Soll-Ist’[Ist Stunden])
    )

起初,我很惊讶它居然有效:

图 5 —计费时间与工作时间

然后我意识到两个表上的过滤上下文都是通过日期表设置的。所以我不需要再做什么来得到正确的结果。

结论

我在报告开发期间写了这篇文章。

我期望在这个例子中有更复杂的代码,因为时间计算可能会很棘手。
幸好没有发生这种情况。

我认为这是由于良好的数据模型。

花时间考虑一个好的数据模型来简化 DAX 代码总是有意义的。

Time_Min 表帮助我计算了结果,尽管我没有使用这个表中的所有列。

但是我得到的最大好处是清楚地定义了需求和我想要回答的问题。
有了明确的要求,我集中精力做我需要做的事情,而不是我喜欢做的事情。

还是那句话,明确需求的时间是值得的。

结果如下:

图 6 —计算结果

如你所见,我 17 %到 31 %的时间花在工作上,75%的工作时间是收费的,考虑到我在 1 月和 4 月休假,而且我在 4 月进行了为期 3 天的培训,这已经不错了。

不幸的是,我在 7 月初失去了一个客户。尽管如此,我仍然有 76.4%的计费工作时间。

我想我可以为这些结果感到高兴。

Ian Stauffer 在 Unsplash 上拍摄的照片

用不忠和敏感来衡量 XAI 方法

原文:https://towardsdatascience.com/measuring-xai-methods-with-infidelity-and-sensitivity-a0625770f17b?source=collection_archive---------31-----------------------

模型可解释性,理解 ML

XAI 方法的不忠和敏感性测量背后的直觉。

可解释的人工智能( XAI )一年比一年受欢迎。每年我们都有更多的方法来解释我们的模型是如何工作的。大多数方法的唯一问题是比较。

我们有两种方法:

  • 定量—基于某个数值
  • 定性—基于意见/调查

让我告诉你问题在哪里:

图 1:相同图像的引导反向传播 vs 集成梯度解释,图像来源: 食用野生植物数据集

这里我们用两种方法对同一个预测(由 ResNet18 模型完成)进行了两种解释,导向反向传播 ( GPB )和综合梯度 ( IG )。正如你所看到的,预测的类是不正确的,但是为什么它应该是 elderberry 的两种解释是完全不同的。作为一个对植物一无所知的人,我会说 GPB 的解释更好,也许是这样的。我们可以问更多的人,最后收集所有的答案,决定哪一个更好。在此之前,让我们跳到一个不同的例子。

图 2:引导反向传播 vs GradCAM 对同一图像的解释,图像来源: 食用野生植物数据集

在这种情况下,我们看到一个不同的方法,叫做 GradCAM 。这个例子没有上一个例子那么明显。GradCAM 的版本噪点更少(更专注于花卉),但没有专家,我们无法确定哪一个更好。我们仍然可以进行民意测验,得到一些答案,但是这个答案是有效的吗?当然,我们可以测试 100 张甚至 1000 张图像,并想出一个关于哪种方法更好的好主意,但当我们的数据集有 10k 张图像时,这不会扩大规模,我们需要使用 5 种不同的模型来比较 5 种不同的方法。

这是定性方法的一个问题。我们可以比较一些例子,但如果我们想在比较投票中增加另一种方法,这不会给我们提供任何有意义的指标。这里是当定量方法来临的时候。定量方法的想法是有某种可复制的度量标准,允许我们比较数值。在关于解释的(不)保真度和敏感度、叶志宽等人的论文中提出了两个度量(称为不忠敏感度),这应该是对那种解释(显著图)的客观评价。

灵敏度

让我们从第二个开始,因为它更容易解释。顾名思义,这个测量告诉我们这个方法有多“灵敏”。

其中:

  • φ是一个解释函数
  • f 是一个黑盒模型
  • x 是一个输入
  • r 叫做输入邻域半径

灵敏度定义为输入的微小扰动引起的解释中的变化。为了计算 SENS 值,我们必须进行这些小扰动,并检查我们的属性相对于未改变输入的原始属性是如何变化的。

原文还将那些、【小扰动】、称为、【无关紧要的扰动】、,这一措辞很重要,因为在衡量不忠时,我们也在实施扰动,但这些扰动是、【重大扰动】、。知道其中区别是很好的。

你可能会问“那个半径是怎么回事?”(r 参数)。基本上是均匀分布U {-r,r} 的上下界。现在,当我们知道这些参数时,下面是一些例子:

图 3:原始输入和扰动输入的属性。这不是一个“无关紧要的干扰”,而是一个直观显示差异的干扰,来源:凯末尔·艾尔丹姆

图三。是在对 3x3x1 图像(灰度)进行操作的简单模型的帮助下做出的。我们从原始的输入中减去 0.1 噪声,并根据新的屏蔽输入计算属性。我们可以看到属性发生了变化( (2,1)屏蔽输入属性网格上的位置)。本例产生的敏感度值约为 0.01 (稍大一些,因为其他属性也发生了变化,但这种变化非常小)。可能会出现灵敏度值为 0 的情况。

图 4:零灵敏度示例,来源:凯末尔艾尔丹姆

这个例子是假的(在下面的应用程序中检查),但我这样做只是为了告诉你什么时候属性可能等于 0。在这种情况下(图 4 ),我们也应用了一些扰动(在(1,1)位置上为-0.1 ),但是新屏蔽输入的属性保持不变。这是灵敏度值为 0 的唯一情况。IRL 灵敏度值不是基于一个扰动计算,而是基于 10 个或更多个扰动。

无信仰

不忠比敏感更复杂一点。结果是获得了比属性更多的值(还有分数和噪声本身)。不忠的等式看起来像:

其中:

  • φ是一个解释函数
  • f 是一个黑盒模型
  • x 是一个输入
  • 是一个显著的摄

这次我们要处理的是一个重大扰动。这篇文章讨论了很多扰动,但是最后,我们有两个主要的选择:

  • 噪声基线——仅使用高斯随机向量
  • 正方形移除 —移除像素的随机子集

从实现和使用的角度来看,第一个选项更容易。我们只需要使用高斯分布生成一个与输入形状相同的向量(确切地说,它是一个张量)。所有的 ML 库都已经内置了这个方法。好的,但是这个计算到底是什么样子的呢?

我们可以把不忠等式分成两部分甚至三部分:

@1

我们需要使用原始输入来计算属性,并将其乘以转置噪声向量。这可能看起来很简单(确实如此),但是有一件重要的事情需要注意。因为我们使用我们的原始噪声向量,如果它有一些 0 值,那么放置在这些值的转置位置上的属性值将被清除(乘以 0)。

图 5:只有一个值不等于 0 的噪声,来源:凯末尔艾尔丹姆

在类似于图 5 中的噪声的情况下,只有来自(2,2)(转置矩阵在这种情况下看起来完全相同)位置的属性值被用于最终得分计算。

@2

这部分甚至更简单,因为我们只是得到我们的黑盒模型输出,并从扰动前的值中减去扰动后的值。

@3

这并不完全是一个独立的部分,但是我们不希望有任何负数,所以我们只是平方结果。

演示

不幸的是,我不能在 Medium 文章中包含应用程序。原始演示可从 https://erdem . pl/2021/03/measuring-xai-methods-with-fifthy-and-sensitivity # demo 获得

现在你知道计算是怎样的了,我准备了一个简单的展示应用程序,你可以玩不同的噪音,并检查它如何影响不忠或/和敏感性。记住,这个例子中的噪声值非常大,通常甚至显著扰动也比这个小得多。

演示截图,该应用可在 https://erdem.pl 获得,来源:凯末尔艾尔丹姆

此应用程序使用一个模型,该模型采用 3x3x1 输入图像并预测分配给两个类别之一。这只是一个简单的模型,你不应该担心它的有用性(它没有任何用处)。我创建它只是为了展示选定的噪音对不忠和敏感的影响。用更大的网格来显示同样的效果会让人不舒服。

结论

我已经介绍了两种方法,可以用来定量测量和比较不同的 XAI 方法。我目前正在写一篇论文,讨论这些方法有多有用,以及我们是否应该用它们来比较不同的方法。

如果您对使用这些方法感兴趣,您可以尝试一下 Captum 库以及它们对这些方法的实现:

  • 对 Captum 的不忠
  • Captum 中的灵敏度

参考资料:

  • 深度网络的公理化归属,Mukund Sundararajan 等人 2017 arxiv 1703.01365
  • 力求简单:全卷积网络,约斯特·托拜厄斯·斯普林根贝格等人,2015 年 arxiv 1412.6806
  • 关于解释的保真度和灵敏度,叶志宽等人 2019 arxiv 1901.09392
  • Grad-CAM:通过基于梯度的定位来自深度网络的视觉解释,R. R. Selvaraju 等人 2016 arxiv 1610.02391

最初发布于https://erdem . pl

医疗人工智能:为什么临床医生向左滑动

原文:https://towardsdatascience.com/medical-ai-why-clinicians-swipe-left-f3bbaa3a660e?source=collection_archive---------12-----------------------

行业笔记

你的医疗 AI 会被临床医生拒绝的最常见原因以及如何克服

图片由艾文·菲利克斯提供。

所以,你已经找到了工业使用的成功训练算法,现在你想训练一种算法来帮助医生帮助病人。为医学应用开发算法是一项具有挑战性的工作,但也是一项值得做的工作;人工智能有可能彻底改变医学实践,就像自动化已经彻底改变了几乎所有其他行业一样。公共医疗数据集随时可用。Kaggle 和其他网站定期举办医疗应用竞赛。然而,很少有算法被 FDA 批准用于临床。

关于为什么会这样的全面讨论超出了本文的范围(关于这个话题的讨论在网上比比皆是)。然而,本文将帮助您克服医学算法被临床医生拒绝的一个最常见的原因。数据科学家经常使用数据集开发医疗机器学习模型和神经网络,这些数据集包含与现实世界中看到的非常不同的医疗状况分布。这种差异导致在平衡的、精选的数据集上表现良好的模型在临床上变得几乎无用。为了解决这种差异,研究人员应该使用两个具有不同类别分布的数据集来验证医学应用的模型:一个数据集富含稀有条件,有助于生成统计能力;和第二数据集,其条件分布等于该模型在临床中将面对的分布。

一个例证

想想美国最普遍的癌症:皮肤癌(美国癌症协会 2021)。每年,美国被诊断患有皮肤癌的人数比所有其他癌症的总和还要多。压倒性地,最威胁生命的皮肤癌是侵袭性黑色素瘤,因为黑色素瘤经常在淋巴结、肝脏和大脑等其他器官中不受控制地转移和生长(皮肤癌基金会 2021)。被称为组织病理学专家的组织专家将大多数皮肤生长诊断为良性(想想痣、雀斑、皮肤标签)。在典型的诊所中,不到 1%的皮肤病被诊断为侵袭性黑色素瘤(Ianni 等人,2020)。诊断皮肤损伤的标准实践是通过活组织检查去除生长物,并使用显微镜观察生长物的组织。2015 年,美国进行了超过 500 万次皮肤活检。大多数皮肤病理学或皮肤病理学诊所几乎跟不上大量的活检。单个活检和患者接受诊断之间的时间可以从几天延长到几周,这意味着患者可能被迫等待开始关键的治疗。由于多种原因,病理学目前正在经历一场变革,从活检的人工显微镜分析转变为对这些活检的高分辨率扫描图像的审查。在这些扫描上训练的计算机视觉模型具有巨大的潜力,可以通过识别可能的侵袭性样本并标记它们以便立即诊断来加快侵袭性疾病的诊断。

让我们想象一个数据科学家想要训练一个模型来检测皮肤活检扫描中的侵袭性黑色素瘤。最简单的方法是收集大约一半黑色素瘤,一半非黑色素瘤的数据集,同时确保收集不同的非黑色素瘤扫描集。让我们想象一下,该模型被训练为在保留的测试集上最大化 F1 分数,并且训练后的模型达到 90%的准确率,同时具有平衡的错误率——这绝非易事。研究人员将这些结果报告给临床合作伙伴,他们一起进行一项初步的前瞻性研究,这意味着他们对未来 6 个月内访问诊所的所有患者进行模型性能测量,以确定模型的效用。

问题是

令研究人员震惊和沮丧的是,临床医生在模型中测量的错误率与开发期间测量的错误率相差甚远——检测到的假阳性比真阳性检测多得多。通过模型精度(也称为阳性预测值)来测量模型的阳性检测中为真阳性检测(而不是假阳性检测)的比例。在开发过程中,研究人员测量的精确度接近 90%,但在临床研究中,临床医生发现精确度接近 10%。这意味着有 80%的惊人差距。开发时的表现和学习时的表现怎么会有这么大的差距?

显而易见的答案是,开发过程中数据集的类别分布与模型在临床中面临的类别分布不同。在临床环境中,医生按时间顺序诊断疾病——类别分布遵循一般人群中的疾病患病率。普通人群中良性疾病的患者比罕见的危及生命的疾病多得多——谢天谢地!收集了某个诊所在一段时间内看到的所有病例(例如一年内诊所的所有病例)的数据集具有长尾:大多数观察结果是良性的,只有少数会包含最罕见的或最威胁生命的情况。

在本文中,我们将遵循按时间顺序排列的临床类别分布的数据集称为“顺序数据集”在开发过程中,研究人员使用了一个丰富的数据集,其中包含的黑色素瘤比医生在临床工作量中看到的病例还要多。如果检测罕见疾病的模型在丰富、平衡的测试数据集上得到验证,顺序工作流程中的错误率将不同于模型开发过程中测量的错误率,因为在临床中,模型在对非黑色素瘤样本进行分类时比黑色素瘤样本进行了更多的“尝试”。让我们更详细地讨论这两种类型的数据集。

问题的交互式说明

在上面的例子中,开发数据集的属性和临床条件分布的属性导致了开发和临床指标之间的巨大差异。为了说明数据集的属性如何影响开发和临床中的模型性能,我们在 Google Colab 上包含了一个模拟模型性能的交互式图形。

五个参数控制模拟,并创建三个图。前两个参数是阳性类别在临床和发展中的频率。第三个参数是开发数据集中案例的数量。最后两个参数是开发期间在测试集上测量的假阳性率和假阴性率。控制每个图的参数显示在图的上方。我们假设建模测试集是整个开发数据集的 15%。

第一个输出图通过显示由测试集中真阳性(α)和假阴性(β)检测的数量参数化的β分布的平均值(天蓝色)和 95%置信区间(品红色)来指示模型真阳性率的不确定性。贝塔分布通过测量观察到的成功和失败的分布从基础事实成功率中产生的可能性来模拟“成功率”中的不确定性(有关更多信息,请参见贝塔分布—直觉、示例和推导)。有了足够的观察,这个分布的平均值将收敛到模型真阳性率(1 减去假阴性率),因为参数化率是最有可能产生开发期间测量的真阳性百分比的比率。在开发测试集中有足够的阳性类的观察值时,beta 分布的天蓝色平均值将为 1 减去假阴性率。然而,这个比率并不是唯一的潜在成功率,它可以产生在模型测试期间发现的成功和失败的分布的单一观察。如果该分布的平均值远离峰值,并且如果分布上的误差棒太大,则发育测量的真阳性率没有足够的统计能力,这意味着临床中的真阳性率很有可能与发育中测量的成功率非常不同。

第二和第三个图使用参数化的错误率显示了模型在 10,000 个临床病例上的性能。第一个图显示了每个分类的日志出现次数或计数,第二个图显示了使用这些计数的一些统计数据:准确度、召回率和精确度。

图 1:托管在 Google Colab 上的 Python Plotly Dash 中的一个交互式图形(此处显示截图),模拟了一个具有平衡或不平衡数据集的模型的开发和临床性能。图片作者。

丰富数据集的不足之处

在上面的例子中,模型的临床精度与开发精度如此不同,因为黑色素瘤在病理学家的案例中只占很小的比例。只要开发和临床中阳性类的百分比相差很远,丰富的数据集将提供较差的临床精度估计(图 2)。

图 2:当平衡数据集上的开发精度固定为 90%时,随着临床中阳性类的频率从 50%降低到 1%,临床精度下降。图片作者。

序列数据集的不足之处

条件的长尾分布是皮肤癌独有的吗?幸运的是,不是这样的。疾病流行率和疾病侵袭性通常以相关的长尾分布——罕见疾病通常是最威胁生命的。从进化的角度来看,侵略性的常见疾病是灭绝的前兆,以至于英语中有一个术语来形容常见的侵略性疾病:疫情。值得庆幸的是,大流行很少发生,尽管我们现在正经历着一场大流行。然而,这给研究人员留下了两个选择来训练检测威胁生命状况的模型:选项 A,在包含大量罕见状况观察结果的极其庞大的前瞻性数据集上训练;选项 B,在丰富的数据集上训练。选项 A 通常实际上是不可实现的,所以选项 B 仍然是不可避免的最佳选择。

在开发监督二进制分类模型以检测诊断皮肤组织图像中的黑色素瘤时,该模型对最威胁生命的皮肤病类型——侵袭性黑色素瘤表现良好是至关重要的。在包含最近 5000 名去皮肤诊所就诊的患者的活检的庞大数据集中,只有大约 50 个样本预计是侵袭性黑色素瘤。考虑到用于验证和测试的典型数据集划分,研究人员只剩下大约 35 个活检用于训练,7 个活检用于验证和测试。这引发的第一个问题是,很难用如此少的正面类的例子来训练模型——交叉验证只能进行模型开发到此为止。第二个更重要的问题是,这种罕见的、危及生命的条件下的测试实例如此之少,使得生成统计能力来证明这种罕见条件下的良好测试性能极其困难(图 3)。这就是为什么用于疾病检测建模的公共挑战数据集相对平衡的原因——这些数据集已经用来自罕见情况的示例进行了丰富,以提供与真实阳性示例一样多的真实阴性示例。

图 3:随着开发案例数量的增加,真阳性率的界限变得更紧,但是即使是一个非常大的顺序开发数据集,其中阳性类的百分比很小,也没有足够的阳性类的测试观察。图片作者。

解决方案

那么,有什么办法呢?如果研究人员只报告丰富测试数据集的统计数据,在临床上罕见情况下的表现将与报告的表现截然不同。如果研究人员仅使用序列数据集开发一个模型,该模型在测量罕见疾病亚型的模型性能时将在统计上不足。如何克服这些问题?

为临床使用开发的模型必须报告连续测试数据集的测试统计数据,以便研究人员测量的性能统计数据与临床产生的统计数据相匹配。如果无法获得连续数据集,则可以在普通人群中观察到的频率下,通过对丰富的测试数据集进行自举随机采样,模拟普通人群中的疾病患病率分布。

为了在检测罕见的、威胁生命的疾病时产生统计能力,还必须使用丰富的数据集来训练医学应用的模型。具有足够稀有类观察值的巨大序列数据集难以收集。稀有类的太少观察导致模型训练差,并且产生对测量的模型性能的信任的能力差。

当报告了强化方案和序贯方案中的表现时,医生可以放心地利用模型,因为医生已经充分了解了模型在罕见的侵袭性疾病中的表现,并且医生对模型在他们的日常临床工作流程中的表现有正确的预期。

通常一个模型可以被调整来减少一种类型的误差,代价是增加其他类型的误差。为了平衡临床中的错误率,具有比精确度更高的临床召回率的可调模型有时可以被调整为更低的假阳性率,同时保持临床精确度相等(通过加权损失函数,或模型预测后处理,以及其他技术)。在模型开发期间调整误差,使得误差在顺序数据集中平衡,这减轻了在开发期间测量的预期性能和在临床中部署的模型的实际性能之间的差异。临床医生需要知道如何期望模型在临床中执行,以及该模型如何在功能强大的数据集上执行,以决定该模型在他们的实践中是否可以接受。

最后的想法

算法有很多机会来改善医疗实践,并且每个建模情况都是独特的。在本文中,我们将重点放在开发涉众希望在临床中平衡误差的情况上。训练医学人工智能肯定还有其他有效的目标。例如,如果目标是最大化回忆,即使是低精度的模型也可能对特定的诊所有用。诊所可能会发现获得一组病例的价值,这些病例几乎肯定包含所有阳性病例,即使精确度较低,也可能允许他们有效地对病例进行优先排序。从另一个角度来看这个高召回、低精度的模型,该模型提供了一个非常纯粹的案例集合,这些案例实际上保证而不是包含正类。医学人工智能仍然是一个非常年轻的学科,所以医学人工智能最有用的应用可能还没有被发现。

然而,为了开发有用的算法,并能够向医生解释它们的性能,研究人员需要清楚地了解对训练模型有用的丰富数据集和可能的长尾条件的实际临床分布之间的差异。如果人工智能研究人员能够更深入地了解在临床上部署算法的挑战,这些知识将加速医疗实践的人工智能革命,让病人护理变得更好。

参考

美国癌症协会。2021.癌症事实&数字 2021 。https://www . cancer . org/content/dam/cancer-org/research/cancer-facts-and-statistics/annual-cancer-facts-and-figures/2021/cancer-facts-and-figures-2021 . pdf

Ianni、Julianna D .、Rajath E. Soans、Sivaramakrishnan Sankarapandian、Ramachandra Vikas Chamarthi、Devi Ayyagari、Thomas G. Olsen、Michael J. Bonham 等。"为现实世界量身定制:一个完整的载玻片图像分类系统,在模拟预期病理学工作量的未切割多站点数据上进行验证."科学报告 10 (3217)。https://doi.org/10.1038/s41598-020-59985-2.

皮肤癌基金会。2021.“黑色素瘤概述。”https://www . skin cancer . org/skin-cancer-information/melanoma/。

医疗成本预测

原文:https://towardsdatascience.com/medical-cost-prediction-4876e3449adf?source=collection_archive---------8-----------------------

R 中的数据科学

能否根据人们的数据准确预测医疗成本?

Bermix 工作室在 Unsplash 上拍摄的照片

**Table of Contents**· [Library](#374c)
· [Dataset](#572d)
· [Data Cleaning](#f78b)
· [Exploratory Data Analysis](#7ae7)
· [Metrics and Validation Strategy](#38e4)
· [Modeling](#49dd)
  ∘ [Linear Regression](#7a27)
  ∘ [Polynomial Regression](#47bb)
  ∘ [Model Evaluation](#9184)
· [Conclusion](#7a50)

健康保险公司只有在收取的费用超过其受益人的医疗费用时才能盈利。另一方面,即使某些疾病在某些人群中更为普遍,医疗成本也很难预测,因为大部分资金来自于患者的罕见疾病。本文的目的是根据人们的数据,包括年龄、体重指数、吸烟与否等,准确预测保险费用。此外,我们还将确定影响保险成本的最重要的变量是什么。这些估计可用于创建精算表,根据预期的治疗费用来设定更高或更低的年度保费价格。这是一个回归问题。

图书馆

我们将完全使用 r。首先,导入所有需要的库。

library(dplyr)          # data wrangling
library(ggplot2)        # graphing
library(caret)          # machine learning functions
library(MLmetrics)      # machine learning metrics
library(car)            # VIF calculation
library(lmtest)         # linear regression model testing
library(GGally)         # correlation plot

资料组

原始数据集可在 Kaggle 上获得。在本文中,它已经被随机分为训练和测试数据集。让我们读一读。

train <- read.csv("train.csv", stringsAsFactors=TRUE)
test <- read.csv("test.csv", stringsAsFactors=TRUE)
glimpse(train)#> Rows: 1,070
#> Columns: 7
#> $ age      <int> 37, 18, 23, 32, 58, 25, 36, 34, 53, 45, 20, 60, 58, 34, 60, 18, 38, 23, 39, 43, 26, 18, 19, 61, 49, 31, 55, 55, 23, ...
#> $ sex      <fct> male, male, female, male, female, female, male, female, male, male, male, female, female, male, male, female, male, ...
#> $ bmi      <dbl> 34.100, 34.430, 36.670, 35.200, 32.395, 26.790, 35.200, 33.250, 31.350, 24.310, 32.395, 28.700, 27.170, 35.815, 29.6...
#> $ children <int> 4, 0, 2, 2, 1, 2, 1, 1, 0, 5, 1, 1, 0, 0, 0, 0, 1, 3, 5, 1, 1, 0, 2, 1, 1, 1, 2, 1, 0, 1, 1, 0, 0, 2, 2, 0, 0, 0, 1,...
#> $ smoker   <fct> yes, no, yes, no, no, no, yes, no, no, no, no, no, no, no, no, no, no, no, no, yes, no, no, no, yes, no, no, no, no,...
#> $ region   <fct> southwest, southeast, northeast, southwest, northeast, northwest, southeast, northeast, southeast, southeast, northw...
#> $ charges  <dbl> 40182.246, 1137.470, 38511.628, 4670.640, 13019.161, 4189.113, 38709.176, 5594.846, 27346.042, 9788.866, 2362.229, 1...

如我们所见,我们得到了这些特征:

  1. age:第一受益人的年龄
  2. sex:保险签约人性别,女,男
  3. bmi:体重指数,提供相对于身高体重相对较高或较低的理解,使用身高体重比的体重客观指数( kg / m ),理想值为 18.5 至 24.9
  4. children:健康保险覆盖的儿童人数,受抚养人人数
  5. smoker:吸烟还是不吸烟
  6. region:受益人在美国的居住区域,东北、东南、西南、西北。
  7. charges:由健康保险支付的个人医疗费用

由于我们预测保险成本,charges将是我们的目标特性。

数据清理

首先,我们可以从上面看到,每个特性都已经有了正确的类型。让我们检查在训练数据集上是否有任何重复的观察。

train[duplicated(train), ]#>     age  sex   bmi children smoker    region  charges
#> 268  19 male 30.59        0     no northwest 1639.563

有一个。两个人不太可能有相同的年龄、性别、身体质量指数和来自同一地区的孩子,都不吸烟,并且有完全相同的医疗费用。我们可以删除这个重复的行。

train <- train %>% distinct()

太好了!现在,我们检查缺失值。

colSums(is.na(train))#>      age      sex      bmi children   smoker   region  charges 
#>        0        0        0        0        0        0        0colSums(is.na(test))#>      age      sex      bmi children   smoker   region  charges 
#>        0        0        0        0        0        0        0

厉害!没有缺失值。

探索性数据分析

这里有一些描述性统计数据。

summary(train)#>       age            sex           bmi           children    smoker          region       charges     
#>  Min.   :18.00   female:543   Min.   :15.96   Min.   :0.00   no :850   northeast:249   Min.   : 1122  
#>  1st Qu.:26.00   male  :526   1st Qu.:26.32   1st Qu.:0.00   yes:219   northwest:253   1st Qu.: 4747  
#>  Median :39.00                Median :30.40   Median :1.00             southeast:294   Median : 9447  
#>  Mean   :39.11                Mean   :30.73   Mean   :1.08             southwest:273   Mean   :13212  
#>  3rd Qu.:51.00                3rd Qu.:34.80   3rd Qu.:2.00                             3rd Qu.:16587  
#>  Max.   :64.00                Max.   :53.13   Max.   :5.00                             Max.   :63770

就分类特征而言,除了smoker之外,数据集的每个类别都有相似的人数。我们不吸烟的人比吸烟的人多,这是有道理的。charges本身价格相差很大,从 1000 美元左右到 64000 美元。

让我们看看charges的分布。

ggplot(data = train, aes(x = charges)) + 
  geom_density(alpha = 0.5) + 
  ggtitle("Distribution of Charges")

这种分布是右偏的,右边有一条长尾巴。在 40,000 美元左右有一个凸起,也许是另一个隐藏的分布。要挖掘这一点,我们需要分类特征。

for (col in c('sex', 'region', 'children', 'smoker')) {
  plot <- ggplot(data = train,
                 aes_string(x = col, y = 'charges', group = col, fill = col)) + 
            geom_boxplot(show.legend = FALSE) + 
            ggtitle(glue::glue("Boxplot of Medical Charges per {col}"))
  print(plot)
}

就给出的charges而言,sexregion在每个类别中没有显著差异。我们可以看到随着children的增加charges有增加的趋势。最后,smoker似乎与健康保险给予的charges有显著差异。

让我们再次画出charges的分布,现在将它们归类到smoker中。

ggplot(data = train, aes(x = charges, fill = smoker)) + 
  geom_density(alpha = 0.5) + 
  ggtitle("Distribution of Charges per Smoking Category")

看看我们找到了什么!吸烟者肯定比不吸烟者有更多的charges

下面我们根据smoker因子,通过agebmichildren来分析一下医疗charges

for (feat in c('age', 'bmi', 'children')) {
  plot <- ggplot(data = train, aes_string(x = feat, y = 'charges', group = 'smoker', fill = 'smoker', col = 'smoker')) + 
    geom_jitter() + 
    geom_smooth(method = 'lm') +
    ggtitle(glue::glue("Charges vs {feat}"))  
  print(plot)
}

smoker似乎对医疗的影响最大charges,尽管chargesagebmichildren一起成长。此外,孩子越多的人吸烟越少。

最后,我们有如下特征之间的相关性。

ggcorr(train %>% mutate_if(is.factor, as.numeric), label = TRUE)

正如我们所看到的,除了smokercharges之外,特征之间几乎没有相关性。

度量和验证策略

我们将使用平均绝对误差(MAE)、均方根误差(RMSE)和均方根对数误差(RMSLE)作为衡量标准。这三个指标的使用取决于业务角度。为了理解我们的意思,考虑一下charges的一次观察的真实价值是 10,000 美元。假设模型预测与真实值完全相同,除了这个模型预测为 x 的特定观察值。我们将把 x 从$1,000 变到$19,000,并查看产生的错误。

true <- 10000
pred <- seq(from = 1000, to = 19000, length.out = 181)
x <- pred - true
rmse <- (x ^ 2) ^ 0.5
rmsle <- ((log(pred) - log(true)) ^ 2) ^ 0.5

par(mfrow = c(1, 2))
plot(x = x, y = rmse, 
     type = "l", 
     main = "Root Mean Squared Error", 
     xlab = "Error (prediction - actual)", ylab = "RMSE")
plot(x = x, y = rmsle, 
     type = "l", 
     main = "Root Mean Squared Logarithmic Error", 
     xlab = "Error (prediction - actual)", ylab = "RMSLE")

正如我们所看到的,RMSLE 对于实际变量的低估比高估招致了更大的惩罚。另外,RMSLE 度量只考虑预测值和实际值之间的相对误差,误差的大小并不显著。另一方面,如果误差范围增大,RMSE 值的大小也会增大。这意味着当低估不可取时,RMSLE 应该比 RMSE 更有用。

梅和 RMSE 对错误的方向漠不关心。由于误差在平均之前被平方,因此 RMSE 对大误差给予相对较高的权重。这意味着当大误差特别不理想时,RMSE 应该比 MAE 更有用。

了解了指标,我们只需将新数据test.csv应用于模型并查看指标得分,即可验证模型的性能。由于数据较少,我们不做 k 重交叉验证。

建模

我们将为这个问题建立和训练线性回归模型。首先,让我们使用模型中所有可用的特性。

线性回归

线性回归将通过使用反向消除的自动特征选择来实现。从使用所有特征开始,反向消除过程将迭代地丢弃一些特征并评估模型,直到找到一个具有最低阿凯克信息标准(AIC)的特征。给定一组数据模型,AIC 根据信息损失估计每个模型相对于其他模型的质量。更低的 AIC 意味着更好的模型。我们将使用step()函数以贪婪的方式应用反向消除

temp <- lm(charges ~ ., data = train)
step(temp)#> Start:  AIC=18667.88
#> charges ~ age + sex + bmi + children + smoker + region
#> 
#>            Df   Sum of Sq          RSS   AIC
#> - region    3   136779757  40475571916 18666
#> - sex       1       43071  40338835230 18666
#> <none>                     40338792158 18668
#> - children  1   294681919  40633474078 18674
#> - bmi       1  4133255306  44472047465 18770
#> - age       1 13330028969  53668821128 18971
#> - smoker    1 95616334451 135955126610 19965
#> 
#> Step:  AIC=18665.5
#> charges ~ age + sex + bmi + children + smoker
#> 
#>            Df   Sum of Sq          RSS   AIC
#> - sex       1      120916  40475692832 18664
#> <none>                     40475571916 18666
#> - children  1   288132465  40763704381 18671
#> - bmi       1  4147134564  44622706480 18768
#> - age       1 13500662196  53976234111 18971
#> - smoker    1 96273590176 136749162092 19965
#> 
#> Step:  AIC=18663.5
#> charges ~ age + bmi + children + smoker
#> 
#>            Df   Sum of Sq          RSS   AIC
#> <none>                     40475692832 18664
#> - children  1   288027795  40763720627 18669
#> - bmi       1  4151671360  44627364191 18766
#> - age       1 13508639838  53984332670 18969
#> - smoker    1 96513856276 136989549108 19965

#> 
#> Call:
#> lm(formula = charges ~ age + bmi + children + smoker, data = train)
#> 
#> Coefficients:
#> (Intercept)          age          bmi     children    smokeryes  
#>    -11905.1        254.9        320.6        429.9      23586.1

将最佳模型保存为lm_all,然后进行预测。之后,计算指标。

lm_all <- lm(formula = charges ~ age + bmi + children + smoker, data = train)
y_pred <- predict(lm_all, test)
mae <- MAE(y_pred, test$charges)
rmse <- RMSE(y_pred, test$charges)

要计算 RMSLE,请注意,我们必须让预测和目标变量的值为正,否则对数将导致 NaN。正如我们在下面检查的,我们在预测中确实有一个负值,观测值 149。克服这个问题的一个常见方法是在训练模型之前使用chargeslog(),然后使用exp()将预测转换回来。但是,由于我们之前已经使用了线性回归模型而没有取chargeslog(),我们将坚持使用这个模型。带有负面预测的观察值将从 RMSLE 计算中简单地丢弃。

y_pred[y_pred <=0]#>       149 
#> -87.05982rmsle <- RMSLE(y_pred[-149], test$charges[-149])
lin_reg <- cbind("MAE" = mae, "RMSE" = rmse, "RMSLE" = rmsle)
lin_reg#>           MAE     RMSE     RMSLE
#> [1,] 3941.464 5672.102 0.5455373

我们得到上述误差。对于梅和 RMSE,我们可以将它们解释为模型预测charges与真实值分别有 3941 美元和 5672 美元的平均差异。然而,我们不能再用charges来解释 RMSLE 了。请注意,RMSE 总是大于或等于梅。其实我们有这个不等式: MAE ≤ RMSE ≤ √n×MAE

多项式回归

我们可以通过特征工程来改进我们的模型,具体来说,就是通过创建新的特征来捕捉现有特征之间的交互。这被称为多项式回归。其思想是生成一个新的特征矩阵,该矩阵由次数小于或等于指定次数的特征的所有多项式组合组成。例如,如果输入样本是二维的并且具有[ ab ]的形式,则 2 次多项式特征是[1, abaabb 。我们将使用 2 度。

我们不希望在生成多项式组合的过程中包含charges,所以我们从traintest中取出charges,分别保存为y_trainy_test

y_train <- train$charges
y_test <- test$charges

从 EDA 我们知道sexregioncharges没有关联。我们可以放弃他们。此外,由于多项式组合对分类特征没有意义,我们将smoker突变为数字。

X_train <- train %>% 
  select(-c(charges, sex, region)) %>% 
  mutate(smoker = as.numeric(smoker))
X_test <- test %>% 
  select(-c(charges, sex, region)) %>% 
  mutate(smoker = as.numeric(smoker))

我们使用下面的公式来应用多项式组合。

formula <- as.formula(
  paste(
    ' ~ .^2 + ', 
    paste('poly(', colnames(X_train), ', 2, raw=TRUE)[, 2]', collapse = ' + ')
  )
)

formula#> ~.^2 + poly(age, 2, raw = TRUE)[, 2] + poly(bmi, 2, raw = TRUE)[, 
#>     2] + poly(children, 2, raw = TRUE)[, 2] + poly(smoker, 2, 
#>     raw = TRUE)[, 2]
#> <environment: 0x0000020d27017690>

然后,将y_trainy_test插回到新的数据集。

train_poly <- as.data.frame(model.matrix(formula, data = X_train))
test_poly <- as.data.frame(model.matrix(formula, data = X_test))
train_poly$charges <- y_train
test_poly$charges <- y_test
colnames(train_poly)#>  [1] "(Intercept)"                        "age"                                "bmi"                               
#>  [4] "children"                           "smoker"                             "poly(age, 2, raw = TRUE)[, 2]"     
#>  [7] "poly(bmi, 2, raw = TRUE)[, 2]"      "poly(children, 2, raw = TRUE)[, 2]" "poly(smoker, 2, raw = TRUE)[, 2]"  
#> [10] "age:bmi"                            "age:children"                       "age:smoker"                        
#> [13] "bmi:children"                       "bmi:smoker"                         "children:smoker"                   
#> [16] "charges"

我们可以看到我们的新数据集train_polytest_poly现在有 16 列:

  1. (Intercept)是由常数 1 组成的列,这是多项式中的常数项。
  2. agebmichildrensmoker为原始特征。
  3. age²bmi²children²smoker²都是正方形的原始特征。
  4. age x bmiage x childrenage x smokerbmi x childrenbmi x smokerchildren x smoker是四个特征对之间的六种相互作用。
  5. charges是目标特征。

现在,我们准备制作模型。像往常一样,我们从所有的特征开始,使用反向消除一路向下。

temp <- lm(formula = charges ~ ., data = train_poly)
step(temp)#> Start:  AIC=18196.7
#> charges ~ `(Intercept)` + age + bmi + children + smoker + `poly(age, 2, raw = TRUE)[, 2]` + 
#>     `poly(bmi, 2, raw = TRUE)[, 2]` + `poly(children, 2, raw = TRUE)[, 2]` + 
#>     `poly(smoker, 2, raw = TRUE)[, 2]` + `age:bmi` + `age:children` + 
#>     `age:smoker` + `bmi:children` + `bmi:smoker` + `children:smoker`
#> 
#> 
#> Step:  AIC=18196.7
#> charges ~ `(Intercept)` + age + bmi + children + smoker + `poly(age, 2, raw = TRUE)[, 2]` + 
#>     `poly(bmi, 2, raw = TRUE)[, 2]` + `poly(children, 2, raw = TRUE)[, 2]` + 
#>     `age:bmi` + `age:children` + `age:smoker` + `bmi:children` + 
#>     `bmi:smoker` + `children:smoker`
#> 
#> 
#> Step:  AIC=18196.7
#> charges ~ age + bmi + children + smoker + `poly(age, 2, raw = TRUE)[, 2]` + 
#>     `poly(bmi, 2, raw = TRUE)[, 2]` + `poly(children, 2, raw = TRUE)[, 2]` + 
#>     `age:bmi` + `age:children` + `age:smoker` + `bmi:children` + 
#>     `bmi:smoker` + `children:smoker`
#> 
#>                                        Df   Sum of Sq         RSS   AIC
#> - `age:smoker`                          1      638029 25718637159 18195
#> - `bmi:children`                        1      817883 25718817013 18195
#> - age                                   1     5612815 25723611945 18195
#> - `age:children`                        1     9673381 25727672511 18195
#> - `age:bmi`                             1    26766877 25744766007 18196
#> - `children:smoker`                     1    45654901 25763654030 18197
#> - children                              1    47711668 25765710798 18197
#> <none>                                                25717999130 18197
#> - `poly(children, 2, raw = TRUE)[, 2]`  1    63400816 25781399946 18197
#> - `poly(bmi, 2, raw = TRUE)[, 2]`       1   257208016 25975207146 18205
#> - `poly(age, 2, raw = TRUE)[, 2]`       1   258018286 25976017416 18205
#> - bmi                                   1   446640632 26164639762 18213
#> - smoker                                1  2015205306 27733204436 18275
#> - `bmi:smoker`                          1 13705549198 39423548327 18651
#> 
#> Step:  AIC=18194.73
#> charges ~ age + bmi + children + smoker + `poly(age, 2, raw = TRUE)[, 2]` + 
#>     `poly(bmi, 2, raw = TRUE)[, 2]` + `poly(children, 2, raw = TRUE)[, 2]` + 
#>     `age:bmi` + `age:children` + `bmi:children` + `bmi:smoker` + 
#>     `children:smoker`
#> 
#>                                        Df   Sum of Sq         RSS   AIC
#> - `bmi:children`                        1      828285 25719465444 18193
#> - age                                   1     4976641 25723613800 18193
#> - `age:children`                        1     9825302 25728462461 18193
#> - `age:bmi`                             1    26787718 25745424877 18194
#> - `children:smoker`                     1    45119820 25763756979 18195
#> - children                              1    47286634 25765923793 18195
#> <none>                                                25718637159 18195
#> - `poly(children, 2, raw = TRUE)[, 2]`  1    63295487 25781932646 18195
#> - `poly(bmi, 2, raw = TRUE)[, 2]`       1   257419441 25976056600 18203
#> - `poly(age, 2, raw = TRUE)[, 2]`       1   257631866 25976269025 18203
#> - bmi                                   1   447419331 26166056489 18211
#> - smoker                                1  2445531784 28164168943 18290
#> - `bmi:smoker`                          1 13756292505 39474929664 18651
#> 
#> Step:  AIC=18192.76
#> charges ~ age + bmi + children + smoker + `poly(age, 2, raw = TRUE)[, 2]` + 
#>     `poly(bmi, 2, raw = TRUE)[, 2]` + `poly(children, 2, raw = TRUE)[, 2]` + 
#>     `age:bmi` + `age:children` + `bmi:smoker` + `children:smoker`
#> 
#>                                        Df   Sum of Sq         RSS   AIC
#> - age                                   1     5196666 25724662109 18191
#> - `age:children`                        1    11062155 25730527598 18191
#> - `age:bmi`                             1    27346149 25746811593 18192
#> - `children:smoker`                     1    46443285 25765908729 18193
#> <none>                                                25719465444 18193
#> - `poly(children, 2, raw = TRUE)[, 2]`  1    63858086 25783323530 18193
#> - children                              1   101526014 25820991458 18195
#> - `poly(bmi, 2, raw = TRUE)[, 2]`       1   256592182 25976057625 18201
#> - `poly(age, 2, raw = TRUE)[, 2]`       1   258013469 25977478912 18201
#> - bmi                                   1   446982085 26166447529 18209
#> - smoker                                1  2466700190 28186165633 18289
#> - `bmi:smoker`                          1 13890308278 39609773722 18652
#> 
#> Step:  AIC=18190.98
#> charges ~ bmi + children + smoker + `poly(age, 2, raw = TRUE)[, 2]` + 
#>     `poly(bmi, 2, raw = TRUE)[, 2]` + `poly(children, 2, raw = TRUE)[, 2]` + 
#>     `age:bmi` + `age:children` + `bmi:smoker` + `children:smoker`
#> 
#>                                        Df   Sum of Sq         RSS   AIC
#> - `age:children`                        1    10800698 25735462807 18189
#> - `age:bmi`                             1    22525274 25747187384 18190
#> - `children:smoker`                     1    46333040 25770995149 18191
#> <none>                                                25724662109 18191
#> - `poly(children, 2, raw = TRUE)[, 2]`  1    59835331 25784497440 18192
#> - children                              1    97931108 25822593218 18193
#> - `poly(bmi, 2, raw = TRUE)[, 2]`       1   252131105 25976793215 18199
#> - bmi                                   1   441828081 26166490190 18207
#> - `poly(age, 2, raw = TRUE)[, 2]`       1   527300413 26251962522 18211
#> - smoker                                1  2465304613 28189966723 18287
#> - `bmi:smoker`                          1 13885998170 39610660279 18650
#> 
#> Step:  AIC=18189.43
#> charges ~ bmi + children + smoker + `poly(age, 2, raw = TRUE)[, 2]` + 
#>     `poly(bmi, 2, raw = TRUE)[, 2]` + `poly(children, 2, raw = TRUE)[, 2]` + 
#>     `age:bmi` + `bmi:smoker` + `children:smoker`
#> 
#>                                        Df   Sum of Sq         RSS   AIC
#> - `age:bmi`                             1    24487435 25759950242 18188
#> <none>                                                25735462807 18189
#> - `children:smoker`                     1    49159638 25784622445 18190
#> - `poly(children, 2, raw = TRUE)[, 2]`  1    64458233 25799921040 18190
#> - children                              1   235234433 25970697239 18197
#> - `poly(bmi, 2, raw = TRUE)[, 2]`       1   255427192 25990889999 18198
#> - bmi                                   1   439409358 26174872164 18206
#> - `poly(age, 2, raw = TRUE)[, 2]`       1   548137618 26283600425 18210
#> - smoker                                1  2461486940 28196949747 18285
#> - `bmi:smoker`                          1 13883155020 39618617827 18649
#> 
#> Step:  AIC=18188.44
#> charges ~ bmi + children + smoker + `poly(age, 2, raw = TRUE)[, 2]` + 
#>     `poly(bmi, 2, raw = TRUE)[, 2]` + `poly(children, 2, raw = TRUE)[, 2]` + 
#>     `bmi:smoker` + `children:smoker`
#> 
#>                                        Df   Sum of Sq         RSS   AIC
#> <none>                                                25759950242 18188
#> - `children:smoker`                     1    49343171 25809293413 18189
#> - `poly(children, 2, raw = TRUE)[, 2]`  1    75043200 25834993442 18190
#> - `poly(bmi, 2, raw = TRUE)[, 2]`       1   246287385 26006237626 18197
#> - children                              1   257057142 26017007384 18197
#> - bmi                                   1   414955971 26174906212 18204
#> - smoker                                1  2440592260 28200542501 18283
#> - `bmi:smoker`                          1 13871440992 39631391233 18647
#> - `poly(age, 2, raw = TRUE)[, 2]`       1 14593600500 40353550742 18666

#> 
#> Call:
#> lm(formula = charges ~ bmi + children + smoker + `poly(age, 2, raw = TRUE)[, 2]` + 
#>     `poly(bmi, 2, raw = TRUE)[, 2]` + `poly(children, 2, raw = TRUE)[, 2]` + 
#>     `bmi:smoker` + `children:smoker`, data = train_poly)
#> 
#> Coefficients:
#>                          (Intercept)                                   bmi                              children  
#>                            11544.691                              -820.904                              1711.975  
#>                               smoker       `poly(age, 2, raw = TRUE)[, 2]`       `poly(bmi, 2, raw = TRUE)[, 2]`  
#>                           -18804.065                                 3.319                                -9.138  
#> `poly(children, 2, raw = TRUE)[, 2]`                          `bmi:smoker`                     `children:smoker`  
#>                             -154.913                              1405.110                              -458.435

将最佳模型保存为lm_poly,然后预测。之后,计算指标。现在,我们很幸运在预测中没有负值,因此可以直接应用 RMSLE 计算。

lm_poly <- lm(formula = charges ~ bmi + children + smoker + `poly(age, 2, raw = TRUE)[, 2]` + 
    `poly(bmi, 2, raw = TRUE)[, 2]` + `poly(children, 2, raw = TRUE)[, 2]` + 
    `bmi:smoker` + `children:smoker`, data = train_poly)

y_pred <- predict(lm_poly, test_poly)
mae <- MAE(y_pred, test$charges)
rmse <- RMSE(y_pred, test$charges)
rmsle <- RMSLE(y_pred, test$charges)

poly_reg <- cbind("MAE" = mae, "RMSE" = rmse, "RMSLE" = rmsle)
poly_reg#>           MAE     RMSE     RMSLE
#> [1,] 2835.106 4327.179 0.3926167

模型评估

来看看我们原来的线性回归模型的总结。

summary(lm_all)#> 
#> Call:
#> lm(formula = charges ~ age + bmi + children + smoker, data = train)
#> 
#> Residuals:
#>    Min     1Q Median     3Q    Max 
#> -11734  -2983  -1004   1356  29708 
#> 
#> Coefficients:
#>              Estimate Std. Error t value             Pr(>|t|)    
#> (Intercept) -11905.11    1060.63 -11.225 < 0.0000000000000002 ***
#> age            254.87      13.52  18.844 < 0.0000000000000002 ***
#> bmi            320.64      30.69  10.447 < 0.0000000000000002 ***
#> children       429.86     156.22   2.752              0.00603 ** 
#> smokeryes    23586.13     468.26  50.370 < 0.0000000000000002 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 6168 on 1064 degrees of freedom
#> Multiple R-squared:  0.7359, Adjusted R-squared:  0.7349 
#> F-statistic: 741.3 on 4 and 1064 DF,  p-value: < 0.00000000000000022

我们有四个特征,所有这些特征对charges都很重要(有真实的影响,而不是由于随机和抽样)。从这些系数中,我们知道一个不吸烟、没有孩子、身体质量指数为零的零岁老人将被医疗保险收取 11905 美元(我们知道这种情况是不可能的)。此外,由于smoker在所有特性中具有最大的系数,所以在所有其他特性都固定的情况下,smoker中的单位变化比其他特性中的单位变化产生更大的变化。在这种情况下,假设所有其他特征都是固定的,不吸烟者将比吸烟者少 23,586 美元的charge,这是有道理的。

该模型还具有 0.7349 的调整后 R 平方,这意味着该模型及其特征解释了charges中总变化的 73%。

现在,让我们将其与新的多项式回归模型进行比较。

summary(lm_poly)#> 
#> Call:
#> lm(formula = charges ~ bmi + children + smoker + `poly(age, 2, raw = TRUE)[, 2]` + 
#>     `poly(bmi, 2, raw = TRUE)[, 2]` + `poly(children, 2, raw = TRUE)[, 2]` + 
#>     `bmi:smoker` + `children:smoker`, data = train_poly)
#> 
#> Residuals:
#>      Min       1Q   Median       3Q      Max 
#> -11017.7  -1932.9  -1339.8   -559.8  29962.3 
#> 
#> Coefficients:
#>                                         Estimate  Std. Error t value             Pr(>|t|)    
#> (Intercept)                           11544.6914   3699.3731   3.121              0.00185 ** 
#> bmi                                    -820.9038    198.6602  -4.132            0.0000388 ***
#> children                               1711.9751    526.3834   3.252              0.00118 ** 
#> smoker                               -18804.0652   1876.3926 -10.021 < 0.0000000000000002 ***
#> `poly(age, 2, raw = TRUE)[, 2]`           3.3189      0.1354  24.505 < 0.0000000000000002 ***
#> `poly(bmi, 2, raw = TRUE)[, 2]`          -9.1376      2.8703  -3.183              0.00150 ** 
#> `poly(children, 2, raw = TRUE)[, 2]`   -154.9126     88.1557  -1.757              0.07916 .  
#> `bmi:smoker`                           1405.1099     58.8124  23.891 < 0.0000000000000002 ***
#> `children:smoker`                      -458.4348    321.7241  -1.425              0.15447    
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 4930 on 1060 degrees of freedom
#> Multiple R-squared:  0.8319, Adjusted R-squared:  0.8307 
#> F-statistic: 655.9 on 8 and 1060 DF,  p-value: < 0.00000000000000022

我们有八个特征,除了children:smoker之外,所有这些特征在charges上都很重要。从这些系数中,我们知道一个不吸烟的零岁无子女且身体质量指数为零的人将被医疗保险收取 11,540 美元(我们知道这种情况是不可能的)。此外,由于smoker在所有特性中具有最大的系数,所以在所有其他特性都固定的情况下,smoker中的单位变化比其他特性中的单位变化产生更大的变化。在这种情况下,假设所有其他特征都是固定的,一个不吸烟的人将比一个吸烟的人多 18800 美元。等等,什么!?从这个分析中,我们知道通过使用多项式组合向我们的模型引入新特征,关于模型的假设可能改变,并且模型的解释可能误导。

该模型的调整后 R 平方为 0.8307,这意味着该模型及其特征解释了charges中总变化的 83%。换句话说,这个多项式回归模型比早期的线性回归模型捕获了更多的方差charges

但是不要自高自大!使用线性回归模型时,需要满足几个假设:

  1. 数据的线性度

我们需要确保预测值和目标变量之间存在线性关系。这可以通过直观地查看每对预测变量和目标变量之间的相关性来实现。

cols <- c('age', 'children', 'bmi', 'smoker')

temp <- train %>% 
  select(cols) %>% 
  mutate(smoker = as.numeric(smoker))
temp$charges <- y_train
ggcorr(temp, label = T)

另一种方法是使用皮尔逊积差相关的假设检验。

  • H0:预测者与charge没有关联
  • H1:预测器与charge相关
for (col in cols) {
  cor <- cor.test(temp[, col], temp$charges)
  print(round(cor$p.value, 4))
}#> [1] 0
#> [1] 0.0062
#> [1] 0
#> [1] 0

由于每个预测值-目标值对的 p 值低于α(0.05),因此拒绝 H0。我们可以有把握地说,预测因素与目标变量相关。

现在,对于多项式回归。

cols <- c('bmi', 'children', 'smoker', 'poly(age, 2, raw = TRUE)[, 2]', 
          'poly(bmi, 2, raw = TRUE)[, 2]', 'poly(children, 2, raw = TRUE)[, 2]', 
          'bmi:smoker', 'children:smoker')

ggcorr(train_poly %>% select(c(cols, 'charges')), hjust = 1, layout.exp = 2, label = T)

for (col in cols) {
  cor <- cor.test(train_poly[, col], train_poly$charges)
  print(round(cor$p.value, 4))
}#> [1] 0
#> [1] 0.0062
#> [1] 0
#> [1] 0
#> [1] 0
#> [1] 0.1327
#> [1] 0
#> [1] 0

我们可以说,预测因子与目标变量相关,除了children²

2。残差的正态性

线性回归模型的残差应该是正态分布的,因为我们期望得到零值附近的残差。为了看到这一点,我们可以绘制残差直方图。

hist(lm_all$residuals)

另一种方法是用夏皮罗-维尔克检验我们的残差。

  • H0:残差呈正态分布
  • H1:残差不是正态分布的
shapiro.test(lm_all$residuals)#> 
#>  Shapiro-Wilk normality test
#> 
#> data:  lm_all$residuals
#> W = 0.89481, p-value < 0.00000000000000022

因为 p 值低于 alpha (0.05),所以拒绝 H0。因此,残差不是正态分布的。

现在,对于多项式回归。

hist(lm_poly$residuals)

shapiro.test(lm_poly$residuals)#> 
#>  Shapiro-Wilk normality test
#> 
#> data:  lm_poly$residuals
#> W = 0.65297, p-value < 0.00000000000000022

同理适用,残差不是正态分布。

3。同方差

异方差是指变量在其值域内的可变性不相等的情况。在线性回归模型中,如果其误差的方差在整个目标变量范围内显示出不等的变化,则表明存在异方差,这意味着残差中存在非随机模式。我们可以通过绘制拟合值与残差图来直观地看到这一点。

plot(lm_all$fitted.values, lm_all$residuals)
abline(h=0, col = "red")

另一种方法是使用 Breusch-Pagan 假说。

  • H0:同性恋
  • H1:异方差
bptest(lm_all)#> 
#>  studentized Breusch-Pagan test
#> 
#> data:  lm_all
#> BP = 89.206, df = 4, p-value < 0.00000000000000022

因为 p 值低于 alpha (0.05),所以拒绝 H0。这意味着残差具有异方差性。

现在,对于多项式回归。

plot(lm_poly$fitted.values, lm_poly$residuals)
abline(h=0, col = "red")

bptest(lm_poly)#> 
#>  studentized Breusch-Pagan test
#> 
#> data:  lm_poly
#> BP = 13.059, df = 8, p-value = 0.1098

因为 p 值高于 alpha (0.05),所以接受 H0。这意味着残差满足同方差假设。

4。预测值之间没有多重共线性

评估多重共线性的统计工具之一是方差膨胀因子(VIF)。简而言之,VIF 是一种在我们的模型中测量预测因子间多重共线性影响的方法。VIF < 10 表示预测值之间没有多重共线性。

vif(lm_all)#>      age      bmi children   smoker 
#> 1.018303 1.013178 1.004060 1.003712vif(lm_poly)#>                                  bmi                             children                               smoker 
#>                            66.442833                            17.844408                            25.228626 
#>      `poly(age, 2, raw = TRUE)[, 2]`      `poly(bmi, 2, raw = TRUE)[, 2]` `poly(children, 2, raw = TRUE)[, 2]` 
#>                             1.020981                            56.740085                             6.702367 
#>                         `bmi:smoker`                    `children:smoker` 
#>                            32.271971                            11.527064

在线性回归模型中未发现多重共线性,但在多项式回归模型中发现多重共线性。这是有意义的,因为多项式回归中的某些要素是通过将线性回归模型中的两个要素相乘而创建的。

结论

result <- rbind(lin_reg, poly_reg)
rownames(result) <- c("Linear Regression", "Polynomial Regression")
result#>                            MAE     RMSE     RMSLE
#> Linear Regression     3941.464 5672.102 0.5455373
#> Polynomial Regression 2835.106 4327.179 0.3926167

对于线性回归这样的简单模型,特征工程在改进模型方面起着重要的作用。在本文中,我们通过对 2 阶特征进行多项式组合来应用这一技术。我们看到模型有了显著的提高,MAE 为 2835,RMSE 为 4327,RMSLE 为 0.39。然而,在这个过程中,一些关于线性回归的假设可能会失效。还有,抽烟对钱包不好!!

🔥你好!如果你喜欢这个故事,想支持我这个作家,可以考虑 成为会员 。每月只需 5 美元,你就可以无限制地阅读媒体上的所有报道。如果你注册使用我的链接,我会赚一小笔佣金。

🔖想了解更多关于经典机器学习模型如何工作以及如何优化其参数的信息?或者 MLOps 大型项目的例子?有史以来最优秀的文章呢?继续阅读:

Albers Uzila

艾伯斯·乌兹拉

从零开始的机器学习

View list8 storiesAlbers Uzila

艾伯斯·乌兹拉

高级优化方法

View list7 storiesAlbers Uzila

艾伯斯·乌兹拉

MLOps 大型项目

View list6 storiesAlbers Uzila

艾伯斯·乌兹拉

我最好的故事

View list24 storiesAlbers Uzila

艾伯斯·乌兹拉

R 中的数据科学

View list7 stories

胸部 x 光片上的医学图像字幕

原文:https://towardsdatascience.com/medical-image-captioning-on-chest-x-rays-a43561a6871d?source=collection_archive---------12-----------------------

使用 Tensorflow 和 Keras 创建图像字幕深度学习模型,该模型可以编写自动医疗报告,作为自我案例研究的一部分

Olga Guryanova 在 Unsplash 上的照片

目录

  1. 商业问题
  2. 使用机器学习来解决业务问题
  3. 评估指标(BLEU 分数)
  4. 资料组
  5. 现有解决方案
  6. 探索性数据分析
  7. 数据预处理
  8. 输入数据管道
  9. 先决条件
  10. 我的实验和实现细节
  11. 总结和结果
  12. 结论
  13. 未来的工作
  14. 链接到我的个人资料
  15. 参考

1。商业问题

医学成像是创建用于临床分析的身体内部的视觉表示以及一些器官或组织的功能的视觉表示的过程。它们被广泛用于医院和诊所来确定骨折和疾病。医学图像由专门的医学专业人员阅读和解释,并且他们关于被检查区域的每个身体的发现通过书面医学报告传达。撰写医疗报告的过程通常需要大约 5-10 分钟。一天之内,医生们要写上百份的医疗报告,这要花很多时间。本案例研究的目的是建立一个深度学习模型,自动编写胸部 x 光医疗报告的印象部分,减轻医疗专业人员的一些负担。在这里,我将从印第安纳大学获取一个公开可用的数据集,该数据集由胸部 X 射线图像和报告(XML 格式)组成,其中包含关于 X 射线的发现和印象的信息。目标是预测附在图像上的医学报告的印象。

2.使用机器学习来解决业务问题

这个问题是一个图像字幕任务。对于这个数据集,我们得到了一些不同患者的图像和 xml 格式的报告。图像名称和患者的信息都包含在 xml 文件中。我们将使用 regex 从 xml 文件中提取这些数据,并将其转换成 dataframe。然后,我们可以使用预训练的模型从图像中获取信息,使用这些信息,我们可以使用 LSTMs 或 GRUs 生成字幕。

3.评估指标(BLEU 分数)

BLEU score 代表双语评估替角。这里我们将使用 BLEU 分数作为衡量标准。BLEU score 比较预测句子中的每个单词,并将其与参考句子进行比较(这也是在 n 元语法中完成的),并根据原始句子中预测的单词数返回分数。BLEU 分数不是比较翻译性能的好指标,因为具有相同意思的相似单词将被扣分。因此,我们不仅要使用 n-gram BLEU 评分,还要对预测字幕进行采样,并手动将其与原始参考字幕进行比较。BLEU 分数范围从 0 到 1。

预测文字:《狗在跳》

参考文本:《狗在跳》

在这种情况下,我们的 BLEU 分数(n-grams)为 1。这里有一些非常好的文章,可供进一步阅读。

https://machinelearningmastery.com/calculate-bleu-score-for-text-python/

4.资料组

在这里,我将从印第安纳大学获取一个公开可用的数据集,该数据集包括胸部 X 射线图像和报告(XML 格式),其中包含关于 X 射线的比较、指示、发现和印象的信息。本案例研究的目标是预测附在图像上的医疗报告的印象。

取自数据源的样本报告

5.现有解决方案

【Keras 图片说明|作者 Harshall Lamba :这里他使用了 flicker 8k 图片作为数据集。每张图片都有 5 个标题,他已经把它们储存在字典里了。对于数据清理,他将小写应用于所有单词,并删除了特殊符号和带数字的单词(如“hey199”等)。).在提取独特的单词后,他去除了所有在整个语料库中出现频率小于 10 的单词,这样模型可以对异常值具有鲁棒性。现在他给每个标题加上“开始序列”和“结束序列”。对于图像的预处理,他使用了具有图像净数据集权重的 inception v3 模型。图像被发送到该模型,倒数第二层的输出被获取(2048 大小向量,瓶颈特征)并保存这些向量。对于字幕的预处理,他对字幕进行了标记化,并找出了字幕中句子的最大长度(发现是 34),这样他就可以用它来填充。他已经为单词嵌入(200 维)使用了预训练的手套向量,并且还为嵌入层设置了 trainable = False。

来源

这是他用于培训的模型架构,不言自明。上述模型的输出给出了一个概率值,对于该概率值,单词在句子中出现的概率最高。在这里,他使用了贪婪搜索法(获得句子中接下来出现概率最高的单词)。然后用 0.001 的初始学习率和每批 3 张图片(批量大小)训练该模型 30 个时期。然而,在 20 个时期之后,学习率降低到 0.0001。使用的损失是分类交叉熵,而使用的优化器是 Adam。

使用注意力机制的图像字幕| sub ham Sarkar | The Startup:这里他使用了注意力机制进行图像字幕。使用的数据集和以前一样是 flickr8k。这里,训练大小是 6000 幅图像,验证数据大小是 1000 幅图像,测试数据大小是 1000。对于预处理,他删除了标点符号,数字值和单个字符。然后,他创建了一个包含文件名和标题列的数据帧。他将每张图像重新调整为 2242243,然后输入到 ImageNet VGG 16 模型上进行预训练。这里他只取了卷积部分(include_top=False)。他移除了最后一个密集层,并将倒数第二层的输出作为主干特征。

对于字幕,他进行了标记化,并添加了作为标记,用于识别那些不在词汇表中的单词。他用语料库中找到的最大句子长度(=39)填充了标记化序列。

来源

对于注意机制,他使用了全局注意和局部注意,但没有将它们结合起来,而是给出了选择使用哪种注意的选项,在代码文件中,他只使用了局部注意。全局注意力将图像视为一个整体,而局部注意力集中在图像的特定部分(在图像字幕的情况下)。这是通过考虑隐藏状态以导出上下文向量来完成的。全局注意力考虑所有的隐藏向量,而局部注意力考虑每个目标词的隐藏向量的特定窗口。

全球关注

这里的编码器是 VGG 16 型。解码器是 RNN。他在这里使用 GRU。他在这里使用了教师强制方法,由此他在训练期间的不同时间步长输入实际记号作为下一个输入,而不是解码器 gru 的预测记号。全局注意力和局部注意力的不同步骤解释如下:

注意机制:

  1. 这里被认为是编码器隐藏状态的特征是来自 VGG-16 的特征。
  2. 先前的解码器隐藏状态和解码器输出通过解码器 gru 来生成新的隐藏状态和预测概率得分。
  3. 使用来自编码器的所有隐藏状态和新的解码器隐藏状态,我们将计算注意力分数。这些注意力分数然后被软最大化,以给出概率分数(所有部分都应该被关注)
  4. 使用这个概率分数,他通过将每个分数乘以编码器隐藏状态(这里是 cnn 特征)来计算上下文向量,然后将其交给 GRU。
  5. 重复此操作,直到达到填充长度。

局部注意

现在为了评估字幕,他使用了两种方法波束搜索和贪婪搜索。这里,为波束搜索考虑的预测数量是 b=3,7,10(最佳预测)。射束预测获得最大概率的字幕(即,这里他对每个单词的概率值求和,并评估该句子/字幕的最终概率)。

在这里,他使用了 BLEU 评分作为评估预测的标准。他建立了一个自定义的损失函数,只根据标题中的单词进行惩罚。他使用了 Adam 优化器。

一个很好的博客,用于进一步了解注意机制。

6.探索性数据分析

形象

来自数据集的 x 射线

我们可以看到数据集的 3 个样本图像。这些是从正面和侧面拍摄的胸部 x 光片。

报告

取自数据源的样本报告

这是一份报告样本。报告以 xml 格式存储。我们将摘录报告中的比较、指示、发现和印象部分。这是使用 regex 完成的。这是一个非常好的网站,我用它来测试我的正则表达式代码,这对你们会很有帮助。

数据中的报告数为 3955。

在 xml 文件中,报告信息是这样存储的。

这是与此报告相关联的两个图像文件。现在,我们将了解与报告相关联的图像数量的最大值和最小值。

我们可以看到,与一个报告相关联的图像的最大数量可以是 5,而最小数量是 0。我们将提取报告的所有信息部分,即比较、指示、调查结果和印象部分,并使用正则表达式和字符串操作将相关报告的相应 2 幅图像(我们将 2 幅图像作为输入,因为 2 幅图像是与报告关联的最高频率)提取到具有 xml 报告文件名的数据帧中。对于超过 2 张的图像,我们将使用新图像和相同信息创建新的数据点。

这是我们从 xml 报告中提取信息后得到的样本数据帧

发现数据帧中缺少值。从数据帧中删除所有 image_1 和 impression 值为空的数据点。然后,对于在图像 2 中发现的所有缺失值,使用与图像 1 相同的数据路径进行填充。

图像 1 和图像 2 的高度分布

从上图中我们可以看出,420 是 image1 最常见的图像高度,而 624 是最常见的图像高度。对于所有数据点,两幅图像的宽度只有一个唯一值,即 512。由于预先训练的模型是为正方形大小的图像建模的,我们可以选择 224224(与 VGG16 的输入相同)作为图像的指定大小。因此,我们将所有的图像调整为 224224 的形状。

带有说明的示例图像

图像+标题

我们可以看到,发现是从 X 射线的观察,而印象是推断获得的。在本案例研究中,我将尝试根据两幅图像预测医疗报告的印象部分。

所有印象值的词云

我们可以从上面的词云看到,有几个词的频率较高,即“急性心肺”和“心肺异常”。我们将通过观察 impression 特性的值计数来进一步研究这一点。

印象列中最常出现的前 20 个值

从上面的值计数中,我们可以看到前 20 个最频繁出现的单词具有相同的含义,因此相同的信息表明一种类型的数据在该数据中占主导地位。我们将对数据应用一组上采样和下采样,以便模型不会为整个数据集输出相同的值,即减少过拟合。

7.数据预处理

因为从我们所看到的大多数 x 光片来看,大多数数据点都有类似含义的标题,即“无疾病”或“无急性心肺异常”。因此,为了减少过度拟合,首先我将删除所有重复的数据点(即那些与一个标题相关联的图像超过 2 个的数据点),只保留一个,然后根据印象值计数将数据集分成两个,其中一个所有印象值计数都大于 5 (other1),另一个是印象列开始和结束时的 value_counts ⇐5 (other2). Then split the other1 data with test_size=0.1. A sample of 0.05*other2.shape[0] will be then taken and will be added on to the test data that was split. The other data from the other2 will be appended to train.

Now I will upsample and downsample the train dataset. For that I will further divide the dataset into 3, one with impression value counts greater than or equal to 100 (df_majority), 2nd one with value counts lesser than or equal to 5 (df_minority) and final one where the impression value counts between 100 and 5 (df_other). Then df_majority and df_other will be downsampled and df_minority will be upsampled. Finally the number of datapoints of the resulting concatenated dataframe is 4487.

After sampling, the text will be added ‘和“”。现在,在安装了标记器之后,我决定使用标题长度的第 80 个百分位值作为每个印象的最大填充值。

8.输入数据管道

首先,我将解释如何为这个案例研究创建输入数据管道。首先,我将创建一个名为 Dataset 的类,它包含 getitem,_ _ len _ _ method。getitem 方法在调用 Dataset 类的对象时被调用,并向其传递元素索引(如 a[i])。len 方法必须存在于每个具有 getitem 的类中,getitem__ 返回类数据集包含的项数(这里是 df 中的行数)。这里我将应用翻转的随机增加(左,右和上,下)。还发现应用浮雕和锐化大大降低了模型的性能,所以它被丢弃。数据集类将输出两个调整大小的图像特征向量和填充的标记化的真实字幕输入(不包含)和真实字幕输出(不包含)。

我创建的下一个类叫做 Dataloader,它继承自 tf.keras.utils.Sequence 类。这里,数据加载器将成批输出数据点,其中第 I 个索引将输出为(图像 1,图像 2,输入标题),输出标题。它将返回第 I 个索引的 batch_size 个数据点。

9.先决条件

这些是读者为了完全理解建模部分而必须熟悉的一些主题:

  1. 转移学习
  2. 注意机制
  3. 模型子类 API
  4. 编码器解码器型号

10.我的实验和实现细节

对于建模部分,我已经创建了 3 个模型。每个都有相似的编码器,但解码器架构完全不同。

编码器

编码器部分将获取两个图像,并将其转换成主干特征,以提供给解码器。对于编码器部分,我将使用 CheXNET 模型。

CheXNET 模型是一个 Denset121 分层模型,针对数百万张胸部 x 光图像进行训练,用于 14 种疾病的分类。我们可以加载该模型的权重,并通过该模型传递图像。顶层将被忽略。

CheXNET 的最后几层

现在我将创建一个包含 chexnet 模型的图像编码器层,这个模型和之前描述的一样。在这里,我将设置 chexnet 模型可训练为假。

简单编码器解码器模型

这个模型将是我们的基线。在这里,我将建立一个图像字幕模型的简单实现。该架构如下所示:

简单编码器解码器模型

在这里,我将通过 Image_encoder 层传递两个图像,并将两个输出连接起来,然后通过密集层传递。填充的标记化字幕将通过一个嵌入层,我们将使用预训练的手套向量(300 维)作为该层的初始权重。这将被设置为可训练的,然后它通过 LSTM,LSTM 的初始状态是从图像密集层的输出。这些然后被添加,然后通过 output_dense 层,其中输出的数量将是在顶部应用 softmax 激活的词汇大小。

为了训练,我选择了“adam”优化器,并创建了一个基于稀疏分类损失的损失函数,其中我只考虑了真实字幕中单词的损失。

注意力模型

在这个模型中,我将使用全局注意力和 concat 公式。

全球关注

全球注意力层

对于解码器,我创建了一个单步解码器层,它接收解码器输入、编码器输出和状态值。decoder_input 将是任何字符令牌数。这将通过嵌入层传递,然后通过嵌入输出和 encoder_output 将通过关注层传递,关注层将产生上下文向量。然后,上下文向量将通过 RNN(这里将使用 GRU ),初始状态为先前解码器的状态。

一步解码器

解码器模型将所有输出存储在一个 tf 中。TensorArray 并返回它。在这里,我将使用教师强制方法来训练 RNN,而不是传递上一个 rnn 的输出,我将传递下一个原始令牌。

RNN 解码器(图片由作者提供)

s1 在这里是零。所有的 I 在通过关注层和编码器输出后将成为原始的令牌。

最终定制模型

在这里,我将使用一个自定义的编码器和解码器,与注意力模型相同。

对于这个模型的编码器部分,我将从 chexnet 模型中提取主干特征,特别是第三层的输出。这将是 Image_encoder 层的输出。然后,我将通过全局流和上下文流来传递它,这实际上是受另一个用于图像分割目的的模型的启发。这将在下面解释。

全局流和上下文流:该架构实现取自注意力引导的链式上下文聚合图像分割(具体来说是链式上下文聚合模块(CAM) ),它用于图像分割,但我将使用它来提取图像信息。这里我要做的是,图像编码器(即 chexnet)的输出将被发送到全局流。那么来自 chexnet 和全局流的输出将被连接并发送到上下文流。这里将只使用一个上下文流,因为我们的数据集很小,如果使用更复杂的架构,可能会导致欠匹配。这里 全局流提取图像的 全局信息,而上下文流将获取图像的局部特征。然后,来自全局流和上下文流的输出将被求和,然后在整形、应用批量规范和丢失之后被发送到解码器。

用于图像分割的注意力引导的链式上下文聚合

全球流动

全局流程

  1. 这里,全局流将从图像编码器层获取信息,然后我将应用全局平均池,这将产生(batch_size,1,1,过滤器数量)。
  2. 然后,我们将对与输入相同的形状应用批量标准化、relu、1*1 卷积和上采样。

上下文流

上下文流程

  1. 我们将从全局流和图像编码器层获得数据,并将其连接到最后一个轴上。
  2. 然后应用平均池,将特征图的大小减少 N*倍。
  3. 那么 3×3 的单个卷积将被应用两次。(不会应用任何 CShuffle)
  4. 之后,我们将应用 11 卷积,随后是 relu 激活,然后再次应用 11 卷积,随后是 sigmoid 激活。这与来自上下文融合模块的输出相乘,然后添加到来自上下文细化模型的输出,该输出然后将以与输入相同的大小被上采样(这里 conv2d 转置将用于获得与输入相同数量的滤波器)。

定制最终模型

上下文流和全局流的总和输出将被连接(这里 add_1 和 add_2 分别是 image_1 和 image_2 的结果),然后将被发送到密集层,该密集层将滤波器的数量转换为 512,在一系列整形、批量归一化和丢弃之后,该密集层将被发送到解码器。解码器架构与注意力模型的架构相同。

11.总结和结果

使用波束搜索和贪婪搜索方法来分析预测。贪婪搜索只输出每个时间步中最可能出现的单词,而波束搜索通过乘以每个时间步中每个单词的概率,得到概率最高的句子,从而显示出最可能出现的句子。贪婪搜索比波束搜索快得多,而波束搜索被发现产生正确的句子。关于该主题的进一步阅读材料:

https://machinelearningmastery.com/beam-search-decoder-natural-language-processing/

简单编码器解码器模型

简单编码器解码器模型的 BLEU 分数

发现贪婪搜索和波束搜索(top_k = 3)的 bleu 分数是相似的,因此我们可以说最佳模型将基于简单编码器解码器模型的贪婪搜索,因为它是最快的。发现 top_k = 5 的 bleu 分数非常慢,因此将其丢弃。

样本测试数据的结果

从上面我们可以看到,模型已经为所有图像预测了相同的标题。结果发现,模型预测所有图像的标题相同,如下所示:

预测字幕的值计数

从上述结果中,我们可以看到,对于每个数据点,该模型都预测“没有急性心肺异常”,这表明该模型已经过拟合。由于这是一个基线模型,我们可以检查其他模型的预测,并将它们的性能与此进行比较。

注意力模型

注意力模型的 BLEU 分数

基于 bleu 评分,该模型的表现类似于简单基线模型。发现波束搜索(top_k = 3)在预测一个字幕时很慢,因此放弃了该方法。现在,我们将观察一些随机预测,看看模型的表现如何:

这里也有一些单词预测正确

该模型比简单基线模型表现得更好,因为它产生了具有更高可变性并且还保持语言相似的字幕。从预测的值计数中可以看出,模型预测的句子比基线模型具有更大的可变性。

最终定制模型

最终定制模型的 Bleu 分数

与其他两种型号相比,这种型号也产生了类似的蓝色。查看预测的值计数,我们可以看到有一点可变性,比基线模型好,但不如第二个模型好。

最终自定义模型的预测值计数百分比

这里也与注意力模型的情况相同,发现波束搜索(top_k = 3)在预测一个字幕时很慢,因此该方法被丢弃。

测试数据的一些随机预测如下所示:

从上面我们可以观察到,该模型仅正确预测了那些没有疾病的图像,而对于其他图像则没有。我们可以得出结论,注意模型是三个模型中表现最好的模型。

12.结论

每种方法的最佳模型按累积 BLEU-4 分数降序排列

就 bleu 分数而言,这些是每种方法的最佳模型。此处,列表根据累积 BLEU-4 分数进行排序。我们得到的最佳模型是注意力模型(贪婪搜索)。该模型的性能远远优于其他两个模型,因为它能够比其他模型更正确地输出疾病名称和一些观察结果。其他模型仅输出标题,假设显示的所有数据点都属于“无疾病类别”。简单的编码器/解码器模型只能为整个数据集输出一个字幕,这表明过拟合。具有更大可变性的更多数据(特别是带有疾病的 X 射线)更有助于模型更好地理解,并有助于减少对“无疾病类别”的偏见。

13.未来的工作

  1. 我们可以使用 BERT 来获得字幕嵌入,而不是使用具有手套向量权重的嵌入层,并将其用作解码器的输入。
  2. 我们可以尝试使用简单的解码器,而不是在最终的定制模型中使用注意力解码器。
  3. 我们也可以使用可视化 BERT。或者尝试在解码器中使用 GPT-2 或 GPT-3 来生成字幕。
  4. 获得更多带有疾病的 X 射线图像,因为该数据集中可用的大多数数据都属于“无疾病”类别。

14.链接到我的个人资料— Github 代码和 LinkedIN

你可以在这个 github 链接 上找到这个案例研究的完整代码。你可以在 Linkedin 或者ashishthomas7@gmail.com联系我。我还使用 Streamlit 创建了一个 Web 应用程序。可以在这里 查看 app

15.参考

  1. 唐,刘,张,江,张,注意引导的链式上下文聚合语义切分(2020)
  2. 应用人工智能课程
  3. https://towards data science . com/image-captioning-with-keras-teaching-computers-to-description-pictures-c 88 a 46 a 311 b 8
  4. https://medium . com/swlh/image-captioning-using-attention-mechanism-f 3d 7 fc 96 eb0e
  5. 【https://medium.com/r/? URL = https % 3A % 2F % 2f machinelearningmastery . com % 2f beam-search-decoder-natural-language-processing % 2F
  6. https://medium.com/r/?URL = https % 3A % 2F % 2f towards data science . com % 2 fan-intuitive-explain-of-beam-search-9b1d 744 e 7a 0 f
  7. https://medium.com/r/?URL = https % 3A % 2F % 2f towards data science . com % 2f bleu-双语-评估-替角-2b4eab9bcfd1
  8. https://medium.com/r/?URL = https % 3A % 2F % 2f machinelearningmastery . com % 2f calculate-bleu-score-for-text-python % 2F

新冠肺炎的医学影像诊断

原文:https://towardsdatascience.com/medical-image-diagnosis-in-covid-19-ba5f6664efe9?source=collection_archive---------41-----------------------

这篇综述介绍了人工智能在新型冠状病毒医学图像上的应用

图来源:此处

在这篇综述中,我将介绍可用的医学图像类型,已发布的深度学习模型的概述,当前挑战和可能的解决方案的概述,以及您可以使用的数据集的链接。最后,如果你想深化某些主题,我还会列出一些可用的资源。而且,我会提供文献参考来探讨。

指数

  1. 医学图像的数据类型
  2. 在 COVID 医学图像中使用人工智能
  3. 当前挑战
  4. 结论
  5. 可用数据集
  6. 其他资源
  7. 参考书目

简介

自其爆发以来,新型冠状病毒已有超过 1 亿例病例,并导致超过 250 万人死亡(1)。最常见的症状是发热、咳嗽和发烧,而一些患者还经历了:腹泻、恶心、食欲不振(2,3)。然而,许多被感染的人是无症状的,是潜在的传染源。Covid 检测通常通过逆转录聚合酶链反应(RT-PCR)进行(4)。尽管做了很多努力,但测试结果需要 2-3 个小时和专家处理(平均在 24-48 小时内处理完),而快速测试仍然存在高假阴性率的问题。此外,在许多国家,工具包的成本和稀缺是重要的问题。然而,这种测试只能检测病毒 RNA,而不能提供患者感染的信息。医学图像提供了对病人状况的洞察。在这篇综述中,我们将重点介绍对患者分层和检测肺部受累很重要的医学影像技术。

图一。每百万人口中的病例数。图来源:(5)

医学图像的数据类型

胸透(CXR) 。CXR 已被用于检测肺部受累。观察到的主要特征是肺结节、玻璃样阴影(GGO、肺密度增加)和间质改变(6)。

图二。新型冠状病毒病人的 CXR。图来源:(7)

胸部 CT。这是最准确的诊断方法(尽管 RT-PCR 可在早期检测到感染,因为 CXR 可能在症状出现后 4-5 天保持正常)并提供进展的证据(8,9)。CT 的另一个优点是其检测肺栓塞的能力。缺点是 CT 费时且不容易获得(也更贵)。CT 发现的主要特征是 GGO,网状影(小的线性阴影)。研究人员在一些病例中发现了胸腔积液和肺纤维化。在 SARS 和 MERS 等其他冠状病毒感染中也发现了类似的异常(尽管一些研究表明 COVID 导致双侧参与,而 SARS 和 MERS 导致单侧参与(10))。

图三。一个新型冠状病毒病人的 CT。图来源:(7)

胸部超声。虽然 CT 和 CXR 是最常用的方法,但一项初步研究显示 CT 扫描和胸部超声之间的结果相似(11)。主要研究是胸膜线的不规则性(距离不规则、不连续和厚度增加(12))。该技术侵入性较小,但是也不太灵敏,并且不能检查肺部的深层区域。

在 COVID 医学图像中使用人工智能

人工智能可以帮助早期诊断和识别有并发症风险的患者。此外,人工智能可以帮助识别与严重疾病相关的风险因素或优化治疗计划。不同的小组已经开发了用于肺部疾病的分类和量化的算法。通常,算法的主要任务是选择肺,提取特征以便检测肺异常。大多数算法是在 CT 或 CXR 图像上训练的,因为有许多可用的数据集。像转移学习(结合已经训练好的学习算法)、集成学习(结合来自多个模型的预测)、级联网络(串联结合两个或多个模型)这样的技术已经被用于提高准确性。我们将在下面看到一些例子。

基于 CT 图像的算法

我们将介绍一些新开发的算法。李等人使用的数据集包括胸部 CT 检查、肺炎和其他病例。他们使用 ResNet-50 提取特征并检测新冠肺炎病例。他们的算法能够区分新冠肺炎和肺炎,尽管它不能评估新冠肺炎病例的严重程度(13)。

Jin 等人使用了从不同中心收集的 10,000 个图像的数据集(分为四类,新冠肺炎,肺炎,非肺炎,流感),他们使用了卷积神经网络(CNN)。该算法取得了良好的性能,但仍然高度依赖于感染规模(14)。Chen 等人提出了基于 UNet++的模型(15)来检测,实现了 94.34%的灵敏度和 99.16%的特异性(16)。

然而,由于是基于分割的算法,缺点是依赖于手动分割进行训练,这对操作者的可变性是敏感的。郑等人在 3D CT 上使用了一种周监督算法。使用 UNet 分割肺部区域,然后使用 3D 深度神经网络预测 COVID 感染的概率(17)。这项研究的缺点是训练样本有限(499 CT)。另一种称为细节关系提取神经网络(DRE 网络)的算法由 Ying 等人开发,用于对 COVID 或肺炎患者进行分类。DRE 网络是一种 ResNet-50 算法,在顶部具有特征金字塔网络(FPN)以提取特征(18)。

基于 CXR 图像的算法

CXR 的一个优点是它可以与 RT-PCR 并行进行,并且它是一种快速和广泛的技术(被认为是临床中的标准设备和存在的便携式系统)。使用人工智能对 CXR 图像进行诊断的研究较少。Abbas 等人在 1746 个图像的数据集上使用 CNN 模型,而 Wang 等人提出了另一种基于 CNN 的模型,使用不同的数据集(19,20)。一项有趣的研究使用迁移学习来比较新冠肺炎检测中的 5 个预训练模型(ResNet-50、inception V3、ResNet-101、ResNet-152 和 Inception-ResNet2)。他们的研究显示 ResNet-50 的准确率最高(98%),但即使数据集很大,也只有一小部分来自新型冠状病毒患者(7406 张图像中的 341 张)(21)。

主要挑战是大型数据集的可用性有限。Khalifa 等人提出使用生成对抗网络(GAN)来生成其他图像(数据增强),然后通过迁移学习进行分类(在他们的案例中,基于 ResNet-18 的模型实现了最高的准确性)(22)。

当前挑战

  • Covid 数据集稀缺。尽管有大量的数据集,但许多数据集只包含少数来自新型冠状病毒患者的图像(有过度拟合的风险)。数据扩充、迁移学习、少量学习是潜在的解决方案。
  • 阶级失衡。有许多关于肺炎或正常肺图像的数据集,并且许多具有新型冠状病毒病例的数据集呈现更多的第一类图像。因此,该模型对新型冠状病毒的精确度较低。重采样、随机欠采样策略、合成少数过采样(SMOTE)、为成本函数中的类分配权重是用于不平衡问题的一些策略。
  • 神器。在 CXR 图像中,经常出现外部物体(电线、项链)、注释错误(左右颠倒)、文本注释。这导致预测减少,并且图像清理是一项耗时的任务。肺部分割(U-Net 等)。
  • 低对比度。直方图均衡化是一种解决方案。
  • 相似的临床表现。有些病例中,病毒性肺炎表现出与新型冠状病毒相似的症状。

图 4。外部对象的示例。图来源:(23)

结论

经过研究人员的努力,在很短的时间内,已经取得了许多进展。一个理想的人工智能检测系统必须是强大和稳定的(输出不应随着人口统计和患者相关的变化而变化,如考虑合并症和年龄)。它必须是快速的、经济的、可靠的、可重复的。

我们提出了许多研究,但在将它们纳入临床实践之前,有必要进行进一步的临床验证。然而,我们仍然需要一些基准数据集来比较这些模型。开发的大多数模型都能够识别晚期新型冠状病毒,而我们需要能够识别早期患者的模型。此外,我们需要能够预测受感染患者结果的模型。另一个挑战是区分新型冠状病毒与 SARS 和 MERS(我们需要增加标记数据的数量)。

可用数据集

CT 数据集:

40 名患者的 100 个轴位 CT:此处

20 CT 扫描带分割:此处

来自 216 名患者的 349 张 CT 图像:此处

CXR 数据集:

数据集:此处

3616 张新型冠状病毒图像(以及更多来自非 COVID 患者的图像):此处

另一个数据集也增加了训练数据:这里是

另一个数据集:这里

另一个数据集:这里

混合 CT/CXR 数据集

包含大量 CXR 和 CT 图像的混合数据集:此处

其他资源

转移学习:此处

特色金字塔网(FPN): 此处

少投学习:此处

参考书目

1.冠状病毒新冠肺炎(2019-nCoV)。可从以下网址获取:https://gisanddata . maps . ArcGIS . com/apps/ops dashboard/index . html #/BDA 7594740 FD 40299423467 b48e 9 ECF 6【2021 年 3 月 22 日获取】

2.傅莉,王 B,袁 T,陈 X,敖 Y,Fitzpatrick T,李 P,周 Y,林 Y,段 Q,等.中国冠状病毒病 2019(新冠肺炎)的临床特征:一项系统综述和荟萃分析.J 感染(2020)80:656–665。doi:10.1016/j jinf . 2020 . 03 . 041

3.Cholankeril G,Podboy A,Aivaliotis VI,Tarlow B,Pham EA,斯潘塞 SP,Kim D,Hsing A,Ahmed A .严重急性呼吸综合征冠状病毒 2 型患者并发胃肠道表现的高患病率:来自加利福尼亚州的早期经验胃肠病学(2020)159:775–777。doi:10.1053/j . gastro . 2020 . 04 . 008

4.闫春,崔军,黄 L,杜 B,陈 L,薛 G,李 S,张 W,赵 L,孙 Y,等.逆转录环介导等温扩增技术快速可视化检测 2019 新型冠状病毒株.临床微生物感染(2020)26:773–779。doi:10.1016/j.cmi

5.Suri JS,Agarwal S,Gupta SK,Puvvula A,Biswas M,Saba L,Bit A,Tandel GS,Agarwal M,Patrick A,等,《使用人工智能对新冠肺炎感染肺中急性呼吸窘迫综合征的特征进行描述性综述》。计算机生物医学 (2021) 130 :104210。doi:10.1016/j . comp biomed . 20021 . 102102020315

6.Jacobi A,Chung M,Bernheim A,Eber C .冠状病毒病-19(新冠肺炎)的便携式胸部 X 线检查:图片综述。临床成像(2020)64:35–42。doi:10.1016/j . clinimag . 2020 . 04 . 001

7.Benameur N,Mahmoudi R,Zaid S,Arous Y,Hmida B,Bedoui MH。利用医学影像技术和人工智能进行新型冠状病毒诊断:综述。临床成像(2021)76:6–14。doi:10.1016/j . clinimag . 2021 . 01 . 019

8.艾涛,杨志,侯红,詹春,陈春,吕伟,陶青,孙志,夏。冠状病毒病 2019(新冠肺炎)胸部 CT 和 RT-PCR 检测的相关性:1014 例报告。放射学(2020)doi:10.1148/radiol . 20027 . 263636363606

9.疑似新冠肺炎感染患者的胸部 CT:RT-PCR 的可靠替代方法Am J Emerg Med(2020)38:2730–2732。doi:2016 年 4 月 10 日

10.新型冠状病毒肺炎的胸部 CT 影像特点及临床价值。临床放射(2020)75:335–340。2002 年 3 月 20 日

11.2019-2020 年流行期间新型冠状病毒肺炎的肺部超声检查结果。重症监护医疗(2020)1–2。doi:10.1007/s 00134–020–05996–6

12.新冠肺炎肺炎的超声征象和模式。超声波 J(2020)12:doi:10.1186/s 13089–020–00171-w

13.李,秦,徐,尹,王,孔,白,陆,方,宋,等。人工智能在胸部 CT 上鉴别新冠肺炎与社区获得性肺炎。放射学(2020)doi:10.1148/radiol . 20025 . 202020202006

14.金春,陈伟,曹勇,徐志,谭志,张 X,邓 L,郑 C,周军,石宏,等。一个用于诊断的人工智能系统的开发与评价。(2020 年)2020 . 03 . 20 . 20。33360 . 38363836366 土井:10.1101/2020 . 03 . 20 . 353838363636

15.周,Siddiquee MMR,Tajbakhsh N,梁 J. UNet++:一种用于医学图像分割的嵌套 U-Net 体系结构。深度学习 Med 影像肛门多模态学习 Clin Decis 支持(2018)(2018)11045:3–11。doi:10.1007/978–3–030–00889–5 _ 1

16.基于深度学习的 2019 新型冠状病毒肺炎高分辨率计算机断层扫描检测模型。科学报告(2020)10:doi:10.1038/s 41598–020–76282–0

17.郑,邓,傅,周,冯,马,刘,王。基于深度学习的胸部 CT 图像弱标记新冠肺炎检测。medRxiv(2020)2020 . 03 . 12 . 2020。50306 . 38668686666doi:10.1101/2020.03 . 12 . 153536736736

18.应生,郑生,李 L,张 X,张 X,黄 Z,陈 J,赵 H,王 R,崇 Y,等。深度学习使新型冠状病毒(新冠肺炎)的准确诊断与 CT 图像。medRxiv(2020)2020 . 02 . 23 . 2002 . 2930。doi:10.1101/2020.02 . 23 . 2323233333326

19.使用 DeTraC 深度卷积神经网络对胸部 X 射线图像中的新冠肺炎进行分类。arXiv:200313815【cs,eess,stat】(2020)可在:http://arxiv.org/abs/2003.13815【2021 年 3 月 24 日获取】

20.王 L,林,黄 A. COVID-Net:一种用于从胸部 X 射线图像中检测新冠肺炎病例的定制深度卷积神经网络设计。 Sci 报告(2020)10:doi:10.1038/s 41598–020–76550-z

21.使用 X 射线图像和深度卷积神经网络自动检测冠状病毒疾病(新冠肺炎)。arXiv:2003 10 849【cs,eess】(2020)可在:http://arxiv.org/abs/2003.10849【2021 年 3 月 24 日获取】

22.哈利法·NEM、塔哈·MHN、哈桑尼恩·艾、埃尔加姆拉维·s .使用胸部 X 射线数据集,基于生成性对抗网络和微调深度迁移学习模型检测冠状病毒(新冠肺炎)相关性肺炎。arXiv:2004 01 184【cs,eess】(2020)可在:http://arxiv.org/abs/2004.01184【2021 年 3 月 24 日获取】

23.拉赫曼 S,萨克 S,米拉杰 MAA,尼哈尔拉,纳迪姆哈克阿克姆,诺曼 AA。深度学习驱动的放射影像新冠肺炎自动检测:一项比较分析。科宁电脑(2021)1–30。doi:10.1007/s 12559–020–09779–5

基于 Python 的医学图像预处理

原文:https://towardsdatascience.com/medical-image-pre-processing-with-python-d07694852606?source=collection_archive---------3-----------------------

用于训练模型的 dicom 图像预处理概述。

CT 图像- 作者提供的图像

数据怎么样

在这篇文章中,我将解释如何用简单的例子预处理美丽的医学图像来训练任何人工智能模型,以及如何通过所有预处理阶段为模型准备数据以给出最高的结果。

我要用的数据是一堆 2D 大脑 CT 图像。它们是 DICOM 格式的。您可以简单地将这些操作应用于您自己的数据,以从您的模型中获得更有效的结果。

在我们开始编码之前,让我们先讨论一下医学数据。首先我来解释一下什么是 CT。

计算机断层扫描是一种扫描,它拍摄从不同角度发送到身体的 X 射线图像,并使用计算机处理器进行组合,以访问身体各个部位的骨骼、血管和软组织的横截面图像(切片)。这些图像提供了比常规 x 射线图像更详细的信息。这样,患者的骨骼、静脉或组织中的异常被检测到。这就是为什么,可以对病人作出更准确的诊断,并相应地继续治疗。

现在我们来讨论一下,DICOM 格式是什么。

DICOM 是医学数字成像和通信的首字母缩写。这种格式的文件很可能以“dcm”文件扩展名保存。DICOM 既是一种通信协议,也是一种文件格式;这意味着患者可以在一个文件中存储医疗信息,如超声和 MRI 图像及其信息。而 png 或 jpg 文件只包含图片的名称、日期和像素数;dicom 格式包括病人的信息,图片的开窗间隔,我们称之为元数据。简而言之,它包括更详细的患者信息。这种格式不仅可以将所有数据保存在一起,还可以确保信息在支持 DICOM 格式的设备之间传输。我从 Kaggle 那里拿了几张 dcm 图像。

**import** **pydicom** file_path = "/Users/esmasert/Desktop/basicsDcm/10095.dcm"
medical_image = pydicom.read_file(file_path)
print(medical_image)

阅读 Dicom 文件— 作者图片

作者图片

在对 CT 和 dicom 做了基本的总结之后,让我们继续进行预处理。在图像的预处理阶段,我使用了 5 个步骤。

这些步骤是:变换到 HU,去除噪声,倾斜校正,裁剪图像和填充。

在将这些预处理步骤应用到数据之后,我们看到模型精度得到了显著提高。我正在讲解预处理方法。要以更清晰的格式查看代码,您可以访问这个链接。

在以 DICOM 格式加载我们的图像数据后,我们将把它转换成 Hounsfield 单位形式。

变身胡

Hounsfield 单位(HU)是一种无线电波强度的相对定量测量单位,放射科医生使用它来更好地解释和理解计算机断层扫描(CT)图像。在 CT 重建期间使用组织内辐射的吸收/衰减系数来产生灰度图像。线性变换产生显示为灰色调的 Hounsfield 标度。更致密的组织,具有更大的 X 射线束吸收,具有正值并且看起来更亮;密度较低的组织对 X 射线束的吸收较少,具有负值,看起来较暗。[1]Hounsfield 单位是以著名的 Godfrey Hounsfield 爵士命名的,他有计算机断层摄影的部分发明,并因此获得诺贝尔奖。

我们可以通过使用重新调整截距和重新调整坡度标题来获得 HU:

**def** transform_to_hu(medical_image, image):
    intercept = medical_image.RescaleIntercept
    slope = medical_image.RescaleSlope
    hu_image = image * slope + intercept

    **return** hu_image**def** window_image(image, window_center, window_width):
    img_min = window_center - window_width // 2
    img_max = window_center + window_width // 2
    window_image = image.copy()
    window_image[window_image < img_min] = img_min
    window_image[window_image > img_max] = img_max

    **return** window_image

如果您想要图像的特定区域,您可以调整图像的窗口。

去除噪音

在预处理过程中,去除噪声是一个非常重要的阶段,因为实施后数据得到改善,我们可以更清楚地看到它。因此,模型可以被更好地训练。[2]

**def** remove_noise(file_path, display=**False**):
    medical_image = pydicom.read_file(file_path)
    image = medical_image.pixel_array

    hu_image = transform_to_hu(medical_image, image)
    brain_image = window_image(hu_image, 40, 80) #bone windowing

    segmentation = morphology.dilation(brain_image, np.ones((1, 1)))
    labels, label_nb = ndimage.label(segmentation)

    label_count = np.bincount(labels.ravel().astype(np.int))
    label_count[0] = 0

    mask = labels == label_count.argmax()

    mask = morphology.dilation(mask, np.ones((1, 1)))
    mask = ndimage.morphology.binary_fill_holes(mask)
    mask = morphology.dilation(mask, np.ones((3, 3)))
    masked_image = mask * brain_image **return** masked_image

作者图片

作者图片

倾斜校正:

倾斜校正是大脑图像对齐的一种建议方式。当脑部 CT 图像经历倾斜时,它可能导致医学应用的不对准。这很重要,因为当我们训练模型时,它可以通过相同的排列看到整个数据。手动校正大规模数据的倾斜既费时又费钱。因此,需要一种在训练前的预处理中执行倾斜校正的自动方法。

img=numpy.uint8 (iskemiMaskedImg)
contours, hier =cv2.findContours (img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
mask=numpy.zeros (img.shape, numpy.uint8)

*# find the biggest contour (c) by the area*
c = max(contours, key = cv2.contourArea)

(x,y),(MA,ma),angle = cv2.fitEllipse(c)

cv2.ellipse(img, ((x,y), (MA,ma), angle), color=(0, 255, 0), thickness=2)

rmajor = max(MA,ma)/2
**if** angle > 90:
    angle -= 90
**else**:
    angle += 96
xtop = x + math.cos(math.radians(angle))*rmajor
ytop = y + math.sin(math.radians(angle))*rmajor
xbot = x + math.cos(math.radians(angle+180))*rmajor
ybot = y + math.sin(math.radians(angle+180))*rmajor
cv2.line(img, (int(xtop),int(ytop)), (int(xbot),int(ybot)), (0, 255, 0), 3)

pylab.imshow(img)
pylab.show()

M = cv2.getRotationMatrix2D((x, y), angle-90, 1)  *#transformation matrix*

img = cv2.warpAffine(img, M, (img.shape[1], img.shape[0]), cv2.INTER_CUBIC)

pylab.imshow(img)
pylab.show()

作者图片

裁剪图像并添加填充:

需要对图像进行裁剪,以将大脑图像置于中心,并去除图像中不必要的部分。此外,一些大脑图像可能被放置在一般图像中的不同位置。通过裁剪图像和添加填充,我们将确保几乎所有的图像都在普通图像本身的相同位置。

作者图片

**def** add_pad(image, new_height=512, new_width=512):
    height, width = image.shape

    final_image = np.zeros((new_height, new_width))

    pad_left = int((new_width - width) // 2)
    pad_top = int((new_height - height) // 2)

    *# Replace the pixels with the image's pixels*
    final_image[pad_top:pad_top + height, pad_left:pad_left + width] = image

    **return** final_image

作者图片

下面是结果!一个干净的,正确的和居中的大脑图像。准备好进去训练了。

我在下面附上了参考资料。非常感谢 https://vincentblog.xyz/!它确实帮助我更深入地理解了图像处理。

如果您有任何建议或问题,请在下面评论。非常感谢!

参考文献

  1. https://www.ncbi.nlm.nih.gov/books/NBK547721/
  2. https://Vincent blog . XYZ/posts/medical-images-in-python-computed-tomography
  3. https://link . springer . com/article/10.1007/s 10278-020-00400-7
  4. 【https://www.eyewated.com

使用 Azure 机器学习的医学成像

原文:https://towardsdatascience.com/medical-imaging-with-azure-machine-learning-b5acfd772dd5?source=collection_archive---------16-----------------------

图片由微软提供。

安德烈亚斯·科普和哈姆克·阿尔克马德

概观

医疗人工智能(AI)应用提供了显著的社会效益。它们通过提高医疗保健专业人员的工作效率,在日常工作流程中为他们提供帮助,扩大了医疗保险的覆盖范围。智能医疗设备用于低人口偏远地区的健康筛查。基于人工智能的早期疾病检测的突破实现了积极主动、往往侵入性更小的治疗,并且可以挽救生命。

此外,随着人们使用智能应用程序进行健康监测和自我检查,医学正变得越来越民主化。然而,这不是要取代医生的访问。相反,患者会收到来自应用程序的指示,提示他或她向医生寻求澄清。医生从患者的日常生活中获得补充的有价值的健康数据。

基于 X 射线、CT、MRI、显微镜或其他成像技术的医学计算机视觉用例尤为重要。给定足够质量和数量的训练数据,算法可以可靠地发现异常,有时甚至超过医疗专业人员。但是医学计算机视觉也有自己的挑战。

  • 医学成像技术使用的光学分辨率通常为每维 100,000 像素,远远超过当今计算机视觉神经网络架构的能力。
  • 可解释性是关键。机器学习算法倾向于利用工件和目标类之间的相关性作为捷径。例如,躺在床上的病人的射线照片通常比典型的 x 射线照片具有更低的图像质量。患者不能移动的事实可能与医疗状况相关。因此,该算法可能会了解到模糊的 x 射线图像与肺炎等特定疾病有关,而不是了解疾病的真正潜在视觉模式。
  • 医疗患者信息属于我们能想到的最敏感的数据。因此,隐私是一个大问题。在医疗保健组织中,关于使用或训练基于受保护患者数据的 ML 模型的监管和隐私影响,存在许多不确定性。

微软 AI Ranger 团队在 Github.com/Azure/medical-imaging 的下提供了几个计算机视觉演示,用于学习目的和在医疗场景中重用。

医学影像库用例。作者图片。

我们的 Python 笔记本的目的是展示如何使用 Azure Machine Learning 来支持医疗成像和数据和模型管理、部署、实验跟踪和可解释性等领域的其他用例。此外,我们涵盖了各种数据科学方法,从使用 PyTorch 的手动模型开发到图像的自动机器学习。另一个重点是提供基于 MLOps 的例子,用于自动化医疗用例的机器学习生命周期,包括新数据可用时的再培训。

脑肿瘤检测的交互式实验

与其他癌症不同,脑瘤属于相当罕见的疾病。每 100,000 人中大约有 7 人患恶性脑瘤。可靠的诊断是基于成像技术,如磁共振成像(MRI)。功能性 MRI 用于外科手术的准备,因为其与邻近区域的关系可以很好地可视化。

对于我们的脑肿瘤分类演示,我们使用代表以下诊断(类别)的 MRI 图像数据集:神经胶质瘤、脑膜瘤、垂体瘤、无瘤

数据集可以从这个位置下载:脑肿瘤分类(MRI) | Kaggle 。我们的笔记本可以在这里找到:互动-实验。

Azure 机器学习支持几个创作选项,包括 Jupyter、JupyterLab、Visual Studio 代码和 Azure ML Studio 中的集成笔记本体验。后一个选项支持智能感知代码完成、代码片段和协作功能,如注释和共同编辑。

Azure 机器学习工作室的笔记本体验。作者图片。

本演示的一个主要目的是展示一位经验丰富的数据科学家如何利用该平台进行灵活的实验,同时管理所有相关资产。我们使用流行的 MLflow 库来管理 Azure ML 服务的实验。

脑肿瘤图像训练数据被用作 Azure ML 中的版本化数据集。我们提取最新版本的数据作为我们培训的基础。首先,我们执行预处理和基本数据扩充(例如,旋转、水平翻转)来训练更一般化的模型。由于这是训练工作流的关键部分,我们存储预处理配置参数和一组实验运行的结果样本图像:

预处理后的脑肿瘤样本图像。作者图片。

使用预训练的 ResNet50 卷积神经网络(CNN)来建立模型。我们还将模型架构记录为实验运行的资产,以便以后可以对其进行审查。这对于确保实验几个月甚至几年后的可追溯性特别有用。

既然我们已经准备好了数据和架构,我们就可以训练模型了。我们正在使用一组基本的超参数(例如优化器、学习率、批量),我们还会在手动超参数流程中对其进行跟踪、审查和调整。

我们正在利用一个基于 Tesla V100 GPU 的 Azure 计算实例进行培训。我们的 PyTorch 训练循环运行 15 个时期,并返回与具有最佳验证准确性的时期相关联的模型。

为了评估模型在看不见的数据上的表现,我们检查了一组基于测试集的指标。一个结果是混淆矩阵,它也是 Azure 机器学习中的交互式资产:

测试集混淆矩阵。作者图片。

通过这个实验,我们实现了大约 96%的测试集准确性,这与我们在训练过程中观察到的验证结果一致。

为了获得对模型预测至关重要的图像相关部分的视觉指示,我们使用 PyTorch Captum 进行解释。

Grad-CAM 对预测类的解释。作者图片。

这些样本显示了被分类为神经胶质瘤肿瘤的脑肿瘤图像的梯度加权类别激活映射(Grad-CAM)。Grad-CAMs 使用流入最终图层的渐变来生成本地化地图,突出显示预测输出的重要区域。虽然 Grad-cam 很有帮助,但这是一种相当粗糙的可视化方法。存在更高级的可解释性技术(例如,分层相关性传播),其更精确地显示关键区域。

我们将 Grad-CAM 热图与本次实验的其他运行资产存储在一起,以提高可追溯性。

训练深度学习模型是一个迭代的实验过程。我们通常在调整预处理、尝试各种架构和调整超参数的同时重复我们的实验,以连续改进模型的性能。进行数十次不同配置的训练是常见的做法。

Azure ML 的实验管理功能支持这一优化过程,这样我们就不会迷失在难以理解的试错练习中。每次我们提交培训流程时,都会创建一次运行,同时跟踪配置设置和最终的性能指标(例如,分类准确性)。

查看 Azure ML Studio 中的实验概述,我们看到了从第一个原型到完成的脑肿瘤检测模型的开发里程碑。

用 Azure ML 跟踪 ML 实验。作者图片。

我们观察选择的超参数以及我们的选择如何影响模型性能。从准确性指标来看,我们发现验证准确性从我们第一个原型的 68.5%提高到了最终模型的 97.8%。

我们选择维护的所有资产(例如,预处理图像样本、神经网络架构定义、混淆矩阵、CAM 解释)都存储在每次运行的上下文中。

使用 MLOps 自动检测肺炎

肺炎是由细菌或病毒引起的呼吸道感染。它影响到许多人,特别是在发展中国家和不发达国家,那里污染严重、不卫生的生活条件和过度拥挤相对普遍,对儿童的影响尤其大。

肺炎的早期诊断对确保治愈性治疗和提高存活率至关重要。胸部 x 光成像是诊断肺炎最常用的方法。然而,胸部 x 光检查是一项具有挑战性的任务,并且容易出现主观可变性。在这个笔记本中,用于检测病毒性或细菌性肺炎的医学图像的相对较小的公共数据集用于训练肺炎分类模型。该数据集包含 5218 张 x 射线图像,具有两类诊断结果:3876 例(病毒性或细菌性)肺炎(74%)和 1342 例无发现(“正常”)(26%)。

您可以在这个位置下找到数据集:胸部 x 光图像(肺炎)| Kaggle

来自肺炎数据集的样本图像。作者图片。

在笔记本中,展示了三种不同的方法来使用 Azure ML 的独特功能训练模型。所有方法都利用 Python 的 Azure ML SDK 来配置和提交训练运行到 Azure ML。数据科学家和人工智能开发人员可以使用 Azure ML SDK,通过 Azure 机器学习服务来构建和运行机器学习模型。不同类型的训练运行允许模型的灵活开发,因为运行可以被配置为在各种计算目标上执行。

笔记本从训练一个基线模型开始,该模型被配置为运行在带有 GPU 虚拟机的 Azure 集群上。第二部分展示了一种调整超参数的方法,使用了一种称为 Hyperdrivestep 的特殊类型的管道步骤。此功能允许您定义要优化的不同参数,以及训练脚本。

笔记本的第三部分也是最后一部分转换了在 Azure ML 管道中运行的远程实验。Azure ML 管道是可独立执行的工作流/步骤集,用于完成机器学习任务。管道是 ML 生命周期自动化的一个很好的工具,因此也是 MLOps 的一个很好的工具。

下图显示了 MLOps 流程的概况。它包含将机器学习模型引入生产的步骤,从培训到部署。此外,基于触发器,该模型被周期性地重新训练。触发器可以基于重现、模型性能(监控)或新的可用训练数据。

MLOps 流程。作者图片。

在笔记本中,根据训练数据集中的文件更改来实现重新训练触发器。这可以使用 Azure ML SDK 来完成:

reactive_schedule = Schedule.create(workspace,  name="MyReactiveSchedule",
description="Based on input file change.",
pipeline_id=pipeline_endpoint_by_name.id,
experiment_name='experiment_name',
datastore=datastore,
data_path_parameter_name="input_data")

具有自动机器学习的血细胞检测

在前面的分类示例中,需要几个手动步骤:我们选择或创建一个特定的 CNN 架构,并提供定制代码来训练 PyTorch 模型。

现在,我们利用 Azure 新的图像自动机器学习功能来简化这一过程,同时将其应用于更具挑战性的计算机视觉用例。

使用 AutoML for Images,我们只需要提供带标签的训练图像并设置训练框架参数。然后,AutoML for Images 有助于为我们想要优化的给定性能指标(例如,分类精度)找到预处理、神经网络架构和超参数的最佳组合。

AutoML for Images 目前支持基于以下计算机视觉架构的多类和多标签分类、对象检测和实例分割。

图像任务和架构的自动化 ML。作者图片。

查看文档或该视频,了解更多关于图像自动化 ML 的信息:

在这个演示笔记本中,我们使用 AutoML 进行血细胞对象检测,并在下面的示例中进行实例分割。

我们用于本演示的小规模数据集包含三个类别:红细胞(RBC)、白细胞(WBC)、血小板。

你可以从这个位置下载数据集: BCCD 数据集| Kaggle 。

连接到 Azure ML workspace 后,我们创建一个新的(或连接到现有的)计算集群用于培训。在本演示中,我们指定了一个具有 4 个节点的 GPU 集群,用于并行化具有不同配置的训练运行,以实现超参数优化。

第一次运行笔记本时,我们需要提供训练数据,并将基于 Pascal VOC 的注释(血细胞类定义和边界框坐标)转换为 AutoML for Images 期望的基于 JSONL 的格式。该笔记本包括示例注释转换代码。转换后,批注和图像文件被上传并注册为托管 Azure ML 数据集。您可以在所有后续运行中重复使用这些数据集,而无需再次上传数据。

在我们的演示中,我们指定了一个专用的验证数据集。这是一个可选步骤。如果没有提供验证数据集,AutoML for Images 将使用 20%的训练样本进行验证。

根据训练数据和配置的数量,优化过程可能需要几个小时。因此,建议在提交一系列超参数调整运行之前,执行一次训练运行作为基线。

使用我们的对象检测模型的默认参数来训练 YOLOV5 模型的配置很简单:

from azureml.train.automl import AutoMLImageConfig
from azureml.automl.core.shared.constants import ImageTask
from azureml.train.hyperdrive import GridParameterSampling, choiceimage_config_yolov5 = AutoMLImageConfig(task=ImageTask.IMAGE_OBJECT_DETECTION, compute_target=compute_target, training_data=training_dataset, validation_data=validation_dataset, hyperparameter_sampling=GridParameterSampling({'model_name': choice('yolov5')}), iterations=1)automl_image_run = experiment.submit(image_config_yolov5)

除了指定任务类型和 GPU 集群,我们还将训练和验证数据集作为参数以及所需的模型类型进行传递。平均精度(mAP)被自动选择为优化对象检测任务的主要度量。

经过 10 个时期的训练后,该模型达到了 83.4%的 mAP。该指标考虑了正确分类的对象数量,以及真实情况和预测边界框之间的正确重叠区域。实验的所有指标都可以在 Azure ML Studio 中的相应运行下进行跟踪。

83.4%的平均精度是一个很好的结果,因为我们没有优化任何超参数。我们能否通过利用 AutoML 针对图像的超参数优化功能来实现更好的性能?

对于第二个实验,我们指定了一个参数空间,该参数空间基于两个计算机视觉架构、学习速率的范围、优化器选择、图像大小和其他参数。

AutoML for Images 通过从定义的参数空间中随机选择配置来训练一系列模型。通过选择提前终止策略,我们可以确保低性能配置被提前终止,从而释放计算资源用于更多试验。

超参数扫描的结果可以在 Azure ML Studio 的实验部分观察到:

图像优化过程的自动化 ML 的结果。作者图片。

在提交的 20 个试验中,有 5 个表现不佳的试验被提前终止。最佳运行实现了 92.9 %的 mAP,超出初始基线运行 9.5 个百分点。

有几个选项可以将模型部署到实际生产中。在我们的演示笔记本中,我们将模型部署到一个可扩展的 Azure Kubernetes 服务中。为了查看我们最佳的血细胞物体检测模型的运行情况,我们提供了基于如下几个测试样本的预测:

血细胞目标检测模型的预测。作者图片。

基于自动机器学习的大规模细胞分割

存储库中的第四个笔记本使用 Azure Auto ML 的图像分割功能,应用于细胞分割。单个细胞的精确分割能够探索复杂的生物学问题,但由于低对比度和高对象密度,这需要复杂的成像处理管道。

LIVECell 数据集是一个大规模的显微图像数据集,例如 2021 年 8 月发布的 2D 细胞培养物中单个细胞的分割。它包含高质量的手动注释和专家验证的图像,是目前同类中最大的。关于数据集的更多信息可以在这里找到: LIVECell 数据集| live cell(sartorius-research . github . io)

以下示例概述了 LIVECell 数据集的形态多样性:

LIVECell 数据集中包含的八种细胞类型的形态多样性。图片基于 Edlund,c .、Jackson,T.R .、Khalid,N. live cell——一个用于无标记活细胞分割的大规模数据集。 Nat 方法 18 **,**1038–1045(2021)。https://doi.org/10.1038/s41592–021–01249–6.

创建标注数据集的挑战之一是为具有不同背景的团队成员提供合适的工具,以便在数据标注项目中进行协作。Azure ML 中的数据标签功能是创建、管理和监控标签项目的中心位置。除了常规图像格式,该服务还支持 DICOM 图像,这是医疗保健人工智能医学图像标签的行业标准格式。支持的项目类型有图像分类、对象检测(边界框)和实例分割(多边形)。下图显示了图像分割的标注体验。

用于实例分割的 Azure ML 标记工具。作者图片。

LIVECell 数据集已经被标记,所以我们使用数据集提供的注释。笔记本中的第一步是将 LIVECell 注释转换为 Azure AutoML for Images 支持的结构。之后,我们展示如何将数据作为 Azure ML 数据集进行检索。之后,我们将展示如何为图像分割任务配置运行。

图像的自动化 ML 通过迭代不同的模型参数并比较结果模型的性能来提高数据科学家的性能。下图显示了由 Auto ML 创建的输出图,该图显示了哪些模型配置已经过测试以及相应的模型性能。

自动 ML 超参数优化器输出图。作者图片。

该笔记本还提供了代码,用于将生成的模型作为 webservice 部署为 Azure Kubernetes 服务中的容器。通过这样做,我们可以根据看不见的细胞图像进行推断。下面的图像显示了一些由 Auto ML 模型生成的预测分割,与原始图像和地面真实分割相邻,以供参考。请注意,在这个实验中,我们只使用了 50%的可用训练数据。我们希望能够通过使用完整的数据集进行训练来提高模型的性能。

由我们的自动 ML 模型生成的预测分割。作者图片。

我们希望这个概述对您有用。随意看看我们的 GitHub 代码库 这里 。我们非常感谢您的问题、反馈和想法,因为我们计划在未来扩展这个库!

带 AWS 的医用 NER 理解

原文:https://towardsdatascience.com/medical-ner-with-aws-comprehend-3f27db0a8255?source=collection_archive---------28-----------------------

使用 AWS intensive 构建用于医疗实体检测的 web 应用程序

图片来自 Unsplash

命名实体识别( NER )是最流行和最受欢迎的自然语言处理任务之一。随着 NER 的扩张,它也变得越来越有 T4 特色。为特定领域(如医疗保健/医疗)构建自定义 NER 模型可能非常困难,并且需要大量数据和计算能力。AWS 领悟是一种高级服务,AWS 提供自动化许多不同的 NLP 任务,如情感分析、主题建模和 NER。“理解”分支创建了一个名为理解医疗的子服务,专门面向医疗 NER。在本文中,我们将介绍如何使用 Streamlit 构建一个 web 应用程序,它可以调用 intensive Medical 并返回检测到的医疗实体。本文假设读者具备 AWS 的基础知识、浏览 AWS 控制台的能力以及 Python 基础知识。我将提供一个我们将要使用的服务列表,并在此广告之后提供更深入的定义,但是如果您已经熟悉这些服务,可以随意跳到情感分析&实体提取的代码演示。

目录

  1. AWS 服务概述
  2. 体系结构
  3. 使用 Streamlit 进行前端创建
  4. 创建 Lambda 函数和 REST API
  5. 将 Lambda 功能与 AWS 集成理解医疗
  6. 连接前端和后端
  7. 代码和结论

1.AWS 服务

AWS 领悟医疗 :一个符合 HIPAA 的 NLP 服务,为用户从文本中提取健康数据提供一个高级 API

AWS Lambda :无服务器计算服务,允许开发者运行代码,无需管理或提供服务器。我们将使用此服务来访问 NER 的 AWS 理解医疗,并REST API 通信以将结果输出到前端。

Boto3 : AWS 软件开发工具包( SDK )对于 Python 开发者来说,我们在我们的 Lambda 函数上使用这个来访问其他 AWS 服务,比如 understand。

身份访问和管理(IAM) :允许您通过权限和角色管理对 AWS 服务的访问。我们将为我们的 Lambda 函数创建一个角色,以便能够访问AWS comprehense 和 API GW。

【AWS API 网关(API GW) :允许开发者创建、发布和监控安全 RESTful 和 Socket APIs 的服务。

2.体系结构

架构图(作者截图)

架构相对简单。我们将构建一个 streamlit web 应用程序来访问我们将使用 Amazon API Gateway 创建的 REST API。这个 REST API 将作为后端 Lambda 函数的接口,该函数使用 Boto3 SDK 来访问 NER 医疗的理解医疗。

3.使用 Streamlit 进行前端创建

对于我们的应用程序,我们将使用一个称为 Streamlit 的漂亮 Python 库创建一个简单的前端,让 Python 开发人员和数据科学家能够快速启动和运行 web 应用程序/仪表板。我们的应用程序只需要一些标题和一个用于输入文本的文本框,我们将对其进行分析,以检测任何潜在的医疗实体

创建基本前端设置的代码

稍后将使用请求库来访问我们创建的 API ,但是现在应该创建了一个基本的前端模板,如果您执行"streamlit run filename . py"操作,您应该会看到设置。

前端创作(作者截图)

4。创建 Lambda 函数和 REST API

注意:这一步有点费力,有许多 API 创建的小细节,为了简单起见,我跳过了,如果你喜欢这一部分的视频演示,请继续并跟随这个链接。

现在,当你登录到 IAM 服务后,就该去 AWS 控制台了。在我们可以使用 Lambda 之前,我们需要为我们的 Lambda 函数创建一个 角色 ,该角色赋予它 权限 来使用领悟医疗和 API 网关。一旦进入 IAM 服务,点击页面左侧的角色,点击创建角色。现在您选择角色所服务的服务,在我们的例子中是 Lambda。单击 next: Permissions ,现在我们可以在 search 选项卡中查找与我们的角色相关的策略。确保检查 AmazonComprehendFullAccess、Amazon APIGatewayFullAccess、amazonapigatewaymanagement

Lambda 角色创建(作者截图)

继续并点击创建角色,我们可以越过到达 Lambda 服务。点击创建函数,命名你的 Lambda 函数,选择 Python 3.8 作为你的运行时,点击更改默认执行角色。现在选择使用现有角色并选择您为 Lambda 服务创建的角色。

Lambda 函数创建(作者截图)

继续并点击创建函数,我们已经准备好了 Lambda 函数。我们的下一步API GW 创建一个 REST API ,并将其与我们的 Lambda 函数集成。转到控制台上的 API 网关服务,然后单击创建 API。选择构建 REST API ,命名您的 API,然后点击 create。

REST API 创建(作者截图)

一旦你创建了你的 REST API,确保创建一个 POST 方法,这样我们就可以从我们的前端向我们的后端 Lambda 函数提供数据。在创建了适当的资源和方法之后,确保部署您的 API 并且启用 CORS 。我们现在有了一个 REST API,可以用来集成我们的前端和后端。

5.将 Lambda 功能与 AWS 集成理解医疗

既然已经建立了体系结构的一般流程,我们可以将重点放在后端工作上,以便为 NER 集成理解医疗服务。使用 boto3 库,我们对医疗 NER 使用 API 调用: detect_entities ,无论我们输入什么文本。用于理解医学的检测实体有五个不同的 类别 它可以将术语分类为:解剖、医学状况、药物治疗、受保护健康信息和测试治疗程序。如需了解 boto3 用于理解医疗的 API 调用的更多文档,请点击此链接。

Lambda 函数访问理解医疗

注意,您必须在我们返回的 JSON 中包含一个头部分,以避免任何 CORS 错误。

6.连接前端和后端

现在我们已经部署了 API,我们需要在前端访问这个 REST API,这样我们就可以将输入文本输入 Lambda 函数。我们使用 Python 请求库来访问我们创建的 REST API。

访问实体的 API & Lambda 的代码

该函数的第一部分接收用户输入,并将其传递给我们的 REST API。在 understand Medical 返回其检测到的实体后,我们将数据解析为可呈现的格式,我们可以将其写回前端。

应用示例

7.代码和结论

要访问演示的所有代码,请访问上面发布的链接。AWS 拥有一套不断扩展的高级人工智能服务,可以真正实现自动化,并为时间少或没有构建定制 ML 模型经验的人提供应用程序。

我希望这篇文章对试图使用 AWS 和 NLP 的人有用。如果您对 ML、数据科学和 AWS 感兴趣,请随时在评论中留下任何反馈或通过 Linkedln 与我联系。感谢您的阅读!

语音技术时代的医学转录

原文:https://towardsdatascience.com/medical-transcription-in-the-age-of-voice-tech-ba218b5a514?source=collection_archive---------45-----------------------

人工神经网络通过用于医疗记录的自动语音识别技术,正在缓慢地革新医疗领域

医生现在比以往任何时候都更愿意专注于治疗病人,而不是把时间花在无休止的行政任务上——为什么?因为 AI。人工神经机器网络(ANN)不仅在数据科学领域,而且在医学领域和相应的翻译行业都在慢慢改变着行业。今天,我们将看看人工智能和翻译技术在医学转录中的应用,看看数据科学、医学和翻译是如何为公众服务的。我们将看看人工神经网络对医学领域的影响,特别是自动语音识别(ASR),以及 ASR 在医患互动中的语言障碍。

【Freepik.com】形象由https://www.freepik.com/premium-photo/medicine-doctor-team-meeting-analysis_5284720.htm#page=2&query=data+science&position=34

医学领域中的人工神经网络

人工神经网络的工作有点像人脑中的神经元,这是它们的模型——神经机器网络具有相同的互联性,能够产生大脑的新“神经元”,但相似之处仅此而已。

我们了解到,由于机器智能程序在加载大量数据时学习速度非常快,它们现在可以从中解析许多上下文信息。根据医学研究,他们可能无法知道背景文化信息,但他们对背景视觉线索反应良好。正如这篇文章所提出的,软件算法正在学习超越人类智能,它们正变得善于通过长期增量增长的海量数据层来学习模式。

随着 2019 年疫情奖的颁发,人工智能在解决医疗领域内的问题,然后通过翻译将这些信息传递给公众方面的重要性得到了紧急关注。人工神经网络(ANN)作为虚拟医生、医疗转录和诊断识别的人工智能工具的构建模块,现在不仅是医学研究的主题。现在,在国际领域中,通过将自动语音识别(ASR)用于医学转录,人工神经网络被广泛实施。这就是医学、技术和翻译为公众利益而共同努力的效果,今天仍然可以感受到。

用人工神经网络作为医学抄写员的医学转录

也就是说,人工神经网络(ANN)正在彻底改变医学研究,如果不是医学领域的话。它们很可能被用于数字成像、识别新疾病等等。人工智能研究中出现的最受欢迎的领域不是使用人工智能来创建一个不需要人类医生的虚拟医生,而是一个呼吁人类和机器之间成功合作的领域:医学转录。医学转录是一种专业的医疗服务,它正在慢慢适应医疗行业的变化。

由人工神经网络构建的自动语音识别(ASR)技术(通常用于翻译行业)正在向医疗领域推广,供希望直接向护士和病人发号施令的医生使用。这些是虚拟的医疗记录,而不是人类的医疗记录。通过记录医生与患者互动的转录,语音识别技术可以实现医生出诊的自动化,而没有成堆成堆的文件的繁琐。

使用 ASR 进行医疗转录有助于从更新患者的电子健康记录(EHR)到自动化医疗图表、标记医疗预约到建立医生间转诊的方方面面。例子包括西雅图初创公司 Nuance Communications 旗下的 Saykara。他们将 Saykara 营销为第一个“自动化医生图表的移动人工智能助手”。

语音识别自 20 世纪 50 年代就已经存在,但像所有伟大的技术一样,直到最近几十年,特别是随着谷歌语音搜索和苹果 Siri 的推出,才开始使用。现在有亚马逊的 Alexa,微软的 Cortana,还有很多其他的。Otter.ai、Augnito 和 Decrypt 只是这个语音技术行业中的一些新品牌,它们通过医疗转录彻底改变了临床工作流程。

一切都很好,对吗?是的,也不是。自动语音识别(ASR)对翻译技术产生了重大影响。本研究提出两个问题:1 .众所周知,音频转录和翻译比文本翻译更费时,而且实际上并不能使这一过程自动化。2.由于背景噪音、重叠语音和其他条件,音频质量可能会变得很差,翻译技术尚未提供最佳解决方案。第三。语言障碍可以通过翻译服务来解决,翻译服务在人工智能和医疗文档方面具有专长。

因此,基于人工智能的转录工具和医疗文档是携手并进的,这些工具可以监听患者与医生的互动,而不需要任何手写的文档。但是当病人说韩语而医生说英语时,会发生什么呢?根据这项研究,在韩国,当有语言障碍需要解决时,语音识别技术就更加困难。

此外,ASR 还需要解决不同程度的语言问题:从一种语言到另一种语言的代码转换,到用未知的方言说话,到受到少数语言对的限制,到识别文化背景。这就是为什么最好利用翻译行业的医疗转录员的服务,因为该行业的人具有专业的语言知识,技术医学知识,并完全配备了人工智能技术。

因此,结论已经出来了:在人工智能中,人工智能还不具备完全转录多语言患者-医生互动的能力。安可能会很快改变这种状况,但不是现在。随着语音识别技术在国际范围内的医院和诊所变得越来越普遍,它有望变得更加先进。

了解 Datascienv —一种用于设置数据科学环境的防故障方法

原文:https://towardsdatascience.com/meet-datascienv-a-fail-proof-method-for-setting-up-data-science-environments-4d734290bc3?source=collection_archive---------14-----------------------

如何通过单个 Pip 安装配置数据科学环境

安东尼·里埃拉在 Unsplash 上的照片

管理数据科学工作流并不有趣。通常会涉及到几十个虚拟环境,整个事情就是一团乱麻。说真的,有多少次执行失败仅仅是因为您的 Python 环境缺少一个依赖项?

想象一下,您已经为您的项目创建了一个新的虚拟环境。就库而言,您几乎需要任何可以想象的东西,从数据预处理到数据可视化、机器学习和部署库。手工安装它们需要时间。有更好的方法。

今天,我将向您介绍datascienv —一个简化数据科学环境设置的开源 Python 包。我们将从一个新的环境开始,并从那里配置一切。

不想看书?请观看我的视频:

什么是 Datascienv?

简而言之,datascienv是一个 Python 包,它提供了数据科学环境设置,只需进行一次 pip 安装。根据官方 Pypi 页面,这是它为你安装的库列表:

图 1 —由 datascienv 安装的 Python 包

从日常数据分析、预处理和可视化到机器学习包,这是非常多的。安装完成后,我发现datascienv安装的软件包比列表中的还要多,但更多是在后面。

接下来看看datascienv怎么上手。

如何安装 Datascienv?

我们将从头开始。我假设您已经通过 Anaconda 安装了 Python。如果不是这样,只需翻译终端命令来匹配您的虚拟环境管理器。

1。创建新的虚拟环境

我基于 Python 3.9 创建了一个名为datascienv的虚拟环境:

conda create --name datascienv python=3.9 -y

图 2——创建一个新的虚拟环境(图片由作者提供)

如果一切按计划进行,您应该会看到如下消息:

图片 3 —创建新的虚拟环境(2)(图片由作者提供)

2。激活虚拟环境

配置虚拟环境后,您可以激活它:

conda activate datascienv

您应该会看到(datascienv)显示在路径前面:

图 4 —激活虚拟环境(图片由作者提供)

就这样——您已经准备好安装datascienv了。

3。安装**datascienv**

如果您使用的是 Windows,请确保首先安装了Microsoft Visual c++ 14.0+。不知道有没有 Mac 或者 Linux 的前提条件。我还没有在我的 M1 MacBook Pro 上安装datascienv,因为它仍然不支持所有的数据科学库。至少没有变通办法。

使用以下命令在您的虚拟环境中安装datascienv:

pip install -U datascienv

图 5 —安装 datascienv(图片由作者提供)

安装需要一段时间,因为它需要拉一吨的 Python 包。完成后,您应该会看到这样的输出:

图 6 —安装 datascienv (2)(图片由作者提供)

你现在已经安装了datascienv。但是这到底是什么意思呢?图书馆包括什么?接下来让我们来探索一下。

Datascienv 包含哪些内容?

事实证明,很多。比 Pypi 官方页面上列出的要多。有无数种方法可以检查,可能最简单的方法是将 Anaconda 虚拟环境导出到一个.yaml文件中:

conda env export > \Users\Dario\Desktop\env.yaml

图 7 —导出 Anaconda 环境(图片由作者提供)

用任何文本编辑器打开env.yaml文件。做好准备,因为文件中有将近 300 行:

图 8 — env.yaml 文件(图片由作者提供)

对于单个 pip 命令来说,这已经很多了。不过,让我们通过打开 Python shell 并导入常见的疑点来验证我们是否拥有了所需的一切:

图 9-检查已安装的 Python 包(图片由作者提供)

是—甚至安装了 Plotly、Flask 和 TensorFlow。这些没有列在官方的 Pypi 页面上。我在大多数数据科学项目中使用它们,所以很高兴确认它们被包括在内。

唯一的问题是——谁应该使用,谁不应该使用datascienv

判决

你已经看到了datascienv的能力。这不是什么开创性的东西,但它确实做到了它所宣传的那样,并且节省了你的时间。我向任何探索数据科学和机器学习的人推荐它,因为它安装了所有需要的东西。没有必要杀死 Python 内核,安装一个遗漏的依赖项,然后再次启动 Python。

如果您计划部署您的数据科学解决方案,并希望保持需求文件整洁紧凑,我不建议您这样做。很可能你并不需要datascienv提供的所有服务。

*你对 Datascienv 有什么想法?你试过吗,如果没有,你打算试试吗?*请在下面的评论区告诉我。

喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。

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

保持联系

  • 注册我的简讯
  • 订阅 YouTube
  • 在 LinkedIn 上连接

满足 M6 —以 1%的 GPT-3 能源成本提供 10 万亿个参数

原文:https://towardsdatascience.com/meet-m6-10-trillion-parameters-at-1-gpt-3s-energy-cost-997092cbe5e8?source=collection_archive---------2-----------------------

较小的玩家现在可以进入大型人工智能模型的游戏

vs148 在快门架上拍摄的照片(已编辑)

我可以自信地说,当一个比另一个大 50 倍的神经网络可以用少 100 倍的能量成本训练时,人工智能正在快速发展——中间只有一年!

6 月 25 日,阿里巴巴 DAMO 研究院(阿里巴巴 R&D 分院)宣布他们已经建立了 M6,一个大型多模态、多任务语言模型,拥有 1 万亿个参数——已经是 GPT-3 的 5 倍,是衡量大型人工智能模型进展速度的标准。该模型旨在实现多模态和多任务处理,比以前的模型向一般智能迈进了一步。

就能力而言,M6 类似于 GPT-3 和其他类似的型号,如武道 2.0 或 MT-NGL 530B (我们对此了解甚少)。中国热门科技杂志【InfoQ 整理了 M6 的主要技能:“(它)拥有超越传统人工智能的认知和创造力,擅长绘画、写作、问答,在电子商务、制造业、文学艺术等诸多领域有着广阔的应用前景。”

然而,阿里巴巴研究人员强调的关键方面是效率和能源成本的显著改善。与 1 亿个语言模型相比,它们将模型的消耗减少了 80%,并提高了效率 x11。

符合绿色 AI 原则和目标的极其重要的新闻。

绿色人工智能去垄断大型语言模型

但是他们并没有就此止步,现在,5 个月后,他们刚刚实现了不止一个,而是两个新的里程碑:他们改进了 M6,使其成为第一个 10 万亿参数大型语言模型,是 GPT-3 的 50 倍。他们已经提高了之前的效率,将能耗降低到 GPT 3 号训练所需能耗的 1%。

他们仅用了 512 个 GPU 就在 10 天内训练了这个模型!

这些成就将对人工智能社区和世界产生深远的积极影响。

一方面,这是在大型人工智能模型的必要性和旨在减少碳足迹的清洁能源运动的要求之间找到共同点的一大飞跃。对大型语言模型的主要批评之一是,它们不能补偿它们产生的大量污染。据估计,训练一个大型人工智能模型(GPT 3 之前)的污染比一辆汽车一生中的污染多 5 倍——它们的用处并不明显。亚马逊和微软等科技公司已经提出了未来几年减少碳排放的计划,但都旨在通过冷却数据中心来解决这个问题,而阿里巴巴已经实现了更好的解决方案;减少训练模型所需的资源。

这还有一个重要的优势。如果阿里巴巴公布他们用来实现其结果的技术和方法,较小的参与者可能会与大型科技公司竞争,这些公司目前垄断了大型人工智能模型的超级利润领域。

研究、培训和推理的成本如此之高,以至于像谷歌这样的巨头都难以为这项技术提供资金。谷歌子公司 DeepMind 决定在创建 AlphaStar 时不调查某个关键组件的不同可能性,以避免超出预算。

OpenAI 可以使用微软提供的 10,000 Nvidia V100 超级计算机(尽管尚未披露他们使用的确切 GPU 数量),在研究人员发现一个错误后,open ai 决定不重新训练 GPT-3,因为这是不可行的。一些粗略的计算估计的培训成本至少为 460 万美元,这对于大多数公司来说是可望而不可及的——这还不包括研发成本,这将使这个数字上升到的 1000-3000 万美元。

小公司如何与之竞争?

相比之下,最新版本的 M6 已经在 512 个 GPU 上训练了 10 天。(GPT-3 是在 V100 上训练的,但研究人员计算出使用 A100s,在 34 天内训练该模型需要 1024 个 GPU。)

通过粗略计算,我们可以比较两种模型的培训成本。我假设阿里巴巴使用 Nvidia A100 和与 AWS 类似的 GPU 实例/小时成本,其中 8 个 Nvidia A100 AWS 实例的成本约为 20 美元/小时。假设他们使用了 512 个 GPU,那么就有 64 个 8-A100 实例。通过计算,我们得出总成本= 64 #实例 20 美元/小时 24 小时/天 10 天= 307,200 美元。

仍然有些昂贵,但远不及 OpenAI 训练 GPT-3 的花费。

未来的一线希望

在过去,我一直对大型语言模型非常不满,原因包括歧视和偏见、错误信息的能力、缺乏理解,甚至是因为为什么我们甚至需要更多的大型语言模型?还因为创建这些系统需要高昂的环境和财务成本。

但今天,我为阿里 DAMO 学院公布的结果鼓掌。

看起来他们致力于改善这种新的人工智能趋势带来的一些问题。仍有许多工作要做——其中一些问题是这些模型固有的,我们只能希望减轻它们——但看到大型科技公司致力于改善当前的景观,是人工智能近期未来的一线希望。

如果你喜欢这篇文章,可以考虑订阅我的免费周报https://mindsoftomorrow.ck.page/!每周都有关于人工智能的新闻、研究和见解!

您也可以直接支持我的工作,使用我的推荐链接 这里 成为中级会员,获得无限权限!:)

见见无法停止在 Kaggle 上获胜的数据科学家

原文:https://towardsdatascience.com/meet-the-data-scientist-who-just-cannot-stop-winning-on-kaggle-dfc0e6fe88f8?source=collection_archive---------5-----------------------

与 Philipp Singer 对话:数据科学家,Kaggle 双特级大师,计算机科学博士。

在这一系列采访中,我在 H2O.ai 展示了一些知名数据科学家和 Kaggle 大师的故事,他们分享了自己的旅程、灵感和成就。这些采访旨在激励和鼓励那些想了解成为一名 Kaggle 特级大师的人。

在这次采访中,我将分享我与**菲利普辛格、**在 Kaggle world 中更为人熟知的 Psi 的互动。他是 Kaggle 双特级大师高级 数据科学家atH2O . ai。Philipp 以优异的成绩获得了格拉茨技术大学的计算机科学博士学位,并在那里完成了软件开发和商业管理的硕士学位。

Philipp 有几项成就,包括多次获奖和在 Kaggle 上名列前茅,以及几项科学荣誉,例如在著名的万维网会议上获得最佳论文奖。他是前世界排名第一的选手,目前在全球范围内的 Kaggle 比赛中排名第二,这既令人印象深刻,同时也令人鼓舞。

Philipp 最显著的成就之一是通过与一位 H2O.ai 数据科学家合作赢得了 NFL 第二届年度大数据碗比赛。来自世界各地的 2000 多名数据科学家在 Kaggle 上竞争预测仓促的游戏结果。Philipp Singer 和 Dmitry Gordeev 凭借他们的方法赢得了 5 万美元的最高奖金。

2019-20 大数据碗获奖者 Philipp Singer 和 Dmitry Gordeev(后)在印第安纳波利斯发表演讲。

在这次采访中,我们将更多地了解他的学术背景、他对 Kaggle 的热情以及他作为数据科学家的工作。以下是我与菲利普对话的摘录:

你有计算机科学的博士学位。你为什么选择数据科学作为职业,而不是坚持学术界的研究方向?

菲利普: 我在奥地利格拉茨工业大学获得了计算机科学博士学位,并在德国做博士后研究员。在我的科学生涯中,我接触了许多不同的数据科学主题,并在著名的会议和期刊上发表了许多论文和文章。作为职业生涯的下一步,我将不得不追求教授职位,这听起来很有趣。然而,即使我热爱教学,我也想深入更多的应用工作,这意味着我希望我的工作比研究中最有可能产生的影响更大。这促使我将数据科学作为职业。也就是说,我非常喜欢我的博士学位,并在那段时间里学到了很多东西,但现在我也很高兴处于数据科学和机器学习的前沿,并在 H2O.ai 担任真正的创造者角色。

你与 Kaggle 的幽会是如何开始的,是什么让你在你的大师之旅中一直保持动力?

菲利普的卡格尔简介

Philipp: 大约八年前,我在 Kaggle 上注册,接近我成为博士的第一步,因为我听说了这个平台,想去看看。但是我除了提交一份样本之外什么也没做,然后六年内不再接触 Kaggle。大约两年前,我和德米特里决定一起在 Kaggle 上尝试一个竞赛,作为工作中的副业。我们对它没有任何期望,但最终赢得了比赛,这让我着迷,并开始了我的 Kaggle 之旅。在 Kaggle 上,我的方法一直是解决新类型的问题以保持动力,并且仍然有新的和令人兴奋的问题需要定期解决。我也喜欢在 Kaggle 上与有才华的人见面和一起工作,并看到社区是如何努力的。

最近,你已经凭借一些惊人的成绩杀入 Kaggle 排行榜,最新的是 NFL 第一名和未来-影响检测 **你获得了第二名。你解决这些问题并取得成功的方法是什么?

Kaggle 上的许多成功都是基于经验和愿意去接触和学习乍一看你不太了解的东西。随着时间的推移,我已经组装了一个特殊的通用工具箱,其中包含了我处理过的每个比赛的构建模块。例如,我知道如何建立适当的交叉验证**,什么用于我的模型,如何恰当地拟合模型,跟踪它们的性能,以及类似的事情。所以我已经有更多的时间来关注最近比赛的新的和关键的方面。每次比赛后,我都会努力改进我的工作流程,让自己变得更有效率和竞争力。**

Kaggle 上的许多成功都是基于经验和主动去接触和学习乍一看你不太了解的东西。

你如何决定参加哪些比赛?

菲利普在 Kaggle 上的最高成就

Philipp: 我主要尝试解决新类型的问题或竞赛,这些问题或竞赛听起来与数据或要解决的问题有关。有时,我也会参加一些更标准的比赛来试试运气,以了解艺术每周的变化情况。

你通常如何处理一个难题?任何喜欢的 ML 资源(MOOCS,博客等。)您想与社区分享的内容?

Philipp: 我会尝试利用我已经积累的方法、工具和经验,然后尝试研究手头的具体问题。这意味着我将在 Kaggle 上研究以前类似问题的解决方案,并阅读相关论文。了解一个问题的最好方法是亲自动手,边做边学。

作为 H2O.ai 的数据科学家,你的角色是什么,你在哪些具体领域工作?

Philipp 和其他 kaggle 大师在 H2O.ai

***菲利普:*AtH2O . ai,**我的角色是非常多面的。我经常参与面向客户的项目,我的目标是用我的数据科学专业知识支持项目。此外,作为 Kaggle 大师,我们总是试图利用我们的经验和知识,不断改进我们的产品,开发新的尖端原型和解决方案。例如,这可能意味着我们在 无人驾驶人工智能 中提出新功能的建议,或者在 Wave 中开发人工智能应用,展示新技术或完整的管道数据科学解决方案。

你通过 Kaggle 学到的最好的东西有哪些是你在 H2O.ai 的专业工作中应用到的?

Philipp: 你在 Kaggle 上学到的一件重要的事情是如何生成健壮的模型,这些模型能够很好地概括,并且不会受到强烈的过度拟合。这在 Kaggle 上至关重要,因为你需要在看不见的私有数据上表现良好。这意味着您会学到很多关于健壮的交叉验证的知识,并关心其他数据方面,如特性分布变化或某些重要方面。我可以在 H2O.ai 的工作中很好地利用这些知识,因为这也是我们产品不可或缺的一部分。我们希望通过我们在该领域的专业知识和知识,让客户能够进行强大的机器学习。

数据科学领域正在快速发展。你是如何设法跟上所有最新发展的?

Philipp: 我大多用 Kaggle 来跟上最新动态;这是一个优秀的新技术过滤器,它要么能解决实际和应用问题,要么不能。通常,健壮的方法会保留下来,偶尔有用的边缘技术会被过滤掉。与此同时,我试图通过在 Twitter 和其他平台上关注知名研究人员和从业者来了解最新情况。

你想在 ML 中应用你的专业知识吗?

菲利普在 2020 年 1 月 9 日举行的维也纳数据科学小组会议上的讲话

菲利普: 我没有什么具体的想法;我通常会对工作或社交中突然出现的有趣问题感到惊讶。深入研究乍看之下你并不感兴趣的问题是非常必要的。你也可以对这个问题有一个公正的看法,或许还可以将你从其他问题中获得的经验应用到手头的数据中。

给刚刚开始或希望开始数据科学之旅的数据科学和 Kaggle 有志人士一点建议?

菲利普: 弄脏自己的手,不要害怕失败,永远渴望学习新的东西。

菲利普的卡格尔之旅相当不寻常。我确信,他的旅程、奉献和成就将成为已经在这一领域工作或试图在这一领域取得成就的其他人的灵感来源。

阅读本系列的其他采访:

  • Rohan Rao:数据科学家从数独到 Kaggle 的旅程
  • Shivam Bansal:数据科学家,负责 Kaggle 上的“有益的数据科学”竞赛。
  • 认识 Yauhen:第一位也是唯一一位来自白俄罗斯的 Kaggle 特级大师。
  • 苏达莱·拉杰库马尔:对数字的热情是如何将这位机械工程师变成了一位围棋大师
  • 加博·福多尔:卡格尔世界“白鲸”的励志之旅🐋

在浏览器中与代码相遇(vscode.dev)—与 Web 代码相遇

原文:https://towardsdatascience.com/meet-vs-code-in-the-browser-vscode-dev-vs-code-for-the-web-cf8740211967?source=collection_archive---------7-----------------------

在任何操作系统的任何浏览器上运行 VS 代码

韦雪恩(魏贤)成龙在 Unsplash 上的照片

Visual Studio (VS)代码是最流行的 ide 之一,因为它不仅免费,而且支持多种通用编程语言,并具有安装各种插件的可扩展性。一般来说,要设置 VS 代码或其他任何 IDE,我们通常需要在自己的电脑上下载并安装 app,这在如今并不是一个痛苦的过程,因为各大 IDE 都简化了安装过程。

然而,当您无法访问自己的计算机或者您的计算机不够强大,无法运行这些 ide 时,您的选择就相当有限了。VS Code 团队一直在努力为我们提供更好的解决方案——VS Code 现在是基于网络的应用。换句话说,你可以在浏览器中打开一个网站——VS Code . dev,启动一个在线 VS 代码编辑器。

VS Web 代码(作者截图)

独立于平台

因为 VS web 代码本质上是一个 web 应用程序,它运行在浏览器上。因此,它几乎可以在任何装有网络浏览器的智能设备上运行。在桌面打开 app 后,在手机上试了一下,如下图。还不算太糟,对吧?

VS 手机上的代码 Web(Chrome)

我尝试的下一个设备当然是 iPad。我没有感觉到和桌面浏览器有什么区别,因为 iPad 的屏幕并不算小。

VS iPad 上的 Code Web(Chrome)

正如你所想象的,只要你能访问设备上的浏览器,你就可以在任何地方运行 VS 代码。

这是我在 XBOX 上运行 VS 代码时的样子。所以你可以和你的孩子一起写代码——你们每个人都用一个控制器:)

使用 Edge 的 XBOX 上的 VS 代码 Web(作者提供图片)

支持通用编程语言

我没有听说过任何一种语言完全不受 VS 代码的支持。然而,由于 web 的 VS 代码是在浏览器中运行的,它对编程语言的支持受到 web 浏览器能力的限制。然而,正如官方博客关于 VS 代码发布的介绍,有三个类别作为支持级别的函数。

  • 最佳语言:这个类别包括与 web 开发最相关的语言,比如 JSON、HTML 和 CSS。你真的看不出网络版和桌面版有什么区别。

一些 HTML 代码

  • 更好的:你可能知道,TypeScript 和 JavaScript 在 web 浏览器中被大量使用。最近,Python 也加入了这个团队。因此,这三种语言与 web 的 VS 代码有更好的兼容性。在语法突出显示和错误方面,您会有非常好的体验。

一些 JavaScript 代码

  • :这一类适用于大多数语言,比如 C/C++、Java、C#、Go。这种支持仅限于语法着色、括号对高亮显示和其他一些基本功能。

一些 SQL 代码

文件系统访问

虽然是 web app,但不代表你永远不想用电脑上的文件。你还是可以的。当您点按“打开文件夹”时,系统会提示您在电脑上选取一个文件夹。

访问文件的授权

一旦你选择了你的文件夹,Chrome 会要求你授权访问你的文件系统。

当您想要尝试向文件系统添加新文件时,可能会有其他提示询问您是否将更改保存到文件系统。

扩展ˌ扩张

在访问我的本地文件后,我向文件夹添加了一个 Python 脚本文件。正如您在桌面应用程序中体验到的,系统会提示您安装推荐的 Python 扩展。

VS 网页代码,扩展提示

毫不奇怪,推荐的扩展是 VS 代码的官方 Python 扩展。然而,它也给出了一个警告,VS 代码的功能可能会受到限制。反正我“反正装”。

Python 扩展

安装 Python 扩展后,我得到了 Python 脚本所需的自动完成提示,如下所示。多好啊!

安装扩展后的 Python 编码

当然,您可以尝试许多其他扩展。然而,许多扩展还不能用于 web 版本。相信很多开发者都会更新自己的插件,为网页版提供更多支持。让我们拭目以待吧。

GitHub 集成

对于我们大多数人来说,我们使用 GitHub 来托管源代码控制的代码。它也集成到了 web 的 VS 代码中。当你点击“打开远程存储库”时,会提示你访问你的 GitHub 账户。

GitHub 的授权

一旦您授权,您将看到您的存储库被列出,您可以选择打开一个您想要的。是不是超级方便?

您的设置同步

如果您想要同步您的设置,您可以登录到您的帐户,以便您的 web 应用程序的设置可以与您的桌面版本同步。因此,你在桌面应用上所做的任何设置,也可以在你的网络应用上使用。

合并设置

结论

在本文中,我们将了解 VS web 代码是如何工作的。本质上,它有代码智能、GitHub 集成等最基本的元素,供你编码。最大的优势是几乎可以在任何可以访问 web 的地方编写代码。

当然,它有一些限制,毕竟它是在网络浏览器上运行的,因此它没有桌面应用程序所提供的全部功能。尽管如此,你可以试着使用它,只是为了好玩!

感谢阅读这篇文章。通过注册我的简讯保持联系。还不是中等会员?通过使用我的会员链接支持我的写作(对你没有额外的费用,但是你的一部分会费作为奖励由 Medium 重新分配给我)。

在她们所在的地方遇见她们

原文:https://towardsdatascience.com/meeting-women-where-they-are-3735b0bd362d?source=collection_archive---------29-----------------------

公平和偏见

利用纽约地铁十字转门数据增加科技产品的代表性

根据数据科学训练营 METIS 的提示,我最近接受了一项挑战,帮助一家科技机构的女性增加曝光率和影响力,在她们即将到来的晚会之前,将她们的员工团队安排在纽约地铁站收集电子邮件。目标车站、时间和原因将主要通过使用 MTA 十字转门数据来确定,这些数据是多年来纽约市每个十字转门的骑车人活动的详细记录。

我的方法是依靠以前的研究作为我分析的基础,使用卡罗琳·克里亚多·佩雷斯 2019 年出版的《看不见的女人:为男人设计的世界中的数据偏差》一书的论点。

背景

在《看不见的女人》中,Criado Perez 将城市旅行描述为一种性别化的体验。虽然许多交通系统主要关注早上去市中心、晚上回家的典型通勤者,但“女性的出行模式往往更复杂。”由于女性完成了世界上 75%的无偿护理工作,她们的城市旅行往往包括连续完成多项差事,也称为“旅行链”在一次通勤中,送孩子上学、买杂货和看望年迈的父母可能是最方便的。有了这些知识,我决定将分析的重点放在确定离学校和托儿所很近的纽约地铁站上,以便在这些地方遇到女性。

数据集

这个项目的主要数据源是 MTA 十字转门数据,它是由单个十字转门构成的,每一行代表每 4 小时一次的十字转门进出记录。我使用了这个数据集的一个子集,范围从 2021 年 1 月到 9 月,以反映 COVID 期间的最近活动,以及学年中的一系列月份。这个数据集需要大量的清理,包括考虑不规则的负值、不切实际的高进入/退出数和重复值。为了更细致地分析十字转门数据,我将车站名称与它们各自的线路名称结合起来,因为纽约市碰巧有多个同名的车站。

此外,为了测量车站、学校和托儿所之间的距离,我还包括了多个带有地理数据的数据集。车站位置、学校位置和儿童保育位置数据都是从纽约市的各种来源收集的,并提供了经纬度坐标用于地理比较。

分析

在这个分析中,我将“乘客量”定义为给定时间段内特定站点的乘客出口数量。我开始试图通过观察一个单一的车站来理解纽约地铁系统中常见的乘客模式,第一大道:

第一大道站遵循周末低客流量的模式。图片作者。

知道每隔一个星期天有一个 x 轴标签,我们可以看到在 9 个月的数据中,第一大道遵循工作日高客流量的一致趋势,周末有所下降。但这只是一个车站——这种趋势会推广到更广泛的地铁系统吗?

整个纽约市的地铁系统都遵循工作日乘客量高、周末乘客量低的趋势。图片作者。

当我绘制了 9 个月以来所有可用车站的图表时(如上),很明显,整个纽约地铁系统都遵循着我们在第一大道看到的相同趋势——这些车站在周末都不太受欢迎。我们还可以通过绘制 9 个月期间每周各天的乘客图表来更清楚地了解这一趋势:

周六和周日的总载客量和平均载客量低于一周中的其他时间。图片作者。

鉴于我计划利用学校的活动进行分析,并且周末的客流量较低,从这一点开始,我将周六和周日从数据集中删除。此外,按照组织的要求,我放弃了所有客流量低于 50%的电视台,以优先考虑曝光率和覆盖范围。

接下来,是时候找到车站离学校和儿童保育设施的距离了。将此数据与十字转门数据关联起来的最佳方式是合并站名和站线的数据集,这是创建唯一车站标识符的要素组合。这不可能干净利落地完成,所以我使用了一个组合的 fuzzywuzzy 字符串匹配(一个比较字符串并返回一个衡量其‘相同’程度的比率的包)和手动匹配,以便组合数据集。我成功地匹配了 215 个站名,这给了我们足够的选项来分析该组织的街道团队。

使用 geopy 的距离函数来计算两个坐标之间的英里距离,我找到了每个车站到每个学校&日托的距离。我将“附近”定义为鸟儿飞行时 0.3 英里以内,这在城市街区很容易行走,并将数据帧过滤为仅“附近”位置的组合。我发现,大多数车站附近的学校和日托所不到 20 个,但有几个超过 80 个,这对我们的团队来说是非常有价值的车站位置:

大多数车站在 0.3 英里范围内只有不到 20 所学校和托儿所,但少数车站有超过 80 所。图片作者。

然而,乘客数量仍然是这次讨论中的一个重要因素——如果在选定的车站没有活动,靠近学校和托儿所就变得不那么重要了。为了记住这两个变量,我们可以根据乘客数量和附近设施数量绘制所有车站:

大多数车站的乘客相对较少,附近的学校设施也很少。图片作者。

使用这个散点图将分析范围缩小到我们的顶级站点,我将重点放在两个主要组上。第一个是“理想”车站,它具有较高的日客流量(> 5000)和较高的设施数量(> 20)。第二组是设施数量非常高(> 80)的车站,即使它们的日客流量相对较低(> 3500),因为这些车站似乎是一个非常有价值的地方,仅在当地学校环境中存在。使用 geopandas ,一个允许你在 python 中操作地理空间数据的包,我在 NYC 的 shapefile 上绘制了最后 12 个顶级站点。你可以看到下面红色的车站,黄色的是附近的学校和托儿所。分别是:125 St-456,145 St-ABCD,Delancey/Essex-FJMZ,Grand St-BD,3 Av-149 St-25,103 St-6,137 St City Col-1,181 St-A,116 St-6,167 St-4,170 St-4,Tremont Av-BD。

这张地图由 Geopandas 制作而成,用红色显示了 12 个被选中的站点,周围的学校和托儿所用黄色标出。图片作者。

在优化女性在技术组织的员工团队的时间表的最后努力中,我查看了这 12 个站点的每小时活动。在人手有限的情况下,重要的是我们要把时间集中在每个车站最繁忙的时段,而在乘客较少的时候转移到其他地方。如果我们只关注每天早上 8 点到晚上 8 点的合理时段,我们可以看到每个车站的活动时起时伏:

早上 8 点到晚上 8 点之间,许多车站的乘客量时高时低。图片作者。

了解了这一点,我们就能够为团队制定一个时间表,在我们的团队在那里的时候最大化车站活动。在上学高峰期,我们可以将注意力转移到附近学校设施高度集中的车站。在一天中改变优先站点将使团队的投资回报率最大化。

这些建议

组织应将学校接送时间(上午 8 点到 9 点)集中在附近学校和托儿所高度集中的车站(167 街、170 街、特雷蒙特街和 145 街)。在街道地铁入口部署团队将利用可能与地铁无关的当地学校行人交通。在上学期间或下班后的休息时间,他们可以关注那些时间段内客流量高的车站,以利用白天的所有时间。团队的潜在时间表如下所示:

图片作者。

在这些站点工作几周后,我们可以使用收集到的数据来重新评估我们的策略。进一步改进我们方法的未来工作可能包括纳入纽约市人口统计数据、社区和技术学院位置数据,或者更密切地关注客户团队的独特限制和偏好,以便切实满足他们的需求。

感谢Maxen Haveles对本文的反馈。要深入挖掘代码,可以随意探索我的 项目回购 Jupyter 笔记本

参考资料:C. Criado-Perez。《隐形女性:在为男性设计的世界中揭露数据偏见》(2019)

用 NLP 熔化面

原文:https://towardsdatascience.com/melting-faces-with-nlp-3d21031fba8c?source=collection_archive---------41-----------------------

用 python 发现 90 年代垃圾音乐中的主题

作者图片,灵感来自 Jeff Kravitz 的摄影作品。

活着是多么美好的时光

90 年代给我们带来了一些致命的东西。这是 Python 和 R 蓬勃发展的十年!90 年代也给了我们谷歌、Windows 95 和第一条短信。另一方面,堆栈溢出直到 2008 年才被发现,所以我不确定人们实际上是如何使用 Python 或 r 的。

我想那时候你真的必须很聪明才能编程,但我那时还只是个青少年。一个不关心成人的事业、发展技能或写关于事业和发展技能的文章的青少年。然而,我在 90 年代确实关心一件事:摇滚。

这十年给我们带来了涅槃乐队、碎南瓜乐队乐队、戴着镣铐的爱丽丝乐队、石神庙舞女乐队、声音花园乐队乐队、珍珠果酱乐队,以及许许多多其他伟大的乐队。但是柯特·科本警告我们:

"He's the one who likes all our pretty songs
But he knows not what it means"

接受挑战。

这个计划

在我们试图理解 90 年代垃圾音乐运动的意义之前,我们需要稍微放慢节奏。当我们思考单词的含义时,这被称为情感。在我们开始从单词、短语、句子、歌曲和专辑中寻找情感之前,还有几个步骤。我打算这是一个多部分的系列,每次深入到 NLP(自然语言处理)的不同方面。

不要绝望!NLP 的早期步骤还可以向我们展示文本中的有趣主题,这些主题可能对人眼(或者在音乐的情况下,对人耳)是裸露的。我们阅读或听音乐的单一经历是时间的小快照,当我们的注意力集中在当前时刻时,之前的时刻很快在我们的记忆中模糊了。NLP 让我们能够一次看到所有这些模糊的时刻,将它们聚焦,并使它们相对于彼此有一个视角。

为了生成和捕捉这些主题,我们将使用短语频率分析。事情是这样的:

  • 导入数据。这将是 6 首伟大的 90 年代垃圾专辑的歌词
  • 把这些文本块分解成单词列表
  • 删除我们不关心的单词
  • 把这些单词组成三个单词的短语
  • 统计每个短语出现的频率
  • 在一些看起来很酷的单词云中可视化这个分析,看看哪些主题上升到顶部

PS:坚持到最后,我会告诉你我是如何在文章顶部制作出那个恶心的柯特·科本吉他云图像的!

入门指南

这里是 Github 上完整项目的链接。它包括文件夹、代码和完成本教程所需的一切。

开始了。首先创建一个名为“短语分析”的文件夹。这是所有事情的发展方向。我更喜欢使用 Pythons 的 Anaconda 发行版,Spyder 是我的 IDE,但是你可以使用任何你觉得舒服的东西。

去这个项目的 GitHub 下载数据 链接 。这个项目的数据是一个只有 3 行的 excel 文件。这个电子表格有两栏,乐队名称和歌词。两张专辑的歌词被塞进了每个单元格,以使这变得容易。所有这些数据都是从 LyricsOnDemand.com 的下载的,里面有整张专辑的歌词。这些是我们将要分析的专辑:

  • 涅槃:没关系&在子宫里
  • 砸碎南瓜:暹罗梦&梅隆牧羊犬和无限悲伤
  • 珍珠果酱:十对五

数据看起来是这样的:

作者图片

在我们开始编码之前,我们需要安装一些软件包。这些是你想要的。

其中许多都带有蟒蛇。然而,您可能需要摆弄其中的几个来安装它们。只需一行一行地运行这些代码。在 Spyder 中,您可以突出显示一行代码,然后按 F9 键只运行突出显示的部分。其他 ide 可能称之为“运行选择”或“运行当前行”。监控控制台的错误,谷歌任何红色。我们没有 1998 年的谷歌,我们有这个漂亮、尖牙利齿的 2021 年的谷歌,所以你的 python 问题的答案总是只需要几次搜索…

接下来,我们有一点我不能为你写的代码。还记得我们创建的名为“短语分析”的文件夹吗?让我们将工作目录指向该文件夹。只需将“PathToTheFolderYouCreated”替换为您计算机上的路径,您猜对了。

如果您的名字是 Jessie,并且您在“我的文档”中创建了文件夹:“C:\ \ Users \ \ Jessie \ \ Documents \ \ Phrase Analysis”

我们为什么要设置工作目录?它让我们对这个项目中的其余代码使用相对路径,并减少了多余的文件路径类型,产生了看起来更整洁的代码。现在,要访问一个文件,你只需键入“file.txt”,而不必硬编码“C:\ \ Users \ \ Jessie \ \ Documents \ \ Phrase Analysis \ \ file . txt”。

Loopapaloza 的后台通行证

接下来,我们将获取数据。让我们将这些数据存储到一个名为 dfAlbums 的熊猫数据帧中。很快我们将遍历 dfAlbums 的行(只有 3 行),并对每个文本块执行 NLP。

我们还将创建 dfNgramFrequency 来存储循环的结果。现在,这一行代码用列 Band、Phrase 和 Frequency 定义了一个空的数据帧。这将是我们进行频率分析的地方。我在循环之外定义了这个 home,这样就不会被它覆盖了!

现在是写一大块代码的时候了!你准备好了吗?大块的代码会让你焦虑到 11 吗?这很好,因为我将要解释每一行的作用。

for index, row in dfAlbums.iterrows():

这就是循环。它在说:为我们的乐队和他们的歌词的数据框架中的每一行,做一些事情。这意味着每个循环是一个乐队!涅槃第一,然后砸南瓜,然后珍珠酱。我们会把这个全明星阵容叫做 Loopapaloza,它会卖光的。

band = dfAlbums.loc[dfAlbums['band'] == row[0]]['band'].item()
lyrics = dfAlbums.loc[dfAlbums['band'] == row[0]]['lyrics'].item()

这将获取当前的循环乐队和歌词,并将它们存储到一个变量中。一旦有了熊猫,这就更容易解析了,这样我们就可以继续与 vairbles 合作了。代码越少越好。

这里有一些稍微有趣的事情

  • 。loc:这有助于我们通过选择一列来访问 dfAlbums 数据帧的内容。在我们的例子中,我们通过波段栏来限制它。
  • 。item():允许我们将单个 dataframe 单元格中的数据存储为字符串变量。没有它,熊猫会把信息存储成一个系列,这不是我们想要的
lyrics = re.sub(r'[^\w\s]', '', lyrics) 

首先,我们把歌词去掉所有标点符号。标点符号对短语频率分析真的没有任何帮助,它只是噪音。我使用一行正则表达式去掉了标点符号。不要相信它那么容易?在控制台中键入以下内容,然后按 enter:

re.sub(r'[^\w\s]', '', "Hey! Wait! I've gotta backpropogate.")

作者图片

stopWords = set(stopwords.words('english')) 

接下来,我们创建一组名为“停用词”的单词。在 NLP 中,停用词是字面上的词,你停在那里。它的意思是不要使用它们。一般来说,这些单词没有意义,或者对句子的情感没有贡献。在控制台中键入“stopWords”来打开它,并查看其中的单词类型:

现在我不是在这里这样做,但是将你自己的停用词添加到列表中是很常见的。你可以这样做来去除数据中的异常值,并添加对发现情绪没有价值的公司专用术语。如果我们想将单词“cheese”添加到这个列表中,我们可以键入:

stopWords.add("cheese")

奶酪现在不见了。

wordTokens = word_tokenize(lyrics)

接下来,我们将打破我们的大块歌词文本,并把它变成一个单词列表。这个过程被称为标记化。

sentenceNoStopwords = [w for w in wordTokens if not w.lower() in stopWords]

最后,我们从歌词列表中删除停用词。这存储在“句子注释”中。这是一个比较两个列表的简单循环,实际上是在说“对于单词令牌中的每个单词,确保它不在停用词中”。

listOGrams = []
n = 3gramGenerator = ngrams(sentenceNoStopwords, n)    
for grams in gramGenerator:
      listOGrams.append(grams[0] + ' ' + grams[1] + ' ' + grams[2])

现在我们来看看好东西!首先,我们定义一个列表来存储我们的短语,我们循环遍历我们的单词列表(sentenceNoStopwords)并将所有三个单词短语存储到列表中。当你在 NLP 中看到“gram”时,把它想象成一个文本单位,通常是一个单词。我们的 n 设置为 3,因此 n 元语法是 3 个单词短语,也称为“三元语法”。

如果我们将 n 设置为 2,它将是两个单词短语,也称为“二元模型”。如果我们将它设置为 1,它将是单个单词,也称为“字母组合”。最后,如果我们把它设置为-1,它会让时间倒流,让整个宇宙及其所有物质冲向大爆炸的奇点,我们称之为天启图。

实际上,python 可能会崩溃。

df = pd.DataFrame(listOGrams, columns = ['Phrase'])

最后,我们把这个列表图转换成熊猫的数据图。这个数据帧只有一个名为“短语”的列。坚持住,我们快到了!!!

df = df.groupby(['Phrase']).size()
df = df.to_frame()
df['Phrase'] = df.index
df.reset_index(drop=True, inplace=True)

这些系做我们与熊猫的聚合。实际上,第一行是聚合,计算每个短语在文本中出现的次数。其余的行只是你必须对熊猫做的愚蠢的事情,以一个干净的数据帧结束。

当你聚集在熊猫中时,它喜欢把你的分组(在我们的例子中是短语)发送到索引中。我们只需要在完成计数后将事情打乱,比如将短语从索引移回一列,然后重新设置索引,这样看起来就不会太草率。

df = df.rename(columns = {0: "Frequency"})
df = df.sort_values(by = 'Frequency', ascending = False )    

df['Band'] = band

我们将聚合列重命名为“Frequency ”,然后按降序对该值进行排序。我们还创建了一个名为“band”的列,其中填充了很久以前创建的 band 变量。现在,我们已经拥有了填充最终表格所需的一切。

dfNgramFrequency = dfNgramFrequency.append(df, ignore_index = True)

这会将我们的聚合插入到 dfNgramFrequency 中,它是在我们的循环之外定义的。看,这并不难,是吗?

可视化结果

我们终于可以看到我们的短语频率分析的结果了。但是在我们开始之前,让我们在控制台中键入下面一行,来看一下我们的新数据:

dfNgramFrequency

作者图片

这里我们有一个数据框架,有 3 列,带,短语和短语出现的频率。短语和频率恰好是非常棒的 stylecloud 库接受的两种格式之一。

Stylecloud 是一个获取数据并生成一些好看的单词云的包。有两种方法可以将数据传递到 stylecloud 中。

  1. 将原始文本传递到库中
  2. 将包含短语和频率的 csv 传递到库中

为什么我不选择第一个选项?Styleclouds 确实有自己的停用词函数,我们可以直接使用它。记住这是自然语言处理系列文章的第一篇。我喜欢在执行 NLP 时完全控制我的数据发生了什么,我觉得 pythons 优秀的 NLTK 库(这就是我们使用的)是这项工作的正确工具。

唯一令人失望的是,我们无法将数据帧直接传递给 stylecloud,尽管我尽了最大努力来破解它,它还是想要一个 csv(我说的最大努力是指我尝试了大约 30 秒后放弃)。作为一种变通方法,我只是将数据写入我们工作目录中的 csv,并让 stylecloud 从中读取。

stylecloud 库的另一个好处是我们可以将云做成字体图标的形状。这些都是网页和应用程序开发中常用的漂亮图标。如果你碰巧是硅谷的数据科学家之一,你的 FANG 股票期权已经被抛到了月球上,你可以每年花 99 美元解锁所有图标。

对于我们这些预算有限做数据科学的人来说,免费的就足够了。说够了,是时候变得时髦了!

开场表演:涅槃

请允许我介绍我们在 Loopapaloza 的开场表演:涅槃:

让我们简单回顾一下这段代码在做什么。第一行获取我们的 dfNgramFrequency 数据帧,将它限制在只有波段是 Nirvana 的行。然后,我们将这些内容写入短语分析目录中的 csv 文件。然后 Stylecloud 从这个文件中读取数据来生成云。“icon_name”参数决定使用字体 Awesome 中的哪个图标。你可以点击他们网站上的任何图标来复制这个标识符。最后,我们显示图像。

结果是:

你现在看到的是“Nevermind”和“In 子宫”的短语频率分析,它们被塑造成有史以来最具表情的编程语言:Python。

看着云,你会注意到出现频率最高的三元模型一定是“说了说了”。只是,这有问题;柯特·科本从来没有说过这句话…

这来自于歌曲“品种”的歌词,它重复了两个词短语“她说,她说,她说”一遍又一遍。但是为什么我们的分析说‘说了说了说了’而不是‘她说了她’或者‘说了她说了’呢?停用词!

停用词主要包含代词(如“我”、“她”和“他”)。我其实不认为这是世界末日。移除停用词有助于我们减少数据集中的噪音,并有助于识别主题。主题不一定是艺术家口中说出的顺序。主题不止于此。如果你从我们的项目中删除停用词代码,词云会变得不那么有趣,并且被没有太多实际意义的短语覆盖。

但是,如果您想将' said said '视为异常值,只需将这个单词添加到我们的停用词列表中,然后重新运行代码。

stopWords = set(stopwords.words('english'))
stopWords.add("said")

作者图片

在那里,它不见了。现在,你可能注意到的另一件事是,歌曲副歌中的单词比歌词中的单词更突出(例如,歌曲“心形盒子”副歌中的“永远的债务无价”)。这是因为在典型的歌曲结构中,合唱是重复多次的,而韵文往往是唯一的。我将把这作为家庭作业留给读者(顺便说一句,就是你),你可以冗长地删除第一首歌之后出现的所有合唱(每首歌一个合唱)。

结果可能很有趣,但我不认为这将有助于我们更好地找到意义或主题。仔细想想这个问题,我认为艺术家为副歌选择这些词是因为它们是歌曲中最重要的部分,所以它们应该比独唱部分更流行。

当我看着这个风格云时,我看到了关于逃离、快乐、精神崩溃、枪支和抱怨的主题。你看到了什么?

第二幕:碎南瓜乐队!

Nirvanas 一组结束了!现在是 Loopapaloza 的第二幕:

结果是:

以下是《暹罗之梦》和《梅隆·柯利和无限悲伤》的歌词,它们被压缩成了最难懂的编程语言:r。

我们可以看到围绕着爱、死亡、自由、归属、信任和退缩的清晰主题。比利·考根变得深沉了。

头条新闻:珍珠果酱

最后我们在 Loopapaloza 有了最后的表演:

结果是:

我不知道你怎么想,但是我不戴眼镜就没法做数据科学。戴眼镜可能是你能做的最酷的事情了。Pearl Jams 'Ten '和' Vs '专辑中的歌词显示了荣耀、活着、儿童和弹丸枪等主题。

安可!

艺术和数据联手。

现在你可能想知道我是如何在文章顶部制作出那个很酷的图片的?我用 Python,Photoshop 和一点艺术技巧做了这个。有些人可能会说 Photoshop 在数据科学中没有一席之地,但我不同意。

我相信,作为数据科学家,我们的工作就是抓住观众的注意力和想象力。当你将艺术和数据融合在一起时,你的作品可以在我们每天面对的条形图海洋中脱颖而出。虽然柯特·科本文字云可能不会比条形图更好地讲述一个故事,但它具有新颖性。作为一种生存机制,人类的大脑天生就能记住并专注于从未见过的新奇事物。让我们挖掘人性的这一弱点,并利用它为我们所用!

你可以通过 4 个简单的步骤制作这张图片!

  1. 购买数百美元的数字艺术设备,如绘图板和 Photoshop 订阅。
  2. 擅长数字艺术。为图像画一堆东西。
  3. 将您的作品用作文字云的剪贴蒙版。
  4. 用 Photoshop 把它们混合在一起。

看容易!

为什么我选择了柯特·科本?有一些子宫内时代的令人敬畏的图像和摄影。有一些很酷的照片和视频,库尔特在真人大小的子宫内专辑艺术人体模型前表演歌曲,让它看起来好像他有天使的翅膀。杰夫·克拉维茨拍下了这些精彩瞬间的照片,你应该看看他的作品这里!

下面是如何做到这一点:

第一步:画一个左手挡泥板野马。

图片作者。

第二步:画一些翅膀

图片作者。

第三步:绘制柯特·科本的风格图。细节在这里并不重要。我们希望我们的目光被吸引到吉他和翅膀上。

图片由作者提供,灵感来自杰夫·克拉维茨和柯特·科本的摄影作品。

第四步:Python!

接下来,我们需要用文字填充翅膀和吉他。

这里我用的是 wordcloud 库,而不是 stylecloud。原因是因为 wordcloud 允许你在蒙版内部生成 word cloud。什么是口罩?你拿一张涂黑的图片,单词云会填充它的形状。在数字艺术中,这有时被称为使用“剪贴蒙版”。假设你在短语分析文件夹里有上面的图片(它们在 Github 项目!).

我也想把文字设置得和这篇文章的标题一样。

Melting Faces with NLP
Finding themes in 90s grunge with python
By: Kendon Darlington

你会注意到,在我的代码中,我多次重复这些单词,以使它们在云中变得更大。我也只是列出一堆数据科学术语,每次一个,这将使那些单词变得非常小,并作为我文章标题下的数据科学的基础。最后,我确保重复我的名字最多,所以它是最大的单词,因为我是一个自我吸收的俄勒冈小径千禧一代,并在关注中茁壮成长。

新吉他:

图片作者。

新翅膀:

图片作者。

现在这些图片本身就足够酷了。但是要真正让它流行起来,我们需要把它结合在一起。你需要在 Photoshop 中对所有的图像进行分层,重新绘制吉他和翅膀的轮廓,使它们更加平滑,并添加一些效果,比如从他嘴里发出的粉红色声波。我希望我在这里展示所有这些步骤,但是这将是一个 photoshop 教程!

最终结果是:

作者图片,灵感来自 Jeff Kravitz 的摄影作品。

在 Github 上下载完整的项目。链接。

Python 中带区间的记忆

原文:https://towardsdatascience.com/memoization-with-intervals-in-python-887ece304278?source=collection_archive---------28-----------------------

应用于更快的时间序列处理

作者图片

动机

数据科学家面临的一个常见任务类型包括
处理不断出现的信息。一个典型的例子是
监控在交易台执行的交易或复杂系统生成的日志
的后处理。

如果您发现自己一天中有很多次要等上三到五分钟,等待统计数据在您的完整数据集上运行,那么您可能会想到,经常会有相当多的时间花在重复的任务上,比如加载数据,无论是从数据库、REST 兼容资源还是文件系统。如果您一直以天真的方式进行,您甚至会发现自己处于这样一种情况,即在更新信息和等待更新所花费的时间之间进行权衡,会使您在刷新系统状态时犹豫不决。

更有效地解决这个问题的一个方法是注意,在典型的
监视任务中,要处理的信息是先前处理的
信息和新到达的信息的联合。简而言之,时间向前运行,并且在许多真实的实际情况下,过去的信息是不可变的(或者对于一个人的特定意图来说,相当接近不可变)。监控频率越高,信息的数量越少(假设泊松过程)。因此,这一大类任务最好增量处理。

具体地说,从午夜到当前时刻加载日志相当于将从上次加载
日志到存储的上次检索以来发生的日志列表连接起来。在处理
不可变部分的操作主导实际的最终处理的情况下(与对这些数据执行
聚合相比,从 DB 中检索数据通常是这种情况),这种观点的转变导致了与以前情况相比的彻底的
改进:刷新越多,等待越少。

包含存储冗长的
操作结果而不是再次执行它们的优化技术被称为惰性评估、缓存或记忆化

后者是一个 60 年代中期的美国词语,来源于拉丁语
备忘录(动词 memorari :“被记住”)。用美国公司的行话来说,发表一份备忘录,即要记住的东西,后来被通俗地称为备忘录化 /这个术语在计算机科学中有了自己的生命,用来表示让一个函数记住它自己的结果的行为。这可以帮助不熟悉的读者理解为什么记忆不是拼写错误。

对惰性评估策略的一个不太粗略的描述是,接收相同参数的函数
将计算一次结果,并以某种方式存储该结果。在以后的某个时间提供相同的参数,它将检索存储的结果并返回它。当然,这个描述中的几乎每一个单词都隐藏了许多情况、约束和设计选择。例如,“相同”需要明确定义。“存储”的含义以及在实践中是如何实现的,已经产生了许多不同的可能算法和实现策略。仅在 Python 中,就有几十个包部分或全部致力于这项技术的各个方面。

时间序列的记忆化:比看上去的要多

解决时间序列惰性评估的简单方法如下。给定时间序列的开始和结束,lazy 评估已经请求的最长序列,其开始和结束时间戳包含在间隔[start,end]中,并且实际上只计算或检索这些时间戳之间的差。因此,以某种方式,在每次评估时保存开始和结束时间戳,并将它们与请求的开始和结束时间戳进行比较。

这个简单的任务可以通过直接实现来处理。现在,如果试图处理非连续请求的情况(从两小时前到一小时前和半小时前到 10 分钟前的时间序列已经被处理的情况),问题出人意料地变得对普通人来说是难以处理的:如果比较日期的案例爆炸,那么的数量。人们面临的是这些典型问题中的一个,它比最初看到的要复杂得多。

区间算术的拯救

通常在这种情况下,问题的答案需要在抽象上更进一步
。幸运的是,这里不涉及抽象的步骤,它所需要的概念在文献中有很好的介绍。更重要的是,对这些概念执行基本操作的包是可用的,我们将在本文后面看到。

在许多实际情况下,时间序列可以通过它的开始和结束时间戳来识别。以系统日志为例,生成的所有日志的时间序列由两个时间戳之间的所有日志明确给出。正如前面所建议的,我们不是看两个时间戳,而是想把时间序列看成是由一个单独的时间间隔定义的。这种看似平凡的视角转变最终成为优雅解决问题的关键。

首先,我们将关注一种情况——一种相当常见的情况——我们希望从文件系统中检索两个时间戳之间的所有日志,或者数据库中的所有传入记录,并显示它们的缩略版本。

假设先前已经进行了一些调用来检索不同时间戳的相似数据
。人们应该注意存储这些不同调用的结果
,它们中的每一个都定义了一个原子时间间隔。一个
原子时间间隔是人们想到一个间隔时自发召唤的图像:被考虑的空间/时间的连续部分。区间的这种自然定义可以推广到这种
区间的并集。为了区分这两种类型,前者被指定为原子

假设新的调用定义了新的原子间隔,让我们考虑两个简单的情况:

  • 原子间隔与先前记录的间隔相分离。这意味着用户从未检索过这些数据,因此需要调用底层函数。一旦这样做了,一个适时地存储结果,并记录时间间隔沿着以前的。
    该机制如下图所示。
  • 新的原子间隔包含在先前记录的间隔之一中。在这种情况下,已经存储了所需结果的超集。人们检索存储的结果,然后应用过滤器。这自然假设过滤操作比查询数据更快,这是一个将进一步放宽的约束。下面的三个“完全重叠”图说明了该机制。

在析取情况下请求新的间隔。图片作者。

析取情况下的结果调用。图片作者。

在分离情况下调用后间隔缓存的新状态。图片作者。

在完全重叠的情况下请求新的间隔。图片作者。

完全重叠情况下的结果调用。图片作者。

完全重叠情况下调用后缓存的新状态。图片作者。

到目前为止,很容易。有了前两个例子,一般情况也不会更困难。一般来说,新间隔可以:

  • 与所有以前的案例相分离(一个已经涉及的案例)
  • 或者重叠一个或几个间隔。

为了处理第二种情况,我们只需要记住,如果一个区间与另一个区间重叠:

  • 他们的区别与后者是脱节的
  • 它们的交集包含在后者中
  • 差和交的并集就是整个区间。

下图对此进行了总结:

在原子重叠的情况下请求新的间隔。图片作者。

原子重叠情况下的结果调用。图片作者。

原子重叠情况下调用后缓存的新状态。图片作者。

现在,如果新请求的间隔与几个已经存储的间隔重叠,只需要对每个存储的间隔应用先前的方法。接下来的三幅图说明了这种一般情况:

在重叠情况下请求新的间隔。图片作者。

重叠情况下的结果调用。图片作者。

重叠情况下调用后缓存的新状态。图片作者。

够抽象了!让我们来看一个实际的代码。有几个
包来处理区间和区间算术。一个精心编写的
一个是亚历山大·德坎的 部分 。该软件包处理所有与区间算术相关的必要概念,这是我们利用的一个。

它特别包含了一个方便的IntervalDict,这正是人们所期望的
,并且完全符合间隔记录器
的目的,同时考虑到了函数调用的思想。不过,这里有一个小的微妙之处,需要马上清除掉。乍一看,人们可能会奇怪为什么不使用一个广义的时间间隔来记录呼叫。嗯,区间是一个聪明的对象,它有一些属性,在处理区间算术时特别有用,但在当前的上下文中并不适用。例如,两个相邻的时间间隔将减少到一个原子时间间隔,这在函数的惰性计算环境中相当于忘记两个函数调用并注册一个从未发生的函数调用。

所以事不宜迟,这里是第一个版本的RecordIntervals对象:

RecordIntervals类的一个实例是一个可调用对象,当
通过一个原子间隔时,它将返回一个要调用的列表,而
将更新已经调用的内部列表。

让我们看一个简单的例子。首先,让我们初始化一个空的
记录器,并以区间[-2,0]进行第一次调用(在这些初始的简单例子中,我们将使用整数
区间:我们可以添加一个时间单位(分钟、小时、天)以在时间序列上下文中将它们设置回来)。注意closed部分函数调用,用于创建一个原子间隔,其两端都包含在该间隔中。

itvals = RecordIntervals()
calls = itvals(portion.closed(-2, 0))
print_calls(calls)

输出“[(-2,0)]”告诉我们需要调用带有参数
(-2,0)的函数。因此,如果到目前为止还没有检索到任何数据,并且需要从两个小时到现在的数据,则需要查询 DB(例如)两个小时的数据。

在第一次检索之后,可以立即查询最后一个小时的数据。

calls = itvals(portion.closed(-1, 0))
print_calls(calls)

输出“[(-2,0)]”反映了数据的超集已经被检索和存储的事实。因此,第二次调用该函数将只返回存储的数据(假设该函数已被记忆)。

下一个案例稍微有趣一些:

calls = itvals(portion.closed(-3, -1))
print_calls(calls)

输出“[(-3,-2),(-2,0)]”表示需要发生一个调用,该调用导致从数据库中检索三小时到两小时前存储的数据
,而第二个调用又是对存储结果的调用。那是动作间隔的记忆。我们只查询尚未存储的内容。

最后,下面的代码:

calls = itvals(portion.closed(-5, -4))
print_calls(calls)
calls = itvals(portion.closed(-6, 0))
print("should be broken in 5 intervals: -6->-5 | -5->-4 | -4->-3 | -3->-2 | -2->0")
print_calls(calls)

产量:

[(-5, -4)]
should be broken in 5 intervals: -6->-5 | -5->-4 | -4->-3 | -3->-2 | -2->0
[(-6, -5), (-5, -4), (-4, -3), (-3, -2), (-2, 0)]

好吧。我们现在有一个原始的机制,给定一个时间间隔,生成正确的参数来利用潜在的现有记忆。我们现在需要做的就是将这种原始机制与现有的记忆化实现相结合,以便能够对参数包含表示时间(或其他)间隔的类型的函数进行惰性求值。

不过,在这样做之前,可能有必要对记忆化本身,或者更具体地说,对 Python 中的记忆化做一些提醒。

python 中的记忆化:概述

最常用的内存化实现可能是functools库中的cachelru_cache ( LRU 代表最近最少使用的)。它们足以说明记忆化的许多不同方面。

示意图利用

惰性评估技术可以总结如下:

  • 创建一个调用字典,即
    一组组合的请求参数
    f(1,2,a=True,b='memo') - > key = keymap(1,2,a=True,b='memo ')
  • 在该集合中查找新调用方参数
  • 如果键存在,从档案中检索相关的结果,否则调用函数。

存档可以是:

  • 进程内存储器
  • 进程外存储器
  • 文件
  • 一个组合

cachelru_cache依赖于字典实现,对它们可以处理的键的类型和在 RAM 中存储结果有限制。

特征

许多策略和实现设计都会影响缓存方法的行为和性能。其中包括:

  • 内存空间的管理:例如,是否允许内存空间随着时间无限制地增长?默认情况下,lru_cache将限制缓存空间,并且一旦达到该限制,将开始丢弃最近最少使用的元素,以允许存储新元素。
  • 密钥生成机制:例如,通过字典的默认实现假设密钥是可散列的。

函数和字典

根据官方 Python 文档,它是一个字典查找的包装器。字典是实现记忆标准方法。考虑到本文的其余部分,值得注意的是,字典和 Python(确定性的)函数有很多共同之处。符号通常不同——圆括号和方括号——但两者都将参数映射到返回值。

作为题外话,值得一提的是,在 C++标准库
中,相当于字典的东西被称为映射,并且在许多函数式语言中映射被用来指定一个高阶函数,该函数将一个函数应用于集合中的每个元素。这两个概念似乎又有内在联系。

可用的软件包

我们可以从本质上区分两类用于在 Python 中执行惰性评估的包。

第一类包括 python 客户机或非 python 实现的包装器,最著名的两个是:

  • Memcached
  • 雷迪斯

另一类包含专用的 python 实现,其中包括:

  • 作业库
  • 环 —优秀的文档
  • 记忆化 ](支持不可散列参数、生存时间策略……)
  • 窃部分的悲情系统。

装饰概念和语法

缓存通常作为装饰器提供,因此如果
原始函数是f:

 [@cache](http://twitter.com/cache) 
    def f(i):
      return i+1 
    # the call below is now to a memoized version
    # of the function f
    f(3)

来自@cache的两行代码为函数f添加了惰性求值。

装饰器可以被认为并且实际上被实现为一个函数对象,它接受一个函数并返回一个函数。这意味着装饰器是一个 python 类,它公开了一个__call__成员函数。我们通过注意到上述语法可以重写为:

def f(i):
   # .... 
   g = cache(f)

其中g这次是缓存增强功能。

一个区间感知的记忆实现

想出一个方便的软件组件来记忆取区间参数的函数需要一定量的元编程。我们选择只公开实现的一部分,这部分将揭示函数的实际使用。其他的可以在代码中找到,代码可以作为自由软件获得。

概观

主组件MemoizationWithIntervals是作为一个函数对象提供的,通常被期望用作装饰器。
以下是该组件的示例性用法:

MemoizationWithIntervals构造函数

为了正确处理通用函数,需要由用户指定作为记忆候选的区间参数。

因此,MemoizationWithIntervals构造函数的前两个参数是位置参数的索引和作为区间的关键字参数的名称。

在章节 Overview 的例子中,唯一的间隔参数是interval参数,并且在第一个构造函数参数中被指定。

下面是一个带有一个位置间隔参数和一个关键字间隔参数的示例:

构造函数中的其他参数将在下面的小节中详细介绍。

返回类型问题:指定聚合方法

重要的是要注意,如前所述,任何涉及区间记忆的实现都与标准的惰性评估算法有着根本的不同。

在标准内存化中,除了速度之外,对缓存函数的调用将返回与对原始函数的调用无法区分的结果。特别是,对缓存函数的调用最多对应于对底层函数的一次调用。

前面几节中描述的算法可能涉及到对底层函数的多次调用。也就是说,结果类型不能先验地匹配初始函数的类型。在我们到目前为止给出的描述中,我们已经建议初始函数将返回一个结果列表。因此,间隔缓存函数将返回一个列表列表。我们已经暗示过,将这样一个列表转化为一个列表会涉及到连接和可能的过滤。

尽管不需要如此。为了恰当地解释一般情况,用户需要灵活地指定一个聚合操作。这个聚集规范将是 memoization
类构造函数的一个参数。聚合函数将从不同的调用中获取结果列表,并返回一个类型与初始函数兼容的结果。

因此,这个聚合函数可以简单到aggregation=list
等价地,对于熊猫数据帧,aggregation=pandas.concatenate。在用户希望通过求和来聚集数值的情况下,将应用类似于aggregation=lambda listr: reduce(lambda x,y:x+y,listr)的东西。

记忆算法

本文中描述的包的唯一目的是对采用区间参数的函数进行预处理,以便惰性求值可以委托给用户选择的现有实现。因此,MemoizationWithIntervals对象的构造器接受一个完全构造的 memoization 对象,它将执行惰性求值。

可以看出,我们选择了klepto包作为默认实现。我们在该软件包中发现了引人注目的功能,并专门使用它。用户可能会发现其他实现更适合他们的需要,因此会更改默认设置。

所以通常使用functools cache算法:

处理其他间隔类型

Alexandre Decan 的部分包是一个很棒的区间算术包。但是,对于 interval 对象本身,这可能不是最常见的实现。按理说,Interval的熊猫可以称得上是冠军。但是一个人可能有自己的实现。使用具有特定间隔类型的 CacheIntervals 需要创建一个特定类型的间隔记录器和一点包装,以允许在部分的本地间隔类型和用户的间隔类型之间进行双向转换。

CacheIntervalsPandas 的间隔类型提供了这样一个包装的例子。实施该特定间隔的目的有两个。一方面,它是希望实现该覆盖的用户的模板。另一方面,熊猫Interval型,加上亚历山大·德坎的本土型应该能满足大部分需求。默认情况下,间隔记录器的类型是适应熊猫的间隔。要改变它,指定新的间隔记录器类型作为构造器的参数,例如:

机制概述

这一节更复杂,主要面向包开发人员。特别是,希望区间记忆可以直接嵌入到标准记忆包中,并随时可用。

在这一节中,我们将深入探讨实现细节。更普通的读者可以放心地完全跳过它。

但是,这些解释中并没有假设 Python 的高深知识。因此,如果好奇发痒,请随意继续。

除了构造函数之外,MemoizationWithIntervals类只有一个成员函数:函数__call__。回想一下,正如在
部分装饰概念和语法中解释的那样,MemoizationWithIntervals对象是一个函数,它接受一个函数并返回这个函数的记忆版本。

让我们看看代码:

细节被主动省略,以便结构突出。从这个代码框架可以清楚地看出,__call__函数所做的就是获取函数f并返回函数wrapper,函数本身在__call__函数内部定义。

从这段摘录中可以看出,wrapper函数本身执行对ff_cached的记忆版本的
调用。

前两行设置一个对象来正确地解析调用参数,据我们所知,Python 语言并没有直接提供这个功能。我们将跳过这部分代码,因为它与本文基本无关。

两次利用记忆算法

区间惰性评估的一个直观但不正确的实现是将每个区间参数与其自己的区间记录器对象相关联。要了解为什么它通常不是我们想要的,让我们看一个简单的例子:

假设调用如下:

*print('==== First pass ===')
print( f'Final result:\n{function_with_interval_param(2, interval=pd.Interval(-2, 0))}')
print('==== Second pass ===')
print(f'Final result: {function_with_interval_param(2, interval=pd.Interval(-3, 0))}')
print('==== 3rd pass ===')
print( f'Final result:\n {function_with_interval_param(3, interval=pd.Interval(-3, 0))}')*

输出将是:

*==== First pass ===
((2, [-2,0]))
==== Second pass ===
((2, [-3,-2]), (2, [-2,0]))
==== Third pass ===
((3, [-3,-2]), (3, [-2,0]))*

细心的读者已经注意到第三遍的结果导致了低效率。当需要一个调用时,会生成两个调用。原因是第一个参数的每个值应该有一个记录器。

正确的算法需要以某种方式为每个非区间参数的组合保留一个记录器。人们可以通过例如字典来手动跟踪这些其他参数,但是人们会立即意识到:

  • 这是一项艰巨的任务,因为键映射并不像最初想象的那样简单
  • 这正是记忆算法要解决的问题。

因此,更巧妙的方法是利用用户提供的缓存
算法来实现。这更适合于用户已经潜在地
对他的设置指定了许多约束:使用的内存类型、持久性、内存管理和其他……为此,让我们回忆一下函数和字典部分的评论:字典和函数之间存在概念上的等价。

因此,我们的想法是两次利用缓存的函数实现:

  • 在对
    功能进行标准评估时,将会调用该函数。
  • 但是,除了这种标准用法之外,它还将用于存储适当的间隔记录器,就像我们使用字典一样。

现在的问题是:我们如何区分标准呼叫和对记录器的请求?一种可能的方法是传递用户永远不会传递的参数。如果我们在任何地方检测到这样的参数,我们知道这不是对底层函数的调用,而是对记录器的请求。

这就是下面这个QueryRecorder类的目的。在
MemoizationWithIntervals对象的构造函数中,变量self.query_recorder被初始化为self.query_recorder=QueryRecorder(),并在每次调用时用于解析
记录器。

*所以在下面的f_cached函数中,有一个初步的机制来
确定我们正在处理什么类型的调用。如果这是记录器的请求
😗

  • 第一次执行实际代码并创建记录器
    (由用户指定类型,例如RecorderInterval
    RecorderIntervalPandas或某些用户的专用)。任何RecorderInterval构造函数的特定参数都可以通过在MemoizationWithIntervals对象的构造函数中将它们指定为kwargs来传递。然后它们被存储到self.kwargsrecorder成员变量中。
  • 此后,记录器被缓存并将被重用。

显然,这涉及到一个小的开销。这是否可以接受取决于用户的上下文。实际上,与内存化的好处相比,这种开销在大多数时候是可以忽略的。

下面是wrapper功能代码的相应摘录。可以看出,当调用包装函数时,
wrapper的第一个任务是将每个区间自变量替换为对其对应记录器的请求。当记录器被解析后,wrapper
函数继续执行,从概念上讲,是对缓存函数的正常调用,实际上,是对所述函数的一系列(可能的)调用(此处未显示)。

解决电话问题

从那以后,算法就相对容易了。从每个时间间隔记录器中,检索要发出的呼叫。最后一系列调用只是可能参数值的
笛卡尔乘积,其中非区间参数只是单值。位置参数和关键字参数的分离给代码带来了一点复杂性,但这基本上就是最后一节的全部内容。

一些最后的细节

容忍

假设被查询数据的数据库是一个每 15 分钟更新一次的代理数据库。如果在很短的时间间隔后一个调用成功于前一个调用,则数据不太可能有明显的变化。另一方面,即使检索少量数据或者根本没有新数据,仍然需要通过网络进行交换,并在数据库本身上进行一些操作。当人们想到单个用户交互地发出请求时,这种情况可能看起来有些牵强,但是在多用户环境或更加自动化的设置中,这实际上可能是一种非常常见的情况。

为了防止在快速连续的请求之后的不必要的事务,或者在更一般的情况下,如果新的查询间隔与原始间隔没有显著不同,可以决定,在容限阈值以下,不发出新的呼叫。这种方法在缓存算法中很常见,通常被称为舍入*。*

事实证明,在我们的例子中,实现舍入非常简单。它只需要对RecordIntervals类做一个小小的修改。构造函数现在接受一个rounding参数,并且disjunct成员函数将测试
新请求的区间的边界是否低于阈值,在这种情况下,新的区间不会被添加。

这种外科手术式的改变是我们所需要的。这是如何使用的:

关于聚合的更多信息

现在是时候透露我们在最初的RecordIntervals中隐藏了一个细节,细心的读者可能会反对。当处理一个标准的聚合算法时,例如取一些列的累积值或它们的平均值,我们当前的策略将不会产生正确的结果。原因是在数据的超集上聚集不是我们所期望的。

因此,我们需要另一种算法。这个想法是,对新间隔的请求不能导致具有更宽间隔的呼叫。以下三幅图展示了新策略的表现。

在原子重叠的情况下请求新的间隔。图片作者。

导致调用帐户进行聚合。图片作者。

在聚合策略的情况下,调用后缓存的新状态。图片作者。

这些数字可能会给人一种错觉,认为这种策略不会产生缓存。然而,事实并非如此:对于新请求的时间间隔中包含的所有已存储的时间间隔,都将进行缓存。

相应的算法如下:

结论

CacheIntervals 包可以避免很多容易出错的锅炉板
代码。鉴于其应用程序无处不在的特性,它允许避免每次都重写代码。这不仅导致开发时间的改进,而且,最值得注意的是,在我们的经验中,有利于系统地使用惰性评估,这最终有利于应用程序的最终用户。

作为临别笔记,一个小趣闻。这个算法是几天前从零开始写的。在设计算法的初步版本时,谷歌搜索引擎网站打开了一扇复活节彩蛋之门。它导致了一个斯巴达式的命令行界面和一些允许访问任务声明的类似 Unix 的命令。必须产生一个代码来帮助另一个星球上的奴才们整理一些任务分配。这个复活节彩蛋是一个随机事件还是之前在 Google 引擎上搜索请求的结果,这个问题的解决方案已经给出了答案:它是 Python 中涉及记忆化的三行代码!在规定的时间内成功提交了第一个挑战后,我得到了一张《黑客帝国》( Matrix-the-movie)动画像素化的图像——我适时地在屏幕上保留了几天。在这个阶段,我被提示输入我的证书以接受进一步的挑战,这被我的公司防火墙阻止了(这可能是最好的结果,因为我仍然可以幻想自己被这家著名的软件公司录用)。尽管如此,这种创新的招聘模式还是给我留下了深刻的印象。所以你看,致力于记忆可以带来意想不到的发现…

参考

CacheIntervals 存储库位于 GitHub 上,地址如下:

*https://github.com/cyrilgodart/CacheIntervals

亚历山大·德坎的部分可以在这里找到:

https://github.com/AlexandreDecan/portion

窃贼来了:

https://github.com/uqfoundation/klepto *

Mercari 价格建议挑战

原文:https://towardsdatascience.com/mercari-price-prediction-challenge-3a8ea00a7d33?source=collection_archive---------14-----------------------

给定产品的细节,产品的价格应该是多少?

迈克尔·朗米尔在 Unsplash 上的照片

目录:

  1. 介绍
  2. 商业问题
  3. 先决条件
  4. 数据源
  5. 现有方法
  6. 理解数据
  7. 电子设计自动化(Electronic Design Automation)
  8. 数据预处理
  9. 基准解决方案
  10. 第一次切割溶液
  11. 基于深度学习的解决方案
  12. 结论
  13. 部署和预测
  14. 未来的工作
  15. Github 知识库和 Linkedin
  16. 参考

1.介绍

Mercari 是一家电子商务公司,目前在美国和日本开展业务。这提供了一个平台,顾客可以出售不再有用的物品。它试图通过提供上门取件、当天送达和许多其他优势,使所有流程变得无障碍。该公司的网站显示,每天有超过 35 万件商品在网站上列出,这反映了它在用户中的受欢迎程度。

2.商业问题

这个问题很容易理解,给定产品的细节和产量,产品的价格应该是多少。当我们将这作为机器学习问题时,我们称之为回归问题,因为输出是实数(价格)。

绩效指标:

性能指标是一个函数,用于了解我们的模型在其工作中所处的位置。根据问题的类型和业务需求,有不同类型的度量标准,例如准确性、roc 曲线、均方误差等。我们这里使用的度量是 均方根对数误差

目标:

显然,我们解决这个问题的主要目标是预测一个准确的价格。基于对产品的描述,必须向卖家建议一个真实价格,以维持卖家和买家之间的可信度。

b)由于机器学习模型将作为 web 应用程序运行,预测的时间延迟必须更短。卖家不喜欢等很久才知道结果。从毫秒到几秒的时间范围应该是向销售者显示输出的适当时间。

3。先决条件

在此,我假设读者知道经典机器学习和深度学习、数据预处理、探索性数据分析的基本概念。

4。数据来源

数据集是通过 Kaggle 获取的,其中包含两个文件,一个用于训练,另一个是测试文件。训练文件包含超过 140 万条记录,测试文件包含大约 300 万条记录。文件在里面。tsv 格式,大小分别为 74MB 和 280MB,经过压缩。

5.现有方法

许多数据科学爱好者用各种方法解决了这个问题。比赛获胜者 Paweł和 Konstantin 采用了非常简单的方法,并结合了一些令人惊叹的技巧。他们用稀疏的输入训练了一个简单的 MLP。他们的技巧包括名称 chargrams,用 PorterStemmer 做词干,文本特征的连接。他们使用单词包-1,2-grams(有/没有 Tf-IDF)和一键编码作为分类特征( link )。其他人也尝试了基于 CNN 的模式。

6.理解数据

定型数据集的每一行都代表一个列出的产品,该产品包含有关它的某些信息。每列显示产品的具体细节。总共有以下七列:

1。 名称: 显示所列产品的标题

2。item_condition_id: 卖家提供的物品状况。这是一个有序的分类特征,分类范围从 1 到 5,其中 1 比 5 好。

3。类别名称: 该特性保存列表的类别名称。例如:“女人/运动服装/裤子,紧身衣,打底裤。

***4。*品牌名称:**该功能给出品牌的名称。该要素还包含空值。

5。价格: 这是我们要预测的特征,也就是目标变量。单位为美元($)。

6。shipping: 这是一个分类特征,告诉谁支付运费。如果卖家付款,则为“1”,如果买家付款发货,则为“0”。

7。item_description: 此功能包含物品的完整描述

7.探索性数据分析

这一步是解决机器学习问题的关键步骤之一,因为它提供了探索和理解数据集的机会。这种对数据集的深入了解不仅对理解现有特征很重要,而且对开发新特征以输入到机器学习模型也很重要。现在我们知道为什么要做 EDA 了,让我们开始吧。

基本统计

在 EDA 的这一部分中,我们将尝试理解整个数据集,包括收集关于原始数据(如形状或数据类型)的初始统计数据。

df = pd.read_csv(“train.tsv”,delimiter=”\t”,index_col=[“train_id”])
print(df.shape)
df.info()

的输出。信息()

我们可以清楚地看到数据集有 1482535 行和 7 列。的。info()表示每列的数据类型,而“类别名称”、“品牌名称”和“项目描述”包含一些空值,我们将在数据处理部分处理这些值。

7.1 单变量分析:

简单地说,通过一次获取一个特征(列)来探索数据是单变量分析。这种方法有助于发展对所考虑的特定特征的理解。

7.1 .价格

我们知道这是我们的目标变量。这个特性的数据类型是 float,因此我们可以检查这个变量的统计数据

df.price.describe()

的输出。德塞利贝()

我们可以看到的一件事是,平均值为 26.7 美元,最大值约为 2000 美元,这意味着数据中肯定存在一些偏差。为了证实这一点,我们可以简单地用下面的图表绘制分布图。

价格分布图

正如我们所料,情节严重扭曲。这看起来非常接近于对数正态分布。当分布是对数正态分布时,值的对数分布是高斯分布。这意味着,如果我们对价格取对数,其结果将接近高斯分布,如下所示:

# Note : We are adding 1 to aboid inf value for 0 price products
df["log_price"] = df.price.apply(lambda x:np.log(x+1))
plt.subplot(1,2,2)
plt.hist(df.log_price,bins=30,color="teal")
plt.title("Log(Price+1) Distribution")
plt.xlabel("log(Price+1)")
plt.ylabel("Count")
plt.show()

这种分布接近高斯分布,高斯分布比原始价格有一定的优势。

  • 与对数正态分布不同,高斯分布中的值在所有范围内分布更加均匀,在对数正态分布中,很少有数据点可用于较高的价格值。
  • 这有利于线性回归等模型,线性回归假设因变量呈正态分布。此外,对于其他模型,高斯分布的目标变量也提供了更好的性能。
  • 无论如何,考虑到我们的性能指标,我们必须计算预测价格和实际价格的对数,因此最好将 log(价格+1)视为我们的目标价值,并计算它的 RMSE。

因此,我们将考虑 log(价格+1)作为我们的目标变量。
有些产品定价为 0 美元。我们稍后将对这些产品进行有趣的 EDA。

7.1.b 项目条件标识

Mercari 将的产品状况分为 5 类。

  • 新的
  • 喜欢新的
  • 好的
  • 公平的
  • 贫穷的;贫困的

这些分别表示在从 1 到 5 的数据集中。现在,让我们来看看这些类别中的产品。

我们可以清楚地观察到,对于 item_condition_id,每个类别下的列表数量存在可变性。最大的组件属于类别 1,而类别 5 拥有最小数量的列表。这是有道理的,因为数字越低,产品的质量/状况就越好,没有人想把他们的产品评为状况不佳。

7.1.c 运输:

我们将对运输执行类似的分析

  • 这里也有一个小的类别不平衡。这些特征随价格的变化将是一个有趣的分析,我们将在双变量分析部分看到。

7.1.d“品牌名称”

品牌名称是最初将大约 40 %的值保存为空的特征。我们将把这些空值视为“丢失”,我们将在预处理部分看到。我们还将从列表名称中提取品牌名称,这将未知品牌名称减少到 27%。

  • brand_name 中有将近 4800 个类别。出现次数最多的前 10 个品牌包括 pink、Lularoe、Nike、victorias secret、apple、Nintendo、forever 21、lululemon、Michael kors ,这些其实都是非常著名的品牌名称。出现最少的品牌有警察、快乐袜、pal zileri 等。 其中很少听说过

7.1.e 类别名称

类别名称 是产品所属类别的名称。这是 Mercari 网站特有的。此功能还包含大约 6k 个空值,这些空值是用文本“缺失”输入的。

让我们先来全面了解一下 category_name。

  • category_name 有 1287 个类别,其中大部分产品来自女装和美容产品。
  • 我们可以在 Mercari 网站上看到,类别是分层的,这意味着一个主类别包含子类别等等,或者我们可以说有不同的层级(我们将使用这一术语)。这些由“/”分隔。

截图来自https://www.mercari.com/

我们将在数据预处理中看到如何分离这些类别,但现在,我们可以假设类别分为三层(Tier_1、Tier_2、Tier_3)。

7.1.f Tier_1

在第一级中,共有 10 个类别(未知值被视为缺失)。几乎一半的产品属于“女性”类别,其次是美容和儿童,相比之下比例相当低。

7.1.g '第二层'

在第二层,我们有 100 多个类别。不出所料,在 10 大女装中,美妆产品独占鳌头。最少出现的类别是'被子'。

7.1.h 第三层

在第三层,我们可以观察到 800 多个类别。排名前十的产品包括 t 恤、紧身裤、游戏、鞋子和其他。最少 10 个产品类别包括视频游戏,瓷砖,教育(有趣),清洁,袖口等。

7.1 . I‘名称’

接下来,我们有一个功能名称,它对于大多数产品都是唯一的,因为我们可以看到该功能中有 100k 个类别,但对于某些产品,它是相同的。出现次数最多的一个名称是“bundle”。这可能是为了大量销售的产品

对于文本特征,我们还可以检查字符长度和字数的分布。

  • 大多数卖家倾向于给出超过 20 个字符的名字,因为一些字符的分布是左偏的。
  • 有些卖家给名字的字数特别多,比如超过 12 个

7.1.j '项目 _ 描述'

项目描述是建模的一个重要特征。它保存了卖方提供的关于产品的描述性信息。此功能包含 4 行空值,被估算为“缺失”。

当我们观察此功能中的类别时,发现大约 5%的列表由*【暂无描述】*占据主导地位。这些可能是在网站上注册列表时为该零件提供的默认值。

  • item_description 中的字符数分布描绘了高度倾斜的图。CDF 图显示大约 90%的描述长度小于 400。

7.2 双变量分析

7.2 .项目条件标识与日志价格

这里我们绘制了 item_condition 中每个类别的 log_prices 的分布。

  • 整个情节相当笨拙,但我们可以观察到的一件事是,item_condition_id =5 的 CDF 比其他的低,这是有意义的,因为 5 的条件很差,因此价格也应该很低。

7.2.b 运输与日志价格

在这里,我们分析 log_price 和 shipping 之间的关系,并为每一类 shipping 绘制分布图。

  • 这个特征中有一些信息,因为类别的分布沿着轴移动。
  • 买方支付的 CDF 图向右,显示买方支付运费的价格更高,这是合理的。
  • 箱线图还表明类别 1 的价格低于类别 0 的价格。这是可以理解的,因为“1”表示卖家将支付运费,这使得产品价格更低。

7.2.c 品牌名称与价格

为此,我们在数据集中绘制了前 25 个最昂贵的品牌。

  • 我们可以观察到,最昂贵的品牌价格可以高达 2000 美元。
  • 这也取决于产品类型,因为有一个品牌戒指最有可能出售戒指。这种产品本身就是一种昂贵的产品,与品牌无关。

7.2.d Tier_1 与价格

现在,让我们看看 Tier_1 类别对产品价格的影响。这里我们绘制了 Tier_1 中每个类别的平均价格和中间价格值,从中我们可以观察到平均值高于中间值。这是右偏分布的性质,我们知道产品的价格是高度右偏的。

  • 每个类别的 log_prices 的方框图表示电子产品、女性用品、美容用品、古董收藏品和手工制品的价格范围高于其他产品。

7.2.e 名称与项目条件标识

这是一个有趣的分析,它告诉我们这些名字是如何根据产品的状况而变化的。我们将利用 Wordcloud 进行分析。

  • 服装产品和品牌在所有图片中都可以看到,比如所有类别中都出现的“粉色”。
  • 在 item_conditon_1 中,“新的”一词经常用于名称中,它还包括非常受欢迎的品牌名称,而在 item _ conditon _ 5 中可以看到“坏的”一词,这是有意义的,因为这一类别是针对劣质产品的。
  • 由于没有单独的尺寸特征,所以在名称本身中提到了尺寸。因此,我们可以在几乎所有的单词 clouds 中看到“size”。

7.2.f 项目描述与项目条件标识

对 item_description 进行类似的分析,结果如下:

  • 出现在每个类别中的所有单词都是可以理解的,而且相当真实。比如 item_condition_id =有这样的词全新,从未用过,全新,高品质。
  • 现在,随着 item_condition_id 数量的增加,描述中使用的词语也会发生变化。在 item_condition_id =3 使用的条件中,轻轻使用的是最显著的字,其对于 item_condition_id =5 变为零件、损坏、工作

7.3 零价格产品分析

在本节中,我们将分析价格等于 0 美元的产品。首先让我们看看有多少产品的价格等于零。

zero_price = df[df.price<=0]
print("The shape of the df =",zero_price.shape)

为了了解这些产品属于哪一类别,我们将在功能 Tier_1、Tier_2、Tier_3 的每个类别中绘制产品图。

  • 大多数价格为“0”的产品是服装或美容产品,属于女性类别。

我们还可以计算出每种情况下产品的百分比,并将其与总百分比进行比较。

  • 价格为零的产品在项目条件为 2、3、4、5 的产品中所占的百分比较高。这表明,可能有一些用过的物品,如衣服等,人们只是为了捐赠而免费发放。

8.数据预处理

数据预处理或数据清洗是构建机器学习模型的一个不可或缺的步骤。在这一步中,我们对数据集进行必要的更改,使其适合进一步处理。数据预处理步骤对于我们正在处理的问题或数据集来说是非常具体的。在这里,我们将看到数据清理的特点,特别是这个问题。

8.1 名称

对于文本特征,有许多技术用于数据预处理。这些包括去收缩、降低字符、词条化、记号化、词干化、停用词移除等等。在这里,我们做了非常简单的预处理,即去收缩、去除特殊字符、去除停用词和降低文本。它的代码如下:

#注意,解收缩功能的代码取自上面给出的 Ref

例子:

8.2 品牌名称

因此,对于 brand_name,不仅完成了基本的文本预处理,而且正如我们之前在 EDA 中讨论的那样,某些产品的名称中包含 brand _ name,因此我们必须提取它们。

例子:

现在,在从名称中提取 brand_names 之前,我们有大约 42%的值“丢失”,但是在采用这种方法之后,我们将这一数字减少到了 27%!!

8.3 类别名称

在 catagory_name 中,也遵循相同的方法进行预处理。

例子:

完成后,我们将通过在“/”处进行分割,将数据集分为三层。

例子:

8.4 项目 _ 描述

该特征也是需要处理的文本特征。还有一些空值需要注意。以下函数用于相同的目的。

举例:

在这里,我已经展示了训练数据的预处理,测试数据也必须遵循同样的预处理。

9.基准模型

我们将使用这个模型来比较我们的机器学习模型的性能。我们在这里使用的基准模型是一个简单的平均模型,它根据两个特性“shipping”和“item_conditon”给出平均输出。简而言之,当我们输入 shipping (0 或 1)和 item_conditon_id(1 到 5)时,模型会提取 shipping 和 item_conditon_id 相似的所有数据。它取这些点的平均值并输出结果。

拆分数据

在测试模型之前,让我们将数据集分别以 9:1 的比例分成训练数据和验证数据。

df_train,df_val = train_test_split(df,test_size=0.1,random_state = 3)
print(df_train.shape)
print(df_val.shape)

火车形状

验证形状

当在验证数据上测试基准模型时,它提供 0.7255 的 RMSLE 值,这成为基准。任何度量值高于该值的模型都表现不佳。

10.首次切割方法

对于第一个 cut 解决方案,我尝试了不同的机器学习模型,但机器学习最重要的部分是特征化。我遵循了两种类型的特征化。

10.1 第一种方法

特征工程

在第一种方法中,我们将尝试保持输出向量的维数较低,以便我们可以更快地训练模型。因此,我们将对分类数据使用顺序编码,对文本数据使用平均 Word2Vec。让我们逐一看看:

分类数据

我们知道数据集中的分类数据由 shipping、item_condition_id、brand_name、Tier_1、Tier_2、Tier_3 组成,其中“shipping”和“item_condition_id”已经正常编码。所以对于剩下的特性,我将使用 Sklearn 库的 OrdinalEncoder 功能。

请注意,在验证数据中会有一些未知的类别,我给它们的值是-1。

文本数据

对于文本数据,我将连接 name 和 item_description。这实际上没有任何具体原因,只是为了减少维数。您也可以单独展示它们。我也分别尝试了它们,因为向量是密集的向量,需要更多的时间来训练。

因此,我在第一部分中使用的文本特征矢量化方法是平均 Word2Vec。在这里,我使用 Gensim 库的 Word2Vec 功能,并在连接数据(名称+项目描述)的语料库上训练它。

想法是每个句子在 Word2Vec 模型的帮助下向量化每个单词,然后取所有这些向量的平均值,这些向量将被视为句子的向量。

缺少 10.1.1.3

如果品牌名称或名称或项目描述中缺少一个值,此功能将给出 1,否则为 0。

df_train["is_missing"]  =  (df_train.brand_name_processed=="missing") | (df_train.name_processed =="missing")| (df_train.processed_item_description=="missing")df_train["is_missing"]  = df_train["is_missing"].astype(int)

堆叠所有特征

所有形成的特征水平堆叠在一起。

'''STACKING ALL FEATURES OF TRAIN DATASET'''
x_train = np.hstack((df_train.item_condition_id.values.reshape(-1,1)    ,          df_train.shipping.values.reshape(-1,1) ,\
           df_train.is_missing.values.reshape(-1,1) ,
           train_vec_brand , train_vec_t1 , 
           train_vec_t2,train_vec_t3 , train_vec_text))

建模

a.套索

该模型是线性模型,通过 l1 正则化来减少平方损失。在这里,我为这个模型使用了 Sklearn 库。该模型是在一系列值上调整的超参数,并根据最佳参数进行训练。

该模型在验证数据上的性能为 0.6037,在 Kaggle(公共分数)上的测试数据上的性能为 0.60476。

b.山脉

脊模型也是线性模型,其减少了平方损失,但是具有 l2 正则化。

该模型的 RMSLE 在验证数据上是 0.6038,在测试数据 Kaggle 上是 0.6048。

c.决策图表

顾名思义,决策树使用基于树的模型来预测输出。我这里用的是 Sklearn 的决策树模型。

验证数据的 RMSLE 为 0.6353,测试数据的 RMSLE 为 0.63648。显然,与早期的模型相比,决策树的性能并不好。

我也尝试了随机森林的这些功能,但它的训练时间非常长,所以不得不停下来。如果感兴趣的话,你可以在 Github 知识库上看到完整的代码。

d.轻型 GBM

集成模型的轻 GBM 模型功能和训练更快,因此它适用于问题。

该模型在验证数据上的性能是 0.5008,在测试数据上的性能是 0.50049(私人 Kaggle 分数)。

10.2 第二种方法

特征工程

在第二种方法中,我们将对分类数据使用一种热编码,对文本数据使用 Tfidf。

10.2.1.1 分类数据(一个热编码)

对于包括运输在内的所有分类数据,项目条件 id、Tier1、Tier2、Tier3 是一个热编码。下面的代码用于实现这一点:

文本数据(Tfidf 矢量器)

Tfidf 声明“术语频率-逆文档频率”。它由两部分组成,第一部分是“词频”,它是一个单词与句子中单词总数的简单比率,它为句子中出现得越多的单词赋予越多的价值。“逆文档频率”是第二部分,它是总文档数与出现错误的文档数之比。IDF 对文档中较罕见的单词给予较高的价值。

我使用二元模型范围到 2,最大特征到 50000。

缺少 10.2.1.3

如果品牌名称或名称或项目描述中缺少一个值,此功能将给出 1,否则为 0。

df_train["is_missing"]  =  (df_train.brand_name_processed=="missing") | (df_train.name_processed =="missing")| (df_train.processed_item_description=="missing")df_train["is_missing"]  = df_train["is_missing"].astype(int)

堆叠

类似地,所有特征水平堆叠在一起,形成一个产品的一个向量。

'''STACKING ALL THE FEATURES'''# STACKING TRAIN FEATURES
x_train = hstack((train_vec_item_con,train_vec_shipping,
                  train_vec_name,train_vec_brand,
                  train_vec_t1,
                  train_vec_t2,
                  train_vec_t3,
                  df_train.is_missing.values.reshape(-1,1)
                   ,train_vec_desc))

型号

a.线性回归

这种线性回归的实现减少了平方损失。这里我用的是 Sklearn 的线性回归实现。

'''TRAINING LINEAR REGERSSION'''lr = LinearRegression(normalize=True)
lr.fit(x_train,y_train)

这个简单的模型为验证数据提供了 0.4620 的性能指标,为测试数据提供了 0.4621 的性能指标(Kaggle 私人评分)

b.岭模型

具有一个热内化和 tfidf 特征的脊模型是在各种参数上调谐的超参数。代码部分将保持与上面讨论的相同。

通过对最佳参数的训练,我们在测试数据(Kaggle Private Score)上得到了 0.4581 和 0.45831 的验证 RMSLE。

观察结果:

  • 到目前为止,我们拥有的最佳模型是具有一个热编码和 TFIDF 特征的脊模型。
  • 对于标签编码和 word2vec 功能,像 Light GBM 这样的复杂模型比像 lasso、ridge 这样的简单线性模型表现得更好。
  • 但是对于 Tdidf 特征,这些特征变得相对较高,因此线性模型给出了相当不错的性能度量值
  • 为了更好的性能,我们不得不尝试深度学习模型。

11.基于深度学习的解决方案

深度学习模型在网络的输入和输出层之间使用多个层,这使得它成为解决问题的强大方法。此外,它与人脑的相似性使这一想法的运作具体化了。

这里我们将使用 RNN 类型的 DL 模型。RNN 代表递归神经网络,包括基于 LSTM 或 GRU 的模型。RNN 模型用于文本数据(名称和商品描述)中具有长序列的数据。因此,你可以看到建筑 GRU 被用于这些功能。

11.1 标记和填充

在我们向网络提供数据之前,必须对其进行矢量化处理。Keras 为矢量化数据提供了嵌入层,但它需要特定格式的输入。为此,我们首先对文本数据进行了标记化。通过标记化,我们将把文本数据转换成数字,这些数字只不过是词汇中单词的索引值,而词汇是通过拟合训练数据来学习的。

接下来,需要对数据进行填充,使每个数据点的输入长度相同。此代码用于标记和填充。

11.2 架构

模型的架构是深度学习实现更高性能的最重要因素。我在这里遵循的架构如下:

11.3 培训

模型的训练是用 Adam Optimizer 完成的,损失是 MSE,我采用的度量是 RMSE。还介绍了模型检查点的回调、学习率调度器和提前停止。

# FITTING THE MODELmodel.fit(x=x_train,y=y_train,validation_data=  (x_val,y_val),epochs=10,batch_size = 100,
callbacks=[save,lr,earlystop])

请注意,该模型适合 10 个时期,但由于提前停止回调,它在 4 个时期后停止。

深度学习模型给我们的性能指标是验证数据 0.4316,测试数据 0.4331(ka ggle 私分)。

12.模型比较和结论

这些是所有模型的性能,实际上显示了从作为基准的基本模型到复杂模型(如深度学习模型)的旅程,每一步都有改进。

它清楚地表明,对于这个问题,深度学习模型比机器学习模型表现得更好。

13.部署和预测

我已经在 AWS 上部署了这个模型。要试用该型号,请点击链接。下面是模型工作的视频和对其中一个产品的预测。请在评论中告诉我你的想法。

14.未来的工作

经过所有这些努力,我认为我已经达到了一个不错的性能指标值,但总有改进的余地。以下是我未来将努力的一些想法。

  • 为机器学习模型执行更多功能工程技术。
  • 使用 Tfidf 功能尝试更复杂的模型。
  • 尝试基于 CNN 的模型。
  • 在深度学习模型中尝试基于 Tfidf/CountVectorizer 的功能。

15.Github 知识库和 LinkedIn

这里是 GitHub 库参考完整代码并在 LinkedIn 上与我联系点击这里。

16.参考

  • https://www . Applied ai course . com/course/11/Applied-Machine-learning-course
  • https://www.kaggle.com/c/mercari-price-suggestion-challenge
  • https://www.youtube.com/watch?v=QFR0IHbzA30&t = 2010s
  • https://GH . mltraints . ru/presentations/lopuhinjankeiewicz _ kagglemercari . pdf

用 Python 合并多个 Jupyter 笔记本

原文:https://towardsdatascience.com/merge-multiple-jupyter-notebooks-together-with-python-1696eb8d0dab?source=collection_archive---------38-----------------------

避免手动复制粘贴笔记本单元格

图片来自皮克斯拜

Jupyter 笔记本被认为是任何数据科学家工具箱中不可或缺的一部分。Jupyter Notebook 也称为 IPython Notebooks,是基于服务器-客户端结构的 web 应用程序。

Jupyter Notebook 提供了一个易于使用的交互式数据科学环境,支持各种编程语言。它对原型设计非常有用,并通过结合代码、文本和可视化提供交互式计算。

在任何项目开发中,使用多个 Jupyter 笔记本是很常见的。有时需要将几个笔记本合并在一起,您需要手动将笔记本的单元格复制粘贴在一起,这是一项繁琐的任务。

在本文中,您可以阅读如何高效地将几个 IPython 笔记本合并在一起,只需编写几行 Python 代码。

Jupyter 笔记本的结构:

Jupyter 笔记本保存为 JSON 文件格式。这个 JSON 文件包含所有笔记本内容,包括文本、代码、输出和绘图。数字、图像和视频被编码为 base64 字符串。Jupyter 笔记本 JSON 文件的基本结构是:

**{
 "cells": [],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 4
}**
  • **元数据:**包含关于内核和所用语言的信息的字典。
  • nbformat,nbformat_minor: 笔记本格式版本(4.0)。
  • cells: Cell 是 JSON 文件最重要的组成部分,它包含了每个单元格的信息。每个单元格由具有以下组件的字典表示:
**{**
**"cell_type": "code",** // can be code, markdown or raw
**"execution_count":** null, // null or an integer
**"metadata": {},** // metadata of the cell: tags,editable,collapsed
**"outputs": [],** // result of code or rendered markdown
**"source": []** // the input code or source markdown
**}**

合并笔记本:

将几个 Jupyter 笔记本合并在一起的基本思想是操作笔记本的内容,并将 JSON 文件合并在一起以创建一个单独的笔记本。

nbconvert 是一个 Python 工具,它为研究人员提供了跨不同格式及时传递信息的灵活性。它可以用来合并几个 JSON 文件的内容。让我们通过以下步骤开始合并笔记本。

  • 导入 nbconvert 库:**import nbconvert**
  • 使用 nbconvert 的**.read()** 函数读取所有 IPython 笔记本,该函数读取 JSON 格式的笔记本。
# Reading the notebooks
**first_notebook = nbformat.read(file1, 4)
second_notebook = nbformat.read(file2, 4)
third_notebook = nbformat.read(file3, 4)**
  • 创建一个新笔记本,其中包含上述三个笔记本的合并内容。新笔记本的元数据将与早期笔记本的元数据相同。
# Creating a new notebook
**final_notebook = nbformat.v4.new_notebook(metadata=first_notebook.metadata)**
  • 将每个 JSON 文件中的**cell** 键的所有值连接起来。**+** 关键字可以用来合并三个 IPython 笔记本的**cell**键上的字典值。
# Concatenating the notebooks
**final_notebook.cells = 
first_notebook.cells + second_notebook.cells + second_notebook.cells**
  • 最后,使用 nbconvert 库中的**.write**函数保存新创建的 IPyton 笔记本。
# Saving the new notebook 
**nbformat.write(final_notebook, 'final_notebook.ipynb')**

用几行 Python 代码将包含每个笔记本内容的新文件保存到所需位置。

结论:

在本文中,我们讨论了如何使用 nbconvert 工具将多个 IPython 笔记本文件合并到一个笔记本中。根据个人需求,他们可以对脚本进行修改,并以简单高效的方式合并笔记本。

nbconvert 可以用来将笔记本转换成其他静态格式包括 HTML,LaTeX 等。这是一个有趣的包,你可以通过文档页面来了解。

参考资料:

[1] NbConvert 文档:https://nbconvert.readthedocs.io/en/latest/

感谢您的阅读

合并 ONNX 图

原文:https://towardsdatascience.com/merging-onnx-graphs-da088dbaf9b4?source=collection_archive---------10-----------------------

(图片由作者提供)

使用 sclblonnx 连接、合并、分割和连接 ONNX 图形。

ONNX 越来越火了。虽然最初主要被认为是一种简单存储 AI/ML 模型的文件格式,但近年来它的用途已经发生了变化。如今,我们看到许多数据科学家使用 ONNX 作为构建和管理完整数据处理管道的手段。随着 ONNX 使用的增长,对创建、检查和编辑 ONNX 图形的良好工具的需求也在增长。幸运的是,ONNX 的大型生态系统正在出现;在这篇文章中,我们描述了由[sclblonnx](https://pypi.org/project/sclblonnx/)包(由 Scailable 策划)提供的 ONNX 连接、分割、合并和连接功能。请注意,当您主动管理有用的 ONNX“子图”时,合并、拆分和连接 ONNX 图非常有用:也就是说,您可能有以 ONNX 格式存储的数据管道中的首选预处理和后处理步骤,并且您希望将这些子图与您刚刚在 TensorFlow 或 PyTorch 中训练的模型连接起来。在这篇文章中,我试图解释这是如何做到的。

**注:**我之前写过 ONNX 编辑合并,见https://towardsdatascience . com/creating-editing-and-merging-ONNX-pipelines-897 e 55 e 98 bb 0。然而,随着[sclblonnx](https://pypi.org/project/sclblonnx/) 0.1.9的发布,功能得到了极大的扩展。

一些 ONNX 背景

在讨论由sclblonnx 0.1.9提供的 ONNX 图形的新的mergeconcatsplitjoin功能之前,提供一点关于 ONNX 图形的背景是有用的。在本文的这一点上,我假设你知道一些 ONNX 的基础知识(如果不知道,请看这篇文章,或者这篇文章)。因此,你知道 ONNX 提供了一个有向计算图的描述,它指定了在(强类型)输入张量上执行哪个[操作](http://onnx operators)来产生期望的输出张量。而且,你知道 ONNX 对于存储经过训练的 AI/ML 模型很有用,用于创建数据科学管道,以一种独立于平台和部署目标的方式。也就是大家一般都知道 ONNX 是好玩的东西。

然而,为了理解如何合并、分割、连接和连接 ONNX 图,我们需要更多的背景知识。我们既需要理解图中的边是如何创建的,也需要更详细地理解图的输入和输出的角色。

按名称隐式创建边

让我们从边的创建开始。尽管 ONNX 图仅仅是一个有向图,因此可以用它的节点和边来描述,但这不是我们创建(也不是存储)ONNX 图的方式。当创建 ONNX 图时,我们并不明确地创建邻接矩阵来标识节点之间的边。相反,我们创建一些type(不同的操作符)的节点,每个节点都有一个命名的inputoutput。这也是 ONNX 文件(实际上只是一个 protobuf)中存储的所有内容:该文件存储了一个操作符类型列表,每个类型都有自己命名的输入和输出。最后的名字允许在图中构造边:如果节点n1有一个名为x1的输出,节点n2有一个名为x1的输入,那么在n1n2之间将创建一条(有向)边。如果随后添加另一个节点n3,该节点有一个命名输入x1,我们最终得到下图:

(图片由作者提供)

因此,在合并、连接、连接和拆分 ONNX(子)图时,了解最终组合的图中存在的输入和输出名称是非常重要的——在某种程度上是内部的,如果您已经从各种培训工具之一导出到 ONNX,您可能不知道这些名称。如果两个图形中出现相同的名称,则不小心合并图形会导致绘制出可能不需要的边。

如果两个图形中出现相同的名称,则不小心合并图形会导致绘制出可能不需要的边。

图形的输入和输出

在合并、分割或编辑 ONNX 图时,另一个有点混乱但非常重要的概念是节点(正如我们刚刚讨论的,用于创建边)的输入和输出与本身的输入和输出之间的区别。图形输入和输出分别表示输入到计算图形的张量,以及执行计算产生的张量。输入和输出以与创建边相同的方式隐式连接到图。实际上,图的输入和输出的一个合理的心理模型是,它们仅仅是只有一个输出(图的输入)或者只有一个输入(图的输出)的节点;这些不在张量上操作的特殊节点各自的输入和输出就是外界。

好吧,这有点神秘。

让我们用下面的符号举几个例子:

I1(name)  # An input (to the graph) with a specific name
O1(name)  # An output (to the graph) with a specific name
N1({name, name, ...}, {name, name, ...}) # A node, with a list of inputs and outputs.

给定这个符号,我们可以例如表示

# A simple graph:
I1(x1)
I2(x2)
O3(x3)
N1({x1,x2},{x3})

这将生成(使用橙色表示输入和输出)下图:

(图片由作者提供)

如果N1是加法运算符,这个图将简单编码为两个张量相加。

让我们做一个稍微复杂一点的图表:

I1(x1)
I2(x2)
N1({x1, x2}, {x3})
N2({x2, x3}, {x4})
O1(x3)
O2(x4)

这将在图形上导致:

(图片由作者提供)

好了,现在我们清楚了图的内部边、输入和输出是如何构造的;让我们仔细看看sclblonnx包中的工具!

使用sclblonnx操作 ONNX 图形

从版本0.1.9的更新开始,sclblonnx包包含了许多更高级的实用函数,将多个 ONNX(子)图合并成一个图。尽管该包的早期版本已经包含了merge函数来有效地将两个图形粘贴在一起(稍后将详细介绍),但此次更新将mergejoinsplit作为一个更高级别的包装器,围绕着一个更加通用、也更难使用的函数concat。让我们从更高级的功能开始。

这里描述的所有函数都可以在sclblonnx包的例子中用 python 代码找到。这些可以在https://github . com/scailable/sclblonnx/blob/master/examples/example _ merge . py找到。另外,请参见所讨论的每个函数的文档:https://github . com/scailable/sclblonnx/blob/master/sclblonnx/merge . py

合并

merge有效地获取两个图形(父图形和子图形),并将父图形的已识别输出粘贴到子图形的已识别输入。默认情况下,merge假设两个图都是完整的(即所有的边都匹配得很好,并且所有的输入和输出都已定义)。merge的签名是

merge(sg1, sg2, io_match)

其中sg1是父子图,sg2是子图,io_match给出了需要与inputs off sg2匹配的sg1输出名称对的列表。因此,根据我们在上一节中发展的符号,如果我们有:

# Parent (sg1)
I1(x1)
N1({x1},{x2})
O1(x2)# Child (sg2)
I2(z1)
N2({z1},{z2})
O2(z2)

merge(sg1, sg2, [(x2,z1)])的调用将创建:

I1(x1)
N1({x1},{x2})
N2({x2},{z2})
O2(z2)

然而,正如你所想象的,我们可以使用这个函数进行更多的合并。注意merge假设两个图的内部命名没有“冲突”;如果不是这样,并且你想更详细地控制这种行为,我推荐使用concat;merge 仅仅是围绕concat的一个用户友好的包装器。

Split & Join

merge一样,splitjoin也是围绕concat的更高层次的包装器(我们将在下面详述)。行为相对简单:

  • split取一个有多个输出的“父”,将一个子图粘贴到这些输出的子集(通过匹配子图的输入),将另一个子图粘贴到父输出的另一个子集。因此,实际上,split创建了一个分支,父图在这个分支中馈入两个子图。
  • join在很多方面与分裂相反:它需要两个“父母”,并且只有一个孩子。父母的输出与孩子的输入相匹配。因此,join有效地连接到更大的树中子图的分支。

串联

上述mergesplitjoin功能的工作台是更加通用的concat功能。理解其功能的最简单方法也许是看一看签名和文档:

def concat(
  sg1: xpb2.GraphProto,        
  sg2: xpb2.GraphProto,        
  complete: bool = False,        
  rename_nodes: bool = True,        
  io_match: [] = None,        
  rename_io: bool = False,        
  edge_match: [] = None,        
  rename_edges: bool = False,        
  _verbose: bool = False,       
  **kwargs): """  concat concatenates two graphs. Concat is the flexible (but also rather complex) workhorse for the merge, join, and split functions and can be used to pretty flexibly paste together two (sub)graphs. Contrary to merge, join, and split, concat does not by default assume the resulting onnx graph to be complete (i.e., to contain inputs and outputs and to pass check()), and it can thus be used as an intermediate function when constructing larger graphs. Concat is flexible and versatile, but it takes time to master. See example_merge.py in the examples folder for a number of examples.    Args:        
  sg1: Subgraph 1, the parent.        
  sg2: Subgraph 2, the child.        
  complete: (Optional) Boolean indicating whether the resulting     
             graph should be checked using so.check(). 
             Default False.         
  rename_nodes: (Optional) Boolean indicating whether the names of 
             the nodes in the graph should be made unique. 
             Default True.          
  io_match: (Optional) Dict containing pairs of outputs of sg1 that 
             should be matched to inputs of sg2\. 
             Default [].        
  rename_io: (Optional) Boolean indicating whether the inputs and 
             outputs of the graph should be renamed. 
             Default False.        
  edge_match: (Optional) Dict containing pairs edge names of sg1 
             (i.e., node outputs) that should be matched to edges of 
             sg2 (i.e., node inputs). 
             Default [].        
  rename_edges: (Optional) Boolean indicating whether the edges    
             should be renamed. 
             Default False        
  _verbose: (Optional) Boolean indicating whether verbose output 
             should be printed (default False) Returns: The concatenated graph g, or False if something goes wrong along the way.    
"""## The implementation...

从签名中可以清楚地看出,更高级别的包装器merge简单地调用concat一次,几乎是用它的默认参数。函数splitjoin分别调用concat两次,以获得它们想要的结果。请注意,参数rename_edges允许用户控制是否应该重命名子图中的所有边(从而避免可能不需要的隐式边创建),而complete允许用户使用 merge 来操作部分图(即尚未定义所有边的图)。

包裹

我希望上面的内容能够揭示 ONNX 提供的巨大可能性,以及一些手动操作 ONNX 图形的工具。我们认为 ONNX 非常适合存储(子)图,这些图以独立于平台的方式存储与数据科学管道相关的有用信息。像sclblonnx包这样的工具使用户能够使用子图作为构建块来创建完整的管道。

在这篇文章中,我有意忽略了关于匹配节点的输入和输出的维度和类型的问题;我希望通过关注所涉及的图表的粗略结构,操作更容易理解;当创建实际的功能图时,显然所涉及的各种张量的类型和维数是很重要的。

放弃

值得注意的是我自己的参与:我是 杰罗尼姆斯数据科学院 的数据科学教授,也是Scailable的联合创始人之一。因此,毫无疑问,我对 Scailable 有既得利益;我有兴趣让它成长,这样我们就可以最终将人工智能投入生产并兑现它的承诺。这里表达的观点是我自己的。注意

使用 SQL 合并表

原文:https://towardsdatascience.com/merging-tables-using-sql-a2e60ff687e9?source=collection_archive---------0-----------------------

本文讨论了如何使用 SQL 通过行和列合并多个表,并给出了一些例子

作者图片

实际上,很少会有只涉及一个表的 SQL 查询。我们可能需要按行(记录)或列(字段)合并多个表,以获得所需的结果。在本文中,我们将讨论 SQL 中的操作符/命令,这些操作符/命令支持按行或列合并表。

按列合并表格

在 SQL 中,可以使用联接通过列来合并多个表。联接基于指定的列(通常是一个表的主键和另一个表的外键)合并两个表。下面是 SQL 连接的一般语法。

**SELECT** 
 *
  **FROM** table_1
   **JOIN** table_2
    **USING** (id);

在上面的语法中, table_1table_2 是带有 key 列的两个表(两个表中的匹配列), id 。只有当键列在两个表中具有相同的名称时,我们才使用关键字。否则,我们需要明确提到两个表的键列,如下所示。

**SELECT** 
 *
  **FROM** table_1 t1
   **JOIN** table_2 t2
    **ON** t1.t1_id = t2.t2_id;

在上面的语法中, t1table_1 的别名, t2table_2 的别名。当两个表中键列的名称不相同时,我们需要使用关键字上的来匹配它们,如上所示。我们现在将讨论 SQL 中几个重要的连接。

内部连接

内部联接按列合并两个表,并只返回两个表中匹配的记录(基于指定的列)。在下面的查询结果中,我们可以看到只返回了 left_tableright_table 中 id 相同的记录。

**SELECT** 
 *
  **FROM** left_table
   **INNER JOIN** right_table
    **USING** (id);

**SELECT** 
 *
  **FROM** left_table l
   **INNER JOIN** right_table r
    **ON** l.id = r.id;

内部连接(作者图片)

左连接

Left join 按列合并两个表,并返回左表中的所有记录,但只返回右表中的匹配记录(基于指定的列)。在下面的查询结果中,我们可以看到两个表中 id 为的记录以及 left_table 的所有记录。 中记录右 _ 表 中没有匹配的id****左 _ 表 中有空值。

****SELECT** 
 *
  **FROM** left_table
   **LEFT** **JOIN** right_table
    **USING** (id);**

****SELECT** 
 *
  **FROM** left_table l
   **LEFT** **JOIN** right_table r
    **ON** l.id = r.id;**

左连接(图片由作者提供)

右连接

Right join 按列合并两个表,并返回右表中的所有记录,但只返回左表中的匹配记录(基于指定的列)。在下面的查询结果中,我们可以看到两个表中具有相同 id 的记录以及 right_table 的所有记录。 中的记录左 _ 表 中没有匹配的id右 _ 表 中有空值。

****SELECT** 
 *
  **FROM** left_table
   **RIGHT** **JOIN** right_table
    **USING** (id);**

****SELECT** 
 *
  **FROM** left_table l
   **RIGHT** **JOIN** right_table r
    **ON** l.id = r.id;**

右连接(作者图片)

完全连接

完全连接可以被认为是左连接和右连接的组合。完全联接按列合并两个表,并返回左表和右表中的所有记录。在下面的查询结果中,我们可以看到两个表的所有记录都被返回。另一个表中没有匹配 id 的记录为空。

****SELECT** 
 *
  **FROM** left_table
   **FULL** **JOIN** right_table
    **USING** (id);**

****SELECT** 
 *
  **FROM** left_table l
   **FULL** **JOIN** right_table r
    **ON** l.id = r.id;**

完全加入(图片由作者提供)

交叉连接

交叉连接返回两个表的笛卡尔积。两个集合的笛卡尔积 A = {1,2},B = {3,4}是 A x B = {(1,3),(1,4),(2,3),(2,4)}。我们不需要在交叉连接中指定键列。

****SELECT** 
 *
  **FROM** left_table
   **CROSS JOIN** right_table**

交叉连接(图片由作者提供)

半连接

从技术上讲,半连接不是 SQL 连接,但其工作方式类似于连接。Semi join 根据右表中的键列返回左表中的匹配记录。半连接在查询结果中不包括右表的列。在下面的例子中,我们要返回来自 左 _ 表中的记录,匹配id右 _ 表 *。换句话说,我们希望中的记录出现在left _ tableidright _ table。*************

****SELECT** 
 * 
  **FROM** left_table
  **WHERE** 
   id **IN** 
   (
    **SELECT** id **FROM** right_table
   )**

半连接(图片由作者提供)

反连接

从技术上讲,反联接也不是 SQL 联接,但其工作方式类似于联接。Anti join 根据右表中的键列返回左表中不匹配的记录。反联接在查询结果中也不包括右表的列。在下面的例子中,我们要返回来自 左 _ 表 中 id与 id右 _ 表 的记录。换句话说,我们要的记录在**left _ tablewhoid中不存在于right _ table***。*************

***SELECT** 
 * 
  **FROM** left_table
  **WHERE** 
   id
    **NOT IN** 
     (
      **SELECT** id **FROM** right_table
     )*

反对加入(图片由作者提供)

自连接

自联接使我们能够将一个表与其自身联接起来。在下面的查询中,我们需要找到具有相同 值的记录。为此,我们将表与自身连接起来,过滤掉具有相同值但不同 id 的记录。**

****SELECT** 
 *
  **FROM** left_table l1, left_table l2
  **WHERE** 
   l1.left = l2.left
   **AND** 
   l1.id <> l2.id
  **ORDER BY** l1.left**

自连接(图片由作者提供)

按行合并表格

联盟

Union 按行合并两个表,前提是一个表的列的数据类型与另一个表的列的数据类型相匹配。我们不能将列数据类型为 integer 和 text 的表与列数据类型为 text 和 integer 的表合并。然而,我们可以合并两个表,即使一个表的列名与另一个表的列名不匹配。Union 只返回两个表的唯一记录。

**(
 **SELECT** 
  * 
   **FROM** left_table
)
**UNION**
(
 **SELECT** 
  * 
   **FROM** right_table
)**

工会(图片由作者提供)

联合所有

与 Union 类似,Union All 也按行合并表。与 Union 不同,Union All 保留两个表的重复记录。在下面的查询结果中,我们合并了 idleft _ tableright_table。我们可以在结果中看到一些重复。

*(
 **SELECT** 
  id 
   **FROM** left_table
)
**UNION ALL**
(
 **SELECT** 
  id 
   **FROM** right_table
)*

联合所有(图片由作者提供)

横断

Intersect 返回两个表的公共记录。在下面的查询结果中,我们可以看到常见的idsleft _ tableright_table

*(
 **SELECT** 
  id 
   **FROM** left_table
)
**INTERSECT**
(
 **SELECT** 
  id 
   **FROM** right_table
)*

相交(作者图片)

除...之外

Except 返回第一个表(左表)中不存在于第二个表(右表)中的记录。在下面的查询结果中,我们可以看到 left_tableid s 在 right_table 中没有。

除外(图片由作者提供)

示例查询

我们将使用从这里下载的 dvd_rental 数据库并恢复它。下面是在 PostgreSQL 中恢复数据库的文档。

**

1.五大常客

在本例中,我们需要找到租赁最多的前 5 名客户。为此,我们将

  1. 使用 customer_id 连接 客户租赁 表。
  2. 通过分组**【customer _ id】**对客户进行计数(如 rental_count )。
  3. 按照 rental_count 降序排列结果。
  4. 将结果限制为前 5 条记录。
***SELECT** 
  c.customer_id,
  c.first_name,
  c.last_name, 
  **COUNT**(c.customer_id) **AS** rental_count
  **FROM** customer c
    **INNER JOIN** rental r
      **USING** (customer_id)
  **GROUP BY** customer_id
  **ORDER BY** 
   **COUNT**(c.customer_id) **DESC**
  **LIMIT** 5;*

作者图片

2.按产生的收入排列的前 5 名和后 5 名客户

在这个例子中,我们将使用通用表表达式(CTE)。使用 cte,我们可以为特定查询创建临时表。下面是 cte 的官方 Postgres 文档。

*https://www.postgresql.org/docs/9.1/queries-with.html

在本例中,我们需要找出收入最高的前 5 名和后 5 名客户。为此,我们将

1.创建一个名为的 CTErevenue _ per _ customerby

  • 使用customer _ id连接 客户租赁 表。****
  • 使用 租金 _id 将结果表与付款表连接起来。**
  • 计算客户为每笔租赁交易支付的总金额(如 total_amount )按customer _ id****分组。
  • 最后,选择 customer_id first _ namelast _ name**total _ amount**。**

2.从上述 CTE 中按收入选择前 5 名客户

  • 【收入 _ 每 _ 客户】【CTE 结果】中的 合计 _ 金额 进行降序排序。
  • 将结果限制为前 5 条记录。
  • 添加注释,将记录指定为“前 5 条”。

3.根据上述 CTE 的收入选择排名后 5 位的客户

  • 【收入 _ 每 _ 客户】【CTE 结果】中的 合计 _ 金额 进行升序排序。
  • 将结果限制为前 5 条记录。
  • 添加注释,将记录指定为“底部 5”。

4.使用合并上述两个结果。

***WITH** revenue_per_customer **AS** 
 (**SELECT** 
   c.customer_id, 
   c.first_name,
   c.last_name,
   **SUM**(p.amount) **AS** "total_amount"
   **FROM** customer c
    **INNER JOIN** rental r
     **USING** (customer_id)
    **INNER JOIN** payment p
     **USING** (rental_id)
   **GROUP BY** c.customer_id)(**SELECT** 
  *,
  'Top 5' **AS** comment
  **FROM** revenue_per_customer
  **ORDER BY** total_amount **DESC**
  **LIMIT** 5)**UNION**(**SELECT** 
  *,
  'Bottom 5' **AS** comment
  **FROM** revenue_per_customer
  **ORDER BY** total_amount **ASC**
  **LIMIT** 5)
**ORDER BY** comment **DESC**, total_amount **DESC**;*

作者图片

我们也可以使用窗口函数得到上面的查询结果。下面是关于窗口函数的官方 Postgres 文档。

*https://www.postgresql.org/docs/9.1/tutorial-window.html

为了找出使用窗口函数产生最多收入的前 5 名和后 5 名客户,我们将

1.创建一个名为total _ AMT _ rank的 CTE

  • 使用customer _ id加入 客户租赁 表。
  • 将结果表与 付款 表连接使用 租金 _id
  • 计算客户为每笔租赁交易支付的总金额(如 total_amount )按customer _ id分组。
  • 最后,选择customer _ id*,* first_name last _ nametotal _ amount和排名 total_amount 这将等级 1 赋予最高金额,依此类推。

2.从上述 CTE 中选择total _ amount _ rank****在 1 到 5 之间的客户,选择收入排名前 5 的客户。****

3.从上述 CTE 中按收入选择排名后 5 位的客户

  • *【total _ AMT _ rank***【CTE 结果】中的total _ amount _ rank按降序排序。**
  • 将结果限制为前 5 条记录。

4.使用 UNION 合并上述两个结果。

*****WITH** total_amt_rank **AS**
 (
  **SELECT**
      c.customer_id, 
      c.first_name,
      c.last_name,
      **SUM**(p.amount) **AS** "total_amount",
      **RANK**() **OVER** (**ORDER BY SUM**(p.amount) **DESC**) **AS** total_amount_rank
       **FROM** customer c
        **INNER JOIN** rental r
          **USING** (customer_id)
        **INNER JOIN** payment p
          **USING** (rental_id)
       **GROUP BY** c.customer_id
  )
(
 **SELECT** *
  **FROM** total_amt_rank
    **WHERE** 
     total_amount_rank **BETWEEN** 1 **AND** 5
)

**UNION**

(
 **SELECT** *
   **FROM** total_amt_rank
    **ORDER BY** total_amount_rank **DESC**
  **LIMIT** 5
)

**ORDER BY** total_amount_rank;***

作者图片*

3.租金最高的前 5 个国家

在本例中,我们需要找到租金最高的前 5 个国家。为此,我们将

  1. 使用 country_id 加入 countrycity 表。
  2. 使用 city_id 将结果表与 地址 表连接。
  3. 使用 address_id 将结果表与客户 表连接起来。
  4. 使用 customer_id 将结果表与 租赁 表连接起来。
  5. 计数 country_id (为 出租 _ 计数 )按分组country _ id***。我们也可以用rental _ id来得到rental _ count****。***
  6. rental_count 降序排列结果。
  7. 将结果限制为 5 条记录。
****SELECT** 
 co.country_id, 
 co.country,
 **COUNT**(co.country_id) **AS** rental_count
  **FROM** country co
   **INNER JOIN** city ci
    **USING** (country_id)
   **INNER JOIN** address a
    **USING** (city_id)
   **INNER JOIN** customer cu
    **USING** (address_id)
   **INNER JOIN** rental r
    **USING** (customer_id)
  **GROUP BY** co.country_id
  **ORDER BY** 
   **COUNT**(co.country_id) **DESC**
  **LIMIT** 5;**

作者图片

有几个地址和城市没有客户。使用 inner join 可以省略这些记录。在下面的查询中,我们将看看在使用 left join 时,结果如何包括没有客户的地址。

4.没有客户的城市/地址?

有几个城市和地址没有任何客户(这些可能是商店地址)。使用内部连接会将它们从结果中忽略,因为在另一个表中没有匹配的条目。例如,加拿大一个名为伦敦的城市在地址表中没有匹配的 city_id 。使用 inner join 会从结果中忽略加拿大的伦敦。同样,加拿大和澳大利亚的四个地址在 客户 表中没有匹配的 address_id

****SELECT** 
 co.country, 
 ci.city, 
 a.address, 
 cu.customer_id 
  **FROM** country co
   **LEFT JOIN** city ci
    **USING** (country_id)
   **LEFT JOIN** address a
    **USING** (city_id)
   **LEFT JOIN** customer cu
    **USING** (address_id)
  **WHERE** cu.address_id **IS NULL**;**

作者图片

5.没有客户的国家

在本例中,我们将通过查找没有客户的国家

1.通过创建子查询来查找至少有一个客户的国家

  • 加入 国家 表与 城市 表使用 国家 _id
  • 使用 city_id 将余数表与 地址 表连接起来。
  • 使用 address_id 将剩余表与 客户 表连接。

2.从国家** 表中选择 国家国家* 国家 _id 不在上述子查询的 国家 _id 中。*

***SELECT** 
 country 
    **FROM** country
     **WHERE** country_id 
       **NOT IN** 
        (
         **SELECT** 
          co.country_id
           **FROM** country co
            **INNER JOIN** city ci
              **USING** (country_id)
            **INNER JOIN** address a
              **USING** (city_id)
            **INNER JOIN** customer
              **USING** (address_id)
        );*

作者图片

6.澳大利亚有商店吗?

在上面的例子中,我们看到澳大利亚没有客户。在本例中,我们将查看澳大利亚是否有商店

  1. 加入 国家 表与 城市 表使用 国家 _id
  2. 使用 city_id 将结果表与 地址 表连接起来。
  3. 使用 address_id 将结果表与 store 表连接。
  4. 选择在澳大利亚store _ id不为空的记录。

Left join 确保没有城市的国家和没有商店的城市也包含在查询结果中。

***SELECT** 
 st.store_id, 
 co.country, 
 ad.address
  **FROM** country co
   **LEFT JOIN** city ci
    **USING** (country_id)
   **LEFT JOIN** address ad
    **USING** (city_id)
   **LEFT JOIN** store st
    **USING** (address_id)
  **WHERE** 
   (st.store_id **IS NOT NULL**) 
    **AND** 
     (co.country = 'Australia');*

作者图片

澳大利亚有一家商店。事实上,整个数据库中只有两家商店。我们将使用下面的查询来查看它们。

***SELECT** * **FROM** store;*

作者图片

7.没有电影的语言

在这个例子中,我们将看看是否有没有电影的语言

  1. 加入 语言 表格与 电影 表格使用 语言 _id 。左连接确保没有任何电影的语言也包括在内。
  2. 过滤 film_id 为空的记录。
***SELECT** 
 *
  **FROM** language l
   **LEFT JOIN** film f
    **USING** (language_id)
  **WHERE** f.film_id **IS NULL**;*

作者图片

我们看到一些语言在数据库中没有电影。我们将通过从 电影 表中选择(2,3,4,5,6)中带有 language_id 的电影来确保这不是一个错误。查询结果不应返回任何记录。

***SELECT** 
 * 
  **FROM** film
   **WHERE** language_id **IN** (2,3,4,5,6);*

作者图片

8.电影在印度的受欢迎程度

在本例中,我们将通过连接前面的示例和中讨论的所需表来查找印度每个电影类别的租赁数量

  1. 国家类别 分组,过滤来自印度的记录,统计电影类别名称(如film _ category _ count*)。*
  2. 按国家升序排序结果,按 降序排序 电影 _ 类别 _ 数量。
***SELECT** 
 co.country, 
 cat.name **AS** film_category,
 **COUNT**(cat.name) **AS** film_category_count
  **FROM** country co
   **INNER JOIN** city ci
    **USING** (country_id)
   **INNER JOIN** address ad
    **USING** (city_id)
   **INNER JOIN** customer cu
    **USING** (address_id)
   **INNER JOIN** rental re
    **USING** (customer_id)
   **INNER JOIN** inventory inv
    **USING** (inventory_id)
   **INNER JOIN** film fi
    **USING** (film_id)
   **INNER JOIN** film_category fc
    **USING** (film_id)
   **INNER JOIN** category cat
    **USING** (category_id)
   */* 
    Using
    WHERE co.country = 'India'
    here, instead of 
    HAVING co.country = 'India'
    reduces the query execution time.
   */*
  **GROUP BY** (co.country, cat.name)
  **HAVING** co.country = 'India'
  **ORDER BY** 
   co.country **ASC**, 
   **COUNT**(cat.name) **DESC**;*

作者图片

9.只有一个演员的电影

在本例中,我们将通过以下方式查找只有一个演员的电影

  1. 加入 电影 表与 电影 _ 演员 表使用 电影 _id
  2. film_id 分组,统计演员人数(如 actor_count )。
  3. 过滤 actor_count 为 1 的记录。
***SELECT** 
 f.film_id, 
 f.title, 
 **COUNT**(fa.actor_id) **AS** actor_count
  **FROM** film f
   **INNER JOIN** film_actor fa
    **USING** (film_id)
  **GROUP BY** f.film_id
  **HAVING COUNT**(fa.actor_id) = 1;*

作者图片

10.一个演员的电影数量(按类别)

在本例中,我们将通过以下方式按电影类别查找演员的电影数量

  1. 创建一个名为actor _ cat _ CNT的 CTE,返回每个 actor_idcategory _ id****的电影数量。
  2. 将上面的 CTE 与 类别 表格用 类别 _id 连接起来。
  3. 使用 actor_id 将结果表与 actor 表连接。
  4. 按升序排列演员姓名(串联 名字姓氏)电影数量 )。
***WITH** 
 actor_cat_cnt **AS**
 (
  **SELECT** 
   fa.actor_id, 
   fc.category_id, 
   **COUNT**(f.film_id) **AS** film_count 
    **FROM** film_actor fa
     **INNER JOIN** film f
      **USING** (film_id)
     **INNER JOIN** film_category fc
      **USING** (film_id)
    **GROUP BY** 
     fa.actor_id, 
     fc.category_id
 )**SELECT** 
 **CONCAT**(ac.first_name, ' ', ac.last_name) **AS** actor,
 ca.name **AS** category,
 film_count
  **FROM** actor_cat_cnt
   **INNER JOIN** category ca
    **USING** (category_id)
   **INNER JOIN** actor ac
    **USING** (actor_id)
  **ORDER BY** 
   **CONCAT**(ac.first_name, ' ', ac.last_name) **ASC**,
   film_count **DESC**;*

作者图片

11.演员的流行类别

在上面的例子中,我们通过电影类别找到了一个演员的电影数量。在本例中,我们将通过以下方式找到演员的热门类别(即演员拥有最多电影的类别)

  1. 创建一个名为 actor_cat_cnt 的 CTE,返回每个 actor_idcategory_id 的电影数量,并按电影数量降序排列每个演员的类别(如cat _ rank*)*。**
  2. 将上面的 CTE 与的类别表用 的类别 _id 连接起来。**
  3. 使用 actor_id 将结果表与 actor 表连接。
  4. 过滤 cat_rank = 1 的记录。
  5. 按升序排列演员姓名(串联 姓)片数 )。
****WITH** 
 actor_cat_cnt **AS**
 (
  **SELECT** 
   fa.actor_id, 
   fc.category_id, 
   **COUNT**(f.film_id) **AS** film_count,
   **RANK**() **OVER** 
    (**PARTITION BY** fa.actor_id 
      **ORDER BY COUNT**(f.film_id) **DESC**) **AS** cat_rank 
    **FROM** film_actor fa
     **INNER JOIN** film f
      **USING** (film_id)
     **INNER JOIN** film_category fc
      **USING** (film_id)
    **GROUP BY** 
     fa.actor_id, 
     fc.category_id
 )**SELECT** 
 **CONCAT**(ac.first_name, ' ', ac.last_name) **AS** actor,
 ca.name **AS** category,
 film_count
  **FROM** actor_cat_cnt
   **INNER JOIN** category ca
    **USING** (category_id)
   **INNER JOIN** actor ac
    **USING** (actor_id)
  **WHERE** cat_rank = 1  
  **ORDER BY** 
   **CONCAT**(ac.first_name, ' ', ac.last_name) **ASC**,
   film_count **DESC**;**

作者图片

本文到此结束。我们已经讨论了使用 SQL 按行或列合并表的方法,以及一些使用 dvd_rental 数据库的例子。这些是我们用 SQL 编写的几乎每个查询中使用的基本概念。我们在实践中可能不会经常使用其中的一些,但是了解它们是必要的。*

使用 Apache Spark 将 Datalake 中过多的小文件合并到较少的大文件中

原文:https://towardsdatascience.com/merging-too-many-small-files-into-fewer-large-files-using-apache-spark-in-datalake-ff9a32807056?source=collection_archive---------7-----------------------

提高对大型数据集的读取查询性能

作者图片

T 在数据平台中存储大量数据的现代方法是将每个数据集分布在集群中的几个节点上。如果使用云,整个数据集将被分成多个对象。这可能会导致“太多的小文件”,这是大数据领域的一个众所周知的问题。创建小文件的原因有几个,比如一条消息一条消息地保存传入的流数据,按具有数据倾斜的键进行分区,等等。当通过 Namenode、MapReduce 或 Spark 作业维护或读取该数据集时,驱动程序必须跟踪所有文件的元数据,以便规划分布式数据处理。当它们太多时,保存元数据会有内存开销,而且列出它们会花费更多的网络 I/O 时间。

在数据湖中工作时,您可能已经注意到,Spark 作业花费太多时间读取 s3/HDFS 上的数据集,甚至需要一段时间才能看到执行器启动。或者您的 Hive 查询需要一两分钟来启动任务。最有可能的是,这是由于驱动程序花费了大部分时间首先在 s3 中检查数据集的文件/对象的所有元数据,尤其是当有太多小文件时。这是因为驱动程序执行数据集中的文件列表,估计大小/分区,然后将实际处理分配给执行器。因此,拥有太多的小文件可能会导致性能瓶颈,在最坏的情况下,驱动程序可能会因内存不足异常而失败。

这是一个简单的 Spark 作业,它可以接收数据集和估计的单个输出文件大小,并将输入数据集合并到更大的文件中,最终减少文件的数量。同时,拥有一个非常大的文件也不好。一般来说,1gb 或 512MiB 的文件是标准的。

这里有完整的剧本 。让我给你介绍一下剧本…

司机:

这是接受三个参数的主要方法..1)小文件所在的源 s3 路径 2)作业将合并文件写入的目标 s3 路径,以及 3)单个合并文件的最大目标文件大小。

这个脚本假设输入目录(第 5 行)再次包含子目录,这些子目录实际上包含最终文件。通常,分区数据集就是这种情况。但是,如果我们只有一个存放所有文件的目录,那么您可以调整第 5 行,将单个元素传递给列表。

接下来,第 9 行到第 11 行很有趣,它使用 Scala 的并行集合来并行提交多个 spark 作业。是的,你没看错。当从每个线程提交时,我们可以运行并行的 Spark 操作。如您所见,所有的多线程都通过使用并行集合被抽象掉了。第 9 行设置并行度,即并行处理多少个目录。在这里,我只是将它设置为子目录的数量。如果超过一百个,我建议成批运行它们,因为集群可能会过载(当然,这取决于集群的大小)。

从第 12 行开始,处理开始于遍历每个输入目录并并行处理它们。每个目录的整个过程可以分为三个步骤:

步骤 1:找到文件大小

这很简单。我只是使用“com . Amazon AWS:AWS-Java-SDK”库列出目录中的文件。在列表过程中,我还发现了每个对象的大小,并将 Map < file-prefix,size >返回给调用者。

步骤 2:确定要合并的文件组

在这里,我们可以找到可以合并的一组较小的文件。如您所见,该方法采用了映射和目标文件大小。在按文件大小对映射进行排序后,它将多个文件分组到一个批处理中,以便该批处理的总大小小于或等于目标文件大小。当然,这可能不是最好的群集,因为我们可能最终得到小于目标文件大小的文件,但不能与其他此类文件进行批处理,因为总批处理大小可能会超过目标文件大小。幸运的是,由于我们是按照文件大小升序对地图进行排序的,所以最小大小的文件会被组合在一起,只有那些已经接近目标大小的文件会保留下来。该函数最后返回一个 List[List[file]],其中包含要合并为单个文件的一批文件的列表。

第三步:合并文件并写出它们

现在到了最后一步,即将前一步中的分组文件合并成一个文件。你可以猜到,这是一个简单的任务。只需使用 spark.read()函数读取文件(在上面的代码中,我读取的是 Parquet 文件,但可以是任何文件格式),方法是传递该组中的文件列表,然后使用 coalesce(1)将它们合并成一个文件。这是另一个有趣的作品。我们再次使用并行集合来执行合并,因此合并同一目录中的每个组是并行进行的。因此,如果每个目录中有 m 个目录和 n 个组,那么就会有 m x n 个并行作业在运行。

当写出合并的文件时,写模式是“追加”,因为,正如我前面提到的,多个目录被并行处理,并且所有目录的目标目录都是相同的,“覆盖”模式将失败,表示“目录已经存在”。因此,您需要确保目标目录是一个新目录。

仅此而已。现在,即使是 KiB 的文件也会被合并成 100 兆或 100 兆字节.当要扫描的文件数量较少时,您可以很容易地看到读取查询的性能提升。

下一步是什么?
1。对这个脚本的一个可能的即兴创作是,在进行磁盘 IO 之前,最好通过写作业合并数据集,而不是合并已经存在的文件。我将分享另一个可以做到这一点的帖子。同时,该脚本可用于合并历史数据集。
2。正如我已经提到的,第 2 步,即确定要合并的文件组,并不是最好的。如果你知道一个聚类算法,可以以一种最佳的方式对文件进行分组,这样我们在合并后就有最少的文件,我欢迎 PR 的:-)

Python 中的价值排序和边际减排成本曲线

原文:https://towardsdatascience.com/merit-order-and-marginal-abatement-cost-curve-in-python-fe9f77358777?source=collection_archive---------5-----------------------

制定批发电价并探索脱碳机会

为了实现《巴黎协定》设定的本世纪末全球气温限制 1.5 摄氏度的目标,不同机构提出了不同的情景。缓解方案中有一个共识,可再生能源等低碳技术的份额需要增加,化石燃料需要在未来的能源组合中稳步下降。

缓解技术方案的部署(如可再生能源、电动汽车、绿色氢等)。)想出自己的一套成本和收益。在一个国家的发电组合中,可再生能源和化石燃料的组合不仅决定了二氧化碳的排放量,还会影响电价。在这篇文章中,我将讨论如何通过**优值顺序曲线(基于边际成本或产量的概念)在批发电力市场中确定电价,以及如何根据边际减排成本曲线评估一个国家或地区或组织的脱碳机会的成本效益。**我将使用 Python 来构建这两条曲线,并在此过程中讨论其实现。

我们开始吧!

优级曲线

一个国家的电力局或公用事业公司可以在其投资组合中拥有不同类型的相互竞争的发电厂,以向零售商提供电力输出。这就是所谓的电力批发市场(也称为现货市场)。发电的成本根据发电厂的类型而不同。例如,太阳能光伏或风力涡轮机发电的成本为零或极低,因为它们不需要任何燃料。另一方面,基于化石燃料的发电厂的运行成本较高,这取决于燃烧发电的燃料(如煤、石油或天然气)的价格。用经济学的语言来说,生产一个额外单位产品的成本叫做 边际生产成本

在电力系统中,基于电力生产的短期边际成本的升序,组合中的可用发电厂被安排在线**。该序列被称为绩效等级。边际成本最低的发电厂将首先满足需求,其次是边际成本较高的发电厂,以优化电力供应成本。**

在本节中,我将实施不同的步骤来构建 Python 中的价值排序曲线,并讨论价值排序如何设定批发电价。

履行

让我们考虑在一个国家的电力组合中有九种类型的发电厂,如列表power_plants所示。这些电厂必须按照marginal_costs的升序排列。本例中使用的数据是基于我的假设,可以视为虚拟数据集。

power_plants **=** ["Solar", "Wind", "Hydro", "Nuclear", "Biomass",
               "Coal", "Gas", "Oil", "Diesel"]

marginal_costs **=** [0, 0, 5, 10, 40, 60, 80, 120, 130]

首先,我将这些数据转换成索引中有power_plants列中有marginal_cost的 dataframe。接下来,我遍历 dataframe 的索引,并要求用户输入每个电厂的可用容量。每个发电厂的可用容量使用如下所示的globals()方法存储在一个单独的变量中。

使用 for-loop 和 globals()方法请求用户输入每个电厂类型的可用容量。

X 国投资组合中的总可用容量总计为 1800 兆瓦。然后,我创建一个名为Cumulative capacity (MW)的新列,它是Available capacity (MW)的累积和。

包含 x 国每种电厂类型的边际成本和可用容量的数据框架。

我要求用户输入给定时间段 X 国的电力需求。

demand = int(input(“Enter total demand (MW): “))

我们假设demand是 1500 MW。

现在,数据准备好了,我可以开始编译价值排序曲线的构建模块了。在优值排序曲线中,发电厂以条形图的形式按发电边际成本的升序排序。条形的宽度代表每种发电厂类型的可用容量,高度代表相应的边际成本。由于条形的宽度不相等,它们看起来与正常的条形图有很大的不同。

第一个条形(此处为 Solar)在 x 轴上的刻度位置与其Available capacity (MW)的一半相同。对于另一种电厂类型,位置的计算方法是:直到它之前的电厂类型的Cumulative capacity (MW)和它的Available capacity (MW)的一半之和。我准备了一个 for 循环来获得dfxpos列中的这个位置,如下面要点的第一部分所示。

接下来,我分析需求在 x 轴上的位置。功率需求与累积功率供应在 x 轴相交的位置决定了cut_off_power_plant。反过来,cut_off_power_plant的边际成本决定了在它之前的所有参与批发市场的发电厂的清算价格。这个批发市场的电力购买者都将支付同样的价格。

顶部的 For 循环确定了 x 轴上各电厂类型的条形的位置。底部的 cut_off 函数确定与电力需求相交的电厂类型。

生成优次曲线的代码如下所示。

函数来生成电力市场中的优序曲线。

结果图如下所示。

给定电力市场的优序曲线。x 轴代表发电厂的可用容量。y 轴代表每个发电厂的边际成本。垂直的红色虚线表示与燃气相交的需求。水平的红色虚线代表天然气发电的边际成本,它决定了市场清算价格。图片作者。

在结果图中,1500 MW 的功率需求由垂直的红色虚线表示。这与所有电厂按边际成本升序排列时,燃气供电相吻合。因此,天然气发电的边际成本,即 80 美元/兆瓦时,是市场清算价格。这意味着在 X 轴上的垂直红色虚线之上和之前的所有发电厂,即太阳能、风能、水能、核能、生物质能、煤和天然气需要输送电力来满足需求。所有这些公司都按照天然气生产的边际成本来定价。

优序效应

当电力系统的需求水平发生变化时,批发电价也会做出相应的反应。这一点在下面的 GIF 图片中有所展示。为了获得 Python 中的动画,我使用了来自ipywidgetsinteract函数,如下所示。我还使用了IntSlider小部件将demand设置为用户自定义的参数来进行交互。为了避免拖动滑块时闪烁,我将continuous_update设置为假。

from ipywidgets import *demand = widgets.IntSlider(
                value = 100,
                min = 0,
                max = df[“Available capacity (MW)”].sum(),
                step = 10, 
                description = “Demand”,
                continuous_update = False #False to avoid flickering
                )interactive_plot = interact(merit_order_curve, demand = demand)

动画展示了当需求变化时切断发电厂和批发电价的变化。图片作者。

如上面的 GIF 图所示,当需求增加时,需要调度更多位于价值顺序右侧的电厂,这增加了清算价格,反之亦然。如果投资组合中有更多的可再生能源发电厂,那么更多的需求份额将由可再生能源满足。这往往会降低电价,这被称为优序效应

边际减排成本曲线

减排成本 是企业为消除或减少生产过程中产生的外部性(负副产品)而承担的成本。边际减排成本衡量减少一个额外外部性单位的成本,例如二氧化碳排放。价值排序曲线是基于按边际生产成本对技术进行排序,而边际减排成本(MAC)曲线是基于按边际减排成本的升序对不同减排机会进行排序。

在 MAC 曲线中,条形的宽度代表任何技术或选项的温室气体(GHG)减排潜力。条形图的高度代表在给定的机会下,减少一个额外单位 GHG 排放量的成本。

包含不同机会的缓解潜力和边际减排成本的数据框架。数据基于作者的假设。

本例中的数据基于我的假设,可以被视为虚拟数据集。Python 中 MAC 曲线的实现类似于优级曲线的实现。

x 国某一年的 MAC 曲线。条形的宽度代表缓解机会的年度 GHG 减排潜力。条形图的高度代表在给定年份减少 1 tCO2e 排放量的估计成本。图片作者。

如上图所示,用 LED 灯替换旧灯是最具成本效益的脱碳措施。某些机会的负边际减排成本意味着当实施这些措施时有净成本节约(或利润)。当我们向 MAC 曲线中 X 轴的右端移动时,减少一个单位的 GHG 排放量会变得更加昂贵。在 MAC 曲线上最宽的横条,重新造林具有 GHG 减排的最大整体潜力。

因此,MAC 曲线允许在同等条件下比较不同部门(此处为电力、交通、建筑、林业和废物)的衡量标准。虽然 MAC 曲线提供了对各种缓解机会的潜力和成本效益的关键初步评估,但将这种评估与来自其他建模工具的见解相结合,为制定政策和投资决策提供了坚实的基础。

结论

随着我们正在经历全球能源转型,燃烧更多的化石燃料来满足我们的能源需求将导致很快超过我们需要遵守的排放预算,以满足全球气候目标。已经存在技术上成熟、经济上可行的机会,可以大幅减少能源和非能源部门的 GHG 排放量。此外,由于规模经济,成熟的减排技术(如风能和太阳能)每年都变得越来越便宜。

重要的是利用已被证明的缓解技术选项来大幅减少排放。缓解方案有自己的一套成本和收益。在这篇文章中,我讨论了可再生能源和化石燃料在一个国家的电力系统组合中的份额如何影响批发电价,其概念是优值曲线 ( 基于边际生产成本)。我还介绍了如何根据边际减排成本曲线评估一个国家的减排潜力和减排机会的成本效益。这些曲线在 Python 中的实现可以在这个 GitHub 库中找到。

美人鱼:快速而轻松地创建图表

原文:https://towardsdatascience.com/mermaid-create-diagrams-quickly-and-effortlessly-d236e23d6d58?source=collection_archive---------1-----------------------

如果你曾经试图解释任何复杂的东西——无论是算法、代码基础结构还是项目计划——你可能知道好的图表有多有用。

你怎么会不想使用一个叫做美人鱼🧜‍♀️的工具呢?安妮特·巴蒂斯塔日照片

然而,随着我们许多人远程工作,当解释不足时,分享自发的绘画变得更加困难。虽然在办公室进行白板会议很容易,但在网上就要尴尬得多。至少可以说,当你的同事在通话中观看你的共享屏幕时,试图用你的鼠标在绘图应用程序中画出不稳定的方框,或者拖动笨重的箭头,并不好玩。

但是有一种方法可以只用代码快速、轻松地生成所有的图表,所以你再也不用画盒子了

Mermaid 是一个让你在 Markdown 中创建复杂图表的工具——它使用简单的命令和直观的语法。什么都不用画,写下你想看的就行了!

您可以创建流程图来帮助您理解算法,甘特图用于项目管理,饼图以及许多其他常见的图表类型。

Mermaid 为您合理地安排了图表,因此,如果您忘记了流程图中的某个中间步骤,您将不必担心手动重新安排所有方框。

在这篇文章中,我将告诉你使用美人鱼的不同方法,并向你展示它的能力的一些例子。这当然是我最喜欢的创建图表的工具,我希望它也能成为你的!

但是我们为什么需要图表呢?

人类的大脑在理解图像和模式方面比文本要好得多。如果我们看到一个复杂的结构的视觉表现,我们处理它的速度会更快,而且我们也能更好地记住它——这被称为图片优势效应。这也很直观——你可以从条形图或饼图中得出结论,比阅读一系列数字要快得多。

图片也抓住观众的注意力。如果你在没有视觉辅助的情况下给出一个冗长复杂的解释,大多数听众会在一分钟后走神,或者已经忘记了开头。如果你包括一个图表,他们可以找到他们的方式回到你的解释。

如果你只是想理解一些需要比你的工作记忆所能处理的更多信息的东西,它们也有助于你自己画。绘制高层代码基础架构,组件或较低层 OOP 类设计中的信息流可以帮助你提前发现设计错误,这是最便宜的纠正方法。

图的一个非常被低估的用途是在你的 GitHub 库 README 文件中放一个,显示代码架构的**。特别是对于许多人贡献的开源 repos,它允许新人快速熟悉代码库,而不是浏览每个目录中的内容。同样的情况也适用于你公司的新员工——他们真的很希望每个存储库都有一个图表。**

一个设计图表可以把你从这个困境中解救出来——xkcd的漫画

是什么让美人鱼变得如此酷

与许多其他基于 GUI 的图形绘制软件不同,Mermaid 是完全基于文本的。没有痛苦的拖动框和箭头,你只需根据 Markdown 启发的语法键入代码。这使得图表非常容易维护。您将始终拥有生成该图的代码,因此您将不会得到一个漂亮的、但是已经过时的、您不能再修改的图像文件。

这也意味着您可以很容易地看到图表的变化,不像其他任何基于图像的方法。如果您将您的图表置于Git版本控制之下(这很容易,因为它是以文本形式存储的),一个简单的git diff将以人类可读的方式向您展示发生了什么变化

我已经提到过,如果你决定在你的图表中添加更多的东西,美人鱼会为你重新排列这些盒子。这使得在视频通话中即时生成图表成为可能,比在笔记本电脑上手绘要快得多。我发现,如果你花大约半个小时学习语法,你可以完全取代你使用的任何其他图形绘制工具。就我个人而言,我发现用鼠标绘图真的很难,所以对我来说,美人鱼比使用油漆或 OneNote 更好,经过一些练习,它的使用速度比 draw.io 快得多。

美人鱼能做什么?

让我们来看一些美人鱼可以为我们呈现的例子,以及生成它的代码是什么样子的:

graph TD
    A{Does your flowchart have arrows?} --> B[No]
    A --> C[yes]
    B --> D(Add them already)
    C --> E(Yay, what a great flowchart!)
    D -.->|you can even add text to them| A

6 行代码的流程图

起点graph TD表示图形的方向:自上而下,与 LR(左右)、RL(左右)或 BT(自下而上)相反。
您可以通过一个短标识符(这里是 A、B、C)来指定节点,并使用括号来指示它应该具有的形状和文本。您可以指定许多形状,包括圆形、菱形或梯形。要指定从一个框到另一个框的箭头,您只需键入由箭头连接的两个节点 id,这取决于您想要的箭头类型。您可以通过目标节点前的|<text>|向箭头添加文本。

还有一种表达更复杂依赖关系的简写方式,可以节省键入所有箭头的时间,使用&将节点捆绑在一起。要创建子图,你可以在声明完里面的内容后输入subgraph <name><end>。美人鱼使用 CSS 样式进行定制,如果你愿意,你可以改变你的节点的样式。虽然仅仅记忆有点困难,但是如果你熟悉 CSS 的话应该没问题:

graph LR

A & B--> C & D
style A fill:#f9f,stroke:#333,stroke-width:px
style B fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5

subgraph beginning
A & B
end

subgraph ending
C & D
end

子图、时髦的箭头和颜色

另一个例子,这是甘特图,通常用于项目管理:

gantt
title Writing my thesisdateFormat  MM-DD
axisFormat  %m-%dsection Research
Procrastinate           :a1, 01-01, 59d
Do it     :after a1  , 10dsection Write-up
Should I start?     :03-01 , 20d
Ugh ok      : 6d

甘特图总是有助于项目管理

我们必须指定日期格式,我们的部分是什么,任务是什么,什么时候开始,持续多长时间。直截了当,不需要样板陈述。

要了解更多关于语法和不同选项如饼图和状态图的信息,请点击这里查看 Mermaid 语法指南。

美人鱼怎么用?

你可以用很多不同的方式使用美人鱼:

  • 在线实时编辑器
  • GitLab
  • 网站(包括 GitHub 页面)
  • 代码编辑器
  • 浏览器扩展
  • 命令行

完整的集成列表可在此处找到。

在线实时编辑器

这是试用美人鱼最简单的方法,使用他们的在线工具。您可以从模板开始,并验证您的更改如何影响最终结果。你可以直接保存你的图片,或者创建一个链接。作为奖励,它还可以用 cookies 保存您最近的更改,因此您不必担心第二天继续。本文中的所有例子都是用 live 编辑器制作的。

GitLab / GitHub

GitLab 在其 Markdown 文件中默认支持 Mermaid。您可以用下面的语法添加您的图,它会神奇地工作:

```mermaid
graph TD;
A →B;
A →C;
B →D;
C →D;

然而,如果你是 GitHub 的用户,我将不得不让你失望——到目前为止,他们还没有正式与美人鱼整合。有一个项目可以把你的[美人鱼图编译成 GitHub 动作](https://github.com/neenjaw/compile-mermaid-markdown-action),所以你仍然可以不做任何事情把图像插入到一个 README 文件中,让管道来处理。

## 网站

*   如果你有一个用 Yarn 作为包管理器的**网站**,添加美人鱼作为依赖项很简单:

yarn add mermaid


*   如果你**没有捆扎机**,使用以下:


这将使 Mermaid 解析器寻找带有类 Mermaid 的

标签,并将它们呈现在图中。所以你会把它当作

graph TD; A-->B; A-->C; B-->D; C-->D;
```
  • 如果您使用 GitHub pages ,只需将以下内容添加到您的***‘header . html’,*** 或从相对路径下载文件和源代码:
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js">

然后,要在你的降价帖子中包含一个图表,使用

```html
<div class="mermaid">
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
</div>

*   或者,如果你有一个不在 GitHub 页面上的 **Jekyll 站点**,你可以使用[Jekyll-美人鱼插件](https://github.com/jasonbellamy/jekyll-mermaid)。
*   如果你使用 **WordPress** ,[也有适合你的插件。](https://wordpress.org/plugins/wp-mermaid/)

## **代码编辑**

可能编辑支持美人鱼,包括[原子](https://atom.io/packages/markdown-preview-enhanced)、 [VIM](https://github.com/zhaozg/vim-diagram) 和 [VS 代码](https://marketplace.visualstudio.com/items?itemName=bierner.markdown-mermaid)。我个人用的是 VS 代码,扩展 [Markdown 预览美人鱼支持](https://marketplace.visualstudio.com/items?itemName=bierner.markdown-mermaid)对 Markdown 预览中的图形渲染很有帮助。

## **浏览器扩展**

浏览器扩展处理在 Markdown 文档中找到的所有 Markdown 语法,这意味着即使 GitHub 不会向您呈现图形,您仍然可以在本地查看图形。这个扩展可以在 Chrome 和 Firefox 上运行。当然,如果你想让其他人也看到这个图,他们也必须下载这个扩展。

## 命令行

Mermaid-CLI 的存在使得您可以使用 CLI 从输入文件中直接创建图表。它在 Docker 容器中工作,你可以在这里看到如何使用它。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/85d60eb1204a9e383b28d26995dfdb3e.png)

漫画作者 [xkcd](https://xkcd.com/1195/)

# 🧜‍♀️现在去和它一起玩吧,🧜‍♀️

一旦你习惯了,Mermaid 是一个非常有趣的工具,当你试图不用纸来创建图表时,它可以节省很多时间和精力。我希望这个简短的指南对你有用,它可以为你的下一张图节省一些时间!

## 你可以在这里找到更多关于美人鱼的信息:

【https://mermaid-js.github.io/ 

## 相关文章:

*   [博客——使用 Mermaid 语法创建图表](https://www.diagrams.net/blog/mermaid-diagrams)
*   [Mermaid —使用类似 Markdown 的语法创建图表|作者 Trevor Lasn |更好的编程](https://betterprogramming.pub/mermaid-create-charts-and-diagrams-with-markdown-88a9e639ab14)
*   [用美人鱼做图好玩。有时候,一个好的图表是最好的……|奥赞·通卡|更好的编程](https://betterprogramming.pub/making-diagrams-fun-again-with-mermaid-8a2c9ea3e471)
*   [图为代码:美人鱼(freshbrewed.science)](https://www.freshbrewed.science/diagrams-as-code-mermaid/index.html)

# 数据 UI 的消息队列

> 原文:<https://towardsdatascience.com/message-queues-for-data-ui-2d31029ea2?source=collection_archive---------34----------------------->

## IPC 简化大中型应用程序开发

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/5db7d0b224ae5c0236b5041c38de1290.png)

IPC 队列从 UNIX SysV 开始就已经存在了。照片由 Ioannis Nompelis 提供。

## 介绍

数据科学可视化通常倾向于显示单独的图表和图形。因此,有大量的材料涵盖了图形库和生成图表的框架。

大多数高级图表和绘图库允许与数据进行交互,但通常这种交互仅限于 web,并涵盖单个图表。

有时,数据需要具有可操作性。在我工作的行业中,一个典型的例子是交易软件,其中多个图表显示关于给定金融工具或资产的信息,交易者或分析师需要与数据进行交互。通常,与一个图表的交互需要生成事件并暗示其他图表的变化,例如添加标记或开仓或平仓。这种情况可能会变得复杂,因为涉及到不同的特征、图表类型、时间范围、增强的数据处理系统和源数据源。

通常,在最初的开发阶段,常见且简单的策略是在不同的图形用户界面实例之间共享所有图表的引用,无论是其他图表还是设置面板。虽然这种方法很简单,并且可以在小项目中工作,但是它通常会以太多的实例被其他太多的实例引用而结束。当您开始忘记引用时,明智的做法是开始考虑一个更加分布式和松散耦合的架构,比如这里描述的通过消息进行同步的架构。

## 作为 IPC 机制的队列

我假设大多数人已经熟悉队列,因为它们在许多环境中被广泛使用;但是对于那些可能不知道队列是什么的人来说,它基本上是一个异步消息传递系统,其中一个系统、进程或应用程序线程可以与其他系统、进程或应用程序线程进行通信。

一个类似的例子是一个共享的邮箱,你可以随时随地发送信件。这些信件被存储在这个临时空间,即邮箱中,直到另一个进程以一种排序的方式选取这些信件。

通过遵循这种方法,您可以设法分离软件的不同区域,因为流程既可以生成事件,也可以对其他参与者的事件做出反应。

队列可以实现一对一、多对一或一对多的通信路径,它们也可以实现总线,这就是我在这里描述的情况。

## 实现总线的队列

在简单的队列中,一个进程传递消息,另一个进程使用消息,作为进一步的发展,我们可以使所有消息在一个总线中对所有进程可用,这样每个进程可以选择与其目的相关的消息。

当在用户界面的上下文中讨论总线时,我想到了两个参考::D 总线(在现代的 Linux/UNIX 系统中)和 ToolTalk(在传统的 UNIX 系统中)。这些可能是图形环境使用的最广为人知的总线框架,允许跨组件的通信。

另一种选择是消息队列系统的众多库和实现之一。其中一些面向集成完整的系统,而另一些旨在充当应用程序中不同模块的通信层。

虽然这些替代方案功能强大,具有许多特性和功能,但它们很可能被过度设计为只交流应用程序的某些部分。他们需要部署和管理服务,并提出了一个不太友好的学习曲线。

不太为人所知的是,UNIX 系统从 80 年代中期就提供了这样的消息队列作为内核服务:最初的 System V 队列和它的现代对应物 POSIX 队列;两种机制是等效的。

## 系统 V IPC

1984 年,美国电话电报公司发布了 UNIX System V,其中包括一套 IPC(进程间通信)机制。这些机制有三种:共享内存、信号量和消息队列。虽然它们现在可以被视为标准功能,但这些改进在当时是一个重要的里程碑。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/ca6287ad4b266e24a6a92045accc413c.png)

SysV 队列在类 UNIX 系统中仍然可用。图片由作者准备。

正如已经提到的,现代的替代品是存在的,但是基本原理还是一样的。我决定尝试一下遗留的 System V UNIX 消息队列,并使用它们编写这个示例。大多数(如果不是全部的话)UNIX、类 UNIX 和 Linux 系统都支持它们。具体来说,FreeBSD 和 Linux 都支持它们,这是我主要使用的两个平台。

## 系统 V 消息队列

UNIX 队列由一个数字键标识,对队列的访问受标准 UNIX 权限的限制。因此,任何知道数字键并具有适当权限的进程都可以访问队列。

鉴于该机制的简单性,所需的代码非常简单。您通过`msgget`获得一个处理程序,以便稍后使用`msgsnd`和`msgrcv`函数发送和接收消息。额外的`msgctl`可用于进一步控制队列的参数和状态。

为了实现总线,使用应用程序范围的已知队列创建一个中央进程,该队列从所有客户端接收寄存器和消息,以便稍后将消息分发到所有客户端。因此,总线是分发接收到的消息的消息接收中心。订阅不同类型消息的替代方案也是可用的。

这些模式可以在具有更复杂和精密的队列实现的大型 web 应用程序中找到,但是 UNIX 队列的吸引力在于它们的简单性,并且它们已经可以使用了。因此,该模式可以很容易地应用于单个应用程序。

缺点是,它们只对小消息和可能的轻负载有效(虽然我没有执行任何性能测试),但是在用于用户交互的模块之间的用户界面通信的上下文中,它们就足够了。

## 带有队列的 X11 示例

我提供了一个基本总线系统的源代码,该系统通过一个中心集线器将消息分发给所有客户机。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/0b3e71272c2ec5e7f3cd03f206bd846b.png)

使用经典 UNIX 系统工具(Motif 和 SysV 队列)的模式实现示例。图片由作者准备。

如图所示,客户端向总线管理器注册,之后消息可以被传送到那些已经注册的客户端。在我们的示例中,第四个客户端没有注册,因此没有接收到任何消息。实用程序命令 ipcs 允许检查系统中的现有队列。

## 源代码

下面的代码实现了总线驱动程序事件。实现总线的进程监听客户端连接,注册这些客户端的队列键,并将接收到的消息分派给所有参与者,从而实现简化的总线。关键在于,当每个客户机注册时,它向总线管理器提供它的队列信息,这样它就可以从其他客户机向它们发回消息。

该代码应包括被认为是完整的其他特征:允许总线清除不再活动的队列的保持活动消息,可以连接的客户端数量的更好管理,在程序退出时终止的队列,以及避免客户端注册两次的检查。为了简单起见,代码没有这些特性。如果需要,它们很容易合并。

它所包含的示例是 X11 代码,用于说明队列如何通过 POSIX 线程与 X11 用户界面分离,因为这是应用程序上下文中的常见场景,通常在 Internet 上的示例中找不到。

这个例子是用 ANSI C 和 Xt/Motif 编写的,这可以被认为是一个遗留的技术栈,但是使用任何其他更新的技术,这个设计模式同样有效。

完整的代码库可以在[这里](https://github.com/marioemmanuel/messagequeues.git)找到。

bus.c 实现

client.c 实现

# 扰乱 AWS 端点 URL

> 原文:<https://towardsdatascience.com/messing-with-aws-endpoint-urls-aa56e6c9627?source=collection_archive---------40----------------------->

## AWS CLI 不知道的不会伤害它。

![](https://gitcode.net/OpenDocCN/towardsdatascience-blog-zh-2021/-/raw/master/docs/img/8e7a717db971a02a58a3d6f34f1457d7.png)

照片由[this engineering RAEng](https://unsplash.com/@thisisengineering?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/technology?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)

如果您键入`aws s3 ls s3://my-bucket`来列出一个 S3 桶的内容,那么您会期望连接到真正的桶并列出它的内容,这是完全有道理的。

但是没有硬性规定说你*必须*连接到真正的桶。事实上,有一个简单的参数[可以传递给上面的 CLI 命令,从而轻松地连接到您选择的任何 URL。](https://docs.aws.amazon.com/cli/latest/reference/)

请考虑以下情况:

$ aws s3 ls s3://my-bucket --endpoint-url http://localhost:5000


注意包含了指向端口 5000 上的本地主机的参数`--endpoint-url`?我们不会在某个地方的 AWS 服务器上列出`my-bucket`的内容,相反,我们会检查我们自己的机器,寻找能够响应`ls`命令的类似桶的资源。

这就引出了一个问题:为什么 AWS 要像这样公开一个参数,为什么我们要使用它?

事实证明,两者都有很好的理由。在本文中,我们将讨论两种情况,在这两种情况下,搞乱`endpoint_url`是有用的。

# 用例 1:本地测试

任何在本地测试过他们代码的人都知道当调用外部依赖时情况会变得多么令人担忧。例如,我们同意单元测试最好是*而不是* *实际上*将测试记录插入到我们的数据库中。

为了解决这个问题,我们有一些策略。我们可以[嘲笑](https://en.wikipedia.org/wiki/Mock_object)。我们可以做 T21 的猴子补丁。或者我们会不愉快地在代码中添加测试专用的逻辑。

通过运行 AWS 服务的[本地实例](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html),然后将代码中的`endpoint_url`参数设置为 localhost 和正确的端口,我们可以轻松有效地模拟服务。

让我们来看看实际情况。

## 使用 moto 的示例

作为一个完整的例子,我会提到优秀的 moto docs,这是一个专门用来模仿 AWS 服务的包。

Moto 有一个独立的服务器模式,这使得它的工作原理特别清晰。pip 安装包后,启动本地 moto 服务器:

$ moto_server s3

  • Running on http://127.0.0.1:5000/

然后在一段代码中,我们可以使用 endpoint_url 指向本地的 moto S3 实例:

mock_s3 = boto3.resource(
service_name='s3',
region_name='us-east-1',
endpoint_url='http://localhost:5000',
)


现在我们可以在`mock_s3`对象上做任何我们想做的操作,而不用担心改变实际存储桶的内容。

# 用例 2:无缝集成

鉴于“云的兴起”,云服务的 API 变得无处不在也就不足为奇了。不管是好是坏,AWS S3 API 现在是“对象存储领域事实上的标准”[ [1](https://min.io/)

当多种技术采用相同的标准时,集成每一种成对组合的痛苦就消失了。

[lakeFS](https://lakefs.io/) 、 [MinIO](https://min.io/) 和 [Ceph](https://ceph.io/ceph-storage/object-storage/) 是说 S3 语的技术的例子。或者更准确地说,它们与 S3 API 的一个有意义的子集保持兼容。

因此,任何希望连接到 S3 的工具也可以通过端点 URL 与这些工具无缝集成!

## 以 Spark 和 lakeFS 为特色的示例

Spark 是经常与 S3 互动的技术的一个例子。最常见的是将数据读入数据帧,进行一些转换,然后将其写回 S3。

lakeFS 旨在增强对象存储上的数据湖的功能,使分支、合并和恢复等操作成为可能。

如果您想同时利用 Spark 和 lakeFS 的优势,并且无法设置自定义端点 URL,那么就必须在两者之间开发一个定制的集成。

幸运的是,通过可配置的端点,集成变成了一个[单行程序](https://docs.lakefs.io/using/spark.html),将 Spark 的 S3 端点指向一个 lakeFS 安装:

spark**.sparkContext.hadoopConfiguration.set("fs.s3a.endpoint",** "https://s3.lakefs.example.com")


现在从 Spark 访问 lakeFS 中的数据与从 Spark 访问 S3 数据完全一样!

# 最后的想法

虽然只是对这个主题的介绍,但希望您对什么是端点 URL 以及何时改变它们的默认值有更好的理解。

*注:本文首次发表于 2011 年 4 月 20 日 lakeFS 博客* *上。*