TowardsDataScience-博客中文翻译-2021-九十三-
TowardsDataScience 博客中文翻译 2021(九十三)
单元 1)爬山器—优化
非经典最优化课程
涵盖爬山及其许多变种:简单,贪婪,随机,等等!
大家好,欢迎回到这个关于非经典优化的完整课程!在这篇文章中,我们将从第一个算法开始:爬山!如果您是本课程的新手,您可以查看下面的概述!
首先,什么是爬山?爬山属于局部搜索领域,其目标是找到目标函数的最小值或最大值。该算法被认为是局部搜索,因为它通过相对于其当前位置的小步前进来工作,希望找到更好的位置。
目录
- 概述和基本爬山算法
- 常见问题
- 登山者的变种
- 构建我们的实现
- 测试目标函数
- 代码
- 结论
概述和基本爬山算法
爬山者得名于类似于徒步旅行者通过走向山的下一个最高部分爬到山顶。徒步旅行者不是走下山坡到达山顶,而是向山坡迈一步,使得下一步在海拔上比前一步更高。
我们可以通过从当前位置向不同的方向步进,并向最大化目标函数(或以最小化的方式最小化)的方向移动,将这种思想带到优化领域!因为爬山器总是向比当前位置更好或相等的位置移动,所以爬山器被认为是一种贪婪算法。例如,如果给我们下面的二维问题:

作者图片
我们的目标是找到最大值点,它出现在(0,0)。爬山者如何找到最高点?首先,我们选择一个随机的初始点,在每个可能的方向上迈出一步,然后移动到函数值最高的位置。对于这个问题,我们对每个位置有八种可能的移动。两个仅用于在 x 方向移动(正向和负向);两个仅用于在 y 方向移动(正向和负向);四个用于在 x 方向(正向和负向)和 y 方向(正向和负向)移动。在我们测试了接下来的八个位置后,我们选择一个最佳的位置。下面左手边描绘的就是这个场景。我们的初始点是红色的,周围的八个彩色点是可能的前进方向。但是,请注意,步进的方向是黄色的左上角点,因为它产生了最佳值。我们简单地重复这个过程,直到收敛。在右手边我们有一个首字母
例如,下面左手边的图片。我们的初始点是红点。我们可以在蓝色向量中看到爬山者在蓝色点达到最终全局最大值之前的步伐。


基本的爬山算法可以描述如下。首先,我们随机选择一个初始状态,然后我们选择不同的变量来步进,步长,然后测试所有生成的新位置。测试后,我们选择最佳位置,并重新开始该过程。当我们的算法已经达到用户定义的最大迭代次数时,或者当算法停滞/收敛时,终止发生:当在指定的迭代次数之后当前位置没有改变时。

常见问题
当实现算法的基本版本时,会出现三个常见问题:
- 对于多维问题,我们在步入之前选择测试多少个变量?
- 每个方向的步长应该是多少?
- 卡住的问题
如果我们有一个 n 维目标函数,存在个 2^n 个可能的状态移动要考虑。对于上面在三维高斯分布中给出的例子,有 8 个可能的移动,2 = 8。这呈指数增长的原因是,我们必须考虑“对角线”移动,即我们在考虑多个变量而不是一个变量之间的相互作用的方向上移动。现在显而易见的问题是,当 n 大于 15 左右时,在每个时间步长评估 2^n 点是一项极其昂贵的任务。我们可以通过不考虑“对角线”状态来减少这种情况;相反,我们只考虑单变量移动。在这个框架中,假设没有对角移动,有 2*n 个可能的单态移动(每个变量向前和向后)。这样,对于贪婪的方法,我们需要在选择最佳状态之前测试出 2*n 个可能的状态。对于大的状态空间,或者当我们允许有限数量的函数求值时,对于较大的 n,这也会消耗时间。处理这个问题的一个常见方法是通过简单地从给定的变量集中随机抽取样本,并且只测试那些变量中的步骤,来减少要步进的变量的数量。
另一方面,步长是一个棘手的问题。大步长可以覆盖地面,但通常会跳过最小值或最大值。小的步长可以通过不跳过最大值或最小值来确保算法具有良好的收敛性;然而,如果当前位置很远,那么在算法到达该位置之前,将需要多次迭代。我们稍后将讨论这一点。
因为爬山器是局部搜索,所以在存在许多局部最优的高度模态函数中,它很难找到最优/全局最小或最大点。使用标准算法,该算法没有能力逃脱这些局部陷阱。此外,如果达到所有邻近点都具有相同函数值的平台,则该算法将收敛,因为如果它们都具有相同的结果,则该算法将不知道要进入哪个方向。
爬山器变体
如上所述,有三个常见的问题困扰着爬山者。作为回应,已经开发了许多变体来处理这些问题。到目前为止,针对爬山者描述的算法被称为最陡爬坡爬山者、,其中传统的简单爬山者逐个测试每个位置,并选择第一个产生更好值的位置,而不是测试所有相邻位置并移动到最佳位置。随机爬山器是另一种流行的变体,它根据概率选择并移动到一个位置。这是三个众所周知的变体——下面将要描述的其余变体没有传统的名称。
一个真正的贪婪方法将不得不在每次迭代中评估 2^n 或者 2*n 个可能的位置,这取决于是否允许对角线。如前所述,当 n 较大时,这会浪费计算时间。为了减少这种情况,我们既可以检查单个随机变量,也可以检查几个随机变量。这可以称为随机变量选择爬山法。
另一方面,假设我们总是选择评估所有 2*n 个可能的位置,那么我们的算法将总是从初始点到收敛确定性地遵循相同的精确路径,因为它将总是选择局部最佳邻居。当算法陷入局部最优时,这可能是有问题的。为了引入方差,算法可以移动到由它们的函数值概率确定的位置。以下面的例子为例,我们有五个可能的邻居。我们计算它们的函数值,并将每个值除以函数值之和,以获得一个概率:

作者图片
现在,在选择最大的 F(x)值时,我们不再总是选择邻居 2,而是从给定的概率中进行采样,以给路径增加一些方差。这可以称为随机邻居选择爬山法。明显的缺点是需要更多的迭代才能收敛到一个点;然而,包含的方差可能是跳出局部最优所需要的。
步长的选择对算法的收敛性和质量有很大的影响。保持步长静态简化了事情,但是不允许算法适应环境。一种解决方案是使用步长缩减计划,以允许在探索的早期使用较大的步长,在实现收敛的后期使用较小的步长。存在许多不同的缩减计划,从线性、指数、余弦到均匀。这些变体可以被称为预定步长的爬山者。
构建我们的实现
对于我们的实现,我们将创建两个不同版本的 Hill Climber。首先,我们将创建经典的最陡爬坡机,它将创建 2*n 个可能的邻州来进行评估,然后选择最佳的邻州。第二,我们将混合使用预定步长和随机变量选择。我们的算法不是在当前位置测试每一个可能的步骤,而是在每次迭代中随机选择一组变量,并为这些选择的变量测试步骤。对于调度步长,我们的算法将采取随机步长,通过统一步长在所选变量中向前或向后。统一步长的工作原理是在两个值之间采取统一的随机步长。例如,我们从 U(-0.1,0.1) 中取一个随机值,将其用作步长。
对于我们的最陡爬坡者,我们需要定义一个步长。因为每个变量的域可能不同,所以我们不能假设一个全局的静态步长;相反,我们需要为每个变量定义一个步长。经典的方法是对每个变量取总域大小的百分比步长。例如,假设在一个 2 输入维度问题中,我们对 x=[-5,5] 和 y=[0,15] 有以下域边界。因此,我们的变量的下界将是 [-5,0] ,而上界将变成 [5,15] 。我们可以创建总界限,衡量每个变量的定义域空间有多大,用每个变量的下界减去上界: [5-(-5),15-0] = [10,15] 。然后我们取百分比步长,比如说 0.01 。然后 0.01*[10,15]=[0.1,0.15] 。这些是我们现在对于各个变量的步长, 0.1 对于 x 和 0.15 对于 y 。以下是最陡爬坡的实现:
对于我们在预定步长和随机变量选择之间的混合的第二个实现,我们需要启用一些微小的修改。首先,我们将只从可能的变量中选择一个随机样本,而不是检查所有可能的 2*n 个可能的邻居。此外,我们将实现统一步长,而不是通过静态步长进行加减,这将在步长之间加减一个统一的随机值。下面是实现过程:
测试目标函数
现在是时候在一些真实的目标函数上测试我们的两个实现了!如果你还记得课程概述页面,我们将在三个函数上测试我们的算法:球体、舒伯特和蛋架——每个函数的目标都是找到全局最小值。球函数的全局最小值对舒伯特来说是 F(X)=0 , F(X)=-12870.88 (不同 n 的变化),对蛋架来说是 F(X)=-959.64 。球是一个凸函数,没有局部最小值,而舒伯特和 Eggholder 都是高度模态的,有许多局部最小值和最大值。



对于 Sphere 和 Shubert,我们将测试 n=1000,并允许 100,000 次函数求值。因为算法的每次运行都依赖于初始随机位置,所以将对每个函数重新运行 30 次,并将保留平均结果用于比较。此外,将在以下值测试步长百分比:【1,0.5,0.1,0.01】。现在,人们应该注意到,步长百分比 1 对于爬山者的局部搜索来说是非常大的,因为步长的最大值大到足以跨越整个域;然而,每当我们测试我们的统一步长时,这将帮助我们创建探索,因为步长是从整个域中统一采样的,而不是静态的。
测试最陡下降
首先,让我们测试我们的最速下降算法。因为该算法测试 2*n 个可能的邻居,并且 n=1000,所以每次迭代将测试 2000 个函数评估。因此,最大迭代次数为 50 次。结果如下:


正如我们所看到的,只有 100,000 个函数评估,最速下降攀岩者找不到最小点,甚至接近实际的全局最小点。这可能是因为我们只运行了 50 次迭代,让我们运行 1000 次迭代:相当于 200 万次函数求值!结果如下:


即使我们运行了 20 倍以上的迭代,最终找到的最小点仍然远离实际的全局最小点。请注意,对于球体函数,步长百分比为 0.5 和 0.1 时性能最佳,而对于舒伯特函数,步长百分比为 1 时性能最佳。这表明,步长百分比是另一个需要针对特定问题进行调整的超参数。
用均匀步长检验随机变量
现在让我们用统一的步长测试我们定制的随机变量!为了简单起见,我们将只在 10 个变量中创建邻居,这本身也是另一个需要优化的超参数。因此,在每次迭代中,我们将测试 10 个邻居,每个随机采样变量一个,相当于每次迭代 10 次函数求值。因此,最大迭代次数将是 10,000,以达到最大 100,000 次函数计算。结果如下:


正如我们所看到的,这个爬山器的自定义实现比传统的随机下降爬山器做得更好。这展示了随机步长的威力。尽管我们在这里使用了 uniform,但也可以从高斯分布或任何其他类型的分布中进行采样。球函数的最佳步长百分比是 0.1,而舒伯特函数的最佳步长百分比是 1 和 0.5。为了保持事物的相似性,让我们看看该算法在 200 万次函数求值中的表现如何:


正如我们在上面看到的,在 200 万次评估中,全局最小值几乎是在球面函数的步长百分比为 0.01 和舒伯特函数的步长百分比为 1 的情况下找到的。
测试蛋架功能
有人可能会反对最速下降由于域空间的高维数而表现不佳,在前两个目标函数中 n=1000。相反,让我们在 2 维输入 Eggholder 函数上测试这两种算法,限制为 1000 次函数求值。结果如下:


正如我们从上面可以看到的,最速下降法在寻找一个好的最小值方面做得很好,但是平均值相对较差,并且标准偏差较大。另一方面,习惯爬山的平均值是最陡下降的两倍;此外,它还具有较低的标准偏差和绝对最小值。
代号
如果你对上面的算法感兴趣,在我的 GitHub 资源库中可以找到这门课的所有代码!
https://github.com/OUStudent/NonClassicalOptimization/tree/main/Unit 1: Hill Climber
结论
总之,爬山法是一种在机器学习中发现的局部搜索方法,用于优化-找到目标函数的全局最小值或最大值。登山者有许多变种,因为其经典形式的斗争。在这篇文章中,我们比较了经典的最速下降爬山器和定制的随机变量选择器以及统一步长的爬山器。后者在三个非常高级的目标函数上优于前者:球体、舒伯特和蛋架。
请继续关注本系列的下一单元,我们将讨论模拟退火!
单元 1)最优化理论
进化计算课程
最优化理论和四种主要最优化问题的概述
大家好,欢迎回到进化计算的完整课程!在这篇文章中,我们将从课程的第一单元开始,最优化理论。在上一篇文章中,我们介绍了课程的基本概况,你可以在这里查看:
首先,最优化理论到底是什么?最优化理论是数学的一个分支,致力于解决最优化问题。优化问题是数学函数,我们希望最小化或最大化函数值。这类问题在计算机科学和应用数学中大量存在。找到这些问题的良好解决方案是赋予数据科学和机器学习模型良好准确性的原因,因为它们本身是建立在用于找到良好解决方案以最小化误差函数的数值方法之上的。机器学习中常见的优化问题的例子是最小化 MSE、MAE、交叉熵等。
目录
- 概观
- 无约束问题
- 受限问题
- 多解问题
- 多目标问题
- 基准测试功能
- 结论
概观
简单地说,最优化理论可以归结为三个问题:
1.目标函数是什么?
2.感兴趣的变量集是什么?
3.约束集是什么?
在选择算法时,了解你的目标函数在维数、定义域和局部极值计数方面的情况是至关重要的。此外,有必要了解每个变量的数据类型:离散型还是连续型?最后,了解约束集对于限制您的算法在域空间中只搜索可行解是很重要的。
如果你还记得微积分,optima,也称为临界点,是定义域空间中的点,使得函数在该点的斜率为零。这些点通常被称为极值,分为最小值或最大值。极值可以分为三四种主要类型:
1.弱极值
2.强极值
3.全局极值
4.鞍点

作者图片
弱极值是临界点,使得周围的相邻点具有相同的函数值,从而创建一个平台(如上图所示)。强极值是临界点,它具有相邻点的最大(最大化)或最小(最小化)函数值,从而形成山谷或山脉。全局极值是强极值点,使得它的函数值对于所有强极值点是全局最大的(对于最大化)或最小的(对于最小化)。最后,鞍点,我们不会在课程中过多讨论,但它们确实存在。鞍点代表向上凹和向下凹趋势之间的拐点。因为我们不会处理鞍点,所以我不会涉及太多他们的直觉。
有四种主要类型的优化问题,无约束、约束、多解和多目标。
无约束问题
无约束问题就像他们听起来的那样,无约束。在这些函数中,给定一组变量,其定义域是连续的、离散的或两者兼有,我们希望最小化或最大化函数。这里我们有一个定义:

作者图片
受限问题
就设置而言,约束问题类似于无约束问题,但是具有输入空间的唯一约束。这里我们有一个定义:

作者图片
作为一个说明性的例子,下面我们有一个这些组件的例子。首先,我们有由 A 表示的目标函数。然后我们在右下角的点 E 得到全局无约束最小值。然而,我们引入由线 D 表示的不等式约束,其中在方格区域中的任何值都是不可行的。因此,我们只剩下领域的一部分来处理。点 B 和 C 是可行域空间中的两个临界点。然而,约束环境中的下一个全局最小值将在点 F 处。因此,在这种情况下,我们希望我们的算法避开不可行域空间,找到下一个最佳最小值。

作者图片
下面我们有一个在约束环境中两个变量之间的定义域空间相互作用的例子。这里我们可以看到 X 和 Y 是连续变量,范围在 0 和 2 之间。然而,我们添加了一个约束,使得当 X 趋向于 2 时,Y 减小;但是如果 Y 趋向于 2,X 开始减小。这可以通过蓝色半环看出,蓝色半环代表可行输入空间,透明背景代表不可行域空间。

https://en.wikipedia.org/wiki/Nonlinear_programming
多解问题
多解问题是不存在唯一全局极值的函数;取而代之的是捆绑在一起的全球解决方案。在这些情况下,我们希望返回所有绑定的全局解决方案(因此称为多解方案),以便我们有一系列选项可供选择。在实际应用中,我们可能希望最小化某种类型的成本函数,我们发现存在多种解决方案。我们的目标是返回这些多解,以便我们能够选择变量值的哪个点或向量最符合我们的兴趣。这里我们有一个确切的定义:

作者图片
下面我们有一个多解类型问题的例子。它是正弦波函数的一个变体。正如我们所看到的,我们已经找到了这个问题的全局最小值和最大值。然而,请注意,并不是所有的多解问题都像正弦波一样周期性地出现,在正弦波中很容易预测下一个值,这些束缚的全局极值很可能在整个域空间中是虚假的,并且没有模式。

作者图片
多目标问题
多目标问题(MOP)是指我们有许多不同的目标,或者优化问题,我们需要同时解决。解决这类问题有两种主要方法:
1.加权聚合
2.帕累托最优
加权聚合是所有目标函数的简单聚合。我们简单地对每个目标函数求和,乘以相关的权重值,并尝试最小化或最大化该和。通常假设权重之和等于 1。这里我们有一个示例聚合函数,我们希望同时最小化 F1 和 F2。请注意,每个函数都有一个关联的权重值,其中较大的权重值表示哪个函数的影响最大。

作者图片
下面是加权聚合的确切定义:

作者图片
帕累托最优是通过在支配结构中对可能的解决方案进行分类来解决 MOP 的另一种方法。支配是区分好的解决方案和坏的解决方案的一种方式。支配主要有两种类型:强势和弱势。当一个可能的解决方案在所有目标上等于或优于另一个可能的解决方案,并且至少在一个目标上优于另一个可能的解决方案时,强支配就发生了。这是教科书上的确切定义:

作者图片
另一方面,弱控制表明一个可能的解决方案在所有目标上并不比另一个解决方案差。这里有一个确切的定义:

作者图片
作为这两种支配类型的例子,这里我们有一个教科书上的数字,其中有些数字被我夸大了:

作者图片
x 轴上是 F1 值,y 轴上是 F2 值。在这种情况下,我们希望最小化 F1 和 F2,因此当 F1 == F2 == 0 时,绝对最佳解出现在原点。点 F 用红色圆圈表示在左下方。首先我们有所有被 F 强支配的点,被 b 部分支配,这些点被 F 强支配,因为它们在 F1 和 F2 中有更差的值。另一方面,A 和 D 部分仅受 F 的弱支配,因为它们至少在一个目标上有更好的值。部分 A 在 F2 方面比 F 差,因为它的函数值较大,但在 F1 方面比 F 好,因为它的函数值较小。D 部分则相反,它在 F1 方面比 F 差,但在 F2 方面比 F 好。最后,我们有 C 部分,它支配着 1、2、4 部分和 F 点,因为这一部分至少在一个目标上优于所有其他功能,同时与其他目标相等或更好。
引入支配这个概念的全部目的是找到帕累托前沿。Pareto-Front 是一组决策向量,使得每个向量弱地支配彼此,但是强地支配输入空间中的所有其他向量。这里我们有一个两个目标的帕累托前沿的例子:

作者图片
我们可以看到,当 F1 == F2 == 0 时,最小值出现;然而,问题是 F1 的最小点可能和 F2 的不一样。因此,我们得到了一系列可供选择的值。如前所述,Pareto 前沿弱支配自身,而强支配域空间的其余部分;通过这种方式,返回前端使用户能够选择最适合其需求的解决方案。例如,如果 F1 对最小化 F2 更重要,那么你可以选择有利于 F1 的弱劣解。这是另一个离散问题的帕累托前沿的例子:

作者图片
我之所以包括上面这张图片,是为了展示帕累托前沿并不总是像以前一样是一条漂亮的凸曲线,而是可能是零星的,难以预测的。
基准测试功能
基准测试函数是为测试算法而设计的数学优化问题。这些都是人为设计的问题,没有应用,但设计的方式使它们比真实世界的应用“困难”或更“困难”。在文献中,不是基于可能需要关于主题的广泛领域知识的某些应用来比较算法;研究人员通常通过在这些基准测试函数中找到全局极值的能力来比较函数。有许多不同类型的基准测试函数,下面是文献中常见的三种测试函数:

作者图片

作者图片

作者图片
在课程的后面,我们会在这样的测试函数上测试我们的算法。
结论
总之,最优化问题可以分为四个不同的类别。边界固定不变的无约束问题。区域根据变量值变化的约束问题。多解问题,其中没有唯一的全局最小值/最大值,我们希望返回所有值。最后,我们希望找到帕累托前沿的多目标。
优化问题在众多研究领域中极其丰富,无论是从交通调度、基于不同变量的最大化生产、高效用电到神经网络训练、游戏建模,还是优化数据科学模型。这些情况下的目标是最小化或最大化优化函数,其中每个最优值代表一个可能的解决方案。然而,我们不仅仅想要一个可能的解决方案,我们想要最好的解决方案,全局最小值或最大值。那么我们如何找到这些全局极值点呢?我们可以使用标准的数值方法,如牛顿法或共轭梯度法;或者我们可以使用其他优化方法,如蛙跳法、爬山法或模拟退火法。在本系列中,我们将介绍如何使用进化计算来寻找这些全局极值。

作者图片
为什么我们要首先使用进化计算而不是经典的优化技术呢?经典优化算法的问题通常是它们是确定性的,也就是说你给它相同的初始点,它总是给你相同的输出,它也是顺序的,也就是说算法一次只能使用一个点,它可能需要微分信息,特别是一阶和二阶导数。另一方面,进化计算是一种随机搜索方法,这意味着如果你给它输入相同的初始点,它不能保证输出是相同的,无论是好是坏,它也通过与初始点群体而不是一个群体一起工作来使用并行搜索,当问题不可微时,它工作得非常好。由于这些原因,进化计算通常用于解决 NP 难问题,如旅行商或调度,或者当优化函数不可微时。
单元 1 的优化理论到此结束,请继续关注单元 2 的进化计算介绍,这是一种解决优化类型问题的方法:
单元 2)进化计算简介
进化计算课程
进化计算和遗传算法概述!
大家好,欢迎回到进化计算的完整课程!在这篇文章中,我们将讨论课程的第二单元,进化计算导论。在上一篇文章中,我们讨论了单元 1,最优化理论,你可以在这里查看:
在这篇文章中,我将从整体上介绍进化计算的基本概况。从生物学的灵感开始,我们将看到 EC 如何寻求解决问题,然后我们将移动到进化算法的基本概述并解释主要细节。在下一单元中,我们将真正看到遗传算法的复杂性。
目录
- 生物灵感
- 进化算法综述
- 染色体的表示
- 原始群体
- 选择适应度函数
- 选拔过程
- 复制运算符
- 停止条件
- 进化算法的差异
- 结论
生物灵感
进化可以被描述为一个过程,通过适应、自然选择和选择性繁殖,个体在不同的环境中变得“更适合”。这里有一张查尔斯·达尔文在加拉帕戈斯岛上的日记中描述的著名雀鸟的照片。他注意到了这一物种喙大小的极端多样性。这是一个突变的例子。

https://commons . wikimedia . org/wiki/File:Darwin % 27s _ finches _ by _ Gould . jpg
这是自然选择、适应和选择性繁殖的一个简单例子。

https://commons . wikimedia . org/wiki/File:Darwin smoa _ jirafaren _ adibidea . png
我们可以看到长颈鹿的植被位于高海拔,这使得脖子较长的长颈鹿更容易够到。因为环境的原因,脖子较短的长颈鹿无法进食,因此饿死,自然选择的例子。另一方面,因为只有脖子更长的长颈鹿存活下来,我们有一定程度的选择性繁殖,只有“最适合”的个体才能繁殖。因为只有“最适合”的个体才会繁殖(脖子更长的个体),后代天生就适应环境(脖子更长)。
在进化计算中,我们试图对这些原则进行建模,以找到问题的最佳解决方案。一个问题的每一个可能的解决方案都被表示为一群人中的一个个体,在那里我们对那些可能的解决方案进行适应、自然选择和选择性繁殖,以最终找到问题的最佳解决方案。
进化算法综述
对于规范遗传算法的简要概述,我们从初始种群开始,然后选择如何将这些解编码为染色体,然后选择我们的适应度函数,大多数情况下是优化函数本身,最后我们选择如何选择哪些个体能够生存和繁殖。

作者图片
上面我们有一个关于这个算法如何工作的基本图表。如果你一开始不明白所有的事情,那也没关系,我们以后会回来的。首先,我们随机初始化我们的种群,然后我们选择那些最适合繁殖的。然后,我们通过杂交在两个父母之间交换等位基因,并通过突变引入新的遗传物质。在这之后,我们有某种生存机制来决定谁能生存,谁不能。我们重复这个交配、繁殖和存活的过程,直到达到终止标准。
染色体的表示
细胞是生物体最基本的组成部分之一。每个细胞都是由 DNA 链构成的。染色体是 DNA 的主要结构,包含大量的基因,每个基因代表某种类型的遗传特征。在 EC 中,我们希望将这些特征建模到我们的优化问题中。在进化计算中,一个个体只是优化问题领域空间中的一个点,其中函数的每个变量值由一个基因表示。因此,每个点指的是个体,其中该点的每个组成部分,即变量值,被编码为基因组中的一个基因。基因型描述了个体的基因构成。另一方面,表现型定义了个体在环境中的样子。
例如,优化问题的域空间中的单个点在其基因组中由其变量的分量值表示;然而,它的表现型是函数的曲面空间中的实际点本身。后来,当我们介绍遗传编程时,它不是将个体表示为一个点,而是一个实际程序或决策树的树形图表示。此外,对于其他类型的 EC 算法,表现型可以是神经网络、设计布局、机器人等。关键在于,基因型代表了个体的构成,而表现型代表了个体在其环境中的物理外观。作为一个具体的例子,下面我们有一个点(红色)在其函数空间中的表现型:

作者图片
然而,上述点的基因型由其变量值组成:

作者图片
在 EC 中,代表个体(即输入空间中的点)的经典方法是使用二进制编码。二进制编码是用于编码个体的第一种方法,因为它受到实际 DNA 化合物的启发,其中基因由用于描述氨基酸的三种可能的碱基组成。

https://commons . wikimedia . org/wiki/File:DNA _ replication _ en . SVG
因此,从这个生物灵感中,我们可以用二进制编码我们潜在的解决方案,以最好地代表 DNA。下面我们可以看到,我们用 32 位二进制编码浮点数有三个部分,即符号位,8 位表示指数,23 位表示分数。

https://en . Wikipedia . org/wiki/Single-precision _ floating-point _ format
然而,在现代实践中,二进制表示已经被浮点表示所取代,我们将在下一单元讨论这一点。
简单的二进制表示有两个主要问题。首先,二进制表示导致了所谓的海明悬崖。我们可以看到这样一个例子:

作者图片
测量二进制向量之间差异的方法是通过汉明距离,它简单地计算不相交的比特数。比如第 7 位= 0111,第 8 位= 1000;它们没有相似的位,因此它们的汉明距离为 4。问题是 7 和 8 彼此相邻,但由于它们的比特不同,它们在二进制编码中的汉明距离很大,导致汉明悬崖。作为回应, gray encoding 通过在比特的 NOT 之间进行逻辑 OR 和 and 运算来创建替代编码,从而解决了这个问题。通过使用格雷编码,我们将任意两个相邻数字之间的汉明距离限制为 1。以下是数字 0 到 7 的二进制编码和相应的格雷编码的示例:

作者图片
然而,尽管有这种解决方案,将解决方案编码成二进制是非常耗时的。
原始群体
EC 算法的初始种群非常重要。进化算法(EA)是一种基于群体的搜索算法,这意味着它通过收集初始点并并行搜索这些点来工作。不像标准的数值方法,如牛顿法,你只给它一个初始值,EA 的工作是利用群体的多样性来搜索更好的解决方案。此外,我们还需要一种方法来表示问题的整个搜索空间/领域。如果我们的初始群体仅包括来自位于搜索空间中心部分的值,那么我们的算法将仅在该搜索空间附近找到最优解。
因此,为了确保多样性,我们需要从我们的领域空间中随机均匀采样。现在,每当我们处理约束问题时,其中的边界是不同的,依赖于变量值,我们最初的均匀采样可能会导致不可行的解决方案。为了解决这个问题,我们要么必须为我们的统一采样硬编码这个约束,要么只在整个输入空间采样,但只保留可行的解决方案。
最后,初始种群规模的选择对算法的收敛性和复杂性至关重要。较大的群体规模增加了初始多样性,并能维持这种多样性;然而,它给计算复杂性带来了沉重的负担。另一方面,较小的尺寸降低了初始多样性和世代间保持的多样性,但它需要较少的计算,提供了更快的替代方案。初始种群大小的选择取决于问题的复杂性、适应度函数的计算成本和可用的等待时间。
选择适应度函数
在进化中,格言是只有环境中最适合的个体才能生存。我们如何确定哪些人是合适的?在 EC 中,适应度函数是优化问题本身,其中具有较大值的值具有较好的适应度。在最小化的情况下,我们可以缩放函数值,使得较小的值产生较大的适应值。适应度函数的选择直接取决于需要优化什么问题。从单元 1,最优化理论,有四种主要类型的问题,无约束,约束,多目标和多解。EA 可以处理所有这些类型的问题,但是可能难以处理两个、受限和多个解决方案。因为域在约束环境中是动态的,所以我们的算法可能会遇到不可行解。为了解决这个问题,我们有两个主要的解决方案。一种是为不可行的个体在函数值上增加一个惩罚项。这样,我们降低了不可行解的适应度。惩罚条款的唯一问题是它们改变了我们搜索空间的适合度。这里我们有一个惩罚项的精确定义:

作者图片
另一方面,我们可以调整我们的繁殖算子,使它只产生可行的解;然而,由于许多限制,这可能会成为问题。
最后,进化算法在多解性问题上有问题,因为进化算法倾向于收敛到奇点或一组点。在有许多解决方案需要返回的情况下,EA 可能会在这种情况下挣扎。为了解决这个问题,我们可以引入欧几里德距离,这样一旦找到一个全局极值,我们就添加一个惩罚项,阻止个体接近欧几里德空间中的那个精确点。我们将在后面讨论这个应用程序。
选择
一旦我们有了初始种群并计算出每个个体的适应度,我们需要一种方法来决定谁将被选择来生存和繁衍。在 EA 中,在开发和探索之间有一个权衡。剥削简单来说就是指算法的收敛质量。如果我们选择一种算法,它总是选择最适合的个体来生存,那么我们就表现出高开发性而低探索性。高剥削导致快速收敛;但是,这也可能导致过早收敛。

https://commons . wikimedia . org/wiki/File:健身-风景-卡通. png
例如,在上面的图片中(假设最大化),全局最大值在 B 处,但是如果该位置没有被我们的算法探测到,那么它可能会过早地收敛到 A 或 C,这取决于已经探测了多少输入空间。然而,在图中,初始点位于 A 和 B 之间。因此,它将收敛到 A 或 B,这取决于来自算法的随机机会。与剥削相对的是探索,这种选择类型并不总是选择最适合的个体来交配,鼓励对输入空间的探索。然而,通过鼓励探索,该算法可能具有较差的收敛质量。
在实践中,有时最好两者都利用,开始时探索,然后到最后开发。有许多选择方法,但这里是最受欢迎的:
- 随机选择
- 比例选择(轮盘赌)
- 基于等级的选择
- 锦标赛选择
- 玻尔兹曼选择
选择压力指的是产生一个统一的种群需要多长时间,因此高选择压力会减少多样性,而低选择压力会鼓励探索。在这个系列中,我们将讨论随机选择、比例选择、基于等级的选择、锦标赛风格选择和波尔兹曼选择。然而,在应用中,我们将主要关注随机、比例和锦标赛,因为它们是最常见的。除了这些方法,我们还有一些可以利用的机制,比如精英主义和名人堂。

https://commons . wikimedia . org/wiki/File:Simple _ random _ sampling。PNG
随机选择是一种简单的方法,具有良好的探索品质,因为它随机选择个体生存;但是,正因为如此,它并不能保证当前的最佳解决方案会保留下来,从而导致可能的解决方案丢失。正如我们马上会看到的,它通常与精英主义结合在一起。

作者图片
比例选择,正如我们在上面看到的,通过将每个适应值除以适应值的总和,从适应值中创建一个概率分布。因为选择是基于概率的,所以它确保了良好的多样性,同时也确保了良好的收敛性;然而,因为概率是基于适应值与其余适应值的比例,所以早期具有大适应值的个体似乎可以支配选择过程。

作者图片
为了缓解大的适应值在比例选择中支配再生空间的问题,已经提出了基于等级的选择。除了被选中的概率不是基于原始的适应值,而是基于个人的排名之外,它类似于比例。根据适合度值,个体从最差到最佳排序,其中最佳排序为 1,然后对于具有较高排序的个体,比例线性或非线性地降低。上面,我们有一个基于排名的选择的例子,除了最好的排名最高,最差的排名最低。个人的确切排名取决于您使用的基于排名的选择方程的类型。

https://commons . wikimedia . org/wiki/File:Deterministic-tournament-selection-3 . SVG
锦标赛选择另一方面,随机选择一组个体,其中的数量称为锦标赛规模,从锦标赛中选择具有最佳适应值的解决方案来生存。这样做是为了确保良好的收敛质量,因为最佳的个体被选择,而且通过锦标赛的规模具有多样性的成分。请注意,锦标赛规模 1 基本上等同于随机选择;因此,较小的锦标赛规模可确保良好的多样性,而较大的锦标赛规模可确保选择最佳的适合度值。
玻尔兹曼选择是另一种形式的选择,其中被选中的概率基于模拟退火方程。我们将在下一单元讨论如何在后代和父母之间选择生存时涉及玻尔兹曼选择。
精英主义只不过是从当前人口中挑选出最优秀的一部分,并总是把他们带到下一代。这保证了良好的收敛性,但如果精英比例很大,也会导致较差的多样性。精英主义通常与随机选择或任何其他技术相结合,以获得良好的多样性和收敛性。
最后,名人堂是一种机制,其中群体中的最佳个体被留在名人堂中,在算法停止后,从名人堂中选择最佳解决方案。通过这样做,它允许算法通过随机选择来支持良好的探索,而不用担心失去最佳个体。然而请注意,精英主义和名人堂的区别在于精英主义从人口中挑选出最优秀的 x %的人,并将他们带入下一代;另一方面,名人堂只选择最优秀的个体,并把他们放在一边,因此留下了不被选择生存的可能性,允许良好的探索,同时也不会在技术上丢失每一代的最佳解决方案,因为它存储在名人堂中。
注意,所有这些方法都是基于选择的概率,因此一个个体可以被选择多次以生存和繁殖。
再现
既然我们已经选择了要繁殖的个体,我们需要一种方法在父母之间共享遗传物质并引入更多。在 EA 中,这是通过突变和交叉完成的。杂交是父母双方的基因组混合在一起创造后代的方式。它的工作原理是选择一个交叉点,然后在父母之间的基因组上交叉,产生两个新的后代。我们可以通过下面的例子看到这一点:

另一方面,突变通过随机翻转位来突变后代基因组中的一个或多个基因,从而引入新的遗传物质。在实践中,变异主要用于差的个体,以鼓励探索,而不是对好的解决方案进行太多的变异,以确保良好的收敛性。
停止条件
既然我们已经定义了复制操作符,我们只需重复选择和复制的整个过程,直到满足停止条件。以下是四种最常见的停止标准:
- 最佳适应值没有变化
- 平均健康值没有变化
- 可接受的解决方案
- 已达到最大代数
常用的停止标准是在一定数量的代之后最佳适应值没有变化时。然而,由于最佳解决方案的值会波动,这可能会出现精英主义未被使用或每代未选择最佳解决方案的问题。此外,如果使用精英主义,可以证明在找到更好的解决方案之前,EA 可以在停滞状态下工作很长时间。
如同在最佳适应值没有变化时停止一样,可以改为在平均适应值没有变化时停止,这表明群体已经在某个区域附近收敛。然而,如果选择和繁殖算子鼓励探索而不是开发,那么种群的平均适应值将会以如此大的方差波动,以至于这个条件永远不会满足。另一方面,当找到可接受的解决方案时,可以停止,例如当 MSE 低于 1 时使用神经网络训练,或者当交叉熵对数损失接近零时用于分类。然而,这可能导致过早地退出该算法,在该算法中可能已经找到了未来更好的可能解决方案。
最后,在实践中最常见的一种方法是对算法进行多次模拟,并在达到最大代数时终止。
进化算法的差异
进化计算有许多不同的领域,这里是我们将在本系列中涉及的六个领域及其差异。
单元 3)遗传算法:模拟遗传进化
单元 4)遗传规划(GP):遗传基因的子集,但个体被表示为树或图
单元 5)进化规划(EP):关注行为,进化个体的表现型
进化策略:进化行为、表现型和基因型
单元 7)差分进化(DE):使用距离和单位向量进行繁殖
共同进化:个体通过合作或竞争进化
结论
概括地说,进化算法都是建立在以下设计选择之上的:
原始群体
适应度函数
染色体编码
选拔过程
复制过程
结束
初始群体的重要性在于确保良好的初始多样性,以及群体大小的选择。大多数情况下的适应度函数将是优化问题本身。接下来,我们需要决定如何编码染色体,以及表型是什么。接下来,我们需要选择我们的选择和复制操作符。有了这两个,记住剥削和探索之间的权衡。最后,我们需要选择何时停止我们的算法。
既然我们已经检查了规范遗传算法的初始图中包含的所有材料,让我们回到它:

作者图片
首先,我们随机初始化我们的种群,然后我们选择那些最适合繁殖的。然后,我们通过交叉交换两个父母之间的等位基因,并通过突变引入新的遗传物质。在这之后,我们有某种类型的生存机制来决定谁能生存,谁不能(我们将在后面讨论)。然后,我们重复这个选择、繁殖和生存的过程,直到达到终止标准。
然而,还有一些其他的设计问题,我们没有涉及,即谁将在复制后生存。在简单的算法中,那些没有被选择交配的是那些没有存活下来的,其中后代取代了父母。然而,在一些算法中,生存可能是一个完全不同的选择过程,要么通过混合后代和父母的另一个选择程序,要么在后代和父母之间进行某种类型的“战斗”,以确定谁能生存下来。我们将在后面的单元中讨论这些可能性。因为这一单元是进化算法的介绍,所以没有应用部分,但是在下一部分我们会这样做。
第二单元的进化计算介绍到此结束,请继续关注第三单元,我们将在第三单元讲述遗传算法:
单元 3 应用)用于时间序列分析的进化神经网络
进化计算课程
第三单元的高潮是应用我们的概念来发展一个预测时间序列问题的神经网络
大家好,欢迎回到进化计算的完整课程!在这篇文章中,我们将总结第 3 单元,这是一个非常令人期待的应用,为时间序列分析进化神经网络的权重!
为了更好地理解本材料,你需要知道的概念和材料是神经网络如何工作的基础,进化计算的全面概述,以及遗传算法如何工作。如果这是您第一次看到这个系列,请阅读我以前写的这两篇文章,以便您能够最好地理解我是如何开发我将很快详述的算法的:
目录
- 时间序列问题
- 神经网络综述
- 实现我们的神经网络
- 神经网络的设计
- 培训、验证和测试数据集
- 遗传算法
- 评估我们的算法
- 通过反向传播训练的神经网络进行比较
- 最终代码和视频实现
- 结论
时间序列问题
时间序列分析是指任何一系列的数据,其中的数据是按时间顺序排列的。时间序列分析非常适用于许多场合,从经济到天气预测和预报。我们将要分析的问题是太阳黑子时间序列数据集。太阳黑子周期是发生在太阳上的太阳磁场活动。自 1749 年以来,太阳黑子的月平均数就有记录。我们的问题是要看看我们能多好地模拟这个时间序列问题,并预测未来的月平均数。这里我们看到了时间序列数据集在其整个生命周期中的样子:

作者图片
你可以在 Kaggle 网站上找到这些数据:
https://www.kaggle.com/robervalt/sunspots
神经网络综述
我们将尝试通过前馈神经网络来解决这个问题。人工神经网络是受生物神经网络(大脑的基本单元)的启发而产生的。这些模型能够执行分类、回归和其他技术。神经网络在机器学习和人工智能中已经变得非常普遍,因为它们在许多问题类型中取得了意想不到的成功。因为我们将大量涉及神经网络,我将简要概述它们是如何工作的。如果你有任何使用神经网络的经验,请随意跳过这一部分,因为它主要是回顾。
神经网络的体系结构分为三个部分,即隐藏层数、每层的节点数和每层的激活函数。每层之间的权重由矩阵表示。我们可以在下面的基本示例中看到,我们有三个输入节点(意味着三个变量),第一个隐藏层中有四个节点,第二个隐藏层中有五个节点,还有一个输出。

作者图片
我们用一个权重来表示两个神经元之间的每一个连接,这个权重可以组合起来形成一个矩阵。因此,我们的输入是一个[N,3]矩阵,N 代表我们想要传递的观察值的数量,3 是变量的数量。通过我们的网络向前传递就是简单地将矩阵相乘,在每一层应用我们的转移函数,然后得到我们的结果。图中没有列出的一个关键因素是各层的偏置矢量。偏差是在激活函数之后添加到每个观测值的[1,M]向量,其中 M 是该层的节点数。
神经网络通常通过某种形式的反向传播来训练;其中当前误差(MSE 或交叉熵)从输出层传播回整个隐藏层,以调整权重,使得它们最小化误差。然而,在我们的应用中,我们将使用遗传算法来训练权重,而不是使用反向传播。
神经网络的设计
对于我们的问题,如何用一个神经网络来预测一个时间序列问题?对于一维时间序列问题,你只有一个单一的变量和一个时间指数,你怎么能预测基于时间的变量呢?在神经网络中解决时间序列问题的最常见实现是使用所谓的递归神经网络。****递归神经网络是一个的网络,其中有一个来自输出的递归层,它被反馈到输入层,用于在随后的时间索引处的下一个值。然而,绕过这个实现的一个简单方法是简单地使输入层成为一个“窗口”,它跨越当前值之前的一些索引值。

作者图片
以上图为例,这里我们有一个时间序列问题的子集。我们的目标是预测黑盒中的值。对于第一行,我们希望预测值 110.5,这可以通过将 141.7、139.2 和 158 作为神经网络的输入来实现。注意,在上面的例子中,我们的“窗口”大小是三。我们将使用之前的值(不包括当前值)作为预测的输入变量。窗口的确切大小取决于问题,通常需要测试。对于我们的问题,我们将测试不同的窗口大小。为了简单起见,我们的神经网络将有三个隐藏层,每个层有 5 个隐藏节点和 ReLU 激活函数。然而,输入的数量将根据“窗口”的大小而变化。
实现我们的神经网络
因为我们将进化神经网络的权重,所以我们需要从头实现一个,因为在常见的 Python 实现中,这需要大量的操作。我们将实现我们的网络,以适应任意数量的层和每层的节点,但将只有每层的 ReLU 激活功能。我会试着解释下面的代码,但它处理神经网络矩阵乘法的细节,这可能会变得非常快;所以现在,如果你不明白它到底是如何工作的,不要担心,因为这不是本文的重点。
培训、验证和测试数据集
所有机器学习模型最常见的失败被称为过度拟合。当我们的模型有如此多的可调参数,以至于它开始记忆输入时,就会发生这种情况。因此,当预测一个它从未见过的值时,它的表现比它被训练的值差。下面是一个过拟合的例子,我们可以通过简单地增加多项式的次数来人为地最小化残差的误差;然而,这种预测对于数据来说是极度超参数化的,并且不能准确地代表实际趋势,因此对于以前没有见过的数据来说是极其错误的。

作者图片
所有机器学习和数据科学的目标都是创建简单但强大的模型,这些模型可以适应并准确地插入新值。有许多不同的方法来防止神经网络中的过拟合,从丢弃层到交叉验证;然而,最常见的仅仅是提前停止。为此,我们可以将数据集分为两个主要部分,训练数据集和测试数据集。我们使用我们的训练数据集来训练我们的模型,然后使用测试数据集来测试它的性能。这样,我们在我们的训练集上训练,一旦我们开始看到过度拟合,这意味着我们的测试集中的误差开始增加,我们就停止我们的算法。这就是所谓的提前停止。如下所示,我们的模型开始过度拟合训练数据,因为训练误差仍在减少,但测试数据停滞不前,并开始略有增加。在几次迭代之后,当我们的测试数据误差增加了某个值时,我们需要执行早期停止,以确保我们防止过度拟合。

作者图片
然而,这有一个关键问题,当我们的模型开始在我们的测试数据集上产生更坏的错误值时,我们就停止训练;这样,我们实际上已经在测试和训练数据集上训练了我们的模型,因为当我们的模型在测试集上表现更差时,我们已经停止了。这正是我们一开始想要摆脱的…为了解决这个问题,我们可以引入验证数据集,它取代了早期停止中的测试数据集。我们现在将数据集分成三部分,训练集、验证集和测试集。我们在训练集上训练我们的模型,然后我们使用验证集执行早期停止、参数调整和模型比较。在我们选择了最终模型及其最终架构之后,我们才评估测试数据集。通过这样做,我们可以确保我们的模型从未见过测试数据集中的值,以便获得我们模型的准确评估。
这些是重要的概念,当我们为我们的神经网络实现遗传算法以防止过度拟合时,我们将回头来看。
遗传算法
现在是时候讨论我们进化算法的具体实现细节了。正如在以前的帖子中所讨论的,每个个体都是由其基因型和表现型组成的。基因型代表个体的实际遗传密码,表现型代表环境中的个体。在我们的问题中,基因型由权重矩阵和偏差矩阵组成,其中每个矩阵都是基因组中的一个基因。对于表型,一起编译的权重和偏差矩阵在环境中形成神经网络。既然我们知道了我们将如何编码我们的染色体,是时候讨论繁殖算子了。对于选择,我们将使用轮盘赌轮选择,它的工作原理是根据个体的适应度值从被选择的个体比例中创建一个累积分布:
对于交叉,我们将实现平均技术,它采用父值的线性组合。对于我们的问题,子代权重和偏差矩阵将只是父代矩阵的线性组合。为此,我们首先实例化一个新的 EvolvableNetwork,但这次 initialize 等于 False,因为我们不希望权重和偏差矩阵被初始化为随机值,因为我们将从父级创建它们:
对于突变,我们将简单地为所有矩阵的权重和偏差矩阵的每个条目添加一些小的随机值:
与我们以前的遗传算法实现不同,这个特定的实现将不具有用于变异、交叉或精英化概率的超参数。相反,我们可以通过去掉这些参数来降低调整算法的复杂性。为了做到这一点,我们的父母将创建一组四个孩子,这些孩子将与他们的父母一起被集中起来,最适合的个体将被选择存活下来。对于子代,四个都将通过不同系数值的交叉产生,然后最后两个交叉子代将通过不同的随机值进行变异。通过这样做,我们保证我们的算法将收敛,因为后代和父母中的最佳个体将被选择存活,并且我们减少了调整算法的需要,因为后代集将包含交叉和变异的个体。这是我们对双亲的复制函数的样子:
现在是我们的适应度函数的时间了,它将取我们的时间序列问题的预测值和实际值之间的均方误差:

作者图片
因为我们想要最小化 MSE 误差函数,所以我们需要缩放我们的适应值,使得较小的值产生较大的值,较大的值产生较小的值,然后最大化缩放的适应值。我们可以通过下面的函数做到这一点:
有关其工作原理的直观表示,请参见下图,其中 x 轴是原始适应值,y 轴是缩放后的适应值。缩放后,接近 0 的值将趋向于 1,然后对其执行标准的最大化。

作者图片
接下来,我们需要将数据分成训练集、验证集和测试集。我们将使用训练数据集来训练我们的算法,并使用验证集来比较我们的模型,并通过提前停止来防止过度拟合。最后,在我们选择了最终的模型之后,我们将通过测试数据集来评估它的准确性。我们将把我们的训练和验证数据传递给我们的进化算法。如前所述,我们将测试不同的窗口大小,因此我们将循环所有可能的窗口大小,创建数据,并测试我们的算法:
既然我们已经定义了所有的辅助函数,是时候定义我们进化算法的主体了。我们的算法将通过对训练数据进行训练来工作,并且如果当前代的验证数据的平均误差连续三代增加,则提前停止。在收敛或提前停止后,验证数据将再次用于从当前代中选择最佳模型。
对于评估,我们将从每个窗口大小中找到具有最佳验证分数的模型,然后基于该窗口大小重新创建数据来评估我们的测试 MSE 分数。
评估我们的算法
最后,是时候测试我们的遗传神经网络了。在编译了到目前为止给出的脚本之后,在种群大小为 100 个个体、最大突变值为 0.1、网络架构为[5,5,5]的情况下,在从 3 到 10 的每个窗口大小下运行 200 代之后,结果如下:

作者图片
从每个窗口大小的最佳模型的结果来看,窗口大小为 5 的模型对于验证数据集具有最小的 MSE,因此我们选择它作为最终模型。在测试数据集上对最终模型进行评估后,我们得到的误差为 616.378。平均验证误差徘徊在 618 左右,标准偏差为 36。对于视觉效果,以下是使用窗口大小为 5:

作者图片
测试通过反向传播训练的神经网络
现在我们已经评估了我们的算法,是时候用通过反向传播训练的神经网络来测试它,看看所有这些麻烦是否值得。因为在一天结束时,如果通过反向传播训练的神经网络能够胜过我们的遗传算法,那么为什么要浪费时间这样做呢?因为大多数人都安装了 scikit-learn ,所以我将使用 scikit-learn 中的神经网络实现,称为 MLPRegressor 。与我们的遗传算法一样,我将在不同的窗口大小下测试每个神经网络,并根据最佳验证数据集误差为每次运行选择最佳模型作为最终模型。为了确保公平的比较,通过反向传播训练的神经网络将使用相同的训练、验证和测试数据集。不幸的是,MLPRegressor 中的提前停止机制从给定的训练数据创建验证数据集,而不是传入特定的数据。在 scikit 中培训 MLPRegressor 的效果如何-了解:
因此,验证数据仅用于比较模型。结果如下:

作者图片
从结果中我们可以看出,窗口大小为 3 产生最小的验证误差,这与遗传算法有很大的不同;然而,窗口大小为 3 的验证分数与大小为 5 的遗传算法的验证分数相当。为了比较这两个框架,我们可以通过使用验证误差的平均值和标准偏差来并列。遗传算法具有较好的平均验证误差 4.65%和较小的标准偏差 25%。此外,其测试均方误差优于 4.18%。因此,有理由得出结论,对于这个特定的问题,我们的遗传算法在训练神经网络方面优于反向传播算法。下面是我们对通过反向传播训练的神经网络的整个时间序列数据集的预测。

作者图片
最终代码和视频实现
这篇文章中有很多代码,这里有一个到我的 GitHub 的链接,整个脚本是完整的,实际上和我在这里使用的一样。运行它,看看奇迹发生吧!
**https://github.com/OUStudent/EvolutionaryComputationCourse/blob/main/Unit3/time_series_problem.py
对于任何想要更深入了解代码的人,我将在这里制作一个视频实现视频,并放在这里。(也就是说,如果你看到这条消息,但网址没有发布,请等待一周左右)
结论
在这篇文章中,我们应用了我们在过去两篇文章中学到的知识来进化用于时间序列分析的神经网络的权重。通过将我们的数据分成训练、验证和测试集,它允许我们精确地训练、比较和评估我们的模型。结果表明,对于给定的时间序列问题,遗传算法在训练神经网络方面优于反向传播算法。这些结果非常有希望。
在计算智能领域,将遗传算法应用于神经网络实际上是一个被称为神经进化的子领域。神经进化可以有许多不同的形状和大小。在这个范例中,不仅权重被进化,而且层数、节点数、激活函数和神经元连接方向也被进化。当适应度函数不可微时,神经进化通常应用于强化型问题,因此不能应用反向传播;或者当神经网络规模较小时,如本例所示。不幸的是,神经进化并不是训练深度神经网络的竞争领域,因为这些网络具有成千上万个用于权重连接的参数,这使得标准遗传算法中的交叉和变异极其耗时,因为评估每个权重矩阵需要 O(n*m)。然而,神经进化是一个越来越感兴趣的领域,不是为了进化深度神经网络的权重,而是为了进化结构和超参数,然后使用标准的数值方法(例如反向传播)来训练这些结构和超参数。这种情况的一个例子可以是发展每层的节点数、层激活函数、包含退出或卷积层、以及用于反向传播的算法超参数,例如动量和学习速率。这被称为自动机器学习,不再需要数据科学家来“调整”算法;相反,利用遗传算法来为给定的神经网络找到最佳的超参数集和结构。
总之,这篇文章总结了单元 3)遗传算法。尽管这个单元已经写了四篇文章,时间有点长,但我希望它对你理解遗传算法的工作原理以及它们在实践中的应用非常有帮助。在下一篇文章中,我们将开始单元 4)遗传编程,这将是非常短的,并在这里处理相同的时间序列问题!**
单元 3)遗传算法:基准测试函数
进化计算课程
将遗传算法课程中的概念应用于一系列实际的优化问题!
大家好,欢迎回到进化计算的完整课程!在本帖中,我们将介绍一种遗传算法,用于评估从第 3 单元遗传算法中学到的基准测试函数。由于这是该系列的继续,如果您还没有检查那篇文章,请这样做,以便您不会被遗忘在黑暗中!你可以在这里查看:
目录
- 问题陈述
- 新论据
- 一些帮助
- 履行
- 各种超参数的结果
- 视频讲解
- 评估其他基准测试函数
- 结论
问题陈述
在上一篇文章中,我们扩展了进化计算的主要概念,即交叉和变异算子,最后以那个“简短”的小例子结束。假设你已经理解了这些材料,那么是时候将我们的知识应用到一个真正的优化问题上了。我们的问题将是六峰驼背功能:

作者图片
我们的工作是找到上界函数的最小值:x1 在[-2,2]之间,x2 在[-1,1]之间。为了直观起见,我们在上面有一个域空间的图表。全局最小值出现在两个位置:

作者图片
新参数
我们将创建一个带有许多参数的遗传算法。首先,它将包含通常的参数,就像在上一单元的简短示例问题中使用的一样:
- 交叉/变异的概率
- 变量值的界限
- 适应度函数
- 变异所依据的边界百分比
- 精英主义的百分比
- 最大世代数
此外,我们将添加一些新组件,即用于选择方法和用于再现方法的参数。在简短琐碎的示例问题中,我们仅通过轮盘赌使用比例选择,并对交叉进行平均。然而,现在我们将包括用于选择的轮盘赌、随机或锦标赛风格的参数,以及用于平均或“直觉”交叉的参数。
有人帮忙
到目前为止,在这个系列中,我们只讨论了寻找适应度函数的最大值,而不是最小值,那么我们如何调整我们的算法来找到正确的最小值呢?我认为最好的解决方案是不要颠倒我们排序的顺序,而是像往常一样找到最大值;然而,我们衡量我们的健康值,它们越小就变得越大,越大就变得越小。我们将在第一层缩放之后进行第二层缩放,这使得所有适应度值都为正:

作者图片
如前一篇文章所述,因为一些适应值可能是负的,我们需要使它们都是正的,以便当我们计算总和除以每个适应值时,它将创建一个比例分布。如果我们最大化,我们不执行第二层缩放,但是如果我们最大化,我们就使用上面的等式进行缩放。例如,如果缩放的适应值是[4,5,1],那么第二层缩放创建[0.2,0.167,0.5]。正如我们所看到的,最大值 5 现在变成了最小值 0.167,最小缩放值 1 变成了最大值 0.5。这样,除了增加第二层缩放,我们仍然可以在不改变太多的情况下找到最大值。
实施
因为我们将处理许多移动的组件,所以我将通过用函数调用替换每个组件来使我们的‘evolve’函数变得非常紧凑。
首先,我们有扩展数据的函数:
接下来,我们有三种选择方法,轮盘赌、随机和锦标赛选择。我们之前已经见过轮盘赌轮盘选择:
另一方面,随机选择只是从 0 到代的大小中选择随机指数:
对于锦标赛的选择,会有一点不同。请记住,在锦标赛选择中,我们随机选择一组个体,并选择最优秀的生存者,因此我们需要另一个参数来确定锦标赛的规模。我们稍后将讨论这一点。现在,我们简单地循环,在每次迭代中,我们创建一个竞争对手的随机样本,其中我们找到具有最大适应值的个体,并将其附加到所选的指数中。
现在我们已经实现了选择方法,是时候使用复制方法了。该方法将接受一组亲本、它们的适应值、变异和交叉的概率,以及变异的界限和繁殖方法。如果再现方法等于 1,我们将使用平均技术,否则我们将使用“直觉”技术:
现在我们已经定义了我们的繁殖方法,是时候进入交叉和变异方法了。第一,跨界。我们有两种类型,平均型和‘直觉型’。与以前不同,我们需要在我们的方法中考虑多维度,以允许可扩展性。因此,我们对每个变量值进行循环,并执行交叉技术:
现在我们有了“直觉”交叉方法。在这个方法中,我们创建了一个随机位的列表,其中如果一个位是零,我们从第一个父项继承该变量值,如果是一个,我们从第二个父项继承该变量。在只有两个变量的情况下,后代有 50%的机会从同一个父代继承两个变量值,因此为了避免这种情况,如果有两个维度,我们强制在两个父代之间分割交叉:
现在我们终于实现了所有的辅助函数,是时候创建算法了。不幸的是,这个算法很长,差不多有 100 行代码。我不认为我能在这里解释这一切,但是如果你真的跟随这个系列并且没有睡着,我想你能推断出正在发生什么:
好了,现在我们已经实现了我们的算法,是时候看看它是如何解决我们的问题的了!这里我们有我们的六峰驼背函数以及我们的初始世代和边界:
现在让我们称之为 25%的交叉,50%的变异,1%的边界最大变异值,10%精英主义,锦标赛选择(sel_method = 3),10%锦标赛大小,“直观”交叉的再现方法(rep_method = 1),并运行 30 次迭代:

作者图片

作者图片
正如我们所看到的,我们的算法几乎已经收敛到全局最小值-1.0316。
各种超参数的结果
现在我们要用不同的超参数运行我们的算法,这里有 5 次不同的运行。为了区分,每个地块在标题中都有一个[s,r,e,t]: [#,#,#,#,#],其中的 s 位置指数表示所使用的选择方法, r 指数表示所使用的繁殖方法, e 表示精英主义延续下来的个体数量, t 指数表示锦标赛规模(除非 s = 3,否则没有任何意义)。所有的运行都进行了 35 代,1%的最大突变界限,和 10 人的群体规模。这里我们用 0.75%来表示杂交和变异:

作者图片

作者图片

作者图片
现在有 25%的交叉和变异:

作者图片

作者图片

作者图片
视频解说
因为这篇文章有很多复杂的组成部分,我决定在一个视频中为你实现这个算法,我会更深入地解释它,你可以在这里查看:
评估其他基准测试函数
之前的优化问题比较容易;然而,我们可以通过测试更难的优化问题来评估我们的算法。我们还将评估另外两个问题,即蛋架函数、罗森布鲁克函数和阿克利函数。为了保持平等,我们将运行每个算法 300 代,初始规模为 100 个个体,75%用于变异/交叉,10%用于精英主义,1%用于最大变异值的界限,以及随机交配。即使我们使用随机选择进行交配,我们也会将它与精英主义结合起来,这样我们就永远不会失去每一代的最佳解决方案。现在,因为算法的成功很大程度上依赖于初始群体的初始点,所以我们的算法将对每个问题运行 30 次,以便我们从域中获得广泛的值,并且每次找到的最小值将被记录。最后,为了查看两种交叉方法之间的差异,我们将对每种交叉方法运行每种算法 10 次。
首先是 Eggholder 函数,这里是下面这个函数,全局最小值只发生在一点:f(x)=-959.6407 at X=(512,404.2319):

作者图片
这里我们有 30 个独立试验的结果,使用的是“直觉”交叉。为了阅读下面的结果,汇编了来自所有运行的最后一代的最佳和平均适应度;然后,如下图所示,计算每一次运行的平均值和标准偏差。例如,任何运行的最后一代的最小平均适合度为-889.967,其中每次运行的平均适合度的平均值为-814,标准偏差为 103。你可以同样解读为最佳健身。我们可以看到,在所有的试运行中,找到的最佳全局最小值是-939,与全局最小值相差 20 个值。这仍然是一个好的结果,因为从图中可以看出有许多局部极值点。

作者图片
现在,我们将使用交叉技术 1(平均法)来运行该算法。

作者图片
乍一看,我们可以看到直观的交叉方法对于每次运行的最佳适应值具有更好的均值和更小的标准差。然而,这两种方法确实找到了相同的最佳最小值。
二、 Rosenbrock 函数,这里是下面这个函数,全局最小值只发生在一点:f(x)=0 在 X=(1,1):

作者图片
现在,我们将使用“直觉”交叉技术运行该算法:

作者图片
现在,我们将使用平均交叉技术运行该算法:

作者图片
从结果中,我们可以看到平均交叉技术对所有统计数据都产生了更好的结果,对平均值和最小值都产生了更好的平均值和最佳拟合度。在所有代中找到的最佳适应值是 7.97e-8,其可接受地接近实际的全局最小值 0。
第三, Rosenbrock 函数,这里是下面这个函数,全局最小值只发生在一点:f(x)=0 在 X=(0,0):

作者图片
现在,我们将使用“直觉”交叉技术运行该算法:

作者图片
现在,我们将使用平均交叉技术运行该算法:

作者图片
通过评估结果,我们可以看到直观的交叉方法找到了更好的全局最小值,并且对于所有代的最佳最小值具有更好的平均值和标准偏差。找到的最佳最小值是 0.000217,接近全局最小值 0,但不到上一个问题答案的程度。
最后,我希望您能看到,没有一组参数会比其他参数更好,这完全取决于问题。因此,必须使用不同的超参数组合多次运行该算法,以“感受”结果。正如我在以前的文章中提到的,创建抽象和简单的遗传算法实际上是进化更高级遗传算法的超参数;然而,这可能很快变得极其计算昂贵。
结论
好了,这个帖子到此结束!希望你已经学到了很多关于遗传算法的知识。我想让你们看到的主要观点是,算法的成功严格依赖于你们给它的超参数。如果适应度函数极其复杂,则通常创建更简单的遗传算法,以在原始函数上进化复杂算法的超参数。这就是所谓的“调整”超参数。如果你熟悉自动机器学习,遗传算法是用来进化机器学习模型,尤其是神经网络的超大型的常用工具。在本单元的最后,我们将讨论为时间序列分析问题训练一个神经网络。请继续关注下一篇文章,我们将讨论遗传算法中的高级概念:
https://morganscottbrandon.medium.com/unit-3-genetic-algorithms-part-2-advanced-topics-a24f5be287d5
请继续关注下一篇文章,我们将讨论遗传算法中的一些高级主题!你可以在我的 GitHub 上找到所有代码:
单元 3)遗传算法(第一部分)
进化计算课程
遗传算法概述—主要是交叉和变异算子
大家好,欢迎回到进化计算的完整课程!在这篇文章中,我们将讨论课程的第三单元,遗传算法。在上一篇文章中,我们讨论了第二单元——进化计算导论。如果你还没有阅读那篇文章,请这样做,因为我们将在这里详细地展开那篇文章,你可以在这里查看:
https://morganscottbrandon.medium.com/unit-2-introduction-to-evolutionary-computation-85764137c05a
在这篇文章中,我将扩展上一单元的内容,并将这些概念应用到一些真实的例子中。首先,我们将介绍标准的规范遗传算法。然后我们将讨论浮点表示,并直接讨论交叉和变异操作符。然后,我们将涵盖遗传算法变体,高级主题,以及如何处理不同的优化问题。在整篇文章中,我们会用 Python 代码给出一些例子。因为这个单元会比较广泛,所以会分解成几个部分。
目录(第一部分)
- 典型遗传算法
- 二进制与浮点表示法
- 交叉技术
- 突变技术
- 变异交叉
- 简短的例子
- 视频讲解
- 结论
标准遗传算法

作者图片
标准遗传算法被认为是最简单的,也是实践中最早使用的遗传算法之一。它利用基因组的二进制/位串表示进行编码和解码,通过轮盘赌进行比例选择,在基因组中进行单点交叉和均匀变异。虽然我们还没有谈到单点交叉或一致变异,但我们将在本帖的后面讨论它们。在这种算法中,那些存活下来的是被选中的父母的后代。此外,不是总是通过变异或交叉来创建后代,而是后代具有被交叉或变异的概率,其中如果它们没有被交叉或变异,它们默认为具有最佳适应值的父代的副本。实施交叉和突变概率的目的是防止来自父母的基因信息丢失,因为后代可能比父母更差。
二进制与浮点表示法
实际上,基因组通常有两种表示方式:
- 二进制的
- 浮点
经典遗传算法利用二进制表示,因为它更受生物学的启发,通过改变化学化合物在 DNA 中发生进化,如翻转比特以进行基因组的突变或交叉。然而,由于必须将浮点数编码和解码为二进制的问题,二进制表示不再大量使用,特别是如果使用格雷斯编码来解决前一单元中讨论的汉明悬崖问题。
另一方面,浮点表示法保持数字不变,从而通过不编码或解码成二进制来节省时间;然而,它不如二进制表示直观,因为它如何变异或交叉浮点数?我们稍后将解决这个问题。最后,可能会质疑两种表示法的准确性,在寻找解决方案方面,浮点表示法比二进制表示法更好吗?事实证明,浮点表示也可以工作得很好,有时甚至比标准二进制编码更好,所以如果使用浮点表示,没有理由担心算法质量。
交叉技术
交叉算子可以分为三种类型,无性、有性和多重重组。无性是指一个后代由一个父母产生,有性是指两个父母产生一个或两个孩子,然后多重组是指利用两个以上的父母产生一个或多个后代。此外,从所选择的用于产生后代的亲本中,我们具有执行交叉的概率,以便不由于总是交叉而丢失亲本的遗传信息,其中较高的交叉概率等同于较高的交叉机会。如果没有应用交叉,那么子代是最适合的父代的副本。此外,如果产生了一个后代,它可以直接或通过类似玻尔兹曼选择的方式概率性地取代最差的亲本。

作者图片
如前所述,我们可以使用二进制或浮点表示进行编码。这里我们将讨论二进制编码交叉技术。有三种主要类型的交叉技术,一点、两点和均匀。我们可以在上面看到这些例子。首先在左上角,我们有一个单点杂交,我们选择一个随机点,交换父母的基因组来创造两个杂交后代。第二,我们有多点交叉,如右上角所示,我们选择多个点进行交叉。最后,我们有统一的交叉,如底部所示,基因组的每个基因都有可能被父母遗传。
对于二进制来说,交叉技术是非常直观的,但是现在的问题变成了我们如何对浮点数进行交叉?有两种主要的浮点交叉技术。下面我们可以看到一个使用多亲本重组的几何例子。

按作者分类的图像(交叉 A)
首先,我们有以上三个选择的亲本用于繁殖,接下来我们计算来自亲本的平均向量,表示为中间的 X 均值,然后我们在亲本的任一方向上向该平均向量添加一些方差以获得后代。这样,后代就是父母之间的某种平均值

按作者分类的图像(交叉 B)
接下来,在上图中,我们通过切换每个变量的浮点值来执行交叉。这种类型的交叉只有离散数量的可能值,因为在创建后代时不包括方差,只有父代中存在的变量值。这是一个问题,因为我们没有办法引入新的遗传物质;然而,我们将通过使用突变来解决这个问题。
对于我们的例子,我们将使用两种常见的交叉技术。第一个操作符是通过组件的线性组合在双亲之间的一种类型的几何平均,当仅使用两个双亲时,这由下面的等式给出:

作者图片
请注意,当 gamma 为 0.5 时,线性组合是两个父项之间的几何平均值。通常要么使γ成为以 0.5 为中心的正态随机值,标准偏差为 0.15,以便它可以在 0.05 到 0.95 之间变化三个标准偏差,要么使它在 0 到 1 之间保持一致,平均值为 0.5。请注意,这可以扩展到多个父代,其中每个父代值都有自己的灰度值,灰度值之和等于 1:

作者图片
然而,当使用多个父对象进行交叉时,可能会出现一个问题,即如何选择 gamma 值。如果你允许伽马值都相等,你就促进了良好的多样性;然而,你可以根据适合度值对父母进行排序,并使适合度最好的父母拥有最大的 gamma 值,以使后代倾向于该父母;但是,如果伽玛值太高,可能会导致过早收敛。实际上,几何平均数通常用于多亲本繁殖。

作者图片
第二种交叉方法是我喜欢称之为‘直觉’的交叉方法;如前所述,这种方法的工作原理是让后代从父母中的一方继承他们的基因。这种方法也可以扩展到多个父母,现在后代的基因是从选择的父母中取样的。在这种方法中,问题变成了孩子应该继承哪个基因,这被称为扫描策略。从父母中的任何一方遗传基因的概率可以是一致的,或者可以通过使用比例选择方法基于他们的适合度值。
按代交叉示例

作者图片
上面我们有一点视觉表现,当我们使用第一个交叉技术时会发生什么。这是通过随机选择两个个体进行交配并通过线性组合繁殖后代来实现的,其中λ从 0 到 1 是均匀随机的。在左上图中,我们的初始种群在边界上——x 和 y 都是 100 到 100。在左下图中,我们有交叉技术到第 10 代的累积搜索空间。我们可以清楚地看到,由于平均效应,这种技术倾向于缩小搜索空间。我们将在高级主题部分解决这个问题。右边是第 100 代和第 300 代的累积搜索空间。我们可以明确地看到,我们的交叉技术彻底探索了原点周围的区域,因为边界-100 到 100 的均匀分布的平均值为零,因为(100-(-100))/2 = 0/2 = 0。

作者图片
现在,当我们使用第二种交叉技术时,我们有了一个直观的表示。这是通过随机选择两个个体进行交配,并交换双亲的 x 和 y 值来产生两个后代。同样,在左上角的图片中,我们有我们的初始代,在左下角,我们有第 10 代的累积搜索空间。我们已经可以看到,由于其交换性质,这种交叉技术更均匀地探索了搜索空间。然而,我们可以在右边的图片中看到,到了第 300 代,该算法留下了一些没有探索的主要漏洞。这是由于这种杂交技术没有引入任何新的遗传物质;因此,域中不存在的任何值,后代将永远不会获得该值。在我们的例子中,因为没有初始值具有-50 的 x 值,所以到第 300 代时,没有后代探索该区域附近。这就是为什么交叉技术与突变配对是至关重要的,我们将在接下来讨论。
突变技术

作者图片
好了,现在我们已经对我们的后代进行了杂交,我们需要一种方法来引入新的遗传物质,这是通过突变完成的。对于二进制表示,我们通常有两种主要的方式来执行变异,其中一种是通过均匀变异,其中每个位都有相等的机会被翻转,或者仅通过随机翻转其中一个位。

作者图片
对于浮点表示,有许多不同的变异技术;然而,我将首先介绍一个基本的突变方法,其中我们从每个给定变量的边界的一致域中给后代的每个组成部分添加一个随机的一致值,正如我们在上面看到的。请注意,如果这个界限很大,它将鼓励勘探,而如果它很小,它将鼓励开发。最大突变值的选择取决于设计者,但是对于给定的变量,通常将其保持在总域空间的 1%左右。在后面的高级主题中,我们将介绍带有突变值的逻辑衰减,以便它们在整个算法中不断变化。
世代突变示例

作者图片
上面我们有一个当我们在浮点表示中使用变异技术时会发生什么的可视化表示。这是通过在每个个体的 x 和 y 方向上增加一个小的随机值来实现的。左上角的图片是我们最初的一代,左下角是我们第 10 代的累积搜索空间。我们已经可以看到,突变只是简单地探索和创造了一个围绕点的小集群。我们可以在右侧看到,这种突变模式导致在第 300 代探索关于初始点的大型集群。这通过引入新的遗传物质显示了突变的力量。在下一小节中,我们将看到第二种交叉方法与变异相结合,以及如何使用它来探索输入空间。
逐代交叉变异实例

作者图片
最后,我们上面有一个可视化表示,当我们使用第二个交叉技术与变异配对时会发生什么。这是通过随机选择两个个体进行交配,并交换两个亲本的 x 和 y 值来产生两个后代,然后在 x 和 y 方向上添加一个小的随机统一值来实现的。同样,在左上角的图片中,我们有我们的初始代,在左下角,我们有第 10 代的累积搜索空间。然而,如果我们跳到右下方的第 300 代,我们可以看到一个与之前完全不同的交叉画面。如果你还记得的话,接近-50 的值没有被探索,因为最初的一代缺乏任何具有该值的点;然而,因为我们增加了突变,它引入了新的遗传物质,允许交叉探索那个区域。这显示了交叉和变异作为遗传算法可行的搜索工具的威力。希望通过这些简单的小图表,你能理解这些机制如何探索输入空间。
简短琐碎的例题
既然我已经介绍了遗传算法的基础知识。让我们看一个简单的例子。假设我们有下面这个我们想要最大化的函数:

作者图片
函数为 f(x)=-x +3,其中最大值出现在 f(0) = 3 时 x=0。在我们开始之前,我们有几个设计问题。首先,我们最初的人口会是什么样子?记住,我们的算法依赖于初始代的变化;因此,我们必须创建一个跨越整个域空间的初始群体,因此我们将从-3 到 3 的域空间中均匀随机地创建我们的初始点。接下来我们需要确定我们的代的大小,这也与算法的成功成正比。因为这是一个很小的例子,我们可以有几千个初始大小,因为适应度函数不需要很长时间来评估;但是,为了简单起见,我们只使用 5。接下来,我们需要选择我们的适应度函数;因为我们希望找到函数的最大值,所以我们的适应度函数将是优化函数本身,因为我们将奖励具有较大输出值的点。接下来,我们需要选择我们的基因组编码,我们将使用浮点表示,因为它比二进制编码和解码更快,并且它是现代实践中使用的。现在,我们需要决定哪些个体将被选择进行繁殖,我们可以通过将轮盘赌和精英主义配对来完成。对于这个问题,我们将精英主义设定为 20%,这意味着这一代中最优秀的 20%将永远被保留下来;然而,因为我们的初始规模只有 5,20%实际上只意味着一个个体。对于繁殖,我们将使用亲本的平均值,其中γ从 0 到 1 均匀分布,突变值将是整个界限的 1%。因为我们并不总是希望执行交叉或变异,因为它可能会丢失来自父代的信息,所以我们可能只在 75%的情况下执行这些操作。最后,我们的算法将在达到允许的最大迭代次数时停止。既然我们已经讨论了我们算法的架构,让我们来看看 Jupiter notebook 并用 Python 实现它吧!
视频解说
我知道所有这些代码可能看起来令人生畏,所以我决定制作一个半短视频来解释这些代码及其工作原理:
结论
在这篇文章中,我们介绍了浮点表示的交叉和变异操作符的基础知识,以及一个简单例子的基本算法。在下一篇文章中,我们将通过处理一个实际的基准测试函数进行更深入的探讨!您可以在这里找到它:
单元 3)遗传算法(第二部分)高级主题
进化计算课程
遗传算法的高级主题——控制参数、选择性交配和遗传变异
大家好,欢迎回到进化计算的完整课程!在这篇文章中,我们将讨论课程的第三单元(第二部分),遗传算法——高级主题。在前一篇文章中,我们实现了一个简单但强大的遗传算法来解决一系列优化问题。如果你还没有阅读这篇文章,请阅读它,因为它将详细阐述所涉及的内容,你可以在这里查看:
在这篇文章中,我将通过介绍一些高级话题来总结第三单元遗传算法的材料和概念。这些主题包括控制参数(超参数),遗传算法的各种变体,通过聚类的选择性交配,以及如何处理约束。
目录
- 控制参数
- 遗传算法变体
- 集群选择性交配
- 约束处理
- 结论
控制参数
如果您一直关注这个系列,您应该已经注意到超参数有很多选择,它们直接影响算法的质量。最常见的超参数是变异概率、交叉概率、最大变异界限百分比和精英百分比。这些超参数可以是三种类型之一:
- 静态
- 动态的
- 自适应
静态超参数就像它们听起来一样,它们在算法的整个评估过程中都是静态的。大的静态值鼓励探索(精英主义除外),小的静态值鼓励剥削(精英主义除外)。到目前为止,我们已经在整个系列中使用了静态超参数。动态超参数在算法执行过程中从大值动态减小到小值。这样做是为了允许在算法的早期阶段进行探索,并在以后进行开发。动态超参数的问题是,随着每一代的过去,无论发生什么,参数都会减少,即使它们表现良好,而静态超参数的问题是它们从不改变。为了解决这个问题,自适应超参数仅根据它们的成功程度而改变,如果最大突变界限值太低,它将自适应增加,如果太高,则减少。在本帖中,我们将不讨论自适应超参数,因为它们是进化规划单元 5)的一个组成部分。
对于动态超参数,处理这种动态减少的最常见方式是通过线性或非线性函数。对于线性函数,您给算法最大和最小超参数值,它将在给定代数上创建从最大到最小的直线值。非线性函数通过非线性方式实现这种值的减少。最常见的非线性函数是逻辑衰减:

作者图片
其中 p_max 是参数的最大值, t 是世代, r 是衰减率。较小的 r 值鼓励开采,而较大的 r 值鼓励勘探。因为逻辑衰减本质上是逻辑的,随着代计数接近无穷大,参数值将接近零,这将导致停滞和过早收敛。我们不希望在实践中发生这种情况,所以我们实现了一个 max 操作,这样如果新的参数值低于某个最小值,它将被设置为该最小值。

作者图片
上面我们有一个逻辑衰减的各种速率值的图表。第 0 代的参数值从 0.75 开始,以 r 的速率逻辑衰减。我们设置的最小值为 0.025,用水平黑线表示,因此当逻辑衰减值低于该值时,我们只需恢复到最小值。选择我们的速率取决于我们期望算法收敛多长时间。可以看到,虽然这个新的增加可以帮助我们的算法在探索-开发权衡,但它确实增加了另一个需要针对问题进行调整的参数级别。
遗传算法变体
与所有算法一样,对于特定的问题,可以实现许多不同的算法。有三种主要类型的流行遗传算法变体遵循迄今为止给出的类似格式:
- 代沟方法
- 不断增长的人口
- 并行运行
- 孤岛算法
代沟方法是实施不同存活选择方法的变体,用于确定谁存活到下一代。这种方法有三种流行的方法:
- 亲子之战
- 杀戮锦标赛
- 精英主义策略(精英主义的形式)
首先,我们可以创造后代并决定后代和父母中谁会存活,而不是总是接受后代而不是父母。这个过程的好处是,它消除了调整突变和交叉概率的超参数的需要,因为后代总是被创建。在父母-后代的战斗中,一对父母对抗他们的后代,后代根据他们的适应值被概率接受。最常见的处理方式是通过玻尔兹曼选择,我们将在下一篇文章中讨论。
第二,和第一种方法一样,我们总是创造后代,这样就不需要交叉和变异的概率,并把它们和双亲放在一起。在将父母和后代集合在一起后,你使用锦标赛选择来生存,除非最不适合的个体被杀死。执行此操作,直到后代父池减小到原来的大小。这种方法的好处是,这种竞争不直接发生在父母和后代对之间,而是发生在总的混合群体中,因为一组父母之间的后代可能比另一组父母更差,但却更好。
最后,精英策略类似于第二种策略,将所有的父母和后代聚集在一起,然后根据健康状况或年龄对他们进行分类。如果根据适合度进行排序,这种方法就像精英主义一样,从排序的个体中选择最好的一半来生存。除了健康分类之外,人们还可以根据年龄和健康分类,年轻人比老年人更受欢迎。在算法中,年龄是通过它们存活了多少代来表示的。这个过程的好处是,算法不会抓住旧的解决方案不放,旧的解决方案可能会导致停滞;但是接下来的问题是,算法会拒绝好的旧解决方案,而支持新的解决方案,即使它们更差。
不断增长的人口变体就像它们的声音一样,算法的人口规模动态增长以模拟自然界的人口规模。有两种常见的方法,一种是通过逻辑衰减和增长。从我们最初的人口规模开始,我们的逻辑增长或收缩取决于特定的环境。例如,如果我们的算法收敛,我们可能会增加我们的规模,以允许更多的探索,而如果我们的算法不收敛,以鼓励开发。这个过程中的主要问题是准确地确定何时衰退或增长,以及衰退或增长的程度,从而给问题增加了更多的超参数。
并行运行变体是设计成并行运行的遗传算法。在工业中,在现实世界的问题上运行遗传算法是非常耗时和耗电的。我们可以通过并行运行我们的算法来降低复杂度。这可以通过三种方式实现:并行执行我们的适应度函数,为每个个体分配一个处理器,或者为每个处理器分配子种群。

作者图片
最后,孤岛算法,正如我们在上面看到的,非常接近于并行运行,因为它们通常在多个处理器上执行,其中每个处理器代表一个孤岛;然而,这些算法可以在单个处理器上实现。在孤岛算法中,我们有所谓的迁移策略,它包含初始化、通信拓扑、迁移速率、迁移选择和替换选择。通信拓扑表示哪些岛能够相互通信,哪些不能。通常,相邻的岛屿具有通信能力。交流通常以迁移的形式发生,即把个人从一个岛送到另一个岛。这些超表的选择是迁移率、迁移选择和替换选择。迁移率意味着一个人从一个岛迁移到另一个岛的频率。迁移选择指的是选择要迁移的一个或多个个体。最后,一旦这些移民到达他们的新岛屿,就需要有一个替换策略来确定移民替换了哪个个体。迁移和替换选择可以使用本系列到目前为止讨论的任何选择策略来执行。迁移的目的是将新的遗传物质引入一个岛屿。岛通常由具有相同超参数或均匀但具有不同超参数(如突变率、概率和交叉算子)的分段域初始化;或者,岛可能是不同的遗传算法变体。有许多方法来创建这些“岛”。
通过群集进行选择性交配
交叉的一个主要问题是,它可能会导致较差的个体。以下面的函数为例:

作者图片

作者图片
我们可以从拓扑中看到,在域的每个角上有四个相等的全局最小值。如果我们的算法有两个个体具有相似的适应值,但是在不同的位置,并且我们使用平均策略进行交叉,我们的后代将会在中间一个差的位置结束。这就是子代父代之争是一种有用的机制,因为父代不会因为子代的不足而被取代。另一方面,如果我们没有这个组件,我们可以通过集群实现选择性交配。这意味着个体只允许与同一个群内的个体交配,从而阻止两个欧氏距离较远的个体交配。然而,这种思路的唯一问题是,由于个体仅在同一集群内交配和繁殖,因此可能导致较差的探索,从而减少遗传物质的全球共享。选择交配可以很容易地用岛算法实现,因为只有同一岛内的个体才允许交配和繁殖。唯一的问题是确定岛屿的数量和谁去哪里。岛屿数量的解决方案是使用某种类型的标准,如 K-means 聚类中的轮廓得分

作者图片
k 均值聚类的工作原理是选择聚类的数量,选择初始 k 均值点,并根据其周围的点更新这些中心点。k 均值聚类的问题是聚类的选择和聚类的初始点。不幸的是,除非已知数据的先验信息,否则没有好的方法来定义初始点;但是,我们可以通过剪影法选择最佳的聚类数。该方法从两个初始聚类开始,并且增加,直到找到每个点聚类和对应的 k 个平均点之间的距离最小的聚类计数。这由介于-1 和 1 之间的轮廓得分表示,其中 1 表示聚类非常密集,没有重叠,正如我们在右侧图片中看到的那样,而-1 表示数据不属于聚类。我们不断增加分类计数,直到我们的最大分类选择,并选择具有最佳分数的分类计数。这可以在初始群体上使用,以将它分成要在孤岛算法中划分的聚类。请注意,选择统一的初始群体对 K 均值聚类不起作用;因此,当使用统一的初始种群时,我们可以改为将域空间分成正方形,并将这些子域中的每一个给定为一个岛,然后使用 K-means 聚类来选择谁与谁繁殖。
约束处理
如果你还记得单元 1)最优化理论,有四种类型的最优化问题
- 不受拘束的
- 约束
- 多目标
- 多解
无约束问题很简单,我们一直在做。然而,有一件事我们还没有涉及到,领域是离散的问题。解决这个问题最流行的方法是不简单地改变任何东西,而是在测试适应度函数时将变量值四舍五入到下一个整数。对于约束问题,通常的解决方案是简单地为不可行的解添加惩罚项,或者改变再生和初始化操作符,从而不产生不可行的解。对于多目标问题,可以通过简单地将所有目标函数相加并最小化总函数来创建一个集合函数;或者,可以使用帕累托优势,将父母和后代集中在一起,采用锦标赛式选择,选择最具优势的个体生存下来。
最后,多解问题可以通过使用一种形式的岛算法来解决。在多解问题中,全局最小值或最大值通常是已知的,但目标是找到发生这种情况的每个位置。因此,在我们的岛算法中,每当产生一个新个体,其适应值接近或等于全局最小值或最大值,但其欧几里德距离与岛的平均值相差某个值时,它迁移到平均值最接近的岛;然而,如果不存在其平均欧几里德距离不同于个体的某个值的岛,它自己创建自己的岛并无性繁殖,直到新的后代或新的迁移者出现。这样,岛算法可以通过使每个岛成为其自己的具有相同全局最小或最大解的聚类来解决多解问题。运行世代计数后,岛的数量表示找到的不同位置的数量。
如果您对这些类型的优化问题感兴趣,请查看我在优化理论上的帖子,在那里我详细讨论了这些类型的约束:
https://morganscottbrandon.medium.com/unit-1-optimization-theory-e416dcf30ba8
结论
总之,我们已经讨论了遗传算法中的高级概念,即在遗传算法中发现的主要变体,如何在进化过程中改变超参数,通过聚类进行选择性交配,以及如何处理约束问题。
在下一篇文章中,我们将总结第 3 单元,应用进化神经网络的权重进行时间序列分析!
第 4 单元)遗传程序设计
进化计算课程
涵盖遗传规划的主要课题,并将其应用于时间序列分析问题
大家好,欢迎回到进化计算的完整课程!在这篇文章中,我们将开始并完成本课程的第四单元,基因编程。在前一篇文章中,我们完成了单元 3,遗传算法,通过应用一种算法来进化用于时间序列分析的神经网络的权重。我强烈建议先阅读这篇文章,因为我们将尝试使用基因编程来解决同样的问题:
在本帖中,我们将简要介绍遗传编程,以及它在染色体表示方面与标准遗传算法有何不同。
目录
- 代表性差异
- 圣菲蚂蚁步道
- 设置问题
- 交叉算子
- 变异算子
- 应用—时间序列分析
代表性差异
遗传编程实际上是遗传算法的子集;然而,两者之间的主要区别是染色体的表示。标准遗传算法处理表型是点或向量的优化问题,但现在遗传编程中的表型是基于树的语法。
如果你不知道,所有的编程语言都是建立在巴克斯-诺尔形式(BNF)语法之上的,如果你没有上过任何自动机理论课,这仅仅意味着每种语言都有一套特定的规则来设置指令,编译器使用这些规则来确定代码是否可以运行。就像在标准英语中一样,英语语法告诉你如何将单词聚集在一起并构建句子。例如,“汤姆是……”,我们有“名词动词……”,英语语法告诉我们,动词“是”后面不能有动词,而是形容词、名词或副词。在编程中,当我们忘记一个关键字时,我们的 IDE 会告诉我们什么时候有“语法错误”,因为它知道基于这种特定于语言的 BNF 语法,什么需要在前面或后面出现。因为程序有一定的结构,遗传算法和语法的结合产生了遗传编程,它首先出现在进化指令集的场景中,称为程序。
除了遗传编程和标准遗传算法之间的表型差异,每个个体都有能力通过添加更多的终端和指令来增长或收缩其基因型。如前所述,遗传编程是基于树的,这意味着它需要自己的基于计算理论的语法,如终端集、函数和转换规则。作为一个简单的例子,下面我们有输入和目标输出的 XOR 真值表。

作者图片
给定语法、操作符和终端,我们想要发展一个基于树的指令集来解决这个问题。下面我们有一个解决这个问题的可能方案,使用一个通过遗传编程进化的基于树的结构。

作者图片
注意树的结构与决策树是多么的相似,遗传编程的许多应用之一是实际进化决策树和行为树,用于分类或玩游戏。
圣菲蚂蚁步道
遗传编程的第一个也是最广为人知的例子之一是在机器人领域。这个问题被称为圣菲蚂蚁踪迹。问题是,给定一个环境,食物被放置在地图周围的小径上,如下图所示,用有限的能量最大限度地收集食物。这个问题是用遗传编程解决的,通过进化出一套指令让蚂蚁去执行。

【https://commons.wikimedia.org/wiki/File:SantaFeTrail.gif 号
遗传编程模型在进化一个基于 if 和 else 语法树的树以基于某些输入采取定向步骤方面非常成功。
设置问题
在所有的进化计算中,有两个主要的先决条件需要注意,与所选的算法无关:
- 初始种群的创建
- 设计适应度函数
与标准遗传算法不同,遗传编程不能从域中均匀随机地创建初始种群。相反,它需要遵循依赖于问题的语法结构。为此,我们首先需要为问题定义 BNF 语法。然后,我们需要设置树的最小和最大深度,其中深度表示树中有多少层。定义最小值和最大值仅影响初始群体,这意味着所有初始代个体将处于最小和最大深度层之间,而后面的个体可以收缩或增长。
为了为每个个体创建树,我们从语法中随机选择一个函数元素,并通过随机选择终端或函数元素进行分支。像在用于约束问题的遗传算法中一样,使用该过程随机生成个体可能导致一些不需要的初始个体,即那些深度小于最小值或大于最大值的个体。为此,我们丢弃这些个体并继续采样,直到我们得到在这个范围内的个体。
取决于手头的问题,适应度函数可以有一系列方法来确定适应度值。就机器人学而言,可能有一些测试案例需要评估,以评估智能体的表现或游戏人工智能的行为树,你可以使用共同进化。与标准的行为和决策树一样,我们希望降低模型的复杂性,以便它可以很好地推广到新的输入,因此我们需要一种方法来惩罚过于复杂的模型。在遗传编程中,我们可以通过评估模型的膨胀来防止复杂模型的进化,膨胀是指当一棵树增加更多的终端节点和深度,而只是略微降低适应度值。
交叉算子
遗传编程中的交叉算子相当直观。正如我们从下面的第一张图中看到的,给定两个父代,我们在每个父代中随机选择一个点,并在该点交叉子树以创建一个后代。这可以针对一个后代进行,更直观的是针对两个后代,其中你得到一个父母的前半部分和另一个的后半部分。

作者图片
在进化计算的早期阶段,由于排列问题,遗传编程中的交叉通常不会被执行。排列问题类似于第 3 单元第 2 部分高级概念中讨论的选择交配问题。你可能有两个相似的适应值的父母,但是他们的架构是如此的不同,以至于把他们交出来会导致非常差的个体。这就是为什么在进化编程的早期,从来没有交叉,只有变异。然而,随着领域的扩大,出现了解决这些问题的方法。最常见的解决方案之一是只交换相似的个体;但是现在的问题是确定谁是相似的,因为你不能使用欧几里德距离。一种方法是实施遗传标记,允许具有相似遗传标记或子树的个体繁殖。
变异算子
像交叉一样,基因编程的变异是非常直观的。有许多不同的方法来执行变异,正如我们在下面看到的。在左上角我们有我们的原始树。我们的第一个突变是功能节点切换,我们将随机的功能节点切换到另一个可行的节点。接下来,我们进行终端节点切换,将一个随机终端节点与另一个可用节点进行切换。接下来,我们交换一个终结项的论点。截断是指我们通过随机删除单个功能节点来缩小树。我们还可以将随机高斯噪声添加到现有的数值中。最后,我们可以通过随机引入一个新的功能节点来扩展我们的树。

作者图片
应用—时间序列分析
因为遗传编程算法的表现型是基于树的,这非常适合在许多分类或游戏人工智能问题中进化行为或决策树。但是,它也可以用于执行回归。在我们的应用程序中,我们将评估前一篇文章中给出的相同的时间序列问题。为了节省空间,我不再重复问题陈述和动态,请阅读我以前的帖子:
我们的问题设置将与之前相同,循环所有可能的窗口大小,并从验证数据中选择每个窗口大小的最佳模型,作为在测试集数据上评估的最终模型。现在,从头开始编写一个遗传编程模型需要很多课外预备知识,例如自动机理论,我不会从头开始执行算法。相反,我将使用 gplearn,一个免费的 python 库,专门为分类和回归的遗传编程算法而设计。
https://github.com/trevorstephens/gplearn
下面是我们如何导入和运行算法,我们也可以使用许多其他超参数,但为了简单起见,我将其限制为以下几种:
我注意到该算法的成功非常依赖于初始群体,所以我在每个窗口大小下运行该算法四次,并保留运行中的最佳模型。不幸的是, gplearn 没有提前停止机制,因此验证数据仅用于确定最终模型;然而,为了模拟早期停止,进化的最大代数被限制为 100,以防止训练数据的过度拟合。运行脚本后,以下是最终结果:

作者图片
从结果中我们可以看到,最佳模型的窗口大小为 8。为了比较,我们将利用前一篇文章的结果,前一篇文章使用遗传算法和反向传播来训练一个神经网络来解决相同的问题。请参阅顶部链接的前一篇文章了解更多信息。遗传算法和标准反向传播的最佳模型窗口大小是 5 和 3,与遗传编程的 8 非常不同。就每个窗口大小的最佳模型的验证误差而言,平均 MSE 值大于遗传算法和反向传播;然而,标准偏差要低得多,比反向传播小 57%,比遗传算法小 42%。就测试集误差而言,MSE 大于遗传算法和反向传播算法;然而,就充分性而言,它是可接受的,仅比遗传算法差 10%,只是不比前两种方法更可取。
对于视觉效果,以下是使用窗口大小为 8:

作者图片
这是最佳模型的实际回归树:

作者图片
最终代码
与其他帖子相比,此帖子中使用的代码相对较短;不过,这里是我的 GitHub 页面,上面有最终回归树的完整脚本和图片:
https://github.com/OUStudent/EvolutionaryComputationCourse/blob/main/Unit4/genetic_programming.py
结论
这就结束了整个遗传编程单元!这绝对是这个系列中最短的,因为它需要自动机理论的知识来从头开发,所以我不得不使用外部库。
标准遗传算法和遗传程序设计之间的主要区别是染色体的表示,包括表型和基因型。遗传编程模型的表型是基于树的图,其中基因组具有通过添加新的末端节点和功能来收缩或增长的能力。
对于应用程序,除了使用 gplearn python 库来发展数据集的基本方程之外,我们涵盖了与上一篇文章完全相同的时间序列问题。结果表明,该算法对时间序列数据集的预测是成功的;然而,在将结果与前一篇文章中的两种方法进行比较后,它并不优于它们。敬请关注下一篇文章,我们将从第五单元进化编程开始。
单元 5 应用)优化约束非线性压力容器设计问题
进化计算课程
我们将应用进化规划来寻找压力容器设计问题的最佳解决方案
大家好,欢迎回到进化计算的完整课程!在本帖中,我们将应用我们所学的进化编程来优化受约束的非线性压力容器的设计,从而结束本课程的第 5 单元。因为这篇文章将只涉及概念的应用,请查看我详细解释进化编程的前一篇文章。您可以在这里找到它:
在上一篇文章中,我们讨论了经典的进化编程算法,我们将从头开始实现它来解决我们的问题!
目录
- 问题陈述
- 文学论文的帮助
- 设计我们的算法
- 进化规划算法
- 结果
- 密码
- 结论
- 参考
问题陈述
我们的问题是设计一个压力容器,它的目的是装液体或气体。目标是通过优化容器的设计变量来最小化材料成本。然而,我们无法实现绝对最小化,因为我们需要满足一些约束条件,以保持容器的安全性、有效性和物理特性。有四个感兴趣的变量:
- 内径— R
- 圆柱段长度— L
- 车身厚度— T_s
- 帽的厚度— T_h

作者图片
我们的问题有四个约束,其中之一是非线性的。这是我们希望最小化的函数和约束条件:

作者图片
问题的定义域集既是连续的又是离散的。半径(R)和长度(L)是范围从 10 到 200 英寸的连续值,而头部(T_h)和主体(T_s)的厚度是从 0.0625 到 6.1875 英寸的离散值,步长为 0.0625 英寸。
正如我们所看到的,我们有最终的约束问题,一个连续和离散混合的变量域,以及四个约束,其中一个是非线性的(G3)。这将是一个完美的问题来测试我们的经典进化规划算法的效率。如果你想要一篇关于最优化理论和约束问题的评论,看看我之前的文章:
文学论文的帮助
约束优化问题可能非常难以处理,因为我们需要一种方法使我们的算法只关注可行域空间并避开不可行解。作为单元 1,最优化理论的回顾,有两种主要的方法来处理约束:调整我们的算法以仅产生可行解(这对于许多约束可能是困难的)或者引入惩罚项来惩罚不可行解的适应值。
在 Adam Chehouri 等人于 2016 年发表的一篇名为“A Constraint Handling Technique for Genetic Algorithms using A Violation Factor”的文献论文中,作者实际上陷入了关于如何处理不可行解以使用遗传算法解决约束优化问题的相同问题[1]。他们提出了两种不同的算法,这两种算法的主要思想都是将好的可行解与坏的不可行解分开。在这种分割之后,他们按照适应值对好的解决方案进行分类,按照“坏”的程度对坏的解决方案进行分类,这是由违反了多少约束条件来决定的。为了繁殖的选择,他们从汇集的好的和坏的解决方案中的两个个体之间进行了锦标赛式的选择。如果两个好的解决方案被配对来战斗,那么具有最佳适应度的解决方案被选择来生存。如果两个糟糕的解决方案被配对来对抗,那么违反约束最少的解决方案获胜。如果一个坏的和好的解决方案被配对战斗,好的解决方案总是被选择生存。
这是他们算法的简要概述,因为他们包括许多不同的复杂组件,并在许多其他优化问题上测试了它们,所以如果你想了解更多关于他们的违反因子和算法,请查看他们的论文。出于我们的目的,我们将使用他们的算法的修改来解决我们的问题。
设计我们的算法
对于我们的问题,我们将使用上一篇文章中讨论的经典进化编程算法。该算法通过使用高斯变异、策略参数的对数正态自适应、我们实例中的标准偏差和精英主义来工作,以从父代和子代的集合中选择最佳的一半,同时通过比赛使用相对适应度。
受前面讨论的文献论文的启发,我们将按适合度对好的解决方案进行分类,而不是按坏的解决方案破坏了多少约束,而是按它们的差异的 z 分数的总和进行分类。例如,使用上述论文中给出的算法,如果一个解决方案只违反了所有四个约束条件的 1%,那么它将被拒绝,而只违反了一个约束条件 20%的解决方案将被接受。在我看来,第一个解决方案应该比后者被接受,即使它打破了更多的约束,因为打破约束的程度比后者大得多。人们可以通过简单地对违反约束的情况下的差异求和并使用该和进行比较来实现这一点。但是,由于每个变量的定义域不同,定义域中 1%的差异可能会导致不同的总和。例如,在我们的示例中,X1 和 X2 1%的差异是 0.06125,而 X3 是 9,X4 是 19。因此,如果我们将差异相加,一个 X4 只有 1%差异的个体相当于一个 X1 有 311%差异的个体。为了解决这个问题,我们需要使用 z 分数来衡量我们的差异。 Z-scores 是表示一个点离其中心平均值有多远的测量值,其中大于 3 的值意味着它是一个异常值,因为它大于平均值的 3 个标准差。我们不需要担心 z 值为-3 的值,因为这表明与其他值相比,个体的差异非常小。我们想要惩罚差异大的个体,因此这就是为什么我们不取绝对值。把好的坏的排序后,把两者按顺序组合,选出最好的一半生存。我们也将利用随机锦标赛的相对适合度。

作者图片
对于我们算法的整体设计,我们将遵循上面的伪代码。首先,我们初始化我们的种群,然后我们变异种群中的所有个体来产生后代。接下来,我们将父代和子代放在一起,并使用锦标赛风格选择来计算相对适合度。然后,我们将好的和坏的解决方案进行分类和汇总,选择最好的一半来生存。
进化规划算法
首先,我们首先需要指定我们的适应度函数,这将是优化问题本身,以及约束条件。在“约束”功能中,我们将计算每个人违反约束的次数;如果坏了,坏了多少。因为 X1 和 X2 是离散的,我们简单地将每个个体的这些变量值四舍五入到最接近的步长 0.0625:
接下来,我们将指定我们的初始人口。记住,每个个体不仅包含他们的变量值,还包含每个变量的策略参数。因为我们使用高斯变异,我们的策略参数将是标准差参数。初始化我们的生成变量很容易,只需从域中统一采样。然而,我们如何初始化我们的策略参数呢?好吧,这取决于设计师;因为较小的标准偏差会导致开发,而较大的标准偏差会导致勘探。对于这个应用程序,我试图使标准差覆盖总域大小的 1%到 20%之间。
因为进化编程对进化个体的行为感兴趣,所以变异是用来创造后代的唯一机制,没有交叉。我们通过使用对数正态自适应更新父变量值和策略参数来创建后代,同时保留原始父值。
接下来,我们需要计算相对适应度。我们对每个人进行循环,并计算其原始体能值比锦标赛中的体能值好多少倍:
现在,是时候解决我们的主要算法了。我将把它分成几个简单的步骤。首先,因为这是一个受约束的环境,我们需要确保我们每个人都遵守领域的界限。如果个体具有大于或小于界限的变量值,那么我们需要将策略参数减少 10%,以减少该变量的突变界限:
现在是计算相对适应度的时候了。我们将父代和子代结合起来,并通过锦标赛选择来计算相对适应度,仅针对好的解决方案,即没有违反约束的解决方案:
现在,我们需要根据糟糕的程度对糟糕的解决方案进行分类,在我们的实现中,糟糕的程度是违反约束的差异的 z 分数的总和:
最后,我们将排序好的和排序坏的解决方案组合在一起,然后使用精英主义从集合的群体中选取最好的一半:
结果
现在是时候运行我们的算法了。因为这是文献中一个众所周知的测试问题,我们需要一个基础来比较我们的算法。在文献论文“使用人工蜂群算法解决结构工程设计优化问题”中,Harish Garg [2]详细描述了他的人工蜂群在 50 次独立运行和平均每次运行 80,000 次函数评估后具有以下结果,其中函数评估是计算适应度函数的次数:

作者图片
现在我们知道了标准算法的结果,是时候运行我们的算法了。为了简单起见,我们打算每次运行 100,000 次函数求值,这将导致 1000 个初始群体和 100 次最大迭代。以下是我们独立运行 100 次后的结果:

作者图片
我们可以看到,我们的算法以 5805.339 的最佳适应度结束。相比之下,我们的算法确实找到了几乎等同于人工蜂群算法的最佳解决方案,只是我们的算法具有更高的中值、更高的平均值、更高的最差值和更高的标准偏差。看来我们的算法有能力找到一个接近最优的解决方案,但在重现结果是不可靠的。这是最终的最佳人选:

作者图片
要查看 100 次跑步的健身值范围,下面是每次跑步最佳健身值的箱线图:

作者图片
对于图形,下面是从所有运行中产生全局最佳解决方案的运行的平均值和最佳适应度:

作者图片
因为我们通过自适应策略参数来进化个体的行为,所以我们可以看到策略参数本身的进化。下面我们有用于变异第四个变量的标准差的平均值。我们可以看到,标准偏差从平均值 25 左右增加到几乎 50,并随着收敛而缓慢下降。请注意第二个峰值,它显示了自适应策略参数的能力,当在此期间减少产生不良结果时,它通过增加进行调整。

作者图片
代码实现
正如我所有的实现一样,你可以在下面我的 GitHub 页面上找到完整的代码:
*https://github.com/OUStudent/EvolutionaryComputationCourse/tree/main/Unit5
结论
总之,我们应用我们的经典进化规划算法来寻找一个现实世界的工程,非线性,约束优化问题的最优解。在将我们的算法与文献[2]中发现的人工蜂群算法进行比较后,我们的算法在多次独立运行后产生了寻找接近最优解的良好结果;然而,每次运行的最佳解决方案在独立运行中的适应性方面有很大差异,这表明我们的算法在仅少量运行中产生结果是不可靠的,并且需要运行多次迭代才能产生接近最佳的解决方案。
这就结束了单元 5)进化编程,请继续关注下一篇文章,在那里我们将开始和完成单元 6)进化策略,它是进化编程的表亲,并加入了交叉机制!
参考
[1]切胡里,亚当&尤尼斯,拉菲克&佩龙,让&伊林卡,阿德里安。(2016).使用违反因子的遗传算法约束处理技术。12.350–362.10.3844/jcs sp . 2016.350–362。
[2]用人工蜂群算法求解结构工程设计优化问题,工业与管理优化学报,10,3,777,794,2013–11–1,Harish Garg,1547–5816 _ 2014 _ 3 _ 777,人工蜂群,结构设计优化,非线性约束。约束处理,*
第 5 单元)进化规划
进化计算课程
涵盖进化编程的主要概念:变异和选择操作符
大家好,欢迎回到进化计算的完整课程!在本帖中,我们将开始课程的第 5 单元,进化编程。在上一篇文章中,我们通过解释标准遗传算法和遗传编程之间的主要区别,开始并完成了单元 4)遗传编程,此外还进化了时间序列分析问题的基本方程。你可以在这里查看:
https://morganscottbrandon.medium.com/unit-4-genetic-programming-d80cd12c454f
在本帖中,我们将涵盖进化编程和标准遗传算法之间的所有主要差异;即生存的变异和选择算子。如果这是你第一次看这个课程,请查看这两篇介绍进化计算的文章,这样你就能理解这里提出的概念:
目录
- 进化编程和标准遗传算法的区别
- 变异算子
- 突变的分布
- 选择运算符
- 更新策略参数的自适应
- 示例实现
- 结论
进化编程和标准遗传算法的区别
尽管名字相似,进化和遗传编程一点也不相似。在整个遗传算法诞生的早期,有许多不同的名称和方法被用来描述文献中流传的进化算法:遗传算法、进化编程、进化策略和差分进化。这些算法中的每一个本质上都是一种进化算法,但在某些方面,由于某些特征,它们彼此不同,因此作者必须通过选择不同的名称来区分它们。
进化编程实际上是最早引入的遗传算法之一。它起源于基本的人工智能原则,并希望模拟人工智能的行为。这种范式的重点是行为进化,而不是基因型。因为基因型不是进化出来的,亲本之间不使用杂交方法,只有突变。个体只能通过变异父母来创造,变异的数量被称为行为。
此外,没有使用原始适应度函数,而是偏向于相对适应度。相对适应度值用于代替原始适应度值来衡量个人相对于周围人的表现有多好。例如,参见下表,其中我们有一个由五名随机选择的个人组成的锦标赛,以及他们的原始和相对健康值(假设在此表中最大化)。相对健康度是通过当前个体比周围人的原始健康度好多少倍来计算的:

作者图片
下图给出了进化编程的基本算法概述。我们首先初始化我们的种群,整个一个循环,其中我们变异每个个体以产生后代,然后将后代和父母集合在一起,并使用锦标赛风格选择来计算相对适应度。在这个过程之后,我们使用某种类型的选择过程来选择幸存者:

作者图片
变异算子
如前所述,进化规划的重点是进化个体的行为,而不是基因型,因此突变是唯一使用的繁殖算子,没有交叉。因此,变异算子需要能够足够有效地引入新的遗传物质,以适应个体之间通过交叉缺乏遗传共享的情况。对于变异算子,有三种主要类型:
- 非自适应(静态)
- 动态的
- 自适应
至此,我们已经介绍了静态和动态变异操作符。静态仅仅意味着我们突变的域保持不变或静态,一个例子是在[-1,1]之间的某个最大界限内突变。动态是指随着每一代慢慢降低这个最大变异界限,以鼓励早期世代的探索和后期世代的开发。请注意,我们已经通过使用单元 3)遗传算法第 1 部分中的逻辑衰减算法实现了动态变异。另一方面,自适应变异指的是变异不是静态或动态的,而是能够在任何给定时刻增加、减少或保持不变,这完全取决于环境的相对适应度。我们将在后面的文章中讨论自适应策略。下面我们有一个突变后代的一般方程:

作者图片
正如我们在上面看到的,后代是通过将某个值(δx)加到父代 x 上而创建的。这个δx 的值由下面的等式计算:

作者图片
正如我们在上面看到的,delta x 是通过将步长 nu 取向概率分布中随机生成的值(大写 eta 和策略参数 sigma)的方向来计算的。不要被符号弄得不知所措,我们以前做过这个。在单元 3)遗传算法中,我们以两种不同的方式进行变异,通过添加一个小的随机生成的值,该值来自高斯分布或均匀分布。对于高斯,我们的概率分布将是正态高斯,我们的策略参数将只是标准差,而我们的步长将是平均值。对于基于均匀变异的变异,我们的概率分布将是均匀的,我们的策略参数将只是分布的界限,而我们的步长将是 1。
我们可以为每个个体个性化一组策略参数,而不是对整个世代使用相同的策略参数,这样他们就有能力表征他们的突变范围。因为每个个体可能需要用不同的值来变异,所以我们可以改变染色体,不仅包括个体的变量值,还包括他们自己的策略参数。这方面的一个例子是贫穷的个体比健康的个体有更高的变异标准偏差。这可以从下面看出:

作者图片
在进化编程中,进化的“行为”机制是这些策略参数,这意味着,除了被进化的个体的变量之外,它们的策略参数也是如此。
突变的分布
在进化规划中有许多用于变异的分布;最常见的有以下几种:
- 制服
- 高斯的
- 柯西
- 征收
- 指数的
- 混乱
- 组合分布

作者图片
因为我们介绍了一些您可能没有听说过的发行版,所以我决定回顾一下每个发行版的基础知识。柯西分布类似于正态高斯分布,只是尾部非常宽,我们可以在左上图中看到一个例子,我们已经绘制了分布密度的 95%。 Levy 分布,看起来非常类似于卡方分布,模拟非负值并且有一个大的驼峰。双指数分布类似于柯西分布和正态分布,因为它具有类似的钟形曲线形状,除了它在均值和较长的尾部有一个峰值。对于混沌分布,有许多不同的类型,但上面是一个使用正弦波函数的例子。在 EP 中,最常见的组合分布是高斯与柯西配对。

作者图片
选择运算符
如前所述,所有的亲本都根据它们自己独特的策略参数变异一些值来产生后代。这些后代和他们的父母被集中在一起,某种类型的选择过程被用来选择谁存活下来。相对适合度用于原始适合度,其中将一些随机选择的竞争者汇集在一起,并使用以下公式计算它们的相对适合度值(假设最大化):

作者图片

作者图片
我们已经执行的第一个等式,简单地说,就是一个人比锦标赛中的其他人拥有更大(假设最大化)的原始体能分数的次数总和。然而,第二个等式是计算相对适合度的不同方式,它创建适合度值之间的比例,并查看该比例是否大于 0 和 1 之间的随机均匀值。
在计算出相对适合度值后,我们使用某种标准的选择程序从将存活下来的后代和父母中进行选择。这可以通过锦标赛选择来实现,其中平局由波尔兹曼选择解决,比例选择,或精英主义(精英主义只是指选择最好的一半,而不是一些最高百分比)。
更新策略参数的自适应
现在,我们将讨论三种非常常见的策略参数自适应运算符:加法、乘法和对数正态。在加法(如下图)中,策略参数通过将某个函数的平方根乘以一个均值为 0、标准差为 1 的随机正常值相加来更新。如果大于零,此函数返回策略参数,如果策略参数为负,则返回某个下限 gamma:

作者图片
对于乘法方法(如下所示),您将启动策略参数乘以下面右侧显示的值,其中 gamma 1、2、3 是用户选择的控制参数, n sub t 表示允许的最大代数。

作者图片
最后,最常用的方法是对数正态运算符。在这种情况下,后代创建如下:

作者图片
其中,我们的概率分布是以 0 为中心的高斯正态分布,标准差为 1,步长为σ。随着每次迭代,我们通过下面的等式更新我们的策略参数 sigma

作者图片
现在,在实践中,已经注意到对数正态算子在较小的参数值下有收敛过快的趋势,因此它通常伴随有最小值,这样,如果它下降到最小值以下,它就切换到该值。
示例实现
这里,我们将介绍 EP 算法的三个常见示例实现:
- 经典进化编程
- 快速进化编程
- 指数进化规划
首先,经典进化规划算法使用高斯变异来变异基因组,使用对数正态适应来更新策略参数,使用精英主义来选择生存。
第二,快速进化规划算法使用柯西分布来变异尺度参数为 1 的基因组,使用对数正态适应来更新策略参数,使用精英主义来选择生存。
最后,指数进化规划算法使用双指数分布作为变异算子,其中策略参数和选择过程是可变的。
结论
总之,进化规划不同于标准的遗传算法,它关注的是个体的行为,因此没有使用交叉来支持变异。行为由分布中的策略参数建模。此外,不是使用玻尔兹曼选择来选择是孩子代替父母,还是孩子总是代替父母,而是将孩子和父母聚集在一起,基于他们在整个一代中的相对适应度为生存而战,这是通过他们比随机选择的比赛中有多少对手更好来计算的。相对适应度揭示了个人相对于当代人的表现有多好。在计算相对适合度后,可以使用标准的生存选择方法。

作者图片
在应用方面,进化规划最常用于调度与路由、电力系统、设计系统等约束环境。请继续关注下一篇文章,我们将通过解决一个真正的约束非线性规划问题来结束第 5 单元,这个问题被称为压力容器设计问题,我们可以在上面看到一个基本的图表。
单元 6)进化策略——寻找帕累托前沿
进化计算课程
涵盖进化策略的主要概念:加号和逗号策略,并应用它们来寻找多目标问题的帕累托前沿
大家好,欢迎回到进化计算的完整课程!在这篇文章中,我们将开始课程的第 6 单元,进化策略。在前一篇文章中,我们开始并完成了第五单元进化编程:
在本帖中,我们将讨论进化策略和标准遗传算法之间的主要区别。我们还将涵盖进化策略的主要概念,即选择的加和逗号策略。作为总结,我们将应用我们的算法来寻找多目标问题的帕累托前沿。
如果你不熟悉多目标问题或帕累托前沿,请查看我之前在优化理论方面的帖子:
此外,因为进化策略是进化编程的一个扩展,请参见上面链接的我以前关于进化编程的文章。
目录
- 进化策略和标准遗传算法的区别
- 选择运算符—逗号和加号
- 多目标问题陈述
- 文献论文:多目标算法
- 进化策略算法
- 结果
- 密码
- 结论
进化策略和标准遗传算法的区别
尽管名字中有“策略”一词,进化策略并不是进化算法的“策略”领域;相反,它本身是进化算法的一个子领域,只是碰巧被称为进化策略。
进化策略与进化编程极其相似,从某种意义上说,是进化编程的延伸。进化编程主要关注通过一种先进的自适应变异算子来进化个体的行为。因此,个体之间没有使用基因共享,这意味着没有交叉,只有突变。这样,进化规划是一种有限的算法,因为后代只有一个父母的遗传组成,而不是通过多组父母的全局共享。进化规划算法的成功是由于其先进的变异算子。通过这种方式,我们可以将进化规划扩展到包括交叉,即多个父母之间共享基因组成。这正是进化策略所做的,它是进化编程的交叉扩展。
进化策略与标准算法的区别还在于它们的生存选择算子,即逗号和加算子。在进化策略中,基本算法如下图所示:

作者图片
初始化我们的种群后,我们进入一个循环,在这个循环中,我们通过交叉使用随机选择来创建后代;然后,我们通过先进的自适应变异算子对所有交叉子代进行变异;接下来,我们将我们的后代和父母聚集在一起,使用逗号或加号策略来生存。此外,在进化策略中,交叉后代的变异只有在更好的情况下才会被接受。
选择运算符—逗号和加号
在进化策略中,通过随机选择来进行交叉选择;然而,有两种选择谁幸存的特定策略:逗号和加号。 Plus 策略产生与父代数量相同或更多的后代,然后执行精英主义,从汇集的一代中选择最好的。逗号策略产生比父代更多的后代,并执行精英主义,从汇集的一代中选择最好的,并添加年龄成分。年龄是通过解决方案存活了多少代来计算的。一旦解的年龄达到其最大值,无论如何它都被丢弃,即使它具有最大的适应值。下面我们有这两者的确切定义:

作者图片
多目标问题陈述
到目前为止,我们一直在处理寻找单个优化问题的最小值。现在,我们将进入多目标问题(MOP)。MOP 是指我们希望使用相同的领域空间同时最小化一组优化问题的问题。有两种主要的方法来处理 MOP,加权聚合和帕累托占优。加权聚合的工作原理是简单地对每个函数进行加权,并将它们的函数值加在一起形成一个聚合,并试图使该和最小化。另一方面,帕累托优势引入了解决方案之间优势的概念,目标是返回帕累托前沿,这是一个决策向量前沿,它强烈支配输入空间中的所有其他决策向量,但彼此仅微弱支配。如果你不熟悉帕累托占优,请查看我以前关于最优化理论的帖子,这样我就不会在这里重复了。
我们要解决的目标是同时最小化以下两个目标:

作者图片
通过使用微积分,F1 函数的全局最小值在 x=0 向量处,而它在 F2 的 x=2 向量处。正如我们在所有 MOP 中看到的,每个目标的全局最小值都是不同的,因此找到一种在目标之间折衷的方法是至关重要的。在给定一组权重的情况下,我们可以很容易地将其转换为聚合函数,然后将该值最小化,这没有问题:

作者图片
然而,我们将使用帕累托优势来寻找最佳决策向量的帕累托前沿。这个问题的精确帕累托前沿如下:

作者图片
可以看出,我们有能力以 F2 为 4 为代价将 F1 最小化为 0,以 F1 为 4 为代价将 F2 最小化为 0,或者选择帕累托前沿上导致两个函数的最小化之间的折衷的任何其他决策向量。
文献论文:多目标算法
我们以前从未讨论过如何实现基于 Pareto 优势的 MOP 算法。作为一点启发,我们要看看文献论文中提出的通用算法,《帕累托存档进化策略:帕累托多目标优化的新基线算法》 约书亚·诺尔斯和大卫·科恩[1]。他们的算法使用了一种我们还没有介绍过的进化策略,即 (1+1)加策略。这个策略实际上甚至不是一个基于种群的算法,因为它接受 1 个父代并产生 1 个子代。他们的用于解决多目标问题的 Pareto 存档算法以两种方式工作,第一种是候选生成器,第二种是存档更新器。目标是创建候选解决方案,然后将其添加到包含不受任何其他解决方案支配的解决方案的“档案”中,本质上是构建帕累托前沿。它们的候选生成器算法如下所示:

作者图片
它的工作原理是创建一个初始个体,将其添加到档案中,然后通过突变连续创建一个单独的后代,然后根据优势取代父代被拒绝或接受。如果子代比父代被接受,则它进入档案更新算法以查看它是否被档案接受。存档接受算法如下所示:

作者图片
归档的目标是自下而上地构建帕累托前沿,并提供固定大小的解决方案。我们希望我们的存档具有跨越整个 pareto 前沿的解决方案,而不仅仅是单个位置,因此我们实现了一个相似性度量,以防止解决方案过于接近和聚集。
我们将通过实现基于群体的 plus 算法而不是单个个体来为我们的应用程序更新该算法。我们将使用三个随机选择的亲本之间的全局交叉来创建后代,对数正态自适应高斯变异,以及基于 Pareto 优势的锦标赛式相对适应度。此外,我们使用上面描述的相同算法更新我们的档案,其中我们通过欧几里德距离测量解决方案的相似性。这样,与档案中的成员解具有相似欧几里德距离的候选解将被扣分。我们算法的基本流程如下:

作者图片
我们创建两个循环,一个内部循环负责创建后代并更新档案,另一个外部循环使用不同的随机生成的群体重复内部循环多次。用许多不同的初始代重复我们的算法的原因是,我们的内部循环的群体将最终收敛到已经存在于档案中的点,因此它们将由于相似性而受到严重惩罚。因此,在我们收敛到一个点之后,我们用一个全新的群体重新开始,以便它现在学会避开归档中已经存在的解决方案。对于生存的选择操作符,我们将实施加法策略,其中后代的数量等于父母的数量,并且我们使用来自混合的后代和父母的精英主义来计算下一代。
进化策略算法
既然我们已经讨论了问题陈述和我们的算法,是时候用 python 来实现它了。首先,让我们为我们的 MOP 定义两个目标:
我们将使用相对适应度,通过锦标赛选择,基于帕累托优势。相对适合度是通过个人在锦标赛中“支配”其他解决方案的程度来计算的:
现在我们将定义我们的交叉和变异方法。我们将有两种交叉方法来处理多亲重组,平均和“直觉”,参见我以前的帖子对这两种方法的解释。此外,我们的变异操作符将与进化编程的前一单元中的相同,如果你不熟悉变异操作符,请参阅那篇文章。
在这里,我们将定义我们的繁殖函数,它将接受一组父母,他们的策略参数,交叉方法,并输出后代。将产生两个后代,一个仅来自交叉,另一个来自交叉后代的突变。因为变异只有在交叉后产生更好的后代时才会被接受,我们将会有一场基于帕累托优势的后代之战,在这场战争中,支配另一个后代的后代将被选择。如果双方都弱于对方,将随机选择获胜者:
现在是我们主要进化算法的时候了。不幸的是,这是太长的细节在这里,所以我留下了评论,将解释到底发生了什么。请务必理解上面给出的算法图,因为下面的代码遵循相同的结构:
https://gist . github . com/ou student/66122 ce 9 e 2249 b5 a 76 f 450979740 da 25
结果
现在是时候测试我们的算法了。我们将以 100 个个体的初始化规模运行我们的算法,10 个变量用于我们的 MOP,3 个双亲用于交叉。因为这是一个相对简单的 MOP,我期望每个种群在 40 代内收敛到一个解。我们将重复这个过程总共 100 次。我们将使用两种交叉方法和两种不同的最大相似性度量来测试我们的算法。
以下是使用最大相似距离为 2 的第一种平均交叉技术的结果:

作者图片
正如我们所看到的,我们的档案几乎跨越了整个 pareto 前沿,在左上角聚集成 F1 的最小值,在中间是 F1 和 F2 的折衷。我们的大多数解决方案并没有完全触及帕累托前沿,只是在它上面徘徊了一点点。
下面是使用第二种直观的交叉技术的结果,最大相似性距离为 2:

作者图片
正如我们所见,直观的交叉技术跨越了比平均更多的帕累托前沿,同时也更接近实际前沿。
最大相似距离为 0.75 的测试
现在,我们将再次测试我们的交叉方法,除了最大相似性距离为 0.75:

作者图片

作者图片
从结果中我们可以看出,两种交叉方法仍然探索不同的帕累托前沿范围;然而,减少的最大相似性距离导致档案聚集在一起并更靠近前端。
总之,这两种交叉方法将导致对 pareto 前沿的不同探索,因此在实践中有必要执行这两种方法,并将最终档案汇集在一起,并基于 pareto 优势进行更新。此外,最大相似性距离的选择直接影响档案中的点可以变得多接近。较小的最大距离将导致点更接近帕累托前沿,同时也更接近。最大距离越大,点离帕累托前沿越远,但前沿的跨度也越大。
密码
你可以在我的 GitHub 上找到这个进化课程的完整代码:
https://github.com/OUStudent/EvolutionaryComputationCourse/tree/main/Unit6
结论
进化策略是进化编程的扩展,其中进化编程的焦点是仅通过变异来进化解决方案的行为。然而,进化策略在此基础上进行了扩展,引入了基因组的交叉以允许种群内的基因共享,同时还使用了先进的自适应变异算子。
在这篇文章中,我们研究了用于处理多目标问题的 Pareto 存档进化策略算法[1],并受此启发,设计了我们自己的算法来寻找一个简单的多维 MOP 的 Pareto 前沿。结果表明,两种交叉方法导致对 pareto 前沿的不同探索,其中档案的紧凑性与最大相似性距离成正比。
敬请关注下一篇文章,我们将在其中讨论第 7 单元自动机器学习的差异进化!
参考文献
[1] J. Knowles 和 D. Corne,“Pareto 存档进化策略:Pareto 多目标优化的新基线算法”,1999 年进化计算大会会议录-CEC99(目录。№99TH8406),1999 年,第一卷第 98–105 页,doi: 10.1109/CEC.1999.781913。
第 7 单元)差异进化—自动机器学习
进化计算课程
将差分进化的概念应用于在 CIFAR-10 数据集上进化深度卷积神经网络的架构
大家好,欢迎回到进化计算的完整课程!在这篇文章中,我们将开始并完成课程的第 7 单元,差异进化。在前一篇文章中,我们应用了一种进化策略算法来寻找多目标问题的 Pareto 前沿。您可以在下面找到它:
如果你是这门课程的新手,请看看我以前的帖子,尤其是这两篇,因为我在那里详细介绍了进化计算和遗传算法的基础知识,因为我没有太多时间在这里重复我自己:
在这篇文章中,我们将介绍差分进化和标准遗传算法之间的主要差异,变异和交叉的单位向量的创建,不同的参数策略,然后以一个自动机器学习的应用程序结束,在这个应用程序中,我们将进化一个卷积神经网络的架构,用于对 CIFAR-10 数据集上的图像进行分类。我没有时间介绍卷积神经网络是如何工作的,也没有时间介绍通过 Keras 创建模型的微调,所以请在进一步深入之前刷新一下自己。
这篇文章将是我迄今为止最长的一篇,所以请喝一杯,准备好潜入兔子洞吧!
目录
- 差异进化和标准遗传算法的区别
- 通用变异和交叉算子
- 单位向量的创建
- 参数策略
- 问题陈述
- 创建模型架构:模块化
- 排列问题
- 差分进化算法
- 结果
- 代码
- 结论
差异进化和标准遗传算法的区别
尽管差分进化有“差分”这个词,但遗传算法的这个子领域与微分导数或方程没有任何关系,我知道这可能会让一些人松一口气。差分进化与标准遗传算法的不同之处在于,它依靠单位向量中的距离和方向信息进行繁殖。另一个独特的特点是,交叉应用于变异后,而不是相反。此外,变异算子不是像前面的算法那样由概率分布产生的,而是由单位向量产生的。该算法的一般流程可以描述如下:

作者形象
初始化种群后,我们使用某种类型的选择程序(我们将在下面讨论)进行多亲生殖。从亲本集合中,我们通过创建单位载体进行突变,然后与“主要”亲本杂交,创建最终的后代。从后代和“主要”亲本中选出最好的,供下一代存活。
一般变异和交叉算子
差分进化中的变异算子由两部分组成,即差异向量和目标向量。差异向量是两个或多个父代之间差异的向量,其中目标向量是在创建单位向量时其方向将被优先化的父代。单位载体是突变过程的最终产物,然后传递给当前“主要”亲本,即突变操作中不包括的感兴趣亲本。下面我们有一个精确的定义:

作者形象
我们可以在下面看到单位向量的几何幂:

作者形象
以上我们有三个亲本载体,即 X1 、 X2 、 X3 。我们创建了 X2 和 X3 之间的差异向量,然后按β进行缩放。经过这个尺度后,我们将其加入到我们的目标向量 X1 中,得到最终的单位向量 U 。正如我们所看到的,差分进化依赖于这些单位向量来操纵域空间,而不是使用来自概率分布的随机突变。差异进化的力量在于能够利用群体中的方向信息来创造后代。
差分进化中的交叉就像标准遗传算法,这意味着我们有两种类型:平均型和直觉型。在我们创建我们的单位向量之后,我们选择不同于上面使用的“主要”亲本,然后在这个新亲本和单位向量之间执行交叉以获得最终的后代。我们可以在下面看到一个例子,我们在父向量和单位向量之间交换变量值,以创建两个新的后代:

作者图片
单位向量的创建
希望你现在可以看到,创建新后代的主要机制是通过单位向量的可变性,这直接影响模型的结果。
在差分进化领域,有许多不同的策略来创建单位向量,称为 DE/x/y/z 策略,其中 x 表示选择的目标向量, y 表示差异向量的数量, z 表示交叉方法。以下是最常见的策略:
DE/Best/1/z: 该方法仅使用一个差向量,其中父代是随机选择的,其目标向量是当前种群中的最佳个体:

作者图片
DE/Rand/n/z: 该方法通过选择一个随机目标向量和 n 个随机选择的差异向量来工作:

作者图片
DE/Rand-to-Best/n/z: 该方法通过在最佳个体和随机母体之间引入线性组合来创建目标向量,并且 n 随机选择的差异向量的数量:

作者图片
DE/Current-to-Best/n/z:该方法通过将“主要”父代与最佳个体合并来创建目标向量,以及 n 随机选择的差异向量的数量:

作者图片
使用随机个体作为目标向量的问题是,我们的算法没有全局方向可循,导致收敛性差,但探索性好。然而,使用目标向量的最佳个体将引导群体朝向中心位置,但是会导致过早收敛。通过使用组合策略可以在两者之间达成妥协。组合策略只是在一组策略之间来回切换,或者是迭代地,或者是概率地。
参数策略
与进化算法中的所有参数策略一样,我们可以使用静态、动态或自适应更新过程。你可以通过阅读前面的单元来深入了解这些类型,特别是单元 3)用于动态的遗传算法高级概念和单元 5)用于自适应的进化编程:
差分进化中的主要参数是缩放系数, beta ,通常取值在【0,无穷大】之间。其中较小的值相当于较小的步长(收敛),较大的值相当于较大的步长(探索)。当使用一个差向量时,标准情况是β值在值 0.5 附近,并按照差向量数量的比率减小。
对于动态更新,我们可以使用逻辑衰减或线性减少,这在上面发布的前几个单元中有所涉及。对于自适应,我们可以调整我们的算法,使得当最小值和最大值之间的适应度差距开始缩小时,β将增加,或者当适应度差距开始增大时,减小β。通过这种方式,当群体中存在大量多样性时,我们会采取较大的步骤,当我们开始收敛时,我们会采取较小的步骤。
问题陈述
我们的问题将是为 CIFAR10 小图像分类数据集发展卷积神经网络的最佳架构。 CIFAR10 数据集包含 60,000 张 32x32 RGB 彩色图像,有十个可能的类别。在这里,我们可以看到 10 个可能类别的一些例子:

作者图片
如果您想了解更多关于数据的信息,请访问下面的机器学习知识库:
选择卷积神经网络的模型结构一直是参数调整的一个非常不确定的领域。通常,模型架构是由具有丰富经验的领域专家构建的;然而,随着自动化机器学习的出现,新颖的程序员可以在几乎没有经验的情况下构建高效的模型。自动机器学习是优化和机器学习理论的一个领域,其目标是为给定的算法找到最佳的超仪集。在我们的例子中,这些超参数对应于 CNN 的架构。
为什么要用差分进化进行自动机器学习?与所有进化算法一样,差分进化是一种先进的导向随机搜索技术,在优化函数的导数未知的优化问题中取得了很好的成功。当优化问题不可微时,如何使用标准的数值方法来调整神经网络的结构?简而言之,你不能,这就是为什么我们求助于非经典优化算法,如进化计算。其他用于调整 CNN 架构的非经典优化算法包括网格搜索;然而,网格搜索的主要问题是层的可能组合的数量随着每个附加层呈指数增长,因此网格搜索实际上在计算上是浪费的和昂贵的。
为什么使用差分进化而不是其他进化算法?几乎在每种情况下,对于大多数优化问题,标准遗传算法都将优于差分进化;然而,差分进化是唯一使用方向信息的进化算法。方向信息的使用是至关重要的,因为该算法通过朝向目标向量的方向采取“贪婪”方法。这样,差分进化将比其他进化算法更快地找到最小值;然而,这个最小值不能保证是全局的或好的最优值,因为算法的探索性很差。因此,当优化问题只有一个或几个局部最优解时,差分进化优于标准进化算法;然而,当输入空间有许多局部最优解时,差分进化应该是你最后的选择。在进化深度神经网络上使用标准进化算法的问题是,适应度函数实际上是从给定的架构中训练模型,这可能需要几个小时甚至几天。这样,我们需要一个快速收敛的算法,以防止运行几天或几周。因此,我们作出一个关键的假设,即我们的结构的输入空间只有一个或几个局部最优值。另一方面,如果您有多个处理器和 GPU 可以分散计算,那么使用标准的遗传算法将是有益的。
在进化模型架构中,另一个可行的差异进化的竞争者是进化编程,在第 5 单元中介绍。在不同的架构上执行交叉可能会产生所谓的置换问题,这将在后面讨论,尽管父母非常优秀,但后代却非常差。一个快速的解决方案是不执行交叉,只执行变异,这正是进化编程所执行的。此外,因为进化编程不执行交叉,它允许物种形成,物种形成,这将很快被涵盖。
创建模型架构:模块化
为了创建我们的算法,我们首先需要定义 CNN 的可能架构。我们的 CNN 将由个模块组成,其中每个模块都是一组可能的层。为了扩大我们的网络规模,我们可以堆叠这些模块,以在我们的模型中创建深度。我们的网络将分为两个部分,即卷积网络和深度网络,其中每个网络都由各自的模块组成。首先,对于我们的卷积模块,我们有可能的层:卷积、池化、批量归一化、激活函数和退出。为了给我们的模型创造深度,我们把这些模块堆叠起来,得到一个完整的网络。正如我们在下图中看到的,网络中的每个模块都可以扩展为以下几层:

作者图片
为了让我们的算法更强大,它将有能力在每个单独的模块中包含这些层。此外,如果选择包含,它还将能够为每个层选择超参数。以下是每个单独卷积模块中每个层的可能选择:

作者图片
正如我们所看到的,该算法将能够包括非卷积层以及调整它们的超参数。然而,该算法将不具有进化激活或优化算法的能力。这背后的原因将在下一节解释。
对于深度模块,我们有可能的层:密集、批量标准化和丢弃。与卷积部分一样,我们可以堆叠多个深度模块来创建深度网络:

作者图片
为了让我们的算法更强大,它将能够在每个单独的模块中包括批量范数和漏失层。此外,如果选择包含,它还将能够为每个层选择超参数。以下是每个单独模块中每层的可能选择:

作者图片
正如我们所看到的,该算法将能够选择密集层的节点数量,以及是否包括其他层及其相关的超参数。
总的来说,训练的每个模型将具有下面的结构,其中算法将具有关闭每个单独模块内的某些层并调整它们的超参数(如果包括的话)的能力。

作者图片
如果你不知道,在这个实验中,我们将允许算法发展四个卷积模块,并与两个深度模块配对,以创建网络模型。
排列问题
排列问题是进化计算中常见的问题。排列问题、又称竞争约定问题、特指与两个或两个以上的父母进行杂交以获得后代的问题。下面的例子很容易解释。假设我们正在进化网络群体的权重、层数、节点数以及每层的激活函数。下面我们有两个被选中的父母和他们的单个后代:

作者图片
正如我们所看到的,两个父架构非常不同,一个父架构只有 2 个隐藏层,每层 9 个节点,而另一个父架构有 4 个隐藏层,节点数从 6 个减少到 3 个。尽管他们的架构不同,让我们说两个父母有相似的健康分数。这里我们有一个竞争约定问题的主要例子,我们有两个不同的模型架构用于解决同一个问题。问题是,父代之间的架构如此不同,以至于当我们执行交叉时,我们会得到一个混合后代,它具有来自父代的权重组合,这些权重是针对一个架构专门设计的,它们被扔在一起以创建一个全新的架构。更具体地说,当父母之间的网络架构的表型如此不同,以至于许多遗传信息在交叉过程中丢失,经常产生极其差的后代时,就会出现置换问题。排列问题最常见的解决方案是要么不要以这样的方式进化架构,这样问题就不会发生,要么不执行任何交叉,只执行变异,这正是进化编程所执行的,在第 5 单元中讨论。然而,另一种常见的策略是创造物种形成,其中只有结构相似的个体被组合在一起形成一个物种,只有同一物种内的个体才被允许繁殖和杂交。这样,我们就可以在进化过程中拥有相互竞争的物种。
在我们的问题中,我们可以有两个完全相同的 CNN 架构,但是具有不同的优化器或激活函数,结果将会显著不同。改进优化器或激活函数将比包含一个层或多个节点产生更大的影响,从而产生排列问题。为了避免这一点,我们可以在我们的算法中实现物种形成,但这将是未来的冒险,因为我不想在这里过于复杂的事情。因此,取而代之的是,我们不会进化优化器或激活函数,让它们保持静态。
差分进化算法
我们将遇到的主要问题是,我们的适应度函数将是训练模型。这里我们有一个选择,我们要么训练几个时期,要么直到收敛/提前停止。如果我们只训练几个时期,我们会限制较大模型的适合度,这可能需要更长的时间来获得好的结果,但是如果我们训练到收敛,那么每个模型可能需要 20 分钟来运行。为了确保我们的算法不会偏向于较小的模型而不是较大的模型,我们必须训练直到收敛/提前停止。因此,现在我们不得不处理长时间的训练。
我测试了模型的初始群体,训练每个模型平均需要 12-15 分钟。假设我们的初始种群只包含 10 个个体,运行每一代大约需要 2 个小时…四天半的时间来运行 50 代…没有人能做到…
为了绕过这个问题,我们可以在来自训练数据的小样本上训练我们的模型,然后在算法已经收敛于实际训练数据之后训练我们的最终一代,以获得我们的结果。 CIFAR10 数据集有 50,000 张训练图像和 10,000 张测试图像。我们可以通过只对 20,000 张训练图像进行训练,将每个模型的平均训练时间减少到 1.5 分钟。这样,我们可以将初始种群增加到 20 个个体,每一代只需要 30 分钟,相当于一天多一点的时间运行 50 代,比以前好得多。
对于预处理,我们将通过最大像素值 255 来缩放我们的图像,使得每个像素值的输入在 0 和 1 之间。此外,执行图像增强以增加模型的泛化能力也是常见的。图像增强是一种通过简单地随机翻转、旋转和移动现有训练图像来人工创建更多训练图像的方法,以便模型可以很好地推广到不同的图像增强。下面是一个例子,我们稍微改变了单个图像的方向:

作者图片
执行图像增强的问题在于,它会创建新的图像,从而增加训练数据集的大小和训练时间,这正是我们在训练数百个模型时所不希望的。为了解决这个问题,我们将在进化过程中只在训练数据集的给定子样本上训练我们的模型,然后在完整的训练数据集上训练最终模型,然后在具有增强图像的数据集上训练最佳模型。这样,我们可以最小化训练时间,最大化进化搜索过程的能力。
如前所述,差分进化有一个贪婪的方法,通过使用目标向量。为了防止在演进过程的早期就提前收敛到特定的架构,我们将只使用一个不同的向量来实现 DE/Rand/n/z 策略。换句话说,这就是我们创建单位向量的方式:

作者图片
我们的算法将如下工作。对于这一代中的每个个体,我们将通过上述突变过程创建一个单位向量,然后进行交叉以获得后代。如果子代架构比“主要”父代产生更好的验证准确性,则父代将被替换。对于交叉,我们将实现平均技术,我们将简单地平均父母之间的每个模块以获得后代。
即使我们利用随机个体作为目标向量,因为我们只接受比‘主要’父代更好的后代,我们的算法也不会丢失好的解,并且最终会收敛或停滞。
结果
每个模型被允许进化四个卷积模块和两个深度模块。在初始种群为 20 个个体的 50 代之后,我们的算法已经收敛到一个架构。以下是每代的最佳和平均验证准确度分数:

作者图片
我们可以看到,我们的算法在大约 22-24 代时收敛到一个特定的架构,因此最佳适应度停滞不前;然而,随着方向向量开始指向该通用架构,平均适合度仍然缓慢增加。
收敛后,在整个数据集上训练最终生成,直到收敛,这里是最终验证准确度分数的箱线图:

作者图片
正如我们从上面看到的,模型在整个数据集而不是子样本上训练后,在获得更好的验证分数方面表现得更好。现在,从上面描述的运行中选择最佳模型再次在整个数据集上运行,除了这次使用图像增强的训练数据来提高通用性。这是最终的模型架构,其中每个模块都有颜色编码:

作者图片
更具体地说,以下是来自 Keras 的模型摘要:

作者图片
在对增强图像训练数据集进行训练之后,我们获得了以下结果。以下是 75 个训练周期后的训练和验证准确性趋势:

作者图片
我们可以看到,随着训练精度的提高和验证分数的停滞,我们的模型开始稍微过度拟合数据。
现在是时候在剩下的 10,000 张图片上测试我们的模型了。在通过最终模型运行测试数据集之后,我们获得了以下测试精度:0.8359 。这是一个非常好的结果!这意味着我们进化的 CNN 架构在几乎 84%的时间里正确预测了测试数据的分类!尽管模型很小,只有 278,247 个可训练参数,但这是一个非常有希望的结果。
尽管 84%的测试准确率是可以接受的,但这并不“有声望”,因为大多数训练有素的模型在这个特定的数据集上可以达到接近 95%的测试准确率;然而,我声称这是我在这个数据集上见过的最好的测试分数之一,因为可训练参数的架构很小,因为我见过的大多数模型要么有数百万个可训练参数,要么使用预训练的网络,要么使用复杂的网络。
为了在进一步的实验中获得更好的结果,我们可以增加 CNN 和 Deep Network 允许的模块数量,希望这将增加模型的能力。此外,我们还可以改变卷积层的通道数量,以便算法支持更多的通道。但是现在,我希望这个简短的例子已经向您展示了利用进化算法,特别是差分进化,通过模块化来调整卷积神经网络架构的能力。
代号
这个项目的原始代码将会在我的 GitHub 资源库中提供给这个特定的课程:
https://github.com/OUStudent/EvolutionaryComputationCourse/blob/main/Unit7/auto_ml_cnn_arch.py
如果你对这个项目感兴趣,并且想把这个算法应用到你自己的数据集中,我建议不要扩充我上面给出的代码。我目前正在开发一个基于进化计算的 Python 库,其中将包含一个基于这里描述的算法构建的专门算法,用于进化深度神经网络的架构。这个库应该会在接下来的两周内发布,当它发布的时候我会发布一个帖子,所以请耐心的关注和等待。
结论
差异进化与标准遗传算法的不同之处在于,它通过使用目标和单位向量来利用群体中的方向信息。这些能力允许差异进化以较差的探索为代价更快地收敛到解。只有当手头的优化问题只有一个或几个局部最小值时,才应该应用差分进化。
在这篇文章中,我们通过在 CIFAR-10 数据集上整合模块化,应用差分进化来进化 CNN 的架构。简单算法在进化过程的早期发现了一个特定的体系结构,并在收敛之前在那个总的方向上执行了一个贪婪的搜索。结果显示,最终模型的测试准确率为 84%,而只有 278,000 个可训练参数,对于这样一个小模型来说,这是一个非常好的分数。到目前为止,一个高级 Python 库正在开发中,它将很快在这里发布,包含进化计算领域的许多高级算法,特别是这里描述的算法的改进。
敬请关注下一篇文章,我们将在第 8 单元“共同进化”中结束课程,在那里我们将共同进化游戏人工智能代理来玩月球登陆车!
单元 8)协同进化——游戏人工智能设计的强化学习
进化计算课程
使用 Python Gym 环境和进化计算库,共同进化用于玩月球着陆器的竞争游戏 AI!
大家好,欢迎回到进化计算的完整课程!在这篇文章中,我们将开始并完成第八单元,共同进化。不幸的是,这将是本课程的最后一个单元,但希望你在这个过程中学到了很多!在前一单元中,我们应用差分进化来进化 Keras 中的卷积神经网络的模型架构,您可以在此处找到该文章:
如果您是本系列的新手,请查看下面的两篇文章,我在其中介绍了理解进化计算所必需的背景信息:
在这篇文章中,我们将简要概述共同进化、竞争适应度、不同类型的共同进化,然后以进化合作/竞争游戏 AI 来玩月球着陆器来结束。
目录
- 协同进化和标准遗传算法的区别
- 竞技健身
- 竞争与合作的协同进化伪算法
- 用于强化学习的 Python 健身房环境
- 月球着陆器的竞争/合作协同进化
- 代码
- 结论
协同进化和标准遗传算法的区别
协同进化和标准遗传算法的主要区别在于,协同进化不是一种进化算法,而是一种同时进化不同物种和种群的方法论。共同进化背后的思想是为了解决同一个问题而进化两个或更多独特的个体物种。例如,假设我们回到单元 3 和单元 4,在那里我们试图预测时间序列问题。单元 3 中采用的方法是进化固定神经网络的权重,而单元 4 中采用的方法是进化决策树。当我们将这两种独特的算法结合起来,创建两个不同的种群、算法种类,并让它们针对同一问题同时进化时,就会出现协同进化。通过这种方式,我们可以创造一个竞争的环境,在这个环境中,每个物种的进化都是基于它们比其他物种好多少,而不一定是基于它们在自己物种中的适应程度。
有两种主要类型的共同进化:
- 捕食者与猎物(竞争)
- 共生(合作)
竞争协同进化通常用于从可能的种群中确定最佳物种,或者为游戏人工智能设计进化不同的策略。另一方面,合作共同进化被执行,其中目标是所有物种一起合作来解决特定问题。
竞技体能
如前所述,在共同进化中,个体的适应度是通过它比其他物种/种群中的个体好多少来计算的,而不是在它自己的物种中。存在不同的方式来计算先前描述的两种类型的协同进化方法的适合度。
对于捕食者与猎物(竞争),我们想要比较物种之间的适应度得分。我们可以通过竞争适应度抽样进行比较,我们随机抽取另一个群体的适应度值与当前值进行比较。有四种主要的采样技术:
- 所有采样 —将每个物种的每个个体与其他物种的所有个体进行比较。
- 随机抽样 —将每个物种的每个个体与每个物种整个种群的随机样本进行比较。
- 锦标赛抽样 —将每个物种的每个个体与每个物种的小型锦标赛进行比较。
- 最佳取样 —将每个物种的每个个体与每个物种的最佳个体进行比较。
对于共生(合作),因为我们希望的一般种群共同进化,所以我们会执行相对适应度,而不是竞争适应度。相对适应度关注的是个体相对于他们周围的人,包括他们自己的物种,表现得有多好。有三种主要的采样技术:
- 所有取样 —将每个个体与所有个体进行比较
- 随机抽样 —将每个人与随机样本进行比较
- 锦标赛抽样 —每个人都与一个小型锦标赛进行比较
正如我们所看到的,竞争和合作共同进化的采样技术极其相似;竞争的主要区别在于每个物种相对于其他物种的表现如何,而共同进化则在于整个物种群体的表现如何。
竞争与合作协同进化伪算法
既然我们已经讨论了共同进化的主要区别,让我们来看两个用于设计游戏 AI 代理的伪代码算法。第一次竞争协同进化:

作者图片
上面我们有一个两个种群物种之间的竞争性共同进化的例子,其中它们的适应度是基于来自另一个物种的适应度值的一些样本。
对于合作式共同进化:

作者图片
上面我们有一个所有种群物种之间合作共同进化的例子。我们简单地计算原始适合度,然后通过上面讨论的某种采样技术计算相对适合度。在合作协同进化中,允许相对适应值差的物种灭绝是很常见的。
用于强化学习的 Python 健身房环境
开发游戏 AI 代理是一个包含许多不同学科领域的广阔领域。在这个领域中,有太多不同的方法来创造游戏人工智能代理;然而,培训这些代理的主要领域被称为强化学习:代理在不同情况下应该如何行动。

https://commons . wikimedia . org/wiki/File:Reinforcement _ learning _ diagram . SVG
在强化学习场景中,我们奖励我们的代理在环境中采取的行动。如果代理采取了不好的行动,我们惩罚它,否则我们奖励它的好行动。通过这种方式,代理人学会在环境中导航,以最大化回报。在这类问题中创建人工智能代理的最常见方式是通过神经网络。我不打算在这里详述神经网络,所以如果你不熟悉,我建议你仔细阅读。神经网络的强化算法有很多,比如演员评判法、 Q 学习、 DDPG 等等。然而,在这篇文章中,我们的目标不是使用上述方法,而是使用遗传算法。
一些人在尝试测试他们的强化学习算法时可能遇到的主要问题是如何创建环境本身。嗯,幸运的是,在 Python 中存在一个名为 Gym 的库,这是一个用于开发和比较强化学习算法的工具包,包含大量旧的 Atari 游戏、复杂的物理问题和其他简单的小游戏。您可以在下面找到他们的网站:
我们将使用体育馆的环境来测试我们的遗传算法。
月球着陆器的竞争/合作协同进化
我们的应用问题是处理月球 Landar-v2 环境。这种环境是健身房特有的。这个问题的目标是让月球着陆器在着陆台上着陆。输入是八个不同的数值,代表着陆器的距离和方向,而输出是四个离散的选项,向左、向右、向上移动,或者什么都不做。当获得 200 的适应值时,认为问题已经解决。
为此我们将使用进化计算库中的神经增强器类,附:我写的。这将是库的一小段,目前还没有完全完成,但是我已经等不及要展示这个例子了。您可以在我的 GitHub 资源库页面上找到更多关于该库及其更新的信息:
https://github.com/OUStudent/EvolutionaryComputation
你也可以在 PyPi 上找到它
https://pypi.org/project/EvolutionaryComputation/
神经强化器类是专门解决输入为数字的强化型学习问题的类,而神经强化器类解决输入为图像的强化型学习问题。神经强化器类通过先进的自适应对数正态遗传算法来进化前馈神经网络的权重和激活函数。神经强化器展示了将竞争和合作机制结合成一种进化机制。它展示了合作的方面,因为目标是让所有物种进化出解决问题的最佳模式;此外,它展示了竞争的品质,因为进化中的每个物种都必须为生存而战,否则就会灭绝……神经强化剂中的物种由其激活功能来指定,这些激活功能可以是所有层的静态激活功能,也可以是混合激活功能。
要建立这个问题,我们首先需要创建我们的适应度函数,它将接受群体并返回每个个体的“适应度”。“健康”只是玩一个游戏后的奖励:
现在我们需要创建神经网络架构。对于这个例子,我们将选择一个具有三个隐藏层和 50、100、50 个节点的网络架构。对于激活功能选择,我们将考虑 relu、leaky relu、卢瑟、elu、高斯、sigmoid、和 tanh 。为了允许进化过程中的一些变化,如果一个个体的当前物种多于一个,它将有 5%的机会转换物种。最后,对于物种形成,我们将把个体各层的激活函数视为静态的。
原始群体
这里我们有来自人群的初始因子。注意他们在登陆月球着陆器时做得很差。然而,他们将很快学会如何在环境中的良好行为得到奖励。






绘图结果-完成层激活
达到最大代数后,就该可视化结果了:
首先,我们有了最佳和平均奖励分数的总体图:


正如我们在上面看到的,最佳奖励在第 0 代和第 100 代之间波动很大,这表明高奖励仅仅是随机获得的。然而,请注意,在第 225 代,平均奖励急剧增加,然后在第 260 代左右停止。给定一代人的最佳回报可以简单地从随机机会中获得,因此建议查看平均回报,因为它展示了人口的行为。在第 240 代,平均奖励增加到 200 多一点,表明该算法成功地进化出一群模型来解决月球着陆器-v2 问题。
下面我们可以看看进化后的物种大小。图例展示了物种,其中激活函数表示特定层的激活。例如,物种“elu,elu,elu”展示了三个隐藏层都具有“elu”作为激活功能。

作者图片
正如我们在上面看到的, leaky relu (绿色)开始规模很大,但很快在 60 代左右消亡,而 elu (蓝色) tanh (粉色)卢瑟(紫色)开始掌权。然而,除了卢瑟之外,其他的都很快开始减少。最上面的两个激活功能可能是卢瑟和坦恩。
现在,因为我们已经确定了顶部的两个激活函数,我们可以重做的演变,除了这一次允许激活函数混合每层之间的顶部两个,卢瑟和坦。这样,我们就能进化出最好的激活功能架构。
绘图结果-混合层激活


正如我们从上面看到的,改进我们的层激活搜索实际上改善了群体收敛。当获得 200 的奖励时,问题被认为解决了,这是在种群中通过第 170 代实现的,比保持层激活静态快 55 代。然而,收敛末期的最佳模型与之前的进化有着相似的最终回报。如果问题更复杂,那么第二次改进的架构搜索也可能获得更好的最佳模型。
下面我们可以看看进化后的物种大小。图例展示了物种,其中激活函数表示特定层的激活。例如,物种“卢瑟,卢瑟,坦”展示了前两个隐藏层都有“卢瑟”作为激活函数,而第三层有“坦”。

正如我们所见,物种大小在进化过程中变化很大。卢瑟,谭,卢瑟’(绿色)是在整个进化过程中体型最大的物种,在大约 190 代时,50 个种群中有 45 个个体;然而,该物种在 10 年左右因趋同而慢慢消亡。在 50 个种群中,大约有 15 个个体的最大最终物种是“谭,谭,卢瑟”。
最终生成后的最佳模型
进化之后,最好的模型被保存下来,下面是连续六场比赛的视觉效果:






结论
共同进化可以是竞争的,合作的,或者两者兼有。协同进化的目标是协同进化不同的物种或算法来解决给定的问题。它最常用于钢筋类型的情况。
在这篇文章中,我们讨论了竞争和合作环境中的两个基本伪算法,而我们的例子结合了这两个算法来解决 Gym 的月球着陆器环境。我们为此使用的 API 是来自进化计算库的神经强化器类。
这篇文章将是进化计算课程的最后一部分。我希望你们都学到了很多关于进化算法的知识,并渴望在自己的问题中使用它们!在下一篇文章中,我将介绍进化计算库,它是我用本课程涵盖的所有材料创建的!
深度学习中的单元测试

JESHOOTS.COM在 Unsplash 上拍照
深度学习/机器学习工作流通常不同于人们对正常软件开发过程的预期。但是这并不意味着人们不应该从这些年来发展的软件开发中汲取灵感并结合最佳实践。在这篇文章中,我将讨论单元测试,为什么以及如何在你的代码中加入这些。我们将从简单介绍单元测试开始,然后是深度学习中的单元测试示例,以及如何通过命令行和 VS 代码测试浏览器运行这些测试。
介绍
单元测试是软件开发人员熟悉的一个概念。这是一种非常有用的技术,可以帮助您防止代码中出现明显的错误和 bug。它包括测试源代码的单个单元,例如函数、方法和类,以确定它们满足需求并具有预期的行为。单元测试通常很小,执行起来不需要太多时间。测试具有广泛的输入,通常包括边界和边缘情况。这些输入的输出通常由开发人员手工计算,以测试被测试单元的输出。例如,对于一个加法器函数,我们会有如下的测试用例。(稍后我们会看到一个深度学习的例子。)

来源:作者
您可以测试具有正输入、零输入、负输入、正输入和负输入的情况。如果被测试的函数/方法的输出等于单元测试中为所有输入情况定义的输出,那么你的单元将通过测试,否则它将失败。您将确切地知道哪个测试用例失败了。可以进一步调查以找出问题。这是您的代码中一个令人敬畏的健全检查。尤其是当多个开发人员在一个大型项目中工作时。想象一下,有人基于某些假设和数据大小编写了一段代码,而新的开发人员更改了代码库中不再满足这些假设的内容。那么代码一定会失败。单元测试可以避免这种情况。
下面是单元测试的一些好处。
- 迫使您编写模块化的、可重用的代码,并清晰地定义输入和输出。因此,您的代码将更容易集成。
- 增加更改/维护代码的信心。它有助于识别由代码更改引入的错误。
- 提高了对单元本身的信心,因为如果它通过了单元测试,我们就可以确定逻辑没有明显的错误,并且单元正在按预期执行。
- 调试变得更容易,因为您知道哪个单元失败了,以及失败的特定测试用例。
Python 中的单元测试
每种语言都有自己的工具和软件包来进行单元测试。Python 也有一些可用的单元测试框架。 unittest 包是标准 Python 库的一部分。我将讨论如何通过命令行/bash 以及作为 VS 代码 UI 接口来使用这个框架。它受 JUnit 的启发,与其他语言中的主要单元测试框架有相似的味道。它支持测试自动化,共享测试的设置和关闭代码,将测试聚合到集合中,以及测试独立于报告框架[4]。
这个框架中单元测试的基本构件是测试用例——必须建立和检查正确性的单一场景。在 unittest 中,测试用例由 unittest 表示。测试用例实例。要制作你的测试用例,你必须编写 TestCase 的子类。
TestCase 实例的测试用例应该是自包含的,这样它既可以独立运行,也可以与任意数量的其他测试用例任意组合运行。TestCase 子类的测试方法应该在名称中有 test 前缀,并执行特定的测试代码。
为了执行测试,TestCase 基类有几个 assert 方法,允许您对照被测试单元的输出来检查测试用例的输出。如果测试失败,将会引发一个异常,并给出一条解释消息,unittest 会将测试用例标识为失败。任何其他异常都将被视为错误。
有两种类型的设置方法可用于为测试设置您的类。
- setUp——这将在类中的每个测试方法之前调用。
- setUpClass —这对于整个类只运行一次。这是你应该用于深度学习测试的方法。在这个方法中加载模型,以避免在执行每个测试方法之前重新加载模型。这将节省模型重新加载的时间。
请注意,各种测试的运行顺序是通过根据字符串的内置顺序对测试方法名称进行排序来确定的。
现在让我们看看我为测试一个分段项目的 PyTorch 数据加载器而创建的单元测试。代码如下所示。
被测试的分割数据集应该将相应的图像和掩模对成批加载。将正确的图像映射到正确的掩模是至关重要的。为此,通常,图像和遮罩在其名称中具有相同的编号。如果你通过一些放大来调整你的图片大小,那么你得到的尺寸应该和预期的一样。对于 PyTorch,数据加载器返回的张量的形式应该是 BxCxHxW,其中 B 是批量大小,C 是通道数,H 是高度,W 是宽度。
现在,我将解释代码中发生了什么。我创建了一个 Test_TestSegmentationDataset 类,它继承了 unittest。测试用例基类。如前所述,我创建了一个 setUpClass 方法,这是一个确保初始化只进行一次的类方法。
这里需要注意的一点是,为了测试目的,我已经禁用了 dataloader 中的混排。因为我希望名称中带有 001 的图像和掩码出现在数据加载器创建的第一批的索引 0 处。检查不同批次的不同样本索引将是一个更好的测试,因为您将确保不同批次之间的顺序一致。我将第一批作为类属性存储在 cls.samples 中。
既然初始化已经完成,我们来看一下单独的测试。
在第一个测试中,我检查了数据加载器返回的图像张量维数。由于我没有调整图像的大小,我希望图像的大小为 320x480,这些图像被读取为 RGB,所以应该有 3 个通道。在 setUpClass 方法中,我指定批量大小为 4,所以张量的第一维应该是 4。如果尺寸有问题,该测试将失败。
下一个测试是完全相同的,除了它是为面具张量。在这个特定的数据集中,掩膜只有一个通道。所以我预计通道数是 1。批量大小应为 4。遮罩形状应为 320x480。
最后一个测试检查两件事。首先是通过手动应用数据加载器中指定的变换获得的张量是否会产生与数据加载器相同的结果。第二是图像和掩模对是正确的。要直接应用 torchvision 转换,您需要实例化转换并将您的图像作为输入传递给该实例。如果 transform 需要 PIL 图像或 numpy . array(ToTensor 就是这种情况),任何其他格式都会导致错误。
现在我们已经准备好了单元测试,让我们先看看如何通过命令行运行这个测试。
您可以使用以下命令:
python -m unittest discover -s Tests -p "test_*"
一旦您指定了搜索目录和搜索模式,Unittest 就可以发现您的测试。
-s or --start-directory directory: It specifies the start discovery directory. In our case, since the tests were in a Tests folder we specified that folder as a value for this flag.-p or --pattern pattern: It specifies the match pattern. I specified a custom pattern just to show you that this functionality is available. Since the default pattern is test*.py, it would work for our test script by default.-v or --verbose: If you specify this you’ll get output for every test method in your test class.
非详细和详细输出如下所示。如果您的所有测试方法都通过了,那么最后您会得到一条 OK 消息。

当所有测试都通过时,Unittest 非详细输出(来源:作者)

所有测试都通过时的 Unittest 详细输出(来源:作者)
但是,如果任何一个测试方法失败了,您将会得到一个失败消息,其中指定了失败的测试。你开始知道哪个特定的断言失败了。如前所述,这非常有助于调试和找到破坏代码的原因。在这种情况下,我改变了正在读取的图像,而不是正在比较的张量,这导致了错误。

未通过所有测试时的详细输出(来源:作者)

未通过所有测试时的详细输出(来源:作者)
您可以将这个测试执行行包含在任何用于自动部署的自动化批处理或 bash 文件中。例如,我们在 GitHub Actions 中使用类似的测试,在更新版本被自动推送到我们的包存储库之前,自动验证代码是否工作。
接下来,我将向您展示如何使用 VS 代码测试浏览器通过 UI 运行这些测试。
在 VS 代码中运行 Python 单元测试[3]
默认情况下,在 VS 代码中禁用 Python 测试。要启用测试,请使用命令面板上的 Python: Configure Tests 命令。这个命令提示您选择一个测试框架,包含测试的文件夹,以及用于识别测试文件的模式。最后两个输入与我们通过命令行运行单元测试时使用的输入完全相同。Unittest framework 不需要进一步安装。但是,如果您选择的框架包没有安装在您的环境中,VS 代码会提示您安装它。

在命令 Pallete 中选择 Python: Configure Tests。(图片来源:作者)

选择所需的框架。在这个例子中是 unittest。(图片来源:作者)

选择测试发现目录。这种情况下的测试。(图片来源:作者)

选择测试发现模式。在本例中是 test_*。py(来源:作者)

对我们来说,这个提示不会出现。(图片来源:作者)
一旦正确设置了发现,我们就会在 VS 代码活动栏中看到带有图标的 Test Explorer。测试资源管理器帮助您可视化、导航和运行测试。您还可以看到测试脚本中直接可用的运行测试和调试测试选项。您可以从这个视图中运行所有的或者单独的测试,也可以在不同的类中导航到单独的测试方法。

VS 代码单元测试接口(来源:作者)
如果测试失败,我会出现一个红叉而不是绿勾。如果您想节省时间,您可以选择只运行失败的测试,而不是所有的测试。

VS 代码单元测试失败的例子(来源:作者)
结论
关于深度学习单元测试的文章到此结束。我们简要地了解了什么是单元测试以及它们的好处。接下来,我们使用 unittest 包框架完成了一个用 PyTorch 编写的 dataloader 单元的实际例子。我们学习了如何通过命令行以及从 VS 代码的 Python 测试浏览器来运行这些测试。我希望你开始为你的代码编写单元测试,并从中获益!感谢您阅读文章。该代码可在https://github.com/msminhas93/DeepLabv3FineTuning获得
参考
[1]https://softwaretestingfundamentals.com/unit-testing/
[2]https://www . tutorialspoint . com/unittest _ framework/unittest _ framework _ overview . htm
[3]https://code.visualstudio.com/docs/python/testing
https://docs.python.org/3/library/unittest.html
[5][https://stack overflow . com/questions/23667610/what-is-the-difference-of-setup-and-setup class-in-python-unittest/23670844](https://stackoverflow.com/questions/23667610/what-is-the-difference-between-setup-and-setupclass-in-python-unittest/23670844#:~:text=The main difference (as noted,before each and every test.&text=A class method called before tests in an individual class are run.)
Python 中的单变量异常检测
从数据集中检测异常值的五种方法

威尔·梅尔斯在 Unsplash 上拍照
异常值检测通常是任何探索性数据分析的重要部分。这是因为在现实世界中,数据通常是杂乱的,许多不同的事情都会影响底层数据。因此,能够从底层数据中识别不同的方法是非常重要的。
那么,首先要问的是“什么是离群值?”离群值可以被归类为不符合模式、数据结构或不在我们对现有数据的预期的正常范围内的一个或几个数据点。这是一个非常主观的定义,因为异常值在很大程度上取决于您检查数据点是否为异常值的环境。
这种异常值可能是许多不同问题的结果:
- 人为误差
- 仪器误差
- 试验误差
- 有意创造
- 数据处理错误
- 抽样误差
- 自然离群值
当然,能够识别该异常值的目的也可以不同。这可能是因为异常值表明产生数据的操作发生了变化,这些数据在以下情况下很有用:
- 欺诈检测
- 入侵检测
- 故障诊断
- 时间序列监控
- 健康监控
异常值表示流程中可能出现了问题,或者生成数据的流程的性质发生了变化。因此,这将需要根据基本的公认正常数据来确定异常值。
在其他情况下,能够从现有数据中移除异常值以确保模型工作是有用的。例如:
- 推荐引擎
- 时间数列预测法
- 科学实验
- 模型结构
其中现有数据中的异常值会影响模型实现,例如线性回归或分类任务。因此,在我们还不确定什么是正常行为的情况下,从现有数据中识别异常值是很重要的。
因此,这两个任务可以分开,第一个任务涉及新生成数据中的新颖性检测,而第二个任务涉及基础数据中的异常检测。我们主要关注第二个领域,尽管下面概述的方法也可以应用于第一种情况。
我为此使用的数据集是前七代神奇宝贝的数据集,总共包含 801 个神奇宝贝。具体来说,我将重点分析这个数据集中 HP 属性的分布,以便能够检测任何潜在的异常。当然,通过这些方法检测到的数据集中的任何异常可能实际上不是异常,但是我们可以根据结果做出选择。
分布图
第一种方法是通过分布图检查基础数据集的分布。这可以使用以下代码实现:
#create the displot
sns.displot(pokemon["hp"],
kde = True)#label the axis
plt.xlabel("HP", fontsize = 15)
plt.ylabel("Count", fontsize = 15)
plt.title("Pokemon HP distribution", fontsize = 15)plt.show()

作者图片
由此我们可以看到,该分布呈现正偏态,有几个点位于该分布的最右端。根据视觉检查,我们可以说,血量大于 200 的神奇宝贝可能是该数据集中的异常值。这可以通过以下方式从数据集中提取:
pokemon_above_150_HP = pokemon[pokemon["HP"] > 150]
pokemon_above_150_HP

作者图片
我们可以看到,这导致了 8 个潜在的异常值,但这只是基于纯粹的视觉检查。
Z 值
我们可以看到 HP 属性的分布大致是正态的,尽管有一定程度的倾斜。因此,我们可以通过使用 Z 分数来补充视觉检查。这基于以下公式:

其中,𝜇是数据的平均值,𝜎是标准偏差。这样做的目的是查看数据点距离数据集平均值有多少标准差。数据点越远,Z 值越高,我们就越有可能认为该数据点是异常值。这可以在 Python 中实现为:
#calculate the Z score
pokemon["HP_z"] = (pokemon["hp"] - pokemon["hp"].mean())/pokemon["hp"].std()
在此基础上,我们可以使用 Z 分数来检查大于平均值 2、2.5、3 或 3.5 倍标准差的数据。选择哪个 Z 值作为临界值将取决于基础数据,但这里选择临界值 3 将提取以下神奇宝贝:

作者图片
这里的主要区别是,这包括那些与 150 或以上的 HP,而不仅仅是大于 150。因此,这表明,就该数据集而言,最初的目测检查和截断点的选择是相对准确的。
箱线图
另一个好方法是箱线图。这又是另一种可视化方法,可用于识别数据集中的任何异常值。这样做的依据是,任何小于下四分位数减去四分位数间距的 1.5 倍或者大于上四分位数加上四分位数间距的 1.5 倍的值都可以被视为异常值。在图上,这显示为超出箱线图本身的最小值和最大值的单独点。这可以使用 Seaborn 库来实现,如下所示:
#create the boxplot
ax = sns.boxplot(x = pokemon["HP"])#add labels to the plot
ax.set_xlabel("HP", fontsize = 15)
ax.set_ylabel("Variable", fontsize = 15)
ax.set_title("pokemon HP boxplot", fontsize =20, pad = 20)plt.show()

作者图片
与分布图相比,这个箱形图更清楚地表明了哪些数据点可以归类为异常值,我们可以看到,这表明不仅在分布的高端有异常值,而且在分布的低端也有异常值。
Tukey fences
可以使用四分位距的值提取这些点,如下所示:
#extract the upper and lower quantiles
pokemon_HP_lq = pokemon["HP"].quantile(0.25)
pokemon_HP_uq = pokemon["HP"].quantile(0.75)
#extract the inter quartile range
pokemon_HP_iqr = pokemon_HP_uq - pokemon_HP_lq#get the upper and lower bounds
lower_bound = pokemon_HP_lq - 1.5*pokemon_HP_iqr
upper_bound = pokemon_HP_uq + 1.5*pokemon_HP_iqr#extract values outside these bounds
Pokemon_IQR_outliers = pokemon[(pokemon.HP <= lower_bound) | (pokemon.HP >= upper_bound)]
Pokemon_IQR_outliers

作者图片
这表明比以前的检查有更多的异常值。事实上,这表明一个神奇宝贝的血量低于下限,而 23 个神奇宝贝的血量高于上限。
考虑到大量的潜在异常值,这种方法的替代方法是使用替代值乘以四分位间距。为了从中获得极端的异常值,而不是只乘以 1.5,我们可以乘以 3。这样做,我们得到:

作者图片
这表明在分布的上端只有六个异常值。您选择哪一个将取决于您想要识别的异常值的类型。
集群
最后一种可能使用的方法是聚类。聚类的目的是能够从数据集中识别组,其中潜在的离群值可能适合极端组或它们自己的组,这取决于它们的特征与基础数据集的不同程度。对于单变量分析来说,这可能会限制其明确识别不同组的能力,但是可视化结果可能会比仅仅检查下面的分布更清楚地识别异常值。因此,使用 K 均值聚类,这可以实现为:
#convert the HP values to a float to be able to use numpy
HP_raw = pokemon["hp"].values.astype("float")
#use the kmeans function from scipy
centroids, avg_distance = kmeans(HP_raw, 4)
#extract the groups from the data
groups, cdit = vq(HP_raw, centroids)#plot the results
#assign groups back to the dataframe
pokemon["HP_groups"] = groups#plot the scatter plot
fig = px.scatter(pokemon, x = "hp", y = pokemon.index,
color = "HP_groups",
hover_name = "name")
#add a title
fig.update_layout(title = "K-Means outlier detection",
title_x = 0.5)
#show the plot
fig.show()

作者图片
从中我们更有可能识别出分布的低端和高端的异常值。
这些方法表明不同的潜在异常值都来自同一数据集的事实表明,异常值检测不是一门精确的科学,必须做出判断。其中的一个重要部分是决定需要对这些异常值做什么,以及它们从数据集中移除的意义,输入值以覆盖这些异常值,或承认它们的存在但继续按原样进行分析。
这当然将取决于识别异常值的目的、基础数据集和未来将执行的分析。虽然试图从整个数据集的单个变量中找出异常值的实际应用可能会受到限制,但这些方法可用于补充多元异常值检测算法,如隔离森林、局部异常值因子或椭圆包络线,其中可分析异常值得分以确定污染值。
可用代码:【https://github.com/PhilipDW183/Outlier_detection
可用数据集:https://www.kaggle.com/rounakbanik/pokemon?select=pokemon.csv
https://philip-wilkinson.medium.com/membership [## scikit-learn 决策树分类器简介
towardsdatascience.com](/introduction-to-decision-tree-classifiers-from-scikit-learn-32cd5d23f4d)
单变量与多变量预测
生命科学的数理统计和机器学习
DESeq2 与 LASSO 对基因表达的预测能力

作者图片
这是我的专栏 生命科学的数理统计和机器学习 中的第二十一篇文章,我试图以简单的方式解释生物信息学和计算生物学中使用的一些神秘的分析技术。在我之前的文章为组学集成选择特征中,我给出了多变量特征选择的例子,并提到了它相对于单变量特征选择的优势,但没有实际演示。在本帖中,我们将比较多变量模型如 【套索】PLS随机森林 与单变量模型的预测能力,如著名的差异基因表达工具 DESeq2 以及传统的 曼-惠特尼 U 检验 和 斯皮尔曼使用骨骼肌 RNAseq 基因表达数据集,我们将展示使用多变量模型构建的预测得分,以优于来自单价特征选择模型的预测得分。
骨骼肌 RNAseq 基因表达数据
这里,我们将量化几种特征选择方法的预测能力:a)单变量(逐个)特征选择,b) 多变量(全部)特征选择。出于演示目的,我们将使用来自 GTEX 人类组织基因表达联盟 的骨骼肌 RNAseq 基因表达数据集(为简单起见,随机取样 1000 个基因)。

版本 6 GTEX 骨骼肌数据集包括 157 个样本。在这里,我们加载基因表达矩阵 X,并删除低表达基因。为了与后选择组学整合特征保持一致,我们将使用性别作为我们将要预测的响应变量 Y。让我们快速观察一下降维后的样本,如 PCA。

作者图片
正如我们所看到的,PCA 图并没有显示基于骨骼肌基因表达数据的雄性和雌性之间的明显区别。我们可以假设仅来自性染色体(X 和 Y) 的基因可以在雄性和雌性之间提供完美的分离,但是我们在这里没有观察到,因为:1)大多数基因来自常染色体,2)随机采样了 1000 个基因,所以我们可能没有从 X 和 Y 染色体上采样很多基因。在下一部分中,我们将把数据集分成训练和测试子集,然后在训练集上实现单变量和多变量特征选择(训练)模型,并且使用平衡假阳性(FPR)和真阳性(TPR)率的 ROC 曲线 技术在测试集上评估模型。
性别预测:套索法与单变量法
为了评估单变量和多变量模型的预测能力,我们需要对独立的数据集进行训练和评估。为此,让我们将数据集分成训练(80%的样本)和测试(20%的样本)子集。为了确保我们的评估不是基于特定的“幸运”数据分割,我们将多次随机分割成训练和测试子集,计算每次 ROC 曲线,并对多个 ROC 曲线进行平均。这样,我们将一举两得:为每个模型的 ROC 曲线建立置信区间,并使 ROC 曲线平滑且美观,否则它们会显得“尖锐”,除非你在测试子集中有大量样本,而我们这里肯定没有。
在之前的文章中,我展示了如何使用 LASSO 进行多变量特征选择,以及使用 Spearman 相关性进行单变量特征选择。为了进行比较,我将再添加一种单变量特征选择方法,即 Mann-Whitney U 检验,它应该在很大程度上与 Spearman 相关性相当,因为两者都是非参数和基于等级的单变量方法,不假设数据的特定分布。为了将通过单变量方法单独选择的基因组合成预测分数,我们将通过其表达和性别之间的个体关联的 p 值来使用它们的排序,用 Bonferroni 程序(或不太保守的 Benjamini & Hochberg FDR 程序)校正多重测试。这通常导致在多次测试调整后,在训练数据集上出现一些差异表达的基因。此外,显著差异表达的(雄性和雌性之间)基因可以通过计算它们对训练数据集的影响/权重(雄性和雌性之间基因表达的 Spearman rho 和倍变化的对数)和它们对来自测试数据集的样品的基因表达值的乘积而被压缩成预测分数。让我们对其进行编码,并比较模型之间的 ROC 曲线。

作者图片
这里我们可以得出结论,LASSO 比两种单变量特征选择方法具有更大的预测能力。为了更好地了解这三种方法的 ROC 曲线下面积(AUC ROC)的差异,并能够进行统计测试来说明 ROC 曲线之间的差异有多显著,让我们根据 LASSO、单变量 Spearman 相关和 Mann-Whitney U 检验制作一个 AUC ROC 的箱线图。

作者图片
我们可以看到,Spearman 相关性和 Mann-Whitney U test 单变量特征选择模型具有可比的 AUC ROC 指标(尽管 Mann-Whitney U test 更好),并且与 multivarite LASSO 相比,两者都具有明显更低的AUC ROC,即预测能力。
然而,这种比较可能会有偏差。在谈到单变量模型(Spearman 和 Mann-Whitney U 检验)时,我们提到在Benjamini&hoch Berg FDR校正多重检验后,只有少数基因是显著的。因此,我们在构建单变量模型的预测分数时只使用了几个基因,而 LASSO 选择了更多的基因,大约 30 个,请参见我的 github 上的完整代码。如果 LASSO 更好的预测能力仅仅是因为更多的特征被用于其预测得分,会怎么样?为了检验这一假设,在下一节中,我们将暂时忽略 Benjamini & Hochberg FDR 校正,并单独使用 p 值排序,通过 Spearman 相关和 Mann-Whitney U 检验确定约 30 个最具预测性的基因。换句话说,我们将使用与用于构建预测分数的多变量套索相同数量的基因。通过所选基因的数量模拟 LASSO 的相应单变量模型将被称为 Spearman(与约 30 个差异表达基因的 Spearman 相关性)和 MWU30(与约 30 个差异表达基因的 Mann-Whitney U 检验)。
性别预测:DESeq2 与多元方法
在本节中,除了将 LASSO 与 Spearman(约 30 个差异表达基因的 Spearman 相关性)和 MWU30(约 30 个差异表达基因的 Mann-Whitney U 检验)模型进行比较,我们还将添加一些其他流行的单变量和多变量模型。
首先,当一个人做差异基因表达分析时, DESeq2 软件是使用的金标准。这个工具名声很好,在 RNAseq 社区中非常受欢迎。这是一个单变量工具,也就是说,它假设基因表达计数的负二项分布,执行逐个基因的测试。此外,它应用了方差稳定程序,其中高表达基因帮助低表达基因被正确测试。将 DESeq2 的预测能力与 Mann-Whitney U 检验和 Spearman 相关性进行比较是很有趣的,它们基本上利用了相同的单变量概念,但都进行非参数类型的检验,与 DESeq2 相反,deseq 2 假定基因表达的负生物学分布,因此进行参数检验。同时,我们将计算一个 DESEQ2_30 模型,该模型使用与 LASSO 选择的相同数量的基因(按其与性别关联的 p 值排序)来构建预测得分。这类似于 SPEAR30 和 MWU30 型号。
其次,我们将添加两个多变量特征选择模型,以与 LASSO 和单变量模型进行比较。这两个是偏最小二乘判别分析( PLS-DA )和随机森林,它们都是常见的多元模型。其中一个(PLS-DA)和 LASSO 一样是线性的,另一个(随机森林)是非线性的。这里,我们不仅旨在比较单变量特征选择模型和多变量特征选择模型,还想看看非线性随机森林与线性 LASSO 和 PLS-DA 相比是否能提高预测。
与上一节一样,我们将通过将数据集分成多次训练和测试来围绕 ROC 曲线建立置信区间。它涉及到很多编码,我在这里不展示代码,但是欢迎你在我的 github 上查看。下面我只给出了在多次训练测试分割后每个模型的平均 ROC 曲线以及 AUC 的箱线图。

作者图片

作者图片
我们在这里观察到一些有趣的事情。首先,与所有多变量模型相比,所有单变量模型的预测能力似乎更差。即使是最差的多变量模型,这里似乎是随机森林(RF),也比最好的单变量模型(这里似乎是曼-惠特尼 U 检验(MWU))具有显著更高的 AUC ROC。
第二,与 LASSO 选择的基因数量相同的所有单变量模型(DESeq230、SPEAR30 和 MWU30)未能与所有其他单变量或多变量模型竞争,这意味着单变量模型具有较差预测能力的原因不是由于选择的特征/基因数量不同,而是由于对构建预测分数的基因的不同排序和权重。
第三,对于这个特定的 RNAseq 基因表达问题,与线性多元 LASSO 和 PLS-DA 模型相比,非线性多元随机森林似乎没有改善预测。然而,根据我的经验,这是许多生命科学问题的常见情况,在开始非线性分类之前,检查简单的线性模型通常是值得的。
第四,也是最有趣的,DESeq2 单变量参数预测得分似乎不仅比多变量模型(LASSO、PLS-DA、Random Forest)表现更差,而且比单变量非参数模型(如 Spearman correlation 和 Mann-Whitney U 检验)表现更差。考虑到提到的非参数测试是如此简单,以及 DESeq2 的杰出声誉,这是非常出乎意料的。然而,事实证明,至少对于这个特定的数据集,简单的 Spearman 和 Mann-Whitney 非参数检验在预测能力方面优于 DESeq2 。
加分:套索 vs 山脊 vs 弹力网
另外,在本节中,我们将比较 LASSO (L1 规范)、Ridge (L2 规范)和 Elastic Net(L1 和 L2 规范的组合)预测得分的预测能力。惩罚线性模型族的这三个成员之间的差别通常并不明显。不涉及细节(有许多文献解释了这种差异),我们只强调 LASSO 是最保守的方法,因此由于生命科学数据的高噪声水平,在生命科学中通常是首选方法。弹性网在脊的凸优化优势和套索的严格性之间提供了很好的平衡,在癌症研究中很受欢迎。下面,我们再次展示了在 RNAseq 基因表达数据集的 100 次训练测试分割后,套索、脊和弹性网的平均 ROC 曲线,以及 AUC ROC 度量的箱线图。

作者图片

作者图片
我们可以看到,LASSO 和 Elastic Net 给出了几乎相同的 ROC 曲线,并且优于已知最宽松的 Ridge 模型,这显然不利于模型泛化,因此也不利于预测目的,因此 Ridge 可能不是处理有噪声的生命科学数据时的首选。
摘要
在本文中,我们了解到,与单变量模型相比,多变量统计模型似乎具有更强的预测能力。至少对于本文研究的 GTEX 骨骼肌 RNAseq 基因表达数据,单变量差异基因表达工具 DESeq2 不仅与 LASSO、PLS-DA 和 Random Forest 等多变量模型相比,而且与 Spearman correlation 和 Mann-Whitney U test 等简单的非参数单变量模型相比,都表现出了较差的预测。
像往常一样,请在下面的评论中告诉我生命科学和计算生物学中的哪些话题对你来说特别神秘,我将在本专栏中尝试回答这些问题。在我的 github 上查看完整的笔记本。在 Medium 关注我,在 Twitter @NikolayOskolkov 关注我,在 Linkedin 关注我。下次我们将讨论如何在 UMAP 空间聚集,敬请关注。
普遍的对抗性扰动可能会对自动驾驶汽车构成威胁
人工智能校准和安全
论文综述:自主车辆物体类别检测对普遍敌对干扰的弹性
在这篇文章中,我将讨论我作为合著者撰写我的第一篇同行评审出版物的旅程:自主车辆物体类别检测对普遍敌对扰动的弹性 , 最近被授予2021IEEE iem tronics(国际 IOT,电子和机电一体化会议)最佳口头陈述。
自动驾驶汽车这些天越来越受关注。为了让自动驾驶车辆识别人、交通灯和其他车辆以做出关键任务决策,物体检测是自动驾驶系统中的关键任务之一。然而,众所周知,基于深度学习的算法容易受到被称为对抗性干扰的精心制作的噪音的影响。
因此,我们必须评估我们在自动驾驶汽车中使用的物体检测算法是否对这些扰动具有鲁棒性,以确保它可以安全地部署在现实世界中。如果不这样做,可能会发生严重的后果,例如,检测到街道上的停车标志,因为不同的物体会使汽车忽略该标志,并撞上另一辆汽车或人。
当我意识到这个问题的动机时,我搜索了关于对抗目标检测攻击的著作,一篇关于“对抗目标检测的普遍对抗扰动”**【1】的论文引起了我的注意。这是第一个从经验上证明物体探测任务中普遍对抗性干扰存在的工作。我想到,对手也可以利用这种针对自动驾驶汽车的对抗性攻击。
为什么目前对对抗性鲁棒性的研究相当普遍,而不涉及自动驾驶汽车等任务关键型应用?难道我们不应该在应用程序级别研究对抗性攻击和防御吗?
这个问题让我和我的顾问专注于一个流行的数据集 COCO2017 中与自动驾驶汽车相关的最重要的类别。在研究领域,理解敌对扰动对与自动驾驶汽车相关的特定类别的影响似乎探索得较少。虽然[1]已经探索了针对对象检测的对抗性攻击,但不是在自动驾驶汽车的背景下。
这种差异导致我制定了三个研究问题 :
- 与[1]中使用的 PASCAL VOC 相比,这种普遍扰动的存在会扩展到更大的真实世界背景数据集 COCO 吗?
- 我们是否完全将普遍扰动的影响从其他因素中分离出来,例如包括没有扰动就已经被错误分类的图像?
- 通用扰动对 COCO 子集(仅包括与自动驾驶相关的类别)的影响有多大?
介绍
本文介绍了与自主车辆相关的五个类别中普遍扰动对物体检测的影响:人、停车标志、汽车、卡车和交通灯。我们从经验上证明,对包含这些类别的图像训练集计算普遍扰动并将那些发现的扰动添加到那些图像中会使对象检测器在大多数图像上检测不到那些类别。
论文的主要贡献包括:
- 在目标探测中建立类别级的脆弱性等级以对抗普遍的敌对扰动
- 通过管理数据集以仅包括其中检测器正确预测了至少一个指定类别的那些图像,完全隔离对抗扰动对检测结果的影响
数据集管理
[1]使用 PASCAL VOC 作为他们实验的主要数据集,这是一个规模相对较小的对象检测数据集,更重要的是,它没有捕捉到真实视觉世界的复杂性。我们在这个实验中使用了 COCO2017 训练集,因为它是用于具有更真实上下文环境的对象检测的事实上的标准大规模数据集。
此外,我们声称,我们需要对目前用于对抗性攻击实验的数据集进行更精细的管理。策展分两个方向进行: **(1)班级(2)影像。
班级
从自动驾驶的背景来看,我们不需要数据集中的所有 80 个类。这可能会妨碍评估普遍扰动的影响,特别是在自动驾驶汽车最常遇到的街道环境中。因此,我们只使用我们的数据集的一个子集,只策展我们感兴趣的图像类别。
为了对自动驾驶汽车最相关的类进行实验,我们选择了 5 个类(人、汽车、卡车、交通灯、停车标志)。我们可以使用 COCO API 轻松搜索我们感兴趣的特定类别的图像。
形象
为了完全隔离敌对扰动对检测性能的影响,我们过滤了特定于类的数据集,以仅包括其中检测器正确预测了至少一个目标类的图像。我们这次实验的主要对象检测模型是来自 Detectron2 库的更快 RCNN /特征金字塔网络(FPN) / Resnet50 基网络(在 COCO2017 训练集上预训练)。
为了滤除图像,我们使用了更快的 RCNN 对阈值为 0.5 IoU 的图像进行检测。只要对象检测器能够检测到我们的对象类别的至少一个实例,我们就会将该图像保留在我们的数据集中。如果检测器错过了图像中的所有对象实例,则该图像将不再被使用,因为该图像不需要被攻击,因为它已经发生故障。
由于时间和资源有限,我们精选了特定于类别的数据集,包括 500 幅人物、汽车和卡车图像、344 个交通灯和 313 个停车标志。特定于类的数据集意味着所有图像至少有一个该目标类的实例,因此这些数据集之间可能有重叠,因为许多图像可能有多个我们选择的类。现在,我们准备计算每个特定于类的数据集的通用扰动。
针对目标检测的通用对抗性扰动
通用对抗性扰动 [2]的想法很有趣,因为与需要为每个图像创建的图像特定扰动相反,图像不可知扰动在某种意义上更危险,因为一旦你发现了一个可以很好地推广到不同数据集、图像和模型的扰动,它就可以广泛地用于攻击各种对象检测系统。
我们建立在由[1]提出的方法上,因为他们第一次证明了在物体探测任务中普遍扰动的存在。[1]引入了 通用密集对象抑制(U-DOS) 算法来计算通用扰动,该算法将使对象检测器看不见,以在图像集 I 的大多数图像中找到对象,同时保持人眼不可察觉。如果你看完这个帖子还不清楚这个算法,请阅读[1]。

图一。U-DOS 算法[1]。
给定对象检测器 D、图像集 I、扰动的最大 inf_norm 约束和历元数,UDOS 计算并返回通用扰动 v 。
对于每个时期,它在图像集 I 中的每个图像上迭代,以首先检查将当前通用扰动添加到该特定图像是否导致遮蔽检测器。如果是,跳到下一张图片。否则,通过优化目标函数(2)来计算 v_i(与我们修改的原始算法 1 略有不同)。v_i 是攻击第 I 个图像并将其添加到我们的通用扰动 v 的扰动。在更新之后,它总是验证更新的扰动的 inf 范数被约束在 Xi 内,该超参数是我们设置的以确保人类的准感知性。

图二。U-DOS 算法的原始目标函数[1]。
我们对[1]提出的算法 1 中的目标函数(第 6 行)进行了如下修改:
1.目标函数中的第一项:取决于对象检测器及其实现,从检测器的输出中访问背景类概率可能是棘手的,因此我们移除了背景类项,并且仅关心对象类的输出的总和。修正后的 F(I_i,v,v_i)如下:

图 3。U-DOS 算法目标函数中第一项的修正。[1]
2.等式中的第二项:曾经是||的 L-inf 范数。||,所以在运行一些时期后,它停留在最大约束(Xi),因此比较超过某个时期的扰动幅度是没有意义的。因此,我们使用平均 L1 范数——L1 范数除以像素数来比较扰动范数如何平均增加。
基于这些变化,我们希望最小化第 I 个扰动图像上的扰动范数和目标检测概率:I_i + v + v_i (I_i:原始图像,v:通用扰动,v_i:第 I 个图像的附加扰动)。需要最小化的函数如下:

图 4。U-DOS 算法的改进目标函数。
为了优化(2),我们使用梯度下降来为每个第 I 个图像找到 v_i。α是我们乘以目标函数相对于 v_i 的梯度的步长,v _ I 是我们为了更新普适扰动而添加的扰动。

图 5。梯度下降以更新增加的扰动。[1]
这种攻击非常简单,因为我们使用其输出相对于输入扰动的梯度,这意味着我们可以攻击图像中任意数量的类。
实验和结果
超参数
对于这个实验,我们需要调整四个主要的超参数:
- n_epochs:时期的数量
- 阿尔法:学习率
- xi:扰动的最大范数约束
- score_threshold:对象检测器将其视为最终预测
在一系列试点实验之后,我们确定了以下参数值:n_epoch=250,Xi=10,α= 20,score_threshold=0.7。
韵律学
在我们的实验中,我们评估了通用扰动对数据集的影响,因为我们通过迭代使用两个指标找到了更强的扰动:实例级和图像级盲度。我们为每个类别运行实验,以在类别级别比较易受干扰的程度。
图像级盲度:物体检测器 D 能够以高于阈值的置信度找到至少一个物体的图像与图像总数 N. (4)的比率
这里要提到的重要一点是,在策展过程中,我们需要确保在添加通用扰动之前,数据集的图像级盲度始终为 1.0。这是因为我们应该只包括至少有一个物体的置信度高于阈值的图像。

图 6。图像级盲度度量[1]。
实例级盲度:检测器 D 在每幅图像中找到的置信度超过给定阈值的平均实例数。此度量显示在实例级别有把握地检测到多少目标类的对象。(5)

图 7。实例级盲度度量[1]。
结果
下面的第一张图显示了随着标准的增加,扰动对每个类别的影响。为了绘制这个图,我们跟踪实例级和图像级的盲度,因为我们为五个类别中的每一个改变扰动范数。在进行了一系列的实验后,我们发现所有 5 个类别的图像和实例级盲度随着我们发现具有更强范数的普遍扰动而降低。很明显人在图像级和实例级失明程度中是最有弹性的类别,其次是汽车、交通灯、卡车和停车标志增加了干扰标准。进一步调查和分析为什么某些类别更能抵御这种攻击将是一个有趣的方向。

图 8。图像级盲度与 5 个类别上的扰动范数[3]。

图 9。实例级盲度与 5 个类别上的扰动范数[3]。
基于我们上面的实验结果,我们已经在图像和实例级盲度中建立了一个类别排名,随着时期和规范的增加,对每个类别对普遍扰动的弹性进行排名。随着扰动范数和迭代次数(历元)的增加,我们通过比较图像和实例级盲度的下降率来设置排序。因为这两个指标密切相关,所以这两个指标的排名遵循相似的模式是有意义的。

表 1。对普遍扰动的复原力的分类等级。从最高到最低[3]。
为了形象化,我挑选了一些样本图像,它们清楚地表明,添加在数据集上计算的发现的通用扰动成功地使 Faster-RCNN 在检测我们指定攻击的目标类时失败。前两幅图像显示,在将计算出的攻击停车标志的扰动添加到图像后,原始图像中的 2 个停车标志检测消失。接下来的两幅图像显示,在添加计算出的扰动以攻击交通灯后,原始图像中的 4 个交通灯检测被移除。除此之外,有趣的是,添加扰动也会影响对非目标类别的检测,例如汽车。

图 10。停止对原始图像[3]的标志检测。

图 11。在受干扰的图像[3]上检测停车标志失败。

图 12。对原始图像进行交通灯检测[3]。

图 13。扰动图像上的交通灯检测失败[3]。
结论
从使用不同的精选数据集评估原始工作的有效性开始,我了解到准可感知的普遍扰动会对与自动驾驶相关的数据集安全的核心造成真正的伤害。此外,发现哪一类特别容易受到普遍扰动的影响,将对我们应该如何设计自动驾驶系统产生重要影响,以优先考虑我们应该重点防御的类别,而不仅仅是建立一个防御系统来抵御整体的敌对攻击。
完成这个实验后,我意识到我可以直接在大规模的驾驶特定数据集上进行实验,例如,Waymo 或 Berkeley DeepDrive,以研究自动驾驶汽车领域的对抗性攻击的影响。
我们未来的工作将是对这些数据集进行不同的对抗性攻击实验,以研究更多攻击状态的影响,或开发针对自动驾驶汽车的新型攻击,并提出可以提高自动驾驶系统鲁棒性的防御措施。
如果有人想使用这个,请引用我们的论文并指出引用,我已经分享了我的代码库。如有任何问题,请随时联系 nayeem@umd.edu 或 aspiringtechsavvy@gmail.com。任何建设性的批评或反馈都会受到欢迎。感谢阅读。
参考
[1]李,张俊杰,黄,王(2020).针对目标探测的通用对抗性扰动。模式识别, 110 ,107584。
[2] Moosavi-Dezfooli,S. M .,Fawzi,a .,Fawzi,o .,& Frossard,P. (2017 年)。普遍的对抗性干扰。IEEE 计算机视觉和模式识别会议论文集(第 1765-1773 页)。
[3] Mohammad Nayeem Teli 和 Seungwon Oh。“自主车辆物体类别检测对普遍扰动的弹性”,被认可出现在 IEEE 国际 IOT、电子学和机械电子学(IEMTRONICS '21)中。
普适近似定理:软件 2.0 的代码重构?
思想实验

约书亚·阿拉贡在 Unsplash 上拍摄的照片
什么是 UAT?
普适逼近定理到底是什么?通俗地说,UAT 的意思就是给一个隐层神经网络足够多的神经元。它可以逼近(或近似模拟)给定输入范围内的任何连续函数。这意味着一个隐层神经网络是一个终极的柔性函数逼近器。可能有点太灵活了。
吸取的教训
由于普适逼近定理的灵活性,过去常常推动 AI 研究人员将注意力大多放在浅层神经网络上,从而在某种程度上阻碍了深度学习的发展进程。这个有意思。想想看,一个“浅而宽”的神经网络倾向于“记住”所有特征来逼近目标函数。然而,更深层次的网络往往在特征提取方面更加抽象,并找出可以应用于数据集许多部分的模式。他们显然概括得更好。并且用更少的计算能力获得更好的结果。
更深入

如果你是一名软件开发人员,你觉得这听起来怎么样?‘代码重构’! 开发人员重构他们的代码,将重复的代码片段放到函数中,尽可能地重用它们。干净的代码通常是更好的代码。深度神经网络在某种程度上做了同样的事情。通过拥有更多的层,它使网络能够更好地“重构”自己,并学习更多的通用模式,从而更有效地实现相同的目标。这导致了更好的(在性能和效率方面)模型。
软件 1.0 与软件 2.0
还有哪些软件开发技术我们可以应用到机器学习上?更准确地说,我们从‘软件 1.0’中学到的东西可以应用到‘软件 2.0’上吗?(如果你不熟悉“软件 2.0”的概念,我强烈推荐你观看下面来自 Andrej Karpathy 的视频,它并不完全适用于所有情况,但绝对值得注意,并得到了特斯拉成功的支持!)
根据 Karpathy 的说法,我们目前在软件工程中所做的事情,即有才华的人编写代码来完成任务和解决问题,是“软件 1.0”,人类通过直接告诉计算机如何做每一步来为这个过程做出贡献。在“软件 2.0”的新范式中,机器学习和深度学习被广泛采用,人类通过以数据集的形式提供大量人们做某事的例子来做出贡献,计算机和模型将自动计算出如何做到这一点。事实上,特斯拉的自动驾驶系统有相当一部分是由深度学习模型驱动的。
一些人仍然怀疑“软件 2.0”方法是否会有光明的未来。我们从 1.0 到 2.0 的道路仍有待讨论。对于机器学习研究者和实践者来说,重新应用传统软件工程的智慧可能仍然是一个很好的探索方向。欢乐时光!
如果你想了解更多关于万有逼近定理的知识,可以参考我下面的文章:
从乐高乐园到神经网络:通俗易懂的通用逼近定理|作者李立伟| 2020 年 12 月|走向数据科学
大学学位 vs 暑期学校 vs 自学:数据科学和数据分析

瓦西里·科洛达在 Unsplash 上的照片
据招聘网站称,受大数据和人工智能的推动,对数据分析和数据科学技能的需求正呈指数级增长。随着公司寻找利用大数据力量的方法,急需精通数据分析和数据科学的技术专业人员。然而,高技能申请人的供应增长速度较慢,这使得这类工作非常适合转行者。
在这篇博客文章中,我将假设你不是高中毕业,而是已经拥有大学学位(不是计算机科学或统计学)和/或已经工作了几年,现在正在考虑如何成为一名合格的数据分析师或科学家。当然,在决定如何提高你目前的技能时,有许多因素需要考虑,比如以前的经验、财力以及你想在这方面投入多少时间,我的意图是概述最常见的提高技能方式的利弊:大学学位、速成课程(比如暑期学校)以及各种类型的在线课程。
我目前是伦敦大学学院计算机科学系的助教,过去曾在一所数据科学暑期学校任教,并且因为我的学士学位是法律,所以我做了大量的自学,所以我相信我可以很好地概述以上所有方面的利弊。
数据科学、机器学习或数据分析硕士学位
显然,大学学位(在英国)的主要缺点是费用。如果你正在考虑全日制学习,你不仅需要支付学费,还需要支付一整年的生活费用。第二个问题是,它是基于申请的,你可能不会被你感兴趣的项目录取。要想做数据科学或者机器学习的硕士,需要有一个量化学科的学士学位。所以,除非你学过数学、工程学、经济学或金融学,否则你没有资格申请这样的硕士课程,你必须先修一门转换课程(计算机科学硕士)。对于没有计算机科学背景,但有财力和时间的人来说,我可以强烈建议在完成法学学士学位后,参加一个转换课程,就像我在 2015 年做的那样。但是目前这已经超出了本文的范围。
另一个不利之处是,在学位结束时,你可能不一定有不同项目的投资组合可以展示给潜在的雇主。课程作业通常不能反映真实的商业问题,你的硕士论文可能更偏重于学术研究。
如果你有必要的定量背景和财务资源(或获得学生贷款),那么攻读数据科学、机器学习、数据分析或金融计算等更专业的硕士学位肯定有其好处:作为结构化课程的一部分,你将有机会接触各种模块,会见该领域的领先学者,结识有趣的人,结交新朋友,并(希望在后新冠肺炎时代)体验学生生活。

暑期学校/数据科学/机器分析短期强化课程
优点:对于以前很少或没有数据分析和数据科学经验的人来说,速成课程(2-4 周)提供了学习 Python 或 R、复习统计和假设检验、学习如何处理(数字)数据以及学习机器学习的基本概念的绝佳机会。像这样的课程有助于了解你是否真的想走这条职业道路,并有助于决定下一步该怎么走(例如,大学学位或其他更深入的课程)。
这种课程的费用和时间投入都很低,因此每个人都可以参加。
缺点:此类短期课程只是介绍性的,并不能为您提供成为数据科学家或数据分析师所需的所有知识和工具。许多概念都以黑盒的方式涵盖,没有深入数学。您可能会从 scikit-learn 中学习如何预处理数据并将其输入到逻辑回归分类器中,但不会学习逻辑回归的实际工作原理。

自学
所谓自学,我指的是一种你可以按照自己的进度进行的结构化在线课程,需要几个月才能完成,包括评估,最好还有某种辅导计划。
缺点:很难找到一个物有所值的合适课程。在我为这篇文章所做的研究中,我发现许多课程(并不便宜!)大纲不在线,需要请求,之后你就被垃圾邮件了。
许多被宣传为“数据科学”课程的课程实际上是数据分析课程,因为它们不涉及任何数学,而是数据库(SQL)和数据可视化方法。因此,如果你正在从事数据分析方面的职业,这些课程将涵盖你需要的一切。它们是否涵盖了一个数据科学家应该知道的一切,这是值得怀疑的。我没有找到一门涵盖线性代数和微积分的英语课程。有趣的是,当我搜索一门确实涵盖必要数学的课程时,我能找到的唯一一门课程是用俄语教授的——与关于数据科学和数据分析的课程是同一种语言,这些课程的教学大纲最吸引我,我过去曾向朋友和家人推荐过这些课程(他们都对它们非常满意)。
鉴于你可以按照自己的进度完成这些课程,你需要有必要的时间管理技巧和动力来完成它们,因为你通常不像在大学那样有严格的截止日期或考试
优点:你可以按照自己的进度学习这些课程,这当然也是一个优点。你不必放下工作,在晚上或周末做这些事情。
这些课程中的许多价格非常合理,因此每个人都可以参加(我会远离价格在 5 位数范围内的课程)。
最后,你更有可能有一个基于行业的项目组合,你可以向雇主展示。其中一些项目甚至与行业合作伙伴密切合作,并可能将你与潜在雇主联系起来。
下面我列出了一些在线课程,我觉得它们的教学大纲很吸引人,价格也很合理:
- 用 Python 进行机器学习:从线性模型到深度学习(注:以下为完整课程)
- 统计与数据科学
- IBM 数据科学专业证书(注:数据分析多于数据科学)
我希望这篇文章能帮助你决定什么样的技能提升方法最适合你,以及在选择学位或寻找在线课程时需要注意什么。
未知知识、贝叶斯推理和结构化高斯过程
思想和理论
为什么领域科学家知道的 ML 比他们想象的要多
马克西姆·兹亚迪诺夫&谢尔盖·加里宁
美国田纳西州橡树岭橡树岭国家实验室纳米材料科学和计算科学与工程中心
大约 20 年前,前国防部长唐纳德·拉姆斯菲尔德在提到情报情况时说了一句著名的话,“…有已知的已知;有些事情我们知道我们知道。我们也知道有已知的未知;也就是说,我们知道有些事情我们不知道。但也有未知的未知——那些我们不知道我们不知道的。”。尽管来自不同的背景,这句话深刻地描述了科学家的工作。我们建立自己的世界观,并基于我们所知道的、可以在教科书中读到的、或在整个职业生涯中从经验、与同事的讨论或在实验室中度过的无数个小时中学到的东西来建立基本的探索。这些是已知的知识。我们也经常知道我们不知道的事情——通常,这些已知的未知是我们研究的直接目标。也就是说,对于每个科学实验来说,未知的未知是至关重要的——我们关注范围之外的因素会极大地影响我们的测量、分析和结论。
显然,拉姆斯菲尔德非常简洁的陈述并不是第一次注意到把世界分成已知和未知的类型。据作者所知,这方面的第一份书面陈述可以追溯到纳赛尔·奥德·丁·图西(1201-1274),他说
Har kas ke bedanad va bedanad ke bedanad
阿斯布-埃凯拉德阿兹贡巴德-埃加敦贝贾汉纳德
Har kas ke nadana d va bedanad ke nadana d
甘兰·卡拉克-凯什贝·曼泽尔·贝雷萨纳德
达尔贾赫勒-莫拉卡巴德奥德-达尔贝马纳德
或者,从波斯语翻译过来:
任何知道的人,以及知道他知道的人
让智慧的骏马飞跃苍穹
任何不知道,但知道自己不知道的人
尽管如此,他还是能把他那只跛脚的小毛驴带到目的地
任何不知道的人,以及不知道自己不知道的人
永远陷在双重无知中
很有可能历史参考可以更早,但我们无法找到它们。也就是说,尽管从阿拔斯和倭马亚时代开始,科学探究工具取得了重大进步,但已知和未知因素在科学发现中的作用依然存在。
例如,在他的科学生涯中,第二作者与电化学应变显微镜(ESM)进行了广泛的合作,该技术允许通过测量由离子运动引起的材料的微小(小于氢原子的半径)膨胀和收缩来探测纳米水平的电化学反应[1,2]。ESM 中测得的信号是局部磁滞回线,代表材料对偏压的电化学响应。对于诸如二氧化铈 CeO₂的经典材料,这些磁滞回线在样品表面的不同位置之间是非常可重复的。这表明它们描述了扫描探针显微镜针尖-表面接合处的反应,并且不受材料成分(我们并没有期望太多)或表面粗糙度(这在 SPM 中总是一个问题)的可能变化的影响。然而,当在受控气体环境和真空中进行测量时,磁滞回线的形状完全改变。这反过来表明,大气环境是测量响应的一个重要因素。如果我们没有在几个大气中进行测量(这需要对显微镜进行非标准的修改),并试图在不考虑大气水的作用的情况下描述机制,这将是未知的未知的典型例子。可以说,它变成了一个已知的未知——这刺激了另一个十年的理论和实验研究,并导致铁电性和表面电化学之间不寻常的耦合的发现[3],以及其他发展。
也就是说,这三个已知/未知的范畴就是全部了吗?我们提出,当积极地将机器学习方法融入领域科学时,我们经常会发现未知的知识——这意味着先验知识的全部范围,从概括(物理定律)和特定领域的数据,一直到难以量化的直觉。因此,现在的问题是,我们如何将先前的知识,未知的知识,整合到科学发现过程中,并以一种有原则的和可量化的方式这样做。
贝叶斯公式奠定了这方面的一般框架。有许多优秀的书籍描述了贝叶斯方法,也有一些中型文章。对于第二作者来说,进入贝叶斯的首选切入点是 Martin [4]和 Lambert [5]的书,Kruschke [6]给出了贝叶斯和频率主义方法之间的系统比较。第一作者认为,关于贝叶斯分析的最好的(对于领域科学家来说)书是 Richard McElreath 的“统计再思考”。
下面,我们从领域科学家的角度给出经典贝叶斯公式。在这里,科学实验的目的是从新获得的实验数据中获得新的知识,也就是理论的概率。

图一。领域科学家视角下的贝叶斯公式。作者图。
这里,给定理论的数据的概率,或贝叶斯语言中的可能性,使用正演模拟直接访问。使用计算方法直接评估数据或证据的概率,无论是马尔可夫链蒙特卡罗还是变分推断。但是第三部分——理论的概率,或者先验——通常会引发最多的问题。事实上,经常针对贝叶斯模型的批评是它们需要先验知识,而这些先验知识很难被一致地建立。事实上,他们是——如果一个人完全停留在统计学领域。对于领域专家来说,先验知识是绝对必要的,它实际上定义了领域专长。然而,它是未知的已知,并且通常这种知识不容易量化或以可追踪算法形式的先验分布的形式建立。也许更重要的是,现在还不清楚如何将先前的知识整合到实验发现的主动学习流程中。让我们逐一考虑。
一旦实验数据可用,贝叶斯推理方法允许进行直接分析。例如,最近,我们利用高分辨率电子显微镜探索了铁电畴壁的结构[7]。这里,畴壁结构由自由能的特定函数形式决定,自由能产生描述畴壁轮廓的解析解。简单的函数拟合,如前所述[8,9],给出了参数的数值估计,但不允许超出这些。相比之下,贝叶斯方法允许以相对常数的先验分布的形式结合材料物理学的先验知识,这反过来可以从大量的公开数据、宏观测量等中获得。例如, Materials Project 等大型材料信息学项目的价值之一就是将来自多个独立来源的数据整合成易于访问的形式。
同样,贝叶斯框架允许回答一些重要的问题,如材料物理学的先验知识是否影响我们从实验中学到的东西(相当令人惊讶的是,我们知道的越多,我们学到的越多),材料行为的特定模型是否可以基于观察到的数据进行区分(在这种情况下不是),以及区分不同模型需要什么样的显微镜分辨率或信息限制。类似地,贝叶斯方法允许以概率方式定义许多“绝对”量——例如,对称性或结构单元等描述符[10]。
然而,下一个问题变成了我们是否可以使用贝叶斯方法进行主动学习和属性优化?原则上,可以使用上面详述的经典贝叶斯方法。这里,我们将先验假设定义为一个结构化的概率模型。给定实验数据,我们改进我们模型的参数,然后在参数空间上探索后验预测概率。与我们之前的帖子中描述的经典贝叶斯优化/主动学习(BO/AL)非常相似,期望值和/或相关的不确定性可用于指导测量(即选择下一个测量点)。
然而,在实践中,基于预期系统行为的结构化概率模型的 BO/AL 在模型仅部分正确时不能很好地工作。与此同时,这正是我们通常在实验中必须处理的问题,在实验中,即使最复杂的理论也只能在一定程度上描述现实,而且可能存在几种竞争模型,更不用说所有与测量相关的非理想性了。当然,另一种选择是用高度灵活的高斯过程(GP)来代替刚性的结构化概率模型。然而,后者通常不允许结合先前的领域知识,并且可能偏向于平凡的插值解决方案。
因此,我们引入了结构化高斯过程(sGP),其中经典的高斯过程被预期系统行为的结构化概率模型所增强[11]。这种方法允许我们平衡非参数 GP 方法的灵活性和编码到参数模型中的先验(物理)知识的刚性结构。后者的贝叶斯处理通过选择模型参数的先验,即结合过去的知识,对 BO/AL 提供了额外的控制。
更正式地说,给定输入参数 x 和目标属性 y ,经典 GP 被定义为 y ~ 多变量 ( m ( x ), K ( x , x `)),其中 m 是通常设置为 0 和的均值函数后者可以使用哈密尔顿蒙特卡罗(HMC)抽样技术来推断。然后,对新的/未测量的输入集的预测由下式给出

其中 θⁱ 是具有核超参数的单 HMC 后验样本。注意,通常假设的观察噪声被吸收到核函数的计算中。(我们用*下标表示新的输入和相关的预测——不幸的是,在撰写本文时,介质还没有对这个下标和其他下标的内嵌支持)。
在我们的 s GP 方法中,我们用一个结构化的概率模型代替上述等式中的常数均值函数 m ,该模型的参数通过 HMC 与核参数一起推断。然后,等式(1b)变成:

其中 ϕⁱ 是具有学习模型参数的单个 HMC 后验样本。该概率模型反映了我们关于系统的先验知识,但它不必是精确的,也就是说,该模型可以具有不同的函数形式,只要它捕捉数据中的一般或部分趋势。
使用结构化 GP 的贝叶斯优化
我们将通过具体的例子来说明 s GP 方法的优势。首先,我们将使用一个稍加修改的福雷斯特函数、f(x)=(5x-2)sin(12x-4),它通常用于评估优化算法。除了全局最小值之外,它还有两个局部最小值。我们的目标是仅使用少量的步骤(测量)来收敛到全局最小值。首先,我们将 BO 与 vanilla GP 一起使用。我们从种子点的“良好”初始化开始(在运行 BO 之前执行的“预热”测量),并使用置信上限 (UCB)采集函数和 k = -0.5,在每一步选择下一个测量点。不出所料,GP-BO 很快收敛到真正的最小值。

图二。使用普通 GP-BO 的黑盒函数优化:“良好”初始化的情况。作者图。
现在,让我们从一个“坏的”初始化开始,其中初始化的点落入一个局部最小值周围的区域。由于这种糟糕的初始化,GP-BO 陷入局部最小值,无法收敛到真正的最小值。我们注意到,在真实的实验中,人们通常没有使用不同的初始化来重新开始测量的奢侈,因此必须准备好处理不好的初始化。

图三。使用普通 GP-BO 的黑盒函数优化:“坏”初始化的情况。
看待上面的例子的一种方式是,我们陷入局部最小值,因为我们的算法不“知道”不同最小值的潜在存在。但是如果我们确实知道可能有更多的极小值(其中一个可能是真实的/全局的极小值),那么这个先验知识可以合并到 s GP 模型中。
具体来说,让我们将结构化概率模型定义为

其中 y ₀ ~ 均匀 (-10,10)aₙ~对数正态 (0,1)wₙ*半正态*(0.1)*x*ₙ⁰均匀 (0,1)为模型参数的先验。这个模型简单地告诉我们,在我们的数据中有两个极小值,但并不假设对它们的相对深度和宽度有任何了解,也不包含它们相距多远的信息。然后,我们用等式(3)中定义的概率模型替换 GP 中的常数先验均值函数 m ,并以与之前相同的方式运行“坏”初始化的 BO。

图 4。在初始化“不良”的情况下,使用 s GP-BO 进行优化。由于关于系统的部分先验知识(其具有不止一个最小值),优化算法能够将其自身从局部最小值中拉出,并收敛于真实/全局最小值。作者图。
我们可以看到,配备了预期系统行为的一些知识的优化算法能够将自己从局部最小值拉出来并收敛到全局最小值。我们已经在 GP-BO 和 s GP-BO 之间进行了系统的比较,针对种子点的几十种不同的随机初始化,并且在存在噪声的情况下,发现 s GP-BO 在几乎所有情况下都有更好的性能【11】。
为了理解 sGP-BO 的优越性能,我们可以比较 GP-BO 和 s GP-BO 在不同优化步骤的采集函数。

图 5。第 1、3、5、7 和第 9 步中普通 GP-BO(顶行)和 s GP-BO(底行)的采集功能。作者图。
显然,普通 GP-BO 很快锁定在局部最小值,其获取函数基本保持不变(图 5 中的顶行),因为算法“假设”它已经找到了真实/全局最小值。另一方面, s GP-BO(图 5 中的底行)中的采集函数在早期显示了一个具有多个最小值的结构,这有助于算法在过程的后期爬出局部最小值并收敛到真正的最小值,即使初始化非常糟糕。图 5 的底行中的采集函数的演变在某种意义上确实是显著的,因为该算法正在寻找第二个最小值,好像是由直觉驱动的。从某种意义上来说,我们在等式(3)中以 GP 均值函数的形式纳入的先验知识假设在某处存在第二个最小值,而采集函数突出显示了第二个最小值最有可能出现的位置。
但是如果这个“先验知识”只是部分正确呢?如前所述,与独立的概率模型相比, s GP-BO 的优势在于 GP 内核的灵活性允许我们使用不精确的,有时甚至是“不正确的”函数形式的模型。我们使用形式为m=aeᵃˣsin(bx)的结构化概率模型来说明这一点,BO 作为独立模型和作为 s GP 的一部分。我们注意到,虽然这显然不是描述 Forrester 函数的正确模型,但它确实捕捉到了数据中的一些趋势,例如存在一个全局最小值和多个局部最小值。

图 6。使用独立概率模型的 BO 结果(左)和使用完全相同的模型增强的 sGP-BO 结果(右)。作者图。
具有独立概率模型的业务对象(图 6,左侧)无法收敛到全局最小值。如果现实不适合所选择的模型框架,那么推论本身就太死板并且会失败。它也不能产生目标函数的令人满意的重建,即使在测量点周围,并且它的不确定性估计似乎不是特别有意义。另一方面, s GP-BO 使用相同的概率模型作为 GP 的一部分,轻松识别出真正的最小值(图 6,右)。它还对测量区域周围的基础函数进行了良好的重建,并提供了这些区域之外的有意义的不确定性估计,这可用于选择下一个测量点以完全恢复目标函数(如有必要)。
主动学习不连续函数的结构化 GP
作为我们的第二个例子,我们展示了如何使用 s GP 从稀疏和有噪声的观测中重建不连续的函数。我们注意到,不连续性的存在在相变物理学中是常见的,在相变物理学中,人们通常希望重建靠近相变点的感兴趣的性质的行为,以及在相变之前和之后的两个阶段中的行为。同时,标准 GP 不具备处理这种不连续性的能力。

图 7。 (a)不连续函数的噪声观测,(b)基础分段函数的基于普通 GP 的重构,(c) s 基于 GP 的重构,具有系统行为的“正确”模型,(d)基于 sGP 的重构,具有部分正确的模型。重构中的不确定性由采样预测中的离散度定义。作者图。
图 7a 示出了由形式的分段函数产生的观察值

我们的目标是从这些有噪声的数据点重建基础函数。首先,我们尝试普通 GP(图 7b)。它表现不佳,这并不奇怪,因为标准 GP 不具备处理不连续性的能力。直观上,它可以通过不连续性给核函数带来的矛盾需求来理解——具有大长度尺度的核不能描述不连续性,而小长度尺度需要多次测量来发现各处的函数行为。请注意,简单 GP 在测量区间之外的外推也很差(尽管在这种情况下,可以使用多种策略来解决这个问题)。
接下来,我们尝试用两种不同的概率模型增强的 s GP。第一个 s GP 由分段概率函数扩充,该函数具有一般幂律形式:对于xaxᵇxₜ和 cxᵈ 对于 x ≥ xₜ ,具有关于 xₜ 的均匀先验和关于其他参数的对数正态先验。它显示了一个良好的整体重建(图 7c),除了过渡区,我们没有足够的观察。然而,该区域也具有非常大的不确定性(采样预测的变化),表明人们可能想要在该区域进行额外的测量。在这种情况下,检查采样预测的行为也是值得注意的-本质上,它们在切换点位置的估计上是不同的。同样,这符合科学家的决策逻辑——如果我们预期相变,并且对相变前后测量响应的功能行为有信心,那么剩下的唯一不确定性就是相变点!
第二个 s GP 由一个分段概率函数扩充,对于 x < xₜ 等于 ax 并且对于 x ≥ xₜ 等于 bx ,在 xₜ 上具有均匀先验并且在 a 和 b 上具有对数正态先验。请注意,这个模型只是部分正确:它有一个转变点,但在转变前后呈现线性(而不是幂律)行为。因此,重建质量(图 7d)较低,尽管它仍然比普通 GP 好一些。请注意,在这种情况下,最大的不确定性与转换前的行为相关(我们不期望幂律行为)。
模型预测中的不确定性可用于指导测量,以获得基础函数的更精确的重建。该过程与贝叶斯优化中的过程相同,除了我们使用模型的不确定性而不是采集函数来预测下一个测量点。

图 8。主动学习的结果,其中(a)普通 GP,(b) s GP 由预期系统行为的“正确”模型增强,(c) s GP 由预期系统行为的部分正确模型增强。作者图。
我们可以看到,两个的 GP 模型在探索参数“空间”的其他部分之前,很快集中在过渡区域(图 8b,c)。因此,我们能够显著提高重建质量。另一方面,使用 vanilla GP 的不确定性导向勘探仍然无法重建过渡区(图 8a)。因此,我们再次看到,即使的 GP 对预期系统的行为有部分正确的模型,也能胜过标准的 GP 方法。
总之,用预期系统行为的(完全贝叶斯)概率模型增强高斯过程,允许更有效的优化和系统属性的主动学习。请注意,即使对于不正确或部分正确的模型,主动学习算法最终也会得出正确的答案——但代价是大量的实验步骤。查看我们的预印本了解更多细节,包括这个概念在物理模型中的应用[11]。也请查看我们的 gpax 软件包,将此工具和其他 GP 工具应用于科学数据分析。如果你想了解更多关于扫描探针显微镜的知识,欢迎来到 M*N 频道:显微镜,机器学习,材料。
最后,在科学界,我们感谢我们的研究赞助商。这项工作在橡树岭国家实验室纳米材料科学中心(CNMS)进行并得到支持,该中心是美国能源部科学用户设施办公室。您可以使用此链接进行虚拟漫游,如果您想了解更多信息,请告诉我们。
可执行的 Google Colab 笔记本在这里有。
参考文献:
1.巴尔克,新泽西州;杰西;莫罗佐夫斯卡;叶利舍耶夫,e;钟德伟;金,y;阿达姆奇克湖;加西亚河;新泽西州杜德尼;锂离子电池阴极中离子扩散的纳米级绘图。 Nat。纳米技术。 2010, 5 (10),749–754。
2.库马尔,a。丘奇,女;莫罗佐夫斯卡;加里宁公司;Jesse,s,《在纳米尺度上测量氧还原/析出反应》。 Nat。化学。 2011, 3 (9),707–713。
3.杨,S. M 莫罗佐夫斯卡;库马尔河;叶利舍耶夫,英国;曹;马泽特湖;巴尔克,新泽西州;杰西;瓦苏德万;杜布尔迪厄,c;纳米级铁电体中的混合电化学-铁电态。自然物理 2017, 13 (8),812–818。
4.Martin,o .,使用 Python 的贝叶斯分析:使用 PyMC3 和 ArviZ 的统计建模和概率编程介绍,第二版。Packt 出版:2018。
5.Lambert,贝叶斯统计学生指南。塞奇出版有限公司;1 版:2018。
6.Kruschke,j .,做贝叶斯数据分析:与 R,JAGS 和斯坦的教程。学术出版社;2 版:2014 年。
7.C. T .纳尔逊;瓦苏德万;张;齐亚特迪诺夫,m;叶利舍耶夫,英国;竹内岛;莫罗佐夫斯卡;通过原子解析 STEM 数据的贝叶斯分析探索铁电畴壁的物理学。 Nat。Commun。 2020、 11 (1)、12。
8.鲍里塞维奇;莫罗佐夫斯卡;金耀明;伦纳德博士;奥克斯利议员;医学博士 Biegalski 叶利舍耶夫,英国;通过拓扑缺陷的原子尺度观测探索空位有序系统的介观物理。物理学家列特。 2012 年, 109 (6)。
9.李,q;C. T .纳尔逊;许淑玲;达摩达兰;李;亚达夫,又名;麦卡特尔,m;马丁;拉梅什河;Kalinin,S. V,《使用机器学习和相场建模对 PbTiO3/SrTiO3 超晶格极涡中的挠曲电进行量化》。 NatCommun。 2017, 8 。
10.加里宁公司;奥克斯利议员;瓦莱蒂,m;张;赫尔曼,注册会计师;郑;张;Eres,g;瓦苏德万;深度贝叶斯局部结晶学。 npj 计算资料 2021、 7 (1)、181。
11.文学硕士齐亚特迪诺夫;戈什,a。物理学有所不同:贝叶斯优化和通过增广高斯过程的主动学习。arXiv:2108.102802021。
释放 Databricks CLI 的威力
从您的控制台舒适地管理您的 Databricks 工作区!

照片由在 Unsplash 上的Launde Morel 拍摄
如果我告诉您,您可以从一个命令行控制台控制所有数据块资产,会怎么样?
Databricks 已经是数据工程师武库中一个非常强大的工具。它在分析方面的成就令人印象深刻,有些人甚至可以说是革命性的。
一个数据块工作空间本质上是一个管理所有数据块资产的环境。工作区允许您组织笔记本、表格、群集,并帮助管理作业。
虽然界面非常光滑和用户友好,但像导出/导入笔记本、启动集群、尝试将每个笔记本链接到您的 git repo 这样的事情感觉更像是痛苦的体力劳动。
这变得令人毛骨悚然,尤其是如果你必须在你的平台上管理多个工作区,并且很难跟踪你的浏览器中打开的所有小标签。
要是有办法从一个地方管理您所有的工作空间就好了。值得庆幸的是有,并且像所有的好东西一样,这个是以 python 包的形式出现的。
如何配置 Databricks CLI
Databricks-CLI 是一个开源库,可以使用 pip 命令安装。
使用适合您的 python 版本的 pip 版本运行pip install databricks-cli。
一旦你安装了库,你可以使用databricks -- version列出版本
要开始编写 CLI 命令,您必须首先设置要管理的 Databricks 工作区的身份验证。这必须只做一次。
我将以 Azure Databricks 为例展示以下步骤,但这些步骤在任何托管 Databricks 服务上都非常相似。
可以使用 Databricks 个人访问令牌设置身份验证。这个令牌可以直接从工作区生成。
- 点击工作区中的用户资料图标,并选择用户设置
- 您将看到一个选项生成新令牌
- 设置生存期并生成令牌。记得复制令牌。
- 从浏览器中获取工作区的 URL,格式为
https://<instance-name>.cloud.databricks.com
现在,您可以通过在 cmd 行上运行databricks configure --token在本地机器上配置认证。系统会提示您输入Databricks Host粘贴工作区的 URL。接下来,系统会提示您输入Token,粘贴您生成的令牌。
要快速检查认证是否有效,运行命令databricks workspace list,如果一切正常,您必须能够在控制台上列出您的数据块工作区中的所有目录。
但是,如果您希望管理属于不同环境的多个工作区,或者您为多个客户端工作,并且每个客户端都有自己的专用工作区,那么这将使您只能管理单个工作区。
您可以通过设置连接配置文件在控制台上进行配置。要添加连接配置文件,请设置唯一的名称来标识特定的工作区。例如开发或 UAT 工作区。
- 运行
databricks configure --token --profile <<profile_name>>这就像为你的每个工作区创建一个别名,这样 CLI 就能记住哪个是哪个。 - 像之前一样输入主机名和令牌。
- 要使用此连接配置文件,您只需用
--profile <<profile_name>>来补充您想要运行的每个 CLI 命令 - 要确认只需运行
databricks workspace list --profile <<profile_name>> - 要切换到另一个工作空间,只需更改配置文件
这是它在游戏机上的样子。

如何配置 Databricks CLI(图片由作者提供)
只需对所有工作区重复相同的配置步骤,并记住给每个工作区取一个唯一的名称,以便于识别。
提示:CLI 将此配置作为.databrickscfg存储在您机器中的一个文件中。如果您想通过控制台为每个工作区配置身份验证,只需手动编辑这个文件。文件看起来像这样。

的内容。databrickscfg 文件(图片由作者提供)
如何使用 CLI 管理笔记本电脑
CLI 命令组合在一起,代表您可以管理的不同资产。您可以使用databricks <<group>> --help列出特定组的子命令。组可以是fs、clusters、workspaces 等等。要列出文件系统的子命令,只需运行databricks fs --help

数据块文件系统子命令(图片由作者提供)
您最想做的一件事是列出您工作区中的所有笔记本,并可能将它们导出到您的本地机器中,然后您可以轻松地将其与 git 存储库集成。
- 使用
databricks workspace list --profile <<profile_name>>列出你工作区中的所有文件夹 - 使用
databricks workspace export_dir /<<directory_in_databricks>> <<Target_path>> --profile <<profile_name>>递归导出工作区目录中的所有笔记本这会将所选目录中的所有笔记本导出到您机器上的目标路径中。
同样,您可以导出单个笔记本,也可以轻松地将笔记本或目录导入工作区。这里有一个与工作空间相关的命令的详尽列表,您可以尝试一下。
如何使用 CLI 管理集群
数据工程师通常会花大量时间管理集群,通常一天中,我必须在许多不同的工作区启动集群,它们需要时间预热。过了一会儿,事情变得如此混乱,以至于你不记得你最初打算做什么。
Databricks CLI 允许您从控制台管理所有集群,而不必访问每个工作区。这无疑为你节省了大量宝贵的时间,并清理了你的浏览器标签。
- 您可以做的最简单的事情就是使用
databricks clusters list --profile <<profile_name>>列出工作区中的所有集群。它列出了工作区中的所有集群及其唯一的集群 id 和当前状态。 - 一旦您有了集群 id 并知道了您的集群的状态,您就可以使用
databricks clusters start --cluster-id <<id>> --profile <<profile_name>>轻松地启动这些集群中的任何一个几分钟后,您的集群将全部预热完毕。

在 CLI 中管理集群(图片由作者提供)
使用集群组命令,您可以做许多更有趣的事情,这里是与集群相关的 CLI 命令的详尽列表。
外卖食品
Databricks CLI 是一种将一些乐趣引入管理 Databricks 的好方法,也是一种帮助我节省宝贵时间的工具,我可以将这些时间花在喝咖啡休息上。
我的收获:
- 您可以在舒适的控制台上控制所有数据块资产。
- 您可以随时关注您的集群。
- 您可以使用简单的命令创建、删除、启动集群并调整其大小,而无需导航到工作区。
这篇博客中列出的命令只是日常任务中微不足道的几个。您可以深入研究 Databricks 文档,尝试一些新的东西,或者用它构建一个完整的应用程序。
如果您是一名数据工程师,请将 Databricks CLI 添加到您已经令人印象深刻且详尽的工具列表中,我保证您不会后悔。
请随意问我关于这篇文章的任何问题,我很想知道你对 Databricks CLI 的看法。
释放 Scikit-learn 管道的力量
scikit-learn 管道中级指南

克里斯托夫·迪翁在 Unsplash 上拍摄的照片
在我的上一篇文章中,我写了一篇介绍 scikit-learn 管道的文章。如果您尚未阅读,可以通过下面的链接访问:
在这篇文章中,我想扩展以前的帖子,展示一些很酷的功能,以创建更强大的管道,在帖子的最后,我将向您展示如何微调这些管道,以提高准确性。
正如我们在上一篇文章中看到的,管道可能非常有用,但是我们在介绍中仅仅抓住了表面。
如果我们想做一些考虑到特征类型的特征转换呢?
假设我们有一个包含数字和分类特征的熊猫数据帧,我们希望以不同的方式处理这两种类型的特征。在这种情况下,我们可以使用 scikit-learn pipeline 中的 ColumnTransformer 组件。我们开始吧!
出于教育目的,我们将使用 Kaggle 的成人人口普查收入数据集:
https://www.kaggle.com/uciml/adult-census-income
该数据集包含 6 个数字特征和 9 个分类特征,因此它似乎适合我们想要构建的管道。jupyter 笔记本将在我的 github 中提供,您可以通过以下链接下载:
在实现管道之前,我们必须对数据集做一点转换,因为所有未知的值都保存为“?”而不是 NaN,收入(目标列)是一个字符串,我们想把它编码成实数。
一旦我们完成了这些转换,我们就可以开始实现我们的管道了。为此,我们将使用 scikit-learn 的 ColumnTransformer 组件,并以不同的方式选择和处理分类和数字特征:
Make_column_selector 将帮助我们按类型选择列。在我们的例子中,这些类型对于数字特性是 int 和 float,对于分类特性是 object。如果我们仔细观察,ColumnTransformer 组件也使用了两个变量,称为 numerical _ pipe 和 categorical _ pipe。这些管道的定义如下:
一旦我们定义了 ColumnTransformer 的组件及其所有元素,我们将使用该组件来创建我们的主管道:
就是这样!我们的管道完工了!让我们用它来训练,并测量它的准确性:
我们使用 33%的数据作为测试集的准确率是 83.2%。那一点也不差!
如何才能提高这条流水线的精度?
为了改善这个管道的结果,我们可以对每个组件执行微调技术。Scikit-learn 使使用 GridSearchCV 进行微调变得容易。该组件将进行广泛的微调,在我们定义的范围内尝试所有可能的超参数组合。这里我们必须小心,因为如果我们尝试太多的组合,与此过程相关的计算复杂性可能会呈指数级增长:
如果我们仔细观察代码,我们已经为管道定义了一些超参数,然后在定义了我们想要优化的指标之后,我们已经开始了拟合所有可能组合的过程。经过这一过程,我们将准确度提高了 0.9%,总准确度达到 84.1%。这听起来不多,但这是一个非常简单的优化,它可以进一步改善定义一个更大的超参数空间,尝试更强大的模型,进行一些贝叶斯或遗传优化,而不是网格搜索优化,等等。
结论
当我们一起使用管道和超参数微调时,它们工作得非常好。这篇文章展示了管道不可思议的潜力,以及它们如何在数据科学家的日常工作中非常有用。虽然这个管道比我第一篇文章中的管道更复杂,但是这些管道可以更复杂。例如,我们可以创建自定义转换,并在管道中使用它们。然而,这超出了这篇文章的范围,但是如果你想要一篇关于定制变形金刚的文章,请告诉我。
参考
https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html https://scikit-learn.org/stable/modules/generated/sklearn.metrics.make_scorer.html https://scikit-learn.org/stable/modules/generated/sklearn.compose.ColumnTransformer.html https://scikit-learn.org/stable/modules/generated/sklearn.pipeline.Pipeline.html
熊猫无限的灵活性
几乎总是有不止一个解决方案

埃里克·普劳泽特在 Unsplash 上拍摄的照片
Pandas 是一个非常流行的 Python 数据分析和操作库。它是数据科学教育中引入的首批工具之一。Pandas 为更有效的数据分析提供了丰富的功能和方法。
我最喜欢熊猫的一点是,完成一个给定的任务几乎总是有多种方式。一种方法可能在时间和复杂性方面胜过其他方法。然而,拥有多种选择会让你跳出框框思考。这也有助于改善你解决复杂任务的方法。
练习用不同的方法来解决问题的另一个好处是,它会大大提高你对熊猫的了解。
在本文中,我们将查看一些示例,展示完成相同任务的不同方法。Kaggle 上可用的医疗费用数据集用于示例。
让我们从导入库和将数据集读入数据帧开始。
import numpy as np
import pandas as pddf = pd.read_csv("/content/insurance.csv")
df.head()

(图片由作者提供)
该数据集包含一些个人信息和保险费用。
第一组示例是基于列中的值过滤行。例如,我们可能想要过滤年龄大于 40 的行。
完成这项任务的更常见的方法如下。
df[df.age > 45]
另一种方法是使用 query 方法,并将过滤条件指定为字符串。
df.query('age > 45')
我将展示的第三种方式通常不被使用,但对展示熊猫的灵活性很重要。
df.where(df.age > 45).dropna(axis=0, how='all')
我们首先应用 where 函数,该函数只接受符合指定条件的行,并将其他行标记为 NaN。然后,我们删除充满 NaN 值的行。
第二个任务是创建一个新列。假设我们需要添加一个包含客户 id 号的新列。
下面是第一种方法。
cust_id = np.arange(1,1339)df['cust_id'] = cust_iddf.head()

(图片由作者提供)
如果我们将客户 id 列作为第一列,可能会更好。熊猫的插入功能就是在那种情况下使用的。
df.drop('cust_id', axis=1, inplace=True)cust_id = np.arange(1,1339)df.insert(0, 'cust_id', cust_id)df.head()

(图片由作者提供)
我们首先删除上一步中添加的列,然后插入客户 id 列作为第一行。insert 函数的参数是要插入的位置、列名和值。
下一个例子涉及到熊猫的功能分组。我们可以用它来计算性别和吸烟者列中每个类别的平均费用。
df[['sex','smoker','charges']].groupby(['sex','smoker']).mean()

(图片由作者提供)
假设我们希望将性别和吸烟者作为数据帧中的列,而不是索引级别。
一种方法是使用 reset_index 函数。
df[['sex','smoker','charges']].groupby(['sex','smoker']).mean()\
.reset_index()
我们还可以使用 group by 函数的 as_index 参数。
df[['sex','smoker','charges']].groupby(['sex','smoker'], as_index=False).mean()
两者都会产生以下数据帧:

(图片由作者提供)
下一个例子简单而实用。我们可能希望看到一列中唯一值的数量。一种方法是隐式使用 value_counts 函数。
value_counts 函数返回列中的唯一值以及出现的次数。如果我们应用带有 value_counts 的 len 函数,我们将得到唯一值的数量。
一个更简单的方法是使用一个专用的函数来完成这个任务,这个函数就是 nunique。顾名思义,它返回一列中唯一值的数量。
len(df.age.value_counts()) == df.age.nunique()
True
正如我们所看到的,两者返回相同的值。
假设我们有以下数据帧:

(图片由作者提供)
索引包含 id 号。我们希望将它们作为名为“id”的列,而不是索引。
一种方法是使用 reset_index 函数,该函数使用索引创建一个新列。但是,该列的名称将是“index ”,因此我们需要使用 rename 函数来更改它。
df.reset_index().rename(columns={'index':'id'})

(图片由作者提供)
另一种方法是首先重命名索引,然后重置索引。
df.rename_axis('id').reset_index()

(图片由作者提供)
结论
我们已经看到了一些例子,说明用不同的方法用熊猫来完成同样的任务是可能的。
在某些情况下,由于时间和复杂性的考虑,一种方法比其他方法更受欢迎。然而,知道多种方法总是更好的。练习和实施不同的方法也会提高你的熊猫技能。
感谢您的阅读。如果您有任何反馈,请告诉我。
Kubernetes 中无限的科学图书馆和应用程序,即刻!
建立一个包和应用程序的共享库,可以动态地加载到您的 Kubernetes 数据科学环境中。

迈克尔·D·贝克与在 Unsplash 上的合影
TL;DR:
在 Kubernetes 上提供 Jupyter-as-a-Service 在规模上是很棘手的,当你需要提供许多不同版本的不同库或应用程序来满足每个用户的需求时。
而不是创建超重的容器图像,或者必须管理(并从中选择!)数百个,或者让人们每次打开笔记本时从头重新安装所有东西,您可以建立一个共享的库和应用程序库,并让用户动态和即时地加载它们。
为开放数据中心提供的完整代码和部署说明!

(作者截图)
免责声明:我在 Red Hat 工作,帮助组织开发数据科学/数据工程平台和解决方案。因此,这里描述的实现使用不同的 Red Hat 产品,如 Red Hat OpenShift (我们的 Kubernetes 企业发行版),或[Red Hat open shift Data Foundation](http://Red Hat OpenShift Data Foundation)(我们基于 Ceph 的 open shift-原生存储解决方案)。然而,整体架构和方法可以适用于其他平台。
现在,让我们开始长篇大论吧!
第一章:麻烦是如何开始的
Kubernetes 是数据科学和数据工程平台的流行选择。敏捷性、多功能性、资源可扩展性……使其成为这些工作负载类型的首选平台。尤其是在拥有共享环境的大型组织中:将您的应用程序、库和依赖项打包到轻量级容器映像中,使它们可复制、一致且安全,这似乎是解决您所有问题的完美答案!
您最近读到的这些内容让您激动不已,您以这种方式打包了一个 Jupyter 容器图像:

(作者图文)
因为你很聪明,想要预测需求(翻译:你不想一次又一次地回到这个话题),所以你甚至创造了一些不同的“味道”来涵盖你能想到的不同用例:仅带 Scipy 的基本笔记本,一个带 Tensorflow 的,一个带 Pytorch 的,……完美,那些容器图像都可以使用。
因此,您在 Kubernetes 环境中设置了 JupyterHub 或 Kubeflow,并为所有用户创建了一个 Jupyter 即服务环境!任务完成了,问题解决了,你就可以回去做其他(总是)迫切的事情了。
几个星期过去了…
然后是 Alice、Bob 和数据科学家团队的其他成员。他们甚至带来了一些 BI 人员,以通过该组织正在经历的数字化转型来展示他们的善意,还带来了一些数据工程师(谁也不知道,可能会有数据管道的漏洞需要修复)。但是没有统计学家,你们都知道为什么…

(图片作者来自 imgflip.com)
爱丽丝开始说,“听着,Jupyter 即服务这个东西太棒了!过去几周我们一直在玩它,我们看到它如何取代我们的个人环境:在我们的笔记本电脑上不再需要安装和管理任何东西,因为笔记本电脑缺乏处理能力和内存。任何地方都可以访问任何东西!”。
“是的,这绝对是一个巨大的进步。”,鲍勃继续说道。“我们现在能够轻松地交换我们的笔记本电脑,因为它们都基于相同的容器映像,并使用中央数据湖。这个库或这个应用程序不会再丢失了!”
有些困惑,因为这以前从未发生过,你问,“好吧,那么,完美。但你为什么在这里,不仅仅是为了感谢我吗?”
“哦,几乎没有什么,对你来说,这很容易!”查理大声说道。“你知道,你为我们创造的不同风格的环境非常棒。它只是缺少这个我经常使用的库,你知道我的 avLib 吗?当然是在 3.2.4 或更高版本,因为在 3.2.3 之前有一些坏的东西。但不是来自新的 4.x 分支,我真的不喜欢他们用它做的事情。”
“是的,如果你还能加上巴丽、LibB 和 LibC,那就太好了。哦,顺便说一下,对于 LibC,我需要最近的 3 个版本,以便能够比较一些东西”,Debbie 补充道。
鲍勃以他一贯的方式插话道,“既然我们在讨论这个问题……当然,所有旧版本的图像和库总是可用的,对吗?因为这是我们审计流程的一部分,可以在任何时候重新计算,你知道,为了负债的目的。因此,当然必须总是使用完全相同的工具。另外,我也不想为了适应新版本而修改代码。”
看到你的脸有点腐烂,爱丽丝打断了它,“好吧,我猜你已经明白了,所以我们会离开你,再次感谢!”
由于你是一个经验丰富的专业人士,你设法隐藏恐慌上升,并迅速切换到解决方案模式…
第二章:几个选择,没有真正的解决方案…
几天后,你会思考你绞尽脑汁的结果…
选项 A :臃肿的形象。
好了,让我们把他们问的所有东西放到同一个容器图像里!这很简单:只需在 requirements.txt 中添加几行,或者在 docker 文件、docker build 中添加一些东西,就完成了!现在让我们把图像上传到回购。什么,预计到达时间 2:34:12?!哦…这个图像是 38.2GB…肯定是不可管理或不可行的:“起始容器。拉图,请明天再来……”。无论如何,我不能适应同一个库的多个版本。
选项 B :永无止境的图像流。
嗯,在现有图像的基础上,我总是可以创建定制的图像来满足各种需求。但是让我们看看,……只有 15 个库,每个库有 2 个可能的版本,理论上有 2 个⁵组合,32 768 个!即使他们只要求百分之一,仍然有 300 多个容器图像可供选择。我很确定哪个图像的下拉选择器不会允许这么多。无论如何,即使用户因为没有选择而应付,从长远来看,整个事情将是不可收拾的。
选项 C :让他们自己处理。
毕竟,做一个 pip 安装并没有那么难!如果有 requirements.txt,我甚至可以通过一些巧妙的机制让一个进程查看笔记本的文件夹,并安装后台列出的所有内容。当然,每次笔记本启动时都会出现这种情况,这会导致延迟和投诉…而且无论如何,它不会解决使用不同集/版本的基础库来维护许多图像的问题。除非我希望用户每次都从头开始重新安装整个堆栈…
这种方法也不能解决无法使用 pip 或 conda 安装应用程序的问题。而且在一个安全的企业环境中,容器不作为 root 运行(这是一个最佳实践),所以人们无论如何也不能安装任何东西。
那么该怎么办呢…?

你不太好的选择…(作者提供图片)
第三章:绝望的深渊
作者注:本章已被自愿过滤为 1)没有必要让每个人都过这种生活,即使是通过代理。2)它与 It 或数据科学没有任何联系。
第四章:这就是方法
在过去,我有机会了解加拿大计算机公司(Compute Canada)和 HPC 世界的许多人,至少是学术界的人,特别是神奇的 T2 easy build(T3)社区,正在做什么来解决这个相同的问题:如何在一个共享的环境中以有效和可重复的方式为您的用户带来应用程序和库,使数据科学家轻松地完成他们的工作,而不是笨拙地安装、打包或编译…?
他们的答案是使用环境模块来动态“加载”预打包的库或应用程序。将其简化到极致,假设您可以访问安装在文件系统中某处的一些共享文件夹,模块引擎将简单地修改您的\(PATH、\)PYTHONPATH 或类似的其他环境变量,以使这些文件夹在您的环境中“可见”。不再有 Python 抱怨当你做“导入 pytorch”时找不到库,因为现在它知道在哪里可以找到它!
当然,所有这些都更加精确,因为它可以跟踪依赖性,加载或不加载什么,卸载什么,以及在交叉依赖性的情况下何时卸载…
但基本上,我们问题的解决方案包括两件事:
- 提供对“模块”库的访问,这些模块可以是 Python 库、Linux 库或成熟的应用程序。这个库可以在繁殖时挂载到您的环境中。
2.有办法在环境中轻松加载这些模块。

Kubernetes 中的实现概念(图片由作者提供)
所以这是我在开放数据中心(开放数据中心,ODH,是一个基于 Kubeflow 的开源项目,提供开源 AI 工具,用于在 OpenShift 容器平台上运行大型分布式 AI 工作负载)内实现的概念验证解决方案:
- 共享库安装在 OpenShift Data Foundation 提供的 RWX 卷上(后台有 CephFS)。
- ODH 正在提供 Jypyter-as-a-Service 环境,在这种情况下,它有一个稍微定制的镜像,可以加载模块,还有一个 JupyterLab 扩展,可以轻松地完成加载/卸载部分。
- 当产生 Jupyter 时,共享库以只读方式安装在 pod 中。
- 然后,您只需在需要时加载您需要的内容!
这是它看起来的样子:

环境快速演示(作者截图)
这是在这个快速概述中发生的事情:
- 我启动了一个基本的笔记本示例,它使用 Torchvision 从一张图片中推断出一只狗的品种(不是任何一只狗,我的!).
- 当我尝试运行笔记本时,出现了 ModuleNotFound 错误,因为我的环境中没有可用的 torchvision 库。
- 所以在左边的面板上,在我的软件的扩展菜单中,我通过输入几个字母来查找这个包,然后我只需点击 Load。
- 由于笔记本已经启动,我必须快速重启我的内核以使更改生效。我可以运行整个笔记本。
在这一点上,你可能会说:“这很有趣,但你可以只做一个 pip 安装”。
当然,特别是这个基本的例子,这也是我们习惯做的,特别是在本地环境中(相对于共享环境)。但是这种替代方法带来了有趣的功能,所有这些功能都不需要创建特定的容器映像:
- 提供人们无法安装的应用程序,因为它们可能没有 Conda 或 Pip 包。在这种共享环境中,人们不应该能够 yum/apt-get 安装任何东西,因为他们不应该有 root 访问权限。我希望您的环境也是如此…
- 测试不同版本的包/应用程序变得非常容易。只需加载/卸载相关的模块,不需要做长时间或复杂的安装/卸载或玩弄依赖关系。
- 您可以提供许多不同版本的包或应用程序,用户可以根据自己的需要选择他们想要的。即使几年后,这些模块仍然可用,不需要删除它们,因为环境已经升级。相反,您可以提供最先进的模块 alpha 版本,而不会将整个环境置于危险之中。
- 用户的环境保持整洁,不会因为安装的库而变得杂乱。此外,他们不必在每次想要运行特定笔记本电脑时都进行所有这些安装。模块加载/卸载速度更快,因为事实上所有内容都已经装载到 pod 中。
- 对于系统管理员来说,生活变得简单多了。只有一个容器映像需要管理。这就是为什么这个项目被称为开放数据中心-汉兰达,“只能有一个!”。
但是等等,还有更多!
在第一个基本示例中,我加载了一个 Python 包。但正如我之前所说,它也可以是一个完整的应用程序,甚至是一个拥有自己的 Web UI 的应用程序,如 RStudio-Server。为了实现这一点,我将使用 jupyter-server-proxy 和一些特殊的配置,使它在模块加载时出现在启动器中。
加载模块:

加载模块(作者截图)
RStudio 会出现一个新的单幅图块:

新 RStudio 磁贴(作者截图)
当你点击磁贴时,浏览器会打开一个新标签,RStudio 会在几秒钟内启动。

RStudio(作者截图)
这是与以前相同的容器环境,这意味着您可以访问相同的文件、相同的工具……这在您想要混合使用 R 和 Python 脚本时非常有用,反之亦然!
更多关于如何使用它的细节
在下面的报告中,您将找到完整的部署说明和代码,包括一个预编译的模块库,该库易于在您的开放式数据中心环境中部署:https://github.com/guimou/odh-highlander

一旦部署好一切,您就可以这样做了…
在您的 JupyterLab 实例中,您可以访问一个新的扩展“软件”。可用模块列表分为两部分。“特色”模块和完整列表(这只是我在这个演示环境中选择提供的模块):

模块列表(作者截图)
您可以使用过滤器框来搜索特定的模块(只需输入几个字母)。过滤同时发生在两个列表上:

过滤列表(作者截图)
如果你点击一个模块名,一个弹出窗口会给你更多的信息:描述,依赖关系,…

模块信息(作者截图)
要加载模块,请将鼠标悬停在模块上,然后单击“加载”按钮:

模块加载(作者截图)
模块及其所有依赖项被自动加载(本例中为 torchvision):

模块已加载(作者截图)
要卸载模块,请将鼠标悬停在“已加载”列表上,然后单击“卸载”:

卸载模块(作者截图)
模块及其依赖项将被自动卸载。
注意 : Lmod ,模块管理器引擎,跟踪每个模块加载的依赖关系。如果两个不同的模块共享依赖关系,一个模块卸载不会影响另一个模块,它的依赖关系仍然存在。只有当模块不再需要它们时,它们才会被卸载!
高级功能
收集
如果您想要创建一个特定的环境,其中包含一组您想要使用的不同模块,那么不需要每次都从 scracth 重新创建它!您可以简单地加载这些模块,然后创建一个“集合”。下一次,只需双击即可恢复此收藏。
要创建一个集合,请加载您想要的模块,单击图标并为集合命名。

创建收藏(作者截图)

命名并保存收藏(作者截图)
当您想要恢复这个环境时,只需单击恢复图标,然后选择并加载您的集合。

还原收藏(作者截图)

还原收藏(作者截图)
进口
您也可以直接从笔记本和脚本加载您需要的模块。要知道要用到哪些模块,可以直接导出相关的 Python 代码!
点击“生成 Python 代码”图标:

生成代码(作者截图)
然后,您可以将完整的代码复制粘贴到您的第一个笔记本单元格或脚本中:

Python 代码(作者截图)
注意:当然,为了在您的笔记本或脚本中工作,您正在使用的容器映像或环境必须是“支持 lmod 的”,并且具有相关模块的库必须可访问/安装到该环境中。
下一步是什么?
如前所述,您将在此回购中找到部署此解决方案的所有资源和说明。在接下来的文章中,我将详细介绍技术实现,尤其是:
- 如何创建一个环境来创建你自己的模块。
- 关于如何使用 EasyBuild 创建模块的说明和示例。
- 如何创建一个 Lmod 增强的 JupyerLab 容器映像来使用模块库。
- 如何创建特定于硬件的模块(比如支持 cuda,需要 GPU),并使它们只在相关的基础设施上可用。
多谢
我必须感谢许多人和组织,因为我在这里没有发明任何东西。我只是把一些项目组合起来,把一些解决方案从一个世界移植到另一个世界,瞧!
- Open Data Hub 团队,感谢他们创建了这个奇妙的数据科学环境,它非常容易在 OpenShift 上部署。
- EasyBuild 社区当然感谢他们创造的伟大工具,但也感谢他们的热烈欢迎(感谢 Kenneth!),以及愿意回答我的 dum 新手问题(谢谢马克西姆!).
- Compute Canada ,是谁让我发现了这种解决方案。他们的设置是病态的,尤其是他们在整个组织中分配模块的方式,真了不起!
- CMD-NTRF 、费利克斯-安托万·福廷,出色的 Jupyter-Lmod 扩展,通过 Jupyter 中流畅的用户界面将解决方案提升到另一个层次。
参考和链接
- 回购与指令:https://github.com/guimou/odh-highlander
- 启用 Lmod 的 JupyterLab:https://github.com/guimou/s2i-lmod-notebook
- 各种 EasyConfigs(如果你想看看哪些模块已经可用):https://github.com/easybuilders/easybuild-easyconfigs
- 各种定制简易配置:https://github.com/guimou/odh-easyconfigs
- 开放数据中心文档:【http://opendatahub.io/docs.html
- EasyBuild 文档:【https://docs.easybuild.io/en/latest/
- 加拿大计算机展上的 EasyBuild 展示:https://www.youtube.com/watch?v=eQ1_iWSnJaI
利用机器学习和行为心理学开启电子商务增长
对 100,000 名在线购物者进行细分和评分预测,以及这对企业意味着什么

rupixen.com在 Unsplash 上拍照
问题是
营销人员和研究人员长期以来一直使用人口统计学和心理统计学来进行客户细分。不可否认,这些信息对于满足客户需求至关重要。然而,随着 insights 实践的发展,我们现在知道基于购买行为的细分更加有效,因为它会带来更好的广告支出回报和更高的转化率。
还有一个心理学基础解释了为什么行为细分可能会带来更好的洞察力——诺贝尔经济学奖得主丹尼尔·卡内曼发现,购物者倾向于使用“系统 1”思维(快速&直觉)而不是“系统 2”(缓慢&深思熟虑)。
在阅读了最近频率货币(RFM)分割之后,我想在一个大型数据集上试验这种方法,并探索它能揭示什么样的洞察力。
简单地说,这是我的发现——
- K-means 聚类有助于根据行为确定购物者细分,从而揭示业务的直接机会。
- 最高价值客户的评价最差,通常位于马拉尼昂,在 Q2 购物最多。相比之下,价值最低的顾客会留下最好的评价,他们往往位于圣保罗,在 Q1 购物最多。
- 我能够以 93%的准确率预测客户群,以 94%的准确率评估得分。
数据
Olist 是巴西最大的电子商务网站。它把全国各地的小零售商联系起来,直接卖给顾客。从 2016 年到 2018 年,该公司在其网站上慷慨地分享了一个包含 11 万份订单的大型数据集。
SQL 风格的关系数据库包括站点中的客户及其订单,其中包含大约 10 万个不同的订单和 73 个类别。它还包括与订单相关的商品价格、时间戳、评论和地理位置。这是真实的匿名商业数据。
方法论
RFM 是一种用于分析客户行为的数据建模方法。它代表—
最近度衡量客户最近一次订购到今天的时间(以天为单位)。
频率衡量客户的订单总数。
货币是他们在这些订单上花费的平均金额。
通过这三个镜头对客户进行细分,我们可以精确定位以特定方式行事的客户群,并创建人物角色,这反过来会带来更好的营销效果。RFM 分析可以帮助企业找出挑战和机遇,关注哪里,做什么。
我使用 K-means 聚类(无监督机器学习)来确定 RFM 聚类,然后给每个客户分配一个总体分数——将他们分为高价值、中价值和低价值。
#creates a generic user dataframe to keep CustomerID and new segmentation scores
df_user = pd.DataFrame(df['customer_unique_id'])
df_user.columns = ['customer_unique_id']#gets the max purchase date for each customer and create a dataframe with it
df_max_purchase = df.groupby('customer_unique_id').order_purchase_timestamp.max().reset_index()
df_max_purchase.columns = ['customer_unique_id', 'MaxPurchaseDate']#we take our observation point as the max purchase date in our dataset
df_max_purchase['Recency'] = (df_max_purchase['MaxPurchaseDate'].max() - df_max_purchase['MaxPurchaseDate']).dt.days#merge this dataframe to our new user dataframe
df_user = pd.merge(df_user, df_max_purchase[['customer_unique_id','Recency']], on='customer_unique_id')df_user.head()

作者图片
为了验证聚类的数量,我使用了 K 均值聚类的“肘方法”。
这种方法估计 K 的最佳值。使用下面的可视化,“肘部”计算失真下降的点,或者换句话说,如果图形看起来像一只手臂,肘部就是前臂开始的地方。

[作者图片]使用肘方法,我们可以确定最近有 5 个理想的集群
在我将同样的方法应用于频率和货币之后,我根据客户的总体聚类分数对他们进行了细分。分数低于 3 分为“低值”,介于 3-6 分为“中值”,高于 6 分为“高值”。
另一种方法是分配权重或进一步分割线段,但我决定在这个分析中保持简单。
#assigning value labels to segments
df_user['Segment'] = 'Low-Value'
df_user.loc[df_user['OverallScore']>3,'Segment'] = 'Mid-Value'
df_user.loc[df_user['OverallScore']>6,'Segment'] = 'High-Value'

[图片由作者提供]虽然只有 10%的客户群,但是高价值客户占了营收的 50%!
从机器学习的角度来看,这是一个多类分类问题。价值段的预测模型结果相当准确,随机森林模型的 F1 得分达到 93%。

[图片由作者提供]最终模型:随机森林| F1 评分:93%
洞察力
探索这 3 个部分被证明是非常有用的,并且差异是明显的。每个细分市场都有不同的购买模式、地点和主要产品类别。这些信息可以告知营销团队如何为这些客户制定最佳策略。

[作者图片]高价值客户有明确的品类偏好,例如推动 2 月份电脑配件销售

[图片由作者提供]3 个细分市场的宏观人物角色
也许最有趣的区别是 3 个部分如何离开评论。最高价值的客户负责最差的评论,这对公司来说是一个巨大的危险信号。对于任何电子商务生态系统来说,评论对于培养买卖关系至关重要。因此,预测客户评论分数并了解是什么推动了积极的评论是合乎逻辑的下一步。

[图片由作者提供]高价值客户留下的平均评论分数仅为 3.2
这让我掉进了预测顾客评论的兔子洞。评论被二进制化为 0 表示负面(1-3),1 表示正面(超过 4)。总体分布似乎更偏向积极阶层。

作者图片
在尝试了几个不同的机器学习模型后,我用 94%的 F1 分数预测了客户满意度,并找到了两个决定正面或负面评价的关键因素-

[图片由作者提供]最终模型:XGBoost | F1 评分:94%
满意度的最大预测因素是实际交付日期和预计交付日期之间的差异。从本质上讲,如果订单比预期提前到达,客户会更满意,如果订单比预期晚到达,客户会不高兴。从认知心理学的角度来看,这可以通过锚定启发式来解决——例如,Olist 可以为预期的交付时间建立一个缓冲窗口,从而导致更高的机会以比预期更早的交付来取悦购物者。
产品描述的长度是第二大预测因素——这意味着描述越长的产品往往会获得更多正面评价。这意味着管理购物者的期望有助于推动正面评价。我们可以再次利用行为偏差来“推动”购物者的行为——这是应用框架效应的绝佳机会。例如,Olist 可以与卖家合作,帮助提供更好(更长)的描述,这可能会带来更好的客户评论。
警告
虽然这是一个迷人的项目,具有明显的商业含义,但我可以想到一些进一步改进的方法—
- 数据仅限于样本。10 万订单只是 Olist 实际业务量的一小部分。如果所有的数据都是可用的,那将会更有洞察力(也更费计算)。
- 人口统计信息(年龄、性别等)的附加层将允许对客户细分进行更深入的分析。
最后的想法
如果你想看的话,可以在我的 GitHub 上找到完整的项目(在那里,我还使用寿命库和 beta geo fitter/负二项式分布模型研究了客户流失和客户终身价值,并创建了一个产品推荐引擎)。
我还发表了一个关于场景故事的宏观观点。
用多语言开启包容性的可能性-NER
思想和理论
海量多语言命名实体识别基础

通过 Unsplash 由 Nick Fewings 拍摄的图像
简介
近年来,命名实体识别(NER)技术发展迅猛,新的模型不断出现。这是一个激动人心的地方,值得观看,也是其中的一部分。就在最近,我的同事 Sybren Jansen 和 Stéphan Tulkens 探索了生物医学命名实体识别(BioNER)的优势和局限性,作为一个例子。
然而,在利用 NER 能力构建值得信赖的人工智能时,仍然存在一个重大挑战:如何适应世界上的多样化语言。绝大多数 NER 模型、训练集、数据、开源代码等等都是英文的。然而,在全球近 80 亿人口中,只有 3.75 亿人的母语是英语。NER 盛行的英语偏见实际上是将世界上相当一部分地区排除在这一重要的技术进步之外。
多语言 NER 模型提供了语言之间的桥梁的可能性。
在这第一篇博文中,我将进一步探讨多语言模型的重要性,并更详细地探讨特定的多语言模型。在后续的文章中,我将更深入地研究测试和结果。
输入:多语
据估计,世界上有 7000 种活跃的语言。[1] NLP 专家通常将语言分为所谓的低、中和高资源语言。高资源语言是这样一种语言,其中有许多不同体裁的在线标记和未标记文本,如英语。低资源语言是一种数据非常少的语言;例如,约鲁巴语。中等资源语言介于这两种类型之间。
在 NLP 中,大多数研究是在英语和其他高级资源语言上完成的。因此,从语言技术中获益是不对称的。[2]
有两个常用术语用来描述可用于不同语言的模型:多语言和多语种。在这一领域,专家们也使用“跨语言学习”这个短语,这是将一种语言的知识应用到另一种语言的过程。这种技术用于训练多语言模型,可以用来在资源较少的语言上表现得更好。
命名实体识别(NER)
我们将通过 NER 的子任务探索多语言模型。在这项任务中,模型必须检测文本中哪些标记引用了命名实体(NE)。NE 的定义因域而异,但 NE 最简单的定义是一个令牌或一组令牌,它指的是现实世界中的特定事物。然而,这并不完全准确,因为“哈利·波特”这个角色也是一个 NE,但在现实世界中并不存在。
没有必要对 NE 有严格的定义,因为哪个令牌属于 NE 将因域和数据集而异。例如,在医学领域中,像肺炎这样的特定情况可以被认为是 NE,但是在商业领域中,它不会被认为是 NE。
当处理多种语言时,NER 任务变得更加困难,因为不同的语言对于如何指示 NE 有不同的约定。例如,在英语中,句子中间以大写字母开头的单词很可能是 NE。在德语中,这不太可靠,因为所有名词都以大写字母开头,但不是每个名词都是 NE。此外,有许多语言不使用拉丁字母,也不区分大写字母和小写字母。语言中表示 NE 的这些不同约定对于多语言领域来说是额外的挑战。
应用 NER:匿名化
能够识别 100 种不同语言的命名实体不同于用 100 种不同语言进行对话。然而,这仍然是一项有用且令人印象深刻的技能。匿名化领域可以通过使用 NER 模型获得显著的价值。随着开放数据趋势的增加[3],匿名化扮演了一个关键角色,因为文档在发布前通常需要匿名化。此外,公众对透明政府的要求越来越高,政府官员提出了对隐私的关切。透明度和隐私之间的权衡是一个合理的考虑。通过 NER 的匿名化,可以帮助消除交易的负面影响。
还有几个其他的用例可以从 NER 文档匿名化中受益。欧盟(EU)对法院和医疗文件的匿名化表示了兴趣,并资助了 MAPA 项目 [4]。该项目旨在创建一个工具,对欧盟所有 24 种官方语言的医学和法律文本进行匿名化处理。这个项目仍处于开发阶段。
当前可用的型号
今天有几种型号可供使用。首先,已在 LENER-BR 数据集上训练了由 Luz 等人[5] 建立的单语巴西葡萄牙语模型。这个数据集是为了训练这个模型而专门开发的。LENER 代表合法命名实体认可,BR 表示该名称在巴西葡萄牙语中。LENER-BR 数据集由 66 份法律文件组成,这些文件是用特定法律实体手动注释的。这些实体不同于标准命名实体,如个人、组织和位置。虽然这些类型的实体也包括在内,但数据集包括对特定法律实体和判例的注释,总共有 6 个类别。基于这些数据训练的模型是基于 LSTM 的 CRF,并使用单词嵌入作为输入。在他们的论文中,他们报告了 6 个班级的平均 92.5 分。
此外,还提供了一种由connaau 等人命名为 XLM-R 的多语种模型供使用。[6] XLM-R 是基于 Transformer [7]的模型,是 XLM [https://arxiv.org/pdf/1901.07291.pdf 8]和罗伯塔[9]的组合。XLM-R 使用掩蔽语言建模方法接受了 100 种语言的培训。下图显示了所包含的语言和每种语言的数据量。

表 1:XLM-R 模型训练数据,取自原论文[6]
XLM-R 模型已经在四个任务上进行了评估,其中之一是 NER。虽然 XLM-R 模型是在 100 种语言上训练出来的,但原始的论文只报告了四种语言的 NER 分数:英语、荷兰语、德语和西班牙语。这些结果已在 2003 年 CONLL NER 数据集上报告。[10]这令人失望,因为在其他任务中,该模型表现良好,即使在资源较少的语言中也是如此。看到 NER 在更多语言上的表现会很有趣。

表 2:XLM-R 在 NER 的性能与其他模型的比较取自原 XLM-R 论文[9]
在上表中,我们看到了原始 XLM-R 文件中关于 NER 的结果报告。多语 BERT (M-BERT) [11]是最成熟的多语种模型之一。M-BERT 用于比较 XLM-R 在原始论文中的表现。前两个模型被用作基线,都是单语模型。对于这四种语言,我们可以看到 XLM-R 模型在所有语言上都优于 M-BERT,在 4 种语言中的 2 种语言上优于单语基线。
幸运的是,我们可以超越这四种语言,因为 Plu [12]采用了 XLM-R,并在 40 种语言上进行微调,并在所有语言上进行 NER 实验。它可用于拥抱脸。[12]该模型在 PAN-X 数据集[13]上进行了微调,该数据集也称为 Wiki-Ann 数据集,它利用了维基百科的数据。在维基百科上,有超过 295 种不同语言的文章。手工标记所有这些页面将是一项非常耗费资源的任务。手工标签被认为是黄金标准。潘等人[13]通过利用维基百科页面标记,使用维基百科页面创建了所谓的银标准命名实体语料库,命名实体通常链接到其他页面。根据语言的不同,性能会有很大的差异,但总体而言,该模型表现良好:对于许多语言,F 值接近 0.9。令人印象深刻的是,这 40 种语言来自非常不同的语系。
让 XLM-R 更进一步的实验
我们已经看到了来自 XLM-R 的令人兴奋的结果。但是有两种方法可以将这种模式进一步发展,以获得更大的收益来执行 NER:
- 领域变更:XLM-R 模型只在三个 NE 类上训练:人员、组织、位置。虽然 XLM-R 已经表明自己能够处理不同的语言,但我们还不知道它是否能够处理不同的领域。当我们在没有任何额外改变的情况下切换域时,看看结果会是什么将会是有趣的。这可能是因为该模型非常强大,可以开箱即用地跨领域应用。这将使它非常容易接近。
- 匿名化:在这篇文章的开始,我们介绍了匿名化任务及其重要性。好消息是,我们可以通过将任务转换为二元任务,将 3 类模型应用于 6 类数据集。也就是预测某个东西是不是 NE。
如果您想匿名化一个文档,您想编校文档中的每一个 NE。然而,在某些情况下,控制哪种类型的实体需要匿名,哪种类型的实体需要保留并进行实验可能是好的;对于被认为是 NE 的一部分的令牌,预测 1,对于不是 NE 的一部分的令牌,预测 0。通过这种方式,我们可以将 XLM-R 模型应用于这个数据集。
在这样的实验中,有两件事需要测试:二值化的效果和 XLM-R 模型是否也可以在跨域设置中工作。为了将单语模型与二进制化的 XLM-R 进行比较,单语模型也应该以二进制方式进行转换。
总结和下一步行动
我们现在已经奠定了多语言 NER 的基础,作为构建更具包容性的自然语言处理的关键技术。在下一篇博文中,我们将看到二进制域转换实验的结果。你认为结果会是什么?你还希望看到什么样的实验?我很想收到你的来信!
如果你想了解更多关于更苗条的人工智能和我们人工智能创新团队的最新研究,请看:【https://medium.com/slimmerai/innovation/home】T2。
参考文献
[1]世界上有多少种语言?(2021 年 2 月 23 日)。从 https://www.ethnologue.com/guides/how-many-languages取回
[2]p . Joshi,s . Santy,a . Budhiraja,k . Bali 和 m . Choudhury(2020 年)。NLP 世界中语言多样性和包容性的状态和命运。arXiv,2004.09095。从 https://arxiv.org/abs/2004.09095v3取回
[3]欧洲联盟出版物处。,凯捷发明。,&欧洲数据门户。(2020).开放数据和隐私。出版办公室。https://doi.org/10.2830/532195
[4]Mapa——公共管理的多语种匿名化。(2021 年 6 月 03 日)。检索自https://mapa-project . eu
[5] Luz de Araujo,P. H .,de Campos,T. E .,de Oliveira,R. R. R .,Stauffer,m .,Couto,s .,& Bermejo,P. (2018 年)。LeNER-Br:巴西法律文本中命名实体识别的数据集。葡萄牙语的计算处理。斯普林格。doi:10.1007/978–3–319–99722–3 _ 32
[6] Conneau,a .,Khandelwal,k .,Goyal,n .,Chaudhary,v .,Wenzek,g .,Guzmán,f .,…Stoyanov,V. (2019)。大规模无监督跨语言表征学习。arXiv,1911.02116。从 https://arxiv.org/abs/1911.02116v2取回
[7] Vaswani,a .,Shazeer,n .,Parmar,n .,Uszkoreit,j .,Jones,l .,Gomez,A. N .,…Polosukhin,I. (2017)。你需要的只是关注。arXiv,1706.03762。从 https://arxiv.org/abs/1706.03762v5取回
[8]兰普尔,g .,&康瑙,A. (2019 年)。跨语言语言模型预训练。arXiv,1901.07291。从 https://arxiv.org/abs/1901.07291v1取回
[9]刘,y .,奥特,m .,戈亚尔,n .,杜,j .,乔希,m .,陈,d,…斯托扬诺夫,V. (2019)。RoBERTa:稳健优化的 BERT 预训练方法。arXiv,1907.11692。从 https://arxiv.org/abs/1907.11692v1 取回
[10]桑,E. T. K .,& De Meulder,F. (2003 年)。CoNLL-2003 共享任务简介:独立于语言的命名实体识别。ACL 选集,142–147。从https://www.aclweb.org/anthology/W03-0419取回
[11] Devlin,j .,Chang,m-w .,Lee,k .,& Toutanova,K. (2018 年)。BERT:用于语言理解的深度双向转换器的预训练。arXiv,1810.04805。从 https://arxiv.org/abs/1810.04805v2取回
[12]jplu/TF-xlm-r-ner-40-郎抱脸。(2021 年 6 月 22 日)。从 https://huggingface.co/jplu/tf-xlm-r-ner-40-lang取回
[13]潘,x .,张,b .,梅,j .,诺斯曼,j .,奈特,k .,&纪,H. (2017)。282 种语言的跨语言姓名标记和链接。ACL 选集,1946-1958。doi:10.18653/v1/P17–117
用数据释放风能的潜力
调查加利福尼亚风能的案例研究

全球变暖严重威胁着子孙后代的福祉以及他们与这个星球互动的能力。虽然有许多行业需要改变协议来帮助应对气候变化,但其中最关键的是能源部门。电力让我们开发出令人难以置信的技术,并将我们与全球联系起来。然而,它也是温室气体排放的主要来源。
“根据美国环保署 2018 年进行的一项研究,电力生产产生了美国温室气体总量的 26.9%。”[1]
可再生能源在减少这一数字方面有很大作用。
可再生能源有很多种,其中一种是风力。风力是通过使用大型涡轮机来利用风的动能而产生的。风力使连接在发电机上的大型涡轮机旋转并发电。这些涡轮机可能非常大,高达 80 多米。一般来说,这些涡轮机建在风力发电场中,这些风力发电场包含大量相互靠近的涡轮机。
在本帖中,我们将关注北加州大风项目的风力发电,该项目是我在斯坦福大学天气和风暴课程项目的一部分。这篇文章的目的是给出相关数据的一般概念,以及如何用它来估算风力。我还想展示一些数据的大致数量,比如一个典型风力发电厂的发电量。让我们了解一下加州风力发电的现状:
2018 年,加州风能项目发电量为 15,078 千兆瓦时,占总发电量的 7.2%。[2]
W帽子是风?你知道实际上有不同类型的风吗?在课堂上,我们学习了四种主要类型:地转、梯度、沿直线等压线的表面和沿曲线等压线的表面。每种风的驱动力组合都不同。例如,沿圆形等压线作用的地面风有科里奥利力、压力梯度、摩擦力和视在离心力的作用。

沿圆形安索帕[3]的地面风力
一个地区的地形对该地区产生的风力也有影响。在北加州,特别是旧金山湾区,地形多山,靠近海边。在海边,当陆地在白天变暖时,你可以感受到陆风,风从陆地吹向海洋。相反,在晚上,当陆地更冷时,你可以得到海湾微风,风从海湾吹到陆地上。压力和温度对大气中的风的形成起着很大的作用。
T 这里的是两种主要类型的风力发电机:垂直轴和水平轴。垂直轴风力涡轮机的轴垂直于气流,而水平轴风力涡轮机的轴平行于气流。一般来说,生产中的大多数风力涡轮机是水平轴的。
仔细观察水平轴涡轮机发电的机械结构,这些结构通过大型螺旋桨状叶片利用风能。通常有三个钢制叶片安装在一个大的塔上。叶片安装在高处,以利用更高海拔处更快的风速。吹过涡轮机的风在叶片的一侧产生低压,从而产生使叶片旋转的升力。由于螺旋桨叶片的惯性质量较大,轴的转速通常为 5-20 转/分。为了产生交流电,螺旋桨叶片的轴连接到齿轮箱,齿轮箱将转速提高到 1800 rpm。
由 El-Ahmar 等人进行的一项研究。al (2017)调查了风力发电的最关键因素,并设计了以下列表:风速、空气密度、转子扫掠面积和塔架高度[4]。这些部件中的每一个都对功率输出产生不同的影响。如你所料,发电量与风速直接相关(即风速越大,发电量越大)。事实上,下面的等式通常用于测量涡轮机的潜在功率输出。我们将利用这一进展来计算功率。

D 如前所述,High Winds 项目选自 USGS 风力涡轮机数据库进行分析,并收集了 2018 年的数据,时间范围为 2018 年 1 月 1 日至 2018 年 12 月 31 日[5]。该项目始于 2003 年,位于费尔菲尔德市东南的海湾东北部。该项目包含总共 90 台额定功率为 1.8 兆瓦的 Vestas V80–1.8 涡轮机,总容量为 162 兆瓦。每台涡轮机高 60 米,转子直径 80 米。下图突出显示了 90 台涡轮机的位置。

加州北部的大风项目[5]
除了单个涡轮机信息之外,还从加州能源委员会收集了功率输出汇总信息[6]。
气候信息是从美国国家海洋和大气管理局(NOAA)的全球预报系统(GFS)大气模型中收集的。地球观测卫星系统覆盖全球,分辨率为 28 公里。在此分析中,考虑了以下变量:时间、地表温度(K)、2m 处的温度(K)、向东风速(m/s)、向北风速(m/s)、2m 处的相对湿度(%)和海平面气压(Pa)。再次收集 2018 年的数据,中心坐标约为(38,-122)。
我找到了什么?根据这一分析,该农场将在 2018 年开工不足。该分析中有一些假设,例如使用简化的风力模型,这可以解释比风力涡轮机通常发现的运行能力略低的原因。在基于风速的预期输出和总容量之间进行了比较。输出和容量用于单台涡轮机。在下图中,红线代表容量,蓝线代表当天风速下的功率输出。从图中可以看出,大多数日子都远远低于容量。

相对于容量的涡轮机输出
创建实际容量百分比分布有助于正确看待之前的数字。从下图可以清楚地看到,2018 年的涡轮机在远低于容量的情况下运行。大部分日子都在 10%或以下。

容量百分比分布图
除了运行容量,还计算了风向趋势。下图总结了 2018 年的风向。360 度罗盘被分成 45 度的相等部分,并且在该范围内的任何数据点被分配相应的罗盘方向。从结果中我们可以看出,风主要来自北/东方向。一个可能的解释是地形,这解释了暗黑风现象。在海湾的东北部,山脉之间有一个缺口,这可以让山风发展并吹向旧金山。

2018 年风向直方图
除了风向之外,速度(绿色)、温度(黄色)、压力(紫色)、相对湿度(蓝色)和功率(红色)都标在了一起。下图显示了 2018 年全年绘制的这些变量。

2018 年气候变量时间序列
要全面了解这种方法,请查看本文底部 github 上的最终报告和 jupyter 笔记本
R可再生能源每年都在增长,如果我们要达到抑制全球变暖的排放目标,就需要继续增长。风能已经并将继续成为解决方案的重要组成部分。
我希望这能让我们更好地了解预测风力发电场发电量的复杂性。虽然这种分析的简单性有些随意,但它显示了使用天气变量预测风能产量可以获得多少洞察力。
为了获得更好的结果,可以使用更复杂的风模型来获得每个涡轮机位置的速度和方向。可以在更长的时间内收集数据,以更好地了解历史趋势并预测产量何时会高或低。随着机器学习的进步,预测这些变量变得更加精确,这可以在需求规划中发挥作用。
Github 完整报告和代码:
https://github.com/jackseagrist/Wind_Available_for_Energy_Production
参考资料:
[1]—[https://www . EPA . gov/GHG emissions/sources-green house-gas-emissions #:~:text = Electricity %20 production % 20(26.9% 20 percent % 20)of,主要是% 20 煤炭% 20 和% 20 天然气% 20。](https://www.epa.gov/ghgemissions/sources-greenhouse-gas-emissions#:~:text=Electricity production (26.9 percent of,mostly coal and natural gas.)
[2] —加州能源委员会,能源年鉴,全系统发电(2018)。(见https://www . energy . ca . gov/data-reports/energy-almanac/California-electricity-data/2018-total-system-electric-generation。))
[3] —丹尼尔·a·瓦列罗,5 —大气物理学,编者:丹尼尔·a·瓦列罗,《空气污染基础》(第四版),学术出版社,2008 年,第 123–153 页,ISBN 9780123736154,【https://doi.org/10.1016/B978-012373615-4/50006-6】T4。(http://www . science direct . com/science/article/pii/b 9780123736154500066)
[4] — M. H. El-Ahmar,A. M. El-Sayed 和 A. M. Hemeida,“影响风力发电机组输出功率的因素评估”, 2017 年第十九届国际中东电力系统会议(MEPCON) ,开罗,2017 年,第 1471–1476 页,doi:10.11109/MEPCON . 2013105
[5]—https://eerscmap.usgs.gov/uswtdb/
[6]—https://WW2 . energy . ca . gov/almanac/renewables _ data/wind/index _ CMS . PHP
利用 ModelOps 释放人工智能在商业应用中的价值

图片来自 Canva
人工智能正迅速成为商业和 IT 应用和运营的关键。
多年来,各组织一直在投资人工智能能力以保持竞争力,正在雇用最好的数据科学家团队,并在人工智能和机器学习系统上投入了越来越多的资金。然而,实现 AI / ML 模型并不容易,失败的风险就在眼前。需要一种可靠的方法来降低这种风险并使公司取得成功。
什么是 ModelOps,它能解决什么问题
人工智能高管多年来一直致力于在商业中获得更多模型。第一个障碍是雇佣数据科学家和快速创建模型的工具。那个问题已经解决了。下一个障碍是及时、合规地将这些模型投入生产。公司积压了大量闲置和退化的模型,对业务没有任何价值/收入贡献。我们称之为模型债务。或者他们正在努力将他们的第一个人工智能模型投入生产,并发现它比他们预期的要复杂得多,花费的时间也长得多。最重要的是,他们意识到模型,尤其是 ML 模型本质上给整个组织带来了很多风险。董事会明白,如果他们想大规模使用人工智能,他们必须有一些流程标准化和自动化,以及强有力的治理。

作者精心制作的信息图
来自运营部门,并参与了许多数字化转型项目,难怪 ModelOps 是每个人工智能项目的基石。在实验室和生产中,团队之间的思维模式/需求/协作有着巨大的差异。这在企业世界中并不是第一次:软件也发生过类似的事情:软件开发人员的代码不能及时有效地投入生产。DevOps 通过软件帮助我们改善了跨 IT 团队的协作,加快了部署周期,并通过现代软件开发方法提供了更好的体验。以类似的方式,ModelOps 正在帮助企业运营模型(人工智能、机器学习、传统模型)。由于模型会随着时间的推移而漂移,并在企业间带来巨大的董事会级风险,这就更加复杂了。数据科学家使用多种工具和语言构建模型,然后在大多数情况下,模型永远不会在生产中出现,或者即使出现也需要很长时间——有时太多,模型在那个时候就不再有用了。如果将模型投入生产,通常模型在没有适当的监控、控制和整体治理的情况下运行,因此它们并不总是能够发挥应有的作用,并且可能会使整个公司面临多种风险:法规遵从性、声誉等。…这是企业级的风险。
也就是说,我们可以将 ModelOps 定义为文化、政策、实践和工具的组合,这是组织大规模运营 AI、ML 和分析模型的能力的关键,以比现有/自主开发的方法更快的速度发展和改善模型治理和性能。这种速度使组织能够采用人工智能决策来更好地服务他们的客户,保护他们的业务,并在他们的市场上更有效地竞争。
影子人工智能:未来几年组织将不得不应对的关键问题
在早期阶段经常被低估的一个重要方面是,ModelOp 和 MLOps 是截然不同的,彼此是分开的。MLOps 面向数据科学家,而 ModelOps 主要面向首席信息官。事实上,生产中的模型必须 24x7 全天候监控和治理,法规即将出台,而且不仅限于金融服务行业。这是应该由首席信息官组织处理的事情。风险是出现另一种情况,就像我们在影子 IT 中遇到的情况一样——我们可以称之为影子 AI:每个 BU 都在没有标准化的情况下将模型投入生产,我们有一个模型的狂野西部。即使是简单的问题:“有多少生产中的模型?”变得很难回答,更不用说了解生产中每个模型的状态和状况,更不用说与法规遵从性和风险管理相关的问题了。
如果我们想到影子 IT,它不一定是坏的,因为它刺激了创新。当然,首席信息官组织必须控制它,而不是真正消除它。我们已经看到了很多问题,我在最近的文章中提到了这个问题,即组织仍然将模型视为业务部门级别的一些资产,即使在生产中也属于业务部门和数据科学家,而不是像 it 组织管理的许多其他共享服务一样,作为应该集中管理的企业资产。这是一个需要的重大思维转变。起点是理解 ModelOps 必然与数据科学分离开来。实验室和生产应该像教会和国家。不应要求数据科学家兼任运营资源,因为他们既没有带宽,也没有技能,也没有兴趣管理确保正常运营的 24x7 复杂模型生命周期。
首席信息官应在首席风险官的帮助下加快步伐,实施模型操作。
没有合适的模型操作能力的公司面临的潜在风险
在最近的一份报告中,Forrester 宣称“如果没有 ModelOps ,你的人工智能转型注定要失败”。ModelOps 当然是企业部署、监控和治理 AI/ML 模型所需的核心能力。不决定采用 ModelOps 的公司可能很难跟上竞争,当然也不会超越或领先。
良好的治理绝对需要严格的政策、实践和工具来实施控制,并跟踪可审计性、风险管理和法规遵从性的步骤和变化。做不好模型操作的风险是不可靠的业务决策(影响收入)、监管和合规处罚以及导致巨额财务和品牌成本的潜在违规。
这不仅适用于金融服务等高度监管的行业。根据《福布斯》上 Stu Bailey 的文章,人工智能正被用于在广泛的业务和行业中实现自动化决策,新的或许不太明显的漏洞正在出现。考虑这些场景:
- 一家使用人工智能驱动产品设计决策的制造商发现,一个机器学习模型出现了错误,导致了严重的客户安全问题。为了抵御责任,该公司被要求证明其人工智能模型得到了适当的开发和维护。
- 一家大型零售商的人力资源部门使用第三方软件工具来评估员工,并做出薪酬和职业发展决策。该工具整合了一些功能,允许公民数据科学家(在这种情况下,是一名没有数据科学或风险管理背景的人力资源经理)创建和部署人工智能模型,以评估员工并确定工资、奖金和晋升。该模型的部署和运行没有任何风险管理或合规团队的参与。一名没有获得晋升的员工声称,人工智能模型存在偏见,就不公平的劳动做法提起诉讼,并要求公司证明该模型是公平的”。
此外,贝利认为,“随着人工智能越来越广泛和常规化,这些只是任何类型的公司都可能很快面临的一些现实挑战。据 Gartner 预测, 到 2024 年,50%的 AI 用例 将接受风险评估,到 2022 年,15%的应用领导者将面临 AI 故障的董事会级调查。显然,是时候关注 AI 合规性了”。
组织应该如何开始做模型操作
对分析和数据科学团队:尽早让您的 IT 部门和风险部门参与进来,因为他们会帮您解决运营方面的问题,这将节省成本,并避免整个公司面临不必要的风险。
利用 ModelOps 平台自动化和标准化生产模型的模型生命周期设计流程。他们中的大多数都有乐高积木,可以缩短生产时间,例如开箱即用的工作流程、控制、监控和补救预设计流程。

选择 ModelOps 平台的另一个重要方面是尊重数据科学家和业务用户的需求:ds 平均使用 3 种模型创建工具/语言,这个数字只会增加,工具会不断地分阶段使用和淘汰。模型在内部、云中运行,并为多个分散的应用程序提供服务。这种“多对多”的情况需要一个不可知的 ModelOps 平台。
最后的想法
模型操作是大规模操作人工智能的必备能力。它包括工具、技术和实践,使组织能够在生产应用程序中部署、监控和治理 AI/ML 模型和其他分析模型。模型运算不仅仅是移动比特。部署模型并不仅仅是提供基础设施和复制代码。机器学习模型是独一无二的,因为它们必须在生产过程中不断受到监控,并定期进行再培训,这需要从数据科学家到运营专家等众多利益相关者的协作。
组织应该开始理解为什么模型操作是重要的,他们需要的能力,以及如何开发或获得它们。
关注我的每日技术和创新更新
参考
ModelOp,“2021 年 ModelOps 状态”报告。2021 年 4 月 15 日
Forrester,“引入人工智能的模型运算”,Kjell Carlsson 博士和 Mike Gualtieri,2020 年 8 月 13 日。
人工智能信任、风险和安全管理市场指南。2021 年 9 月,阿维瓦·谭力,法尔汉·乔杜里,杰瑞米·德霍因
用 Spray 将 ERDDAP 数据解组到 Akka/Scala 中
实践教程
科学数据唾手可得。有一种方法可以将它吸收到你的 Akka/Scala 项目中。
ERDDAP 是 NOAA 开发的工具,以可扩展的格式提供科学数据集,供公众广泛使用。如果您以前曾经处理过科学数据,您就会知道处理这些数据极具挑战性,因为这些数据格式是为非常特定的科学用途而构建的,具有复杂的要求,例如在多维数组中工作,并且通常是使用 Fortran 之类的计算机编程语言构建的,这些语言在科学世界之外很少使用。由于像美国宇航局或欧洲核子研究中心这样的长期组织不太可能在一夜之间改变他们的做法,所以 ERDDAP 是对公众的一种令人惊叹的服务,即使它在美学方面有一点缺乏。它吸收各种常见科学数据格式的数据,并以各种有用的格式提供易于访问的 API,以便于使用。许多组织使用 ERDDAP 来处理和分发他们的数据,以实现开放访问(和其他)的目的。
基于 ERDDAP 的数据服务中的大多数链接都指向 ERDDAP 表单,这本身就有点吓人。

ERDAP 表单——有点难看,但是考虑到您可以从 ERDAP 服务器提取的数据量,这种难看是可以原谅的。
默认的提交屏幕将为您提供一个 html 表格。我认为您可以尝试解析它,但是使用原始的 JSON 数据更有意义。您还可以使用许多其他格式,包括 MATLAB 二进制文件和 netcdfs。要访问 json 数据,只需更改文件类型。

获取一些好的旧 json 数据!
我们现在将使用普通的 JSON,但是我们也将查看。jsonlKVP(键值对流)。如果运行 submit,ERDDAP 会将 json 下载到您的计算机上——但是如果您想从服务器或以编程方式访问数据呢?为此,您可以使用“只生成 URL”服务。或者,您可以学习erd DAP 如何生成 URL 并完全跳过表单过程。

也许根本没有必要使用这个表单。
为了这项工作,我决定从加拿大渔业和海洋部收集数据——这是一个位于加拿大新斯科舍省普尔湾的水温和压力计。的。json 输出如下所示:
{
"table": {
"columnNames": ["station_name", "time", "water_pressure", "water_temperature", "battery_voltage", "latitude", "longitude"],
"columnTypes": ["String", "String", "float", "float", "float", "double", "double"],
"columnUnits": [null, "UTC", "mbar", "degree_C", "V", "degrees_north", "degrees_east"],
"rows": [
["POOLC_20200929", "2020-09-29T15:18:51Z", null, null, 0, 47.679933, -55.43001667],
["POOLC_20200929", "2020-09-29T15:18:51Z", null, null, 0, 47.679933, -55.43001667],
["POOLC_20200929", "2020-09-29T15:18:51Z", null, null, 0, 47.679933, -55.43001667], [...] ["POOLC_20200929", "2020-09-29T16:08:51Z", 83.5167, 14.4, 12.73, 47.679933, -55.42001667] [...]
}
如果你了解 Scala,你可能已经心脏病发作了。你看,与更流行的 Python 不同,Scala 非常关心进出程序的数据类型。作为一种强类型语言,它希望确切地知道正在使用什么数据类型。作为一种面向对象的语言,它希望使用类将数据放入对象中。最后,作为一种相当新的基于 Java 的语言,它会希望尽可能避免空值。
好消息是 Spray 被设计成将所有这些数据类型放入漂亮、干净的类 java 对象中。Spray 的另一个优点是它得到了 Akka 框架的支持。虽然我不会在这里过多地讨论 Akka 的内部工作方式,但主要的要点是,使用 Akka,您可以构建并发操作的流管道,而不会阻塞分布式系统。因此,如果你想获得所有这些可爱的 ERDDAP 数据,并通过一个巨大的消防水管快速可靠地发射出去,Akka 是一个很好的工作方式。Akka 也有一个名为 Alpakka 的姐妹库,它将允许你将这些流与其他服务挂钩,例如与 Apache Kafka 或 Amazon Kinesis 流挂钩,与 Cassandra 或 MongoDB 挂钩,通过像 Lambda 这样的无服务器应用程序,与像 Parquet 这样最新最棒的大数据格式挂钩(这是额尔达普人可能会做的事情)
但是足够证明 Akka,让我们进入编码的过程。我们首先需要一个对象来存储我们所有的类。这需要一些导入。
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import spray.json._
import DefaultJsonProtocol._
第一次导入允许我们在 Akka 代码中使用 Spray。二是喷 json 库本身。最后,spray 中的 DefaultJsonProtocol 库包含了 Json 中可以出现的所有基本数据类型如字符串、数组、对象、整型、浮点型等等。
接下来,我们构建对象并创建一些 case 类来存储数据。
object erddapConstants extends SprayJsonSupport {
//(implicit val will go here eventually)
final case class Root(table: Table) // the root class
final case class Table(
columnNames: List[String],
columnTypes: List[String],
columnUnits: List[Option[String]],
rows: ???) abstract class ErddapDataFormat {} final case class **P**oolCoveSensorRow(
station_name: String,
time: String,
water_pressure: Option[Float],
water_temperature: Option[Float],
battery_voltage: Float,
latitude: Double,
longitude: Double) extendsErddapDataFormat}
该对象扩展了“SprayJsonSupport ”,以确保我们可以将所有可爱的 json 工具翻译成基于 Akka 的命令。我们有一个承载所有数据的根类,然后有一个对应于其余可用数据的表类。幸运的是,我们不必创建对象来处理列表特性,因为 Spray 的 DefaultJsonProtocol 已经为我们做了这些。我们还通过要求 Spray 在遇到空数据时创建一个Option[T]来解决我们的null问题。再一次,DefaultJsonProtocol为我们做了繁重的工作。最后,有一个 PoolCoveSensorRow case 类来处理行数据本身。
然而,我们在弄清楚如何管理这些行时遇到了一个大问题。Spray 不支持构建一个值为Any类型的List。此外,spray 将无法将列表直接封送到 PoolCoveSensorRow 类中,因为没有可供 Spray 用来将行值与 case 类匹配的键值对。我们将在后面讨论这个问题。
首先,我们必须看看 spray 如何自动获取 json,并根据我们的需要将其放入我们的 case 类中。答案是通过包含隐式值来处理数据。同样,Spray 为我们提供了一些 jsonFormat 函数,帮助我们将 json 与 case 类相互转换。我们只需要知道我们的 case 类有多少个参数。
implicit val poolcSensorRowJsonFormat: JsonFormat[**P**oolCoveSensorRow] = jsonFormat7(PoolCoveSensorRow)
implicit val rootFormat: RootJsonFormat[Root] = jsonFormat1(Root)
implicit val tableFormat: JsonFormat[Table] = jsonFormat4(Table)
现在是最后一部分。我们如何处理这些行?我的第一次尝试尝试使用 Spray 的自定义格式化代码。为此,您用一个write和一个read方法构建一个隐式对象。大概是这样的:
implicit object PoolCoveSensorRowFormat extends RootJsonFormat[PoolCoveSensorRow] {
// writing is not so important here as you can simply
// create a JsObject out of the case class.
// How you want to handle it is up to you. def write(item: PoolCoveSensorRow) = {} // do nothing def read(json: JsValue) = {
val jsObject = json.asJsObject
jsObject.getFields("rows") match {
case List[List[***???***] if (some test) => ***(put List items into PoolCoveSensorRow object)***
case _ => ***(handle error)***}
在尝试了许多可能的格式选项后,我决定采用更简单的方法。我们可以将数据整理成 spray 的 JsValue 类型,然后根据需要进行修改,而不是搞乱数据格式。这涉及到更改表 case 类。
final case class Table (columnNames: List[String],
columnTypes: List[String],
columnUnits: List[Option[String]]
rows: List[List[JsValue]) { def poolCoveSensorTest: Boolean = rows(0).length == 7 && rows(0)(0).convertTo[String] == "POOLC_20200929" def convertedRows[T <: ErddapDataFormat]: List[T] = rows match {
case x if poolCoveSensorTest => rows.map({row: List[JsValue] =>
PoolCoveSensorRow(
row(0).convertTo[String],
row(1).convertTo[String],
row(2).convertTo[Float],
(etc)
)
case _ => throw new Exception("Rows did not match any known ErrdapDataSet")})
}
}
}
这种方法的一个好处是,您也可以为其他类型的数据创建测试,然后将测试用例包含在 convertedRows 定义中以进行数据处理。
最后,您只需将这个对象导入到另一个类中进行数据摄取。
**import** akka.NotUsed
**import** akka.actor.ActorSystem
**import** akka.http.scaladsl.Http
**import** akka.stream.scaladsl.{Sink, Source}
**import** akka.http.scaladsl.model.{DateTime, HttpRequest, HttpResponse}
**import** java.util.concurrent.ExecutorService
**import** scala.concurrent.{ExecutionContext, ExecutionContextExecutorService}class ProcessErddapData () {
implicit val system: ActorSystem = ActorSystem()
val execution: ExecutorService = java.util.concurrent.Executors.newSingleThreadExecutor()
implicit val ec: ExecutionContextExecutorService = ExecutionContext.fromExecutorService(execution) import erddapConstants._ // Get source from url
val poolCoveUrl: String = // the url def jsonDataSource(url: String): Future[HttpResponse] = {
val http = Http(system)
http.singleRequest(HttpRequest(uri = url)
} val poolCoveData: Source[PoolCoveSensorRow, NotUsed] = {
val data = jsonDataSource(poolCoveUrl)
.flatMap(resp => Unmarshal(resp.entity).to[Root])
.map(_.table.convertedRows[PoolCoveSensorRow])
Source.future(data).flatMapConcat(iter => Source(iter)) }}
现在,您可以使用poolCoveData值作为流源,以泵入 Kafka producer,映射,与其他数据源合并,过滤,并最终运行 Akka streams 接收器进行输出。
现在有另一种方法可以用来构建流媒体源。除了在 Erddap 中使用 json 格式,还可以使用 Jsonl 格式,它只是一组由换行符分隔的键值对。看起来是这样的:
{"station_name":"POOLC_20200929", "time":"2020-09-29T15:18:51Z", "water_pressure":null, "water_temperature":null, "battery_voltage":0, "latitude":47.679933, "longitude":-55.43001667}{"station_name":"POOLC_20200929", "time":"2020-09-29T15:38:51Z", "water_pressure":null, "water_temperature":null, "battery_voltage":0, "latitude":47.679933, "longitude":-55.43001667}{"station_name":"POOLC_20200929", "time":"2020-09-29T16:18:51Z", "water_pressure":83.5167, "water_temperature":null, "battery_voltage":12.73, "latitude":47.679933, "longitude":-55.43001667}[...] and so on...
为什么我一直不用这个?活到老学到老!
不用说,使用 Akka 和 spray 收集 jsonl 数据要简单得多,几乎不值得运行教程。当然,缺点是您无权访问列数据类型和单位,并且必须自己决定转换。主要的挑战是,您需要告诉 Akka 数据是如何构造的——\n定界的(如本例所示)或类似 json list 的其他东西。
class ProcessErddapStreamData () {
// This part is the same as before. Set up your actor system,
// and the context for your concurrent operations (single thread
// in this case). implicit val system: ActorSystem = ActorSystem()
val execution: ExecutorService = java.util.concurrent.Executors.newSingleThreadExecutor()
implicit val ec: ExecutionContextExecutorService = ExecutionContext.fromExecutorService(execution) import erddapConstants._// Add a Flow to handle the framing of your Json with max size
val jsonStream: Flow[ByteString, ByteString, NotUsed] = JsonFraming.objectScanner(1000) val poolCoveUrl = // the url ending with '.jsonlKVP' def jsonDataSource(url: String) = {
// You need to fully run the Http request before
// processing the streams. If you do not, Akka will throw an
// error to avoid having an http request open too long. Http()
.singleRequest(HttpRequest(uri=url)
.flatMap(resp => resp.entity.dataBytes.runReduce(_++_))} val poolCoveData = Source.future(jsonDataSource(poolCoveUrl))
.via(jsonStream)
.map(_.utf8String.parseJson.convertTo[PoolCoveSensorRow])}
使用 JsonFraming 类 objectScanner 方法,既可以标识要使用的分隔值,又可以为数据流设置最大值。因为这里的数据是新行分隔的,并且默认情况下在 JsonFraming 类中处理,所以除了简单地创建一个流来处理流之外,没有太多事情要做。接下来,您可以使用 Spray 的.convertTo[Class]方法,轻松地将 json 对象转换成适当的PoolCoveSensorRow类。
清理数据从来都不容易,处理不熟悉的数据会带来许多挑战。和往常一样,随着您越来越了解数据源内部的情况以及您的用户希望如何与数据源进行交互,找到一个可行的解决方案并随着时间的推移对其进行改进是没有坏处的。
为学习更多关于数据的知识并找到使用数据的新方法干杯!
数据科学家应该开博客的非正统理由
这些都不是你应该开博客的常用理由

Ioana Tabarcea 在 Unsplash 上拍摄的照片
每隔几周左右,我就会看到一篇关于数据科学家为什么应该开博客的新文章。
此时,互联网上充斥着这些文章,它们都在重复同样众所周知的原因,即为什么数据科学家昨天就应该开始写作。
“数据科学家应该开一个博客,这样他们就可以练习交流,这样他们就可以提高对数据科学概念的理解,这样他们就可以增加他们的专业投资组合……”
你很清楚我在说什么。
不要误会,这些文章没有错。然而,现在是时候分享一些新的观点,并提到一些不同的理由,说明为什么数据科学家应该出去写作。
因此,如果你是一名数据科学家,而那些文章并没有完全说服你应该开一个博客,那就继续读下去。
你的工作是有报酬的。
如果我说错了,请纠正我,但是开博客的最好理由之一就是每个月有一点额外的零花钱。谁不想有一些额外的被动收入呢?此外,额外的收入真的可以用来偿还学生贷款、购买食品杂货、支付汽车贷款、投资或增加你的储蓄账户。
当我开始写关于数据科学和编程的文章时,我开始每月从我在 Medium 上发表的文章中额外赚几百美元。这份额外的收入是我继续写作的一个明确的动力,它的一个奇妙的副产品是开始探索我有经验的学科以及我不太熟悉的技术领域。
有时候,除了处理数字和编写代码,做点别的事情对你的大脑有好处。
我们有时都需要休息,你的大脑也不例外。
数据科学家的工作充其量是繁重的,这使得休息一下去做一些完全不同的事情变得更加必要。写作可以是创造力和尝试新事物的一个很好的出口,否则你将无法在常规工作中获得。
我发现成功人士有趣的一点是,他们通常总是有一个与他们的工作没有任何相似或相关的爱好。爱好是充电的好方法,成功人士利用这一点来避免疲惫和放松。例如,杰克·多西徒步旅行,梅丽尔·斯特里普编织,桑德尔·皮帅打板球,史蒂夫·沃兹尼亚克打赛格威马球(是的,那确实是赛格威上的马球)。
因此,也许你不需要做像打赛格威马球这样疯狂的事情,但你可以写作,作为放松、充电和放松的一种方式。
你有机会了解你想成为什么样的数据科学家。
与普遍的看法相反,成为数据科学家的方式不止一种。
数据科学家可以根据他们的工作类型分为不同的类别,或者“原型”。例如,数据科学家可能更倾向于工程方面,他们编写代码来确定事情是如何工作的。或者,他们可能对事物的数学方面更感兴趣,并对发现事物工作的原因感兴趣。最后,他们可以更多地关注事情的商业方面,关注什么是有效的。或者,坦率地说,数据科学家可能是这三种类型的混合体。这在的这篇文章中有更好的解释。
关键是,通过创建博客和发现数据科学的不同领域,你可能会发现你真正感兴趣的数据科学领域和你真正不关心的领域。
你可以探索数据科学的不同领域,否则你将无法涉足。
根据你所从事的行业,数据科学职位可能会受到你要做的工作类型的限制。无论是你工作描述中的工作类型,还是你将要承担的项目类型,你工作的行业通常会预先决定这些事情。
虽然这绝不是一件坏事,但扩展您的视野并探索数据科学的其他领域并无大碍。
写关于数据科学的文章可以让你发现你不一定习惯的机器学习、深度学习、人工智能、数学和编程领域。此外,您将看到不同行业如何使用数据科学来解决他们的业务问题。
写作可以是一种释放压力的好方式。
或者写作是一种缓解压力的好方法——取决于你问的是谁。
在漫长的一天工作后,每个人都有自己释放压力的方式,无论是健身、遛狗、烹饪、听音乐还是阅读。写作是一种非常规的消遣方式,也能缓解压力。
写作是进入心流状态的一种极好的方式,它不需要与工作相关。通过学习写作进入心流状态,你可以鼓励你的身体在工作之外的时间进入心流状态。
为什么进入心流状态对释放压力很重要?一种心流状态鼓励注意力集中,没有来自你肆无忌惮的想法的额外噪音,给人一种清晰的感觉,良好的感觉被推到你头脑的最前沿,在达到心流状态后你甚至会变得更快乐。此外,当你专注于释放压力的事情时,压力、担忧和自我怀疑就会变得遥远。
最后的想法。
希望你带走了一些你应该开博客的不同理由。除了学习如何沟通、更深入地理解数据科学概念以及增加您的专业投资组合之外,很少触及其他原因。博客并不像听起来那样无聊和重复。
相反,把写博客看作是一种赚钱的方式,减轻一些压力,开始一个富有成效的爱好,并发现不同的领域,否则你不会有。
解除熊猫数据框的透视
如何将熊猫的数据框按摩成你需要的形状

托德·夸肯布什在 Unsplash 上拍摄的照片
介绍
上一篇文章我们分享了一个尴尬的时刻,这鼓励我们学习和使用熊猫来旋转数据帧。今天我们来看看熊猫内置的.melt()函数来反转我们的旋转数据。当您需要改变数据帧的形状或取消数据帧的透视时,.melt()函数就派上了用场。
入门指南
在开始之前,如果你还没有阅读Pivoting a Pandas data frame或者之前没有接触过 Python Pandas,我们建议首先从 Pandas 系列& DataFrame Explained 或者 Python Pandas 迭代 DataFrame 开始。这两篇文章都将为您提供安装说明和今天文章的背景知识。
我们离开的地方
为了让你快速了解,我们在下面加入了一段 Python 代码,摘自旋转熊猫数据框架。这个代码片段一旦执行,将会创建一个透视数据帧。
上面的 Python 代码片段将创建一个包含虚拟数据的 Pandas 数据框架。

上面的截图是执行上面的 Python 片段的结果。
既然我们的透视数据框架已经创建好了,我们将学习熊猫.melt()函数的语法,然后再将其应用到我们的数据框架中。
熊猫。melt()语法
上面是显示熊猫融化函数语法的 Python 片段。
因素
id_vars:您可以在这里输入一列或多列,Pandas 将使用这些列作为每条记录的标识符。value_vars:您可以使用该参数设置您希望取消透视的列。如果您没有设置该参数,Pandas 将使用除了在id_vars中指定的列之外的所有剩余列。var_name:这将为变量列设置一个带标签的列名。您可以为包含已取消透视的组的列传入所需的标签。value_name:您可以在这里设置值栏的名称。values 列表示对应于原始透视列类别的值。col_level:如果列是多索引的,您可以使用它来设置熔化发生的级别。ignore_index:接受一个布尔值,True将保留原来的索引,False将删除它。
我们的解决方案
上面的 Python 片段演示了熊猫融化函数。

上面的截图显示了熊猫融化被调用后的数据帧。
为了取消原始数据帧,我们需要将staff_no和name传递给id_vars。这样做将告诉 Pandas,我们将使用员工编号和员工姓名作为每个分组的标识符。然后,我们使用var_name和value_name为变量和值列设置标记为列的数据帧。通过不引用原始数据帧中的 year 列作为id_vars,Pandas 知道这些是我们想要取消透视的列。
如果您在运行上面的 melt 片段时遇到问题,您可以在这里找到一个完整的脚本。
摘要
在旋转熊猫数据框和这篇文章之间,我们为你提供了两种非常强大的方法来重塑数据框。能够应用.pivot()和.melt()将允许您以灵活的心态处理数据,因为您知道您可以将数据框调整到您想要的形状。
感谢您花时间阅读我们的故事,我们希望您已经发现它的价值。
不受欢迎的观点:我们将放弃机器学习作为主要的人工智能范式
人工智能
这一时刻将会到来,就像它发生在象征性人工智能身上一样。

巴林顿·拉特里夫在 Unsplash 上拍摄的照片
机器学习和深度学习将慢慢失去它们的地位,直到它们被归入它们真正的样子;奇特的统计技术。
至少二十年来,人工智能一直由连接主义人工智能(基于神经网络的人工智能)主导。从识别手写数字到掌握语言,突破一个接一个地出现。人工智能发展得如此之快,世界已经跟不上了。尽管该领域很受欢迎,但神经网络革命的主要先驱之一,人工智能的教父杰弗里·辛顿(Geoffrey Hinton)认为,我们应该从头开始重新思考一切:“我的观点是扔掉所有东西,重新开始。”
机器学习(ML)和深度学习(DL)是目前领先的人工智能范式,迄今为止已经做了出色的工作。一个很好的例子是最近基于 transformer 的语言模型的繁荣——像 GPT-3。但是,前面还有一些障碍似乎是今天的方法无法触及的。有新生的框架将在未来几年接管人工智能。这里有两个原因可以解释为什么我们会放弃将连接主义人工智能作为推动该领域前进的主导力量。
1.以目前的模式,我们无法达到人工智能的目标
人工智能可以从两个非常不同的角度来看。
首先,人工智能的实际应用方面。我们正在用深度学习方法解决问题,而就在几年前,我们还认为这是不可能的。人类水平、创造性系统、对话机器人或语言大师的对象/语音检测和识别。从这个角度来看,AI 看起来不错。
但是还有另一个角度:看着我们前进的方向。而且看起来也没那么好。在这个领域有一个持续的争论。没有人知道哪条路是通向人工智能的正确道路(AGI)。基于深度学习的解决方案是好的,但最终目标——假设可以解决一切——是,而且一直是,AGI。专家们对最新技术的认同程度不亚于他们对下一步该怎么做的分歧。
有人说我们正朝着更大、更强大的语言模型的正确方向前进——考虑到 GPT-3 及其后继者的成功,这是公平的。其他人说我们必须在这里和那里做一些曲折——想想完全自我监督的模型、强化学习、、混合模型。其他人认为我们必须添加尚不存在的新东西——比如系统 2 推理、因果关系,或者直观物理学。
我们是我们所知的唯一高智商的例子。可以合理地假设,人工智能至少在某种程度上会与某些特征相似。这就是为什么越来越多的研究人员捍卫“具体化人工智能”的观点:没有一个与世界互动的身体,智力是无法获得的。阿尔瓦·诺伊在他的《感知中的行动》一书中认为,“感知不是大脑中的一个过程,而是身体作为一个整体的一种熟练活动。”我们的智慧来自于我们在这个世界上成长、生活和互动的方式。
发展机器人学的子领域旨在以物理机器人的形式建造人工智能机器,可以像人类儿童一样成长。但是这并不是一个新的想法;图灵本人认为“与其试图制作一个模拟成人思维的程序,不如试着制作一个模拟儿童思维的程序。”然后,我们可以将问题分成两个更明确的部分:构建孩子的大脑——在图灵的假设中很容易编程——然后教育它。
这种解决方案对于今天的 ML/DL 来说完全是遥不可及的。虚拟人工智能可以解决一些问题,但不是全部。因为基于 ML 和 DL 的人工智能并不生活在现实世界中,它们无法像我们这样与现实世界互动。因此他们永远不会变得像我们一样聪明。
2.我们正在通过在黑暗中射击接近 AGI
但是让我们假定 ML 和 DL 是无辜的,假设我们可以通过继续这条道路来建设 AGI。
自从 2012 年对 DL 的兴趣飙升以来,我们一直在建立更大的模型,在更大的计算机上用更多的数据进行训练。这种“越大越好”的想法已经在自然语言处理和计算机视觉等子领域取得了重大成功。只要我们能够开发更大的模型,这种方法可能会不断给我们带来更好的结果。希望在未来的某个时候,这些模型中的一个变得如此智能,以至于达到 AGI 的地位——我们现在甚至还没有接近。
GPT 3 号是这种态度的一个很好的例子。它成为有史以来最大的神经网络,拥有 1750 亿个参数——比它的前身 GPT-2 大 100 倍。它展示了自己,在许多不同的语言任务中表现出色,甚至解决了以前留给我们的问题,如写诗或音乐,将英语转换成代码,或思考生命的意义。
GPT-3 比其他模型强大得多,以至于我们很快发现自己无法评估它的局限性:作者没有考虑人们发现的许多用例。人们不断试图找到它的弱点,一次又一次撞上了自己局限的墙。GPT 3 号的能量超出了我们测量工具的极限。无论它的智力水平如何,我们所测量的都低于这个水平。
另一个例子是一个月前发布的 Wu Dao 2.0,它现在保持着有史以来最大的神经网络的记录,它很快就会失去这个记录。这个 1.75 万亿参数的怪物比 GPT-3 大 10 倍。我们无法充分衡量 GPT 3 号的智能水平——尽管人们普遍认为它没有 AGI 的水平——我们一直在建造越来越大的模型。
为什么我们这样接近 AGI?这将把我们引向何方?
我们在黑暗中拍摄。经济利益是一个诱人的追求目标,这也正是这些模式背后的大多数公司和机构所追求的。如果我们继续构建我们无法评估的更大的智能模型,会发生什么?
通过做出我在本节开始时描述的假设,我们得出结论,我们最终将使用当前的技术构建一个 AGI 级别的系统。我们会寻找它,我们会找到它。但是我们不会知道,因为我们用来定义现实的工具会告诉我们另一个故事。而且,因为我们关灯向前走,我们甚至不会停下来怀疑这是否发生了。
这种情况有多危险?我们试图建立有史以来最强大的实体。我们在黑暗中进行。我们在盲目地做这件事。我们这样做是因为金钱和权力。如果最终,ML 和 DL 可以创造 AGI,我们最好找到一种方法来避免这种情况。我们应该改变我们的心态和范式——让其他人更容易理解,更负责任。
最后的想法
ML 和 DL 是今天 AI 的同义词。在过去的 15-20 年里,该领域的每一个重大突破都是由于这些方法的不合理的有效性。他们在解决一些问题上创造奇迹,但他们不是万灵药;他们不会解决 AI 的终极问题。
我们要么用其他更适合的范式取代这些范式,这些范式确实能够不断推动该领域向前发展,越过连接主义人工智能的障碍。或者我们最好让他们不再是主力前锋,除非我们想在不知道的情况下撞上 AGI,并潜在地发现我们自己处于 AI 推翻和否决我们的可怕场景中。
让我们去浪漫化机器学习和深度学习。让我们试着不要要求他们超出他们所能给予的。它们是非常有用的范例,但是历史一次又一次地向我们表明,即使是看起来像最终解决方案的东西也往往不是。
跟我一起去未来旅行了解更多关于人工智能、哲学和认知科学的内容!此外,欢迎在评论中提问或在 LinkedIn 或 Twitter 上联系!😃
推荐阅读
[## GPT 三号吓到你了?遇见武道 2.0:1.75 万亿参数的怪兽
towardsdatascience.com](/gpt-3-scared-you-meet-wu-dao-2-0-a-monster-of-1-75-trillion-parameters-832cd83db484)
Python 中的无监督异常检测
初学者指南

什么是异常检测?
异常检测也称为离群点检测,是在数据集中寻找与其他数据点不同的数据点的过程。异常检测的常见应用包括金融交易中的欺诈检测、故障检测和预测性维护。
广义地说,异常检测可以分为监督和非监督领域。监督异常检测需要带标签的数据集来指示记录是“正常”还是“异常”。无监督异常检测涉及未标记的数据集。它假设未标记数据集中的大多数数据点是“正常”的,并且它寻找不同于“正常”数据点的数据点。
在本文中,我们将使用 Pycaret 来检测异常。Pycaret 是一个自动机器学习(AutoML)工具,可用于监督和非监督学习。
无监督异常检测
让我们从安装 PyCaret 开始。
pip install pycaret==2.3.5
pip install scipy==1.4.1
导入必要的模块
from pycaret.anomaly import *
from sklearn.datasets import load_breast_cancer
加载数据集
我们将使用来自 UCI 机器学习库[2]的威斯康星州乳腺癌(诊断)数据集[1],该数据集包含乳腺肿块细针抽吸的特征计算数字化图像以及肿块是良性(B)还是恶性(M)的诊断。该数据集通常用于演示受监督的机器学习,其中模型被训练来预测诊断。出于演示无监督异常检测的目的,我们将忽略诊断。
我们将数据分成训练集,保留一小部分“看不见的”数据用于评分。
df = load_breast_cancer(as_frame=True)['data']
df_train = df.iloc[:-10]
df_unseen = df.tail(10)
设置 Pycaret
anom = setup(data = df_train,
silent = True)
当执行setup时,将silent参数设置为True会自动确认数据类型的输入。如果silent设置为False,,Pycaret 要求用户手动确认输入数据类型,如下图所示。

作者图片
如果没有显式定义,Pycaret 将推断数据类型。我们还可以使用categorical_features、numeric_features、ordinal_features参数定义哪些列包含分类、数字、序数和日期特性。定义不同的数据类型将影响列的预处理方式。例如,categorical_imputation参数决定了如何处理缺失的分类值。可用选项有constant和mode。类似地,对于数字特征,numeric_imputation参数决定了如何处理丢失的数值。可用选项有mean、median或zero。
还有各种其他预处理选项,如归一化、降维甚至自定义转换。查看文档了解更多详情。
查看可用车型
让我们看看 Pycaret 中有哪些异常检测模型。
models()
reference 列指示模型是从哪个源包构建的。PyOD (Python 离群点检测)是一个提供各种离群点检测模型的包。

作者图片
训练异常检测模型
anom_model = create_model(model = 'iforest', fraction = 0.05)
我们通过定义模型的 ID 来训练异常检测模型。fraction参数指示数据集中存在的异常值的数量。默认值是0.05,表示数据集有 5%的异常值。
对数据集评分
results = assign_model(anom_model)
assign_model使用训练好的模型对训练数据集进行评分,并返回模型的预测,与训练数据连接在一起。Anomaly列为二进制,其中1表示记录异常,0表示记录正常。Anomaly_Score列给出了记录的原始分数,负值表示记录正常。

作者图片
剧情模式
plot_model功能有助于使用 TSNE 或 UMAP 可视化高维数据。
plot_model(anom_model, plot = 'tsne')

作者 Gif
plot_model(anom_model, plot = 'umap')

作者图片
保存模型
save_model将转换管道和训练好的模型保存为 pickle 文件。pickle 文件包含一个 scikit-learn 管道对象。
save_model(model = anom_model, model_name = 'iforest_model')
加载模型
load_model加载保存的转换管道和训练模型作为 scikit-learn 管道对象。
loaded_model = load_model('iforest_model')type(loaded_model)
>> sklearn.pipeline.Pipeline
看不见的数据得分
predict方法将得到1或0的二进制输出,其中1代表异常记录而0代表正常记录。
loaded_model.predict(df_unseen)>> array([0, 0, 0, 0, 0, 1, 0, 0, 1, 0])
predict_proba方法将得到记录被分类为0或1的概率。
loaded_model.predict_proba(df_unseen)>> array([[0.81527091, 0.18472909],
[0.88219213, 0.11780787],
[0.4789605 , 0.5210395 ],
[0.46277058, 0.53722942],
[0.47638085, 0.52361915],
[0.4421625 , 0.5578375 ],
[0.68153289, 0.31846711],
[0.75207438, 0.24792562],
[0.27120702, 0.72879298],
[0.54821746, 0.45178254]])
decision_function方法将返回异常值
loaded_model.decision_function(df_unseen)>> array([-0.11826784, -0.14024322, -0.00783128, -0.00251488, -0.00698418,
0.00425233, -0.07435137, -0.09751556, 0.06039016, -0.03057366])
结论
在本文中,我们探讨了如何使用 Pycaret 快速训练无监督异常检测模型,使用 TSNE 或 UMAP 可视化高维特征,并使用训练好的模型预测未知数据。
加入 Medium 阅读更多这样的故事。
参考
[1] 乳腺癌威斯康星州(诊断)数据集
创作者:
1.威廉·h·沃尔伯格医生,普通外科部
威斯康辛大学,临床科学中心
麦迪逊,WI 53792
沃尔伯格' @ 'eagle.surgery.wisc.edu
2.威斯康星大学计算机科学系 w . Nick Street
,威斯康辛州麦迪逊市西代顿街 1210 号,邮编:53706
Street' @ 'cs.wisc.edu 608–262–6619
3.Olvi L. Mangasarian,计算机科学系
威斯康星大学,威斯康辛州麦迪逊市西代顿街 1210 号,邮编:53706
olvi' @ 'cs.wisc.edu
捐赠者:尼克街
许可证:CC BY 4.0
[2]Dua d .和 Graff c .(2019 年)。UCI 机器学习知识库[http://archive . ics . UCI . edu/ml]。加州欧文:加州大学信息与计算机科学学院。
使用 Python-K Means ++和剪影分数进行聚类的无监督学习技术
K 入门指南意味着使用 Python 进行集群

丽贝卡·坎贝尔在 Unsplash 上拍摄
没人监督我!
对于那些想知道什么是无监督学习技术的人,想象一下你在克莱顿经营一家体育用品店。你计划为你的高价值客户推出促销优惠,以示感谢。每天都有成百上千的顾客来拜访你。您应该将此报价发送给谁?艰难的选择!嗯,也许是高价值的?但是那些经常购买但花费不多的人呢?这是一个很大的困境。幸运的是,您的积分卡计划可以捕获您的客户信息,而您的 oracle 数据库包含所有交易详情。您可以使用这些数据,形成一组具有相似属性的客户,而无需预先定义谁属于哪里。简而言之,没有预定义的结果变量(在这种情况下是你的客户细分)但在研究客户属性后形成一个结果变量的学习技术被定义为无监督学习技术。
遭受维度的诅咒
从理论上讲,你对你的客户了解得越多,你就能更好地对他们进行分组。难道不是吗?然而,在现实生活中,比你想象的要复杂一点。实际上,太多的信息(特征)会导致你的客户分组不佳。但是为什么呢?
- 特征可能高度相关,年龄越高,花费越低
- 太多的特征会引入大量的噪声,使得算法很难对数据执行良好的操作——Sam,您的邻居客户几乎每天都来打招呼并购买价值一美元的蛋白质棒。他是一个非常受重视的客户?
- 训练任何无监督模型所需的数据点数量会随着维度(特征)呈指数增长。太多的信息意味着需要更多的数据来训练模型
为了克服这些问题,不同的降维技术,如主成分分析,因子分析等。可以使用。我们引入了维数灾难的概念,为围绕无监督学习技术的假设铺平了道路。
算法
无监督学习技术的一些常见算法包括:
- k 表示聚类
- 分层聚类
- k 模式聚类
- DBScan 聚类-基于密度的空间聚类
- 主成分分析
- 要素分析
我们今天讨论什么?
人们通常认为现实是一系列非黑即白的事件。这是一个具有挑战性的问题,因为那些受这种思维方式折磨的人可能永远不会觉得现实足够好。今天,我们试图解决简单和抑郁的问题,同时确定因抑郁症住院的精神病患者中更高水平的黑白思维和更高水平的自我报告抑郁之间的关系。
数据源
用于此分析的数据来自 Ginzberg 数据框架,该数据框架基于因抑郁症住院的精神病患者。数据收集自《应用回归分析和广义线性模型》一书,第二版,作者 Fox,J. (2008)。该数据集包括三个变量——简单性(非黑即白的思维)、宿命论和抑郁症及其调整后的分数。
算法— K 表示聚类
分解一下,K 表示组数,Means 表示平均值。本质上,我们有基于平均距离计算的 K 个组。我猜不清楚!对于任何要工作的 K 均值聚类算法,我们需要有以下参数。
- K —我们想要多少组。注意——该算法不具备决定或得出 k 值的预测能力。这是由肘图和轮廓分数等少数技术指导的用户调用
- 数据点——在这种情况下,简单、宿命论和抑郁得分,即你的输入数据
- K 个质心 —把它想象成 K 个组中每一组的锚点/中心值。大多由算法随机选择。质心的选择影响最终集群/组的形成方式。此外,如果您熟悉使用 O(n)表示的任何算法的时间复杂度,已经观察到次优的质心分配使得时间复杂度为多项式,即算法运行和收敛的时间更长。因此,David Arthur 和 Sergei Vassilvitskii 提出了一种更智能的方法(也称为 K 表示++的意思),它执行以下操作:
-从输入数据点(比如 X)
中随机选择一个中心 C1-对于尚未选择的每个数据点 X∈**X,计算 D(x),即 X 和已经选择的最近质心之间的距离
-对于所有 x* ∈选择一个新的中心 Ci,选择 x* ∈ X,使其概率为 D(x) / Sum(对于所有 x ∈ X D(x) )
-重复步骤 2 和 3,直到 k 个中心被选择 - 距离度量 —主要是欧几里德距离,作为多个患者之间相似性的度量。例如,当我们谈论足球或众所周知的足球时,我们经常使用总得分来对优秀球员进行分组。说 c 罗,梅西,沃纳,普基分别进了 35,32,10,8 个球。你将如何衡量类似球员的质量?大概是 c 罗和梅西(各 35、32 球)和沃纳和普基(各 10、8 球)。请注意,我们研究了进球得分的差异。如果我添加“助攻”作为另一个指标,我们需要使用不同的衡量标准。比如助攻分别是 10,8,2,1。所以 c 罗和梅西之间的新距离变成了的平方根((35–32)+(10–8))。注意——其他距离度量,如曼哈顿、城市街区、雅可比距离等。也被其他一些算法所采用。
K-如何意味着工作?

图一。K 均值聚类算法的工作可视化。图片来源:图片来源——Wikimedia.com GIF

图二。说明了 K 均值算法的逐步分解。图片来源——由作者使用 PowerPoint 开发。
重要术语和公式
注:所有图片均由作者使用 latex 复制。
- 质心

1. Ci - ith Centroid where i ∈ K (or K Groups of Cluster)
2\. Si - Group of Data points that belong to Cluster K with Ci as its centroid; j represents attributes of a point Xi
3\. ||Si|| - Number of data points from Si
- 欧几里德距离

1\. Dx is the distance of of a point Xi from it's centroid Ck
- 收敛准则 K 意味着是一个迭代过程。一旦所有数据点都被分配给质心(基于最短的欧几里德距离),质心被重新计算,并且该过程继续,直到质心停止重新移动,即新的和先前的质心编号没有显著变化
- 类内和类间距离
类内距离是同一类中的点之间的总距离,类间距离是不同类中的两点之间的距离
假设
- k 表示如果数据包含异常值,算法性能很差
- k 表示算法不能处理缺失值
- 聚类变量之间不应该高度相关
- 所有聚类变量都应标准化,因为变量的大小会影响距离计算
还在想怎么决定 K?让我们深入挖掘
# Numerical libraries
import numpy as npfrom sklearn.model_selection import train_test_splitfrom sklearn.cluster import KMeans# to handle data in form of rows and columns
import pandas as pd# importing ploting libraries
from matplotlib import pyplot as plt
%matplotlib inline#importing seaborn for statistical plots
import seaborn as snsfrom sklearn import metricsimport pandas as pd# reading the CSV file into pandas dataframe
mydata = pd.read_csv(“Depression.csv”)mydata.drop('id', axis=1, inplace=True)
检查相关性
# — — — — — — — — — — — — — — — -Heat map to identify highly correlated variables — — — — — — — — — — — — -
#-------------------------------Heat map to identify highly correlated variables-------------------------
plt.figure(figsize=(10,8))
sns.heatmap(mydata.corr(),
annot=True,
linewidths=.5,
center=0,
cbar=False,
cmap="YlGnBu")
plt.show()

图 3。展示了关联矩阵的热图。注意——所有变量都与其调整后的分数高度相关。这是一个关于是保留原来的分数还是继续调整分数的个人问题。这无法得到任何统计假设的支持。图片来源——作者使用 Jupyter Notebook 开发。
异常检测和处理
mydata.drop(columns = {‘simplicity’, ‘fatalism’,’depression’}, inplace=True)#--Checking Outliers
plt.figure(figsize=(15,10))
pos = 1
for i in mydata.columns:
plt.subplot(3, 3, pos)
sns.boxplot(mydata[i])
pos += 1

图 4。聚类变量的箱线图摘要。图片来源——作者使用 Jupyter Notebook 开发。
col_names=list(mydata.columns)
display(col_names)for i in col_names:
q1, q2, q3 = mydata[i].quantile([0.25,0.5,0.75])
IQR = q3 — q1
lower_cap=q1–1.5*IQR
upper_cap=q3+1.5*IQR
mydata[i]=mydata[i].apply(lambda x: upper_cap if x>(upper_cap) else (lower_cap if x<(lower_cap) else x))
标准化属性/聚类变量
##Scale the data
from scipy.stats import zscoremydata_z = mydata.apply(zscore)mydata_z.head()
K 表示使用 Sklearn 进行聚类
# List to store cluster and intra cluster distanceclusters = []
inertia_vals = []# Since creating one cluster is similar to observing the data as a whole, multiple values of K are utilized to come up with the optimum cluster value
#Note: Cluster number and intra cluster distance is appended for plotting the elbow curve
for k in range(1, 10, 1):
# train clustering with the specified K
model = KMeans(n_clusters=k, random_state=7, n_jobs=10)
model.fit(mydata_z)# append model to cluster list
clusters.append(model)
inertia_vals.append(model.inertia_)
参数
- n_clusters —要形成的簇的数量;也等同于要生成的质心的数量
- init 指定初始质心初始化的方法(默认 K-means++)
- n_init —使用不同质心种子运行 k-means 算法的次数
- max _ ITER—k-means 算法单次运行的最大迭代次数。满足收敛标准时停止,使用 tol(公差选项)定义
- random_state —协助质心初始化的随机数生成。导入以确保可重现的结果
什么是惯性 _?
惯性被定义为数据点到它们最近的聚类中心(质心)的平方距离之和。距离越小,聚类的紧密度越好。但是,随着 k 值的不断增加,惯性会不断降低。具有两个聚类的算法将始终比具有四个聚类的算法具有更高的惯性分数,依此类推。这就是我们结合肘部绘图和轮廓分数来决定 k 的最佳值。
肘击剧情
这里的想法是为不同的 K 值绘制总惯性分数,并确定 K,超过 K 惯性变化不大。惯性的公式如下。

1\. I = Inertial = Sum of the squared distance of data points to their closest cluster
2\. n = Total number of records in the dataset
3\. D()= Sum of squared distance
4\. Xi = Record i in the data (i.e. in this case record of a given patient)
5\. Ck = Cluster to which record Xi is assigned

图 5。说明了肘图。X 轴表示聚类的数量(K ), Y 轴表示惯性值。图片来源——作者使用 Jupyter Notebook 开发。
# plot the inertia vs K values
plt.plot(range(1,10,1),inertia_vals,marker='*')
plt.show()
研究上图可以发现,4、5 或 6 是 k 的最佳值。请注意,从 6 到 7,曲线趋于平缓,而斜率在 4 个聚类后没有观察到显著变化。
比我们想象的要难!让剪影配乐成为救世主

轮廓分数用于测量聚类之间的分离程度。在上面的公式中,bi 表示一个点到 I 不属于的任何其他聚类中所有点之间的最短平均距离,而 ai 是 I 和来自同一聚类的所有数据点的平均距离。从逻辑上来说,如果 bi > ai,那么一个点与它的相邻聚类分离得很好,而它更接近它所属的聚类中的所有点。
from sklearn.metrics import silhouette_scorefor i in range(1,9,1):
print("---------------------------------------")
print(clusters[i])
print("Silhouette score:",silhouette_score(mydata_z, clusters[i].predict(mydata_z)))
输出
KMeans(n_clusters=2, n_jobs=10, random_state=7)
Silhouette score: 0.40099183297222984
---------------------------------------
KMeans(n_clusters=3, n_jobs=10, random_state=7)
Silhouette score: 0.3191854112351335
---------------------------------------
KMeans(n_clusters=4, n_jobs=10, random_state=7)
Silhouette score: 0.281779515317617
---------------------------------------
KMeans(n_clusters=5, n_jobs=10, random_state=7)
Silhouette score: 0.2742499085300452
---------------------------------------
KMeans(n_clusters=6, n_jobs=10, random_state=7)
Silhouette score: 0.2790023172083168
---------------------------------------
KMeans(n_clusters=7, n_jobs=10, random_state=7)
Silhouette score: 0.2761554255999077
---------------------------------------
KMeans(n_jobs=10, random_state=7)
Silhouette score: 0.2666698045792603
---------------------------------------
KMeans(n_clusters=9, n_jobs=10, random_state=7)
Silhouette score: 0.27378613906697535
观察局部最大值的轮廓分数用于确定与肘图一起的聚类的最佳数量。因此,在本例中,我们可以将 6 作为最佳群集数量。
关于作者:高级分析专家和管理顾问,帮助公司通过对组织数据的商业、技术和数学的组合找到各种问题的解决方案。一个数据科学爱好者,在这里分享、学习、贡献;你可以和我在 上联系 和 上推特;
无监督机器学习解释
它是什么,方法和应用

Jase Bloor 在 Unsplash 上拍摄的照片
当我们想要发现数据的底层结构时,无监督学习是一个很好的解决方案。与监督学习相比,我们不能将非监督方法应用于分类或回归类型的问题。这是因为无监督的 ML 算法从未标记的数据中学习模式,而我们需要知道输入-输出映射来执行分类或回归(在大多数情况下,我将在后面谈到这一点)。本质上,我们的无监督学习算法将发现数据中的隐藏模式或分组,而不需要人类(或任何人)标记数据或以任何其他方式干预。
接近无监督学习
当我们的数据未被标记时,这种学习方法通常被利用。例如,如果我们想要确定我们想要发布的新产品的目标市场是什么,我们将使用无监督学习,因为我们没有目标市场人口统计的历史数据。执行无监督学习时主要有三个任务(不分先后):1)聚类 2)关联规则 3)降维。让我们更深入地研究每一个。
聚类
从理论上讲,同一组中的实例将具有相似的属性和/或特征。聚类包括根据相似性和差异对未标记的数据进行分组,因此,当两个实例出现在不同的组中时,我们可以推断它们具有不同的属性和/或特征。
这种无监督学习的方法非常流行,并且可以进一步细分为不同类型的聚类,例如排他聚类、重叠聚类、层次聚类和概率聚类——这些方法的细节超出了本文的范围。一种流行的聚类算法是 K 均值聚类。
关联规则规则
关联规则学习是一种基于规则的机器学习方法,用于发现给定数据集中变量之间的有趣关系。该方法的目的是使用感兴趣的度量来识别在数据中发现的强规则。这些方法通常用于市场购物篮分析,使公司能够更好地了解各种产品之间的关系。有许多不同的算法用于关联规则学习,但最广泛使用的是 Apriori 算法。
降维 降维
无监督学习的另一种形式是降维。这是指将数据从高维空间转换到低维空间,使得低维空间保留原始数据的含义属性。我们降低数据维度的一个原因是为了简化建模问题,因为更多的输入特征会使建模任务更具挑战性。这就是所谓的 维度诅咒 。我们这样做的另一个原因是为了可视化我们的数据,因为可视化超过 3 维的数据是困难的。
在机器学习工作流程的的数据预处理或解释性数据分析(EDA)阶段,通常会采用降维技术。流行的算法有主成分分析、奇异值分解、自动编码器。
无监督学习的应用
当我们谈论机器学习时,很容易看出监督学习在商业中的位置,但在无监督学习中就不那么容易了。一个原因可能是,无监督学习自然会引入更高的不准确结果风险,因为没有什么可以衡量算法得出的结果——企业可能不愿意承担这种风险。
尽管如此,它仍然为查看数据提供了一个很好的探索途径,使企业能够发现数据中的模式,比他们手动观察数据要快。
无监督学习在现实世界中的常见应用包括:
- 新闻分段:众所周知,谷歌新闻利用无监督学习对来自不同新闻媒体的基于同一故事的文章进行分类。例如,足球(为我在大西洋彼岸困惑的朋友准备的足球)转会窗口的结果都可以归类到足球下。
- 计算机视觉:物体识别等视觉感知任务利用无监督学习。
- 异常检测:无监督学习用于识别偏离数据集正常行为的数据点、事件和/或观察值。
- 客户角色:可以使用无监督学习创建有趣的买家角色档案。这有助于企业了解客户的共同特征和购买习惯,从而使企业能够更好地调整产品。
- 推荐引擎:过去的购买行为与无监督学习相结合,可以帮助企业发现数据趋势,从而制定有效的交叉销售策略。
最后的想法
无监督学习是发现未标记数据的潜在模式的好方法。这些方法通常对分类和回归问题毫无用处,但我们可以使用无监督学习和监督学习的混合方法。这种方法被称为半监督学习——我将在另一篇文章中更深入地讨论这一点。
感谢阅读!
如果你喜欢这篇文章,请通过订阅我的免费每周简讯与我联系。不要错过我写的关于人工智能、数据科学和自由职业的帖子。
相关文章
** https://medium.com/geekculture/how-to-get-paid-to-learn-data-science-machine-learning-37c9088ab925 **
人脑分布式区域的无监督表示学习(下)
无监督学习和人脑的交集

腹侧颞叶皮层感兴趣区域的潜在空间(图片由作者提供)
这是该系列的第四篇文章,即“腹侧颞叶皮层时空功能磁共振成像的认知计算模型”。如果你想了解整个系列,请点击下面的链接。
我将介绍人脑中分布式区域上的无监督表示学习的主题及其在大脑解码研究中的用例。让我们开始吧。
所有相关资料都放在我的 GitHub 页面上。别忘了去看看。如果你是一个纸质爱好者,你可以阅读这一系列文章的纸质版,也可以在我的回购中找到。
无监督的&人脑中的流形学习
F 如果考虑用标准成像参数采集的所有体素或表面坐标,功能性 MRI 数据是非常高维的。在我们的数据集中,4D 时间序列图像数据的结构,我们有一个维数曲线的问题。因此,降维和流形学习算法可以通过在较低的空间表示中保持测地线关系来降低 fMRI 空间的维度。我们使用主成分分析、线性判别分析、独立分量分析、NNMF 和 MDS 作为降维算法。此外,t- SNE,UMAP,ISOMAP,LLE 和频谱嵌入执行生成低维流形的功能磁共振成像空间。让我们开始发现无监督学习和人类大脑的交集。我执行了许多无监督学习算法,因为它进一步帮助理解人类大脑下面的测地线信息,并给出关于分布式区域中的神经活动是否可解码的先验信息。
让我们安装并导入所有必需的包。请参考以前的文章(第一部分)了解数据集。请注意,本文不是为了回答以下问题(这些问题之前已经回答过了)
- fMRI 数据是如何构成的?(第一部分)
- 为什么对人脑进行时空掩蔽?(第二部分)
下面是安装我们需要的所有东西的 pip 命令。
我们安装了所有需要的东西(甚至更多)。下面我们来导入一下。
我们准备好了!让我们获取哈克斯比数据集。
接下来的事情是准备,屏蔽,标准化和转换 fMRI 数据为 NumPy 矩阵形式如下。
预处理部分完成。因此,我们可以转到我们想要执行的实际流程。在这个代码片段中,“masks”是人脑中被屏蔽的区域,我们希望对其执行认知任务,类别只是它们的标签。我们不使用整体体素进行表示学习。我们的主要想法是提取和可视化人类大脑中的潜在变量,其中神经活动的分布和重叠模式正在发生。
最后,我们可以开始实际的业务如下。在下图中,不同的颜色代表不同的类别。(即蓝色属于第 1 类,橙色属于第 2 类,等等。)
为了恰当地可视化潜在空间,我使用了 plotly python 包。以下代码以交互方式执行 2D 和 3D 可视化。
降维:主成分分析
PCA 是一种线性无监督降维算法,它计算主向量来改变表示的基础[26]。PCA 是在从图像压缩到文本去相关的广泛主题中使用的算法。这里,我们对受试者 5 的 RoI 进行了 PCA,并将其可视化如下。

人类大脑掩蔽区域的主成分分析(图片由作者提供)

人类大脑掩蔽区域的主成分分析(图片由作者提供)
降维:LDA
LDA 是一种监督降维算法,它是 Fisher 线性判别式的推广,旨在找到表征原始数据空间的线性子空间。因为它是受监督的,所以它是表征学习中一个强有力的范例。这里,我们对受试者 5 的 RoI 执行 LDA 并将其可视化。从图中,我们可以看到,通过独特地分离流形中的测地线距离,LDA 优于其他方法。

LDA 研究人类大脑中的掩蔽区域(图片由作者提供)
降维:ICA
ICA 是一种计算方法,用于将多变量信号分离成它们的加性分量。这是无监督降维的自然范例。这里,我们对受试者 5 的 RoI 进行 ICA,并将其可视化如下。

人类大脑中掩蔽区域的 ICA(图片由作者提供)
降维:NNMF
NNMF 是一种迭代的非负因子分析,将一个非负矩阵分解成它的线性子空间。这对于提取原始数据样本的自然线性子空间是有用的。在这里,我们对受试者 5 的 RoI 进行了 NNMF,并将其可视化。

NNMF 论人类大脑中的掩蔽区域(图片由作者提供)
流形学习:MDS
MDS 是通过保持流形中的测地线距离来提取原始数据空间的非线性子空间的经典方法。获得较低维度的嵌入来表示流形中的原始数据。在这里,我们对受试者 5 的 RoI 进行了 MDS,并将其可视化。

MDS 论人类大脑中的掩蔽区域(图片由作者提供)
流形学习:t-SNE
T-SNE 是一种迭代统计方法,用于通过保留小的成对距离或局部相似性来产生原始数据空间的非线性嵌入。它最小化了低维嵌入和高维数据的联合概率之间的 Kullback-Leibler 散度。在这里,我们对受试者 5 的 RoI 进行了 t-SNE 检查并将其可视化。

t-SNE 研究人类大脑中的掩蔽区域(图片由作者提供)
流形学习:UMAP
UMAP 是非线性嵌入的最近方法,并且它通常以显著的优势胜过 t-SNE。它与 t-SNE 非常相似,但也保留了数据的全局测地线结构。在这里,我们对受试者 5 的 RoI 进行了 UMAP,并将其可视化。

UMAP 论人类大脑中的掩蔽区域(图片由作者提供)
流形学习:ISOMAP
ISOMAP 映射也是一种通过等距映射的非线性嵌入算法,用于通过保持流形中的测地线距离来精确估计流形的内在几何形状。这里,我们对受试者 5 的 RoI 执行了 ISOMAP,并对其进行了可视化。

人脑掩蔽区域的 ISOMAP(图片由作者提供)
流形学习:LLE
LLE 是一种保持拓扑的非线性降维算法,试图保持流形中的邻居结构,并且它通常在优化和速度方面优于 ISOMAP,因此它在文献中具有非常实际的用途。在这里,我们对受试者 5 的 RoI 进行了 LLE,并将其可视化。

LLE 论人类大脑中的掩蔽区域(图片由作者提供)
流形学习:光谱嵌入
频谱嵌入也是一种非线性嵌入算法,它形成一个亲和矩阵并将频谱分解应用于拉普拉斯图。这里,我们对受试者 5 的 RoI 进行了光谱嵌入,并将其可视化。

人脑掩蔽区域的光谱嵌入(图片由作者提供)
本文到此为止。我们涵盖了人脑中分布式区域的无监督表示学习及其在大脑解码研究中的用例。恭喜你!您已经完成了第四篇文章,并通过认知计算方法对人脑解码迈出了一步。
在下一篇文章中,我们将执行从经典 ML 算法到神经网络的全面解码算法。
文章链接
- 发表文章
https://cankocagil.medium.com/discovery-neuroimaging-analysis-part-ii-b2cdbdc6e6c3
2.在路上(即将到来…)
- 第五部分的占位符
进一步阅读
我在机器学习和神经科学方面的研究中使用了以下参考文献列表。我强烈建议复制粘贴参考资料,并简要回顾一下。
参考
[1]巴、基罗斯和辛顿。图层归一化,2016。
[2] L. Buitinck,G. Louppe,M. Blondel,F. Pedregosa,A. Mueller,O. Grisel,V. Niculae,P. Prettenhofer,A. Gramfort,J. Grobler,R. Layton,J. VanderPlas,a .乔利,B. Holt,10 和 G. Varoquaux。机器学习软件的 API 设计:scikit-learn 项目的经验。在 ECML PKDD 研讨会:数据挖掘和机器学习的语言,第 108–122 页,2013。
[3]褚,田,王,张,任,魏,夏,沈。双胞胎:重新审视《视觉变形金刚》中空间注意力的设计,2021。
[4] K .克拉默、o .德克、j .凯舍特、s .沙莱夫-施瓦茨和 y .辛格。在线被动攻击算法。2006.
[5] K. J .弗里斯顿。统计参数映射。1994.
[6]格罗斯、罗查-米兰达和本德。猕猴下颞皮质神经元的视觉特性。神经生理学杂志,35(1):96–111,1972。
[7] S. J .汉森、t .松坂和 J. V .哈克斯比。用于物体识别的腹侧颞叶组合编码。
[8]哈克斯比、戈比尼、富里、伊沙伊、斯豪滕和彼得里尼。《视觉物体识别》,2018。
[9]赫克曼、哈伊纳尔、贾巴尔、吕克特和哈默斯。结合标记传播和决策融合的自动解剖脑 mri 分割。神经影像,33(1):115–126,2006。
10d .亨德里克斯和 k .金佩尔。高斯误差线性单位(gelus),2020。
[11]黄少华,邵文伟,王明林,张德庆.人脑活动视觉信息的功能解码:简要综述。国际自动化和计算杂志,第 1-15 页,2021。
[12] R. Koster、M. J. Chadwick、Y. Chen、D. Berron、A. Banino、E. Duzel、D. Hassabis 和 D. Kumaran。海马系统内的大循环复发支持跨发作的信息整合。神经元,99(6):1342–1354,2018。
[13]马奥尔。勾股定理:4000 年的历史。普林斯顿大学出版社,2019。
[14] K. A. Norman、S. M. Polyn、G. J. Detre 和 J. V. Haxby 超越读心术:功能磁共振成像数据的多体素模式分析。认知科学趋势,10(9):424–430,2006。
[15]奥图尔、江、阿卜迪和哈克斯比。腹侧颞叶皮层中物体和面孔的部分分布表征。认知神经科学杂志,17(4):580–590,2005。
[16] F .佩德雷戈萨、g .瓦洛夸、a .格拉姆福特、v .米歇尔、b .蒂里翁、o .格里塞尔、m .布隆德尔、p .普雷登霍弗、r .魏斯、v .杜伯格、j .范德普拉斯、a .帕索斯、d .库尔纳波、m .布鲁彻、m .佩罗特和 e .杜切斯内。sci kit-learn:Python 中的机器学习。机器学习研究杂志,12:2825–2830,2011。
17 r . a .波尔德拉克。功能磁共振成像的感兴趣区域分析。社会认知和情感神经科学,2(1):67–70,2007。
[18] M. Poustchi-Amin、S. A. Mirowitz、J. J. Brown、R. C. McKinstry 和 T. Li。回波平面成像的原理和应用:普通放射科医师回顾。放射学,21(3):767–779,2001。
[19] R. P. Reddy,A. R. Mathulla 和 J. Rajeswaran。心理健康专家的观点采择和情绪传染的初步研究:移情的玻璃脑观点。印度心理医学杂志,0253717620973380,2021 页。
[20]史密斯、米勒、萨利米-科尔希迪、韦伯斯特、贝克曼、尼科尔斯、拉姆齐和伍尔利奇。功能磁共振成像的网络建模方法。神经影像,54(2):875–891,2011。
21 田中先生。下颞叶皮层和物体视觉。神经科学年度评论,19(1):109–139,1996。
[22] M. S .特雷德。Mvpa-light:一个多维数据的分类和回归工具箱。神经科学前沿,14:289,2020。
[23] M. P .范登赫维尔和 H. E .波尔。探索大脑网络:静息态功能磁共振成像功能连接综述。欧洲神经精神药理学,20(8):519–534,2010。
[24] G. Varoquaux,A. Gramfort,J. B. Poline 和 B. Thirion。大脑协方差选择:使用群体先验的更好的个体功能连接模型。arXiv 预印本 arXiv:1008.5071,2010。
[25] Y. Wang,J. Kang,P. B. Kemmer 和 Y. Guo。一种利用偏相关估计大规模脑网络功能连接的有效可靠的统计方法。神经科学前沿,10:123,2016。
26s . Wold、K. Esbensen 和 P. Geladi。主成分分析。化学计量学和智能实验室系统,2(1–3):37–52,1987。
27s . Wold、K. Esbensen 和 P. Geladi。主成分分析。化学计量学和智能实验室系统,2(1–3):37–52,1987。
基于 Lbl2Vec 的无监督文本分类
对无标签文本文档的基于嵌入的分类的介绍

本帖基于我们的论文“LBL 2 vec:一种基于嵌入的预定义主题无监督文档检索方法(2021)”。你可以在那里读到更多的细节。
文本分类的任务是给一个句子或文档分配一个合适的类别。类别取决于所选数据集,可以涵盖任意主题。因此,文本分类器可用于组织、构建和分类任何类型的文本。
常见的方法使用监督学习来分类文本。尤其是近年来基于 BERT 的语言模型取得了非常好的文本分类效果。这些传统的文本分类方法通常需要大量的标注训练数据。然而,在实践中,用于训练最先进的分类算法的带注释的文本数据集通常是不可用的。数据的标注通常涉及大量的人工工作和高昂的费用。因此,无监督的方法提供了对未标记数据集运行低成本文本分类的机会。最近,无监督文本分类也被称为零镜头文本分类。在本文中,你将学习如何使用LBL 2 vec来执行无监督的文本分类。
Lbl2Vec 是如何工作的?
Lbl2Vec 是一种用于无监督文档分类和无监督文档检索的算法。它自动生成联合嵌入的标签、文档和词向量,并返回由手动预定义的关键字建模的类别的文档。该算法的核心思想是许多语义相似的关键词可以代表一个类别。在第一步中,该算法创建文档和单词向量的联合嵌入。一旦文档和单词被嵌入到共享向量空间中,该算法的目标是从先前手动定义的表示类别的关键词中学习标签向量。最后,该算法可以基于文档向量与标签向量的相似性来预测文档与类别的从属关系。在高层次上,该算法执行以下步骤来分类未标记的文本:
1.对每个感兴趣的类别使用手动定义的关键字
首先,我们必须定义关键字来描述每个感兴趣的分类类别。这个过程需要一定程度的领域知识来定义描述分类类别并且在分类类别中彼此语义相似的关键词。

不同运动分类类别的关键词示例。图片作者。
2.创建联合嵌入的文档和单词向量
嵌入向量是允许我们在多维空间中表示单词或文本文档的向量。嵌入向量背后的思想是相似的单词或文本文档将具有相似的向量。 - 阿莫尔
因此,在创建共同嵌入的向量之后,文档被定位成靠近其他相似的文档并且靠近最有区别的单词。

联合嵌入的单词和文档向量。图片作者。
一旦我们有了一组单词和文档向量,我们就可以进入下一步。
3.找到与每个分类类别的关键字向量相似的文档向量
现在我们可以计算文档和每个类别的手动定义的关键字之间的余弦相似度。与类别关键词相似的文档被分配给相应类别的一组候选文档。

分类类别关键字及其各自的候选文档集。每种颜色代表不同的分类类别。图片作者。
4.清除每个分类类别的异常文档
该算法使用 LOF 从每组候选文档中清除离群文档,这些文档可能与一些描述性关键字相关,但不完全匹配预期的分类类别。

红色文档是从候选文档集中移除的离群值。图片作者。
5.计算异常值清除文档向量的质心,作为每个分类类别的标签向量
为了得到分类类别的嵌入表示,我们计算标签向量。稍后,将使用文档与标签向量的相似度来对文本文档进行分类。每个标签向量由一个类别的异常清除文档向量的质心组成。该算法计算文档而不是关键词质心,因为实验表明仅基于与关键词的相似性来分类文档更加困难,即使它们共享相同的向量空间。

标签向量,计算为各个清除的候选文档向量的质心。点代表各个主题的标签向量。图片作者。
6.文本文档分类
该算法为数据集中的每个标签向量和文档向量计算标签向量< - >文档向量相似度。最后,文本文档被归类为具有最高标签向量< - >文档向量相似度的类别。

数据集中所有文档的分类结果。点表示分类类别的标签向量。文档颜色代表其预测的分类类别。图片作者。
Lbl2Vec 教程
在本教程中,我们将使用 Lbl2Vec 对来自 20 个新闻组数据集的文本文档进行分类。它是大约 20,000 个文本文档的集合,平均分布在 20 个不同的新闻组类别中。在本教程中,我们将重点关注由类别“rec.motorcycles”、“rec.sport.baseball”、“rec.sport.hockey”和“sci.crypt”组成的 20 个新闻组数据集的子集。此外,我们将为每个分类类别使用已经预定义的关键字。预定义的关键字可以从这里下载。还可以在 GitHub 上访问更多 Lbl2Vec 示例。
安装 Lbl2Vec
我们可以使用 pip 和以下命令安装 Lbl2Vec:
pip install lbl2vec
读取数据
我们将下载的“20newsgroups_keywords.csv”文件存储在与 Python 脚本相同的目录中。然后我们读取带有熊猫的 CSV,并从 Scikit-learn 获取 20 个新闻组数据集。
预处理数据
为了训练 Lbl2Vec 模型,我们需要对数据进行预处理。首先,我们处理用作 Lbl2Vec 输入的关键字。

我们看到,关键字描述了每个分类类别,关键字的数量可能会有所不同。
此外,我们还需要对新闻文章进行预处理。因此,我们对每个文档进行单词标记化,并添加gensim . models . doc 2 vec . tagged document标签。Lbl2Vec 需要标记化和标签化的文档作为训练输入格式。

我们可以在数据框中看到文章文本及其分类类别。“tagged_docs”列包含作为 Lbl2Vec 输入所需的预处理文档。“class_name”列中的分类类别仅用于评估,而不用于 Lbl2Vec 培训。
培训 Lbl2Vec
准备好数据后,我们现在可以在训练数据集上训练 Lbl2Vec 模型。我们用以下参数初始化模型:
- keywords_list :列表的可重复列表,包含每个类别的描述性关键字。
- tagged _ documents:gensim . models . doc 2 vec . tagged document 元素的可迭代列表。每个元素由一个文档组成。
- label_names :每个标签自定义名称的可重复列表。同一主题的标签名称和关键字必须具有相同的索引。
- similarity_threshold :只有与各个描述关键词的相似度高于该阈值的文档才用于计算标签嵌入。
- min_num_docs :用于计算标签嵌入的最小文档数。
- 纪元:语料库的迭代次数。
文本文档的分类
在模型被训练之后,我们可以预测用于训练 Lbl2Vec 模型的文档的类别。
[Out]: F1 Score: 0.9054393305439331
我们的模型可以预测正确的文档类别,得分相当可观,为 F1≈0.91 。这是在培训期间甚至不需要看到文档标签的情况下实现的。
此外,我们还可以预测未用于训练 Lbl2Vec 模型并因此对其完全未知的文档的分类类别。为此,我们从以前未使用的测试数据集中预测文档的类别。
[Out]: F1 Score: 0.889937106918239
我们训练的 Lbl2Vec 模型甚至可以预测新文档的分类类别,得分为 F1≈0.89 。如前所述,这是通过完全无监督的方法实现的,其中在训练期间不使用标签信息。
关于 Lbl2Vec 可用特性的更多细节,请查看 Lbl2Vec GitHub 库。我希望这篇教程对你有用。
摘要
Lbl2Vec 是最近开发的一种方法,可用于无监督的文本文档分类。与其他最先进的方法不同,它在训练期间不需要标签信息,因此提供了对无标签数据集运行低成本文本分类的机会。开源的 Lbl2Vec 库也非常容易使用,允许开发人员只需几行代码就可以训练模型。
来源
- 绍普夫,t。Braun d .和 Matthes f .(2021)。 Lbl2Vec:基于嵌入的预定义主题无监督文档检索方法,(2021),第 17 届网络信息系统与技术国际会议论文集
- https://github.com/sebischair/Lbl2Vec
具有 BERT 嵌入的会议的无监督主题分割(摘要)
NLP 研究论文演练

修改后的图片来自 来源
在这篇博客中,我试着根据我的理解总结了 使用 BERT 嵌入的会议无监督主题分割 这篇论文。请随时评论你的想法!
问题陈述
在过去的几年里,各种在线会议工具的使用突然激增,比如 Zoom、Google Meet、Microsoft Teams 等。几乎所有的时间你都会因为这样或那样的原因记录这些会议,但是因为它们的原始形式和某人操纵会议中讨论的要点所需要的痛苦,它们真的被再次看到或提及吗?我想不会。受这个问题的启发,脸书的研究人员提出了一种无监督的方法,使用预先训练的 BERT 嵌入将会议分割成主题。
会议背景下的主题分割是预测多人会议记录中主题边界的任务。作者在无人监督的情况下特别针对这项任务,因为大规模手动注释这些会议很难。
方法建议
假设我们有一份会议记录 M ,其中有 U 个总话语数和 T 个总话题边界数需要我们识别。这里 T 个话题中的每一个都是用 U 个连续的话语集合来表示的,姑且称之为可以用 U(i)到 U(i+z)来表示的块。这里, z 是块中考虑的连续话语的计数。现在以 M 和 U 作为输入,任务是预测长度为 U 的布尔向量,其中 0 表示话语 U(i)包含在正在进行的主题中,1 表示话语 U(i)是正在进行的主题的主题转移。
如前所述,作者利用 BERT 嵌入来获得会议片段的语义表示。他们为此测试了两种技术,其中一种是 max-pooled 预训练 RoBERTa 在架构中倒数第二层的嵌入。第二个是句子-BERT 模型(BERT 使用暹罗网络在 SNLI 数据集上训练)。
在此之后,他们将整个会议语料库分成大小为 z 的多个块,并通过对话语嵌入分层执行最大池操作来获得每个块的嵌入表示。在获得块表示之后,它们计算对话中相邻块表示之间的角距离。最后,基于某个阈值,他们给话语分配 0 或 1 值。
下图直观地说明了这一点—

提议方法的插图|作者提供的图片
我的想法
我觉得这确实是一项有趣、实用、同时又具有挑战性的任务。所提出的方法看起来很有希望,尽管在下一次迭代中,有趣的是看到围绕发言者的身份、会议的主题、专业领域地名词典等添加额外的特征将如何影响当前的分数。此外,观察使用非上下文嵌入时的分数变化也是值得尝试的。下一步可以尝试的一件更有趣的事情是提出一种自动标记这些主题的技术。
如果你愿意,你也可以 看看我写的其他研究论文摘要 。
随意看论文,对作者说“ Hi ”,感谢他们的贡献。此外,如果你喜欢看视频而不是文字(就像我:D 一样),一定要看看——
⏩ 论文标题:具有 BERT 嵌入的会议无监督主题分割
我希望你喜欢读这篇文章。如果你愿意支持我成为一名作家,可以考虑注册成为一名媒体成员。每月只需 5 美元,你就可以无限制地使用 Medium。谢谢!
揭示新加坡 2021 年预算与以往预算趋势的差异
R 中基于文本的分析

图片来源:石民·德( Unsplash
每年 2 月左右,我们的财政部长都会公布新加坡的财政预算。但预算报告通常相当冗长,长达 40-50 页。
此外,如果我们想研究过去的趋势如何演变,以了解政府的立场,我们可能必须手动阅读过去的预算报表,这是非常耗时的。
使用 R 和少量基于文本的分析,我们可以:(1)有效地获得 2021 年预算中的主要主题,以及(2)快速地与过去预算报表中的先前趋势进行比较,以研究政府的立场是否发生了变化。
具体而言,通过将《2021 年预算》与以前的预算进行比较,可以揭示政府在各种主题上的立场,如亲商、进步、气候变化和数字化工作。
这篇文章是上一篇文章的延伸,在这篇文章中,我通过查看新加坡过去 19 年(2002 年至 2020 年)的预算报表,分析了新加坡是否真的正在变成一个支持国家。
我将重复使用与以前相同的工作流程,做一些小的调整。
好了,让我们从代码开始吧!
数据
这项分析的第一步将是从财政部的网站下载最新的《2021 年预算》报表,并将其添加到我们过去的预算报表集合中。
我把它们保存为。txt 文件,因为它们以后会更容易处理。

作者图片
以下是我们需要的库:
**# Libraries**library(tidyverse)
library(tidytext)
library(readtext)
library(scales)
library(wordcloud)
现在我们将 R 中的工作目录设置为上面的文件夹,并读入。我们 20 个预算报表的 txt 文件:
**# Call the files from working directory into R**list_of_files <- list.files(path=".", recursive = TRUE, pattern="\\.txt$",full.names=TRUE)data <- lapply(list_of_files, readtext)
data1 <- do.call(rbind, data)
接下来,我们将句子分解成单个单词,并删除停用词,例如 the 和 a 。最后,我们计算每个单词的频率。
**# Unnest tokens**dtm <- data1 %>%
unnest_tokens(word, text)**# Clean the dtm, remove stopwords**dtm_clean <- dtm %>%
anti_join(get_stopwords())**# Get the words frequency**words <- dtm_clean %>%
group_by(doc_id) %>%
count(word, sort=TRUE)
粗略检查后,似乎有必要进行二次手动清洁。txt 文件似乎包含许多奇特的符号。
**# Secondary "manual" cleaning of the words data frame**droptext <- c(letters, 0:2021, "â", "ï", "page", "ii", "iii", "iv", "can", "also", "å", "ä", "ç", "æ", "è", "çš", "kg", "é", "year", "years", "must", "annex")**# Drop the words with 1-2 characters only**words1 <- words %>%
mutate(
numchar=nchar(word)
) %>%
filter(!word %in% droptext,
!numchar %in% c(1,2))
在第二次手工清理之后,我们在数据框中添加一个“年”列,并按升序对预算报表进行排序:
**# Create a new column in words1 which is the year column**words2 <- words1 %>%
mutate(year= as.numeric(str_match(doc_id, "\\d{4}")))**# Sort by year**words3 <- words2[order(words2$year),]
2021 年预算的主题
现在基础工作已经完成,我们可以开始提取见解了!
使用单词云有助于快速了解 2021 年预算的主要主题:
**# Word Cloud**wcloud <- words3 %>% filter(year == 2021)dev.new(width=1000, height=1000, unit="px")
wordcloud(words=wcloud$word, freq=wcloud$n, scale=c(3,0.2),
min.freq=2, max.words=80, random.order=FALSE,
rot.per=0.1, colors=brewer.pal(8, "Dark2"))

作者图片
一眼望去,Covid、就业、工人和企业占据了中心舞台,其次是数字化、技能升级和气候变化等主题。
这些主题也与亚洲新闻频道报道的《2021 年预算》标题一致。
【2021 年预算与以往预算趋势对比
《2021 年预算》与之前各种主题的预算相比如何,如亲商、进步、气候变化和行业转型努力?
危机期间的亲商立场?
有趣的是,我发现在经济不稳定时期,提及“商业相关”词汇的次数,如企业、商业、雇主、和公司,往往高于提及“工人相关”词汇的次数,如工人和雇员。
我们可以将“业务相关”单词和“工人相关”单词汇总如下:
**# Business/Worker ratio****# Create vector of "Business-related" & "Worker-related" words** selectedwords <- c("worker","workers","employer","employers",
"business","businesses","employee","employees",
"enterprise","enterprises","company","companies")words4 <- words3 %>%
group_by(word) %>%
filter(word %in% selectedwords) %>%
select(year, word, n)**# Classify words into "Business" or "Worker" group** words4 <- words4 %>%
mutate(
status = ifelse(word %in% c("employer", "employers", "business",
"businesses", "enterprise",
"enterprises", "company",
"companies"),
"Businesses", "Workers")
)words4 <- words4 %>%
group_by(year, status) %>%
summarise(n=sum(n)) %>%
spread(status,n)**# Rescale Businesses and Workers to a similar scale** words4$Businesses <- rescale(words4$Businesses, to=c(50,100))
words4$Workers <- rescale(words4$Workers, to=c(50,100))**# Create a ratio score: Businesses/Workers** words4 <- words4 %>%
mutate(
ratio = round(`Businesses`/`Workers`,2)
)
现在,我们可以使用 Loughran 词典创建一个不确定性得分,这对于分析带有金融术语的文本非常有用。
**# Use the Loughran lexicon to get Uncertainty score**
senti_dfloughran <- dtm_clean %>%
group_by(doc_id) %>%
inner_join(get_sentiments("loughran")) %>%
count(sentiment) %>%
spread(sentiment, n, fill=0)**# Add year column to senti_dfloughran to compare across time**
senti_dfloughran <- senti_dfloughran %>%
mutate(year=as.numeric(str_match(doc_id, "\\d{4}"))) %>%
filter(year >= 2002)**# Rescale uncertainty score to match the Business/Worker scale**
senti_rescale <- senti_dfloughran %>%
select(year,uncertainty)senti_rescale$uncertainty <- rescale(senti_rescale$uncertainty,
to=c(50,100))**# Correlation between uncertainty score & the Business/Worker ratio**
cor(words4$ratio, senti_rescale$uncertainty)
**#0.865**
在下面的图表中,我将每年预算报表的不确定性得分与企业员工比率进行了比较,企业员工比率是通过将“企业相关”词语的总频率除以“员工相关”词语的总频率获得的。
在给定的一年中,企业与员工的比率为 2 意味着“与企业相关”的词汇提及次数是“与员工相关”的两倍。

注:业务人员比率乘以 100,以便更好地与不确定性得分进行比较。
不确定性得分和企业与员工比率之间的相关性为 0.865,这意味着它们表现出强烈的协同运动。
虽然我们观察到在经济不稳定时期,企业与工人的比率似乎有上升的趋势,但这并不意味着政府优先考虑企业而不是工人。
通常,政府的危机管理策略包括为企业支付商业和工资成本,确保足够的信贷额度,同时提供一些家庭支持。
总体想法是首先让企业保持运转,进而避免危机期间大范围的裁员和失业。
新加坡越来越进步了吗?
如果我们观察“福利相关”词汇的使用趋势,如支持、帮助、提供、和补贴,并将它们与衡量收入不平等的基尼系数进行比较,我们确实发现了一种反比关系。
**# Create a vector of "Welfare-related" words**
selectedwords <- c("support","help","provide","give","subsidy",
"subsidies")words5 <- words3 %>%
group_by(word) %>%
filter(word %in% selectedwords) %>%
select(year, word, n)words5 <- words5 %>%
group_by(year) %>%
summarise(n=sum(n))**# Plot the trend of "Welfare-related" words**
words5 %>% ggplot(aes(x=year,y=n))+geom_line()+geom_point()+
xlab("")+ylab("")+
theme_bw()+
labs(title="Trend of Welfare-related words",
subtitle = "2002-2021")+
scale_x_continuous(breaks=seq(2001,2021, by=4))+
annotate("rect", xmin = 2015, xmax = 2021, ymin = 40, ymax = 300,
alpha = .1,fill = "blue")+
scale_y_continuous(expand=c(0,0))

注:蓝色阴影部分表示财政部长(2015 年至今)的任期

资料来源:基尼系数数据来自新加坡统计局(Ref Id: 12307,经合组织标度)
随着“福利相关”词语的使用增加,基尼系数逐年下降,这意味着收入不平等正在缩小,这可能是额外的支持措施和货币转移的结果。
“福利相关”词汇的使用在 2015 年左右达到高峰,此后一直保持在相当高的水平,这与 DPM Heng 担任新加坡财政部长的时间相吻合。
与此同时,基尼系数继续呈下降趋势。转移支付后的基尼系数在 2020 年大幅下降,这是因为向家庭发放了大量补贴和凭证以应对新冠肺炎。
气候变化是一种生存威胁吗?
如果我们看看“气候变化相关”词汇的使用趋势,如气候、可持续发展和环境,很可能政府在 2017 年左右开始认识到气候变化的严重性。
**# Environment & climate change****# Create vector of "climate change-related" words**
selectedwords <- c("green", "sustainable", "sustainability",
"environment", "climate")words6 <- words3 %>%
group_by(word) %>%
filter(word %in% selectedwords) %>%
select(year, word, n)words6 <- words6 %>%
group_by(year) %>%
summarise(n=sum(n))**# Plot the trend of "climate change-related" words**
words6 %>% ggplot(aes(x=year,y=n))+geom_line()+geom_point()+
xlab("")+ylab("")+
theme_bw()+
labs(title="Trend of Climate Change-related words",
subtitle = "2002-2021")+
scale_x_continuous(breaks=seq(2001,2021, by=4))+
annotate("rect", xmin = 2015, xmax = 2021, ymin = 0, ymax = 120,
alpha = .1,fill = "blue")+
scale_y_continuous(expand=c(0,0))

注:蓝色阴影部分表示财政部长(2015 年至今)的任期
我个人认为,自 2017 年以来,推动可持续发展的紧迫性增加了,这令人鼓舞。
政府今年还公布了“绿色计划 2030”,这将加快电动汽车和绿色债券融资的采用,因为新加坡寻求将自己定位为绿色中心。
新冠肺炎是否阻碍了政府对数字化的关注?
自 2010 年以来,供应委员会建议新加坡应优先考虑生产力,将其作为未来 GDP 增长的关键驱动力,进而减少对进口外国劳动力的依赖。
要实现生产率增长,公司必须采用数字化,工人必须提高技能。
从“数字化相关”词汇的使用趋势来看,如技能提升、创新、变革、和技能未来,这似乎是政府持续关注的焦点,并在 2015-2016 年左右有所增加。
**# Create vector of "digitalisation-related" words**
selectedwords <-c("skills","upgrade","upgrading","digital",
"digitalisation","restructure","restructuring",
"transform","transformation","upskill",
"upskilling","innovate","innovation",
"skillsfuture")words7 <- words3 %>%
group_by(word) %>%
filter(word %in% selectedwords) %>%
select(year, word, n)words7 <- words7 %>%
group_by(year) %>%
summarise(n=sum(n))# **Plot the trend of "digitalisation-related" words**
words7 %>% ggplot(aes(x=year,y=n))+geom_line()+geom_point()+
xlab("")+ylab("")+
theme_bw()+
labs(title="Trend of Digitalisation-related words",
subtitle = "2002-2021")+
scale_x_continuous(breaks=seq(2001,2021, by=4))+
annotate("rect", xmin = 2015, xmax = 2021, ymin = 0, ymax = 200,
alpha = .1,fill = "blue")+
scale_y_continuous(expand=c(0,0))

资料来源:总生产率(每实际工作小时的增加值);新加坡统计局(引用 Id: 16098)
2015-2016 年的激增很可能是由于全国技能未来计划的推出,该计划为工人提供信用以提升他们的技能。
技能未来计划今天仍然有效,是政府旨在提高我们缓慢的生产率增长数字的众多政策之一。
结论
在这篇文章中,我们应用基于文本的分析来研究新加坡的 2021 年预算,以及以前预算报表中的趋势,而不必手动阅读每份文件的 40-50 页。
通过单词 cloud,我们可以很快确定 2021 年预算中的关键主题与 Covid、就业、工人和企业有关,其次是数字化、技能升级和气候变化的主题。
从不确定性得分的下降可以看出,《2021 年预算》显示了谨慎的乐观。虽然这仍然是一个旨在支持工人和企业的扩张性预算,但重点已经慢慢开始从仅仅提供紧急财政援助转向为新冠肺炎后的增长重新定位经济。
结合过去的预算报表,我们还可以看到,进步、气候变化和数字化等主题仍然是政府前进的关键优先事项。
非常感谢您的阅读!
升级和重建主机
从零开始逐个任务地进行数据科学
在我最后一次通宵黑客攻击后,事情进展得很快

如果你开车并经历过左前爆胎,我可以很舒服地说,我知道你看到 Denny Müller 在 Unsplash 上发布的照片时的感受。在我的驾驶生涯中,我有过两次危险的井喷。有一次,在进入一个急转弯时,我的轮胎爆裂了,失去了控制,那真是太可怕了。最近,在另一个急转弯时,我撞到了一块岩石,粉碎了我的左前轮。一次完全不可思议且令人心痛的经历。当我说 “在我的日志文件中找不到支持 Cuda 的设备” 对我来说是一次同样痛苦和令人沮丧的经历,而且不像爆胎那样容易修复,也许你不一定同意。
如果你一直在阅读我的文章,你会从我的上一篇文章中知道我熬了一个通宵——在 Ubuntu 20.4 LTS 上配置 GPU 库。在我的数据科学机器上配置了 TensorFlow GPU 和 OpenCV 4.0,这很难抑制我纯粹的兴奋。不过,我并不那么有信心,就像那些瘪了的轮胎一样,事情可能而且经常会出错。从字面上看,我期望整件事都破裂, 果然如此。
依附神赢得了另一场战斗。让我们讨论一下发生了什么,以及我是如何解决的。这次我感觉更舒服了,但只有时间会证明一切。

发生了什么
我所知道的和表面上的事实如下。12 月 31 日,我安装了 TensorFlow 2.0 GPU,用 Cuda 库编译了 OpenCV。我还安装了 Virtual Box,并用一些 Ubuntu 和 Windows VM 做了一些工作。2021 年 1 月的第一周,一切都很好。由于一切都运行良好,我创建了一个 FastApi 端点并包装了我的 Yolo 类,为我提供了一个我可以使用的端点。我对这个设置很满意。使用 Curl 或 Postman,我可以向端点发送一张照片,并取回在 JSON 中检测到的对象。API 的响应时间非常好。我的下一步是集成 webhook 和来自 Motion Eye 设备的调用,使我走向实时触发和警报。我取得了这么大的进步,这看起来很疯狂。
我有一些专业的东西需要我 120%的注意力,但我记得 Ubuntu 上的一些更新,我选择在我的构建中实现它们。这似乎是一个错误的举动!更新后,我用我的鸟类检测相机,想知道 Yolo 是否会正确检测和分类当地的鸟类。所以我对我的 FastAPI 端点进行了 API 调用,并发出了第一张图片。在 Postman 中,我收到了来自端点的错误消息。查看日志, 我发现了“没有找到支持 Cuda 的 GPU”错误 。不管你是否同意我的比喻,我感觉我的心脏像被刀捅了一刀。这似乎是我人生旅途中的又一次爆胎。在某种程度上,我对配置和功能的损失感到难过。对于开发机器来说,这可能过于戏剧化了,但是对于生产主机来说,这个错误可能会很麻烦。
然而,不仅仅是我的深度学习工具链被破坏了! 虚拟框也开始给我报错 。所以我也不能使用我的虚拟机,这也很痛苦。我做过一些 OSINT ( 开源智能 )配置,也很享受这段旅程。
在这一点上,我不得不接受一些教训,让我来分享这些,这样你就可以反思你自己的实践。
我学到了什么?
对我来说,最大的问题是我不得不接受我熬夜的方式可能导致了我的问题。
第一件事是我已经在我的 Ubuntu 20.04 系统中添加了 Ubuntu 18.04 库。更糟糕的是,我甚至可能将更老的库添加回 16.04。我认为这真的是一个可怕的想法。在发出 sudo add-apt 之前,考虑对系统的影响可能是明智的。
sudo add-apt-repository universe
sudo add-apt-repository multiverse
sudo apt update
接下来,一些二进制文件中出现了错误和差异。Virtual Box 6 在从主机到客户或客户到主机的复制和粘贴方面存在一些问题。为了解决这个问题,我安装了 Virtual Box 5,但是我在系统上的 APT 库中混合了一些库。
对我来说,最大的教训是那句古老的格言— “如果它没坏,就不要修理它!” 这适用于在没有做任何研究的情况下接受可能不必要的更新或升级。那才是真正的杀手。
最后一个教训是,我们不能盲目地从教程中复制粘贴命令,从一个教程跳到另一个教程,直到有东西可以用为止!搜索谷歌并执行命令希望修复只会导致一个不稳定的系统和最终的 “没有找到支持 Cuda 的设备”。
我遇到的一些困难的根本原因似乎是 未签名的二进制文件,Ubuntu 要求所有二进制文件都要签名 。所以我猜是安装了一个安全更新,引发了旧版本的一些问题。
好吧,你可以说,我也听得见,这些东西很复杂,必须加载许多依赖项。这当然不容易,事情可能会出错。我是怎么修好的?嗯,我是通过 有条不紊的细心得到的! 那是底线。让我解释一下!我是怎么修好的
我是怎么修好的
解决我的问题包括几件不同的事情。首先,我必须接受教训,让自己适应用不同的方式做事。然后是一段时间的反思和对主机硬件的回顾。最后确定方向,执行计划,回到正轨。让我用简短的总结来解释这些步骤。
反映在主机上并定义所需的硬件
我的 Linux 数据科学工作站最初是为我女儿建造的游戏平台。它是专门为 在 最大性能 下玩模拟人生 4 而配置制作的。回想起来,我觉得那是大约 6 年前的事了。当我购买组件时,我的预算有限,所以我添加了一个当时已经被取代的 GPU。我得出的结论是,硬件需要检查和更新。
现在,你可能会说并认为这种升级是可以避免的。毕竟,我在 GTX 750TI 上编译了 CUDA Open CV DNN 模块,我对出错前我的端点的响应时间很满意。事实是我也有编译错误。考虑用 Cuda 构建 OpenCV 的 cmake 命令。
cmake -D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=/usr/local \
-D INSTALL_PYTHON_EXAMPLES=ON \
-D INSTALL_C_EXAMPLES=OFF \
-D OPENCV_ENABLE_NONFREE=ON \
-D WITH_CUDA=ON \
-D WITH_CUDNN=ON \
-D OPENCV_DNN_CUDA=ON \
-D ENABLE_FAST_MATH=1 \
-D CUDA_FAST_MATH=1 \
***-D CUDA_ARCH_BIN=7.0 \***
-D WITH_CUBLAS=1 \
-D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib/modules \
-D HAVE_opencv_python3=ON \
-D PYTHON_EXECUTABLE=~/.virtualenvs/opencv_cuda/bin/python \
-D BUILD_EXAMPLES=ON ..
特别是 CUDA_ARCH_BIN=7.0,就是麻烦的那一行。同样,您不能只是复制粘贴并执行这个 cmake 命令。在我的系统上,CUDA_ARCH_BIN = 5.0

摘自 Nivida 的《GPU、CUDA 库和计算能力指南》
尽管如此,用 CUDA_ARCH_BIN = 5.0 编译 Cuda 10,OpenCV 4 只是产生了 Cuda 编译错误。于是我尝试了 Cuda 9,用 CUDA_ARCH_BIN = 5.0 打开 CV 3。然后我得到一条错误消息,告诉我 5.0 不是允许的值,需要 CUDA_ARCH_BIN > 5.3。所以你知道这一切将走向何方。 我很难解释甚至回忆起我有多少次擦系统,安装了一个全新的 Ubuntu 安装,调用了 CMake,安装了 TensorFlow GPU。TensorFlow 有很好的记录,并不是真正的问题;相反,我努力编译 OpenCV 来获得我的 CV2 Python 与 Cuda 的绑定。最后,我决定是时候换一个新的显卡了。
我目前的 GPU 是 GTX 750Ti,根据英伟达的说法,这是“第一代英伟达 Maxwell 架构”。如果您愿意,可以在 NVIDIA 产品网站上找到完整的规格。最新的 GPU 架构是 Ampere——NVIDIA 的第二代 RTX 架构,在 NVIDIA 的产品特定页面 有很好的描述。现在,从第一代 Maxwell 转向第二代 RTX 将需要一大笔钱,这也将是痛苦的。痛苦在于,需要用许多啤酒代币来换取另一个 GPU,而这些卡供应有限,在新冠肺炎全球疫情期间不可能在爱尔兰买到。
云计算与内部部署
我们都会抓住的另一个论点是 IBM、Google、Oracle、AWS 和 Microsoft 上可用的 GPU 实例。我们应该租用 GPU 实例,而不是购买、安装和配置本地工作站。这个问题永远不会有直截了当的答案。这将归结为项目,可用的预算,安全和隐私的要求。
将我的前门系统连接到公共云,我会感到不舒服。想想这篇文章“几十个人说他们在家里的智能摄像头被黑后受到了死亡威胁、种族诽谤和勒索。”来自守护者。
考虑运行一项服务所需的系统数量至关重要。这些是:
- 生产实例;
- 预生产、试运行或用户测试环境;
- 集成测试代码的开发环境;
- 面向个人工程师的开发机器
当我们从环境数量的角度来看这种情况时,使用基于云的服务的价值就变得更加明显了。当我们谈论一个个人工作站来做实验、设计服务和写书或写论文时,云并没有多大意义。我在 AWS 上有基于 GPU 的虚拟机,但偶尔我会收到巨额账单,因为我忘了关闭开发机器。
考虑到所有的事情,我决定购买一张新卡,并安装在我现有的系统中。我很了解云的论点,但我也知道事情可能会很快失控。
制定计划
做出购买新卡的决定后,我开始寻找我能得到的东西。这里的新冠肺炎疫情比以往任何时候都更糟糕,此外,我们还受到英国退出欧盟的影响。经过一番寻找,我设法得到了一个 Quadro P2000 交付。对我来说,Quadro P2000 产品描述包含了一些要点。它拥有一个“ Pascal GPU,拥有 1024 个 CUDA 内核,超大 5 GB GDDR5 板载内存“ ,来自 Nvidia 产品页面。
从第一代 Maxwell 架构转移到 Pascal 框架允许我从 计算能力 5.0 跳到 6.1。对我来说,就所涉及的金钱而言,这似乎是合理的。有了新设备,剩下的就是安装卡并让它工作! 执行计划老兄!
执行计划
由于工作站是我自己建造的,所以我很容易就能打开盖子,接触到机器的内部。移除几个螺丝让我可以将 GPU 从插槽中取出。请不要忘记, 主板有一个锁紧机构 ,在插卡之前必须打开。否则,你会弄坏东西的!安装卡、更换外壳和连接外围设备非常简单。我没有把我的显示器直接插入 GPU 卡,因为我的 CPU 芯片上也有一个 GPU。在训练深度神经网络时,最好移除屏幕工作负载,甚至直接启动到终端。使用带有 UI 的计算机对 GPU 来说是一个很大的工作量,会降低速度。 你不能一边看《网飞》,一边你的深度神经网络在同一台机器上训练!
我决定最好清除硬盘,安装 Ubuntu 18.04 作为新的安装程序,重新进行配置。环顾四周,阅读 NVIDIA CUDA 文档,显示对 Ubuntu 20.04 的参考相对较少,所以我认为我应该继续使用 18.04。
为了配置tensor flow 2.0我使用了以下教程
https://www.pyimagesearch.com/2019/12/09/how-to-install-tensorflow-2-0-on-ubuntu/
为了配置 OpenCV DNN 和 Cuda 我使用了这个教程
对于虚拟盒子,我使用的是版本 6.0.24,如下所示,但我已经得到了 关于更新版本 的通知。抵抗 不是徒劳的! 忽略它!

作者显示“关于虚拟框”对话框的图像

作者展示虚拟盒子升级通知的图片。
双向复制和粘贴功能 不工作 ,但我已经决定生活没有这些。在我的新 Ubuntu 系统上安装 Virtual Box 5.0 失败。
这次 我没有添加任何新的库 。没有 sudo apt-add!但是我已经从 Ubuntu 18.04 得到了两个“系统问题”的报告。我又一次担心这个新版本会很快遇到问题。
靶心——一切又恢复了。OMG!我的 Yolo 课程终于又开始工作了,这是多么美妙的一刻啊。 '模块不是用 CUDA 编译的!'再也没有了! 下面你可以看到一段处理要使用的设备的代码。
coprocessor = {
'***cuda***': {
'backend' : cv2.dnn.DNN_BACKEND_CUDA,
'target' : cv2.dnn.DNN_TARGET_CUDA
},
'***cpu***': {
'backend': cv2.dnn.DNN_BACKEND_DEFAULT,
'target': cv2.dnn.DNN_TARGET_CPU
},
'***myriad***': {
'backend' : cv2.dnn.DNN_BACKEND_INFERENCE_ENGINE,
'target' : cv2.dnn.DNN_TARGET_MYRIAD
}
}

随着系统的工作,但很明显,操作系统级的问题存在,我可以再次考虑前进。
走向
要回到我原来的位置,还有很多工作要做。 Nginx 必须安装, Gunicorn,FastAPI ,我需要重新做 我的 OSINT 虚拟机 。但这是个好日子!我的 GPU 升级了,性能也提高了。当然,我安装可视代码是我做的第一件事!为了说明新的性能,我进行了一些测试。
from myYoloC import myYolo
my = myYolo(device='cuda')
a = my.run()
myY = myYolo(device='cpu')
a = myY.run()
之前我创建了一个名为 myYolo 的类,在上面的代码中,我简单地用两个不同的设备创建了该对象的一个实例。示例“my”是用 Cuda 后端和分配给 CPU 的“myY”模型制作的。五个代码 行简直可以击垮我的精神, 但是我必须勇敢的去执行它们。关于可视代码,我非常喜欢的一个特性是虚拟环境支持。

作者展示可用 Python 环境的图片
我的 opencv_cuda 环境包含了用 cuda 编译的 OpenCV 4.0 版本。我的“dl4cv”虚拟环境具有 TensorFlow GPU 配置。这与 pyimagesearch.com 的指导是一致的。
选择正确的环境允许我运行代码。

代码执行后作者从可视代码中获取的图像
因此,从磁盘加载 Yolo,并向前传递 192 张照片,拍摄了 7.57 秒的,平均每张照片花费了0.039 秒。这是新 GPU 的出色表现。在 CPU 上花费了 44.75 秒,平均每张图片 0.23 秒。这也不错,但是用户不会喜欢这样的延迟。围绕 CPU 和 GPU 信令,当然还有两个代码片段中的模块加载,大约有 7 秒钟的构造和解构造任务。**
当我在 GTX 750TI 上运行同样的代码时,我得到了不同的答案。这毕竟是第一代麦克斯韦和帕斯卡,所以如果答案是一样的,我会感到惊讶。正如下面的截图,从我的上一篇文章中可以看出,Maxwell 的 GPU 平均值为 .13,而 Pascal 的 GPU 平均值为. 04。那大约是 时间 的 1/3。太神奇了!同样的工作 在老款 GPU 上需要 31 秒,而在新卡 上只需要 14 秒。

图片由作者基于前一篇文章。
无论我们安装什么样的 GPU,基于 CPU 的处理都不会改变。我没有在 Linux 机器上安装 OpenVino,所以我不知道 Intel Myriad 设备会怎样。在 Raspberry Pi 板上,我们知道 Myriad 很快。
****
图片由前一篇文章的作者提供
从上面的图片中,右上方显示了基于 CPU 的 ARM 处理器,配备了 Raspberry Pi 4 8GB——这项工作花费了32 分钟,一切都变得很紧张。在 Myriad 上,左上 同样的工作量用了 3 分钟 。我猜这些统计数据证明了为什么使用 Pascal 架构对我如此有吸引力。装备更好的 Linux 工作站可以在 14 秒内用 192 幅图像进行正向传递神经网络推理。这些事实都让我非常兴奋,继续我的任务,使用 Yolo、计算机视觉和廉价的基于 Raspberry Pi 的运动检测器来计数经过的车辆。
蛋糕上的糖衣也许可以用 TensorFlow 来演示
**import tensorflow as tf
print(tf.__version__)
print(tf.test.is_gpu_available())**

图片由 is_gpu_available()的结果作者提供
is_gpu_available() = True
是不是很神奇?有一种轮胎被修理过的感觉。我车上所有的轮子都是对的,我在开阔的道路上行驶。尽情享受吧!

照片由 KAUE FONSECA 在 Unsplash 上拍摄
作为数据科学领域的职业成年人的技能提升
我在一个月内完成了 6 门在线课程,以下是我的方法

作者照片
如果你是数据科学从业者或与数据科学团队密切合作,你会意识到总是需要继续提升工作任务或有趣的副业项目的技能。

作者照片
数据科学领域非常广泛,各行业不断推出新的平台/包。数据科学领域的任何职位都需要继续保持和掌握新技能。取决于任务的紧急程度,学习-应用-遇到障碍的循环可能会加快。
9 月,我开始担任数据科学主管的新职务。这从一整套不同的挑战开始,例如处理几 TB 的数据、可视化这些数据的趋势以及管理利益相关者的期望。
随着向新角色的转变,我利用这个机会提升了自己应对新挑战所需的一些基本技能,并在一个月内完成了 6 门课程。

作者照片
现在,任何订阅过在线课程的人都知道,完成整个课程并不是一帆风顺的。一个 4 小时的课程不需要花 3 个小时来完成。将它乘以 3 倍的可能性更大,或者它可能永远不会完成,因为大多数人中途停下来,让它无人照管。

作者照片
这里有一些关于如何完成在线课程以及如何在这个过程中最大化学习的有价值的提示。
内容
- 看一眼终点线
- 设定雄心勃勃的目标
- 每天一口的大小
看一眼终点线
激励我们参加在线课程的一件事是知道在终点线等待我们的是什么水果。
我强烈建议在订阅课程后立即执行这一步骤。成为流氓,直接滚动到最后几章,看看高级话题是什么样子的。
现在,回想一下,所有通向大结局的章节都是完成一个相对更复杂的任务的基础。

肯·苏亚雷斯在 Unsplash 上的照片
这将培养我们作为学习者理解较小模块的毅力,并有助于将我们推向终点线。
设定雄心勃勃的目标
在成年人的工作生活中,参加在线课程不再仅仅是为了通过考试。我们也可能没有人在背后给我们施压,要求我们完成一门课程。这有时会导致我们一旦认为我们学得足够多,甚至更糟,失去兴趣,就放弃了。
为了克服这一点,在开始时设定一个雄心勃勃的最终目标可以提供完成课程的动力。

目标是不同的,通常对学习者来说是个人的,促使他/她首先学习一门课程。然而,我们的目标不应该是一个唾手可得的果实,而是一个令人兴奋的、当我们想到它时会让我们夜不能寐的果实。
供参考的一些目标:
- 通过添加认证来提升专业形象
- 提高完成经理分配的任务的能力
- 构建有趣的辅助项目
每天一口的大小
在 Coursera 、 Linked in Learning 或 Udemy 等学习平台上选修任何一门课程,你会发现这门课程被精心制作成了一个个小章节。每个单元持续时间为 3 至 10 分钟。这是有意在合理的时间内吸引学习者的注意力。
我发现致力于学习在线课程的一个有效策略是利用平台的功能。目标是在更短的时间内完成更小的里程碑。对我来说,这意味着每天坚持几个小节。

作者照片
或者,可以在 LinkedIn Learning 上设定每周目标。这很好地让我们保持在正轨上,如果我们比计划晚了,可以在周末进行冲刺。

作者照片
订阅一门课程并不仅仅是为了走完这段旅程并完成它。对于数据科学从业者来说尤其如此。这里的目标是最大限度地学习特定主题,并将其应用于我们面临的棘手问题。
学习,弄脏你的手,创造出令人敬畏的产品。

作者照片
下次见!
成为最受欢迎的数据科学家
成为成功的数据科学家的全面技能列表

随着技术的发展,保持行业领先地位所需的技能也在发展。过去几年来,成为一名数据科学家一直是许多人的梦想。不用说,这是一份高收入的工作。你们中的一些人可能是数据科学家,其他人可能会在这个领域寻找职位。在这篇简短的文章中,我将列出现代数据科学家的技术技能要求。如果你是一名在职的数据科学家,你将拥有其中一些技能,但是,你可能需要在许多新的领域提高技能。如果你是一个初学者,这个全面的列表将帮助你规划成为一名数据科学家的职业生涯。
我先说最本质的技巧。
编程语言的选择
任何机器学习项目,像任何其他软件项目一样,都需要选择合适的编程语言。没有一个软件开发者会想到用 Fortran 做商业应用;同样,您永远不会在科学应用程序中使用 COBOL。也就是说,每种编程语言在设计时都有特定的目的。
我不需要解释,但是 Python 是大多数 ML 开发的选择。如果你调查一下招聘信息,你会注意到 Python、SQL 和 R 可能是最热门的。我会推荐 Python。如果你是一个经验丰富的开发者,考虑一下 Julia 的高性能。
这里最重要的问题是,你需要了解 Python 到什么深度?初学者可能会惊讶地发现,每一个机器学习代码都只有几行——不到 100 到 200 行。框架代码在所有 ML 应用程序中保持不变。这是完整的骨架代码(算法):
- 数据清理
- 数据预处理
- 探索性数据分析
- 特征工程
- 创建培训、测试和验证数据集
- 选择 ML 算法
- 培养
- 评估模型的性能
训练后,如果您对模型对看不见的数据的推断感到满意,您可以将模型移动到生产服务器。
上述培训和测试代码在所有 ML 项目中保持不变;改变的只是 ML 算法。尽管如此,根据您的数据类型和您试图解决的问题,每个步骤都会有所不同。然而,顶层视图在所有项目中保持不变。我希望你说到点子上了。对于编写这种模型训练代码,您不需要掌握很深的 Python 技能。我很少在我的 ML 项目中使用 Python 的面向对象和函数式编程。
与其他编程语言形成对比。如果不使用这些高级特性,您将永远无法开发出高性能的软件应用程序。在 ML 中,什么是高性能?即使模特训练需要额外的时间,我也没意见。对我来说,重要的是结果,也就是模型的准确性。第二,通过使用函数式和面向对象的代码,我会让见习开发人员更难读懂。因此,我总是坚持简单的基本代码,即使是新手也能很容易理解。
你甚至可以为 400+ ML 算法找到现成的模板。使用这样的服务将会剥夺你写一个框架代码的工作。
接下来的技巧是机器学习库的选择。
机器学习库
scikit-learn 是许多 ML 开发者的选择。scikit-learn 为预测建模和分析提供了大量工具。需要用熊猫,NumPy,Matplotlib/Seaborn 来补充 sklearn。NumPy 和 Pandas 是创建机器可理解的数据集、要素/目标选择和创建数据集所必需的。Matplotlib 和 Seaborn 将协助 EDA。
任何 ML 项目中最大的挑战是选择一个合适的算法,最终给你看不见的数据最好的预测。下面是帮助你选择最佳算法的 AutoML。
AutoML
对于算法选择,请使用 AutoML。AutoML 是一个工具,它可以将几个 ML 算法应用到您的数据集,并按照性能准确性的升序对它们进行排序。使用 AutoML,您不需要开发那些核心数据科学家的专业知识。在学习过程中节省了很多——你只需要学习各种算法背后的概念以及它们解决了什么问题。
即使是 ML 的初学者也可以学习使用这些工具来帮助选择最佳性能的模型及其所有微调的超参数。有一个巨大的清单,自动和超参数调整库。我已经生成了一个完整的列表供您快速参考。
AutoML 库/工具:
Autosklearn , TPOT , Autokeras , MLBox ,autoglon, autoweka , H2OAutoML , autoPyTorch ,亚马逊 Lex , DataRobot , TransmogrifAI ,Azur
这一领域值得研究的新成员是 BlobCity AutoAI 。我想提到的最有趣的特性是,它泄露了生成模型的项目源,数据科学家可以声称这是他自己的创造。已编辑—2022 年 1 月 17 日
超参数调节库/工具:
Optuna ,hyperpt, Scikit-optimize , Ray tune ,randomsearccv, GridSearchCV ,微软 NNI
我不想为你背书或推荐一个。你可以自己尝试一下。在 ML 开发中,算法选择和微调超参数是最艰巨的任务,而这些工具的使用使这变得很容易。
接下来是统计建模和人工神经网络之间的选择。
经典 ML 和 ANN 之间的选择
在这里,我可以向你推荐我之前发表在 Medium 上的文章现代数据科学家的建模方法,或者你可以观看 YouTube 上关于 @blobcity 频道的演示,它讲述了数据科学家在建模中采用的方法。
如果你更喜欢神经网络而不是经典的 ML,开发者的首选是 TensorFlow。它不仅仅是一个人工神经网络开发库,而是一个完整的平台,允许您创建数据管道,使用分布式培训来减少培训时间,根据您拥有的资源使用 CPU/GPU/TPU,允许在本地服务器、云甚至边缘设备上部署。出于您的善意考虑,我可能会让您参考我的书以了解更多细节,其中也包含 25 个以上基于 ANN 的真实项目。
另一个流行的 ANN 开发工具是 PyTorch。
迁移学习
训练 DNN 需要巨大的计算资源,数周的训练时间,以及数百万张图像或数兆字节文本数据的数据集。许多科技巨头创造了几个 DNN 模型来解决某一类问题。这些公司创造的模式,在这个世界上没有人能做到。
这里有一个全面的列表供您快速参考。
图像分类 : ImageNet,CIFAR,MNIST
计算机视觉的 CNN 模型 : VGG19,Inceptionv3 (GoogLeNet),ResNet50,EfficientNet
NLP 机型 : OpenAI 的 GPT-3,Google 的 BERT,微软的 CodeBERT,ELMo,XLNet,Google 的 ALBERT,ULMFiT,脸书的 RoBERTa
幸运的是,这些公司已经公开了这些模型。作为一名数据科学家,您可以原样重用这些模型,或者扩展它们以满足您的业务需求。我们称之为迁移学习。他们为特定的目的训练每个模型。你只需要在将他们的学习转移到你的模型之前理解它。
现在出现了一个重大的变化,它将改变我们多年来开发模型的方式。
动态模式
到目前为止,我们一直在静态数据上开发 ML 模型。在今天的疫情情况下,你认为这些大约 2 年前根据静态数据训练的模型会帮助你预测你的客户访问模式和他们新的购买趋势吗?您需要根据新数据重新训练模型。这意味着所有基于静态数据开发的模型都必须定期重新训练。这需要消耗资源、时间和精力。这需要额外的成本。
于是就出现了动态建模的概念。您可以根据流数据不断地重新训练模型。您在流中收集的单位时间内的数据点数量将决定窗口大小。解决方案现在可以归结为时间序列分析。动态建模的主要优势之一是您可以使用有限的资源进行模型训练。在静态建模的情况下,它们需要您在内存中加载大量的数据点,这也会导致很长的训练时间。
开发流数据模型需要额外的技能。看看 PySpark。您需要将一些 web 框架应用到您的模型中。请注意,您会持续训练模型,并使用它进行即时实时预测。web 框架应该能够产生快速响应。
在我的下一篇文章中,我将更深入地讨论动态模型。
颠覆性创新来了。
MLaaS
最后,我必须提到 MLaaS,它可能会取代数据科学家。亚马逊、谷歌、IBM 和微软等科技巨头提供完整的机器学习服务。
你只需要把你的数据上传到服务器上。他们做全部的数据预处理、模型建立、评估等等。最后,他们只是给你一个模型,可以直接移植到他们的产品服务器上。尽管这对数据科学家来说是一种威胁,但我仍然建议数据科学家应该习惯这些新技术,并为其所有者提供快速解决方案。毕竟,让老板满意有助于你保住工作。
摘要
我已经为您提供了在当前数据科学状态下成为顶尖数据科学家所需技能的全面观点。随着技术的进步,你不需要掌握每一项技能。比如到目前为止设计的统计 ML 算法就有几百种。您现在可以使用像 AutoML 这样的工具来为您的业务用例选择一个性能最好的。甚至就编程语言而言,你已经看到,要成为一名 ML 开发人员,你并不需要成为 it 专家。
人工神经网络的成功揭开了 ML 发展的新篇章。有几件事现在被过分简化了。创建数据管道、自动特征选择、无监督学习和在万亿字节数据上训练网络的能力只是 ANN 技术的几个优点。作为一名数据科学家,您需要获得这些技能,如果您目前还没有这样做的话。使用 DNN 和预先训练的模型进一步简化了数据科学家的工作。
另一个出现的重要需求,可能是因为当前的流行病,是需要开发动态模型。作为一名数据科学家,他们现在要求你开发流数据模型。开发高频数据的动态模型是当前研究的主题。
最后,你可以选择 MLaaS 作为快速解决方案。
祝你好运,努力成为最受欢迎的数据科学家。
你可能会喜欢在 YouTube 上观看我的演讲。
信用

城市健康第 2 部分:预测发展中国家的粮食不安全

这个故事是我的城市健康教程的第二部分。在这个故事中,我们将使用各种机器学习技术预测发展中国家的粮食不安全状况。你可以在这里找到第一部分:https://towards data science . com/healthy-cities-in-the-us-part-1-food-62b 9724467 E8
工具和数据
- Python,Jupyter 笔记本,Sklearn,Pandas,Numpy,Seaborn,Scipy
- 数据:https://data.humdata.org/dataset
该数据集是通过收集 28 个国家的食品数据创建的,包含以下变量:
'Dietary_Adj', 'Food_Production', 'Dietary_Supply', 'Cereal_Supply',
'Average protein supply (g/cap/day) (3-year average)',
'Average supply of protein of animal origin (g/cap/day) (3-year average)',
'GDP', 'Prevalence of undernourishment (percent) (3-year average)',
'Number of people undernourished (million) (3-year average)',
'Severe_Food_Insec', 'Mod_Sev_Inad',
'Number of severely food insecure people (million) (3-year average)',
'Number of moderately or severely food insecure people (million) (3-year average)',
'Cereal import dependency ratio (percent) (3-year average)',
'Percent of arable land equipped for irrigation (percent) (3-year average)',
'Value of food imports in total merchandise exports (percent) (3-year average)',
'Political stability and absence of violence/terrorism (index)',
'Per capita food production variability (constant 2004-2006 thousand int$ per capita)',
'Per capita food supply variability (kcal/cap/day)',
'Percentage of population using at least basic drinking water services (percent)',
'Percentage of population using at least basic sanitation services (percent)',
'Percentage of children under 5 years affected by wasting (percent)',
'Percentage of children under 5 years of age who are stunted (percent)',
'Percentage of children under 5 years of age who are overweight (percent)',
'Prevalence of obesity in the adult population (18 years and older)',
'Prevalence of anemia among women of reproductive age (15-49 years)',
'Prevalence of exclusive breastfeeding among infants 0-5 months of age',
'Prevalence of low birthweight (percent)'
数据清理和预处理
初始数据包括 2003 年至 2019 年期间约 28 个国家及其食品相关指标。这是一张快照:

如您所见,这些指标分布在各个行上,每一行都代表该特定指标一年的价值。让我们转换数据,将这些变量作为列名:
以下是更改后的数据集:

在我们继续可视化之前,我们还需要修正 NaNs 和 N/A 类型值。使用此函数可以轻松解决前者:
为了固定 N/A 虚拟值,我将它们替换为 1,然后取每列中其他非 1 值的平均值,如下所示:
(英)可视化(= visualization)
现在我们有了一个干净的数据集,让我们更熟悉一下这些数据。我们将寻找正态分布,因为正态是机器学习数据的一般假设。首先,这是一张所有国家平均膳食能量供应充足度的图表,符合一条相对正常的正态分布线:

接下来,让我们看看另一个变量是如何分布的,即“中度和重度食物不足”,它看起来也聚集在 40 左右,但仍然是正常的:

接下来,我们希望这些变量中的许多变量相互关联,因为它们是不同的饮食指标。让我们使用热图 : 来想象一下

正如你所看到的,诸如膳食供应等变量与食物不足呈负相关,这是可以预料的。然而,国内生产总值与膳食充足性的相关性很弱(0.58),这可能令人惊讶。
线性回归
现在,我们可以开始让模型适合我们的数据集。我们将使用数据中的所有列(除了我们正在预测的那一列)来预测平均膳食能量供应充足性。我们可以从一个简单的线性回归模型开始:
我们使用的测试规模为数据的 0.2 或 20%,随机状态等于 27。让我们看看斜率和截距是多少:
- 系数(斜率):56660 . 68686868661
- 截距:【4.64360863 e-03,3.89312086e-02,-6.49480051e-02,-1.59175835e-02,
1.38100769e-02,-1.06957325e-04,7.38095045e-02,6.35949018e-03
为了衡量我们的模型有多好,我们可以使用 sklearn 的一些内置指标:
- 均方误差
顾名思义,均方误差衡量的是误差平方的平均值。误差被定义为实际值和预测值之间的差异。线性回归模型的误差约为 2.72。与以下两个指标一起,它可以用来确定一个模型有多好。
- 绝对平均误差
平均绝对误差是误差绝对值的平均值,在我们的模型中,它约为 1.65。
- r 的平方
R square 衡量数据点与模型预测线的接近程度。分数越高,模型的表现越好。
里脊回归
我们也可以使用岭回归来预测我们的变量。当数据存在多重共线性时,通常使用岭回归,即当一些预测变量与其他预测变量相关时,其主要好处是避免数据的过度拟合。这个模型可以很容易地构建成这样:
现在,让我们看看山脊模型的三个指标:
- r 的平方值:0.9662149081149515
- 平均绝对误差:1.6。36860.68686868661
- 均方误差:2。38860 . 68868686861
R 平方值略低于线性回归模型,因为平均绝对误差和均方误差非常相似。我们能进一步调整这个模型以提高它的性能吗?
测试各种α值
在这里,我们将查看一组将传递给模型的 alpha 值,然后构建一个散点图来查看它们的 R 平方值:

如您所见,alpha 的最佳值是 0.001 和 0.01,因为它们产生最高的 R 平方值。
套索回归
另一种回归技术是套索回归。它与岭回归有相似的用例,因为它也减少了过度拟合。拟合该模型的代码如下:
以下是 lasso 回归的指标:
- r 的平方是:0.964876876866
- 平均绝对误差:1.646868686866
- 均方误差:2.864886888866
lasso 回归的指标稍差,尤其是误差值。我们可以使用不同的 alpha 值来优化模型吗?
测试各种α值
让我们像处理岭回归一样输入多个α值。下面是 alpha 数组(0.001,0.01,0.1,1,10)与均方误差的散点图:

如您所见,alpha 值为 0.001、0.01、0.1 时,错误率最低。
随机森林
作为我们的最后一个模型,我们将为数据集拟合一个随机森林回归量。这方面的代码如下:
以下是衡量标准:
- r 的平方是:0.987686868866
- 均方误差:1.00003863686
- 平均绝对误差:1.000038686866
需要注意的一点是,随机森林模型的误差值似乎有所减少,这可能会使其更好地用于现实世界的数据集。
做预测
现在我们已经建立了几个模型,我们可以用它们来做一些预测。让我们挑选一些国家,比如塞尔维亚和印度尼西亚,看看这个模型对他们的平均膳食能量供应充足性的预测是什么:
从上面可以看出,模型预测值分别为 119.07 和 114.52。现在我们已经建立了一些模型并对它们进行了评估,是时候继续建立神经网络来进行与食物相关的预测了。这将是本系列的第 3 部分。感谢您的阅读,您可以在这里找到代码:
城市中的数据科学:一个故事

美国宇航局在 Unsplash 拍摄的照片
问题:如何利用数据科学来帮助我们更好地了解发展中的城市?
你好中号!
数据科学是一个跨学科领域,其大部分应用仍未开发。数据科学可以帮助改善的领域之一是城市规划和发展。例如,我一直对“智能城市”的概念很感兴趣,也就是被设计成尽可能高效的城市。在本教程中,我们将看看地拉那,阿尔巴尼亚的首都,也是我的家乡。现在,地拉那远远不符合“智能城市”的描述——事实上,地拉那在很大程度上是一个发展中国家的首都:熙熙攘攘,脏乱不堪,充满活力。
通过与城市中的其他人交谈,我听到最多的一个抱怨是城市过于拥挤:交通太拥挤,建筑太多,绿色和休闲空间太少。所以我想,我们能不能用数据科学来更仔细地看待这个说法?让我们开始吧:
首先,指导这一过程的几点:
- 地拉那的人口密度在世界城市中排名很高吗
- 地拉那是否缺少绿色空间(也就是说,没有足够的空间供居住在那里的每个人使用)?
要使用的库:
让我们看看地拉那的人口数量和密度。对于这部分,我将使用来自 opendata.tirana.al 的 GeoJSON 数据。地拉那的城市分为 11 个行政区,每个行政区有不同的面积和人口数:“states”是一个 GeoPandas 数据框,显示每个地区及其相应的对称性,“pa”显示每个地区的 2020 人口。

管理员。区域及其各自的多边形

每个行政区的人口(在“人口亚洲 _2020”下)。区域
现在,让我们通过使用人口除以表面积来查看每个区域的密度,如下所示:首先,我们创建一个数据框架,其中包含每个行政区域的人口和表面积数据。然后,我们计算密度,将它们作为一列添加,并将几何多边形添加到数据帧中。在此基础上,我们绘制数据帧,用不同颜色显示密度:

如你所见,这些区域有不同的密度。但是什么是平均密度,它与不同的世界城市相比如何?下面,您可以找到返回平均密度为 20229.54889607044 的 Python 计算,以及从联合国收集的城市密度数据集:
现在,让我们使用条形图绘制该数据集,x 轴为国家,y 轴为密度:

正如你所看到的,地拉那的城市在列表中名列前茅(请记住,这一数据仅涉及城市人口,尽管地拉那包括 8 个农村地区)。
现在,让我们看看地拉那的绿色空间。为了回答它们是否足够多的问题,我们首先需要像上一节中的多边形一样可视化和绘制数据。我找不到地拉那公园的任何 GeoJSON 数据,所以我访问了 Geojson.io 并手工绘制了这些区域,然后将其导出到 Geojson 文件中。然后,我可以用 geopandas 读取该文件,结果如下:

多边形显示地拉那的公园和娱乐区
为了测量有多少绿色空间,我决定使用 shapely 来计算每个多边形的面积。基本上,坐标列表包含了所有绿色空间的几何形状,但是是以弧度的形式,然后 area_greens 计算坐标中每个元素的面积,单位是平方公里。
如果我们把面积加起来,大约有 4.02815753409 平方公里。地拉那市区总面积为 41.8 平方公里。因此,地拉那大约有 10%的绿地。具体来说,它与其他世界城市相比如何?嗯,看看下面排序的数据集,地拉那的身材排名最接近巴黎(令人惊讶!)和布宜诺斯艾利斯。

世界城市,“数字”是绿色区域的百分比
现在我们知道了地拉那与其他城市相比如何,我们如何决定是否有足够的绿色空间给它的每个居住者?为了回答这个问题,我们需要有某种考虑到绿地比例和密度的指标。绿地比例除以人口密度怎么样?为了解释这一点,我们可以认为大的值意味着人们有更多的绿色空间,小的值意味着更少的空间。下面的单元格对地拉那和数据集中的其他城市进行计算,然后绘制结果:

我们计算的度量标绘了
结论:
正如我们在这个简短的教程中看到的,与其他世界城市相比,地拉那看起来确实人口过剩。此外,该市居民认为该市缺乏绿色空间的想法可能是正确的,在比例和比例/密度这两项指标上排名都很低。
- 另外,如果您对本教程的代码感兴趣,可以在下面找到它:
感谢阅读!
(几乎)免费使用量子云
实用量子计算 AWS Braket 101

演职员表:佩克斯公司的弗拉达·卡尔波维奇
这个故事是关于地理和社会包容的!
地理包容性:量子计算机目前并不便宜(我们谈到了 1500 万美元的投资),更不用说设置它的知识了。好消息是,地球上的任何人都可以通过云平台开始使用量子计算机。所以,不管你住在挪威、美国、秘鲁还是博茨瓦纳。只要你能上网,你就能在量子电路上运行程序。不幸的是,它们不是完全免费的,但是它们的价格是可以接受的(例如每次运行 0.3 美元)。
社会包容:在这篇文章中,我将介绍其中一个平台。这不是一个过于全面的指南,我也没有将这个平台与其他平台进行比较。我想让你展示在量子电路上运行程序是多么容易。需要量子物理博士吗?显然这样更好,但是如果你只是一个高中生,你可以从这里开始,如果你喜欢,你可以在将来继续。还有,姑娘们这不是书呆子气的东西,你们可能会喜欢:-)所以我们先从亚马逊 Braket 说起。
1.初始步骤
Amazon Braket 是一个易于在线访问的平台。这是一个量子计算平台,允许访问来自不同公司(D-Wave、IonQ、Rigetti)的多个量子设备,使用它的一种方式是通过 Braket Python SDK 。我会指导你基本的步骤。老实说,我更喜欢 IBM QPU(实际上完全免费)和 Qiskit,但我认为正是因为这个原因,我将来会在 dept 教程中写得更多。无论如何,这里让我们首先关注 Braket。
首先,打开浏览器,进入网址https://aws.amazon.com/braket/
然后,您必须选择一个北美云和一个数据存储库。亚马逊 Braket 将您的算法结果存储在任何亚马逊 S3 桶中。
我们可以选择量子处理器 (D-wave,IonQ,Rigetti)或者模拟器。D-Wave 和 Rigetti 有几种选择。也有可能那些量子机器暂时没有。在您选择之前,让我们先简要了解一下这些连接的设备。这个仪表板实际上更多的是可能性的概述,实际的选择是通过编码进行的。

亚马逊 Braket 初始截图
D-Wave —优势系统
Advantage quantum processor unit(QPU)基于量子位和耦合器的物理点阵,称为 Pegasus。飞马架构包括一个重复的结构,其中每个量子位耦合到其他相对排列的量子位。一个基本单位细胞包含 24 个这样的量子位。
D-wave 机器的一个特别之处在于它是一个量子退火器,特别适合优化解决方案。退火是一种寻找全局最小值/最大值的元启发式算法。

8 个量子位回路(黄金)的示意图。蓝点是允许量子位交换信息的 16 个耦合元件的位置。在数学上,这些元素将你希望计算机解决的问题中的变量结合在一起。演职员表: D-wave
IonQ
IonQ 是一台通用门量子计算机,带有俘获离子。更具体地说,有 11 个由镱制成的量子位,带有由激光定义的陷阱链。尽管只有 11 个量子位,但这种拓扑结构非常有趣,因为该设备是完全连接的,这使得量子位交互非常有效。使用来自锁模脉冲激光器的一对反向传播光束,通过双光子拉曼跃迁给出门。

IonQ 拓扑的亚马逊市场图
还有关于校准保真度(两个量子态有多接近的度量)的信息:
{
“braketSchemaHeader”: {
“name”: “braket.device_schema.ionq.ionq_provider_properties”,
“version”: “1”
},
“fidelity”: {
“1Q”: {
“mean”: 0.99717
},
“2Q”: {
“mean”: 0.9696
},
“spam”: {
“mean”: 0.9961
}
},
“timing”: {
“T1”: 10000,
“T2”: 0.2,
“1Q”: 0.000011,
“2Q”: 0.00021,
“readout”: 0.000175,
“reset”: 0.000035
}
}
这意味着我们有 99.7%的机会为一个量子位应用一个门读出与预期相同的状态。
里盖蒂
Rigetti 是另一台带有超导量子位的计算机。它有 30 个量子位(大于 10 个量子位),但拓扑结构完全不同:

Rigetti Aspen 8 的 Amazon Braket 地块
并非所有的量子位都是相互连接的,这需要在量子位上编程 c-零门时特别注意。例如,我们将需要交换门,这将增加错误测量的机会(量子电路仍然容易出错)。至于 IonQ,Braket 也提供校准信息,包括每个量子位的相干时间。
T1 和 T2 是 cubit 失去相干性需要多长时间的时间量化(可以存储的时间量子信息量)。
T1 是自旋晶格弛豫时间,而测量一个量子位漂移回基态需要多长时间。T2 的退相时间也叫 自旋弛豫或横向弛豫。 当一个量子比特处于某个状态,但它开始绕轴旋转时,就会发生去相位(而这显然是不好的)。为了实现高精度,目标是使这些值尽可能高。
模拟器
Braket 还提供了一个多达 34 个量子位模拟器。模拟器只是一台经典计算机伪装成量子计算机。然而,它可以用来测试电路,以及当我们在量子电路上运行它们时,它们的成本是多少。不幸的是,模拟器也不是免费的,但它肯定比真正的量子计算机便宜。
在 Braket 行话中,镜头是一个程序运行的次数(这对成本也有影响)。
一旦你理解了所有这些设置,你就可以启动一个笔记本和一个程序。由于价格是由时间和镜头给出的,请记住,您使用笔记本的次数越多,价格越高(不要忘记关闭实例,如果您不在乎,我不负责您的账单)。
2.笔记本实例
点击左侧菜单“笔记本”进入笔记本部分:

现在点击橙色按钮“创建笔记本实例”。
提供实例名称、实例类型以及是否加密的选项。一旦您启动它,将需要几分钟时间(您可以在菜单中看到“待定或“服务中”)。
当你看到“服役中”时,你可以点击名字,从那里打开 Jupyter 笔记本(在那里你可以看到一些例子)。

如果你想创建一个新的脚本,点击角落,你可以创建一个新的笔记本(选择 conda_braket)。如果你是全新的,Jupyter 笔记本正在编写网页脚本,我们可以在其中运行 Python 脚本。如果你完全不懂 Python,最好先学一些基本语法(import,notation…)。
我通过一个简单的 Python 脚本(就像一个“量子 Hello World”)来指导你,在最相关的一行(以 bell=…)我们建立了一个具有两个量子位的 bell 态。贝尔态是两个量子位的四个特定最大纠缠量子态。
首先在 Box1 中选择量子计算机,导入特定的库,然后我们在 Box2 中定义贝尔态。框 3 中的“运行方法”是实际启动任务的代码行。
在这个例子中,我选择了一个模拟器:

然后将电路提交给本地模拟器并获得结果

要在 quantum 处理器上运行,请使用以下代码行:


如果我们在模拟器上运行脚本,你会得到上图中绘制的结果,这是因为第一个量子位处于零状态,受控的非门对第二个量子位没有做任何事情,第二个量子位仍然处于零状态。然而,量子位处于一个状态,它将第二个量子位从零状态翻转到一个状态。第一个量子位处于相等的叠加状态,这意味着它有 50%的几率坍缩到零状态,有 50%的几率坍缩到一。
在真实的量子机器上运行的结果可能与 50–50 略有不同,因为真实的量子硬件可能会有误差。这确实是一个独立的话题,目前有几个解决方案正在调查。
在 Jupyter 笔记本的示例文件夹中,还有一个子文件夹,其中有更多关于 D 波 QPU 和网络分析的有趣示例(如图形划分、最小顶点覆盖问题等)

永远记得停止实例,费用很低,但如果你让它每天或每月运行;这将是昂贵的,所以选择实例并停止它:

3.如何从这里继续
为了保持超级拼版的主题,你可以在这里看到 Python 中使用其他量子计算库的相同 Bell 对示例。
如果你想了解更多关于 Braket 云系统的信息,起点大概是亚马逊 Braket 指南。
关于其他量子云解决方案(Azure 和 IBM)的简要介绍,请查看
https://medium.com/technology-hits/azure-quantum-computing-b7b9e009de7a https://medium.com/illumination-curated/is-quantum-machine-learning-the-next-thing-6328b594f424
结论
有了 AWS Braket,你可以真正启动你的量子计算脚本。这是一个让你真正接触量子计算机和模拟器的合理方法。这样,任何人,即使没有量子物理/计算博士学位,在地球上的任何地方(从挪威到卢旺达,从智利到印度),任何人都可以成为量子开发者。你需要网络连接和信用卡上的一些美元。显然,这还远远算不上高级研究。为此,你需要更多的量子位、更少的噪音和更长的相干时间。11 或 30 个量子位太小,无法实现量子计算机的算法,声称量子优势有用(如寻找整数质因数的肖尔算法,,即使它们在例子中可用)。IBM Quantum cluster 和 Azure 给出了其他替代解决方案。无论如何,这个行业还处于起步阶段,希望所有的量子云在(不久的)将来会更有效率。
想连接就连接



https://web.facebook.com/dralecrimi
主动学习促进你的智力问题
你也可以看看我在 FWdays 大会上的演讲和视频关于主动学习和弱监督用法解决 NLP 问题

迷因作者使用了 imgflip.com上的照片
很难否认,解决机器学习(ML)问题的最广泛和最有效的方法是香草监督学习。在过去的十年里,算法改进了很多,特别是深度学习模型。然而,模型的成功在很大程度上取决于数据的质量和数量,而这反过来又需要时间、财力和人力资源。这里可能会出现并发症。
如果您对开发成功的数据科学解决方案感兴趣,但遇到资源不足的问题或没有足够的资源来获取标签,那么这些材料正适合您!即使你或你的公司有足够的(财政)资源来获得标签,这篇文章仍然是有益的。即使在 Wix 这样的大公司,我们有一个完整的内部标签部门,我们仍然在获得令人满意的高质量标签数据方面遇到问题。
考虑可能的情况:
- 您的问题涉及特定领域的数据,如司法文档或生物数据。要获得标签,你需要高度合格的专家;
- 你不能给你的数据贴上廉价众包平台的标签,因为它有 NDA 下的敏感信息;
- 你没有很多数据——例如,不平衡的数据集和罕见的类;
- 你需要训练一个更复杂的模型,比如神经网络,对数据特别饥渴;
- 贴标签的过程非常耗时,您希望以某种方式加快它的速度。
所以,不管上面发生了什么情况,你都没有足够的标签数据。
在这里,主动学习成为焦点!
有了这个神奇的工具,您在数据科学任务中可能需要更少的手动标记样本,而不会损害甚至提高性能!
在Wix.com,我们用它来解决文本分类(业务分类)问题,对用户支持请求、对象和肖像分割进行情感分析。
还有,除了贴子,你还可以回顾一下准备好的 Colab 笔记本,用编码弄脏自己的手:)
开始吧!
目录
数据集和起始组件
在本帖中,为了简单易懂,我们将使用经典的垃圾短信收集数据集【1】【2】来检测垃圾短信。该数据集包含大约 5200 个样本。班级比例如下图 1 所示。
我们将该数据集分为三个部分:
- 取 20% 作为测试集。
- ~75% 作为短信池,我们假设这里没有标签。
- 剩余的(~ 200 个样本)作为带标签的初始训练集—种子。
我们将用一个初始的小训练集来模拟标签短缺的情况,并展示主动学习将如何解决这个问题!
作为模型,我们将采用 TF-IDF 矢量器和简单的逻辑回归。作为受监控的指标—F1—对“垃圾邮件”类别的评分。

图 1 —垃圾短信数据集中的类别比例—按作者分类
主动学习框架
拥有用于模型训练的无尽资源、时间和金钱——您只需收集所有可用的数据信号,并为每个信号分配一个标签。但遗憾的是,这是不可行的。现在,想象一下,你的数据科学模型非常聪明,它可以决定它喜欢和想要训练的数据。当然,该模型没有关于数据的情感,但是仅在显著影响其性能的样本上训练该模型仍然是一个好主意。这实际上就是主动学习所做的。
AL 背后的主要思想是允许我们的模型以" smart 的方式选择用于训练的数据。在 AdaBoost 算法中使用了类似的想法,其中它将更高的权重分配给信息更丰富的样本,即模型犯更多错误的样本。我们将为模型提供最丰富、最复杂的例子。因此,由于标记更少的数据点,训练过程的成本将被优化。
有不同类型的主动学习[ 3 ]。我们将专注于最流行的一种——基于池的类型的主动学习(AL)。
让我们试着找出常规的 ML 和主动学习用法的 ML 之间的区别。

图 2——经典学习和主动学习 ML 对比——作者图片

图 3——主动学习周期——作者的图片,灵感来自[ 3 ]
从上图中可以看出,经典的监督训练和基于池的主动学习的区别在于查询策略——如何选择训练集数据进行标注。
此外,不要忘记 ML 过程本质上是迭代的——如果模型评估结果不够好——您可以返回并决定是添加更多数据还是玩模型。虽然在常规 ML 中,您可能会以第一次迭代结束,但在主动学习中,您肯定会有两次迭代——您将设置批处理大小参数——您将在一次迭代中标记多少数据样本。
主动学习的提问策略
理解算法的最好方法就是编码。因此,我们将在这里实现选定的查询策略。
你可以尝试各种已经实现了不同查询策略的主动学习软件包(比如 modAL 或者 ALiPy )。但是,如果您不需要复杂的 POC 或想要创建快速 POC,请自己编写代码。
我使用了本教程【5】中的实现思想。
首先,我们需要定义一个只有一个抽象方法的元类——“选择”。
基本查询策略
随机选择
我们用来选择数据进行标记的天真方法是随机抽样——只是从未标记的池中随机查询样本——这实际上是被动学习。与其他非随机选择相比,它有一个显著的优势,因为您的训练集分布将反映现实,尽管它有其他明显的问题,如高成本。
下面看看如何实现“选择”方法( pool_len 是未标记池中数据样本的数量, num_samples —我们的批量大小,我们将在一次迭代中标记多少样本):
不确定性采样:余量选择
另一组最流行的查询策略是不确定性采样——在模型不确定的地方选择样本。对于支持概率输出的模型来说,这是一个非常简单的概念,比如逻辑回归,而对于只输出[0,1]范围内的分数的模型来说就不那么简单了。
在 边界选择 — 不确定性抽样查询策略之一的情况下,最不确定的样本将是两个最高置信度预测之间的差值最小的样本(概率最高)。
保证金分步选择的实施:
- 对于未标记池中的每个样本,将其预测概率从最大到最小排序:
2.计算两个最大概率之间的差值:
3.取值最小的样本(在数量 _ 样本数量中)——最不确定的样本:
要了解更多关于查询策略的信息,请阅读本主动学习手册http://burrsettles.com/pub/settles.activelearning.pdf***【3】。*****
想了解更多主动学习 python 的包—看这个 调查【4】。****
如何选择查询策略
不同的查询策略会选择不同的样本;因此,需要选择您的模型将从中受益最多。
模型不支持 概率输出。在这种情况下,您可以考虑不使用概率的查询策略,如按委员会查询。此外,请记住:'最不确定的实例位于分类边界上,但并不“代表”分布中的其他实例,因此知道其标签不可能提高整体数据的准确性 ' [3]。这就是为什么即使您的模型具有概率输出,您也可能不仅希望使用基于不确定性的查询策略。**
****不平衡的数据集。当您有异常/欺诈检测任务或不平衡的数据集时,这种情况下的“次要”类别通常更重要。在这种情况下,基于相似性或密度的查询策略会更有用。
****分散或高密度特征分布。如果查询策略试图增加数据方差并获取最不相似的样本,它可能会查询异常值,这不会提高模型性能。
****每次迭代,模型性能不会改变。一些查询策略是贪婪的——在一次迭代中,它们将选择信息含量相似的数据点。因此,我们需要选择一种查询策略,它不仅要选择信息量最大的数据,还要选择信息量多样的数据。例如,您可以组合几个查询策略或者使用批处理感知策略[ 7 ]。
不要忘记冷启动。开始时,试着使用一些查询策略,而不是专注于某些特定的策略。****
幸运的是,您不局限于现有的查询策略。此外,您可以创建适合您的特定问题的查询策略。不要害怕使用定制的查询策略,例如,基于一些规则或启发。你会看到:这很容易做到!
如何定制查询策略
正如你已经知道的,中心点是给模型最有价值的数据实例。您可以决定在特定问题中哪些样本可以提供信息。您可能对某个特定的类感兴趣,而您的模型很难检测到它。或者,在错误分析之后,您已经知道了模型的弱点,并打算利用它们。
经典的基于不确定性的查询策略选择接近 0.5 预测概率值的样本。但是,如果您将决策阈值从默认的 0.5 更改为另一个值会怎么样呢?现在,您可能对落在阈值附近的概率值感兴趣。然后,您可以创建策略来选择接近阈值的样本。
区间内的最大误差
另一种了解模型在哪里表现更差的方法是查看每个不同预测概率值的误差分布直方图,并找到具有最大错误分类量的最有问题的概率范围。

图 4 —不同概率范围导致的误差分布示例—按作者分类
例如,在上面的图中,我们可以说 (0.3,0.4] 概率值区间是误差最大的区间。
基于此,我们将实现一个查询策略,从具有最高错误数的概率范围中选择样本:
- 将模型预测的概率分成 10 个(随意设置另一个数字)大小相等的区间。
2.在训练集上,进行预测并计算每个区间的错误数。
3.选择错误数量最多的时间间隔。
4.从未标记的池样本中选取,这些样本的预测概率落在从第 3 步选择的区间内。
这种“最大误差”查询策略可以用于任何分类问题。
文本相似度查询策略
在 Wix,对于网站分类任务,我们遇到了有数十亿个 Wix 网站的问题,例如,要找到瑜伽教练网站,您需要随机选择并标记大量数据。因此,有了 Yoga 站点示例的小种子,我们选择只标记与种子有相似文本的站点。如下所示,我们能够以低得多的成本标记和构建大量类别的数据集!
现在,试着回答这个问题:对于垃圾短信检测来说,哪些样本的信息量最大?
在大多数垃圾邮件检测应用中,正确的“垃圾邮件”检测至关重要,因为我们只有 13%的垃圾邮件样本,所以我们可以说“垃圾邮件”类别比常规的非垃圾邮件样本更重要。
因此,这里有一个基于相似性的查询策略,它使用文本嵌入来计算数据点的相似性,并根据您的需要选择特定的样本。
我们可以使用简单的 TF-IDF 或 BOW,预训练的单词嵌入,如 TensorFlow 的通用句子编码器或任何其他先进的预训练嵌入(BERT、LaBSE 等)。).为了使用文本嵌入为我们的“垃圾邮件”类获取最相似的样本,我们还必须定义一个相似性度量。我们决定使用通用语句编码器嵌入和余弦相似度度量。****
现在,让我们一步一步地编写这个查询策略:
0.首先,准备嵌入函数。
- 从我们已经训练好的数据中获取“垃圾邮件”样本并嵌入它们。
2.计算未标记池的嵌入。
3.计算来自步骤 1 的“垃圾邮件”样本和来自步骤 2 的池嵌入之间的余弦相似性。
4.从未标记的库中获取具有最高相似性的样本。
仅此而已!您可以通过选择与整个训练集最不相似的样本(以增加数据集方差)或选择与所有类相似的样本(如果认为这样的样本最棘手)来修改此查询策略。
如您所见,定制查询策略不是火箭科学,而是您可以发挥创造力并从中获得乐趣的领域!
完整管道
既然我们已经实现了查询策略,那么是时候将它们放入主动学习循环并训练模型了。
- 定义分类器:
2.在我们的初始小标签集上训练分类器。
3.对未标记的池进行预测:
4.使用所选的查询策略进行选择(例如,边距选择):
5.将带有标签的选定数据样本添加到已经存在的初始训练集:
6.从库中删除所选项目(因为我们已经将这些数据点添加到训练集中):
7.在更新的训练集上训练分类器,并计算更新的度量。
8.重复步骤 2-7,直到您获得满意的结果或耗尽资源。
结果
最后,让我们在一个使用不同查询策略的测试集上回顾一下模型性能。

图 5 —主动学习结果—按作者分类的图表
在图 5 中,我们可以看到,如果我们标记整个未标记池,我们会得到一个87%F1-分数。因此, 87% F1 将是分数基线——因为这是你标注所有数据后得到的表现。****
利用余量选择,在 14 次迭代(140 个数据样本)上实现相同的性能。对于 207 行的初始测试,总共有 347 个样本。那可是比少十倍的数据!****
在下表中,您可以看到使用不同的查询策略获得 87% 基线 F1 分数需要多少个标签。边距选择的性能几乎与区间选择中的最大误差相同,而文本相似性选择稍差,但仍然非常好。
表 1:主动学习实验结果
总之,无论我们使用哪种查询策略,我们都可以用更少的数据标签获得良好的性能。
在 SMS 案例中,最好的是边缘选择策略,但请记住,我们对一个非常简单的分类问题进行了实验,并使用了逻辑回归模型,该模型给出了校准良好的概率输出。在真实的情况下,仅使用基于不确定性的查询策略可能无法获得如此好的性能。
摘要
让我们总结一下我们在上面所做的一切:
- 主动学习可以显著减少标记数据点的数量(在我们的例子中,数据几乎减少了十分之一!);
- 主动学习查询策略可以针对特定任务轻松定制和优化。
在您的数据科学任务中,有许多方法可以克服数据瓶颈。你只需要选择一个最适合你的问题。在这篇文章中,我们用主动学习工具成功地解决了这个问题。
致谢 非常感谢 Gilad Barkan、Olga Diadenko 和 Lior Sidi 对本文早期版本的反馈!
参考
[1] UCI 机器学习知识库:垃圾短信收集数据集
【2】Almeida,T.A .、GÃ mez Hidalgo,J.M .、Yamakami,A. 对垃圾短信过滤研究的贡献:新的收集和结果 (2011),2011 年 ACM 文档工程研讨会会议录(DOCENG'11)。
【3】毛刺落定。主动学习文献调查 (2010),计算机科学技术报告 1648,威斯康星大学麦迪逊分校。
【4】亚历山大·亚伯拉罕。主动观察主动学习包——来自渠道的数据 (2020)。
【5】Ori Kohen。主动学习教程 (2018)。
[6]陈育坤、托马斯·A·拉斯科、朱樵·梅、约书亚·C·丹尼、华旭。临床文本中命名实体识别的主动学习方法研究 (2015),生物医学信息学杂志,第 58 卷,第 11-18 页。
【7】丹尼尔·吉辛。批量主动学习|判别主动学习 (2018)。
使用并增强这个 Python 类来下载 Excel 工作簿,并为分析做准备
使用 Python 类 c_download_prep_excel 从网站上自动下载 excel 电子表格,并准备用于数据分析项目

免费图片,由 Pixabay.com 提供。
介绍
在最近的一篇文章中,我分享了一个从 analytics.usa.gov 的 T2 下载报告的 Python 类。这些文件提供了有关公众如何访问大约 5,700 个美国政府网站的数据。他们是干净的,同质的设计,并准备在画面中可视化。
相比之下,美国联邦调查局(FBI)在其网站上公布的一些 Excel 文件则存在挑战。我需要转换他们的数据,然后才能在数据分析项目中使用它们。本文介绍了一个可重用的 Python 类,称为 c_download_prep_excel,它从网站上下载 excel 工作簿(如 FBI 发布的工作簿),并重新构造它们的数据,为数据分析项目做准备。
这种技术和这里介绍的 c_download_prep_excel 类可以用来从其他站点下载和处理 excel 文件。任何人也可以修改这个类来处理不同来源的 Excel 文件和执行其他任务。
联邦调查局报告样本
我使用了 from 2019 年暴力犯罪统计报告来开发和测试 c_download_prep_excel 类。单击下载 Excel-表 1 以检索 Excel 工作簿。

美国联邦调查局 2019 年美国犯罪报告。图片来自 FBI 网站。
报告问题
下面显示的示例 Excel 工作表还不能用于数据分析。虽然可以接受在 Excel 中查看,但应该对其进行清理和转换,以使其适合分析。以下是它的一些问题:
- 第 1 行到第 3 行包含元数据的标题信息。
- 第 25 到 31 行包含页脚信息,这些信息也是元数据。
- 第 1 列中的某些年份包含向年份值添加数字的上标。
- 数据的矩阵格式不适合加载到数据分析工具中。

联邦调查局暴力犯罪报告。作者拍摄的图片。
解决方案
为了准备用于数据分析的报告,我编写了一个 Python 类和程序,将数据从原始工作表转录到新工作表中。结果格式如下所示,每行一个数据点(年份、犯罪类型和犯罪率的组合)。

将联邦调查局暴力犯罪报告中的数据转换成可供分析的格式。图片由作者提供。
可以根据需要使用和增强该类,以便从 FBI 网站或其他网站下载和准备其他 Excel 文件。本文后面给出的示例程序执行这些步骤:
- 将 Excel 工作簿从 FBI 网站下载到本地文件夹。
- 如果工作簿是 xls 文件,请以 xlsx 格式保存一份副本。
- 保持 xlsx 文件的原始工作表不变。相反,创建一个新的工作表,其中包含所需格式的数据。以下步骤描述了将工作表 19tbl01 中的数据转录到新工作表中。
- 创建三个列名:年份、犯罪类型和犯罪率。
- 对于犯罪类型,在第 1 列中每年写一行,在第 2 列中写犯罪类型。
- 将包含上标的年份值截断为四位数。
- 将每种犯罪类型的犯罪率数据从原始工作表转录到新工作表中。
- 右对齐第 1 列和第 3 列的列名。
- 设置列宽。
本项目中使用的工具
我使用了以下工具来开发程序。
- Python 3.9.2
- 微软 Excel 365
- Microsoft Visual Studio 社区
程序、c_download_prep_excel 类和 Python 模块
该计划
下载和准备用于数据分析的 Excel 工作簿的程序由文件 process_fbi_data.py 中的控制器模块和文件 c_download_prep_excel.py 中的 c_download_prep_excel 类组成。process_fbi_data.py 中的代码控制程序。控制器创建 c_download_prep_excel 的一个实例,并调用它的函数将 FBI 报告文件的原始工作表中的数据转录并转换到一个新的工作表中。
c _ 下载 _ 准备 _excel 类
c_download_prep_excel 类执行控制器请求的任务,如上面的解决方案部分所述。它可以根据需要进行修改和增强,以满足特定的要求。
程序控制器和 c_download_prep_excel 类代码见 附录 A 。
Python 模块
程序调用 openpyxl 模块中的函数来创建新的工作表并格式化数据。该程序还调用 pyexcel 函数,这些函数使用 pyexcel-xls 和 pyexcel-xlsx 将 xls 工作簿复制到 xlsx 工作簿。
从命令行运行下面列出的命令,或者如果支持,在您的集成开发环境(IDE)中运行这些命令,以安装这些模块:
- pip 安装 openpyxl
- pip 安装 pyexcel
- pip 安装 pyexcel-xls
- pip 安装 pyexcel-xlsx
Python 包括程序使用的操作系统和请求模块
Tableau 公共仪表板
我将支持分析的 Excel 工作表加载到 Tableau Public 中,并创建了一个简单的仪表板来显示各种犯罪类型的趋势。
潜在的改进
该程序很好地执行了它的基本任务,从 FBI 报告电子表格中准备暴力犯罪数据用于数据分析。以下增强功能可以使程序为生产做好准备,并扩展其处理不同格式的各种 Excel 工作簿的能力。
- 添加错误处理来从严重错误中恢复或者优雅地关闭程序。
- 将程序活动写入日志文件,以跟踪程序的启动、完成、错误和动作。
- 向类中添加函数以执行额外的格式化并应对新电子表格带来的挑战。
- 从其他文件或数据库中查找并替换数据。
- 执行额外的数据清理。
结论
Excel 电子表格可以从网站上手动下载,并手动转换成可供分析的形式。但是程序可以自动完成这项任务。当需要下载多个文件并准备在数据分析项目中使用时,这是非常有益的。另外,如果设计良好,您可以在将来为类似的任务重用 Python 类和程序。
关于作者
Randy Runtsch 是一名数据分析师、软件开发人员、作家、摄影师、自行车手和冒险家。他和妻子住在美国明尼苏达州东南部。
关注 Randy 即将发表的关于公共数据集的文章,以推动数据分析见解和决策、编程、数据分析、摄影、自行车旅行等。你可以在 shootproof.com 和 shutterstock.com 看到他的一些照片。
附录 A —源代码
程序的 Python 控制器代码和 c_download_prep_excel 类代码如下所示。请随意复制代码并根据您的需要进行修改。
使用注释来讲述更好的故事

以及如何使用plotly在 Python 中实现
俗话说,一幅画胜过千言万语。在数据科学中,我们经常使用不同类型的情节来探索数据或讲述故事,通常是针对非技术观众。
数据可视化通常被认为是一项独立的技能,需要花费专门的时间和精力来掌握。几乎任何人都可以运行一行程序来创建一个简单的情节。但是有许多技术或细节可以极大地改善图形,强化我们想要传达的信息。
有许多很棒的文章和书籍致力于该主题,例如, 用数据讲故事:商业人士的数据可视化指南 (免责声明:引用链接)。在这篇短文中,我们将关注一个可以极大地帮助理解故事的方面——情节注释。
关于封面故事的一些背景信息
你很有可能已经听说过这个故事,即使你可能对加密货币没有明确的兴趣。2021 年初,埃隆·马斯克发布了几条关于 DOGE 的推文,DOGE 最初是作为一个笑话而创造的。这些推文对 DOGE 的价格产生了很大的影响,使其暴涨。我们将仔细看看硬币的价格,以及它是如何受到推文的影响的。
引发这一切的推特
用 Python 探索 DOGE 价格
和往常一样,我们首先需要导入库。对于本文,我们将使用绘图库plotly。这是因为我发现它是最容易实现注释的。然而,这在其他库中也是可能的。
我们将使用yfinance库下载 2021 年 1 月至 4 月的 DOGE-USD 历史价格。关于如何使用这个库的更多细节,你可以参考我以前的文章。我们还计算了每日百分比变化,以供进一步分析。
下载的数据如下所示:

作者图片
我们可以使用以下代码片段轻松创建 DOGE 收盘价的交互式线图:

作者图片
我们确实可以看到 DOGE 的价格是如何随着时间的推移而变化的。在那 4 个月里,价格增长了 65 倍!使用plotly的交互式工具提示,我们可以详细了解任意一天的价格。
注释的一个方便的用例是指出埃隆·马斯克在哪一天发推文。然后,我们可以清楚地观察到价格是如何响应这些推文而变化的(注意:推文本身并不是价格变化的唯一原因)。
首先,让我们定义注释。
在这个列表中,我们提到了埃隆·马斯克在 2021 年的 4 个月里发布的 4 条推文。对于每个注释,我们指定以下内容:
x-用于注释的 x 轴值。在这种情况下,我们使用推文的日期。y—用于注释的 y 轴值。这里,我们使用当天的收盘价。text—注释的标题。在这里,我们使用的是埃隆·马斯克的推文内容。arrowhead—表示我们想要使用哪种形状的箭头
我们可以使用的一些附加参数:
arrowsize—决定箭头的大小ax/ay—用于沿轴移动标注。当我们不希望注释相互重叠或与线条重叠时,这就很方便了。
当我们准备好列表时,我们可以使用update_layout方法将注释应用到图中。
运行该行会更新该图,如下所示:

作者图片
通过注释,我们可以清楚地看到 DOGE 的价格在推文发布当天以及随后几天的表现。有趣的是,当我们查看 2021 年之前的价格历史时,与推文发布后的暴涨相比,价格基本持平。
注意:我们也可以使用fig对象的add_annotation方法来添加注释。参数与上面指定的参数相同。我们使用了update_layout方法,因为一次添加多个注释更方便。
通过稍微修改包含注释(y 轴值)的列表,我们可以得到相同的图,但 DOGE 价格的每日百分比变化除外。

作者图片
正如我们所看到的,tweets 和随后的几天可以被描述为 DOGE 价格的异常高的每日百分比变化。
外卖食品
- 在情节上使用注释可以极大地帮助向观众传达故事,尤其是非技术性的观众,
- 在 Python 中,注释可以添加到任何类型的绘图中,但是要记住,不同库之间实现的细节是不同的,
plotly提供了一种非常简单的方法来给它的图形添加注释。
你可以在我的 GitHub 上找到本文使用的代码。此外,欢迎任何建设性的反馈。你可以在推特或评论中联系我。
喜欢这篇文章吗?成为一个媒介成员,通过无限制的阅读继续学习。如果你使用这个链接成为会员,你将支持我,不需要额外的费用。提前感谢,再见!
您可能还会对以下内容感兴趣:
[## 用 Python 创建交互式烛台图表的最简单方法
towardsdatascience.com](/the-simplest-way-to-create-an-interactive-candlestick-chart-in-python-ee9c1cde50d8)
参考
使用贝叶斯定理在列表中查找不同的名称
通过这个实用的 Python 应用程序学习对数概率贝叶斯

国会跑步俱乐部想让你找到他们的比赛结果。比赛公布结果,所以只要在那些结果中寻找俱乐部成员的名字,对吗?然而,如果你只寻找一个名字,比如“罗伯特”或“斯科特”,你会发现你得到太多的匹配。而且,如果你寻找全名,你有时会仍然得到太多的匹配(“罗伯特·斯科特”)。由于错误、打字错误、昵称和缩写(例如,“Chellie Pingree”显示为“Chelly Pingree”),您有时也会得到很少的匹配。一个好的解决方案是给与众不同的名字(“Pingree”)比普通的名字(“Robert”)更大的权重。让这个解决方案可行的关键是贝叶斯定理。
具体来说,我们将学习“朴素贝叶斯”的对数概率版本。这个奇妙的公式让我们把匹配和不匹配的分数加起来。它还为我们提供了一种原则性的方法:
- 根据名字的独特性来打分
- 缺少一个名字时,减去不同的点数
- 将名和姓的点结合起来
- 处理昵称
- 处理附加信息,例如,跑步者的家乡,它有时会出现在比赛结果中。
我真正的跑步俱乐部使用这些工具的版本。除了这篇文章,我还提供了一个您可以使用的 Python 笔记本。除了比赛结果,你应该会发现它在其他名字/地方/等方面很有用。匹配应用程序。此外,虽然朴素贝叶斯被广泛教授,但其美丽的对数概率公式经常被忽视,学习起来很有用。
我们将通过考虑一系列概率问题来发展我们的匹配方法。我们的目标不是产生“完美”的概率;相反,我们希望概率是:
- 简单—易于计算
- 合理—足以突出大多数匹配
- 原则性——在给出一些简化假设的情况下,与全概率理论一致。
如果需要,这些结果可以进一步改进。
让我们开始提问:
随机线是关于罗伯特的
问:假设你从一场本地比赛的结果中随机选择一条线,并随机选择一名跑步俱乐部的成员。这条随机线指向这个随机成员的(合理)概率是多少?
答:让我们假设一个给定的俱乐部成员有 1%的机会参加给定的本地比赛。我们还假设一场典型的比赛包括 1000 名跑步者,因此有 1000 条结果线。因此,随机线指向随机俱乐部成员的概率约为 1%/1000 = .00001。
输入:
prob_member_in_race = .01
result_count = 1000prior_prob = prob_member_in_race / result_countf"{prior_prob:.5f}"
输出:
'0.00001'
我们的罗伯特导致“罗伯特”
问:关于成员 Robert Scott 的为的结果行包含单词“Robert”的概率是多少?
答:你可能认为这是肯定的事情,但是“事情总会发生”,比如错误、错别字、不知名的昵称、缩写等等。让我们假设一个错别字的概率,等等。一个名字是 40%。因此,答对一个名字的概率是 60%。
输入:
prob_name_right = .6
prob_name_right
输出:
0.6
其他人指向“罗伯特”
问:假设我们选择一个随机的结果行,这个结果行是而不是关于那个成员 Robert Scott 的。随机行包含“罗伯特”这个词的概率有多大?
答:让我们假设这个概率与美国人口中“罗伯特”这个名字的出现频率相同。利用公开的政府数据,我创建了一个包含 245,000 个名字及其(大概)频率的表格。(这张表是给你用的。)它报告“罗伯特”的概率为 3%。
输入:
import pandas as pd
from pooch import retrievedef load_name_to_prob():
name_prob_file = retrieve("https://github.com/CarlKCarlK/bayesmatch/releases/download/v0.0.1/name_probability.tsv",
known_hash="md5:cf2b3277a9e88b846c18bed559a4fbea",
fname="name_probability.tsv")
name_to_prob_df = pd.read_csv(name_prob_file, sep="\t")
name_to_prob_df.set_index("name", inplace=True)
name_to_prob = name_to_prob_df.to_dict()["probability"]
return name_to_probname_to_prob = load_name_to_prob()
prob_coincidence = name_to_prob["ROBERT"]
prob_coincidence
输出:
0.03143
“罗伯特”来自罗伯特
问:包含“Robert”的结果行是关于我们的成员 Robert Scott 的概率有多大?
到目前为止,我们已经看到了导致结果的原因。例如,我们说一个关于成员 Robert Scott(原因)的结果行将包含单词“Robert”(结果),概率为 60%。
贝叶斯定理让我们逆转这一点,从结果到原因进行反向推理。在示例中,当结果行包含单词“Robert”(一个结果)时,贝叶斯定理告诉我们结果行关于成员 Robert Scott(一个原因)的概率。
通常的贝叶斯定理公式是:
P(C1|E) = P(E|C1) P(C1) / P(E) 何处P(E) = (E|C1) P(C1) + P(E|C2) P(C2)。
我们将使用等价的对数概率表:
logodds(C1|E) = logodds(C1) + log(P(E|C1)/P(E|C2))
公式的两个版本都告诉我们如何
- 一个原因的先验概率(随机线是关于罗伯特的),
- 给定一个原因的结果的概率(我们的罗伯特导致“罗伯特”),
- 给定所有其他原因的结果的概率(其他人导致“罗伯特”)
把它们变成
- 给定结果的原因的后验概率(“罗伯特”来自罗伯特)
我们喜欢这个公式的对数概率版本,因为它包含的项更少。此外,它让我们创建一个积分系统。具体来说,称为“对数贝叶斯因子”的术语log(P(E|C1)/P(E|C2))告诉我们,如果我们看到“罗伯特”,应该增加多少分。在 Python 中,计算变成:
输入:
import numpy as npdef logodds(prob):
return np.log(prob / (1.0 - prob))def prob(logodds):
odds = np.exp(logodds)
prob = odds / (odds + 1.0)
return probprior_points = logodds(prior_prob)
print(f"prior: {prior_points:.2f} points, {prior_prob:.5f} probability")delta_points = np.log(prob_name_right / prob_coincidence)
print(f"delta: {delta_points:.2f} points")post_points = prior_points + delta_points
print(f"post: {post_points:.2f} points, {prob(post_points):.5f} probability")
输出:
prior: -11.51 points, 0.00001 probability
delta: 2.95 points
post: -8.56 points, 0.00019 probability
换句话说,在结果行中看到“Robert”会使该行与成员 Robert Scott 有关的概率增加 19 倍。(它将概率从 0.00001 移动到 0.00019。)
该代码还显示了如何从概率转换到对数优势点,以及如何转换回来。此表显示了一些转换值。

没有“罗伯特”,但仍然来自罗伯特
问:包含“Robert”的结果行而非是关于我们的成员 Robert Scott 的概率有多大?
答:我们看到,在结果行中找到“Robert”会增加该行与 Robert Scott 相关的概率。同样,找不到“罗伯特”会降低概率。有趣的是,潜在的增加通常不同于潜在的减少。公式是:
logodds(C1|not E) = logodds(C1) + log(P(not E|C1)/P(not E|C2))
这告诉我们如何采取
- 原因的先验概率(如前),
- 给定一个原因遗漏一个结果的概率(我们的罗伯特不会导致“罗伯特”,0.4,一个错别字的概率,等等。),
- 在给定所有其他原因的情况下,遗漏一个效应的概率(其他人不会导致“罗伯特”,大约 97%,人口不命名为“罗伯特”)。)
把它们变成
- 给定缺失结果的原因的后验概率(没有“罗伯特”,但仍然来自罗伯特)
术语log(P(not E|C1)/P(not E|C2))是在结果中看不到“罗伯特”的对数贝叶斯因子。在 Python 中,计算变成:
输入:
print(f"prior: {prior_points:.2f} points, {prior_prob:.5f} probability")delta_points = np.log((1.0-prob_name_right) / (1.0-prob_coincidence))
print(f"delta: {delta_points:.2f} points")post_points = prior_points + delta_points
print(f"post: {post_points:.2f} points, {prob(post_points):.6f} probability")
输出:
prior: -11.51 points, 0.00001 probability
delta: -0.88 points
post: -12.40 points, 0.000004 probability
换句话说,看不到“罗伯特”会减少我们 0.88 分。因此,它将这条线关于成员 Robert Scott 的概率从 0.00001 降低到 0.000004。
“罗伯特”和“斯科特”来自罗伯特·斯科特。
问:包含“Robert”和“Scott”的结果行是关于我们的成员 Robert Scott 的概率有多大?
答:如果我们愿意忽略“罗伯特”和“斯科特”之间可能存在的依赖关系(详见下文),那么公式就变成了:
logodds(C1|E1&E2) = logodds(C1) + log(P(E1|C1)/P(E1|C2)) + log(P(E2|C1)/P(E2|C2))
旁白:我们称之为“朴素贝叶斯”,因为它忽略了证据之间可能的相关性。例如,它忽略了一个粗心的键盘输入者比一般人更有可能在两个 T2 的名字中造成一个错别字。它还忽略了来自苏格兰的姓(“斯科特”)与常见的苏格兰名(“罗伯特”)相关联。尽管如此,这种“天真”的假设在实践中往往很有效,并极大地简化了我们的数学和编程。
新公式只是原始公式的第二个增量“Scott”。在 Python 中:
输入:
print(f"prior: {prior_points:.2f} points, {prior_prob:.5f} probability")first_name_points = np.log(prob_name_right / name_to_prob["ROBERT"])
print(f"first_name: {first_name_points:.2f} points")last_name_points = np.log(prob_name_right / name_to_prob["SCOTT"])
print(f"last_name: {last_name_points:.2f} points")post_points = prior_points + first_name_points + last_name_points
print(f"post: {post_points:.2f} points, {prob(post_points):.5f} probability")
输出:
prior: -11.51 points, 0.00001 probability
first_name: 2.95 points
last_name: 4.70 points
post: -3.86 points, 0.02055 probability
换句话说,看到“罗伯特”增加了 2.95 分(和之前一样),看到“斯科特”又增加了 4.70 分。因此,概率从 0.00001(非常、非常、非常不可能)变为 0.02(不可能)。
当我们试图将一名俱乐部成员与一个更独特的名字 Chellie Pingree 匹配时,会发生以下情况:

她的姓值 12 分,所以如果我们看到她的姓,但没有看到她的名(-0.92 分),我们最终有 40%的可能性结果行是关于她的。同样,她更独特的名字本身就值 13 分以上,因此后验概率为 72%。最后,如果我们看到两个名字,我们将 99.99%确定结果行是关于她的。
“鲍勃”来自罗伯特
问:包含“Bob”和“Robert”的结果行是关于我们的成员 Robert Scott 的概率有多大?
答:为了处理昵称,我们需要指定关于成员 Robert Scott 的结果包含的概率,例如,“Robert”、“Bob”、“Rob”和其他内容。
让我们假设我们仍然有 40%的机会出现打印错误,等等。然而,现在,如果有昵称,我们假设有 50%的机会看到主名。我们将剩下的 10%概率平均分配给昵称。请注意,这些概率加起来是 100%。
我们将在这里使用的公式是:
logodds(C1|E1 ∨ E2 ∨ E3) = logodds(C1) + max( log(P(E1|C1)/P(E1|C2)), log(P(E2|C1)/P(E2|C2)), log(P(E3|C1)/P(E3|C2)) ) 其中“∨”表示“逻辑或”
这表示当寻找多个相关效果时,我们将分别找到每个效果的分数,然后使用分数最高的那个。这是一个保守的假设,它阻止我们对包含名字的两个版本的结果行给予额外的积分,例如,“Robert (Bob)”。
当我们对包含“Robert”、“Bob”和“Scott”的行运行 Python 时,结果是:
prior: -11.51 points, 0.00001 probability
ROBERT: 2.77 points
BOB: 4.51 points
ROB: -0.05 points
first_name: 4.51 points
last_name: 4.70 points
post: -2.30 points, 0.09083 probability
我们既看到“罗伯特”,也看到“鲍勃”,但“鲍勃”更有特色,所以我们用其中的点。
然而,即使有了昵称,我们仍然只有 9%的把握这一行是关于我们的成员罗伯特·斯科特的。为了做得更好,我们需要一些额外的信息。正是因为这个原因,大型比赛通常包含城市信息。
旁白:带连字符的名字(比如奥卡西奥-科尔特斯)和多词名字(比如范·霍伦)怎么办?我们将像对待昵称一样对待它们,将昵称作为主要名称和一个或多个附加名称。
“贝尔维尤”指的是罗伯特·斯科特的小镇。
问:如果结果包含城市信息,那么包含“Bellevue”的结果行是关于我们住在 Bellevue 的成员 Robert Scott 的概率有多大?
答:要回答这个问题,我们必须回答两个子问题。
- 如果这条线是关于成员罗伯特·斯科特的,它包含“贝尔维尤”的概率是多少?我们将再次假设 60%(因此,40%的打字错误的概率,等等。)
- 如果这条线是关于别人的,那么它包含“贝尔维尤”的概率是多少?我们将在感兴趣的比赛结果中使用“Bellevue”的频率,而不是在美国使用名称的频率。查看一个真实的比赛结果,我发现在 1592 行中有 170 行包含“Bellevue”。我们将稍微平滑一下,把这些计数变成一个概率:
P(“Bellevue”|about someone else) = (170+1)/(1592+2)= 0.11
在 Python 中,我们添加这些行:
city_name_points = np.log(0.60 / ((170+1)/(1592+2)))
print(f"city: {city_name_points:.2f} points")
这导致了以下输出:
prior: -11.51 points, 0.00001 probability
ROBERT: 2.77 points
BOB: 4.51 points
ROB: -0.05 points
first_name: 4.51 points
last_name: 4.70 points
city: 1.72 points
post: -0.58 points, 0.35846 probability
因此,现在我们认为这条结果线以 36%的概率指向我们来自贝尔维尤的俱乐部成员罗伯特·斯科特。这足以说明这条结果线值得一看,也许可以通过突出它来说明。
首字母和遗漏的姓名
问:如果我们不知道一个成员的名字或者只知道一个首字母,应该如何调整概率?
答:处理这个问题的一个实用方法就是跳过评分。如果我们不知道成员的名字或者只知道名字的首字母,我们可以跳过记录他们的名字。如果我们不知道成员的城市,或者如果结果不包含城市,我们可以跳过城市评分。
后续步骤
有了这些问题的答案,我们可以有效地从列表中挑选出名字。您可以在笔记本[CarlKCarlK/Bayes match:(github.com)]中看到用于创建这篇论文的 Python 代码。我的跑步俱乐部也在使用这些方法作为一个更大的比赛结果项目的一部分,用 c#[luillo 1/race results(github.com)]编写。(如果您对我将 Python 笔记本扩展成完整的 Python 包感兴趣,请告诉我。)
这些方法可以在几个方向上更进一步。
- 更多信息——我们可以用编辑距离来模拟拼写错误的概率,而不是忽略拼写错误。我们也可以考虑谁比赛更频繁,谁倾向于一起比赛。最后,通过更好地解析比赛结果,我们可以尝试匹配性别、年龄、首选距离和预期速度。当我实际寻找比赛结果时,我发现从程序的输出开始,然后使用这些额外的信息源来手动确认匹配是很有用的。
- 新的应用——除了比赛结果,我还在一个未发表的项目中使用这些方法来识别科学论文的作者。我使用的主要信息来源是名称独特性、合著者、机构和主题领域。我认为这些方法在系谱学中也是有用的。
- 作为机器学习的输入—想象一个简单的机器学习系统,它将对数贝叶斯因子作为输入,即来自匹配或缺失效应的点。这样一个系统可以轻易地覆盖数十万个名字和数千个城市,包括那些以前从未见过的城市。它可以以一种比我们现在更数据驱动的方式将它们结合起来。(如果你知道这样的作品,请留下评论。)
- 作为贝叶斯推理的第一个构件,概率编码了数据和由于我们缺乏数据而产生的不确定性。
摘要
我们已经看到了贝叶斯定理如何提供了一种从结果到原因反向推理的有原则而又实用的方法。使用其奇妙的对数概率公式,将影响的存在或不存在转化为简单的点。最后,“朴素贝叶斯”假设让我们用加法将这些点结合起来。
感谢 David Heckerman 和 Luke Bordonaro 对本文的建议。
用例:在几分钟内构建一个用于材料缺陷检测的 CNN
我们如何建立一个图像识别模型,可以对 AC 活塞的图像进行分类

空调(图片来源)
制造或建筑中使用的材料可能有各种各样的缺陷,从物理异常(如断裂)到化学问题(如油性表面)。这些问题可能在制造、安装或安装后(例如,由于磨损、环境暴露等)发生。).这种问题通常需要尽快检测以避免随后的问题,或者被标记为待修复或被重新分类为较低质量等级,这取决于情况。
在涉及高精度组件的情况下,检测此类缺陷对于避免后续问题变得更加重要。一个这样的例子是制造空调(AC)单元的活塞,该活塞必须在严格的公差内制造,以使单元在现场可靠地运行。
随着工业物联网 (IIoT)和工业 4.0 越来越多地使用计算机视觉来分析和检测缺陷,我们受到启发,建立了一个图像识别模型,可以将 AC 活塞的图像分类为正常(即无缺陷)、油性/油腻或有缺陷(即破裂、变形或掉落)。这包括准备和处理训练数据,构建一个. csv 文件来将数据映射到分类,并在 PerceptiLabs 中迭代模型。
数据
对于这个模型,我们使用了来自这个 Kaggle 数据集的图像,这些图像代表了 AC 活塞的三种分类:
- 缺陷 1 :破损、变形或掉落。
- 缺陷 2 :有油渍、油污或锈迹。
- 正常:正常、无缺陷活塞。
图 1 显示了一些正常交流活塞图像的示例:

图 1:来自描述正常 AC 活塞的训练数据集的图像。
我们对图像进行了预处理,将每个图像的大小调整为 80x80 像素的分辨率,并创建了一个. csv 文件,以将图像映射到标称分类值:0 表示缺陷 1 ,1 表示缺陷 2 ,2 表示正常。下面是一个部分的例子。csv 文件看起来:

将数据加载到感知实验室的. csv 文件示例,其中 0 表示缺陷 1,1 表示缺陷 2,2 表示正常。
车型总结
我们的模型只用了三个组件:
组件 1:卷积路径大小=3,步幅=2,特征映射=16
组件 2:密集激活= ReLU ,神经元=128
组件 3:密集激活= Softmax ,神经元=3

图 2:percept ilabs 中的最终模型(图片来源percept ilabs)。
培训和结果

图 3: PerceptiLabs 在训练时的统计视图(图片来源percept ilabs)。
我们用 32 个一批 25 个历元训练模型,使用 ADAM 优化器,学习率 0.001,交叉熵损失函数。
使用大约 21 秒的训练时间,我们能够实现 100%的训练准确率和 98.25%的验证准确率。在下面来自 PerceptiLabs 的屏幕截图中,您可以看到准确性如何在 25 个时期内提升到这些百分比,其中大部分提升仅发生在前六个时期内:

图 4:精度图(图片来源 感知器 )。
与此同时,在前三个时期,损失下降最多:

图 5:损失图(图片来源 感知器 )。
垂直应用
使用这种图像识别模型来发现缺陷可以在工业物联网( IIoT )应用中发挥关键作用,范围从机械到石油和天然气等。公司有可能使用它来自动检测缺陷,从而获得对资源的更多控制,减少缺陷,并优化他们的运营。
该模型本身也可以用作转移学习的基础,以创建检测其他类型缺陷的更高级的模型。
总结
这个用例是一个简单的例子,说明了如何使用 ML 通过图像识别来识别材料缺陷。如果你想在几分钟内建立一个类似的深度学习模型,运行 PerceptiLabs 并从 GitHub 获取我们预处理过的数据集的副本。
用例:使用图像识别的自动化天气分析

图片来源。
许多行业都需要识别当前和过去的天气状况。这些数据有助于他们计划、组织和/或优化运营。例如,农民可能会查看当前的天气来决定是否应该打开或关闭洒水装置。滑雪场经营者可能会根据山上不同的天气条件选择启用造雪机。建筑工人可能会计划好他们在一个偏远的工地需要的物资和雨具。
目前,做出这样的决定可能需要手动查看来自远程摄像机的视频,依赖天气预报,或者只是看着窗外。
通过提供一只数字眼,使用机器学习(ML)提供了自动化的潜力。更具体地说,如果可以建立图像识别 ML 模型来通过简单地查看天气图像来识别条件,则它可以部署在如上所述的场景中。例如,农场上的摄像机馈送可以由部署在边缘的物联网设备(例如,智能摄像机)上的 ML 模型来处理。然后,该模型可用于自动确定当前的天气状况,并相应地启用或禁用喷水阀。
为了演示这个用例,我们在 PerceptiLabs 中建立了一个模型,用于对四种不同类型的天气进行分类。我们使用了来自多类天气数据集的 1123 张图像进行图像分类:多云、晴朗、日出和下雨。
数据

图 1:来自描述不同天气条件的训练数据集的图像。
我们对图像进行了预处理,将每个图像的大小调整为 224x224 像素的分辨率,并创建了一个. csv 文件来将图像映射到它们各自的分类枚举。下面是一个部分的例子。csv 文件看起来:

将数据加载到 PerceptiLabs 的. csv 文件示例,其中 0 表示多云,1 表示下雨,2 表示晴天,3 表示日出。
型号总结
我们的模型仅由三个组件组成:

ResNet50 可以在这里找到。ReLU 可以在这里找到。Softmax 可以在这里找到。

图片来源:感知器
训练及结果

图片来源:感知实验室
我们使用 ADAM 优化器,学习率为 0.001,交叉熵损失函数,以 10 个历元为一批,每批 50 个来训练模型。
在大约四分钟的训练时间内,我们能够实现 99.4%的训练准确率和 95.9%的验证准确率。在以下来自 PerceptiLabs 的屏幕截图中,您可以看到准确度如何在 10 个时期内提升到这些百分比,其中大部分提升发生在前三个时期内:

图片来源:感知实验室
与此同时,在前五到六个时期,损失下降最多:

图片来源:感知实验室
垂直应用
使用图像识别自动识别当前天气状况的能力可以在工业物联网( IIoT )应用中发挥关键作用,从农业到石油和天然气等等。公司可以利用它来控制资源、节约能源和优化运营。
该模型本身也可以用作迁移学习的基础,以创建更高级的模型,检测其他天气条件,甚至分析给定的环境。
总结
这个用例是一个简单的例子,展示了 ML 如何通过图像识别来识别天气状况。如果你想在几分钟内建立一个类似的深度学习模型,运行 PerceptiLabs 并从 GitHub 获取我们预处理过的数据集的副本。
数据集鸣谢:Ajayi,Gbeminiyi (2018),“用于影像分类的多类天气数据集”,门德利数据,V1,doi: 10.17632/4drtyfjtfy.1
用例:水果分类
图像识别模型能理解新鲜农产品的奇特之处吗?

图片来源。
供应链中水果和蔬菜等易腐食品的处理可能涉及许多过程,如分拣、称重和识别过期产品。传统上,这些过程是手工完成的,但随着技术的进步,自动化程度越来越高。现在,工业物联网(IIoT)和 ML 等领域在供应链中发挥着越来越重要的作用,行业参与者可以利用图像识别等技术来帮助对产品进行分类,在边缘做出决策,并优化他们的运营。
像这样的垂直市场对 ML 来说是一个很好的测试。举个例子,像水果和蔬菜这样的食物。每个标本的大小、形状和特征都各不相同,而照明等环境因素都增加了图像处理训练数据的复杂性。
谢天谢地,在感知力实验室,我们从不在挑战面前退缩。因此,我们在感知实验室中建立了一个图像识别模型,看看它能否通过分析图像来识别不同类型的水果。
有了足够高的准确性,像这样的模型可能会帮助农民、批发商和超市自动化供应链中负责管理和处理这些产品的不同流程。让我们看看进展如何。
数据集
为了训练我们的模型,我们使用了来自 Kaggle 上的 Fruits 360 数据集的图像,这些图像最初是使用视频捕获获得的,如这里的所示。
数据集包含超过 90,000 种颜色。jpg 图像分为 131 类,代表不同类型的水果。使用 6500 多张图像的子集,我们使用 PerceptiLabs 的数据向导中的预处理选项将每张图像的大小调整为 224x224 像素。图 1 显示了该数据集中的一些苹果图片示例:

图 1:来自数据集的图像示例—图像 来源 。
为了将分类映射到图像,我们创建了一个. csv 文件,将每个图像文件与适当的水果分类标签相关联,以便通过 PerceptiLabs 的数据向导加载数据。下面是一个部分的例子。csv 文件看起来:

的例子。csv 文件,用于将数据加载到将图像文件映射到其关联标签的 PerceptiLabs 中。
我们已经将图像数据和这个 CSV 文件在 GitHub 上进行实验。
型号汇总
我们的模型只使用了一个组件:
组件 1: VGG16
我们采用的 VGG16 模型是具有 16 层的神经网络架构,其中 13 层是卷积层,其余三层是密集层。图 2 显示了感知实验室中模型的拓扑结构:

图 2:感知实验室中模型的拓扑结构—图片 来源 。
训练和结果
我们用 32 个批量 5 个历元训练模型,使用 ADAM 优化器,学习率 0.001,交叉熵损失函数。使用大约 74 分 44 秒的训练时间,我们能够实现 99.82%的训练准确率、99.54%的验证准确率和 99.6%的测试准确率。全球培训损失为 0.19%,全球验证损失为 0.14%。
图 3 显示了训练期间 PerceptiLabs 的统计视图:

图 3:训练时 PerceptiLabs 的统计视图。训练损失和精度分别显示在左上角和右上角,右下角可以看到渐变—图片 来源 。
在这里,我们可以看到验证和训练损失随着时间的推移逐渐下降,两者的曲线非常相似。验证和训练准确性的曲线也反映了彼此的形状,并在稳定在略低于 1.0 之前的前三个时期上升。
图 4 显示了模型的分类标签度量表测试结果和混淆矩阵:

图 4:模型的分类标签度量和混淆矩阵—图像 来源 。
混淆矩阵表明,模型正确地测试了几乎所有的样本(即,只有少数假阳性或假阴性)。Labels Metrics 表通过显示以下各项的高(标准化)值来证实这一点:分类准确度(每个类别的准确度对所有类别进行平均)前 K 个分类准确度(前 K 个预测类别中正确类别的频率)精确度(正面预测的准确度),以及召回(找到的正面的百分比(即,没有被错误分类为负面而不是正面)。
垂直应用
像这样的模型可以用于水果的收集、分类和分配。例如,该模型可用于分析通过相机获取的照片或视频帧,以分析传送带上不同种类的苹果,从而帮助对它们进行分类。该模型本身也可以用作迁移学习的基础,以创建用于对其他类型的食品、材料或产品进行分类的模型。
总结
此使用案例展示了如何构建图像识别模型来支持整合了 IIoT 和/或 ML 的供应链流程。如果你想建立一个类似这样的深度学习模型,运行 PerceptiLabs 并从 GitHub 中抓取一份我们预处理过的数据集。
Mihai Oltean,水果 360 数据集:包含水果和蔬菜的图像数据集,2020,Kaggle.com,2020.05.18.0,https://www.kaggle.com/moltean/fruits,麻省理工学院许可证
用例:将木材单板分为干和湿
我们如何构建一个图像识别模型来自动化密钥验证过程

图片来源。
工业物联网在工业 4.0 中发挥着重要作用,尤其是在自动化制造流程方面。随着传感器、摄像头和人工智能在边缘的集成,组织现在可以自动化许多流程,如视觉质量控制检查。
以制造木皮为例。制造过程的一个重要部分包括在高达 320°F 的温度下干燥木材片,以获得大约 8%至 12%的含水量。在此干燥过程之后,必须对板材进行多次验证,以确保它们已正确干燥并符合质量控制标准。
为了帮助自动化这一验证过程,我们开始在 PerceptiLabs 中构建一个图像识别模型,该模型可以识别单板是干还是湿。像这样的模型可能有助于制造商自动识别水分含量过高的单板。
数据集
为了训练我们的模型,我们使用了来自单板 21 数据集的图像。原始数据集包括高分辨率。png 图像(通常超过 4000x4000 像素)分为两类,分别代表湿和干木皮。使用 PerceptiLabs 的数据向导,我们将图像预处理为 224x224 像素。图 1 显示了来自该数据集的干单板的一些示例图像:

图 1:显示干燥单板的数据集的图像示例— 图像来源 。
为了将分类映射到图像,我们创建了一个. csv 文件,将每个图像文件与适当的分类号(0 =干,1 =湿)相关联,以便使用 PerceptiLabs 的数据向导加载数据。下面是一个部分的例子。csv 文件看起来:

的例子。csv 文件,用于将数据加载到将图像文件映射到其分类号的 PerceptiLabs 中。
车型总结
我们的模型只用了三个组件:
组件 1:卷积神经网络(CNN),Activation= ReLU ,特征映射=16,Patch_size=3,Stride=2
组件 2:密集,激活= ReLU ,神经元=256
组件 3:密集,激活= Softmax ,神经元=2
图 2 显示了感知实验室中模型的拓扑结构:

图 2:感知实验室中模型的拓扑结构— 图片来源 。
训练和结果
我们用 32 个批量 5 个历元训练模型,使用 ADAM 优化器,学习率 0.001,交叉熵损失函数。在大约 22 分 10 秒的训练时间内,我们实现了 100%的训练准确率和 99.7%的验证准确率。图 3 显示了训练期间 PerceptiLabs 的统计视图。

图 3: PerceptiLabs 在训练时的统计视图— 图片来源 。
下面的图 4 和图 5 显示了训练期间五个时期的准确度和损失:

图 4:训练时的准确度— 图片来源 。

图 5:训练中的损耗— 图片来源 。
这里我们可以看到,在训练和验证的第一个时期,精确度增加最多,损失减少最多。在第一个时期之后,精确度和损失在其余时期保持稳定。
垂直应用
像这样的模型可以用来检测生产线上的制造缺陷。例如,该模型可用于分析照片或视频帧,这些照片或视频帧是通过工厂地板上的摄像机采集的,这些摄像机在制造过程中捕捉通过装配线的单板。任何含有 T2 湿木材的板材都会被标记出来,供工厂工人进一步检查。该模型本身也可以用作迁移学习的基础,以创建用于检测其他类型的材料或产品中的缺陷的附加模型。
总结
这个使用案例是一个例子,说明图像识别如何用于帮助制造业。如果你想建立一个类似这样的深度学习模型,运行 PerceptiLabs 并在 GitHub 上查看我们为这个用例创建的 repo。
https://ghar pedia . com/blog/manufacturing-process-of-wood-单板/
T.Jalonen,F. Laakom,M. Gabbouj 和 T. Puoskari,“使用暹罗神经网络的视觉产品跟踪系统”,IEEE Access,第 9 卷,第 76796–76805 页,2021 年,doi:10.1109/Access . 2021 . 3082934(cc 4.0)。
使用云托管实现 AWS 安全性自动化
了解如何使用云托管自动保护您的 AWS 帐户使用易于使用的免费政策。
任何使用过亚马逊网络服务( AWS )的人都知道有一些很棒的自动化安全功能,比如 AWS Config 。不利的一面是,根据你的账户有多忙,费用会很快增加。AWS 自己的客户之一, Capital One 创建了一个名为云托管的社区项目。
该项目在执行时即时创建临时的 Lambdas ,以对照策略进行检查,并在发现配置不合适时采取纠正措施。要了解更多关于云托管如何工作的信息,你可以查看 AWS Re:Invent 视频这里。让我们开始实现免费的自动化安全!
先决条件:确保 Python3 已安装并可在您的终端中访问。您还需要确保设置了 AWS CLI 和您的 IAM 密钥或您的 [SSO](https://aws.amazon.com/blogs/security/how-to-create-and-manage-users-within-aws-sso/#:~:text=AWS Single Sign-On (AWS,set permissions across those groups.) 凭证。然后根据您的操作系统使用以下语法安装云保管人:
#For Linux and Mac Terminals
python3 -m venv cloud-custodian-lab
source cloud-custodian-lab/bin/activate
pip install c7n
#For Windows using Powershell:
Python -m venv cloud-custodian-lab
. .\custodian\Scripts\Activate.ps1
pip install c7n

创建虚拟环境并安装云托管
现在让我们创建一些资源,然后编写一个策略来检查它。创建 VPC 和开放安全组。使用 VPC 向导创建一个带有一个公共子网的 VPC。然后创建一个安全组,允许从 0.0.0.0/0 到 tcp 端口 22 和 3389 的整个入站。

VPC 中不安全安全组的创建示例

向您的安全组添加开放的 RDP 和 SSH 规则入口列表
现在,让我们在 YAML 创建一个样本样板策略来展示云托管的健壮性。检查提供的样板样本策略。该示例策略将检查允许 RDP 和 SSH 从互联网进入的 VPC 安全组。删除规则,然后标记资源:

YAML 的云托管政策示例
如果你想跟进,请随意从我的文本中复制粘贴到这里:
### EC2 Related ###
policies:
- name: open-world-admin-port-securitygroups
resource: aws.security-group
description: Removes 0.0.0.0/0 and ::/0 as sources ingress for security groups
filters:
- or:
- type: ingress
Ports: [22, 3389]
Cidr:
value: "0.0.0.0/0"
- type: ingress
Ports: [22, 3389]
CidrV6:
value: "::/0"
actions:
- type: remove-permissions
ingress: matched
- type: tag
tag: NonCompliantResource
value: remediated
您将对我们的不良实践安全组使用样本策略,并确保规则已随云保管人标记的安全组一起删除。利用以下命令:
custodian validate <your policy name.ext
custodian run --dryrun -s result <your policy name.ext>
custodian run -s result <your policy name.ext>
如果您一直这样做,您的输出将类似于下面这样:

Powershell 验证和运行云托管策略
运行示例策略后,检查您创建的 AWS 帐户 VPC 安全组,您会看到对它的更改。观察以下结果,包括被删除的违规规则和对象上的新标签:

云托管人删除了不合规的规则

云保管人向资源对象添加了自定义标记
您还可以在 CloudTrail 中检查对 AWS 帐户的 API 调用和结果。打开 CloudTrail,注意正在使用的 IAM 用户编程键或 SSO 凭据,深入研究标记事件和规则删除,并查看 Cloudtrail 提供的详细信息:

AWS Cloudtrail 显示成功添加的自定义标记
恭喜您,您现在已经成功利用云托管来自动化您的 AWS 帐户安全的一个方面。在我让您自己进行试验之前,这里有另一个非常受欢迎的示例策略,您可以从它开始。以下是如何始终确保 S3 存储桶被加密的方法:
policies:
- name: s3-bucket-encryption-policy-absent
resource: s3
description: s3 buckets without encryption required and re-enable encryption
filters:
- type: no-encryption-statement
actions:
- type: set-bucket-encryption
crypto: AES256
enabled: True
- type: tag
tag: NonCompliantResource
value: remediated
那么下一步是什么?您可以在您的 CI/CD pipeline 中添加云托管作为您的部署后操作的一部分。或者你甚至可以将云托管策略和引擎封装在另一个 lambda 中,并在 CloudWatch Events 中的预定 cron 任务上运行。
一如既往,如果你觉得这些信息有用,并希望更深入的咨询;请随时在 www.scissecurity.com联系我
快乐安全发展!
通过这些技巧更有效地使用 Colab
招数
充分利用 Google Colab 笔记本电脑

来源:https://pixabay.com/images/id-4905013/
合作实验室,简称“Colab”,是由谷歌托管的 Jupyter 笔记本,它们允许你通过浏览器编写和执行 Python 代码。旋转 Colab 很容易,因为它直接与您的谷歌帐户集成在一起。Colab 提供对 GPU 和 TPU 的免费访问,要求零配置,实现代码无缝共享。
Colab 有一段有趣的历史。它最初是作为谷歌内部的数据分析工具出现的。然而,后来它被公开推出,从那以后,许多人一直在使用这个工具来完成他们的机器学习任务。许多学生和没有 GPU 的人依靠 colab 的免费资源来运行他们的机器学习实验。
本文汇编了一些有用的技巧和窍门,我用它们来完成我在 Colab 中的工作。我已经试着列出我第一次阅读的大部分资料来源。希望这些技巧能帮助你充分利用你的 Colab 笔记本。
1.使用🖥本地运行时
通常情况下,Colab 会为您提供免费的 GPU 资源。但是,如果您有自己的 GPU,并且仍然希望利用 Colab UI,有一种方法。您可以在本地运行时使用 Colab UI,如下所示:

在 Colab |作者图片中使用本地运行时
这样,您可以在本地硬件上执行代码并访问本地文件系统,而无需离开 Colab 笔记本。下面的文档更深入地介绍了它的工作方式。
https://research.google.com/colaboratory/local-runtimes.html
2.便条簿📃
你是否最终创建了多个名为“untitled 1.ipynb”和“untitled 2.ipynb”等的 Colab 笔记本??在这方面,我想我们大多数人都在同一条船上。如果是这样,那么 Cloud scratchpad 笔记本可能适合你。云端暂存是一种特殊的笔记本,可在网址[https://colab.research.google.com/notebooks/empty.ipynb](https://colab.research.google.com/notebooks/empty.ipynb)找到,不会自动保存到您的硬盘帐户。它非常适合实验或非平凡的工作,并且不占用 Google drive 的空间。

Colab 中的便签本|作者图片
3.直接在 Colab 中打开 GitHub Jupyter 笔记本📖
Colab 笔记本的设计方式可以轻松地与 GitHub 集成。这意味着你可以直接将 Colab 笔记本加载和保存到 GitHub。多亏了成宰·瑞恩·李,有一种简便的方法可以做到这一点。
当你在 GitHub 上的一个笔记本上想要在 Colab 中打开时,在 URL 中将github替换为githubtocolab,其他的保持不变。这会在 Colab 中打开同一个笔记本。

在 Colab 中打开 GitHub Jupyter 笔记本|作者图片
4.获得已完成单元执行的通知🔔
即使您切换到另一个选项卡、窗口或应用程序,Colab 也可以通知您已完成的执行。你可以通过Tools → Settings → Site → Show desktop notifications来启用它(并在提示时允许浏览器通知)来查看它。

在 Colab|按作者排列的图像中启用单元格通知
这是一个演示,演示了即使导航到另一个选项卡,通知也是如何显示的。

获得已完成单元格执行的通知|作者图片
附加提示
你希望你的 Jupyter 笔记本也有同样的功能吗?我会掩护你的。您还可以在 Jupyter 笔记本中启用单元格完成通知。详情请阅读👇
5.搜索驱动器中的所有笔记本🔍
您要在驱动器中搜索特定的 Colab 笔记本吗?导航到驱动器搜索框并添加:
application/vnd.google.colaboratory
这将列出您的 Google Drive 中的所有 Colab 笔记本。此外,您还可以指定特定笔记本的标题和所有权。例如,如果我想搜索一个由我创建的标题为“Transfer”的笔记本,我会提到以下内容:

按作者搜索驱动器|图片中的所有笔记本
6.Kaggle 数据集到 Google Colab🏅
如果你的预算有限,并且已经用完了 Kaggle 上的 GPU 资源配额,这个黑客可能会给你一个喘息的机会。可以将任何数据集从 Kaggle 无缝下载到您的 Colab 基础架构中。你需要做的是:
- 下载您的 Kaggle API 令牌:

按作者访问您的 Kaggle API 令牌|图像
点击。Create New API Token选项卡,将会生成一个包含您的 API 令牌的kaggle.json文件。在 Google Drive 中创建一个名为 Kaggle 的文件夹,并将kaggle.json文件存储在其中。

包含 kaggle.json 文件的 Kaggle 文件夹|作者图片
2。在 Colab 笔记本中安装驱动器

在 Colab 笔记本中安装驱动器|图片由作者提供
3。提供“kaggle.json”的配置路径,更改当前工作目录
import os
os.environ['KAGGLE_CONFIG_DIR'] = "/content/drive/My Drive/Kaggle"%cd /content/drive/MyDrive/Kaggle
4。复制要下载的数据集的 API。
对于标准数据集,可以如下访问 API:

【2021 年福布斯亿万富翁数据集在 Kaggle 上公开发布 |图片由作者提供
对于与竞赛相关联的数据集,API 位于“数据”选项卡下:

[IEEE-CIS 欺诈检测竞赛](http://IEEE-CIS Fraud Detection) [公开](http://Forbes Billionaires 2021 3.0)在 Kaggle 上[可用|图片由作者提供](http://IEEE-CIS Fraud Detection)
5.最后,运行以下命令下载数据集:
!kaggle datasets download -d alexanderbader/forbes-billionaires-2021-30or!kaggle competitions download -c ieee-fraud-detection

Kaggle 数据集到 Google Colab |作者图片
7 .通过终端访问 Colab
好吧,如果你不是笔记本的忠实粉丝,但想使用 colab 提供的 GPU 和 TPU,有一个办法。感谢 Mark Saroufim 在社区中公开分享这个黑客。
为此,您需要安装 tmate 。让我们看看这是如何工作的:
!apt-get install tmate
!ssh-keygen
!tmatessh into the generated url

8.在 Colab 上访问 Visual Studio 代码(VS 代码)💻
不使用笔记本要不要使用 Colab 的基础设施?那么这个提示可能是给你的。由于社区的努力,创建了一个名为Colab code的包。现在可以在 Colab 中运行 VSCode 了。从技术上讲,这是通过代码服务器完成的——一个运行在远程服务器上的 Visual Studio 代码实例,可以通过任何网络浏览器访问。安装软件包的详细说明可以在下面找到。
https://github.com/abhi1thakur/colabcode
这是这个过程的一个快速演示。

通过作者访问 Colab | Image 上的 Visual Studio 代码(VS 代码)
9.数据表扩展🗄
Colab 包括一个扩展,它将熊猫的数据帧渲染成可以动态过滤、排序和浏览的交互式显示。要启用熊猫数据帧的数据表显示,请在笔记本单元格中键入以下内容:
%load_ext google.colab.data_table#To diable the display
%unload_ext google.colab.data_table
这里有一个相同的快速演示:【https://colab.research.google.com/notebooks/data_table.ipyn】T4b

Colab |作者图片中的数据表扩展
10.比较笔记本👀
Colab 可以很容易地比较两个笔记本。使用 Colab 菜单中的View > Diff notebooks或导航到[https://colab.research.google.com/diff](https://t.co/wu8ce4ngMl?amp=1)并将要比较的笔记本电脑的 Colab URLs 粘贴到顶部的输入框中。

比较 Colab |作者图片中的笔记本
包裹
这些是我发现非常有用的一些 Colab 技巧,特别是在 GPU 上训练机器学习模型的时候。尽管 Colab 笔记本最多只能运行 12 个小时,然而,有了上面分享的技巧,你应该能够充分利用你的会议。
👉有兴趣看我写的其他文章。这个 回购 包含了我分类写的所有文章。
使用计算机视觉捕捉真实世界的事件
变更数据
利用计算机视觉协助 COVID 疫苗分发

在这篇博文中,我将描述使用计算机视觉捕捉不同事件的一般方法,重点是如何使用计算机视觉进行一系列应用来帮助疫苗分发。这篇博文比较长,但是我涵盖了一些不同的场景,这些场景在很多情况下会有帮助。如果你想查看代码,可以在这里找到。如果对应用程序进行了改进,本文也会更新!
一年多来,新冠肺炎疫情以多种方式影响了我们的生活,也给我们带来了诸多挑战。有了现在可用的疫苗,我们有能力加速疫情的终结,只要疫苗能够安全有效地分发。应该尽一切努力帮助这一进程。进入计算机视觉!
在开发应用程序时,最好考虑到特定的用例及环境。为了确定计算机视觉如何帮助疫苗接种过程,我们的团队咨询了疫苗接种中心的一名志愿者,以了解更多关于该过程的信息,并寻找应用计算机视觉的机会。如果你想看这次讨论的录音和应用的演示,你可以在 YouTube 上观看。
在志愿者的疫苗接种中心,有三个主要阶段:等待接种疫苗(和/或等待登记)、接种疫苗和接种后监测。我们的志愿者确定的主要问题是,我们认为计算机视觉可以很容易地帮助我们:减少长时间的等待时间,帮助个人练习社交距离,以及监控疫苗接种后的房间。由于目前的方法是手动点击器计数器,因此有一种方法来验证已经进行了多少次疫苗接种也是有用的。
疫苗接种过程中的一些其他障碍,如难以导航的网站,不适合用计算机视觉来解决。其他挑战,如多余的表单输入,可以通过将计算机视觉许可证验证解决方案与更高效的在线注册流程相结合来解决!
该解决方案使用三个模块化应用程序,可以单独使用或与仪表板结合使用,以帮助解决上述问题。第一个应用程序监视等候室,记录个人之间的距离、戴口罩的个人比例以及等候室中有多少椅子是空的。第二个应用程序监控发生了多少次疫苗接种事件。第三个应用程序监视患者何时举手,表示他们需要志愿者的帮助。通过整合这些解决方案,以及一些关于正在使用的疫苗类型和当天疫苗接种预约数量的一般输入数据,我们还可以帮助提供一种验证方法,以确定打开了多少个疫苗瓶、打开的疫苗瓶中还剩多少剂量,以及是否到达了当天的最后预约。这些数据可以帮助管理等待时间,如果排队速度比预期慢或快,就会自动发送短信通知即将到来的预约,如果一天结束时打开的小瓶中还有疫苗剂量,就会拨打等待名单。
因为这些应用程序不是为真实的站点开发的,只是尽可能接近真实场景,所以使用了库存视频数据,而这些数据正是驱动应用程序实现的原因。如果您构建自己的疫苗接种应用程序,确切的应用程序将很可能非常不同;要么过程会略有不同,要么拍摄角度会有所不同。
候诊室申请

照片由来自 Pexels 的 Paula Schmidt 拍摄。
候诊室应用协助分析社交距离和面具检测,以及在候诊室椅子无人时进行监控。距离其他人至少 6 英尺(大约)并且戴着面具的人有绿色的边界框;如果一个人与另一个人的距离小于 6 英尺,或者没有戴口罩,就会显示红色方框。
为了确定等候室有多满,我们检测等候室的椅子上有多少人。这是通过使用免费提供的股票 alwaysAI 模型 yolov3 进行对象检测来实现的。结果过滤函数用于将结果限制为带有“人”标签的结果,以确保只检测到对人的预测。这是通过在每把椅子周围创建一个边界框来实现的,并注意当一个人的边界框与最近的椅子边界框重叠超过某个阈值时——比如大约 70%。使用该阈值是因为一些人在椅子上前倾,或者将脚伸向侧面,因此使用阈值< 100%允许以更一般化的方式检测一个人正坐在椅子上。关于检测到多少个面具、每对人之间的平均距离或距离以及有多少椅子被占用的报告被发送到服务器(参见最后一节)。
开发该应用程序的第一步是执行遮罩检测。
alwaysAI 的产品经理托德·格里德(Todd Gleed)训练了一个新模型,使用标签“面具”(代表任何戴着面部覆盖物的脸)和“无面具”(代表没有任何覆盖物的脸)。通过与我分享项目的模型,我能够测试不同的版本。在应用程序中测试该模型时,它偶尔会工作,但在股票视频数据上有困难,这些数据具有较小的遮罩实例(人比他们在训练数据中离摄像机更远)。股票数据碰巧只使用了更柔和的中性色。我还注意到,当患者坐在登记工作人员的正对面时,该模型并不总是能够做出任何预测(无论是“戴口罩”还是“不戴口罩”);所以从那个角度拍摄的图像在数据集中可能没有得到很好的表现。我们当然可以对模型进行更多的训练,使用更接近用例的数据,但是我修改了应用程序代码。
因为知道哪些人戴着面具是有用的,要么人的边界框必须与面具检测的边界框进行比较,要么对应于人检测框的图像可以简单地用作面具检测模型的输入,这就是我选择要做的。
对于每个“人”检测框,对应于人的原始帧部分被切掉,并且该图像通过掩模检测模型运行。在这个应用中使用了类似的方法。此外,如果模型不能做出任何预测,我们就有能力说‘这里有一个人,但我不知道他们是否戴着面具’;如果有人背对着相机,或者由于其他原因很难检测到遮罩,这将非常有用。如果检测到一个人,但无法确定他们是否戴着面具,则此人的边界框会用红色标记,并给出“未检测到面具”的标签。该应用程序在登机工作人员正对面的角度上仍然有问题。
应用程序的这一部分确实使用了跟踪,但是,这可能是不必要的。在这种情况下,跟踪对于查看病人如何从一张椅子移动到另一张椅子是有用的,但是对于候诊室,我们可能只关心是否所有的椅子都满了,或者注意有多少椅子是空的,等等。这可以通过物体检测来实现。每当有人坐在椅子上或离开椅子时,这些信息都会被发送到仪表板应用程序。
在这个应用中,距离是通过对一个人使用硬编码的标准测量来完成的,大约 3.5 英尺,并使用像素标度,例如在这个示例应用的中使用的。如果你想看这个逻辑的演示和解释,你可以在 YouTube 上查看这个关于距离测量的视频教程。在这种情况下使用三英尺半,因为人们通常坐在用于开发应用程序的股票数据中。使用硬编码的比例是可能的,因为只有一个静态摄像机,每当检测到一个人坐着时,整个人都在画面中。根据相机角度和环境,您的应用可能需要更强大的距离测量方法,例如使用帧中尺寸已知的其他对象,或者检查关键点以查看是否找到完整的人,或者如果只找到部分人,则使用不同的比例。你也可以使用深度相机进行更精确的测量。
疫苗接种申请

对于疫苗接种应用程序,我建模的场景是单个表的静态视图。医疗专业人员坐在桌旁,并且定期有患者过来坐在桌旁。一段时间后,医务人员绕过桌子,站在病人旁边,给他们注射疫苗。因此,可以通过记录两个人何时进入图像的一部分,记录他们何时离开,然后基于这些时间戳确定他们在该区域停留了可接受的持续时间,来捕捉“接种事件”。每次发生疫苗接种事件时,都会向仪表板服务器发送 POST 请求。
这种实现只需要跟踪并注意到被跟踪的边界框在设定的时间内位于图像的特定部分,与上一节中描述的方式完全相同。根据摄像机角度和每个疫苗接种站的摄像机数量,您可能需要采取类似于候诊室椅子的方法,或者您可能需要使用关键点来记录某人何时坐着或站着。
疫苗接种后监测应用程序

照片由 RODNAE Productions 从 Pexels 拍摄。
在个人接种疫苗后,他们通常被放入一个大房间约 15 分钟,在那里他们可以坐下来,如果他们感到不适,可以通过举手通知医疗保健专业人员。第三个应用程序旨在协助此疫苗接种后监测。这是一个非常简单的概念——捕捉举手动作。在这个应用程序中也做了类似的事情。在这种情况下,我没有定义什么是不良姿势,而是抓住了“举手”的不同概念。在本应用程序使用的普通视频中,当一个人的肘部高于相应的肩部时(注意:这意味着肘部的 y 坐标小于相应肩部的 y 坐标),可以通过记录来捕捉举手动作。
不过,这是一种过于简单的抓牌方式。看看这一部分顶部图像中的所有人,或者想想当你举起手臂时,你的手臂与你的头、肩膀和其他关键点的关系。也许你的朋友举起手臂的方式有所不同——也许你的手臂向上伸直,所以你的手腕在你肘部的正上方,肘部在你肩膀的正上方。也许你的朋友只是把他们的手放在他们的脸上,所以他们的手腕在他们的肩膀上,但是他们的肘部在侧面,在垂直位置上在手腕和肩膀之间。在相机视图中,也许你看不到手腕、胳膊肘或肩膀。您可以使用真实世界的举手动作进行测试,看看在给定相机角度的情况下,哪些关键点关系反映了最常见的举手动作!
为了防止捕捉到某人刚刚拂去脸上的头发或可能调整眼镜时的动作,这些动作可能导致与举手非常相似的关键点关系,我们可以在应用程序中使用“共识”逻辑。这将在检测到举手时启动一个计时器,然后计算在特定超时时间内检测到举手的次数。总结果数除以当前人数,然后将其与检测到“硬加薪”的总次数进行比较。例如,如果我们运行循环 10 次,有一个人举手,我们应该有 10 个“举手”结果和 20 个“没有举手”结果。只要“举手”事件的数量大于或等于事件总数除以人数,我们就将其记录为真正的举手。每当有人举手,这就被发送到仪表板服务器。在我们的手势控制应用中使用了类似的方法。你也可以看看关于这个话题的 YouTube 的记录!
将这一切结合在一起

迈克·范·肖特沃特摄于佩克斯。
在我最初的实现中,三个应用程序使用一个用 Dash Plotly 生成的仪表板捆绑在一起。因为 alwaysAI 是基于 Python 的,所以您可以将这些应用程序与现有的 Python 包集成。这个 GitHub 存储库提供了一个基本的 Dash 仪表板作为 alwaysAI 应用程序;您可以将所有数据(在每个应用程序中使用“server_url”属性)发送到这个仪表板,处理事件日志,并使用这些数据填充仪表板中的各种图形和表格!请注意,在单个应用程序中,我们将数据发送到“路由”和 URL。为了处理传入的数据,您需要在 dashboard_server/app.py 中的各个应用程序中创建发送数据的路由,如下所示:
@app.route("/event", methods=["POST"])
def event():
body = request.json
# process body further, send to managing object
您可以创建额外的路由(如 '/setup' ,或者您可以为每个单独的应用程序创建不同的端点)。创建一个特定的类来解析和管理传入的数据可能会很有用。因此,您可以在 dashboard_server/app.py 中实例化一个“经理”对象(您可以自己定义的类的实例),然后将所有数据传递给这个对象并处理它。例如,烧瓶路径可能如下所示:
manager = DataManager()@app.route("/event", methods=["POST"])
def event():
body = request.json
manager.process_data(body)
事实上,如果您只想聚合数据并发出警报,或者根据结果编写数据文件,那么您可以不使用 Dash 可视化工具来完成这些工作——只需使用这些路由来处理传入的数据,并让一些应用程序逻辑使用数据管理实例来确定要采取的操作。
如果您正在使用 Dash,那么您的 dashboard_server/app.py 中的回调函数可以调用这个管理对象上的方法来获取数据以填充您的可视化。例如,上述 GitHub 存储库中的回调可以修改为如下所示:
# Dash Callbacks
@dash_app.callback(
output=[Output("logs", "data"), Output("logs", "columns")],
inputs=[Input('interval-component', 'n_intervals')]
)
def render_log_table(n_intervals):
df = manager.get_log_table_df()
return df.to_dict('records'), [{"name": i, "id": i} for i in df.columns]
在“DataManager”类中,您可以定义函数 get_log_table_df() 并实现它来返回正确的数据。
将重新格式化的数据存储在 pandas 数据帧中可能是有用的,因为 Dash 中的许多组件都将数据帧作为输入。
最后的想法
虽然所有其他应用程序都设置为使用 alwaysAI streamer 功能(用于查看应用程序输出的本地网页)运行,但这主要是为了展示应用程序是如何工作的。如果您需要对 streamer 进行更多的控制,您可以自己构建;你可以在这里找到各种关于 T2 的例子。如果您不需要整合可视化,也可以将此模板用于您的中央服务器。您也可以在没有流媒体工具的情况下运行这些应用程序,只需使用计算机视觉服务生成的信息向您的服务器发送请求。如果你想使用 streamer,但是你想隐藏人脸,你总是可以使用人脸检测模型(比如alwaysai/res10 _ 300 x300 _ SSD _ ITER _ 140000),然后在对 edgeiq.blur_objects() 的调用中使用从结果返回的预测。当然,如果您曾经开发过使用健康相关数据的生产应用程序,您将需要遵守 HIPAA 和卫生组织的所有要求。
疫苗接种中心用例很可能不再需要,但是您可以使用计算机视觉来捕捉许多不同的事件!
利用数据科学找到你的下一家餐馆

在你的城市开一家 Chipotle 连锁店| Justin Snyder 摄影在 Unsplash 上
这个博客提供了一种利用现有的特许经营位置来决定你的餐馆的最佳位置的方法。
注意:关于详细的分析和统计,请查看 GitHub 代码!
虽然传统上,根据可行性研究、各种成本和经济因素来确定在哪里开餐馆是一个重要的决定,但依靠数据科学可以提供更深入的见解。住在伊利诺伊州布卢明顿的我继续寻找下一个最好的墨西哥卷饼店,使用他们在 Kaggle 上的位置数据,如下图所示。

图 1。 Chipotle 在美国的特许经营店。热门位置(红色)和其他位置(蓝色)|作者图片
地理分析
下一个需求是获取邻居信息,这里我们没有直接提供。幸运的是,各种供应商都提供 API 数据,如 Google、Yelp 等。存在。我推荐 Foursquare ,它每天提供大约 50000 个免费 API 信用点,并且易于使用,提供:
- 场馆-该地区的所有场馆
- 用户-用户的个人资料详细信息
- 提示-用户的评分、照片和评论
我们可以迭代我们的位置并查询场馆以获得相关信息,如下所示:
结果为我们提供了邻近位置及其类别。数据可以整理为一个数据帧:
数据预处理
数据可能有一些重复次数太少的类别。根据数据集的大小,设定一个截止频率(例如:最少出现 5 次),并去除其他异常地点。然后,我们可以得到如下所示的数据帧:

图二。包含位置和查询的邻域数据的初始数据帧|作者的图像
我们的目标是为我们的餐厅主题确定最佳场地,并以此为基础筛选出我们城市的类似区域。最后的预处理步骤是为每个地点找到前 10 个最常见的地点。

图 3。入围网点附近的公共场馆|图片作者
聚集场馆
入围后,一种方法是只选择最常见的场馆。另一方面,聚类方法可以为我们提供一个地区的共同主题的答案。使用 K-Means 聚类提供了这些途径。
对于当前场景,最佳位置与其他特许经营位置的比较如下:

表 1。最佳表演地点和普通小吃店的场馆主题对比
两个城市都有相似的顶级场馆,唯一的区别参数是顶级场馆附近的人口密度较高。
本地相似集群
最后一项任务是查询所选城市中的类似位置。使用 Foursquare API,查询以上场馆,可以入围最佳位置。根据聚类,可行的位置必须靠近其他类似的餐厅,尤其是墨西哥的场馆。
关于地点的最终决定还必须考虑避免新的特许店靠近现有的 Chipotle 地点。
使用该图,有 3 个位置应该避免,留下两个区域有相似的场地:城市的西部和南部(市中心)部分。

图 4。布鲁明顿墨西哥餐馆的位置(绿色)和墨西哥小吃店的位置(红色)|图片由作者提供
商业决策
根据查询,我们比较这两个地区,以最终确定餐馆可以在哪里开业。最后要考虑的是社区的安全和价格。

图五。西部的位置更安全,同样昂贵|作者图片
结论
考虑到以下因素,城市西部似乎是最佳选择:
- 南部有一大群餐馆,竞争会很激烈。
- 除此之外,市中心区(南部)附近有许多餐馆,竞争非常激烈。
- 另一方面,在相对安全的西部地区,餐馆都很远。
- 此外,该地区是一个很好的匹配,因为其他机构在价格和目标人群方面与 Chipotle 相似。

图 6。布卢明顿所有餐馆的位置。突出显示的区域是建议打开下一个系列的地方|作者图片
这是对地理位置的预可行性可行性检查,最终位置可以根据该地区的实际土地可用性、价格和其他因素来决定。
非常感谢您的阅读!我希望听到您的反馈,如果您有任何疑问,我会尽快回复。
使用 git 子模块在 docker 映像中安装一个私有的定制 python 包
这是一个复杂的题目,但我发誓并不难

这条蟒蛇已经包装好了,可以装船了!(图片由世界光谱在像素上拍摄)
哇,这个标题包含了很多术语!简单地说,这篇文章为您提供了最好的方式,让您可以私下轻松地分享您的 Python 代码,甚至可以在 docker 容器中运行它!最终,您将能够分发随处运行、易于维护和更新的私有包。我们来编码吧!
准备
您已经创建了一个 Python 脚本,其中包含了您想要与其他人共享的各种方便的函数。对于如何实现这一点,您有两种选择:
- 将您的代码打包并在 PyPi 上公开发布,详见本文 。这意味着任何人都可以通过调用
pip install *yourpackage*来安装这个包,就像熊猫一样 - 将你的代码打包并私下分发 如本文 所述。这意味着只有某些人能够安装这个软件包,比如你公司的同事。
我们选择第二个选项,确保我们的代码保持私有。我们将基于第二个选项中的文章,因此请务必先阅读它。阅读完之后,您就有了一个 Python 包,您可以从您的 g it 存储库中 pip 安装它。

一个专为你准备的私人 Python 包!(图片由像素在像素上拍摄)
问题是
如果你遵循了本文 中的说明,你的 git repo 中有一个包,你可以像pip install git+https://github.com/mike-huls/toolbox.git一样安装它。在这个例子中,我们将想象我们正在创建一个 Python 项目,它使用了本文中的工具箱包。
我们希望在 docker 容器中运行我们的项目。为了构建 Docker 映像,我们必须首先将源代码复制到映像中,然后安装我们使用的所有软件包。这是通过提供我们使用的所有包的列表来完成的。您可以使用pip freeze > requirements.txt生成这个列表。生成的 requirements.txt 将包含我们所有包的名称和版本。
问题是即使是我们私装的包也是按名称和版本列出的。这导致 pip 在 PyPi 上搜索工具箱,但是它找不到这个包,因为我们已经从我们的 Git repo 中私下安装了它。我们也不能提供我们的git+[GIT_REPO_URL],因为我们的 Docker 映像没有登录 git 的凭证。有一种方法可以使用 ssh 密钥来解决这个问题,但在我看来这很难管理;有一个简单得多的选项,可以让您的同事更容易地获得代码,而不必麻烦地生成或分发 ssh 密钥。

pip 试图在公共 PyPi 上找到我们的私有包(图片由 Andrea Piacquadio 在 Pexels 上提供)
解决方案
我们将使用 git 子模块将包拉到我们的项目目录中,安装它,然后修改我们的 Docker 文件,将包安装到我们的 Docker 映像中。方法如下:
1 添加子模块
转到您的项目根目录,然后:
git submodule add [https://github.com/mike-huls/toolbox](https://github.com/mike-huls/toolbox) _submodules/toolbox
(为了便于说明,我在这里使用了一个公共存储库) 这将在您的根目录下创建一个名为 _submodules 的文件夹,其中包含另一个包含您的 Python 包的文件夹。
2 安装软件包
只需执行
pip install _submodules/toolbox来安装软件包
3 创建 Dockerfile 文件
一旦我们想要创建 Docker 图像,只需重复前面的两个步骤;将子模块文件夹复制到映像中,然后再次运行安装。
**FROM** python:3.8
WORKDIR **/**app
**#** Install regular packages
**COPY** requirements.txt .
RUN pip install **-**r requirements.txt
**#** Install submodule packages
**COPY** _submodules**/**toolbox _submodules**/**toolbox
RUN pip install _submodules**/**toolbox*--upgrade*
**#** **copy** **source** code
**COPY** .**/** .
**#** command **to** run **on** container **start**
CMD [ "python", "./app.py"]
就是这样!现在你有了一个拥有“真相之源”的中央储存库。您可以在这里提交问题、添加功能请求、协作和推送更新,非常简单。其他优势包括:
可用性:
用户可以继续使用pip install git+REPO_URL;这不足以建立形象。你的程序员可以用一种非常简单的方式继续安装你的包。然后,当代码准备好构建到映像中时,只需拉出子模块并将其包含在 docker 文件中,就可以非常容易地包含该包。
一个干净的 repo
除此之外,git 将为您跟踪您的子模块。调用`pip freeze > requirements.txt``并注意到工具箱没有列出。这是因为 git 知道它是一个子模块。它也被忽略了,这样你就不会“污染”你的项目报告。
轻松更新
更新我们项目中使用的软件包非常简单;只需执行:
git submodule update --remote --merge
pip install _submodules/toolbox --upgrade
这将首先把所有新代码拉到包文件夹中,然后升级包。别忘了用一个虚拟环境!

用定制的、私人的 Python 包运送那些容器(图片由 Cameron Venti 在 Unsplash 上提供)
结论
定制的私有 Python 包易于安装、维护、更新和分发。使用这个简单的方法将它们构建成 Docker 图像会使它们更加令人惊叹。
我希望我在这篇文章中的解释是清楚的。如果你有建议/澄清,请评论,以便我可以改进这篇文章。同时,查看我的其他关于各种编程相关主题的文章。编码快乐!
—迈克
页(page 的缩写)学生:比如我正在做的事情?跟我来!
使用谷歌云平台发送电子邮件通知
使用 Postfix、Mailjet 和 DNS 记录配置您自己的每日股票摘要报告

布雷特·乔丹在 Unsplash 上的照片
如果你曾经想过找到一种方法,可以轻松地不断了解你个人投资的当前结果,这篇文章就是为你准备的。
在以前的帖子中,我介绍了一种使用谷歌服务管理个人投资的方法,如谷歌资产负债表和计算引擎(来自谷歌云平台)。我鼓励您也访问这些帖子,因为您可以浏览主要实现文件(但也包括虚拟机目录)所需的步骤,我们将在这里使用这些文件作为我们解决方案的输入。
下面,我列出了我们在这篇文章中将要经历的主要步骤:
- 文件输入和存储它的实例(1)
- 用于邮件服务本身的实例(2)
- Mailjet 和电子邮件真实性
请记住,我使用谷歌云平台作为选择的云服务提供商,但如果你愿意,你应该可以使用其他服务。
1.文件输入和存储它的实例(1)
如上所述,这里将要使用的文件输入是我之前分享的其他帖子中提到的解决方案的结果。为了理解当前的文章流,阅读这些文章并不是强制性的。然而,为了从整体上实现解决方案,您还应该遵循其中涵盖的所有步骤。
文件本身非常简单:
实例 1 中的每日结果示例文本文件
导入要突出显示的内容:
- “lucasnunesfe9”是实例用户;
- “实例-示例”是正在使用的实例;
- 该文件的路径是:"。/python script/log/result _ output . py "
除此之外,这个文件的内容是什么?
这是两列,代表一个表。第一列“Ticker”是在巴西股票市场上交易的本例投资组合公司的每一张股票(用作示例),第二列“Result”是在给定时刻每张股票的余额。更具体地说,该文件设置为从上午 10 点到下午 5 点(UTC-3)每 15 分钟生成一次,使用 cron 作业。
好了,这应该足以让你熟悉我们将在本文中使用的输入。
2.用于邮件服务本身的实例(2)
你可以考虑从谷歌云平台实例发送电子邮件的多种方式。在我的案例中,我选择了 Mailjet 服务,因为:
- 从构建和维护 SMTP 中继服务器的角度来看,我需要选择一些第三方电子邮件服务来避免这种开销;
- 从选择哪个第三方电子邮件服务选项的角度来看,主要是因为在撰写本文时,它提供了相当数量的每月免费发送的电子邮件(6,000 封),并且如果需要,它有一个很好的界面用于一些配置和活动指标分析。此外,它的部署过程相当直截了当。
请使用后面的链接来浏览部署它的非常详细的步骤,我没有理由在这里粘贴它们。我会确保在整篇文章中突出可能的棘手部分。
SSH 文件输入实例
由于这个项目考虑了两个实例,所以需要确保这两个实例可以相互访问。更具体地说,应该允许前面提到的第二个实例连接到第一个实例。
请看下图:

实例 1 和实例 2 通信图
这是必需的,因为我们通过部署了 Mailjet 服务的实例(实例 2)发送电子邮件通知内容(输入文件,在实例 1 中可用)。
防火墙规则
您可能会注意到,在上图中,我还包括了“VPC 网络”标签。这是相关的,因为默认情况下,2 个实例所属的 GCP VPC 网络拥有一些防火墙显式规则,这些规则允许实例之间通过 SSH 进行通信(更具体地说,“default-allow-internal”和“default-allow-SSH”-确保它们都已设置并应用于实例,并且没有排除实例 IP 的 IP 范围过滤器)。
除了上面提到的默认防火墙规则之外,还有一个防火墙规则需要额外设置:
- 流量类型:出口
- 协议:TCP
- 端口:2525(Mail Jet 服务的一部分,逐步部署)
发送电子邮件
您应该在实例 2 中设置一个 cron 作业,理想情况下,在实例 1 中设置的最后一个文件输入更新之后运行(以便每天使用最后一个更新)。
我将 cron 作业输入拆分如下,以便单独识别每个部分:

Cron 作业语法分解
这就是发送电子邮件作为每日股票结果通知的目的。
3.Mailjet 和电子邮件真实性
在遵循 Mailjet 的服务部署流程时,系统会指示您配置您的电子邮件发件人地址。只要选择流程中的这一步,您就可以通过访问此链接轻松进行配置(向下滚动页面,直到出现下图):

Mailjet 片段
如果已经完成了这一步,那么只需要等待 Instance 2 cron 作业运行。当它运行时,您将在几秒钟内收到电子邮件通知:

电子邮件通知片段
好了,成功了!但是,有没有什么引起你的注意?这个橙色的消息框肯定不是理想的。这是什么意思?
在这个例子中,我使用我的 Gmail 帐户发送电子邮件通知。正如你所注意到的,据说“邮件声称是从你的账户发出的,但 Gmail 无法核实实际来源”。换句话说…不可能确保这封邮件是由我在 Mailjet 中列为授权发件人的电子邮件地址发送的。这就是当涉及到 DNS(域名服务器)记录配置。
DNS 记录是一堆信息(简要查看),在处理域名和主机名时可能非常重要。特别是在我们的例子中,我们特别感兴趣的是 TXT 记录(可以用于您的邮件域之外的源的文本信息,在这个例子中,用于 Mailjet),称为 SPF(发件人策略框架)。
在其他也可以用于安全措施的 TXT 记录中,如 DKIM 和 DMARC,SPF 专门用于指定允许为您的域发送电子邮件的邮件服务器。您可以通过配置您的域的 DNS 记录来做到这一点。
问题是免费的网络邮件服务,如 Gmail(我的例子),还有其他如雅虎或 Outlook,不允许我们配置他们的 DNS 记录。出于这个原因,你应该使用一个域名托管服务(GoDaddy,Bluehost,DreamHost,仅举几个例子),它通常允许你编辑它的 DNS 记录。
因此,这一步将取决于你选择哪个域名托管服务,但你应该找到它的 DNS 管理页面,在那里你将把 Mailjet 的信息添加到你的域名 DNS 记录中。
通过更新 Mailjet 中的邮件发送者列表和 cron 作业的邮件发送者部分,我等待 cron 作业再次运行,以检查橙色消息框是否已经消失:

DNS 配置后的电子邮件通知片段
略有不同,对吧?但是,这对于确保您的电子邮件通知不会被标记为垃圾邮件(从安全角度来看是正确的)非常重要!
结论
通过上图概括你所做的事情:

总图
你已经设法每天将你的股票结果通知到你的电子邮件中,并确保你的电子邮件客户端不会将其标记为垃圾邮件。
这一过程为您提供了一个基础,您可以根据自己的情况考虑不同的需求和条件,在此基础上进行调整。
是的,电子邮件的正文设计必须改进!
注来自《走向数据科学》的编辑: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
使用 ipywidgets 和 matplotlib 可视化噪声分解:这是毕达哥拉斯的
使用 3D 向量来帮助你的大脑。
自本月(4 月 22 日)起,Medium 决定你的作品必须拥有至少 100 名追随者才能获得奖励。 如果你喜欢这篇文章,只要你按下那个“跟随”按钮 :)很多欢呼,我希望你喜欢这篇文章!

图片作者。
众所周知,独立噪声源的总噪声方差是方差之和。因此,对于方差为σi 的 3 个独立噪声源,我们得到:

因此,总噪声标准偏差为:

这个方程可以简单地比作坐标向量(X,Y,Z)的通常欧几里得距离,其长度为:

看到这里的模式了吗?
这意味着,我们可以将具有独立标准偏差(σ₁、σ₂、σ₃)的“噪声矢量”表示为具有坐标(X=σ₁、Y=σ₂、Z=σ₃)的空间中的点:总标准偏差则由该矢量的范数(或欧几里德距离)给出。
我们的大脑习惯于将一个和分解成一组互相相加的项(x+y+z = total),但它真的不擅长使用“平方”分解(d = x +y +z)来分解。使用我们的大脑更习惯看到的 3D 向量表示,可以帮助我们更好地理解总噪声来自“哪里”:它主要来自σ₁吗?一点σ₂?没有σ₃?
在这篇文章中,我们将看到如何快速构建一个交互式可视化工具来表示噪声源及其对总噪声的影响,使用 ipywidgets 来实现交互体验,以及 matplotlib 的 3D 绘图功能。
Python 实现
为了可视化我们的噪声分解,我们将使用:
—“matplotlib”及其用于绘图的 3D 工具包
—“ipywidgets”来创建滑块和回调以更新绘图
我们将创建 3 个滑块来控制每个标准偏差的值,这类似于 3D 噪声空间中总噪声的(X,Y,Z)坐标。
我们首先创建 3 个浮动滑块来设置我们的独立噪声分量,一个标签来显示总标准偏差,一个“输出”来保存我们的 matplotlib 数字/ax。然后,我们将所有东西一起装入一个“HBox”中。注意创建子情节时的“投影=3d”参数:这是我们告诉 matplotlib 我们想要在 3d 中工作的方式。
现在,在回到` init '构造函数方法之前,我们将添加一些有用的方法。首先,我们创建一种方法来检索每个标准差分量值,并使用众所周知的等式σtot = √(σ₁ +σ₂ +σ₃来计算总标准差。
现在,我们使用 matplotlib 的“颤动”函数来绘制一组向量,以显示每个噪声分量以及总噪声。
最后,一个简单的“repr__”方法可以轻松地显示我们的滑块和图形输出:
现在回到“init”方法,我们定义了一个回调函数,它将用于在任何滑块的每次变化时更新标签和颤动图。然后我们在每个滑块上注册这个回调:
最后,我们初始化箭筒,并添加轴标签和限制:
这再容易不过了
我们现在可以创建一个实例,并开始试验:
瞧!
然后,您可以移动每个浮动滑块来设置每个标准差的值。该图会相应地更新每个向量的长度,并显示总标准偏差,这也表示 3D 向量的长度。

在我让你走之前,请注意:
- 尝试更改主向量的颜色,以便更好地区分独立向量和分量向量。
- 尝试使用
%matplotlib qt获得更好的图形性能
完整代码
使用机器学习来检测数据集中的错误
如何使用泛化语言大海捞针

雷诺·莱蒂安在 Unsplash 上的照片
结构化数据集中损坏的数据值比您想象的要常见得多。早期检测这些错误对于下游分析任务的执行至关重要。然而,手动检查每个数据点既不高效也不可能。
这个故事利用了泛化树和泛化语言的概念,并提出了一种聚合不同语言的结果来发现表格数据集中的格式错误的方法。如果你需要泛化树和语言的详细介绍,你可以浏览下面的故事。
简而言之,由泛化树导出的泛化语言将原始值泛化成模式。例如,让我们考虑下图中给出的一般化树。

作者图片
从那棵树,我们可以得到一种语言,如果x不是一个符号,它将每个变量x转换成一个值\A。这种语言将把像03.17.2019这样的原始日期值转换成模式\A{2}.\A{2}.\A{4}。
沿着这个思路,在 《自动检测:表中数据驱动的错误检测 、 、黄志鹏和叶烨中,他使用了一种泛化语言来缓解检测错误时的稀疏性问题。正如我们将在后面看到的,不同的语言对不同类型的错误是敏感的。因此,问题陈述可以概括为一个约束优化难题。
我们如何选择一组适当的泛化语言,由一个特定的泛化树归纳,以检测尽可能多的错误,受内存预算和精度值?
根据这一定义,我们得出以下挑战:
- 如何创建验证数据集来评估每种泛化语言
- 如何设置阈值
θ,低于该阈值时,我们的分数表示样本错误 - 如何聚合选择不同泛化语言所导致的结果
学习率是为那些对 AI 和 MLOps 的世界感到好奇的人准备的时事通讯。你会在每周五收到我关于最新人工智能新闻和文章的更新和想法。在这里订阅!
生成验证数据的远程监督
给定一种泛化语言L,我们必须有一种方法来量化它在检测错误时的精度。例如,给定一个表语料库C(即,一组用作训练数据的许多表),如果我们将一个值v归纳为一个模式p₁ = L(v),并且发现它与由C中一个表的同一列中的不同值u导出的另一个模式p₂ = L(u)不兼容,我们应该能够判断这是正确的预测还是误报。
我们可以手动标记例子,并用它们来验证一种泛化语言的性能,但这通常太昂贵,而且扩展性不好。为了克服这个问题,我们可以使用一种叫做的远程监控技术。因此,为了创建一个将验证泛化语言的数据集,我们从我们的表语料库C中取出两个干净的列,我们将其标记为C₁和C₂。然后,我们对来自C₁的值v进行采样,并将其与C₂的副本合并,产生一个新的“脏”列C₂'。这是因为v很可能与C₂中的其他值不兼容。

由于日期格式不同,C₁的红色值与 C₂的其他值不兼容。因此,C₂'现在是一个新的,由作者生成的“肮脏”专栏— 图像
接下来,我们可以使用C₂中的任意值u和我们合并的值v:(u,v,-)——符号表示不兼容的对,来创建否定验证示例(即不兼容值对)。同样,我们可以创建兼容值对:C₂中任意一个u,t的(u,t,+)。这样工作,我们有一组用 **T¯** 表示的不相容对和一组用 **T⁺** 表示的相容对。这两个集合的联合构成了验证集合 **T** 。这组数据并不完美,但我们可以廉价地创建的大量数据往往会比那些根据少量手工标注的样本训练的模型更好。
在下面的部分中,我们使用具体的例子来更好地理解。

表 1: NPMI 使用三种不同的概括语言对 10 个例子进行评分——图片由作者提供
表 1 给出了如果我们使用三种一般化语言:L₁、L₂和L₃一般化五个正面(即兼容)和五个负面(即不兼容)的例子,我们得到的 NPMI 结果。NPMI 代表归一化的点态互信息,由下面的公式给出。

简而言之,计算两个值在同一列中共存的概率,超过在一列中遇到值v和u的概率的乘积。现在,我们应该定义一个阈值θ,低于该阈值的 NPMI 分数表示错误。
阈值定义
现在让我们考虑表 1 中的语言L₁。如果我们想要计算它的精度分数,我们应该将正确分类的错误的数量除以它识别为错误的所有示例的数量。

因此,第一步是定义一个阈值,低于该阈值的示例被认为是损坏的。例如,让我们设置这个阈值θ = -0.4。从T⁺开始,第三个示例被归类为错误,从T¯开始,第六、第八和第九个示例被视为无效。因此,我们可以计算精度为p = 3/4 = 0.75。问题是θ = -0.3或-0.2会产生相同的结果。我们应该有办法唯一确定θ。为此,我们定义以下公式。

有了这个,我们可以为θ设置一个唯一的值,为固定的精度值P。因此,在上面的例子中,为了达到精度p = 0.75,我们设置了θ = 0.5。这是因为任何小于0.5的值在精度方面表现较差,而任何大于0.4或0.3的值违反了第二个约束。
以这种方式,我们可以确定哪些例子应该被标记为错误,给定它们的 NPMI 分数。然而,为了达到更好的覆盖率,我们应该选择一套泛化语言,因为每个人对不同类别的错误都很敏感。因此,下一步是要看看我们如何聚集由我们的语言选择引发的预测。
结果汇总
对于本节,让我们定义两种语言。第一个定义如下。

第二个的公式如下。

让我们考虑两个示例值:03.17.2019和03-17-2019;两个日期使用不同的模式。使用第一种语言,我们相应地将两个例子概括为\A{2}.\A{2}.\A{4}和\A{2}-\A{2}-\A{4}。对于第二种语言,我们得到同样的模式:\D{2}\S\D{2}\S\D{4}。正如我们所看到的,用语言L₂概括不会发现格式错误。因此,一个简单的获得平均 NPMI 分数的方法是行不通的,因为使用第二个推广计算的结果应该被完全忽略。
然而,考虑到这两种语言的互补性,我们应该选择这两种语言,因为L₁不会发现第一个示例值和像03.MR.2020这样的模式之间的差异。为了汇总结果,我们只需要一种语言就可以对它产生的分数有信心。因此,我们选择最低的 NPMI 分数。直觉上,因为每种语言对不同类型的错误敏感,如果一种语言对一个值v产生非常低的兼容性得分,我们信任它,因为它真的很自信。因此,汇总不同泛化语言结果的一种方法是联合它们的预测。例如,使用表 1,如果我们联合L₁和L₃预测的误差,我们检测到每个误差加上由L₁产生的一个假阳性。
下一节将介绍一种贪婪算法,根据内存预算和精度值来选择由特定泛化树导出的最终泛化语言集。然后,我们可以使用这个集合来概括我们的值,计算 NPMI 分数,并根据某个阈值来检测错误。
自动检测的贪婪算法
假设我们有一组三种候选泛化语言。每种语言都在内存中占据一个特定的空间,该空间与一个阈值θ相关联,并且根据使用该阈值做出的预测,还会有一个精度分数。表 2 总结了所有这些。第一组包含每种语言在T¯中指示为错误的示例,第二组包含每种语言在T⁺中预测为错误的示例。

表 2:一组候选的泛化语言及其度量——作者图片
我们的目标是根据内存预算选择一个子集,其中包括我们拥有最佳错误覆盖率的语言M = 500MB。下面的算法返回这个集合。

让我们一步一步来。作为输入,我们有一个集合L,它是候选泛化语言的集合。我们还初始化一个空集G和一个变量来计算使用的内存(即curr_size = 0)。首先,从候选集L中,我们只保留那些其内存占用在我们限制范围内的泛化语言M。因此,从表 2 中可以看出,所有语言都在500MB的限制范围内。接下来,我们寻找能提供最大增量收益的语言。这由下面的公式给出。

我们增加curr_size变量,并从我们保存的语言集中减去选择的语言。根据内存预算,我们不断迭代,直到选择了所有可能的语言。然后,我们也选择最好的单个语言,并将其与集合G的错误覆盖率进行比较。最后,我们返回检测到更多错误的内容。
为了使用上面的例子将其接地,首先,我们选择L₁,因为它具有最大的增量增益,并将其附加到 set G。

下一轮,我们只能选择L₂,因为选择L₃会违反我们的记忆限制(200 + 400 > 500)。然后,我们选择单一的最佳语言,即L₃,因为它具有最佳的错误覆盖率。最后,我们将集合G ( 5错误)的错误覆盖率与语言L₃ ( 4错误)的覆盖率进行比较,并返回集合,因为它检测到更多的错误。
有了最佳的泛化语言集,我们就可以基于它们来泛化我们的值,计算 NPMI 分数,并预测可能的错误。
结论
在这个故事中,我们介绍了 【自动检测:表中数据驱动的错误检测】【黄志鹏】和何,以及他们自动检测结构化数据集中格式错误的方法。
我们提出了一种使用远程监督创建验证集的方法,一种为每种语言唯一确定阈值θ的方法,以及一种从候选集中找到泛化语言最佳组合的贪婪算法。
关于作者
我叫迪米特里斯·波罗普洛斯,我是一名为阿里克托工作的机器学习工程师。我曾为欧洲委员会、欧盟统计局、国际货币基金组织、欧洲央行、经合组织和宜家等主要客户设计和实施过人工智能和软件解决方案。
如果你有兴趣阅读更多关于机器学习、深度学习、数据科学和数据运算的帖子,请在 Twitter 上关注我的 Medium 、 LinkedIn 或 @james2pl 。此外,请访问我的网站上的资源页面,这里有很多好书和顶级课程,开始构建您自己的数据科学课程吧!
面向开源可复制机器学习的 MLflow 和 DVC
DVC 和 MLflow 是两个被广泛采用的开源项目,各有所长。DVC 擅长数据版本控制,MLflow 是多种工具的结合体,但主要用于实验跟踪和工件记录功能。这两个工具都是开源的,但这并不意味着您可以轻松地拥有一个使用它们的开源项目。您无法将使用 DVC 版本化的数据与开放世界共享,也无法将为您生成的实验页面 MLflow 显示给所有人-这种情况已不复存在。
DAGsHub 已经与 DVC 整合,你可以在我之前讨论 DVC 和 DAGsHub 的文章中了解更多。它现在也与 MLflow 集成在一起。这意味着 MLflow 用户现在可以在一致的环境中管理 MLflow 实验及其代码和数据集。
如果您只对集成细节感兴趣,您可以滚动到下面关于 MLflow with DAGsHub 的部分。否则,请继续阅读,以获得 MLflow 的概述、MLflow 的一些优势和差距,以及关于 DAGsHub 和 MLflow 集成的详细信息
什么是 MLflow
MLflow 是一个管理 ML 生命周期的开源平台,包括实验、再现性、部署和中央模型注册
GitHub 上每天约有 60,000 次下载,8,000 次下载——ml flow 是一款开源工具,最初由 Databricks 推出,自 2018 年推出以来广受欢迎。它帮助数据科学家管理机器学习生命周期的多个阶段。其中一个阶段是“实验跟踪”——这是一个公认的术语,用来记录模型每次运行时得到的结果的变化。MLflow 提供的收集这些实验的解决方案被称为“MLflow Tracking”。其他工具也提供了很好的功能,我推荐阅读这个实验跟踪工具对比来更深入地了解它们之间的区别。
MLflow 有什么用途,为什么它是实验跟踪的首选解决方案
它有最大的社区
一个大型的机器学习从业者和公司社区已经采用 MLflow 作为解决方案。这通常是一个强有力的指标,表明某个工具正在做正确的事情。一个大的社区意味着更多的教程,更多的在线问答,或者针对用户遇到的常见问题在 GitHub 上的公开问题。MLflow 已经有 290 个贡献者,据报道有 200 多家公司在他们的工作流程中使用它,这对这类项目来说意义重大。这提供了一种安全感,MLflow 将在未来许多年继续开发和使用
简单且开源
开源工具是个人、研究人员和非营利组织的必需品。这为他们提供了免费的解决方案、社区支持和最大的灵活性。MLflow 无疑是当前实现这一承诺的先锋之一。跟踪 API 是精心设计的,具有全面而简单的客户端库,提供简单的手动日志记录功能,如:
# Start a run
mlflow.start_run()# Log an hyper-param
mlflow.log_param()# Log a metric
mlflow.log_metric()
它还为大多数常见的高级机器学习框架提供自动日志记录功能,包括 Scikit-learn、Tensorflow、Keras、XGBoost、FastAI、PyTorch 等。
以 Scikit-learn 为例:
# Automatic logging with mlflow
mlflow.sklearn.autolog()
设置起来相对容易——开始跟踪实验所需要的只是安装一个客户端库(即 python 库)并在代码中使用日志 API 调用。
要预览实验或比较结果,您需要通过在命令提示符下运行以下命令来启动一个基于 web 的本地用户界面:
# Start the mlflow web ui
mlflow ui
你可以在[http://localhost:5000](http://localhost:5000.) 的浏览器中找到它。
实时跟踪
“实时”意味着您可以在代码运行时跟踪性能——或任何指标,这在您想要观察损失时特别有用,例如,如果没有收敛,就在中间停止训练。实时日志记录是一把双刃剑,因为它不能保证再现性。这将实验跟踪工具的领域一分为二。有些人——比如 ml flow——将跑步训练的开始视为基线。其他人——像DVC——在培训结束时将存储库的状态视为需要签入和访问的时间点。
MLflow 缺少什么使其成为显而易见的选择
设置远程服务器很难
为一个小团队或开源项目设置一个持久的远程服务器在技术上是复杂的,并且可能是昂贵的。
首先,你需要建立一个在线机器,可以被外界访问,最好有一个可读的、持久的 URL。
其次,由于您不希望任何人能够查看或记录您的服务器上的实验,您需要管理访问控制。您可以通过支持身份验证的中间服务器来运行您的调用,然后将通信传递给服务器本身。如果您希望能够添加用户、删除用户并提供对项目的公共访问,您需要围绕它构建一个完整的系统。不幸的是,对许多人来说,这些复杂性将是交易的破坏者。如果你最终设法解决了这些问题,你仍然需要向云提供商支付运行机器的费用,你可能不会为你正在写的论文或你的开源人脸检测项目这样做。这就是为什么 MLflow 实验可能永远不会走出您的本地机器或内部网络。
用户界面缺乏灵活性
Mlflow 的用户界面有一个主菜单,您可以在其中选择和切换不同的实验。“实验”是一组“运行”,用户可以将其定义为一个层次结构。您可以决定是为每个项目创建一个实验,还是为同一个项目创建多个实验。您可以选择多个运行并在它们之间进行比较,但不能在不同实验的运行之间进行比较。
对于每次运行,您可以选择您记录的图形,并在单独的页面中显示它们,但是您不能在单个摘要页面中一次性预览它们。
缺乏背景和连通性
假设您已经安装了 MLflow 服务器,并使用它为多个项目记录实验。现在,您想与同事共享项目的状态。您需要给她发送一个指向存储库的链接,一个指向 MLflow 服务器的链接,并指定这个特定项目的实验内容。另一种常见的情况是,当您想要将您的研究结果发布到工作场所之外的社区时。您需要找到一种方法来应用如前所述的凭证管理,以便只有他们可以查看项目。此外,您的 MLflow UI 作为整个项目的一部分是不可发现的。您可以在存储库Readme.md文件中放置一个链接,但是前面提到的访问问题仍然适用。
带 DAGsHub 的 MLflow
连接到项目的其余部分
随着 MLflow tracking now 与 DAGsHub 的集成,您最终可以以一种有意义的方式管理 MLflow 实验。项目的所有组件现在都已连接。换句话说,您的存储库主页现在将显示您的代码、您的数据管道和您的所有 MLflow 实验。

按作者
您可以比较实验,看到一个有趣的实验,并轻松地一键查看生成实验的代码和数据集。正如在谈论 MLflow 的用户界面时提到的,比较不同实验的运行需要打开两个不同的窗口。现在情况不再是这样了。现在可以比较不同实验的运行,当然,可以在一个地方看到所有的度量图。

按作者
最小设置
首先,你需要在注册,创建一个存储库,并向其中推送代码。然后,在您的存储库主页上,您将获得 MLflow 的跟踪 URI。

按作者
假设您已经熟悉如何使用 MLflow 的,那么您现在要做的就是将这个 URI 设置为您的跟踪 URI,或者使用环境变量MLFLOW_TRACKING_URI或者在您的代码中添加以下代码行:
# Set an mlflow remote server programatically
mlflow.set_tracking_uri("https://dagshub.com/<DAGsHub-user-name>/<repository-name>.mlflow")
如果您想要记录参数和指标,您需要拥有对存储库的写权限——也就是成为贡献者。MLflow 客户端支持基本认证,为了指定您的凭证,您需要使用环境变量MLFLOW_TRACKING_USERNAME和MLFLOW_TRACKING_PASSWORD。
就是这样,你可以记录你的实验,并像这个例子项目一样与公众分享。如果你的知识库是私有的,那么实验当然也是私有的。
分发您的项目并与任何人共同创作
我们在 DAGsHub 的任务之一是降低开源机器学习生命周期工具的准入门槛。我们在 DVC 做到了这一点,现在我们正在与 MLflow 合作。我们让用户决定如何利用他们喜欢的每个方面。我认为,为社区提供一个专业管理工作的解决方案,而不需要 DevOps 知识,将使开源、可复制的数据科学不仅仅是一个幻想。
摘要
我们已经看到 MLflow 被广泛采用,它是开源的,并且简单明了,但是如果没有任何 DevOps 知识,免费使用它会有一些困难。现在,通过使用专用的 MLflow 远程服务器将 MLflow 连接到任何存储库,您可以从 DAGsHub 的实时日志记录中受益。这里有一个关于如何使用 MLflow 和 DAGsHub 的视频链接。如果您有任何问题,或者您希望我们支持更多 MLflow 功能,请随时通过我们的社区聊天联系我们。
机器学习在经济研究中的应用:文献告诉我们什么
对过去四十年来发表在主要经济期刊上的 20 篇优秀文章的选择性综述。

克拉克·杨在 Unsplash 上的照片
M 机器学习主题在 20 世纪 80 年代开始大规模出现在经济文献中,当时主要概念如反向传播、递归神经网络(RNNs)和受限玻尔兹曼机器(RBM) 被发现,像计算机视觉这样的主题吸引了很多关注。在过去的几十年里,在四个主要的经济期刊上发表的关于机器学习的文章的频率增加的趋势是显而易见的(见下图)。
在这篇文章中,我分享了机器学习在经济学中应用的文献中的发现,并简要回顾了我认为在这一领域最有影响力的 20 篇文章。这是从经济期刊(基于文章影响力得分)中主观选择的论文,可能对计划深入这一令人兴奋的领域的应用研究人员或对人工智能溢出到其他领域感兴趣的任何人有用。
我确定了在经济文献中使用 ML 方法的四个主要领域:
- 预测和预报
- 情感分析和自然语言处理(NLP)
- 图像处理和计算机视觉
- 流程自动化和优化
最后,我参考了这位领先经济学家对未来的展望,并对经济学中的整个人工智能领域的发展方向做了一些评论。

经济学前 4 名期刊(QJE、计量经济学、AER、Restud)的 ML 文章,来源: Constellate。
将人工智能融入经济学的早期尝试
关于这个主题的第一篇文章是 1984 年美国经济评论(AER)的一篇文章,作者是 Cohen 和 Axelrod ,该文章展示了一个将信念更新为新体验的模型,该模型依赖于一个相当古老的人工智能程序来玩跳棋。作者表明,该模型的性能优于其他国家的最先进的模型。
在一项令人兴奋的研究中,亚瑟(AER,1991) 设计了一个参数化的学习自动机,一种“复制”人类行为的算法,并根据人类受试者对其进行校准。该论文的结论是,我们可以设计人工学习代理,并校准它们的“合理性”,以复制人类行为。
霍兰德和米勒(AER,1991) 研究了复杂适应性人工智能系统和经济主体的行为。如今,存在广泛的基于计算机的自适应算法来探索这种系统,包括分类器系统、遗传算法、神经网络和强化学习机制。
过程自动化
在任何领域中,ML 的理想用例是优化和过程自动化,从这个意义上讲,是节省劳动力成本和(或)提高生产率。例如, Fernald 和 Jones (AER,2014) 提出,人工智能将允许机器取代工人,这在某种程度上可能导致未来更高的增长。新的研究技术可能会让计算机和机器人取代劳动力,从而极大地影响生产和经济增长。
预测和预言
监督最大似然法已经在许多应用中用于预测,在那里它达到极好的性能。与其他一些统计方法相比,超参数调整、训练验证测试分割和改进的优化算法(如 ADAM )有助于实现更好的 ML 性能。
Kleinberg 等人(AER,2015) 和 Kleinberg 等人(QJE,2017) 强调了使用机器学习技术改进预测如何能够产生巨大的政策影响。用这种方法可以解决许多问题。例如,在刑事司法系统中,法官必须决定是拘留还是释放被捕者。依赖于对被捕者犯罪概率的预测的决定可以得到 ML 的支持。
其他例子包括:(1)在教育中,预测哪个老师会有最大的增值;(2)在劳动力市场政策中,预测失业持续时间以帮助工人决定储蓄率和求职策略;(3)在监管方面,有针对性地进行卫生检查;(4)在社会政策方面,预测高风险青年,以便有针对性地进行干预;以及(5)在金融领域,贷款人识别潜在借款人的潜在信用价值。,2015) 。

照片由 Unsplash 上的 Clarisse Croset 拍摄
同样, Chalfin 等人(AER,2015) 证明了使用最大似然法提高工人生产率预测所带来的社会福利收益。他们在两个基本应用中说明了这种方法的价值——警察招聘决策和教师任期决策。
Bajari 等人(AER,2015) 回顾并应用几种流行的 ML 方法进行需求估算。他们表明,与标准线性回归或 logit 模型相比,这些方法可以产生更高的预测准确性。
还有许多其他的例子,在这些例子中,ML 被用来预测对政策决策很重要的变量。在国际经济学中,艾买提等(JIMF,2018) 利用序列岭回归对主要国际货币的汇率进行预测。这是一个为政策制定实施最大似然法的实际例子,因为他们得出结论,他们的最大似然法模型导致改进的预测和更低的均方根误差(RMSE)。
情感分析和自然语言处理
越来越多的带标签的训练数据集刺激研究人员在经济学的各种问题上使用情感分析。带有文本每个部分的标签的情感数据集(例如,1 =正面,0 =负面;1 : 25 是 1 最负,25 最正,等等。)帮助研究人员制作可以在自己的数据集中使用的分类器。
随着 2010 年代 NLP 和语言建模的巨大进步,一些优秀的论文出现在顶级期刊上。一些综述文章包括阿尔加巴等人(JES,2020) ,概述了在经济科学中使用文本、音频和视觉数据的语义分析方法。 Gentzkow 等人(JEL,2019) 纯粹关注文本数据,并审查适当的统计方法和对经济问题的各种应用。
在货币经济学中,汉森等人(QJE,2017) 利用计算语言学发现央行行长的沟通模式。文本数据的量化可以衡量央行行长声明的影响,并简化丰富的沟通措施的构建。 Hansen 和 McMahon (JIE,2016) 用类似的基于文本的方法研究同一主题。
NLP 技术可能有助于提高标准统计方法的准确性。例如,在企业融资中, Goberg 和 Maksimovic (RFS,2014) 改进了对金融约束(即企业获得融资的能力)的衡量。在预测不利经济冲击后的投资削减方面,基于文本的指标优于文献中使用的其他指标。
NLP 的商业经济学应用包括 Bandiera (JPE,2020) 使用广泛使用的 NLP 算法(潜在的狄利克雷分配— LDA)构建 CEO 行为指数。除了良好的性能,它还允许减少数据的高维度。
图像处理和计算机视觉
图像处理和图形数据处理文章常用各种卷积神经网络(CNN),并频繁使用大数据。许多数据集已经可以免费获得,研究人员可以为他们的应用程序使用预处理的数据或预训练的模型(为了完整性,这个过程被称为迁移学习)。
Donaldson 和 Storeygard (JEP,2016) 使用卫星数据(来自轨道的遥感数据)审查文章的文献。小卫星在地球上空一定距离飞行,收集照片图像和其他数据,这些数据可用于研究地球上的过程。例如, Henderson 等人(AER,2012) 使用卫星数据来衡量次国家和超国家地区的经济活动(GDP 增长),这在当前的衡量标准下是不可能的。
纳伊克等人(PNAS,2016) 使用街景的 360 度全景图像来预测美国五个城市的城市物理变化(街道的物理外观)的决定因素。纳伊克等人(AER,2016) 关注城镇的物理外观与其居民的行为和健康之间的实证联系。他们使用支持向量机从街道图像中量化城市外观。
我们要去哪里?
经济学中 AI 的研究向何处去,未来的研究有哪些方向?让我们从苏珊·艾希(斯坦福大学商学院)那里得到启发,她预测在不久的将来会有新的发明出现。
我在此引用了来自 Athey (2019) 的确切内容:
- 为预期任务(预测、分类和聚类,例如文本分析)采用现成的 ML 方法。
- 对预测方法的扩展和修改,以考虑公平性、可操作性和可解释性等因素。
- 开发基于机器学习的新计量经济学方法,旨在解决传统的社会科学评估任务。
- 利用现代数据设置,包括大面板数据集和具有许多小实验的环境,对因果效应的识别和估计策略的渐进进展。
- 更加强调模型的稳健性和其他辅助分析,以评估研究的可信度。
- 经验主义者大规模采用新方法。
- 生产力和测量研究的复兴和新路线。
- 设计和分析大型管理数据的新方法,包括合并这些来源和隐私保护方法。
- 跨学科研究的增加。
- 经济研究的组织、传播和资助的变化。
- 作为工程师的经济学家与公司、政府一起在数字环境中设计和实施政策。
- 与企业和政府合作,设计和实施数字实验,包括一次性的和持续的过程,包括多臂 bandit 实验算法。
- 研究开发可快速测量的高质量指标,以促进快速增量创新和实验。
- 在各级经济学教学中更多地使用数据分析;跨学科数据科学项目的增加。
- 人工智能和 ML 对经济影响的研究。
结论
我收录了 20 篇 A 级文章,展示了 ML 方法在经济学中的大量应用。在未来,我们可以期待在经济期刊上发表的文章快速增长,以及人工智能社区正在开发的新技术的实施。目前,最大似然法仍然是一种应用统计方法,但由于其独特的统计方法,它在许多情况下优于标准统计方法。特别是在预测方面,它在预测的准确性方面占主导地位。
我想提到的最后一篇论文是 Mullainathan 和 Spiess (JEP,2017) 对这个主题做了很好的介绍。
PS:你可以订阅我的 邮箱列表 每次我写新文章都会收到通知。如果你还不是中等会员,你可以在这里加入https://medium.com/@petrkorab/membership。
参考文献的完整列表(按出现的顺序):
[1]迈克尔·d·科恩和罗伯特·阿克塞尔罗德,1984 : 应对复杂性:改变效用的适应性价值,《美国经济评论》,第 74 卷,第 1 期(1984 年 3 月),页 30-42。
[2 ] W. Brian Arthur,1991: 设计行为像人类代理人的经济代理人:有限理性的行为方法,《美国经济评论》,第 81 卷,第 2 期,论文与会议录。(1991 年 5 月),第 353-359 页。
[3]约翰·h·霍兰德和约翰·h·米勒,1991:经济理论中的人工适应性主体,《美国经济评论》,第 81 卷,第 2 期,论文与会议录。(1991 年 5 月),第 365-370 页。
[4] John G. Fernald 和 Charles I. Jones,2014:美国经济增长的未来,《美国经济评论》,第 104 卷第 5 期,论文和会议录。(2014 年 5 月),第 44–49 页。
[5]乔恩·克莱因伯格、延斯·路德维希、森迪尔·穆莱纳坦和齐亚德·奥伯迈尔,2015:预测政策问题,《美国经济评论》,第 105 卷,第 5 期。,论文和会议录,第 491-495 页。
[6]乔恩·克莱因伯格,希马宾杜·拉卡拉朱,朱雷·莱斯科维奇,延斯·路德维希,森迪尔·穆莱纳坦,2017:人类决策与机器预测,《经济学季刊》,第 133 卷第 1 期,(2018 年 2 月),第 237–293 页。
[7]亚伦·查尔芬、柳文欢·达尼埃里、安德鲁·希利斯、祖宾·杰尔维、迈克尔·卢卡、延斯·路德维希和森迪尔·穆莱纳坦,2015:机器学习的生产率和人力资本选择,《美国经济评论》,第 106 卷第 5 期,(2016 年 5 月),第 124–27 页。
[8] Patrick Bajari,Denis Nekipelov,Stephen P. Ryan 和 Miaoyu Yang,2015: 需求估计的机器学习方法,《美国经济评论》,第 105 卷第 5 期,论文与会议录(2015 年 5 月),第 481–485 页。
[9] Christophe Amat,Tomasz Michalski,Gilles Stoltz,2018:简单机器学习方法的基本面和汇率可预测性,J 国际货币与金融杂志,第 88 卷,2018 年 11 月,第 1–24 页。
[10] Andres Algaba,David Ardia,Keven Bluteau,Samuel Borms,Kris Boudt,2020:计量经济学与情绪:方法和应用概述,经济调查杂志,第 34 卷,第 3 期,2020 年 7 月,第 512-547 页。
[11] Matthew Gentzkow,Bryan Kelly,和 Matt Taddy,2019:作为数据的文本,经济文献杂志,第 57 卷,第 3 期,第 535-574 页。
[12]Stephen Hansen Michael Mcmahon Andrea Prat,2018:Fomc 内部的透明度和审议:一种计算语言学方法,经济学季刊,第 133 卷第 2 期,2018 年 5 月,第 801–870 页。
[13] Gerard Hoberg,Vojislav Maksimovic,2017:重新定义金融约束:基于文本的分析,《金融研究评论》,第 28 卷第 5 期,2015 年 5 月,第 1312–1352 页。
[14] Oriana Bandiera,Andrea Prat,Stephen Hansen,Raffaella Sadun,2020: 政治经济学杂志,第 128 卷第 4 期,2020 年 4 月。
[15]戴夫·唐纳森和亚当·斯托里加德,2016:从上面看:卫星数据在经济学中的应用,《经济透视杂志》,第 30 卷第 4 期,第 171–198 页。
[16] J. Vernon Henderson,Adam Storeygard,David N. Weil,2012:从外层空间衡量经济增长,《美国经济评论》,第 102 卷第 2 期,第 994-1028 页。**
[17] Nikhil 纳伊克、Scott Duke Kominers、Ramesh Raskar、Edward L. Glaeser 和 César A. Hidalgo,2017:计算机视觉揭示城市物理变化的预测器,美国国家科学院学报,7 月 18 日,第 114 卷,第 29 期,第 7571-7576 页。
[18]尼基尔·纳伊克,拉梅什·拉斯卡尔,塞萨尔·a·伊达尔戈,2016:城市也是物质的:使用计算机视觉来衡量城市外观的质量和影响,《美国经济评论》,第 106 卷第 5 期,2016 年 5 月,第 128-32 页。
[19] Susan Athey,2019:机器学习对经济学的影响,载于:人工智能经济学:一个议程,芝加哥大学出版社,2019 年 5 月,第 507–547 页。
[20] Sendhil Mullainathan 和 Jann Spiess,2017:机器学习:一种应用的计量经济学方法,经济展望杂志,第 31 卷第 2 期,2017 年春季,第 87–106 页。
在 TensorFlow 服务中使用预先训练的 Huggingface 模型
将数千个社区 NLP 模型投入生产

克里斯托夫·高尔在 Unsplash 上拍摄的照片
HuggingFace 简化了 NLP,只需几行代码,你就拥有了一个完整的管道,能够执行从情感分析到文本生成的任务。作为一个预训练模型的中心,加上它的开源框架 Transformers,我们过去做的许多艰苦工作都得到了简化。这使我们能够编写能够解决复杂的 NLP 任务的应用程序,但缺点是我们不知道幕后发生了什么。尽管拥抱脸和变形金刚执行了这种惊人的简化,但我们可能希望从所有代码中提取一些抽象,并简单地使用许多可用的预训练模型中的一个。在这篇文章中,我们将学习如何使用 TensorFlow 服务中的许多预训练模型之一,这是一种将机器学习模型投入生产的流行服务。
我将使用这个 Distilbert 预训练模型进行情感分析,它将预测给定文本是正面还是负面的。不幸的是,Transformers 没有直接导出到 TensorFlow Serve 的功能,因此我们必须做一些工作来实现我们的目标。首先,我们需要安装 Tensorflow、Transformers 和 NumPy 库。
pip install transformers
pip install tensorflow
pip install numpy
在第一部分代码中,我们将从 Transformers 加载模型和标记器,然后以正确的格式将其保存在磁盘上,以便在 TensorFlow Serve 中使用。
from transformers import TFAutoModelForSequenceClassification
import tensorflow as tfMAX_SEQ_LEN = 100model = TFAutoModelForSequenceClassification.from_pretrained("distilbert-base-uncased-finetuned-sst-2-english")callable = tf.function(model.call)
concrete_function = callable.get_concrete_function([tf.TensorSpec([None, MAX_SEQ_LEN], tf.int32, name="input_ids"), tf.TensorSpec([None, MAX_SEQ_LEN], tf.int32, name="attention_mask")])model.save('saved_model/distilbert/1', signatures=concrete_function)
首先,我们用 TFAutoModelForSequenceClassification 加载模型。加载变形金刚模型的一个关键方面是选择正确的类。由于我们使用预训练模型进行情感分析,我们将使用 TensorFlow 的加载器(这就是为什么我们导入了 TF AutoModel 类)进行序列分类。如果你不确定加载什么类,只需检查模型卡或“在变形金刚中使用”页面上的“拥抱脸模型”信息,以确定使用哪个类。
在代码片段中,我们声明了一个签名函数,它是 TensorFlow Serve 所必需的。这个函数对于向 TF Serve 中的可服务模型声明我们的数据的输入形状是必要的,它由变量 MAX_SEQ_LEN 定义。在这种情况下,我已经定义了模型将接受两个输入,两个大小为 200 的列表,分别用于 input_ids(我们从 tokenizer 获得的 id)和 attention_mask(如果输入序列长度小于最大输入序列长度时使用)。记住我们为每个输入声明的名称也很重要,因为它是我们稍后将在发送给模型的 HTTP 请求中定义的参数。在执行这几行之后,我们应该在工作目录中有一个包含以下文件的新目录:
如果你还没有安装 TensorFlow Serve (我推荐和 Docker 一起使用)现在就做吧。安装完成后,我们可以从 Docker 中提取映像,并使用加载了以下命令的 Distilbert 模型开始运行我们的服务:
docker run -p 8501:8501 --mount type=bind, source=/PATH_TO_YOUR_DIRECTORY/saved_models/distilbert, target=/models/distilbert -e MODEL_NAME=distilbert -t tensorflow/serving
这将导致 TensorFlow Serve 在端口 8501 加载 Distilbert(模型文件必须位于用数字命名的目录下,否则 TF Serve 不会加载模型),我们可以通过 HTTP 请求该端口。在下一节中,我将展示如何对我们的加载模型进行预测。
import requests
from transformers import AutoTokenizer, AutoConfigtokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased-finetuned-sst-2-english")text = "I like you. I love you"
encoded_input = tokenizer(text, pad_to_max_length=MAX_SEQ_LEN, max_length=MAX_SEQ_LEN)#TF Serve endpoint
url = "http://localhost:8501/v1/models/distilbert:predict"
payload={"instances": [{"input_ids": encoded_input['input_ids'], "attention_mask": encoded_input['attention_mask']}]}print(payload)
>> { "input_ids": [101, 1045, 2066, 2017, 1012, 1045, 2293, 2017, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "attention_mask": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }headers = {
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, data=json.dumps(payload))
print(json.loads(response.text)['predictions'])
>>[[-4.2159214, 4.58923769]]
我们首先从 transformers 调用模型标记器,将输入文本转换成 id 列表及其相应的注意掩码,两者都有填充,以获得发送到我们加载的模型所需的格式。令牌化后,我们对 TensorFlow 执行 POST 请求,使用正确的有效负载提供服务,在这里我们获得呈现的值。但是等等,这个模型不是应该对文本中的情感进行分类吗?嗯,我们还缺少最后一步,将 softmax 函数应用于我们的结果向量。
import numpy as npdef softmax(x):e_x = np.exp(x - np.max(x))
return e_x / e_x.sum(axis=0)print(softmax(json.loads(response.text)['predictions'][0]))
>>[0.0001 0.9999]
得到的向量是输入文本为负或正的概率。在这种情况下,0.0001 的概率为负,0.9999 的概率属于正类。如果我们将结果与用变压器加载管道的输出进行比较,我们可以看到两者是相同的。
总结这篇文章,我们使用了许多 huggingface 预训练模型中的一个,并将其加载到 TensorFlow Serve 上,这样我们就可以发出 HTTP 请求,并轻松地将这些模型扩展到生产中。我们还学习了如何使用模型记号化器向我们加载的模型发出请求,以及如何将 softmax 函数应用到我们的结果中以获得所需的值。在我的下一篇文章中,我将展示我们如何通过编写我们的记号赋予器来将自己从 transformers 库中完全抽象出来,并使用它来请求我们加载的模型。喜欢随时提出建议或改进意见,希望这篇文章能帮助你实现你的目标。
使用 Data.gov 上编目的公共数据集推动数据科学项目
Data.gov 存储了描述美国联邦、州和地方政府级别发布的超过 280,000 个免费和公共数据集的元数据。它简化了通过下载或 API 查找和获取数据的过程。

笔记本电脑上的图表照片。由 unsplash.com 的卢卡斯·布拉泽克提供。
介绍
最近,我发表了一篇关于如何获取和分析 analytics.usa.gov上关于公众使用大约 57,000 个美国联邦政府网站的数据的文章。Data.gov,另一个政府网站,作为各种政府数据集的公共交换中心。
Data.gov 包含描述超过 280,000 个免费公共数据集的元数据。它对由美国联邦政府实体管理的丰富多样的数据集合进行了编目,在某些情况下,还包括州、地方和部落政府级别的数据。Data.gov 是一个向美国公众开放的政府数据交换中心。
本文介绍了什么是 Data.gov,政府实体如何在网站上发布关于其公共数据集的元数据,网站编目的数据类型,如何搜索数据,以及如何从其源实体下载数据集。
什么是 Data.gov?
美国总务管理局(GSA)内的技术转化服务部 (TTS)负责管理 Data.gov。它在 2009 年建立了这项服务。迄今为止,TTS 已经收集、记录和发布了 280,518 个数据集的元数据。
以下声明指导 Data.gov 的工作:
使命 : 为美国公众设计并交付一个数字政府。
愿景 : 全民可信的现代政府体验。
TTS 用 CKAN 和 WordPress 建造了 Data.gov。它在 GitHub 上公开开发代码。
在 data.gov 上索引的数据集遵循DCAT-美国模式 v1.1 指南。通过这种模式,一组一致的元数据(标题、描述、标签、发布者等)被应用于所有数据集,以使它们可被发现和理解。
政府实体如何向 Data.gov 添加数据集?
《开放政府数据法案》是美国国会循证决策法案的基础的一部分,它要求联邦政府以开放和机器可读的形式向公众提供数据。同时,它必须保证隐私和安全。
操作指南指导政府机构发布元数据来描述网站上的公共数据集。一致的元数据提高了可发现性和影响力。
Data.gov 主要是一个联邦政府网站和服务。但是州、地方和部落政府也可以在平台上发布元数据来描述他们的公共数据集。
Data.gov 存储什么类型的数据?
如上所述,Data.gov 存储的元数据描述了存储在其他地方的成千上万个数据集中的数据。它不保存定义的数据,但存储和显示可下载文件和 API 的链接以获取数据。
超过 280,000 个数据集,Data.gov 上索引的数据太大了,无法简明地描述。以下是可用数据类型的一个小示例:
- 国内生产总值
- 气候和天气
- 税收、税率和退税
- 地理空间数据
- 住房统计
- 海洋
- 人口普查和人口
- 风能和太阳能
- 教育
- 出生率和死亡率
我怎样才能找到 Data.gov 的数据?
Data.gov 的数据目录使得搜索数据集变得容易。下面的屏幕截图提供了使用数据目录搜索页面查找数据集的提示。

Data.gov 数据目录搜索屏幕。作者捕获的图像。
如何从 Data.gov 获取数据和 API 信息?
通过点击搜索结果中的数据源链接,可以访问数据和 API。
下载数据
如下图所示,在数据目录搜索页面的搜索文本框中搜索关键字 fishing ,会返回 14230 个数据集。前两个数据集包含关于北达科他州鱼类资源和渔业设施的数据。

搜索“钓鱼”的数据集结果作者捕获的图像。
在本例中,当您单击数据源链接时,您将被转到北达科他州托管的网站或数据集文件。
点击 CSV 下载一个 CSV 文件。当在 Excel 中打开时,该文件显示该州在其湖泊中放养的鱼的数据。

北达科他州鱼群数据集的子集。作者捕获的图像。
API 信息
元数据 API
Data.gov 管理数据集的元数据,而不是原始数据。虽然搜索工具可以很容易地找到感兴趣的数据集,但可以在程序中使用 CKAN API 和 CSW 端点来查询数据集和检索元数据。更多信息参见数据采集。
数据集 API
大多数数据集都是可下载的文件。较少的数据集具有可用于访问数据的 API。要查找 API 或有关它们的信息,请使用在格式过滤器部分选择的 API 搜索Data.gov 数据集目录。

“钓鱼”和包含“API”的格式的搜索结果作者捕获的图像。
单击大烟雾山国家公园鱼类分布(2014) 数据集的 API 链接将打开一个新的浏览器选项卡。它显示一个页面,其中包含有关访问数据的信息。

单击大烟山国家公园鱼类分布(2015)数据集的 API 链接时显示的 arcGIS 页面。作者捕获的图像。
关于 Data.gov 的新文章
关注即将发布的关于 Data.gov 的文章,例如:
- 编写一个使用 CKAN API 访问数据集元数据的程序。
- 探索各种类型的数据集。
- 探索数据工具以支持数据从业者的工作。
- 有趣或不寻常的数据集。
结论
Data.gov 为美国各级政府管理的数据集提供易于查找的元数据。该平台简化了通过下载或 API 查找和获取数据的过程。
Data.gov 是政府做得好的一个例子。
关于作者
Randy Runtsch 是一名作家、数据工程师、数据分析师、程序员、摄影师、自行车手和冒险家。他和妻子住在美国明尼苏达州东南部。
关注 Randy 即将发表的关于公共数据集的文章,以推动数据分析见解和决策、编程、数据分析、摄影、自行车旅行等。你可以在 shootproof.com 和 shutterstock.com 看到他的一些照片。
使用 Python 和 Bulk Insert 将数据从 CSV 文件快速加载到 SQL Server 表中
了解如何编写 Python 代码来执行 SQL Server BULK INSERT 实用程序,以便将 CSV 文件中的数据高速加载到数据库表中
介绍
数据采集是许多数据分析项目和系统开发生命周期的重要组成部分。本文将向您展示如何编写一个简单的 Python 程序,该程序使用 BULK INSERT 实用程序将 CSV 文件中的数据快速插入到 SQL Server 数据库表中。

SQL Server 的批量插入实用程序可以直接从 Python 执行,以便将 CSV 文件中的数据快速插入到数据库表中。图片由作者提供。
为什么使用这种方法?
有许多方法可以将 CSV 文件中的数据加载到 SQL Server 表中。这里有几个方法:
- 从命令行运行大容量插入实用工具。
- 从 SQL Server Management Studio (SSMS)运行大容量插入实用工具。
- 使用 SQL Server Management Studio (SSMS)导入平面文件向导。
- 编写一个程序,打开 CSV 文件,逐个读取其记录,并调用 SQL INSERT 语句将行插入到数据库表中。
如果有这么多方法可以将数据从 CSV 导入 SQL Server 数据库,为什么还要编写一个 Python 程序来调用 BULK INSERT 实用程序将数据加载到表中呢?以下是为什么这可能是一个有用的方法的一些原因:
- Python 程序可以在执行大容量插入之前执行流程步骤。
- Python 程序可以确保不会将重复数据写入目标表。
- 该程序可以在大容量插入之后清理或转换数据。
- 它可以执行错误处理功能。
- 它可以通过电子邮件或其他方式发送有关其行动的通知。
本文中使用的软件
我使用 Windows 10 环境和各种微软工具及其他工具进行软件开发、数据库和数据分析工作。下面是我在这篇文章中使用的软件列表:
- Windows 10 Home —虽然我使用 Windows,但其中一些工具可能在其他操作系统中也能工作。
- 记事本 —记事本有助于查看原始格式的 CSV 文件数据。
- Microsoft Excel — Excel 有助于查看列和行格式的 CSV 文件中的数据。
- 微软 SQL Server 2019 Express—SQL Server 有很多版本。Express 和 SQL Server Developer 都是免费版本。
- 微软 SQL Server Management Studio(SSMS)——使用这个前端工具管理和查询 SQL Server 数据库。它是免费的,运行良好。
- 微软 Visual Studio 社区版 —我试过微软 Visual Studio 代码 (VS 代码)集成开发环境(IDE)。尽管如此,我还是更喜欢用 Visual Studio 编码,因为我已经使用它很多年了,我发现它很容易配置和使用,除了它众多的深度菜单选项。
- Python 3 . 9 . 2—这是撰写本文时最新的 Python 版本,但任何最新版本都应该可以工作。
生成测试数据
对于本文给出的例子,我从免费的在线数据生成器生成了一个包含 10,000 条虚构数据记录的 CSV 文件。该文件名为 c:\test_data_folder\person.csv。它包含列 ID、职务、电子邮件地址和姓氏。Python 程序将执行 SQL Server BULK INSERT 语句,将数据从文件加载到表中。

person.csv 文件的内容。图片由作者提供。
创建数据库和表
关于 SQL Server 版本的重要说明
微软在 SQL Server 2017 中引入了使用 BULK INSERT 从 CSV 文件插入数据的功能。因此,您需要该版本或更新版本才能使用此功能。
创建数据库
下面是创建名为 HR(人力资源)的数据库的步骤:
连接到 SQL Server
- 发射 SSMS。
- 连接到数据库服务器。在本例中,我使用 Windows 身份验证连接到本地安装的 SQL Server Express 实例。

连接到 SQL Server Management Studio (SSMS)中的 SQL Server 数据库引擎。图片由作者提供。
创建人力资源数据库
- 在对象资源管理器中展开[+数据库]节点。右键单击[+数据库],然后单击[新建数据库…]。
- 在“新建数据库”对话框中,在“数据库名称”文本框中输入“HR”。保持所有设置不变。单击[确定]创建数据库。

与 SSMS 一起创建人力资源数据库。图片由作者提供。
验证 HR 数据库出现在对象资源管理器中。如果没有,右键单击数据库,然后单击[刷新]。它应该进入视野。

对象资源管理器列出数据库。图片由作者提供。
创建表格
此时,HR 数据库将不包含任何表或其他对象,如存储过程。虽然 BULK INSERT 可以在运行时创建表,但我发现提前创建表有好处。例如,我可以指定表的键列以及每列的类型和长度。再次查看 CSV 文件中的示例数据,让我们创建具有相同名称和以下数据类型的列:
- ID — INT
- 职位名称 — NCHAR(60)
- 邮箱地址 — NCHAR(120)
- 名字姓氏 — NCHAR(80)
因为所有行都有所有列的值,所以将每一列设置为 NOT NULL。此外,由于 ID 是每一行的唯一标识符,因此选择它作为键。
按照以下步骤创建表格:
- 在对象资源管理器中,单击[+ HR]查看数据库中的选择。
- 右键单击[+表格],单击[新建],然后单击[表格…]。
- 输入如下所示的数据。输入列名、数据类型和允许空值后,右键单击 ID 列名并单击[设置主键]。将 ID 设置为键将确保表中只有一行可以包含任何 ID 值。

Person 表的列信息。图片由作者提供。
- 单击功能区菜单中的保存图标,在“选择姓名”对话框中,输入姓名“Person”点击[确定]保存表格。
- 在对象资源管理器中,单击[+ Tables]展开节点。然后,右键单击[- Tables]并单击[Refresh]。现在应该可以看到 Person 表了。
- 点击[+ dbo。Person]然后点击[+ Columns]来检查表格的结构。它应该类似于下图。

对象资源管理器中显示的人员表列。图片由作者提供。
Python 程序
现在 HR 数据库和 Person 表已经存在,让我们研究一个使用批量插入实用程序的简单 Python 程序。它只是将 CSV 文件中的所有记录插入到 Person 表中。
代码模块
这个 Python 程序由两个模块或文件组成:
- c_bulk_insert.py 包含了 c_bulk_insert 类。它包括连接数据库、构建和执行 BULK INSERT 语句的函数,以便将 CSV 文件中的数据插入到数据库表中。
- SQL _ server _ bulk _ insert . py简单地实例化 c_bulk_insert 类,并用完成其工作所需的信息调用它。
代码逻辑
当程序实例化类 c_bulk_insert 时,它执行以下步骤:
- 连接到 SQL Server 数据库。
- 使用目标表的名称、输入 CSV 文件和一些设置来构造大容量插入查询。
- 打开数据库游标。
- 执行查询。
- 清理:提交批量插入事务,关闭游标,并关闭数据库连接。
代码
模块 c_bulk_insert.py 中的 Python 类 c_bulk_insert 执行上述代码逻辑部分中描述的逻辑。
"""
Name: c_bulk_insert.py
Author: Randy Runtsch
Date: March 17, 2021
Description: This module contains the c_bulk_insert class that connect to a SQL Server database
and executes the BULK INSERT utility to insert data from a CSV file into a table.
Prerequisites: 1\. Create the database data table.
2\. Create the database update_csv_log table.
"""import pyodbcclass c_bulk_insert:def __init__(self, csv_file_nm, sql_server_nm, db_nm, db_table_nm):# Connect to the database, perform the insert, and update the log table.conn = self.connect_db(sql_server_nm, db_nm)
self.insert_data(conn, csv_file_nm, db_table_nm)
conn.closedef connect_db(self, sql_server_nm, db_nm):# Connect to the server and database with Windows authentication.conn_string = 'DRIVER={ODBC Driver 17 for SQL Server};SERVER=' + sql_server_nm + ';DATABASE=' + db_nm + ';Trusted_Connection=yes;'
conn = pyodbc.connect(conn_string)return conndef insert_data(self, conn, csv_file_nm, db_table_nm):# Insert the data from the CSV file into the database table.# Assemble the BULK INSERT query. Be sure to skip the header row by specifying FIRSTROW = 2.qry = "BULK INSERT " + db_table_nm + " FROM '" + csv_file_nm + "' WITH (FORMAT = 'CSV', FIRSTROW = 2)"# Execute the querycursor = conn.cursor()
success = cursor.execute(qry)
conn.commit()
cursor.close
sql_server_bulk_insert.py 模块实例化 c_bulk_insert。它用以下方式调用它:
- CSV 文件名
- SQL Server 实例引擎名称
- 数据库名称
- 目标表名
"""
Name: sql_server_bulk_insert.py
Author: Randy Runtsch
Date: March 17, 2021
Description: This program is the controller that uses the Microsoft Transact-SQL BULK INSERT
statement to quickly insert the rows from a CSV file into
a SQL Server table.
Prerequisites: 1\. Create the database data table.
2\. Create the database update_csv_log table.
"""from c_bulk_insert import c_bulk_insertbulk_insert = c_bulk_insert(r'c:\\test_data\\person.csv', 'xxxxx-DESKTOP-\\SQLEXPRESS', 'HR', 'Person')
结果呢
程序运行后,在 SSMS 执行一个选择查询,显示它将 CSV 文件中的记录写到 Person 表中。

在 SSMS 运行 SELECT 查询显示,Python 程序成功执行了 BULK INSERT 实用程序,将 CSV 文件中的记录写入 Person 表。图片由作者提供。
从这里去哪里
可能有许多原因和方法来增强程序。这里有一些想法:
- 将错误处理添加到数据库连接、查询执行和程序的其他部分。例如,程序可以使用错误处理来正常关机,重试一定次数,并通过电子邮件通知相关方。
- 自动化程序在需要时将数据插入数据库。例如,使用 Windows 任务计划程序安排程序定期运行,以便从新的 CSV 文件中插入人员记录。
- 创建并写入一个日志表以捕获程序开始和结束时间以及其他重要事件。添加其他消息,如错误详细信息,以确定要解决的问题。
结论
正如您所看到的,使用 Python 调用 BULK INSERT 是一种自动化部分工作流的方式,可以快速地将数据从 CSV 文件插入到 SQL Server 数据库表中。这可能是一种添加到您的数据分析或软件开发工具包中的便捷技术。
关于作者
Randy Runtsch 是一名数据分析师、软件开发人员、作家、摄影师、自行车手和冒险家。他和妻子住在美国明尼苏达州东南部。
关注 Randy 即将发表的关于公共数据集的文章,以推动数据分析解决方案、编程、数据分析、摄影、自行车旅行、啤酒等。
使用 Python 自动更新 PowerPoint
使用 Python 编辑 PowerPoint 幻灯片的分步教程

由 cyda 编辑的 PowerPoint 幻灯片
目标
在两篇关于更智能地使用 Microsoft Excel 的文章之后,我收到了一些直接消息,说他们对这一领域感兴趣,并希望我能分享更多关于其他 office 产品的信息。它刺激我做一个与办公室小贴士相关的系列文章的收藏。第一个是微软的 PowerPoint 。在本文中,您将了解到
- 如何确定 PowerPoint 幻灯片中的形状。
- 修改幻灯片,如插入图像或改变文字。
- 以不同格式输出幻灯片的方法,如 PNG 或 PDF。
另外,如果你还没有看过我写的关于微软 Excel 的文章,请随意查看下面的链接。
https://medium.com/mlearning-ai/use-excel-to-scrape-data-no-codes-required-f13748587b0d
背景
在许多人的日常工作中,他们不得不频繁地定期更新 PowerPoint 幻灯片中的数字,如表格数字、日期、KPI 统计数据等。每天花 15-30 分钟来处理这些乏味的工作是很烦人的。
在下面的示例中,我们假设定期更新货币汇率,以弹出变化最大的货币汇率,我们需要显示变化百分比最大的货币汇率的价格趋势。
所有代码和资料都上传到我的 GitHub。你可以查一下民间的 和这种回购的 来进一步研究。=)
步骤 1-定义相应的组件
首先,我们必须决定要更新的元素。以我的幻灯片为例,总共有 7 个地方需要更新。
- 上次更新日期时间
- 前 5 名表
- 底部 5 表
- 顶级数字标签
- 底部图形标签
- 最高数字
- 底部图形

滑梯的 7 个部件(照片由 cyda 拍摄)
步骤 2 —数据准备
在理解了要更新的组件之后,接下来是要用于更新的数据源。在一般的办公工作中,数据可以从 SQL server 中提取或从电子邮件中接收数据文件等。这里,我们将演示从互联网上抓取数据的情况。我们将使用雅虎财务数据作为例证。
[https://finance.yahoo.com/currencies](https://finance.yahoo.com/currencies)

来自雅虎财经的货币(照片由 cyda 拍摄)
我不会深入讨论数据是如何收集的,因为这不是本文的重点。基本上就是两行代码。首先,我们使用requests获取页面。然后,我们使用pandas提取表格。请注意,我们还记录了刮擦日期和时间,以备后用。
import requests
import pandas as pd
from datetime import datetimedatetime_now = datetime.now()
full_list_url='[https://finance.yahoo.com/currencies'](https://finance.yahoo.com/currencies')
full_list_page = requests.get(full_list_url)
df = pd.read_html(full_list_page.text)[0].drop_duplicates()
df['pct_change'] = df['% Change'].str.slice(stop=-1).astype(float)

测向数据结构(cyda 拍摄
这张桌子看起来不错,包括了我们需要的所有东西。我们现在可以进入下一步,对表进行排序,并获得前 5 名和后 5 名货币的汇率。
top_df = df.sort_values(['pct_change'], ascending=False).reset_index(drop=True)[:5]
top_df = top_df[['Name', 'Last Price', 'Change', '% Change']]bottom_df = df.sort_values(['pct_change'], ascending=True).reset_index(drop=True)[:5]
bottom_df = bottom_df[['Name', 'Last Price', 'Change', '% Change']]

top_df 和 bottom_df 的数据结构(图片由 cyda 提供)
数字都准备好了。唯一剩下的就是那两块地了。要生成绘图,您只需遵循以下代码。放心吧!我会一步一步地解释,这很容易理解。
- 获取货币汇率的数据。
- 提取收盘价并绘制成折线图。
- 设置图表的格式,如颜色、字体大小或透明背景,以便与 PPT 主题保持一致。
- 将绘图保存到 PNG 中。
import json
import matplotlib.pyplot as plttop_name = top_df['Name'][0].replace('/', '')
bottom_name = bottom_df['Name'][0].replace('/', '')for idx in range(2):
name = [top_name, bottom_name][idx]
file_path = ['top.png', 'bottom.png']
url = '[https://query1.finance.yahoo.com/v8/finance/chart/'](https://query1.finance.yahoo.com/v8/finance/chart/') + name + '=X?region=US&lang=en-US&includePrePost=false&interval=30m&useYfid=true&range=1mo&corsDomain=finance.yahoo.com&.tsrc=finance'
header = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'}
page = requests.get(url, headers=header)
temp_json = json.loads(page.text)
price_list = temp_json['chart']['result'][0]['indicators']['quote'][0]['close']
price_list = [price for price in price_list if price != None]
fig, ax = plt.subplots(figsize=(12, 6))
ax.plot(price_list, color='#43B7A4')
ax.set_xticks([])
ax.tick_params(axis='y', colors='#43B7A4', labelsize=20)
for axis in ['top','bottom','left','right']:
ax.spines[axis].set_color('#43B7A4')
ax.spines[axis].set_linewidth(4)
plt.savefig(file_path[idx], transparent=True)
1.获取数据
特定货币汇率的价格系列数据可以在以下链接中找到。只有一个参数,那就是该货币汇率的name。
url = '[https://query1.finance.yahoo.com/v8/finance/chart/'](https://query1.finance.yahoo.com/v8/finance/chart/') + **name** + '=X?region=US&lang=en-US&includePrePost=false&interval=30m&useYfid=true&range=1mo&corsDomain=finance.yahoo.com&.tsrc=finance'
请注意,这次您需要指定一个header,以便成功获取页面数据。标题用于假装您正在通过浏览器访问页面。
header = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'}
其余的应该很容易理解,这是获取页面数据并以 JSON 格式加载的常见过程。
page = requests.get(url, headers=header)
temp_json = json.loads(page.text)
2.绘制折线图
要得到价格表,我们只需检查数据的字典结构,就可以得到。有一点需要提醒的是,我在这里进行了一次检查,删除了价格表中的那些None数据,因为我发现价格表中有一些数据缺失。
price_list = temp_json['chart']['result'][0]['indicators']['quote'][0]['close']
price_list = [price for price in price_list if price != None]
我们使用matplotlib来绘制直线。
fig, ax = plt.subplots(figsize=(12, 6))
ax.plot(price_list, color='#43B7A4')
3.将图表风格化
我做了一些东西来润色剧情。
- 删除日期的 x 轴刻度
ax.set_xticks([])
- 更改 y 轴刻度的字体大小和字体颜色
ax.tick_params(axis='y', colors='#43B7A4', labelsize=20)
- 改变边框颜色,使线条宽度变大
for axis in ['top','bottom','left','right']:
ax.spines[axis].set_color('#43B7A4')
ax.spines[axis].set_linewidth(4)
4.保存情节
最后,我们将图形保存为 PNG 格式。请注意,要使背景透明,只需指定transparent=True。
plt.savefig(file_path[idx], transparent=True)

顶部和底部货币图(照片由 cyda 拍摄)
步骤 3— PowerPoint 更新
这是我们今天的重点。在使用 Python 编辑 PowerPoint 之前,需要有python-pptx包。要安装它,您可以在终端中键入以下代码。
pip install python-pptx
按照我们通常的做法,我先给你看所有的代码,然后一步一步地教你。
from pptx import Presentation
from pptx.util import Inches
import win32com.client
import oscurrencies_ppt = Presentation('Currencies.pptx')
slide = currencies_ppt.slides[0]shapes = slide.shapes
for shape in shapes:
if shape.shape_type == 13:
shapes.element.remove(shape.element)
top_img_path = 'top.png'
bottom_img_path = 'bottom.png'
top_pic = slide.shapes.add_picture(top_img_path, Inches(0.40), Inches(4.85), width=Inches(5.30))
bottom_pic = slide.shapes.add_picture(bottom_img_path, Inches(5.25), Inches(4.85), width=Inches(5.30))
ref_element = slide.shapes[0]._element
ref_element.addprevious(top_pic._element)
ref_element.addprevious(bottom_pic._element)shapes = slide.shapes
text_box_list = []
auto_shape_list = []
table_list = []
for shape_idx in range(len(shapes)):
shape = shapes[shape_idx]
if shape.shape_type == 17:
text_box_list.append(shape_idx)
if shape.shape_type == 1:
auto_shape_list.append(shape_idx)
if shape.shape_type == 19:
table_list.append(shape_idx)last_update_date_textbox_height = max([shapes[shape_idx].height for shape_idx in text_box_list])
last_update_date_idx = [shape_idx for shape_idx in text_box_list if shapes[shape_idx].height == last_update_date_textbox_height][0]
top_label_left = min([shapes[shape_idx].left for shape_idx in auto_shape_list])
top_label_idx = [shape_idx for shape_idx in auto_shape_list if shapes[shape_idx].left == top_label_left][0]
auto_shape_list.remove(top_label_idx)
bottom_label_idx = auto_shape_list[0]
top_table_left = min([shapes[shape_idx].left for shape_idx in table_list])
top_table_idx = [shape_idx for shape_idx in table_list if shapes[shape_idx].left == top_table_left][0]
table_list.remove(top_table_idx)
bottom_table_idx = table_list[0]paragraph = shapes[last_update_date_idx].text_frame.paragraphs[0]
paragraph.runs[4].text = datetime_now.strftime("%#d %b %Y %H:%M")
paragraph = shapes[top_label_idx].text_frame.paragraphs[0]
paragraph.runs[0].text = top_df['Name'][0].replace('/', ' / ')
paragraph = shapes[bottom_label_idx].text_frame.paragraphs[0]
paragraph.runs[0].text = bottom_df['Name'][0].replace('/', ' / ')
top_table = shapes[top_table_idx].table
for i in range(5):
for j in range(4):
cell = top_table.cell(i+1, j)
paragraph = cell.text_frame.paragraphs[0]
run = paragraph.runs[0]
run.text = str(top_df.iloc[i, j])
bottom_table = shapes[bottom_table_idx].table
for i in range(5):
for j in range(4):
cell = bottom_table.cell(i+1, j)
paragraph = cell.text_frame.paragraphs[0]
run = paragraph.runs[0]
run.text = str(bottom_df.iloc[i, j])currencies_ppt.save('New_Currencies.pptx')ppt_file_path = os.getcwd() + '\\New_Currencies.pptx'
powerpoint = win32com.client.Dispatch('Powerpoint.Application')
deck = powerpoint.Presentations.Open(ppt_file_path)
img_file_path = os.getcwd() + '\\Currencies.png'
powerpoint.ActivePresentation.Slides[0].Export(img_file_path, 'PNG')
pdf_file_path = os.getcwd() + '\\Currencies.pdf'
deck.SaveAs(pdf_file_path, 32)deck.Close()
powerpoint.Quit()
os.system('taskkill /F /IM POWERPNT.EXE')
代码看起来又长又复杂。不要惊慌。如果你理解了结构,你就能很容易地处理它。
- 指定您正在处理的幻灯片。
- 移除现有地块。
- 添加新的地块。
- 定义元件的形状指数。
- 逐个更新组件。
- 把 PPT 导出成我们想要的任何格式。
1.加载 PPT 并指定幻灯片
currencies_ppt = Presentation('Currencies.pptx')
slide = currencies_ppt.slides[0]
从上面的代码可以看到括号里的数字0。它指的是幻灯片编号。因为我正在制作第一张幻灯片,所以我将编号指定为0。例如,如果您的常规更新幻灯片在幻灯片 10 中,那么您必须将编号指定为9。
2.移除旧地块
在步骤 1 中,我们定义了 7 个组件,有两个图——组件 6 和组件 7。要替换它们,我们首先必须删除它们。否则,新图将与旧图重叠。但问题是‘我们如何指定幻灯片中的特定项目?’
在python-pptx中,不同的物体有不同的形状。要检查幻灯片中的形状,您可以执行以下代码。
for shape in slide.shapes:
print(shape.shape_type)
以我的幻灯片为例,下面是我收到的输出。
PICTURE (13)
PICTURE (13)
TEXT_BOX (17)
TABLE (19)
TABLE (19)
TEXT_BOX (17)
TEXT_BOX (17)
AUTO_SHAPE (1)
AUTO_SHAPE (1)
然后我们就可以算出老剧情应该是PICTURE (13)。要删除它们,我们只需要检查形状类型是否等于 13。
shapes = slide.shapes
for shape in shapes:
if shape.shape_type == 13:
shapes.element.remove(shape.element)
3.添加新的地块
使用以下功能可以很容易地添加图表。
add_picture(image_file, left, top, width=None, height=None)
基本上,你要做的事情是设置图像文件路径和你要放的图像的 x-y 位置,就像下面这样。
top_img_path = 'top.png'
bottom_img_path = 'bottom.png'top_pic = slide.shapes.add_picture(top_img_path, Inches(0.40), Inches(4.85), width=Inches(5.30))
bottom_pic = slide.shapes.add_picture(bottom_img_path, Inches(5.25), Inches(4.85), width=Inches(5.30))
请注意,图形的另一个步骤是指定图像的放置顺序。通过这样做,你必须找到一个相对形状作为参考形状,并使用addnext和addprevious函数来指定它们的关系。这就好比 PowerPoint 中的Bring Forward和Send Backward的概念。
ref_element = slide.shapes[0]._element
ref_element.addprevious(top_pic._element)
ref_element.addprevious(bottom_pic._element)

有无相对位置的区别(照片由 cyda 拍摄)
4.查找形状索引
要编辑形状,我们必须确定它引用了哪个形状索引。简而言之,我们首先将形状分类到不同的列表中,并根据x-coordinate (left)和y-coordinate (height)确定相应的形状索引。
shapes = slide.shapes
text_box_list = []
auto_shape_list = []
table_list = []
for shape_idx in range(len(shapes)):
shape = shapes[shape_idx]
if shape.shape_type == 17:
text_box_list.append(shape_idx)
if shape.shape_type == 1:
auto_shape_list.append(shape_idx)
if shape.shape_type == 19:
table_list.append(shape_idx)

将形状分类成列表(照片由 cyda 拍摄)
文本框列表
文本框有三种形状。为了识别最后更新日期时间形状,我们可以找到具有最大高度的形状。
last_update_date_textbox_height = max([shapes[shape_idx].height for shape_idx in text_box_list])
last_update_date_idx = [shape_idx for shape_idx in text_box_list if shapes[shape_idx].height == last_update_date_textbox_height][0]

文本框(照片由 cyda 拍摄)
自动形状列表
只有两种自动形状,即顶部标签和底部标签。它们具有相同的高度,但 x 坐标不同。在left中具有最小值的一个将是顶部标签,另一个将是底部标签。
top_label_left = min([shapes[shape_idx].left for shape_idx in auto_shape_list])
top_label_idx = [shape_idx for shape_idx in auto_shape_list if shapes[shape_idx].left == top_label_left][0]
auto_shape_list.remove(top_label_idx)bottom_label_idx = auto_shape_list[0]

自动整形(照片由 cyda 拍摄)
表格列表
由于也只有两个表格,我们可以简单地应用类似的技巧,就像我们在自动形状列表中所做的那样。

表(照片由 cyda 拍摄)
top_table_left = min([shapes[shape_idx].left for shape_idx in table_list])
top_table_idx = [shape_idx for shape_idx in table_list if shapes[shape_idx].left == top_table_left][0]
table_list.remove(top_table_idx)bottom_table_idx = table_list[0]
5.更新组件
要更新组件,一个非常重要的规则是遵循形状内部的变化值,而不是格式。在下面,我用最后更新日期时间作为例子。
纯文本(不要用这个)
shapes[last_update_date_idx].text = 'Last Update: ' + datetime_now.strftime("%#d %b %Y %H:%M")
带格式的文本(用这个)
paragraph = shapes[last_update_date_idx].text_frame.paragraphs[0]
paragraph.runs[4].text = datetime_now.strftime("%#d %b %Y %H:%M")

纯文本和带格式文本的插图(照片由 cyda 拍摄)
从上面的图像中,你可以看出区别。一个段落实际上不仅用于存储形状的值,还用于存储格式信息,如对齐、字体、超链接等。因此,请记住更改段落运行中的文本值,而不是形状值。
附图的标记实际上具有相同的结构,我将再次重复这个概念。让我们转到表值的更新。
top_table = shapes[top_table_idx].table
for i in range(5):
for j in range(4):
cell = top_table.cell(i+1, j)
paragraph = cell.text_frame.paragraphs[0]
run = paragraph.runs[0]
run.text = str(top_df.iloc[i, j])
基本上,这很简单。我们找到感兴趣的表格,并为表格中的每个单元格更改段落运行中的值。请注意,i指的是行记录,j指的是列。
6.导出文件
教程差不多完成了,最后一步是将文件转换成我们想要的格式。基本上,让我分享人们会使用的最常见的文件结构。
另存为 PPT
currencies_ppt.save('New_Currencies.pptx')
另存为 PNG
import win32com.client
import osppt_file_path = os.getcwd() + '\\New_Currencies.pptx'
powerpoint = win32com.client.Dispatch('Powerpoint.Application')
deck = powerpoint.Presentations.Open(ppt_file_path)img_file_path = os.getcwd() + '\\Currencies.png'
powerpoint.ActivePresentation.Slides[0].Export(img_file_path, 'PNG')deck.Close()
powerpoint.Quit()
os.system('taskkill /F /IM POWERPNT.EXE')
另存为 PDF
import win32com.client
import osppt_file_path = os.getcwd() + '\\New_Currencies.pptx'
powerpoint = win32com.client.Dispatch('Powerpoint.Application')
deck = powerpoint.Presentations.Open(ppt_file_path)pdf_file_path = os.getcwd() + '\\Currencies.pdf'
deck.SaveAs(pdf_file_path, 32)deck.Close()
powerpoint.Quit()
os.system('taskkill /F /IM POWERPNT.EXE')
结论
关于使用 Python 在 PowerPoint 中自动化工作的 office 技巧到此结束。如果你有兴趣了解更多这类工作技巧,请给个赞并关注。请继续关注我的下一篇教程。=)
如果你觉得我的文章有用,请在我的 LinkedIn 页面上为我的技能背书,鼓励我写更多的文章。
https://medium.com/mlearning-ai/use-python-to-send-outlook-emails-d673ce9e33e4 https://medium.com/mlearning-ai/use-python-to-process-pdf-work-7a9d77d718f4
用 Python 构建 Excel 自动化工具
用于销售分析的 Excel 自动化工具的设计可供您的同事在没有任何 Python 知识的情况下使用

Excel 自动化—(图片由作者提供)
你读过多少文章说“Excel 已死,Python 万岁”,“Python 是新的 Excel”或者“Excel 过时了”?
但是当你环顾四周,在你的团队或其他部门你几乎找不到其他同事使用 Python。
而当你提到它时,它被视为一个人们无法信任的黑匣子,因为“它太复杂了”、“我们看不到公式”或“我无法在我的电脑上运行它”。
推广它的第一步是让他们有可能在没有 python 知识的情况下在他们的计算机上运行您的脚本。
💌新文章免费直入你的收件箱:时事通讯
如果你喜欢看,看看这篇文章的视频版本
1.方案
你是一家大型零售公司的数据科学家,供应链团队的同事请求你帮助分析销售数据。
对于本例,我们将从 Kaggle 挑战:商店商品需求预测挑战中获取一个数据集。我在另一篇文章(链接)中使用这个数据集进行商店需求预测。
数据集
- 2013 年 1 月 1 日至 2017 年 12 月 31 日的交易
- 913,000 销售交易
- 50 独特的 SKU
- 10 店铺
目标:你的同事想要为每个商店建立一个按商品统计的月销售额数据透视表。
问题:拥有近 100 万条记录,您的同事正在接近 Excel 的极限。因此,他们请求数据科学家的支持,以找到神奇的解决方案。
2.解决办法
我们将设计一个 python 脚本来执行数据处理,创建数据透视表并将结果导出到 Excel 文件中。
您的脚本正在执行以下任务:
- 从 Kaggle Challenge 导入初始原始数据(sales_data.csv)
- 日期格式化
- 每对商店、商品按月销售的数据透视表
- 按商店和项目对记录进行排序
- 将最终报告保存在 Excel 文件中
3.使用可执行文件(.exe)
目标:创建一个可执行文件(。exe),它将运行您的脚本,并且可以在您的计算机上没有安装 python 的情况下使用。
工具箱中的新 Python 库:Pyinstaller
PyInstaller 将 Python 应用程序及其所有依赖项打包到一个包中。用户可以运行打包的应用程序,而无需安装 Python 解释器或任何模块。
第一步:安装 Pyinstaller
pip install pyinstaller
步骤 2:将 python 脚本保存在当前目录中
将它保存在与销售记录初始文件(sales_data.csv)相同的目录中。将文件命名为:“pivot.py”
步骤 3:使用 Pyinstaller 创建可执行文件
在命令提示符下输入以下命令
pyinstaller --onefile pivot.py

Pyinstaller 处理日志—(图片由作者提供)
当该过程完成时,您将看到此消息“从 EXE-00.toc 成功构建 EXE”还有你的(。exe) fill 会弹出一个名为“Build”的文件夹。
步骤 4:使用你的可执行文件

您的新(。文件处理日志—(图片由作者提供)
请你的同事剪切并粘贴这个(。exe)文件,并单击以启动脚本。由于在脚本的每一步都放置了打印消息,他们可以跟踪这个过程。
结果:在流程结束时,他们可以找到保存在您文件夹中的销售报告(sales_report.xlsx)。
超越:自动化数据提取
在开始构建报告之前,您需要从 ERP 中收集数据。如果您正在使用 SAP,您可能会对这一系列关于 ERP 自动化的文章感兴趣
https://www.samirsaci.com/sap-automation-of-product-listing-for-retail/ https://www.samirsaci.com/sap-automation-for-retail/
4.结论
关注我的 medium,了解更多与供应链数据科学相关的见解。
您现在有了一个工具来与其他同事分享您的工作,并构建将影响您公司的生产力并支持人们自动完成枯燥任务的工具。
这可以与使用 seaborn 的酷可视化、获取数据的外部 API 的连接、预测未来销售的机器学习模型或任何可以为您的计算带来附加值的东西相结合。
关于我
让我们在 Linkedin 和 Twitter 上连线,我是一名供应链工程师,正在使用数据分析来改善物流运营和降低成本。
如果你对数据分析和供应链感兴趣,可以看看我的网站
参考
[1] Kaggle 数据集,商店商品需求预测挑战,链接
使用 Python 来格式化 Excel
使用 Python 格式化 Excel 电子表格的分步教程

自动生成风格化报告的步骤(照片由 cyda 拍摄)
目标
你必须每天定期更新报告吗?你有没有想过一种方法来自动化这些繁琐,无聊,机器人的工作?你可能会说“是的,但是我不能,因为有很多格式的东西需要我手动添加到 Excel 中”。今天,我将向您展示 Python 如何处理 Excel 电子表格格式并自动生成报告。
加密货币为例
我是一个加密货币爱好者,我会每天查看不同硬币的价格。然而,逐个检查所有硬币是非常耗时的,这就是为什么我想每天早上用一个程序自动为我生成总结报告。如果你有兴趣了解更多的金融数据分析,可以随时查看我的出版物。
https://medium.com/financial-data-analysis
所有的代码和数据都上传到了我的 GitHub。你可以和民间的 这种回购 来进一步研究。=)
数据抓取
首先,我们需要收集数据以显示在表格中。例如,您可以从公司数据库中查询数据。您可以从您的业务伙伴处获取数据。或者就像我这个案例,瞬间从网上刮数据。我在这里使用的数据源是 CoinDesk API。关于这个 API 如何工作和参数设置的更多细节,你可以查看我下面的文章。
import json
from urllib import request
url = "[https://production.api.coindesk.com/v2/tb/price/ticker?assets=all](https://production.api.coindesk.com/v2/tb/price/ticker?assets=all)"
response = request.urlopen(url)
data = json.loads(response.read().decode())
通过上面的代码,我们可以很容易地获得不同硬币的最新加密货币定价。我在下面展示了数据应该是什么样子。
{'statusCode': 200,
'message': 'OK',
'data': {'BTC': {'iso': 'BTC',
'name': 'Bitcoin',
'slug': 'bitcoin',
'change': {'percent': 0.6642429710971107, 'value': 298.538427},
'ohlc': {'o': 44944.160494,
'h': 46843.67,
'l': 43480.540171,
'c': 45242.698921},
'circulatingSupply': 18814943.79249204,
'marketCap': 851238837219.2552,
'ts': 1631590859000,
'src': 'tb'},
'ETH': {...}}}
数据列表
然而,数据是 JSON 格式的,不容易阅读。因此,我们必须将数据预处理成表格格式。
def flatten_json(coin_dict):
flatten_dict = {}
def flatten(x, name=''):
if type(x) is dict:
for key in x:
flatten(x[key], name + key + '_')
else:
flatten_dict[name[:-1]] = x
flatten(coin_dict)
return flatten_dictimport pandas as pd
master_df = pd.DataFrame()
for coin in data['data'].keys():
temp_df = pd.json_normalize(flatten_json(data['data'][coin]))
master_df = master_df.append(temp_df)
master_df = master_df[['iso', 'name', 'ohlc_o', 'ohlc_h', 'ohlc_l', 'ohlc_c', 'change_percent']].reset_index(drop=True)
master_df.columns = ['Symbol', 'Name', 'Open', 'High', 'Low', 'Close', 'Pct_Change']
master_df.iloc[:, 2:] = master_df.iloc[:, 2:].apply(lambda x: round(x, 2))
master_df['Pct_Change'] = master_df['Pct_Change'] / 100
master_df = master_df.sort_values('Pct_Change', ascending=False).reset_index(drop=True)
master_df.to_csv('master_df.csv', index=False)
master_df.head()
为了将 JSON 转换成表,您可以遵循以下步骤。
- 展平 JSON 中每一个硬币数据的字典
- 将所有硬币数据帧添加到一个主数据帧中
- 选择我们感兴趣的栏目
- 重命名列以便于参考
- 四舍五入以简化表格
- 按百分比变化排序

cyda 拍摄的照片
Excel 格式
最后是主菜。只要记住一件事,在写代码之前,你应该先在头脑中有一个设计。例如,下面是我的模板草稿。有草稿的原因是因为它帮助你更好地定义列或索引来放置元素(标题、表格、备注等)。

照片由 cyda
1.将表格添加到 excel 中
file_name = "Cryptocurrency.xlsx"
sheet_name = "Summary"writer = pd.ExcelWriter(file_name, engine='xlsxwriter')
master_df.to_excel(writer, sheet_name=sheet_name, startrow = 2, index = False)
只需注意一点,您可能会看到有一个参数 startrow 设置为 2。这是因为我们希望保留前两行作为标题和间距,我们将在第 3 行开始写表(Python 计数值从 0 开始,所以基本上设置为 2 意味着第 3 行)

照片由 cyda 拍摄
2.将标题添加到 excel
from datetime import datetime
workbook = writer.book
worksheet = writer.sheets[sheet_name]worksheet.write(0, 0, 'Cryptocurrency Pricing Summary on '+datetime.now().strftime('%d %b %Y'), workbook.add_format({'bold': True, 'color': '#E26B0A', 'size': 14}))
若要定义要写入文本的单元格,您可以从上到下然后从左到右计数。比如你在写 A1,那么它就是(0,0)。如果你写的是 C4,那么应该是(3,2)。

照片由 cyda
3.将备注添加到 excel 中
worksheet.write(len(master_df)+4, 0, 'Remark:', workbook.add_format({'bold': True}))
worksheet.write(len(master_df)+5, 0, 'The last update time is ' + datetime.now().strftime('%H:%M') + '.')
这里有一个技巧要记住。由于数据框在每次更新中可能具有不同的行数,因此在编写注释字符串时,您应该更好地考虑它的记录。例如,这里我将行索引设置为 len(master_df)+4 ,这是表中的行数加上标题和间距。

照片由 cyda
4.给表格标题添加颜色
header_format = workbook.add_format({'bold': True, 'text_wrap': True, 'fg_color': '#FDE9D9', 'border': 1})
for col_num, value in enumerate(master_df.columns.values):
worksheet.write(2, col_num, value, header_format)
对于表头,引入了两个参数。第一个是 text_wrap 。它被设置为 True,这样,如果空间不足以显示,页眉的文本将换行到下一行。第二个是 fg_color。它用于设置单元格的前景色。

照片由 cyda
5.向表格添加边框
row_idx, col_idx = master_df.shape
for r in range(row_idx):
for c in range(col_idx):
if c == 6:
worksheet.write(r + 3, c, master_df.values[r, c], workbook.add_format({'border': 1, 'num_format': '0.00%'}))
else:
worksheet.write(r + 3, c, master_df.values[r, c], workbook.add_format({'border': 1, 'num_format': '0.00'}))
诀窍和以前的案例类似。只需记住将行索引设置为 r+3 ,因为表中第一个值前面有三行。

照片由 cyda
6.设置列宽
worksheet.set_column(0, 6, 12)
worksheet.set_column(1, 1, 20)
供您参考,函数参数如下:
(起始列索引、结束列索引、列宽)

cyda 拍摄的照片
结论
希望这篇文章能帮助到那些不得不定期更新 Excel 电子表格的人。如果你想知道更多的编程技巧,你可以订阅我的媒体,我会更新和分享更多的技巧。=)
如果你觉得我的文章有用,请在我的 linkedIn 页面上为我的技能背书,鼓励我写更多的文章。
原载于 cydalytics.blogspot.com 的
https://medium.com/mlearning-ai/use-excel-to-scrape-data-no-codes-required-f13748587b0d https://medium.com/mlearning-ai/use-python-to-send-outlook-emails-d673ce9e33e4 https://medium.com/mlearning-ai/use-python-to-process-pdf-work-7a9d77d718f4
使用 R 探索未开发的数据领域!

来源:https://unsplash.com/@andrewtneel
关于如何使用 R 从外部来源收集数据的案例研究。
背景
让我们假设对于一个项目,你需要关于你的客户的社会经济背景的数据,比如他们居住的社区的平均收入、教育水平、就业水平等等。通常,这些数据由一些外部公共或私人组织提供,最好是通过某种 API 提供。在美国,美国人口普查局是这类数据的重要来源。在我们最近的一个项目中,我们使用来自人口普查的家庭收入数据作为表中缺失的实际家庭收入数据的代理。
在本文中,我将展示如何使用“tidycensus”软件包从 ACS5 调查中获取收入数据。为此,我将展示如何编写定制的函数来包装现成的函数,以满足您的定制目的。
作为奖励,我将简要描述如何自动化整个过程,这样你就可以从服务器上运行这个过程,将数据保存在你的数据库中,并且每年用新的人口普查数据自动更新表格。
美国人口普查调查的简要背景
人口普查局进行不同类型的调查。基于它们的性质,它们并不都具有相同的数据或相同级别的数据。在这里,您可以找到不同调查类型的详细信息以及其他详细信息:https://www.census.gov/programs-surveys.html。就我们的目的而言,我们需要一个在最精细的地理层次上拥有家庭收入数据的调查。我们最终使用了美国社区调查 ACS5 调查,因为该调查包含人口普查区域级别的家庭收入数据,这是人口普查中家庭收入数据的最细化级别。
准备
获取您的 API 密钥
对于这个项目,我们将使用一个名为tidysensus的 R 包。在后台, tidycensus 包调用人口普查局提供的 API。要调用这个 API,您需要一个键,这是一个惟一的 ID,由人口普查局针对每个新用户自动生成。你可以从这里得到一把钥匙:https://api.census.gov/data/key_signup.html。
一旦你有了密钥,就把它保存在一个单独的文本文件或 r 脚本中。以便稍后我们可以加载该文件,并在函数中使用该键。
设置密钥
一旦你有了可用的 API 密匙,你需要设置它以供 tidycensus 包中的函数使用。您可以使用下面一行代码来设置它:
census_api_key('YOUR_API_KEY', install = FALSE, overwrite = TRUE)
现在我们已经具备了调用 Census API 的所有先决条件。
调用人口普查 API 获取家庭收入
使用下面几行代码,您可以获得美国整个州的所有地区级家庭收入数据。我们使用 ACS5 调查中的变量 B19013_001 。点击此链接,您可以了解有关所有可用变量的更多信息:【https://api.census.gov/data/2018/acs/acs5/variables.html 因此,要获得 2018 年 ACS5 调查的 IL 地区家庭收入中位数,您可以运行以下代码行:
get_acs(state = 'IL', year = 2018, geography = 'tract', variables = 'B19013_001', geometry = FALSE, survey = 'acs5', show_call = TRUE)
代码说明
get_acs() 函数用于调用 acs 调查的 API。您应该通读代码描述以了解详细信息,但这里是在这种情况下使用的参数的简要描述:
- 州:缩写名或州名,
- 年份:您要查找哪一年的 ACS 调查。
- 地理:你在看哪个地理层次。我们正在寻找道级数据。其他选项有县、街区等。
- 调查:你要找哪个调查。我们正在寻找**ACS5*调查,
- show_call:将其设置为 true 会在调用 API 时在 R 控制台中打印消息输出。当您将此功能投入生产时,在功能失败的情况下检查其背后的原因是非常有用的。
本规范的限制
我们的目标是获取数据,将数据存储在数据库中,然后安排脚本每年自动重新运行。由于以下限制,使用这种现成的功能并不能完全达到目的:
- 它必须有一个州名作为输入。我们可以把它做成动态的,这样我们就可以用它来加载所有状态的数据,或者只加载一组选定的状态,或者只加载一个状态,
- 我们希望将它安排在特定日期自动运行。但是,由于 ACS5 调查的发布日期每年并不完全相同,我们需要有一定的灵活性,以便在我们期望的年度调查没有填充的情况下,该函数可以获取最新的可用数据。
- 我们希望有一个列,其中记录了该数据被调用时的日期和时间,并将它与其他数据一起存储在数据库表中。
在接下来的几节中,我将指导您编写一个定制的函数来解决这些限制。
克服限制
在我们开始构建复杂的函数之前,我将从一个基本的包装器函数开始。然后我们会不断添加额外的参数来克服局限性。下面是 R 函数框架的样子:
functionName = function(input01, input2){
Logic
}
您给它起一个名字,以便将来您可以保存该函数并重用它。在括号内的函数()中,包含输入变量名。你把逻辑写在花括号里。
现在让我们编写一个基本函数来包装我们之前编写的两段代码,以获得 ACS 数据:
getAcsIncome = function(names, year, KEY = 'YOUR_KEY'){
## setting up API call key
census_api_key(apiKey, install = FALSE, overwrite = TRUE)
## calling get_acs()
get_acs(state = names, year = year, geography = 'tract', variables = 'B19013_001', geometry = FALSE, survey = 'acs5', show_call = TRUE)
}
我已经在一个单独的脚本中保存了我的 API 密匙。因此,我已经加载了脚本,并使用脚本中的密钥从 2018 年 ACS5 调查中获取 IL 的跟踪级别数据。
# Loading libraries and key
library(tidycensus)
source('KEY.R')# Wrapper function
getAcsIncome = function(names, year, KEY){
## setting up API call key
census_api_key(key = API_KEY, install = FALSE, overwrite = TRUE)
## calling get_acs()
get_acs(state = names, year = year, geography = 'tract', variables = 'B19013_001', geometry = FALSE, survey = 'acs5', show_call = TRUE)
}# Calling the function and display glimpse of result
IL_HH_Income = getAcsIncome(names = 'IL', year = 2018, KEY = API_KEY)head(IL_HH_Income)## # A tibble: 6 x 5
## GEOID NAME variable estimate moe
## <chr> <chr> <chr> <dbl> <dbl>
## 1 17001000100 Census Tract 1, Adams County, Illinois B19013_0~ 44613 6384
## 2 17001000201 Census Tract 2.01, Adams County, Illinois B19013_0~ 44878 4356
## 3 17001000202 Census Tract 2.02, Adams County, Illinois B19013_0~ 46964 10202
## 4 17001000400 Census Tract 4, Adams County, Illinois B19013_0~ 33750 7386
## 5 17001000500 Census Tract 5, Adams County, Illinois B19013_0~ 38526 4846
## 6 17001000600 Census Tract 6, Adams County, Illinois B19013_0~ 51491 10117
使州名输入更加灵活
现在我们有了一个操作函数,我们将进入下一步,我们将向它添加第一组参数,以使状态名输入更加灵活。
我们将在 R 中使用一个内置常量,即 state.abb,它包括 50 个州名缩写。在我们定制的包装函数中,我们将添加一些更改,以解决以下用例:
- 当输入为“全部”/“全部”时,下载所有状态数据
- 当输入是缩写形式的一个/多个州名时,下载选定的州数据
- 如果提供的输入与上述两种输入类型都不匹配,则提供一条错误消息
# Wrapper function
getAcsIncome = function(names, year, KEY){
## setting up API call key
census_api_key(key = API_KEY, install = FALSE, overwrite = TRUE)
## setting up blank array to store state names
stateNames = NULL
# when all states are required
if(names %in% c('all', 'ALL')){
stateNames = state.abb
}
# when specific state or states are mentioned in names
else if(names %in% c(state.abb)){
stateNames = names
}
# in any other cases
else{
print("Provide a value in stateNames variable. Available options: all/ALL/any of the 50 states (abb.)")
}
## calling get_acs()
get_acs(state = stateNames, year = year, geography = 'tract', variables = 'B19013_001', geometry = FALSE, survey = 'acs5', show_call = TRUE)
}head(getAcsIncome(names = 'all', year = 2018, KEY = API_KEY))## # A tibble: 6 x 5
## GEOID NAME variable estimate moe
## <chr> <chr> <chr> <dbl> <dbl>
## 1 01001020100 Census Tract 201, Autauga County, Alabama B19013_0~ 58625 14777
## 2 01001020200 Census Tract 202, Autauga County, Alabama B19013_0~ 43531 6053
## 3 01001020300 Census Tract 203, Autauga County, Alabama B19013_0~ 51875 8744
## 4 01001020400 Census Tract 204, Autauga County, Alabama B19013_0~ 54050 5166
## 5 01001020500 Census Tract 205, Autauga County, Alabama B19013_0~ 72417 14919
## 6 01001020600 Census Tract 206, Autauga County, Alabama B19013_0~ 46688 13043
在年份输入中添加回退功能
为了增加这种能力,我们将使用一个名为 tryCatchLog 的包。我们将使用的 tryCatch()函数的基本框架如下:
result = tryCatch({
expr
}, warning = function(w) {
warning-handler-code
}, error = function(e) {
error-handler-code
}, finally = {
cleanup-code
}
在这里,在花括号内,您添加要评估的代码,在第二个函数内,在警告/错误之后,提供第一个代码块失败时要执行的逻辑。上面的骨架就是从这篇文章里抄来的。那篇文章对如何应用 trycatch 函数有更详细的讨论。
在我们的例子中,我们将使用 trycatch 函数来更新一个变量。然后,我们将添加一个基于该变量的值运行的代码块。此外,如果第一个代码块失败,我们将打印出一条消息,错误消息将从显示它尝试的年份的日期开始打印。
函数中的 tryCatch 代码块如下所示:
# starting with variable: an.error.occured with value of FALSE
an.error.occured <- FALSE
tryCatch({
# trying for current year - 2
year = as.numeric(substr(Sys.Date(), start = 1, stop = 4)) - 2
# calling api to get data
data = tidycensus::get_acs(state = name, year = year, geography = 'tract', variables = 'B19013_001', geometry = FALSE, survey = 'acs5', show_call = TRUE)
}, error = function(e) {
# updating the variable
an.error.occured <<- TRUE
# printing out error message to be stored in log with the
message(paste0("Year tried: ", year, "/n", e))})
在上面的代码块中,如果我们对代码块的第一次尝试失败,我们将进行捕获。如果失败,我们将把变量更新为真。这将触发下一个块,我们将使用一年前的值。
最终,添加了完整 trycatch 功能的最终函数将如下所示:
getAcsIncome = function(names, year, KEY){
## setting up API call key
census_api_key(key = API_KEY, install = FALSE, overwrite = TRUE)
## setting up blank array to store state names
stateNames = NULL
# when all states are required
if(names %in% c('all', 'ALL')){
stateNames = state.abb
}
# when specific state or states are mentioned in names
else if(names %in% c(state.abb)){
stateNames = names
}
# in any other cases
else{
print("Provide a value in stateNames variable. Available options: all/ALL/any of the 50 states (abb.)")
}
# starting with variable: an.error.occured with value of FALSE
an.error.occured <- FALSE
tryCatch({
# calling api to get data
data = tidycensus::get_acs(state = stateNames, year = year, geography = 'tract', variables = 'B19013_001', geometry = FALSE, survey = 'acs5', show_call = TRUE)
}, error = function(e) {
# updating the variable
an.error.occured <<- TRUE
# printing out error message to be stored in log
message(paste0("Year tried: ", year, "\n", e))})
# try for 2 year older data
if(an.error.occured == TRUE){
year = year - 2
# calling api to get data
data = tidycensus::get_acs(state = stateNames, year = year, geography = 'tract', variables = 'B19013_001', geometry = FALSE, survey = 'acs5', show_call = TRUE)
}
## returning resulting data
return(data)
}head(getAcsIncome(names = 'IL', year = 2020, KEY = API_KEY))## To install your API key for use in future sessions, run this function with `install = TRUE`.## Getting data from the 2016-2020 5-year ACS## Census API call: [https://api.census.gov/data/2020/acs/acs5?get=B19013_001E%2CB19013_001M%2CNAME&for=tract%3A%2A&in=state%3A17](https://api.census.gov/data/2020/acs/acs5?get=B19013_001E%2CB19013_001M%2CNAME&for=tract%3A%2A&in=state%3A17)## Year tried: 2020
## Error: Your API call has errors. The API message returned is <html><head><title>Error report</title></head><body><h1>HTTP Status 404 - /data/2020/acs/acs5</h1></body></html>.## Getting data from the 2014-2018 5-year ACS## Census API call: [https://api.census.gov/data/2018/acs/acs5?get=B19013_001E%2CB19013_001M%2CNAME&for=tract%3A%2A&in=state%3A17](https://api.census.gov/data/2018/acs/acs5?get=B19013_001E%2CB19013_001M%2CNAME&for=tract%3A%2A&in=state%3A17)## # A tibble: 6 x 5
## GEOID NAME variable estimate moe
## <chr> <chr> <chr> <dbl> <dbl>
## 1 17001000100 Census Tract 1, Adams County, Illinois B19013_0~ 44613 6384
## 2 17001000201 Census Tract 2.01, Adams County, Illinois B19013_0~ 44878 4356
## 3 17001000202 Census Tract 2.02, Adams County, Illinois B19013_0~ 46964 10202
## 4 17001000400 Census Tract 4, Adams County, Illinois B19013_0~ 33750 7386
## 5 17001000500 Census Tract 5, Adams County, Illinois B19013_0~ 38526 4846
## 6 17001000600 Census Tract 6, Adams County, Illinois B19013_0~ 51491 10117
在打印的消息中,下面的消息块显示 trycatch 函数中的代码块失败了。然后回落到 2 年前的数据。原因是 ACS5 中可用的最新调查数据是 2018 年的。
## Year tried: 2020
## Error: Your API call has errors. The API message returned is <html><head><title>Error report</title></head><body><h1>HTTP Status 404 - /data/2020/acs/acs5</h1></body></html>.
在我们继续添加下一个参数块以克服最后一个限制之前,我们需要再做一个更改。因为我们的最终目标是从服务器上运行这个函数,所以让我们将年份输入嵌入到函数中。
我们将在函数中引入一个名为 year 的变量,默认值为(当前年份— 2) value,然后在秋季我们将该变量更新为(当前年份— 3)。这将确保每当我们运行代码时,它将要求 2 年的旧数据,即使 2 年的数据不可用,它也将调用 3 年的旧数据。
下面是将要添加的两行代码:
# creating year variable with default value
year = as.numeric(substr(Sys.Date(), start = 1, stop = 4)) - 2
#updating year variable
year = as.numeric(substr(Sys.Date(), start = 1, stop = 4)) - 3
您可以看到添加了当年功能的最终代码块。
添加数据和时间列
这是本教程最简单的部分。基本上,我们将把 Sys.time()作为一个附加列添加到已经获取的数据中。
下面是最后一段代码:
getAcsIncome = function(names, KEY){
## setting up API call key
census_api_key(key = API_KEY, install = FALSE, overwrite = TRUE)
## setting up blank array to store state names
stateNames = NULL
# when all states are required
if(names %in% c('all', 'ALL')){
stateNames = state.abb
}
# when specific state or states are mentioned in names
else if(names %in% c(state.abb)){
stateNames = names
}
# in any other cases
else{
print("Provide a value in stateNames variable. Available options: all/ALL/any of the 50 states (abb.)")
}
# starting with variable: an.error.occured with value of FALSE
an.error.occured <- FALSE
tryCatch({
# creating year variable with default value
year = as.numeric(substr(Sys.Date(), start = 1, stop = 4)) - 2
# calling api to get data
data = tidycensus::get_acs(state = stateNames, year = year, geography = 'tract', variables = 'B19013_001', geometry = FALSE, survey = 'acs5', show_call = TRUE)
}, error = function(e) {
# updating the variable
an.error.occured <<- TRUE
# printing out error message to be stored in log
message(paste0("Year tried: ", year, "\n", e))})
# try for 2 year older data
if(an.error.occured == TRUE){
#updating year variable
year = as.numeric(substr(Sys.Date(), start = 1, stop = 4)) - 3
# calling api to get data
data = tidycensus::get_acs(state = stateNames, year = year, geography = 'tract', variables = 'B19013_001', geometry = FALSE, survey = 'acs5', show_call = TRUE)
}
# adding update data to a column
data$UPDATE_DATE = Sys.time()
## returning resulting data
return(data)
}summary(getAcsIncome(names = 'all', KEY = API_KEY))## GEOID NAME variable estimate
## Length:72877 Length:72877 Length:72877 Min. : 2499
## Class :character Class :character Class :character 1st Qu.: 42353
## Mode :character Mode :character Mode :character Median : 57099
## Mean : 64289
## 3rd Qu.: 78323
## Max. :250001
## NA's :1013
## moe UPDATE_DATE
## Min. : 550 Min. :2020-07-10 09:03:57
## 1st Qu.: 6051 1st Qu.:2020-07-10 09:03:57
## Median : 8711 Median :2020-07-10 09:03:57
## Mean : 10212 Mean :2020-07-10 09:03:57
## 3rd Qu.: 12521 3rd Qu.:2020-07-10 09:03:57
## Max. :126054 Max. :2020-07-10 09:03:57
## NA's :1092
下一步是什么?
现在,要在服务器中设置这个脚本自动运行,还有两件事情要做:
- 正在添加日志文件。任何时候,如果您想让脚本在服务器上运行,您都应该考虑为它添加日志记录功能。万一脚本失败,调试起来会非常方便。
- 自动化这个脚本。在 Windows 中,一个简单的方法是使用 Windows 的任务计划程序。你可以看看我的另一个教程''自动化你的重复性报告!'了解如何使用 windows 任务计划程序自动运行脚本的详细信息。
美国人口普查局是美国人口数据的重要来源。有各种各样有趣的数据可用,比如失业数据、种族相关数据、教育相关数据等等。你需要做的就是浏览我之前链接的变量的文档,这里又是。希望本教程能让您的人口普查局数据探索之旅更容易、更有用,以防您希望持续使用这些数据。
感谢阅读!
阅读完本文后,您应该对如何利用 R 的能力从外部数据源获取数据有所了解,R 已经有了一些方便的包!
不确定接下来要读什么?我为你挑选了另一篇文章:
https://towardsdatascience.com/whos-missing-d717ee657674
阿拉法特·侯赛因
使用 R 从能源部的 EIA API 中提取能源数据

注意:我写了一个 Python 版本的这篇文章, 你可以在这里访问那篇文章。 每篇文章的开头都一样。它将带您了解如何注册 EIA API 并获得 API 密钥。
能源信息署负责美国能源部的统计和数据。美国有大量与能源相关的数据和信息,包括可再生能源、核能、原油、汽油、喷气燃料、柴油、电力和天然气。
然而,在 EIA 的网站上导航和查找数据可能会很棘手。为了帮助解决这个问题,EIA 创建了一个易于访问的 API。你可以在www.eia.com/opendata.找到 API。根据不同类型的数据,EIA 每周、每月或每年发布新数据。该 API 对于在每周和每月数据发布时刷新它们非常有用。
API 访问— API 密钥
首先,您需要从 EIA 获得一个 API 密钥。转到www.eia.com/opendata并点击“立即注册”按钮。

这将带你到一个注册表格,看起来像这样。输入您的信息,然后单击“注册”。您将收到一封包含新 API 密钥的电子邮件。它可能会被发送到您的垃圾文件夹,所以如果您在几分钟内没有收到 API 密钥,请务必检查那里。

查找数据集
现在,您可以通过 API 搜索想要获取的数据集。您可以通过点击 API 主页上的“API 查询浏览”来浏览数据集。

在下一页,您可以点击“儿童类别”下的链接来搜索您需要的数据。

另一种搜索数据的方法是点击页面顶部的“来源和用途”并浏览网站。当您遇到想要的数据集时,EIA 通常会在 API 中发布一个指向该数据集的链接。
获取系列 ID
每个数据系列都有一个特定的系列 ID。您将使用这个系列 ID 和 API 键从数据库中提取数据集。例如,下图显示了 PADD 3(美国墨西哥湾沿岸)原油消费数据的系列 ID。该代码允许您一次获取多个系列。跟踪所有您想要使用的系列 id。

趣闻: PADD 代表石油管理局防区。这些区域是在 1942 年第二次世界大战期间建立的,用来组织汽油和柴油等燃料的分配。今天,PADDs 仍然用于按地区组织数据。

使用 R 拉入数据
现在我们有了 API 键和系列 id,我们可以编写 R 代码来访问数据。首先,导入必要的库。我们需要使用 httr 和 jsonlite 库。
#Import libraries
install.packages(c("httr", "jsonlite"))library(httr)
library(jsonlite)
现在,将您的 API 密钥粘贴到代码中。然后粘贴您想要提取的系列 id。用逗号分隔您的系列 id。您还可以使用“startdate”和“enddate”变量选择想要提取的日期范围。
# API Key from EIA
key <- 'PASTE YOUR API KEY HERE'# Paste your Series IDs in the list, separated by commas
padd_key <- list('PET.MCRRIP12.M','PET.MCRRIP22.M',
'PET.MCRRIP32.M','PET.MCRRIP42.M',
'PET.MCRRIP52.M')# Choose the start and end dates
startdate <- "2010-01-01" #YYYY-MM-DD
enddate <- "2020-01-01" #YYYY-MM-DD
最后,调用 API 以 json 格式提取数据。您的 url 链接可能会根据您正在获取的数据集而变化。要检查所需的确切 url,请查看 API 查询浏览器中的“要使用的 API 调用”链接。

API 调用使用链接是您需要用来拉入 API 的链接。它可能与下面提供的代码不同。如有必要,用适当的 API 调用替换“url”变量中的链接以使用 link。
代码将遍历您选择的每个 Series ID,提取 json 格式的数据,然后将其转换成 R 数据帧。现在,您拥有了一个 R 数据框,可以根据您的需要轻松操作、分析和可视化。
j = 0
for (i in padd_key) {url <- paste('[http://api.eia.gov/series/api_key=',key,'&series_id=',i,sep=](http://api.eia.gov/series/?api_key=',key,'&series_id=',i,sep=)"") # Make the call to the EIA's API
res <- GET(url)
json_data <- fromJSON(rawToChar(res$content))
data <- data.frame(json_data$series$data)
data$Year <- substr(data$X1,1,4)
data$Month <- substr(data$X1,5,6)
data$Day <- 1# Create date format
data$Date <- as.Date(paste(data$Year, data$Month, data$Day, sep='')) # Rename the column to its given name from the EIA
colnames(data)[2] <- json_data$series$name # Drop the unnecessary date columns
data <- data[-c(1,3,4,5)]
if (j == 0){
data_final <- data
}
else{
data_final <- merge(data_final,data,by="Date")
}
j = j + 1
}# Splice the data between the start and end dates
data_final <- subset(data_final, Date >= startdate & Date <= enddate)
最终的数据框将如下图所示。

EIA API 拉取中的数据帧
用拨浪鼓来帮助你学习 R
初学者指南
来自@ Giphy.com 的 thebossbaby
不,不是那种拨浪鼓——尽管有时你似乎需要一个。
“rattle”包为 R 功能提供了一个 GUI 界面——比 RStudio 提供的更多。下面是从 r 内部运行 rattle()时得到的结果。

作者的拨浪鼓截图
拨浪鼓对很多事情都有用。如果你想运行一个快速和肮脏的模型?太棒了。想看看你的数据是什么样的吗?这对那个也有好处。但是对于尝试学习 R 的 R 程序员新手来说,最有用的方面可能是“Log”选项卡。每次你告诉 Rattle 做某件事的时候,log 标签都会记录它用来做这件事的代码。有了这个特性,你可以使用 Rattle 在 R 中做一些事情,然后查看 Log 选项卡,看看它是如何做到的,这样你就可以学习做同样的事情。
装置
我假设此时您已经安装了 R——也许还有 RStudio。这个你不需要后者,但是不疼。要安装拨浪鼓,您需要拨浪鼓包。这将安装大多数依赖项。现在,您需要单独安装 RGtk2 软件包。然后加载库并运行 Rattle。
install.packages("rattle")
install.packages("RGtk2")
library(rattle)
rattle()
第一次运行 Rattle,它会问你要不要安装 GTK+(是的,再来一遍)。选择确定。当您尝试使用 Rattle 中的某些功能时,会出现类似的弹出窗口,告诉您需要安装相关的软件包才能继续。
(我听说有些人在 MAC 上使用 GTK+有问题。我用的是 PC,所以从来没出过问题,但是 YMMV。)
使用拨浪鼓
在你做任何事情之前,你需要载入一些数据。Rattle 带有几个内置的数据集。对于本文,我们将使用天气数据集。这是 Rattle 的默认数据集,如果你不告诉它加载其他东西,它就会加载这个数据集。要使用它,只需点击“执行”按钮(确保您仍在数据选项卡上)。Rattle 会注意到你没有指定数据集,所以它会问你是否要加载天气数据集。说“是”。
数据集的其他选项包括从文件加载、从 ODBC 数据库加载、从 R 加载您已经在处理的数据集(我最喜欢的选项)等。
顺便说一下,在 Rattle 中选择执行按钮是您需要习惯的事情。每次你做某件事时,你需要按下那个按钮来告诉 Rattle实际上去做。当你不这样做的时候,奇怪的事情就会发生。
加载数据集后,您将看到数据集中存在的变量的摘要,以及一些设置选项。这是天气数据集中变量的部分截图。

作者截图
请注意,对于每个变量,您将看到 Rattle 已经推断出数据类型、变量的用途(输入、目标等。),以及变量的唯一值的数量和缺失值的数量的摘要。
数据类型字段经常错误地将类别变量归类为数字。这是因为许多数据集使用数字来表示类别。例如,一个收入变量可能有 10 个类别,等级为 1-10。如果您想将类型从数字更改为类别(或者进行其他更改),您可以在 Transform 选项卡上完成。如果我有足够的兴趣,我会在另一篇文章中介绍。
目标变量的识别也可能是偶然的。浏览数据,确保所有东西都被正确分类。如果不是,请选择正确的单选按钮。如果您想同时更改一组变量,请选择它们(使用 ctrl-select 或 shift-select),然后单击代表输入或忽略的红灯或绿灯。
除此之外,您可以决定是否要对数据进行分区(训练/验证/测试),并选择您想要使用的种子。我喜欢默认的 42。毕竟,它是对生命、宇宙和一切的终极问题的答案。(如果你不知道我在说什么,不要慌!去读读道格拉斯·亚当斯的《银河系漫游指南》。记住,永远带着你的毛巾。)
让我们继续对数据进行分区。使用默认的 70/15/15。然后单击执行。
在我们深入讨论日志选项卡之前,我应该指出格式可能有点复杂。Rattle 做的一些事情是为了 Rattle 自己的目的。我不知道它为什么会做一些事情。关键是看 Rattle 用的代码,试着理解它是做什么的,根据自己的目的修改它。R help()命令将是您的好朋友。
现在我们已经读入并准备好了数据集,让我们看看 Log 选项卡,看看 Rattle 为我们做了什么。往下跳一点,你会看到:
crv$seed <- 42
您将在整个日志中看到“crv\(”和“crs\)”。Rattle 创建一个环境变量,其中包含您将使用的所有单个变量。一般来说,如果你想复制代码,如果你把那部分去掉,就不会那么混乱。我会的,从现在开始。
有两个与种子相关的命令。第一个创建一个变量作为种子,而第二个(屏幕下方一点)实际上是设置种子。这样,如果您想要更改种子值,您只需要在一个地方修改代码。
seed <- 42
set.seed(seed)
在这两个命令之间,您将看到 Rattle 是如何加载数据集的。
fname <- system.file("csv", "weather.csv", package = "rattle")
dataset <- read.csv(fname, encoding = "UTF-8")
如您所见,代码对于我们的目的来说过于复杂。在 R 中加载嵌入的数据集时,使用 data()函数更容易:
data("weather")
这将创建名为“天气”的数据集。Rattle 方法是一个很好的内部过程,允许它使用变量名“dataset”来引用它正在处理的任何数据集。(这是和设置种子类似的概念,就在上面。)
接下来,我们可以看到 Rattle 用来对数据集进行分区的代码。
nobs <- nrow(dataset)
train <- sample(nobs, 0.7 * nobs) # the training set
nobs %>%
seq_len() %>%
setdiff(train) %>%
sample(0.15 * nobs) ->
validate # the validation set
nobs %>%
seq_len() %>%
setdiff(train) %>%
setdiff(validate) ->
test # the testing set
同样,这有点不合常规。正常方法会将新变量放在第一位,如:
validate <- nobs %>%
seq_len() %>%
setdiff(train) %>%
sample(0.15 * nobs)
顺便说一下,如果您还不理解这段代码,那么%>%是一个“管道”操作符(可以在 magrittr 和 dplyr 包中找到),它使用管道前面的部分作为管道后面的函数的第一个参数。所以这相当于:
validate <- sample(setdiff(seq_len(nobs), train), 0.15* nobs)
使用管道可以使你的代码更容易阅读。R 中有很多括号需要正确放置。然而,这并不意味着是关于管道的教程。
这样做的目的是创建一个随机的数字列表,其中包含从 1 到 nobs (seq_len(nobs))的 15%的整数,而不包含已经是 train 的一部分的数字(setdiff)。然后,这可以用作数据帧的索引,如下所示:
weather[train,]
weather[validate,]
weather[test,]
“测试”索引是以同样的方式创建的,但是这一次,setdiff()使用了两次:一次用于删除训练索引,另一次用于删除验证索引。
此时日志文件的大部分内容实际上只供 Rattle 内部使用——定义输入变量和目标变量,以及数字变量和类别变量。
建模
让我们尝试一些建模。首先,我们要做一个决策树。转到模型选项卡,选择树,然后点击执行。Rattle 将创建一个具有标准参数集的决策树,根据数据集创建一组规则来预测明天是否会下雨。

Rattle 中的决策树模型(作者截图)
这里有很多信息,但是我将不得不在另一篇文章中讨论。决策树的关键部分就在中心上方。这些是决策树创建的规则。
- 如果下午 3 点的云量小于 6.5,那么就不会下雨。
- 否则,它仍然可能不会下雨,但检查气压。
- 如果下午 3 点气压至少 1016 毫巴,就不会下雨。
- 否则很可能会下雨,不过下午 3 点查一下风向
- 如果风向是 ESE,SSE,W,WNW,或者 WSW,那么就不会下雨。
- 否则,会下雨。
决策树的主要优点是它们创建的规则易于理解。
要查看这是如何编码的,我们可以转到 Log 选项卡并滚动到底部。这里我们看到下面的代码(为了清楚起见,我将决策树变量的名称从“rpart”改为“tree”):
library(rpart) # provides the code to create the decision treetree <- rpart(RainTomorrow ~ .,
data = dataset[train, c(input, target)],
method = "class",
parms = list(split = "information"),
control = rpart.control(usesurrogate=0, maxsurrogate= 0),
model = TRUE)print(tree)
printcp(tree)
现在我们已经看到了代码,我们可以回到 R(或 RStudio)并找出所有这些参数的含义。
?rpart
您还可以使用 Rattle Model 选项卡上的参数来查看它如何影响代码。例如,在我为四个默认数字参数中的每一个选择了不同的值之后,Rattle 会生成以下代码:
tree <- rpart(RainTomorrow ~ .,
data=dataset[train, c(input, target)],
method="class",
parms=list(split="information"),
control=rpart.control(minsplit=15,
minbucket=6,
maxdepth=20,
cp=0.001000,
usesurrogate=0,
maxsurrogate=0),
model=TRUE)
我们还可以通过单击右边的两个按钮来获得决策树的规则以及树的图形表示。规则将显示在异响屏幕中(比之前显示的格式更好)。但是,Rattle 生成的所有图都将进入 RStudio Plots 窗口(如果使用 RStudio ),否则将进入 R 图形设备。
回到 Log 选项卡,我们可以找到用于创建绘图的代码。
fancyRpartPlot(tree, main = "Decision Tree weather.csv $ Raintomorrow")

Rattle 创建的决策树(作者截图)
现在我们知道了如何创建一个图形化的决策树,包括图形标题。底部的文本是 fancyRpartPlot 创建的默认副标题,但这也是可编辑的。查看 fancyRpartPlot()函数的文档以了解一些选项。请随意使用这些选项,看看它们能做些什么。
模型评估
最后,我们将评估我们刚刚创建的模型。转到评估选项卡:

Rattle 评估选项卡(作者截图)
首先,让我们为模型生成一个误差矩阵。已经选择了错误矩阵,所以点击执行。

天气决策树模型的误差矩阵(作者截图)
这个模型预测“无雨”相当准确,但“下雨”就不那么准确了。这并不奇怪,因为在这个模型中下雨是非常罕见的。如果我们更关心预测什么时候下雨而不是什么时候不下雨,那么我们可以调整模型上的损失矩阵参数,以有利于真正的正面而不是真正的负面。但那是以后的事了。
现在,回到 Log 选项卡,看看误差矩阵是如何创建的。
pr <- predict(tree, newdata = dataset[validate, c(input, target)],
type = "class")# Generate error matrix with counts
rattle::errorMatrix(dataset[validate, c(input, target)$RainTomorrow,
pr, count = TRUE))# Generate error matrix with proportions
(per <- rattle::errorMatrix(dataset[validate, c(input,
target)$RainTomorrow, pr))#Calculate and display errors
cat(100 - sum(diag(per), na.rm = TRUE)
cat(mean(per[,"Error"], na.rm = TRUE)
让我们画一个 ROC 曲线。在 Evaluate 选项卡上,选择 ROC 单选按钮,然后点击 Execute。同样,最终的情节不会显示在 Rattle 中,所以请返回 R 或 RStudio 查看。AUC(曲线下面积)值允许我们给模型分配一个“等级”。一个好的经验法则是使用 AUC 值,就像你在学校里用分数一样。超过 90%是 A,80–90%是 B,等等。

天气决策树的 ROC 曲线(作者截图)
并再次返回到 Log 选项卡,查看生成该图的代码。亲爱的读者,我将把这个留给你去解读。记住,help()命令是你的朋友。
# ROC Curve: requires the ROCR package.
library(ROCR)# ROC Curve: requires the ggplot2 package.
library(ggplot2, quietly=TRUE)# Generate an ROC Curve for the rpart model on weather.csv [validate].
pr <- predict(tree, newdata=dataset[validate,
c(input, target)])[,2]# Remove observations with missing target.
no.miss <- na.omit(dataset[validate, c(input,
target)]$RainTomorrow)
miss.list <- attr(no.miss, "na.action")
attributes(no.miss) <- NULLif (length(miss.list))
{
pred <- prediction(pr[-miss.list], no.miss)
} else
{
pred <- prediction(pr, no.miss)
}pe <- performance(pred, "tpr", "fpr")
au <- performance(pred, "auc")[@y](http://twitter.com/y).values[[1]]
pd <- data.frame(fpr=unlist(pe@x.values), tpr=unlist(pe@y.values))
p <- ggplot(pd, aes(x=fpr, y=tpr))
p <- p + geom_line(colour="red")
p <- p + xlab("False Positive Rate") + ylab("True Positive Rate")
p <- p + ggtitle("ROC Curve Decision Tree weather.csv [validate]
RainTomorrow")
p <- p + theme(plot.title=element_text(size=10))
p <- p + geom_line(data=data.frame(), aes(x=c(0,1), y=c(0,1)),
colour="grey")
p <- p + annotate("text", x=0.50, y=0.00, hjust=0, vjust=0, size=5,
label=paste("AUC =", round(au, 2)))
print(p)# Calculate the area under the curve for the plot.
# Remove observations with missing target.no.miss <- na.omit(dataset[validate, c(input,
target)]$RainTomorrow)
miss.list <- attr(no.miss, "na.action")
attributes(no.miss) <- NULLif (length(miss.list))
{
pred <- prediction(pr[-miss.list], no.miss)
} else
{
pred <- prediction(pr, no.miss)
}
performance(pred, "auc")
结论
正如我前面提到的,Rattle 日志中的许多代码更多地是为内部使用而设计的,而不是供用户复制供自己使用。因此,如果没有其他原因,您可能会希望修改代码以提高可读性。尽管如此,通过充分使用 R help,您应该能够使用 Rattle 的日志来帮助您编写自己的代码。
进一步阅读
拨浪鼓
https://cran.r-project.org/web/packages/rattle/rattle.pdf
(上面这本书的链接是附属链接。)
管道
https://www . data camp . com/community/tutorials/pipe-r-tutorial
在你的下一个 NLP 项目中使用拥抱脸的数据集库
拥抱脸的数据集库快速使用指南!

数据科学就是关于数据的。网络上有各种来源可以为您的数据分析或机器学习项目获取数据。最受欢迎的来源之一是 Kaggle,我相信我们每个人在数据之旅中都用过它。
最近,我偶然发现了一个为我的 NLP 项目获取数据的新来源,我很乐意谈论它。这是拥抱脸的数据集库,一个快速高效的库,可以轻松地共享和加载数据集和评估指标。因此,如果你正在从事自然语言处理(NLP)工作,并且想为你的下一个项目获取数据,除了拥抱脸,别无他求。😍
动机: 拥抱脸提供的数据集格式与我们的熊猫数据框架不同,因此最初使用拥抱脸数据集可能会令人望而生畏。😱拥抱脸有很好的文档,但是信息量太大了。我刚刚写了一些我们在处理数据集时的基本步骤。😄本文绝非详尽无遗,如果您想对数据集做更多的事情,我强烈建议您查看他们的文档。
让我们先了解一下 Hugging Face 和 datasets 库,然后举一个例子来了解如何使用这个库中的数据集。😎
抱紧脸 🤗是自然语言处理(NLP)技术的开源提供商。你可以使用拥抱脸最先进的模型(在变形金刚库下)来建立和训练你自己的模型。您可以使用拥抱脸数据集库来共享和加载数据集。您甚至可以使用这个库进行评估。
数据集库
根据拥抱脸网站的说法,数据集库目前拥有超过 100 个公共数据集。😳这些数据集不仅有英语的,还有其他语言和方言的。👌它支持大多数数据集的一行数据加载器,这使得数据加载成为一项轻松的任务。🏄🏻根据网站上提供的信息,除了易于访问数据集之外,该图书馆还有以下有趣的特点:
- 在大型数据集上茁壮成长:数据集自然将用户从 RAM 限制中解放出来,所有数据集都使用高效的零序列化成本后端(Apache Arrow)进行内存映射。
- 智能缓存:永远不要等待你的数据处理几次。
- 轻量级和快速的透明和 pythonic API(多处理/缓存/内存映射)。
- 内置与 NumPy、pandas、PyTorch、Tensorflow 2 和 JAX 的互操作性。
哇!好处还挺多的。👏
在本文中,我将展示我们通常在数据科学或分析任务中所做的一些步骤,以理解我们的数据或将我们的数据转换成所需的格式。所以,让我们快速进入这个库,编写一些简单的 Python 代码。🐍请注意,本文只涉及数据集,不涉及指标。
数据集版本: 1.7.0
使用 pip 安装
!pip install datasets
进口
**from** datasets **import** list_datasets, load_dataset
**from** pprint **import** pprint
从数据集库中,我们可以导入 list_datasets 来查看该库中可用的数据集列表。 pprint 模块提供了“漂亮打印”的功能。你可以在这里了解这个模块的更多信息。👈🏼
截至 2021 年 6 月 7 日,数据集库拥有 928 个数据集。🙌我们可以使用以下代码查看可用数据集的列表:
datasets = **list_datasets()**
**print**("Number of datasets in the Datasets library: ", **len**(datasets), "\n\n")#list of datasets in pretty-print format
**pprint**(datasets, **compact=True**)

数据集列表的片段
如果您想在下载数据集之前就知道它的属性,该怎么办?我们可以用一行代码做到这一点。☝️只需将索引设置为数据集的名称,就可以了!
#dataset attributes
squad = **list_datasets**(**with_details=True**)[datasets.index('squad')]#calling the python dataclass **pprint**(squad.**__dict__**)

数据集属性
挺有意思的!😬
加载数据集
squad_dataset = **load_dataset**('squad')
引擎盖下发生了什么?🤔datasets . load _ dataset()执行了以下操作:
- 从 Hugging Face GitHub repo 或 AWS bucket 下载并导入到库中的 SQuAD python 处理脚本(如果它尚未存储在库中)。
- 运行小队脚本下载数据集。在缓存箭头表中处理和缓存小队。
- 根据用户要求的拆分返回数据集。默认情况下,它返回整个数据集。
让我们来理解一下我们得到的数据集。
**print**(squad_dataset)

班数据集有两个部分——训练和验证。features 对象包含有关列的信息—列名和数据类型。我们还可以看到每次分割的行数(num_rows)。相当翔实!
我们还可以在加载数据集时指定拆分。
squad_train = **load_dataset**('squad', **split=**'train')
squad_valid = **load_dataset**('squad', **split=**'validation')
这将在 squad_train 中保存训练集,在 squad_valid 中保存验证集。
然而,您将意识到加载一些数据集会抛出一个错误,并且在检查错误时,您会意识到您需要第二个参数 config。
这里有一个例子:
amazon_us_reviews = **load_dataset**('amazon_us_reviews')
错误消息:

加载数据集时引发错误
一些数据集包括几个配置,这些配置定义了需要选择的数据集的子部分。
解决方案:
amazon_us_reviews = **load_dataset**('amazon_us_reviews', 'Watches_v1_00')
这将加载带有配置观察器的 amazon_us_reviews 数据集。
因此,如果加载任何数据集抛出一个错误,只需遵循追溯,因为拥抱脸已经给出了关于错误的好信息。👍
让我们继续我们的数据集。🏃🏻
我们看到了数据集信息中的行数。我们甚至可以使用标准的 len 函数来得到它。
**print**("Length of training set: ", **len**(squad_train))
训练集长度:87599
检查数据集
要查看数据集的示例:
**print**("First example from the dataset: \n")
**pprint**(squad_train[0])

小队训练数据集第一个例子
想用几个例子得到切片,代码和我们用熊猫数据框的一样。
**print**("Two examples from the dataset using slice operation: \n")
**pprint**(squad_train[14:16])

数据集中的示例切片
想要查看列中的值?用列名索引数据集。这是“问题”专栏的一个片段。
**print**("A column slice from the dataset: \n")
**pprint**(squad_train['question'][:5])

小队的纵队
您可以看到,行的切片给出了一个字典,而列的切片给出了一个列表。 getitem 方法根据查询的类型返回不同的格式。例如,像 dataset[0]这样的项将返回元素的字典,像 dataset[2:5]这样的片将返回元素列表的字典,而像 dataset['question']这样的列或列的片将返回元素列表。这看起来令人惊讶,但拥抱脸做到了这一点,因为它实际上更容易用于数据处理,而不是为每个视图返回相同的格式。
请看这个有趣的例子:
**print**(squad_train[‘question’][0])
**print**(squad_train[0][‘question’])
输出:
据说 1858 年圣母玛利亚在法国卢尔德向谁显现?据说 1858 年在法国卢尔德出现的圣母玛利亚是给谁看的?
两者都返回相同的输出。我们来验证一下!🕵
**print**(squad_train['question'][0] == squad_train[0]['question'])
输出将为真。不错!我们在使用熊猫数据框时犯的一个常见错误在这里并不算是错误。
注: 数据集由一个或几个 Apache 箭头表支持,这些表是键入的,允许快速检索和访问。您可以加载任意大小的数据集,而不必担心 RAM 限制,因为数据集不占用 RAM 中的空间,并且可以在需要时直接从驱动器中读取。
让我们进一步检查数据集。
**print**("Features: ")
**pprint**(squad_train.features)**print**("Column names: ", squad_train.**column_names**)


数据集功能和列名
**print**("Number of rows: ", squad_train.**num_rows**)
**print**("Number of columns: ", squad_train.**num_columns**)
**print**("Shape: ", squad_train.**shape**)
输出:
行数:87599
列数:5
形状:(87599,5)
注意,您也可以使用 len 函数获得行数。
添加/删除新列
添加一个名为“new_column”的列,其中包含条目“foo”。
new_column = ["foo"] * **len**(squad_train)
squad_train = squad_train**.add_column**("new_column", new_column)
**print**(squad_train)

添加到数据集中的新列
现在让我们删除该列。
squad_train = squad_train.**remove_columns**("new_column")
重命名列
squad_train = squad_train.**rename_column**("title", "heading")
**print**(squad_train)

标题列重命名为标题
修改/更新数据集
要修改或更新数据集,我们可以使用 dataset.map. map()是一种受 tf.data.Dataset map 方法启发的强大方法。我们可以将该函数应用于一个示例,甚至一批示例,甚至生成新的行或列。
逐个示例地修改:
updated_squad_train = squad_train.**map**(**lambda** example: {'question': 'Question: ' + example['question']})
**pprint**(updated_squad_train['question'][:5])
输出:

使用映射将“问题”追加到列的每一行
让我们使用现有列添加一个新列,并删除旧列。
updated_squad_train = squad_train.**map**(**lambda** example: {'new_heading': "Context: " + example['heading']}, **remove_columns**=['heading'])
**pprint**(updated_squad_train.column_names)
**pprint**(updated_squad_train['new_heading'][:5])
输出:

使用“标题”列中的内容(和前缀)添加了“新标题”列,并从数据集中删除了“标题”列
您可以使用地图对数据集执行多项操作。一定要根据你的需求尝试新事物。🙃
除此之外,您还可以批量处理数据。
展示熊猫数据框等示例
我们总是喜欢将我们的数据集视为一个格式良好的表格,就像我们看到一个熊猫数据框一样。出于显示目的,我们可以将数据集转换为相同的格式。
**import** random
**import** pandas **as** pd
**from** IPython.display **import** display, HTML**def** display_random_examples(dataset=squad_train, num_examples=5):
**assert** num_examples < len(dataset)
random_picks = []
**for** i **in** **range**(num_examples):
random_pick = **random.randint**(0,**len**(dataset)-1)
random_picks.**append**(random_pick)
df = **pd.DataFrame**(dataset[random_picks])
**display**(**HTML**(df.**to_html()**))
display_random_examples(squad_train, 3)
输出是一个格式良好的表格。👌

我们数据集的格式良好的表格
本文到此为止。从这里,您可以根据您的项目需求预处理您的数据,并构建您的模型或创建漂亮的可视化。不可能在一篇文章中涵盖所有内容。但是,通过阅读本文,您可以了解如何使用 datasets 库中可用的方法。如果你需要对数据集做更多的事情,请查看文档。还有很多很多方法,如排序,洗牌,分片,选择,过滤,连接数据集等。您还可以将数据集格式化为 PyTorch、Tensorflow、Numpy 和 Pandas。如果你想分享你自己的数据集,你也可以这样做。在这里阅读!
如果你想看代码,请参考这个链接到我的 Github repo。
参考文献:
感谢大家阅读这篇文章。请分享您宝贵的反馈或建议。快乐阅读!📗 🖌
使用这些原则来设计出色的仪表板
与 Dash 和 Plotly 一起使用的适用提示

法伊祖尔·拉赫曼在 Unsplash 上拍摄的照片
从事分析工作
作为一名从事数据科学的软件产品分析师,同时也是一名热爱数据的人,我经常创建仪表板来传达项目涉众感兴趣的关键性能指标和度量的信息。最近,在工作中,我一直在挖掘产品使用数据,并使用 Elasticsearch 和 Kibana 创建仪表板。那么,是什么使仪表板有效,为什么每个人都想使用它们?
为什么要构建仪表板?
仪表板最终是一种交流工具,并与技术交流领域的其他可交付成果共享许多设计原则。与基于快照的静态报告相比,仪表板通常会自动刷新,这使用户能够更轻松、更快速地获得一目了然的洞察力。一个设计良好的仪表板将让用户与它互动,并使用它进行数据驱动的决策。如果用户经常质疑仪表板上的度量和可视化背后的含义,本文中的原则将有助于缓解这种情况。
我最喜欢的免费仪表盘工具之一是 Dash by Plotly 。它允许您使用纯 Python 创建强大的仪表盘和 web 应用程序。除了这些原则之外,我还将介绍如何在使用 Dash 创建仪表板时应用这些原则。如果你对 Dash 完全陌生或者想学习它,可以在这里查看我的入门教程,或者我的网站pythondashboards.com
仪表板设计原则
如果你遵循这些原则,用户不仅会使用你的仪表板,还会理解它并从中获得价值。
- 了解你的受众,为启示而设计
- 澄清模糊性,设计易访问性
- 美观大方,设计美观
- 验证用户接受度和长寿设计
了解你的受众
对于一个有效的仪表板,考虑谁将使用,他们需要知道什么。这可以称为仪表板的启示。总的来说,了解你的听众是技术交流 101。例如,侧重于商业智能和关键绩效指标(KPI)的仪表板,受众往往是管理层。然而,根据您的具体使用情况,仪表板可能适用于任何人,甚至是客户!无论他们是谁,在为您的仪表板整合需求时,都要让您的用户参与进来,以确保它与他们相关。
定义您的仪表板
让你的仪表板有一个清晰的焦点,确保定义它的目的。仪表板的用处取决于它上面显示的信息。如果你的仪表板包含太多的信息,用户通常会觉得难以承受而停止使用它。
原型布局
最后,确保仪表板的布局适合使用它的观众。例如,受众是否需要深入细节的数据表?或者他们需要显示宏观视图的简单图表?最终,您希望以一种向用户传达故事的方式来组织仪表板。这里有两个你应该知道的常见设计模式:

仪表板布局创意(图片由作者提供)
破折号提示
为了在 Dash 中设计响应性布局,我使用 Dash 引导组件库。dash-Bootstrap-components库允许您应用受引导 CSS 框架影响的引导前端组件。
pip install dash-bootstrap-componentsimport dash_bootstrap_components as dbc
利用行和列组件的响应式网格系统允许您轻松地组织仪表板布局。引导网格包含 12 列和 5 个响应层。
下面是一些示例代码,您可以将它们添加到 dash 布局中来测试网格系统。
body = html.Div([html.H1("Bootstrap Grid System Example")
, html.H4("no_gutters = False")
, dbc.Row([
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3),
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3),
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3),
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3)
])
, html.H4("no_gutters = True")
, dbc.Row([
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3),
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3),
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3),
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3)
], no_gutters = True)
, html.H3("Examples of justify property")
, html.H4("start, center, end, between, around")
, dbc.Row([
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=4),
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=4),
],
justify="start")
, dbc.Row([
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3),
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3),
],
justify="center")
, dbc.Row([
dbc.Col(html.Div(dbc.Alert("One of three columns")), width=3)
, dbc.Col(html.Div(dbc.Alert("One of three columns")), width=3)
, dbc.Col(html.Div(dbc.Alert("One of three columns")), width=3)
],
justify="end")
, dbc.Row([
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3),
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3)
, dbc.Col(html.Div(dbc.Alert("One of three columns")), width=3)
],
justify="between")
, dbc.Row([
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=4),
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=4),
],
justify="around")
, html.H4("Container Example")
, dbc.Container([
dbc.Row([
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3),
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3),
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3),
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3)
]), html.H4("no_gutters = True")
, dbc.Row([
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3),
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3),
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3),
dbc.Col(html.Div(dbc.Alert("One of two columns")), width=3)
], no_gutters = True)
])
])
渲染时会是这个样子。请注意不同的选项如何影响组件在列和行中的行为。因为有 12 列,所以最大宽度是 12。

引导网格(图片由作者提供)
另外,我使用 dbc。Card 组件来包装我的仪表板部分,使它们看起来更好,并使最终用户更容易识别每个部分。查看我的完整教程,深入了解应用 Dash 引导组件。
澄清所有模糊之处
没有什么比打开仪表板,看到它充斥着复杂的术语和首字母缩写词,令人困惑的标题和不直观的颜色更糟糕的了。通过澄清模糊性,您可以让更多的用户访问仪表板。例如,公司或部门的新成员可能不知道所有用于度量和 KPI 的术语。永远不要假设用户会理解缩略语;拼出单词。作为沟通者,你有责任确保人们理解你输出的信息。
添加注释
在需要的地方添加注释。这里或那里的一些文本给用户一个关于统计数据的更深层次的解释会大有帮助。只要记住适当地利用空白,不要创建巨大的文本块。笔记应该简短而甜蜜。如果需要,链接到较长的文本,不要在仪表板中包含较长的文本。
使用清晰的标题
最后,确保你的可视化标题。我喜欢用问题的视觉答案来命名我的视觉化图像。例如,我可以将这种可视化称为“所选单元与平均 BM 单元(尺寸 1-4)相比如何?

普罗特利的散点图(图片由作者提供)
破折号提示
Dash 包含一个 Markdown 组件,允许您向仪表板添加 Markdown 部分。这非常适合添加简短注释或格式化文本,如项目符号列表。这是我的减价备忘单:
#use with dcc.Markdown component
markdown = """
# This is the markdown example!
# Markdown H1
## Markdown H2
### Markdown H3Regular paragraph text:
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.** Double asterisk makes text bold **
*Single asterisk makes texts italics*create `inline code` with a back tick around the code
Create links like `[``links``]``([http://commonmark.org/help)`](http://commonmark.org/help)`) but remove the back ticksYou can make lists too!
* item 1
* item 2
* item 3
1\. item 1
2\. item 2
3\. item 3Follow this list for all the features [links]([http://commonmark.org/help](http://commonmark.org/help))""" # end markdown
查看本教程,了解在 Dash 中使用降价组件的深入示例。
赏心悦目
为了让用户与你的仪表板互动,使用视觉元素是很重要的。注重仪表板的美观,使用直观的颜色和正确的视觉效果。做得好的话,你的用户会更容易使用仪表板并从中获得意义。
配色方案
使用直观的颜色可能意味着几件不同的事情。例如,通常以绿色显示积极的财务变化,以红色显示消极的财务变化,就像您在股票图表上看到的那样。

市场低迷的一天(图片由作者提供)
但除此之外,直观的颜色也意味着品牌和主题。如果仪表板面向客户,使用品牌颜色可能非常重要。确保您使用的颜色不会使数据更难理解。
图表选择
至于选择正确的可视化,有几个有用的流程图可以帮助你根据你想从数据中得到什么来决定使用哪种可视化。例如,如果您想要显示单个变量的分布,您可以选择一个列或线形直方图。或者,如果您想要显示三个变量之间的关系,气泡图可能是最合适的。
破折号提示
使用 Dash Bootstrap 组件网格可以帮助您在整个仪表板中留出适当的空白区域。如果组件看起来靠得太近,使用 html 在它们之间添加一个分隔符。Br() 组件。
将 Plotly 可视化添加到 Dash 应用程序超级容易,我推荐学习 Plotly Express 库 ,因为这是用 Dash 生成动态和交互式图表的最快方法。
pip install plotlyimport plotly.express as px
- 基础知识 :
[scatter](https://plotly.com/python/line-and-scatter/)、[line](https://plotly.com/python/line-charts/)、[area](https://plotly.com/python/filled-area-plots/)、[bar](https://plotly.com/python/bar-charts/)、[funnel](https://plotly.com/python/funnel-charts/)、[timeline](https://plotly.com/python/gantt/) - 三维 :
[scatter_3d](https://plotly.com/python/3d-scatter-plots/),[line_3d](https://plotly.com/python/3d-line-plots/) - 1D 分布 :
[histogram](https://plotly.com/python/histograms/)、[box](https://plotly.com/python/box-plots/)、[violin](https://plotly.com/python/violin/)、[strip](https://plotly.com/python/strip-charts/)、[ecdf](https://plotly.com/python/ecdf-charts/) - 2D 分布 :
[density_heatmap](https://plotly.com/python/2D-Histogram/),[density_contour](https://plotly.com/python/2d-histogram-contour/) - 整体的一部分/层次 :
[pie](https://plotly.com/python/pie-charts/),[sunburst](https://plotly.com/python/sunburst-charts/),[treemap](https://plotly.com/python/treemaps/),[icicle](https://plotly.com/python/icicle-charts/),[funnel_area](https://plotly.com/python/funnel-charts/) - 多维 :
[scatter_matrix](https://plotly.com/python/splom/),[parallel_coordinates](https://plotly.com/python/parallel-coordinates-plot/),[parallel_categories](https://plotly.com/python/parallel-categories-diagram/)
验证用户接受度
最终的设计原则围绕着与用户合作。由于仪表板的目标是交流信息,用户经常会有关于如何改变或改进的问题或建议。如果用户知道他们的反馈被听到了,他们更有可能长期使用仪表板。当您向用户展示仪表板时,请务必询问他们对仪表板的反馈。

约翰·施诺布里奇在 Unsplash 上的照片
有计划
此外,与您的最终用户交流维护计划,如果可能的话,为他们提供直接向您或您的团队提交问题和反馈的方法。例如,在我的工作中,我们使用吉拉软件来管理开发项目。允许内部涉众查看即将到来的增强的进展也是很好的。
破折号提示
如果仪表板是外部的或面向客户的,则向其添加一个联系人表单,以便用户可以提交反馈。将这段代码添加到您的 Dash 应用程序布局中,开始使用 Dash Bootstrap 组件创建联系人表单。
**email_input** = dbc.FormGroup([dbc.Label("Email", html_for="example-email-row", width=2)
, dbc.Col(dbc.Input(type="email"
, id="example-email-row"
, placeholder="Enter email") #end input
, width=10) #end col
], row=True)#end formgroup
**user_input** = dbc.FormGroup([dbc.Label("Name", html_for="example-name-row", width=2)
,dbc.Col(dbc.Input(type="text"
, id="example-name-row"
, placeholder="Enter Name"
, maxLength = 80)#end input
, width=10) #end column
],row=True)#end form group
**message** = dbc.FormGroup([dbc.Label("Message", html_for="example-message-row", width=2)
,dbc.Col(dbc.Textarea(id = "example-message-row"
, className="mb-3"
, placeholder="Enter message"
, required = True)
, width=10)
], row=True)

联系人表单输出(图片由作者提供)
查看本教程,深入了解如何在 Dash 应用程序中创建功能联系人表单。
关于仪表板的最终想法
永远记住,仪表板是一种沟通工具。为了使沟通有效,它必须清晰和引人入胜。遵循这些原则,你就有更好的机会创造出用户认为有价值的东西:
- 了解你的受众,为启示而设计。
- 澄清模糊性,设计易访问性。
- 赏心悦目,设计美观。
- 验证用户接受度和长寿设计。
谢谢大家!
- 如果你喜欢这个, 关注我的 Medium 了解更多
- 通过订阅 获得完全访问权限并帮助支持我的内容
- 让我们连线上LinkedIn
- 用 Python 分析数据?查看我的 网站
https://erickleppen.medium.com/the-ultimate-guide-to-erics-articles-d32df8241353
使用这些工具更快地学习数据科学
准备用 1000+ AI/ML 代码模板给专业数据科学家

我将从计算机科学专业毕业。我没有得到工作机会。我想在一个月内成为一名数据科学家。我不愿意花钱去上那些昂贵的课程。你能帮我吗?
这是成千上万即将毕业的学生表达的观点。他们在获取成为数据科学家的技能方面缺乏明确的方向。数据科学是一个非常热门的领域,它提供了丰厚的薪水,尤其是在家工作的能力。所以,以上的期望还是挺可以接受的。现在,需要有人引导他们成为数据科学家,这也是一个快速发展的过程。凭借数十年的学术和行业经验,我将提供一个快速的解决方案,帮助所有这些有志之士实现这些目标。
首先,让我们试着从商业人士的角度理解对数据科学家的需求,然后你就会明白什么是当今世界的数据科学。
需要一名数据科学家
对于一个商人来说,最大的抱负是如何发展我的生意?他们是这样说的:
过去,我有成千上万的客户。我已经收集了他们购买的大量数据。有人能帮助我了解他们的购买习惯、偏好、购买模式,从而为我提供他们下一步将购买什么的高级信息吗?然后,我将管理我的产品库存,并计划广告。
这就是数据科学家发挥作用的地方。现在,让我们试着理解数据科学家如何帮助商人满足他们的需求。
现在,让我们先试着了解一下一个数据科学家的工作要求是什么?
数据科学家的工作
数据科学家开发了一个机器学习模型,他根据历史数据进行训练,然后使用它来进行未来预测。听起来很简单,不是吗?是的,就是这么简单。在过去,许多人认为这是一项非常复杂的任务,需要大量的技能。告诉我;开车,需要学汽车工程吗?数据科学也是如此。你只需要了解一个机器学习模型是如何开发出来的。
机器学习模型是如何开发的?
整个机器学习模型开发过程是琐碎的。这些是模型开发的标准步骤。
- 数据清理
- 数据预处理
- 创建训练/验证数据集
- 算法选择
- 模特培训
- 模型检验
- 对未知数据的推断
不管您试图解决的是什么问题,上述步骤都是一样的——回归、分类、聚类。这些步骤也独立于学习类型——有监督的 v/s 无监督的。我说的是经典的 ML,它已经在过去几年里通过大量的案例研究证明了它的价值。还有其他进步,如神经网络、DNN、预训练模型,当然,还有强化学习。我改天再处理这些。让我们目前把重点放在传统的 ML 上,那里有大量的工作机会。
如果你看看这些步骤,你可以很容易地得出结论,最令人担忧的步骤是算法选择。其余的步骤看起来像标准程序,事实上也的确如此。为这些步骤编写的代码在您的所有模型中保持不变。如果有人给我一个模型开发的模板怎么办?事实上,有人已经投入了大量的资源来创建市场上数百种 ML 算法的模板,这些算法由顶尖的数据科学家验证和开发。我们只是要重复利用他们的工作。提供这些模板的公司是 BlobCity ( 我还在谷歌搜索其他类似的网站。)
快速浏览 BlobCity 人工智能云
BlobCity AI Cloud 是一个开源项目,它为机器学习算法提供了 1000 多个现成的代码模板,这些算法被很好地分类成组。这些都是开源的,免费用于学习和商业用途。

他们把所有这些算法分成不同的组。比如你想学习不同分类算法的工作原理,选择分类组。你会发现一堆算法,像 SVM,决策树,kNN,朴素贝叶斯,以及更多。不仅如此,你会发现每一种都有不同的版本。要使用模板,只需插入数据集,选择要素和目标,然后执行笔记本中的所有命令。它将在您的数据集上训练模型,并为您提供评估结果。就这么简单。
您可以试验一下数据预处理和模型参数,看看它们如何影响模型的预测准确性。您可以对您选择的其他算法重复此过程,并研究它们相对于其他算法在数据集上的执行情况。你还会发现一些 Cloudbooks,本质上是一个由 AI 社区在其网站上贡献的 Juypter 笔记本。
BlobCity 标记了分类、回归、聚类、EDA、降维、时序分析、自然语言处理、视听等几个组。最后一个是视听产品,可以帮助您在图像、视频和音频数据集上开发机器学习模型。我敢说这项工作非常详尽,他们试图涵盖这个星球上的大多数机器学习算法,同时考虑所有数据类型——二进制、文本、图像、音频和视频。
在我看来,BlobCity 通过提供精选的、经过技术审查的代码模板,让学习者可以非常容易地在一个地方学习数百种算法。专业的数据科学家根据他们的经验知道使用哪种算法,可以简单地在他们的数据集上应用所需的模板。
如果你感兴趣,他们在 Github 上接受开源贡献。
我没有见过任何其他网站为机器学习算法提供这种现成的 1000 多个代码模板(根据我的观察,还在增长——几天前是 600 个)。我将很高兴听到你们是否知道任何类似的网站。谢谢!
结束语
要快速学习数据科学家的技能,请关注市场上的数百种算法。研究它们在不同数据集上的表现。比较它们的输出。研究数据预处理如缩放、归一化、特征选择对其性能的影响。一旦你研究了不同算法的工作原理,考虑使用像 AutoML 这样的高级工具来发现性能最好的算法。请注意,您不必研究算法实现,scikit-learn 和其他几个库中提供的实现是由技术高超的开发人员完成的。不要想象你会比他们做得更好。简单地信任它们的实现并学习使用它们。像 BlobCity 这样的公司提供了几个围绕这些实现开发的代码模板,使您的工作更加容易。祝您快速学习数据科学!
信用
Pooja Gramopadhye—复制编辑
如果有许多异常值,请使用这种聚类方法
稳健结果的 k-中位数变化

如果您曾经想要将数据分组,您可能已经尝试过著名的 k-means 算法。由于它非常简单,所以被广泛使用,但它的简单性也带来了一些缺点。其中之一是它对异常值的
敏感性,因为它使用经典的欧几里德距离作为相异度
度量。
不幸的是,现实世界中的数据集经常带有许多异常值,您可能无法在数据清理阶段将其完全删除。如果你遇到过这个问题,我想给你介绍一下 k-medians 算法。通过使用中值而不是平均值,并使用更健壮的相异度度量,它对异常值不太敏感。
在本文中,我将向您展示以下内容:
像往常一样,你也可以在我的 GitHub 上找到这篇文章的所有代码。
k-中位数直觉
k-medians 试图通过选择
不同的相异度度量来减轻 k-means 对异常值的敏感性。我们通常使用绝对差而不是欧几里德距离,这也称为 L1 范数或曼哈顿或出租车距离(因为您可以使用它来计算出租车到达矩形网格中的目标所需的转弯次数)。
这对异常值不太敏感,因为这些异常值只与
它们到中心的实际距离有关,而不是距离的平方,因为
是欧几里德距离的情况:

两点 p 和 q 之间的曼哈顿度量。对于每个维度,计算两点之间的绝对差值并求和。这也被称为 L1 规范。图片作者。
但是,如果更合适的话,我们也可以使用其他指标,例如 Kullback-Leibler 散度来比较分布。
为了更加可靠,我们还选择了中间值而不是中心的平均值。所以最后我们需要优化以下问题:

k-中位数优化问题的数学表述。尝试找到一组聚类 C,使每个点到其所属聚类中心 Ci 的绝对差最小。图片作者。
k-中值的方法与 k-均值非常相似,也是 Llodyd 的
算法。简单总结一下:
Input parameter k (number of clusters) and n_iter (number of iterations)
Randomly initialize k objects in the data set as centers
Do n_iter times:
Assign each object to its closest center
Calculate the new centers
你可以在我关于 k-means 的文章中找到关于劳埃德算法的更详细的解释:
[## 对 k-means 的深入探究
towardsdatascience.com](/a-deep-dive-into-k-means-f9a1ef2490f8)
在 R 中从头开始实现
如果我们看看编程实现,我们会发现它不像 k-means 那样无处不在。例如在 R 中,stats 包中没有 k-medians
函数。所以让我们自己编码:
在虹膜数据集上进行测试
接下来,让我们看看我们的函数如何在公共虹膜数据集上执行。我们将
将它与基本的 R k-means 实现进行比较,看看它们可能有什么不同:

k-中间值与真实值和 k-均值的比较。两种聚类算法都倾向于找到正确的聚类,只需要观察最小的距离。
对于这个数据集,我们只观察到 k-中位数和 k-均值之间的微小差异,但它也不包含太多的离群值。
摘要
如你所见,它与 k-means 非常相似,我们只是使用了不同的
距离函数,并使用了中值。
关于 k-中位数的一个常见误解是,作为中心返回的中位数总是数据集中的实际点。很容易看出这不是真的。考虑以下由 5 个点组成的示例集群:

为什么聚类的中心不需要成为 k-中位数的实际数据点的示例。考虑由上述 5 个点组成的集群。因为中位数是在 k-中位数中为每个维度单独计算的,所以中位数将是 x = 3,y = 3。但是在数据集中不存在点(3,3)。
因为中位数是在 k-中位数中为每个维度单独计算的,所以中位数将是 x = 3,y = 3。但是在数据集中不存在点(3,3)。
最后,当然您可以将 k-medians 与改进的
初始化结合在一起,比如 kmeans++,使其更加健壮。你可以在我的文章中找到关于如何做的细节:
下次见,届时我们将讨论 k-means 的最高级变体,围绕 medoids 进行划分,结束这个迷你系列。
使用投票分类器来提高你的 ML 模型的性能
投票分类器集成基本指南

图片由 Wokandapix 来自 Pixabay
你永远不知道你的模型是否有用,除非你评估机器学习模型的性能。数据科学家的目标是训练一个健壮且高性能的模型。有各种各样的技术或方法可以提高模型的性能,模型的集成就是其中之一。
集成是一种强大的技术,它通过组合各种基础模型来提高模型的性能,从而产生一个最优的、健壮的模型。组装技术的种类包括:
- 打包或引导汇总
- 助推
- 堆积分级机
- 投票分类器
阅读我以前的一篇文章来更好地理解 bagging ensemble 技术:
在本文中,我们将讨论投票分类器的实现,并进一步讨论如何使用它来提高模型的性能。
投票分类器:
投票分类器是一种机器学习估计器,它训练各种基本模型或估计器,并在汇总每个基本估计器的发现的基础上进行预测。聚集标准可以是针对每个估计器输出的投票的组合决策。投票标准可以有两种类型:
- 硬投票:投票是在预测的输出类上计算的。
- 软投票:投票是根据输出类的预测概率来计算的。
投票分类器如何提高性能?
投票分类器在硬投票或软投票的基础上聚集预测的类别或预测的概率。因此,如果我们向投票分类器提供各种基础模型,它可以确保通过任何模型来解决错误。

(图片由作者提供),左:硬投票,右:软投票
实施:
Scikit-learn 包用几行 Python 代码实现了投票分类器。
对于我们的样本分类数据集,我们正在训练逻辑回归、随机森林、高斯朴素贝叶斯和支持向量分类器的 4 个基本估计器。
参数voting=‘soft’或voting=‘hard’使开发者能够在硬投票或软投票聚合器之间切换。参数weight可以根据用户进行调整,以掩盖一些性能良好的基本估计器。在对软投票进行平均之前,对硬投票的预测类别标签的出现或类别概率进行加权的权重序列。
我们使用软投票分类器和权重分布[1,2,1,1],其中两倍的权重分配给随机森林模型。现在让我们观察每个基本估计器相对于投票分类器的基准性能。

(图片由作者提供),基准性能
从上表可以看出,投票分类器比其基本估计器性能提高了性能。
结论:
投票分类器是一种机器学习算法,经常被 Kagglers 用来提高他们模型的性能和爬上等级阶梯。投票分类器也可以用于现实世界的数据集来提高性能,但它有一些限制。模型的可解释性降低,因为人们不能使用 shap 或 lime 包来解释模型。
与其他模型不同,Scikit-learn 不提供计算投票分类器最高性能特征的实现,但我有一个计算相同特征的黑客。您可以通过基于权重组合每个估计值的重要性分数来计算要素的重要性。跟随我的前一篇文章来更好地理解这一点:
参考资料:
[1] Scikit-learn 文档:https://sci kit-learn . org/stable/modules/generated/sk learn . ensemble . voting classifier . html
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一小部分会员费,不需要你额外付费。
https://satyam-kumar.medium.com/membership
感谢您的阅读
使用纽约市自行车共享出行数据的天气历史记录
通过可视交叉网络服务了解花旗自行车使用情况和每日天气历史
由于外部事件,尤其是恶劣天气,自行车共享的使用情况可能会有所不同。通过获取天气数据来扩充 Citi Bike 提供的出行数据文件,我们可以更好地了解该系统的使用情况。

每日乘车和温度-2020 年 12 月-所有图片由作者提供
花旗自行车提供出行数据文件,以便任何感兴趣的人都可以分析该系统是如何使用的。花旗自行车系统数据页面描述了所提供的信息。在我之前的文章探索纽约自行车共享数据中,我展示了如何通过首先准备数据来使这些数据变得更有用。
下载花旗自行车旅行数据
在我的上一篇文章中,我使用了 2020 年 3 月的数据,当时纽约因疫情而基本关闭。对于本文,我使用 2020 年 9 月的旅行数据文件。数据显示,该系统已经从 3 月份的急剧下降中恢复过来,9 月份是有史以来乘坐次数最多的一个月。
在命令提示符下创建一个bikeshare目录,下载行程数据文件,将其解压缩,然后删除 zip 文件。
mkdir bikeshare && cd bikeshare
wget [https://s3.amazonaws.com/tripdata/202009-citibike-tripdata.csv.zip](https://s3.amazonaws.com/tripdata/202003-citibike-tripdata.csv.zip)
unzip 202009-citibike-tripdata.csv.zip
rm 2020009-citibike-tripdata.csv.zip
导入库和数据
启动 Jupyter,从浏览器窗口在您的bikeshare目录中创建一个新的笔记本。本文中使用的带有 Python 代码的 Jupyter 笔记本在 GitHub 上的名称是 weather.ipynb 。
首先导入常用的库:用于分析的 Pandas 和用于图表的 Seaborn 。此外,对于这个分析,import Requests 提供了一种从调用 RESTful web 服务的 HTTP 请求中获取结果的简单方法。关于这个库的详细信息,请参见 Requests: HTTP for Humans 。
import pandas as pd
from pandas import to_datetime
import seaborn as sns
import matplotlib.pyplot as plt
import requests
首先将 Citi Bike trip 数据文件读入 Pandas 数据框,并使用info()方法获取数据类型和列名。
df = pd.read_csv('202009-citibike-tripdata.csv')
df.info()

tripdata 的信息
这个范围告诉我们表中有多少行,因此九月份有多少次乘坐:将近 200 万次!
准备数据
作为初始数据准备步骤,将对象的starttime转换为日期时间值,然后提取start hour和start day列。另外创建一个布尔列weekend,即周末的True和工作日的False。更多解释见我之前的文章。
df['starttime'] = pd.to_datetime(df['starttime'])
df['start hour']=df['starttime'].dt.hour.astype('category')
df['start day']=df['starttime'].dt.day.astype('category')
df['weekday']=df['starttime'].dt.weekday.astype('category')
df['weekend'] = [ d >=5 for d in df['weekday']]
按小时乘车
从查看一天中乘车次数的分布开始。出于这个目的,我认为把工作日和周末放在一起很有说明性,所以我使用了支线剧情。
fig, ax = plt.subplots(1,2, figsize=(14,5))
sns.countplot(data=df[~df['weekend']],x="start hour",\
ax=ax[0]).set_title("Weekdays")
sns.countplot(data=df[df['weekend']],x="start hour",\
ax=ax[1]).set_title("Weekends") ;

工作日和周末按小时计算乘车次数
这个月,我看到工作日乘车显示早晚高峰时间,而周末乘车显示更均匀的分布。请注意,两个图表中的比例不同。周末每小时的骑行从来没有达到工作日的高峰时间。
白天乘车
但是总体来说什么时候乘坐次数比较多呢?工作日还是周末?
我可以使用countplot来查看白天的游乐设施,使用hue来给条形着色以突出周末,并设置dodge以便有一个条形而不管其值。我还添加了更多描述性的轴标签和标题。
plt.figure(figsize=(12,5))
sns.set_palette("Set2")
sns.countplot(data=df,x="start day", hue='weekend', dodge=False )
ax=sns.countplot(data=df,x="start day", hue='weekend', dodge=False )
ax.set(xlabel="Day of Month", ylabel="Ride Count",\
title='September 2020') ;

显示周末的 2020 年 9 月每天的乘车次数
我发现周末的乘车次数通常比临近的工作日要高一些。乘客量在 9 月 12 日达到顶峰,是该系统历史上最多的一次。
然而,我确实注意到乘车日计数中的一个异常现象:乘车人数在 9 月 10 日显著下降,在 29 日几乎下降了同样多。这是为什么呢?会不会是天气的原因?
天气
为了进一步探索这个问题,我需要一个气象数据源。我发现到的视觉交叉是众多此类来源中比较容易使用的一种。你可以在他们的天气数据服务登录页面注册一个免费的“永久”账户。这给了你每天 1,000 条结果记录的免费配额。对于今年 9 月的数据,我们只需要其中的 30 个。
从他们的网站上,你可以很容易地下载一个 CSV 文件,并把它读入熊猫的数据帧。如果你只打算使用一个月的数据,那就没问题了。然而,如果你想自动化这个过程,你需要使用 API。
- 登录。如果提示“输入位置”,请使用 10001
- 点击天气 API ,打开“时间线天气 API”页面
- 单击日期或日期范围的单选按钮
- 选择日期 2020 年 9 月 1 日至 2020 年 9 月 30 日
- 点击单选按钮 obs 查看历史观察
- 点击按钮复制查询将 URL 复制到您的剪贴板

时间线天气 API 条目页面
然后将 URL 粘贴在引号中,如下所示。注意,它是用您的 API 密钥生成的。您可以看到邮政编码和日期范围是如何包含在 URL 中的。
url='https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/10001/2020-9-1/2020-9-30?unitGroup=us&key=__YOUR_API_KEY_GOES_HERE_&include=obs'
然后用requests取回结果,检查状态码,应该是 200。
response=requests.get(url)
response.status_code
API 返回一个 JSON 文档,我可以使用keys方法获得元素列表:
response.json().keys()

关键名称
我发现days包含一个字典列表,每个月的每一天都有一个字典,所以让我们看第一个。
response.json()['days'][0]

2020 年 9 月 1 日的天气数据字典
这看起来不错,所以我将把所有日期的列表转换成数据帧,并检查列名和数据类型。
dfweather=pd.DataFrame(response.json()['days'])
dfweather.info()

然后我看一下数据。这里有很多信息,所以为了便于阅读,我将选择我最感兴趣的列。
dfweather[['datetime','tempmax','tempmin','temp','humidity',\
'precip','snow','windgust','windspeed','visibility']].head(10)

九月天气—精选栏目
我看到列datetime是一个对象(字符串),尽管它的名字是,但它只包含一个日期。我可以使用带有参数format的熊猫to_datetime()方法将其转换为实际的日期时间值,然后提取一个月中的某一天作为day。
dfweather['datetime'] = pd.to_datetime(dfweather['datetime'],\ format='%Y-%m-%d')
dfweather['day'] = dfweather['datetime'].dt.day
沉淀
由于大多数骑自行车的人不喜欢在雨中骑车,这可以解释为什么在某些日子里骑自行车的人较少。我将使用 seaborn barplot来看看:
sns.set_style("whitegrid")
plt.figure(figsize=(12,5))
ax = sns.barplot(data=dfweather,y="precip", x='day', color="green" )
ax.set(xlabel="Day of Month", ylabel="Precipitation in inches"\
title="Precipitation") ;

白天降水量
事实上,10 号那天下了将近一英寸的雨,这就解释了为什么。然而,降雨量并不能解释 29 日客流量的下降;雨直到第二天才下。
风
骑自行车的人不喜欢顶风骑车。这是一个因素吗?天气数据包括风速和阵风的值。我可以在 Seaborn barplot中显示这两者,用叠加图创建一个堆叠条形图,阵风高于风速。
plt.figure(figsize=(12,5))
sns.barplot (data=dfweather, y="windgust", x='day',\
color="paleturquoise")
overlay=sns.barplot(data=dfweather, x='day', y='windwpeed',\
color = "darkturquoise")
overlay.set(ylabel="Wind Speed and Gusts MPH",\
xlabel="Day of Month", title='September 2020 - Wind');

风速和阵风—2020 年 9 月
虽然这张图表显示 10 日和 29 日是大风天,但也有其他大风天。
温度
温度呢?我不认为一天的平均温度是一个有意义的指标,可以表明一天的骑行程度。更有意义的是看到一天中温度的范围。在下面的图表中,我把最低温度的条看不见了(因为条和背景都是白色的)。
最后,我设置了一个标题和轴标签(要获得度数符号,请按住 Alt 键并在数字键盘上键入 0176)。然后,我限制显示的温度范围,以获得我想要的视觉效果。
sns.set_style("white")
plt.figure(figsize=(12,5))# Main plot - Maximum Temperature
sns.barplot(data=dfw, x='day', y='tempmax' , color="coral" )
# overlay - Mininum Temperature - whiteout
overlay = sns.barplot(data=dfw, x='day', y='Tempmin' , color = "white")overlay.set(ylabel="Temperature Range °F",\
xlabel="Day of Month", title='Septemeber 2020 - Temperature');
overlay.set_ylim(40,90) ; # show temperature range 40° to 90°

白天的温度范围
但是对于九月来说,气温似乎没有太大的影响。
十二月
十二月呢?为了查看更多月份的数据,我将上面的探索性代码转换成了函数。参见 GitHub 上的 weather.ipynb 。
我下载了 12 月的旅行数据文件,运行了温度图表,并添加了一个积雪。

看起来系统在 12 月 17 日被关闭了。温度表显示那天气温在零度以下。

2020 年 12 月每天的温度范围
通过绘制降雪图,我们可以看出为什么:16 日下了 5 英寸的雪,17 日又下了 3 英寸。

十二月的日间降雪量
结论
使用 requests 库可以轻松获得来自可视交叉网络服务的天气数据,并用于增强对花旗自行车出行数据的分析。
暴雨和暴风雪影响自行车共享使用并不奇怪,但用图表来看这种影响很有启发性。
你应该学习的有用的大查询分析函数
熟悉它们,对你的数据探索和准备任务会有很大帮助。

艾萨克·史密斯在 Unsplash 上拍摄的照片
在本文中,我将提供 BigQuery 分析函数,我发现这些函数在日常数据探索和准备工作中非常有用。另外,我会分享在使用它们时可能出现的情况,这样你会有更好的理解。
我假设你在阅读这篇文章之前已经熟悉了 SQL 和 BigQuery。如果没有,下面是基于 BigQuery 文档的概述:
BigQuery 是一个企业数据仓库,它通过使用 Google 基础设施的处理能力实现超快速的 SQL 查询来解决这个问题。
你可以在这里阅读更多:【https://cloud.google.com/bigquery/docs/introduction】T4
本职位面向(不限于):
- 完全初学者,希望为您的数据科学之旅添加更多知识和武器。
- 已经在使用 BigQuery,但想提高使用该工具的技能和效率。
- 已经知道这个概念,但仍然不能掌握它在现实生活中如何工作的学生/新兵训练营成员。
享受…
什么是大查询分析函数?
基于大查询标准的 SQL 文档:
分析函数计算一组行的值,并为每一个行的返回一个结果。这不同于聚合函数,聚合函数为一组行的返回单个结果。
简而言之,它不需要 GROUP BY 子句,并且它为每一行返回一个单值。分析函数的其他特征是,它们在子句上使用,并增加了窗口的规格和范围。
我不会在这里讨论细节,你可以在文档中仔细阅读。另一方面,通过使用我将在下一节中提供的示例,您可以看到这些子句是如何工作的。
示例和使用案例
请注意,我会过度简化用例,但我希望我仍然可以反映现实生活中的用例。
1.行号
用例:
您有一个包含从主应用程序数据库(MongoDB、MySQL 等)中提取的数据的表。提取每天在的基础上运行,并将当前值的每日快照存储在数据库中,唯一的标识符是最后更新字段,它指示最后摄取时间。
您希望获得只包含每个 unique_id 的最新状态的数据,并将其包含在您的分析中。那么你会怎么做呢?
示例数据:
对于这个用例,让我们以 BigQuery 公共数据FCC _ political _ ads中的数据为例。

示例数据(图片由作者提供)
如您所见,对于每个唯一标识符,我们都有一些 latest_status 值。在我们的分析中,我们只对最近的感兴趣。
解决方案:
通过使用分析函数行号,我们可以根据它们的 unique_id 给每一行一个基于新近度的排名。

将 row_number 作为辅助对象的示例数据(图片由作者提供)
请注意,现在您有了这个新的标识符字段,您可以使用它来只过滤包含最近更新的行。

最终结果(图片由作者提供)
2.滞后和超前
https://cloud . Google . com/big query/docs/reference/standard-SQL/navigation _ functions # lag
用例:
你有一个每日跟踪器,跟踪一定数量的事件。你要计算该数字与昨天的数字的差。你会怎么做?
示例数据:

示例数据(图片由作者提供)
解决方案:
首先,我们可以使用 LAG 函数来创建一个帮助字段prev _ percent _ congestion

使用滞后函数后的结果(图片由作者提供)
最后,我们可以用昨天的数字来计算今天的数字。

使用滞后函数后的最终结果(图片由作者提供)
请注意,每个 city_name 的第一个数据将具有空值,因为一开始就没有以前的数据。
你也可以使用一个类似的函数,即 LEAD 函数。它执行与滞后功能相反的操作,不是取前一个值,而是取下一个值。
3.首值和末值
用例:
我们将使用一个类似的用例,如上面的例子,关于城市中的拥堵,但现在让我们说拥堵并不是每次都发生(为了举例)。
与前面的用例类似,我们想计算每个城市的当前拥堵与上次记录的拥堵之间的差异。
示例数据:
对于这个用例,我们将使用与上一个例子相同的数据,但是我将删除一些值来反映我们的用例。

包含一些空值的返工数据(图片由作者提供)
解决方案:

使用最后一个值函数后的结果(图片由作者提供)
然后,我们可以像在第二个用例中一样执行简单的减法。如果您对获取第一个值感兴趣,您可以只使用第一个值函数。
结束语
您已经学习了 Bigquery 中的一些有用的分析函数,可以应用到您的特定用例中。让自己适应它们需要一些时间,但是一旦你有了足够的实践,你会发现自己在使用查询准备、探索和分析数据方面做得更有效率。
最后,快乐学习!!🚀
有用的 IPython 魔术命令
成为 Jupyter 笔记本超级用户
使用带有 IPython 内核的 Jupyter Notebook 时,IPython 神奇的命令就派上了用场。这些神奇的命令使得完成某些任务变得更加容易。您可以将它们视为 Python 语法之外的一组额外的有用语法。在本帖中,我们将熟悉一些你可以在 Jupyter 笔记本中使用的有用的魔法命令。

IPython 魔法命令💫
如果你不熟悉魔法命令,很可能你在不知不觉中使用了一些。这个语法:%matplotlib inline你看着眼熟吗?大概是吧?%matplotlib是一个 IPython 魔法命令。你看这个命令是如何以%开头的?这是魔法命令的一个共同特征:它们以%开始。有两种魔法命令:
- 线魔法命令(从
%开始) - 单元格魔术命令(从
%%开始)
对于行魔术命令,在同一行中的命令之后提供输入。对于单元魔术命令,整个单元中的内容成为其输入。如果您不太确定我们这样说的意思,第 3 节中的一个例子将有望澄清。
现在我们对它们有了一点了解,是时候探索一些有用的魔法命令并熟悉它们的示例用法了!✨
📍 1.使用 %load 导入代码
我们可以使用这个神奇的命令将代码从外部资源加载到 Jupyter Notebook 的一个单元格中。这里有一个有用的应用:
对于大多数数据科学项目,您可能会发现自己在不同的笔记本上一遍又一遍地导入同一套库。为了使这个过程更快,我们可以为 Jupyter 笔记本准备一个标准的设置脚本,并在每个笔记本的开头导入这个脚本,以减少重复输入。让我们看一个例子来说明这是什么意思。

假设我们正在位于项目 1 文件夹中的 magic_commands.ipynb 中工作,并且 setup.py 包含以下设置脚本:
# Contents in setup.py
# Data manipulation
import numpy as np
import pandas as pd
pd.options.display.max_columns=None
pd.options.display.float_format='{:.2f}'.format# Visualisation
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(style='whitegrid', context='talk', palette='rainbow')
我们可以在不离开笔记本的情况下,使用以下一个命令行导入 setup.py 中的内容:
%load setup.py

按 Ctrl + Shift 运行单元格
从这个例子中我们可以看到,当我们运行这个命令时,它会插入来自 setup.py 的代码,并对自己进行注释。由于标准导入可以在大多数项目中使用,您可能更喜欢将脚本保存在桌面(父目录)中,并仅在需要时在项目文件夹中有一个项目特定的设置(例如,NLP 项目将需要一组额外的导入)。

如果我们想从同一个笔记本的父文件夹中加载 setup.py ,我们可以更新文件路径以反映这一变化:
%load ..\setup.py
虽然这个例子可能看起来微不足道,但这是一个可以开始实践的小变化,并且有望启发其他应用程序。
在我们进入下一个命令之前,值得一提的是,从导入代码时。py 文件是常用的,你也可以从其他文件如导入内容。txt 和。md 。此外,您还可以从 URL 导入代码,如下所示:
%load [https://gist.githubusercontent.com/zluvsand/74a6d88e401c4e3f76c2ae783a18689b/raw/5c9fd80a7bed839ba555bf4636e47572bd5c7e6d/pickle.py](https://gist.githubusercontent.com/zluvsand/74a6d88e401c4e3f76c2ae783a18689b/raw/5c9fd80a7bed839ba555bf4636e47572bd5c7e6d/pickle.py)
📍 2.用 %%writefile 保存代码
这个命令让我们执行与上一个命令相反的操作。我们可以使用这个神奇的命令将代码从 Jupyter Notebook 中的一个单元格保存到外部源代码中。如果我们想象自己仍然在 magic_commands.ipynb 中,这就是我们如何在不离开笔记本的情况下创建 setup.py 到桌面:
%%writefile ..\setup.py
# Data manipulation
import numpy as np
import pandas as pd
pd.options.display.max_columns=None
pd.options.display.float_format='{:.2f}'.format# Visualisation
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(style='whitegrid', context='talk', palette='rainbow')
如果不存在,这将创建一个 setup.py 文件。否则,它将覆盖现有文件中的内容。
📍 3.带有 %timeit 或%%timeit 的时间代码
通常有多种方法来完成同一项任务。在选择这些选项时,一个重要的考虑因素是速度。或者有时您只想对代码计时以了解其性能。无论您的用例是什么,知道如何为代码计时都是很有用的。幸运的是,用%[%]timeit计时编码很容易。
首先,我们将准备一些虚拟数据:
import numpy as np
np.random.seed(seed=123)
numbers = np.random.randint(100, size=1000000)
让我们假设我们想要对这个代码进行计时:mean = np.mean(numbers)。我们可以用下面的一个 liner 来实现:
%timeit mean = np.mean(numbers)

输出显示了多次运行和循环中速度的平均值和标准偏差。与基于单次运行的计时相比,这是一种更为严格的计时方式。
现在让我们来理解一下%timeit和%%timeit的区别(下面的指导方针适用于大多数行和单元魔术命令):
◼ ️️To 使用%timeit,一个行魔术命令,你想要计时的代码应该由一个单行组成,并被写入跟在魔术命令后面的同一行。尽管这是一个很好的通用规则,但根据文档的调整,多行是可能的(详情见文档)。
◼使用%%timeit,一个单元魔术命令,你想要时间的代码可以由任意数量的行和写在下一行跟随魔术命令。
下面是使用%%timeit的前一段代码的等效代码:
%%timeit
mean = np.mean(numbers)

很可能您想要计时的代码将由多行组成,在这种情况下%%timeit将会派上用场。
这里有一个快速测验来测试你的理解能力。你认为下面两个细胞的输出有什么不同?继续之前,试着想想答案。💭
##### Cell A start #####
%timeit mean = np.mean(numbers)
np.mean(numbers)
##### Cell A end ########## Cell B start #####
%%timeit
mean = np.mean(numbers)
np.mean(numbers)
##### Cell B start #####
答案来了。在单元格 A 中,首先我们计算第一行代码的时间:mean = np.mean(numbers)然后我们求出平均值,而在单元格 B 中,我们计算两行代码的时间:

你可以看到小区 B 的平均速度大约是小区 A 的两倍。这是有意义的,因为我们实际上是在单元格 b 中对相同的代码计时两次(一次有赋值,一次没有赋值)。
%[%]timeit根据执行代码所需的时间自动调整循环次数。这意味着运行时间越长,重复的次数就越少,反之亦然,所以不管代码有多复杂,它总是花费相同的时间。但是,您可以通过调整可选参数来控制运行和循环的次数。这里有一个例子:
%timeit -n500 -r5 np.mean(numbers)

这里,我们指定了 5 次运行和 500 次循环。
📍 4.使用%历史记录、%笔记本、%回忆检查会话历史记录
如果你试验了一堆东西,并且已经开始变得混乱,所以很难确切地记得你做了什么,这些命令组是非常有用的。我们可以用%history检查我们在当前会话中运行的命令的历史。值得注意的是,%hist可以用来代替%history.
假设我们在第 3 部分开始了一个新的会话。我们可以通过以下内容查看会话历史:
%history

这很好,但是很难看出一个命令在哪里结束,另一个命令在哪里开始。以下是如何检查每个命令的历史记录:
%history -n

这更容易处理。现在,让我们学习如何导出历史。如果我们想将历史记录写到与笔记本在同一个目录下的名为 history.py 的文件中,那么我们可以使用:
%history -f history.py
如果我们想将历史记录写入到一个名为 history.ipynb 的 Jupyter 笔记本中,该笔记本与当前笔记本在同一个目录中,那么我们使用%notebook:
%notebook history.ipynb
这将把每个命令插入到一个单独的单元格中。相当方便不是吗?
有时,我们可能想从历史中调用一段命令来调整它或重新运行它。在这种情况下,我们可以使用%recall。使用%recall时,我们需要从历史中为命令段传递相应的编号,如下例所示:
%recall 1-2

上面的代码将历史中的前两个命令插入到下一个单元格中。
📍 5.其他魔法命令
在这篇文章中,我们只讨论了一小部分命令。所以在这一节,我想提供一个简单的指南,告诉你如何自己探索更多的神奇命令。
◼️要查看所有可用的魔法命令,运行%lsmagic。
◼️要访问所有命令的文档,查看文档页面或运行%magic。
◼️要访问一个魔术命令的文档,你可以运行这个魔术命令后跟?。比如:%load?。
最后,尝试在您的 Jupyter 笔记本中运行以下一个程序:
%config MagicsManager

如果你得到相同的输出,即使我们没有在一个魔术命令的开头写%或%%,它仍然会被识别。例如,如果您尝试运行以下语法,您将看到与之前相同的输出:
config MagicsManager
虽然我认为这是一个方便的特性,但是编写前缀会使代码更具可读性,因为很容易通过前缀看出这是一个神奇的命令。
Voila❕,我们已经介绍了一些有用的魔法命令,你可以自己探索更多。✨曾经说过“学习而不做真的是不学习。知而不做真的是不知”,希望大家练习一下这些神奇的命令,巩固今天所学的知识。

您想访问更多这样的内容吗?媒体会员可以无限制地访问媒体上的任何文章。如果您使用 我的推荐链接 ,成为会员,您的一部分会费将直接用于支持我。
谢谢你看我的帖子。如果你感兴趣,这里有我的一些帖子的链接:
◼️ 数据科学的 Python 虚拟环境简介
◼️ 数据科学的 Git 简介
◼️ 用这些技巧组织你的 Jupyter 笔记本
◼️python 中的简单数据可视化,你会发现有用的
◼️ 关于在 Seaborn (Python)
中更漂亮和定制的情节的 6 个简单技巧
再见🏃💨
对数据科学有用的 pip 命令
看看 Python 中最常用的包管理系统

作者图片
塞巴斯蒂安·拉什卡 等人在 2020 年 2 月发表了一篇深入的文章,研究了 Python 在机器学习生态系统中的角色和重要性。题为Python 中的机器学习:数据科学、机器学习和人工智能的主要发展和技术趋势 的论文提出了一个有趣的观察,我想在这里引用一下:
历史上,广泛的不同编程语言和环境已被用于实现机器学习研究和应用程序开发。然而,随着通用 Python 语言在过去十年中在科学计算社区中的流行程度大幅增长,大多数最新的机器学习和深度学习库现在都是基于 Python 的。
Python 真正改变了数据科学的面貌,并成为当今数据科学中使用最多的库之一。这一点从创建和使用的 python 包的数量上也很明显。截至 2020 年 7 月,超过 235,000 个 Python 包%20or%20precompiled%20%22wheels.%22)可以通过 PyPI 访问。那么 PyPI 是什么呢?
Python 包索引 (PyPI)是 Python 编程语言的软件仓库。这个存储库包含了由不断增长的 Python 社区创建和共享的包。您可以使用 pip 从 Pypi 安装任何包,pip 是 Python 的包安装程序。每个 Python 程序员,不管是新的还是老的,都会多次使用pip install <package name>。但是,还有其他有用的 pip 命令,尤其是从数据科学的角度来看,可能非常有用。本文试图解释一些常用的 pip 命令及其常用选项。
入门指南
首先,我们将创建一个虚拟环境。这样,更容易显示各种 pip 命令的运行情况。让我们使用venv来创建这个新的虚拟环境,并将其命名为**env.**。python 的 venv 模块用于创建轻量级的“虚拟环境”
# Creating virtual environment in Mac/Linux
python3 -m venv env# Creating virtual environment in Windows
py -m venv env
一旦创建了 env 环境,我们将激活它,然后就可以开始工作了。

让我们从检查我们的环境中是否安装了 pip 开始。从技术上来说,如果你使用Python 2 >=2.7.9或Python 3 >=3.4, pip 应该已经安装。pip --version命令返回安装的 pip 的位置和版本。

既然一切就绪,现在让我们一个接一个地看看几个重要的和最常用的 pip 命令。
1.pip 帮助
如果你在终端中输入pip help,你将得到一个单页可滚动的文档。

作者图片
它显示了可用于 pip 的各种命令,以及如何使用这些命令。此外,如果您希望查看有关单个 pip 命令的详细信息,您可以:
pip help <command_name>
example: pip help <install>
这将显示您感兴趣的单个命令的详细信息。
2.pip 列表
如果想要查看所有已安装的软件包,您可以执行pip list,它将输出环境中当前安装的所有软件包。
语法:
pip list

上面的输出显示,目前,我们只安装了两个包,其中,pip 本身属于一个过时的版本。
选择
pip list可与一系列选项一起使用,例如:
--outdated/ -o用于列出所有过期的包
pip list --outdated or
pip list -o

看起来两个安装的包都过时了。
- -update/-u 用于列出所有最新的软件包
pip list --uptodate or
pip list -u
--format选择在屏幕上显示已安装软件包的输出格式。可用选项包括-列(默认)、冻结或 JSON。

3.pip 安装
pip install 命令用于安装新的软件包。让我们在我们的虚拟环境中安装pandas,数据科学的面包和黄油包。

为了检查熊猫的包是否已经安装,我们可以做一个快速的pip list来查看所有已安装的包。

我们可以看到,pandas及其其他依赖项已经很好地安装在虚拟环境中。
选择
pip 安装也没有什么有用的选项。
--upgrade/ -U用于将所有指定的软件包升级到最新的可用版本。

**--requirement** <file>/ -r用于从给定的需求文件中安装。一个需求文件是一个项目所有依赖项的列表。这个文本文件包含所有必需的包,包括每个依赖项的特定版本。需求文件通常看起来是这样的:

requiremnts.txt 文件的快照|按作者排序的图像
要安装 requirements.txt 文件中提到的所有包,您只需:
pip install -r requirements.txt
4.皮普秀
该命令显示关于已安装软件包的信息。人们可以选择在屏幕上显示的信息量。假设我们想知道关于pandas包的细节,我们知道这个包安装在我们的环境中。为了展示有限的细节,我们可以做pip show熊猫:
pip show pandas

如果你想要完整的细节,你可以使用pip show命令的verbose选项,
pip show --verbose pandas

5.pip 卸载
顾名思义,pip uninstall会卸载想要的包。根据文档,很少有无法卸载的异常。它们是:
- 用
python setup.py install安装的纯 distutils 包,以及 - 由
python setup.py develop安装的脚本包装器。
我们现在将卸载最近安装的熊猫包。这个过程非常简单,如下所示:
pip uninstall pandas

选择
pip 卸载有两个选项,即:
--requirement <file>/ -r用于从需求文件中卸载软件包。--yes / -y。如果选择此选项,在卸载软件包时不会要求确认。

6.pip 冻结
在第 3 节中,我们提到了项目中对requirements file的需求。嗯,pip freeze 可以让你轻松地创建一个。它以需求格式输出所有已安装的包及其版本号。

然后,freeze 命令的输出可以通过管道传输到需求文件中,如下所示:

结论和额外资源
这些是 Python 中一些有用的 pip 命令,我在日常活动中使用它们。这可以作为了解 pip 的便利资源。还有其他一些命令没有在本文中介绍。如果你想更深入地了解细节,官方文档是一个极好的资源。
有用的 Python NLP 库
你不知道的事

简介
最近在玩自然语言处理吗?如果您有,您可能已经使用 spacy 或 NLTK 库构建了一些基本的 NLP 算法并处理了文本数据。然而,还有其他 python 库可以帮助进行数据预处理或可视化。
在本文中,我将向您展示我在处理文本时经常使用的三个鲜为人知的库。它们大多是为完成特定任务而设计的小型库。
让我们开始吧。
数字器
我想向你介绍的第一个库是 numerizer。顾名思义,它将书面数字转换成相应的数字表示。
您可以使用以下代码安装它:
pip install numerizer
这个图书馆很容易使用。你只需要在字符串编号上调用 numerize 函数。以下示例将“55”更改为其数字表示形式“55”。
>>> from numerizer import numerize
>>> numerize(‘fifty five’)
'55'
它也适用于分数和不太传统的分数表示。例如,当提到分数时,你可以使用像四分之一、一半等词,并且仍然应该正确转换。
>>> numerize(‘fifty and a quarter’)
'50.25'
这个库非常有用,我在处理转录文本时经常使用它。
表情
这是另一个有用的库,当你处理文本时,尤其是当人们使用大量表情符号和表情符号时,来自社交媒体的文本。
该库获取表情符号和表情符号,并返回它们的文本表示。就这么简单。
您可以使用以下代码安装 emot:
pip install emot
让我们看一个关于如何在一些包含表情符号的字符串上使用 emot 库的实际例子。我们将通过它'我爱蟒蛇☮🙂 ❤'.
>>> import emot
>>> emot_obj = emot.emot()
>>> text = “I love python ☮ 🙂 ❤”
>>> emot_obj.emoji(text){'value': ['☮', '🙂', '❤'],
'location': [[14, 15], [16, 17], [18, 19]],
'mean': [':peace_symbol:', ':slightly_smiling_face:', ':red_heart:'],
'flag': True}
正如你所看到的,在导入库之后,我们必须创建一个 emot_object。一旦我们这样做了,我们可以简单地调用表情符号文本上的 emoji()函数。
结果是字典具有关于包含在字符串中的表情符号和表情符号的详细信息,例如它们的文本表示和位置。
还有一个很有用的函数叫做 bulk_emoji(),可以让你从字符串列表中提取表情符号。这可能是很多 NLP 数据的样子,所以不需要手动遍历列表。只需调用 bulk_emoji():
>>> import emot
>>> emot_obj = emot.emot()
>>> bulk_text = ["I love python ☮ 🙂 ❤", "This is so funny 😂",]
>>> emot_obj.bulk_emoji(bulk_text, multiprocessing_pool_capacity=2)[{'value': ['☮', '🙂', '❤'],
'location': [[14, 15], [16, 17], [18, 19]],
'mean': [':peace_symbol:', ':slightly_smiling_face:', ':red_heart:'],
'flag': True},
{'value': ['😂'],
'location': [[17, 18]],
'mean': [':face_with_tears_of_joy:'],
'flag': True}]
结果,您将得到一个字典列表,如上例所示。
WordCloud
另一个非常有用的库是 WordCloud。它帮助你在给定的文本中可视化词频。
你听说过“一图胜千言”这句话吗?
我认为这是这个图书馆成就的精髓。通过查看给定文本的单词云,你可以不用阅读就能猜出该文本的内容。
那么它是如何工作的呢?
您可以使用以下代码安装它:
pip3 install WordCloud
一旦你下载了这个库,使用起来就非常简单了。只需在 WorldCloud 对象上调用 generate()函数,并将一些文本作为参数传递给它。
让我们看看我从 CNN 健康中摘录的以下文字:
“作为两个小女孩的母亲,我发现教孩子如何做饭——尽管这有挑战性——也是我作为父母最有收获和最愉快的经历之一。我的女儿们从蹒跚学步开始就在厨房里看着我——基本上是从她们能吃鳄梨泥开始。
这是我的第一点建议:早点开始。让你的孩子观察你做饭,让他们在小的时候参与简单的食物准备,这不仅仅是帮助他们在厨房里变得舒适。这也增加了他们享受健康食品的几率。"
您可以使用下面的代码为上面的文本创建一个单词云。
from wordcloud import WordCloudtext = “As a mom of two young girls, I’ve found that teaching kids how to prepare meals — while it comes with its challenges — has also been one of my most rewarding and enjoyable experiences as a parent. My daughters have watched me in the kitchen since they were toddlers — basically since they could eat mashed avocado. That’s my first bit of advice: start early. Allowing your children to observe you cooking and involving them in simple food prep at a young age will do much more than help them become comfortable in the kitchen. It also increases the odds they will enjoy eating healthy foods.”wordcloud = WordCloud().generate(text)
一旦你生成了云数据,显示它是非常容易的。你只需要从 matplotlib 导入 plt 模块,设置一些显示参数。
import matplotlib.pyplot as pltplt.imshow(wordcloud, interpolation=’bilinear’)
plt.axis(“off”)
plt.show()

瞧啊。你可以立即看到这篇课文是关于:小孩、妈妈和厨房。
你可以玩这个库,通过改变云的大小和颜色以及指定文本中使用的停用词来根据你的需要设置它。
总结
在本文中,我分享了三个 NLP 库,在处理文本数据时,我用它们来进行一些文本处理和可视化。
我希望您喜欢这篇文章,并且有机会在一些真实的数据集上使用本教程中介绍的库。
最初发布于 aboutdatablog.com: 你不知道的有用的 Python NLP 库…2021 年 8 月 26 日。
PS:我正在 Medium 和*https://www.aboutdatablog.com/上撰写深入浅出地解释基本数据科学概念的文章。你可以订阅我的* 邮件列表 每次我写新文章都会收到通知。如果你还不是中等会员,你可以在这里加入。
下面是一些你可能会喜欢的帖子
** </9-things-you-did-not-know-about-jupyter-notebook-d0d995a8efb3> **
Python 中二进制数据的用户相似性
为二进制数据选择相似性度量时需要考虑什么

作者图片。
用户聚类和相似性计算在当今的行业中越来越重要。顾客被要求对特定的 f. e .产品进行评分,然后与其他顾客的评分进行比较,以找出它们之间的相似之处。
大多数用户聚类应用程序使用分级评级,例如 0-5 颗星,或者像 IMDB 上的评级等级 1-10。在这些情况下,我们可以很容易地应用像欧几里德距离或余弦相似度这样的度量来发现用户的选择有多相似/不同。如果我们没有这样的收视率,我们在处理 二进制数据怎么办?
在本文中,我将向您展示在二进制数据上使用欧几里德距离度量时为什么要小心,使用什么度量来计算用户相似性,以及如何创建这些用户的排名。我将使用 Python 3 和 Scipy 和熊猫 T21。
import scipy.spatial
import pandas as pd
1 |计算适当的相似性度量
假设我们有三个用户:A、B 和 c。这些用户都填写了关于他们最喜欢的水果的多项选择调查。

表 1:用户对喜爱水果的选择。图片作者。
用户的选择可以被解释为独热编码向量,而✔️被替换为 1,❌被替换为 0。
user_choices = [[1, 1, 0, 0],
[1, 1, 1, 0],
[1, 0, 0, 0]]df_choices = pd.DataFrame(user_choices, columns=['Apples',
'Bananas', 'Pineapples', 'Kiwis'],
index=(["User A", "User B", "User C"]))
为什么欧氏距离或余弦相似性等度量不适用于此数据集?
首先看一下表 1,我们会建议用户 A 和用户 B 有更相似的口味,因为他们都选择了“苹果”和“香蕉”作为他们最喜欢的水果。
但是,计算用户之间的欧几里德距离会得到如下结果,如表 2 所示:
euclidean = scipy.spatial.distance.cdist(df_choices, df_choices,
metric='euclidean')user_distance = pd.DataFrame(euclidean,
columns=df_choices.index.values,
index=df_choices.index.values)

表 2:用户之间的欧几里德距离。图片作者。
尽管我们看到用户 A 和用户 B 都选择了更多相同的水果,但欧几里德距离为用户 B 和用户 c 返回了相同的距离值 1.00。为什么会这样呢?
欧几里德距离考虑了联合缺席(即两个用户在相同的位置上具有 0)。因此,它也认为用户 A 和用户 C 是相似的,因为他们都没有选择“菠萝”。同样的原理也适用于余弦相似性。对于某些用例,欧几里德距离可能仍然是二进制数据的合适度量,但是对于我们的用例,它给出了错误的结果。
我们不希望根据用户没有做出的选择来假设用户是相似的。
为了我们的目的,我们应该转向一种叫做雅克卡距离的度量。

图 1: Jaccard 距离方程。作者图片。
TT (True True)是两个用户选择相同水果的次数(即两个用户在相同的位置都有 1)。TF (True False)和 FT (False True)是只有一个用户选择水果的次数(即一个用户选择 1,另一个用户在相同位置选择 0)。我们不考虑两个用户都不选择水果的情况。
现在让我们看看 Jaccard 距离如何对我们用户的相似性进行评级:
jaccard = scipy.spatial.distance.cdist(df_choices, df_choices,
metric='jaccard')user_distance = pd.DataFrame(jaccard, columns=data.index.values,
index=data.index.values)

表 3:用户之间的 Jaccard 距离。图片作者。
从上表中我们可以看到,实际上,与用户 C 到用户 A 的距离值相比,Jaccard 距离值认为用户 B 与用户 A 更相似,因为它的距离值更低。这是我们希望我们的用例达到的结果。
2 |根据用户选择的相似性对用户进行排名
为了完成我们的任务,让我们根据用户的相似性对他们进行排序,并将他们导出为 Python 字典。
# prepare a dictionary
user_rankings = {}# iterate over the columns in the dataframe
for user in user_distance.columns: # extract the distances of the column ranked by smallest
distance = user_distance[user].nsmallest(len(user_distance))
# for each user, create a key in the dictionary and assign a
# list that contains a ranking of its most similar users
data = {user : [i for i in distance.index if i!=user]}
user_rankings.update(data)
输出字典将如下所示:
{'User A': ['User B', 'User C'],
'User B': ['User A', 'User C'],
'User C': ['User A', 'User B']}
用户 A 对喜爱的水果的选择更类似于用户 B,而不是用户 C。
用户 B 对喜爱的水果的选择更类似于用户 A,而不是用户 C。
本文演示了 Jaccard 距离如何成为计算二进制向量之间相似性的合适度量。
尽管如此,始终确保根据数据集类型和您试图实现的目标明智地选择相似性/距离度量。
参考资料:
[1] A. S. Shirkhorshidi,S. Aghabozorgi S,T. Y. Wah,连续数据聚类中相似度和相异度的比较研究 (2015),PLoS ONE 10(12): e0144059。
[https://www.ibm.com/support/knowledgecenter/SSLVMB_sub/statistics_mainhelp_ddita/spss/base/cmd_proximities_sim_measure_binary.html 2] IBM,二进制数据的距离相似性度量 (2021)
通过简单的例子解释用户对用户和项目对项目的协同过滤(第 1 部分)
简单解释了协同过滤背后的魔力。

Samuel Regan-Asante 在 Unsplash 上拍摄的照片
如果你是一名数据科学家,想要建立一个项目组合来推荐书籍/电影/产品,或者你只是想了解协同过滤是如何工作的,这篇文章将会帮助你。
概括地说,有两种主要类型的推荐系统。基于内容的协同过滤。顾名思义,第一种基于内容的类型通过推荐与你喜欢的内容相似的产品来工作。一种常见的方法是推荐具有相似描述的产品(即内容)作为您最喜欢的产品。在这篇文章中,我们将关注第二种类型,即协同过滤方法,这种方法根据用户评级推荐产品,在行业中被广泛采用。
让我们用电影推荐来说明这个概念。如果我们使用用户对用户的方法,我们会发现用户之间的相似性,而推荐给用户的电影将基于与他们最相似的另一个用户喜欢的电影。
另一方面,如果我们使用项目到项目的方法,我们会发现电影之间的相似性。然后如果一个用户喜欢一部电影,我们会推荐和那部最相似的电影。
用户到用户和项目到项目之间的区别可能会令人困惑,帮助我区分它们的概念是我们是否使用用户或项目作为绘制的轴。如果我们使用用户作为轴,那么这意味着我们在使用项目到项目的方法。反之亦然,如果我们用电影作为轴心,这意味着我们在做用户对用户的方法。我们来举例说明。
用户对用户的方法
这里有一个例子,其中 3 个用户(u1、u2、u3)对 2 部电影(m1、m2)进行了评级。

3 电影用户评分 1 & 2
然后,我们可以通过将电影 1 的收视率作为 x 轴,将电影 2 的收视率作为 y 轴来绘制这些用户。

用户对用户的方法
如你所见,用户 1 和用户 3 非常相似。如果我们有一些电影 X,用户 1 确实喜欢,而用户 3 没有看过,我们将只把电影 X 推荐给用户 3。
逐项方法
现在让我们假设只有 2 个用户(u1,u2)对 3 部电影(m1,m2,m3)进行了评级。

用户 1 & 2 评价的 3 部电影
我们不会试图寻找用户之间的相似性,而是将重点放在以每个用户评级为轴绘制电影上。

逐项方法
通过查看图表,我们可以看到电影 1 和 3 更接近,因此更相似。起初,这可能看起来有点像魔术,但肯定是有效的,因为喜欢动作片的用户可能会对其他类型的评分很低,从而导致这些相似性聚类的出现。但是我们如何量化这种相似性呢?
确定相似性的两种流行方法是余弦距离和皮尔逊相关性,余弦距离本质上是两个向量之间的角度投影,皮尔逊相关性计算向量之间的成对相关性。
余弦相似度

确定两个向量 A 和 b 之间余弦相似性的公式。分子表示两个向量的点积,分母表示它们的大小相乘。
让我们用 python 计算余弦距离。

from sklearn.metrics.pairwise import cosine_similaritycosine_similarity(df)

或将余弦相似性数组放入数据帧中

余弦相似矩阵
我们做了什么,这意味着什么?
通过做余弦相似度(df),我们已经计算了每部电影之间的 cos(Theta)。因此,第一行向我们展示了电影 1 与其自身完全相关/相似,与电影 2 相关性差,与电影 3 相关性非常强。相关性越强,电影越相似;这使得我们能够向一个用户推荐他过去可能喜欢的类似电影。
标准化评级
将用户相对于其他用户的“审阅者难度”标准化的常用方法是通过减去平均值并将评级除以范围来标准化他们的评级。
如果我们将评级标准化,然后取余弦相似度,我们会得到

归一化余弦相似矩阵
现在你可以看到相关性更容易解释,但得出的结论与我们之前的分析相同。
皮尔森相关性
就拿电影之间的皮尔逊相关性来说吧。提醒一下,这是我们的逐项评级数据框架(df)

我们仍然在做项目到项目的方法,但是为了计算电影之间的相似性,我们必须对这个矩阵进行转置,使每部电影成为一列。原因是 Pearson correlation 将每一列视为一个向量,并将计算它们之间的成对相关性。

项目间的皮尔逊相关矩阵
正如你所看到的,对角线上的词都是 1,因为每部电影都有完美的相关性/完美的相似性。但是为了解释其含义,从第一行我们可以看到,电影 1 与电影 2 的相关性非常差,而与电影 3 的相关性非常强,这也与我们使用归一化余弦相似性方法得到的结果一致。
最后的话
如果用户告诉我们他们最喜欢的电影,我们如何扩展这种方法并创建一个函数或后端 API 来向用户推荐电影?
例如,一个简单的策略是,如果他们告诉我们他们喜欢电影 1;我们将获得 m1 的相似性行,按照降序排列相似性值,并选取与电影 1 最相似的前 10 或 50 个项目,并将它们推荐给用户。
我将在下一篇文章“如何使用开源 MovieLens 数据集构建电影推荐引擎后端 Flask API(第 2 部分)中进一步探讨这个问题。
参考文献
- https://medium . com/code-heroku/how-to-turn-your-machine-learning-scripts-into-projects-you-can-demo-CBC 5611 ca 442
- https://real python . com/build-re commendation-engine-collaborative-filtering/
在拥抱脸中使用数据加载器
PyTorch 版本
每一个深入 DL 世界的人都可能听说,相信,或者是被试图说服的目标,这是一个 变形金刚 的时代。自从第一次出现,变形金刚就成了几个方向大量研究的对象:
- 研究人员寻找建筑的改进。
- 人们研究管理这个领域的理论。
- 搜索可能使用此方法的应用程序。
那些旨在深入研究变形金刚的读者可能会找到大量详细讨论变形金刚的资源。简而言之,转换器通常用于为 NLP 问题开发语言模型。这些模型用于构建句子、Q & A 和翻译等任务。在一个非常高级的描述中,转换器可以被认为是复杂的自动编码器,它接收键、值和查询(单词)三元组作为输入,并研究一个语言模型,其中每个单词都有一个依赖于其语义上下文的特定表示。
伯特&拥抱脸
BERT ( 来自变压器的双向编码器表示)在此引入。随着变形金刚的出现, BERT 的想法是采用变形金刚预先训练好的模型,并根据特定任务对这些模型的权重进行微调(下游任务)。这种方法产生了一类新的 NLP 问题,可以通过最初使用转换器来解决,例如分类问题(例如情感分析)。这是通过将网络的上层修改成集群的结构或不同类型的序列来实现的。因此,我们有许多伯特模型。这样一个伟大的“模特银行”就是抱脸。该框架提供了一个包含三个基本组件的包:
- 各种预先训练的模型和工具
- 令牌化引擎
- 框架灵活性(例如 Torch、Keras)
这个软件包可以处理大量的 NLP 任务。
那我为什么要写帖子呢?
当我开始使用拥抱脸时,我对它提供的优秀的“端到端”管道以及它提供的数据结构的便利性印象深刻。然而,我觉得他们的教程中有一部分没有很好地涵盖。在我自己设法找到解决方案后,我觉得作为一个“激进的开源”我必须分享它。
为了说明这个问题,我将简要描述拥抱脸提供的特征提取机制。我们给出的数据很简单:文档和标签。
最基本的函数是记号赋予器:
**from** transformers **import** AutoTokenizer
tokens = tokenizer.batch_encode_plus(documents )
这个过程将文档映射成变形金刚的标准表示,因此可以直接用于拥抱脸的模型。这里我们提出一个通用的特征提取过程:
**def** regular_procedure(tokenizer, documents , labels ):
tokens = tokenizer.batch_encode_plus(documents )
features=[InputFeatures(label=labels[j], **{key: tokens[key][j]
**for** key **in** tokens.keys()}) **for** j **in** range(len(documents ))]
**return** features
该方法的输出列表:特性是一个可以用于训练和评估过程的列表。我发现的一个障碍是缺乏使用 Dataloader 的教程。
在所有教程中,假设我们使用训练/评估期间可用的数据。这个假设对于新兵训练营的需求来说是明确的,但是对于现实世界的任务来说是错误的。我们正在处理大数据:
在大数据中,代码指向数据,而不是数据指向代码
我开始尝试。我的目标是创建一个能够用 PyTorch 数据加载器访问的特性文件夹。我的第一次尝试如下:
**def** generate_files_no_tensor(tokenizer, documents, labels ):
tokens = tokenizer.batch_encode_plus(documents )
file_pref =**"my_file_"
for** j **in** range(len(documents) ):
inputs = {k: tokens[k][j] **for** k **in** tokens}
feature = InputFeatures(label=labels[j], **inputs)
file_name = file_pref +**"_"**+str(j)+**".npy"** np.save(file_name, np.array(feature))
**return**
这段代码运行得很好,但不是最佳的。它的主要缺点是节省了 numpy 个对象,而抱紧的模型需要个张量。这意味着我的 getitem 函数将有额外的任务,但上传文件:
**def** __getitemnumpy__(self, idx):
aa = np.load(self.list_of_files[idx], allow_pickle=**True**)
cc = aa.data.obj.tolist()
c1 = cc.input_ids
c2 = cc.attention_mask
c3 = cc.label
**return** torch.tensor(c1), torch.tensor(c2), c3
在训练过程中,我们需要将数量级的对象转换成张量。
我决定以一种不同的方式工作:我开发了我的 getitem 并强迫数据“承认它的规则”。
**def** __getitem__(self, idx):
aa = torch.load(self.list_of_files[idx])
**return** aa[0], aa[1], aa[2]
现在让我们面对挑战。让我们试试这个:
**def** generate_files_no_tensor(tokenizer, documents, labels ):
tokens = tokenizer.batch_encode_plus(documents )
file_pref =**"my_file_"
for** j **in** range(len(documents) ):
inputs = {k: tokens[k][j] **for** k **in** tokens}
feature = InputFeatures(label=labels[j], **inputs)
file_name = file_pref +**"_"**+str(j)+**".pt"** torch.save(file_name, feature)
**return**
有用!我们甚至可以直接接触到张量。但是这个循环非常慢。当我观察这些数据时,我看到了两个“现象”:
- 所有文件都有相同的大小
- 文件很大!!
我花了一段时间才意识到所有的文件都保存了整个张量(它们可能保存了一个指向它的位置的点)。因此,我们必须将它切片。
**def** generate_files_with_tensor(tokenizer, documents, labels ):
tokens = tokenizer.batch_encode_plus(doc0, return_tensors=**'pt'**)
file_pref =**"my_file_"
for** j **in** range(len(documents) ):
file_name = file_pref +**"_"**+str(j)+**".pt"** input_t = torch.squeeze(torch.index_select(tokens[**"input_ids"**],dim=0,
index=torch.tensor(j)))
input_m = torch.squeeze(torch.index_select(tokens[**"attention_mask"**],dim=0,
index=torch.tensor(j)))
torch.save([input_t, input_m, labels[j]], file_name)
**return**
index_select 函数对张量进行切片,挤压允许移除尺寸为 1 的尺寸。它达到了要求。现在我有一个快速的 getitem 除了上传数据什么也不做。
具有数据结构和数据加载器的代码示例存在于这里。
希望你会觉得有用。
使用人工智能模拟 TS-9 吉他踏板

装有 TS-9 的踏板(图片由作者提供)
开源,使用机器学习实时数字克隆 TS-9 吉他踏板(带视频演示)
A nalog 是吉他效果领域的王者,但数字建模已经走过了漫长的道路来复制它。详细的电路分析和数学方程可以重现许多这类设备的声音,从电子管放大器到过驱电路再到弹簧混响。通常需要电子和物理建模领域的专业知识。
但是,如果我们能跳过所有的数学和物理,直接去做我们真正想要的东西,美妙的声音,会怎么样呢?如果你可以说,我不在乎那些金属盒子里发生了什么,我只想要声音!这种方法被称为黑盒 建模,公开可用的人工智能框架使任何人都有可能做到。
作为一名工程师,我的一部分想大喊,“嘿,那是作弊!”你真的能跳过分析电路、建立元件模型和令人头疼的数学运算,直接把数据扔给计算机替你解决吗?答案是既肯定又否定。
人工智能采用传统的问题解决方式,并彻底改变了它。你仍然需要了解如何设置你的问题,但它的解决方式不同。找出最佳的模型架构和训练参数本身就是一门科学(或艺术),这一切都取决于良好的数据。在 TS-9 吉他踏板的情况下,数据以音频记录的形式出现。
通过记录踏板的输入和踏板的输出,你把它当作一个黑匣子。输入和输出是已知的,但我们不需要了解它是如何工作的。根据音频数据训练神经网络,使其表现与真实踏板相同。你甚至可以在你的吉他上实时运行这个模型,方法是围绕它构建一个高性能的音频应用程序,本质上是制作一个数字克隆。
注:你可以在这里 阅读更多关于我的数据收集方法 。
这对于在特定设置下采集踏板声音非常有用。在 TS-9 踏板的情况下,这可能看起来像 100%的驱动,50%的音调和 50%的水平。但是我们如何在一个模型中复制所有可能的旋钮位置呢?更多数据!

TS-9 踏板上的控件(图片由作者提供)
参数调节允许我们创建一个模型,它可以像真正的踏板一样调整。通过在每个旋钮的离散步骤中记录踏板的音频,您可以训练一个条件模型,该模型对旋钮或旋钮组的整个范围进行插值。
对于 TS-9,驱动和音调控制被选为条件参数。水平旋钮可以被包括在内,但它可以近似为实时音频插件中的一个简单的音量旋钮。这也会给培训增加更多的数据和复杂性。
对于训练数据,在 0%、25%、50%、75%和 100%旋钮位置进行 2 分钟采样。对于两个旋钮,这产生 5 * 5 = 25 种不同的组合,用于 25 个单独的 2 分钟录音加上基线输入音频文件。一旦录制了音频,每个音轨都被导出为 32 位浮点单声道 WAV 文件。音频数据的总大小约为 520MB。
自动化吉他弹模项目的吉他弹模叉用于训练。此分叉包含用于条件模型音频处理的额外代码。该项目用 python 编写,使用 Pytorch ,包含几个机器学习模型,用于模拟效果建模。LSTM(长短期记忆)模型用于 TS-9 踏板。
注意:处理后的输入 WAV 文件包含三个通道,一个用于音频,一个用于驱动旋钮参数(范围 0.0 到 1.0),一个用于色调旋钮参数(范围 0.0 到 1.0),作为 LSTM 模型的输入。输出 WAV 文件包含一个通道,即输出音频。

实时插件(作者图片)
JUCE 框架用于构建实时应用程序,命名为“TS-M1N3”(这是一个不好的双关语)。Windows/Mac 安装程序可以从 Github 和源代码下载,有 VST3 / AU / Standalone 格式。 RTNeural 用于 LSTM 模型的神经网络推理,并显著提高了速度。
注:关于使用 LSTM 神经网络创建实时音频应用程序的更多细节,请查看这些 文章 。
下面是一个视频演示,比较了不同设置下原始 TS-9 踏板和插件克隆:
(作者视频)
特别感谢 UAH(位于亨茨维尔的阿拉巴马大学)MLAMSK 高级设计团队,他们的研究和辛勤工作直接影响了本文呈现的结果。
我希望你喜欢这篇文章,感谢阅读!
使用 Python 后端
从零开始逐个任务地进行数据科学
用于 NLP 的 Python Flask 和 Vue.js

我正在写一个关于创建 NLP 应用程序来帮助人们改进简历的迷你系列文章。我在的第一篇文章中介绍了核心思想,在的最后一篇文章中,我展示了如何将标记应用于特定的关键词。在这篇文章中,我将向您展示如何连接 Python 后端,并为我在上一篇文章中使用的用户界面提供服务。我希望如果我能把这些任务分解成简短的帖子,我就能成功地解释我的过程。截至撰写本文时,我已经做了许多这样的应用,如 概念证明 。你准备好进入后端世界了吗?我们开始吧!
Python 后端
虽然有时我可能看起来是一个死忠的 Python 爱好者,但事实是 Python 只是我工具箱中的一个工具。我经常使用 Express with Node,这实际上涉及到安全性和各种外部服务的可用 SDK,如 Okta 。因为我只对 概念证明 感兴趣,所以我的生活没有那些不得不 保证数据隐私、安全、性能和弹性的人复杂。为什么我们选择 Python 作为后端语言归结起来,对于我来说,到 机器学习生态系统 。我从来不喜欢姜戈,但我知道它非常受欢迎。Flask 一直是我的首选,我从未遇到过性能问题。自然,你需要用 Nginx 和 Gunicorn 之类的用 Supervisor 来部署。

照片由 Hitesh Choudhary 在 Unsplash 上拍摄
用 Python 挂接一个单页面前端 UI 有几个步骤。首先,我们需要 Python 后端作业,然后我们需要对 Vue.js 应用程序进行一些更改。好了,我们编码吧!我现在用的是视觉代码!
瓶
让我们讨论一下 Flask 服务器端的工作
我的开发方法一直是极度敏捷的过程。我的重点是在每次迭代中运行软件。一次迭代可以是几分钟,也可以是几小时,但一般来说,有一个大约 3 小时的 时间框。 虽然可能不明显,我的 专业更侧重于的产品管理,而我更喜欢把专业人员留给真正的产品开发。
web.py 文件中的第 1 行到第 3 行处理库的导入。请注意,我们已经将 Gensim 纳入范围。第 5 行是这个脚本中最重要的一行。 Flask 期望一个静态文件夹 ,但是 Vue 和 NPM 构建时设置了一个 Dist 文件夹。 我们需要把这两个人撮合起来。
第 13 行为主页创建了一个路由,在这个早期阶段,它只为 index.HTML 服务,是从 NPM 的 build 命令生成的。第 19 行运行应用程序, 但是请不要在面向 web 的上下文中这样做! 留意图 1 所示的警示井。

图 1:带有警告可视代码中的终端视图。从未在生产中使用过
既然开发 web 服务器已经启动并运行,让我们把注意力转向调整 Vue 前端接口。
某视频剪辑软件
如果你只是在中场休息时才收看比赛,你可以通过阅读前一篇文章来赶上比赛,那会告诉你我们是如何来到这里的!如果你允许类比团队 数据科学原型 在这个区间都涨了 2!

康斯坦丁·希莫年科在 Unsplash 上拍摄的照片
回顾是敏捷的一个重要组成部分,它是一个很有意义的仪式!一般我们都是坐着而不是站着!你记得吗?我告诉过你 Flask 需要一个静态文件夹,但是 NPM build 设置了一个 Dist 文件夹。我们需要将这两者结合起来,我们通过为 Vue 引入一个配置文件来做到这一点。
第 2 行 vue.config.js 定义了构建输出文件夹。第 5 行创建了 Flask 需要的静态文件夹。这是一个关键的步骤,如果忽略,我们将给出 404 错误。

现在你需要记住,我们正在处理两种不同的技术。
- Python 是运行时解释的。
- NPM 需要使用 transpilers、webpack 和 voodoo 来构建,这很无聊,但很有必要。网络浏览器的兼容性比永恒恶臭的沼泽还要糟糕。
所以底线是
- Git 添加。(展示变化)
- Git commit -m(在变更对象中包装变更)
- Git push(更新对 repo 的更改)
- NPM 跑搭( 跑巫毒和闻泥塘 )
- Python backend.py ( 执行服务端 )
必须是你的过程,当然也是我的过程。所以现在我们准备好执行连接的流程。
运行它
对于这一部分,剩下的工作就是运行 Python 脚本。我已经完成了构建并检查了输出。图 2 显示了结果。

图 2:导航到端口 5006,我们得到显示的应用程序。图片作者。
除了我们在端口 5006 而不是图 3 所示的 Vue 开发服务器的 8080 端口之外,真的没有什么新的东西可看。

图 3: npm run serve 在端口 8080 上创建一个服务。图片由作者提供。
但是,我现在已经向您展示了如何创建前端应用程序和后端服务器端作业。Gensim 已经包括在内,我有自己的 NLP 代码从最初的职位。在下一篇文章中,我们将继续这一旅程。
- 给前端添加一个更好的用户界面。
- 添加一些前端 API 调用来处理简历和职位描述。
- 添加一些服务器端 API 端点。
现在我们开始有趣的事情。如果你在问自己这与数据科学有什么关系,你应该接受用 Python 构建 NLP 模型是不够的。你需要一个用户界面,你需要资金,你需要说服其他人投资他们的现金和他们的家庭时间在你的项目上。最后,如果您是项目经理、产品经理或产品所有者,您需要构建 backlog 并交付项目。您需要了解将 NLP 解决方案带给消费者的所有步骤。与其说这是科学,不如说这是编码、营销、设计、隐私、安全和大量艰苦工作的现实生活。
回来获取下一期,我会添加那些新特性!同时,如果你对开发过程感兴趣,想看我演示如何从零开始制作产品的视频,你可能会喜欢我的 YouTube 系列。构建 web 服务、API 调用、用户界面都有详细的讨论。**
在神经网络中使用激活函数
机器学习|神经网络|激活函数|
常见的激活函数及其使用
激活函数是将输入信号转换成输出信号的神经元的内部状态。
基本上,神经元计算其输入的加权和,加上偏差,然后将值输入到激活函数,该函数决定它是否应该吐出输出。激活函数为神经网络提供非线性属性。如果没有激活函数,神经元的输出值可以在(-无穷大)到(+无穷大)之间变化。
我们都知道特性缩放以及为什么要这样做。特征缩放是在数据预处理期间执行的,并且是为了归一化/标准化独立特征。在标准化过程中,我们在 0 到 1 的范围内重新调整特征值。激活函数也在做类似的事情。它将输出值带到特定的范围。
在这里阅读更多关于特征缩放的内容:https://becoming human . ai/feature-scaling-in-machine-learning-20 DD 93 bb 1 BCB
让我们用一个小例子来理解激活函数。
一群 4 个朋友决定去度假。他们已将选择范围缩小到三个国家。泰国、牙买加和迪拜。为了选择最终的目的地,他们每个人都决定在-10 到 10 的范围内为这些国家投票。-10 表示最不感兴趣,10 表示最感兴趣。0 可以认为是中性的。
从下表中,你可以注意到,例如,Bkon 对泰国最感兴趣,对访问牙买加最不感兴趣。

作者创建的虚构数据
让我们给每个变量分配初始随机权重。为了简化计算,我们将保持偏差值= 0.01 或简单地为 0。
观察下图:神经元计算其输入的加权和,并提供输出值。这里的神经元在没有激活功能的情况下将为泰国、牙买加和迪拜各提供 1 个值。即泰国= -2,牙买加= 5,迪拜= 8。由于这不是一个回归问题,这些值将很难解释。

作者创建的图像
不同的激活功能及其输出:
阶跃函数 —该激活函数可用于二元分类。它产生二进制输出 0 或 1。任何低于 0 的值都自动被赋予值 0,任何高于 0 的值都被赋予值 1。
Sigmoid/逻辑函数 —该激活函数可用于二元和多标签分类,我们将输出预测为概率。这个团队去迪拜的可能性有多大?该函数将任何实数值作为输入,并输出 0 到 1 范围内的值。输入越正,输出值就越接近 1,反之,如果输入越负,输出就越接近 0。
Softmax 函数 —当我们将输出预测为概率并希望得到明确答案时,该激活函数可用于多类分类。在 softmax 中,所有概率相加为 1,选择概率最高的类。在我们的例子中,迪拜将是明显的赢家。
在 Sigmoid 函数中,输出的概率值加起来不是 1,而在 Softmax 函数中,概率加起来是 1,因此给出了最终的赢家。
双曲正切函数 —该激活函数类似于 sigmoid 函数。然而,如果两个事件略有不同,它们都将被赋予高概率。该函数将任何实数值作为输入,并输出-1 到 1 范围内的值。输入越正,输出值就越接近 1,反之,如果输入越负,输出就越接近-1。这主要用于递归神经网络(RNNs)。
ReLU 函数 —该激活函数主要用于多层感知器(MLPs)和卷积神经网络(CNN)。任何低于 0 的输入值都被赋予 0 值,任何高于 0 的值都是值本身。因此,它的范围可以从 0 到无穷大。
当不确定为你的神经网络使用哪种激活函数时,最好尝试几种并比较结果。
感谢您的阅读。如果您有更多问题,请通过链接联系我们
使用人工智能生成复杂的和声
在巴赫合唱数据集上训练

范二·阿夫沙里在 Unsplash 上的照片

照片由 Lorenzo Spoleti 在 Unsplash 上拍摄
创造性内容是计算机复制的最复杂的东西:非常低的损失将意味着网络的输出不是原创的,而非常高的损失将意味着网络的输出与源材料没有任何相似之处。因此,需要大量的超参数调整来获得有效的结果。
概念:
巴赫合唱数据集是一个广泛的数据集,包含了巴赫合唱的 381 个音符值。每首赞美诗都被分解成一个 CSV 文件,有 4 列。每列包含合唱中 4 种声部(女高音、女低音、男高音和男低音)的音符值。
你可以在这里找到数据集。
我的目标是将这些数据转换成空间数据,使网络能够链接过去的笔记,为下一个笔记做出决定。我最初的印象是将文件转换成图像,因为图像适用于许多不同类型的网络。
网络将得到损坏的数据(具有丢失信息的数据),并试图填补空白。
数据收集:
该脚本导入运行程序所需的所有库。Numpy 用于矩阵操作,PIL 用于图像,pickle 用于在本地文件中存储列表值,以便将来节省时间。Array2img 是一个特殊的函数,用于将巴赫合唱团数组转换为图像。它采用空白图像(全黑),如果有注释,则添加白色像素。
这个脚本查看一个目录中的所有文件。使用 os.chdir()移动到存储所有数据的目录。这些值被拆分成 106 乘 106 的数组。这是因为 midi 文件中的音符范围是 106,正方形图像比矩形图像更容易处理。这是因为在卷积网络中更容易根据内核大小来处理。这些数据将被保存起来以备将来使用。
这个脚本将 csv 文件转换成一个数组,然后该数组可以转换成一个图像。再加上另一个将图像文件转换成 midi 文件的脚本,这应该可以完美地工作了,对吗?
问题 1:转型
我对将创建的图像转换为 img2midi 函数的输入感到非常困惑。
最终,经过几个小时的测试,我发现将图像逆时针旋转 90 度可以得到最好的结果。
这是一个巴赫合唱曲的形象例子:

由作者创建
问题 2:音高
即使在完成项目后,我也不确定是什么导致了这个问题。函数输出的音符都是错的。当打印计算机使用的值时,它与 csv 文件中的值不匹配。
我不得不重新编写 img2midi 脚本,以便可以将 csv 中的值直接输入到函数中。
这个脚本的原始代码不是我的。这里可以找到。
我将 csv 中的注释直接输入到图像中。笔记是一个名为“notesy”的列表。
通过直接输入值而不是使用图像,它防止了错误音高的发生。
使用另一个 midi2img,我将 midi 转换回图像,然后比较这两个图像。
我观察到了不同之处,并对 array2img 代码做了一些调整。
在经历了许多图像和 midi 文件的困难之后,我决定只使用 midi 音符本身作为网络的输入和输出。从长远来看,这是可行的,因为图像包含大量冗余值。4 声道输入也将导致网络输出一个 4 部分的和声,这正是我在寻找的。
数据综合:
随机损坏:
想到的第一个想法是随机移除某些音符。这个 scr
损坏系数用于计算将从合唱中随机移除的值的数量。这是用合唱中的音符数乘以系数计算出来的。
经过一点点测试后,这种方法的效果并不太好,因为数据集中定义的时间步长非常短,所以脚本只是将笔记分成更多的片段。这对于模型来说太容易外推,因此是无用的数据。
以下是该算法生成的数据示例:


作者创建的两个图像(左:原始,右:损坏)
逐块:
这个脚本使用不同的策略来实现这一点。它创建定义大小的块,并垂直清除合唱。这意味着合唱团的所有 4 个声部的音符在合唱团的一小段中被移除。
不是知道损坏纸币的确切数量,而是使用概率来确定图像损坏的程度。
左边的要点还提供了一个脚本来生成一个具有定义数量的数据点的数据集。
以下是该算法造成的损坏:


作者创建的两个图像(左:原始,右:损坏)
逐行:
此脚本删除了合唱中除低音线以外的所有声部。要保留的这条线可以用 line_n 变量来确定。因为 python 使用基于 0 的索引,所以 0 表示高音,3 表示低音。
以下是使用该算法生成的图像示例:


作者创建的两个图像(左:原始,右:损坏)
经过一些测试后,我认为定义数据的最佳方式是将其表示为大小为(106,4)的数组。每个声部将有 4 个通道,合唱部分将被分割成 106 个时间步长的片段。
模型
假设您已经在当前工作目录中保存了所有的 pickled 文件,左边的代码将准备所有基于块的数据。
它还有另外两个有趣的功能:
填写区块:
填充块接收网络的输出,并移除模型输出的所有重叠,以创建完整的模型输出。这可以通过原始输出获得更多信息。
数组 2 图像分析:
Array2img 分析允许将 array2img 与像素颜色一起转换。这样就很容易分辨出哪些笔记是电脑生成的。
这个基线模型只使用密集模型和 relu 激活函数。它只使用密集层的事实使得它很难学习更多的空间模式。虽然这通常会导致收敛速度变慢,但在这种情况下,几乎没有学习。
以下是结果示例:



作者的所有图片(左:已填充,中:真实,右:未过滤)
该模型类似于基线模型,但增加了漏失层。我认为基线模型产生的大多数平线是由于最佳预测是相同的 4 个音符。因此,这意味着漏失层将导致更大数量的变化,从而获得更好的结果。
辍学可以得到更多的控制,但这仅仅是一个概念的证明。
这是我最初打算使用的第一个包含一维卷积层的模型。
这是一个基本的卷积网络,有一些基本的超参数调整。kernel_size 经过了很好的优化:4 非常适合通道数,并且比其他值收敛得更快。
在研究最佳模型架构时,我发现 GANs 的想法是另一种有效的模型架构。
这个项目最理想的 GAN 是 Pix2Pix GAN。问题是这将需要很长时间来编码,我决定我不想在这个项目上花太多时间,并决定放弃这个想法。
然而,我设法创建了一个类似于 Pix2Pix GAN 中发生器的编码器-解码器设置。
编码器减小输入的维度,直到它具有相同图像的较小表示。然后,解码器将图像放大,填补这个过程中的漏洞。
此外,编码器-解码器模块之间存在跳跃连接,允许信息从不同的处理级别传递。
以下是结果示例:



作者的所有图片(左:已填充,中:真实,右:未过滤)
结论:
我认为这个项目基本上是成功的。然而,在这个项目中使用 Pix2Pix GAN 会非常有趣:
Pix2Pix GAN 将能够产生更多原创和有趣的结果,因为发电机损耗不仅仅是地面真实值和发电机输出之间的 MSE。从理论上讲,这将产生真正原创的结果。
我希望在不久的将来为这个项目使用 Pix2Pix GAN。
我的链接:
如果你想看更多我的内容,点击这个 链接 。
用 AI?你可能希望开始进行人权影响评估(HRIA)
公平和偏见
欧洲人工智能监管目标之争的教训

在最近的一篇文章中,我提供了一些关于拜登政府将如何改变人工智能领域的预测。主要的收获是,人工智能监管的到来比你想象的要快,你最好从实施内部人工智能治理开始准备。如果你在北美开展业务,这将有助于你超越监管机构和竞争对手。如果你在欧洲做生意,你需要遵守欧洲法律。
算法影响评估 (AIAs)和像人工智能注册这样的工具是开始记录你的人工智能的简单方法。然而,鉴于最近的发展,你可能需要在你的人工智能治理工具箱中添加另一个工具:人权影响评估(HRIA)。为什么?让我们看看欧洲围绕人工智能监管的激烈辩论,看看我们可以从中吸取什么教训。其中一些可能很快就会适用于我们,因为美国的方法与欧洲正在采取的方法有相似之处。
这两种方法都倾向于有选择的、有针对性的监管,关注特定的行业,而不是“一刀切”。两人都认为技术应用的目的、背景和范围很重要。例如,NIST 信息技术实验室主任 Elham Tabassi 说,高度争议的面部识别如果用于解锁汽车,可能不会有高风险。这真的取决于用例。
AI 监管:基于风险还是基于价值观?
欧洲委员会关于人工智能的白皮书引发了欧洲的辩论。该文件于 2020 年 2 月进入公众咨询,通常被理解为即将到来的欧洲人工智能立法提案的基础。(该提案有望在 Q1 2021 年提出)。这份文件得到了很多回应,包括来自 60 多个民间社会组织的一封高度批评的公开信 。
争论的核心是人工智能监管应该基于风险还是基于权利的问题,以及鉴于迄今为止记录的许多侵权行为,基于风险的方法是否足以保护人权。
委员会认为,基于风险的方法将确保监管干预是相称的、平衡的和有重点的。这种方法将保护公民和消费者,而不会给部署人工智能/人工智能的组织带来不必要的负担,也不会扼杀这一领域的创新。这种方法也将有助于立法者和监督机构关注最有可能发生危害的领域。(这可能会帮助他们更好地管理资源。)
上周,该委员会人工智能和数字产业主管 Lucilla Sioli 重申了该委员会的意图。她指出,委员会希望建立一个“繁荣的人工智能市场”。她在活动上发言“公正的人工智能转变:机会和威胁在哪里?”,由专门研究欧盟政策的独立泛欧媒体网络 EURACTIV 举办。
委员会的白皮书提出了两个标准来确定人工智能系统是否是高风险的:
1)如果它“被用于预计会发生[…]重大风险的部门[…],例如保健部门;运输;能源和部分公共部门。”
2)“此外,有关部门人工智能应用的使用方式可能会产生重大风险。”
委员会还指出,一些应用程序可能被认为是高风险的,即使它们不符合上述标准。例如,在招聘中使用人工智能是合格的,还有“远程生物识别和其他侵入性监控技术”。你可能会问,这种方法有什么问题?

(人工智能)法则的目的是什么?
第一个争论点是围绕人工智能/人工智能的法律的目的:它是建立标准,维持秩序,解决争端,保护自由和权利,还是促进人工智能的吸收“同时解决与这种新技术的某些使用相关的风险”?委员会的白皮书指出,它有两个目标——委员会“致力于实现科学突破,保持欧盟的技术领先地位,并确保新技术为所有欧洲人服务——改善他们的生活,同时尊重他们的权利。”
作为回应,Access Now 写道“任何技术的采用,尤其是在公共部门,都不应该是一个独立的目标,它本身没有价值”。 Access Now 是一家专注于捍卫数字权利的非营利组织,也是上述公开信的签名者之一。在接下来的文章中,Access Now 写道,“人工智能并不是所有情况下的正确解决方案,也不应被视为灵丹妙药”。
这也是我向 InfoTech 的许多客户提供的建议:并非所有的商业挑战都可以或应该用人工智能来解决。人工智能只是你商业工具包中的一个工具,它不应该取代其他方法(包括一些低技术含量的方法)。
Access Now 表示,欧洲监管机构和立法者不应该强调人工智能的采用,而是应该“通过降低风险来确保技术值得信赖”。[……]欧盟应该通过将保护基本权利置于对人工智能全球竞争力的担忧之前,赢得人们对其人工智能举措的信任。首要目标应该是避免对个人和社会的伤害,而不是减轻伤害。”(同上)
这就引出了下一个争论点。
人工智能监管应该是被动的还是主动的?
除了现有的立法,该委员会还概述了几项要求,希望为高风险的人工智能应用开发这些要求。(这些要求可以作为标准进一步充实。)它们的范围包括确保 AI/ML 系统中使用的训练数据符合欧盟安全规则,披露系统的能力和限制,以及通过人类监督保护人类自主权。
该提案的批评者说,所有这些都是好的,非常需要,但还不够。基于风险的方法会给解释和公司法律操作留下太多空间。与此同时,自动化决策系统已经在伤害人民、社区和环境,而且它们也在侵犯基本人权。然而,这些权利是不可谈判的,无论与外部因素相关的风险水平如何,它们都必须得到尊重。 (原强调)
上面的公开信敦促委员会建立“明确的监管红线,以防止侵犯基本权利的人工智能的使用”。信中指出“即将出台的监管提案在法律中明确限制人工智能的合法使用,这一点至关重要”(原文强调)。换句话说,某些人工智能用例应该被完全禁止,或者因为与民主社会不相容而受到法律限制。具体来说:
生物特征监控
预测性警务
刑事司法中的人工智能,例如风险评估工具
移民和边境管制
人工智能用于社会评分和决定获得社会权利和福利(如福利、教育和就业)的系统。
公开信还呼吁委员会“明确处理”人工智能的使用:
加剧现有的结构性歧视;
限制获得医疗保健、社会保障和其他基本服务;
允许监视工人并侵犯他们的权利;
促进对公众舆论和人类行为的大规模操纵(例如,“轻推”、深度造假)以及“对人类尊严、机构和集体民主的相关威胁。”

图片:联合国大会,维基百科
人权快速复习
根据联合国人权事务高级专员办公室的说法,“人权是我们作为人类而拥有的权利——它们不是任何国家赋予的。这些普世权利是我们所有人固有的权利,不分国籍、性别、民族或种族、肤色、宗教、语言或任何其他身份。这些权利从最基本的权利——生命权——到使生命值得活下去的权利,如食物权、教育权、工作权、健康权和自由权。”换言之,人权是普遍的、不可剥夺的、不可分割的和相互依存的:
普遍性:它们适用于每一个人;我们都平等地享有我们的所有人权。
不可剥夺的:任何人都不能剥夺个人的人权,“除非在特定情况下并根据正当程序”——例如,“如果一个人被法院判定有罪”。
不可分割和相互依存:所有人权地位平等;“一套权利离开另一套就无法充分享受”;侵犯一项权利可能会对其他权利产生负面影响。
人权被编入 1948 年联合国大会一致通过的《世界人权宣言》( UDHR ),以及随后的文件中,这些文件共同构成了《国际人权法案》。
人权和 AI 有什么关系?
如果你没有关注关于人工智能和自动决策系统造成的许多滥用和伤害的新闻,这封公开信和 Access Now 的两篇文章(上文引用)是一个很好的起点。
或者你可以看看大赦国际2019 年的报告《监控巨头:谷歌和脸书的商业模式如何威胁人权》。这份报告简明扼要地解释了谷歌和脸书无孔不入的监控机器是如何侵犯核心人权的,比如尊严权、自主权和隐私权;控制自己信息的权利;以及拥有自由表达自我的空间的权利。
如果你想长时间阅读,我推荐以下几本书:
凯茜·奥尼尔的《摧毁数学的武器》
弗吉尼亚·尤班克斯的《自动化不平等》
卡罗琳·克里亚多·佩雷斯的《隐形女人》
詹妮弗·埃伯哈特的《偏见》
弗兰克·帕斯奎尔的《黑箱社会》
《监视资本主义的时代》作者肖莎娜·祖博夫
还有很多视频。一个是凯特·克劳福德的 NIPS 2017 主题演讲“偏见的麻烦”。另一个是 YouTube 上这个搞笑的 3 分钟喜剧小品“苏格兰语音识别电梯——11 点!”,这说明了有偏见的语音识别系统是如何惩罚甚至是母语人士的。

图片:Getty
人工智能技术并不是第一个引发伦理担忧的技术
我想重温一下 Access 的断言“我们的权利是不可谈判的,无论风险有多大,都必须得到尊重”。除了人工智能的军事应用(这是一个值得拥有自己空间的话题),我们需要确保我们正在建设造福全人类的系统。这个目标需要小心地处理一些技术和用例,甚至可能禁止它们。例如,克隆人类在 70 个国家被禁止,理由很充分。尽管如此,克隆其他物种是允许的——包括宠物和家畜的商业克隆。克隆濒危物种实际上可能会确保它们的生存。
我们可能还需要为人工智能时代增加新的人权,例如:
知情权:了解个人数据的使用方式、技术的发展方式及其对个人、社区、社会和环境的影响;
选择退出的权利:可以选择使用低技术途径(在可能的情况下)或与人类互动,但仍然能够有意义地参与经济和社会;
与自动决策系统打交道时的补救权;
数据代理权;
· …
这些问题对您的组织意味着什么?
立即获取组织认为“希望开发或部署人工智能系统的实体[应该]承担举证责任,通过强制性人权影响评估证明其没有侵犯人权(HRIA)。这一要求将适用于所有领域的所有应用,作为更广泛的尽职调查框架的一部分,它应适用于公共和私营部门。”
监管机构是否同意这一提议仍有待观察。无论如何,我支持 Access Now 和签署公开信的 60 个组织——人权影响评估是构建道德、安全和负责任的人工智能的基础。此外,根据您业务的性质和您经营的地区,您可能已经在进行这样的评估。将它们扩展到你的 AI/ML 项目应该是很自然的下一步。
不知道如何开始使用人权影响评估?—参考丹麦人权研究所的这份优秀指南。这里是雀巢公司评估其商业活动中人权影响的经验。
人权影响评估将帮助你让你的人工智能和你的组织免受牢狱之灾(转述詹姆斯·泰勒在预测分析世界 2020 大会上的专家小组)。所以你晚上可能会睡得更好。他们还将帮助您的组织建立竞争优势,促进信任,这是企业和社会的基础。
在 F#中使用 ActiveX 控件
F#和 COM
我想在我的 F#中添加一个对遗留 ActiveX 控件的引用。NET Framework)项目。问题是,当它添加引用时,它是以一种非常基本且无益的方式添加的。我在 StackOverflow 上发帖,看看是否有人有解决方案,但建议的东西都不起作用:
https://stackoverflow.com/questions/67933379/how-to-access-com-variant-properties-with-f
最后,我自己解决了这个问题,这篇文章是关于我是如何解决的。花了不少功夫,但是键用的是 Visual Studio 2017(完全支持 F#。NET Framework)并利用 C#作为临时过渡。我写这篇文章是为了防止其他人发现自己处于同样的情况,并想知道如何解决它。对我来说也是个参考:)
F#控制台应用程序
创建一个 F#控制台应用程序。NET Framework)在 Visual Studio 2017:

F#控制台应用程序(。NET 框架)
添加一个 C#类库(。NET Framework)添加到解决方案中,并将其命名为 DummyLib :

C#类库(。NET 框架)
右键单击 DummyLib 的引用,并选择添加一个引用。然后选择一个 COM 引用。您应该会看到一个包含 ActiveX 控件的 COM 对象列表。为了演示起见,请选择微软的 ActiveMovie 控件(在现实世界中,您可以添加您想要的 ActiveX 控件):

添加对 Microsoft ActiveMovie 控件的引用
一旦你点击“确定”按钮,它将添加 COM 引用,并且它将被列在“引用”树下。如果您现在双击引用中的一个视图,这将打开对象浏览器窗口。展开互操作。AMovie 对象,您将看到 AMovie 名称空间。展开它,您会看到 ActiveMovieClass。如果选择,您将看到所有方法、属性和事件:

对象浏览器中的 ActiveMovieClass
如果您现在右键单击 DummyLib 项目并选择卸载该项目,那么您可以再次右键单击它并编辑它:

编辑 DummLib.csproj 文件
向下滚动到定义 COMReference 的 ItemGroup 元素,并将其复制到安全的地方:

COMReference 部分
现在需要卸载 AxLegacy 控制台应用程序,然后以同样的方式编辑 FSPROJ 文件。向下滚动到文件底部,将复制的部分插入到最后一条注释的正上方:

AxLegacy.fsproj 中插入的部分
右键单击两个项目文件并重新加载它们。我们快到了!
构建解决方案
如果你现在查看 AxLegacy 项目的 Obj 输出文件夹,你会看到 tlbimp 工具已经创建了引用 COM 库的Interop.AMovie.dll文件。我们需要确保将它复制到我们的输出文件夹中,我们确保这一点的方法是将其添加为后期构建步骤:

构建后副本
那一行写着:
xcopy /Y /F $(ProjectDir)obj\*.dll $(TargetDir)
这应该适用于以这种方式包含的任何 COM 对象。
现在可以安全地从解决方案中移除 DummyLib 了,请继续操作。
测试
为了查看我们是否可以正确地访问我们的 COM 对象,我们将在控制台应用程序中执行如下操作:
结论
在 Visual Studio 2019 中,做一些本应开箱即用的事情需要做很多工作!我希望当我们得到 Visual Studio 2022 时,它也能支持 F#。NET 框架应用程序。
资源
您可以从我的 GitHub 下载我在这里创建的示例应用程序:
https://github.com/ianormy/AXLegacy
2021 年用 iPad 做数据科学!

图片由装有 Juno 应用程序的 iPad 的作者拍摄
查看一些应用程序,以支持您在 iPad 上学习数据科学
一、爱情。我的 iPad。自从它在 2010 年推出以来,我不得不有一个。那时,我还是一个贫穷的大学生,所以我不得不省下一大笔钱来买我的第一个。我不后悔得到它,即使你真的不能做太多。
在我看来,iPad 无疑是最好的消费机器。无论是在网飞看你最喜欢的节目还是浏览网页,iPad 都是最好的个人大屏幕体验,而不是一台成熟的笔记本电脑。但不可否认的是,iPad 一直缺乏让它成为真正好的生产机器的东西。
随着时间的推移,苹果慢慢增加了越来越多的功能,使 iPad 越来越像一台生产机器。从增加键盘支持到允许用 Apple Pencil 进行非常好的图形设计体验,iPad 已经越来越接近真正的笔记本电脑替代品。事实上,在我 2018 年成为数据科学学生之前,有一段时间我完全抛弃了我的笔记本电脑,只使用 iPad。当然,我很快不得不在 2019 年初给自己买了一台新的笔记本电脑,因为很明显,我将需要它来进行数据科学。
但现在是 2021 年 1 月,苹果公司继续大步前进,让 iPad 更像一台生产机器。随着 iOS 14 突破性地支持在最新的 iPad 键盘上使用光标在触控板上烘焙,我现在很好奇 iPad 作为数据科学机器的可行性如何。在这篇文章的剩余部分,我们将看到我们如何在数据科学领域利用 iPad,并对这种体验的有用性做出最终判断。
事不宜迟,我们开始吧!
(哦,还有一件事……我 100%知道,如果你使用 AWS 或 GCP 这样的云服务,你肯定可以把 iPad 当作生产机器来使用,但这感觉像是作弊。这篇文章将涵盖 iPad 的硬件、软件和应用程序,以获得不依赖云计算的体验。)
硬件

作者拍摄的图像
我认为可以有把握地说,过去两年发布的任何 iPad 都有足够强的性能,可以与大多数普通笔记本电脑相媲美。我们将在我们的应用程序中进一步探讨这一点,但使用应用程序从 CSV 加载数据集并使用 Pandas 探索它们是一种非常爽快的体验。我个人是一名 16 英寸的 MacBook Pro,用于我作为机器学习工程师的日常工作,对于用 Python 工作,我要说我的 iPad Pro 和全 MacBook Pro 一样快。
但是我们不能忽视巨大的缺点:iPad 本身就是一个大触摸屏。使用虚拟键盘和使用手指作为光标是一种非常笨拙的体验。(更不用说虚拟键盘占用了一吨宝贵的屏幕空间。)iPad 增加对物理键盘的支持已经有一段时间了,这非常有帮助。2020 年夏天,苹果在 iOS 14 中正式添加了光标支持,以使用支持蓝牙的鼠标等。同样,这是将 iPad 用作生产机器的又一次巨大飞跃,但是这些额外的东西并不便宜。
如果你有一个普通的 iPad,并且没有兴趣购买这些额外的配件,那么你将很难在你的 iPad 上进行数据科学活动。可能吗?从技术上来说,是的,但是这种体验太糟糕了,我宁愿用低端笔记本电脑来工作。
软件

由 Medium、Pythonista 和 PIP 视频的作者拍摄的截图
尽管苹果在让 iOS 对生产客户更具吸引力方面取得了长足进步(甚至最近将 iPad 上的 iOS 重新命名为 iPadOS),但它仍然是 iOS 的核心。它最终限制了我在普通笔记本电脑上做的一些事情,不幸的是,我永远无法真正用 iPad 取代我的工作笔记本电脑。也就是说,我不能使用命令行和命令行工具、构建 Docker 映像等等。
但这并不意味着我们完全不知所措。我们仍然可以做很多很酷的事情,我们将在应用程序部分介绍,但停留在 iOS 的基础水平,有一些事情值得注意。首先是 iOS 文件系统。这与我们对传统笔记本电脑的预期完全不同,但至少苹果在短短几年前就加入了他们自己的变体。这很棒,因为许多应用程序现在可以在所有应用程序中使用相同的文件,就像你在传统计算机上使用任何文件一样。此外,苹果的 iCloud Drive 使您在所有苹果设备之间同步文件变得轻而易举。(当然,如果您处理的是非常大的文件,那么最后一个会有所增加。)而且我也有责任提一下并行应用的非常好的用户界面!
苹果做出的这些重大改进值得称赞,但归根结底,如果没有好的应用,这些改进没有多大意义。让我们探索一下截至 2021 年初,这些应用包括哪些内容。
应用程序

作者捕获的屏幕截图
整个 iOS 生态系统的面包和黄油在于它的应用程序商店,幸运的是,一些伟大的人已经推出了一些伟大的应用程序,以帮助整个代码开发和数据科学过程。我们甚至会谈到这些应用程序是如何协同工作的,甚至会谈到你在 GitHub 上的工作,但我必须做到 100%透明:这不是一个简单的过程。你最终会像我一样习惯它,但它肯定不会像使用传统电脑那样无缝。
让我们进入我发现对数据科学有很大价值的应用程序。
iOS 版 Juno

《iOS 版朱诺》作者截图
(价格:$14.99,App Store 链接 )
不用说,使用 Jupyter 进行探索性数据分析和数据可视化是一项巨大的资产,而 Juno 带来的正是这一点。Juno 是一个允许你运行 Jupyter 笔记本界面的应用程序,它可以与 Python 内核甚至一些包如 Pandas 和 Scikit-Learn 一起工作。如前所述,Juno 等应用程序现在可以直接与 iOS 文件系统交互,因此您可以轻松地从 Kaggle 等网站下载数据集,并直接在 Juno 中使用它。不错!
不幸的是,Juno 并非没有缺点。它建立在 iOS 之上,不能运行传统的 shell 命令。此外,虽然您可以从 PyPi 安装一些额外的包,但它们必须完全用 Python 编写。如果它们不是纯用 Python 编写的,那么它们就不能下载到 Juno。(例如,对于模型透明度,我可以下载石灰,但不能下载 SHAP。去想想。)Juno 在导航文件系统时也可能有点笨拙,但最终,我认为 Juno 是一个令人惊叹的应用程序,并期待着看到它如何随着时间的推移继续发展。
腐霉 3 号

Pythonista 3 作者截图
(价格:$9.99,App Store)链接
在 Jupyter 中工作是很棒的,但是当涉及到形式化你的训练和推理代码时,我们真的需要走出 Jupyter 的世界。幸运的是,Pythonista 是一个优秀的基于 Python 的 iPad IDE,它正好满足了我们的要求。像 Juno 一样,它也可以与 iOS 文件系统无缝同步,这样您就可以在同一个地方使用 Juno 的 Jupyter 笔记本和 Python 的 Python 脚本。Pythonista 还提供了一些非常酷的例子,展示了您可以在应用程序中实际完成的事情,因此,请查看这些例子,了解一些您可以在下一次数据科学工作中考虑的巧妙想法。
工作副本

工作副本的作者捕获的屏幕截图
(价格:基本功能免费,高级功能 19.99 美元,App Store 中的 链接)
还记得我说过你可以把你的作品存回 GitHub 吗?这就是这个应用程序的全部内容。就像 Juno 和 Pythonista 一样,工作副本可以直接进入 iOS 文件系统,以创建可以同步回 GitHub 的 Git repo。(或者 Filelocker 或者 GitLab 或者更多!)这是一个简单的应用程序,但对于在 iPad 上工作来说,它是一个超级好的补充。在这三个应用程序和 iOS 文件系统之间,你现在可以在 iPad 上的一个地方共同工作,这个地方可以很容易地同步到源代码库中。不错!!
当然,我不得不提到一个小缺点:用户界面。你可以说我疯了,但实际上我喜欢使用命令行,由于 iPad 对 iOS 的依赖,这显然是不存在的。工作副本确实有一个 UI 来执行这些传统的 Git 命令,但是…它可能需要一些工作。好消息是,核心功能已经存在,所以如果这款应用的开发者能够真正优化他们的用户界面,我认为这款应用可能会比现在更加出色。但不管 UI 如何,它仍然足够好,我仍然在自己的工作中使用它。
荣誉奖
上面提到的三个应用程序是在 iPad 上执行数据科学的“必备”,但我认为还有其他应用程序可能值得一试。我确信我可能遗漏了一些你想添加到这个列表中的内容,所以请在评论中说出你想在这里看到的内容。
【text astic(价格:9.99 美元) : Textastic 是 App Store 上最受欢迎的编码应用,因为它在语法高亮显示方面做得非常好,并且适用于许多不同的语言。但是因为我大部分时间都在使用 Python,所以我不太需要在 Juno 或 Pythonista 上使用这个应用程序。不过,把它放在诸如 markdown 文件编辑之类的地方还是不错的。
【GitHub(价格:免费) :这款应用最近才问世,支持流行的源代码库。我喜欢用 GitHub 展示我所有的个人作品,但 iPad 应用程序在我的书中并不是“必须的”。我个人不在 GitHub 上进行任何多人协作,但它在这些场景中确实显得更有用。因为它是免费的,为什么不下载呢?
【Carnets Jupyter(价格:免费) :这是我在 Jupyter 笔记本中用于交互的应用程序,但后来我换成了 Juno,老实说我不记得为什么了。它一点也不差,当然,它是免费的,没人能否认。
你能抛弃你的传统笔记本电脑吗?
因此,我们终于来到了我们一直在等待的问题,那就是,我们最终可以单独使用 iPad 进行数据科学工作吗?不幸的是,答案最终是否定的。因为 iOS 不是传统的操作系统,由于我依赖命令行工具、Docker 映像等等,所以有很多事情我做不了。我很有兴趣看看苹果是否能够克服这些障碍,但不幸的是,它们在 2021 年 1 月仍然存在。
但这并不意味着数据科学在 iPad 上完全死亡。我发现,如果有合适的硬件(例如,物理键盘和鼠标/触控板),iPad 是一个令人惊叹的学习和概念验证工具。如果你正在编写一些基本的代码,想要做一些简单的探索性数据分析,或者测试你刚刚学到的一些新技能,iPad 最终能够胜任所有这些事情。在一天结束的时候,我肯定不会扔掉我的笔记本电脑,但很高兴知道如果我真的想的话,我可以用我的 iPad 度过难关。
使用 Apache Airflow DockerOperator 和 Docker Compose

围绕[DockerOperator](https://airflow.apache.org/docs/apache-airflow-providers-docker/stable/_api/airflow/providers/docker/operators/docker/index.html)的互联网上的大多数教程都很棒,但是它们有一个缺失的环节,我今天想在这里介绍一下,它们都没有假设你正在用 Docker Compose 运行 Apache Airflow。
这里的所有代码和进一步的说明都在 repofclesio/air flow-docker-operator-with-compose中。
游戏攻略
a)首先,使用 webservice 创建一个容器,并创建airflow用户,如官方文档中所述:
结果应该大致类似于下图:

b)完成初始设置后,通过docker-compose启动网络服务和其他组件:

当您运行下面的语句时,您可以使用docker container ps来检查 docker 容器。结果应该大致如下所示:

c)所有容器都已启动并运行,让我们使用airflow作为登录和密码进入气流 UI:

d)在 Airflow UI 中,您会发现一个与 Docker 操作符相关的 DAG:

e)取消暂停 DAG:

f)单击 DAG 并进入图形模式:

您将看到正在执行的两个任务。这是下面的 DAG 代码:
如我们所见,我们正在执行一条sleep语句和另一条带有echo语句的语句。[N1]
在执行过程中,这将是docker container ps语句的结果:

在这幅图中,我们可以看到每个任务都有自己的容器。我使用任务中的container_name属性来命名这些任务。我强烈建议你总是在每个任务的容器中输入名字,因为如果你有一些容器在做一些意想不到的事情(例如无限循环,花费很长时间等等),调试一些未命名的容器将会非常困难。
g)我在 DAG 中做了一个小的改动,我加入了两个echo语句来显示这些信息将如何返回到气流日志中。只需将以下代码复制并粘贴到您的 DAG 中:
我们将向负责该任务的每个容器发送两条echo语句。一个带有hello,另一个带有world:

h)刷新并触发 DAG:



I)使用图形视图检查 DAG 中的执行情况:

在我的机器上执行相当快,但是我们可以通过查看日志来检查每个任务的结果。
如果我们点击docker_command_hello任务并选择logs选项,我们将看到command的输出



我们在docker_command_world任务中也会得到相同的结果:


j)在实验结束时,运行docker-compose down并停止所有容器。
一些实际的用法
- 独立于语言的任务编排(例如,我目前正在一个完全用 Ruby 编写的数据管道中工作);
- 提交第三方平台的作业(例如 AWS Sage Maker、Spark jobs、Databricks、Azure 等);
- 在隔离环境中运行 dbt 模型;
- ETL/ELT 任务的容器化;
- 用 soda.sql 进行一些 ETL/ELT 测试;
- 包括一个额外的步骤数据剖析、验证和测试,并对其寄予厚望,没有环境问题。
使用的小技巧
- 正如 Marc Lamberti 所描述的如果您的 docker 映像不是在相同的本地环境中构建的(例如在 worker 中),这意味着它需要被下载,这将增加 SLA
- docker 容器用户(在这个例子中是用户
[airflow](https://github.com/fclesio/airflow-docker-operator-with-compose/blob/main/dags/docker_job/Dockerfile)) 需要在 docker 守护进程中有权限(一个简单的chmod给气流用户权限就可以解决[N2】); - 一个单独的
[dockerfile](https://github.com/fclesio/airflow-docker-operator-with-compose/blob/main/dags/docker_job/Dockerfile)可以被转换成一个图像,供每个 DAG 使用。有一个" god docker image "包含所有的依赖项和包,可以运行 ELT 管道中的所有任务,这很有吸引力。请不要这样做。图像会变得不必要的臃肿,构建会花费很长时间(这将反映在 SLA 中); - 由于 DockerOperator 实现中的一个问题,选项
[AIRFLOW__CORE__ENABLE__XCOM_PICKLING](https://github.com/fclesio/airflow-docker-operator-with-compose/blob/11e67d9248d0639c9786313643190028b27659f0/docker-compose.yaml#L54)应该设置为[true](https://github.com/fclesio/airflow-docker-operator-with-compose/blob/11e67d9248d0639c9786313643190028b27659f0/docker-compose.yaml#L54); - 我们需要将运行 docker 守护进程的主机的 URL 链接作为一个卷传递给以允许 web 服务器容器启动 Docker 映像;
- 不要忘记在
[DockerOperator](https://github.com/fclesio/airflow-docker-operator-with-compose/blob/11e67d9248d0639c9786313643190028b27659f0/dags/docker_job/docker-job.py#L36)中建立[container_name](https://github.com/fclesio/airflow-docker-operator-with-compose/blob/11e67d9248d0639c9786313643190028b27659f0/dags/docker_job/docker-job.py#L36)属性。这一点非常重要,因为如果您忘记建立不同的名称,您可能会遇到容器名称冲突,DAG 将会失败(长话短说:出于某种原因,我发现 Airflow 正在尝试重新连接一些上游启动的容器;而这引起了一些冲突); - 默认情况下,
[image](https://github.com/fclesio/airflow-docker-operator-with-compose/blob/11e67d9248d0639c9786313643190028b27659f0/dags/docker_job/docker-job.py#L35)属性将总是拍摄最新的图像。根据经验,我强烈建议使用最近的Dockerfile构建所有图像。如果您希望 100%确保作业将始终使用(事实)最新的映像,请考虑为您的映像包含一个以前的构建任务。类似docker build -f path/to/Dockerfile -t yourname/image_name .的东西。 - 据我查看 Docker 实现的源代码,Airflow 在 Docker 套接字中使用了一种绑定安装来启动容器。我不是 100%确定,但这意味着气流没有做对接可能会导致其他一些过多的问题。
结束语
我个人认为 Airflow + Docker 是一个很好的组合,可以为 ELT/ETL 任务提供灵活、可扩展和无麻烦的环境。不幸的是,在互联网上,大多数使用 Docker Compose 的 Airflow 实现都没有考虑到使用 DockerOperator 作为灵活的任务编排环境的可行替代方案。
就 ELT/ETL 架构而言,这种使用 Docker Compose + DockerOperator 的气流组合认为是一个很好的替代方案,与以下相比:(I)由于我们不需要处理工作人员供应,(ii)由于其固有的复杂性,(iii)使用 Docker Swarm 管理这些任务,但其在供应和故障恢复能力方面存在局限性。
参考
有用的链接,我在这个过程中使用的重要性排序。
- 对接器中的运行气流
- 我已经在 Docker 中运行 Airflow,如何在 Airflow 中使用 DockerOperator?
- 在您的 CI 或测试环境中使用 Docker-in-Docker?三思而后行。
- 如何在 Apache Airflow 中使用 docker operator
- 如何使用气流而不头疼
- 码头工人灭霸(小心使用)
- DockerOperator 无法将 XCom 值序列化到 JSON 中
- 从 docker 内部运行 docker 可以吗?
- 停靠栏中的 chmod 无法正常工作
- air flow docker operator:connect sock . connect(self . UNIX _ socket)file not found error:[Errno 2]没有这样的文件或目录
- 如何使用正确的组将 docker 插座作为卷安装到 docker 容器中
- 关于通过 Apache Airflow 在 Python 子进程中运行 docker 命令的权限问题
- 如何在 Docker 容器中运行 Docker
笔记
[N1] —我在任务名称和命令语句中犯了一点小错误。
【N2】—在 Reddit 的这个帖子中,一个用户通过chmod 777 /var/run/docker.sock为主机 Docker 守护进程中的权限做了一个激进的解决方案。我个人不推荐。
使用人工智能来预测野火的蔓延,用 Python

Benjamin Lizardo 在 Unsplash 上的照片
这是一个关于如何使用数据分析和机器学习来预测野火蔓延的分步指南
在过去的几天里,可怕的野火已经蔓延到撒丁岛、意大利和全世界。图像和视频令人心碎。当夏季开始时,这些事件往往会更频繁地出现,最终结果是灾难性的。
我决定学习人工智能(AI)的原因之一是能够直接帮助人们和解决现实世界问题的想法。我想这是其中之一。
在这篇文章中,我将指导你一步一步地使用人工智能来预测野火的蔓延。
让我们开始吧。
1图书馆:
您要做的第一件事是导入 Python 库。使用了很多不同的工具, Pycaret 用于开发机器学习部分, Pandas 和 Numpy 用于数据分析, geopy 用于获取距离等信息…
注意:这是一个通用的设置,我为这个笔记本做了一点改动。如果有什么东西在代码部分不会用到,我很抱歉。另外,我使用 plt.rcParams 来设置我最喜欢的绘图参数。你可以放心地跳过这一步。
2数据集
这是我用来下载数据集的源。这是一个加州野火的集合。让我们来看看。
可以看出,纬度和经度列不太可靠(例如 Ventura 是(0,0))。我们将在这方面努力。然后我们有了市 ( 县列)和野火发生的日期,即开始。名为 AcresBurned 的一栏是被野火烧毁的英亩数。
3数据可视化
让我们使用 geopy 通过使用县列来定位城市。让我们修复一些错误(例如,圣克鲁斯位于墨西哥,因此我们必须将其从数据集中删除)并绘制结果:
这是数据集的世界地图:
对于我们的分析,我们想要预测最终野火的城市在数据集中出现一定次数是很重要的。我们来策划一下吧!
随着洛杉矶出现足够高的次数,这个城市将被使用和研究。
我们需要的另一条信息是城市之间的距离。
让我们用这段代码得到它们,并绘制热图。
最后一步是绘制数据集的 4 个城市野火直方图:
4数据预处理
好了,现在我们对数据有了一个大致的概念,我们必须很好地定义我们需要什么。
我们想回答这些问题:
“鉴于野火 X 发生在名为 Z 的城市的第 Y 天,从这里到 3 天,洛杉矶市会发生野火吗?3 天到 7 天会有野火吗?7 天到 10 天会不会有野火?”
为了做到这一点,我们希望有一个包含如下行的数据集:
A .野火发生的日期
B .野火的距离
C .野火烧毁的英亩数
D .这场野火与洛杉矶另一场野火之间的天数
特别是,d .列必须包含以下 4 个类中的一个:
D.1 一到三天
D.2 三到七天
D.3 七到十天
D.4 十多天(这意味着没有相关的野火)
这种数据集是通过使用以下代码行获得的:
- 划分 4 个等级
2。将它们全部归入单个数据集
3。创建最终数据集
为分类准备的数据集如下:
5机器学习
你可能听说过,很多时候,机器学习部分是最快的一个。尤其是在 2k21 中用 PyCaret 这样的工具。已经测试了各种各样的机器学习模型。最好的一个在测试集上给了我们 87+%的准确率。
这是混淆矩阵,向我们展示了非常好的结果。
最终考虑
我们谈论的是人的生命,所以我们不能给虚假的希望。这篇文章只是野火预测挑战中一条看似有希望的道路的一个例子。必须使用大量研究和资源来验证这种方法。此外,各种不同的方法(例如,检查温度、湿度等)可以用来预测野火,当我写这篇文章时,研究人员正在开发新的方法。
如果你喜欢这篇文章,你想知道更多关于机器学习的知识,或者你只是想问我一些你可以问的问题:
A.在 Linkedin 上关注我,在那里我发布我所有的故事
B .订阅我的 简讯 。这会让你了解新的故事,并给你机会发短信给我,让我收到你所有的更正或疑问。
C .成为 推荐会员 ,这样你就不会有任何“本月最大数量的故事”,你可以阅读我(以及成千上万其他机器学习和数据科学顶级作家)写的任何关于最新可用技术的文章。
使用 Azure Functions 核心工具获取本地开发的应用程序设置
使用 Azure Function Core 工具,我们可以从函数中检索设置,以简化我们的本地开发和调试工作!
在部署我们的 Azure 功能之前,最好在本地调试我们的功能,以确保它按预期工作。本地运行我们的函数需要一个 local.settings.json 文件来存储我们的应用程序设置。
当我们第一次在 Visual Studio 中创建函数时,会为我们生成一个 local.settings.json 文件。但是,如果我们从一个库克隆一个功能 app,这个文件就不会被克隆了!(希望它不会被克隆。最好不要把这个文件提交给你的回购,因为它有应用程序的秘密!).
幸运的是,我们可以使用 Azure Function Core Tools 创建一个 local.settings.json 文件,并将我们的函数设置导入到该文件中,这样我们就可以在本地运行我们的函数,就像我们在那个环境中运行它一样!
我们为什么要这么做?
比方说,如果我们有多个环境(开发、测试、UAT 等),我们希望使用这些环境设置在本地调试一个功能,我们可以使用 Azure Function Core 工具来简化这些设置的检索。尤其是对于有很多设置的函数。我们不想浪费时间复制和粘贴所有这些设置!
开始之前
你需要确保你的机器上已经安装了 Azure 功能核心工具。Azure Functions 核心工具包括一个支持 Azure Functions 运行时的相同运行时版本,我们可以在我们的机器上运行它。
虽然我们将只关注获取我们的应用程序设置,但函数核心工具附带了允许我们创建函数、部署函数等命令!
如果您还没有安装这些工具,请查看本文档开始安装。
我们开始吧
对于本教程,我从 GitHub 克隆了一个现有项目。我已经把这个功能部署到 Azure 和我的。gitignore 文件排除了我创建项目时生成的 local.settings.json 文件。
为了帮助我进行本地调试,我将使用 Function Core 工具来帮助从 Azure 获取我的应用程序设置。为了验证本地设置文件不在我的项目目录中,这里有一个屏幕截图:

图片作者:Will Velida
我们需要创建一个 JSON 文件来写入我们的应用程序设置。为此,右键单击 Functions 项目并创建一个新文件。创建一个 JavaScript JSON 配置文件,命名为 local.settings.json
步骤 1:设置我们的 Azure 订阅
如果你使用多个 Azure 订阅,我们需要使用 AZ CLI 来设置它,以确保当我们运行我们的功能核心工具时,它在正确的位置。
在您的计算机上打开命令行或 PowerShell,并通过运行以下命令登录到您的 Azure 帐户:
az login
登录后,您应该会看到您有权访问的订阅列表。要设置托管您的函数的订阅,请运行以下命令:
az account set -s "<subscription-name-or-id>"
-s 是订阅的简写参数。
步骤 2:获取我们的应用程序设置
现在,我们可以从 Azure 的功能应用程序中获取应用程序设置。确保您位于项目的目录中,并在终端中运行以下命令:
func azure functionapp fetch-app-settings '<function-name>' --output-file local.settings.json
您应该会在终端中看到类似如下的输出:

图片作者:Will Velida
回到 Visual Studio 中的项目,并签出您的 local.settings.json 文件。我们可以看到,我们的函数设置已经被检索并写入我们的本地设置文件!
然而,只有一个问题…

图片作者:Will Velida
我们的设置被加密了!当在本地运行我们的函数时,这对我们没有帮助!函数应用程序设置在存储时是加密的,只有在启动时被注入函数的进程内存之前才会被解密。
要了解 Azure Functions 中应用程序设置的更多信息,请查看这篇文章。
第三步:解密我们的设置。
谢天谢地,我们可以使用 Azure Function Core 工具解密我们的设置!在您的终端中,运行以下命令:
func settings decrypt
回到 Visual Studio,您将看到您的设置已经被解密,并准备好用于您的本地调试需要!
(当然我不会给你那个的截图!这是秘密!😂😉).
我们现在可以在本地启动我们的功能,就像我们在 Azure 环境中运行它一样!
这里要注意一点!如果您在 Key Vault 中存储了一些应用程序设置(您应该这样做!),则当您检索该设置时,您可能只能获得存储该机密的密钥库 URL。要使用实际值,您需要从密钥库中检索它。
包扎
正如你所看到的,使用 Azure Function Core 工具可以帮助我们快速检索功能应用设置,从而加快我们的开发过程。
如果您想了解更多信息,请查看以下资源:
如果您有任何问题,请随时在 Twitter 上联系我或在评论中提问!
编码快乐!👨💻👩💻💻
使用 Azure 日志分析工作区从虚拟机收集自定义日志
不,我们不是在谈论带有预定义指标的 Azure Monitor。

马丁·范·登·霍维尔在 Unsplash 上的照片
动机:
我们都已经在虚拟机页面上看到了“Monitoring”选项卡的关键指标。是的,通过 CPU 指标来查看 CPU 是否一直在运行,以指标来检查虚拟机何时通过网络从外部世界获取数据,以及虚拟机是否正在使用磁盘操作/秒指标进行任何类型的写操作,这都很有用,但对于我们在虚拟机上构建的定制服务来说,这并不有效。因此,在这篇博客中,我将向您展示如何基于客户服务创建您自己的日志,使用其默认代理将它带到Azure Log Analytics Workspace,并根据我们的需要查询它,甚至更好地在其上创建警报。
先决条件:
- 蓝色账户
- Azure 虚拟机服务
- Azure 日志分析工作区服务
- Azure 警报服务
Azure 虚拟机:
让我们从虚拟机本身开始。您已经在虚拟机上运行了服务,但是不知道如何将这些日志放入门户,甚至不知道如何为您的服务创建日志。因此,让我们假设您还没有为您的服务创建日志。因此,在这篇博客中,我将以一个简单的 flask 应用程序作为我的服务示例。要启动并运行它,我们必须从 pip 下载 flask 库,然后创建一个 flask 应用程序,如下所示:
苏拉布什雷斯塔。使用 Azure 日志分析工作区从 VM 1 收集自定义日志。2021.JPEG 文件。
没什么,只是一个运行在默认端口 5000 上的简单的 flask 应用程序。让我们通过下面的命令来运行它:
python app.py
该服务必须正在运行。现在完成了,让我们移动到日志部分。我们将要使用的技巧都取决于 grep。由于 grep 命令将帮助我们找到正在运行的服务,我们将利用它来了解 app 服务是否正在工作。因此,我创建了这个 bash 脚本来了解应用服务是否正在运行。如果它正在运行,我会给它一个 200 成功代码,如果它没有运行,那么它会得到 500 错误代码。
苏拉布什雷斯塔。使用 Azure 日志分析工作区从您的虚拟机 2 收集自定义日志。2021.JPEG 文件。
一直到现在都很简单。现在尝试运行脚本,它会根据您的服务状态显示 200 或 500。但是我们不能永远手动操作。所以为了自动化,我使用 crontab 每分钟为我运行一次脚本。通过“crontab -e”打开 crontab,然后输入以下命令
苏拉布什雷斯塔。使用 Azure 日志分析工作区从您的虚拟机 3 收集自定义日志。2021.JPEG 文件。
为了解释它,第一部分处理 bash 脚本所在的位置,第二部分处理您希望将生成的日志文件放在哪里。我建议你创建这个文件和文件夹,并在把它放到 crontab 之前给它适当的权限' chmod '。现在,让我们等待 5 分钟,检查日志是否已写入。

苏拉布什雷斯塔。使用 Azure 日志分析工作区从您的虚拟机 4 收集自定义日志。2021.JPEG 文件。
看起来它像预期的那样工作,因为我在 crontab 上运行它之前已经关闭了我的服务。复制其中 5 条消息,并将其保存在一个新文件中,我们需要向日志分析工作区提交一个样本。现在,让我们转到 Azure 门户。
Azure 日志分析工作区:
转到您的虚拟机,然后选择“日志”并单击“启用”按钮。系统将提示您创建新的工作空间。

苏拉布什雷斯塔。使用 Azure 日志分析工作区从您的虚拟机 5 收集自定义日志。2021.JPEG 文件。
工作区需要 5-10 分钟,请耐心等待。创建工作空间后,转到 Insights 选项卡
我们现在正在做的是通过点击我们的按钮来安装 Azure Log Analytics 工作空间。我们也可以使用 CLI 创建,但我更喜欢门户方式。当且仅当门户不起作用时,我才会选择 CLI 方式。现在让我们配置我们的机器来获取定制日志。转到您创建的日志分析工作区,单击“高级设置”,这将提示您进入新页面。

苏拉布什雷斯塔。使用 Azure 日志分析工作区从 VM 6 收集自定义日志。2021.JPEG 文件。
点击“数据”和“自定义日志”。你会看到一个“添加+”按钮。点击它,你会得到一个新标签的提示。

苏拉布什雷斯塔。使用 Azure 日志分析工作区从您的 VM 7 收集自定义日志。2021.JPEG 文件。
上传您在上面保存示例日志,然后单击“下一步”。您将看到“新行”和“时间戳”选项。选择“新行”并点击“下一步”

苏拉布什雷斯塔。使用 Azure 日志分析工作区从您的 VM 8 收集自定义日志。2021.JPEG 文件。
给出 crontab 上的路径,然后点击“下一步”

苏拉布什雷斯塔。使用 Azure 日志分析工作区从您的 VM 9 收集自定义日志。2021.JPEG 文件。
给出适当的名称,因为如果需要,我们将在查询中使用该名称。

苏拉布什雷斯塔。使用 Azure 日志分析工作区从您的 VM 10 收集自定义日志。2021.JPEG 文件。
不要忘记选择“将以下配置应用于我的 Linux 机器”。现在让我们看看日志是否到达。Azure 文档说日志可能需要 1 小时到达,但是在我的例子中,它在 20 分钟内到达。所以请按照步骤做好,等待日志到达。现在,点击“日志”,会出现一个窗口提示您输入一些类似 SQL 的命令。它实际上是 Kusto 查询语言,类似于 SQL,所以我们可以使用 Azure 提供的引用来使用它。我还将在下面的参考资料部分链接该指南。

苏拉布什雷斯塔。使用 Azure 日志分析工作区从您的 VM 11 收集自定义日志。2021.JPEG 文件。
如你所见,它成功了。如果你已经做到了这一步,那么恭喜你,因为你已经很好地将你的自定义日志引入了 Azure 门户。现在您可以使用它,查询它,分析它,等等。在下一部分中,我将基于自定义日志创建一个警报。
Azure 提醒:
到目前为止,我们将自定义日志引入 Azure 门户,这已经是一项相当大的工作,但我们还想做得更多。因此,让我们基于自定义日志创建一个警报。正如你已经意识到的,自定义日志是混乱的,所以我们要做的是首先使用 Kusto 查询语言(KQL)清理它。

苏拉布什雷斯塔。使用 Azure 日志分析工作区从您的 VM 12 收集自定义日志。2021.JPEG 文件。
我们在这里所做的只是从我们的自定义日志中提取状态代码。我应该说非常整洁。现在,我们要做的是根据状态代码创建一个警报。如果服务不运行,我们将得到一个 500 错误代码,因此,如果服务不工作,我们将使用 500 错误代码创建一个警报。单击“新建警报规则”,这将提示您进入新页面。

苏拉布什雷斯塔。使用 Azure 日志分析工作区从您的 VM 13 收集自定义日志。2021.JPEG 文件。
输入我所做的选择。我们使用聚合类型作为最大值,因为我们只有两个状态代码,并且警报逻辑本身是描述性的。点击“下一步”和“创建行动组”,输入您的电话号码或电子邮件。

苏拉布什雷斯塔。使用 Azure 日志分析工作区从您的 VM 14 收集自定义日志。2021.JPEG 文件。
创建行动组后,返回,您将看到您新创建的行动组。给出您的提醒名称和“查看+创建”。现在让我们等待 5 分钟,看看是否会弹出一封电子邮件。

苏拉布什雷斯塔。使用 Azure 日志分析工作区从您的 VM 15 收集自定义日志。2021.JPEG 文件。
耶,警报被触发了,我们收到了一封邮件。祝贺你,如果你已经做到了这一步,因为这不是一个简单的任务。
结论:
我们创建了一个服务,每分钟生成一个日志,将它带到 Azure 门户,并基于它创建一个警报。如果您想要在为机器学习任务训练模型时创建 GPU 的日志以了解其状态,则可以将相同的逻辑应用于 GPU 指标。它还可以应用于不同的 web 服务,以了解它们的状态等等。可能性是无限的,选择是无限的,你想做什么取决于你自己。如果你遇到任何问题或难以遵循这些步骤,请在下面评论这篇文章或在 tsulabh4@gmail.com 给我发消息。你也可以在 Linkedin 和 GitHub 上和我联系。
资源:
[1]:KQL-1:https://docs . Microsoft . com/en-us/azure/data-explorer/kusto/query/extract function
[2]KQL-2:https://docs . Microsoft . com/en-us/azure/data-explorer/kusto/query/re2
[4]KQL-4:https://docs . Microsoft . com/en-us/azure/data-explorer/kusto/query/SQL cheat sheet
[5] Azure 日志分析工作区:https://docs . Microsoft . com/en-us/Azure/Azure-monitor/agents/data-sources-custom-logs
利用 PyMC3 中的贝叶斯分层模型推断新冠肺炎的疾病参数
在本帖中,我们来看看如何使用 PyMC3 来推断新冠肺炎的疾病参数。PyMC3 是一个流行的概率编程框架,用于贝叶斯建模。实现这一点的两种流行方法是马尔可夫链蒙特卡罗( MCMC )和变分推断方法。这里的工作着眼于使用美国感染病例的当前可用数据作为时间序列,并试图使用分室概率模型对此建模。我们想尝试推断疾病参数,并最终使用 MCMC 采样估计 R0 。然后,我们将探索如何使用贝叶斯分层模型来做同样的事情,以及与池化或非池化模型相比的好处。我们总结了这个模型的局限性,并概述了改进推理过程的步骤。
这里介绍的工作仅用于说明目的,现实生活中的贝叶斯建模需要比这里显示的复杂得多的工具。这里作出了关于种群动态的各种假设,这些假设可能不适用于大型非同质种群。此外,这里不考虑社会距离和疫苗接种等干预措施。
本帖将涵盖以下内容:
- 流行病的分室模型
- 数据从何而来,如何获取
- 疾病动力学 SIR/SIRS 模型
- PyMC3 常微分方程的贝叶斯推理
- 使用层次模型扩展工作
- 概率编程的指南和调试技巧
我还在 Coursera 上推出了一系列课程,涵盖贝叶斯建模和推理的主题,课程 2 和 3 与本文特别相关。请到https://www.coursera.org/specializations/compstats查看。
流行病的分室模型
关于房室模型及其行为的概述,请参考朱莉亚—https://github.com/sjster/Epidemic.的笔记
房室模型是封闭种群的一组常微分方程(ODEs ),这意味着种群没有进出该房室的运动。这些旨在模拟同质人群中的疾病传播。可以想象,这些假设在大人群中可能不成立。这里还必须指出的是,人口出生和死亡人数等生命统计数据可能不包括在这个模型中。下面的列表提到了一些分室模型以及疾病传播的各个分室,然而,这决不是一个详尽的列表。
- 易感感染(SI)
- 易感感染者康复(SIR)
- 易受感染的
- 易感感染恢复易感(SIRS)
- 易感感染者康复死亡(SIRD)
- 易感暴露感染康复(SEIR)
- 易感暴露感染恢复易感(SEIRS)
- 易感暴露感染康复死亡
- 母源性免疫易感传染病痊愈(MSIR)
- 西达尔特(https://www.nature.com/articles/s41591-020-0883-7)
上面列出的最后一个是最近的,专门针对新冠肺炎的,可能值得感兴趣的人读一读。现实世界的疾病建模通常不仅仅涉及疾病阶段的时间演变,因为许多与分室相关的假设都被违反了。为了了解疾病是如何传播的,我们需要观察疾病在人群中的空间离散化和演变。GLEAM 是模拟这种时空演变的框架的一个例子(图 1)。

图 1-真实世界流行病建模(时空动态)。
GLEAM 等工具使用人口普查数据和流动模式来了解人们如何在地理上流动。GLEAM 将地球划分为大约 25 公里 x25 公里的空间网格。大致有两种类型的移动:全球或远程移动和本地或短程移动。长期流动性主要涉及航空旅行,因此机场被认为是疾病传播的中心。海上旅行也是另一个重要因素,因此军港是另一种进入点。与上面列出的数学模型一起,这提供了一个随机框架,可用于进行数百万次模拟,以推断参数并进行预测。
新冠肺炎数据
此处使用的数据来自约翰·霍普金斯 CSSE Github 页面,该页面中的病例数会定期更新:
这些数据以 CSV 文件的形式提供,可以通过 Python pandas 读取。
SIR 和 SIRS 模型
SIR 模型
SIR 模型由下面所示的三个常微分方程组(ODEs)给出。这个模型有三个部分。

这里的‘S’、‘I’和‘R’指大小为‘N’的人群中的易感、感染和康复部分,因此

这里的假设是,一旦你从疾病中康复,个人将获得终身免疫。许多疾病的情况并非如此,因此可能不是一个有效的模型。
λ是感染率,μ是从疾病中康复的速度。从感染中康复的人的比例由“f”给出,但是为了这项工作的目的,“f”在这里被设置为 1。我们以我们的常微分方程组的初值问题(IVP)结束,其中 I(0)被假设为从疫情开始的情况计数中已知,并且 S(0)可以被估计为 N-I(0)。这里我们假设整个人口都是易感的。我们的目标是实现以下目标:
- 使用贝叶斯推理对λ和μ进行估计
- 使用上述参数估算任何时间“t”的 I(t)
- 计算 R0
正如已经指出的,λ是疾病传播系数。这取决于在单位时间内与有传染性的人互动的次数。这又取决于人群中的感染人数。

在任何时间‘t’的感染力或风险被定义为:

另外,μ是单位时间内发生的恢复的分数。因此,μ的倒数就是平均恢复时间。“基本再生数” R0 是由单个主要案例产生的次要案例的平均数(例如https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6002118/)。 R0 也被定义为λ和μ的比值,由下式给出

以上假设 S0 接近 1。当 R0 > 1 时,我们有了疾病的扩散,我们有了疫情。随着最近对脆弱人群进行疫苗接种的努力,这一点变得更加需要理解。如果我们给人口中的一小部分“p”接种疫苗以获得(1p)R0<1,我们就能阻止疾病的传播。
SIRS 模型
如下图所示,SIRS 模型并没有假设感染者一旦康复后会终身免疫。因此,从恢复区室到易感区室。因此,对于新冠肺炎来说,这可能是一个更好的低保真度基线模型,因为它表明获得性免疫是短期的。这里唯一的附加参数是γ,它指的是免疫力丧失和受感染个体从恢复池移动到易感池的速率。

对于这项工作,只实现了 SIR 模型,SIRS 模型及其变体留待将来的工作。
使用 PyMC3 推断疾病参数
我们可以使用一阶或二阶时间差分方案对 SIR 模型进行离散化,然后将其传递给 PyMC3,pymc 3 将使用这些离散化方程及时推进求解。然后可以使用蒙特卡罗采样程序拟合参数λ和μ。
一阶方案

二阶方案

PyMC3 中的微分方程方法
虽然我们可以选择高阶离散化方案来手动提供离散化,但这很快就会变得繁琐且容易出错,更不用说在 Python 这样的语言中计算效率低下了。幸运的是,PyMC3 有一个 ODE 模块来完成这个任务。我们可以使用 ODE 模块中的微分方程方法,该方法将一个函数作为输入,该函数以向量的形式返回一组 ODE 的值、需要求解的时间步长、对应于方程数量的状态数量以及我们想要求解的变量数量。尽管这仍然比手工离散化要快,但是这种方法对于问题的规模来说扩展性很差。推荐的最佳实践是使用 PyMC3 中的“sunode”模块(见下文)。例如,对于 100 个样本、100 个调整样本和 20 个时间点,相同的问题使用微分方程需要 5.4 分钟,而使用 sunode 需要 16 秒。
self.sir_model_non_normalized = DifferentialEquation(
func = self.SIR_non_normalized,
times = self.time_range1:],
n_states = 2,
n_theta = 2,
t0 = 0)def SIR_non_normalized(self, y, t, p):
ds = -p[0] * y[0] * y[1] / self.covid_data.N,
di = p[0] * y[0] * y[1] / self.covid_data.N — p[1] * y[1]
return[ds, di]
使用 sunode 模块的语法如下所示。虽然有一些句法上的差异,但总体结构与微分方程是相同的。
import sunode
import sunode.wrappers.as_theanodef SIR_sunode(t, y, p):
return { ‘S’: -p.lam * y.S * y.I,
‘I’: p.lam * y.S * y.I — p.mu * y.I}……sir_curves, _, problem, solver, _, _ =
sunode.wrappers.as_theano.solve_ivp(
y0={ # Initial conditions of the ODE
‘S’: (S_init, ()),
‘I’: (I_init, ()),},
params={
# Parameters of the ODE, specify shape
‘lam’: (lam, ()),
‘mu’: (mu, ()),
‘_dummy’: (np.array(1.), ())} # currently, sunode throws an error, without this # RHS of the ODE
rhs=SIR_sunode,
# Time points of th solution
tvals=times,
t0=times[0],)
SIR 模型的推理过程
为了对我们寻求的参数进行推断,我们首先为疾病参数选择合理的先验。基于我们对疾病现象行为的理解,对数正态分布是疾病参数的合理先验。理想情况下,我们希望这个对数正态分布的平均参数在我们期望的参数附近。对于良好的融合和解决方案,数据似然性是适当的(领域专业知识!).通常选择以下之一作为可能性。
- 正态分布
- 对数正态分布
- 学生的 t 分布
我们从 ODE 求解器获得易感(S(t))和传染(I(t))数,然后对λ和μ的值进行采样,如下所示。
with pm.Model() as model4: sigma = pm.HalfCauchy(‘sigma’, self.likelihood[‘sigma’], shape=1) lam = pm.Lognormal(‘lambda’, self.prior[‘lam’], self.prior[‘lambda_std’]) # 1.5, 1.5 mu = pm.Lognormal(‘mu’, self.prior[‘mu’], self.prior[‘mu_std’]) res, _, problem, solver, _, _ = sunode.wrappers.as_theano.solve_ivp(
y0={‘S’: (self.S_init, ()), ‘I’: (self.I_init, ()),},
params={‘lam’: (lam, ()), ‘mu’: (mu, ()), ‘_dummy’: (np.array(1.), ())},
rhs=self.SIR_sunode,
tvals=self.time_range,
t0=self.time_range[0]) # likelihood distribution mean, these are the predictions from the SIR model ODE solver if(likelihood[‘distribution’] == ‘lognormal’): I = pm.Lognormal(‘I’, mu=res[‘I’], sigma=sigma, observed=self.cases_obs_scaled) elif(likelihood[‘distribution’] == ‘normal’): I = pm.Normal(‘I’, mu=res[‘I’], sigma=sigma, observed=self.cases_obs_scaled) elif(likelihood[‘distribution’] == ‘students-t’): I = pm.StudentT( “I”, nu=likelihood[‘nu’], \
mu=res[‘I’],
sigma=sigma,
observed=self.cases_obs_scaled) R0 = pm.Deterministic(‘R0’,lam/mu) trace = pm.sample(self.n_samples, tune=self.n_tune, chains=4, cores=4) data = az.from_pymc3(trace=trace)
PyMC3 的推理流程
因为开发这样一个模型,使用贝叶斯推理来估计疾病参数,是一个迭代过程,我们希望尽可能自动化。用各种参数实例化一类模型对象并自动运行可能是个好主意。为每次运行保存跟踪信息、推理度量(如 R̂ (R-hat ))以及其他元数据信息也是一个好主意。诸如 NetCDF 之类的文件格式可以用于此,尽管它可以像使用 Python 内置数据库模块“shelve”一样简单。这里没有显示用于数据提取的类,但是显示了它们的调用,以便您对这里使用的数据和模型参数有所了解。
covid_obj = COVID_data(‘US’, Population=328.2e6)covid_obj.get_dates(data_begin=’10/1/20', data_end=’10/28/20')sir_model = SIR_model_sunode(covid_obj)likelihood = {‘distribution’: ‘normal’, ‘sigma’: 2}prior = {‘lam’: 1.5,‘mu’: 1.5, ‘lambda_std’: 1.5, ‘mu_std’: 1.5 }sir_model.run_SIR_model(n_samples=500, n_tune=500, likelihood=likelihood, prior=prior)
这些结果纯粹是为了说明的目的,需要进行大量的实验才能从模拟中得到有意义的结果。美国 1 月至 10 月的病例数如下所示(图 2)。

图 2 -美国新冠肺炎病例数可视化示例
图 3 示出了推理运行的结果,其中显示了λ、μ和 R0 的后验分布。执行贝叶斯推断的一个优点是,分布显示了平均值估计以及用于量化不确定性的最高密度区间(HDI)。检查跟踪是一个好主意(至少!)以确保正确采样。

图 3 -使用 PyMC3 显示最高密度区间(HDI)的推理运行结果示例。
池化、非池化和分层模型
假设您有关于美国各州感染人数的信息。使用该数据来推断新冠肺炎的疾病参数(例如 R0) 的一种方式是将其全部相加以估计单个参数。这被称为集合模型。然而,这种方法的问题是可能包含在这些单独的状态或组中的细粒度信息丢失了。另一个极端是估计每个状态的单个参数 R0 。这种方法产生了一个非池模型。然而,考虑到我们正在尝试估计对应于同一病毒的参数,必须有一种方法来集体执行此操作,这将我们带到了层次模型。当在某些州没有足够的信息来创建准确的估计时,这特别有用。分层模型允许我们使用共享的“超优先级”来共享来自其他状态的信息。让我们以下面的例子来更详细地看看这个公式:
对于混合模型,我们可以从具有固定参数λ_μ,λ_σ的单个分布中提取

对于未规划的模型,我们可以从具有固定参数λ_μᵢ、λ_σᵢ

对于分层模型,我们有一个先验,它由来自其他分布的非常数参数来参数化。这里,我们为每个状态画一个λ值,但是它们通过共享的超优分布(具有常数参数)连接,如下所示。

查看课程 3 贝叶斯建模和推理的 PyMC3 介绍(【https://www.coursera.org/learn/introduction-to-pymc3?】T2)specialization=compstats )来了解更多关于层次模型的细节。
分层模型的新冠肺炎数据
这里我们绘制并使用了美国和巴西这两个国家每天的感染病例数。然而,在层级模型中可以使用的国家的选择或数量没有限制。以下案例发生于 2020 年 3 月 1 日至 2021 年 1 月 1 日。这些图表似乎遵循相似的轨迹,尽管这些国家在 y 轴上的比例不同。考虑到这些病例来自同一个新冠肺炎病毒,这是合理的。然而,在现实场景中,需要考虑差异,例如不同的变量、不同的地理结构和社会距离规则、医疗保健基础设施等等。

图 4 -两个国家的新冠肺炎病例数量图
参数推断
对于分层模型,下面给出了执行疾病参数推断的代码片段。
with pm.Model() as model4: # narrow std is roughly equivalent to a constant prior parameter, if there are issues with sampling from the prior distribution
# make the variance of the mean smaller. Variance of the distribution of the variance parameter seems less relevant in this regard. nsamples = 8000 ntune = 4000 Hyperprior = {“Lambda mean”: 0.75, “Lambda std”: 2, “Mu mean”: 0.75, “Mu std”: 2} Prior = {“Lambda std”: 1.0, “Mu std”: 1.0} Likelihood = {“Name”: “Normal”, “Parameters”: {“std”: 0.01}} prior_lam = pm.Lognormal(‘prior_lam’, Hyperprior[‘Lambda mean’], Hyperprior[‘Lambda std’]) prior_mu = pm.Lognormal(‘prior_mu’, Hyperprior[‘Mu mean’], Hyperprior[‘Mu std’]) prior_lam_std = pm.HalfNormal(‘prior_lam_std’, Prior[‘Lambda std’]) prior_mu_std = pm.HalfNormal(‘prior_mu_std’, Prior[‘Mu std’]) lam = pm.Lognormal(‘lambda’, prior_lam , prior_lam_std, shape=2) mu = pm.Lognormal(‘mu’, prior_mu , prior_mu_std, shape=2) # — — — — — — — — — — ODE model — — — — — — — — # res, _, problem, solver, _, _ = sunode.wrappers.as_theano.solve_ivp(
y0={ ‘S’: (S_init, (2,)), ‘I’: (I_init, (2,)),},
params={‘lam’: (lam, (2,)), ‘mu’: (mu, (2,)), ‘_dummy’: (np.array(1.), ())},
rhs=SIR_sunode,
tvals=time_range[1:],
t0=time_range[0]) I = pm.Normal(‘I’, mu=res[‘I’], sigma=Likelihood[‘Parameters’][‘std’], observed=cases_obs_scaled[1:]) R0 = pm.Deterministic(‘R0’,lam/mu) # if you increase the variance and the distributions looks choppy, increase the tuning sample size to sample the space more effectively
# also, increase the total number of samples trace = pm.sample(nsamples, tune=ntune, chains=8, cores=8) data = az.from_pymc3(trace=trace)
样本后验分布及其 94%的最高密度区间(HDI)如下所示。


图 5 -采样后验分布及其 94%的最高密度区间(HDI)。
我们还可以检查轨迹图的收敛性,它显示了所有变量的良好混合——这是采样器很好地探索了空间的好迹象。所有的痕迹之间有很好的一致性。这种现象可以从上图中相当窄的 HDI 区间得到证实。

图 6-R0 和其他变量的轨迹图和密度图
下表总结了各种推断变量和参数的分布,以及抽样统计。虽然对变量的估计是必不可少的,但此表对于告知我们采样器的质量和效率特别有用。例如,R-hat 都接近或等于 1.0,表明所有链之间的一致性良好。有效样本量是另一个关键指标。如果这与样本总数相比很小,这肯定是取样器有问题的迹象。即使 R-hat 值看起来不错,也一定要检查有效样本量!

图 7 -推断变量分布表以及抽样统计
注意事项和指南
建模和推理的一些一般准则:
- 使用至少 5000 个样本和 1000 个样本进行调整。
- 对于上面显示的结果,我使用了:均值:λ= 1.5,= 1.5,两个参数的标准差:2.0。领域专家和他的知识在设置这些参数时是非常宝贵的。
- 至少从 3 条链上取样。
- 将 target_accept 设置为> 0.85(取决于采样算法)。
- 如果可能,用核心=n 并行采样,其中‘n’是可用核心的数量。
- 检查轨迹是否收敛。
- 有限的时间样本会影响推断的准确性,拥有更多高质量的数据总是更好的。
- 规范化您的数据,大值通常不利于收敛
调试您的模型
- 因为 PyMC3 的后端是 theano,所以 Python print 语句不能用于检查变量值。使用the no . printing . print(DESCRIPTIVE _ STRING)(VAR)来完成这个。
- 通过传递“testval”来初始化随机变量。这非常有助于检查那些讨厌的“坏能量”错误,这些错误通常是由于对可能性或先验的错误选择。使用 Model.check_test_point() 来验证这一点。
- 使用步骤= pm。Metropolis() 对于快速调试来说,这运行得更快,但结果却更粗糙。
- 如果采样很慢,检查你的先验分布和似然分布。
- 先验分布的窄西格玛值可用于模拟常数先验,并有助于调试从先验分布采样的问题。
- 如果增加方差导致不连续的后验分布,则增加调整样本大小和样本数量,以便更有效地对空间进行采样。
未来的工作
虽然这为我们的参数产生了令人满意的估计,但我们经常遇到采样器不能有效地执行的问题。对于未来的工作,有几种方法可以诊断和改进建模过程。下面按难度递增的顺序列出了这些问题:
- 增加调整大小和抽取的样本数。
- 减小采样器的 target_accept 参数,以减少样本间的自相关。使用自相关图来证实这一点。
- 向观测数据中添加更多样本,即增加采样频率。
- 为参数使用更好的先验和超先验。
- 使用模型的替代参数化。
- 将社交距离测量等变化纳入模型。
了解更多信息
你可以在我的 Coursera specialization(https://www.coursera.org/specializations/compstats)了解更多关于这些主题的信息,该课程包括以下课程:
- 贝叶斯统计导论(https://www.coursera.org/learn/compstatsintro?专门化=compstats
- 贝叶斯推理与 MCMC(【https://www.coursera.org/learn/mcmc? 专门化=compstats
- 用于贝叶斯建模和推理的 PyMC3 简介(【https://www.coursera.org/learn/introduction-to-pymc3?】T4)专业化=compstats
参考
- Priesemann 小组的工作
https://github.com/Priesemann-Group/covid_bayesian_mcmc
2.Demetri Pananos 在 PyMC3 网站上的工作
3.太阳之歌
https://github.com/aseyboldt/sunode
4.闪光
利用大转移预测疟疾
使用迁移学习将血液涂片分类为未感染或感染了引起疟疾的寄生虫

Pixabay 在像素上拍摄的照片
迁移学习是利用在一项任务中获得的知识来解决另一项任务的过程。先前训练的模型被用作基础,在其上添加一个层来微调模型以解决新的任务。
本博客的主要目的是让读者了解什么是大转移,以及如何以最小的努力将它微调并应用于与预训练模型完全不同的领域中的问题。
在的论文“医学成像规模的监督转移学习”中,来自谷歌的研究人员证明,使用大转移( BiT ),可以提高各种医疗任务的准确性,如乳房 x 线摄影癌症识别、胸部 x 射线解释和皮肤状况分类。该论文还发现,使用 BiT 模型,他们仅使用 30%-60%的训练数据就能够实现基线性能。
在此之前,研究人员不清楚对一组自然图像的迁移学习如何适用于医学图像,因为图像大小的差异、域的差异以及使用医学图像中纹理的局部变化来检测感兴趣的病理。
我看到了 Mark Rober 的视频,讲述了斯坦福大学的一个团队如何开发了一台显微镜( Foldscope )和一台纸离心机( Paperfuge ),总共花费 68 美分,可以由一名训练有素的专业人员用来查看幻灯片和检测疟疾等疾病。虽然我没有设法得到从 Foldscope 收集的幻灯片图像数据集,但我发现了另一个数据集,其中幻灯片图像是由连接到传统光学显微镜的智能手机收集的。这一点很重要,因为在最需要这些检测的贫困社区,缺乏设备和资源是这些检测不容易获得的主要原因。

数据集的未感染(左)和寄生(右)载玻片示例
将所有这些放在一起,以及我对测试在医学图像数据集上微调 BiT 有多容易的兴趣,我决定在这个疟疾数据集上尝试大转移。
本博客将让您了解使用位超规则(BiT-HyperRule)对特定任务的位模型进行微调是多么容易,位超规则是一种启发式方法,用于确定所有用于微调的超参数,并获得很好的结果。比特超规则是一个很好的起点,这也是我将在本教程中经历的。我假设读者具备深度学习的基础知识。你可以在这里查看这个教程的 Jupyter 笔记本。
大传输(位)
BiT 的主要特点是使用 ImageNet-21k 和 JFT-300M 等大型数据集进行预训练。本教程中使用的模型是 BiT-M,它已经在 ImageNet 21k 上进行了预训练,ImageNet 21k 是一个数据集,包含 21,843 个不同类别的 1,400 万张图像,并基于 ResNet-50x1 架构。还有其他变化的钻头可用,这里列出了。
BiT 的优点是:
- 适用于各种各样的任务,甚至是那些领域不匹配的任务,如医学成像
- 使用位超规则,可以获得很好的结果,并且可以通过进一步调整超参数来改善这一点(下面将详细介绍)
- 模型的快速收敛。
记住这一点,让我们开始吧。
我建议在阅读本教程之前,先浏览一下在这里找到的的比特文件。
数据预处理:
该数据集包含 27558 个图像,未感染和寄生的载玻片图像之间平分。然后,我们将数据集分成 3 个不同的子集:训练集、验证集和测试集。训练集用于训练模型,然后基于验证集的结果,选择最佳模型。通常,模型的超参数将使用验证集进行调整,但是因为我们使用位超规则,所以我们将只使用验证集来选择最佳模型。最后,测试集用于评估模型的最终性能。请注意,测试集应该是独立的,永远不要用于调整模型。
在这种情况下,训练集包含 90%的图像,验证集包含 9%的图像,最后测试集包含 1%的图像。
train_size = int(0.90 * num_examples)
val_size = int(0.09* num_examples)
test_size = int(0.01 * num_examples)full_dataset = ds.shuffle(reshuffle_each_iteration=False, buffer_size=len(ds))
train_dataset = full_dataset.take(train_size)
test_val_dataset = full_dataset.skip(train_size)
val_dataset = test_val_dataset.take(val_size)
test_dataset = test_val_dataset.skip(val_size)
数据扩充和标准化;
收集和标记数据是一个昂贵的过程。数据扩充是用现有数据毫不费力地创建更多数据的方法。通过修改现有图像,如旋转、翻转,可以从现有图像生成新图像。其他数据扩充方法包括裁剪等技术,但在这种情况下不使用,因为它可能会导致图像的主要特征被删除。
features['image'] = tf.image.random_flip_left_right(features['image'])
features['image'] = tf.image.random_flip_up_down(features['image'])
我们还对图像数据进行了归一化处理,使该值介于 0-1 之间。这有助于确保数据具有相似的分布,从而有助于模型更快地收敛。这也有助于提高模型的稳定性。
features['image'] = tf.cast(features['image'], tf.float32) / 255.0
添加输出图层:
因为我们将使用的位模型是一个特征提取器,所以我们需要添加一个输出层,神经元的数量等于类的数量(本例中为 2)。然后,我们将该层的权重初始化为 0。
class MyBiTModel(tf.keras.Model):
"""BiT with a new head."""def __init__(self, num_classes, module):
super().__init__() self.num_classes = num_classes
**self.head = tf.keras.layers.Dense(num_classes, kernel_initializer='zeros')**
self.bit_model = module
使用位超规则设置超参数:
- 批量:512 个
- 学习率:0.003
- 计划长度:500
- 计划界限= 720,1440,2160
比特超规则规定,对于具有小于 20 k 的标记示例的数据集,调度长度应该是 500 步,而那些具有小于 500 k 的标记示例的数据集应该是 10 k 步。因为这个问题中的标记示例数是 27558,刚好超过 20 k 标记示例边界,所以我决定坚持 500 步。
初始学习率是 0.003,并且在步骤 720、1440、2160 中衰减 10 倍。这些步骤分别占整个训练步骤的 30%、60%和 90%
提前停止回调以防止过拟合:
为了防止过度拟合,使用了早期停止,这是一种在被监视的度量没有按预期表现时停止训练的方法。在这种情况下,如果验证损失连续 10 个时期没有减少,则停止训练,并且恢复来自具有最小验证损失的时期的模型权重。
es = EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=10,restore_best_weights=True)
优化器和损失函数:
使用带动量的随机梯度下降优化器。选择的损失函数是稀疏分类损失函数。当标签的数量为 2 或更多时,使用该损失函数,并且它期望类别标签是整数。
optimizer = tf.keras.optimizers.SGD(learning_rate=lr_schedule, momentum=0.9)
loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
训练模型:
现在是激动人心的部分,按回车键+回车,让模型训练!完成这一步后,去给自己做一杯热饮吧,这是你应得的!
model.compile(optimizer=optimizer,loss=loss_fn,metrics=['accuracy'])
在这种情况下,验证损失在时段 31 之后没有减少,因此从时段 31 开始恢复并使用。
列车运行前检查:
在花费时间和资源在 x 时代上训练模型之前,重要的是执行某些健全性检查以确保模型按预期执行。
最重要的是检查输出值是否有意义。在分类模型的情况下,概率的总和值应该是 1。
test_logits = model(test_image) #running the model on a test image
**sum_proba = (np.sum(tf.nn.softmax(test_logits).numpy()[0]))** #summimg the probabilities
#sum_proba should be 1\.
列车运行后检查:
重要的是,对于特定的图像,当图像稍微改变时,例如旋转或翻转,模型输出不会改变。这被称为不变性检查。为了测试这一点,我们增加了测试集中的图像,并检查模型的预测在单个图像的所有不同变化中是否一致。
Pseudocode for post-train check:
1\. Classify image with the trained model
2\. Augment the image (rotate/ flip) and then classify it again
if result from 1 == result from 2:
Model passes post-train check
else:
Retrain model such that it is invariant to augmentations which does not change the main features of the image.

用于不变性检查的旋转图像
评估模型:
一旦训练完成,我们确保用来自最佳时期的权重来拟合模型。在这种情况下,这是验证损失最小的模型。
model.load_weights('best_model')
然后,我们可以通过查看以下指标来继续分析该模型:
- 测试集上的准确性:使用 274 幅图像的整个测试集,我们测试它的准确性。我们发现准确率高达 97.46%
2.混乱矩阵:

混淆矩阵允许我们以 NxN 矩阵的形式评估模型,其中 N 是类的数量。它让我们很好地了解模型的性能以及真阳性、真阴性、假阳性和假阴性的数量。该数据还可以用于重新训练模型,以改进特定类别的结果。例如,在医疗保健环境中,假阴性(当患者实际上患有疾病时,他/她被确定为健康)比假阳性(当他/她实际上没有疾病时,患者被检测为患有疾病)成本更高。
这里,错误分类的例子的数量是 7,其中 5 个被错误地预测为寄生。其他两个图像被预测为未感染,而它们实际上是寄生图像。
3。ROC-AUC:

受试者工作特征(ROC)曲线下的面积是衡量模型性能的一个非常重要的指标。以下摘录来自对 ROC-AUC 曲线的简单解释此处:
“AUC-ROC 曲线是在各种阈值设置下对分类问题的性能测量。ROC 是概率曲线,AUC 代表可分性的程度或度量。它告诉我们这个模型在多大程度上能够区分不同的类。AUC 越高,模型预测 0 为 0 和 1 为 1 的能力越强。以此类推,AUC 越高,模型在区分患病和未患病患者方面就越好”
结论:
使用 BiT-HyperRule 得到的模型在验证集上的准确率为 96.81%,在测试集上的准确率为 97.46%,ROC-AUC 为 0.99。这表明 BiT 在对医疗数据集进行分类时表现出色,即使它所训练的图像在性质上非常不同。
一个有趣的观察是,在开始稳定之前,验证损失和准确性在最初的几个时期急剧下降。

验证损失与纪元图
这可能是由于学习率和批量大小。当用不同的超参数进行试验时,即通过将批量减少到 64(而不是 512)并将学习率从 0.003 降低到 0.0001,发现验证损失遵循递减趋势,并且不像上图那样波动。但是,在得出任何结论之前,还需要进行进一步的实验。
人工智能有助于让这些疾病的诊断变得更便宜、更容易,当大规模应用时,这将产生巨大的影响。我很乐观,并且真的相信人工智能可以真正彻底改变医疗保健的状况,特别是在发展中国家和不发达国家,那里最需要廉价和容易获得的医疗保健,而且疟疾等致命疾病仍然存在。
感谢谷歌的研究人员,他们研究了比特技术,也感谢谷歌实验室笔记本的作者展示了如何使用比特技术。
“医学成像规模的监督迁移学习”的作者指出,模型可以通过超参数优化进一步改进。在下一篇博客中,我将尝试通过使用定制的超参数来提高模型的性能。所以请继续关注第 2 部分!
感谢阅读。如果你有任何问题,请在下面评论。当这个博客的第二部分出来的时候,请跟随我在 Medium 上阅读它。我在LinkedIn和Twitter上也有空。希望你有美好的一天🙂
使用 BigQuery 数组分析我的网飞观看历史
如何在 SQL 中进行高级数据清理
介绍
当我听说你可以从网飞那里获得你所有的个人资料时,我很兴奋地想看看我能找到什么。最后,我不仅对自己有了更多的了解——比如我最常看《老友记》的哪一季(第六季)——而且这也是一个检验 BigQuery 分析能力的绝佳机会。本文介绍了我如何在 BigQuery 中完成最复杂的转换,但是您可以在这里找到完整的项目:
https://count.co/n/0erWed0YsMF
为什么是 BigQuery?
也许显而易见的问题是,为什么我选择在 BigQuery 中进行这种分析,而在 Python 中却可以轻松完成。嗯,我有几个原因:
BigQuery 很快
尤其是大数据。现在,这些数据并不大,但是如果大的话,用 Python 进行这种类型的转换将需要很长时间。因此,为了迎接不可避免地需要对大数据进行大量字符串和数组操作的那一天,我想我应该准备好我的 BigQuery 技能。
并且缺乏实际例子
在做这个分析时,我意识到在 BigQuery 中进行复杂转换和分析的实际例子远没有 Python 中的多。然而,我和许多仍然依赖并喜欢使用 SQL 进行数据分析的人交谈过。我认为分享我的经验是值得的,这样下一个人就不必像我过去几天那样盯着 BigQuery 的数组文档看很久了。
使用笔记本使 BigQuery 成为这项任务的理想选择
使用 SQL IDE 进行这种类型的分析将是一场噩梦——疯狂的嵌套查询,几十个临时表会让我在不到一天的时间里回到 Jupyter 的怀抱。但是为了这个分析,我使用了一个 SQL 笔记本,这样我就可以一个单元一个单元地分解每个步骤。一切都变了。
声明:我为 Count 工作,所以我是一个粉丝,但也有其他笔记本替代品!
第一部分:准备
当我下载并把数据加载到我的笔记本上时,我注意到网飞并没有帮我把这些数据变得易于分析。

查看历史预览
特别是,他们将所有有趣的信息都放在一个字段中: Title 。
乍一看,我们似乎可以通过“:”来拆分标题,以获得标题 : 第四季(如果可用):第五集(如果可用)。
唉,这并不适用于以下情况:
- 片名中带有“:”的电影如 《星球大战:第七集:原力觉醒》
- 标题中带有“:”的显示:如 《虎王:谋杀、伤害与疯狂:限量系列:废黜(第七集)》
- 多集如 【办公室(美国)】:第六季:尼亚加拉:第一集(第四集)
只要稍微复杂一点,我们就能大幅提高精确度。具体来说,我们可以做到以下几点:
- 将每个标题归类到节目的类型:电视节目、限量系列或电影和特别节目
- 对于电影,我们将保持标题不变——不拆分。
- 对于电视节目和限量系列,我们将按“:”进行分割,
- 找到标题与季节相关的地方(即系列、系列等。)并称之为季,
- 将赛季前的串联成标题,
- 并把第季之后的一切串联成集。
当然还有其他方法,但这是对我来说最有意义的方法。该方法需要对 BigQuery 中的字符串和数组函数有很好的了解。
让我们用几个例子来看看它的实际应用:

例子
1.将我们的标题拆分成多个元素

2.查找包含季节信息的元素:
使用 REGEXP_CONTAINS 和合并

3.提取我们的标题,季节,和插曲

第二部分:分析
清理完数据后,我就可以开始探索了!事实上,去年我看《T4》和《网飞》的时间比前几年要少,但我的口味并没有太大变化——我仍然主要看电视节目。

然而,我在 2020 年暴饮暴食的次数(在 7 天或更少的时间里观看一季 7 集以上)比以往任何时候都多。

最后,我不得不看看我到底看了多少朋友。答案是:很多,尤其是和我其他的顶级电视剧相比。


我们都知道会这样。
由于我在网飞看的电视节目比其他任何东西都多,我决定建立一个互动报告来比较我在不同时间观看不同节目的习惯。这里有一个预览:

您可以在此查看整个项目,从清洁、探索到互动报告:
https://count.co/n/0erWed0YsMF
结论
除了了解我个人的查看习惯和偏好之外,这次练习也是我了解 BigQuery 强大功能的一个很好的方式。我真的对自己能够如此轻松地复制我在 Python 中应用的相同类型的逻辑印象深刻。
有用的资源:
如果您想进一步了解笔记本电脑如何改变您和您的团队处理数据的方式?伸出手去 [我](mailto: taylor@count.co) 还有剩下的 算一算 团队了解更多!
利用脑-机接口和脑电信号对情绪进行分类
我们能从脑电波中学到什么?

Unsplash.com 的乔希·里默尔图片
神经科学和脑电信号
脑电图是由电极测量的大脑活动的记录。汉斯·伯杰在 1924 年首次记录了脑电图信号,这是一个令人难以置信的发现,它导致了一个至今仍在进行大量研究的研究领域,其中有许多未知因素。脑电信号的采集是非侵入性的,电极用凝胶或浆糊放在头皮上。出于医学原因,EEG 信号最常见的用途包括癫痫研究和睡眠研究。它们也被用于发现脑损伤、脑部炎症和中风。
DEAP 数据集
该数据集由伦敦玛丽女王大学创建,可在 https://www.eecs.qmul.ac.uk/mmv/datasets/deap/index.html访问。数据集对公众开放使用,并要求签署发布表格。
数据集包括:
32 名参与者
40 个通道(前 32 个通道为脑电)
40 个一分钟视频
标签:效价、唤醒、支配、喜欢、熟悉、顺序
脑电通道名称- 'Fp1 '、' AF3 '、' F3 '、' F7 '、' FC5 '、' FC1 '、' C3 '、' T7 '、' CP5 '、' CP1 '、' P3 '、' P7 '、' PO3 '、' O1 '、' Oz '、' Pz '、' Fp2 '、' AF4 '、' F4 '、' F8 '、' FC6 '、' FC2 '、' Cz '、' C4 '、' T8 '、' CP6 '、'
项目动机
虽然 EEG 信号通常用于医疗实践,但这项研究的重点是 EEG 信号是否以及如何用于更主观的方法,如人类的情感和情绪。通过整理参与者关于音乐视频(主观)和脑电图数据(客观)的调查,我们可以开始了解是否有可能从脑电图信号中预测情绪。
可视化和信号处理 Python 库
这项研究中主要使用的 python 库是 MNE-Python,这是一个开源的 Python 包,用于分析人类神经生理数据,包括脑磁图、脑电图和其他信号。
传感器位置
电极是小金属盘,根据 10/20 放置规则有策略地放置在头皮上。根据受试者头部的固定位置测量这些电极的位置。下面,我们可以看到该数据集实验中使用的 Biosemi 系统的传感器位置。

Biosemi32 系统头部的传感器位置(图片由作者提供)
本实验中的 Biosemi 系统是一个 32 通道系统,用于研究环境,不用于医疗用途。
偶数指头部右侧,奇数指左侧。传感器的命名惯例取决于传感器的位置:

原始的、未经处理的脑电图信号(图片由作者提供)
F —额叶
T —颞叶
C —中央叶
P —顶叶
O —枕叶
处理脑电信号
信号预处理的目的是提高信噪比和检测实验效果。这是通过带通滤波来实现的(它通过一定范围内的频率,而拒绝该范围之外的频率)。此外,在预处理期间,死通道被丢弃,伪像被去除。
标记不良频道
重要的是标记坏的信道,即故障的、未使用的或不显示任何信号的信道,以便将它们从信号分析中排除。我们可以在下面的第一个图中看到,至少有一个坏通道。错误消息告诉我们通道是 GSR2。我们会将此标记为坏通道,并重新绘制图形,看看是否可以更好地看到其他通道。我们还从原始数据集描述中了解到,通道“Erg1”和“Erg2”也不是使用过的通道,因此我们也将它们标记为坏通道。我们可以在下面的第三个图中看到,未使用的通道不再包括在内。

(图片由作者提供)
我们还知道数据集中还有其他非 EEG 通道(例如皮肤电反应),因此我们也将这些通道排除在外,因为我们不会在模型中使用它们。现在,我们已经确定了所有未使用的和非脑电图通道,我们可以设置蒙太奇。这将给出传感器的位置,并将它们转到收集数据的系统(Biosemi32)。在上面的最后一张图中,我们只能看到 EEG 通道,这些通道用颜色将信号与其传感器位置关联起来。

功率谱密度图(图片由作者提供)
通过期望的通带过滤数据
脑电波中最有用的信息存在于 30hz 以下。我们可以在这张功率谱密度图中看到,频率在 30-40hz 之间下降,因此出于研究目的,我们将截止 30hz。
检测伪影并用 ICA 消除
在决定选择哪种方法来修复工件之前,可视化并观察数据中的工件是很重要的。有三种类型的伪像会干扰 EEG 数据。其中包括环境(电线、摔门、电梯噪音、手机、空调等)仪器仪表(头皮连接不良、电磁干扰)生物假象(心跳、眨眼、吞咽)。

(图片由作者提供)
首先,我们将从数据中移除 SSP(信号空间投影)投影仪。这是一个矩阵乘法,通过将数据投影到一个更低维度的子空间来降低数据的秩。
重要的是检测伪影(视觉和心跳),以便确定它们是否足够重要而需要修复,并且还确定使用哪个工具来修复它们。我们将使用独立分量分析(ICA ),它试图将多元信号分解成独立的非高斯信号。

眨眼伪影出现在不同的通道上(图片由作者提供)
寻找事件和时期
一旦信号被处理,我们就能够识别信号中的事件。事件由刺激通道标记。对于这个数据集,刺激通道被称为“状态”。状态通道有 7 个事件标记。这些标记事件如实验开始、音乐开始、出现注视屏幕、实验结束等。这些事件标记可以对 EEG 信号进行切片以创建历元,这些历元是从连续 EEG 信号中提取的特定时间窗口。下面的第一个图像显示了这个数据样本中的所有事件标记。第二个图像是一个小的时间窗口,事件标记覆盖在信号的顶部。

一个数据样本中的标绘事件(图片由作者提供)

脑电图数据覆盖的标绘事件(图片由作者提供)
被拒绝的纪元
当我们搜索事件时,我们能够执行另一种拒绝数据的方式,这意味着眨眼(我们记得这是一种假象)。下图非常有趣,因为我们可以看到额叶中的传感器拒绝的数据比例最大。这符合常识,即额叶最靠近眨眼发生的眼睛,因此产生最强的伪像。

被拒绝的纪元的百分比(按作者分类的图像)
机器学习——监督
在数据集上测试了几种分类模型,包括 KNN、决策树分类器、随机森林分类器和袋装树。还执行了 GridsearchCV 来超调参数。

目标标签:参与者对视频的熟悉程度(图片由作者提供)
结论和前进
不幸的是,EEG 信号有一些缺点。EEG 信号具有较差的空间分辨率(距离头皮约 10cm2 ),导致需要进行密集的解释,以了解特定响应激活了大脑的哪些区域。从这项研究中我们也可以看出,脑电信号也很难处理。因此,向前推进,改进该研究结果的一种方法是进一步处理和测试信号。此外,我们可以在我们的机器学习模型中看到,我们只对“熟悉度”进行了实验,但可以做一个回归问题来解决效价、唤醒和支配。
此外,KNN 模型最初在所有模型中表现最好,但由于数据集(1 个参与者样本)较小,没有一个模型表现得令人难以置信地好。因为这项研究有 32 名参与者,所以将所有的 EEG 信号解释到一个更大的机器学习模型中会很有趣。
参考文献
- A.Gramfort,M. Luessi,E. Larson,D. Engemann,D. Strohmeier,C. Brodbeck,R. Goj,M. Jas,T. Brooks,L. Parkkonen,m . hml inen,使用 MNE-Python 进行脑磁图和脑电图数据分析,神经科学前沿,第 7 卷,2013 年,ISSN 1662–453 x,【DOI】
- 哈斯,L . F .(2003 年)。“汉斯·伯杰(1873–1941),理查德·卡顿(1842–1926),以及脑电图”。神经病学杂志&神经外科杂志。 74 (1): 9。doi:10.1136/jnnp . 74 . 1 . 9。PMC1738204。PMID 12486257 。
- 舍雷尔,“什么是情绪?以及如何衡量它们”,社会科学信息,第 44 卷,第 4 期,第 695-729 页,2005 年。
- 南 Koelstra,C. Muehl,M. Soleymani,J.-S. Lee,A. Yazdani,t .易卜拉希米,T. Pun,A. Nijholt,I .帕特雷,“DEAP:使用生理信号进行情感分析的数据库”, IEEE 情感计算汇刊,系统构建和评估的自然情感资源特刊,正在出版
使用 Codex 将 Keras 翻译成 fastai

作者图片
Codex 能成为深度学习框架的中间人吗?
OpenAI 的 Codex 提供了一组特定于计算机语言的操作,可以用来从英语语言描述中生成代码,自动记录代码,并在编码语言之间进行翻译。在过去的几个月里,我一直在探索 Codex 的能力,包括它从英语提示中生成 web 应用程序的能力和从 COBOL 中生成现代代码的能力。我很好奇 Codex 将如何处理两个不同深度学习框架之间的翻译。
挑战
我有幸写了两本书,一本是关于使用结构化数据的深度学习的,其中展示了用 Keras(tensor flow 的高级深度学习框架)编写的深度学习示例,另一本是关于使用 fastai 的[深度学习的,fastai 是基于 PyTorch 构建的高级框架。在写第二本书的过程中,我需要创建一个现有 Keras 应用程序的 fastai 实现。当我写这本书的时候,我通过反复试验“手工”创建了 fastai 应用程序,直到我得到大致相当于 Keras 代码的工作代码。如果我在写书的时候能够接触到 Codex,我会利用它从 Keras 自动生成 fastai 吗?在这篇文章中,我将描述一个实验来回答这个问题。](https://www.amazon.com/Deep-Learning-fastai-Cookbook-easy/dp/1800208103/ref=sr_1_1?crid=3SYARIO8M3ZU3&dchild=1&keywords="deep+learning+with+fastai+cookbook"&qid=1635994009&qsid=130-4637480-5001025&s=books&sprefix=deep+learning+with+fastai+cookbook+%2Cstripbooks-intl-ship%2C90&sr=1-1&sres=B0971LB85Z&srpt=ABIS_BOOK)
MNIST——深度学习的“你好世界”
你可以解决的最简单的深度学习问题是使用手写数字的 MNIST 数据集来训练一个模型,该模型可以预测手写数字图像中描绘的数字。

来自 MNIST 的示例图像
你可以说,为 MNIST 创建一个模型是深度学习的“你好世界”。
我关于 fastai 的书包含了 MNIST 模型的 Keras 和 fastai 实现的并排比较。这个比较说明了这两个框架之间的差异,并为熟悉 Keras 的开发人员探索 fastai 奠定了基础。
实验
为了锻炼 Codex 从 Keras 翻译到 fastai 的能力,我决定采用我为我的书手工转换成 fastai 的相同的 Keras MNIST 实现,看看 Codex 是否可以正确地完成翻译。
以下是 Keras MNIST 实现的起点,改编自https://github . com/tensor flow/docs/blob/master/site/en/tutorials/quick start/beginner . ipynb:
克拉斯 MNIST 模型
为了让 Codex 将它翻译成 fastai,我使用了以下提示模式:
// Rewrite this as a fastai model[ Keras code goes here ]// fastai version:
食品法典委员会得出的结果看起来像 fastai 一样合理:
fastai 输出从 Keras 输入产生 Codex
我把这段代码复制到了 Colab 笔记本上,但是它不能正常工作。为了让它在 Colab 中正确运行,我需要做两处修改:
- 用笔记本的 include 语句替换 include 语句:
#import fastai
#from fastai.vision.all import *
!pip install -Uqq fastbook
from fastbook import *
from fastai.vision.all import *
- 注释掉编译语句:
#hello_world_model.compile(optimizer=optim.Adam,
# loss_func=nn.CrossEntropyLoss(),
# metrics=[accuracy])
通过这些改变,我能够在 Colab 中成功运行 fastai 代码,并获得一个有效的 MNIST 深度学习模型:

由 Codex 训练 MNIST 模型生成的 fastai 代码
虽然 Codex 生成的 fastai 代码进行了一些小的修改,但它并不完全等同于 Keras 输入代码。特别是,输入的 Keras 代码包括显示测试数据集的模型损失和准确性的语句:
test_scores = hello_world_model.evaluate(x_test, y_test, verbose=2)
print('Loss for test dataset:', test_scores[0])
print('Accuracy for test dataset:', test_scores[1])
Codex 生成的 fastai 代码包括以下语句,用于在测试集上训练模型,但它并不像原始 Keras 代码那样显示聚合结果:
test_dl = dls.test_dl(get_image_files(path/'testing'))preds,_ = learn.get_preds(dl=test_dl)
结论
给定 MNIST 深度学习模型的 Keras 代码作为输入,Codex 能够产生 fastai 代码作为输出(经过几次小的修改),成功训练了 MNIST 深度学习模型。以下是我的主要观察结果:
- 一方面,这令人印象深刻。使用 Keras 框架的代码与使用 fastai 的代码有很大不同,作为一个人,我花了几次尝试,以 Keras 代码为起点,为 MNIST 开发 fastai 代码。Codex 第一次尝试就产生了(几乎)正常工作的 fastai 代码。我不必摆弄提示或对输入的 Keras 代码做任何更改。
- 另一方面,我不得不怀疑 Codex 是否简单地“记住”了作为 github 语料库一部分的 Keras 和 fastai MNIST 实现。例如,我用 fastai 书编写的[深度学习的代码(包括](https://www.amazon.com/Deep-Learning-fastai-Cookbook-easy/dp/1800208103/ref=sr_1_1?crid=3SYARIO8M3ZU3&dchild=1&keywords="deep+learning+with+fastai+cookbook"&qid=1635994009&qsid=130-4637480-5001025&s=books&sprefix=deep+learning+with+fastai+cookbook+%2Cstripbooks-intl-ship%2C90&sr=1-1&sres=B0971LB85Z&srpt=ABIS_BOOK)这个手写的 fastai MNIST 应用和它的同伴 Keras MNIST 应用)自 2021 年年中以来一直在 github 中,所以 Codex 的训练集可能包括了我书中的例子,而 Codex 所做的只是从它在训练集中看到的手写的 MNIST 应用中“查找”fastai 语句。
总的来说,我不得不说,我对 Codex 能够从 Keras 代码中生成(几乎)有效的 fastai 代码印象深刻。甚至将“hello world”从一个框架自动翻译到另一个框架也是一种成就。我期待着用更具挑战性的 Keras 到 fastai 翻译来测试 Codex。
与本文主题相同的视频:https://youtu.be/xUMBiMP4xNw
利用认知科学和教育科学来改善你交流结果的方式
办公时间
提高演讲技巧的八个技巧

由 Austin Distel 在 Unsplash 上拍摄
你们中有多少人在找工作时,在“必备技能”部分看到了“强大的沟通技能”?如果不是特别明确,肯定会有“人际交往技能”或“表达技能”之类的东西。这些在每个行业都很常见,所以最好问一句,你见过不具备上述条件的招聘信息吗?
好消息是,就像其他技能一样,我们可以通过练习和对要点的理解变得更好。在本文中,我们将重点应用认知科学和教育科学中的一些基于证据的结论,以改善我们作为数据科学家和分析师交流结果的方式,尽管这些观点可以推广到任何你发现自己在公开演讲的应用中。如果你想查看原始资料,你可以通读这些 学术 论文。
回到数据科学家;让我们面对现实吧,我们这些花时间埋头于数字的人并不总是最擅长用一种令人信服和易于理解的方式来解释它们。但这有点傻,对吧?无论我们发现模式和做出推论的能力有多好,如果我们不能让团队成员相信这些模式,我们的结果将没有任何影响。作为一个能够应对交流结果这一挑战的人,一个需要对好的演示中隐含的技巧进行一些阐述的人,我发现以下技巧很有帮助。当你浏览这个列表时,我鼓励你思考一下最近的演示如何通过应用来改进。所以,事不宜迟:
1.细致的调查
精心调查“……促使学习者对明确陈述的事实做出解释。”通过提问,我们在社交上吸引了我们的听众(稍后会有更多介绍),并鼓励听众处理正在呈现的信息,增加注意力。作为数据科学家,我们可以通过要求团队成员回忆以前幻灯片或会议的相关结论,或者利用每个人都可能拥有的一些领域知识,来进行精心的调查。精心调查和其他通过提问参与的方法之间的一个重要区别是听者能够回答问题;通过这种技巧,我们有意让听众将相关信息带入他们的头脑中。
2.实践测试
本节的标题不完全具有代表性;我们可能更喜欢在头脑中使用的术语可能是修辞或引导提问。我们不想测试我们听众的知识,因为这会让他们感觉如何,尤其是在一个问题的情况下,我们,演示者,将是唯一知道答案的人,这通常是在演示结果时的情况。然而,标题可能对读者有类似的影响,这导致了这种技术是有益的:

图片来自 knowyourmeme.com
回想一下学校里的考试公告,或者更糟的,可怕的突击测验。它们可能会让你觉得自己很像皮卡丘,感到惊讶并希望你知道更多。学习时的压力显著地促进了记忆的形成,然而,对于记忆回忆来说,情况正好相反。因此,我们对测试同事不感兴趣;事实上,当在演示过程中提问时,我们通常根本不期望他们回答。作为数据科学家,我们将利用这种压力反应以及由此带来的记忆回忆和大脑参与的改善,通过问一些修辞性或引导性的问题。一个有效的实现可以是展示初步数据,并简单地问一些类似这样的问题,“现在,从我们目前所看到的,我们可以推断出什么?”重要的是,在你的问题之后要有一个短暂的停顿,但要足够长,让听众考虑自己的解释和趋势,或者至少足够长,让他们思考,“嗯,我不确定。”然后,转到下一张幻灯片,用你来之不易的洞察力打击他们,在他们的大脑已经准备好接受它之后,他们更有可能记住和理解。
3.心理意象
继续我们的心理参与方法,我们可以使用意象来提高听众处理新信息的能力。毕竟,我们的眼睛是我们感官信息的主要来源。除了你为阐明而创造的美丽的形象化之外,有许多好的资源可以用来制作,要求听众在转述结论时富有想象力也是有帮助的。人类处理图像的速度比文本快 6 万倍,我们的大脑天生就能处理视觉数据。例如,假设你和一群房地产经纪人坐在一个房间里,并解释说,在房子的其他一切保持不变的情况下,我们的模型表明额外的卧室会使房子的售价下降。这可能感觉违反直觉(房地产经纪人也可能像皮卡丘一样看着你),但如果我们让我们的房地产经纪人朋友想象自己站在他们最近出售的房子里,那么建议他们进一步想象,在总面积、浴室数量等没有增加的情况下。,再加一间卧室……嗯,放在哪里合适?为了打造另一个适合居住的卧室,我们必须重新利用现有房间的空间,让它们变得更加拥挤,更多的人最终会共用相同数量的浴室,等等。通过创造心理图像,我们可以用一种相关的、容易理解的方式传达结果,甚至那些一开始可能没有意义的结果。

XKCD 漫画
4.重复和突出显示
虽然教育研究表明这两种方法实际上都不是很好的学习技巧,不管它们有多受欢迎,但我们可以从它们糟糕的原因中获得价值。研究人员认为,突出显示尤其无效,因为你必须已经知道哪些信息是有价值的,才能突出显示正确的信息。幸运的是,我们确实知道什么信息是重要的,可以为我们的观众做重点介绍。这可以通过突出可视化的重要特征来实现,也可以通过将我们的幻灯片文本限制为简洁地捕捉主要思想的几个要点来实现,不过,如果你像我一样,实际的讨论将会冗长得多。最后,虽然我们将在下面进行有效的总结,但我们可以通过在每张幻灯片的结尾收集我们的想法,并通过陈述“继续,但这张幻灯片的主要内容是 _____,稍后您会看到为什么这很重要”来进行简短的总结,从而将重复作为一种突出的类比
5.社会和代理包容
离开时对团队环境感觉积极的听众更有可能关注我们的结果,因为他们更有可能将注意力放在我们身上,并对我们作为同事充满信心。社会包容可以包括指名道姓地提及个人或团队,并在演讲前后到场进行交谈或回答问题(我们都知道我们需要提前到场,以确保我们的演示文稿和演示准备就绪并正常工作!).如果我们碰巧在一天的早些时候看到同事的评论,我们也可以给他们留言,比如,“你会很兴奋地看到我今天下午演示的一些结果!”代理包容,“代理”指的是一个人对其环境施加影响的能力,激励听众在结果中感受到自己的存在,或感受到自己是引导演示流程的一部分。一定要承认其他团队和个人的努力,因为嘿,我们都在一起;创造数据洞察力是我们应该做的。允许观众提问,同时注意尊重时间限制,不要轻视或允许偏离我们精心制作的故事。建议稍后进行进一步的对话,以确保团队不偏离正轨,这是完全可以接受的。提出一个答案是“谢谢你,丽莎,很高兴你问了”的问题,这种感觉总是很棒。看到这种模式,我也想知道……”然后点击下一张幻灯片,上面有漂亮的可视化效果和谈话要点,正是为了满足这种好奇心而准备的。即使他们没有问这个问题,这也是我们真正追求的数据驱动的逻辑流程。
6.为不同的认知风格做好准备
我们需要了解我们的观众。我们是否在与数据团队的其他成员交流?软件部门的朋友,编码背景好但不一定懂数据?或者是销售部门的人,我们依赖他们面对客户的专业知识,或者是你向其解释项目的面试官,他们可能根本没有技术背景?是混合团吗?确保准备演示时考虑到目标受众。此外,在每个小组中,认识到存在不同的认知风格;有些人视觉学得更好,而有些人听觉学得更好。有许多原因,甚至在相似的个体中,为什么一个人的理解力会受到挑战。让我们尽最大努力为回答问题做好准备,记录下我们一路上遇到的困难,同时也考虑到由于我们独特的背景和经历,我们认为理所当然的信息可能会让其他人感到困惑。最后,如果经常向同一群人演示,注意他们问的问题类型和证明有用的可视化。我们可以做很多事情来改善我们的结果沟通,认识到重复的问题或同一类型的多个问题通常意味着我们未能以某种方式在我们的报告中包含必要的信息。
7.让您的结果令人难忘
为了让演示产生持久的影响,帮助提高我们的知名度,并让同事们期待我们未来的成果,我们应该在我们系统的工作环境中设定结果。我们刚刚花时间详细介绍了一个具体的结果;让我们不要忘记提醒每个人为什么我们首先对这个结果感兴趣。如果结果产生进一步的问题,解释未来的探索路径,并在必要时请求其他个人和团队的支持。如果提出问题的解决方案,说明对日常操作程序的影响。然而,作为数据科学家,我们可能不负责有关方向的决策,但我们可以利用这一点来增加我们的演示的普遍性。我们可以问一些问题,比如调查结果会如何影响我们的团队和邻近的团队,或者引发一场关于下一次调查应该关注什么的讨论,此外,我们还可以问一些其他问题,这些问题会让听众离开,通过我们的结论对下一步产生广泛的疑问。以“好吧,让我们在接下来的几天里都考虑一下,然后在我们的下一次会议上再回来”来结束一次会议可能会很好。吸引演示空间之外的同事的注意力,阐明结果的影响,通过增加其他人对其价值的感知,并给他们一个对下一部分好奇的理由,增加他们关注的欲望,使我们的演示更好。
8.摘要
在一次彻底的演讲后,听众会得到很多新的信息。对于回忆和持久的理解来说,重要的是确定想法之间的联系,以及哪些想法和结果是最重要的。花时间做总结,不要从最后一张幻灯片上读出一系列要点。虽然视觉上表现这些点是好的,但我们的口头表达应该以连贯的方式连接这些点,从头到尾总结这个故事。

图片来自 memegenerator.net
在这里,我将尽我所能写一份有效的总结。虽然我们已经介绍了很多技巧,但是所有的技巧都是通过引起听众的注意和让听众参与到信息处理中来实现的。我们通过提问、鼓励对旧问题的反思以及引导他们接受新问题来吸引他们。我们通过包容让他们参与进来;在社交方面,通过创造积极的工作环境;在代理方面,通过形象地回答问题,以个人为基础,并认可他人的贡献。最后,我们让他们参与进来,确保通过有效使用可视化和以重复信息的形式创建“点”作为小摘要来突出重要信息,然后在最终的连贯图片中“连接这些点”。通过这种方式,我们的观众会在离开时感受到我们的结果的价值,以及它在我们整个工作环境中的位置。
如果你已经走了这么远,谢谢你!现在你已经读了这篇文章并且理解了这些技巧…我在写作中使用了它们中的哪一个?我是否遗漏了什么,我的陈述如何才能更好?有没有我做过的有效但没有列在清单上的事情?你有没有最喜欢的老师和演讲者,他们有没有使用这里没有提到的技巧?请在评论中告诉我你的想法!
利用复杂网络改进机器学习方法
使用高级数据分类算法来利用数据的拓扑特征

艾莉娜·格鲁布尼亚克在 Unsplash 上的照片
数据科学为数据科学家工具箱提供了许多不同的范例,如监督学习、非监督学习、时间序列、强化学习等等。要精通所有这些范例是不可能的,但是,对每一个范例都有一个基本的和可行的知识可以帮助我们更好地解决问题。
最近一个为数据科学家提供有洞察力的工具的领域是复杂网络,这是一个专注于研究基于实体之间关系的复杂现象的领域。
在这篇文章中,我将告诉你一些关于复杂网络的知识,以及如何将它们与机器学习算法结合使用,以提高使用 sknet 库进行预测的性能。
复杂网络
让我们从定义什么是复杂网络开始:一个被称为节点的实体的集合,这些节点通过表示某种关系的边相互连接。
如果你在想:这是一个图表!嗯,你是对的,大多数复杂的网络可以被认为是一个图。然而,复杂网络通常会扩展到数千或数百万个节点和边,这使得它们很难用标准的图算法进行分析。
复杂网络和数据科学领域之间有很多协同作用,因为我们有工具来尝试和理解网络是如何构建的,以及我们可以从整个系统中预期什么行为。因此,如果您可以将数据建模为一个复杂的网络,您就有了一套新的工具来应用它。
事实上,有许多机器学习算法可以应用于复杂的网络,也有一些算法可以利用网络信息进行预测。尽管这个交叉点相对较新,但我们已经可以对它进行一些尝试。
因此,为了开始我们连接这两个领域的旅程,现在让我们看看如何将一些表格数据转换成复杂的网络,以及如何对其进行分类。
将数据转化为复杂的网络
从表格数据构建复杂网络的方法有很多。在这里,我将描述一种 KNN 构造方法,该方法使用每个数据点的最近邻来构造网络。
给定一个表格数据集,算法基本如下:
- 选择一个 K 值
- 数据集的每一行都是空间中的一个点,因此将成为一个节点
- 获取空间中的每个点,并计算到其他点的距离
- 从正在评估的节点中选择 K 个最近的节点,并在它们之间绘制一条边
这个想法是连接最近的点。请注意,这种方法不会产生单个节点(没有连接的节点),因为每个节点到另一个节点都有距离。
附注:当用类转换数据时,可能需要每个类都在一个单独的组件上。这是一些算法的要求。
这种方法的脆弱性发生在你有密集的区域,有许多节点彼此靠近。可以想象一个密集的区域也应该是密集连接的,但是 KNN 构造器只会为每个节点创建 K 条边。
还有其他算法试图解决这个问题。一个例子是 Epsilon Radius 构造函数,它在密集区域工作得很好,但是在稀疏区域会产生单值,这就不好了。然后可以结合这两种方法,使用ε半径 KNN 构造函数。所有这些算法都可以在 sknet 库中找到。
高级数据分类
既然我们可以将数据转换成复杂的网络,那么我们该如何利用它呢?嗯,我们可以做很多事情,但在这里,我将侧重于一个特定的算法:高级数据分类。
高级数据分类算法试图将来自传统机器学习算法(如支持向量机和随机森林)的发现与通过分析复杂网络的度量而促进的结构模式识别相结合。
它基本上由两部分组成:
- 一个低级分类器,是我们使用的传统机器学习算法的一个有趣的名字,如支持向量机或 Boosting 算法
- 高级分类器,一种复杂的基于网络的分类机制
然后,该模型将两种结果结合起来生成预测。这里的想法是,复杂网络机制能够识别数据的拓扑方面,而低级分类器将不能容易地识别这些方面。
但是这种复杂的网络机制是如何工作的呢?基本上,对于每个预测点,我们将它添加到网络的每个类组件,并验证该组件的一些度量如何变化。如果变化很小,那么我们确信该点没有改变组件的结构,并且可能从那里开始。然而,如果度量发生了很大的变化,那么这个点就改变了组件的结构,并且可能不属于那里。
人们可以通过设置一个称为 p 的参数来控制高级分类对最终预测的影响程度,该参数是一个介于 0 和 1 之间的值,用于定义高级影响。
关于高级数据分类如何工作的更多细节可以在[1]中找到。
编码
让我们看看如何使用 sknet 库将我们的表格数据转换为复杂的网络,然后利用结构模式识别来尝试和改进我们的标准机器学习算法。
在开始任何编码之前,第一步是使用 pip 安装 sknet 库:
pip install scikit-net
现在,让我们看一下代码:
from sklearn.datasets import load_wine
from sklearn.metrics import accuracy_score
from sknet.network_construction import KNNConstructor
from sknet.supervised import HighLevelClassifierX, y = load_wine(return_X_y = True)
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size=0.2)
knn_c = KNNConstructor(k=5)
classifier = HighLevelClassifier()
classifier.fit(X_train, y_train, constructor=knn_c)pred = classifier.predict(X_test)
print(accuracy_score(y_test, pred))
如您所见,代码相当简单,但让我们看看每一部分,了解发生了什么:
X, y = load_wine(return_X_y = True)
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size=0.2)
在这里,我们只是使用 sklearn 库来导入 Wine 数据集,然后将它分成一个包含 20%数据的维持测试集。
knn_c = KNNConstructor(k=5)
classifier = HighLevelClassifier()
classifier.fit(X_train, y_train, constructor=knn_c)
在第一行中,我们将创建一个 KNN 构造函数,它将负责将葡萄酒数据集转换成一个复杂的网络。在下一行,我们正在实例化高级分类器类,就像我们对来自 sklearn 的分类器所做的一样。
最后,我们在训练数据上拟合分类器。注意,我们将构造函数传递给 fit 函数,这样它就知道如何在 fit 期间转换数据。这是库中每个算法的标准方法。
pred = classifier.predict(X_test)
print(accuracy_score(y_test, pred))
最后,我们只是使用拟合的分类器来预测测试集的标签,并将其与真实标签进行比较。
注意这里我没有参数化分类器。为了简单起见,我使用了默认参数,但是,我们能够控制我们将使用哪个低级分类器,我们应该对高级预测赋予多少权重,我们应该对高级分类使用哪些指标,等等。每个参数的列表可以在这里找到。
关于 sknet 库的更多信息
sknet 库的开发旨在弥补在复杂网络上实现机器学习算法的现有差距。它已经提供了将表格数据转换成复杂网络的工具,正如你在这篇文章中看到的,而且还提供了将时间序列转换成网络的工具。它还实现了监督、非监督和半监督算法,可用于从复杂网络中学习或改进现有方法。
希望你喜欢它并测试 lib!
[1] Silva,T.C .,赵,L .基于网络的高层数据分类(2012),IEEE Trans .神经网络。学习。系统。23(6)
在 M1 Mac 电脑上使用 conda
运行多个 conda 发行版,从两个世界获得最佳效果。

如果你最近在工作中购买或获得了一台新的 M1 Mac,并且你正在使用 Python 开发或从事数据科学项目,你可能已经浪费了一些时间来运行一些包。我仍然在我的 Mac 上与 Python、Docker 和 conda 作斗争,但是我找到了一种让许多包在 conda 环境中运行的方法。
由于 M1 是基于 ARM 的系统,许多 Python 包无法正确安装,因为它们是为在 AMD64 (x86)上运行而构建的。像许多其他人一样,我使用 conda 为我的项目建立环境——最好是使用 Anaconda 或 Miniconda。
当我第一次想在我的 Mac 上安装 Tensorflow 时,我偶然发现了 mini forge(https://github.com/conda-forge/miniforge),它可以与 Miniconda 相媲美,但 conda-forge 是默认通道,并专注于支持各种 CPU 架构。Tensorflow 与 Miniforge 一起安装时工作正常。
但是当我需要安装我工作中使用的某些包时——比如 SimpleITK(现在也有 M1 Python wheel!)— Miniforge 无法安装它。这有点像赌博。在某个时候,我意识到我可以在同一个系统上同时安装和使用 Miniforge 和 Miniconda。
编辑:正如Lucas-Raphael müller给我指出的,你不需要两个都装,Miniconda 和 Miniforge。你可以像这里说的一样选择是使用针对 Intel 芯片编译的包还是针对 Apple Silicon 编译的包:https://github . com/Haydnspass/miniforge # Rosetta-on-MAC-with-Apple-Silicon-hardware。
安装 Miniforge 后,初始化命令将在您的。bashrc/。zshrc:
# >>> conda initialize >>>
# !! Contents within this block are managed by ‘conda init’ !!
__conda_setup=”$(‘/Users/xyz/miniforge3/bin/conda’ ‘shell.zsh’ ‘hook’ 2> /dev/null)”
if [ $? -eq 0 ]; then
eval “$__conda_setup”
else
if [ -f “/Users/xyz/miniforge3/etc/profile.d/conda.sh” ]; then
. “/Users/xyz/miniforge3/etc/profile.d/conda.sh”
else
export PATH=”/Users/xyz/miniforge3/bin:$PATH”
fi
fi
unset __conda_setup
# <<< conda initialize <<<
这将使用 Miniforge 初始化 conda。你只需要复制你的。bashrc/。zshrc 文件,并将 miniforge3 更改为 miniconda3,并选择默认情况下要使用的文件。更改非常简单,只需运行source .bashrc进行所需的 conda 初始化。
例如,我正在做一个项目,在这个项目中,我需要 SimpleITK 来预处理图像,需要 Tensorflow 来训练一个模型。我无法让两者在 M1 的同一个小型锻造环境中工作。所以我把预处理和训练分成两个环境,一个利用 Miniconda 运行 SimpleITK,一个 Miniforge 环境运行 Tensorflow。
它的好处是你可以通过运行conda env list同时看到 Miniconda 和 Miniforge 环境。唯一的区别是,您将看不到用其他安装程序构建的环境的名称,只能看到路径。初始化 Miniconda 后,您需要使用 Miniforge 环境的完整路径运行conda activate。
这在 bash 脚本中仍然很容易管理,以便使用由多个 conda 发行版构建的多个环境来运行脚本。
我希望这只是一个临时的解决办法,直到越来越多的软件包可以在 M1 上工作,但是我确信这需要一些时间。
希望这能帮助一些在 M1 上使用 conda 处理 Python 包的人。
使用条件深度卷积 GANs 从文本描述生成自定义人脸
本项目代码见:【https://github.com/evanhu1/pytorch-CelebA-faCeGAN】
GANs(生成对抗网络)是无监督学习模型的一个子集,它利用两个网络以及对抗训练来输出与输入数据相似的“新”数据。更具体地说,GANs 通常涉及“捕获数据分布的生成模型 G,以及估计样本来自训练数据而不是 G [1]的概率的判别模型 D。”

生成性对抗网络。来源
条件甘是原始甘模型的修改,后来由 Mehdi Mirza 和 Simon Osindero 在论文“条件生成对抗网络”(2014)中提出。在 cGAN(条件 GAN)中,鉴别器被给予数据/标签对,而不仅仅是数据,并且除了噪声向量之外,生成器还被给予标签,指示图像应该属于哪个类。标签的添加迫使生成器学习不同训练数据类的多种表示,允许显式控制生成器的输出的能力。在训练模型时,标签通常与生成器和鉴别器的数据样本相结合。实现这一点的代码将在下面给出。
在本文中,我将描述 PyTorch 中条件深度卷积 GAN 的实现,它使用英文文本而不是单个数字作为标签。我们的模型是深度卷积 GAN (DCGAN),也就是说,它在其架构中使用深度卷积层,而不是原始论文中的全连接层。我们将在名人面孔的数据集上进行训练,图像被裁剪为 64x64。我们的模型架构包含五个卷积/转置卷积层,具有批量归一化和泄漏 ReLU 激活,以及用于鉴别器输出层的 sigmoid 激活和用于发生器输出层的 tanh 激活。使用 Adam 优化器和二元交叉熵损失。
参数和数据清理步骤由关于 GAN 训练的“传统智慧”指导,从诸如 GANHacks 和原始论文等来源收集。这些包括 Adam 优化器的学习率为 0.0002 (beta 0.5),步进卷积而不是向下/向上采样,使用高斯分布的自定义权重初始化(平均值为 0.0,标准差为 0.02),以及将真实图像从[0,1]缩放到[-1,1]以匹配伪图像中的 tanh 输出。

发生器模型的架构——鉴别器模型几乎完全相同,只是输入/输出维度和简单卷积层有所不同,而不是转置层。作者图片
在确定了设计之后,我们用 PyTorch 编写了我们的模型。
至于数据集,CelebA 为每张图像提供了 40 个二进制属性注释,如下例所示。

使用这些二元属性作为我们的标签或条件,我们可以训练我们的 cGAN 生成具有我们控制的特定特征的人脸。此外,我们可以选择多个属性进行训练和学习,只需将它们连接起来形成多维标签。给定足够的属性,我们可以生成具有不同人类特征的各种各样的人脸。例如,我们的初步训练结果显示,该模型可以学习二元属性,[男性,年轻],允许我们生成 4 种不同的面部类型组合(年轻男性,“不年轻”男性,年轻女性,“不年轻”女性)

作者图片
在我们的代码中,我们构建了一个定制的 PyTorch 数据集,它带有一个参数来指定在总共 40 个二进制属性中包含哪些属性,因为对所有 40 个属性进行训练太困难了。
对于我们模型的实际输入,我们创建了沿标签轴具有二维的多热点编码张量。具体来说,假设我们在训练中仅使用 64x64 图像的两个二进制属性,则生成器将接收 32x2x1x1(批量大小、标签数量和反卷积层要作用的 2 个“伪”图像尺寸)的 multi-hot 编码张量作为每个训练样本的标签。相反,鉴别器将接收 32×2×64×64 的多热编码张量,根据训练样本的二进制属性填充 0 和 1。
在训练期间,在每个标签通过卷积/去卷积层之后,使用 torch.cat()层将这些标签与数据样本连接在一起。创建标签并将其处理成正确尺寸的代码在训练循环中,该循环基本上与普通的 GAN 训练循环相同,只是增加了将标签与数据样本一起输入的步骤。
鉴别器模型的训练代码
在 Google Collaboratory GPUs 上,每个时期的训练时间约为 30 分钟,平均到第 5 个时期时产生了有趣的结果。我们尝试稳步增加训练数据中用于描述面部的属性数量,希望了解 cGAN 能够学习多少。
每个图像有 5 个二进制属性:

作者图片
10 个属性:

['秃头','黑发','金发','棕发','灰发','男性','无胡须','后退 _ 发际线','直发','波浪发']。作者图片
正如您可能看到的,随着属性数量的增加,结果会受到影响。考虑到我们有限的计算资源和时间,我们没有超过 10 个属性,但看看它能走多远,以及一个足够大的模型是否能学习 celebA 的所有 40 个二进制属性将会很有趣。
作为额外的工作,我们还在 celebA 数据样本上使用旋转变换进行实验,看看 cGAN 是否不仅可以学习二进制属性,还可以学习旋转等增强。从论文通过预测图像旋转的无监督表示学习中获得灵感,我们生成了旋转 90、180 和 270 度的样本用于训练。

作者图片
结果表明,令人惊讶的是,cGAN 模型确实能够学习面部属性以及图像增强。
总之,这是对 GANs 从数据中学习的明确能力以及 cGANs 令人印象深刻的灵活性的引人入胜的观察。我们希望你会觉得有趣,就像我们一样。
承认
这个项目是与来自伯克利机器学习的杰克·奥斯丁、雅利安·贾恩和布莱恩·刘共同合作的成果。
参考
[1]生成性对抗网络:https://arxiv.org/abs/1406.2661
利用卷积神经网络进行图像分类

纳斯蒂亚·杜尔希尔在 Unsplash 上的照片
卷积神经网络(CNN 或 ConvNet)是神经网络的一个子类型,主要用于图像和语音识别。其内置的卷积层在不丢失信息的情况下降低了图像的高维度。这就是 CNN 特别适合这个用例的原因。
图像处理问题
如果我们想使用一个全连接的神经网络进行图像处理,我们很快就会发现它不能很好地扩展。
https://medium.com/illumination/intuitive-guide-to-artificial-neural-networks-5a2925ea3fa2
对于计算机来说,RGB 符号的图像是三个不同矩阵的总和。对于图像的每个像素,它描述了该像素显示的颜色。为此,我们在第一个矩阵中定义红色分量,在第二个矩阵中定义绿色分量,然后在最后一个矩阵中定义蓝色分量。因此,对于 3 个像素大小为 3 的图像,我们得到三个不同的 3x3 矩阵。

3x3x3 RGB 图片|图片:作者
为了处理图像,我们将每个像素作为输入输入到网络中。因此,对于大小为 200x200x3 的图像(即 200 个像素对 200 个像素,具有 3 个颜色通道,例如红色、绿色和蓝色),我们必须提供 200 * 200 * 3= 120,000 个输入神经元。那么每个矩阵的大小为 200×200 像素,因此总共有 200 * 200 个条目。这个矩阵最终存在三次,分别代表红色、蓝色和绿色。问题出现在第一个隐藏层,因为那里的每个神经元都有来自输入层的 120,000 个权重。这意味着当我们增加隐藏层中神经元的数量时,参数的数量会迅速增加。
当我们想要处理具有更多像素和更多颜色通道的更大图像时,这一挑战变得更加严峻。这种具有大量参数的网络很可能会过拟合。这意味着该模型将为训练集提供良好的预测,但不会很好地推广到它尚不知道的新情况。此外,由于大量的参数,网络将很可能停止关注单个图像细节,因为它们将在纯粹的质量中丢失。然而,如果我们想要对图像进行分类,例如,图像中是否有狗,这些细节,例如鼻子或耳朵,可能是正确结果的决定性因素。
卷积神经网络
出于这些原因,卷积神经网络采取了一种不同的方法,模拟我们用眼睛感知环境的方式。当我们看到一幅图像时,我们会自动把它分成许多小的子图像,并逐个进行分析。通过组合这些子图像,我们处理和解释图像。如何在卷积神经网络中实现这一原理?
工作发生在所谓的卷积层。为了做到这一点,我们定义了一个过滤器,它决定了我们正在查看的部分图像应该有多大,以及一个步长,它决定了我们在计算之间继续多少像素,即部分图像彼此有多接近。通过采取这一步骤,我们大大降低了图像的维数。
下一步是池层。从纯计算的角度来看,这里发生的事情与卷积层相同,不同之处在于,根据应用,我们只从结果中取平均值或最大值。这保留了对任务解决方案至关重要的几个像素中的小特征。
最后,还有一个全连接层,正如我们从常规神经网络中已经知道的那样。现在,我们已经大大降低了图像的维度,我们可以使用紧密网格层。这里,各个子图像被再次链接,以便识别连接并执行分类。
现在,我们已经对各个图层的大致功能有了基本的了解,我们可以详细了解图像是如何成为一个类别的。为此,我们尝试从 4x4x3 图像中识别其中是否有狗。
细节:卷积层
在第一步中,我们想要减少 4x4x3 图像的尺寸。为此,我们为每种颜色定义一个尺寸为 2x2 的过滤器。此外,我们希望步长为 1,即在每个计算步骤之后,滤波器应该向前移动一个像素。这不会减少太多的维度,但图像的细节将被保留。如果我们用一个 2x2 迁移一个 4x4 矩阵,并在每一步中前进一列或一行,我们的卷积层将有一个 3x3 矩阵作为输出。矩阵的单个值通过取 2x2 矩阵的标量积来计算,如图所示。

卷积层|图片:作者
详细信息:池层
(最大)池层将卷积层的 3×3 矩阵作为输入,并尝试进一步降低维度,另外获取图像中的重要特征。我们希望生成一个 2x2 矩阵作为这一层的输出,因此我们将输入划分为所有可能的 2x2 部分矩阵,并在这些字段中搜索最高值。这将是输出矩阵字段中的值。如果我们使用平均池层而不是最大池层,我们将计算四个字段的平均值。

池层|照片:作者
汇集层还从图像中滤除噪声,即图像中对分类没有贡献的元素。例如,狗是站在房子前面还是森林前面,这一开始并不重要。
细节:完全连接的层
完全连接的层现在做的正是我们在开始时打算对整个图像做的。我们为较小的 2x2 矩阵中的每个条目创建一个神经元,并将它们连接到下一层中的所有神经元。这大大减少了我们的维度,也减少了培训所需的资源。
然后,这一层最终知道需要图像的哪些部分来进行狗或非狗的分类。如果我们的图像比我们的 5x5x3 示例大得多,当然也可以在进入全连接层之前连续几次设置卷积层和池层。通过这种方式,您可以将维数降低到足以减少训练工作量的程度。
数据集
Tensorflow 有各种各样的数据集,我们只需几行代码就可以下载和使用。当您想要测试新模型及其实现,并且因此不想长时间搜索适当的数据时,这尤其有用。此外,谷歌还提供了一个数据集搜索,人们可以通过点击几下找到合适的数据集。
对于我们的示例卷积神经网络,我们使用通过 Tensorflow 获得的 CIFAR10 数据集。该数据集包含总共 60,000 幅彩色图像,分为十个不同的图像类别,例如马、鸭或卡车。我们注意到,这是一个完美的训练数据集,因为每个类正好包含 6,000 幅图像。在分类模型中,如果可能的话,我们必须始终确保每个类在数据集中出现的次数相同。对于测试数据集,我们总共获取 10,000 张图像,因此训练数据集获取 50,000 张图像。
这些图像的尺寸都是 32×32 像素。像素依次具有 0 到 255 之间的值,其中每个数字代表一个颜色代码。因此,我们将每个像素值除以 255,以便将像素值归一化到 0 和 1 之间的范围。
为了检查所有图像是否正确显示,我们打印前十个图像,包括它们所属的类。由于这些只是 32×32 的图像,它们相对模糊,但你仍然可以分辨出它们属于哪一类。
建立一个卷积神经网络
在 Tensorflow 中,我们现在可以通过定义每层的序列来构建卷积神经网络。因为我们正在处理相对较小的图像,我们将使用卷积层和最大池层的堆栈两次。正如我们已经知道的,这些图像有 32 个高度维度、32 个宽度维度和 3 个颜色通道(红、绿、蓝)。
卷积层首先使用 32 个滤波器,然后使用 64 个具有 3×3 内核的滤波器,最大池层搜索 2×2 矩阵内的最大值。
经过这两次叠加后,我们已经将图像的尺寸显著减少到 6 个高度像素,6 个宽度像素,总共 64 个滤镜。利用第三个也是最后一个卷积层,我们将这些维度进一步降低到 4x4x64。在我们现在由此构建全网状网络之前,我们用 1024 个元素(4464)的向量替换每个图像的 3×3 矩阵,而不会丢失任何信息。
现在,我们已经充分降低了图像的维度,并且可以在模型结束于具有十个不同类别的十个神经元的输出层之前,再添加一个总共具有 64 个神经元的隐藏层。
该模型总共有 122,570 个参数,现在可以开始构建和训练了。
编译和训练模型
在我们开始训练卷积神经网络之前,我们必须编译模型。在其中,我们定义了应该根据哪个损失函数、优化器(即,根据哪个算法来改变参数)来训练模型,以及为了能够监控训练过程,我们希望显示哪个度量。
评估模型
在对卷积神经网络进行总共 10 个时期的训练之后,我们可以查看模型精度的进展,以确定我们是否对训练满意。
我们对图像类别的预测在大约 80%的情况下是正确的。这不是一个不好的值,但也不是一个特别好的值。如果我们想进一步增加这一点,我们可以让卷积神经网络训练更多的时期,或者可能以甚至不同的方式配置密集层。
这是你应该带走的东西
- 卷积神经网络用于图像和语音处理,并且基于人类视觉皮层的结构。
- 它们由卷积层、汇集层和完全连接层组成。
- 卷积神经网络将图像分成更小的区域,以便第一次单独查看它们。
- 使用 Tensorflow,卷积神经网络只需几步即可编程。
- 根据不同的使用情况调整卷积层和最大池层的安排非常重要。
如果你喜欢我的作品,请在这里订阅https://medium.com/subscribe/@niklas_lang或者查看我的网站* 数据大本营 !还有,medium 允许你每月免费阅读 3 篇 。如果你希望有无限制的 访问我的文章和数以千计的精彩文章,不要犹豫,点击我的推荐链接:【https://medium.com/@niklas_lang/membership】每月花$5***获得会员资格**
*https://medium.com/codex/why-you-should-know-big-data-3c0c161b9e14 https://medium.com/@niklas_lang/understanding-mapreduce-with-the-help-of-harry-potter-5b0ae89cc88
参考
使用自定义图像作为 Tableau 中的地图
你知道你可以在 Tableau 里画出任何东西吗?
是的,你没看错。您可以使用任何图像作为背景,并在其上添加数据点。在这篇文章中,我将带领你创建你自己的自定义地图的步骤。

由 Unsplash 上的 GeoJango Maps 拍摄
最近,我试图为奥运会绘制时间表。出于本教程的目的,我将使用相同的 viz。
它是如何工作的?
我们知道我们需要两个坐标来绘制地图——一个位置的经度和纬度。如果您还记得散点图,我们基本上使用 X 和 Y 坐标来绘制数据点。因此,地图可以被认为是一个散点图,但在 Tableau 中有一个背景图像。你只需要坐标,就可以开始了。
如何向地图添加数据点?
为此,您需要首先准备数据源,添加图像并在数据源中添加所需的坐标。下面是详细的步骤。
1.准备数据源
打开一个电子表格文件,添加三列——X、Y 和一个标识符。我用的是年份。在 X 和 Y 列中输入随机数。

初始数据源(图片由作者提供)
2.设置工作表
转到 Tableau 中的新工作表,将 X 添加到 Columns shelf,将 Y 添加到 Rows shelf。右键单击 X 和 Y,然后选择“尺寸”。另外,将图表类型更改为地图。

(图片由作者提供)
3.找到图像
找到您想要使用的合适大小的图像。我用的图片尺寸是 1200 X 554。
请务必给予应有的学分,以使用图像。
一旦你有了图像,进入地图>背景图像。单击数据源的名称。将会打开一个弹出窗口。点击“添加图像…”


添加背景图片(图片由作者提供)
浏览到您的图像,并根据图像的大小设置 X 和 Y 字段限制。我的图像尺寸是 1200 X 554。如果需要,您也可以选择冲洗图像。单击确定。
你现在应该看到图像作为背景。在此阶段,最好将 x 轴和 y 轴固定在正确的范围内,以便图像与工作区域对齐。


将自定义图像添加为地图(作者提供的图像)
4.添加数据点
找到您添加到电子表格的一个数据点。右键单击该点,选择注释>点,然后单击确定。您也可以单击图像上的任意位置来注释该点。默认情况下,注释会显示 X 和 Y 坐标。
现在,将注释从(100,100)移动到图像上的一个点。你刚刚得到了第一个数据点。将这些坐标添加到电子表格中。根据所需的数据点数量重复此步骤。


使用注释获取坐标(图片由作者提供)
完成本练习后,您的电子表格应该如下所示。您可以添加更多包含与您的 viz 相关的详细信息的专栏。

(图片由作者提供)
保存电子表格并刷新数据源。
您的图像应该在刚刚映射的位置上有点。小白点是我们刚刚绘制的坐标。

映射的自定义图像(作者提供的图像)
瞧啊。您已经成功地使用了自定义图像并在其上绘制了数据。
你可以用这种技术做很多有趣的事情。这是我做的和我最终的 viz 看起来的样子。

来源:即
感谢阅读!
直到下一次…
使用 CycleGAN 在网络摄像头上执行风格转换
实践教程
关于如何设置 CycleGAN 和添加网络摄像头兼容性的教程

在我的网络摄像头上实时播放著名艺术家的作品。
注意:本文假设您了解 GANs。 这里的 是巨大的资源。
你有没有想过如果你是莫奈画的你会是什么样子?还是传说中的梵高?
随着我对 GANs 的了解越来越多,一个特别的应用引起了我的注意:风格转换。风格转移的目标是学习如何将一个域 X 的图像映射到另一个不同的域 y 的图像。一个经典的例子是让一个域成为一组照片,而另一个域包含一位著名艺术家(如克洛德·莫内)的绘画,并应用风格转移将普通照片转换为看起来像是他画的。

使用循环一致对抗网络的不成对图像到图像翻译。
这被称为图像到图像的转换,它传统上需要一个大型的成对图像数据集,这意味着为了学习上面例子中的映射,你需要相同位置、相同角度、相同时间的绘画和照片,等等。由于显而易见的原因,获得这种配对数据是不现实的,而且通常是不可能的。如果不用成对的例子就能把图像翻译成画,岂不是很棒?这个问题已经被探索,并且通过循环一致的对抗网络 (CycleGAN)已经实现了不成对的图像到图像翻译的有效方法,该方法使用 GAN 架构来学习必要的映射并执行高质量的图像翻译。我很好奇 CycleGAN 在视频上使用时有多有效,所以在这篇短文中,我将简要介绍 CycleGAN 的工作原理,然后介绍如何使用官方 CycleGAN 实现将莫奈、梵高和其他人的艺术风格应用到您自己的网络摄像头上。
周期根的直觉
CycleGAN 的整体架构相当复杂。记住 CycleGAN 的目标,以及这些目标如何影响它的架构,有助于使它更容易理解。CycleGAN 的目标如下:
- 学习将域 X 映射和转换到域 Y(反之亦然)
- 维护图像的一致性:来自域 X 的图像在翻译到域 Y 时(反之亦然)应该看起来像原始图像,但是应用了必要的风格变化。
为了实现第一个目标,我们使用具有两个相应鉴别器的两个生成器,并应用下面的对抗性损失:

其中生成器 G 获取图像 X 并生成看起来属于域 y 的图像 G(X) 。鉴别器 Dy 试图正确地区分所生成的样本和从域 y 中随机选择的真实图像。这个相同的目标可以用于映射和翻译域 Y →域 x。
仅仅这样还不足以实现想要的风格转换。仅用对抗性损失进行训练将导致从域 X 到域 Y 的成功生成,但是不能保证生成的图像看起来像原始图像。我们希望映射是“循环一致的”,这意味着(通过其生成器)转换到目标域,然后转换回其原始域的图像应该尽可能地类似原始图像:X→G(X)→F(G(X))→≈X其中 F 是从域 Y 转换到 X 的生成器

为了实施这一原则,并实现我们的第二个目标,我们在下面定义了周期一致性损失函数。损失函数鼓励X→G(X)→F(G(X))→≈X和 Y→ F(Y) → G(F(Y)) → ≈ Y.

使用循环一致对抗网络的不成对图像到图像翻译。
我们目前有三种不同的损耗,用于在训练期间更新 CylceGAN:
- G 对 Dy (X→Y)的对抗性损失
- F 对 Dx (Y→X)的对抗性损失
- 循环一致性损失
而这些足以训练出 CycleGAN。我将简单地提一下,添加一个身份损失是有益的,这有助于保留翻译图像的颜色和色调。
CycleGAN 的作者建议通过使用变量 lambda λ来赋予这些损失函数不同程度的影响——如果您对 CycleGAN 架构的这一方面或其他方面(如层架构)感兴趣,我建议看一看原始论文。有了一点 CycleGAN 直觉/更新,让我们用网络摄像头工作吧!
将风格转换应用到您的网络摄像头
为了将莫奈、梵高和其他风格应用到您的网络摄像头,我们将使用论文作者创建的预训练 CycleGAN 模型。首先克隆库并在终端/命令行中导航到根目录。从这里开始,您将运行./scripts文件夹中提供的一组 bash 命令来加载必要的模型。
bash ./scripts/download_cyclegan_model.sh style_monet_pretrained
bash ./scripts/download_cyclegan_model.sh style_ukiyoe_pretrained
bash ./scripts/download_cyclegan_model.sh style_cezanne_pretrained
bash ./scripts/download_cyclegan_model.sh style_vangogh_pretrained
这些会将预训练的 CycleGANs 保存在./checkpoints文件夹中。
我们现在将在根目录下创建一个名为webcam.py的 Python 文件,它将是test.py的修改版本,而不是通过 CycleGAN 运行数据集,而是通过它传递我们的网络摄像头馈送。
一旦你创建了webcam.py,开始导入必要的包:
然后,使用来自test.py的相同代码行来解析来自命令行的参数:
我们将通过编辑./options/base_options.py来编辑webcam.py的所需参数,并将参数--dataroot的所需布尔值设置为 false,因为使用网络摄像头,我们不需要提供数据的目录。现在,我们使用 cv2 来设置我们的网络摄像头,如果设置失败,就会引发错误:
然后,我们添加代码来获取网络摄像头的每一帧并执行必要的转换,以便它可以被我们加载的模型读取和转换。
最后,我们在一个新窗口中显示风格化的框架。如果用户在任何时候按下“Esc”键,窗口将关闭。
此时,您应该能够使用根文件夹中的以下命令运行您选择的 CycleGAN 模型的webcam.py。
python webcam.py --name MODEL_NAME --model test --preprocess none --no_dropout
如果你对此满意:太好了!您可以通过修改--name参数来尝试其他预训练模型。在下一节中,我将展示如何添加一些额外的功能:通过按键实时切换样式。
在风格中循环
没有太多的修改必须作出,以便能够通过循环的风格,并应用到我们的网络摄像头实时。首先创建一个列表,列出您希望循环遍历的所有模型名称,并初始化一个变量来索引该列表。
在 while 循环之前插入以下代码。在我的代码中,这是在我们用 cv2 设置了网络摄像头之后。
我们还想在窗口的左上角添加文本,说明我们正在应用谁的艺术家的风格。首先为字体大小和位置设置一些参数。
除了前面的代码之外,在 while 循环之前插入以下代码。
为了将文本放置在左上角,我们将使用cv2.putText()并改变样式,我们将添加一个条件,如果按下“c”键,它将加载style_models列表中的下一个模型。下面是webcam.py的完整的最终实现:
它可以用前面列出的相同 bash 命令运行:
python webcam.py --name MODEL_NAME --model test --preprocess none --no_dropout
结论
您可能会注意到每种风格都有一些闪烁/噪音。在视频上执行风格转换是一个相对较新的发展,许多酷的、计算昂贵的工作正在进行,试图使帧之间的转换更加一致。
CycleGAN 存储库文档教你如何在自己的数据集上训练,从而创建自己的风格!我认为收集漫画书作为一个数据集将导致一个非常酷的风格转移。
虽然已经有像 Photo Booth 这样的带有网络摄像头过滤器的程序,但风格转移很酷,因为他们学习了更高的理解,并且风格的细微差别可以有效地应用它。它可能不会在低分辨率的网络摄像头中显示出来,但在高分辨率的照片上,您可以真正看到它的威力:

Pietro De Grandi 原创图片

梵高的!
这也只是 CycleGAN 的一个具体应用,我建议浏览网页或查阅文献,看看它拥有的其他迷人的功能。
对于 webcam.py 和存储库文件,这里是我在 GitHub 上的 fork。
感谢阅读!
使用 D3.js 创建动态地图和视觉效果,展示 21 世纪相互竞争的气候变化情景
如何使用 D3.js、Angular、JavaScript 和 Python 为您的数据科学项目创建地图和动画的代码演练

利用 D3.js 制作未来 80 年气候变化预测的动画和地图:在zach-alexander.com/climate-change的完整可视化和故事|图片由作者提供
随着拜登政府下周正式上任,应对气候变化的努力出现了乐观情绪。正因为如此,我认为探索未来 80 年政策作为(或不作为)对这一问题的潜在影响将是有趣的。
最近,我致力于一个完整的数据可视化故事,比较两种竞争气候变化情景下的温度变化预测。 这两种情景(“A2”和“B1”)是 IPCC 在 21 世纪初制定的。这些情景概述了在这个问题上不同层次的全球合作及其对碳排放的潜在影响。如果你有兴趣阅读完整的数据故事并与其可视化互动,请随时前往:https://zach-alexander.com/climate-change【zach-alexander.com/climate-change】。
写完这个数据故事后,我意识到写一篇概述为观众创建 web 可视化的能力的文章可能会有所帮助。作为一名数据科学研究生(职业是数据工程师),我经常对我们如何传达我们不懈努力完善的分析和数据的结论缺乏后续跟进(包括我在内)感到失望。最后,有时我们创建的算法或模型可能会失败,如果它们没有给出上下文或以令人信服的方式传达给受众。虽然现在有很多数据可视化平台(PowerBI,Tableau 等。)可以快速制作图表,在网上构建数据故事可以让分析更上一层楼。
在这篇文章中,我将从我为数据故事创建的一个可视化中选取一部分,并希望为如何使用https://angular.io/和 D3.js 创建一个模拟 21 世纪气候变化模型的动画地图提供基础。****
如果你想跟随完整的代码,你可以从这个库 中提取 。它将包含下面列出的所有代码,包括:
- 将 csv 数据合并和转换为 JSON(python)
- 运行本地角度应用程序(Angular-cli)
- 安装 D3.js 并将 json 文件加载到 Angular 组件中(Angular)
- 使用 D3.js 创建一个世界地图,并在 4 个时间间隔内制作动画(角度和 Javascript)
如果你发现自己想要在这篇文章中开始的动画的基础上构建,你 可以从我关于这个主题的综合数据故事中提取额外的代码 。
确定你的想象或故事的目标
在开始任何网站工作之前,开发任何数据可视化的关键的第一步是考虑你最终想要向你的观众描绘什么。
我的目标:我想根据 IPCC 的气候变化情景,直观地展示未来 80 年各国的土地温度变化(以华氏度为单位)。
带着这个目标,我开始寻找一个令人信服的数据集。
查找数据集并识别其结构
幸运的是,有无数的气候变化数据集可供使用,而这个 世界银行数据集 包含了预测未来一个世纪陆地温度变化的集合数据(基于全球环流模型和 IPCC 气候变化情景),正是我所寻找的。
太好了!我有一个数据集,现在我如何把它转换成一个网页?
嗯,在我们着手把这些数据放到网页上之前,我们确实需要认真考虑我们的数据的哪一方面是我们想要呈现给我们的观众的。此外,由于 D3.js 可以很好地处理以JavaScript Object Notation(JSON)格式 格式化的数据,我们必须在构建可视化之前进行一些数据转换。
因为我想创建一个世界地图,所以我们也在处理地理坐标,它采用了一个更具体的 json 结构,称为“geojson”。
什么是 geojson?
简而言之,geojson 结构是专门用于编码各种地理数据结构的 json 格式的扩展。在 D3.js 中使用地图时,有许多内置函数和过程依赖于以 geojson 或 topojson 格式定向的数据。这两者的区别可以在这篇 stackoverflow 帖子 中详细概述。我们将使用的格式 geojson 采用以下结构:
**{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [38.00,−97.00]
},
"properties": {
"name": "United States"
}
}**
对于上面这个简单的例子,我们可以看到,当这个数据被读入 D3.js 时,它会在相邻的美国的正中心画一个点(或点)。提到的“几何”是“点”的类型,附加到该点的“坐标”是该点将在网页上绘制的位置。您还可以将特定的属性附加到一个点上,比如一个“名称”,D3.js 也可以引用它(稍后我们将看到它在我们的调色板中发挥作用)。
虽然这是一个简单的例子,但你可以想象当你画特定的多边形(或形状)时,“坐标”会变得更长。本质上,当处理多边形时,您需要一个彼此相连的点的列表。每个多边形都有一组基于特定投影的坐标,然后这些坐标被渲染到页面上。作为一个实例,如果我们要查看内华达州的多边形坐标,您可以在下面找到它:
**{
"type": "Feature",
"properties": {"name": "Nevada"},
"geometry": {
"type": "Polygon",
"coordinates": [[[-117.027882,42.000709], [-114.04295,41.995232],[-114.048427,37.000263],[-114.048427,36.195153],[-114.152489,36.025367],[-114.251074,36.01989],[-114.371566,36.140383],[-114.738521,36.102045],[-114.678275,35.516012],[-114.596121,35.324319],[-114.574213,35.138103],[-114.634459,35.00118],[-115.85034,35.970598],[-116.540435,36.501861],[-117.498899,37.21934],[-118.71478,38.101128],[-120.001861,38.999346],[-119.996384,40.264519],[-120.001861,41.995232],[-118.698349,41.989755],[-117.027882,42.000709]]]
}
}**
我们可以看到,“几何图形”比我们的第一个例子长得多,“类型”现在是多边形而不是“点”。“坐标”是根据所使用的投影列出的,然后会呈现在页面上。
使用 python 将。csv 到。json
有了 geojsons 的新知识,我设计了一个计划,从 Kaggle 获取包含气候变化预测的 csv 文件,并最终将它们转换为 D3 的 json 文件。尽管许多数据操作可以通过许多 D3s 内置函数在 JavaScript 中正确完成( 您可以将 csv 数据直接读入 JavaScript ),但我认为在将数据加载到网页之前操作数据会更有效,因为数据集是静态的(不依赖于对第三方 API 的访问/连接)。这样做的好处是,它应该使代码更高效,我们的可视化更敏捷,并减少页面加载时渲染地图和图表所需的时间。
为此,我使用了 python。你可以在 Github 上找到记录我的数据转换的整个 jupyter 笔记本。
总之,在阅读了 Kaggle 的 csv 文件后,我首先必须对各种日期范围和模型输出进行大量的数据汇总。然后,我基本上采用了一个公共 geojson 文件,该文件包含世界上所有国家的多边形,并将 Kaggle csv 文件中的温度数据和其他属性(利用属性信息中的公共标识符)合并到每个国家的属性中。在最后一段代码中,我创建了一个自定义函数来将数据帧输出为 json 文件:
**# array of pandas dataframes with our various model data across the four time intervalsdataframes = [first20_med_a2, first20_med_b1, second20_med_a2, second20_med_b1, third20_med_a2, third20_med_b1, fourth20_med_a2, fourth20_med_b1]# empty array of json files that eventually will load in datajson_files = ['first20_med_a2.json', 'first20_med_b1.json', 'second20_med_a2.json', 'second20_med_b1.json', 'third20_med_a2.json', 'third20_med_b1.json', 'fourth20_med_a2.json', 'fourth20_med_b1.json']# custom function to take the pandas dataframes and store them in separate json filesdef createjsons():
for idx, d in enumerate(dataframes):
for i in features:
for index, row in d.iterrows():
if(row['Country'] == i['id']):
i['Change_c'] = row['Change_c']
i['Change_f'] = row['Change_f']
i['Date_range'] = row['Date_range']
i['Percentile'] = row['Pctile']
i['Scenario'] = row['Type']
else:
pass
with open(json_files[idx], 'w') as outfile:
json.dump(gj, outfile)createjsons()**
这一步的最终目标是创建单独的 json 文件,存储国家名称、温度变化(摄氏度)、温度变化(华氏度)、日期范围、模型计算百分比和场景类型。
正如我们在下面看到的,经过数据整理后,我的最终 json 文件中的一个 country 属性如下所示:
**# I abbreviated the length of the coordinates to cut down on size of code block{
"type": "Feature",
"id": "USA",
"geometry": {
"type": "MultiPolygon",
"coordinates": [[[[-155.54211, 19.08348], [-155.68817, 18.91619], [-155.93665, 19.05939], [-155.90806, 19.33888], [-156.07347, 19.70294], [-156.02368, 19.81422], [-155.85008, 19.97729], [-155.91907, 20.17395], [-155.86108, 20.26721], [-155.78505, 20.2487], [-155.40214, 20.07975], [-155.22452, 19.99302], ...]]]
},
"properties": {
"name": "United States of America",
"Change_c": 1.5040905413108334,
"Change_f": 2.7073629743595,
"Date_range": "2020-2040",
"Percentile": "median",
"Scenario": "a2"
}
}**
与上面的快速 geojson 示例类似,我们可以看到我们正在利用许多点来创建我们的美国多边形,但是我使用 python 来合并附加的“属性”,以便稍后用于我的可视化。我们很快就会看到,change_f值将用于我们的 choropleth 着色。
json 文件准备就绪后,我们可以开始想象我们的可视化会是什么样子。从用户的角度来看,我们希望显示土地温度随时间的变化,这可以通过读取特定时间间隔内的 json 文件来实现——所有这些都通过 D3.js 制作成动画。
数据整理已经够了!是时候用 Angular 建立一个网页了。
好吧!现在我们已经准备好了数据文件,我们可以启动一个本地单页面应用程序。
根据您想要使用的 JavaScript 框架,您需要下载命令行界面(cli)来启动本地开发服务器和包管理器。
对于这个例子,我们将使用的包管理器是 npm。安装 npm 最简单的方法之一是下载 node.js,它也将在您的安装中包含 npm。 这方面的说明可以在这里 找到。
作为我们的 Javascript 框架,我们将使用 Angular。为了简化我们的操作,您可以下载 Angular cli 来启动开发服务器。 这里的指令可以找到 ,由四行组成。
下载 node.js 和 Angular cli 后,可以打开 VSCode 终端窗口(或任何终端窗口)并运行以下命令:
**// make sure npm is installed on your computer before running this:**npm install -g @angular/cli****
安装完成后,在你的电脑上找到一个你想要存储应用程序文件的地方,然后运行(将“我的梦想应用程序”改为你想要的目录名称):
****ng new my-dream-app****
这将需要一两分钟才能完全运行,但最终它将创建一堆文件,这些文件包含您的新 Angular 应用程序,并提供编译所需的目录结构。当命令完成时,您可以导航到新的应用程序文件夹并运行“ng serve”来启动开发服务器。
**// change directories into your new angular application**cd my-dream-app**// start the development server**ng serve****
如果一切运行正常,您应该能够编译新创建的 Angular 应用程序,并在您的终端中看到:

如果您的 Angular 应用程序编译成功,您应该会在您的终端窗口中看到此消息
为了保持这一部分的简短,我不会深入讨论 Angular 如何工作的更多细节,但如果你对 Angular, 完全陌生,请阅读这篇精彩的帖子 以了解基本的应用程序开发。此外, Angular 文档 对那些喜欢在杂草中获得更多的人非常有帮助。
让我们的网络可视化!
随着我们的 Angular 开发服务器的运行,我们现在可以开始构建我们的可视化。首先,我们需要从 npm 安装 D3.js 包,以便能够在我们的应用程序中使用这个库。为了这个视觉,我决定安装 D3 的版本 4(出于各种原因)。为此,在终端中停止开发服务器(ctrl + c ),并运行:
**npm install d3v4**
完成后,您可以通过运行以下命令来重新启动开发服务器:
**ng serve**
然后,您应该会看到与前面相同的“编译成功”消息。接下来,我们可以将 json 文件添加到应用程序内部的“assets”目录中(下面是文件夹结构):
**|- application-name
|- src
|- assets
|- first20_med_a2.json
|- second20_med_a2.json
|- third20_med_a2.json
|- fourth20_med_a2.json**
你可以在这里 找到这些已经预制好的 json 文件。然而,如果你愿意,你也可以利用上面的 python 脚本。
如果您使用的是 Angular,因为我们使用的是 Typescript,您可能还需要做一个快速配置,将 json 文件直接加载到 app.component.ts 文件中。 这篇文章 很好地总结了如何做到这一点。很可能,您只需将下面一行加粗的代码添加到您的 tsconfig.json 文件中:
**{
"compileOnSave": false,
"compilerOptions": {
.
.
.
**"resolveJsonModule": true,**
.
.
.
}
}**
渲染我们的第一张地图
随着我们的 json 数据存储在应用程序目录中,D3 库导入到我们的 node_modules 中,Angular 准备好接受 json 文件,我们可以开始在 web 上看到数据可视化的威力了!
为了呈现我们的一个 json 文件,我们首先需要确保我们在 app.component.ts 文件中引用它(下面是文件夹结构):
**|- application-name
|- src
|- app
|- **app.component.ts****
导航到 app.component.ts 文件,通过将加粗的代码添加到现有代码中,将 json 数据导入到组件中:
**import { Component} from '@angular/core';
**import * as d3 from 'd3v4';****// load the json file from the assets folder, give it a name**
**import * as firstModel from '../assets/first20_med_a2.json';**@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})export class AppComponent { **// make sure to reference the correct structure
firstModelData = firstModel['default'];** ngOnInit() { ** // print the json data in the browser console
console.log(this.firstModelData)** }
}**
保存这个文件,返回到您的浏览器,如果一切顺利,您应该能够在页面刷新后在开发工具中看到 json 数据打印到控制台:

Geojson 数据打印到 Google Chrome 控制台|作者图片
然后,我们需要删除 app.component.html 文件中的默认 html 语法,并创建一个快速的“div”。导航到 app.component.html 文件(文件夹结构如下):
**|- application-name
|- src
|- app
|- **app.component.html****
然后,删除当前存在的所有 html 代码,并添加以下 div:
**<div class = "world-map"></div>**
现在,json 数据读入组件,我们可以开始编写 D3.js 函数了。导航回 app.component.ts 文件,并在 AppComponent 类中编写下面的粗体代码:
**export class AppComponent { firstModelData = firstModel['default']; ngOnInit() {
console.log(this.firstModelData) } **setMap(width, height, dataset) {** **const margin = {top: 10, right: 30, bottom: 10, left: 30};
width = width - margin.left - margin.right;
height = height - margin.top - margin.bottom;** **const projection = d3.geoMercator()
.rotate([-11, 0])
.scale(1)
.translate([0, 0]);** **const path = d3.geoPath().projection(projection);** **const svg = d3.select('.world-map')
.append('svg')
.attr('viewBox', '0 0 1000 600')
.attr('preserveAspectRatio', 'xMidYMid')
.style('max-width', 1200)
.style('margin', 'auto')
.style('display', 'flex');** **const b = path.bounds(datapull),** **s = .95 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0] [1]) / height),** **t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2];** **projection.scale(s).translate(t);** **svg.selectAll('path')
.data(datapull.features)
.enter()
.append('path')
.attr('d', path)** **}**
}**
简而言之,这段代码声明了一个函数setMap(),它接受三个参数:宽度、高度和我们的数据集。然后,它定义一个边距,并从画布的所有四边减去边距像素。接下来,由于我们使用的是地图坐标,我们必须定义一个 地图投影 ,稍微旋转地图,这样俄罗斯的最东部就不会出现在画布的左侧,并设置我们的比例和平移。然后,我们将这个投影映射到我们的路径上,这将最终基于我们的投影输入和我们定义的画布尺寸来绘制我们的多边形。
然后,通过使用语法“d3.select(”选择空白 div 来创建我们的第一个 svg。世界地图”)。通过将 svg 附加到我们现有的 div,然后我们设置尺寸,并通过使用 viewBox 和 preserve spectra ratio 使其响应,并最终设置 1200px 的最大宽度,并将地图置于页面的中心(如果视口大于 1200px)。
然后,我们希望确保比例和平移图正确,并且我们画布的定义边界正确,因此变量s和t帮助定义我们墨卡托投影的边界。
最后,我们选择新添加的 svg,读入 geojson 特性,并在页面上绘制路径!为了在页面上呈现,我们需要使用实例化的函数并在ngOnInit()中调用它。将这行代码添加到 ngOnInit()函数内的 app.component.ts 文件中。
**ngOnInit() {
console.log(this.firstModelData) // add this line of code below to render your map: **setMap(1000, 600, this.firstModelData)**}**
如果一切顺利,您应该会在页面上看到类似这样的内容:

setMap()函数首次渲染世界地图|图片作者
好吧!现在我们有所进展了!然而,不幸的是,我们的多边形填充是黑色的,我们希望根据以华氏度为单位的温度变化来创建每个国家的 choropleth 颜色。还记得我们使用 python 将该数据合并到每个国家的属性中吗?好了,现在我们可以使用每个国家的change_f值来区分我们的填充颜色!
为此,我们可以在我们的setMap()函数中添加以下代码(仍然在我们的 app.component.ts 文件中),然后向我们的 svg 添加一个属性:
**setMap(width, height, dataset) {**...****const color_domain = [2.5, 4, 7, 9, 10];****const color_legend = d3.scaleThreshold<string>().range(['#fee5d9', '#fcbba1', '#fc9272', '#fb6a4a', '#de2d26', '#a50f15']).domain(color_domain);****...**svg.selectAll('path')
.data(datapull.features)
.enter()
.append('path')
.attr('d', path)
**.style('fill', function(d) {
const value = d['Change_f'];
if (value) {
return color_legend(d['Change_f']);
} else {
return '#ccc';
}})
.style('stroke', '#fff')
.style('stroke-width', '0.5')**}**
简而言之,我们可以使用 D3.js 中的 d3.scaleThreshold()函数来帮助我们创建 choropleth 调色板。由于时间的原因,我不会深入讨论这个问题,但是如果你有兴趣的话,你可以在这里 阅读更多关于这些秤的信息。此外,在将属性添加到我们的路径之后,我还将多边形(国家)的轮廓变得更粗和更灰。
如果这些附加功能正常工作,您应该会在页面上看到以下内容:

添加色标后的地图渲染|图片由作者提供
让我们来制作动画!
如果你已经做到了这一步,你绝对可以拍拍自己的背!可视化的最后一步是在不同的时间间隔内制作动画。如果你还记得从一开始,我们的目标是显示每个国家在四个不同时间间隔的温度变化:
- 2020 年到 2040 年
- 2040 年至 2060 年
- 2060 年至 2080 年
- 从 2080 年到 2100 年
正如你现在看到的,我们能够用一个 json 文件创建一个静态 choropleth 地图,显示 2020 年到 2040 年之间的温度变化。然而,为了使它在其他三个时间间隔内具有动画效果,我们希望在一段时间内将其他 json 文件加载到这个函数中。D3.js 使这变得相对容易。
首先,我们必须将其余的 json 文件加载到我们的 app.component.ts 文件中,类似于上面的第一个文件。为此,您可以添加下面几行加粗的代码:
**import { Component, OnInit } from '@angular/core';
import * as d3 from 'd3';**# load the rest of the json files from the assets folder, give it a name**
import * as firstModel from '../assets/first20_med_a2.json';
**import * as secondModel from '../assets/second20_med_a2.json';
import * as thirdModel from '../assets/third20_med_a2.json';
import * as fourthModel from '../assets/fourth20_med_a2.json';**@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})export class AppComponent {**# make sure to reference the correct structure** firstModelData = firstModel['default']; **secondModelData = secondModel['default'];
thirdModelData = thirdModel['default'];
fourthModelData = fourthModel['default'];****jsons;**...ngOnInit() {...}**
然后,我们需要在页面加载时将 json 文件存储在一个数组中。因此,我们可以将其添加到我们的ngOnInit()函数中:
**ngOnInit() {...**this.jsons = [this.firstModelData, this.secondModelData, this.thirdModelData, this.fourthModelData]**...}**
现在,为了让我们的动画工作,我们必须在我们的setMap()函数下面创建一个新函数,叫做transitionMap():
**transitionMap(json, i) { const svg = d3.select('.world-map');
const color_domain = [2.5, 4, 7, 9, 10]; const color_legend = d3.scaleThreshold<string>().range(['#fee5d9', '#fcbba1', '#fc9272', '#fb6a4a', '#de2d26', '#a50f15']).domain(color_domain); svg.selectAll('path')
.data(json[i].features)
.transition()
.delay(100)
.duration(1000)
.style('fill', function(d) {
const value = d['Change_f'];
if (value) {
return color_legend(d['Change_f']);
} else {
return '#ccc';
}
})}**
这个函数将接受两个参数,json 和迭代器值,迭代器值将作为 json 数组的索引号。当调用该函数时,它将选择我们的地图 svg,刷新调色板,然后用相应 json 中引用的数据重新绘制我们的地图。因为我们的。data()函数正在引用 json[i],我们可以循环遍历刚刚创建的 json 数组,并在延迟一段时间后连续加载每个 json 文件。
现在,我们可以使用 Javascript 中的 setInterval()函数设置一个时间间隔来定义我们的“I”值。通过将以下代码添加到我们的 ngOnInit()函数的底部,该过程将在页面加载时开始:
**ngOnInit() {... let time = 1; let interval = setInterval(() => {
if (time <= 3) {
this.transitionMap(this.jsons, time)
time++;
} else {
clearInterval(interval);
}
}, 2000);}**
如果工作正常,您的页面上应该有一个成功的动画 choropleth 地图呈现:

最终 D3.js 地图动画|图片作者
恭喜你!接下来呢?
如果你已经看完了,我相信你可以开始想象一些额外的东西可以添加到这个可视化中——包括第二个气候变化场景的四个 json 文件。
对于添加按钮、滑块、图例和附加数据的代码,可以去我的 Github 拉更多我的代码!
把这一切放在上下文中
最后,本文的目标是为 web 构建一个相当复杂的地图动画。我们处理了一些不同的项目,从 python 中的数据整理,到基本的 web 开发原则,以及各种可视化技术。我希望你喜欢使用这个数据集,我鼓励那些有问题的人随时联系我。
此外,如果你喜欢这篇文章,请随时查看我的更多故事,在那里我讨论了我感兴趣的其他主题——即数据工程、机器学习等等!
也可以登陆我的网站【zach-alexander.com】联系提问!感谢阅读!
利用数据科学改善教育系统:印度尼西亚案例
变更数据
数据科学如何在实现印度尼西亚 2045 年教育路线图中发挥重要作用

我在印度尼西亚的小学建筑(来源:作者)
背景
学习是为了乐趣,所以学校应该是一个快乐的地方。这就是我在美国读小学时的感受。每节课都有一种令人兴奋的感觉,从做课堂项目的兴奋到看到我的老师装扮成她的科学家化身的幽默。我积极地和我的同学一起参加这些学习会议,我的老师是主持人,确保所有的声音都被听到。然而,当我搬到印度尼西亚时,我看到了如此显著的不同:学习主要是为了取得好成绩。课堂基本上是记忆课本和数学测验。主要目标是获得高分和班级前三名。老师被视为家长,而不是促进者。这改变了我对学校的看法,从一个快乐的地方变成了一种义务。然而,我现在很兴奋地看到变化即将到来。
2019 年,佐科·维多多总统任命科技独角兽 Go-Jek 的联合创始人 Nadiem Makarim 担任教育和文化部长,以改革这一体系。几个月后,他的团队发布了 2020 年至 2045 年的路线图,我觉得这非常令人兴奋。该路线图旨在通过一项名为 Merdeka Belajar (学习自由,这是下表所示的一系列系统性变化)的倡议,让年轻人更好地应对全球挑战。

来自 Merdeka Belajar 倡议的指示(来源:作者的分析)
这一举措将在大约 600 所学校进行试点,分为两组,100 所发达学校和 500 所发展中学校,根据准备情况区分不同的方法。试点小组的目标是到 2025 年底扩大到 10,000 所学校,并在未来十年内扩大到 30,000 所学校。学生应培养他们解决问题、认知和社交的技能,以体现以下特征:
- 高尚的
- 自主的
- 创造性的
- 合作的
- 批判思想家
- 全球意识
由于这似乎是一个伟大的方向,我的主要问题是如何确保正确的执行。这就是数据科学派上用场的地方。教育部确实在开发数字平台,以促进利益相关者的合作,提高学习效率。此外,这将是一个很好的工具,可以生成大量数据来收集关于该倡议实施情况的见解。要开始数据科学项目,我们需要首先定义项目章程,这将在下一节中讨论。
定义项目章程
在我们进入技术细节之前,我们必须首先确定目标、分析方法和利益相关者,这就是所谓的项目章程。就印度尼西亚的教育路线图而言,我们的目标是:
- 默迪卡·贝拉加测试在培养学生解决问题、认知和社交技能方面是有效的。
- 确定方向是否正确执行。
- 找出执行中的主要差距。
第一个目标需要一个最能代表有效性的北极星指标。根据路线图的最低评估部分,最好的衡量标准是学生在读写和计算方面的能力增长。这可以通过对默迪卡·贝拉加倡议前后的直接比较来实现。根据目标实现情况(使用公平基准)或与非试点学校的显著差异,试点学校的结果可被视为良好。
另一个值得考虑的北极星指标是评估学生的认知、社会意识和现实生活中解决问题的能力。这种方法可以使用能够捕捉这些方面的在线文章来专注于特定的主题。历史可能是一个很好的题材。我上学的时候,历史主要是记忆关键的日期和事件。除非老师鼓励,我的老师就是这么做的,否则学生不需要在这门课中运用批判性思维和解决问题的技巧。在这种情况下,可以向控制组(非飞行员)和治疗组(飞行员)提供在线论文。问题应该是:“从印度尼西亚的殖民历史中吸取教训,政府需要什么样的政策和制度来保护自己免受外国的剥削?”一个可接受的答案将提供殖民时期的真实案例,找出根本原因,并提出一套政策来防止类似事件在未来重演。对关键词进行分类的高级机器学习算法可用于预评估,以及专家的最终评估。论文评估结果将有助于确定的有效性。
对于下一个目标,我们需要对两组学生进行深入分析。Merdeka Belajar 可以作为一个框架,根据北极星标准来决定将学生分为哪几类。通过提取正确的数据,我们可以看到哪些领域存在重大差距,需要立即采取行动。
该分析可以转化为决策树,如下所示,以确定利益相关者的行动项目:教师、校长、地方当局和教育部。

印度尼西亚教育利益相关者决策树(来源:作者的分析)
为了应用这个决策树,需要一个完整的数据框架,这将在下一节讨论。
获取数据
在进行分析之前,我们需要确定如何提取、转换数据并将其加载到适当的数据库中。教育部目前有一些在线平台,如用于虚拟学习的 Rumah Bela jar(Study House),作为学校校长电子预算和商务平台的 Marketplace BOS,以及作为教师工作场所平台的 T2 Guru ber bagi(教师共享)。这些平台将有助于获取学校内部生态系统、教师表现、课程、教学法和评分系统的数据。地方当局也有平台更新学校的地理信息,以了解其当前的环境。父母也应该有一个平台,这样他们的参与程度就可以被追踪。将需要所有这些特征来提取关键成分以形成数据帧。请注意,非试点学校也需要这样做,以便进一步比较。

主要平台功能的主要数据输出(来源:作者的分析)
创建数据帧
数据框架中最重要的元素是拥有惟一的 ID:学校 ID、教师 ID 和学生 ID。这将使我们能够创建与各种数据集的关系,并形成一个整体数据源。下一步是将从平台中提取的数据转换成定义好的可量化指标。我想出了一些如下陈述:

我们模型的数据框架的关键列及其逻辑(来源:作者的分析)
在这些数据集被提取、转换并加载到数据仓库后,我们现在可以创建 DataFame 进行深入分析,如下图所示。请注意,数据应该处于相同的时间框架级别,在这种情况下,应该是每半年一次。

组装主要数据框架(来源:作者的分析)
应用数据科学模型
有了最终的数据框架,我们可以通过简单的分析得出关键的见解。我们可以创建简单的图表或重点表格,将北极星的结果与目标学校和/或非试点学校进行比较,并确定每个类别的主要差距。这种数据可视化将有助于为利益相关者提供总体分析。为了进行深入分析,我们可以使用数据科学来识别学生能力倾向增长或在线作文分数背后的关键因素。
我想到的第一个数据科学模型是 k-means 聚类。该算法将识别 k 个质心,然后基于相似性将学生聚类成 k 个组。一旦结果准备就绪,我们就可以看到哪些是最主要的集群,并根据最突出的指标进行描述。

k 均值聚类的例子(来源:scikit-learn.org)
在这种特殊情况下,我们可以从使用 k-means 算法对学生进行聚类开始,根据 6 个维度定义试点学校学生的共同特征。结果不仅会显示符合北极星标准的学生比例,还会显示他们的相似性。例如,数据可能会显示聚类 1 不仅在北极星中得分高,而且在生态系统和教育学方面也得分高,而聚类 2 则相反。下一步将是确定每个学校的主导集群,并根据决策树确定所需的支持类型。
使用线性回归模型,数据科学还可用于预测学生在应用改进措施后的北极星指标成绩。这个模型可以被设计来计算因果影响,北极星上升,从一套行动,以确定其有效性。它的工作方式是在没有干预的情况下,将实际结果与预测进行比较。这种预测将基于控制组,控制组可以是来自具有相似特征的非试点学校的学生。

线性回归的例子(来源:stackoverflow.com
结束语
当前的技术使我们能够收集和处理无限量的数据,并投入使用,包括改善教育系统。拥有多个数字平台,我很高兴看到数据科学如何帮助印度尼西亚教育部确保 2045 年路线图的无缝执行。本文中讨论的模型和技术只是一些例子,还有很多方法可以探索和分析教育发展的进程。
然而,主要的挑战实际上是解释和交流数据见解,以便做出正确的决策。总会有这样的情况,利益相关者由于对当前行为的强烈信念而选择否认这样的发现。有些人可能还会通过走捷径来实现指标,比如在能力测试之前上强化课,或者报告不符合实际的课程。另一个挑战是公众对试点和非试点学校的潜在抗议,因为许多利益攸关方可能对成为实验对象有负面看法,反之亦然。
然而,有了像 Nadiem Makarim 这样一位强有力的杰出领导人,我坚信这一路线图能够实现,数据科学将在其中发挥关键作用。从个人经验来说,我衷心希望印尼的下一代;在每个省、市、镇、村;将学校视为一个有趣的学习场所,而不仅仅是一种义务。
利用数据科学造福社会:移民及其致命路线
移民及其最终旅程的数据探索
过去——火车旅行
2016 年一个晴朗的夏日,我在从萨格勒布到维也纳的火车上,穿梭于欧洲各国首都之间,欣赏着窗外的风景,喝着小杯咖啡,做着客户演示。在距萨格勒布约 50 英里的小城瓦拉日丁,有一个短暂的预定停留,数百人开始涌入火车;他们无处不在——坐在厕所附近的地板上,随身带着袋子,里面装着他们曾经在自己国家生活过的小样本。一个家庭(我猜想)也进入了我的车厢,我拿起了放在一个空座位上的夹克;已经在车厢里的女乘客和我互相看着对方,我们通过眼睛对情况进行了短暂的非语言交流,并不情愿地接受了事情的现状。

凯蒂·穆姆在 Unsplash 上的照片
火车从车站开出后,这家人似乎有点舒服,清点了他们所有的财产。我开始和他们交谈,问他们要去哪里,他们用蹩脚的英语说,只要安全,他们就可以开始新的生活。我了解到他们来自叙利亚,夫妻俩都是一个村庄的教师,乘船到达希腊海岸,就在这时,一个小孩抱怨说饿了,打断了我们的谈话;这位母亲迅速在盒子里变出少量燕麦片,并从一个锡罐里拿出 baklava 形状的长面包,分成 8 块,分发给车厢里的每个人。我礼貌地拒绝了几次,但她很坚持,我勉强接受了。他们在火车上呆了一个小时左右,剩下的旅程多少有些沉默,只有断断续续的对话。我们互道再见,我告诉他们注意安全,并祝他们接下来的旅程好运。那次小小的互动就这样结束了。
我陷入沉思,决定写一篇关于这件事的博客,但我从来没有这样做。
现在——一项分析
几周前,我偶然发现了失踪人口项目和相关数据,它刷新了我对火车事故的记忆。所以,我花了一些时间在数据中寻找一些令人大开眼界的事实。
这篇文章并不是展示数据科学的能力,所以我不会在文章中添加代码块,但是如果你想继续,那么 GitHub 上有 python 代码和数据。 此外,我决定给主题赋予更多的单色观想。这是我的一点小小的尝试,来揭示一些人为了拯救自己的生命而不得不经历的致命而危险的旅程。
失踪移民项目正试图捕捉尽可能多的关于移民的信息,这些信息来自无数的来源——其中一些是合法的,其余的可能出于各种目的而被少报。esp 有很多缺失的数据。对于…
- 幸存者人数,85%失踪——人们可能在上岸后失踪,或出于政治原因,如安抚公民,没有太多的移民进入该国。
- 幸存儿童人数,86%失踪——轻微贩卖。
- 移民路线,55%失踪——由于政策,幸存者可能不告诉他们采取的确切路线,或者他们甚至不知道他们在哪里。
我处理了丢失的值,并更深入地查看了数据。数据时间线为 2014 年 1 月至 2021 年 2 月。
最主要的死因

溺水导致大多数死亡(图片由作者提供)
不出所料,溺水死亡人数最多。正如我所收集到的,数百人在几周的时间里乘着小船,任凭风的摆布,到达岸边。这些数字是根据找到的尸体数量估算的。你把浩瀚的大海和无情的暴风雨天气添加到图片中,然后进行计算。在我看来,x 轴上的数字更低。
致命的岁月

作者图片
不出所料,2016 年是移民危机新闻最多的一年。2020 年将会下降,但这可能是由于新冠肺炎或资源缺乏。
致命区域

地中海和北非仍然是温床(图片由作者提供)
地中海和北非的死亡人数最多,这可能是因为试图逃离该地区的人数太多。我从图表中选择的两个地区是美墨边境和东南亚。我确实知道中美洲的移民,但我从未把它等同于非洲/中东向欧洲移民的水平,尽管我知道洪都拉斯、危地马拉和尼加拉瓜的独裁政府以及委内瑞拉的失败经济。此外,孟加拉国移民到印度和缅甸的罗辛亚危机都是有据可查的新闻,但我没有与之相关的数字。
让我把死亡和幸存的人数加起来,看看移民的规模。

作者图片
看看数据告诉我们的在地中海前线所做的尝试的绝对规模。在大约 60,000 次尝试中,有 40,000 次成功,成功率超过 65%。
死亡原因
鉴于大多数路线是水上的,我预计大多数死亡是由于船只倾覆和船只失事。

地中海的数据高达 10k,我将其限制为 3k,这样人们也可以看到其他地区的条形高度(图片由作者提供)
美墨边境因枪击事件而闻名,但不知何故在这里却没有被捕捉到;上面写着死因是 【不明遗骨】 。大多数人步行几个月,他们的朋友和亲戚可能会在他们找到的任何地方埋葬他们,这就是骨骼遗骸出现在顶部的原因。
溺水仍然是大多数移民旅程的首要死因。
死亡时间表

对不起,坏的 x 轴,我知道它可以做得更干净,但我不记得如何。(图片由作者提供)
2016 年夏天的新闻充斥着欧洲移民危机,同样的情况也反映在上面的图表中,2016 年 5 月达到了顶峰。
出现峰值可能是因为两个原因:
- 2016 年 2 月,一条 125 英里长的反移民壕沟竣工,这条壕沟沿着突尼斯-利比亚边境延伸
- 北非的严重干旱(来源)
夏季月份预计会有更多的活动。

五月、六月、九月死亡人数最多(图片由作者提供)
迁徙旅程的温床
从上面的数据可以看出,我希望北非——靠近利比亚、中美洲和东南亚的地区——会被突出显示。
在地图上画出同样的东西,揭示了符合预期的真相。

作者图片
在过去 6 年的约 4 万例死亡中,我们没有信息对其中的约 2.8 万例进行分类。这表明,所有这些数字都是估计的,实际数字可能比我们面前的数字高得多。

作者图片
未来——未知的深渊还是希望?
尽管受到全世界的关注,这场危机仍然规模巨大。大规模的努力正在解决这个问题,但是到目前为止所取得的成绩是微不足道的。我知道这是一个多层面的危机,解决起来一点也不容易,但我们必须找到解决办法,否则人数将继续增加。
从火车上不知道家里发生了什么;我希望他们到达了他们想去的地方,开始了远离动荡的新生活。
Kein mensch 是非法的(没有人是非法的)

没有人是非法的(来源)
现在使用数据科学技能:让你的猫快乐。
使用 streamlit 和 API 在 30 分钟内创建一个亚马逊搜索 web 应用程序

图片由来自 Pixabay 的 Markus Bieck 提供
学习数据科学的组成部分既简单又有趣。无论是学习还是教授概念,关键之一就是要让它变得有趣。不是所有的概念都需要同时学习。简单点。让它变得有趣。
在数据科学中,你是否能挖掘出新的见解或训练出最好的模型可能并不重要。数据科学的一个重要方面是让您的用户或业务合作伙伴可以访问结果。一种方法是创建简单的交互式 web 应用程序来展示调查结果并与之交互。
在你开始学习高级技术之前,先试着做一些简单的事情。例如,你能以一种让你的猫感兴趣的方式呈现数据吗?
第一步:你的猫对什么感兴趣?
关键是找到感兴趣的话题。在这种情况下,什么话题能让你的猫保持兴趣?比特币?大概不会。猫薄荷玩具?现在你有所发现了。
第二步:数据在哪里?(5 分钟)
第一个想到的地方可能是亚马逊。这似乎是一个合理的初步猜测。
好的,你如何从亚马逊获得产品数据?你既不是合伙人也不是销售人员,所以你需要寻找外部 API。
一个快速的谷歌搜索就把我们带到了雨林 API。有 30 天的免费试用期(100 个电话)。免费很好。
同样好的是,有一个 API Playground 来帮助您生成可以在 web 应用程序中使用的 python 代码。注册并获得你的第一个代码片段不到五分钟。

使用 API Playground 创建代码片段—作者截图
3.构建一个简单的 Web 应用程序(5 分钟)
我强烈推荐 streamlit,它是一个快速简单的 web 应用程序。我已经在几个数据科学应用中使用过它,并且很满意。以后如果需要规模化,可以考虑 dash。但是现在,让我们看看 streamlit。
streamlit 的美妙之处在于您需要安装软件包,创建一个简单的。py 脚本,并运行应用程序。嘣,你跑了。你可以在一个窗口中更新代码,当你点击重新运行时,应用程序会自动选择更改。太棒了!
就我而言,我创造了一个新的环境。激活新环境后,我进行了 pip 安装 streamlit。我运行了一个简单的命令,然后我启动并运行了下面这个世界上最简单的脚本。说真的,就是这么简单。

运行 streamlit 应用程序—作者截图

进行代码更改并保存;Streamlit 提示您重新运行以获取更改—作者截图
4.剪切并粘贴我的雨林 API 代码(1 分钟)
我的下一步是将雨林 API 代码剪切并粘贴到我的脚本中,看看我在处理什么。在 streamlit 中使用 st.write(xx)而不是 print(xx)。

剪切、粘贴、重新运行—作者截图
5.重复直到你满意为止(15 分钟到 15 年)
因为我更喜欢使用数据帧而不是 JSON,所以我修改了 JSON 响应来创建更好的数据帧。这是它在幕后的样子。

将 JSON 转换成可读的数据帧——作者截图
我添加了两个按钮来选择两个特定的搜索。另一种选择是使用文本输入进行搜索。您可以很容易地看到如何将这个框架应用于 Amazon 搜索工具。

作者截图
下面是您可以运行的最终代码——当然是使用您自己的 API 密钥。
后续步骤和结论
进行调整和创建新的网络应用程序很简单。只要对我们的 cat 应用程序做一些调整,我们就有了一个通用的亚马逊产品搜索 UI。

作者截图
仅仅 5 分钟的编码就让我在一个交互式数据框架中显示原始数据,并将结果保存到一个. csv 文件中。您可以很容易地看到如何从一个有趣的小应用程序发展成为一个有用的数据科学工具!

作者提供的交互式数据帧的 gif
感谢您的阅读,我希望您能在创建自己的 web 应用程序时获得一些乐趣!通过添加新功能(图表、图形)慢慢增加你的技能,在你知道之前,你会用你的 viz 技能给每个人留下深刻印象。
使用数据科学预测负面客户评论
提高客户满意度,同时优化您的业务

如果你能自信地预测到一个顾客会留下一个负面评价,你的企业会有什么不同的做法?
- 主动干预以改善客户体验,并希望转移他们留下不好的公众评论分数?
- 从预测模型中的重要功能中学习,以解决客户体验不佳的根本原因?
在仔细考虑了成本效益权衡和公司实施纠正措施的能力后,答案是“是”。
“负面评论说服了 94%的消费者避开一个商家”——评论追踪者
商业问题——糟糕的顾客评价
巴西面向小企业的领先电子商务市场是 Olist。Olist Store 使巴西各地的商家能够使用 Olist 物流合作伙伴向客户销售和运送产品。交付后,客户会收到一份电子邮件满意度调查,使用 1(不满意)到 5(满意)的评分标准。
Olist 友好地发布了 2017 年至 2018 年超过 10 万笔交易的匿名客户评论和订单详情。这个流行的数据集已经被数据科学家下载了超过 76K 次。

按作者分类的 Tableau 图表
让我们为“坏顾客评论”建立一个预测模型,定义为在 5 分制中得到 1 分或 2 分。在我们已交付订单的大样本中,13.2%的评论得分为差(负面)。
我们将求解一个二元目标变量:review_bad = 1。
我们的目标是找到一个受监督的机器学习分类模型,它可以以最佳精度预测糟糕的客户评论。
高精度表明,当我们的模型预测一个差评时,它通常是正确的。这最大限度地降低了公司对预测的负面客户评论采取不正确行动的风险。
将数据转换为要素
Olist 提供了来自 8 个关系表的数据,它们具有很好的引用完整性。下图显示了一个熟悉的数据模型,它具有惟一的订单 id,便于连接多个商品和每个订单的付款。主数据表包括产品、客户和卖家。客户评论链接到一个整体订单,该订单可以包括多个产品或卖家。

由 Olist 通过 Kaggle 拍摄的图像
8 个表中的原始数据包括 52 个不同的列。我开发的预测功能可以按照这些领域进行分组。

作者图片
为了简洁起见,我没有包括这个项目中使用的全部代码。详情请参考我在 GitHub 上的 Jupyter 笔记本。
用于准备预测特征的数据转换:
- 过滤掉 2016 年的记录和未交付的订单(不到 1%)
- 只填写了少量存在空值的记录
- 如图所示,订单、付款、产品和卖家的综合指标
- 使用订单的生命周期日期计算的天数间隔指标
- 使用哈弗森函数中的中值纬度和经度,估计卖方和客户邮政编码之间的交付距离(公里)
- 为支付方式、客户状态、星期几和一天中的时段(上午、下午等)创建了一次性编码变量(标志)。)
- 清除任何订单的多个评论,仅保留最终评级;这影响了不到 1%的订单
视觉洞察力
在数据分析过程中,我通常将最终合并的数据帧从 Jupyter notebook 导出到 Tableau,以便更快地进行可视化检查。让我们来看看最有趣的观点。

按作者分类的 Tableau 图表
延迟交付天数: Olist 在订购时向客户提供预计交付日期,平均为 24 个预期处理/运输天数。如上所示,实际交付性能非常重要。对于延迟交付的订单(与预计日期的负差异),客户差评平均为 55%,而非延迟交付的订单为 10%。迟到天数越大,差评比例越高。

按作者分类的 Tableau 图表
卖家-产品质量:散点图描绘了延迟交付和差评之间的线性趋势,每个观察值都是卖家在所有客户中的平均表现。延迟订单并不能解释整个故事,因为许多卖家的交付绩效高于平均水平(圈出),但差评率更高。

按作者分类的 Tableau 图表
产品种类:使用树形图,我们可以看到不同产品类别差评率的变化。深入到一些单个产品,我们可以看到一些差评超过 50%的“狗”,它们显然没有达到客户的期望。产品的平均差评率是最终模型的一个重要特征。

按作者分类的 Tableau 图表
地理分布:巴西有 26 个州和 1 个联邦区。我们可以肯定地看到各州之间差评的差异。
- 圣保罗——在我们的数据中,该州占订单的 42%,在每一项指标上都表现出色。
- 里约热内卢——这个邻近的州占订单的 13%,在每项指标上都明显表现不佳。
这两个州都是巴西最大的两个城市之一的所在地,人口密度高,彼此相对接近。里约热内卢应该成为更深入挖掘交付绩效和产品组合的目标。为什么平均而言,客户会订购差评指数相对较高的产品?(148 的产品指数意味着平均购买的产品差评率比巴西整体平均产品差 48%)。
相关性分析
在 Python 中对我们的 review_bad 目标变量运行 Pearson 相关性,在很大程度上证实了我们在之前的图中看到的关联。

作者图片
当查看单个特征相关性时,我们看到所有形式的交付间隔(is_late、act_delivery_days、days_late 和 days_variance)都呈中度正相关。
我们还发现,产品和卖家总体差评率的正相关性分别为 0.23 和 0.17。
最后,我们看到了对订单/卖家商品数量、运费/订单价值和交付距离的一些其他次要影响。在 27 个州中,相关性大于 0.03 的仅有两个州是里约热内卢(+0.07)和圣保罗(-0.06),这支持了上面的州地图。
预测客户差评
七个监督学习分类模型在“精度”指标上进行优化,但寻找合理的“召回”(我们的模型正确预测的差评比例)。对于每个模型,我对 80%的训练数据进行网格搜索,并进行分层的 5 重交叉验证,以探索各种参数组合。

按作者分类的 Tableau 图表
比较的模型包括随机森林、AdaBoost、梯度增强、XG 增强、逻辑回归、SGD 分类器和高斯朴素贝叶斯。
相关特征使用 StandardScaler 进行缩放,因此我将一致的训练特征作为每个模型的输入(即使大多数模型不需要缩放)。
然后,每个分类器的最终“最佳模型”在之前未见过的 20%比例测试数据上运行,比较模型结果如上所示。
我推荐了具有以下性能的梯度增强模型:准确率 84%,召回率 29%,准确度 88%,AUC 64%。最佳估计值的参数为:
'learning_rate': 0.01, 'max_depth': 8, 'min_samples_leaf': 25, 'n_estimators': 100
使用这种具有标准 0.50 概率决策阈值的模型导致预测大约 1/3 的差评(召回),而仅在 6 次中有 1 次是错误的(精确度、假阳性)。这将是采取行动的一个开始,我们希望继续利用经验和更广泛的数据来完善这个模型。

按作者分类的 Tableau 图表
梯度推进模型中最重要的特性包括:
- Strong:延迟交付天数、天数差异、产品差评百分比、订单项目数
- 中等:订单上的卖家数量,卖家差评百分比
- 弱:实际交付天数、产品订单总数、卖方订单总数、订单运费、交付距离(公里)
总之,差评对延迟发货、产品/卖家历史、多个卖家/产品的配合最敏感。 Olist 应努力解决交付绩效不佳的根本原因。Olist 也可以尝试一个随机的成本效益试验,看看主动沟通、承认或客户让步是否可以防止差评被发布。
最终,负面评价是一份礼物。这表明你的顾客非常关心你的品牌,愿意花时间给你留下反馈。如果处理得当,即使是最糟糕的评论也可以变成积极的经历。— 艾米丽·希斯利普
数据质量观察
对数据质量的一些观察和本项目固有的假设。
- 样本与总体:100,000 个订单的样本,每个订单都有一个相关的评论评级,对于构建模型来说很好,但不能代表总体。鉴于只有 5-10%的客户写评论,而且不是每次都写,我们需要做一些工作来翻译我们的模型,以针对全部 Olist 订单进行归纳。
- 需要额外的功能:虽然 84%的准确率预测 29%的差评是一个开始,我们需要做得更好。我建议 Olist 为进一步的功能开发获取额外的数据:订单、客户退货、客户网络日志、客户服务互动、客户角色、人口统计、品牌/产品/卖家社会情绪的完整数据库,以及来自卖家的数据。
- 营销漏斗:Olist 还在 Kaggle 上发布了一个营销漏斗数据集,其中包含来自卖方资格认证流程的 2 个上游表格。当加入订单模式时,该数据不可用,因为它仅包含 2017-2018 年间注册的卖家,代表了这一时期实际订单的一小部分(大多数是由已建立的卖家完成的)。
https://chuckutterback.medium.com/membership
使用数据科学来预测病毒推文
数据科学
你能使用机器学习来预测哪些推文会传播开来吗?

亚历山大·沙托夫在 Unsplash 上的照片
在之前的一篇文章中,我写道为过去的数据科学竞赛构建了一个 XGBoost 模型来预测视频流行度。它涵盖了从加载数据和库到构建模型本身的所有内容。
Bitgrit 最近发布了一个新的竞赛,奖金为 $3000 💵待售商品:
⭐ 病毒推文预测挑战 ⭐
如果你想知道为什么推文会像病毒一样传播,这是你利用数据科学找到答案的绝佳机会!
比赛将于2021 年 7 月 6 日结束,因此注册以获取数据,并跟随本文开始吧!
如果你不能通过付费墙,请点击这个链接阅读这个故事。
目标
开发一个机器学习模型,根据推文内容、推文附带的媒体和发布日期/时间等属性,预测每条推文的病毒级别。
数据是什么样的?
📂 **Tweets**
├──test_tweets_vectorized_media.csv
├──test_tweets_vectorized_text.csv
├──test_tweets.csv
├──train_tweets_vectorized_media.csv
├──train_tweets_vectorized_text.csv
└──train_tweets.csv
📂 **Users**
├──user_vectorized_descriptions.csv
├──user_vectorized_profile_images.csv
└──users.csv
tweets 文件夹包含我们的测试和训练数据,主要特征都在tweets.csv中,其中有日期、标签数量、是否有附件以及我们的目标变量——病毒率等信息。矢量化 csv 文件是推文文本和媒体(视频或图像)的矢量化格式
“用户”文件夹包含有关用户的关注者和关注计数、推文计数、他们是否被验证等信息。他们的个人简历和图像也矢量化成单独的 csv 文件。
数据之间的关系
用户数据通过user_id关联,而推文数据通过tweet_id连接。
下面是这种关系的可视化。

关于竞赛指南部分数据的更多信息。
现在你有了目标和一些给你的数据信息,是时候开始做数据科学任务了,以实现预测推文病毒式传播的目标。
所有的代码都可以在Google collab或者 上找到威风凛凛的 。
注意,出于轻量级的目的,并非所有的代码都包含在本文中,所以请参考笔记本以获得完整的代码。
加载库
与所有数据科学任务一样,您首先要为自己配备完成各种任务所需的库。
加载数据
你可以在 2021 年 7 月 6 日之前在这里报名参赛获取数据。
打印每个数据的形状:
我们可以看出,我们的数据集中总共有 52 个用户有多条推文。请注意,矢量化媒体的行数比推文少,这意味着并非所有推文都有媒体,还要记住,一些推文有多个媒体。这将在合并数据时导致一些问题,但我们稍后会担心这个问题。
还要注意,矢量化的媒体/图像都有相同数量的列/特征——2048,而矢量化的文本都有 769 个特征。
现在让我们继续进行一些探索性的数据分析,以便更好地理解我们的数据。
探索性数据分析
EDA 对于发现数据趋势以及确定需要哪些转换和预处理来为建模准备数据非常重要。
数据是什么样子的
看数据,看起来挺标准的。我们有两个主键、特征和目标变量——病毒率。
但是请注意tweet_topic_ids是如何包含数组的?稍后,我们将不得不做一些预处理来处理这个问题。
我们的矢量化数据看起来很标准,其中每一列代表数字特征空间中的一个坐标。
现在让我们分析一下我们的 tweets 数据中的特征。
推特数据特征分析
我们的推文中总共有 11 个特征。
首先,我们来看看病毒式传播。
我们看到,平均来说,病毒水平为 2,大部分是 1。
查看时间特征,我们看到来自我们数据的推文在 2020 年有最高的计数,大多数推文是在 12 月和第 27 天创建的。他们也大多在晚上发推文(17 小时)。
继续讨论标签、网址和提及次数。大多数推文没有标签,只有一个网址,也没有提及。
绘制附件类和语言 id,我们看到 A 类是最普遍的,而 b 类很少。至于语言 id,很大一部分是 0,我们可以假设是英语。
大多数推文也有媒体附件。
特征的相关性
现在让我们看看我们的特征是如何相关的,或者换句话说,我们的特征和病毒之间的线性关系有多强。
形象化的一个好方法是使用热图。
从我们的热图中,我们可以看出,我们的特征与病毒传播无关,但一些特征确实彼此相关。
以数值表示相关性,我们看到相关性相当低,其中一些是负的。
尽管如此,这并不意味着我们的特征是无用的,因为它们仍然具有预测能力,所有这一切意味着它们不能“线性地”预测病毒水平。
现在让我们看看我们的用户数据。
用户数据分析
我们在用户中总共有 10 个功能。
可视化计数数据,我们可以观察到,喜欢计数大多在 0 到 5000 个喜欢之间,离群值在 300k 左右。
对于users_followers_count来说,很大一部分用户在 0 到 100k 的范围内,部分用户的关注者高达 160 万。
对于users_following_count来说,大部分也在 0 到 100k 的范围内,但也有一小部分超过 1m。
对于users_listed_on_count来说,他们中的大多数在 0 到 5000 之间,一些用户被列在多达 40 万的名单上。
我们的用户大多拥有 50 万左右的 tweet 数,大多数账户都是在 2011 年和 8 月份创建的。
转到二进制数据,我们的大多数用户在他们的帐户上列出了位置,他们在他们的简历中有一个 URL,并且他们中的大多数没有被验证。
现在我们已经对数据做了一些 EDA,让我们开始为建模做准备。
数据预处理
这些是我们将在这个特定示例中执行的几个预处理任务。注意肯定有各种各样的方法来实现它,更不用说还有更复杂的预处理和特征工程可以做,所以只把下面的方法作为一个例子,而不是一个客观的方法。
处理缺失数据
使用我编写的助手函数(笔记本中的代码),我们将检查行中缺失数据的数量以及用户和推文数据的百分比
在主题 id 列中有一些丢失的数据,所以我们将处理它。我们还将使用热图来可视化缺失的数据,因为这对于搜索特定模式非常有用。我们丢失的数据似乎很分散。
为了处理na值,我们将用一个 0 — [‘0’]的数组填充它们,以表示该特定 tweet 没有主题 id。我把它放在一个数组中的原因是,以后当我们进行单热编码时,它可以表示为一个零的 topic_id。(同样,这只是一种方法,还有其他方法可以解决这个问题)
数据清理是一项必备技能。如果您想提高您的数据清理技能,请查看我们的 使用 Python 进行数据清理的文章。
我还注意到 hashtag、URL 和 reference 的计数都是浮点数据类型。然而,它们根本没有小数位,它们有唯一的值,可以被认为是分类的,所以我把它们转换成整数,如下所示。
tweets 功能上的一个热门编码
对于 topic_ids,数据是一个数组,所以非常棘手。你能做的是将数组分解成单个的值,然后将它们变成虚拟的。
对于年、月、附件等其他列,使用 pandas 库中的get_dummies()进行编码非常容易。
至于小时数据,由于我们必须考虑时间数据中的时间特征,我们必须进行循环编码。
在我们对我们的特性进行编码后,我们可以删除旧的特性,然后加入新的编码特性。
现在,我们的火车推文数据有多达 151 列!
一个关于用户特征的热编码
我们还将对我们的用户特性进行一次热编码,主要是年、月和用户验证列。就像我们对火车推特数据所做的那样。
推文数据的特征选择
矢量化数据有大量的特征,如果我们能够“将小麦从谷壳中分离出来”,或者选择可以帮助我们预测病毒传播的最佳特征,那将是最好的。这可以帮助我们节省尺寸,加快我们的模型训练时间。
我们将从我们的train_tweets_vectorized_media数据开始。由于不是所有的推文都有媒体,我们必须将每条推文的病毒式传播添加到有媒体的推文中。这可以通过在 tweet_id 上进行右连接来实现。这样,每条推文的所有病毒式传播都可以与推文图像特征相匹配。
因为我们不希望 train_tweets 中除了病毒式传播之外的列与我们的矢量化数据合并,所以我们可以使用.difference函数只获得病毒式传播。
然后,我们可以利用这个数据集,开始进行特征选择。
我们的目标是病毒式的,特性是包含“img_”的列
使用集合库,我们可以计算模型选择的特性的数量。在这种情况下,只有两个特性被认为是不“有价值”的,所以它不是那么有用。
尽管如此,通过我们模型中的索引,我们可以将它们与我们的媒体和 tweet id 连接起来,形成比以前少了两列的最终train_tweets_media数据。
对于train_tweets_vectorized_text,和上面的情况一样。我们必须做一个正确的连接来匹配每条推文的病毒式传播,然后进行特征选择。
用户数据的特征选择
为了对用户数据进行特征选择,我们需要为每个用户添加病毒级别。但由于每个用户都有多个推文,一种方法是获得他们的病毒率的中位数,然后执行右连接,以匹配每个用户的病毒率中位数。然后,使用这个新的数据框,我们可以执行特征选择。
使用熊猫的groupby和agg,我们可以找到中间值,然后合并它们。
将一切融合在一起
现在是时候将所有内容合并到最终的列车数据帧中了。
我们面临两个主要问题:
- 不是所有的推文都有媒体,有些推文有多个媒体。我们如何将它与我们的火车推文数据框架结合起来?
- 用户矢量化数据和我们的 tweets 矢量化文本数据具有相似的列名,这将在合并时导致问题(pandas 将向列追加 _x 和 _y)
一种方法是根据 tweet id 获取特征的平均值,以处理第一个问题。我们可以使用groupby函数来实现。
那么要解决特征列名称重叠的问题,我们可以使用重命名功能。
现在我们可以合并所有的数据框。
你可以有不同的方法来处理这个问题,但我的方法基本上是首先合并推文数据。因为不是所有的 tweets 都有媒体,所以会有数据丢失,所以用零填充这些 NA 值。
下一步是合并用户数据。之后,我将我的 tweets 数据加入到train_tweets中,然后是用户数据。
我们数据框的最终形状是 2946 列。
我们在训练中所做的预处理必须在测试数据上重复。如果不这样做,我们使用训练数据训练的模型将无法用于我们的测试数据。(这里没有显示测试数据预处理的代码)
为训练和测试匹配列数
在对我们的测试数据应用相同的预处理之后,我们面临另一个问题。
训练和测试的列数不匹配,这是因为它们中的任何一个缺少某些功能。
为了解决这个问题,我们可以在列上使用 set 函数,然后将它们相减,找出它们之间的差异。然后,我们可以将那些缺失的列添加到数据中,并将它们设置为零。
我们还发现我们的火车在测试中缺少列,但幸运的是没有。
构建轻量级 GBM 模型
与 XGBoost 相比,使用 LightGBM 有几个好处,主要是更快的训练速度和更高的效率,以及更准确和使用更少的内存。
我们将建立一个非常简单的基础模型,没有任何参数调整。如果您想更多地尝试调整模型,我在下面链接了一些资源。
列车测试分离
列virality、tweet_user_id、tweet_id和user_id不是特性,所以我们可以删除它们。然后,我们将病毒率设为我们的目标变量。
然后,我们将数据分成 70%的训练数据和 30%的测试数据。
使数据符合模型
创建基本 LGBM 分类器模型。
安装x_train和y_train,我们可以训练我们的基本轻型 GBM 模型。
然后利用它,我们可以在测试数据集上预测它。
我们可以使用sklearn.metrics中的accuracy_score函数轻松地打印出我们模型的准确度分数
我们的基本模型达到了 66.45%的准确率!
我们还可以绘制出模型中重要的特征。
这里我们看到user_follower_count在预测病毒传播中是最重要的,这很有意义,因为你的粉丝越多,你的推文就越有影响力。
我们还在前 10 个特性中看到相当多的user_vectorized_profile_images特性,还有user_has_location和user_like_count
这种准确性仍然可以提高,但我们将在这里结束,并使我们的模型适合公开数据。
使模型适合测试/公共数据
瞧啊。我们有一个简单的模型来预测推文的病毒传播水平。
这样,您就可以发布提交文件并上传到 Bitgrit 竞赛了!
提高准确性的技巧
赢得数据科学竞赛并不容易,仅仅有一个工作模型是不够的。您需要一个具有高精确度的模型来战胜其他解决方案,更不用说还能很好地处理看不见的数据了。
提高准确性的一些技巧:
一个很好的学习方法是通过 Kaggle 上的好笔记本。这里有一些可能会有帮助。
这篇文章到此为止,谢谢你的阅读,希望你学到了新的东西!
另外,如果你认为我有更好的方法来解决这些问题,请在下面留下评论。我将非常感激。
本次比赛由 Bitgrit 主办,如果你喜欢这篇文章,也许你会喜欢这些文章:
关注 Bitgrit 数据科学出版物了解更多信息!
关注 Bitgrit 的社交网站📱了解讲座和即将举行的比赛的最新消息!
使用数据成为敏捷分析团队
我们如何使用可操作的敏捷来改进我们的工作流程 30%
回到今年(2021 年)3 月,我开始了解我团队的 JIRA 董事会是否达到了应有的效率。我向工作中的一位敏捷教练寻求帮助(你好,Zara)。接下来是一段旅程,我的思维方式和工作方式发生了完全的转变,这是我没有想到的。
在我们的第一次会议中,Zara 提到我们 JIRA 董事会的状态非常好,并且符合敏捷最佳实践。作为一个分析团队,我们试图“采用”看板方法和思维方式来帮助管理和可视化我们的工作。利益相关者会提出待处理的问题单,并与他们合作,我们会相应地安排优先级。不幸的是,这就是我们采用看板的终点。我们没有做任何事情,甚至没有代表敏捷的核心哲学。我们不是一个敏捷的团队!
看板板设置
下面的截图显示了我们当前的看板。如果你使用看板方法,你的看板可能看起来像这样:

产品分析团队 JIRA 董事会
我们用来管理工作流的列有待办事项、本周、进行中、评审中、完成和等待(希望名称不言自明)。我们用泳道来代表我们支持的不同团队。还有一个专用于职能工作的泳道,包括个人发展、数据质量改进、招聘等
可操作敏捷(AA)
在我们查看了董事会设置后,Zara 问我是否遇到了 可操作的敏捷 。我没有。但当 Zara 从演示帐户加载这个散点图给我看 AA 时,我就被迷住了。我不知道我在看什么,我只知道不管它是什么,我想要它——毕竟我是一名分析师。“周期时间散点图”的屏幕截图,演示数据如下:

可操作的敏捷演示数据
什么是可操作的敏捷?
可操作的敏捷是一种帮助你理解和可视化你的团队工作如何在过程中移动的方法(专栏)。有许多图表可以帮助您分析关键的工作流程指标,例如:
- 周期时间—票证从“积压”中取出后到达“完成”栏所需的时间
- 吞吐量—任何时间段内完成的票据数量
你也可以运行蒙特卡罗模拟来预测一件工作需要多长时间。下面是你可以用 AA 做的所有事情的列表。我已经标记了我们每周用来帮助我们处理过程的那些。

了解可操作的敏捷
尽管我对图表有明显的热情,但在我完全理解我在看什么以及我们如何从中受益之前,我不会天真到改变团队的工作方式。所以我和 Zara 开了一系列的会议来学习更多关于敏捷工作方式和可操作的敏捷。
以下是我和 Zara 一起参加的为期 3 周的速成班的总结和结果:
可操作的敏捷让我能够在宏观层面上分析我的团队的平均周期时间(见下图),并在微观层面上理解何时票据有风险。使用 AA 中的数据,我建立了我的团队的历史基线数字,并确定了我们应该采取的行动来改善它们。我还对 JIRA 董事会做了一些改动,以便我们能够更有效地使用 AA。了解了足够多的信息,知道这是我们可以从中受益的东西,并对我所谈论的内容充满信心,我与团队分享了 AA-他们对图表同样感到兴奋。一旦他们都买了,我们为自己创造了一个 OKR。
设定目标
我们的目标是减少数据的可变性,并作为一个团队变得更加可预测。关键结果是将 Q2 第 80 百分位循环时间减少了 30%。
结果
注意:每个点代表一张或多张 JIRA 门票,y 轴是周期时间,即门票移动到“完成”需要多长时间。x 轴是票被移动到“完成”的日期。一起创造了我们工作的时间序列。
以前

敏捷前图表
在...之后

后敏捷图
乍一看,可能不容易看出影响,但如果仔细观察,您会发现以下情况:
- 散点图上的数据点被压缩得更多,异常值明显更少(可变性降低)。
- 第 80 百分位线已经从 30 天变成了 19 天(改善了 37%)。 OKR 实现了!
- 第 70 和第 50 百分位线分别下降了 30%和 33%。
我们是如何做到这一点的?
访问我们的历史数据是有用的,因为它让我们看到我们作为一个团队是如何做的,但是如果我们想看到我们的周期时间有所改善,我们需要改变我们的工作方式。
1)站立
我们做的最基本的改变是引入了单口站立!我们每周一都有团队会议,涵盖了一大堆主题,包括团队在做什么,但我们很少看 JIRA 的董事会。因此,我们决定花前 30 分钟浏览每个泳道和栏目中的每张有效门票。我们还在周三早上增加了一个更短的 15 分钟站立。
看黑板这个简单的动作经常在这方面发挥巨大的作用。也许这只是你把票移到“完成”时产生的多巴胺冲动。不管是什么原因,做倒立肯定有积极的心理影响。我甚至可以肯定,我们减少的周期时间中有一部分是因为我们及时地移动和关闭了票证。
2)减少在制品
每周一,我会向我们所有的利益相关者发送我们正在做的事情的更新,但到了周中,很明显团队不会完成他们承诺的一半。这不是他们的错!持续不断的会议、数据问题、反馈循环、电子邮件、松散信息和“小”请求意味着不可能找到固定的时间来专注于实际工作。
所以除了站立,下一个改变是减少我们每周的工作量。我们承担的更少,所以我们可以交付更多。再一次,进展中的事情越少的心理影响,意味着团队可以更集中精力,更频繁地交付。
3)审查进行中的老化工作
我们在每次会议中使用的第二个图表是“老化工作进行中”图表。这个图表让我们可以看到每个活动票证的年龄,并发现任何有风险的内容。有时事情太多,很容易忘记时间。拥有识别风险的简单方法,意味着我们可以快速行动。这可能是更新利益相关者,重新安排工作的优先次序或寻找依赖。(是的,我知道有一张票漂浮在红色区域。我们只是人类)。

4)更好地界定更大任务的范围
我们还没有完善这一点,但我们更加关注它。有一项分析花了两个月才完成,事后看来,我们应该把它分解成更小的任务。事实上,这个分析是促使我联系 Zara 的催化剂。结果是更好地界定了工作范围,与利益相关方商定了明确的成果,并在需要时使用了 epics。
5)控制可控性
以上所述让我们意识到,虽然我们容易受到许多我们无法控制的因素的影响,但作为分析师,有太多东西是我们可以控制的。当有人要求我们做不在优先考虑的事情时,说“不”或“还没有”就是其中之一。直到今天,我知道团队仍然会对“小恩惠”说“是”,但是他们说的越来越少了。我还观察到团队态度的变化。他们不再纠结于任务,从而更多地限制了自己的时间。当 SQL 查询超时时,它们会继续前进。当他们在等待利益相关者的反馈时,他们会继续前进。
经验教训
我们已经看到我们的工作流指标有了巨大的改进,我真的相信我们学到的东西可以应用于任何分析团队,但如果你自己也开始了这一旅程,我想分享一些最后的经验教训。
- 为你的团队创造一个安全的空间 —这不是一个点名羞辱的练习。他们应该能够开诚布公地讨论为什么事情比他们希望的时间长,而不会感觉像在显微镜下一样。
- 保持纪律d——在任何情况下都不要跳过单口相声。
- 继续适应和迭代——我们仍然没有做到完美。不断回顾你的过程。
- 说实话 —这些数字的提高是因为你改进了你的流程,还是你在操纵这些数字?
- 采用良好的 JIRA 实践 — AA 并不完美,所以要养成使用 JIRA 的特性来帮助你去除数据中的噪音的习惯。
最后的话
我不知道我们是否已经达到了敏捷团队的状态,但是我们肯定不是三月份的那个团队了。
我非常感谢 Zara 让我看到了一个全新的数据世界,但更重要的是,它有耐心帮助我了解如何正确使用它。如果你足够幸运,在你的公司里有敏捷教练,我建议你去找他们。在过去的 5 个月里,我学到了很多。
你可以在 Twitter 上关注我,也可以不费吹灰之力在 Medium 上关注我和 废话连篇 。如果你喜欢这篇文章,请向下滚动,给它一些掌声:)
阿黛尔是一个了不起的歌手-数据可以说明为什么!
如果你想证明你最喜欢的歌手有多棒,为什么不用数据呢?

马特·博茨福德在 Unsplash 上的照片
ONG 已经存在了很久,并以这样或那样的方式慢慢进入了人们的生活。我从 9 岁左右开始定期听歌,近十年后,我已经沉浸在许多不同流派的歌曲中。
这些年来,在我听过的所有艺术家中,我最喜欢的是阿黛尔。如果你听过她最著名的歌曲如“ 你好”、“有人喜欢你” 或 “在深处打滚” ,你就会知道她的唱腔是丰富的,令人着迷的,她的歌真的把强烈的情感倾注到你的心里。它们让你感受到从未有过的感觉。
听“有人喜欢你”,让我 10 岁的时候第一次感到心碎。
作为数据科学项目的一部分,我决定利用我对歌曲和阿黛尔的欣赏,分析 这个 数据集。它收集了 1921 年至 2020 年间在 Spotify 上发布的 16 万多首歌曲的音频特征。
Spotify 是一项数字音乐、播客和视频流媒体服务,让您可以访问来自世界各地艺术家的数百万首歌曲和其他内容。
结果呢?
我想出了问题的数据驱动原因:为什么阿黛尔是一个了不起的歌手?
主要支持问题
为了恰当地展示阿黛尔的神奇,我需要问一些支持性的问题,并在数据集中找到它们的答案。
我有三个问题:
- 最近歌曲有什么值得注意的趋势吗?
- 与 2020 年的竞争对手相比,阿黛尔的受欢迎程度如何?
- 阿黛尔和她的顶级竞争对手之间的主要区别是什么?
在下面的章节中,我将只讨论与我的论点相关的部分发现。如果你想要一套完整的发现,可以去看看我的 Kaggle 笔记本 或者我的 github 。
更多关于数据的信息
在我们深入研究答案之前,我将首先解释数据集的一些相关但不那么直观的特性。如果您不确定某项功能的含义,请随意返回此处。
valence: 一首歌在音乐上有多积极/快乐。popularity: 一首曲目在获取数据时的受欢迎程度。要知道数据集是 2020 年 6 月发布的,所以我们这里使用的所有流行度特征,都是指 2020 年 6 月的赛道流行度。explicit: 曲目是否包含露骨内容。 1 为显式,0 为不显式。speechiness: 一个音轨中的口语单词等级。更接近于 1 意味着只专门包含语音。接近 0 表示仅包含音乐。
1.近年来宋的发展趋势
对于这一部分,我把“最近几年”框定为从 2010 年开始。换句话说,我所做的观察,是从 2010 年到 2020 年每一年的平均歌曲特征中得出的。

数据外观图片|作者图片
此外,我还获得了 2010 年至 2020 年每年 100 首流行歌曲的平均特征表。
我在这部分有一些非常有趣的发现。
a)从 2010 年开始,歌曲变得越来越悲伤,但从 2017-2018 年开始,歌曲变得越来越快乐

多年来普通歌曲和流行歌曲的价格比较|作者图片

多年来普通歌曲和流行歌曲的能量比较|作者图片
- 从 2010 年到 2017 年至 2018 年,普通歌曲的价格和能量大多下降,然后再次飙升,这意味着歌曲在反弹之前在音乐上变得越来越悲伤和沉闷。
- 2010–2020 年的流行歌曲也遵循这一趋势,在 2016–2018 年达到最悲伤和最没有活力的水平。
- 这些趋势可能表明,2016 年至 2018 年是一个充满心碎的时期,因此更多的歌曲更悲伤,这些严肃的歌曲最终变得流行。然而,我们需要做更多的数据挖掘来证明这一点。
- 足够有趣的是,2020 年似乎鼓励歌曲在快乐和积极方面大幅飙升。
在 2020 年所有的消极中,我想这个世界转向了歌曲来获得积极的突破。
b)歌曲变得越来越适合跳舞。

多年来普通歌曲和流行歌曲的可跳性呈上升趋势 | 作者图片
普通歌曲和流行歌曲的可跳性呈上升趋势。
c)歌曲越来越露骨。

多年来普通歌曲和流行歌曲清晰度的上升趋势|作者图片
随着时间的推移,普通歌曲更容易露骨,这种露骨的歌曲越来越受欢迎。
d)这些趋势与阿黛尔有什么关系?
在这一点上,你可能想知道,这些趋势与阿黛尔有什么关系?简单来说:
阿黛尔并不“新潮”。
看看下面的数字:

歌曲的价格和可跳舞性分布|按作者分类的图像
阿黛尔没有追随最近歌曲的潮流。与 2020 年的歌曲相比,她的歌曲平均价低,平均可跳性也低。
除此之外她的明确度仅为 2020 年歌曲的1/10:

清晰|作者图片
至少在过去三年左右,所有这些特征都呈上升趋势,如果阿黛尔坚持自己的风格,她可能会进一步落后于潮流。
第 1 节的结论:
自 2017-2018 年以来,歌曲一直趋向于变得更快乐,更适合跳舞,更明确。但阿黛尔的音乐类型属于 流行、R & B 和灵魂乐 的范畴。因此,她的歌曲更像是饱含深意的情感歌谣——它们不追随快乐、乐观的潮流。
因此,很容易得出这样的假设:阿黛尔的歌曲在最近一段时间可能不那么受欢迎了。嗯,这就把我们带到了下一个问题。
2.与 2020 年的竞争对手相比,阿黛尔的受欢迎程度
根据我们掌握的数据,发现阿黛尔在 2020 年有多受欢迎是很简单的。歌曲特征已经包括了popularity,所以为了得到阿黛尔受欢迎程度的一个很好的代理,我们可以总结她所有歌曲的受欢迎程度,并将其与所有其他艺术家的受欢迎程度相比较。
这样做,我们得到:

作者图片
这个数字看起来很合理,但我们必须记住,数据集也包含许多 20 世纪的旧曲目。也就是说,当我们使用这种方法时,我们是在将阿黛尔的歌曲与弗兰克·辛纳屈、猫王等的传奇经典进行比较。
事实上,根据这种方法,以下是最受欢迎的五位艺术家:

根据合计人气排名的前 5 位人气艺人|作者图片
称披头士或弗兰克·辛纳特拉为阿黛尔的“竞争对手”似乎不太公平。毕竟,他们是永恒的传奇,几十年来,他们的歌曲已经享誉全球。与此同时,阿黛尔的第一首歌《故乡的荣耀》在 2007 年才发行。
因此,我从 2007 年开始发行歌曲的艺术家中挑选了一部分,认为这些艺术家是阿黛尔的竞争对手。
结果如下:

前 5 名最受欢迎的艺术家(2007 年以后发行的歌曲)+ Adele |作者图片

阿黛尔受欢迎的确切数字|作者图片
从这一点开始,当我们讨论前 5 名艺术家时,我们指的是在 2007 年以后发行歌曲的艺术家。
第 2 节的结论
阿黛尔在 2020 年极受欢迎。她是 Spotify 全球排名前 1.63%的艺人之一,也是 2007 年以来发行过歌曲的前 0.3%的艺人之一。
带着这个想法,我试图找到最后一个问题的答案。
3.阿黛尔和她的竞争对手之间的主要区别
知道了阿黛尔的受欢迎程度,我想进一步发现是什么把前五名最受欢迎的艺术家与阿黛尔区分开来。
她是否在某些方面有所欠缺?前 5 名艺人之间有什么共同点吗?
通过更多的操作,我找到了前 5 名艺术家的平均轨迹特征。与阿黛尔的平均轨迹特征相比,以下是获得的结果:
a)他们的歌更快乐,更有活力,人们更可能随着他们的歌起舞
****
与阿黛尔相比,前 5 名艺术家的能量和效价|作者图片

与阿黛尔相比,前 5 名艺术家的舞蹈能力|作者图片
- 在三个特征能量、价和可舞性中,所有前五名艺人的平均曲目都比阿黛尔的平均曲目高。
- 换句话说,就拥有快乐和振奋人心的歌曲旋律而言,前五名艺术家比阿黛尔更追随流行歌曲的最新趋势。
b)大多数前 5 名的歌曲具有较高的口语词比率

与阿黛尔相比,前五名艺术家的演讲|作者图片
- 德雷克、未来、利尔·简自豪·维特、李尔·韦恩的平均语速都是阿黛尔平均语速的至少 2.5 倍。
- 这是意料之中的,因为除了泰勒斯威夫特,前 5 名的其余艺人都是说唱歌手。
c)前五名艺术家中的大多数都发行了许多更露骨的歌曲

与阿黛尔相比,前 5 名艺术家的清晰度|作者图片
- 再一次,除了泰勒·斯威夫特之外,所有前五名艺术家的清晰度都比阿黛尔高得多(相差超过 10 倍)。
- 有人可能会认为,因为他们是现代说唱歌手,他们有很多明确的曲目。嗯,需要注意的一点是, 相关性并不等同于因果关系 。
- 换句话说,不能因为他们都是现代说唱歌手,内容露骨度高,就代表因为前者,后者就跟着来了。
d)有趣的是,阿黛尔的歌曲比大多数歌曲的平均受欢迎程度都高。

与阿黛尔相比,前 5 名艺术家的清晰度|作者图片
- 平均来说,阿黛尔的歌曲实际上比未来、里尔·简自豪·维特、李尔·韦恩和泰勒·斯威夫特更受欢迎。
- 她的歌曲在流行程度上也接近德雷克的歌曲。
那么为什么阿黛尔不在前 5 名艺术家之列呢?
e)阿黛尔所缺乏的
好吧,因为我们总结了每个艺人歌曲的流行度,作为一个艺人受欢迎程度的代表,这导致了一种可能性——与前 5 名艺人相比,阿黛尔发布的歌曲很少。
果然,经过更多的数据探索,我们可以看到:

轨道数量比较
注意:这里我用了“已经涉及”这几个字,因为只要一首 Spotify 的歌把一个艺人列为它的创作者之一,我就把这首歌包括进去了。
第 3 节的结论
与前五名艺术家相比,阿黛尔的音乐在风格和情绪上相对不同。然而,这并不是阿黛尔不在前 5 的原因。****
相反,这仅仅是因为与前五名艺人相比,她很少创作歌曲。
总体结论:那么为什么阿黛尔是一个伟大的歌手/音乐家?
原因是我们之前的三个结论的汇总。
1.阿黛尔的音乐更悲伤,更缓慢,更深情的音乐类型没有遵循最近流行歌曲的趋势,快乐和乐观的歌曲。
2.然而,数据显示,她仍然是最受欢迎的艺术家的前 0.3%(从 2007 年开始发行的歌曲)。
3.事实上,她不在前五名的原因,不是因为她不新潮的风格,而是因为她发行了一些小曲。她的歌曲质量不言自明,因为它们的平均受欢迎程度可与前 5 名艺术家发行的歌曲相媲美。
既然我们已经结束了,我想问你几个问题:
当提出这些论点时,你会同意阿黛尔是一个伟大的音乐家/歌手吗,即使你不喜欢她?
这种数据驱动的论点是否遗漏了什么?
感谢您阅读本文!我希望你觉得这很有趣。热烈欢迎任何评论或批评。****
此外,请随时与我联系,就这些话题进行交流,并通过 LinkedIn 与我联系。
在 ML 管道中使用数据块集群
运行生产 Spark 作业,并在 Databricks 作业集群上培训 ML 模型。

在这篇文章中,我想描述一下我在 Databricks 作业集群上执行 production Spark 和 ML 作业的经历。
到目前为止,我是 Databricks 解决方案的忠实粉丝,因为我发现它们比我用过的替代方案好得多,不,我不是 Databricks 的员工。
在我的日常工作中,我开发了一个自动化的基于人工智能的预测分析平台,该平台简化并加快了构建和部署预测模型的过程。
过去两年,我一直在构建数据和 ML 管道,包括数据清理、结构化、特征工程、培训、评估、预测和监控工作。
有两个原因让我开始使用 Databricks:
- 我想让别人帮我管理 Spark。我不想在 Spark echo-system 上花费太多时间(安装、配置、优化和调优等)。
- 我们在 Kubernetes 上使用 JupyterHub 作为游戏环境。管理它相当具有挑战性,尤其是当我们希望它可扩展并运行繁重的 Spark 任务时。
自从我开始使用 Databrikcs 以来,我发现了许多我喜欢的更强大的功能,我将在这篇文章中讨论这些功能。
在数据块集群上运行生产作业
我们根据自己的需求开发了定制的 Databricks 气流操作器,用于执行生产任务。这个 Airflow 操作符根据作业的类型和工作负载为每个作业创建两种类型的集群之一:
- 标准群集—包含 3–512 个节点(自动横向扩展和纵向扩展)的 Spark 群集,我们将这种类型的群集用于 Spark 作业。
- 单节点集群—我们还运行非分布式训练算法,在这种情况下,常规的多节点集群不适用。
每个作业都有一个专用群集,该群集在作业完成后立即关闭。为了执行许多独立的任务,可以并行运行许多集群。
操作员知道使用正确的参数在正确类型的集群上执行每个作业,管理故障、重试等。
使用 Databricks REST API 创建集群很简单,这是一个创建单节点集群的请求示例:
{
"run_name": "my_run",
"new_cluster": {
"spark_version": "7.4.x-gpu-ml-scala2.12",
"aws_attributes": {
"instance_profile_arn": "my_instance_profile",
"availability": "SPOT_WITH_FALLBACK",
"zone_id": "my_zone",
"ebs_volume_count": 1,
"ebs_volume_size": 100},
"num_workers": 0,
"spark_conf": {
"spark.master": "local[*]",
"spark.databricks.cluster.profile": "singleNode"},
"node_type_id": "p3.2xlarge",
"spark_python_task": {
"python_file": "s3://my-bucket/my-file.py",
"parameters": []},
"libraries": [{"pypi": {"package": "dill==0.3.1.1"}}],
"max_retries": 1,
"timeout_seconds": 36000
}
你可以在文章[1]的结尾找到关于不同参数的更多信息。
积分福利
Managed Spark
Spark 已经安装并配置完毕,支持快速按需创建集群,轻松管理集群,并在任务完成后关闭集群。
data bricks Runtime data bricks 提供了几个可用的运行时配置,例如“Databricks Runtime ML”,它可以自动创建针对机器学习优化的集群。
该配置包括最流行的机器学习库,如 TensorFlow、PyTorch、Keras、XGBOOST、Scikit-Learn、Pandas 等等。
它加快了集群创建时间,我可以用特定的运行时配置“标记”我的作业,这样我就可以在推理管道中再次使用它。它承诺在训练管道和预测管道之间有相同版本的库,使它们相互兼容。
调试简单 使用“数据块-连接”[2]可以调试作业。它只需要安装在本地虚拟环境中,并使用 Databricks 帐户详细信息进行配置。
它支持在集群上远程调试作业,并且变得非常有用,特别是当我们想要调试大量不适合本地机器内存的数据时。
易于使用自己的 Python 包
可以在集群上安装 Python 包,并从我们的作业中访问它们。它使得使用内部包、公共对象和代码变得非常容易。
可以从几个来源安装软件包,并使用 UI 或 API。

库安装— Databricks 统一分析(来源:作者)
内置 AWS 和 Azure 集成 Databricks 和 AWS 以及 data bricks 和 Azure 之间存在集成。我们可以使用单个 API 在两个云中执行作业,而不是在我们这边构建这些集成。
笔记本和数据目录 Jupyter 笔记本已经上市,具备预配置的 spark 会话和开箱即用的可视化功能。
还可以轻松执行繁重的工作负载,并且可以将处理后的数据帧保存到数据目录中,让其他团队和同事使用他们的笔记本来访问它们。
结论
在这篇文章中,我简要描述了为什么我喜欢使用 Databricks 集群作为基础设施来运行 Spark 作业和训练模型。有太多的细节我没有描述,因为我想给出一个主题的概述,而不是太深入。
如果有什么具体的东西让你感兴趣,我会很感激你的评论,我会在我的下一篇帖子里写出来。
链接:
1。数据块工作 API: https://docs.databricks.com/dev-tools/api/latest/jobs.html
2.data bricks Connect:
https://docs . data bricks . com/dev-tools/data bricks-Connect . html
使用数据流提取、转换和加载自行车共享多伦多乘客数据到 BigQuery
关于构建 ETL 管道的说明,该管道用于将 Bike Share Toronto ridership 数据加载到 BigQuery 表中,以便它可以用作 Data Studio 创建数据可视化的源

安德烈·费塔多在 Unsplash 上拍摄的照片
在我之前的博文中,我使用 Google Data Studio 和 BigQuery 作为数据源来分析疫情是如何影响自行车共享出行的。在这篇博客文章中,我将一步一步地演示在尝试将 Bike Share Toronto ridership 数据从云存储中直接加载到 BigQuery 时遇到的一些问题,以及如何通过使用 Dataflow 使用 Apache Beam 执行 ETL 过程来解决这些问题。
下载数据
- 让我们首先在云壳中打开一个会话,并将 2020 年自行车共享多伦多乘客数据下载到一个单独的文件夹
'2020'。
wget [https://ckan0.cf.opendata.inter.prod-toronto.ca/dataset/7e876c24-177c-4605-9cef-e50dd74c617f/resource/5f5d78c4-d810-4048-9dac-c18273abffac/download/files-1.zip](https://ckan0.cf.opendata.inter.prod-toronto.ca/dataset/7e876c24-177c-4605-9cef-e50dd74c617f/resource/5f5d78c4-d810-4048-9dac-c18273abffac/download/files-1.zip) -O temp.zipunzip temp.zip -d 2020rm temp.zip
上面的第一个命令下载名为temp.zip的 zip 文件,第二个命令将其解压到名为2020的文件夹中,第三个命令删除下载的 zip 文件。
2.让我们通过运行下面的命令来看看文件解压缩后的内容。
ls 2020
我们看到,2020 年的乘客数据分为 12 个 CSV 文件,都以2020开头:
2020–01.csv 2020–02.csv 2020–03.csv 2020–04.csv 2020–05.csv 2020–06.csv 2020–07.csv 2020–08.csv 2020–09.csv 2020–10.csv 2020–11.csv 2020–12.csv
3.我们可以通过读取 CSV 的标题来读取列名:
head -n 1 2020/2020–01.csv
这将输出以下列名:
Trip Id,Trip Duration,Start Station Id,Start Time,Start Station Name,End Station Id,End Time,End Station Name,Bike Id,User Type
将文件复制到云存储桶
接下来,让我们将文件复制到云存储桶中。这将允许我们利用对云存储 URI 的通配符支持,用一个命令将多个文件批量加载到一个 BigQuery 表中。
4.首先,用您的项目 ID 设置一个项目变量,并设置项目属性。
export PROJECT=my-project-idgcloud config set project $my-project-id
5.使用 make bucket gsutil mb命令在项目中创建一个新的 bucket。
gsutil mb -l northamerica-northeast1 gs://my-bucket-name
6.使用gsutil cp命令将文件复制到我们刚刚创建的云存储桶中。
gsutil cp 2020/* gs://my-bucket-name
7.检查文件是否已成功复制。
gsutil ls gs://my-bucket-name/
8.一旦数据被成功复制到云存储桶,就从云外壳中删除该文件夹。
rm -r 2020
创建大查询数据集
9.在将数据装载到 BigQuery 表之前,我们需要为该表创建一个 BigQuery 数据集。
bq --location=northamerica-northeast1 mk mydataset
尝试直接从云存储中将数据加载到 BigQuery 时出错
10.接下来,让我们尝试使用bq load命令将数据加载到一个 BigQuery 表中,并使用一个通配符,在基础上附加一个星号(*)
bq load --autodetect --source_format=CSV mydataset.biketrips2020 gs://my-bucket-name/*
然而,我们得到一个错误:
- gs://my-bucket-name/2020-10.csv: Error while reading
data, error message: CSV table references column position 9, but
line starting at position:3401930 contains only 9 columns.
该错误消息表明加载作业失败,因为至少有一行的列数少于自动检测到的架构规定的列数。
11.为了找到这些错误的来源,我们可以检查错误字节位置附近的 CSV 文件。为此,我们使用 gsutil cat命令。
gsutil cat -r 3401700–3402200 gs://my-bucket-name/2020–10.csv
由此,我们发现有许多行中的Trip_Id和Trip_Duration的值被错误地连接在一起。例如,下面的第二行应该以10000084,625,7120,…开始
10000083,720,7239,10/03/2020 13:28,Bloor St W / Manning Ave — SMART,7160,10/03/2020 13:40,King St W / Tecumseth St,5563,Annual Member10000084625,7120,10/03/2020 13:28,Gerrard St E / River St,7120,10/03/2020 13:38,Gerrard St E / River St,5250,Annual Member10000085,1526,7239,10/03/2020 13:28,Bloor St W / Manning Ave — SMART,7544,10/03/2020 13:53,Foster Pl / Elizabeth St — SMART,3956,Annual Member
我们需要找到所有这样的串联值,并将它们拆分。这是在 BigQuery 中无法完成的数据转换。因此,我们需要构建一个 Apache Beam 管道来转换数据并加载到 BigQuery 中。
此外,我们看到上述行中的Start_Time和End_Time列不符合 BigQuery 兼容的 [datetime](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#examples) 格式。我们还需要用数据流进行转换。
12.处理错误的一个选择是在bq load中设置max_bad_records标志。这将忽略坏行,并且不会将它们加载到表中。例如,我们可以将max_bad_record标志设置为 100:
bq load \
--autodetect \
--max_bad_records=100 \
--source_format=CSV \
mydataset.biketrips2020 \
gs://my-bucket-name/*
然而,这仍然会给我们带来错误,尽管是不同类型的错误。
Could not parse ‘NULL’ as INT64 for field Bike_Id (position
8) starting at location 5061057 with message ‘Unable to parse’
出现此错误是因为对于 BigQuery,在 CSV 中表示空值的标准方式是使用空字段。我们可以通过在bq load命令中设置标志null_marker=NULL来处理这个错误,从而指定这个特定的文件使用字符串 NULL 来标记 NULL。例如:
bq load \
--autodetect \
--max_bad_records=100 \
--source_format=CSV \
--null_marker=NULL \
mydataset.biketrips2020 \
gs://my-bucket-name/*
这处理了由于解析空值引起的错误。然而,我们还将在 Apache Beam 管道中包含这种转换,以便单个管道执行所有必要的转换。
建立数据流管道
13.接下来,我们编写一个 Apache Beam 管道,它提取文件,执行转换,并将数据加载到 BigQuery 中。Python 文件[etl_pipeline.py](https://github.com/bilalmkhan/etl-pipeline-beam) https://github.com/bilalmkhan/etl-pipeline-beam/blob/main/etl_pipeline.py包含管道的 Python 代码。三个函数执行主要的转换:deconcat()、replace_nulls()、format_datetime_bq()。我们可以使用云壳编辑器上传 Python 文件。
设置 Python 环境
14.在云 Shell 中运行以下命令,设置虚拟环境来运行我们的代码:
sudo pip install virtualenv virtualenv -p python3 venv source venv/bin/activate pip install apache-beam[gcp]==2.24.0
运行管道
15.运行 Python 文件[etl_pipeline](https://github.com/bilalmkhan/etl-pipeline-beam/blob/main/etl_pipeline.py).py会创建一个运行DataflowRunner的数据流作业。我们需要指定一个云存储桶位置,用于在管道仍在运行时暂存和存储临时数据,以及包含 CSV 文件的云存储桶。
python etl_pipeline.py \
--project=$PROJECT \
--region=northamerica-northeast1 \
--runner=DataflowRunner \
--staging_location=gs://my-bucket-name/test \
--temp_location gs://my-bucket-name/test \
--input gs://my-bucket-name/*.csv
--save_main_session
16.当数据流作业完成时,我们可以在 Gooogle 云控制台中导航到云数据流,并查看作业图表和其他作业完成详细信息:

17.导航到 BigQuery 验证数据是否成功加载到表中。这些数据现在可以用作 Cloud Data Studio 创建可视化的数据源。

感谢您的阅读!如果您对此工作流程有任何建议,请在评论中分享。
利用深度学习对抗新冠肺炎
人工智能如何以 98%的准确率可视化和诊断新冠肺炎

如果你收听了去年的新闻,新冠肺炎患者死亡的主要原因与无法通过充满肺部的液体呼吸有关。这缩小了我们肺部的总容量,使呼吸更加困难。这种液体可以使用 CT 或计算机断层扫描来观察。当急诊室的病人出现呼吸问题时,CT 扫描通常是医生建议进一步治疗的第一个预后工具。如果可以通过 CT 扫描正确诊断 Covid,这可以加快对处于危急或危及生命状况的患者的诊断,或者在测试用品有限时提供一种诊断方法。
为了预测新冠肺炎,之前的工作和在 建立的数据集被用于新型冠状病毒识别 (Soares et al. 2020) 的真实患者 ct 扫描的大型数据集。这包含来自 250 名 COVID 阴性患者和 247 名 COVID 患者的健康肺的约 1300 次 CT 扫描,以及 Covid 感染肺的约 1300 次 CT 扫描。这个数据集然后被应用于一个名为 ResNet-18 的卷积神经网络架构,训练一个新的模型来将健康患者与 Covid 感染患者进行分类。ResNet 的工作原理是利用剩余学习,或者替代直接前馈神经网络的概念,允许跳过连接,这允许激活跳过网络中的一层,从而简化网络并帮助解决消失梯度问题。如果您对 ResNet 架构是如何构建的感兴趣,这篇面向数据科学的文章将详细解释这一点。
该网络的总体 F-1 分数为 97.87%,不仅超过了原始数据集的研究,而且与最准确的 Covid 测试一样好,如果不是更好的话。全部结果如下表所示。

ResNet-18 结果
为了建立在这些结果的基础上,并提供新冠肺炎看起来像什么的一般化模型,在相同样本上开发了第二无监督自动编码器模型。
自动编码器是一种神经网络,用于学习一组数据的表示(编码)。一旦学习了这种表示,就可以将其重建为可视图像(解码)。这使得我们可以忽略图像中的噪声,而专注于所有图像之间的相似性。我们的特定网络获取大小为 244x244x1 的图像,学习大小为 500x1 的编码,并将其解码回 244x244x1 的图像(最后的 x1 表示灰度与彩色图像)。完整的架构可以在下图中看到。

新冠肺炎自动编码器
这种自动编码器的结果非常有说服力——下面是 4 次 CT 扫描及其相应输出的示例。

自动编码器生成的图像
很明显,自动编码器确定图像的重要部分是肺本身,选择使该区域变暗并忽略肺内部的大部分成分。还可以看出,covid 感染的肺只有健康肺总肺活量的 50%左右。
这项研究背后的希望是,这两种算法可以帮助需要立即诊断的医生,并在持续抗击新冠肺炎的前线提供援助。
如果你想看完整的研究,GitHub 库可以在这里找到!这也包括对研究结果的更多验证!
你喜欢这篇文章吗?我希望这有助于展示机器学习如何用于医学预测。如果你喜欢它,请跟随并查看我的其他文章,如这篇我运行了一些 GPU 性能测试!
没有我的队友:安德鲁·马蒙、德万什·辛格、帕拉维·米斯拉和里什·古尔纳尼,这篇文章是不可能完成的
用微分方程模拟浮油的扩散
变更数据
将数学技术应用于环境问题

我们将调查一个假设的海面浮油扩散的场景。不时地,但不定期地,一架直升机被派遣去拍摄浮油。每次飞行,直升机都会到达浮油上空。飞行员拍了一张照片,等了 10 分钟,又拍了一张照片,然后回家。在七次航行的每一次中,浮油的大小(平方英里的面积)都是从两张照片中测量的。
我们将使用微分方程来模拟浮油的扩散,预测不同时间浮油的平方英里数,绘制预测函数相对于时间的曲线,并确定记录下表中观察值的时间。需要注意的是,我们将完成一系列具体的任务,这些任务来源于本文末尾的参考资料。

第一部分:为浮油的大小建立一个模型
首先,我们可以在上表中添加一列,描述 10 分钟时间段内浮油面积的变化率,也等于δA(t)/δt(其中 A ( t )表示时间 t 时的浮油面积)。计算方法如下:

因此,我们得到了下表,并附加了列δA(t)/δt。

现在,我们可以用浮油面积的变化率来画出最初的观察值。

根据 Microsoft Excel 计算(如上图所示),最佳拟合的直线为y=–0.001x+0.0102,即δA(t)/δt=–0.001x+0.0102。这也可以表示为一阶线性微分方程模型:

将该微分方程的两边除以–0.001x+0.0102,然后相对于 t 进行积分以分离出 A ( t )得出:

假设第一次观测发生在时间 t = 0,我们可以代入初始条件( t ,a(t)=(0,1.047),求解 c₆.简化后,我们得到 c₆ = 0。因此:

(b)部分:预测 t = 10 分钟、t = 20 分钟和 t = 120 分钟时浮油的未来大小。
我们必须将我们给定的t-值 10、20 和 120 代入之前找到的函数 A ( t )。

第(c)部分:绘制你的模型,将浮油的大小作为时间的函数。
下面是基于功能 A ( t )的模型。

(d)部分:找出浮油面积为 8 平方英里的时间。
我们必须设置 A ( t )等于 8,并求解 t 。

(e)部分:确定第一次、第三次、第五次和第七次初次观察的时间。
我们可以使用与第(d)部分所示相同的过程来找到第一次、第三次、第五次和第七次初始观察的时间。
第一次初步观察

第三次初步意见

第五次初步意见

第七次初次意见

结论
通过这个项目,我们探索了如何通过首先绘制油膜面积变化率的初始观测值,计算精确的趋势线(R 值非常高,为 0.9967!),并利用数学生成浮油面积的预测方程。当用我们的函数 A ( t )和我们的初始数据执行“现实检查”时,我们看到这个函数是合乎逻辑的,因为它对于浮油开始快速增长然后随着时间的增加快速减慢到小得多的增长率是有意义的。我们还能够计算出进行某些观察的时间,以及估计浮油未来的大小。
我们可以用来模拟浮油扩散的另一种方法是根据 10 分钟后的观察值绘制初始观察值,而不是根据面积变化率。当执行时,这是通向微分方程的另一条途径。虽然我们可以预期这些结果与我们目前的方法提供的结果略有不同,但它们应该非常接近。
这种方法的一个局限性是,它假设浮油面积的变化只遵循一个微分方程。其次,它没有考虑任何可能影响浮油扩散程度的环境因素。这就很难扩展到其他场景。
总的来说,我们通过将一阶微分方程的技巧应用到现实生活场景中,练习了对浮油区域的分析和建模。在根据给定数据推导精确模型的过程中,我们使用了积分、数学建模和求解给定初始值的微分方程。
参考
[1]贾德森,T. W. (2020 年 8 月 1 日)。常微分方程项目。斯蒂芬 f 奥斯汀州立大学。http://faculty . SF ASU . edu/judson tw/ode/html-2020 08 01/ode project . html
[2]温克尔,b .(未注明)。浮油扩散。用微分方程模拟调查和机会的系统倡议。https://www . simode . org/resources/2038/download/1-5-S-oil click-student version . pdf
用蒸馏来保护你的神经网络
蒸馏是如何被用来保证神经网络安全的
蒸馏是一个热门的研究领域。对于蒸馏,你首先训练一个深度学习模型、、、、来解决你的任务。然后,你培养一个学生网,可以是任何模式。
教师接受真实数据的培训,学生接受教师输出的培训。它预测老师的输出:无论是标签,标签的概率分布,还是其他。

查尔斯·德鲁维奥在 Unsplash 上拍摄的照片
注:有蒸馏的变体。一种是自学,老师和学生的网络是一样的!

Bermix 工作室在 Unsplash 上拍摄的照片
蒸馏的好处
蒸馏有不同的好处。例如,您可以选择一个比教师网络小的学生网络。你将能够达到比从头开始训练学生模型更高的精确度。同时,你会有一个比老师更小、更简单、更快的模型。
蒸馏还提供了正则化,使你的网络更加一般化。在这篇文章中,我们将讨论蒸馏的一个主要用途:保护你的神经网络免受攻击!
为什么有效
就像这篇关于反思正则化的文章一样,蒸馏提供了更平坦的局部极小值。因此,输入数据的微小变化不太可能改变预测值。为什么这很重要?攻击者可以创建对抗性示例。这些例子包括导致错误预测的真实输入的小变化(例如,改变几个像素)。
这是我的上一篇关于 COVID mask 预测的文章中提到蒸馏的部分原因。如果没有提取,通过修改几个像素来欺骗掩模预测模型是相当容易的。
一个特别危险的例子是:自动驾驶汽车。想象一下,如果一名黑客在停车标志上贴了一张贴纸。贴纸可能看起来像最初的停车标志,但 2%的像素变化会让自动驾驶汽车错过停车标志。这些变化被称为对抗性扰动。有了蒸馏,黑客将需要使用更定制的攻击或改变更多的像素,这可能是显而易见的。
前进

照片由威利安·贾斯登·德·瓦斯康塞洛斯在 Unsplash 拍摄
你往往不需要万无一失的安全性,只需要比同类目标更好的安全性。如果我们能找到一种方法使每个神经网络高度安全,那将是理想的,但目前看来这是遥不可及的。
安全是一场猫捉老鼠的游戏,攻击往往比防御发展得更快。蒸馏是一种基本的防御手段,可以用来 让你的网络对攻击 更加稳健。你可以使用像 KD_Lib 这样的库来尝试提取你的网络。
当攻击变种进化到回避防御时,蒸馏变种也进化到对这些攻击免疫。蒸馏会让攻击变得更难,但并非不可能。这是您安全工具箱中的一步。
使用分布式计算进行神经成像
是的,终于到了使用 PySpark 进行医学成像的时候了

学分:Pexels
如今,由于分辨率、磁场强度、联合体和基础设施的提高,神经影像数据也成为大数据。事实上,每天用几种医学成像模式采集大量的 3D 和 4D 图像。几个数据集与数千个文件一起公开可用,仅举几个例子:
- 人类连接体项目(HCP)
- 1000 功能连接体项目 (1000FCP)
- 老年痴呆症神经影像倡议
这些数据集包括功能、结构、扩散、人口统计和其他信息,这些信息每天都在增加,这就产生了用分布式和并行系统加速处理的需求。在影像模态中,功能磁共振成像(fMRI)是一种通过检测血流的相关变化来测量大脑活动的技术。在被称为血液动力学反应的过程中,活跃的神经元比不活跃的神经元接收更多的氧气,这可以通过核磁共振成像仪检测到。从数据的角度来看,这被转换成 4D 体积(对于脑扫描是 3D 的,然后在时间上跟随血液演变)。因此,如果我们分析几个时间点,每个文件的大小以陡峭的斜率线性增长。此外,信号极易受噪声影响,因此必须应用多种噪声消除算法。类似的情况也发生在扩散数据上,这里我们用不同的梯度来代替时间点。因此,文件也将是 4D 的,但不是时间点,每个体积将代表不同的梯度角度。

fMRI 文件的简单表示(鸣谢 Sarraf & Ostadhashem
存储、预处理和分析这种类型的数据在计算上是昂贵且耗时的,因此应该利用具有既定方法的并行计算的优势。功能性和扩散 MRI 的解决方案之一是使用 Spark / PySpark。这些工具允许读取和加载成像数据,将它们转换为可并行操作的弹性分布式数据集,并将它们转换回成像格式,如 NIFTI 。据报道,与传统方法相比,使用(Py)Spark 的计算时间可以减少到四分之一(参见例如 Makkie 等人 2019 )。在本文中,我总结了一些越来越复杂的实验/测试。从简单的独立成分分析开始,到功能连接,再到训练大脑时间序列的深度卷积网络。
Spark 大数据技术和工具,如 Hadoo p,是面向可靠、可扩展、分布式计算的开源软件编程平台和项目。特别是 Spark 正变得广泛使用,这是由于使用数千个节点的计算时间的改进,以及 Spark (MLlib)和 GraphX(GraphX)的机器学习库的扩展。特别是对于神经成像,典型的管道将使用 Nibable 包(http://nipy.org/nibabel)来访问数据(DICOM 或 Nifti),然后将数据转换为弹性分布式数据集(RDD)格式,这是 Spark 架构的基本结构。实际上,rdd 是分区的记录集合。奇特之处在于它们是容错的,可以并行操作。Spark 独立调度程序由三个主要进程组成:主进程、工作进程和驱动程序。应用程序被提交给驱动程序,然后主程序协调进程,将它们分发到集群上的工作节点。典型的工作流程(未显示细分主/工作)如下所示:

fMRI 火花管道示例(鸣谢 Sarraf & Ostadhashem
Saman Sarraf 和 Mehdi Ostadhashem 向展示了一项任务,包括加载、通过 MELODIC 工具进行独立组件检测,比较了使用和不使用 PySpark 在 Python 中执行的相同过程,显示了以下计算成本:

同一任务的处理时间(学分萨拉夫&奥斯塔哈舍姆
尽管很成功,但这只是在本地机器上的一个简单实验。Boubela 等人在 2015 年描述了一个更现实的场景,他们甚至在亚马逊云上使用了下面的架构。在这种情况下,任务是通过使用时间序列的皮尔逊相关性来构建功能性连接体。
架构可以总结如下

使用建议分析方法的数据流。2015 年布贝拉等人的功劳
特别是,在前面提到的研究中,作者在具有 192 GB RAM、两个英特尔至强 X5690 处理器和四个 Nvidia Tesla GPUs 的服务器 Ubuntu Linux(可选 GPU 计算)上从 fMRI 数据计算功能连接性;Spark 集群由 10 台 48 GB 内存的 Linux 机器和 2 个英特尔至强 X5550 处理器组成。
为了在云上实现这一点,亚马逊提供了一个名为 EMR 的解决方案,已经由 Yarn、Hadoop 和 Spark 进行了设置。常规设置是一个带有 S3 数据桶的集群。下面的视频展示了向这样的基础设施发送数据是多么容易。
关于实现,作者设计了Scala(Spark 的本地语言)中的专用文件阅读器,在多个节点上并行读取 4D NIfTI 文件,并将结果收集到 Spark 环境中的 RDD 中。读者已经友好的通过 github 分发。
voxelwise 时间序列数据存储在一个 RowMatrix 对象的列中,该对象是用于与 Apache Spark 机器学习库 MLlib 接口以处理大数据的最常见对象。运行独立成分分析或主成分分析的代码将非常类似于众所周知的 Python 脚本,如 Sci-kit :
**from** **pyspark.mllib.linalg** **import** Vectors
**from** **pyspark.mllib.linalg.distributed** **import** RowMatrix# Toy exmple data
rows = sc.parallelize([
Vectors.sparse(5, {1: 1.0, 3: 7.0}),
Vectors.dense(2.0, 0.0, 3.0, 4.0, 5.0),
Vectors.dense(4.0, 0.0, 0.0, 6.0, 7.0)
])
mat = RowMatrix(rows)
*# Compute the top 4 principal components.* pc = mat.computePrincipalComponents(4)
*# Project the rows to the linear space spanned by the top 4 principal components.* projected = mat.multiply(pc)
此外,连接组学研究特别适合 Spark,因为 GraphX 库定义了两个 rdd,一个包含顶点,另一个包含边,以便在图上进行分布式计算。
其他值得一提的研究是显微镜 Spark 框架 Thunder ,以及 Milad Makkie 关于用于 fMRI 大数据分析的快速和可扩展的分布式深度卷积自动编码器的工作。

演职员表 Makkie 等人 2019
特别是 Makkie 和他的同事表明,即使在 Spark 上运行来自 TensorFlow 的 autoencoder,也有可能在 Spark 上实现 fMRI 分析框架(如下图所示)。需要重点承担的是,这个版本的 TensorFlow 不是传统的,需要专门下载。
# for tensorflow>=2.0.0
pip install tensorflowonspark# for tensorflow<2.0.0
pip install tensorflowonspark==1.4.4
总之,基于 Spark 的分布式计算已经被广泛应用于多个任务。如今,需要分析来自 fMRI、扩散或显微镜的大数据集,利用分布式结构作为 Spark 联合到 GPU 上的并行计算可以是一种解决方案。我在这里只提到了几个例子,显示了计算时间和其他性能的提高。
如果科学家无法访问 Spark 集群(例如从大学),一个可能的解决方案是在亚马逊云上运行 EMR 结构,使用 S3 存储桶来存储数据(如上面的视频所示)。
在神经成像的特殊情况下,加强再现性也很重要,并且还应该记住另外两件事(尽管它们不是特定于分布式系统的,也应该用于独立的应用中):

fMRIprep 工作流程(图片来自 J.Gorgolewski )
如果您对 neuroimage 的分布式和并行解决方案有进一步的建议,欢迎在此发表评论。
参考文献
随意连接:


将 Docker 用于深度学习项目
作为一名机器学习工程师,我每天都使用 docker 容器,这帮助我节省了大量时间,并保持有序。

保罗·泰森在 Unsplash 上的照片
在这篇文章中,我将解释如何在我的日常项目中使用 Docker。关于这个主题有很多很好的文档和视频,但我想分享一种我在从事的几个行业项目中一直使用的方法。
Docker 是什么?
Docker 使用操作系统级虚拟化来交付称为容器的软件包中的软件。每个停靠容器都是从停靠图像中创建的。一个映像拥有构建环境的所有信息(库、文件夹、文件、操作系统等)。容器是相互隔离的。

Docker 流-按作者分类的图像
官方 Docker 文档可以在此链接找到。
要下载 Docker,您可以访问这个链接,在本文的其余部分,我将假设 Docker 已经正确安装在机器上。
深度学习项目为什么要用 Docker?
当你在机器上运行深度学习培训时(在云上或本地),你需要能够轻松地运行培训,而不必每次都为设置环境而挣扎。此外,如果出于某种原因,您想在另一台机器上运行此培训,您不希望再次经历所有设置。
理想情况下,我们希望有一个命令可以放心地跨机器运行。这就是为什么我几乎一直使用 Docker 容器进行培训。
以下是 Docker 对我的主要优势:
- 所有必需的包都已经安装了训练的正确版本:例如,如果我想使用 Tensorflow2 进行一次训练,我为此配置了一个 docker 映像,另一个用于 Tensorflow1 ,另一个用于 Pytorch 。
- 如果在单个 GPU 上运行,我可以对所需的 GPU 进行分区。
- 由于所有容器都是独立的,因此如果执行崩溃,其他进程不会受到影响——因此其他 GPU 不会受到影响,例如当我选择特定的 GPU 用于训练时。
- 如果使用 git 存储库作为源代码,我通常会将它添加到主 docker 映像中,然后在每次创建 docker 容器时进行 git pull,并切换到我想要使用的分支/提交。
- 本地文件夹/文件和 NAS 可以在启动容器时轻松挂载——因此不需要复制,节省了时间,尤其是在我调试某些东西的时候。
Docker 简介
以下是您需要了解的一些基本命令:
- 显示当前运行的容器:
docker ps
- 显示所有容器(甚至那些不再运行的容器):
docker ps -a
- 显示本地保存的图像:
docker images
- 移除 docker 容器:
docker stop container_name # if container is running
docker rm container_name
- 移除所有 docker 容器(不再运行):
docker container prune
- 移除图像:
docker rmi image_name
- 删除所有 docker 图像(非常小心这一个!):
docker image prune -a
在 Docker 容器中运行 Tensorflow2 培训
Docker 图像
首先,我们提取包含 Tensorflow 版本 2.1.04 和 Python3 的 NVIDIA 映像(这将需要一些时间):
docker pull nvcr.io/nvidia/tensorflow:21.04-tf2-py3

Docker pull done 按作者分类的图像
完成后,我们使用命令docker images检查图像是否在本地图像列表中:

列出 Docker 本地图像—按作者分类的图像
我们确实看到了带有标签21.04-tf2-py3的图像nvcr.io/nvidia/tensorflow。
用这个图像启动一个容器,并浏览一些标志
我们可以使用此图像创建一个容器:
docker run -it --rm --name tensorflow2-container --network=host nvcr.io/nvidia/tensorflow:21.04-tf2-py3 bash

Tensorflow2 图像中的 Docker 容器已打开-作者提供的图像
我们在这个命令中使用了一些特定的标志:
-it用于打开一个交互终端--rm是用来当我们退出集装箱时,它会把它移走--name用于用自定义名称给容器命名--network=host用于访问容器中的互联网(与主机相同的网络)- 然后,我们有了要使用的图像的名称,后跟
bash以在容器中创建一个交互式 shell
如果我们在另一个终端中运行docker ps,我们将看到我们的新容器:

docker ps 命令来查看新启动的容器——按作者排序的图像
我们确实看到了我们的集装箱tensorflow2-container ✅
现在,如果我们想将我们的本地工作区用于训练脚本,我们可以通过使用-v /Users/margauxmforstyhe/workspace/:/workspace将我的工作区文件夹挂载到容器中。此参数将我们计算机上的工作区文件夹挂载到容器中的基本工作区文件夹。
在我们当前的容器中,如果我们运行ls,我们会看到:

Docker 容器中的默认工作空间文件夹-按作者排序的图像
让我们退出当前使用命令exit打开的 docker 容器,并使用工作区文件夹创建一个新的容器:
docker run -it --rm --name tensorflow2-container --network=host -v /Users/margauxmforstyhe/workspace/:/workspace nvcr.io/nvidia/tensorflow:21.04-tf2-py3 bash
并运行ls:

Docker 容器中的本地工作区文件夹-按作者排序的图像
➡️本地工作区安装在 Docker 容器中,我们现在可以使用它进行培训/测试。
当我做一些训练测试时,我使用一台带有 GPU 的机器,并为 GPU0 选择一个带有--gpu=device=0的 GPU。然后,当我完成测试时,我通常运行如下命令来开始训练:
docker run -i -d --rm --gpus=device=0 --name tensorflow2-container --network=host -v /Users/margauxmforstyhe/workspace/:/workspace nvcr.io/nvidia/tensorflow:21.04-tf2-py3 bash -c "export PYTHONPATH=/workspace && python3 /workspace/training_repo/train.py .... {parameters for the training}"
这里我们有一个处于分离模式的 docker 容器 ( -d意味着我们看不到终端中代码的执行)在 0 号 GPU 上运行本地训练脚本。
注意:这和推理脚本完全一样,你只需要改变被调用的 python 脚本。
另一种方法是拥有一个包含所有训练脚本的 git 存储库,并将其作为映像的一部分添加进来。让我们用一个 Dockerfile 来做这件事吧!
使用 Docker 文件构建 Docker 映像,并使用 git 存储库作为训练存储库
Dockerfile 文件用于创建图像。例如,我们想要在我们之前使用的映像nvcr.io/nvidia/tensorflow:21.04-tf2-py3之上创建一个映像,然后我们想要从 Github 克隆training _ repo并安装运行培训的所有需求(例如install rasterio,或者安装一个特定包的版本),这为我们提供了这个 docker 文件:
FROM nvcr.io/nvidia/tensorflow:21.04-tf2-py3RUN apt-get update
RUN git clone [https://github.com/MargauxMasson/training_repo.git](https://github.com/MargauxMasson/training_repo.git)
RUN pip install -r /workspace/training_repo/requirements.txt
RUN lsWORKDIR /workspace/CMD "ls"
为了构建这个图像——我们将把它命名为tensorflow-21.04-tf2-py3-with-requirements-and-git-repo——我们使用命令docker build(需要在 Dockerfile 所在的文件夹中运行):
docker build . --network=host -t tensorflow-21.04-tf2-py3-with-requirements-and-git-repo

使用 Dockefile 构建图像—按作者分类的图像
我们看到图像的构建工作正常,当我们用docker images检查时,我们确实看到了新的图像tensorflow-21.04-tf2-py3-with-requirements-and-git-repo。
注意:docker 构建命令中的“.”表示名为Dockerfile的 docker 文件位于我们运行命令的文件夹中。
现在,当我们使用这个映像启动容器时,我们不需要挂载本地工作区,因为 git repo 已经在映像中了:
docker run -it --rm --name tensorflow2-container --network=host tensorflow-21.04-tf2-py3-with-requirements-and-git-repo bash

实际上, training_repo 就在容器的工作区中。
即使 git 存储库中的代码发生了变化,也可以不加修改地使用这个映像。当启动容器时,我们可以git pull或git checkout到任何想要的分支/提交:
docker run -i -d --rm --gpus=device=0 --name tensorflow2-container --network=host tensorflow-21.04-tf2-py3-with-requirements-and-git-repo bash -c "cd /workspace/training_repo && git pull && git checkout my_training_dev_branch && export PYTHONPATH=/workspace && python3 /workspace/training_repo/train.py .... {parameters for the training}"
同样,正如启动容器时所建议的,我们可以添加这些标志:--shm-size=1g --ulimit memlock=-1 --ulimit stack=67108864
所以:
docker run -i -d --rm --gpus=device=0 --name tensorflow2-container --network=host --shm-size=1g --ulimit memlock=-1 --ulimit stack=67108864 tensorflow-21.04-tf2-py3-with-requirements-and-git-repo bash -c "cd /workspace/training_repo && git pull && git checkout my_training_dev_branch && export PYTHONPATH=/workspace && python3 /workspace/training_repo/train.py .... {parameters for the training}"
有很多方法可以使用 Docker,但这是我喜欢使用它进行训练和推理的方式,它帮助我保持有组织性,因为我有一些特定的图像(或至少 Docker 文件),我可以自信地使用它们,并知道我的训练代码将毫无困难地运行。

GIF 来自https://media.giphy.com/media/g0gtihsbzj5pSWgcml/giphy.gif
使用 EDA 生成业务理解
数据科学
对客户细分和外部因素的洞察
在几个以神经网络为特色的背靠背项目(你可以在这里探索这里和这里)并决定从头开始构建一个神经网络之后,我想开始一个以 EDA 为重点的项目,以保持我的熊猫技能敏锐并回到数据科学的根源。从数据中获得深刻的、可操作的理解和建议。

数据
首都自行车共享数据集分布很广。可以在 UCI 或数据世界上找到。它包含 2011 年和 2012 年的约 17,000 条意见。虽然数据相当干净,但有点棘手,因为数据的粒度,或者一次观察代表的是一天中的一个小时。这些功能包括天气数据 —温度、风阻和天气类型,时间数据 —日期、星期几、小时和假日,以及乘客数量 —分为临时乘客和注册乘客。
如果我们绘制两年内每小时的总用户数,我们可以快速了解全年的乘客量变化。
如果适用的话,我喜欢通过查看我的主要特性和目标特性的分布来开始我的 EDA 项目。在这一点上,我没有建模任何东西,但是查看这个分布可以快速了解高级行为。

一段时间内的用户总数。图片作者。
客户细分…
…是一个强大的工具。它可以阐明客户群之间的主要差异,以及这些不同的偏好如何影响业务决策并推动客户体验的改善。
潜伏在这个数据集中的是一个客户细分力量的完美例子,以及当你没有意识到它的发生或者你愿意忽略它时会发生什么!
按一周中的每一天来看每小时的总用户数,这几天之间似乎没有显著差异!

一周中每一天每小时的用户总数。图片作者。
所以我们可能会得出结论,一周中的某一天并不会真正影响乘客数量,对吗?好吧,让我们试着把总用户分解成临时用户和注册用户。

一周中每一天每小时的登记乘客和临时乘客。图片作者。
这里我们看到一个非常不同的故事!这两个群体有相反的行为。注册用户通常是在工作日骑行的商务通勤者,而临时用户是为了休闲或锻炼,因此在周末骑行更多。凑巧的是,将它们聚合在一起几乎完美地隐藏了这种区别。
这就是 EDA 可以为企业提供有价值的细节的地方。现在,我们可以开始区别对待这些细分市场,以满足它们不同的定位和需求。
也许我们想为通勤者提供折扣,或者为临时用户启动一个“打卡”计划,让他们每乘坐 X 次车就能获得一次免费租赁。或者尝试用打折的升级包把临时用户转化为注册用户。或者走另一条路!只在周末给注册用户一个免费的“伙伴通行证”,这样他们可以带一个朋友,增加临时乘车人数(并可能获得一个新客户)。
我们可以通过查看每小时登记的乘客数量来确认登记的乘客主要是通勤者。上午 8 点和下午 5 点至 6 点的高峰时段有明显的高峰。

一天中每个小时登记的乘客人数。图片作者。
我们看到休闲骑手的模式非常不同(记住,他们在周末骑得更多)。每小时的客流量集中在下午,毕竟那是最舒适的乘车时间!请注意,y 轴的比例相对于注册乘客量要小得多。

一天中不同时段的临时乘客。注意与上面相比的 y 轴刻度。图片作者。
为了巩固我们的理解,我们可以看看这两组人在假期是如何骑车的。如果我们知道注册用户大多是商务通勤者,我们可能会希望他们在假期少骑点车,因为他们不用上班。另一方面,也许临时用户利用这一天去公园骑自行车!

假日的登记乘客和临时乘客。图片作者。
这似乎有一些证据,尽管数据中似乎也有相当多的噪音。如果是假日,注册用户确实骑得少,临时用户骑得差不多——大多数假日是在工作周,所以因为临时用户在工作周骑得不多,我们真的不希望看到他们的骑行量有很大差异。
天气
现在让我们看看一些影响乘客的外部因素,主要是环境因素,如天气和温度。气温对整体乘客量有很大影响。甚至全年客流量的差异也常常归因于温度。冬天的客流量并不是因为冬天而减少,而是因为外面是 0 度!
数据集有 4 类天气:
- 晴朗,少云,部分多云
- 薄雾和多云,薄雾和破碎的云,薄雾
- 小雪、小雨和雷雨、小雨和零散的云
- 大雨、冰粒、雷暴和雾、雪和雾
这是一个相当武断的评级系统,但它会完成工作。

按天气类型划分的注册乘客和临时乘客。图片作者。
在这两个客户群中,天气恶劣程度和乘客量之间存在负相关关系。随着天气恶劣程度的增加,乘客数量会减少。(或者你可以说这是一种积极的关系,随着天气的改善,乘客数量会增加)
但是在注册乘客中有一个有趣的现象。暴风雨条件(最坏的天气)的 95%置信区间包括其他天气类型!这里发生了什么事?
嗯,没有进一步的调查,很难说出细节,但我们可以假设。例如,我假设对于许多骑车人来说,自行车租赁是他们唯一的通勤选择,因此无论天气如何都必须骑车上下班。他们的行为非常缺乏弹性,或者很少有其他选择(也许他们没有汽车,火车/地铁也不在他们的社区服务)。
另一个假设是,这些通勤者在早上条件正常时骑车去上班,然后一场突如其来的暴风雨(或错过的天气预报)让他们骑车去上班。他们别无选择,只能骑自行车下班回家,否则他们可能会努力避免这种情况。
结论
探索性数据分析是数据科学过程中的一个重要步骤。你需要成为数据专家,才能理解你正在看的东西!即使是看起来简单明了的数据,比如我们的原始数据,乘客数量看起来稳定,也可能隐藏着细微的差别,这将改变你对你的商业案例的看法。
深入了解您的客户群将使您能够针对特定人群制定战略,并避免“一刀切”的客户获取和保留心态。毕竟,我们已经看到注册用户和临时用户有非常不同的需求,但在对公司的影响方面却是互补的。你不想把重点放在注册用户上,以至于疏远临时用户。
最后,一些因素——比如众所周知的天气——将永远在你的控制之外。再多的营销或复杂的建模都无法阻止一月对大多数乘客来说太冷,也无法阻止他们乘坐预报中有雷雨的火车。但了解这些外在因素如何影响你的客户并将其纳入你自己的业务预测中仍然很重要。
你可能只需要一些创造性的思维——也许是一个动态定价模型,当天气/温度变得不舒服时降低价格,以鼓励继续骑行。无论你的商业案例是什么,都要带着不断学习的心态去对待它,记住要像使用左脑一样多的使用右脑。
连接
我一直在寻找连接和探索其他项目!这个项目的回购,所有的数字和代码都可以在我的 GitHub 上找到。
使用实体嵌入提高机器学习模型的性能
在 ML 模型中实现由神经网络学习的嵌入的教程

米卡·鲍梅斯特在 Unsplash 上的照片
本文的目的是提供关于如何在 ML 模型中实现由神经网络学习的嵌入的信息。因此,我们不会详细讨论嵌入理论。
注意:假设你了解深度学习和机器学习的基础知识
什么是实体嵌入,为什么使用实体嵌入?
不严格地说,实体嵌入是分类变量以连续方式的向量表示。在神经网络的上下文中,嵌入将每个实例的特征从其原始空间转换到低维向量空间表示,同时保留来自其特征的信息,并且还在嵌入空间中有意义地表示每个类别。因此,使用嵌入可以让模型了解每个变量之间的相互关系,从而提高性能。
实现
使用由神经网络学习的嵌入来训练 ML 模型的一般步骤是:
- 用嵌入层训练神经网络。
- 从训练好的神经网络中提取嵌入。
- 用来自训练好的神经网络的分类变量的嵌入替换分类变量。
- 使用嵌入来训练你的 ML 模型。
在本教程中,我们将使用 sklearn、fastai、PyTorch 和著名的泰坦尼克号数据集进行演示。您可以使用自己选择的框架来复制这一点。在本教程之前,已经完成了数据清理和特征工程。
#required libraries
from sklearn.metrics import classification_report
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
from sklearn import preprocessing
from sklearn.ensemble import RandomForestClassifier
from fastai.tabular.all import *
在使用 FastAI 将数据输入神经网络之前,对数据进行预处理
dls = TabularPandas(df_train, y_names="Survived", y_block=CategoryBlock,
cat_names = ['Cabin', 'Title', 'Sex'],
cont_names = ['Age', 'Pclass', 'Fam_size'],
procs = [Categorify, FillMissing, Normalize], splits = RandomSplitter(valid_pct=0.2)(range_of(df_train)))
to_nn = dls.dataloaders()
创建一个 TabularLearner,用 lr_find()找到一个合适的学习速率。
learn = tabular_learner(to_nn, metrics=accuracy)learn.lr_find()
训练神经网络。
learn.fit_one_cycle(8, 2e-2)
从训练的神经网络中提取嵌入,并用来自训练的神经网络的分类变量的嵌入来替换分类变量。
#function to embed features ,obtained from fastai forums
def embed_features(learner, xs):
xs = xs.copy()
for i, feature in enumerate(learner.dls.cat_names):
emb = learner.model.embeds[i]
new_feat = pd.DataFrame(emb(tensor(xs[feature], dtype=torch.int64)), index=xs.index, columns=[f'{feature}_{j}' for j in range(emb.embedding_dim)])
xs.drop(columns=feature, inplace=True)
xs = xs.join(new_feat)
return xsemb_xs = embed_features(learn, to_nn.train.xs)
emb_valid_xs = embed_features(learn, to_nn.valid.xs)
使用嵌入来训练你的 ML 模型。
rf = RandomForestClassifier(n_estimators=400, min_samples_leaf=10,
max_features=1/2, max_samples = 50)
rf = rf.fit(emb_xs,to_nn.train.y)
valid_preds = rf.predict(emb_valid_xs)
print(classification_report( to_nn.valid.y,valid_preds))

图片来自作者。
与在原始数据集上训练的随机森林相比,在嵌入数据上训练的随机森林上,我们获得了 3%的准确性提高,这是一个相当大的提高!
结论
使用实体嵌入不仅可以帮助我们更好地理解和可视化我们的数据,而且还可以提高 ML 模型的性能。嵌入是处理分类变量的有用工具,也是对传统编码方法(如一键编码)的升级。
为数据项目使用环境:初学者指南

照片由克里斯里德在 Unsplash 上拍摄
通过一些简单的步骤改善您和您的团队的编码体验
介绍
当我开始用 Python 编程时,对我来说比较困惑的问题之一是如何恰当地管理我安装在计算机上的包。我通常的工作流程是,当我需要一个新的包时,我将它安装在默认的 Python 系统中,而不知道我在做什么。因此,由于包之间的不兼容问题,我的 Python 开始出现错误。此外,我的代码在我同事的机器上无法运行,因为他们没有我使用的相同版本的软件包。几个月后,我发现了虚拟环境这个神秘的概念。当我发现这个工具以及如何使用它们时,它完全改善了我的编码体验。
虚拟环境基本上是一个隔离的设置,您可以在其中指定与依赖项及其版本相关的所有功能,以开发特定的项目。简而言之,就是在你的电脑上安装一个新版本的软件(比如 Python,Rstats,Javascript ),它不会与默认版本或其他环境共享任何东西。在这种情况下,虚拟环境允许您:
- 根据您正在进行的每个项目的需求,拥有几个版本的 Python(或 R)。例如,由于 Python 开发人员在发布新版本时会增加和减少特性,这将有助于避免版本错误和不兼容性。
- 您可以精确地指定每个项目需要哪些包以及哪些版本。当您定义需求时,您的合作者也可以复制您的环境,避免由于不同机器上的不同规范而导致的不兼容性。
为 Python 创建虚拟环境有两种定义明确且有据可查的方法:[virtualenv](https://docs.python.org/3/library/venv.html)和。一方面,我们有[virtualenv](https://docs.python.org/3/library/venv.html),一个允许我们创建和控制我们的环境的环境管理器。安装软件包最简单的方法是通过。对于 Stata 用户来说,这相当于 scc。另一方面,我们有conda,他既是环境经理也是包经理。
在这篇文章中,我将教你如何用这两种工具创建环境,并利用这一神奇的工具!
目录
- 使用
venv.创建和管理环境 - 使用
conda创建环境。 - 我的个人工作流程。
- 结束语
使用venv创建和管理环境。
# 1\. Update pip package manager:
# Mac/Linux:
$ (base) python -m pip install --user --upgrade pip
# Windows:
$ (base) python -m pip install --upgrade pip # 2\. Install virtualenv.
# Mac/Linux:
$ (base) python -m pip install --user virtualenv
# Windows:
$ (base) python -m pip install --user virtualenv# 3\. Using your terminal, go to the folder of the project where you are working:
$ (base) cd path/to/your/cool/project# 4\. Now, you can create a virtual environment using $ (base) python -m venv your-env-name # 5\. Activate the environment:
# Mac/Linux:
$ (base) source your-env-name/bin/activate
# Windows:
$ (base) your-env-name\Scripts\activate.ps1
恭喜你。!你只是创造了一个环境。如果您使用的是 Anaconda,您的终端可能看起来像这样:
$ (base)(your-env-name)
现在,我们可以开始在虚拟环境中安装软件包了。为了便于说明,我们将使用一个最关键的包来执行科学计算:[NumPy](https://numpy.org)。
# Check the installed packages
$ (base)(your-env-name) pip freeze# Install the latest version of numpy
$ (base)(your-env-name) pip install numpy# Install a specific version:
$ (base)(your-env-name) pip install numpy==1.17.2 # Install a version greater than or equal to a specific one:
$ (base)(your-env-name) pip install numpy>=1.17.2 # Upgrade a package to a newer version:
$ (base)(your-env-name) pip install --upgrade numpy
使用pip安装包有很多变化和命令。以下是文档的链接。
为一个项目安装多个包是很常见的。除了numpy,让我们想象你需要处理数据帧(pandas)和图形(NetworkX)。您可以指定一个requirements.txt文件来管理您需要的所有包。
# Location of this file: path/to/your/cool/project/requirements.txt networkx>=2.4
pandas>=1.1.0
numpy>=1.17.2
使用以下命令安装requirements.txt中的所有软件包:
$ (base)(your-env-name) pip install -r requirements.txt
最后,要停用或删除环境:
# Deactivate the environment
$ (base)(your-env-name) deactivate# Delete the environment
# Mac/Linux:
$ (base) rm -rf your-env-name
# Windows:
$ (base) Remove-Item -LiteralPath "your-env-name" -Force -Recurse
使用conda创建环境。
我们已经知道如何使用venv和pip来管理环境和包。另一种广泛使用的方式是使用conda。正如我们前面所讨论的,conda既是一个环境又是一个包管理系统,所以您可以使用conda创建环境和安装包。根据你的操作系统,点击这里安装conda。
# 1\. Check if conda was installed correctly
# This command will show all the installed packages in base ...
$ (base) conda list # ... and this will show all the environments
$ (base) conda env list # 2\. Create a new environment with an specific python version
$ (base) conda create -n your-env-name python=3.8 # You can create the environment with some packages
$ (base) conda create -n your-env-name python=3.8 networkx pandas>=1.1.0 # Activate (deactivate) the environment
$ (base) conda activate your-env-name
$ (my-env) conda deactivate
默认情况下,您的所有环境都位于您的conda目录中。例如,在我的机器中,环境保存在/Applications/anaconda3/envs/your-env-name中。这种方法与venv后面的方法不同,因为后者在项目的同一个文件夹中创建环境。
# Create and env in an specific directory
$ (base) cd path/to/your/project
$ (base) conda create -p ./your-env-name # or alternatively
$ (base) conda create -p path/to/your/project/your-env-name # To activate this environment
$ (base) conda activate -p path/to/your/project/your-env-name
作为一个包管理器,conda默认安装了来自 Anaconda 仓库的包。你也可以从第三方软件仓库安装软件包( Conda-Forge 是最流行的一个),也可以从pip安装。这就是它的工作原理!
# IMPORTANT! # Remember to activate the environment before installing packages $ (base) conda activate -n your-env-name # -f path/to/your/project/your-env-name # Install a package from the Anaconda repo
$ (your-env-name) conda install numpy # Install a package from conda forge
$ (your-env-name) conda install -c conda-forge numpy # ... and add the channel to the configuration
$ (your-env-name) conda config --append channels conda-forge
# you can also define which channel to prioritize
$ (your-env-name) conda config --set channel_priority strict # Try to avoid pip if you are using conda!
$ (your-env-name) pip install numpy # Install a requirements.txt file
$ (your-env-name) conda install --file requirements.txt
我发现用conda管理环境的一个惊人之处是,您可以在单个.yml文件中指定配置的每个方面。例如,让我们假设您有一个environment.yml配置:
name: your-env-name
channels:
- conda-forge
- defaults
dependencies:
- python=3.7
- pytest
- ipykernel
- ipython
- pip:
- autopep8==1.5.4
- dropbox==10.4.1
- paramiko==2.7.2
- redis==3.5.3
- twilio==6.41.0
通过该文件,您可以使用以下方式创建环境:
# To create the env
$ (base) conda env create -n your-env-name -f /path/to/environment.yml # To update the env
$ (base) conda env update -n conda-env -f /path/to/environment.yml
同样,如果你想在.yml中保存一个环境的规格,你可以这样做!
$ (base) conda activate your-env-name
$ (your-env-name) conda env export > your-env-name.yml
更多详细信息,请阅读文档!
我的个人工作流程。

来源:照片由 Kelly Sikkema 在 Unsplash 上拍摄
在我使用的任何环境中,有几个包是我一直想要的。这里是规范:)!
name: null
channels:
- conda-forge
- defaults
dependencies:
- python>=3.7 # Specify your desire version
- ipython # To develop and run my code
- pytest # To perform testing to the code
- autopep8 # Code formatter for the PEP8 guidelines
- mypy # Static typing
- flake8 # For linting (spell and grammar checker of python code) prefix: path/to/env/conda-env
使用venv,该工作流程如下:
$ (base) cd path/to/your/project
$ (base) python -m venv venv
$ (base) source venv/bin/activate
$ (venv) pip install ipython pytest autopep8 mypy flake8 --upgrade pip
$ (venv) pip install -r requirements.txt # This is where I specify all the packages I'm gonna use!
结束语
使用虚拟环境将有助于您避免许多因不兼容问题导致的未知错误给自己和同事带来的麻烦。如果你喜欢这个帖子或者你有一些意见,请在 Twitter 上联系我!开始为您的项目使用环境吧!
参考资料和进一步阅读
最初发布于https://Ignacio riveros1 . github . io。
使用 fastai 回调进行高效的模型训练

图像快门库
利用提前停止和模型保存回调的力量
当你训练一个深度学习模型时,你想从你用来训练模型的资源中获得最大的收益。如果你使用像 Paperspace Gradient 这样的按小时付费的环境,时间就是金钱。如果你能在更短的时间内训练出你的模型,你就能省钱。即使你正在使用 Colab,并且计价器没有运行,你自己的时间仍然是宝贵的,所以知道如何最大限度地利用你可用的时间和能力来训练你的深度学习模型是值得的。在本文中,我将描述两个回调,你可以在 fastai 中使用它们来确保你的模型训练尽可能高效。我在本文中描述的例子在我的 Packt bookDeep Learning with fastai Cookbook中有更详细的解释。
训练深度学习模型的两个问题
fastai 与 Keras 有一个共同的特点,Keras 是另一个常用的深度学习高级框架。在这两个框架中,模型训练过程不是开箱即用的。默认情况下,模型训练过程存在以下问题:
- 训练过程将按照您在 fit 语句中指定的次数继续进行,即使您想要优化的指标不再改进。
- 您在训练过程结束时获得的模型具有来自最后一个时期的权重,即使存在其权重会导致模型具有更好性能的更早时期。
幸运的是,fastai 和 Keras 都以回调的形式包含了这两个问题的解决方案。在本文的剩余部分,我将解释 fastai 中的这些回调。关于如何使用回调来控制 Keras 中的模型训练过程的类似描述,请参见我的曼宁著作的第 6 章“结构化数据深度学习”。
基线:在没有回调的情况下训练模型
为了查看 fastai 回调的影响,我们将从训练一个没有回调的模型开始。该模型在 fastai 策划的数据集 ADULT_SAMPLE 上进行训练,该数据集包含个人的详细信息,如受教育年限、婚姻状况和职业类别。

成人 _ 样本数据集
在成人样本数据集上训练的模型的目标是预测给定个人的工资是高于还是低于 50 k。
我们首先训练没有回调的模型:
请注意对 set_seed()的调用。我们这样做是为了在不同的训练运行之间的每个时期获得一致的结果。这使我们能够在训练运行之间进行比较,并强调回调的影响。如果我们不调用 set_seed(),我们将在运行之间得到不一致的结果。例如,历元 2 上的精度对于每次训练运行将是不同的。
以下是应用于不带回调的学习者对象的 fit 语句的输出:

没有回调的基线的 fit 语句的输出
精确度增加到时段 2,然后在时段 3 下降,并在剩余时段振荡直到时段 9。当我们对学习者对象运行 validate()时:
learn.validate()
输出显示,在训练运行结束时,训练模型的精度是历元 9:

不带回调的模型的 validate()输出
由于没有回调,我们遇到了本文开头提到的两个问题:在模型停止改进之后,训练运行继续进行,并且从训练运行中出来的经过训练的模型的性能比在训练运行期间看到的最佳性能差。
添加提前停止回调,以便在模型性能停止提高时停止训练运行
现在我们已经建立了一个没有回调的基线,让我们添加一个早期停止回调,以便在模型性能停止提高时停止训练运行。
我们将使用用于训练基线模型的相同的 dataloaders 对象,但是这一次我们将在 fit 语句中指定一个早期停止回调:
fit 语句的输出现在显示,尽管指定了 10 个时期,但是训练运行只进行到时期 5。耐心参数被设置为 3,因此在每个高精度标记之后,训练过程得到 3 个时期的改进。如果在高水位标记后的 3 个时期内没有改善,训练过程将自动停止。

带有提前停止回调的 fit 语句的输出
在时段 2 中精度的高水位标记之后,精度没有变得更好,因此训练运行在 3 个时段之后,在时段 5 之后停止。
当我们在学习者对象上运行 validate()时,输出显示训练过程产生的模型的精度再次是训练运行的最终时期的精度,即使更早的时期具有更高的精度:

通过提前停止回调训练的模型的 validate()输出
保存训练过程中的最佳砝码组
添加早期停止回调是对基线的改进,因为在准确性不再提高后,我们没有运行那么多无效的时期。然而,在最终的训练模型中,我们仍然没有达到最佳精度,因此仍然有改进的空间。为了获得最佳的准确性,我们将添加一个模型保存回调,以确保经过训练的模型在训练过程中具有最佳的准确性。
我们将再次使用用于训练基线模型的相同的 dataloaders 对象,但是这次我们将在 fit 语句中指定两个回调。我们还会将学习者对象的路径设置为可写目录,以便在培训过程中保存模型:
就像上一节中的模型一样,fit 语句的输出显示,即使指定了 10 个时期,训练运行也只进行到时期 5。

带有提前停止回调和模型保存回调的 fit 语句的输出
再次,由于提前停止回调,在精度在时段 2 中的高水位标记上停止提高之后,训练过程自动停止 3 个时段。
训练结束时,训练好的模型的精度如何?对该模型的学习者对象运行 validate()表明,精度不是来自训练运行的最终时期的精度。这一次,精确度与我们在纪元 2 的高水位标记中看到的一致。

通过提前停止回调和模型保存回调训练的模型的 validate()输出
通过回调、提前停止和模型保存,我们解决了本文开头提到的两个问题:
- 多亏了早期停止回调,我们避免了做一堆无效的时期,因为精度不再提高。
- 由于模型保存回调,从训练过程中得到的训练模型具有我们在训练过程中看到的最佳准确性。
结论
通过使用 fastai 中的提前停止和模型保存回调,您可以从培训周期中获得最大收益。您将避免在模型没有改进的时期耗尽容量,并且您可以确保在训练周期结束时,您拥有具有最佳性能的模型。
以下是与本文相关的一些资源:
- 本文检查的代码:https://github . com/packt publishing/Deep-Learning-with-fastai-Cookbook/blob/main/ch8/training _ with _ tabular _ datasets _ callbacks . ipynb
- 预定地点:https://www . packtpub . com/product/deep-learning-with-fastai-cookbook/9781800208100
- 关于这个主题的视频:【https://youtu.be/qkRok0e3yvs
使用 FastAPI 重新创建烧瓶教程
实践教程
不仅仅是另一篇“Flask 与 FastAPI”文章——学习 Python web 框架的有用练习

Flask 和 FastAPI 是两种流行的 Python web 框架。很容易在网上找到这两个框架的大量比较——如果你正在阅读这篇文章,你可能已经读过你的“Flask vs. FastAPI”文章。
这里有一个非常的快速总结。Flask 出现的时间更长,并且面向小型网络应用。作为较老的框架,它往往有更大的用户群和更多的教程和回答的问题。FastAPI 较新,适合创建 REST APIs。它越来越受欢迎,尤其是对于机器学习用例。但是一般来说,这两个框架非常相似——您可以用 Flask 做的任何事情都可以用 FastAPI 来完成,反之亦然。
Flask 拥有的一个东西是一个伟大的初学者教程,用于构建一个简单的应用程序,用户可以在其中注册、登录和创建帖子。FastAPI 有很好的构建 API 的文档,但是缺少像 Flask 示例这样的基础应用的简单教程。我正在学习这两个框架,所以我决定使用 FastAPI 重新创建 Flask tutorial 应用程序是一个很好的练习。我想其他初学者可能会从我学到的东西中受益。
如何充分利用这篇文章
- 查看 GitHub 上的 源代码。我不是在这里写一个完整的教程。我将指出 Flask 和 FastAPI 应用程序之间的一些关键差异,但您必须去 GitHub 查看完整的源代码以及所有内容是如何组合在一起的。
- 阅读 烧瓶教程 。它解释了我们正在构建的应用程序,此外,教程中还有许多与框架无关的有用信息。如果你已经熟悉 web 框架,你可以浏览一下教程,但是如果你是新手,完成它可能是有益的(最多只需要几个小时)。
- 自己动手!从头开始编写应用程序,从 Flask 示例开始并将其转换为 FastAPI,或者克隆我的 GitHub 库对其进行修补。最好的学习方法是实践!
教程应用程序
在 Flask 教程中,我们构建了一个名为 Flaskr 的应用程序。我使用 FastAPI 构建了一个等效的应用程序,并将其命名为 Fastr。这两款应用都允许用户使用用户名和密码注册,登录,并以博客格式发表文章。这些应用程序运行在存储用户和帖子数据的 SQLite 数据库之上。

使用 Flask(左)和 FastAPI(右)创建的同一个 web 应用程序。
Flask 和 FastAPI 应用程序之间的区别
除了字体和颜色的表面变化,这些应用程序在幕后的实现方式也有一些关键的不同。我将在这里重点介绍其中一些,大致按照您在 Flask 教程中遇到它们的顺序。在这篇文章中我不会讲太多技术,所以请务必参考 GitHub 上的源代码以了解更多细节。
这不是一个完整的列表,但这里有一些 Flask tutorial 应用程序和我在 FastAPI 中的实现之间的关键差异:
- 无应用工厂。Flask 教程推荐在
__init__.py文件中实现一个应用工厂。我通常不喜欢在__init__.py文件中放太多代码,所以我在main.py文件中创建了 FastAPI app 对象,这是 FastAPI 文档中的典型模式。 - 用紫玉米跑步。在 Flask 中,我们设置一些环境变量,然后调用
flask run。FastAPI 推荐使用uvicon,所以我们用uvicorn fastr.main:app运行我们的应用。 - 同一事物的不同名称。代码的某些部分几乎完全相同,只是命名规则略有不同。例如,Flask 中的 Blueprint 相当于 FastAPI 中的 APIRouter,用法几乎相同。
- 填写缺失的部分。 Flask 内置了许多功能,其中一些功能是一个基本的 FastAPI 应用程序所没有的。我们需要手动挂载静态文件并指定我们的模板引擎(这两个都是在 Flask 中自动完成的)。我们也没有像在 Flask 中那样内置“会话”对象——我们需要使用 SessionMiddleware 对象来实现该功能。一旦我们添加了这些部分,它们在框架之间的行为几乎是相同的。
- Pydantic 模型和类型检查。 FastAPI 与 Python 类型提示和 Pydantic 数据模型紧密集成,允许大量自动数据验证。例如,在我们的应用程序中,我们不必像在 Flask 中那样显式验证“用户名”字段是否已填充。
- 更强大的数据库实施。这并不是严格意义上的必要更改,但是我使用 SQLAlchemy 实现了后端数据库,而不是直接使用 sqlite3。这是在 FastAPI 文档中推荐的模式,它有一些好处,比如允许我们以后容易地改变到不同的数据库后端(例如 PostgreSQL)。这种变化需要一些代码重组 Fastr 应用程序在
db/目录下有所有与数据库相关的代码。 - 重写测试。这是比较棘手的部分之一,因为 Flaskr 单元测试的设置是非常特定于 Flask 框架的。首先,我们需要在
conftest.py文件中更改设置测试数据库和应用程序的夹具。之后,我们对 Flask 和 FastAPI 应用程序进行了大量相同的测试,但是在一些测试中,语法和预期的响应是不同的。许多变化都是在 FastAPI 应用程序中使用 SQLAlchemy 的结果。如果您运行 Fastr 应用程序的单元测试,您会发现它们通过了全面覆盖。
结论
如果你比较一下 Flask 教程应用和 my FastAPI 实现的源代码,你会发现它们的相似之处多于不同之处。FastAPI 应用程序中有更多的样板代码来复制 Flask 内置的一些功能。但是你也获得了 FastAPI 的一些好处,比如来自类型提示的数据验证和自动生成的交互式文档。
对于这类 app,我会推荐 Flask 或者 FastAPI 吗?我不知道。对于如此简单的事情,这并不重要。一旦我们开始添加更多的功能,我们可能会从一个框架中受益更多。例如,如果我们想增加对异步请求的支持,FastAPI 本身就包含了这种能力。或者,如果我们有一个具体而复杂的特性想要快速实现,我们可能会更幸运地在大量的 Flask 社区示例中找到一个起点。最终,任何一个人都可以完成工作,并且很容易将你的知识从一个人转移到另一个人。
成为媒体会员访问成千上万作家的故事!
在 Python 中使用 For 循环:计算概率
为什么循环对于构建完整的统计模型是不可或缺的

循环是学习如何用 Python 编码的一个非常重要的部分,尤其是在实现大量数字的计算时。
对于统计学家和数据科学家来说,最常见的诱惑是跳过编码中更平凡的方面——我们假设软件工程师可以简单地以适当的方式重新格式化代码。
然而,在许多情况下,编写代码的人需要理解模型背后的统计数据以及如何通过循环迭代模型输出——这两个过程根本不能独立开发。
这里有一个例子,说明在 Python 中对循环使用可以极大地增强统计分析。
累积二项式概率的背景
在进行概率分析时,考虑事件发生几率的两个变量是 N (观察次数)和λ(λ——我们的命中率/在单个间隔内发生的几率)。当我们谈论累积二项式概率分布时,我们的意思是说试验次数越多,事件发生的总体概率越高。
probability = 1 — ((1 — λ)^N)
例如,在公平骰子上掷出数字 6 的几率是 1/6。然而,假设同一骰子滚动 10 次:
1 — ((1–0.1667)^10) = 0.8385
我们看到掷出数字 6 的概率现在增加到 83.85%。
基于大数定律,试验次数越大;事件发生的概率越大,即使单次试验中的概率非常低。因此,让我们生成一个累积二项式概率,来演示在试验次数增加的情况下概率是如何增加的。
无循环模型
这是一个不使用循环计算累积二项式概率的脚本。
import numpy as np
import pandas as pdl = 0.02
m = 0.04
n = 0.06p=np.arange(0, 100, 1)h = 1 - l
j = 1 - m
k = 1 - nq = 1-(h**p)
r = 1-(j**p)
s = 1-(k**p)
- l 、 m 、 n 代表三个个体概率。
- p 代表试验次数(最多 100 次)
- q 、 r 和 s 代表累积二项式概率,即试验次数每增加一个单位,概率增加
以下是生成的输出示例:
>>> qarray([0., 0.02, 0.0396, 0.058808, 0.07763184, 0.0960792, 0.11415762, 0.13187447, 0.14923698, 0.16625224, ..., 0.8532841, 0.85621842, 0.85909405, 0.86191217, 0.86467392])>>> rarray([0., 0.04, 0.0784, 0.115264, 0.15065344, 0.1846273, 0.21724221, 0.24855252, 0.27861042, 0.307466, ..., 0.97930968, 0.9801373, 0.9809318, 0.98169453, 0.98242675])>>> sarray([0., 0.06, 0.1164, 0.169416, 0.21925104, 0.26609598, 0.31013022, 0.35152241, 0.39043106, 0.4270052, 0.46138489, 0.49370179, 0.52407969, 0.5526349, 0.57947681, ..., 0.99720008, 0.99736807, 0.99752599, 0.99767443, 0.99781396])
我们看到,对于概率 q 、 r 和 s ,对于给定数量的试验,累积概率以不同的速率增加。
也就是说,在不使用循环的情况下开发这个模型有一个关键的缺点,即个体概率只能采用最终用户指定的值。如果我们希望从 0.01 连续迭代到 0.99 呢?
循环模型:列表理解和 2D 数组
这一次,将通过使用一个单独的概率变量来构建模型,该变量迭代值 0.01 到 0.99 ,并且将使用 100 次试验来计算累积二项式概率。
import numpy as np
import pandas as pd # List comprehension
probability=[x*0.01 for x in range(1,100)]
probability=np.array(probability)
probabilityh = 1 - probability
h# Construct 2D array
result = 1-h[:, np.newaxis] ** np.arange(1,100)
result
生成的概率变量的输出如下:
>>> probabilityarray([0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, ... 0.96, 0.97, 0.98, 0.99])
注意,对于概率变量,有必要使用列表理解。这是因为 Python 的 range() 函数只能处理整数,不能处理浮点值。更多信息见下面的堆栈溢出指南。
当查看代码的最后两行时,您会注意到构建了一个 2D 数组来计算累积二项式概率。当最初尝试计算这些值而不是使用 2D 数组时,数组已被计算出来,但值并没有按预期的顺序排列。
>>> for i in range(1,100,1):
>>> print(1-(h**i))[0.01 0.02]
[0.0199 0.0396]
...
[0.62653572 0.86191217]
[0.63027036 0.86467392]
相反,我们希望数组的顺序为 [0.01,0.0199,…,0.62653572,0.63027036] 和 [0.02,0.0396,…,0.86191217,0.86467392] 。
正如下面的 Reddit 线程中所解释的,转置上面的不会有任何用处,因为 h 是一维数组。
另一种方法是计算 2D 数组,然后直接打印出来:
>>> result = 1-h[:, np.newaxis] ** np.arange(1,100)
>>> resultarray([[0.01, 0.0199, 0.029701, ..., 0.62653572, 0.63027036], [0.02, 0.0396, 0.058808, ..., 0.86191217, 0.86467392], [0.03, 0.0591, 0.087327, ..., 0.94946061, 0.9509768], ..., [0.97, 0.9991, 0.999973, ..., 1., 1., 1.], [0.98, 0.9996, 0.999992, ..., 1., 1., 1.], [0.99, 0.9999, 0.999999, ..., 1., 1.,1.]])
从上面可以看出,计算了从 0.01 到 0.99 的累积二项式概率。
以这种方式对循环使用允许我们自动从 0.01 迭代到 0.99——试图手动这样做太麻烦且容易出错。
结论
在本例中,您看到了如何:
- 用 Python 计算累积二项式概率
- 使用 for 循环遍历大范围的值
- 使用列表理解处理一系列浮点值
- 当无法转置 1D 数组中包含的值时,设计 2D 数组
非常感谢您的宝贵时间,非常感谢您的任何问题或反馈。
免责声明:本文是在“原样”的基础上编写的,没有任何担保。它旨在提供数据科学概念的概述,不应被解释为专业建议。本文中的发现和解释是作者的发现和解释,不被本文中提到的任何第三方认可或隶属于任何第三方。
在 Python 中使用函数装饰器
了解如何通过使用函数装饰器来扩展函数的功能

在这篇文章中,我将谈谈 Python 中的 函数装饰器 ,一个不容易把握的话题,却是 Python 编程中极其有用的设计模式。
在 Python 中,函数装饰器实际上是函数包装器。
函数装饰器通过包装函数来扩展函数的功能,而不修改其最初的预期行为。
像往常一样,让我们从基础开始,慢慢地理解什么是函数装饰器。
在函数中定义函数
在 Python 中,函数体可以包含一个或多个函数:
def do_something():
def internal_function():
return "In internal_function() function"
return internal_function
在上面的代码片段中,在do_something()中我有另一个名为internal_function的函数。当do_something()被调用时,它会将internal_function返回给调用者。让我们验证一下:
f = do_something()
type(f) # *function*
在上面的语句中,当internal_function返回给调用者时,我将它赋给了一个名为f的变量。因为可以将函数赋给变量,所以现在可以使用变量调用函数,如下所示:
f() # 'In internal_function() function'
将函数传递给函数(也称为包装函数)
让我们修改do_something(),使它现在有一个名为f的参数。该参数现在将接受一个可以在internal_function()中调用的函数参数:
def do_something(**f**):
def internal_function(): **return f()** return internal_function
假设现在我有了另一个名为function1的函数:
def function1():
return 'function1'
我现在可以将这个函数传递给do_something():
f = do_something(**function1**)
f现在将包含对internal_function()的引用,该引用返回执行function1的结果(传递到do_something())。作为函数调用f和直接调用function1()是一样的:
f() # 'function1'
# same as:
function1() # 'function1'
本质上,我们在这里做的是用另一个函数包装
*function1*,在本例中是*do_something*。
函数装饰器
在 Python 中,do_something()被称为函数装饰器。而不是将function1传入do_something()(就像我们在上一节所做的那样):
f = do_something(**function1**) # think of this as wrapping function1
# with do_something
Python 允许您给函数装饰器加上前缀“ @”符号,并将其放在您想要换行的函数之前,如下所示:
**@do_something**
def function1():
return 'function1'
他被称为函数装饰者。
您现在可以正常调用function1():
function1() # 'function1'
带参数的函数修饰符
在前面的例子中,您的函数装饰器(do_something())包装了一个不接受任何参数的函数(function1()不接受任何参数)。如果您现在想将它包装在一个接受参数的函数中,比如下面的函数,该怎么办呢?
def add(n,m):
return n + m
在这种情况下,需要给internal_function()添加参数:
def do_something(f):
def internal_function(**n,m**):
return f(**n,m**) return internal_function
您现在可以像这样使用函数装饰器:
@do_something
def add(n,m):
return n+madd(**4,5**) # 9
如果你不使用@符号,你的代码将如下所示:
f = do_something(add)
f(4,5) # 9
这个函数装饰器现在可以应用于任何接受两个参数的函数。但是,如果您想将它应用于接受不同数量参数的函数,该怎么办呢?在这种情况下,您可以使用 *args 和 **kwargs 类型定义一个通用函数装饰器。
如果你不熟悉它们的工作方式,请参考我的文章“理解 Python 中的args 和 * kwargs”(https://towards data science . com/Understanding-args-and-kwargs-in-Python-321937 f 49 C5 b)。
我们修改后的do_something()函数装饰器现在看起来像这样:
def do_something(f):
def internal_function(***args, **kwargs**):
return f(***args, **kwargs**) return internal_function
您现在可以将do_something()函数装饰器应用于具有不同参数的函数:
@do_something
def add(n,m):
return n+m**@do_something
def square(n):
return n**2**print(add(4,5)) # 9
**print(square(2)) # 4**
函数装饰符的使用
我知道你脑子里的下一个问题是:那么函数装饰器的用例是什么?上面的函数 decorators 似乎没有做任何有用的事情。
我给你举个例子。假设您想创建一个日志文件,在每次调用函数时记录一个条目,详细信息如下:
- 被调用函数的名称
- 函数接收的参数
- 调用了时间函数
如果没有函数装饰器,您需要将代码插入到您希望记录的所有函数中。但是这样做,你实际上是在修改函数的功能。您希望在不修改函数的情况下做到这一点。
你可以用一个函数装饰器轻松解决这个问题。
要创建一个函数装饰器,首先要创建一个函数,比如说,function_logger(),其中有一个内部函数叫做wrapper()(你可以使用任何你想要的名字):
from datetime import datetimedef function_logger(f):
def wrapper(*args, **kwargs):
# store the current date and time, and function details
# into a log file:
date_time = datetime.now().strftime("%y-%m-%d %H:%M:%S")
with open("log.txt", "a") as logfile:
logfile.write(f'{f.__name__}: {args}, {date_time}\n')
return f(*args, **kwargs) return wrapper
现在,您可以对任何函数应用函数装饰器:
**@function_logger**
def add(n,m):
return n+m**@function_logger**
def square(n):
return n**2print(add(4,5))
print(square(2))
调用这些函数将导致在名为 log.txt 的日志文件中添加一个条目:
add: (4, 5), 21-05-29 14:16:56
square: (2,), 21-05-29 14:16:56
如果您不再需要记录函数,只需删除函数装饰器!
带参数的函数修饰符
上一节中的例子显示了function_logger函数装饰器在每次调用被记录的函数时为日志文件创建一个条目。如果您希望根据函数的类型将条目记录到不同的文件中,该怎么办?嗯,很简单——你只需用另一个函数包装function_logger(),把文件名作为参数传入,然后返回function_logger函数,就像这样:
from datetime import datetime**def logger(filename):**
def function_logger(f):
def wrapper(*args, **kwargs):
date_time = datetime.now().strftime("%y-%m-%d %H:%M:%S")
with open(**filename**, "a") as logfile:
logfile.write(
f'{f.__name__}: {args}, {date_time}\n')
return f(*args, **kwargs)
return wrapper
**return function_logger**
现在,您可以指定要使用的日志文件:
**@logger('log1.txt')**
def add(n,m):
return n+m**@logger('log2.txt')**
def square(n):
return n**2print(add(4,5))
print(square(2))
如果不使用@符号,您的代码将如下所示:
f = logger(‘log1.txt’)(add)
f(4,5)
简单?我希望这篇文章能揭开 Python 中函数装饰器背后的奥秘。下次您遇到函数装饰器时,您不必再想了——只要把它们想象成函数包装器就行了。
基于模糊逻辑的道路交通拥挤指数估计
思想和理论
如何利用大量 GPS 数据为基于模糊的道路交通拥堵量化系统提供信息

在本文中,将对“基于速度概率分布的拥挤指数估计的模糊推理系统”一文进行概述。目标是以一种更“可读”的方式呈现论文的发现,并鼓励读者学习新的东西或在他们的研究中使用这些知识。
在讨论大城市面临的挑战时,拥堵是主要话题。因此,本文的主要目标是提出另一种道路交通拥堵量化方法,但这一次使用了一种称为速度转换矩阵(STM) [ 2 ]的新型道路交通建模技术。拥塞量化方法基于四个步骤:
- STM 计算。
- 质心(CoM)坐标的计算代表了从 STM 提取的交通状态。
- 使用 CoM 坐标作为模糊推理系统的输入导致范围[0,1]内的拥塞指数。
- 通过从《公路通行能力手册》(HCM) [ 3 ]中提取领域知识,使用遗传算法优化 FIS。
让我们从一些使用方法的背景开始。
速度转换矩阵
STMs 的概念最近才出现[ 2 ],它基于马尔可夫链理论,表示被观察系统的两个或多个状态之间的转移概率。STM 表示矩阵,该矩阵示出了在某个时间间隔内在两个连续路段之间过渡时改变车辆速度的概率。关于 STMs 的详细信息可以在以下文章中找到:
质心
使用由 STM 表示的交通模式的 CoM 坐标来估计拥塞指数。使用这种方法是因为物体在 STM 中的位置提供了道路交通分析环境中的基本信息。它揭示了受监控的十字路口或路段的交通状态。下图显示了使用 STMs 时 CoM 计算的重要性。如果将 CoM 放在左上角,我们可以观察到源链路和目的链路的速度都很低,表现为拥塞。如果将 CoM 放在矩阵的中间,可以观察到一些不稳定的操作。而如果将 CoM 放在右下角,我们可以观察到由于起点和目的地链路的速度非常大而导致的自由流动情况。使用这些陈述,我们可以同意当使用 STM 作为交通数据模型时,CoM 是交通状态估计的关键参数。

三个 STM 例子的质心:拥塞(左),不稳定流(中),自由流(右)(图片作者:作者)
模糊推理系统
基于知识的决策有时很难用离散值来完成,而一些非离散值可以更好地代表问题。例如,如果你需要根据屋外的温度来决定如何着装,你可能会使用一些非离散值,如“热”、“相对热”、“不太冷”或“冷”这些变量被称为语言变量,因为它们不代表离散值,而这些值是用单词量化的。
关于用 Python 实现模糊推理系统的有趣教程可以在[ 4 中找到。
在这里,模糊推理系统(FIS)派上了用场!FIS 可以用语言术语表示变量,用隶属函数表示具有特定术语的隶属度。让我们使用下面的图像来显示上述温度示例:

用语言值表示的简单模糊变量(图片来源:作者)
请注意本例中的第 1 点和第 2 点。在第一点,很明显温度是“热”的最强成员,而在第二点,它介于“不太冷”和“冷”之间。为了创建决策系统,我们需要基于一些先验知识定义规则来帮助我们,在这种情况下,决定穿什么。在基于 FIS 的系统中,我们使用简单的“如果-那么”规则,如下图所示。

基于模糊的决策规则集(图片来源:作者)
此示例显示了最简单的 FIS,只有一个输入变量(温度)和一个输出变量(衣服类型)。
对于拥塞量化 FIS,我们使用了两个输入变量(CoM 的 x 坐标和 CoM 的 y 坐标)和一个输出变量(拥塞指数),如下图所示。

基于经验的初始 FIS 变量(图片来源:作者)
输入和输出变量用语言表达式“小”、“中”和“大”来描述输入变量被限制使用值[0,20],因为 STM 维度是 20x20。输出变量表示拥挤指数,由于结果的解释更容易,拥挤指数被限制在区间[0,1]内。
该 FIS 的 If-then 规则按以下方式设置:
- x 和 y 坐标的“小”值代表“大”拥堵指数。
- x 和 y 坐标的“中等”值表示“中等”拥堵指数。
- x 和 y 坐标的“大”值代表“小”拥堵指数。
总共有九条规则涵盖了上述基本价值观以及它们的其他组合。详细内容可以在论文[ 1 ]中找到。
在这里,最初的 FIS 是代表。它是仅利用先前研究者的经验构建的。为了验证初始 FIS,必须使用一些领域知识进行调优。
用遗传算法优化 FIS
在这种情况下,优化涉及调整初始 FIS 隶属函数、规则或两者,以便更好地表示领域知识。
如果你使用的是 Matlab,关于 FIS 优化的细节可以在这里找到[ 5 ]。
专家系统的关键部分之一是领域知识表示。本文参考了 HCM 的领域知识表示方法。数据基于 HCM 服务水平值进行标注,该值使用相对于自由流速度(FFS)的速度值进行定义。FFS 可以定义为单车在空路段的速度,也可以定义为观察路段的速度限制。HCM 定义了从 A 到 F 的六个服务水平,其中 A 代表 FFS 的交通流量,车辆之间几乎没有相互作用,F 代表严重拥堵情况。创建 LoS 矩阵是为了直观地表示转换矩阵上的 LoS 值,如下图所示。

从 HCM 中提取的用于标注测试数据集的矩阵(图片来源:作者)
它用颜色表示 LoS,两个 LoS 值之间的过渡用相应的颜色混合表示。为了量化拥堵级别,以如下方式将 LoS 值合并为三类:(I)由标记为 A 和 B 的 LoS 表示的自由流动交通状况被标记为“低”,(ii)由标记为 C 和 D 的 LOS 表示的交通状况被标记为“中”,以及(iii)由 LOS 值 E 和 F 表示的拥堵交通状况被标记为“高”。然后,通过用提到的类别标签标记数据来创建两个数据集。
第一个是用于基于遗传算法的 FIS 优化的训练数据集,第二个是用于结果验证的测试数据集。适应度函数和关于优化过程的细节可以在原始论文[ 1 ]中找到。
优化结果可以在下图中观察到。优化后,它显示了初始 FIS 变量(纯黑色)和 FIS 变量(红色)。可以观察到,除了输出变量之外,初始 FIS 非常适合优化的那个。我们可以看到“大”这个词偏离了最初的 FIS,它应该改为一个阶跃函数。

基于领域知识的优化 FIS 变量(图片来源:作者)
结果
优化 FIS 的结果如下图所示。它代表输入变量和输出变量之间的关系。可以看出,当 CoM 的 x 和 y 坐标较小时,拥塞指数较高。在这种情况下,车辆在输入和输出观察到的道路交通路段上具有非常低的速度。当 CoM 的 x 和 y 坐标很大时,可以观察到相反的情况。在这里,我们有一个小的拥堵指数和接近自由流速度的速度。

优化 FIS 的结果;CoM 坐标与拥堵指数关系图(图片来源:作者)
问号代表 STM 的区域,可以用从非常低到非常高的速度和相反的速度的偏离跃迁来描述。这些区域可以代表异常区域,并且可能是瓶颈检测的方法。这些主题将在我们未来的研究中涉及。
结论
本文提出了一种利用大量 GPS 数据和模糊推理系统进行拥堵量化的方法。这里,速度转换矩阵的新概念被用作交通数据建模方法。
目的是以一种更具“可读性”的方式展示论文“基于速度概率分布的拥堵指数估算模糊推理系统”[1”的研究结果,并鼓励读者学习新知识或在自己的研究中使用这些知识。
如果你对类似的、与交通相关的话题感兴趣,请关注我的媒体简介,或者在研究门户:)查看发表的研究
如有任何问题或建议,欢迎评论或联系我!https://www.linkedin.com/in/leo-tisljaric-28a56b123/
参考
[1]l . tiljari,E. Ivanjko,Z. Kavran,T. Cari,基于速度概率分布的拥堵指数估计模糊推理系统,
交通研究程序,55,2021:1389–1397
[2]l . tiljari,T. Cari,B. Abramovi 和 T. Fratrovi,使用速度转换矩阵对全市范围内的交通状态进行评估和分类(2020) ,可持续性,12,18:7278–7294
[3] HCM 2016:公路通行能力手册。华盛顿特区:交通研究委员会,2016 年。
[4]模糊推理系统在 Python 中的实现,https://towards data science . com/Fuzzy-Inference-System-implementation-in-Python-8 af 88d 1 f 0 a 6 e
[5]调整模糊推理系统,https://WW2 . mathworks . cn/help/Fuzzy/tune-Fuzzy-Inference-Systems . html
使用 fuzzyjoin 帮助您分析讨厌的在线调查数据
… 名字不符的时候特别有用!
几天前,我被要求帮助某人整理一项在线调查的结果。问题是,在线调查分两部分进行,每一部分都有自己的谷歌表格。请求是执行一个简单的线性回归模型,其中一个 Google 表单记录了响应变量,另一个 Google 表单记录了解释变量。然而,每个调查参与者都没有唯一的标识符,他们被告知输入的唯一信息是他们的名字。这会很有趣的!下面是我如何在 r 中使用fuzzyjoin来做这件事(及其背后的思考过程)的记录。
数据
为了让你更好地了解这些数据有多糟糕,这里有一个在两种表格中记录姓名的快照(姓名已被更改,因为这是个人数据,但这实际上是看起来的样子)。第一组数据记录了他们的总体幸福感…

幸福数据
第二组数据记录了他们在工作和与孩子相处时面临的困难。第二组数据是在谷歌表单上完成的,但名字是用下拉列表。这就迫使这些名字按照他们注册的名字有固定的格式。

难度数据
所有 4 个名字都出现在两组数据中,但是正如你所看到的,在名字的输入上有相当多的差异。例如,在新加坡,许多老一辈的人只登记了中文名字(例如“郭丁丽”),却采用了英文名字(例如“Mary Kwek”),而没有在政府登记。事实证明这的确是个问题。
输入模糊连接
和我一起工作的人熟悉 R 语言,因此要求我用 R 语言做这个项目。看着各种可用的库,我最终决定用fuzzyjoin作为最好的前进方式。
导入文件后,我使用在fuzzyjoin库中找到的stringdist_join函数来执行两个文件的匹配和组合。在继续解释每一步的用途之前,我将把我使用的整个代码块放进去。
前两个参数,by和mode类似于通常的dplyr连接,所以我不会真的触及它们。
第一个重要论点是method。在这种情况下,method 允许您选择想要使用的度量,以便量化单词字符串之间的距离。点击,您可以找到完整的指标列表。我并不真正熟悉所有的指标,但我知道有几个(例如 Hamming )肯定不合适,因为它们要求文本字符串具有相同的字符数。相反,为了找出哪种方法给了我最好的结果,我创建了数据集的一个子集,并尝试了几种方法,直到我找到一种我认为(通过目测结果)看起来最好的方法。如你所见,我最终选定了 Jaro 距离。经过反复试验,它给了我最好的结果。
下一个参数是ignore_case,我显然将它设置为“TRUE ”,因为有些名字是大写的,而有些不是。
max_dist然后设置最大允许距离。在这种情况下,因为我使用“JW”返回 0 和 1 之间的距离,所以我选择了 0.5 之间的某个值。同样,这将涉及一点点的尝试和错误,以找到一个最佳的距离。max_dist越大,返回的匹配数越多,返回的数据帧也越大。在我的子集中只有 25 个名字,将其设置为 1.0 会返回 1078 行,而设置为 0.5 会得到 564 行。
distance_col允许你创建一个存储匹配之间距离的列——这是一个非常有用的列,可以在以后过滤匹配。
有了 564 行之后,我按照 Name.x(这是第一个数据集中的姓名列,带有“官方姓名”)将它们分组,并使用slice_min来只保留每个姓名之间距离最小的那一行。
然后我把所有的名字按降序排列,看距离最高的名字。我被告知,并不是每个调查参与者都完成了两个调查,因此一些名字将不得不被删除,因为事实上根本没有匹配。快速浏览这些名字,我看到第一次“真正的比赛”是在距离降到 0.27 以下后才开始的,因此增加了一个filter。
然后……完成了!我从两个完全不同的数据集中提取了两组相当混乱的名字,并用fuzzyjoin.将它们连接在一起。感谢阅读,我希望当你需要分析任何名字混乱、不匹配的调查数据时,这篇技巧对你有所帮助!
最初发表于 http://zachlim98.github.io/me
使用高斯混合模型来转换用户项目嵌入并生成更好的用户聚类
通过使用高斯混合模型生成新的和更紧密的用户特征来改进用户项目嵌入的聚类

马库斯·斯皮斯克在 Unsplash 上的照片
一.导言
一家销售音乐唱片的大公司聘请你帮助他们更好地了解客户和他们的偏好,以便他们可以个性化他们的服务。该公司主要感兴趣的是了解听众中存在的群体类型、他们的聆听模式和偏好。为了这个任务,他们给了你他们最珍贵的工件——他们完整的用户-艺术家交互数据集。
用户-项目交互数据是表示用户和项目之间不同参与形式的常见方式,如用户观看的电影、用户点击的广告或用户购买的购物项目。它被认为是一种有点原始但有用的输入形式,用于根据用户参与的项目找到他们之间的相似之处,或者预测他们将来会参与的项目。

由 9 个用户和 4 个项目组成的原始交互数据。表示相同数据的另一种常见方式是使用用户-项目对列表。
因为原始交互数据通常非常稀疏和嘈杂,所以通常的做法是首先将其嵌入到较低维度的空间中,以便仅保留关于每个项目或用户的重要或相关信息。然后,数据的低维表示可以用于各种下游任务,例如根据用户选择的项目之间的相似性对用户进行分类或聚类。
在低维空间中嵌入用户-项目交互数据的方式不止一种。最简单的方法是通过一种降维算法(如 PCA)来运行它,同时保持尽可能多的方差。有人可能会说,输出并不是真正的嵌入,尽管它可以类似地用于对数据进行聚类,并找到用户之间的相似之处。
另一个非常常见(并且通常更加准确)的策略是使用某种形式的矩阵分解,以便将用户项目矩阵(N_users * N_items)分解为两个低维矩阵——N _ users * N _ latent _ factors 和 N _ items * N _ latent _ factors——其乘积就是原始的用户项目矩阵。得到的潜在维度将捕获关于每个项目的最相关的信息或特征,并因此提高下游聚类任务的性能。

3 个用户和 3 个潜在因素的低维嵌入数据

3 个项目和 3 个潜在因素的低维嵌入数据
假设您确实能够创建一个较低维度的嵌入层,它仅使用 50 个维度来准确表示您所获得的数据集中存在的大约 4,000 位艺术家(或项目)。然而,您尝试的大多数聚类算法仍然无法在数据中找到任何有意义的聚类,这些数据似乎太密集,无法以有意义的方式进行聚类。
这是一个很常见的场景。随着使用的频繁和项目数量的增加,嵌入层的维度和复杂性通常也会增加。当您有几千个经常使用的项目时,很可能您的嵌入层将包含至少几十个压缩维度,这并不总是很好地聚集(尽管它们可能对其他类型的任务非常有用)。显然,您可以将数据嵌入到任意数量的维度中,但是在某个点上,如果您的维度太低,您可能会开始丢失关于用户之间区别的重要信息。
在这篇文章中,我想建议一种方法来进一步细化难以聚类的用户嵌入,以提高其聚类的性能和结果。简而言之,它基于从项目嵌入数据中学习高斯混合模型,然后基于每个用户成为每个项目聚类分布的一部分的概率,使用它来生成新的用户向量。除了简单且在足够多的实验中被证明是有用的这一事实之外,所提出的方法没有什么特别巧妙的地方,使得共享它成为一个好主意。虽然它是针对聚类用户-项目嵌入的用例,因为它依赖于共享相同向量空间的项目嵌入和用户嵌入的可用性,它当然可以扩展到其他上下文。
这篇文章分为四个主要部分。第二部分展示了一种从用户-项目交互数据生成嵌入的方法(尽管还有更多)。第三部分尝试对数据进行聚类,并展示了聚类中的主要问题。第四节介绍并实现了我们提出的方法。第五节得出结论。
二。嵌入交互数据
这里我要用的数据集是 LastFM 360K 。正如您在下面看到的,它可以很容易地格式化为普通的用户-项目交互数据。

不幸的是,我不能使用任何形式的原始数据集或子集来测试这里提出的方法,但 LFM 的数据是公开可用的,似乎是一个很好的替代方案。然而,值得注意的是,LFM 数据集在几个方面有所不同,这实际上使得结果不如我们进行的原始实验令人印象深刻。仅举一个明显的例子——LFM 的数据集实际上登记了用户、项目和使用次数。这意味着每个“行”或用户-条目对都应该根据所报告的交互数量进行加权,以获得准确的结果。为了简单起见,我选择忽略这些复杂性,但这显然应该在实际场景中加以考虑。
所以第一步是生成嵌入。我实际使用的嵌入模型是 Word2Vec。虽然 W2V 是一个最初设计用于根据单词在句子中的出现来创建单词嵌入的模型,但它也可以很好地创建多种序列的嵌入,包括根据用户“购物篮”中的出现来嵌入项目。关于如何生成嵌入,我不会涉及太多的细节,因为这里描述的方法可以应用于任何嵌入,而不管它是如何生成的,而且,我相信附件笔记本中的相关代码是非常简单明了和可重复的。
不过简而言之,我使用了(Gensim 的)W2V,以便根据艺术家在用户历史中的出现来学习嵌入他们。换句话说,我们将向模型提供用户列表和每个用户听过的艺术家列表,模型将为每个艺术家生成代表其最重要特征的潜在因素向量。
例如,由于列表中的艺术家之一是“罗伯特·舒曼”,我们训练的模型将生成相应的嵌入记录,该记录用由我们数据集中所有艺术家组成的空间中的向量来表示“罗伯特·舒曼”,如果它是准确的,则将其“定位”为更接近于类似于的艺术家的向量,即通常与舒曼一起出现在具有类似历史的用户中的艺术家。

舒曼的嵌入向量
虽然有更准确的方法来验证嵌入的准确性,但使用一些可以直观测试的示例来验证它也是一个好主意。如下所示,我们确实期望从音乐艺术家和收听用户的数据集学习的嵌入来检测相同流派的艺术家之间的相似性。

因此,现在我们有了准确的嵌入矩阵,我们可以继续对我们的用户进行聚类,并更好地了解数据中存在的组的类型和结构。然而,使用 W2V 嵌入交互数据的一个缺点是,当我们对聚集用户感兴趣时,它只留给我们嵌入项目或艺术家。处理这种情况的一种方法是获取每个用户参与的项目或艺术家的所有向量,并获取它们的平均值或中间值。换句话说,如果我听了舒曼和米勒·戴维斯的音乐,那么我的用户将由一个向量表示,该向量等于:
(model.wv['robert schumann'] + model.wv['miles davis']) / 2
详细介绍这种方法的优点和缺点或者回顾其他方法需要很长时间,但这是一种在相关潜在空间中捕捉用户“位置”的相当有效的方法。上面得到的向量将在舒曼和迈尔斯之间,以某种方式描述我的偏好和像我一样的用户的偏好。例如,在下面的代码中,我为一个听了两位古典艺术家(舒曼和马勒)和两位爵士艺术家(米勒·戴维斯和桑尼·罗林斯)的用户生成了一个均值向量。您可以看到,与该用户均值最相似的 10 位艺术家确实是爵士乐和古典音乐艺术家的混合体。

因此,我们将通过取每个用户听过的所有艺术家的平均值来创建用户矩阵。
三。聚类用户
所以现在我们开始尝试将我们的用户聚集起来。我们将使用 HDBSCAN,这是一种高效的基于密度的聚类算法,因为我们希望该算法根据数据的结构来确定数据中的聚类数,而不是对数据的形状及其分布进行假设,还因为它是一种相对快速高效的算法。不幸的是,我们不太成功。

不出所料,HDBSCAN 已经确定,除了少数几个小集群,我们的大部分数据都是噪声,上面的 2D 投影(用户嵌入)在一定程度上解释了这一点。我们的数据非常密集,这意味着用户不容易区分零件。这种结果很常见,通常需要进行更多的数据转换和超参数调整实验。
在这一点上,有些人会争辩说,试图“撕毁”聚类有些人为或徒劳,因为数据以这种方式聚集在一起的原因是…因为这就是数据点之间的关系和距离。然而,记住手头的问题是至关重要的。我们的任务是对拥有相似偏好和行为模式的用户进行细分。换句话说,该任务的目的是将数据分割成簇。然而,因为我们的数据非常密集,所以我们需要找到一种方法,让相似的用户比他们真正的更近,让不同的用户彼此更远。
如上所述,这篇文章的目的是为你处理这种情况的方法库增加一个工具。但是,在此之前,我只想简要展示两个其他方法或转换,作为基线。
首先,尽管有些争议,但提炼“大块”数据的一种非常有用的方法是使用流形学习算法,如 TSNE 或 UMAP,以便更好地表示局部结构,并通过使相似的样本更接近来进一步提炼聚类。我选择使用 UMAP 是因为它在大 Ns 上的良好性能以及它在局部和全局结构之间的平衡。
正如你在下面所看到的,我们的数据现在有了一些改进,因为 UMAP 确实进一步细化了聚类,将它们分成一部分,使 HDBSCAN 能够找到更密集的结构。但是,我们的大部分数据仍然是集中在一起的。

另一种常见的(也是更简单的)分解集群的方法是简单地获取日志并规范化数据。我在这里忽略了很多理论,但主要部分是,使用下面的代码应用这种转换将为我们留下以下结构。

使用对数变换和归一化来分离聚类

这种方法似乎也是一种改进,尽管它在 2D 空间中比在多维空间中分离得更好。换句话说,如果要在 2D 执行聚类,这种转换会很有帮助,但这意味着将丢失大量数据,这意味着结果的值将取决于手头的问题和数据。
三。从集群到分布
让我简要解释一下提议的转换的逻辑,然后深入研究实现。
我们有一个向量空间,包含了项向量和用户向量(其中每个用户向量等于用户参与的项向量的平均值)。我们想把相似的用户拉近到一起。因为使用户相似的是他们听相同的项目或艺术家的事实,那么,换句话说,我们希望用户更接近他们参与的项目,而远离他们不参与的项目。这让我们尝试下面这个简单的方法。
- 通过学习一个混合模型来聚类项目向量,该混合模型基于每个项目都是从高斯分布中采样的假设。当这个阶段完成时,我们有 K 个项目集群,而每个集群代表一个项目分布。一个好的结果将产生相似项目的聚类,例如摇滚乐队、流行艺术家等的聚类。但是,即使这种聚类不准确,它也仅用于转换数据(或从数据中生成新的要素)。让我们称这个阶段的结果为项目集群。
- 基于每个用户成为每个项目聚类的一部分(或其分布的一部分)的概率,为每个用户创建一个向量。例如,假设在步骤 1 中,我们找到了两个项目分类—一个是古典音乐艺术家,另一个是爵士乐艺术家。在步骤 2 中,我们迭代我们的用户向量,并估计每个用户向量成为每个项目聚类的一部分的概率。换句话说,对于每个用户,我们估计其向量从古典艺术家项目聚类中采样的概率,以及其向量从爵士艺术家项目聚类中采样的概率。简单来说:
Assuming we have 2 clusters (or distributions): Z1 and Z2, and user vector X1The new vector of user 1 will be [P(Z1|X1), P(Z2|X1)]
为什么这很有帮助?我还不能提供一个数学上严格的证明,部分原因是它并不总是正确的,但是下面的例子可能是解释直觉的一个起点。
考虑下图中的向量 A、B 和 C(每个向量由 2D 空间中的数据点表示),并假设 B 和 C 之间以及 A 和 B 之间的距离相等(或几乎相等)。很明显,根据它们在空间中的位置,很难确定它们相对于周围其他向量的相似性(或不相似性)。换句话说,问题“B 更像 C 还是更像 A?”很难回答,因为它们的距离几乎相等。这就是为什么基于密度的聚类算法的性能有些差。但是,如果我们将数据视为矢量分布的混合,并且如果我们发现有两种分布——蓝色和红色,那么通过比较矢量 A、B 和 C 由蓝色分布生成的概率,它们与周围其他矢量相比的相对相似性(或不相似性)会变得更加明显。之所以如此,是因为相对清楚的是,样本 B 和 C 最有可能是从与 a 不同的分布中“取样”的。

幸运的是,使用 sklearn 的高斯混合模型(GMM),这种方法也很容易实现。当 Sklearn 的 GMM 类适合数据集时,它假设数据点是从 K 个高斯分布的混合中生成的,并从数据中学习高斯混合模型,从而提供为每个样本或向量分配其最可能属于的高斯的能力。
因此,对于步骤 1,我们使用 GMM 来聚类我们的项目并生成项目聚类,而每个聚类对应于项目向量的高斯分布。
我使用这个简单的 util 函数来对项目进行聚类,并返回训练好的模型,我需要这个模型来估计以后的概率。
可以使用多种方法来选择 K 分量的数量。对于我的任务,我使用了最小化 davies_bouldin 分数的 K。

对于阶段 2,这是重要的一点,对于每个用户向量(代表用户已经听过的项目向量的平均值),我们使用训练模型的 predict_prob 方法来获得它成为每个项目聚类的一部分的概率。
因此,我们最终得到一个用户数据集,而每个用户向量现在由它成为每个项目聚类的一部分的概率向量来表示。让我们再次尝试使用 HDBSCAN 对其进行集群。

好多了。正如预期的那样,我们的新数据或转换后的数据本质上更紧密地聚集了用户,这些用户的向量最有可能是由某个项目集群或分布生成的。如上所述,这种转换使得用户之间的相似性或差异变得更加“激烈”,因为现在每个向量代表一组概率,而不是多维空间中的位置。
这些结果可以而且应该被进一步检验,看看这些分类是否有意义。这个数据集和我的实现中的一个问题是,我没有去掉最受欢迎的艺术家,这在某种程度上扭曲了结果,以及为了简单起见我跳过的其他步骤,这也使结果不那么令人印象深刻。
然而,我们仍然可以注意到某些用户群最常听的艺术家在主题上的明显相似性。例如,我们注意到一群用户对电子音乐(下图中的群 2)或重金属摇滚(下图中的群 1)有着强烈的偏好。

喜欢电子音乐的用户群

喜欢重金属摇滚的用户群
四。结论
当用户和项目之间的交互更加强烈并且用户偏好变化很大时,用户项目嵌入通常很难聚类。这种数据集最常见的方法通常包括应用某种形式的特征变换,以便使相似样本之间的相似性更明显,差异更清晰。
在这篇文章中,我分享了另一种可能有助于解决这种情况的方法。虽然它以类似的方式操作,但它可以被视为一种特征生成方法,而不是一种变换。该方法首先基于物品数据学习高斯混合模型,该模型本质上假设数据是从混合高斯分布生成的。然后,它为每个用户向量或记录生成一个新的特征集,该特征集表示从每个分布中被采样的概率。在这样做的时候,它本质上是用一个不同的度量来代替距离相似性度量,这个度量就是成为任一分布的一部分的概率。
这种方法已经在足够多的实验中产生了非常好的结果,值得分享,但是它还远远没有经过认真的思考或测试。话虽如此,但它通常做得很好,而且肯定会有益于出现类似困难的其他领域。
希望会有帮助
笔记本可以在这里找到
使用高斯过程回归作为生成模型,使用 Python

由 Unsplash 上的 Edge2Edge 媒体拍摄
以下是如何使用高斯过程生成新数据
如今,我们可以有把握地说生成模型是人工智能的热点。处理数据的人可能知道技术细节,而对于非技术人员来说,能够从现有数据集生成新东西的想法听起来基本上像科幻小说。出于这个原因,当非技术人群遇到像 这样的东西时,深度假冒 它会爆炸得相当快。
实际上有很多生成模型,而且大多数都使用深度学习方法(你听说过 GANs 吗?)。
你必须忍受的主要的、永恒的、巨大的权衡是,同时拥有可解释性和深度学习几乎是不可能的。
我的意思是,当你使用深度学习时,你有如此多的参数、层和操作,以至于你真的无法理解你的模型在做什么:你只知道(或希望)它工作得非常好。
在我们的研究中,我们需要创建一个具有特定属性的生成模型:它需要是可解释的。我们需要非常确定它实际上在创造什么,为什么,以及如何创造。所以我们使用高斯过程。
在这篇文章中,我想告诉你如何使用一个非常简单的算法,在给定一个参数作为输入的情况下,从现有的点中创建一组新的点。我们开始吧!
1.先决条件
让我们把事情简单化:我们正在谈论高斯过程回归。这意味着,首先,它是一个回归模型:
假设在某个值 X_1 =10 处有一个点 Y_1 = 5,在另一个值 X_2 = 14 处有另一个点 Y_2 = 20。回归模型帮助您找出 X_1 = 10 和 X_2 = 14 之间的 Y 值。
当然,你可以尝试用直线,多项式,正弦和余弦来做。高斯回归当然使用高斯分布。你基本上是在假设你可以用一个平均值和一个标准差来模拟你的点数。这意味着您的过程的不同表示将根据平均值和标准偏差进行分布。
如果你想更深入一点,这里是它的工作原理。

我使用 LaTeX 生成的图像
这意味着给定点 X1 的子集和值 y1 的子集,我们可以预测在点的 X2 子集中会发生什么。
那些已经知道所有这些东西的人可能已经注意到,在一天结束时,一切都依赖于一个被称为内核k .的函数
现在,我可以花几个小时写关于内核的东西,我甚至不是这方面的专家,所以我甚至不会去尝试(查看这里以了解更多)。我们需要知道的是内核根据其他点与固定点 x 的距离来确定我们给予其他点的权重。
例如,有理由假设,如果你想了解湖人队是否会在 2021 年赢得戒指,你会看他们 2019 赛季的赛程,也许还有 2018 赛季的赛程,但你不会看 1969/1970 赛季的赛程,因为这可能会误导人。
通常,设置权重的方式是使用 RBF 核 。你也可以使用 白化内核 添加一些噪点修正。
所以说重点吧。假设你的鼻窦受到某种噪音的干扰。
请不要运行这个!你会得到一个错误,因为我没有定义很多东西。
如果你使用我们所说的,我们可以得到这样的东西。
这就是你通常使用高斯过程回归的方式…
但是我们如何从我们的数据集中生成一组新的点呢?
换句话说:
如何才能最终使用高斯生成回归作为生成模型?
基本上我们要这样做:给定某个输入值(我们就称之为 T)我们要生成一个全新的点列表(称为时间序列)。
让乐趣开始吧。😃
2.这个想法
现在我们知道了这一切是如何工作的,我们需要理解如何真正地产生新的东西。
假设你有一个正弦函数,这个正弦的振幅取决于外界温度。大概是这样的:

我用乳胶制作的图像
其中噪声具有零均值和固定方差。
想象你有 200 个温度,每个温度 100 个点。这意味着,例如 T = [1,2,…,200]和 x = [1,2,…,100]。您将拥有一个 200 行 100 列的矩阵(表格)。你可以为每张桌子复制我们之前讨论过的想法。
另一方面,你可以反过来想!你可能会想到 100 个点,说每个点有 200 个温度值。如果现在你考虑你能得到的这种“颠倒”的情况,给定温度 t1 下的某一点 x_0 ,和在同一点 x_0 下的,对于另一个温度 T2 = t1+1,在两者之间会发生什么,例如在 T3 =(T2+t1)/2。
如果对数据集的所有点都这样做,你将得到 T3 =(T2+t1)/2 的整个点集。
伟大的事情:极其简单。坏消息:数据集的每个点都有一个模型,所以你不想有太多的点。
3.代码
我说了很多。让我们用一些代码深入研究一下。😄
3.1 图书馆
这是你需要的:
其实没什么特别的,只是相当主流的模块,比如 sklearn,matplotlib,numpy,pandas,seaborn…
3.2 实验
在我的实验中,我保持事情简单。当然,你可以想象有一个不同参数的时间序列列表,并应用你将在这几行中找到的相同推理。
我坚持分析这个信号的想法:

我用乳胶制作的图像
但是让我们说得更清楚些:
训练集特性:
- t 是一个“温度”,我们假设从 100 到 200 有 100 个等间距的温度
- x 是一个固定向量,由 0 和 10 *π之间的 50 个点组成
- 噪声方差= 5,均值= 0
测试设置特性:
- x 是同上
- T 是通过采样由中间温度组成的等间距数组的 20 个值 T29 得到的:T_test = (100.5,101.5,102.5,…)
下面是如何生成这个数据集。
训练集代码:
测试设置代码:x
实际上,您可以在这里用这段代码来拟合您的模型:
拟合你的模型:
这个模型会给你一堆拟合的模型(我们说过,每个点一个),如果你要的话,训练集上的结果。
然后,您可以使用以下代码生成新点:
运行训练好的模型并生成新的点。
整个过程就是由这个简单的函数完成的:
但是事情变得有趣多了。😃
还记得我们之前讲过的内核吗?嗯,可以调整内核以获得尽可能好结果。
实际上,来自 sklearn 的伟大的家伙们会优化这个过程,以便你可以尽可能地获得最佳值,但是尝试给算法一个提示作为起点是明智的。然后他们会开始围绕它进行优化。
当然,我们不知道该给出什么样的暗示。为此,我们将进行网格搜索,并从中获得超参数的最佳组合。
在优化我们的模型之前,我们当然需要定义我们在寻找什么。一般来说,我们可能需要两样东西:
- 良好边界(几乎所有数据都在 1.96 方差内)
- 好的意思(很合适……相当合理)。
换句话说,我们想要评估边界和 MSE 中包含的点数。您可以使用以下代码来实现:
评估自己的表现
这就是你如何网格搜索你的最优值。注意,我们玩的是一个双参数游戏: l 和 n 。有关它们的更多信息,请访问https://scikit-learn.org/stable/modules/generated/sklearn.gaussian_process.kernels.WhiteKernel.html和https://scikit-learn.org/stable/modules/generated/sklearn.gaussian_process.kernels.RBF.html。
网格搜索功能(一):
网格搜索功能(二)
4.结果呢
网格搜索功能执行以下步骤:
- 生成您的培训数据
- 生成您的测试数据
- 网格搜索你输入的所有模型
- 给你表演
让我们试试这个:
现在让我们看看表演吧!
有些模型的均方误差很差,但是 l=0.5 的模型看起来不错。
边界包含的点数是多少?
好的,如果我们设置 l=0,我们得到基本上完美的边界(记住 x 只有 50 个点),但是均方误差很可怕。所以就 MSE 和 in 点数而言,最好的是 n=0.01 和 l = 0.5。
让我们修复它们:
并使用它们:
这是你的最佳预测,从一个输入(T=101.5)生成一个全新的时间序列:
放大:
给定一个温度输出,我们生成了整个点序列:
我们使用高斯回归作为生成模型!
性能怎么样?它们符合我们的预期:
5.结论
如果你喜欢这篇文章,你想知道更多关于机器学习的知识,或者你只是想问我一些你可以问的问题:
A.在 Linkedin 上关注我,我在那里发布我所有的故事
B .订阅我的 简讯 。这会让你了解新的故事,并给你机会发短信给我,让我收到你所有的更正或疑问。
C .成为 推荐会员 ,这样你就不会有任何“本月最大数量的故事”,你可以阅读我(以及成千上万其他机器学习和数据科学顶级作家)写的任何关于现有最新技术的文章。
再见:)
利用图论有效解决数据科学问题
即使是最基本的也会让你开始!

为什么我应该现在学这个,而不是以后?
在数据科学领域有很多东西要学。通常,作为一名数据科学家,您倾向于边走边学。如果你当前的任务需要新的方法,你做一些研究,直到你能解决手头的问题。然而,如果我们没有学习这门学科的基础知识,图论的应用通常隐藏在我们意想不到的地方。在这篇文章中,我将向你展示在某些应用中,图论的基础知识是如何发挥作用的
- 大大加快你的代码
- 减少大量循环、索引等功能。来甜的俏皮话
- 使以前几乎无法解决的任务成为可能
所有学到的概念都会非常基础,并配有图片或代码。另外,我不会介绍超过 5 个新名词。
基本概念和术语
一个图由一组 节点 {a1,a2,…,和}以及一组 边 ,组成,这些边将部分或全部节点相互连接起来(图 1 )。例如,假设有一组机场{ap1,ap2,ap3}。如果所有机场之间都有航班,但 ap2 只提供到 ap3 的航班,则边是{(ap1,ap3),(ap3,ap1),(ap2,ap3),(ap3,ap2)}。典型地,术语“图”是指所有边也以它们的反转形式(ap1,ap3)——>(ap3,ap1)存在的那些图。否则,它被称为,这不是本指南的一部分。****

图 1:有三个节点和两条(唯一的)边的图的例子。除非另有说明,所有图片均由作者提供。
每一个图都可以用它的 邻接矩阵 用数字形式表示。在这种情况下,它是:

如您所见,这个对称矩阵在存在连接两个节点的边的地方保存 1,在不存在边的地方保存 0。更技术性的描述是所有节点对之间长度为 1 的路径的数量。
这就是你开始行动所需要的全部理论。让我们解决一些现实世界的问题。你可以在 github repo 中找到所有的代码。
数据准备
资料组
作为一个现实世界的应用程序,我决定使用一个飞行数据集。公开发布,该数据集包括在 2500 个参与机场起降的所有航班。下载指纹为“MD5:0 df 632 f 65 E1 d 7 b 7 DD 6 f 89294 e 81861 e 0”的数据集来复制我的分析。
清洁
对于这个分析,我只保留了开始(“起点”)和到达(“目的地”)的位置以及时间戳(“日期”)。此外,出于演示的目的,我随机抽取了 1000 个航班作为样本。关于精确的复制,请参见链接库。

分析
为了举例说明图论方法的效用,我们将向数据科学家提出一些关于飞行数据集的基本问题。然后,我们将比较典型的 pandas 方法和图表方法的简单性和效率。
预分析:创建邻接矩阵
图形方法基于操纵邻接矩阵。因此,我们必须首先创建它。将计算邻接矩阵所花费的时间视为一项投资。这是值得的。
首先,我们需要获得数据集中所有唯一机场的列表。使用 pandas 和 numpy 很容易做到这一点(不要忘记导入它们)。
**airports = np.unique(np.append(df[“origin”], df[“destination”]))**
由于 python 列表是有序的,每个机场现在都与一个固定的数值相关联。这个值将代表它在邻接矩阵中的位置。此外,我们可以使用这些来将数据集中的每个字符串映射到一个数值,这允许有效地计算邻接矩阵。
**mapping_dict = {k:i for i,k in enumerate(airports)}
df_mapped = df.applymap(lambda x: mapping_dict[x])**
现在,邻接矩阵只有一步之遥。我们将创建一个 nxn 零矩阵,n 是唯一机场的数量。然后,我们遍历每个数据帧行一次,看看是否需要将 0 值更改为 1。
**# Create null-matrix
A = np.zeros((len(airports), len(airports)), dtype = int)# Enter 1 into the null-matrix where there is an edge
for date, flight in df_mapped.iterrows():
i, j = flight["origin"], flight["destination"]
if A[i,j] == 0:
A[i,j], A[j,i] = 1,1**
好吧,我们做到了!这是我们的邻接矩阵:

问题 1:你能直接从 A 飞到 B 吗?
这个问题看起来极其琐碎。确实是!然而,即使对于这种问题,图表方法也优于标准方法。解决这个问题的常见方法是遍历每一行,如果有这样的直接路径,则返回 True。这种类型的函数可能如下所示:
**def exists_direct_path(df, node1, node2):
# Loop through each row
for i, row in df.iterrows():
# Check whether (node1, node2) or (node2, node1) is in the dataset
if {row["origin"], row["destination"]} == {node1,node2}:
return True
return False**
使用邻接矩阵,简单地索引它就可以了:
**A[node1, node2] == 1**
但是等等?这不是伪比较吗?“exists _ direct _ path”函数不做我们在预分析中做的事情吗?事实上,图表方法在这里只有优势,因为我们已经预先计算了普通方法现在做的事情。然而,这才是重点!我们已经遍历了每一行一次,现在可以简单地通过索引来检查每一条路径。使用“exists_direct_path”,函数的每个调用都将执行相同的循环。这显然是低效的。
事实上,做 200 次这样的比较花了我的笔记本电脑 18 秒。图表法耗时 0.0006 秒。我们已经可以看到:
有些问题使用标准的 python 和 pandas 方法完全可以解决,但是与基于图形的方法相比效率非常低。
问题 2:A 直接链接到多少个机场?
让我们关注一个稍微复杂一点,但仍然简单的问题。想象一下,你的老板想在另一个城市开设一家子公司,但这个城市与世界其他地方的最佳连接是至关重要的。
用传统方法解决这个问题意味着遍历所有行,并跟踪在类似列表的东西中找到的所有可能的目的地。它可能是这样的:
**def degree(df, node): # degree is the technical term
# Setup empty list
flights = []
# Loop through every row
for i, row in df.iterrows():
# If the node is either an origin or a destination, there must be a direct path
if row["origin"] == node:
flights.append(row["destination"])
elif row["destination"] == node:
flights.append(row["origin"])
# Remove duplicates
flights = list(set(flights))
return len(flights)**
使用邻接矩阵,这同样简单得多:
**A[node].sum()**
就像以前一样,运行时效率是惊人的。获得 200 个节点的度数花了我的笔记本电脑 19.5 秒,而基于图的方法花了 0.02 秒。
问题 3:从 A 地到 B 地旅行时,我需要换几次车?
让我们来看最后一个相当复杂的问题。假设一位顾客正在计划他的假期。他想在当地机场起飞,心中有五个可能的目的地。然而,他只想在旅途中换乘两次或更少。在这种情况下,我将从基于图的方法开始。你很快就会明白为什么。
我们可以利用邻接矩阵的另一个便利特性。请记住,前面我将邻接矩阵定义为表示每对节点之间长度为 1 的路径的数量。结果是,如果你把邻接矩阵提升到 2 的幂,你会得到长度为 2 的路径数,以此类推。换句话说:如果机场 ap1 和 ap2 之间的航班需要至少三次转机,你会发现:(A3)[ap1,ap2] > 0。因此,要找到两个机场之间的最短路径,我们需要将 A 乘以自身,直到我们找到一个值> 0。**
这个技巧允许我们通过提高 A 的幂来有效地找到最短路径,直到找到一条路径。一旦找到它,最后一个 A 数就是最小路径长度。我们还需要告诉计算机什么时候停止寻找,否则它会一直寻找,直到找到一条路径。在这种情况下,我将这个最大长度设置为 10。如果没有找到匹配的⁰,程序将返回 10。
**def shortest_path(A, x, y, iterations = 10, mapping_dict = mapping_dict):
# Create copy of Matrix to work on
M = A.copy()
# Get numerical representations of airports
i, j = mapping_dict[x], mapping_dict[y]
# Define the maximum power to which A is to be raised
iterations = 10 # A = A*A until a path is found or the max iterations are reached
for k in range(iterations):
if M[i,j] == 0:
M = np.matmul(M,A)
else:
return (x,y,k)**
回到手头的问题,您的客户想要在“LFPO”起飞,并记住了目的地“YWHA”、“LTAC”、“LIRF”、“埃夫拉”和“KRFI”。
**home_airport = "LFPO"
vacation_destinations = ["YWHA", "LTAC", "LIRF", "EVRA", "KRFI"]
max_changes = 2**
使用“shortest_path”功能,我们可以很容易地告诉他最多 2 次换乘就可以到达哪些目的地。
**n_changes = [shortest_path(A, home_airport, destination) for destination in vacation_destinations]acceptable = [destination for location, destination, changes in n_changes if changes <= 2]acceptable**
输出:
**['LIRF', 'EVRA']**
现在,让我们用一种不基于图论的不同方法来实现它。弄清楚如何做到这一点并不容易。事实上,我认为解决这个问题的唯一方法是尝试将多个航班相互链接的每种组合。一旦找到所有组合,最短路径就是所有组合中具有正确起点和终点的最短长度的组合。
如果你曾经使用过具有指数复杂度的算法,你可能已经知道这种方法有多糟糕。总共有 5 或 10 个机场时,它可能工作得很好。但是以 1000 个机场为例,在你的分析完成之前,你的孙辈们就已经退休了。与这种指数复杂度相比,基于图的方法具有 O(n ) 的复杂度,因为它所做的只是乘以矩阵。
可能是一些非常聪明的人想出了我不知道的算法,这些算法比这种暴力方法更有效。然而,很明显的是:
对于关于多个对象之间存在某种关系的某些数据科学问题,了解基本的图论将允许您编写高效的代码,并解决用其他方法实际上无法解决的“基本”问题。
结论
我们已经探索了使用图论方法来回答某些(看似)简单的数据科学问题。很明显,许多问题我们可以用循环来解决。使用基于图形的方法更容易解决。一旦邻接矩阵被创建,关于感兴趣的对象之间的关系的存在的大多数问题可以使用像索引或求和这样的单一表达式来回答。如果您的代码要应用于大量数据,或者如果同样的问题可能会再次发生,那么计算一次邻接矩阵是非常值得的。
此外,使用普通方法实际上无法解决一些问题,因为算法的复杂性甚至与中等规模的数据集不兼容。如果出现这种情况,您可能想后退一步,看看基于图的方法是否有一个简单的解决方案,就像本指南中所做的那样。
为了进一步阅读,我将把你链接到 Vaidehi Joshi 的这篇伟大的文章。它涵盖了更详细的理论基础,提供了更多的图论应用实例,并链接到一些更深入的文献。
非常感谢您的阅读!
用 Python 中的 Matplotlib 创建测井数据直方图
用直方图可视化数据分布

在 Unsplash 上 Marcin Jozwiak 拍摄的照片
介绍
直方图是探索性数据分析和数据科学中常用的工具。它们是优秀的数据可视化工具,看起来类似于条形图。然而,直方图使我们能够深入了解一组数据中的值的分布,并使我们能够在一个简洁的图中显示大量数据。在岩石物理学和地球科学领域,我们可以使用直方图来识别异常值,也可以挑选关键的解释参数。例如,来自伽马射线的粘土体积或页岩体积端点。
要创建直方图:
- 我们首先取一条测井曲线,并确定其中包含的数值范围。例如,me 可能有一个伽马射线测井,来自我们的一口井,范围从 5 到 145 API。
- 然后,我们将整个范围划分为不同的仓或区间。以伽马射线测井为例,我们可以得到从 0 到 10、11 到 20、21 到 30 一直到 141 到 150 的区间。
- 创建容器后,我们获取数据并将每个值分配到适当的容器中。
- 我们最终得到的是一个区间与频率的关系图,如下图所示

在这个简短的教程中,我们将看到如何使用 matplotlib 在 python 中快速创建直方图。我们还将看到如何定制绘图以包含额外的信息,如百分位值和平均值。
相关的 Python 笔记本可以在这里找到。
本教程的相关视频可以在我的新 YouTube 频道上找到:
导入库和加载 LAS 数据
任何 python 项目或笔记本的第一步通常是导入所需的库。在这种情况下,我们将使用lasio来加载我们的 las 文件,pandas来存储我们的测井记录数据,而matplotlib用于可视化我们的数据。
import pandas as pd
import matplotlib.pyplot as plt
import lasio
我们在这个简短教程中使用的数据来自公开发布的 Equinor Volve 数据集。详情请见这里。该数据集由 Equinor(前挪威国家石油公司)发布,旨在促进研究、开发和学习。在本练习中,我们将使用该数据集中的一口井。
为了读取数据,我们将使用 lasio 库,这是我们在之前的笔记本和视频中探索过的。
las = lasio.read("Data/15-9-19_SR_COMP.LAS")
然后,我们将 las 文件转换为 pandas dataframe 对象。
df = las.df()
使用.describe()方法,我们可以探索数据的汇总统计。
df.describe()

我们可以看到,这个文件中有七条测井曲线。
- AC 代表声波压缩慢度
- 井径仪校准
- 容积密度的 DEN
- 伽马射线的 GR
- 中子孔隙度的 NEU
- RDEP 深电阻
- 中等电阻率 RMED
使用熊猫创建直方图
我们可以使用 pandas 创建一个快速直方图,而不依赖于导入其他库。
df['GR'].plot(kind='hist')
plt.show()

使用 df['GR']创建的 Volve 数据集的伽马射线测井的简单直方图。plot(kind='hist ')
使用 matplotlib 创建直方图
我们也可以像这样使用 matplotlib 创建相同的直方图。
plt.hist(df['GR'])
plt.show()

使用 plt.hist(df['GR'])创建的 Volve 数据集的伽马射线测井的简单直方图。图片由作者提供。
这产生了一个非常小的情节。我们可以看到值的范围从 0 到 150,API 为 250 时有很小一部分数据。每个 bin 大约有 25 个 API 宽,这是一个相当大的范围。
我们可以通过为 bins 参数指定一个 set number 来控制它,在本例中,我们将它设置为 30。
plt.hist(df['GR'], bins=30)
plt.show()

条柱数量增加的直方图。图片由作者提供。
让我们通过给箱子添加边缘颜色来稍微整理一下情节。
plt.hist(df['GR'], bins=30, edgecolor='black')
plt.show()

当我们这样做时,我们可以看到 API 低于 100 的仓位实际上是两个独立的仓位。
为了进一步整理绘图,我们可以指定 x 和 y 标签,并设置 x 轴限制。
plt.hist(df['GR'], bins=30, color='red', alpha=0.5, edgecolor='black')
plt.xlabel('Gamma Ray - API', fontsize=14)
plt.ylabel('Frequency', fontsize=14)
plt.xlim(0,200)
plt.show()

除了条形,我们还可以添加一个核密度估计,它为我们提供了一条显示数据分布的线。
df['GR'].plot(kind='hist', bins=30, color='red', alpha=0.5, density=True, edgecolor='black')
df['GR'].plot(kind='kde', color='black')
plt.xlabel('Gamma Ray - API', fontsize=14)
plt.ylabel('Density', fontsize=14)
plt.xlim(0,200)plt.show()

matplotlib 直方图,顶部绘制了核密度估计。图片由作者提供。
向图中添加额外信息
当计算粘土和页岩体积作为岩石物理工作流程的一部分时,我们通常使用百分位数作为我们的解释参数。这减少了异常值或少量数据点的影响,例如,这些数据点可能代表薄的热页岩。
我们可以使用内置的熊猫函数来计算关键的统计数据:mean()和quantile()。
mean = df['GR'].mean()
p5 = df['GR'].quantile(0.05)
p95 = df['GR'].quantile(0.95)
print(f'Mean: \t {mean}')
print(f'P05: \t {p5}')
print(f'P95: \t {p95}')
这将返回以下输出。
Mean: 71.98679770957146
P05: 12.74656
P95: 128.33267999999995
为了更好地了解这些点相对于我们的数据的位置,我们可以使用 axvline 将它们添加到绘图上,并传入计算的变量、颜色和标签。
df['GR'].plot(kind='hist', bins=30, color='red', alpha=0.5, edgecolor='black')
plt.xlabel('Gamma Ray', fontsize=14)
plt.ylabel('Frequency', fontsize=14)
plt.xlim(0,200)
plt.axvline(mean, color='blue', label='mean')
plt.axvline(p5, color='green', label='5th Percentile')
plt.axvline(p95, color='purple', label='95th Percentile')
plt.legend()
plt.show()

绘制了关键统计线的 matplotlib 直方图。图片由作者提供。
摘要
在这个简短的教程中,我们介绍了如何将测井曲线显示为直方图,并定制一个适合包含在报告和出版物中的图。
感谢阅读!
如果你觉得这篇文章有用,请随时查看我的其他文章,这些文章从不同的角度研究了 Python 和测井数据。你也可以在 GitHub 找到我在这篇文章和其他文章中使用的代码。
如果你想联系我,你可以在LinkedIn或者我的 网站 找到我。
有兴趣了解更多关于 python 和测井数据或岩石物理学的知识吗?跟我上 中 。


浙公网安备 33010602011771号