TowardsDataScience-博客中文翻译-2021-八十一-
TowardsDataScience 博客中文翻译 2021(八十一)
统计#02 —测量和可视化数据的传播
范围、四分位数的概念,以及如何使用 Python 构建盒状图

亨特·哈里特在 Unsplash 上的照片
目录
在本系列的第篇文章中,我们研究了最常见的集中趋势、平均值、中值和众数,以及异常值的概念。
然而,这些值可能无法为我们提供分析数据集所需的所有信息。在本文中,我们将开始讨论如何测量和可视化分布中的数据离散度。
范围
第一种,也可能是最简单的,测量数据分布的方法是范围。它可以通过取集合中的最大值并减去最小值来确定。
让我们考虑上一篇文章中引用的同一个例子。在这种情况下,我们假设我们调查了 10 名应届毕业生的工资,得到了以下数据:

下面是我们如何确定这个集合的范围:

下面的直方图将帮助我们直观地了解值在集合中的分布情况。

图一。显示范围的直方图(图片由作者提供)
请注意,尽管大多数值集中在 15,000 和 35,000 附近,但该范围被异常值拉长了。这是因为范围对异常值非常敏感。
如果我们忽略异常值,我们的范围会急剧减少到 20,000 ,可能会给我们一个更好的数据画面,但是故意忽略数据集中的值不是好的做法。那么我们能做什么呢?
我们可以从谈论百分位数的概念开始。考虑到数据是按升序排列的,百分位数将数据集分割成百分之一。举例来说,50ᵗʰ百分位就是中位数,因为 50%的数据都低于这个标准,而另外 50%则更高。****
在统计学中,常见的是将数据分成四分位数,这代表了数据的四分之一,或者说 25 个百分点。
四分位数
由于四分位数代表四分之一的数据,我们可以将数据集分成 4 个四分位数。
第一个四分位数,也称为 Q1 ,是 25%数据的标志。第二个四分位数, Q2 ,是中间点,也就是中位数。第三个四分位数, Q3 有 75%的数据在它下面。
如何识别四分位数
确定 Q1、Q2 和第三季度非常简单,因为你只需要知道如何找到中位数——记住,有几种方法可以计算四分位数。在这里,我将向您展示其中一个简单且广泛使用的方法。
简而言之,下面是如何找到中位数(数据集必须是有序的):
- 如果观察的数目是奇数,它就是正中间的那个数。
- 如果观察数是偶数,则是中间两个数的平均值。
现在,要找到四分位数,您应该执行以下操作:
- 找到中间值( Q2 ),将数据切成两半;

(图片由作者提供)

(图片由作者提供)
2. Q1 是数据下半部分的中位数;

(图片由作者提供)
3. Q3 是数据上半部分的中位数。

(图片由作者提供)
四分位间距
另一个需要记住的重要概念是四分位距( IQR )。它表示 Q1 和第三季度之间的数据。
********
(图片由作者提供)
如您所见,四分位数范围忽略了低于 Q1 和高于 Q3 的值,仅使用数据的中间 50%。这意味着 IQR 对异常值的敏感度远远低于我们在之前的交易中看到的范围。
箱线图
箱线图是一种图表类型,非常适合用来表示集合中的范围和数据离散度。箱形图显示了 Q1、Q2 和 Q3,以及下限和上限,超出这些范围就是异常值。
箱形图的剖析
让我们来看看一个盒子情节的要素。(请注意,该图没有针对我们的数据集进行缩放,它只是一个帮助我们可视化所有元素的示例)

(图片由作者提供)
图表中间的方框显示了中间 50%的数据。它的左边是 Q1,右边是 Q3,里面的红线显示的是中间值。方框的长度代表四分位数范围(IQR)。
在盒子外面,两条线在每一边延伸。这些线条经常被称为“须状物”。左须向下扩展到下限(通常计算为 Q1 1.5×IQR),右须向上扩展到上限(Q3 + 1.5 × IQR)。位于下限和上限之外的所有点都被认为是异常值。
用 Python 构建盒状图
现在,作为一个额外的主题,让我们看看如何使用 Python 构建一个方框图。
有几种不同的工具可以用来构建图形和图表。在这里,我使用了 matplotlib ,一个用于可视化的 Python 库。请找到下面的情节和我使用的代码。

图二。箱线图(图片由作者提供)
注意盒子是如何被挤压到左边的。这是因为我们有一个极值,200,000,在图中用一个小的菱形标记表示。顺便提一下,值得一提的是 matplotlib 对四分位数的评估与我们稍有不同,但结果是相似的。
结论
无论您只是在寻找一些统计主题的简单解释,还是正在学习如何用 Python 绘制图表,我希望本文能对您有所帮助。未来的文章将深入可变性的度量,描述方差和标准差。如果你对统计学中的其他主题感兴趣,可以查看本系列的第一篇文章,内容包括均值、中位数和众数。
总而言之,让我们回顾一下这篇文章中涉及的内容。
范围
- 测量数据分布的简单方法。
- 它是一个集合中最大值和最小值之差。
- 不太可靠,因为它对异常值非常敏感。
四分位数
- 将数据分成四份。
- Q1 代表了一个 25%的数据低于该值的点。Q2 是中间值,Q3 表示 75%点。
- 四分位数范围(IQR)代表 Q1 和第三季度之间的数据。与范围相比,它对异常值远没有那么敏感。
箱线图
- 一种非常适合显示数据分布的图表。
- 它显示 Q1、Q2 和第三季度,以及下限和上限,以及异常值。
- Matplotlib 是一个很棒的工具,可以用 Python 构建盒状图。
完整代码请参考笔记本。
参考
[1]格里菲思,D. Head First 统计学:一个对大脑友好的指南。奥莱利,2008 年。
【2】https://www.itl.nist.gov/div898/handbook/NIST/sema tech 统计方法电子手册,2012 年。**
统计#03 —标准差和方差
用 Python 中的几个代码测量数据可变性

托尔加·乌尔坎在 Unsplash 上拍摄的照片
目录
在本文中,我们将进一步了解如何度量数据可变性。在接下来的课程中,您将学习标准差和方差的基础知识。这项工作是本系列的上一篇文章的一个序列,在那里我们学习了如何测量和可视化数据的传播。
如果你没有看过上一篇文章,不要着急!简而言之,我们检查了数据分散,介绍了范围、四分位数的概念,以及箱线图如何帮助我们可视化数据在数据集中的分布。
根据你的目标,举例来说,有范围、平均值或中位数就足够了。但有时,您可能需要更进一步,分析数据的可变性,即值如何围绕集合的平均值波动。为此,今天我们将学习方差和标准差的概念。
差异
用一句话来说,方差是平均值的平方差的平均值。
为了理解它的意思,让我们先来看一下在以前的文章中使用的相同的数据集,其中我们有一个 10 种工资的列表,如下所示:


图一。显示平均值和范围的直方图(图片由作者提供)
请注意,大多数值都集中在 15,000 和 35,000 左右,但是有一个 200,000 的极值(异常值)将平均值推高到 40,500,并将范围扩大到 185,000。
现在,回到前面介绍的概念,让我们计算方差。我们将把每个点与平均值之差的平方相加,然后除以集合中值的数量。
记住均值(μ)是 40500。在 Python 中,使用名为 Pandas 的库,我们可以简单地编写以下代码来获得平均值:
DataFrame.mean()


方差通常表示为希腊小写字母适马的平方(σ)。求方差的一种方法是使用下面的等式:

其中 x 代表集合中的每一项,μ是平均值, n 是集合中的项数。
还有一种更快的方法来找到方差。请检查下面的等式。

在 Python 中,我们可以使用这一行简单的代码来获得方差。
DataFrame.var(ddof=0)

Obs:注意,我使用了参数 ddof ,并将其设置为零。别为此费心了。 ddof 代表“自由度增量”,我将它设置为零,因为 Python Pandas 给出了由 n 归一化的方差——ddof,默认情况下 ddof 定义为 1 。
好,现在我们有了方差的值,但是你可能已经注意到它非常大!这是因为方差是用平方值来衡量的。
你应该问问你自己,你能从如此庞大的数字中获得什么样的洞察力!虽然方差被广泛使用,但当我们测量均值附近数据的可变性时,还有另一个更直观的统计概念。那就是标准差。
标准偏差
在确定方差之后,找到标准差是非常简单的。是方差的平方根。
记得方差的符号是σ吗?标准差用σ表示。

在我们的数据集中,标准偏差将是:

在 Python 中,我们可以这样计算:
DataFrame.std(ddof=0)

请注意, 53,500 感觉与我们列表中的值更相关,但这意味着什么呢?
这意味着我们榜单中的平均薪资水平与平均值相差 53500 美元。
数值越接近平均值,标准偏差越小。在我们的例子中,标准差的值被拉长了,因为我们有一个异常值。
现在,为了看看标准差是如何变化的,让我们剔除异常值。我们的薪水清单现在仍然有 9 个值:


如果没有异常值,标准差的值会急剧下降。考虑到这一组新的值,平均而言,工资将偏离平均值 6,285 美元,在本例中为 22,777 美元。
结论
我希望这篇快速的文章可以帮助你理解方差和标准差的概念。这是介绍统计概念系列的第三篇文章。有兴趣的可以找之前的文章,关于数据离散的,这里。
让我们回顾一下今天在这里看到的内容。
在统计学中,方差和标准差帮助我们测量数据的可变性,即值如何围绕平均值波动。
差异

- 它是平均值的平方差的平均值。
标准偏差

- 它是方差的平方根。
- 通常,比方差更直观。
完整代码请参考笔记本。
参考
[1] Griffiths,D. Head First Statistics:一个对大脑友好的指南。奥莱利,2008 年。
统计学#04 —概率介绍
基本概念,如并集、交集和条件概率,以及维恩图和概率树的可视化

西蒙·伯杰在 Unsplash 上的照片
目录
今天,我们将谈论概率。如果你正在开始你的统计学研究,或者如果你只是想回忆一些基本概念,这个系列是为你准备的!
概率是什么?
一句话,概率就是某件事发生的可能性有多大。
例如,当你早上离家前查看天气预报应用程序时,应用程序显示今天将是一个阳光明媚的日子,下雨的可能性只有 5%,你可能不会带雨伞或雨衣,因为不太可能下雨。这并不是不可能的,因为天气预报并不总是准确的,即使它显示 0%的降雨机会,也可能会下雨。它们是通过收集大量关于温度、湿度、风等数据做出的预测,以确定大气条件在未来可能如何演变。
因此,概率可以帮助人们和公司根据收集的数据和过去的经验,考虑预测的结果,做出明智的决定。
如何计算和显示概率
首先,我们来看一个可以进行概率性思考的最简单的例子:抛硬币。
当你掷硬币时,只有两种可能的结果(事件 ): 正面或反面。对于一枚标准硬币,这两个事件发生的可能性相等,即我们有 50%的机会得到正面,50%的机会得到反面。
在本例中,这两个可能的事件可以表示为:
- P(正面) —硬币落在正面的概率
- P(尾部) —硬币落在尾部的概率
例如,如果我们想要找到 P(人头),我们需要将人头出现的方式数除以结果总数,如下所示:

概率可以表示为分数、小数或百分比,只要所有概率的总和等于 1 或 100%。在这种情况下,我们可以说 P(头) = 1/2 或 0.5 或 50% 。
概括地说,考虑下面的等式:

在哪里
- P(A) 是事件 A 发生的概率;
- n(A) 是 A 可能发生的方式数;
- n(S) 为结果总数。
结果总数也被称为样本空间,这里用字母 S 表示。
在每本统计书中都能找到的另一个常用符号是互补事件(A′)。
P(A′)是事件 A 不发生的概率,可以表示为:

在我们的例子中,正面朝着 而不是的概率也是 50%。
用维恩图可视化概率
可视化概率的一个简单方法是使用文氏图。文氏图可以帮助我们可视化的概率每一个事件,以及他们的交集和样本空间。

作者图片
请注意,上面的文氏图直观地为我们提供了关于概率的有价值的信息。它显示了两个圆圈,代表抛硬币后正面或反面的概率。观察两个圆是分开的,因为这两个事件没有交集,也就是一个硬币不能正面和反面都朝上落地。
这意味着这两个事件(正面和反面)是互斥的。
请注意,我们的图中也表示了样本空间。这里,头尾覆盖了整个样本空间内的所有事件,除此之外没有其他可能的事件,用零表示。意思是头和尾都是集合穷举事件。
既然我们讨论了互斥事件,现在让我们展示一个事件和相交的图表。
假设我们从一副牌中随机抽取一张牌。挑到的牌是红套 k 的概率有多大?让我们考虑一副包含 54 张牌的牌,分为 4 种花色——黑桃和梅花 (黑色)、红心和方块 (红色)。每种花色包含从 2 到 10 的牌,一张 j,一张 q,一张 k 和一张 a。此外,我们还有两个未着色的小丑。
那么,我们如何画一个维恩图来帮助我们形象化得到一张红套 k(红心或方块)的概率呢?

作者图片
这个例子比硬币的例子要复杂一点。54 张牌中有 4 张是国王,54 张牌中有 26 张是红套。然而,这些事件可以同时发生,我们有 2 种可能性来选择一张既是国王又是红色花色的牌,代表大约 4%的可能性。
请注意,这一次我们有一个圆圈外的值(0.481)。它表示所选的牌既不是国王也不是红色套装的概率。
交集和并集
为了理解交集和并集的概念,让我们继续看最后一个例子。我们已经知道 P(国王)和 P(红色),如下图:

我们可以把这张牌是国王和红色(国王和红色的交集)的概率写成如下:

有了这些信息,我们可以确定另一个值。这张牌成为国王或红色的概率(A 和 B 的结合):

条件概率
这是事情开始变得更有趣的地方!当我们将一个条件应用于某个概率时,我们试图确定一个事件 A 发生的概率,假定另一个事件 B 已经发生。
一个条件概率可以表示为 P(A|B) ,我们可以使用下面的等式找到它:

更进一步,我们现在有了另一种方法来寻找两个事件之间的交集:

或者

由于 P(A∩B) = P(B∩A) 。
用概率树可视化概率
当我们处理条件概率时,用文氏图来形象化它们会变得很棘手。这时候概率树就派上用场了。概率树的基本结构如下:

作者图片
现在,让我们考虑一下我们之前做的卡片组练习,但这次我们做了一点改变。假设我们已经知道我们拿着一张红牌。这张牌是王的概率有多大?概率树如何帮助我们形象化它?
首先,让我们应用条件概率的等式。请注意,我们正试图测量 P(King) ,假设牌来自红色套装。

现在让我们检查概率树:

作者图片
注意概率树是如何帮助我们理解我们正在处理的概率的。请记住,这是一个非常简单的例子,为了更好地理解,这些值被四舍五入了。根据您试图预测的事件,树表示可能有几个不同的附加分支。
结论
我希望这篇文章能帮助你快速掌握概率,如何计算它们,以及如何利用维恩图和概率树来可视化你正在处理的概率。
这并不是概率论的详尽材料。如果你想更深入地研究这个主题,更进一步的是学习其他关键理论,比如全概率定律和 T2 贝叶斯定理。
总结一下,我们来复习一下今天看到的内容。
计算概率

- 事件 A 发生的概率是 A 可能发生的方式数除以可能结果的总数。
- 互斥事件不能同时发生(像头和尾)。
- 事件和的补充是事件没有发生。 A 不发生的概率用 P(A’)表示。
交集
- 事件A和 B 发生的概率是 A 和B的交集
- 用 P(A∩B) 来表示。
- 如果 A 和 B 是互斥事件, P(A∩B)=0。
联盟
- 事件A或 B 发生的概率是 A 和B结合的概率
- 用 P(A∪B) 表示。
条件概率
- 这是事件 A 发生的概率,假设事件 B 已经发生。
- 用 P(A|B) 来表示。
可视化概率
- 维恩图是可视化概率的好方法。它可以显示样本空间中所有事件的概率,以及它们的并集和交集。
- 概率树对于可视化复杂的概率更有用,例如,当条件适用时。
参考
【1】格里菲思 D. Head First 统计学:一个对大脑友好的指南。奥莱利,2008 年。
统计学 101:二项式分布
解释了二项式分布以及如何在 R 中找到它的概率

埃里克·麦克林在 Unsplash 上的照片
由一系列相同且独立的试验组成的试验,产生两种结果中的一种,称为二项式试验。遵循二项式分布的事件示例有
- 抛硬币时的头数 n 次
- 掷骰子时的六个数 n 次
- 在大样本人群中喜欢巧克力口味冰淇淋的人群比例
二项分布是最常见的统计分布之一。在这篇文章中,我将解释它的统计理论,并展示如何在 r 中找到二项式概率。
属性和定义
二项式实验具有以下特性:
- 由固定数量的、、相同的和独立的试验组成
- 每次试验都有两种结果,成功或失败
- 每次试验成功的概率等于某个值 p ,失败的概率等于 q = 1 — p
- 感兴趣的随机变量是 Y ,观察到的成功次数为 n 次试验
二项式分布的定义是:

其中 y 为观察到的成功次数, n 为试验次数, p 为成功概率, q 为失败概率(1- p )。
假设我们将一枚公平的硬币抛两次,然后数它有多少次正面朝上。可能的结果是 0、1 或 2 次。通过将这些值代入公式,我们可以计算出恰好得到 0、1 和 2 个头的概率。(如果硬币是公平的,正面的概率是 0.5。)
抛硬币两次(n = 2)时,没有正面(y = 0)的概率是:

抛两次硬币(n = 2)得到 1 个正面(y= 1)的概率是:

掷硬币 2 次(n = 2)时,得到 2 个正面(y = 2)的概率是:

可视化二项式概率
我们可以将概率绘制成柱状图。分布的形状看起来类似于正态分布,但是很难用如此少的观测值来确定。

作者图片
如果我们把抛硬币的次数增加到 10 次,我们可以看到一个更明显的形状。当 p 为 0.5 时,概率近似正态分布。然后 p 小于 0.5,分布向左居中,当 p 大于 0.5 时,分布向右居中。

作者图片
二项分布的期望值和方差
如果变量遵循二项式分布,我们可以计算参数如下:
- 期望值, E(Y) = np
- 方差, Var(Y) = np(1-p)
- 标准差,SD = sqrt(Var)= sqrt(NP(1-p))
如特定可能结果的二项式概率直方图所示,我们实验中的观察值将围绕期望值:在 10 次试验中,概率为 0.1 时大约 1 次成功,在 10 次试验中,概率为 0.5 时大约 5 次成功,在 10 次试验中,概率为 0.9 时大约 9 次成功。这在直觉上也是有意义的,如果成功的概率只有 0.1,我们预期观察到的成功很少。如果概率是 0.5,我们期望在大约一半的试验中观察到成功。而如果成功的概率高达 0.9,我们期望观察到很多成功。
R 中的二项分布
r 有内置的统计分布,所以很容易计算特定的概率,分位数和从统计分布生成随机数。让我们来看看不同的函数,看看它们产生了什么。
dbinom:求概率 P(X = x)
dbinom 函数返回特定随机变量( x) 、试验次数(大小)、和每次试验成功概率( prob)的二项式分布的概率密度函数(pdf)* 的值。*概率密度函数计算特定次数成功的概率。
例:我们将一枚硬币抛 10 次,有多少概率恰好 3 次正面朝上?

作者图片
例:我们掷一枚公平的硬币 10 次,每种可能结果的概率是多少?(这些是上面直方图中绘制的概率。)

作者图片
pbinom:求概率 P(X ≤ x)
pbinom 函数返回某个随机变量( q) 、试验次数(大小)和每次试验成功概率(概率)的二项式分布的累积密度函数(cdf) 的值。累积密度函数给出了二项式分布中给定值( q) 左侧(或右侧)的面积。
举例:我们将一枚公平硬币抛 10 次,得到至少 3 个头 P(X≤3)的概率是多少?

作者图片
举例:我们将一枚公平硬币抛 10 次,得到 3 个或 3 个以上人头 P(X≥3)的概率是多少?

作者图片
qbinom:求分位数
qbinom 求二项式分布的第 p 个分位数。

作者图片
rbinom:从二项式分布中生成随机数
rbinom 函数生成长度( n) 、试验次数(大小)和成功概率(概率)的二项分布随机变量的向量。

作者图片
何时使用二项式分布,何时用其他分布近似
正态分布
如上所述, p 为 0.5 时的二项式分布是对称的,大致呈正态分布。对于少量的 n 来说,该分布已经采用了标准形式。当分布偏斜时(当 p 大于或小于 0.5 时), n 必须大得多才能接近正态分布。作为指导原则,如果 np(1-p) > 5,则可以用正态分布来近似二项分布。
泊松分布
如果 n 很大并且 p 接近于 0,你可以用泊松分布来近似,因为二项式概率函数实际上收敛于泊松分布。这里的一个粗略准则是,当 n 大, p 小并且泊松参数 lambda (lambda = np)小于 7 时,泊松概率可以近似于二项式概率。
超几何分布
最后,我们应该考虑抽样投票人口或客户偏好的例子,因为这是应用二项式分布的常见情况。假设一大群人中有 30%的人偏爱素食。随机选择 10 个人作为样本,观察喜欢素食的人数。对于第一个被抽样的人,他们喜欢素食的概率是 0.3。现在要评估的关键是下一个被选中的人偏好素食的条件概率。请记住,二项式试验的一个特性是,每次试验的成功概率是相同的。如果人群很大,样本量相对较小,那么在以后的试验中成功的条件概率将大致保持不变。但是如果人口是一个 20 人的班级,那么相对于人口规模,我们的样本规模是非常大的。这意味着在以后的试验中选择一个偏爱素食的人的条件概率会因样本中早先选择的人的偏爱而大大改变。这将不再是一个二项式实验。当样本量相对于总体量较大时,应使用超几何概率分布。
如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名媒体会员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你注册使用我的链接,我会赚一小笔佣金。
https://medium.com/@andreagustafsen/membership
统计学:你是贝叶斯主义者还是频率主义者?
诊断统计一致性的最快方法
如果我告诉你,我可以通过一次抛硬币向你展示贝叶斯统计和频率统计之间的区别,会怎么样?
在我们继续之前,演示在视频形式中效果最好,所以在你看过之前不要看下面的摘要和剧透。如果有些术语不熟悉,我会链接友好的解释来帮助你。

为什么这些猫的照片?在左边,都是关于透视的。右边,都是不动的量。但最重要的是,我需要一些东西来保护你的眼睛免受下面剧透的影响,直到你看过的视频。
摘要
在视频中,有一个瞬间我问你,“我手里的硬币正面朝上的概率是多少?”硬币已经落地了,我在看着,只是你还看不到。你在那一刻给出的答案是一个强烈的暗示,表明你是倾向于贝叶斯思维还是频率主义思维。
常客: “没有概率。我可能不知道答案,但这并不能改变一个事实,如果硬币正面朝上,概率是 100%,如果硬币反面朝上,概率是 0%。”
贝叶斯: “对我来说,概率是 50%!对你来说,它是你的一切。”
只有坚持认为参数不是随机变量 (Frequentist)才有意义谈论你的方法提供 正确 答案的能力。一旦你让参数成为随机变量(贝叶斯),就不再有任何对错的概念。只有你个人的观点。
频数:参数 是 而不是一个随机变量。
贝叶斯:参数 是的一个随机变量。
一个字,巨大的差异。让我们仔细看看。
频繁主义者对贝叶斯
哪些词告诉你你在和谁打交道?
有哪些行话告诉你,你已经踏入了他们的领地?
频数主义者:置信区间,p 值,功效,显著性
贝叶斯:可信区间,先验,后验
他们的目标是什么?
他们用统计数据来改变他们对什么的看法?
Frequentist: 要采取的行动(默认行动,见本解释 )
Bayesian: 意见要有(先验信念)
主要区别是什么?
频数:参数是一个固定量(关于它没有概率)
贝叶斯:参数是一个随机变量(没有正确答案)
对你有什么好处?
加入他们的思维方式,你有什么收获?
【frequent ist:谈论你的方法的质量和“得到正确的答案”是有意义的
贝叶斯:直观的定义,例如可信区间是你希望的置信区间(但不是!)
你放弃了什么?
如果你选择了他们那边,你会失去什么?
频繁主义者:核心概念更难理解(例如 p 值和置信区间有反直觉的、冗长的定义),懒惰的思考者经常把它们搞得一团糟。
贝叶斯:你失去了谈论任何“正确答案”和“方法质量”概念的能力——没有统计显著性或拒绝空值这样的东西。从你的角度来看,只有“更有可能”和“不太可能”。
如果没有固定的正确答案,就没有错误的答案。
那么,哪个更好呢?
问题错了!选择哪一个取决于你想如何做决策。例如,如果您没有默认操作,请使用贝叶斯方法。没有默认的行动,频率主义者的方法不如贝叶斯方法实用,除非你有特殊的哲学理由在你的计算中调用真理的概念。
(注意:最后三个字很重要。我们不是在谈论一般意义上的真理概念,而是在推动这些统计方法的数学中如何处理它。这两个阵营之间的区别归结为您是否将感兴趣的参数视为固定常数。)
好吧…那么哪个更客观呢?
都不是!它们都是基于假设的,所以从根本上说是主观的。参见这篇关于假设的文章。
关键的区别在于,一旦决策背景已经形成,它们如何协助决策。
等等,样本量呢?贝叶斯不是小数据的必经之路吗?
如果你一直和那些 “如果有大量数据,则是频率主义者;如果没有数据,则是贝叶斯主义者” 的人混在一起,你可能会被说服,你应该让样本大小来决定选择哪个阵营。唉,如果你戳破他们建议背后的理由,就会变得不可靠。
是的,常客们确实拒绝 babby 数据集。如果你的手指比例子多,他们几乎肯定会告诉你不要麻烦。(了解更多这里。)
是的,这是真的,如果你采取贝叶斯方法,你可以进行少到一个(!)数据点。数学检查过了。当然可以。你能做到的。
…但是应该你吗?
允许处理少量数据可能是一个缺陷,而不是一个特性。有些情况下,你肯定不想这么做。(统计不是炼金术。我们不是凭空造金。不管你向哪个学派宣誓效忠,在一个数据点上有相同数量的数据。)
允许处理少量数据可能是一个缺陷,而不是一个特性。
“需要更少数据”的方法是做出更大的假设(这适用于两种哲学)……所以,当你的主要成分不是数据,而是你编造的一些废话时,一定要花点时间思考你的结论的营养内容。如果你在处理微小的数据时过于认真,专业的贝叶斯主义者和常客一样会忘记他们的差异,以至于以你为代价捧腹大笑。
凯西,你会害死我们的。你是贝叶斯主义者还是频率主义者?
都是!我根据我如何制定决策来选择。这取决于情况是要求在行动之间做出选择还是形成基于证据的观点。
我应该选一边吗?
我建议不要只参加一个阵营(除非你已经花了几年时间思考统计哲学,并且你愿意死在这座山上)。
老实说,宣称自己是这样或那样的有点傻,除非你已经非常深入地思考过它们。我有幸在杜克大学完成了我的研究生工作(这对于贝叶斯统计大约相当于梵蒂冈对于天主教),我注意到关于贝叶斯统计优越性的最大声的不是教授们…而是那些新学生们,他们松了一口气,不用再去记忆怪异的频率主义者置信区间的定义了(贝叶斯可信区间要容易得多)。教授们明白“更好”取决于你为什么要努力去做。他们花大量时间用贝叶斯方法思考,因为这符合他们感兴趣的决策方法。所以,我的建议是?不要选边站。将它们视为适合两种不同决策和推理风格的两种不同方法,然后让自己选择使用哪一种适合自己所处的心态/环境。
了解更多信息
- 8 分钟统计简介:【http://bit.ly/quaesita_statistics
- 统计思维课程播放列表:【http://bit.ly/statthinking
- 关于 p 值的争议:http://bit.ly/quaesita_needles
放弃
视频演示的目的是让学生直面他们对固定变量和随机变量的感受,并给他们一个线索,解释为什么 frequentist 统计学的新手在曲解 p 值和置信区间时会遇到这么多麻烦。它没有教导贝叶斯统计在实践中是如何进行的(以及什么是分布结束了),所以当我在课堂上这样做时,我立即从这个转移到先验的概念。(如果你想让我优先为你写这篇文章,转发是最快到达我内心的方式。)
感谢阅读!
一如既往,您的所作所为决定了您的社区将会听到谁的声音。请在社交媒体上分享好的、有用的文章,这样它就能超越垃圾。无为是杀死一篇文章的最好方法。(哦,你知道 Medium 允许你点击赞按钮 50 次来获得起立鼓掌吗?)
想尝试一个为初学者和专家设计的有趣的应用人工智能课程吗?我做了一个给你看。
在这里欣赏整个课程播放列表:bit.ly/machinefriend
与凯西·科兹尔科夫联系
让我们做朋友吧!你可以在 Twitter 、 YouTube 、 Substack 和 LinkedIn 上找到我。有兴趣让我在你的活动上发言吗?用这个表格联系。
不着边际的幽默
毕竟,这个小小的戏剧娱乐可能会让你的一天变得愉快。
统计学,优生学,还有我
对我未能承认我的领域起源的个人反思
本周是大屠杀纪念日,也是我在伦敦大学学院(UCL)统计系五年学习的结束。几十年来,这里一直是最杰出的优生学家的家。
直到本周,我在高尔顿讲堂和皮尔逊大楼研究卡尔·皮尔逊和罗纳德·费雪的作品,这并没有困扰我。但它应该已经完成了。
我的大部分家人都在大屠杀中被杀害,这是一个旨在根除所谓“劣等种族”的政权的一部分。然而,我仍然认为皮尔逊、费希尔和高尔顿(以及其他人)是统计学之父,他们的贡献应该得到认可和尊重。我曾天真地认为他们是那个时代的产物,他们的研究是遗传学背后的统计学的自然发展。事实并非如此。
注意:这是我写的一篇个人文章,目的是让我自己的天真负起责任。然而,这是针对其他统计学家和数据科学家的,因为我肯定不是唯一一个没有考虑我们领域真正起源的人。

2021 年 1 月 27 日——大屠杀纪念日——由大卫·托马塞蒂在 Unsplash 上拍摄
弗朗西斯·高尔顿爵士
弗朗西斯·高尔顿,“行为与教育统计领域的奠基人”:
- 率先使用问卷调查
- 发现了均值回归
- 重新发现了相关性和回归性,并发现了如何在人类学、心理学等领域应用它们
- 定义了标准差的概念
高尔顿被认为对统计学的发展至关重要,以至于许多人会故意忽略他在 1883 年建立的优生学领域。
他那个时代的产物?
在回顾关于高尔顿生平的四本书时,克劳萨(2007)用这个论点来批评其中一本书:
这本书也受到布鲁克斯倾向于将高尔顿生活的方方面面与他的优生学观点联系起来的限制。布鲁克斯没有把高尔顿的观点放在他生活的时代背景下……尽管高尔顿对他在非洲遇到的土著居民的观点很可能被视为受维多利亚时代标准的启发,布鲁克斯用 21 世纪的视角看待他们,并找到了高尔顿不宽容的证据。
多年来,“时代的产物”这一论点经常被用来为偏见辩护。从这个角度来看:高尔顿受到了他堂兄的著作《物种起源》的启发。达尔文可以被称为他那个时代的产物,因为他用“野蛮人”这样的词来描述某些人群。然而,达尔文公开反对种族主义,没有促进或帮助高尔顿的任何优生学工作。如果对高尔顿的“优生学”中固有的种族主义有任何疑问,下面是他创造这个术语的理由:
我们非常想要一个简短的词来表达改善股票的科学,它……认识到所有的影响,无论这种影响在多大程度上倾向于给予更适合的种族或血统更好的机会来迅速战胜不太适合的种族或血统。
如果对高尔顿的观点仍有疑问,他还写道:
有一种情绪,在很大程度上很不合理,反对一个劣等种族的逐渐灭绝。⁴
如果在我上大学的第一天,坐在高尔顿演讲厅,我被告知高尔顿认为种族灭绝“在很大程度上是不合理的”,我可能会对他的名字和照片感到不舒服。我的研究从根本上依赖于高尔顿的工作,我感激高尔顿。这并不意味着我需要在三年里每天听到并说出他的名字。

高尔顿展览在 UCL 举行,以提高他们对优生学贡献的认识。信用:UCL 形象店。⁵
卡尔·皮尔逊
卡尔·皮尔逊是高尔顿的得意门生,并取得了许多显著的成就:
- 发展的假设检验
- 开发了 p 值的使用
- 定义了卡方检验
- 介绍了矩量法
反犹太主义者
在《我的奋斗》出版的那一年,皮尔逊写了一篇关于犹太人的文章:
“[他们]将发展成一个寄生种族……平均来看,就男女两性而言,这些外来犹太人在生理和心理上都不如本地人。”⁶
当希特勒被任命为总理时,皮尔逊指出:
即使在今天,有太多的一般印象来自有限的或经常被错误解释的经验,太多的不充分证明和太轻易接受的理论,任何国家匆忙进行无限制的优生学立法。⁷
这似乎是合理的,直到紧接着他的警告:
然而,这一声明决不能被当作无限期中止所有优生学教学和一切形式的关于性问题的集体行动的借口。⁷
我在皮尔森大楼的一个小教室里,以一个演讲结束了我的本科课程,这个演讲从根本上依赖于皮尔森的工作。皮尔逊是 UCL 大学优生系的第一任系主任,他对我的生活和工作的贡献是我不能也不会忽视的。他的贡献对统计学和许多其他领域至关重要。但我又一次怀疑,如果我被告知更多关于这个以其命名的人的背景,我是否会感到同样舒适地走过皮尔逊大厦。

UCL 的皮尔逊大厦(左),1985 年。⁸ 信用:UCL 形象店。
罗纳德·费雪爵士
费希尔在统计学方面的工作建立并促进了许多重要的统计推断方法。他的贡献包括:
- 将 p = 0.05 确定为显著 p 值的正常阈值
- 促进最大似然估计
- 开发方差分析(ANOVA)
- 数据集(这似乎是一个难以置信的小贡献,但我用它 daily)⁹
像皮尔逊和高尔顿一样,费希尔备受尊敬。当被问及谁是“自达尔文以来最伟大的生物学家”时,理查德·道金斯提名了费舍尔·⁰(鉴于道金斯自己公开的观点,这并不奇怪)。费希尔的一个学生博德莫尔写了一本关于费希尔生平的简短但热情洋溢的传记,并把费希尔描述为“可爱”和“善良”。都没有提到优生学。
关于纳粹优生学家奥特马尔·弗赖赫尔·冯·弗舒尔,费希尔写道:
尽管他们的偏见,我也毫不怀疑,党真诚地希望有利于德国的种族股票,特别是通过消除明显的缺陷,如那些精神上有缺陷的,我不怀疑冯 Verschuer 给了,我应该做的,他的支持这样一个运动。
直到在 UCL 的第三年,我才第一次听说费希尔的名字,他再次被誉为伟大的统计学家。不用说,没有讨论种族灭绝。
建筑和陈述
在最近的一篇文章中,讨论了以人命名的建筑:
理解他们和他们的想法的正确方法是通过博物馆中适当的语境化展示,而不是通过一个隐藏的比揭示的更多的不加评论的纪念馆。
对人的笼统陈述也是如此。我假设当道金斯把费希尔描述为“自达尔文以来最伟大的生物学家”时,这仅仅是基于纯粹的产出和对该领域的贡献。然而“最伟大”这个词,就像一座以费希尔命名的建筑一样,包含了对这个人本身的判断。它隐含地假设这个人是“伟大的”,我们自动地假设(尽管这不是定义的一部分)“伟大”意味着“好”。笼统的陈述,就像未加注释的备忘录一样,隐藏的比揭示的更多。
统计学和优生学之父
给建筑物和演讲厅命名很容易。这是很久以前就应该迈出的一步,但我并不是在批评 UCL。值得称赞的是,他们花了数年时间收集信息,开展调查,听取每个人的反馈和意见。
从事统计学或数据科学工作就是在优生学的阴影下工作。五年来,我甚至没有意识到我处于这种阴影之下,我深感羞愧。但我不能责怪自己。我使用了提供给我的方程式,研究了我被告知的方法,我没有考虑属于这些方法的名字。我相信我很可能和其他数学家和统计学家一样占多数。这是不对的。我认为所有的统计学课程至少应该包括一堂关于统计学及其“祖先”的完整历史的课。
从失败中获得成功
即使建筑物被重新命名,我仍然会(每天)使用‘皮尔逊卡方检验’和‘费雪信息’。他们的名字永远留在了我的脑海里,现在他们的信仰也是。虽然我会尊重这些人对统计学的贡献,甚至受到他们的启发,但我现在可以谈论他们,而不会赞美他们的性格或无意中纵容他们的信仰。我将利用过去的方法来促进未来积极的、好的和合乎道德的选择。学习别人失败的地方将帮助一代又一代的统计学家学习如何成功。
我们时代的产品
不幸的是,我只能以警告结束这一切。虽然优生学不太可能(尽管并非不可能)在现代政治中重新出现,但统计数字和数据现在比以往任何时候都更容易被操纵。
从返回错误结果的机器学习的天真误用,到原始数据的恶意操纵,这个领域正在接受考验。从政府到个人,从俄罗斯到帕洛阿尔托,从反面具者到反魔鬼;数据和统计舞弊的全部后果仍然未知。
袖手旁观会让我们成为“他们时代的产物”。这还不够好。数据科学需要更多的监管。医生有希波克拉底誓言,为什么我们没有南丁格尔誓言:“不操纵数据和结果。促进统计的道德使用。只训练你理解的模型。不要宣传优生学”。

弗洛伦斯·南丁格尔,统计学之母,好人。⁴信用:https://en . Wikipedia . org/wiki/file:Florence _ nightingale _(h _ hering _ npg _ x 82368)。jpg
参考
克劳萨贝。弗朗西斯·高尔顿的生活和工作:行为统计学之父的四本新书述评。教育与行为统计杂志。2007;32(4):440–444.doi:10.3102/1076998607307449
https://medium . com/science-and-philosophy/Charles-Darwin-on-racism-slavery-and-eugenics-CB 6416 b 8277 c
Galton。 探究人类的官能及其发展 。麦克米伦。1883.第 24 页。
以色列的⁴charny;阿达连,鲁本·保罗;史蒂文·雅各布斯;埃里克·马库森;马克·谢尔曼(1999 年)。 灭族百科:A-H 。ABC-CLIO。第 218 页。ISBN978–0–87436–928–1。https://imagestore.ucl.ac.uk/imagestore/start/images?
⁵view = preview&fuid = UCL % 20 library/library % 20 events/Galton % 20 exhibit % 20 opening/Galton _ exhb _ 0025 . TIF
⁶pearson、卡尔;莫尔,玛格丽特(1925)。"通过对俄罗斯和波兰犹太儿童的调查来说明外国人移民到英国的问题"。优生学年鉴。一(二):125–126。doi:10.1111/j . 1469–1809.1925 . TB 02037 . x。
卡尔·⁷pearson(1933)。“淡水河谷!”。优生学年鉴。 5 (4): 416。doi:10.1111/j . 1469–1809.1933 . TB 02102 . x。
⁸https://bartlett 100 . com/article/bartlett-buildings-the-Pearson-building . html
⁹https://www.garrickadenbuie.com/blog/lets-move-on-from-iris
⁰https://www . edge . org/conversation/who-is-the-the-great-time 生物学家
沃尔特·博德默(Walter Bodmer),RA Fisher,统计学家兼遗传学家非凡:个人观点,国际流行病学杂志【T51 希拉·费斯·维斯。Isis 2010 101:4,722–758
https://www . newstatesman . com/international/science-tech/2020/07/ra-fisher-and-science-恨意
⁴https://en . Wikipedia . org/wiki/Florence _ nightingale #/media/file:Florence _ nightingale _(h _ hering _ npg _ x 82368)。jpg 。**
数据科学统计学:如何在几周内学会足够的知识
几周的学习可以帮助你从长远来看脱颖而出

照片由来自 Pexels 的 Couleur 拍摄
“你的弱点是什么?”面试官抛出了一个老生常谈的典型问题。
“数据科学的统计学”,我不假思索地回答。
我可以看到他们关切地扬起眉毛。他们没想到会这样。“那你对此做了什么?”他们问,正是我计划的方式。
除非我有一个合适的回答,否则我不会走上那条路。
“如你所知,我来自计算机科学专业。我擅长数学、编程和机器学习。这足以让我进入数据科学领域。”我愣了一下。
“我知道一些基本的统计学,但我没听说过中心极限定理,所以那不算数。在过去的几个月里,我的重点是通过参加在线课程来提高我的统计知识,我必须说我已经取得了巨大的进步。你现在可以问我中心极限定理了。”,我笑着回应。
我最终得到了这份工作,这就是我如何将劣势转化为优势的。我没有撒谎——我在统计学方面确实很弱。在别人指出之前,我自己发现了这一点,这就完全不同了。
如果你正在读这篇文章,很可能你对数据科学的统计数据还没有信心。你甚至可以跳过它,在开始的时候专注于机器学习和编程。我也像你一样,但不一定要这样。
请继续阅读,了解如何在几周内将你的弱点转化为优势。
用 Python 统计数据拯救你
在我意识到自己的弱点后,我进入了秘密学习模式。我想从一所知名大学学习与数据科学相关的统计学。
在回顾了多门课程并听取了具有统计学背景的同事的意见后,我选定了密歇根大学提供的优秀的统计学 Python 专门化课程。
我花了大约 6 周的业余时间来完成这 3 门课程的专业化,但是,嘿,我耐心投入的所有时间和精力都是值得的。我希望我能更早了解它,因为我慢慢地开始理解所有的统计概念。
这门课程包含许多例子、案例研究和练习,对初学者很有帮助。讲座从基础开始,逐渐增加复杂性。它们包括:
- 理解和可视化数据:您需要的所有统计基础知识,如研究设计、数据管理、可视化数据、解释不同类型的数据和数据采样。
- 推理统计学:课程重点是学习使用数据进行估计和评估理论背后的原理。
- 将统计模型与数据拟合:最后一门课程集中于将统计模型与数据拟合的科学和艺术,包括对变量之间的关系进行推断,并为未来的观察结果生成预测。
我觉得这种专业化对于理解行业中常用的统计数据来说已经足够了,你必须在早期就对这些主题有所了解。
只有做到这一点,你才能掌握统计学
除非你能自信地挑战面试官问任何问题,否则你完成课程后获得的证书没有任何用处。
只有当你在现实世界中应用统计数据时,你才会有信心。设计和分析调查,评估假设,提出统计主张等等。
理解这背后的理论是一回事,但将它们应用于现实世界又是另一回事。
有大量的项目和作业作为课程的补充——试一试,不要跳过它们。如果你在工作中无法接触到真实世界的统计密集型项目,这将是一个很好的起点。
对于我的硕士,我创建了一个调查并收集反馈。我分析了这些回答,形成了假设,用统计学意义验证了它们,并做了总结。事实是,一个人不需要注册硕士来做这件事。
除非学以致用,否则无法掌握数据科学的统计学。
这就是你学习足够数据科学统计的方法
你跳过统计数据,因为它似乎不那么重要。你学习所有的数学、编程和机器学习。你建立项目并创建投资组合。你甚至申请工作,最后闯入数据科学。
然后你意识到你的弱点:你对统计学一无所知。你觉得自己像个骗子。在别人注意到它之前,你秘密地开始工作。
你从一流大学挑一门靠谱的网络课程,工作后再学。你通过项目应用你学到的东西,巩固你的概念。你多次重温中心极限定理,以防万一。
当你感到自信时,你去暴露你的弱点,只是为了把它变成一种优势。
我的朋友,这就是你如何在几周内学会足够的统计学。
作为披露,本文中使用了一些附属链接来分享我使用过的最好的资源,并且没有额外的费用。
要获得更多关于进入数据科学、真实体验和学习的有用见解,请考虑 加入我的电子邮件好友私人列表 。
Python 中的统计数据-共线性和多重共线性
了解如何发现数据集中的多重共线性

由 Valentino Funghi 在 Unsplash 上拍摄的照片
在我的上一篇文章中,您了解了数据集中数据之间的关系,无论是在同一列中(方差),还是在列之间(协方差和相关性)。
当您开始机器学习之旅时,您通常会遇到的另外两个术语是:
- 共线性
- 多重共线性
在本文中,我想解释共线性和多重共线性的概念,以及在准备数据时理解它们并采取适当措施的重要性。
相关性与共线性和多重共线性
如果您还记得, correlation 测量数据集中两列之间的强度和方向。相关性通常用于查找特征和目标之间的关系:

作者图片
例如,如果其中一个特征与目标有很高的相关性,它会告诉您这个特定的特征对目标有很大的影响,因此在训练模型时应该包括在内。
另一方面,共线性是两个特征线性相关的情况(高相关性,它们被用作目标的预测值。

作者图片
多重共线性是共线性的一种特殊情况,其中一个要素与两个或多个要素呈现线性关系。

作者图片
共线性和多重共线性问题
回想一下多元线性回归的公式:

作者图片
线性回归的一个重要假设是,每个预测值( x ₁、 x ₂等)与结果 y 之间应该存在线性关系。但是,如果预测值之间存在相关性(例如, x ₁和 x ₂高度相关),您将无法在保持另一个常量的情况下确定其中一个的影响,因为两个预测值会一起变化。最终的结果是系数( w ₁和 w ₂)现在变得不那么精确,因此更难解释。
修复多重共线性
训练机器学习模型时,在数据预处理阶段筛选出数据集中显示多重共线性的要素非常重要。你可以用一种叫做VIF——方差膨胀因子的方法来实现。
VIF 允许你确定各个自变量之间相关性的强度。它是通过取一个变量并将其与其他变量进行回归来计算的。
VIF 计算出一个系数的方差被夸大了多少,因为它与其他预测因子存在线性相关性。因此得名。
VIF 是这样工作的:
- 假设您有一个特征列表— x ₁、 x ₂、 x ₃和 x ₄.
- 你首先取第一个特征, x ₁,并对其他特征进行回归:
x₁ ~ x₂ + x₃ + x₄
实际上,你是在执行上面的多元回归。多元回归通常解释多个自变量或预测变量与一个因变量或标准变量之间的关系。
- 在上面的多元回归中,您提取了 R 值(在 0 和 1 之间)。如果 R 是大,这意味着 x₁ 可以从三个特征中预测出来,因此与三个特征——x₂、 x ₃、 x ₄.高度相关如果 R 是小,这意味着 x₁ 无法从特征中预测,因此与三个特征 x ₂、 x ₃、x₄.不相关
- 基于为 x₁ 计算的 R 值,现在可以使用以下公式计算其 VIF :

作者图片
- 大的 R 值(接近 1)会导致分母变小(1 减去一个接近 1 的值会得到一个接近 0 的数)。这将导致一个大的 VIF。大 VIF 表示此要素与其他要素存在多重共线性。
- 反之,一个小的 R 值(接近 0)会导致分母变大(1 减去一个接近 0 的值会得到一个接近 1 的数)。这将导致一个小 VIF。小 VIF 表示此要素与其他要素的多重共线性较低。
(1- R )又称公差。
- 对其他要素重复上述过程,并计算每个要素的 VIF:
x₂ ~ x₁ + x₃ + x₄ # regress x₂ against the rest of the features
x₃ ~ x₁ + x₂ + x₄ # regress x₃ against the rest of the features
x₄ ~ x₁ + x₂ + x₃ # regress x₄ against the rest of the features
虽然相关矩阵和散点图可用于查找多重共线性,但它们仅显示独立变量之间的二元关系。另一方面,VIF 显示了一个变量与一组其他变量的相关性。
使用 Python 实现 VIF
既然你已经知道了 VIF 是如何计算的,你可以使用 Python 来实现它,在 sklearn 的一点帮助下:
import pandas as pd
from sklearn.linear_model import LinearRegressiondef **calculate_vif**(df, features):
vif, tolerance = {}, {} # all the features that you want to examine
for feature in features:
# extract all the other features you will regress against
X = [f for f in features if f != feature]
X, y = df[X], df[feature] # extract r-squared from the fit
r2 = LinearRegression().fit(X, y).score(X, y)
# calculate tolerance
tolerance[feature] = 1 - r2 # calculate VIF
vif[feature] = 1/(tolerance[feature]) # return VIF DataFrame
return pd.DataFrame({'VIF': vif, 'Tolerance': tolerance})
让我们试一试
为了了解 VIF 的实际情况,让我们使用一个名为 bloodpressure.csv 的样本数据集,其内容如下:
Pt,BP,Age,Weight,BSA,Dur,Pulse,Stress,
1,105,47,85.4,1.75,5.1,63,33,
2,115,49,94.2,2.1,3.8,70,14,
3,116,49,95.3,1.98,8.2,72,10,
4,117,50,94.7,2.01,5.8,73,99,
5,112,51,89.4,1.89,7,72,95,
6,121,48,99.5,2.25,9.3,71,10,
7,121,49,99.8,2.25,2.5,69,42,
8,110,47,90.9,1.9,6.2,66,8,
9,110,49,89.2,1.83,7.1,69,62,
10,114,48,92.7,2.07,5.6,64,35,
11,114,47,94.4,2.07,5.3,74,90,
12,115,49,94.1,1.98,5.6,71,21,
13,114,50,91.6,2.05,10.2,68,47,
14,106,45,87.1,1.92,5.6,67,80,
15,125,52,101.3,2.19,10,76,98,
16,114,46,94.5,1.98,7.4,69,95,
17,106,46,87,1.87,3.6,62,18,
18,113,46,94.5,1.9,4.3,70,12,
19,110,48,90.5,1.88,9,71,99,
20,122,56,95.7,2.09,7,75,99,
数据集由以下字段组成:
- 血压(血压),单位为毫米汞柱
- 年龄,以年为单位
- 重量,单位为千克
- 体表面积( BSA ),单位为 m
- 高血压持续时间( Dur ),年
- 基础脉搏(脉搏),单位为每分钟心跳数
- 应力指数(应力)
首先,将数据集加载到 Pandas 数据框架中,并删除多余的列:
df = pd.read_csv('bloodpressure.csv')
df = df.drop(['Pt','Unnamed: 8'],axis = 1)
df

作者图片
可视化列之间的关系
在进行任何清理之前,使用 pair plot(使用 Seaborn 模块)来可视化各个列之间的关系是很有用的:
import seaborn as sns
sns.pairplot(df)
我发现一些列似乎存在很强的相关性:

作者图片
计算相关性
接下来,使用 corr() 函数计算列之间的相关性:
df.corr()

作者图片
假设您试图建立一个预测血压的模型,您可以看到与血压相关的主要特征是年龄、体重、 BSA 和脉搏:

作者图片
计算 VIF
现在,您已经确定了要用于定型模型的列,您需要查看哪些列具有多重共线性。因此,让我们使用我们之前编写的 calculate_vif() 函数:
calculate_vif(df=df, features=['Age','Weight','BSA','Pulse'])

作者图片
解读 VIF 价值观
VIF 的有效值范围从 1 到无穷大。解释 VIF 价值观的经验法则是:
- 1-要素不相关
- 1
- VIF> 5 —特征高度相关
- VIF>10 —特征之间的高度相关性,值得关注
从上一节计算 VIF 的结果中,可以看到重量和 BSA 的 VIF 值大于 5。这意味着体重和 BSA 高度相关。这并不奇怪,因为较重的人有较大的体表面积。
因此,接下来要做的事情是尝试删除一个高度相关的特征,看看 VIF 的结果是否会改善。让我们试着去掉的重量,因为它的 VIF 更高:
calculate_vif(df=df, features=['Age','BSA','Pulse'])

作者图片
现在让我们去掉 BSA ,看看其他特性的 VIF:
calculate_vif(df=df, features=['Age','Weight','Pulse'])

作者图片
正如您所观察到的,与移除 BSA 相比,移除重量会导致所有其他特性的 VIF 降低。那么你应该去掉的重量吗?嗯,理想情况下,是的。但出于实际原因,移除 BSA 并保留重量会更有意义。这是因为稍后当模型被训练并用于预测时,获得患者的体重比他/她的体表面积更容易。
再举一个例子
让我们再看一个例子。这次您将使用 sklearn 附带的乳腺癌数据集:
from sklearn import datasets
bc = datasets.load_breast_cancer()df = pd.DataFrame(bc.data, columns=bc.feature_names)
df

作者图片
这个数据集有 30 列,所以我们只关注前 8 列:
sns.pairplot(df.iloc[:,:8])

作者图片
您可以立即观察到一些特征高度相关。你能认出他们吗?
让我们计算前 8 列的 VIF:
calculate_vif(df=df, features=df.columns[:8])

作者图片
您可以看到以下要素具有较大的 VIF 值:

作者图片
让我们试着一个一个地移除这些特征,并观察它们的新 VIF 值。首先,移除平均周长:
calculate_vif(df=df, features=['mean radius',
'mean texture',
'mean area',
'mean smoothness',
'mean compactness',
'mean concavity',
'mean concave points'])

作者图片
VIFs 立即全面下降。现在让我们移除平均面积:
calculate_vif(df=df, features=['mean radius',
'mean texture',
**# 'mean area',**
'mean smoothness',
'mean compactness',
'mean concavity',
'mean concave points'])

作者图片
现在让我们去掉意思是凹点,其中 VIF 最高:
calculate_vif(df=df, features=['mean radius',
'mean texture',
**# 'mean area',**
'mean smoothness',
'mean compactness',
'mean concavity',
**# 'mean concave points'**
])

作者图片
最后,让我们去掉平均凹度:
calculate_vif(df=df, features=['mean radius',
'mean texture',
**# 'mean area',**
'mean smoothness',
'mean compactness',
**# 'mean concavity',**
**# 'mean concave points'**
])

作者图片
现在所有的 VIF 值都低于 5。
摘要
在本文中,您了解了相关性、共线性和多重共线性之间的区别。特别是,您了解了当一个要素与两个或多个要素呈现线性关系时会发生多重共线性。要检测多重共线性,一种方法是计算方差膨胀因子 ( VIF )。应将 VIF 大于 5 的任何要素从训练数据集中移除。
值得注意的是,VIF 只对连续变量有效,对分类变量无效。
https://weimenglee.medium.com/membership
Python 中的统计—在 Python、NumPy 和 sklearn 中生成随机数
要在 Python 中生成随机数,您只需知道

纳赛尔·塔米米在 Unsplash 上的照片
生成随机数是编写应用程序时需要执行的常见任务之一。随机数有许多用途——从密码学到机器学习,随机数在使我们的应用程序正确工作方面发挥着极其重要的作用。
作为一名 Python 程序员,当涉及到生成随机值时,您有太多的选择,因为有太多的方法可以做到这一点。然而,这种灵活性是有代价的——通常不太清楚何时使用什么。这正是我在这篇文章中想要解决的问题。
在本文结束时,您将会对使用什么函数来生成您想要的随机数有一个更加清晰的了解。
用 Python 生成随机数
要在 Python 中生成随机数,可以使用 random 模块:
import random
要生成浮点随机数,请调用 random() 函数:
**random.random**() # e.g. 0.49543508709194095
random() 函数在半开区间—【0,1】产生一个浮点数。这意味着生成的数字将从 0 到 1(其中不包括 1)。
如果您想生成一个从 0 到 10(包括 0 和 10)的整数值,请使用 randint() 函数:
**random.randint**(0,10) # [0,10] - 0 to 10 (inclusive) e.g. 6
如果您想要一个特定范围内的随机浮点数(例如 1 到 5(不含)),使用 uniform() 函数:
**random.uniform**(1,5) # [1,5) e.g. 4.756596651114043
要生成特定范围内的随机整数值列表(如 0 到 32(不含))而不重复值,使用 sample() 函数:
**random.sample**(range(0, 32), 5) # result is a list of 5 values
# from [0,32) with no repeats
# [12, 15, 26, 10, 7]
对于像幸运抽奖这样需要从一个值列表中挑选一些获胜者的情况,函数 sample() 非常有用。
如果想要一个半开区间内随机浮点值的列表,可以通过 list comprehension 使用 random() 函数:
**[random.random() for _ in range(5)]** # [0, 1)
# [0.26800994395551214,
# 0.3322334781304659,
# 0.5058884832347348,
# 0.2552912262686192,
# 0.33885158106897195]
同样,如果您需要特定范围内的随机浮点值列表,您可以这样做:
**[random.uniform(1,5) for _ in range(5)] ** # [1, 5)
# [1.4556516495709206,
# 1.94075804553687,
# 4.775979596495107,
# 4.118159382173641,
# 3.860434558608088]
最后,如果你需要生成一个随机整数列表,使用这个:
**[random.randint(0,10) for _ in range(5)]** # [0,10]
# [3, 9, 8, 7, 10]
使用 NumPy 生成随机数
如果您正在使用 NumPy 模块,您也可以使用它来生成随机数。random 模块包含几个函数,允许你生成随机数。
uniform() 函数在半开区间内生成一个浮点数:
import numpy as np**np.random.uniform()** # [0,1) e.g. 0.6603742810407641
您也可以指定范围:
**np.random.uniform(1,5)** # [1,5) e.g. 2.1809140016758803
以及要生成的随机值的数量:
**np.random.uniform(1,5,8)** #[1,5) x 8
# array([3.15101237, 3.52431302, 2.43564056, 4.22373224,
# 1.82549706, 4.30782957, 2.1383488 , 3.71130947])
您也可以指定想要的结果形状:
**np.random.uniform(1,5,(2,4))** # [1,5) - result is a 2D array of
# 2 rows and 4 columns
# array([[4.85777402, 2.41464442, 3.47972032, 3.61706258],
# [1.39591689, 2.41386733, 3.34813041, 3.13411887]])
如果只是想在半开区间生成数字,还有一个函数可以用——rand():
**np.random.rand()** # [0,1) e.g. 0.11705786929477491
rand() 函数可以轻松生成各种维度的半开区间值:
**np.random.rand(5)** # [0,1) x 5
# array([0.52310231, 0.87305847, 0.03870784, 0.69239079, 0.47626848])**np.random.rand(2,3)** # [0,1) in 2D
# array([[0.16926449, 0.06317189, 0.03222409],
# [0.24243086, 0.11270682, 0.40499002]])
rand() 函数接受额外的参数作为返回结果的形状,而 uniform() 函数接受三个参数——低、高和大小。
另一个类似于 rand() 函数的函数是 random() 。它也在半开区间产生数字。这两者之间的关键区别在于, random() 函数接受一个参数来表示要生成的数字的维数。
**np.random.random(5)**
# array([0.90351056, 0.96734226, 0.06753921,
# 0.31758607, 0.69686297])**np.random.random((2,3))** # passed the dimension you want
# as a tuple
# array([[0.04207297, 0.92656545, 0.93526291],
# [0.8104269 , 0.18834308, 0.58731822]])
random() 和 uniform() 的区别在于 random() 函数接受一个参数。所以如果要生成多维数组结果,就需要把形状包装成元组。
如果你需要一个随机的整数值,使用 randint() 函数:
**np.random.randint(0,9)** # [0,9) e.g. 7
您也可以生成多维的整数值列表:
**np.random.randint(0,9,5)** # [0,9) x 5
# array([3, 7, 3, 2, 8])**np.random.randint(0,9,(4,5))** # [0,9) in 2D array
# array([[5, 2, 4, 8, 0],
# [5, 2, 3, 7, 2],
# [6, 1, 2, 4, 7],
# [2, 3, 5, 8, 4]])
指定数字的分布
到目前为止,我们生成的所有数字都是均匀分布的。
均匀分布是一种连续的概率分布,与同样可能发生的事件有关。
这意味着,如果您试图生成大量的值,那么生成任何值的机会应该是均等的。您可以通过尝试使用 random() 函数生成一百万个值,然后将数字范围划分为 25 个区间,并计算每个区间中每个值的出现次数来看到这一点:
import matplotlib.pyplot as plt
_ = plt.hist(np.random.random(1_000_000), bins = 25)
上面的语句显示了一个直方图,显示了一百万个数字的分布情况:

如果你想生成一个正态分布的数字列表,你可以使用 randn() 函数:
**np.random.randn(4,3)**
# array([[-0.58617287, 0.99765344, 1.00439116],
# [-0.45170132, -0.01265149, 0.75739522],
# [ 0.70970036, -0.1740791 , 1.14584093],
# [ 1.2637344 , 0.77962903, -0.97546801]])
正态分布,又称高斯分布,是一种关于均值对称的概率分布,大部分数据都聚集在均值附近。在图表上,正态分布显示为钟形曲线。
randn() 函数返回来自标准正态分布的样本值。在上面的代码片段中, randn() 函数返回 2D 数组中的结果。
标准正态分布是均值为零、标准差为 1 的正态分布。对于标准正态分布,68.27%的观察值位于均值的 1 个标准差以内;95.45%位于平均值的两个标准差之内;99.73%位于平均值的 3 个标准偏差内。

以下示例生成正态分布的一百万个数字,然后用于绘制一个直方图,将这些数字分成 50 个区间:
_ = plt.hist(**np.random.randn(1_000_000)**, bins=50)
您应该会看到如下所示的内容:

播种你的随机数生成器
关于随机数的讽刺之处在于它们并不是真正随机的。相反,Python 中的随机数生成器使用当前时间来生成随机数,因为每次运行代码来生成随机数时,时间都会改变,所以您会认为这些数字是真正随机的。但这不是我们大多数人关心的问题。相反,出于再现性的原因,我们经常希望确保生成的随机数是相同的,这样我们就可以在分析中始终得到相同的结果。
如果您用 Python 生成随机数,请使用 seed() 函数,传递一个整数值:
**random.seed(1) # pass in an integer value as the seed**
random.sample(range(0, 32), 5)
# [8, 4, 16, 7, 31]
上面的代码片段将总是生成相同的随机数列表。
如果使用 NumPy,使用 random.seed() 函数:
**np.random.seed(2)** **# pass in an integer value as the seed**
np.random.uniform(0, 10, 5)
# [4.35994902 0.25926232 5.49662478 4.35322393 4.20367802]
NumPy 还附带了 RandomState 类,您可以使用随机种子创建它的一个实例,然后使用它来生成不同类型的随机值:
r = **np.random.RandomState(1) # pass in an integer value as the
# seed**print(r.uniform(0, 10, 5)) # [0,10)
# [4.17022005e+00 7.20324493e+00 1.14374817e-03 3.02332573e+00
# 1.46755891e+00]print(r.rand(2,3)) # [0,1)
# [[0.09233859 0.18626021 0.34556073]
# [0.39676747 0.53881673 0.41919451]]
使用 sklearn 生成随机数
除了生成均匀分布或正态分布的随机数之外,有时还需要生成线性分布或围绕特定质心聚集的数。例如,您可能想要使用一组点来尝试线性回归,或者想要尝试一些用于无监督学习的聚类算法。
生成线性分布的随机数
您可以利用sk learn . datasets模块中的 make_regression() 函数生成一组线性分布的点:
from sklearn.datasets import **make_regression**
import numpy as npx, y = **make_regression**(n_samples=100, n_features=1, noise=12.3)
n_samples 参数指定要生成多少个数字, n_features 指定要生成的列数, noise 表示应用于数字的标准偏差(它们分散了多少)。上面的代码片段将产生如下所示的输出:
**print(x)**
# [[ 1.20630427]
# [-1.02041981]
# ...
# [-0.95098556]
# [ 0.09247152]]**print(y)**
# [ 66.34055577 -52.39063718 51.46433162 -12.56089116
# 10.62491393 8.00035735 4.80360232 -28.99765946
# ...
# 12.75554229 9.75147261 2.67890648 -32.4981596
# -30.16046261 -4.56704054 -43.56250488 -9.30790306]
理解这些数字的更好方法是绘制一个散点图:
import matplotlib.pyplot as plt
_ = plt.scatter(x, y)

如果将噪波修改为更大的值:
x, y = make_regression(n_samples=100, n_features=1, **noise=19**)
_ = plt.scatter(x, y)
您将看到这些值现在更加分散:

如果把 n_features 改成 2 呢?在这种情况下, X 将是一个 2D 阵列:
**X**, y = make_regression(n_samples=1000, **n_features=2**, noise=3)
**print(X)**
# [[-0.10171443 1.59563406]
# [ 0.39154137 -0.21477808]
# [ 0.00732151 0.24783439]
# ...
# [-0.62820116 0.16688806]
# [-0.35656323 -1.1761519 ]
# [ 0.04589981 0.59696238]]
可视化生成的随机数集合的一个好方法是使用 scatter3D() 函数绘制一个 3D 散点图:
from sklearn.datasets import make_regression
import numpy as np
import matplotlib.pyplot as pltX, y = make_regression(n_samples=1000, n_features=2, noise=3)fig = plt.figure(figsize=(13,13))
ax = plt.axes(projection='3d')**ax.scatter3D(X[:,0], X[:,1], y, c=y, cmap='Greens')**
ax.set_xlabel('X[0]')
ax.set_ylabel('X[1]')
ax.set_zlabel('y')
plt.show()
Y 您应该将上面的代码片段保存在一个名为 random_regression.py 的文件中,并在命令提示符下运行它。然后,您将能够通过旋转它来可视化绘图。
以下是从不同角度看的情节:



对生成的随机数进行插值
由 make_regression() 生成的值可能不在您想要的范围内。例如,如果您想要生成一组点来显示一组人的身高和体重之间的关系。在这种情况下,您希望身高在 148cm 到 185cm 之间,体重在 44kg 到 74kg 之间。以下代码片段使用 NumPy 中的 interp() 函数缩放 x 和 y 值:
x, y = make_regression(n_samples=100, n_features=1, noise=2.6)# scale x (e.g. height in cm) to 148..185 range
x = np.interp(x, (x.min(), x.max()), (148, 185))# scale y (e.g. weight in kg) to 44..74 range
y = np.interp(y, (y.min(), y.max()), (44, 74))plt.scatter(x, y)
散点图证实了执行的插值:

生成围绕质心聚集的随机数
很多时候,当你进行无监督学习时,你需要生成围绕几个质心的随机点。为此,您可以使用 sklearn.datasets 模块中的 make_blobs() 函数:
from sklearn.datasets import make_blobsX, y = make_blobs(n_samples = 500,
centers = 3,
n_features = 2)
上面的代码片段返回 500 对随机数(包含在 X 中), y 包含每个点所在的类:
print(X)
# [[ -9.86754851 9.27779819]
# [-11.50057906 8.88609894]
# ...
# [ -5.96056302 -3.21866963]
# [-10.38173377 8.82254368]]print(y)
# [2 2 0 1 2 2 0 1 2 1 1 1 0 2
# 1 1 2 1 1 1 2 2 0 1 1 1 1 1
# ...
# 2 0 0 0 2 0 1 0 2 2 1 2 1 2
# 2 1 2 2 1 1 1 0 0 0 2 1 2 1]
像往常一样,视觉化总是让事情变得更加清晰:
rgb = np.array(['r', 'g', 'b'])# plot the blobs using a scatter plot and use color coding
_ = plt.scatter(X[:, 0], X[:, 1], color=rgb[y])
plt.xlabel('X[0]')
plt.ylabel('X[1]')

3D 点怎么样?当然,只需将 n_features 设置为 3,并使用 scatter3D() 函数绘图:
from sklearn.datasets import make_blobs
import numpy as np
import matplotlib.pyplot as pltX, y = make_blobs(n_samples = 1500,
centers = 3,
**n_features = 3**)fig = plt.figure(figsize=(13,13))
ax = plt.axes(projection='3d')
ax.scatter3D(**X[:,0], X[:,1], X[:,2]**, c = y, cmap = 'tab20b')
ax.set_xlabel('X[0]')
ax.set_ylabel('X[1]')
ax.set_zlabel('y')plt.show()
Y 你应该将上面的代码片段保存在一个名为 random_blobs.py 的文件中,并在命令提示符下运行它。然后,您将能够通过旋转它来可视化绘图。

为了再现性,将随机状态参数设置为一个值:
X, y = make_blobs(n_samples = 1500,
centers = 3,
n_features = 3,
**random_state = 0**)
摘要
唷,看起来在 Python 中有很多不同的方法来生成随机数。最好的记忆方法是用它来参考下面这篇文章中我们讨论过的函数的总结。

我错过了什么重要的功能吗?请在评论中告诉我!
Python 中的统计学-了解方差、协方差和相关性
理解数据之间的关系,知道皮尔逊相关系数和斯皮尔曼等级相关系数之间的区别

数据科学家必须了解的一个主题是数据集中存在的关系。在开始机器学习过程之前,准备好数据以便只有数据集的相关部分用于训练是至关重要的。要了解数据集中的关系,您需要了解以下概念:
- 差异
- 协方差
- 相互关系
和往常一样,我的目的是让你容易消化这些话题。我们开始吧!
创建样本数据集
为了理解数据集中的关系,让我们创建一个简单的关系并加载到 Pandas 数据框架中:
import pandas as pd
import numpy as npdf = pd.DataFrame({
'a':[1,3,4,6,8],
'b':[2,3,5,6,8],
'c':[6,5,4,3,2],
'd':[5,4,3,4,6]
})
df
数据帧包含五行和四列:

差异
方差是数据集中的值围绕其平均值的分布。它告诉你数据集中的每个数字离它的平均值有多远。方差( s )的公式定义如下:

作者图片
F 或样本方差,分母为 n-1 。对于人口方差,分母为 n 。
方差 ( s )的平方根就是标准差 ( s )。方差的计算方法是,取数据集中每个数字与平均值的差,对所有差求和,最后除以数据集中值的个数。
较大的方差表明数据集中的数字远离平均值,并且彼此远离。另一方面,较小的方差表明这些数字接近平均值并且彼此接近。方差为 0 表示数据集中的所有数字都相同。最后,方差的有效值总是正数(0 或更大)。
通常,能够可视化数据集中数字的分布是很有用的,这样您可以更好地理解方差的概念。
使用 Seaborn,您可以绘制一个带状图和一个盒状图来显示列 a 到 d 中的数字分布:
import seaborn as snsg = sns.stripplot(data = df.melt(),
x = 'variable',
y = 'value',
color = 'red')sns.boxplot(data = df.melt(),
x = 'variable',
y = 'value',
color = 'yellow')

作者图片
如您所见,列 a 中的值比其他列分散得多,同样,列 b 中的值比 b 和 c 中的值分散得多,依此类推。与其余列相比, d 中的值是最接近的分组。因此,你会期望 a的方差最大,d的方差最小。****
使用 NumPy 计算方差
使用 NumPy,很容易计算一系列数字的方差。下面是根据前面看到的公式计算列 a 的方差的语句:
(np.square(df['a'] - df['a'].mean())).sum() / (df.shape[0] - 1)
# 7.3
然而,NumPy 也有 var() 函数来计算一个数组的方差。您可以直接将数据帧传递给 var() 函数来计算数据帧中一系列列的方差:
**np.var(df[['a','b','c','d']], ddof=1)**
# a 7.3
# b 5.7
# c 2.5
# d 1.3
# dtype: float64
ddof 代表 Delta 自由度。该值用于方差计算的分母( n — ddof ),其中 n 代表元素的数量。默认情况下,ddof 为零(总体方差)。当的DD 设置为 1 时,您正在计算样本方差。
正如所料,您可以看到列 a 的方差最大,列 d 的方差最小。
协方差
既然您已经看到了每一列的差异,现在是时候看看列之间是如何相互关联的了。方差测量数据在其均值内的分布,协方差测量两个随机变量之间的关系。
在统计学中,协方差是两个随机变量之间方向关系的度量。
让我们绘制一个散点图,看看数据框架中的列是如何相互关联的。我们将首先从 a 和 b 列开始:
import matplotlib.pyplot as plt
plt.scatter(df['a'], df['b'])
plt.xlabel('a')
plt.ylabel('b')

作者图片
正如你所看到的,在 a 和 b 之间似乎有一种趋势——随着 a 增加, b 也增加。
在统计学中,已知 a 和 b 有一个正协方差。正协方差表示两个随机变量倾向于同时向上或向下移动。
列 b 和 c 怎么样?让我们看看:
plt.scatter(df['b'], df['c'])
plt.xlabel('b')
plt.ylabel('c')

作者图片
这一次,趋势似乎向相反的方向发展——随着 b 增加, c 减少。
在统计学中, b 和 c 已知具有负协方差。负协方差表示两个变量倾向于彼此远离,当一个向上移动时,另一个向下移动,反之亦然。
最后,让我们检查列 c 和 d :
plt.scatter(df['c'], df['d'])
plt.xlabel('c')
plt.ylabel('d')

作者图片
c 和 d 之间似乎不存在直接的线性关系。
在统计学中, c 和 d 已知有零协方差(或接近零)。当两个随机变量独立时,协方差将为零。然而,反过来就不一定了——协方差为零并不意味着两个随机变量是独立的(在协方差为零的两个随机变量之间仍然存在非线性关系)。在上面的例子中,你可以看到存在某种非线性的 v 形关系。
数学上,协方差的公式定义如下:

作者图片
计算两个随机变量之间的协方差的方法是,取每个随机变量的值与其平均值之间的差值的乘积,对所有乘积求和,最后除以数据集中值的数量。
像往常一样,让我们使用 NumPy 手动计算 a 和 b 之间的协方差:
#---covariance for a and b---
((df['a'] - df['a'].mean()) * (df['b'] - df['b'].mean())).sum() / (df.shape[0] - 1)
# 6.35
像方差一样,NumPy 有 cov() 函数来直接计算两个随机变量的协方差:
np.cov(df['a'],df['b'])
# array([[7.3 , 6.35],
# [6.35, 5.7 ]])
cov() 函数的输出是一个包含以下值的 2D 数组:

作者图片
在这种情况下,a 和 b 的协方差是 6.35(正协方差)。
以下是 b 和 c 的协方差(-3.75,负协方差):
np.cov(df['b'], df['c'])
# array([[ 5.7 , -3.75],
# [-3.75, 2.5 ]])
以及 c 和 d 的协方差(-0.5,负协方差):
np.cov(df['c'], df['d'])
# array([[ 2.5, -0.5],
# [-0.5, 1.3]])
虽然协方差测量 2 个随机变量之间的方向关系,但它不显示 2 个随机变量之间关系的强度。其值不受约束,可以从-无穷大到+无穷大。
此外,协方差取决于值的比例。例如,如果您将列 a 和 b 中的每个值加倍,您将获得不同的协方差:
np.cov(df['a']*2, df['b']*2)
# array([[29.2, **25.4**],
# [**25.4**, 22.8]])
测量两个随机变量强度的一个更好的方法是相关性,我们将在接下来讨论。
Cor 关系
两个随机变量之间的相关性测量它们之间存在的线性关系的强度和方向。有两种方法来衡量相关性:
- 皮尔逊相关系数 —捕捉两个连续变量之间的线性关联的强度和方向
- Spearman 等级相关系数—确定两个有序(分类)或连续变量之间存在的单调关系的强度和方向。
皮尔逊相关系数
皮尔逊相关系数的公式为:

作者图片
皮尔逊相关系数定义为 x 和 y 的协方差除以每个随机变量的标准差的乘积。
将公式中的协方差和标准偏差 x 和 y 代入,得到:

作者图片
简化后,公式现在看起来像这样:

作者图片
Pandas 有一个函数 corr() 计算数据帧中各列的相关性:
df[['a','b']].corr()
结果是:

作者图片
对角线值为 1 表示每列与其自身的相关性。显然, a 与 a 本身的相关性为 1,对于列 b 也是如此。 0.984407 的值是 a 和 b 的皮尔逊相关系数。
b 和 c 的皮尔逊相关系数为 -0.993399 :
df[['b','c']].corr()

作者图片
c 和 d 的皮尔逊相关系数为 -0.27735 :
df[['c','d']].corr()

作者图片
像协方差一样,皮尔逊相关系数的符号表示关系的方向。然而,皮尔逊相关系数的值被限制在-1 和 1 之间。根据该值,您可以推导出以下相关度:
- 完美 —接近 1 的值
- 高度数 —介于 0.5 和 1 之间的数值
- 中度 —介于 0.3 和 0.49 之间的值
- 低度 —低于 0.29 的值
- 无相关性 —数值接近 0
从上面的结果可以看出, a,b ,和 b,c 的相关度很高,而 c,d 的相关度很低。
了解数据集中各列之间的相关性是为机器学习准备数据过程中的一个重要部分。您希望使用与数据集标签相关性最高的列来定型您的模型。
与协方差不同,相关性不受值的比例影响。作为实验,将列 a 和 b 相乘,您会发现它们的相关性:
df['2a'] = df['a']*2 # multiply the values in a by 2
df['2b'] = df['b']*2 # multiply the values in b by 2
df[['2a','2b']].corr() # the result is the same as
# df[['a','b']].corr()
结果与 a 和 b 相同:

作者图片
斯皮尔曼等级相关系数
如果你的数据不是线性分布,你应该用 Spearman 的秩相关系数而不是 Pearson 相关系数。****斯皮尔曼秩相关系数是为单调的分布设计的。
在代数中,一次函数是一个梯度从不改变符号的函数。换句话说,它是一个要么总是增加,要么总是减少的函数。接下来的前两个数字是单调的,而第三个不是(因为梯度从左到右改变几次符号)。

来源:https://en.wikipedia.org/wiki/Monotonic_function
斯皮尔曼等级相关系数的公式为:

作者图片
其中 d 是两个随机变量之间的等级差。举个例子就清楚了。
对于本例,我将有另一个数据帧:
df = pd.DataFrame({
'math' :[78,89,75,67,60,58,71],
'science':[91,92,90,80,60,56,84]
})
df

首先将数据可视化是有用的:
plt.scatter(df['math'], df['science'])
plt.xlabel('math')
plt.ylabel('science')

作者图片
这看起来像是单调分布。下一步是在 Pandas 中使用 rank() 函数对分数进行排序:
df['math_rank'] = df['math'].rank(ascending=False)
df['science_rank'] = df['science'].rank(ascending=False)
df
现在,您有两个额外的列包含每个科目的排名:

作者图片
让我们再创建两个新列来存储等级与其平方值之间的差异:
df['diff'] = df['math_rank'] - df['science_rank']
df['diff_sq'] = np.square(df['diff'])
df

作者图片
现在,您可以使用之前定义的公式计算斯皮尔曼等级相关系数:
n = df.shape[0]
p = 1 - ((6 * df['diff_sq'].sum()) / (n * (n**2 - 1)))
p # 1.0
你会得到完美的 1.0 分!当然,为了省去手工计算斯皮尔曼秩相关系数的所有工作,您可以使用 corr() 函数,并为方法参数指定“斯皮尔曼”:
df[['math','science']].corr(method='spearman')

作者图片
如果我们用皮尔逊相关系数来计算相关性会怎么样?
df[['math','science']].corr(method='pearson')
您会得到以下内容:

作者图片
请注意,我上面列出的 Spearman 等级相关系数公式是针对不同等级的情况(意味着数学或科学分数没有关系)。在等级相同的情况下,公式稍微复杂一点。为了简单起见,我将不进入并列等级的公式。函数 corr() 将自动处理并列的等级。
你应该使用哪种方法?皮尔逊或斯皮尔曼的
那么应该用哪种方法呢?请记住以下几点:
- 皮尔逊相关描述了 线性 关系,而斯皮尔曼相关描述了 单调 关系
- 散点图将有助于可视化数据,如果分布是线性的,请使用皮尔逊相关。如果是单调的,使用 Spearman 相关。
- 您还可以同时应用这两种方法,并检查哪种方法执行得好。例如,如果结果显示 spearman 等级相关系数大于 Pearson 系数,则意味着您的数据具有单调关系,而不是线性关系(就像上面的示例)。
总结
我希望你现在对方差、协方差和相关性的概念有了更清晰的认识。特别是相关性让你知道你的随机变量之间关系的强度和方向,你可以利用皮尔逊相关系数(对于线性关系)或斯皮尔曼等级相关系数(对于单调关系)方法。
https://weimenglee.medium.com/membership
Python 中的统计数据-使用方差分析进行要素选择
了解如何使用 ANOVA 比较分类变量和数值变量

Gabriel Gurrola 在 Unsplash 上拍摄的照片
在我的上一篇文章中,我谈到了使用卡方统计从数据集中选择用于机器学习的特征。当自变量和因变量都是分类变量时,使用卡方检验。但是,如果你的自变量是分类,因变量是数值呢?在这种情况下,您必须使用另一种称为 ANOVA 的统计测试— 方差分析。
因此,在本文中,我们的讨论将围绕方差分析以及如何在机器学习中使用它进行特征选择。和我之前所有的文章一样,我将用一个具体的例子来解释这个概念。
在我们开始之前,总结一下我们到目前为止讨论过的不同的特性选择方法是很有用的:

作者图片
如果你需要复习一下皮尔森相关性、斯皮尔曼等级相关性和卡方,我建议你现在就去查阅它们(见下面的链接),一旦你完成了就回来看这篇文章。本文中讨论的一些概念类似于卡方检验,所以我建议您查看一下。
什么是方差分析?
ANOVA 用于测试两个变量,其中:
- 一个是分类变量
- 另一个是数值变量
当分类变量有至少 3 组(即三个不同的唯一值)时,使用 ANOVA。
如果你只想比较两组,就用 t 检验。我将在另一篇文章中介绍 t-test。
方差分析让你知道你的数值变量是否根据分类变量的水平而变化。
ANOVA 使用 f 检验对平均值的相等性进行统计检验。F-tests 以它的测试统计量 F 命名,F 是为了纪念罗纳德·费雪爵士而命名的。
这里有一些例子,可以让你更容易理解什么时候可以使用方差分析。
- 您有一个数据集,其中包含一组人的社交媒体使用情况和睡眠时间信息:

作者图片
你想知道社交媒体的使用量(分类变量)是否对睡眠时间(数字变量)有直接影响。
- 您有一个数据集,其中包含三种不同品牌的药物以及药物生效的天数:

作者图片
你想找出一个特定的品牌和它的有效性之间是否有直接的关系。
ANOVA 检查关于数字响应的分类特征组之间是否有相等的方差。
如果各组之间的方差相等,这意味着该特征对反应没有影响,因此它(分类变量)不能被考虑用于模型训练。
用手演奏阿凡纳
理解方差分析的最好方法是用一个例子。在下面的例子中,我使用了一个虚构的数据集,记录了一组人在喝某种饮料时的反应时间。
样本数据集
我有一个名为 drinks.csv 的样本数据集,包含以下内容:
team,drink_type,reaction_time
1,water,14
2,water,25
3,water,23
4,water,27
5,water,28
6,water,21
7,water,26
8,water,30
9,water,31
10,water,34
1,coke,25
2,coke,26
3,coke,27
4,coke,29
5,coke,25
6,coke,23
7,coke,22
8,coke,27
9,coke,29
10,coke,21
1,coffee,8
2,coffee,20
3,coffee,26
4,coffee,36
5,coffee,39
6,coffee,23
7,coffee,25
8,coffee,28
9,coffee,27
10,coffee,25
总共有 10 个小组——每个小组由 3 人组成。团队中的每个人都有三种不同的饮料——水、可乐和咖啡。喝完饮料后,他们被要求进行一些活动,并记录他们的反应时间。这个实验的目的是确定饮料是否对人的反应时间有任何影响。
让我们首先将数据集加载到熊猫数据框架中:
import pandas as pd
df = pd.read_csv('drinks.csv')
记录观察尺寸,我们稍后会用到:
observation_size = df.shape[0] # number of observations

作者图片
可视化数据集
使用箱线图直观显示数据的分布非常有用:
_ = df.boxplot('reaction_time', by='drink_type')

作者图片
你可以看到这三种饮料的平均反应时间差不多。
旋转数据框
为了便于计算方差分析,我们需要旋转数据框:
df = df.pivot(columns='drink_type', index='team')
display(df)

作者图片
列代表三种不同类型的饮料,行代表 10 个队。我们还将利用这个机会记录每组中的个项目,以及稍后将使用的组个项目:
n = df.shape[0] # 10; number of items in each group
k = df.shape[1] # 3; number of groups

作者图片
定义假设
您现在定义您的零假设和替代假设,就像卡方检验一样。它们是:
- H₀ (零假设)——组间均值无差异。
- H₁ (替代假设)——至少有一组显著不同于因变量的总体均值。
步骤 1 —计算所有组的平均值
我们现在准备开始方差分析的计算。首先,让我们找出每组的平均值:
df.loc['Group Means'] = df.mean()
df

作者图片
从这里,你现在可以计算出总的意思是:
overall_mean = df.iloc[-1].mean()
overall_mean # 25.666666666666668

作者图片
第二步——计算平方和
现在我们已经计算出了的总体平均值,我们可以继续计算如下:
- 所有观察值的平方和— SS_total
- 范围内的平方和—SS _ 范围内
- 之间的平方和— SS_between
所有观察值的平方和— SS_total
所有观察值的平方和的计算方法是从总平均值中减去每个观察值,然后将所有差值的平方和相加:

作者图片

作者图片
通过编程, SS_total 计算如下:
SS_total = (((df.iloc[:-1] - overall_mean)**2).sum()).sum()
SS_total # 1002.6666666666667
范围内的平方和— SS_within
在中的平方和是他们组平均值周围分数的方差之和:

作者图片
通过编程, SS_within 计算如下:
SS_within = (((df.iloc[:-1] - df.iloc[-1])**2).sum()).sum()
SS_within # 1001.4
之间的平方和— SS_between
接下来,我们根据总体平均值计算组平均值的平方和:

作者图片

作者图片
通过编程, SS_between 计算如下:
SS_between = (n * (df.iloc[-1] - overall_mean)**2).sum()
SS_between # 1.266666666666667
您可以验证:
SS _ total=SS _ between+SS _ within
创建方差分析表
计算完所有值后,您现在可以完成 ANOVA 表。回想一下,您有以下变量:

作者图片
您可以如下计算各种自由度:
df_total = observation_size - 1 # 29
df_within = observation_size - k # 27
df_between = k - 1 # 2
根据上述内容,计算各种均方值:
mean_sq_between = SS_between / (k - 1) # 0.6333333333333335
mean_sq_within = \
SS_within / (observation_size - k) # 37.08888888888889
最后,您可以计算 F 值,它是两个方差的比值:
F = mean_sq_between / mean_sq_within # 0.017076093469143204
回想一下,我之前提到过 ANOVA 使用 f 检验来统计检验均值的相等性。
一旦获得 F 值,你现在必须参考 f 分布表(例如参见http://www.socr.ucla.edu/Applets.dir/F_Table.html)来获得 f 临界值。f 分布表是根据 α 值(通常为 0.05)组织的。因此,您需要首先根据 α= 0.05 定位表格:

来源:http://www.socr.ucla.edu/Applets.dir/F_Table.html
接下来,观察 f 分布表的列基于 df1 ,而行基于 df2 。你可以从我们之前创建的变量中得到你的 df1 和 df2 :
df1 = df_between # 2
df2 = df_within # 27
使用 df1 和 df2 的值,您现在可以通过定位 df1 列和 df2 行来定位f-临界值:

来自 http://www.socr.ucla.edu/Applets.dir/F_Table.html 的表格;作者注释
从上图可以看出f-临界值为 3.3541 。使用该值,您现在可以使用 F 分布曲线决定是接受还是拒绝零假设:

作者图片
由于 f 值 (0.0171,这是我们可以计算的)小于 f 分布表中的 f 临界值,我们接受零假设— 这意味着不同组中没有方差—所有均值都相同。
对于机器学习来说,这个特征——饮料 _ 类型,应该 而不是 被包括在训练中,因为不同类型的饮料似乎对反应时间没有影响。
只有当您拒绝零假设时,您才应该包括一个用于训练的特征,因为这意味着饮料类型中的值对反应时间有影响。
使用统计模块计算 f 分数
在上一节中,我们手动计算了数据集的 f 值。实际上,有一种更简单的方法—使用 stats 模块的 f_oneway() 函数来计算 f 值和 p 值:
import scipy.stats as stats
fvalue, pvalue = stats.f_oneway(
df.iloc[:-1,0],
df.iloc[:-1,1],
df.iloc[:-1,2])print(fvalue, pvalue) # 0.0170760934691432 0.9830794846682348
f_oneway() 函数将组作为输入,并返回方差分析 F 和 p 值:

作者图片
上图中 f 值为 0.0170760934691432 (与我们手动计算的相同) p 值为 0.9830794846682348 。
注意到 f_oneway() 函数接受可变数量的参数:

作者图片
如果有很多组,那么逐个传递所有组的值会非常繁琐。所以,有一个更简单的方法:
fvalue, pvalue = stats.f_oneway(
***df.iloc[:-1,0:3].T.values** )
我将把上面的内容作为一个练习,让你理解它是如何工作的。
使用 statsmodels 模块计算 f 分数
另一种计算 f 值的方法是使用 statsmodel 模块。您首先使用 ols() 函数构建模型,然后在模型的实例上调用 fit() 函数。最后,在拟合的模型上调用 anova_lm() 函数,并指定要对其执行的 anova 测试的类型:
这里有 3 种要执行的方差分析测试,但是它们的讨论超出了本文的范围。
import pandas as pd
import statsmodels.api as sm
from statsmodels.formula.api import olsdf = pd.read_csv('drinks.csv')model = ols('reaction_time ~ drink_type', data=df).fit()
sm.stats.anova_lm(model, typ=2)
上面的代码片段产生以下结果,这与我们之前计算的 f 值( 0.017076 )相同:

作者图片
anova_lm() 函数也返回 p 值( 0.983079 )。您可以利用以下规则来确定分类变量是否对数值变量有任何影响:
- 如果 p < 0.05, this means that the categorical variable has significant influence on the numerical variable
- if p > 0.05,这意味着分类变量对数值变量没有显著影响
由于 p 值现在是 0.983079 (>0.05),这意味着饮料类型对反应时间没有显著影响。
摘要
在这篇文章中,我解释了方差分析如何帮助确定一个分类变量是否对一个数字变量有影响。到目前为止,我们讨论的方差分析测试被称为单向方差分析测试。方差分析有几个变体:
- 单向 ANOVA —用于检查一个数值变量如何响应一个独立分类变量的水平
- 双向 ANOVA—用于检查一个数值变量如何响应两个独立分类变量的水平
- 多向 ANOVA —用于检查一个数字变量如何响应多个独立分类变量的水平
使用双向 ANOVA 或多向 ANOVA ,您可以调查两个(或更多)独立分类变量对一个相关数值变量的综合影响。
希望这篇文章对你有用。敬请期待下一篇文章!
https://weimenglee.medium.com/membership
Python 中的统计数据-使用卡方进行要素选择

萨姆·穆卡达姆在 Unsplash 上拍摄的照片
在我之前的两篇文章中,我谈到了如何测量数据集中各列之间的相关性,以及如何检测它们之间的多重共线性:
然而,当你试图比较的变量是连续的时,这些技术是有用的。如果你的变量是分类的,你如何比较它们?在本文中,我将向您解释如何测试数据集中的两个分类列,以确定它们是否相互依赖(即相关)。我们将使用一个称为卡方的统计检验(通常写成 χ 2 )。
在我们开始讨论卡方之前,这里有一个可用于测试各种变量的测试方法的快速总结:

使用卡方统计确定两个分类变量是否相关
卡方( χ 2)统计是一种检查 两个分类名义变量 之间关系的方法。
名义变量包含没有内在排序的值。名义变量的例子有性别、种族、眼睛颜色、肤色等。序数变量,另一方面,包含有排序的值。顺序变量的例子有年级、教育水平、经济状况等。
卡方检验背后的关键思想是将数据中的观察值与期望值进行比较,看它们是否相关。特别是,这是检查两个分类名义变量是否相关的有用方法。这在机器学习中尤其重要,在机器学习中,您只希望将与目标相关的特征用于训练。
卡方检验有两种类型:
- 卡方拟合优度检验 —检验一个变量是否可能来自给定的分布。
- 独立性卡方检验 —检验两个变量是否相关。
查看https://www . jmp . com/en _ us/statistics-knowledge-portal/chi-square-test . html以获得关于上述两个卡方测试的更详细讨论。
当比较两个分类变量是否相关时,您将使用独立的卡方检验。
执行卡方检验的步骤
要使用卡方检验,您需要执行以下步骤:
- 定义你的零假设和替代假设。它们是:
- H₀ ( 零假设 ) —被比较的两个分类变量相互独立。
- H₁ ( 替代假设 ) —被比较的两个分类变量相互依赖。
2.决定 α 值。这是你愿意承担的得出错误结论的风险。举例来说,假设在测试独立性时,您将 α =0.05。这意味着你承担了 5%的风险,得出两个变量是独立的结论,而实际上它们并不独立。
3.使用两个分类变量计算卡方** 得分,并使用它计算 p 值。一个 低 p 值意味着你的两个分类变量之间存在一个 高 相关性(它们相互依赖)。p 值通过卡方评分计算得出。p 值将告诉您测试结果是否有意义。**
在卡方分析中,p 值是获得与当前实验中一样大或更大的卡方的概率,但数据仍然支持假设。它是仅仅由于偶然因素而偏离预期的可能性。一般来说,0.05 或更大的 p 值被认为是临界的,任何更小的值都意味着偏差是显著的,被测试的假设必须被拒绝。
要计算 p 值,您需要两条信息:
- 自由度— 类别数减 1
- ****卡方评分。
如果获得的 p 值为:
- < 0.05 (the α 你选择的值)你拒绝 H₀ ( 零假设),接受 H₁ ( 备择假设)。这意味着两个分类变量是依赖于的。
- > 0.05 你接受 H₀ ( 零假设),拒绝 H₁ ( 替代假设)。这意味着这两个分类变量是独立的。
在机器学习的特征选择的情况下,您可能希望与目标进行比较的特征具有较低的p 值(小于 0.05),因为这意味着该特征依赖于(相关于)目标。****
有了计算出来的卡方得分,您还可以使用它来参考卡方表来查看您的得分是在拒绝区域还是在接受区域。
上面的所有步骤听起来都有点模糊,真正理解卡方如何工作的最好方法是看一个例子。在下一节中,我将使用 Titanic 数据集,并对一些特征进行卡方检验,看看它们是否与目标相关。
在 Titanic 数据集上使用卡方检验
理解一个新话题的好方法是通过一个例子来理解概念。为此,我将使用经典的泰坦尼克号数据集(https://www.kaggle.com/tedllh/titanic-train)。
Titanic 数据集通常用于机器学习,以演示如何建立机器学习模型,并使用它进行预测。具体来说,数据集包含几个特征(p 类、性别、年龄、上船等)和一个目标(幸存)。数据集中的几个要素是分类变量:
- ****p class-乘客所处的客舱等级
- ****性别-乘客的性别
- ****登船——登船港
- ****幸存——如果乘客幸免于难
因为本文探讨了分类特性和目标之间的关系,所以我们只对包含分类值的那些列感兴趣。
加载数据集
现在您已经获得了数据集,让我们将它加载到 Pandas 数据框架中:
import pandas as pd
import numpy as npdf = pd.read_csv('titanic_train.csv')
df.sample(5)

作者图片
数据清理和特征工程
有些列并不真正有用,因此我们将删除它们。此外,还有一些丢失的值,所以让我们删除所有那些值为空的行:
df.drop(columns=['PassengerId','Name', 'Ticket','Fare','Cabin'],
inplace=True)
df.dropna(inplace=True)
df

作者图片
我们还将在 Parch (父母或子女)和 Sibsp (兄弟姐妹或配偶)列的基础上,单独添加一个名为的列。我们想探究的是独自一人是否会影响乘客的存活率。所以如果 Parch 和 Sibsp 都为 0,则只有为 1,否则为 0:****
df['Alone'] = (df['Parch'] + df['SibSp']).apply(
lambda x: 1 if x == 0 else 0)
df

作者图片
可视化特征和目标之间的相关性
现在,数据已经清理完毕,让我们试着想象一下乘客的性别与他们在事故中的生还率之间的关系:
import seaborn as sns
sns.barplot(x='Sex', y='Survived', data=df, ci=None)
性别栏包含名义数据(即排名不重要)。

作者图片
从上图可以看出,所有女乘客中,70%以上生还;在所有的男人中,大约 20%活了下来。似乎在性别和幸存特征之间存在着非常强的联系。为了证实这一点,我们稍后将使用卡方检验来证实这一点。
Pclass 和幸存怎么样?他们有关系吗?
sns.barplot(x='Pclass', y='Survived', data=df, ci=None)

作者图片
或许不出所料,这表明乘客所处的等级越高,乘客的存活率越高。
下一个有趣的特征是,出发地点是否决定了谁能幸存,谁不能:
sns.barplot(x='Embarked', y='Survived', data=df, ci=None)

作者图片
从图表上看,似乎更多从瑟堡出发的人幸存了下来。
C =瑟堡;Q =皇后镇;S =南安普敦
你还想知道独自旅行是否会让一个人更有生存能力:
ax = sns.barplot(x='Alone', y='Survived', data=df, ci=None)
ax.set_xticklabels(['Not Alone','Alone'])

作者图片
你可以看到,如果一个人与他们的家人在一起,他/她将有更高的生存机会。
可视化每个特征之间的相关性
既然我们已经可视化了分类特征与目标之间的关系(幸存),我们现在想要可视化每个特征之间的关系。在此之前,您需要将 Sex 和abowed列中的标签值转换为数值。为此,您可以使用 sklearn 中的 LabelEncoder 类:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as snsfrom sklearn import preprocessing
le = preprocessing.LabelEncoder()
le.fit(df['Sex'])
df['Sex'] = le.transform(df['Sex'])
sex_labels = dict(zip(le.classes_, le.transform(le.classes_)))
print(sex_labels)le.fit(df['Embarked'])
df['Embarked'] = le.transform(df['Embarked'])
embarked_labels = dict(zip(le.classes_,
le.transform(le.classes_)))
print(embarked_labels)
上面的代码片段对性别和着手列进行了标签编码。输出显示了每一列的值的映射,这在以后执行预测时非常有用:
{'female': 0, 'male': 1}
{'C': 0, 'Q': 1, 'S': 2}
以下陈述显示了着手和性别之间的关系:
ax = sns.barplot(x='Embarked', y='Sex', data=df, ci=None)
ax.set_xticklabels(embarked_labels.keys())

作者图片
似乎从南安普敦( S )登船的男性比从皇后镇( Q )和瑟堡( C )登船的男性多。
****上船和单独怎么样?
ax = sns.barplot(x='Embarked', y='Alone', data=df, ci=None)
ax.set_xticklabels(embarked_labels.keys())

作者图片
似乎很大一部分从皇后镇出发的人都是独自一人。
最后,让我们看看性和独处之间的关系:
ax = sns.barplot(x='Sex', y='Alone', data=df, ci=None)
ax.set_xticklabels(sex_labels.keys())

作者图片
如你所见,独自旅行的男性多于女性。
定义假设
你现在定义你的零假设和替代假设。如前所述,它们是:
- H₀ ( 零假设 ) —待比较的两个分类变量相互独立。
- H₁ ( 替代假设)——被比较的两个分类变量相互依赖。
您根据以下 p 值条件得出结论:
- p< 0.05 — this means the two categorical variables are 关联 。
- p > 0.05 —这意味着两个分类变量是 而不是 相关 。
手动计算χ2
让我们手动浏览计算χ2 值的步骤。第一步是创建一个列联表。以性别和幸存列为例,首先创建一个列联表:

作者图片
上面的列联表显示了两个分类列的频率分布——性别和幸存。
****自由度接下来被计算为 (行数-1) (列数-1) 。在本例中,自由度为(2–1)(2–1)=1。
创建列联表后,汇总所有行和列,如下所示:

作者图片
以上是你的观测值。
接下来,您将计算期望值。以下是它们的计算方法:
- 将观察值中的每个值替换为其列的总和与其行的总和的乘积除以总和。
下图显示了如何计算第一个值:

作者图片
下图显示了如何计算第二个值:

作者图片
以下是预期值的结果:

作者图片
然后,使用公式 χ 2 计算每个单元格的卡方值:

作者图片
将此公式应用于观察值和预期值,您得到卡方值:

作者图片
****卡方值是卡方值的总和:

作者图片
您可以使用以下网站来验证这些数字是否正确:
上述步骤的 Python 实现包含在下面的 chi2_by_hand() 函数中:
def **chi2_by_hand**(df, col1, col2):
#---create the contingency table---
df_cont = pd.crosstab(index = df[col1], columns = df[col2])
display(df_cont) #---calculate degree of freedom---
degree_f = (df_cont.shape[0]-1) * (df_cont.shape[1]-1) #---sum up the totals for row and columns---
df_cont.loc[:,'Total']= df_cont.sum(axis=1)
df_cont.loc['Total']= df_cont.sum()
print('---Observed (O)---')
display(df_cont) #---create the expected value dataframe---
df_exp = df_cont.copy()
df_exp.iloc[:,:] = np.multiply.outer(
df_cont.sum(1).values,df_cont.sum().values) /
df_cont.sum().sum()
print('---Expected (E)---')
display(df_exp)
# calculate chi-square values
df_chi2 = ((df_cont - df_exp)**2) / df_exp
df_chi2.loc[:,'Total']= df_chi2.sum(axis=1)
df_chi2.loc['Total']= df_chi2.sum()
print('---Chi-Square---')
display(df_chi2) #---get chi-square score---
chi_square_score = df_chi2.iloc[:-1,:-1].sum().sum()
return chi_square_score, degree_f
chi2_by_hand() 函数接受三个参数——包含所有列的 dataframe,后面是两个字符串,包含要比较的两个列的名称。它返回一个元组——卡方得分加上自由度。
现在让我们使用 Titanic 数据集测试上面的函数。首先,让我们比较一下性别和幸存的列:
chi_score, degree_f = **chi2_by_hand(df,'Sex','Survived')**
print(f'Chi2_score: {chi_score}, Degrees of freedom: {degree_f}')
您将看到以下结果:
Chi2_score: 205.1364846934008, Degrees of freedom: 1
使用卡方得分,您现在可以使用卡方分布曲线决定是接受还是拒绝零假设:

作者图片
x 轴代表 χ 2 分数。临界卡方区域右侧的区域被称为剔除区域。它左边的区域称为接受区域。如果您获得的卡方得分落在可接受区域内,则零假设被接受;否则另一个假设被接受。
那么如何获得临界卡方区域?为此,您必须检查卡方表:

你可以在 https://www.mathsisfun.com/data/chi-square-table.html 的查看卡方表
这就是卡方表的使用方法。当您的 α 设置为 0.05,并且 1 自由度时,临界卡方区域为 3.84 (参考上图)。将该值放入卡方分布曲线,您可以得出以下结论:

作者图片
- 由于计算的卡方值( 205 )大于 3.84 ,因此它落在拒绝区域,因此零假设被拒绝,而替代假设被接受。
- 回想一下我们的替代假设: H₁ ( 替代假设 ) —被比较的两个分类变量相互依赖。
这意味着列中的和列中的列是相互依赖的。
作为练习,您可以在其他功能上使用 chi2_by_hand() 功能。
计算 p 值
上一节展示了如何通过检查卡方得分并将其与卡方分布曲线进行比较来接受或拒绝零假设。
接受或拒绝零假设的另一种方法是使用 p 值。请记住,可以使用卡方得分和自由度计算 p 值。
为简单起见,我们将不详细讨论如何手工计算 p 值。
在 Python 中,可以使用 stats 模块的 sf() 函数计算 p 值:
def chi2_by_hand(df, col1, col2):
#---create the contingency table---
df_cont = pd.crosstab(index = df[col1], columns = df[col2])
display(df_cont) ... chi_square_score = df_chi2.iloc[:-1,:-1].sum().sum()
**#---calculate the p-value---
from scipy import stats
p = stats.distributions.chi2.sf(chi_square_score, degree_f)** return chi_square_score, degree_f**, p**
您现在可以调用 chi2_by_hand() 函数并获得 chi_square 分数、自由度和 p 值:
chi_score, degree_f**, p** = chi2_by_hand(df,'Sex','Survived')
print(f'Chi2_score: {chi_score}, Degrees of freedom: {degree_f}**,** **p-value: {p}**')
上述代码会产生以下 p 值:
Chi2_score: 205.1364846934008, Degrees of freedom: 1, **p-value: 1.581266384342472e-46**
快速回顾一下,您接受或拒绝假设,并根据以下 p 值条件形成您的结论:
- p< 0.05 — this means the two categorical variables are 关联 。
- p > 0.05 —这意味着两个分类变量是 而不是 相关 。
并且由于p<0.05——这意味着两个分类变量是 相关 。
尝试其他功能
让我们尝试包含名义值的分类列:
chi_score, degree_f, p = chi2_by_hand(df,'**Embarked**','**Survived**')
print(f'Chi2_score: {chi_score}, Degrees of freedom: {degree_f}, p-value: {p}')# Chi2_score: 27.918691003688615, Degrees of freedom: 2,
# p-value: **8.660306799267924e-07** chi_score, degree_f, p = chi2_by_hand(df,'**Alone**','**Survived**')
print(f'Chi2_score: {chi_score}, Degrees of freedom: {degree_f}, p-value: {p}')# Chi2_score: 28.406341862069905, Degrees of freedom: 1,
# p-value: **9.834262807301776e-08**
由于着手和单独的 p 值都是< 0.05,您可以得出结论,即着手和单独特征都与幸存目标相关,并且应该包括在您的模型中用于训练。
摘要
在本文中,我简要介绍了卡方统计测试的工作原理,以及如何将它应用于 Titanic 数据集。这里需要注意的是:
- 当皮尔逊系数和斯皮尔曼等级系数测量两个变量之间关联的强度时,卡方检验测量两个变量之间关联的显著性。它告诉你的是,你在样本中发现的关系是否有可能存在于总体中,或者由于抽样误差而偶然出现的可能性有多大。
- 卡方检验对你的列联表中的小频率很敏感。一般来说,如果列联表中的某个单元格的频率等于或小于 5,那么卡方检验将导致结论错误。此外,如果样本量小于 50,则不应使用卡方检验。
我希望您现在对卡方如何工作以及如何将其用于机器学习中的特征选择有了更好的理解。我的下一篇文章再见!
**https://weimenglee.medium.com/membership **
统计死了,统计万岁!
认识一下重采样,这是一种通用的现代统计方法

估计置信区间和假设检验是两种常见的统计任务。他们周围有一种神秘的气氛,因为他们依赖于伴随着复杂和特定案例假设的数学。虽然这仍然是许多人做统计的方式,但我认为这不是最好的方式。现代计算能力使我们能够利用更容易理解且没有假设和近似的重采样方法。
在过去,当计算能力只是我们今天拥有的一小部分时,统计学家别无选择,只能求助于数学函数,如 t 分布来逼近抽样分布。从评估回归系数的显著性,到计算置信区间,再到 A/B 测试,t 统计数据随处可见。然而,这些天来,我们可以放弃这种重假设的方法,转而支持重采样方法。
重采样归结为从数据中重复采样值,以评估从这些数据计算的统计数据中的随机可变性。它有两种主要口味:
- 自举,用于评估估计或预测的可靠性。它可以用来计算置信区间或预测区间(不要混淆这两者!),以提高机器学习模型的准确性(如 bagging,或 bootstrap aggregating,随机森林和许多 boosting 模型的基础),或估计输入缺失数据的不确定性。
- 置换,用于假设检验。
让我们来看看它们中的每一个,以及如何使用它们以一种没有数学和假设、标准化、容易出错的方式来解决常见问题。

拔靴带
Bootstrapping 简单来说就是用替换从原始数据中抽取许多样本。这些被称为引导样本,因为我们是用替换来绘制的,所以同一个数据点可能在一个引导样本中出现多次。这样做的目的是模拟从一个假设的总体中获得许多样本,这样我们就可以观察到抽样的不确定性。然后,我们对每个独立的 bootstrap 样本执行所需的计算,并将结果合并到反映其可变性的某些感兴趣的统计分布中。
(引自我的关于自举置信区间的文章)

改编自作者在 DataCamp 讲授的 R 课程中的用插补处理缺失数据。
bootstrap 是一种统一的标准化方法。无论你想要实现什么,你总是遵循相同的步骤:用替换抽取许多样本,计算每个样本上你关心的任何东西,将所有样本的结果组合成一个分布,并使用这个分布来了解你的估计的可变性。让我们看一些例子。
Bootstrap 从数据中抽取替换样本来测量采样不确定性。它有无数的用例。
示例 1:在 A/B 测试中引导置信区间
南加州的房子比该州北部的更宽敞吗?让我们根据加州住房数据进行一次 A/B 测试!
要做到这一点,我们可以按纬度中位数分割数据,比较南方的平均房价(222k 美元)和北方的平均房价(191k 美元),得出 31k 美元的差额。请注意,数据集和价格都是 1997 年的——希望它们是今天的价格,不是吗?
这种差异显著吗?或者,如果我们收集了另一部分数据,我们会得到一个不同的数字,甚至可能是负数?置信区间就是答案。95%置信区间告诉我们,如果我们收集了许多其他关于加州房屋的数据集,并对每个数据集进行了这样的 A/B 测试,在 95%的情况下,南方和北方价格之间的真正平均差异(如果我们有加州所有房屋的数据,我们就会知道)将被区间覆盖。
以下是如何通过引导计算我们的 A/B 测试的 95% CI:
Mean diff: 0.30806777712026046
95% CI: [0.28223704 0.33397099]
我们可以 95%确定 28k-33k 区间包含了真正的差异,所以是的,南方的房子很可能更贵。多亏了 bootstrap,我们可以在不假设价格服从正态分布的情况下这么说(事实显然并非如此)。这是由于中心极限定理的魔力——bootstrap 样本是随机的,因此是独立的,因此它们的均值差异是正态分布的。
示例 2:均值的自举标准误差
让我们继续研究加州的住房数据,看看 1997 年该州的平均房龄是多少,以及它的标准误差是多少。我们可以计算出数据中的平均房龄为 28.6 年,但是这个样本估计值对于加州房屋的总体准确度如何呢?让我们自举看看!
Average mean: 28.6383311627907
Standard error: 0.08733193966359917
平均房龄的 bootstrap 估计与我们在数据中观察到的情况相对应,我们也得到了 0.08 年的 bootstrap 标准差,接近一个月。这表明我们对平均年龄的估计相当准确!
示例 3:引导预测间隔
在我的上一篇文章中,我已经写了信心和预测区间的区别。这篇文章包括在回归模型环境中引导两者的代码——不要犹豫,绕道去看看吧!
示例 4:引导和缺失数据
在处理不完整的数据时,通常的做法是使用某种插补方法来填补缺失值,然后对这种填补的数据集进行分析或训练模型。这是一种可怕的做法!
这种方法完全忽略了一个事实,即估算值只是带有一些不确定性的估计值,而这种不确定性会转移到基于估算数据的任何模型或分析中。换句话说,我们报告的任何模型预测或统计估计都应该伴随一些可变性测量,包括插补的不确定性。我在这里写了(一个 R 例子)和(处理缺失数据的一般说明)。
再一次,bootstrap 来帮忙了!我们需要从不完整的数据集中提取 bootstrap 样本,单独估算它们中的每一个,分别对它们中的每一个训练一个模型,然后将来自所有这些模型的预测组合成一个分布。然后,我们可以根据该分布计算区间或标准差,以量化部分来自插补的预测中的不确定性。
自举:警告的话
自举似乎像魔术一样管用。它让我们逼近任何我们想要的量,甚至是那些经典统计学不知道公式的量,并告诉我们其估计的不确定性,所有这些都是无假设的。
但是不要忘记,bootstrap 不会创建更多的数据,也不会补偿一个小样本。只有当我们拥有的数据代表了感兴趣的人群时,它才起作用。如果是这样的话,那么 bootstrapping 告诉我们,如果我们收集了更多的数据,情况会怎样。

排列测试
除了自举,第二种重采样方法是置换。在概率论中,一组项目的排列只是这个集合中项目的重新排序。例如,数字集合[1, 2, 3]可以以五种不同的方式排列,以获得以下集合:[1, 3, 2]、[2, 1, 3]、[2, 3, 1]、[3, 1, 2]和[3, 2, 1]。这个简单的想法使我们能够以一种无公式的方式进行假设检验,以一种易于解释和理解的方式,并且不依赖于分布假设,而这在经典的检验方法中是典型的情况。
为了理解排列测试是如何工作的,我们需要快速回顾一下经典假设测试(更深入的阐述,请查看本文)。为了保持冷静,我们将再次查看加州住房数据。这次我们来测试一下这个州东部和西部的房价是不是一样的。
经典假设检验
我们要进行的测试是均值差异测试:我们想知道东部的平均房价(数据中为 21.5 万美元)与西部的平均房价(数据中为 19.9 万美元)是否相同。观察到的 16k 美元的差异是随机的结果,还是东部的房子真的更贵?t-test 很好地服务了这个用例。让我们一步一步地完成测试过程。
- 首先,我们需要建立我们的假设。零假设将是东部的平均房价等于西部的平均房价,我们将试图拒绝它,支持另一个假设,即它们不相等。
- 接下来,我们需要一个叫做测试统计的东西。一个测试统计量是根据某种公式计算出来的一个单一的数字,特定于我们正在进行的测试。它必须满足两个条件:它需要从数据中计算出来,我们需要知道它的分布,假设零假设为真。幸运的是,我们测试均值差异的案例在几年前就已经得到了充分的研究,我们知道,如果平均价格相等(零假设为真),并且我们观察到平均值中的一些差异只是由于偶然因素,那么下面的测试统计量具有 t 分布。

t 统计公式,截屏来自维基百科。
3.上面带破折号的 X s 表示两组(东部和西部)的平均价格, s 是价格的方差, n 是观察的次数,假设两组相同。我们可以用这个公式来计算我们的测试统计。
t-stat: 9.627149556121305
4.最后,我们需要利用这样一个事实,即我们知道如果零假设为真,那么我们的 t 统计量具有一个自由度为 (2n)-2* 的 t 分布。看起来是这样的:

检验统计量:相对于观察值的空值分布。图片由作者提供。
由此,我们得出结论,如果零假设为真(如果东西方的平均价格相同),那么我们获得的 t 统计值几乎不可能得到。这意味着最有可能的是,null 是假的,我们应该拒绝它:平均价格不同。
5.我们可以通过计算 p 值来量化这个决定。p 值是假设空值为真,我们可以从数据中得到实际检验统计值的概率(或者更极端的概率)。换句话说,蓝色质量的百分比位于虚线的右侧,在这种情况下似乎接近于零。让我们来计算一下,这次使用一个 scipy 函数。调用scipy.stats.ttest_ind(price_east, price_west)给我们以下输出:
Ttest_indResult(
statistic=9.627692430826853,
pvalue=6.791637088718439e-22
)
您可以看到 9.627 的测试统计值与我们的手动计算相匹配,并且 p 值实际上为零。因此,我们拒绝零假设。唷!
请注意,我们是如何依赖基于多种假设的 t 统计量的近似公式的。我们已经隐含地假设,对于西方和东方的价格,方差和观察次数是相同的。我们还假设这两个数据子样本是独立的并且是正态分布的。最后,样本量要足够大。我们已经有了五个假设,其中一些显然不成立(价格很少是正态分布的!).
经典假设检验带有大量特定案例的假设。在更复杂的情况下,可能没有现成的公式。置换测试解决了这两个问题。
当然,您可以调整公式以满足您的需求,但首先,另一个公式将带有另一组隐藏的假设,其次,在比简单的 t 测试更复杂的情况下,封闭形式的公式甚至可能不为您的特定用例所知。进入排列测试!
简而言之,置换测试
排列测试背后的一般思想是获取所有可用的数据并随机排列。这对应于没有影响的无效假设。接下来,我们从这个组合和置换的数据集中重复抽取样本(没有替换),计算感兴趣的量,并将它们组合成一个分布。我们知道,这种分布体现了由于排列步骤而偶然产生的变化。最后,我们将我们在数据中观察到的感兴趣的数量与其随机变化的分布进行比较。如果它正好位于这个分布中,我们就没有证明任何东西——没有证据可以拒绝零假设,因为我们的结果可能是偶然产生的。然而,如果我们的观察值发现自己远离分布,我们有理由说它不可能是偶然产生的,我们拒绝零假设。我们还可以计算 p 值,就像在经典测试中一样。实践中来看看吧!
示例 1:t 测试的排列测试
在我们的东西价格测试中,我们需要组合所有的价格,重复排列它们,创建两组与原始组匹配的大小,并计算平均价格的差异。这样,我们得到了偶然产生的差异的分布。让我们将其与我们观察到的 16k 的差异进行比较。

置换差异与观察差异。图片由作者提供。
我们观察到的东西方平均价格的差异比偶然产生的要大得多,因此我们将拒绝零假设。我们通过计算 p 值来量化一下。由于 p 值是红线右侧蓝色质量的比例,我们可以很容易地计算如下。
在这个非常明显的例子中,p 值当然为零,就像在经典测试中一样。
示例 2:方差分析的排列测试
以非常相似的方式,我们可以运行 A/B/C 测试,称为方差分析,或 ANOVA。假设我们正在为我们的网站测试不同的颜色主题,目的是吸引用户。
作为实验的一部分,我们随机向用户展示了不同的网站变体,并计算了他们在网站上花费的时间。下面是 15 个用户的数据(五个显示黄色版本,另外五个显示蓝色版本,最后五个显示绿色版本)。自然地,为了使这样的实验有效,我们需要 15 个以上的用户,这只是一个演示。我们感兴趣的是均值之间的差异在多大程度上大于随机产生的差异。如果它们非常大,那么我们可以得出结论,颜色确实影响了网站的停留时间。
(引自我关于概率分布的文章)

花在网站不同颜色变体上的时间。图片由作者提供。
虽然我们可以基于统计理论和 f 分布来做这件事(查看本文中的 f 分布部分来了解如何做这件事),我们也可以使用排列测试。
p-value: 0.00844
让我们将观察到的方差可视化在置换方差之上。

置换方差与观察方差。图片由作者提供。
你会发现 p 值非常接近我们按照经典 f 检验方法得到的值。由于它相当小,我们得出结论,颜色确实影响网站的粘性。
例 3:卡方检验的排列检验
χ2 或卡方检验在某种意义上是离散数据方差分析的一个版本。再次考虑网站的三种颜色。这一次,我们测量的不是在网站上花费的时间(一个连续变量),而是通过网站购买的次数(一个离散变量)。我们想知道哪种颜色的购买量最大。每种颜色都向 1000 名用户展示过,结果如下:

网站色彩实验结果。图片由作者提供。
我们可以运行一个经典的 chi2 测试(见这里的如何做)但是一个无假设置换测试可能是一个更好的选择。
第一步是假设三个网站版本平均产生相同的购买数量(我们的零假设)。如果这是真的,我们预计每个版本的购买次数都是一样的,应该是
(17+9+14)/3,或者 13.33 次购买。
(引自我关于概率分布的文章)
因此,在我们的排列测试中,我们通过假设所有网站颜色中有 40 次购买和 2960 次未购买,生成一个随机生成的统计分布,并从中取样。我们感兴趣的 chi2 统计量是置换购买和预期购买之间的平方差之和。
p-value: 0.270826

置换与观察到的 chi2 统计。图片由作者提供。
高 p 值让我们得出结论,不同颜色网站购买数量的差异可能仅仅是随机因素造成的。
有趣的是,经典卡方检验中使用的 χ 2 分布实际上只是检验统计量的真实分布的近似值。出于这个原因,从我们的置换测试中重新采样的 p 值与我们从经典测试中得到的值略有不同(我们得到 0.3)。这是支持重采样方法的又一个证据——这里不需要近似。

来源
本文中的一些想法和代码基于:
Bruce,Bruce & Gedeck (2020),数据科学家实用统计学,第二版,O'Reilly

感谢阅读!
如果你喜欢这篇文章,为什么不订阅电子邮件更新我的新文章呢?并且通过 成为媒介会员 ,可以支持我的写作,获得其他作者和我自己的所有故事的无限访问权限。
需要咨询?你可以问我任何事情,也可以在这里 为我预约 1:1 。
你也可以试试我的其他文章。不能选择?从这些中选择一个:
统计和 R 是 2 岁
了解如何使用{ googleAnayticsR } R 软件包跟踪页面浏览量、会话、用户参与度和更多 Google 分析数据

照片由米切尔·罗
介绍
tats 和 R 正好在两年前推出。就像去年的一样,我认为这是一个很好的时机,通过分享一些关于博客受众的数据来对过去的 12 个月做一个回顾。
这篇文章不是炫耀我的数字,而是展示如何使用谷歌分析数据分析你的博客或网站流量的一种方式。关于我的博客的读者的数据对你来说可能是无用的(我相信,不应该与此相比)。然而,这篇文章中使用的代码可以在你自己的博客或网站上重复使用(前提是你也使用 Google Analytics 来跟踪你的受众)。
请注意,我使用了{googleAnalyticsR} R 包来分析我博客的 Google Analytics 数据。如果您不熟悉这个包,请先查看先决条件。
如果您已经使用了该软件包,您可以选择如下的帐户(确保使用您自己的属性名称编辑代码):
library(googleAnalyticsR)accounts <- ga_account_list()# select the view ID by property name
view_id <- accounts$viewId[which(accounts$webPropertyName == "statsandr.com")]
除此之外,我还假设您对[{ggplot2}](https://statsandr.com/blog/graphics-in-r-with-ggplot2/)有基本的了解——这是一个流行的 R 包,用于绘制漂亮的图形和可视化效果。
分析学
去年我主要关注的是节数。今年,我主要关注页面浏览量来说明一个不同的指标。
供您参考,会话是在给定时间范围内发生的一组用户与您网站的交互,而页面视图顾名思义,被定义为您网站上某个页面的视图。
您可以随时通过编辑下面代码中的metrics = c("pageviews")来更改指标。在这篇文章中查看 Google Analytics 提供的所有可用指标。
用户和页面视图
至于去年的点评,先说一些大概的数字,比如整个网站的用户数和浏览量。
请注意,我们仅分析去年的流量,因此我们提取 2020 年 12 月 16 日至昨天(2021 年 12 月 15 日)的数据:
# set date range
start_date <- as.Date("2020-12-16")
end_date <- as.Date("2021-12-15")# get Google Analytics (GA) data
gadata <- google_analytics(view_id,
date_range = c(start_date, end_date),
metrics = c("users", "pageviews"),
anti_sample = TRUE # slows down the request but ensures data isn't sampled
)gadata## users pageviews
## 1 549360 876280
在过去的一年里,Stats and R 吸引了 549,360 名用户(访问该网站的新老用户数量),他们总共产生了 876,280 次页面浏览量。
这意味着 2021 年每天平均有 2,401 次页面浏览,而 2020 年每天有 1,531 次页面浏览(增长了 56.81%)。
一段时间内的页面访问量
分析博客受众的第一个有趣指标是流量随时间的演变。
随着时间的推移,页面浏览量的每日数量可以用散点图呈现出来——连同一条平滑的线——来分析你的博客的受众的演变:
# get the Google Analytics (GA) data
gadata <- google_analytics(view_id,
date_range = c(start_date, end_date),
metrics = c("pageviews"), # edit for other metrics
dimensions = c("date"),
anti_sample = TRUE # slows down the request but ensures data isn't sampled
)# load required libraries
library(dplyr)
library(ggplot2)# scatter plot with a trend line
gadata %>%
ggplot(aes(x = date, y = pageviews)) +
geom_point(size = 1L, color = "steelblue") + # change size and color of points
geom_smooth(color = "steelblue", alpha = 0.25) + # change color of smoothed line and transparency of confidence interval
theme_minimal() +
labs(
y = "Page views",
x = "",
title = "Evolution of daily page views",
subtitle = paste0(format(start_date, "%b %d, %Y"), " to ", format(end_date, "%b %d, %Y")),
caption = "Data: Google Analytics data of statsandr.com"
) +
theme(plot.margin = unit(c(5.5, 17.5, 5.5, 5.5), "pt")) + # to avoid the plot being cut on the right edge
scale_y_continuous(labels = scales::comma) # better y labels

作者图片
尽管页面浏览量变化很大(异常值在一天内超过 5000 次页面浏览量,有些日子低至不到 1000 次页面浏览量),但它似乎是周期性的,在夏季会有所下降。值得注意的是,去年出现了同样的下降,可能是因为人们在暑假期间不太可能阅读关于统计和 R 的帖子。
因此,如果你在博客中写一些技术方面的东西,夏季的低数据可能是意料之中的,并不一定意味着你的网站有问题。
每个频道的页面访问量
了解人们如何访问你的博客也是一个非常重要的因素。
以下是如何在线图中可视化每个频道每日页面浏览量的变化:
# Get the data
trend_data <- google_analytics(view_id,
date_range = c(start_date, end_date),
dimensions = c("date"),
metrics = "pageviews",
pivots = pivot_ga4("medium", "pageviews"),
anti_sample = TRUE # slows down the request but ensures data isn't sampled
)# edit variable names
names(trend_data) <- c("Date", "Total", "Organic", "Referral", "Direct", "Email", "Social")# Change the data into a long format
library(tidyr)
trend_long <- gather(trend_data, Channel, Page_views, -Date)# Build up the line plot
trend_long %>%
filter(Channel != "Total") %>%
ggplot() +
aes(x = Date, y = Page_views, group = Channel) +
theme_minimal() +
geom_line(aes(colour = Channel)) +
labs(
y = "Page views",
x = "",
title = "Evolution of daily page views per channel",
subtitle = paste0(format(start_date, "%b %d, %Y"), " to ", format(end_date, "%b %d, %Y")),
caption = "Data: Google Analytics data of statsandr.com"
) +
scale_y_continuous(labels = scales::comma) # better y labels

作者图片
从上面的图表中我们可以看出,绝大多数的页面浏览量来自有机渠道(比如来自谷歌、必应等搜索引擎)。),当一篇文章发表时,一些峰值来自推荐(主要来自 R-bloggers 和 RWeekly)。(顺便说一句,如果你想在新帖子发布时收到电子邮件通知,你可以随时订阅时事通讯。)
如果你碰巧写教程,你也可以期待大部分访问者来自有机渠道。如果你在社交媒体上非常活跃,你很可能会从社交渠道吸引更多的访问者。
一周中每天和一年中每月的页面浏览量
正如在前面的情节中看到的,有许多起伏和交通似乎是周期性的。
为了进一步研究这个问题,我们绘制了一个一周的每一天的页面浏览量的箱线图:
# get data
gadata <- google_analytics(view_id,
date_range = c(start_date, end_date),
metrics = "pageviews",
dimensions = c("dayOfWeek", "date"),
anti_sample = TRUE # slows down the request but ensures data isn't sampled
)## Recoding gadata$dayOfWeek following GA naming conventions
gadata$dayOfWeek <- recode_factor(gadata$dayOfWeek,
"0" = "Sunday",
"1" = "Monday",
"2" = "Tuesday",
"3" = "Wednesday",
"4" = "Thursday",
"5" = "Friday",
"6" = "Saturday"
)## Reordering gadata$dayOfWeek to have Monday as first day of the week
gadata$dayOfWeek <- factor(gadata$dayOfWeek,
levels = c(
"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
"Sunday"
)
)# Boxplot
gadata %>%
ggplot(aes(x = dayOfWeek, y = pageviews)) +
geom_boxplot(fill = "steelblue") +
theme_minimal() +
labs(
y = "Page views",
x = "",
title = "Page views per day of week",
subtitle = paste0(format(start_date, "%b %d, %Y"), " to ", format(end_date, "%b %d, %Y")),
caption = "Data: Google Analytics data of statsandr.com"
) +
scale_y_continuous(labels = scales::comma) # better y labels

作者图片
我们还可以计算每天的总浏览量和平均浏览量,得到一个数字摘要,而不是一个图表:
# compute sum
dat_sum <- aggregate(pageviews ~ dayOfWeek,
data = gadata,
FUN = sum
)# compute mean
dat_mean <- aggregate(pageviews ~ dayOfWeek,
data = gadata,
FUN = mean
)# combine both in one table
dat_summary <- cbind(dat_sum, dat_mean[, 2])# rename columns
names(dat_summary) <- c("Day of week", "Sum", "Mean")# display table
dat_summary## Day of week Sum Mean
## 1 Monday 141115 2713.750
## 2 Tuesday 143145 2752.788
## 3 Wednesday 146472 2763.623
## 4 Thursday 140712 2706.000
## 5 Friday 128223 2465.827
## 6 Saturday 85766 1649.346
## 7 Sunday 90847 1747.058
不出所料,与周末相比,工作日的读者更多。
可以对一年中的每个月而不是一周中的每一天进行相同的分析:
# get data
gadata <- google_analytics(view_id,
date_range = c(start_date, end_date),
metrics = "pageviews",
dimensions = c("month", "date"),
anti_sample = TRUE # slows down the request but ensures data isn't sampled
)# Boxplot
gadata %>%
ggplot(aes(x = month, y = pageviews)) +
geom_boxplot(fill = "steelblue") +
theme_minimal() +
labs(
y = "Page views",
x = "Month",
title = "Page views per month",
subtitle = paste0(format(start_date, "%b %d, %Y"), " to ", format(end_date, "%b %d, %Y")),
caption = "Data: Google Analytics data of statsandr.com"
) +
scale_y_continuous(labels = scales::comma) # better y labels

作者图片
# compute sum
dat_sum <- aggregate(pageviews ~ month,
data = gadata,
FUN = sum
)# compute mean
dat_mean <- aggregate(pageviews ~ month,
data = gadata,
FUN = mean
)# combine both in one table
dat_summary <- cbind(dat_sum, dat_mean[, 2])# rename columns
names(dat_summary) <- c("Month", "Sum", "Mean")# display table
dat_summary## Month Sum Mean
## 1 01 68534 2210.774
## 2 02 80953 2891.179
## 3 03 92629 2988.032
## 4 04 88679 2955.967
## 5 05 81739 2636.742
## 6 06 64460 2148.667
## 7 07 49772 1605.548
## 8 08 46389 1496.419
## 9 09 68046 2268.200
## 10 10 79237 2556.032
## 11 11 77745 2591.500
## 12 12 78097 2519.258
从图表和数字摘要中可以清楚地看出,我们的样本中的页面浏览量在一周中的每一天和一年中的每个月都是不同的。
如果你有兴趣更进一步,你可以随时执行一个 ANOVA 来查看每天或每个月的页面浏览量是否有显著的差异。
每月和每年的页面访问量
如果你有超过一年的数据,比较这些年来你每月博客的流量会很有用。
使用下面的代码,我们创建了一个条形图,显示每月和每年的日页面浏览量:
# set new date range
start_date_launch <- as.Date("2019-12-16")# get data
df2 <- google_analytics(view_id,
date_range = c(start_date_launch, end_date),
metrics = c("pageviews"),
dimensions = c("date"),
anti_sample = TRUE # slows down the request but ensures data isn't sampled
)# add in year month columns to dataframe
df2$month <- format(df2$date, "%m")
df2$year <- format(df2$date, "%Y")# page views by month by year using dplyr then graph using ggplot2 barplot
df2 %>%
filter(year != 2019) %>% # remove 2019 because there are data for December only
group_by(year, month) %>%
summarize(pageviews = sum(pageviews)) %>%
# print table steps by month by year
# print(n = 100) %>%
# graph data by month by year
ggplot(aes(x = month, y = pageviews, fill = year)) +
geom_bar(position = "dodge", stat = "identity") +
theme_minimal() +
labs(
y = "Page views",
x = "Month",
title = "Page views per month and year",
subtitle = paste0(format(start_date_launch, "%b %d, %Y"), " to ", format(end_date, "%b %d, %Y")),
caption = "Data: Google Analytics data of statsandr.com"
) +
scale_y_continuous(labels = scales::comma) # better y labels

作者图片
这个柱状图可以很容易地看到几个月来页面浏览量的变化,但更重要的是,可以比较不同年份之间的变化。
表现最佳的页面
衡量你的博客或网站表现的另一个重要因素是每页的浏览量。
诚然,在谷歌分析中可以很容易地找到一年中页面浏览量最高的页面(你可以通过Behavior > Site Content > All pages访问)。
然而,对于感兴趣的读者,这里是如何获得 R 中的数据(注意,您可以在下面的代码中更改n = 7来更改要显示的最高性能页面的数量):
## Make the request to GA
data_fetch <- google_analytics(view_id,
date_range = c(start_date, end_date),
metrics = c("pageviews"),
dimensions = c("pageTitle"),
anti_sample = TRUE # slows down the request but ensures data isn't sampled
)## Create a table of the most viewed posts
library(lubridate)
library(reactable)
library(stringr)most_viewed_posts <- data_fetch %>%
mutate(Title = str_trunc(pageTitle, width = 40)) %>% # keep maximum 40 characters
count(Title, wt = pageviews, sort = TRUE)head(most_viewed_posts, n = 7) # edit n for more or less pages to display## Title n
## 1 Outliers detection in R - Stats and R 119747
## 2 Descriptive statistics in R - Stats a... 109473
## 3 Variable types and examples - Stats a... 83025
## 4 Correlation coefficient and correlati... 65703
## 5 Chi-square test of independence in R ... 62100
## 6 The complete guide to clustering anal... 40440
## 7 ANOVA in R - Stats and R 32914
如果你和我一样喜欢可视化而不是表格,下面是如何在柱状图中画出表现最好的页面的表格:
# plot
top_n(most_viewed_posts, n = 7, n) %>% # edit n for more or less pages to display
ggplot(., aes(x = reorder(Title, n), y = n)) +
geom_bar(stat = "identity", fill = "steelblue") +
theme_minimal() +
coord_flip() +
labs(
y = "Page views",
x = "Page title",
title = "Top performing pages in terms of page views",
subtitle = paste0(format(start_date, "%b %d, %Y"), " to ", format(end_date, "%b %d, %Y")),
caption = "Data: Google Analytics data of statsandr.com"
) +
scale_y_continuous(labels = scales::comma) + # better y labels
theme(plot.margin = unit(c(5.5, 17.5, 5.5, 5.5), "pt")) # to avoid the plot being cut on the right edge

作者图片
这给了我一个很好的第一手概览,关于帖子在页面浏览量方面的表现,所以在某种意义上,人们发现什么是有用的。去年排名前三的文章是:
请注意,这个排名是基于过去 12 个月的总浏览量。因此,一篇最近的文章可能会出现在列表的底部,仅仅是因为它在比旧文章更短的时间段内收集了页面浏览量。因此,最好避免将最近的文章与旧文章进行比较,或者你可以在对页面浏览量进行“时间标准化”后再进行比较。请参见上一年的评估,了解该指标的更多详情和图示。
你也可能有兴趣了解表现最差的人(以便最终改进他们或者将他们列入更高质量的帖子):
tail(subset(most_viewed_posts, n > 1000),
n = 7
)## Title n
## 49 A guide on how to read statistical ta... 1477
## 50 About - Stats and R 1470
## 51 How to embed a Shiny app in blogdown?... 1455
## 52 About me - Stats and R 1277
## 53 One-proportion and chi-square goodnes... 1226
## 54 Running pace calculator in R Shiny - ... 1049
## 55 Shiny - Stats and R 1018
请注意,我有意排除了浏览量少于 1000 的页面,以便从排名中删除已删除或隐藏的页面。
这个排名的另一个问题是,由于一些页面已经重复(例如,如果你编辑了标题),它可能会有所偏差,但至少你对表现最差的页面有一个大致的了解。
按国家列出的页面浏览量
了解你的读者来自哪个国家对一些内容创作者或营销者来说也是很方便的。
我的读者的位置对我来说并不重要,因为我打算为每个人写作,但如果你在卖东西或经营企业/电子商务,这可能完全相反。
# get GA data
data_fetch <- google_analytics(view_id,
date_range = c(start_date, end_date),
metrics = "pageviews",
dimensions = "country",
anti_sample = TRUE # slows down the request but ensures data isn't sampled
)# table
countries <- data_fetch %>%
mutate(Country = str_trunc(country, width = 40)) %>% # keep maximum 40 characters
count(Country, wt = pageviews, sort = TRUE)head(countries, n = 10) # edit n for more or less countries to display## Country n
## 1 United States 244752
## 2 India 71797
## 3 United Kingdom 53272
## 4 Germany 38322
## 5 Canada 33812
## 6 Belgium 26421
## 7 Australia 26225
## 8 Philippines 23193
## 9 Netherlands 21944
## 10 Brazil 17956
同样,如果你更喜欢图表而不是表格,你可以在条形图中显示页面浏览量排名靠前的国家:
# plot
top_n(countries, n = 10, n) %>% # edit n for more or less countries to display
ggplot(., aes(x = reorder(Country, n), y = n)) +
geom_bar(stat = "identity", fill = "steelblue") +
theme_minimal() +
coord_flip() +
labs(
y = "Page views",
x = "Country",
title = "Top performing countries in terms of page views",
subtitle = paste0(format(start_date, "%b %d, %Y"), " to ", format(end_date, "%b %d, %Y")),
caption = "Data: Google Analytics data of statsandr.com"
) +
scale_y_continuous(labels = scales::comma) + # better y labels
theme(plot.margin = unit(c(5.5, 7.5, 5.5, 5.5), "pt")) # to avoid the plot being cut on the right edge

作者图片
我们看到很大一部分读者来自美国,而比利时(我的国家)在页面浏览量方面仅排在第六位。
请注意,由于不同国家的人数差异很大,如果您按绝对数值比较不同国家的页面浏览量,这个排名可能会隐藏一些见解。在去年回顾的部分中可以看到原因。
设备的用户参与度
人们也可能有兴趣根据设备类型检查用户如何参与。为了对此进行研究,我们绘制了三张图表,描述了:
- 页面浏览量如何按设备类型分布?
- 按设备类型划分的第页上的平均时间(秒)
- 按设备类型划分的每个会话的页面浏览量
那么首先,页面浏览量是如何按设备类型分布的?
# GA data
gadata <- google_analytics(view_id,
date_range = c(start_date, end_date),
metrics = c("pageviews", "avgTimeOnPage"),
dimensions = c("date", "deviceCategory"),
anti_sample = TRUE # slows down the request but ensures data isn't sampled
)# plot pageviews by deviceCategory
gadata %>%
ggplot(aes(deviceCategory, pageviews)) +
geom_bar(aes(fill = deviceCategory), stat = "identity") +
theme_minimal() +
labs(
y = "Page views",
x = "",
title = "Page views per device",
subtitle = paste0(format(start_date, "%b %d, %Y"), " to ", format(end_date, "%b %d, %Y")),
caption = "Data: Google Analytics data of statsandr.com",
fill = "Device" # edit legend title
) +
scale_y_continuous(labels = scales::comma) # better y labels

作者图片
从上面的图中,我们看到大多数读者从桌面上访问博客,只有很小一部分来自平板电脑。
这是有意义的,因为我猜想许多访问者在使用 R(只在桌面上可用)的同时阅读我的文章或教程。
然而,每种设备类型的总页面浏览量信息并不能告诉我在每个页面上花费的时间,因此也不能告诉我每种设备类型的参与度。以下情节回答了这个问题:
# add median of average time on page per device
gadata <- gadata %>%
group_by(deviceCategory) %>%
mutate(med = median(avgTimeOnPage))# plot avgTimeOnPage by deviceCategory
ggplot(gadata) +
aes(x = avgTimeOnPage, fill = deviceCategory) +
geom_histogram(bins = 30L) +
scale_fill_hue() +
theme_minimal() +
theme(legend.position = "none") +
facet_wrap(vars(deviceCategory)) +
labs(
y = "Frequency",
x = "Average time on page (in seconds)",
title = "Average time on page per device",
subtitle = paste0(format(start_date, "%b %d, %Y"), " to ", format(end_date, "%b %d, %Y")),
caption = "Data: Google Analytics data of statsandr.com"
) +
scale_y_continuous(labels = scales::comma) + # better y labels
geom_vline(aes(xintercept = med, group = deviceCategory),
color = "darkgrey",
linetype = "dashed"
) +
geom_text(aes(
x = med, y = 125,
label = paste0("Median = ", round(med), " seconds")
),
angle = 90,
vjust = 3,
color = "darkgrey",
size = 3
)

作者图片
从上面的图中,我们看到:
- 大多数来自平板电脑的读者实际上很快就离开了页面(参见平板电脑方面 0 秒左右的峰值)。
- 台式机和移动设备上的读者平均浏览时间分布非常相似,平均浏览时间大多在 125 秒(= 2 分 5 秒)到 375 秒(= 6 分 15 秒)之间。
- 非常令人惊讶的是,访客在移动设备上花费在页面上的平均时间的中值略高于桌面上的(中值=移动设备上 316 秒,桌面上 266 秒,参见代表桌面和移动设备中值的垂直虚线)。这表明,尽管更多的人从桌面访问博客(如设备的总页面浏览量所示),但似乎移动设备上的人每页花费的时间更多。我发现这个结果非常令人惊讶,因为我的大多数文章都包含 R 代码,并且需要一台计算机来运行这些代码。因此,我认为人们在桌面上花的时间会比在移动设备上多,因为在移动设备上他们会快速浏览文章,而在桌面上他们会更仔细地阅读文章,并试图在自己的电脑上复制代码。至少我在手机上阅读博客和在桌面上阅读博客时是这样做的。更耐人寻味的是,去年就已经是这样了。如果有人发现类似的结果,并有一个可能的解释,我会很高兴听到她的意见(如果可能的话,在文章结尾的评论中,这样每个人都可以从讨论中受益)。
- 顺便提一句,我们看到今年的中位数比去年高(2021 年分别为 266、316 和 114 秒,而 2020 年在桌面、移动和平板电脑上分别为 190、228 和 106 秒)。这多少有些令人鼓舞,因为这表明人们在每个页面上花费了更多的时间(这至少部分表明了谷歌博客的质量)。 1
鉴于这一结果,我也相信通过设备类型来说明一个会话期间页面浏览的数量是有帮助的。
事实上,可能是这样的情况,移动设备上的访问者平均在每个页面上花费更多的时间,但是桌面上的人每次会话访问更多的页面(记住,会话是在给定的时间框架内发生的与你的网站的一组交互)。
我们通过密度图验证了这一观点,为了提高可读性,我们排除了平板电脑的访问:
# GA data
gadata <- google_analytics(view_id,
date_range = c(start_date, end_date),
metrics = c("pageviewsPerSession"),
dimensions = c("date", "deviceCategory"),
anti_sample = TRUE # slows down the request but ensures data isn't sampled
)# add median of number of page views/session
gadata <- gadata %>%
group_by(deviceCategory) %>%
mutate(med = median(pageviewsPerSession))## Reordering gadata$deviceCategory
gadata$deviceCategory <- factor(gadata$deviceCategory,
levels = c("mobile", "desktop", "tablet")
)# plot pageviewsPerSession by deviceCategory
gadata %>%
filter(deviceCategory != "tablet") %>% # filter out pageviewsPerSession > 2.5 and visits from tablet
ggplot(aes(x = pageviewsPerSession, fill = deviceCategory, color = deviceCategory)) +
geom_density(alpha = 0.5) +
scale_fill_hue() +
theme_minimal() +
labs(
y = "Frequency",
x = "Page views per session",
title = "Page views per session by device",
subtitle = paste0(format(start_date, "%b %d, %Y"), " to ", format(end_date, "%b %d, %Y")),
caption = "Data: Google Analytics data of statsandr.com\nDashed lines represent the medians",
color = "Device", # edit legend title
fill = "Device" # edit legend title
) +
scale_y_continuous(labels = scales::comma) + # better y labels
geom_vline(aes(xintercept = med, group = deviceCategory, color = deviceCategory),
linetype = "dashed",
show.legend = FALSE # remove legend
)

作者图片
最后一幅图显示,桌面和移动设备上的读者每次访问的页面数量大致相同,这可以从这两种分布相互重叠且相距不远的事实中看出。的确,使用台式机的人比使用移动设备的人的中位数要高,但差距很小(而且两者之间的差距比去年的调查要小)。
总结一下我们从最后三幅图中学到的东西,我们现在知道:
- 大多数读者从桌面访问博客;
- 移动设备上的读者在每个页面上花费的时间比桌面上的读者多(甚至比平板上的读者更多);
- 台式机和移动设备上的用户似乎每个会话访问了大约相同数量的页面。
有人可能想知道为什么我选择比较中位数而不是平均值。主要原因是中位数是一种更稳健的方式来表示非正态数据。对于感兴趣的读者,请参阅关于平均值和中值之间的差异的注释,以及每种度量更合适的上下文。
如果你想更进一步,你也可以执行一个学生的 t-test 来测试每个会话的平均页面浏览量在移动和桌面组之间是否有显著的差异。
浏览器信息
从更技术性的角度来看,您可能还对浏览器的页面浏览量感兴趣。我个人并不真的想知道我的访问者使用最多的是哪种浏览器(主要是因为这个博客可以在所有常见的浏览器上使用),但你们中最极客的可能就是这样。
该信息可通过以下柱状图可视化:
# get data
browser_info <- google_analytics(view_id,
date_range = c(start_date, end_date),
metrics = c("pageviews"),
dimensions = c("browser"),
anti_sample = TRUE # slows down the request but ensures data isn't sampled
)# table
browser <- browser_info %>%
mutate(Browser = str_trunc(browser, width = 40)) %>% # keep maximum 40 characters
count(Browser, wt = pageviews, sort = TRUE)# plot
top_n(browser, n = 10, n) %>% # edit n for more or less browser to display
ggplot(., aes(x = reorder(Browser, n), y = n)) +
geom_bar(stat = "identity", fill = "steelblue") +
theme_minimal() +
coord_flip() +
labs(
y = "Page views",
x = "Browser",
title = "Which browsers are our visitors using?",
subtitle = paste0(format(start_date, "%b %d, %Y"), " to ", format(end_date, "%b %d, %Y")),
caption = "Data: Google Analytics data of statsandr.com"
) +
scale_y_continuous(labels = scales::comma) # better y labels

作者图片
大多数读者使用 Chrome 、 Safari 和 Firefox 访问该网站(这是意料之中的,因为它们是最常见的浏览器)。
这是本次审查中展示的最后一个指标。当然,更多的可能性取决于你的 R 技能(主要是,数据操作和[{ggplot2}](https://statsandr.com/blog/graphics-in-r-with-ggplot2/))以及你在 SEO 或分析谷歌分析数据方面的专业知识。希望,感谢这篇评论,也可能来自去年的,你将能够使用 R 和{googleAnalyticsR}包分析你自己的博客或网站。至少,这是本文的目的。
对于那些对更简洁的分析感兴趣的人,请看我的自定义谷歌分析仪表板。 2
结束注释
我还想补充一点,这些数字不是用来比较的。每个网站或博客都是独一无二的,每个作者都是独一无二的(有不同的优先级和不同的议程),而且越多并不总是越好。感谢这个博客,我学到了很多东西,我把它用于个人目的和我的学生,作为我教学任务的一部分。只要我喜欢,只要我有时间,我就会继续写下去,不管点击量多低或多高。
感谢过去一年的所有读者,一年后再会!与此同时,如果你保持博客,我会很高兴听到你如何跟踪它的表现。
和往常一样,如果您有与本文主题相关的问题或建议,请将其添加为评论,以便其他读者可以从讨论中受益。
- 在每个页面上花更多的时间对谷歌来说是一个有利因素,因为这意味着人们会更仔细地阅读它。如果博客或帖子质量一般,用户往往会很快反弹(被称为反弹率),并在其他地方寻找问题的答案(最终导致在页面或网站上花费的时间更少)。 ↩︎
- 感谢 RStudio 博客的启发。 ↩︎
相关文章
- 如何在 R 中跟踪自己博客的表现?
- 为什么我有一个数据科学博客?分享代码的 7 个好处
- R 中的图形与 ggplot2
- R 闪亮中的抵押贷款计算器
- 如何发布闪亮的应用程序:以 shinyapps.io 为例
原载于 2021 年 12 月 16 日https://statsandr.comT22。
数据科学统计基础
入门
分析思维的基础

由 Artem Maltsev 在 Unsplash 上拍摄的照片
无论您是整天呆在电子表格还是 TensorFlow 中,成为一名高效的数据科学家都需要对统计学基础有扎实的理解。很难不写一本关于所有有用的统计数据的教科书,但在这篇文章中,我缩小了一组基本技能,无论你去哪里,它们都将帮助你开始你的角色。我们开始吧!
坚持住。我真的需要学习统计学吗?
在大数据和机器学习的时代,人们很容易对学习任何统计数据不屑一顾。当普通的笔记本电脑比让我们登上月球的计算机[1]强大 200 万倍时,将数据集投入深度学习算法比以往任何时候都更容易,在它处理数据时喝杯咖啡,然后回到总能提供震惊世界的见解的模型。对吗?嗯……不完全是。[2]
作为数据科学家,我们工作的一个核心部分是生成帮助我们理解过去和预测未来的模型。尽管获得正确的模型很重要,但是很容易创建出有严重缺陷的模型。然后基于这些有缺陷的模型,很容易向利益相关者做出自信的建议。下面的引用通常指的是进入分析或预测的数据质量,但我认为这是一个恰当的总结,说明了为什么我们也需要关心统计数据。
垃圾进,垃圾出
模型是现实的简化表示。如果这种描述是有缺陷的,它描绘的画面很容易是荒谬的或误导的。人们毕生致力于研究统计数据的原因是,将现实浓缩成模型极具挑战性,但也是必要的。
在做决定之前处理好每一个细节通常是不可能或不切实际的;例如,我们的大脑不断使用处理捷径来更快地解释世界。你不需要记住外面各种可能的温度下该穿什么衣服;你知道,一般来说,随着气温的下降,你会穿上更多的衣服。这个心理模型并不完美——有时有风或潮湿,不同的层数会感觉更舒服——但这是一个很好的经验法则。

作者图片
当我们建立一个模型时,问题不是如何建立一个没有缺陷的模型;而是如何确保瑕疵不会影响结论。风或湿度有时会改变最佳层数,但这并不能改变“天气越冷,我应该穿的衣服越多”是一个很好的模式。正如统计学家乔治·博克斯(据称是)所说:
“所有的模型都是错的,但有些是有用的。”
一个错误但有用的模型和一个只是错误的模型之间的区别通常隐藏在细节中。与编程不同,在半成品模型上点击“运行”将会输出一个在质量上看起来与高度抛光的精确模型相同的结果。但是这个模型是否代表我们实际生活的现实需要一双训练有素的眼睛。
好吧,那么我到底需要多少统计数据?
当谈到数据科学的重要统计概念时,很难不写一整本教科书。考虑到预期的统计知识深度的巨大差异,也很难确定哪些概念与数据科学家最相关。
如果你希望为公共政策或公司发展方向等重大决策提供信息,你需要的不仅仅是介绍统计数据,但如果你的角色深入数据科学的工程领域,基本统计数据可能就足够了。
同样,如果你所在的领域实际上可以访问流程中的所有数据,比如分析物联网(IoT) 传感器数据,或者将自然语言处理应用于法律文件,那么你会希望深入了解其他统计技能,比如时间序列分析、聚类和异常检测。
然后,考虑以下概念,作为起点,然后您可以根据您的特定角色进行构建和定制。我假设你对统计学有一些基本的了解,但可能还没有深入研究假设、系数、残差等的细微差别。**
这里有(一些![4])统计要点我认为任何数据科学家都应该能够轻松地向技术和非技术受众解释:
- ****实验设计:抽样和偏倚、对照组、相关性与因果关系
- 组间比较: t 检验,方差分析
- ****预测建模:回归、分类
- ****模型内部:系数、残差、p 值、R
我们将在接下来的文章中逐一讨论。我们开始吧!

试验设计
广义地说,实验设计指的是我们如何构建数据收集过程。我们是在脸书上调查我们的朋友,在商场里调查路人,还是随机的电话号码?是每个病人都得到药物,还是我们给一些人安慰剂?
把我们运行的任何分析的质量想象成一个漏斗,从我们收集的数据的质量开始。如果我们有坚实的数据,我们可以问更多有趣的问题,发现更多有意义的见解。如果我们有粗制滥造的数据,结果是否真的可信总是会有疑问的阴影。因此,让我们确保我们能够确定如何获得好的数据。
采样和偏差
要理解的一个关键概念是,当你收集数据时,你是从人口中抽样。(除了像物联网这样较新的领域)。)因为我们要把一个庞大、多样的群体浓缩成一个相对较小的样本,所以我们需要确保这个样本实际上看起来像是更广泛人群的一个缩影。

作者图片
例如,在上图中,我们的样本并不能真正代表总体——有几种颜色根本不存在!我们不能对这个样本进行分析,然后推广到整个人群;我们只能概括为红色、橙色、黄色和绿色。无论我们对样本数据的建模有多完美,我们的模型的范围都是有限的。如果我们试图对更广泛的人群进行评论,我们会发现我们看似准确的模型突然做出令人尴尬的不准确预测。
这种样本人口差异的最近一个例子是 2020 年美国大选预测。在特朗普总统在 2016 年出人意料地赢得藐视绝大多数民意调查后,民意调查者花了数年时间调整他们的模型,修复盲点,为 2020 年的救赎做准备。然而,随着各州于 11 月 3 日开始发布结果,我们发现自己再次看到民调低估了特朗普选民的数量。
Civis Analytics 的前政治数据科学主管 David Shor 认为,这些预测之所以如此离谱,是因为它们的潜在样本并不能代表美国选民。 简而言之,对民意调查做出回应的人往往在社会信任度上得分较高,而综合社会调查显示,这仅代表了 30%的美国人。直到 2016 年,这一群体过去的投票与不接民意调查电话的低信任度选民相当——现在,低信任度选民倾向于更保守地投票,因此在样本中代表性不足。
如果我们意识到这些差异,我们可以尝试实现一些修正,比如在样本中对类进行差分加权。但是最好的补救办法是确保样本真正代表更广泛的人口。注意:这通常是说起来容易做起来难!
对照组
实验设计需要知道的另一个关键概念是对照组。通常,当我们进行实验时,我们希望量化某些治疗的效果。抗抑郁药能减轻抑郁吗?新的网站布局增加了销售额吗?为了理解我们得到的效果大小的数字,我们需要一个基线来与之比较。这就是控制组介入的地方。

图片改编自 Kumar 等人 2013 年
在现实世界中,无数的因素影响着我们观察到的每一个过程。我们需要一种方法来控制尽可能多的这些因素,这样我们就可以专注于我们感兴趣的一个因素——我们的治疗。
****把一个好的对照组想象成我们治疗组的一个(几乎)同卵双胞胎,不同的只是我们的治疗方法。“减去”控件,就像上面的背景减法一样,让我们处理的效果突出出来。(或者不是,如果我们的治疗实际上没有效果的话。)
安慰剂是控制的典型例子。检查新药有效性的医学研究总是包含一个得到安慰剂而不是真正药物的对照组,因为人们通常只知道他们得到了一种药物就会感觉更好,即使“药物”只是一颗糖丸。如果没有安慰剂组,我们的假阳性率会高得惊人。
另一个经典的对照组是治疗组本身,在接受治疗之前。受试者内部设计非常强大,因为我们可以更好地控制所有可能影响我们实验的外部因素:他们实际上是相同的参与者!
您可以在双样本 t 检验与配对 t 检验的等式中看到这种额外的功效:配对检验的 t 值将更大,因为分母更小,因为您只计算一个(配对)样本的 n 。[5]

最后一个与 web 开发特别相关的例子是 A/B 测试。为了实验性地确定提高用户参与度或转化率的方法,公司可以向用户呈现几乎相同的网页版本,仅在一个方面不同,如按钮的颜色。然后,该公司可以将这些网页变体相互比较,并与原始网页(对照组)进行比较,以选择最有效的选项。
相关性与因果关系
在分析数据时,通常会看到两个变量相关:当 A 发生变化时,B 也会发生变化。例如,我们可能会注意到冰淇淋和防晒霜的销售整齐地一前一后,但是这是否意味着冰淇淋的销售导致了防晒霜的销售?(“我想要一勺巧克力片,嗯……我们也买些防晒霜吧。”)
弄清楚冰淇淋销量的变化是否导致了防晒霜销量的变化(或者反之亦然),有一些隐藏的因素影响着两者,或者这只是一个随机的巧合是实验设计的工作。要真正说 A 导致 B,我们需要控制 A 和 B 的外部变化*,然后仔细操纵 A 并观察 B。例如,我们可以全年进行冰淇淋营销闪电战,无论天气如何都推动销售,并观察防晒霜的销售是否随之而来。*****
注意,说 A 和 B 是相关的没有错。如果有相关性,它仍然告诉我们关于 A 和 b 的一些事情。但是如果你想说一个导致另一个的话,这个障碍要高得多。

Ludemeula Fernandes 在 Unsplash 上拍摄的照片
组间比较
统计学以及生活中的一个核心问题是事物是相同的还是不同的。吸烟者患肺癌的几率是否比不吸烟者高?早上吃一个苹果比吃一个橘子更有效率吗?当我们收集吃苹果和吃橘子人群的数据时,我们的样本的方法将不可避免地不同。但是吃苹果的人和吃橘子的人的生产力有区别吗?
我们需要使用统计学从样本中对总体进行推断。下面我将简要介绍 t 检验和方差分析。
t 检验
双样本 t 检验背后的主要思想是确定样本是否来自同一人群。我假设这不是你第一次阅读关于 t-test 的文章(如果是的话,有很多很好的资源,比如这篇文章,所以我将关注如何避免误用 t-test。
当你进行 t 检验时,你假设你的数据如下:
- 样本中的数据是连续的 T21,而不是离散的
- 你的样本中的数据彼此独立并且都同样可能从它们的总体中选择**
- 你的样本没有偏斜,也没有异常值(随着样本量的增加越来越不重要)
- 对于双样本检验,总体分布的方差是相等的
如果这些条件都不满足,不要运行 t-test!* R、Python 和 t-test 等式本身都不会阻止你生成一个无意义的结果—实现是否应该运行测试取决于你自己。*
第二点对粗心的研究人员来说尤其具有毁灭性;违反这个假设意味着你不得不使用一些粗糙的高级方法或者丢弃数据并再次尝试。#3 更慷慨:可以使用非参数替代方法,如 Wilcoxon 检验或转换你的数据使其呈正态分布。
方差分析
如果你同时比较两个以上的样本,你需要运行一个方差分析。不要进行多次连续的 t 检验! 我在这里深入研究了当你对多个组进行连续的两两 t 检验时,假阳性率是如何飙升的。我认为下面的热图很好地总结了主要信息。

作者图片
简而言之:如果你试图确定多个样本总体的均值是否不同,首先运行 ANOVA,然后是 Tukey 的方法或 Bonferroni 的校正,如果你发现显著差异。

预测建模
预测建模是关于**获取数据并尝试对生成该数据的潜在过程进行建模。**一旦我们理解了底层规则,我们就可以为新数据生成预测。回想一下我们的室外天气与服装模型,我们不需要记住在每一个可能的温度下穿什么衣服;我们只需要使用我们的心智模型。
本节将介绍回归和分类。但是在我们开始之前,一个快速的专业提示:在你开始构建任何模型之前,一定要绘制你的数据!这一步可以帮助您捕捉异常值,确定是否需要像日志转换这样的特征工程步骤,并确保您的模型实际上描述了您的数据。
回归
当我们想要预测一个连续的值时,我们使用回归。这是线性回归的方程式。好好学吧!

这里 h ( x )是我们的预测值, n 是我们数据中的特征数。我们根据学生的学习时间( x ₁)和前一天晚上的睡眠时间( x ₂)来预测学生的考试分数( h ( x ),这个模型的等式如下所示[6]:

无论你在哪里工作,都很难逃脱一个好的线性回归模型的简单和方便。线性回归的计算速度非常快,也很容易解释:系数给出了每个变量如何影响输出的清晰解释[7],你只需将所有的 βjxj 相加就能得到你的输出。确保你为回归准备好了“30 秒的演讲”,因为你可能要向不同的利益相关者重复解释这些模型。
一旦你适应了,确保温习更高级的主题,如特征缩放、交互和共线性,以及模型正则化和系数如何计算。这听起来可能很多,但是考虑到你可能经常在工作中运行和解释回归,真正理解它们是关于什么的是有好处的。
分类
分类模型预测不同的输出类别。上述模型的逻辑回归版本,我们现在根据学生学习和睡眠的小时数来预测他们是否通过考试,看起来像这样:

这里h(x)=β0+β₁x₁+β₂x₂和 y 是通过考试的事件。[8]
给定我们的预测器,我们的模型将输出 y 发生的概率。我们可以直接处理这些输出的概率(如在信用违约风险模型中),或者我们可以将它们二进制化为 0 和 1。在我们的学生模型中,这意味着预测学生是通过了考试(1)还是没有通过考试(0)。我们通常使用 P ( y ) = 0.5 作为概率截止值。
让我们快速浏览一下逻辑回归的两个重要概念:
- 理解 h ( x )的值如何转化为概率 P ( y )
- 理解决策边界
将 h(x)转化为 P(y) 将 h ( x )设置为极端有助于阐明其在等式中的作用。假设 h ( x )是极负。这将意味着 h ( x )将是正的,这将使 1+e−*h*(*x*)*巨大的*。例如,如果*e*−h(x)是 10000000,我们看到 P ( y )几乎为零。

在另一个极端,如果 h ( x )是极正的,那么 h ( x )变成极小的,这意味着我们实际上是在用 1 除 1。当e^−h(x)为 0.0000001 时,我们看到 P ( y )差不多为 1。

最后,当 h ( x )等于零时会发生什么?任何实数的零次方都等于 1,所以e^h(x)变成 1。

当 h ( x )等于零时, P ( y )等于 0.5。如果我们使用 0.5 作为概率截止值,这意味着如果 h ( x )为正,我们将预测学生通过。如果 h ( x )为负,我们将预测该学生失败。这很好地引导我们进入下一部分…
搞清楚 h(x) 那么 h ( x )是怎么回事呢?简而言之,当 h ( x ) = 0 时,我们得到一条最好地将我们的数据分成类的线。*训练逻辑回归模型就是识别在哪里放置这条线以最好地分离数据中的类。*

作者图片
在上图中,我们绘制了一些通过与未通过考试的学生的虚假培训数据。蓝线是模型的决策边界,它根据 x ₁和 x ₂.确定了“通过”与“失败”类别的最佳分离
对于落在决策边界左侧的任何新数据,我们的模型将预测该学生失败。对于任何落在右边的新数据,我们的模型将预测学生通过。
它并不完美——有些“及格”的学生在左边,有些“不及格”的学生在右边——但这是该模型能想到的最好的区分。
一旦你熟悉了这些主题,就可以向两个以上类别的逻辑回归模型迈出一小步,例如多项式和一对其余分类。

模型内部
一旦我们实际上适合了一个模型,它就在 R 或 Python 中,我们实际上有什么呢?我们如何判断哪些特征是重要的,以及模型是否真正解释了我们的目标变量的变化?本节将研究系数和残差,以及 p 值和 R 背后的含义。
系数
我们再来看看预测学生考试成绩的线性回归模型。

截距( β ₀)、学习乘数( β ₁)、睡眠乘数( β ₂)是我们模型的系数。这些参数将我们的输入(学习时间和睡眠时间)转换成输出(考试分数)。例如,₁的系数 10 意味着学生每多学习一小时,分数就会增加 10 分。截距为 30 意味着如果学生根本不学习或睡觉,他们将得到 30 分。
模型系数有助于我们理解数据中的趋势,比如多学习一个小时和多睡觉是否会导致更高的考试分数。但是在接受我们的模型之前,我们应该总是仔细看看系数。
当我检查一个模型时,我总是试图在精神上验证每个系数的强度和方向,确保它与我预期的差不多,如果不是,就仔细看看。例如,负的睡眠系数 β ₂会表明我们的数据有问题,因为睡眠应该提高考试成绩!(如果不是,可能是我们的学生或者他们参加的考试很奇怪……)
类似地,如果我们的截距大于 100,并且学习和睡眠系数为负,我们可能数据太少,或者有异常值劫持了我们的模型。确保绘制你的数据,以确认趋势确实是你认为的那样。
最后,在接受系数之前,我们应该始终查看系数的置信区间。例如,如果区间过零,我们的模型就说它不能确定我们的特征影响目标变量的方向。除非你有充分的理由保留该特性(例如,特别表明它缺乏影响力),否则你应该将其从模型中删除。
同样,如果区间没有过零,但相对于系数的大小仍然很大,我们的模型表明它不能精确定位我们的特征对目标变量的具体影响,因此我们可能需要更多的数据或不同的模型公式来理解这种关系。
残差
一旦我们建立了一个模型,我们如何判断它是否好呢?一种方法是将模型的预测与我们数据中的实际值进行比较。换句话说,给定一些样本输入,模型认为输出是,而认为输出实际上是?对于回归模型,残差是预测值与实际值之间的距离。[9]

作者图片
你可以在上图中看到这一点。预测值(红线)和实际值(黑点)之间的距离是残差。建立模型的目标是获得尽可能相似的预测值和实际值——换句话说,就是最小化残差。[10]与不准确的模型相比,更准确的模型往往会产生更接近实际值的预测。
特别是对于线性模型,残差应该正态分布在零附近,这意味着我们的预测通常很好,但有时有点太高或太低,很少太高或太低。

作者图片
作为本节的第三个提醒,绘制数据图很重要!R 和 Python 不会阻止你拟合一个没有意义的模型,如果利益相关者在你的模型中发现你没有发现的逻辑漏洞,他们会很快对你的建议失去信心。(说服涉众相信一个具有无懈可击的逻辑的模型通常已经够难的了…不要让它变得更难!)
例如,假设你建立了一个模型来预测一个人有多快乐作为他收集的神奇宝贝卡片数量的函数。你在模型预测的基础上绘制数据,绘制残差,然后看到如下结果:

作者图片
两组点和双峰残差清楚地表明,有一些未解释的因素影响了我们的数据…也许这个人是儿童还是成人!一个简单的解决方法是在我们的模型中添加一个“儿童对成人”的特性,或者将模型分成一个儿童模型和一个成人模型。

作者图片
好多了!
p 值
p 值是一个棘手的问题。鉴于他们作为重要结果守门人的地位,研究人员面临着巨大的压力来“黑掉”分析,使他们的模型输出低于 0.05 的值,这是普遍接受的阈值。例如,来自Perneger&combes cure 2017的下图是来自四种医学期刊的 667 个报告 p 值的分布。请注意低于 0.05 的 p 值和高于 0.05 的 p 值之间的惊人差异…

但是,直到每个人都转向贝叶斯统计,p 值会一直存在,你需要理解它们。假设零假设是正确的,p 值的正式定义是获得至少与我们的结果一样极端的结果的概率。我们的零假设通常是真实效应大小、总体间均值差异、变量间相关性等。是否为零。p 值是一种普遍接受的方法,通过这种方法,我们可以说我们观察到的差异是:
- 大到足以拒绝零假设*(意味着我们观察到的模式具有统计显著性**)*
- 不足以拒绝零假设*(意味着我们观察到的模式只是噪声**)。*
请注意这里小心的措辞。你需要谦逊地对待统计数据;从我们目前的数据中得出的结论是我们对更广泛的人群的最佳猜测,但是当未来有更多的数据可用时,这个猜测可能会被证明是错误的。
即使有了“显著”的 p 值,我们可能还是错了! 我们的接受门槛是也是我们的误报率。换句话说,在截止值为 p = 0.05 的情况下,预计有 5%的重要结果实际上是假阳性。如果您的分析结果是重要决策的依据,我会将 p 值阈值设置为 0.01 甚至 0.001。
回到我们的“学习和睡眠”模型,如果我们看到我们的学习系数 β ₁的 p 值是 0.0008,而我们的睡眠系数 β ₂的 p 值是 0.26,我们会得出结论,学习会影响考试成绩,而睡眠不会。
稀有
在我们结束这篇大文章之前,最后一个概念。r 平方是确定我们的模型是否真的好的一个有价值的度量。简而言之,R 是我们的目标变量的变化比例,由我们的预测变量的变化来解释。度量范围从 0(我们的模型对我们的目标没有任何解释)到 1(我们可以完美地预测我们的目标)。**
在某种程度上,R 越高越好。我们的“学习和睡眠”模型的 R 为 0.8,这意味着学习和睡眠可以解释我们数据集中学生考试成绩变化的 80%。也许像他们是否吃早餐这样的额外特征可以将我们的 R 提升到 0.85,这意味着我们的模型在解释分数变化方面更好一些。
事实上,如果我们有很多特征可供选择,我们可以进行特征选择来确定哪些特征最有预测性。
但是一旦我们开始得到大于 0.97 的 R,我敢打赌我们会遇到以下问题之一:
- 在我们的模型中有太多的特征,而且这个模型对我们的数据来说太多了
- 一开始我们的数据太少了
- 一些特征可能通过以另一种形式作为目标来“窥视”目标变量(例如包含目标的工程特征)
现实世界很乱,很难浓缩成一个模型。除非我们在模拟宇宙的物理规律,否则在我们的模型中总会有我们无法解释的变化。也许学生的铅笔在考试中途断了,这让他们失去了兴趣。也许有一个学生实际上是一个 x 战警,可以偷偷阅读答案。
这再次反映了我们在谈论统计数据时需要的谦逊:我们的模型是解释现实世界的最佳猜测。

总结想法
唷,那是一阵旋风!重申之前的话,不写一本大规模的教科书写出对数据科学有用的统计数据是有挑战性的…但是我们至少可以写一篇大规模的博客文章。
Stats 是一系列用于从我们的数据中的噪音中解析信号的工具,你拥有的工具越多,你可以处理的问题类型就越多。但是当然,有了这么多的数据,我们需要选择先学什么。
这篇文章关注的是基础知识,而不是最新的前沿库,因为基础知识不会有太大的变化,高级主题建立在核心概念的基础上。把这些真正理解好了,剩下的就水到渠成了。**
最好,
马特
脚注
1.坚持住。我真的需要学习统计学吗?
阿波罗制导计算机有 4.096 千字节的内存。2021 年,一台普通笔记本电脑拥有 8 GB,这是其 195 万倍的性能。如果我们使用一个内存优化的 AWS EC2 实例,我们可以获得比阿波罗任务多 15.7 亿倍的计算能力。所有这些都是为了识别猫的照片…
2.坚持住。我真的需要学习统计学吗?
大数据没有消除对统计的需求,反而加剧了常见的统计风险。我在下面链接了一些进一步的阅读。**
3.好吧,那么我到底需要多少统计数据?
如果人们向你寻求帮助,从数据中做出关键决策,你会想要考虑统计上的细微差别,如随机效应、回归不连续性、非参数或贝叶斯替代频率主义、自举等等。
4.好吧,那么我到底需要多少统计数据?
在写这篇文章的时候,我一直在想象统计专家批评这里的遗漏,或者那里的细节太少。经过几周的写作,我决定不再深入研究 AIC 、聚类、高斯分布以及其他一些话题。我们必须在某个地方停下来!我只是说这是一个不完整的列表。把它当作一个起点;根据需要添加到您的曲目中!
5.对照组
在 R 或 Python 中,您可以很容易地看到这种效果。(下面我用 R。)注意:将g2设置为等于向上移动的g1非常重要,而不仅仅是另一个具有更高平均值的rnorm,因为成对 t 检验密切关注向量中每个元素之间的成对差异。如果g1和g2是不相关的样本,配对 t 检验将可能返回比双样本检验更弱的结果,因为g1 - g2的分布跨越零。在 #pruittgate 惨败……之后必须注意的是,在科学研究中永远不要捏造数据!!这些演示的目的严格来说是为了更好地理解统计测试是如何工作的,而不是为了玩弄一个系统。**
6.回归
如果你仔细观察(或者用线性代数做了足够的统计),你可能会注意到

期望有一个 x ₀陪伴 β ₀,但是在我们的示例模型h(x)=β₀+β₁x₁+β₂x₂.的等式中没有 x ₀
这反映了我们是否使用矩阵乘法来生成预测的差异。使用线性代数的一个必要的预处理步骤是在我们的特征矩阵中添加一列为 1 的( x 0 );否则,特征的数量( n )和系数的数量( n +1)不匹配。因为 x ₀总是 1,只是一个簿记步骤,当你展开它时,它通常会从等式中省略。
7.回归
这里有和一个关于理解回归模型系数的容易程度的重要警告。是的,它们确实显示了每个输入变量对输出的影响,但是这些系数受到模型中所有其他变量的影响。
例如,在我们的“学习和睡眠”考试模型中,从我们的模型中删除“学习时间”将导致“睡眠”系数飙升,因为它现在完全负责将“睡眠时间”转换为考试分数。
您会发现,当您添加或删除预测值并重新运行模型时,变量的系数会缩小、爆炸,甚至改变符号。试图理解这些变化是你需要深入理解你的数据的地方。
8.分类
请注意,“成功”与“失败”的概念完全是任意的。如果在我们的数据中1对应于通过考试,而0对应于失败,那么我们的“成功”就是通过考试,并且我们的模型输出通过的概率,给定我们的预测器。
但是如果我们翻转1和0,那么我们的“成功”就变成了考试失败,我们的模型只是输出失败而不是通过的概率。
9.残差
分类模型的“实际与预测之间的距离”更简单——唯一的选项是分类是正确还是不正确,即使对于多类分类也是如此。衡量分类模型准确性的一个重要标准是 混淆矩阵 ,它提供了大量关于你的模型的准确性、假阳性和假阴性率等信息。
10.残差
我在这篇博文的中详细讨论了最小化残差,在这篇博文中,我手工重建了 R 的线性回归函数。如果您想深入了解如何确定线性回归模型的系数,请查看该网站。
棒球迷统计:投球版
一名数据科学家表示,作为一名普通球迷,ERA 是最重要的统计数据

又到棒球时间了。在我写作的地方,芝加哥,雪已经开始融化,小熊队在亚利桑那州的春训中给了我们希望。
当然,是时候掸掉统计记录的灰尘,和数据科学家一起看看棒球和统计数据了。
正如我在之前关于进攻统计的博客中所描述的,Moneyball 的统计学家会用不同的标准来评估一个球员的技术。投球也不例外。像“每 9 局允许的得分”和“调整后的胜数高于替补数”这样的指标允许那些分析师通过控制球队的防守能力或投手进入比赛时的比赛状态来了解投手的真实影响。
不幸的是,这些指标在国家电视广播中是不可用的,你将无法在观看比赛直播时计算它们。所以普通球迷在评估 MLB 投手时应该注意什么!?在这篇文章中,我解释了要关注的单一指标——在统计学的帮助下。
我为什么要写这个?
一篇棒球文章怎么会出现在数据科学家的博客上?这是因为我把我从小热爱的东西(棒球)和我建立职业生涯的东西(数据科学)融合在了一起。)
我从小就是一名棒球卡收集者。我父亲用他辛苦挣来的钱,时不时地在下班后给我买一两包,和一个玩少年棒球联盟并喜欢上这项爱好的儿子分享。
棒球卡上写满了数字。分解玩家如何玩游戏的每个小细节的指标。我会比较和对比我最喜欢的球员,一些最明显的数据会跳出来。佐治亚州的大个子棒球运动员弗兰克·托马斯会击出无数个本垒打,他的牌会证明这一点。诺兰·莱恩是德克萨斯的一名投手,他会有一大堆的三振出局数。
即使是那些把这些数据当做爱好的人也不知道应该关注什么来理解游戏中的最佳球员。
今天的博客是我上一篇博客的续篇,着眼于比赛中最好的击球手;我会让最普通的粉丝简单地回答这个问题:
要了解你队中最好的投手,你需要关注的一个统计数据是什么?那个统计是玩家的时代;但是让我们开始一段旅程,用统计学来理解为什么会这样。
TLDR:你最喜欢的球队中 ERA 数据最低的球员很可能是他们最好的投手。当你听到他们的入场音乐时,记得起立欢呼。
投球统计是信息过载。

投球统计是疯狂的。美国职业棒球大联盟使用高性能摄像机来收集在所有美国职业棒球大联盟体育场投掷的速度和旋转。同样的系统也测量每个球员在球场上的位置。
作为一名分析师和数据科学家,这个数据让我着迷。作为一个只想享受比赛的球迷,它在电视上看起来很酷——但没有办法处理所有的数据。作为一个不经意的球迷,碰巧有一个衡量标准可以让你了解任何球队的最佳投手。
当然,要了解谁是最好的,你可以依靠经理的决定。他们把谁放在轮换阵容的最前面,投手在比赛结束时要求谁上场,或者在与对手的关键比赛中谁是首发。那也行,但是我们是分析师,我们想知道为什么做出那个决定。或许还能理解谁该被叫到替补席上。那么,我们如何穿过噪音,评估那个阻止我的球队得分的球员呢?
首先,我们必须解决一些关于统计和棒球的问题。
为了有效地分析数据,我们必须处理棒球中的人为因素。
用投手的个人表现来理解投球数据的价值是有问题的。

玩家会生病。他们休假。经理让他们退出比赛,因为他们打得不好。有些人在比赛前一晚玩得太开心了。⁴
对于我们的投手分析,我们需要再次控制人为的可变性。我们不分析投手,而是分析球队的投球能力,以确定需要关注的指标。
我们关注常规赛球队的表现,因为平均而言,在 MLB 常规赛中的球队是可比的。球队通常打相同数量的比赛,有相同的机会得分,也有相同的机会得分。数据正常。标准化数据是统计学家最好的朋友。
这是球员表现与团队水平相同指标的对比图。一个显示了各种各样的个人球员的表现,许多球员得分很少,一些惊人的球员得分超过 2000 分!另一个显示团队表现趋于正常,分布在一个赛季中每个团队的平均 700 分左右。

作者图片
投球=不让对方得分。
Dictionary.comT2 这样定义棒球:
(棒球)两个九人队之间的一种球类运动,通常在一个场地上打九局,场地的焦点是一个菱形内场,有一个本垒板和三个相隔 90 英尺(27 米)的其他垒,形成一个必须由跑垒者完成才能得分的循环,中心进攻动作是用木制或金属球棒击打投球并跑垒,得分最多的一方获胜。
最好的投手应该是在防止球队得分方面做得最好的投手。—我
我们专注于得分,因为这是棒球唯一最重要的目标——击中跑者得分。得分最多的队赢得比赛。投手正在阻止这种情况发生。在对投手的这种分析中,我们的目标函数将是对投手所在球队的得分(即,得分)。)我们的目标是找到与跑垒最相关的投球统计。我们的假设是,一个投手在没有保送球员的情况下防止安打的能力将是一个强投手的信号。
球队中最好的投手可能有最低的 ERA。
Chadwick 棒球 Database⁵包括我们将用于此分析的原始统计数据。它不包括被认为是评估投手 talent⁶.的关键指标它也不包括显示在棒球记分牌上的指标。因为,作为一个普通的球迷,我们可以看到记分牌,基本的投球比率是这次分析的目标。潜在的度量包括平均得分率(ERA)、每局保送加安打数(WHIP)、每 9 局安打数(h/9)、三振出局百分比(K%)以及处理或放弃的安打统计的其他常见投球度量(K、BB、H、2B、3B、HR 等)。)
由于 Chadwick Baseball 数据库不包含计算的指标,我们将通过计算来包含这些指标。
计算出的投球统计数据及其计算如下所示:
鞭笞=(保送+安打)/投球局数
K/BB 比率=三振/保送
ERA =得分/投球局数* 9
在这个分析中,我通过删除一些异常的季节进一步规范化了团队数据。以下是我的团队剔除异常值的全部细节:
- 删除 1970 年之前的球队:1970 年赛季之前没有跟踪几个关键指标(包括牺牲苍蝇、被投球击中和其他)。)我们也知道规则的改变使得 1970 年成为一个很好的转折点来规范投球数据。
- 删除了比赛次数少于 158 场的团队赛季:我想删除因罢工和其他日程古怪而缩短的赛季。
- 删除了不在国家或美国联盟打球的球队:我们的重点是美国职业棒球大联盟,在投手的最终评估中包括非裔美国人联盟。
有了干净的数据,我们运行一个简单的相关性统计,将常见的投球指标与投手团队的得分相关联。分析表明 ERA 是最重要的指标,相关系数为 0.982。这很有意义,因为平均得分显示了一个投手/球队平均得了多少分。我们的结论:时代越低,投手越好。

作者图片

作者图片
ERA 有史以来最佳投手,带调整
既然我们知道我们应该最关注 ERA,那么根据这个统计数据来看谁是有史以来最好的球员是很自然的事情。如果我们的分析中有很多名人堂的天才,我们就一定会有所发现。
不幸的是,这并不像计算每个玩家的纪元那么简单。
如果我看看棒球历史上球员的最低年龄,我注意到一个非常有趣的趋势…

作者图片
现代投手比 1871-1968 年的投手有更高的自责分率!什么?为什么会这样?
答案是:在早期棒球史上,规则(或者说缺乏规则)给了投手优势。
来自“死球”时代(1871-1920)甚至“黄金时代”(1921-1968)的球员以他们使用现在是非法的投球技术而闻名。

何塞·弗朗西斯科·莫拉莱斯在 Unsplash 上拍摄的照片
已知死球投手使用吐球。一种投球,现在是非法的,在棒球运动中不可预测地移动并损害击球手的生产力。
黄金时代的投手有较大好球带的优势。1968 年,好球带缩小,对投球技术的限制更加严格。
为了选出任何时代的最佳投手,我们需要调整他们的时代,以适应这些规则的变化。幸运的是,我们的统计“印章”将有助于做到这一点。
线性模型
为了确定我应该调整多少死球时代和黄金时代的投手,我们需要使用一个模型,可以说明投手投球的年份的影响。因此,我们返回到我们的团队数据,并使用线性模型来确定对主要在每个时代投球的任何投手的适当调整。
我的方法是添加一个虚拟变量。如果投手在死球时代的出场次数超过 50%,他们在“ap_era_deadball”功能中被分配“1”。如果一个投手在黄金时代出场超过 50%,他们在“ap_era_golden”功能中被分配“1”。所有其他投手都被定为 0 分,因为他们是现代投手。
我们使用线性回归模型来了解在棒球的死球和黄金时代,我们应该调整每个投手的投球量。
我的模型的输出如下:

作者图片
你会发现这个模型没问题。我们的每一项统计数据都具有统计学意义;但模型只解释了 19.8%的方差。然而,系数是最重要的输出。它们具有统计学意义,并给了我们一种调整早期投手的方法,这样现代投手就有公平的机会成为历史上最好的投手。
我的调整:
“死球”时代投手的职业生涯时代将增加 0.83529。
“黄金”时代投手的职业生涯时代将增加 0.15738。
“现代”时代的投手对他们的职业时代将没有调整。
不多说了,根据我的分析,下面是历史上最伟大的 15 名首发球员的名单。

作者图片
我童年的英雄,诺兰·莱恩,根据时代进行了删减。前 15 名没有现役首发;格罗弗·克利夫兰“皮特”亚历山大是首发之王。
根据我的分析,这是前 15 名救援队员的名单。

作者图片
拥有有史以来最快快速球记录的投手阿洛迪斯·查普曼是最佳中继投手。他也是纽约扬基队的一名活跃球员,前芝加哥小熊队队员,准备在 2021 年向我们展示他的东西。
根据我的分析,最后是前 15 名结案者。

作者图片
一个有争议的选择,克雷格金布雷尔,坐在关闭名单的顶部。大多数人会反对我写这个,但马里安诺·李维拉紧随其后——所以给我一点信任。任何我个人投票选出的游戏中最好的小胡子,由 Rollie Fingers 拥有,在 15 个关闭者名单中排名第 11。
最后
自责分率是一个经过检验的真实指标,当关键时刻到来时,它会告诉你哪些投手最擅长防止得分。
我的建议是留意你团队中 era 最低的投手。那些投手是你阻止对方击球手得分的最好机会。

作者图片
[1]“棒球。”Dictionary.com 2021。https://www.dictionary.com/browse/baseball(2021 年 3 月 23 日)
[2]E baccelleri,DH,喷壶和……最少三个击球手?主要规则变化简史 (2019),《体育画报》杂志(2021 年 3 月 23 日)
[3] 最快棒球场(男) (2010),吉尼斯世界纪录有限公司(2021 年 3 月 23 日)
[4]M . feinstand, CC Sabathia 不记得季后赛前的巴尔的摩本德,他说喝酒并没有影响他为洋基队投球:“我就像一个酒鬼” (2015),《纽约每日新闻》(2021 年 3 月 24 日)
[5]https://github.com/chadwickbureau/baseballdatabank(2020),查德威克棒球统计局(19,2021 年 3 月)
[6] 美国职业棒球大联盟的统计词汇 (2021),美国职业棒球大联盟(19,2021 年 3 月)
与神经科学保持同步:2021 年 4 月必读
麻省理工学院和突触强化🎰、神经调谐和代表性几何🏗皮质神经元是贝叶斯最好的朋友🤝👊

你为什么应该关心神经科学?
神经科学是当今人工智能🧠的根源🤖。阅读并意识到神经科学中的进化和新见解不仅会让你成为一个更好的“人工智能”的家伙😎而且还是一个更好的神经网络体系结构的创造者👩💻!
在这里,我列出了 3 篇值得这个月阅读的论文,它们涉及神经元和认知。第一部分对神经动力学有所启发,这可能有助于开发新的基于马尔可夫链的神经网络。第二篇是关于神经编码和统计背后的数学的精彩综述——从数学的角度来看,这无疑是一个更好地理解神经元行为的好机会。最后,论文用提出的神经网络模型证明了皮层神经元的贝叶斯行为。享受:)
丘脑皮层对解决神经系统信用分配的贡献
米安·布拉巴·王,迈克尔·哈拉萨, 论文
神经科学的标志之一是学习在不确定的环境中选择哪种行动。当动物探索陌生的环境时,它们倾向于强化导致意外回报的行为。这种加强来自突触连接的变化。特别是,一个最受认可的模型表明,多巴胺(DA)携带奖赏预测误差信号(RPE),在基底神经节(BG)的神经支配水平上起作用。然而,是什么让这种电路加强了适当的连接仍然是一个谜。这种强化意味着理解什么是正确的线索、背景和行为,这是一个被称为信用分配问题的问题。此外,这种问题可以分为结构性信用分配、情境性信用分配和时间性信用分配。在结构任务中,我们需要理解导致奖励的线索,而不是干扰物线索;语境任务可以被认为是学习新的东西而不忘记通常的行为——例如,一个在美国的人在过马路前学会向左看,但是,在英国呆了一段时间后,这个人在回到美国时不应该忘记向左看的行为;时间上的信用处理的是回报远在时间上的决策。一个例子是围棋的游戏,几百手之后会有一个奖励显露出来,可以得到职业棋手的认可。
应对这一挑战的人工帮助可能来自机器学习。ML 模型解决信用分配问题的方法是反向传播。反向传播允许根据数据训练神经网络,加强突触连接的权重。虽然反向传播的实现是通用的,但这种方法通常会遇到一个被称为灾难性遗忘的问题。事实上,随着时间的推移,ML 模型往往会忘记他们所学的内容,尽管一些计算昂贵的解决方案正在试图解决这个问题。作者认为人类的大脑可以被看作是以一种特殊方式组合在一起的特殊硬件的集合。专门的硬件安排可以在大脑中发生,其中丘脑及其与基底神经节(BG)和皮质的相互作用作为这三种类型的信用分配的系统级解决方案。
事实上,作者提出,基底神经节的强化过程选择丘脑控制功能,随后激活皮层关联,以允许跨不同背景的灵活映射。这个过程可以简化为三个步骤:
- 首先,一个基本的学习发生在基底神经节,在皮质纹状体突触中,正如 DA 的水平所强调的。这是一个快速学习系统,但是推广起来非常慢
- 另一方面,皮层可塑性在一个慢得多的时间尺度上运作,但允许灵活的行为和快速的概括:元学习
- 丘脑学习这两种动态,并提供控制节点,使皮质活动关联空间参数化。
作者强调了这一工作流程,描述了文献支持的三个主要特征:1) BG 可以指导丘脑皮层结构的可塑性,特别是在初始学习中;2)丘脑皮层结构在不同的时间尺度上巩固了对丘脑控制功能的 BG 选择,以实现元学习;3)如图 1 所示,丘脑选择性地放大功能性皮质连接,作为情境信用分配和灾难性遗忘的解决方案

图 1 对比信用分配问题,提出了人脑的作用机制。在任务 1 中,BG 选择放大相关皮层子网络的丘脑神经元。这发生在快速时间尺度上,这允许系统巩固灵活的切换行为。当下一个任务,即任务 2,到来时,BG 选择其他丘脑神经元,因为前一个神经元受到保护,不会被覆盖。与此同时,一个缓慢的时间尺度正在发挥作用,让皮层神经元发展出一种与任务相关的共享表征,这种表征可以在不同背景下进行归纳。这使得动物可以在不同的任务之间自由转换,而不会忘记之前的任务。
总之,与传统的丘脑的中继观点相反,作者提出丘脑皮层相互作用是元学习的位点,其中丘脑提供皮层控制功能,如感觉过滤、工作记忆门控和 os on* 。 BG 在两个特定的时标上工作,选择特定的丘脑控制功能,实现元学习,解决学分分配问题。较快的可塑性学习上下文关联,以实现快速的行为灵活性,而较慢的可塑性建立概括的皮层表征。*
神经调谐和表征几何
尼古拉·克里格斯科特,薛-韦辛, 论文
这是一篇精彩的综述,对于感兴趣的读者来说,这是一个了解更多关于神经调谐及其几何表示的绝佳机会。神经元可以用调谐来表征,作为对刺激的反应。调谐曲线继续为单个神经元如何在感觉、认知和运动过程中编码行为相关信息提供有用的一阶描述——例如调谐曲线告诉我们单个神经元的平均活动水平如何根据刺激而变化。另一方面,神经元可以通过解码视角作为一个群体来研究。解码解决了从一群神经元中可以读出什么信息的问题,使用了简单的生物学似是而非的解码器,如线性解码。因此,解码返回群体响应空间中的特定响应模式。这个解码方面严格地依赖于表示几何,它从它们的调谐曲线中捕获神经群体响应模式的所有可能投影。图 2 举例说明了几何学在神经科学中的意义。
几何可以被视为一个称为流形的数学对象,即一个超曲面,其中每个点对应于来自神经群体调谐曲线的定义的特征和变量。如你所见,图 2 a)和 b)给出了在几何空间中混合的两条调谐曲线的简单例子,显示了神经元流形空间。如果可以计算点之间的欧几里得距离,通常接受神经流形。例如,图 2 e)示出了不可行的神经流形的例子,但是仅仅对一条神经调谐曲线进行简单的校正,图 2 f)就可以将该流形带回欧几里德空间。
几何图形由分离任意两个刺激的表示的多元响应空间中的距离决定。调谐曲线定义了响应模式,从中我们可以计算代表距离。因此,几何形状取决于调谐,但反之亦然是无效的。

图 2 上图显示调谐曲线,下图显示调谐歧管。图 a)和 b)考虑几个神经元。在这种情况下,根据两条曲线之间的偏移,流形类似于圆形或椭圆形。图 c)、d)、e)、f)、g)和 h)显示了 3 个神经元的调谐曲线的情况。图 d)显示了由两条 van Mises 调谐曲线引起的新月形响应流形。图 e)显示了一个非欧几里德流形几何,这在原则上是不可能达到的神经群体。这种纠缠的情况可以如 f)所示解决,其中第三个神经元再次返回流形。
调谐和几何是密切相关的,它们可以根据 Fisher 信息和互信息量化从神经群体中检索的信息。特别是,反应 r 和刺激 T 之间的互信息(MI) 测量反应传达的关于刺激的信息——这大致是一个变量包含关于另一个变量的多少信息。另一方面,自然群体代码的 Fisher 信息(FI) 仅是刺激的函数,反映了代码对刺激的局部变化有多敏感,即对刺激属性进行编码的精度。
图 3 示出了歧管几何形状与 FI 和 MI 之间的关系。在该图中,作者模拟了均匀的先验刺激 T 。图 3 a)示出了直线神经响应流形,然后在图 3 b)和 c)中对其进行调制。如果刺激沿着流形的分布是均匀的,那么刺激在空间的每一点都是可分辨的,FI 保持不变。MI 略有变化,最小值出现在图 3 c)中,其中流形的两个极端在空间上接近,使得这些点更加容易混淆。如图 3 d)、e)和 f)所示,这种情况在存在噪声的情况下变得完全不同。

图 3 流形几何、Fisher 信息和互信息之间的关系。这些是一维刺激空间编码的流形,来自两个神经元。响应分布显示在背景(灰度)中,用于刺激空间上的均匀先验。总流量与总管的长度成正比。如果存在一些噪声(d),e)和 f)),FI 减小。这证明 FI 测量沿流形的可分辨性,而几何和 MI 测量所有刺激之间的可分辨性。
一旦解释了神经调谐、几何、FI 和 MI 的概念,就清楚了什么是有效的代码。如果神经代码提供关于刺激的大量信息,高 MI,并在尖峰和神经元数量的限制下保持一致的响应,即高 FI ,则该神经代码是有效的。这样的定义提供了一种理性,可以解释为什么神经元采用特定的调谐函数来表示各种感官和非感官变量——即为什么大脑不同区域的神经元可以对相同的刺激做出反应,但它们有不同的调谐曲线。此外,有效的代码应该将更多的资源分配给更频繁的刺激,这可以通过刺激参数化的非线性变形来实现。
学习贝叶斯最优树状意见池
雅各布·乔丹、若昂·萨克拉门托、威廉 A.M 怀博、米海·a·彼得罗维奇、沃尔特·森、T3、纸
在这篇论文中,作者通过创建一个反映生物学对应物的特别人工神经网络,提出了树突状膜电位和电导如何编码观点以及它们作为决策者的作用。该论文的核心部分如图 4 所示。

图 4:基于电导的神经元动力学实现概率线索整合。a)显示多感官刺激、图像和听觉输入。b)首先,基于电流的神经元产生响应,其仅使用其偏好的特征来积累关于图像可能是什么的意见 c)然后,基于电导的神经元帮助基于电流的神经元整合它们的信息,总结它们的意见和可靠性。d)总身体电导可靠性,您可以清楚地看到基于电导的神经元何时起作用。e)体细胞膜电位。f)最终电路实现
大脑中的信息是通过各种方式传递的。行为证据表明,人类能够整合来自不同模态和先前经验的感觉输入,以实现与贝叶斯最优线索整合模型相似的性能。成像你看到一个模糊的图像,最初基于电流的神经元会试图整合信号以给出响应。然后,通过使用树突电导作为额外的编码维度,通过加权听觉输入和视觉输入,有可能获得正确的响应。这一观点建立了一个新的框架,以电导为中心的神经元意见加权框架,它预测了不同于经典的基于电流的模型的神经元反应特性。
电导中心模型的特性显示出与贝叶斯统计的严格关系— 也在具有胡须刺激实验的小鼠上得到实验证明。在没有感觉输入的情况下,先前的观点被编码在体细胞膜电位中。这些先验通常可靠性较低。因此,神经元更容易受到背景噪声的影响,导致先前电位附近的膜电位大幅波动。当提示出现时,突触前活动增加,因此先验分布与提示特异性分布结合,这导致具有适应的平均值和减少的方差的更新的体细胞分布。如果先验信息与线索信息非常不一致,则均值的变化大于先验信息和线索信息一致的情况。重要的是,无论是否与之前的信息冲突,在新信息出现的情况下方差总是减小的:贝叶斯推理!
我希望你喜欢 2021 年 4 月神经科学arxivg.org论文的这篇综述。请随时给我发电子邮件询问问题或评论,地址:stefanobosisio1@gmail.com
与神经科学保持同步:2021 年 8 月必读
婴儿如何学习说🗣️语?;哈佛大学🏛️为生物🧠启发的强化学习定义了一个新的数学框架;神经元雪崩建模🏂

加州,图片由尼克·莱特在 T2 Unsplash 电视台拍摄
https://medium.com/@stefanobosisio1/membership
你为什么要关心神经科学?
神经科学是当今人工智能🧠的根源🤖。阅读并意识到神经科学中的进化和新见解不仅会让你成为一个更好的“人工智能”的家伙😎而且还是一个更好的神经网络体系结构的创造者👩💻!
奥古斯特提出了三篇复杂的论文,我试图让它们变得简单易懂。我猜你们中的许多人一直想知道婴儿是如何将所有的声音放在一起并学习语言的。加州大学戴维斯分校的🗣️·罗利希和奥赖利试图评估这个问题,他们开发了完全受生物启发的神经网络,这证明了在婴儿身上的实验结果——已经在 1996 年进行了测试!第二篇论文来自哈佛的研究人员。这是一个数学问题,一个奇妙的阅读,其中强化学习是根据奖励预测误差和基于生物学假设进行审查的:🔝。第三篇论文是我喜欢的,因为它与统计物理学有关——这是我隐藏的热情。作者展示了一个接近非平衡态的简单伊辛模型如何模拟大脑中的相变,模拟诸如神经元雪崩之类的对立动力学🔥和神经元振荡📶。享受:)
语音中的统计学习:一个基于生物学的预测学习模型
约翰·罗里希,兰德尔·c·奥莱利, 论文
婴儿时期,我们如何从纯粹原始的感官经验中学习语言?你想得越多,声音就越复杂,婴儿的大脑是如何处理和排列声音,并在几个月后将它们转换成声音和真正的语言的。这个问题在神经科学中被称为统计学习,它一直是多年来研究的中心。事实上,几十年的研究一直致力于建立预测学习方法,这是一种可能成为统计学习驱动过程的机制。在这篇论文中,作者建立了一个人工神经网络,以模拟皮质丘脑回路,新皮质互连和丘脑核为基础,提供对统计学习的见解。该模型阐明了婴儿的语音分段范式。简而言之,婴儿预测言语中的下一个单词音节——被视为原始的听觉体验——并从那里开始学习说话。
正如我们从许多其他评论中看到的,该模型的核心是该过程是预测性错误驱动的学习。错误驱动的过程是由新皮层和丘脑高阶核之间的连接模式触发的。对于听觉皮层,主要输入来自核心区 A1 和 R 中的内侧膝状体(MGB)。前一个区域可以跟踪快速声振幅调制(20-30 毫秒振荡),而 R 区域处理 100 毫秒或更长时间的调制。因此,在核心区本身内存在时间响应差异,对高阶核团中的丘脑中继细胞(TRCs)产生几个微弱的预测。这些预测在皮层的第 5 层产生驱动输入,来自爆发的神经元 5ib,每 100 毫秒触发一次。因此,很明显,我们有两个不同的区域,在不同的时间激活,这种时间差产生了预测误差。时间差异可以驱动整个新皮层的突触变化,这可能类似于一种生物误差反向传播,以随着时间的推移改善预测。
图 1 显示了计算的对应物。该模型模拟 R 和 A1 区域。从那里,预测被发送到更高级的丘脑核(红色箭头,RBTh,RPBTh 和 SYSTh)。同时,每个丘脑核的预测通过相同神经网络内的子预测得到增强,通过反向传播(绿色虚线箭头)得到时间上的改善。该模型的任务是预测下一个简短的语音片段,它可以是下一个词的过渡或词内音节的过渡。

图 1:基于听觉皮层实现的神经网络示意图。有关模型的具体信息,请查看论文的第 18 页。该模型从区域 R 和 A1 开始,将初始预测发送到丘脑高阶核 RBTh、RPBTh 和 STSh。每个高阶电路都有自己的预测,这些预测通过反向传播得到增强。
结果支持 Saffran 的早期发现——发表于 1996 年。特别是,该模型比语音中的下一个单词更准确地正确预测下一个音节。进一步的测试和模拟显著地证明了正确地预测了词内转换,而不是下一个词的转换,婴儿的实验结果和计算结果之间有重叠。
实际结果是什么?该模型证明了婴儿的初步实验结果,具有以下关键点:
- 该模型完全模仿了听觉皮层中可能遵循的生物路径
- 这个过程被证明是错误驱动的,因此学习本身也是错误驱动的
- 用于训练的扩展声音集类似于我们在婴儿时听到的声音——就像一大堆声音。这些声音的多样性有助于生物网络概括类似音素的表示
- 类似音素的表示是词内过渡,因此语音学习从简单的音节预测开始,而不是从下一个词的过渡开始
具有奖励预测误差的表征学习
威廉·H·亚历山大,塞缪尔·J·格什曼, 论文
又来了!奖励预测误差的概念是神经科学的核心,它可以在现代机器学习世界中带来惊人的后果。在这篇论文中,哈佛大学的作者提出了一个新的强化学习(RL)的数学框架,将学习过程建模为多巴胺能系统(DA)。特别是,作者将 RL 框架固定在根据回报预测误差(RPE)检查 RL 的表示上。该表示可以是目标函数、时间或空间域。
从生物学角度来看,来源于 RL 的 RPE 可能对学习最佳状态表征以及学习联想本身是有用的。我们将不会通过数学的见解,但图 2 可以大大有助于理解亚历山大和格沙曼 RPE-RL 方法的基础是什么。粗略地说,作者导出了一种 RPE 方法来校正单元激活函数中的错误,而不是单个单元的权重,检索参数来导出用于调整 RL 高斯参数的生物友好学习规则。

图 2 使用 RPE 生物启发方法的 RL 的一般视图。a)RL 模型的总体视图。为了将刺激和表征联系起来,在训练期间使用奖励预测误差。通常,误差通过模型的权重传播。然而,在作者的框架中,误差可以被传播,以便调整单元的活动。b)在这里,我们可以证明,通过改变单元激活函数的参数,可以使误差最小化。正误差(B-左)可以通过增加高斯分布(蓝线)或向预测误差方向移动高斯位置(B-右)来减少。反之则为负误差。
这个模型的一个特殊例子是学习时间表征。为了证明基于 RPE 的 RL 如何学习时间表示,作者采用了 RL 标准时间差异学习规则(TD) ,图 2 显示了来自训练会话的结果。进一步的例子给出了空间表示,类别表示和运动控制。

图 3:学习时间表征。a)增加训练,从试验 1 到试验 500,当给予刺激时,模型识别时间动态 B)随着训练预测收敛,预测误差随时间减少 C)RL-RPE 有多精确?第一个图描绘了给定神经单元的活动变化,这达到了更精确的模式。特别是,高斯位置和方差随时间重新分布,向奖励方向收敛
我们能从这里带什么回家?这篇论文为 RPE RL 框架奠定了基础。这证明 RPE 可以调节来自外部刺激的内部 RL 表征的位置和扩散。此外,我们在这里采用了完全生物启发的 RL,而不是模仿 DA 系统的纯人工 RL。很多发展很快!
量化神经元振荡和雪崩的共存
法布里齐奥·隆巴迪、塞尔维尔·佩皮奇、柳文欢·什里基、加斯珀·特拉克、丹尼尔·德·马蒂诺、T3 纸
正如你从以前的帖子中看到的,我们经常将大脑动力学与人工神经网络联系起来。然而,我们是否总是需要人工神经网络来证明验证人脑?本文给出了一个更基于物理的方法来看待大脑,采用一个来自统计物理学的简单模型来解释大脑中多种神经元动力学的共存。
作者提出了伊辛模型的非平衡扩展,以捕捉神经振荡、极端事件统计和无尺度神经元雪崩。这些现象是周期性的,或者在电记录(例如 EEG、MEG)中被表示为大偏转,并且被认为在脑动力学中起着基本作用。此外,大脑的振荡通常是周期性和非周期性状态的共存——对立状态。一个例子是对大脑节律中“神经元雪崩”的观察,其中记录显示了神经元活动的级联,没有特征时间或空间尺度。
伊辛模型可以被认为是 2D 网格中的神经元(图 4)。每个神经元由定义极化波的自旋状态(例如+1 和-1)定义。外场可以改变神经元群体的极化,在网格中创建不同的状态,从而导致不同的结构阶段。监控结构阶段返回阶段转换的简化现实模型。

图 4:伊辛模型。A)神经元伊辛模型的草图,其中神经元极化用向上或向下的箭头表示,外部时变场 h(t)类似于依赖于活动的反馈机制。b)作为伊辛模型的结果,我们可以得到相变。对于β= 1,在自持振荡(绿色)和间歇振荡(黄色)之间存在分离阶段。C)反转时间,即总网络活动 m(t)的连续过零事件之间的时间间隔。从这里,我们可以提取幂律行为,其决定了临界点(β= 1)附近的反转时间的 P(t)的脑动力学 D)分布
从实验结果来看:
- 伊辛模型用人脑静息清醒状态的脑磁图记录进行了测试,其中主要振荡在阿尔法波段(8-13 赫兹)。通过信号自相关函数的推断,伊辛模型正确地模拟了来自阿尔法带的大脑活动。此外,自适应伊辛模型检索单个脑磁图传感器的动态
- 神经元雪崩和极端事件的动力学被正确地建模。此外,该模型可以再现连接平均雪崩大小和它们的持续时间的标度关系。
这些都是显著的成果。伊辛模型是我们能够创建的最简单的物理模型,如果调整正确,它可以结合兴奋性神经元尖峰的微观和随机描述。这种描述通过依赖于活动的反馈的粗粒度平均场模型得到增强,因此可以模拟任何外部刺激。令人印象深刻的是,一个简单的模型可以再现对立动力学的共存,如神经元雪崩和周期性振荡。
我希望你喜欢 2021 年 4 月神经科学arxivg.org论文的这篇综述。请随时给我发电子邮件询问问题或评论,地址:stefanobosisio1@gmail.com
与神经科学保持同步:2021 年 2 月必读
每天都有数百篇经过预先审核的论文发表在arxiv.org Stay updated with me 上,这个迷你指南会告诉你每个月最有趣的论文。2021 年 2 月最奇特的论文

大脑。图片由 Alina Grubnyak 在 Unsplash 上拍摄
神经科学是当今人工智能🧠的根源🤖。阅读并意识到神经科学中的进化和新见解不仅会让你成为一个更好的“人工智能”的家伙😎而且还是一个更好的神经网络体系结构的创造者👩💻!
2021 年 2 月有 3 篇有趣的论文值得一读,在这里我总结并将其转换为外行术语
相关神经群体中信息编码的几何学
Rava Azeredo da Silveira,Fred Rieke, 论文
神经元在其集体活动中代表信息。这种神经群体在编码和解码刺激信息时的保真度取决于每个单个神经元的响应可变性是否以及如何在其他神经元之间共享。因此,定量神经科学的研究试图将神经反应的几何学(统计学)与刺激或任务联系起来,以更好地理解相关噪声。然而,这个问题很难解决,因为它包含了大量的变量,是一个高维的问题。本文作者试图定义噪声相关性对神经编码的相关性,回顾文献并强调噪声相关性如何影响信息模式(即神经元携带的信息)的几何观点。
作者定义的一般方法考虑了信噪比上的噪声相关性。这种相关性取决于没有噪声相关性的独立神经元所携带的信息、噪声相关性本身所携带的信息以及由于信号相关性和噪声相关性与神经群体之间的相互作用而导致的增量或减量信息保真度。这些贡献可以从图 1 所示的几何角度来看。信号(图 1A 中的绿线)受到噪声的影响,噪声可能出现在这条绿线上的任何一点。噪声由蓝色椭圆表示。在这些噪音中,我们可以将两个神经元携带的相互信息,或信噪比(SNR)——即信息响应的保真度——定义为绿线在刺激的“测试”方向上的投影。因此,SNR 取决于噪声区域内定义的角度θ。图 1C 示出了为了最大化 SNR,神经群体可以执行的优化任务,通过将当前 SNR(绿色圆圈)移动到最大值(红色点),找到正确的信息方向。因此,在具有噪声相关性的神经群体的信息编码中,重要的是噪声沿着信息维度的投影。

图 1:两个神经元的信息和噪声的几何关系。答:绿线是信号,定义自己的超曲线。对于绿线中的每个点,我们可以定义相关噪声,其统计分布由蓝色椭圆表示。想象一个由黑色“测试”方向定义的信号方向,通过将绿线信号投射到测试方向上,可以看到噪声对信号的影响。因此,信噪比(SNR)将取决于角度θB:SNR 比率的行为是θ和测试方向的函数。绿色圆圈对应于信噪比和测试方向相同的情况。C:SNR 与角度θ的函数关系。绿点是我们想要优化的,直到红点处的最大 SNR 值,它定义了一个信息方向。图片摘自作者的论文
大脑皮层通讯掩盖了计算能量的使用,但是这些结合起来预测突触数量
威廉·b·利维,维多利亚·g·卡尔弗特, 论文
作者从能源效率的角度定义了一种数学方法来确定神经计算成本——即神经元执行计算任务的成本。
这个数学优化问题可以根据每个神经元的突触数量 N 来解决。他们发现,从能量效率的角度来看,每个神经元的最佳突触数量约为 2500 个。然而,这样的值与经典的统计物理推导结果非常不一致,导致每次计算的能量消耗大约为 10⁸比特/J!这种不协调可能源于物理简化——例如,物理上时间与神经元通信无关,因此脉冲式通信是无能量的。虽然类似的考虑可能适用于数学-物理学推导,但生物学必须为这一阶段付出代价,并且在神经元之间的交流方面付出代价。
深入研究通信和计算成本,他们得出的计算消耗约为 0.1 W(图 2)。将灰质(GM)通信成本与总白质(WM)成本相结合,约占葡萄糖摄入能量的 71%,总 4.94 W 中的 3.52 W 用于通信,相比之下占计算的 2%。假设所有的 WM 成本本质上都是通信成本,那么通信与计算的比率是 35:1。
这些考虑导致我们重新考虑大脑中 20 W 的总葡萄糖能量消耗。从最近的 PET 扫描来看,大脑大约需要 17 W,代替索科洛夫(~ 1950)的 20W 值。去掉 8.89 W 加热后,全脑可用的只有 6.19 W。这些支持作者的发现,分配给 GM 大约 3.09 W,在计算、通信和内务成本之间分割。

图 2:计算和通信成本的比较。左边的柱状图将计算成本定义为两种离子型谷氨酸受体(AMPAR 和 NMDAR)的总和。中间的饼图将能量成本划分为白质(WM)、灰质(GM)、计算(蓝色)和大脑内务管理成本(SynMod+)。特别是,计算成本仅占总可用能量的 2%。WM 几乎只计算通信成本,SynMod+包括跨膜过程的增长和传输。最后一个饼图,在右边,细分了 GM 的通讯成本,即前突触激活,轴突成本和休息潜力。图片摘自作者的 论文
脑疾病中的神经回路功能冗余
比阿特丽斯 E.P 水崎,钱璐·安东内尔,论文

图 3:作为行为/环境参数θ和冗余度的函数的神经回路。作者将冗余聚类细分为“典型的人”(浅蓝色)和“遗传性障碍”(绿色),用于三个冗余子概念:溢出、依赖性和多解性。图片来自作者 论文
作者研究了冗余在识别大脑疾病中的可能作用,将冗余与神经组件功能变化联系起来。这项研究试图提出一种新的研究路线,为神经回路中每一种特定的冗余变化提供更具体、更有针对性的治疗
冗余在神经系统中普遍存在,它是神经回路采用的一种方式,具有不同的细胞和突触配置,可以在大脑的不同位置实现相同的神经功能。冗余可以分为三个不同的子类别进行分析:
- 草率:这种想法认为,高层次的电路属性对其每个组件的属性并不同样敏感。例如,对特定神经组件中的一束神经元的扰动可能导致整体功能的极端变化,而其他组件可能具有类似的变化并导致微小的变化
- 依赖性:一种发展现象,其中多个回路相互协调,它们对整体功能影响之间有很强的依赖性
- 多种解决方案:观察到蜂窝组件的各种配置不需要相互连接来实现令人满意的电路级功能。
如图 3 所示,与典型的冗余模式相比,作者将冗余定义为调查可能的遗传疾病的新关键。真实的系统将依赖于数百万个参数,然而,作者采用了一个清晰的 2D 可视化大脑回路作为冗余功能。图 3A 显示了两种可能的基因突变 A 和 B 如何与相同的大脑疾病相关联,用较暗的粉红色区域突出显示。这种突变可能会导致参数θ的变化,这可能是 pH 值或温度的变化——正如实验中在龙虾神经节(STG)上发生的那样。在现实生活中,人们可以想象图 3A 描绘了一种环境效应,如药物或压力生活事件,这可能导致在一个方向(θ₁).对于普通人和 A 型遗传疾病患者来说,这种影响是良性的,因为它不会引起电路功能的变化。然而,这种影响对基因突变 b 的人来说可能是可怕的。类似的场景可以描述为所有的冗余子类,所以草率,依赖和多解。
这项研究可以与文献研究相比较,文献研究探索了大脑疾病中冗余的后果。即使这一领域的文献并不多,但大多数报道的研究都发现了稳态补偿(如温度变化)的证据,其中一个大脑神经成分的变化似乎被其他成分的变化抵消(这一过程可以被视为冗余)。因此,由于冗余似乎是神经系统的一个核心特征,作者认为,在试图了解或开发大脑疾病的治疗方法时,研究应该更多地关注冗余模式变化的核心考虑。
我希望你喜欢这篇关于 2021 年 2 月神经科学 arxiv.org 论文的评论。请随时给我发电子邮件询问问题或评论,地址:stefanobosisio1@gmail.com
与神经科学保持同步:2021 年 7 月必读
DeepMind 🧠谈深度强化学习和神经科学;艾👍🏻重新提出了 SGD 动力学的数学基础;果蝇🪰启发神经网络优于经典 ones⚡⚡⚡

加固。图片由 Ricardo Gomez Angel 在 Unsplash 上拍摄
https://medium.com/@stefanobosisio1/membership
你为什么要关心神经科学?
神经科学是当今人工智能🧠的根源🤖。阅读并意识到神经科学中的进化和新见解不仅会让你成为一个更好的“人工智能”的家伙😎而且还是一个更好的神经网络体系结构的创造者👩💻!
今天这里有三篇来自 arxiv.org 的新论文。DeepMind 和 UCL 发表了一篇关于深度强化学习及其对神经科学的影响的精彩评论。毫无疑问,这是一个热门话题,将会有更多的论文发表,以及关于人类大脑的新发现!第二篇论文是脸书·艾写的。研究人员已经对扩散 SGD 动力学的起源及其与统计物理的关系进行了奇妙的数学工作。这些暗示揭示了神经网络最初应该如何在纸上创建,具有正确的设置和期望,而不是等待“随机”收敛。最后,KAIS 的研究人员发表了一项基于稀疏编码的模型的伟大工作,以模仿果蝇的嗅觉系统,这优于经典的香草模型。享受:)
深度强化学习及其神经科学含义
Matthew Botvinick,Jane X. Wang,Will Dagney,Kevin J. Miller,Zeb Kurth-Nelson, 论文
在这篇论文中,DeepMind 和 UCL 的作者撰写了第一篇关于深度强化学习(DRL)在神经科学中的应用的综述。尽管 DRL 的研究才刚刚开始,但对神经研究的潜在影响却非常大。事实上,DRL 反映了大脑组件(神经元)、连接(突触)和系统必须自行学习的环境情况。DRL 可能会导致对什么是记忆,以及动物和社会认知如何管理情境探索的更深入的见解。总结本次审查,我们可以定义这些部分:
- DRL 对神经科学的早期研究
在文献中,我们可以找到 DRL 的神经任务的早期方法:
Yamins & DiCarlo 创建了一个深度卷积网络来模拟猕猴腹部流各个部分的组成;
Niv 进行了寻找阶段性多巴胺释放与时差报偿预测误差信号(RPE)之间联系的研究;
【宋、杨和王设计了一个网络来解决实验研究中猴子的任务。由此产生的模式与神经生理学实验数据密切相关。
- 元和分布学习
王等 探索了元强化学习:对于特定的相互关联的任务,网络能够在不改变权值的情况下适应新的任务,突出了隐含网络结构中的动力学效应,即慢 RL 驱动学习和快 RL 学习;
达布尼等人。将 DRL 带入了一个新的场景,分布 RL,即 RL,其中 RPE 不仅是一个标量,还是一个向量,更类似于与大脑相关的结构,并为任务提供了更广泛的概率

图 1:分布 RL:图中显示了达布尼的实验。a)时差任务。经典 RL 算法返回相同的 RPE,而在分布式 RL 中,RPE 的值可能有很大不同,导致概率从悲观到乐观。b)在这种情况下,代理必须学会在平台之间跳跃,概率回报分布显示在右边。c)真实案例场景。用概率奖励训练老鼠是可能的(右边灰色阴影区域)。奖励可以清楚地解释为一个分布 RPE。(图片由作者提供)
- 米艾默里
DRL 可以帮助我们理解记忆是如何支持基于奖励的学习和决策的。
Mnih 展示了 DRL 模型如何依靠记忆/经验重放,存储过去的经验,并在雅达利游戏的新挑战中使用它们,模仿海马体进行记忆巩固;
Pfeiffer 和 Foster 研究了在线决策的记忆维护和提取。特别是,在 DRL,在线决策是两个记忆之间的相互作用:一个是“情节”记忆,它读取和写入长期记忆槽,另一个是“激活”记忆,即存储激活信息;
- 探险
对于 DRL 来说,探索是一个棘手的概念,因为 RL 算法通过测试新的可能性和组合来找到解决方案。
内在动机作为 RL 探索的替代方案出现,并由 Badia 和 Osband 进一步发展,在那里代理人被鼓励探索预测不那么有信心的新环境。
关于这个研究领域,元学习提供了有趣和有前途的解决方案。元强化学习系统中的探索可以被认为是假设驱动的实验,这使得该解决方案成为研究动物任务探索的最有吸引力的解决方案之一
- 社会认知
社会认知是神经科学中一个不断发展的领域。DRL 最近在这一领域有了新的发展,有了多智能体场景,比如竞技团队游戏或社交困境。有希望的未来研究致力于潜在的心智模型,被称为‘心智理论’
重新思考 SGD 的极限动力学:修正损失、相空间振荡和异常扩散
大卫·库宁、哈维尔·萨加斯图伊-布雷纳、劳伦·吉莱斯皮、埃希德·马尔加利特、田畑秀则·田中、苏亚甘古利、丹尼尔 L.K 亚明斯、 论文
这是斯坦福、NTT 和脸书人工智能研究人员的一项了不起的工作,他们研究了随机梯度下降(SGD)的动力学,将模型超参数探索与统计物理联系起来。作者从基本假设出发,认为现在的神经网络模型缺少理论驱动的设计。事实上,训练通常是通过启发式参数完成的,并且没有将超参数优化与神经网络体系结构和数据集的几何结构联系起来。这项研究试图量化所有这些元素如何在 SGD 动力学中发挥作用,理解为神经网络的学习动力学建立完整理论的关键步骤。

图 2:5 个卷积神经网络的局部(上图)和全局(下图)位移的平方欧几里德范数。尽管模型的性能已经收敛,但是在训练之后,模型仍然在探索相空间。对于局部位移,这以固定的恒定速度发生,而对于全局位移,存在扩散的准布朗行为
首先,作者挑选并训练预训练模型,并监控局部参数位移(逐步超参数之间的差异)和全局参数位移(当前步骤的超参数与预训练模型的参数之间的差异)的波动。如图 2 所示,局部位移(图 2 顶部)继续以恒定速度波动,而全局位移(图 2 底部)根据幂 c 以布朗方式演变为步数的函数。这就产生了下面的问题:为什么网络在训练结束时继续扩散地探索相空间?
为了回答这个问题,作者将 SGD 建模为欠阻尼的朗之万方程。朗之万方程描述了一个粒子在随机力作用下的运动。这种随机力是布朗运动的本质,由于周围分子的碰撞,朗之万计算返回了流体中粒子运动的统计数据。将 SGD 建模为朗之万过程,作者发现欠阻尼版本的朗之万方程正确地模拟了相空间中网络的波动轨迹。
在以下设置下,作者用 SGD 分解线性回归,发现由 SGD 驱动的相空间探索导致了奥恩斯坦-乌伦贝克过程(OU 过程)。OU 过程是一个嘈杂的松弛过程。例如,弹簧可以在长度上摆动。在过阻尼的情况下,会有一个摩擦系数阻碍波动。如果存在热波动,弹簧长度将围绕弹簧静止长度随机波动。由此,通过使用梯度噪声和 Hessian,可以计算超参数优化过程的统计矩的解析表达式。第一个矩描述了扩散过程的振荡行为。二阶矩可以洞察驱动随机成分。
为了进一步理解这第二个组成部分或 SGD 扩散行为的驱动力,可以将 OU 过程发展为 Fokker-Plank 方程(FP 方程)。FP 方程返回了朗之万动力学下粒子位置分布的时间演化。在这一点上,作者意识到,关于线性回归,数据分布中的各向异性导致梯度噪声和扩散中的各向异性。对于大多数数据集,SGD 轨迹不是由原始最小二乘损失驱动的,而是由修改的损失驱动的。
也可以为神经网络计算这种修改的损失。根据经验,作者可以确认超参数的优化受到扩散指数的强烈影响,扩散指数表现为学习速率、批量大小和动量的函数。所有这些元素独立地起作用,并引起异常扩散机制。此外,较大的学习速率会导致欠阻尼振荡,从而降低扩散指数。另一方面,批量大小不影响阻尼比,但导致对扩散指数的非单调影响
果蝇持续学习的算法洞察
杨慎,Sanjoy Dasgupta,Saket Navlakha, 论文
这是一篇精彩的灵感来自大脑的方法论文,旨在创建一个可能能够匹配神经网络性能的新模型。作者研究了果蝇如何不断学习将气味与行为联系起来,发现了一种新的人工神经网络,它可以超越经典网络,而无需反向传播。特别是,连续学习对于现代神经网络来说是一项复杂的任务,因为经常会出现灾难性的遗忘问题。受果蝇嗅觉系统的启发,作者创建了一个模型,该模型使用稀疏编码、突触冻结和感知器式学习来学习并返回任何气味的特定动作。

图 3 两层嗅觉系统网络:A)嗅觉系统表示。当一种气味被接收时,一个深层的两层网络被启动。投射神经元对输入信号进行预处理。然后,由凯尼恩细胞(KCs)执行随机投影。通过 APLs 反馈,只有 5%的细胞被激活,并能与 MBONs 相互作用,根据行为对气味进行分类。b)当第二种气味进入时的例子。在这个阶段,不同的 Kenyon 细胞被激活,产生与不同 MBONs 的相互作用
果蝇有两层嗅觉系统(图 3)。当闻到一种气味时,就被投射神经元(PNs)的放电频率编码。PNs 响应进入第一层。在第一层中,2000 个凯尼恩细胞(KC)通过从 PNs 中随机采样,将 PNs 输入表示转换成稀疏和高维的表示。然后,信号被发送到抑制性神经元(APL),反过来,抑制性神经元将抑制反馈到每个 KC。因此,95%的低点火 KC 被关闭,剩下的 5%用于赢家通吃(WTA)计算。第二层把气味和行为联系起来。这是通过 KCs 和蘑菇体输出神经元(MBONs)之间的相互作用来完成的。KCs 细胞以相同的重量向“接近”和“避开”MBON 细胞发出气味。如果气味与不良行为有关,就会释放一种多巴胺信号来减少 KCs 和“接近”MBONs 之间的联系。
计算对应物也在 2 层电路中实现(FlyModel)。第一层计算输入信号的高维稀疏表示。输入层通过稀疏的二进制随机矩阵连接到第一层。然后,应用赢家通吃过程,仅顶部最活跃的 KC 保持开启,而所有其余的被设置为零。第二层看到这个稀疏的输入。给定学习速率和背景记忆衰减,可以在输入和输出之间编纂权重更新规则(论文的等式 3)。
该电路已经在 MNIST-20 数据集上进行了测试,其中训练数据被排序并分成顺序任务,并与以下各项进行比较:
- EWC:弹性权重合并模型,其中费雪信息标准用于确定权重
- GEM:梯度情景记忆,存储以前学习任务的数据子集
- BI-R:大脑启发的重放,它用一个生成模型来重放活动模式,从而保护旧的记忆
- 标准全连接神经网络
- 离线,像香草,但学习任务随机,而不是顺序
结果,FlyModel 的表现超过了所有的模型。对于 MNIST-20,FlyModel 达到了 0.86 的精度,而 BI-R 为 0.77,GEM 为 0.69,EWC 为 0.58,Vanilla 为 0.19。此外,在训练步骤结束时,FlyModel 的记忆损失只有大约 7%。对于 CIFAR-100 数据集记录了类似的结果
为什么 FlyModel 取得了类似的成绩?FlyModel 的主要特点是稀疏编码和突触冻结。两者在神经科学界都很有名,研究人员发现这些方面对学习和大脑启发的模型有巨大的影响。然而,很难确定为什么这些特征也是顺序学习的关键因素。
请记住这篇文章是一篇开创性的工作,可以让你进入一个更受生物大脑启发的神经网络世界:)
我希望你喜欢 2021 年 7 月神经科学arxivg.org论文的这篇综述。请随时给我发电子邮件询问问题或评论,地址:stefanobosisio1@gmail.com
与神经科学保持同步:2021 年 6 月必读
稀疏神经连接🖇️如何影响功能行为?一个更符合生物学原理的反向传播:误差向量广播📡,又是如何关注 work⚡⚡⚡的?

图片来自 Unsplash
https://medium.com/@stefanobosisio1/membership
你为什么要关心神经科学?
神经科学是当今人工智能🧠的根源🤖。阅读并意识到神经科学中的进化和新见解不仅会让你成为一个更好的“人工智能”的家伙😎而且还是一个更好的神经网络体系结构的创造者👩💻!
今天这里有三篇来自 arxiv.org 的新论文。第一个研究稀疏连接性及其对功能模块性的影响。第二篇文章提出了一种反向传播的新方法,这可能在生物学上更合理——毫无疑问,这里应该进行大量的研究。最后,第三篇论文研究深度学习神经网络中的注意力,试图捕捉注意力在大脑中是如何工作的。
极度的稀疏导致了功能的专门化
加布里埃尔·贝娜,丹 F.M 古德曼, 论文
神经网络是模块化,也就是说,它们可以分解成独立的子网。这些子网络呈现出结构模块化,其中神经元被划分为不同的模块,以及功能模块化,其中每个模块都可以执行独立的操作。给定这些概念,结构模块化和功能模块化之间有关系吗?我们能评估结构模块化在多大程度上影响功能模块化吗?作者对这些方面进行了研究,提出了一种可控的结构模块化神经网络,并监测功能模块化度量,了解这两种模块化在多大程度上相互影响,试图找到神经生物学对应物的答案。
具有高模块化的网络在模块内的节点之间具有密集的连接,但是在不同模块的节点之间具有稀疏的连接
所提出的架构由两个子网络组成。这些网络作者具有不同程度的互连,从不同子网络模块中的节点之间的稀疏连接,到实现全密集架构时的无模块性。作为输入任务,每个子网接收一个 MNIST 数字,并且该网络必须与所有其他子网通信,以返回跨网络的两个数字是否具有奇偶性。同时,每个子网能够专门识别它们自己的数字。为了测量功能模块性,作者监控了三个度量标准:
- 瓶颈度量:在读出以检查它们是否能够识别输入数字之后,窄的 5 个神经元层的平均准确度
- 权重屏蔽度量:这是一个专门化的度量,其中保留了来自一个子网络的参数的子集 q% ,以检查给定任务的性能
- 相关性度量:对隐藏层进行分析,计算相同数字示例的子网络 n 的隐藏状态之间的皮尔逊相关系数
图 1 显示了所研究的子网的三个度量中的每一个的最终结果。总的来说,所有这三个指标都随着网络的结构模块性而变化,用稀疏性(活跃连接的比例)和 Q 模块性来衡量。可以得出以下结论:
- 强加结构模块化(Q 模块化的高值)导致更多的功能模块化(活动连接比例的高值)
- 在稀疏性或结构模块化的极端水平上,我们在所有三个度量上都有高度的结构专门化
- 需要高水平的结构模块性(Q 模块性)来确保功能模块性——即功能模块性可以通过子网之间非常稀疏的连接来实现

图 1:左边是两个子网络之间的稀疏度,右边是 Q 模块度。线条表示该指标的平均值,而阴影区域表示同一实验的 10 次重复的 1 个标准偏差
这些结论对生物神经网络有显著的意义。连接组学的整个思想,即知道网络的结构属性就足以让我们理解功能属性,可能不再正确。正如作者所写的:
我们不应该仅仅通过观察适度的结构模块化来得出任何程度的功能模块化
通过广播全局误差向量的信用分配
大卫·g·克拉克,L.F 阿博特,钟素妍, 论文
神经回路不实现反向传播方法(BP),因为进化已经找到了另一种算法或路线,允许回路可塑性工作并训练所有的大脑网络。BP 的一个可能的生物学解决方案是学分分配——这里是我们讨论学分分配问题的回顾——一个神秘的全球学习信号在整个网络中传播。
从这里,作者提出了一种新的替代 BP 的方法,称为误差向量广播(GEVB) ,其中全局学习信号被广播到神经网络中的所有隐藏单元以更新权重。特别地,在网络上分发的信号是关于输出误差的信息,充当无单元特定反馈。实现 GEVB 的神经网络被称为矢量化非负网络(VNNs) 。此外,GEVB 回忆起生物神经网络,对第一层施加非负权重,因为它发生在皮质投射神经元的兴奋行为上。最后,通过由突触前激活和全局误差向量的内积给出的量来更新每个权重。图 2 显示了理解 BP 的各种替代方案的差异的图形方式。

图 2:关于反向传播(BP)算法的替代实例:a)BP 算法,其中通过逐步转置误差来逐层更新权重;b)反馈对准(FA ),其中误差被逐层向后发送;c)直接反馈对准(DFA ),其中误差被直接广播到每个隐藏层;d)提出的全局误差向量广播(GEVB ),其中全局误差向量被广播到所有隐藏单元,而没有单元特定的反馈。
我们有什么结果?首先,作者尝试使用矢量化非负网络(非负权重网络)和传统网络,针对不同的连接范围,针对 MNIST 和 CIFAR-10 数据集,比较 BP 和 GEVB。我们的注意力将集中在矢量非负网络上,如表 1 和表 2 所示。总的来说,误差比较在不同的连接性和权重更新算法上是兼容的,在 GEVB 和 BP 之间有显著的重叠
表 1 矢量网络的 MNIST 检验误差(%)比较,在不同连接范围(完全连接、卷积连接、局部连接)下,GEVB 和 BP 之间具有非负权重或混合符号 wegiths。
标签。2: CIFAR-10 测试误差(%)比较,术语根据表 1
当使用 t-SNE 对 CIFAR-10 图像进行聚类时,获得了进一步令人鼓舞的结果,其中 GEVB 聚类质量在统计上优于 BP 方法。
这些显著结果可能为使人工神经网络更类似于生物神经网络铺平新的道路。GEVB 算法提出了一个新的问题,关于矢量化的生物学实现,以及突触前和突触后神经元的响应如何相互干扰,在网络中传播“权重更新”信号。伙计们,请继续关注!
通过内部门控的基于对象的注意
乔丹·雷,阿里·本雅明,康拉德·保罗·柯丁, 论文
注意力是大脑用来选择给定刺激或特征的有意义子集的机制
大脑中的一个神秘机制是注意力。计算神经科学和机器学习已经成功地为我们提供了使用注意机制在简单任务中检测和识别对象的模型。使用深度神经网络已经取得了更彻底的结果,这些网络被证明能够理解更复杂的场景,但是,它们仍然远远不能达到人脑的水平。人类大脑的注意力可以被认为是这些给定元素的混合:
- 神经激活的调制:当受试者识别一个物体时,视觉神经元表现出活动的变化,从大约 5%到 30%的调制,增加了视觉皮层中的注意力机制
- 注意力不变调谐:虽然神经活动的调节是开启的,但是注意力保持神经元的调谐特性不变
- 内部门控:注意力是过滤不相关的特征,返回更清晰的信号
- 分层处理:在大脑中,存在着分层组织的细胞,这些细胞呈现出广泛的调谐特性。这使得大脑能够学习复杂的非线性特征。这些细胞的输出增加了注意力层收集的信息
- 自上而下的注意神经调节:以视觉问题为例,信息是以前馈、反馈、横向流动的方式行进的。反馈和横向流动使自上而下的注意成为可能,而前馈路径则定义了早期层中的感受野。
- 返回抑制:由视觉输入激活的区域被抑制,以允许受试者从一个检测到的物体移动到一个新的物体。
作者提供了一个新的神经网络模型,它可以包含所有这些特征,以便研究对视觉刺激的注意力的本质,试图复制生物学上正在发生的事情,以给出一个似乎合理的生物学答案。
图 1 显示了实现的神经网络,其被细分为 3 个主要区域,反映了大脑视觉路径。存在三条线,前馈(黑线),其可以生成特征图并返回预测;实现注意力屏蔽的反馈通路(橙色线)和将前馈通路投射到反馈通路上的水平连接(绿色线)。注意力屏蔽在 V2 中充当内部注意力门控,并且在最后阶段,创建像素空间可解释的图像。最后的结果对注意及其机制给出了一个可能的解释

图 3:左边是实现的基于注意力的神经网络,右边是视觉注意力路径,因为它可能在大脑中。所实现的神经网络的特点是保持前馈、横向和反馈连接。
图 4 显示了 MNIST 和可可数据集的结果。根据注意机制,原始输入在不同阶段被处理。门控输入显示了注意力如何作用于输入图像,黑白区域是受抑制的区域,黑色区域是不受抑制的区域。注意掩码驱动门控机制,集中在最重要的区域,最后,IOR 掩码(返回的抑制)指定哪些区域已经被检查,哪些区域将被抑制用于将来的迭代。

图 4 是 MNIST 数据集(左侧)和可可数据集(右侧)的关注层的结果。
这种学习方法促进了对大脑中注意力机制的理解,这种机制是前馈、反馈和与内部门控和返回抑制的横向联系的混合。从神经网络输出来看,似乎有一个处理对象的一般注意规则:
- 神经元的调谐曲线不会改变
- 在不同的神经层次都有抑制
- 在深层神经层有一个注意力的峰值调制
这项研究为更深入地理解注意力奠定了基础,但未来的工作必须继续。与前一篇文章相关的一个注意事项是:如果我们对网络实现一个更符合生物学原理的反向传播算法,比如 GEVB,会怎么样?
我希望你喜欢 2021 年 6 月神经科学arxivg.org论文的这篇综述。请随时给我发电子邮件询问问题或评论,地址:stefanobosisio1@gmail.com
与神经科学保持同步:2021 年 3 月必读
本月:脸书人工智能 vs 大脑信号👍人工神经网络中的树突🚨理解视网膜动力学中的错觉👁️

深度递归编码器:模拟大脑信号的可扩展端到端网络
奥马尔·谢哈布,阿列安德烈·笛福塞,让-克里斯托夫·卢瓦索,亚历山大·格拉姆福特,让-雷米·金, 论文 , 代码
这篇论文直接来自脸书人工智能研究所、巴黎大学和巴黎高等师范学院的合作。这篇论文的主旨是设计一种新的方法来帮助神经科学界分析大脑对感觉输入的反应。特别是,作者将注意力集中在从阅读活动中获取大脑信号,定义单词长度和单词使用频率在大脑中的反映。
事实上,来自人类任务的大脑记录经常是非常嘈杂和高维的。只是给你一个想法,一个小的眨眼可以破坏信号记录,以及心脏跳动需要考虑在内,以避免太多的噪声存在。迄今为止,神经科学界试图用线性技术来破译这些记录,如时间感受野(TRF)或递归时间感受野(RTRF),然而,这些技术无法处理记录信号产生的如此多的非线性。
为了解决这一挑战,作者提出了一种特定的端到端深度学习架构,经过训练可以一次预测多个受试者的大脑反应(深度递归编码——DRE)。该架构基于两个相互堆叠的改进的长短期记忆(LSTM)模块。输入数据通过卷积层和 ReLU 函数进行编码,然后 LSTM 模型对隐藏状态进行排序,最后通过卷积转置 1D 层和 ReLU 激活函数转换回 MEG 活动估计。
该模型在 68 名受试者身上进行了测试,他们有一个小时的阅读任务,同时用 273 通道的 CTF 脑磁图扫描仪进行记录。这项任务包括快速阅读屏幕上闪现的大约 2700 个单词。4 个众所周知的特征在这项研究中受到关注:单词长度、自然语言中的词频、序列中第一个和最后一个单词的二元指示器。
结果证明,不仅 DRE 比经典的 TRF 方法更好地预测大脑反应,而且 DRE 的特征重要性突出了哪些特征是大脑反应中最突出的特征。图 1 示出了作为 MEG 扫描的空间位置的函数的单词长度和频率的排列重要性的结果。在后部脑磁图通道中,单词长度在 150 毫秒后达到峰值,而在额颞脑磁图通道中,单词使用频率在 400 毫秒左右达到峰值。此外,DRE 能够跟踪一个额外的现象,这是大脑中词汇处理的侧化。事实上,对于词频,峰值反应出现在两个半球,但在额叶区的左半球幅度很大。

图 1:经典线性方法(TRF)和 DRE 之间的比较,用于分析大脑区域的特征峰值。根据时间活动和大脑位置(颜色编码的大脑地形图),在两个模型中比较单词长度和单词(使用)频率。DRE 的字长在 150 毫秒左右出现峰值,这与后部脑磁图通道的活动有关。TRF 和 DRE 都在 400 左右出现词频峰值。DRE 女士强调这种反应来自大脑的 3 个区域,但活动集中在左半球。通过将单词长度(左侧)和单词频率(右侧)的排列重要性(δR)用作空间位置(由脑地形图中的通道位置进行颜色编码)和相对于单词开始的时间的函数
生物限制会损害树突计算吗?
伊莲娜·西蒙娜·琼斯,https://arxiv.org/abs/2103.03274论文
作者研究了人工神经网络(ANN)中树枝状非线性的影响。出发点是想知道将树枝状连接建模为线性积分器的主导思想是否有意义。事实上,由于其电压依赖离子通道,枝晶是高度非线性的。三点被确定为在人工神经网络中模拟更类似生物的树状结构的关键:
- 树突显示出非线性激活函数,其类似于泄漏整流线性单元(LReLU),但是可以用 NaCaK 函数(钠、钙和钾电压依赖性的总和)以更生物合理的方式建模;
- 通常树突输入是 0/1,但是它们可以被建模为基于电导的突触非线性;
- 由于类似于隔间之间的轴向阻力,树突的权重参数可以是正的和负的,或者建模为非负的值。
这三点可以看作是对 ANN(或约束)的一种生物学上的改进。这些约束在二叉树模型中实现,修改了不同论文中提出的经典 k 树算法结构,如图 2 所示,并且针对 7 个机器学习数据集的性能与对照 2 层全连接神经网络(FCNN)进行了比较。

图 2: (A):实现的 k 树模型,具有树状约束,以及“控制”完全连接的神经网络(FCNN)。蓝色节点是输入的突触非线性。白色节点应用可选的树枝状非线性。黑线是受约束的非负权重。“k”决定了有多少相同输入的子树在模型中重复,类似于不同树突树上重复的突触输入。“h”是 FCNN 隐藏层中的节点数。(LReLU 和树突的 NaCaK 激活功能之间的比较。(C)没有突触非线性和具有突触非线性的模型的比较(D)没有和具有非负权重约束的模型权重的比较。
通过将 MNIST、CIFAR-10、FMNIST、EMNIST、KMNIST、SVHN 和 USPS 数据集的 k 树结果与 FCNN 进行比较,作者得出了以下结论:
- 对于 MNIST、FMNIST、KMNIST 和 EMNIST,NaCaK 激活函数优于 ReLU、LReLU 和 sigmoid 激活函数,而对于 SVHN、USPS 和 CIFAR-10 数据集,达到类似的性能
- 突触非线性约束非线性地将树突的输入映射到实际的毫伏单位,影响 k 树的性能,对于 MNIST、FMNIST、KMNIST、EMNIST 实现了比 FCNN 更高的精度,并且对于 SVHN、USPS 和 CIFAR-10 实现了相同水平的精度
- 非负权重添加到 k 树非线性映射 synapse 在具有 synapse 的正 k 树和负 k 树的水平上执行或者比其更好。对于 MNIST、KMNIST 和 CIFAR-10,该模型优于 FCNN。
尽管有这些令人鼓舞的结果,目前的 k-tree 树突模型仍有一些局限性。首先,NaCaK 函数是一个近似值,因为在树突中有不止 3 个离子通道,尽管钠、钾和钙是最具代表性的离子通道。第二,非负权重是离子通道在整个树枝状形态中分布的结果。因此,比 k 树更现实的形态和每个节点的可学习的 NaCaK 函数的组合可以引入更多生物学相关的自由度,这可以影响模型计算性能。最后,k-tree 结构呈现对称性,这在真实的自然界中是不重复的。对多突触输入重复以及这些输入如何到达不同的树状子树的进一步研究是必要的,以允许架构被更好地训练。总的来说,这项研究显示了神经生物学的限制有多重要,以及如何在当前的人工神经网络文献中反映出来,以便找到更好的神经网络结构。
视网膜预期动力学中的信息协同
齐,周博宇,陈国华, 论文
我们的视觉很容易出现错误,这就是所谓的视错觉。例如,闪光滞后现象是一种保护机制,由我们的视网膜针对移动物体触发。同时,预期是一种时间错觉,它让动物从过去的经历中感知未来的事件。本文作者研究并证明了视网膜的预期可以描述为负群时延(NGD)现象。NGD 是一个物理模型,其中“预期”是由神经网络基于过去感知的延迟反馈创建的。
为了研究这种效应,作者对青蛙的视网膜进行了实验和计算研究。从牛蛙身上切下一小块视网膜,固定在 60 通道多电极阵列 (MEA)上,灌注并充氧长达 10 小时。随机光刺激 x(t) 通过 LED 照明发送到视网膜,强度 I 与 x 成比例。视网膜的反应由 MEA 在不同频率 fc (1、2、3.5 和 5 Hz)和强度时间 0.5 s 的刺激下在 25°c 下记录。
图 3 显示了大约 20 个视网膜的预期效果的平均刺激结果。 x(t) 为随机信号, r(t) 为视网膜放电频率,即从受刺激的视网膜上提取的电锋电位。特别地, r(t) 可以包含由视网膜创建的预期信息,作为信号 x(t) 及其时间修改 ẋ(t).的结果对于刺激频率 fc =1Hz(或每秒 1 个周期)的实验,视网膜的响应被分析为跨神经网络的互信息,作为时滞δt 的函数,这对于视网膜来说足够小以产生预期的响应。人们可以清楚地看到I(r;x) 在原点的右边(0.0 s 处),表示来自视网膜的响应(尖峰)是 x(t) 的预期。此外,对于 I(r,ẋ,可以注意到额外的峰值,再次在原点的左侧,指示预期行为。图 3(b)显示了I(r;)通过部分信息分解(PID),进一步证明了预期现象的存在。

图 3:来自强度 I(r,x)的刺激的视网膜检测信号。(a):随机光信号为 I(r,x),红色;δt 是时间延迟,蓝色、紫色和黑色表示视网膜对信号的预期反应。(b)部分信息分解贡献
接下来,作者用计算线性模型模拟了视网膜的反应。该模型结果依赖于参数 λ(t) ,该参数作为时间的函数而变化。 λ(t) 似乎取决于刺激频率 fc 以及神经节细胞的特性,神经节细胞负责视网膜中“预测”细胞的放电——即触发预期事件的细胞。
总之,这篇论文阐明了视网膜中发生的预期事件。这种现象来自一组细胞的原始刺激中不存在的信息的重组。特别是,似乎在输入信号 x(t) 和它的时间修正 ẋ(t) 之间存在协同作用,这引起了视网膜的预期。因此,视网膜电路能够以某种方式从 x(t) 中提取 ẋ(t) 的信息,然后将它们重组以形成棘波 r 。
我希望你喜欢这篇 2021 年 3 月神经科学 arxiv.org 论文的综述。请随时给我发电子邮件询问问题或评论,地址:stefanobosisio1@gmail.com
与神经科学保持同步:2021 年 11 月必读
我们能雇用甘斯吗💻在神经科学实验中🐁?几何流形是什么💎我们的记忆存在于?我们能进行生物启发的独立成分分析吗📈?

杰弗里·布鲁姆在 Unsplash 上拍摄的图片
https://medium.com/@stefanobosisio1/membership
你为什么应该关心神经科学?
神经科学是当今人工智能🧠的根源🤖。阅读并意识到神经科学中的进化和新见解不仅会让你成为一个更好的“人工智能”的家伙😎而且还是一个更好的神经网络体系结构的创造者👩💻!
本月 3 篇惊险论文!来自爱丁堡大学的科学家们第一次使用了生成对抗性神经网络,从而拓展了神经科学分析的边界。对小鼠进行了实验,以研究学习前和学习后阶段之间的神经元动力学变化。第二,斯坦福大学、普林斯顿大学、哥伦比亚大学和高级研究所合作,利用递归神经网络研究了记忆几何流形应该是什么样子。最后,罗格斯大学的研究人员展示了生物启发的神经网络独立成分分析的实现及其对神经形态计算的影响。
使用循环一致的对抗网络进行神经元学习分析 布莱恩·m·李、奥克利托斯·阿姆夫罗西迪斯、娜塔莉·l·罗什福尔、阿诺·翁肯、 论文
神经科学的核心研究领域旨在了解学习过程中发生的神经元重塑动力学。从神经实验中提取可解释的信号并进行有意义的分析现在已经成为进一步理解神经元动力学的优先要求。许多出版物显示了使用标准技术(如 TCA 的五氯苯甲醚)得出的显著结果,但是,这些标准技术具有线性响应和映射的基本假设。在这篇论文中,爱丁堡大学的研究人员将分析扩展到生成对抗性神经网络,特别是,他们采用朱的 CycleGAN 来学习神经元活动的学习前和学习后之间的映射。
CycleGAN 可以学习两种概率分布,在这种情况下是学习前和学习后的神经元动力学,并将一种转换为另一种。这一过程使我们能够更好地理解哪些可能是学习过程中出现的关键特征,以及从神经元获得即时反应模式。
在这个实验中,我们用一只头部固定的老鼠研究了学习前后的动力学,这只老鼠被放在一个可以前后移动的线性跑步机上。在鼠标前面,有一个带有明确光栅图案的监视器。如果鼠标能够在跑步机上前进 120-140 厘米,显示器将切换为黑色模式。在这里,如果老鼠在虚拟奖励区内舔了一下,就会得到奖励(水滴)。这项活动迫使老鼠学会利用屏幕上的视觉信息和自身运动来获得最大的回报。初级视觉皮层神经元用钙指示剂标记,并监测 4 天,随时间测量相对荧光。
第一天是学习前,第四天是学习后,所以从这几天开始,我们会分别得到学习前和学习后的概率分布。GAN 的生成器和鉴别器必须以数据驱动的方式识别与动物实验相关的模式。图 1 显示了 GAN 鉴别器和生成器的主要结果。前两个顶部图显示了分别计算为平均注意力掩模的学习前和学习后鉴别器注意力图。学习前鉴别器专注于虚拟动物位置上 100-130 厘米之间的特定神经元组,这与奖励区一致。学习后鉴别器突出了两组神经元,总是在 100-130 厘米左右。同样,对于发电机来说——两个底部的图。特别是,预学习生成器(左下方)专注于流程开始和结束时的活动(奖励区)。后学习生成器在奖励区之前给予更多的关注。这些结果表明,要了解从学习后到学习前反应的转变,最重要的特征是在老鼠到达奖励区时获得的。

图 1:来自 CycleGAN 模型的注意力地图,是老鼠在跑步机上每跑一段距离的平均值。上图:学习前(左)和学习后(右)辨别者的注意力地图。下图:学习前(左)和学习后(右)生成者的注意力地图。虚线表示奖励区。
总之,本文首次将 GAN 的方法应用于活体神经元实验。作者能够通过 GAN 生成的潜在特征来可视化学习过程,强调奖励区周围的活动在整个动力学中具有高度影响力。未来的研究必须加强记录过程,利用垂直和水平空间信息来丰富模型的特征。
连续变量工作记忆的递归神经网络模型:活动流形、连接模式、动态代码 克里斯托夫·j·库埃瓦、阿德尔·阿达兰、米沙·佐戴克斯、钱宁、 论文
我们的记忆能够以适当维度的连续结构存储信息。例如,考虑两个视觉刺激,它们在我们视觉区域的相同位置依次出现。刺激是一样的,唯一不同的是方向,比如一个刺激指向左边,另一个指向右边。我们的大脑如何将两个刺激识别为两个不同的刺激,并避免第二个刺激在记忆中覆盖第一个刺激?从数学和计算的角度来看,这是一个奇妙的问题。为了模拟大脑动力学,我们需要找到一种在模型中存储记忆的方法,利用一些几何特性。在这篇论文中,麻省理工学院、普林斯顿大学、哥伦比亚大学和 IAS 的研究人员通过递归神经网络(RNNs)研究了这个问题。rnn 用于存储两个不同方向的连续闪光,并研究网络的连接模式。RNN 之所以被选中,是因为他们能把刺激保持在记忆中。
图 2 显示了实验装置。延时后,两个输入信号按顺序给出。除了方向之外,信号是相同的。用于网络模式研究的 RNN 由 100 个全连接单元组成。

图 2:实验设置。(a)训练 RNN 记住两条线的方向,给定两条线之间的延迟时间。(b)RNN 建筑:100 个循环连接的单元。输入是 32 个方向调谐的信号线。输出由方向角的余弦和正弦表示
主要结果如图 3 所示。首先,作者将主成分分析应用于一段时间内循环单位的活动,以了解低维结构。当两种刺激都作为输入出现时,主成分图显示 Clifford torus 流形的出现。一个标准的环面流形——就像一个甜甜圈形状的物体——当它有两个不同方向的两个信号的两个宿主时就会变形。相反,Clifford 环面可以同等地表示这两种信号。在正交性检查中给出了进一步的证明(如图 3 所示)。如果我们在两个环面上运行正交性和平行性测试,我们可以看到标准环面如何不对称地处理角度,而 Clifford 环面可以对所有角度保持对称。在早期训练阶段的 RNNs 显示出与标准圆环相似的结果。在后学习中,RNN 单元可以识别不同的方向,给出关于 Clifford 环面的正交性和平行性测试输出。

图 3:标准环面和 Clifford 环面的区别。RNN 在早期训练阶段展示了标准环面几何的典型行为。学习之后,由于克利福德流形,记忆得以保留。
此外,作者研究了所有 100 个单位的连接模式。出现了在第一延迟周期期间存储关于特定线路的信息的单元在第二延迟周期期间不总是继续存储关于同一线路的信息。特别是,有一个权重的调整和平衡,这可以防止内存被覆盖。
结论是:
- 记忆可能存在于类似 Clifford 环面的几何流形上。这个环面可以保持信号的连续表示,其中任何信号都可以被区分,它是一个正交基。
- 在学习时,存储第一方位记忆的单元随时间改变它们的调谐,防止覆盖第一个存在的方位。
- 这可能是拥有更多类脑网络和记忆系统的通用解决方案
- 这是对 RNN 如何存储多个连续变量的解释
一种用于独立成分分析的规范且生物学上可行的算法 Yanis Bahroun,Dmitri B. Chkolvskii,Anirvan M. Sengupta, 论文
大脑是一个奇妙的机器,它可以很容易地将系统信号(如听觉、视觉和嗅觉系统)分开,从系统混合物中识别潜在的来源。这项任务被称为盲源分离(BSS)。在计算机科学中,通常用独立分量分析(ICA)来解决盲分离问题。ICA 假设潜在的刺激是独立源的线性组合。尽管在文献中有成千上万种 ICA 风味,但是很少有 ICA 具有生物学启发的实现。对作者来说,生物启发意味着该算法可以 1)以流的形式运行,而无需将数据集保留在内存中 2)如果 ICA 是神经网络的一部分,则应该使用局部学习规则来更新突触权重。作者从四阶盲识别(FOBI)过程中获得灵感,探索了一种可能的生物启发 ICA 算法。
ICA 算法是以这样一种方式构造的,使得它可以在生物似然神经网络(NN-ICA)中实现。首先,神经网络输入有 d 个神经元,是要分离成独立分量的输入数据。网络呈现输入和输出的树突之间的前馈突触,以及横向突触(图 4)。实际上,输入信号首先乘以神经元的权重矩阵。输出投影然后在输出层的树枝状部分被白化。最后,躯体区域通过用局部学习规则平衡权重来计算最终输出。

图 4:生物启发 ICA 的神经网络实现。输入信号由 d 个神经网络单元混合和接收。这里前馈突触分解输入信号。输出层由两个神经元组成,其权重遵循局部学习规则。输出的树枝状部分白化数据。身体空间重建了源头。蓝色阴影意味着输出活动被调节,以模仿大脑的可塑性。
图 5 显示了来自不同输入源的结果。图 5A 报告了合成数据,其中周期信号和随机噪声作为 NN-ICA 的输入给出。图 5B 报告了在 16kHz 下记录的真实世界语音信号的结果。图 5C 的结果来自自然场景图像,其中算法从混合图像中恢复所有元素。很明显,对于大范围的输入数据集,所提出的规则可靠地收敛到正确的解。此外,整个 NN-ICA 算法易于实现,除了扩展我们关于大脑如何能够执行 BSS 任务的知识之外,还可以进一步促进神经形态计算。

图 5:神经网络独立分量分析结果。a)来自周期信号混合的结果 B)真实语音数据分解 C)自然场景图像。
我希望你喜欢 2021 年 11 月神经科学arxivg.org论文的这篇综述。请随时给我发电子邮件询问问题或评论,地址:stefanobosisio1@gmail.com
与神经科学保持同步:2021 年 10 月必读
脸书艾和因里亚👍🏻调查语言🗣️和 brain🧠;罗格斯·uni🇺🇸用生物反向传播算法推进了神经形态计算;🇩🇪 🇺🇸用信息论研究大脑状态👩💻揭示了社会任务的高信息含量!

https://medium.com/@stefanobosisio1/membership
你为什么应该关心神经科学?
神经科学是当今人工智能🧠的根源🤖。阅读并意识到神经科学中的进化和新见解不仅会让你成为一个更好的“人工智能”的家伙😎而且还是一个更好的神经网络体系结构的创造者👩💻!
这个月有很多激动人心的出版物!首先,FacebookAI 和 Inria 以及 CNRS 提出了一种新的基于模型的方法来研究大脑中的语言基础,带来了一种新的研究方法,可以与经典的和需要大量数据的实验相媲美。第二,Rutgers 大学对神经形态计算的一个了不起的改进,它定义了一个满足神经形态原则的生物启发的反向投影。最后,宾夕法尼亚大学、圣达菲大学、朱利希研究中心和亚琛工业大学合作,确定连接体如何节能和优化,以处理高信息量状态。
基于模型的大脑活动分析揭示了 305 名受试者的语言等级
夏洛特·柯谢特,亚历山大·格拉姆福特,让-雷米·金, 论文
正如我们在这个系列中多次看到的,语言的基础,语言在大脑区域中的结构,我们如何学习语言是神经科学研究中的热门话题。为了更深入地了解大脑内部的语言过程,神经科学家通常会运行一个标准的实验程序,该程序由 Lerner 等先驱成功应用,采用功能性磁共振成像(fMRI)。功能磁共振成像记录刺激后的大脑反应信号(例如,听一个故事,一个特定的段落,或杂乱的声音)。然后,对于单个受试者脑体素,利用来自其余受试者的平均脑体素活动,计算称为受试者间相关性(ISC)的相关性。最终的大脑地图定义了语言刺激特别激活的区域。虽然勒纳的技术已经被证明是成功的,但 fMRI-ISC 需要大量的数据来获得有意义的结果,随着受试者的数量乘以刺激的数量而扩大。在本文中,来自脸书 AI、Inria 和 CNRS 的研究人员提出了一个基于模型的 Lerner 实验,该实验使用更少的数据,可以达到与 ISC 研究相同的性能。
图 1 比较了这两种方法。如果一方面,Lerner 的方法“脑-脑关联”需要来自不同对象的大量记录来实现对大脑时间感受域(TRFs)的解释,那么“模型-脑关联”利用深度学习模型(如 GPT-2)的能力来实现相同的性能。GPT 模型已经得到了广泛的研究,它们在构造常规语音和修改的刺激响应的映射方面与人脑有许多相似之处。通过分析和提取第八 GPT-2 层的潜在特征,语言处理的层次被恢复和足够准确地预测。选择第八层是因为它是证明编码相关语言特征的转换器的中间层。

图 1:传统脑-脑关联和模型-脑关联的比较。a)在开创性的神经科学研究(Lerner 等人)中,每个受试者都有一个故事、故事中的一个段落、一个句子、特定的单词和声学加扰信号(条件)。b)对于每种情况,ISC 计算为平均脑反应和单个 y 脑反应之间的相关性。c)本研究试图复制传统方法,仅使用由常规故事诱发的录音。GPT-2 层用于提取加扰的激活信号。然后将该信号与线性变换后的受试者大脑活动进行比较。
图 2 报告了实验结果,其中每种颜色代表一种特定的刺激。基于模型的方法复制了相同的勒纳的实验表现,只使用 7 名受试者听 7 分钟的故事和混乱的叙述。作为收敛性的进一步证明,作者将该模型扩展到 305 名受试者,让他们听一个 4 小时的故事,展示了该模型是如何有效的。
这篇论文是一个很好的例子,它强调了自然刺激和深层神经网络对语言基础研究形成了一个强有力的结合。如果一方面我们需要了解一些深度学习模型的限制,另一方面我们可以为这些模型想出新的解决方案,将它们常规地用于体内研究。

图 2:传统神经科学研究之间的结果比较(Lerner 等人)。这种颜色描绘了大脑区域对一个段落(蓝色)、一个句子(绿色)、一个单词(黄色)和一个声音加扰信号(红色)的反应。a)在标准的无模型实验中,受试者听 7 分钟的故事或声音信号 B)基于模型的能够复制 Lerner 的研究,数据来自 75 个受试者听 7 分钟的相同故事。c)此外,基于模型能够将研究扩展到 305 个受试者,听 4 小时的 15 个音频故事。
BioGrad:用于脉冲神经网络的基于生物似然梯度的学习
唐,尼莱什库马尔,约安尼斯波利克雷蒂斯,论文
神经形态计算是一种新兴的计算范式。这种硬件是受大脑启发的,允许轻松旋转神经网络架构。最有前途的神经网络架构之一是脉冲神经网络(SNN),它已经显示出在神经形态硬件上解决人工智能问题的高能效、大规模并行和低延迟的解决方案。尽管他们取得了成功,SNN 还有一个障碍需要解决,那就是反向传播的使用。反向传播在传统的神经网络应用中已经被广泛使用,然而,它不是由人脑启发的。这导致研究寻找 backprop 的替代品,它可以满足三个神经形态原则:
- 基于尖峰的计算:每种计算都是基于尖峰的,应该提供节能的解决方案(backprop 没有)
- 局部信息处理:神经形态网络应该能够实现异步计算和大规模并行处理——这是 backprop 很难实现的
- 快速在线计算:网络不应需要来自未来时间步骤的信息(低延迟解决方案)
出于这些原因,罗格斯大学的研究人员在这篇论文中提出了生物学上看似合理的基于梯度的学习被称为 SNN 的生物梯度。图 1 显示了该算法的构建模块,它满足神经形态计算原理。该算法的核心是使用一个多隔室神经元模型,该模型由一个锋电位的躯体部分和一个非锋电位的心尖区组成。特别是,体细胞区域将前馈突触前输入整合到其膜电压中,每当电压高于阈值时就会出现尖峰。顶室接收来自错误神经元的自上而下的反馈,整合其膜电压。引入周期性睡眠阶段,使得权重以无监督的方式用局部 Hebbian 规则和随机输入更新。

图 3: A)使用 BioGra 实现的建议 SNN。SNN 从输入尖峰开始,由隐藏层进行处理。每个隐藏层作为 BioGrad 实现,由顶端(红色圆圈)和体细胞(蓝色圆圈)隔间组成。顶端隔室具有从错误神经元接收的自上而下的反馈(红色箭头),而体细胞隔室整合前馈输入(蓝色箭头)。b)调查 BioGrad。我们可以识别学习和睡眠阶段。睡眠阶段基于资格轨迹更新神经元的权重 C)突触前和反馈尖峰对隔室电压和资格轨迹的影响的例子。
该实验装置在 MNIST 数据集上进行了测试,并与生物启发的方法进行了比较(表 1)。BioGrad 在 MNIST 数据集上实现了 98.13%的准确率,这优于或相当于其他方法,但是这些方法不满足神经形态学原则。BioGrad 还经过了标准反向投影算法的测试,采用随机梯度下降或 Adam 优化器,达到了相同的精度水平。
最后,为了表明 BioGrad 算法可以直接在片上使用,作者在英特尔的 Loihi 处理器上部署了该模型。由于硬件限制,训练是在单个隐藏层上进行的,MNIST 上有 100 个隐藏单元。最终测试准确率为 93.32%,每个训练样本消耗的能量比在 GPU 上少 400 倍。
表 1:MNIST 数据集的 BioGrad 与文献中生物学启发的 backprop 的比较。BioGrad 是唯一一个满足所有三个神经形态学计算要求的方法,达到 98.13%的准确率,与其他方法相当或更好。
大脑状态的信息内容由状态能量学的结构约束来解释
莱昂·韦宁格、普拉加·斯里瓦斯塔瓦、戴尔·周、杰森·z·金、伊莱·j·康布拉斯、麦克斯韦·a·贝尔托莱罗、乌特·哈贝尔、多里特·梅尔霍夫、达尼·s·巴萨特、T3【论文】
大脑在处理一条信息时会发生什么?我们认为这一信息触发了信号传播,信号沿着大脑的结构连接体传播并引发变化。然而,我们对这些变化了解多少呢?不同状态之间的过渡如何?在宾夕法尼亚大学、亚琛工业大学、Julich 研究中心和圣达菲研究所的合作中,作者研究了 fMRI 数据集的信息内容,并证明了以下假设:
- 基于特定的认知功能,大脑会显示不同层次的信息内容
- 结构连接体被组织起来以支持观察大脑状态的转换,具有高效的能量消耗
- 达到高信息含量状态所需的能量大于达到低信息含量状态所需的能量。

图 4:信息内容状态研究。a)fMRI 可以基于特定活动 x(t)跟踪大脑区域的激活强度 B)从 fMRI 中,可以从激活的直方图中检索信息内容及其大小 C)从一个区域的激活强度,作者通过该区域的信息理论确定信息内容(Iparcel)。也可以检索整个大脑的信息内容,对大脑的所有区域求和。d)给定一个控制信号,大脑的状态从初始状态(E)移动到最终状态。f)这种运动是由能源景观决定的。
图 5a 示出了对 596 名受试者的所有观察任务的平均信息量的分析。有趣的是,社会活动任务显示出比所有其他任务显著更高的信息含量。另一方面,赌博和情绪任务显示出较低的信息含量。此外,如图 5b 所示,作者已经研究了信息内容在大脑区域的分布。工作记忆、情绪和关系任务显示出正偏斜,表明大脑中各向异性的区域分布,一些区域比其他区域贡献更大。总的来说,剩下的任务显示了高斯分布。

图 5:对 596 个亚受试者进行功能磁共振成像扫描,得到不同活动的信息内容。a)为不同活动计算的平均信息量。平均而言,社交任务的信息量较高,而情感和赌博任务的信息量最低。b)信息内容的地区差异。总体而言,大多数任务呈高斯分布,而工作记忆、情绪和关系任务呈正偏态分布。
偏斜分析导致作者调查高信息状态的能量需求,高信息状态应该远离平均状态,并且应该比低信息内容的状态需要更高的能量。事实上,根据统计分析,高信息状态(如社交任务)需要更高的能量来驱动大脑进入这些状态。特别是,结构连接体看起来结构膨胀,达到高信息含量状态,满足最终论文的假设。
综上所述,作者对 fMRI 进行了全面的统计分析,以检测大脑信息内容。社交任务显示的信息含量最高,而情感和赌博任务的权重较低。将大脑作为一个整体来分析,信息内容对大脑区域之间的任务差异很敏感,这可以用作理解大脑动力学改变、功能障碍和精神病理学的关键假设。最后,本文证明了信息论可以如何容易地应用于神经科学,以及我们可以有多少关于大脑能量约束和连接体的发现,以在不久的将来进一步扩展人工神经网络。
我希望你喜欢 2021 年 10 月神经科学arxivg.org论文的这篇综述。请随时给我发电子邮件询问问题或评论,地址:stefanobosisio1@gmail.com
与神经科学保持同步:2021 年 9 月必读
星形胶质细胞在记忆处理中的作用✨:SISSA 的研究人员致力于生物学启发的强化学习🧬;普林斯顿大学🎓和数学神经网络记忆流形👩🔬

图片由博斯科在 Unsplash 上拍摄。一张夜间高速公路照片完美地再现了在黑暗中寻找大脑连接答案的过程。
https://medium.com/@stefanobosisio1/membership
你为什么应该关心神经科学?
神经科学是当今人工智能🧠的根源🤖。阅读并意识到神经科学中的进化和新见解不仅会让你成为一个更好的“人工智能”的家伙😎而且还是一个更好的神经网络体系结构的创造者👩💻!
这个月 3 篇惊人的论文!第一个向我们展示了人脑和人工神经网络中星形胶质细胞的重要性。星形胶质细胞对于人类的记忆存储和处理至关重要,本文为所有数据社区敲响了警钟,让他们更密切地关注生物启发的神经网络。第二篇论文来自 SISSA 神经研究人员,他们将行为克隆规则转化为强化学习,使这种方法更受大脑启发。最后一篇论文是普林斯顿大学的一项艰巨工作,在那里,作者创建了一个新的门控递归神经网络(gRNN),它可以存储记忆而无需微调参数,只是从 gRNN 的基础知识中发展了进一步的数学见解。尽情享受吧!😃
星形胶质细胞在多层神经元-星形胶质细胞网络中介导模拟记忆
尤利娅·齐比纳、因诺肯蒂·卡斯塔尔斯基、米哈伊尔·克里沃诺索夫、阿列克谢·扎伊金、维克托·卡赞采夫、亚历山大·戈尔班、苏珊娜·戈尔德列娃
我们能在人工神经网络中模拟星形胶质细胞吗?人脑中星形胶质细胞的作用是什么?在这篇论文中,作者将他们的注意力集中在一个长期未知的问题上:大脑如何处理信息并将其作为记忆储存,在星形胶质细胞层中找到了一个可能的解决方案。星形胶质细胞是神经胶质细胞家族的一部分。它们通常呈星形,主要职责是处理神经元的突触。例如,一个人的星形胶质细胞可以同时与多达 200 万个突触相互作用😱特别是,星形胶质细胞通过钙离子调节突触神经元传递,导致放电频率的调节。这些调节已被证明与工作记忆有关,揭示了星形胶质细胞记忆处理的关键作用。
在这项工作中,作者进一步发展了以前的生物启发神经网络模型(SNN ),增加了星形胶质细胞介导的反应作为突触权重的变化,以存储输入图像的记忆。SNN 模型是由稀疏连接的伊兹克维奇神经元组成的。这里,通过微分方程描述神经元,该微分方程考虑了神经元跨膜电位,取决于输入信号、来自所有突触前神经元的总突触电流和星形胶质细胞通过钙离子诱导的电位调制。两层神经元与星形胶质细胞层相互连接,由 Ullah 的模型模拟。星形胶质细胞可以与神经元群进行双向交流,一方面提供生物相似性,另一方面提供信息的加载、存储和检索。
该模型被训练以记忆灰度图像(图 1)。这些图像被转换成输入电流,供给神经元层。神经元以不同的速率放电,这取决于输入电流的幅度。这种反应的差异引发了星形胶质细胞的钙反应,形成了每一个输入的特定模式。这种钙浓度分布持续几秒钟,并参与记忆储存过程。

图 1:A)SNN-星形胶质细胞模型。输入 BW 图像馈入神经元网络。每个神经元都有自己的放电频率,组成一个整体反应(神经元层中的蓝色阴影)。放电频率由星形胶质细胞调节。双向连接有助于分类过程,在神经元层解码为输出模式。b)生物学相似性:神经元集合触发星形胶质细胞中的钙浓度水平。结果,谷氨酸被释放,因此突触强度可以被调节。
图 2 显示了伴随星形胶质细胞双向调节的 SNN 的输入-输出响应的实际例子。一方面,输入灰度图像用于刺激神经元活动。神经元反应由星形胶质细胞调节,星形胶质细胞最终能够存储来自钙浓度信号的图像。这个信号持续几秒钟,这允许系统保持原始信息并检索它。

图 2:输入图像信号 A)的例子,该图像被转换为输入电流 B),来自神经元层放电速率 C)的结果和来自钙浓度的星形胶质细胞记忆。
这是一个了不起的结果,一如既往地提醒了数据科学界。事实上,这篇论文是向大脑启发的人工智能迈出的一小步。可以在神经形态计算中找到应用,其中可以通过简单地考虑星形胶质细胞介导的反应来增强神经元和突触计算。此外,星形胶质细胞层提供一次性学习,这是对通常的通用神经网络架构的巨大改进。这种方法可以帮助实现比深度学习本身更好的结果,在训练过程中使用更少的数据!
循环尖峰网络中的行为克隆:一个综合框架
克里斯蒂亚诺·卡彭,保罗·穆拉托雷,皮尔·斯坦尼斯劳·保鲁奇,https://arxiv.org/abs/2109.01039
正如我们在前一篇论文中看到的,学习方法是神经科学中的一个热门话题。特别是,有两种互补的学习方法:基于错误策略的学习和基于目标策略的学习。在前一种方法中,误差信息被注入到神经网络中,并用于改善未来的性能,而在基于目标的方法中,选择并学习目标。在本文中,作者设计了一个新的更通用的框架,它可以被视为基于错误和基于目标的方法的来源,为神经网络学习动力学提供了新的见解。这种普遍观点可以看作是模仿学习和行为克隆的自然进化。特别地,基于文献,该模型假设了基于尖峰定时的神经网络的形式,该神经网络在实验上被认为在大脑中是关键的。
作者提出了一个循环尖峰模型,其中每个神经元可以暴露一个可观察的状态,该状态代表一个神经元在某一时间出现一个尖峰。这个模型必须与环境互动,以解决特定的任务。该模型从模仿学习最优策略中学习,而不是使用强化学习。模仿学习允许代理在给定一组状态的情况下复制一组专家行为。对于学习步骤,模型通过目标而不是从错误中学习。内部权重受反馈矩阵的影响,反馈矩阵的等级用作基于其等级值检查学习的度量。
这里调查了两种情况:按钮和食物任务,其中代理必须按下按钮解锁食物并够到它,以及来自 OpenAI 数据集的 2D 两足步行器,其中代理必须学习走路并尽可能远地旅行。我们将把注意力集中在第一个实验上,其结果如图 3 所示。在这个实验中,针对反馈矩阵的不同秩值来训练代理。所有的训练条件都显示出结果的趋同性。当给出高等级的反馈结构时,奖励是最大的。在第二个实验中,2D 两足步行器,尖峰脉冲时间被调整,而不是反馈矩阵秩,这证明了学习也是特定尖峰脉冲模式的结果。

图 3:按钮和食物任务 A)任务,代理必须点击红色按钮来解锁目标食物。一旦食物被解锁,代理必须找到到达食物的轨迹,不要忘记按钮已经被按下。b)由受过训练的代理为不同目标位置产生的轨迹的例子。箭头(紫色)是专家行为。c)作为目标位置的函数的最终奖励(通过角度测量),重复 10 次。d)作为反馈矩阵秩 d 的函数的平均奖励
以下是结论:
- 反馈矩阵秩修改导致代理的更高的解决方案空间,这可以阐明错误如何在大脑的不同区域传播的实验发现
- 另一方面,典型的运动任务确实需要并受益于精确的定时编码。这对于获得更精细的移动控制以实现更好的性能可能是必要的。在这种情况下,高等级是不相关的,而尖峰调制是相关的。
记忆流形的出现
Tankut Can,Kamesh Krishnamurthy, 论文
这不是一篇容易阅读的论文,因为它的根源是在一个新的机器学习数学领域。我将很快就此发表一些东西,特别是关于 Martin-Siggia-Rose-De Dominic is-Jansen(ms rdj)形式主义,它为神经网络带来了新的见解。这篇论文背后有一个简单的想法。
人脑可以储存记忆,并根据输入的任务在合适的时间提取记忆。这意味着生物系统可以在比神经元反应的内在时间尺度更长的时间内保持记忆。对于计算神经网络来说,这是一个难以解决的问题,即使有了最新的进展,在正确的时间利用记忆仍然是一个巨大的障碍。
从数学的角度来看,记忆/大脑系统不断产生变量,这就产生了一个对象:记忆流形。流形是一种几何结构,存在于多维空间中,其中每个点都可以被视为给定输入问题的固定点(解决方案)。从这里进一步的数学问题出现了:流形是稳定的吗?什么时候不稳定?有分叉点吗?歧管有需要调整的参数吗?
为了在计算上解决这个记忆流形问题,作者为神经网络定义了一个新的数学视角。换句话说,论文的关键问题是:如何在不改变数量或使用特殊对称性或微调参数的情况下,在神经网络中实现内存?最终目标是扩展网络的内存,而不要过多地使用它的参数,在正确的时间使用内存,所以很长一段时间没有看到这些。
作者将记忆流形定义为处于边际稳定点的系统。然后,他们用“冻结稳定”(FS)定义了一个可能的场景。FS 是一个过程,其中神经网络家族可以自组织到一个临界状态,表现出记忆流形而无需进一步调整。因此,递归神经网络(RNN)框架是从其数学根源发展而来的,定义了一个二元变量,该变量可以根据当前系统状态减慢系统的一部分。

图 4: a)香草 RNN (vRNN ),其中每个节点都有随机偏差,动态通常流向定点 b)门控 RNN (gRNN ),其中一半的种群被冻结,另一半在任何给定的时间内自由进化。冻结的那一半给进化的那一半提供了随机偏差。gRNN 的相图(黑线),稳定 vRNN 动态所需的临界方差(红色虚线)。
早期的结果非常令人鼓舞。FS 允许神经网络自组织到展示记忆流形和长时间尺度的状态。神经元的内部动力学是冻结的,而它的一部分仍在进化,并偏向另一半。
我希望你喜欢 2021 年 9 月神经科学arxivg.org论文上的这篇综述。请随时给我发电子邮件询问问题或评论,地址:stefanobosisio1@gmail.com
通过线性模型保持竞争力
可交代的 AI
有了正确的特征,线性模型可以变成野兽

作为一名数据科学家,你有许多不同类型的闪亮的机器学习模型可供选择。有神经网络模型、梯度增强模型、bagging 模型,仅举几个主要类别。
在这些家伙的阴影下,也存在着像线性和逻辑回归这样的模型。线性模型通常在最好的情况下仅仅被用作基线,在最坏的情况下被取笑。然而,我认为这是不合理的。简而言之,我的请求如下:

作者在 imgflip.com 创作的。
让我通过考虑回归任务来解释我的意思,但是记住这同样适用于分类任务。
首先,我将向您展示带有坏特征的线性回归会导致什么,这样您就不会上当。然后,我将向您展示线性回归是非常通用的,如果给予良好的特性,它可以用于模拟复杂的模型。最后,我们将使用线性回归进行简单有效的时间序列预测。
添加多项式特征(不好!)
我们可能已经全部完成了。创建新要素的一种最常见、最简单但也最可怕的方法是添加基础要素的多项式幂。除了只使用特征 x 之外,您还可以添加 x ,x ,… 作为特征,然后进行多元线性回归。让我们在 x 中创建一个非线性的小型一维数据集来说明这一点,这样每个人都在同一页上。
import numpy as np
np.random.seed(123)
x = np.random.uniform(0, 10, 10)
y = 3*np.sin(x) + x + np.random.randn(10)
以 x 为唯一特征的简单线性回归产生一条不太符合数据的直线。

图片由作者提供。
现在让我们添加一些四次幂的多项式项。
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline
lr = make_pipeline(
PolynomialFeatures(degree=4),
LinearRegression()
)
lr.fit(x.reshape(-1, 1), y)
我们得到了这种契合:

图片由作者提供。
好多了,对吧?我们可以通过设置degree=9变得更疯狂,得到如下结果:

很难看出,但这是一个完美的拟合,它通过所有的点。不幸的是,曲线围绕摆动很大。训练集中的所有标签 y 都在 0 到 12 之间,但是该模型以某种方式预测大约x1 的值大于 10.000。为什么?因为多项式特性不好。可悲的是,这通常是线性回归旅程的终点。
“线性模型是给孩子们看的,让我们继续看好的东西。涉及树或者前馈连接的模型,你懂的!”—也许是你?
然而,让我告诉你为什么这样想是错误的。可以创建行为完全类似于神经网络的线性模型或基于决策树的模型。你只需要好的特性,而不是多项式特性。
模拟高级模型
神经网络
快速提醒一下,这是一个简单的前馈神经网络的例子,它有一个大小为 3 的隐藏层,将一个数字 x 作为输入,输出一个值 y (没有偏差):

图片由作者提供。
可以看到 x 进入神经网络。然后使用三种不同的变换对其进行变换 T ₁、 T ₂和 T ₃,留给我们三个新值x₁=t₁(x)、x₂=t₂(x和 x ₃ = 【T36 这些变换通常涉及乘法、求和以及某种非线性激活函数,如 ReLU 或 Sigmoid 。
然后,这三个导出值被用作神经网络最后一步的输入:将它们乘以一些值 α ,然后将所有值相加。因此,最终的输出是y=α₁x₁+α₂x₂+α₃x₃=α₁t₁(x+α【t67
观察最后一个等式是如何在三个新特性 x ₁、 x ₂和 x ₃中只是一个线性模型!
这就是神经网络的神奇之处:特征是自动组装的。我们不必像处理多项式特征那样手工制作它们,而是由网络来学习它们。但是一旦我们得到了这些新的特征,最后一步就是运行一个简单的线性回归。
让我们看一个使用小玩具数据集的具体例子。
from sklearn.neural_network import MLPRegressor
mlpr = MLPRegressor(
hidden_layer_sizes=(3,),
random_state=142,
max_iter=10000
)
mlpr.fit(x.reshape(-1, 1), y)
这会产生以下输出:

图片由作者提供。
这一切都很好,但现在我声称,当使用正确的特性时,我们可以通过简单的线性回归来重新创建这个输出。而我们可以从训练好的神经网络中得到这些特征。下面的代码模拟了神经网络在中间部分所做的事情:它将我们的 10 个一维输入样本转换为 10 个三维智能输入样本。
transformation_coefs = mlpr.coefs_[0]
transformation_biases = mlpr.intercepts_[0]
x_linear_transformation = x.reshape(-1, 1) @ transformation_coefs + transformation_biases # linear transformation
x_clever = np.maximum(x_linear_transformation, 0) # ReLU
从图形上看,这种情况会发生:

图片由作者提供。
我们现在可以用x_clever代替x来训练一个线性回归模型。结果如下:

图片由作者提供。
请注意,线性回归拟合略有不同。事实证明,它对训练数据的跟踪甚至比神经网络的输出更好!这是因为神经网络必须一次优化许多参数——它必须专注于特征创建和最终的实际线性回归。特别是第一步要对麻烦负责,因为它使拟合过程优化非凸函数,这是困难的。另一方面,找到线性回归的最佳参数很容易。
因此,在这种情况下,我们甚至可以提高一点点性能,至少在训练集上。我们仍然要小心过度拟合,但这不是我们在这里的主题。关键是:有了正确的特征,我们能够建立一个像神经网络一样好的模型。
我已经听你说过了:
“好吧,那么我们先训练一个神经网络。然后,我们从中提取特征来训练一个线性模型,它和原始的神经网络一样好。为什么不直接用神经网络?”
你是对的。我们投入了更多的工作来创建一个与原始神经网络一样好的线性模型。但是我从来没有说过找到好的特性是容易的。这实际上是建模最难的部分。
艺术是想出好的特征,而不必窥视一些神经网络正在做什么。这些特征也可能看起来与神经网络创建的完全不同。我们不需要使用 ReLU 函数或者类似的函数。例如,在我们的例子中,sin( x )将成为一个很好的特性,因为我们甚至在数据生成过程中使用了它。
总之,我们已经看到了如何使用线性回归来模拟神经网络的输出。现在让我们转向树木,因为我们可以在那里做同样的事情!
决策树
我不会介绍决策树是如何工作的,因为有很多很好的资源可以让你重温这方面的知识。你只需要知道决策树输出分段常数函数。让我们训练一个深度为 2 的决策树。
from sklearn.tree import DecisionTreeRegressor
dt = DecisionTreeRegressor(max_depth=2)
dt.fit(x.reshape(-1, 1), y)
结果是具有(最多)个 2^max_depth 不同值的分段常数函数。

图片由作者提供。
为了提取允许我们模拟这个函数的特征,函数值变化的点——分裂点——是很重要的。我们可以这样得到它们:
from sklearn.tree import plot_tree
plot_tree(dt)

图片由作者提供。
此外,让我们在-∞和∞处再增加两点。总的来说,我们在(-∞,4.08,6.18,6.91,∞)处有分裂点,我们创建的特征将如下所示:

图片由作者提供。
其中( a ₁=-∞ ,a ₂ ,a ₃,…,∞)为拆分点,升序排列。
注: 这基本上意味着我们 bin 了样本,见下图。
对于熊猫,你可以这样做:
import pandas as pd
split_points = [-np.inf, 4.076, 6.181, 6.906, np.inf]
x_clever = pd.get_dummies(
pd.cut(
x,
bins=split_points),
prefix='x'
)

使用这些新特性进行线性回归,我们可以得到:

图片由作者提供。
在这种情况下,您无法区分原始模型和线性模型,它们完全相同。这是因为决策树不像神经网络那样有数字上的困难。于是,我们学会了如何用线性回归模拟单个决策树!
再次强调:想出这些特性并不容易,但是一旦你拥有了它们,一切都变得简单了。
我们甚至可以从这里继续:随机森林、AdaBoost、梯度增强、额外的树……都只是具有特殊权重的决策树的总和。分段常数函数的和也是分段常数函数,只是可能有更多的分裂点。这意味着,只要有合适的特性,我们也可以用线性模型模拟这些更高级的树模型!
时间数列预测法
到目前为止,我们已经处理了很多抽象的东西。理论上,线性模型和更复杂的模型一样好,只要有正确的特征。但是,如果我们不想使用预先训练好的神经网络或基于树的模型,这些正确的特征会是什么样子呢?
我认为一个简单但非常有用的领域是时间序列预测,这是一项在你的日常数据科学工作中经常出现的练习。
数据
现在让我们用一些真实的数据。首先,通过!pip install sktime安装 sktime,这是一个很棒的时间序列预测库。我们可以得到如下的航空乘客数据集:
from sktime.datasets import load_airline
y = load_airline()
数据看起来像这样:

图片由作者提供。
有两件事应该引起你的注意:
- 有上升趋势,而且
- 每年都有季节性。
如果人们想做一个预测,通常他们会选择统计方法,比如 ARIMA T2、萨里玛、萨里玛……但是我不太喜欢这些方法,因为它们太复杂了。让我们做一些简单的事情,用可解释的特性和一个简单的线性模型。
简单的线性模型
为了捕捉上升趋势,我们可以创建一个向上计数的特征。最早的观察值—在我们的例子中是 1949 年 1 月—得到值 0,1949 年 2 月得到值 1,1949 年 3 月得到值 3,…在代码中:
import numpy as np
from sklearn.linear_model import LinearRegression
x = np.arange(len(y)).reshape(-1, 1)
lr = LinearRegression().fit(x, y)
y_forecast = pd.Series(lr.predict(x), index=y.index)
结果看起来像这样:

图片由作者提供。
它看起来并不可怕,尤其是考虑到我们到目前为止只添加了一个单一的功能。要模拟每年的季节性,最简单的方法是为每个月创建一个热编码变量,一个例子是特征“month_5 ”,它在第 5 个月(5 月)的值为 1,在其他月份的值为 0。你可以这样做:
X = pd.get_dummies(y.index.month, prefix='month')
X['trend'] = range(len(y))
X.index = y.index

图片由作者提供。
这些都是非常便宜的特征,但是看看基于它们训练的线性模型给我们带来了什么:

图片由作者提供。
这相当不错。模型捕捉到了趋势和季节性,很容易将这条曲线延续到未来。然而,只有最后一个挑战:季节性的强度随着时间的推移而增加。我们的模型尽了最大努力,但在开始时,我们的模型在附近摆动得更强,最后比原始时间序列更弱。这是我们无法用更多的功能来解决的。但是处理这个问题的一个简单方法是
- 用对数变换原始标签
y, - 对这个新数据集使用线性回归,然后
- 使用求幂运算将预测值转换回来。
在 scikit 中有一个简洁的方法——学习实现这三个步骤,而不需要太多的开销并跟踪转换:
from sklearn.compose import TransformedTargetRegressor
from sklearn.linear_model import LinearRegression
lr = TransformedTargetRegressor(
regressor=LinearRegression(),
func=np.log, # use logarithm on the no. of passengers
inverse_func=np.exp # transform the data back with exp
)
lr.fit(X, y)
然而,结果是惊人的,在训练集上 r 为 0.98,。

图片由作者提供。
我希望你能享受这有多棒!该模型仍然是一个简单的线性模型,即使进行了变换。此外,构建特征是琐碎的,然而结果是极其精确的。像这样的模型是黄金。

完整的模型,简化的符号。图片由作者提供。
你甚至可以创建一个更长的时间范围,并获得预测。
future = pd.date_range(
start='1949-01-01',
end='2021-12-31',
freq='M'
)
X_future = pd.get_dummies(future.month, prefix='month')
X_future['trend'] = range(len(future))
X_future.index = future
如果我们把这个输入模型,我们会得到 1949 年 1 月到 2021 年 12 月之间的预测。

图片由作者提供。
根据该模型,我们今年的航班乘客人数将接近 100 万。然而,这个模型被输入了非常旧的训练数据,而且它也不了解新冠肺炎,所以对这个预测有很大的怀疑。根据我找到的数字,在 Covid 之前,2019 年每月约有 40 万名航空乘客。我们的模型说的是 60 万左右的东西,至少和真相差不多一个量级。但这说明过去的增长比现在高,是有道理的。这就是为什么该模型高估了真实的航空乘客数量。
结论
我们已经看到,就性能而言,说线性模型不好而复杂模型好通常是错误的。通过正确的功能,线性模型可以像神经网络或梯度推进一样执行。不幸的是,找到这样的优点很难,但这也是这份工作有趣的地方。如果对原始数据进行简单的拟合/预测就足够了,许多数据科学家可能明天就会失业。
虽然有时候想出好的特性很容易。当处理时间序列数据时,一个很好的起点是添加月份、星期几、星期几。或者像圣诞节、复活节、黑色星期五这样的特殊日子。包含一个趋势特征也是很好的。这可以是一个从零到观测值减一的数,正如我们在飞机乘客的例子中所看到的。但这只能让我们模拟一个线性趋势。我们还可以添加一个二次或根趋势,我们所要做的就是应用一些简单的函数,例如,0,1,4,9,…,,(#observations-1)。找到正确的指数,让它成为 1、2、0.5 或其他值是超参数调整的典型用例,您不必手动尝试所有东西。
因此,精心制作一些有意义的特性,尝试一下线性模型。这样做的好处是,该模型易于解释,快速且健壮,并且您可以确保线性回归适用于每种编程语言。这是而不是像 CatBoost 等高级模型的情况。
我希望你今天学到了新的、有趣的、有用的东西。感谢阅读!
作为最后一点,如果你
- 想支持我多写点机器学习和
- 无论如何都要计划获得中等订阅量,
为什么不做 通过这个环节 ?这将对我帮助很大!😊
透明地说,给你的价格不变,但大约一半的订阅费直接归我。
非常感谢,如果你考虑支持我的话!
如有问题,请在 LinkedIn 上写我!
正确使用熊猫类别是很棘手的,原因如下…
了解常见的陷阱和意想不到的行为,如何避免让猫抓你
在 pandas 中,分类数据类型经常被吹捧为减少数据帧内存使用的简单方法,它们确实是一个有用的工具。然而,如果你想象你可以在代码的开头加上一个.astype("category"),其他的都一样(但是更有效),你可能会失望。
本文主要关注在 pandas 中使用分类数据类型时可能会遇到的一些现实问题;要么调整您现有的思维模式,使用类别编写新代码,要么尝试使用类别列将现有管道迁移到流中。
本文中描述的行为是截止到*pandas==1.2.3*(2021 年 3 月发布)的当前行为,但是如果您在更晚的日期阅读本文,请不要担心,所描述的行为不太可能在未来的版本中有重大改变——但是如果有,请留下评论!
为什么在 pandas 中使用分类数据类型?
如果分类数据是一种痛苦,那么为什么不干脆完全避免它呢?嗯,使用类别可以带来一些显著的好处:
- 内存使用 —对于有许多重复值的字符串列,类别可以大大减少在内存中存储数据所需的内存量
- 运行时性能 —有一些优化可以提高某些操作的执行速度
- 库集成 —在某些情况下,库对分类列有特殊的功能,例如
lightgbm在构建模型时以不同的方式处理分类列
快乐之路
让我们做一个强制性的“快乐之路”的例子。这是在一个简单的世界里,每个人都互相微笑的样子:
df_size = 100_000
df1 = pd.DataFrame(
{
"float_1": np.random.rand(df_size),
"species": np.random.choice(["cat", "dog", "ape", "gorilla"], size=df_size),
}
)
df1_cat = df1.astype({"species": "category"})
这里我们创建了两个数据帧,df1包含作为对象列的species和df1_cat,后者是相同数据帧的副本,但是使用species作为分类数据类型。
>> df1.memory_usage(deep=True)Index 128
float_1 800000
species 6100448
dtype: int64
我们可以在这里看到,根据我们的内存使用情况,保存一个包含字符串的列是多么昂贵——这里的字符串列占用大约 6MB,如果这些字符串更长,它会占用更多;与之相比,float 列占用了 0.8MB。我们可以用一个字符串列的价格购买近 8 个float64列……很贵。
>> df1_cat.memory_usage(deep=True)Index 128
float_1 800000
species 100416
dtype: int64
在转换到一个类别后,查看内存使用情况,我们看到了一个相当大的改进,内存使用量减少了 60 倍,非常好。现在,我们可以用一个float64列的价格购买 8 个这样的字符串列,哦,形势发生了多么大的变化。
这很酷,但是,只有我们能保持这种状态才是真正的酷…
分类列是脆弱的东西
听起来很奇怪吗?嗯……处理类别很像玩那些摇摇晃晃的洋娃娃,当你把它们推过来时,它们又会弹回来;如果您不密切关注使用分类列的每个操作,它们将很容易返回到object中。
在最好的情况下,这是令人讨厌的,在最坏的情况下,它会降低您的性能(因为转换类型很昂贵),和/或耗尽您的内存(因为您只能将这些数据作为一个类别存储到您的内存中)。
对分类列进行操作

这是我对猫手术的描述。我知道通常情况下,人类会给猫做手术,但是现实比小说更奇怪:我们有熊猫给猫做手术!
很可能在某个时候,你会想对你的分类列做些什么,其中之一可能是一个转换。这是第一个我们必须表现出勤奋的地方…
由于分类列通常是基于文本的列,让我们来看一个使用字符串操作的例子,我们可以像通常对基于文本的object列那样对分类列进行操作;通过使用.str访问器。
非分类字符串系列:
>> %timeit df1["species"].str.upper()25.6 ms ± 2.07 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
关于分类字符串系列:
>> %timeit df1_cat["species"].str.upper()1.85 ms ± 41.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
分类版本在性能上是明显的赢家,在这种情况下大约快了 14 倍(这是因为内部优化意味着.str.upper()只对唯一的类别值调用一次,然后从结果中构造一个序列,而不是对序列中的每个值调用一次)。然而,这是我们遇到的第一个主要问题…
>> df1_cat["species"].str.upper().memory_usage(deep=True)6100576
我们已经丢失了我们的分类类型,结果是一个object类型的列,数据压缩也消失了;结果现在又是 6MB 大小。在此之后,我们可以重铸回一个类别,但这需要在类型之间来回转换,这会使我们的代码更加混乱,并且不会降低我们的内存使用峰值。
有效的替代方案
通常,一种有效的替代方法是重写操作分类列的代码,直接对类别本身进行操作,而不是对它们的值序列进行操作。这需要在思维模式(和实现)上做一点改变,你可以认为它只是对列中的每个唯一的值做一次操作,而不是对列中的每个实例。如果在 100,000 行的类别中只有 2 个唯一值的列,那么只需执行 2 次操作,而不是 100,000 次。
例如,在上述情况下,我们可以执行以下操作:
%timeit df1_cat["species"].cat.rename_categories(str.upper)239 µs ± 13.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
这比前面的任何一个选项都快 10 倍,主要的好处是我们从不将分类系列转换成昂贵的中间object列状态,所以我们保持了很好的内存效率。
这是一个玩具示例,但原则适用于更复杂的示例,如果没有特定的.cat访问器方法来帮助您的特定情况,请考虑对包含唯一分类值的df1_cat[“species”].dtype.categories进行操作,而不是对整个系列进行操作。
与分类列合并

描绘一些合并的猫确实是一个巨大的尝试。
在除了最简单的用例之外的所有用例中,我们可能不只有一个数据帧,而是有多个数据帧,我们可能希望在某个时候将它们粘在一起。例如,我们将收集一个小型“参考”数据集,其中包含第一个数据集的物种栖息地:
df2 = pd.DataFrame(
{
"species": ["cat", "dog", "ape", "gorilla", "snake"],
"habitat": ["house", "house", "jungle", "jungle", "jungle"],
}
)
df2_cat = df2.astype({"species": "category", "habitat": "category"})
和以前一样,我们已经创建了这个数据集的一个分类版本,以及一个带有object字符串的版本。这里需要注意的一点是,我们有一个额外的物种(snake),这是我们将与之合并的数据帧df1中没有的,这在以后会很重要(但不要担心不会有测试)。
我不会展示将两个object列合并在一起的例子,因为你们都知道会发生什么,object + object = object,没有魔法,这只是一个合并。
在分类列上合并对象列
在这个小测试中,我们将选取一个分类数据帧,并将其与另一个数据帧上的对象类型列合并。
>> df1.merge(df2_cat, on="species").dtypesfloat_1 float64
species object
habitat category
dtype: object
所以这里我们有左边的物种object和右边的物种category。我们可以看到,当我们进行合并时,我们得到了结果数据帧中合并列的category + object = object。如此等等等等,当我们回到object s 时,这又一次在记忆中击中了我们。这并不令人惊讶,但同样,这是在处理类别时需要注意的事情。
合并两个分类列
希望我已经让你认为我正在走向category + category = category。让我们来看看:
>> df1_cat.merge(df2_cat, on="species").dtypesfloat_1 float64
species object
habitat category
dtype: object
报警,我被骗了… category + category = object。
那么尾巴上的刺是什么?在合并中,为了保留分类类型,两个分类类型必须完全相同。与 pandas 中的其他数据类型不同(例如,所有的float64列都具有相同的数据类型),当我们谈论分类数据类型时,数据类型实际上是由特定类别中存在的一组值来描述的,因此您可以想象包含["cat", "dog", "mouse"]的类别与包含[“cheese”, “milk”, “eggs”]的类别是不同的类型。虽然在我们的例子中,它们看起来非常相似(一个是另一个的子集),但在df2中,我们有一个物种在df1中不存在(狡猾的snake)。
我们可以这样看待合并列的行为:
category1 + category2 = object
category1 + category1 = category1
因此,采用前面的例子,我们可以得到我们想要的和预期的结果:
>> df1_cat.astype({"species": df2_cat["species"].dtype}).merge(
df2_cat, on="species"
).dtypes float_1 float64
species category
habitat category
dtype: object
从上面可以看出,设置分类类型进行匹配,然后进行合并,得到了我们想要的结果……最后。
使用分类列分组

我不是一个爱猫的人,我不清楚如何最好地对猫进行分类(我想它们中的很多会下地狱)
在修改代码以处理分类数据类型时,用类别分组曾经让我自己伤透了脑筋。一次导致行为意外改变,给出一个充满空值的数据帧,另一次导致操作无限期挂起(尽管以前使用object数据类型只需要几秒钟)。
当您对分类数据类型进行分组时,默认情况下,您会对该数据类型中的每个值进行分组,即使它不在数据本身中。
那是什么意思?最好用一个例子来说明。
habitat_df = (
df1_cat.astype({"species": df2_cat["species"].dtype})
.merge(df2_cat, on="species")
)house_animals_df = habitat_df.loc[habitat_df["habitat"] == "house"]
所以这里的habitat_df是上一节的合并示例(其中species和habitat最终都是分类的),而house_animals_df只包含住在房子里的动物,在我们的例子中是cat和dog。让我们试着找出这些species的平均值float_1。
>> house_animals_df.groupby("species")["float_1"].mean()species
ape NaN
cat 0.501507
dog 0.501023
gorilla NaN
snake NaN
Name: float_1, dtype: float64
有些事情看起来有点不对劲,我们现在在 groupby 中得到一堆空值。默认情况下,当对分类列进行分组时,pandas 会为类别中的每个值返回一个结果,即使这些值不在数据中。这可能很烦人(因为这是行为上的意外变化),如果数据类型包含许多相关数据帧中不存在的组,尤其是在多个不同的分类列上分组时,这可能会影响性能。
为了得到我们想要的结果,我们可以将observed=True传递给 groupby 调用,这样可以确保我们只得到数据中值的分组。
>> house_animals_df.groupby("species", observed=True)["float_1"].mean()species
cat 0.501507
dog 0.501023
Name: float_1, dtype: float64
是的,但这又是一个让我们保持警觉的陷阱。
分类列索引

这是一个猫专栏,你明白吗?
这个例子稍微有点小众,但它为你有时会遇到的意外问题增加了一点色彩,如果你在使用范畴时没有保持头脑的话。有些情况下,您可能会将行值移动到列中,例如,groupby-unstack组合有点像职业玩家的移动。
>> species_df = habitat_df.groupby(["habitat", "species"], observed=True)["float_1"].mean().unstack()
>> species_df species cat ape dog gorilla
habitat
house 0.501507 NaN 0.501023 NaN
jungle NaN 0.501284 NaN 0.501108
现在我们有了一个数据框架,列中有物种,到目前为止一切正常,让我们添加另一列,看看会发生什么。
>> species_df["new_col"] = 1 TypeError: 'fill_value=new_col' is not present in this Categorical's categories
通常这段代码完全没问题,我们只是试图添加一个名为new_col的新列,它的值总是 1。只是看了一眼数据帧,看起来没有任何错误,错误似乎有点令人困惑,它在谈论哪个Categorical,我们只是试图添加一个新列?
所发生的是.unstack()(对于外行来说,它将索引翻转到列中,就像一个支点)将分类索引移动到列索引中。不可能将条目添加到不属于分类数据类型的分类索引中,因此会出现错误。虽然这并不是立即显而易见的,但如果你最终来到这里,挠头也是情有可原的。
可能不存在拥有分类列索引的强有力的例子,但是如果意外地拥有了一个分类列索引和/或您开始看到类似这样的奇怪错误,那么检查您正在处理的所有事物的数据类型并确保没有任何奇怪和分类的事情发生可能是值得的。
结论
Pandas categorical dtypes 很酷,并且有一些好的性能优势。当采用分类数据类型时,了解数据类型在不同的常见情况下如何表现是很重要的,尤其是对来说,确保分类在整个程序流程中保持分类,而不是返回到object。
以下是一些需要注意的事项的高度概括:
- 对分类列进行操作时,选择对数据类型中的类别进行操作的选项,而不是对包含该数据类型的系列中的值进行操作的选项。这应该允许您保留分类的性质,同时提高性能。
- 在分类列上进行合并时,请注意,为了保持分类性质,每个数据帧的合并列中的分类类型必须完全匹配。
- 当对分类列进行分组时,默认情况下,您将获得数据类型中每个值的结果,即使它不在数据中,您也可以使用
.groupby中的observed=True来更改它。 - 当你期望工作的事情出乎意料地停止工作时,考虑是否有一种与范畴的奇怪互动在起作用
祝你好运,并祝你快乐!
用 NLTK 对文本进行词干分析
词干分析是文本规范化中使用最广泛的技术之一。在这篇文章中,我们将探索 nltk 的三个最著名的词干分析器

马库斯·斯皮斯克的照片
T 处理文本数据的能力是一项巨大的技术成就,使公司和组织能够处理非结构化信息,并根据过去极难收集和存储的信息支持决策。
自然,由于计算机不像人类那样真正理解字符和单词,这带来了许多新的挑战,这些挑战可以通过使用专门处理文本数据的特定技术来解决,如词干化或词汇化。
在这篇文章中,我们将探讨词干的概念,以及词干如何帮助你挖掘文本管道。
词干定义
词干提取,也称为后缀剥离,是一种用于减少文本维数的技术。词干化也是一种文本标准化类型,使您能够将一些单词标准化为特定的表达式,也称为词干。
让我们从一个句子的例子开始:
这绝对是一个争议,因为律师称该案件“极具争议”
如果我们想将这个文本表示为一个数组(查看本系列的另一篇文章 )单词争议和争议**会被认为是不同的标记,并在您可能构建的任何数组中有不同的位置。
词干有助于我们标准化有相同后缀的单词,这些单词通常是语法相似单词的派生词。
但是,像往常一样,在几个文本挖掘管道中,有许多可能导致信息丢失的选择。请记住,考虑到我们今天能够在 NLP 管道中应用的计算能力和更高级的模型(例如深度神经网络),在 NLP 领域中词干提取是否真的是一个好主意,这是一个正在进行的讨论。
词干提取对于处理大量维度的算法来说可能是个好主意。回想一下,词干分析是一种标准化技术,但是像大多数标准化问题(例如主成分分析)一样,它会导致你丢失文本中的一些原始信息。对于 NLP 管道来说,这是一个好的还是坏的应用程序,这实际上取决于您使用的模型、您保存数据所需的空间量(尽管现在空间应该不是问题)以及您想要使用的算法的特征。
鉴于此,让我们探索一下 NLTK(自然语言工具包)库中最常见的词干分析器。
波特的斯特梅尔
波特的斯特梅尔实际上是计算机科学中最古老的词干分析器应用程序之一。它第一次被提及是在 1980 年 Martin Porter 的论文 中的一种后缀剥离 算法,它是【nltk】中广泛使用的词干分析器之一。
波特的斯特梅尔应用一套五个连续的规则(也称为阶段)来确定句子中常见的后缀。举例来说,第一阶段首先从句子中移除复数和过去分词,例如单词 cats 被转换成 cat :
猫->猫
从第二阶段到第五阶段,波特的算法根据前面辅音和元音的不同组合来阻止常见的词尾。
每个阶段的详细描述见此处。
porter stemmer 最重要的概念之一是以下公式中的 m 的概念:

波特的斯特梅尔算法辅音和元音公式
每个 C 都被认为是一个包含一个或多个辅音的字符串。每个 V 都被认为是一个包含一个或多个元音的字符串。元音字母被认为是字母 a,e,I,o,u 和 y 有一定的限制。**
m 是波特词干分析器中最重要的概念之一,因为它定义了对特定单词应用特定规则的阈值。让我们来看一些单词的例子:
- 树->这个词可以用表示法 CV 来表示,因为 TR 被认为是 C 表示法而 ee 被认为是 V 表示法。在这种情况下, m 是零,因为这个单词只适合格式[C][V]
- 创作->这个词可以用 CVCVC 来表示。为了符合波特公式,可以用[C][VC]来表示,因此m等于 2。
- 燕麦->这个词可以用符合波特公式的 VC 来表示为[VC]。在这种情况下 m 等于 1。
请记住这个“T2”的概念,因为它对下面的一些解释很重要。
我们在 nltk 图书馆有波特的《斯特梅尔》:
*from nltk.stem import PorterStemmer*
要使用它,必须在导入后声明 PorterStemmer 的实例:
*porter = PorterStemmer()*
定义了 PorterStemmer 的实例后,我们可以根据算法中定义的规则对单词进行词干处理:
*porter.stem('cats')*
这个命令产生单词:‘cat’—单词 cats 的词干版本。我们来看看更常见的例子:
*porter.stem('amazing') returns ‘amaz’*
出现这个词干是因为 ing 在英语单词中是一个如此常见的词尾,以至于单词 amazing 被词干化为单词 amaz。这个 amaz 词干也是由下面这些词产生的,惊叹、拍案叫绝、惊叹:
*porter.stem('amazement') returns ‘amaz’
porter.stem('amaze') returns ‘amaz’
porter.stem('amazed') returns ‘amaz’*
惊异、惊异、惊异和惊异这四个单词在经过词干处理后,在我们的文本中被认为是同一个单词——这就是为什么我们称之为文本规范化!**
作为一个反例,单词 amazon 并不是源于 amaz :
*porter.stem('amazon') returns ‘amazon‘*
为什么亚马逊中上的没有被移除?这是因为某些词尾,比如上的**,在波特的算法中是不被考虑的——只有当它们前面有 i 的时候,比如民族、预感、等等。让我们测试这两个例子的词干:****
*porter.stem('nation') returns ‘nation‘
porter.stem('premonition') returns 'premonit'*
现在一个新的问题出现了——为什么预感被阻止了,而国家却没有?
某些词尾只考虑某个类似字长的阈值(我们的 m !).如果你查看波特规则,你会看到后缀 ION 或 TION 仅在基于 m 的特定条件下应用于第二和第四阶段。
让我们一步一步地了解每个阶段,真正理解波特的斯特梅尔背后的规则。当词干分析器被顺序应用时,可以应用不止一个规则,例如,单词 条件 (m 等于 4) 在斯特梅尔阶段中遵循以下流程:
- 阶段 1:它不匹配该步骤认为的任何模式,例如以 IES 或 SSES 结尾,以及阶段 1b 和 1c 中存在的其他规则。
- 阶段 2:当 m 大于 0 并且单词以国家结尾时,词干分析器应用后缀 al 的裁剪,并且我们的单词成为 条件。
- 阶段 3:我们的单词条件(注意,现在我们将我们的单词视为条件,而不是原始的条件,因为我们已经在阶段 2 中应用了一些东西)。
- 阶段 4:这个单词以第个第个第个第个第>个第 1,所以后缀第个第个第个第个第个第个第个第个第个第个第个第个第个第个】个第个】个第个第【20】个第【18】个第【19】个】个第【18】个第【19】个】个第【**
- 阶段 5:我们的单词 condit 与阶段 5 中可以剥离的任何模式都不匹配,因此我们的最终词干是 condit 。
正如您在上面的过程中看到的,在词干提取过程的各个阶段,我们的单词可能会被词干提取多次。
如果您需要检查每个阶段应用的规则列表,请返回此链接。
波特的斯特梅尔是自然语言处理中最常用的词干技术之一,但自从它首次实现和开发以来已经过去了近 30 年,马丁·波特开发了一个名为 Porter2 的更新版本,由于它的 nltk 实现,它也被称为雪球斯特梅尔。
雪球斯特梅尔
Snowball stemmer(正式名称为 Porter2)是波特的斯特梅尔的更新版本,引入了新规则,修改了波特的斯特梅尔中已经存在的一些现有规则。
雪球/搬运工 2 的详细规则在这里找到。
其逻辑和过程与波特的斯特梅尔完全相同,单词在词干分析器的五个阶段中被依次词干化。
Snowball 和 Porter 之间的另一个相关区别是,Snowball 还详细说明了其他语言的规则——例如,您可以在这里访问葡萄牙语的规则。
你可以很容易地在 nltk 中访问 Snowball,就像我们访问波特的斯特梅尔一样——这里我们也同时定义了实例,但是我们必须将语言定义为一个参数:
*from nltk.stem import SnowballStemmer
snowball = SnowballStemmer(language='english')*
除了广泛回顾雪球的规则,让我们用一些例子来检查波特和雪球之间的一些重要区别:
*porter.stem('fairly') -> returns fairli
snowball.stem('fairly') -> returns fair*
在下面的例子中,斯诺鲍在规范副词方面做得更好,让它产生词干 fair,,而波特产生词干 fairli。这样做使得单词的词干相当与形容词尚可,相同,从规范化的角度来看似乎是有意义的。
这个规则也适用于其他副词:
*porter.stem('loudly') -> returns loudli
snowball.stem('loudly') -> returns loud*
Snowball 的另一个改进是它的规则防止了一些越界(从单词中去掉太多后缀)。让我们看看一般的和慷慨的这两个词的例子——从波特的第一个开始:**
*porter.stem('generically') -> returns gener
porter.stem('generous') -> returns gener*
根据波特的斯特梅尔,这些表达完全不同意思的词将被归结为同一个表达 gener。
对于 Snowball,这种情况不会发生,因为两个单词的词干不同:
*snowball.stem('generically') -> returns generical
snowball.stem('generous') -> returns generous*
总之, SnowballStemmer 被广泛认为是波特的《斯特梅尔》的改进版本。
nltk 的实现基于 Porter2,是对马丁·波特开发的原始算法的改进。
兰卡斯特斯特梅尔
兰开斯特·斯特梅尔是兰开斯特大学的克里斯·佩斯在论文 中提出的另一个斯特梅尔 中开发的词干分析器。
它的规则比波特和雪球更具侵略性,它是最具侵略性的词干分析器之一,因为它倾向于超越许多单词。
作为一般的经验法则,认为兰开斯特的斯特梅尔的规则试图减少单词到尽可能短的词干。你可以在这里查看兰卡斯特的一些规则。
下面是 nltk 的 实现兰卡斯特的斯特梅尔:
*from nltk.stem import LancasterStemmer
lanc = LancasterStemmer()*
让我们来看一些例子,看看兰开斯特的斯特梅尔是如何词干化的,并将结果与斯诺鲍·斯特梅尔的方法进行比较——从单词salt开始:
*snowball.stem('salty') returns 'salti'
lanc.stem('salty') returns 'sal'*
现在对比一下销售这个词:
*snowball.stem('sales') returns 'sale'
lanc.stem('sales') returns 'sal'*
正如你所看到的,Lancaster 把单词 salty 和 sales 放在同一个词干中,这对于两个单词的意思来说没有多大意义。发生这种情况是因为 Lancaster 倾向于过度词干,这是我们说 Lancaster 词干是最积极的词干分析器之一的主要原因。
评估词干分析器的影响
既然我们已经理解了词干是如何通过裁剪后缀来规范化文本的,那么我们如何理解这种规范化在我们的语料库中的影响呢?
由于词干化是一种导致信息丢失的标准化技术,最直接的方法之一是了解词干化前后文本长度之间的比率。这将给出一个近似值,即词干化后保留的信息的百分比。
对于这个例子,让我们使用维基百科中欧盟定义文章的第一段和第二段来评估我们的词干分析器:
*eu_definition = 'The European Union (EU) is a political and economic union of 27 member states that are located primarily in Europe. Its members have a combined area of 4,233,255.3 km2 (1,634,469.0 sq mi) and an estimated total population of about 447 million. The EU has developed an internal single market through a standardised system of laws that apply in all member states in those matters, and only those matters, where members have agreed to act as one. EU policies aim to ensure the free movement of people, goods, services and capital within the internal market; enact legislation in justice and home affairs; and maintain common policies on trade, agriculture, fisheries and regional development. Passport controls have been abolished for travel within the Schengen Area. A monetary union was established in 1999, coming into full force in 2002, and is composed of 19 EU member states which use the euro currency. The EU has often been described as a *sui generis* political entity (without precedent or comparison).The EU and European citizenship were established when the Maastricht Treaty came into force in 1993\. The EU traces its origins to the European Coal and Steel Community (ECSC) and the European Economic Community (EEC), established, respectively, by the 1951 Treaty of Paris and 1957 Treaty of Rome. The original members of what came to be known as the European Communities were the Inner Six: Belgium, France, Italy, Luxembourg, the Netherlands, and West Germany. The Communities and their successors have grown in size by the accession of new member states and in power by the addition of policy areas to their remit. The United Kingdom became the first member state to leave the EU on 31 January 2020\. Before this, three territories of member states had left the EU or its forerunners. The latest major amendment to the constitutional basis of the EU, the Treaty of Lisbon, came into force in 2009.'*
让我们通过我们的搬运工的斯特梅尔,看看结果。为了在 nltk 的实现上传递整个文本,我们必须使用 word_tokenize 函数来标记我们的文本:
*from nltk.stem import PorterStemmer
from nltk.tokenize import word_tokenizetokenized_eu = word_tokenize(eu_definition)porter_eu = [porter.stem(word) for word in tokenized_eu]*
让我们检查最终的词干版本:
*'the european union ( EU ) is a polit and econom union of 27 member state that are locat primarili in europ . it member have a combin area of 4,233,255.3 km2 ( 1,634,469.0 sq mi ) and an estim total popul of about 447 million . the EU ha develop an intern singl market through a standardis system of law that appli in all member state in those matter , and onli those matter , where member have agre to act as one . EU polici aim to ensur the free movement of peopl , good , servic and capit within the intern market ; enact legisl in justic and home affair ; and maintain common polici on trade , agricultur , fisheri and region develop . passport control have been abolish for travel within the schengen area . A monetari union wa establish in 1999 , come into full forc in 2002 , and is compos of 19 EU member state which use the euro currenc . the EU ha often been describ as a sui generi polit entiti ( without preced or comparison ) .the EU and european citizenship were establish when the maastricht treati came into forc in 1993 . the EU trace it origin to the european coal and steel commun ( ecsc ) and the european econom commun ( eec ) , establish , respect , by the 1951 treati of pari and 1957 treati of rome . the origin member of what came to be known as the european commun were the inner six : belgium , franc , itali , luxembourg , the netherland , and west germani . the commun and their successor have grown in size by the access of new member state and in power by the addit of polici area to their remit . the unit kingdom becam the first member state to leav the EU on 31 januari 2020 . befor thi , three territori of member state had left the EU or it forerunn . the latest major amend to the constitut basi of the EU , the treati of lisbon , came into forc in 2009 .'*
这是使用波特词干分析器对 eu_definition 文本进行词干分析的版本。这对于人类来说是不可能的,但是请记住,词干提取的主要目的是规范化文本,以提高机器学习算法的泛化能力。我们的词干版本包含 1430 个字符,不包括空格。我们可以通过以下方式获得:
*len(''.join(porter_eu))*
我们的原始句子包含 1591 个字符,我们可以使用:
*len(''.join(word_tokenize(eu_definition)))*
信息保留的百分比是这些数字之间的比值(1430/1591),约为 89.88%。
如果我们对 Snowball 和 Lancaster 的相同文本应用相同的逻辑,我们会得到以下值:
- 雪球:90.88%
- 兰卡斯特:78.44%
不出所料,保留较少信息的词干分析器是兰卡斯特·斯特梅尔。
如果您想在项目中使用词干,这里有一个小要点和代码:
就是这样!词干是最常用的文本规范化技术之一,当您想要减少将在代码中使用的词汇(独特的单词)时,它可能适合您的自然语言管道。但是请记住,与任何规范化一样,每次应用词干都会丢失语料库中的一些原始信息。
尽管如此,词干分析对于数据科学家来说是一个很好的工具,因为它仍然广泛应用于整个 NLP 应用程序,并且可能对监督和非监督方法都有好处。
感谢您花时间阅读这篇文章!随意在 LinkedIn 上加我(【https://www.linkedin.com/in/ivobernardo/】)查看我公司网站(https://daredata.engineering/home)。
如果你有兴趣获得分析方面的培训,你也可以访问我在 Udemy(https://www.udemy.com/user/ivo-bernardo/)上的页面
这个例子摘自我在 Udemy 平台 上为绝对初学者开设的 NLP 课程——该课程适合初学者和希望学习自然语言处理基础知识的人。该课程还包含 50 多个编码练习,使您能够在学习新概念的同时进行练习。
*https://ivopbernardo.medium.com/membership *
逐步在关系数据库中创建 SQL 表
初学者教程,学习创建带有主键和外键的关系表的艺术

大量 SQL 教程向您展示了如何查询和连接数据库中的表,以提取有用的信息。这些教程基于假设已经创建了表,并且表之间的关系已经建立… 对于从零开始的人来说,情况并非如此!本文将展示如何从空白脚本开始。我们将建立一个非常简单的数据库,其中有三个相互关联的表:病人-医生-医院。
您将在本文中发现:
- 如何在 SQL 中创建表
- 如何在表中添加唯一标识符
- 如何使用主键和外键关联表
- 如何用真实数据填充表格
如果您还不是会员,请在此获得您的中级会员资格!
需要了解的几个有用的首字母缩写词:
什么是 RDBs: 关系数据库( RDB )是由表、记录和列组成的多个数据集的集合。
什么是 SQL: SQL 是一种用于编程的特定领域语言,旨在管理关系数据库管理系统中的数据
什么是表:在关系数据库中保存数据的数据库对象
用户故事
一个人走进医院,马上就会被联系到一个医生。当发现一种疾病或状况时,病人会得到一种药物并被送回家。至少这是我们今天要讨论的最好的情况。同样,每个医生在医院的特定部门工作。显然,一个医生可以为多家医院甚至多个部门工作,但是对于这个用例,我们将假设只有一个可能的值。
因此,我们可以说病人表链接到医生表,医生表链接到医院表。间接地,病人通过分配给他们的医生与医院有联系。
在开始创建表的代码和语法之前,让我们看一下最终结果。

存储在关系数据库中的表及其
第一步:创建表格
在 SQL 语言中,我们使用以下语法创建一个表:
create table patient
(
name varchar,
lastname varchar,
hospital int,
id serial,
primary_doctor int
);
create unique index patient_id_uindex
on patient (id);
alter table patient
add constraint patient_pk
primary key (id);
让我们将代码分解成小的逻辑步骤:
该查询有三个主要部分:
- 创建表格
这将创建一个表,其中包含我们希望该表包含的所有列和存储的信息类型。首先给列一个名称,然后给它们存储一个数据类型。 Integer , serial (顺序整数),varchar(一串字/秒)。
- 创建唯一索引
这表明在第一步中刚刚创建的列 id 只能取唯一值。这是什么意思?任何两个患者都不能有相同的标识符。将这个约束添加到病人的 id 中是有意义的,否则同名的人可能会混淆。
- 涂改表
最后,我们取同一个 id 列,我们给它分配主键的角色。当一列中的值是唯一的并且是整数形式时,我们只能将该列指定为主键。在我们的例子中,“patient_pk”代表患者主键。
SQL 表不需要主键,但它是保持数据整洁和跟踪每个条目的有用功能。
对医院和医生表重复相同的过程。
create table doctor
(
name varchar,
hospital varchar,
id serial
);
create unique index doctor_id_uindex
on doctor (id);
alter table doctor
add constraint doctor_pk
primary key (id);
创建存储医院信息的表:
create table hospital
(
name varchar,
id serial
);
create unique index hospital_id_uindex
on hospital (id);
alter table hospital
add constraint hospital_pk
primary key (id);
第二步:通过外键和主键关联表
目前,我们只有三个相互独立的表,不知道其他表的存在。让我们通过添加 外键 来设置表之间的关系。
这方面的代码如下:
alter table patient
add constraint patient_doctor_id_fk
foreign key (primary_doctor) references doctor (id);
让我们把它分成三个步骤:
- Alter table :由于表已经被创建,我们需要改变它的当前状态来添加一个外键。
2.添加约束:用于指定表格中数据的规则。这就是我们给将要做的事情命名的地方。在这种情况下,添加一个外键。
3.外键:这里我们指定我们取列" primary_doctor "并将其与 doctor 表中的" id "列相关联。这样,当我们向某个医生添加一个患者时,这两个表会立即链接起来,我们可以在输入患者时搜索医生信息,反之亦然。
类似地,要链接医生表和医院表:
alter table doctor
add constraint doctor_hospital_id_fk
foreign key (hospital) references hospital (id);
用真实的医院、患者和医生信息填充表格
当开始填充数据时,我们需要非常注意填充的顺序。至少对于这个初级教程来说,我建议您从填充医院表开始,然后是医生,最后是病人。

这样做的原因是因为在查看表之间的关系时,医院可以自己袖手旁观。当我们添加新医院时,不需要其他患者或医生的信息。而医生和病人之间存在依赖关系。
向表中添加新医院
在本例中,我们需要提供的唯一值是医院名称/位置。 id 列由 SQL 在后台自动填充。第一个条目将具有 id =1,并且每个新条目增加 1。
INSERT INTO hospital (location) VALUES ('Johns Hopkins')
现在我们有了一家医院,我们可以在医生表中添加一名医生。在这里,我们提供医生的姓名、工作部门和医院的 id。这是直接引用医院的表。在本例中,id = 1,因此我们引用上面创建的约翰霍普金斯医院。
INSERT INTO doctor (full_name, department, hospital) VALUES ('alex','dermatology', 1)
最后我们可以插入到患者表中。就像我们对于医生一样,第一列表示每个患者的特征值,最后我们添加与该患者相关联的医生的 id。
INSERT INTO patient (name, age, gender, entry_date, primary_doctor) VALUES ('james','85', 'M', '01/01/1982', 1 )
就是这样!确保在添加新患者和新医生时,只插入表中已经存在的外键值。当表中只注册了 3 名医生时,不要创建主医生 id = 7 的患者…
一些陷阱:
- 一个病人有多个医生会怎么样?
- 一个医生在多家医院工作会怎么样?
- 病人定期去医院会怎么样?
我们是否将它们注册到一个新行中,并因此分配一个新的惟一 id?我们是否将多个条目与同一个患者/医生相关联?但是,这如何与唯一主键约束保持一致呢?
对于更高级的 SQL 用户来说,这些都是非常好的问题。现在,我鼓励您用一些简单的例子创建自己的小表结构。
让你开始的想法
使用上面提供的代码尝试这些关系。这是真正测试您对 SQL 和关系数据库的理解的最佳方式。祝你好运!
- 学生—教师—教室
- 足球运动员—球队—进球
- 产品—商店—购买
非常感谢你的阅读!
更多类似的文章,请点击在媒体上找到我。
如果您有任何关于如何改进的问题、建议或想法,请在下面留下评论或通过 LinkedIn 这里取得联系。
Sci-kit 学习管道的分步教程
实践教程
立即使用管道来简化您的数据科学项目!

大多数数据科学项目(像我一样热衷于说所有项目)都需要一定程度的数据清理和预处理,以充分利用机器学习模型。一些常见的预处理或转换包括:
a.输入缺失值
b.移除异常值
c.标准化或规范化数字特征
d.编码分类特征
Sci-kit learn 在预处理包下有一堆支持这种转换的函数,比如 StandardScaler,simple imputr…等等。
典型且简化的数据科学工作流需要
- 获取训练数据
- 清理/预处理/转换数据
- 训练一个机器学习模型
- 评估和优化模型
- 清理/预处理/转换新数据
- 根据新数据拟合模型以进行预测。
您可能会注意到,在工作流中,数据预处理至少要进行两次。尽管这一步既繁琐又耗时,但如果我们能自动化这一过程并将其应用于所有未来的新数据集,那该多好啊。
好消息是:是的,我们绝对可以!有了 scikit learn pipeline,我们可以轻松地将流程系统化,从而使其极具可重复性。接下来,我将带您了解使用 scikit learn pipeline 的过程,让您的生活更加轻松。
** 免责声明:这篇文章的目的是了解 scikit learn pipeline 的用法,而不是训练一个完美的机器学习模型。
读入数据
我们将使用来自微软神奇的机器学习学习材料的“每日自行车共享”数据。
**import** pandas **as** pd
**import** numpy **as** npdata = pd.read_csv(‘[https://raw.githubusercontent.com/MicrosoftDocs/ml-basics/master/data/daily-bike-share.csv'](https://raw.githubusercontent.com/MicrosoftDocs/ml-basics/master/data/daily-bike-share.csv'))
data.dtypes

data.isnull().sum()

幸运的是,该数据集没有缺失值。虽然看起来所有的特征都是数字,但实际上我们需要识别一些分类特征。那些是 ['季节','月份','假日','工作日','天气'] 。为了便于说明,我仍然将它视为缺少值。我们先过滤掉一些明显没用的功能。
data = data[['season'
, 'mnth'
, 'holiday'
, 'weekday'
, 'workingday'
, 'weathersit'
, 'temp'
, 'atemp'
, 'hum'
, 'windspeed'
, 'rentals']]
拆分数据
在创建管道之前,我们需要首先将数据分成训练集和测试集。
**from** sklearn.model_selection **import** train_test_split
X = data.drop('rentals',axis=1)
y = data['rentals']X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=123)
创建管道
我们将要处理的管道的主要参数是' steps 。从文档来看,它是一个链接的(名称,转换)元组(实现 fit/transform)的“列表,按照它们被链接的顺序,最后一个对象是一个估计器。”
简单地看一下管道应该是什么样子更容易:
Pipeline(steps=[('name_of_preprocessor', preprocessor),
('name_of_ml_model', ml_model())])
“预处理器”是复杂的部分,我们必须自己创建。让我们继续吧!
预处理器
我们需要的包如下:
**from** sklearn.preprocessing **import** StandardScaler, OrdinalEncoder
**from** sklearn.impute **import** SimpleImputer
**from** sklearn.compose **import** ColumnTransformer
**from** sklearn.pipeline **import** Pipeline
首先,我们需要为数字和分类特征定义转换器。变换步骤由元组表示。在这个元组中,首先定义转换器的名称,然后定义想要应用的函数。元组的顺序将是管道应用转换的顺序。这里,我们首先处理缺失值,然后标准化数字特征和编码分类特征。
numeric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='mean'))
,('scaler', StandardScaler())
])categorical_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='constant'))
,('encoder', OrdinalEncoder())
])
我们需要做的下一件事是指定哪些列是数字的,哪些是分类的,这样我们就可以相应地应用转换器。我们通过使用 ColumnTransformer 将转换器应用于特征。将转换器应用于特征是我们的预处理器。与 pipeline 类似,我们将由(‘name’,‘transformer’,‘features’)组成的元组列表传递给参数“ transformers ”。
numeric_features = ['temp', 'atemp', 'hum', 'windspeed']categorical_features = ['season', 'mnth', 'holiday', 'weekday', 'workingday', 'weathersit']preprocessor = ColumnTransformer(
transformers=[
('numeric', numeric_transformer, numeric_features)
,('categorical', categorical_transformer, categorical_features)
])
有些人会根据数据类型创建数字/分类特征列表,如下所示:
numeric_features = data.select_dtypes(include=['int64', 'float64']).columnscategorical_features = data.select_dtypes(include=['object']).drop(['Loan_Status'], axis=1).columns
我个人不建议这样做,因为如果你有伪装成数值数据类型的分类特征,比如这个数据集,你将无法识别它们。仅当您 100%确定只有数字要素是数字数据类型时,才使用此方法。
估计器
在组装我们的预处理器之后,我们可以添加估计器,这是您想要应用的机器学习算法,以完成我们的预处理和训练管道。因为在这种情况下,目标变量是连续的,所以我将在这里应用随机森林回归模型。
**from** sklearn.ensemble **import** RandomForestRegressorpipeline = Pipeline(steps = [
('preprocessor', preprocessor)
,('regressor',RandomForestRegressor())
])
为了创建模型,类似于我们过去用机器学习算法所做的,我们使用 pipeline 的‘fit’功能。
rf_model = pipeline.fit(X_train, y_train)
print (rf_model)

使用常规方法评估模型。
**from** sklearn.metrics **import** r2_scorepredictions = rf_model.predict(X_test)print (r2_score(y_test, predictions))
>> 0.7355156699663605
使用模型
为了最大限度地提高再现性,我们希望对新的输入数据重复使用该模型。让我们使用“joblib”包将模型保存为 pickle 文件。
**import** joblibjoblib.dump(rf_model, './rf_model.pkl')
现在我们可以调用这个管道,它包括我们需要的各种数据预处理和训练模型,只要我们需要它。
# In other notebooks
rf_model = joblib.load('PATH/TO/rf_model.pkl')new_prediction = rf_model.predict(new_data)
结论
在了解 scikit learn pipeline 之前,每当我想将同一个模型应用于不同的数据集时,我总是不得不重做整个数据预处理和转换工作。这真是一个乏味的过程。我试图编写一个函数来完成所有这些任务,但是结果并不令人满意,也没有为我节省很多工作量。
Scikit learn pipeline 确实让我的工作流程更加顺畅和灵活。例如,您可以轻松比较许多算法的性能,例如:
regressors = [
regressor_1()
,regressor_2()
,regressor_3()
....]for regressor in regressors:
pipeline = Pipeline(steps = [
('preprocessor', preprocessor)
,('regressor',regressor)
])
model = pipeline.fit(X_train, y_train)
predictions = model.predict(X_test)
print (regressor)
print (f('Model r2 score:{r2_score(predictions, y_test)}')
,或者调整预处理/转换方法。例如,使用“中值”来填充缺失值,对数字特征使用不同的定标器,改变为一次性编码而不是顺序编码来处理分类特征、超参数调整等。
numeric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median'))
,('scaler', MinMaxScaler())
])categorical_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='constant'))
,('encoder', OneHotEncoder())
])pipeline = Pipeline(steps = [
('preprocessor', preprocessor)
,('regressor',RandomForestRegressor(n_estimators=300
,max_depth=10))
])
所有上述调整现在都可以简单地通过改变函数中的一个参数来完成。
我希望这能对你有所帮助,欢迎任何评论或建议!
斯蒂芬金文本生成与人工智能(RNN),使用 Python

J. Kelly Brito 在 Unsplash 上的照片
以下是我如何训练一个深度学习架构来编写一个类似斯蒂芬·金的文本。
我喜欢阅读,我是斯蒂芬·金的超级粉丝。另外,我是一名物理学家和数据科学家,目前在获得硕士学位后正在休假。在这段时间里,我决定读另一本斯蒂芬·金的书,我真的很喜欢它。
我永远不会让电脑写一本斯蒂芬·金的书,原因很简单。
我们还没有掌握这种技术。
如果你看一些 NLP 实验(例如 AI 写笑话这里)你可以有把握地说我们在写作方面不如人类。
所以你可能会问“为什么在一篇解释如何用人工智能生成文本的博客文章中,你告诉我我们不能让人工智能生成文本?”。答案是这样的:
虽然我们确实不能让计算机写一整本书(就目前而言),但人工智能仍然能够提出情节建议,获得见解,并帮助人类写作。
实际上,它在这些方面做得非常好!
因此,我决定拿一堆斯蒂芬·金的文本,用一个人工智能(深度学习)算法写一个小的新的。这是游戏计划:
- 理论
- 代码
- 结果
我知道你们中的一些人可能只对结果感兴趣,或者只对理论部分感兴趣,或者只对代码感兴趣。每个部分都是前后一致的,所以请随意跳到你最喜欢的部分。
我们潜进去吧!
1。 理论
注意:这是人工智能结构背后所需理论的简要概述。如果你觉得你需要更扎实的理论背景,你应该参考不同的资料或书籍。
当我们说 AI 时,我们会说很多东西。另一方面,当谈到“文本生成”时,我们可能希望参考深度学习。深度学习是一种特殊的人工智能,能够通过执行分层学习来完成任务。让我们简单点。如果你想识别你面前的动物是不是猫,你可能会从看颜色开始,然后是形状,然后是眼睛,等等。所以你基本上从简单的概念开始,然后从简单的概念中建立更复杂的概念。出于这个原因,我们说学习是分层的(即输入由不同的层处理)。
假设输入是由 N = N×N 个像素组成的图像。 这个输入被不同的层处理,然后如果动物是猫,结果是 1,如果不是猫,结果是 0。很简单,对吧?
需要知道的重要一点是,这个结构需要训练。这意味着你必须使用大量的猫和非猫的标签图片(idk,dogs?)并建立一个学习过程,让机器学习如何执行任务。
现在,让我们谈谈生意。当我们谈论文本生成时,我们必须记住两个重要的概念:
a)将字嵌入
b)递归神经网络
单词嵌入是机器把一个文本(一个字符串)转换成一个向量(一个数字序列)的部分。
递归神经网络是一种特殊的深度学习结构。这种结构能够收集一个序列顺序(如一个句子)的所有先前输入,以预测获得某个输出的概率。特别是有一个词汇包含了文本的所有单词(假设有 V 单词)和一个 Logit 输出。这是一个在[0,1]范围内的 V 数序列,其和为 1。序列的每个数字(假设是 j)告诉你输入序列后获得 j 字的概率。
到目前为止所解释的一切都总结在下面的方案中:

来源:https://www.tensorflow.org/text/tutorials/text_generation
2。代码代码
注:本部分灵感来自本来源
这可能会让你吃惊,但是代码部分非常简单。
这些是您将需要的库:
我用过的所有斯蒂芬·金的文本都可以在这里找到。
在你得到它们之后,很容易用一些非常简单的 Python 普通代码行来使用它们:
这些是为了使文本适应 TensorFlow 并创建字典而运行的代码行:
建立模型:
训练模型:
获取训练好的模型:
这里是生成文本功能:
可用于实际生成以下简单行的文本(假设给定了输入字符串):
如"后"已作为输入序列给出。
3结果
到目前为止描述的人工智能结构原则上可以从单个输入生成无限的文本。当然,这会导致糟糕的表现,因为它最终只会一遍又一遍地重复同样的话。因此,从输入中生成一个句子是安全的。需要注意的是,有一个参数叫做 t(温度),它允许你设置算法的创造性。t= 0 意味着重复数据集的相同单词,t=1 意味着发明非英语句子或单词。这两者之间的良好平衡是关键。
给你看看一些结果吧!
第一个例子(t = 0.1,中等输入):
Input: "After the"
Output: "After the sun was warm on our faces we were both wearing sweater (.) You know how confused dreams are logic like"

第二个例子(t=0.1,长输入):
Input: "So I decided"
Output: "So I decided the ordinary was going on. I think this was because in my heart I believed that such conditions only"

第三个例子(t=0.7,短输入):
Input: "I"
Output: "I fell in love to the sound of breaking glass edged it liked jagged lace. The two ved her be a fluke I r"

第四个例子(t=0.2,短输入):
Input: "He"
Output: "He who spotted her. He recognized her by her red handbag spread out in front of me and it was like takin"

一些乐趣(wordcloud):
单词越大,使用频率越高。

我用 Python 制作的图片
结论:
正如可能看到的,我们无法用这种技术实现一个完全一致的斯蒂芬·金文本生成器。尽管如此,斯蒂芬·金的粉丝可能会意识到,在生成的文本中有一些东西让他们想起了斯蒂芬·金。这并不令人惊讶,因为人工智能算法已经“看到”了由斯蒂芬·金文本组成的数据集。另一方面,令人惊讶的是,该算法已经发明了一种新的斯蒂芬·金式(非常短)文本!
如果你喜欢这篇文章,你想知道更多关于机器学习的知识,或者你只是想问我一些你可以问的问题:
A.在 Linkedin 上关注我,在那里我发布我所有的故事
B .订阅我的 简讯 。这会让你了解新的故事,并给你机会发短信给我,让我收到你所有的更正或疑问。
C .成为 推荐会员 ,这样你就不会有任何“本月最大数量的故事”,你可以阅读我(以及成千上万其他机器学习和数据科学顶级作家)写的任何关于最新可用技术的文章。
任何人都快乐斯蒂芬·金!
走进甘斯的神奇世界
甘的一步一步教程

https://unsplash.com/photos/6dN9l-gopyo
一个生成模型有潜在的魔力,如果训练得当,它可以像专家一样写诗、创作音乐、绘制图像。GAN 的目标是生成非常逼真的合成样本。甘模型在对抗环境中学习技巧。有两个多层神经网络,一个作为发生器,另一个作为其对手,称为鉴别器。两者都使用常规反向传播方法训练,尽管具有不同的和冲突的损失函数(对抗性设置)。一旦训练完成,鉴频器被移除,生成器被用于产生样本。
下图对此进行了说明。这里的任务是能够创建像 MNIST 手写数字。

图 1a:GAN 的训练阶段(图片来源:作者)
该图有两个神经网络,第一个是生成器,第二个是鉴别器。
生成器将随机噪声向量作为输入,并生成与真实数据维数相同的图像。为什么用噪声作为输入?这确保了生成器不会产生真实数据中已有数据的副本。
鉴别器是一个进行二元分类的简单分类器。来自生成器的类“0”或假类,以及来自真实图像的类“1”,在本例中为 MNIST。
鉴别器使用交叉熵的常规损失函数,而生成器训练通过鉴别器,保持其权重恒定(否则收敛将像移动目标一样),并且这里损失函数测量具有 1 类概率的伪图像。这就是冲突的来源。鉴别器希望真实类被分类为真实类,假类被分类为假类,生成器希望假类被分类为真实类。
一旦训练完成,鉴别器可以丢弃,生成器可以用来产生真实的样本,如下图所示。

图 1b:氮化镓的生成阶段
现在是实现部分,编码是根据 Jason Brownlee 的博客完成的。
步骤 1:定义生成器
def define_generator(latent_dim):
model = Sequential()
*# foundation for 7x7 image*
n_nodes = 128 * 7 * 7
model.add(Dense(n_nodes, input_dim=latent_dim))
model.add(LeakyReLU(alpha=0.2))
model.add(Reshape((7, 7, 128)))
*# upsample to 14x14*
model.add(Conv2DTranspose(128, (4,4), strides=(2,2), padding='same'))
model.add(LeakyReLU(alpha=0.2))
*# upsample to 28x28*
model.add(Conv2DTranspose(128, (4,4), strides=(2,2), padding='same'))
model.add(LeakyReLU(alpha=0.2))
model.add(Conv2D(1, (7,7), activation='sigmoid', padding='same'))
return model
生成器将噪声向量作为输入,并生成 28 X 28 的图像。下面是测试生成器的代码
noise = tf.random.normal([1, 100])
generator = define_generator(100)
generated_image = generator(noise, training=False)
plt.imshow(generated_image[0, :, :, 0], cmap='gray')
生成的图像如下

图 2:产生的随机噪声(图片来源:作者)
步骤 2:定义鉴别器模型
这是一个简单的二元分类网络。
def define_discriminator(in_shape=(28,28,1)):
model = Sequential()
model.add(Conv2D(64, (3,3), strides=(2, 2), padding='same', input_shape=in_shape))
model.add(LeakyReLU(alpha=0.2))
model.add(Dropout(0.4))
model.add(Conv2D(64, (3,3), strides=(2, 2), padding='same'))
model.add(LeakyReLU(alpha=0.2))
model.add(Dropout(0.4))
model.add(Flatten())
model.add(Dense(1, activation='sigmoid'))
*# compile model*
opt = Adam(lr=0.0002, beta_1=0.5)
model.compile(loss='binary_crossentropy', optimizer=opt, metrics=['accuracy'])
return model
步骤 3:创建组合模型
这里的目的是训练发生器模型,鉴别器权重不会改变或者不可训练。
def define_gan(g_model, d_model):
*# make weights in the discriminator not trainable*
**d_model.trainable = False**
*# connect them*
model = Sequential()
*# add generator*
model.add(g_model)
*# add the discriminator*
model.add(d_model)
*# compile model*
opt = Adam(lr=0.0002, beta_1=0.5)
model.compile(loss='binary_crossentropy', optimizer=opt)
return model
第四步:搭建舞台。
- 加载真实数据: —从 MNIST 获取数据
- 生成真实数据: —从真实数据集中获取一个样本,并附加类标签‘1’
- 生成噪声数据:- 生成随机噪声作为发生器的输入
- 生成假日期:- 从生成器获取一个样本,并附加类标签‘0’
def load_real_samples():
*# load mnist dataset*
(trainX, _), (_, _) = mnist.load_data()
*# expand to 3d, e.g. add channels dimension*
X = expand_dims(trainX, axis=-1)
*# convert from unsigned ints to floats*
X = X.astype('float32')
*# scale from [0,255] to [0,1]*
X = X / 255.0
return Xdef generate_real_samples(dataset, n_samples):
*# choose random instances*
ix = randint(0, dataset.shape[0], n_samples)
*# retrieve selected images*
X = dataset[ix]
*# generate 'real' class labels (1)*
y = ones((n_samples, 1))
return X, ydef generate_latent_points(latent_dim, n_samples):
*# generate points in the latent space*
x_input = randn(latent_dim * n_samples)
*# reshape into a batch of inputs for the network*
x_input = x_input.reshape(n_samples, latent_dim)
return x_input def generate_fake_samples(g_model, latent_dim, n_samples):
*# generate points in latent space*
x_input = generate_latent_points(latent_dim, n_samples)
*# predict outputs*
X = g_model.predict(x_input)
*# create 'fake' class labels (0)*
y = zeros((n_samples, 1))
return X, y
第五步:训练方法甘
下面的代码是重要的一部分,有两个循环,外部的一个用于 Epochs,内部的一个用于 batches。正在训练两个模型,一个是鉴别器模型,另一个是具有鉴别器权重常数的组合模型。Epoch 的数量被设置为 5,这不会给出一个好的结果,但会让你知道它是否工作。
def train(g_model, d_model, gan_model, dataset, latent_dim, n_epochs=5, n_batch=256):
bat_per_epo = int(dataset.shape[0] / n_batch)
half_batch = int(n_batch / 2)
*# manually enumerate epochs*
for i **in** range(n_epochs):
*# enumerate batches over the training set*
for j **in** range(bat_per_epo):
*# get randomly selected 'real' samples*
X_real, y_real = generate_real_samples(dataset, half_batch)
*# generate 'fake' examples*
X_fake, y_fake = generate_fake_samples(g_model, latent_dim, half_batch)
*# create training set for the discriminator*
X, y = vstack((X_real, X_fake)), vstack((y_real, y_fake))
*# update discriminator model weights*
d_loss, _ = d_model.train_on_batch(X, y)
*# prepare points in latent space as input for the generator*
X_gan = generate_latent_points(latent_dim, n_batch)
*# create inverted labels for the fake samples*
y_gan = ones((n_batch, 1))
*# update the generator via the discriminator's error*
g_loss = gan_model.train_on_batch(X_gan, y_gan)
*# summarize loss on this batch*
print('>**%d**, **%d**/**%d**, d=**%.3f**, g=**%.3f**' % (i+1, j+1, bat_per_epo, d_loss, g_loss))
步骤 6:使用参数运行训练方法
*# size of the latent space*
latent_dim = 100
*# create the discriminator*
d_model = define_discriminator()
*# create the generator*
g_model = define_generator(latent_dim)
*# create the gan*
gan_model = define_gan(g_model, d_model)
*# load image data*
dataset = load_real_samples()
*# train model*
train(g_model, d_model, gan_model, dataset, latent_dim)
生成阶段:
seed = tf.random.normal([num_examples_to_generate, noise_dim])
predictions = g_model(seed, training=False)
fig = plt.figure(figsize=(4, 4))
for i **in** range(predictions.shape[0]):
plt.subplot(4, 4, i+1)
plt.imshow(predictions[i, :, :, 0] , cmap='gray')
plt.axis('off')
下面是 5 个时期后生成的图像,当然,如果我们训练更多的时期,这将给出一些有意义的东西。

图 3:生成器生成的图像(图像来源:作者)
结论:
GAN 是最酷的一个,具有很大的潜力和积极的发展,这个入门只是让你开始。关于鉴别器的收敛性和严格性还存在一些问题,有兴趣的读者可以进一步深入了解。
你也可以从我们的视频讲座开始。
为了每个人的利益踩在每个人的脚趾上
数据科学部门在 21 世纪组织中的角色
介绍

(作者创作)
数据科学部门是传统公司结构的新成员。以下是如何有效地创造一个有益于你的组织,而不是一个创造不必要的内耗。
早在《哈佛商业评论》宣布数据科学是 21 世纪最性感的工作之前,数学家约翰·w·图基(John W. Tukey)就在他的论文“数据分析的未来”(1962)中对电子计算的到来将如何影响统计学做出了同样性感的预测。他将高度理论化和学术化的数理统计领域的不可或缺的概括应用到更实用的“数据分析”领域,其特点是在从数据中确定真理的实践中增加了一些程序,如“计划数据的收集以使其分析更容易、更精确或更准确,以及统计学的所有机制”。毕竟,这是科学最基本的准则:如何区分真理和谬误,阐明其完整的背景和适用性,以理解我们所居住和构成的宇宙?无论在哪个领域,都是通过获取数据来指导旅程。数据也可能被滥用——就像经常发生的那样——通过明确的意图、无意的偏见或简单的粗心来推动错误的叙述。图基写道,当时电子计算在数据分析方面的新生应用刚刚进入讨论,他指出,“携带计算尺比台式电脑更容易”,“计算机使完全不可行的事情变得可行”。随着计算能力的增长,数据揭示真相和谎言的能力也在增长。适用性的广度也在增长,数据科学现在几乎渗透到了研究或工业实践的每个领域。因此,出现了一个科学和工业相结合的新领域:“数据科学”,其特征是几个领域的交叉,如数学、统计学、数据分析、计算、数据库,以及大量以主题专业知识和科学探究为统一核心的艺术故事。鉴于这些组织现在面临的数据的复杂性、丰富性和重要性,数据科学填补了传统企业组织结构中出现的功能缺口。忽视这一差距的代价是根据与现实不符的叙述行事的后果。
“数据科学”在 2008 年首次被称为“数据科学”,它已经成为一个新的角色,出现在从许多其他组织合并而来并涉足许多领域的组织中。过去难以解决的问题现在可以通过这一新角色来解决,前提是它在组织中的职能得到明确定义,以补充现有的部门角色,而不是激发领土反弹。然而,要充分履行这一新角色,需要其他传统部门所具备的独特技能组合。数据科学在操作上用代码来表达,但是软件工程师通常不具备必要的数学或统计背景。此外,工程师的目标是建造为特定目的而工作的东西,而不是通过经验主义来寻求真理。受过传统训练的统计学家在数据分析的基本原则和经验主义方面有很强的基础,但通常没有受过软件和数据库设计或管理方面的训练。数据库管理员和数据工程师可以有效地管理大量数据,但不一定从事过科学研究。人工智能本身是一个子领域,位于数据科学的中心,除了编码、数据库和统计建模技能之外,还需要自己的专业知识。鉴于这一研究领域在 2020 年大约只有 10 年的历史,在其现代化身中,其领导者几乎总是在某个相关领域接受过正规教育,并在必要时移居到该领域。早期领导者的个人经历以及他们所服务的组织的需求为数据科学所扮演的角色提供了信息,随着行业普遍努力理解这一新职能角色的效用,这一角色可能会因组织而异。
作为一名实验性高能物理学家,我在学术界从事了多年的工作,研究宇宙如何利用粒子对撞机生成的数据集构成的最深刻的已知原理,并在一家飞行 wifi 提供商的数据团队中担任了几年的全栈软件工程师,在过去的几年里,我花了几年时间为 rMark Bio 建立数据科学项目,在那里我们致力于数据驱动的方法,以提高生命科学中的运营效率和可操作的洞察力收集。作为一家不受传统部门、产品或思想束缚的年轻公司,我们有机会定义数据科学在一个新组织中的作用,这个新组织受制于 21 世纪第二个十年的技术和经济力量。我想分享我们学到的东西。
数据科学部门的首要原则
我经常向数据科学家强调通过理解正在研究的问题背后的首要原则来解决问题的重要性,所以让我们以同样的方式来解决定义和建立数据科学部门的问题:数据科学部门的首要、最基本的目标是什么?我提议:
“数据科学计划的目标是战略性地识别数据事实,理解这些事实的全部复杂性,识别由此产生的相关事实陈述,然后将这些知识应用于与数据科学计划所在组织的使命相一致的一些实际目的。数据科学表现为经验主义,专门研究用于实现这一目标的方法和技术。”
最根本的目标不是构建有用的东西,即使这样做对于可靠地处理数据是必要的;不是有效地营销叙事,即使一旦发现数据的真实叙事,这样做是数据科学的责任;而不是销售产品,即使数据驱动的洞察力可能是该产品设计的组成部分。数据科学的首要原则是通过经验主义进行科学探究;其他一切都是追求的结果。通过经验主义寻求真理是数据科学部门区别于其他部门的角色,数据科学部门必须与其他部门协同工作。正如在任何科学领域一样,表达挑战传统智慧的创造性自由,无论努力的最终结果是加强现有智慧,更好地理解它,还是完全推翻它,都是这一角色的关键,也是数据科学追求其使命所必须尊重的。
数据科学包括软件工程、数据架构和统计学,但不是它们
数据科学最相似的一个角色是软件,因为在编写管理和研究数据的代码的同时,组织、清理和管理数据是一种实际需要。然而,对数据进行操作的软件的目标和输出不仅仅是转换、重组或显示数据,即使所有这些都可能是过程的一部分。识别数据中的真理和见解的目标导致数据科学软件的结果更不可预测和更不明确。机器学习模型的特定输入的正确输出不能像定义良好的数据转换那样预先预测,因此显式测试脚本不能总是以软件工程师习惯的方式编写。模型从来没有明确的“正确”或“不正确”,而是占据了一个模糊的领域,在某些适用的上下文中足够好(或不够好),而在其他上下文中不够好,通过准确性分数和其他度量来衡量,这些度量永远不可能是 100%(如果它们曾经是,这将否定建模的需要)。尽管如此,数据科学家的永恒任务是按照这些指标来改进模型。数据科学不是以数据的自动计算和转换为特征,而是对数据的自动研究,数据科学家的心态必须相应地不同于软件工程师,同时仍然掌握类似的技术技能。
如果软件工程是数据科学家实现其目标的工具集,那么统计学的经验主义就是产生创造愿景的创造性灵魂。除了一些新颖的边缘案例,“机器学习”可以被理解为用代码执行的统计建模的应用,而不是前电子计算时代统计学家所从事的笔到纸(或滑尺)的计算。如果您在由标准“x”(水平)和“y”(垂直)轴表示的二维平面中有一些数据,并且您想要找到穿过该数据的最佳拟合直线,您正在进行线性回归,这是统计建模的最简单形式之一。如果你用运行在计算机上的代码执行线性回归,你现在正在做“机器学习”。大多数传统的统计回归建模是以最能代表数据的方式将一些数学函数拟合到现有数据的实践。无论哪种数学函数适合数据集,确定最佳拟合函数的过程都遵循一个标准的高级流程:
- 定义要解决的问题并设计研究
- 收集、清理和组织数据集
- 大部分数据集将用于训练模型。数据集的小的、独立的和随机的子样本被保留用于被训练的模型的“测试”和“验证”。
- 在训练数据集上训练模型,使用测试数据集在训练过程中监视模型的质量,使用验证数据集查看模型在未明确训练的代表性数据上的表现。
- 修改并注意模型是训练过度还是训练不足(即偏差-方差权衡)。
- 将该模型应用于最初启动其构建的任何实际目的。
关于如何有效地执行这些公认的非常高层次的步骤,整本书都可以写,而且已经写了,但是这些概述了回归问题是如何执行的。

图示了多层感知器的计算图形表示,以及它的复杂性如何取代经典回归的单一数学函数(图片摘自作者关于在寻找希格斯玻色子中使用神经网络的论文,发表于 2010 年夏天。)
假设我们现在有一个数据集,我们想在数据中的一个形状上训练一个模型,这个形状不能用任何一个特定的数学函数来表示。可以通过用数学函数网络替换拟合数据的数学函数,对刚才描述的回归过程进行调整。这是经典统计建模与我们现在所说的“人工智能”(AI)相吻合的转折点。使用数学函数网络而不是单个数学函数网络已被证明在对比单个数学函数更复杂的数据要素进行建模时非常有效。虽然现在有各种各样的机器学习和人工智能方法,但最标准的形式在很大程度上遵循上述相同的基本过程(即“监督训练”过程)。如果说经典的统计建模方法论是“统计学 1.0”,那么 AI 就是“统计学 2.0”。然而,理解和执行人工智能模型需要数学和计算方面的专业知识,这往往超出了统计学领域的严格范围。此外,在实践中,现代人工智能建模还需要一定程度的数据架构和云计算专业知识来开发企业人工智能解决方案。
一个人对数据最关键的理解之一是如何表示它的形状。在考虑任何类型的建模之前,我们需要管理和理解数据本身。有多种方法来表示数据的形状,如何针对特定数据集做出明智的决策对数据仓库架构有着至关重要的影响。例如,客户、支票账户、储蓄账户、抵押贷款等的银行模型。传统上使用“关系数据库管理系统”,该系统将各种形式的数据表示为相互关联的表格。一个表包含所有的客户数据。另一种方法是将所有带有引用号的支票帐户返回到 customer 表中拥有该支票帐户的客户。塑造数据的另一种方式是“图,其特征是特定数据点具有潜在的复杂关系网。社交网络的用户以及每个用户与谁联系(即,与谁是“朋友”)非常适合表示为图形数据。当然,可以在图形数据库中表示银行数据,也可以在关系数据库管理系统中表示社交网络数据,但是这样做是否明智、有效和划算,需要对数据架构有良好的判断。在数据的进一步研究开始之前,数据科学的许多领域都需要做出并执行这样的判断。数据科学部门必须了解如何构建数据,并经常管理至少部分架构本身。
最后,如果我们不能将数据科学部门的发现传达给其他利益相关者,那么这些发现就毫无用处。组织中的其他领导者依靠事实来做出明智的决定。数据科学部门负责了解他们的工作如何被使用的后果,这需要所使用的数据领域中某种程度的主题专业知识。该专业知识可能与生产、营销、产品、设计或任何其他需要基于对数据的准确理解做出业务决策的职能相关。将数据和得出的结果联系起来同样重要。主题专业知识和对正在解决的业务问题的定性理解是“数据分析师”的任务,也应该在数据科学领域内考虑。
数据科学部门需要与软件应用、数据工程、IT、产品/设计、业务开发和其他部门密切合作,以确保组织作为一个整体在正确理解可用数据的基础上做出明智的决策。虽然数据科学领导者通常来自软件工程、应用统计、IT 或物理、数学和社会科学等学术领域,但为了实现数据科学部门与传统组织角色不同的愿景和功能,扩展个人原始培训之外的技能组合是必要的。
数据科学部门的职责
如果数据科学部门的角色需要融合各种现有的专业领域,以及一些新的特定于数据科学的领域,以完成与其他部门不同的任务,那么下一个自然问题是:数据科学部门的职责和期望是什么?
让我们从不要做什么开始。
在解决问题的过程中,有一个我经常遇到的错误,我不得不在这次对话的开始提到它——一个新手数据科学家和经验丰富的企业高管经常犯的错误:以“我们需要使用人工智能”的结论开始,然后试图构建一个这样做的理由。对于刚刚进入该领域的新数据科学家来说,能够说一些先进的机器学习或人工智能方法已经付诸实践,既有兴奋感,也有职业压力。在实验设置、繁琐的数据清理、数据库设计和管理方面做得非常出色,选择一种简单而经典的建模方法并不能像大型 Word2Vec 或其他人工智能模型那样,传递自我并恢复建立街头信誉。同样,在许多拥有大量数据但不擅长数据管理的传统公司中,人们有被视为“投资于人工智能”未来的压力,因为这是过去十年中最大的价值驱动型流行语之一。如果不小心接近,大量的资金会很快被浪费在无效的应用上。使用数据有效解决问题需要从要解决的业务问题开始,确定可用的相关数据(或者如果不可用,可以收集哪些数据),然后返回到最适合该问题的解决方案,而不要过早地偏向特定的方法。这听起来很抽象,但使用基于人工智能的解决方案的压力,即使是在脱离上下文的情况下,也是普遍和强烈的,因为它们是短视的。一个负责任的数据科学部门建议不要做这种蠢事。
假设这些担忧已经消除。
数据科学部门的任务是确定数据驱动的决策战略,推动其所在的大型组织的使命。首先应该确定要解决的业务问题,并获得足够的专业知识。数据科学家不能有效地执行他们的功能,他们盲目地收集数据,而没有对环境的直观理解,并启动一些建模或分析方法,这是另一个太常见的陷阱。我们需要愿意与拥有与问题相关的主题专业知识的涉众坐在一起,并理解他们流程中的痛点。我们不是来取代他们的;我们在这里帮助他们更有效地执行他们的职能。这也应该有技巧地沟通。即使在这样一门硬科学中,人际交往能力和情商也是绝对必要的。
然后需要确定相关数据,包括已经存在的数据和需要获取的数据。必须理解并记录数据的性质、上下文和形式。需要明智地设计大大小小的数据集。应该解决的无数问题中的几个是:
- 数据将如何收集,需要经过多少步转换?也就是说,设计 ETL(“提取、转换、加载”)过程。
- 数据应该如何存储?它需要在 CSV 文件、关系(SQL)数据库、JSON (NoSQL)、图形数据库、时间序列数据库等中吗??数据的各个部分是否需要以各种形状存储?
- 如果数据本质上是表格形式的,那么它是否需要在 SQL 数据库中,或者 CSV 文件是否足够?维护云托管的 SQL 数据库比维护 blob 存储中的 CSV 文件要昂贵得多。在许多情况下,处理 CSV 文件对于建模来说也要高效得多。成本问题不容忽视。
- 数据是否包含受欧盟《一般数据保护条例》(GDPR)或《加州消费者隐私法》(CCPA)保护的“个人身份信息”(PII)?在这种情况下,必须解决管理这些数据的数据科学部门的法律责任。不这样做可能会导致足以拖垮中小型公司的巨额罚款。
- 当需要唯一标识符时,是否有数据类型没有适当的唯一标识符?(这个在 rMark Bio 很常见)
- 所有的值都被转换成正确的类型了吗?例如,如果预期为整数的值实际上是字母数字字符,则会出现意外问题。如果数据科学家不挑剔,可能会出现“1 + 2 = 12”这样的问题。
- 确定常见的数据清理问题,例如:丢失的数据、不完整的数据、以无法处理的格式到达的数据,以及某段数据是否只是被输入了错误的列?(也很常见)
- 设计数据时应该考虑候选的方法或建模解决方案。
如果输入了垃圾数据,世界上最复杂的建模或分析方法也只会产生垃圾结果。出于所有这些原因以及更多原因,数据科学部门需要拥有根据自身需求设计数据的自由。这并不意味着数据科学部门不能或不应该与外部数据工程组织协同工作;这确实意味着数据科学部门需要具备技能和资源来构建执行任务所需的数据。
有了可供使用的架构数据集,就可以开始研究数据了。数据科学家可用的分析工具集非常庞大,并且在快速增长。创造过程的混乱无人能幸免,发现数据的真相就是这样。在找到可行的解决方案之前,需要培养和尊重创造性的过程。一些解决方案可能以一次性的、孤立的研究报告的形式提交。其他时候,解决方案需要以服务的形式来维护,创造性努力的结果需要清理、记录并重组为可维护的软件。创作混乱需要纪律;技术掌握是艺术价值创造的必要伙伴。在浏览器窗口中执行脚本的 Python 笔记本是数据科学家中非常流行的工具,非常适合教学。适当的和可维护的软件,它们绝对不是。除了孤立的研究,我强烈认为数据科学的成果应该是组织良好的、可维护的、可部署的软件。数据科学部门为我们职责范围内的主题开发软件。因此,像所有软件一样,我们的软件需要用健壮的软件开发生命周期来管理。
最后,如果不能有效沟通,任何解决方案都不值得花费时间、精力和人力成本。无法交流的数据科学是无法利用的。如果数据科学家的中心任务是找到关于数据集的真实叙述,那么有效的沟通技巧,无论是书面还是口头的,都是至关重要的,没有商量的余地。
当然,数据科学部门不仅仅生产软件,数据科学部门的每个成员都没有必要掌握上面讨论的每一项技能。数据科学部门需要数据工程师和架构师、机器学习和人工智能专家的功能,这些专家可以作为强大的软件、统计学家和数学家、数据分析师、可视化专家和有效的沟通者来完成他们的工作。数据科学部门应该有这些领域的专家。
哪些数据科学可以实现,而其他重叠部门/角色不能实现
只是在 21 世纪,在信息时代的黎明,数据变得无处不在,以至于组织内的新职能角色变得必不可少。然而,鉴于此,这种需要挤入现有组织结构的新角色的出现是不可避免的。需要高效地管理和理解数据的数量、种类和速度(大数据的“3v”)。要做到这一点,不仅需要独特的技术组合,还需要过去许多组织可能不存在的科学心态。直觉和本能,从个人经历和对它们的主观感知中表现出来,是有缺陷的;常识没有那么普遍,也没有那么普遍错误。数据科学可以实施和执行对组织正在处理的现实的客观评估,以确保决策是正确的。这种情境化的、基于数据的对现实的感知也将随着情境及其产生的数据的变化而不断变化。在充斥着大量数据的商业和经济环境中,训练有素的科学头脑和执行这一任务的专业技术变得越来越不可或缺。
机器学习过程的步骤
机器学习过程由本质上循环的几个步骤组成。组织通过 MLOps 实现自动化的步骤越多,机器学习过程就越成熟。

约书亚·索蒂诺在 Unsplash 上拍摄的照片
MLOps 是关于 DevOps 哲学在机器学习系统中的应用(要阅读更多关于这两种实践的内容,请查看我的文章:【https://towardsdatascience.com/mlops-vs-devops-5c9a4d5a60ba】)。将机器学习模型投入生产的机器学习过程包括几个步骤。组成机器学习过程的步骤的自动化水平决定了机器学习过程的成熟度。通常,过程越自动化,给定新的输入/模型实现,训练新模型的速度就越高。
机器学习过程的步骤
- 数据提取:该步骤涉及从各种数据源整合用于机器学习任务的数据。这一步的目的是选择在机器学习任务中使用哪些数据。
- 数据分析:在此步骤中,执行探索性数据分析(EDA)以更好地理解提取的数据。这一步的目的有两个:
1:理解数据、模式和要用作模型输入/标签的数据分布。
2:确定执行机器学习任务所需的任何数据准备步骤和特征工程。 - 数据准备:该步骤涉及数据清理(处理缺失数据、删除无意义数据等)、数据拆分为训练/验证/测试,以及通过创建新特征进行特征工程,以有望提高模型的预测能力。这一步的输出是训练、验证和测试数据,所有的数据都被清理,新的特征以正确的格式被添加,可以被传递到模型中。
- 模型训练:使用数据准备步骤的输出来实现不同的算法,以训练各种机器学习模型。通常,我在这一步骤中进行超参数调整,以探索超参数空间,从而确定最佳模型。这个模型的输出是在这个步骤中找到的最佳模型的模型工件(模型架构和模型权重)。
- 模型评估:根据数据准备步骤中收集的测试数据,对上一步中找到的最佳模型进行评估。在此步骤之前,必须决定一个评估度量或一组评估度量,以确定如何评估模型。
- 模型验证:选择的模型必须足够令人满意,才能被认为足以部署到生产中。为了确定这一点,通常模型必须比基线表现得更好。这可能是测试模型预测是否比它试图改进的当前过程更好。
- 模型服务:选择的模型被部署到适当的环境中,以使应用程序/流程能够使用模型的预测。要阅读关于各种模型部署选项的更多细节,请参考我的文章:https://towardsdatascience . com/machine-learning-model-deployment-options-47 C1 F3 d 77626。根据业务需求,该模型可以部署到以下位置之一:
1 .使用 REST API 的在线预测,其中应用程序/流程通过端点向模型提供输入数据,并接收回模型的预测。
2。模型嵌入到边缘设备中,并在边缘设备上计算模型的预测。
3。利用批量预测过程,其中周期性地或在某些事件上创建计算资源,并且在计算资源上部署模型以处理对输入数据的预测。 - 模型监控:监控部署的模型的性能,以确定机器学习过程是否需要经历另一次迭代。通常有一个评估度量阈值集,如果部署的模型的评估度量恶化超过这个阈值,那么可能是时候进行另一次迭代了。
一旦确定机器学习过程需要经历另一次迭代,所有的步骤被再次重复,最有可能的是对各个步骤进行调整或增强,以针对数据的变化进行调整。通过这种重复和循环的过程,很明显,自动化这些步骤将提高效率、一致性和可扩展性。
一般来说,一个组织能够自动化的这些步骤越多,机器学习过程就越成熟。能够自动化大部分或所有这些步骤的好处是,组织可以有效地进行许多实验,并更快地将经验证的模型部署到生产中。此外,通过消除手动流程,它降低了由人为错误导致的潜在故障的几率。
有 3 个级别的 MLOps,组成机器学习过程的步骤具有不同的自动化级别。这些级别因其特点和挑战而有所不同。我将在以后的文章中更详细地介绍这三个层次的特点和挑战。
Python 中的逐步回归教程
使用逐步回归发现数据中的意义

弗兰基·查马基在 Unsplash 上拍摄的照片
你如何在数据中找到意义?在我们的迷你项目中,我和我的朋友 @ErikaSM 试图预测新加坡的最低工资(如果我们有最低工资的话),并在一篇文章中记录了这个过程。如果你还没有读过,一定要看一看。
从那以后,我们收到了对我们的流程的评论和建议,以便更深入地了解我们的信息。因此,这篇后续文章概述了两个主要目标:发现数据中的意义,以及学习如何进行逐步回归。
上下文
在上一篇文章中,我们讨论了关于新加坡最低工资的话题是如何频繁地成为争论的热点。这是因为新加坡采用累进工资模式,因此没有最低工资。
新加坡政府的官方立场是,有竞争力的薪酬结构将激励劳动力努力工作,这与新加坡文化中根深蒂固的精英管理价值观是一致的。无论支持或反对新加坡最低工资的观点如何,穷人都在努力购买必需品,照顾自己和家人。
我们采取了一种中立的立场,承认双方论点的有效性,而是使用不同国家的某些指标对新加坡最低工资的预测进行了比较。预测的最低工资还与某些工作的累进工资模型(PWM)中的工资下限进行了对比,以引发一些关于最贫困者是否赚得足够多的讨论。
方法论
我们使用来自维基百科和世界数据的数据来收集关于最低工资、生活成本和生活质量的数据。生活质量数据集包括几个类别的分数:稳定性、权利、健康、安全、气候、成本和受欢迎程度。
所有指标和类别的分数都被输入一个线性回归模型,然后利用新加坡的统计数据作为独立变量来预测最低工资。这个线性模型是使用 sklearn 在 Python 上编码的,关于编码的更多细节可以在我们之前的文章中查看。然而,我也将在本文中简要概述建模和预测过程。
新加坡的预计年最低工资为 20,927.50 美元。下图中可以看到一个简单的对比。

新加坡的年最低工资约为 20,000 美元(图片来自作者)
我们的教授鼓励我们使用逐步回归来更好地理解我们的变量。从这个迭代中,我们结合了逐步回归来帮助我们降维,不仅产生一个更简单和更有效的模型,而且从我们的数据中获得洞察力。
逐步回归
那么到底什么是逐步回归呢?在任何一种现象中,都会有某些因素在决定一个结果中发挥更大的作用。简单来说,逐步回归是一个帮助确定哪些因素重要,哪些因素不重要的过程。某些变量具有相当高的 p 值,对我们预测的准确性没有有意义的贡献。在此基础上,只保留重要的因素,以确保线性模型根据有助于产生最准确结果的因素进行预测。
在这篇文章中,我将概述逐步回归的使用,它使用向后消除的方法。这是最初包含所有变量的地方,在每一步中,最不具统计意义的变量被丢弃。换句话说,最‘没用’的变量被踢了。重复这一过程,直到剩下的所有变量都具有统计显著性。
编码位
在继续分析回归模型之前,我们首先修改数据以反映月工资而不是年工资。这是因为我们认识到大多数人倾向于以月为单位而不是以全年为单位来看待他们的工资。以这种方式表达我们的数据可以让我们的受众更好地理解我们的数据。然而,还值得注意的是,这种规模的变化不会影响建模过程或结果。
查看我们之前的模型,我们生成了统计数据来测试模型的准确性。但在此之前,我们首先必须指定相关的 X 和 Y 列,并从数据文件中获取该信息。
## getting column names
x_columns = ["Workweek (hours)", "GDP per capita", "Cost of Living Index", "Stability", "Rights", "Health", "Safety", "Climate", "Costs", "Popularity"]
y = data["Monthly Nominal (USD)"]
接下来,为了收集模型统计数据,我们必须使用 statmodels.api 库。这里创建了一个函数,它从一个列表中获取感兴趣的列,然后用一个普通的最小二乘线性模型来拟合它。然后可以非常容易地打印出统计数据摘要。
## creating function to get model statistics
import numpy as np
import statsmodels.api as smdef get_stats():
x = data[x_columns]
results = sm.OLS(y, x).fit()
print(results.summary())get_stats()

原始回归统计数据(图片来自作者)
这里我们关注的是列“P > |t|”。本专栏引用了来自加州大学洛杉矶分校数字研究与教育研究所的一些技术解释,给出了用于检验零假设的双尾 p 值。
p 值小于α的系数在统计上是显著的。例如,如果您选择 alpha 为 0.05,则 p 值为 0.05 或更小的系数在统计上是显著的(即,您可以拒绝零假设,并说该系数与 0 显著不同)。”
换句话说,我们通常希望删除 p 值大于 0.05 的变量。从上面的初步总结中可以看出,统计学意义最小的变量是“安全性”, p 值为 0.968。因此,我们希望删除“安全”这一变量,如下所示。下面也显示了新的摘要。
x_columns.remove("Safety")
get_stats()

仅去除“安全”后的回归统计(图片来自作者)
这一次,新的统计意义最小的变量是“健康”。同样,我们也想去掉这个变量。
x_columns.remove("Health")
get_stats()

去除“安全”和“健康”后的回归统计(图片来自作者)
我们继续这个过程,直到所有的 p 值都低于 0.05。
x_columns.remove("Costs")
x_columns.remove("Climate")
x_columns.remove("Stability")

最后,我们发现还有 5 个变量,即工作周、人均 GDP、生活成本指数、权利和受欢迎程度。由于每个 p 值都低于 0.05,所有这些变量都被认为具有统计学意义。
我们现在可以根据这组新变量建立一个线性模型。我们也可以用这个来预测新加坡的最低工资。正如所见,预测的月最低工资约为 1774 美元。
## creating a linear model and prediction
x = data[x_columns]
linear_model = LinearRegression()
linear_model.fit(x, y)
sg_data = pd.read_csv('testing.csv')
x_test = sg_data[x_columns]
y_pred = linear_model.predict(x_test)
print("Prediction for Singapore is ", y_pred)>> Prediction for Singapore is [1774.45875071]
在数据中寻找意义
这是整个过程中最重要的部分。惠普(Hewlett-Packard)前首席执行官卡莉·菲奥莉娜(Carly Fiorina)曾经说过:“我们的目标是将数据转化为信息,将信息转化为洞察力。”这正是我们的目标。
"我们的目标是将数据转化为信息,将信息转化为洞察力."
~卡莉·菲奥莉娜,惠普前首席执行官
仅从变量来看,我们就可以很容易地预测出哪些变量具有统计学意义。例如,人均国内生产总值和生活费用指数在逻辑上是一个国家最低工资的良好指标。甚至一周工作的小时数作为一个指标也是有意义的。
然而,我们注意到“权利”仍然包含在线性模型中。这促使我们首先来看看权利和最低工资之间的关系。在绘制图表时,我们发现了这种美学上令人愉悦的关系。

月最低工资侵犯权利(图片由作者提供)
最初,我们不会认为权利与最低工资相关,因为 GDP 和生活成本这两个更明显的候选因素在最低工资水平中的贡献更大。这让我们重新考虑我们如何理解最低工资,并迫使我们深入研究。
从世界数据来看,“权利”涉及公民权利,主要围绕人民参政和腐败。我们发现民权指数包括民众的民主参与和打击腐败的措施。该指数还包括公众对政府的看法,包括来自 Transparency.org 的数据。
“此外,其他因素包括人民的民主参与和(不太强调)反腐败措施。为了不仅评估反腐败措施,而且评估民众对反腐败的看法,还考虑了基于 Transparency.org 的腐败指数。”
这迫使我们考虑民权和最低工资之间的相互关系。了解了这些信息后,我们做了进一步的研究,找到了几篇可能解释这种相关性的文章。
美国民权利益集团——民权和人权领袖会议发布了一份报告,阐述了为什么最低工资是一个民权和人权问题,以及加强最低工资政策的必要性,以减少不平等,确保在低收入工作中挣扎的个人和家庭获得公平的报酬。因此,这是有道理的,因为一个民主参与程度更高的国家也可能对最低工资表示担忧,迫使人们展开讨论,并最终随着时间的推移提高最低工资。
我们研究的下一个变量是受欢迎程度。我们首先研究了如何从世界数据中衡量这一点。
“因此,总体移民率和外国游客数量被视为一个国家受欢迎程度的指标。较低的评级也被用来比较各自国家的难民状况。更多的外国难民导致更高的受欢迎程度,而大量逃离的难民降低了受欢迎程度。”

月最低工资对比人气(图片由作者提供)
乍一看,似乎没有任何关联。然而,如果我们将中国、法国、美国和西班牙视为异常值,大多数数据点似乎更符合指数图。这引发了两个问题。首先,为什么受欢迎程度和最低工资有关系?其次,为什么这四个国家是离群值?
老实说,这难倒了我们。我们根本看不出受欢迎程度与最低工资有什么关联。然而,有一点很重要:在预测一个国家的最低工资时,这种受欢迎程度在某种程度上具有统计学意义。虽然我们可能不是发现这种关系的人,但这让我们深入了解了我们原本意义不大的数据。
结论
引用卡莉·菲奥莉娜的话很重要,“我们的目标是将数据转化为信息,将信息转化为洞察力。”作为人类,我们需要工具和方法将数据转化为信息,需要经验/知识将信息转化为洞察力。
我们首先使用 Python 作为工具,执行逐步回归来理解原始数据。这让我们不仅发现了我们预测到的信息,还发现了我们最初没有考虑到的新信息。很容易猜测工作周、GDP 和生活成本将是最低工资的有力指标。然而,只有通过回归,我们才发现民权和受欢迎程度也具有统计意义。
在这种情况下,我们发现网上有研究可以解释这些信息。这导致了新的见解,即最低工资实际上被视为一项人权,民主参与的增加可能会导致更多关于最低工资的讨论,从而提高最低工资。
然而,并不总是能够那么容易地发现数据中的意义。不幸的是,作为大学生,我们可能不是为我们的信息提供可能解释的最佳人选。这可以从我们试图解释受欢迎程度和最低工资之间的关系中看出。然而,我们有能力获取这些信息并将其传播到世界各地,让它成为一个开放的问题进行讨论。
这就是我们如何利用数据为世界增加价值。
- 与 Erika Medina 合作撰写
还在用 CSV 格式保存数据吗?尝试这些其他选项
了解如何以不同的格式(CSV、压缩、Pickle 和 Parquet)保存数据,以节省存储空间并减少读/写时间和资金

保存数据帧最流行的文件格式之一是 CSV(逗号分隔值)。CSV 是众所周知的,并在各种平台上得到广泛支持。然而,尽管 CSV 无处不在,但当您处理大量数据时,它并不是一种合适的格式——其大小会变得非常大,并且从中提取数据会变得非常慢。
在本文中,我将向您介绍将数据帧保存到存储时的一些选项。特别是,我将讨论将您的数据帧保存为:
- CSV 文件
- 应用压缩的 CSV 文件
- 使用 pickle 模块的二进制文件
- 使用 pickle 模块并应用了压缩的二进制文件
- HDF 文件
- 镶木地板
我们的样本数据集
我在本文中使用的样本数据集名为 travel insurance.csv (来源:https://www.kaggle.com/mhdzahier/travel-insurance)。它有 63,326 行和 11 列,混合了 object、int64 和 float64 列。让我们用熊猫作为数据帧来加载它:
**import pandas as pd
import os****filename = 'travel insurance'
df = pd.read_csv(filename + '.csv')****display(df)
display(df.shape) # (63326, 11)
display(df.dtypes)** # Agency object
# Agency Type object
# Distribution Channel object
# Product Name object
# Claim object
# Duration int64
# Destination object
# Net Sales float64
# Commision (in value) float64
# Gender object
# Age int64
# dtype: object

为了记录每种数据格式的文件大小、写入和读取时间,我将创建一个包含四列的数据帧:
df_results = pd.DataFrame(columns=
['method', 'file_size', 'write_time', 'read_time'])
display(df_results)

我还将创建一个 helper 函数,向包含每个实验细节的数据帧追加一行:
def add_result(df_results, method, file_size,
write_time, read_time):
row = {
'method': method,
'file_size': file_size,
'write_time': write_time,
'read_time': read_time
}
return df_results.append(pd.Series(row), ignore_index = True)
另存为 CSV 文件
我想尝试的第一种方法是将数据帧保存为 CSV 文件,然后读回它。以下代码块执行以下操作:
- 将数据帧保存为 CSV 文件
- 获取物理 CSV 文件的大小
- 再次将 CSV 文件作为数据帧加载
- 将使用的方法、文件的大小、将数据帧写入文件并再次加载回数据帧所需的平均时间写入
df_results数据帧
#---saving---
result_save = %timeit -n5 -r5 -o **df.to_csv**(filename + '_csv.csv')#---get the size of file---
filesize = os.path.getsize(filename + '_csv.csv') / 1024**2#---load---
result_read = %timeit -n5 -r5 -o **pd.read_csv**(filename + '_csv.csv')#---save the result to the dataframe---
df_results = add_result(df_results,
'CSV',
filesize,
result_save.average,
result_read.average)
df_results
特别是,我使用了**%timeit**这个神奇的命令来记录系统执行一条语句所花费的时间。例如,下面一行记录了系统将数据帧保存到 CSV 文件所花费的时间:
**%timeit -n5 -r5 -o** df.to_csv(filename + '_csv.csv')
%timeit魔法命令有以下选项:
**-n5**选项意味着您希望在一个循环中运行该语句 5 次。**-r5**表示循环运行 5 次,取最佳结果。这允许我们获得读写文件的平均操作时间。- -o 选项表示您希望返回计时结果,而不是将其打印出来。在这种情况下,结果被传递给写部分的
result_save变量和读部分的result_read。 - 使用从
%timeit命令返回的结果,您可以使用average属性获得执行写和读操作所需的平均时间。
有了写入和读取的结果,现在可以调用add_result()函数将结果添加到 dataframe 中。对于这一部分,我得到了以下结果:
165 ms ± 7.11 ms per loop (mean ± std. dev. of 5 runs, 5 loops each)
50.4 ms ± 9.32 ms per loop (mean ± std. dev. of 5 runs, 5 loops each)
df_result数据帧看起来像这样:

文件大小以兆字节(MB)为单位,时间以秒为单位。
保存不带索引的 CSV
默认情况下,将数据帧保存为 CSV 文件时,会保存其索引。然而,大多数时候没有必要将索引(它只是一系列流水号)保存到 CSV 文件中。所以这一次我们将省略它:
#---saving---
result_save = %timeit -n5 -r5 -o df.to_csv(filename + '_csv.csv', \
**index=False**)#---get the size of file---
filesize = os.path.getsize(filename + '_csv.csv') / 1024**2#---load---
result_read = %timeit -n5 -r5 -o pd.read_csv(filename + '_csv.csv')#---save the result to the dataframe---
df_results = add_result(df_results,
'CSV No Index',
filesize,
result_save.average,
result_read.average)df_results
结果如下:

您可以看到,文件大小略有减小,写入和读取时间都缩短了。
对 CSV 使用压缩
当你保存数据帧到 CSV 文件时,Pandas 支持压缩。具体来说,Pandas 支持以下压缩算法:
- gzip
- bz2
- 活力
- xz
让我们看看压缩将如何有助于文件大小以及写入和读取时间。
GZIP
要在将数据帧保存为 CSV 格式时使用压缩,请将压缩参数设置为您想要使用的算法。同样,当您加载压缩的 CSV 文件时,将压缩参数设置为用于压缩文件的算法:
#---saving---
result_save = %timeit -n5 -r5 -o df.to_csv(filename + '.gzip', \
index=False, \
**compression='gzip'**)#---get the size of file---
filesize = os.path.getsize(filename + '.gzip') / 1024**2#---load---
result_read = %timeit -n5 -r5 -o pd.read_csv(filename + '.gzip', \
**compression='gzip'**)#---save the result to the dataframe---
df_results = add_result(df_results,
'CSV No Index (GZIP)',
filesize,
result_save.average,
result_read.average)df_results
结果如下:

文件大小大幅减少。写入时间稍长,但读取时间与解压缩版本相当。
有关 GZIP 的更多信息,请参考:https://en.wikipedia.org/wiki/Gzip
BZ2
另一种流行的文件压缩算法被称为 BZ2。以下代码片段显示了如何使用 BZ2 压缩数据帧:
#---saving---
result_save = %timeit -n5 -r5 -o df.to_csv(filename + '.bz2', \
index=False, \
**compression='bz2'**)#---get the size of file---
filesize = os.path.getsize(filename + '.bz2') / 1024**2#---load---
result_read = %timeit -n5 -r5 -o pd.read_csv(filename + '.bz2', \
**compression='bz2'**)#---save the result to the dataframe---
df_results = add_result(df_results,
'CSV No Index (BZ2)',
filesize,
result_save.average,
result_read.average)df_results
我得到了以下结果:

您可以观察到,与 GZIP 相比,BZ2 的压缩率更高,但是相应的读写速度更慢。
有关 BZ2 的更多信息,请参考:https://en.wikipedia.org/wiki/Bzip2
泡菜
CSV 文件是纯文本文件(压缩文件除外),这使得它们无处不在,在所有平台和所有主流软件上都受支持。然而,CSV 的主要缺点是它的大小。您可以将数据帧保存为二进制文件,而不是将数据帧保存为纯文本文件。
在 Python 中,可以使用 pickle 模块将数据(包括数据帧)作为二进制文件持久化。 pickle 模块将 Python 中的对象序列化为二进制文件,然后将二进制文件反序列化为 Python 中的对象。
让我们尝试使用 pickle 保存我们的数据帧:
#---saving---
result_save = %timeit -n5 -r5 -o **df.to_pickle(filename + '.pkl')**#---get the size of file---
filesize = os.path.getsize(filename + '.pkl') / 1024**2#---load---
result_read = %timeit -n5 -r5 -o **pd.read_pickle(filename + '.pkl')**#---save the result to the dataframe---
df_results = add_result(df_results,
'Pickle',
filesize,
result_save.average,
result_read.average)df_results
我得到了以下结果:

文件大小小于 CSV 文件,但大于对 CSV 文件使用压缩时的大小。但是,请注意,到目前为止,写入和读取时间是最快的。
压缩使用泡菜
像另存为 CSV 一样,也可以用 pickle 进行压缩。首先,让我们用 GZIP :
#---saving---
result_save = %timeit -n5 -r5 -o **df.to_pickle(filename + '.pkl', \** compression='gzip'**)**#---get the size of file---
filesize = os.path.getsize(filename + '.pkl') / 1024**2#---load---
result_read = %timeit -n5 -r5 -o **pd.read_pickle(filename + '.pkl', \
compression='gzip')**#---save the result to the dataframe---
df_results = add_result(df_results,
'Pickle (GZIP)',
filesize,
result_save.average,
result_read.average)df_results
我得到了以下结果:

然后,我们用 BZ2 :
#---saving---
result_save = %timeit -n5 -r5 -o **df.to_pickle(filename + '.pkl', \
compression='bz2')**#---get the size of file---
filesize = os.path.getsize(filename + '.pkl') / 1024**2#---load---
result_read = %timeit -n5 -r5 -o **pd.read_pickle(filename + '.pkl', \
compression='bz2')**#---save the result to the dataframe---
df_results = add_result(df_results,
'Pickle (BZ2)',
filesize,
result_save.average,
result_read.average)df_results
我得到了以下结果:

接下来,我们使用 zip :
#---saving---
result_save = %timeit -n5 -r5 -o **df.to_pickle(filename + '.pkl', \
compression='zip')**#---get the size of file---
filesize = os.path.getsize(filename + '.pkl') / 1024**2#---load---
result_read = %timeit -n5 -r5 -o **pd.read_pickle(filename + '.pkl', \
compression='zip')**#---save the result to the dataframe---
df_results = add_result(df_results,
'Pickle (ZIP)',
filesize,
result_save.average,
result_read.average)df_results
我得到了以下结果:

关于 zip 的更多信息,请参考:https://en . Wikipedia . org/wiki/ZIP _(file _ format)
最后,我们使用 xz :
#---saving---
result_save = %timeit -n5 -r5 -o df.to_pickle(filename + '.pkl', \
compression='xz')#---get the size of file---
filesize = os.path.getsize(filename + '.pkl') / 1024**2#---load---
result_read = %timeit -n5 -r5 -o pd.read_pickle(filename + '.pkl', \
compression='xz')#---save the result to the dataframe---
df_results = add_result(df_results,
'Pickle (xz)',
filesize,
result_save.average,
result_read.average)df_results
我得到了以下结果:

关于 xz 的更多信息,请参考:https://en.wikipedia.org/wiki/XZ_Utils
从结果中可以看出,在 pickle 中使用压缩大大减小了文件的大小,而与不使用压缩相比,写入和读取时间略有增加。
HDF
另一种可以用来保存数据帧的文件格式是 HDF — 分层数据格式。HDF 是一种开源文件格式,支持大型、复杂、异构的数据。HDF 使用类似于文件目录的结构来组织文件中的数据。
可以把 HDF 想象成一种文件结构,它允许你在一个物理文件中存储不同的数据帧。
以下代码片段将数据帧保存为 HDF 文件:
#---saving---
result_save = %timeit -n5 -r5 -o **df.to_hdf(filename + '.h5', \
key='key', \
mode='w')**#---get the size of file---
filesize = os.path.getsize(filename + '.h5') / 1024**2#---load---
result_read = %timeit -n5 -r5 -o **pd.read_hdf(filename + '.h5', \
key='key', \
mode='r')**#---save the result to the dataframe---
df_results = add_result(df_results,
'HDF',
filesize,
result_save.average,
result_read.average)df_results
我得到了以下结果:

正如您所观察到的,另存为 HDF 并没有真正减少文件大小,即使写时间比原始 CSV 好。
镶木地板
我要讨论的最后一个文件格式是 Parquet 。那么什么是拼花,或者更准确的说, 阿帕奇拼花 ?
Apache Parquet 是一种文件格式,旨在支持复杂数据的快速数据处理。它是 Apache Hadoop 许可下的开源文件格式,与大多数 Hadoop 处理框架兼容。Parquet 是自描述的——包括模式和结构的元数据嵌入在每个文件中。
更重要的是,Parquet 将数据存储在列中,而不是行中。
Parquet 如何存储您的数据
考虑以下具有三列的数据帧:

作者图片
当您将数据帧保存为 CSV 文件时,它使用基于行的存储。将 CSV 文件加载到数据帧中时,每行一次加载一行,每行包含三种不同的数据类型:

作者图片
另一方面,Parquet 使用基于列的存储方式来存储数据。每列数据都被组织为一个特定数据类型的列:

作者图片
简而言之,当您在基于列的存储中组织数据时,您的文件将更加轻量级,因为所有相似的数据类型都被分组在一起,并且您可以对每一列应用压缩。更重要的是,使用基于列的存储使得提取特定列变得非常有效,这是数据分析项目中经常要做的事情。
要使用拼花地板,您需要安装快速拼花地板模块:
**!pip install fastparquet**
以下代码片段将 dataframe 保存到一个 parquet 文件中,然后将其加载回来:
# !parq Building_Permits.parquet --head 10#---saving---
result_save = %timeit -n5 -r5 -o **df.to_parquet(filename + \
'.parquet', \
engine='fastparquet')**#---get the size of file---
filesize = os.path.getsize(filename + '.parquet') / 1024**2#---load---
result_read = %timeit -n5 -r5 -o **pd.read_parquet(filename + \
'.parquet')**#---save the result to the dataframe---
df_results = add_result(df_results,
'Parquet',
filesize,
result_save.average,
result_read.average)df_results
我得到以下结果:

从结果中,您可以看到文件大小显著减小(虽然没有在 CSV 或 pickle 上使用压缩那么大,但这种减小仍然是显著的),并且写入和读取时间是最快的。
那么拼花地板为什么有用,为什么要用呢?为什么应该使用拼花地板而不是 CSV 存储数据,有几个令人信服的理由:
- 云服务(如 AWS 和谷歌)根据你的数据大小收费。拼花文件比 CSV 文件小得多,这意味着您为云存储支付的费用更少。
- 云服务也根据每次查询扫描的数据收费。在上面的结果中,你可以看到读写时间比 CSV 文件快。更短的扫描时间意味着更低的云电荷。
为了查看 parquet 在加载特定列时与 CSV 文件相比的性能,让我们尝试以下实验,我们将从三个 CSV 文件(一个未压缩,一个用 BZ2 压缩,一个用 GZIP 压缩)和一个 Parquet 文件中加载两列(代理类型和产品名称):
%timeit -n5 -r5 -o **pd.read_csv(filename + '_csv.csv', \
usecols=['Agency Type','Product Name'])**%timeit -n5 -r5 -o **pd.read_csv(filename + '.bz2', \
usecols=['Agency Type','Product Name'], \
compression='bz2')**%timeit -n5 -r5 -o **pd.read_csv(filename + '.gzip', \
usecols=['Agency Type','Product Name'], \
compression='gzip')**%timeit -n5 -r5 -o **pd.read_parquet(filename + '.parquet', \
columns=['Agency Type','Product Name'])**
上述语句返回以下结果:
27.4 ms ± 6.23 ms per loop (mean ± std. dev. of 5 runs, 5 loops each)77.4 ms ± 3.26 ms per loop (mean ± std. dev. of 5 runs, 5 loops each)35 ms ± 2.15 ms per loop (mean ± std. dev. of 5 runs, 5 loops each)**4.72 ms ± 1.73 ms per loop (mean ± std. dev. of 5 runs, 5 loops each)**
最后的结果(使用拼花地板)轻而易举地击败了其他人。使用 Parquet 检索这两列平均需要 4.72 毫秒,而 CSV 文件需要 27 到 77 毫秒。
为每项任务寻找最佳方法
现在我们已经有了各种方法的统计数据,我们可以很容易地找出哪种方法对于每项任务来说是最理想的。
如果你想最小化读取时间,使用 pickle 保存是最好的解决方案:
df_results.sort_values(by='read_time')

注意,就读取时间而言,Parquet 紧随其后。此外,它还大大减少了文件大小。如前一节所述,在云服务上存储数据时,最大限度地减少读取时间和存储对于降低成本至关重要。
如果您想最大限度地减少写入时间,似乎使用 pickle 也是保存数据帧的最佳方式:
df_results.sort_values(by='write_time')

如果您想要最小化数据帧的文件大小,请将 pickle 与 bz2 压缩算法结合使用:
df_results.sort_values(by='file_size')

摘要
本文讨论了一些可以用来保存数据帧的文件格式。这绝不是一个详尽的测试,但它让您对每种文件格式的性能有一个总体的感觉。值得注意的是,我在本文中获得的结果是特定于我的数据集的。不同的数据集可能会有不同的结果,这取决于您拥有的数据类型。如果数据集有很多对象(文本)列,压缩会非常有效。重要的是,使用本文中描述的各种方法测试数据集,并得出自己的结论。特别是,比较最常执行的操作的统计数据,并决定哪种方法更适合您的数据。
https://weimenglee.medium.com/membership
还在用平均值?
不要这么刻薄。

对算术平均值说“不”的描述😉(图片由作者提供)
在社会科学以及许多其他领域中,传统的推断统计学经常用于比较群体和测量关联。不幸的是,基于均值和标准差的方法,如 T-和 F-检验、相关性以及统计学入门教材中的许多其他检验,都不够稳健。实际上,这意味着它们会导致假阳性率的增加和统计能力的缺乏。呸!
在这篇文章中,我将把几种集中趋势的测量方法放在一起,以证明平均值不是一个稳健的估计量。但是不用担心!有很多稳健的估计量(和假设检验)可供选择。
我正在使用 Deepnote 进行这一分析,你可以在一个现成的笔记本这里找到所有代码,你需要的一切来揭露卑鄙,嗯,如此卑鄙…😉!
如何衡量稳健性
评估稳健性的最直接方法之一是比较各种估计量的所谓标准误差(SE)。SE 是对总体中随机样本的估计量变化程度的度量。如果我们的估计量在不同的样本之间变化很大,那么它就有很高的标准误差。另一方面,如果样本之间相似,则标准误差相对较小。我们希望选择一个相对于我们可能做出的另一个选择具有最小可能标准误差的估计量。
在本文中,我们将比较均值、中值、一步估计量和 20%修整均值的 SE。如果你想了解更多关于这些评估者和他们发展的起源,看看这本书。
在我们讨论代码、图表和模拟之前,让我们快速地看一下 SE 是如何推导出来的,这样您就可以看到计算没什么大不了的。
估计标准误差的步骤
使用计算机和您选择的编程语言可以很容易地估算出标准误差。以下是测量估计量的标准误差的步骤:
- 从已知的分布中随机抽取一个大小为 N 的样本
- 计算您选择的估计值(例如,平均值)
- 重复第 1 步和第 2 步 B 次(假设 B =5000,但任何以千为单位的都足够了)
- 最后,计算得到的 B 估计值的标准偏差,瞧,这就是那个估计值的标准误差!
设计人口
在共享笔记本和下面的 GIF 中,你会看到我正在定义一个理论人口的特征。在现实生活中,我们永远不会真正知道被研究人群的形状,但这不是现实生活,这是一个模拟!这很重要,因为产生异常值的分布会影响 SE。我们想知道我们的估计量在各种潜在条件下的 SE 表现如何。

既然我们已经选择了总体形状及其异常值(即污染)的比例,我们可以构建相应的概率分布函数并绘制图表。
污染χ分布的 PDF(我们的理论总体)
选择评估者
甜食👌!这将是一个很好的人群样本,以估计 SE。现在,让我们选择一个估计量和样本大小。我们快到了!

如果上面的 GIF,我选择了可信的(或者不那么可信!)表示 N 为 20(即本次模拟的样本量)。一旦我做出了选择,我就可以通过运行下面的代码行来开始模拟。如果你已经忘记了模拟所采取的步骤,请参考上面称为估计标准误差的步骤部分。
基于所选χ分布的平均值的抽样分布
经过多次随机抽样后,我们最终得到了一组基于给定总体的结果平均值,如上所示。平均值的这种分布被称为平均值的抽样分布。
最后,为了得出平均值的标准差,我们只需取上述抽样分布的标准差。结果是 ~0.64 (基于前面定义的χ分布)。
让评估人员面对面
现在,问题是:与均值相比,其他估计量的表现如何?记住,我们希望一个估计量相对于我们可能选择的其他估计量具有较低的 SE。
没问题,让笔记本中的代码来完成工作,并将 SE 作为人口形状和估计量的函数绘制成图表,如下所示:
SE、总体形状和估计量(N=12)。缩放/污染值可在笔记本中找到。
如您所见,使用笔记本中定义的参数,有几个外卖结果:
- 相对于其他估计量,均值在许多情况下的标准差表现不佳
- 均值在完全正态下表现最好,但差不了多少
- 许多人都熟悉的中位数,在正常情况下并不像人们想象的那样表现良好
- 污染分布(具有重尾的分布)会增加估计量的 se(尤其是均值)
- 20%的修整平均值在各种情况下表现都很好
感谢阅读!
关于稳健性的研究已经有半个多世纪了。传统推断假设检验的问题已经被很好地记录下来,替代方法,如基于 20%修整均值的方法已经被开发出来,并在许多统计软件包中可用。带着它们转一圈,享受更低的误报率和更高的功率💪!
如果你想了解更多,我肯定会推荐兰德的以下书籍。r .威尔科克斯:
还在用 Python 里的 OS 模块?这个选择明显更好
Python 的 OS 模块是管理文件和文件夹的噩梦。你应该试试 Pathlib。

照片由 Pontus Wellgraf 在 Unsplash 上拍摄
用 Python 的os模块管理文件和文件夹简直是一场噩梦。然而,它是每个数据科学工作流程的重要组成部分。保存报告、读取配置文件,凡是你能想到的,都不在话下。
想象一下——你花了几周的时间围绕你的模型构建一个 API,它完美地工作,至少在你的机器上。一旦部署,这是一个完全不同的故事。您的 API 会在意想不到的地方失败,甚至无法运行,因为您硬编码的绝对路径根本不存在。
有一个简单的解决方案。Python 3.4 及更高版本默认提供了pathlib库。这是目前为止在你的应用程序中处理文件、文件夹及其连接最人性化的方式。最棒的是——今天你将了解这一切。我并不是说os模块毫无用处,只是说pathlib在文件和文件夹管理方面更胜一筹。
首先,创建一个新的 Python 文件并导入pathlib库:
import pathlib
你需要在一个.py文件中才能使用一些功能。例如,您将无法访问 Jupyter 笔记本中的__file__属性。其他一切都应该运行良好。
以下是你今天将学到的内容:
**·** [**Get a path to the current Python file**](#bfc5) **·** [**Get a path to the current working directory**](#c12b) **·** [**Get a first parent folder path**](#73ec) **·** [**Get an Nth parent folder path**](#fa39) **·** [**Join paths**](#a452) **·** [**Create a directory if it doesn’t exist**](#a315) **·** [**Create files**](#bcba) **·** [**Check if the path is a folder**](#cf03) **·** [**Check if the path is a file**](#b03e) **·** [**Get the name of the file**](#70e5) **·** [**Get the file extension**](#2501) **·** [**Iterate over files in a folder**](#26f8)
获取当前 Python 文件的路径
有时,您需要包含 Python 文件名的工作目录的绝对路径。用pathlib可以快速获得。记住,这个命令不会在 Jupyter 笔记本中工作,因为你不能访问那里的__file__属性。如果您是笔记本用户,请跳过这一部分。
总之,下面是如何获得 Python 脚本的绝对路径+文件名:
curr_file = pathlib.Path(__file__)
print(curr_file)
这是它在我的机器上的样子:
>>> /Users/dradecic/Desktop/pathlib_guide/python_pathlib.py
简单,对吧?对。
如果不需要文件名,有一个更简单的解决方案。
获取当前工作目录的路径
这相当于在 Unix shell 中执行pwd。它将返回您当前所在目录的路径,或者正在运行的脚本所在的位置。
下面是它的使用方法:
cwd = pathlib.Path.cwd()
print(cwd)
这是它在我的机器上打印的内容:
>>> /Users/dradecic/Desktop/pathlib_guide
如你所见,没有文件名。
但是如果你需要访问父文件夹中的文件呢?对我来说,那就是。让我们接下来讨论这个问题。
获取第一父文件夹路径
这个很简单。你只需要访问当前工作目录的parent属性。这是如何做到的:
one_above = pathlib.Path.cwd().parent
print(one_above)
这是它在我的机器上打印的内容:
>>> /Users/dradecic/Desktop
太好了!但是如果一个父文件夹不够呢?让我们看看你有什么选择。
获取第 n 个父文件夹路径
你有选择。第一个是像疯子一样多次呼叫进入parent房产。这里有一个例子:
mul_above = pathlib.Path.cwd().parent.parent.parent
更简单的方法是访问parents属性数组并对其进行索引。例如,下面是获取第二个父文件夹路径的方法:
mul_above = pathlib.Path.cwd().parents[1]
print(mul_above)
结果如下:
>>> /Users/dradecic
数组索引从 0 开始,所以访问parents[1]会进入第二个父文件夹。
你现在有足够的知识开始加入路径。接下来看看如何。
加入路径
假设一个包含销售报告的文件夹位于您当前位置的两个目录之上,该报告称为summer-sales.csv。有办法用绝对路径访问吗?
当然有。
您已经知道如何访问第 n 个父文件夹路径。您将通过调用joinpath()并提供报告名称作为参数来扩展该功能:
tgt_path = pathlib.Path.cwd().parents[1]
tgt_fname = 'summer-sales.csv'
print(tgt_path.joinpath(tgt_fname))
这是它在我的机器上打印的内容:
>>> /Users/dradecic/summer-sales.csv
joinpath()函数可能是我用的最多的一个。超级有用。
如果目录不存在,则创建一个目录
如果每次因为我忘记创建目录而导致生产失败时,我都会得到一美元…这是一个非常常见的错误,而pathlib可以让你没有太多麻烦地解决它。
假设您想要将销售报告存储在当前工作目录下的reports文件夹中。您必须先创建该文件夹,然后才能在其中存储文件。只有当它不存在时,您才应该创建它。
简而言之——使用mkdir()创建一个文件夹,使用exists()检查文件夹是否已经存在。
以下是完整的代码片段:
tgt_path = pathlib.Path.cwd().joinpath('reports')
if not tgt_path.exists():
tgt_path.mkdir()
执行上述代码将会创建一个reports文件夹,您已经猜到了:

图 1 —使用 Pathlib 创建文件夹(作者图片)
干净利落。接下来让我们看看如何在该文件夹中创建文件。
创建文件
您通常会通过一些第三方库保存报告。然而,您也可以使用pathlib创建任何类型的空文件。
以下是如何在reports文件夹中创建 CSV 和 TXT 文件:
tgt_path = pathlib.Path.cwd().joinpath('reports')
tgt_path.joinpath('summer-sales.csv').touch(exist_ok=True)
tgt_path.joinpath('winter-sales.txt').touch(exist_ok=True)
exist_ok=True参数告诉 Python 覆盖一个已经存在的文件。
让我们看看文件是否已创建:

图 2 —使用 Pathlib 创建文件(作者图片)
很管用。
检查路径是否是文件夹
如果你想检查一个给定的路径是否是一个文件夹,只需看看is_dir()函数。它返回一个布尔值。
以下示例对文件夹和文件都使用了上述函数:
tgt_path = pathlib.Path.cwd().joinpath('reports')
print(tgt_path.is_dir())
print(tgt_path.joinpath('summer-sales.csv').is_dir())
以下是您应该看到的打印内容:
>>> True
>>> False
这就是全部了!
检查路径是否是文件
与前面的例子类似,您可以使用is_file()函数来检查给定的路径是否会产生一个文件。
下面的示例在文件夹和文件中都使用了它:
tgt_path = pathlib.Path.cwd().joinpath('reports')
print(tgt_path.is_dir())
print(tgt_path.joinpath('summer-sales.csv').is_dir())
正如你所想象的,这次你会得到完全相反的结果:
>>> False
>>> True
在今天结束之前,让我们探索几个更有用的功能。
获取文件的名称
如果需要从绝对路径中提取文件名,可以访问name属性。
这里有一个简单但不太有用的例子。它打印我们的summer-sales.csv文件的文件名:
tgt_path = pathlib.Path.cwd().joinpath('reports/summer-sales.csv')
print(tgt_path.name)
下面是您应该在控制台中看到的内容:
>>> summer-sales.csv
没什么。
获取文件扩展名
有时候你需要的只是一个文件扩展名。可能你想对不同的文件类型做不同的处理,对文件名不太在意。suffix物业有你罩着。
以下是如何从同一个summer-sales.csv文件中获取文件扩展名的方法:
tgt_path = pathlib.Path.cwd().joinpath('reports/summer-sales.csv')
print(tgt_path.suffix)
下面是您应该看到打印出来的内容:
>>> .csv
最后,让我们来看看迭代。
遍历文件夹中的文件
假设您在单个目录中有一堆 CSV 报告,并且想要逐个处理它们。你只需要iterdir()功能。
整个过程再简单不过了:
tgt_path = pathlib.Path.cwd().joinpath('reports')
for file in tgt_path.iterdir():
print(file)
这是它在我的机器上打印的内容:
>>> /Users/dradecic/Desktop/pathlib_guide/reports/summer-sales.csv
/Users/dradecic/Desktop/pathlib_guide/reports/winter-sales.txt
今天到此为止。你现在已经拥有了一切,再也不会犯愚蠢的生产错误了。
你对 *pathlib* 有什么想法?它是你最喜欢的文件和文件夹管理库,还是你对其他东西感兴趣?请在下面的评论区告诉我。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
https://medium.com/@radecicdario/membership
保持联系
还在用同样的旧假设检验数据科学吗?

罗斯·芬登在 Unsplash 上的照片
尝试这三种方法,让你的数据科学项目更上一层楼
如果你正在读这篇文章,我假设你知道假设检验是如何工作的。如果你不知道,请先阅读这篇文章。
何必呢?
首先,为什么要费心去取代通常的假设检验呢?嗯,显然使用它们没有错,本身:它们已经存在了几十年,在统计学上表现得很好。为了统计。
然而,在数据科学中,情况略有不同:
- 你通常有更多的数据可用。起初,这似乎不是一个问题,但是大量的样本通常会导致您的测试返回具有统计意义的结果,因为它们考虑了样本大小
- 你可能最终会做更多的同步测试,这通常意味着它们中的一些会被认为是偶然的。这可以通过使用某种调整(如 Bonferroni 调整)来部分解决
- 有时候,你不能因为在他们身上测试东西而失去宝贵的客户
当您遇到一个或多个这样的问题时,您有一些传统假设测试的替代方法,可能会帮助您将数据科学项目提升到下一个级别。
替代方案
排列测试
好吧,公平地说,排列测试仍然是一种假设测试。然而,它们是不同的,不仅因为它们是非参数化的,而且因为,尽管它们有用,它们却被极度忽视,因此未被充分利用。
“非参数”意味着我们不需要数据来遵循特定类型的分布,这是非常有用和实用的。对于参数测试,我们需要一些初步测试来验证最终测试所需的假设。这些在这里都是不需要的,这不仅让我们的生活变得更容易,也让结果变得更可靠、更有解释力。
既然我们已经理解了排列测试的好处,那么我们实际上是如何做的呢?假设你想测试两个不同的群体(我们称这两个群体为 A 和 B)之间是否存在工资差距。让我们来完成必要的步骤:
- 将不同组的所有观察结果放在一个数据集中
- 打乱数据,然后随机抽取一个与 A 组相同大小样本(没有替换)
- 使用剩余数据,抽取一个与 B 组相同大小的随机样本(无替换)
- 如果你有两个以上的小组,重复这个过程,直到你完成了所有的观察
- 计算你的兴趣统计数据(在我们的例子中,是各组工资的差异)并记录在某个地方
- 多次重复前面的步骤(大约在 1000 到 2000 之间),以获得感兴趣的统计数据的排列分布
- 最后,将原始统计数据与排列分布进行比较,看它是否位于 x%的中间范围(x 是您想要的置信水平。95%是通常情况,但这取决于域)。如果是的话,这可能意味着观察到的差异是由于随机的机会
该算法的 Python 实现可从这里获得。
多臂 bandit 算法
一个多臂强盗算法对于 A/B 测试来说是一个很好的工具,它稍微向你正在测试的更成功的方法倾斜分布。与传统测试相比,这可以更快地做出决策。
这里有一个例子:你为你的网站的特定页面设计了一个新版本,但你不知道它是否会比旧版本更好。所以你开始将一些流量导向新版本,然后你进行实验,这样一个版本运行得越好,它获得的流量就越多。从长远来看,这将向您展示最好的版本,而不会因为一个糟糕的版本而损失很大一部分流量。
“多臂土匪”是一类算法,有许多可能的实现方式,但这里有最常见的一种,叫做衰变ε-贪婪,应用于我们的网站版本示例:
- 定义一个在 0 和 1 之间的数字,称为ε,它随时间减少。例如,这可以是 1/(10 * t) ,其中 t 是从实验开始以来经过的时间(以天为单位),从 1 开始。ε越大,你探索的就越多,但是你利用已有知识的就越少。在这个例子中,你将在第一天用 10%的观察值开始测试,在第 10 天,这个数字将下降到 1%。如果你的网站没有太多的流量,在分母中使用一个小于 10 的数字。
- 每当客户访问您的网站时,生成一个随机数,均匀分布在 0 和 1 之间
- 如果该数字介于 0 和ε之间,随机选择两个页面版本中的一个,以相同的概率(50%)让该客户查看
- 如果数字介于 epsilon 和 1 之间,向客户展示到目前为止具有最佳指标的页面版本(该指标可以是您试图优化的任何指标,如转换率或在页面上花费的时间)
虽然这个例子只考虑了 2 个页面版本,但是该算法也适用于比较多于 2 个组的,,只需修改步骤 3 以 1/n 的概率选择 n 个组中的一个。
当足够的时间过去后,算法将结束收敛到最佳页面 版本,而不会在较差版本上浪费太多流量。
贝叶斯推理
贝叶斯统计(以及贝叶斯推断)比本文的目标要深入得多,所以我们在这里会尽量保持实用性。如果你想了解更多关于贝叶斯统计的知识,请查看下面的“进一步阅读”部分。但是,如果我们试着用两句话来总结:
你对世界如何运转有一个先验的信念。一旦你得到数据,你就相应地更新这种信念。
因此,当谈到概率时,贝叶斯主义者将其视为“你有多相信某件事是真的”,而不是“这件事多久发生一次”(频率主义者的方法)。
也就是说,让我们看看贝叶斯推理如何解决一个典型的例子:“一个硬币公平吗?”利用之前投掷硬币的数据。在这种情况下,你想知道θ = 50%,其中θ是正面(或者反面,无所谓)的概率。
你首先定义一个先验,也就是你认为θ的实际分布。例如,这可能是β(2,2),它将使θ对称分布在 50%左右。然后,你通过使用观察到的投掷和一个伯努利分布更新这个分布,用于(0,1)实验(伯努利)。这将允许算法根据你观察到的θ的可能性创建一个模型。然后,您可以使用该模型生成样本,并从这些样本中估计θ的平均值和 99%的最高后验密度(HPD),这是一个包含θ的概率为 99%的区间(直观上这看起来很像置信区间,但它们不是一回事)。在这篇文章中使用 Python 代码详细描述了整个过程。
可以使用相同的过程来比较两组:如果我们回到我们的网站页面版本示例,您可以分别对每个组的转换率进行此过程,然后通过计算其中一个组的转换率较高的概率来比较两个分布。
进一步阅读
- 彼得·c·布鲁斯(Peter C. Bruce)、安德鲁·布鲁斯(Andrew Bruce)和彼得·格德克(Peter Gedeck)所著的《数据科学家实用统计学》(Practical Statistics for Data Scientists)一书简要介绍了排列测试和多臂土匪算法。你可以在这里阅读我对这本书的总结。
- 要更深入地了解贝叶斯推理,可以试试艾伦·唐尼的书《思考贝叶斯》,这本书可以在作者的网站上免费获得。
如果你喜欢这篇文章,你可能也会喜欢这些:
如果你想进一步讨论,请随时通过 LinkedIn联系我,这将是我的荣幸(老实说)。
现实生活中解释的随机梯度下降
预测你的披萨的烹饪时间

作者图片
梯度下降是挑选最适合训练数据的模型的最流行方法之一。通常,这是最小化损失函数的模型,例如,最小化线性回归中的残差平方和。
随机梯度下降是一种随机梯度下降自旋。它改进了梯度下降法的局限性,在大规模数据集上表现得更好。这就是为什么它被广泛用作像深度学习这样的大规模、在线机器学习方法中的优化算法。
但是为了更好地理解随机梯度下降,你需要从梯度下降部分开始。
柯西的遗产:梯度下降
梯度下降和优化算法的历史并不是从现代机器学习开始的,它可以一直追溯到 19 世纪的法国。
法国数学家、工程师和物理学家奥古斯丁·柯西是复分析和抽象代数领域的先驱。他的影响永远印在数学和物理学中,有几个定理和概念以他的名字命名,如柯西分布、柯西定理或柯西弹性材料。

法国数学家奥古斯丁-卢伊斯·柯西(学分)
在柯西的众多关键贡献中,据说他还发明了梯度下降。
柯西的动机是他想不用解微分方程来计算天体的轨道,而是解代表天体运动的代数方程[1]。
梯度下降,最陡的下降
梯度下降也叫最速下降。该算法背后的思想是找到一种达到函数最小值的有效方法。
这正是你在机器学习模型中想要做的!
模型的损失函数,通常被称为成本函数,告诉你模型拟合训练数据的好坏。成本函数越小,模型拟合得越好。
梯度下降的特别之处在于,要找到一个函数的最小值,这个函数本身需要是一个可微的 凸函数。这些名字听起来很复杂,但是有一种直觉。

凸函数和非凸函数、可微函数和不可微函数的例子。
凸函数
当你听到凸函数时,想象一个形状像碗的函数。
最简单的凸函数是抛物线。

抛物线的例子。
可微分函数
一个可微的函数是任何给定的函数,它对它的每个点都有导数。
函数在 x 的导数是该函数在点(x,f(x))的图形的斜率。
我们来解开导数的概念。
线性函数的斜率是它上升或下降的速率,你可以认为它已经描述了那个函数的方向。但是这种直觉只对线性函数有效。
对于非线性函数,不同点的斜率可能不同。要找到非线性函数在特定点 p 的斜率,你需要找到 切线在 p 点的斜率。
函数在点 p 的切线是最接近该点函数的直线。
正切是一个近似值,因此,寻找特定点的斜率正切,就像目测该点的函数斜率[2]。

点 p (x,f(x))处的切线示例。
为了增加切线斜率的精确度,你需要找到点 p. 处割线的斜率
割线是逼近切线的一种更精确的方法。
割线仍然是一个近似值,但它更精确,因为它使用了两个点,切点和第二个点。

割线的斜率。
当选择的第二个点越接近切点时,近似效果就越好。
因此,为了找到函数 f 在切点 p = (x,f(x))处的精确斜率,当 x 的变化极限趋于零时,你就找到了割线。

如果极限存在,那就是函数 f 在点(x,f(x)) 的导数。

函数 f 在点 p 的导数。
但是你可能想知道,所有这些和梯度下降有什么关系?
到底什么是梯度?
函数的梯度是组织成向量【3】的所有偏导数的集合,用一个倒三角形表示,称为 nabla 。
在机器学习模型中,您可以将梯度视为偏导数的集合,每个偏导数都与模型的一个特征相关。

函数的梯度总是指向函数中增加最大的方向。如果你的函数是一座山,梯度总是想要到达顶峰。
要了解梯度如何总是朝着最大增加的方向发展,您可以用 Python 编写一个简单的测试。
定义一个二次函数,然后计算几次梯度。
def gradient_loop(runs=3):
*""" Repeatedly computes the gradient of a function
Computes the gradient given the starting points and then uses the result of the gradient to feed the next iteration, with new points.
Prints out the result of the function at each iteration
:param: runs: number of iterations to compute
"""* # starting points
x = np.array([1, 2, 3])
# quadratic function, a parabola
y = x**2
for run in range(0, runs):
print("Iter " + str(run) + ": Y=" + str(y)) # compute first derivative
x = np.gradient(y, 1) # update the function output
y = x ** 2 gradient_loop()
在这种情况下,函数 gradient_loop 取一条抛物线 y = x,和一些由 x 表示的点。然后,它计算函数在这些点上的梯度,并将结果作为下一次迭代的点集。
由于梯度总是想要到达山的顶峰,你可以看到函数值在每次迭代中增加。

梯度环路测试的输出。
如果你正在寻找一个函数的最大值,这将是完美的,但是在梯度下降中你想要找到最小值,你想要到达山的底部。
所以,在梯度下降中,你需要让梯度往反方向走[4]。
梯度下降算法
在非常高的层次上,或者在伪代码中,梯度下降遵循以下步骤:
- 在函数中选择一个随机点 w ,这是起点
- 当梯度没有收敛时:
2a。计算在 w 处的负梯度,梯度向相反方向。
2b。用 2a 的结果更新点 w ,并返回步骤 1。
3.成功,你已经找到了最小值。

梯度下降沿函数向下爬升,直到达到绝对最小值。
1。在功能中选择一个随机点
在初始步骤中,你只需要在函数中选取一个随机点,用行向量 w 表示。

2。而梯度还没有收敛
这是算法的迭代部分,想法是继续下去,直到你达到最小值。
但是你不会知道什么时候停下来。你运行这个算法的全部原因是为了找到最小值。
为了减轻这种情况,您设置了一个收敛阈值。
如果与前一次迭代相比,点 w 的新梯度变化没有超过您定义的收敛阈值,则算法已经收敛。你宣布你已经找到了这个函数的最小值。
因此,虽然梯度没有收敛,但在每次迭代中,你会收敛
- 计算 w 处的负梯度。
- 用 2a 的结果更新点 w ,并返回步骤 1。
从数学上来说,在每次迭代中,你运行一个更新规则,在这里你定义你要下降的下一个点,一个在函数中更靠下的点。

步骤 t+1 的梯度下降更新规则。
这就是为什么函数是凸的至关重要。如果函数不是凸的,你可能认为你已经找到了绝对最小值,但是你实际上是在一个局部最小值。

梯度下降出错的例子。在非凸函数中,梯度下降可能会找到局部最小值而不是函数的绝对最小值。
查看更新规则,有当前点 w ,梯度,然后有学习率α。
这个学习率α是多少?
学习率α是梯度下降在达到全局最小值之前一直采用的步长,它直接影响算法的性能。
学习率太大
当学习速度太快时,你就迈出了大步。你可以跨过最小值几次,然后来回几次。在大步长的情况下,梯度下降甚至可能永远不会达到最小值和收敛。
发现 alpha 太大的一个方法是当梯度不是在每一步都减小的时候。
学习率太小
当学习速度太慢时,你每次只能迈出很小的一步。算法最终会收敛,但这可能需要很长时间。

梯度下降的局限性
梯度下降是一个强大的算法。但是在数据集不断增长的现实世界中,它有两个主要限制:
- 计算整个数据集的导数是耗时的,
- 所需的内存与数据集的大小成正比。
梯度下降的更新规则应用于数据集中的所有数据点,这一切都在一个步骤中完成。随着数据集的增加,这种计算会变得更慢,因此收敛时间会增加。
这些计算通常在内存中完成,因此数据集越大,内存需求就越大[5]。
这就是随机梯度下降的用武之地!
随机梯度下降
考虑到梯度下降的局限性,随机梯度下降成为解决性能问题和加速大型数据集收敛的一种方法。
随机梯度下降是梯度下降的概率近似。这是一个近似值,因为在每一步中,算法都会计算随机选取的一个观察值的梯度,而不是计算整个数据集的梯度。

步骤 t+1 的随机梯度下降更新规则。
当数据集包含数百万个观察值时,这代表了显著的性能改进。
随机梯度下降的唯一条件是随机选取的观测值的期望值是函数在点 w 的次梯度。
与梯度下降法相比,随机梯度下降法速度更快,更适合大规模数据集。
但由于梯度不是针对整个数据集计算的,而是针对每次迭代中的一个随机点计算的,因此更新的方差更大。与梯度下降相比,这使得成本函数在每次迭代中波动更大,使得算法更难收敛。
随机梯度下降的新变化已被开发来解决这些问题。例如小批量随机梯度下降,通过在每次迭代中从数据集中选取一个 n 个观察值的样本而不是一个【5】来解决方差问题。
让我们看看随机梯度下降的作用!
预测比萨饼的烹饪时间
谈到完美的自制披萨,你和你的好朋友有不同的偏好。
最近,在你们的一次群聊中,有人开始了关于如何制作完美披萨的讨论,并暗示他们已经找到了自己最喜欢的食谱。
他们甚至可以根据不同的配料组合来确定烹饪时间,每次都能得到可预测的结果。
一段时间以来,你也一直试图完善你的食谱。但是,你可以用精确的烹饪时间制作你个人最喜欢的披萨,这个想法很有趣。
那么,为什么不利用你的数据科学技能来解决这个问题呢?
操作第一顺序,开始实验,记笔记!尽管你还不知道正确的比例,但有四样东西可以做出完美的披萨:
- 烤箱温度,
- 奶酪,
- 酱,
- 额外的配料。

制作完美自制披萨的要素。
回顾您详细的烹饪领域笔记,您的数据集如下所示:

你最近做的一些自制披萨实验。
你还假设烹饪时间和制作完美披萨的特征之间存在线性关系。因此,为了预测您的完美自制披萨的烹饪时间,您决定使用线性回归模型,并使用随机梯度下降来最小化成本函数普通最小二乘法。
ScikitLearn 有非常全面的关于随机梯度下降和不同可用函数的文档。还有足够的空间来调整不同的参数。
您将使用整个数据集来训练模型,然后预测披萨的烹饪时间,该披萨具有:
- 烤箱温度:510 华氏度,
- 50 克奶酪,
- 35g 酱,
- 10 克你喜欢的配料。
在模型适合数据集之前,您需要使用标准缩放器缩放您的要素。因为我们的目标是逐步达到函数的最小值,所以让所有的特征都处于相同的尺度有助于这个过程。它使得每个步骤在所有特征上具有相同的尺寸。
使用管线,保证在使用模型之前缩放所有特征。Python 中的管道用于链接应用于估计器的变换,在这种情况下是随机梯度下降。
import time
import numpy as np
from sklearn.linear_model import SGDRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScalerdef stochastic_gradient_descent(feature_array, target_array, to_predict, learn_rate_type="invscaling"):
*""" Computes Ordinary Least SquaresLinear Regression with Stochastic Gradient Descent as the optimization algorithm.
:param feature_array: array with all feature vectors used to train the model
:param target_array: array with all target vectors used to train the model
:param to_predict: feature vector that is not contained in the training set. Used to make a new prediction
:param learn_rate_type: algorithm used to set the learning rate at each iteration.
:return: Predicted cooking time for the vector to_predict and the R-squared of the model.
"""* # Pipeline of transformations to apply to an estimator. First applies Standard Scaling to the feature array.
# Then, when the model is fitting the data it runs Stochastic Gradient Descent as the optimization algorithm.
# The estimator is always the last element.
start_time = time.time()
linear_regression_pipeline = make_pipeline(StandardScaler(), SGDRegressor(learning_rate=learn_rate_type))
linear_regression_pipeline.fit(feature_array, target_array)
stop_time = time.time()
print("Total runtime: %.6fs" % (stop_time - start_time))
print("Algorithm used to set the learning rate: " + learn_rate_type)
print("Model Coeffiecients: " + str(linear_regression_pipeline[1].coef_))
print("Number of iterations: " + str(linear_regression_pipeline[1].n_iter_)) # Make a prediction for a feature vector not in the training set
prediction = np.round(linear_regression_pipeline.predict(to_predict), 0)[0]
print("Predicted cooking time: " + str(prediction) + " minutes") r_squared = np.round(linear_regression_pipeline.score(feature_array, target_array).reshape(-1, 1)[0][0], 2)
print("R-squared: " + str(r_squared))feature_array = [[500, 80, 30, 10],
[550, 75, 25, 0],
[475, 90, 35, 20],
[450, 80, 20,25],
[465, 75, 30, 0],
[525, 65, 40, 15],
[400, 85, 33, 0],
[500, 60, 30, 30],
[435, 45, 25, 0]]target_array = [17, 11, 21, 23, 22, 15, 25, 18, 16]
to_predict = [[510, 50, 35, 10]]stochastic_gradient_descent(feature_array, target_array, to_predict)
你的披萨需要 13 分钟才能做好,算法需要 249 次迭代才能找到最小值!

带有随机梯度下降的线性回归的输出,使用逆缩放选择学习率。
在回归的随机梯度下降的 Python 实现中,选择学习率的默认算法是逆缩放。
但是有许多选择学习率的方法,这直接影响算法的执行。
如何选择学习速度
根据问题和手头的数据集,可以使用不同的算法来选择学习率。
最简单的算法是选择一个常数,一个足够小的数,比如 10^-3.你开始使用它,并逐渐增加或减少它取决于如何快速或缓慢梯度下降找到最小值。
但是,理想情况下,你会希望有一个自动的方法来选择学习率。而不是基于常数并通过反复试验来调整。
例如,你可以使用一个自适应算法,它根据之前迭代的梯度来选择学习速率。例如,Adam[6] Adadelta[7]使用过去梯度的衰减平均值。
重新运行上面的代码,现在有了一个自适应的学习速率,只需要改变 learn_rate_type 参数。
stochastic_gradient_descent(feature_array, target_array, to_predict, learn_rate_type="adaptive")
你的披萨还是要 13 分钟才能做好,但是随机梯度下降找到最小值只需要 98 次迭代!

使用自适应算法选择学习率的随机梯度下降线性回归的输出。
预测的烹饪时间仍然相同,但有几处改进:
- r 平方较高,意味着模型更适合该数据集。
- 总运行时间低于使用反向缩放时的运行时间。
- 该模型收敛速度更快,只需要减少 60%的迭代次数。
在这个小数据集中,运行时间和迭代次数的差异是很小的,实际上可以忽略不计。
但是你可以想象在一个拥有数千个特征和数百万个观测值的数据集中改变学习率算法的影响!
你刚刚用随机梯度下降法模拟了你最喜欢的披萨食谱的烹饪时间!
这是一个简单的例子,但我希望你喜欢它!现在,您对梯度下降的威力以及随机梯度下降如何更好地解决现实世界的大规模问题有了更好的理解。
感谢阅读!
参考
[1]莱马里查尔,C. (2012 年)。 柯西与梯度法 。Doc Math Extra:251–254
[2] Larson,R. & Edwards B. (2006) 微积分:一种应用方法(第 7 版。)霍顿·米夫林
,可汗书院
[4]沙莱夫-施瓦茨和本-戴维(2014 年)。理解机器学习:从理论到算法。剑桥:剑桥大学出版社。
[5]鲁德,S. (2017)。梯度下降优化算法概述。arXiv 预印本 arXiv:1609.04747
[6] Diederik P. Kingma 和 Jimmy Ba (2017) 亚当:随机优化的一种方法 arXiv 预印本 arXiv:1412.6980
[7]泽勒,马修·d .(2012)阿达德尔塔:一种自适应学习率方法。arXiv 预印本 arXiv:1212.5701。
Rie Johnson 和张彤。2013.使用预测方差缩减加速随机梯度下降。第 26 届神经信息处理系统国际会议论文集第 1 卷(NIPS'13)。
图片由作者提供,除非另有说明。
随机梯度下降:从头开始的解释和完整实现

照片由 Unsplash 上的 Allef Vinicius 拍摄
使用单个感知器
随机梯度下降是机器学习和深度学习中广泛使用的方法。这篇文章解释了随机梯度下降使用一个单一的感知器,使用著名的虹膜数据集。我假设你已经知道梯度下降的基本知识。如果你需要复习,请查看这个线性回归教程,它用一个简单的机器学习问题解释了梯度下降。
什么是随机梯度下降?
在深入研究随机梯度下降之前,让我们先了解一下规则梯度下降。梯度下降是一种迭代算法。我们来举个简单的例子。正如我提到的,我将使用一个单一的感知器:

下面是简单的线性公式:
Y = WX+ W0
这里 W0 是偏置项。如果有多个特征:
Y = W1X1 + W2X2 + … WnXn + W0
使用此公式,让我们一步一步地检查如何执行梯度下降算法:
1.首先,随机初始化 Ws。
2.使用这个随机 Ws 计算预测输出 Y_hat。
3.取 Y 和 Y_hat 的均方根误差。
4.使用以下公式计算更新后的 Ws:W = W—步长*均方根的导数,其中需要选择步长。
5.不断重复步骤 2 到 4,直到收敛。
在这个过程中,我们在每次迭代中使用整个数据集。如果你只有一百个数据点,那就可以了。但是,如果你有 10,000 或 100,000 个数据点,你必须运行 200 次迭代来收敛,计算变得缓慢和昂贵。为了解决这个问题,可以为每次迭代选择一个随机数据点,而不是使用所有 100,000 个数据。计算将变得非常快速和简单。这叫做随机梯度下降。
除了选择一个数据点,我们还可以选择一个小批量的数据点,如 10/15 或 20 个数据点。我稍后将展示的例子将在每次迭代中使用 12 个数据点。
数据准备
首先从 sklearn 库中加载 iris 数据集:
from sklearn.datasets import load_iris
iris = load_iris()
这个虹膜数据集是字典格式的。特征在键“数据”中,目标在键“目标”中。这是所有的钥匙:
list(iris.keys())
输出:
['data',
'target',
'frame',
'target_names',
'DESCR',
'feature_names',
'filename']
我们需要前两个键:数据和目标。我喜欢使用数据框架。因此,我将把数据转换成数据帧:
import pandas as pd
ir = pd.DataFrame(iris['data'])
ir['Species'] = pd.DataFrame(iris['target'])
ir
输出:

在物种栏里,只有数字。但是这些数字代表了鸢尾花的种类。iris 数据集中的 Target_names 键显示了它们。
ir['Species'] = ir['Species'].replace({0: "setosa", 1: "versicolor", 2: 'virginica'})
我只是把物种栏里的数字换成了真正的物种名称。有三种不同的物种。
我的目标是演示随机梯度下降。所以,我想做一个简单的二元分类。因此,我将“setosa”作为阳性类,其余的物种作为阴性类。这意味着。这个分类器会告诉我们一朵花是不是“setosa”。这里,我们需要将“setosa”更改为 1,将其余的物种更改为 0。
**for** i **in** range(len(ir['Species'])):
**if** ir['Species'][i] == 'setosa':
ir['Species'][i] = 1
**else**:
ir['Species'][i] = 0
ir['Species']
输出:
0 1
1 1
2 1
3 1
4 1
..
145 0
146 0
147 0
148 0
149 0
Name: Species, Length: 150, dtype: object
数据集准备好了!现在好戏开始了。
使用随机梯度下降方法开发分类器
首先,我们将使用 sklearn 库中的 train_test_split 函数将数据集分离为训练集和测试集。在此之前,需要将特征和目标分开。
from sklearn.model_selection import train_test_splitir_features = ir.drop(columns = 'Species')
ir_label = ir['Species']x_train, x_test, y_train, y_test = train_test_split(
ir_features, ir_label,
test_size = 0.2,
random_state = 10
)
现在,我们分别设置了培训和测试。正如我之前解释的,我们将为每次迭代随机选择一些数据点。我将组合 x_train 和 y_train,因为我们也需要 y_train 进行训练,
x_train['Species'] = y_train
df = x_train
下面是为每次迭代从训练集中随机选择 12 个数据点的函数。在原始的 iris 数据集中,有 50 个‘setosa’和 100 个其他物种。这意味着有 50 个正类和 100 个负类数据。样品应该符合那个比例。所以,我们从正类中取 4 个数据,从负类中取 8 个数据。
def stratified_spl(df):
df1 = df[df['Species'] == 1]
df0 = df[df['Species'] == 0]
df1_spl = df1.sample(n=4)
df0_spl = df0.sample(n=8)
return pd.concat([df1_spl, df0_spl])
我将在这里使用 sigmoid 激活函数:
def hypothesis(X, w):
z = np.dot(w, X.T)
return 1/(1+np.exp(-(z)))
下一个函数将为训练目的再次分离 12 个数据的小型训练集的特征和目标。
正如你在上面的线性公式中看到的,我们需要一个偏差 W0。1 的额外特性是作为偏置项添加的。我们将在每次迭代中改进偏置项:
def xy(df):
df_features = df.drop(columns = 'Species')
df_label = df['Species']
df_features['00'] = [1]*12
return df_features, df_label
让我们定义误差函数来计算均方误差(MSE)。该函数将首先计算预测目标,然后计算 MSE:
def error(X, y, w):
n = len(X)
yp = hypothesis(X, w)
return np.sum((yp-y)**2)/n
所有功能都准备好了。现在梯度下降函数。在每次迭代中,
- 它将使用之前定义的 layered _ spl 函数对 12 个数据点进行采样。
- 然后将特征和目标分割为 X 和 y。
- 使用 Ws 和 Xs 计算预测的 y。
- 使用梯度下降公式更新 Ws。
在分类问题中,通常使用对数来正则化误差项。但是我这里用 MSE。因为我猜更多的人熟悉 MSE。这个数据集很容易使用,我们为这个项目设计了 MSE。
请随意尝试用日志记录错误术语。这里有一个例子:
我们将收集每次迭代和 Ws 中的错误。
def grad_des(df, w, alpha, epoch):
j = []
w1 = []
w1.append(w)
for i in range(epoch):
d = stratified_spl(df)
X, y = xy(d)
n= len(X)
yp = hypothesis(X, w)
for i in range(4):
w[i] -= (alpha/n) * np.sum(-2*X[i]*(y-yp))
w[4] -= (alpha/n) *np.sum(-2*(y-yp))
w1.append(list(w))
j.append(error(X, y, w))
return j, w1
分类器完成了!现在,它需要被测试。
测试分类器
我会随机初始化 Ws。包括偏差在内共有五个特征。所以,对于每个特征或 X,我需要初始化一个 W,就像你在开始看到的线性公式一样。
import numpy as np
w = np.random.rand(5)
w
输出:
array([0.05837806, 0.91017305, 0.71097702, 0.91990355, 0.71139191])
现在,使用梯度下降函数,步长(alpha)为 0.01,迭代 100 次:
j, w1 = grad_des(x_train, w, 0.01, 100)
对于 100 次迭代,这将为我们提供 100 个 w1。使用这 100 个 w1,我们可以计算 100 个 MSE,以观察每次迭代中 MSE 的变化。我需要一个函数,应该计算每个 w1 的预测 y,然后误差。
def err_test(X, y, w):
er = []
for i in range(len(w1)):
er.append(error(X, y, w[i]))
return er
在每次迭代中看到 MSE 图是很好的。
下面是绘图函数:
def plot(X, y, w):
error = err_test(X, y, w)
return plt.scatter(range(len(error)), error)
记得我们在 x_train 中添加了目标。因此,将目标从 x_train 中分离出来,并添加偏差项:
X = x_train.drop(columns = 'Species')
X['00'] = [1]*len(X)
现在,绘制训练集的 MSE:
import matplotlib.pyplot as plt
plot(X, y_train, w1)

让我们在测试集中添加一个偏差项。
X_t['00'] = [1]*len(x_test)
现在,绘制测试数据的 MSE:
plot(X_t, y_test, w1)

这个数据中发生的一个不寻常的事情是第一个 MSE 太低。那通常不会发生。通常情况下,第一个 MSE 太高,在第一个 MSE 之后,它会逐渐下降,如图所示。我找不到原因。
不看准确性,绩效评估是不完整的。让我们为精确度定义一个函数:
def accuracy(X, y, w):
yp = hypothesis(X, w)
for i in range(len(yp)):
if yp[i] >=0.5:
yp[i] = 1
else:
yp[i] = 0
return sum(yp == y)/len(y)
现在,我们想看看精确度如何随着每次迭代而变化:
def accuracy_series(X, y, w1):
acc = []
for i in range(len(w1)):
acc.append(accuracy(X, y, w1[i]))
return acc
使用此函数查看训练集的精确度系列:
np.array(accuracy_series(X, y_train, w1))
输出:
array([0.975, 0.34166667, 0.34166667, 0.34166667, 0.34166667,
0.34166667, 0.34166667, 0.34166667, 0.33333333, 0.23333333,
0.34166667, 0.5 , 0.6 , 0.63333333, 0.64166667,
0.65833333, 0.65833333, 0.65833333, 0.65833333, 0.65833333,
0.65833333, 0.65833333, 0.65833333, 0.65833333, 0.65833333,
0.65833333, 0.65833333, 0.65833333, 0.65833333, 0.65833333,
0.65833333, 0.65833333, 0.65833333, 0.65833333, 0.65833333,
0.66666667, 0.66666667, 0.66666667, 0.66666667, 0.66666667,
0.66666667, 0.675 , 0.69166667, 0.69166667, 0.69166667,
0.69166667, 0.71666667, 0.74166667, 0.75 , 0.76666667,
0.76666667, 0.78333333, 0.78333333, 0.83333333, 0.83333333,
0.83333333, 0.83333333, 0.85833333, 0.86666667, 0.83333333,
0.85 , 0.88333333, 0.88333333, 0.88333333, 0.90833333,
0.90833333, 0.90833333, 0.91666667, 0.925 , 0.925 ,
0.925 , 0.925 , 0.925 , 0.94166667, 0.94166667,
0.94166667, 0.95833333, 0.95833333, 0.96666667, 0.98333333,
0.98333333, 0.98333333, 0.98333333, 0.98333333, 0.98333333,
0.98333333, 0.99166667, 0.99166667, 0.99166667, 0.99166667,
0.99166667, 0.99166667, 0.99166667, 0.99166667, 0.99166667,
0.99166667, 0.99166667, 0.99166667, 0.99166667, 0.99166667,
0.99166667])
又来了。用第一个 w 获得 99%的准确性是不寻常的,因为第一个 w 是随机启动的 w,这在现实生活的项目中不会发生。
现在,测试集的精度系列:
np.array(accuracy_series(X_t, y_test, w1))
输出:
array([.93333333 , 0.3 , 0.3 , 0.3 , 0.3 ,
0.3 , 0.3 , 0.3 , 0.3 , 0.16666667,
0.33333333, 0.53333333, 0.6 , 0.63333333, 0.63333333,
0.7 , 0.7 , 0.7 , 0.7 , 0.7 ,
0.7 , 0.7 , 0.7 , 0.7 , 0.7 ,
0.7 , 0.7 , 0.7 , 0.7 , 0.7 ,
0.7 , 0.7 , 0.7 , 0.7 , 0.7 ,
0.7 , 0.7 , 0.7 , 0.7 , 0.7 ,
0.7 , 0.7 , 0.7 , 0.7 , 0.7 ,
0.7 , 0.73333333, 0.73333333, 0.73333333, 0.83333333,
0.83333333, 0.83333333, 0.83333333, 0.83333333, 0.86666667,
0.86666667, 0.86666667, 0.86666667, 0.86666667, 0.86666667,
0.86666667, 0.86666667, 0.86666667, 0.86666667, 0.9 ,
0.9 , 0.9 , 0.9 , 0.93333333, 0.93333333,
0.93333333, 0.96666667, 0.96666667, 0.96666667, 0.96666667,
0.96666667, 0.96666667, 0.96666667, 0.96666667, 0.96666667,
0.96666667, 0.96666667, 0.96666667, 0.96666667, 0.96666667,
0.96666667, 0.96666667, 0.96666667, 0.96666667, 0.96666667,
0.96666667, 1\. , 1\. , 1\. , 1\. ,
1\. , 1\. , 1\. , 1\. , 1\. ,
1\. ])
你可以看到最后准确率变成了 100%。
结论
我的目标是演示随机梯度下降的实现。我希望那是成功的。如果你很难理解,我的建议是自己运行代码。会更清晰。你可以看到,为什么随机梯度下降如此受欢迎。因为我在每次迭代中使用了 12 个数据,而不是使用数据集的所有 150 个数据。当数据集中有 100,000 个数据点时,可以使用相同的技术。这将节省大量的时间和计算成本。
欢迎在推特上关注我,喜欢我的 T2 脸书页面。
更多阅读
随机鹦鹉论文:对未来危害的警告
了解紧迫的问题和当前的缓解措施,以减少 NLP 研究中的偏见和公平性

在过去的三年里,在自然语言处理(NLP)领域推出了 Transformer s 之后,已经有了无数的突破。此外,关于 NLP 研究和人工智能技术进步的副产品所造成的危害,有越来越多的意识、讨论和辩论。NLP 伦理领域的最后一年以有争议的解雇而结束,新的一年以最令人期待的随机鹦鹉论文的发表而开始,该论文由 Timnit Gebru 等人在 facct 2021 上发表。我感谢作者们为触发警报所做的努力。
文章发人深省。我看过很多关于这篇论文的正反讨论[ 1 、 2 、 3 、 4 。这篇有争议的文章中有什么 post 很好地总结了这篇文章,但没有表明立场。发表偏倚是指倾向于只发表积极的和有意义的结果。我相信这篇论文是克服发表偏倚的一个例子。这引发了学术界和企业界关于技术危害及其对社会影响的讨论。在过去的几个月里,我一直在研究自然语言处理和负责任的人工智能研究中的人工智能伦理、偏见和公平。本文旨在提出本文讨论的一些迫切问题。总结当前的研究成果,以减轻或揭示大型语言模型的问题。
Gebru 等人讨论了大型语言模型(LLM)对环境的影响、财务成本、与从互联网上公开可用的免费文本中获得的大型训练数据相关的风险、对边缘社区造成的危害以及该领域研究方向的问题。本文还提供了一些缓解这些问题的建议。讨论的危害对所有尺寸的 LMs 都是真实的。LLM 展示了人类在技术发展中所能达到的顶峰。自 LLMs 开发以来,在应用开发方面取得了令人印象深刻的工程成就,一些值得注意的应用是 DALL-E 和 Clip 。作为一名研究人员,我总是被研究突破所感动。我们正试图打破导致创新应用开发的限制。然而,本文中提出的问题是为了反映底线,即在我们创建 LLM 之前,我们是否可以做更多的研究来了解和减轻当前可用的 LM 所造成的危害和问题?
一些个人想法
环境和财务成本
LM 模型的训练会导致巨大的财务和环境成本。环境关注点适用于许多新兴技术。作者提到,“在 GPU 上训练一个单一的 BERT 基本模型(没有超参数调整)估计需要的能量相当于一次穿越美国的飞行。”也许作者提供的关于环境成本的比较值得商榷。然而,真正要问的问题是:我们正在做什么来解决这些新兴技术带来的环境问题?
世界上存在着经济和技术差距。培训 LLM 会产生巨大的财务成本。谁在训练这些 LLM 模型?谁将从应用程序中获益最多?
大型语言模型(LLM)中使用的训练数据
读完这篇论文后,我发现主要的和令人担忧的问题是现代逻辑推理小说如何对霸权文本进行编码。用于训练 GPT-2 的文本是通过抓取 Reddit 出站链接收集的,每个人都知道,Reddit 中属于特定年龄、阶级和种族的男性人数过多。当我们用有偏见的基于网络的训练数据开发应用程序时,会反映出什么类型的意识形态?最近的研究显示,LLMs 中持续存在着刻板印象、性别和反穆斯林偏见。我们需要更加关注使用主流文本作为不完全代表世界的训练数据。互联网上用户生成文本的人口统计数据是多少?这就把我们带到了下一个问题。人工智能模型应该对世界进行编码,还是应该更好?谁来决定?
使用公开数据的另一个主要问题是隐私问题,因为数据集可能包含个人信息。研究人员已经表明,可以从 LLMs [ 8 ]中提取训练数据。自从数字营销兴起以来,数据隐私一直是最紧迫的问题。我们将如何解决人工智能隐私问题?与开发新技术相比,目前有多少量化和解决隐私问题的努力正在进行中?
静态快照 LLMs
提出的另一个要点是,LLM 是时间的静态快照,因为鉴于成本较高,重新培训 LLM 的机会较少。如论文所述,LLM 是随机鹦鹉。近年来,在黑人的命也是命、模仿运动以及负责任的人工智能和人工智能伦理方面已经做了大量工作,这些工作将不会在这些 LLM 中反映出来。作者讨论了谁会从进步中受益,谁会受到伤害,因为技术的使用还没有达到社会的大部分。你认为伯特知道新冠肺炎吗?静态快照将如何描绘动态世界?
我们如何校准 LLM?
主要问题“语言模型会不会太大?”这让我想到我的孩子掌握语言的能力。他的词汇量有限,因为他还在学习这门语言。对于一个 6 岁的孩子来说,许多事情都没有意义,因为大脑整合正在发展中。他有时会在错误的地方用词,这些错误会慢慢帮助他下次改正。我想到了一个思维实验。如果给一个 5-6 岁的孩子看来自维基百科、Reddit、YouTube 和脸书视频的所有数据。结果会是什么?在我看来,这将是一个更大的语言模型。前几天,他从一个儿童电视节目中学到了一个 F 字。他用了这个词,我必须告诉他这个词在孩子的语言中是什么意思,以及为什么不用它。然而,成人使用被认为是正常的。什么是好什么是坏是很主观的。但成长是一个学习、观察、互动和犯错的过程,最终会在成年后校准我们的系统。
我们在训练这些不同大小的语言模型时,是否理解了这些模型可能犯的错误?我们要如何校准这些 LLMs 和 AI 系统?然而,实践经验(使用 LMs 构建技术)可能会发现隐藏的危害,犯错误和改正错误将有助于我们开发更好更公平的技术。我们可以用现有的 LM 来做这件事吗?
LLMs 中最近的研究偏差和公平性
去年,来自 OpenAI、斯坦福以人为中心的人工智能研究所和其他大学的研究人员聚集在一起,讨论围绕最大的语言模型“生成性预训练变形金刚 3”(GPT-3)的开放式研究问题。LLMs 开发在推动开始询问如何创建更符合伦理的语言模型的努力中发挥了重要作用?在开发这些算法时,需要在为 LM 开发受控文本生成、如何评估算法、如何评估偏差、如何评估数据集、算法审计、和全包团队的方向上进行研究,并在追求研究方向时仔细权衡这些风险。这篇论文发出的警报引起了人们对 LLM 危害研究的关注。在最近的一篇论文中, Creolizing the web 提出了一种理解来自 Reddit 的真实世界数据集的方法,以识别社区和观点的回音室。我们需要像这样的技术来评估大型数据集,然后才能训练更大的模型。最有希望的方法是用人在回路框架(HITL )校准人工智能系统。从研究努力的方向来思考是很重要的。通过提问。与新技术的发展相比,目前有多少研究工作是在减轻或减少技术上的偏见和公平?
总之,新技术的影响是不可预测的,我们无法预见它将如何改变世界。我们应该把这篇论文看作是向提出问题和提供解决方案的建议迈出的一步。现在是时候提出更多问题并找到答案,以创造一个公平的技术,创造一个更美好的世界。让我们用所有的镜头,努力寻找有意义的未来方向。
感谢 Suhas Pai 的观点、讨论和对本文写作的帮助。
用随机过程看未来
包含 R 代码的入门指南

你们中的许多人可能以前没有听说过随机过程,想知道它们与你们有什么关系。首先,统计学家可能会发现随机过程是建模概率事件的好方法。此外,那些对强化学习感兴趣的人可能会发现,这些信息巩固了他们对马尔可夫链等强化学习概念的理解。最后,本文简短易懂,所以如果你对随机过程本身感兴趣,那么这是一个很好的介绍。
以下教程的读者应该知道矩阵运算,如乘法,以及线性代数的基本知识。为了简洁起见,许多线性代数概念在本教程中没有解释。本文为感兴趣的人提供了 R 代码,但是没有必要在阅读本文时遵循这些代码。
基础和定义
你们中的许多人可能会有的第一个问题是随机过程的确切性质。“随机”一词的字面意思是“随机的”,尽管随机过程不一定是随机的:事实上,它们可以是完全确定的。简单地说,随机过程是可以用一族随机变量建模的任何数学过程。抛硬币是一个很好的例子,因为它很简单。我们从硬币平视开始,然后恰好翻转一次。硬币正面着地的概率是 0.5,反面着地的概率是 0.5。如果硬币正面朝上,世界的“状态”不变,而如果硬币正面朝下,世界的“状态”就变了。这可以用下面的矩阵来建模:

简单的转移矩阵。
或者,更直观的方法:

同一个矩阵,这次是一个有向图。
行代表状态,转换到其他状态的概率在列中。如果你处于状态 0(正面)并掷硬币,你最终处于状态反面(状态 1)的机会可以在上面矩阵的(0,1)项中看到(. 5)。
再举一个例子,假设您在一个库中有两台打印机。当两台打印机都工作时,每天有 30%的几率其中一台坏掉,而两台都坏掉的几率为 0%。如果一台打印机坏了,那么有 20%的几率会有修理工在那天来修理,有 40%的几率第二台打印机会坏。当两个都坏了的时候,有百分之百的可能会有修理工来修理两台打印机。代表该流程的矩阵如下所示:

转换矩阵。请注意,索引从 0 开始。
请注意,因为(0,1) = .3 和(0,2) = 0,所以(0,0) = .7。每一行的总和必须为 1,因为它们是由离散的概率分布填充的。这同样适用于 entry (1,1)。注意,最后一行确定性地从状态 2 变为状态 0。为了确保您理解,请回答以下问题:
1.(0,1)处的条目代表什么?
2.如果修理工用 P = .7 修好了两台打印机,但有时当两台打印机都用 P = .1 坏了时,他只有时间去修一台打印机,那么新矩阵会是什么样子?
马尔可夫性质
现在我们已经看到了两个简单的例子,让我们来谈谈许多随机过程的标志性性质:马尔可夫性质。当知道过程的当前状态就像知道整个历史一样好时,一个系统或变量被称为马尔可夫的。在前面的例子中,如果我们将随机变量 X 定义为在过程的第 n 步运行的打印机的当前数量(小写 X 表示某一步的实际值),那么知道 X 就足以给出 Xn+1 的分布。简单来说:

从数学上讲,这个等式说明 Xn+1 只取决于 Xn 的值。
直觉上,知道两台打印机都在工作,就足以知道所有未来状态的概率。在前一个状态或前一个状态中有多少台打印机坏了并不重要。抛硬币是另一个例子:抛硬币正面朝上的几率与之前任何一次抛硬币的结果完全无关。马尔可夫过程被称为“无记忆的”,因为它们状态的历史是不相关的。只有当前状态才重要(虽然,不一定。在掷硬币的例子中,你甚至不需要知道当前的状态是 P(正面)= .5,P(反面)= .5。在强化学习的例子中,想法是指定一个具有某种概率分布的最优移动,许多棋盘游戏可以被认为是马尔可夫的。国际象棋就是这样一种游戏,因为选择最佳走法不受当前状态之前的准确走法顺序的影响。虽然也有一些例外,比如顺带规则和是否有任何一方失败,但这些都可以编码到当前状态中,这就形成了马尔可夫链。
预测未来🔮
现在我们已经讨论了 Markov 属性,让我们回到打印机的例子。请注意,计算 P(Xn = 1 | Xn-1 = 0)(假定链当前处于状态 0,链变为 1 的概率)是很简单的,因为它是矩阵中的(0,1)项。但是如果我们想提前两步计算机会呢?要做到这一点,我们需要对从 0 开始并导致 1 的所有可能的两步跃迁求和:

为什么会这样呢?这个概率是每一个可能导致状态的步骤的概率总和。从状态 n-2 开始,有三种方式前进到 n-1(矩阵的行 0 中的每个值)。在 n-1 的所有可能值中,第 1 行包含导致 1 的转移概率。
您可能已经注意到,这实际上相当于第一行和第二列的点积。事实上正是如此!事实上,如果我们对每个条目都这样做,那么我们可以得到所有 P(i,j)的两步超前概率。
让我们用 R 来求矩阵的平方:
matrixpower <- function(mat,k) {
if (k == 0) return (diag(dim(mat)[1]))
if (k == 1) return(mat)
if (k > 1) return( mat %*% matrixpower(mat, k-1))
} # matrix power function from Introduction to Stochastic Processes with R by Robert Dobrowprinters <- matrix(c(.7, .2, 1, .3, .4, 0, 0, .4, 0), nrow = 3, ncol = 3) # make the matrixprinters [,0] [,1] [,2]
[0,] 0.7 0.3 0.0
[1,] 0.2 0.4 0.4
[2,] 1.0 0.0 0.0matrixpower(printers, 2) [,0] [,1] [,2]
[0,] 0.55 0.33 0.12
[1,] 0.62 0.22 0.16
[2,] 0.70 0.30 0.00# Note that I've manually changed the indexing to start from 0, as that's the typical way of indexing matrices for stochastic processes. R indexing, however, starts from 1\. I did this for the sake of consistency. You only need to keep this in mind should you explore this in you own R Notebook
为了得到三步概率,我们再做一次:
matrixpower(printers, 3) [,0] [,1] [,2]
[0,] 0.571 0.297 0.132
[1,] 0.638 0.274 0.088
[2,] 0.550 0.330 0.120
它适用于任何提前 n 步的概率:
matrixpower(printers, 6)
[,0] [,1] [,2]
[0,] 0.588127 0.294525 0.117348
[1,] 0.587510 0.293602 0.118888
[2,] 0.590590 0.293370 0.116040
因此,假设我们从 0 台损坏的打印机开始,在链中的 6 个步骤之后,我们有 2 台损坏的打印机的概率是 M6(0,2) = .117。
使用这种方法,我们可以计算出一种叫做不变量或平稳分布的东西。不变分布描述了处于任何状态的长期概率,以及处于该状态的时间比例。既然这个概率是‘长期’的,那么起始状态就无关紧要了。因此,长期概率分布的列应该都具有相同的值。用π表示的这种分布的矩阵对于 3×3 矩阵采取以下形式,这可以推广到 n×n:

不变概率分布。
你可以通过将一个转移矩阵提升到一个非常大的幂来很容易地找到这一点。更正式地说:

matrixpower(printers, 100)
[,0] [,1] [,2]
[0,] 0.5882353 0.2941176 0.1176471
[1,] 0.5882353 0.2941176 0.1176471
[2,] 0.5882353 0.2941176 0.1176471
或者你可以用一个方程组来求解。该系统可以仅使用 2 个事实来构建:首先,行必须加和为 1(这在直觉上必须是真实的,给定π值的实际解释);其次,πn 必须等于其他长期概率乘以它们的转移概率,从而得出πn。在打印机示例中:

请随意使用计算出的不变分布来验证这些方程是否成立。
沟通很重要💬
然而,重要的是要注意,在某些情况下,极限概率分布并不以所描述的形式存在。为了理解这种情况,我们必须定义马尔可夫过程的可约性。可归约性与马尔可夫链的状态连接方式有关-当且仅当 A 可从 B and B 到达且 A 可从 A 到达时,状态 A 和 B 才能通信。通信类由一组相互通信的状态组成(见下图)。

具有两个通信类的马尔可夫链。

我们说,如果马尔可夫链由单个通信类组成,那么它就是不可约的。如果不是这样,那么我们说链是可约的,π不存在上面的形式。然而,仍然有一个有限的分布。
reducible <- matrix(c(0, 0, 0, 0, 0, .4, .9, .7, 0, 0, 0, .1, .3, 0, 0, .6, 0, 0, .5, .5, 0, 0, 0, .5, .5), nrow = 5, ncol = 5)reducible [,0] [,1] [,2] [,3] [,4]
[0,] 0 0.4 0.0 0.6 0.0
[1,] 0 0.9 0.1 0.0 0.0
[2,] 0 0.7 0.3 0.0 0.0
[3,] 0 0.0 0.0 0.5 0.5
[4,] 0 0.0 0.0 0.5 0.5
在上面的场景中,状态 0 转到不与其他类通信的 2 个循环中的 1 个。因此,假设我们从 start 0 开始,我们将导航到状态 1 或 3,然后在这两个状态之间永久转换。在这种情况下,极限分布是什么样的?
matrixpower(reducible, 100)
[,0] [,1] [,2] [,3] [,4]
[0,] 0 0.350 0.050 0.3 0.3
[1,] 0 0.875 0.125 0.0 0.0
[2,] 0 0.875 0.125 0.0 0.0
[3,] 0 0.000 0.000 0.5 0.5
[4,] 0 0.000 0.000 0.5 0.5
我们看到矩阵中的两个方块看起来像极限概率分布。事实上,他们是。尝试验证这些单独的分布是否正确。状态 0 很有趣,因为它将被恰好访问一次,然后就没有返回的路了。有人可能会认为这意味着π0 = 0,这正是我们所看到的。这表明处于状态 0 的长期概率为 0,也就是说状态是瞬态。瞬态也可以被定义为具有有限数量的预期访问的状态,而循环状态则相反,具有无限数量的预期访问。
还要注意,第 0 行中的概率与单个循环中的长期分布不匹配。在前面的例子中,各个列中的所有概率都是相同的,因为开始状态与长期行为无关(因为任何状态最终都可以从任何其他状态到达)。然而,现在开始状态是相关的,因为链现在有 3 个通信类。如果我们从状态 2 开始,那么就不可能到达状态 4。然而,如果我们从状态 0 开始,那么任何状态都可以达到,但是状态 3 和 4 总体上更有可能,因为有 0.6 的一次性机会永远进入该链,而从状态 1 达到状态 1 和 2 的机会只有 0.4。(当从状态 0 开始时,对所有其他状态的预期访问次数仍然是无穷大,因为任何分数乘以无穷大仍然是无穷大)。
随机漫步

了解了瞬态在长期未来的表现后,我们可以参观一个经典的随机过程:随机漫步。
在这个例子中,一个喝醉的人正试图回家。4 号州是他家。如果他回到家,他就睡觉,不离开。状态 0 是海洋。如果他碰巧在海洋中跌倒,他也不会离开(可怜的家伙)。因为他有点不在状态,当他处于两个状态之间时,他以 0.5 的概率向两个方向移动。下面是显示他运动的矩阵:
random_walk <- matrix(c(1, .5, 0, 0, 0, 0, 0, .5, 0, 0, 0, .5, 0, .5, 0, 0, 0, .5, 0, 0, 0, 0, 0, .5, 1), nrow = 5, ncol = 5)random_walk [,0] [,1] [,2] [,3] [,4]
[0,] 1.0 0.0 0.0 0.0 0.0
[1,] 0.5 0.0 0.5 0.0 0.0
[2,] 0.0 0.5 0.0 0.5 0.0
[3,] 0.0 0.0 0.5 0.0 0.5
[4,] 0.0 0.0 0.0 0.0 1.0
上面的矩阵是具有吸收边界的随机游走。状态 1 和状态 4 被认为是吸引人的,因为它们只与自己交流,因此一旦到达它们,你就永远不会离开。应该清楚的是,边界是循环的,而中间状态是短暂的。
如果我们将矩阵提升到 100 次方,我们会得到以下结果:
matrixpower(random_walk, 100)
[,0] [,1] [,2] [,3] [,4]
[0,] 1.00 0.000000e+00 0.000000e+00 0.000000e+00 0.00
[1,] 0.75 4.440892e-16 0.000000e+00 4.440892e-16 0.25
[2,] 0.50 0.000000e+00 8.881784e-16 0.000000e+00 0.50
[3,] 0.25 4.440892e-16 0.000000e+00 4.440892e-16 0.75
[4,] 0.00 0.000000e+00 0.000000e+00 0.000000e+00 1.00
注意中间的所有状态都有 P ~ 0(短暂的标志)。我们还看到长期概率根据矩阵的行而变化。同样,这是因为起始状态很重要,只是现在有多个起始状态。我们看到,从状态 1 或状态 4 开始,只会分别导致状态 1 和状态 4。从状态 1 开始,有 75%的机会掉进海里,有 25%的机会回家。正如我们可能预期的,中间状态,状态 2,以相等的概率导致任一吸收状态。
如果醉汉稍微清醒了一点,现在有 0.75 的机会走向他的家,有 0.25 的机会走向大海,会怎么样?
random_walk_2 <- matrix(c(1, .25, 0, 0, 0, 0, 0, .25, 0, 0, 0, .75, 0, .25, 0, 0, 0, .75, 0, 0, 0, 0, 0, .75, 1), nrow = 5, ncol = 5)random_walk_2 [,0] [,1] [,2] [,3] [,4]
[0,] 1.00 0.00 0.00 0.00 0.00
[1,] 0.25 0.00 0.75 0.00 0.00
[2,] 0.00 0.25 0.00 0.75 0.00
[3,] 0.00 0.00 0.25 0.00 0.75
[4,] 0.00 0.00 0.00 0.00 1.00matrixpower(random_walk_2, 100) [,1] [,2] [,3] [,4] [,5]
[1,] 1.000 0.000000e+00 0.000000e+00 0.000000e+00 0.000
[2,] 0.325 2.514973e-22 0.000000e+00 7.544920e-22 0.675
[3,] 0.100 0.000000e+00 5.029947e-22 0.000000e+00 0.900
[4,] 0.025 8.383245e-23 0.000000e+00 2.514973e-22 0.975
[5,] 0.000 0.000000e+00 0.000000e+00 0.000000e+00 1.000
正如我们所料,成功回家的几率大大增加。
感谢您阅读这篇文章。有任何问题请联系我。当然,还有更多的内容需要介绍,但是对于任何想要快速了解概念并通过一些简单的例子来巩固知识的人来说,这是一个良好的开端。如果你喜欢读这篇文章,那么我鼓励你看看我关于这个主题的第二篇文章:马尔可夫链的魔术。
延伸阅读:
随机过程导论。一
带 R 的随机过程导论。2 和 3
萨顿和巴尔托的《强化学习导论》。2 和 3
Python 中的随机波动率定价
蒙特卡罗模拟高级证券定价

证券定价
证券定价有多种方法。我认为这个话题既是一门科学,也是一门艺术。本文讨论了使用模拟(主要以随机输入的形式)为金融工具定价的优势。如果你对证券定价不熟悉,我建议你阅读以下文章……
- 随机过程的简明介绍
- Black-Scholes 模型中使用的主要随机过程
- 推导 Black-Scholes 模型的随机积分的计算
- 逐步推导出臭名昭著的欧式期权定价模型
这些文章将为使用封闭式解决方案对特定证券进行定价提供坚实的基础。之后,为了掌握基本的模拟定价,请查看以下文章…
- 模拟资产价格路径为金融工具定价
- 专门针对特殊工具的高级模拟定价
对这些不同定价方法的深刻理解将反映出这门科学的上述艺术方面。每种定价方法都是不同的——从最初的假设到得出证券价格的实际方法(数值或分析)。这并不意味着任何特定的方法更正确或不正确;这就是为什么理解每种价格是如何得出的如此重要。了解布莱克-斯科尔斯价格(对数正态分布的股票价格和无交易成本,仅举两个例子)与蒙特卡洛价格(可能包括交易成本)的假设将有助于指导您的定价需求。理论解析解和数值解(往往会收敛到一些理论解)都有美。事实上,我以前的一些研究着眼于修正布莱克-斯科尔斯模型,以更好地反映一般市场条件…
我现在假设你有很好的证券定价知识基础。事不宜迟,让我们看看为什么模拟定价通过允许模型输入的变化而如此强大。
随机波动过程
经验观察到的股票价格的异方差在几何布朗运动中并不保留,因为波动率保持不变。赫斯顿(1993) 找到了一个类似于 Black-Scholes 坚持随机波动率概念的准封闭解。然而,他的解的推导要比 Black-Scholes 模型的推导复杂得多,超出了本文的范围。然而,他用来推导模型的基本过程可以很容易地应用到定价模拟中,从本质上讲,捕捉到了相同的结果:随机波动性。
基本上有两个过程:
I. 用于生成新方差值的随机过程
二。 用于生成新股票价格值的随机过程
首先,让我们先了解一下我们正在使用的参数…
- S —初始股票价格
- r —无风险利率
- v_t —瞬时方差
- α,β —平均回复参数
- σ_v —方差的波动率
- ε—生成错误
- ρ —误差相关性
- δt—随时间变化
现在让我们来看看流程本身…

误差分布

产生方差的随机过程

产生股票价格的随机过程
从根本上说,这些过程没有什么特别令人惊讶的。每个过程本质上都可以分解为第一项中的期望和第二项中对该期望的冲击。
特别敏锐的读者会注意到,通过为波动性创建一个独立的随机过程,我们再次修正了波动性。也就是说,我们修正了方差的波动性。这可能成为一个循环论证。方差的波动性是否应该有一个独立的随机过程,它现在是一个独立的随机过程?这方面有研究,值得注意,但为了这篇文章,我跑题了。
现在让我们看看 Python 如何实现这些过程来给证券定价。
Python 中的随机方差定价
首先,让我们导入 numpy 并定义随机方差过程的参数,以及一些为标准欧式期权定价的参数…
接下来,我们需要创建负责运行模拟 n 次的外部循环…
在此循环中设置了一些参数,这些参数将在下一个循环中变得更加清晰。首先,在系列仿真中的每个新仿真开始时,我们需要重置基本参数: St 、 inst_var_now 、 prev_inst_var 和 step 。这些与本文第一部分中定义的输入参数同义。请注意,瞬时方差被分解为两个变量,这是为了让我们在生成新方差的随机过程中,对每一步使用适当的误差。让我们创建一个内循环来遍历一条具有随机方差的股票价格路径…
在这个 while 循环的每一次迭代之后,我们将得到最终的股票价格值。这个最终的股票价格值可以在外循环中再次使用,以根据我们正在定价的证券产生收益。在这个例子中,我正在为一个欧式期权定价,所以根据这些参数,我计算出适当的收益…
请注意,当将收益附加到收益列表时,收益会按照时间 t 内的无风险利率进行贴现。如果货币的时间价值是一个外来概念,请参见 Python 中的量化金融。
为了计算这种欧式期权的模拟价格,我们简单地将附加到收益列表中的值进行平均…
14.648492872411381
我们做到了!我们通过一个随机方差过程得出了模拟价格。
结论
本文展示了如何使用随机方差过程来模拟证券的价格。同样,与其他定价方法相比,不要认为这个价格或多或少是正确的。相反,要理解最终价格差异的原因。这个概念可以扩展到影响金融工具的每个参数,使它们都成为随机过程。
随机方差模拟定价脚本:
熊猫和牛郎星的股价分析
熊猫和牛郎星实用指南

马库斯·温克勒在 Unsplash 上的照片
股票价格分析是时间序列分析的一个例子,时间序列分析是预测分析的主要领域之一。时间序列数据包含附加到连续时间戳的测量值或观察值。对于股票价格,时间戳可以是秒、分钟或天,这取决于任务。
这篇文章是一个实践指南,涵盖了股票价格分析中的一些基本和必要的操作。我们将使用 Pandas 进行数据操作,使用 Altair 进行数据可视化。两者都是数据科学中常用的 Python 库。
获取股价数据的资源有很多。我们将使用熊猫的数据阅读器 API。我们首先导入依赖项。
import numpy as np
import pandas as pd
from pandas_datareader import data
import altair as alt
我们现在可以使用数据读取器来创建包含股票价格数据的 Pandas 数据框架。
start = '2019-1-1'
end = '2020-12-31'
source = 'yahoo'apple = data.DataReader("AAPL", start=start ,end=end, data_source=source).reset_index()ibm = data.DataReader("IBM", start=start ,end=end, data_source=source).reset_index()microsoft = data.DataReader("MSFT", start=start ,end=end, data_source=source).reset_index()
我们使用 DataReader 函数来获取股票价格。股票名称、开始和结束日期以及来源是必需的参数。
该函数返回如下数据帧:

(图片由作者提供)
对于苹果、IBM 和微软,我们有一个单独的数据框架。最好将它们结合起来,但是我们需要一个列来指定股票的名称。
apple['Symbol'] = 'AAPL'
ibm['Symbol'] = 'IBM'
microsoft['Symbol'] = 'MSFT'
每个数据帧中的符号列表示名称。我们现在可以使用 Pandas 的 concat 函数将它们组合起来。
stocks = pd.concat(
[apple[['Date','Close','Volume','Symbol']],
ibm[['Date','Close','Volume','Symbol']],
microsoft[['Date','Close','Volume','Symbol']]],
axis=0)

(图片由作者提供)
我只包括了日期、收盘、成交量和符号栏。我们先创建一个苹果股价的基本线图。
(alt.
Chart(stocks[stocks.Symbol == 'AAPL']).
mark_line().
encode(x='Date', y='Close'))

(图片由作者提供)
代码中的第一行是顶级图表对象。我们将要绘制的数据传递给这个对象。使用 symbol 列应用了一个筛选器,只包含属于 Apple 股票的数据。
在下一行中,指定了绘图的类型。编码功能指示要绘制的列。我们在 encode 函数中作为参数写入的任何内容都必须链接到传递给 Chart 对象的数据。
除了偶尔的几次下跌,苹果的股票价格似乎一直在上涨。2020 年 4 月的一次可能与冠状病毒引起的全球疫情有关。
我们可以在一个视图中显示所有三家公司的股票价格。
(alt.
Chart(stocks).
mark_line().
encode(x='Date', y='Close', color='Symbol').
properties(height=300, width=500)))

(图片由作者提供)
区分这些公司的是符号栏。它作为参数传递给 encode 函数的 color 参数。苹果和微软的股价也遵循类似的趋势。IBM 似乎受疫情的影响更大。
在最后一行,我们使用属性函数来改变图形的大小。
我们可以通过重新采样数据来平滑这条线。重采样基本上意味着用不同的频率表示数据。一种选择是使用重采样功能 Pandas。它根据指定的频率和聚合函数聚合数据。
msft_resampled = stocks[stocks.Symbol == 'MSFT'].resample('7D', on='Date').mean().reset_index()
上面的代码基于 7 天周期的平均值对 Microsoft 股票价格进行重新采样。
(alt.
Chart(msft_resampled).
mark_line().
encode(x='Date', y='Close').
properties(height=300, width=500))

(图片由作者提供)
唯一的区别是我们将重采样的数据传递给图表对象。这条线现在看起来顺畅多了。
Altair 使得在一个可视化中拥有多个图变得非常简单。例如,我们可以将微软股票的收盘价和成交量绘制如下。
price = (alt.
Chart(stocks[stocks.Symbol == 'MSFT']).
mark_line().
encode(x='Date', y='Close'))volume = (alt.
Chart(stocks[stocks.Symbol == 'MSFT']).
mark_line().
encode(x='Date', y='Volume'))price | volume

(图片由作者提供)
我们将每个图分配给一个变量。逻辑运算符可以很容易地组合变量。例如,“|”将它们并排放置,而“&”将第二个图放在第一个图的下面。
Altair 提供了许多选项,通过传达更多信息来丰富可视化效果。例如,我们可以添加一条线来表示平均值。
line = (alt.
Chart(stocks).
mark_line().
encode(x='Date', y='Close', color='Symbol').
properties(height=400, width=500))avg = (alt.
Chart(stocks).
mark_rule().
encode(y='average(Close)', color='Symbol',
size=alt.value(2)))line + avg
第一个图是一个线图,就像前面创建的图一样。第二个图是计算每行平均值的规则。用 Altair 转换数据非常简单。例如,在第二个图的编码函数中,聚合用字符串(' average(Close)')表示。

(图片由作者提供)
在我们的例子中,我们可以直观地比较平均值,因为值之间有很大的差距。然而,当值接近时,指出平均值可能是有用的。
结论
时间序列数据有许多定义,所有这些定义都以不同的方式表示相同的含义。一个直截了当的定义是,时间序列数据包括附加到连续时间戳的数据点。
我们已经介绍了熊猫和牛郎星的一些基本操作。时间序列分析涵盖了更多的内容。事实上,这是一个非常复杂的话题,在这个领域已经做了大量的研究。
也就是说,理解基础知识然后开始更高级的话题几乎是有益的。
感谢您的阅读。如果您有任何反馈,请告诉我。
stock stats——一个方便的面向股票的熊猫数据框架包装器

丹尼尔·劳埃德·布兰克-费尔南德斯在 Unsplash 上的照片
只需调用列名即可访问各种技术指标!
在这篇文章中,我想简单描述一下我最近遇到的另一个有趣的库— stockstats。它是一个位于pandas数据框架之上的包装库,其主要目标是提供对与股票价格相关的各种度量和技术指标的即时访问。让我们直接开始吧!
准备
在能够看到stockstats能提供什么之前,我们需要导入库并下载数据。
使用yfinance,我们从 2020 年开始下载苹果的 OHLC 价格(+成交量)。关于库的更多细节,请参考我的其他文章。
这些数据足以展示stockstats的特色。该库的主类是StockDataFrame,它是位于pandas数据框架之上的一个包装器。使用下面的代码片段,我们可以很容易地将后者转换成前者。
stock_df = StockDataFrame.retype(df)stock_df

作者图片
到目前为止,没有什么真正的变化,新的 DataFrame 包含了与以前完全相同的列,只是现在名称是小写的。
stockstats提供了一种非常有趣的方法来计算不同的度量——我们只需要选择我们想要看到的列,库将使用底层方法来计算请求的值。
让我们从一些可以用于分析的基本信息开始——股票回报。
stock_df[["change", "rate", "close_-1_d", "log-ret"]]

作者图片
使用正常的pandas选择列的方式,我们获得了请求的值,这非常方便。简单地说,对色谱柱的简短描述如下:
change/rate——这些是简单回报,即股票价格之间的每日百分比变化。数值以百分比表示。close_-1_d—这是时间 t 和 t-1 的差价。我们稍后将回到这个请求值的特殊方式是stockstats。log-ret—日志返回。
值得一提的是,当我们像上面那样请求这些列时,我们只获得请求的输出。然而,完全相同的列被附加到源对象stock_df。
在小标题里我承诺了技术指标,那么我们来创建一个非常简单的——10 日简单移动平均线(SMA)。
stock_df[["close_10_sma"]]

作者图片
我们再次使用了请求定制指标的特殊方式。为了充分理解它,我们可以使用下面的模式{columnName_window_statistics}来查看所请求的列名,其中:
columnName—我们要计算指标的列的名称。在上面的例子中,我们使用了收盘价。window—需要考虑多少观察结果。我们在上面使用了 10 天的窗口。statistics—我们要计算的度量。在上面的例子中,我们使用了简单移动平均线(sma)。要获得所有可用指标及其对应列名的列表,您需要检查stockstats的 GitHub repo 。
我们现在将结合一些我们已经看到的元素来创建一个收盘价和两条移动平均线的简单图。

作者图片
另外,StockDataFrame仍然保留了pandas数据帧的功能。所以使用一个方括号来请求一个列会产生一个pandas系列。我们可以使用 pandas 索引来分割数据帧,例如:
这个图与第一个图非常相似,但是它只包含从2020–06–01开始的数据。
另一个我们很容易计算和绘制的 TI 是布林线。

作者图片
还有很多技术指标可用,如相对强弱指数(RSI),移动平均线收敛发散(MACD),随机振荡器,威廉超买/超卖指数,平均真实范围(ATR),等等。
所有这些指示器都使用一些基于行业标准的硬编码窗口值。例如,使用 20 天移动平均线(BOLL_PERIOD)计算布林线,布林线位于 2 个标准差处(BOLL_STD_TIMES)。我们可以通过访问StockDataFrame的相应元素来覆盖它们。
最后,当我们玩弄技术指标时,我们经常想要创造信号——这是一个买入/卖出的信号。一些简单而流行的策略包括检查一个值是否在另一个值的上方/下方交叉。stockstats也提供这样的功能。
stock_df["close_xu_close_20_sma"]

作者图片
我们得到一个布尔序列,表明收盘价是否越过了相同收盘价的 20 天 SMA。我们还可以请求更复杂的交叉,例如两个 SMA 的交叉。
stock_df["close_10_sma_xd_close_50_sma"]
在这种情况下,当 10 日均线穿过 50 日均线时,我们得到一个信号,两者都是用收盘价计算的。
意见
stockstats绝对是一个有趣的库,在做一些股票价格的快速分析时可以派上用场。它还提供了一种独特的方法,通过选择适当的列名来计算所请求的指标。熟悉用于访问组合指标的模式可能需要一段时间。
然而,该库的文档记录很少。大部分信息可以从回购中的README文件中获得,或者直接通过检查代码获得。但这也提供了一个窥视技术指标是如何计算的可能性,这是一个很好的学习经历。
外卖食品
stockstats是一个位于pandas数据框架之上的包装器,提供对各种股票相关指标的便捷访问。- 该库提供了广泛的流行技术指标,如 SMA、EMA、RSI、MACD 等。
- 您可以通过调用所需的列名来计算指标,所有这些都将在幕后处理。
您可以在我的 GitHub 上找到本文使用的代码。此外,欢迎任何建设性的反馈。你可以在推特上或者评论里联系我。
如果你对学习如何使用 Python 进行量化金融感兴趣,你可能想看看 Quantra ( 免责声明:附属链接),它提供了关于这个主题的各种不同的课程。
注来自《走向数据科学》的编辑: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
如果您喜欢这篇文章,您可能还会对以下内容感兴趣:
</5-free-tools-that-increase-my-productivity-c0fafbbbdd42>
停止追逐每一项闪亮的新技术,专注于真正重要的东西

莎伦·麦卡琴在 Unsplash 上的照片
围绕继续教育设定目标,以促进你的职业发展。
简介:
几年前,我曾经带着我的柯基去狗公园,在那里她会带着一对德国短毛猎犬(快速狗)追逐网球。令人惊讶的是,她有三分之一的机会拿到球。两只狗都可以围着她转圈,但它们很容易分心。她只需要额外的五秒钟专注,就能以两倍的速度打败狗。
任何数据方面的职业都需要不断的学习,很容易分心。一名数据操作员需要学习的科目数量之多简直令人应接不暇:
- 我应该专注于 Python 吗,或者更好地使用 Tableau 进行可视化更重要?
- 但是能够将代码投入生产是超级重要的;所以我绝对应该学阿帕奇气流。
- 另一方面,通过加深对随机森林的了解,我真的可以做很多事情。
- 很少有雇主使用回归分析之外的模型,所以这可能是我应该关注的地方。
- 另一方面,我公司的数据治理现在一团糟,也许我应该花时间深入了解这一点。
- 熊猫 1.2 最近发布了——现在是一个很好的时间来最终弄清楚熊猫在我的笔记本上给我的警告是什么。
随着数百个合法的主题被掌握,你很容易感觉到你必须对每一个新出现的技术做出反应。我真诚地相信,一个现代的超级大国只是能够保持专注,优先考虑重要的事情。就像我的柯基一样,拥有这种额外的专注可以让你战胜比你更快、更聪明、更有经验的人。
真正重要的是:
有效教育计划的基石是大致了解你想在庞大的数据相关工作生态系统中处于什么位置。一旦你知道你想去哪里,你就可以开始设置一个课程,防止你追逐每一个出现在你面前的闪亮的新技术,并帮助你摆脱似乎永无止境的半成品课程。
如果你不知道你到底想要什么类型的数据职业,那也没关系。关注数据技术堆栈的基础:
- 电子表格
- 结构化查询语言
- 一种脚本语言(R 或 Python)
- 可视化工具(Tableau 或 Power BI)
无论你的职业方向如何,这些技能都会很好地为你服务。避免追逐花哨的新技术,这些技术不会立即给你带来好处,而且可能在一两年后过时。
把小麦从谷壳中分离出来;
写下你想学的每一项技能,以及为什么要学,和你的目标有什么关系。这一步可以让你专注于你需要做的 4-6 件事情。你从计划中删减的一些项目将会很痛苦,因为有太多很酷的技术。
我花了几年时间想学习 D3,并做了一些半吊子的尝试。问题是学习 D3(和 JavaScript)不太符合我的职业目标。我想学它,因为它很酷。虽然放弃一项很酷的技术很痛苦,但我的 Python 技能已经有了指数级的提高,因为我不再分心了。
不要单干:
我将我的职业轨迹定为工程。这些都是我喜欢解决的问题。一旦我意识到这就是我想要的,我就开始和公司里那些自学成才的工程师交谈,看看他们是如何做到的。他们能够让我接触到一些真正影响我学习计划的资源。确定可以帮助你找出你应该关注的问题的导师。试着找到和你背景相似的导师。如果你想进入机器学习领域,并且你不是来自学术背景,那么拥有计算语言学博士学位的人不会为如何填补你的知识空白提供最相关的建议。
建立课程:
利用来自导师的信息,开始建立一个课程。将课程记录在电子表格中,并对每个科目的重要性进行排序。使用这个电子表格的关键部分是你一年只允许更新一次到两次。一旦你设置了课程,你只允许花时间学习电子表格中的科目。
我实际上保留了两门课程。一个是长期的,基于我的职业目标。我还保留了一个短期计划,重点关注我工作所需的技能。就我而言,我所有的长期目标都与工作一致,但并不是我工作所需的所有技能都与我的长期目标一致。
批准的教育来源:
在继续教育上花很多钱是非常容易的。为了区分支出的优先顺序,我还保留了一份经过批准的教育资源清单。如果一个潜在的新资源出现了,而它不在列表中,我会记录下来,这样我下次编辑我的课程时就可以研究它。
我强烈建议你看看当地图书馆给你提供了哪些教育资源。我的图书馆,西雅图公共图书馆,让我进入 LinkedIn Learning 和 O'Reilly 教育平台。直到你看了才知道你的图书馆提供了什么,但是北美的大多数图书馆系统都可以访问一些在线学习资源。
理论、实践和过程:
当设计你的课程时,你要平衡理论和实践(实际应用的一种奇特说法)。许多在线资源过分强调实践,但是你要确保你了解足够的理论,从概念上理解你正在做的事情。
我还鼓励你花一些时间投资学习更好的流程,这不同于理论和实践。我目前正在学习如何使用 Vim,作为一种让自己成为更高效的程序员的方法。我最近还学会了如何更有效地记笔记。这些都与我的技术技能没有直接关系,但确实极大地改善了学习和工作的过程。
其他过程类型的技能可以包括掌握 Git,学习 Jupyter 笔记本的细节,或者用最好的扩展配置 visual studio 代码。
宽而浅,还是窄而深?
有两种方法可以增强你的技能,学习新技能来扩大技能基础,或者加深你对现有技能的理解。每一个都有优点和缺点,广泛而浅薄的技能将让你解决许多不同类型的相对简单的问题。对于数据职业生涯的早期阶段,宽而浅是一个很好的方法。许多入门级的角色在本质上更通才,让自己接触各种各样的问题会让你知道你想如何专业化。
当你从入门级角色过渡到助理/职业生涯中期角色时,你应该转变为接受狭隘/深刻的心态。在这一点上,你知道你喜欢什么,你擅长什么——更深刻的经验让你处理更复杂的问题。
最佳策略是混合策略。你需要对核心数据技术有足够的了解,这样你才不会在专业领域之外束手无策,但是过于一般化会限制你的价值和职业视野。
结论:
采用这种策略极大地提高了我的教育效率。而不是不断追逐酷技术,我在控制。在一个混乱的不断发展的领域,保持清晰和专注帮助我打造了我想要的职业。
开始对话:
你找到了哪些最好的继续深造的资源?
我最喜欢的是:
https://teachyourselfcs.com/
它是帮助自学成才的软件工程师学习计算机科学理论基础的完整课程。
关于:
Charles Mendelson 是西雅图的商业智能分析师,他的角色是叙事分析和数据工程的混合体。如果您有任何问题或想与他取得联系,您可以通过 LinkedIn 联系他。
原载于 2021 年 7 月 11 日https://charlesmendelson.com。
蓝鲸 GPS 追踪中的停止检测—移动熊猫 0.6
如何用 KeplerGL 中的海洋巨型动物 GPS 轨迹创建地图动画

图片由作者提供。GPS 轨迹的地图动画。
我们又来了移动熊猫。由 Anita Graser 开发的 python 库是分析运动数据的有力工具。在后期的帖子里,我发表了如何创建地图动画以及如何用鸟类迁徙数据 (GPS 追踪)提取停靠点作为点。Movingpandas 0.6 新版本中的 停靠点检测算法 直接将停靠点作为点返回,这绝对是我想要包含在带有移动数据的工作流中的,因为它大大简化了编码。尤其是带着一个TrajectoryCollection。
对于本教程/工作流,我使用 GPS 数据在南加州跟踪蓝鲸,以创建一个地图动画与停止的持续时间。收集数据集是为了研究海洋巨型动物的深潜行为对定位误差的影响。为了更好地理解,您可以查看 Irvin 等人(2020)在动物生物遥测学上发表的论文,并在 Movebank 中查看 GPS 数据集的详细信息。
这个 demo 最后的地图动画是 这里!按下播放,探索!
此处储存有分析工作流和!

图片由作者提供。鲸鱼追踪,以分钟为单位的停止持续时间
鲸鱼运动数据集包含了从 2014 年 8 月到 2015 年 8 月收集的鲸豚和鲸豚的 13 条鲸鱼轨迹。在这个演示练习中,我将只使用时间范围为 2 个月的 5 条轨迹:2015.07 到 2015.08 。请记住,在移动熊猫 中使用 停止检测算法时,您必须参照持续时间和搜索电台指定停止参数。在这种情况下,我将 指定为最小 5 分钟,并将搜索无线电指定为 30 米 。但是,您可以指定您认为适合您的研究的内容。如果你正在研究鲸鱼的进食行为,那么考虑一下它们在同一个地方一跳就能捕捉到食物。我决定在 30 米的搜索收音机上选择 5 分钟,因为这足以找到车站。我会考虑关于浮游植物的重叠信息,这样我们就能确认鲸鱼提供午餐的地点。
让我们把手放在。你可以在本帖 的 库中查看材料,但这里的分析示例将分为三个部分进行说明:
1)将移动数据集格式化为移动熊猫。写一个函数,在移动熊猫 0.6 中返回停止点。
3)开普勒 GL 中的地图动画。
1)将运动数据集格式化为运动熊猫
这个过程是在 Jupyter 笔记本中完成的,所以首先,我们导入所有需要的库。请确保您使用的是移动熊猫 0.6。
import pandas as pd
import geopandas as gpd
from datetime import datetime, timedelta
from keplergl import KeplerGlimport movingpandas as mpd
print(mpd.__version__)import warnings
warnings.simplefilter(“ignore”)
然后,我们读取包含在地理数据框架df的存储库中的鲸鱼运动数据集。在运行过程中,用我们在本演示中使用的 5 头鲸鱼(标签 ID)对表进行分组。
# opening file
cd = r’data/whale-movement.geojson’
file = open(cd)
df= gpd.read_file(file)# subset of 5 whales
fiveset = ['2015CA-MK10-00838', '2015CA-MK10-00840', '2015CA-MK10-04177', '2015CA-MK10-05650', '2015CA-MK10-05654']
df = df.loc[df['tag_ident'].isin(fiveset)]
为了使移动数据集准备好用于移动熊猫,有必要将索引格式化为日期时间格式。接下来是 :
# preparing the data for movingpandas
df[‘t’] = pd.to_datetime(df[‘timestamp’])
df = df.set_index(‘t’).tz_localize(None)
至于建议。确保您的地理数据框架df具有 CRS WGS84。目前,我们已经用DateTime index和 5 只鲸鱼的子集格式化了表格。新子集的时间范围从 2015 年 7 月到 2015 年 8 月。现在我们打算用它来移动熊猫。
2)在移动熊猫 0.6 中返回停止点的功能
在本节中,我将创建一个函数,该函数返回一个以停靠点为点的 地理数据框 ,并包含一个以分钟为单位的停靠持续时间列和另一个以鲸鱼的标签 id 为单位的列。请注意,该函数使用以分钟为单位的参数。你可以在我最近的帖子中找到以小时为单位的参数的例子,在安妮塔·格拉泽库中找到以秒为单位的例子。停靠点代表轨迹的一段,而作为点的停靠点是该段轨迹的起点。
该函数的参数有:moving _ df
def get_stop_point_traj_collection(moving_df, traject_id_column, minutes, searchradio):
all_stop_points = gpd.GeoDataFrame()
# create a traj collection with movingpandas
traj_collection = mpd.TrajectoryCollection(moving_df, traject_id_column)
for i in range(len(traj_collection)):# create a stop detector
detector = mpd.TrajectoryStopDetector(traj_collection.trajectories[i])
# stop points
stop_points = detector.get_stop_points(min_duration=timedelta(minutes=minutes), max_diameter=searchradio)
# add duration to table
stop_points['duration_m'] = (stop_points['end_time']-stop_points['start_time']).dt.total_seconds()/60
# add ID
stop_points['tag_id'] = [tag.split('_')[0] for tag in stop_points.index.to_list()]
all_stop_points= all_stop_points.append(stop_points)
return all_stop_points
然后,我们将它与带有 GPS 记录的地理数据框架df一起使用,我们将鲸鱼的标签 id 包含在列tag_ident中,将5分钟用于停止检测,将30米作为搜索无线电。
%%time
whale_stops = get_stop_point_traj_collection(df, ‘tag_ident’, 5, 30)
如果您用行whale_stops.head()检查最终表格whale_stops,您将看到一个如下所示的漂亮表格:

图片由作者提供。将鲸鱼停靠点作为移动熊猫分析的点的表
3)开普勒 GL 中的地图动画
在 KeplerGL 中工作的第一步是避免列中的 日期时间格式 。我们将移除地理数据框中的 日期时间格式 以及所有 GPS 轨迹df和地理数据框whale_stops。使用下一个代码:
# remove datetime format for GPS tracks
df = df.reset_index(drop=True) # remove datetime format for whale stops
whale_stops[[‘start_time’, ‘end_time’]] = whale_stops[[‘start_time’, ‘end_time’]].astype(str)
whale_stops= whale_stops.reset_index(drop=True)
然后,我们创建一个 KeplerGL 实例,并添加两个地理数据框:df和whale_stops,代码如下:
# Create KeplerGl instance
m = KeplerGl(height=600)# Add stop durations
m.add_data(whale_stops, ‘stops’)# Add gps records
m.add_data(df, ‘trajectories’)
现在你可以通过 KeplerGL 调用实例来格式化贴图动画。
m

图片由作者提供。在 KeplerGL 中格式化地图动画
我们可以观察到鲸鱼是如何偏好停在海峡群岛国家海洋保护区附近的。也许附近有个喂食的好地方。
最后,保存用下一行格式化的地图:
# Save map as html
m.save_to_html(file_name=’index.html’)
这就是全部!现在你可以可视化你的鲸鱼 GPS 轨迹的移动。
本教程/帖子主要关注移动熊猫 0.6 的使用和停止检测算法。如果您有任何问题,请随时通过联系我,我在 LinkedIn 上的个人资料。
GPS 轨迹中的停止检测—移动熊猫和开普勒
如何在鸟类迁徙追踪中创建带有停留时间的点地图

图片由作者提供,GPS 轨迹的地图动画。
在过去的几个月里,我通过分析移动数据(GPS 跟踪数据)发现了 移动熊猫 的力量。由 Anita Graser 开发的这个 python 库包含了让以非常方便的方式操作运动数据的算法。提出运动分析的问题与旅行行为模式有关,并找出被跟踪对象以特定方式运动的原因。真正抓住我并最吸引我的是 停止检测算法 ,它在特定的搜索半径(即邻域)内找到被跟踪的对象在轨迹中具有特定持续时间(停止)的路段。在这一点上,使用这种算法的想法可以在例如交通分析中的城市应用、繁殖行为的生物学应用、迁移分析的地理学应用中得到推广,你可以找到更多。关键的一点是,作为一名分析师,你必须在时间和搜索半径上保持明确和合理的参数。时间将揭示你可能想要发现的活动,例如在娱乐中心花费的时间和搜索半径社区/城市的大小。
本帖最终地图动画可以在 这里 !
按下播放,探索!
这里分析 这里分析 !

图片由作者提供,鸟类轨迹中带有停留时间的点地图。
让我们把手放在它上面。在这个分析示例中,我使用了来自全球生物多样性信息中心的数据集,其中包含三只黑背鸥的 GPS 记录:Eric、Nico 和 Sanne。你可以在 GBIF 网站上找到有更多海鸥的整个数据集,这三种鸟类的数据集在本文 的 资源库中。该过程分为三个部分进行解释:1)使用 Geopandas 创建图层,2)使用移动 pandas 进行停止检测,3)使用 KeplerGl 进行点地图可视化。
1。使用 Geopandas 创建图层
我们将在一个 Jupyter 笔记本中完成整个分析,因此导入我们将要使用的所有 python 库是非常有效的。
import pandas as pd
import geopandas as gpd
import movingpandas as mpd
from shapely.geometry import Point
from pyproj import CRS
from keplergl import KeplerGl
from datetime import datetime, timedelta
然后,过程变得简单。读取 CSV 文件,添加几何列,在 WGS84 中创建地理数据框。是这样的:
# Reading file
fp = r'data/bird_tracking_data.csv'
data = pd.read_csv(fp)# Creating a geometry column as Point geometry
data['geometry'] = [Point(long, lat) for long, lat in zip(data['longitude'].to_list(), data['latitude'].to_list())]# Creating a Geodataframe. Be aware it is CRS 4326 WGS84
geodata = gpd.GeoDataFrame(data, crs = CRS.from_epsg('4326'))
现在我们有了geodata我们可以开始下一个使用移动熊猫的部分。
②。停止侦测移动的熊猫
在创建移动熊猫的轨迹之前,第一个主要步骤是在geodata中添加时间戳作为索引
# Create timestamp with Pandas datetime. Inclue None as timezone
geodata['t'] = pd.to_datetime(geodata['date_time']).dt.tz_localize(None)# Set timestamp as Index
geodata = geodata.set_index('t')geodata.head()
你会有一张漂亮的桌子,如下图所示:

图片由作者提供,表格准备好用于移动熊猫分析
此时,您可以分析数据集的一些特征,如长度(61920 条记录)和本例中的鸟总数:Eric、Nico 和 Sanne。跟踪的时间范围从 2013 年 8 月 15 日开始,到 2014 年 4 月 30 日结束,共 9 个月。
对于移动熊猫,您可以创建一个TrajectoryCollection,指定轨迹的 ID,在本例中是带有鸟的名字的列。
# Create a Trajectory Collection with Movingpandas
traj_collection = mpd.TrajectoryCollection(geodata, 'bird_name')
在 Anita Graser 的资源库中,您可以探索这个TrajectoryCollection的属性,但在这里我将直接继续讨论 停止检测算法 。当我们探索 9 个月的鸟类轨迹数据集时,我将在 1000 米的局部水平上以适度的搜索半径识别鸟类的停留,停留持续时间为 12 小时或更长。
# Define parameters in Hours and Search radius in meters
Hours = 12
SearchRadio = 1000# stop detection
stops = mpd.TrajectoryStopDetector(traj_collection).get_stop_segments(min_duration=timedelta(hours=Hours), max_diameter=SearchRadio)
注意!关于 停止检测算法 的一些有趣的事情是,每一次停止代表一段轨迹。所以,这一段是海鸥在 1 公里搜索半径内花了 12 个小时的地方。从逻辑上讲,作为一段轨迹的停止点具有持续时间、长度、起点、开始时间、终点和结束时间(以及其他属性,如速度)。作为一个快速视图,您可以看到下一个捕捉中的停靠点:

作者图片,停止可视化(轨迹段)
为了得到一个点地图作为最终输出,我们将使用停止的起点。在这一点上,我们汇总了停止持续时间,这基本上意味着在停止(轨迹段)中花费的总时间。为了聚合点中的停止持续时间,我们创建了一个新的地理数据框,其中包含了该属性。接下来是代码:
# Create a new Geodataframe and define geometry column
stops_start = gpd.GeoDataFrame(columns = ['geometry'])
stops_start = stops_start.set_geometry('geometry')# Add the ID of each stop track and define it as index
stops_start['stop_id'] = [track.id for track in stops.trajectories]
stops_start= stops_start.set_index('stop_id')# Iteration over the Stop Trajectories
for stoptrack in stops.trajectories:
# add stop duration in hours
stops_start.at[stoptrack.id,’duration_h’] =stoptrack.get_duration().total_seconds()/3600
# add length
stops_start.at[stoptrack.id, 'length_m’]=stoptrack.get_length()
# add bird name
stops_start.at[stoptrack.id, 'bird’]=stoptrack.id.split(’_’)[0]
# add datetime
stops_start.at[stoptrack.id, 'datetime’]= pd.to_datetime(stoptrack.id.split(’_’)[1]).tz_localize(None)
# geometry with start point
stops_start.at[stoptrack.id, 'geometry’] = stoptrack.get_start_location()
stops_start.head()
你会有一张漂亮的桌子,看起来像这样:

图片由作者提供,停止持续时间表已准备好进行可视化
请记住,在接下来的步骤中,KeplerGl 不接受时间戳类型,为了避免混淆,我们必须重置索引geodata 和stops_start。您只需运行这些行:
# Reset indexes
stops_start = stops_start.reset_index(drop=True)
geodata= geodata.reset_index(drop=True)
3。用开普勒 Gl 实现点地图可视化
KeplerGl 是由优步开发的一个强大的 python 库,它有助于以一种非常有效的方式可视化大量的点。我发现适合运动数据的是,你可以添加一个时间窗口,并将其作为地图动画播放。运动数据中的动画有助于可视化之前提到的旅行行为,这篇文章旨在让您可视化鸟类的轨迹,它们在哪里停留了多久。
从 KeplerGl 开始,首先创建一个实例:
# Create KeplerGl instance
m = KeplerGl(height=600)
然后将数据添加到实例中。我们想要可视化的是停止和轨迹。因此,您添加了下一个地理数据框:
# Add stop durations
m.add_data(stops_start, 'stops')# Add gps records
m.add_data(geodata, 'trajectories')
调用实例后,您可以在 KeplerGl 控制台中编辑可视化,并放置颜色、大小、过滤器等。请记住,我们需要一个基于持续时间的点地图,所以我们在点大小中添加了以小时为单位的持续时间。您还可以添加时间过滤器。
m

一旦找到了最佳的可视化效果,就可以将地图保存为 Html 格式以供发布。在这里你可以查看 这张最终地图 。用下一行:
# Save map as html
m.save_to_html(file_name='index.html')
仅此而已!
在您自己的研究中使用 stop 检测算法找到最佳见解。如果你需要工作流自动化方面的支持,我可以支持你。查看我的简介。
作为一名数据科学家,不要再这样做了
意见
…数据科学家的常见错误和相应的解决方案

由Unsplash【1】上的 krakenimages 拍摄的照片。
目录
- 介绍
- 忽视沟通技巧
- 过度承诺不切实际的结果
- 没有衡量对业务的影响
- 摘要
- 参考
介绍
和任何工作一样,有我们经常遇到的错误或我们应该忘记的习惯。对于数据科学家来说,没有例外。在你的职业生涯中,你可能会犯错误,这没什么。然而,事先了解它们,这样你就可以避免大多数容易犯的错误,这对你的职业生涯非常有益。话虽如此,让我们更深入地探讨作为数据科学家应该尽快避免的三个错误,同时讨论解决方案或应该做些什么,如果您也想了解关于这些错误的注意事项,请继续阅读下面的内容。
忽视沟通技巧

照片由 krakenimages 在Unsplash【2】上拍摄。
在数据科学的教育中,我们关注统计学、算法,当然还有数据。这些都是非常重要的技能,然而,有一项技能经常被忽略。这个技能就是沟通。即使我们确实在大学或训练营中学习了它,当我们的很多工作都是埋头苦干时,我们可能仍然认为它不重要。有趣的是,为了完成我们的工作,我们需要与业务的几个部分进行沟通,无论是直接的利益相关者,如产品经理、高管还是软件工程师。
为了避免忽视沟通技巧,我们应该学习一些特定的技巧,包括以下几点:
- 在他人面前口头陈述
- 将复杂的数据科学术语转化为易于理解的概念
- 分解并讨论项目步骤(例如,数据收集、特性创建、模型比较、部署、业务影响以及这些步骤的时间表)
如你所见,你的工作很大一部分包括协作,如果没有沟通技巧,你将很难成为一名数据科学家。在讨论数据科学项目的开始和结束时,关于业务影响和范围,您可以期望与产品经理进行最多的交流。同样,在项目开始和结束时,您将与数据分析师、数据工程师、软件工程师和机器学习工程师讨论数据、代码、部署和应用程序集成。
过度承诺不切实际的结果

在 Unsplash 上由 Austin Distel 拍摄的照片。
作为数据科学家,我们可以对使用模型来预测和自动化业务的某些部分感到兴奋,从而为公司带来巨大的利益。我们期望对业务产生巨大的影响,有时确实如此,但也有很多时候,一个模型只是稍微改进了一个过程,但仍然值得去实现它。
以下是一些可以避免过度承诺结果的方法:
- 避免测试一个模型并过早地报告其结果(例如,在一个不能很好地概括整个群体的测试集上报告 94%的准确性)
- 避免假设您将在生产中测试所有的特性(),例如,您可能认为您将在推断时使用所有 40 个特性,而您可能只能使用其中的 10 个特性
- 分享显示特定影响的 方向 的结果,而不是测试时的准确结果(例如,利益相关者可以坚持使用您几周前分享的准确数字,并将其报告给其他人,在更多的迭代和产品验证之后,准确性可能会有很大的不同)
- 注意:当你承诺这些不切实际的结果时,会让你看起来很糟糕,也会让别人因为你给出了错误的希望而感到不安
总的来说,说实话,对一些初步结果持保留态度是很重要的,不是因为结果不正确,而是因为当它们成为最终形式时可能会发生变化。有时,项目需要得到批准,你必须展示准确性度量标准(或类似的),所以记录下你的模型在未来可能出现的问题或可能的变化是非常重要的。
没有衡量对业务的影响

在Unsplash【4】上由 Clark Tibbs 拍摄的照片。
如果你在一个较小的公司,这个错误可能会更突出,而在较大的公司,通常会采取一些步骤来衡量对业务的影响,作为项目放在首位的一个要求。然而,重要的是要知道一旦模型投入生产,它将对企业产生什么影响。
当你作为一名数据科学家工作时,仅仅让你的模型有很高的准确性或很低的误差是不够的。
具体来说,在衡量您的模型对业务的影响时,需要考虑以下几点:
- 这种模式节省了多少成本?
- 你的模型需要哪些资源?(例如,其他软件工程师、工具或平台)
- 哪些 KPI(关键绩效指标)会受到影响?
- 这种模式本身表现很好,但从其他方面有所欠缺吗?(例如,您可能正在使用一个过于具体的模型,可能会拒绝客户,导致企业失去大量客户和资金)
总的来说,模型不仅仅影响它试图预测的东西。企业通常是一个生态系统,在这个生态系统中,一件事情可以对企业的许多方面产生多米诺骨牌效应。长话短说,模型的高准确性实际上可能会对业务产生负面影响,这是数据科学社区中不太谈论的事情。
摘要
这三个错误非常容易犯,而且是我亲身经历过的。我希望这篇文章能够揭示如何避免这些错误,以及应该做什么,或者至少在您的特定情况下需要考虑的一些事情,而不是通过艰难的方式来学习。
总而言之,作为一名数据科学家,以下是你应该停止做的一些事情:
* Overlooking Communication Skills* Overpromising Unrealistic Results* Not Measuring Impact on Business
我希望你觉得我的文章既有趣又有用。如果您同意或不同意这些常见的数据科学错误,请随时在下面发表评论。为什么或为什么不?你认为还应该避免哪些错误?这些当然可以进一步澄清,但我希望我能够对您作为数据科学家可能犯的一些常见错误有所启发。感谢您的阅读!
请随时查看我的个人资料、 马特·普日比拉、和其他文章,并通过以下链接订阅接收我博客的电子邮件通知,或点击屏幕左侧的的订阅图标,如果您有任何问题或意见,请在 LinkedIn 上联系我。
订阅链接:https://datascience2.medium.com/subscribe
参考
[1]照片由 krakenimages 在Unsplash(2020)上拍摄
[2]照片由克拉肯 images 在 Unsplash 上拍摄,(2020)
[3]照片由奥斯汀·迪斯特尔在 Unsplash 上拍摄,(2019)
[4]Clark ti BBS 在 Unsplash 上拍摄的照片,(2017)
停止丢弃离群值!3 次升级,让你为现实世界的线性回归做好准备
如何教你的回归区分相关异常值和无关噪声

威尔·梅尔斯在 Unsplash 上拍照
你是巴塞罗那足球俱乐部的数据科学家,负责构建一个模型,预测未来 2 年、5 年和 10 年年轻人才的价值增长。您可能希望回归一些有意义的指标的值,如助攻数或进球数。有些人现在可能会应用这个标准程序,从数据集中删除最严重的异常值。虽然你的模型可能平均预测得不错,但不幸的是,它永远也不会理解是什么造就了梅西(因为你把梅西和所有其他“离群值”放在了一起)。
在回归问题中丢弃或替换异常值的想法来自于这样一个事实,即简单的线性回归比较容易在数据中出现极端值。然而,这种方法对您作为 Barcelona 的数据科学家没有太大帮助。简单的信息:离群值并不总是坏的!
我们通常希望了解真实数据生成过程的每一个细节(DGP)。因此,我们需要应用足够稳健的回归方法来区分有意义的极端值和无意义的异常值。以下案例说明了这些问题。我们将比较几种稳健回归技术在一个简单回归问题上的表现。在本文中,所有方法都是从零开始实现的。
简单回顾一下线性回归
有大量优秀的文章详细描述了线性回归背后的数学和直觉。如果你需要详细的复习检查这里或者这里。为了我们的缘故,让我们只是提醒我们,我们的目标是预测一个给定的目标 y 的数据点 x。
我们回归的一般形式和矩阵形式如下:

线性回归的一般形式
我们训练回归模型的目标是学习图示的参数向量β。为了达到这个目的,我们最小化一个预定义的成本函数,这个函数描述了任何可能的 beta 向量的残差。
数据生成
为了测试现有回归技术的稳健性,我们首先需要一个数据集来说明极端值和异常值的概念。我们选择了一组具有 4 个自由度的 Student-t 分布的点,用正弦变换作为我们的基本真理 DGP。然后,我们在 y 方向添加一些强异常值。

(普通)最小二乘法
您正在 Excel、Python 或任何其他工具中应用开箱即用的线性回归函数?那么 OLS 很可能就是你正在使用的。事实上,最小二乘法确实是一个很好的选择,因为它的成本函数是凸的,并且在 0 处可微。这使得优化变得非常容易。下图直观地显示了最小化简单的抛物线最小二乘损失函数是高中数学。另外,普通最小二乘捷径给出了你解的一步估计。

最小平方损失函数
听起来天衣无缝?可悲的是,简单是有代价的。在等式中,我们看到残差的非负性是通过平方来保证的。这导致了一个平滑的函数,但也增加了我们对大残差的权重。实际上,我们教导我们的回归线认为异常值对接近拟合的值更重要。这使得最小二乘法特别容易出现异常值。在数据集上运行简单的最小二乘回归时,我们可以看到毁灭性的结果。那么我们能做些什么来改善这种情况呢?

最小二乘损失(左);带有“+”标记的最小二乘回归线和带有“o”标记的基础训练数据(右)
你可以在我的 Repo 中找到从零开始实现最小二乘回归的 或本文常用的库如Scikitlearn或stats models。
升级#1:最小绝对偏差
鉴于我们对最小二乘的讨论,简单地抛弃平方残差惩罚是直接的。最小绝对偏差通过评估绝对残差而不是平方残差来确保积极性。这种影响在下面的成本函数中是可见的。

绝对偏差损失函数
与上面的最小二乘法相比,关于可微性的不利性质通过观察是可见的。然而,在许多情况下,这是有区别的。在我们的例子中,这取决于我们选择的性能指标。从均方误差(MSE)来看,LS 回归优于最小二乘约 3.3 倍。然而,就平均绝对误差(MAE)而言,最小二乘优于最小二乘约 5.1 倍。我们现在必须分清轻重缓急。目测回归线我们的选择将落在最小偏差回归上。但是视觉剧情分析应该不是模型决定因素…所以我们再深入一点!

最小偏差损失(左);带有“+”标记的最小偏差回归线和带有“o”标记的基础训练数据(右)
你可以在我的回购 中找到 从零开始实现的最小偏差回归。
升级#2:胡伯损失
平方损失和绝对离差各有优缺点。现在,如果我们有一种将两者结合在一起的技术会怎么样?瑞士统计学家彼得·胡伯也有同样的想法。
Huber 损失将小误差的二次损失与大误差的线性损失结合起来。用哪一个来惩罚我们的残差取决于新的超参数增量。Delta 是决定对给定数据点应用哪个惩罚的阈值。这种机制导致混合损失函数。该函数变得越来越线性,在 0 处仍然可微,并且取决于超参数δ。为了形象化,检查下面的图。

胡伯损失函数
Delta 是一个绝对测量值。因此,不存在可以确保良好性能的通用默认值。选择完全取决于基础数据和我们愿意接受的偏差。
在我们的数据中,评估 MSE 和 MAE 的最佳回归线符合 Delta = 1。其结果是 MSE 为 10.0,MAE 为 0.75。但是我们可以做得更好!

取决于 delta 的 Huber 损耗(左);带有“+”标记的 Huber 回归线和带有“o”标记的基础训练数据(右)
再看看 从零开始在我的回购 中实现这一亏损函数为本文或常用库如Scikitlearn或stats models。
升级#3:分位数回归
我们已经看到如何使用绝对值可以超过最小二乘。我们还发现,一个基于具体情况决定应用哪种惩罚的灵活公式可以进一步提高性能。最后,让我们看看分位数回归。
如果你愿意,分位数回归是最小偏差的一个特例。细心的读者可能已经注意到,最小偏差技术有效地估计我们的 DGP 的条件中值。既然中位数只不过是中间分位数,为什么不灵活设计呢?分位数回归估计由新的超参数 Tau 指定的分位数。为了提高您的直觉,Tau = 0.5 的分位数回归线应该非常类似于我们在应用简单的最小偏差时看到的回归线。同样,良好的 Tau 取决于我们正在使用的数据集。
应用于我们的例子,分位数回归赢得了奖金。在 Tau=0.1 时,我们获得了 0.66 的均方误差和 0.2 的 MAE。

带“+”标记的分位数回归线和带“o”标记的基础训练数据(右)
看一下 从零开始实现的分位数回归在我的回购 为本文或看看常见的库如stats models。
结论与展望
如果我们对真实世界的数据建模,丢弃超出某个置信范围的异常值很容易出错。在我们寻找更可靠的选择时,我们讨论了几种显著改进开箱即用线性回归的技术。具体来说,我们专注于教导我们的模型区分我们不希望我们的模型学习的异常值和我们努力理解的潜在过程中承载有价值信息的极值。
在我们的例子中,分位数回归胜出。然而,正如简单性一样,性能也是有代价的。分位数回归没有连续损失函数,但要求我们优化线性规划。在实践中,模型选择总是众多变量中的一个。这个集合为您提供了一系列工具,帮助您更好地认识到哪些离群值可以丢弃,哪些可能包含有价值的信息。
我很想和你聊聊这些和其他话题,所以 不要犹豫,伸出手 。
一些进一步的阅读和参考资料
鲁瑟夫(1984)
鲁瑟夫&范·德里森(2005)
柯恩克(2008):分位数回归
伯勒斯:迭代重加权最小二乘法
https://stats.idre.ucla.edu/r/dae/robust-regression/
https://scikit-learn . org/stable/modules/Linear _ model . html # robustness-regression-outliers-and-modeling-errors
https://docs . pymc . io/notebooks/robust-with-outliers-detection . html # Load-Data
https://jermwatt . github . io/machine _ learning _ refined/notes/5 _ Linear _ regression/5 _ 5
停止解决数据质量问题
创建竞争性数据质量评估的快速指南

数据质量评估是评估您的数据是否符合标准的连续科学过程。这些标准可能与您的业务或项目目标相关联。
随着获取数据的多种不同方式的出现,确保数据质量的需求也在增加。
单独处理单个数据源有时会很有挑战性。比方说,一个客户调查。即使使用在线调查工具,通常也很难将每个受访者的信息标准化。现在想象一下整合和标准化来自 ERP、CRM、HR 系统的数据,更不用说我们现在使用的许多不同的传感器了。如果没有数据质量评估,这将是一辈子的问题。
但是有好消息!我们已经沿着围绕数据获取和管理的复杂性发展。
数据质量评估在数据治理中发挥着至关重要的作用。它们有助于我们在数据管道的各个层面发现不正确的数据问题。它们还帮助我们量化业务影响,并尽快采取纠正措施。
质量差的数据会产生严重的后果。
以医疗保健行业的数据质量问题为例。假设数据录入人复制了一个病人的记录;病人将接受两剂药物,而不是一剂。后果可能是灾难性的。
不管什么行业,上述质量问题都会产生可怕的影响。但是重复只是数据质量问题的一种。我们还需要担心一系列其他质量问题。
让我们假设你正在解决一个库存优化问题。库存通过自动化系统进行监控。如果你的一个传感器发送的数值是原来频率的两倍会怎么样?不可靠的数据会让你囤积已经在仓库里的物品,却错过了所有高需求的东西。
从不同角度看你的数据采集和管理流程。
数据质量有六个维度:准确性、完整性、一致性、及时性、有效性、唯一性。我们还可以将数据责任及其有序性视为其他关键特征。

数据质量的 6 个维度—由作者创建。
这里讨论的不同维度是我们评估数据质量的尺度。在庞大的数据湖中保持 100%的质量几乎是不可能的。数据质量容忍度是我们必须尽早做出的战略决策。但那是为了以后的帖子。
数据准确性。
数据准确性是一个普遍的质量方面,每个人都在努力做到正确。但是数据准确性到底意味着什么呢?
数据准确性是指现有数据对现实的捕捉程度。表面上的原因是数据输入——姓名输入错误和年龄值错误。
但是还有更多灾难性的问题。
NASA 曾经损失了一艘价值 125 美元的飞船。洛克希德·马丁公司,一个英国工程团队,正与美国宇航局合作运行该计划。两个小组使用的不同测量单位导致了与航天器的通信中断。
测量单位是数据不准确的最常见原因。
数据的完整性
数据完整性指的是您的数据集在每条记录上都有所有必需的信息。这些要求取决于应用程序和业务需求。例如,电话号码对于机器学习模型来说只有很少的用处,而对于递送系统来说却是至关重要的。
表单验证和数据库约束有助于减少完整性错误。然而,规划错误通常会对数据质量产生巨大影响。
数据完整性是一种权衡。你在球场上越严格,记录就越少。
这种折衷对于手动和自动数据采集都是有效的。如果您将调查中的所有字段都设置为必填项,您得到的回复不会像预期的那么多。在自动化方面,假设您为来自远程摄像机的数据流设置了 GPS 坐标约束。您安装了一套可能不支持 GPS 的新设备,并向您的数据湖发送不会被接受的数据。
这是一个具有挑战性的维度。随着您从更多来源获取数据,复杂性会越来越高。
数据一致性
数据一致性是指从不同来源收到的数据没有矛盾。因为每个数据源可能有独特的信息度量方式,所以它们有时与其他数据源不匹配。
假设您想要了解某个特定产品的日销售量。您的库存管理会根据剩余的商品跟踪销售情况。您的 POS 会根据售出的商品进行跟踪。退回的商品可能会偷偷进入库存系统,而在 POS 中没有记录。
在集成时,这两个系统会显示不同的日销售量数字。
在理想世界中,这两个系统都应该考虑到回报。但鉴于大规模组织的复杂性,这种情况很少发生。
数据质量的时间线
数据应该在系统需要的时候可用。假设您每周五生成一个报告,但您的所有数据还没有到达;这将严重改变你的组织的决策和方向。
影响数据及时性的原因有几个:
- 有网络问题。如果你认为城市有像样的互联网连接,没有什么可担心的,读读边缘计算。整个概念旨在减少网络延迟。
- 可能存在操作问题。产品退货和每日销售计算也是缺乏及时性的好例子。
- 我们在数据收集方面出现了问题。它们可能是错误的数据输入、传感器故障等。
无效数据
大约 12 年前,我高中毕业。但是我仍然会收到一些针对在校儿童的机构寄来的小册子。这是一个拥有无效数据的极好例子。
无效数据是一组不再有意义的记录。它们毫无用处地占据了空间。而且,当他们被使用的时候也可能是危险的。
无效数据的成本很高,但在某些情况下,使数据无效的规则很模糊。例如,除非你是医生或病人本人,否则我们如何知道病人是否已经完全康复?一些死者可能有一个平均恢复时间范围。但不是全部。在这种情况下,您将无效数据保存在数据存储中,并基于它们做出痛苦(有时是有害的)的决策。
独特性
数据的唯一性是指同一信息没有两次或更多次的复制。它们以两种形式出现;多处记录重复,信息重复。
重复的记录通常很容易挑选。它们只是在相同的数据集中出现不止一次,并且自动移除它们相对简单。
一个好的做法是使用一个键列来施加唯一性约束,而不是整个记录。因为某些重复的条目可能包含一些不再唯一的字段。大多数事务条目都有时间戳,这是一个很好的例子。如果我们不使用一个或几个字段的组合进行重复数据消除,它们就不会显示为重复项。
信息复制是将相同的信息存储在不同的地方。例如,患者的年龄可能在入院表和手术表上。不仅仅是好的设计。
重复的信息是其他质量问题的入口。未能更新所有记录会造成不一致。至少其中一个是不准确的。
另一个不太明显的重复是派生信息。记录年龄和出生日期。一个足以找出另一个。但是存储两者会产生歧义。
如何进行数据质量评估?
我们需要对数据存储中的每个关键领域进行数据质量评估。你能去的最细的地方是字段级。但是您也可以一直检查到数据库级别。

数据质量评估的步骤-由作者创建。
数据质量评估是一个反复验证数据是否符合要求标准的过程。每个迭代将有以下六个阶段。
1。定义数据质量目标。
在“定义”阶段,我们将业务目标转化为数据质量目标,并决定什么是可接受的质量。该矩阵应根据数据质量的六个维度进行衡量。
在大规模应用中达到 100%是不太可能的。但是如果你在处理更小的数据集,你可以对它们更加严格。
如果您是一个发送后续剂量警报的医疗保健应用程序,您需要维护患者服用的每次剂量的日志。每条记录中的时间戳字段对于下一次用药都是至关重要的信息。因此,它应该有一个针对所有六个维度的接近 100%的阈值。
但如果你拥有一家蛋糕店,并且想每年都寄一张生日贺卡,你的规则可以灵活得多。地址或电话号码字段应该具有较高的准确性阈值(比如大约 90%)。然而,他们可以有一个适度的独特性目标(大约 60%),因为人们有时会在购买时给出他们的替代电话号码。
这些阈值也取决于域。正如在最后两个例子中所看到的,与医疗保健相比,第二种情况下的错误成本微乎其微。
这些规则可以是多粒度的。例如,地址列可以有一个唯一的阈值。但是我们也可以设置一个完整性阈值,因为每个记录都应该有电话号码或邮寄地址。
2。数据质量评估
在“评估”阶段,我们根据我们在六个数据质量维度上定义的规则来评估数据集。每一个都会以一个可以接受的分数结束。接受分数是满足条件的记录的百分比。
在小型数据集或数据库上,手动进行这些实验非常容易。然而,在庞大的数据仓库中,您需要一些自动化来验证数据质量。
3.分析评估分数。
数据质量评估不会随着评估阶段的结束而结束。数据质量评估旨在尽早识别业务影响并实施纠正措施。评估业务影响是这个阶段的目标。
这是一项复杂的工作,而且不是领域不可知的。一个组织汇总评估分数的方式与其他组织不同。
但是阶段的目标是显而易见的。我们正在寻找数据质量泄漏的最大漏洞,并将修复它。
4.集思广益寻求改进
在“头脑风暴”阶段,我们合作开发可以弥补我们发现的差距的想法。最好有一个包括来自每个团队的成员的单元,以便计划
- 对它负责的人所希望的;
- 技术上可行;
- 经济上可行。
合意性、可行性和生存能力可以成就或摧毁一个想法。
这里有一个例子。
假设我们在分析阶段发现患者没有在前台调查中填写一些关键信息。您的技术团队可能会建议将调查电子化。电子调查可以实施难以绕过的验证检查。小组中的医生或护士可能会说,这种解决方案是不可取的,因为人们急于进入治疗。前台工作人员可能会说他们很着急,甚至在退房的时候。因此,您的最终解决方案可能是在入院时填写一份初级表格,并在治疗后进行详细的电子调查。
你可能已经注意到解决方案并不总是技术性的。仅仅修复数据管道并不能提高数据质量。有时,它可能需要不明显的非常规策略。
然而,一些最常想到的答案也会有所帮助。这里有几个。
自动化数据收集。
大量的质量问题可以通过这个简单的技巧得到解决。它提高了所有六个维度的质量。如果系统能做到,就让它去做。
尽可能创建电子表格。
当自动化不可行时,下一个最好的解决方案是创建一个带有验证的电子表单。与纸质调查相比,在电子调查中很难跳过关键问题。通过避免数字化你的收藏,你也节省了大量的时间。
创建最新的元数据并与相关方共享。
元数据是数据集的描述。它们包括除数据本身之外的所有内容,以帮助用户理解该数据集存在的原因。通常,元数据包括字段类型、字段验证和约束。
维护更新的元数据有助于加速自动化,并在团队之间清晰地交流需求。
5.实施提高数据质量的策略。
与本评估中的其他五项任务相比,实施需要更多的时间和精力。但是在分析和集思广益阶段花更多的时间可以减轻这里的工作。
我们知道提高数据质量的策略可以是非传统的。做调查的好时机是至关重要的,因为要对电子表格进行验证。因此,这些想法的实现是来自多个团队的承诺。在大多数情况下,一直到 C 组。
6.控制
最后的“控制”阶段更多的是关于数据质量评估的下一次迭代。我们如何测试我们刚刚实现的策略?做同样的矩阵工作,或者我们应该需要一套不同的措施?我们应该提高(或降低)质量目标吗?下一次迭代的合理时间框架是什么时候?
最后的想法
在这篇文章中,我讨论了数据质量维度和评估的基础。
数据质量评估是维护可靠数据源的关键。它们对于数据驱动的决策至关重要。根据应用程序的领域和类型,低质量数据的影响可能从微不足道到灾难性不等。
为了理解数据质量评估,我们必须首先理解数据质量的六个维度。我们根据这六个维度进行质量评估。
数据质量评估本身是一个循环过程。每个迭代可以有六个阶段,以确保正确地识别质量差距,并采取措施来实现它们。一次迭代的学习有助于调整下一次迭代的目标。
还不是中等会员?请使用此链接 成为会员 因为我为你免费推荐赚取佣金。
停止在 Python 应用程序中硬编码敏感数据
使用将您的设置和凭据保密。环境文件

作者制作的图像
作为一名数据科学家,我每天使用 Python 来构建依赖于凭证和敏感设置的应用程序。
下面是一些例子,我想了想:
- 访问第三方服务的 API 密钥
- 密码和凭证
- 电子邮件地址或个人数据(姓名、年龄、社会保险号等。)
- 调试标志
- 主机,网址,URI
很明显,更多的事情。
其中一些设置本质上是私有的。其他人很敏感,因为他们可以提供对网络攻击或威胁的见解。
️️️️⚠️️This 不言而喻,但我们还是要说出来 :这些数据永远不应该被硬编码到你的 python 文件中。你必须找到一种方法来保持它的私密性,永远不要与世界分享(尤其是 Github)
在这篇快速的帖子中,我们将看到如何使用环境变量和一个名为
***.env***的特殊文件来解决这个问题。
我们还将学习如何使用***python-dotenv***模块与该文件交互,并使您的敏感数据隐藏起来。
事不宜迟,我们来看看🔍
什么是环境变量?
环境变量是保存您不想硬编码到程序中的数据的变量。它们被抽象出来,从代码中删除。
它们的值存在于你的操作系统中;它们可以是内置的,也可以通过自定义应用程序来设置。
一些内置的环境变量

“PATH”环境变量—作者截图

“SHELL”环境变量—作者截图
环境变量由键/值对组成,您可以使用它们来存储不同类型的数据。
- 域名
- 执行模式(生产、开发、暂存)
- 认证密钥、登录或密码等凭证
- 电子邮件地址等。
如何使用 Python 访问环境变量?
要访问环境变量,您需要使用os模块,该模块提供了读写这些值的实用程序。
这似乎是隐藏敏感数据的一种简便方法,对吗?
想象一下,您需要在代码中使用一个 API 键,而不泄露它的值。您需要做的就是从环境变量中加载它:
**api_key = os.gentenv("SECRET_API_KEY")**
但是等等,我不记得把我的 API 键设置为环境变量了。我该怎么做?
有两种常见的方式:
- 通过键入以下命令:
**export SECRET_API_KEY=XXXXXXXXXXXX**
- 或者在你的
.**zshrc**、.**bashrc**或.**bash_profile**中添加相同的行,并对其进行采购
如果你需要只在特定项目中使用的环境变量,我不推荐这两种方法。其实,
- 如果你使用你的终端,你需要记住每次在运行你的程序之前设置你的环境变量,这似乎是不对的。
- 如果每次需要添加新的环境变量时都要修改
.zshrc或.bashrc,这些文件很快就会被大量不必要的信息弄得乱七八糟。如果您的环境变量与非常具体的项目相关联,那么将它们设置在一个可以随时随地访问的全局范围内是没有意义的。
但愿, **.env** 文件似乎能解决这个问题。
。env 文件:在局部范围内设置环境变量的一种方式
**.env** 文件首先是文本文件,包含应用程序所需的所有环境变量的键/值对。
它们使您能够在不污染全局环境名称空间的情况下使用环境变量。事实上,每个单独的项目都可以有自己的.env文件。
这里有一个例子:
文件通常放在项目的根目录下。要访问其中列出的值,您需要安装**python-dotenv**库。
**pip install python-dotenv**
然后,您只需要添加两行代码:
- 一个要导入的库:
**from dotenv import load_dotenv**
- 寻找一个
**.env**文件并从中加载环境变量
**load_dotenv()**
👉当这两行代码被执行时,环境变量被注入到项目运行时。当项目终止时,它们被刷新,并且在任何时候都没有被添加到全局命名空间中。
正在访问。python 中的 env 文件
🔴永远不要承诺。env 文件或将它们推送到 Git
一个.**env**文件的全部意义在于将敏感数据从代码库中具体化
因此你不应该将这个文件版本化或者推送到 Github。
您可以将它保存在您的本地开发环境中进行测试,并且永远不要与公众共享它。
避免这种不必要情况的一个简单方法是将.env添加到您的.gitignore文件中:
**.env**
让我们结束它
Python 应用不仅仅是一系列指令和代码。它们还与数据和配置设置相关联。
大多数时候,这些配置数据是敏感的,需要以保密的方式访问。
希望环境变量和.env文件能够处理这种情况。
要开始使用.env文件,您可以这样做
- 使用 pip 安装
python-dotenv模块 - 使用项目中适当的环境变量创建
.env文件 - 将它添加到
.gitignore文件中以防止 git 提交它 - 使用
python-dotenv模块将设置加载到 Python 文件中
资源
虽然这个主题并不复杂,但我浏览了一些很棒的资料,并学到了一些我不知道的关于环境变量和使用.env文件的知识。
这是一些资源的精选列表。
python-dotenv文档- https://better programming . pub/getting-rid-of-hard coded-python-variables-with-the-dotenv-module-d 0 aff 8 ce 0 c 80
- https://dev . to/Jake witcher/using-env-files-for-environment-variables-in-python-applications-55a 1
- https://www.askpython.com/python/python-dotenv-module
- https://www.youtube.com/watch?v=YdgIWTYQ69A&ab _ channel =乔纳森·索玛
- https://www.youtube.com/watch?v=rKiLd40HIjc
- 一个漂亮的网站生成。gitignore 文件:https://www.toptal.com/developers/gitignore
也许你也会学到一些东西😉
感谢阅读🙏
如果你已经做到这一步,我真的很感谢你的时间。
今天就这些了。直到下一次的其他编程技巧👋

由 Unsplash 上的 Karsten Winegeart 拍摄
新到中?你可以订阅每月 5 美元,并解锁无限的文章——点击这里。
停止雇佣“数据科学家”
意见
将人工智能模型投入生产需要一系列技能

想实现一个 AI 项目?这不仅仅是建立一个伟大的模型。这篇文章描述了为什么生产数据科学需要一个以上的人来扮演“数据科学”的角色。
"如何将人工智能模型投入生产?"
有无数这样的例子,公司已经开发出了模型——只是让它们坐在炼狱里,永远无法投入生产。在人工智能社区中有很多关于这个话题的讨论。
人们并不是不知道如何将人工智能模型投入生产。太糟糕了。
为什么?
很难。
将一个模型投入生产(并保持)和构建它需要同样多的工作。
部署模型通常需要有人
—监控管道的可用性
—监控模型随时间的准确性
—不断用新的数据点重新训练模型
—版本代码、模型,甚至可能是数据集
—等等。
很复杂。
如果你是一名知道如何调整学习率和辍学值的数据科学家,你是否也有信心使用 DevOps 工具,如 Terraform 、 Kubernetes 、 Slurm 、普罗米修斯、 Nomad 或芹菜?
大概不会。
数据科学家大概不会感兴趣。
有些人当然对 DevOps 的工作非常感兴趣,也非常熟练。你通常可以在像“开发工程师”这样的角色中找到这些人
这些类型的人擅长构建稳定的、可伸缩的、易于维护的基础设施。他们擅长自动化流程。他们关注为他们最终用户(开发人员)执行怪异的角落案例的计划的细节。他们写测试。
对一些人来说,这绝对是令人着迷的,但它与传统的“数据科学家”活动(如模型开发)截然不同。这是一种维护心态,而不是重复概念证明的创新心态。
让专家成为专家。
人工智能的开发和生产都很复杂。对于人工智能(甚至比传统的软件开发更是如此),最佳实践仍在开发中。这是一种投资,投入其中,找出当前做事情的最佳方式。
致力于磨练其模型构建技能的数据科学家不应该因为学习 100 多种 DevOps 技术而削弱他们的专业知识。从事平台工作的数据科学家不会像从事数据科学的数据科学家那样高效。
同样,您的 DevOps 工程师可能没有时间成为您的数据科学家所做工作的专家。有人必须对基础设施/维护/监控任务有深入的了解。
不要试图让一个人扮演所有的角色。我们需要这个难题各个方面的专家。
行动呼吁
公司,不要试图雇佣数据科学家来填补所有的角色。
如果你还没有看到,这里有一个来自谷歌的机器学习中隐藏技术债务的伟大图表(来自 2015 年,但消息是永恒的)。

只有一小部分真实世界的 ML 系统由 ML 代码组成,如中间的小黑框所示。所需的周边基础设施庞大而复杂。”——图片和说明来自谷歌
中间那个小黑框是 ML 代码。对于该项目,您有多少工作需求?其他箱子你开了多少?
“数据科学家”不应该是一个实现人工智能平台不同组件的包罗万象的角色。没有所谓的“全栈数据科学家”
没有预算雇佣一个以上的人?不是借口。加入众多人工智能服务公司之一。
开发者们,不要再试图成为数据科学家了。
除了“如何制作一个更好的 XYZ 模型”,还有一系列有趣的人工智能工作这些地区需要帮助!通过专攻与模型开发稍微接近的东西,您有机会形成最佳实践。
教育可能会更难,因为培训资源更少,但这是值得的,因为你的需求量很大。
(对教育团体大声疾呼:我们需要更多关于 MLOps 的材料,尤其是全面的真实世界培训。)
人工智能初创公司,有助于使人工智能的 DevOps 方面更容易。
最近有一堆文章提出了人工智能平台架构。他们试图将众多组件收集到一个视图中。这是一个很好的起点,但是由此产生的计划写起来比实现起来容易得多。
(几个铺天盖地的例子: A16Z 的“现代数据基础设施的新兴架构”&现代数据工程师路线图 2021”
对于一般的组织来说,实现这些由 20 个部分组成的流程图太复杂、太困难了。尤其是当我们还没有雇佣一系列专家来构建这个平台的时候。
我们需要简化管理人工智能管道的工具。
结论
组织经常对部署他们的人工智能模型所付出的努力感到惊讶。但我们仍处于将人工智能部署到现实世界场景的早期,因此公司应该计划这是一项艰巨的任务,不能捆绑到现有的角色中。创建一个单独的工作流来设计人工智能项目的 DevOps 方面,并让数据科学家不断建立模型。
停止(并开始)雇佣数据科学家
提高人才保留率的三种“停停走走”策略

TL;大公司的许多数据科学家正在流失到小公司。高管和经理应该怎么做?我提出了三个“停停走走”的策略来帮助定义一种新的招聘和工作方式。
免责声明 :所有观点均为本人观点;它们并不反映我的雇主。本文所用数据全部来自 Kaggle 数据科学调查 。所有的观察都来自我在大公司和小公司的数据科学团队工作的经历。
喜欢读什么? 跟我上 中LinkedIn,以及Twitter。如果您想获得经过处理的调查数据集,请联系我。**
发生了什么事?
大公司正在失去大约 20%的数据科学家;他们中的许多人可能去了创业公司,而一些人可能已经离开了这个行业。与科技行业 13%的平均离职率相比,大公司的数据科学团队显然面临着严重的人员流失问题。

2021 年 5 月 8 日提取的 Kaggle 调查数据;2020 年 n=4033,2019 年 n=3502,跨主要国家。仅包括自称为“数据科学家”或“机器学习工程师”的受访者
数据科学家为什么要跳槽?
数据科学家辞职主要是因为期望与现实之间存在差距。
大多数数据科学家希望将大部分时间花在与建模和洞察发现直接相关的工作上,而数据工程师则准备干净的数据集并构建管道。然而,他们花了将近 50%的时间在数据收集和清理上,就像数据工程师一样。

2021 年 5 月 8 日提取的 Kaggle 调查数据;2018 年 n=584,主要国家都有。仅包括自称为“数据工程师”或“数据科学家”的受访者预计花费的时间基于作者的观察和对 Udacity、Coursera、Udemy 和 Youtube 上数据科学课程相关内容建模的粗略估计时间。
大多数数据科学家希望用尖端的解决方案来解决复杂的问题。与大公司的同行相比,小公司的数据科学家更有可能**只定期使用高级算法。

2021 年 5 月 8 日提取的 Kaggle 调查数据;2019 年 n=3502,主要国家都有。仅包括自称为“数据科学家”或“机器学习工程师”的受访者传统算法包括线性和逻辑回归以及随机森林。高级算法包括梯度推进法、贝叶斯法、CNN / RNN 和变压器网络。
我们做什么呢
我们需要接受流动,因为这很正常。但是,比流失率最高的行业高出 55%的人员流动率意味着我们现在需要采取一些措施。
对于大公司的招聘经理和高管来说,让我们培养一种新的、可持续的招聘和工作方式。加乒乓球台有帮助,但解决不了问题。
- 招人。停止雇佣那些只关心酷的东西的数据科学家,开始雇佣那些以和别人一起制造和运输东西为荣的数据科学家。我们应该利用招聘流程重新设定数据科学家的期望和角色——从校园活动、职业网络研讨会到实际面试。让来自业务、产品管理、数据工程和软件开发的团队参加面试。这将引发连锁反应,帮助有抱负的数据科学家培养正确的期望和技能。
- 激励。停止关注“科学”,开始一起庆祝“数据+科学+工程”。自 Kaggle 早期以来,这是一个受欢迎的数据科学竞赛平台,它将数据科学纳入了主流职业道路,我们一直过度关注数据科学个人的算法工作和成功。事实上,要在现实世界中取得成果,需要数据工程师和软件开发人员的团队努力,包括大量艰苦(和聪明)的工作。他们构建了承载科学家开发的模型数据的应用程序的主干。可悲的现实是,许多用户看不到也不理解后端;因此,他们低估了无名英雄的工作。数据、科学和工程(以及商业战略、UX 设计和产品管理)必须走到一起,共享聚光灯。
- 交货。停止在孤岛中运行数据科学团队,开始创建数据产品平台。很多大公司会雇人到专门的数据科学团队;这筑起了墙,强化了错误的期望。我们应该采用已被证明成功的产品开发团队模式——他们按照核心产品类别或功能进行组织。在这种情况下,我们应该组织并雇佣人员进入数据产品舱。每个 pod 是一个专门的或临时的团队,由了解数据并拥有设计和运输可行产品的所有技能的人组成统称。基本角色包括产品经理、数据工程师、数据科学家和全栈开发人员。最重要的是,成功归功于产品的质量和整个团队的贡献,而不是某个特定的角色或个人。**
喜欢读什么? 跟我上 中LinkedIn,以及Twitter。如果您想获得经过处理的调查数据集,请联系我。还有,看看我的《 对机器学习的影响 》指南。它帮助数据科学家更好地解决问题、设计和交流。****
对数据科学职业和劳动力市场趋势感兴趣?阅读我关于这个话题的其他文章。
**** ****
停止学习数据科学去寻找目的,找到学习数据科学的目的
我如何改变教育模式,成为更有效的数据科学家
数据科学家在需求,这是毫无疑问的。工作报酬丰厚,有大量的空缺职位,在这个后大流行时代的数字世界里,这个行业似乎还在发展。因此,数据科学专业的学生也是世界劳动力中日益增长的一部分也就不足为奇了。但是学习数据科学并不容易。事实上,难,之所以难有几个很好的理由:
1.数据科学作为一种职业融合了许多不同的子专业,这些子专业本身就是职业,例如数据工程、编程、统计和数据可视化。
2.这个行业以及相关的工具和技术正在快速发展,这使得人们很难知道应该把研究的重点放在哪里。
3.教育环境(大学、数字教程)中教授的数据科学与企业中使用的数据科学之间存在差距。
4.由于所需知识的广度很大,人们很容易对自己向未来雇主有效传达教育价值的能力失去信心。
我记得自己试图从一名精通数据的学术研究人员成为行业数据科学专家的经历。我尽可能地接触了所有的教程、博客和 MOOC。我沉浸在行业新闻和趋势中。我把水桶装满,发现我学得越多,就越意识到自己不知道。我压力很大,对自己拥有的技能缺乏信心,感觉自己像一个冒名顶替者,参加数据科学面试,希望自己不会因为没有在损失函数上花足够的时间而被“逮到”。
我让自己沉浸在数据科学教育中,希望我的广泛接触能让我实现自己的目标,并获得更好的薪水。我当时没有意识到的是,我已经本末倒置了。我是如此渴望学习,以至于我把所有的时间都花在学习很多“东西”上,从来没有停下来问自己;所有这些“东西”是如何聚集在一起解决实际问题的?
请允许我告诉你一个显而易见的秘密,大多数企业不关心数据科学“事情”大部分商家只关心那些东西能不能解决商业问题。因此,问题就在这里,试图学习所有数据科学的工具,以便你的简历可以充满不断扩大的“事物”列表(Python、R、回归、随机森林、朴素贝叶斯、马尔可夫链、支持向量机、k-means 聚类、XGBoost、卷积神经网络、自然语言处理等等),这是徒劳的。
这些“东西”不会引导你达到你的目标,因为你的目标仅仅是由你觉得有价值的地方来定义的。当您允许您不断发展的数据科学知识应用于解决问题时,您会感到有价值。能够交流你如何利用数据科学的一些工具来解决问题,将比简单地列出你在一个或另一个课程中接触过的所有算法在业务上走得更远。
那么,我应该如何学习数据科学呢?
总之先找个目的。你在乎什么?你的激情在哪里?你想解决什么问题?一旦你有了一个列表,选择一些东西,并考虑如何应用你的数据科学知识来解决与该兴趣相关的问题。

皮沙贝
有目的的数据科学的好处
通过首先找到您的目标,您将在上下文中处理您的数据科学教育,您将寻求学习的工具将不会感到势不可挡,因为它们中有应用意义的必然会更少。
知识、激情和对问题的理解也会开启你的创造力。创造性的解决问题是看我们对两个或更多不同领域的理解如何以新颖的方式结合起来。如果我们沉浸在“罐装”数据集和冷静分配的问题的环境中,只学习数据科学,我们就不再能够跨越我们在多个领域的知识深度。
通过首先找到你的目的,你将很快了解到有许多不同的数据科学解决方案来解决同一个问题。换句话说,数据科学中很少有对错之分,更常见的是业务问题可以用无数种方式解决。有些解决方案比其他的更好吗?当然可以。但这并不意味着那些不太理想的就是错的,而是他们没有那么好。有了足够的钱和时间,总会有一个“更好”的解决方案,所以最好不要陷入这种恶性循环。相反,专注于你所拥有的知识如何能够带来比以前更多的价值,或者通过揭示别人没有的新见解来增加现有的解决方案。
通过首先找到你的目的,你将解决大多数数据科学课程中经常没有讲授的问题,但它们是企业数据科学家每天面临的问题。以寻找正确数据的简单问题为例。大多数数据科学课程不会教你数据发现的价值,但在企业中,数据科学家通常负责发现新的数据集并与之融合,以进一步实现收集的数据和聘请的数据科学家的价值。首先,有目的地学习数据科学将迫使你寻找获取与你的问题最相关的数据的方法,这将要求你访问、争论和设计这些数据,以便可以用机器学习模型进行训练。
最后,通过首先找到你的目的,你将知道如何传达你所建立的解决方案的价值。
我的目标是什么,它如何改变了我的教育?
我的目标是社会正义。我想使用数据科学的工具和技能来产生揭露不公正的见解,为积极的社会变革提供解决方案,并帮助我们认识到人类偏见的影响。

皮沙贝
在我的第一个项目中,我想帮助第三班工人识别车辆犯罪的口袋,以支持更安全的停车决策。我必须找到当地的公共警察报告数据,并将其与其他数据源(如人口普查数据)混合。利用我所掌握的数据科学知识,我发现我可以建立一个预测模型,根据周围位置的特征来预测汽车发生车辆犯罪(例如盗窃、故意破坏)的可能性。这个项目让我学会了基本的数据争论,如何导出一些地理空间特征,测试不同分类模型的准确性,如随机森林、逻辑回归和朴素贝叶斯,使用 Tableau Public 的基本可视化,以及如何设置一个管道以在每次警方数据刷新时刷新仪表板。
我还能解决其他问题吗?当然了。我是否可以使用其他工具来解决这个特定的问题?绝对是。我是否提出了最好的解决方案,甚至是市场上唯一的解决方案?没有机会,但我的解决方案比什么都没有的更好。
我不仅学到了上面提到的具体工具,还对数据科学的进程有了更多的直觉。我能够更清楚地阐明为什么我想要使用具有特定数据类型的特定分类模型。最重要的是,我能够充满激情地谈论这些工具如何让我通过组合数百个数据点来做出明智的决策。
现在,当面对新的目标并被问及是否有数据科学解决方案来克服与该目标相关的问题时,我不再对自己不知道的事情缺乏信心。我用这个目的来应用我所知道的,解释我的方法,并带着我能做到的信心去发现新的东西。
比如参与学习更多关于数据科学的知识?加入我。
停止一次性编码基于时间的要素
循环特征的特征转换基本指南

特征工程是数据科学模型开发流程的重要组成部分。数据科学家将大部分时间用于分析和准备要素,以训练稳健的模型。原始数据集由各种类型的要素组成,包括分类要素、数值要素和基于时间的要素。
机器学习或深度学习模型只理解数字向量。分类和基于时间的特征需要编码成数字格式。对分类特征进行编码的特征工程策略多种多样,包括一键编码、计数矢量器等等。
基于时间的特性包括**day of month**、**day of week**、**day of year**、**time**。基于时间的特征本质上是周期性或季节性的。在本文中,我们将讨论为什么对于循环特性应该避免使用一键编码或哑编码,而是讨论并实现一个更好、更优雅的解决方案。
为什么不是一次性编码?
独热编码是一种将分类特征转换成数字向量的特征编码策略。对于每个特征值,一键转换创建一个新的特征来区分特征值的存在与否。

(图片由作者提供),一键编码示例图
一键编码为每个实例创建 d 维向量,其中 d 是数据集中特征值的唯一数量。
对于具有大量唯一特征值或类别的特征,一键编码不是一个好的选择。还有各种其他技术来编码分类(序数或名义)特征。
阅读下面提到的文章,了解分类特征的几种特征编码策略:
基于时间的特征,如**day of month**、**day of week**、**day of year**等,具有周期性,并且具有许多特征值。一键编码**day of month**特征产生 30 维向量,**day of year** 产生 366 维向量。一次性编码这些特征并不是一个好的选择,因为这可能会导致维数灾难。
想法:
对这些循环特征进行编码的优雅解决方案可以是使用数学公式和三角学。在本文中,我们将使用三角学的基本公式,通过计算特征的正弦和余弦来对循环特征进行编码。
(作者代码)
**day of week** 该特征有 7 个唯一的特征值。取特征值的正弦和余弦将创建二维特征。

(图片由作者提供),计算星期特征的正弦和余弦
现在,代替使用一键编码创建 7 维特征向量,2 维变换的特征向量将用于表示整个特征的目的。现在,让我们用散点图来可视化新的二维变换特征向量。

(图片由作者提供),星期几特征的正弦和余弦散点图
散点图清楚地显示了**day of week** 特征的循环性质。这 7 个特征值(从 0 到 6)现在被编码成一个二维向量。
**day of month** 和**day of year** 本质上是循环的,分别具有 31 和 366 个特征值。对它们进行一次性编码将增加最终数据集的维数。因此,使用特征值的三角变换将达到编码分类特征的目的。


(图片由作者提供),左:计算正弦和余弦,右:正弦和余弦散点图;一个月中的某一天功能


(图片由作者提供),左:计算正弦和余弦,右:正弦和余弦的散点图;一年中的某一天功能
结论:
所讨论的基于三角学的特征变换可以在任何循环出现的特征上实现。一次热编码适用于相对少量的分类值,但不建议对具有许多特征值或类别的要素进行一次热编码。
阅读我以前的一篇关于特征编码的文章:
参考资料:
[1] Scikit-learn 文档:https://sci kit-learn . org/stable/modules/generated/sk learn . preprocessing . onehotencoder . html
感谢您的阅读
不要过度使用“+”来连接 Python 中的字符串
这里有三种选择,它们将帮助你做更多的事情,而不仅仅是连接字符串

数据科学家在收集和清理数据时必须处理的一项常见任务是处理字符串。这包括格式化以及连接字符串(也称为字符串连接)。
在 Python 和其他编程语言中连接字符串就像使用加号运算符+一样简单。在 Python 中,您可能已经使用下面的代码数百次来连接字符串。
>>> str1 = "Hello "
>>> str2 = "World">>> str1 + str2
"Hello World"
这种方法很好。然而,这在处理列表、数据框架时变得不切实际,或者如果你的目标是可读性。
在本文中,我将向您展示 3 种替代方法,它们不仅可以帮助您连接字符串,还可以让您轻松地连接列表元素,在 Python 中正确格式化字符串,并使调试变得不那么复杂。
f 弦
Python f-string 是在 Python 3.6 中引入的,它允许我们像加号操作符那样连接字符串;然而,f 字符串使用花括号{}来存储将被格式化为字符串的值。
例如,让我们使用 f-string 打印句子“Python 由吉多·范·罗苏姆创建,并于 1991 年发布”。在这种情况下,我们将使用名称“吉多·范·罗苏姆”和年份“1991”作为变量。
让我们来看看 f 字符串的语法。
>>> name = 'Guido van Rossum'
>>> year = 1991>>> f'Python was created by {name} and released in {year}'
它的语法比+操作符更简洁,不是吗?
您也可以使用 f-string 格式化文本数字或日期。假设我们从datetime库获取了今天的日期,并希望将其添加到文本中。
>>> import datetime
>>> now = datetime.datetime.now()>>> f'Today is {now:%B} {now:%-d}, {now:%Y}'
Python 会打印‘Today is October 1, 2021’。作为旁注,%B、%-d 和%Y是格式代码。您可以在这个Python strftime cheat sheet中找到其他可用的时间格式代码。
现在让我们格式化一个数值。
>>> gpa = 3.355>>> f'His GPA is {gpa:.1f}'
Python 会将数字四舍五入到第一个小数,并打印出‘His GPA is 3.4’在这个例子中,.f是一个表示“浮点数”的格式代码,所以在上面的例子中,我们指定了精度的1位数。你可以在这个链接上找到更多类似这些的格式代码。
f-string 很酷的一点是,开发人员不断增加新的功能。Python 在 Python 3.8 中引入了一个全新的特性,将=添加到 f 字符串中。这简化了频繁的打印调试,因此不用编写下面的代码来打印变量名及其值。
>>> python_version = 3.8
>>> f"python_version={python_version}"'python_version=3.8'
你现在只能这样写:
>>> f"{python_version=}"'python_version=3.8'
加入()
当您想要连接存储在一个列表中的多个字符串时,最简单的方法是使用join()方法。您只需要在使用join()方法之前指定一个分隔符。
假设我们之前使用了同一个句子,但是现在每个单词都存储在一个名为words的列表中。
words = ['Python', 'was', 'created', 'by', 'Guido', 'van', 'Rossum', 'and', 'first', 'released', 'in', '1991']
我们使用 join 方法和一个空字符串‘ ’作为分隔符来构建之前的句子。
>>> ' '.join(words)
join()方法不仅是连接列表元素的有效方法,而且由于其性能优于+操作符。[join()](/do-not-use-to-join-strings-in-python-f89908307273)方法可以比使用 [+](/do-not-use-to-join-strings-in-python-f89908307273) 来连接列表中的字符串快 4 倍。
str.format()
我们可以使用str.format()在 Python 中连接字符串。我们只需要为我们想要添加到字符串中的每个变量插入花括号{}。花括号操作符将帮助我们在 Python 中格式化字符串。
让我们看一看。
>>> name = 'Guido van Rossum'
>>> year = 1991
>>> "Python was created by {} and released in {}".format(name, year)
str.format()优于+操作符的一个原因是我们不需要在连接之前显式地将整数转换成字符串。在上面的例子中,我们不必像使用+操作符那样将year变量转换成字符串。
不幸的是,使用str.format()的一个缺点是,当处理许多变量和长字符串时,代码会变得很长。这就是 f 弦优于str.format()的原因。
就是这样!这是 Python 中用+操作符连接字符串的 3 种替代方法。明智地使用它们来编写更好的 Python 代码。
与 3k 以上的人一起加入我的电子邮件列表,获取我在所有教程中使用的 Python for Data Science 备忘单(免费 PDF)
如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑报名成为一名媒体成员。每月 5 美元,让您可以无限制地访问数以千计的 Python 指南和数据科学文章。如果你使用我的链接注册,我会赚一小笔佣金,不需要你额外付费。
https://frank-andrade.medium.com/membership
停止以 CSV 格式保存数据框
使用各种数据格式进行读取和保存操作的基准时间比较

数据科学就是与数据打交道。整个数据科学模型开发流程包括数据争论、数据探索、探索性数据分析、特征工程和建模。读取和保存中间文件是模型开发管道中的一项常见任务。数据科学家通常喜欢以 CSV 格式读取和保存熊猫的数据帧。处理小规模或中等规模的数据非常容易,不需要太多的开销,但当处理大规模数据集时,由于资源的限制,工作流会变慢。
CSV、Excel 或其他文本文件格式在处理大型数据时失去了吸引力,或者您的工作流涉及频繁的读取和保存操作。有各种二进制数据格式可以优于 CSV 文件格式,熊猫包支持使用这种数据格式。在本文中,我们将比较各种数据格式的内存消耗、保存和读取操作次数,并进一步总结每种用例可以使用的数据格式。
要比较的数据格式:
在本文中,我们将比较下面提到的 8 种数据格式 的指标,包括 与熊猫的读取时间,与熊猫的节省时间,以及 的磁盘内存消耗。
- CSV(逗号分隔值)
- 压缩 CSV
- JSON
- 泡菜
- 压缩泡菜
- 镶木地板
- HDF
- 羽毛
数据:
我将使用来自 Kaggle 的纽约市出租车持续时间数据集来比较基准数据,该数据集有 1,458,644 条记录和 12 个特征。
基准:


(图片由作者提供),左:读取和保存时间对比(秒),右:内存消耗(MB)
建议:
通过观察上述每种文件格式的读取和保存操作以及内存消耗的基准数,我们可以针对不同的用例遵循以下建议:
- 要保存会话之间的数据或中间文件,请使用 【泡菜】 、 羽毛 或 拼花 格式。酸洗为最优先推荐,因为它的读数最低,节省时间。
- 要以最小的大小保存数据或优化内存消耗,请使用压缩 pickle、parquet 和压缩 CSV 格式。最推荐使用 压缩泡菜 ,因为它的内存消耗最少(比 CSV 格式优化了 78%)。
- 压缩 pickle 格式占用的内存最少,但是在保存数据时需要很多时间。人们可以使用 拼花 格式,这种格式具有相对更快的读取和节省时间,并且比标准 CSV 格式优化了 70%。
- 要保存非常大的数据帧,使用 HDF 格式。
- 要读取不支持其他格式的另一个平台(非 Python)上的数据,使用 CSV , 压缩 CSV 格式。
- 要使用 Excel、Google Sheets、记事本查看和观察数据,请使用 CSV 格式。
结论:
在本文中,我们讨论了可用于保存原始数据和中间数据的 8 种数据格式,并比较了基准时间数和内存消耗。上面讨论的建议是通过观察基准数字和领域知识得出的。没有遵循特定数据格式的经验法则,但它取决于用例。
拼花、压缩泡菜、压缩 CSV、羽毛数据格式可用于优化数据的内存消耗。与标准 CSV 格式相比,使用这些文件格式可以优化高达 78%的内存。
Pickle、Parquet、Feather 文件格式优于标准 CSV 格式,因为它们具有更快的读取和保存能力。
读取熊猫数据框中的数据后,可以通过降级每个要素的数据类型来优化内存使用。阅读下述文章中的以了解实现和用法:
参考资料:
[1]熊猫文献:【https://pandas.pydata.org/docs/reference/io.html
感谢您的阅读
停止将数据视为商品
为什么 87%的应用 ML 项目从未进入生产——从超过 50,000 个模型的培训中学到的

弗兰基·查马基在 Unsplash 上拍摄的照片
当你在“走向数据科学”上阅读这篇文章时,我可能不需要告诉你在 ML 研究中发生的所有令人惊讶的事情。我也不需要告诉你人工智能应用于现实世界用例的潜力。然而,87%的应用深度学习项目仍然在概念验证阶段失败,永远无法投入生产[1]。
在这篇文章中,我将更深入地探讨我们在 hasty.ai 与我们的用户一起工作于超过 10,000 个项目和培训超过 50,000 个视觉模型的经验。我总结了我们观察到的许多应用 ML 项目失败的原因,并提出了解决问题的方法。
TL;我们需要从以模型为中心到以数据为中心的 ML 开发的范式转变。
免责声明:在这里,在hasty . ai,我们正在为以数据为中心的 visionAI 构建一个端到端的平台。因此,我使用的所有例子都来自视觉空间。然而,我相信提出的概念仍然与人工智能的其他领域相关。
许多应用 ML 团队模仿研究思维
为了理解为什么 87%的应用 ML 项目会失败,我们需要后退一步,了解今天大多数团队是如何构建 ML 应用程序的。这是由 ML-research 的工作方式决定的。
研究团队大多试图为特定的任务重新定义艺术(SOTA)的状态。SOTA 是通过在给定数据集上达到最佳性能分数来衡量的。研究团队保持数据集固定不变,并试图改进现有方法的一部分,以实现. x%的性能提升,因为这将使他们得以发表。
由于研究中的每一次炒作都是由这种追逐 SOTA 的心态产生的,许多致力于应用 ML 的团队都采用了这种心态,相信这也会引领他们走向成功。他们投入大量资源开发模型,将数据视为已知或易于外包的东西。
在应用 ML 中追逐 SOTA 是一个失败的秘诀
然而,这种想法是有缺陷的。
首先,研究和应用 ML 的目标明显不同。作为一名研究人员,你希望确保你的工作是前沿的。另一方面,如果你在应用 ML 中工作,你的主要目标应该是让它在生产中工作——如果你使用一个五年前的架构,那又怎么样?
第二,你在现实世界中遇到的情况与研究环境大相径庭,使事情变得更加复杂。

在研究中,团队在完美的条件下处理干净的结构化数据。然而,在现实中,数据看起来非常不同,团队面临着一系列全新的挑战。图片由作者提供,由imgflip.com创作。
让我告诉你一个我最喜欢的城市神话,从自动驾驶的世界来说明这一点:
和大多数 ML 研究一样,原型是在海湾地区开发的。在中西部进行首次试驾之前,车队花了数年时间调整模型。当最终开车到中西部时,模型坏了,因为所有的训练数据都是在阳光明媚的加利福尼亚收集的。模型无法应对中西部恶劣的气候,因为突然路上有雪,相机上有雨滴,让模型感到困惑。
附注:我一遍又一遍地听到这个故事,但从未找到可引用的来源。如果你知道一个,如果你能和我分享,我会非常感激。
这个轶事对于一个更大的问题来说是一个完美的象征:你在研究环境中会遇到相当愉快的条件,就像加利福尼亚的好天气一样。然而,在实践中,条件更加艰苦,事情变得更具挑战性;你不能应用所有理论上可能的东西。
如果你想做应用 ML,这意味着一系列全新的挑战。不幸的是,仅仅通过改进底层架构和重新定义 SOTA 是无法解决这些问题的。
应用 ML ≠研究
更准确地说,这里有 5 种应用型 ML 环境与研究型 ML 环境的不同之处:
1—数据质量变得重要
在应用 ML 中,有这样一句话:
“垃圾进,垃圾出。”
这意味着你的模型将只和你训练它们的数据一样好。大多数研究人员忽略了跨领域的 10 个最常用的基准数据集对于训练和验证数据的误分类率为 3.4%[2]。
最常用的基准数据集的标签错误率为 3.4% [2]。尽管在比较模型架构时可以忽略这一点,但在应用 ML 时,这可能会产生严重的后果。
许多研究人员认为,不要太关注这一点是合理的,因为您仍然可以通过比较模型架构获得有意义的见解。然而,如果你想在你的模型上建立一些业务逻辑,举一个例子,确定你看到的是狮子还是猴子是非常关键的。
2—通常情况下,您需要自定义数据
研究中使用的大多数基准数据集都是巨大的通用数据集,因为目标是尽可能地一般化。另一方面,在 applied ML 中,您最有可能遇到公开的通用数据集中没有表示的特殊用例。
因此,您需要收集和准备自己的数据,以恰当地代表您的特定问题。通常,选择正确的数据对模型性能的影响比选择哪种架构或如何设置超参数更大。

在 ML 开发过程早期做出的决策对最终结果有着巨大的影响。特别是,开始时的数据收集和标记策略至关重要,因为级联随着每一步而累积。图片摘自[3]。
在论文“每个人都想做模型工作,而不是数据工作:高风险人工智能中的数据级联”[3]中,谷歌人工智能实验室的一个团队令人印象深刻地展示了在项目开始时选择错误的数据策略会如何对模型后来的性能产生不利影响。我真的推荐阅读整篇论文。
3—有时,您需要处理小数据样本
在研究中,对低模型性能的一个常见回答是:“只需收集更多数据。”通常,当你试图向一个只有几百个例子的会议提交论文时,他们很可能会拒绝你。即使你实现了高性能,争论也会是你的模型过度拟合。
然而,在应用 ML 中,收集大量的数据集通常是不可能的或者太昂贵了,所以你必须利用现有的资源。
然而,过度拟合的论点在这里并不重要。如果您试图解决一个明确定义的问题,并且在一个相当稳定的环境中行动,那么训练一个过度拟合的模型可能是可行的,只要它产生正确的结果。
4—不要忘记数据漂移
为了使应用 ML 中的事情更加复杂,您很可能会遇到某种数据漂移。一旦模型投入生产,当现实世界中要素或目标变量的基础分布发生变化时,就会发生这种情况。
https://medium.com/hasty-ai/dealing-with-data-shift-44280ce6ea59
一个明显的例子是,您在夏天收集了初始训练数据并部署了您的模型,但在冬天,世界看起来完全不同,模型崩溃了。另一个更难预料的例子是当你的用户在与你的模型交互后开始改变他们的行为。

伟大的 Josh Tobin 的幻灯片总结了最常见的数据漂移[4]。
一小部分研究集中在这个问题上(见上面伟大的乔希·托宾的幻灯片),但是大多数研究人员在他们追逐 SOTA 的工作中忽略了这一点。
5—你会遇到计算限制
您是否尝试过将深度模型部署到德州仪器的 ARM 设备上?我们做到了。最终,我们让它工作了,但这是一个非常疯狂的过程。
根据您的情况,您可能需要在 edge 上进行推理,并行和实时地服务数百万用户,或者您根本没有预算来无限消耗 GPU。所有这些原因都限制了研究环境中理论上的可能性。研究人员习惯在 GPU 项目上花费 20,000 多美元。
解决方案:从模型到以数据为中心的 ML
如上所述,应用 ML 中截然不同的环境提出了一系列全新的挑战。只专注于改进模型(因为这在研究中很常见)无法应对这些挑战-模型和数据之间的关系比模型本身重要得多!
然而,应用 ML 中的大多数团队采用研究的思维方式,进行 STOA 追踪。他们将数据视为可以外包的商品,并将所有资源投入到模型工作中。根据我们的经验,这是如此多的应用 ML 项目失败的最重要的原因。
然而,应用 ML 中越来越多的人认识到,只关注模型会产生令人失望的结果,倡导应用 ML 新方法的运动正在兴起:他们正在推动从模型到以数据为中心的 ML 开发的转变。
要了解更多,我真的推荐你看下面吴恩达的演讲,他比我更雄辩地阐述了以数据为中心的 ML。
在这个演讲中,吴恩达给出了很多很好的例子来说明为什么在应用 ML 中以数据为中心的方法优于以模型为中心的方法。
但总而言之,主要论点是:当你做应用 ML 时,你不应该太担心 SOTA。即使是几年前的模型对于大多数用例来说也足够强大了。调整数据并确保您的数据适合您的用例并且是高质量的具有更大的影响。
为了让这一点更具体,让我来和你分享一个演讲的趣闻:
Andrew 和他的团队在从事制造业缺陷检测项目时,准确率停留在 76.2%。然后他解散了团队。一组保持模型不变并添加新数据/提高数据质量,另一组使用相同的数据但试图改进模型。处理数据的团队能够将准确率提高到 93.1%,而另一个团队根本无法提高性能。
我们看到的大多数成功的团队都遵循类似的方法,我们试图将这一点放在我们交谈的每个用户的心中。具体来说,根据我们的经验,以数据为中心的 ML 开发可以归结为以下几点:
1—数据飞轮:串联开发模型和数据
在上面的谈话中,Andrew 提到他的团队通常在项目的早期开发一个模型,而不会在最初花时间调整它。在第一批带注释的数据上运行后,他们通常会注意到数据中的一些问题——类的代表性不足,数据有噪声(例如,图像模糊),数据标记不佳等等
然后,他们花时间修复他们的数据,只有当模型的性能不再通过修复数据而提高时,他们才回到模型工作,比较不同的架构并进行微调。
我们还利用一切机会倡导这种方法。我们称之为数据飞轮。这是一个 ML 管道的想法,允许你快速迭代模型和数据。您应该能够快速构建模型,向它们展示新数据,获取预测不佳的样本,对这些样本进行注释,将它们添加到您的数据集中,重新训练您的模型,然后再次测试它。这是构建应用 ML 最快最可靠的方法。

数据飞轮是一个 ML 管道,允许您迭代地开发您的数据和模型,在真实世界条件下达到不断改进的性能。作者图解。
这篇文章不是为了宣传,而是为了向你表明我们在 hasty.ai 对这个想法有多认真:我们实际上围绕这个想法建立了我们的整个业务,从我们所知的情况来看,我们是唯一这样做的人——至少在视觉领域是这样。当您使用我们的注释工具时,您会在我们的界面中得到一个现成的数据飞轮。
我们不断地在后台为你(重新)训练一个模型,而不需要你写一行代码,同时你给你的图像做注解。然后,我们使用该模型为您提供对下一张图像上标签的预测,您可以纠正哪些显著提高了模型的性能。您还可以使用我们的 API 或导出模型来构建带有您自己的(可能面向客户的)接口的飞轮。
一旦你通过提高你在 Hasty 中的数据达到了性能的稳定期,你可以使用我们的模型运动场来微调我们的模型,达到 99.9%的准确率。
2—自己注释数据,至少在开始的时候
做以数据为中心的 ML 也意味着你应该自己注释你的数据。大多数陷入 SOTA 追逐思维的公司将数据视为商品,并将标签外包。
然而,他们发现,实际上,他们正在构建的数据资产是他们未来的竞争优势,而不是他们训练的模型。如何建立模型是众所周知的,并且(对于大多数用例)很容易复制。现在每个人都可以免费学习 ML 的基础。然而,建立一个大的地面实况数据集可能非常耗时和具有挑战性,并且不投入时间是不可复制的。
您的用例越复杂,构建数据资产就越困难。让我们来看看医学成像,这只是许多需要主题专家来创建高质量标签的例子中的一个。但是,即使外包明显简单问题的注释工作也会导致问题,正如下面的例子所示——再次,它是从吴恩达的伟大演讲中偷来的。

如何给物体贴标签并不总是很清楚。在注释的同时识别这样的边缘情况,可能会节省您在调试模型时搜索它们的时间。
这是来自 ImageNet 的一张图片。标签说明是“使用边界框来指示鬣蜥的位置”,乍一看,这听起来像是一个不容错过的说明。然而,一个注释者绘制了标签,使得边界框不重叠,并且忽略了尾部。相比之下,第二个也考虑了左鬣蜥的尾巴。
这两种方法本身都很好,但是当一半的注释是一种方式,另一半是另一种方式时,就有问题了。当您外包注释工作时,您可能会花费几个小时来发现这样的错误,而当您在内部做注释时,您可以调整得更好。
此外,遇到这样的问题可能会让您对可能导致模型在生产中崩溃的边缘情况有很好的见解。例如,鬣蜥看起来有点像没有尾巴的青蛙,您的模型可能会将两者混淆。
当然,这个例子是虚构的,但是当你注释自己的时候,你经常会碰到不知道如何标记的对象。一旦投入生产,你的模型也将与完全相同的图像发生冲突。尽早意识到这一点,可以让你采取行动,减轻潜在的后果。
通常,公司将注释工作外包出去,因为他们认为自己做太费力太痛苦。然而,越来越多的工具提供了高度的自动化,极大地加快了标签工作的速度。
为 hasty.ai 工作,当然,我认为我们的注释工具是最好的。不过,我并不是想说服你使用我们的工具。我想强调的是,检查可用的工具并观察哪一个最适合您的需求绝对是值得的。使用正确的注释工具可以使内部注释数据变得经济可行,为您带来我上面提到的所有好处。
3—使用工具尽可能减少 MLOps 的麻烦
遵循以数据为中心的方法会带来比仅仅标记数据更多的挑战。如上所述,构建数据飞轮在基础设施方面相当棘手。
做好这件事是 MLOps 的艺术。这是应用 ML 世界中的一个新术语,描述了管道管理,并确保您的模型在生产中正常运行。有点像传统软件工程的 DevOps。
如果你以前曾经潜入过 MLOps 的世界,你可能会看到谷歌论文“机器学习系统中隐藏的技术债务”中的下图。[5]

应用 ML 不仅仅是模型代码。所有其他任务组成了 MLOps。有越来越多的工具使 MLOps 变得简单。聪明一点,用其中的一些来解决应用 ML 的一些麻烦。图片摘自[5]。
它显示了 MLOps 的所有不同元素。在过去,能够实现这一点的公司建立了大型团队,并维护了一套工具,编写了无数行胶水代码来使其工作。大多数情况下,FAANG 公司是唯一能够负担得起的公司。
但是现在,越来越多的初创公司开始提供工具来简化过程,并允许您构建生产就绪的应用 ML,而无需 MLOps 忙乱。我们在 hasty.ai 就是这些创业公司中的一员。我们提供端到端的解决方案来构建复杂的视觉应用,并为您处理飞轮的所有基础设施。
但是不管你是否最终使用了 Hasty ,聪明的最终检查那里有什么,而不是试图自己处理所有的 MLOps。这样可以腾出时间来关注数据和模型之间的关系,从而增加任何应用项目的成功机会。
结论
将 ML 研究中发生的所有令人兴奋的事情应用到现实世界用例中的潜力是无穷的。然而,87%的应用 ML 项目仍然在概念验证阶段失败。
根据我们从事 10,000 多个项目和培训 50,000 多个生产模型的经验,我们认为从以模型为中心到以数据为中心的开发的范式转变是使更多这些项目成功的解决方案。带着这种想法,我们并不孤单,而是行业中快速发展运动的一部分。
许多应用 ML 团队还没有采用这种思维模式,因为他们追逐 SOTA,模仿 ML 在研究社区中的工作方式,忽视了你在应用 ML 中遇到的不同情况。
然而,无论如何,我不想贬低研究团体或者给人留下他们的方法毫无价值的印象。尽管对 ML 学术世界的批评是有道理的(在这篇文章中我甚至没有提到)[6],但在过去的几年中产生了多少伟大的作品是令人着迷的。我期待学术界能有更多的开创性成果。
但这正是重点。学术界的目标是做好基础工作,为应用型 ML 铺平道路。然而,应用 ML 中的目标和环境是完全不同的,所以我们需要采用不同的、以数据为中心的思维方式来使应用 ML 工作。
根据我们的经验,以数据为中心的 ML 可以归结为以下三点:
- 数据飞轮:串联开发模型和数据
- 自己注释数据,至少在开始的时候
- 使用工具尽可能减少 MLOps 的麻烦
谢谢你一直坚持到现在,并阅读这篇文章。我很乐意听到您的反馈,并了解您如何应对应用 ML 的挑战。你可以随时在 Twitter 或者 LinkedIn 联系我。
如果你喜欢这篇文章,请分享它来传播数据飞轮和以数据为中心的 ML 思想。
如果你想阅读更多关于如何做以数据为中心的 visionAI 的实践文章,请确保在 Medium 上关注我。
https://medium.com/hasty-ai/uncovering-hidden-biases-in-your-data-93e978daf432
关于 Hasty.ai🦔
Hasty 由工程师于 2018 年在柏林成立,他们希望更容易、更快、更便宜地将视觉人工智能应用到生产环境中。在为德国制造商从事广泛的人工智能项目后,我们发现自己花费了无数的时间为看似简单的用例做手工注释和 MLOps。
今天,我们将为您扫除所有干扰和障碍,以便您能够专注于重要的事情——将 visionAI 投入生产。
来源
[1] " 为什么 87%的数据科学项目从未投入生产?(2019),VentureBeat 杂志文章
[2] C. Northcutt,A. Athalye,J. Mueller,测试集中的普遍标签错误破坏机器学习基准 (2021),ICLR 2021 RobustML 和弱监督学习研讨会,NeurIPS 2020 关于数据集监管和安全的研讨会
[3] N. Sambasivan,S. Kapania,H. Highfill,D. Akrong,P. Paritosh,L. Aroyo
【4】s . Karayev,J. Tobin,P. Abbeel,全栈深度学习 (2021),加州大学伯克利分校课程
【5】d . Sculley,G. Holt,D. Golovin,e .达维多夫,T. Phillips,D. Ebner,V. Chaudhary,M. Young,J. Crespo,D. Dennison,机器学习系统中隐藏的技术债务(2015) 的进展
停止使用所有特征进行建模
使用递归特征选择来选择最佳特征集

图片来自皮克斯拜
真实世界的数据集包含大量相关和冗余的要素。的确,就实例或行数而言,更多数据的存在导致训练更好的机器学习模型。
在继续之前,我们必须知道为什么不建议使用所有的功能集。为了训练稳健的机器学习模型,数据必须没有冗余特征。特征选择之所以重要有多种原因:
- 垃圾输入,垃圾输出:用于训练模型的数据质量决定了输出模型的质量。真实世界的数据包含大量冗余特征,需要移除这些特征,以便训练稳健的模型。
- 维数灾难:随着数据维数或数据中特征数量的增加,特征覆盖的配置数量减少。如果与实例数量相比,数据包含更多的特征,则训练模型不会推广到新的样本。
- 奥卡姆剃刀:当输入数据具有大量特征时,模型的可解释性降低,因此难以解释模型。
因此,从数据集中移除不相关的要素至关重要。数据科学家应该选择他/她用于模型训练的特征。选择所有的特征组合,然后选择最佳的特征集是一个多项式解决方案。选择最佳特征集有各种技巧,阅读这篇文章,就知道 7 种特征选择技巧。
在本文中,我们将讨论如何使用递归特征选择算法及其实现来选择最佳特征集。
什么是递归特征选择?

来源,特征选择包装方法
递归特征选择是一种包装方法,它使用贪婪优化算法来选择 k 个特征的最佳集合。它递归地训练模型,考虑越来越小的特征集,并消除不太相关的特征。逐步递归特征选择算法;
- 估计器在所有初始特征集上被训练。
- 使用 sklearn 库中的特定属性,如
**coef_**或**feature_importance_**,计算每个特征的重要性。 - 从特征集中删除最不重要的特征。
- 递归地重复步骤 2 和 3,直到获得所需数量的特征。
实施:
Sklearn 提出了 RFE(递归特征消除)实现。开发人员只需要指定估计器并更新参数。
**Some important Parameter of RFE function:****estimator:** Supervised learning estimator**n_features_to_select:** The final number of features to select. If 'None', then 50% of features are selected.**step:** If step>=1 thenNumber of features to remove at each iteration. els if step in [0,1) then percentage (rounded down) of features to remove at each iteration.**importance_getter:** If 'auto', uses the feature importance either through a *coef_* or *feature_importances_*attributes of estimator.
结论:
在本文中,我们讨论了如何使用递归特征选择技术来选择 k 个特征的最佳集合。Sklearn 用一行代码实现了 RFE。
还有其他各种包装器方法,包括 SelectKBest ,这也是 Sklearn 实现的一部分。SelectKBest 算法根据 k 个最高分选择相关特征。每个数据科学家都应该知道各种其他的特征选择技术,阅读下面提到的文章来了解 7 种这样的特征选择技术。
参考资料:
[1] RFE 文档:https://sci kit-learn . org/stable/modules/generated/sk learn . feature _ selection。RFE.html
感谢您的阅读
停止使用 CSV 存储—以下是 5 大替代方案
CSV 耗费您的时间、磁盘空间和金钱。以下是每个数据科学家都必须知道的五种选择。

每个人和他们的祖母都知道 CSV 文件是什么。但这是存储数据的最佳方式吗?不,如果你不打算实时查看或编辑数据,这可能是最糟糕的存储格式。
如果您要存储大量数据,选择 CSV 将耗费您的时间和金钱。
今天,您将了解五种 CSV 替代方案。无论是在读/写时间还是在文件大小方面,两者都各有优势。有些甚至各方面都更好。
在查看文件格式之前,让我们先设置一下环境。
入门—环境设置
您将需要几个库来跟进。最佳实践是将它们安装在虚拟环境中,这正是您要做的。下面的代码片段通过 Anaconda 创建了一个新的虚拟环境,并安装了每个必需的库:
conda create --name file_formats python=3.8
conda activate file_formats
conda install -c conda forge numpy pandas fastavro pyarrow feather-format jupyter jupyterlab
安装完成后,您可以执行以下命令来启动 JupyterLab 会话:
jupyter lab
下一步是导入库并创建任意数据集。您将创建一个 5 列 10M 行的数据库:
import numpy as np
import pandas as pd
import feather
import pickle
import pyarrow as pa
import pyarrow.orc as orc
from fastavro import writer, reader, parse_schema
np.random.seed = 42
df_size = 10_000_000
df = pd.DataFrame({
'a': np.random.rand(df_size),
'b': np.random.rand(df_size),
'c': np.random.rand(df_size),
'd': np.random.rand(df_size),
'e': np.random.rand(df_size)
})
df.head()
它看起来是这样的:

图片 1-随机虚拟数据集(图片由作者提供)
现在,您已经具备了开始尝试不同数据格式所需的一切。先来盖 ORC。
妖魔
ORC 代表优化行列。这是一种针对 Hive 中的读写而优化的数据格式。由于 Hive 慢得令人痛苦,Hortonworks 的人决定开发 ORC 文件格式来加速它。
在 Python 中,可以使用 Pandas 的read_orc()函数来读取 ORC 文件。不幸的是,没有替代函数来编写 ORC 文件,所以您必须使用 PyArrow。
这里有一个写熊猫数据帧的例子:
table = pa.Table.from_pandas(df, preserve_index=False)
orc.write_table(table, '10M.orc')
下面是读取 ORC 文件的命令:
df = pd.read_orc('10M.orc')
你可以在这里了解更多关于兽人的信息:
Avro
Avro 是一个开源项目,为 Apache Hadoop 提供数据序列化和交换服务。它用一个类似 JSON 的模式存储数据,所以正确的数据类型是预先知道的。这就是压缩发生的地方。
Avro 对每一种主流编程语言都有 API,但是默认不支持熊猫。
下面是将熊猫数据帧保存到 Avro 文件的一组命令:
# 1\. Define the schema
schema = {
'doc': 'Float data',
'name': 'Data',
'namespace': 'data',
'type': 'record',
'fields': [
{'name': 'a', 'type': 'float'},
{'name': 'b', 'type': 'float'},
{'name': 'c', 'type': 'float'},
{'name': 'd', 'type': 'float'},
{'name': 'e', 'type': 'float'},
]
}
parsed_schema = parse_schema(schema)
# 2\. Convert pd.DataFrame to records - list of dictionaries
records = df.to_dict('records')
# 3\. Write to Avro file
with open('10M.avro', 'wb') as out:
writer(out, parsed_schema, records)
读取 Avro 文件也不容易:
# 1\. List to store the records
avro_records = []
# 2\. Read the Avro file
with open('10M.avro', 'rb') as fo:
avro_reader = reader(fo)
for record in avro_reader:
avro_records.append(record)
# 3\. Convert to pd.DataFrame
df = pd.DataFrame(avro_records)
您可以在此了解有关 Avro 的更多信息:
镶木地板
Apache Parquet 是一种为提高效率而设计的数据存储格式。这背后的原因是列存储架构,因为它允许您快速跳过不相关的数据。这样,查询和聚合都更快,从而节省了硬件。
最好的消息是——Pandas 完全支持拼花文件。
下面是将熊猫数据帧写入拼花文件的命令:
df.to_parquet('10M.parquet')
这是阅读的对等词:
df = pd.read_parquet('10M.parquet')
你可以在这里了解更多关于拼花地板的信息:
泡菜
您可以使用pickle模块来序列化对象并将它们保存到一个文件中。同样,您可以反序列化该序列化文件,以便在需要时将其加载回去。Pickle 与其他格式相比有一个主要优势——可以用它来存储任何 Python 对象。最广泛使用的功能之一是在训练完成后保存机器学习模型。
最大的缺点是 Pickle 是特定于 Python 的,所以不能保证跨语言支持。例如,对于任何需要 Python 和 R 之间的数据通信的项目来说,这可能是一个障碍。
以下是如何将熊猫数据帧写入 Pickle 文件:
with open('10M.pkl', 'wb') as f:
pickle.dump(df, f)
读取 Pickle 文件时,您只需更改文件模式:
with open('10M.pkl', 'rb') as f:
df = pickle.load(f)
你可以在这里了解更多关于泡菜的信息:
羽毛
羽化是一种用于存储数据帧的数据格式。它是围绕一个简单的前提设计的—尽可能有效地将数据帧推入和推出内存。它最初是为 Python 和 R 之间的快速通信而设计的,但是您并不局限于这个用例。
您可以使用feather库来处理 Python 中的羽毛文件。这是目前最快的选择。
以下是将熊猫数据帧保存到羽毛文件的命令:
feather.write_dataframe(df, '10M.feather')
下面是读取的命令:
df = feather.read_dataframe('10M.feather')
你可以在这里了解更多关于羽毛的信息:
比较时间-您应该使用哪种数据格式?
如果您需要即时更改甚至查看数据,许多高度优化的文件格式都是无用的。如果不是这样,你通常应该避免 CSV。
下面是 CSV 和其他提到的数据格式在写入时间上的比较。目标是在本地保存之前创建的 10Mx5 数据集:

图 2 —以秒为单位的写入时间比较(CSV:34.7;兽人:9.66;avro:9.58;拼花:2.06;泡菜:0.5;羽毛:0.304)(图片由作者提供)
差异是巨大的。对于存储相同的数据集,Feather 比 CSV 快大约 115 倍。即使您决定使用更兼容的产品,如拼花地板,写入时间仍然会减少 17 倍。
接下来说说阅读次数。目标是比较读取不同格式的相同数据集所需的时间:

图 3 —以秒为单位的读取时间对比(CSV:3.83;ORC: 3.02,Avro:27.6;拼花:1.23;泡菜:0.193;羽毛:1.16)(图片由作者提供)
CSV 在这里没那么可怕。由于需要解析,Apache Avro 绝对是最糟糕的。Pickle 是最快的,所以如果你只使用 Python,它看起来是最有前途的选择。
最后,让我们比较一下磁盘上的文件大小:

图 4 —以 MB 为单位的文件大小比较(CSV:963.5;ORC:379.5;avro:200.3;拼花:401.4;咸菜:400;羽毛:400.1)(图片由作者提供)
CSV 的情况看起来不妙。文件大小减少了 2.4 倍到 4.8 倍,具体取决于文件格式。
总而言之,如果您每天存储千兆字节的数据,选择正确的文件格式至关重要。如果你只使用 Python,Pickle 是不会错的。如果你需要更通用一点的东西,就用其他提到的格式。
您对这些 CSV 替代品有什么想法?如果不需要实时查看和编辑数据,您会使用哪一种?请在下面的评论中告诉我。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
https://medium.com/@radecicdario/membership
保持联系
停止使用 CSV 进行存储——泡菜是速度快 80 倍的替代品
它还轻了 2.5 倍,并提供了每个数据科学家都必须知道的功能。

将数据存储在云中会花费你一大笔钱。很自然,您会希望远离最广为人知的数据存储格式— CSV —并选择稍微轻松一些的格式。也就是说,如果您不关心动态查看和编辑数据文件。
今天,您将了解在 Python 中存储几乎任何东西的最简单的方法之一——Pickle。Pickling 不仅限于数据集,您很快就会看到,但是本文中的每个示例都基于数据集。
泡菜到底是什么?
在 Python 中,可以使用pickle模块来序列化对象并将它们保存到文件中。然后,您可以反序列化该序列化文件,以便在需要时重新加载它们。
Pickle 与其他格式相比有一个主要优势——可以用它来存储任何 Python 对象。没错,你不限于数据。最广泛使用的功能之一是在训练完成后保存机器学习模型。这样,您就不必在每次运行脚本时重新训练模型。
我也多次使用 Pickle 来存储 Numpy 数组。这是一个在代码中设置某种检查点的简单解决方案。
听起来是一种完美的存储格式?嗯,沉住气。Pickle 有几个你应该知道的缺点:
- 不能保证跨语言支持——Pickle 是特定于 Python 的,所以没人能保证你能阅读另一种编程语言的 Pickle 文件。
- pickle 文件是特定于 Python 版本的——当在一个 Python 版本中保存文件而在另一个版本中读取它们时,您可能会遇到问题。如果可能的话,尽量使用相同的 Python 版本。
- 酸洗不会压缩数据 —酸洗一个对象不会压缩它。当然,与 CSV 相比,该文件会更小,但是您可以手动压缩数据以获得最大效率。
您将很快看到一个压缩数据的解决方案。接下来让我们看看如何在 Python 中使用 Pickle。
如何在 Python 中使用 Pickle?
让我们从导入所需的库并创建一个相对较大的数据集开始。你需要熊猫、熊猫、泡菜和 BZ2。您将使用最后一种方法进行数据压缩:
import numpy as np
import pandas as pd
import pickle
import bz2
np.random.seed = 42
df_size = 10_000_000
df = pd.DataFrame({
'a': np.random.rand(df_size),
'b': np.random.rand(df_size),
'c': np.random.rand(df_size),
'd': np.random.rand(df_size),
'e': np.random.rand(df_size)
})
df.head()
以下是数据集的外观:

图片 1-随机虚拟数据集(图片由作者提供)
接下来就本地保存吧。您可以使用以下命令来清理数据帧:
with open('10M.pkl', 'wb') as f:
pickle.dump(df, f)
文件现在保存在本地。您也可以用类似的方式阅读——只需将模式从wb更改为rb
with open('10M.pkl', 'rb') as f:
df = pickle.load(f)
太棒了。如前所述,默认情况下,Pickle 不会进行任何压缩。您必须手动处理。Python 让bz2模块变得非常简单:
with open('10M_compressed.pkl', 'wb') as f:
compressed_file = bz2.BZ2File(f, 'w')
pickle.dump(df, compressed_file)
这只是一行额外的代码,但它可以为您节省一些磁盘时间。储蓄过程会明显更长,但这是你必须接受的权衡。
读取压缩文件需要一行额外的代码:
with open('10M_compressed.pkl', 'rb') as f:
compressed_file = bz2.BZ2File(f, 'r')
df = pickle.load(compressed_file)
只需确保不要弄乱文件模式,因为读取文件时提供wb会删除其所有内容。最好为读写操作编写助手函数,这样就不会把它们搞砸。
下一节将介绍与 CSV 文件格式在文件大小、读取和写入时间方面的比较。
CSV 与泡菜-您应该使用哪一种?
回答这个问题并不像看起来那么容易。当然,CSV 提供了查看和编辑权限,任何人都可以打开它们。出于明显的原因,这也可能被视为一个不利因素。此外,您不能将机器学习模型保存到 CSV 文件中。
不过,让我们在文件大小、读取和写入时间方面比较一下这两者。
下图显示了在本地保存最后一部分的数据帧所需的时间:

图像 2-CSV 与 Pickle 本地保存时间(秒)(CSV:34.9;泡菜(压缩):32.7;泡菜:0.44)(作者图片)
如果你不在乎压缩,这大约是速度增加的 80 倍。
接下来,我们来比较一下读取时间—读取不同格式的相同数据集需要多长时间:

图像 3-CSV 与 Pickle 读取时间(秒)(CSV:3.71;泡菜(压缩):15.3;泡菜:0.339)(作者图片)
泡菜在没有压缩的情况下,这次大约快 11 倍。在读取和保存文件时,压缩是一个巨大的难点。但是,让我们看看它能节省多少磁盘空间。
这就是以下可视化的答案:

图像 4-CSV 与 Pickle 文件大小对比,单位为 MB(CSV:963.5;泡菜(压缩):381.8;泡菜:400)(作者图片)
与 CSV 相比,文件大小显著减小,但在这种情况下,压缩并没有节省太多磁盘空间。
概括地说,从 CSV 到 Pickle 有明显的优势。不完全明显的是,Pickle 允许您存储其他对象——任何内置于 Python、Numpy 数组甚至机器学习模型中的对象。CSV 和其他纯数据格式不具备这种能力。
你对泡菜有什么想法和经历?这是您对 Python 生态系统的常用格式吗?以下留言请告知。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
https://medium.com/@radecicdario/membership
保持联系
停止使用 CSV 进行存储—这种文件格式速度快 150 倍
CSV 耗费您的时间、磁盘空间和金钱。是时候结束了。

汤姆·斯温宁从派克斯拍摄的照片
CSV 不是唯一的数据存储格式。事实上,这可能是你应该考虑的最后一个。如果你不打算手动编辑保存的数据,坚持这样做是在浪费时间和金钱。
想象一下,你收集了大量的数据,并将它们存储在云中。您没有对文件格式做太多的研究,所以您选择了 CSV。你的费用高得惊人!一个简单的调整可以减少一半,如果不是更多的话。这个调整就是——你已经猜到了——选择不同的文件格式。
今天,您将了解羽毛数据格式的来龙去脉,这是一种用于存储数据帧的快速、轻量级二进制格式。
羽毛到底是什么?
简单来说,就是一种存储数据帧的数据格式(想想熊猫)。它是围绕一个简单的前提设计的—尽可能有效地将数据帧推入和推出内存。它最初是为 Python 和 R 之间的快速通信而设计的,但是您并不局限于这个用例。
所以,不,Feather 并不局限于 Python 和 R——你可以使用每一种主流编程语言中的 Feather 文件。
数据格式不是为长期存储而设计的。最初的意图是 R 和 Python 程序之间的快速交换,以及一般的短期存储。没人能阻止你把羽毛文件转储到磁盘上,然后保存几年,但是有更有效的格式。
在 Python 中,你可以通过 Pandas 或一个专用库来使用 Feather。本文将向您展示如何使用这两者。你需要安装feather-format来跟随。下面是终端命令:
# Pip
pip install feather-format
# Anaconda
conda install -c conda-forge feather-format
这就是你开始工作所需要的一切。打开 JupyterLab 或任何其他数据科学 IDE ,因为下一节将介绍 Feather 的基础知识。
如何在 Python 中使用羽毛?
让我们从导入库和创建一个相对较大的数据集开始。你将需要羽毛,Numpy 和熊猫跟随。数据集将有 5 列和 10M 行随机数:
import feather
import numpy as np
import pandas as pd
np.random.seed = 42
df_size = 10_000_000
df = pd.DataFrame({
'a': np.random.rand(df_size),
'b': np.random.rand(df_size),
'c': np.random.rand(df_size),
'd': np.random.rand(df_size),
'e': np.random.rand(df_size)
})
df.head()
以下是数据集的外观:

图片 1-随机虚拟数据集(图片由作者提供)
接下来就本地保存吧。您可以使用以下命令将数据帧保存为带有熊猫的羽化格式:
df.to_feather('1M.feather')
下面是如何用羽毛库做同样的事情:
feather.write_dataframe(df, '1M.feather')
没多大区别。这两个文件现在都保存在本地。你可以用熊猫或者专用图书馆来阅读它们。首先是熊猫的语法:
df = pd.read_feather('1M.feather')
如果您正在使用羽毛库,请将其更改为以下内容:
df = feather.read_dataframe('1M.feather')
这涵盖了你应该知道的一切。下一节介绍了与 CSV 文件格式的比较,包括文件大小、读取和写入时间。
CSV 与 Feather——您应该使用哪一个?
如果您不需要即时更改数据,答案很简单—您应该使用 Feather over CSV。尽管如此,让我们做一些测试。
下图显示了在本地保存上一部分的数据帧所需的时间:

图 2 — CSV 与 Feather 本地保存时间(秒)(CSV:35.6;羽毛(熊猫):0.289;羽毛:0.235)(图片由作者提供)
这是一个巨大的差异——native Feather 比 CSV 快 150 倍左右。如果您使用 Pandas 来处理羽毛文件,这没有太大关系,但是与 CSV 相比,速度的提高是显著的。
接下来,让我们比较一下读取时间—读取不同格式的相同数据集需要多长时间:

图 3 — CSV 与以秒为单位的羽毛读取时间(CSV:3.85;羽毛(熊猫):0.472;羽毛:0.326)(图片由作者提供)
再一次,显著的差异。CSV 的读取速度要慢得多。当然,它们会占用更多的磁盘空间,但是具体会多多少呢?
这就是下一个可视化的答案:

图 4 — CSV 与 Feather 文件大小(CSV:963.5 MB;羽毛:400.1 MB)(图片由作者提供)
如您所见,CSV 文件占用的空间是羽化文件的两倍多。
如果您每天存储千兆字节的数据,选择正确的文件格式至关重要。在这方面,Feather 击败了 CSV。如果你需要更多的压缩,你应该试试 T4 拼花地板。我发现这是迄今为止最好的格式。
综上所述,将to_csv()改为to_feather(),将read_csv()改为read_feather()可以为你节省大量的时间和磁盘空间。在您的下一个大数据项目中考虑这一点。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
https://medium.com/@radecicdario/membership
保持联系
停止使用 CSV 存储—这种文件格式更快、更轻便
CSV 耗费您的时间、磁盘空间和金钱。有一个解决办法。

CSV 不是唯一可用的数据存储格式。事实上,如果您不打算动态查看和编辑数据,这可能是您应该选择的最后一种方法。如果您计划转储大型数据集并使用自动化进行处理,那么使用 CSV 将是一个漫长且代价高昂的错误。
想象一下,你收集了大量的数据,并将它们存储在云中。您没有对文件格式做太多的研究,所以您选择了 CSV。你的费用高得惊人!一个简单的调整可以减少一半,如果不是更多的话。这个调整就是——你已经猜到了——选择不同的文件格式。
今天,您将了解 ORC 数据格式的来龙去脉,这是一种最初为加快 Hive 处理速度而开发的存储解决方案。不,你不需要一个大数据环境来跟进。
兽人——这是什么?
ORC 代表优化行列。这是一种针对 Hive 中的读写进行优化的数据格式,Hive 是一种针对大数据环境的数据查询和分析工具。
如果你有任何使用 Hive 的经验,你就会知道它的速度有多慢。无论数据集大小如何,即使是最简单的查询也要花费很长时间。Hortonworks 的人在 2013 年决定加快 Hive 的速度,这导致了 ORC 文件格式的开发。
ORC 文件由条带组成,这些条带包含索引数据、行数据和页脚。下图大致向您展示了 ORC 文件在表面之下的样子:

图 1 — ORC 文件格式结构(图片由作者提供)
每个条带的索引数据包括每列的最小值和最大值以及它们的行索引位置。此外,索引位置提供了偏移量,因此 ORC 可以在右块中进行搜索。换句话说,ORC 提供了行跳过功能,这使得读取速度比其他选择更快。
文件页脚包含 ORC 文件中的条带列表和关于每个条带的元数据,比如行数、数据类型和汇总统计数据。
在 Python 中,可以使用read_orc函数用熊猫读取 ORC 文件。不幸的是,对于编写 ORC 文件来说,没有替代函数。你将不得不使用pyarrow库来这样做。以下是安装方法:
# Pip
pip install pyarrow
# Anaconda
conda install -c conda-forge pyarrow
还有其他库可以使用 ORC,但是 PyArrow 可以在所有主流操作系统上运行——包括 M1 MAC 电脑。
现在,您已经具备了开始工作所需的一切。打开 JupyterLab 或任何其他数据科学 IDE ,因为下一节将介绍 ORC 的基础知识。
如何在 Python 中使用 ORC?
让我们从导入库并创建一个相对较大的数据集开始。你将需要 Numpy,熊猫和 PyArrow 跟随。数据集将有 1000 万行随机数,分布在五列中:
import numpy as np
import pandas as pd
import pyarrow as pa
import pyarrow.orc as orc
np.random.seed = 42
df_size = 10_000_000
df = pd.DataFrame({
'a': np.random.rand(df_size),
'b': np.random.rand(df_size),
'c': np.random.rand(df_size),
'd': np.random.rand(df_size),
'e': np.random.rand(df_size)
})
df.head()
以下是数据集的外观:

图片 2-随机虚拟数据集(作者提供的图片)
接下来就本地保存吧。您可以使用以下命令通过 PyArrow 将数据帧写入 ORC 文件格式:
table = pa.Table.from_pandas(df, preserve_index=False)
orc.write_table(table, '10M.orc')
正如你所看到的,保存数据到 ORC 文件需要更多的输入,因为还没有一个官方的 Pandas writer 函数。不过,阅读也是一样的。
您可以使用以下命令从您的计算机中读取 ORC 文件:
df = pd.read_orc('10M.orc')
不会变得更容易。您现在知道如何使用 Python 读取和保存 ORC 文件了。下一节介绍了与 CSV 文件格式的比较,包括文件大小、读取和写入时间。
CSV 和 ORC——你应该使用哪一个?
如果您需要即时更改甚至查看数据,许多高度优化的文件格式都是无用的。如果不是这样,通常应该避免 CSV、XML 和 JSON 文件格式。接下来你会明白为什么。
下图显示了在本地保存我们的 10M 数据帧所需的时间:

图 3 — CSV 与 Feather 本地保存时间(秒)(CSV:34;兽人:9.49)(图片由作者提供)
没有与羽毛的差别那么大,但还是很明显。
接下来,让我们比较一下读取时间—读取不同格式的相同数据集需要多长时间:

图 4 — CSV 与 ORC 读取时间(秒)的对比(CSV:3.72;ORC: 2.48)(图片由作者提供)
很接近了,但是 ORC 读起来还是更快。相比之下,读取羽毛格式的相同文件只需要 0.326 秒。
CSV 读取速度较慢的部分原因是文件大小的增加。下面的图像向你展示了 ORC 到底有多小:

图 5 — CSV 与 ORC 文件大小(CSV:963.5 MB;ORC: 379.5 MB)(图片由作者提供)
如您所见,CSV 文件占用的空间是 ORC 文件的两倍多。
如果您每天存储千兆字节的数据,选择正确的文件格式至关重要。在这方面,ORC 是更好的 CSV。如果你需要更多的压缩,你应该试试镶木地板或 T2 羽毛。
总而言之,即使对 Python 代码进行最小的调整,也能显著减少磁盘使用量并节省时间。如果您将这些操作扩展到万亿字节的数据,就不难看到好处。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
https://medium.com/@radecicdario/membership
保持联系
停止使用网格搜索交叉验证进行超参数调整
各种基于交叉验证的超参数调优技术的基准时间数

(图片由作者提供),使用 Pixlr 编辑
为了训练健壮的机器学习模型,必须选择最合适的机器学习算法以及相应的超参数的最佳集合。为了找到最适合用例的方法,数据科学家需要用不同的超参数集手动训练数百个模型,以比较它们的性能。选择模型的手动搜索是一项繁琐的任务,并且会降低建模流水线的速度。
超参数调整是指为模型选择最佳参数集的过程。建议在超参数空间中搜索最佳交叉验证分数的估计量。各种交叉验证技术可用于优化估计量的超参数空间。网格搜索交叉验证是一种流行的技术,用于优化各种机器学习算法的超参数空间并选择稳健的模型。
本文将讨论超参数优化何时以及为什么应该避免网格搜索交叉验证,并讨论各种其他类似的技术及其运行时基准时间数。在我的上一篇文章中,我们已经讨论了 7 超参数优化技术,这里我们将比较时间
开始使用:
从 Kaggle 下载的信用卡欺诈检测数据集将用于比较每种交叉验证技术的运行时间,以优化超参数。我们将使用随机森林分类器模型和具有 100 个组件的超参数空间来训练所有交叉验证技术。
***# Number of Components: 5*5*2*2 = 100*****param_grid** =
**{**'**n_estimator**':[10,25,50,100,250],
'**max_depth**':[5,10,25,50,None],
'**max_features**':['auto',None],
'**min_sample_split**':[2,5]
**}**
在本文中,我们将讨论和比较以下各项的运行时间:
**CV based Hyperparameter Optimization Checklist: *1) Grid Search CV
2) Randomized Search CV
3) Halving Grid Search CV***
阅读sci kit-了解交叉验证的文档 以便更好地理解。
1.网格搜索 CV:
网格搜索也可以称为手动超参数搜索的自动化版本。网格搜索 CV 在参数网格的所有组合上训练估计器,并返回具有最佳 CV 分数的模型。Scikit-Learn 包附带了 GridSearchCV 实现。
网格搜索交叉验证技术计算量很大。网格搜索 CV 的复杂性随着参数网格中参数数量的增加而增加。因此,网格搜索 CV 技术不推荐用于大尺寸数据集或具有大量组件的参数网格。

(图片由作者提供),网格搜索 CV 执行时间和测试信用卡欺诈检测数据集的各种样本的 AUC-ROC 分数
在这里找到的代码片段来实现网格搜索 CV。
2.随机搜索简历:
随机化搜索 CV 克服了网格搜索 CV 的局限性(时间复杂度高)。超参数组合随机子集上的随机搜索训练模型。与网格搜索相比,随机搜索训练估计器的组合总数较少。
Scikit-learn 包还附带了随机搜索 CV 的实现。可以采样的参数设置数量可以通过调整 RandomizedSearchCV 功能中的**n_iter** 参数来决定。**n_iter** 可以在解决方案的运行时间和性能之间进行权衡。

(图片由作者提供),针对信用卡欺诈检测数据集的各种样本的随机搜索 CV 执行时间和测试 AUC-ROC 得分
在这里找到的代码片段来实现网格搜索 CV。
3.减半网格搜索 CV:
网格搜索和随机搜索在全部数据的所有成分或成分的随机样本(参数网格的组合)上训练估计器,而等分网格搜索 CV 遵循连续等分方法。Scikit-Learn 包附带了减半网格搜索 CV 实现。
将网格搜索(HGS) CV 减半的步骤如下:
- HGS 将随机数据样本训练成超参数的组合。
- 选择性能最佳的候选参数。
- 一个相对较大的数据样本被训练在表现最好的候选人身上(来自步骤 2)。
- 重复上述 3 个步骤,直到最佳超参数集保持不变。
阅读我之前的文章,描述如何使用 HGS 将建模工作流程提高 20 倍。

(图片由作者提供),将网格搜索 CV 执行时间减半,并测试信用卡欺诈检测数据集的各种样本的 AUC-ROC 分数
使用 HGS,对于每个经过的迭代,参数分量在减少,而训练数据集在增加。由于该算法遵循连续减半的方法,因此与网格搜索 CV 相比,该算法的时间复杂度相对较小。
基准性能:

(图片由作者提供)
结论:
在本文中,我们讨论了各种基于交叉验证的超参数优化技术,并比较了它们的执行时间和 ROC 分数。
从上述基准数据中,我们可以得出结论,整个数据集(250000 个样本)的测试 AUC-ROC 得分几乎相同。但是随机搜索 CV 的执行时间是网格搜索 CV 技术的五倍(T0)快(T1),减半网格搜索是网格搜索 CV 技术的三倍(T3)。对于具有大量组件的大型数据集或参数格网,建议使用另一种讨论过的技术。
随着参数网格组件数量的增加,对半网格搜索 CV 的性能将进一步提高。阅读这篇文章以获得更好的理解。
参考资料:
[1]sci kit-学习文档:https://scikit-learn.org/stable/modules/grid_search.html
感谢您的阅读
停止对神经音频合成使用图像插值
理解大数据
不同类型的神经上采样器,以及在深度学习音频合成项目中应该使用哪一种。
在这个故事中,我想提升你对音频合成背景下的神经上采样器的理解。并提供了一个简单的亚像素 1D Keras 层实现,作为我们今天讨论的许多任务的替代方案。
我们都知道上下采样在计算机视觉的深度学习中是一种重要的操作,例如,在像图像超分辨率或图像生成这样的任务中。使用 GANs、U-Nets 或自动编码器等流行架构的音频合成也是如此。虽然下采样是一种相对简单的操作,但总是很难找到一种不会导致图像或音频伪像的好的上采样策略。关于计算机视觉任务中的二维棋盘格工件的初级读本,请阅读这篇伟大的文章【1】。
现在让我们更深入地研究一维音频上采样。在音频领域,我们使用三种主要的上采样技术[2]:
- 转置卷积(广泛使用)
- 插值+卷积(常用)
- 子像素卷积(很少使用,但在视觉任务中很突出)
它们的使用实例可以在许多出版物中找到,如 Demucs(音乐源分离)[3],MelGAN(波形合成)[4],SEGAN(语音增强)[5],conv-塔斯内(语音分离)[6]或 Wave U-Net(源分离)[7]。
TensorFlow Keras 提供了第四种上采样解决方案,即上采样 1D 层,然而,截至目前(3 月 21 日)这一层在 GPU 上仍然非常慢,尽管问题已经解决。
转置卷积

图 1:使用转置卷积的 2 倍上采样|作者图片
转置卷积是卷积的逆运算,Keras 提供了 1D、2D 和 3D 实现。您可以像使用 Conv1D 层一样使用 Conv1DTranspose 层,并且通过传递stamps = 2得到的张量将使其时间维度的大小加倍,如图 1 所示。在这种情况下,上采样由在训练期间学习的卷积运算的权重控制。
插入文字

图 2:采用不同插值方案的 2 倍上采样|作者图片
相比之下,插值本身没有可学习的参数,这就是为什么这种操作之后应该有一个神经网络层。否则我们只会对一些潜在空间的高级特征进行上采样。根据插值方案以及在神经网络中部署的结果,计算性能可能会有很大差异。稍后我们将看到为什么其他方法是有利的,但这里有一个在 1D 实现插值运算的简单方法:
x = tf.image.resize(x, [samples * stride, 1], method='nearest')
子像素

图 3:采用亚像素方法的 2 倍上采样|作者图片
这个很酷!想法是在信道维度而不是时间维度中执行上采样。通过用卷积将通道的大小加倍,我们可以应用整形操作,该操作周期性地将通道混洗到时间维度中。这比解释更好,所以请看图 3。子像素层有不同的实现方式,其计算复杂度也各不相同。
在接下来的部分中,我们将更深入地了解亚像素 1D 层的实现,但让我们先来谈谈目前为止这三种方法的优缺点。
上采样伪像
所有上面提到的上采样方法都会将伪像引入到神经音频合成模型中,然而,理论上,您的模型可以决定学习一种方法来最小化这些伪像。在 Jordi Pons 等人[2]最近的预印本中,该团队描述了神经上采样器如何引入音调和滤波伪像。如果你想更深入地研究这个主题,强烈推荐你读一读!然而,为了对实际使用的预定方法进行排序,这里有一个总结:
- 转置卷积:部分重叠的滤波器会导致更强的音调伪像,因此您应该将滤波器+步幅参数化为完全重叠或无重叠
- 为了避免频谱复制不要使用 ReLu-activation 并移除卷积层中的任何偏差。
- 插值:用最近邻插值代替线性插值。插值方法会导致滤波伪像。
- 子像素和转置卷积显示在类似 Demucs 的架构中训练速度快了 25%。它们还能获得最佳的信号失真比。
- 与音调伪像相比,滤波伪像在感觉上没有那么令人讨厌
- 目前,从数据中学习是克服音调伪像的唯一方法,尤其是通过随机权重初始化引入的那种
分析表明,转置卷积和子像素 CNN 是神经音频合成的发展方向。它们实现了更好的 SDR 分数,并且在计算上比使用图像插值方法更有效。然而,插值模型似乎更好地概括了看不见的数据,只引入了感觉上不那么烦人的滤波伪影。
实现子像素 1D
子像素 1D 的这种实现利用 tf.batch_to_space() 函数来执行周期性混洗。因此,我们首先置换维度,使得信道维度是第一个,在应用 batch_to_space 操作之后,我们只需要将维度置换回原位,以获得我们的上采样张量。我们假设子像素 1D 层的输入已经预先经历了卷积层,这适当地增加了信道维度。如果通道维度大小不能被上采样因子 r 整除,则会产生错误。
现在让我们通过为原始音频数据实现一个简单的自动编码器来实现这一层。我们假设模型的输入长度为 16384 个样本,因此我们可以轻松地以 4 倍的因子对层进行上下采样。
最后,我们可以让我们的神经上采样器工作,并将任意音频数据编码到我们的自动编码器的潜在空间中。当然,您可以用上面出版物中提到的任何模型来替换这个模型草图。查看图 4 中的模型总结,我们可以看到所提出的架构仅使用卷积层。这是伟大的,因为它保持了我们的模型简单和高性能,你的训练时间应该显着改善,如果你以前使用插值方法!

图 4:作者的亚像素 CNN 自动编码器图片摘要
恭喜
现在,您已经准备好为音频合成任务(以及任何其他时间序列数据)构建最先进的神经上采样器。通过决定所介绍的上采样方法的哪些属性更适合您的项目,您应该能够避开感知上令人讨厌的伪像的陷阱。此外,我们实现了一个子像素 1D 层,它在 CNN 架构中提供了最佳的计算性能,同时表现出与转置卷积相同的行为。
资源
[1] A. Odena 等人,反卷积和棋盘状伪像(2016),【http://distill.pub/2016/deconv-checkerboard
[2] J. Pons 等人,神经音频合成中的上采样伪像(2021),https://arxiv.org/pdf/2010.14356.pdf
[3] A. Défossez 等,波形域中的音乐源分离(2019),https://hal.archives-ouvertes.fr/hal-02379796/document
[4] K. Kumar 等,MelGAN:用于条件波形合成的生成对抗网络(2019),【https://arxiv.org/pdf/1910.06711.pdf
[5] S. Pascual 等人,SEGAN:语音增强生成对抗网络(2017),【https://arxiv.org/pdf/1703.09452.pdf
[6] Y .罗,n .梅斯卡拉尼,-塔斯奈特:超越理想时频幅度掩蔽的语音分离(2019),
[7] D. Stoller,S. Ewert 和 S. Dixon,Wave-U-Net:用于端到端音频源分离的多尺度神经网络(2018),https://arxiv.org/pdf/1806.03185.pdf
如果你正在阅读这篇文章,我们可能有相似的兴趣或者在同一个行业,欢迎你联系我。在LinkedIn上找我。
停止使用一对一或一对多分类任务
纠错输出码基本指南(ECOC)

图片由 Gerd Altmann 从 Pixabay 拍摄
诸如逻辑回归、支持向量机等机器学习算法可以对二进制类数据集进行分类,但在处理多类分类数据时,它会失败。多类分类任务具有超过 2 个基数的目标类标签。对于多类分类或多标签分类任务,我们需要使用某些技巧或技术以及机器学习算法来训练数据集。
一对一和一对一就是这样两种可以处理多类或多标签分类任务的技术。One-vs-Rest 分类器为目标类标签为**‘c’**基数的数据训练**‘c’**分类器,每个估计器适用于一个类,并针对所有其他类。而一对一分类器为每对类别训练一个分类器。纠错输出码(ECOC)与 OvO 和 OvR 分类器有很大不同。在本文中,我们将讨论 ECOC 如何在幕后工作,以及如何使用它来训练多类分类任务的模型。
什么是 ECOC?
纠错输出码(ECOC)是一种为多类分类问题设计的集成分类技术。虹膜数据集和 MNIST 数字识别数据集是多类分类数据集的例子。ECOC、OvO 和 OvR 技术结合多个二元估计器来开发多类分类模型。
ECOC 将多类目标类标签预处理成二进制代码(0 和 1 的数组)。使用这种策略,目标类别标签在二进制码的欧几里德空间中表示,使用跟踪编码的码本。

(图片由作者提供),15 位编码中 10 个类目标标签的表示
上面提到的图像显示了 10 类目标标签的 15 位编码。每个目标标签被分配一个唯一的 15 位编码。码本矩阵跟踪每个目标类的比特编码。
开发者可以控制比特编码的维度。如果位编码的维数大于目标类别标签的基数,则一些分类器的预测可以被其他分类器校正。在这种情况下,要训练的估计量的数量比一对其余技术要多。
如果我们保持比特编码的维数小于目标类标签的基数,那么它训练的估计器相对比一对其余分类器要少。理论上,**log2(n_classes)**足以明确地表示目标类标签。对于 10 类目标标签 log2(10)=4 应该是选择。

(图片由作者提供),以 4 位编码表示 10 个类目标标签
在 d 维比特编码中对目标类标签进行编码之后,需要拟合“d”个估计量,一个二进制估计量用于编码的每个比特。在预测时,基本估计器预测编码目标类的每个相应比特,并且最接近这些点的目标嵌入类被认为是最终预测。
实施:
Scikit-learn 包附带了一个 OutputCodeClassifier() 函数,该函数在一行 Python 代码中实现了 ECOC 分类器。参数 code_size 可用于决定目标类的位编码。介于 0 和 1 之间的值可以用于压缩模型,或者 code_size > 1 可以使模型对错误更鲁棒。
需要调整 code_size 的值:
- 0 < code_size <1: Train a compressed model where the number of estimators fitted is less than in the case of the One-vs-Rest classifier.
- code_size> 1:训练误差校正模型,对误差稳健。拟合的估计量的数量多于一对其余分类器的情况。
(作者代码),使用 Scikit-learn API 实现 ECOC
可以调整超参数 code_size 来改变目标类嵌入的维度。我使用带有逻辑回归模型的 OutputCodeClassifier 作为基本估计量,训练了一个 20 类分类数据集。

(图片由作者提供),调优代码大小超参数上的性能指标分布
从上面的分布图中,我们可以观察到性能指标的增加,但随后,它变平了。代码大小= 10 的值可以被认为是一个阈值。对于代码大小= 10,我们得到了 25.9%的准确率、27.3%的精确度和 26.5%的召回率。进一步增加嵌入维数对模型的性能没有影响。
结论:
OutputCodeClassifier 是一个方便的函数,可用于拟合实现 ECOC 算法的多类分类数据集。开发人员可以控制基分类器的数量,这与一对一或一对其余技术相比是一个额外的优势,在一对一或一对其余技术中,基估计器的数量取决于目标类的基数。
模型的性能取决于基本估计量的数量。理论上,log2(n_classes)足以明确地表示目标类,但它可能不会导致健壮的模型。我们需要增加代码大小来适应一个不受错误影响的健壮模型。
遵循我以前的一篇文章,更好地理解多标签分类和多输出回归任务
参考资料:
[1] Scikit-learn 文档:https://sci kit-learn . org/stable/modules/generated/sk learn . multi class . output code classifier . html
[2]纽别杰夫文献:【https://newbedev.com/scikit_learn/modules/multiclass
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一小部分会员费,不需要你额外付费。
https://satyam-kumar.medium.com/membership
感谢您的阅读
停止使用 p<0.05
应用统计学—数据科学
通过在统计测试中选择适当的显著性水平来改进您的预测模型和数据分析

作者迷因。模板来源:imgflip.com
使用 0.05 的固定 p 值临界值来确定显著性是各个领域的研究人员和应用统计方法的行业专家普遍存在的错误。
原因可能是显而易见的,解决方案也很简单。
使用 p<0.05 常常是一个错误
当统计学家、数据科学家和经济学家学习统计学时,95%的置信区间(对应于 0.05 的 p 值截止值)是传导假设检验的默认选项。
从 SAS 到 R 和 Python,这也是许多跨不同统计编程包的自动选择过程的默认设置(当然也有例外)。
但是正如在下面的文章中进一步详细解释的那样,使用 p<0.05 常常是一个错误。如上所述,数据集的大小决定了适当的置信区间(从而决定了正确的 p 值)。
假设检验中不当使用 p 值的问题由于错误地强调它们而被放大。
一个常见的误解是,在零假设统计检验(NHST)中,由于具有统计学显著性的 p 值而拒绝零假设等于零假设是错误的。
因此,使用正确的 p 值并不一定足以修复 NHST 的广泛滥用。但这仍然是重要的一步,而且很容易实施。
如何选择合适的 p 值
在 NHST 中确定截止 p 值时,了解 p 值的含义很有用。
p 值是在我们的假设下,我们观察到的结果至少与数据产生的统计数据一样极端的概率。
统计学的核心是小样本的处理。我们如何使用样本对整个人口进行推断。
对于小样本,我们愿意接受一些错误结论的风险,因为否则我们无法从数据中推断出任何东西。
但是随着样本量的增加,我们应该调整置信区间。
适当的 p 值各不相同
在大样本中,拒绝每一个 p 值小于 0.05 的显著性零假设会导致过度拒绝。
在现代,数据集通常由成千上万个数据点组成。在那种情况下,p<0.01 也不会切。
过度拒绝意味着我们经常会拒绝一个真正的零假设。拒绝一个真正的零假设也被称为 1 型错误。相反,类型 2 错误是错误的零假设的非拒绝。
如果 p 值没有根据数据集中的观测值进行调整,则增加观测值的数量只会减少第 2 类错误。最理想的情况是,我们希望两者都减少。
除了观察值的数量之外,决定给定检验的适当 p 值的是统计模型的选择、如何处理异常值和缺失数据、预测值的数量以及自由度。
因此,适当的 p 值根据上下文而变化。
几个例子
考虑一个有 j 个回归变量和 n 个观测值的线性回归模型。第一步自然是用零假设进行 f 检验,假设回归系数的一组独立线性限制 s 得到满足。
在真零假设下,相关的统计量将是具有 s 和(n-j)个自由度的 F 分布。
当数据集非常大时,j 在 n-j 中所占的份额很小。如果 F > (n / s)(ns/n — 1),我们可以拒绝空值。
在大样本中,这是 F > ln(n)的近似值,其中 ln 是自然对数。
如果 n = 100.000 且 s 为 5,则 F 统计的适当临界值为 11.5,对应于 0.0000000000039(或 3.9e-11)的 p 值。
a 如下表所示,当观察次数增加时,上述 f 检验的正确 p 值远低于 0.05。

f 检验的临界值。按作者分类的表格。来源:戴夫·贾尔斯
很明显,使用 p <0.05 leads to way more frequent rejections of the null-hypothesis than what is deemed optimal.
Here’s another example, with the often-used t-test.
Assume instead (following an example from Dave Giles ,你想要测试你的模型中的回归变量是否显著。这是通过进行 t 检验来完成的。
测试单个限制时,t 统计量 t 遵循自由度为 1 和 v 的 F 分布。如果| t |>√[n(n^(1/n)-1)]√ln(n),我们可以拒绝针对双边备择假设的零假设。
因此,当 n = 100.000 时,检验单个回归变量的显著性意味着临界值为 3.394。
对应的 p 值为 0.000345 。
外卖
重要的是要记住 NHSTs 不是没有错误的。盲目使用 p 值会导致错误的数据分析和错误的模型选择。
但是在使用 NHSTs 时,首先要确保使用正确的临界值。相应的 p 值可能变化很大。如果您有幸处理大型数据集,使用正确的 p 值至关重要。
让 p 值随着观察值的数量而变化(就像它取决于检验统计量的分布、限制和自由度一样)。
在放大数据集时缩小 p 值临界值将有助于避免过度剔除,从而避免模型中有太多重要变量。
https://medium.com/datadriveninvestor/the-cape-ratio-suggests-the-stock-market-is-attractive-7eb3fc22816b https://medium.com/swlh/3-truly-disruptive-businesses-built-on-founder-knowledge-d71ab2482d13
停止使用 Pandas get_dummies()进行特征编码
熊猫 get_dummies()和 Sklearn One-hot 编码器基本指南

图片由 Mediamodifier 来自 Pixabay
机器学习算法需要特定格式的数字向量的输入数据源。特征工程是数据科学模型开发生命周期的重要组成部分,指的是将原始数据转换为适合训练稳健模型的数字格式。
一个数据科学家大约 80%的时间花在数据准备和特性工程上。模型的性能取决于特征工程策略。原始数据集包含各种数据类型的要素,包括数字、分类、日期时间等。有各种各样的特征工程技术将不同数据类型的数据特征转换成数字向量。
虚拟编码是指将分类特征转换成数字向量格式的编码策略。有各种其他技术来编码分类特征,包括计数编码器、单热编码器、Tf-Idf 编码器等。
**pd.get_dummies()**是 Pandas 的一个函数,它在一行代码中执行虚拟编码。数据科学家大多将此用于特征编码,但不建议在生产或 Kaggle 比赛中使用。在本文中,我们将讨论其背后的原因以及对于get_dummies()函数的最佳选择。
熊猫库中的**get_dummies()**函数可用于将分类变量转换成虚拟/指示变量。在某种程度上,它是一种静态的行为编码技术。
我们将采用一个随机数据集,其中包含 2 个数值特征和 1 个分类特征(“颜色”),用于进一步演示。“颜色”分类变量有 3 个独特的类别:绿色、红色、蓝色。

(图片由作者提供),训练数据的 get_dummies()使用结果
您可以观察到pd.get_dummies()的编码结果。不建议在生产或 Kaggle 上使用此函数,因为它的行为本质上是静态的。它不能从训练数据中学习特征,因此不能将其发现传播到测试数据集中。
分类特征颜色有 3 个特征值:绿色、红色、蓝色。这使得将颜色特征编码成 3 个特征类别。但是测试数据可能有也可能没有所有的特征值,这可能会在建模时导致数据不匹配的问题。

(图片由作者提供),测试数据的 get_dummies()使用结果
颜色特征中缺少蓝色特征值,这导致编码数据中缺少蓝色特征列。这是因为pd.get_dummies没有学习训练数据的特征,在预测时会进一步造成特征不匹配。
实施:
#to print the encoded features for train data **pd.get_dummies(X_train)**#to print the encoded features for test data **pd.get_dummies(X_test)**
pd.get_dummies()的唯一优势是它的易解释性,以及它返回一个列名清晰的 pandas 数据框的事实。
一个热编码器:
一键编码器是一种流行的特征编码策略,其性能类似于pd.get_dummies(),但具有额外的优势。它通过为每个类别的每个分类特征分配一个二进制列来编码一个名义或分类特征。Scikit-learn 附带了一键编码器的实现。


(图片由作者提供),一个热编码器结果,左:训练,右:测试
从一个用于训练和测试数据的 hot 编码器的上述结果中,可以观察到,在对训练数据的分类变量进行编码时,编码的特征被保存,并且在对测试数据进行编码时,其发现或特征值被传播。
虽然蓝色特征值没有出现在测试数据中,但是一个 hot 编码器为蓝色类别创建了一个特征列,因为它出现在训练管道中。
实施:
**from sklearn.preprocessing import OneHotEncoder**# one hot encoding **enc = OneHotEncoder(sparse=False)
color_onehot = enc.fit_transform(X_train[['color']])**#to print the encoded features for train data **pd.DataFrame(color_onehot, columns=list(enc.categories_[0]))****# tranform encoding for test data**
test_onehot = enc.transform(X_test[['color']])**#to print the encoded features for train data** pd.DataFrame(test_onehot, columns=list(enc.categories_[0]))
结论:
**pd.get_dummies()**在要素编码后返回具有干净列名的 pandas 数据框,但仍不建议将其用于生产或 Kaggle 竞赛。应该首选一键编码或计数矢量器策略,因为它具有每个分类特征的特征值特征。
新的特征类别也可以使用一个热编码器的***handle_unknown=’ignore’*** 参数进行处理,这可能进一步导致**pd.get_dummies()**中的数据不匹配问题
参考资料:
[1] Scikit-learn 文档:https://sci kit-learn . org/stable/modules/generated/sk learn . preprocessing . onehotencoder . html
感谢您的阅读
停止使用 Pandas 来读/写数据—这种替代方案速度快 7 倍
读取和写入 CSV 数据集的速度比熊猫快 7 倍

凯西·霍纳在 Unsplash 上的照片
我喜欢 Python 的熊猫图书馆。这是我分析、转换和预处理数据的首选方式。但是当读取和保存数据文件的时候,它是慢的。这是一个巨大的时间浪费,特别是如果你的数据集的大小是千兆字节。
想象一下,您想要查看存储在本地或云上的千兆字节的 CSV 数据。您将使用 Pandas 进行分析,尽管您知道在读取 CSV 文件时速度非常慢。因此,大部分时间都在等待读写操作完成。有更好的方法。
它被称为 py Arrow——Apache Arrow 项目的一个惊人的 Python 绑定。它引入了更快的数据读/写时间,并且不会干扰您的数据分析管道。这是两全其美,因为你仍然可以使用熊猫进行进一步的计算。
您可以将 PyArrow 与 Pip 和 Anaconda 一起安装:
pip install pyarrow
conda install -c conda-forge pyarrow
找视频版?你很幸运:
让我们创建一个虚拟数据集
让我们从库导入开始。今天你会需要很多:
import random
import string
import numpy as np
import pandas as pd
import pyarrow as pa
import pyarrow.csv as csv
from datetime import datetime
接下来,我们将创建一个有点大的数据集。它将包含大约 1100 万个日期、浮点和字符串值。日期信息完全是虚构的,以分钟为间隔从 2000 年到 2021 年。其他列也是完全随机的:
def gen_random_string(length: int = 32) -> str:
return ''.join(random.choices(
string.ascii_uppercase + string.digits, k=length)
)
dt = pd.date_range(
start=datetime(2000, 1, 1),
end=datetime(2021, 1, 1),
freq='min'
)
np.random.seed = 42
df_size = len(dt)
print(f'Dataset length: {df_size}')
df = pd.DataFrame({
'date': dt,
'a': np.random.rand(df_size),
'b': np.random.rand(df_size),
'c': np.random.rand(df_size),
'd': np.random.rand(df_size),
'e': np.random.rand(df_size),
'str1': [gen_random_string() for x in range(df_size)],
'str2': [gen_random_string() for x in range(df_size)]
})
它看起来是这样的:

图片 1-虚拟数据集(作者提供的图片)
这是 11,046,241 行混合数据类型,因此生成的 CSV 文件将非常庞大。
用熊猫读/写 CSV 文件
我们将使用熊猫作为基线解决方案。如果像 PyArrow 这样的库不存在的话,你就会用到它。这一节只包含代码—您将在本文后面找到比较和图表。
使用以下代码将我们的数据集df保存到一个 CSV 文件:
df.to_csv('csv_pandas.csv', index=False)
如果你不在乎写速度,你可以节省一些磁盘空间。熊猫的to_csv()函数有一个可选参数compression。让我们看看如何使用它将数据集保存为csv.gz格式:
df.to_csv('csv_pandas.csv.gz', index=False, compression='gzip')
最后,您可以使用read_csv()功能阅读两个版本:
df1 = pd.read_csv('csv_pandas.csv')
df2 = pd.read_csv('csv_pandas.csv.gz')
这里没有什么新的或有趣的,但我想涵盖所有的基础。接下来看看 PyArrow 是怎么工作的。
用 PyArrow 读/写 CSV 文件
关于 PyArrow,您应该知道一件事——它不能处理日期时间列。您必须将date属性转换成时间戳。方法如下:
df_pa = df.copy()
df_pa['date'] = df_pa['date'].values.astype(np.int64) // 10 ** 9
这是数据集更改后的样子:

图 2 —时间戳转换后的虚拟数据集(图片由作者提供)
还是同样的信息,只是呈现方式不同。现在可以将 DataFrame 转换为 PyArrow 表。这是将数据集转储到磁盘之前的一个必要步骤:
df_pa_table = pa.Table.from_pandas(df_pa)
在我的机器(M1 MacBook Pro)上,转换需要 1.52 秒,并且将被包含到比较图表中。
使用 PyArrow 的csv.write_csv()函数转储数据集:
csv.write_csv(df_pa_table, 'csv_pyarrow.csv')
添加压缩需要更多的代码:
with pa.CompressedOutputStream('csv_pyarrow.csv.gz', 'gzip') as out:
csv.write_csv(df_pa_table, out)
您可以使用csv.read_csv()功能读取压缩和未压缩的数据集:
df_pa_1 = csv.read_csv('csv_pyarrow.csv')
df_pa_2 = csv.read_csv('csv_pyarrow.csv.gz')
两者都将以pyarrow.Table格式读取,所以使用下面的命令将它们转换成 Pandas 数据帧:
df_pa_1 = df_pa_1.to_pandas()
这就是你今天应该知道的。让我们看看这些在性能上的比较。
熊猫和皮阿罗——你应该用哪一个?
如果你的数据很大,每次都用 PyArrow。就这么简单。原因如下。
下图显示了使用 Pandas 和 PyArrow 保存数据帧所需的时间,包括未压缩版本和压缩版本:

图 3 —熊猫 vs. PyArrow 以秒为单位节省时间(熊猫 CSV:54.5;熊猫 CSV。GZ: 182 人;py arrow CSV:7.76;皮阿罗 CSV。GZ: 84)(图片由作者提供)
未压缩文件的速度提高了约 7 倍,压缩文件的速度提高了约 2 倍。我知道会发生什么,我仍然印象深刻。
接下来,让我们比较一下读取时间——读取熊猫和 PyArrow 的 CSV 文件需要多长时间:

图 4 —熊猫 vs. PyArrow 阅读时间(秒)(熊猫 CSV:17.8;熊猫 CSV。GZ: 28 名;py arrow CSV:2.44;皮阿罗 CSV。GZ: 9.09 分)(图片由作者提供)
我们获得了类似的性能提升——未压缩数据集约为 7 倍,压缩数据集约为 3 倍。我说不出话来。看在上帝的份上,这是相同的文件格式。
最后,让我们看看文件大小有什么不同。这两个库之间应该存在任何差异:

图 5 — Pandas 与 PyArrow 文件大小(GB)(Pandas CSV:2.01;熊猫 CSV。GZ:1.12;py arrow CSV:1.96;皮阿罗 CSV。GZ: 1.13)(图片由作者提供)
在未压缩版本中略有不同,但这可能是因为我们用 Pandas 存储 datetime 对象,用 PyArrow 存储 integers。不出所料,没什么值得大书特书的。
结论
总而言之,如果你的应用程序经常从磁盘上保存/加载数据,那么把这些操作交给 PyArrow 是一个明智的决定。见鬼,对于相同的文件格式,速度快了 7 倍。想象一下,我们引入了拼花文件格式。这是下一篇文章将要讨论的内容。
你对皮阿罗有什么看法?日常使用中有没有遇到什么痛点?请在下面的评论区分享你的想法。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
https://medium.com/@radecicdario/membership
保持联系
停止使用“打印”,开始使用“记录”
编程;编排
如何使用 Python 日志库工具来跟踪代码事件和调试

图片由作者提供。
日志记录是跟踪代码中的事件或调试的一种流行解决方案。我们许多人(Python 程序员和数据科学家)都有使用 print()调试和跟踪代码中事件的坏习惯。
为什么使用 print()进行日志记录和调试不是一个好的做法?
- 如果您的代码不能访问控制台,print()语句将失败。
- 要定义基本的日志需求,需要几行代码。
- 包含额外的日志信息并不容易。
- print()语句仅在控制台上显示消息。在文件中记录日志数据或通过互联网发送需要额外的工作。
跟踪代码事件和进行调试的更好方法是使用“日志”库(Python 标准库)。
日志库为您提供了五种工具来完成日志任务。
- 调试(
logging.debug()):为诊断问题提供信息。 - Info (
logging.info()):跟踪一个程序的正常运行。 - 警告(
logging.warning()):虽然代码仍然像预期的那样工作,但是发生了一些意想不到的事情。 - 错误(
logging.error()):代码无法运行某些部分。 - 严重(
logging.critical()):代码无法运行。
在使用这些工具之前,让我们了解一下 Python 中不同级别的日志记录。
Python 中的日志记录级别
Python 中有六个日志记录级别。最高层很关键。如果将日志记录级别设置为严重,将只显示严重级别的日志记录消息。要将您的日志记录级别设置为 CRITICAL,您可以使用logging.basicConfig()。
logging.basicConfig(level=logging.CRITICAL)
或者
logging.basicConfig(level=50)
如您所见,logging.basicConfig()中的level参数采用一个整数(即50或常数logging.CRITICAL)并设置日志记录级别。从最低到最高的日志记录级别是 NOTSET=0、DEBUG=10、INFO=20、WARNING=30、ERROR=40 和 CRITICAL=50。
默认级别为WARNING(即30),这意味着日志模块只显示/记录具有警告、错误或严重严重性的事件。同样,请记住,您可以使用logging.basicConfig()设置您的日志记录级别。
下面是显示不同级别日志消息的示例代码。注意,我将日志级别设置为最低(logging.NOTSET),以显示所有消息。
输出是这样的:
DEBUG:root:Here you have some information for debugging.
INFO:root:Everything is normal. Relax!
WARNING:root:Something unexpected but not important happend.
ERROR:root:Something unexpected and important happened.
CRITICAL:root:OMG!!! A critical error happend and the code cannot run!
让我们将日志级别更改为 WARNING。
在这种情况下,输出是:
WARNING:root:Something unexpected but not important happend.
ERROR:root:Something unexpected and important happened.
CRITICAL:root:OMG!!! A critical error happend and the code cannot run!
如您所见,因为我将我的日志记录级别设置为警告(使用logging.WARNING常量值),所以只显示严重性为警告或更高的消息。
好吧,使用日志工具来显示适当的消息是好的,但是我能更好地格式化我的消息来帮助我对我的代码进行日志记录吗?答案是肯定的。让我们阅读下一节来更好地格式化我们的日志消息(以一种更有意义的方式)。
格式化日志消息
使用日志模块来跟踪我们的代码的优势之一是能够根据我们的需要格式化消息。例如,在我的代码中,我希望用适当的消息记录日期和时间。这里有一个例子。
这是输出。
2021-02-14 23:02:34,089 | DEBUG: Here you have some information for debugging.
2021-02-14 23:02:34,089 | INFO: Everything is normal. Relax!
2021-02-14 23:02:34,090 | WARNING: Something unexpected but not important happend.
2021-02-14 23:02:34,092 | ERROR: Something unexpected and important happened.
2021-02-14 23:02:34,092 | CRITICAL: OMG!!! A critical error happend and the code cannot run!
如您所见,为了格式化我的日志消息,我需要向logging.basicConfig()传递一个字符串。格式化字符串可以混合包含字符和属性。例如,在我的格式化字符串中,属性是 as time(%(asctime)s)、levelname ( %(levelname)s)和 message ( %(message)s)。第一个属性 asctime 显示日志记录的日期和时间。levelname 属性输出日志级别(调试、信息等。).最后,我们有消息属性,这就是我们想要显示的消息。根据您的需要,您可以使用 20 个有用的属性。以下是他们的列表(来源)。

Python 文档 LogRecord 属性 https://docs.python.org/3/library/logging.html#logrecord-attributes ( 链接)。
写入文件
到目前为止,我们已经将日志消息发送到控制台进行打印。有时,我们需要将日志消息和信息记录在一个文件中作为记录。从打印到控制台再到在文件上写东西,切换起来超级容易。我们只需要向logging.basicConfig()传递一个文件名,就可以开始登录一个文件。这里有一个例子。

照片由 Aryan Dhiman 在 Unsplash 上拍摄
更好的记录方式
对于基本的日志记录,到目前为止提到的已经足够了。虽然如果你喜欢做更复杂的日志记录任务,你可能需要做得更专业。
在更高级的方法中,我们使用 logger 对象来跟踪代码中的事件。您需要通过 4 个简单的步骤来设置您的 logger 对象。
步骤 1)实例化一个记录器对象
步骤 2)设置日志记录的最低严重性
步骤 3)为您的日志设置一个目的地(也称为处理程序)(例如,控制台、文件或 HTTP。)
步骤 4)为处理程序设置消息格式
以下代码展示了所有 4 个步骤。
如您所料,这段代码的输出是:
2021-02-15 15:04:03,364 | ERROR: Something unexpected and important happened.
2021-02-15 15:04:03,364 | CRITICAL: OMG!!! A critical error happend and the code cannot run!
作为最后一个例子,让我们更进一步,假设您需要两个记录器。一个记录器只将重要消息(错误和更高)打印到控制台。第二个日志记录器在一个日志文件中记录更多的日志消息(INFO 和更高的信息),并提供更多的细节(例如,它包括执行行号或%(lineno)d)。下面是处理这种情况的代码。
如果您运行此代码,您将在控制台中看到以下输出。
2021-02-15 15:04:25,349 | ERROR: Something unexpected and important happened.
2021-02-15 15:04:25,350 | CRITICAL: OMG!!! A critical error happend and the code cannot run!
但是在你的日志文件(sample.log)里,你会看到更详细的信息。
2021-02-15 15:04:25,340 | INFO | 22: Everything is normal. Relax!
2021-02-15 15:04:25,349 | WARNING | 23: Something unexpected but not important happend.
2021-02-15 15:04:25,349 | ERROR | 24: Something unexpected and important happened.
2021-02-15 15:04:25,350 | CRITICAL | 25: OMG!!! A critical error happend and the code cannot run!
正如您在代码中看到的,我只是创建了一个 logger 对象。然后,我开始创建我的控制台处理程序,并设置它的级别和格式。我将控制台处理程序添加到 logger 对象中。我对文件处理程序重复了类似的过程。最后,我开始日志记录,日志记录程序正确地管理了两个处理程序。非常简单明了。现在,您可以想象用 print()语句复制相同的功能有多难。
有许多高级工具和设置,您可以在您的记录器中使用。例如,您可以使用 HTTP 日志处理程序(logging.HTTPHandler())通过 HTTP 将您的日志记录结果发送到一个中央 URL 地址。或者您可以使用过滤器来要求记录器对不同的事件做出不同的反应。
Python 日志库提供了许多用于处理不同日志任务的工具和设置。你可以在日志记录手册中了解更多关于所有可用工具和设置的信息。
摘要
使用 print()进行日志记录和调试是一种不好的做法。在 Python 中,我们在“日志”库中有专门的工具来处理不同的日志和调试任务。在本文中,我向您展示了如何使用基本的日志工具和设置。
停止使用 Print 或 Logger 来调试 Python 代码
代码调试冰淇淋包基本指南

图片来自 Pixabay 的 Dhruvil Patel
对每个开发人员来说,调试代码是一项重要但令人厌倦的任务。当代码输出不符合预期或抛出错误时,执行代码调试是非常重要的。调试是发现并修复程序中错误的过程。
语法错误和语义错误是开发人员在程序中面临的两种错误。语法错误是由命令或代码的错误键入、缩进错误引起的,通过遵循 Python traceback 指令很容易修复。当代码输出不符合预期时,会导致语义错误,这可能是由于算法的错误实现造成的。
语法错误很容易处理,但有时处理语义错误是一项困难且耗时的任务。开发人员需要检查代码片段并修复错误。
通常,开发人员使用打印语句或在代码片段之间添加日志来进行调试。有各种用于调试的开源 Python 库。在本文中,我们将讨论这样一个库冰淇淋,它可以帮助调试您的 Python 代码,而无需插入大量的打印和日志语句。
冰淇淋:
冰淇淋?是的,它是一个帮助开发者调试 Python 代码的 Python 包的名字。Icecream 帮助开发人员在调试时摆脱编写多个打印和日志语句来修复 bug。相反,他们可以使用冰淇淋包中的**ic**类。
安装:
冰淇淋包可以从 PyPl 安装,使用
**pip install icecream**
并导入**ic** 类进行调试**from icecream import ic**
用法:
Icecream 可以打印表达式/变量名及其值。输出格式也可以被格式化,并且还可以包括程序上下文,例如文件名、行号和父函数。
检查变量和函数:
**ic()**冰淇淋包中的函数检查自身,并打印自己的参数和这些参数的值。

(图片由作者提供)
只要给ic()一个变量、表达式或函数,就大功告成了。
检查执行情况:
开发人员通常使用记录器或打印语句来了解正在执行的代码部分以及函数流。在每个函数调用或循环中使用 ic()函数可以用来在调试时跟踪代码的执行。
要检查 Python 函数的执行情况,请将自定义函数作为参数传递给 ic()。ic()返回自定义函数的参数,因此可以很容易地在预先存在的代码中实现。

(图片由作者提供),带有 ic()的函数调用
通过观察 ic()函数的打印功能,可以很容易地跟踪函数的执行。
附加优势:
用户可以配置和定制**ic()**功能的输出。使用该函数,用户可以添加前缀、更改输出函数、定制输出语句的参数,以及包含或排除程序上下文,如文件名、行号和父函数。
**def unixTimestamp():
return '%i |> ' % int(time.time())**# add unix time stamp as prefix
**ic.configureOutput(prefix=unixTimestamp)**
完成代码调试后,您可以删除所有的 ic()函数调用,或者使用**ic.disable()**函数禁用输出。稍后可以使用**ic.enable()** 功能重新启用。
结论:
冰淇淋包可以用来代替编写多个打印或日志语句进行代码调试。输出格式的数据结构非常清晰,包括表达式/变量名及其值和其他程序上下文。在调试时编写打印语句或记录器来输出所有这些信息是一项单调乏味的任务,但是 ice cream 仅用几行代码实现就提供了所有这些。
除了 Python 之外,冰激凌包的实现还有 12 种编程语言,包括 C++ 、 Java 、 Ruby 、 R 、 Go 等。
参考资料:
[1]冰淇淋 GitHub:https://github.com/gruns/icecream
感谢您的阅读
停止使用打印!面向数据科学家的 Python 日志记录
大约 80%的您需要了解的关于 5 分钟内登录的内容

在每个生产数据科学项目中,都有代码变得复杂的时候,为了保持头脑清醒,重构是必要的。也许您希望将常用的代码抽象成包含类和函数的 Python 模块,以便可以通过一行代码重用它,而不是在项目中多次复制粘贴整个代码块。无论您的原因是什么,将信息性日志记录写入您的程序是至关重要的,以确保您可以跟踪它的操作,并在不可避免地出现问题时排除故障。
在本文中,我将分享我作为数据科学家需要了解的大约 80%的 python 日志功能。有了这些知识,我们可以实现以下两个要求:
- 向终端记录一些消息:例如,记录程序执行步骤。
- 同时将一些其他消息记录到一个文件中:例如,在模型训练和测试期间记录结果。
我的 Github 账户上有完整的代码
Python 日志模块
我们将使用 Python 日志模块[ 链接 ]中的以下功能来解决我们的两个需求。
基本配置
basicConfig功能,顾名思义,用于设置测井系统的基本配置。我发现在设置 basicConfig 时指定以下三个参数是有利的
level:表示记录消息的最低级别。下表显示了不同日志记录级别的值。例如,如果您设置了level=logging.INFO,则任何记录为DEBUG的消息都不会出现在您的日志中,而任何记录为INFO或以上的消息都会出现在您的日志中。format:日志信息出现的格式。我喜欢我的日志消息有时间(asctime)、级别名称(levelname)和实际的日志消息(message)。因此我指定format='%(asctime)s %(levelname)s: %(message)s'datefmt:时间出现的格式。我希望我的日志消息有一个完整的日期时间,所以我指定datefmt='%Y-%m-%d %H:%M:%S'以年-月-日小时:分钟:秒的格式记录时间。
设置日志记录的基本配置

Python 日志记录级别。图像来源[ 链接
获取记录器
现在我们已经设置了基本配置,我们可以在所有我们希望记录器工作的.py文件中使用一个公共名称来实例化一个记录器对象。我发现将这个通用名称存储在一个外部的constants.yaml或constants.py文件中是很有利的,然后我可以将它们导入到我想要使用同一个日志记录器的每个.py文件中。
实例化记录器对象
文件处理器
到目前为止,我还没有提到将任何日志消息写到文件中。因此,我们所有的日志消息将只显示在终端上。因为我们的第二个需求是将某些消息记录到一个文件中,所以我们将使用FileHandler和一个我称之为METRICS的自定义日志级别,只用五行代码就实现了这一点!只需确保这个自定义日志记录级别大于级别CRITICAL,以确保没有其他日志记录消息被写入文件。
设置文件处理程序,将“METRICS”自定义级别的某些日志写入文件“metrics.log”
把所有的放在一起
以上三个概念基本上是我们在代码中设置出色的日志记录功能并满足我们的两个需求所需要知道的全部内容。下面的三个.py文件展示了所有这些是如何一起工作的。
constants.py:该文件仅用于定义几个常量。一个用于自定义日志记录级别METRICS,另一个用于通用LOGGER_NAME,这样我们可以在多个其他.py文件中使用它们- 这是我们的主要 python 程序。请注意,我是如何使用上述概念来设置基本配置、实例化日志记录器和创建文件处理程序,以便仅将带有自定义
METRICS级别的日志消息路由到文件metrics.log。要使用我们的 awesome logger 实际记录一条消息,如果我们想用INFO级别记录一条消息,我们将调用logger.info,或者如果我们想用自定义的METRICS级别记录一条消息,我们将调用logger.log,如下面的代码所示。 test_print.py:这个文件展示了如何通过使用相同的LOGGER_NAME在另一个.py文件中实例化相同的记录器。这将把任何METRICS定制日志路由到同一个metrics.log文件。
定义常数
主程序
测试实例化和使用我们在主脚本中创建的相同记录器
结论:
将一些消息记录到终端并将一些其他消息记录到文件的能力对于数据科学程序来说是很方便的。我定期记录程序执行步骤,以跟踪程序到终端和模型训练的进度,在日常工作中将测试结果保存到文件中。如果您使用 MLFLOW ,您甚至可以使用mlflow.log_artifact('metrics.log')将此日志文件添加到您的 MLFLOW 服务器,以跟踪历史进度!
我希望这篇关于 Python 日志的文章对您有用。你也可以在我的 Github 账号上访问完整代码。感谢阅读!
在 Python 中停止使用 Print 进行调试。用冰淇淋代替
你是使用打印还是日志来调试你的代码?用冰淇淋代替。

照片由 Kenta Kikuchi 在 Unsplash 上拍摄
动机
如果您使用 print 来调试代码,您可能会发现在终端上查看多行输出,然后试图找出每个输出属于哪个代码是令人困惑的。
例如,运行下面的脚本
会给你
30
40
这些输出哪一个是num1?这些输出哪一个是num2?找出两个输出可能没那么难,但是如果有超过 5 个不同的输出呢?试图找到负责输出的源代码可能很耗时。
您可以尝试在 print 语句中添加文本,以便更容易理解:
num1 30
num2 40
输出更容易阅读,但是同样,写出文本也很耗时。有没有一种方法可以打印出负责输出的代码,而不需要像下面这样的附加文本?
ic| num1: 30
ic| num2: 40
这时候冰淇淋就派上用场了。
什么是冰淇淋?
Icecream 是一个 Python 库,它用最少的代码使打印调试更具可读性。
要安装冰淇淋,请使用
$ pip install icecream
让我们通过打印 Python 函数的输出来尝试一下。

通过使用ic,我们不仅可以看到输出,还可以看到函数及其参数!多方便啊!您终端中的颜色也将和上面显示的输出一样丰富多彩。
检查执行情况
要定位代码执行的位置,您可以做如下所示的事情来查找哪个语句被执行了
I'm user
冰淇淋让你更容易做类似上面的事情,只需运行ic()而不需要额外的文本

现在你知道第 5 行的代码在函数hello中被执行了,而第 7 行的代码没有被执行。
自定义前缀
如果您想在打印语句中插入一个自定义前缀,比如代码执行的时间,icecream 也允许您这样做。

现在代码执行的时间会自动显示在输出中!多酷啊。
我能得到更多的上下文吗?
除了知道负责输出的代码之外,您可能还想知道代码是从哪一行和哪一个文件执行的。要了解代码的上下文,请将includeContext=True添加到ic.configureOutput()

现在您知道第一个输出是由文件icecream_example.py第 7 行的函数plus_five执行的。
调试完成后删除所有冰淇淋
您可以将冰淇淋仅用于调试,而将打印用于其他目的,如漂亮的打印

既然你可以区分调试打印和漂亮打印,那么你在调试后搜索和删除所有的ic语句就容易多了。

删除所有调试打印后,你的代码就干净了!
结论
恭喜你!您刚刚学习了如何使用 icecream 使打印调试更具可读性。冰淇淋对我来说是一个很好的调试工具,我希望你也会发现它很有用。
我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以通过 LinkedIn 和 Twitter 与我联系。
如果你想查看我写的所有文章的代码,请点击这里。在 Medium 上关注我,了解我的最新数据科学文章,例如:
停止使用 SMOTE 处理所有不平衡的数据
过采样和欠采样技术的结合

弗兰基·查马基在 Unsplash 上拍摄的照片
在分类任务中,可能会遇到目标类别标签分布不均匀的情况。这样的数据集可以称为不平衡数据。数据中的不平衡可能会阻碍数据科学模型的训练。在不平衡类问题的情况下,模型主要在多数类上训练,并且模型变得偏向多数类预测。
因此,在进行建模管道之前,处理不平衡类是至关重要的。有各种类平衡技术通过生成少数类的新样本或移除一些多数类样本来解决类不平衡的问题。处理类平衡技术可以大致分为两类:
- 过采样技术:过采样技术是指创建人为的少数类点。一些过采样技术有随机过采样、 ADASYN 、 SMOTE 等。
- 欠采样技术:欠采样技术是指去除多数类点。一些过采样技术有 ENN 、随机欠采样、 TomekLinks 等。
阅读下面提到的文章中的来了解处理类不平衡问题的 7 种过采样技术。
</7-over-sampling-techniques-to-handle-imbalanced-data-ec51c8db349f>
使用欠采样技术的缺点是,为了平衡类,我们丢失了许多多数类数据点。过采样技术弥补了这一缺点,但在少数类中创建多个样本可能会导致模型过拟合。
SMOTE 是数据科学家社区中流行和著名的过采样技术之一,它在少数类样本的聚类中创建人工少数数据点。其思想是将过采样和欠采样技术结合起来,并且一起可以被认为是处理不平衡类数据的另一种采样技术。
过采样和欠采样技术的结合:
SMOTE 是著名的过采样技术之一,在处理类不平衡方面非常有效。其思想是将 SMOTE 与一些欠采样技术(ENN、托梅克)相结合,以提高处理不平衡类的效率。
SMOTE 和欠采样技术结合的两个例子是:
- 击杀 ENN
- 用托梅克击杀
在将 SMOTE 与欠采样技术结合起来之前,我们先来讨论一下什么是 SMOTE 以及它是如何工作的。
什么是击打?
SMOTE 代表合成少数过采样技术,是一种创建合成少数类数据点以平衡数据集的过采样技术。
SMOTE 使用 k-最近邻算法来创建合成数据点。SMOTE 算法的步骤是:
- 识别少数类向量。
- 决定最接近的数字(k)的数目,以供考虑。
- 计算少数数据点与其任何相邻数据点之间的连线,并放置一个合成点。
- 对所有少数数据点及其 k 个邻居重复步骤 3,直到数据平衡。

(图片由作者提供),SMOTE
SMOTE 和一些欠采样技术的结合被证明是有效的,并且一起可以被认为是一种新的采样技术。
SMOTE 与 Tomek 链接的组合:
Tomek 链接是一种欠采样启发式方法,它识别所有彼此最接近但属于不同类别的数据点对,这些数据点对(假设 a 和 b)被称为 Tomek 链接。Tomek Links 遵循以下条件:
- a 和 b 是彼此最近的邻居
- a 和 b 属于两个不同的类别
试探性地,这些 Tomek 链接点(a,b)出现在两个类别的分离边界上。因此移除 Tomek 链接点的多数类增加了类分离,并且还减少了沿着多数聚类的边界的多数类样本的数量。
想法:
SMOTE 是一种过采样技术,创建新的少数类合成样本,而 Tomek Links 是一种欠采样技术。
对于不平衡数据集,首先应用 SMOTE 创建新的合成少数样本以获得平衡分布。此外,Tomek 链接用于移除靠近两个类的边界的样本,以增加两个类之间的分离。
实施:
Imblearn 包实现了 SMOTE 和 Tomek 链接的组合。
您可以使用**pip install imblearn**从 PyPl 安装这个库
**from imblearn.combine import SMOTETomek****smt = SMOTETomek(random_state=42)
X, y = smt.fit_sample(X, y)**
SMOTE 与 ENN 的组合:
ENN(编辑过的最近邻)是一种欠采样技术,用于移除边界或边界上的多数类实例,这些实例由 KNN 算法做出的预测不同于其他多数类点。
类似于 SMOTETomek,首先应用 SMOTE 来创建少数类样本的合成数据点,然后使用 ENN 来移除边界或边界上的数据点以增加两个类的分离。
实施:
imblearn 包中还提供了 SMOTE 和 ENN 算法的组合。
**from imblearn.combine import SMOTEENN****smt = SMOTEENN(random_state=42)
X, y = smt.fit_sample(X, y)**
结论:
SMOTE 和欠采样技术(ENN 和托梅克链接)的结合被证明是有效的。SMOTE 基本上用于创建少数类的合成类样本以平衡分布,然后欠采样技术(ENN 或托梅克链接)用于清除两个类的边界中的不相关点以增加两个类之间的分离。
Imblean 包附带了 SMOTETomek 和 SMOTEENN 的实现。有一条经验法则告诉我们哪种方法最有效。人们可以编写手动 Python 代码来组合一种过采样技术和一种欠采样技术,以获得最佳结果。
你也可以阅读下面提到的文章中的来了解处理类不平衡的 7 种过采样技术。
</7-over-sampling-techniques-to-handle-imbalanced-data-ec51c8db349f>
参考资料:
[1]不平衡学习 API 文档:http://glemaitre.github.io/imbalanced-learn/api.html
感谢您的阅读
停止用火花做 ML!
让你的机器学习管道尽可能简单的指导方针。

以赛亚·鲁斯塔德在 Unsplash上的照片
如果您有大量的数据需要处理,Spark 是很好的选择。spark 和 Pyspark(用于与 Spark 交互的 Python API)是数据工程师工具箱中的关键工具。有一个很好的理由:
“无论您的数据增长到多大,您仍然能够处理它。”
是一个常见的论点。尽管现代公司使用 Spark 端到端构建“经典”数据管道来组合、清理、转换和聚合他们的数据以输出数据集是有效的。
ML 的火花成本
对于构建数据管道的数据科学家和 ML 工程师来说,上述论点并不总是成立的,这些管道输出机器学习模型。对于机器学习管道来说,涉及到成本:
- 👭熟悉度— 从我的经验数据来看,科学家对(Py)Spark 的熟悉程度不如对熊猫。两者都使用数据帧的概念,但是用于操作数据帧中的数据的 API 是完全不同的。这里有一个熊猫对 Pyspark 的例子,我们创建了一个新的列:
df['Age_times_Fare'] = df['Age'] * df['Fare'] df = df.withColumn("AgeTimesFare", $"Age" * $"Fare") - 🔧维护—Pandas、Scikit-learn、Pytorch、Tensorflow 等 Python 包是数据科学家和 ML 工程师的主要工具。通过引入 Spark,您在您需要维护的管道中引入了第二个额外的数据处理工具。
- 🏋🏽训练复杂性— Spark 在 Python 进程之外的集群上运行,这增加了测试和运行定制代码的复杂性。对于测试,您必须在您的系统上配置一个 Spark 环境。当使用 PySpark 时,第三方 Python 库需要被运送到 Spark 执行器以避免“导入错误”。
- 🔄推理复杂度—根据我的经验,Spark/SparkML 很少用于建模,虽然我看到 Spark 用于特征工程。使用特征工程,您最好在推理时从训练时复制相同的步骤。为此,您可以在训练时将特征工程步骤打包为模型,并在推理时使用该模型。因为 ML 模型通常基于不同的技术(Scikit-learn、PyTorch、Tensorflow 等),所以您需要在推理时管理基于 Spark 的特征工程模型和 ML 模型之间的编排。这增加了运行模型的复杂性。
我不是说你应该把火花扔进垃圾桶。在选择它的时候反而显得挑剔。
简单性准则
在编写一行 Spark 代码之前,下面的指南提出了一些替代方法和更简单的方法。
1.垂直缩放🐣
尝试垂直扩展您的计算实例,以便您可以将所有数据保存在内存中。尝试使用 Pandas 处理您的数据,看看处理时间是否仍然合理。
2.你需要所有的数据吗?📈
您是否需要所有数据来实现业务价值?多一年数据的附加值是什么?
你的模型性能的收益是否超过涉及 Spark 的成本?在获取更多数据之前,请查看您的 ML 评估曲线。如果你已经有了好的结果,并且你没有过度拟合,也许你不需要更多的数据。
3.结构化查询语言📇
在编写一行(Py)Spark 代码之前,检查一下您是否无法通过使用 SQL 达到您的目标。一般来说,SQL 更容易编写和维护。如果您已经有了一个 Spark 集群,请尝试 SparkSQL。你也可以尝试通过一个无服务器的 SQL 引擎来公开你的数据,比如谷歌大查询或者 AWS Athena。您甚至可以通过 dbt 使用 SQL 进行更高级的转换,看看吧!
4.云服务☁️
如果你需要建立一个 Spark 集群,首先检查一下无服务器云服务比如 AWS Glue 。它允许您在几秒钟内启动多达 100 个执行器的 spark 集群,其中每个执行器有 4 个 CPU 和 16GB RAM。
如果你需要一个更具可配置性的集群,并且你已经对你正在做的事情有所了解,你可以尝试一个托管服务,比如 AWS EMR 。
火花的替代品
一些替代方案可以帮助您处理大量数据:
Vaex
Vaex 是一个 Python 数据处理库,API 与 Pandas 类似。这是一个核外解决方案,每秒可以处理多达 10 亿行。数据集的大小限制等于硬盘的大小。⁴
达斯克
Dask 在某种意义上类似于 Spark,你可以水平扩展来处理大量数据。但这是不同的,因为 Dask 是用 Python 编写的,并且支持 Pandas 和 Scikit 这样的库——开箱即用。
如果你有任何建议,让我知道在回应!
文森特·克拉斯
👋如果您想了解更多关于 ML 工程和 ML 管道的信息,请关注我的 Medium 、 Linkedin 和 Twitter 。
脚注
[1]:https://www . signify technology . com/blog/2019/04/pandas-vs-spark-how-to-handle-data frames-part-ii-by-Emma-grima ldi
【2】:在 AWS 上,您可以以大约 8.5 美元/小时的价格访问 ml . r 5.24 xlage 等具有 96 个 CPU 和 768 个 GiB 内存的实例。更多信息可在此处。
【3】:https://towards data science . com/vaex-out-of-core-data frames-for-python-and-fast-visualization-12c 102 db 044 a
【4】:https://www . kdnugges . com/2021/05/vaex-pandas-1000 x-faster . html
停止使用机器学习简易按钮

里奇·史密斯在 Unsplash 上的照片
仅仅因为你知道 ML,并不意味着你应该把它应用到每一个问题上。
随着机器学习 MOOCs 和更便宜的计算能力的兴起,数据爱好者探索机器学习和数据科学工具包的深度变得更加容易。在流行新闻中不断出现围绕机器学习的成功故事,数据外行人开始对数据科学感兴趣。在我继续之前,我需要明确一点,我认为自学成才的数据科学家的不断增加总体上是数据科学领域的一个显著发展。随着越来越多的人加入进来,这只会带来进步。我在这里关注的是那些实际上是从您刚刚通过的在线课程中复制粘贴的模型。
“给一个小男孩一把锤子,他会发现他遇到的一切都需要敲打。发现一个科学家用一种他自己特别擅长的技术来解决问题,这并不奇怪—亚伯拉罕·卡普兰
仅仅因为你学习了 TensorFlow 并不意味着你从事的下一个项目需要 12 层的神经网络。我们从新闻中看到的对机器学习、人工智能和所有其他数据科学术语的兴趣增加,往往在商业世界中获得更多动力;尤其是在大公司。它传播了一种“如果我不使用 ML,我的公司将被远远甩在身后”的感觉。再加上分析师现在正在学习通过几行 python 代码创建深度学习模型的方法,我们很快就会忘记优秀的老式分析的基本原理。
一个漂亮的演示文稿覆盖了一个分析师创建的最新的神经网络、决策树或回归模型,但并不能对模型的有用性提供任何信心。出于某种原因,不管机器生成的模型和人类创建的模型表现如何,它们似乎都有一种可信度,至少在商业领域是如此。
对于一个消费者来说,知道这个网站会了解你喜欢什么,并根据你的反馈提出相关建议,这真是太棒了。听起来太棒了!即使你只看过《办公室》、《公园和娱乐中心》和《雷诺 911 》,模型推荐《教父》也没关系。模型了解你,它在学习,它只是犯了一个可爱的错误…对吗?
会有什么后果?
今天,大多数公司的经理和高管通常对机器学习有足够的了解,可以在令人惊叹的演示过程中跟上,但很少有足够的能力识别出一个糟糕的模型。这不是针对管理层的一枪。他们不是被雇来评估模型的,而是被雇来评估人的。他们知道约翰逊是一个质量分析师,他有最好的意图,见鬼,他甚至一直在参加 Coursera 的机器学习课程!
更多的股票投入到模型存在的事实中,而不是模型产生的东西中。现在,这显然是一个概括,不适用于更倾向于技术的行业。不过,对于一家企业来说,看到一个未能捕捉到制造缺陷的糟糕神经网络的不良影响,要比错误分类客户情绪容易得多。当然,我们在测试中达到了 82%的准确率,当我们分析结果时,每个人都认为事情看起来不错。但是,一旦这种模型被投入生产,哪家公司会花时间或金钱来密切关注它呢?
只要它还在输出结果,另一个员工需要做的手工工作就少了。最重要的是,可能会有一些新的 KPI 来跟踪模型自然产生的量,一切都很好。让我们把约翰逊放在下一个项目上,他做得很好!
有什么问题?有总比没有好。
我经常发现这些模型最终建立了依赖关系,或者是业务流程的基线。这最终导致员工从实际上更有效的手工工作中转移出来,尽管范围可能更有限。是的,一台机器现在正在扫描 1,000 个事件,而我只需要审查它抛出的 5 个事件,而我以前是手动审查 30 个案例。这听起来是一个了不起的进步,对吗?
不对。
如果您的手动过程是热垃圾,而模型是稍微冷一点的垃圾,那么好吧,也许这样更好。更有可能的是,您的手动过程实际上在识别您正在寻找的用例方面相当有效。此外,您将更加熟悉输出、流程和功能。你回顾的每一个不正确的用例都意味着时间的浪费,一般来说,人们不喜欢在无意义的任务上浪费自己的时间。与一般的 ML 模型相比,随着时间的推移,这导致对手动过程的小的优化,以及更具体和精确的过程。
同样,我关注的是生产过程中的“简单按钮”模型。给予模型适当的时间、资源、监督,并致力于解决正确的问题,模型几乎总是胜过人工过程。然而,因为对于较大的公司来说,从手工工作的转变通常是渐进的,很少有归因于模型本身或开发人员的。相反,管理层可能会看到业绩下降,并寻求新的模式来帮助解决看似新的业务问题。进一步延续循环,但希望下一个模型至少可以从第一个模型中吸取一些教训。
那么对此能做些什么呢?
要批判。
确保你面临的这个新问题能够真正受益于机器学习。如果是这样,那么绝对要试一试,但要对结果持批评态度。如果看起来不太好,那么你应该知道你已经尝试过了,并且可能在这个过程中学到了一些东西。否则,别忘了基本分析。
随着大数据的兴起,数据透视表受到了不好的指责,但让我们现实一点——关键的业务流程仍然依赖于它们,如果做得好,它们可能比大多数机器学习模型更有洞察力。
现在,销售的计数和总和不会刺激新的行动,但你只是试图为你的新模型想出新的功能,对不对?让我们把那些小狗放进去,开始四处看看。是的,我明白这是探索性分析。探索性分析的结果通常比试图回归和不相关变量的 Jupyter 笔记本更有洞察力和可操作性。
与机器相比,人类的速度很慢,我们可以很容易地看出,功能 1 在 b 维中似乎真的不正常。沿着这条线索深入探索,您将更有可能发现问题的实际原因,而不是实现一个模型来帮助您缓解问题。
概括起来
- MOOCs 很棒,更多的人学习数据科学很棒,但不是每个通过课程的人都应该将模型推向生产。
- 仅仅因为一个模型是活的,并不意味着它总是比老式的手工过程好。
- 不要试图开发一个 ML 模型来解决一个可以通过基本/探索性分析解决的问题。
- 如果模型是相关的,要对结果持批评态度,并接受有时它不会返回有意义的结果。数据科学应该会遇到很多死胡同,如果你没有发现任何死胡同,那么你就没有做对。
停止使用 XGBoost…
意见
而是使用 CatBoost。

目录
- 介绍
- XGBoost 不利
- CatBoost 优势
- 摘要
- 参考
介绍
我首先要说的是,XGBoost 是一种非常强大的机器学习算法,已经被证明赢得了无数的数据科学比赛,并且很可能处于数据科学中最专业的用例的最前沿。出于这个原因,这种算法在过去几年中一直受到关注,这是一件好事。也就是说,这意味着它的缺点也受到了关注,因此,产生了开发类似算法的动机,这种算法在 XGBoost 没有的地方表现出色。让我们讨论 XGBoost 及其竞争对手 CatBoost,并强调为什么您会想要使用这种新兴的算法,无论您是在学校,还是作为专业数据科学家和/或机器学习工程师工作。请继续阅读下面的内容,了解 XGBoost 的缺点以及 CatBoost 的优点。
XGBoost 不利

Sebastian Unrau 在Unsplash【2】上的照片。
这个算法【3】是基于决策树的,利用优化的分布式梯度推进。
仅数值
XGBoost 很棒——有数值。这意味着它不能处理分类特征,除非它们被转换以使模型工作。大多数数据科学家会使用 one-hot-encoding,它会将特征值转置为列本身,并将一个0或1作为值。这也很棒,但是会导致另一个问题。
稀疏数据集
这里我所说的稀疏是指,因为一个分类特性不能有一个单独的列,所以该分类特性中的唯一值有多少列就有多少列。例如,如果有 100 个唯一值,您将有 100 多列追加到您的数据集,充满 0 和 1。当然,这会导致另一个问题。
长期培训
随着越来越多的分类要素转换为数值要素,这可能意味着您的模型将需要更长的时间来运行更大的数据集。
部署
当您想要部署模型并对新数据进行推断或预测时,这些数据必须与您的训练数据具有相同的结构。这个问题意味着您将不得不转换您的预测数据,这将导致更多的代码、更多的出错空间以及更多的麻烦。
因素
XGBoost 非常强大,但是参数太多,也很难调整。理解所有参数并对它们进行调优可能需要一些时间。
如您所见,这些缺点大多与分类特征有关。所以,你可以看到为什么会有一个类似的模型与 XGBoost 有很多相同的好处,但是专注于修复 XGBoost 的类别问题。因此,命名为 CatBoost——分类增强。
CatBoost 优势

Ludemeula Fernandes 在Unsplash【4】上拍摄的照片。
这个算法【5】也是一个基于梯度提升无决策树的高置换算法,重点是处理分类特征,这在过去常常被忽略。以下是 CatBoost 的优势:
处理分类特征
- 不需要将分类特征转换成数字特征
- 这意味着您不必对数据进行太多的预处理(——取决于您的用例)
- 您的数据集可以保持其原始格式,如原始列,而不必制作一次性热编码列,这更容易/更简单,更有利于部署,并向利益相关者解释您的功能
简单参数
- 利用 CatBoost 的默认参数最有可能获得最佳结果
- 也就是说,花在调整参数上的时间更少
准确度提高
- 虽然 XGBoost 在大多数算法中往往具有最好的准确性,但 CatBoost 已经在严重的流行数据集上被证明可以击败它
这两种算法都很棒,但是如果你有分类特征,那么 CatBoost 是一个很好的选择。
摘要
这篇文章是片面的,因为我强调了一种算法的优点,同时讨论了另一种算法的缺点。然而,这就是为什么 CatBoost 是这样的。但是,没有 XGBoost 就没有 CatBoost,所以总而言之,算法和各自的库都是强大的、有益的,最终由您决定使用哪一个。还可能出现其他因素,比如文档。XGBoost 有更多的在线文档和示例,这是 CatBoost 努力的地方,但随着时间的推移,我可以看到 CatBoost 在这方面有所改进。或许,会出现另一种甚至比 CatBoost 更好的算法。
总而言之,以下是使用 CatBoost 的主要原因:
* Ease of using categorical features* Implementation of dataset processing* Improved accuracy
我希望你觉得我的文章既有趣又有用。如果你同意这些比较,请在下面随意评论——为什么或为什么不同意?你更喜欢 XGBoost 还是 CatBoost?如果你有,请随意评论你喜欢其中一个的原因。CatBoost 有哪些缺点?你是否知道一种更新的机器学习算法和库,在简单性、易用性和准确性方面可以最好地超越 CatBoost?谢谢你看我的文章!
请随时查看我的个人资料和其他文章, 马特·普日比拉 ,也可以在 LinkedIn 上联系我。
参考
[1]图片由 Pacto Visual 在 Unsplash 上拍摄,(2016)
[2]Sebastian Unrau 在 Unsplash 上拍摄的照片,(2015)
[3] xgboost 开发人员, XGBoost 文档,(2020)
[4]lude meula Fernandes 在 Unsplash 拍摄的照片,(2017)
[5] Yandex, CatBoost 文档,(2021)
停止在 PyTorch 数据集上浪费时间!
使用 FiftyOne 加速和简化数据集工作流的指南

图片217425.jpg(CC BY 2.0)来自 COCO 数据集在五十一中可视化
PyTorch 是最受欢迎的深度学习库之一。它在简单易学和快速创建和训练模型的强大框架之间提供了最佳平衡。像 py torch Lightning这样的扩展使得编写和扩展网络变得更加容易。
虽然您可以轻松地创建和训练复杂的模型是一件好事,但是构建模型架构只会让您在完成任务的过程中走得更远。
在您的任务中实现高性能不仅取决于您的模型架构的改进,还取决于您的数据的改进。
根据您的任务,细化数据集可能会变得困难。尤其是当您增加数据的维度时。处理表格数据通常比处理图像或视频数据集更简单,因为图像或视频数据集无法一次性加载到内存中。
PyTorch 数据集为加载复杂的数据集提供了一个很好的起点,让您定义一个类来从磁盘加载单个样本,然后创建数据加载器来有效地向您的模型提供数据。当您想要开始迭代数据集本身时,问题就出现了。PyTorch 数据集是刚性的。他们有许多限制。首先,它们需要为任何更改进行大量的代码重写,这导致在项目的整个生命周期中浪费了几十个小时。其次,它们仅仅是从磁盘加载数据的一种方式,它们不支持任何可以帮助您构建更好的数据集的数据可视化或探索。
五十一,我一直致力于开发的类似熊猫的开源可视化数据集工具,可以与 PyTorch(和许多其他工具)配合使用,帮助你更接近数据集并与之互动。它为图像和视频数据集提供了更加灵活的表示方式,允许您在51 API和应用的帮助下搜索、切片和可视化它们,而无需因频繁更改而进行任何重写。虽然本文主要是关于 PyTorch 的,但是其他常见的框架,比如 TensorFlow,也面临类似的挑战。

第五十一个应用程序示例(图片由作者提供)
让 fiftone 如此灵活地克服这些 PyTorch 数据集限制的神奇之处在于fiftone Views。基本上,从一个通用 51 数据集,你可以用一行代码创建一个数据集的特定视图;然后,该视图直接用于创建 PyTorch 数据集。
例如,假设您训练了一个在汽车、卡车和公共汽车之间混淆的对象检测模型。首先训练模型以将它们都预测为“交通工具”可能是有益的。将 FiftyOne 整合到您的培训工作流程中可以让这变得像以下一样简单:

在第五十一个应用中可视化vehicle_view(图片由作者提供)
PyTorch 数据集与 51 个数据集很好地协同工作,解决困难的计算机视觉问题,如分类、对象检测、分割和更多,因为您可以使用 51 个数据集来可视化、理解和选择数据,然后使用这些数据来训练您的 PyTorch 模型。FiftyOne 数据集的灵活性使您可以轻松试验和微调用于训练和测试的数据集,从而更快地创建性能更好的模型。在这篇博文中,我将重点放在物体检测上,因为这是最常见的视觉任务之一,同时也相当复杂。然而,这些方法适用于大多数 ML 任务。具体来说,在这篇文章中,我将介绍:
- 将您的标注数据集加载到 51
- 编写 PyTorch 对象检测数据集,利用您加载的 51 个数据集
- 浏览 51 个数据集的视图以进行训练和评估
- 在 51 个数据集视图上训练火炬视觉对象检测模型
- 在 51 中评估您的模型以优化您的数据集
- PyTorch 之外的 51 个数据集上的训练模型
跟随在科拉布
你可以通过这款 Google Colab 笔记本在你的浏览器中直接关注这篇博文!
把你的数据加到 51
将数据放入 FiftyOne 通常比放入 PyTorch 数据集更容易。此外,一旦数据达到 51,它将更加灵活,允许您轻松地查找和访问甚至是最具体的数据子集,然后您可以使用这些子集来训练或评估您的模型。
这篇博客的目标是训练一个物体检测模型,所以我只是用一个标准的物体检测数据集作为例子。具体来说,我使用的是 COCO 2017 数据集,我可以直接从五十一数据集动物园中加载它。为了让这篇文章更容易理解,我只使用了 COCO 数据集的一个子集(5000 个验证图像和标签),然后从这个子集创建定制的训练和验证分割。

51 中加载的 COCO 数据集的图片(图片由作者提供)
我们在本文后面需要图像的高度和宽度,所以我们需要计算数据集中图像的元数据:
加载您的自定义数据
如果你在磁盘上有遵循某种格式的数据(例如用于分类的目录树、 COCO 检测格式或更多),那么你可以在一行代码中将它加载到 FiftyOne 中:
如果你的数据集没有遵循标准格式,不要担心,把它放到第五十一个中仍然很容易。您只需要创建一个51 个数据集,并迭代地将您的数据解析为51 个样本,然后将这些样本添加到数据集。
定义 PyTorch 数据集
PyTorch dataset 是一个类,它定义了如何通过一个简单的迭代器接口从磁盘加载一个静态数据集及其标签。它们不同于 51 数据集,51 数据集是面向可视化、查询和理解的数据的灵活表示。
这两种数据集表示之间的共生关系来自于这样一个事实,即 51 个数据集经过优化,可帮助您收集和整理用于训练的数据集,而 PyTorch 数据集旨在将静态数据集封装在一个标准接口中,该接口可在训练期间有效加载
使用 51 个数据集的灵活表示来了解和选择适合您任务的最佳训练数据,然后将该数据传递到 PyTorch 数据集,以便更快地高效加载更好的模型。
选择一个模型
每个 PyTorch 模型都希望数据和标签以某种格式传递给它。在能够编写 PyTorch 数据集类之前,您首先需要理解模型所需的格式。也就是说,我们需要确切地知道数据加载器在遍历数据集时应该输出什么格式,这样我们就可以在 PyTorch 数据集中正确地定义__getitem__方法。
在这个例子中,我正在遵循 Torchvision 对象检测教程并构建一个 PyTorch 数据集来处理他们基于 RCNN 的模型。如果您正在学习,这段代码使用一些实用程序和方法进行培训和评估,因此您需要在 PyTorch git 存储库中克隆教程代码:
# Download TorchVision repo to use some files from
# references/detection
git clone [https://github.com/pytorch/vision.git](https://github.com/pytorch/vision.git)
cd vision
git checkout v0.3.0cp references/detection/utils.py ../
cp references/detection/transforms.py ../
cp references/detection/coco_eval.py ../
cp references/detection/engine.py ../
cp references/detection/coco_utils.py ../
这些对象检测模型期望我们的 PyTorch 数据集为每个样本输出一个(image, target)元组,其中target是包含以下字段的字典:
boxes (FloatTensor[N, 4]):[x0, y0, x1, y1]格式的N包围盒的坐标,范围从0到W和0到Hlabels (Int64Tensor[N]):每个边界框的标签。0始终代表背景类。image_id (Int64Tensor[1]):图像标识符。它在数据集中的所有影像之间应该是唯一的,并在评估过程中使用area (Tensor[N]):包围盒的区域。这在评估 COCO 指标时使用,以区分小、中和大盒子之间的指标得分。iscrowd (UInt8Tensor[N]):is crowd = True 的实例将在评估过程中被忽略。(如果你的数据集不支持群组,那么这个张量将总是0的)
以下代码使用 Torchvision 的 ResNet50 主干加载 Faster-RCNN,并根据我们正在训练的类的数量修改分类器:
( )上述 ) 的代码来源和字段描述
通常,无论您使用什么模型,相应的 PyTorch 数据集都需要输出加载的图像数据以及每个样本的相关注释和元数据。例如,对于分类任务,target将只是一个代表样本所属类别的整数。
编写 PyTorch 数据集
既然我们已经决定了模型并理解了加载的数据需要遵循的格式,我们可以编写一个 PyTorch 数据集类,它接受 51 个数据集作为输入并解析相关信息。由于 API 被设计为易于使用,因此从 51 数据集解析样本通常比从磁盘解析样本更容易。
数据集类的构造函数需要接受我们的 51 个数据集,创建一个image_paths列表,这个列表可以被__getitem__方法用来索引单个样本,还可以通过文件路径访问相应的 51 个样本。FiftyOne 将类标签存储为字符串,因此我们还需要将这些字符串映射回整数,以供模型使用。
然后__getitem__方法需要接受一个唯一的整数idx,并使用它来访问相应的 51 个样本。然后,由于这些模型是在 COCO 数据集上训练的,我们可以使用 FiftyOne 中的 COCO 实用程序将样本中的每个检测重新格式化为 COCO 格式,用于检测、标签、区域和人群。为了可用性,我们还添加了长度和get_classes方法。
构建一个 51 视图
一旦为您的数据和模型组合构建了 PyTorch 数据集,您就需要创建一个 PyTorch 数据加载器。这些数据加载器是使用您编写的数据集代码来导入数据的可迭代程序。它们相当简单,但是提供了一些有用的功能,比如混排、批处理和并行加载数据。
由于我们决定在 51 中备份我们的数据集,我们还可以通过在我们的数据集中创建视图来执行像拆分和重组我们的数据这样的操作。第 51 个数据集上的[take](https://voxel51.com/docs/fiftyone/api/fiftyone.core.collections.html?highlight=take#fiftyone.core.collections.SampleCollection.take)方法返回一个子集,该子集包含数据集中的随机样本。[exclude](https://voxel51.com/docs/fiftyone/api/fiftyone.core.collections.html?highlight=exclude#fiftyone.core.collections.SampleCollection.exclude)方法阻止我们从验证分割的训练分割中提取任何样本:
现在,假设您想要更具体地了解用于训练和测试模型的数据。例如,您可能想要对特定的类子集进行训练或重新映射一些标注。如果您只是使用 PyTorch 代码,那么您需要返回并重写您的数据集类,甚至可能返回更改磁盘上的数据集文件。
FiftyOne 可以做的远不止是 拆分 和 混排 数据而且可以很容易地得到你的模型所需要的数据。与您的 51 个数据集交互的主要方式之一是通过不同的 视图 进入您的数据集。这些是通过应用类似于过滤、排序、切片等操作构建的,这些操作会产生数据集的某些标签/样本的特定视图。这些操作使试验不同的数据子集变得更容易,并继续微调数据集以训练更好的模型。
例如,杂乱的图像使得模型很难定位对象。我们可以使用 FiftyOne 来创建一个视图,其中只包含超过 10 个对象的样本。您可以对视图执行与数据集相同的操作,因此我们可以从该视图创建 PyTorch 数据集的实例:

在第五十一个应用中可视化 busy_view(图片由作者提供)
另一个例子是如果我们想要训练一个主要用于道路车辆检测的模型。我们可以轻松地创建只包含类car, truck,和bus的训练和测试视图(以及相应的 PyTorch 数据集):

在第五十一个应用中可视化vehicles_view(图片由作者提供)
训练模型
既然我们已经决定了要用来训练和测试模型的数据,下一步就是构建训练管道。这取决于你想用你的模型完成什么。PyTorch 中构建和训练模型的细节超出了本文的范围,为此,我建议您参考其他资料[ 1 、 2 、 3 、 4 ]。
对于这个例子,我们正在按照 PyTorch 对象检测教程编写一个简单的训练循环。该函数将模型和我们的 PyTorch 数据集作为输入,并使用 Torchvision 对象检测代码中的train_one_epoch()和evaluate()函数:
让我们继续上一节中的车辆示例。我们可以使用torch_dataset和torch_dataset_test来定义和训练模型:
每个时期,这个训练循环在测试分割上打印评估度量。在我们从 COCO 数据集选择的样本上经过 4 个时期后,我们已经达到 36.7%的 mAP。这并不奇怪,因为这个模型是在 COCO 上预训练的(但不是这个类的子集)。
IoU metric: bbox
Average Precision (AP) @[ IoU=0.50:0.95 ] = 0.367
评估模型
打印评估指标在训练期间非常有用,这样您就可以看到模型学习的速度以及何时开始饱和。然而,这个 Torchvision 评估协议仅返回数据集范围的地图度量。为了更好地了解您的模型在哪里表现得好和不好,从而有希望改进它,需要您查看模型在单个样本上的表现。
51 的一个主要吸引人的地方是能够找到你的模型的故障模式。内置评估协议告诉你你的模型哪里做对了,哪里做错了。在我们评估这个模型之前,我们需要在我们的测试集上运行它,并将结果存储在 FiftyOne 中。这样做相当简单,只需要我们对测试图像进行推理,获得它们对应的 51 个样本,并为每个样本添加一个名为predictions的新字段来存储检测结果。
返回的[DetectionResults](https://voxel51.com/docs/fiftyone/api/fiftyone.utils.eval.detection.html#fiftyone.utils.eval.detection.DetectionResults)对象存储像地图这样的信息,并包含让您绘制混淆矩阵、精确召回曲线等等的函数。此外,这些评估运行在 51 中被跟踪,并且可以通过像[list_evaluations()](https://voxel51.com/docs/fiftyone/api/fiftyone.core.collections.html#fiftyone.core.collections.SampleCollection.list_evaluations)这样的功能来管理。
然而,我们感兴趣的是,[evaluate_detections()](https://voxel51.com/docs/fiftyone/api/fiftyone.core.collections.html#fiftyone.core.collections.SampleCollection.evaluate_detections)用属性更新了predictions字段中的检测,这些属性表明它们是真阳性、假阳性还是假阴性。使用 51 个视图和应用程序,我们可以通过误报排序快速找到模型表现最差的样本:

假阳性最多的样本视图(作者图片)
通过查看这些样本,一个模式浮现出来。一些卡车和汽车的注释实际上是不正确的。像货车和 SUV 这样可以互换标注为轿车或卡车的东西,似乎有些混淆。

标注为“卡车”但预测为“轿车”的 SUV 图片(图片由作者提供)
看到这些类之间的混淆矩阵会很有趣。默认情况下,评估仅匹配具有相同类别(classwise=True)的基本事实对象的预测。我们可以使用classwise=False重新运行评估,并绘制混淆矩阵。

汽车和卡车分类混淆(图片由作者提供)
最好是重新标注这些数据来修复这些错误,但同时,我们可以通过简单地创建一个新视图来重新映射标签car、truck和bus到vehicle,然后用它来重新训练模型,从而很容易地解决这个问题。由于我们的训练数据是由 51 个数据集支持的,这种转换很容易!
由于我们能够使用 FiftyOne 轻松地可视化和管理我们的数据集,我们能够发现数据集问题并采取措施,如果我们只关注数据集范围的评估指标和固定的数据集表示,这些问题可能会被忽视。通过这些努力,我们设法将模型的贴图增加到 43%。尽管这个示例工作流可能不是在所有情况下都有效,但是这种类合并策略在不需要更细粒度区分的情况下是有效的。
替代培训框架
如果您主要关注于开发一个新颖的模型架构,那么您可能希望直接从 PyTorch 开始。但是,如果您的目标是在自定义数据集和常见任务上训练模型,那么有许多训练框架可以让您更加轻松。
一些著名的目标检测训练框架是 Detectron2 和 MMDetection 。但是无论您选择什么样的框架,它们都不会直接使用 PyTorch 数据集和数据加载器,而是要求您以某种方式格式化磁盘上的数据。例如,Detectron2 和 MMDetection 希望您的数据以COCO 格式存储。一旦您重新格式化了您的数据集,它就可以直接加载到这些框架中并进行训练。
即使你没有自己加载数据集,51 仍然可以帮助你完成这个过程。有了 FiftyOne,你加载到其中的任何数据集或视图都可以导出成十几种不同的格式(包括 COCO)。这意味着您可以将数据集解析为 51 个,并在一行代码中导出,而不需要自己编写大量脚本来转换它。
摘要
PyTorch 和相关框架提供了快速简单的方法来引导您的模型开发和培训管道。然而,他们在很大程度上忽略了调整和微调数据集以有效提高性能的需要。 FiftyOne 是一款 ML 开发工具,旨在轻松将数据集加载到灵活的格式中,这种格式与现有工具配合使用,允许您为训练和测试提供更好的数据。俗话说,“垃圾进,垃圾出”。
关于体素 51
高质量、有针对性的数据对于训练优秀的计算机视觉模型至关重要。在 Voxel51 ,我们拥有超过 25 年的 CV/ML 经验,非常关心如何让社区将他们的 AI 解决方案带入生活。这就是为什么我们开发了 FiftyOne ,一个帮助工程师和科学家更快更好地完成 ML 的开源工具。
想了解更多?请在 fiftyone.ai 查看我们的网站。
不要担心杜松子酒的配置
gin-config能帮助消除数据科学项目中令人头痛的配置文件吗?

图片由 Ernest_Roy 在 pixabay 上提供
我们都去过那里,你在建模,一个接一个的超参数开始增加。起初,你忽略了这个问题,并告诉自己以后会回来解决它,但不可避免地不会。有许多解决项目配置问题的坏方法,它们包括但绝不限于:
配置项目的真正愚蠢的方法:
- 在你的脚本顶部创建一个变量列表,你可以修改它,直到你找到合适的为止
- 也许你只是为你动态改变的函数和类定义了默认值
配置项目的不那么愚蠢的方法:
最后两个选项并不是不好的选项,但是它们带来了额外的开销,即必须携带一个参数字典,您可以将它作为 T1 传递给函数或类。如果我们能把这些都抽象出来,让 Python 来帮我们做所有的重活,会怎么样?这就是杜松子酒的用武之地。Gin 是 Google 开发的一个轻量级 Python 库,专门尝试解决这个问题。在这篇文章中,我们将看一下如何使用这个库的基础知识,然后试着找出你是否真的会用到它。
如何使用杜松子酒
考虑以下函数:
def make_drink(drink_name:str, ingredients:list, ice:bool=False):
print(f"New drink called: {drink_name}")
print(f"Drink contains: " + ", ".join(ingredients))
if ice is True:
print("With ice")
else:
print("Without ice")
要使这个 Gin 可配置,我们需要做的就是给函数[@gin](http://twitter.com/gin).configurable添加一个简单的装饰器,它告诉库将这个函数索引为一个可以在.gin文件中配置的函数——如果你不知道什么是装饰器,我在下面链接了一篇解释它们如何工作的文章。
因此,让我们开始编写 gin 文件:
make_drink.drink_name = "G&T"
make_drink.ingredients = ["Gin", "Tonic", "Lemon"]
make_drink.ice = False
语法非常简单,第一部分是函数或类名,后面是一个点,然后是参数名。这种语法支持所有主要 Python 变量类型的赋值,正如你所料,这里我们有一个字符串、一个列表和一个布尔值,但同样也可以是一个字典、一个整数或一个元组。
所以现在我们的功能变成了:
import gin
gin.parse_config_file("path/to/gin_file.gin")[@gin](http://twitter.com/gin).configurable
def make_drink(drink_name:str, ingredients:list, ice:bool=False):
print(f"New drink called: {drink_name}")
print(f"Drink contains: " + ", ".join(ingredients))
if ice is True:
print("With ice")
else:
print("Without ice")
我们可以这样称呼它:
>>> make_drink()
New drink called: G&T
Drink contains:Gin, Tonic, Lemon
Without ice
如果你和我一样,这应该会让你感到烦恼。我们没有为任何参数定义任何默认值,所以对于任何阅读代码的人来说,不清楚这些值来自哪里!
Gin 的开发者看到了这一点,并提供了一个gin.REQUIRED值,可以用在参数的位置,以明确该值应该来自哪里。如果您未能在 Gin 文件中提供该值,这带来了自定义错误的额外好处。
>>> make_drink(gin.REQUIRED, gin.REQUIRED, ice=gin.REQUIRED)
New drink called: G&T
Drink contains:Gin, Tonic, Lemon
Without ice
现在让我们构建我们的代码并创建一个Drink类…
[@gin](http://twitter.com/gin).configurable
class Drink:
def __init__(self, name, ingredients, ice=True):
self.name = name
self.ingredients = ingredients
self.ice = ice[@gin](http://twitter.com/gin).configurable
def make_drink(drink: Drink):
print(f"New drink called: {drink.name}")
print(f"Drink contains: " + ", ".join(drink.ingredients))
if drink.ice is True:
print("With ice")
else:
print("Without ice")
print("\n")
类和函数都是可配置的。Gin 很酷,所以我们可以对它进行配置,让make_drink接受Drink的一个实例,该实例本身接受参数值:
make_drink.drink = [@Drink](http://twitter.com/Drink)()
Drink.name = "Gin Martini"
Drink.ingredients = ["Gin", "Vermouth"]
Drink.ice = False
并称之为…
>>> make_drink(gin.REQUIRED)
New drink called: Gin Martini
Drink contains: Gin, Vermouth
Without ice
但是你知道什么比一杯酒更好…两杯酒…让我们考虑下面的例子…
[@gin](http://twitter.com/gin).configurable
def make_two_drinks(drink1: Drink, drink2:Drink):
print("Making drink 1")
make_drink(drink1)
print("Making drink 2")
make_drink(drink2)
两者都需要一个Drink的实例,你可以试着写类似这样的东西。
make_two_drinks.drink1 = [@Drink](http://twitter.com/Drink)()
Drink.name = "Gin Martini"
Drink.ingredients = ["Gin", "Vermouth"]
Drink.ice = Falsemake_two_drinks.drink2 = [@Drink](http://twitter.com/Drink)()
Drink.name = "Gin Fizz"
Drink.ingredients = ["Gin", "Sparkling Water"]
Drink.ice = True
但是这不会有预期的效果。
>>> make_two_drinks(gin.REQUIRED, gin.REQUIRED)
Making drink 1
New drink called: Gin Fizz
Drink contains: Gin, Sparkling Water
With ice
Making drink 2
New drink called: Gin Fizz
Drink contains: Gin, Sparkling Water
With ice
我们只要两杯杜松子汽酒!幸运的是,Gin 有一个作用域的概念,允许我们为两个输入定义不同的饮料。
make_two_drinks.drink1 = [@drink1/Drink](http://twitter.com/drink1/Drink)()
drink1/Drink.name = "Gin Martini"
drink1/Drink.ingredients = ["Gin", "Vermouth"]
drink1/Drink.ice = Falsemake_two_drinks.drink2 = [@drink2/Drink](http://twitter.com/drink2/Drink)()
drink2/Drink.name = "Gin Fizz"
drink2/Drink.ingredients = ["Gin", "Sparkling Water"]
drink2/Drink.ice = True
这正是我们想要的。
>>> make_two_drinks(gin.REQUIRED, gin.REQUIRED)
Making drink 1
New drink called: Gin Martini
Drink contains: Gin, Vermouth
Without ice
Making drink 2
New drink called: Gin Fizz
Drink contains: Gin, Sparkling Water
With ice
如果一个函数调用另一个函数,并且两者都在 Gin 中进行了配置,那么树中最上面的将优先。
make_two_drinks.drink1 = [@drink1/Drink](http://twitter.com/drink1/Drink)()
drink1/Drink.name = "Gin Martini"
drink1/Drink.ingredients = ["Gin", "Vermouth"]
drink1/Drink.ice = Falsemake_two_drinks.drink2 = [@drink2/Drink](http://twitter.com/drink2/Drink)()
drink2/Drink.name = "Gin Fizz"
drink2/Drink.ingredients = ["Gin", "Sparkling Water"]
drink2/Drink.ice = Truemake_drink.drink = [@Drink](http://twitter.com/Drink)()
Drink.name = "French 75"
Drink.ingredients = ["Gin", "Champagne"]
Drink.ice = False
会给我们…
>>> make_two_drinks(gin.REQUIRED, gin.REQUIRED)
Making drink 1
New drink called: Gin Martini
Drink contains: Gin, Vermouth
Without ice
Making drink 2
New drink called: Gin Fizz
Drink contains: Gin, Sparkling Water
With ice
正如我们所期望的…
>>> make_drink(gin.REQUIRED)
New drink called: French 75
Drink contains: Gin, Champagne
Without ice
正如我们所料。
这里我已经向您介绍了基本功能,Gin 包含了大量的其他特性,比如宏和以编程方式设置和检索配置参数的能力。
真实世界的例子
这是基础知识,接下来我们将看看这如何应用于现实世界的例子。我们将构建一个快速模型和一个训练循环,并展示如何使用 Gin 来配置它。
import torch.nn as nn
import torch.nn.functional as F[@gin](http://twitter.com/gin).configurable
class DNN(torch.nn.Module):
def __init__(self, input_size, output_size, hidden_size):
super(DNN, self).__init__()
self.fc1 = nn.Linear(input_size, hidden_size)
self.fc2 = nn.Linear(hidden_size, hidden_size)
self.fc3 = nn.Linear(hidden_size, output_size) def forward(self, x):
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x[@gin](http://twitter.com/gin).configurable
def train(model: torch.nn.Module,
optimizer: torch.optim.Optimizer,
loss_func: torch.nn.modules.loss,
epochs: int,
train_dataloader: torch.utils.data.DataLoader,
):
opt = optimizer(model.parameters()) for epoch in range(epochs): for i, (inputs, labels) in enumerate(train_dataloader):
opt.zero_grad() outputs = model(inputs)
loss = loss_func(outputs, labels)
loss.backward()
opt.step() print('Finished Training')
我们可以像这样建立一个 Gin 文件…
train.epochs = 10
train.model = [@DNN](http://twitter.com/DNN)()
DNN.input_size = 10
DNN.output_size = 10
DNN.hidden_size = 128
这很好,但是优化器和损失函数呢?我们也能配置它们吗?Gin 允许你注册外部函数,这样我们就可以!
gin.external_configurable(torch.optim.Adam)
gin.external_configurable(torch.nn.CrossEntropyLoss)
Gin 内置了很多 TensorFlow 和 PyTorch 函数,所以你不需要为每件事都这样做,你需要做的只是导入gin.tensorflow和/或gin.torch。我们的新 Gin 文件看起来像这样…
train.epochs = 10
train.model = [@DNN](http://twitter.com/DNN)()
DNN.input_size = 10
DNN.output_size = 10
DNN.hidden_size = 128train.optimizer = [@Adam](http://twitter.com/Adam)
Adam.lr = 0.001
train.loss_func = [@CrossEntropyLoss](http://twitter.com/CrossEntropyLoss)()
请注意Adam和CrossEntropyLoss()之间的细微差别,Adam 需要用模型参数实例化,这发生在fit函数中,而CrossEntropyLoss()作为实例化的类传递。
这真的有什么好处吗?
简单的回答是肯定的。我认为它节省了很多配置项目的任务带来的麻烦。我认为配置带来的困难,总的来说,是知道你可能会做过头。在配置文件中隐藏得越多,代码的可读性就越差。Gin 开发者也意识到了这一点,并表示我们应该“负责任地使用 Gin”。另一件值得补充的事情是,学习 us Gin 和学习最佳实践会有一点开销,这样您的代码仍然是 Pythonic 化的和可读的。这对于学习使用 TOML 或 YAML 来说就不那么真实了。使用这些有自己的学习曲线,但我只是觉得有更少的可能出错。
我将补充说明这个观点:为了写这篇文章,我花了几个小时研究这个库的一些核心功能。在现实中,我可能应该在愤怒地将它用于现实世界的项目后形成一种正确的观点。但是,嘿,你不应该相信我的意见,你自己试试,让我知道你是怎么想的。
相关信息
存储:构建机器学习模型的重要组成部分

在之前的一篇文章中,我提出了一个简单的框架来应对使用数据构建机器学习(ML)模型所涉及的各种挑战。这个框架如下图所示。

作者图片
上一篇文章讨论了数据收集的挑战,并展示了一个与数据收集问题相关的相对简单的基础设施栈。我们将从我们停下来的地方继续,更具体地说,尝试解决我们的 cat 识别应用程序的存储挑战。我还将描述我们在 Kheiron 使用的存储层。
但首先,我们回到我们的猫识别应用程序!
在我们开展卡特彼勒识别工作的早期,存储可能并不是一个大问题。我们会得到一批图像,然后把它们存储在本地的某个地方,也许是在共享的网络驱动器上,也许是在 S3 的云中。我看到的第一个存储挑战是数据孤岛,尤其是在 ML 中。一个例子可以说明这一点。
你可能还记得,我们一开始提供了一个简单的猫识别应用程序。随着时间的推移,我们的应用程序变得非常成功,企业要求我们通过提供一个新功能来增强它:在照片中突出猫的位置(本地化)。这项功能需要对我们现有的数据集进行一些修改,并建立一个新的 ML 模型,一个可以识别和定位图像中的猫的模型。后来,公司要求我们建立模型,识别亚洲国家常见的猫的图像。

作者图片
当这些新的请求进来时,阻力最小的途径就是简单地复制我们已有的数据,并用它来构建更新的模型。一个复制品将用于识别猫的原始 ML 模型。具有附加数据的另一个复制品将被用于建立具有本地化的新 ML 模型。然而,另一个副本将在稍后用于亚洲猫模型。此外,这些请求中的每一个都伴随着增量数据更改。定位模型需要原始图像之外的数据——它需要猫在训练数据集中的坐标。同样,亚洲模型将需要亚洲常见的猫的图像和一些元数据。
这种复制方法本身没有任何问题。我们在软件中一直这样做:我们分支和分叉代码库。我们这样做的原因是为了给一个特性开发团队,或者一个发布版本一个自己的小天地。然而,传统软件(软件 1.0 )有一个非常丰富的工具生态系统来促进这一点。我们有 Git 和其他版本控制系统来帮助分支和版本控制。这些工具也提供这些服务,即无需进行完整复制就可以进行分支。我应该注意到有一些新的工具,像 DVC ,正在试图解决这个问题。我没有使用过,也没有体验过——目前还没有。这种复制方法的主要好处是原型开发和开发的速度,而明显的挑战是存储和可管理性方面的成本。那么,我们该怎么办呢?
在我深入介绍我们在 Kheiron 的方法的细节之前,我将首先提供一点关于我们所做的事情和我们放松的一些约束的背景。凯龙为癌症检测,更具体地说是乳腺癌,建立 ML 模型。我们的数据集由乳房造影图像和关于这些图像的元数据组成。元数据包括人口统计和临床数据,如活检结果、癌症史、以前的乳腺癌检查等。前者对于理解我们的模型在各种人口分层中的有效性至关重要。后者形成了我们的地面真相的基础,这可以被简单地视为试图回答这个问题:“这个图像包含恶性肿瘤吗?”当我们建立 ML 模型时,我们依赖于原始图像以及元数据。
在考虑存储时,我们提出了一些有助于降低问题复杂性的要求和约束。这些是:
- 我们将整合 AWS 上的所有数据,包括非结构化数据(图像)和结构化数据(元数据)。
- 我们将对数据团队以外的任何人设置对此数据的只读权限。
- 我们不关心数据的版本,尤其是图像。
第一个限制相当明显。我们选择的存储基础架构将是 AWS。稍后,我将深入探讨我们选择哪种存储介质的细节。选择 AWS 的主要动机是不需要管理任何内部存储基础架构,并且易于在 AWS 上扩展基础架构。我们还想让我们的数据靠近我们的计算集群。我们将在下一篇文章中讨论这个主题。
第二个约束设置数据存储层和它的所有消费者之间的接口:只读。消费者,大部分是 ML 工程师建立模型,只能读取数据,不能修改。从我们的数据层读取后发生的任何修改都是短暂的,不是持久的。最常见的例子是图像变换(开窗、过滤、旋转等)。如果我们确实遇到了常见的数据转换,我们会在将数据保存到存储层之前尝试这样做。我们的目标是使我们存储的数据无需任何修改即可用于 ML 训练。
其中,最微妙的是第三个限制。它消除了解决版本问题的需要。忽略数据版本控制的主要动机——至少现在——是一个重要的假设:更多的数据意味着更好的 ML 模型。简单地说,我们假设如果我们的数据集随时间变化,主要是通过数据添加,那么我们的模型将会改进。只要这个假设成立,我们就不需要担心版本、快照和回到过去。最近可用的数据应该产生“最佳”模型。在下一篇文章中有更多关于模型评估和选择的内容。在某些情况下,我们确实关心用于训练模型的数据集,并希望在一组实验中固定该数据集。例如,我们可能正在评估各种模型,并希望修复训练和测试数据集来比较模型,而不用担心不同数据集的影响。我们可以在不制作复制品的情况下实现这一点,这一点很快就会得到证实。
考虑到这些限制,我们决定使用 S3 和红移来构建我们的存储堆栈,这是一个惊喜。我们使用 S3 存储所有非结构化数据,其中大部分是图像。Redshift 是我们存储所有图像元数据的首选数据仓库。

作者图片
值得注意的是,我们还在原始数据存储层使用了 S3,这是下面堆栈中的最顶层。顾名思义,原始数据是我们尚未映射或转换为可用于训练和构建 ML 模型的数据。这些数据有多种格式,具体取决于其来源。原始数据层下面的一层——角力层——负责清理、映射并将其转换为我们可以用来训练 ML 模型的格式。这些转换是使用 lambda 函数和 Apache Airflow 的组合来完成的。我们还使用我们开发的定制工具来直观地检查原始数据,以捕捉图像中经常出现的任何异常。简而言之,角力层负责将杂乱的数据(原始数据)映射到一些通用的格式和模式中,这些格式和模式很容易被 ML 训练管道使用。我们将在下一篇文章中讨论这个主题,它涉及使用这些数据进行分析和 ML 训练。
我刚才介绍的存储堆栈很简单。可能太简单化了,但这就是重点。我们希望从最简单的堆栈开始,并通过放宽要求来做到这一点。当前的栈完成了它想要解决的工作,即促进 ML 模型的训练。
【https://karimfanous.substack.com】最初发表于https://karimfanous.substack.com/p/storage-an-essential-component-of。
用于机器学习的存储和计算
关于 GPU,有几个重要的限制需要记住。

在过去的两篇文章中,我们讨论了与数据收集和存储相关的各种活动。这些是下面概述的 3 步过程的一部分。现在我们到了最后一步,这涉及到使用我们收集和存储的数据。

作者图片
使用数据是用它“做一些事情”的功能,为了这篇特别的文章,它将被限制在使用数据建立人工智能模型的范围内。为了建立这些模型,我们需要数据,我们已经讨论了如何获取和存储数据,更重要的是,需要计算资源来使用这些数据训练模型。
人工智能模型主要使用 GPU 进行训练。GPU 是训练模型的事实标准,因为它们可以同时处理多个计算。它们有大量的内核,这允许更好地计算多个并行进程,这是 ML 模型可以极大受益的特性。
关于 GPU,有几个重要的限制需要记住。
首先,它们也相当昂贵,其中一家厂商(Nvidia)占据了相当大的市场份额。如果你要建立人工智能模型,你很可能会使用英伟达图形处理器。其次,您会希望让您计算机靠近您的数据。让昂贵的 GPU 空闲等待 I/O 请求是没有意义的。第三,以天或周计算训练时间并不罕见。训练模型所需的时间取决于模型的架构,模型越复杂,训练所需的时间就越长,可用于训练的数据量和计算能力也越大。
你可能还记得我在上一篇文章中提到,我们选择的存储介质是 S3 和红移,两者都托管在 AWS 上。因此,使用运行在 AWS 上的 GPU 实例是有意义的。问题是,这些可能很贵,尤其是如果你想全天候训练模特的话。美国东弗吉尼亚州/北弗吉尼亚州的 GPU 实例的 EC2 按需定价从约 0.50 美元/小时到约 32 美元/小时不等。
根据我的经验,完全依赖基于云的 GPU 实例可能会非常昂贵,你最好购买内部部署的 GPU,如英伟达 DGX ,如果你需要更多的计算能力,可以使用云。值得注意的是,主要的云提供商正在构建自己的 ASICs,并以比 Nvidia 驱动的 GPU 低得多的价格提供这些芯片。例如,AWS 提供了推理实例( Inf1 ),并计划用像 Trainium 这样的新模型来补充这些实例。
故事还有一个转折。您不会希望昂贵的 GPU 闲坐着,要么等待 I/O,要么利用不足。你也不希望你的 ML 团队试图找出如何调度 ML 作业在一个 GPU 集群上运行。这就是像 Polyaxon 这样的 MLOPs 引擎发挥作用的地方。Polyaxon 有许多替代品,有些是由 AWS Sagemaker 等云提供商提供的,有些是 Algorithmia 等云提供商提供的。在 Kheiron,我们选择了 Polyaxon,因为它具有丰富的功能集,并且能够在内部和云中运行。Polyaxon 还提供托管服务。
有了所有这些,我们的计算和存储架构如下图所示。在堆栈的顶部,您可以看到我们的存储层,它由临床数据和其他元数据的 S3(图像)和红移组成。我们还利用 AWS 上可用的 GPU 实例,如果我们需要超出可用的内部容量。

作者图片
我们的绝大多数计算集群都是内部部署的(英伟达 DGX)。为了减少我们的内部基础设施和 AWS 上托管的存储之间的 I/O 延迟,我们利用了像 Minio 这样的 S3 缓存。我们缓存的存储介质是快速 NVMe 固态硬盘。我们选择的 MLOPs 平台是 Polyaxon,我们广泛使用它来分配 ML 作业可以在其上运行的 GPU 资源。这些资源默认为我们的内部 GPU,但我们也能够通过在 AWS 上启动 GPU 实例来爆发。当我们这样做时,我们可以直接从 S3 读取,而无需通过缓存层。Polyaxon 提供的不仅仅是管理和调度大型计算集群的能力。它的众多好处之一是跟踪它在本地 Postgres 数据库上运行的模型的大量历史数据。
最后,我们利用 BI 工具来评估存储在 S3 的训练数据和红移与模型性能之间的关系。后者储存在 Polyaxon 中。通过这一分析,我们想回答许多问题。虽然我们显然关心模型的整体性能,但我们也对了解训练数据集的分布感兴趣。例如,如果我们的数据集偏向于某个年龄组,例如 45 岁以下的女性,那么如果我们的模型遇到训练分布之外的数据(在这种情况下是 45 岁以上的女性),则它们的性能可能不够好。我们关心训练数据的分布,以了解我们的模型的整体风险状况,以及这些数据在更广泛的分布中的推广情况。这种分析对于帮助我们找出训练数据中的差距也是非常宝贵的。我们的训练数据和总体数据(即人口)分布之间的重叠越大,模型的泛化能力就越好。模型泛化将是另一篇文章的主题。在一篇文章中简单地提到了这一点,但是我会在以后的文章中进一步充实它。
原载于https://karimfanous.substack.com。
存储数据:R 数据类型
教程| R |数据类型和结构
R 中数据科学的构建块

Iker Urteaga 在 Unsplash 上拍摄的照片
为了用 R 做数据科学,我们需要将数据存储在变量中。根据我们的数据是什么,我们需要给它正确的类型。通过选择不同的类型,我们能够在以后访问不同的工具来操作和使用该变量。有些更灵活,有些计算效率更高。让我们开始吧。
数据类型
让我们从不同类型的数据开始。虽然这不是一个全面的列表,但它将涵盖数据科学中使用的一些最重要的数据类型。
- 数字的
- 因素
- 性格;角色;字母
- 日期/时间
数字的
数字类型可以有多种形式,例如数字、整数和复数。数字可以是任何带或不带小数位的数字。在其他语言中,它们被称为浮点数。31、-4.2、0.00324都是数字。整数只包含整数。3和-24是整数的例子。复数有虚数部分,比如2 + 3i。
您可以像这样定义这些数值类型:
# Numeric
x <- 27.5# Integer
y <- 12L# Complex
z <- 2 + 3i
如果使用 RStudio,可以将鼠标悬停在环境窗格中的每个变量上,以查看其类型。

z 是“复杂”类型
typeof()函数也可以用来获取变量的类型。x 的double类型是处理数字数据时最常见的数字格式之一。
typeof(x)
# "double"typeof(y)
# "integer"typeof(z)
# "complex"
定义了一些数字数据后,就可以把 R 当作计算器使用了。所有的标准运算符都工作,如+、-、*和/。还有一些有用的函数,比如abs(),用于查找一个数字的绝对值,或者exp(),用于计算e到括号内的值。
x + y
# 39.5x * z
# 23+36iabs(x)
# 27.5exp(x)
# 877199251319
因素
因素是数据中的类别。在你的数据中,你通常会看到像雄性和雌性或者刚毛藻、杂色花、海滨草这样的东西(来自鸢尾数据集)。这些是分割你的数据的方法,它们会包含很多重复。因子将每个类别表示为一个数字,有助于更有效地存储这些信息。
gender <- factor("male", levels = c("male", "female"))flower_type <- factor("setosa", levels = c( "virginica", "setosa", "versicolor"))print(gender)
# male
# Levels: male femaleprint(flower_type)
# setosa
# Levels: virginica setosa versicolor
打印因子时,您会看到级别的名称。如果您在 RStudio 的环境窗格中查看,您会看到一个数字。通过将因子表示为变量的相应级别,节省了空间。

在幕后,flower_type 是 a 2,gender 是 a 1
当我们创建gender_2时,您可以开始看到这是如何节省空间的。我们将有 5 个因素,而不是 1 个因素。
gender_2 <- factor(c("male", "female", "female", "female", "male"), levels = c("male", "female"))print(gender_2)
# male female female female male
# Levels: male female
在我们的环境窗口中,我们现在看到在gender_2中有多个 1 和 2 来代表我们不同的性别。

在幕后,gender_2 只是一系列表示因子级别的数字
table()函数对于处理因子很有用。它给出了数据中每个级别的计数。
table(gender_2)
# gender_2
# male female
# 2 3
根据您需要了解的内容,nlevels()和levels()分别给出了因子中的级别数和级别名。
nlevels(gender_2)
# 2levels(gender_2)
# "male" "female"
特性
字符是 R 表示文本的方式。如果你来自另一种编程语言,这些通常被称为字符串。您可以将整本书存储在单个字符串变量中,或者从网站调查的文本条目中获得自由回答。
为了定义 R 中的字符,我们将文本放在引号中。可以用单引号('),也可以用双引号(")。r 也是这么解释的。我发现双引号在 R 中更常见,但是单引号在其他编程语言中也经常使用,所以你可能也会遇到。
character <- 'string'
print(character)
# "string"text <- "This is also a string"
print(text)
# "This is also a string"
r 包含一些内置函数,在处理字符文本时很有用。nchar()给出字符变量中的字符数,而paste()将字符变量组合在一起,用空格分隔。
nchar(text)
# 21paste(character, text)
# "string This is also a string"
还有一个包[stringr](https://stringr.tidyverse.org/),它允许文本操作技术,比如替换、获取字符可变长度,甚至用正则表达式(或 regex)查找文本。正则表达式非常灵活,对于掌握文本操作非常重要。
日期和时间
在商业世界和纵向研究中,相同的数据被定期收集,例如在月末、每天或每年。为了组织这些观察结果以便以后进行可视化和分析,我们需要日期和时间变量。
r 有一些内置的日期和时间变量,但是使用[lubridate](https://lubridate.tidyverse.org/)包使它们更容易解析和处理。您可以使用lubridate以多种不同的格式定义日期。它甚至可以自动处理日期中的分隔符,如斜线、破折号、冒号或空格!
library(lubridate)sample_date <- ymd("2020-01-21")
print(sample_date)
# "2020-01-21"sample_datetime <- mdy_hms("1/23/2021 2:53:22")
print(sample_datetime)
# "2021-01-23 02:53:22"
一旦你定义了日期和时间,你可以用它们做很多不同的事情。您可以提取日期时间的每个单独部分,添加一段时间,或者对它们进行舍入。
# Extract datetime components ----------------
month(sample_date)
# 1
second(sample_datetime)
# 21# Add a period of time -----------------------
sample_date + years(50)
# "2070-01-21"# Round a datetime down by month -------------
floor_date(sample_date, unit = "month")
# "2020-01-01"
你可以用日期和时间做很多其他的事情,如果你有兴趣了解更多,看看lubridate 备忘单。
结论
通过对 R 中一些最常见的数据类型的简要概述,您可以了解如何存储变量以及不同类型的一些优点。为了继续学习 R 中的数据类型知识,您需要了解一下数据结构。这些是 R 中的变量集合,它们用于存储数据和更高级的分析。
https://realdrewdata.medium.com/membership https://realdrewdata.medium.com/storing-data-r-data-structures-717245c6bab8
存储数据:R 数据结构
教程| R |数据结构
存放数据科学剩余产品的容器
数据类型的集合

照片由 Syed Hussaini 在 Unsplash 上拍摄
现在你知道了不同类型的变量,让我们看看 R 收集这些变量的一些选项。我们将了解以下类型:
- 向量
- 矩阵
- 数据帧
- 列表
向量
向量是 R 中最基本的类型之一,是许多运算的主干。虽然它们被限制为只包含一种类型的变量,但这允许它们比使用 for 循环更快地执行操作。它们也是我们将在本文后面讨论的数据帧的构建块。让我们做一些向量!
numbers <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)strings <- c("this", "is", "some", "text")bad_example <- c(1, "text", ymd_hms("2020-3-1 3:12:16"))
现在我知道你在想什么了。我刚才说一个向量里面只能放 1 种类型的变量,但是bad_example有 3 种!不完全是。如果你看一下环境面板,你会看到它是一个有 3 个元素的字符向量。将 vector 的所有元素强制或转换为同一类型。

日期由从 1970 年开始的秒数表示,所以我们得到 1583032336(作者)
一些函数在向量上使用时返回单个输出,比如mean()。其他的会对向量的每个元素执行一个动作,然后返回一个等长的向量,像paste()。
mean(numbers)
# 5.5paste("test", strings)
# "test this" "test is" "test some" "test text"
这些函数作用于向量的方式给了 R 很大的速度。它们比编写 for 循环要快得多。通过知道所有元素都是相同的类型,R 可以消除它在 for 循环中的大量内部开销。
矩阵
矩阵用于存储数字网格。某些统计函数将这些作为输入或在内部使用它们来加速计算。R 中的 K-Means 聚类将一个矩阵作为其输入,用于神经网络的nnet包在内部使用矩阵来进行计算。下面让我们看看创建一些矩阵的选项。
创建矩阵时,我们可以向matrix函数传递一个包含要放入矩阵中的数字的向量。
long_matrix <- matrix(c(1, 2, 3, 4, 5, 6, 7, 8, 9))print(long_matrix)
# [,1]
# [1,] 1
# [2,] 2
# [3,] 3
# [4,] 4
# [5,] 5
# [6,] 6
# [7,] 7
# [8,] 8
# [9,] 9
如果我们想要指定列或行的数量,我们可以使用ncol和nrow参数。
square_matrix <- matrix(c(1, 2, 3, 4, 5, 6, 7, 8, 9), nrow = 3, ncol = 3)print(square_matrix)
# [,1] [,2] [,3]
# [1,] 1 4 7
# [2,] 2 5 8
# [3,] 3 6 9
默认情况下,R 接受输入向量并填充每一列。要填充每一行,只需添加byrow参数并将其设置为TRUE。将它与之前square_matrix的输出进行比较。
square_matrix <- matrix(c(1, 2, 3, 4, 5, 6, 7, 8, 9), nrow = 3, ncol = 3, byrow = TRUE)# [,1] [,2] [,3]
# [1,] 1 2 3
# [2,] 4 5 6
# [3,] 7 8 9

RStudio 环境面板中的矩阵(鸣谢:作者)
如果你曾经上过线性代数的课程,你会知道矩阵可以相加、相乘,并且有一些特殊的性质,比如叉积和特征值。r 通过这些特殊的操作符和函数支持一系列这样的函数
# Add matrices
square_matrix + square_matrix
# [,1] [,2] [,3]
# [1,] 2 4 6
# [2,] 8 10 12
# [3,] 14 16 18# Multiply matrices
square_matrix * square_matrix
# [,1] [,2] [,3]
# [1,] 1 4 9
# [2,] 16 25 36
# [3,] 49 64 81# Find the transpose of a matrix
t(square_matrix)
# [,1] [,2] [,3]
# [1,] 1 4 7
# [2,] 2 5 8
# [3,] 3 6 9# Find crossproduct of 2 matrices
crossprod(square_matrix, square_matrix)
# [,1] [,2] [,3]
# [1,] 66 78 90
# [2,] 78 93 108
# [3,] 90 108 126# Find Eigenvalues of a matrix
eigen(square_matrix)
# eigen() decomposition
# $values
# [1] 1.611684e+01 -1.116844e+00 -1.303678e-15
#
# $vectors
# [,1] [,2] [,3]
# [1,] -0.2319707 -0.78583024 0.4082483
# [2,] -0.5253221 -0.08675134 -0.8164966
# [3,] -0.8186735 0.61232756 0.4082483
数据帧
数据框非常类似于大多数人习惯看到的由行和列组成的表格中的数据。R 中的数据帧由向量列组成。数据框非常适合绘制数据,并用作线性和逻辑回归(K-最近邻算法)的输入。
利用我们已经学过的关于向量的知识,它们非常容易制作。如果我们创建几个等长的向量,我们可以用data.frame函数将它们组合在一起。
例如,让我们创建一个数据框来保存班级中学生的姓名和分数。我们将创建 3 个向量:名、姓和分数。然后我们将向量传递给数据框函数,将数据框保存为student_scores。
first_name <- c("John", "Catherine", "Christopher")
last_name <- c("Cena", "Zeta-Jones", "Walken")
test_score <- c(84, 92, 100)student_scores <- data.frame(first_name = first_name, last_name = last_name, test_score = test_score)print(student_scores)
# first_name last_name test_score
# <fctr> <fctr> <dbl>
# John Cena 84
# Catherine Zeta-Jones 92
# Christopher Walken 100
如您所见,每个参数都是一个列名,该列中的数据是我们在data.frame函数中将列名设置为等于的向量。当我们打印student_scores时,我们得到列名、数据类型和数据。
您可能会注意到名和姓的类型是一个因素。默认情况下,R 将字符串作为因子,但是这种行为对我们的名字没有太大意义。创建数据框时添加stringsAsFactors = FALSE参数会将它们设置为字符。

我们的数据框和我们用来创建它的列(致谢:作者)
数据框具有一些功能,可帮助您获取有关其包含的数据的更多信息:
nrow()/ncol()-返回数据框中的行数或列数
ncol(student_scores)
# 3nrow(student_scores)
# 3
names()-返回数据框中的名称列名称
names(student_scores)
# "first_name" "last_name" "test_score"
head()/tail()—返回数据帧的前几行或后几行。默认显示的行数是 6。使用n参数改变显示的行数
# Display up to the first 6 rows of the data frame
head(student_scores)# Display up to the last 2 rows of the data frame
tail(student_scores, n = 2)
summary()—返回数据帧每一列的汇总信息。数值类型将显示该列的最小值、最大值、平均值和分位数指标
# Display summary information about the columns of the data frame
summary(student_scores)
# first_name last_name test_score
# Length:3 Length:3 Min. : 84
# Class :character Class :character 1st Qu.: 88
# Mode :character Mode :character Median : 92
# Mean : 92
# 3rd Qu.: 96
# Max. :100
同样值得注意的是,它能够提取单个列向量,并执行我们之前在向量上看到的所有相同操作。您可以使用美元符号符号(data.frame$column_name)或括号符号(data.frame[2]或data.frame[“column name”])
# Select the last_name column
# with dollar sign ($) notation
student_scores$last_name# Select the 2nd column of the data frame
# with bracket notation
student_scores[2]# Select the first_name column of the data frame
# with bracket notation
student_scores["first_name"]
列表
列表完善了我们的 r 的基本构建块。它们的用途非常广泛,因为它们可以包含几乎任何内容,甚至不同类型的变量,甚至可以相互嵌套来组织更多的信息。它们使用起来可能有些慢,但通常用于存储线性回归等模型的输出。
让我们创建一个包含不同数据类型的列表。我们将在示例列表中添加一个矢量、数据框和另一个列表。
example_list <- list(c(1, 2, 3, 4, 5), student_scores, list("first", "second", "third"))print(example_list)
#[[1]]
#[1] 1 2 3 4 5#[[2]]
# first_name last_name test_score
#1 John Cena 84
#2 Catherine Zeta-Jones 92
#3 Christopher Walken 100#[[3]]
#[[3]][[1]]
#[1] "first"#[[3]][[2]]
#[1] "second"#[[3]][[3]]
#[1] "third"
当打印列表时,您可以看到列表中的每个元素都由一个用双括号括起来的数字表示。因为 vector 是列表的第一个元素,所以它是[[1]]。我们的列表中的列表元素,或者嵌套列表,用双括号中的 2 个数字表示。可以通过首先指定与嵌套列表[[3]]相对应的元素,然后通过嵌套列表的元素来引用它们,例如第二个元素的[[2]]。
# 2nd element of the example list
example_list[[2]]# 2nd element of the nested list (the 3rd element of the example list)
example_list[[3]][[2]]

我们在 RStudio 环境窗格中的示例列表(鸣谢:作者)
列表元素也可以被命名。这可以通过在创建列表时为它们指定名称来实现,与创建数据框时指定名称的方式非常相似。
named_list <- list(vector = c(1, 2, 3, 4, 5), data_frame = student_scores, list = list("first", "second", "third"))
现在,我们可以通过使用美元符号和括号符号的组合来获得列表的不同元素。
# Access the named element of named_list
named_list$list# Access the 2nd element of the list element of named_list
named_list$list[[2]]

我们在 RStudio 环境窗格中的命名列表(鸣谢:作者)
结论
现在,您已经开始使用 R!进行数据科学、分析或数据可视化了!了解构件将有助于你组织你的变量。了解存储在列表中相对于存储在数据框中的优势将有助于保持代码的优化,即使数据开始增长。了解用于探索和处理数据的函数将有助于您了解这些函数中存储了什么。
https://realdrewdata.medium.com/membership
冲击 Kaggle 排行榜
使用 TensorFlow 的迁移学习来提高 Kaggle 狗与猫比赛模型的准确性

冲击 Kaggle 猫 vs 狗排行榜,图片作者
当我研究深度学习时,我喜欢通过参加 Kaggle 比赛来实践理论。之前,我用 TensorFlow 创建了一个卷积神经网络,以加入 Kaggle 上的猫与狗比赛。这个卷积神经网络让我在 Kaggle 公共排行榜上排名 833。
CNN 的准确率在 90%左右。我想努力提高我的模型的准确性,并在 Kaggle 排行榜上上升。我将使用迁移学习来增强我的模型。迁移学习是一种技术,在这种技术中,您可以重用其他人训练的模型作为您的模型的起点。TensorFlow Keras 包含几个内置的预训练模型,您可以使用。
源代码可以在 Github 上获得。我用的是 Python 3.8 和 Tensorflow 2.3。
迁移学习
迁移学习是一种提高模型准确性的流行技术。你拿一个别人为不同的任务训练过的模型,然后重用它。
第一次听说迁移学习,很迷茫。你如何重用一个别人为了不同的目的而训练的模型?我发现答案在于卷积网络的工作方式。
还记得我们在前一篇文章的张量流模型中有三个卷积层吗?训练过程中发生的是 TensorFlow 针对图像的特定特征训练每个卷积层。
2013 年,马特·泽勒和罗布·弗格斯发表了《可视化和理解卷积网络》本文展示了如何可视化在每个模型层中学习的神经网络的权重。请看下面 CNN 第一层和最后一层的可视化。左上角的块是权重的可视化,而其余部分显示了训练期间该层的激活。

CNN 第一层的激活(马特·泽勒等)
如果你看看 CNN 可视化的其他层,你会发现每一层都检测到图像的更具体的特征。通过我们的 CNN,最后一层的可视化包含了狗或猫的特定部分。请看下面马特·泽勒报告中第五层的可视化。

第五层的可视化重量和活化(马特·泽勒等人)
通过迁移学习,我们可以重用 CNN 模型中对所有图像通用的层。例如,这意味着我们保留现有模型的前 70 层,并添加和重新训练新的层。
现在你知道什么是迁移学习,我们将使用 TensorFlow Keras 来实现它。
TensorFlow Keras 迁移学习
TensorFlow Keras 包含几个内置模型,可用于迁移学习。我会用 Inception V3 深度学习模型来改进我的模型。
Inception V3 模型包含 159 层和 2300 万个参数。这比我们自己的 CNN 模型还要大,有九层,九百万个参数。希望这能提高我们模型的准确性。
我们将创建与以前不同的模型。我们没有使用Sequential类,但是我们创建并使用了InceptionV3模型的一个实例。
创建 InceptionV3 模型
input_shape还是老样子(150x150x3)。为了防止重新训练现有层,我们通过将每个层的trainable属性设置为False来锁定所有层。
最后一行打印模型的摘要。你可能需要滚动很多次,因为正如我们之前所说的,这个模型由 192 层组成。
添加可训练层
我们必须添加额外的层来训练使用我们的猫和狗图像的模型。我们在 InceptionV3 模型的第 130 层之后添加我们的可训练层。这一层被称为“mixed7”,见打印的模型摘要。
我们使用它的名字检索这个层,然后添加我们的可训练层。
向 InceptionV3 模型添加自己的层
我们添加一个展平,隐藏密集,和密集输出层。然后,我们使用现有的构建好的layers_out层来创建模型。从这一部分开始,编译和训练模型与以前一样。
编译和训练模型
如您所见,我仍然使用 ImageDataGenerators 和图像增强将训练图像输入管道。详见我的上一篇文章。
我们训练模型 100 个时期。
最终的训练准确度和验证准确度
我们看到,当我们将这些结果与我们的卷积网络结果进行比较时,我们看到训练和验证精度都从 0.9 比 0.6 开始变高。

训练结果,图片由作者提供
我们还看到,验证准确度开始时高于训练准确度,但是在 60 个时期之后,它开始降低。这种模式看起来又像过度拟合。


使用 InceptionV3 迁移学习时的训练和验证准确性以及训练和验证损失,图片由作者提供
创建和提交预测
我们看到,如果我们创建一个预测并提交给 Kaggle,我们的得分是 0.18834。使用我们的 CNN 图像增强的最佳得分是 0.26211。所以情况有所好转。这将使我们在公共排行榜上名列第 762 位。一跃 71 位!

我们使用迁移学习提交的结果,图片由作者提供
优化模型
我们能进一步优化模型吗?我们可以使用的一种方法是漏失层。基本思想是在训练时随机地将一些激活改变为零。这有助于防止过度配合。
该技术首先由 Nitish Srivastava 等人在“辍学:防止神经网络过度拟合的简单方法”中介绍。下图直观地显示了脱落层的情况。

将辍学应用于神经网络的示例(Nitish Srivastava 等人)
我们在致密层之前添加了下降层。我在第九行将退出量设置为 20% (0.2)。
向我们的模型添加一个 Dropout 层
其余的代码保持不变。我们再次训练 100 个时期的模型,并可视化精度和损失。


添加缺失图层时的训练和验证准确性以及训练和验证损失,图片由作者提供
训练和验证准确性都遵循相同的趋势,这表明我们正在创建一个好的模型,并且没有过度拟合。
创建并提交新的预测
我们看到,如果我们创建一个预测并提交给 Kaggle,我们的得分现在是 0.14293。这与没有脱落层的模型相比是相当大的改进。这使我们在 Kaggle 公共排行榜上又上升了 63 位,达到 699 位。

一个改进我们的分数使用辍学层,由作者的形象
在 Keras 框架中有更多的预训练模型可用。
进一步优化,选择不同的模型
到目前为止,我们使用 InceptionV3 作为我们的基础模型。正如您可能已经看到的,有许多 Keras 模型可以用于迁移学习。我想尝试提高我们精确度的另一个模型是DenseRes2.01。DenseRes2.01是预先训练好的密集连接卷积网络。
研究表明,如果卷积网络在靠近输入的层和靠近输出的层之间包含较短的连接,那么卷积网络会更加精确。DensRes2.01 就是这样一个密集连接的卷积网络。
随着这些预训练模型变得更大,训练一个纪元的时间也变得更长。我已经不断地训练模型大约 100 个时代了。Keras 包含回调函数,如果它检测到精度没有增加或减少,就会自动停止训练。我将使用的回调函数是EarlyStopping。
我们将EarlyStopping回调设置为model.fit方法的参数。创建EarlyStopping时,您给它一个名为patience的参数,该参数设置 TensorFlow 停止训练后没有改进的时期数。有时精度会轻微反弹,如果精度在一个时期后下降,耐心并不能帮助停止。见下面的例子。
将提前停止添加到培训流程中
我们增加的另一个优化技术是调整学习率。初始学习率作为参数提供给优化器使用compile方法。一旦精度不再提高,如果降低学习率,模型通常可以得到改善。
ReduceLROnPlateau可以自动降低学习率,一旦它看到几个时代没有改善。与EarlyStopping一样,ReduceLRonPlateau也是一个回调函数,作为参数提供给model.fit方法。
您可以在下面看到 DensRes2.01 模型的完整创建和初始化。
将提前停止和学习率调整添加到模型中
我们将DenseNet201模型直接添加到Sequential中,然后添加展平和一个隐藏的致密层。最后,我们为二进制输出添加一个带有单个单元的密集层。
我们在第 15 行和第 17 行创建了EarlyStopping和ReduceLROnPlateau实例,并将它们添加到第 26 行的回调参数中。
最终的训练准确度和验证准确度
在我的机器上,这个模型训练了十个纪元,直到因为提前停止而停止。
训练和验证准确率接近 0.99,而损失接近 0.025。


使用 DenseNet201 模型时的训练和验证精度以及训练和验证损失
创建并提交最后一个预测
剩下的工作就是创建预测并提交给 Kaggle。当我们提交预测时,我们看到我们的得分是 0.11625。针对 InceptionV3 模型的另一个改进,尽管这种比较并不完全真实,因为我们没有在初始阶段使用学习率调整。

作者使用 DenseNet201 图片改进了我们的分数
得分 0.11625 使我们在公共排行榜上排名第 624 位,又上升了 75 位。
结论
本文描述了如何在 TensorFlow 中使用迁移学习,并将其用于 Kaggle Dogs vs. Cats 比赛。通过迁移学习,您可以重用现有模型来提高模型的准确性。您锁定大多数层以防止重新训练,并在最后添加您的自定义层。
我们从卷积神经网络开始,然后我们添加了图像增强以增加训练图像的数量。接下来,我们对 InceptionV3 模型使用迁移学习。之后,我们在 InceptionV3 模型中使用了一个 Dropout 层。
最后,我们使用 DenseNet201 模型结合自动学习率调整达到了最高分。
猫对狗的尝试及其得分概述
也许如果我们在最后一次尝试中增加一个辍学层,我们可以增加一点分数。我将把这个练习留给你,我的笔记本电脑需要冷却。
您可以在 GitHub 上找到本文的源代码。存储库很大,因为它包括所有的训练和测试图像。
感谢您的阅读!
为人工智能讲故事:如何围绕你的技术建立一个故事
如果一个模型提供了很好的预测,它应该很容易找到它的观众。不幸的是,良好的表现往往不足以传播消息,即使是在内部。

由 Rain Bennett 在 Unsplash 拍摄的照片
虽然实验和实现在人工智能领域得到了很多关注,但技术项目或应用程序的故事讲述方面经常被忽略。许多伟大的项目仍然在抽屉里,沮丧的工程师和失望的项目经理。虽然工程师仍然是人工智能项目的中心部分,但他们(和他们的产品所有者)永远不应该忘记,一个成功的项目需要许多其他人的参与,这些人通常来自非常不同的专业背景。
我怎么知道什么是好故事?
多亏了我的多动症,我的注意力持续时间和勺子一样长。换句话说,任何太长、不够有趣的东西都不会吸引我超过 35 秒的注意力,即使在我最好的时候,即使我很努力。因此,我只听过(积极地听,而不是睡过头)非常好的故事。
为什么讲故事对任何技术项目都如此重要?
我参与了几个数据项目,有些成功了,有些失败了。失败从来都不是缺乏知识、技能或创新的结果。完整的项目或应用程序被简单地放在抽屉里,再也不会被看到。没有人需要它们,或者需要它们的人从未听说过。
当我回顾失败时,我清楚地看到缺乏讲故事的能力,无法为相关观众明确价值。那么作为一个项目经理、产品负责人或工程师,你需要什么来传播你的故事并展示你的 AI 项目?
从展示你的技术的价值开始。
如果他们甚至不知道你的解决方案是否首先与他们相关,没有人愿意坚持无聊的技术部分。这意味着你需要向你的观众证明为什么你的演讲值得关注。你在解决什么问题,他们可以联系起来?你的项目对他们有什么价值?
另一个让你的价值清晰的方法是在你和你的听众之间创造一个共同点。你听过你的客户抱怨 X 吗?你是否为你公司的交货延迟而苦恼?如果你能预测它们呢?
让你的技术易于理解和解释
找一个你的大部分观众都能理解的简单概念,并以此为基础来构建你的故事。当我向从未编码或使用过 Git 的人解释版本控制时,我理解了(几乎)从零开始建造一个小房子的比喻。每个人都知道房子是什么,厨房是什么,并且明白它需要 4 面墙。他们能够理解确保可能由几个人同时建造的相邻房间需要在设计、门等方面相互配合的挑战。
限制你使用花哨的技术词汇
如果有人很难记住聚会上其他参加者的名字(或者只有我?),不要指望他们记得什么是激活函数,或者机器学习模型的精度与其召回率相比是什么。选择最多两个与你的技术相关的技术词汇(如果你感觉很疯狂并且你真的有一个很棒的故事,选择三个)并坚持使用它们。
当您需要技术术语或评估指标来证明您的方法时,您需要定义它们,将它们可视化,或者/和将它们与更简单的概念进行比较。不要依赖你的观众记住你所有的评估指标和你的人工智能术语。很长一段时间以来,你每天都在与它打交道,而你的观众却没有。记住这一点。
小心无意义的流行语
我之前提到过,我对流行语有一点问题,(我过去可能对大数据有一点抨击)。在企业界使用流行语让我感到沮丧。每个人都想要创新、大数据、实时、智能系统,而这些词的含义在无尽的 PowerPoint 路线图中迷失了。
如果你的故事中包含流行语,它们的使用必须是合理的。当它们真正为您的解决方案增加价值时,请将它们放在适当的位置。甚至非技术人员也学会了看过去的流行语,现在想知道人工智能在实践中能提供什么,以及你的解决方案有多可行。
太简单总比太复杂好
如果你的受众混杂,或者你不确定他们的技术水平,那么最好低估他们的技术知识,让你的故事过于“简单”而不是高估他们的技术知识。如果有人感兴趣,你可以补充说你愿意更深入地了解。这就像说“如果你们中的一些人感兴趣,我可以在以后告诉你们更多关于这一部分的内容。”
这就是低估更安全的原因。如果你的观众想要更多的细节,他们会问的。如果你的观众对你的故事一无所知(或知之甚少),他们就不太可能这么说。大多数人都有一定程度的自豪感,他们可能不愿意在 20 分钟后提到他们对你的最后 15 张幻灯片一无所知。在这种情况下,我会在 2 张幻灯片后放弃,并可能在接下来的 13 张幻灯片中滚动 Instagram。
另一方面,如果你的听众要求更多的技术细节,准备好提供给他们。通过问一些关于你的技术的问题,他们会表现出他们对技术的理解,并且很乐意这样做,这就开始了你与听众的互动。同样的,你会发现你的故事引起了足够多的人的兴趣,他们会问你更多的细节。这是双赢的局面。
有许多成功的方法来展示和推广一个技术项目。如果你的故事成功了,它会在你的观众中传播,这意味着他们会开始为你传播这个词,包括销售、管理、营销(以及任何有预算的人!)在你的公司或者外部。你只需要记住,一旦你的 AI 原型被开发出来,讲故事就是进一步开发你的项目的关键。
生物医学科学家机器学习导论
机器学习和免疫系统有一些共同点:它们都从经验中学习来预测未来的 T2。

图片来自 Unsplash
机器学习无处不在,生物医学也不例外。从推进个性化医疗的大规模基因组数据分析到通过从氨基酸序列预测蛋白质折叠来解决生物学中 50 年的挑战,毫无疑问,机器学习正在实现突破,塑造生物医学研究的未来。但是,机器学习到底是什么?
根据亚瑟·塞缪尔的定义,机器学习(ML)是一个“在没有明确编程的情况下赋予计算机学习能力的研究领域”。ML 算法能够从数据中学习。但是,我们如何定义机器的学习?从形式上来说,“一个计算机程序被称为从经验 E 中学习关于某类任务 T 和性能测量 P ,如果它在 T 任务中的性能,如 P 所测量的,随着经验EE】而提高。**

作者图片
为了让生物医学科学家熟悉这一点,让我们想想免疫系统。我们的免疫系统保护我们的身体免受外界入侵,如细菌、病毒、真菌和毒素。当先天免疫反应不足以控制感染时,由 T 细胞和 B 细胞介导的适应性或获得性免疫反应被激活。让我们来看一个例子。
当抗原进入我们的身体时,抗原呈递细胞(APCs)会检测并吞噬它们。它们形成抗原的许多不同片段,并将它们运输到膜表面。一些 T 细胞通过刺激 B 细胞准备应答来应答 APCs。b 细胞然后产生称为抗体的蛋白质,这种蛋白质对入侵的病原体具有特异性,并结合到入侵病原体的表面,以标记其被破坏。感染后,病原体特异性 B 细胞群得以维持,因此在再次感染的情况下,免疫反应更快、更有效。
这意味着我们的免疫系统已经学会区分抗原的致病性,并在再次感染的情况下利用该信息。或者,换句话说,我们的免疫系统从经验中学习,以更好地完成的任务——在这种情况下是,杀死病原体。这差不多就是机器学习所做的。
给定 ML 的正式定义,我们可以定义任何机器学习算法的的 4 个主要组成部分:一个数据集(经验)、一个模型(解决任务的算法)、一个成本或损失函数(衡量模型解决任务的好坏),以及一个优化算法(校准模型以提高其性能)。
让我们更详细地了解一下每一项:
数据集(经验)
ML 算法可以理解为被允许体验一个数据集,这个数据集是很多例子的集合。根据它们在学习过程中被允许拥有的经验,ML 算法可以大致分为无监督或有监督 。
无监督算法体验包含许多特征的数据集,然后学习该数据集结构的有用属性。通常,它们旨在学习生成数据集的整个概率分布,或者将数据集划分或聚类成同类组(即样本相似的组)。
监督算法体验包含特征的数据集,以及与每个示例相关联的标签或目标。监督算法旨在为输入示例输出正确的标签。

作者图片
任务(T)
ML 使我们能够处理那些用人类编写和设计的固定程序难以解决的任务[1]。学习的过程本身不是任务。学习是我们获得完成任务能力的手段。任务和数据集高度关联。有人可能会说,我们可以设置的任务类型将取决于数据集,或者相反,我们想要处理的任务类型将决定我们需要使用或收集的数据集。
例如,如果我们的起点是基于 BRCA 基因的突变状态预测乳腺癌患者是否会对化疗治疗产生良好反应的任务,我们需要一个具有野生型和突变型 BRCA 基因(称为特征)的患者数据集,以及他们对化疗治疗的结果(称为目标/标签)。但是,如果我们的起点是乳腺癌患者的数据集,具有特征(如 BRCA 突变状态、年龄、种族、阶段等)和化疗反应标签,我们可以使用监督方法(如我们之前介绍的),或者我们可以使用非监督方法,仅分析患者特征,以找到特征驱动的聚类(组)。例如,通过后一种方法,机器可以发现所有 BRCA 野生型患者都小于 50 岁(假设)。
第一个任务对应于分类(识别输入属于哪个类 k ),第二个任务是聚类(识别数据集中相似示例的组)。其他任务的例子包括:
- 回归。要求算法从输入中预测一个数值。例如,预测细胞系对药物治疗的反应。
- 机器翻译,将一种语言的符号序列转换成另一种语言的符号序列。例如,从英语翻译成西班牙语。
- 异常检测。检测给定数据集中的异常实例。例如,检测欺诈性银行交易。

作者图片
业绩计量(P)
为了评估我们的模型,我们必须设计一个对其性能的定量测量(我们称之为成本或损失函数)。性能通常使用准确性(正确输出值的比例)或其等价的错误率(模型产生错误输出的示例的比例)来衡量。损失函数有时称为 0–1 损失(在分类模型中,如果答案正确,损失函数的值为 0 ,如果答案不正确,损失函数的值为 1 )。
优化算法
机器学习算法通常使用数值方法进行优化,以使损失函数最小化,假设损失函数的最小值意味着最大性能。
我们如何确保 ML 算法在未来会有良好的表现?
当我们构建一个 ML 算法时,我们的目标是让它能够预测新的数据或未知的数据。回到免疫系统的例子,我们感兴趣的是免疫系统来区分给定的抗原是否是以前见过的病原体,以便 B 细胞准备好产生针对它们的抗体。或者例如,如果我们建立一个算法来预测乳腺癌患者对化疗治疗的反应,我们希望它能够对新患者做出正确的预测,而不仅仅是我们用于模型训练的那些患者。这种属性被称为泛化,即在之前未观察到的数据上表现良好的能力。
让我们思考一下这个问题。当我们用一个数据集训练一个模型的时候,我们怎么知道它是在用良好的泛化能力学习,还是仅仅记忆数据集?要回答这个问题,我们或许应该先回答另一个问题:ML 算法是否有能力记忆一个数据集?答案是在大多数情况下,是的,它们是:任何具有足够容量的 ML 算法,都能够记忆或学习一个数据集。让我们看看下图中的例子。当试图通过颜色来分离 2 组示例时,对于低容量的模型,该任务是不可能的。然而,具有高容量的模型可以容易地创建任意边界来分隔点集。

作者图片
现在我们已经回答了这个问题,我们可以回到:我们如何知道一个 ML 模型是具有良好泛化能力的学习还是仅仅记忆数据集?我们已经知道,任何足够强大的模型都能够记住数据集,那么,我们如何确保模型在处理看不见的数据时表现良好?有两种主要做法可以解决这个问题。
第一个包括设计模型训练程序,以便模拟真实生活场景。这意味着将(即分割/分离)数据集分割成训练、验证和测试集合。该算法在训练期间可以访问训练和验证集合,并且在测试集合中的最终模型性能被用作未知新数据中的性能的代理。

作者图片
在实践中,该模型通过更新其内部参数值来学习,以便在每次训练迭代后其预测变得更好。在每次迭代中,模型从训练集合中学习,其性能在验证集合中测试。最佳模型状态将是在验证集中性能最大的模型状态。训练后,在测试集中的最终模型性能给出了它在新数据中表现如何的指示。
通常,我们会多次重复这个过程,将样本分配到不同的集合,这样我们就可以获得对模型性能的更可靠的评估。最典型的方法叫做 k 倍交叉验证,将数据集分成 k 组(叫做倍,过程重复 k 次,每次保留一倍作为测试集,其余数据作为训练+验证集(见下图)。此设置有许多变体,例如,可以在交叉验证文件夹中对每个 train+val 集合执行交叉验证,从而产生一个嵌套交叉验证。也可以省去一个唯一的测试集,对 train+val 集进行 k 重交叉验证。

作者图片
第二种做法是选择一款容量正好的型号。回到分离示例集的任务,我们将选择一个模型,该模型具有足够的能力从数据集学习并具有良好的泛化能力,但没有太大的能力来记忆训练集并在测试集中表现不佳(过拟合),或者太小的能力以至于根本无法学习(欠拟合)。这在实践中并不那么直截了当,有时是由试错法决定的。
TLDR
机器学习和免疫系统有一些共同点:它们都从经验中学习来预测未来。任何机器学习算法都有 4 个基本要素:一个数据集,一个模型,,,一个性能,度量,以及一个优化算法。数据集是模型用来学习的经验。模型 的选择将取决于要解决的任务(回归、分类等)。在训练期间,一个性能测量指示模型解决任务的情况。对于要学习的 ML 算法来说,它需要有恰到好处的容量,并且实验设计应该是这样的,即它的最终性能是在 看不见的 数据(训练期间看不见的数据)上测试的。
欢迎在评论区提出任何问题
感谢 Andrea Gonzalez Pigorini 博士对本文医学部分的贡献,感谢 Matias Mikkola 校对本文并提供建议。
参考
伊恩·古德菲勒、约舒阿·本吉奥和亚伦·库维尔。2016.深度学习。麻省理工学院出版社。
[2]免疫系统笔记来自:OpenStax,生物学。OpenStax CNX。2021 年 9 月 18 日https://opentextbc . ca/biology/chapter/23-2-adaptive-immune-response/
[3]机器学习和深度学习笔记来自:【1】
生物医学科学家机器学习导论——第二部分:实践教程
生物医学科学家的 ML 介绍:动手编码教程

图片来自 Unsplash
这是帖子的第二部分,介绍生物医学科学家的机器学习,它为生物医学科学家介绍了机器学习(ML)的概念。在本帖中,我们将获得使用生物医学数据的实践编码教程。
你可以在谷歌实验室这里轻松运行这个教程
我们将建立一个模型来预测乳腺肿块患者的诊断。我们将使用的数据集是由美国威斯康辛州麦迪逊市威斯康辛大学医院的医生威廉·h·沃尔伯格博士创建的。为了创建数据集,沃尔伯格博士使用了取自乳腺实性肿块患者的液体样本,并使用图形计算机软件来计算样本中细胞的特征。数据集包含 30 个像元特征,其中包括:
- 半径(从中心到周边各点的平均距离)
- 纹理(灰度值的标准偏差)
- 周长
- 区域
- 平滑度(半径长度的局部变化)
- 紧凑性(周长/面积— 1.0)
- 凹度(轮廓凹陷部分的严重程度)
- 凹点(轮廓凹陷部分的数量)
- 对称
- 分形维数(“海岸线近似值”-1)
首先,让我们加载所有的包和数据集:
输出:

[图片由作者提供]
如我们所见,该数据集包含 569 名患者,其中 212 名为恶性乳腺肿块,357 名为良性乳腺肿块。诊断(恶性/良性)编码在目标中,作为二进制指示器(即 0 代表恶性诊断,1 代表良性诊断)。
现在让我们想象几个特征:
输出:

[图片由作者提供]
我们在这里可以看到的一件事是,具有恶性诊断的患者倾向于具有较高凹度值(y 轴)的乳房肿块,而具有较低平滑度值(例如,低于 0.08;x 轴)倾向于良性诊断。看到这种情况,我们可能会想,‘嗯,我们可以探索所有的特征,并创建边界来对患者进行分类,对吗?".当然,我们有可能遍历所有的特征,并找到边界来分离这些组。但是如果我们有 100 个或者 1000 个特征呢?即使只有 30 个特征,这个过程也是棘手和耗时的。相反,让我们转向 ML 来为我们做这件事。我们将使用一个决策树分类器,它会自动执行这个过程。
首先,我们将数据分成训练集和测试集:
输出:

我们在训练集中有 426 名患者,在测试集中有 143 名患者。
现在,我们实例化该模型,并使用三重交叉验证对其进行训练:
和我们在测试集中的表现:
输出:

我们可以看到,测试集中 93.57%的患者被正确诊断
我们可以看到为诊断患者而创建的树:
输出:

[图片由作者提供]
在每一层上,该算法找到将患者分类成不同组的界限。在这种情况下,它从特征开始表示凹点。如果患者的平均凹点 ≤ 0.049,则沿左侧路径,进入良性组,而平均凹点> 0.049 的患者则相反。在下一个级别,对每个组重复这个过程。这一过程被优化以最小化基尼系数。基尼系数是一个量化群体纯度的指标。大于零的基尼系数意味着包含在该节点内的样本属于不同的类别。基尼系数为零意味着该组是纯的,在该组中只存在一类样本。通过探索生成的决策树,我们能够解释算法做出的决策。
有了这个,我们就完事了!
欢迎在评论区提出任何问题
用地图讲故事:圣达菲省的选举
地理空间数据开源免费软件

图片来自 Unsplash 的 Arnaud Jaegers
地图
地图被定义为在二维表面上对一个区域的图形表示。它们还被定义为代表某一地区特征的示意图或布局,如其尺寸、坐标、地理特征或其他相关方面。我们通常称之为地方地图、地区地图、大陆地图或世界地图,这取决于所代表的地域范围。
根据其目的或功能,有不同类型的地图。我们对三种地图特别感兴趣:政治地图;人口地图;和专题地图。
政治地图:在这种类型的地图中,没有物理元素被表现出来,而是仅仅通过将地区划分为国家、自治区、省或者部门来表示领土的行政划分。
人口地图:这种类型的地图通过百分比、居民数量、统计或人口普查来研究人口。这些地图的研究对象可以是生育率、死亡率、迁徙运动或人口密度。
专题地图:它们是为了反映所定义的地理区域的特定方面而制作的。他们可以关注物理、社会、、政治、文化、经济、社会学以及任何其他与特定领域相关的变量。顾名思义,它们是为特定的目的而设计的,或者是为了说明一个特定的主题。在专题地图的制作中,一般地图用于为前者所描述的现象发生的位置提供准确的基线参考。
用 Python 绘制地图
能够以简单的方式在地图上显示地理信息和专题信息,并且速度比桌面 GIS 更快,这使得许多数据科学家的工作变得更加容易。这可以通过几行 Python 代码并使用两个著名的库来实现,比如 matplotlib 和 geopandas。
Matplotlib 是一个面向数据可视化的扩展函数库。这个图书馆是由美国神经生物学家约翰·d·亨特在其神经生物学博士后研究期间创建的,用于可视化癫痫患者的皮层脑电图数据。遗憾的是,亨特于 2012 年 8 月 28 日因结肠癌去世。
Matplotlib 是一个基于 Python 的开源库,已经成为数据科学 Python 用户使用最广泛的绘图库。受 MATLAB 的启发,Matplotlib 使用三个主要元素工作:轴、图形、和图。Matplotlib 有几个专门用于不同技术领域的模块,这推动了它越来越多的使用。
Geopandas 是另一个基于 Python 的开源库,其目标是方便使用地理空间数据。 Geopandas 由凯尔西·乔达尔于 2013 年创立;他在 2014 年 7 月的一次 Scipy 会议上发布了 0.1.0 版本。该项目目前由乔里斯·范登博舍领导。
GeoPandas 以熊猫为原型。它扩展了 Pandas 数据类型,以包含几何列并执行空间操作。GeoPandas 依赖于几个库,如 Shapely ,它允许对与数据相关的几何图形执行地理空间操作, Fiona 用于文件访问,以及 Matplotlib 用于绘图。地理数据框架是主要的数据结构,扩展了著名的熊猫数据框架。
圣达菲省选举
圣达菲是位于阿根廷东北部的一个农业-工业省份。面积 133,007 平方公里,人口(2010 年人口普查)33.69 亿。该省在政治上分为 19 个省。人口最多的两个城市是罗萨里奥省的罗萨里奥市(948,312 人,2010 年人口普查)和首都省的圣达菲市(484,000 人,2010 年人口普查)。
圣达菲是该国选民人数第三多的地区,有 2768525 人有资格投票,占总数的 8.06%,仅次于布宜诺斯艾利斯省(37%)和科尔多瓦省(8.69%)。
2021 年 11 月 14 日,举行大选,重新选举 9 名全国众议员和 3 名全国参议员。这是一次至关重要的选举,因为这可能意味着执政党在参议院中失去法定人数。
从历史上看,在最近的选举中,三个选举联盟在不同的省份占主导地位:1。- 进步阵线 (FAP),社会民主取向;2.- Cambiemos (JxC),自由民主取向;3.——Frente de Todos(FdeT,Juntos),民族主义民粹主义取向。
让我们看看如何使用专题地图来说明最近的选举结果,并在 2019 年与之前的选举进行比较。
用地图讲故事
我们使用了三个数据集:1 . - arg_adm2.shp ,阿根廷共和国各省地图(1);2.-pobl _ deptos _ SF . CSV,圣达菲省各省居民(2);3.-generales 2019 CSV . CSV,圣达菲省大选最终选举结果日期 06/16/2019 (3)。
首先,我们导入 pandas 库作为 pd, numpy 作为 np ,geopandas 作为 gpd ,matplotlib 作为 plt:
import pandas as pd
import numpy as np
import geopandas as gpd
import matplotlib.pyplot as plt
我们使用命令 geopandas.read_file() 读取 ESRI 形状文件并返回一个地理数据框架对象。我们过滤了圣达菲省对应的数据,去掉了无用的列。然后我们做了一些编码、解码和替换的工作,因为西班牙语的口音有一些常见的问题:
## shapefile from Argentina
arg_sheip = gpd.read_file(path + 'ARG_adm2.shp')santa_fe = arg_sheip[arg_sheip.NAME_1 == 'Santa Fe']col_to_drop = ['ID_0', 'ISO', 'NAME_0',
'TYPE_2', 'ENGTYPE_2', 'NL_NAME_2', 'VARNAME_2']santa_fe = santa_fe.drop(col_to_drop, axis = 1)santa_fe['NAME_2'].str.normalize('NFKD').str.encode('ascii', errors='ignore').str.decode('utf-8')santa_fe['NAME_2'] = santa_fe['NAME_2'].str.replace(u"San MartÃn", "San Martin")santa_fe['NAME_2'] = santa_fe['NAME_2'].str.replace('Nueve', '9')santa_fe['NAME_2'] = santa_fe['NAME_2'].str.replace(u"ó", "ó")
然后,我们在第二个数据集上使用 Pandas 进行 ETL(提取、转换、加载),并合并两个数据帧:
# file with province population
df_pop= pd.read_csv(path + 'pobl_deptos_SF.csv',
index_col = False, header = None, sep = ';',
skiprows = 8, skipfooter = 4, engine = 'python')df_pop.columns = ['NAME_2','Pop1', 'Pop2',
'Perc1', 'Perc2', 'Perc_Hab']df_pop['NAME_2'] = df_pop['NAME_2'].str.replace(u"San Martín", "San Martin")## merging dataframes
santa_fe2 = santa_fe.merge(df_pop, on = 'NAME_2')
因为我们想要指出每个部门的名称,所以我们使用 Shapely 的方法[representative_point()](http://toblerity.org/shapely/manual.html#general-attributes-and-methods)返回一个计算成本很低的点,该点保证在几何对象内。然后,我们向 geodataframe ( ['coords'] )添加一个新列,以便拥有一组与每个面对象(每个部门)相关的坐标。
由于 geopandas 提供了 matplotlib 库的接口,我们简单地使用了方法 plot ()来绘制地图。 alpha 参数控制不透明度,而 edgecolor = 'r' 用红色高亮显示部门的轮廓。
我们通过在新列(‘coords’)中遍历地理数据框架来包含相应的注释。
santa_fe2['coords'] = santa_fe2['geometry'].apply(lambda x: x.representative_point().coords[:])santa_fe2['coords'] = [coords[0] for coords in santa_fe2['coords']]## First Map: Santa Fe, political subdivision of the province
f1, ax = plt.subplots(figsize =(18,18))santa_fe.plot(ax = ax, alpha = 0.3, edgecolor = 'r')ax.set_title("Departaments Santa Fe Province", fontsize = 18)## Add annotations
for idx, row in santa_fe2.iterrows():
plt.annotate(s=row['NAME_2'],xy=row['coords'],
horizontalalignment='center')
plt.show()
我们的第一张地图显示了该省的政治分区及其 19 个省,并带有相应的纬度和经度值。

图 1:作者用 Matplotlib 做的政治地图。
我们的第二张地图是人口分布图。Geopandas 通过使用 plot() 命令创建一个 choropleth,其中 column 参数设置为我们决定为其分配颜色值的列(下图中的 column = 'Perc_Hab' )。
我们设置 legend = False ,因为我们使用了注释来包含百分比的数值。cmap = 'coolwarm' 是一张发散的彩色地图以清楚地突出罗萨里奥和洛杉矶首都省组成了该省一半以上的选民。因为我们在第一张地图中显示了纬度和经度值,所以我们不需要再次显示它们,所以我们使用 ax.set_axis_off() 关闭它们。最后,我们决定使用深色背景以获得更好的视觉对比度。
## Second Map, Percentage of population in the departments
plt.style.use('dark_background')f2, ax = plt.subplots(figsize =(18,18))santa_fe2.plot(ax = ax, column = 'Perc_Hab',
legend = False, cmap = 'coolwarm')ax.set_title("Population per Departament [%]", fontsize = 22)
ax.set_axis_off()for idx, row in santa_fe2.iterrows():
plt.annotate(s=row['Perc_Hab'], xy=row['coords'],
color = 'orange', size = 22,
horizontalalignment='center')plt.show()
我们的第二张地图显示了该省 19 个部门的人口密度:

图 2:作者用 Matplotlib 制作的人口分布图。
以下代码显示了 2019 年大选中三个主要联盟在该省不同部门的省长和参议员职位上获得的结果。
在读取了 csv 文件后,我们使用了 groupby() 方法和。agg('sum') 将各党在各部门获得的两类选票相加。我们再次执行了与包含地理空间数据的数据帧的合并。
## 2019 election results
df = pd.read_csv(path + 'generales2019CSV.csv',
index_col = False, header = 0,
sep = ';', engine='python')#...... Governor and Senator per Department & Political Partysum_df = df.groupby(['departamento', 'partido', 'nombrePartido'])
['gobernador', 'senador'].agg('sum')df_sum = sum_df.reset_index()## Spanish stuff
df_sum['departamento'] = df_sum['departamento'].str.title()df_sum['departamento'] = df_sum['departamento'].str.replace(u"Constitucion", "Constitución")df_sum['departamento'] = df_sum['departamento'].str.replace(u"San Cristobal", "San Cristóbal")df_sum['departamento'] = df_sum['departamento'].str.replace(u"San Jeronimo", "San Jerónimo")df_sum['departamento'] = df_sum['departamento'].str.replace(u"Gral Lopez", "General López")df_sum['departamento'] = df_sum['departamento'].str.replace(u"Gral Obligado", "General Obligado")df_sum['departamento'] = df_sum['departamento'].str.replace(u" De ", " de ")santa_fe3 = pd.merge(santa_fe2, df_sum,
left_on = 'NAME_2', right_on = ['departamento'],
how = 'inner')
for 循环允许我们过滤三个投票最多的联盟,以便进一步绘制:
##.........votes per party per departament
year = ' 2019'
list_part = [16,40,44]for partido in list_part:
if partido == 16:
santa_fe4 = santa_fe3[santa_fe3.partido.isin([16])]
sigla = ' FAP'
elif partido == 40:
santa_fe4 = santa_fe3[santa_fe3.partido.isin([40])]
sigla = ' FdeT'
elif partido == 44:
santa_fe4 = santa_fe3[santa_fe3.partido.isin([44])]
sigla = ' JxC'
santa_fe4['coords'] = santa_fe4['geometry'].apply(lambda x: x.representative_point().coords[:])
santa_fe4['coords'] = [coords[0] for coords in santa_fe4['coords']]## Third Map, governor: votes per party per departament
f34, (ax1,ax2) = plt.subplots(1,2, figsize =(14,14))
col = 'gobernador'
santa_fe4.plot(ax = ax1, column = col , legend = False,
cmap = 'YlGnBu')
ax1.set_title( "Votes for Governor" + sigla + year,
fontsize = 18)
ax1.set_axis_off()
for idx, row in santa_fe4.iterrows():
s = f'{row[col]:.0f}'
ax1.annotate(s, y=row['coords'],
horizontalalignment='center',
size = 15, color = 'red')## Fourth Map, senator: votes per party per departament
col = 'senador'
santa_fe4.plot(ax = ax2, column = col ,
legend = False, cmap = 'YlGnBu')
ax2.set_title( 'Votes for Senator' + sigla + year,
fontsize = 18)
ax2.set_axis_off()
for idx, row in santa_fe4.iterrows():
s = f'{row[col]:.0f}'
ax2.annotate(s,xy=row['coords'],
horizontalalignment='center',
size = 15, color = 'crimson')
plt.show()
我们的第三和第四张地图显示了 2019 年 Cambiemos 联盟的票数。在第三张图中,我们使用州长的投票数来指定颜色强度。出于同样的目的,在第四个地图中,我们使用参议员的投票数列。

图 3:作者用 matplotlib 制作的 choropleth 图。
以下 choropleths 表示在 2019 年大选中哪个政党在每个省赢得了州长和参议员的职位。获得的票数包括在内。
现在我们使用了带有聚合函数的 groupby() 方法。agg('max') 用于列 ['gobernador'] 和 ['senador'] 。我们创建了两个新的数据框架,按部门列出了获胜者,并添加了一列获胜联盟的名称。该新列将用于颜色分配。
## Political party that won in each departmentdf_maxg = df_sum.groupby('departamento', as_index = False)
['gobernador'].agg('max')df_maxs = df_sum.groupby('departamento', as_index = False)
['senador'].agg('max') list_partg = []
for depart2 in df_maxg.index:
for depart in df_sum.index: if (df_maxg['departamento'] [depart2] ==
df_sum['departamento'] [depart]): if (df_maxg['gobernador'] [depart2] ==
df_sum['gobernador'] [depart]): part = df_sum['nombrePartido'][depart]
list_partg.append(part)df_maxg['partido'] = list_partglist_parts = []
for depart3 in df_maxs.index:
for depart in df_sum.index: if (df_maxs['departamento'] [depart3] ==
df_sum['departamento'] [depart]): if (df_maxs['senador'] [depart3] ==
df_sum['senador'] [depart]): part = df_sum['nombrePartido'][depart]
list_parts.append(part)df_maxs['partido'] = list_partssanta_fe5 = pd.merge(santa_fe2, df_maxg,
left_on = 'NAME_2', right_on = 'departamento'],
how = 'inner')
santa_fe5['coords']=santa_fe5['geometry'].apply(lambda x: x.representative_point().coords[:])santa_fe5['coords'] = [coords[0] for coords in santa_fe5['coords']]santa_fe6 = pd.merge(santa_fe2, df_maxs,
left_on = 'NAME_2',right_on= ['departamento'],
how = 'inner')santa_fe6['coords']=santa_fe6['geometry'].apply(lambda x: x.representative_point().coords[:])santa_fe6['coords'] = [coords[0] for coords in santa_fe6['coords']]
这些 chloropleths 包括图例标识每个部门的获胜联盟: legend = True,marker = 'o ',legend_kwds={'loc ':'右下角' }。
## Fifth and Sixth Maps, Political party that won in the departmentf56, (ax1,ax2) = plt.subplots(1,2, figsize =(14,14))santa_fe5.plot(ax = ax1, column = 'partido', marker = 'o',
legend = True, categorical = True, cmap = 'Accent',
legend_kwds={'loc': 'lower right'})ax1.set_title("Governor Winner " + year, fontsize = 18)
ax1.set_axis_off()col = 'gobernador'
for idx, row in santa_fe5.iterrows():
s = f'{row[col]:.0f}'
ax1.annotate(s, xy=row['coords'],horizontalalignment='center',
size = 12, color = 'yellow')santa_fe6.plot(ax = ax2, column = 'partido', marker = 'o',
legend = True, categorical = True, cmap = 'Dark2',
legend_kwds={'loc': 'lower right'})ax2.set_title("Senator Winner " + year, fontsize = 18)
ax2.set_axis_off()col = 'senador'
for idx, row in santa_fe6.iterrows():
s = f'{row[col]:.0f}'
ax2.annotate(s, xy=row['coords'],horizontalalignment='center',
size = 12, color = 'yellow')plt.show()

图 4:作者用 Matplotlib 制作的 choropleths 图。
圣达菲省政府尚未建立最近参议院选举正式结果的档案。因此,我们决定将 2021 年 11 月 14 日举行的选举结果手动纳入一个特定文件,以便与 2019 年的结果进行比较。
使用与前面描述的代码非常相似的代码,我们生成了两个 choropleths,一个显示每个部门的获胜者以及获得的选票,另一个显示每个部门获胜者联盟的百分比。

图 5:作者用 Matplotlib 制作的 choropleths 图。
可以清楚地观察到选民决定的深刻变化:Cambiemos 从 2019 年只赢得一个省,到最近的选举中赢得 16 个省。此外,执政联盟在其余三个省获胜,其中一个省是人口密度最高的省(罗萨里奥),但他们的胜利是百分比值最低的省。最终结果显示,坎比埃莫斯获得 40.32%的选票,紧随其后的是 Juntos(东帝汶国防军)获得 32.20%的选票,FAP 获得 12.49%的选票。执政党失去了在参议院的法定人数。
总结:地图是最古老的呈现数据或讲述故事的手段之一。特别是根据观众容易理解的逻辑,用相同颜色或不同颜色的不同强度表示数据的色标。在本文中,我们打算讲述圣达菲省选民对阿根廷共和国参议院中代表他们的参议员的偏好发生深刻变化的故事。我们认为,一幅政治地图、一幅人口统计地图和几幅专题地图的结合,非常精确地传达了这个故事。
参考文献
- IGN,Instituto geográFico Nacional(T4,阿根廷共和国国家地理研究所),公共领域数据。
- 圣菲省,2010 年全国人口普查:公共领域数据。
- 圣达菲省选举法庭,result ados Escrutinio definitio Elecciones general 2019 年 6 月 16 日,公共领域数据。
如果你发现了这篇感兴趣的文章,请阅读我之前的(https://medium.com/@dar.wtz):
选举调查是用柱形图、堆积柱形图还是发散柱形图?
平行坐标图与 Plotly,规范化或标准化?
奇怪的事情:添加…猫?你在 R 上的阴谋
如何借助带有“ggcats”包的滑稽猫来分析和可视化数据

奇怪的事情:添加…猫?到您在 R-R studio 预览上的绘图(图片由作者提供)
许多人会认为统计分析不可能有趣。如你所知,如果你一直关注我发表的文章,你会注意到我试图分享实际的例子,但也很有趣。我开展的一项活动是教学,尤其是在今天,当我们大部分时间都在跳跃,突然一切都变得遥远时,要进行面对面的互动,需要付出额外的努力来保持学生的注意力。
你喜欢猫吗?你有一只作为宠物吗?如果我告诉你,在这些毛茸茸的朋友的帮助下,你可以让你的情节有所触动,你会对我说什么?
我想向您介绍 "ggcats" ,这是一个基于 "geom_image" 的有趣软件包,它可以帮助您在您的报告、演示和报告中保持观众的注意力,当然是非正式的。
软件包安装
在一个新的 R 脚本中使用它是非常容易的,首先,你必须从它的存储库中安装这个包。
# INSTALL GGCATS PACKAGE install.packages("remotes")
remotes::install_github("R-CoderDotCom/ggcats@main")
测试 ggcats
有十五种不同的小猫可以使用:“shironeko”、“venus”、“hipster”、“lil_bub”、“maru”、“colonel”、“grumpy”、“mouth”、“pop”、“pop_close”、“pusheen”、“pusheen_pc”、“toast”、“bongo”和“nyancat”(默认)。
您可以在一个网格中查看它们,首先查看每一个:
# LOAD LIBRARIESlibrary(ggplot2)
library(ggcats)
library(Ecdat)
library(tidyverse)
library(gganimate)# VISUALIZE AVAILABLE CATStheGrid <- expand.grid(1:5, 3:1)
dataFrame <- data.frame(x = theGrid[, 1],
y = theGrid[, 2],
image = c("shironeko", "venus", "hipster", "lil_bub", "maru", "colonel", "grumpy", "mouth", "pop", "pop_close", "pusheen", "pusheen_pc", "toast", "bongo", "nyancat"))ggplot(dataFrame) +
geom_cat(aes(x, y, cat = image), size = 4) +
xlim(c(0.25, 5.5)) +
ylim(c(0.25, 3.5))
你会发现这些都是世界各地流行的迷因中的著名小猫。

奇怪的事情:添加…猫?到 R 上的 plots 在 RStudio: grid 中生成的 Plot,用于可视化可用的猫(图片由作者提供)
如何将猫添加到你的情节中
你可以看到使用“geom _ cat”函数将它们添加到你的图中是非常容易的,例如,取 R 默认提供的数据,假设你想添加“Pusheen Cats”。
# TESTING PUSHEEN CAT AND MARU CATggplot(mtcars) +
geom_cat(aes(mpg, wt), cat = "pusheen", size = 4)
你会得到如下图。

奇怪的事情:添加…猫?到 R 上的您的 plots 在 RStudio: pusheen cats 中生成的 plots(图片由作者提供)
或者说,你要加“丸猫”。
ggplot(mtcars) +
geom_cat(aes(mpg, wt, size = cyl), cat = "maru")
现在你会得到一个如下图所示的图。

奇怪的事情:添加…猫?到 R 上的你的情节——在 RStudio: maru cats 中生成的情节(图片由作者提供)
更进一步…动画情节
你可以利用你有两只小猫的事实,一只张开嘴,另一只同样闭着嘴,玩 gganimate 包。例如:
# EXAMPLE GGCAT WITH GGANIMATElibrary(Ecdat)data(incomeInequality)newData <-
incomeInequality %>%
select(Year, P99, median) %>%
rename(income_median = median,
income_99percent = P99) %>%
pivot_longer(cols = starts_with("income"),
names_to = "income",
names_prefix = "income_")newData$cat <- rep(NA, 132)newData$cat[which(newData$income == "median")] <- rep(c("pop_close", "pop"), 33)
newData$cat[which(newData$income == "99percent")] <- "bongo"ggplot(newData, aes(x = Year, y = value, group = income, color = income)) +
geom_line(size = 2.5) +
labs(title="Testing ggcats with gganimate",
subtitle = "Pop cat and Bongo cat") +
geom_cat(aes(cat = cat), size = 6) +
transition_reveal(Year)
你会得到如下的动画情节。

奇怪的事情:添加…猫?到 R 上的您的 plots 在 RStudio 中生成的 Plot:gg cats 和 gganimate(图片由作者提供)
剩下的就看你的创造力了,如你所见。我想继续写一些不寻常的事情,你仍然不知道你可以用 R 做什么,并继续写一系列这样的短文,让我知道你是否认为这是一个好主意。
非常感谢您的善意阅读。和我的大多数文章一样,在这里你可以找到完整的代码:https://github.com/cosmoduende/r-ggcats
感谢你坚持到最后,祝你分析非常愉快,可以把一切都付诸实践,对结果感到惊讶,和我一样开心!
不平衡数据回归的策略和方法

(图片由作者提供)
思想和理论
深入探究深度不平衡回归(ICML 2021,龙口述)
给大家介绍一下我们的最新作品,已经被 ICML 2021 接受为 长篇口述 展示: 深度挖掘不平衡回归 。在经典的数据不平衡问题下,本工作探索了一个非常实用但很少研究的问题:不平衡回归。现有的处理不平衡数据的方法大多只针对分类问题——即目标值是不同类别的离散指标;然而,许多实际任务涉及连续,有时甚至无限目标值。这项工作提升了传统不平衡分类问题的范式,将数据不平衡问题从离散目标扩展到连续目标。
我们不仅提出了两种简单而有效的方法来提高不平衡回归问题的模型性能,而且还为计算机视觉、自然语言处理和医疗保健领域的常见现实任务建立了五个新的基准不平衡回归数据集。代码、数据和模型已经在 GitHub 上开源:https://github.com/YyzHarry/imbalanced-regression。
首先,我想先总结一下本文的主要贡献:
- 新任务:我们正式定义了现实环境中出现的深度不平衡回归(DIR) 任务。DIR 旨在从具有连续目标的不平衡数据中学习,处理某些区域的潜在缺失数据,并推广到整个目标范围。
- 新技术:我们开发了两种简单、有效、可解释的 DIR 寻址算法:标签分布平滑(LDS) 和特征分布平滑(FDS) ,它们利用了标签和特征空间中邻近目标之间的相似性。
- 新基准:我们为计算机视觉、自然语言处理和医疗保健中常见的现实世界任务策划和基准测试大规模 DIR 数据集。它们的范围从单值预测,如年龄、文本相似性得分、健康状况得分,到密集值预测,如深度。新的数据集可以支持实际评估,并有助于不平衡回归的未来研究。
接下来,我们将进入正文。我将首先介绍非平衡回归问题的背景(与非平衡分类相比),以及目前的一些研究现状。那我就介绍一下我们的思路和方法,省略不必要的细节。
背景和动机
数据不平衡在现实世界中是普遍存在和固有的。数据不是在每个类别上保持理想的均匀分布,而是经常呈现带有长尾的偏斜分布,其中某些目标值的观察值明显较少。这种现象给深度识别模型带来了巨大的挑战,并激发了许多解决数据不平衡的现有技术。
特别是过去的解决方案大致可以分为基于数据的和基于模型的解决方案。基于数据的解决方案要么对少数类进行过采样,要么对多数类进行欠采样,例如 SMOTE 算法,该算法通过对同一类中的样本进行线性插值来为少数类生成合成样本。基于模型的解决方案包括重新加权、调整损失函数和利用相关的学习范式,如迁移学习、元学习和两阶段训练。更详细的回顾可以在我的上一篇文章中找到。
然而,现有的从不平衡数据中学习的解决方案集中在具有分类指数的目标上,基本上目标是不同的类别。例如,下图显示了一个用于地点分类的典型真实数据集,该数据集是不平衡的,具有长尾标注分布,并且标注是不同的类,例如住宅、森林和博物馆。类似地,一个用于物种分类的现实世界不平衡数据集,称为 iNaturalist,目标也是分类的,并且具有硬边界,不同类别之间没有重叠。

现有的不平衡学习方案主要针对具有类别索引的目标。目标具有硬边界,不同类别之间没有重叠。(图片由作者提供)
然而,许多现实世界的任务可能涉及连续,有时甚至无限的目标值。例如,在视觉应用中,人们经常需要根据不同人的视觉外观来推断他们的年龄。在这里,年龄是一个连续的目标,在整个目标范围内可能是高度不平衡的。例如,这里有一个真实世界的年龄估计数据集,它在不同年龄之间有一个倾斜的标签分布。在这种情况下,将不同的年龄视为不同的阶层不太可能产生最好的结果,因为它没有利用年龄相近的人之间的相似性。

左图:一个计算机视觉应用程序,我们根据不同人的视觉外观来推断他们的年龄。右图:大规模年龄估计数据集 IMDB-WIKI 在不同年龄之间存在偏态标签分布。(图片由作者提供)
类似的问题也发生在医疗应用中,我们希望推断患者人群中不同的健康指标,如心率、血压和氧饱和度。这些指标也是连续的,并且通常在患者人群中有偏斜的分布。

不平衡回归问题在医疗保健领域也很常见。左图:大多数健康指标,如心率、血压和氧饱和度,都是连续的目标,通常在患者群体中高度不平衡。右图:真实世界数据集 SHHS 的健康状况评分的偏态标签分布。(图片由作者提供)
此外,许多重要的现实世界应用(如经济学、危机管理、故障诊断或气象学等。)也有类似的要求。在这些应用中需要预测的连续目标变量往往有许多罕见的极值。这种连续域中的不平衡问题在线性和深度模型中都存在。在深度模式下更为严重。这是为什么呢?因为神经网络预测往往过于自信,而这种数据失衡问题被严重放大。
因此,在这项工作中,我们定义并研究了深度不平衡回归(DIR) ,从这种具有连续目标的不平衡数据中学习。具体而言,给定具有连续目标值的数据集,DIR 旨在从这种具有不平衡和偏斜分布的数据中学习,处理某些目标区域的潜在缺失数据,并推广到整个支持的目标范围。特别是,我们感兴趣的是推广到在连续目标值的整个范围内平衡的测试集,这为 DIR 提供了全面和无偏见的评估。这也符合不平衡分类的设置。

深度不平衡回归(DIR)旨在从具有连续目标的不平衡数据中学习,处理某些区域的潜在缺失数据,并推广到整个目标范围。(图片由作者提供)
不平衡回归的挑战
然而,我们注意到,DIR 带来了不同于其分类对应物的新挑战。
(I) 首先,给定连续且潜在无限的目标值,类之间的硬边界不再存在。当直接应用诸如重新采样和重新加权的传统不平衡分类方法时,这可能导致模糊。
(II) 此外,连续标签固有地拥有目标之间有意义的距离,这暗示了我们应该如何解释连续设置中的数据不平衡。例如,假设有两个目标标签 t1 和 t2 ,它们在训练数据中都有同样少量的观察值。然而, t1 处于高代表邻域,如图所示其邻域范围内样本较多,而 t2 处于弱代表邻域。在这种情况下, t1 不会遭受与 t2 相同水平的不平衡。

连续标记固有地拥有目标之间有意义的距离,这暗示了我们应该如何解释连续设置中的数据不平衡。例如,即使 t1 和 t2 具有相同数量的观察值,t1 也不会遭受与 t2 相同程度的不平衡。(图片由作者提供)
(III) 最后,与分类问题不同,在 DIR 中,某些目标值可能根本没有数据,这也激发了对目标外推和插值的需求。

在不平衡回归中,某些目标值可能根本没有数据,这也激发了对目标外推和内插的需求。(图片由作者提供)
综上所述,与传统的不平衡分类相比,DIR 有着新的困难和挑战。那么,应该如何进行深度不平衡回归呢?在接下来的两个部分中,我们提出了两种简单有效的方法,分别是标签分布平滑(LDS)和特征分布平滑(FDS ),通过利用在标签空间和特征空间中附近目标之间的相似性。
标签分布平滑(LDS)
我们首先展示一个例子来说明当不平衡出现时分类和回归之间的区别。
激励示例:我们采用了两个不同的数据集,(1) CIFAR100,这是一个 100 类分类数据集,以及(2)IMD b-WIKI 数据集,这是一个大规模图像数据集,用于根据视觉外观进行年龄估计。这两个数据集具有本质上不同的标签空间:CIFAR-100 展示了分类标签空间,其中目标是类别索引,而 IMDB-WIKI 具有连续标签空间,其中目标是年龄。我们将年龄范围限制为 0~99,以便两个数据集具有相同的标签范围。此外,我们对两个数据集进行二次抽样,以模拟数据不平衡,同时确保它们具有完全相同的标签密度分布。我们使两个测试集平衡。

(图片由作者提供)
然后,我们在两个数据集上训练一个简单的 ResNet-50 模型,并绘制它们的测试误差分布。如第一幅图所示,在 CIFAR-100 上,我们观察到误差分布实际上与标签密度分布相关。具体而言,作为类别索引函数的测试误差与分类标签空间中的标签密度分布(即 0.76)具有高度负皮尔逊相关性。这种现象是意料之中的,因为拥有更多样本的多数班比少数班学得更好。

(图片由作者提供)
然而有趣的是,IMDB-WIKI 的误差分布非常不同,它具有连续的标签空间,即使标签密度分布与 CIFAR-100 相同。特别是,误差分布更加平滑,并且不再与和标签密度分布(0.47)相关。
这种现象表明,对于连续标签,经验标签密度不能准确反映模型或神经网络所看到的不平衡。因此,在连续的情况下,经验标签分布不能反映真实的标签密度分布。这是因为邻近标签处的数据样本之间的相关性(例如,接近年龄的图像)。
标签分布平滑(LDS) :事实上,在统计学中有一个重要的文献是关于在这种情况下如何估计期望密度的。因此,标签分布平滑(LDS)提倡使用核密度估计来学习对应于连续目标的数据集中的有效不平衡。给定连续的经验标签密度分布,LDS 将对称核 k 与经验密度分布卷积,以提取核平滑版本,该版本考虑了附近标签的数据样本的信息重叠。由 LDS 计算得到的有效标签密度分布与现在的误差分布有很好的相关性,Pearson 相关系数为 0.83。这表明 LDS 抓住了影响回归问题的真正不平衡。

(图片由作者提供)
既然有效标签密度是可用的,用于解决类不平衡问题的技术可以直接适用于 DIR 上下文。例如,一种直接的适应可以是成本敏感的重新加权方法,其中我们通过将损失函数乘以每个目标的 LDS 估计标签密度的倒数来重新加权损失函数。
特征分布平滑(FDS)
我们已经证明了可以有效地利用标签空间中的连续性来寻址 DIR。我们进一步受到直觉的激励,即目标空间中的连续性应该在特征空间中产生相应的连续性。也就是说,如果模型工作正常并且数据平衡,人们期望对应于附近目标的特征统计彼此接近。同样,我们用一个说明性的例子来强调数据不平衡对 DIR 中特征统计的影响。
激励示例:同样,我们使用在 IMDB-WIKI 数据集中的图像上训练的简单模型来从视觉外观推断一个人的年龄。我们关注已学习的特征空间,即图中的 z 。我们为标签空间引入了一个额外的结构来进行分析,其中我们将它分成具有相等间隔的仓。我们使用 b 来表示目标值的组索引。这里,在年龄估计中,箱的长度被定义为 1,表示感兴趣的最小年龄差为 1。现在,使用这种结构,我们将具有相同目标值的要素分组到相同的条柱中。然后,我们针对每个箱中的数据计算特征统计量(即,均值和方差)。

(图片由作者提供)
现在我们准备好可视化特征统计之间的相似性。首先,我们选择一个锚仓,表示为 b0 ,并且我们有这个锚仓的特征统计。此外,我们还计算了其他面元的统计量,最后,我们计算了 b0 和所有其他面元之间的特征统计量的余弦相似度,并汇总了锚年龄的结果,如下图所示为 30。该图还使用紫色、黄色和粉红色显示了具有不同数据密度的区域。

(图片由作者提供)
有趣的是,我们发现锚仓周围的特征统计与它们在锚仓的值高度相似。具体来说,25 至 35 岁之间的所有箱的特征均值和特征方差的余弦相似性与它们在 30 岁(锚年龄)时的值相差几个百分点。我们注意到仓 30 处的锚年龄落在多次拍摄区域中。因此,该图证实了当有足够数据时的直觉,并且对于连续目标,特征统计类似于附近的面元。
然而,有趣的是,该图还显示了数据样本非常少的地区的问题,如年龄范围为 0 至 6 岁的地区。请注意,这个范围内的平均值和方差显示出与 30 岁时出乎意料的高相似性。这种不合理的相似性是由于数据不平衡。具体来说,由于没有足够的 0 到 6 岁的图像,因此该范围从具有最大数据量的范围(大约 30 岁的范围)继承其先验。
特征分布平滑(FDS) :受这些观察的启发,我们提出了特征分布平滑(FDS),它在特征空间上执行分布平滑,基本上在附近的目标箱之间转移特征统计。该程序旨在校准特征分布的潜在有偏估计,尤其是针对代表性不足的目标。因此,我们有了一个将输入数据映射到连续预测的模型。现在,通过首先估计每个箱的统计来执行 FDS。不失一般性,我们用协方差代替方差来反映 z 内特征元素之间的关系。给定特征统计,我们再次采用对称核 k 来平滑特征均值和协方差在目标仓上的分布。这导致统计数据的平滑版本。现在,有了估计和平滑的统计数据,我们就可以按照标准的白化和重着色过程来校准每个输入样本的特征表示。通过在最终特征地图后插入一个特征校准层,FDS 的整个管道被集成到深度网络中。最后,为了在训练期间获得特征统计的更稳定和准确的估计,我们采用跨每个时期的运行统计的动量更新。

(图片由作者提供)
我们注意到,FDS 可以与任何神经网络模型相结合,以及任何过去在改善标签不平衡方面的工作。
基准 DIR 数据集和实验
为了支持不平衡回归方法的实际评估,并促进未来的研究,我们策划了五个 DIR 基准,涵盖计算机视觉、自然语言处理和医疗保健。它们从单值预测(如年龄、文本相似性得分、健康状况得分)到密集值预测(如深度)不等。

策划了五个基准 DIR 数据集。(图片由作者提供)
- IMDB-WIKI-DIR (vision,age) :第一个叫做 IMDB-WIKI-DIR,包含人脸图像和对应的年龄,用于年龄估计。我们使验证和测试集达到平衡。
- AgeDB-DIR(视力,年龄):类似地,第二个数据集,称为 AgeDB-DIR,也是根据单个输入图像的年龄估计。请注意,AgeDB-DIR 的标签分布不同于 IMDB-WIKI-DIR,即使它们展示了相同的任务。
- NYUD2-DIR (vision,depth) :此外,尽管是单个目标值预测,我们也采用 NYU2 数据集进行深度估计,这是一个密集值预测任务,并为不平衡回归评估建立 NYUD2-DIR 数据集。
- STS-B-DIR (NLP,文本相似度得分):我们还构造了一个 NLP 领域的 DIR 基准,叫做 STS-B-DIR。任务是推断两个输入句子之间的语义文本相似度得分。分数在 0 到 5 的范围内是连续的,并且具有不平衡的分布。
- SHHS-DIR(医疗保健,健康状况评分):最后,我们在医疗保健领域创建了一个 DIR 基准,称为 SHHS-DIR。任务是推断一个总的健康分数,该分数在 0 到 100 之间连续分布,分数越高,健康状况越好。输入是每个患者在整个晚上睡眠期间的高维多导睡眠图信号,包括 ECG 信号、EEG 信号和呼吸信号。如图所示,分数分布也不均衡。
所有的数据和模型都在 我们的 GitHub repo 开源。在评估过程中,我们在平衡的测试集上评估每种方法的性能。我们进一步将目标空间分成几个不相交的子集:多镜头、中镜头和少镜头区域,反映训练数据中不同数量的样本。对于基线,由于文献中只有一些关于 DIR 的建议,除了过去使用合成样本的非平衡回归工作,我们采用了一些非平衡回归分类方法,并提出了一组强基线(有关更多详细信息,请参考我们的论文)。
实验:由于实验较多,这里只展示 IMDB-WIKI-DIR 上有代表性的结果(所有结果请参考论文)。我们首先根据使用的基本策略将不同的方法分为 4 个部分。在每个组中,我们进一步将 LDS、FDS 以及 LDS 和 FDS 的组合应用于基线方法。最后,我们报告了 LDS+FDS 相对于普通模型的绝对改进。如表所示,无论使用哪种基础训练技术,LDS 和 FDS 都取得了显著的成绩。特别是,对于少炮区域,我们可以通过获得比基线模型多 40%的相对改进。

(图片由作者提供)
理解 FDS 的分析:我们仔细看看 FDS,分析它是如何影响网络训练的。类似于之前的设置,我们绘制锚年龄为 0 的特征统计相似性。如图所示,由于目标箱 0 中的样本非常少,特征统计可能具有较大的偏差,即年龄 0 与区域 40≁80 具有较大的相似性。相比之下,当添加 FDS 时,统计数据得到更好的校准,导致仅在其邻域中具有高相似性,并且随着目标值变大,相似性得分逐渐降低。我们在训练期间进一步可视化运行统计和平滑统计之间的 L1 距离。有趣的是,随着训练的进行,平均 L1 距离变得越来越小,并逐渐减少。这表明模型学习生成即使没有平滑也更准确的特征,并且最终可以在推断期间移除平滑模块。

(图片由作者提供)
外推分析&插值:最后,在现实世界的 DIR 任务中,某些目标值可以根本没有数据。例如,回想一下 SHHS-DIR 和 STS-B-DIR 基准测试中的标签分布。这激发了对目标外推和内插的需求。我们从 IMDB-WIKI-DIR 的训练集中筛选出几个子集,这些子集在某个区域没有训练数据,但是在原始测试集上进行评估,用于零镜头泛化分析。这里,我们将我们的方法相对于普通模型的绝对 MAE 增益可视化。如上所述,我们的方法提供了对多、中、少以及零触发区域的综合处理,在整个频谱上实现了显著的性能增益,并且对不同的不平衡标签分布是鲁棒的。

(图片由作者提供)
结束语
在这篇文章的结尾,我们提出了(1)一个称为深度不平衡回归的新任务,和(2) 新技术,标签分布平滑和特征分布平滑,以解决具有连续目标的学习不平衡数据,和(3)五个新基准以促进未来的研究。也请查看我们的代码、数据和论文。感谢您的聆听。我们的工作填补了实际不平衡回归问题的基准和技术的空白。这些结果可能会引起更广泛的不同应用领域的兴趣。最后,我附上了几个与本文相关的链接;感谢阅读!
代码:https://github.com/YyzHarry/imbalanced-regression
项目页面:http://dir.csail.mit.edu/
如何使用 Scikit-learn 训练内存不足的数据
使用 partial_fit API 进行增量学习的基本指南

图片来自 PublicDomainPictures 来自 Pixabay
Scikit-learn 是数据科学社区中流行的 Python 包,因为它提供了各种分类、回归和聚类算法的实现。人们可以使用 scikit-learn 包用几行 Python 代码训练一个分类或回归机器学习模型。
Pandas 是另一个流行的 Python 库,它在将数据提供给 scikit-learn 模型之前提供对数据的处理和预处理。人们可以使用 Pandas 和 Scikit-learn 包轻松处理和训练内存中的数据集(可以放入 RAM 内存的数据),但当处理大型数据集或内存外的数据集(无法放入 RAM 内存的数据)时,它会失败,并导致内存问题。
Scikit-learn 提供了 API 选项,如**warm_start**和**partial_fit** 来扩展计算。在本文中,我们将讨论如何读取、处理和训练内存不足的数据集。
什么时候需要增量学习?
对于机器学习任务来说,新的一批数据随着时间的推移而出现,用以前和新的一批数据重新训练模型是一个计算量很大的过程。因此,人们可以采用增量学习方法,而不是用整个数据集来重新训练模型,其中将恢复模型的过去学习,并用新的一批数据来训练相同的模型。
此外,对于内存不足(大型)的数据集,不可能一次将整个数据加载到 RAM 中,可以分块加载数据,并为每个数据块拟合训练模型。
部分拟合:
为了执行增量学习,Scikit-learn 附带了 partial_fit API 选项,它能够从一批实例中进行增量学习。当整个数据集太大而无法一次放入内存时,partial_fit非常有用。该方法预计将在数据集的不同块上连续调用几次,以便实现核外学习。
partial_fit具有一定的数值稳定性和性能开销,因此建议对相当大批量的数据(适合内存)调用 partial_fit 函数,以克服开销的限制。
实施:
其思想是以块的形式读取大型数据集(第 8 行),并对每个数据块执行特征工程。此外,使用具有逻辑回归模型的增量学习(第 11 行)来训练每个单独的数据块。
(作者代码)
对于分类任务,必须将可能的目标类标签列表传递给**classes**参数,以处理第一批数据中未出现的目标类。
实施partial_fit所带来的估算器列表如下:
- 分类:多项式分类器,伯努利分类器,SGD 分类器,被动积极分类器,感知器
- 回归: SGDRegressor ,passiveaggressiverregressor
- 聚类:迷你批处理方式
- 分解:minibatchdictionaryllearning,minibatchkman
“温暖状态”和“部分适合”有什么不同?
如前所述,**partial_fit** API 用于对小批量数据进行增量学习,保持估计器常数的超参数。另一方面,**warm_state** 不用于增量学习,因为它是在估计器的超参数变化的相同数据集上训练的。
根据Scikit-学会文档 :
[**partial_fit**](https://scikit-learn.org/stable/glossary.html#term-partial_fit)也保留了调用之间的模型,但是有所不同:在[**warm_start**](https://scikit-learn.org/stable/glossary.html#term-warm_start)中,参数会改变,而对**fit**的调用之间的数据(或多或少)是恒定的;使用**partial_fit**,小批量的数据变化和模型参数保持不变。
结论:
**partial_fit**是一个方便的 API,可用于在小批量的内存不足数据集上执行增量学习。使用**warm_state**的主要目的是在用不同组的超参数值拟合同一数据集时减少训练时间。通过重用从先前的参数值学习的模型的方面,它可以用于优化网格搜索实现。
参考资料:
[1.] Scikit-Learn 文档:https://sci kit-Learn . org/0.15/modules/scaling _ strategies . html
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一小部分会员费,不需要你额外付费。
https://satyam-kumar.medium.com/membership
感谢您的阅读
分层归一化:使用附加信息提高神经网络的性能
思想和理论
一种新的基于脑电的情感分类数据集的深度神经网络归一化训练方法。

几个月前,当我迅速面临开发脑成像方法时最具挑战性的问题:参与者之间 EEG 活动的不均匀性时,我开始研究如何使用 EEG 记录对诱发的情绪进行分类。使用图 1 可以很容易地解释这个问题,在图 1 中,首先提取每个视频和参与者的每个 EEG 记录的特征,然后实施降维工具 UMAP 来嵌入数据。在左边,颜色代表情感;在右边,颜色表示数据对应于谁。因此,如果我们直接尝试使用一个经过训练的模型来对四个参与者的情绪进行分类,并尝试推断第五个参与者的情绪,结果将非常糟糕,因为没有说零。
为了解决这个问题,最常用的解决方案是使用校准阶段来预训练 AI 模型。然而,这非常耗时,因此许多研究人员一直在试图寻找替代解决方案。
虽然本文主要关注脑成像方法,但同样的问题也发生在许多其他领域,如图像识别、语音识别或面部表情。

图一。嵌入脑电图记录。在左边,颜色表示情绪,而在右边,颜色表示参与者。
在过去的几年中,已经有多个研究人员提出了解决这个问题的方案。对于脑成像,大多数提出的解决方案是基于选择鲁棒的特征。然而,(1)用这些特征训练的与参与者无关的模型的分类器性能仍然低于与参与者相关的模型,以及(2)这些特征是与任务相关的,这不仅阻碍了跨任务的健壮解决方案,也阻碍了跨领域的健壮解决方案。
因此,我们研究了一种新的基于参与者的归一化方法,称为分层归一化,用于训练深度神经网络。
该方法的主要思想是将附加信息按比例分配给神经网络以改善其性能。需要提醒的是,这些额外的信息需要与我们试图推断的信息不同。例如,在我们的数据集中,我们试图对情绪进行分类,因此使用的附加信息是每个数据的会话和参与者。
我们的分析是关于从 EEG 信号进行跨主题情绪分类的任务,结果表明,用分层归一化训练的网络明显优于用批量归一化训练的标准网络。
为了简要介绍我们的论文,我将重点介绍所提出的方法,并将它与众所周知的批处理规范化方法进行对比。此外,为了澄清解释,我将首先简要说明我用于分析的数据集。最后,我将展示我们的结果,并以一个小结论结束。
资料组
使用的数据集是种子数据集,是由鲁保良教授领导的 BCMI 实验室提供的脑电数据集集合。这个数据集和 DEAP 数据集是基于脑电图的影响计算中最重要的两个数据集。
它包含从 15 名参与者那里收集的 62 通道脑电图数据,这些参与者对相同的 15 个电影剪辑进行了三次训练。对于每个电影剪辑,通过平均 20 名参与者的评级来分配情感评级(积极、中立或消极),这些参与者被要求在观看后指出三个关键词中的一个。
总而言之:
- 参加者:15 名参加者
- 会议 x 视频:3 x 15(每个参与者 45 个视频)
批量标准化
Ioffe 和 Szegedy (2015) 首次引入批量标准化,以解决内部协变量移位的问题,这是学习过程导致的神经元激活分布的不必要漂移。它是神经网络中实现最多的归一化,并且在多个应用中表现出很好的性能。
正如下面进一步解释的,我们为了我们的目的稍微修改了这个方法。图 2 说明了我们的批处理规范化方法的实现。对于这种方法,对每批的每个特征进行归一化。

图二。批量标准化方法。数据按特征标准化,独立于参与者和会话。
分层标准化
分层归一化是所提出的方法,并且由每个参与者和会话的特征归一化组成。图 3 详细描述了分层归一化方法的实现。
与先前的归一化方法相比,该方法归一化每个特征、参与者和会话的数据。与批处理规范化相比,主要缺点是要么需要包含一个大的批处理集,要么需要研究每个批处理中每个类有多少数据。

图 3。分层归一化法。数据按特征、参与者和会话进行标准化。
结果和结论
使用图 4 所示的架构,结果如图 5 和图 6 所示。

图 4。分类器的体系结构。
图 5 和图 6 显示了预测值在神经网络输出层的嵌入。结果表明,在输出层,采用分层归一化训练的模型情感识别准确率较高,参与者识别准确率较低。事实上,这一次,分层归一化比批量归一化在 UMAP 上的嵌入更紧凑,情绪评级比参与者数量更容易识别——对于参与者数量,颜色的分布似乎表明大部分大脑签名确实消失了。
这些结果表明分层归一化对于跨主体情绪识别任务的高度适用性,表明该方法不仅可以应用于其他 EEG 分类数据集,还可以应用于需要域自适应算法的其他应用。

图 5。用批量标准化和三种情感类别嵌入神经网络的输出。

图 6。用分层归一化和三种情感类别嵌入神经网络的输出。
作为参考,这里我留下了包含本文中解释的所有内容以及许多其他细节的论文。如果你有任何问题或想法,请不要犹豫写评论或联系我。
https://www.frontiersin.org/articles/10.3389/fnins.2021.626277/full
如果你喜欢这个帖子,请考虑 订阅 。你将获得我所有的内容+所有其他来自牛逼创作者的文章!
使用 Python 和 Pandas 进行分层随机抽样
如何对样本数据进行分层以匹配总体数据,从而提高机器学习算法的性能

查尔斯·德鲁维奥在 Unsplash 上拍摄的照片
介绍
有时,数据科学家得到的样本数据并不符合我们对更广泛的人口数据的了解。例如,假设数据科学团队收到了调查数据,我们注意到调查受访者中 60%是男性,40%是女性。
在现实世界中,英国总人口中男性比例接近 49.4%,女性比例接近 50.6%(资料来源:https://tinyurl.com/43hpe5e4),当然不是 60% / 40%。
我们 60%的男性样本数据可能有很多解释。一种可能性是数据收集方法可能有缺陷。也许营销团队在他们的营销活动中意外地击中了更多的男性,导致了不平衡。
如果我们可以确定样本数据应该更好地反映总体情况,那么我们就可以对数据进行“分层”。这将涉及对样本数据进行重新采样,以使比例与总体相匹配(更多信息,请参见https://www . investopedia . com/ask/answers/041615/what-are-advantage-and-missions-layered-random-sampling . ASP)。
更复杂的是,可能涉及到多个特性列。本文中的示例显示了如下两个因素的组合
- 男大学生=人口的 45%
- 女大学生=人口的 20%
- 男性研究生=人口的 20%
- 女研究生=人口的 15%
如果我们的样本数据有 70%的男大学生,它将不能代表人口。
在机器学习算法中,这可能会导致问题。如果我们继续在样本数据上训练我们的模型,而样本数据具有错误的比例,那么模型很可能会过度拟合训练数据,并且当我们根据真实世界或测试数据以正确的比例运行模型时,它也很可能表现不佳。
此示例显示了如何对样本数据进行重新采样,以使其反映有可能提高机器学习模型准确性的总体
入门指南
让我们首先导入所需的库并读入一些从https://www . ka ggle . com/c/credit-default-prediction-ai-big-data/overview下载的数据

作者图片
设置测试数据
为了使示例有意义,我将简化“房屋所有权”特性,使其具有两个最常见的值,并添加一个名为“性别”的新特性,其中约 60%为“男性”,约 40%为“女性”,然后快速查看结果…
(Home Mortgage 0.531647
Rent 0.468353
Name: Home Ownership, dtype: float64,
Male 0.601813
Female 0.398187
Name: Gender, dtype: float64)
准备分层
在我们的示例中,我们希望对样本数据进行重新采样,以反映性别和房屋所有权的正确比例。
我们需要做的第一件事是创建一个包含我们想要分层的所有数据的单一要素,如下所示…
Male, Home Mortgage 0.321737
Male, Rent 0.280076
Female, Home Mortgage 0.209911
Female, Rent 0.188277
Name: Stratify, dtype: float64
现在我们有了,我们在样本数据中有一组比例,我们打算用它们来训练我们的模型。然而,我们与我们的营销团队核实,他们向我们保证人口比例如下…
- 男性,住房抵押贷款=人口的 45%
- 男性,房租=人口的 20%
- 女性,住房抵押贷款=人口的 20%
- 女性,房租=人口的 15%
…两个团队同意,他们必须对数据进行重新采样,以匹配这些比例,从而建立一个准确的模型,该模型将在未来的真实世界数据中很好地工作。
将数据分层
下面是一个函数,它使用DataFrame.sample从源数据中用正确的值精确地采样正确数量的行,这样结果将按照参数中指定的那样精确地分层...
测试
以下代码指定了根据所需比例对数据进行分层的值和比例,即-
- 男性,住房抵押贷款=人口的 45%
- 男性,房租=人口的 20%
- 女性,住房抵押贷款=人口的 20%
- 女性,房租=人口的 15%
…并查看新分层的数据集…

作者图片
为了确保我们得到了正确的结果,让我们来看看Stratify专题专栏的整体比例...
((6841, 20), (6841, 20))
Male, Home Mortgage 0.449934
Female, Home Mortgage 0.199971
Male, Rent 0.199971
Female, Rent 0.150124
Name: Stratify, dtype: float64
结论
我们首先指出,数据收集过程中的缺陷有时会导致样本数据与总体数据的已知比例不同,这可能会导致过度拟合的模型在遇到比例正确的测试或实时数据时表现不佳。
我们继续探索如何对训练数据进行分层和重新采样,使其达到正确的比例,从而解决问题并提高生产算法的性能。
然后,我们选择了一个对两个要素进行分层的复杂示例,要素将这两个要素设计到一个新列中,并定义了一个执行计算并返回分层数据集的函数。
最后,我们检查了结果,以确保计算是正确的。
完整的源代码可以在 GitHub 上找到:
https://github.com/grahamharrison68/Public-Github/blob/master/Resampling/Stratified Sampling.ipynb
感谢您的阅读!
如果你喜欢读这篇文章,为什么不看看我在 https://grahamharrison-86487.medium.com/的其他文章呢?
此外,我很乐意听到您对这篇文章、我的任何其他文章或任何与数据科学和数据分析相关的内容的看法。
如果你想联系我讨论这些话题,请在 LinkedIn 上找我—【https://www.linkedin.com/in/grahamharrison1 或者发电子邮件到ghar rison @ Lincoln college . AC . uk。
分层抽样:你可能完全错误地分割了你的数据集
随机生成数据集的分割并不总是最佳解决方案,因为目标变量中的比例可能会有很大差异。让我向您介绍 Python 中的分层验证。

照片由 Testalize.me 在 Unsplash 上拍摄
在机器学习模型的开发过程中,通常会将数据集分为训练和测试,甚至验证拆分以获得更具代表性的结果。然而,在生成这些分割时,有一些东西可能会影响预测的质量,这是最常被遗忘的:目标变量的分布。
这些数据集划分通常是根据目标变量随机生成的。但是,这样做时,不同分割中目标变量的比例可能会有所不同,尤其是在小数据集的情况下。这意味着我们是在异质子群中训练和评估,会导致预测误差。
解决方法很简单:分层抽样。该技术包括强制目标变量在不同分割中的分布相同。这一微小的变化将导致在被评估的同一人群中进行训练,从而实现更好的预测。
履行
为了说明分层的优势,我将展示在将一个数据集分为训练集、测试集和验证集(有或没有分层采样)时,目标变量分布的差异。举个极端的例子,使用的输入将是虹膜数据集。
请注意,对于这两种情况,目标变量的分布如下:

传统拆分
在不考虑目标变量的情况下,通过将 150 条记录划分为训练、测试和验证,发现了每个部分中的以下比例。

数据集越小,在不同分割中发现目标变量的不同比例的可能性就越大。正如你在这个例子中看到的,亚群体是非常不同的。
分层分裂
另一方面,当在生成分割之前考虑目标变量并按其分组时,得到的分布是:

Python 代码
这个例子可以在 Gist 上公开获得,在这里我提供了一个实用方法get _ dataset _ partitions _ PD,你可以用它轻松地生成熊猫数据帧的分层分割。
最后的话
尽管认为在准备用于训练机器学习模型的数据时只需要随机分割,但事实是数据集分割的随机生成并不总是导致每个数据子集具有相同的目标变量分布,这可能会显著影响结果。
在本文中,我向您介绍一个简单的解决方案:分层抽样;以及如何在 Python 上实现。此外,一个使用分层抽样和不使用分层抽样的例子显示了一个小数据集,说明了在分裂的目标变量比例的巨大差异。
如果你想发现更多像这样的帖子,你可以在下面找到我:
使用优化的分组数据集的分层分裂
本文解释了如何将一个分组数据集分层分割成训练集和验证集

斯科特·韦伯在 Unsplash 上拍摄的照片
机器学习管道中最常见的步骤之一是将数据分成训练集和验证集。这是所有从业者在解决任何问题之前必须掌握的必要技能之一。拆分过程需要对数据进行随机洗牌,然后使用预设阈值进行分区。在分类变体上,您可能希望使用分层来确保两个集合上的类分布相同。处理时间序列数据时,您可能希望跳过混排,并保留对训练集的最早观察。您可能还对数据进行了分组,在这种情况下,您可能希望在任一数据集上将组放在一起。最后,你可能很幸运地遇到了以上几种情况的混合,却没有现成的解决方案,就像我遇到的那样。
本文介绍了我为处理分组数据的分层分割而设计的解决方案,要求各组在任意一个数据集上保持在一起。
背景
我最近从事一个针对几千台工业设备的预测性维护项目。这些设备通过无线连接将遥测数据传输到后端系统,在那里进行存储和进一步处理。所有设备都要进行定期的预防性维护,这不足以避免偶然的意外故障。由于这些故障的破坏性,设备制造商决定创建一套预测性维护模型,以尝试和预测下一个维护周期之前不合时宜的设备故障的概率。
我的团队被指派研究一个特定的设备组件,并设计一个预测性维护模型来估计未来一段时间内的故障概率。我们选择使用分类模型,并经历了建立管道的整个过程。当我们进入数据集分割阶段时,我们决定不在训练集和验证集之间分割设备数据。每个设备数据要么在前者上,要么在后者上,从不在两者之间划分。我们还决定做分层分割,确保两组有相同比例的正面观察。
此时此刻,我们明白没有任何成文的方法来解决这个问题。 Scikit-Learn 包实现了对分割成组数据集或执行分层分割的解决方案,但不能同时实现两者。稍微思考一下,这是有意义的,因为这是一个多目标的优化问题。您必须沿组边界拆分数据,确保请求的拆分比例,同时保持整体分层不变。
不用说,管道的这一部分是手动的,使用一个随机数生成器和一些试验。不,我并不以此为荣,因此写了这篇文章。
问题陈述
给定一个经过标记和分组的数据集,我们希望将其分成训练集和验证集,同时保持标签分布在完整性和组完整性方面尽可能接近。将数据分成两个数据集后,这些组必须保持其完整性,分配给任一数据集,而不是在它们之间拆分。此外,拆分过程应严格遵守强制拆分比例和标签分层。
此问题的预期结果是,给定一个输入数据集,分配给每个数据集的组列表,确保训练/验证分割和分层尽可能接近指定值。
解决方案研究
我开始从经典的优化包中寻求帮助,比如 Google 的或者-Tools 。不幸的是,我在那里没有找到解决办法。实现的优化解决方案似乎接近我所需要的,但仍然没有雪茄。我在寻找一个可以从 Python 管道中使用的解决方案,所以一些著名的基于 Java 的优化器似乎遥不可及。为什么不自己卷呢?
几年前,我参与开发了一个车辆路径优化器,这让我学到了很多关于离散优化算法的知识。我在下面的文章中总结了我的心得。
在回顾了所有这些想法之后,我决定为这个问题开发一个基于 Python 的优化解决方案。我现在展示的是一个概念验证的代码,这个概念验证可能会导致一个独立的 Python 包。让我们从回顾问题模型开始。
问题模型
为了解决这个问题,我们不需要处理整个数据集,只需要处理它的计数。我们只需要确定不同组的数量和每个组的班级数。为了激发对问题模型的讨论,让我们看一个玩具例子,我们想要强加一个 70/30 的分割。

上图描述了一个玩具问题的例子,以及一个可能的解决方案。行包含十个组的计数,第一列包含总计数,第二列包含一类样本的数量。最后一列是建议的解决方案,值 1 表示对验证数据集的赋值。(图片来源:作者)
上图展示了一个基本问题的模型和解决方案。每个表行包含一个组的描述,第一列是大小,第二列是一类样本的数量,第三列表示验证集的包含性(建议的解决方案)。您可以在底部看到列总数,以及第一类样本的计算比例(20.70%)。
那么,提议的解决方案表现如何呢?为了检查是否符合 70/30 分割,我们需要将第一列中所有标记为(值 1)的值添加到第三列中,作为属于验证集的值。部分和(10960)占总数的 29.74%,非常接近要求的 30%。为了检查分层,我们做同样的事情,但在第二列。总和(2328)是上次计数的 21.24%,这意味着我们非常接近所需的 20.70%。
值得注意的是,问题和解决方案的表示都非常简单。我们可以将一个解表示为一个布尔数组,这是非常紧凑的。
那么,我是如何找到解决方案的呢?
优化模型
如前所述,我们希望找到一个既接近分割目标又接近分层目标的解决方案。请注意,如果您有两个以上的类,分层将有多个目标。
优化过程从使用组的随机排列生成初始近似解开始。
最初的解决方案
最初的解决方案是我们对问题的第一次尝试,是搜索过程中的一个辅助。为了让优化过程快速开始,我们将只关注训练/验证比例和,并从随机洗牌开始。如您所见,下面的函数使用随机排列的组大小来选择所需比例的近似值,不考虑地层。一旦验证集超出其所需容量,该过程就会停止。
上面的代码使用训练/验证比例作为唯一的停止标准来生成初始解决方案。(图片来源:作者)
既然我们有了解决方案,我们可以尝试改进它。我们将通过对解决方案做一些小的改动来做到这一点,实质上就是翻转解决方案数组中的位。但是我们如何知道我们正在改进解决方案呢?我们如何衡量改进?进入成本函数。
成本函数
成本函数测量从现有解决方案到理论最优的“距离”。我们从问题陈述中知道这个最优值,并希望生成一个解决方案数组,该数组与分割和地层比例最匹配。下面的函数计算与给定解决方案阵列相关的成本,并使用 Numba 进行 JIT 编译以提高性能。
优化过程使用上述成本函数评估所有解决方案,较低的成本等同于较好的解决方案。如您所见,成本函数考虑了请求的数据集分割比例与评估的解决方案的分割比例之间的差异。接下来,对于问题的层次和解决方案的层次比例之间的差异也是如此。
现在我们有了一个成本函数,我们如何生成解决方案来评估它呢?我们从开发初始解决方案开始,然后从它开始迭代。
搜索过程
搜索过程基于先前的解决方案迭代地生成新的解决方案,并对它们进行评估。在这个特殊的例子中,我们通过翻转一点解决方案向量来创建一个解决方案,从而将一个组移入或移出验证集。我们必须小心不要创建先前生成的解决方案,以免我们将优化搜索过程送进无限循环。
为了避免循环,我们和阿里阿德涅在她的迷宫里做的一样;我们标出走过的路。我们不使用线程,而是使用 Python 集合来存储搜索空间的访问状态。但是为每一步保留整个状态数组既不节省内存也不快速。为了加快集合包含测试的速度,我们必须首先将状态数组转换成一个更紧凑的表示,即一个字符串。Python 处理字符串散列非常有效,并且我们还通过压缩状态表示而获益。下面的函数展示了如何将一个状态数组转换成一个 ASCII 字符串。注意,这个过程每个字符只使用 6 位,并且是使用 Numba 进行 JIT 编译的。
这个函数通过每六位聚合将一个解向量转换成一个 ASCII 字符串。(图片来源:作者)
现有解决方案
我们总是将目前为止最好的解决方案,即现任解决方案放在手边,并在优化过程结束时将其作为最终方案。每当流程产生一个成本更低的更好的解决方案时,它就会用更好的解决方案替换现有的解决方案。这样,优化器总是有一个现成的答案,与选择的终止标准无关。
优化过程使用特定的标准,通过翻转先前生成的解决方案的单个位来生成新的解决方案。在这里,我们将探讨这一过程的两种不同的方法。第一种方法使用试探法来产生新的解决方案,并且非常类似于在通知搜索算法中使用的方法。第二种方法使用成本函数的梯度来确定下一个最佳解决方案,并且在精神上类似于梯度下降。
启发式方法
基于启发式的优化器的主循环从通过翻转所有父解位来生成可能的派生状态列表开始。然后,它使用一个存储字符串编码的二进制状态数组的集合过滤掉已经访问过的状态(就像一个没有任期的禁忌列表)。正如您从下面的函数中看到的,生成新的解决方案只是迭代位数组,翻转每个位数组,并过滤掉已经探索过的解决方案。
上述函数为给定的解决方案生成所有可能的移动。(图片来源:作者)
然后,该过程使用成本函数评估每个新生成的状态,并相应地对它们进行排序。该是流程选择下一个扩展候选对象的时候了,也是它使用启发式算法的时候了。很简单,最佳候选人是成本最低的候选人。这种方法使优化过程在成本空间中寻找更接近前一个的更好的解决方案。在离散优化问题上,发现局部极小值非常接近是很常见的,所以这种方法是有意义的。在专业文献中,它被称为强化。
需要注意的是,我们不一定要通过选择成本最低的解决方案来改进现有的解决方案。我们只是从生成的列表中选择成本最低的解决方案,但这可能比我们迄今为止的最佳方案成本更高。通过这样做,我们正在进行所谓的“T4”爬山打个比方,你可以想象一个滑雪者爬上一座小山,希望找到一个更陡的斜坡从对面下来。
然而,使用这种策略,我们有被困在当地高海拔山谷的风险。优化程序跟踪它上坡的频率,并在预定数量的降低现任成本的失败尝试后,它采取不同的行为。回到滑雪者的比喻,想象一下,她在试图找到一个好的斜坡时感到无聊,于是呼叫她的私人直升机带她去邻近的更有希望的山峰。运气好的话,她可能会找到一条更刺激的赛道。
这种策略被称为“多样化,”。搜索过程不是使用最低的成本,而是在感觉到一个谷底时寻找最高的成本。这种策略允许优化过程搜索不同的路径,直到难以捉摸的全局最小值。
下面是使用启发式搜索解决优化问题的函数。
上述函数使用试探法搜索最佳训练/验证分割。(图片来源:作者)
如您所见,在每次迭代中,该函数都会生成一个可能解决方案的列表,并且只使用其中一个作为下一步。对于有很多组的问题,这种方法在实际应用中会变得太慢。
梯度方法
我们可以尝试通过遵循成本函数梯度来选择最佳扩展,作为前面方法的替代方案。通过查看成本函数,我们可以快速导出它的梯度。下面的代码将成本函数梯度计算为一个向量。注意,梯度向量仅包含沿着成本的每个维度的计数的差异。
上面的函数计算成本函数梯度。(图片来源:作者)
我们现在可以使用这个向量来搜索对解决方案的最佳修改。请记住,我们仅仅是在训练集和验证集之间移动组,所以我们希望使用成本梯度来确定哪个移动将最大地影响成本值。注意,当验证集计数低于其期望值时,成本向量的第一个分量(上面的第 14 行)是正的。为了降低成本,我们应该向验证集添加另一个组。地层成分也是如此。如果优化过程获得具有正元素的成本梯度向量,则它应该从训练集中选择一个组,并将其移动到验证集中。另一方面,如果所有成本梯度分量都具有负值,我们应该从验证集中移除一个组,并将其放回训练集中。对于中间所有其他情况的决定将取决于向量相似性,如下所述。
优化过程应该如何选择在数据集之间传输的最佳组?这里,我们必须使用向量相似性来将成本梯度向量与问题矩阵中的每个向量进行匹配,选择的度量标准是余弦相似性。

余弦相似度是两个向量的内积和它们的范数的乘积之比。(图片来源:作者)
余弦相似性来源于两个向量之间的内积的定义,并且当两个向量指向相同方向时最大,当相反方向时最小。下面的代码实现了余弦相似性计算,对我们的问题进行了修改,反转了验证组向量的符号。
通过反转验证向量的符号(上面的第 11 行),该算法确保这些向量将更接近于成本梯度,该成本梯度指向与训练数据集的移动相反的方向。
现在我们可以使用余弦相似度来生成下一步棋。下面的代码包含基于余弦相似性标准产生下一个最佳解决方案的函数。
解生成函数计算在来自问题矩阵的每个组向量和计算的成本梯度向量之间计算的余弦相似性得分的向量。下一个最佳解决方案对应于翻转问题向量中得分最高的位。请注意,对于多样化变动,我们使用最低分数。
上面的函数使用成本梯度搜索最佳训练/验证分割。(图片来源:作者)
代码
本文的所有代码都可以在附带的 GitHub 资源库中找到。它存在于单个 Python 文件中,您可以从命令行或您喜欢的 IDE 中运行该文件。有一组独立的函数和两个类,每个都实现了上面描述的一种优化方法。两个解算器不断循环,直到满足停止条件。目前,他们使用最小成本值或无改进的最大迭代次数作为停止标准。
搜索求解器
这个类的求解器方法将当前解扩展到所有允许的状态。然后根据成本值对它们进行排序,并选择最低的进行下一次扩展。该求解器收敛到一个解的速度可能非常慢,因为扩展过程可能需要很长时间来运行许多组的问题。我们可以通过人为地限制扩展的数量,同时增加一点随机性(这里不做)来规避这个缺点。
梯度求解器
梯度求解器使用每个解决方案的成本梯度来选择下一个最佳移动。该求解器不是扩展所有允许的解决方案,而是通过选择最佳解决方案来“直奔主题”。如前所述,该求解器使用余弦相似度来寻找成本梯度向量和组向量之间的最佳匹配。这种设计大大加快了求解器的速度,使它在几乎每个问题实例上都优于搜索求解器。
结果
为了比较这两种方法的性能,我进行了一系列实验,改变了组的数量并测量了运行时间。对于每个组大小,我对这两种方法都执行了十次代码,并在几秒钟内取平均值。除了组的数量,我保持所有其他参数不变。
下图显示了两种优化方法在时间上的表现,作为问题定义中的组数的函数。正如您所看到的,启发式优化器的性能是超线性的,而梯度似乎是线性的。

该图表显示了两种优化方法的时间性能比较,作为组数的函数。(图片来源:作者)
请注意,这些结果应该只是给你一个关于这两种方法性能的提示,因为优化质量(以最终成本值衡量)变化很大。更好的性能比较可能需要限制两种方法在一个窄的时间间隔内报告结果。然而,我总是更喜欢使用梯度法而不是启发式方法。
结论
在本文中,我们研究了将分组的机器学习数据集分割成训练和验证的问题,同时考虑了所需的分割比例和固有的标签分层。我们已经从标准 Python 库中寻找了可能的解决方案,当没有明显的解决方案时,我们决定推出自己的解决方案。我们把这个问题作为一个离散的优化过程,并设计了两个可能的解决方案。第一种解决方案来自于使用启发式的知情搜索,而第二种解决方案使用成本函数梯度来最小化解决方案成本。基于梯度的方法比启发式方法表现出更好的执行性能。
未来的工作
正如我之前所说的,你在这里看到的工作仍然是一个概念验证。我已经展示了如何使用离散优化方法解决分组数据集的分层分割。下一步将是使用 Cython 编译将这段代码转换成一个具有改进性能的成熟 Python 包。
这个想法的另一个可能的扩展将是使分层的 K 倍分裂成为可能。
资源
joo Paulo Figueira 在葡萄牙里斯本的戴姆勒卡车和公共汽车公司 tb.lx 担任数据科学家。
使用 OCI 流和 Oracle 数据库进行流分析
高容量数据流无处不在,无论是服务器日志、web 点击数据、消息、媒体等。Oracle Cloud infra structure Streaming service 为实时接收和使用大容量数据流提供了一个全面管理、可扩展和持久的解决方案。
获取这些数据流后的下一步是将它们分析成有意义的结果,这些结果可用于做出关键的业务和设计决策。Oracle 工具和服务可用于构建端到端的管道;从流到结果的可视化。
OCI 流媒体完全兼容阿帕奇卡夫卡。如果你熟悉卡夫卡式的建筑,它主要有生产者在生产信息。这些消息以数据流的形式(跨主题)存储在 Kafka 集群中。然后,消费者可以从集群中获取数据。

Oracle 帮助我们使用这种简化的方式。OCI 流有流池,你可以考虑取代卡夫卡集群。每个流池都有流,按照上面的架构,这些流是主题。消费者将是 Oracle SQL 数据库。

对 Kafka 或 OsaK 的 Oracle SQL 访问是一个 Oracle PL/SQL 包。它是一个外部表预处理器。您可以通过以下两种方式读取和处理来自 Kafka topic 的实时事件和消息:
- 使用 Oracle SQL 访问 Kafka 启用的视图
在这种情况下,数据不需要存储在数据库中。每次你调用它,它都会从 Kafka topic 中读取最新的数据 - 对 Kafka powered 表的 Oracle SQL 访问
在这种情况下,数据实际上存储在数据库中
使用 OSaK 有多种好处:
- 运行实时流分析作业。直接从流服务读取,无需移动到外部数据源。
- 如果需要,还可以使用它将数据从流服务移动到数据库。
- OSaK 处理偏移管理,消除丢失或重新读取流记录。Oracle SQL 只是消费者。
- 来自测井/传感器等的实时数据。通常是加密的。您可以将它与传统的 SQL 表数据集成,以生成未加密的有意义的仪表板。

在这篇博客中,我将重点介绍启用 OSaK 的视图:
- 它是一个 Oracle 视图,是一个 Kafka 客户端应用程序
- 它被绑定到 Kafka 集群、组、主题、分区
- 它从 Kafka 主题中读取一组记录,并将它们转换成由 Oracle 视图定义抽象的一组 SQL 表行
- 对 OSaK 视图的查询访问同一个主题分区,并从每个分区读取记录
- 在查询启动时,提取分区中现有的行,直到分区中的最新记录
让我们进入设置的实际部分,并开始接收流数据。
第一步:登录你的 OCI 帐户/租赁,并设置一个流服务流池。然后在流池中创建一个流。

从 OSaK 的角度来看,流实际上是 3 分区的卡夫卡主题。
步骤 2:在控制台上进入身份->用户,创建一个新用户。
步骤 3:用户需要通过流服务认证自己(使用 Kafka APIs)。为此,单击您刚刚创建的用户并生成 auth-token。OSaK 需要用户名和认证令牌来使用 Kafka 消费者 API 访问流服务。

一旦生成,这是您唯一可以查看或复制身份验证令牌的时间。请复制它,因为我们为 OSaK 配置 Kafka 集群时会用到它。
步骤 4:创建一个用户组。将您的用户添加到组中。在 OCI,用户在分配给他们所属用户组的策略的帮助下获得特权。因此,为用户组创建一个策略,以便该组中的所有用户都获得访问流服务集群的权限。
因此,当您打开群组时,您可以看到其中的用户。

添加以下策略。您可以输入自己的组和隔离专区名称。

步骤 5:现在我们开始在 Oracle 数据库主机上安装 OSaK。
下载最新版本的 Oracle SQL developer。OSaK 套件随其提供。Oracle SQL 对 Kafka kit 的访问位于 orakafka.zip 文件中。
步骤 6: SSH 进入 Oracle 数据库节点,并将 orakafka.zip 移动到
/home/oracle
步骤 7:切换到 oracle 数据库主机上的 Oracle 用户。
确保您当前的工作目录是/home/oracle。
解压缩 orakafka.zip 文件

第 8 步:在/home/oracle 中创建新目录 ora_kafka_home。我们将使用它作为 OSaK 的基本目录。安装将使用中的脚本orakafka _ distro _ install . sh来完成。/orakafka-1.2.0

最后,您会收到一条消息,确认 orakafka kit 已成功安装。
步骤 9:为 OSaK 配置 JAVA_HOME
要查找 java 路径,请运行命令:
java -XshowSettings:properties -version 2>&1 > /dev/null | grep ‘java.home’
这将给出您在下一个命令中输入的路径
export JAVA_HOME=<Enter path here>
转到目录:/home/Oracle/ora _ Kafka _ home/ora Kafka/bin
然后设置

最后,您会收到一条消息,确认 JAVA_HOME 已成功配置。
您可以通过运行命令来验证安装:

这将产生:

步骤 10:将我们之前设置的流服务流池添加到 OSaK。

在这种情况下,kc1 是集群的名称。您可以随意命名集群。
步骤 11:将流服务流的安全属性添加到/home/Oracle/ora _ Kafka _ home/app _ data/clusters/KC1/conf/ora Kafka . properties
您可以在之前设置的流池下的“Kafka 连接设置”中获得这些属性。

您可以在配置文件中取消对这些特定字段的注释,并填写 Kafka 连接设置中的值:
security.protocol=SASL_SSLsasl.mechanism=PLAINsasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required username=”<YOUR_TENANCY_NAME>/<YOUR_OCI_USERID_HERE>/<YOUR_OCI_STREAMPOOL_OCID>” password=” YOUR_AUTH_TOKEN”;sasl.plain.username=”<YOUR_TENANCY_NAME>/<YOUR_OCI_USERID_HERE>/<YOUR_OCI_STREAMPOOL_OCID>”sasl.plain.password =”YOUR_AUTH_TOKEN”;
第 12 步:以 sysdba 身份登录 SQL,将容器设置为您的 PDB 并运行
@/home/oracle/ora_kafka_home/app_data/scratch/orakafka_create_KC1_CONF_DIR.sql
这将创建目录 KC1 _ CONF _ 目录。
步骤 13:验证您的 Kafka 集群正在工作。转到/home/Oracle/ora _ Kafka _ home/ora Kafka/bin 并运行此命令。您使用-c 参数传递集群名称,使用-b 参数传递引导服务器。
./orakafka.sh test_cluster -c kc1 -b <Enter bootstrap server here>
您应该得到您的主题列表以及它们的分区作为输出。
步骤 14:为 Kafka 集群配置一个 Oracle 数据库用户。
如果您没有现有用户,则需要在 PDB 级别创建一个。然后在/home/Oracle/ora _ Kafka _ home/ora Kafka/bin 中运行:
./orakafka.sh adduser_cluster -c kc1 -u <Enter username here>
以 sysdba 身份登录 SQL,并将容器设置为您的 PDB。然后运行:
@/home/oracle/ora_kafka_home/app_data/scratch/orakafka_adduser_cluster_KC1_user1.sql
这将授予您的用户对 KC1 _ CONF _ 目录的权限。
步骤 15:为这个用户的数据传递父目录,这些数据与他被添加到的 Kafka 集群上的 OSaK 活动相关。
在/home/Oracle/ora _ Kafka _ home/ora Kafka/bin 中,运行:
./orakafka.sh install -u <Enter username> -r /home/oracle/ora_kafka_home/<Username>_user_data_dir
这将生成 2 个包含 SQL 命令的 todo 任务,首先您需要以 sysdba 身份运行(在您的 PDB 中)
@/home/oracle/ora_kafka_home/app_data/scratch/setup_db_dirs_user1.sql
这将创建目录对象
第二个 todo 任务将作为您在 PDB 的用户运行。这将在您的用户模式中安装 OSaK 及其对象。
@/home/oracle/ora_kafka_home/app_data/scratch/install_orakafka_user1.sql
该输出将确认一切设置成功:

步骤 16:在 PDB 中的用户下,用 OSaK 过程 ORA_KAFKA 注册集群。寄存器 _ 集群

步骤 17:创建 oracle 数据库表——它需要与我们 Kafka 主题中的消息具有相同的模式。如果您的邮件是 CSV 格式,则这一步是必需的。

步骤 18:为 Kafka 主题创建视图

由于我设置了 3 个分区,我看到了以 KV_ <cluster_name>_ <group_name>TOPIC <num_of_partition>格式创建的三个视图:</num_of_partition></group_name></cluster_name>

步骤 19:产生数据流
在 web 控制台上,转到您的流并单击生成测试消息

以您设置的模式形式输入 csv,然后单击“生成”

单击 load message,您将看到消息被加载到哪个分区

你可以看到这是在分区 0。转到 SQL dev 并打开分区 0 的视图。

您还可以使用 Java/Python 或 OCI SDK 中的标准 Kafka Producer API 进行流服务,以向流服务流生成消息
每条消息在视图中都有自己的一行,但数据不会持久化。
OSaK 获取并存储元数据信息,如消息的偏移量、它的分区以及附加列中的时间戳。
OSaK 也支持 JSON 格式。如果要输入 JSON 格式的数据,就不需要引用表了。在这种情况下,您可以使用这种格式为 Kafka 主题创建视图:

输出将以键、值:

该值将只存储整个输入—在该输入的情况下是 json blob。
这篇博客介绍了 OSaK 的设置和从数据流中读取数据。在下一篇文章中,我将探索更多关于使用 Python 生成 OCI 流和创建实时分析仪表板的内容。
快乐流媒体!
流处理变得简单
卡夫卡+react vex = Maki Nage

迈克·刘易斯 HeadSmart 媒体在 Unsplash 上拍摄的照片
越来越多的数据科学用例是实时完成的:警报、缺陷检测、预测、自动恢复就是一些例子。然而,实施和部署它们可能非常具有挑战性。 Maki Nage 是一个 Python 框架,旨在简化实时处理应用的开发和部署。本文介绍了如何使用它,说明应该简化流处理!
解决推理差距
在我们接触一些例子之前,让我简单解释一下为什么我们创建 Maki Nage。在我的数据实验室,我们经常致力于实时流媒体服务。在实践中,我们构建的算法和机器学习模型是由卡夫卡流提供的。然而,我们用于学习和训练的数据是从数据湖中检索的。这些是批量数据,而推理是以流模式运行的。因此,我们为数据准备编写的代码不能用于部署:我们通常使用 Pandas 和 Spark 来处理批量数据,没有办法在部署中重用它。所以最后另一个团队必须重新实现特性工程部分,通常是用 Java 和 Kafka 流。这种情况在数据科学中相当普遍。流媒体通常会加剧这种情况。
最终,这种双重实现会降低部署速度,并增加潜在的错误,因为数据在训练和推理之间的转换方式不同。
Maki Nage 旨在缩小这一推理差距。它通过以下方式实现:
- 公开一个简单且富于表现力的 Python API。
- 以流模式处理批处理数据。
- 准备部署为 Kafka 服务,无需额外的基础设施。
已经有一些工具可以进行流处理。但是他们中的大多数目标更多的是开发者而不是数据科学家:Kafka Streams、Apache Flink 和 RobinHood Faust 就是这样的框架。Spark 结构化流似乎是以专用集群为代价的例外。
Maki Nage 允许运营团队部署由数据科学家编写的代码。尽管如此,它支持整个 Python 数据科学栈。
现在让我们开始吧!
第一步
在本文中,我们将使用 Maki Nage 的数据科学库: RxSci 。使用 pip 以通常的方式进行安装:
python3 -m pip install rxsci
在接下来的示例中,我们将使用整数列表作为数据源:
import rx
import rxsci as rs
source = rx.from_([1, 2, 3, 4, 5, 6, 7, 8])
from_ 操作符从 Python Iterable 创建事件流。在 RxSci 术语中,一个流被称为一个可观测的,一个事件被称为一个项。这些名字来自 ReactiveX ,Maki Nage 的一个构建模块。
这里,源可观察对象由一个小列表组成,但它也可能是一个不适合内存的大数据集。流式处理的一个优点是可以处理比可用系统内存大得多的数据集。
现在让我们处理这些数据。
无状态转换
第一类转换是无状态操作。在这些操作符中,输出仅依赖于输入项,而不依赖于过去接收到的其他项。你可能已经知道其中一些了。 map 操作符是许多转换的构建块。它对每个源项应用一个函数,并发出另一个应用了该函数的项。下面是它的使用方法:
source.pipe(
rs.ops.map(lambda i: i*2)
).subscribe(on_next=print)
结果是:
2 4 6 8 10 12 14 16
第一行中的管道操作符允许连续链接几个操作。在这个例子中,我们只使用了映射操作符来加倍每个输入项。最后的 subscribe 函数是执行计算的触发器。RxSci API 是惰性的,这意味着您首先定义一个计算图,然后运行它。
另一个广泛使用的操作符是过滤器。过滤器操作员根据条件丢弃项目:
source.pipe(
rs.ops.filter(lambda i: i % 2 == 0)
).subscribe(on_next=print)
结果是:
2 4 6 8
有状态转换
第二类转换是有状态转换。这些运算符的输出取决于输入项和某些累积状态。有状态操作符的一个例子是 sum :计算总和依赖于当前项目的值和所有先前收到的项目的值。总和是这样计算的:
source.pipe(
rs.state.with_memory_store(rx.pipe(
rs.math.sum(reduce=True)
)),
).subscribe(on_next=print)
结果是:
36
使用有状态转换时有一个额外的步骤:它们需要一个状态存储来存储它们的累积状态。顾名思义, with_memory_store 操作符创建一个内存存储。还要注意和操作符的减少参数。默认行为是发出每个输入项的当前总和。Reduce 意味着我们希望只有当源可观察性完成时才发出一个值。显然,这只对非无限的可观察对象有意义,比如批处理或窗口。
作文
当将所有这些基本块组合在一起时,RxSci 开始变得很棒。与大多数数据科学库不同,聚合并不是世界末日:在聚合之后可以使用任何可用的操作符。
考虑下面的例子。它首先将项目分组为奇数/偶数。然后,对于每个组,应用 2 个项目的窗口。最后,在这些窗口中的每一个上,计算总和。这是用 RxSci 用非常有表现力的方式写出来的:
source.pipe(
rs.state.with_memory_store(rx.pipe(
rs.ops.group_by(lambda i: i%2, pipeline=rx.pipe(
rs.data.roll(window=2, stride=2, pipeline=rx.pipe(
rs.math.sum(reduce=True),
)),
)),
)),
).subscribe(on_next=print)
结果是:
4 6 12 14
现实的例子
我们将以一个现实的例子来结束本文:实现基于天气指标预测房屋耗电量的特性工程部分。
如果您想了解更多细节,可在制造文档中找到完整示例。
我们使用的数据集是一个 CSV 文件。我们将使用它的下列栏目:大气压力,风速,温度。我们想要的功能是:
- 温度。
- 大气压和风速之间的比率。
- 滑动窗口内温度的标准偏差。
这些都是简单的功能。它们应该在几行代码中实现!
首先是存储要素和读取数据集的一些准备工作:
Features = namedtuple('Features', [
'label', 'pspeed_ratio', 'temperature', 'temperature_stddev'
])
epsilon = 1e-5
source = csv.load_from_file(dataset_path, parser)
然后,处理分三步完成:
features = source.pipe(
# create partial feature item
rs.ops.map(lambda i: Features(
label=i.house_overall,
pspeed_ratio=i.pressure / (i.wind_speed + epsilon),
temperature=i.temperature,
temperature_stddev=0.0,
)),
# compute rolling stddev
rs.state.with_memory_store(rx.pipe(
rs.data.roll(
window=60*6, stride=60,
pipeline=rs.ops.tee_map(
rx.pipe(rs.ops.last()),
rx.pipe(
rs.ops.map(lambda i: i.temperature),
rs.math.stddev(reduce=True),
),
)
),
)),
# update feature with stddev
rs.ops.map(lambda i: i[0]._replace(temperature_stddev=i[1])),
)
第一步将 CSV 数据集的输入行映射到一个特征对象。除了温度的标准偏差之外的所有字段都在该运算符之后准备就绪。
然后创建一个步长为 1 小时的 6 小时滚动窗口。对于每个窗口,计算温度的标准偏差。然后转发每个窗口的最后一项。
最后一步是更新 Features 对象。第二步中使用的 tee_map 操作符对每个输入项进行了多次计算。它输出一个元组,每个计算分支一个值。这里我们有两个,所以元组中有两个条目:第一个是窗口的最后一个特征对象,第二个是温度的标准偏差。
更进一步
希望您对 Maki Nage APIs 的外观以及如何用它们处理时间序列有一个很好的了解。从一开始就记住,目标之一是使用相同的代码进行训练和推理。最后一个例子是机器学习模型的特征工程阶段。该代码可以用于在生产中处理来自 Kafka 流的数据。
Maki Nage 包含将 RxSci 代码部署为服务的工具。它还有一个模型服务工具来部署与 MlFlow 打包在一起的模型。
请阅读文档了解更多详情。
在撰写本文时,Maki Nage 仍处于 alpha 阶段,但我们预计不会有重大的 API 变化。我们仍在致力于该框架的许多方面,您可以参与进来:请随意尝试,给出一些反馈,以及贡献!
原载于 2021 年 1 月 11 日【https://blog.oakbits.com】。
使用 Spark 3 将您的 Cosmos DB 更改传输到 Databricks

图片来自皮克斯拜
Azure Cosmos DB Apache Spark 3 OLTP 连接器使用 Python 在 Databricks DBR 8.0+中实现
Cosmos DB 越来越受欢迎,因为它的低延迟响应使它更适合作为事务数据库解决方案。然而,Cosmos DB 中的数据也可能需要用于数据分析和报告。在这种情况下,我们需要使用更合适的工具。Spark 通常被认为是大数据分析的最佳选择。
Databricks 作为一个现代化的云数据平台,构建在 Spark 之上,提供了许多很酷的功能,以改善开发体验和实际的数据处理性能。因此,它被各行各业的许多公司所使用。
微软曾经有大量关于 Spark 2 连接器的资源和参考资料。然而,当涉及到基于 Spark 3 的 Databricks DBR (Databricks 运行时版本)8.x 时,我们必须使用相应的 Spark 3 连接器。不幸的是,还没有太多的文档和示例可供参考。
在本文中,我将提供这种场景的一个简单示例,以及我们遇到的一些常见问题及其解决方案。
建立结构化流

结构化流是一个基于 Spark SQL 引擎的可扩展和容错的流处理引擎。您可以像表达静态数据上的批处理计算一样表达您的流计算。Spark SQL 引擎将负责增量地、持续地运行它,并随着流数据不断到达而更新最终结果。
Cosmos DB Change Feed 的官方文档建议使用 Azure 函数来接收增量数据。有了 Databricks,我们不必再增加一层,因为 Spark Streaming 是解决这一问题的完美方案。
现在,跟着我一步一步地学习这些教程。这并不困难,你可以在 10 分钟内将你的火花传送到 Cosmos DB change feed。
1.准备连接详细信息
我们需要拥有 URI 端点和我们将要读取的来自 Cosmos DB 的密钥。建议使用只读键,因为我们不会将任何内容写回 Cosmos DB。授予最低要求的权限总是一种好的方式。
这个 URL 可以在 Azure 门户的 Cosmos DB 概述页面上找到。

在同一个页面上,您还需要记下数据库的名称。

然后,可以在“密钥”选项卡中找到只读密钥。

还强烈建议将这些连接详细信息放入 Azure Key Vault,这样我们就不会将它们作为明文代码放在 Databricks 笔记本中,这将成为一个严重的安全漏洞。
2.安装 Cosmos DB Spark 3 连接器
在使用连接器之前,我们需要将库安装到集群上。转到 Databricks 工作区中的“Compute”选项卡,选择要使用的集群。然后,导航到“库”选项卡,并单击“安装新的”。

之后选择 Maven 作为库源,输入坐标:
com.azure.cosmos.spark:azure-cosmos-spark_3-1_2-12:4.0.0

请注意,根据您阅读我的文章的时间,版本可能会有所不同:)最好从 Azure 文档中查看最新版本。
3.在 Databricks 中配置 Cosmos DB 连接的读取
一旦我们将 Cosmos DB 只读密钥放入 Azure Key Vault,我们需要在 Databricks 工作区中创建一个秘密范围。这不是本文的重点,所以我将跳过它。如果您不知道如何创建 Databricks secret 作用域,请遵循以下文档。
https://docs.microsoft.com/en-us/azure/databricks/security/secrets/secret-scopes
在创建 Python 字典作为变更提要配置对象之前,我们需要从 Azure Key Vault 中读取连接细节。
cosmosDatabaseName = dbutils.secrets.get('YOUR-KEY-VAUL', 'DB_NAME')
cosmosContainerName = dbutils.widgets.get("container_name")
cosmosEndpoint = dbutils.secrets.get('YOUR-KEY-VAUL', 'URI_ENDPOINT')
cosmosMasterKey = dbutils.secrets.get('YOUR-KEY-VAUL', 'READ-ONLY-KEY')
是否使用widgets作为容器名称是可选的。在我的例子中,我希望参数化容器名,以便以后可以重用这个笔记本。
现在,我们可以构造配置对象了。
# Initiate Cosmos Connection Config Object
changeFeedCfg = {
"spark.cosmos.accountEndpoint": cosmosEndpoint,
"spark.cosmos.accountKey": cosmosMasterKey,
"spark.cosmos.database": cosmosDatabaseName,
"spark.cosmos.container": cosmosContainerName,
"spark.cosmos.read.partitioning.strategy": "Default",
"spark.cosmos.read.inferSchema.enabled" : "false",
"spark.cosmos.changeFeed.startFrom" : "Now",
"spark.cosmos.changeFeed.mode" : "Incremental"
"spark.cosmos.changeFeed.maxItemCountPerTriggerHint" : "50000"
}
一些配置对象需要一些解释:
spark.cosmos.read.partitioning.strategy
这是为了定义所使用的分区策略(默认、定制、限制或主动)。通常,我们可以使用默认设置,以便动态计算分区数量,从而保持优化。
spark.cosmos.read.inferSchema.enabled
我建议不要启用推断模式,尽管它看起来很聪明。当我们在 Cosmos DB 中有相对大量的文档时,推断出的模式很有可能是错误的。此外,如果 Cosmos 容器的模式不一致,几乎肯定会导致模式错误。
我们可以读取原始的 JSON 字符串,然后使用 PySpark 对其进行处理。
spark.cosmos.changeFeed.startFrom
此项告诉连接器从哪里开始。通常,我们可以将它设置为“Beginning ”,这样流的第一批将从表的开头开始。
然而,如果我们从一个巨大的容器中阅读。更聪明的做法是从“现在”开始读取,并在单独的作业中加载历史数据,以提高性能和健壮性。
spark.cosmos.changeFeed.mode
这必须设置为Incremental,因为我们将从更改提要中读取。
spark.cosmos.changeFeed.maxItemCountPerTriggerHint
这是可选的。但是,如果您在 Cosmos DB 端的更新不一致,这将非常有用。例如,有些批次很小,而有些批次很大。该选项将尝试每个触发器处理有限数量的项目,以便当有非常大的一批项目到达时,不会立即破坏您的集群。
4.配置流数据帧的写入
为了简化本教程,我们不必为 Spark 流编写复杂的配置对象。我们唯一需要的是检查站的位置。
writeCfg = {
"checkpointLocation": "/FileStore/checkpoints/"
}
检查点非常重要,因为如果我们的 Spark 流被计划或非计划地停止,它会记住检查点,以便我们可以从那里读取。
Cosmos Spark 连接器将创建一个子目录,这样我们就不必为当前正在读取的特定容器指定名称。
5.创建一个表来接收数据
我们需要创建一个表,然后才能写入从 Cosmos DB Change Feed 接收到的增量数据。如果您没有像我一样使用推断模式特性,那么该表应该定义如下。
CREATE TABLE IF NOT EXISTS <schema_name>.<table_name> (
_rawBody STRING,
id STRING,
_ts LONG,
_etag STRING
)
USING DELTA
LOCATION '<dbfs_location>';
如果您使用推断模式,您将需要用相应的列定义表。
6.建立火花流
现在我们可以使用配置对象来建立流。以下代码将建立流,并将其读入 Spark 数据帧。
df = spark.readStream.format("cosmos.oltp.changeFeed").options(**changeFeedCfg).load()
您可能想对数据帧进行一些转换。之后,我们可以将它写入刚刚创建的表中。
df.writeStream.format('delta').outputMode('append').options(**writeCfg).table('<schema_name>.<table_name>')
创建数据块作业

图片由 www_slon_pics 发自 Pixabay
我们实际上已经完成了笔记本。如果您运行它,它将建立 Spark 流,并开始接收来自 Cosmos DB 的任何更改,并将其写入表中。
但是,强烈建议将所有内容都放在一个作业中,这样我们就可以使用作业集群,而不是与所有其他活动共享的交互式集群。此外,如果我们将流作为一项工作来运行,它在可靠性方面会更有信心。
导航到 Databricks 工作区中的“作业”。输入任务名称,并选择我们刚刚创建的笔记本。

不要忘记添加连接器作为依赖库。

解决纷争

由免费提供的图像-来自像素的照片
在我们的开发过程中,因为连接器在文档方面相对不成熟,我们发现了以下两个问题。希望如果你也遇到他们,这能帮你节省一些时间。
1.火花流总是从开始开始
如果您在 cosmos DB 中有一个相对较大的容器,您可能希望在当前时间戳开始流传输,并在单独的活动中加载历史数据。
但是,如果更改源已设置为Beginning,并且检查点未被清除,则流可能无法从“现在”开始,因为检查点“告诉”它:“嘿,您应该从我们之前到达的地方开始!”
"spark.cosmos.changeFeed.startFrom" : "Beginning"
# v.s.
"spark.cosmos.changeFeed.startFrom" : "Now"
解决方案是从 DBFS 手动删除检查点,或将检查点更改为新路径。然后,论点“现在”将正确地发挥作用。
2.异常:readtimeout 异常
在流运行一定时间后,您可能会看到此异常。
azure_cosmos_spark.io.netty.handler.timeout.ReadTimeoutException
出现此异常的原因之一可能是群集的当前运行节点太少,因此可用性将成为瓶颈。此外,如果突然有一个大批量进入,一个相对较小的集群将无法按时完成处理。
因此,解决方案有两个步骤。
首先,我们可以将“最低工资”增加到至少 2 人。如果你真的在处理高速运动,那就给它更多。

第二个解决方案是为作业添加重试策略。因此,当作业失败时,它将在一段时间后重新运行。
我们可以在“高级选项”中找到重试策略

然后,由您决定设置重试的次数以及等待重试的时间。

摘要

图片由 StartupStockPhotos 来自 Pixabay
在本文中,我使用了一个简单的例子来演示如何使用 Azure Cosmos DB Spark 3 OLTP 连接器建立 Spark 结构化流来读取 Cosmos DB change feed。我相信这是将增量变化从 Cosmos DB 读入 Databricks workspace 进行进一步数据分析的最简单方法。
https://medium.com/@qiuyujx/membership
如果你觉得我的文章有帮助,请考虑加入灵媒会员来支持我和成千上万的其他作家!(点击上面的链接)
通过自定义人工智能层流式传输到 Apple Home

如何将旧网络摄像头用作现代安全摄像头
介绍
家庭自动化系统如今变得越来越受欢迎。智能家居中可以实现的一个功能是安全摄像头。
我选择不花钱买一个全新的摄像头,而是使用一个简单的网络摄像头,并将其传输到远程控制应用程序。
有趣的是,使用这种定制解决方案,可以很容易地在图像上添加层信息,并且对于我们数据科学家来说,限制是天空(或相机分辨率)。
设置
在这里,我将描述一个简单的设置,一个网络摄像头和一个 RaspberryPi 连接到一个神经计算棒。
它们可以组装在一起,构建一个集成了人工智能的网络服务器。硬件连接很简单,人们只需注意将 NCS 连接到 Raspberry 的 USB3.0 端口,即可获得更好的性能。
网络摄像头记录视频,Pi 获取帧,并使用计算棒在它们上面添加一些信息。

设置。图片作者。
流式传输帧:网络服务器
我们需要发送帧,为此我们将使用一个简单的 web 服务器,通过 http 将 ffmpeg 兼容的帧发送到给定的端口。
webserver 的核心在下面的代码中:每次调用 enerator 时,捕捉一个帧,调用一个模型并提供结果图像,这一切都非常快。
def generator():
import io
global model
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
frame = model.process(frame)
encode_return_code, image_buffer = cv2.imencode('.jpg', frame)
io_buf = io.BytesIO(image_buffer) yield (b'--frame\r\n'+ b'Content-Type: image/jpeg\r\n\r\n' + io_buf.read() + b'\r\n')@app.route("/video")
def video():
return Response(generator(), mimetype='multipart/x-mixed-replace; boundary=frame')
使用 Flask 和 OpenCV 可以从通过 USB 连接到 RPi 的摄像机获取视频帧序列,然后将它们流式传输到可以使用许多不同工具访问的网页。
人工智能层
当捕捉到帧时,可以使用图像分类模型对其进行解析,并将关于图像中存在哪个对象的信息添加到帧中。
使用 openVINO,分类过程将像
exec_net.infer(inputs={self.input_blob: images})
另一个更有趣的可能性是使用 YOLO 模型。经典方法将模型应用于多个位置和尺度的图像。图像的高分区域被认为是检测。
YOLO 采用了一种完全不同的方法。它将一个单个神经网络应用于整个图像。该网络将图像分成多个区域,并预测每个区域的边界框和概率。因此,可以非常高效地识别一幅图像中的多个物体。
在这里,我描述了两个例子:一个分类器和 YOLO ,但是,当然你也可以运行任何模型,只要简单地将它转换成与神经计算棒兼容的格式。
向移动设备发送视频:Homebridge
一个人有了视频帧,就有可能将它们流式传输到其他设备,我最喜欢的方法是将其流式传输到专门用于家庭管理的应用程序,这样它们最终可以与其他设备集成。在这种情况下,我使用了 Homebridge 。
Homebridge 是一个非常强大的工具,可以连接各种设备,甚至是定制设备,我们很快就会看到苹果家庭基础设施。
ffmpeg 插件
在这个例子中,我选择了一个使用 ffmpeg 来读取这些帧的插件。
https://github.com/Sunoo/homebridge-camera-ffmpeg
这应该被配置为从端口 :5000 (或者配置在 Flask 中的端口)上的本地主机读取帧。也可以为设备和其他设置选择名称。
"accessories": [
{
"name": "Camera FFmpeg",
"cameras": [
{
"name": "raspiwebcam",
"videoConfig": {
"source": "-f mjpeg -i [http://localhost:5000/video](http://localhost:5000/video)"
}
}
],
"platform": "Camera-ffmpeg"
}
]
苹果 Home 应用
当 Homebridge 设置好后,服务器启动并运行我们就可以在 Apple Home app 中检查来自网络摄像头的图像了。
正如你在下面的截图中所看到的(我知道来自网络摄像头的图像质量很差)
基于初始的图像分类器可以添加关于模型当前正在看到哪个对象的信息。

苹果首页截图。图片作者。
跑步 yolo
https://pjreddie.com/darknet/yolo/
一个简单的脚本可以用来处理帧和检测帧上的物体

苹果主页上的 yolo。图片作者。
现在我们已经准备好在相机上捕捉每一个有趣和可爱的东西!
密码
https://github.com/fvalle1/ai_server/tree/raspbian 的是一个仓库,里面有让所有这些正常工作所需的所有脚本和代码片段。
自然语言建模的流式词汇:动态单词替换
实践教程

图片由 Eli Digital Creative 来自 Pixabay
最近,我一直在研究如何在我的网页内容推荐系统中使用递归神经网络模型。虽然这似乎是 RNN 的一个自然应用:人们一个接一个地访问 web 文档,并且在这些序列中可能存在可消化的因果关系,但一个巨大的挑战初看起来变得显而易见。新文档不断出现,而模型的词汇是不可变的。为了应对这种情况,一种可能性是训练模型推荐最近几天访问过的文档,部署并开始发布推荐,在下一次训练迭代之前忽略所有新发布的文档,在下一次训练迭代之后,必须从头开始训练另一个模型。另一种可能性是找到一种方法来替换模型词汇表中的文档 id,用新文档替换不再被访问的旧文档。后者的巨大优势是培训过程的连续性。也就是说,一个单一的模型可以持续很多年,不断更新,并记住更长期的模式。我发现了一个有趣的方法来实现这一点。让我分享一下我的发现。
流式自然语言模型
我要换个方式,用一个自然语言模型来说明我的发现。虽然乍看起来,这个主题似乎与推荐系统无关,但两者有一些重要的共同点。推荐系统中的令牌(“单词”)是可以被推荐的项目,“文本”是由单个人消费(观看)的项目序列。在这种情况下,预测“文本”中的下一个“单词”是推荐系统的目标。动态令牌替换在两个域中都适用。我选择了自然语言建模,因为它更受欢迎,也更容易验证。
我将建立一个简单的 LSTM 模型,它不断地接受新闻文章,将学会预测文本中的下一个单词。为什么是“流”?我不会在同一个数据集上训练 epochs,相反,我会假设文本数据是无限的,并且没有办法预先检查它。重要的是,我将比较具有静态词汇表的基线模型与具有动态词汇表的相同模型的性能。
但是首先,让我定义一下什么是静态和动态词汇表。
完整的代码可在这里找到
当静态词汇足够大,并且您有足够的资源来训练具有大量类的神经网络时,模型很可能会表现良好。但重要的不仅仅是字数。有时,新词会出现。例如,如果你碰巧在 2018 年训练了一个超大质量的 10 亿字模型,它不会知道“covid”这个词,不是吗?从某种程度上来说,其他很多词也是如此。单词出现,变得频繁,变得不频繁,不断消失。当然(与推荐系统不同,在推荐系统中,被推荐的不是单词,而是项目)在 NLP 中,这个问题并不是很饱和。但是它是存在的,如果有一种方法可以根据最新的趋势改变词汇,那将会很方便。如果过程是自动化的,那就更好了。
当谈到维护最频繁访问的有限对象集时,首先想到的是广泛使用的 LRU 缓存回收算法。
完整的代码可在这里
尽管这个实现解决了新单词的起源问题,但是它有一个严重的缺陷。具有低范围频率的单词必须竞争获得索引。这导致许多单词消失,并在词汇表中以不同的索引重新出现。反过来,这在神经网络中产生大量噪声,并最终导致模型的更高复杂度。当然,根据应用的不同,这可能不是一个大问题。但是有一种更好的动态单词替换方法。
将第二个队列添加到动态词汇表的想法同样源于缓存算法。特别是 1994 年提出的 2Q 算法 (PDF)。如今,2Q 算法已经过时了。自适应替换算法(例如 ARC )以类似的计算成本输出更高的命中率。但在词汇应用中,命中率并不靠前。这里更重要的特性是 2Q 相似算法能够阻止非频繁项在主队列中的驻留。这意味着,与 DynamicVocabularyLRU 中不同,一个在整个宇宙中一个世纪才发音一次的单词(如“kakorrhaphiophobia”)不会通过从词汇表中驱逐其他更频繁的单词来获得索引。
完整的代码可在这里
在这种实现中,未知单词的概念获得了新生。这不一定是件坏事。根据应用程序的不同,较低的模型复杂度可能会超过对更高的词汇表多样性的需求。直观上,两者是有联系的。在其他条件相同的情况下,你可以通过缩小词汇量和让更多的不常用词保持未知来降低困惑。
就数据而言,在快速搜索了大量新闻文本后,我发现了 Kaggle 的所有新闻数据集。它包含 143,000 篇新闻文章,时间跨度约为 3 年。这足以说明新频繁出现的词语的问题。我下载了数据集,将 3 个 CSV 文件连接在一起,按“日期”列进行排序,并将整个数据集保存为一个文件。然后我编写了一个适合数据集的基本标记器,并开始研究它的特性。
完整的代码可在这里
对于自然语言来说,词频分布看起来相当标准。在整个数据集中出现 100k 到 1m 次的有 90 个词,出现 1 到 10 次的有 250k 个词。

在数据集的前 10m 个单词中,词频分布的比例看起来非常相似。

这就是为什么我一直在看前 1000 万个单词。当我使用数据集的 1000 万个单词时,文本生成器停在了日期为 2016 年 2 月 19 日的文章上。让我们想象这一天就是今天。我们被要求创建一个模型,提供今天的所有新闻,预测明天的股票价格。手头的数据是去年 14k 篇新闻文章中的这 1000 万字和股价历史(实际上,时间跨度更大,但这都是假设的情况)。该模型应该从今天开始预测,并随着时间的推移不断从实际数据中学习。为了学会将今天的词汇映射到明天的价格,首先,我们建立一个全面的词汇。
我在数据集的其余部分比较了 static vocabulary 2q 和 DynamicVocabulary2Q 的性能。这两个词汇表都被输入了来自数据集中前 1000 万个单词的全部 110 万个唯一单词,然后被询问 1 亿次数据集中的每个下一个单词是否都出现在单词 2idx 中。

在 DynamicVocabulary2Q 稳定在大约 0.5%的未登录词之前,需要相当大量的文本。但更有趣的是,统计词汇中未知词的比例有明显的增长趋势。这证明了语言的核心是随着时间而变异的。由于这种现象,使用静态词汇的长期自然语言模型的性能不可避免地会下降。
现在来看看实际的模型。不幸的是,我负担不起上述价格预测器的建立和训练,以至于其结果与上述发现相吻合。本节的重点是说明动态词汇表的灵活性是以模型的更高复杂度为代价的。我建立了一个更小的模型,它没有任何特殊用途,只是学习预测文章中的下一个单词。
完整的代码可在这里获得
超参数如下:词汇表的大小是 10k 个单词,嵌入层输出的维数是 256,隐藏状态的维数是 256,有两个 LSTM 层,其间有 0.2 的下降。
用三种不同的词汇对 RNN 进行了三次训练:静态词汇、动态词汇和动态词汇。为了实验的纯粹性,所有条件都是相同的:我跳过了前 50k 篇新闻文章(为了达到文章在发表日期方面更密集的程度,以避免不必要的噪音),消耗 500k 个单词来预热词汇(或者填充,在静态词汇的情况下),然后在接下来的 3m 个单词上训练网络,单词序列的大小为 10,一批的大小为 100。平均困惑度每 1000 批储存一次。这里上面两段是用 Python 写的。

正如所料,由于词汇表的高度不稳定性,具有 DynamicVocabularyLRU 的模型表现出最差的性能。其他两者的表现相当接近,静态词汇如预期的那样占优势。虽然,这样的比较不能只基于困惑的衡量。如果我们设想一种退化的情况,其中词汇表由单个标记组成:UNK(未知单词),这样的模型在一系列 UNK 之后立即学习以 100%的概率预测 UNK。它的困惑度将会是 1。当一个词汇表包含一个 UNK 和 10k 个在文本中从未使用过的垃圾单词时,也会出现类似的情况。这样的模型在猜测下一个单词时也很快变得非常确定。将它的困惑与一个拥有同样规模的更有意义的词汇的模型进行比较是不公平的。
出于好奇,我看了看 UNK 在整个训练中有多少次成为最热门的预测。我排除了 DynamicVocabularyLRU,它从不预测 UNK,因为它“知道”所有的单词。

尽管两个词汇表的大小相同,但 UNK 成为最佳猜测的频率在静态词汇表中明显更高。动态词汇模型的语言更加多样化,因此具有更高的复杂性。
总结一下,DynamicVocabulary2Q 有效地遵循了语言的核心,而后者随着时间而变化。它温和地驱逐那些不再流行的单词,而不会在神经网络中产生太多噪音。输出的措辞更加多样化。该模型可以训练很长时间而不会过时。所有这些都是以更高的困惑为代价的。
使用 dbt-generator 简化您的 dbt 项目部署
运行 50 个命令来生成基本模型?为基础模型编写 100 次相同的变换?这个软件包将为您简化这一过程。

我们热爱 dbt,我们几乎在所有的咨询项目中都使用它。在 dbt hub 中有许多很酷的预制软件包,可以显著提高您的工作效率。例如,Fivetran 为 Hubspot、Salesforce、Google Ads 和脸书广告等许多常见来源创建了预制的初始模型。Fishtown Analytics 也创建了一些很棒的启动包,比如 dbt-utils、audit-helper 和 code-gen。
当开始一个新客户的项目时,我们通常会经历相同的步骤来建立一个新的 dbt 项目。在将来自数据源的数据集成到数据仓库之后,我们使用 code-gen 包来生成包含所有表的源模式。我们还使用相同的包为我们的表生成基础模型,在这里我们可以重命名列并进行一些简单的转换。
对于较小的客户机或表较少的数据源,这完全没问题。然而,当您在一个源中有 10 个以上的表时,运行相同数量的命令来生成基本模型可能会令人望而生畏。此外,同一个源的列通常有相同的命名约定。我们的时间可以更好地用在其他地方,而不是把write_at列改成modified_date列 50 次。
dbt 发生器
介绍[dbt-generator](https://pypi.org/project/dbt-generator/)🎉🎉。我写了一个简单的 Python 包来帮助解决这个问题。
这个包有助于生成基本模型并批量转换它们。对于拥有 10 个以上模型的源,这个包将通过批量生成基础模型并将它们转换为公共字段来为您节省大量时间。使用这个包是开始您的建模或加入新资源的好方法。
入门指南
要使用这个包,您需要安装 dbt 并配置一个概要文件。您还需要从 dbt Hub 安装 code-gen 包。将以下内容添加到 dbt repo 中的 packages.yml 文件,并运行dbt deps来安装依赖项。
packages:
- package: fishtown-analytics/codegen
version: 0.3.2
通过运行以下命令,在与 dbt 安装相同的环境中安装软件包:
pip install dbt-generator
这个包应该在您的 dbt repo 中执行。这个包的源代码可以在这里找到:
https://github.com/tuanchris/dbt-generator
生成基础模型
要生成基础模型,使用dbt-generator generate命令。这是一个围绕codegen命令的包装器,它将生成基本模型。当您有很多模型,并且想要一次生成它们时,这尤其有用。
Usage: dbt-generator generate [OPTIONS] Gennerate base models based on a .yml sourceOptions:
-s, --source-yml PATH Source .yml file to be used
-o, --output-path PATH Path to write generated models
--source-index INTEGER Index of the source to generate base models for
--help Show this message and exit.
例子
dbt-generator generate -s ./models/source.yml -o ./models/staging/source_name/
这将读入source.yml文件,并在staging/source_name文件夹中生成基础模型。如果您在yml文件中定义了多个源,使用--source-index标志来指定您想要为哪个源生成基础模型。
转换基础模型
对于同一个源,表之间通常有一致的命名约定。例如,created_at和modified_at字段通常在所有表格中被命名为相同的名称。将所有这些字段更改为不同数据源的通用值是一种最佳做法。然而,对 10 多个表中的所有日期列都这样做是一件痛苦的事情。
使用这个包,您可以编写一个将被读入的transforms.yml文件(.yml文件可以被命名为任何名称)。该文件将包含您想要应用到所有基础模型的转换。您可以重命名基本模型中的字段,或者对转换后的字段应用定制的 SQL select。
Usage: dbt-generator transform [OPTIONS] Transform base models in a directory using a transforms.yml fileOptions:
-m, --model-path PATH The path to models
-t, --transforms-path PATH Path to a .yml file containing transformations
-o, --output-path PATH Path to write transformed models to
--drop-metadata BOOLEAN The drop metadata flag
--case-sensitive BOOLEAN The case sensitive flag
--help Show this message and exit.
例子
ID:
name: ID
sql: CAST(ID as INT64)
CREATED_TIME:
name: CREATED_AT
UPDATED_TIME:
name: MODIFIED_AT
DATE_START:
name: START_AT
DATE_STOP:
name: STOP_AT
这个.yml文件在应用于staging/source_name文件夹中的所有模型时,会将所有ID字段转换为 INT64,并将所有日期列重命名为name键中的值。例如,CREATED_TIME将被重命名为CREATED_AT,DATE_START将被重命名为START_AT。如果没有提供sql,包将只重命名字段。如果提供了sql,包将执行 SQL 并使用name键重命名字段。
dbt-generator transform -m ./models/staging/source_name/ -t ./transforms.yml
这将使用transforms.yml文件转换staging/source_name文件夹中的所有模型。您还可以通过将drop-metadata标志设置为true来删除元数据(删除列从_开始)。--case-sensitive标志将决定转换是否使用区分大小写的名称。
限制
以下是当前版本的一些限制。如果您想投稿,请打开一个问题或拉动请求。
- 转换只适用于由 code-gen 包生成的模型。
- 您不能转换已经转换的模型
- 您不能在转换的字段选择中使用通配符(如
*_id) - 还没有测试
- 还没有错误处理
Streamlit 1.0 刚刚登陆——5 分钟教程
数据科学 web 应用变得简单

照片由阿莱西奥·弗尔兰在 Unsplash 上拍摄
网络应用仍然是任何公司或任何项目的重要渠道之一。原因很简单——web 应用程序是跨平台的,现在几乎不需要学习曲线。对于数据科学家来说,我们没有接受过 web 开发人员的培训,制作 web 应用对我们来说从来都不是最简单的事情。幸运的是,已经有一系列 web 框架可以使 web 应用程序的开发变得更加容易。
其中, streamlit 是一颗冉冉升起的新星,尤其是在数据科学领域,大多数用户将 Python 作为他们的日常工作语言。值得注意的是,streamlit 是完全 Python 友好的,因为您可以仅使用 Python 创建 web 应用程序。
几天前,streamlit 通过发布其官方 1.0 版本达到了一个里程碑,正如其官方博客所报道的那样。随着来自多个投资者的支持越来越多,我们可以预计 streamlit 会发展得更好。
无论如何,本教程是为你提供一个快速入门,我希望你只需要几分钟就可以启动你的第一个 web 应用程序。
不信?让我们把手弄脏吧。
装置
假设您使用 Python 已经有一段时间了,那么您应该熟悉创建虚拟环境和安装 Python 包。我习惯使用 conda 来管理虚拟环境,所以我们从使用 conda 创建虚拟环境开始。在终端中(如果您使用 Mac),导航到您想要放置虚拟环境的目录,并运行以下命令。
- 创建名为
st_web的虚拟环境:
conda create -n st_web
2.激活虚拟环境:
conda activate st_web
3.安装 streamlit 软件包。虽然 conda 可以通过 conda-forge 安装 streamlit,但我遇到了一些问题。因此,我建议使用 Python 的官方包安装管理器pip,运行以下命令:
conda install pippip install streamlit
4.通过简单地找出安装的版本来检查安装。
streamlit --version
如果您发现您的版本不是 1.0.0,您可以通过运行以下命令来更新您的版本:
pip install -U streamlit
创建并启动您的应用程序
在目录中,让我们创建一个名为st_first_app.py的 Python 脚本文件。在该文件中,从以下代码行开始:
import streamlit as st
st.title("My First Web App")
在终端中,可以通过运行:streamlit run st_first_app.py来运行 web app。您将看到 web 应用程序在默认的 web 浏览器中自动弹出。

截图 Web 应用程序
显示数据集
熊猫是许多数据科学家的首选图书馆。作为关键数据结构,DataFrame用于处理类似电子表格的数据。让我们尝试在应用程序中显示一个DataFrame。如果您还没有在虚拟环境中安装 pandas,您可以通过运行pip install pandas来完成。
现在,我们的剧本变成了这样:
显示数据帧的代码
这是显示数据集的 web 应用程序的屏幕截图。

显示数据帧
展示身材
图表是展示信息的最佳方式,因为它们可以让用户对数据集有一个直观的印象。Streamlit 本身提供了不同种类的绘图,如折线图、面积图和条形图。为了简单起见,我们只添加下面几行代码:
st.header("Fare Cumulative")
fare = df['fare'].cumsum()
st.line_chart(fare)
这样,网络应用程序将会显示:

流线图
显示代码片段
有时,您希望显示特定操作的相关代码。您可以向 web 应用程序添加代码片段:
st.header("Code Snippet")
st.code("""a = 100
b = 'text'
c = [1, 2, 3]""")
这样,你会在应用程序上看到一个代码片段。

代码片段
交互式小工具
Streamlit 包括许多常用的 web 小部件,如单选按钮、滑块、文本字段、多选等等。为了简单起见,让我们通过将下面几行代码添加到脚本文件来查看一些小部件。
st.header("Interactive Widgets")
st.radio("Choose One", ["To be", "Not to be"])
st.slider("How old are you?", min_value=1, max_value=100)
st.date_input("Choose your favorite date")
您将在应用程序上看到这些微件。

交互式小工具
复活节彩色蛋
这是我必须提到的事情。你可以认为这是一个复活节彩蛋。只需将以下内容添加到您的脚本中。
st.header("Show Balloons")
your_energy = st.slider(
"Try setting your energy level to 90 or above",
min_value=1,
max_value=100
)
if your_energy >= 90:
st.balloons()
你会看到一堆漂亮的气球!

气球
结论
在本文中,我向您展示了一些可以添加到 streamlit web 应用程序中的常见元素。正如你所看到的,所有的元素都很好地显示在网页上,你不用担心任何元素的布局。重要的是,它可能包括了构建数据科学或机器学习应用程序所需的所有元素。
没试过 Streamlit?是时候了!
感谢阅读这篇文章。通过注册我的简讯保持联系。还不是中等会员?使用我的会员链接,通过支持我的写作。
简化动手操作:增强应用程序用户体验的功能和技巧
通过真实的使用案例和完整的代码示例提升您的 Streamlit 技能

图片来源: Pixabay
介绍
Streamlit 是一个免费、开源的全 python 框架,使数据科学家能够快速构建交互式仪表盘和 web 应用,无需前端 web 开发经验。它于 2019 年首次引入数据科学世界,此后迅速在数据科学从业者和爱好者中流行起来。
我已经使用 Streamlit 几个月了,我的大部分数据可视化工作都是如此,我非常喜欢这个工具,以至于我甚至写了一个初学者教程 Streamlit Hands-On:从零到你的第一个出色的 Web 应用 。在该教程中,我向您展示了有关 Streamlit 的所有基础知识,并通过一个实际操作的示例向您展示了如何构建一个简单的交互式仪表板,以可视化美国各州的房地产市场洞察。
你的学习之旅不应该就此止步。现在,您已经了解了基础知识,并有了一些使用框架的经验,让我们通过学习和应用各种可以极大地增强您的应用程序用户体验的功能和技巧,将您的 Streamlit 技能提升到一个新的水平。
关于我们正在构建的应用程序
在本教程中,我们将构建一个住房市场洞察应用程序(类似于我们在初学者教程中构建的应用程序,但使用县级数据)。在构建这款应用的过程中,我们将详细讨论以下概念/功能,这些概念/功能可以极大地提高我们应用的可用性:
- 使用 st.sidebar 来组织输入小部件,使应用程序界面更加整洁
- 使用 st.forms 和 st.form_submit_button 一起批量输入小部件,并使用一个按钮提交
- 为输入小部件添加工具提示,为应用程序用户提供有用的信息
- 使用 st.expander 将一个容器插入您的应用程序,该容器仅在展开时显示额外信息
- 使用 st.image 将您的品牌标志添加到应用程序中
我们正在构建的住房市场应用程序将如下所示:

图片作者。数据来源: Redfin 数据中心

住房市场洞察应用程序演示(图片由作者提供。数据来源: Redfin
下载数据
转到 Redfin 的数据中心,向下滚动到“它如何工作”部分,并下载“县”一级的区域数据。该数据集包含自 2012 年 1 月以来县级的月度住房市场指标。

从 Redfin 数据中心下载数据(图片由作者提供)
由于我们将在应用程序中创建一个交互式 choropleth 地图,我们还需要获得定义每个县的地理边界的 geojson 文件,以便在地图上绘制县。你可以进入public.opendatasoft.com,向下滚动到“地理文件格式”部分,下载美国县界的 geojson 文件。

图片作者(数据来源:https://public.opendatasoft.com/)
需要注意的一点是,不幸的是,redfin 县级数据集没有县 FIPS 代码列,这是每个县的唯一标识符,稍后我们需要使用它作为与 geojson 文件连接的键。因此,我们需要下载第三个文件,该文件同时包含县名和 FIPS 代码。您可以从nrcs.usda.gov获取数据,并将其复制粘贴到一个. csv 文件中。

作者图片(数据来源:美国农业部)
启动 Streamlit 并准备数据
本教程的目标是通过构建我们在初学者教程中创建的应用程序来探索和学习 Streamlit 的各种功能,因此我们不会花太多时间来解释数据准备部分是如何完成的(例如,使用 @st.cache 进行缓存,为什么以及如何使用 geojson 文件等)。)。这些概念已经在初学者教程中介绍过了。
但是,我在下面的代码中加入了注释,简要解释了代码的作用,以确保您阅读了注释并理解了数据准备过程。

df_final(图片由作者提供)
向应用程序添加边栏
现在我们准备开始构建我们的应用程序!我们将首先向应用程序添加一个侧边栏,其中包含一条欢迎消息“欢迎简化者”。侧边栏功能非常适合在一个部分中组织所有的交互式输入小部件,并且可以扩展/折叠,以允许用户专注于应用程序中的内容。

作者图片
向应用程序添加过滤器/输入部件
我们将在侧边栏部分添加一些过滤器,以便用户可以通过以不同方式分割数据来与我们的应用程序进行交互。这是通过使用各种 streamlit 输入小部件实现的,如 st.selectbox、st.radio、st.slider 等。要将小部件添加到侧边栏部分而不是主界面,我们只需使用 st.sidebar.selectbox、st.sidebar.radio 等。
为了帮助用户更好地理解每个过滤器的含义以及如何使用它们,我们还将通过在 st.sidebar.selectbox() 小部件中包含语法 help='tooltip text' 来为每个小部件添加一个工具提示。

作者图片
现在让我们将这些过滤器/用户输入传递到数据框中,并检查它们是否正常工作。

作者图片
这个看起来不错!需要注意的一点是,每次用户与小部件或过滤器交互时,应用程序都会重新运行并更新数据。如果您只有几个过滤器或者您的数据框不是很大,这可能不是什么大问题。然而,想象一下,当你有一个复杂的机器学习模型,有许多输入小部件和非常大的数据时,这可能会导致糟糕的用户体验。
那么我们如何解决这个问题呢?幸运的是,几个月前 Streamlit 引入了一对名为 st.form 和 st.form_submit_button 的命令来专门解决这个问题。您可以使用这些命令批量输入小部件,并通过单击一个按钮提交小部件值,这只会触发整个应用程序的一次重新运行!
带有提交按钮和表单的批量输入部件
让我们看看如何调整代码来使用表单和提交按钮来批量输入小部件。可以使用带有语句的来声明表单,并且可以在一个表单中包含多个小部件。
以下代码创建了一个包含 5 个小部件和一个名为“应用过滤器”的提交按钮的表单。用户可以随心所欲地与小部件进行交互,而不会导致重新运行。完成选择后,用户应该点击表单的提交按钮来更新应用程序。

作者图片
构建应用程序的主界面
我们现在继续构建应用程序的主界面,我们将在其中添加应用程序的标题、品牌徽标(可选)、扩展器,最后是可视化县级住房市场指标的 choropleth 地图。
添加标题和品牌标志(并排)
St.columns 是一个非常有用的命令,它可以让你插入并排排列的容器。我们将使用 st.columns 创建两列,以便我们可以并排添加标题和品牌标志。
在标题下添加一个扩展器
我们还可以在标题下添加一个扩展器来提供关于应用程序的信息。扩展器可以扩展或折叠,以提供更多的细节,同时节省应用程序的空间,这是一个可以利用的巧妙功能。

作者图片
将 Choropleth 地图添加到应用程序
现在让我们在应用程序中绘制 choropleth 地图。您可以按照下面的代码使用 follow 创建 choropleth 映射。该地图通过月份、财产类型和住房市场指标(例如,销售价格中值、售出房屋、销售与上市比率等)来可视化美国住房市场数据。),取决于用户的输入。
在上面的代码中,第 4 行到第 20 行启动一个空的美国地图,并将地图的默认中心位置设置为(40,-96)。然后,它根据用户在“metrics”参数上的输入创建一个 choropleth 地图,该地图确定每个县的阴影。第 24 到 65 行向地图添加定制的工具提示。第 67 行将 choropleth 地图添加到应用程序的主界面。
需要注意的一件小事是,在代码的第 20 行,我使用了 .geojson.add_to(us_map) 而不是。添加到(美国地图)。这是我发现的一个小窍门,可以隐藏地图中的图例。如果用。add_to(us_map),图例出现在地图中,但比例编号重叠,因此我从地图中移除了图例。
最后,我们将使用 st.columns 布局将一些文本覆盖到 choropleth 地图上。该文本显示在地图的右上角,指示 choropleth 地图显示的指标,具体取决于用户在侧栏的“选择住房指标”过滤器中的选择。这是使用第 2、3、69 和 70 行实现的。

图片作者(数据来源: Redfin )
这就是县级住房市场洞察应用程序!借助 Streamlit 中各种简洁的功能和技巧,它比我们在初学者教程中创建的基本应用程序更加复杂和用户友好。感谢阅读,我希望你喜欢这篇文章。快乐学习!
本教程使用的数据源:
- Redfin 数据中心 : Redfin 月度房市数据——县级。这是由Redfin一家全国性的房地产经纪公司提供的一个开放数据集,你可以免费下载,也可以引用供你自己使用。
- public.opendatasoft.com:美国各县的边界。这是一个由public.opendatasoft.com提供的开放数据集,你可以免费下载。
- nrcs.usda.gov:FIPS 县代码。这是一个由美国农业部(USDA)提供的开放数据集,您可以免费下载。
你可以通过这个推荐链接报名 Medium 来支持我。通过这个链接注册,我将收到你的会员介绍费的一部分。谢谢大家!
简化实践:从零到你的第一个优秀的网络应用
学习核心概念并使用 Streamlit 创建您的第一个 Web 应用程序的分步指南

图片来源: Pixabay
目录
第 1 部分:Streamlit 基础知识
∘ 简介
∘ 什么是 Streamlit
∘ 为什么要将 Streamlit 添加到您的数据科学家工具包中
∘ 如何在 Windows 上安装 streamlit
第二部分:创建你的第一个 streamlit web App
∘先决条件
∘ 启动 Visual Studio 编辑器和 Streamlit
∘ 数据准备
∘ 构建 Streamlit App
∘ 将 Choropleth 地图添加到 app 中
第 1 部分:简化基础知识
介绍
自从我开始使用这个奇妙的基于 python 的框架来构建交互式仪表盘和机器学习 web 应用程序以来,我一直想写一个 Streamlit 教程。在我看来,Streamlit 应该是任何数据科学家工具箱中的必备工具,因为它简单、灵活、健壮且免费。
在本教程中,我将向您简要介绍什么是 Streamlit,为什么我喜欢它并向任何在分析和数据科学领域工作的人推荐它,以及如何在您的计算机上安装它。
安装完成后,我将一步一步地向您展示一个详细的实践示例,通过可视化美国各州的房价,使用 Streamlit 创建您的第一个交互式仪表板。您将通过 python 编写代码来完成创建此 web 应用程序的完整端到端流程,并使用两个可免费下载的开放数据集来简化它。
在本教程结束时,您应该对 Streamlit 的核心概念及其出色的功能有了坚实的理解。您可以将此示例保存为起始模板,以便在将来的项目中重用和扩展。
我们正在构建的仪表板/web 应用程序看起来会像这样。你准备好了吗?我们开始吧!

使用 Streamlit 创建的交互式仪表板/ Web 应用程序(由作者提供)
什么是细流
Streamlit 是一个免费的开源全 python 框架,使数据科学家能够快速构建交互式仪表盘和机器学习 web 应用,无需前端 web 开发经验。如果你懂 python,那么你就可以在几小时内,而不是几周内,使用 Streamlit 创建和共享你的 web 应用。
如果你有兴趣了解更多,你可以查看 Streamlit 网站的图库页面,了解一些由其用户开发的很棒的应用程序:【https://streamlit.io/gallery】T2。
为什么您应该将 Streamlit 添加到您的数据科学家工具包中
我是 Tableau 的长期用户,一直使用 Tableau 作为我的主要工具,为工作中的各种分析任务创建交互式仪表盘和数据可视化。虽然我喜欢 Tableau 全面的分析和可视化功能,但我一直在为自己的分析项目寻找一个灵活、免费和可扩展的替代方案。
当我碰到 Streamlit 的时候,我立刻就爱上了它!这都是 python,所以如果你已经知道 python(像许多数据科学家一样),那么利用 Streamlit 只需几行代码就可以让你的仪表板或数据应用程序启动并运行,而不需要任何前端 web 应用程序开发知识。
最棒的是。免费的!免费的!免费的!重要的事情说三遍!😂因此,如果你是一名自由职业的数据科学家或正在考虑开始自己的分析咨询业务,我强烈建议你探索 Streamlit,并将其添加到你的工具包中,因为它简单、灵活、可伸缩且免费。
对于 Tableau 用户,我也建议你花些时间去探索和学习 Python 和 Streamlit。请记住,开源是一种趋势(就像 Python 在短短几年内取代 SAS 在分析行业的统治地位一样),所以要向前看,做好走在时代前列的准备!
如何在 Windows 上安装 Streamlit
要在 Windows 上安装 Streamlit,让我们先安装 Anaconda(如果您还没有这样做),因为根据 Streamlit 文档,Streamlit 在 Windows 上正式支持的环境管理器是 Anaconda Navigator 。
第一步:安装 Anaconda(如果你已经安装了 Anaconda,跳过这一步)
进入 Anaconda 下载页面并在 Windows 上下载 Anaconda 个人版。关于如何安装和启动 Anaconda 的更多详细说明可以在这里找到。
步骤 2 :使用 Anaconda navigator 设置您的环境
遵循 Anaconda 提供的这些详细说明,使用 Anaconda navigator 来创建和管理您的环境。
步骤 3: 在您的新环境中打开终端(我将我的环境命名为‘geo _ env’)

作者图片
第四步:安装 Streamlit
在打开的终端中,键入以下命令:

作者图片
步骤 5: 测试 Streamlit 安装是否成功
在命令行中键入“Streamlit hello”。如果安装成功,您将会在终端上看到欢迎消息,并且在 web 浏览器上显示 Streamlit 的 Hello 应用程序。

作者图片

作者图片
第 2 部分:创建您的第一个 Streamlit Web 应用程序
先决条件
在我们开始编码和创建我们的应用程序之前,我们需要先做一些准备,包括下载本教程所需的数据集和用于编码的文本编辑器。
本教程需要两个数据集,你可以点击下面的链接下载它们。它们都是开放数据集,可免费用于我们的项目演示目的:
(1):数据集 1 (Redfin 美国住房市场数据):
转到 redfin 的数据中心,向下滚动到“它如何工作”部分,并下载“州”级别的区域数据。在本教程中,为了简单起见,我们将只查看各州的房价,但是您当然可以将可视化扩展到其他地区级别,如城市、县、邮政编码等。

来源: Redfin 数据中心
该文件是一个. gz 文件,如下所示:

作者图片
(2):数据集 2(美国州界 Geojson 文件):
转到public.opendatasoft.com,向下滚动到“地理文件格式”部分,下载美国州界的 geojson 文件。这个 geojson 文件定义了每个州的地理边界/坐标,我们需要用它来绘制我们的 choropleth 地图。
如果你不熟悉 choropleth 地图或者不知道什么是 geojson 文件以及我们为什么需要它,请阅读我以前的文章如何使用 python/folio 创建 choropleth 地图了解更多信息。

geojson 文件(数据来源:https://public . opendatasoft . com/explore/dataset/us-state-boundaries/export/)
(3):用于编码的文本编辑器:
你可以为这个项目使用任何你喜欢的文本编辑器(只是不要使用记事本;不是专门为编码和开发者设计的文本编辑器)。如果你没有,你可以去 https://code.visualstudio.com/的下载/安装微软的 Visual Studio 编辑器。

下载 Visual Studio 代码编辑器(图片由作者提供)
启动 Visual Studio 编辑器并简化
让我们启动 VS 代码编辑器,打开一个新的空文件,将其命名为“my_first_app ”,并将其保存为项目文件夹中的 python 文件(my_first_app.py)。

创建一个新的 python 文件“my_first_app.py”(图片由作者提供)
让我们通过打开 Anaconda Navigator 来启动 Streamlit,单击“Environments ”,然后选择您在第 1 部分中创建的环境,并单击“Open Terminal”。

从 Anaconda Navigator 打开终端(图片由作者提供)
在终端中,首先您需要将您的目录更改为保存“my_first_app.py”文件的目录。然后键入以下命令启动 Streamlit。您将看到一个新的 web 浏览器打开,暂时没有内容。
简化运行 my_first_app.py

从终端启动 Streamlit(图片由作者提供)
在网络浏览器中,单击右上角的菜单图标,然后单击“设置”。在“Settings”下,让我们选中“Run on save ”,这样每次我们在 VS code editor 中进行代码更改并保存时,更改都会自动反映在 Streamlit web 应用程序中。

Streamlit 设置(图片由作者提供)
数据准备
让我们在 VS 代码编辑器中键入以下代码,导入本教程所需的所有包,并通过只选择少数感兴趣的字段来准备我们清理的数据集。之后,我们将清理后的房屋市场数据集与 geojson 文件合并。
在上面的代码中,需要注意/记住一些重要的事情:
首先,streamlit 从上到下读取并执行您的脚本。每当代码发生变化或用户与应用程序交互时(例如应用过滤器),Streamlit 会从头到尾重新运行整个 Python 脚本。
其次,Streamlit 提供了一种缓存机制,允许您的应用程序在加载、读取或操作大量数据时保持高性能,这些数据可能会导致昂贵的计算。这是用@st.cache 装饰器完成的。
例如,在我们的案例中,如果没有缓存,每次用户与应用程序交互并且应用程序被刷新时,数据都会被重新加载和重新计算,这可能会使应用程序运行缓慢,并导致糟糕的用户体验。通过缓存,数据只需加载到应用程序中一次。下次调用缓存的函数时,如果函数输入没有任何变化,Streamlit 将跳过函数的执行,而是返回之前存储在缓存中的输出。
第三,可以使用 st.write()向 app 写入参数。您想要传递给应用程序的参数可以是很多东西,比如数据框、图表、字符串、函数等。在上面的代码中,我们将 df_final 数据帧写入应用程序,以便查看最终数据的样子。
当您保存代码时,一个代表 df_final 的表格将出现在您的应用程序中。在你看完数据并对所有字段有了很好的理解之后(你可以在这个页面中参考 Redfin 的数据字典),你可以注释掉 st.write()命令,因为我们不打算在我们的应用程序中显示这个表。

用于创建 Choropleth 地图的已清理数据(图片由作者提供)
构建 Streamlit 应用程序
现在我们已经准备好了数据,让我们开始使用 Streamlit 创建我们的应用程序。非常刺激!
首先,让我们在应用程序中添加一个侧边栏,显示一些关于这个应用程序的介绍性信息或者免责声明。我们还将在应用程序的主界面中添加标题和副标题。
保存代码后,您将看到您的 web 应用程序也自动重新运行/更新,如下所示:

作者图片
其次,让我们添加一些过滤器,让用户能够灵活地对数据进行切片和切块,并从不同角度生成见解。我们希望将这三个过滤器并排放在同一行中,因此,我们使用下面的代码创建三列。

作者图片
到目前为止,这个看起来很不错…但是等等!当用户在这些过滤器中选择不同的选项时,Streamlit 如何接受这些输入并相应地过滤数据?例如,如果用户想查看每个州基于最近一个月(2021–09–01)的销售价格中值,并且只查看单户住宅,我们如何将这些过滤器传递回我们的数据框?
我们可以使用下面的代码,根据用户的选择更新 df_final。然后,您可以将数据框写入应用程序,尝试更改过滤器并检查它们是否正常工作(确保在完成检查后注释掉 st.write)。
将 Choropleth 地图添加到应用程序
现在我们的数据已经准备好了,让我们在应用程序中绘制 choropleth 地图。请参考我的上一篇文章如何使用 python/leav 创建 choropleth 地图来更详细地理解关于使用 python/leav 创建 choropleth 地图的代码和步骤。
*
让我们使用下面的代码来创建 follow map,它根据用户的输入,按不同的时期、财产类型和评估指标来可视化美国住房市场数据。
在代码中,第 2 行和第 3 行初始化一个空的美国地图,并将地图的默认中心位置设置为(40,-100)。第 6 行到第 18 行根据用户在三个过滤器上的输入创建 choropleth 映射。请注意第 10 行,它通过从第三个过滤器传递的“metrics”参数来确定每个状态的阴影。

Choropleth 地图(图片由作者提供)
这个看起来不错!然而,如果我们还能在用户悬停在地图上时显示每个州的一些信息,那就更好了。因此,让我们通过在上面代码的最后一行 folium_static(m)之前插入以下代码来为地图添加工具提示。
这就对了。您已经使用 Streamlit 创建了第一个交互式仪表板/web 应用程序。玩玩它,想想如何扩展它的功能,或者给它添加更多的功能。您也可以阅读我的文章Streamlit hand-On:增强应用程序用户体验的功能和技巧 ,了解增强该应用程序可用性的其他功能和技术。
祝贺你完成这个项目,我希望你喜欢这个教程。快乐学习!

作者图片
你可以通过这个推荐链接注册 Medium 会员(每月 5 美元)来获得我的作品和 Medium 的其他内容。通过这个链接注册,我将收到你的一部分会员费,不需要你额外付费。谢谢大家!*
罢工和排水沟,起起落落
(不是关于杰夫·布里吉斯;但是伦敦桥)

澄清一下——这不是伦敦桥,而是塔桥。(照片由 Unsplash 上的 Gabriel Kraus 拍摄)
好吧,也许这是一篇关于伦敦桥梁高度限制的简短博客的标题,有点误导;我还能怎么让你读这个?
我偶然发现了一个有趣的伦敦交通局(TFL)数据集,名为“桥梁高度限制”,如下:
https://blog.tfl.gov.uk/2019/10/09/data-drop-ordnance-survey-data-bridge-height-restrictions/
根据该网站,整个伦敦平均每月有两起桥梁撞击事件。在这种背景下,大伦敦边界/M25 内有 877 个结构,包括矮桥、隧道和路障。粗略的数据分析表明最常见的类型的桥梁限制在 4.6 米和 5.1 米之间;

最常见的桥梁类型在 4.6 米和 5.1 米之间(由作者可视化)
探索性数据分析
这就是为什么我需要借用一个大的勒波斯基的报价来引起你的注意;TFL 数据集可以产生更多的见解,但一些数据清理是必要的。请随意跳到下一部分。
在这个阶段,值得强调的是,如果没有 OSMNX,这篇博客中的分析是不可能的;这是我以前曾经用过的一个包,因为它在这个数据集的上下文中有很多可能性,所以我又回到了这个包中。阅读此处的文档:
下面是清理数据集的几行代码(数据集包含大量重复信息):
利用一点当地知识将伦敦行政区分为北部和南部(作者代码)
按行政区划分的限制分布
利用清理后的数据集,我们可以构建一个交互式地图,邀请用户悬停并点击伦敦的各个区,以直观地检查每个区的高度限制分布情况。当用户将鼠标悬停在每个区的每个高度限制类别上时,“计数”参数会给出该类别在该区出现的频率。当然,整个数据集是根据特定的行政区是在河的北面(还是南面)来划分的。
浏览图表(作者可视化)
这些身高限制都去哪了?
使用一个交互式的叶群地图,我们可以看到大伦敦地区的所有高度限制。将鼠标悬停在每个标记上并点击,应会产生有关限制类型的更多信息,以及有关该限制生效的道路名称或道路编号(如果有)的信息。
我的路线上有什么限制吗?
我们并不是每天都坐在双层汽车或重型货车的方向盘后面,但是如果我们坐了,我们会比现在每天更加注意桥梁高度的限制。作为该分析的一部分,我们编写了一个简单的函数,它绘制了大伦敦两点之间的最短驾驶路线,其中也标出了可能与 HGV 驾驶员相关的所有高度限制。代码如下所示:
现在我们有了这个函数,我们可以沿着随机选择的三条路线开车;一条在河的北面,一条在河的南面,最后一条(刚好)穿过河。实际路线是红色的,而标记表示可能是适用的限制。某些面向 HGV 驾驶员的导航系统可能已经有了这种功能,所以用现有的数据从头开始构建这种导航系统(至少对我来说)很有趣。
3 条穿越伦敦的随机路线(作者可视化)
结论
很容易忘记数据集是关于桥高限制而不是桥的位置。虽然限制的存在是桥梁存在的合理替代,但情况并非总是如此。
如果您还记得,数据集中记录的最高净空水平与 4.6 米至 5.1 米之间的车辆限制有关;该数据集实际上对任何潜在的高于 5.1 米的净空没有任何限制。这就是为什么在哈格斯顿和伦敦桥之间的第三条路线上,伦敦桥的火车隧道(对你们伦敦人来说,这是一个人刚刚穿过泰晤士河到达博罗市场之前的一段)几乎没有记录。这是因为它们高得令人难以置信,可能超过 5.1 米(我还没有检查过,下次路过时我会检查的)。
这是一个(有趣的)数据限制——它描绘了限制条件,而不是实际的桥梁。如果一个人驾驶一辆超过 5.1 米高的汽车,这可能会产生一些有趣的结果;但是任何超过这个高度的交通工具都应该有自己的博客。
使用 FuzzyWuzzy 库比较字符串很容易
了解用于字符串比较的 FuzzyWuzzy 库——它如何工作以及何时有用

安娜斯塔西娅·罗曼诺娃在 unsplash 上拍摄的图片
大约一年前,我看到我的一个同事在处理一个大型数据集,旨在测量每对字符串之间的相似度。我的同事开始开发一种计算字符串之间“距离”的方法,并提出了一套规则。
自从我熟悉了FuzzyWuzzypython 库,我知道有一种更快更有效的方法来确定一对字符串是相似还是不同。不需要繁琐的计算,不需要重新发明轮子,只需要熟悉 FuzzyWuzzy,以及我今天将与你分享的一些其他技巧。
什么是字符串比较,FuzzyWuzzy 有什么帮助?
FuzzyWuzzy 是一个 Python 库,它计算两个给定字符串的相似性得分。
相似性分数以 0(完全不相关)到 100(非常匹配)的范围给出。
毕竟,如果您需要的只是精确的字符串比较,Python 可以满足您:
>>> **"check out this example" == "check out this example"** True>>> **"check out this example"** == **"something completely different"** False
但是,如果您的字符串不一定完全相同,但您仍然需要知道它们有多相似,该怎么办呢?
>>> **"check out this example"** == **"check out this exampel"** False
这不是很有帮助。
但是看看这个:
>>> fuzz.ratio("**check out this example"**, "**check out this exampel"**)
95
好多了。
入门指南
如果您的虚拟环境中没有安装相关的库,并且在使用之前没有导入,您将需要运行以下程序:
在命令行中:
pip install pandas
pip install fuzzywuzzy
***# or — preferably***pip install fuzzywuzzy[speedup]
***# [speedup] installs python-Levenshtein library
for better performance***
在您的 ide 中:
>>> import pandas as pd
>>> from fuzzywuzzy import fuzz
《距离》——模糊的幕后
不同的 FuzzyWuzzy 函数使用 Levenshtein 距离——一种计算两个字符串之间距离的流行算法。这些字符串彼此“越远”,距离分数就越大(因为反对模糊不清的输出)。
然而,Levenshtein 距离有一个主要的缺点:
它有一个逻辑,而在现实生活中有几种不同的方法来定义字符串的相似性。
Levenshtein 距离无法用一条逻辑覆盖不同的情况。有些情况会被人类定义为相似的字符串,但被 Levenstein 遗漏了。
例如:
>>> Levenshtein.distance("**measuring with Levenshtein",
"measuring differently"**)
11>>> Levenshtein.distance("**Levenshtein distance is here",
"here is distance Levenshtein"**)
17
如果有人必须决定哪对字符串更相似,他们可能会选择第二个选项。但是,我们可以看到它实际上获得了更高的距离分数。
我们需要更好的东西。
了解这些功能
FuzzyWuzzy 有一些不同的功能。每个函数都接受一对字符串,并返回一个 0-100 的匹配分数。但是这些函数中的每一个都有稍微不同的逻辑,所以我们可以选择最合适的函数来满足我们的需要。
让我们来了解一些突出的模糊不清的函数:
- fuzz . ratio:T2 最简单的函数。基于以下逻辑计算分数:
给定两个字符串,其中 T 是两个序列中元素的总数,M 是匹配的数目,这些字符串之间的相似性为:
2(M / T)100
>>> fuzz.ratio("**great"**, "**green"**)
60
在本例中-
T = len(" great ")+len(" green ")= 10
M = 3
,因此公式为 2(3/10)100
- fuzz.partial_ratio:
让我们看看下面三个例子,然后讨论它们:
>>> fuzz.partial_ratio("**let’s compare strings"**, "**strings"**)
100
>>> fuzz.partial_ratio("**let’s compare strings"**, "**stings"**)
83
>>> fuzz.ratio("**let’s compare strings"**, "**strings"**)
50
以上示例演示了以下内容:
1 .B 中的字符串完全包含在字符串 A 中,因此匹配分数为 100。
2。除了一个“错别字”,B 中的字符串几乎完全包含在字符串 A 中,因此匹配分数是 83。
3。与第一个示例相同,但是在 A 和 B 之间使用了 ratio 函数,而不是 partial_ratio。因为 ratio 函数的目的不是处理子串的情况,所以分数较低。
- fuzz.token_sort_ratio 这将为字符串 A、B 检索 100 的匹配分数,其中 A 和 B 包含相同的标记,但顺序不同。
看下面的例子,一次用 token_sort_ratio 函数,一次用 ratio 函数,看看不同的结果。
>>> fuzz.token_sort_ratio("**let's compare strings"**,
"**strings compare let's"**)
100
>>> fuzz.ratio("**let's compare strings"**, "**strings compare let's"**)
57
同样的令牌但是不同的计数怎么办?
>>> fuzz.token_sort_ratio(**"let's compare", "let's compare compare"**)
76
这不是这个函数的专长。在这种情况下,我们有 token_set_ratio 来拯救我们。
-
fuzz.token_set_ratio
我们可以看到下面的两个例子得到了相同的匹配分数,尽管在第二个例子中一些记号被重复了,并且它们的顺序被改变了。
>>> fuzz.token_set_ratio("**we compare strings together"**,
"**together we talk"**)
81
>>> fuzz.token_set_ratio("**strings we we we compare together"**,
"**together together talk we"**)
81
在将稍微简化之后,这个函数背后的逻辑如下:
给定字符串 A,B (让 A = 'hi hey ho '和 B = ' yo yay ho '):
C = A 和 B 的交集(' ho ')
D = C+A 的余数(' ho hi hey ')
E = C+B 的余数(' t
完整的逻辑包含额外的规范化,比如对 A 和 B 应用 set(),对 C 应用 sort()等等。
由上述每个函数实现的代码,以及其他有用的 FuzzyWuzzy 函数,可以在这里找到。
触及本质
在本文中,我们介绍了 FuzzyWuzzy 库及其功能的基础知识。
然而,为我们的项目获得最好的结果并不是从知道哪些功能可用以及如何使用它们开始和结束的。
像专业人士那样做意味着知道如何在处理数据之前清理数据,如何不仅比较成对的字符串,还比较大的表格,使用哪个(或哪些)阈值分数等等。
为了了解所有这些,我邀请你阅读名为 FuzzyWuzzy 的连续文章——之前和之后。
SQL 第 1 部分中的字符串
使用 SQL 字符串技术从字符列中提取有意义的信息

费德里科·布尔加拉西在 Unsplash 上的照片
介绍
读完这篇文章后,我们应该更有信心提取包含在字符变量中的数据洞察力。一个字符变量或字符串可以用文本值来表示,比如一个人的;姓名、工作或地址。所使用的许多 ML 模型需要数字格式的特征来提取含义。使用字符串操作技术,可以清理数据并将其转换成所需的格式。
本文中显示的 SQL 代码是使用 Microsoft SQL Server Management Studio 18 的一个实例创建的。所使用的每个示例都旨在强调可用的不同 SQL 字符串函数的基本概念。
输入数据
对于这段分析,IBM HR 分析数据集的一个样本取自 Kaggle。在这个数据集中,有许多字符变量可用。
https://www.kaggle.com/pavansubhasht/ibm-hr-analytics-attrition-dataset
查林德克斯
字符索引方法可用于检查字符列中是否存在字符串值。
使用 charindex 方法定位表达式的 SQL 代码 1.1
在上面显示的 SQL 代码中,我们在该方法的第一个应用程序中搜索值“sales”。字符串搜索显示该值不区分大小写,因为搜索的列中的实际值显示的是 camel 大小写格式。当筛选子句应用于数据集时,查询结果中将只显示与筛选表达式匹配的值。

SQL 输出 1.1 来自对 JobRole 列的 charindex 检查
如上面的输出所示,已经搜索的两个字符串值将返回表示第一个字符的索引位置,该字符与正在搜索的整个字符串值相匹配。如果字符串值不存在,则将返回零值。我们可以在第二行中看到,列值“代表”与搜索的“执行”值不匹配,因此返回零。
PATINDEX
当检查包含在列中的模式时,模式索引方法是合适的。
SQL 代码 1.2 标识 JobRole 中的模式
通过使用百分比符号,我们为该方法提供了一个通配符。正是这个通配符允许在要搜索的模式之间存在任意数量的其他值。

SQL 输出 1.2 匹配模式的职务范围
结果突出显示,在找到第一个字母“b”之前存在两个值,然后在第二个字母“y”之前存在六个值。
SQL 代码 1.3 检查要搜索的模式索引的值列表
在对 job role 列的检查中,我们寻找包含在 string 列中任意位置的值的列表。

SQL 输出 1.3 字母 x 似乎是模式列表中最常见的
从 SQL 输出中可以看出,在这个搜索中只识别出了字母“x”。然而,如果列表中的任何或所有字母出现在该行中,那么将显示这些结果。
左侧和右侧
这两种方法可用于返回从字符串开头或结尾开始的字符串部分。
SQL 代码 1.4 从开头和结尾提取字符串的元素
通过选择 left 方法,将返回字符串开头的前三个字符值。

SQL output 1.4 返回的字符串元素
而使用正确的方法,则返回最后三个字符值。如果字符串的开头或结尾是您分析的起点,这些方法中的每一种都有助于返回所需的结果。但是,如果需要另一个起始位置,那么使用 substring 方法会更实用。
子链
能够识别所需字符串的起始位置和长度位置是 substring 方法的核心。有了这两个位置参数,在检查字符串变量时就有了更大的灵活性。
SQL 代码 1.5 创建字符串逻辑来测试方法
使用已经声明的句子变量,可以提取许多项。使用 substring 方法和索引中的起始值和长度值,用户可以找到两个水果项。由于子字符串索引从 1 开始,并且长度位置参数是包含性的,因此可以返回位置参数值为 1 和 6 的“Apples”。

SQL 输出 1.5 从创建的字符串中提取水果项目
通过将起始位置值从索引扩展到适当的位置,已经识别出第二个水果。在这个例子中,长度位置指的是所需水果的长度。如果没有包括长度位置参数,那么查询结果中将返回起始位置之后的所有字符串值。
替换
在识别出我们感兴趣的字符串值之后,可能会有需要调整这个字符串值的时候。对于此任务,可以使用替换方法。
SQL 代码 1.6 调整字符串中的符号
在 department 列中,找到了&符号,但需要将其改为“and ”,而不是“&”符号。方法中的参数是第二个参数的旧项和第三个参数的新项。

SQL 输出 1.6 返回带有 new_name 列的字符串的扩展版本
随着结果的突出显示,新的部门名称已按预期创建。这是一个小例子,说明了在识别可以转换成另一种格式的文本时可以实现的功能。
结论
可以从包含在文本列或要素中的数据中获得许多附加信息。在本文中,展示了许多不同的方法来帮助这个提取过程。每种方法都强调了理解字符串的索引是如何为创建新功能提供机会的。
在下一篇文章中,我们将继续探索可以使用的不同 SQL 字符串方法。
非常感谢您阅读
[1]:ka ggle dataset IBM HR analytics attraction dataset 来自【https://opendatacommons.org/licenses/dbcl/1-0/】https://www . ka ggle . com/pavansubhasht/IBM-HR-analytics-attraction-dataset,许可协议为
兼职数据科学硕士课程如何改变我的生活
加入加州大学伯克利分校(MIDS)的非全日制研究生院影响了我生活的方方面面。以下是我如何试图在这一切中获得成功(和理智),并最终将我的职业生涯转向数据科学。
两年多来,我一直在兼职做加州大学伯克利分校 MIDS 分校(信息和数据科学硕士)项目的研究生,同时做全职工作。加入该项目一年后,我获得了第一份数据科学工作,两年后,我又获得了另一份工作。这不是没有血,汗水和眼泪,我做了一些艰难的权衡,让 MIDS 为我工作。我想和你们分享这个项目是如何影响我的日常生活和职业生涯的。如果你正在考虑 MIDS 或类似的非全日制研究生课程,我希望能帮助你做出明智的决定,踏上数据科学之旅。
(另见 )加州大学伯克利分校 MIDS 分校值得吗? “为什么我选择这个项目来推动我的职业生涯。)

虚拟快乐时光、zoom 上的宠物客串、分组会议……在 WFH 和在线教育因 COVID 而变得很酷之前,我们已经在线教育了很长时间
剧透警告:很难
没有糖衣它。由于各种各样的原因,一边全职工作一边兼职学习可能会很难。我最大的挑战是,作为一名前风险顾问和欺诈调查员,我需要弥合数据科学职业道路上如此广泛的技术技能差距(点击阅读更多关于我的 MIDS 之路的信息)。这在一定程度上抵消了我的日常工作要求不高,我不需要照顾任何人的事实。对于一个有两个孩子和一个工作配偶的软件工程师,或者一个每周工作 70 小时的精算师来说,挑战和优势看起来会有所不同。以下是我在 MIDS 实现工作、生活、学校平衡并取得成功的方法。
- 对你重要的事情进行优先排序。随着情况的变化,不要忘记重新审视优先事项。
- 做出有目的的取舍。你不能拥有一切,至少不能同时拥有。
- 休息一下。这是马拉松,不是短跑。每隔一段时间充电,平衡工作量。
- 继续前进。关注奖品,也就是你加入该计划的原因。有了 MIDS 提供的所有资源,如果你继续朝着你的目标前进,你会得到回报的。
在下面的部分中,我将详细描述我是如何应用这些原则的。

在我追求教育的过程中,我以笔记本和贴纸的形式杀死了太多的树木——照片由费伦茨·霍瓦特在 Unsplash 上拍摄
投入时间
首先,这是我这些年学术活动的概述。小时/周栏代表我每周在每项活动上花费的大致时间。



没人在乎研究生成绩(说真的!),但我确实如此——主要是因为成绩给了我继续前进的动力,给了我学到东西的自我满足感。

准备餐点是一种生活救星,直到它不是。另一方面,罐装金枪鱼……作者插图。
生活方式调整
我在学校花了很多时间。在许多周末,我每天学习 8-10 个小时(或者当我做助教的时候评分)。从周一到周四,我试着每晚花 1-2 个小时。如果作业到期,这些时间会增加。我把星期五留给了一点酒和电视。“这很好,”我想。“反正我只会浪费时间,所以我还不如学习,提高效率。”
尽管工作量很大,但当我第一次开始这个项目时,一切似乎都很容易管理。我在上课的日子里大批量做饭,准备即食餐。我计划了我的工作会议,以便我能及时回家参加现场会议。只要我能预测到,我就会在作业到期日或其他繁忙的星期提前安排我的 PTO。我对我的同事是透明的,他们理解我。我仍然试图优先考虑与我的伴侣共度美好时光,他非常支持我的学术追求。相反,我没有参加生日聚会,这样我可以在周末完成作业。
然而,这种生活方式是不可持续的。第一年结束时,我已经精疲力尽了。
为什么我投入了这么多时间
起初,我害怕自己会落后。在基本知识很少的情况下,个人努力是我唯一能控制的事情。这些小时是我弥补技能差距所需要的最低限度。但是后来,一旦赶上了,就是解决问题的快感和随之而来的成就感。因此,尽管有一些疼痛,我最初并没有感到筋疲力尽。相反,我很受鼓舞,因为我看到了我的努力和学习成绩之间的直接联系。结果,我愉快地投入了时间。例如,我尽可能多地在办公时间提问或倾听。我的默认做法是虔诚地完成所有指定的阅读材料和视频,这对于通过这门课来说可能有点过头了,但它确实有利于我的成长和自我满足。
重新排列优先次序,仍然筋疲力尽
2020 年春天,我的第三个学期,我修了两门中级课程:应用机器学习(W207)和实验与因果推断(W241)。在一个班里玩 Python,在另一个班里玩 R,在一个班里玩机器学习,在另一个班里玩统计学,我都快疯了。那个学期的第五周是我在 MIDS 的整个职业生涯中最糟糕的一周,因为两门课的作业都要交了。
然后,COVID 锁定在美国开始。每个人的生活都天翻地覆,我也不断地被压力困扰。我不再有精力像以前那样百分之百地投入到学校、生活和工作中。我必须分清主次。我知道我想面试需要实验技能的工作,所以我决定继续在因果关系课上努力,但只做 ML 课指定阅读的一部分。因此,我没有从 ML 课程中获得我想要的那么多。这对我来说没什么,因为我无法让自己再努力了。
当学期结束的时候,我已经筋疲力尽了,而且我的学位只完成了一半。我决定下学期休学。

作者插图。
休息一下
必须磨快斧子才能继续砍
为了让自己坚持下去,我战略性地休息了一下,就好像我的生存有赖于此。我休学,专注于我的家庭和事业。我暂停工作,专注于我所爱的人和学业。不可能同时拥有这一切,但我通过有目的的权衡让它长期发挥作用。
休学一学期
例如,有一年,在那个充满压力的学期之后,我决定在 2020 年夏天不上课。我知道许多学生这样做是为了充电,因为全职工作之外的 MIDS 令人疲惫不堪。每个学期持续 14 周,然后是两周的假期(12 月三周),后半学期你用来准备下学期的第一周。几乎没有足够的时间喘口气。秋季和春季学期包括感恩节和春假中间的一周假期,但这些总是在一眨眼之间蒸发掉。
虽然我休学了一个学期,但我的余生仍在继续。我想换工作,结果花了大量时间面试。在短暂的中断之后,我又开始了演讲。我很感激有机会在暂停学业的同时推进我的事业,尽管我觉得没有得到充分休息。
很快,新学期开始了。我得到了我的第一份 DS 工作,在优步数据科学团队担任产品分析师,这正好赶上了一门要求特别高的课程,叫做深度学习的自然语言处理(W266)。虽然我的工作时间并不是不合理的,但在新的工作职能和领域中加速让我筋疲力尽,每天下班后都让我更加疲惫不堪。我不再有休更多学期的奢侈,但我有一个秘密武器。

作者插画。
调整课程量
我还没有上过数据工程基础(W205),这是一门必修课。学生们通常会在项目的早期参加考试,并以自己的方式解决问题,但我推迟了,因为我打算参加弃权考试,并用选修课代替。数据工程以工作量较轻而闻名,我已经熟悉了一些材料。最初,我认为简单的课程不值得我花费时间和金钱。然而,为了我的心理健康,我决定参加一个简单的课程并在毕业前获得相同数量的学分会有很多好处。
这学期是有史以来最好的学期之一。我又有了自己的生活。低时间承诺允许我在一天内烹饪多种新鲜的食物,并花时间与家人和朋友在一起。在疫情呆了一年后,我终于有了一个很酷的检疫爱好(皮划艇)。我低调地更加努力地工作,因为尽管我在这个领域的任期很短,但我想成为一名出色的 DS。尽管我学到了一些额外的东西(学习小组、演讲和再次面试),这学期还是给了我急需的精神喘息的机会。
工作之间的有意中断
几个月后,我在脸书找到了一份新工作。我知道开始一份新工作令人疲惫不堪,我的班级“大规模机器学习”(W261)是 MIDS 最难的班级之一。因此,我尽可能晚地开始工作,并在两次工作之间休息了 6 周。这段时间我和我爱的人在一起。我仍然把假期的很大一部分用来在学校取得进步,这样我就可以充分利用课堂,防止在开始新工作时不知所措。

我从我的同学那里学到的和从官方学校材料中学到的一样多——图片来自 Unsplash
建立友谊
我选择 MIDS 项目的主要原因之一是扩大我的人脉。我很快意识到,网络也可以满足我的日常社交需求,这是我以前在规划工作-生活-学校平衡时忽略的一个领域。当你不断拒绝聚会和晚餐邀请去学习时,这对你的人际关系和心理健康都有影响。我很感激找到了一群新朋友,与他们分享我在 MIDS 的经历。在 COVID 之前,我每学期都会和当地学生一起吃几次午饭。一旦地方开始开放,我们感到足够安全可以见面,我就和个人或小团体出去吃户外餐或去划独木舟。
学习小组是与学生和校友交朋友的一种特别神奇的方式。所以我把它当成了我的事情。每学期都为班级创建学习小组 Slack 频道,公开邀请大家。有时我加入课外学习小组,如面试准备和编码实践。学习小组成了我在 MIDS 学习经历和社交生活中必不可少的一部分。我们通常每周在 Zoom 上见面一个小时,互相教导,分享资源,建立联系。我和一些正式会员成了亲密的朋友,以至于即使我们住得很远,我们也会在现实生活中一起出去玩或者互相问候。在整个隔离期间,我从未感到孤独,因为我通过我的学校朋友获得了健康的人际交往。
在接下来的 2-3 个月里,我每周都和一小群朋友进行模拟面试,主题包括统计、编码、商业案例和软技能。
改变我的职业
没有免费的数据科学工作
为了确定一个专业项目是否值得你花费时间和金钱,你不仅应该通过课程质量,还应该通过职业成果来定义成功。我必须在一个全新的功能(数据科学)和领域(产品)中驾驭就业市场,我必须说服潜在的雇主,尽管缺乏直接相关的经验,但我应该得到这个机会。此外,我需要修改一切:我的简历、LinkedIn 简介、电梯推销和面试技巧。在我的休学期间没有课要上,我每周花 10-15 个小时致力于我的事业。MIDS 网络派上了用场。
利用 MIDS 的网络和资源
我花了一些时间单干,比如润色我的简历或者研究空缺职位。我剩下的时间都花在了与 MIDS 社区的交流上。我素未谋面的学生和校友主动提出查看我的简历,并在电话上和我聊了一个小时,说他们也接受了帮助,他们只是向前支付。顶级科技公司的员工和其他人只是闲聊,他们很乐意为我推荐。我看到推荐的回拨率比直接申请高得多。我的推荐人也支持我,并提供了关于招聘流程和公司文化的内幕消息。
MIDS 通过提供技术教育使我成为一个更强有力的候选人,但仅仅通过简历和招聘人员的筛选还不够。每个雇主都在寻找现实生活中的技能和品质,而这些在(虚拟的)教室里很难获得。因此,我在 2 到 3 个月的时间里,每周与一小群朋友一起练习模拟面试,主题包括统计、编码、商业案例和软技能。每次面试后,我都会做详细的笔记,并和我的模拟面试小组一起听取他们的汇报。由于我缺乏自己专业领域之外的工作经验,听取朋友们的见解有助于我发展自己的商业直觉。当我在优步交通数据科学团队获得第一份 DS 工作时,我所有的努力终于得到了回报。
不到一年后,脸书招兵买马。我把那学期的学习成绩放在次要位置,这样我就可以尽我所能为脸书的面试做好准备。我每周花几个小时练习案例研究和采访。最终,脸书邀请我担任产品分析部门的数据科学家,在这个过程中我得到了晋升。这一次换工作带来的薪酬增长将在几个月内弥补我在 MIDS 的全部学费。

你是在跑马拉松,而不是短跑。—照片来自 Unsplash
不要停留在问“MIDS 给我带来了什么好处?”相反,你还应该问,“我准备好利用 MIDS 来实现我的目标了吗?”
要点:没有付出,就没有收获
通过对我的生活方式进行重大调整,并做出战略性的权衡,我得到了我来这里的目的。正如我所说,旅程并不容易,生活可能会给你带来意想不到的变化(COVID,婴儿,国际行动,等等。).然而,如果你分清轻重缓急,做出取舍,休息一下,继续朝着你的目标前进,我相信你会在生活中找到一个合理的平衡,也会有一个伟大的职业生涯。
MIDS 大学的学费将近 80,000 美元(不包括任何奖学金和赞助),但这并不能保证什么,除非你为此努力。当然,它提供了很棒的课程,但是你必须学习才能把知识变成你的。它拥有一个很大的校友网络,但你必须建立这些关系。这会让你的简历从一堆简历中脱颖而出,但是你必须展示出雇主想要的品质。不要停留在问“MIDS 给我带来了什么好处?”相反,你还应该问,“我准备好利用 MIDS 来实现我的目标了吗?”
收场白
我的朋友开玩笑说,现在我有一份很棒的数据科学家工作,我不需要完成我的学位,我应该只剩下一个学期了。当然,我会完成我开始的工作。在我在顶点的最后一个学期,有更多的友谊要建立,新的想法要见证。此外,当我完成学位时,我的旅程并没有结束。他们称毕业典礼为“毕业典礼”是有原因的。
结构方程建模
结构方程建模的全面概述,以及使用 R 和 Python 的示例的完整演练

结构方程建模——照片由朗达克拍摄,佛罗里达州本地民间艺术家在 Unsplash
什么是结构方程建模?
结构方程模型是解释测量变量和潜在变量之间的关系、潜在变量之间的和关系的模型。潜在变量是作为人类,我们理解为一个概念的变量,但是不能直接测量。
无法直接测量的潜在变量的一个很好的例子是智力。我们有很多学校考试、智商测试、心理测试来衡量像智力这样的概念,但它们总是归结为:
- 在多个领域的多项选择问卷上测量分数
- 将这些分数转换成对智力的评估
所以你需要一个模型来将测量变量(考试分数)转化为潜在变量智力。这就是结构方程建模作为一种最好的工具来评估这些关系的地方!
何时使用结构方程建模?
那么,什么时候用结构方程建模呢?让我们讨论一些考虑因素,看看结构方程建模是否适合特定的用例。
- 如果你想使用结构方程建模,在你的用例中有一个测量和潜在变量的概念是很关键的。结构方程模型的目标是模拟测量变量和潜在变量之间的关系,或者多个潜在变量之间的关系。如果你想使用结构方程建模,你必须尝试识别重要但不可测量的潜在概念。
- 其次,结构方程建模主要应用于确认和测试方法。正如您将在本文后面看到的,您通过定义您心中的关系假设来开始建模阶段。例如,如果你做一个智力模型,你需要从识别你认为可能影响智力的不同的测量和潜在变量开始。
- 因此,结构方程建模不适合作为纯粹的探索工具。如果你还不知道你的用例之间的关系是如何联系起来的,你最好使用其他的技术来探索潜在的可变问题。探索性因素分析在这种情况下是一个很好的选择。
- 结构方程模型非常适合分析。它们帮助你了解许多影响潜在现象的不同概念。然而,结构方程模型在预测性能方面并不总是有效的。如果你纯粹是为了预测性能而不是理解和解释,结构方程建模可能是错误的选择。
- 结构方程模型根据变量之间的假设关系给出了系数的估计值。除了您指定的关系之外,找不到其他关系。使用结构方程模型的一个很好的方法是提供多个假设模型,对每个模型进行估计,然后分析它们之间的差异,以朝着越来越好的模型努力。
结构方程建模的类型
有不同类型的结构方程模型。在我们进入其中两个的代码实现之前,让我们先讨论一下 SEM 模型的一般情况。
如果我们查看官方定义,我们将不得不承认许多模型是结构方程建模的类型。例如,可视为结构方程建模类型的模型包括:
- 验证性因素分析
- 验证性复合分析
- 路径分析
- 偏最小二乘路径建模
- 潜在增长建模
然而,在本文中,为了简单起见,让我们不要进入这些模型的细节,并保持结构方程模型的范围是具有以下特征的模型:
- 测量变量
- 潜在变量
- 变量之间的假设关系
结构方程建模示例
我们将使用一个关于工作表现的结构方程模型的例子。这个项目最重要的变量是 工作绩效 。在我们的例子中,工作绩效是一个潜在变量,因为它不可能直接测量。注意:一些工作场所使用非常明确的 KPI 来定义工作表现,它可能不是一个潜在的变量。
在我们的案例中,考虑一项办公室工作,其中工作绩效基于三个测量变量进行评估:
- 客户满意度:你的主要客户给你的满意度评分,在 1 到 100 分之间
- 超级评估员:你的上司对你工作表现的评估,介于 1 到 100 分之间
- ProjCompl :你的项目成功交付的百分比
让我们从工作绩效受到其他三个潜在变量强烈影响的假设开始:员工的社交技能、和动机。
这些潜在变量中的每一个也不能直接测量。因此,让我们也为每个独立的潜在变量定义测量变量。
潜在变量社交技能将基于以下两个测量变量:
- 心理测验 1:1-100 之间的分数
- 心理测试 2: 也是一个介于 1-100 之间的分数
潜在变量智力技能将基于以下两个测量变量:
- YrsEdu :接受高等教育的年数
- 智商:智商测试的分数
潜在变量动机将基于以下两个测量变量:
- HrsTrain :花费在训练上的小时数
- 工作周的平均小时数
结构方程模型图
让我们通过创建结构方程模型图来使这个复杂的例子更容易理解。结构方程模型非常复杂,因为它们包含许多方向的许多系数,图表是理解结构方程模型的最佳方式。
结构方程模型通常遵循一些一般惯例:
- 潜在变量用圆圈表示
- 测量变量用正方形表示
- 关系用箭头表示
- 方差和残差由从变量到自身的箭头表示
以下是工作绩效问题的图表:

结构方程建模图
因为有许多变量和关系,让我们放大不同的方面。通常,结构方程模型图中没有任何颜色,但它肯定有助于更好地理解它:

结构方程建模图
在此图中,您可以看到:
- 工作绩效作为一个因变量,右边是三个测量变量。
- 三个独立潜变量 社会技能、智力技能、和动机在 JobPerf 的左边是
- 从三个独立的潜在变量到工作绩效的箭头表明这三个潜在变量预计会影响工作绩效。
- 左边的六个测量变量是预期每个变量影响一个潜在自变量。箭头显示哪个潜在变量受到每个测量变量的影响
现在,结构方程建模技术的目标是估算图表中每个箭头的系数。每个箭头是一个系数,标准误差也是要估计的。这些估计将允许你量化测量变量和潜在变量之间的复杂关系。
结构方程建模技术
为了拟合一个结构方程模型,你需要估计相当多的系数:图中每个箭头一个系数。有许多软件产品和技术用于估计结构方程模型,包括:
- 利斯雷尔
- 阿摩司
- MPlus
- 在 Stata 或 SPSS 中实现 SEM
- r 包
sem、lavaan、OpenMx、LISREL、EQS和Mplus - Python 包
semopy
结构方程建模很棘手,因为应该使用哪种算法没有绝对的定义,不同的软件包可以给出不同的结果。报告结构方程模型时,建议始终报告您使用过的软件。
在本文的下一部分,让我们使用 R 和 Python 来估计我们的工作绩效模型的系数。
R 中的 SEM
我已经将数据上传到了 S3 的一个桶中,以便于你理解。您可以使用 R 中的下面一行来获取数据:
R 中的 SEM 数据导入
数据看起来是一个千行 CSV 文件,文件头如下所示:

R 中的 SEM 数据
下一步是安装包lavaan,这是一个很棒的结构方程建模包:它有很好的文档记录,易于使用,并且与其他 R 包的语法一致。您可以使用以下两行代码安装并打开该库:
R 中的 SEM 安装 lavaan 包
下一步是描述我们想要拟合的模型。这必须代表我们之前在图表中绘制的架构。我们可以指定三种不同类型的关系:
- 使用
=~符号定义潜在变量如下:
a_latent_variable =~ measured_var_1 + measured_var_2 - 使用
~符号从一个潜在变量回归一个或多个其他潜在变量,如下:
latent_variable_1 ~ latent_variable_2 + latent_variable_3 - 如果您期望某些测量变量具有潜在变量中没有表示的相关性(例如没有必要),请使用
~~符号,如下:
measured_var_1 ~~ measured_var_2
使用这个语法,我们可以使用下面的 R 语法来指定我们的图:
R 中的 SEM 定义模型
作为最后一步,您需要拟合模型并显示模型摘要。您可以使用以下代码来实现这一点:
R 中的 SEM 拟合模型
您将获得下面的汇总表,其中我强调了最重要的学习内容:

R 中的 SEM 输出摘要
海伍德案件
我们首先看到的是 lavaan 报告了一个警告。有些差异是负的。这被称为海伍德案例,在结构方程建模中相对常见。结构方程模型从来都不是完美的,所以如果负方差在不太重要的参数中,那么继续下去就没有问题。否则,您可能想玩玩模型定义和数据集:Heywood 案例的一个常见原因是变量相关性太强。
总体结构方程模型的卡方检验
第二件要看的是卡方检验的意义。这个测试给你一个 p 值,显示你的模型是否解释了数据变化中足够重要的部分。
如果你对使用 p 值没有信心,看看这个对假设检验和 p 值的直观解释。通常,p 值需要低于 0.05 才具有显著性。这是我们的情况,所以我们可以得出结论,我们的模型总体上做得很好。
结构方程模型的回归系数
既然我们知道这个模型运行良好,我们将分析它告诉我们什么。为此,我们主要需要查看回归部分。在这里,我们发现了社会、智力和动机这三个潜在变量是如何影响工作绩效的。
首先要看的是 P( > |z|)表示的独立潜变量的 P 值。每个独立的潜在变量都有自己的一行。如果一个独立潜变量的 P 值低于 0.05,我们可以得出结论,这个特定的潜变量影响了因变量(工作绩效)。在我们的案例中,三个独立潜在变量中的每一个的 P 值都小于 0.05** ,这意味着每个独立潜在变量都显示出对工作绩效有影响。**
既然我们知道了这一点,我们可以更进一步问每个潜在的独立变量对工作绩效的影响有多大。我们通过查看估计列(系数的估计值)来实现这一点。我们看到:
- 动机的系数最高(2.758),这意味着动机的变化对工作绩效的影响最大。
- 智力具有第二高的系数(0.725),这意味着智力对工作表现的影响第二高。
- 社交技能排在最后,系数为 0.325。社交技能仍然对工作表现有影响,只是不如动机和智力。
至此,我们得出了我们的结构方程模型的结论。数据验证了我们的理论:好消息!
Python 中的 SEM
如果您对 Python 更熟悉,您可以使用 Python 包semopy来拟合与之前完全相同的模型。您可以使用下面的语法开始。
首先,您可以使用以下代码直接从 S3 时段导入数据:
Python 中的 SEM 导入数据
然后安装并导入semopy包,如下所示:
下一步是模型的规范。这与 R 代码的工作方式完全相同。然后实例化并拟合模型,并使用 inspect 方法显示结果表:
Python 中的 SEM 定义和拟合模型
您将获得一个包含系数估计值和 p 值的数据框。与 R 代码的区别在于 Python 输出的组织性稍差,因此您需要查看变量的名称(lval 和 rval 列)来找到您的系数:

Python 中的 SEM 输出摘要
回归系数可以在第 0 行到第 2 行找到。您可以在p-value列中看到,这三个变量的 p 值都小于 0.05,因此它们都是显著的。这证实了 R 结果。
在Estimate栏中,您可以获得系数估计值:
semopy发现动机的系数为 2.96,与lavaan的估计接近。- 对智力的估计排在第二位,就像在 R 代码中一样,尽管它的估计要高得多(
semopy中的 1.04 对lavaan中的 0.725)。 - 和 R 一样,社交技能是影响最小的潜在变量。
semopy的估计值是 0.527,而lavaan的估计值是 0.325。
尽管估计值略有不同,但 R 和 Python 实现在变量方面给出了相同的结论。这不是问题,许多高级建模技术都会出现这种情况:只需报告您使用的软件。
关键要点
总之,您已经首先了解了什么是结构方程建模以及何时可以使用它。您还看到了不同类型和技术的存在。
然后,您将结构方程模型应用于工作绩效数据集。基于九个测量变量,你假设工作表现可以用潜在变量社会技能、智力技能和动机来解释。
统计指标表明假设模型非常合适,你最后得出结论,动机对工作表现最重要,其次是智力技能和社交技能。
我希望这篇文章对你有用。不要犹豫,继续关注更多的数学、统计和数据内容!
结构方程模型
在使用它们的人中最受欢迎…想知道为什么吗?
如果你从事行为科学或社会科学,你可能知道或听说过结构方程模型(SEM)。人们在这些领域的经验越丰富,就越有可能将 SEM 列为他们的首选工具。这是为什么呢?
如果您正在寻找 SEM 的简要介绍,以帮助您理解为什么这个工具在使用它的人当中受到高度重视,那么这篇文章就是为您准备的。如果你是一名 SEM 专家,那么这篇文章可能是一个很好的资源,向你的同行解释你是做什么的(以及为什么你如此热爱它!).以下是扫描电镜的基本知识。
统计模型=路径图
SEM 的一个中心特点是 所有的模型都可以用一个路径图 来表示。对比图 1 中的等式和图表。如果你必须向一个 6 岁的孩子解释你的统计模型,你更喜欢哪个版本?

图一。W、X 和 Y 变量之间关联的方程与路径图表示。图片作者。
SEM 用户欣赏路径图的直观性质,这使他们能够有效地向广泛的受众传达他们的模型。因此,我们必须从描述如何正确创建路径图开始。绘制路径图所需的构建模块在图 2 的左侧,SEM 的两个标准路径图在图 2 的右侧。

图二。用于绘制 SEM 路径图的构建模块(左)和两个简单 SEM 模型的示例(右)。图片作者。
以下是创建和解释路径图的主要指南:
- 你直接测量的变量,在 SEM 行话中称为“显式”变量,用正方形画出。
- 未观察到的变量或“潜在”变量是因素分析意义上的因素。也就是说,它们代表其显式变量(即指标)的共同方差,它们导致我们在它们的指标中观察到的变化。潜在变量用圆圈表示。
- 三角形用于表示常数,它是 SEM 的一部分,可以估计平均值和截距(将变量回归到常数上,就可以得到它的平均值)。当模型不对变量均值进行限制时,三角形通常会被忽略。
- 单向箭头代表回归效应或负荷(负荷是显式变量对潜在变量的回归)。
- 双头箭头在相同变量上开始和结束时表示方差,在不同变量上开始和结束时表示协方差。
使用这些指南,您可以绘制指定高度复杂模型的路径图(描绘图 1 中的模型,其中 W、X 和 Y 都是潜在变量,并且包括附加的预测器和结果)!潜在变量之间的建模能力是 SEM 经常被认为是因子分析和回归的结合的原因。
图 2 右侧描述的第一个模型是单因素验证性因素分析。这里,潜在变量有指向 W、X 和 Y 的箭头,因为这些变量中观察到的变化是由潜在变量引起的。请注意,潜在变量和观察变量有一个方差。然而,要估计这样的模型,必须为潜在变量设置一个尺度。这通常是通过将负荷或潜在变量的方差固定为 1 来实现的。图 2 右侧描述的第二个模型是一个简单回归。注意,X 有一个方差,因此假设它是正态分布的。这种假设在标准最小二乘回归中是不存在的。然而,如果我们有缺失的数据,做出这样的假设使我们能够在分析中保留所有可用的数据——这是 SEM 的一个巨大优势!
路径图=数据结构
SEM 的另一个主要特征是 路径图暗示了变量如何协变的特定结构 。例如,图 3 顶部方框中的路径图没有连接任何 W、X 和 Y 变量。因此,这意味着这些变量完全不相关。然而,双向箭头表示变量具有非零方差。图 3 中的下一个方框显示了路径图对数据协方差结构的影响。事实上,模型隐含的协方差矩阵显示了方差的三个参数和其他地方的零。

图三。结构方程模型的估计:从路径图到模型拟合评估。图片作者。
自然,我们收集的数据都有自己的协方差矩阵。因为我们假设我们的数据是多元正态的,这个矩阵(有时是变量的均值,但我将在另一篇文章中讨论)足以满足我们的需求。事实上,样本协方差矩阵是我们对总体协方差矩阵的最佳估计。因此,SEM 中的 估计算法试图尽可能地匹配样本协方差矩阵中的值,同时保留模型 所隐含的约束。在图 3 中,模型暗示 W、X 和 Y 的方差有三个非零值,因此,模型的估计值就是样本协方差矩阵中对角线的值。
最后,我们可以通过比较样本协方差矩阵和估计的模型协方差矩阵来衡量模型的拟合度。这些矩阵之间的差异(图 3 中的最后一个框)为我们提供了模型的残差,可以将其归一化并总结为拟合指数,以量化模型的拟合度。请注意,这些残差是 SEM 特有的,因为它们是样本和估计的协方差之间的差异,而不是响应和预测值之间的差异,如在标准回归模型中。
图 3 用一个非常简单的例子抓住了 SEM 的本质。结构路径图对数据的影响可能要复杂得多,但是如果图 3 有意义,那么你就已经开始理解 SEM 了!
使用 SEM 的主要原因
如果您有以下任何需求,SEM 特别有用:
- 无法直接测量的模型变量(也称为潜在变量)
- 具有测量误差的模型变量(并解释其原因)
- 指定一个模型,其中的变量既是预测者又是结果
- 测试关于变量关联的特定理论
- 使用尖端方法处理缺失数据,避免多重插补的麻烦
- 图表直观地描述你的模型
如果你与这些需求中的任何一个都不相关,那么 SEM 可能不适合你的分析。事实上,就像有很多很好的理由使用 SEM 一样,也有其他的理由阻止你使用这个框架。
不使用 SEM 的主要原因
当分析师 没有 有一套理论 或一套竞争理论,旨在解释数据中的模式时,不应使用 SEM。对 SEM 的自动模型搜索的研究正在进行中,但是标准软件并不实现从该工作中产生的专门算法。因此,为了避免第一类错误(参见 MacCallum,Roznowski,& Necowitz,1992 ),分析师应该根据理论和以前的研究提前仔细设计他们的模型。这对于避免确认偏差尤为重要。
如果你能轻松地将回归模型拟合到小样本(比如说,N = 30),那么你同样可以轻松地在 SEM 中拟合等价的回归模型。然而,对于更复杂的模型,分析师必须考虑他们的样本量是否足够。变量的数量、它们的分布、缺失值和效应大小在确定适当的样本量时起着作用(提示:没有简单的经验法则!).一些研究指出每个参数估计值有 5-10 个观察值(Bentler & Chou,1987),但是当效应大小较低或变量有偏差时,即使这样也可能是错误的。
SEM 中的各种模型
SEM 可用于一系列目的:从拟合简单的线性回归,到用预测和作为该过程结果的因素来建模随时间变化的非线性过程。其他应用可能包括:
- 通过 验证性因素分析 开发用于测量一个或多个潜在变量的测试或调查
- 一组变量通过 路径分析 导向其他变量的测试机制
- 通过 中介分析 调查一个或多个变量对其他变量的间接影响
- 通过 潜在增长曲线分析 表征过程的个体和平均轨迹
- 通过 动态因子分析 研究时间序列过程内部和之间的动态
推荐
尽管路径图简单直观,但 SEM 可能是一项非常复杂的技术。因此,如果您想了解关于这个建模框架的更多信息,这里有一些参考资料可供您参考:
一本应用焦点的优秀书籍:
Kline,R. B. (2016 年)。 结构方程建模原理与实践 (第 4 版。).纽约:吉尔福德出版社。
有技术细节和应用的优秀书籍:
博伦,K. A. (1989)。 带潜变量的结构方程 。威利。
如果你没有时间看一整本书,试着用 SEM 的概述来搜索这篇文章:
乌尔曼,J. B .,&本特勒,P. M. (2013 年)。结构方程建模。在 J. A. Schinka,W. F. Velicer 和 I. B. Weiner(编辑。),《心理学手册:心理学中的研究方法》(第 661–690 页)。美国新泽西州霍博肯:约翰·威利父子公司。
准备好安装自己的 SEM 了吗?查看这篇文章了解为什么 JMP Pro 中的 SEM 平台会让你的生活变得轻松!
本文原载于 2019 年 10 月 23 日 JMP 用户社区 。
“构建您的数据科学项目!”
一个可扩展和可复制的模板来构建您的项目

Jo Szczepanska 在 Unsplash 上拍摄的照片
我为什么要考虑我的项目结构呢?
我花了很多时间试图学习和实现“最佳编码实践”,但我从未真正关注过“最佳项目结构实践”。
在进行课程或者小任务的时候,我从来不会去思考我应该如何架构项目;我会尝试将输入、输出、脚本和日志文件混合在一起,马上解决这个问题。
然而,随着项目开始增长,新的问题从这些即兴分析中产生,我发现自己经常不得不重写大量代码来理解数据,并且不确定如果有人要求我重新运行分析,我是否能够这样做并得到相同的结果。事实上,在我的第一次实习期间,我觉得把一个按照我的“最佳混乱实践”构建的项目交给一位同事很尴尬。我意识到我需要改变这种方式。
理想情况下,我希望找到最简单的项目结构——及其相应的工作流程——以促进可重复性和可伸缩性。当我开始谷歌搜索时,我注意到关于构建数据科学项目艺术的资源比预期的要少。特别是应用于生物信息学或计算生物学,我的主要兴趣领域。
剧透:我还没有找到理想的项目结构,但是在网上搜集了更多信息并自己实现了一些方法后,我创建了一个项目模板作为 GitHub 库,到目前为止它为我做了这项工作,我希望它也能为你工作。到目前为止,至少从一开始就有一个清晰的项目结构在避免空白页综合症和保持事物有序方面帮助了我很多。
原则:命名和模块化
为了创建这个项目模板,我试图遵循最佳编码实践的文献中经常提到的两个原则:清晰的名称和模块化。
例如,在命名目录、子目录和文件时,应该尽量使用既有意义又能在阅读文件的完整路径时相互补充的词。
另一方面,人们应该尝试构建项目,迫使它模块化地增长。可能受我过去在湿实验室的影响,我喜欢将项目的数据分析部分视为一系列试图回答假设的实验。我试图让每个实验回答一个特定的子问题,这样如果我们不需要一个实验,我们可以删除包含它的目录,这个操作不会影响其余的实验。在一个实验的结果是另一个实验的输入的情况下,子实验最好作为父实验的一部分引入,以避免这种依赖性。
再现性的基本工具
正如我提到的,我希望我的项目简单且可重复。幸运的是,这些属性符合社区中许多人的兴趣,他们在这方面开发了大量不同的工具。其中,我发现工作流和环境管理器非常有用,让我们能够在相同的环境中自动重新运行所有的分析。我最喜欢的是基于 python 的工作流和环境管理器: snakemake 和 conda 。
基本工作流程
这是使用此项目结构模板的基本工作流示例:

基本项目工作流程。图片由作者提供。
- 根据您的喜好修改
config.yml,添加对项目有用的变量。 - 创建工作流,分别在
workflows/download/和workflows/preprocess下载和预处理项目数据。确保区分专门用于项目那一部分的代码——放在你工作流程的scripts/子目录中——或者可以在整个项目中使用的代码——放在项目的src/模块中,并调用你工作流程中的函数。 - 现在,您可以分析您的数据,创建不同的实验作为
workflows/analyses的子目录,这些子目录将从data/获得输入,并在results/your_experiment_name/输出。 - 提交您的工作,并考虑添加自述文件。
- 检查和探索在
reports/notebooks/创建 jupyter 笔记本的结果,这些结果可以通过[jupyter-book](https://jupyterbook.org/intro.html)呈现到静态网页中。通过修改reports/_toc.yml来构建你的项目手册。
详细的结构
data/
data
├── prep
├── raw
└── references
在这里,我们将放置项目所需的所有数据,以便从任何地方都能轻松访问。子目录名称不言自明:
- 在
raw/,我们存储实验产生或下载的数据; - 在
prep/中,我们存储我们以某种方式处理过的原始数据,即预处理、清理或估算的数据;和 - 在
references中,我们存储有用的“基本事实文件”或查找表,比如,在我的例子中,基因组注释。
envs/
envs
└── main.yml
与 conda 等环境管理人员合作,可以无缝地促进大多数项目的可重复性。在这个目录中,我们放置了“main.yml”项目环境文件,其中包含了在整个项目中使用的包。我们可以添加某些工具可能需要的附加环境;当我们运行工作流程时,可以通过 snakemake 轻松激活它。
reports/
reports/
├── _config.yml
├── images
│ └── logo.png
├── notebooks
│ ├── example_notebook.md
│ ├── intro.md
│ └── README.md
├── README.md
└── _toc.yml
数据科学最重要和最有趣的部分之一是沟通。由于这个原因,我认为每个项目产生一系列报告是很重要的,通过这些报告,人们试图用我们的分析结果来回答问题。在这种情况下,我认为 jupyter-book 是一个好朋友,因为它允许我们创建一个简单的静态网页,将我们所有的笔记本结合起来,并可以作为 GitHub 页面发布,就像这个。
workflows/
workflows/
├── analyses
│ └── new_experiment
│ ├── README.md
│ ├── run_all.sh
│ ├── scripts
│ │ └── workflow_step.py
│ └── snakefile
├── download
│ ├── README.md
│ ├── run_all.sh
│ ├── scripts
│ │ └── workflow_step.py
│ └── snakefile
├── preprocess
│ ├── README.md
│ ├── run_all.sh
│ ├── scripts
│ │ └── workflow_step.py
│ └── snakefile
└── README.md
这是我们在项目中花费大部分时间的目录。在这里,我们将创建我们的“实验”,尝试回答一个特定的问题。
workflows/analyses/中的每个实验都使用data/中的文件作为输入,并将输出保存到results/中。例外的是,在项目开始时,workflows/download和workflows/preprocess中的工作流将输出到前者的data/raw和data/references,并输出到后者的data/prep。
然后,可以通过bash run_all.sh来执行实验,这是一个简单的脚本,它会告诉您最喜欢的工作流管理器使用一些通用参数来运行实验。我们的工作流管理器脚本——这里是“snake file”——包含了执行我们分析的每一步的规则。最后,workflows/analyses/new_experiment/scripts/子目录包含那些执行“new_experiment”的特定步骤的脚本。
results/
results
├── new_experiment
│ ├── files
│ │ └── output_example.tsv
│ └── plots
│ └── output_example.pdf
└── README.md
这里我们将放置我们实验的所有输出。results中的子目录应该与实验同名,并且通常可能包含可以从我们的报告中轻松读取的文件和图表。
src/
src
└── python
├── setup.py
└── your_project_name
└── config.py
为了最大限度地减少跨不同实验复制粘贴函数或脚本所带来的错误,在这个目录中,我们将创建项目范围的模块,可以从项目中的任何地方调用这些模块。在这种情况下,我认为“config.py”是必不可少的模块之一,它将读取我们的顶级“config.yml”以从项目中的任何地方访问变量。
到目前为止,结论是
以我的经验来看,使用这种项目结构尤其有助于我一次专注于一件事。虽然构建项目的每一步都需要更多的时间,但我觉得这些步骤是可靠的,从长远来看,可以让我从所有的重构、错误修复和错误纠正中节省时间,而这些都是我以前“混乱”的方法所必须做的。
如果你正在读这篇文章,你可能对这个话题感兴趣,并且正在努力寻找最适合你需求的项目结构。所以,请随时联系我们,更好的是,提交一份 PR 并改进 project_template !
参考
- 弗吉尼亚州布法罗(2015 年)。生物信息学数据技能:用开源工具进行可重复的和健壮的研究。奥莱利媒体公司。链接
- Noble WS (2009)组织计算生物学项目的快速指南。公共科学图书馆计算机生物学 5(7): e1000424。https://doi.org/10.1371/journal.pcbi.1000424
- Eric Ma —原则性数据科学工作流程
- Pat Schloss-riff omonas 项目
像诺贝尔奖获得者一样组织你的数据
为了便于操作、可视化和建模

在 Unsplash 上由 Samet Kurtkus 拍摄的照片
你认为有“最好的方式”来组织你的数据吗?
我特别想在评论中听听你的看法。因为就我而言,我最初认为必须有许多方法来构造数据集,我们存储和操作数据的方式是特定于我们正在进行的项目的。
但事实上,有一种方法——一种框架——我们几乎可以系统地使用它来尽可能简单高效地清理数据。
我第一次听说这种方法是在麻省理工学院在线课程“社会科学家数据分析”中埃丝特·杜弗洛(2019 年诺贝尔经济学奖)的一次演讲中,她在演讲中推广了“整齐的数据”和一个名为“Tidyverse”的框架。
这种结构非常有利于数据操作、可视化和建模。我们可能应该将它添加到我们下一个项目的最佳实践列表中。你不觉得吗?
在这篇博客文章中,我想给你一个概述:
- 整洁数据的概念,
- 数据集有多乱,以及
- 我们可以使用 python 函数将杂乱的数据转换成整齐的数据。
让我们开始吃吧。
那么,什么是整齐的数据呢?
整齐数据的概念是由 Wickham Hadley 在他的论文“整齐数据”中首次提出的,他的框架在“欢迎来到 Tidyverse”中提出。
我们来看一下定义。
为了被认为是“整洁的”,数据应该按照以下三个规则排列在表格中:
- 每个变量在其自己的列中
- 每个观察值在它自己的行中
- 各自单元格中的每个值
仅此而已。只有三条规则。但这实际上很好,因为一小组规则导致一小组工具,我们需要这些工具来重新排列和清理杂乱的数据集。
为了便于说明,我将定义两个简单的整齐数据集。第一个连续两年观察最小和最大值的观测单位(在下图的左边)。第二个观察单位观察不同国家的大小(下图右侧)。
这两个表很整洁:每个变量都有自己的列,每个观察值都有自己的行,每个值都有自己的单元格。

整洁观测单位的两个例子
这种结构非常简单,但是在下一部分中,您将会看到,在查看杂乱无章的数据时,这样的安排并不总是显而易见的。
能有多乱?
杂乱的数据集可以有几种形式,但谢天谢地,Wickham Hadley 为我们过滤了噪音,并将“杂乱的数据”分为 5 个常见的类别。
- 当列标题有值而不是变量时。
例如,如果我们在列标题中设置年份(1992,1993),就会出现这种情况。

列标题是值而不是变量
- 当一列包含多个变量时。
在我们的例子中,如果我们将最小值和最大值追加到一列中,就会出现这种情况。

一列包含多个变量
- 当变量同时存储在列和行中时。这个例子更难发现。如果您发现一个存储变量名(在我们的例子中是 Min,Max)而不是值的列,通常会发生这种情况。

变量同时存储在列和行中
- 当多种类型的观测单位存储在同一个表中时。
大量未定义的单元格就是这种情况的最好证明。在上一部分中,我们定义了两个表(观察单位),用于报告不同的研究。如果我们将它们合并在一起,我们最终会得到许多未定义的“N/A”值。

多种类型的观测单位存储在同一个表中
- 当单个观测单位存储在多个表中时。
在我们之前的示例中,在同一个研究中测量了“最小”和“最大”变量,我们同意使用单个表格更方便。

一个观测单位存储在多个表中
既然我们已经看到了数据集有多乱,那么是时候看看我们可以用来整理它们的 Python 函数了!
将杂乱的数据转换成整齐的数据
让我们来看看常见的 Python 函数,我们可以使用这些函数来整理前一部分中描述的五种类型的杂乱数据集。
好消息是整齐的数据是一种非常直观的处理熊猫数据框架的方式。例如,Rodrigo Marino 分析了上述所有场景,并在本文中演示了如何整理每一个场景。根据他的研究,我得出了以下我认为最相关的函数:
- 透视表 _ 表格 : 创建电子表格样式的透视表
- 融化 : “逆透视”一个数据帧从宽到长的格式
- 赋值 : 计算并追加一个或多个新列
- 因式分解 : 将列表编码为分类变量
- 串联 : 将两个熊猫对象串联在一起
- sort_values :按列或行的值排序
为了理解每个函数的机制,我想与您分享我的 Jupyter 笔记本的代码,这些代码通过简单的示例来实践它们:
将杂乱的数据转换成整齐的基本 Python 函数
最后的话
我们一起看到了整洁数据的概念,它会变得多乱,以及我们可以使用什么工具来整理它们。
这种简单框架的第一个主要优点是,我们可以用少量的函数整理混乱的数据集。在我看来,第二个主要优点是,一个整洁的数据框架在 Python 中与流行的 Pandas 库完美且非常直观地配合工作。
数据争论是一个很大的领域,“整齐的数据”只涵盖了它的一部分,但它肯定会给你一个良好的开端!
参考
[1]韦翰,哈德利。“整齐的数据。”统计软件杂志59.1(2014):1–23。
[2]韦翰、哈德利等人,“欢迎来到蒂德弗斯。”开源软件杂志 4.43 (2019): 1686。
通过不断讲述故事来构建您的数据科学项目
我们试图计划那些本来难以计划的事情
介绍
“我们的项目进展很糟糕,过去几周我们没有取得任何进展,”我的一个学生在一次进度会议上告诉我。听到这个消息我非常惊讶,因为我对他们取得的进步感到非常高兴。事实证明,It 学生习惯于运行比一般的数据科学研究项目更直接的项目。我用来向学生解释这一点的类比是穿越森林。在一个 IT 项目中,我们做一些研究,我们启动一个路线规划器,设定一条路线,并在这条路线上执行。在一个研究项目中,我们只是拿着手电筒走进森林,试图找到我们的路,有时如果我们发现自己被困在沼泽中,就会原路返回。这种在研究项目中缺乏直接途径的内在缺陷使得 IT 专业的学生非常没有安全感。
但是仅仅不计划一个研究项目也有很大的风险。我见过许多博士候选人做了几个月的研究,却没有什么真正的成果,这让候选人非常焦虑。所以现在我们面临一个难题:如何计划难以计划的事情?在这篇文章中,我想提出一些想法,旨在不断告诉你自己和你的合作者你的项目的故事或叙述:我们为什么这样做,我们取得了什么进展。这些想法来自于我自己的经历,来自于帮助我的学生做他们的项目。这绝不是如何运行数据科学项目的全部,但我希望这些想法对您有价值。

反思才是王道
我在数据科学项目中看到的最大风险之一是,你继续做研究而不反思你的进展。你做了很多工作,但不一定能解决问题。让这种反思变得困难的是,你需要把自己从工作的本质中提升出来,从高处俯瞰。我的结果如何让我朝着最终目标前进?
真正有助于形成直升机视角的是使用 CRISP-DM 这样的方法。该模型将数据科学工作流分为多个阶段,从评估业务问题和数据,到执行分析并思考它们对业务问题的意义。我们的目标不仅仅是进行统计分析,而是精心设计一个故事来全面回答业务问题。CRISP-DM 有组织的流动提醒我们经常反思。
拥抱迭代
通过像 Scrum 这样的敏捷方法,IT 世界已经接受了迭代工作。在研究中,这同样重要,如果不是更重要的话。我们的项目不是由一个大的反射(CRISP-DM)周期组成的,而是由几十个周期组成的,这些周期制造了几十个故事。这些小故事构成了大故事的主干。
不断完善和融合
使用反思、叙述和迭代,我们可以构建我们的数据科学项目。根据我的经验,在项目开始时,我们从对业务问题非常基本的了解开始。比如:如何才能检测出 x 光图像中的异常。从这个基本前提出发,我们开始构思最初的几个故事。一个简单的解释是,我们要求医院提供两组 x 光图像,一组有异常,另一组没有异常。我们只是用眼睛看,看是否能区分两组图像。通过视觉观察,我们构思了一个故事。在叙述的评估部分,关于统计工具的想法可以作为新的前瞻性叙述出现。
随着项目的进展,叙述变得越来越复杂,并建立在彼此之上。此外,一些叙述被认为是死胡同,被丢弃。如果我们在最后写一篇研究论文,这就呈现了最终的整体叙述,它囊括并总结了之前所有的小叙述。后来错综复杂的叙述往往更强烈地影响最后的论文。
规划叙事
但是我们如何计划所有这些环环相扣的叙述的流程呢?我们可以尝试借鉴 Scrum 的一些概念。我们可以使用计划板来跟踪我们的叙述,将预期的叙述列在待办事项中。在 sprint review 中,我们可以展示和讨论这些叙述,进一步完善它们的故事情节。此外,我们可以使用 sprint 计划来决定待办事项列表中的哪一个叙述是最佳的前进方式。最后,在 Sprint 回顾中,我们作为一个团队讨论如何更有效地合作。固定冲刺长度或使用故事点进行叙述等其他方式可能不会很好,但我们仍然可以在难以规划的数据科学研究世界中创建一种结构化的工作方式。
我是谁?
我叫 Paul Hiemstra,是荷兰的一名教师和数据科学家。我是科学家和软件工程师的混合体,对与数据科学相关的一切都有广泛的兴趣。你可以在 medium 上关注我,或者在 LinkedIn 上关注我。
如果你喜欢这篇文章,你可能也会喜欢我的其他一些文章:
熊猫与空间的结构化自然语言处理
通过将非结构化数据结构化来加速分析

由于自然语言数据缺乏结构性,处理自然语言数据通常具有挑战性。大多数数据科学家、分析师和产品经理都熟悉由行和列组成的结构化 表格,但不太熟悉由句子和单词组成的非结构化 文档。出于这个原因,知道如何处理自然语言数据集可能是相当具有挑战性的。在这篇文章中,我想展示如何使用令人敬畏的 Python 包**spaCy**和**Pandas**,来构建自然语言并快速提取有趣的见解。
空间介绍
spaCy 是一个非常流行的用于高级自然语言处理的 Python 包——我这里有一个关于 spaCy 的初学者友好介绍。spaCy 是应用数据科学家在从事 NLP 项目时的完美工具包。API 非常直观,软件包运行速度极快,并且有很好的文档记录。可以说它是目前最好的 NLP 通用软件包。在开始构建 NLP 数据之前,熟悉 spaCy 库和 api 的基础知识是很有用的。
安装软件包后,您可以加载模型(在这种情况下,我加载的是简单的 Engilsh 模型,它针对效率而非准确性进行了优化),即底层神经网络的参数较少。
import spacy
nlp = spacy.load("en_core_web_sm")
按照惯例,我们将这个模型实例化为**nlp**。在整篇文章中,我将使用这个著名的激励语录数据集。让我们将**nlp**模型应用于数据中的单个报价,并将其存储在一个变量中。

数据集标题-按作者分类的图像。
doc = nlp(df.quote[819])
print(doc)

图片作者。
如果我们打印这个文档,你可以看到它只是返回了原来的引用,但是在这个引擎盖下,发生了很多 NLP 的魔法——我们创建了一个 spaCy **doc**对象。那么我们能用它做什么呢?
首先,我们可以做两个非常酷和有趣的可视化。下面的可视化显示了文档的依赖结构。

这些依存关系通过显示文档中每个单词如何与其他单词相关来描述句子的语言结构。值得停下来惊叹一下。在引擎盖下,spaCy 已经将一个大型神经语言模型应用于文档,解析了依赖结构,并使我们能够轻松地查看它— 所有这一切都只需要一行代码。如果你想更好地理解如何解释这个依赖结构,你可以使用**spacy.explain**函数来获得每个依赖类型的定义。
下一个可视化显示了已经被语言模型识别的实体。在这种情况下,语言模型已经识别出几个属于日期类型的单词。

图片作者。
我们可以通过编程来访问文档的这些属性,如下所示。
[(i, i.label_) for i in doc.ents]
**doc.ents**方法允许访问 spaCy 语言模型预测的实体。和依赖标签一样,你可以使用**spacy.explain**来理解每个实体标签的意思。
**doc**对象的其他有用属性是**sents**和**noun_chunks**方法,它们使您能够将文档分割成句子或名词块(即名词加上它们的描述符)。
doc = nlp(df.quote[0])
spacy.displacy.render(doc, style="ent")
doc_nouns = list(doc.noun_chunks)
print(doc_nouns)

图片作者。
如果我们从文档中访问一个单词,我们实际上是在访问一个空间 **token** 对象。像**doc**对象一样,**token**对象包含许多有用的属性。例如,我们可以遍历文档来提取每个令牌及其各种属性。
[(i, i.ent_type_, i.is_stop) for i in doc]

图片作者。
既然我们已经介绍了 spaCy 和 api 的一些基础知识,让我们对数据采取一种更结构化的方法。
结构化自然语言数据

这里的目标是将非结构化文档转换成结构化的数据表。本质上,我们希望从文档、令牌及其元数据的非结构化语料库转移到行和列的结构化数据集。
- 步骤 1: 我们首先需要将
**spaCy**语言模型应用于整个报价集合。最简单和计算效率最高的方法是使用**nlp.pipe**函数。这将迭代每个文档并应用语言模型。
docs = list(nlp.pipe(df.quote))
-
第二步:定义一个函数,提取你想要包含在表格中的每个单词的所有属性。该函数将在单个
**doc**中迭代令牌,并将提取各种属性,如词条、位置、实体和标签。使用该函数定义您想要从token对象中提取的所有属性。在这种情况下,我提取我经常使用的公共属性,这将有助于分析数据。 -
步骤 3: 定义一个函数,将上面的函数应用于所有文档,并将输出存储在一个 Pandas
**dataframe**中。
如果我们运行这些步骤,我们最终会得到如下所示的数据集:

图片作者。
每行代表一个令牌,各列捕获该令牌上的各种元数据。重要的是,我们还可以使用doc_id列将每个单词链接回它的文档。
分析结构化自然语言数据
现在我们有了这种格式的数据,我们可以像分析任何其他结构化数据集一样分析它。例如,我们可以计算每篇文档的字数,并用直方图显示出来。
tidy_docs.groupby("doc_id").size().hist(figsize=(14, 7), color="red", alpha=.4, bins=50);

图片作者。
或者,我们可能有兴趣了解在语料库中识别的最常见的实体:
tidy_docs.query("ent_type != ''").ent_type.value_counts()

图片作者。
在过滤掉停用词和切分后,我们可以对单词做同样的事情:
tidy_docs.query("is_stop == False & is_punct == False").lemma.value_counts().head(10).plot(kind="barh", figsize=(24, 14), alpha=.7)
plt.yticks(fontsize=20)
plt.xticks(fontsize=20);

图片作者。
我们可以对结构化数据进行的另一个有趣的分析是查看所有格,以了解 corpu 中的所有权。下面的代码按每个文档分组,然后提取前面和后面的标记,然后过滤带有**POS**标记的标记,该标记指示所有格关系,即 前一个标记的对象拥有后一个标记的主题。

图片作者。
这使我们能够理解语料库中的所有格关系。例如,你可以看到这样的短语:【一个人的勇气】【某人的祈祷】【今日运动会】。深!
正如您所看到的,由于数据是结构化的,处理数据变得非常简单。
把一切都包起来
这篇文章展示了如何将非结构化的文本数据转化为易于分析的结构化数据集。我经常发现这是对 NLP 项目的数据有感觉时有用的第一步,并且经常可以揭示有趣和有用的见解。
感谢阅读!
附:这篇文章的所有代码可以在这里找到。
用先进的 EDA 技术为讲故事进行结构化思考
为开放式分析定义好的 DS 问题的 5 步指南

格伦·卡丽在 Unsplash 拍摄的照片
大多数数据科学(DS)项目都有我们想要实现的明确目标,比如开发一个有监督或无监督的机器学习(ML)模型,或者我们想要测试的一个假设。然而,在没有定义任何特定分析目标的情况下,数据集被用于探索也并不罕见。这种情况发生在你参加数据聚会,或者像 Meetup 小组这样的学习小组的时候。提供数据集是为了激发 DS 的创意,看看你能提供多少见解,你能用它讲述什么故事。在我看来,这些任务比那些预先定义了 DS 问题的任务更难。在清理数据并绘制一系列图表来检查数据分布后,很容易陷入困境,因为您会开始问一些关键问题:
>数据可视化还能做什么?
>要从哪些角度去挖掘数据,以讲故事和有价值的见解?
问这些问题是作为数据科学家的高级 EDA (探索性数据分析)向结构化思维迈进的第一步。结构化思维不仅需要头脑风暴,还需要合理的逻辑。在这篇博客中,我希望分享一种方法,你可以遵循这种方法来获得数据清理和分布检查之外的更高级的 EDA,以及基于实践数据集和 Jupyter 笔记本(可在我的 Github 中获得)来处理这些开放式 DS 项目的技巧
1.高级 EDA 的结构化思维概述
- 什么是结构化思维?
结构化思维是把一个框架放到一个非结构化问题上的过程。拥有一个结构不仅有助于分析师从宏观层面理解问题,还有助于识别需要更深入理解的领域。(参考 1)
- 什么是“进阶 EDA”?
对于目标明确的 DS 项目,EDA 旨在帮助理解数据的问题和分布,然后对数据进行相应的清理和预处理,以获得模型就绪的数据集。对于没有明确目标的 DS 项目,高级 EDA 包括绘制数据中隐藏相关性的额外步骤,以及从特定角度进行深入分析。
- 让我们从方法开始吧!
下面总结了 5 个步骤的方法,我将在下面的部分用练习数据集来说明更多的细节。请注意,这种方法有局限性,可能不适用于每个数据集。请看本博客最后一节的讨论。
- 第一步:单变量初步 EDA(单变量)
目标:使用数据字典了解每个变量的含义,检查它们的分布,执行数据清理和预处理以处理异常值、缺失值等。
- 第二步:根据特征对变量进行分类
目标:根据数据的收集方式,将变量分为情境变量(变量代表年龄、地点、性别等特征)和动态变量(在数据收集过程中观察/监控的变量,如账户余额、交易次数、游戏结果)。
- 第三步:探索变量之间的相关性
目标:通过创建显示相关性的图表、对观察值进行分段、创建新变量等,理解变量之间的相互关系。
- 步骤 4 :“放大”分析
目标:通过将子集与总体进行比较,选择一个子集进行深入分析。子集可以是一个观察结果(例如,一个对象对所有对象)或一个类别(有机食品对所有种类的食品)
- 第五步:发掘建模潜力
目标:调查数据建模的潜力,并用统计方法和/或机器学习技术提供分析结果。例如,尝试找到一个变量作为预测建模的响应变量,或者执行聚类以了解下面的相关性。
2.练习数据集简介
为了展示这种方法是如何工作的,以及如何在分析的同时讲述一个故事,我们用一个马拉松比赛的练习数据集创建了一个示例 DS 项目。数据字典如下所示:
-地点:每名选手相对于同性别选手的完赛顺序
-编号:选手的围兜号
-姓名:选手姓名
- Ag:选手年龄
-家乡:选手家乡。对于国内赛车手,是“城市,州”,对于国外赛车手,是“国家,。
-性别:选手的性别
-组别:年龄组包括相同性别组的选手
-枪时间(分钟)表示从比赛正式开始到选手越过终点线所经过的时间
-净时间(分钟)表示从选手越过起跑线到选手越过终点线所经过的时间
-速度时间(分钟):选手在这场比赛中每英里的平均时间(分钟)
这是该数据集前几行的视图:

作者图片
看起来很简单,不是吗?我很确定检查每一列的分布并不困难。我们已经提到了一些要制作的基本描述图,如数字变量(如步速、枪支时间)的密度图和箱线图,分类变量(如性别、部门)的条形图和饼状图
现在让我们深入数据集,看看我们如何一步一步地应用该方法!
3.逐步实现高级 EDA
3.1 初步 EDA(单变量)
大多数人已经熟悉了通常与数据清理相关的这一步。这部分有很多教程,所以我不打算多谈。这一步的目标是生成一个干净的数据集,让您感觉良好,开始挖掘其中的真知灼见这一激动人心的部分!如果你对我所做的数据清理感兴趣,请随时查看 Github 中名为“part1_data_cleaning”的 Jupyter 笔记本。
3.2 分组变量
第二步在编码上不花太多功夫,但在批判性思维上花了很多。在我们对数据有了大致的了解之后,我们希望将它们分组为上下文变量(变量代表特征)和动态变量(数据收集过程中观察到的变量)。为什么是这一步?因为:
*为第 3 步做准备,在第 3 步中,我们创建图表来查看两组变量如何相互作用。
*选择讲述故事的视角(即从上下文变量中选择子集)用于步骤 4 中的“放大”分析
*发现在步骤 5 中使用上下文变量进行聚类分析和赛车细分建模的潜力
*在步骤 5 中探索建模潜力时在动态变量中寻找负责任的变量
为了对变量进行分组,我们需要考虑数据是如何收集的,以及数据来自什么结构。对于此示例数据集,下表显示了分组结果:在马拉松比赛期间收集了比赛结果(例如,净时间、枪时间),其他特征是参赛者的特征:

作者图片
3.3 探究变量之间的相互作用
一个有趣的数据故事总是包括探究变量之间如何相互作用。当数据集有许多变量时,查看数据的方式有很多,因此可能很难知道如何有效地探索数据集以获得最有价值的见解。这就是为什么我们在步骤 2 中对变量进行了分组,现在我们可以使用一些技术来有效地从变量中找到有用的相关性。使用上下文变量和动态变量,在探索它们之间的相互作用时可以有 3 种组合,每种组合导致不同的数据探索视角:

作者图片
让我们用练习数据集来试试:
1 个上下文变量与上下文变量
在 3.1 节(初步 EDA)中,我们已经对参赛者的分布和统计数据有了一些基本的了解。下图显示了一些关于性别和年龄的统计数据。

作者图片
总的来说,评估背景变量之间的相关性可以帮助我们理解混杂效应。为了找到性别和年龄之间的进一步关联,我们可以使用并列图来显示不同分区中的计数(基于年龄)。一些有趣的见解显示在下面的图中,例如大多数参赛者年龄在 30-49 岁(61.9%),男女都是如此,除了 20-29 岁和 30-39 岁,所有组别中女性参赛者都少于男性。
import plotly.express as px
fig = px.histogram(df, x="div", color="gender",
hover_data=df.columns,opacity=0.8)
fig.update_xaxes(title="Division")
fig.update_xaxes(categoryorder='array', categoryarray= ['0-14','15-19','20-29','30-39','40-49','50-59','60-69','70-79','80-89'])fig.update_yaxes(title="Count of Racers")
fig.update_layout(barmode='group')
fig.show()

作者图片
类似地,我们可以尝试绘制其他上下文变量。比如用性别和家乡画个地图,看看全国男女赛车手分布是否均匀。
2 动态变量对动态变量
响应变量通常选自 ML 模型中的动态变量。动态变量是我们想要研究来回答现实世界问题的变量。例如,银行分析客户的账户余额以预测客户是否会流失,商店分析客户的购物记录以决定向他们推荐什么产品。在这里,帐户余额和购物记录是我们想要调查的动态变量。在示例数据中,动态变量是比赛结果(净时间、枪时间、速度和名次)。他们是我们想用来讲述比赛故事的关键变量。
探索数据不应该局限于提供的变量,我们也可以创建新的变量,就像我们在特征工程中所做的一样。比赛结果包括两个时间变量:枪时间和净时间。看了数据描述,我们知道枪响时间是净时间加上枪响后越过起跑线所用的时间。因此,我们可以使用这个信息来创建一个新的变量,我称之为“cross_t ”,来表示枪时间和净时间之间的差异。
为什么这个新变量很有趣?我们需要使用数据本身之外的一些知识。思考收集数据时的情况是发现观察数据的新角度的好方法——我们通过首先提出好的问题来讲述好的故事。马拉松开始前,参赛者排队等候发令枪响。有获胜野心的参赛者会在乎他/她是否站在起跑线附近吗?这对他们的比赛结果会有什么影响?为了回答这些问题,我画了一个交叉时间与净时间的图表,来看看谁跑得最快:
```python
fig = px.scatter(df, x=”cross_t”, y=”net_s”, color=’gender’,opacity=0.5)
fig.update_xaxes(title=”Cross Time (minutes)”)
fig.update_yaxes(title=”Net Time (minutes)”)
fig.show()

作者图片
讲故事的洞见 :
-一般来说,穿越时间较短的选手也有较短的净时间
-大多数最快的选手(净时间< 40 分钟。)也花了更少的时间在发令枪响后冲过起跑线(他们从一开始就认真对待比赛!)
3 上下文变量与动态变量
通过将情境变量和动态变量放在一起,我们可以看到参赛者的特征如何影响他们的结果。请注意,这不同于步骤 1(初步 EDA)中的基本分析:现在我们正在评估不同人群子群中的 race 结果。
让我们首先从枪支时间结果和性别组开始,我们可以检查它们是如何与并排箱线图交互的:
import plotly.express as px
fig = px.box(df, y='gun_s', x='gender', color="gender", points="all",
notched=False, # used notched shape
title="Box plot of net time",)
fig.update_xaxes(title="Gender")
fig.update_yaxes(title="Net time (minutes)")
fig.update_traces(orientation='v') # horizontal box plots
fig.show()

作者图片
类似地,我们可以做同样的速度与性别对比图:

作者图片
讲故事的感悟:男性组比女性组快。
我们还可以检查各组的净时差:
fig = px.box(df, x="div", y="net_s", color="gender")
fig.update_traces(quartilemethod="exclusive") # or "inclusive", or "linear" by default
fig.update_xaxes(categoryorder='array', categoryarray= ['0-14','15-19','20-29','30-39','40-49','50-59','60-69','70-79','80-89'])
fig.update_xaxes(title="Division")
fig.update_yaxes(title="Net time (minutes)")
fig.show()

作者图片
讲故事的洞察力:15-19 岁年龄组的赛车手拥有最好的上网时间记录,无论男女。此外,与女选手相比,男选手在每个组别都有更好的成绩。
还记得我们在上一节中创建的新变量“cross_t”吗?我迫不及待地想看看它是如何与上下文变量交互的!但在此之前,让我先画出不同性别群体的持枪时间和上网时间:
plt.figure(figsize=(12, 6))
sns.kdeplot(df_f.loc[:,'gun_s'], color='orange', linestyle='--',label='Female gun time (min)', linewidth=2)
sns.kdeplot(df_m.loc[:,'gun_s'], label='Male gun time (min)', linestyle='--', color='green',linewidth=2)
sns.kdeplot(df_f.loc[:,'net_s'], color='orange',label='Female net time (min)', linewidth=2)
sns.kdeplot(df_m.loc[:,'net_s'], label='Male net time (min)', color='green',linewidth=2)
plt.legend(loc=1, prop={'size': 10})
plt.xlabel('Time', fontsize=16);plt.ylabel('Density', fontsize=16)

作者图片
在这里,我注意到男性持枪时间的密度曲线比女性持枪时间的密度曲线更平坦,这让我很想知道在开枪后发生了什么,直到参赛者越过起跑线。所以我绘制了新变量“cross_t”相对于性别的密度:
plt.figure(figsize=(12, 6))
sns.kdeplot(df.loc[df.gender=='F','cross_t'], label='Female', color='orange',linewidth=2)
sns.kdeplot(df.loc[df.gender=='M','cross_t'], label='Male', color='green',linewidth=2)plt.xlabel('Cross Time (minutes)', fontsize=16);plt.ylabel('Density', fontsize=16);
plt.title('Distribution of cross time in minutes', fontsize=18)plt.legend(loc=1, prop={'size': 10})

作者图片
首先,这个情节很有趣,因为有三个高峰。此外,如果我们查看大约 2.2 分钟的时间戳,我们可以看到一些模式:在开始时,雄性组的密度线在雌性组的线之上,但是在 2.2 分钟之后,情况走向相反。所以我做了一些简单的计算来抽象出讲故事的见解:
——大多数参赛者的穿越时间是 1.3 分钟,3.6 分钟。5.2 分钟,与性别无关
-男性选手在开始时更具侵略性:75.5%的女性选手花了> 2.2 分钟冲过起跑线,相比之下,男性选手的这一比例为 54.7%
在我们进入下一步 :
之前的其他想法-除了上面提到的图表,我们可以绘制带有“家乡”变量的地图,并查看它如何影响比赛结果
-此外,为所有数值变量绘制相关图表(例如热图)总是很好的。如果您决定在步骤 5 中拟合模型,这样做可以帮助您避免共线性
3.4“放大”分析
在上一节的步骤 3 之后,我们已经从数据集中获得了一些很好的见解。我们如何进一步调查?当我深入挖掘数据来讲述一个故事时,“放大”分析是一个有用的技巧。“放大”意味着我们从总体中挑选一个数据子集或一组数据,并定义一个有趣的问题进行分析。到目前为止,我们一直专注于整个人口(又名所有赛车)。我感兴趣的是挑选一个中等水平的赛车手(克里斯·多伊),并尝试回答“克里斯与顶级赛车手相差多少时间”这个问题。以下是这位赛车手的一些基本信息:

作者图片
然后,我做了一些计算,并创建了一些可视化效果,将他的结果与他所在组别中前 10%的选手进行比较:

作者图片
fig = px.histogram(df_cd, x="net_s")
dt = df_m.loc[df_m.Name=="Chris Doe",'net_s']
fig.add_vline(x=np.sum(dt), line_width=3, line_dash="dash", line_color="brown")
fig.add_vline(x=perc, line_width=3, line_dash="dash", line_color="yellow")
fig.add_vline(x=np.sum(np.mean(df_top.net_s)), line_width=3, line_dash="dash", line_color="red")
fig.update_yaxes(title="Count of racers")
fig.update_xaxes(title="Net time (minutes)")
fig.show()

作者图片
讲故事的洞察力 :
-克里斯需要减少他的净时间 8 分钟才能进入前 10%的赛车组!
-克里斯的净时间比排名前 10%的赛车组的平均净时间多 11.6 分钟
哇!看来要成为顶级赛车手还有很长的路要走!克里斯,希望你下次能做得更好!
3.5 利用机器学习发现建模潜力
通过遵循上述步骤,我们提供了不同视角的见解。我相信我们对数据和比赛有了更多的了解。现在是时候集思广益,利用机器学习(ML)技术,从这个数据集找到创建 DS 模型的机会了。
我首先想到的是以净时间为响应变量的分层建模。当我在第 3.3 节绘制部门与性别的对比图时,我注意到每个部门的参赛者数量都非常不同。换句话说,有些部门有超过 300 名选手,但有些部门只有不到 50 名。这表明,通过考虑部门和性别的随机效应,很有可能建立一个层次模型。我们可以添加其他变量(即交叉时间)作为固定效应的独立变量。所以我开始用一个简单的双向 ANOVA(方差分析)来测试我的想法。
# get ANOVA table as R like output
# drop div 80-89 because only male group has racer in this range
df1 = df.copy().loc[df.loc[:,'div']!='80-89',:]
# Ordinary Least Squares (OLS) model
model = ols('net_s ~ C(div) + C(gender)+ C(div):C(gender)', data=df1).fit()
anova_table = sm.stats.anova_lm(model, typ=2)
anova_table.round(2)

作者图片
讲故事的洞察力:双因素方差分析结果显示,分班和性别会显著影响上网时间。但是它们的交互作用没有显著影响。
下一步将是创建分级模型来测试随机截距和随机斜率的影响,并比较模型性能。受限于博客的长度,我没有继续这项工作。但是如果你感兴趣,你可以继续探索。我建议在 R 中使用一个名为 lme4 的库来适应这个模型。此外,使用“brms”来尝试贝叶斯层次模型。
我能想到的另一个建模机会是对赛车进行聚类分析。我们可以试着对参赛者进行细分,看看这是否有助于预测净时间。
4 总结和讨论
这个博客提供了一个简单易懂的方法来帮助你思考和发现数据背后的洞察力,使用先进的 EDA 技术。我希望通过遵循这些步骤,您学会了结构化思维的基础知识,能够用“开放式”数据科学项目讲述一个好故事。
同时,我想指出这种方法的局限性。将变量分组为上下文变量和动态变量的方法可能不适用于每个数据集。例如,对于时间是重要考虑因素的数据集。假设我们有一个关于用户一年购物活动的数据集,我们可能没有太多的“上下文”信息(用户的特征,如年龄、性别等)。),相反,我们有与时间戳相关联的信息(即,用户何时开户或进行购买)。然后,我们需要将季节性、用户活动顺序等因素纳入建模考虑。对于 EDA 来说,这将是一个不同的话题,希望我可以很快在另一个博客上与你分享更多。
💖喜欢这个故事吗?请随意订阅 DS 和 ML 粉丝的邮件列表,成为会员!🤩
这篇博客最初发表在我的个人网站上。
参考:
1。结构化思考与分析的艺术(https://www . analyticsvidhya . com/blog/2013/06/art-structured-thinking-analyzing/)
用图形表示结构化文本
如何用图来表示自由文本,使其结构清晰明了,并且易于被下游算法管理。
文本是一种数据类型,如果使用得当,它可以成为有价值的信息来源。然而,探索文本形式的数据,尤其是自由文本形式的数据,可能具有挑战性。
自由文本缺乏明确的结构和标准化。在这篇文章中,我将向你展示如何用一个图来表示自由文本,,使它的结构清晰,易于下游算法管理。有各种自然语言处理(NLP)任务可以从自由文本的图形表示中受益,例如关键词提取和摘要。这是因为文本的隐含结构现在以图形的形式表现出来,可以用标准的基于图形的算法来探索,例如中心性、最短路径、连接部分和许多其他算法。
在这篇文章中,我将展示文本文档的 3 种不同的图形表示。这些是:
1)无向、未加权的图;
2)有向的、未加权的图;
3)有向、加权图;

从左到右,从上到下:(1)无向、无权重图;(2)有向的、未加权的图;(3)有向加权图。图片作者。
无论表示是什么,主要思想总是相同的:首先,识别文本中的实体以表示为图中的节点,其次,识别那些实体之间的关系以表示为图中节点之间的边。实体和关系的确切类型取决于上下文和任务。
实体可以是单个单词、二元模型、n 元模型、可变长度的序列等。;关系可以表示句子中实体之间的邻接、固定长度窗口中的共现、某种语义或句法关系等。

根据我们的假设,示例抽象文档的无向加权图表示。图片作者。
为了简单易懂,我将把单个单词视为节点,把视为相邻的(也就是说,它们在一个句子中形成一个二元组)视为一个关系。
尽管我将展示一个基于 NetworkX 的 Python 实现,但是所有的思想和方法都可以很容易地移植到任何其他实现中。
以下文档将是我们的运行示例(改编自维基百科):
在数学中,图论是对图形的研究,图形是用于建模对象之间成对关系的数学结构。在这种情况下,图由顶点组成,也称为节点或点,它们通过边连接,也称为链接或线。无向图和有向图是有区别的,在无向图中,边对称地连接两个顶点,在有向图中,边不对称地连接两个顶点。图形是离散数学的主要研究对象之一。
(1)无向、未加权的图
如前所述,无论用什么图形表示,的高级方法总是相同的,在这种情况下,由的 4 个步骤组成:
- 文档预处理;
- 识别实体(图中的节点);
- 识别关系(图中的边);
- 构建实际的图表。
我现在将详细介绍这些步骤(以及各自的实现)。这些步骤中的每一步的实现,尤其是实体和关系获取器,是我们为构建其他图形表示所做的改变。
主函数的 Python 实现。输入=文本文档(字符串)。Output = NetworkX 图形对象。
文档预处理
根据上面的规范,单个单词是节点,句子中的二元模型是边。因此,预处理步骤必须将文档拆分成句子,并将每个句子拆分成单词。此外,因为我们想最小化可变性,它还必须将所有文本小写(例如,单词“hello”和“Hello”被认为是同一个术语)。因为我们在任务定义中忽略了标点符号,所以它们也被删除了。
识别实体(图中的节点)
根据我们的定义,单个单词是图中的节点。因此,对于文档中的每个唯一单词,必须有一个节点。
识别关系(图中节点之间的边)
根据我们的定义,如果相应的单词在输入文档的句子中形成二元模型,则图中的两个节点之间存在边。幸运的是,在预处理步骤中,我们相应地分割了文档,这有助于计算。
构建图表
现在我们有了节点和边,我们可以轻松地构建实际的图。在这个例子中,我使用 NetworkX 构建一个图形对象。我们现在可以用一个简单的函数来绘制实际的图形。

运行主函数 build_graph()后,示例文档的无方向、无权重的图形。图片作者。
(2)有向未加权图
如前所述,为了创建一个有向图,我们只需要稍微改变我们构建图的方式。
我们将两个节点之间的边的方向定义为相应单词在二元模型中出现的顺序。因此,二元组“a 图”将导致从节点“a”到节点“图”的边。幸运的是,我们之前捕获的二元模型已经考虑了文本中的原始词序(即,我们从来没有故意改变它们的顺序)。这意味着我们唯一需要改变的是构建实际图形对象(在这种情况下,用 NetworkX)的部分,告诉考虑边的方向。
因此,我们只需要更改该行:
G = nx.Graph()
To(有向图代表有向图):
G = nx.DiGraph()
我们结束了。

在运行 main 函数 build_graph()并对其进行适当修改(在本例中,只有一行)后,示例文档的有向、未加权图形图。图片作者。
(3)有向加权图
最后,最后一块对应于给图的边增加权重。在这种情况下,我们将根据相应的二元模型在文档中出现的次数对每条边进行加权。因此,如果二元模型“a 图”在文档中出现 3 次,则链接节点“a”和“图”的有向边(在该方向)的权重等于 3。
因此,我们需要改变之前定义的关系 getter,来计算每个二元模型在文档中出现的次数。
最后,修改主函数以说明新的加权边缘获取器。注意,构建的图仍然是有向图,就像之前一样,但是添加加权边的方法有稍微不同的接口(不同的名字)。

在运行主函数 build_weighted_graph()并进行适当修改后,示例文档的有向加权图。图片作者。
好了,现在我有了一个图表…我能用它做什么呢?
将自由文本转换为图形表示使文本的隐含结构显而易见。这意味着现在您可以立即访问信息,例如哪些单词最常用(度),哪些 n 元语法最常用,哪些单词最常用于信息流动(图中每两个节点之间的路径),等等。 NetworkX 提供了大量的方法应用于你的图表,提取有价值的信息。
我将在以后的文章中讨论从文本中提取信息,其中一些将主要基于我们在这篇文章中看到的图形表示。
编辑(2021 年 10 月 22 日):你可以在我的新帖中了解和看到一个例子,关于如何应用文本的图形表示进行关键短语/关键词提取。
构建您的 Dash 应用
选择正确的结构使得开发和部署 Dash 应用程序变得更加容易

引入破折号
简而言之,Dash 是一个 Python 包,允许在不使用 HTML、CSS 或 JavaScript 的情况下用纯 Python 创建“仪表板”。由于它是可编程的,它比其他仪表板选项更强大。
近年来,有一股将 Python 脚本制作成 web 应用程序的强大推动力。某种程度上,甚至 Jupyter 笔记本也在这个方向推动 Python。然而,包括 Dash 和 Streamlit 在内的新工具通过允许用户将 web 应用程序编写为脚本,使 Python 的分发变得更加容易。
为什么结构和工具很重要?
您已经完成了项目,并创建了一个应用程序来展示您的成果。现在,您想通过部署 Dash 应用程序来分享您的项目见解。本文将带您了解如何将您的应用程序构建成易于部署的形式!
如果这种工具看起来很多,确实很多。但是,如果你不愿意,你不需要把它都写出来。我们不仅在下面的回购中提供了代码,而且还将其作为模板。所以,你可以点击“使用这个模板”按钮,按照指示开始编辑app/目录中的代码。
https://github.com/edkrueger/dash-template [## GitHub-edkrueger/dash-模板
github.com](https://github.com/edkrueger/dash-template)
示例应用程序
我们将使用一个示例应用程序来展示一个项目。让我们快速看一下我们正在部署的应用程序。Erin 的项目是收集微小房屋列表、将结果存储为 CSV 并在 Dash 应用程序中可视化结果的结果。有些地块需要地图框,因此需要地图框标记。

应用程序截图(来源:作者)
如何构建你的 Dash 应用
在我们开始之前,这里有一个我们将涵盖的概述。
我们将涵盖:
- 关于 Pipenv 的内容已经足够,可以开始了
- 如何使用 Pipenv 安装依赖项
- 我们如何以及为什么安装开发依赖项
- 我们如何以及为什么使用预提交钩子
- 如何设置环境变量
- 如何构建存储库
- 如何测试我们所有的工具
Pipenv 基础知识
强烈建议使用 pipenv 包管理您的应用程序的虚拟环境,因为 pipenv 允许其他功能,例如:
- 自动获取环境资源的能力。env)文件。
- 在 Pipfile 中指定应用需求的能力,避免了在生产中使用它们的需要。
- 虚拟环境中使用的 Python 版本的规范。
pipenv 提供的功能使部署您的应用程序变得更加容易。如果您希望在此时安装 pipenv,可以通过运行以下命令来完成:
pip install pipenv
属国
在项目文件夹中,运行:
pipenv install <your app dependencies>
我们的微型家用仪表盘应用程序使用熊猫、Dash 和 Plotly。因此,为了创建我们的 pipenv 环境,我们运行:
pipenv install pandas dash plotly gunicorn
注意加了 gunicorn。将 gunicorn 包含在您的 Pipfile 中非常重要,这样,如果您希望部署到 Google 云平台应用程序引擎服务或为您的应用程序构建 Docker 容器,两者都将按预期工作。
上面的pipenv命令将创建一个 Pipfile 和一个 Pipfile.lock,包含您的应用程序的依赖项。
开发依赖性
开发依赖是应用程序在生产中运行时不需要的所有依赖,但可以增强开发人员的体验或代码质量。
我们将使用以下开发依赖项:
black将格式化你的代码。pylint会检查你的应用程序的代码风格,并对一行代码的长度,是否存在未使用的变量,导入是否正确地放在模块的顶部等提出建议。pipenv-to-requirements将您的应用需求写入 requirements.txt 文件,多家云提供商要求以此格式正确识别。pre-commit是在你的代码提交到你的 GitHub repo 之前,自动检查你的代码的工具(这里是 black,pylint 和 pipenv-to-requirements)。
可以用pipenv install --dev --pre black pylint pipenv-to-requirements pre-commit安装。
或者,从我们的模板中取出下面的代码片段,粘贴到您的 Pipfile 中并运行pipenv install --dev --pre。
[dev-packages]
black = "*"
pylint = "*"
pre-commit = "*"
pipenv-to-requirements = "*"
预提交挂钩:预提交配置. yaml
接下来,您将通过运行以下命令将pre-commit-config.yaml文件添加到您的项目中:
touch pre-commit-config.yaml
*注意:为了正确识别,该文件必须命名为 pre-commit-config.yaml !
您可以使用下面的文本轻松复制并粘贴我们的pre-commit-config.yaml 文件的文本:
repos:
- repo: [https://github.com/psf/black](https://github.com/psf/black)
rev: 19.10b0
hooks:
- id: black
- repo: [https://github.com/pre-commit/pre-commit-hooks](https://github.com/pre-commit/pre-commit-hooks)
rev: v2.5.0
hooks:
- id: check-added-large-files
- repo: local
hooks:
- id: pylint
name: pylint
entry: pylint app/
language: system
always_run: true
pass_filenames: false
- id: pipenv-to-requirements
name: pipenv-to-requirements
entry: pipenv_to_requirements
language: system
always_run: true
pass_filenames: false
如果你的文件夹不是 git repo,你需要在创建预提交钩子之前运行git init。现在,通过运行以下命令,使用pre-commit dev 依赖项(在创建虚拟环境时安装)来安装和设置 git 挂钩:
pipenv run pre-commit install
pipenv 包再次用于运行预提交钩子。你开始明白我们为什么推荐它了吗?现在pre-commit在运行git commit时会自动运行!
以下是文件在您选择的编辑器中的外观:

pre-commit-config.yaml (source: by authors)
如果你想阅读更多关于预提交钩子的内容,可以看看我们下面的文章。
环境变量
环境变量有助于提高应用程序的安全性。您可以使用环境变量引用令牌,而不是将令牌保存在脚本中。您的应用程序中使用的令牌的一些示例可能是到数据库的连接、第三方 API 等。
在我们的例子中,我们使用 Mapbox 来呈现美国的交互式 follow 地图,而不是在脚本中保存我们的令牌,我们使用一个环境变量来引用我们的令牌的密钥。
要创建一个环境变量,您将首先使用以下命令创建一个.env文件:
touch .env
接下来,创建一个.gitignore文件,并将刚刚创建的.env文件添加到.gitignore文件中,如下所示:

.gitignore (source: by authors)
在.env文件中,我们现在可以创建我们的环境变量。为此,指定变量的名称,这里命名为MAPBOX_TOKEN,并指定其值。为了安全起见,我们把令牌的密钥藏在这里了。

.env (source: by authors)
现在,当运行pipenv时,您的环境变量将自动从.env文件中获取并加载。跑步时:
pipenv run echo $MAPBOX_TOKEN
您的令牌值将打印到控制台。
如果您最初没有以这种方式设置您的令牌,您可以很容易地将您的 env 变量设置为等于您的应用程序的令牌的原始名称(此处为mapbox_access_token),以避免在您的应用程序中出现的每个地方都更改令牌名称:

app.py (source: by authors)
构建您的应用程序代码
现在,您已经拥有了部署应用程序所需的所有文件,您需要确保您的应用程序能够被理解并轻松部署到云。
创建一个名为app的文件夹,并将所有必要的文件保存到 app 文件夹中,这样您的项目结构如下所示:

Tree Structure of the Repo (source: by authors)
测试你的应用
首先在本地运行你的应用程序来确保一切按预期运行总是一个好主意!您不希望将您的应用程序部署到云后发现它在那里不工作,然后在问题与您的云部署相关的前提下进行调试,却发现问题出在应用程序本身!

Dash App in Action (source: by authors)
一旦你确认你的应用可以在你的机器上运行,你就可以把你的工作提交给 GitHub 了!
提交到 GitHub
如果您还没有创建 GitHub repo 来备份您的文件,现在就创建吧。您可以通过遵循这些说明来轻松设置 GitHub repo。将您的应用程序文件暂存并添加到存储库中:
git add pre-commit-config.yaml Pipfile Pipfile.lock app/
并承诺他们:
git commit -m "Update package management"
现在,您的应用程序已正确构建,并且所有文件都已暂存,要对所有应用程序文件运行挂钩,请运行:

The Pre-commit Hooks Running (source: by authors)
这将显示并执行您已经实现的钩子。
** 注意:预提交挂钩要求文件在运行 pipenv run pre-commit run --all-files时被分阶段发现
当使用预提交钩子时,你需要添加和存放你的文件两次,因为black和pipenv-to-requirements会对你的代码进行修改,也就是说 black 会格式化你的脚本,pipenv-to-requirements 会创建一个 requirements.txt 文件。这将导致本地存储的版本和已提交的版本不同步。
通过运行以下命令,添加刚刚通过pipenv-to-requirements钩子创建的 requirements.txt 文件:
git add requirements*
完成后,通过运行以下命令推送文件:
git commit -m "your-message"
不错!你的应用已经被结构化、清理并保存到 GitHub。
如果你想更仔细地观察和更深入地理解这个过程,看看我们下面的文章。注意:我们使用一些不同的工具,但是概念是相同的。
我们的下一篇文章将带您了解如何使用 Google 的应用引擎服务部署 Dash 应用。在这个平台上部署很方便,因为不需要对你的应用进行 Dockerize,而在部署到谷歌的云运行和计算引擎服务时,这是必要的。
我们希望你喜欢这篇文章!有关数据科学、机器学习和开发的更多内容,请查看 Edward 的 YouTube 频道并订阅我下面的邮件列表,成为第一个听到新文章的人!
https://edkruegerdata.com/subscribe [## 每当爱德华·克鲁格发表文章时,就收到一封电子邮件。
edkruegerdata.com](https://edkruegerdata.com/subscribe)
苦苦记忆 Matplotlib 和 Seaborn 参数?用这个懒人的工具。
让您在 Python 可视化中的学习不那么痛苦,更有趣和可持续
在我的数据科学之旅的早期,我知道数据可视化是需要掌握的最重要的技能之一。这是一个将我的分析结果传达给商业利益相关者的强大工具。但是,每当我想拿出一个来的时候,我总是拖着脚步。有一次,我试图用 Matplotlib 在同一个画布上制作一个直方图和一个表格,我花了将近一周的时间找出如何做,花了很多时间调整两个轴上的标签,然后才使绘图看起来对齐和像样。
虽然互联网上有非常好的关于 Matplotlib 和 Seaborn 的文档。通常,当涉及到现实生活的实践时,当定制一个情节时,我需要参考不同的来源(官方/非官方)并且可能是 StackOverflow 来创建一个。我发现这很讨厌,也是我每次拖延的原因(懒人的借口)。我相信很多像我一样的分析师都会有这个问题。
为了克服拖延症,我决定让我在 Python 可视化方面的学习更加深思熟虑和有趣。我想出一个主意,写一个小应用程序,我可以“存储”我以前使用过的图和它们的参数。那样的话,我就不需要一直参考多个来源了。
这是它的工作原理。
我将绘图方法及其参数加载到数据库中。为了说明不同类型的图,我使用了来自 Kaggle 的 Fifa 2020 数据集的精简版本。然后,我创建了一个表单(在 Flask 中)来显示参数,这样我就可以用一个图来“玩”不同的参数。我只需要按“生成图”按钮生成一个图,明白了吗?足够了说一个下面步骤的快速演示胜过千言万语。
步骤 1:
选择一个我想创建的样本图作为开始。预设参数将被预加载到表单中。在下面的例子中,我选择箱线图作为样本,将图形大小设置为(8,6),将箱线图的线宽设置为 2,最后选择调色板为“深”。

步骤 1:选择一个样地并设置参数
我可以通过调整参数来进一步定制情节。准备就绪后,单击“生成图”按钮。
第二步:
中提琴!该图根据我的参数显示。我可以做无数次的调整,生成一个情节,直到我觉得对了。

第二步:预览你的情节
一旦我对微调感到满意,向下滚动到最后一步。
第三步:
点击“点击复制代码”按钮复制源代码。然后,您可以将代码粘贴到您的 IDE 中,并进行自定义以适合您的数据集。

第三步:复制生成的源代码(自豪地)
要开始使用这个工具,请访问在setParam.com的工具,请不要客气,欢迎您对任何错误/增强或我将来可以构建的酷东西提出反馈。保持安全,享受使用!
尝试让 PySpark 在您的数据科学环境中工作时遇到困难?这里有另一种方法。
如何用 Docker 和 Jupyter Notebook 搭建 PySpark 开发环境
将配置工作留给 Docker,开始编写 PySpark 代码

作者蒙太奇
Apache Spark 是全球数据科学家日常工作中最常用的工具之一。Spark 无处不在,任何数据科学家都可以从熟悉其 API 和最佳实践中受益。
如果您需要处理来自多个来源的大量数据或与企业数据湖进行交互,那么您很可能会在数据处理管道中以某种形式使用 Spark。而且,如果您是初学者,您肯定很难设置运行 PySpark 脚本的环境。
本文将向您展示一种简单的方法来启动并运行一个全功能的 PySpark 环境,这样您会更加得心应手。
但是,在我们开始之前,让我们先看一下您需要遵循的一些要求:
- 关于 3.5 GB 可用在你的机器上;
- 安装码头工人;
- 对于 Windows 和/或 macOS 用户:在完成本教程中的步骤时,确保 Docker 桌面应用程序正在运行(绿灯)。
- 对于 macOS 用户:如果你有一台 M1 Mac,确保有苹果芯片的 Docker 桌面。否则,当您试图运行容器时会遇到问题,并且不会获得 M1 体系结构的好处;
到本文结束时,我们将拥有一个运行在 Docker 容器上的隔离环境,其中安装了功能齐全的 PySpark,并且可以访问您计算机的本地文件,所有这些都可以通过一个终端命令完成。
步骤 1:为开发环境设置 Docker
Docker 容器是一种预配置的虚拟机(1) 。开发人员和其他专业人员可以编写一个配置文件(一个 Dockerfile ),其中包含一组指令,说明如何从头开始构建这个虚拟机,从选择哪个操作系统到在其上安装什么 python 包。
任何人都可以使用docker build命令并在他们的本地环境中用手头的这个文件复制这个虚拟机。
由于 Docker 的灵活性,开源社区非常喜欢它,而且它也在工业界得到了广泛的应用。对于数据科学工作来说,这没什么不同。在 Docker 生态系统中学习自己的方式需要很长时间。
随着这种流行,很容易在互联网上的特殊存储库中找到预建的 docker 图像。幸运的是,来自 Project Jupyter 的人员已经开发了一系列 docker 映像,其中包含了在本地机器上运行 PySpark 代码所需的所有配置。在本文中,我们将使用名为jupyter/pyspark-notebook的图片。
第一步是下载并安装这个映像。我们可以用一个docker pull命令来做这件事。
# downloading the required image
docker pull jupyter/pyspark-notebook
如果一切正常,上面那行应该开始下载指定的 docker 映像并安装到您的计算机上。然后,我们可以使用docker run name-of-the-image从命令行运行它。
但是,我们将稍微改变一下这个过程,使它更容易与 Docker 环境交互。
步骤 2:智能地运行 PySpark docker 映像
Docker 容器上运行的应用程序通常被设计成独立的体验。除了其他限制之外,他们通常不能访问host机器上的数据。类似地,默认情况下,主机不能直接访问容器上下文中生成的数据。
在我们的例子中,我们希望与在 Docker 环境中创建的 Jupyter 笔记本进行交互,并将文件移入和移出 Docker 环境。
下面这个讨厌的命令显示了如何进行这种设置。不要害怕。我将解释配置的每一点,以阐明我们在做什么。
# if running on Windows (cmd.exe):
docker run -it -p 8888:8888 -v %cd%:/home/jovyan/work/projects/ jupyter/pyspark-notebook# if running on a mac or linux computer:
docker run -it -p 8888:8888 -v `(pwd)`:/home/jovyan/work/projects/ jupyter/pyspark-notebook
了解设置
当我们使用docker run时,我们可以向它传递几个参数来控制我们如何使用容器。跟在docker run语句后面的参数是这些底层控件。
第一个是-it。这意味着我们想在interactive模式下运行容器。-it允许我们查看 docker 容器运行时被编程执行的命令输出。在这种情况下,它只是运行jupyter notebook,因此我们将能够看到运行jupyter notebook命令的结果,这在以后是必要的,因为我们将复制一些打印到终端窗口的信息,并将其粘贴到我们的浏览器中。
命令的第二部分是-p 8888:8888。-p表示“出版”该参数将容器中的一个网络端口“映射”(2)到主机中的一个网络端口。在本例中,我们将主机中的端口8888映射到容器中的端口8888。语法是-p {host's port}:{container's port}。
实际上,这意味着当我们在端口8888 (localhost:8888)打开本地机器时,我们将在屏幕上看到容器发送到同一个端口的内容。
当我们用这个参数运行容器时,我们计算机中的 Jupyter 笔记本服务器将可以像从常规命令行运行jupyter notebook一样被访问。
最后是-v参数,代表 音量 。它的工作方式几乎与-p的情况相同,并且具有相似的语法。在第一种情况下,我们映射网络端口,现在我们映射卷,这是 Docker 所说的“文件目录”当我们传递参数-v (pwd):/home/jovyan/work/projects/时,我们告诉 Docker 将(pwd) (3)映射到容器中的/home/jovyan/work/projects/目录,这是当前的工作目录(换句话说,您的终端当前所在的位置)。
现在,我们可以直接从 Jupyter 笔记本界面或容器内的命令行访问当前目录,这使得移动文件变得更加容易。
步骤 3:访问 PySpark 环境
当我们按照描述运行整个命令时,我们会得到如下所示的输出。

完整命令的结果信息
结果的最后一位包含一个紧接在*?token=*之后的访问令牌。将这一整行复制并粘贴到你的浏览器中。你现在应该看到熟悉的 jupyter 笔记本界面。

在 Docker 容器中运行的 Jupyter 笔记本
我们现在在 Jupyter 上运行了一个全功能的 PySpark 环境,您可以开始了解这个流行工具的更多信息。
给来到这里的人的额外提示
如果您是 macOS 或 Linux 用户,有一种简单的方法可以将上面的命令打包成 bash 函数或别名,这样您就不必每次都复制并粘贴上面的代码行。
在您的.zshrc或.bashrc中,添加如下所示的函数定义:
*# defining a wrapper function for running the command
run_spark() {
docker run -it -p 8888:8888 -v `(pwd)`:/home/jovyan/work/projects/ jupyter/pyspark-notebook
}# on the command line then run this to update your profile:
source ~/.zshrc
# or
source ~/.bashrc*
如果您现在在命令行中运行run_spark,您会得到与上面讨论的相同的结果。
Windows 用户 : 当我找到一种简单的方法来做类似的事情时,我会把它添加到本文中。
如果您想了解更多关于 PySpark for Data Science 的信息
随时 关注我上媒每周新鲜内容!**
脚注
- (1)这是对 Docker 容器概念的过度简化,但对于本文来说,这仍然是一个很好的类比。
- (2)映射也不是真正在
-p参数中发生的事情,但是将它视为端口之间关系的映射有助于理解信息是如何流动的。 - (3)我们对 Windows 用户有同样的想法,但是我们把
(pwd)换成了%cd%。
茱莉亚一个月基础学习计划
学习 Julia 语言的基本资源和行动计划

作者图片
ulia 是麻省理工学院为高速计算开发的一种相对较新的编程语言。它比 Python、R 和 Matlab 快得多,但仍然保持了简单的语法。它的创造者在 2019 年因数值软件获得了James h . Wilkinson Prize,Julia 被许多人认为是机器学习和科学计算的未来编程语言。原因很简单:Julia 速度非常快,处理大量数据的速度比大多数其他编程语言都快。要阅读更多关于不同语言的 Julia 的性能比较,请点击这里。
我最近决定坚持下去,并花了一些时间学习 Julia 的基础知识,以便能够完成一些基本的数据科学任务并编译简单的 ML 模型。这篇文章描述了我走向茱莉亚之旅的学习计划。
我的背景
我对 Python 和 STATA 等其他一些程序有扎实的知识。从这个角度来看,学习 Julia 的基础知识很简单。Julia 的语法和 Python 非常相似,有些东西在 Julia 中甚至比在 Python 中更直观。
所以,如果你很了解 Python,并且显然有一些标准的统计学知识,那么学习 Julia 对你来说应该很有趣🙂。
朱莉娅一步一步地学习
我使用了一种从一般到特殊的方法,首先完成了两个介绍性教程,然后完成了数据处理和统计课程,转到了图形准备教程。为期 4 周的学习窗口以机器学习介绍结束。
茱莉亚语言:简明教程
https://syl1.gitbook.io/julia-language-a-concise-tutorial/
使用本教程主要是为了在计算机上安装 Julia。我运行了 JupyterLab 中所有扩展名为 IJulia 的 Julia 笔记本。本文展示了 Jupyter 的 Julia 内核的安装过程。开发者可能会在 VSCode 插件中使用 Julia 来编写 VS 代码。
Julia 介绍(面向程序员)
要开始编程,强烈推荐茱莉亚学院教程。
Julia Academy 的第一门课程涵盖基本主题,如处理字符串、数据结构、循环、函数、包介绍和绘图。在这里,你真的可以看到 Julia 和 Python 是多么的相似,因为它们之间只有很小的语法差异。所有笔记本都可以下载,这样您就可以在您的 JupyterLab 中运行它们。
Julia 负责数据科学
茱莉亚学院(Julia Academy)更高级的数据科学课程处理面向数据的人在日常工作中需要的主题。课程只展示了 Julia 中的编程实现,很大程度上忽略了背后的统计理论。同样,Python 用户可以更容易地浏览教程。
涵盖的主题有:
- 聚类、降维
- 线性代数
- 统计,优化
- 分类、回归、神经网络
- 高级绘图。
Julia 支持导入 R、Python 和 Matlab 格式的数据。还有,用 RCall 包,可以在 Julia 内部运行 R 代码进行数据科学运算。优秀的 R 库如 ggplot2 可以在 Julia 中调用,我认为这是一件非常酷的事情。

使用 ggplot2 在 Julia 中绘制散点图。来源:GitHub 上的 RCall.jl。
py call包对 Python 做同样的事情。可以从 Julia 导入 Python 模块,调用函数,从 Julia 方法定义 Python 类,在 Julia 和 Python 之间共享大型数据结构,而无需复制。
Juliaplots 教程
因为 Julia 主要是为科学计算开发的,所以它只需要几行代码就可以提供漂亮的出版图。
*http://docs.juliaplots.org/latest/tutorial/#tutorial
JuliaPlots 教程从简单的线形图绘制到更复杂的图形绘制,解释了使用不同后端和输入数据绘制属性,并提供了颜色方案、宏和动画的详细文档。你可以运行你 Jupyter 笔记本里的所有代码(安装了 IJulia 内核)。
Julia 中准备的一些图表看起来令人印象深刻:

来源: JuliaPlots 教程。
机器学习
一门更高级的茱莉亚学院课程展示了茱莉亚如何处理经典的人工智能话题,这就是 Knet 的机器学习世界。涵盖了手写数字分类、影评情感分析、递归神经网络(RNNs)语言建模、卷积神经网络(CNN)图像分类等基本课题。
在登陆它之前,您可以刷新您的机器学习技能,因为这个简短的教程假设了一些 ML 原则的基础知识。
结论
处理数据包括不断学习、阅读论文以及跟踪新的软件和技术。Julia 是一种令人兴奋的语言,因为它比竞争对手快得多,这使它非常适合机器学习和大型数据处理。我介绍的免费课程是一个很好的起点。
当然,还要查看的官方文档。
PS:你可以订阅我的 邮件列表 在我每次写新文章的时候得到通知。如果你还不是中等会员,你可以在这里加入https://medium.com/@petrkorab/membership。*
用 SQL 为 2021 年东京奥运会做准备
用 Python 查询 PostgreSQL 数据库,用 Pandas 显示结果,用 Matplotlib 可视化
现在,我比以往任何时候都更希望自己是一名职业运动员,处于人生中最好的状态,为 2021 年东京奥运会做好准备。但是,当我用枕头、毯子和非蔬菜小吃在沙发上摆好位置时,我没有为自己感到难过,而是认为这将是一个应用我的一些 SQL 知识(对 Udemy 和 Coursera 大喊)并从历史奥运数据集中学习一两件事的合适时机。四年——嗯,现在是五年——是一段很长的时间。客观地说,在去年夏季奥运会的时候,我以前从未编写过代码,对数据科学几乎是零接触。在这段时间里,我们很难在观看奥运会的时候保持正确的心态。我知道我个人只是盯着屏幕,对展示的难以想象的运动能力感到敬畏。不过,这次不会了!

来源:https://unsplash.com/photos/ZIoi-47zV88作者@kfdias
这个项目的动机是询问和回答关于奥运会如何随着时间的推移而演变的问题,包括关于女性的参与和表现、不同运动项目和不同国家的运动员体格(即身高、体重、年龄)的差异,以及通过获得的奖牌数证明的不同国家在奥运会上的主导地位。
我将使用现代奥运会的历史数据集,包括从 1896 年雅典到 2016 年里约的所有奥运会(夏季和冬季)。来源是从 www.sports-reference.com刮来的,并作为 Kaggle 数据集发布(感谢 Kaggle 用户 rgriffin )。如果您想继续,您将需要下载该数据集中的两个可用文件:athlete_events.csv 和 noc_regions.csv。大部分数据包含在前者中,但后者将有助于在我们的数据表上执行 SQL 连接,并获得国家/地区名称,而不是国家奥委会的 3 个字母代码。

Kaggle 数据集的屏幕截图
我们将使用的数据属性包括:id、姓名、性别、年龄、身高、体重、团队、noc、比赛、年份、赛季、城市、运动、事件、奖牌。注意,有几个“NA”值代表缺失的数据,因此唯一包含纯整数的列是 year 列。还要注意的是,虽然奥运会现在每两年在夏季和冬季运动之间交替举行,但情况并不总是如此,这就是为什么季节栏会很有用。
在这个循序渐进的教程中,我将带您了解不同类型的 SQL 查询,这些查询的难度和复杂性各不相同。至于在编码环境中进行设置,您需要确保在本地机器上安装了 PostgreSQL 和 PgAdmin。如果您还没有安装这些工具,并且需要帮助,请参考本文末尾的参考资料部分。接下来,您将希望通过本地主机启动一个服务器。您可以通过 psql 命令行或通过 PgAdmin 来实现这一点。默认端口可能是 5432。我将我的服务器命名为“playground ”,但是服务器的名称并不重要。请确保您记住了数据库的名称和用户名,因为我们稍后会用到它们。

PostgreSQL 服务器配置的屏幕截图
为了编写 Python 代码,您还需要导航到您喜欢的代码编辑器。为了获得最愉快的体验,我建议跳到一个空白的 Jupyter 笔记本上。在您的 Python 环境中,确保您安装了以下包: psycopg2 、熊猫和 matplotlib 。
在笔记本的第一个单元格中,粘贴以下导入内容:
import psycopg2
import pandas.io.sql as sqlio
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns # optional
plt.style.use('ggplot') # optional
我们必须做的第一件事是使用 psycopg2 库建立到 PostgreSQL 数据库的连接。
使用 psycopg2 方法创建到数据库的连接,创建表,并将数据复制到表中

从执行的 SQL 查询中取出一个结果
既然我们所有的数据都位于我们的两个关系数据库表中,我们应该能够用一个简单的 select 语句来访问它。然而,psycopg2 库的一个不太理想的方面是,以可理解的格式查看结果集有点困难。
虽然 cursor 类允许我们通过迭代或使用[**fetchone()**](https://www.psycopg.org/docs/cursor.html#cursor.fetchone)、[**fetchmany()**](https://www.psycopg.org/docs/cursor.html#cursor.fetchmany)和[**fetchall()**](https://www.psycopg.org/docs/cursor.html#cursor.fetchall)等方法从数据库中检索数据,但如果能够以表格格式查看数据,那就更好了。
幸运的是,pandas.io.sqlio类允许我们这样做。具体来说,[**read_sql_query()**](https://pandas.pydata.org/pandas-docs/version/0.16.0/generated/pandas.read_sql_query.html?highlight=read_sql_query#pandas.read_sql_query)方法有两个参数:要执行的 SQL 查询和到数据库的连接。然后,它返回一个包含结果集的 pandas DataFrame。

从 olympics 数据表中选择所有列,并使用 sqlio.read_sql_query()显示结果
好多了!现在我们知道了如何查询数据集和查看结果,让我们进入更高级的内容。在接下来的探索中,我将遵循以下每个子主题的结构:
- 研究问题
- SQL 查询
- 表格或可视化(如果适用)
- 答案/结论/发现
当我们希望在处理数据时对数量级有一个高层次的理解时,COUNT和DISTINCT函数就派上了用场。COUNT()是一个内置函数,用于检索符合查询条件的行数。DISTINCT是用于从结果集中删除重复值的运算符。
有几个队?
SELECT COUNT(DISTINCT noc) FROM olympics
甲:230 英镑
有多少种运动?
SELECT COUNT(DISTINCT sport) FROM olympics
甲:66
有多少个项目?
SELECT COUNT(DISTINCT event) FROM olympics
甲:765
下面的查询使用了WHERE谓词,它限制了结果集,评估为真、假或未知。您还将看到正在使用的ORDER BY操作符,它根据列值对结果集中的行进行排序,可以是升序,也可以是降序(默认情况下 SQL 使用升序)。最后,LIMIT操作符用于将我们的结果集限制在前 N 行。
有史以来参加比赛的最年轻的前 5 名运动员是谁?
SELECT DISTINCT name, CAST(age AS INTEGER), sport, event, noc, games, city, medal FROM olympics
WHERE age <> 'NA'
ORDER BY age
LIMIT 5

哇哦。我不知道你是怎么想的,但是在我 10 岁的时候,我嚼着金鱼饼干,试图通过我五年级的拼写测试,却没有在雅典赢得体操铜牌。帮你自己一个忙,如果你想看有史以来参加比赛的最年轻运动员的漂亮的棕褐色照片,谷歌一下“迪米特里奥斯·伦德拉”。
参加过奥运会的年龄最大的前 5 名运动员是谁?
SELECT DISTINCT name, CAST(age AS INTEGER), sport, event,noc, games, city, medal FROM olympics
WHERE age <> 'NA'
ORDER BY age **DESC**
LIMIT 5

约翰·昆西·亚当斯·沃德以 97 岁高龄参加了 1928 年夏季奥运会。虽然在那个年龄还活着并且身体健康令人印象深刻,但在前面的陈述中有一个星号,因为这 5 名年龄最大的运动员都参加了“艺术比赛”,而不是更具竞争性、体力消耗大的运动。1912 年至 1948 年间,艺术比赛是奥运会的一部分。建筑、文学、音乐、绘画和雕塑都获得了奖牌。
由于它们不再是奥运会的一部分,让我们来完善我们的搜索。实际上有两种方法可以做到这一点:
- 通过增加一个
WHERE条款,将“艺术比赛”排除在兴趣运动之外
SELECT DISTINCT name, CAST(age AS INTEGER), sport, noc, games, city FROM olympics
WHERE age <> 'NA' AND sport <> 'Art Competitions'
ORDER BY age DESC
LIMIT 5
2.使用子查询来指定我们希望结果集包含截至 2014 年仍处于活动状态的 sport
SELECT DISTINCT name, CAST(age AS INTEGER), sport, noc, games, city FROM olympics
WHERE age <> 'NA' AND sport IN (SELECT DISTINCT sport FROM olympics WHERE year >= 2014)
ORDER BY age DESC
LIMIT 5

两种方法产生相同的结果!原来我们新的最老的运动员是一位来自奥地利的 72 岁的马术运动员。尽管 Aurthur von(插入非常长的姓氏)早在 1936 年就参加了比赛,但老年比赛似乎在马术比赛中仍然很常见(见第四行)。
有史以来参加比赛的最重的前 5 名运动员是谁?
SELECT DISTINCT name, CAST(weight AS DECIMAL), sport, event, noc, games, city, medal FROM olympics
WHERE weight <> 'NA'
ORDER BY weight DESC
LIMIT 5

重量级男子柔道和超重量级男子摔跤是最重运动员参加的运动,这并不奇怪。这里,重量以千克为单位。对于美国人来说,214 公斤相当于 472 磅。你可能还想谷歌一下“小里卡多·布拉斯”来获得完整的效果!对身高和体重的研究让我想起了体育运动有许多不同的形式……而且围绕着即将到来的东京奥运会的纸板床的神话几乎肯定会被揭穿。
GROUP BY允许我们根据某个类别聚合列。它们通常出现在FROM或WHERE语句之后。一个棘手的规定是,如果我们想在使用GROUP BY后过滤我们的聚合结果,我们不能在其后放置一个WHERE谓词。这样做的原因是因为一个WHERE谓词在之前被执行,结果集与GROUP BY聚合。解决方法是在GROUP BY语句后使用HAVING条件,在结果被聚合后过滤结果。
随着时间的推移,可视化每个地区每个运动项目的运动员人数
让我们利用GROUP BY子句通过几种不同的分类分组方案来计算运动员的人数。由于sqlio,我们一直将结果集视为 pandas 数据帧,因此我们可以轻松地制作 matplotlib 条形图,进行更强大的数量级比较。首先,我们可以执行一个简单的查询来了解一段时间内有多少运动员参加了夏季奥运会。
SELECT year, COUNT(*) AS num_athletes FROM olympics
WHERE season = 'Summer'
GROUP BY year

参加夏季奥运会的运动员数量似乎相对呈线性增长。自 1896 年第一届奥运会以来,当时只有不到 1000 名运动员参赛,现在运动员人数已经增加到将近 14000 人。不幸的是,我们很有可能在 2021 年看到一个明显的下降,因为许多运动员要么在新冠肺炎的测试中呈阳性,要么由于疫情经历了一些其他情况,使他们无法参加比赛。接下来我们来看看每个运动项目的运动员人数,按照运动员最多的运动项目排序。
SELECT sport, COUNT(*) AS num_athletes FROM olympics
GROUP BY sport
HAVING COUNT(*) > 5000
ORDER BY COUNT(*) DESC

我们可以看到,运动员人数最多的前三项运动是田径(也称为田径)、体操和游泳。这些运动的特点是项目众多,而且主要是个人运动。
为了查看哪些地区派出了最多的竞争对手,我们几乎可以复制相同的查询,只是将“noc”而不是“sport”作为分类列。这里的一个警告是,我们希望能够看到完整的国家/地区名称,而不是 3 个字母的国家奥委会缩写。因此,我们将使用较小的数据集ON执行LEFT JOIN操作,即“noc”列。A LEFT JOIN返回左表中的所有行,即使右表中没有匹配的行。
SELECT region, COUNT(*) AS num_athletes FROM olympics
LEFT JOIN noc_regions
ON noc_regions.noc = olympics.noc
GROUP BY region
HAVING COUNT(*) > 5000
ORDER BY COUNT(*) DESC

派出最多运动员参加奥运会的前两个国家是美国和德国,每个国家总共有 15,000 多名运动员。法国、英国、俄罗斯和意大利各派出 1 万多名运动员。我个人对加拿大、日本和澳大利亚没有排在前列感到有点惊讶。众所周知,加拿大参加冬季奥运会的人数很多,但也许它参加夏季奥运会的运动员较少。我假设在这个结果集中,日本和澳大利亚没有其他大国那么多的田径运动员。
在这一节中,我将在使用GROUP BY和LEFT JOIN的基础上引入一个新的主题:子查询!子查询允许您构造复杂的查询,实质上是对另一个查询的结果执行一个查询。该语法涉及两个 SELECT 语句;首先执行括号中的代码(子查询),其结果集用作主(外部)SELECT 语句的筛选表。
女篮的世界是怎么回事?
这个问题是针对那些我个人认为没有得到足够认可的女球员的。我最近了解到,他们已经取得了相当大的连胜,所以我开始验证这一点,并在全球舞台上发现一些相关的背景。以下 SQL 查询确定了在过去 10 场比赛中哪个国家获得了女子篮球金牌。
SELECT year, region AS gold FROM olympics
LEFT JOIN noc_regions
ON olympics.noc = noc_regions.noc
WHERE sport = 'Basketball' AND sex = 'F' AND medal = 'Gold'
GROUP BY year, region
ORDER BY year DESC

美国女篮自 1992 年以来还没有尝过失败的滋味,那是连续 6 块金牌!客观地说,在我出生之前,他们就已经是卫冕冠军了。令人印象深刻,但我现在需要知道更多。让我们做一些调查,找出他们过去 10 场比赛的亚军是谁。这就是子查询发挥作用的地方。
当FROM子句中的表名被子查询替换时,子查询就形成了所谓的派生表。派生表可以被赋予别名,以使选择列不那么冗长。当从两个不同的(派生的)表中选择列时,我们必须指定一个WHERE谓词,表明它们共享相同的索引列,否则将执行交叉连接,这将产生比我们预期的多得多的行。
SELECT G.year, G.gold, S.silver FROM
(SELECT year, region AS gold FROM olympics
LEFT JOIN noc_regions
ON olympics.noc = noc_regions.noc
WHERE sport = 'Basketball' and sex = 'F' and medal = 'Gold'
GROUP BY year, region) G,
(SELECT year, region AS silver FROM olympics
LEFT JOIN noc_regions
ON olympics.noc = noc_regions.noc
WHERE sport = 'Basketball' and sex = 'F' and medal = 'Silver'
GROUP BY year, region) S
WHERE G.year = S.year
ORDER BY year DESC

有意思——西班牙和法国分别在最近两届夏季奥运会上获得银牌。在此之前,澳大利亚是美国女篮连续三场比赛的亚军。尽管从理论上讲,她们正处于几十年来的连胜状态,但美国女子篮球队最近在奥运会前的一场表演赛中以 70 比 67 输给了一个缺少明星球员的澳大利亚队,这可能是一个值得警惕的原因。
谁是美国女子篮球队的关键球员?
SELECT name, COUNT(*) FROM olympics
WHERE sport = 'Basketball' and sex = 'F' and medal = 'Gold' and noc = 'USA'
GROUP BY name
HAVING COUNT(*) >= 3
ORDER BY count DESC

为了让这个小小的探索更进一步,研究这些年来让球队背负重担的传奇人物的名字总是很重要的。丽萨·莱斯利、苏·伯德、戴安娜·陶乐斯、塔米卡·凯金斯和特蕾莎·爱德华兹各自为美国赢得了 4 次金牌,这对于任何一个精英运动员来说都是一个巨大的职业成就,性别除外。
本节将涉及一些由多个子查询组成的相对复杂的查询。如果您是 SQL 新手,请不要害怕。解释图表是这一分析中更重要的部分。
也就是说,如果您是一名 SQL 专家或者正在努力成为一名 SQL 专家,本节将介绍聚合函数的使用,比如MIN()、MAX()和AVG(),这些函数接受一个列名,并在执行数学运算后返回计算出的值。由于聚合函数只能应用于由数字数据组成的列,因此CAST操作符在这里会派上用场,使我们能够从一种数据类型(字符串)转换为另一种数据类型(整数或小数)。
按运动项目分析运动员体格(身高和体重)
与过去的 SQL 查询不同,有时我们不一定知道我们希望突出显示数据中的哪些关系。在这些场景中,能够将我们的数据集过滤到一个可管理的大小,并将整个结果集放入一个探索性的seaborn绘图函数中,比如蜂群绘图,这很好。群体图能更好地表示值的分布,但它不能很好地适应大量的观察结果。在这里,SQL 查询将我们的结果集缩小到参加了 2012 年或 2016 年夏季奥运会、参加了一项“流行”运动并获得了一枚奖牌的运动员。
SELECT sport, CAST(height AS INTEGER) FROM olympics
WHERE sport IN (
SELECT sport FROM olympics
GROUP BY sport
HAVING COUNT(*) > 3000
)
AND height <> 'NA'
AND season = 'Summer'
AND year >= 2012
AND medal <> 'NA'
ORDER BY sport


上面的群体图描述了高水平运动员的身高和体重分布。凭直觉,运动员最高的项目似乎是篮球、手球和排球,而运动员最矮的项目似乎是体操、举重和摔跤。
就体重而言,我们看到摔跤、举重、柔道和拳击的分布更广,因为这些项目都有重量级别,运动员会有意识地训练他们的身体。田径运动描述了一点尾巴效应,这可能是因为许多投掷项目需要大量的力量。体操、曲棍球、足球、游泳分布最集中。
在分析这个数字时,我想到我没有按性别来区分数据。与其制作更多的群体图来解决这个问题,不如让我们应用一些集合函数,用两个系列的数据制作一个散点图,一个是雄性的,另一个是雌性的。完成这一任务的复杂 SQL 查询已经成为感兴趣者的要点:

看着上面的体重散点图,我们可以收集到一些有趣的发现。最引人注目的是,女性拳击手的平均体重非常接近男性拳击手的平均体重,我们在举重中也看到了类似的趋势。也许这和肌肉量或者重心低有关,但只能推测。对于花样滑冰来说,根据体重数据,普通的女运动员更娇小、更瘦,而普通的男运动员则更重。这具有直观的意义,因为男性花样滑冰运动员在运动中经常举起他们的女性伙伴,所以他们必须有更强壮的体格。最后,体操运动员是所有运动员中最轻量级的也就不足为奇了。
只是为了好玩,看看奖牌获得者按运动分类的年龄群图可能会很有趣。身体什么时候能发挥到巅峰?什么时候是退役的时候?

在我们的“流行”运动中,从青少年到 40 岁出头似乎是奥运会奖牌获得者,更一般地说,是精英运动员的标准职业生涯。也就是说,如果你是马术或射击运动员,不要担心年龄!你可以经历中年危机,但仍然追求黄金。另一方面,如果你的运动是体操、柔道或举重,30 岁出头似乎是放弃的好时机。也许重冲击运动对一个人的关节有“磨损”效应,使其多年难以高水平发挥。说到这里,让我们看看所有运动项目中奖牌获得者的年龄分布,不分性别。
SELECT CAST(age AS INTEGER), COUNT(*) AS num_medalists
FROM olympics
WHERE age <> 'NA' AND medal <> 'NA'
GROUP BY age

一个漂亮的正态分布!23 岁似乎是获得奥运奖牌的最佳年龄。这种分布有一个右偏——一个长右尾——意味着在 20 多岁时追逐奖牌的几率仍然相当高,然后随着一个人接近 40 岁或更高,它们缓慢但肯定会直线下降。
我们将采用的最后一个 SQL 课程是CASE语句,它本质上类似于其他编程语言中的“if-else”语句。它使我们能够对列应用条件逻辑,并将数据转换为对我们的任务更有意义或更方便的值。
谁收集了有史以来最多的硬件?
最后,也许是最重要的,是时候看看谁在奥运会历史上收集了最多的奖牌。我们将在奖牌栏中应用CASE语句,以计算加权和。我们希望考虑运动员获得的金牌、银牌和铜牌的数量,以一种符合逻辑的方式对它们进行缩放,而不是笼统地计算获得的奖牌数量。
SELECT name, sex, noc, sport, SUM(CASE medal
WHEN 'Gold' THEN 3
WHEN 'Silver' THEN 2
WHEN 'Bronze' THEN 1
ELSE 0
END)
FROM olympics
GROUP BY
name, sex, noc, sport
ORDER BY sum DESC
LIMIT 10;

迈克尔·菲尔普斯和帕沃·鲁米是我唯一认识的名字,但这很酷。在这十大奖牌获得者中,有十分之八是男性,并且有很多来自美国和俄罗斯的代表。还有很多代表来自三大运动:田径、体操和游泳!如果我们不列出有史以来前 10 名女奥运选手,这个项目会感觉不平衡,让我们继续下去,尽快完成。

哪些国家在某项运动中表现出了奥运优势?
为了构建这个查询,我们将使用前面提到的相同的加权和列,以金牌、银牌和铜牌的组合来衡量成功。从结果集中,我们将能够识别出最值得记住的配对(国家→运动),然后才能进行为期 3 周的持续运动!
SELECT region, sport, SUM(CASE medal
WHEN 'Gold' THEN 3
WHEN 'Silver' THEN 2
WHEN 'Bronze' THEN 1
ELSE 0
END)
FROM olympics
LEFT JOIN noc_regions
ON noc_regions.noc = olympics.noc
GROUP BY sport, region
ORDER BY sum DESC
LIMIT 8;

如果你想在这个周末的客厅里听起来很聪明,只要记住美国队以其在游泳、田径、篮球和赛艇上的成功而闻名;德国以赛艇成功著称;俄罗斯在体操领域大放异彩;冰球场上的加拿大;游泳池里的澳大利亚。
跟着走
这是一个总结!如果你能走到这一步,非常感谢。距离我的上一篇文章已经有一段时间了,但是我计划继续做一些项目,同时将我推出舒适区,并帮助我对感兴趣的领域有更多的了解。如果您有任何建议或反馈,请不要犹豫,通过电子邮件与我联系。
对于那些想要复制这项工作或按照自己的方向进行的人,这里是包含所有代码和数据的 GitHub 存储库:
https://github.com/sejaldua/sql-olympics
请随时在 LinkedIn 上与我联系,或者查看我的投资组合网站!
参考
https://www.postgresqltutorial.com/install-postgresql/ https://www.pgadmin.org


浙公网安备 33010602011771号