理解 AdaSum:用于扩展分布式训练的自适应求和方法
深度神经网络的数据并行分布式训练将训练数据集划分为 N 个子集,其中 N 个计算节点中的每一个训练数据的子集。通过设置小批量等于单节点训练中的小批量,我们实质上将有效批量增加了 N 倍,从而提高了训练吞吐量。
然而,增加有效批量将对【1】中讨论的训练准确性产生负面影响。[1]中提出的解决方案是将学习速率与总批量成比例。然而,它仍然会达到一个上限,在该上限处,即使学习率缩放,训练也不能收敛。
论文【2】标题为自适应求和缩放分布式训练对造成单节点训练和分布式训练精度差异的一个关键因素给出了非常直观的解释。然后,提出了一种基于下降方向对梯度求和的增强算法,并表明它可以帮助减少 30%的收敛时间。
动机示例
图一。两个数据批次上的 SGD。
图二。分布式训练中两个数据批次的 SGD。
图一。显示了对单个节点上的两批数据顺序执行梯度下降的示例。从 w0 开始,在第一步执行梯度下降后,损耗移动到w1;并在第二步后从 w1 移动到 w2 。图二。显示了使用相同的批处理大小,但同时在两个训练节点上执行梯度下降的示例。在这种情况下,两个训练节点上的梯度下降都开始于 w0 。一步之后,一个节点中的损耗在 w1 处结束,另一个节点中的损耗在 w3 处结束。在分布式训练中,来自两个节点的梯度的算术平均值被应用于原始权重 w0 ,并且得到的权重*w2’被用于更新两个节点上的权重。在大多数情况下,这个更新的权重w2’*将不同于 w2 ,作者在【2】中认为这是单节点训练和分布式训练之间准确性差异的来源。
论文接着研究了 w2 和*w2’差异最大的情况。他们在图 3 中观察到。以下说明当 w1 和 w3 为两个正交向量时,可以忽略 w2 和w2’之差;但当它们沿同一方向时, w2 和w2’*之间的差异非常显著。
图 3。梯度向量求和的不同情况。
基于这一观察,该论文建议我们应该考虑梯度向量之间的相关性,而不是使用简单的 allreduce 运算来聚合梯度。作者提出了一种称为 AdaSum 的方法,当梯度向量正交时相加,当梯度向量方向平行时执行算术平均。关于这个算法的更多细节,请参考原始论文[2]。
结果
作者在[2]中表明,使用 AdaSum 运算,BERT 大训练比常规 Allreduce 收敛速度快 30%。与 *NCCL 缩减法、*相比,该操作符大约有 10~15%的延迟开销,但这通常只是总步进时间中微不足道的一部分。 AdaSum 操作符在 Horovod 分布式培训库中实现并准备使用。
[1] Priya Goyal 等人,精确、大型迷你批量 SGD:1 小时训练 ImageNet(2017),CVPR 2017。
[2] Saeed Maleki 等人,自适应求和的缩放分布式训练 (2021),MLSys 2021。
理解阿尔法、贝塔和统计能力
如何在假设检验中最小化错误和最大化结果
托马斯·凯利在 Unsplash 上的照片
介绍
对于任何有抱负的数据科学家来说,知道如何设置和进行假设检验是一项关键技能。起初,试图理解 alpha、beta、power 和 I 型或 II 型错误可能会感到困惑。我在这篇文章中的目标是帮助你建立直觉,并提供一些视觉参考。
首先,让我们设想建立一个标准的 A/B 实验,其中 A 组是控制组,B 组是实验组。我们的零假设是两组是相等的,应用于 B 组的变化没有显著影响(A = B)。我们的另一个假设是,这两个组是不一样的,应用于 B 组的改变实际上引起了显著的差异(A ≠ B)。我们可以将采样分布形象化为如下形式:
作者创造的形象
置信水平和 Alpha
置信水平 (CL)是指在我们拒绝零假设之前,我们希望有多确定(在我们说实验有重大影响并实施 B 组的改变之前,我们希望有多确定)。这是事先挑选的,以概率百分比表示。为了拒绝空值,我们要有 95%的把握吗?也许我们需要 99%的把握。置信水平将取决于你的测试,以及如果你错了,后果会有多严重。通常,标准的起始置信水平值为 95% (.95)。
α值表示为 1-CL。如果置信水平是 0.95,那么 alpha 值将是 0.05 或 5%。这表示当零假设实际上是正确的时候,我们愿意拒绝它的概率。换句话说,如果我们有 5%的 alpha 值,那么我们愿意接受 5%的可能性,即当实际上没有差异时,我们会得出有差异的结论。像这样的错误被称为假阳性或第一类错误。让我们再看一遍我们的图片,获得一个视觉上的直觉。
作者创造的形象
置信度/alpha 值创建了一个决策边界。高于边界的值将被视为分布 B 的一部分并支持替代假设,低于边界的值将被视为分布 A 的一部分并支持零假设。现在,您可以从图表的阴影部分看到,alpha 值如何表示我们愿意错误归类为分布 b 一部分的值的百分比。我们必须设置这一决策界限,并为这些错误答案做好准备,因为两个分布之间存在重叠,会产生歧义。阴影部分是那些事实上支持零假设的值(分布 A 的一部分),但我们误认为支持替代假设(我们将它们误解为分布 B 的一部分)。这就是为什么我们称它们为假阳性——它们错误地支持阳性检测。为了更好地理解这一点,如果我们的置信水平为 95%,alpha 为 5%,这意味着图像中的阴影区域将是分布曲线 a 下面积的 5%。
统计功效和β
假设检验的功效是检验将正确支持替代假设的概率。换句话说,功效是属于分布 B 的条目被正确识别的概率。功率计算为 1-β。那么什么是β呢? Beta 是我们接受零假设的概率,即使另一个假设实际上是真的。在我们的例子中,这是我们将一个值误认为是分布 A 的一部分的概率,而它实际上是分布 b 的一部分。标准的功率度量通常是 0.8 或 80%,这使得β为 0.2 或 20%。同样,在决定适合你的测试级别时,你需要考虑你自己的测试。让我们想象一下贝塔。
作者创造的形象
阴影区域代表β。您可以看到这些值是如何被认为是分布 A 的一部分(支持零假设),从而得到一个否定的测试结果。这就是为什么它们被认为是假阴性。这种类型的测试误差被称为II 型误差。
组装在一起
在了解了α和β之后,你就可以看到这两者之间往往是一种平衡。如果我们想避免假阳性或 I 型错误,那么我们可以提高我们的信心水平。但是我们在避免假阳性方面越严格,那么我们得到假阴性或 II 型错误的可能性就越大。在尝试解决这个问题时,您可以考虑以下几点:
- 您可以考虑您的特定测试,以及哪种类型的错误会更糟糕。想象一个新冠肺炎测试作为例子。如果你要对某人进行新冠肺炎病毒测试,假阴性比假阳性更糟糕,因为某人携带病毒但被告知没有携带(假阴性),可能最终会感染许多其他人,因为他们认为自己没有携带病毒。最好是错误地将人们识别为病毒携带者(假阳性),因为最糟糕的情况是,即使没有必要,他们也会呆在家里进行隔离。因此,新冠肺炎的测试应该优先考虑较低的 beta 值。但是,也许一家测试网站的公司会希望最大限度地减少 I 类错误或误报,因为做出改变可能是一项充满风险的昂贵努力,除非他们非常确定它会产生预期的积极影响,否则不值得。他们可能会错过一些机会,但他们会避免代价高昂的错误。通过考虑你的特殊问题,你可以最小化或最大化对你最重要的错误。
- 对于你的问题,你可以考虑你所关心的最小差异量。假设你正在做一个测试,看看一个网站的改变是否会增加转化率。如果转换率只有微小的变化,就很难发现假设检验中的差异,你也更有可能出错。通过设置一个阈值来衡量指标需要改进多少才能值得进行更改,您将创建一种情况,在这种情况下,两个分布之间的重叠会更少,模糊和错误的区域也会更小。如果在实验中测量的变化非常小,那么产生错误的风险更高,因为两个分布之间可能有更多的重叠。
- 你可以考虑增加样本的大小。通过在您的实验中使用更大的样本量,您的实验更容易找出两种分布之间的差异。如果小的差异对你来说仍然很重要的话,用大量的样本也更容易发现小的差异。你也可以使用互联网上的计算器,根据期望的 alpha、功效和最小效果大小,计算出所需的样本大小。
结论
我希望这是对这些不同因素以及它们如何相互影响的充分解释。你的家庭作业是在网上玩一个互动工具来帮助你建立直觉。如果这些概念现在让你感到困惑,那么要知道它们一开始让每个人都感到困惑。只需要一点点练习。
了解最接近的政策优化(舒尔曼等人,2017 年)
作为一名初学者,我是如何自下而上地接触 PPO 文件的
近年来,政策梯度方法的研究一直很流行,TRPO、GAE 和 A2C/A3C 等算法显示了优于 Q-learning 等传统方法的最新性能。这个策略梯度/行动者-批评家领域的核心算法之一是 OpenAI 实现的近似策略优化算法。
在这篇文章中,我试图做到以下几点:
- 通过对政策梯度方法和信赖域方法(TRPO)提供一个初学者友好的概述来讨论 PPO 背后的动机
- 了解 PPO 的核心贡献:削减替代目标和多时代政策更新
目的
破坏性策略更新
我们首先需要理解如下定义的策略梯度方法的优化目标:
政策损失函数(舒尔曼等人,2017 年)
策略 pi 是我们的神经网络,它将来自环境的状态观察作为输入,并建议要采取的行动作为输出。优点是对当前状态下所选动作的相对值的估计,因此 hat 超过 A。它被计算为折扣奖励(Q) —价值函数,其中价值函数基本上给出了奖励折扣总额的估计值。在训练时,这个代表价值函数的神经网络将经常使用我们的代理在环境中收集的经验进行更新。然而,这也意味着值估计会由于网络引起的变化而非常嘈杂;网络并不总是能够预测该状态的精确值。
将政策产出和优势函数的对数概率相乘,给了我们一个巧妙的优化函数。如果优势为正,意味着代理人在样本轨迹中采取的行动导致了高于平均水平的回报,政策梯度将为正,以增加我们在遇到类似情况时再次选择这些行动的概率。如果优势是负的,政策梯度将是负的,以做完全相反的事情。
在一批收集的经验中不断地执行梯度下降步骤是很有吸引力的,它将经常更新参数,使之远远超出导致**“破坏性大的策略更新”的范围**
信任区域策略优化
防止这种破坏性策略更新的方法之一是 信任区域策略优化(舒尔曼等人,2015) 。在本文中,作者实现了一种算法来限制策略梯度步骤,使其不会偏离原始策略太多,从而导致经常完全破坏策略的过大更新。
首先,我们将 r(θ)定义为当前策略下的动作与先前策略下的动作之间的概率比。
舒尔曼等人,2017 年
给定一系列采样动作和状态,如果特定动作对于当前策略比对于旧策略更有可能,则 r(θ)将大于 1。对于我们当前的策略,当行动的可能性较低时,它将介于 0 和 1 之间。
现在,如果我们将这个 r(θ)乘以前面提到的优势函数,我们会以更易读的格式得到 TRPO 的目标:
(舒尔曼等人,2017 年)
在这个 TRPO 方法中,我们注意到它实际上非常类似于左边的普通政策梯度方法。实际上,这里唯一的区别是 log 运算符被替换为当前策略的动作概率除以前一个策略下的动作概率。优化这个目标函数在其他方面是相同的。
此外,TRPO 添加了 KL 约束,以限制梯度步长使策略远离原始策略。这导致梯度停留在我们知道一切正常的区域,因此命名为“信任区域”然而,已知这个 KL 约束增加了我们的优化过程的开销,这有时会导致不期望的训练行为。
近似策略优化(PPO)
剪切替代目标
基于上述动机,近似策略优化试图简化优化过程,同时保留 TRPO 的优点。本文的主要贡献之一是删减的替代目标:
删减的替代目标(舒尔曼等人,2017 年)
这里,我们计算最少两项的期望值:正常 PG 目标和削波 PG 目标。关键部分来自第二项,其中正常的 PG 物镜被 1-ε和 1+ε之间的限幅操作截断,ε是超参数。
由于 min 运算,当优势估计为正或负时,此目标表现不同。
(舒尔曼等人,2017 年)
让我们首先来看看左图,它描绘了积极优势:当选定的行动对结果产生了比预期更好的影响时的情况。在图表中,当 r 变得太高或者当在当前政策下比在旧政策下更有可能采取行动时,损失函数变平。我们不想因为走得太远而使动作更新过度,所以我们“剪辑”了目标以防止这种情况,同时用一条平线阻挡渐变。
当优势估计为负时,这同样适用于右图。当 r 接近零时,损失函数将变平,这意味着在当前政策下不太可能采取特定行动。
尽管这种方法很聪明,但裁剪操作也有助于我们“撤销”政策的错误。例如,右图中突出显示的部分显示了这样一个区域,在该区域中,最后一个梯度步骤使所选操作更有可能发生,同时也使策略变得更糟,如负优势所示。令人欣慰的是,我们的裁剪操作将友好地告诉梯度走在另一个方向,与我们搞砸的数量成比例。这是唯一一个min()
里面第一项比第二项低的部分,作为备用方案。最棒的是,PPO 无需计算额外的 KL 约束就能完成所有这些工作。
所有这些想法都可以在最终损失函数中进行总结,将这个删减的 PPO 目标和两个附加项相加:
(舒尔曼等人,2017 年)
c1 和 c2 是超参数。第一项是负责更新基线网络的价值函数的均方误差。第二个术语,可能看起来不熟悉,是一个熵术语,用于确保我们的代理有足够的探索。这个术语将推动政策更自然地运行,直到目标的另一部分开始起主导作用。
策略更新的多个时期
最后,让我们一起来看看算法及其并行演员的优点:
PPO 算法(舒尔曼等人,2017 年)
该算法由两个大线程组成:米色线程和绿色线程。米色线程收集数据,计算优势估计值,并为绿色线程提供小批量样本。一个特别之处是:这些是由 N 个并行的角色各自独立完成自己的任务。
对样本运行梯度下降的多个时期并不常见,因为存在破坏性的大规模策略更新的风险。然而,在 PPO 的剪切代理目标的帮助下,我们可以利用并行参与者来提高采样效率。
每隔一段时间,绿色线程就会启动,对我们的削波损失函数运行随机梯度下降。又一次特殊拍摄?我们可以在相同的轨迹样本上运行 K 个优化时期。在 PPO 之前也很难做到这一点,因为在本地样本上采取大步骤的风险,但 PPO 防止了这一点,同时允许我们从每个轨迹中了解更多。
参考
最初发表于
https://blog . tylertaewook . com/posts/proximal-policy-optimization
理解和解释 Python 中的数据
Python 中的探索性数据分析
弗兰基·查马基在 Unsplash 上拍摄的照片
作为数据科学家,我们都必须处理大量不同的数据集。要在数据集上拟合任何预测模型,我们需要先了解数据集的复杂性,然后再决定使用哪个预测模型来获得最佳性能。但是,数据集通常很大,手动理解数据集中的每个数据点是不可能的。因此,我们需要一些度量和可视化来帮助我们理解数据的本质,并给我们一个简要的概述。
探索性数据分析(EDA)对数据进行分析和可视化,以从中获取洞察力。它可以被描述为一个总结数据的重要特征以便更好地理解的过程。为了了解 EDA 的过程,我们将使用住房数据集,可在这里获得。我们将详细了解 EDA 的每个必要步骤,为什么需要它,以及如何实现它。
我们还将通过对数据集执行探索性数据分析来探索数据集。本博客涉及的主要话题如下:
- 统计洞察力
- 删除重复项
- 异常值处理
- 单变量分析的可视化
- 二元分析的可视化
统计洞察力
这里我们将分析数据统计,即均值、方差、分位数、唯一元素等。在我开始之前,让我们先看看 python 代码需要的库,以及如何加载数据。
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as npdf = pd.read_csv("train.csv")
为了简要了解数据,我们将检查以下参数:
- 数据集的前 5 行
- 存储在每列中的变量的数据类型。
- 平均值、中间值和其他统计属性,
- 每行中的唯一元素。
要获得前 5 行,请使用以下命令:
df.head()
作者图片
输出如上图所示。这个函数为我们提供了关于数据集中存在的列以及每一列中存在的值的基本概念。
为了更好地理解每一列中的值的数据类型,我们使用下面的命令。
df.info()
作者图片
这个列表很大,有 81 列。但我只给出了前 10 列的截图。
下一步是检查数据的统计汇总,我们使用下面的函数。
df.describe()
作者图片
该函数的输出具有以下形式:
它在列中显示计数、平均值、标准差、最小值、25%百分点值、中值(50%百分点值)、75%百分点值和最大值。这里解释一些不太常见的术语:
- Count 列中存在的非空值的总数。
- 25%百分位— 25%的数据小于该值
- 50%百分位— 50%的数据小于该值,也与中位数相同
- 75%百分比— 75%的数据小于该值
现在我们已经有了关于每一列的基本信息,让我们看看如何找出数据集中某一列的唯一元素。
df["OverallQual"].unique()
作者图片
标题为“总体平等”的列只有很少的独特元素。列中的元素是离散的,最大值为 10,这可以从上图中得到验证。
删除重复项
所谓重复,我指的是数据集中的两行,每一列都包含相同的值。在数据集中有重复项是不可取的,这通常会导致过度拟合。因此需要移除它们。
df.duplicated().sum()
作者图片
df.duplicated()提供了一个布尔列表,指示一行是否重复。然后 sum()将给出数据集中重复行的总数。在我们的例子中,它是 0,如图所示。但是如果有重复的行,可以按如下方式删除它们
df.drop_duplicates(inplace=True)
异常值处理
异常值是一般情况下不会出现的极端观察值,只有在非常特殊的情况下才会出现。我们训练的预测模型实际上是用来寻找一般情况下的输入和输出之间的关系,而不是特殊情况。因此,使用异常值来训练这种预测模型可能会导致性能恶化。因此,我们必须消除它们。
找出异常值的标准过程是
- 找到 Q1 和 Q3 四分位数。
- 将两个列表相减,找出内部四分位数范围( IQR )。
- 任何小于Q1-1.5 iqr或大于 Q3+1.5IQR 的点都被视为异常值。
为了找出异常值,我们将使用“销售价格”列。
作者图片
因此,数据集中出现的异常值总数为 61,我们应该在使用数据集进行训练之前删除它们。
处理缺失数据
缺失数据意味着数据集中几乎没有未填充的位置。首先,我们来看看如何找出缺失的数据。
df.isnull().sum()
作者图片
df.isnull()提供一个布尔数组,然后。sum()给出列中存在的空值的总数。由于有很多列,我不能在这个截图中显示所有的信息。
那么我们如何处理这些丢失的数据呢?这里,我将只讨论包含整数和浮点类型变量的列。如果一个列包含字符串,那么所有这些方法可能都不适用。
- Drop — 我们将删除丢失的值,也就是说,如果特定行中有任何丢失的值,将删除整行。此方法仅适用于缺少值的行数非常少的情况。
df.dropna(subset=["BsmtQual"], inplace=True)
它将删除列“BsmtQual”包含空值的行。
- 用统计参数估算— 用平均值、中值或众数替换缺失值。这里通常使用平均值,但是在这一步之前检查异常值的数量是非常可取的。如果有很多异常值,那么使用中位数更为可取。
df["$NA"][df["$NA"].isnull()] = df["$NA"].dropna().mean()
将$NA 替换为要用 mean 替换其中缺失值的列名。
- 插值— 根据一列对数据集进行排序,“最重要的一列”,这通常是您想要预测的列。然后使用线性插值来填充缺失的值。
单变量分析的可视化
单变量分析是分析数据的最简单形式。这里“uni”代表一个变量。因此,在这里我们将看到如何可视化和分析一列数据。我们将讨论以下可视化
- 箱形图
- 柱状图
箱形图
箱形图仅用一个数字就包含了大量关于数据的统计信息。
方框图描述(图片由作者提供)
箱线图告诉我们中值、上四分位数、下四分位数、最大值、最小值和异常值,因此涵盖了所有的统计参数。
sns.boxplot(df["SalePrice"])
作者图片
对于我在这里展示的情况,方框图是水平的。请注意,所示的箱形图在下部区域有异常值。
柱状图
直方图显示了变量的频率分布。它将数值数据划分到称为“箱”的桶中,然后计算落入每个箱中的数据点的数量。因此,纵轴实际上代表每个箱中数据点的数量。
我们将可视化数据集中的“BedroomAbvGr”列。
作者图片
上面的直方图基本上表明,在给定的数据集中,大多数房子都有 3 间卧室。
二元分析的可视化
双变量分析用于理解数据集中存在的两个或多个变量之间的关系。例如,随着房屋整体质量的变化,分析房价的变化。让我们看看如何直观地理解两个变量之间的关系。我们将要讨论的可视化如下:
- 相关矩阵
- 回归图
相关矩阵
相关性用于衡量一个变量相对于另一个变量的变化程度。为了更好地理解这一点,考虑上面的例子。房子的价格会随着整体品质的变化而发生怎样的变化?我们基本上是找到数据集中存在的所有数值变量之间的相关性来构造相关矩阵。
作者图片
混淆矩阵对我们的数据集来说是巨大的,因为有很多变量。白色表示相关值为 1,这意味着这两个变量是相同的。可以看出,对角线是白色的,这是合乎逻辑的,因为对角线值表示元素与其自身的相关性,该相关性将为 1。黑色表示相关值为-1,这意味着两个变量完全相关,但方向相反。也就是说,如果一个值增加,另一个值就会减少,反之亦然。黑色和白色之间的一种特殊颜色表示相关值为 0,这意味着两个变量完全独立。现在,当您重新访问上述相关矩阵时,可以检查浅色块具有正相关值,深色块具有负相关值。在预测“销售价格”时,与“销售价格”相关的绝对值较高的特征是有益的。
回归图
它用于理解两个变量之间的关系。它与分析两个变量之间的相关性相同,但它只用于分析两个变量,这与相关矩阵不同。这可以看作是一个正常的图表,我们绘制所有的数据点,然后找到最佳拟合线。
sns.regplot(x='BedroomAbvGr',y='SalePrice',data=df)
作者图片
这是总体质量与房价的回归图。由于斜率增加,这意味着相关值为正。此外,斜率值越高,表示绝对相关值越大。
作者图片
现在从这两个图表中,我们可以观察到以下情况:
- 房子的整体质量和价格之间的相关性,以及卧室数量和房子价格之间的相关性是正相关的。
- 房屋整体质量与价格的相关性高于卧室数量与房屋价格的相关性。
太激烈了!!
但是现在我们知道一列中有多少异常值,如何识别和删除这些异常值,整个数据中有多少行存在缺失数据,一个变量如何影响另一个变量,哪些特征对于预测来说是最重要的等等。
我希望这篇博客能帮助你理解我们是如何探索数据并从中提取信息的。我建议在找到合适的预测模型之前使用这些技巧,因为每次理解和探索您的数据是至关重要的。
如果您喜欢这些内容,并希望了解数据科学的广阔领域,请关注我们的 medium 。
成为 介质会员 解锁并阅读介质上的许多其他故事。
了解阿帕奇蜂房 LLAP
来源: Unsplash
当你看的时候,Apache Hive 是一个复杂的系统,但是一旦你去寻找更多的信息,它比复杂更有趣。Hive 有多种查询引擎,在查询引擎之上还有 LLAP,使实时、交互式查询更加可行。当你了解更多的时候,长寿和过程,或者 LLAP,是一个令人敬畏的概念和执行。
这正是我所做的,我读了一些关于 LLAP 的文件和其他帖子,对它有了更好的了解。我也理解 Tez 查询引擎,并期待很快看到关于它的帖子。在这篇文章中,我将展示我对 LLAP 的了解,以及如果你使用它会有多棒。
配置单元查询引擎
Hive 有三个查询引擎:
- MapReduce (MR)
- 阿帕奇技术中心
- 阿帕奇火花
MapReduce (MR)
MapReduce 是 Hive 附带的第一个查询引擎,也是最慢的一个。当您使用 MR 作为查询引擎提交一个 Hive 查询时,每个查询都会被转换成 MapReduce 作业并提交给 YARN 容器。YARN 或另一种资源协商器在 MR 和 Tez 查询引擎之间是常见的。但是 MR 查询引擎的问题是所有的查询都需要转换成 MR jobs。转换本身需要时间。因此,您可以想象这个查询引擎是如何因为大量的延迟而变得缓慢的。
阿帕奇技术中心
为了克服这个延迟问题,Tez 作为一个查询引擎被引入到 Hive 的更高版本中。Tez 使用有向无环图(Dag)来处理查询,而不是 MapReduce 作业。这大大减少了延迟并缩短了查询响应时间。在最新版本的 Hive 中,尽管不推荐使用 MR 查询引擎,但它是默认引擎。但是,每当您输入 Hive shell 时,都会收到反对警告,并建议您切换到 Tez 或 Spark 作为查询引擎。而且普遍建议改用 Tez。我们将在我的下一篇文章中看到原因。
阿帕奇火花
最后,我们有 Apache Spark 作为第三个查询引擎选项。到目前为止,Spark 是其中最快的。有人声称 Spark 可以将 Hive 的性能提高 100 倍,这是一个非常大胆的说法。Tez 没有提供如此高的性能提升,公认的提升是 10 倍。所以你可能会说,好吧,火花是明显的赢家,对不对?嗯,那得看情况!Tez 和 Spark 都使用 Dag 来优化 MR 的查询性能,因此两者都可以并行或并发执行大量任务。
在 Tez 和 Spark 之间选择一个查询引擎完全取决于您的应用程序和需求。但是无论你选择什么,LLAP 和坐在那个查询引擎的上面并且更好的性能甚至更多。例如,我们知道,如果我们再次需要数据,Spark 可以在内存或磁盘上缓存(持久化)数据。但是这种缓存在同一个 Spark 作业中是可用的,另一个 Spark 作业不能访问该数据。如果我告诉你 LLAP 也有这种可能呢?我知道,这太疯狂了。所以,让我们看看 LLAP 还能做什么。
长寿和进步(LLAP)
我们终于要谈谈 LLAP 了。首先你需要知道,就像我已经说得很清楚的,LLAP 不是一个查询引擎。它位于查询引擎之上,使查询和数据处理更快。如果你把不同的蜂巢组件想象成一个堆栈,HDFS 在底部,纱线在顶部,蜂巢在顶部。现在想象一下在 HDFS 之上有一层缓存和内存处理。这意味着很多请求根本不会发送到 HDFS。这就是 LLAP 在很高层次上所做的。
当我说 LLAP 是一个缓存和内存处理层时,我肯定是把事情过于简化了。肯定有更好的说法。我来详细说明一下。
你可以把 LLAP 想象成另一个运行在 Hadoop 集群中所有数据节点上的 YARN 应用。唯一的区别是 LLAP 是一个长期的过程(因此得名)。但这并不意味着它会耗尽你所有的资源。它可以被配置成一个非常小的进程来处理简单的查询,或者它可以被配置成在需要的时候动态地伸缩。与 Spark 相比,这带来了非常大的不同。因为 LLAP 仍然致力于纱线,它带来了纱线的所有优点,如分布式和容错。
LLAP 在数据节点上运行守护进程,这些守护进程不依赖于发出配置单元查询的用户。这是一个非常重要的区别,因为这允许 LLAP 跨用户重用缓存的数据。所以你和我都在同一个表上执行类似的查询,LLAP 将能够使用已经可用于我们两个查询的缓存,这将提高我们两个的性能。如果没有 LLAP,这两个查询将不得不分别执行相同的操作。你可以想象这是多么的不理想。
LLAP 不是一个查询引擎,它是可选的
你现在应该意识到了,LLAP 是完全可以选择的。使用 LLAP 进行配置单元查询并不是强制性的。只有当您希望在交互和批处理模式下提高配置单元查询的响应能力时,才使用 LLAP。即使使用 LLAP,也不像查询的所有部分都在 LLAP 执行。LLAP 不是用来做这个的,这才是查询引擎的用途。LLAP 接受部分查询,这些查询可以通过使用缓存或长寿命进程而受益。
LLAP 没有承诺它会自己执行整个查询。事实上,是查询引擎决定了什么可以进入 LLAP,什么不可以。目前,查询引擎 Tez 和 Pig 等其他框架可以在其堆栈中使用 LLAP。不幸的是,对 MapReduce 的支持还没有计划,不要对此抱太大希望。
而且因为 LLAP 仍然是为了和 YARN 一起工作而建立的,资源分配完全在 YARN 的控制之中。这也是 LLAP 守护进程节点能够相互通信并跨节点共享数据的原因。这里的另一个优点是守护进程本身不需要很多资源来运行。YARN 分配进程本身所需的最少资源,并会根据工作量在需要时增加资源分配。为了避免堆问题或 JVM 内存问题,缓存的数据总是保存在堆外的大缓冲区中。因此,与查询引擎相比,LLAP 对 group by 和 joins 等聚合的处理要快得多。
查询片段
我已经提到过,LLAP 通常执行部分查询,而不是整个查询。查询的这些部分被称为查询片段。这些片段包括过滤器、数据转换、部分聚合、投影、排序、分桶、连接、半连接、散列连接等。需要注意的是,LLAP 只接受某些“受祝福的”UDF 和 Hive 代码。
为了流程的稳定性和数据的安全性,LLAP 不本地化任何代码,并且动态执行。因为守护进程不依赖于任何特定的用户(如前所述),所以 LLAP 节点可以允许跨查询和会话并行执行各种查询片段。这是性能提高的主要原因之一。另一个好消息是,特别是对开发人员来说,LLAP API 可以通过客户端 SDK 直接获得。您可以使用这些 API 直接指定关系转换。
输入和输出
正如我已经提到的,守护进程本身占用的内存非常小,这是因为其他的事情大部分都是通过将工作卸载给多线程来完成的。例如,输入和输出被卸载到线程。和转换是在单独的线程中完成的。因此,一旦 I/O 线程准备好数据,数据就被传递给单独的线程进行处理。这使得 I/O 线程可用于新的 I/O 操作。
数据在进程中以游程编码(RLE)列格式进一步传递给其他线程。这在很大程度上减少了跨线程和进程复制数据。通过扩展,缓存也是同样的 RLE 格式。你可以从这里开始看到好处。
I/O 和缓存在很大程度上依赖于存储数据的文件格式。如果 I/O 和缓存必须是高性能的,这是必要的。所以 LLAP 借助插件将这些知识具体化了。ORC 是 LLAP 支持的第一种文件格式。这就是为什么越来越多地采用 ORC 作为外部 Hive 表的首选文件格式的原因之一。
说到缓存,元数据和数据本身都会被缓存。正如我在前面几节中提到的,数据被缓存在堆外以避免其他潜在的问题。但是另一方面,元数据作为 Java 对象存储在进程中。这确保了即使数据本身被逐出缓存,元数据仍然在内存中,以避免一些开销。
我提到了缓存中的数据驱逐。如您所料,有各种各样的数据驱逐策略。默认情况下,使用 LRFU 策略。但是您可以随时插入任何其他策略。
处理
这是 Hive 中争论的一大领域,交易还是不交易。但这是另一篇博文的主题。就 LLAP 而言,它理解交易。它足够智能,可以在数据被缓存之前执行转换(比如合并或增量文件)。如果在同一个表上执行各种事务(这种情况很常见),LLAP 可以为每种变化存储多个版本的数据。并且将根据查询动态地从缓存中获取正确的版本。这将确保不会对相同的数据一次又一次地执行相同的转换集,从而减少大量的处理时间。
了解 LLAP 不止于此。还有更多,你越想理解它,它就变得越有趣。随着我探索的深入,我打算写更多关于它的东西。但现在,这是我的全部。理解 LLAP 的工作方式将使编写查询变得容易得多,而且编写查询的方式可以利用这些优化来减少延迟。我希望这对你的 Hadoop 或 Hive 之旅有一点帮助。
如果你喜欢这里看到的,或者在我的个人博客和 Dev 上看到的。要写博客,并希望在未来看到更多这样有用的技术帖子,请考虑在 Github 上关注我。
原载于 2021 年 11 月 18 日【https://blog.contactsunny.com】。
理解 Python 中的*args 和**kwargs
了解如何在 Python 中向函数传递可变数量的参数
如果您是一名初级 Python 程序员,您可能会遇到带有如下所示参数的函数声明:
def do_something(num1, num2, ***args**, ****kwargs**):
上面的*
和**
操作符允许您向函数传递可变数量的参数。然而,这个特性经常给 Python 程序员带来很多困惑。本文将试图阐明*
和**
操作符的用途,以及如何在日常编程任务中使用它们。
使用* 传递可变数量的参数
首先,考虑以下函数定义:
def do_something(num1, num2, *args):
print(num1)
print(num2)
要调用 do_something() ,需要传入:
- 两个强制参数,后面是,
- 可选的可变参数数量
让我们试试这个,看看会发生什么。我们可以先这样调用 do_something() :
# calling the function with two mandatory arguments
do_something(5,6)
您现在应该看到:
5
6
您也可以在前两个参数后传递附加参数,如下所示:
do_something(5,6,**7,8,9**)
第三个参数向前传递到 do_something() 函数中的 args 变量中。 args 的数据类型为元组。因此,您可以在函数中使用一个 for-in 循环来提取传入的单个可选参数:
def do_something(num1, num2, *args, **kwargs):
print(num1)
print(num2)
**for arg in args:
print(arg)**
因此,您的输出将如下所示:
5
6
7
8
9
以下对 do_something() 函数的调用都是有效的:
do_something(5,6,**7**)
do_something(5,6,**7,8**)
do_something(5,6,**7,8,9**)
do_something(5,6,**7,8,9,True**)
do_something(5,6,**7,8,9,True,3.14**)
虽然您可以为可选参数传入不同类型的不同参数,但传入相同类型的参数更常见(也更符合逻辑)。例如,您可能有一个名为 average() 的函数来计算一组数字的平均值:
def average(*args):
return sum(args) / len(args)print(average(1,2)) # 1.5
print(average(1,2,3)) # 2.0
print(average(1,2,3,4,5)) # 3.0
print(average(1,2,3,4,5, 6.1)) # 3.516666666666667
在这种情况下,传入相同类型(数值类型)的参数很有意义。
值得一提的是,变量参数的命名并不局限于“ args ”。您可以使用自己的变量名来命名它。
使用*解包参数
在上一节中,您看到了如何向函数传递可变数量的参数,如下所示:
do_something(5,6,**7,8,9**)
如果您的可选参数存储在一个列表或元组中,如下所示:
values = (7,8,9) # tuple
在这种情况下,要将 values 变量传递给 do_something() 函数,您可以使用*****
操作符来解包值,以便可以将它们传递给函数:
do_something(5,6,***values**)
*
操作符也处理列表:
values = **[**7,8,9**]** # list
do_something(5,6,*values)
使用**传入“带关键字的”参数
可变参数的第二种形式对初学者来说稍微有点混乱。考虑以下函数定义:
def do_something(num1, num2, ****kwargs**):
kwargs 中的 kw 代表 k ey w orded。同样,您可以使用自己喜欢的名称给这个变量命名。
除了传入前两个强制参数之外,现在还可以传入一个可选的“关键字”参数。一个例子可以说明这一点:
do_something(5,6,**x=7**)
do_something(5,6,**x=7,y=8**)
就个人而言,我更喜欢称 x=7 和 y=8 为键/值对。
在上面的例子中,您可以通过指定键(x
和y
)和它们相应的值(7 和 8)来传递额外的参数。
在这个函数中,关键字对作为一个字典被传递给**kwargs**
。因此,您可以使用 for-in 循环提取它们,如下所示:
def do_something(num1, num2, ****kwargs**):
print(num1)
print(num2)
**for k,v in kwargs.items(): # get each key/value pair one at a
print(k,v) # time**
当然,您也可以提取键,然后像这样提取值:
def do_something(num1, num2, **kwargs):
print(num1)
print(num2)
**for k in kwargs.keys(): # retrieve all the keys
print(k, kwargs[k]) # then get the value based on the key**
在任一情况下,输出将如下所示:
5
6
**x 7
y 8**
**kwargs
的用途
关于**kwargs
的一个常见问题是它的用例。****kwargs**
在什么情况下有用?考虑以下情况,其中您有一个包含四列的数据帧:
import pandas as pd
import numpy as npdf = pd.DataFrame(np.random.randn(5,4),
columns=['Summer','Winter','Autumn','Spring'])
df
数据帧看起来像这样:
以熊猫数据为例
假设您被要求编写一个函数,比如说 **fetch_data(),**允许用户从一个或多个单元格包含的值大于某些特定值的列中提取行。例如,您希望从 Summer 列中提取值大于 0.5 的所有行。此外,您可能还想从 Winter 列中提取值大于 0.2 的行。在这些情况下,您的函数应该足够灵活,可以指定多个列名和值,如下所示:
fetch_data(Summer=0.5) # retrieve only the Summer column
fetch_data(Summer=0.5, Winter=0.2) # retrieve the Summer and Winter
# columns
要实现此功能,您的函数可以使用关键字对接受这些列和值,如下所示:
def fetch_data(****kwargs**):
for k,v in kwargs.items():
print(df[[k]][df[k]>v]) # df[[k]] first returns the specified column (k) as a
# dataframe, then [df[k]>v] retrieves all the rows whose
# cell value is more than the value specified (v)
所以下面调用 fetch_data() :
fetch_data(Summer=0.5, Winter=0.2)
产生以下输出:
Summer
0 0.842614
2 0.767157
4 0.935648
Winter
0 0.843960
3 0.663104
使用**解包参数
正如您可以使用*
操作符解包值列表并将其传递给具有可变参数的函数一样,您也可以使用**
操作符解包 dictionary 对象并将其传递给接受关键字参数的函数,如下所示:
**kwpairs = {'Summer':0.5, 'Winter':0.2} # dictionary
fetch_data(**kwpairs)** # same as
fetch_data(Summer=0.5, Winter=0.2)
可变参数和关键字参数的顺序
在函数声明中,可以有以下类型的参数:
- 强制参数,和/或
- 可变参数,和/或
- 关键词论据
然而,如果一个函数接受带关键字的参数,它必须总是放在函数声明的最后处。以下函数声明无效:
def do_something(**kwargs, *args):
...def do_something(*args, **kwargs, num1):
...def do_something(**kwargs, num1):
...
如果一个函数接受带关键字的参数,它必须总是放在函数声明的最后。
以下示例显示了一些有效的函数声明:
def do_something(num1, *args, **kwargs):
...def do_something(num1, num2, *args):
...def do_something(*args, **kwargs):
...def do_something(**kwargs):
...def do_something(*args, num1):
...def do_something(*args, num1, **kwargs):
...
我将把它作为一个练习留给读者,让他们尝试上面的函数声明,并探索如何将值传递给它们。
我希望你现在对*args
和**kwargs
有更好的理解!
使用 PyCaret 的 ARIMA 模型实用指南—第 1 部分
奠定统计基础
📚简介
PyCaret 是一个用 Python 编写的开源、低代码的机器学习库,可以自动化机器学习工作流。这是一个端到端的机器学习和模型管理工具,可以成倍地加快实验周期,提高您的工作效率。
PyCaret 最近发布了一个时间序列模块,它附带了许多方便的特性,使得时间序列建模变得轻而易举。你可以在这篇文章中了解更多。
在这个由多个部分组成的系列文章中,我们将重点讨论如何使用 PyCaret 这样的“低代码”库来更深入地理解 ARIMA 模型的工作原理。在第一部分中,我们将对 ARIMA 模型及其参数进行概述。然后,我们将从一个简单的 ARIMA 模型开始,为这个模型的预测和预测区间创建一个理论框架,以发展对其工作的直觉。最后,我们将使用 PyCaret 构建这个 ARIMA 模型,并将理论计算与模型输出进行比较。将模型的输出与我们的理论框架联系起来,有望加深我们对模型的理解,并为后续文章中更详细的分析奠定基础。
📖建议的先前阅读
本文推荐给那些对时间序列有基本了解(例如,季节性、自相关 ACF、白噪声、训练-测试-分割等概念)但有兴趣了解更多 ARIMA 模型的人。如果你不熟悉这些概念,我建议你先阅读这些短文。这将有助于理解这篇文章。
👉什么是「白噪音」?
1️⃣ ARIMA 模型概述
ARIMA 是一个经典的时间序列模型,用于模拟时态数据的自回归特性。自回归意味着依赖于自身过去的值。时间序列可能依赖于(1)其最近的过去值,或者(2)可能表现出季节性行为,并在一定数量的时间点过去后重复自身。
直观上,(1)的一个例子是股票数据。这是时间数据,明天的价格受今天(或最近的过去)价格的影响很大,而与一个月或一年前发生的事情没有太大关系。类似地,( 2)的一个例子可以是在零售店销售冬装。这通常在冬季前达到高峰,在夏季逐渐减弱。因此,它表现出季节性模式。今年冬天的销售额将与去年冬天的销售额高度相关。
现在我们已经看到了一些基本的例子,让我们看看这是如何在模型中表示的。经典的 ARIMA 模型有几个超参数,但我们将重点关注其中的两个主要参数— order
和seasonal_order
。
**order:** iterable or array-like, shape=(3,), optional (default=(1, 0, 0))
The (p,d,q) order of the model for the number of AR parameters, differences, and MA parameters to use.**seasonal_order:** array-like, shape=(4,), optional (default=(0, 0, 0, 0))
The (P,D,Q,s) order of the seasonal component of the model for the AR parameters, differences, MA parameters, and periodicity.
现在,你可能想知道这些术语与我们上面强调的例子有什么联系,以及order
和seasonal_order
中的三个和四个术语分别是什么意思。在非常高的层次上,order
与上面的例子(1)相关,其中下一个值与紧接的前一个值强相关。seasonal_order
与上面的示例(2)相关,其中下一个值与过去一个或多个季节的数据相关。
但这仍然没有回答什么是(p, d, q)
和(P, D, Q, s)
的问题。这正是我们将在这一系列文章中试图回答的问题。
注意:本系列文章采用“代码优先”的方法来理解 ARIMA 模型。我强烈建议读者也参考[5],以便从理论角度更好地理解。
2️⃣基线 ARIMA 模型
为了更好地理解 ARIMA 模型的各种超参数,让我们从一个非常简单的 ARIMA 模型开始——一个没有任何自回归属性的模型。这通常用于表示白噪声(即数据中没有任何信号)。你可以在[1]和[2]中找到更多关于它的信息。该模型由以下超参数表示:
##############################################
#### Baseline ARIMA Model Hyperparameters ####
##############################################(p, d, q) = (0, 0, 0)
(P, D, Q, s) = (0, 0, 0, 0)
理论上,由于时间序列数据没有任何信号,未来预测(样本外)的最佳表示是用于训练模型的数据的平均值(详细信息请参见[2])。同样,样本内预测的最佳估计也是用于训练模型的数据的平均值。
我们可以通过计算样本内拟合的残差来获得这些预测的预测区间。残差表示实际样本内数据(即用于训练的数据)和由该模型产生的样本内估计(在这种情况下是训练数据的平均值)之间的差异。
好吧,理论是一回事,但一张图胜过千言万语。所以让我们来实践一下。我们将使用 PyCaret 的时间序列模块来更深入地了解这个模型。注意,可以在这里的 和参考资料部分找到代码的笔记本 。
3️⃣使用 PyCaret 了解 ARIMA 模型
PyCaret 的时间序列模块提供了超过 2000 个已知属性的时间序列的数据游乐场。我们将使用来自这个操场的“白噪声”数据集来解释这个例子。
👉步骤 1:设置 PyCaret 时间序列实验
#### Get data from data playground ----
y = get_data("1", folder="time_series/white_noise")
实验的白噪声数据(仅前 5 行)[图片由作者提供]
接下来,让我们设置一个时间序列实验来探索和建模这个数据集。“时间序列实验”是一个一站式商店,用户可以在这里执行探索性分析(EDA) 、模型构建、模型适合度评估以及部署——同时遵循引擎盖下的最佳建模实践。
在这个设置中,我将预测范围fh
设置为 30(即我们打算预测接下来的 30 个时间点)。我还暂时将seasonal_period
设置为 1。注意,如果您的数据有一个DatetimeIndex
或PeriodIndex
指数(这里不是这种情况),那么季节周期是自动推断的。如果是别的什么,我们需要度过季节期。我还传递了一个会话 ID,它充当种子并使实验可重复。
#### Setup experiment ----
exp = TimeSeriesExperiment()
exp.setup(data=y, seasonal_period=1, fh=30, session_id=42)
[图片由作者提供]
该设置从一开始就为我们提供了一些方便的信息。
(1)我们看到我们的数据有 340 个数据点。在这个实验中,pycaret 保留了最后 30 个数据点(与预测时间范围相同)作为“测试集”,剩余的 310 个数据点用于训练。
(2)我们通过了季节性周期 1,但是在内部测试季节性之后,没有检测到它。这是意料之中的,因为这是没有季节性的白噪声数据。
(3)内部进行了白噪音的永盒测试(见前面的读数),确定数据与白噪音一致。同样,这也是我们所期望的,因为我们是从白噪音领域中挑选数据的。
👉步骤 2:执行 EDA
接下来,我们可以对这些数据进行快速 EDA:
# `plot_model` without any estimator works on the original
exp.plot_model()
绘制数据[图片由作者提供]
# Plot ACF ----
exp.plot_model(plot="acf")
ACF 图显示没有显著的自相关[图片由作者提供]
ACF 数据显示没有显著的自相关,并且与白噪声一致。这也是意料中的事(详见前面的内容)。
👉第三步:理论计算
接下来,让我们从实验中获得一些数据统计,并添加上面我们框架中的理论计算。
注意:训练和测试数据分割可以使用实验对象的“get_config”方法获得,如下所示。
# Get Train Data
y_train = exp.get_config("y_train")# Get Test Data
y_test = exp.get_config("y_test")#########################################################
# Convert into dataframe & add residual calculations ----
#########################################################train_data = pd.DataFrame({"y":y_train})
train_data['preds'] = y_train.mean()
train_data['split'] = "Train"test_data = pd.DataFrame({'y': y_test})
test_data['preds'] = y_train.mean()
test_data['split'] = "Test"data = pd.concat([train_data, test_data])data['residuals'] = data['y'] - data['preds']
data.reset_index(inplace=True)
接下来,让我们计算进行理论预测和计算预测区间所需的计算。
#######################################################
#### Compute values based on theoretical equations ----
#######################################################y_train_mean **=** data**.**query("split=='Train'")['y']**.**mean()
resid **=** data**.**query("split=='Train'")['residuals']
resid_sigma2 **=** (resid******2)**.**sum()**/**len(resid)
print(f"Mean of Training Data: {y_train_mean}")
print(f"Residuals Sigma2: {resid_sigma2}")***>>> Mean of Training Data: 176.01810663414085
>>> Residuals Sigma2: 0.9769567915901368***######################################
#### Compute Prediction Intervals ----
######################################import scipy.stats as st
alpha = 0.05# 2 sided multiplier
multiplier = st.norm.ppf(1-alpha/2)lower_interval **=** np**.**round(y_train_mean **-** multiplier ***** resid_sigma2******0.5, 2)
upper_interval **=** np**.**round(y_train_mean **+** multiplier ***** resid_sigma2******0.5, 2)
print(f"Prediction Interval: {lower_interval} - {upper_interval}")***>>> Prediction Interval: 174.08 - 177.96***
👉步骤 4:构建模型
既然我们已经完成了理论计算,让我们使用 PyCaret 构建这个模型并检查结果。模型创建就像从实验对象调用create_model
方法一样简单。此外,此时您还可以将特定的超参数传递给模型。在我们的例子中,我们正在建模没有任何自回归属性的数据,所以我们将为order
和seasonal_order
传递全零。如果你想了解更多关于如何用 PyCaret 创建时间序列模型,以及如何定制它们,请参考[3]。
model1 = exp.create_model(
"arima",
order=(0, 0, 0),
seasonal_order=(0, 0, 0, 0)
)
👉第五步:分析结果
接下来,我使用 PyCaret 一行程序编写了一些小的包装器函数来帮助进行模型评估(参见附录中的定义)。这些是
(1) summarize_model
:生成模型的统计概要
(2) get_residual_properties
:绘制并显示残差的方差
(3) plot_predictions
:绘制样本外和样本内预测
如果你想知道更多关于这些功能是如何工作的,特别是summarize_model
,请参考【4】。
现在,让我们看看当我们通过这些助手函数运行模型时会得到什么
summarize_model(model1)
get_residual_properties(model1)
ARIMA 模型的统计摘要[图片由作者提供]
模型残差和残差方差[图片由作者提供]
上面显示了模型的统计摘要。截距是训练数据的平均值。它与我们上面的理论框架中的计算相吻合!
sigma2 是残差的方差。我们怎么知道?检查get_residual_properties
功能的输出。它显示了残差的方差。这个值与统计概要完全匹配,也与我们的理论框架的计算完全匹配!
但是预测呢?接下来让我们检查一下。
plot_predictions(model1)
样本外预测和预测区间[图片由作者提供]
在样本预测中[图片由作者提供]
默认情况下,PyCaret 使用plotly
库提供交互式绘图,因此我们可以通过悬停在绘图上来快速验证值。我们可以很快看到,样本外和样本内预测值都等于 176.02,这与我们理论框架的计算结果相匹配。预测区间也与我们理论框架中的值相匹配!
🚀结论
希望这个简单的模型为我们理解 ARIMA 模型的内部运作打下了良好的基础。在下一组文章中,我们将开始逐一介绍其他参数,并查看它们对模型行为的影响。在那之前,如果你愿意在我的社交渠道上与我联系(我经常发布关于时间序列分析的文章),你可以在下面找到我。暂时就这样了。预测快乐!
🐦推特
📘 GitHub
喜欢这篇文章吗?成为 中等会员 继续 无限制学习 。如果你使用下面的链接, ,我会收到你的一部分会员费,而不会给你带来额外的费用 。
https://ngupta13.medium.com/membership
🔖建议下次阅读
使用 PyCaret 的时间序列模块理解 ARIMA 模型—第二部分
📗资源
- Jupyter 笔记本 包含这篇文章的代码
📚参考
[1] 诊断 PyCaret 中的“白噪声”
[3] 创建和定制时间序列模型
[5] 第八章 ARIMA 模型,《预测:原理与实践》,罗伯 J 海曼和乔治阿萨纳索普洛斯
📘附录
- 助手功能
**def summarize_model(model):**
"""
Provides statistical summary for some statistical models
"""
# Statistical Summary Table
try:
print(model.summary())
except:
print("Summary does not exist for this model.")**def get_residual_properties(model):**
"""
Plots and displays variance of residuals
"""
*#### Residuals ----*
try:
plot_data = exp.plot_model(
model,
plot="residuals",
return_data=True
)
resid = plot_data['data']
resid_sigma2 = (resid**2).sum()/(len(resid))
print(f"Residual Sigma2: {resid_sigma2}")
except:
print("Residuals can not be extracted for this model.")**def plot_predictions(model):**
"""
Plots out-of-sample and in-sample predictions
"""
# Out-of-Sample Forecast
exp.plot_model(model) # In-Sample Forecast
exp.plot_model(model, plot="insample")
使用 PyCaret 的 ARIMA 模型实用指南—第 2 部分
理解趋势术语
贾斯汀·摩根在 Unsplash 上的照片
📚介绍
在上一篇文章中,我们看到了 ARIMA 模型及其各种超参数的简要概述。我们从一个非常简单的 ARIMA 模型开始,这个模型没有表现出时间依赖性。本文将着眼于 ARIMA 模型的“趋势”部分,看看这是如何建模的,它代表了什么。
📖建议的先前阅读
本系列的前一篇文章可以在下面找到。我建议读者在继续阅读本文之前先浏览一遍。本文建立在前一篇文章中描述的概念的基础上,并且重用了前一篇文章中完成的一些工作。
使用 PyCaret 的 ARIMA 模型实用指南—第 1 部分
ARIMA 模型中的 1️⃣“趋势”概述
顾名思义,趋势决定了时间序列如何随时间变化。在 ARIMA 模型中,这可以使用下面的基本方程[1]来建模。“a”称为“截距”,“b”称为“漂移”项。“漂移”不过是直线的斜率。
图片由作者使用https://latex2png.com/
有几种方法可以用这个方程来模拟趋势。
(1)不随时间变化的趋势分量。在这种情况下,漂移“b”= 0。
(2)从 0 开始并随时间线性变化的趋势分量(零截距“A”和非零斜率/漂移“b”)。
(3)不从 0 开始并随时间线性变化的趋势分量(非零截距“A”和非零斜率/漂移“b”)。
2️⃣️使用 PyCaret 理解趋势项
为了更好地理解这个概念,我们将使用在本系列的上一部分中使用的相同数据集。回顾一下,这个数据集是一个“白噪音”数据集。详细内容可以在本文的 Jupyter 笔记本中找到(可在文章末尾找到)。
白噪声数据集[图片由作者提供]
👉步骤 1 & 2:设置 PyCaret 时间序列实验并执行 EDA
因为我们已经在上一篇文章中执行了这些步骤,所以我们不再赘述。详情请参考上一篇文章。
👉第三步:理论计算
如上所述,ARIMA 模型中的“趋势”可以用 3 种方法计算。
(1)趋势组件固定的第一种方法与上一篇文章中介绍的模型相同。因此,同样的理论计算也适用于这种情况。作为复习,预测的估计值(样本内和样本外)是训练数据集的平均值(即“a”= 176.02)。预测区间的估计值为 174.08–177.96。
(2)第二种方法将趋势分量建模为截距“a”= 0 的直线。为了从理论上对此建模,我们可以使用 scikit-learn 的线性回归模型来重新创建它。当我们最终使用 ARIMA 模型对此进行建模时,我们预计会看到相同的结果。
X_train = np.arange(len(y_train)).reshape(-1, 1)
reg = LinearRegression(**fit_intercept=False**).fit(X_train, y_train)
print(f"Expected Intercept: {reg.intercept_}")
print(f"Expected Slope: {reg.coef_}")***>>> Expected Intercept: 0.0
>>> Expected Slope: [0.85317393]***
(3)第三种方法将趋势分量建模为截距“a”不为 0 的直线。我们可以使用 scikit-learn 如下再次创建它。
X_train = np.arange(len(y_train)).reshape(-1, 1)
reg = LinearRegression(**fit_intercept=True**).fit(X_train, y_train)
print(f"Expected Intercept: {reg.intercept_}")
print(f"Expected Slope: {reg.coef_}")***>>> Expected Intercept: 175.95815015173943
>>> Expected Slope: [0.00038807]***
👉第四步:建立模型
现在,我们已经讨论了理论计算,让我们看看这些模型的实践。
**# Trend with method 1 ----**
model2a = exp.create_model(
"arima",
order=(0, 0, 0),
seasonal_order=(0, 0, 0, 0),
**trend="c"**
)**# Trend with method 2 ----**
model2b = exp.create_model(
"arima",
order=(0, 0, 0),
seasonal_order=(0, 0, 0, 0),
**trend="t"**
)**# Trend with method 3 ----**
model2c = exp.create_model(
"arima",
order=(0, 0, 0),
seasonal_order=(0, 0, 0, 0),
**trend="ct"**
)
👉第五步:分析结果
我们将重用在上一篇文章中创建的相同的助手函数来分析结果。
方法一:trend = “c”
summarize_model(model2a)
get_residual_properties(model2a)
作者图片
这些结果与我们的理论计算相符,并与上一篇文章中的结果完全相同。我们将不再详细讨论它们。您可以参考本文末尾的 Jupyter 笔记本了解更多详细信息。
方法二:trend = “t”
**summarize_model**(model2b)
**get_residual_properties**(model2b)
方法二:统计汇总[图片由作者提供]
正如我们可以看到的,模型的漂移(0.8532)与使用 scikit-learn 的理论计算相匹配。
方法 2:残差和 sigma 2[图片由作者提供]
类似地,残差 Sigma2 (~ 7777)手动计算与模型中的计算相匹配。需要注意的一点是,对于这个数据集来说,这不是一个好的模型,因为无法解释的方差(sigma2)比以前高得多。让我们也来看看预测。
**plot_predictions**(model2b)
方法 2:样本内预测[图片来源
方法 2:样本外预测[作者图片]
我们可以看到,预测(样本内)从接近 0 开始,线性增加。PyCaret 提供了交互式图表,因此用户可以放大预测的各个部分,以更详细地分析它们。例如,可以将鼠标悬停在连续的点上,注意到它们的漂移值相差 0.8532。
样本外预测是样本内预测的延续,再次以 0.8532 的斜率/漂移线性上升。
对于目光敏锐的观察者来说,这是一个细微的差别。第一个样本内预测不为 0。它从值 0.8532(漂移值)开始。你能解释一下为什么吗?
方法三:trend = “ct”
**summarize_model**(model2c)
**get_residual_properties**(model2c)
方法三:统计汇总[图片由作者提供]
我们可以看到,模型的截距(175.9578)和漂移(0.0004)与使用 scikit-learn 的理论计算相匹配。漂移如此接近 0 是意料之中的,因为这是白噪声数据。此外,我们可以看到,该模型与方法 1 中的模型之间的“拟合优度”(较低的σ2 =更好的拟合)几乎没有差异。
让我们也来看看预测。除了小斜率/漂移部分之外,它们应该与方法 1 中的非常相似。同样,PyCaret 中的交互式绘图允许轻松检查结果,如下面的缩放结果所示。
方法 3:样本外预测(缩放)[图片由作者提供]
此外,这可能是显而易见的,但值得明确指出的是,尽管方法 1 和方法 3 在这种情况下给出了类似的拟合(假设数据是白噪声),但情况并不总是如此。当数据呈现线性趋势时,与方法 1 相比,方法 3 通常会给出更好的拟合。我鼓励读者创建一个虚拟的线性数据集并尝试一下。
🚀结论
希望这个简单的模型为我们理解 ARIMA 模型中趋势部分的内部运作打下了良好的基础。在下一组文章中,我们将开始逐一介绍其他参数,并查看它们对模型行为的影响。在那之前,如果你愿意在我的社交渠道上与我联系(我经常发布关于时间序列分析的文章),你可以在下面找到我。暂时就这样了。预测快乐!
🐦推特
📘 GitHub
喜欢这篇文章吗?成为 中等会员 继续 无限制学习 。如果你使用下面的链接, ,我会收到你的一部分会员费,而不会给你带来额外的费用 。
https://ngupta13.medium.com/membership
📗资源
- Jupyter 笔记本 包含这篇文章的代码
📚参考
[1]R 中的常数和 ARIMA 模型,Hyndsight
[2] 第八章 ARIMA 模型,《预测:原理与实践》,罗布·J·海德曼和乔治·阿萨纳索普洛斯
理解音频:什么是声音,我们如何利用它。
使用人工智能和声音的物理属性来处理音频。
戈弗雷·尼安杰奇在 Unsplash 上拍摄的照片
音频无处不在。虽然音频处理已经存在了几十年,但人工智能和大数据的兴起让我们能够揭开音频的隐藏秘密,为企业和最终用户提供以前无法获得的见解。
更好地理解音频可以帮助我们在客户服务行业,让企业最大限度地提高客户满意度。情绪分析算法可以识别客户服务电话中客户的语气,并分析根本原因,使企业能够改变策略,以更好地支持他们的消费者。
另一个例子是美国宇航局的 SoundSee,该计划旨在为几个迷你机器人配备一系列麦克风,以监控国际空间站上的机器音频。使用人工智能,这些机器人识别违规行为,通知各方解决问题,充当系统故障的第一道防线。
在本文中,我们将探索声音到底是什么,如何测量它,以及如何使用人工智能来利用它。
那么,什么是声音呢?
简而言之,当振动的物体导致空气分子相互碰撞时,就会产生声音。这些空气分子的振荡在空气分子内产生微小的压差,这有效地产生声波。这些波也称为机械波,通过介质传播,将能量从一个位置传递到另一个位置。如果你想一想,这恰恰是为什么太空中没有声音;在真空的空间中,根本没有可以传播声音的介质。
来源:https://www . research gate . net/figure/压力-声波-空气传播 _fig2_334784649
在上图中,底部的粒子图代表了声音在空气中造成的低压和高压区域。低压区域的颗粒密度较低,高压区域的颗粒密度较高。基于这种压力差,可以生成一条曲线,在具有较高气压的区域具有峰,在具有低气压的区域具有谷。
这种声波的可视化被称为波形,它提供了关于声音的大量细节,当试图从音频中提取特征时,可以利用这些细节。其中一些最基本的功能是:
频率
在一个波中,周期是完成一个周期所需的时间(参考下图)。频率是周期的倒数,用 Hz 表示,转换为每秒的周期数。本质上,完成一个周期所需的时间越短,频率就越高,反之亦然。从视觉上看,波峰彼此靠近的波比波峰较远的波频率更高。
来源:https://www . future learn . com/info/courses/presenting-data-with-images-and-sound/0/steps/53151
但是我们如何感知频率呢?
我们对频率的感知通常用声音的音高来表示。虽然频率描述了波形循环重复率的数值度量,但音高更多的是我们用来描述声音的主观术语。频率越高,声音的音高越高,频率越低,声音的音高越低。
强度
就像频率一样,强度是理解声音构成的另一个重要维度。声强描述的是声音在一个区域内移动的声功率,单位是瓦特每平方米。声音的功率是声音在单位时间内传递能量的速率。简而言之,强度本质上是声音所转移的能量。
现在,就像频率一样,我们感知强度的方式更加主观。我们通常将强度较高的声音视为较响,强度较低的声音视为较柔和。然而,所有听众的音量并不一致。持续时间、频率和听者的年龄等混杂因素会影响声音的音量。
音色
到目前为止,我们已经讨论了两个一维声音属性:频率和强度。与这些容易量化的属性不同,音色是一种相当神秘的声音属性,它描述了赋予声音特性的众多属性。音乐家喜欢把音色描述为声音的颜色,这是一个有趣但模糊的描述。
为了探究什么是音色,我们来看一个简单的例子。想象一个小号以和小提琴相同的音高、持续时间和强度演奏一个音符。虽然这两种声音有着大部分相同的特性,但对你我来说,这两种声音听起来明显不同。将这两种声音分开的特征的组合可以称为声音的音色。
从物理世界到数字世界
现在,我们对声音的物理性质及其属性有了基本的了解,我们如何利用这些属性并进行一些音频处理呢?首先,我们需要能够将音频转换成某种数字信号,其中包含操作和处理音频所需的信息。
麦克风的工作原理:模数转换(ADC)
自然,所有音频都是模拟信号。模拟信号是时间与声音振幅的连续图,在每个无限小的时间单位内具有无限值。存储原始模拟信号几乎是不可能的,需要无限的存储空间。相反,我们以固定的时间间隔执行一系列操作来从模拟信号中提取值。这使得我们可以在一小部分内存中存储数字格式的信号,同时收集足够的数据来再现声音。这一过程称为模数转换(ADC),它使用采样和量化来收集任意给定模拟信号的有限数值。
采样:采样不是收集连续模拟信号中的每个值,而是以固定的等距时间间隔提取值。音频最常见的采样速率是 44.1 kHz,即声音每秒 44,100 个值。这个采样速率最好地允许我们提取人类听觉范围内存在的所有数据值。
量化:采样侧重于沿水平轴以固定的时间间隔提取值,而量化将波形垂直轴上的值划分为一系列固定的等距值。当在给定的时间间隔选择一个值时,量化将给定时间的精确值舍入到最接近的量化值。量化值的数量也称为分辨率,以位为单位。普通的 CD 具有 16 位的位深度或分辨率,这意味着它具有 65,500 个量化值。量化期间的位深度越高,将模拟信号转换为数字信号时的动态范围就越大。
当麦克风拾取音频时,麦克风内部的振膜振动,形成模拟信号,该信号被发送到声卡。这个声卡执行 ADC,并将新生成的数字信号发送到计算机进行操作或处理。
利用人工智能进行音频处理
我们知道什么是音频,以及如何将音频从物理格式转换成数字格式,但我们如何利用它做任何事情呢?虽然有各种不同的方法来处理音频,但我们将专注于人工智能如何渗透到音频领域,让我们更好地理解、增强和再现音频。
虽然我们不会详细讨论如何在音频处理中实现人工智能,但我们将讨论人工智能应用于音频的不同方式。
整体而言,人工智能是指计算机能够完成通常比一系列逻辑过程需要更高智能水平的任务。深度学习是人工智能的一个子集,模仿人脑的复杂算法本质上是从海量数据中学习。
为了让深度学习算法真正提供有价值的音频见解,我们需要访问大量的音频数据。这些数据集不仅需要很大,还需要整洁有序。大而干净的数据集与高效的人工智能算法的完美结合将为任何人工智能过程产生最佳结果。使用这些大型数据集,人工智能模型将观察这些不同声音的属性模式,如频率、持续时间、强度和音色。
语音识别
目前,人工智能在音频领域最常见的用途之一是语音识别。亚马逊 Alexa、谷歌 Home 和苹果的 Siri 等个人助理都利用人工智能将一个人的语音转换为文本,理解他们请求的含义,并产生听觉响应。
语音/音乐合成
语音识别只是 AI 在音频行业带来的机会的一小部分。目前,研究人员正在实现能够完全从零开始创造声音的人工智能模型。这种能力对于任何使用语音合成产生音频的文本到语音转换程序来说都是非常有用的。深度学习模型在数百小时的演讲及其抄本上进行训练。最终,他们学会了每个单词和字符听起来如何与他们的上下文相关,并能在给定一段文本时产生语音。
同样,人工智能正被用于音乐行业,在给定一组参数的情况下制作复杂的音乐作品。音乐合成的一个很好的例子是谷歌最近的 Blob Opera 应用程序,不管用户组织 Blob 的方式如何,它都能产生美妙的声音和声。
语音增强
它不止于此。我们可以使用人工智能来智能地操纵数字音频,以改善现有的音频,满足我们的需求。例如,人工智能可以用于通过从一些音频中删除任何背景噪音或不必要的人工噪声来创建更干净的语音。音频超分辨率是音频增强的另一个方面,它允许我们通过增加保真度来显著增强低质量的音频。所有这些功能都可以改善通话过程中的音频质量,并提高录制不佳的音频的清晰度。
结论
今天,我们讲述了什么是声音以及声音背后的物理原理。具体来说,我们研究了声音的频率、强度和音色。理解声音和波形的属性对于构建更好的人工智能声音处理算法至关重要。
这个行业的潜力是无限的,随着越来越多的研究人员和开发人员认识到这一前景,我们可以期待在每个行业都看到人工智能音频处理。
感谢您花时间阅读本文。一如既往,我感谢您的任何意见/反馈。
最初发表于 Audo AI 。
了解自相关
乔纳森·丹尼尔斯在 Unsplash 上拍摄的照片
及其对数据的影响(举例)
T4:最近我一直在处理自相关数据。在金融领域,某些时间序列(如房价或私人股本回报率)是出了名的自相关。正确考虑这种自相关对于构建稳健的模型至关重要。
一、什么是自相关?自相关是指过去的观察对当前的观察有影响。例如,如果你可以使用苹果公司上周的回报来可靠地预测其本周的回报,那么你可以说苹果公司的股价回报是自相关的。用数学术语来说:
y = B0 + B1*y_lag1 + B2*y_lag2 + ... + Bn*y_lagn + errorIf any of B1 to Bn is significantly nonzero, then we can say that the time series represented by y is autocorrelated.
我们为什么要关心自相关?
我想到两个原因:
- 许多事情都是自相关的。因此,当我们试图通过构建模拟(例如经济的蒙特卡罗模拟)来研究世界时,我们需要将自相关性考虑在内。否则我们的模型会产生错误的结果(原因见原因 2)。
- 自相关导致波动性被低估,尤其是在复合时。自相关随机变量的复合乘积,即(1+r1)*(1+r2)…可以比没有自相关的随机变量具有更宽的结果分布。
通货膨胀数据的例子
我们用一些实际数据来研究一下这个。我从 FRED(圣路易斯美联储银行的数据仓库)下载了一些 CPI 数据。看起来是这样的:
CPI(资料来源:圣路易斯美联储银行,图片由作者创作)
每当分析像 CPI 这样的时间序列数据时,你应该从变化率开始(使其更加平稳)。让我们这样做:
Here's what my raw CPI data looks like (stored in df):values
date
1950-01-01 23.51
1950-02-01 23.61
1950-03-01 23.64
1950-04-01 23.65
1950-05-01 23.77
1950-06-01 23.88
1950-07-01 24.07
1950-08-01 24.20
1950-09-01 24.34
1950-10-01 24.50# My CPI data is stored in a dataframe called df
# The following line calculates the monthly rate of change
df_chg = (df/df.shift(1)-1).dropna()
结果看起来像这样(有一些明显的季节性,我们今天将忽略):
CPI 月度变化(来源:圣路易斯美联储银行,图表由作者创作)
我们可以通过观察 CPI 的月度变化与其滞后值的相关性来检查自相关性。我们可以使用移位方法来创建滞后。
df_chg.rename({'values': 'unlagged'}, axis=1, inplace=True)lags = 10for i in range(lags):
if i > 0:
df_chg['lag_'+str(i)] = df_chg['unlagged'].shift(i)
绘制 CPI 变化的未标记值与其各种滞后之间的相关性,我们看到存在显著的自相关性:
大量的自相关(由作者创建的图形)
忽略自相关,风险自负
Let’s do a quick simulation to see what happens if we ignore autocorrelation. The monthly change in inflation has a standard deviation of 0.32%. If it were a normally distributed random variable, we can annualize the 0.32% by multiplying it by the square root of 12 (because we are going from monthly to annual) — which gives us an annualized standard deviation of 1.11%. That’s a pretty low standard deviation and would imply that extreme events such as hyperinflation are virtually impossible.
The following code simulates a year’s worth of inflation 10,000 times so we can look at the difference in outcomes between including autocorrelation and ignoring it.
# c is the regression constant, a.k.a. the mean value
# we will set c to zero to simplify things
c = 0# List to store autocorrelated simulation results
auto_correl_list = []
# List to store normally distributed simulation results
normal_list = []# Run 10,000 scenarios
for sims in range(10000):
# In each scenario, generate 12 months of "inflation"
shocks = np.random.normal(0, target_vol, (12,))
y_list = []
# This loop takes the 12 shocks and adds autocorrelation
for i, e in enumerate(shocks):
if i == 0:
y = c + betas[0]*0 + betas[1]*0 + e
y_list.append(y)
elif i == 1:
y = c + betas[0]*y_list[i-1] + betas[1]*0 + e
y_list.append(y)
else:
y = c + betas[0]*y_list[i-1] + betas[1]*y_list[i-2] + e
y_list.append(y)
# Calculate the compounded products
auto_correl_list.append(((pd.Series(y_list)+1).cumprod()-1).iloc[-1])
normal_list.append(((pd.Series(shocks)+1).cumprod()-1).iloc[-1])
Let’s take a look at the distribution of outcomes. Look at how much wider the autocorrelated version (in blue) is than the normal (in orange). The simulated standard deviation of the normal (the standard deviation of the orange histogram) is basically what we calculated earlier — 1.11%. The standard deviation of the autocorrelated version is 7.67%, almost seven times higher. Notice also that the means for both are the same (both zero) — autocorrelation impacts the variance but not the mean. This has implications for regression, which I will cover in a future article.
Finally, let’s talk a bit about why this occurs. When something is autocorrelated (and the correlation coefficients are positive), it’s much more susceptible to feedback loops. Trends tend to snowball — for example, in cases where the last few observations were high, the next observation tends to be high as well because the next is heavily impacted by its predecessors.
We can see this snowball effect by looking at a few of the individual paths from our CPI simulation (this time simulated out to 60 months instead of just 12). In the autocorrelated version, once things get out of hand, it tends to stay that way, either going to the stratosphere or to -100%.
Feedback loops can produce extreme results (Graphic created by author)
The normal version of our CPI simulation fails to capture this snowball effect and therefore understates the range of possible outcomes:
Assuming no autocorrelation understates volatility (Graphic created by author)
Which is closer to reality? My autocorrelated simulation definitely overstates the likely range of outcomes for inflation — it’s a simple simulation that fails to account for some of inflation’s other properties such as mean reversion. But without autocorrelation, you would look at your simulation results and assume that extreme phenomena like hyperinflation or persistent deflation are statistical impossibilities. That would be a grave mistake.
理解时间序列分析中的自相关
对于初学者来说,理解自相关是如何工作的是非常重要的,这样可以使他们的时间序列分析之旅更加容易。
克里斯·利维拉尼在 Unsplash 上的照片
两年前我开始了我的时间序列分析之旅。最初,我开始通过 YouTube 视频学习,在那里我遇到了自相关,这是时间序列分析的一个基本概念。根据一些视频,自相关的定义是时间序列与其先前版本的关系/相关性。很少有人说,因为我们需要两个变量来计算相关性,但在一个时间序列中我们只有一个变量,我们需要计算时间序列与其自身第 k 个滞后版本的相关性。
什么是第 k 个滞后?
这是我想到的第一个问题。随着我的继续,我知道了一个有第 k 个滞后的时间序列(y)是它在时间上落后第 t-k 个周期的版本。具有滞后(k=1)的时间序列是原始时间序列在时间上落后一个周期的版本,即 y(t-1)。
这些视频大多以股票市场每日价格为例来解释时间序列分析。这些价格每天都有记录。他们解释说,股票价格的自相关性是当前价格与过去“k”个时期价格的相关性。所以,具有滞后(k=1)的自相关是与今天的价格 y(t)和昨天的价格 y(t-1)的相关性。类似地,对于 k=2,计算 y(t)和 y(t-2)之间的自相关。
现在主要问题来了
我们如何计算今天的价格与昨天的价格的相关性(甚至协方差)?因为相关性只能在具有多个值的变量之间计算。如果我试图计算两个单值之间的相关性,我会得到一个“NaN”。另外,这两个变量必须有相同的长度(值的个数),所以,我甚至不能计算 y(起点到 t)和 y(起点到 t-1)之间的相关性。
然后,我开始寻找自相关的理论解释,偶然发现了如下所示的自相关公式。
作者图片
作者图片
理解公式
- 自相关公式与相关公式相似(但不完全相同)。
- 分子类似于时间序列的当前版本和滞后版本之间的协方差(但没有“N-1”作为分母)。对分子的两个组成部分进行更仔细的检查表明,从它们中减去的是原始时间序列的平均值,即 mean(y),而不是分别减去 mean(y(t))和 mean(y(t-k))。这使得公式的分子与协方差有点不同。
- 分母类似于原始时间序列的标准差(也称为方差)的平方(但没有“N-1”作为分母)。
让我们来回答这个问题,如何计算自相关?通过用 Python 实现它
我们将使用 Nifty(跟踪 50 只股票的印度股票指数)从 2007 年 9 月 17 日到 2021 年 7 月 30 日的收盘价数据。这些数据以 csv 格式从雅虎财经下载。我们将首先为时间序列分析准备数据。
俏皮的时间序列情节(作者图片)
我们将定义一个名为“autocorr”的函数,通过将时间序列数组和第 k 个滞后值作为输入,返回单个滞后的自相关(acf)。此函数将嵌套在另一个名为“my_auto_corr”的函数中,该函数通过调用“autocorr”函数来计算每个滞后值的 acf,从而返回滞后[k,0]的 acf。
让我们通过传递‘nifty’时间序列数据帧和 nlags=10 作为参数来调用‘my _ auto _ corr’函数。我们还将比较’ my_auto_corr ‘函数和’ statsmodels ‘的’ acf '方法的输出。
作者图片
“my_auto_corr”的结果与“statsmodels”的“acf”方法的结果相同。让我们再来看一下我们之前看到的自相关公式,并试着去理解它。
作者图片
- 分母非常简单,它类似于原始时间序列的方差,但分母中没有“N-1”。它在代码中用‘分母’变量来表示。
- 如前所述,分子类似于时间序列的当前版本和滞后版本之间的协方差(没有 N-1 作为分母)。让我们来了解一下如何计算分子。
作者图片
- 棕色矩形代表分子第一部分的 y(t) 。从原始时间序列的平均值中减去 mean(y) 。第一部分由公式中的代码& y(t)-mean(y)中的‘分子 _ P1’表示。 y(t)固定在底部,滞后(k)每增加一个单位,其顶部向下移动 1。
- 同样,绿色矩形代表分子第二部分的 y(t-k) 。它也是从原始时间序列的平均值中减去的, mean(y)。第二部分由代码& y(t-k)中的‘分子 _ p2’——公式中的均值(y)表示。y(t-k)在顶部是固定的,滞后(k)每增加一个单位,其底部就向上移动 1。
然而,正如我们前面看到的,公式的分子与协方差并不完全相同。然而,分母类似于原始时间序列的方差,但分母中没有“N-1”。因此,计算棕色和绿色矩形的协方差并除以原始时间序列的方差并不能得到自相关系数。
将自相关公式分解成片段并在 Python 中实现有助于我们更好地理解它。我们看到了分子中的协方差是如何在时间序列的当前版本和滞后版本之间计算的。因此,为了更好地理解一个概念,无论是机器学习算法还是统计学中的一个概念,了解其本质是很重要的。
理解反向传播
允许神经网络学习的方程的可视化推导
(图片由作者提供)
在最基本的情况下,神经网络获取输入数据并将其映射到输出值。无论你看的是图像、文字还是原始的数字数据,网络看到的都是数字,它只是在这些数字中寻找模式。输入数据通过权重矩阵进行过滤,权重矩阵是网络的参数,其数量可以是数千到数百万或数十亿。微调这些权重以识别模式显然不是任何人想做或能做的任务,因此设计了一种方法来做这件事,几次,但最著名的是在 1986 年[1]。该方法采用神经网络输出误差,并通过网络反向传播该误差,确定哪些路径对输出具有最大影响。这当然是反向传播。
反向传播识别出哪些路径对最终答案更有影响,并允许我们加强或削弱连接,以达到预期的预测。这是深度学习的一个基本组成部分,它总是会在你选择的包中为你实现。所以你真的可以在一无所知的情况下创造出令人惊叹的东西。同样徒劳的是,你也可以像我们许多人一样驾驶你的汽车,而丝毫不知道发动机是如何工作的。你不必去想它,仍然可以毫无问题地驾驶……直到你在路边抛锚。那你肯定希望你能明白引擎盖下发生了什么。
那辆坏掉的车,或者用现在的比喻来说,坏掉的模型把我带到了这一步。我需要理解,所以我开始挖掘,快速的维基百科搜索很快揭示了神经网络如何学习的内部工作原理:
本质上,反向传播将成本函数的导数的表达式评估为从左到右的每层之间的导数的乘积——“向后”——每层之间的权重梯度是部分乘积的简单修改(“向后传播误差”)。"
你说什么?我喜欢维基百科,但这是一个严重的拗口。很快就变得很清楚,反向传播不是一个简单的概念,确实需要一些认真的努力来消化将扔给你的概念和公式。虽然在你亲自动笔之前,你永远不会完全理解某些东西,但这里的目标是提供一种资源,使这样一个至关重要的概念更容易被那些已经在使用神经网络并希望“在引擎盖下达到顶峰”的人所理解。基本面不应该隐藏在公式的面纱后面,如果只是以一种连贯的方式呈现,将呈现一个路线图,而不是一个路障。
在理解反向传播的情况下,我们得到了一个方便的视觉工具,实际上是一张地图。这张地图将直观地引导我们完成推导,并把我们带到最终目的地,反向传播公式。我所指的图是神经网络本身,虽然它不遵循与计算图相同的约定,但我将以大致相同的方式使用它,并将它称为计算图,而不是图,以将其与更正式的图结构区分开来。这种视觉方法只有在读者能够看到过程发生时才真正受到启发,在一系列步骤中写下来会遇到常见的陷阱,即生成一大堆公式,其中的联系不是立即可见的,读者会不知所措。需要说明的是,我们最终仍然会得到许多公式,这些公式本身看起来令人生畏,但是在观察了它们演化的过程之后,每个公式都应该是有意义的,事情变得非常系统化。
这里用来传达这种视觉信息的工具是 manim,这是一个数学动画库,由来自 3Blue1Brown YouTube 频道的 Grant Sanderson 创建。我还必须从他的神经网络系列的网络类中使用一些代码。如果你不熟悉他的频道,帮自己一个忙,去看看吧( 3B1B 频道)。虽然 manim 是我选择的工具,但它不是最容易的,在某种程度上介于“我已经走得太远了,现在不能停下来”和“我已经超出了我的能力范围”之间。我可能后悔这个决定,但我们在这里。如果你刚开始学习神经网络和/或需要复习前向传播、激活函数之类的东西,请参见[参考中的 3B1B 视频。2 获得一些立足点。一些微积分和线性代数也会对你有很大的帮助,但是我试着解释一些基本的东西,所以希望你仍然掌握基本的概念。虽然用代码实现神经网络对理解有很大的帮助,但是你可以在没有真正理解它的情况下很容易地实现反向传播算法(至少我是这样做的)。相反,这里的重点是详细了解反向传播实际上在做什么,这需要理解数学。
网络和符号
使用一个简化的模型来说明概念,以避免过程过于复杂。使用了一个 2 输入、2 输出、2 隐藏层网络,如图 1 所示。输出节点用表示误差的 e 表示,尽管你也可以看到它们通常用 C 表示成本函数。这通常是一个函数,如均方误差(MSE)或二进制交叉熵。 E 节点是 e ₁和 e ₂.的总误差或总和与典型的神经网络布局相比,这里的主要区别在于,我已经明确地将隐藏节点分成两个独立的函数,加权和( z 节点)和激活( a 节点)。这些通常被分组在一个节点下,但是为了更清楚,这里需要单独显示每个功能。我假设我们一直在处理一个训练示例,实际上,您必须对训练集中的所有训练示例进行平均。
图 1:示例神经网络设置(图片由作者提供)
现在,成功的一半是把符号弄清楚。图 2 显示了示例网络中节点和权重的符号。上标表示层,下标表示节点。
图 2:索引符号(作者图片)
权重下标索引可能会向后显示,但在我们构建矩阵时会更有意义。以这种方式索引允许矩阵的行与神经网络的行对齐,并且权重索引符合典型的(行、列)矩阵索引。
在我们开始数学之前,最后说明一下,为了传达推导的视觉本质,我使用了 GIF。有时你可能想停止或放慢一个动画,GIF 显然不是理想的,所以也请参见附带的 YouTube 视频以更好地控制速度。
最终层方程
反向传播的最终目的是找到误差相对于网络中权重的变化。如果我们在寻找一个值相对于另一个值的变化,那就是导数。对于我们的计算地图,每个节点代表一个函数,每个边在连接的节点上执行一个操作(乘以权重)。我们从错误节点开始,一次向后移动一个节点,取当前节点相对于前一层中的节点的偏导数。每一项都链接到前一项上以获得整体效果,这当然是链式法则。
图 3:追踪 w11 的地图错误(图片由作者提供)
注意:该层上的权重仅影响₁e或₂e中的一个输出,因此在最终方程中仅出现相关误差。
在图 4 中,对从错误节点到最终层中每个权重的每条路径都进行了这种边和节点的追踪,并快速通过该路径。
图 4:追踪最终图层中剩余权重的地图(图片由作者提供)
在这一点上,我们现在有一堆公式,如果我们不做一些簿记工作,这些公式将很难保持跟踪。这意味着将术语放入矩阵中,这样我们可以更容易地管理/跟踪术语。图 5 显示了术语是如何分组的,值得注意的是 Hadamard 运算符的使用(圆圈内有一个点)。这用于元素式矩阵乘法,有助于简化矩阵运算。
图 5:将术语排列成矩阵(图片由作者提供)
矩阵符号的最终方程如图 6 所示,我用大写字母表示变量的矩阵/向量形式。右边的前两项被重新分解为一个 delta 项。当我们追踪前面的层时,这些项会重复出现,因此计算一次并将其存储为 delta 变量以备将来使用是有意义的。
图 6:最终图层的最终矩阵(图片由作者提供)
更深层
对于更深的层,同样的方法适用于两个关键的更新:1)增量项再次出现在后面的层中,因此我们将进行适当的替换;2)现在从总误差节点到感兴趣的权重将有两条路径。当多个分支汇聚在一个节点上时,我们将添加这些分支,然后继续乘以剩余的函数链。图 7 显示了第一层权重之一的过程。
图 7:第一层链规则示例(图片由作者提供)
现在,如果我们仔细观察,您会注意到重复的项,图 8 回顾了以前的层方程以及我们当前的 w₁₁ 方程,并显示了哪些项是重复的,并将它们作为 delta 因子。
图 8:在最后一层和前一层计算中找到相似的项,并将其分解为增量项(图片由作者提供)
所有后续等式都遵循相同的方法。因为这变得相当重复,并且因为我只能把这么多的长度塞进 GIF,所以在图 9 中对所有剩余的权重快速重复这个过程。
图 9:第一层剩余的渐变计算(不要眨眼)
我们又一次有了一大堆公式,我们将通过把它们输入矩阵来整理它们。一般分组遵循与第一层相同的模式。
图 10:第一层的矩阵分组(作者图片)
最左边的矩阵当然可以进一步分解,我们需要单独的 delta 值,这样我们就可以简单地插入从前一层计算的值。现在,按照图 11,您必须回忆点积,它是行乘以列,所以我们还添加了一个转置到 delta 项。
图 11:分解增量并最终确定矩阵公式(图片由作者提供)
添加矩阵符号给出最终公式
图 12:第一层的最终矩阵方程(图片由作者提供)
现在,无论激活函数是什么,图 12 中的一些导数项都是相同的。由边连接的节点产生线性输出,然后将其馈送到激活函数以引入非线性。给定函数的线性性质,找到节点函数相对于前一节点的导数是简化的,并且可以直观地确定。取 dz/da 项,这个导数告诉我们输出( z ₁)相对于输入、 a ₁.如何变化这些功能只通过边缘w₁₁连接,所以权重是唯一的方式,其中 a ₁可以改变 z ₁也就是 dz/da = w ₁₁.图 13 表示由边连接的节点,并将边值代入矩阵作为导数项的解。
图 13:节点之间连接的可视化指示和结果衍生(图片由作者提供)
对于 dz/dw 来说也是类似的情况。在这种情况下,导数是相对于边而不是节点的,但同样的逻辑成立,只有一个通过边连接的函数影响 z 节点,那就是输入(我将它标记为 x⁰,但也可以标记为 a⁰).然后,导数解可以代入图 14 的矩阵方程。
图 14:第一个节点和输入层之间连接的可视化指示,以及将导数解代入矩阵方程的结果(图片由作者提供)
我们终于有了最终层和初始层的方程。
图 15:两层网络的最终方程(图片由作者提供)
广义方程
如果你在阅读迈克尔·尼尔森的优秀在线书籍[3],他会像 3B1B [4]一样注意到一些更通用的等式。这里的方程同样可以进一步推广。这里,上标 1 表示当前层( l) ,上标 0 表示前一层 (l-1) 。顶部等式中的上标 2 指的是下一层( l+1 ),而在底部等式中,它指的是最后一层(L)…或者只看图 16 中的替换。
图 16:推广任意层深的方程(图片由作者提供)
偏见
尽管跳过偏见并告诉你这很简单并且是从上面得出的很有诱惑力,但看到它至少被解决一次确实很有帮助。因此,我将在图 17 中介绍一个例子。
图 17:描绘出一个偏向梯度(图片由作者提供)
如你所见,过程是一样的。需要注意的重要一点是,对于给定的层,除了最后一项,所有的项都与我们刚刚找到的关于给定权重的方程相同。最后一项只是偏差,其值为 1(偏差权重项用于调整偏差)。这使我们能够相对容易地简化和推广偏置方程,如图 18 所示。
图 18:归纳偏见术语(作者图片)
将所有方程组合起来,我们就得到矩阵形式的最终广义方程组。
图 19:最终的反向传播方程(图片由作者提供)
结束语
如果一张图片胜过千言万语,那么一打以上的 GIF 肯定更有价值(或者你可能再也不想看到另一张 GIF 了)。我真的希望这有助于阐明一个棘手的概念。如果你喜欢这个,我希望很快会有更多的内容。这篇文章实际上是我的原始项目的一个迂回,我的原始项目是建立一个单一的拍摄对象探测器,在几个点上打破了导致我进入这个兔子洞。
与本文相关的其他资源:
参考
[1] D. Rumelhart,G. Hinton 和 R. Williams,通过反向传播错误学习表征 (1986),自然
[2] G. Sanderson,但是什么是神经网络呢?深度学习,第一章 (2017),3Blue1Brown
[3] M. Nielsen,神经网络与深度学习—第二章,决心出版社 2015
[4] G. Sanderson,反向传播微积分——深度学习,第 4 章 (2017),3Blue1Brown
贝多芬定理是我用过的一个 YouTube 频道,里面有很多关于使用 Manim 的有用视频。
理解人工智能系统中的偏见和公平
公平和偏见
对关键问题的一些基本概念的图解介绍
当人工智能成为头条新闻时,往往是因为偏见和公平的问题。一些最臭名昭著的问题与面部识别、警务和医疗保健有关,但在许多行业和应用中,我们已经看到了机器学习在创造一个一些群体或个人处于不利地位的社会方面的失误。那么,我们如何开发人工智能系统来帮助做出导致公平和公正结果的决策?在 Fiddler,我们发现它始于对人工智能中的偏见和公平的清晰理解。因此,让我们用一些例子来解释我们使用这些术语的意思。
如果你更喜欢看视频而不是读文章,看看这个视频吧!此外,视频中有一些我在本文中提到的偏见类型的具体、真实的例子。
AI 中的偏见是什么?
偏差可以以多种形式存在,并且可以在模型开发流程的任何阶段引入。从根本上来说,偏见存在于我们周围的世界,并被编码到我们的社会中。我们无法直接解决世界上的偏见。另一方面,我们可以采取措施从我们的数据、我们的模型和我们的人工审查过程中剔除偏见。
作者图片
数据偏差
数据中的偏差有几种表现形式。以下是罪魁祸首:
历史偏见是世界上已经存在的偏见,已经渗透到我们的数据中。即使给定完美的采样环境和特征选择,这种偏差也可能发生,并且倾向于出现在历史上处于不利地位或被排斥的群体中。2016 年的论文“男人对于计算机程序员就像女人对于家庭主妇”说明了历史偏见,该论文的作者表明,在谷歌新闻文章上训练的单词嵌入展示了事实上延续了社会中基于性别的陈规定型观念。
表示偏差稍有不同——这发生在我们定义和抽样人口以创建数据集的方式上。例如,用于训练亚马逊面部识别的数据大多基于白人面孔,导致检测深色皮肤面孔的问题。代表性偏差的另一个例子是通过智能手机应用程序收集的数据集,这最终可能会低估低收入或老年人口的代表性。
测量偏差发生在选择或收集用于预测模型的特征或标签时。容易获得的数据通常是实际特征或感兴趣标签的嘈杂代理。此外,测量过程和数据质量往往因群体而异。随着人工智能被用于越来越多的应用,如预测性警务,这种偏见可能会对人们的生活产生严重的负面影响。在 2016 年的一份报告中,ProPublica 调查了预测性警务并发现,在预测累犯(某人将再次犯罪的可能性)中使用代理测量可能会导致黑人被告比白人被告因同样的罪行获得更严厉的判决。
建模偏差
即使我们有完美的数据,我们的建模方法也会引入偏差。这有两种常见的表现方式:
评估偏差发生在模型迭代和评估期间。使用训练数据来优化模型,但是它的质量通常是根据某些基准来衡量的。当这些基准不代表总体,或者不适合模型的使用方式时,就会产生偏差。
聚合偏差在模型构建过程中出现,不同的群体被不恰当地组合在一起。在许多人工智能应用中,感兴趣的群体是异质的,单一模型不可能适合所有群体。医疗保健就是一个例子。为了诊断和监测糖尿病,模型在历史上使用血红蛋白 AIc(hba1c)的水平来进行预测。然而,2019 年的一篇论文显示,这些水平在不同种族之间以复杂的方式存在差异,适用于所有人群的单一模型必然会出现偏差。
人类评论中的偏见
即使您的模型做出了正确的预测,当人类评审员决定是接受还是忽略模型的预测时,他们也会引入自己的偏见。例如,一个人类评审员可能会基于他们自己的系统偏差推翻一个正确的模型预测,说一些大意为“我知道那个人口统计,他们从来没有表现得很好。”
什么是公平?
在许多方面,人工智能中的偏见和公平是一个硬币的两面。虽然公平没有普遍认同的定义,但我们可以广义地将公平定义为没有基于个人或群体特征的偏见或偏好。记住这一点,让我们看一个机器学习算法如何遇到公平问题的例子。
人工智能示例中的公平性
假设我们创建了一个二元分类模型,我们相信我们有非常准确的预测:
作者图片
如果这个数据实际上包括两个不同的潜在群体,一个绿色群体和一个蓝色群体,那么公平性就会受到质疑。这些组可以代表不同的种族、性别,甚至是地理或时间上的差异,比如早晚用户。
作者图片
这种公平性问题可能是聚合偏差的结果,就像前面提到的将糖尿病患者作为一个同质组进行治疗的例子一样。在这种情况下,对这两类人群使用单一阈值会导致不良的健康结果。
最佳实践
为了公平地解决这个问题,最佳实践是确保您的预测对每个组都经过了校准。如果你的模型的分数没有针对每一组进行校准,很可能你系统地高估或低估了你的一个组的结果的概率。
除了组校准之外,您可以决定为每个组创建单独的模型和决策界限。对于我们这些群体来说,这比单一门槛更公平。
作者图片
然而,创造额外的门槛会导致一个新的问题:个人公平。在下面的图表中,黑色箭头指向蓝色和绿色的个体,它们拥有非常相似的特征,但被 AI 系统完全不同地对待。正如你所看到的,在群体公平和个人公平之间经常存在一种紧张关系。
作者图片
如图所示,人工智能中的偏见和公平是一个发展中的领域,越来越多的公司,尤其是受监管行业的公司,正在加大投资,以建立治理良好的实践。在以后的帖子中,我将介绍更多关于公平的概念,包括交叉公平,并展示如何使用不同的度量来解决这些问题。
参考文献:
鲁本·宾斯。论个体公平与群体公平的表面冲突。更正,abs/1912.06883,2019。网址 http://arxiv . org/ABS/1912.06883。
乔伊.波伦维尼和蒂姆尼特.格布鲁。性别差异:商业性别分类的交叉准确性差异。在公平、问责制和透明度会议上,第 77-91 页,2018 年。
Ninareh Mehrabi、Fred Morstatter、Nripsuta Saxena、Kristina Lerman 和 Aram Galstyan。机器学习中的偏见和公平问题综述。更正,abs/1908.09635,2019。网址http://arxiv.org/abs/1908.09635。
理解偏差:神经科学&伦理人工智能的关键理论
公平和偏见
将批判理论框架应用于 AI 伦理学,同时使用神经科学来理解具有突触可塑性的无意识偏见。
一年前,当讨论面部识别中存在的种族偏见时,人工智能先驱严乐存在推特上引起了争议,“当数据有偏见时,ML 系统就会有偏见”(来源:推特)。这激起了人工智能伦理研究员 Timnit Gebru 的回应,她对这个问题过于简单的框架表示失望,这是基于她在人工智能伦理方面的专业知识的观点(来源:Twitter )。Gebru 的回答和随后的对话被主流媒体放大,虽然这确实在人工智能社区引发了更广泛的偏见讨论,但媒体的焦点是 Lecun 选择如何(错误)沟通。
抛开诚意不谈,乐存的道歉(来源)让我想起了奥德·洛德关于内疚的想法,
“太多时候,它变成了一种保护无知和事物延续现状的手段,是对永恒性的终极保护。”(洛德,1981 )。
关于被边缘化的人应该负责教育他人偏见的合理期望,Lorde 说,
“我们的精力不断流失,而这些精力本可以更好地用来重新定义我们自己,并设计出改变现在、建设未来的现实情景。”(洛德,1980 )。
我和格布鲁一样沮丧。社会偏见渗透到人工智能的每一个方面,从数据集,研究环境,甚至从业者本身。一个公平的未来取决于我们创造道德人工智能的能力;因此,我认为批判性地反思的偏见是很重要的——尽管这在情感上是多么困难,尽管没有简单的答案。此外,讨论偏见而不解决社会和结构问题是一种空洞的、最终毫无意义的努力。因此,我寻求将批判理论与神经科学相结合,以理解无意识偏见,并绘制出一条通往伦理人工智能的道路。
在一个由两部分组成的系列中,我从神经科学、遗传学、心理学、批判理论、语言学、数学和教育学等不同领域中汲取经验,阐述我的观点,即人工智能伦理学可以受益于结合各种学科的非常规方法。这第一篇文章使用神经科学来理解无意识偏见,同时与人工智能建立直接联系,所有这些都在一个关键的理论框架内。第二篇文章探索了关于神经科学的学习,并提出了一个基于关键理论的因果关系观点,允许讨论神经符号人工智能,作为一种潜在实现机器正念的方式。
在这里,我从批判理论开始,并将该领域与 STEM 学科进行对比,以表明人工智能从业者可以从交叉性的思考中受益。接下来,我将简要介绍神经科学,并强调特定子学科之间的重要差异以及它们与人工智能的关系。这直接导致了对突触可塑性的讨论,这为讨论本文的中心话题无意识偏见奠定了基础。接下来,我用批判理论解决了数据集中的偏见问题。最后,我回到我以前的语言学系列(第 1 部分 & 第 2 部分),并使用人工智能语言模型中的偏见作为一个实际例子来展示神经科学和批判理论与人工智能伦理的联系。
批判理论与人工智能伦理
很久以前,我被介绍给那些帮助我形成对世界的看法的作家:奥德·洛德、玛娅·安杰洛、贝尔·胡克、奥克塔维亚·巴特勒和安吉拉·戴维斯。我相信我姐姐的影响;与此同时,她也分享了批判理论家的作品,如弗朗兹法农,爱德华·萨义德,保罗·弗莱雷和米歇尔福柯。虽然批判理论有时被狭隘地定义为法兰克福学派,但我支持批判理论的更广泛的观点,其中学科的目的是旨在减少压迫和增加自由的社会调查。引用福柯的话来说,这需要挑战传统的权力结构,
“批判并不在于说事情不像现在这样好。它在于看清公认的做法是基于什么样的假设、熟悉的概念、既定的和未经检验的思维方式……进行批评就是使那些现在太容易的行为变得更难。”(福柯,1982 )。
STEM 学科中流行的态度要么是对批判理论的无知,要么是对批判理论的蔑视;这个领域被普遍认为在智力上不如纯科学。流行的观点包括宣称“事实胜于感觉”的优越性。考虑非经验主义主张的价值已经变得激进,数据的缺乏足以成为否定整个学科的理由。我不同意这种观点,而是选择接受批判理论有价值的激进观点,特别是在涉及到人工智能新兴领域的发展时。具体来说,创造道德人工智能的尝试将受益于对交叉性的理解(克伦肖,2017 )。偏见不仅仅是种族主义、性别不平等或宗教不容忍,它是所有形式的偏见相互作用的结果。
1989 年,金伯利·克伦肖创造了“交叉性”这个术语,试图描述社会身份相互作用的复杂方式。如今,这一含义被那些受到当前社会等级丧失威胁的人扭曲了,被嘲笑和斥为“身份政治”。然而,根据克伦肖自己的说法,
“一个透镜,一个棱镜,让我们看到各种形式的不平等是如何相互作用、相互加剧的。我们倾向于将种族不平等与基于性别、阶级、性取向或移民身份的不平等区分开来。人们经常忽略的是一些人是如何受到所有这些的影响,而这种体验不仅仅是其各个部分的总和。”(来源)。
人工智能已经在许多人生活的许多方面交织在一起;这是一个不断增长的趋势,因为人工智能的使用在各个行业都在增加。在 2018 年的一本书中,*人工智能超级大国:中国,硅谷和新的世界秩序,*人工智能先驱和风险投资家李开复将人工智能的现状描述为处于“实施时代”。在谈到人工智能的潜在危害时,李开复说,
“[b]将它们推向市场不需要人工智能研究的重大新突破,只需要日常实施的具体工作:收集数据、调整公式、在实验和不同组合中迭代算法、制作产品原型,以及对商业模式进行实验。”(李,2018 )。
李对人类与人工智能共存的愿景是基于促进彼此的爱;他建议将爱作为设计社会项目的蓝图,以对抗人工智能未来的生存威胁。虽然我同意爱情的重要性,但我认为他的观点所缺乏的是对当前对我们共同人性的生存威胁的理解,这种根深蒂固的系统性和结构性不平等正是人工智能产生的根源。
伦理人工智能需要不仅仅是确保像 GPT-3 这样的语言模型不会反刍种族主义文本,或者面部识别可以识别不同的面孔。一个鼓舞人心的例子是强有力的纪录片 《编码偏见 ,它始于 Joy Buolamwini 和 Timnit Gebru 撰写的一篇论文,题为“ 性别阴影:商业性别分类的交叉准确性差异 ”。交叉性和批判理论的影响贯穿整部电影。这一点在让所有利益相关方参与进来的努力中尤为明显,尤其是那些目前受到面部识别技术负面影响的利益相关方。重要的是,研究人员咨询了受影响的人,并将他们纳入了围绕伦理人工智能的讨论。更重要的是,这项工作正在持续进行,并朝着让研究变得可及的方向不断努力,我希望所有参与人工智能伦理的研究人员和组织都能采用这种方法。
整个人工智能行业、政府,尤其是人工智能从业者,应该提出交叉性的问题。问题如:谁研究、构建和教授 AI?谁能接触到,谁从中获利,谁的工作被 AI 服务取代?谁生产,谁拥有用于构建人工智能的数据?谁控制着 AI 所需的基础设施?。更广泛地说,谁对艾的意见被听到了?谁来决定围绕 AI 的法律,谁来执行这些法律?哪些国家有能力研究或建设人工智能,它将被部署在哪里?哪些国家的经济会因 AI 而受益或受损?。这些问题仅仅触及了表面,但目前那些有能力实施变革的人并没有讨论这些问题。
理解交叉只是朝着正确方向迈出的一步;然而,这是一个包括所有人在内的容易实现的目标。女权主义者。AI 是一个通过交叉女权主义参与进来的组织,致力于让每个人都能接触到 AI——他们的资源页面是一个开始学习的好地方。他们推荐的一些书籍包括:萨菲亚·乌莫哈·诺布尔的 压迫算法 、凯茜·奥尼尔的 数学毁灭武器 以及鲁哈·本杰明的 科技之后的种族 。当前的数据科学、机器学习和人工智能教育计划将受益于将这些材料纳入他们的课程,为学生提供人工智能伦理的交叉框架。我不自称在批判理论或交叉性方面有专长,但这些想法是帮助我批判性思考人工智能的基础。作为起点,交叉性提供了希望,随着人工智能的发展,我们不会把那些拥有最少而可能受益最多的人抛在后面。如上所述,接下来的两个部分介绍了神经科学和无意识偏见,然后在最后两个部分回到批判理论,进一步讨论人工智能的发展和伦理。
神经科学:精选分支学科概述
神经元是构成人脑结构的细胞单位,这是 Santiago Ramón y Cajal 在 19 世纪 80 年代发现的事实( Rapport,2006 )。目前认为,人类大脑包含大约 860 亿个神经元,每个神经元大约有 10,000 个突触——这些可以被认为是神经元之间的连接。这篇来自《约翰·霍普斯金医学》的有用文章提供了一个大脑解剖和功能的简单概述。为了便于理解,本文中介绍的所有神经科学概念都将有足够详细的解释,以保证没有技术复杂性的理解。
在最简单的层面上,神经科学是对神经网络如何工作的研究,与研究大脑疾病的神经学形成对比。神经科学有几种类型或子学科,这里我重点介绍与人工智能最相关的三种:系统神经科学、认知神经科学和计算神经科学。系统神经科学与系统生物学相关,后者涉及复杂生物系统的计算和数学建模。因此,系统神经科学包括在分子和细胞水平上研究大脑,然后将这种理解与认知功能(如记忆存储和语言)联系起来。这一分支学科对人工智能很有用,因为它采用了研究物理大脑的方法来理解思维,例如,大脑的物理结构如何与意识相关联。
认知神经科学采取了相反的方法,它受心理学的影响很大,并依靠理解心灵(认知)来帮助理解大脑。例如,它采用记忆的心理学分类,通过使用情景、语义和程序记忆的定义。关于人工智能,一个关键的概念是意识的神经关联,它可以被定义为与特定意识体验相关的大脑活动的特定模式。例如,考虑强化学习,决策的神经关联表明,大脑的额叶皮层通过添加情景记忆的上下文进行干预,这样决策就受到了过去事件的影响。认知神经科学也与符号人工智能领域直接相关,其中智能被设计为根据因果规则通过逻辑运算操纵符号。
除了认知神经科学,符号 AI 植根于数学的哲学;它遵循基于逻辑主义的观点,即人类的认知是通过形成内部符号表示和创建处理这些概念的规则来实现的。所以,有了符号化的 AI,机器认知就是以一种机器拥有世界知识的方式,将这些因果规则形式化的结果。作为一名哲学家和数学家,伯特兰·罗素是逻辑主义的坚定捍卫者,他认为数学可以还原为逻辑。符号逻辑的建立可以归功于罗素;1910 年,他出版了《数学原理》,提出了一个公理系统,所有的数学都可以建立在这个系统上。
二十年后,库尔特·哥德尔提出了他的不完全性定理,证明了任何原始递归算法系统都有极限。本质上,哥德尔证明了不可能使用符号逻辑来创造一套完整一致的公理来对世界进行建模。这一缺陷延伸到符号人工智能,它在 20 世纪 50 年代和 80 年代流行,但在很大程度上被放弃,转而支持神经元的连接主义观点。神经网络。从符号人工智能向神经网络的转变反映在神经科学中,认知神经科学在 1985 年被计算神经科学的新分支超越。
虽然哥德尔的不完全性定理可以作为符号 AI 对于认知不足的数学基础,但我们仍然没有正式的数学理论来解释神经网络在实践中为什么有效。因此,计算神经科学的重要性;这个领域寻求用数学模型来理解不仅仅是认知,还有大脑的结构和发展。在这个分支学科中,有兴趣开发突触可塑性现象的数学模型,这是理解无意识偏见所需的关键神经科学概念。接下来的部分涵盖了这一点,本系列的第二篇文章更详细地涵盖了计算神经科学。
突触可塑性和无意识偏向
突触可塑性,有时也被称为神经可塑性,由唐纳德·赫布于 1949 年首次提出(来源)。这个想法是神经元之间的突触连接能够改变;这种灵活性很重要,因为活动的变化决定了突触连接的强度。突触之间的现有连接可能会消失或变得沉默。此外,新的连接可以在一对神经元突触之间的新位置形成。这意味着生物神经网络的网络结构是动态的,突触之间的连接表现出时间可塑性。
有趣的是,学习是通过调整突触连接的强度来实现的,这个想法将在本系列的第二篇文章中深入探讨。这里,突出的一点是,突触可塑性可以被视为神经元在学习过程中的适应。与此相关的是,Hebb 试图通过理论化的方法来解释突触可塑性,即突触效能的增加源于突触前细胞对突触后细胞的重复和持续刺激( Choe,2014 )。更正式地说,Hebbian 学习理论是这样一种原理,即一个突触前神经元 A,如果在它本身(神经元 A)活跃时成功地重复激活一个突触后神经元 B,将逐渐变得更有效地激活神经元 B ( Hebb,1949 )。
短期突触可塑性是指在亚秒时间尺度上发生的突触强度变化,例如强度快速增加或减少,随后连接恢复正常。另一方面,长期突触可塑性可以持续几分钟到几年,并代表了大脑将信息存储为记忆的方式(来源)。在去年发布的一本神经科学书籍: Sway:解开无意识偏见 中,Pragya Agarwal 用长期突触可塑性来解释无意识偏见。在《科学美国人》 、 发表的一篇评论文章中,Agarwal 写道:
“刻板印象等社会态度和期望会改变大脑处理信息的方式,因此基于大脑的行为特征和认知技能的差异会随着时间、地点和文化的变化而变化。这意味着我们的无意识偏见并不是天生的。它们是通过我们的经验习得的,因此也可以不学。”(阿加瓦尔,2020 )。
Agrawal 使用神经成像研究来支持她关于无意识偏见的说法,具体来说,最近的fMRI研究表明,人们在对熟悉和陌生的情况进行推理时使用了大脑的不同区域。该研究指出了大脑的两个特定部分,即杏仁核和前额叶皮层,这两个部分都对刻板印象做出反应。Agrawal 解释说,当遇到一个新的人时,我们会迅速“将行为转化为带有可识别信息的神经信号,以形成对他们的印象”。当这种情况发生时,在潜意识层面,前额叶皮层同时监控来自我们所有感官的神经信息,与社会规范或个人偏好联系起来。这意味着有意识的大脑会形成不完整的解释,并且经常会包含某种偏见,也就是说,我们的社交、记忆和经历会产生无意识的偏见。
杏仁核对于理解偏见非常重要,因为它会无意识地标记传入的刺激,因此人们会迅速将他人归类为"像我和不像我"。Agrawal 声称这是偏见和歧视的根源,这意味着固型激活了大脑中与威胁和恐惧相关的部分(杏仁核)。更具体地说,fMRI 数据显示,当人们看到“与自己不同种族背景的人的面部图像时,往往比看到相同种族的人更容易激活杏仁核。”。此外,研究表明,“群体外的负面偏见甚至比群体内的同理心更加突出。”(来源)。
虽然神经成像清楚地表明无意识偏见与杏仁核和前额叶皮层都有关,但突触可塑性提供了一种解决这两个区域偏见的方法。阿格拉瓦尔强调的要点是:就像无意识偏见是后天习得的一样,它也可以是后天习得的。在本系列的第二篇文章中,我回到了这个想法,在那里我将突触可塑性与正念冥想联系起来,提出了一种可以在机器中消除无意识偏见的方法。接下来的两节提供了偏见和人工智能发展的关键理论框架。
数据集偏差和人工智能开发
Borealis AI 的产品总监凯瑟琳·休姆(Kathryn Hume)提供了一个很好的理由来批判性地思考关于人工智能发展的无意识偏见。她说
“算法就像折射人类偏见的凸面镜,但以一种相当生硬的方式进行。他们不允许像那些我们经常用来维持我们的社会的礼貌小说”(来源)。
她进一步警告说,
“我们需要小心,因为如果我们没有很好地设计这些系统,它们将会对数据进行编码,并可能放大当今社会存在的偏见。”(来源)。
当严乐存提出人工智能中的偏见源于数据中的偏见时,他暗示数据本身是客观的,一个平衡的和种族代表性的数据集将解决这个问题。如前所述,这是一个有问题的方法,因为它天真地忽视了系统和结构的偏见。尽管如此,它提供了一个机会来更深入地挖掘数据应该被视为客观的观点。在 被压迫者教育学 中,保罗·弗莱雷暗示,主体性在改变权力结构的斗争中发挥着作用。弗莱雷指出,“没有主观性,就无法想象客观性。两者都不能脱离对方而存在,也不能一分为二。”;他提出了一种“主观和客观处于不断的辩证关系中”的方法(“T10”Freire,1970 )。
数据主观性的一个令人信服的例子是由 Northpointe 创建的用于评估被告再犯风险的 COMPAS(替代制裁的矫正罪犯管理概况)软件。2016 年 ProPublica 的一项研究揭露了算法中编码的种族偏见;该软件更有可能对黑人产生 1 型错误(假阳性),对白人产生 2 型错误(假阴性)。这意味着黑人被告被错误地分配了比白人被告更高的分数,因此在预测暴力犯罪的风险时,该算法的准确率只有 20%(来源)。COMPAS 是一种专有软件,算法的工作原理被视为商业秘密,这意味着该软件相当于一个黑匣子。假设这些算法是在一个平衡的、具有种族代表性的数据集上训练的,这将强调一点,即简单地将有色人种包括在数据集中并不能解决偏见的问题。
然而,清楚的是,当累犯风险评估工具基于诸如“你的父母之一曾经被送进过监狱吗?”(来源),偏差问题延伸到数据之外。Northpointe 的创始人、前统计学教授蒂姆·布伦南(Tim Brennan)声称,很难构建一个不包括与种族相关的项目的分数,他说,“如果这些项目从你的风险评估中被忽略,准确性就会下降”(来源)。根据 Northpointe 的披露,他们的 COMPAS 数据涵盖了贫困、失业和社会边缘化(来源)。
这些社会问题与结构性和系统性种族主义密不可分;事实上,监狱作为一种制度和犯罪本身的定义,有着种族主义的历史。革命的书里,“ 监狱过时了吗? ”,安吉拉·戴维斯考察了美国奴隶制和早期监狱系统之间的历史联系。她指出,从历史上看,在美国,种族一直在构建犯罪推定中发挥着核心作用,
奴隶制废除后,前蓄奴州通过了新的立法来修改奴隶法典,以类似奴隶制时期的方式来规范自由黑人的行为。(戴维斯,2003 年)。
Brennan 和 Northpointe 忽略了历史和当前的现实,主观地选择了数据,从而产生了编码和传播社会不公的算法。因此,将数据视为客观忽略了根深蒂固的有问题的偏见。
因此,在实践中,在收集手段、框架、包容性措施、问题的适当性、质量标准以及最重要的结构性和系统性压迫的存在方面,数据应被视为主观的。为了支持这样的调查,麻省理工学院和 MILA 大学的研究人员创建了一个数据集,用来测量人工智能语言模型中的刻板印象偏见。 StereoSet 提供了一种测量语言模型对与种族、性别、宗教和职业相关的刻板印象的偏好的方法,同时确保所使用的去偏置技术不会对潜在的模型性能产生负面影响。本文的最后一节继续讨论人工智能语言模型中的偏差;然而,请注意,这种偏见存在于所有类型的人工智能中,这些模型只是一个例子。
人工智能语言模型和语言偏见
我对自然语言处理(NLP)有着浓厚的兴趣,并且我一直在与人工智能语言模型中的偏见问题进行直接斗争。这场斗争促使我在今年早些时候写了两篇文章,使用语言学理论来探索人工智能语言模型。第一篇对比了语言相对论和语言普遍性,以更好地理解指导语言模型发展的哲学。在第一篇文章的前面,我提到了谷歌如何解雇 Timnit Gebru,因为他研究人工智能语言模型的社会和环境影响。我的第二篇关注语言学的文章直接讨论了偏见的问题;作为一个实际的例子,我主要关注令人印象深刻的大型 GPT-3 模型。引用最初的 GPT-3 论文,“[ GPT-3 ]保留了它被训练的数据的偏差。…这可能导致模型产生刻板印象或偏见的内容”。
我目前的观点是,人工智能语言模型中的偏见不仅仅是数据,结构也很重要;具体来说,语言的结构很重要。通过研究经济不确定性,我偶然发现了沃尔夫社会经济学,康奈尔大学的托马斯·佩平斯基教授将其描述为,
“一个新兴的跨学科研究领域,认为语言结构解释了跨社区的信仰、价值观和观点的差异”( Pepinsky,2019 )。
当文本被用于经济分析时,经济语境中的相关性是理解语言所带来的不确定性。M. Keith Chen ( 2013 )在美国经济评论上的先前研究也表明语言对经济行为有影响。陈认为,一种语言的未来和现在的语法联系会影响一个人的储蓄习惯(陈,2013 )。经济研究的当前趋势包括采用机器学习技术;我本人写过经济学的因果 ML,这里这里和这里。更重要的是,NLP 对经济学非常有用;因此,语言结构的重要性直接关系到人工智能对经济学的有用性。
撇开经济学不谈,既然语言的结构具有相关性,那么将语言学应用于人工智能的发展是很自然的。伦斯勒理工学院认知科学系的研究人员最近出版了一本名为“*人工智能时代的语言学”*的书,该书专注于自然语言理解的语言学方法(NLU)。令人惊讶的是,作者 Marjorie McShane 和 Sergei Nirenburg 已经开源了整本书,这本书可以在麻省理工学院出版社网站上获得。作者声称,歧义是 NLU 的一个障碍,人类的方法是利用上下文来解读单词背后的意思。然而,当前的 NLP 范式是使用非常大的数据集和单词序列之间的统计关系来确定上下文,这种方法不能捕获含义。此前,在 AI 语言模型出现之前,包含词汇结构和本体的基于知识的系统用于向机器解释语言。不幸的是,这种类型的知识工程有很高的资源成本;因此,缺乏对意义的理解的替代统计方法流行起来。“修行者的行为就好像文字足以代表他们的意思,但事实并非如此.”,批判麦克沙恩(来源)。
她还声称,“在其发展的这个阶段,神经科学无法为我们承担的认知模型类型和目标提供任何令人满意的(句法或结构)支持。”。也许这是 NLU 的真实情况;然而,语言结构、突触可塑性和无意识偏见之间的联系提供了关于语言在人工智能模型中偏见编码中所起作用的见解。假设语言结构包含偏见的表达,假设这种结构直接影响大脑活动(例如,储蓄习惯),那么操纵突触可塑性可以提供一种解决人工智能语言模型中编码的偏见的方法就不难了。在本系列的第二篇文章中,我从多个角度探讨了这个观点。
最后的想法
批判理论可以帮助人工智能实践者认真思考人工智能伦理,神经科学可以提供对偏见问题的神经洞察力。简单来说,系统神经科学依赖于生物学,认知神经科学依赖于心理学和符号逻辑,计算神经科学以对生物神经网络的定量理解为中心。相关地,可以说人工神经网络(AI)的目标是在设计和功能上模仿生物神经网络。为此,突触可塑性提供了一种理解生物神经网络的方法,其机制可以扩展到改进人工神经网络。
因此,在这个系列的第二部分中,我将人类学习与机器学习进行了对比,并讨论了层次结构的概念,以将批判理论与认知联系起来。然后,我将重点放在将突触可塑性与正念冥想联系起来,这让我能够探索从错误中学习是可能的这一想法。最后,我回到符号人工智能,并介绍神经符号人工智能的新领域,它是融合不同学习范式的混合方法的代表。这为从批判理论的角度讨论因果关系创造了空间,连接到用有意识的机器创造有道德的人工智能的主要目标。
如果读者对将交叉性融入人工智能伦理学的进一步材料感兴趣,我再次推荐以下书籍:萨菲亚·乌莫哈·诺布尔的 【压迫算法】;凯茜·奥尼尔的 【数学毁灭武器】 和鲁哈·本杰明的*【科技之后的种族】* 。
对本系列提到的话题感兴趣的读者可以在 Linkedin 上与我联系。我欢迎提问和反馈。
为数据科学家理解大 O 符号
包含 Python 示例和练习
伯纳德·赫曼特在 Unsplash 上的照片
由于数据科学被描述为数学、领域知识和计算机科学的交叉点,大 O 符号在计算机科学中是一个非常重要的概念。理解它将有助于我们开发更有效的算法,优化速度和内存,这在我们处理越来越多的数据时是最基本的。
在本文中,我们首先定义大 O 符号,以及它如何描述时间和空间复杂性。然后,我们研究不同的情况,并为每种情况导出大 O 符号。最后,我们建议你做一些练习来练习你的学习。
定义大 O 符号
大 O 符号是一个数学表达式,描述了当算法的自变量趋向于一个非常大的数时算法的效率。
它用于描述给定函数的时间和空间复杂度。
时间复杂度是一个描述函数运行时间的概念:函数完成任务需要多长时间。评估时间复杂度要考虑的重要因素可以是数组的长度、进行比较的次数或者必须调用递归函数的次数。
空间复杂度描述了运行函数必须分配的内存量或空间。评估空间复杂性时要考虑的重要因素还有数组的长度、声明的变量数量、数据结构的副本数量等。
大 O 符号常用来描述预期情况的最坏情况。我们很少关心最好的情况。
例如,考虑数组排序的时间复杂度。最好的情况是我们排序的每个数组都已经排序了!这样一来,数组有多大就无关紧要了。这样,时间复杂度与数组的大小无关,记为 O(1)。然而,知道这一点并不是很有用,因为如果所有的输入数组都已经排序了,我们很少需要开发排序算法。
让我们通过一个简单的例子来获得对大 O 符号的直觉。
获得大 O 符号的直觉
考虑以下情况:你住在离杂货店 5 分钟路程的地方,你把每样东西都装进了自己的袋子里。
现在,假设你有一个特别大的购物清单,并决定在一天内买下清单上的所有东西。时空复杂度是多少?
知道你住的地方离杂货店只有 5 分钟的路程,无论你买了多少东西,你都要花 5 分钟的时间去拿你的杂货。如果你买了一盒牛奶,那么你需要 5 分钟把它带回家。如果你买了 10 盒牛奶,你仍然需要 5 分钟才能把它带回家。因此,所花费的时间与你购买的商品数量无关。因此,时间复杂度表示为 O(1)。
空间复杂度呢?在这种情况下,您将每件物品打包在自己的袋子中,这意味着一个袋子只能装一件物品。因此,随着你购买的物品越来越多,你需要的袋子数量会线性增加:1 件物品需要 1 个袋子,3 件物品需要 3 个袋子,10 件物品需要 10 个袋子,以此类推。因此,空间复杂度被表示为 O(N),其中 N 是你在杂货店购买的商品数量。
可视化空间和时间复杂性
既然我们对如何得到大 O 符号有了直觉,让我们看看不同的表达式是如何相互比较的。下图显示了随着 N 的增加,一些常见的大 O 符号。
图片由作者提供。我们可以看到 O(N!)绝对是复杂度最差的场景,因为当 N 变大时,复杂度会迅速增加。另一方面,与其他情况相比,O(log(N))保持相对平坦。
看上面的图,我们可以看到我们是多么希望避免复杂度为 O(N!),因为当 N 变大时它迅速增加。
您可能还会注意到图中没有 O(1)。这是因为很难在避免错误结论的同时确定界限。原因是 O(1)不一定意味着比 O(N)或 O(log(N))好。它只是表示一条平坦的线,因此它不会随 N 增加。但是,O(1)可能比 O(N)或 O(log(N))慢。
因此,我们可以改进我们对大 O 符号的定义,并将其解释为时间和空间复杂度的增长率。
大 O 符号杂集
在进入例子和练习之前,这里有各种大 O 符号的提示、技巧和约定。
我们不关心 N 的倍数
如果我们把一个函数的复杂度评估为 O(2N),那么它等价于 O(N)。同样,O(5N)变成 O(N)。记住,大 O 符号描述了时间和空间的复杂性是如何扩展的。因此,我们可以放心地忽略 n 的任何倍数。
删除非主导术语
假设我们有 O(N + N)。从上图中,我们知道 O(N)的伸缩速度比 O(N)快得多。因此,O(N + N)等价于 O(N),因为当 N 较大时,O(N)的贡献与 O(N)相比将是最小的。
什么 O(N + log(N))?再一次,我们看上面的图,看到 O(N)是主导项,因为它比 O(log(N))增加得更快。因此,O(N + log(N))等价于 O(N)。
什么时候做加法和乘法?
当操作必须一个接一个地进行时,我们增加了复杂性。考虑下面的代码:
def some_func(a: list, b: list) -> None:
for each_a in a:
print(each_a)
for each_b in b:
print(each_b)
我们看到some_func
首先遍历数组a
,然后遍历数组b
。这样,时间复杂度为 O(A + B),其中 A 是数组a
的长度,B 是数组B
的长度。
现在,当函数必须为每个操作执行一个任务时,我们增加了复杂性。这通常发生在嵌套的 for 循环中。
def nested_some_func(a: list, b:list) -> None:
for each_a in a:
for each_b in b:
print(each_a, each_b)
这里,nested_some_func
有一个嵌套的 for 循环,为a
的每个元素打印b
的每个元素。所以时间复杂度相乘,表示为 O(A * B),其中 A 是数组a
的长度,B 是数组b
的长度。
我们什么时候看到 O(log(N))?
当问题空间在每次迭代中减半时,O(log(N))的复杂度是常见的。例如,二分搜索法的时间复杂度为 O(log(N))。
递归函数的复杂性
递归是一个概念,在这个概念中,一个问题通过一个较小问题的迭代来解决。以下列计算整数阶乘的函数为例:
def factorial(x: int):
if x < 0:
raise ValueError('Integer must be greater than or equal to 0')
elif x == 0:
return 1
elif x == 1:
return 1
else:
return (x * factorial(x-1))
假设我们想计算factorial(3)
,那么这个递归函数会做:
- 3 * 2!
- 3 * 2 * 1!
- 3 * 2 * 1 = 6
那么这里的时间复杂度是多少呢?在本例中,该函数运行了 3 次。因此,我们看到了整数和函数运行次数之间的线性关系。因此,时间复杂度为 O(N)。
我们现在准备着手做一些练习。
练习
花点时间通读每个函数,在向下滚动并查看解释之前,尝试自己找出时间复杂度的大 O 符号。
练习 1
def foo(a: list) -> None:
for each_a in a:
print(a)
for each_a in a:
print(a)
回答:我们对同一个数组迭代两次,得到 O(2N),相当于 O(N)。
练习二
def foo(a: int) -> bool:
if a > 0:
return True
else:
return False
回答:这是一个简单的 if 语句。所以它的时间复杂度不成比例,所以是 O(1)。
练习 3
def foo(a: list) -> None:
for i in a:
for j in a:
print(i, j)
答:我们在同一个数组上有一个嵌套的 for 循环。因此,它是 O(N)。您可以更精确地说 O(A),其中 A 是数组的长度a
。
练习 4
def is_prime(x: int) -> bool:
i = 2
while (i**2 <= x):
if x % i == 0:
return False
i += 1
return True
回答:我们来考虑一下例子is_prime(9)
。该函数将执行以下操作:
- 2 小于 9 和
9 % 2 != 0
。因此,我们将i
增加到 3。 - 3 是 9,所以这是最后一次检查。
9 % 3 == 0
,所以 9 是质数,函数返回True
。
那is_prime(11)
的案子呢?
- 2 小于 11 和
11 % 2 != 0
。所以我们将i
增加到 3。 - 3 小于 11 和
11 % 3 != 0
。所以我们将i
增加到 4。 - 4 大于 11。因此,11 是质数,函数返回
True
。
我们应该看到在最坏的情况下,当i
达到x
的平方根时,我们如何停止检查。因此,时间复杂度为 O(sqrt(N))。
摘要
大 O 符号是一个数学表达式,它描述了算法的效率随着其参数的增加而变化的速率。
它用来表达时间和空间的复杂性。
空间复杂度与一个功能需要分配的内存有关。时间复杂度与完成一项功能所需的时间有关。
我希望这篇短文能帮助你更好地理解大 O 符号的概念。虽然在最初的几个问题中它可能看起来模糊不清,但是随着你练习更多的问题,它会逐渐变得清晰。
干杯🍺!
来源:破解编码访谈——盖尔·拉克曼·麦克道尔
理解生物信息学——具有生物学领域知识的数据科学
看看什么是生物信息学和生物信息学家做什么
马库斯·斯皮斯克在 Unsplash 上的照片
生物信息学是一个很大的领域,对不同的人来说似乎意味着稍微不同的东西。因此,对于局外人来说,很难确切地知道这个领域到底是什么。在这篇文章中,我想对什么是生物信息学和生物信息学家做什么做一个广泛的概述。
在关于生物信息学的对话中很少提到的一个有趣的事实是,生物信息学和数据科学之间有很多相似之处。首先,这两个领域很难全面定义。数据科学家应该具备的核心技能包括数学、计算机科学和特定领域的知识。生物信息学家应该对数学、计算机科学和生物学驾轻就熟。考虑到这一点,生物信息学对我来说是数据科学的一个领域。生物信息学家经常进入传统的数据科学角色——本质上是将生物学领域的知识换成其他东西。
从根本上说,生物信息学就是使用强大的计算机来分析大量的生物数据。生物信息学越来越受欢迎,这与生物学研究最近所处的状态有关。我们现在正在生成如此大量的数据,以至于我们需要专业的工具和人员来理解这些数据。
为了很好地理解什么是生物信息学,它有助于了解我们所说的组学在生物学研究中的意义。
什么是组学
组学是指生物学中四个研究领域的集合。这些是基因组学、转录组学、蛋白质组学和代谢组学。这四个领域的总体目标是了解它们各自的生物分子。
这四个领域是生物信息学流通的大规模数据集的来源。我们来看看每个领域都关注什么,大数据从何而来。
基因组学
基因组学可能是大多数人在考虑生物信息学时想到的东西。基因组学研究所有生命形式的 DNA。DNA 本质上只是储存细胞运转所需的信息。这可能是所有组学中最容易研究的,因为 DNA 相对来说是不变的。
基因组学的数据集是由下一代测序技术生成的。简而言之,NGS 帮助我们解读了遗传密码。NGS 输出需要特殊工具来处理和操作的大数据集。
转录组学
转录组学是关于 RNA 分子的研究。你可以认为 RNA 是 DNA 的表亲。它与 DNA 有很大的不同,因此它需要自己的研究领域。RNA 实际上来源于 DNA——它是一种短得多的分子,在生命系统中做着令人困惑的大量工作。
转录组学中使用的技术与基因组学中使用的技术基本相同——下一代测序。还有其他研究转录组学的技术,但是它们不能为生物信息学产生足够大的数据集。
蛋白基因组学
蛋白质组学就是研究蛋白质。蛋白质是活细胞的主力——它们无处不在,做着许多不同的工作。这可能是所有四个组学中最难的,因为要研究的蛋白质种类繁多。蛋白质组学中生成的数据集可以来自质谱技术。
代谢组学
这个领域主要是研究活细胞中的小分子。与蛋白质组学一样,代谢组学数据将来自质谱和核磁共振技术。
流式细胞术
流式细胞术不是真正的组学,但它属于生物信息学范畴。流式细胞术涉及测量细胞群体的特性。
生物信息学家可以参与构建和/或使用两种资源;数据库和算法。我们将看看每一个的例子,主要集中在基因组学和生物信息学上。
数据库
生物信息学中的数据库是来自给定类型数据集的数据集合。基因组学中的一个例子是序列读取存档,它是下一代测序数据的最大储存库。NCBI的生物信息学家将负责维护这些巨大的资源供研究人员使用。生物信息学家也可以利用这里的数据进行研究。
算法
一旦获得数据,您需要专业工具来正确地分析它,并从中获得有意义的见解。这就是生物信息学算法的用武之地。同样,生物信息学家可以构建或使用这些工具,或者两者兼而有之!基因组分析工具包(GATK) 为 NGS 数据的分析建立了许多工具。他们为预处理数据,寻找 DNA 样本中的突变,以及评估突变的重要性等制定程序。
我已经提到了生物信息学基因组学领域的数据库和算法,但所有四个组学和流式细胞术都有它们的等价物。
结论
从根本上说,生物信息学家使用强大的计算机来处理大的生物数据。他们通过建立和/或使用算法和数据库来做到这一点。
它有助于了解生成生物信息学数据集的四个组学。这些是基因组学、转录组学、蛋白质组学和代谢组学。基因组学和转录组学通过高通量下一代测序技术为生物信息学生成数据集。蛋白质组学和代谢组学通过质谱技术产生数据集。
最后,生物信息学可以被认为是一门具有生物学领域知识的数据科学。像生物信息学这样一个广阔的领域,总是有可能遗漏一些东西。在这里,我试图给这个领域一个广泛的概述,涵盖其中的主要子领域。
通过实现来理解:k-最近邻
建立你自己的模型
了解 k-最近邻分类器的工作原理并在 Python 中实现它
1-最近邻分类器的判定区域。图片由作者提供。
A 第二天,另一个经典算法:k-最近邻居。像朴素贝叶斯分类器一样,这是解决分类问题的一个相当简单的方法。该算法非常直观,并且具有无与伦比的训练时间,这使得它成为您刚刚开始机器学习生涯时学习的绝佳候选。话虽如此,但做出预测是极其缓慢的,尤其是对于大型数据集。由于维数灾难,具有许多特征的数据集的性能可能也不是压倒性的。
在本文中,您将了解到
- k 最近邻分类器的工作原理
- 为什么它被设计成这样
- 为什么它有这些严重的缺点,当然,
- 如何用 NumPy 在 Python 中实现?
因为我们将以 scikit learn-conform 的方式实现分类器,所以也值得看看我的文章构建您自己的定制 scikit-learn 回归。然而,scikit-learn 的开销很小,无论如何您都应该能够跟上。
你可以在 我的 Github 上找到代码。
理论
这个分类器的主要思想非常简单。它直接来源于分类的基本问题:
给定一个数据点 x,x 属于某类 c 的概率是多少?
在数学的语言中,我们搜索条件概率p(c|x)。虽然朴素贝叶斯分类器试图使用一些假设直接模拟这种概率,但还有另一种直观的方法来计算这种概率——概率的频率主义观点。
对概率的天真看法
好吧,但这是什么意思呢?让我们考虑下面这个简单的例子:你掷出一个六面的、可能有作弊的骰子,想要计算掷出六的概率,即 p (掷骰数 6)。如何做到这一点?嗯,你掷骰子 n 次,写下它显示 6 的频率。如果你见过数字六 k 次,你说在 k / n 左右看到六的概率是**。这里没什么新奇的东西,对吧?**
现在,假设我们想计算一个条件概率,例如
p(掷 6 号|掷一个偶数)
你不需要贝叶斯定理来解决这个问题。再掷一次骰子,忽略所有奇数的骰子。这就是条件作用:过滤结果。如果你掷骰子 n 次次,已经看到 m 个偶数并且其中的 k 个都是一个六,那么上面的概率是在k/m左右而不是 k / n 。
激励 k-最近邻居
回到我们的问题。我们要计算p(c|x),其中 x 是包含特性的向量, c 是某个类。在这个例子中,我们
- 需要大量的数据点,
- 过滤掉具有特征 x 和
- 检查这些数据点属于类别 c 的频率。
相对频率是我们对概率p(c|x)的猜测。
你看到这里的问题了吗?
**通常,我们没有很多具有相同特征的数据点。**往往只有一个,也许两个。例如,假设一个数据集有两个特征:人的身高(厘米)和体重(千克)。标签为公或母。于是, x= ( x ₁, x ₂)其中 x ₁是身高, x ₂是体重, c 可以取值男性和女性。让我们来看看一些虚假数据:
图片由作者提供。
因为这两个特征是连续的,所以具有两个数据点的概率是可以忽略的,更不用说几百个数据点了。
**另一个问题:**如果我们要对一个我们从未见过的特征的数据点进行性别预测,比如(190.1,85.2),会发生什么?这就是预测的真正含义。这就是为什么这种天真的方法不起作用。相反,k-最近邻算法所做的如下:
它试图逼近概率 p ( c | x ),不是用具有精确特征 *x、*的数据点,而是用具有接近 x 特征的数据点。
**从某种意义上来说是不太严格的。**代替等待许多身高=182.4 和体重=92.6 的人,并检查他们的性别,k-最近邻居允许考虑接近具有这些特征的人。算法中的 k 是我们考虑的人数,它是一个超参数。
这些是我们或超参数优化算法(如网格搜索)必须选择的参数。它们不是由学习算法直接优化的。
图片由作者提供。
该算法
我们现在有了描述算法所需的一切。
培训:
- 以某种方式组织训练数据。在预测时间内,该顺序应该能够为我们提供任何给定数据点 x 的 k 个最近点。
- 已经这样了!😉
预测:
- 对于新的数据点 x ,在组织好的训练数据中找到 k 最近邻。
- 聚合这些 k 邻居的标签。
- 输出标签/概率。
目前还不能实现这个,因为有很多空白要填。
- 组织是什么意思?
- 我们如何衡量接近度?
- 如何聚合?
除了 k 的值,这些都是我们可以选择的,不同的决定给了我们不同的算法。让我们简单地回答如下问题:
- 组织=按原样保存训练数据集
- 邻近度=欧几里德距离
- 合计=平均
这需要一个例子。让我们再次用人员数据检查图片。
我们可以看到 k =5 个最接近黑色的数据点有 4 个男性标签和 1 个女性标签。因此,我们可以输出属于黑点的人实际上是 4/5=80%男性,1/5=20%女性。如果我们希望一个类作为输出,我们将返回 male。没问题!
现在,让我们实施它。
履行
最难的部分是找到一个点的最近邻居。
快速入门
让我们用一个小例子来说明如何在 Python 中做到这一点。我们从
import numpy as np
features = np.array([[1, 2], [3, 4], [1, 3], [0, 2]])
labels = np.array([0, 0, 1, 1])
new_point = np.array([1, 4])
图片由作者提供。
我们已经创建了一个由四个数据点和另一个点组成的小型数据集。哪些是最近的点?新点的标签应该是 0 还是 1?让我们来找出答案。输入
distances = ((features - new_point)**2).sum(axis=1)
给我们四个值 distance =[4,4,1,5],这是从new_point
到features
中所有其他点的平方欧几里德距离。太棒了。我们可以看到三号点是最近的,其次是一号点和二号点。第四点是最远的。
如何从数组[4,4,1,5]中提取最近的点?一个distances.argsort()
帮助。结果是[2,0,1,3],它告诉我们索引为 2 的数据点最小(输出点编号为 3),然后是索引为 0 的数据点,然后是索引为 1 的数据点,最后是索引为 3 的数据点最大。
注意argsort
将distances
中的第一个 4 放在第二个 4 之前。根据排序算法,也可以反过来,但是在这篇介绍性文章中,我们不讨论这些细节。
例如,如果我们想要三个最近的邻居,我们可以通过
distances.argsort()[:3]
并且标签对应于这些最近的点
labels[distances.argsort()[:3]]
我们得到[1,0,0],其中 1 是离(1,4)最近的点的标签,零是属于下两个最近点的标签。
这就是我们所需要的,让我们开始真正的交易。
最终代码
你应该对代码很熟悉。唯一的新功能是np.bincount
,它计算标签的出现次数。注意,我首先实现了一个predict_proba
方法来计算概率。方法predict
只是调用这个方法,并使用argmax
函数返回概率最高的索引(=类)。该类等待从 0 到 C -1 的类,其中 C 是类的数量。
**免责声明:**此代码未经优化,仅用于教育目的。
import numpy as np
from sklearn.base import BaseEstimator, ClassifierMixin
from sklearn.utils.validation import check_X_y, check_array, check_is_fitted
class KNNClassifier(BaseEstimator, ClassifierMixin):
def __init__(self, k=3):
self.k = k
def fit(self, X, y):
X, y = check_X_y(X, y)
self.X_ = np.copy(X)
self.y_ = np.copy(y)
self.n_classes_ = self.y_.max() + 1
return self
def predict_proba(self, X):
check_is_fitted(self)
X = check_array(X)
res = []
for x in X:
distances = ((self.X_ - x)**2).sum(axis=1)
smallest_distances = distances.argsort()[:self.k]
closest_labels = self.y_[smallest_distances]
count_labels = np.bincount(
closest_labels,
minlength=self.n_classes_
)
res.append(count_labels / count_labels.sum())
return np.array(res)
def predict(self, X):
check_is_fitted(self)
X = check_array(X)
res = self.predict_proba(X)
return res.argmax(axis=1)
就是这样!我们可以做一个小测试,看看它是否符合 scikit-learnk-最近邻分类器。
测试代码
让我们创建另一个小数据集进行测试。
from sklearn.datasets import make_blobs
import numpy as np
X, y = make_blobs(n_samples=20, centers=[(0,0), (5,5), (-5, 5)], random_state=0)
X = np.vstack([X, np.array([[2, 4], [-1, 4], [1, 6]])])
y = np.append(y, [2, 1, 0])
看起来是这样的:
图片由作者提供。
使用我们的分类器,k = 3
my_knn = KNNClassifier(k=3)
my_knn.fit(X, y)
my_knn.predict_proba([[0, 1], [0, 5], [3, 4]])
我们得到了
array([[1\. , 0\. , 0\. ],
[0.33333333, 0.33333333, 0.33333333],
[0\. , 0.66666667, 0.33333333]])
**阅读输出如下:**第一点是 100%属于类 1 第二点以 33%均等地位于每个类中,第三点是大约 67%的类 2 和 33%的类 3。
如果你想要具体的标签,尝试
my_knn.predict([[0, 1], [0, 5], [3, 4]])
它输出[0,0,1]。请注意,在平局的情况下,我们实现的模型输出较低的类,这就是点(0,5)被归类为属于类 0 的原因。
如果你检查图片,它是有意义的。但是让我们在 scikit-learn 的帮助下让自己安心吧。
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X, y)
my_knn.predict_proba([[0, 1], [0, 5], [3, 4]])
结果是:
array([[1\. , 0\. , 0\. ],
[0.33333333, 0.33333333, 0.33333333],
[0\. , 0.66666667, 0.33333333]])
唷!一切看起来都很好。让我们检查算法的决策边界,因为它很漂亮。
图片由作者提供。
再说一遍,上面的黑点不是 100%蓝色的。它是 33%的蓝色、红色和黄色,但是算法确定性地决定了最低等级,即蓝色。
我们还可以检查不同值的 k 的决策边界。
图片由作者提供。
注意蓝色区域最后变大了,因为这个类的这个优待。我们还可以看到,对于 k =1,边界是混乱的:模型是过度拟合。另一个极端是, k 与数据集的大小一样大,所有点都用于聚合步骤。因此,每个数据点得到相同的预测:多数类。在这种情况下,模型欠拟合。最佳位置介于两者之间,可以使用超参数优化技术找到。
在结束之前,我们先来看看这个算法有哪些问题。
k-最近邻的缺点
这些问题如下:
- 寻找最近的邻居需要很多时间,特别是对于我们幼稚的实现。如果我们想预测一个新数据点的类别,我们必须对照数据集中的每一个其他点来检查它,这是很慢的。使用高级数据结构组织数据有更好的方法,但问题仍然存在。
- 下面的问题 1:通常,您在更快、更强的计算机上训练模型,然后可以在较弱的计算机上部署模型。例如,想想深度学习。但是对于k-最近邻来说,训练时间比较容易,繁重的工作在预测时间内完成,这不是我们想要的。
- 如果最近的邻居一点都不近会怎么样?那它们就没有任何意义。在包含少量要素的数据集中,这种情况可能已经发生,但当要素数量增加时,遇到这种问题的几率会大大增加。这也是人们所说的维数灾难。在 Cassie Kozyrkov 的这篇文章中可以找到一个很好的形象化的例子。
尤其是因为问题 2,你不会太经常在野外看到k-最近邻分类器。这仍然是一个你应该知道的很好的算法,你也可以把它用于小数据集,这没什么错。但是,如果你有数百万个包含数千个特征的数据点,事情就变得棘手了。
结论
在本文中,我们讨论了k-最近邻分类器是如何工作的,以及为什么它的设计有意义。它试图估计数据点 x 属于类别 c 的概率,并尽可能使用最接近 x 的数据点。这是一种非常自然的方法,因此这种算法通常在机器学习课程的开始讲授。
注意,构建一个k-最近邻回归量也非常简单。不需要计算类的出现次数,只需对最近邻的标签进行平均即可获得预测。你可以自己实现它,这只是一个小小的改变!
然后,我们模仿 scikit-learn API,以一种简单的方式实现了它。这意味着您也可以在 scikit-learn 的管道和网格搜索中使用这个估计器。这是一个很大的好处,因为我们甚至有超参数 k ,你可以使用网格搜索,随机搜索,或贝叶斯优化。
然而,这种算法有一些严重的问题。它不适合大型数据集,也不能部署在较弱的机器上进行预测。加上对维数灾难的敏感性,这是一种理论上很好的算法,但只能用于较小的数据集。
我希望你今天学到了新的、有趣的、有用的东西。感谢阅读!
作为最后一点,如果你
- 想支持我多写点机器学习和
- 无论如何都要计划获得中等订阅量,
为什么不做 通过这个链接 ?这将对我帮助很大!😊
透明地说,给你的价格不变,但大约一半的订阅费直接归我。
非常感谢,如果你考虑支持我的话!
有问题就在LinkedIn上写我!