TowardsDataScience-博客中文翻译-2021-六十四-
TowardsDataScience 博客中文翻译 2021(六十四)
Python 中的元分析:PythonMeta、PyMARE 和 NiMARE 简介
Python 中三个主要元分析库的介绍

在 Unsplash 上由 Cameron Cress 拍照
M eta 分析是一种研究综合的定量方法,最初被开发用于评估医学研究的结果。在医学中,元分析经常用于得出关于医学实例对各种疾病的治疗效果及其副作用强度的结论。其背后的主要思想是,研究人员收集所有相关的研究,并使用元分析对其进行评估。每篇文章中的结果可能大不相同。因此,做一个荟萃分析对了解真实效果是什么非常有用。
1990 年,元分析开始扩展到经济学和其他社会科学,在那里它发现了许多用例。这里的一些例子包括评估自然资源对经济增长的影响,学费对大学入学率的影响,日光节约对能源消耗的影响等。元分析的复兴出现在 2010 年代,当时它成为了实证经济学的传统方法。

注:论文标题中带有“荟萃分析”或“元回归分析”的 EconLit 注册作品数量。资料来源:岩崎一郎(2020)。新兴市场和经济体的元分析:特刊介绍。新兴市场金融和贸易,56(1)。
统计元分析方法是在大多数标准软件包中实现的。r 包含了大量的库,提供了各种各样的估计、绘图和统计测试。他们精疲力尽的概述是这里。大多数经济学家使用 STATA,这是元分析中学术经济研究事实上的主流工具。本页展示了 STATA 为研究人员提供的可能性。
由于许多数据科学社区都使用 Python,所以我将介绍三个主要的专门用于执行元分析的 Python 库:PythonMeta、PyMARE 和 NiMARE。
PythonMeta
PythonMeta 提供了用于效果测量、异质性测试和绘图(森林图、漏斗图等)的基本模型。).它只包括广泛使用的方法,缺乏许多更高级的功能(如多变量元分析,这是 STATA 17 中的新功能)。在包站点上描述了 PythonMeta 中的所有元素。
我将展示一个森林图的实现,用于显示我的样本和一些主要测试中的研究结果。经过一些修改,您可以在其他数据集上使用该代码。
首先,让我们导入 PythonMeta。
接下来,该函数计算每个研究的效果、总体效果的权重、置信区间、所有研究的总体效果以及异质性检验。
该函数加载内置的 PythonMeta 类,从字典中获取数据、模型和算法设置,并绘制结果和森林图。
这是 Python 的输出:

调用主函数产生了这个森林图:

来源:使用样本数据的 PythonMeta。
皮马雷
这个库明确关注元回归。这是一种通过对一组虚拟变量进行回归来探索数据异质性的技术,这些虚拟变量捕捉了样本中研究的各种特征(发表年份、经验方法、区域焦点等)。)
我想避免代码和文档的复制和粘贴。GitHub 网站上对一切都有很好的解释,所以请在这里查看。
尼玛雷
NiMARE 是 Python 中最令人兴奋的元分析实现。这是一个由图书馆、方法和工具组成的生态系统,用于进行集中于神经影像文献的荟萃分析。要探索它们,请前往主站点。
例如,它包含:
- 超过 14,000 篇神经影像论文的数据库
- 神经影像学研究的坐标和地图库
- 为预测性荟萃分析构建的库和坐标数据库
- 图像数据库。
NiMARE 的一大局限是它专注于大脑和神经科学的元分析。研究人员和数据科学家应该意识到,它在这个领域之外的应用可能会导致一些问题。
结论
尽管 Python 目前还不是可以用于元分析中所有任务的主流程序,但是这些 Python 包可以很好地服务于较小的项目,并且非常容易实现。STATA 或 R 包含了更多开发的包,但是如果你很了解 Python,购买 STATA 的预算有限,或者 R 语法看起来太复杂,这三个库都适合你。
PS:你可以订阅我的 邮箱列表 每次我写新文章都会收到通知。如果你还不是中等会员,你可以在这里加入https://medium.com/@petrkorab/membership。
R 中的荟萃分析
分组分析和累积估计
这篇文章将是我关于 r 中的荟萃分析的介绍性文章的延伸。首先要澄清的是,分组分析在分析治疗效果时肯定有其应有的位置,但永远不应该被滥用。有太多的例子显示了 p-hacking 的危险,当亚组分析没有包括在研究方案中时(意味着采样的数据不打算在组之间划分),您(作为读者)应该非常小心。然而,如果你有一个坚实的(生物学的)理由来进行亚组分析,这一努力在 r。
为了进行和展示荟萃分析,只需要几个软件包。实际上,对于这个例子,我仅仅依赖于 metafor 包。
让我们加载数据,按照感兴趣的结果和结果类型进行划分。

我现在已经在 r 中加载了一个数据集,该数据集包含每个研究和每个治疗的总结结果。请记住,为了对连续结果进行荟萃分析,您需要——每次治疗——平均值、标准差和观察次数。
创建单独的数据框架,使分析更加容易。当然,您也可以使用 dplyr 或 R 内置管道将数据库连接到分析。

包含特定结果和级别的子集。在这里,那分别是 ADG 和高。
分析的最大部分实际上是进行分析。metafor包中需要使用的代码非常直观。下面,我要求进行一个随机效应荟萃分析,并根据 subADG &环境变量进行一个额外的亚组分析。 hakn=TRUE 语句请求 Hartung-Knapp 调整。
下图显示了每项研究治疗之间的平均差异和 95%置信区间。共纳入了 9 项研究,累计包含 233 项观察结果。随机效应模型没有显示出效应。人们可能会认为,这很容易通过查看 p 值来发现,但我希望你们忘记这些值。如果曾经有一个案例着眼于置信区间,而不是 p 值,你会在这个荟萃分析中找到它。这些置信区间包含的是零,意味着没有影响。
然后,看看 tau 和 I,这些是你的异质性指标。至少,任何亚组分析都应该减少这些指标,这是强信噪比的暗示。通过包含子群,你正在分割你的数据,这意味着有效样本量的减少,这通常意味着更多的差异。如果异质性降低,那么这是分割数据实际上增加了信号的迹象。我们看到这种情况确实在发生——从全球 40%的异质性,我们分别下降到 0%和 22%。因此,在这里,分组分析似乎是有意义的,但情况当然不会总是如此。
在那之下,还有另一个 p 值,但是再一次忘了它。看置信区间。

是时候创造那些美丽的森林了。
定制你的情节的许多可能性。

该图清楚地显示了组内和组间方差、组内和组间异质性水平以及总体汇总估计的有效性。不需要 p 值,只需要眼球。
如果你想使用荟萃分析的力量,你不能超越累积性荟萃分析。这一过程很简单——每次“添加”新研究时都会计算出治疗差异汇总,因此您可以跟踪治疗估计值和异质性水平的变化。这样,你可以看到是否进行了太多的研究,还需要进行多少研究,或者进行更多的研究是否会有所不同。还是那句话,不要看 p 值,要看治疗估计值和汇总置信区间的变化。
****
增加更多的研究不会缩小置信区间——你增加的噪音和信号一样多。
下面,您将看到上面的工作流,但现在应用于将按比例转换的事件数据。对于这个例子,我将关注死亡率。

结果衡量不再是平均差异,而是优势比。同样在这里,亚组分析删除了异质性,并清楚地展示了异质性的来源。
下面,你会看到我在元分析中比较两种不同的事件数据分析方法。
我想比较两个荟萃分析包——meta for和 meta 。

包之间的结果重叠。唯一的区别是缺少港币调整选项。
现在,我们似乎可以相信这个算法,让我们再次建立森林的情节。

森林图清楚地显示了每个结果是如何估计的。为了解释,通过公式(6/616) / (16/616)得到 0.37 的比值比(OR),即 0.0097 / 0.0259 = 0.37
****
和累积的荟萃分析结果。在本例中,添加更多研究等于添加更多信号,这意味着没有效果。
如您所见,进行亚组和累积元分析非常简单。将一项研究纳入荟萃分析需要做出哪些选择,以及在决定亚组分析时使用哪些标准,这些永远不会是简单明了的。
但是话说回来,研究从来都不是简单明了的。也不容易。这就是它的乐趣所在:-)!
元政策梯度:一个综述
理解大数据
自动化超参数调整和 RL 目标发现

大多数学习曲线趋于平稳。在初步吸收了统计规律之后,系统饱和了,我们达到了手工制作的学习规则和归纳偏差的极限。在最坏的情况下,我们开始过度适应。但是,如果学习系统能够批评自己的学习行为,那会怎么样呢?以一种完全自我参照的方式。学会学习…如何学习如何学习。内省和对先前学习经验的递归引导——这是智力的关键吗?如果这听起来对你来说很熟悉,你可能已经有幸听过的于尔根·施密德胡伯。
每一个真正自我参照的进化系统都应该加速它的进化。— 施密德胡伯(1987 年,第 45 页)
早在 90 年代,这些想法是有远见的——但非常不切实际。但是随着可扩展的自动分化工具箱的出现,我们正在向元-元-学习系统靠近。在这篇文章中,我们回顾了一组新颖的强化学习(RL)算法,这些算法允许我们自动化许多“手动”RL 设计工作。它们被称为元策略梯度(MPG ),可以通过高阶梯度调整 RL 管道中几乎所有可区分的成分。

迭代学习改进的元范式。灵感来自大卫·西尔弗的 ICML 2020 研讨会。
解开元算法& RL 代理➰
“元学习”或“学会学习”旨在通过将整个学习过程分解为两个阶段来自动发现学习算法:智能体“生命周期”的内环展开和经验的外环内省。这种一般的两阶段范式可以有许多不同的风格,这里仅列举几种:
- 在任务分配上训练具有内部存储器的基于 RNN 的代理(例如 R1; Hochreiter 等人,2001;段等,2016;王等,2016 。
- 在任务分配上训练外部记忆增强代理(例如,MANN桑托罗等人,2016;里特等人,2018;格雷夫斯等人,2016 年。
- 训练一个 RNN 来增强智能体内循环中基于梯度的优化更新(例如 Andrychowicz 等人,2016;梅斯等人,2019;梅斯等人,2020 年。
- 对代理的初始权重进行元优化,以便在几个梯度下降步骤(例如 MAML;芬恩等人,2017;尼科尔等人,2018; Flennerhag 等人,2019 。
但是大多数传统的元学习算法很难将元学习算法与代理本身分开。元学习算法很难解释,并且其元测试泛化能力有限。例如,在 RL 中,递归权重动态通过调整有效策略来编码学习算法。在 MAML 中,元学习初始化与代理的网络架构固有地纠缠在一起。另一方面,元策略梯度方法旨在通过优化元级别来为较低级别提供目标,从而最大化后续学习进度,从而克服这些限制。这可以从单独优化单个参数到学习黑盒神经网络参数化的内环目标。但是外环目标是什么?最常见的选择是强化学习问题本身,我们使用由强化估计器产生的伪梯度(威廉姆斯,1992 )。因此,命名为元策略梯度。接下来跟随徐等(2018) 介绍所需的数学背景。
背景:二阶政策梯度∇
让我们假设我们想要训练一个由θ参数化的代理(例如,一个策略/价值网络)。标准的学习循环(如图 1 所示)将重复执行 3 个步骤:
- 通过在代理的网络中执行正向传递来收集环境中的轨迹𝜏。
- 评估 RL 目标 L,例如均方 Bellman 误差或具有熵正则化、基线校正等的策略梯度目标。
- 计算梯度估计值以最小化损失,并使用更新函数 f(例如,您最喜欢的梯度下降变量)更新网络参数θ→θ’。

图 1:RL 中传统的一阶参数更新:收集、评估、更新。
现在让我们假设我们的更新函数明确地依赖于一组元参数η(例如折扣因子γ):

重要的是,假设更新函数相对于 η 是可微的。然后我们可以问以下问题:更新后的性能如何依赖于用于执行更新的元参数?给定代理的当前状态,我们应该如何为下一次更新选择η?为了回答这个问题,我们需要一个元目标来评价一个候选人η?一种方法是使用从更新后策略收集的另一个拒绝轨迹𝜏',由( Sutton,1992 )执行在线交叉验证。为简单起见,我们可以假设元目标 l(我们用它来评估𝜏'上的η)与内环目标 l 相同(例如,某个加强变量)。因此,我们想要估计元梯度:


图 2:基于在线交叉验证的元策略梯度更新。
使用链式法则,我们可以重写更新参数θ’相对于η的导数:

更新参数对η的依赖性归结为一个动力系统:

当我们展开网络时,可以在线累积这个加法序列的参数和相应的雅可比矩阵。这又为我们提供了执行外环梯度下降更新以改进超参数η的所有必要成分:

注意,我们只考虑了单次更新后元参数对性能的影响。考虑多个步骤在概念上很简单,但是数学要求我们通过时间反向传播来传播梯度。我们可以使用一组固定的 K 步骤和自动微分工具箱来进行梯度簿记。完整的元策略梯度过程可以归结为重复 3 个基本步骤(参见图 2):
- 使用更新函数 f 和 l 基于𝜏更新θ。将得到的梯度累积到轨迹{z,z ',…}中。
- 之后,使用元目标 l 交叉验证更新的代理在新的后续轨迹𝜏上的性能
- 使用 SGD 根据元策略梯度∂ L/∂η更新元参数η。
有多种计算上的考虑:首先,在线交叉验证并不意味着样品效率的降低。我们可以简单地在下一个过程迭代的步骤 1 中重用它,而不是在元评估后丢弃𝜏'。第二,更新 f 相对于元参数的导数计算起来很昂贵。这是因为我们必须通过(多步)内循环优化过程来执行“展开”优化。存在一组启发式解决方案,其导致半/伪元策略梯度更新:
- 使用 df/dθ的廉价近似值。这可以是例如对角化的版本。
- 使用衰减因子,该指数忘记了早期更新的重要性。
- 完全截断序列,只考虑η对几个步骤的影响。
最后,MPG 的记忆复杂度随着“元轨迹长度”K 的增加而增加。选择小的 K 会引入截断偏差,而较大的 K 会遭受高方差。此外,计算元策略梯度所需的 z 轨迹可能很难适应内存(取决于θ和 K 的维数)。现在我们已经介绍了正式的 MPG 框架,让我们看看如何利用它。
用于在线深度 RL 超参数调谐的 MPGs💨
RL 问题假设了一个人工代理,该代理最大化其期望的贴现累积报酬。这个奖励是从一个随机且未知的奖励函数中抽取的。因此,在监督学习的情况下,不存在简单和明确的可微分目标。相反,我们必须想出一个代理目标,如均方差贝尔曼误差或政策梯度目标。这些目标都有自己的超参数,这些超参数通常很难调整,并且可能需要动态调度。 MPG 方法承诺在学习过程的任何给定时间发现能够提供最佳可能代理的适应性目标。在下一节中,我们将重点关注 MPG 设置如何使用演员-评论家(AC)目标来改善控制问题。我们涵盖了标准交流设置(徐等人,2018 )以及 IMPALA 架构(等人,2020) 。
徐等(2018)——元梯度强化学习
Xu et al. (2018) 将 MPG 框架应用于整个基于价值的 RL 和政策梯度基线修正中使用的返回函数。元参数η = {γ,λ}控制 RL 中的偏差-方差权衡。我们在多大程度上依赖蒙特卡罗估计,在多大程度上依赖未来回报的自助估计?g 定义关键目标,基线修正强化项:

其中 g 重新调整了临界和熵损失项的有效学习速率。对于一组固定的超参数η,内环更新 f 归结为:

元策略梯度的下一个要素是更新操作相对于元参数η的导数:

最后,在线验证损失的导数 wrt。更新的参数由下式给出:

现在,我们可以将所有这些连接在一起,存储我们的轨迹并更新元参数γ,λ。太好了!但是还有一个基本的挑战:我们的内环函数逼近器正在追踪一个移动的目标。如果目标的性质随着每次元参数更新而改变,我们如何学习值?以下是一些解决方案:
- 调整学习率标度(外环/内环比率)或使用信赖域约束更新来确保优化过程本身在元参数中足够平滑。
- 徐等(2018) 提出不仅学习单个元参数集的值函数&策略,而且学习所有η!实际上,这意味着我们提供元参数的嵌入作为价值/政策网络的输入(如在 UVFA 中; Schaul 等人,2015 年。
在徐等人(2018) 的最初论文之后,这种方法被扩展到更多的超参数。以下是参考文献列表,将 mpg 应用于大量基本 RL 设计选择:

表 1:超参数调整的 MPG 方法概述。
萨哈维等人(2020)——一种自调整演员-评论家算法
虽然这些项目主要关注于调整小的超参数集,但最终我们希望自动调整整个 RL 管道。萨哈维等人(2020) 通过优化 IMPALA ( 埃斯佩霍尔特等人,2018 )架构的几乎所有超参数,向这个方向迈出了一大步。但这还不是全部:他们还调整了网络架构和目标,以最大限度地利用 mpg 的功能。但让我们先退后一步:IMPALA 是一个大规模的非策略演员-评论家算法,它使用分布式演员-学习者框架允许高数据吞吐量。许多工作人员异步收集轨迹,然后将轨迹发送给集中的学习者。这个学习者处理经验并执行梯度更新。有一个警告:轨迹很可能不是使用最近的策略收集的。因此,该算法并不完全符合策略,并且梯度(根据过时的策略展开估计)将变得“陈旧”。IMPALA 通过使用一种叫做 v-trace 的程序来校正这种协变量偏移。V-trace 通过修改的重要性采样(IS)比率来应对转换的非策略性质,该比率明确控制梯度估计量的方差和收缩速度:

其中ρ通过充当折扣比例来控制目标价值函数的性质。另一方面,c 执行一种形式的轨迹切割(à la Retrace, Munos 等人,2016 )。限幅阈值限制了重要性权重乘积的方差:

那么 mpg 在哪里发挥作用呢?STAC 算法(萨哈维等人,2020) 然后旨在通过利用 V-trace 参数的凸组合版本来学习校正程度:

然后,元目标可以相对于α进行微分,并且可以在目标策略 π 的近似贝尔曼迭代的固定点和行为策略之间进行平滑插值。较低的α_c 使 IS 的权重更大,这反过来意味着收缩更大,但方差也更大。另一方面,大的α_c 强调 v-trace,这导致较小的收缩,但也导致较低的方差。为了稳定外环非平稳性的影响,萨哈维等人(2020 )修改了元目标并添加了一个类似于 TRPO 的 KL 正则化子:

为了确保内部和外部损耗幅度之间的适当缩放,作者建议对损耗系数进行 sigmoid-squash 和缩放。

萨哈维等人(2020) 的第二个创新是在共享躯干的顶部添加了一组辅助输出头,对应不同的策略和批评家参数化,它们有自己的元参数。然后,元控制器可以针对潜在的不同时间尺度来控制进入躯干的梯度流。只有第一个头部被用于生成训练轨迹,而其他头部充当隐式正则化器。该论文表明,在 ATARI 上,MPG 改进增加,我们用 MPG 设置优化的参数越多(图 3 中的面板 B)。

图 3: STACX ATARI 关键结果。来源:改编自 萨哈维等(2020)
根据经验,外环中的高折扣和价值损失系数似乎表现更好(图 3 中的面板 A)。最后,当检查詹姆斯·邦德雅达利游戏的在线优化元参数时间表时,我们可以看到三个头部之间的显著变化和训练过程中的强非线性动力学(图 3 中的面板 C)。
用于离线发现目标的 mpg📖
RL 问题本质上是被错误指定的。代理人的任务是最大化折扣奖励的预期金额,而无需访问基础奖励功能。为了克服这个基本问题,我们定义了代理目标。但是可以肯定的是,对于我们人类来说,目标函数在我们的一生中是变化的。潜在地使学习变得更容易。有没有可能完全抛弃理查德·贝尔曼的巧妙遗产,并学会预测什么来最好地解决 RL 问题?以下部分中介绍的思想扩展了 MPGs,不仅优化了特定的超参数,还对整个参数化代理目标函数进行了元优化,其目的是为 RL 代理提供最佳的学习课程。第一组方法是离线并且跨大量训练内循环和不同 MDP 进行元学习。元学习参数化的损失函数 L_φ的优点在于,当改变任务分布时,可能更加鲁棒。优化问题的隐含特征允许超越元训练分布的一般化,并解决了核心的激励问题:从学习代理中解开学习的学习算法。
Houthooft 等人(2018 年)——演变的政策梯度(EPG)
计算高阶梯度是昂贵的,并且截断展开的优化具有在梯度估计中引入偏差的不期望的效果。为了克服这一技术困难, Houthooft 等人(2018) 提出使用进化策略,利用元损失参数化群体进行梯度估计。(潜在不可微的)函数 L 的梯度使用基于总体的蒙特卡罗估计器来近似,例如通过对偶采样:

其中ϵ是多元高斯变量。p 表示估计的群体大小,σ控制扰动方差,β有效地重新调整学习速率。可以并行收集单个功能/网络/代理评估。 Houthooft 等人(2018) 引入了元损失的参数化,它使用了代理人先前经验的时间卷积。它考虑了最近的历史(见图 4)。扩展的记忆有助于激励长时间范围内问题的结构化探索。

图 4: EPG 的建筑。来源:改编自 Houthooft 等人(2018)
此外,额外的输入可以允许 los 执行环境/任务识别,并将其目标追踪到特定的应用。此外,元丢失被输入一个 1 的向量,它可以作为一个显式的存储单元。在内环优化过程中,代理使用元损失,通过标准 SGD 来优化它们的策略网络参数。内环的总损耗由参数化损耗和标准 PPO 损耗的凸组合给出。一组 MuJoCo 实验揭示了 EPG 优于普通 PPO 目标,并且所得梯度相关但不同(相关性约为 0.5)。有趣的是,作者发现 EPG 损失学会了自适应梯度重标度,这让人想起 TRPO 和 PPO 中明确实施的政策更新平滑正则化思想(见图 4)。人们必须注意到,这是以元损失优化的进化外环的巨大成本为代价的。最后,作者表明,可以将该方法与学习的策略初始化(如 MAML)相结合,并且这种损失确实推广到更长的训练范围、不同的架构以及新的任务。
离线学习损失的一般思想后来由 Chebotar 等人(2019) 扩展,他们使用基于梯度的元优化,并根据额外的任务和上下文信息明确限定元损失。有趣的是,作者表明,预处理有助于塑造损失景观,以提高内环优化的平滑度。
Kirsch 等人(2020)——使用学习目标提高元强化学习的泛化能力
虽然之前的工作主要考虑了基于策略的 RL 代理, Kirsch 等人(2020) 将元策略梯度扩展到非策略设置,这允许利用重放缓冲区。因为在“浅”非元 RL 中,这可以提高采样效率,因为多次重复使用过渡来构建梯度。从概念上讲,提议的 MetaGenRL 框架建立在非策略连续控制算法 DDPG ( Lillicrap 等人,2015 )及其扩展 TD3( Fujimoto 等人,2018 )的基础上:
“我们的关键见解是,可微分的评论家 Q_ θ : S x A → R 可用于衡量基于相应政策梯度的质量局部改变目标函数参数 α 的效果。”— 基尔希等人(2020 年,第 3 页)
那么这在算法层面上意味着什么呢?标准 DDPG 在最小化评论家 Q_θ的均方 Bellman 误差和使用价值函数 wrt 的确定性梯度的策略改进之间交替。政策参数。在 MetaGenRL 中,添加了一个额外的转换中间层:

图 5: MetaGenRL 图解。来源:改编自 基尔希等人(2020)
批评家现在的任务是改进目标函数 L_α,然后用它来构建政策梯度。实际上,L 由一个小 LSTM 参数化,它以相反的顺序处理轨迹数据。这允许学习目标基于一集内的未来经历来评估特定的过渡,并模仿伪引导的形式。作者以 MuJoCo 和传统元学习(如 RL 和 EPG)为基准。他们发现 MetaGenRL 不仅样本效率更高,还允许推广到全新的环境。此外,他们执行了一组消融研究,表明元目标即使在没有时间戳输入的情况下也能很好地执行,但需要一个值估计输入来诱导学习进度。
Oh 等人(2020)——发现强化学习算法
习得性政策梯度 (LPG,Oh 等人,2020) 在几个方面扩展了 MetaGenRL。他们不依赖于批评家的概念,而是通过学习他们自己的表征来克服手工制作的语义,这些表征对于在一组代表性的马尔可夫决策过程中进行预测可能是重要的。它们不对代理的向量值预测施加任何意义。相反,元学习者决定必须预测什么,从而发现自己的更新规则。

图 6:习得性政策梯度示意图。来源: Oh 等(2020)
LPG 框架建议使用递归神经网络来发出内部循环优化目标(参见图 6)。 π -hat 表示政策调整目标,而 y-hat 表示与特定状态相关的分类分布的输出。同样,RNN 以相反的顺序处理一个情节,并且通过对由先前的目标参数化产生的内环梯度下降更新序列执行梯度下降来训练。但与 MetaGenRL 不同,它不依赖于值函数的概念来进行引导。 y-hat 可以自由学习自己的语义。LPG 目标然后可以用于基于交叉熵和 KL 散度项的组合来构建内环更新:

内环中的 KL 损失项让人想起诸如 C51 ( Bellemare 等人,2017 )的分布式 RL 中的思想,并且允许比期望值更详细的学习信号/信念估计。外环损耗类似于前面介绍的目标,但也包括一组额外的正则项,旨在确保整个优化过程 wrt 的稳定性。敬η。在图 7 面板 A 中,我们可以看到不同的维度如何对不同的价值相关估计进行编码,类似于经典的价值函数自举。

图 7:习得性政策梯度关键结果。来源:改编自 Oh 等(2020)
给我留下深刻印象的是图 7 面板 C 中描绘的结果:作者表明在一组玩具 MDP(grid world 和 delayed chain MDPs)上学习提供目标的 RNN 是可能的,它几乎能够推广到 ATARI 以及带有奖励剪辑和所有额外调料的手动调整的 DQN 目标。由于 MDP 的底层元训练分布编码了一组基本 RL 问题(例如,长期信用分配和传播状态知识的需要),LPG 能够进行大量的分布外推广。此外,向元训练集添加更多类型的环境提高了 lpg 的测试性能。
用于在线发现目标的 mpg🔛
上一节回顾的论文涉及离线发现目标或参数表。但是,我们能不能让 mpg 发现潜在的甚至更好的支柱来在线和在单次优化运行期间解决 RL 问题?
徐等(2020)——在线发现目标的元梯度强化学习
徐等(2020) 提出 FRODO 算法,与珠宝、妖精无关,代表网上发现的柔性强化目标。弗罗多的特别之处在于,天真的外在迷失和复杂的内在迷失之间的相互作用是在单个任务和单个代理人的一生中在线训练的。它不需要任何任务分配或重置环境的能力。目标只是随着主体经历世界而适应。有点像真正的终身学习!在离线情况下,η不再是低维的,即一个单一的超参数,而是一个参数化整个目标回波的神经网络 G(𝜏) = g_η(𝜏).

内环目标值不必编码至关重要的贝尔曼一致性,即当前状态值可以分解成奖励和折扣的下一个状态值。相反,这可能(也可能不会)被外部循环发现。你可能会想象,从非静态学习动力学的角度来看,在线培训整个网络可能更加棘手。你是对的。为了克服这一点( Xu et al .,2020 )提出在元级别上添加预测一致性正则化器:


图 8:佛罗多关键雅达利结果。来源:改编自 徐等(2020)
作者在 ATARI 基准测试中验证了 FRODO,图 8 的面板 A 清楚地表明,FRODO 诱导的学习进度在整个学习过程中都有所提高(正如本文开头的漫画草图所示)。此外,消融研究为稠度损失的重要性提供了证据(图 8 的 B 图)。最后,C 图显示了不同 ATARI 游戏以及训练过程中学习目标的变化。对于一些游戏,FRODO 学习提供接近 v-trace 的目标回报(例如,对于像 Pong 和 Seaquest 这样的简单游戏),而对于其他更具挑战性的游戏,学习的回报目标可能会有很大差异。
开放研究问题?
在这篇文章中,我们回顾了一组算法,这些算法使用高阶梯度来调整 RL 管道的大部分。这不仅实现了非常重要的训练计划,而且让系统决定在过程的不同阶段学习什么是重要的。这种层次化的优化范式能推进多远?以下是一组我个人的开放式问题:
- 我们如何改变架构和一般 RL 设置以达到“最佳可调”?像 STACX 那样增加额外的输出头只是一种方式,但可能还有其他方式。什么样的环境集合包含了所有相关学习问题的大部分方面?换句话说——什么是通用元训练分布?
- Schmidhuber 的自我参照学习系统的愿景需要内省的概念,这超越了简单的基于梯度的学习。我们如何推动当前的元学习设置更接近理论上的最佳 AIXI 模型?
- 里奇·萨顿(Rich Sutton)写了一篇观点鲜明的博客‘痛苦的教训’,认为机器学习领域最近的大部分突破仅仅是基于计算资源的急剧增加。MPG 属于同一个伞下,增加了研究的障碍。但是,我们如何进一步降低计算要求并提高采样效率呢?
元类
Python 的心脏

凯文·Ku 的图片来自 Pexels
Python 是一种面向对象的编程语言。事实上,人们常说Python 中的一切都是对象。
所以为了完全理解 Python,你真的需要理解像对象和类这样的底层结构,以便能够理解真正发生了什么。
这篇文章是关于 Python 中元编程的概念,我们将看到如何操作类以及它们的实例化。
元编程可以被宽泛地定义为操纵代码的代码。
一个对象当然是一个类的实例化,作为好奇的人,当我们听到 Python 中的一切都是对象时,一个自然的问题会在我们的脑海中浮现(对吗?),也就是说,类也是对象吗?如果是,是什么实例化了它们?
让我们找出答案。
类别和对象
我们首先需要讨论的是对象和类之间的关系。
假设我们有下面的类
我们创建一个空类,不包含任何属性和方法。然后我们在对象 kasper,中实例化它。最后,我们通过类型函数询问它是哪个类的实例化。
该代码将返回
<class '__main__.Person'>
表明实例化对象 kasper 的类是 Person 并且它是在您请求的 kasper 类型的同一个模块中定义的。
Python 中的一切都是对象,所以我们可以对内置类型做同样的事情,比如
它会打印出来
<class 'str'>
所以字符串“Medium”是由类 str 实例化的对象。
元类
好的,那么类的类型是什么呢?好吧,让我们试试。
输出是
<class 'type'>
<class 'type'>
<class 'type'>
有意思。看起来类的类型是类型。
这是因为类型就是所谓的元类。元类实例化类就像类实例化对象一样。
这些都很好,但是你如何使用它们呢?
好吧,既然元类在某些步骤中创建了一个类,如果我们可以进入并操纵那个过程,从而创建我们自己的自定义元类,它将实例化不同于类型的类,这可能是有用的。
这种通过在执行前操纵代码来改变行为的概念在 decorators 中是众所周知的。
Python 中的 Decorators 使我们能够在调用函数之前操纵它们,事实上,正如我们马上会看到的,这两者有很多共同点。
类实例化
当我们谈论元类时,首先要理解的是 Python 中的类是如何由默认元类类型构建的。
当实例化一个类时,Python 获取一些变量,如类的名称和该类从其继承的基类,并通过调用名为 prepare 的类型上的 dunder 方法基本上形成一个字典。
然后执行类的主体,以便用属性和方法填充字典。
最后一步是将名称、基类和上面的字典作为参数提供给类类型。它看起来像下面这样:
Person = type('Person', (Base1, Base2), cls_dict)
对象 Person 现在是一个从其父类 Base1 和 Base2 继承的类,并且具有由 cls_dict 指定的属性和方法。
如果我们要重新创建名为 Person 的空类,那么我们可以像这样动态地做
Person = type('Person', (), {})
这相当于上面的定义,我们使用关键字传递来创建一个空类。
我们如何创建自定义元类?
考虑下面的例子:
这里我们创建了一个名为 custom 的自定义元类,在这里我们覆盖了 type 的 dunder 方法 new 。
在 new 内部,我们从传递给 new 方法的字典中收集方法,以便对它们进行计数,并检查在类中是否最多有两个方法。如果没有,我们甚至会在类被实例化之前抛出一个错误。
方法 new 在被调用时返回一个类,我们有机会在实例化它之前操作该类的参数。
完成后,我们简单地调用 type 的 new 方法来获取类并返回它。
考虑一下这个:
这将会回来
WOOOF!
没问题。但是如果我们尝试运行以下代码:
它将崩溃,并显示以下消息
TypeError: More than two methods
元类和装饰器
假设我们有一堆类都继承自同一个基类,我们想对所有这些类的所有方法进行一些自动调试。
我们还希望遵循干原则(不要重复自己),避免代码重复。这个问题的最佳解决方案是什么?
显然,我们想到了 decorator,事实上 decorator 的概念是一个很好的调试工具和一个巨大的干式优化器,在 decorator 中,我们可以在函数(或类)执行之前和之后做各种事情。
让我们创建一个定制的调试器,它可以告诉我们一个类中有多少参数被传递给方法。
这当然只是一个玩具示例,但它将在本文中发挥作用,人们可以想象我们将编写一个日志装饰器,它将写入日志或其他一些实际有用的装饰器。
这段代码的输出是
arguments has 3 arguments
16
相当不错。包装函数中的 print 语句在调用函数参数之前执行。
注意,上面的@语法只是语法糖。幕后真正发生的是,当调用函数时,您将函数作为参数传递给装饰器。
如果我们希望在类中的每个方法上都有这个装饰器,一种方法当然是一个接一个地装饰所有的方法。然而,作为懒惰的程序员,我们讨厌重复自己,所以有一个小技巧。
考虑下面的自定义类装饰器:
现在,我们可以定义一个类,并在上面放置装饰器,以便在一次清扫中调试所有的方法(除了类方法和静态方法)。
输出是
walk has 1 arguments
miaaw has 1 arguments
这些都很好,但是回想一下上面的问题陈述,我们有很多类继承自同一个基类。
我们能不能以某种方式使用这种继承来保存所有需要放置装饰器的代码行?
嗯,是的。我们可以使用自定义元类。
假设我们所有类继承的公共基类叫做 Base 。然后我们可以创建下面的元类,然后基类可以继承它。
我们所有从基类继承的类现在都将调试由 debug_function 装饰器指定的所有方法。
什么时候应该使用元类?
实践中很少使用元类,因为对于大多数问题,有很多其他的解决方案可以使用元类。例如, decorators 给了我们一个强大的工具,在大多数情况下这就足够了。
然而,我认为了解 Python 的内部工作方式是很重要的,这样你才能真正理解其内部发生了什么,并知道何时使用合适的工具来完成工作。
现在您已经了解了元类以及何时使用它们是有意义的。如果你有很多类都有一个共同的基类,那么元类将确保通过继承一直向下渗透到祖先树中。
经验法则是这样的:如果你有一个场景需要改变类的实例化方式,那么使用元类是有意义的。
decorator 不会给你这样的东西,因为类实际上是在你用 decorator 做任何事情之前被实例化的。
我希望这篇文章对你有用。
一如既往,如果您有任何问题、评论或担忧,请随时联系 LinkedIn。
https://www.linkedin.com/in/kasper-müller-96ba95169/
MetaClean 自动化最高质量评估
利用机器学习来检测低质量的集成,节省手动评估峰值的时间

来源:作者
即使是最好的代谢组学管道也有一定程度的差异,这可能导致样品之间的峰整合不佳。这降低了您准确定量代谢物的能力,通常意味着您必须手动检查每个(感兴趣的)峰的质量。
Kelsey Chetnik、Lauren Petrick 和 Gaurav Pandey 开发了一个新的框架和 R 包,称为 MetaClean。这结合了 11 个峰质量指标和 8 个机器学习算法,以自动检测集成较差的峰。
我们采访了 Petrick 和 Pandey 关于代谢组学的最新论文,并了解了 MetaClean 的工作原理以及如何将其应用于您的数据。
当前的自动峰值积分是不可靠的
液相色谱-质谱联用(LC-MS)是代谢组学工作流程中鉴定和定量化合物的标准工具。在 LC-MS 中,相同化合物(代谢物)的保留时间和峰形会因测量而略有不同。轻微的波动是正常的,但是自动积分仍然会错误地表征峰,并使样品间的比较复杂化。
在许多情况下,通过标准后处理工具积分的高达 30% 的峰可能无法通过人工检查。低质量的峰可能是共洗脱分析物、错误校准或背景噪音的产物。因此,您不能期望任何两个样品具有完全相同的峰,即使它们的组成和浓度相匹配。但是为了比较浓度,您仍然需要对不同样品的峰进行精确积分。

LC-MS 常见的三个可能的集成问题示例。来源:作者
评估峰积分的一种方法是手动查看并判断峰形和包含区域。这不仅极其耗时;当你分析每批数千个峰时,这几乎是不可能的。因此,手动 QC 通常只针对一小部分数据,而且通常只针对感兴趣的峰。虽然这可能会从您的分析中消除假阳性,但它不能解决假阴性,并且集成不良的峰可能会漏掉。
机器学习可以自动进行峰值评估
个体质量度量可以量化个体峰值质量。然而,这些度量本身通常不足以完全区分低质量和高质量的集成。
机器学习模型可以找到复杂而有价值的指标组合,有助于这种区分。您所需要的只是描述峰值积分是高质量还是低质量(您的目标变量)的指标(您的特征)和标签。
有了这些特征和目标变量,您可以训练模型并评估它们对未知示例的峰值积分的预测程度。如果您发现模型非常准确,您可以轻松地将这些模型应用于数据集中的每个峰(包含数千个峰)。然后,您可以在分析数据之前过滤掉低质量的峰。
MetaClean 特性:11 个峰值质量指标
Chetnik,Petrick 和 Pandey 探索了质量度量的组合是否可以更好地作为峰值质量分类器的有效特征集。该团队创建了 MetaClean 来生成这些分类器,并针对几个数据集评估它们的性能。
该团队使用了来自张等人(度量)的四个度量和来自 Eshghi 等人 (M7 度量)的七个度量,以及由所有度量组成的组合 M11 集合。这些指标用于量化样品之间的总体峰形和保留时间一致性。

来源:作者
元清理训练数据:500 个人工审查的峰值
该团队对 89 份血浆样本中的 500 个预处理过的峰进行了手工分类:几乎 40%没有通过手工目测。他们确保训练数据中有各种不同的峰形,因此模型将在一组有代表性的峰上进行训练。

来源:作者
元清洁模型:八种最大似然分类算法中的佼佼者
下一步是找到对峰值质量进行最佳分类的机器学习算法。MetaClean 评估了八种最常用的算法,每种算法都与 M4、M7 和 M11 指标配对,以开发 24 种不同的峰值质量分类器。每个分类器的性能通过五重交叉验证进行评估,重复十次,并对结果进行平均。为此,MetaClean 评估了:
- 决策树;
- 逻辑回归;
- 朴素贝叶斯;
- 神经网络;
- 线性核的 SVM;
- AdaBoost
- 模型平均神经网络;
- 随机森林。

MetaClean 使用五重交叉验证评估了 24 个不同的潜在峰值分类器,这些分类器是从三个峰值质量度量集和八个机器学习算法的组合中构建的。来源:作者
在比较了每个模型的性能后,该团队观察到使用 M11 度量集的 AdaBoost 算法性能最佳。他们称这个模型为“全局峰值质量分类器”,它在开发数据集上的分类达到了几乎 85%的准确率。
然而,仅仅在同一批样本上测试该模型并不能提供该模型在真实情况下表现如何的准确描述。有效的模型还需要在跨批次和跨平台方面表现良好:
- 跨批次:来自不同批次的其他样品,在同一台机器上以相同的设置进行测量。
- 跨平台:在不同机器上测量样品,设置不同。
因此,研究小组评估了另外四个数据集的 500 个峰值进行测试。他们对 Global Peak Quality 分类器进行了评估:
- 来自相同仪器的不同数据集,用于测试相同平台的性能;
- 来自不同平台的三个数据集,用于测试跨平台性能。
MetaClean 在分析来自同一平台的数据时表现出色
全局峰质量分类器评估了来自同一仪器的不同数据集(测试 1)。该分类器在几乎 81%的情况下准确地对峰进行了分类,即使该数据集来自不同的样本类型。
MetaClean 在跨平台分析中表现相当好
该团队将 Global Peak Quality 分类器应用于四个可公开访问的数据集,每个数据集使用不同的 MS 仪器、LC 色谱柱和/或电离模式:

来源:作者
即使有这些平台和样品基质的可变性,分类器也能准确地对 65-80%的峰进行分类。然而,这些数据集的性能下降表明,跨平台分析仍然非常具有挑战性。

全局峰值质量分类器对所有测试集(包括来自不同平台和/或样本矩阵的测试集)实现了超过 65%的准确率。来源:作者
基于每个平台培训元清理
该团队使用来自不同于生成开发集的实验室的两个数据集(测试 3 和测试 4)作为独立案例,根据他们的数据重新训练和评估模型。他们用来自同一个平台的正面和负面模式数据训练了两个新模型,相互评估。他们表明,针对另一个数据集使用每个模型会产生很好的跨模式结果。每个模型都可以以特定于平台的方式进行优化,甚至可以应用于不同的电离模式。

当在每个平台的基础上训练时,在正模式数据上训练的模型相对于负模式数据表现得非常好,反之亦然。来源:作者
MetaClean 与 RSD 过滤法的比较
随机分析误差可能难以解决,尤其是在复杂样品中。通常,它们是背景噪音或前一次运行的样品残留的产物,与当前问题无关。这些峰值通常变化很大,它们的积分也不一致。
发现这些误差的一种方法是通过相对标准偏差(RSD)过滤,其中混合样本被分析多次。丢弃随机峰,并评估共有峰的一致性。当峰面积的标准偏差超出临界值时,它们将被移除。
由于 MetaClean 的运行独立于 RSD 过滤,研究小组认为首先对数据进行 RSD 过滤可以达到双重目的:
- 将 MetaClean 的性能与已建立的方法进行比较;
- 通过结合 RSD 过滤提高 MetaClean 的性能。
该小组发现,MetaClean 在所有测试案例中的表现都优于 RSD 过滤法,比这种广泛使用的方法更能准确地对峰的质量进行分类。
虽然 RSD 在确保随机分析误差最小化方面做得很好,但 MetaClean 专注于消除可能通过 RSD 过滤的整合问题。由于 RSD 和 MetaClean 是数据质量过滤的互补方法,它们结合使用非常有效。
对您的数据使用元清理
在您的数据上训练一个元清理模型对您的工作流可能是有价值的。只需很少的前期时间投入,您就可以在实验室中快速分析所有运行的所有峰,并大大降低错误积分率,而无需花费数小时进行手动峰审查。
Kelsey、Lauren 和 Gaurav 提供了一个 open R 包,它根据八个 ML 和三个质量度量集来评估您的数据,以开发一个模型。您可以立即下载软件包并训练您的模型来评估您的 LC-MS 集成。
请务必阅读发表在代谢组学上的 MetaClean 文章,以了解关于该平台的更多信息。
金属网和浓缩咖啡
咖啡数据科学
使用顶部金属网检查
在过去的几年里,有一个想法是在浓缩咖啡过滤篮的顶部使用一个纸过滤器来提高流量。我没有发现一个改进,但这可能是由于顶部纸过滤器使杠杆机器更糟糕的通道。不过金属网滤网出来的时候我还在关注。
几年前,受纸质过滤器的启发,金属网过滤器上市了。据我所知,他们是从杠杆机器 La Pavoni 开始的。


所有图片由作者提供
大多数杠杆机器没有三通阀来释放注射后的压力。因此,在拍摄结束时,冰球中的水和咖啡可以通过淋浴屏幕向上流动。对我来说,这导致了一年几次拔出活塞进行更深入的清洁。金属网过滤器解决了这个问题,它迫使水在拍摄过程中更均匀地流出。
当 Flair 推出 Flair 58 时,他们为他们的新机器出售了这个屏幕,但他们也单独提供它。我有一个天赋签名,我喜欢控制淋浴屏幕相对于冰球的位置。这些金属筛网是由 BPlus 制造的。
我想知道这如何影响拍摄,所以我做了一些顶部有和没有金属过滤器的配对拍摄。这个实验中一个令人困惑的变量是金属网很厚,它会对顶部空间(冰球顶部和淋浴屏幕之间的空间)产生影响。我不确定它的影响有多大,但保持顶部空间不变将会改变剂量或夯实。所以我觉得不容易分开。
对于照片,我正在拉两双,这是复杂的分层拍摄。然而,它们具有非常高的提取率。如果对拍摄有任何影响,我会看到的。我还在底部和中间使用了布过滤器。
镜头性能指标
最终得分是 7 个指标(强烈、浓郁、糖浆、甜味、酸味、苦味和余味)记分卡的平均值。当然,这些分数是主观的,但它们符合我的口味,帮助我提高了我的拍摄水平。分数有一些变化。我的目标是保持每个指标的一致性,但有时粒度很难,会影响最终得分。
使用折射仪测量总溶解固体(TDS ),该数字用于确定提取到杯中的咖啡的百分比,并结合一杯咖啡的输出重量和咖啡的输入重量,称为提取率(EY)。
数据分析
首先,我们来看口味或者最终评分。我在散点图上比较了它们,如果它们落在 y=x 线上,那么就没有区别。有些结果似乎是异常值。我也用烤的方法把它们分开。


对于提取率(EY),这一点更加不清楚。我不认为这些数据中的任何一个显示一个比另一个更好。


我也比较了 TDS 和 EY,但模式非常一致。

冰球后分析
一个有趣的现象是投篮后的冰球。过去几个月我一直在测量后期拍摄。因为我在圆盘中间使用了一个布过滤器,所以顶部和底部很容易分开。所以我在拍摄后的几分钟内称量了每一个。
从这个测量值和我的 EY 测量值,我可以估计每一半的 EY 贡献,并做出底部/顶部重量的比率或 EY 估计值。似乎有了金属网,冰球内部的工作方式发生了变化。

时间分析
以防万一,我检查了拍摄的时间。TCF 是时候覆盖过滤器,我发现它是一个非常有价值的衡量标准,以了解拍摄质量。然后是总时间。两者都没有显示出有趣的模式,但是我提供它只是为了以防有人想看。

T-Test!
我对这些数据使用了双尾配对 T 检验,唯一具有统计显著性的差异(T 检验 p 值< 0.05)是拍摄后的底部/顶部比率。

从我看到的数据来看,冰球顶部的金属网滤网并不影响投篮的表现,无论是正面还是负面。然而,过滤器使清理更容易,减少了机器的维护,所以在我看来这是一个胜利。
如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。也可以关注我中。
我的进一步阅读:
方法是你所需要的:数据科学中要避免的 7 个错误
行业笔记
来自我们经验的精选收藏

曾几何时,数据科学只对少数大型科技公司有价值。那些日子已经过去了。数据科学正在彻底改变许多“传统”行业:从汽车到金融,从房地产到能源。
普华永道的研究估计,到 2030 年,人工智能将为全球 GDP 贡献超过 15.7 万亿美元——作为参考,2018 年欧元区的 GDP 价值 16 万亿美元[1]。
现在,所有企业都将其数据视为资产,并将获得的洞察力视为竞争优势。
然而,超过 80%的数据科学项目失败了[2]。
为什么?
每个失败的项目都有其独特的原因,但是,根据三年的经验,我们注意到了一些模式。所以,这些就是:你在下一个数据科学项目中应该避免的七个错误。
1。假装数据科学不是软件开发
model.ipynb , model_new.ipynb , model_final.ipynb …我们多少次看到笔记本和脚本不受控制的泛滥?几个星期后,你就不可能记得它们包含了什么,也不可能记得它们最初是为什么被创造出来的。混乱导致重复,重复导致错误,错误导致缓慢。缓慢的实验导致糟糕的结果。
通常,数据科学家没有软件工程背景:尽管如此,这也不是接受代码质量妥协的借口。
生产代码的质量是没有商量余地的。
不要搞错:我们正在谈论生产代码。为了快速了解实验的真相,走捷径是可以的。实际上,这是一个很好的实践:快速自由的实验是找到越来越好的解决方案的关键。然而,实验和生产是两个不同的世界:正如我们在上一篇文章中所讨论的,一旦一个解决方案开始工作,它就应该从笔记本中拿出来,进行重构,设计成适当的软件模块,进行自动化测试,并集成到生产代码库中。
2。假装数据科学只是软件开发
不,这里没有滥用复制粘贴——这一点与前一点相反。数据科学不是软件工程的子领域:它是一个复杂的学科,包含了数学、软件工程和领域知识的元素。
因此,最适合软件开发的实践可能不适合数据科学。这种差异在项目工作流程、部署后维护和测试中尤为明显。
敏捷方法,如 Scrum 和看板,广泛应用于软件开发中。不幸的是,他们没有考虑数据科学项目中涉及的不同步骤:
- 定义问题陈述,
- 检索有用的数据,
- 执行探索性分析,
- 开发模型并评估结果。
因此,至少需要一个更高层次的框架,比如微软团队数据科学流程[3]。更常见的是,采用特定的工作流:全面的讨论超出了本文的范围,但可能会出现在未来的帖子中——如果您感兴趣,请告诉我!
我们列表中的第 7 点解决了第一次部署后模型的维护和发展:这留给我们测试。
在软件开发中,测试金字塔是一个公认的隐喻[4]。您可以通过单元测试来测试每个功能或方法的正确执行,通过集成测试来测试各个组件的正确集成,通过端到端测试来测试您的应用程序的某个特性的良好行为。所有的检查都是自动进行的,并且可以在每次引入更新时重复进行。
现在,这很好,应该尽可能地应用到数据科学项目中,但是……如何检查模型或探索性分析的“正确性”?
需要一种不同的方法。首先,在建模阶段开始之前,应定义建立模型性能的适当指标,以避免任何偏差。然后,数据也应该被版本化,因为它们是每个实验可重复性的关键。最后,应该对每个更新的建模管道的性能进行自动检查,以评估它是否带来任何额外的好处。对于“建模管道”,我们在这里指的是从原始数据到训练模型的一系列转换。这一流程是提升 MLOps 范式的核心,并通过 MLFlow 等新工具得以简化。
3。隔离您的数据科学家
一个数据科学项目需要很多不同的技能,而这些技能在个人身上很难找到。数据科学项目通常涉及至少三个群体:IT 团队、业务利益相关者和数据科学家。
假装一群有能力的数据科学家可以独自解决数据科学项目是毫无意义的。首先,因为数据科学是一种工具,应该被用来产生商业价值。业务价值所在的知识,提出正确问题的能力,都在业务涉众之中。同样,如果没有 IT 团队的帮助,将经过验证的解决方案投入生产也是极其困难的。
因此,这三个小组必须一起工作,能够相互理解,并恰当地融合他们的专业知识。为此,建立一个共享的术语表和一个像 Slack 或 MS Teams 这样的交流平台是很有用的。此外,接受并充分利用失败也很重要。即使在客户-顾问环境中,没有性能改进的实验仍然必须被展示和讨论,因为它们有助于为将来的迭代指明方向。
笔记本是促进有效协作的强大工具:对于数据科学家来说,它们是快速实验的完美平台,对于 IT 和业务人员来说,它们是易于理解的报告。
4。忽略数据
探索性数据分析(EDA)是任何数据科学项目的关键阶段。如果总运行时间的 80%用于检索、清理和分析数据,那么也可以说项目的成功很大程度上取决于提供给模型的数据的质量。
正如我们在的上一篇文章中所讨论的,从 EDA 中收集的洞察可以带来内在价值。另一个来自我们项目的例子来自食品和饮料行业。该项目旨在评估广告和折扣对销售额和利润的影响,以便自动计算出最佳策略。在 EDA 过程中,我们注意到我们客户的竞争对手能够同时提升他们产品的大部分销售,而我们的客户却不能。这一事实让他们的营销部门感到惊讶,并引发了对这一现象的详细调查。
然而,EDA 的主要目的是了解如何在建模之前清理数据,以及将哪个假设嵌入到模型本身中。在这样一个阶段偷工减料会极大地损害整个项目的结果。
5。跳过文档
在文档上投入时间和资源是一种浪费。代码是不言自明的,不是吗?
不对。
高绩效的组织发现前期的文档记录可以避免以后的痛苦[5]。
生产代码可能会告诉你你正在做什么,但是它不会向你提供关于为什么你正在做这件事的提示。那么所有产生次优性能的实验呢?导致最终解决方案的所有小见解、发现和挑战呢?这类问题的答案和解决方案本身一样有价值。
是的,但是如何记录数据科学项目的发展呢?
我们发现背页和出口的 Jupyter 笔记本上的乳胶极其有效。如果它们被适当的可再现性补充,用 git 实现代码版本化,用 DVC 实现数据版本化,用 MLFlow 或一些类似的工具实现实验版本化,就更是如此。
6.试图让第一个模型正确
数据科学项目本质上是迭代的。第一次尝试就获得最优解是不可能的,而且通常也是没有用的。
在对解决方案进行过度设计之前,有必要研究一下经过试验和测试的方法的文献。对于计算机视觉任务,很少有人会开发自己的神经网络架构:依赖现成的模型和迁移学习要容易得多,也更有效。
在不同的环境中,例如处理时间序列或表格数据,从简单的模型开始通常是明智的,更容易调整和解释。对于时间序列预测来说,优秀的老 ARIMA 预测器肯定不是最先进的,但它们可以告诉我们很多关于序列结构的信息,并指导更复杂方法的开发。
此外,最重要的指标不是关于准确性或错误,而是关于业务价值。用一个足够好的模型快速投放市场会带来有意义的反馈,并有助于引导随后的迭代产生最大的价值,而不仅仅是最好的模型。
最好的组织从简单开始,并将结果融入业务。在用更复杂的方法更新模型之前学习和测量[5]。
7.认为模型是永恒的
没有一种模式是永恒的。即使一个预测器今天在生产中有效,它也可能在几个星期后失效。这是因为一个模型的有效性通常取决于模型现象发生的背景。
当 COVID 在 2020 年初引发 lockdowns 中风时,我们开发的一些预测电能需求的模型经历了性能的急剧下降。这是可以的:没有一个模型被训练来应对全国性的关闭,因为(I)这以前从未发生过,以及(ii)考虑到这种遥远的可能性没有经济意义。
这是一个极端的例子,但模型运行的环境或数据中不太显著的变化会对它们的性能产生深刻的影响。因此,建立适当的监控基础设施并尽快将新的解决方案投入生产,同时避免危险的错误是至关重要的。
5 岁时,你的 ML 测试分数是多少?Google 的 ML 生产系统标题仍然是这个主题的主要参考之一。这篇论文解决的许多问题现在都是 MLOps 的核心,它有望给数据科学带来像 DevOps 给软件开发带来的一样大的推动。
谢谢你,我的读者,来到这里!
我们永远欢迎每一个评论、问题或总体反馈。如果你对 me 或者XT ream很好奇,可以上 LinkedIn 看看我们!
如果你喜欢这篇文章,你可能会感兴趣:
参考文献
[1]普华永道研究小组,确定奖项:人工智能对你的业务有什么真正的价值,你如何才能利用它?,2017
[2] Brian T. O'Neill,分析、人工智能和大数据项目的失败率= 85% —哎呀!,2019
[3]微软,什么是团队数据科学流程?,2021 年
[4]汉姆·沃克,实用测试皮拉姆,2018
[5] Domino,大规模数据科学管理实用指南,2021 年
[6]埃里克·布雷克,·蔡,埃里克·尼尔森,迈克尔·萨利布,d·斯卡利,你的 ML 测试成绩是多少?大规模生产系统规则,2016 年
鸣谢
本帖的大部分内容和原稿由 马可·帕鲁西奥 创作。xtream 的整个数据科学团队在审阅本文时提供了有效的支持。
我在机器学习生涯中保持高效的方法
生产力技巧、惯例、习惯追踪等等

安德里亚斯·克拉森在 Unsplash 上拍摄的照片
生产力不是目的地,而是一段旅程。
简单地认识到生产力不是一个目的地,目标或指标,而是一个以不同速度接近的旅程,让我们接受我们在最大化生产力时会遇到的共同缺点。
机器学习学科关注过程的优化和自动化。我发现自己在用不同的超参数重新训练机器学习模型,以挤出小单位精度。如果我在日常活动中遇到重复的任务,我的大脑会立即想办法自动完成这个手工过程。
这是大多数机器学习实践者随着时间的推移而获得的思维模式,所以我们作为机器学习实践者寻求优化我们生活领域的方法也就不足为奇了。你可以通过提高生产率来实现实际生活过程的优化。
但我不得不让你失望了。
读完这篇文章后,你的生产率不会提高十倍。
我试图让你做好准备的是,为了看到一些显著的改善,一些活动必须持续进行,直到它们在你的个性中根深蒂固,成为习惯。
本文介绍了机器学习从业者或任何技术人员可以采用的方法,以提高他们的产出,并充分利用一天的时间。
生产力在基于技术的角色中是必不可少的,因为机器学习相关学科通常需要个人同时平衡学习和交付软件功能。
制定日常计划

照片由 Prophsee Journals 在 Unsplash 上拍摄
你可以主宰一天,也可以让它主宰你。
让我们将常规定义为根据资源(如时间、能力(精神、身体)和可用性)计划的活动的结构化组织。一个体面的、经过深思熟虑的日常工作感觉就像是让你的一天和任务自动安排。
就我个人而言,例行公事可以让我对我为任务付出的努力设定时间表,这样就不会让自己筋疲力尽。对我来说,花 3 个多小时修复漏洞或实现新功能很容易,但事实是,在一项任务上花这么多时间,另一项任务就会受到影响。
这让我们看到了制定计划的另一个重要好处,那就是能够相应地进行优先排序。
下面是我在一个典型工作日的日常工作的快照。

我理想的日常生活
像大多数人一样,我会绊倒并偏离我设定的常规,有时会突然出现意想不到的会议,或发生紧急情况。不管怎样,有一个计划良好的日常工作能让我重新回到高效的道路上。
在我的日常工作中,我会花 2-3 个小时来完成任务,通常包括编码。我发现保持完全集中注意力超过一个小时有点难,所以我利用了****番茄工作法。
我仍然每个月都在我的日常时间和活动中玩耍。我总是试图找到最佳生产率和产量的最佳点。下面的步骤应该可以帮助你开始整理一个适合你的体面的日常工作。
你如何计划一个好的日常?
- 把你的一天分成一两个小时的时间段。
- 确定你所执行任务的类别。例如,白天工作,学习,烹饪等。
- 尝试执行任务的最佳时间。例如,我晚上锻炼,晚上做一些个人项目。
- 对基本任务有一个特定的模块。这些是具有高优先级的任务。
- 让你的日常活动可见
如果你喜欢的话,规划一个好的日常生活的步骤可能会很复杂和漫长。但是说实话,没必要把一些鸡毛蒜皮的事情复杂化。
里士满,如果这么琐碎,为什么我觉得很难养成好的习惯和套路?
是的,我也觉得很难。
在读了一些自助书籍后,我发现了几个可以让旅程变得更轻松的主意。
第一是将你的日常事务和习惯与身份联系起来。想一想你想要效仿的理想人物,然后像你想象中的“完美”人物那样度过你的一天。
第二个想法是加入一个奖励/惩罚系统。奖励好行为,惩罚坏习惯。
最后一个想法是套路很无聊。它们最终变得单调。随着时间的推移,一个新的惯例或习惯的兴奋消失了。为此做好准备,学会热爱无聊。
作为一名机器学习实践者,我习惯于在执行 ML 相关任务时遵循某些例程和流程。有经验的 ML 从业者会告诉你,跳过一个步骤会降低项目成功的机会。
拥有一个经过深思熟虑的程序就像拥有一辆特斯拉,或者更确切地说,一辆 2 级自动驾驶汽车。你深思熟虑的日常工作可以引导你的一天,加快或减慢你完成任务的速度。尽管如此,偶尔你还是需要亲自动手,针对旅途中不可预见的颠簸和障碍调整你的日常安排。
关键外卖
一个深思熟虑的日常工作指引着一天的方向。
通过对一天中的各种任务进行结构化的时间分配,大脑不必去想“下一步是什么?”。
经过测试和完善的常规让你可以轻松地确定优先级,同时培养习惯。
有责任
在我的 ML 职业旅程的早期,我一直保持在我的学习之上,并熟悉常见的机器学习技术和概念。我甚至写了大量的文章来转述我所学到的东西。
但是,我不得不承认,最近,我已经慢下来了。我仍然渴望成为一名伟大的 ML 从业者,所以现在我对自己负责。
为了确保我每天都能学到新的东西,我给自己定了一个任务,写 1000 字任何与 ML 相关的东西;它可能是一项新技术,一篇新发表论文的摘要,或者只是一些修改笔记。
我只需要写至少 1000 字。
有一天我写不出 1000 字,我会转 5 到我信任的朋友的银行账户。五英镑是一个很大的损失,但打破习惯的惩罚必须是痛苦的,否则我不会认真对待它。
通过连续五天写 1000 字,我有办法拿回我被没收的任何钱。每当你的自我激励储备不足时,对自己设定的习惯和目标负责会给你额外的动力。
我上面提到的场景是一对一责任的一个例子。在数字时代,你可以利用互联网让自己负起责任。
在线问责的一个很好的例子是 #66daysofdata 。66daysofdata 是由肯·吉发起的一个在线学习项目,他是数据科学领域的一个热门 YouTuber。
关键外卖
责任感确保你“出现”。
通过对你为自己设定的目标和职业抱负负责,你反过来允许自己变得更加富有成效。
软件是你的朋友

照片由 Fotis Fotopoulos 在 Unsplash 上拍摄
我不可能是唯一一个通过无数生产力工具和应用程序来寻求成为生产力的物理体现的人。
事实证明,在一个社交媒体应用程序是生产力黑洞的世界里,一些应用程序实际上站在你这边,帮助你进步。下面列出了一些应用程序,它们让我作为一名 ML 工程师保持了良好的工作效率。
1.森林
这款手机应用在苹果应用商店(在撰写时排名第一是有原因的。
Forest 是一款移动应用,它将自己定位为一款帮助用户保持专注和“活在当下”的工具。森林是一个番茄工作狂应用程序。它能让用户设定一个专注于任务的时间,这个时间会倒数到零,完成后,一棵植物就会从曾经的种子中发芽。
我利用 forest 来确保我的一天在编码相关的活动和休息之间有一个很好的平衡。我通常花一个小时编码/学习/写作,然后休息 15 分钟。
2.观念
观念是很多东西。它是一个数据库,日历,提醒和更多。但对我来说,观念是第二个大脑。

如果你感兴趣,你可以阅读我如何使用概念建立机器学习组合。
以下是我在机器学习生涯中运用概念的几种方式:
- ****任务规划者:在我的工作场所内运行计算机视觉计划意味着提前规划未来的发展和任务。目前,预计每个季度都会有功能开发。我使用观念看板结合日历提醒作为跟踪任务完成和交付的方式。
- ****文档:有许多文档工具可供技术人员利用和记录软件实现细节。我发现 ideas 是一个与非技术团队成员交流想法、概念和实现的好地方。在概念编辑器中,您可以使用@特性提及个人,向文本部分添加注释,轻松集成代码等等。
- 项目管理 : Trello 曾经是我多年来选择的项目管理工具。每个人都用 Trello,我打赌你甚至用过(或者仍然用过)。我可能会对此有一些反弹,但得出这样的结论是否安全:这种观念已经取代了 Trello 作为中小型创业公司项目管理工具的选择?
3.谷歌日历

照片由 Nadeena Granville 在 Unsplash 上拍摄
毫不奇怪,谷歌日历是我用来确保高效工作的主要工具。谷歌日历是生产力工具的“OG”。
虽然,谷歌日历的功能对于不常使用的用户来说并不明显。例如,在日历视图上显示多个时区。
我参与的项目涉及来自美国、巴基斯坦和英国的团队,因此每当我安排会议时,在我的日历上看到所有相关时区会有所帮助。Google Calendar 允许用户在日历显示中添加几个时区。不用再去想在世界的另一个地方下午 5 点是什么时间了。
如果您有兴趣了解与 Google 日历相关的更多提示和技巧,请查看 Jeff Su 制作的短片。
4.黑暗阅读器
Dark Reader 是 chrome 的一个扩展,它可以将你访问的几乎所有网站变成黑色版本(负对比度),或者在黑色背景上放置白色文本。

图片作者:里士满·阿拉克(作者)
机器学习从业者花费大量时间盯着显示器,显示器的炫目光线会导致长时间的眼睛疲劳。
使用黑暗版本的网站让你的眼睛休息一下。我们已经在黑暗模式下使用 ide 了。
这篇文章提供了一些关于阅读更多与屏幕亮度变化模式及其优势相关的文献的深刻信息。
关键外卖
利用生产工具,大多数这些软件产品都是为了简化您生活而构建的。
你可以识别这些工具,因为它们旨在消除分散注意力的因素,强化良好的习惯,或者帮助创造一个高效的环境。
不要用大量的生产力工具来淹没自己。尝试各种工具,并在 2-3 个你会经常使用并适合你生活方式的应用程序中进行选择。
硬件也是你的朋友

2020 年 3 月 23 日,星期一,英国开始封锁。六个月后,我房间/工作环境的四面墙成了我因不出门而感觉到的被困的字面上和隐喻上的代表。
我必须承认,我否认封锁对我的精神健康的影响,更影响我的生产力和工作效率。原本需要 30 分钟的任务似乎需要几个小时。我必须找到动力来度过一个工作日。
我知道你环境中的微妙变化会有很大的帮助。
首先,我重新安排了我的床的位置,整理了我的衣柜,最后用一个三星 49 英寸曲面超宽 LED 显示器取代了我的显示器。我通常一天盯着我的显示器超过六个小时,所以这是我能改变的工作环境中最重要的方面。它得到了回报。
人类的大脑被新奇的事物所吸引。我们的大脑会捕捉日常事务或环境的微小变化。新的闪亮的东西吸引我们的注意力。对我来说,我看待我的想法、文章和视频的方式的不同足以让任务看起来令人兴奋。
我在 ML 角色中使用的硬件的变化导致了对我工作的看法的积极转变,从而提高了工作效率。
而它没有;必须全是硬件。给我的工作空间带来意想不到的影响的是我现在工作的公司送给我的台式空气植物。他们也意识到,工作环境的微小变化有助于在禁闭中重振灵魂。
如果您对硬件以及它们如何提高生产率感兴趣,可以看看本文末尾链接到的文章。
关键外卖
有研究表明,当引入新奇事物时,人脑中的多巴胺水平会增加。
竞争是无聊的,而变化是令人兴奋的,利用这一知识,通过改变你在职业生涯中使用的硬件来重新点燃失去的火花。更换记事本、键盘或显示器。
你的环境是你思想的反映

工作环境的设计有助于确保你快速进入并保持高效状态。
两年前,在我的硕士学习期间,我很难集中注意力并保持良好的习惯——我无法保持专注反映在我的一些课程相关的工作中。在期末考试来临之前,我知道我必须全力以赴。
在尝试设计创造和坚持好习惯的新方法时,我看到了詹姆斯·克利尔的《原子习惯》这本书。詹姆斯用整整一章的篇幅讲述了环境的重要性,以及环境设计如何有助于保持好习惯,消除坏习惯。
你不必成为环境的受害者。你也可以成为它的建筑师。
—詹姆斯·克利尔,《原子习惯》
当时,我意识到我所处环境的设计确保了我长时间保持注意力分散,短时间保持高效。我的手机总是触手可及;我的游戏机离我只有两步之遥,朋友们离我的房间只有几层楼梯。
对我的环境进行简单的重新设计产生了积极的结果。在不到一分钟的时间里,我可以轻松地花三个小时写论文(由 cans Red bull 提供动力),这一切都是因为我把手机放在厨房里或在图书馆学习。
对于现在没有论文要写或课程要交的职业中的机器学习从业者来说,我们也可以从环境重新设计中受益。
作为 ML 从业者,我们可能每天大部分时间都盯着屏幕并连接到网络,因此将我们的环境想象成物理和虚拟的会有所帮助。
- 在需要全神贯注的时候,把你的手机移到另一个房间。
- 如果你想补充更多的水分,在办公桌上放一升水。在你的桌子上放一个瓶子是提醒你喝酒的视觉信号。
- 你曾经在无精打采的时候感到有动力或有效率吗?购买一把符合人体工程学的办公椅有助于提高工作效率,并有助于保持良好的姿势和健康。
关键外卖
你的环境设计直接影响到你的工作效率。环顾你的工作空间,用那些能强化你想成为的人的东西来代替那些分散注意力的东西。
用一本书代替电话支架,或者用一株小植物代替零食抽屉。
结论
让我给你介绍一下生产力地狱🔥。
你听说过“回调地狱”吗?
在异步编程中,这是指几个函数的嵌套。看看下面的图片,理解回调函数在编程上是什么样子的。这被认为是不好的做法,会产生容易出错的代码。

里士满·阿拉克的回调地狱图像
你可以把生产力地狱想象成回调地狱。首先,你尝试一两个提高效率的小技巧,它们很有效,事实上非常有效。
然后你再尝试一两次,然后越来越多。
最终,你会面临选择疲劳,不知道该使用哪种生产力工具,黑客还是技巧。你花了太多的时间和精力去研究和选择生产力工具,以至于忘记了生产力。
如果你从这篇文章中得出的只是一种提高你职业生涯生产率的方法,那就很好。
正如我在文章开头提到的,生产力是一个旅程,而不是目的地。做一些小的渐进的改变,享受这个过程。
在开始之前,让我们重温一下你可以利用的生产力技巧和窍门:
- 每天都要有一个常规,把一天中特定的时间用于重要的任务
- 对你的目标负责。找一个负责任的伙伴(朋友、同事、直线经理或在线社区)
- 请利用生产力应用程序,但不要被它们淹没
- 在技术角色中,硬件变化可以提高工作效率和专注度
- 你所处的环境对你的工作效率至关重要。清除工作场所周围令人分心的东西。
硬件和生产力
**https://www.fastcompany.com/90590901/best-productivity-software-hardware-for-2021 https://betterhumans.pub/hardware-to-boost-your-productivity-in-2020-670c08e0ded1
我的更多文章
**
基于度量(基于评级)的联合分析
用于洞察消费者偏好的技术
由吉纳·戈麦斯
与格雷戈·佩奇
在营销分析中,联合分析是一种用于获得关于消费者偏好的具体见解的技术。联合分析通常来源于消费者调查,可以告诉我们,例如,手机购买者更关心更大的屏幕、更大的硬盘容量还是更长的电池寿命。此外,通过测量特定功能对消费者选择的精确影响的模型,这种分析可以告诉我们消费者对这些功能的每个选项有多关心。
为了做出这样的决定,营销人员首先向调查对象提供假设的功能组合或捆绑。一个手机套装可能包括 5 英寸屏幕、16 千兆字节硬盘和 12 小时空闲电池寿命,而另一个包括 7 英寸屏幕、32 千兆字节硬盘和 8 小时空闲电池寿命。捆绑销售可以附有价格,以衡量消费者对成本的敏感度,也可以不带价格信息,以简单地衡量各种选择的偏好。
有几种类型的联合分析方法,其中包括基于选择的联合分析(向消费者提供一系列非此即彼的选择)、全排名联合分析(要求消费者对一系列捆绑包进行排名,从最好到最差),以及基于指标或评级的联合分析。
在基于度量的联合分析(本文的主题)中,消费者被要求对一系列捆绑包进行评级,通常从 1 到 10。在营销人员汇总了所有调查数据后,可以建立一个模型来“连接”功能选项和调查受访者评级之间的点。这种模型中使用的输入可以是连续的或分类的。因为结果变量是数字,所以线性回归是这种情况下的理想建模工具。
基于度量的方法的效用函数是:
γ=X1β1+X2β2 +X3β3… + ε
其中γ是每个完整配置文件的评级或可接受性度量,Xn 是输入要素,n 是与属性集相关的回归系数,ε是随机误差项。
在接下来的部分中,我们将展示一个基于度量的联合分析的完整例子。
首先,将干净的数据集读入环境:

这个数据集中有七个变量。其中六个是自变量,第七个变量(评级)是因变量。数据集描述如下所示:
Pax percar:“Pax”是乘客的缩写。选项包括每节车厢 2、3 或 6 名乘客。
高度:离地面 100、200 或 300 英尺,从上到下测量。
opentop: 每辆摩天轮“车”是应该完全遮盖,还是顶部大开。
总时间:从上车到下车的时间长度,以秒计算。选项有 80、240 或 420 秒。
摇摆:如果用户摇晃汽车,汽车应该能“摇摆”吗?(是或否回答)。
颜色:颜色选项有绿色、白色、红色、紫色。
评级:每个捆绑包的评级或可接受性度量
paxpercar 有三个选项,三个高度选项,两个开放式选项,三个总时间选项,两个摇摆选项,四个颜色选项,共有:
3 x 3 x 2 x 3 x 2 x 4 = 432 种可能的独特功能组合。
数据集有 432 行,每行代表一个唯一的包或要素组合。结果变量 rating 表示看到该捆绑包的所有调查受访者对该捆绑包的平均评分。请注意,每个调查受访者并没有对每个捆绑包进行评级——这将导致相当大的“调查疲劳”问题!但是,如果成千上万的调查受访者每个人对 6-8 个捆绑包进行评级,那么所有这些评级的结果可以汇总起来,生成一个数据集,就像这里使用的数据集一样。
从上面的描述中,我们知道 sway、opentop 和 color 是分类输入变量,而 height、totaltime 和 paxpercar 是数字输入变量。要为基于度量的联合分析准备数据,所有变量都应该虚拟化(即使是数字变量)。这样,模型会将这些变量的值视为一系列特定的离散选项,而不是一个连续的范围。对数字输入进行虚拟化的失败会导致两个问题:首先,它会错误地表达数据,暗示选项是连续存在的;第二,也是更重要的,如果最理想的选项落在选择范围的中间,可能会导致重大的解释问题。由于线性回归模型只为数值变量分配了一个系数,因此该模型会根据系数的符号给出一个结果,表明较大的值或较小的值是最佳值。
如下所示,pandas 的 get_dummies()函数用于为线性建模准备这些变量。请注意,包含了 drop_first = True 参数-这是避免由于输入中的多重共线性而导致模型结果不可靠的风险的重要步骤。
当我们解释模型结果时,我们需要记住,每个变量的第一级不会出现在系数列表中。作为“参考水平”,它可以被视为一个零当量:任何正系数表明一个选项比参考水平更可取,而任何负系数表明一个选项不如参考水平可取。

然后,使用 scikit-learn 的 LinearRegression 模块,以评级为输出变量,以所有其他变量为输入变量,构建了一个线性模型。(使用不同的库,我们验证了模型的 f 检验分数显示了总体显著性,并且预测变量也具有统计显著性)。

在所有的分析之后,结果向我们展示了调查中所有特性中最受欢迎的选项。从上面显示的输出中,我们可以看到受访者最希望的属性是:每辆车 6 名乘客,离地 300 英尺的高度,摩天轮“车”应该完全覆盖,乘坐时间长度应该是 420 秒,非摇摆的汽车,以及红色。
然而,这并不意味着委托这项调查的公司应该立即实施所有这些功能!如果这个分析的目标是简单地确定最受欢迎的包,我们可以跳过整个建模过程,只使用一个 pandas 函数从上到下对 432 个包评级进行排序。相反,我们可以通过查看系数的相对差异和某些特征系数的大小来增加对这些系数值的分析洞察力。我们还可以指出字面解释模型结果的一些潜在问题,并思考这如何指导未来的其他研究工作。
系数值的相对大小让我们了解人们意见的强度。看看颜色——这往往会引起人们的强烈反应,在 10 分制的评分系统中,红色占三分以上。与绿色(参考水平)相比,紫色和白色也引起了相当强烈的反应。一个可能的原因是人们很容易想到颜色。虽然其中一些特征看起来有点抽象,不在摩天轮上或附近很难想象,但是颜色很容易想象,也很容易形成一个观点。
我们还可以从我们在各种特征中看到的任何模式中收集见解。由于对敞篷汽车的明显偏好,以及对摇摆汽车的反对,调查受访者中可能存在一种普遍的“支持安全”态度,如果他们被纳入未来的调查,这种态度将延续到其他与安全相关的选项中。
在这些调查中,在数字变量选择中也出现了一种模式:在所有情况下,人们都倾向于“最大”的选项——他们选择最高的高度、最长的乘坐时间和最大的摩天轮车。

亚历克斯·奇斯托尔,摄影师。(2020 年 2 月 23 日)。摩天轮。从 Pexels 检索。
然而,这真的是人们想要的吗?上图中的伦敦眼有 443 英尺高。即使从这张照片中,也很难获得 443 英尺离地面到底有多高的视角。最有可能的是,人们在回答这项调查时没有任何关于身高的真实参考框架,他们可能认为最高的选项将是最有趣的。当建造实际的游乐设施时,公园可能希望考虑对高度的总体偏好趋势,但也可能不得不考虑工程挑战、保险成本以及与建造如此高的游乐设施相关的其他实际问题。
至于乘车时间变量,受访者表现出对较长乘车时间的偏好。当人们回答这个问题时,他们最有可能想象自己在骑行,玩得开心——但他们不太可能事先考虑到他们需要排队等候的长度。公园运营专家可能还必须考虑排队等候时间来确定最佳乘车时间。
此外,我们这里没有任何关于谁参加了这项调查的具体细节——这些人可能是主题公园的游客吗?如果是的话,他们有可能乘坐摩天轮或类似的游乐设施吗?或者,受访者只是普通大众的一个代表?
基于这些结果,我们可能产生的潜在问题从理论上讲是无穷无尽的。基于度量的联合分析最终并不是要得出某种确定的解决方案。然而,这种模糊不清不应被视为一个问题——我们可以从基于指标的联合分析中获得的大部分价值来自它帮助我们产生的问题,以及它可以揭示的关于消费者偏好的总体趋势和见解。
度量学习技巧和诀窍
实践教程
如何在没有标记数据的情况下训练对象匹配模型并用于生产
目前,大多数机器学习相关的商业案例都是作为分类问题来解决的。分类算法在实践中得到了很好的研究,即使原始问题不是直接的分类任务,它通常也会被分解或近似转换成一个分类任务。
然而,尽管分类任务很简单,但其要求可能会使生产集成和扩展变得复杂。例如,它需要固定数量的类,其中每个类应该具有足够数量的训练样本。
在本文中,我将描述我们如何通过切换到度量学习来克服这些限制。通过匹配职位和候选人的例子,我将展示如何在没有人工标记数据的情况下训练度量学习模型,如何估计嵌入置信度,以及如何在生产中服务度量学习。
什么是度量学习,为什么要使用它?
根据维基百科的说法,度量学习是学习对象上的距离函数的任务。在实践中,这意味着我们可以训练一个模型,为任何一对给定的对象给出一个数字。并且这个数字应该表示这些给定对象之间的相似度或相似度分数。例如,得分为 0.9 的对象可能比得分为 0.5 的对象更相似。实际分数及其方向在不同的实现中可能有所不同。
在实践中,有两种主要的度量学习方法和两种相应类型的神经网络结构。第一种是基于交互的方法,它首先在两个对象之间建立本地交互(即本地匹配信号)。深度神经网络学习分层交互模式进行匹配。神经网络架构的示例包括 MV-LSTM、ARC-II 和 MatchPyramid。

MV-LSTM,基于互动的模型示例,万圣贤等人通过 Researchgate
第二种是基于表示的方法。在这种情况下,距离函数由两个部分组成:编码器将一个对象转换为嵌入表示——通常是一个大的浮点向量,而比较器从编码器获取一对对象的嵌入,并计算它们的相似性。这种嵌入表示最著名的例子是 Word2Vec。
比较器通常是一个非常简单的函数,计算速度非常快。这可能是余弦相似性,甚至是一个点产品。两阶段模式允许每个对象只执行一次复杂的计算。一旦被转换,比较器可以独立于编码器更快地计算对象相似性。
为了更方便,嵌入可以放入专门的存储或矢量搜索引擎。这些搜索引擎允许使用 API 管理嵌入,使用向量执行搜索和其他操作。

C-DSSM,基于表示的模型的例子,李雪等人通过 arXiv
也可以使用预先训练的神经网络。倒数第二层的输出可以作为嵌入式表示。在本文的后面,我将重点介绍基于表示的方法,因为它被证明更加灵活和快速。
那么,与分类相比,使用度量学习有什么优势呢?对象编码器不假定类的数量。所以,如果你不能把你的对象分成类,如果类的数量太多,或者你怀疑它将来会增长,考虑使用度量学习。
在我们的案例中,业务目标是为指定所需职位名称的候选人找到合适的空缺。为了解决这个问题,我们使用分类器来确定空缺职位和候选人的工作类别。但是这个解决方案仅限于几百个类别。候选人抱怨说他们找不到适合自己的类别。为新类别训练分类器将会太长,并且需要为每个新类别提供新的训练数据。切换到度量学习允许我们克服这些限制,得到的解决方案可以比较任何配对位置描述,即使我们还没有这个类别参考。

T-SNE 与工作样本,由作者图像。自己玩嵌入投影仪
通过度量学习,我们学习的不是具体的工作类型,而是如何将候选人简历中的工作描述与空缺职位相匹配。其次,有了度量学习,不需要模型再训练,很容易增加更多的参考职业。然后,我们可以将引用添加到矢量搜索引擎中。下一次我们将匹配职业——这个新的参考向量将是可搜索的。
公制学习数据
与分类器不同,度量学习训练不需要特定的类标签。所需要的只是相似和不相似物体的例子。我们称之为阳性和阴性样本。
同时,它可能是一对对象之间的相对相似性。例如,双胞胎看起来比一对随机的人更像。随机的人比一个人和一只猫更相似。模型可以使用这样的相对例子来学习。
好消息是,分类只是确定相似性的一个特例。要使用这样的数据集,只需将一个类的样本声明为阳性,而将另一个类的样本声明为阴性。通过这种方式,可以将几个具有不匹配类的数据集组合成一个用于度量学习的通用数据集。
但并不是只有划分成类的数据集才适合提取正反例。例如,如果在对象的描述中有附加特征,这些特征的值也可以用作相似性因子。可能没有类成员关系那么明确,但是相对的相似度也适合学习。
在工作描述的情况下,有许多职业的本体,由于这种方法,它们能够被组合成一个单独的数据集。我们甚至更进一步,使用相同的职位来寻找相似的描述。
结果,我们得到了一个自我监督的通用数据集,不需要任何手动标记。
不幸的是,普遍性不允许在训练中应用某些技术。接下来,我将描述如何克服这个缺点。
训练模型
有几种方法来训练度量学习模型。其中最流行的是使用三元组或对比损失函数,但我不会在本文中深入探讨它们。然而,我将告诉你一个有趣的技巧,它帮助我们使用统一的训练示例。
有效训练度量学习模型的最重要的实践之一是硬负挖掘。该技术旨在包括负样本,在该负样本上,模型在上一个训练时期给出了更差的预测。大多数描述这种技术的文章都假设训练数据由许多小类组成(大多数情况下是人脸)。有了这样的数据,就很容易找到不好的样本——如果两个来自不同类别的样本有很高的相似度得分,我们就可以把它作为负样本。
但是我们的数据中没有这样的类别,我们仅有的是假定在某些方面相似的职业对。我们不能保证在这一对中没有更好的匹配了。这就是为什么我们不能对我们的模型使用硬负挖掘。

Alfonso Medela 等人 via arXiv
为了弥补这一限制,我们可以尝试增加随机(弱)阴性样本的数量。实现这一点的一种方法是训练模型更长的时间,这样在训练结束时它会看到更多的样本。
我们在调整损失函数时找到了一个更好的解决方案。在三联体或收缩损失的常规实施中,将每个阳性对与一些或几个阴性样本进行比较。我们所做的是允许在整批中进行配对比较。在这种情况下,损失函数惩罚所有的随机对象对,如果它的分数超过一批中的任何正分数。
这个扩展给出了 ~N × B 的比较,其中 B 是批次的大小, N 是批次的数量。远大于常规三重态损耗中的 ~ N × B 。
这意味着增加批次的大小会显著增加负比较的数量,因此应该会提高模型的性能。
我们能够在实验中观察到这种依赖性。类似的想法我们也在文章监督对比学习中发现。
模型置信度
在现实生活中,经常需要知道模型在预测中的可信度。是否需要手动调整或验证结果。
使用常规分类,很容易通过分数了解模型对结果的置信度。如果不同类别的概率值彼此接近,则模型是不自信的。相反,如果最可能的类别相差很大,则该模型是可信的。
乍一看,这不能应用于公制学习。即使预测的对象相似性分数很小,也可能仅仅意味着参考集没有合适的对象可以比较。相反,该模型可以对得分较大的垃圾对象进行分组。
幸运的是,我们发现了对嵌入生成器的一个小修改,它允许我们以与使用 Softmax 激活函数的传统分类器相同的方式定义置信度。
修改在于构建嵌入作为特征组的组合。每个特征组在嵌入中被表示为一个独热编码子向量。如果模型可以有把握地预测特征值,则相应的子向量在其一些元素中将具有高绝对值。为了更直观的理解,我建议不要把嵌入看作空间中的点,而是一组二进制特征。
为了实现这种修改并形成适当的特征组,我们需要将一个常规的线性输出层改为几个 softmax 层的连接。每个 softmax 组件将代表一个独立的特征,并迫使神经网络学习它们。
例如,我们有 4 个 softmax 组件,每个组件有 128 个元素。每一个这样的组件都可以被粗略地想象成一个 0 到 127 范围内的热编码数字。因此,得到的矢量将代表 128⁴可能的组合之一。如果训练好的模型足够好,你甚至可以尝试单独解释奇异特征的值。

Softmax 特征嵌入,作者图片。
神经规则
机器学习模型很少训练到 100%的准确率。在传统的分类器中,误差只能通过修改和重复训练过程来消除。然而,度量训练在这方面更加灵活,它允许您引入额外的步骤来纠正已经训练好的模型的错误。
度量学习模型的一个常见错误是错误地声明对象是封闭的,尽管实际上它们不是。为了纠正这种错误,我们引入了排除规则。
规则由编码到向量空间中的 2 个对象锚组成。如果目标对象落入锚点的效果区域之一,就会触发规则。它将从预测结果中排除第二锚区域中的所有对象。

神经排除规则,作者图片。
使用嵌入的便利之处在于,不管有多少规则,您只需要对每个对象执行一次编码。然后,为了找到合适的规则,比较目标对象的嵌入和预先计算的规则锚的嵌入就足够了。当被实现时,它只转化为对向量搜索引擎的一个额外的查询。
生产中的矢量搜索
当在生产中实现度量学习模型时,出现了关于向量的存储和管理的问题。如果服务中出现新的工作描述,应该很容易添加新的向量。
在我们的例子中,我们还需要对搜索应用附加条件。例如,我们需要筛选候选人的位置和语言熟练程度。
我们没有找到现成的工具来进行这种向量管理,所以我们创建并开源了我们内部的向量搜索引擎,名为 Qdrant 。
它允许你用一个简单的 API 添加和删除向量,独立于你正在使用的编程语言。您也可以将有效载荷分配给向量。这个有效负载允许在搜索请求期间进行额外的过滤。
Qdrant 有一个预建的 docker 映像,开始使用它就像运行一样简单
docker run -p 6333:6333 generall/qdrant
带示例的文档可以在这里找到。
结论
在本文中,我展示了度量学习如何比分类模型更具可伸缩性和灵活性。我建议在你的任务中尝试相似的方法——可能是匹配相似的文本、图像或音频数据。利用现有的各种预训练神经网络和矢量搜索引擎,很容易构建基于度量学习的应用程序。
在 MoBerries 尝试 ML-powered 求职。
订阅我的电报频道,在那里我谈论神经网络工程,发表度量学习和神经搜索应用的其他例子。
指标、指标和更多指标,另外:我们的按需初学者指南在这里
这一期的《变量》一如既往地载有过去一周的一些最佳 TDS 读物。不过,在我们开始之前,我们想分享一个我们最近刚刚推出的新资源——我们免费的、基于电子邮件的按需初学者指南。
如果你目前正在开始学习数据科学,你可以注册来接收每日的初学者友好文章和实用技巧。(是两周的课程,可以随时退订。)我们希望你喜欢它。

好吧,阅读推荐!让我们从约瑟夫·罗卡和巴普蒂斯特·罗卡对复杂的勘探开发困境的深入探究开始。它们涵盖了任何基于数据的决策过程中的一个棘手部分——当您决定是否有足够的数据来满足您的需求,或者在最佳行动方案变得明显之前应该等待更长时间。
继续模糊情况的主题, Cassie Kozyrkov 探索了棘手的度量世界,并坚持认为坚持你可以精确定义的东西是至关重要的:“与其爱上一个词并为其本身而追求它,不如深入思考你想要测量的真实世界的量。”
如果度量标准可以是混乱的,我们可以说人工智能伦理是什么?在这个快速发展的领域,理论上进步似乎触手可及,但实际上事情往往变得更加模糊。大卫·格劳思认为我们应该将公平原则应用到现实世界的问题中,提供了几个强有力的例子,说明在新闻推荐和招聘等不同领域,公平原则可能会是什么样子。还有心情讨论人工智能的未来吗?不要错过最新的 TDS 播客第集,其中 Jeremie Harris 和嘉宾 Ryan Carey 讨论安全、风险管理以及我们应该如何为人工智能设计正确的激励措施。
Ismael Kherroubi Garcia 在他的探索数据科学中数据的意义中也有类似的方向。只有当我们以正确的方式处理数据——牢记认知谦逊和多样性的原则——才能“分析产生与现实世界相关的可操作知识”
如果本周你感觉更多的是动手而不是清高(或者既动手又清高?),我们也有几个选项供您考虑:
- 向 Nina Hristozova 了解 ROUGE 评分以及这个指标如何帮助你评估抽象文本摘要的质量。
- 跟随 Leigh Johnson 从概念到原型再到试运行构建人工智能/人工智能驱动产品 Print Nanny 的旅程。
- 阅读我们的作者 Spotlight Q & A 与 Kurtis Pykes 的文章,他分享了他作为一名数据科学家在自由职业道路上取得成功的一些技巧和经验。
- 掌握一项新技能——Steve Attila Kopias发布了一个新的分步指南,教我们如何将网格数据集和基于网络的地图结合在一起。
我们希望你喜欢本周的选择——如果有任何帖子引起了你的特别共鸣,请考虑为其作者留下评论。(很少有什么事情比与读者互动更让作者欣赏的了!)
我们一如既往地感谢您的支持,感谢您选择加入我们的数据科学探险。
直到下一个变量,
TDS 编辑器
我们策划主题的最新内容:
入门指南
- dataset 简介:由 Khuyen Tran 在一行代码中探索并发布您的数据
- Kubeflow 管道:如何从零开始建立你的第一个 Kubeflow 管道作者 Fernando López
- 女性如何在数据分析领域重新开始职业生涯作者鲁奇·德什潘德
实践教程
- 使用 Bayseian 扩散模型进行高级预测作者 Fraser Lewis
- Julia 上带跳转的混合整数规划综合研究(第二部分)作者 Ouaguenouni Mohamed
- 地理空间分析的数据准备和 Python 中的 la guerre-Voronoi ML作者 Sunayana Ghosh
深潜
思想和理论
绩效指标:浓缩咖啡
咖啡数据科学
设计你的品味尺度!
浓缩咖啡是一种很难掌握的饮料,但也不是不可能。在过去的几年里,我已经通过这些不同的方法以及一张数据表极大地提高了我的技术。因此,我将回顾一下我所使用的性能指标,以帮助那些有兴趣收集自己的数据来帮助改善他们的浓缩咖啡实验的人有一个更广泛的概述。
味觉测量
最终得分 是记分卡 7 个指标(尖锐、浓郁、糖浆、甜味、酸味、苦味和回味)的平均值。当然,这些分数是主观的,但它们符合我的口味,帮助我提高了我的拍摄水平。分数有一些变化。我的目标是保持每个指标的一致性,但有时粒度很难确定。

所有图片由作者提供
前三个测量值需要解释,因为其他四个是不言自明的:
尖锐指的是当浓缩咖啡第一次触及你的舌头时的尖锐程度,以及它对你的味觉的震撼程度。我不喜欢因为喝了这么烈的东西而咳嗽,但这是我喜欢的尖锐的标志。这个标准的一个问题是,一个平滑的镜头不会很锐利,但这并不意味着它是一个糟糕的镜头。因此,这让我开始审视我所使用的衡量标准的变化,甚至是通过 Q-gradier 认证。
丰富是指镜头整体的丰富和复杂。我更喜欢 1:1 的比例(输入重量与输出浓缩咖啡的重量),因为我想重温我喝的第一杯浓缩咖啡。我的第一枪尝起来就像在嘴里融化了一块巧克力;它完全占据了我的舌头。
糖浆指的是质地,我的目标是我记忆中的第一个镜头。浓咖啡在我的舌头上感觉很浓,像糖浆或融化的巧克力。另一个词是口感。我坚持用糖浆来赞美我第一份工作的日子。
我的评分不同于 Q 级选手用杯装咖啡时的评分。Q 分数是在咖啡杯过程中记录的 10 个因素的顶点。我根据自己对 SCA 杯状协议的理解,总结了每个指标。他们定义了如何准备咖啡样品,如何品尝,以及如何评分。我相信网上有很多资源可以帮助你通过快速搜索了解如何拔火罐。

作者提供的图片与本文中的所有图片一样
重量/时间指标
重量测量:
- 输入重量(咖啡渣)
- 输出重量(称量液体咖啡)
- 射击时的输出重量
重量有助于控制一致性,并允许更好的拍摄比较。
时间 度量:
- 总拍摄时间
- 预输注时间
- 时间覆盖过滤器(TCF) 。
这些指标有助于保持一致性。
来自更好的天平的流量测量可以提供一些见解,它们可以帮助复制拍摄。最重要的是,在击球时使用秤可以让你每次都打出相同的重量。

高级指标
圆盘底部 我观察了圆盘的底部,以帮助找到较暗的斑点或图案,这些斑点或图案表明我的分配或夯实工作存在问题。

无底的入口滤镜 在拍摄过程中非常有用,可以看到滤镜上发生了什么。如果你看到喷水,你可以减少一些机器(杠杆机器)的压力,或者如果时间太长,你可以强迫更多的水通过,暂停,让咖啡沸腾。



总溶解固体(TDS)是用折射仪测量的,这个数字结合弹丸的输出重量和咖啡的输入重量用来确定提取到杯中的咖啡的百分比,称为提取率(EY)。****
提取率(EY) 是溶解到咖啡杯中的咖啡渣的百分比。通常情况下,只有 30%的咖啡是可溶的,对于浓缩咖啡,你的目标是 18%到 22%,如果没有涩味的话,可能的话再高一点。
超过最高指标
这些指标应用于数据,以了解镜头内部发生了什么。它们需要更多的时间和专业知识以及大量的实验。
****图可以帮助可视化不同的指标,以帮助人们看到模式。从这些图中,最佳拟合线可用于给出一个指标,衡量趋势线与数据的拟合程度。

相关性 是表示两个变量彼此相似程度的度量。高度相关并不意味着一个变量会引起另一个变量,而是当情况发生变化时,两个变量的涨跌幅度相同。我从一开始就假设一些分级变量有很高的相关性,因为它们从不同的时间点观察味道,如丰富度和回味。

咖啡颗粒分析 可用于帮助了解您的研磨机如何工作,以及哪些设置最适合您的设置。这通常必须使用筛选或成像来完成。

过滤篮分析 可用于了解屏幕上的图案是否与过滤篮的孔尺寸和间距有关。它也可以用来帮助理解总孔面积和比较一个过滤篮与另一个。

咖啡等级和风味分析 可以用来寻找相互补充的咖啡,也可以更好地了解世界各地咖啡之间的差异以及它们在当地的相似性。
****
我不建议任何人使用我使用的所有指标。我发布它们是为了鼓励人们找到对他们最有意义的指标。当我第一次设计我的味觉量表时,我没有使用 Q 级量表。我深入思考了一杯浓缩咖啡对我最重要的意义,然后试图找到合适的词来描述它。最终,制作浓缩咖啡是根据你最喜欢的口味来酿造的,你的口味偏好是一些严肃的个人事务。
如果你愿意,可以在推特和 YouTube上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。你也可以关注我的媒体。
我的进一步阅读:
机器学习库的微内核体系结构
实践教程
Python 元类的微内核架构示例
什么是微核架构
核心系统包含使系统运行所需的最少功能。插件组件是独立的,彼此独立,每个组件都用附加功能增强或扩展了核心系统。
核心系统还维护一个插件注册表,它定义了核心系统和每个插件组件之间的信息和契约,如输入/输出签名和格式、通信协议等。

图片来自 O'Reilly: 软件架构模式
微核架构的利弊是相当明显的。一方面,关注点的分离使得它非常灵活和可扩展,并且易于维护、测试和发展。它特别适合于基于产品的应用程序,在这种应用程序中,您可以向客户提供一个 MVP,并在这个过程中添加更多的版本和特性,而几乎不做任何更改。
另一方面,这种体系结构不适合需要频繁通信和不同组件之间依赖的系统。您当然可以在插件模块之间添加依赖项,但是依赖项的数量越多,维护成本将呈指数级增长,这超过了它带来的好处。
为什么机器学习库采用微内核架构
除了训练和预测,典型的机器学习库还有许多关注点,如数据源的可配置源/汇,以及标准化、监控、安全等跨领域关注点。这些方面和问题是孤立的,独立于数据科学家的建模技术和算法。最好由其他工程师来解决。
微内核架构非常适合这样的目的。它支持数据科学和插件工程组件的并行开发,同时保持高度的灵活性和可维护性。
Python 元类的微内核架构示例
什么是 Python 元类
在 Python 中,元类是类的类。它定义了一个类的行为方式以及该类的实例是如何被实例化的。您可以将元类视为类工厂,这允许您在创建类时做一些额外的事情。在我们的例子中,我们创建了一个元类,用于向主模型类注册插件组件。从而将建模实例与工程模块分离。数据科学家可以将所有注意力集中在建模上,而工程师可以并行处理工程组件。
这个例子
我为这个微内核架构创建了一个 PyPi 包 mkml 。如何使用请参考 github 库。

具有微内核架构的 ML 库
出于演示目的,我创建了 3 个插件组件,分别是标准化、监控和数据源插件:
- 标准化插件实施模型类的方法签名。在我们的例子中,模型类必须有拟合、预测和评分方法
- 监控插件监控模型类的所有功能。在我们的例子中,它记录了输入参数、异常以及模型类中每个函数的持续时间
- LocalDataSourcePlugin 帮助在本地加载数据。它动态地将数据加载函数接收到模型类中,帮助数据科学家检索数据,而不用担心如何检索
从 PyPi 安装
pip install mkml
通过从链接到自定义元类的 BaseModel 扩展来创建你自己的模型
from sklearn.linear_model import LinearRegression
from mkml import BaseModel
class UserModel(BaseModel):
def __init__(self):
self._model = LinearRegression()
def fit(self, features, labels):
self._model.fit(X_train, y_train)
def predict(self, features):
self._model.predict(features)
def score(self, features, labels):
return self._model.score(features, labels)
实例化模型类实例,加载用于训练和预测的特征和标签
um = UserModel()
features = um.get_local_data(feature_mart_location='data', group_id='train_features')
labels = um.get_local_data(feature_mart_location='data', group_id='train_labels')
um.fit(features, labels)
test_features = um.get_local_data(feature_mart_location='data', group_id='test_features')
test_labels = um.predict(test_features)
创建你自己的定制插件模块(即远程数据源的插件)
from mkml import BasePlugin
class RemoteDataSourcePlugin(BasePlugin):
def __init__(self, name):
self._name = name
def apply(self, attrs, **kwargs):
logger.debug('Entering data source plugin')
attrs['get_remote_data'] = self._get_remote_data
def _get_remote_data(self, feature_mart_location, group_id):
# To be implemented
pass
向元类注册自定义插件模块
## You can add additional instantiation parameters to the Plug-in class as well
MKMLMetaclass.register('remote_datasource', RemoteDataSourcePlugin, 'remote_ds_plugin')
元类 MKMLMetaclass 控制用户模型类的实例化行为。因此,一旦 RemoteDataSourcePlugin 向元类注册,UserModel 类将具备插件带来的能力。
使用新的远程数据源插件检索特征和标签
um = UserModel()
features = um.get_remote_data(feature_mart_location='http://fm', group_id='train_features')
labels = um.get_remote_data(feature_mart_location='http://fm', group_id='train_labels')
样本用法请参考本笔记本。
结论
在本文中,我介绍了什么是微内核架构,并且我还创建了一个 PyPi 包 mkml 来演示这种架构如何给 ml 库设计带来好处。
在我看来,微内核架构的最大优势是数据科学和工程工作负载之间的解耦,同时保持系统非常高的可扩展性和可维护性。一个好的架构设计总是追求控制、逻辑和数据的清晰分离。该架构清楚地展示了这样一个特征。
最后一点要注意的是,虽然 Python 的元类用于实现,并且它是一个非常强大的工具,但是在尝试使用它时要非常小心,因为如果使用不当,它会导致许多问题。
午夜黑客 5:使用机器学习对 Spotify 播放列表进行分类
使用 KNN 和随机森林对 Spotify 曲目进行分类和聚类。

向播放列表中添加一首曲目可能只需要一秒钟,但作为精英拖延者的代表,我拒绝这样做。相反,当我喜欢一首歌的时候,我只需要点击喜欢按钮,然后永远把它留在那里。
偶尔,你会有给自己的生活带来一些秩序的冲动,这通常发生在凌晨 2 点。所以你坐下来,打开 Spotify ,随机播放你喜欢的歌曲,并试图一首接一首地将它们添加到播放列表中,结果却不知所措,中途退出,在网上订购食物。
在这里,我将向你展示如何不像我一样,利用机器学习为你带来优势。在这篇文章中,我们将涵盖监督和非监督的方法。在无监督的方法中,我们将使用歌曲特征来聚类音轨。在监督方法中,我们将使用现有的播放列表来训练一个模型,以了解曲目和播放列表之间的关系,并使用该模型将喜欢的曲目分类到这些播放列表之一。
我们还将介绍自动创建播放列表,以及使用 Spotify APIs 向播放列表添加曲目。如果你不熟悉 Spotify 认证和范围,我建议你看看我以前的文章。
数据准备:
- 获取喜欢的曲目并生成音频特征。
无监督方法:
- 使用 KNN 将喜欢的曲目分类成簇。
- 基于群集组创建播放列表并将曲目添加到播放列表。
监督方法:
- 获取现有的播放列表和曲目,并生成训练数据。
- 训练一个随机森林模型进行轨迹分类。
- 使用训练好的模型将喜欢的曲目分类到现有播放列表中。
数据准备:
我们可以通过 spotipy 包轻松访问 Spotify APIs。查看上面提到的文章,获取 Spotify client_id 和client_secret.
一旦我们准备好 Spotify 客户端,我们就可以通过客户端使用所有 Spotify APIs。在这里,我们可以使用current_user_saved_tracks() API 来获得一个喜欢的曲目列表。Spotify 的大部分列表 API 都是分页的,以限制 API 过载。在处理了分页逻辑之后,我们得到了我们喜欢的曲目的完整列表。
Spotify 存储了大量曲目元数据。这里我们只保存了曲目的id和name。
为了生成音频特征,最初我想下载所有曲目的音频波形,并使用类似**librosa** 的库来手动生成音频特征,但在浏览 Spotify API 文档时,我遇到了返回类似danceability, energy, key, loudness和 7 个其他音频特征的audio_features() API。

为了获得更好的准确性,我们应该用正确的函数对特征和每个特征进行规范化。例如,音量以分贝为单位,这是一个对数标度,因此最小-最大标度并不准确。对于这个项目,我没有标准化的功能。
我只为自动化使用了这些功能,但附加功能如地区/语言信息,轨道长度等。也可用于提高模型精度。
无监督方法:
现在我们有了轨迹数据,我们可以对轨迹进行聚类。但是在分组之前,我们需要确定应该生成多少个聚类(K)。我们可以使用弯头方法来完成。在这种方法中,用不同数量的 K 值来拟合模型。成本值是相对于 K 值绘制的,曲线断裂的地方被认为是最佳 K 值。这不是一个固定的规则,但它缩小了 K 范围,以便更好地进行聚类。

正如你在上面的图中所看到的,曲线在 K=3 附近断裂,但是直到 K=5 才开始饱和。我使用 K=5,并将我的曲目分成 5 个播放列表。
因为我使用了 11 个音频特征,所以很难在 2d 图上显示 11 维向量。因此,我使用 TSNE 来降低音频向量的维数,以便进行聚类分析。

在上面的图中,每个点代表一首曲目,每种颜色代表一个播放列表。既然我们已经将喜欢的曲目映射到组,我们可以使用 Spotify APIs 为每个组创建播放列表,并将各自的曲目添加到播放列表中。

这是自动创建的播放列表的屏幕截图,其中包含已整理的轨道。

监督方法:
无监督 KNN 方法简单快速,但也有问题。它假设所有的特性都是同等重要的,我们没有任何衡量标准来确定哪个特性比其他特性更重要。
在无监督的方法中,我们拟合了一个模型,根据音频特征对曲目进行分组,但我们能教会它去爱吗?表现情绪?大概不会。但我们肯定可以教它学习现有播放列表及其曲目之间的关系。
当我们已经有几个播放列表时,我们有一个目标变量。我们可以识别用户逻辑,并推断用户下意识地用来将某个音轨分类到播放列表的权重。当然,这里我们假设用户也下意识地使用了相同的音频特征。因此,相关功能是重要的,但我们将与我们现有的工作。
首先,我们需要准备训练数据,我们将在这些数据上训练我们的机器学习模型。
我选择了一些我的播放列表,其中有合理数量的曲目。我们拥有的数据越多越好。
这个数据结构与喜欢的曲目数据相同,但是这里每个曲目都被映射到一个播放列表。

有了训练数据,我在 70%的数据上使用默认配置训练了一个随机森林分类器。这里可以增加max_depth以获得更好的准确性,但这可能会导致模型过度适应训练数据。
在对剩余的 30%的数据进行测试时,模型精度达到了 67%左右,考虑到有限的数据集和最小的超参数调整,这是相当不错的。
现在,我们可以使用这个经过训练的模型来预测和分配喜欢的曲目的播放列表,并使用我们在上一节中使用的相同 API 来将曲目添加到播放列表。

脚注:
这里讨论的方法并不完美,仅仅是概念上的证明。通过使用相关特征、适当的数据标准化、测试不同的分类器和使用适当的超参数,我们可以极大地提高模型的准确性。
直到下一次…
从 AWS Glue 迁移到 BigQuery 进行 ETL

安德鲁·鲁伊斯在 Unsplash 上的照片
我用大量 AWS 胶水记录了我们的旅程【https://tymlez-au.medium.com/
为什么要迁移?
自从几年前在 AWS re:invent 上看到关于 AWS Glue 的主题演讲,我就对它感兴趣,它似乎在“你知道的工具”和“没有设置”之间取得了很好的平衡,有一段时间它工作得很好。
它在某种程度上填补了这些工具通常具有的无服务器编排的空白&。
不幸的是,为了做到这一点,它必须清除这些工具所具有的一些 USP,这样做会造成复杂性。
AWS Glue 为我们的批量摄取提供了很好的服务,但很快就发现,尽管 AWS Kinesis 是一个受支持的输入,但它在当前版本中无法扩展到实时摄取。
我们需要一种不那么痛苦的替代解决方案。
我们的胶水管道

TYMLEZ 的 AWS 仪表数据设置(图片由作者提供)
简而言之,glue pipeline 接收批量数据,这些数据是我们的计量技术和智能仪表读数转储到 S3 的(稍后将详细介绍这一点,因为这很重要),并通过各种脚本运行这些数据,以将数据转换为我们需要的数据。
具体设置可以在这里了解更多:https://tymlez-au . medium . com/smart-meter-data-ETL-systems-at-tymlez-5643 e 232 dfbb
编排是通过 AWS Glue 触发器串行完成的(例如,当这一步完成时,运行下一步),不幸的是,这意味着每次都需要部署来使用它们,这大大减慢了开发过程,并且由于其他问题(这里讨论:https://tymlez-au . medium . com/AWS-Glue-is-a-mess-886 cc 1d 13 ca 9)没有简单的方法来设置一个简单的本地环境来手动运行这些项目。
简而言之——它制造的麻烦比解决的问题还多。
为什么是 GCP?
很简单——因为 BigQuery。当谈到轻松处理大量数据时,市场上没有像 Google BigQuery 这样的产品。
归结起来就是简单,我们的工作流在从设备读取时非常标准,设备每 x 分钟发出一次 JSON——我们需要获取该 JSON 并将其写入我们的数据仓库。
AWS 流程:
设备->*处理器** " - > Kinesis - > AWS 胶表- >雅典娜
让 Kinesis 与 AWS Glue Tables 一起工作几乎是不可能的(试图在本地开发这一点甚至更难)。
GCP 流量:
设备->*处理器**->big query
*处理器是某种形式的处理能力,如 EC2、Lambda、云函数等
GCP 流只是工作,它工作得如此简单,以至于我可以在这里包含我用来为开发摄取的代码:
一旦数据进入 BigQuery,我们只需使用一个标准的 ELT 模式,根据需要将数据分成一堆新表,并设置它们通过 cron 运行。
我们的管道现在看起来像这样:

多重云设置(图片由作者提供)
大型 PySpark 脚本被直接处理 BigQuery 中导入数据的 SQL 数据所取代。
CREATE OR REPLACE TABLE data_clean AS ( SELECT x FROM data…)CREATE OR REPLACE TABLE data_business AS ( SELECT x FROM data_clean…)CREATE OR REPLACE TABLE prediction_input AS ( SELECT x FROM data_business…)CREATE OR REPLACE MODEL usage_forecast OPTIONS(model_type=’ARIMA_PLUS’...
注意,作为使用 SQL 的这一步骤的一部分,我们还可以直接从 BigQuery 中训练一个模型?我们不仅将数据 ETL 到 BI 和其他报告所需的表格中,还生成了一个 ARIMA+模型,用于根据大量不同的输入预测数据。此外,我们可以单独使用相同的数据来训练 AutoML 模型进行测试。
ML 管道— Sagemaker vs Vertex。人工智能
Sagemaker 很棒,它允许通过 SAGEMAKER.invoke_endpoint 请求进行真正干净的模型部署。它可以处理很多事情,比如为训练好的模型轻松地创建端点,以便进行预测。
GCP 有顶点。AI 与此类似,尽管它不太成熟,不支持 AutoML 模型的端点创建,但目前更简单的是简单地批处理日期预测模型,并使它们在 BigQuery 或其他表中可用,然后可以通过静态 API 查询。
这也可以通过 Cron 非常简单地完成,方法是基于以前的日期批量创建日期预测输入,并针对重新训练的模型运行批量预测。
不需要步进机、触发器或任何东西——如果一天内需要更新,这也是很好的幂等方法。
数据可视化— Quicksight 与 Data Studio
AWS 有 Quicksight 作为它的 BI 工具,但是我从来没有用过它,它看起来总是限制太多。有时,您希望快速搭建一个仪表盘,从数据中获得一些高管或董事会层面的见解,而无需花费大量时间。
谷歌有 Data Studio,在我看来,这是一个非常好的产品,感觉就像是谷歌产品套件的一部分。
我可以创建一个数据集,并与我们业务中的任何人共享,以便创建特定的 BI 仪表板。

然后,谁可以拖放图表并将其与易于理解的数据表示联系起来:

我们的数据工作室设置(图片由作者提供)
我特别喜欢 Blend Data 接口,它抽象了许多需要完成的底层连接,以便从系统中获取大部分数据,并允许您轻松地连接几个表以获得所需的结果:

混合数据界面(图片由作者提供)
结论
当我们开始深入挖掘它的流功能时,我们的 AWS Glue 之旅有点艰难,如此多的层的编排增加了我们没有预料到的巨大开销,虽然大多数都是在 AWS 产品套件中处理的,但将我们的管道切换到 GCP 和 BigQuery 有太多的好处不容忽视。
下一步是通过使用 Cloud Composer (Airflow)来协调每个表的创建,并提供一个监控仪表板来帮助我们检测故障并采取行动,从而完成我们的部署。
更新—2021 年 9 月 13 日
我要说的是,在我发表上一篇文章后,AWS 与我取得了联系,我与 AWS Glue 产品团队通了电话,用他们的话说,我已经“触及了几乎所有可能的尖锐边缘”(这似乎是我的一个持续主题——也许我应该转行做 QA 工程师?),他们有一系列令人兴奋的更新正在进行中,应该可以解决其中的许多问题,我期待着看到它们,因为我仍然坚持认为,对于不太复杂的数据批处理,AWS Glue 是一个具有很高可见性的优秀产品。
关于作者

首席技术官丹·沃伊斯
Dan 负责 TYMLEZ 的技术战略、架构和开发。
一位从业超过 20 年的创业老手,擅长建立高绩效开发团队,并带领他们成功地为澳大利亚和英国的一些大公司提供解决方案。
从 Heroku Postgres 迁移到亚马逊 RDS
完成如何将大型 PostgresSQL 数据库从 Heroku 迁移到 RDS 的分步指南

首先是背景, Heroku Postgres 数据库和 Amazon RDS for PostgreSQL 有什么区别,为什么还要费事迁移?
每个用过 Heroku 的人都知道它的插件简单易用。它们让您在几分钟内就可以开始使用数据库、日志监控或十几个其他有用的服务。这是一个完美的组合,可以让事情顺利进行,或者只是消除管理外部服务的开销。问题是什么时候使用 Heroku 附加组件是不合适的,什么时候应该转而使用你能更好控制的服务?
在高级别上 Heroku Postgres 数据库和 Amazon RDS for PostgreSQL 都是可伸缩的按需 PostgreSQL 数据库。
当我们关注控制、可扩展性和生态系统方面的细节时,差异是显而易见的。 Heroku Postgres 附加软件提供通用的“等级”,您可以根据自己的需要选择等级。借助 RDS,您可以更精细地控制实例类型(内存、CPU、容量)、参数配置、冗余级别和成本。
这里只是一些例子:
- 成本节约:如果您认为您将使用同一类型的数据库至少一年,RDS 可以选择购买专用实例。这将立即削减你一半的成本。
- 配置灵活性:借助 RDS,您可以控制配置和数据库管理,并获得相同(如果不是更好)的正常运行时间、成本和监控保证。
- 还可以选择不同类型的复制实例类型;其中 Heroku 需要与主数据库的类型相同。

从 Heroku PostgresSQL 迁移到 Amazon RDS PostgresSQL
好的。您已经在 Heroku Postgres 工作了一段时间,现在决定迁移到 RDS。
当您处理一个非常大的数据库(几十到几百 GB)时,迁移可能是一项非常具有挑战性的任务。您在想,我如何才能以最短的停机时间执行迁移,并保持现有的安全性。为此,我将迁移计划分为 3 个部分:安全性考虑、迁移优化&速度和迁移步骤。
1.安全考虑
在 AWS 生态系统中,您通过 VPN 和 VPN 安全组来控制访问。
将 Heroku 应用程序连接到 RDS 实例时,您需要使用公共 URL,可能还需要 IP 限制。通常,如果您在 AWS 中工作,您希望避免使用公共 URL 进行数据库连接,但是在这种情况下,这可能是唯一的选择。那么,我们如何提高安全性:
- 将 Postgresql 连接限制到 Heroku 的私有 IP 只有当你有 Heroku 私有空间或者你购买了一个专用的 IP 插件(那些通常是按请求/带宽付费的)时,这个选项才可行。这两种选择都可能变得非常昂贵,而且会抵消迁移到 RDS 所节省的成本。
- 使用安全组
限制 RDS 入站流量无论您是否拥有私有 IP,都应该将 RDS 实例的入站连接限制为 PostgreSQL TCP 流量。
一般使用0.0.0.0/0或您的私有 IP(如果适用)

在 RDS 安全组中限制入站流量
- 在 AWS 中:RDS 实例上的强制实例 SSL 连接-
a)更改用于强制 SSL 的 RDS 实例参数组的默认配置。你将需要改变参数rds.force_ssl = 1
我的建议是复制默认的参数组,并使用一个新的修改过的参数组。
b)下载您的 RDS 实例所在地区的 SSL 证书。在写的时候,你可以从这里的下载

rds 参数组中的 rds.enforce_ssl 参数
- 在 Heroku 上:使用 SSL 证书连接到新的 RDS 实例- a) 在你的 app 中保存 RDS SSL 证书并推送到 Heroku
b)在DATABASE_URL中引用证书。假设您在项目中的config/amazon-rds-ca-cert.pem保存了证书,那么将 URL 查询参数sslca作为DATABASE_URL=postgres://username:password@hoistname/dbname?**sslca=config/amazon-rds-ca-cert.pem**添加到连接 URL 就很简单了
2。迁移优化&速度
使用 EC2 实例而不是本地机器/本地机器来执行整个迁移。在大多数情况下,您只需要一个非常低层的实例,其中只有连接的存储可能需要更大,具体取决于您的备份大小。您将在那里执行以下步骤:下载 Heroku Postgres 备份,并使用并行作业恢复备份(在迁移过程&计划中概述)。如果你正在处理一个非常大的数据库备份,这会给你成倍增加备份和pg_restore命令的下载速度。此外,您将在 AWS VPC 内工作,这将使事情更加安全。您将避免本地机器很可能出现的超时问题。
在 EC2 实例中,您可能必须安装postgres,它也会安装pg_restore。这通常是通过amazon-linux-extras命令完成的。更多信息点击此处。
给 Mac 用户的 EC2 提示:在连接到 EC2 实例之前,将行
ServerAliveInterval 20添加到您的~/.ssh/config文件中,以保持您的终端会话活动。
不要使用浏览器控制台会话来连接和执行步骤。它会超时。
3.迁移流程和计划:
第一阶段:
- 将 Heroku 应用程序置于维护模式
heroku maintanance:on -a {app-name} - 缩小/关闭所有后台作业动态
- 监控 Heroku 应用程序的任何活动和你可能错过的任何事情。
- 监控你的应用程序的流量
二期:
- 启动 Heroku 数据库备份
heroku pg:backups:capture -a {app-name} - 当最新的备份准备好了,你需要下载它。它将有一个类似
b03的名字
- 列出备份的公共 URL
heroku pg:backups public-url {backup-name} --app {app-name} - 开始下载备份到一个文件
my-backup.dump。 理想情况下在 EC2 实例上。
curl -o my-backup.dump “{public-url}”
*不要忘记在*public-url*两边加上双引号】
3.将数据库转储导入新的 RDS 数据库实例。 理想情况下使用 EC2 实例 pg_restore --verbose --clean --no-acl --no-owner -h {rds-instance-url} -U {db-user} -d {db-name} --jobs={number-of-jobs} {my-backup.dump}
这里的 PRO 提示是使用选项
**--jobs**来表示工作数量。这将使用多个线程并行运行恢复,使恢复过程成倍加快。更多信息请点击:https://www.postgresql.org/docs/9.2/app-pgrestore.html
4.步骤 5(可选):用 GUI 或命令行连接到数据库,并进行健全性检查。比较一些表的大小以及是否创建了索引。(我最喜欢的 PSQL 免费 GUI 是 Beekeeper Studio )
5.最后一步/不可逆 —更改 Heroku 应用配置中的DAATABASE_URL。这一步是不可逆的,因为您必须删除 Heroku Postgresql 加载项,这会终止当前数据库。这是一个不幸的过程。参见我在“最后的思考”部分的观点。
将数据库 URL 更改为您的 AWS RDS URL 。
DATABASE_URL=postgres://username:password@hostname/dbname?sslca=config/amazon-rds-ca-cert.pem

删除 Heroku Postgres 附加组件
6.启用所有后台作业动态
7.将应用从维护模式中移除。
heroku maintanance:off -a {app-name}
最后的想法
- Heroku Postgres 附加软件依赖于一个 Heroku 应用程序。
不能单独使用。这是一个问题,因为当 HEROKU Postgres 附加组件处于活动状态时,无法更改DATABASE_URL。为了更改DATABASE_URL,需要删除附加组件。将数据库复制到另一个应用程序并不是一个正确的选择,并且/或者表现得像一个全新的pg_restore。如果在不移除 Postgres 插件的情况下更改DATABASE_URL是可能的,那么当您需要返回到之前的 Heroku 数据库时,这将是回溯情形的理想选择。 - 执行几次试运行:
在不改变实际**DATABASE_URL**的情况下执行整个过程(跳过迁移阶段 II,步骤 5) 。通过这样做,您可以在迁移笔记中概述迁移。这也将帮助你确定几件事:
- 完成迁移所需的总时间—因此,您可以计划一个预定的维护窗口。
- 在这个过程中你错过了什么吗?
- 监视 EC2 实例度量和 RDS 度量。
在大多数情况下,CPU 可用内存受到的影响最大。您在pg_restore中使用的选项--jobs中的作业越多,受影响的 CPU 就越多。尽管有一点,更多的作业不会更快地完成迁移。最有可能的情况是,一些作业(在较大的表上工作)将占用与其他所有作业一样多的时间,并且整个迁移将依赖于它们来完成。
- AWS 服务器迁移服务
我做 Heroku 到 RDS 数据库迁移的第一直觉是,让我们使用 AWS 服务器迁移服务。在迁移过程中,这个工具会给我 99.9%的正常运行时间,直到DATABASE_URL发生变化。,我艰难地发现(在我设置好一切之后),服务迁移服务依赖于逻辑复制,而逻辑复制不能使用 Heroku Postgres 数据库作为源进行配置。有关 AWS 迁移服务和 Postgresql 迁移的更多信息—https://AWS . Amazon . com/blogs/database/migrating-Postgresql-from-on-premises-or-Amazon-ec2-to-Amazon-rds-using-logical-replication/
4.将数据加载到 PostgreSQL 数据库实例的有用 AWS 提示这里是。其中一些很有意义,但对于我的迁移来说,它们并没有太大的区别,而且这会增加整个迁移过程,因为您必须将这些更改应用到 RDS 实例,然后在完成迁移后恢复它们。在你的演习中尝试一些是理想的。
最后也是非常重要的 PRO 提示是在与 Heroku 实例相同的****区域创建 RDS 实例。这将大大减少与数据库通信的延迟。在为 heroku US 主持写作的时候,那是**
**us-east-1**地区。您可以在此 处查看 设置的准确区域。**
希望这篇文章能给你下一次 Heroku 到 RDS Postgres 迁移带来一些好的想法。我知道有许多移动部分,但是如果您一步一步来,并执行一个模拟场景,真正的迁移将会顺利进行。如果您有任何改进流程的建议,请告诉我。
联系人
我是一名软件、数据和机器学习顾问,通过了 AWS 机器学习 & AWS 解决方案架构师认证——专业。如果您的下一个机器学习项目需要帮助,请联系我们。
要了解我最新的文章和项目,请在 Medium 上关注我。
其他联系人:
Kafka 跨集群复制的迁移工具和技巧:MirrorMaker
从其他复制工具迁移到 MirrorMaker 的开源工具

这篇文章是关于从其他跨集群 Kafka 复制工具迁移到新的 MirrorMaker (或“MirrorMaker 2”)的工具和技巧
新的 MirrorMaker 有几个优点。仅举几个例子:
问题
最近,来自社区的用户已经从 Confluent Replicator (一个企业商业“跨集群”复制工具)迁移到 MirrorMaker 和,他们面临以下问题:
在相同主题上启动 MirrorMaker 时,已由汇合复制程序复制的消息将再次被复制。这不应该发生,因为消息在目标集群上被复制。
由于合流复制器和 MirrorMaker 都是作为 Kafka Connect 的“连接器”构建的,或者更具体地说是作为源连接器构建的,因此它们维护一个内部“偏移主题”来跟踪消费者偏移,并在合流复制器或 MirrorMaker 重新启动时加载。
对于合流复制器,偏移主题默认为“连接-偏移”。然而,在 MirrorMaker 中,偏移主题被命名为"mm2-offsets . primary . internal "。
另一个区别是内容格式。对于汇合复制程序,邮件内容如下所示:
[“replicator”,{“topic”:”foo”,”partition”:2}] {“offset”:1}
(“复制器”是合流复制器的默认消费群名称)
对于 MirrorMaker,消息上下文如下所示:
["MirrorSourceConnector",{"cluster":"primary","partition":1,"topic":"foo”}] {"offset":10}
(“MirrorSourceConnector”是 MirrorMaker 的默认消费群名称)
解决
使用不同偏移主题调整开源 MirrorMaker 并适应不同的消息格式听起来可能是可行的。但从实践来看,这需要对 MirrorMaker 有深入的了解和代码更改。
正如社区帖子所讨论的,这里有一个非侵入性的、简洁的解决方案:
For each topic and partition, whenever a new message is replicated a new message with same key but increased offset is produced to the connect-offsets topic, convert the key of this message to Mirror Maker format and produce it in the internal "offset topic" of Mirror Maker.Key : ["replicator-group",{"topic":"TEST","partition":0}]
Value: {"offset":24}After posting the message, once the mirror maker is restarted, it will read the internal topic to get the latest offset of that topic for which the message has to be replicated and this way we can ensure no duplicate messages are replicated.Key: ["mm-group",{"cluster":"primary","partition":0,"topic":"TEST"}]
Value: {"offset":24}
以下代码片段通过利用本机Kafka-console-consumer . sh和Kafka-console-producer . sh(Apache Kafka的一部分)实现了上述想法,带来了几个好处:
- 没有第三方依赖
- 与编程语言和操作系统无关
- 更好的与卡夫卡融合(如 卡夫卡-控制台- *)。sh 应始终工作
- 易于执行和监控,例如玉米作业
自述在这里:https://github.com/ning2008wisc/mm2-migration
结论
社区主题中提出的想法简洁明了,可以应用于其他类似的涉及 Apache Kafka 的迁移场景。在未来,更多有用的工具和技巧将在这里讨论。敬请关注!
并不是所有的矢量数据库都是相同的
Milvus、Pinecone、Vespa、Weaviate、Vald、GSI 和 Qdrant 的详细比较
在撰写这篇博文的过程中,我有幸与所有搜索引擎的主要开发人员/领导进行了交流:鲍勃·范·路易特和艾蒂安·迪洛克(Weaviate)、格雷格·科岗(松果)、帕特·拉塞尔、乔治·威廉姆斯(GSI 科技公司)、菲利普·哈尔特迈尔(Milvus)、乔·克里斯蒂安·贝尔古姆(Vespa)、汤川纪一郎(Vald)和安德烈·扎亚尼(Qdrant)
这个博客已经在 https://news.ycombinator.com/item?id=28727816 的HN上被讨论过
更新:矢量播客推出!
更新 2:这篇博客构成了 Deepset 的 NLP meetup 上以下演示的基础:
向量数据库的前景:在 Deepset 的 NLP 会议上与Max Irwin共同展示
更新 3:在 Sease 组织的伦敦 IR Meetup 上做了一个新的演示,讨论了矢量搜索的参与者:
向量搜索中的玩家:算法、软件和用例。在由 Sease 组织的伦敦 IR Meetup 上展示

我们已经接近在搜索引擎体验的基础层面上涉及机器学习:在多维多模态空间中编码对象。这不同于传统的关键字查找(即使增加了同义词/语义),在很多方面都很有趣:
- 对象级的集合级相似性。您可以使用相似性函数(距离度量)而不是稀疏关键字查找来查找查询的邻居。在带有分片的 BM25/TF-IDF 方法中,您将拥有来自不兼容的分片级集合的文档分数(除非您设置了全局更新的 IDF 缓存)。
- 将几何相似性的概念作为语义的一个组成部分,而不仅仅是原始对象的特定属性(在文本的情况下——它的关键字/术语)。
- 多模态:对任何对象进行编码——音频、视频、图像、文本、基因组、软件病毒,一些复杂的对象(比如代码,你有一个编码器和一个相似性度量标准——并在这些对象之间进行无缝搜索。
同时,关键字可以以互补的方式与相似性搜索相结合,特别是在你面临长尾零命中问题的情况下(这可能相当大,就像在电子商务领域)。
这篇博客文章独立地试图强调 7 个向量数据库之间的共性和差异,每个向量数据库都提供商业云支持。七分之五的人提供他们的代码作为你自己主机的开源。帖子不包括神经搜索框架(像纪娜。AI 、 FAISS 或者 deepset 的草堆),这些都配得上自己的博文。此外,它没有专注于大型云供应商的垂直搜索引擎,如必应或谷歌的矢量搜索引擎。算法基准测试超出了这个范围,因为你可以随时求助于https://github.com/erikbern/ann-benchmarks来找到关于单个算法性能和权衡的细节。如果你对在 Apache Lucene 、 Solr 、 Elasticsearch 和 OpenSearch 中的矢量搜索感兴趣——我在以前的博客文章中已经提到过。
我冒昧地从以下五个角度来考虑每个搜索引擎:
- 价值主张。让整个向量搜索引擎脱颖而出的独特之处是什么?
- 类型。本引擎通用类型:向量数据库,大数据平台。托管/自托管。
- 建筑。高级系统架构,包括分片、插件、可伸缩性、硬件细节(如果有的话)。
- 算法。该搜索引擎采用了什么算法来进行相似性/向量搜索,它提供了什么独特的功能?
- 代码:是开源还是闭源?
每个搜索引擎都附有元数据:
🌍链接到描述该技术的主网页
💡类型:自托管和/或托管
🤖源代码的代码链接(如果有)
📹与这个数据库的创建者的矢量播客插曲
米尔乌斯
🌍链接:【https://milvus.io/
💡类型:自托管矢量数据库
🤖代码:开源
📹矢量播客:https://www.youtube.com/watch?v=fHu8b-EzOzU
- 价值主张:关注整个搜索引擎的可扩展性:如何高效地索引和重新索引矢量数据;如何缩放搜索部分?独特的价值是能够使用多种人工神经网络算法对数据进行索引,以比较它们在您的用例中的性能。
- 架构:

Milvus 实现了四层:接入层、协调器服务、工作节点和存储。这些层是独立的,以实现更好的可伸缩性和灾难恢复
3.算法:允许多个基于 ANN 算法的索引:FAISS、airy、HNSW、RNSG。
松果
💡类型:托管矢量数据库
🤖代码:关闭源代码
📹矢量播客:https://www.youtube.com/watch?v=jT3i7NLwJ8w
- 价值主张:全面管理的矢量数据库,支持您的非结构化搜索引擎之旅。最近的 2.0 版本带来了单阶段过滤功能:在一个查询中搜索您的对象(毛衣)并根据元数据(颜色、尺寸、价格、可用性)进行过滤。
- 建筑:

Pinecone 是一个托管矢量数据库,采用 Kafka 进行流处理,采用 Kubernetes 集群实现高可用性以及 blob 存储(矢量和元数据的真实来源,实现容错和高可用性)
3.算法:FAISS 供电的精确 KNN;由专有算法驱动的人工神经网络。支持所有主要的距离度量:余弦(默认)、点积和欧几里德。
Vespa
💡类型:托管/自托管矢量数据库
🤖代码:开源
📹矢量播客:【https://www.youtube.com/watch?v=UxEdoXtA9oM
- 价值主张:引用官方文档:“Vespa 是针对大型数据集的低延迟计算引擎。它存储和索引您的数据,以便可以在服务时对数据进行查询、选择和处理。功能可以通过托管在 Vespa 中的应用组件进行定制和扩展。”Vespa 提供面向深度学习的深度数据结构,如数据科学,例如张量。
- 架构:

Vespa 的鸟瞰图
3.算法 : HNSW(针对实时 CRUD 和元数据过滤进行了修改);一套重排序和密集检索方法。相关视频。
变弱
🌍链接:https://www.semi.technology/developers/weaviate/current/
💡类型:托管/自托管矢量数据库
🤖代码:开源
📹矢量播客:https://www.youtube.com/watch?v=iHC5oeAN29o
- 价值主张:用 Graphql-like 接口支持表达性查询语法。这允许您在丰富的实体数据上运行探索性数据科学查询。该产品最重要的元素是矢量搜索、对象存储和用于布尔关键字搜索的倒排索引的组合,以避免数据漂移和存储与对象/倒排索引分开的矢量数据的不同数据库之间的延迟。 Wow-effect :有一个令人印象深刻的问答组件——可以带来一个 Wow!-将一项新的搜索功能作为现有产品或新产品的一部分进行演示。
- 建筑:
这是 Weaviate 的系统级架构图。它显示了索引组成:您可以存储向量、对象和倒排索引数据,以混合和匹配适合您的用例的搜索功能。支持不同任务的模块,如问答。

系统级概述

使用虚拟碎片将碎片分发到节点上(受卡珊德拉碎片的启发)
3.算法:定制实现的 HNSW,调整到可扩展,并支持完整的 CRUD。系统支持插件 ANN 算法,只要能做 CRUD。
瓦尔德
💡类型:自托管矢量搜索引擎
🤖代码:开源
- 价值主张 : Vald 在十亿向量规模上使用,提供云原生架构。来自官方文档:“Vald 具有自动矢量索引和索引备份,以及从数十亿特征矢量数据中进行搜索的水平缩放。”该系统还允许插入带有出口过滤器的自定义重新排序/过滤算法。额外收获:可以直接安装在 macOS 上。
- 架构:

Vald 运行在 Kubernetes 集群之上,以利用其 HPA 和分布式功能
3.算法:基于最快的算法: NGT ,比很多强算法都快,比如 Scann,HNSW。
用于弹性搜索和开放搜索的 GSI APU 板
🌍链接:https://www.gsitechnology.com/APU
💡类型:矢量搜索硬件后端为您的elastic search/open search
🤖代码:关闭源代码
📹矢量播客:https://www.youtube.com/watch?v=EerdWRPuqd4
- 价值主张:十亿级搜索引擎后端,将你的elastic search/open search能力扩展到相似性搜索。你可以实现一个能量和时间有效的多模态搜索,增加你的关键词检索。它以内部 APU 板的形式提供,并通过插件与你的 Elasticsearch / OpenSearch 部署相连。
- 建筑:

GSI APU 供电的 Elasticsearch 架构的架构(截图由 GSI 科技提供)

APU 板特征
3。算法:汉明空间局部保持神经哈希。阅读更多在这里或观看这个视频。
Qdrant
💡类型:托管/自托管矢量搜索引擎和数据库
🤖代码:开源
📹矢量播客:
https://www.youtube.com/watch?v=kVCIDTmiZyk(用户)
https://www.youtube.com/watch?v=AU0O_6-EY6s(qdr ant 的工程师)
- 价值主张:具有扩展过滤支持的向量相似度引擎。Qdrant 完全用 Rust 语言开发,实现了动态查询规划和有效负载数据索引。Vector payload 支持多种数据类型和查询条件,包括字符串匹配、数值范围、地理位置等。有效负载过滤条件允许您构建几乎任何定制的业务逻辑,这些逻辑应该在相似性匹配的基础上工作。
- 建筑:

集合级架构
3.算法 : 自定义 HNSW 在 Rust 中实现。
这篇博客文章给了你一个当今市场上正在开发的 7 个矢量搜索引擎的简明摘要。随着我对这些系统的进一步研究,我将增加更深入研究的链接,所以回到这个帖子或者简单地订阅来获得及时更新是个好主意。
模仿科学家:走向自动化科学发现
思想和理论
如何通过三个简单的步骤构建科学发现引擎

介绍
在本文中,我演示了如何构建一个简单的自动化科学发现引擎。 检查演示完毕 。
当前的科学实践受到我们认知和社会学局限的束缚。机器比我们更快、更高效、更耐用。最重要的是,机器不受人类偏见的影响。将人工智能融入科学过程可能会引发下一场科学革命。
但是,有可能实现科学自动化吗?尽管今天它很微弱,但许多人认为它不仅是可能的,而且是我们力所能及的。艾伦·图灵研究所提出了一个挑战,那就是为科学发现建造一个与顶级人类科学家没有区别的引擎。这个想法是,一个明确的使命声明,以获得诺贝尔奖为目标,是加速挑战的最佳方式。
机器是否会与顶级人类科学家无法区分只是拼图的一部分。著名的中文房间论点认为,仅仅使用句法规则来操纵符号不会产生真正的理解。类似的担忧也出现在人类对科学过程的理解上( Gigerenzer 2004 , Altman 1994 )。然而,通过图灵测试的变化是必要的第一步。
指定研究区域
最初的提议集中在生物系统上。我认为不是生物系统,而是社会系统最不适合自动化。他们代表了最复杂的,认识论上错综复杂的研究领域。由于社会系统过于复杂,人类无法进行推理,在经验假设的广阔空间中进行自动搜索是向前迈出的关键一步。解决社会科学面临的这一挑战将为其他学科的自动化提供有力的证据。
关注社会系统的另一个原因是社会科学研究者所展示的成熟程度。社会研究的重要性不可估量。这将推动社会科学家处于可靠、诚实和透明的前沿( Camerer et al .,2018 , Open Science Collaboration,2015 ,【常和李,2015 )。达到那样的学术水平是一项艰巨的挑战。
这里展示的原型集中在民主和经济增长的问题上。以这种方式缩小关注范围很有吸引力,原因有二。首先,关于该主题的文献数量庞大且高度确定( Acemoglu、Naidu、Restrepo 和 Robinson,2019 、波苏埃洛、Slipowitz 和 Vuletin,2016 )。第二,政治经济宏观系统是高阶系统。这一层次的研究是方法论复杂性、想象力和创造力的巅峰(曼昆 1995 年,第 307 页,林道尔和普里切特,2002 年,第 18 页)。
自动化科学发现的三大支柱
获取大数据
我们生活在大数据时代。大数据是灵活生成假设的沃土。这种灵活性是实现人类水平的科学发现能力的关键组成部分( Gelman 和 Loken,2014 、 Simmons、Nelson 和 Simonsohn,2011 )。
在这项研究中,我使用了 WDI 数据库(CC BY 4.0)。世界发展指标是最大的国家级数据库。它包含 217 个经济体和 40 多个国家集团的 1400 个时间序列指标。
搜索假设空间
我们能让我们的探索范围更大吗?答案是肯定的,通过应用成千上万的模型而不是单一的模型。每个模型代表一个假设。人类的认知限制使我们无法同时处理多个假设。AI 的优势在于它赋予了机器无需显式编程就能学习的能力( Gründler 和 Krieger,2016 )。这确保了发现的过程是全面的,没有人为的偏见。
在假设空间上搜索也需要一个快速和可扩展的算法。在这项研究中,我依赖于一个现代人工智能框架— 具有线性激活函数和时空连续体约束的感知器。
公式化理论
人们应该意识到强烈的批评意见,即当前的大数据分析是由理论调查驱动的( Monroe,Pan,Roberts,Sen 和 Sinclair,2015 )。对纯统计估计背后的潜在机制进行理论化是一个安全网。一个单独的估计或一个单独的理论可能被认为是弱证据。然而,当很好地结合在一起时,它们会相互加强。这种方法是人机共生的第一块基石,不应该被轻视。因此,所有的估计都必须有坚实的理论支持。
结论
我们正处于人工智能革命的中期,我们是分裂的。有些人认为这是一种生存威胁,是文明的夕阳,在它的照耀下,人类策划了自己的灭亡。还有一些人认为这个特性已经存在了( Bem,2011 )。这个原型是乐观主义者向前迈出的一小步,也是“方法论恶霸”的又一场败仗( Singal,2018) 。
参考
- Acemoglu,d .,Naidu,s .,Restrepo,p .,& Robinson,J. A. (2019 年)。民主确实会导致增长。政治经济学杂志, 127 期 (1),第 47–100 页。
- 奥尔特曼博士(1994 年)。糟糕的医学研究丑闻。
- 贝姆博士(2011 年)。感知未来:对认知和情感异常追溯影响的实验证据。《人格与社会心理学杂志》, 100 期 (3),407 期。
- 卡默勒,C. F .,德雷伯,a .,霍尔茨迈斯特,f .,何,T. H .,胡贝尔,j .,约翰内松,m .,…,吴,H. (2018)。评估 2010 年至 2015 年间自然和科学中社会科学实验的可复制性。自然人类行为, 2 (9),637–644。
- 张阿昌,李,p(2015)。经济学研究可复制吗?来自 13 种期刊的 60 篇发表的论文说“通常不会”。
- Gelman,a .,& Loken,E. (2014 年)。科学中的统计危机:数据依赖分析——一个“分叉路径的花园”——解释了为什么许多具有统计意义的比较站不住脚。美国科学家, 102 (6),460–466。
- Gigerenzer,G. (2004 年)。愚蠢的统计数据。《社会经济学杂志》, 33 (5),587–606 页。
- k . gründler 和 t . Krieger(2016 年)。民主与增长:来自机器学习指标的证据。欧洲政治经济学杂志, 45 ,85–107。
- Hanck,c .,Arnold,m .,Gerber,a .,& Schmelzer,M. (2019)。与 r杜伊斯堡-埃森大学一起介绍计量经济学。
- 北野,H. (2021)。诺贝尔图灵挑战:创造科学发现的引擎。 npj 系统生物学与应用, 7 (1),1–12。
- 林道尔博士、普里切特博士、罗德里克博士和埃考斯博士(2002 年)。有什么大想法?第三代经济增长政策[附评论]。经济学家, 3 (1),1–39。
- N. G .曼昆、E. S .费尔普斯和 P. M .罗默(1995 年)。国家的发展。布鲁金斯经济活动论文, 1995 (1),275–326 页。
- Monroe,B. L .,Pan,j .,Roberts,M. E .,Sen,m .,和 Sinclair,B. (2015)。不要!形式理论、因果推理、大数据在政治学中并不是相互矛盾的趋势。 PS:政治学&政治学, 48 (1),71–74。
- j . singal(2018 年)。心理学内部的“方法论恐怖主义”争论。伤口。十月十二日。https://www . the cut . com/2016/10/inside-psychologys-methodology-terrorism-debate . html
- j .波苏埃洛、a .斯利波维茨和 g .武莱廷(2016 年)。民主不会导致增长:内生性论点的重要性。
- 开放科学合作。(2015).评估心理科学的再现性。理科, 349 (6251)。
- 西蒙斯,J. P .,尼尔森,L. D .,&西蒙森,U. (2011 年)。假阳性心理学:数据收集和分析中未公开的灵活性允许呈现任何有意义的东西。心理科学, 22 (11),1359–1366。
- 《图灵人工智能科学家大挑战》https://www . Turing . AC . uk/research/research-projects/Turing-AI-scientist-grand-challenge
- 世界银行,世界发展指标。https://data topics . world bank . org/world-development-indicators/
有头脑的数据科学家:创建一个无偏见的模型

意识到认知偏差
如果数据科学家不仔细考虑认知偏差,他们可能容易受到他们建立的模型中固有缺陷的影响。这些偏见可能会导致对变量之间的因果关系和联系的错误结论,从而导致不正确的预测和判断。对于任何数据科学家来说,了解认知偏差都是非常重要的,这样你就可以确保你的模型尽可能地公正!
认知偏差可能源于多种原因,但思维捷径可能起着重要作用。虽然这偶尔会令人惊讶地正确,但它可能会导致不正确的想法。随着年龄的增长,认知灵活性可以减少认知偏差。年龄和人类思维的认知灵活性(或缺乏)是这些偏见的其他原因。当一个人在做决定时必须考虑每一个可能的选择时,认知偏差通常更容易发生。
让我们来看看其中的一些。
1.确认偏差
确认偏差是一种心理现象,在这种心理现象中,人们寻找、解释和记忆证实他们先入为主的想法和态度的信息。确认偏差是一个数据科学问题,因为数据科学家必须愿意用数据测试假设,以便在他们分析的数据中找到模式或真理。它可能导致数据科学家确认数据样本,创建偏向于某一组信念或价值观的数据模型,并不准确地解释数据。这种数据偏差可能会导致数据科学家得出无效的结论,这可能具有潜在的危险。
数据科学家在创建数据模型时应始终保持公正,重要的是找出矛盾的数据,以测试假设,避免数据科学过程中的确认偏差,并为数据消费者创建有用的数据产品。
为了减少数据科学过程中的确认偏差,数据科学家必须创建专注于数据本身而不是他们个人信念的数据模型。保持不偏不倚很重要,这样才能生产出提供价值的数据产品,而不会对特定的价值观、信念和观点造成伤害或偏见。
意识到确认偏差和其他认知偏差可以帮助数据科学家克服这些行为挑战。你仍然可以通过思想开放,准备从一个你不习惯的新角度来审视事物,来对抗确认偏见。这种倾向是无意识的,但是训练你的思维模式更加灵活可以帮助减轻确认偏差的影响。
2.可用性偏差
可用性偏差(也称为可用性启发法)指的是倾向于相信容易浮现在脑海中的事物的例子比它们更普遍。这不是错误记忆的结果,而是对记忆的夸大。
如果你能立即想象出支持一个判断的几个事实,你大概可以认为这个判断确实是正确的。如果你看到关于酒精和酒吧斗殴的故事和头条,你应该考虑什么?当信息持续存在时,你的记忆会更强。根据研究,记忆中容易找到的信息似乎比看起来更可靠。
作为数据科学家,我们努力构建由数据驱动和数据支持的公正模型。意识到可用性偏差是构建数据模型的一个关键因素,尤其是不要忽略潜在的相关数据或特性。
3.锚定偏差
与可用性偏差类似,锚定偏差与过分强调一条信息和潜在忽略相关信息有关。锚定偏差是指做决定时过度依赖第一手信息的倾向。你在研究早期学到的东西会对你的假设和建模产生更大的影响。
一旦获得锚定数据,它就成为数据分析和解释的基础。这通常会导致数据模型有偏差,因为它们过分强调了可能与问题陈述不相关的数据点。
当数据科学家过于重视一个数据点而没有考虑其他数据点时,就会出现锚定偏差,从而导致他们误入歧途或做出不正确的决策。锚定偏差可能以多种方式出现,这些方式是由于在数据分析中对初始数据点进行过度加权而导致的。
锚定偏差会影响数据科学家,使他们在为特定结果分析数据集时容易出现某些类型的错误。这是因为数据科学涉及统计思维和对不确定性的推理。数据科学家应该总是问这样一个问题:我们可能遗漏了什么?
为了解决潜在的锚定偏差,数据科学家必须形成不仅仅依赖于一个数据点或数据源的数据采集策略。数据科学家应该问这样一个问题:如果我只看这个数据集,我会错过什么?此外,不要忘记质疑第一个数据集的适用性。
4.自私偏见
利己偏见是一个心理学概念,指的是人们倾向于将好的结果归功于个人,而将不好的结果归咎于外部环境。我们的大脑更喜欢重申我们的信念,当我们正确的时候,我们会感觉很好。知道我们所相信的是真的,对我们来说是安全的。基本上这样,人们会喜欢我们,不会对我们生气。
你在工作中已经看到了。当你的模型成功时,我们中的一些人很快就把功劳归于它,把成功归因于我们的辛勤工作和奉献,但当它失败时,却指向外部因素,如外部环境的变化或意想不到的事件。
我们没办法。我们希望自己是对的,希望自己的工作得到认可,但当事情没有按照我们希望的方式发展时,我们希望避免承担责任。这导致对不支持我们现有想法的数据不开放。我们只寻找数据来证实我们已经相信的东西,而忽略挑战我们的数据或结果。此外,当别人试图给我们有价值的反馈时,我们通常会变得有所防备。
认知偏差只是数据科学家应该在工作中保持警惕的众多原因之一。重要的是,在构建模型时要小心,这样它们才不会因错误或错误的结论而产生偏差,这可能会对您的业务及其成功产生不利影响!
你能想到我没有提到的其他常见认知偏见吗?在下面留下你的评论,让我知道!
有意识的机器:神经科学&伦理人工智能的关键理论
公平和偏见
利用对正念的神经科学理解,结合对因果关系和神经象征人工智能的批判性理论知情观点,创建伦理人工智能。

DNA 的发现是一项科学成就,无疑改变了世界。令人失望的是,因为这项发现而获得诺贝尔奖的三个人之一,詹姆斯·沃森,长期以来一直有种族主义、性别歧视、同性恋和反犹太人的言论(来源)。尽管时光流逝,在他的特权保护下,他无知的观点一直保持一致。在 2019 年发布的 PBS 纪录片中,沃森说,“在智商测试中,黑人和白人的平均水平存在差异。我会说这种差异是遗传造成的。”(来源)。为什么这么重要的科学贡献一定要和一个白人至上主义者结盟?每本生物学教科书都短视地将沃森视为遗传学的鼻祖,而他的偏见揭示了他对这门学科的理解有多么贫乏,这说明了我们的社会是什么样的?
当我十几岁时第一次迷上遗传学时,我记得我对这种不公平现象感到愤怒,并努力将社会不公平现象置于所谓的科学客观性之内。从那以后的几年里,我从批判理论和神经科学两方面对偏见有了更好的理解。在讨论大脑在 上的智力 时,神经学家杰夫·霍金斯写道:
“我们的大脑总是在观察模式并进行类比。如果找不到正确的关联,大脑会非常乐意接受错误的关联。伪科学、偏执、信仰、偏狭往往植根于错误的类比。”(霍金斯,2004 )。
在这个由两部分组成的系列的第二篇文章中,我继续探索神经科学和批判理论可能如何帮助构建伦理人工智能。
在这个系列的第一篇文章中,关于批判理论,我认为交叉性对人工智能伦理学很重要,数据不是客观的,语言的结构具有关联性。关于神经科学,我讨论了无意识偏见,并介绍了计算神经科学、突触可塑性和符号 AI 的主题,这些都将在这里进一步探讨。第二部分的目的是从理论上分两步走。首先,通过使用突触可塑性来理解学习和正念冥想,其次,使用批判理论来构建因果关系和神经象征人工智能的应用,作为创造正念机器的一种手段。
更具体地说,我从比较人类学习和机器学习开始,并探索作为连接认知和批判理论的一种方式的层次结构。在讨论了学习之后,我通过突触可塑性的透镜来解释正念,并利用注意力的共享概念来连接认知和机器学习。接下来,我解释“从错误中学习”这个短语如何提供了一个更好地理解人类思维的机会,同时改进了人工智能的开发。然后,我介绍了神经象征人工智能的新领域,其中包括讨论人工智能排列问题和因果关系。最后,我将批判理论与神经科学联系起来,以想象有意识机器的可能性——基于突触可塑性、使用因果推理的系统。
人类 vs 机器学习
开始学习的神经科学讨论的一个好地方是探索新大脑皮层的作用。这是哺乳动物独有的,是大脑最大的组成部分,也是最负责智力的部分。在一篇名为: 智能机器需要向新大脑皮层学习什么 的文章中,霍金斯将新大脑皮层描述为,
“一张大约 2 毫米厚的深深折叠的纸,如果平放,大约有一张大餐巾纸那么大。对人类来说,它占据了大脑体积的 75%(霍金斯,2017 )。
大脑皮层从白板开始,通过经验学习。我们与世界互动的方式是由这个大脑区域控制的,这意味着知识、运动指令和语言有着不可分割的联系。
在本系列的第一篇文章中,我介绍了突触可塑性,并描述了学习是如何通过调整突触连接的强度来进行的。今年早些时候,霍金斯发布了一本新书:一千个大脑:一种新的智能理论 ,他在书中描述了大脑如何创建许多模型,数量以万计,以模拟世界。这篇中的文章提供了一个全面的回顾,但是为了这篇文章的目的,我将只关注霍金斯的理论,因为它与突触可塑性和学习有关。霍金斯提出,新大脑皮层的神经元被排列成皮层列,每个都有一个独特的突触连接图,因此,每个单元都有自己独特的参考系(霍金斯,2021 )。千脑理论可以简化为,数千个模型中的每一个都是半自主的大脑,有自己的参考框架,而智力是这些模型之间民主共识的结果。突触可塑性负责存储与学习相关的神经放电模式,换句话说,就是设置参考框架。
在我之前的文章中,我强调了语言结构对于人工智能发展的重要性——霍金斯的理论进一步支持了这一观点。他解释说,大脑的物理进化是通过运动来学习,但我们通常只能依靠语言来建立我们的世界模型。由于语言的结构,这种基于语言的学习过程很容易出错。再者,如果没有一个自我修正的机制,比如身体的接触,人们就非常容易形成世界的不同模式。这个理论对教育学的意义在于,当教学时,信息的基本结构很重要。因此,关于机器学习,上下文空间很重要。更具体地说,霍金斯认为,机器需要能够通过重新布线、稀疏表示和具体化来学习(霍金斯,2017 )。
重连线简单描述了突触可塑性的机制;然而,除了突触连接的物理位置,突触调节也很重要。在生物化学上,突触调节具有复杂的分子机制,在遗传学上,表观遗传学意味着每个神经元可以表现不同。心理健康研究员, Alex Lussier ,与我分享了我听过的描述表观遗传学的最佳类比;他在邓恩实验室的研究将表观遗传学与心理健康结果联系起来。他将表观遗传学描述为控制基因表达变异的机制,如果所有的基因都由 20,000 个灯泡代表,每个灯泡都可以打开或关闭,那么表观遗传学可以被认为是每个灯泡上的调光开关,可以微调每个灯泡发出的光量(基因表达)。此外,在新大脑皮层内有 100 万亿个突触连接( Hawkins,2017 )。考虑到连接的数量和调节的复杂性,试图用人工神经网络模拟生物神经网络可能是一项不可完成的任务。
然而,稀疏表示是有希望的,它只是意味着只有少数神经元需要活跃才能表示信息。这种稀疏编码已经适用于机器学习;例如,具有稀疏成本函数的无监督特征学习和稀疏编码算法( Lee 等人,2006 )。霍金斯的公司 Numenta 开发的分级时间记忆 (HTM)是另一个这样的例子( Ahmad & Hawkins,2015 )。基于大脑生理学,HTM 依赖于存储、学习、推断和回忆高阶序列的学习算法;这是通过稀疏分布式表示实现的。通过无监督学习,HTM 在无标签数据中学习基于时间的模式;因此,它非常适合于预测和异常检测等任务。
从教育学的角度来看,大脑依靠感官输入来了解世界。对于新大脑皮层,类似于深度学习神经网络,感觉输入通过提取越来越复杂的特征的层次结构。然而,不同之处在于,新大脑皮层只有四个层次,而深度学习模型通常需要更多的处理才能达到相同的结果,例如物体检测。生物神经网络的优势在于感觉运动整合,即运动与变化感觉的耦合。霍金斯认为,机器智能系统需要建立世界的多维模型,他将这一过程称为具体化(霍金斯,2017 )。这对人工智能发展的意义在于,有必要建立结合各种“感官输入”的系统。也就是说,最近的模型,如 OpenAI 的 DALL-E ,具有优势,因为它们结合了文本和图像数据。或许,创建一个从各种文本、图像、音频、时间序列和图形数据中学习的人工智能系统,将使我们更接近复制人类的学习过程。记住人工和生物神经网络的等级性质,下一节讨论组织结构。
异质结构:认知与批判理论
异质结构是由沃伦麦卡洛克在 1945 年首次提出的概念,该术语描述了他在数学生物物理学背景下对生物神经网络拓扑结构的组织理解(麦卡洛克,1945 )。麦卡洛克研究了认知结构,并证明了人脑不是按等级组织的,而是在一个集体组织中,各种元素可以按几种不同的方式排列。异质组织。这两种组织形式并不相互排斥;在大脑中,等级和异质平行存在。自 1945 年以来,异质结构已被应用于人工智能、信息论、数学、社会学和批判理论。
在本系列的第一篇文章中,我解释了批判理论关注的是挑战权力结构,以减少压迫,增加自由。在政府内部,等级制度和他等级制度都是权力结构,决定着整个政治体系的行为。这种组织形式可以想象成网络结构。在层次结构中,每个节点最多连接到一个父节点,而在异构结构中,一个节点可以连接到任何其他节点,而无需经过父节点或从父节点获得许可。在社会中,等级制度将权力和特权集中在高层手中,而异质制度可以更好地在参与者之间分配特权和决策权。
在逆转传统的权力关系和重新分配特权方面,异质体系也有更大的灵活性,哲学家 Gilles Deleuze 和 Félix Guattari 在他们的书 资本主义和精神分裂症 (1972) 中称这个概念为去领土化。德勒兹和瓜塔里将一系列关系称为一个领土,去领土化是一个领土失去其当前组织和背景的过程。当被改变的关系重新组合成别的东西,创造出新的意义。批判理论家使用异质结构来解释全球化,并更好地理解社会系统,如高等教育。
在 20 世纪 70 年代,异构组织对人工智能和计算机设计产生了影响,例如,可以相互调用的计算机子程序的组织就是异构的。我遇到的关于艾的最有趣的一本书是普利策奖获奖作品: 哥德尔、埃舍尔、巴赫 (1979) 道格拉斯·霍夫施塔特著。这本书探索了认知是如何从神经机制中产生的。Hofstadter 使用句子的句法结构来描述递归结构,称为递归转移网络(RTN ),并继续说:
“可能有一整个家庭的 RTN 都纠缠在一起,像疯了一样互相呼叫和呼叫自己。具有这样一种结构的程序,其中没有单一的“最高级别”或“监控程序”,这种程序称为“混合体系结构”(霍夫施塔特,1979 )。
从那时起,计算机设计层次结构已经实际应用于制造系统,例如,Albert D. Baker 探索了工厂控制算法如何在多代理层次结构中实现( Baker,1998 )。
关于未来的人工智能发展,heterarchies 提供了一种有趣的方式来重组深度神经网络的传统层次结构,使其更类似于生物神经网络。尽管批判理论家认为,层级结构有利于更好地分配权力,但层级结构也存在偏见的风险。最近,Schoenherr 和 Dopko 研究了心理学中基于性别的歧视,并认为男性和女性存在的不同奖励系统代表了一种异质结构( Schoenherr & Dopko,2019 )。他们声称,由于在学术机构的社会网络中使用不同的标准来分配妇女的地位,这种异质结构的存在导致了歧视。因此,尽管异质组织可能在模仿生理认知结构方面提供好处,但人工智能研究人员应该意识到这种组织可能隐藏偏见。下一节通过将神经科学与正念联系起来,进一步讨论偏见。
正念和突触可塑性
正念冥想可以被描述为对当下体验的非判断性关注。作为一种治疗干预的形式,正念已经被神经科学家和心理学家研究了 25 年。,2015 。普遍接受的是,突触可塑性(也称为神经可塑性)在正念的有效性中发挥作用;具体来说,作为支持强化注意调节的神经机制(Tang et al ., Tang,Rothbart & Posner,2012 )。与突触可塑性的联系得到了更多最近研究的支持,这些研究利用了脑磁图来绘制大脑活动( Lardone 等人,2018 )。
正念的效果主要与前扣带皮层(ACC)有关,这是大脑中负责注意力和控制的区域(范维恩&卡特,2002 )。ACC 通过检测不兼容的信息处理流中出现的冲突来工作,并与额岛皮层、一起形成了一个网络的一部分,该网络通过与其他大脑区域的远程连接来促进认知处理( Sridharan 等人,2008 )。虽然人们一致认为正念会改变大脑结构( Fox 等人,2014 ),但对这些变化的确切理解尚未达成( Tang,Holzel & Posner,2015 )。抛开这个谜团,正念本质上是一种学习形式。练习者训练他们的大脑对刺激做出不同的反应,他们学会以改变大脑结构的方式调节他们的注意力。
显而易见的是,正念的关键是注意力。有趣的是,自然语言处理(NLP)领域的最新进展是如何归功于机器学习注意力的实现。由瓦斯瓦尼等人(2017) 撰写的开创性论文厚脸皮地命名为:“ 注意力是你所需要的全部 ”。本文介绍了 Transformer 架构,提供了一种使用注意力机制的方法,而没有递归或卷积。这是一个突破,导致了目前在研究和实现中主导 NLP 的最先进的语言模型。在之前的一篇文章中,我讨论了阿拉伯语 NLP 中的机器学习进展,我更详细地介绍了注意力机制。总之,注意机制保留了输入序列的自回归特性,因此预测未来的能力得到了过去位置背景知识的帮助。
机器学习注意力基于认知注意力,正念被证明是一种有效的注意力调节方法。也许,更好地理解正念和突触可塑性,将提供关于如何改善机器学习注意力机制的见解。在本系列的第一篇文章中,我介绍了一个神经科学理论(赫布边理论),试图解释突触可塑性是如何负责学习过程的。在研究人工神经网络时,Hebbian 理论为无监督学习提供了神经元基础。与之相关的是,两个最重要的人工智能语言模型,谷歌的 BERT 和 OpenAI 的 GPT-3 ,都是使用注意力机制,在无监督的情况下,在极其庞大的数据集上进行了预训练。
这两个广泛使用的模型都存在偏差问题( Bender 等人,2021 , Brown 等人,2020 ),考虑到无监督学习和注意力的结合,很自然地会想到这种结合是如何对偏差进行编码的。此外,考虑到无监督学习和注意力的神经科学基础,思考理解突触可塑性和正念如何在减少偏见时改善机器学习是有意义的。下一节介绍了实现减少偏差目标的实用方法。
从错误中学习
在这篇文章的前面,我提到了道格拉斯·霍夫施塔特(Douglas Hofstadter)的基于神经科学的 AI 著作 哥德尔、埃舍尔、巴赫(1979);这本兼收并蓄的书中的观点挑战简单的解释,因为它们都是非常相互关联的。在这里,我将简单地关注这本书提供的关于人工智能发展的灵感,并描述视角的变化如何能够建议一种不同的方法来解决人工智能中的偏见。霍夫施塔特的观点是,人工智能的发展提供了一个更好地理解人类认知的机会。这种翻转的视角可以简化为“从错误中学习”。也就是说,研究人工智能模型在“思考”中犯的错误也许可以帮助我们了解人类思维是如何工作的,特别是因为模型在某种程度上总是会犯错误。引用霍夫施塔特的话,
“我一直认为,人类完全理解其思维复杂性的唯一希望是通过在计算机上模拟思维过程,并从模型不可避免的失败中学习。"(来源)。
Hofstadter 的研究是围绕理解程序的功能组件而建立的,其中程序的部分被选择性地隔离,以测试没有这些组件程序如何运行。他还通过调整参数来测试性能。传统上,为了优化特定任务的 AI 模型,一个常见的开发步骤是在监督学习设置中调整超参数。Hofstadter 在调整参数时的目标是不同的,他想了解为什么调整一个特定的参数会以那种特定的方式影响性能。他从错误中学习的方法旨在更好地理解人类认知。
我认为双向视角可能有优势,不仅通过观察机器来理解大脑,而且利用对大脑的理解来建造更好的机器。对于后一个方向,对机器如何从错误中学习的理解,涉及到两个人工智能概念:强化学习和错误反向传播。强化学习的基本原则是,通过探索,模型将做出次优选择,然后可以利用这些知识在未来做出更好的决策。强化学习的总体目标是通过从以前的“错误”中学习来最大化累积回报。
误差反向传播指的是通常用于训练人工神经网络的算法。反向传播算法是有用的,因为它们提供了一种有效的方法来计算相对于神经网络的权重的损失(误差)函数的梯度,通常使用梯度方法,例如随机梯度下降。简单地说,反向传播算法允许神经网络以迭代的方式从错误中学习。关于这个概念的更多信息,我建议这个中等职位。与之相关的是,许多用强化学习训练的人工智能模型在学习过程中使用反向传播来调整每个学习步骤。
人类的学习依赖于神经反向传播的生物过程,它可以被描述为一个神经元在与另一个神经元通信时发出一个脉冲(电压尖峰)后动作电位的反向传播。这些反向动作电位的功能目前是一个有争议的研究课题,计算神经科学文献致力于试图证明现有的假设。就本文的目的而言,我严格关注神经反向传播如何影响突触可塑性。重要的是,神经反向传播是新皮层中的一个活跃过程,这与突触可塑性存在于这一特定脑区的观察结果一致。 Stuart 等人(1997) 解释了反向传播电流引起电压变化,增加了神经元树突中的钙离子浓度,这一事件与突触可塑性的模型一致。
有一个狭窄的时间窗,在此期间突触前和突触后神经元的同时放电将诱导可塑性;这种与时间相关的过程被称为尖峰-时间相关塑性,可以用赫布边理论(卡波拉莱&丹,2008 )来解释。此外,由于反向传播动作电位本质上传输神经元的一个副本(轴突)放电模式,它们有助于在突触前和突触后神经元之间建立同步( Waters & Helmchen,2004 )。最近,AI 开发对突触可塑性产生了兴趣,并尝试使用误差反向传播算法来近似人工神经网络中的可塑性( Miconi 等人,2018 )。类似地,计算神经科学家试图使用误差反向传播和神经网络来模拟人脑中的可塑性( Whittington & Bogacz,2017 )。此外,在神经计算的背景下,研究人员试图将强化学习与尖峰时间相关的可塑性联系起来( Baras & Meir,2007 )。
深度学习的先驱杰弗里·辛顿(Geoffery Hinton)也对理解神经反向传播感兴趣。去年,在与神经科学研究人员的合作中,Hinton 认为,神经环境中的反馈连接可以用于局部逼近错误信号,从而推动大脑深层网络中的有效学习( Lillicrap 等人,2020 )。也许,这条神经科学启发的人工智能研究路线也为处理人工智能模型中的偏见带来了希望。在本系列的第一篇文章中,我解释了如何通过开发突触可塑性来消除无意识偏见。如果我们认为偏见是一种错误,那么利用错误反向传播来近似突触可塑性可能会提供一种纠正偏见的方法。下一节介绍了神经象征人工智能的新领域,并应用了因果关系的关键理论观点。
神经象征 AI 和因果关系
当我在本系列的第一篇文章中介绍神经科学时,我提到了认知神经科学是如何与符号 AI 联系在一起的。我解释说,有了符号 AI,智能就是符号逻辑结合因果规则的结果。我还跑题讨论了哥德尔的不完全性定理,该定理证明了不可能使用符号逻辑来创建一套完整且一致的公理来对世界进行建模。Hofsteadter 的书, 哥德尔,埃舍尔,巴赫 (1979)如题所示,也是依靠不完全性定理指出符号 AI 的局限性。我与 Hofsteadter 的观点不同之处在于,我认为从符号人工智能中拯救因果规则,并开发将因果关系应用于数据科学和当前人工智能模型的方法非常重要。我在一系列以经济学为重点的因果教程中推动了这一观点,首先是传统计量经济学,然后是两种因果 ML 方法:因果森林和使用工具变量的深度学习。
因果关系提供了一种解决人工智能发展问题的方法;通过改善深度神经网络有限、脆弱的智能,通过允许可解释的人工智能(XAI),并最终通过解决人工智能对齐问题。后者是哲学和人工智能发展共同关心的问题。问题是如何让机器符合人类的价值观,而这些风险往往被框定为对人类的生存威胁。这个问题的重要性在于,2018 年,DARPA(美国国防部的研究部门)宣布为下一代人工智能提供 20 亿美元的资金,其中一部分涉及解决人工智能对齐(来源)。来自 AI Next campaign 的投资已经产生了神经象征人工智能的新领域,这个令人兴奋的领域的先驱是麻省理工学院-IBM 沃森人工智能实验室的研究人员。
简单来说,神经符号 AI 代表了符号 AI 和深度神经网络的结合;本质上是逻辑驱动的因果规则和数据驱动的模式识别的结合。对深度神经网络的一个常见批评是,它们充当了一个“黑匣子”,缺乏可解释性。虽然符号人工智能天生具有可解释性,但它无法揭示复杂的相关性,不能很好地扩展,并且完全依赖于人类编码的规则。神经符号人工智能的工作原理是使用神经网络将世界建模为符号,从数据中提取模式,并自动创建逻辑规则和领域知识库。然后,符号算法可以应用逻辑和语义推理,在用于训练神经网络的数据之外进行推断,揭示与“常识”的新关系。这种混合方法的优点是需要较少的训练数据,并且可以跟踪得出推论所采取的步骤。这也意味着神经符号人工智能可以跨领域传递知识,并模仿人类理解模糊和抽象概念的能力。
本体在符号算法中被用来描述数据的组织结构,同样,本体已经在计算神经科学中被用来描述大脑的层次和异质组织(拉森&马通,2009 )。它们也很重要,因为它们以批判现实主义的形式将批判理论与因果关系联系起来。作为一种哲学,批判实在论结合了科学哲学先验实在论和社会科学哲学批判自然主义。关键的想法是,科学应该通过使用明确的本体来集中识别因果机制。对因果推理和依赖本体论的强调,意味着批判现实主义提供了一种将神经科学与人工智能发展联系起来的明确方式。
神经符号人工智能的关键是语言和图像之间的联系;人类用符号思考,由于符号表征(符号操作),我们能够将文字与图片联系起来。1921 年,哲学家路德维希·维特斯坦根提出了语言的图像理论,他后来反驳了这一理论,但随后对其进行了修改,将其作为人类心理的隐喻。维特根斯坦的作品可能会令人费解,因为他后来倾向于自相矛盾。然而,语言的图像理论可以简单地表述为,如果语言有意义,那么它可以被图像化。有了这种语言/视觉的联系,维特根斯坦为智力提供了一种解释,它是我们连接感知系统的能力的产物:思考、说话和看。根据维特根斯坦的理论,麻省理工学院-IBM 沃森人工智能实验室的研究人员开发了神经符号概念学习器(NSCL) 。NSCL 是一个混合系统,它使用基于规则的程序和神经网络来解决视觉问答问题。
MIT-IBM Watson AI 实验室还发布了一个视频推理基准数据集,名为: CLEVRER:视频表示和推理的碰撞事件。CLEVRER 数据集训练 AI 识别对象,并从视频中对它们在物理事件中的行为进行因果推理,只使用传统上深度神经网络所需的一小部分数据。有充分的证据表明,人工智能系统正在努力处理视频中的因果关系(来源)。例如,给定一个人扔鸡蛋的视频,人工智能系统不会理解因果关系,也不会预测鸡蛋在与地板碰撞时会破碎。这对于人类来说是一项简单的脑力任务,但对于仅用神经网络构建的 AI 系统来说是一项非常困难的任务;因此,需要混合神经系统。
婴儿发展和意识机器
神经象征人工智能的最新方向是融入对婴儿发展的理解。具体来说,麻省理工学院-IBM 沃森人工智能实验室的研究人员正在试图创造能够像婴儿一样学习的人工智能。一岁以下的孩子被视为婴儿,他们有能力识别物体,有理解力,并能在新奇的情境中用理性指导自己的行动。借鉴游戏开发,即使用游戏引擎来构建交互式和沉浸式世界,人工智能研究人员提出,婴儿天生就有对世界的预编程理解,这样他们就可以创建模拟(来源)。这个想法合并成了智能体数据集,被创建来测试直觉心理,这是婴儿天生的东西,可以被描述为“对驱动可观察行动的隐藏心理变量进行推理的能力”(来源)。这篇 IBM 研究论文详细介绍了代理数据集和两个基线模型,一个建立在贝叶斯逆规划上,另一个模仿思维理论。
关于直觉心理学和婴儿,神经科学已经证明突触修剪的过程随着婴儿的发育而发生。突触修剪简单来说就是去除不使用的突触连接,是突触可塑性和学习的一部分。因此,由于婴儿发育目前是神经象征人工智能的驱动力,也许突触可塑性可以用来进一步推动这种类型的研究。此外,对正念的更好理解将增加我们对突触可塑性的理解,以及我们对因果关系的神经机制的理解。更重要的是,因果推理对于伦理人工智能的发展非常重要。
一个有意识的机器系统需要能够近似突触可塑性的神经网络,并接受各种形式的输入数据:文本、图像、音频、时间序列和图形。这样的机器还需要符号算法来创建一个包含层次结构和异质结构的本体。然后,为了积极地对抗偏见,正念机器可以利用正念激发的注意力机制和错误反向传播来从它的错误中学习。最后,这种混合神经象征方法的因果推理将是解决偏见的一个重要部分,有可能让我们创造一个伦理意识机器系统。
最终想法
目前,连接所有这些命题依赖于公认的脆弱的联系,这些是否能实际影响人工智能伦理,还有待观察。我的观点是,虽然人工智能研究应该试图模仿人类的学习,但人类容易出现错误和偏见;因此,人工智能发展目标需要与道德人工智能目标相结合。这表明有理由将神经科学和批判理论应用于人工智能伦理。如本文所示,这两个学科的结合提供了一个有趣的偏见视角,可以用来构建有意识的机器。
目前,伦理人工智能的最大障碍是缺乏多样性;这意味着研究是由一个狭窄的视角指导的,这个视角并不能完全代表最终将受到技术影响的人群。作为一名数据科学家,我敏锐地意识到困扰这个领域的多样性的缺乏。统计数据显示,只有 15%的数据科学家职位由女性担任,其中不到 3%是有色人种女性(中级)。代表性很重要,我的文章只是为对话做出贡献的一个小小的方式。我的动力来自于我对他人的责任感,因为我代表了一小部分有幸成为数据科学家的人。
对本系列中提到的主题感兴趣的读者可以在 Linkedin 上与我联系。我欢迎提问和反馈。
迷你数据马拉松:数据科学黑客马拉松需要的平台
3 个步骤,这就是全部!

Alex Kotliarskyi 在 Unsplash 上的照片
你听说过多少次黑客马拉松很难举办?我是来给你展示另一面的!
TL;速度三角形定位法(dead reckoning)
我在努力寻找一个简单的实现方法后开发了这个工具,以便在英特尔建立我们一年一度的内部黑客马拉松。有了这个工具,您可以在几分钟内建立自己的完全运行的数据通平台,并提供完整的排行榜和评估。下面是一个例子。

heroku 上托管的迷你数据通平台(图片由作者提供)
显示本用户信息
嘿你!我是通用汽车公司研究自动驾驶汽车的技术主管,在数据科学领域工作了 8 年多。
我喜欢数据马拉松,我已经在公共部门、法国数据( 2016 、 2018 和 2019 )、以色列数据( 2019 、 2022 )和私营部门组织了大量数据马拉松,并在英特尔举办了内部黑客马拉松。
语境
每当您想要创建一个 Datathon 时,您都需要考虑相当多的元素,包括:
- 定义问题
- 为它获取数据
- 向入职培训参与者(以及可选的赞助商)宣传活动
- 后勤:在哪里举行,奖励,食物,参与者和组织者所需的计算能力,等等。
- 搭建数据通平台,评估参与者
在本帖中,我们将只讨论数据通平台和参与者的评价。这是技术难度最大的一步,也是大多数人纠结的地方。我发现现有的工具很难安装,或者缺少一些功能。*
这就是为什么我建立了我的平台。它完全是在 streamlit 中完成的,包含很少几行代码。我邀请你看一看代码!
PS:如果你对构建数据马拉松的其他元素感兴趣,请在评论中告诉我,我可以在未来就这些主题发表文章。
怎么设置?
3 个简单的步骤!
- 在您的服务器中克隆 repo 或简单地提取 docker 映像。
2.修改管理员用户的密码,并在 users.csv 中添加其他参与者
3.修改 config.py 文件&的内容,在终端中运行命令streamlit run main.py
瞧啊。您的数据马拉松已经准备就绪:)
请不要忘记通知参与者,提交文件需要是 csv 格式,订购方式与 train 中提供的相同。
例子
heroku 上部署了代码的一个示例版本,如下: web app

Datathon 平台(图片由作者提供)
幕后…
数据库
该平台只需要保存 2 个组件:
1.排行榜
排行榜实际上是一个 csv 文件,每次用户提交他们的预测时都会更新。csv 文件包含 4 列:
- id :用户的登录
- 得分:用户的最高分
- nb_submissions :团队上传的提交数量
- 等级:团队的直播等级
每个用户只有一行,因为只有最大分数被保存。
默认情况下,基准分数会推送到 leaderboard.csv
| id | score |
|-----------|-------|
| benchmark | 0.6 |
更多详情请参考脚本 leaderboard.py
2.用户
和排行榜一样,这是一个 csv 文件。它应该由竞赛的管理者来定义。它包含两列:
- 登录
- 密码
首先创建一个默认用户,开始使用该平台:
| login | password |
|-----------|----------|
| admin | password |
为了添加新的参与者,只需将行添加到当前的 users.csv 文件中。
更多细节请参考脚本 users.py
所有这些解释和脚本都可以在我的个人回购中找到:【https://github.com/jeremyatia/mini_datathon
如果你喜欢这个项目,请给我买杯咖啡:)告诉我
*您可能想尝试的其他平台…
我通常尽量不去多此一举。随着时间的推移,我发现一些 Datathon 平台并不完全符合我的需求。
我想要一个可以在几分钟内完成设置和编辑的平台。
根据您的需求,以下平台可能与您相关:
- EvalAI :很好的用户界面和非常完整的工具,但是不容易安装。如果你组织一个大型黑客马拉松,我建议这个。
- 由朋友杨奇煜·沃切尔斯开发的这个平台有一个非常好的用户界面。这是我们用于 FrenchData 黑客马拉松的工具。
- 提交:我在英特尔的内部黑客马拉松中使用了这个。设置一般,用户界面很简单,我们在排行榜上有问题。
其他我没试过的工具: Dribdat 、ML _ Competition _ Platform、DataScienceLeaderboardPlatform、 Internal-kaggle 、 Datathon_builder 、 Yairf11 的 Streamlit-Leaderboard 、 Vindruid 的 Streamlit-Leaderboard
时间序列预测的监督学习迷你指南
通过监督学习进行时间序列预测的模型训练、特征工程和误差计算技术

斯蒂芬·道森在 Unsplash 上拍摄的照片
我学到的第一个时间序列预测技术是 ARIMA。但是当我开始构建预测模型时,我看到了关于使用监督学习模型预测时间序列数据的研究论文和博客。这些模型比 ARIMA 更有优势,尤其是在预测需要多种粒度的情况下。在这篇博文中,我将总结我在构建这些模型时学到的知识,以及一些应该做/不应该做的事情。我将介绍一些关于交叉验证和模型训练、特征工程、目标变量工程和误差计算的知识。
- 数据拆分和测试
要将您的预测问题转换为基于监督学习的回归问题,您需要重构您的数据,使其具有一个目标变量,即y .**简单的数据重构可能看起来像 t-5、t-4、t-3、t-2 和 t-1 时间戳的特征集,而目标变量是 t。
为了解释这一点,我使用了众所周知的来自 Kaggle 的洗发水销售时间序列数据集,其中包含了 3 年期间洗发水每月的销售数量。我应用了一些预处理来为数据集生成时间戳,并创建了一个函数来从单个时间序列生成特性集和目标变量。
*import pandas as pddef supervised_sets(data, lookback):
'''
params:
data - df with series
lookback - number of time periods to look back
return:
X - feature set
y - target variable
'''
X = pd.DataFrame()
y = pd.DataFrame()
for i in range(0, len(data) - lookback):
train = data.iloc[i : lookback + i]
test = data.iloc[lookback + i]
X = pd.concat([X, pd.DataFrame(train[['Sales']].values.T)])
y = pd.concat([y, pd.Series(test[['Sales']].values[0])])
for k in range(lookback):
X = X.rename(columns = {k : 't' + str(k)})
return X.reset_index(drop = True), y.valuesdata = pd.read_csv('shampoo_sales.csv')
data['Timestamp'] = pd.to_datetime(data['Month'].apply(lambda x : '200' + x))X, y = supervised_sets(data, 5)*
一旦您拆分了数据并准备好供模型使用,一个基本问题就是如何拟合和测试数据。在没有时间结构的监督学习问题中,我们可以简单地进行 k-fold 分裂来训练和测试整个数据集。但是,如果您正在提供一个时间序列,您会希望测试数据集具有在训练后发生的时间戳。如果我们进行 k-fold 分裂,那么当我们在行为开始之前的观察结果上测试它时,我们有训练数据集泄露新的前瞻性行为的风险。
我会试着用一张图来解释清楚:

作者图片
对于分割数 2 和 3,训练数据将具有来自未来的数据。此外,分割数 3 的平均误差将是不准确的,因为前瞻行为的任何变化将导致对过去的不准确值的预测。
为了解决这个问题,我们可以通过日前向链接利用嵌套的交叉验证。我将使用 sklearn 的一个函数来拆分数据。
*import numpy as np
from sklearn.model_selection import TimeSeriesSplittscv = TimeSeriesSplit(n_splits = 10)
for train_index, test_index in tscv.split(X):
print("TRAIN:", train_index, "TEST:", test_index)
X_train, X_test = X.iloc[train_index], X.iloc[test_index]
y_train, y_test = y[train_index], y[test_index]*
这个函数以这样一种方式拆分数据,即我们迭代地向训练数据集添加时间段,同时使用后续时间段作为测试数据。我添加了上面代码中的几行结果,以更好地解释该函数的作用:
*TRAIN: [ 0 1 2 3 4 5 6 7 8 9 10] TEST: [11 12]
TRAIN: [ 0 1 2 3 4 5 6 7 8 9 10 11 12] TEST: [13 14]
TRAIN: [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14] TEST: [15 16]
TRAIN: [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16] TEST: [17 18]*
2。时态特征的特征工程
时间序列预测基于这样的假设,即历史时间戳遵循可用于预测未来时间戳的模式。因此,时态要素的要素工程是构建性能良好的模型的关键部分。
分析时间序列数据的第一步是绘制时间序列的线图。

作者图片
当我绘制洗发水销售图时,我观察到倍增的季节性,不同时间段之间的变化随着时间而增加。我们还可以观察到两个月的弱季节性,之后每个月我们都会观察到洗发水销售的增加/减少。
从特征工程开始的一个好地方是绘制自相关函数(ACF)和部分自相关函数(PACF)。这将使您了解哪些滞后是重要的,以及数据集中的季节性。季节性分解也是理解数据中潜在的时间模式的好方法(尽管这需要一些参数调整)。
在绘制相关函数之前,我们将对序列进行差分,使其稳定,这样序列的统计特性就与时间无关。
*from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
import matplotlib.pyplot as pltplot_acf(data['Sales'].diff().dropna().values)
plt.show()plot_pacf(data['Sales'].diff().dropna().values)
plt.show()*

作者图片
这里,ACF 表明滞后 1 是重要的,即先前的时间戳是重要的。此外,滞后的交替符号证实了每个时间段后增加/减少的假设。然而,这种趋势并不是在所有滞后中都是一致的。将 t-1 作为一个特征包含在模型中会有所帮助。

作者图片
我们看到 PACF 表明第一个滞后是显著的,随后是 11 到 15 之间的一些滞后。这些孤立的滞后影响当前时间戳。最好将这些时间戳作为单独的特征,其中滞后 1 可能指示水平,而 11–15 指示弱季节性。我们可以在模型中添加年份和月份特征作为指示变量,以及这些滞后值。
以上讨论是关于如何使用数据分析启动要素生成过程的一些想法。当然,反复试验会产生最好的结果:)
3。说明基于时间的特征的周期性
我们可能希望将记录观察结果的时间戳的特征(日期、日期、月份、小时、年份)放入特征集中。例如,我们可能希望输入观测记录的月份和年份(如果我们知道系列的级别每个月都在增加),或者我们可能希望输入观测记录的小时(如果我们知道每小时级别有很强的季节性)。
您不希望将这些值直接作为整数使用,因为这意味着与 9 月份(月份= 9)记录的观测值相比,12 月份(月份= 12)记录的观测值与 1 月份(月份= 1)记录的观测值之间的距离更大。我们知道事实并非如此。我们希望模型知道 1 月比 9 月更接近 12 月,23 小时比 4 小时更接近 0 小时。我们可以通过获取时间特征的正弦和余弦来捕捉周期性。
*data['sin_month'] = np.sin(data['Timestamp'].dt.month)
data['cos_month'] = np.cos(data['Timestamp'].dt.month)*
4。设计您的目标变量
当我为时间序列预测建立第一个监督学习模型时,我使用了一阶滞后作为我的特征之一,并且我能够获得极低的误差指标。在进一步的检查中,我意识到这个模型只是在执行随机漫步,因为我的下一个时间步严重依赖于前一个时间步,所以我能够获得很好的度量。
为了避免这种情况,将目标变量定义为一阶差分是很有用的。因此,ML 模型不是预测 t + n + 1 时间戳的值,而是预测{ t + n + 1 时间戳} — {t+ n 时间戳}的值。以这种方式,该模型将不仅依靠前一个时间戳来预测下一个时间戳,而且将使用历史滞后和外生回归量。阅读 Vegard 的文章可以获得关于这个主题的深入解释:https://towards data science . com/how-not-to-use-machine-learning-for-time-series-forecasting-avoiding-the-陷阱-19f9d7adf424
5。错误监控
为了跟踪基于监督学习的预测模型的准确性,平均绝对误差(MAE)和均方根误差(RMSE)是很好的指标,但我去年发现并经常使用的一个指标是平均绝对标度误差(MASE)。如果我们使用随机游走方法,其中上一个时间戳的值将是下一个时间戳的预测值,则此指标考虑了我们会得到的误差。它将模型的误差与简单预测的误差进行比较。这也有助于你从数字上评估你的模型比天真的预测好多少。
*def MASE(y_train, y_test, pred):
naive_error = np.sum(np.abs(np.diff(y_train)))/(len(y_train)-1)
model_error = np.mean(np.abs(y_test - pred))return model_error/naive_error*
您还可以根据数据集的季节性来自定义 MASE 指标。例如,如果您正在处理显示每周季节性的数据集,您可以执行 7 阶而不是 1 阶的差分来计算原始误差。您可以根据您的预测频率和数据集季节性来设置自己的基准模型。
如果 MASE > 1,则该模型的性能比基线模型差。MASE 越接近 0,预测模型越好。
如果你做到了这一步,感谢你的阅读!在使用监督模型进行时间序列预测的过程中,我试图捕捉我的一些经验。我很乐意在评论中听到你的意见。我相信我们需要更多的博客/文章来分享预测领域的经验。让我知道你的想法:)
面向 2021 年数据科学家的最小 SQL

本·怀特在 Unsplash 上的照片
有志成为数据科学家或分析师的基本必备 SQL
时不时看到新媒体文章说“7 个必备技能”“10 个重要技能”“3 大技能”等。对于一个数据科学家来说。所有这些帖子都承认 SQL 是数据科学家的必备技能。在我进入这个行业之前,来自计算物理学背景的 SQL 对我来说是陌生的。在学术上,你用的不多。然而,对于大多数公司来说,数据通常存储在关系数据库中。SQL 是一种查询语言(结构化查询语言),用于通信(获取、存储、操作等)。)用一个数据库。你可以把 SQL 想象成数据科学家 与数据交流的 通用语。事实是,学习起来同样容易,因为它是一项必不可少的技能。
这篇文章告诉你作为一名数据分析师/科学家,你需要知道的最基本的 SQL。
注意: 这篇文章并不是关于 SQL 的详尽、全面的课程。当然,为了精通您的工作,您将每天学习和提高您的 SQL 技能。
通常,真实的数据库(一个数据库有几个表)是巨大的;一个表可能包含超过 10 亿行数据。然而,要处理如此庞大的数据,您只需要知道 SQL 构建块。
因此,我将从创建一个简单的数据库开始,它有一个非常小的表,仅包含 6 行。同样的概念可以用在拥有数百万或数十亿条目的真实数据集上。
1)在哪里键入和执行 SQL 查询?
您可以分两步在您的 PC 上练习您将在这里阅读的所有查询/命令:1)从这里安装开源 MySQL 社区服务器,2)安装服务器后,安装 MySQL 工作台(编辑器)以运行从这里的 SQL 查询。只要继续遵循安装说明。
2)创建数据库和表格,并在其中存储数据
数据存储在一个可以有多个表的数据库中。假设一个城市有几所学校,每所学校代表一个数据库,其中不同的班级/年级代表一个数据库(学校)中不同的表。简单来说,一个公司就像一个城市,有不同的数据库。
让我们创建一个简单的数据库(与在 MySQL 中使用模式同义),名为示例。下面这段代码首先检查名为 SAMPLE 的数据库是否存在。如果有,它将被丢弃(删除)。然后创建数据库。也可以把DROP DATABASE写成DROP SCHEMA。
接下来,我们在示例数据库中创建一个名为 Class 的表,因此,您必须在创建该表时指定“ SAMPLE.class ”。在创建表之前,再次检查该表是否存在。一个表可以有几个相同或不同数据类型的列。创建表时,必须指定列名及其各自的数据类型。
现在已经创建了该表。现在您可以在其中插入条目(行形式的数据)。代表数据的值应该与创建表格时指定的列顺序相同。
注意:SQL 是不区分大小写的。习惯上用大写字母写关键词,但这不是必须的。

创建一个名为“SAMPLE”的数据库/模式,并在其中创建第一个名为“Class”的表。
3)从表中访问/查询数据
恭喜你。您已经创建了您的第一个数据库和一个表。您在上面看到的所有代码行都被称为查询。您正在数据库和表格中查询某些数据/信息。
您可以使用“选择 ***** ”命令显示表格的内容。此处的星号(*)代表所有列。下图显示了上面创建的表格内容。

显示“类”表的所有内容。
如果只想查询(选择/显示)某些列,可以指定所需的列名,用逗号分隔,如下所示。

仅显示从“类”表中选择的列。
请注意,数据的打印(查询)顺序与插入顺序相同。要按一定的顺序打印,在按关键字排序的之后,指定要排序的字段。您可以指定多个列名,以便按多个字段排序。例如,以下查询显示了按学生姓名(学生)和成绩的升序打印的数据。默认情况下,数据按升序排序。要按降序排序,请在要排序的字段后指定关键字desc。比如ORDER BY Student desc, Grade desc。

按多列对表格内容进行排序。
4)在查询输出中添加新列
如果要打印基于现有列的附加列,请在星号(*)后用逗号分隔指定它们。例如,下面的代码添加了一个将等级除以 100 的列。如果不指定输出列名(通过使用AS 'Grade_new'),它将留空。

基于算术计算添加新列。
5)过滤数据
您可以使用 WHERE 关键字过滤满足特定条件的数据。例如,以下查询获取得分超过 85 分的学生的信息。您还可以通过AND关键字使用多个字段过滤查询结果。例如,尝试使用WHERE Grade > 85 AND Course = 'Math'。注意,您也可以通过在最后使用 ORDER BY 关键字来对过滤后的结果进行排序。由于 WHERE 关键字的优先级,不能在 WHERE 关键字之前指定 ORDER BY。

使用 WHERE 关键字过滤查询结果。
6)计算行数
要获得表中的总行数,可以使用SELECT count(*) FROM SAMPLE.class。如果想统计等级超过 85 的行,可以添加WHERE关键字。尝试使用和关键字指定多个条件,如前所示。

对满足条件的行进行计数(这里:等级> 85)。
7)对数据进行分组
有时,对于一些统计分析,您需要根据一些字段对数据进行分组。这是使用 GROUP BY 关键字完成的。这是最常用的 SQL 关键字之一。当然,当您分组时,您会对一些聚合值感兴趣,例如平均值、最小值、最大值、标准偏差等。一个团体。
以下查询返回学生姓名及其平均分数。

查询以获得每个学生的平均成绩。
过滤分组数据
假设您只想显示或查询平均成绩(上面计算的)大于 81 的学生。HAVING 关键字在这里派上了用场。以下查询显示了如何使用平均分数过滤结果。

将筛选器与 GROUP BY 关键字一起使用的查询。
您也可以在 HAVING 关键字中组合多个过滤器(条件)。例如,尝试使用HAVING AVG(Grade) > 80 and AVG(GRADE) < 88。
8)案例陈述
CASE 关键字非常有用。它用于 if-else 情况中,如果条件为真,您必须分配一个值,如果条件为假,您必须分配其他值(您也可以有多个 else 情况)。以下是 SELECT 语句中 CASE 语句的语法。
CASE WHEN condition_1 THEN value_1
WHEN condition_2 THEN value_2
WHEN condition_3 THEN value_3
...
ELSE some_value END AS output_columnname
下面说明了类表的 CASE 关键字的用法。

CASE 关键字的用法。
组合事例和分组依据
下面显示了 CASE 和 GROUP BY 关键字的用法。

9)连接表格
这可能是真实 SQL 应用程序中使用最广泛的操作。这个想法是将一个表与一个或多个表连接起来,从后者获得附加 信息。
有几种方法可以连接两个表。例如,您可以拥有两个表中的所有合并数据、两个表中的公共数据、一个表中的数据而另一个表中的数据,反之亦然。这个页面用简单的例子解释了所有的连接。
我将展示两个这样的连接操作,名为左连接和内连接。到目前为止,我们的数据库中有一个名为 Class 的表。我们创建第二个表 Address,包含数据库中学生的姓名、城市和州。

在示例数据库中创建第二个名为“Address”的表。
让我们看看地址表的内容。

查询地址表的内容。
左连接
根据我的经验,左连接是最常用的操作。下图解释了左连接的要点。该操作返回左表中的所有行和右表中相应的匹配条目。

左连接操作的示意图。
假设我们想查询学生的城市和州,以及他们的课程和成绩。现在,我们的左桌(主桌)是级。为了从右表( Address )中获取城市和州,我们需要一个公共列来进行一对一的匹配。在我们的两个表中, Student 是公共列。
以下代码显示了如何使用 LEFT JOIN 将类表连接到学生列上的地址表。正如您所注意到的,Dave 的城市和州字段是空的。这是因为右表(地址)不包含 Dave 的数据,因此,左连接找不到任何匹配的条目。我们使用别名作为表名,使用"作为"关键字,来引用列(这里左边的是 l ,右边的是 r )。

左连接主表“类”和“地址”表。
您还可以在使用 WHERE 关键字进行左连接时使用过滤。将下面的输出与上面不带 WHERE 关键字的输出进行比较。

在左连接期间过滤数据。
内部连接
在上面,您看到左连接查询也为 Dave 生成了行,尽管在右(地址)表中没有 Dave 的地址。如果要排除这样的空(不可用)条目,可以使用内部联接,如下图所示。该操作返回在两个表中具有匹配值的条目(行)。

内部联接操作的示意图。
语法与 left join 相同,只是现在使用关键字 INNER JOIN 。正如您所注意到的,内部连接不会产生戴夫的数据,因为它对两个表来说不是公共的。

内部连接主表" Class "和" Address "表。
我建议读者访问这篇文章,查看所有 SQL 连接。
10)嵌套查询
到目前为止,简单的例子允许您使用 WHERE 和 HAVING 关键字以及 GROUP BY 来过滤结果。在上面的内部连接(或左连接)的输出中,注意城市和州是重复的,因为每个学生在表中都有多个条目。假设您想要他们的平均成绩和地址(城市+州)。您需要 1)使用 GROUP BY 计算平均值,2)使用 LEFT JOIN 获得地址。
下面的代码包含一个计算平均值的内部查询。内部查询的输出充当左连接的表。内部查询输出别名为 l 。这两个查询一起构成了一个嵌套查询。

嵌套查询的示例。
结论
这篇文章中介绍的 SQL 基础知识构成了作为数据科学家、分析师或 ETL 相关职位的人开始处理数据所需的最基本的基础知识。真正的表可能包含几十或几百列,几百万或几十亿行,选择查询跨越大量的行。然而,这里获得的知识将帮助您理解复杂的 SQL 查询。
由于 python 编程语言是数据科学家另一个最受欢迎的技能,你也可以参考我在 python 上的以下帖子:
最小可行开发人员:在软件领域的第一个月表现出色
办公时间
开发软件不仅仅是编码——这里有一个关于编程的所有东西的指南,将帮助你在你的第一个角色中取得成功
无论是实习还是全职,你很容易被第一个软件角色的新奇感和要求所淹没。
这是一个你在真实的软件商店中会遇到的工具、技能和仪式的指南。它假设您已经知道编码的机制,并且关注于从事学校项目和为业务关键代码库做贡献之间的主要区别。
它适用于数据科学、后端或前端角色;这是关于所有的事情,不是写你喜欢的代码风格的。
这份指南对加入创业公司的人来说比加入老牌公司更有用。FAANG 公司会让你经历超过 4 周的正式入职培训。在一家初创公司,你更有可能得到一台笔记本电脑和一件 t 恤,并被要求加入你的同事,拼命试图将一艘正在下沉的船驶向盈利。让我们在最短的时间内把你从负重者变成划手。
软件生命周期
当你加入你的第一家公司时,你会面对令人眼花缭乱的大量信息。你将不得不做出一些艰难的选择,决定你应该把精力集中在哪里。
与其花一周时间阅读整个代码库,了解用户,或者与组织中的每个人坐下来了解他们的角色,我建议你专注于理解代码生命周期:代码被编写并整合到产品中的过程。

代码生命周期。尽可能快地完成这个循环,最大化你在工作中的学习。船对应的是部署(也叫出货)。来源:作者
你的目标应该是最大化你在工作最初几个月经历的这些周期的数量。这对于你来说是很好的,因为它最大化了你的学习,对于组织来说也是很好的,因为他们有很多机会推动你走向有价值的结果。
以下是我们将要讨论的步骤,您应该很快就能明白:
0.设置:如何在我的电脑上获取代码并运行?希望你只需要这样做一次,所以它不是核心循环的一部分。
1.任务:任务组织在哪里,如何分配?
2.代码:我如何编写和测试代码?
- Git :如何将代码推送到版本控制?
4.评论:人们是如何评论我的作品的?
5.部署:它最终是如何部署的?
为什么您应该专注于执行这个生命周期?
刚从大学毕业,满脑子都是算法,你可能认为你的时间最好花在和你能找到的最聪明的程序员坐在一起,学习如何有效地实现旅行推销员算法的技巧。
请不要这样做。相反,在你需要理解的系统中快速导航,以快速通过这个循环。
如果你想跟踪某人,选择团队中完成任务最多的人,即使他们不是最性感或最有趣的。他们已经找到了快速度过这个周期的方法。
为什么快速完成这些步骤如此重要?这 5 个步骤是您想要对代码库进行的任何更改的固定成本。它们的成本与你所写代码的复杂程度不成比例。这意味着,无论您是修复一个打字错误还是重写整个 API,它们花费的时间大致相同。

在代码生命周期中,对代码库进行大的或小的更改需要花费大致相同的时间。如果你花了很长时间来完成这些步骤,那么小任务就会慢得惊人。来源:作者
如果你能把执行它们的成本降低到一个很小的数量,你就能捡起小块的工作并有效地执行它们。小块的工作有更高的机会被快速部署,你将更快地完成一个完整的周期。你会更早地得到更多的反馈,你会比奶酪滚下山更快地积聚动力。

你想挑选许多小任务并快速执行它们。如果你纠结于基本的,你会倾向于选择更复杂的任务来弥补长周期时间,并在一周结束时什么都没有部署——糟糕的一周。来源:作者
这篇文章的其余部分是对这些步骤的一步一步的指导,你需要使用的工具,以及如何有效地执行它们。
如果我被卡住了怎么办?
坏消息是:你在学校或大学里不会学到这些东西。
好消息是:和你一起工作的人也没有在大学里学过!
从事软件工作的每个人都通过大致相同的三种策略来解决问题:
- Google stuff(通常会导致堆栈溢出)
- 如果他们是守旧派,读一本书;如果他们注意力不集中,读一篇博客
- 问问可能知道的人
请不要因为严重依赖第三条而感到焦虑或尴尬。
你周围的人花了几个小时对软件问题感到困惑和沮丧,他们很乐意与你分享这些经历的好处。
以下是我的朋友 Jonny 的一些建议,她在几个月内从实习生晋升为 Element AI 的一个产品团队的核心人物:
“以我的经验来看【卡顿】对新手来说是最大的减速。他们觉得他们需要证明自己,因此不想承认错误或知识差距——讽刺的是,这阻止了他们快速迭代和学习。我的建议是,在寻求帮助之前,设定好时间。此外,熟悉内部文档工具(如 Confluence ),并在询问前进行搜索。一直寻求帮助是我成为今天的开发者的唯一原因。这可能是你指派的“伙伴”,你团队的一员,最后修改代码的人(通过 git 责备检查)或者任何人!只是不要让自己被挡住,呆在那里。”
0.我如何让代码在我的电脑上工作?
大多数公司使用版本控制来跟踪他们的代码。这允许多人在不覆盖彼此工作的情况下处理同一个项目,同时保留以前版本的防弹日志,以便在需要时可以很容易地恢复旧的工作。代码的多个副本存储在互联网上,一个叫做库的结构中。

一个版本控制程序,如 Github(他们有这个奇怪的 octocat 字符作为吉祥物,如图中所示),允许多个开发人员独立开发,定期更新互联网上的代码共享副本。来源:作者。
在 21 世纪,这种版本控制通常意味着使用一种叫做 git 的技术。开发人员将一个在线 git 库复制到他们的本地机器上——这被称为克隆——然后在将它们复制回服务器之前进行修改——推动他们的工作。
虽然底层技术— git —是开源的和标准的,但是有很多不同的网站会为你托管你的 git 库。Github、Gitlab、Bitbucket 是主要的三个。
为了简洁起见,我假设您使用的是 Github,但是下面的一切同样适用于其他提供者。
1.访问在线代码库
如果您还没有 Github 帐户,请创建一个。让你的主管给你项目的权限。
好吧,这很简单。我们开始得很快。
2.安装 SSH 密钥
为了避免每次推送或提取代码时都必须进行身份验证,您应该为您的版本控制系统设置一个 SSH 密钥。 SSH 密钥允许您的计算机向服务器识别您的身份,并确认您应该被授予访问权限。
Github 有一个很好的生成和上传密钥的指南。
每个 SSH 密钥对由一个公钥和一个私钥组成。公钥是您上传到 Github 的那个,它看起来像这样:
$ cat ~/.ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3 Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA t3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/En mZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx NrRFi9wrf+M7Q== schacon@mylaptop.local
私钥保存在您的计算机上,每次您需要进行身份验证时,它会被发送到您的版本控制提供商。Github 会将您的公钥与您发送的私钥进行比较,以确认它们是同一个密钥对的一部分,并且您应该被授予访问权限。
我建议不要在你的 SSH 密钥上设置密码短语。你的电脑已经有密码保护了(对吧?)而且我也不认为有这个额外的障碍对迭代有帮助。
3.使用终端克隆回购协议
现在你需要复制你将要处理的存储库——或者回购——到本地机器上。这被称为克隆,因为你正在创造一个精确的复制品,就像克隆羊。
为此,您将需要某种终端,它将使用像bash、zsh或fish这样的语言来处理基本的系统级操作。

iTerm,最受欢迎的 Mac OSX 终端。资料来源: iTerm (GPL-2)
我认识的大多数 Mac 用户都使用 iTerm 而不是内置终端,这给了你各种各样的好功能,比如 T21 分割面板。
根据我的经验,大学不太擅长教人们如何使用终端,缺乏基本的熟练程度会让你慢下来。我建议你花几个小时练习如何:
- 创建和删除文件和目录(
touch、rm、mkdir) - 显示文件内容并在其中搜索(
cat, grep) - 反向搜索以查找您以前使用过的命令(
ctrl+r)
Unix(大多数 shells 所基于的语言)是一个极其强大和灵活的计算环境,大多数用户(比如我)对它只是皮毛。事实上,如果你是一名awk忍者,大多数开发者会比你完成了在线 Tensorflow 证书更有印象:考虑在这里投资一些时间,而不是更华丽的选择。这本书不错,如果想深入的话。
好吧,让我们假设你正在运行这样的东西
git clone git@github.com/mycompany/myrepo.git
你会看到这样一条信息
Cloning into 'my-first-repo'... remote: Enumerating objects: 58, done. remote: Counting objects: 100% (58/58), done. remote: Compressing objects: 100% (43/43), done. remote: Total 58 (delta 8), reused 54 (delta 8), pack-reused 0 Receiving objects: 100% (58/58), 48.83 KiB | 200.00 KiB/s, done. Resolving deltas: 100% (8/8), done.
现在你有一个回购准备好了。
4.让它跑起来
让您的项目运行的具体说明应该在项目中描述,通常在一个名为README.md的文件中。如果没有,请某人带你完成代码运行,并主动为回购编写README.md——这是一项非常有价值的任务!

来自 https://github.com/huggingface/transformers.的 README 示例如果您在存储库的根目录下包含一个名为 README.md 的文件,当人们在线导航到代码时,它通常由您的版本控制系统呈现,作为用户指南。所有这些颜色鲜艳的徽章都与回购的各种构建和测试状态相关。来源:拥抱脸(阿帕奇 2.0)
首次建立一个新的回购协议可能是一个棘手的挑战,因为许多项目都依赖于外部系统,如亚马逊网络服务(AWS)或谷歌云平台(GCP)。
您可能还需要为它们登录,并且可能需要安装命令行界面(CLI)来更顺畅地使用它们。尽早询问你 可能需要的所有登录信息,因为人们可能需要一段时间才能给予你访问权限。我们将在第 6 节回到这些部署环境。
尽早请求所有你可能需要的登录信息,因为人们可能需要一段时间才会给你提供访问权限
在很多情况下,你可能还会用到一个叫做 Docker 的工具。
Docker 是一个用于创建隔离的、可重复的计算环境的工具,就像在你的计算机中运行的小虚拟机,具有严格控制的环境。这些环境——容器——解决了环境差异的常见问题——一些人在 Mac 10.13 上工作,另一些人在 Mac OSX 10.9 上工作,各种不同的 Linux 发行版甚至(令人震惊的)Windows。
我能想到的最好的类比是音乐。Docker 以一个 Dockerfile 开始,它就像一段指定歌曲的乐谱。当它被 Docker 编译后,就变成了一个 Docker 镜像,有点像那首歌的 MP3。每当我们想要从 docker 映像启动一个计算任务时,结果就是一个 Docker 容器,这是一个由映像指定的短暂(意味着寿命很短)、一致(总是相同)的计算环境——就像每次播放 MP3 都会产生完全相同的短暂体验一样。

Docker (logo 是蓝鲸)是一个创建一致运行时的工具。它从对你的计算环境的书面描述开始,就像乐谱一样。这然后被编译成图像,有点像音乐被录制成文件。从这个图像中,我们可以启动容器,它们是短暂的、相同的计算作业,有点像 MP3 的每次播放都会给听者带来完全相同的体验。来源:作者
如果您的环境使用 Docker,那么有必要考虑一下创建和销毁 Docker 容器的基础知识。熟悉这些概念将减少开发的摩擦,并使您能够更快地审查代码。特别是,试着理解:
- 图像和容器的区别
- 图像是如何存储的
- 什么是
docker compose以及它为什么有用
让我们假设您的项目在 Docker compose 中组织得很好,并且只有一个入口点,在这种情况下,您将运行如下代码:
docker compose up -d
1.任务组织在哪里?
团队需要一种方法来跟踪需要做什么工作,什么时候完成,谁来做。
大多数现代软件团队被描述为“敏捷”的,这基本上意味着他们以 1-4 周的周期工作,之后他们会停下来想想下一步该做什么。这包括选择一堆任务——通常被称为票或问题——为下一个周期工作。
开发人员中使用最频繁但非常不受欢迎的系统叫做吉拉。还有很多其他的,许多版本控制系统都内置了自己的问题跟踪器。
有三种主要类型的门票让你流连忘返:
- 任务:需要完成的一些工作,通常需要 1-12 个小时
- bug:坏掉的东西
- Epics :元票证代表一大块工作,包含多个任务
票证最重要的属性是其状态,通常是以下之一:
- 待办事项或待办事项:需要做的事情
- 正在做:正在做
- 在评审中:工作已经完成,其他人正在评审
- 完成:完成了

一个典型的情况是:许多任务被搁置,这意味着它们不会很快完成。被选中的任务最终会出现在“白板”上,在那里它们会移动到“要做”,进入“正在做”,然后进入“回顾”,最后完成。然后票就完成了,或者说“关闭”了。来源:作者
站立、计划和追溯
大多数软件团队有 3 次例会,在会上回顾进展并做出决定。在这些会议中,你将选择或被分配你的任务,报告你在这些任务上的进展,并思考如何更顺利地完成这些任务。
规划
频率:每次冲刺一次
持续时间:1 小时以上
目的:团队,或者有时仅仅是领导团队,决定下一个 sprint 应该做什么的大型会议。人们使用许多技术来区分工作的优先次序,并估计能完成多少工作。更多来自亚特兰蒂斯的。
单口站立
频率:通常每天
持续时间: 10 分钟
目的:每天检查谁在做什么,昨天完成(或没有完成)了什么,以及人们遇到的任何问题。
复古
频率:每次冲刺一次
持续时间: ~30 分钟
目的:这是团队反思哪些进展顺利,哪些进展不顺利的机会。当团队成熟并相互信任时,这些真的很有用。然而,很多时候,他们很尴尬。不过,还是值得一试。一些流行的技术是启动、停止、继续和快艇。
选择你的首要任务
公司喜欢谈论“快速出货”和“快速迭代”,这被普遍认为是软件工程中的良好实践。
快速发货可以让你迅速发现你对需要建造什么以及建造它的最佳方式的理解中的缺陷。尤其是在初创公司,我们对用户的了解并不完善,业务经常以意想不到的方式发展。应对这种不确定性的最佳方式是快速发货。
作为一名新开发人员,这些原则对你来说更加重要,因为有太多你不知道的事情。了解用例、软件开发传统以及如何编写好代码的最佳方式是发布大量的用例,获得反馈,并进行改进。
记住,你的目标是最大化你经历的周期数,为了做到这一点,你需要挑选一些小任务。
试着找到你能做出的最小可能的贡献,然后去做。如果是一个 bug 修复比一个新特性要好,因为开发者并不真正喜欢 bug 修复,所以你是在帮所有人的忙。
具有相对独立的解决方案的小任务(意味着您不必在太多地方修改代码)是非常好的,因为:
- 他们帮助你建立动力和信任:让你和你的团队觉得你很早就做出了贡献,这对你有好处
- 他们很容易被其他人审查:审查代码可能非常耗时,你需要赢得人们花时间来帮助你改进的权利——保持简单,开始并限制你同事的认知开销
- 你不太可能做错:在小任务中出错的空间更小,你也更容易在较小的工作中整合反馈
让我们假设你给自己找了一个不错的小任务,让一个按钮变大。它有一个引用 MP-910 ,我们稍后将使用它将您的工作与您的代码正在解决的问题联系起来。
你的 JIRA 机票可能是这样的:

一张吉拉票的例子。来源:作者吉拉截图。
您通过将自己设置为受分配人来表明您完成该票证的意图,并将其移动到“进行中”,我们就可以开始了。
2.设置编写代码
有了应用程序的功能性本地版本和我们自己的任务,是时候编写一些代码了。
选择 IDE
为了在 MP-910 上工作,你需要编写代码,而要编写代码,你需要一个文本编辑器。
更传统的开发者使用内置于他们终端的编辑器,比如 vim 或者 emacs 。看着精通此道的人会令人眼花缭乱,因为他们敲击出晦涩难懂的键盘快捷键,手不离开键盘就能快速浏览文件。
然而,学习曲线非常陡峭,我强烈建议在编写软件的第一年避免尝试学习这些工具。
相反,你应该使用集成开发环境,或 IDE ,它是你电脑上专门用于编写代码的独立应用程序。它们有很多导航和操作代码的有用特性,并且它们通常由社区编写的丰富的插件生态系统支持,这些插件增加了有用的功能,如文件预览和代码林挺(稍后将详细介绍)。它们通常内置了终端和版本控制集成,因此您可以在单个应用程序中编写、运行和提交代码。
如果你正在写 Python,那么我会推荐 PyCharm ,它拥有最好的 Python 代码导航。Java 对应的是 IDEA ,也很优秀。

Pycharm 是一个很棒的 Python IDE。您可以在不离开编辑器的情况下提交代码并将其推送到版本控制。来源: JetBrains
对于其他或多或少的东西,我建议使用 Visual Studio 代码,它非常可扩展、快速,并且越来越受欢迎。你也可以在浏览器中使用它,所以它给了你一个非常便携的工具箱。
设置自动棉绒机
林挺是一个有趣的词,与你在肚脐上发现的绒毛毫无关系。
它描述了以一致的方式格式化你的代码,使得其他人更容易阅读。它本质上是一套规则,应该遵循这些规则来保持事物的标准化。这些规则因语言和组织而异,但大致如下:
- 类名应该大写:
class MyClass而不是class my_class - 引号应该是双的,不是单引号
const name = "Archy"不是const name= 'Archy' - 最大行长度,以保持代码简洁易读
不遵守这些准则很容易激怒你的同事。许多公司会运行一个 linter ,检查你的代码是否符合他们的规则,防止你提交不符合规则的代码。
为了使每个人的生活更容易,我建议安装一个代码格式化程序,你可以配置它来自动格式化你的代码,使其符合你公司的指导方针,并通过 linter 检查。
下面是来自最受欢迎的 Javascript 格式化程序之一pretty的介绍,它很好地表达了格式化程序的优点:

来源:更漂亮(麻省理工学院许可)
更漂亮的工作得非常好,并与 ESLint 和 TSLint 等棉绒集成。
如果你是 Python,我建议用黑色。您可以将它作为 PyCharm 的插件安装,然后添加一个键盘快捷键来格式化您的代码。这是魔法。
使用 IDE 执行和调试代码
大多数 ide 都包括执行您正在编写的代码的功能。
正确设置这一点是值得的,因为能够快速获得关于代码是否编译或运行无误的反馈非常有帮助。这是另一个你应该尽快完成的反馈循环。
第二个很大的优势是,这使您可以访问可视化调试器,允许您暂停代码执行并检查代码状态以识别问题。这比print或者console.log好多了。

调试器允许您暂停执行并检查程序在特定点的状态。来源: Visual Studio
测试您的代码
与学校或学术界最大的区别之一是,公司不指望你的代码是正确的,因为它看起来是正确的:他们指望你用测试来证明它。
每当有人试图对代码库的主体进行修改时,以及在部署新代码之前,都会运行这些测试。他们确保一切按预期运行。软件测试的基本格式如下:
def test_when_country_is_England_then_language_is_English():
lang = get_language_for_country('England')
assert lang == 'English'
这是一个非常好的主意,因为:
- 编程时很容易出错:自动化测试可以防止这一点
- ****自动化测试允许我们更自由地编辑代码库,因为我们已经有了一套警报,它会告诉我们在 A 处的改变是否影响了我们不知道的 B 处的一些功能。这让我们可以更快地行动,更快地度过那该死的生命周期。
- 测试提供了软件如何工作的“活”文档:设计良好的测试描绘出信息在系统中流动的主要路径。阅读测试通常比阅读文档更容易弄清楚事情是如何工作的。
- 测试强化了某些良好的编程习惯:它迫使你将代码组织成小块,并鼓励你尽可能将不同的功能块分开。如果你写意大利面条式的代码,你会发现它很难测试。
而且,作为事务所的新手,你可以通过写好测试,大大增加同事对你第一批变革的信心。验证测试的逻辑通常比底层实现的逻辑更容易:一组编写良好的测试会告诉你的同事
我已经仔细考虑了这件事可能出错的方式,并且我已经编写了测试来确保它们不会出错
正如在下面的代码评审中所讨论的,你的同事是忙碌的人,容易受到压力、愤怒和不宽容的影响。通过编写好的测试,你可以让他们的生活变得容易得多。反过来,他们更有可能提供高质量的、有趣的反馈,比如“你可以重构这部分来提高时间复杂度”,而不是无聊的反馈,比如“我不知道这是否可行,写一些测试”,或者“这真的可行吗?!"。
3.将代码推送到版本控制
好了,现在你可以让这个按钮变大了!您可以在本地运行项目,并使用调试器来修复问题,同时根据公司的规范自动格式化您的代码。太好了。
还记得我们为了获得当前版本的代码而进行的回购吗?我们还使用 git 来记录我们对代码库的更改,并将它们提交给团队的其他成员进行审查。我们将通过从main分支中检出一个新分支,添加一些commits,然后打开一个pull request,在merging该工作返回到main branch之前,我们在其中要求同事提供反馈。

基本的工作流程。我们为我们的特性创建一个新的分支,并在合并回主分支(白点)之前添加提交(黑点)。来源:作者
最终,您的工作将形成对代码库的一系列原子变化的一部分,这些原子变化层层叠加,形成一个(有希望)伟大的产品,其中包含代码如何随时间变化的信息历史。确保你在故事中的角色以一种让你自豪的方式被写出来是值得的。

这是 pandas 的 git 历史,一个流行的 Python 数据分析库。这里的每一行都是到master分支的commit,以及关于提交引入了什么变化的消息。master中的当前代码是依次应用所有这些更改的结果。来源:Github 上熊猫的作者截图。
基本 git 命令
你需要对git有一些基本的精通,以便为协作代码库做出贡献。Git可能会让人不知所措,但是已经有很多好的内容了:
- Github : Git 手册
- 罗耀拉·玛丽蒙特大学:Git 简介
- 自由代码营:什么是 Git,如何使用
您需要很好地理解的命令有:
branch-创建代码的副本,这些副本将彼此独立发展commit-你改变了一些东西的一大块工作add-向特定提交添加修改或新文件pull-从服务器抓取代码的变更push-将您的本地更改复制到服务器上
尽管这可能令人畏惧且不直观,但好消息是git是你的保险单。这是一个安全网,它使得你不太可能永久性地破坏什么东西。这意味着你可以自由地四处乱搞,尝试一些东西,为自己打破一些东西,而不会影响到团队的其他成员。
以下是最简单的两步工作流程:
1.在分支机构中组织您的工作
分支是一个核心git概念,它描述了代码的副本,可以独立于其余部分对其进行更改。大多数软件仓库都有一些特殊的分支,你不能直接将代码推送到这些分支,比如主库、主库、生产库、或开发库。不同的组织在版本控制中采用不同的工作组织策略。
在所有情况下,目的都是将稳定的代码体与新的、不稳定的功能分离开来,一旦通过了必要的审查和测试步骤,新的、不稳定的功能就会被合并到主代码库中。这是 Github 流程中概述的基本模型。

简单的分支模型。顶部的直线是main分支。我们分支到一个特性分支,添加一些提交(白色圆圈),打开一个拉请求(带箭头的大圆圈),然后在审查(语音气泡)之后它被部署(松鼠,出于某种原因),并再次合并到main分支。来源: Github 流量
为了做出改变,你需要自己的分支。有多种分支命名约定:一些公司使用前缀 feature 或 bugfix 来表示分支的类别。找出惯例,从主分支中取出一个分支,开始你的第一个任务:
git checkout -b feature/MP-910-make-button-bigger
那个MP-910是对你选择的任务的引用。通过将它包含在分支名称中,您给了您的版本控制和任务管理系统一个交流的机会,并在代码被推送时自动转移票证(希望您公司中的某人已经设置了这一点——如果没有,这对您来说是一个很好的小任务😉).
2.将您的工作拆分为多个提交
一个提交是对现有代码的一组变更,与解释变更背后的逻辑的消息相关联。
理想情况下,您的提交代表了您完成任务过程中的直观步骤。有时在开始之前将工作分解是有帮助的,然后以此作为提交的指导。
用提交来组织你的工作有什么意义?
首先,如果您的工作引入了一个 bug,那么很容易通过一系列的提交来找出问题是在什么时候引入的。
其次,如果在某个时候你想重用你的工作的一些而不是其余的,你可以把你想要的提交放到别的地方(这叫做<a href="https://git-scm.com/docs/git-cherry-pick">cherry-picking</a>,这是一个有点高级的话题)。
第三,也是最重要的一点,提交描述了代码更改的逻辑,将您的工作分解成一系列更容易被他人(以及您未来的自己)理解的小块。
在我们的例子中,我们想让一个按钮变得更大,这可能只需要在一个地方添加代码。但是我们实际上可能需要做两处修改,一处是按钮,另一处是自述文件中描述有问题的按钮的截图。在这种情况下,最好将工作分成两次提交:
(43a1r1) Increase sign up button size to accomodate new text (71282e) Update README screenshot to show new button
括号中的数字是提交的散列——Git 用来标识提交和处理合并工作的唯一标识符。这些句子是提交消息,用简单的术语描述了提交中所做的工作。
编写好的提交消息是一种艺术形式,但是有三个要点:
- 以大写字母开始
- 用现在时写:“增加”,而不是“增加”。这描述了当某人将与该提交相关的变更应用到代码时会发生什么
- 试着传达你正在做的事情的意图,而不是技术。人们可以自己阅读代码的变更:提交消息允许你捕获关于为什么你要做那些变更的额外细节
更多来自 Chris Beams 的良好提交消息。
好了,现在您已经准备好提交了!
4.人们如何评价我的作品?
在您对代码所做的编辑被合并到项目的主分支之前,您的同事会希望对其进行检查。
代码评审是一个非常有用的练习,有助于保持代码质量,让每个人都知道代码库是如何开发的。对你来说最重要的是,这是一个向同事学习的好机会。
与大多数专业情况不同,在代码审查期间,人们真的会受到激励,给你高质量的反馈。这是因为你正在编写和编辑他们将来可能会用到的代码,所以确保它是好的符合他们的利益。
连续累计
你必须满足的第一个审查者通常是自动化的:一个所谓的持续集成系统,如 Jenkins 、 CircleCI 或 Github Actions 。这个名字是对自动化测试给了我们更高的信心,我们的改变不会破坏任何东西,允许我们走向连续的(而不是零星的)部署。
这些检查代码可以被成功地构建和测试。通常,版本控制系统是这样配置的,你不能合并没有通过这些检查的代码。

一个已经建立并测试成功的公关,来源: Coletiv
有些系统还会为您创建一个测试部署。这是一个非常好的特性,允许您的评审人员实际操作您的代码,而不需要他们在本地进行设置。

查看 Heroku 上的应用程序。左侧的框对应于打开拉请求时创建的临时部署。来源: Heroku 作者截图。
提交您的代码以供审查
完成你的第一次代码审查可能会非常紧张。尽量不要将任何反馈视为针对个人,如果人们发现了你忽略的错误,也不要感到惊讶——另一双眼睛通常非常有用。
这里有几个要点,可以让你的第一次代码评审轻松愉快,而不是伤痕累累:
记录你的公关
从简洁的描述开始,你为什么要做这样的改变,以及代码评审者需要了解的任何额外的背景。
如果有你想过但没有做的事情,这里可以是一个很好的地方,这样评论者就不会浪费时间写“你想过……”了。
如果你想改变某样东西的外观,可以附上一张截图,甚至是一段视频,说明这件东西是如何改变的!我们试图让评论者的生活变得简单。不要让他们检查你的分支,看看改变 10px 的利润对按钮的影响!

尽可能在你的公关中加入更多的背景信息:甚至可以是一段视频。织机是一个有用的工具。来源:织机
跑起来
如上所述,在提交代码以供审查之前,自动格式化代码确实很有帮助。
这将捕获许多愚蠢的错误——如注释代码、未使用的导入或奇怪命名的变量——否则只会让您的评审者感到沮丧。
检查所有东西都在本地运行
这听起来很愚蠢,但是你会惊讶于我们经常没有正确地检查代码是否在本地运行。
特别是如果您正在使用一个多组件系统——前端、后端和数据库——请务必检查所有组件是否如您所期望的那样很好地协同工作。
检查测试是否通过
另一个令人惊讶的常见错误是无法在本地运行测试。
然后你推你的代码,去做别的事情,30 分钟后意识到当持续集成系统试图运行测试时,测试失败了。此时,您正在做其他的事情,并且评审您的代码已经滑落到评审者的任务列表的底部。
回顾你自己的公关
您的版本控制系统将显示您的拉取请求,并突出显示您所做的更改。
自己看看这些真的是个好主意!我经常发现我无意中更改了一个文件,或者在拉取请求中添加了一个不相关的文件。
有时候,从评审者的角度来看你的代码有助于你发现澄清或清理代码的变化。站在评审者的角度,可以帮助你更客观地评估自己的工作。

Git diffs:红色表示删除,绿色表示添加。你可以在 Github 上以统一或者拆分的风格查看这些。来源: StackOverflow
审查他人的代码
当谈到审查别人的代码时,试着记住提交自己的代码来审查是多么可怕,以及即使是最温和的建议也是对你作为一个人的价值的根本挑战。
关于人性化代码审查的一些建议:
- 忌尤。试试“我们少了一个逗号”而不是“你少了一个逗号”
- ****短语为建议,而非指示。试着说“也许这样会更好”,而不是“你应该改变它”
- ****有建设性。如果你建议改变,考虑提供一段工作代码,做你所建议的“如果我们使用字典代替像
a = dict(A=1)那样会更有效率” - ****错了还是不一样?有时候人会以一种你意想不到的方式完成一项任务。膝跳反应就是告诉他们这一点。在你这样做之前,考虑一下他们的方法是否真的更差,或者只是与你的想法不同。
- 使用表情符号来缓和你批评性评论的语气🤨 🙋💡
- ****在代码评审中,并不是所有事情都必须是负面的!如果你认为某件事很酷,或者你学到了什么,说出来。
- ****可以提问!审查不必是单向的——编写代码的人可能非常乐意深入解释他们发现的将时间复杂度从 O(n)降低到 O(1)的简洁方法——利用这一点。
更多提示,请查看谷歌关于代码审查的工程最佳实践。
最后,记住修复一个坏的功能比修复一段坏的关系要容易得多。尤其是如果你是一名工程师。
5.部署
部署是橡胶上路的地方:您的代码成为向用户公开的产品的一部分。
这通常是一个两阶段的过程。大多数公司都有一个或多个阶段环境,这允许你在将你的变更发布到外面的世界之前,检查它们在沙箱中的表现。一旦您在试运行中测试了功能,它就会被部署到生产中。

典型的部署流程。通常,阶段化将被称为“dev”,或者“开发环境”。在一些流程中,staging 与一个 git 分支(通常是develop)相关联,而 production 与另一个分支(main或master)相关联。来源:作者
持续部署
上面讨论的持续集成的兄弟是持续部署。这是代码部署的自动化:当您将一个 PR 合并到一个给定的分支时,代码被部署到一个指定的环境中。
这通常由与您的 CI 相同的服务来处理,可以是您的版本控制系统(例如 Github)、第三方服务(例如 CircleCI)或您的托管环境(例如 AWS)。
托管环境
当人们谈论软件被部署在“云”上时,他们实际上是指软件被部署在一个大型数据中心,该数据中心由一家不用付很多税的公司所有。
亚马逊网络服务(AWS)和谷歌云平台(GCP)最受欢迎,微软 Azure 和 Heroku 分别受到老牌公司和业余爱好者的青睐。
替代方案是本地或本地部署,其中代码在特定的命名数据中心运行——这种情况很少见,通常只用于非常敏感的环境(例如医疗保健)或有不寻常的硬件需求时(例如深度学习)。
如果你幸运的话,在你的第一个角色中,你不需要与主机环境纠缠太多。配置和维护部署环境本身就是一门艺术,由此产生了开发运维规程,其中 50%的人穿着西装革履的顾问谈论如何提高速度,而 50%的人穿着 Metallica t 恤衫,他们有一种超自然的能力来找出您的构建失败的原因。
概括地说,你的主机环境将会帮助你:
- 部署东西:把运行在你电脑上的代码放在某个数据中心运行,通常是在一个 Linux 实例上或者作为一个 Docker 容器。
- ****让其他人也能访问到它:将它公开到网络上,并允许你将用户路由到它。当你有许多不同的服务时,你将使用工具来整齐地组织它们与互联网的接口,比如虚拟专用云和 API 网关。
- ****但不是所有人:你的托管环境通常会帮助你认证你的用户并管理他们的权限。
- … 有时会超出您的预期:云计算的一大优势是可以轻松扩展您的服务来满足更多需求。如果你的信用卡额度足够高,这可以自动处理——这就是所谓的自动缩放。
结束循环
邦扎。您的代码现在已经部署好了。如果任务管理自动化还没有为您完成,您可以从审核中的移动您的票证- >完成!
希望现在的产品稍微好一点,你也稍微聪明一点。

记下你在每个冲刺阶段学到的东西会很有帮助。第一个教训是真实的;新工作开始几周后,我设法意外地创建了一个名为~的本地文件夹,然后在尝试删除目录的过程中擦除了我的整个主目录。来源:作者
在完成这项工作时,考虑记下你学到的任何东西。当你遇到以前见过的问题时,日志可以派上用场,它为面试中那些讨厌的行为问题提供了大量的例子。
最后,如果有机会,试着演示你的作品。这可能只是对你的开发同事,对公司的其他人,甚至(理想情况下)对客户。这不仅可能会带来一些有用的反馈,而且解决某人的问题,然后向他们展示解决方案也是一种满足感。
希望你从演示工作中获得的兴奋感能让你立刻去买下一张票。
进一步阅读
我希望这篇指南已经给了你足够的心智模型,让你可以开始写代码,并从你的同事那里积累反馈。
如果你想了解更多关于如何在你的第一个科技职位上取得成功的信息,我建议下一个话题是科技公司及其团队是如何工作的。
以下是我喜欢的一些作品:
- 务实的程序员:经典。有用的编程技巧,但关于如何理解你在公司中的角色以及如何与你的同事和用户互动的笔记更有价值
- 不要称自己为程序员:这是一篇关于如何把自己想象成解决商业问题的人,而不是代码猴子的精彩文章
- 返工:Basecamp 可能不像其创始人在本书中描述的那样美好,但我仍然认为这些理智、冷静、高效团队合作的原则是正确的
- Rands In restes:一个伟大的科技博客,里面有大量的战争故事和享受科技生活的建议
如果你对我写的其他东西感兴趣,你可以在 Twitter 或我的博客上找到我。
原载于 2021 年 8 月 16 日【https://deberker.com】。**
Power BI 中的最小可行流仪表板:第 1 部分
关于开发实时流仪表板及其在 Power BI 中的用例的三部曲。

传统仪表板。图片由作者提供。
我们大多数人都熟悉传统仪表板的概念,它是关键指标和图表的汇编,显示出来为决策提供有用的信息。
这些控制面板中的大多数都由底层数据集提供支持,这些数据集以预定的时间间隔(每周、每天或每小时)刷新。
在任何给定的时间点,仪表板都为您提供静态信息的快照,这些信息将在未来的某个时间被刷新。而且这种设计模式已经被证明是普遍存在的。
然而,利益相关者有时会要求仪表板能够显示几分钟甚至几秒钟前生成的数据。
一个示例场景是监控关键医疗设备的现场工程师。为了捕捉机器停机时间,并立即做出反应以最大限度地减少对医疗服务的中断,工程师需要能够查看实时数据馈送。
输入功率 BI。Power BI 简化了我们使用无代码/低代码方法实现该解决方案的流程。
在这个由三部分组成的系列中,我将介绍如何在 Power BI 中设置流式仪表盘,以便您可以实现自己的流式仪表盘。
配置数据集
要显示流数据,首先需要考虑的是为仪表板提供动力的数据集。
Power BI 可以利用两种不同类型的数据集,它们是为在实时仪表盘上显示而设计的——流数据集和推送数据集。
就其核心而言,配置这两个数据集的方法几乎相同,主要区别在于,流式数据集将数据存储在临时缓存中,该缓存会很快过期,而推送数据集将历史数据存储在由 Power BI 创建的数据库中。
在本文中,我们将主要关注流数据集,并浏览一个简短的演示教程。然后,我们将看看这种解决方案发挥作用的一些用例及示例。
在本系列的第 2 部分中,我们将向前推进,探索流数据集的近亲——推送数据集。
流式数据集:利弊
如前所述,对于流数据集,数据存储在临时缓存中,很快就会过期。数据集只能存储过去一小时内生成的信息。
如果没有底层数据库来存储您的数据,您将无法在 Power BI 中使用流数据集创建报告。您只能创建仪表板拼贴。
因此,Power BI 报告中的过滤等功能不可用。
与使用推送数据集相比,这种设计的优点是仪表板平铺显示以平滑的动画更新。数据生成和可视化之间的延迟也降至最低,这有助于显示您想要监控的关键应用程序或设备生成的信息。

仪表板平铺中的平滑动画。Gif by 作者。
在数据流进入 Power BI 之前,您可以执行简单的聚合,比如平均值,这样您就不仅仅局限于数据源生成的原始数据。
缺点是不能进行历史数据分析。特定时间范围或维度的筛选、DAX 表达式、穿透钻取或工具提示功能等报表功能不可用。
您可以对数据执行的聚合类型也是有限的。具有多个表连接和筛选器的复杂聚合会给仪表板带来延迟。显示复杂指标的代价是仪表板性能不佳。
如果需要复杂的聚合,您可以考虑每小时刷新一次的推送数据集或仪表盘作为其他选项。
演示:PubNub 数据流
有两种方法可以将 Power BI 仪表板连接到流数据集:
- 从现有的 PubNub 数据流中读取数据。
- 将数据推送到 Power BI REST API。
在本文中,我们将利用来自 PubNub 数据流的数据,在第 2 部分中,你将看到后一种方法的演示。
创建超级商务智能帐户
在我们为演示创建数据集之前,请使用下面的链接访问 Power BI 网站,并使用您的工作或学校电子邮件地址注册 Power BI 帐户:
https://powerbi.microsoft.com/en-us/get-started/
无需下载 Power BI Desktop,因为我们无法使用流数据集创建报告。因此,在本教程中,我们只使用 Power BI 服务。
添加流数据集
现在,我们可以继续创建我们的流数据集。首先,在主页上点击我的工作区,创建一个新的仪表盘。
将新仪表板命名为 PubNub 传感器集群。

在 Power BI 中创建新的仪表板。图片由作者提供。
成功创建仪表板后,您应该会看到一个空白画布,我们可以在其中设计流式仪表板。所以我们来添加一个磁贴。
继续点击工具栏中的编辑按钮,然后添加一个图块。
在出现的右侧窗格中,我们可以选择流数据的来源。选择自定义流数据,点击下一步。
继续添加流数据集。

选择自定义流数据源。图片由作者提供。
选择 PubNub 作为我们的数据源,点击下一步。
这将告诉 Power BI 您想要从现有的 PubNub 流中读取数据。
请注意,PubNub 没有将数据推送到 Power BI 服务——我们只是从 PubNub 的 API 中读取数据。

PubNub 流数据源。图片由作者提供。
填写以下字段:
- 数据集名称: PubNub 传感器网络
- 子键: mspowerbi
- 通道名称:传感器网络
- 您可以将 PAM 身份验证密钥字段留空。
单击 Next 后,您将看到 PubNub 传感器网络数据集的模式和列名,后面是 JSON 格式的数据结构。这包含将由 Power BI 处理的数据。
继续并点击创建按钮来完成创建您的第一个流数据集。
现在,您将回到仪表板画布。
再次点击编辑按钮并且添加一个图块。
选择自定义流数据,您将看到您刚刚创建的数据集可用于创建仪表板图块。

JSON 格式的 PubNub 传感器网络数据结构。图片由作者提供。
设计串流仪表板
这很容易,现在剩下要做的就是通过添加磁贴来设计我们的仪表板。
使用您之前创建的数据集,用以下信息创建一个卡片视觉:
- 场:奈.噪
- 数值小数位: 1
- 标题:钦奈的噪音
- 副标题:分贝
您最终应该得到一个类似下图的数据卡,数据从 PubNub API 实时传输到您的仪表板。

钦奈的噪音(卡片视觉)。Gif 作者作者。
对 Novato、Redmond 和 San Francisco 重复上述步骤。
完成后,您的仪表板应该有单独的卡片显示来自 4 个不同国家的噪音量。

不同国家的噪音水平。Gif 作者作者。
接下来,用以下信息创建一个仪表视觉:
- 值:檀香山.噪音
- 最小值: Novato.noise
- 最大值:钦奈噪声
- 标题:檀香山噪音
- 字幕:相对于诺瓦托和钦奈
然后,继续创建一个簇状 柱形图可视化。
- 值:选择所有以温度为后缀的列。C 。这将把每个国家的摄氏温度添加到视觉效果中。
- 标题:各国温度
- 副标题:摄氏度
将鼠标移到簇状柱形图右下角的小箭头上。
单击并拖动箭头以控制聚集柱形图视觉效果的大小。
调整视觉效果的大小,使整个图例可见。

具有仪表和聚集柱形图视觉效果的仪表板。Gif 作者作者。
最后,创建一个折线图视觉。
- 轴:时间戳
- 值:选择以湿度为后缀的 Redmond 和 San Francisco 列
- 标题:雷德蒙对旧金山
- 字幕:湿度%
调整视觉效果的大小,如下所示:

带折线图视觉效果的最终仪表板。Gif 作者作者。
用例与真实场景
拥有流式数据集的最大优势是其极低的延迟。
但是,由于流数据集的局限性,即缺少过滤器、工具提示、穿透钻取等,如果您的企业希望跟踪需要在检测到关键事件后立即解决的关键事件,而不需要对数据执行复杂的计算,则应考虑实施此类解决方案。
真实场景的示例包括但不限于:
- 网站可靠性工程—通过监控响应时间和修复加载缓慢的页面,提供流畅的用户体验。此外,通过响应服务器上过载的请求,防止 web 和移动应用程序停机。
- 物联网传感器摄取-测量给定区域的人数以促进资源规划,并提出防止过度拥挤的措施,例如在公共交通上。
- 车队维护——对自动车辆故障做出快速反应,例如送货车辆、电动汽车等,以建立强大的供应链,并确保高质量的客户服务。
- 关键机器—快速维修机器故障,例如工厂机器,以确保高生产质量。
- 数据中心运营-维护数据中心机器,以确保它们按需提供 IaaS 和 PaaS 服务,并满足预定义的客户 SLA。
- 金融交易—实时查看证券数据,以便金融专业人员做出买入/卖出决策。
结论
流媒体技术服务
在过去的十年中,流媒体技术有了很大的发展。如果没有经验丰富的开发人员的帮助,这样的仪表板在几年前是很难构建和维护的。
然而,现在有许多新兴服务使每个人都能够轻松地构建流媒体应用程序。
如您所见,业务用户现在只需点击几下按钮,就可以自行构建流式仪表盘,无需太多系统管理和维护。
本文中使用的演示最初是由 PubNub 在其官方网站上编写的,因此我强烈建议您也查看他们的文章以供参考。
https://www.pubnub.com/blog/microsoft-power-bi-streaming-business-data-to-dashboards/
PubNub 示例数据流
我们刚刚探索了 PubNub 提供的众多数据流中的一个。如果你对用流技术构建更多应用的前景感到兴奋,今天就在他们的网站上探索 PubNub 的 API 吧。
https://www.pubnub.com/developers/realtime-data-streams/
数据集的范围从 Twitter 互动到股票市场数据,再到维基百科的文章,所以请随意使用 PubNub 的数据流之一来构建您的下一个项目。
在那之前,我们将在第 2 部分中见面,届时我们将讨论推送数据集,以及如何将数据推送到 Power BI REST API。
Power BI 中的最小可行流仪表板:第 2 部分
具有推送数据集和 Power BI REST API
欢迎回到最小可行流仪表板系列的第 2 部分,在这里我们继续讨论实时仪表板以及如何在 Power BI 中配置它们。
回到第 1 部分,我们讨论了流数据集以及它们如何利用临时缓存来存储数据。数据过期快,导致历史数据分析无法进行。
然而,在本帖中,我们将探讨如何配置推送数据集。您将会看到更多的功能可供我们使用,包括过滤、工具提示、穿透钻取等等。
将数据推送到 Power BI REST API
Power BI Rest API
使用 Power BI REST API 将数据推送到 Power BI 数据集。
API 包含一组服务端点,使开发人员和 Azure 服务能够在其上构建应用程序。尽管我们今天不会明确地调用这些端点,但有必要理解,您可以利用这个特定的操作集在 Power BI 中创建您自己的推送/流数据集。
这就是 Azure 服务(如 Azure Stream Analytics)与 Power BI 数据集交互的方式。
推送数据的配置方法
1。API
有两种方法可以将数据从数据源推送到 BI 的 REST API。
第一种是使用 Power BI 服务中提供的 API 选项。

Power BI 服务中的 API 选项。图片由作者提供。
当您单击 API 时,您将看到一个屏幕,您可以在其中命名 Power BI 数据集,并设计它的 JSON 模式。

设计数据集。图片由作者提供。
注意历史数据分析 默认为 关。
这是一个开关,您可以使用它在创建流数据集和利用两种模式下提供的功能的推送+流数据集之间切换。
关闭切换只会创建一个流数据集,其工作方式类似于我们在第 1 部分中读取数据的 PubNub 流。
唯一的区别是,对于 PubNub 来说,Power BI 仅仅是从 PubNub 的 API 中读取一个数据流。

Power BI 创建的推送 URL 端点。图片由作者提供。
您可以将数据推送到 REST API,而无需通过 Power BI 服务的 UI。但是请记住,您必须授权您的应用程序使用权限来对您想要创建数据集的 Power BI 工作区进行更改。
这是一个值得专门撰写博客的话题。
今天,我们将主要关注将数据推送到 Power BI REST API 的第二种方法,使用 Azure Stream Analytics。
2。Azure 流分析(ASA)

Azure Stream Analytics 徽标。图片由微软提供。
Azure Stream Analytics 是一个托管的 PaaS 服务,运行在微软的 Azure 平台上。如果你以前使用过 Spark 的结构化流媒体引擎,那么 ASA 引擎非常相似。
ASA 作业是用类似 SQL 的查询语言编写的,这是任何使用过数据库的开发人员都能理解的通用语言。
该服务的设计理念与 Spark 的结构化流项目高度相似,即作为服务的消费者,您不必担心事件流处理的复杂性,即延迟数据到达时间、数据无序、事件交付保证。
您应该能够告诉 ASA 您想要的输出,用 SQL 编写您的查询,就像您正在运行一个批处理查询一样,服务会计算出如何将该查询转换成可以在流上运行的版本。
演示:事件流
这是我们今天要构建的应用程序的示意图。

演示应用程序架构图。图片由 作者 提供。
对于我们的事件数据源,我们将使用一个简单的 Python 脚本向事件中心发送事件。
所以,废话不多说,让我们从 Event Hub 开始,创建我们的应用程序所需的资源。
为事件摄取创建事件中心名称空间
我们必须创建的第一个资源是一个事件中心名称空间,用于接收所有事件。
在此之前,请前往 Azure 并注册一个免费试用版。你可以获得 200 美元的免费信用点数,在 30 天内探索 Azure 服务。

Azure 免费试用注册屏幕。图片来自作者。
如果你目前是学生,你可以获得价值 100 美元的额外 Azure 点数。
1。创建事件中心名称空间
注册完成后,在门户的搜索栏中搜索事件中心,单击事件中心,并填写以下属性:
- 订阅:选择您的默认订阅
- 资源组:创建新的,并为资源组选择一个名称
- 名称空间名称:为事件中心名称空间选择一个名称
- 位置:选择离你最近的地区
- 定价层级:标准
- 吞吐量单位: 1
- 启用可用性区域:未选中
- 启用自动充气:未选中

创建事件中心命名空间。图片由作者提供。
事件中心名称空间充当多个事件中心实例或 Kafka 术语中的主题的管理容器。
事件发布者可以使用基于 Azure Active Directory 的身份验证和授权,或者使用共享访问签名(SAS)将事件推送到每个主题。
2。添加 SAS 策略
我们需要一个 SAS 令牌来授权我们的 Python 应用程序向 Event Hub 发送事件。
因此,转到 Event Hub 名称空间中的 Shared Access Policies 窗格,添加一个包含以下详细信息的新策略:
- 策略名称:选择策略名称
- 管理:勾选

添加事件中心命名空间 SAS 策略。图片由作者提供。
复制为您创建的连接字符串-主键,并将其保存在方便的地方,我们将在稍后的 Python 脚本中使用它来向事件中心发送 web 事件。
3。创建一个活动中心
将鼠标悬停在“事件中心”窗格上,并使用以下属性创建一个事件中心实例:
- 名称:为事件中心实例选择一个名称
- 分区计数: 1
- 消息保留: 1 (如果您需要一天以上的时间来完成本文中的步骤,请随意设置一个更长的保留期)
- 捕捉:关

创建事件中心实例。图片由作者提供。
用 Python 编写事件生成器
我们今天要使用的代码可以在这个 Github repo 中找到。
1。安装 Python 3
在你的系统上安装 Python 3 。确保您使用的版本至少为 3.6 及以上。我用的是 3.7.9 版本。
2。安装 Visual Studio (VS)代码编辑器
安装您的 Linux / Windows / MacOS 版本的 Visual Studio 代码。
然后,在您的目录中创建一个项目文件夹。我把我的命名为 事件流 ,但是你可以随意命名你的。
3。列出所需的 Python 包
继续,打开刚才在 VS 代码中创建的文件夹。创建一个requirements . txt文件。将 Github repo 中的代码复制并粘贴到您的文件中。

必需的 Python 包。图片由作者提供。
4。编写模拟随机网络事件的 Python 代码
完成后,创建一个send _ events . py文件。将 repo 中的代码复制并粘贴到文件中。
在您的终端中,运行以下命令:
pip3 install -r requirements.txt
这将安装包我们指定在requirements . txt文件前面。
如果 pip3 命令失败, cd 到 VS 代码终端中您电脑上Python/Python 37/Scripts文件夹中的确切 pip3 文件路径。

Python 脚本模拟随机网络事件。图片由作者提供。
5。运行 Python 文件将事件发送到事件中枢
在运行文件将事件推送到事件中心之前,将连接字符串-主键粘贴到 <连接字符串> 参数中。
同样,在 <事件中枢> 参数中填写您的事件中枢主题名称。
完成这些步骤后,右键单击代码编辑器中的任意位置,并选择在终端中运行 Python 文件。
从 VS 代码编辑器创建 Azure 流分析作业
1。安装 Azure 流分析工具扩展
在 VS 代码中,点击扩展标签,搜索 Azure Stream Analytics 。
安装 Azure 流分析工具扩展。
作为依赖,还为我们安装了 Azure 账号扩展。这个扩展使我们能够从 Azure Portal 中检索数据,并将我们的 ASA 作业发布到门户。

在 VS 代码中安装 Azure 流分析扩展。图片由作者提供。
2。创建新的流分析项目
在 Visual Studio 代码中,导航回资源管理器选项卡。
使用 Ctrl+Shift+P / Cmd+Shift+P 打开命令面板。
然后,在搜索栏中输入 ASA。
选择 ASA:创建新项目,输入您的项目名称。我把我的矿命名为。
3。定义输入数据
展开输入文件夹,打开 input.json 文件。我们将使用这个文件来指定一个输入数据流,我们的 ASA 作业可以从中读取数据。
在编辑器窗口的左上角,点击添加实时输入按钮。选择活动中心,然后从您的 Azure 订阅中选择。
给你的输入流起个名字,我命名为 mineevent-hub-stream。

将实时输入流添加到流分析作业。图片由作者提供。
然后你可以使用新的**和。在输入文件夹中为您创建的 json* 文件填充您的事件中枢连接设置,通过点击交互按钮。***

配置事件中心实时输入流。图片由作者提供。
点击预览数据位于**左上角。json*** 文件,以便了解我们之前使用 Python 脚本发送到 Event Hub 的数据。*

预览事件中心数据。图片由作者提供。
请注意,输入流中的所有数据都被视为字符串。对于其中的一些列,例如event _ timestamp,我们将在下一步中使用 ASA 查询将它们的数据类型更改为 datetime 。
4。编写流分析查询
中的。asaql* 文件,插入来自 Github repo 的 SQL 查询。*
如果您希望修改查询,可以在这里找到流分析查询语言提供的功能的综合参考。

流分析 SQL 查询。图片由作者提供。
5。定义输出接收器
执行与步骤 3 中相同的过程,为 ASA 作业创建要写入的输出接收器。
展开输出文件夹,打开 output.json 文件。
点击添加输出,选择 Power BI ,然后从你的 Azure 订阅中选择。
给你的输出起个名字,我的是 事件-中枢-数据集 。
然后你可以使用新的 。在 Outputs 文件夹中为您创建的 json 和文件,用于填充您的 Power BI 工作空间和数据集信息。
记下 GroupId uuid 值。该值唯一标识您选择的 Power BI 工作空间。我们稍后将使用它向 ASA 作业授予写入该工作区的权限。

为流分析作业配置 Power BI 输出数据集。图片来自作者。
注意我的工作空间选项在 VS 代码中不可用(虽然在 Azure Portal 中创建 Power BI 输出接收器时可用)。
因此,您必须首先在 Power BI 服务中创建一个工作区,然后在 VS 代码的下拉列表中选择工作区。
这需要你有一个专业版账号,或者免费试用专业版。
如果您没有 Pro 许可证,您必须将输出数据集工作空间设置在我的工作空间中,这是通过在 Azure Portal 中编辑您的 ASA 作业来完成的。
6。编辑您的 SQL 查询以引用您新创建的输入流和输出接收器
回到你的 。asaql 文件,并更改输出来反映你的 Power BI 输出 sink,更改输入来反映你的 Event Hub 流。
他们将各自的名字命名为 。被创建的 json 文件。

指定 SQL 查询以事件中心输入流为目标,并将数据写入 Power BI 数据集。图片由作者提供。
将 ASA 作业发布到 Azure 门户
1。提交 ASA 作业并创建服务主体
在你的**中。asaql*** 文件,并通过点击左上角的提交到 Azure 将您的作业提交到 Azure 门户。*
给 Azure 几分钟时间来完成作业的部署。
您可以在 Azure Stream Analytics 终端窗口中监控 ASA 部署日志。
部署完成后,转到 Azure Portal 查看您的新 ASA 作业。
点击管理身份面板,确保使用系统分配的管理身份在上设置为。

流分析应用程序的服务主体 ID。图片由作者提供。
Azure 已经为您的 ASA 应用程序自动创建了一个服务主体,以及一个与相关联的主体 ID ,用于在将数据写入 Power BI 工作区时进行身份验证。
复制主体 ID,我们将在下一步中使用它。
2。授予 ASA 工作贡献者对您的 Power BI 工作区的访问权限。
现在我们要做的就是授予 ASA 作业对 Power BI 工作区的写访问权限。
我们可以通过使用MicrosoftPowerBIMgmtPowerShell cmdlet 来做到这一点。
在您的计算机上打开 Windows PowerShell,并键入以下命令来安装 cmdlet:
*Install-Module -Name MicrosoftPowerBIMgmt*
然后,继续使用您的凭据登录 Power BI:
*Login-PowerBI*
输入以下命令,授予 ASA 服务主体 Contributor 对您指定的输出工作空间的访问权限。
将
**Add-PowerBIWorkspaceUser -WorkspaceId <group-id> -PrincipalId <principal-id> -PrincipalType App -AccessRight Contributor**
3。启动作业,并在 Power BI 工作空间中检查您的输出数据集
在 Azure 门户中,在 ASA 作业的概览窗格上,按 Start 运行作业。
导航到 Power BI 工作区,并验证数据集是否已成功创建。
如果没有创建数据集,您的事件可能已经过期,因为我们只将事件中心中事件的保留期设置为 1 天。
在这种情况下,再次使用send _ events . py文件发送 web 事件,然后验证事件是否已经写入 Power BI 数据集。
结论
在本文中,我们探讨了如何在 Power BI 中创建推送数据集。
我们从将 web 事件从一个简单的 Python 脚本接收到一个事件中心开始。然后,使用 Azure Stream Analytics 作为处理引擎,对数据输入流进行转换。
在第 3 部分中,我们将通过更多地谈论流分析及其成本模型来结束这一系列,并发布我们的第一份 Power BI 报告,其中包含我们在本帖中构建的数据集。
在结束之前,我想简单介绍一下当今架构中使用的服务组件。
事件交付保证
在使用流技术时,您应该注意一些警告,其中最重要的是事件交付保证。
ASA 保证至少向输出接收器传递一次,这保证了所有记录都被传递到 Power BI,但是数据集中可能会出现重复的记录。
在需要一次性交付记录的情况下,修改流分析查询,使用唯一标识符/主键来处理记录的重复数据删除。
流分析创建的 Power BI 数据集中的默认参数
ASA 使用推送数据集服务端点创建一个 Power BI 推送数据集,其 默认模式 参数设置为 推送流 ,从而产生一个数据集,该数据集可以利用我们在第 1 部分中看到的报告功能和平滑动画流切片。
ASA 还自动将数据集的retention policy参数设置为 basicFIFO 。有了这个设置,Power BI 中支持 Push dataset 的数据库存储了 200,000 行,这些行以先进先出(FIFO)的方式被删除。
因此,即使使用推送数据集,我们也无法分析跨越太远的业务历史数据。然而,这是对仅利用临时缓存来存储数据的流数据集的改进。
资源
我要感谢微软的贡献者,他们为我们在这篇博文中使用的技术写了很棒的文档。
如果您需要更多信息,请从下面列出的文章开始研究:
- 电力 BI 中的实时流
- 使用 Python 从 Azure 事件中心发送或接收事件
- 流分析查询语言参考
- 事件交付保证(Azure 流分析)
- Power BI REST API
- 使用托管身份认证您的 Azure 流分析作业,以支持 BI 输出
- 假网络事件| PyPI
同样,我们今天使用的所有代码片段都可以在提供的 Github 资源库中找到。在那之前,我们在第三部再见。
**https://github.com/natworkeffects/power-bi-streaming **
Power BI 中的最小可行流仪表板:第 3 部分
在 Power BI Desktop 中开发报告,并将报告发布到 Power BI 服务

使用推送数据集的最终功率 BI 报告。图片由作者提供。
欢迎回到最小可行流仪表板系列的最后一部分。
在本帖中,我们将介绍如何使用我们在第 2 部分中创建的推送数据集来构建一个 Power BI 报告。
为了提醒您,我们目前有一个 Azure Stream Analytics (ASA)作业在 Azure cloud 中运行,它将事件写入 Power BI Push 数据集。

应用程序架构图。图片由作者提供。
现在,我们剩下要做的就是通过连接到该数据集来开发我们的 Power BI 报告。
在 Power BI Desktop 中构建报告
在我们开始之前,请从微软网站下载 Power BI Desktop 。
下载如下所示的 64 位版本。

下载 Power BI 桌面。图片作者作者。
完成安装后,在 Windows 搜索栏中搜索 Power BI Desktop,右键单击应用程序图标,并将应用程序快捷方式固定到任务栏。
您应该会在任务栏上看到一个黄色的图表图标,如下所示:

Pin Power BI 桌面应用程序快捷方式到任务栏。图片由作者提供。
连接到推送数据集
点击任务栏上的应用程序图标,打开 Power BI Desktop。

打开 Power BI 桌面应用程序。图片由作者提供。
点击获取数据,使用您的 Power BI 账户登录。
在各种连接器的搜索栏中,搜索 Power BI 数据集。

Power BI 数据集连接器。图片由作者提供。
点击右下角黄色的连接按钮。
搜索您在第 2 部分中创建的推送数据集的名称。我的叫做 事件-中枢-数据集 。

点击创建按钮,建立与数据集的实时连接。
在最右侧的字段窗格中,您将看到由 ASA 作业创建的所有列。
在页面底部附近,您会看到一条消息,说明您已实时连接到 Power BI 推送数据集。

建立与 Power BI 推送数据集的实时连接。图片由作者提供。
Power BI 桌面简介
我会花一点时间来解释我们将在主报告选项卡上使用的元素。

用于报告开发的主报告选项卡。图片由作者提供。
这是报告选项卡,在这里您可以设计可视化,添加交互式过滤器,并为最终用户配置您的报告。
中间的大块空白被称为画布,是用户用来与发布的报告进行交互的主要界面。
最右边是前面提到的字段窗格。这是显示所有表格及其各自列的地方。
您可以看到,对于某些列,在列名旁边有一个求和符号。

表示值的默认汇总的求和符号。图片由作者提供。
每当您看到这种情况时,Power BI 就会在该列上应用默认汇总,只要它在 visual 中用作一个值。
为了更好地控制列的汇总方式,Power BI 中的最佳实践是创建明确的度量,定义我们希望如何对列进行汇总。
我们可以通过点击 event_count 列名旁边的省略号,并点击 New Measure 来实现。

在 Power BI 中创建新的明确衡量标准。图片由作者提供。
在报表画布顶部弹出的公式栏中输入以下 DAX 表达式:
# of Page Views = SUM('<dataset-name>'[event_count])
按下键盘上的 Enter 键。
现在,每当我们在我们的一个视觉效果中使用页面浏览量的度量时,Power BI 知道给我们页面浏览量的总数。
选中 #页面浏览量 度量,将度量工具功能区中的格式选项更改为整数。
此外,继续选择格式化部分的逗号图标。
这将使我们的页面浏览量更加符合人眼的视觉效果。

在 Power BI 测量工具功能区中设置测量格式。图片由作者提供。
设计视觉效果
让我们开始为我们的用户创造一些互动的视觉效果。
我们将创建一个摘要页面,显示我们网站上发生的事件的总数。
我们还将允许用户深入了解每个事件的细节。
1。汇总页面
在可视化窗格中,找到并选择卡片可视化。
勾选我们创建的页面浏览量度量旁边的复选框,将显式度量添加到卡片。
这将显示迄今为止的总浏览量。

以百万为单位显示页面浏览总数的卡片。图片由作者提供。
选择卡视觉后,转到可视化窗格中的格式选项卡(油漆滚筒图标)。**
展开数据标签下拉菜单,将显示单位改为无。**

更改卡片可视的显示单位,以显示页面浏览总数。图片由作者提供。
接下来,从可视化窗格中选择地图可视化,将 geo_country 拖动到 Location 字段框中,然后将geo _ region _ name拖动到 geo_country 下面。**
这将有效地创建一个层次结构,使您能够从国家级别深入到地区级别。
将*的页面浏览量添加到大小字段框中,你会看到我们的访问者来自哪里的全球分布。*

地图视觉与地理层次显示网站访问者的全球传播。图片由作者提供。
选择单击打开下钻箭头,激活地图视图的下钻模式。**

激活地图可视化的下钻模式。图片由作者提供。
然后,点击地图北美部分附近的美国气泡。**
这将允许您深入到地区级别,并能够看到美国的不同地区。**

在可视地图上向下钻取到美国地区级别。图片作者作者。
区域名称在这里没有什么意义,因为我们的事件是以编程方式生成的。
请注意当我们在同一个画布上与地图视觉交互时,卡片视觉指标是如何变化的。**
Power BI 使同一个画布页上的视觉效果能够作为默认设置相互交互。
这非常方便,因为它为报表开发人员节省了一个额外的配置交互的步骤。
点击向上钻取箭头返回上一级至国家级别,并在地图视图上取消向下钻取模式。**

取消地图视觉上的向下钻取模式。图片由作者提供。
现在我们知道了我们的大部分流量来自哪里,我们也可以分析用户是如何来到我们的网站的。
为此,向画布添加一个圆环图*可视化,并检查页面浏览量的和 utm_source 列。***
确保页面浏览量的*被添加到值字段框中,并且 utm_source 在图例字段框中。*

显示广告平台表现的环形图。图片由作者提供。
现在我们可以看到网页浏览量的网站来源。这告诉我们哪些广告平台的营销活动表现更好。
选择圆环图视觉后,转到可视化窗格中的格式选项卡。
展开细节标签下拉菜单,将显示单位改为千位。

更改圆环图视觉效果的显示单位,以显示页面浏览总数(以千为单位)。图片由作者提供。
点击地图图表中的美国气泡,然后Ctrl+点击谷歌部分的圆环图视觉。****

在同一画布上使用多种视觉效果的过滤卡视觉效果。图片由作者提供。
****卡片中的数字显示了通过我们在谷歌上发起的营销活动访问我们网站的美国用户总数。
最后,让我们在画布上添加一个日期切片器。
点击切片器视觉,并从字段窗格中检查 事件 _ 时间戳 。
您应该会看到如下所示的切片器,它允许用户查看特定时间段的信息。

允许用户指定时间段筛选器的日期切片器。图片由作者提供。
选择切片器视觉,转到可视化窗格中的格式选项卡。
关闭切片机机头。
接下来,展开日期输入下拉菜单,并将文本大小更改为 12 。
如下图所示排列您的视觉效果:

带日期切片器文本大小格式的最终仪表板设计布局。图片由作者提供。
将此页面重命名为活动概述。
2。详细页面
添加一个新的空白报告页面,并将其重命名为活动详情。

创建新的市场活动详细信息报告页。图片由作者提供。
在画布中插入一个表视图,并按以下顺序将这些列添加到表中:
- 事件时间戳
- 页面访问量
- utm_source
- utm_campaign
- utm_content
- 用户 _ 自定义 _id
- 地理国家
- 地理区域名称

直观显示市场活动详细信息的表格。图片由作者提供。
选中表格视觉后,转到可视化窗格中的格式选项卡,展开样式部分。
将视觉样式更改为最小以获得更好的可读性。

更改表格视觉样式以提高可读性。图片由作者提供。
让我们在表中更改一些其他格式设置。
展开列标题部分,将字体族设置为 Segoe UI Semibold ,将文本大小设置为 11 。
对于值,将字体族保留为 Segoe UI ,并将文本大小设置为 11 。
您的最终表格结果应该如下所示:

更改字体选项后的最终表格外观。图片由作者提供。
现在回到可视化窗格下的字段选项卡。
向下滚动,直到看到一个框,显示在此添加钻取字段。
将以下字段拖到该部分:
- 地理国家
- 地理区域名称
- utm_source
这将告诉 Power BI 使用您添加的字段来监听任何想要从报告中的其他页面钻取到详细页面的用户。

将钻取字段添加到市场活动详细信息页面。图片由作者提供。
如果您现在返回到战役概述页面,并在地图视图中右键单击美国,您应该会看到一个名为钻取的选项。

从活动概览页钻取到详细信息页。图片由作者提供。
将鼠标悬停在该选项上,并点击活动详情进入该详情页面。
请注意活动概述页面中的所有过滤器将如何延续到详细信息页面,即仅向用户显示美国页面浏览量。

从概览页应用了筛选器的活动详细信息页。图片由作者提供。
****Ctrl+点击左上角的箭头返回摘要页面。
点击 UI 左上角的保存图标,保存您的 Power BI 报告。
将您的报告发布到 Power BI Service
现在我们有了报告,点击主页功能区中的发布,将报告发布到工作区。
我将在 事件-中枢-数据集 所在的部分中使用同一个。

向 Power BI 服务发布最终报告。图片由作者提供。
登录 Power BI Service ,在发布报告的工作区查看报告。
现在,我们可以使用过滤、钻取和工具提示等功能了!
这就是使用可以分析历史数据的推送数据集的好处。

在 Power BI 服务中查看已发布的报告。图片由作者提供。
将事件发送到事件中心
让我们使用我们在第 2 部分中创建的send _ events . py文件再次向事件中心发送一些事件。
给 ASA 作业一些时间来完成事件处理,并刷新报告。
您应该看到您的指标立即得到更新,尽管没有以前在流数据集中看到的平滑动画。

测试 Power BI 服务中已发布报告的功能。 Gif 作者作者。
Power BI Push 数据集最多只能存储 20 万行。
如果您发现您的 【页面浏览量】 减少了,那是因为您已经达到了极限,数据集中较老的行已经以先进先出(FIFO)的方式被推出。
结论
我们终于走到了 Power BI 系列中最小可行流仪表板的尽头。
在这一部分中,我们介绍了如何使用推送数据集开发 Power BI 报告,并将其发布到 Power BI 服务,以向我们的最终用户展示精选的数据和见解。
如果您在这些帖子中学到了对您有用的新东西,请随时关注我,了解更多关于构建现代数据管理平台的内容。
流分析成本模型:基于流单元数量的每小时计费
每小时 0.127 美元,3 个流媒体设备全天候运行,每月仅需支付 283 美元。
不需要管理服务器,所以您可以只关注代码的输出。
清理 Azure 门户中的资源
由于我们不再发送事件,您可以继续在门户中停止您的 ASA 作业。
否则,会产生不必要的费用。

停止 Azure 流分析作业。图片作者作者。
如果您认为将来可能仍然需要,可以保留已停止的 ASA 作业和事件中心,因为如果 ASA 作业没有运行/处理事件,事件中心没有接收任何新事件,它们不会产生任何成本。
资源
同样,如果您没有先阅读第一部分和第二部分就直接阅读了本文,我将包含这两部分的链接。
所有用于研究目的的微软文档也在下面链接。
- Azure 流分析中的流单元
- Azure Stream Analytics |定价
- 活动中心|定价
- Power BI 中的最小可行流仪表板:第 1 部分
- Power BI 中的最小可行流仪表板:第 2 部分
今天使用的所有 Python 代码片段都可以在
Github repo:
**https://github.com/natworkeffects/power-bi-streaming
感谢您通读本系列,下一篇文章再见。**
使用预先训练的语言模型挖掘经济新闻文章

这篇文章中分析的整篇新闻文章的依赖网络的可视化。节点表示文档标记,边表示依赖关系。节点大小与介数中心性成比例,颜色表示词性标签。仅显示名词的标签。图片作者。
找到关键实体,建立依赖网络,并衡量新闻文章中的情绪
是时候我决定超越文档分类,使用自然语言处理来理解文本文档中的内容了,在这种情况下,是一篇彭博文章。我选择的文章标题是“股市创纪录,拜登呼吁更多刺激措施:市场回暖”;这篇文章报道了股票市场对乔·拜登宣布将提供数万亿美元援助以支持美国经济的反应。
我之所以选择这篇文章,是因为从 NLP 的角度来理解它似乎很有挑战性;它谈论许多相关的事件和实体,而不是专注于一个人或一家公司。我想提取三条信息:
- 这篇文章的主题是谁/什么?(我认为是股市和/或乔·拜登)
- 主体发生了什么,主体依赖于谁/什么?
- 正在发生的事情是好是坏,例如,对主题的看法是什么?
我将分三个独立的部分来研究这些问题,最后得出一些结论,并提出一些改进的建议。但是,首先,我要说一下我们将要使用的工具。
包装和建模
这个项目由 SpaCy 的' en_core_web_sm '语言模型支持,这是一个卷积神经网络,在 OntoNotes5 数据集上训练。该模型将一个文本文档作为一个字符串,并将其转换为一个 Doc 对象,该对象是一个 Token 对象的集合。 Token objects 表示单词和标点符号,但具有附加属性,如词性(POS)标签(动词、形容词、名词等。)、依赖关系标签(标记在语法上是如何相关的)、词汇化形式等等。我们将使用这些属性与文本进行交互,并提取我们想要的信息。
还有第二个库 Textacy,它构建在 SpaCy 之上。它使用 SpaCy 的语言模型预测的令牌属性来执行 SpaCy 没有提供的其他有用任务。我将在这里使用它来提取关键术语和 SVO 模式,这将在后面讨论。注意:要在 Windows 10 上安装带有 pip 的 Textacy,我首先需要下载微软 C++构建工具。
除了 SpaCy 和 Textacy,我将使用另外两个 Python 库进行信息提取。第一个是 Networkx,我们将用它来构建依赖网络,找出哪些实体依赖于哪些实体;第二个是 TextBlob,我们将使用它来估计情绪。依赖项的完整列表可以在 Github repo 中的requirements . txt文件中找到。
关于代码回购的一个注意事项:在本文中,我将代码编写为独立的片段,您几乎可以复制和粘贴,但我很快将重新构造回购,将其转换为 CLI 工具。
提取关键术语
Textacy 允许您实现四种不同的关键字提取算法:TextRank、YAKE、sCAKE 和 SGRank。我不会详细介绍每个算法是如何工作的,但是,它们都遵循相似的方法来计算“重要性”,使用图形和统计技术的组合。
在纯图形算法中,如 TextRank,文档被表示为图,以单词或短语为节点,以语义或上下文关系为边;然后根据连接到每个节点的边的数量和类型给每个节点分配分数。在统计算法中,例如 YAKE,它不构建图形,重要性由词频和其他度量来确定,例如给定词与停用词的相似程度。sCAKE 和 SGRank 算法是混合算法,使用图形和统计方法来生成重要性分数。
每个算法返回的前 10 个关键字如下所示:

由 Textacy 提供的四个关键术语提取算法返回的关键术语的排名。图片作者。
sCAKE(Semantic Connectivity Aware Keyword Extraction)算法在我们的新闻文章中表现最佳,发现' stock '是最重要的关键词,而'elect**Joe Biden '是第二重要的关键词,其中 elect 很可能来自短语 president-elect。其他算法识别出的最重要的关键词是“芝加哥联邦储备银行行长 Charles Evans ”、“川普”和“美国国会大厦”。
计算关键字并生成上述图的代码如下所示。
使用 Textacy 提供的每个文本提取算法提取和可视化文档的前 10 个关键词的代码。
理解主体发生了什么以及它依赖于哪些实体
为了找出我们最重要的关键词所代表的主要主题发生了什么,我将提取两种语言结构:主语-动词-宾语(SVO)模式和句子。SVO 可以用 Textacy 的 extract 类直接计算;它们是三元组,会告诉我们关键字在做什么,但不会告诉我们为什么,因为它们只是单词的三元组。SVO 模式的一个例子是“总统宣布支持”:总统”是主语,“宣布是动词,“支持是宾语。
为了理解事情发生的原因,我们将浏览包含关键术语的句子的依存树。依存树使用语法标签来解释句子中不同的单词如何相互依存。当我们创建一个 Doc 对象时,SpaCy 会自动为我们构建这个,如上面代码片段的第 47 行所示。
以下代码提取了所有包含我们的关键术语的 SVO 模式和句子:
如何从整个文档中获取所有 SVO 模式和句子,然后过滤它们以找到包含关键术语的那些。
首先,我使用 Textacy 提取 SVO 模式,然后过滤它们以获得包含我们的关键术语的模式。只有两个 SVO 模式包含词汇'股票'后的引理:
- 股票创纪录
- 股票上涨 10%
虽然这些肯定是信息丰富的,但它们并没有告诉我们是什么在推动股票发生变化。额外的信息来自句子,这些句子是通过循环docs . sents返回的生成器获得的;我对它们进行了过滤,以获得包含关键术语的内容。
包含“股票”的句子,在引理之后:
- 拜登呼吁出台更多刺激措施,股市创历史新高
- 纽约时间下午 4 点,标准普尔 500 指数上涨约 0.5%
还是那句话,只有两个;在结论中我会建议如何提取更多的句子,但现在,让我们看看如何使用它们。通过打印依赖树,我们可以看到哪些实体依赖于哪些实体。下面是句子“股市创纪录,拜登呼吁更多刺激”的依存树:

SpaCy 为句子“拜登呼吁更多刺激,股市创纪录”生成的依存树。箭头显示单词之间的语法依赖关系,箭头旁边的标签告诉您依赖关系的性质。在每个单词下面,我们还有位置标签。图片作者。
树标签'用依存标签' nsubj' 储备了,表示名词性主语,实质上就是句子的主语。通过箭头,我们可以看到主题依赖于哪些单词,标签告诉我们关系的性质。红色箭头通过动词' hit '和' calls '突出了单词' Stocks 如何依赖于' Biden 。
为了与依赖树进行交互,必须使用 Python 的 Networkx 模块将其转换为图形/网络。节点代表记号,边代表依赖关系:下面是句子“股票创纪录,拜登呼吁更多刺激”的依赖网络的可视化。

使用 Networkx 生成的依赖图。节点根据其位置标签进行着色。图片作者。
将句子表示为网络允许我们定义记号之间距离的概念(以及任何其他图形属性)。我们可以使用距离作为两个令牌相互依赖程度的基本度量,距离近的令牌比距离远的令牌相互依赖程度更高。以下代码向您展示了如何构建一个网络,并使用它来计算名词性主语与所有其他名词和专有名词之间的最短路径。
代码从 SpaCy 的依存关系树构建依存关系网络,计算所有名词性主语与其他名词和专有名词之间的最短路径,然后最终将结果保存在 DataFrame 中。
使用上面的代码,我们发现单词' Stocks '依赖于三个名词:
- 记录’—距离:2
- 拜登’—距离:4
- 刺激’—距离:5
虽然我们可以接受股市取决于拜登和他对经济的刺激,但我不会说股市取决于他们创下的纪录。这表明这种计算依赖强度的基本方法并不完美。为了改进,您可能希望只计算专有名词和命名实体的距离,尽管我们不会在本文中讨论命名实体标签(它们很容易使用, SpaCy 会为您预测它们)。
分析情绪
此时,我们已经通过计算关键术语发现了文章的主题——股票;主题发生了什么(股票在上涨),通过提取 SVO 模式;通过建立依赖网络,我们知道主体依赖于哪些实体(拜登和刺激)。剩下要做的就是估计关于主题的情绪。
为此,我将使用 TextBlob 的情感分析器来计算我之前提取的所有句子的情感,然后取平均值以获得特定主题的情感分数。我还试图估计 SVO 模式的情绪,但他们都返回了 0.0 分,表明中性情绪,可能是因为他们没有包含足够的信息来提供准确的估计。

平均情感极性得分,由四个关键词提取算法返回的关于顶部关键词的所有句子得出。图片作者。
正如预期的那样,关键词“股票”被发现具有积极的情绪,而“当选人乔·拜登”是中性的,“川普”是积极的,而“芝加哥联邦储备银行行长查尔斯·埃文”是消极的。快速计算情绪的代码在这里:
计算我们之前提取的句子的平均情感极性得分。
结论
本文的主要观点是,您可以使用预先训练的语言模型,尤其是 SpaCy 提供的语言模型,从文本文档中提取大量信息。此外,我希望这篇文章向您展示,您不需要使用最先进的模型架构来获得良好的结果,因此,与其将时间花在训练语言模型上,不如将时间花在提取信息和利用现有模型构建知识上。
虽然我们能够快速获得大量信息,但仍有几点需要改进:
- 首先,如果你事先没有读过这篇文章,很难知道哪个关键词提取算法是最好的。为了解决这个问题,在选择一个算法之前,您可能希望使用所有 4 个算法并跟踪每个算法对于您给定任务的性能。
- 第二,我们收到的 SVO 句型和句子数量很少。这部分是因为一些关键词含有噪音。例如,只有一个句子包含“选举乔·拜登”,但许多句子包含“拜登”。因此,您可能希望在提取 svo 和句子之前清理您的关键术语。例如,您可以通过检查关键字中有哪些 POS 标签,并只保留专有名词来做到这一点。另一个建议是找到关键术语的同义词,例如,除了搜索“股票”之外,还要搜索包含“市场”和“指数”的句子。
我们已经了解了如何提取关键术语、SVO 模式和句子,如何构建依赖网络并使用它们来发现句子中标记之间的关系,最后,如何评估文档中关于关键术语的情感。这里的代码应该可以帮助你快速开始挖掘你自己的文本文档,你可以到项目报告这里获得一些额外的绘图功能,并查看我们提取的数据!
关键词提取算法引用: TextRank , YAKE , sCAKE , SGRank
保持联系: linkedin
挖掘意见以了解客户趋势:第 1 部分,共 2 部分
用 PyTorch 微调预训练变压器

ahin Sezer diner 在 Unsplash 上拍摄的照片
随着互联网上可用信息的丰富,挖掘意见以了解客户对你的品牌、产品和服务的感受已经成为一个重要的成功指标。公司在投资人工智能以从客户头脑中获得有价值的见解时不会退缩。
“你最不满意的顾客是你最大的学习来源”~比尔·盖茨
为了挖掘客户的意见,公司经常利用自然语言处理(NLP)的进步,在这篇博客中,我想探索一些可以用来解决手头问题的 NLP 的关键概念。
目标
在这个由 2 部分组成的系列中,我使用 2017 年改变 NLP 面貌的模型 Transformers,使用亚马逊评论分析不同产品领域的客户情绪。除了 Transformers,我还探索了并行计算、迁移学习和交互式仪表板的概念。
本系列的第 1 部分着重于微调预先训练好的变压器,第 2 部分详细介绍了我在 Jupyter 笔记本上创建交互式仪表盘的实验。
1.在我们编码之前…

1a。变压器-ing NLP

Transformer 是一种 NLP 编码器-解码器架构,它使用多头自关注机制来并行处理输入序列。
让我们分解上面的句子,并为每个部分开发一个直觉:
- “NLP”:自然语言处理或 NLP 是机器学习中的一个领域,它帮助机器从人类语言中获取意义。NLP 的应用范围从理解语言(如总结文本、社交媒体监控)到生成语言(如为图片创建字幕),有时两者同时进行(如语言翻译、聊天机器人)。
- "编码器-解码器架构":计算机/机器无法理解文字,因此我们以数字的形式给它输入语言。编码意味着将数据转换成编码信息(在我们的例子中是数字向量,也就是隐藏状态),而解码意味着将编码信息转换成可理解的语言。解码器的输出将取决于模型的目标,例如,在将英语翻译成印地语时,解码器会将编码的消息转换成印地语。
- "自我关注机制":简单的编码器-解码器架构的缺点是,由于消失梯度问题,它不能熟练地管理长句子序列。为了解决这个问题,引入了注意机制。目的是允许 NLP 模型在解码每个时间步长时更多地关注输入序列的相关部分。有种不同类型的注意力,变形金刚使用的那种叫做自我注意力。它通过利用上下文向量来捕捉句子中单词之间的上下文关系。
注意是理解变压器和 NLP 最重要的概念之一,在这里阅读更多关于它的内容!
- "多头注意力":自我注意力过程并行重复多次,每次都称为一个头,因此得名"多头"注意力。多头注意力允许嵌入学习每个单词意思的不同方面。一种思考方式是,一座建筑可以用它的高度、宽度、颜色、位置等来描述。而拥有不同的描述,让最终的画面更加丰富。
- "并行处理输入序列":变形金刚的前身按顺序(一个单词接一个单词,从左到右或从右到左)编码输入,不允许模型利用 GPU 并行计算的魔力。假设 Transformer 架构为每个时间步长创建上下文向量,则单词相互独立,可以并行处理。该属性还允许模型中的双向性,即每个单词预测都考虑当前单词两侧的上下文。(图一)

图 1:变形金刚在编码过程中从两个方向考虑上下文(图片由作者提供)
变形金刚在 NLP 中是一股强大的力量,有大量文章解释了原始论文。上面的简短描述并没有很好地体现建筑的复杂之美——我鼓励你多读一些关于它的内容。这里有一篇这样有用的文章。

图 2:变压器架构(来源)
1b。将学习转化为救援
当我们开始向机器学习模型教授人类语言时,我们理解得相当简单的人类语言似乎具有无限的复杂性。找到捕捉所有细微差别的数据集来制作高级 NLP 模型是一项艰巨的任务,一旦我们找到了必要的数据量,模型的实际训练就变成了一项计算开销很大的任务。为了解决这个问题,我们使用迁移学习。
在迁移学习中,我们重用一个预先训练的模型作为另一个模型的起点。潜在的想法是,在第一个任务中学到的特征足够普遍,可以重新用于第二个任务。

图 3:迁移学习的图解(图片由作者提供)
2018 年,谷歌彻底改变了 NLP 格局(再次!)通过从变压器释放双向编码器表示( BERT )。BERT 本质上是“变形金刚”的“编码器”部分,它是在维基百科语料库上训练出来的(意思是很多很多的数据!).
来自 BERT 的迁移学习显示了 2018 年对 11 个不同下游任务的异常预测。
BERT 的目的是制造一个健壮的编码器,它可以嵌入单词,同时从两个方向考虑句子的上下文。(当前单词前后的单词)唉,它就是这么做的!使用来自 BERT 的预训练表示显著地增加了 NLP 模型预测的准确性,并且甚至更显著地减少了达到该准确性所需的计算开销。
经过预先训练的语言模型已经成为自然语言处理研究中的一项重要内容,在过去的三年中取得了许多突破。 GPT-2 、 GPT-3 、 T5 、罗伯塔和 XLNet 是其他一些流行的语言模型。
1c。并行计算的力量
随着数据可用性的扩展和体系结构的复杂性,给你的神经网络增加新的层次对你的 CPU 来说是一项艰巨的任务。让“工作”变得更容易的一个简单方法是利用多个 CPU 内核。这有助于 CPU 并行处理进程,减少周转时间。
加快处理速度的更好方法是使用GPU。CPU 并行计算线程的能力受限于内核的数量,然而,GPU 可以同时处理多个计算。事实证明,GPU 对于 ML 模型非常有价值,尤其是对于深度学习。
GPU 可能不适合所有人,但不用担心,ML 社区会支持你!网上有多种选择可以使用免费的 GPU!在这篇博客中,我将利用 Google Colab 的 NVIDIA GPU。
对于 NVIDIA GPUs,我们可以使用 CUDA 来创建一个简单的解决方案,以允许在我们的模型中进行并行处理
2.现在,让我们编码

上面讨论的概念可以用来创建一个 ML 模型来挖掘意见和理解当前的客户情绪。我将这个过程分为 4 个步骤:
- 数据准备
- 迁移学习
- 微调和培训
- 预测!
我将使用来自多领域情感数据集(版本 2.0) 的“未处理的”tar 文件。该数据集包含过去几十年中多个类别的亚马逊产品评论。
2a。数据准备
数据以 XML 格式呈现,首先需要转换成 Pandas 数据框架。我们可以用美味的汤来做到这一点。
“Beautiful Soup 是一个 Python 库,用于从 HTML 和 XML 文件中提取数据。它有助于导航、搜索和修改解析树”
代码块 1 :使用 Beautiful Soup 将 XML 数据转换成 Pandas DataFrame
我们现在将使用 BERT 词汇表将文本转换成标记。 PyTorch-Pretrained-BERT 库为所有的 BERT 模型提供了令牌。点击阅读更多关于他们的信息。
每个模型都有一个“最大序列”限制,我们需要将我们的令牌截断到伯特的限制,即 512。我们还将添加一个标记来区分句子(“CLS”)和填充(“填充”),以确保所有句子都具有相同的长度。
代码块 2 :创建一个处理器来标记数据集
我们现在将创建一个数据加载器来遍历数据。数据加载器支持数据的自动批处理,在我们希望并行处理数据操作时非常有用。在将文本放入数据加载器时,我们对数据集的每一行进行标记,并用相应的标签加载它。
代码块 3 :创建一个带有文本标记和相应标签的数据加载器
2b。迁移学习
对于迁移学习,我采用了来自NAACL 2019 迁移学习教程的预训练模型。
代码块 4 :变压器型号
在模型的顶部,我们将为业务问题添加一个分类器头。在我们的例子中,我们希望预测“积极”或“消极”的客户反应——因此,我们的模型将有两个输出类。
代码块 5 :模型分类头
2c。模型微调和培训
我们现在需要创建一个函数来训练数据,并创建一个评估函数来评估每个时期的验证数据。我们还需要为模型优化定义一个配置。定义一个单独的字典来微调模型允许我们在迭代超参数时进行简单的修改。
代码块6:定义列车功能
代码块 7 :定义评估函数
PyTorch 的 Ignite 库只需要几行代码就可以非常方便地进行培训和评估。
代码块 8 :训练模型
可以使用定义的赋值器来检查模型的准确性。预训练模型使我们能够通过几千个例子达到 92%的准确率。

图 4:测试集上的模型准确性
2d。预测!
为了预测样本,我们将首先像代码块 2 一样标记数据,然后将其输入到模型中。
代码块 9: 定义预测功能
上述函数可直接用于输出单个句子的肯定和否定类别。在这个博客的第 2 部分,我们将使用这个函数进行批量预测。

图 5 : 单输入的模型预测
结论
概括地说,我们学习了如何使用漂亮的 Soup 读取 XML 文件,使用 PyTorch 数据加载器创建迭代器,使用 BERT 标记文本,并在预训练的模型上使用迁移学习来创建情感分析的基础。一个分类器也被添加到基本模型中,使其能够预测积极和消极的情绪。
查看“注意力是你所需要的全部”这篇文章,阅读更多关于变形金刚的内容。请参考源代码了解更多细节,如果您有任何问题/建议,请随时联系我!
在这篇博客的第二部分,我将创建一个交互式仪表板,它将根据变压器模型的预测绘制实时趋势!
挖掘推文回复:一个演练
一段简短的历史,讲述了我是如何在 Twitter 上挤来挤去,围着我,抢着回复一条推文

我在 twitter 上的“新闻”快照
Twitter 的 API 是我们所有人都喜爱的,不是吗?它让我们几乎实时地接触到信息宝库、错误信息、虚假信息等等。它满足了初学者 NLP 工程师/数据科学家高效收集可处理数据的梦想。可笑的是,足够多的社区成员通过他们的博客帖子、堆栈溢出和包装器对此进行了很好的记录。根据我的记忆,我记得至少有五种不同的 python 包装器来访问 API。
尽管如此,我偶然发现了一个文档很差、解决方案实现更差的任务(这不是程序员的错):挖掘一条推文的回复。
*For the rest of this post, I am going to assume that the reader is familiar with twitter API terminology such as a tweet_id or a tweet object. If this is new, please do refer to the* [*twitter's documentation*](https://developer.twitter.com/en/docs)*.*From experience, tutorials that use wrappers such as [tweepy](https://docs.tweepy.org/en/latest/) make it more intuitive to get started- for advanced usage however, the source documentation might help.
这是这个问题的解决方案。这个也是。精湛的实现。它们在大多数情况下都有效。不幸的是,他们受到了 Twitter 本身的限制。如果您不想深入这些链接中的代码,下面是它们所遵循的过程的快速概述:
**Mining replies to tweets**1\. Find the *tweet_id* of the tweet you want the replies to.
2\. Query twitter for tweets that have replied to this tweet id. This can be done based on the *in_reply_to_user_id* attribute that every tweet object carries. Every tweet is represented as a tweet object.
3\. Compile the replies, assert their correctness. Done.
然而,缺点是当您检索这些结果时,Twitter 的旧 API 只返回最近的或流行的结果——或者两者的组合(您可以指定哪一个)。这些新近或流行并不是由每条推文来判定的——而是由发布你想要回复的根推文的用户来判定的。
当你与我的账户互动时,你可以找到我收到的几乎每一个回复。如果你与《纽约时报》的账户互动,你甚至无法对他们几个小时前发布的内容进行回复,因为他们收到了大量“最近”和“热门”的推文。由于只有一定数量的回复被返回,最近的回复和流行的回复保持快速变化,使得不可能为高活动帐户重新创建回复活动。
不是所有的都失去了。其实现在更好!
幸运的是,我意识到 Twitter 在去年年底发布了它的 v2 API。“这只是更新-没有太花哨的我猜”(除了这是我最错误的̶ ̶i̶n̶ ̶l̶i̶f̶e̶当谈到 API)。
这里有一个不错的小帖子解释了新的东西。
我找不到很多使用 v2 端点的 python 包装器(我不喜欢我之前尝试在没有包装器的情况下与 API 交互的经历),但是纯属巧合和最后一次绝望尝试的勇气——我找到了救星包装器: TwitterAPI 。
在新 API 中所有有趣的特性中,对这项任务最重要的是属性 conversation_id ,它现在伴随着每条推文。在错综复杂的推文中,有回复、对回复的回复、对回复的回复也是回复等等,每条推文都被分配了相同的 conversation_id ,以尝试将所有内容串连在一起。
TwitterAPI 的示例代码(我在这篇文章中大量借鉴并做了一点修改)就依赖于这个属性。让我们逐步编码。您可以复制并粘贴代码,它将按照描述的方式工作。记住从您自己的 twitter 开发人员门户设置身份验证令牌。
注意:gists 并不意味着独立运行。py 文件。它们只是用来表示的。您可以从这里按顺序复制代码,然后重新创建文件(或者以笔记本的形式使用它这里)。
1.导入魔法。
**# INSTALL TWITTER API (!pip install TwitterAPI) and import necessary libraries/packages
# TwitterAPI Documentation here:** [**https://github.com/geduldig/TwitterAPI/**](https://github.com/geduldig/TwitterAPI/)from TwitterAPI import TwitterAPI, TwitterOAuth, TwitterRequestError, TwitterConnectionError, TwitterPager
import pandas as pd
2.验证你的应用。点击获取您的代币。记得在实例化 API 时传递 version=2 参数。
consumer_key= "CONSUMER KEY"
consumer_secret= "CONSUMER SECRET"
access_token_key= "ACCESS TOKEN KEY"
access_token_secret= "ACCESS TOKEN SECRET"api = TwitterAPI(consumer_key, consumer_secret, access_token_key, access_token_secret, api_version='2')
3。算出推文的“conversation_id”。以下是我所做的,我发现这是最方便的。
-
从发布了您想要回复的推文的帐户中查找最近的推文。使用 tweepy 来收集 tweet 对象——他们使用 v1 API,但是检索 tweet 对象,因此检索tweet _ ids——在我看来,他们的方法是最简单、最快的方法(但是你也可以找到其他方法——我们只需要最少的tweet _ ids—conversation _ ids
-
使用 TwitterAPI 使用其 tweet_id 查找 tweet,并检索其 conversation_id 。
4.如上所述检索推文。有了代码,您也应该有他们的“conversation _ ids ”,所有这些都整齐地打包到一个数据帧中。
# RETRIEVE TWEETS FROM THE SCREEN NAMES(S)tweets = get_new_tweets(names)# RETRIEVE CONVERSATION IDs OF THE RETRIEVED TWEETS
tweets = add_data(tweets)# WRITE THE RETRIEVED TWEETS TO CSV
tweets.to_csv("tweets.csv")# VIEW THE DATAFRAME HEAD WITH THE TWEETS RETRIEVED
tweets.head()
5.转到 tweets.csv 文件,找到您需要回复的 tweet 的“conversation_id”。复制它。马上回来。
conv_ids = ['1349843764408414213']
# replace this with conversation IDs from above- this is just a place holder
6.构建一个数据结构来处理 tweet 层次结构。
我不打算以一种详细的方式从技术上深入这一步。这里所发生的是创建一个树形数据结构来跟踪 tweet,其中根是启动线程的第一条 tweet。
(可选逻辑:每次回复 root 都是一级回复。对 1 级回复的每个回复都是 2 级回复,依此类推。有趣的是,整个线程中的每条 tweet 都带有 conversation_id 属性。使用这个并使用 in_reply_to_user 属性,您可以重新创建线程中每个回复的级别。使用这些信息和每条推文的时间戳,你几乎可以重建整个线程。)
7.实际检索回复。
我们简单地使用上面创建的树结构来存储每个回复,并通过给每个回复分配一个级别来找到它在线程层次结构中的位置。
我对这篇文章做了一点简化,添加了一些代码,只检索对一条推文的即时回复,而不检索对回复的后续回复。如果您想看到整个对话树被重新创建,请取消注释上面要点的第 27 行、下面要点的第 37 行和第 38 行,以及下面要点的第 36 行,以观看奇迹的发生。
可选逻辑:一旦你获得了根和具有相同 conversation_id 的 tweet 列表,你需要做的就是使用这个并使用 in_reply_to_user_id 找到每条 tweet 的父节点,直到你通过蛮力找到根。然而,递归实现会更有效。函数 find_parent_of () 就是这样做的。
使用以下代码运行此代码:
replies = reply_thread_maker(conv_ids)# WRITE REPLIES TO FILE
replies.to_csv("replies.csv")#VIEW SAMPLE REPLIES
replies.head()
8.请访问 replies.csv 并分析您的回复。鳍。
我试图把这种方法放在一个 colab 笔记本上,让它像一个基于网络的工具一样工作。它仍然需要一些编程知识和对 API 的理解,但是我已经尝试让它尽可能的即插即用,而不是把它变成一个完整的项目。
我还没有看到很多处理这个话题的教程或博客,尤其是在 Twitter 的 v2 API 发布之后。就我个人而言,我所知道的希望这个特性早点出现的人比我用手指和脚趾所能数出来的还要多,这只能意味着一件事——一篇博客文章。
在花了几乎一整天的时间寻找检索推文回复的方法之后——这是我放弃前的最后一次尝试——我偶然发现了这个解决方案。这个故事的寓意是,坚持不懈真的会有回报。这也是我作为程序员在使用 twitter 的 API 时最美好的一天。如果你读到这里,读到了这句话--我欠你一杯咖啡--但只有在我毕业并找到工作之后--这些条件才适用。
未来方向
我不希望你问我得到这些回复有什么意义,所以我借此机会列出了一些可能有用的地方:
- 测量推特引发的毒性
- 衡量推文引起的偏见或政治宽容(左倾/右倾反应分类)
- 研究公众对新闻的反应情绪
- 研究公众对谣言和错误信息的反应
对于任何评论,反馈,问题,聊天等。你可以在这里找到我。
—
aditya Narayan an是布法罗大学的研究生。他专门从事 运筹学 并有 计算机科学 的背景。阅读新闻并加以评论是他的本能。
他有另一个自我,无聊时喜欢创作各种数字内容,但更喜欢纸和笔。他为多个大型体育实体创建了数字内容,如 LaLiga Santander、Roland-Garros 和 NBA 篮球学校。
挖掘“2020 年 Kaggle 机器学习和数据科学调查”数据
本文将 Apriori 算法应用于“2020 年 Kaggle 机器学习和数据科学调查”数据,以找出受访者使用的技术之间的关联。本文假设读者具备 Apriori 算法及其 Python 实现的工作知识。

威廉·艾文在 Unsplash 上的照片
2020 Kaggle 机器学习&数据科学调查是由 Kaggle 在 2020 年进行的一项调查。该调查于 2020 年 10 月在网上进行。经过数据整理,该调查有 20,036 份回复。该调查有 39 个以上的问题,询问受访者关于他们的人口统计数据、用于数据科学和机器学习的技术(编程语言、ide、算法、库和云产品)、他们计划在未来学习的技术等。
Kaggle 还从 2020 年 11 月 19 日到 2021 年 1 月 7 日发起了一年一度的数据科学调查挑战,供社区使用这些数据讲述一个关于数据科学和机器学习社区的故事。在本文中,我们将尝试找出被调查者使用的各种技术之间的关联规则。让我们开始挖掘“2020 年 Kaggle 机器学习和数据科学调查”数据。这篇文章的完整代码可以从我的 Kaggle 笔记本中获得。
数据导入和预处理
从响应中删除“None”和“Other ”,因为它们不携带任何信息,还会导致整个数据集重复。例如,我们无法区分“使用的编程语言”问题和“使用的 IDEs”问题中的“无”或“其他”。
有两个回答,分别是‘MATLAB’和‘Shiny’,出现在两个不同的问题中。“MATLAB”出现在“使用的编程语言”和“使用的 IDEs”问题中。同时,Shiny 出现在“使用的数据可视化库”和“用于公开共享机器学习应用程序的产品”问题中。因此,我们将把“IDEs used”问题下的“MATLAB”替换为“MATLAB IDE”,把“Products used to public share machine learning applications”问题下的“Shiny”替换为“Shiny(public share)”。
数据存储在数据框中,但 Python 中“apriori”包的“apriori”方法需要一个列表列表,而不是数据框。因此,我们将把数据框转换成一个列表的列表(我们称之为“主列表”),其中“主列表”中的每个列表代表数据框的一行/一个索引。

“主列表”中的列表(图片由作者提供)
对数据应用 Apriori 算法
数据经过处理,可以进行关联规则挖掘。我们将设置“最小支持度”为 0.02,“最小置信度”为 0.6,“升力”为 3,“最大长度”为 2。

生成的前 5 条规则记录(按作者排序的图片)
我们将筛选支持度大于 0.02、置信度大于 0.8 和提升度大于 3 的规则,并将它们组织成句子,这有助于读者轻松理解它们。

作者图片
这些是从 Kaggle 调查数据中生成的一些重要的关联规则。具有高置信度的规则听起来非常明显。很少有规则(如规则 40)显示出“Colab”和“Github”之间的强关联,这在这个关联规则挖掘练习之前是没有预料到的。然而,通过调整 support、confidence、lift 和 max_length 超参数进行的进一步分析可能会揭示听起来很有趣的其他见解。
微型火箭:快速和准确的时间序列分类
思想和理论
使用 Python 实现系列分类的最快的先进算法

大多数最新的(SOTA)时间序列分类方法受到高计算复杂度的限制。这使得它们在较小的数据集上训练缓慢,并且在大数据集上实际上不可用。
最近,ROCKET(随机卷积核变换)已经实现了 SOTA 精度,其时间仅为其他 SOTA 时间序列分类器的一小部分。ROCKET 使用随机卷积核将时间序列转换为特征,并将特征传递给线性分类器。
迷你火箭甚至更快!
MiniRocket (最小随机卷积核变换)是 Rocket 的(几乎)确定性重构,在较大数据集上速度快 75 倍,精确度大致相当。
MiniRocket 是 Rocket 的新默认变体
在 UCR 档案馆的 108 个数据集上,Rocket 在单个 CPU 内核上运行了大约 2 小时。 MINIROCKET 只用了 8 分钟就跑完了!相比之下,第二快的 SOTA 算法(cBOSS)需要大约 14 个小时。
火箭快速入门
其核心是,Rocket 是一种时间序列转换或特征提取的方法。提取的特征包含与系列类成员资格相关的信息,其可以由线性分类器建模。
Rocket 通过首先将每个序列与 10,000 个随机卷积核进行卷积来转换时间序列。随机卷积核具有随机长度、权重、偏差、膨胀和填充。
然后 Rocket 分别将全局最大池和 PPV“正值比例”池应用于卷积输出,为每个内核生成 2 个特性,每个池方法一个特性。这导致每个输入时间序列有 20,000 个要素。
正值的比例表示如何对内核捕获的模式的流行程度进行加权。这个值是 ROCKET 最关键的元素,有助于它的高精度。

zi 是卷积运算的输出
Rocket 提取的特征用于训练线性分类器。
有关 Rocket 的更多背景信息,请参阅我之前的文章:
https://link.medium.com/qYcaC7lL69
MiniRocket 和 Rocket 有什么不同?
MiniRocket 和 Rocket 都依赖于使用卷积结果的 PPV 或正值比例来汇集卷积值。作为快速复习,您可以将 PPV 定义为:

PPV =将 X 与权重 W 卷积并减去偏差 b 后,大于 0 的值的比例。
核超参数
MiniRocket 使用一个小的、固定的卷积核集合,而不是具有随机超参数的核。
一般来说,MiniRocket 会最小化每个内核的超参数选项的数量。微型火箭内核的数量保持尽可能少,同时仍然保持精度,以最大限度地提高计算效率。

长度超参数
Rocket 从值 7、9 和 11 中随机选择内核长度;MiniRocket 只使用长度为 9 的内核。这也意味着序列长度必须大于或等于 9,或者填充长度至少为 9。
重量超参数
Rocket 从 0 到 1 之间的正态分布中随机选择权重;MiniRocket 将权重值限制为-1或2。MiniRocket 进一步将其内核集限制为恰好有 3 个值2 (例如【1,1,1,1,1,1,1,2,2】)。
事实上,MiniRocket 的权重参数正好使用 2 个固定值,这对速度优化至关重要
两个选定权重值的确切值和比例并不重要,只要内核权重总和为 0。这确保内核仅对输入值的相对幅度敏感。
因此,MiniRocket 使用一个小的固定的 84 个内核的集合,并且几乎完全是确定性的。
偏倚超参数
Rocket 从-1 和 1 之间的均匀分布中随机选择偏差;MiniRocket 只是从卷积输出中采样偏差值。
对于每个卷积核,偏差值是从一个随机训练示例的卷积输出的分位数中提取的。
这是 MiniRocket 唯一的随机组件。
扩张超参数
膨胀允许将较小的内核应用于较宽的数据窗口。

【1511.07122 v2】通过扩张卷积的多尺度上下文聚合(arxiv.org)
Rocket 随机选择一个膨胀值;MiniRocket 将每个内核的最大扩展数限制为 32。较大的膨胀值会降低变换的效率,并且不会提高精度。
卷积输出的汇集
Rocket 和 MiniRocket 都将 PPV 池应用于卷积输出以生成输出要素。
Rocket 还将全局最大池应用于卷积输出,以生成附加要素。MiniRocket 不使用全局池,因为它不会提高其他更改的准确性。因此,MiniRocket 生成的功能是 Rocket 的一半,并且保持同等的准确性。
MiniRocket 怎么比 Rocket 快?
MiniRocket 通过 4 个关键的优化显著加快了转换速度,这些优化是由小型固定内核集的属性和 PPV 实现的。
同时计算 W 和 W 的 PPV
这种优化利用了固定内核和 PPV 的数学特性。
基本思想是 PPV = 1-PNV(负值比例)。如果你计算一个内核的 PPV,你可以“免费”得到 PNV,不需要运行额外的卷积。由于 PNV 只是反转内核的 PPV,这允许 MiniRocket 在不增加计算量的情况下将应用于一个系列的内核数量增加一倍。
实际上,一旦计算出权重α= 1 和β = 2 的核集合的 PPV,就可以快速计算出 PNV = 1-PPV。PNV 是权重α = 1 和β= 2 的反向核的 PPV。
重复使用卷积输出来计算多个要素
对于较小的膨胀,使用相同的内核来计算多个特征,例如不同的偏差值。因此,通过对多个特征重新使用来自相同核的卷积输出,减少了时间。
避免卷积运算中的乘法运算
加法运算比乘法运算快。因为内核权重被限制为 2 个值,所以大多数乘法运算可以从数学上分解出来,并替换为加法。
核心直觉是:每个输入序列只需要计算一次αX 和βX,因为没有其他权重值。然后,这些值可以被重复使用,以仅通过加法来完成卷积运算。
对于每一次膨胀,“几乎一次”计算所有内核
这种优化使用智能数学来进一步预计算和重用卷积输出。基本思想是在单个步骤中计算(α+β)X,而不是为每次膨胀分别计算αX 和β*X。
如何配合 Python 使用 MiniRocket?
像 Rocket 一样,MiniRocket 转换是在[sktime](https://www.sktime.org/en/latest/examples/rocket.html) python 包中实现的。
https://link.medium.com/TyOyddIK19
以下代码示例改编自sktime 小型火箭演示。
首先,加载所需的包。
import numpy as np
from sklearn.linear_model import RidgeClassifierCV
from sktime.datasets import load_arrow_head # univariate dataset
from sktime.transformers.series_as_features.rocket import MiniRocket
接下来设置训练和测试数据—在这种情况下,我加载单变量 箭头 系列数据集。注意,输入序列的长度必须大于等于 9。如果您的系列较短,请使用 sktime 中的 *PaddingTransformer* 功能填充系列。
与 Rocket 不同,MiniRocket 的一个好处是不需要对输入序列进行规范化。
*X_train, y_train = load_arrow_head(split="test", return_X_y=True)
X_test, y_test = load_arrow_head(split="train", return_X_y=True)
print(X_train.shape, X_test.shape)
*>> (175, 1) (36, 1)**

使用 MiniRocket 变换来变换训练数据。MiniRocket 使用 10,000 个卷积核来创建 10,000 个特征。
*minirocket = MiniRocket()
minirocket.fit(X_train)
X_train_transform = minirocket.transform(X_train)
X_train_transform.shape
>> (175, 10000)*
从 scikit-learn 初始化并训练线性分类器。作者建议对较小的数据集使用[RidgeClassifierCV](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.RidgeClassifierCV.html) (<20k 训练示例)。对于更大的数据集,使用随机梯度下降[SGDClassifier(loss='log')](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.SGDClassifier.html)训练的逻辑回归。
*classifier = RidgeClassifierCV(alphas=np.logspace(-3, 3, 10), normalize=True)
classifier.fit(X_train_transform, y_train)*
最后,为了给训练好的模型评分并生成预测,使用 Rocket 转换测试数据并调用训练好的模型。
*X_test_transform = minirocket.transform(X_test)
classifier.score(X_test_transform, y_test)
>> 0.9167*
对于多元数列,步骤是相同的,但是使用MiniRocketMultivariate类代替MiniRocket。
相关著作
感谢您的阅读!你可能也会喜欢下面的相关文章。如果你还不是灵媒会员,今天就加入灵媒吧!
*
参考
【arxiv.org】【2012.08791】MINIROCKET:时间序列分类的一种非常快速(几乎)确定性变换
GitHub—Angus 924/mini rocket:mini rocket:一种用于时间序列分类的非常快速(几乎)确定性变换
艾伦大师的 sk time/mini rocket . ipynb-图灵研究所/sktime GitHub*
误导性图表
…以及如何修复它们!
任何与数据相关的工作都有一个可能被低估的方面,那就是呈现和可视化您的结果。交流你所掌握的数据会非常困难。随之而来的是意外创建误导性图表的可能性。
虽然我们大多数人都知道饼状图可以呈现的许多问题(这里的、这里的和这里的),但图表可能会在许多方面产生误导。

不是所有的饼状图都不好!图片由作者提供。
为了客观地看待这个问题,我发现自己在过去创造了误导性的图表,现在必须小心不要再这样做了!
我认为大多数人都有这个问题。我们毕竟是人,并严重受制于我们的偏见。
注意会导致误导性图表的陷阱
在本文中,我们将讨论图形中的常见错误,并定义修复它们的方法。
注意:通过数据可视化,可以发现许多规则的例外。记住这一点,下面的原则仅仅是指导方针,绝对不是真理。
1.操纵 Y 轴
可以说,误导性图表最常见的形式是 Y 轴被操纵。当相互比较大的数字时,许多人试图从 Y 轴中排除零,以便更好地显示实例之间的差异。
这很容易引起误解,因为它似乎夸大了实例之间的差异。很重要的一点是,要知道你看到的是数值之间的相对差异还是绝对差异。
让我们来看一个真实世界中 Y 轴被操纵的例子:

操纵 Y 轴人为放大差异。检索到此处。
视觉上,从上面的例子看,获得福利的人数增加了一倍。然而,因为他们从 94,000,000 开始,差别被非常夸大。
解决办法
这个问题有两个解决方案。
首先,简单地在你的图表中包含零值。它将按原样显示数据,没有读取 Y 轴的困难。
第二,可能有些时候加零实际上是相当误导的。假设您想要在 0–100 摄氏度的范围内观察人体体温。这种范围会阻止您发现患者体内威胁生命的几度体温升高。在这种情况下,删除零是有意义的。
在这种情况下,我会建议你添加一个零断点来表示你已经从图表中删除了零。数据可视化的透明性非常重要。

通过从零开始或添加零断点来修正误导性图表。图片由作者提供。
2.两个 Y 轴
有时,您可能希望看到具有不同 Y 轴的两条线。最著名的例子是泰勒·维根创造的,他创造了一系列有趣的可视化,展示虚假的相关性。
虚假相关是指两个事件或变量看似相关,但没有因果关系。您可以通过匹配两个不同事件的 Y 轴来创建这种效果。

由泰勒·维根创作。
结果就是上面这个惊人的可视化。通过重叠这两条线,现在看来任何科学支出都与自杀有关。事实上,这绝对不是这种情况,这是一个很好的带有两个 Y 轴的误导图的例子。
解决办法
在两个 Y 轴上都包含零是一种快速解决方法,可以防止图表产生误导。然而,如果没有任何可证明的因果关系,您的线碰巧重叠,您仍然可能偶然发现虚假的相关性。
这时,您应该考虑根本不显示图表。当你提出这样的关系时,用额外的数据、实验或结果来支持它们是很重要的。
3.三维图形
除了饼状图,这是我个人最讨厌的东西…
具有两个轴的 3D 图形!
这些图表摆脱了比例,并有根据角度使事物看起来更大或更小的趋势。

In,Junyong & Lee,Sangseok。(2017).统计数据展示。韩国麻醉学杂志。70.267.10.4097/kjae.2017.70.3.267. 此处检索。
上面的例子摘自 Junyong In 和 Sangseok Lee 的论文,他们在论文中研究了数据呈现是如何呈现和扭曲信息的。
从这个例子中我们可以看出,两张图表中的死因 A 看起来非常不同。事实上,对我来说,A 和 B 在 3D 图中看起来非常相似!
解决办法
我认为,在很多情况下,3D 图比具有相同数量轴的 2D 图更能提供信息。
不要用 3D 图!
一般来说,我会建议你不要使用 3D 图形,因为它太容易陷入失真陷阱。
4.不当缩放
为了使可视化更具视觉吸引力,一些人倾向于在图表中使用图像和象形图。虽然这可以增强图表,但也有可能创建一个误导性的图表!
象形图的问题可以在它们的相对大小中找到。假设你有两个值,100 和 300,你想用象形图来表示它们。
如果你将它们统一缩放,就会产生一种感知上的误导性比较。通常,人们倾向于解释象形图的面积,而不仅仅是它们的高度。

调整象形图的大小会导致尺寸的过度夸大。不是 3 倍数值,感觉是 9 倍!图片由作者提供。
上图显示了调整象形图的大小是如何导致一个误导性的图形的。通过将高度与数值相匹配,你实际上是在求差值的平方,从而夸大了较大的象形图。
解决办法
解决方案相当简单。与其让人们关注象形图的面积,不如让他们关注高度。您可以通过堆叠象形图而不是调整区域大小来实现这一点。

通过堆叠而不是调整大小来修复象形图。图片由作者提供。
5.樱桃采摘
创建误导性图表的最麻烦的方法之一是摘樱桃。摘樱桃是有选择地挑出支持你观点的证据,而忽略不支持你观点的数据的行为。
当你期望从数据中得到某些东西时,很容易陷入这个陷阱。不幸的是,有些人故意挑选最适合他们的数据。
例如,下图显示了从 1997 年到 2012 年的全球气温。这个图表的目的是为了否定全球气温正在上升的说法。

通过挑选 15 年来误导图表,使全球变暖无效,并忽略所有其他年份。从这里检索。
事实上,从图表来看,气温似乎已经稳定了几年。然而,这个图表有很大的误导性!
cherry 图表选择了证实他们观点的 15 年,而忽略了在那之前发生的任何事情。

关于全球变暖的全部故事。从这里取回。
如果你把从 1900 年到 2012 年的所有平均温度相加,你会看到一个非常不同的视图,如上图所示。
你可以看到,如果你选择一小部分数据,你很容易误导你的观众。
解决办法
重要的是,你要问自己以下问题:
“我没有看到哪些数据?”
回答这个问题可以帮助您避免创建一个仅呈现数据的单个有偏见视角的图表。
感谢您的阅读!
如果你像我一样,对人工智能、数据科学或心理学充满热情,请随时在 LinkedIn 上添加我,或者在 Twitter 上关注我。
缺失值处理—插补和高级模型
不同插补方法和自动纳入缺失值的模型的利弊

阿尔文·恩格勒在 Unsplash 上的照片
缺失值是许多数据科学项目中的常见问题。在我上一篇关于缺失值的文章中,我讨论了缺失数据和识别每种类型。在这篇后续文章中,我们将讨论处理缺失值的插补方法和模型。
归罪
插补是处理缺失值的有效工具。通过插入一个描述性的值,甚至根据剩余的已知值计算一个值,可以缓解不同缺失数据类型的问题。
虽然没有一种方法是完美的,也没有比实际数据更好的方法,但插补可能比完全删除实例要好。
缺失值插补有许多不同的方法,但这里只重点介绍几种方法。
- 简单插补
- KNN 插补
- 迭代插补
这些方法可以在常用的 scikit-learn 包中找到,并且与 Python 中的标准数据格式兼容。用给定的估算器将缺失值估算到数据帧中的基本过程写在下面的代码块中。
imputer = SimpleImputer(strategy=’mean’)# df is a pandas dataframe with missing values
# fit_transform returns a numpy array
df_imputed = imputer.fit_transform(df)# Convert to pandas dataframe again
df_imputed = pd.DataFrame.from_records(df_imputed, columns=df.columns)
简单插补
最基本的插补方法是为每个缺失的数据点插补一个常数值。或者,您可以使用这些方法来计算和估算数据集的平均值、中值或最频繁值。
当要素数量相对较大且缺失值较少时,这是一种实用的方法,因为少量缺失值对整体模型性能而言可以忽略不计。
KNN 插补
KNN 插补提供了比简单插补更详细的方法。使用与具有缺失值的实例最相似的 K 个记录,可以对缺失值和非缺失值之间的一些依赖关系进行建模。
因此,这种方法更加灵活,可以处理随机丢失的数据。
KNN 插补在计算上比简单插补更昂贵。尽管如此,如果您的数据集不在数十万条记录的范围内,这种方法也能很好地工作。
迭代插补
与 KNN 插补相似,迭代插补可以模拟已知值之间的复杂关系,并预测缺失特征。这种方法是一个多步骤的过程,它创建一系列模型,根据其他要素的已知值来预测缺失的要素。
迭代插补是一种复杂的算法,但总体方法相对简单。
- 用简单估算法估算缺失值。这一步允许模型正确地拟合和预测。
- 确定插补顺序。实现有几个选项。此参数会影响最终预测,因为以前的预测用于将来的预测。
- 通过在所有其他特征上训练模型来估算一个特征。目标变量是包含一些已知值的被估算的特征。
- 对每个特征重复此过程。
- 对所有特征重复该过程几次,或者直到完整迭代之间的变化低于阈值容差。
迭代插补使用贝叶斯岭回归作为默认估计量;但是,您可以根据自己的选择对其进行修改。
迭代插补的一个缺点是,与其他插补方法相比,它的计算成本更高。因此,对于庞大的数据集,KNN 插补可能更可取。
插补方法的比较
这些估算方法在估算数据的方式上有所不同。为了比较每个估算者,我进行了几次实验,删除数据,然后用每个估算者再次输入数据。
所使用的数据集针对不同程度的遗漏随机移除数据。实验进行了十次,以确保结果的有效性。使用三个 UCI 数据集重复该实验。
插补后,具有真实已知值的原始数据集与插补数据集进行比较。计算两个数据集之间的均方误差,以评估插补方法的有效性。
从实验中可以清楚地看出,由于有许多缺失值,简单插补的性能会迅速下降。最频繁的插补总是表现较差,也许只有当数据的分布众所周知时才最好使用。
KNN 提高了性能,但更依赖于底层数据分布。
当数据中有许多缺失值时,迭代插补优于插补。但是,当有许多丢失值时,这些方法可以提高性能。另一方面,当只有少量缺失值时,简单插补的表现相当。

用不同的插补方法重建数据集(作者提供照片)
处理缺失值的模型
可能的情况是,缺少值这一事实很重要。例如,假设有一个乳腺癌数据集的扩展。在这个备选集中,有一个与乳腺癌相关的特征“测量血压”。
在某些情况下,医生不会测量该特征,因为从医生的角度来看,所有其他特征都表明它是不相关的。
现在,这一特性确实有实际价值,但不得而知。此外,与输入其他值相比,数据丢失的事实可能更有价值。
幸运的是,有一些模型可以处理缺失数据而不需要插补。这里使用的每个模型都是一个梯度推进模型,一个基于决策树的集成模型。
这些模型处理树内拆分处的缺失值;缺失的值会进入分离侧,从而最大程度地降低分离处的总损耗。当有一个以前在测试过程中从未见过的缺失值时,实例会转到包含最多样本的一侧。
估价
这些模型中的每一个都有许多允许微调的参数。有关如何正确微调模型的详细信息,请参见我的另一篇关于超参数优化的文章这里。
我使用乳腺癌数据集和默认模型超参数来比较每个模型。
对于每个测试,我会从数据集中随机删除越来越多的数据,然后拆分这些数据。我用 70%的数据训练模型,用 30%测试每个模型。
该实验在每个缺失水平下重复 10 次,并对 10 次实验的准确度进行平均。结果如下图所示。
这里的性能不如随着丢失数据部分的增加而降低的性能相关。在大多数情况下,超过 50%的缺失数据不太可能。请注意,当删除 50%的数据时,这些模型的性能下降不到 5%。

比较缺失数据的不可知模型(作者提供图片)
结论
丢失数据是数据科学中令人沮丧的一个方面。此外,确定丢失数据背后的机制本身也是一个很大的问题。
但是,有几个选项可以用来处理或避开丢失的数据。这些方法各有利弊,没有完美的办法。
插补为保持数据的灵活性提供了一个出色的解决方案,无需完全删除实例。然而,这里没有讨论更多的插补方法。
总结 TLDR;
- 插补是处理缺失值的有效方法。尽可能使用 KNN 插补和迭代插补来模拟随机缺失和非随机缺失数据。选择主要取决于可用的计算资源和数据的性质。
- 如果数据缺失的事实是有意义的,请尝试在训练和预测期间允许缺失数据的模型,如 XGBoost、直方图梯度增强和 LightGBM。
如果你有兴趣阅读关于新颖的数据科学工具和理解机器学习算法的文章,可以考虑在 Medium 上关注我。
如果你对我的写作感兴趣,并想直接支持我,请通过以下链接订阅。这个链接确保我会收到你的会员费的一部分。
https://zjwarnes.medium.com/membership
缺失值处理-缺失数据类型
有不同类型的缺失数据。了解你的血型决定了应该如何处理。

缺失数据
缺失数据在数据科学和机器学习的许多不同领域都很常见。不幸的是,有效地处理这一问题具有挑战性,而且通常没有最佳解决方案。
缺失值会严重影响您的模型,这取决于您处理它们的方式。值缺失的事实可能在您的模型和目标问题中具有重要意义。
没有完美的方法来处理丢失的值。本文将讨论不同类型的缺失值,何时可以考虑删除缺失值的实例,以及何时应该考虑不同的方法。
对于每个示例,我都引用随机生成合成数据集。该数据集具有四个要素 A、B、C、D、五个实例和二元要素。

缺失数据的类型
缺失数据通常分为四种类型。完全随机缺失(MCAR),随机缺失,非随机缺失,结构性缺失。每种类型都可能出现在您的数据中,甚至是多个缺失数据类型的组合。
完全随机失踪(MCAR)
这些丢失的数据点没有可辨别的模式。通常,选择要估算的值取决于抛硬币。当您无法从剩余的已知变量中预测缺失值时,这些值最容易识别。它丢失的事实与其余变量无关。
然而,通常情况并非如此,除非数据是以这种方式专门设计的。这方面的一个例子是明确随机生成的数据或使用预定义列表中问题的随机子集的调查数据。即使从产生缺失值的不一致传感器测量的数据也可能仍然具有缺失数据的潜在模式。
通常,假设数据完全随机缺失不是一个合理的假设,除非产生数据的机制是完全已知的。
例如,我在下表中显示了 MCAR 的数据。这些值是随机移除的,没有可辨别的模式。

随机数据完全缺失(作者照片)
随机失踪(MAR)
与 MCAR 相反,MAR 不认为其他变量不能预测缺失值。这种类型可能更常见于正确记录数据时出现错误的情况。
例如,假设一个传感器错过了某一分钟的测量,但捕捉到了前一分钟和后一分钟的数据。丢失的值可以从剩余的值粗略地内插,达到合理的精确度。
要确定数据是否包含随机缺失数据,请观察不同特征的条件概率。例如,与比例分布相比,如果另一个特征的条件增加了特定值的可能性,则该值很可能是 MAR。
我在下面的数据集中显示了 MAR 数据。特征 D 缺失,可以从剩余数据中进行概率推断。

随机数据缺失(作者照片)
非随机缺失(MNAR)
非随机缺失或不可忽略的数据是数据缺失原因机制已知的数据。尽管如此,这些值仍然不能被有效地推断或预测。
这种形式的数据的一个例子是用于回避问题的调查的人口统计。例如,也许某一年龄/收入阶层的人拒绝回答他们拥有多少辆车或房子。这里的机制可能是显而易见的,但是推断丢失数据的实际值是很难准确预测的。
这种类型的缺失数据类似于下面的缺失数据类型,结构性缺失数据,但是有一个很大的区别。在结构性缺失数据中,该机制易于分析。相比之下,当数据真正丢失而不是随机丢失时,该数据很难处理,因为机制可能不明显。
这是一个丢失非随机数据的例子。当特征 A 为 0 时,特征 C 缺失。

不随意遗漏数据(作者照片)
结构性缺失数据
丢失的数据丢失的原因很明显。通常,导致数据丢失的机制很容易推断出来。这种类型是故意丢失的数据。例如,一项要求就业收入的调查可能会遗漏那些没有工作的人的值。
这种类型的缺失数据也可能纯粹是数据工程的结果。例如,在处理时间序列数据时,将数据转换成表格格式是很常见的。虽然这种表格格式对于开箱即用的监督模型来说是理想的,但是处理数据的过程可能会产生缺失值。当聚合某个时间间隔内的数据时,如果数据具有不一致的时间戳,则某些时间间隔没有记录的数据点。这种结构会导致表格聚合中缺少值。
有些情况下,删除结构性缺失数据是合适的。但是,根据正在解决的问题,这可能会导致其他问题。
这是一个结构性缺失数据的例子。最后一个实例几乎包含了所有缺失的值。可能在记录数据或数据处理过程中出现错误。

结构性缺失数据(作者照片)
缺失数据的类型
很可能仍然不清楚如何准确区分丢失数据的类型以及如何处理每种数据类型。此外,即使您确定是特定类型的数据,也可能有不同缺失数据类型的元素,这使问题更加复杂。
继续进行的选择通常分为三种方法。
- 删除丢失的数据实例。如果缺少的值很少,并且有很多数据,这种方法应该是可以接受的。
- 插补方法,这是一种常见的方法,它允许大多数模型照常运行,无需任何修改。
- 保留缺失的值,并使用包含它们的模型。这种方法限制了可用的模型。
删除丢失的数据
根据数据实例的数量或数据类型,简单地删除实例是最直接的方法。如前所述,当数据在结构上缺失时,可以做到这一点。但是,在删除这些数据时,必须考虑删除这些实例会如何影响模型。
删除数据时更重要的问题是有多少记录会受到影响。当数据集很大,并且很少有实例包含缺失值时,就没什么可担心的了。然而,在许多问题中,数据集很小,并且每个实例对于整体理解问题是至关重要的。
多方面的方法也是切实可行的。例如,在彻底分析数据中存在的缺失值并确定频率后,决定要删除的记录子集可能是最佳选择。
然而,简单地删除数据仍然感觉像是通过将记录的其余部分合并到模型中而错过了对模型的潜在改进。
结论
缺失数据有几种类型,不同类型之间的差异很微妙。此外,丢失的数据可能在同一个特征中呈现多种类型,这加剧了问题的严重性。在某些情况下,可以删除丢失的数据。然而,其他一些方法更有效。在这些关于缺失数据的帖子的第二部分,讨论了处理缺失值的插补方法和模型。
总结 TLDR;
- 缺失数据有多种形式。因为根据定义它们是未知的,所以通常很难确定确切的类型。
- 如果您有大量的数据和很少的缺失值,删除缺失值的实例是可以的。
如果你有兴趣阅读关于新颖的数据科学工具和理解机器学习算法的文章,可以考虑在 Medium 上关注我。
如果你对我的写作感兴趣,并想直接支持我,请通过以下链接订阅。这个链接确保我会收到你的会员费的一部分。
https://zjwarnes.medium.com/membership
缺失的值将消失
处理数据集中缺失值的策略。

清理数据是几乎所有数据项目的必要组成部分。数据清理的一个常见主题是处理不完整的样本。插补有多种选择,每一种都有其好处和风险。我将介绍几个我喜欢使用的标准选项,然后讨论选择插补策略时的重要考虑因素。
策略 1 —丢弃脏样品
这是最直接的方法,也是我遇到的最常见的方法之一。删除包含缺失值的行。事实上,作为我最初的 EDA 步骤的一部分,我经常选择这样做来获得一些关于我的数据看起来像什么的基线图。然而,这牺牲了你的一些宝贵的数据!在大多数情况下,这不是一个很好的策略,因为您正在减少用于得出结论或训练模型的信息总量。
策略#2 —用 0 替换缺失值
这是我看到人们采取的另一个简单策略;然而,我认为这几乎不合适。用常数替换缺失值是一个不错的选择,但是,这个常数不应该总是 0。这里的问题是,在大多数情况下,0 是一个任意的选择,并不反映数据集试图描述的实际情况。这里有一个警告,如果你已经将你的数据归一化到平均值为 0,那么 0 是一个很好的选择。(看下一个策略!)
策略# 3——用平均值替换缺失值
这比用 0 这样的任意值替换丢失的值要好得多。这是因为样本都分布在该值周围,如果缺失值没有缺失,它很可能位于平均值附近。在我看来,这是最简单的方法。此外,请记住,这里有不同的可能的平均值。通常,插补的一个好选择是该列的总体平均值。但是,在处理时间序列数据时,您可以使用该特征的个体平均值。
策略 4——用线性模型代替缺失值
这种策略适用于时间序列数据。有时,当您的数据有趋势时,每个个体的平均值通常不适合插补。这是因为它没有考虑那个变量随时间的变化!但是,用简单的线性回归模型拟合数据将会估算(和外推)时间序列的数据点。不幸的是,当您的数据集显然没有描述线性趋势时,这就没什么用了。
策略# 5——用多项式模型代替数值
同样,这也是时序数据集的一个选项。在前面的策略中,我们讨论了使用线性模型进行插补和外推,我们可以通过使用多项式模型来进一步扩展这一概念。这允许我们估算非线性趋势的值!
策略 6 —使用聚类替换值
输入值时的另一个有效选项是使用无监督学习技术(如聚类)来识别个人属于数据集的哪个子组,从而对要输入的值做出明智的决定。我已经在 K-means 聚类中成功地使用了这种策略,最近又在相似性传播中使用了这种策略。相似性传播是识别代表每个聚类中心的样本的聚类技术。使用这个中心样本会给你一些平均值的概念,用于该组其他成员的插补。
策略# 7——用其他机器学习模型代替价值观
我想你可以开始看到一个熟悉的模式。有无数其他有效的机器学习(ML)模型可以用于此目的。我不会把它们一一列出来,我会把这个策略作为一个总括。请记住,在为此目的选择机器学习模型时,您必须仔细了解该模型的工作原理,以及它可能通过插补向您的数据集引入什么样的偏差。

缺失值消失!| GIF from: Giphy
在处理插补时,还有其他重要的考虑因素。一个大问题是稀疏性,以及您处理的实际缺失数据有多少。在有大量缺失数据的情况下,像均值插补这样的方法通常不是一个好的选择。它极大地改变了数据集的方差,并导致分布不再代表数据集编码的基础概念。我觉得线性/多项式/ML 建模在这些情况下是更好的选择,因为它们估算了一系列值。虽然它们很少与基础数据集的实际方差相匹配,但它们将大大减少在稀疏数据集上使用均值插补等方法获得的巨大峰度。
当你希望将多项式模型用于插补和外推时,它有一个明显的缺点。高阶多项式倾向于在你适合它们的区域之外向无穷远处发射。这意味着您的推断值很有可能不代表实际趋势。当然,这是你可以想象的,所以不要完全排除这个选项,只要记住在你想要推断的整个领域检查这个模型发生了什么是很重要的,而不仅仅是你适合的领域。
有时,如果您清理这些数据的目的是构建一个机器学习模型,您可能根本不需要清理这些值!现代的 ML 模型有时可以把缺失的信息当作一个独立的特征。例如,LightGBM 在内部使用缺失值作为模型信息的一部分!
缺失值是我们数据科学家永远不得不面对的问题。我希望这篇文章有助于阐明这个问题的复杂性,并为您了解如何管理它们的旅程提供一个起点。祝你好运,编码愉快!
https://pandas.pydata.org/ https://scikit-learn.org/stable/
我在机器学习生涯中犯的错误
办公时间
以及如何避免它们
事实是,在你的职业生涯中,你会犯很多错误。有利的一面是,对于你犯的每一个错误,你都有机会学习和提升。

布雷特·乔丹在 Unsplash 上拍摄的照片
在这篇文章中,你会遇到我在作为计算机视觉/机器学习工程师的职业生涯中迄今为止所犯的错误;以及你作为一名 ML 从业者如何避免我犯的每一个错误。
为什么这很重要?
普通人一生中花 50 年的时间在工作上,对我们大多数人来说,我们才刚刚开始我们的职业生涯,此外,我发现最好向那些更有经验的人学习,因为这让我在职业发展和成功方面处于最佳位置。两分钟阅读这篇文章可以让你避免数月或数年的职业停滞。
我忽视了我的学习

弗瑞德·卡尼在 Unsplash 拍摄的照片
机器学习的全职工作可能会让人不知所措。在我职业生涯的初期,平衡朝九晚五的工作和保持非工作时间的学习比现在更容易管理。
机器学习领域的兴奋和新奇,加上一个责任很少的角色,意味着我可以在工作之外额外花两个小时跟上 ML 的发展并完成个人项目。
但是几个月过去了,我的责任和工作量增加了。
在你的 ML 角色中获得更多的责任是一种成就(特别是如果伴随着💲).虽然,过了一段时间,你的脑力承受一个朝九晚五的角色的能力减弱了,在这个角色中,你变得精神疲惫,同时保持 2-3 个小时的学习日程。
由于我的智力下降,缺乏动力,也许是精疲力竭,我停止了工作之外的学习。虽然在我的 ML 角色中有很多东西要学和做,但拥有一个成长的心态意味着我在知识和专业知识的不断积累中找到了职业成功和相关性。
如何避免
在与我目前工作的公司的首席技术官的一次坦率交谈中,事情发生了令人兴奋的转变。我解释说,我的外部学习努力正在减少。我的首席技术官认识到公司团队成员不断积累知识和技能的重要性。
请允许我指出一个显而易见的事实:你工作的公司会从你的发展和知识增长中获得积极的收益。如果你在工作之外的学习能让你找到一种有效的方法来实现公司旗舰产品中使用的算法,你的公司最终会从你的解决方案和新发现的知识中获益。这也是公司允许员工花一些时间进行个人发展的原因之一。
确保你不会落后于学习和发展的一个可靠方法是在工作时间向你的直线经理要求一段时间来学习和发展。大多数雇主和经理都会同意这一点,只要你提出一个合理的个人发展时间,也许是周四或周五下午。
一些雇主甚至更进一步,报销员工在书籍、视频和课程等学习材料上的任何费用。
你是否有兴趣跟我一起学习?请查看我即将在奥赖利开设的现场课程:
思维理论>实践

我最喜欢的消遣之一是听机器学习内容创作者的播客和 YouTube 视频,如莱克斯·弗里德曼、特维米尔、肯·吉等。通常这些节目的嘉宾是 AI/ML 专家,他们雄辩地讲述他们的领域。当然,他们的口才来自于他们各自职业生涯中积累的知识和经验。
你不会怪我认为我必须阅读大量的书籍和研究论文,才能变得和我们在播客上听到的嘉宾一样博学。在我机器学习之旅的早期,有几个月,我完全专注于理论。我阅读研究论文,从教科书上记笔记,这一切都是为了增加我对感兴趣领域的知识。
我的策略的问题是,ML 工程师并不仅仅依靠知识而成功。任何一个经验丰富的 ML 工程师可能会确认在 ML 的实践方面和理论之间存在着不均衡的分裂。研究;这种分裂在工程角色中更倾向于 ML 的实践方面。
虽然大量的理论知识是必不可少的,但在机器学习领域,应用知识解决问题是最重要的。这就是我们在这里的目的。
如何避免
你不必完全避免进行研究和获取 ML 理论知识。一些 ML 角色高度依赖于理论和研究。但对于我们这些渴望成为 ML 工程师和应用数据科学家的人来说,最好用动手实践经验来伴随你的学习。
ML 中涵盖的介绍性主题和概念是围绕神经网络及其内部工作的(权重、激活函数、偏差等)。).这些主题涵盖深入和广泛,为新的学习者提供一个坚实的基础。
避免忽视机器学习的实践方面的一个可靠方法是给自己配备有用的 ML 教科书,如用 Scikit-Learn、Keras 和 TensorFlow 进行机器学习实践;数据科学手册;数据科学从无到有等。这些书更关注 ML 的实际方面,避免与构建商业 ML 应用程序无关的深入信息。
没有足够频繁地使用“不”。

照片由 Gemma Evans 在 Unsplash 上拍摄
如果有人想让我为他们的“天才”工作,而我有一美元的时间。我会多几百英镑。在我职业生涯的早期,我答应了几乎所有要求我帮助和时间的人。对你遇到的每一个项目都说“是”的问题是你把自己分散得太开了。
说“不”的想法不仅仅适用于项目请求,软件工程师需要对超出范围的特性请求说不;当被要求使用机器学习技术来解决明显不需要 ML 解决方案的问题时,数据科学家需要说“不”。
如何避免
这个很简单:说“不”。试着说得更好一点,但你已经明白了。
我已经拒绝了几份写作工作和工作邀请,主要是因为我已经确定了自己承担项目和工作的能力,无论如何我都不会超出这个能力。
如果你犹豫要不要错过机会,试着通知有兴趣与你合作的个人、公司或组织,让他们知道你现在没空,但在接下来的几个月里可能会有一些工作机会,并且希望保持联系。这样做,你并没有完全关上大门。
独自做事

由 Eréndira Tovar 在 Unsplash 上拍摄的照片
对我来说,在本科阶段独自完成小组项目要比和其他不愿意努力或者根本不能胜任任务的同学一起工作容易得多。
虽然我独自完成了足够多的小组任务,但我错过了早期获得沟通和协作技能的机会。
在科技世界里,没有人能独自完成任何事情。有一些项目、软件和开源工具依靠在线社区的协作凝聚力蓬勃发展。
对于那些科技行业的新手来说,从这篇文章中可以学到一点:站在巨人的肩膀上总是比独自一人更容易。从更实际的意义上来说,利用库和工具使您的工作更容易,而不是重新发明轮子。
如何避开
不要害怕向高级开发人员或比你更有见识的人寻求帮助。与一个更有经验的开发人员进行两分钟的对话将会节省您筛选 StackOverflow 或错误日志的时间。
还有,寻求帮助不一定要亲自去;有很多在线社区都有专家愿意帮助那些恰当表达简洁问题的人。
以下是一些与 ML 工程师和数据科学家相关的论坛和讨论渠道:
结论
错误是游戏的一部分,但确保错误不再重复,并确保从每一个错误中吸取教训是一个很好的实践。
在机器学习领域的成功职业生涯是你愿意付出多少努力和时间来获取和实施 ML 知识,同时保持持续增长的心态的组合。
下面是这篇文章的主要内容。
- 如果你发现你在工作时间之外没有学到足够的知识来保持你的知识在一个合理的水平,试着与你的经理或雇主进行一次谈话,安排时间专注于你的个人学习发展。
- 虽然 ML 的理论是必不可少的,但是不要害怕深入到机器学习的实践方面。
- 不要过度投入到项目或任务中,让自己过于分散。
- 需要时,总是寻求帮助。
在我的职业生涯中,我肯定会犯更多的错误,并从中吸取教训。现在,我将留给您一些针对一般开发人员的通用技巧。
- 不要在生产环境中修复错误
- 不要直接推送到主服务器
- 不要把软件发布安排在周五晚上
- 当训练机器学习模型时,不要测试你的训练数据
- 数据稀缺?尝试增强
感谢阅读
想要更多吗?
- 订阅 在我发表文章时得到通知。
- 成为推荐媒介会员,支持我的写作 。
- 通过 LinkedIn 联系我
第一次使用 Twitter 数据时要避免的错误

阅读:我第一次使用 Twitter 数据时犯的错误
我刚刚花了很多时间收集、整理和分析 2020 年黑人的命也是命抗议活动的财富 100 强推文。在几千条推文之后,我遇到了很多困难和棘手的情况。下面我列出了一些我在使用 Twitter API 时遇到的错误。
许多这样的失误都在 Twitter 的文档中解决了,因此,尽管不必阅读所有内容就可以尝试网络抓取和机器人制作,但在做任何重要的事情之前,一定要浏览一下 API 的字段描述和限制!对于那些刚刚入门的人来说,这里有一个视频教程,这里有一个快速博客帖子——尽管这些绝不是唯一的教程。
确保获取全文数据。
这是一个非常标准的失误:tweets 过去只有 140 个字符,但现在是 280 个字符。确保无论是进行直接 API 查询还是使用包装器,都将extended设置为等于true(默认情况下,该值设置为false)。该参数将告诉 Twitter 您想要完整的 280 个字符。当对您的数据集应用任何类型的验证器时,我建议包括如下内容:
for tweet in dataset:
assert not tweet[‘truncated’]
这将有助于确保每一次抓取都能捕捉到完整的推文。
规范回车,因为 Twitter 没有。
\r\n和\n都可以出现在你的推文中。然而,当从 JSON 响应创建 CSV 时,这可能会导致问题。一个有两个单独行尾的 CSV 会让熊猫困惑,并且违背了商定的标准。也许最简单的处理方法是使用 python 的repr函数在字符串中显式包含特殊字符。这将迫使所有内容都在一行中,并防止混淆:
>>> lengthy_string = “Look at these cat pictures\n#cats”
>>> print(lengthy_string)
Look at these cat pictures
#cats
>>> print(repr(lengthy_string))
‘Look at these cat pictures\\n#cats’
但是,如果您希望分析文本数据,该方法将在以后添加步骤。另一个合理的解决方案是简单地删除\r,并用空格str.replace(‘\r’, ‘ ‘)替换它,但这种方法取决于您的研究问题。
使用正确的字段来确定转发
确定一条推文是否被转发的最可靠方法是查看是否存在retweeted_status字段或quoted_status字段。
is_retweet = hasattr(tweet, ‘retweeted_status’)
is_quoted = hasattr(tweet, ‘quoted_status’)
在解析 tweet JSON 时,这些字段并不明显。被转发的领域是一个红鲱鱼。它指示进行身份验证的用户(注册 API 令牌的人)是否发出了 retweet。而且找文首的“RT @”也不靠谱。
请记住,这两个字段是可空的,这意味着由于原始 tweet 可以被删除,与 retweeted_status 字段相关联的值可以为空。所以,你需要寻找这些字段的存在。不要给他们设置任何条件。类似于…
if tweet.get(‘retweeted_status’):
print(‘is_retweet’)
…如果原始推文被删除,会错误地将一条推文定性为普通推文。
确定一条推文是否是回复是复杂的。
事实证明,没有一个单一、统一的方法来确定一条推文是否是回复。一些在线评论者建议在推特 JSON 中寻找字段in_reply_to_status_id。但是一些正常的 tweets 有这个字段,只是设置为 null。您可以查看字段是否为空,但这也是不可靠的。如果原始推文被删除,则in_reply_to_status_id变为 null,如果用户删除了他们的帐户,则in_reply_to_user_id变为 null。当然也不是每条以“@”开头的推文都是回复;只有在回应特定状态时,推文才会被视为回复。
简而言之,没有一种可靠的方法可以做到这一点。幸运的是,这个问题在 Twitter API 的 v2 中得到了解决。要了解更多信息,请阅读 Twitter 开发者社区论坛上的讨论。您还可以根据 API 请求中的回复进行过滤。
不要将 Twitter IDs 存储为整数。
Tweet IDs 是非常大的数字,一些软件由于内存空间不足,会在你不知道的情况下四舍五入整数 id。Twitter 建议您专门使用 id_str 字段。如果您的程序将 ID 视为一个字符串,那么它将不会面临存储为整数时所面临的内存挑战。大多数程序可以将 Twitter IDs 的当前大小作为整数来处理,所以欢迎您自担风险使用这些整数,但是在验证过程中要考虑仔细检查 id_str 是否等于 id。
让我的生活变得更轻松的其他技巧
-
微软 Edge 加载推文的速度比 Firefox 或 Chrome 快得多。如果您需要滚动浏览许多推文,这可能会很有用。
-
请记住,并非一条推文的所有语言都包含在
full_text中。如果你在做文本分析,如果对你的研究很重要,你可能也要考虑图像或视频中的文本。如果您需要下载与推文相关的媒体,这里有一个片段: -
确保您熟悉 Twitter API 的局限性。你只能获得最新的 100 条转发,你只能在个人资料上查看最新的 3200 条推文,你只能每 15 分钟获得给定用户的最新 75000 名关注者。程序员对这些限制的误解导致了巨大的软件失败,所以在创建一些被认为是可靠的东西时要记住它们。
阅读更多关于数字人文中心@普林斯顿的博客
出生错误:最后的眉毛
实践教程
一份让你皱眉头的 Allomancer 社会动力学分析
【TLDR】:欣赏一些 的交互可视化 总结了 Mistborn 中的眉毛交互数据。

《雾与怒》——罗斯·罗斯 88 的作品(经许可使用)
如果你发现自己对布兰登·桑德森的《生不逢时》系列感到惊讶,你并不孤单。是的,是一本有趣的奇幻读物:第一个时代的特点是一群可爱的小偷,他们在暴虐的帝国的钢铁统治中穿梭于社会差异、政治阴谋和革命之间。虽然 Mistborn 的魔法系统 allomacy——能够通过摄取各种金属的小碎屑来巧妙运用多种能力——可能是我读过的最酷的魔法系统,但故事中有一个特征确实超越了其他特征:眉毛。
是的,眉毛。这个系列中的角色互相“扬眉”的次数令人震惊。也许是有声读物讲述者的语调让这句话如此清晰地跳了出来,但当我们听的时候,它成了我和我的伙伴的一个笑话,每当一个角色“扬起眉毛”,我们就会立即转向对方,露出我们最疯狂的扭曲眉毛的表情。
但是后来…我很好奇。
凯尔西耶“扬了多少次眉毛”?有什么模式吗?他是平等地向所有的船员扬起眉毛,还是发现某些船员特别令人费解?作为一名有抱负的数据科学家,我决定更深入地研究一下。
下面是对生不逢时系列的第一本书《最终帝国》中的社会动态的初步分析。第二册和第三册将在以后的文章中讨论。**这篇文章包含了最终帝国、*** 的剧透,所以如果你还没有看过并且想看的话,就此打住,留到以后吧。但是,如果你被吸引住了,并对此感兴趣,请继续阅读涵盖三个主要分析的数据和解释:*
- 哪些角色最常扬起眉毛?哪些角色最常接受眉毛上扬?
- 人物的扬眉行为有时间意义吗?
- 根据人物眉毛交流的频率和方向性,我们可以推断出他们之间的社会动态是怎样的?
眉毛数据收集
仅提供数据收集方式的简要概述:
- 我们借了一本电子书《最终帝国》、、【升天之井】、和【千古英雄】。**
- 我们控制了“眉毛”这个词的例子
- 我们阅读了关于眉毛互动的文章,以确保它确实是眉毛的“抬起”或“竖起”或“提升”——基本上我们计算了任何类型的提升。
- 值得注意的是,“眉毛”这个词在整个系列中只出现了两次,而且角色之间没有相关的互动:在第三部中,时代的英雄,马什在进入一个空房间时扬起眉毛,而斯洛斯威特被描述为有“浓密的眉毛”这些不包括在数据中。
- 最后,我们记录了:1)扬起眉毛的角色(“源”),2)扬起眉毛的角色(“目标”),以及 3)交互的页码。
总的来说,我们在最终帝国中记录了 53 次眉毛互动,在提升之井中记录了 48 次,在时代英雄中记录了 42 次。
眉头为谁扬起
我的第一个分析是简单地计算每个角色的眉毛源(“raiser”)和目标(“raisee”)实例的总数(图 1)。

图 1。 每个角色的眉毛交互类型(“源”或“目标”)的原始计数。(图片由作者提供)
不出所料,Kelsier 是最常见的眉毛来源,给出了 19 个明显的提升。这是有道理的:他作为船员领袖的地位,加上他流氓般的魅力,自然有助于对他古怪的船员进行诙谐的戏谑和友好的屈尊俯就。此外,如果“扬起眉毛”是桑德森的作者怪癖之一,凯尔西作为两个主要视点(POV)角色之一的角色只是给他更多的页面时间来晃动眉毛。
下一个最常见的眉毛来源是凯尔西耶的学徒和共同主视点角色,文,打卡在 8 次加薪。再次,文获得了大量的页面时间,所以她第二名的地位并不令人惊讶,尽管讽刺的舒缓微风以 6 紧随其后。但是真正让 Vin 突出的是她的第一眉毛目标地位:Vin 接受了高达 22 次的眉毛提升!安静而奇怪,船员们并不总是知道如何看待文,常常带着关切或怀疑的眼光看待她。然而,这种名副其实的眉毛瞄准山不能纯粹用页面时间来解释,因为凯尔西耶只收到 6 次加薪,并说了很多荒谬的事情。
到目前为止,这些结果并不令人兴奋:这两个视点角色参与了大部分眉毛互动,主要支持角色 Breeze,Ham,Dockson,Sazed 和 Elend 各增加了少量。几个次要人物顺便引起了一两次争论。
接下来,我研究了眉毛互动的时间动态,以观察角色的眉毛行为是否在整个故事中发生了变化。
眉间的颞弧
为了更好地了解眉毛在整个系列中的分布,对于每个角色,我绘制了他们随着时间推移的眉毛互动——作为来源或目标。x 轴是页码(时间),y 轴包括至少 3 次互动中涉及的主要人物(图 2)。

图二。主角眉在最终帝国中的互动。(图片作者)**
从这个视图中,我们仍然可以清楚地看到 Kelsier 和 Vin 参与了最多的眉毛交互,因为它们有最多的点,但是在这些交互发生的时间上有一个有趣的差异!凯尔西耶在整个故事的开始有一个相对规则的眉毛上升模式,穿插着针对他的零星眉毛。但是在全书进行到大约⅔的时候,他突然停止对其他人皱眉…这不是他的死亡——这发生在书的最后,在图中我们可以看到在最后的几百页中他仍然活着并且是目标(橙色的点)。我相信这次突然的停火与他的计划(摧毁耶登的隐藏部队)的重大挫折几乎同时发生。尽管意识到他推翻主统治者的计划极其冒险,成功的可能性极小,但凯尔西耶可能意识到开玩笑的时间已经结束了,他需要认真对待。当然,他仍然向工作人员宣传微笑和保持积极态度的重要性,但是当他调整最后一幕的计划时,他高度活跃的眉毛不再是他领导风格的一部分…
相反,Vin 在这本书的整个前半部分都被当作一个眉毛上扬的目标。如前所述,她易受惊的性格需要一段时间才能让其他人适应,当她试图在这群愚蠢的船员中找到自己的位置时,她仍然保持警惕。
但是后来!事情发生了变化:在故事进行到一半的时候,文突然连续五次竖起眉毛!虽然在她突然成为眉毛来源后,她仍然获得了一些加薪,但这一时刻似乎让她敞开了心扉,她在未来变得舒适地扬起眉毛…
发生了什么事?是什么引发了这种转变?为了进一步挖掘这个问题,我重新格式化了图形设计,以额外可视化每次眉毛相遇的源和目标之间的连接(图 3)。这个图有点野,提前为视觉上的复杂道歉!虽然我喜欢图 2 的简单,但我设计了这个替代版本,以允许随着时间的推移对特定眉毛互换进行调查。我认为这个图作为一个交互式可视化稍微有效一些,在这里可以看到。
只是为了便于您了解,它的设置方式与图 2 相同:x 轴是页码(时间), y 轴有一个部分用于标记每个角色的眉毛交换。现在,如果角色是眉毛的来源,那么一个点就是菱形,如果角色是眉毛的目标,那么这个点就是圆形,而不是蓝色和橙色。垂直线现在直接连接每个交互的源和目标,颜色与源角色相关联。

图三。在最终帝国的过程中所有角色的眉毛互动,现在与眉毛“源”(菱形)和“目标”(圆形)的每个互动由一条垂直线连接。(互动版,我法师作者)
回到对 Vin 突然加入眉毛提升者联盟的分析,让我们关注她的互动模式,下面用粗体强调:

在图 3 中仔细观察 Vin 的眉毛互动。(图片作者)**
在剧情中间沿着文的轨迹滑动,我们仍然看到开放的圆圈标志着她是许多眉毛扬起的目标——它们主要来自凯尔西耶,但哈姆、微风、马什和萨泽德也加入进来。但是上面说的 Vin 的眉源簇完全是* 针对 Elend 的!几页前,在 Keep Venture 的舞会上,当他们初次相遇时,Elend 偷偷提高了 Vin,但在 Vin 的第二次和第三次舞会上,她鼓起勇气对一位杰出的贵族提高了眉毛。*
这标志着文转型的一个关键时刻:我们看到她是一个街头顽童——躲在阴影中,试图逃避审查——看着她成为宫廷阴谋游戏中的真正玩家,并在她作为一个出生不熟的人进入权力中心时成长为一个自信的船员。眉毛上扬显然是这一弧线的重要标志:在 Elend 公开展示眉毛肌肉后,她接着向她最亲密的导师 Sazed 和 Kelsier 开火。事实上,Kelsier 在该系列的最后一次眉毛互动是作为 Vin 的接收者。哦,徒弟真的变成师傅了…
一个额头抬起他们,并在深处捆绑他们
在我的最终分析中,我对人物的整体社会网络感到好奇,这些网络是由他们的眉毛交流来定义的。在这里,我删除了时间组件,并在一个有向图中总结了数据(图 4)。

图 4。有向图捕捉了最终帝国中所有角色眉毛扬起的频率和方向性。每个角色都是一个节点,大小与他们参与的眉毛互动总数成比例。节点之间的每条边指示源角色和目标角色之间眉毛交互的方向;箭头的大小也与从源到目标的眉毛总数成比例。(图片作者)**
这个网络再次表明,Vin 和 Kelsier 是眉毛活动的中心,但从 Kelsier 到 Vin (12)和从 Vin 到 Kelsier (1)的提升之间有明显的区别。这种方向性感觉很重要,因为它强调了 Snark 大师与令人困惑的学徒权力动态。
我们还可以看到更多的船员动态:微风和火腿与凯尔西耶和 Vin 创建了一个紧密连接的组件。也许我们可以感觉到微风对哈姆的哲学化的高度恼怒,因为从微风的节点到哈姆的节点有更多的眉毛。此外,文和凯尔西耶与萨泽德交换眉毛的频率大致相同,而坚定的道克森只有外向的眉毛:朝向凯尔西耶和一名无名士兵。合乎逻辑的是,没有人会对 Dox 提出质疑。
值得注意的是,文是唯一一个与贵族成员交换眉毛的船员。她对贵族们来说也是一个谜,并从埃伦和珊那里得到友好和不那么友好的加薪。但是,最粗的箭头再次标志着对埃伦眉毛的稳固流出,展示了她日益增长的信心。虽然她的贵族女性即兴创作有点粗糙,但她最终接受了自己的角色,并从政治棋子演变为政治玩家。
(以下是该网络的互动版本。虽然交互性对于理解网络来说并不重要,但我正在学习/练习 D3,并认为拖动角色泡泡很有趣。)
低眉和无眉
虽然“扬起眉毛”是出生不良的宇宙中许多角色共有的一种习惯,但也有一些明显的例外。在船员中,俱乐部是零眉交流。这位头发花白的将军没有被任何船员的恶作剧所迷惑,也没有说任何特别令人费解的话。在邪恶中,两个义务者竖起了几根眉毛,但我们从统治者或任何钢铁检察官那里都没有(他们甚至有眉毛吗?).当这些坏蛋到达现场时,激烈的行动迅速跟进,没有多余的时间来开玩笑。
眉毛上扬,帝国衰落
( 🎵我们已经看到了船员们所做的一切!🎵 )
这群傻乎乎的船员齐心协力,完成了一项不可能的任务。当他们的金属掌握占据了中心舞台,他们用钢推、铁拉和白镴…重击他们的方式取得了对主统治者的毁灭性胜利时,背景中的眉头真的把他们都皱在了一起。总的来说,眉毛的交换似乎在塑造角色之间的社会动态方面发挥了重要作用,也强调了角色转变的关键时刻,因为他们考虑到了他们不断变化的世界。
而这就是最终帝国的最终眉!当一部系列小说把人物的发展轨迹贯穿全书的时候总是很棒的,因此检查这本书和系列其他部分之间人物眉毛行为的变化可能会揭示一些有趣的模式。特别是当主角凯尔西耶在接下来的系列赛中退出时,谁会挑起眉毛呢?**
请留意后续分析,深入探究升天之井和时代英雄、出生错误系列第 2 和第 3 册中的眉毛动作。只是为了好玩:
- 三本书的剧情互动版本都在这里整理。
- 数据和代码*可在https://github.com/erinhwilson/mistborn获得,主要在本 Jupyter 笔记本和本 Observable 笔记本中。*
非常感谢马特·约翰逊、Claire Johnson 和 Kylie Fournier 在数据收集/可视化和早期反馈方面的帮助!
用 AIF360 减轻人工智能中的偏差
预测贷款批准并检查这样做的偏差

凯文·Ku 在 Unsplash上的照片
随着人工智能变得越来越普遍,我们这些设计这种系统的人必须认识到这种系统加剧偏见的潜在可能性。在这篇文章中,我展示了数据中的偏差是如何通过机器学习加剧的。我首先创建了一个有偏见的 ML 模型,然后使用 IBM Research 的开源工具包 AI Fairness 360 (AIF360),试图减轻这种偏见。
本练习的数据集和 Jupyter 笔记本在 GitHub repo 这里 有。
数据集
在选择数据集时,我认为重要的是选择一个明确涉及法律保护群体/类别的数据集,其结果变量是二元的,具有明确定义的有利结果和不利结果。
我从 Kaggle 中选择了一个相对较小的数据集,其中包含贷款申请人的信息,以及贷款是被批准还是被拒绝(由我们的结果变量Loan_Status表示)。
让我们来看看数据集的功能:
- 贷款 ID
- 性别(男或女)
- 已婚(是或否)
- 家属 (0、1、2 或 3+)
- 学历(表明主申请人是否高中毕业)
- 个体经营者(是或否)
- 申请人收入
- 共同申请人收入
- 贷款金额
- 贷款申请人期限
- 信用记录 (0 或 1,0 表示信用记录良好)
- 物业区域(农村、半城市或城市)
- 贷款状态(是或否)
在这个数据集中,有三个变量与受保护的类直接相关,应该检查它们的偏差:Gender、Married和Dependents
在这个练习中,为了简洁起见,我只检查了与Gender相关的偏差,尽管也应该检查其他受保护类的偏差。
入门指南
首先,我用 Pandas 导入数据集,并检查缺失值。
import numpy as np
import pandas as pd
df = pd.read_csv('credit_risk.csv')
df.info()

看一看原始数据集。
不幸的是,数据集中有丢失的值(见上文),所以我简单地删除了空值行:
df = df.dropna(how='any', axis = 0)
数据探索
接下来,我仔细查看了输出变量Loan_Status。特别是,我希望看到其值之间的频率分解:

输出变量的二进制类的频率。
显然,各阶级之间存在着明显的不平衡。我们的模型很容易偏向多数阶级(事实上,如果模型只是简单地总是预测贷款批准,它已经有大约 72%的数据准确性)。减轻这种情况的一种可能方法是使用 SMOTE 算法(合成少数过采样技术)用合成样本对少数进行上采样,但我选择执行特征缩放,并确保用于构建模型的技术/算法“意识到”类中的不平衡。
还有很多本可以并且应该完成的数据探索,但是我想保持这篇文章简短,所以我没有深入研究数据探索。
清理数据
首先,我丢弃了那些不会给我们的模型增加任何价值的特征(Loan_ID),并通过一次热编码将分类特征编码成二进制表示。
我手动编码了Gender和Loan Status列,因为我想分别将它们保存为一列,并使用pd.get_dummies函数将其他分类变量转换为指示变量:
# Drop Loan_ID
df = df.drop(['Loan_ID'], axis = 1)# Encode Male as 1, Female as 0
df.loc[df.Gender == 'Male', 'Gender'] = 1
df.loc[df.Gender == 'Female', 'Gender'] = 0# Encode Y Loan_Status as 1, N Loan_Status as 0
df.loc[df.Loan_Status == 'Y', 'Loan_Status'] = 1
df.loc[df.Loan_Status == 'N', 'Loan_Status'] = 0# Replace the categorical values with the numeric equivalents that we have above
categoricalFeatures = ['Property_Area', 'Married', 'Dependents', 'Education', 'Self_Employed']# Iterate through the list of categorical features and one hot encode them.
for feature in categoricalFeatures:
onehot = pd.get_dummies(df[feature], prefix=feature)
df = df.drop(feature, axis=1)
df = df.join(onehot)
分割数据集
然后,我执行了特征缩放,并将数据集分成训练集和测试集:
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
data_std = scaler.fit_transform(x)
# We will follow an 80-20 split pattern for our training and test data, respectively
x_train,x_test,y_train,y_test = train_test_split(x, y, test_size=0.2, random_state = 0)
在解释我如何训练模型之前,我想解释一下我如何量化和测量数据集中的偏差。
量化偏差
我们如何量化偏见?有许多不同的方法,但我使用了一个称为“不同影响比率”的指标:

用简单的英语来说,完全不同的影响比率是无特权群体(在我们的例子中是女性)的积极结果比率(Loan_Status =1)除以特权群体(男性)的积极结果比率。AIF360 工具建议可接受的下限是. 8。也就是说,如果无特权组收到正面结果的时间少于特权组的 80%,则这是一个不同的影响违规。
评估测试分割中的偏差
我想通过计算不同的影响比率来检查我们的模型所测试的实际数据中存在的偏差:

计算不同的影响
我得出了一个 0.83 的不同收入比率。这表明实际的测试分割有利于特权群体(男性),因为不同的收入比率 1 表明完全平等。
训练模型
我使用了逻辑回归,一种更简单的分类算法,因为它在预测的结果变量是二元的情况下是有效的。在这个数据集中,Loan_Status确实是二进制的,Loan_Status的值为0,表示贷款被拒绝,1的值表示贷款被批准。
我使用了 scikit-learn 的LogisticRegression模块,我指定了要平衡的类的权重。这很重要,因为我想小心地消除在数据探索阶段发现的类不平衡所产生的偏见。
from sklearn.linear_model import LogisticRegression
# Liblinear is a solver that is very fast for small datasets, like ours
model = LogisticRegression(solver='liblinear', class_weight='balanced')# Fit the model to the training data
model.fit(x_train, y_train)
评估模型
评估绩效
接下来,我试图评估模型的性能。我使用 scikit-learn 的度量模块来计算分类性能指标:

对于这样一个简单的基线模型来说还不错!显然,在现实世界中,我想尝试多种其他技术,但是这个练习的重点不是建模,我想保持这篇文章的简短。
评估预测结果的偏差
为了量化偏差,我计算了完全不同的影响比率,就像之前一样,除了没有使用测试数据中的实际结果,我计算了由我刚刚训练的模型产生的预测结果的完全不同的收入比率。

如上所示,我得出了一个 0.66 的不同影响比。
这种完全不同的影响比比实际测试结果更糟糕,实际测试结果为 0.83,比我们刚刚训练的模型的 0.66 偏差更小。这并不令人惊讶,因为已经一次又一次地表明,偏见很容易在机器学习模型中被放大。
利用人工智能公平性减轻偏差 360
工具包
为了减轻偏见,我利用了 IBM Research 在 2018 年推出的开源工具包/Python 度量和算法包。偏差减轻算法通常可以分为三类:预处理(在训练之前影响数据)、处理中(影响分类器本身)和后处理(影响输出的预测标签)。我选择应用 AIF360 包提供的预处理算法DisparateImpactRemover,它编辑特征值以增加组的公平性。要了解更多,您可以访问文档(和/或源代码),并且这篇中间文章也提供了很好的解释。当然,在现实世界中,我希望尝试多种偏差缓解技术,而不是只有一种。
应用预处理
这就是棘手的地方。客观地说,AIF360 文档并不是最好的,但是在继承链上,我看到 AIF360 要求用户在应用不同的影响消除算法之前,将 Pandas 数据帧转换成他们称之为BinaryLabelDataSet的数据类型(参见此处的文档):
import aif360
from aif360.algorithms.preprocessing import DisparateImpactRemover
binaryLabelDataset = aif360.datasets.BinaryLabelDataset(
favorable_label=1,
unfavorable_label=0,
df=encoded_df,
label_names=['Loan_Status'],
protected_attribute_names=['Gender'])
然后我创建了一个DisparateImpactRemover对象,用于在数据集的未受保护的特征上运行修复程序。运行修复程序后,我将运行DisparateImpactRemover产生的BinaryLabelDataset转换回熊猫数据帧,我称之为transformed。
di = DisparateImpactRemover(repair_level = 1.0)
dataset_transf_train = di.fit_transform(binaryLabelDataset)
transformed = dataset_transf_train.convert_to_dataframe()[0]
训练模型(预处理后)
然后,我重复了前面使用的相同步骤,只是对数据集进行了预处理。将转换后的数据集分成单独的 x 和 y 数据帧后,我执行了特征缩放,并将数据集分成训练集和测试集:
x_trans = transformed.drop(['Loan_Status'], axis = 1)
y = transformed['Loan_Status']
scaler = StandardScaler()
data_std = scaler.fit_transform(x_trans)
x_trans_train,x_trans_test,y_trans_train,y_trans_test = train_test_split(x_trans, y, test_size=0.2, random_state = 0)
接下来,我用转换后的训练集训练模型:
model.fit(x_trans_train, y_trans_train)
评估模型(预处理后)
评估绩效
我很想知道使用不同的影响消除算法进行偏差缓解预处理是否会降低性能,因此我运行了该模型的分类性能测量:

查看这些性能指标,性能没有下降!但是偏见真的有什么变化吗?
评估预测结果的偏差
我计算了不同的影响比率,但这次是根据模型产生的预测结果,该模型是在我们运行不同影响消除算法的转换的数据集上训练的:

如上图所示,我得出了 0.71的悬殊收入比。
这种完全不同的影响比仍然比实际的测试分割差(. 83)。然而,这种完全不同的影响比比基于原始的、未修改的数据训练的模型产生的完全不同的影响比要好——没有任何分类器指标表明性能有所牺牲!
结束语
这个练习的目的是开始探索偏倚——看看偏倚在 ML 模型中是如何被放大的,以及减少偏倚的潜在方法。在训练模型之前,我已经观察到原始数据集的测试值中的偏差(完全不同的收入比率为. 83)。当我训练一个模型并评估其偏差的预测值时,偏差被显著放大了(完全不同的收入比率为. 66)。然后,我应用了一种称为异类影响消除的预处理技术,并使用转换后的数据训练了一个模型。这导致预测值偏差更小(完全不同的收入比率为 0.71)。这离理想还差得很远,但这是一个开始!
我绝不是这方面的专家。然而,我想强调的是,如果一个人不小心,创建一个编码和放大偏差的模型是多么容易(尽管,通过有限的数据探索,一个基本的模型和一个小数据集)。最后,我还想探索用开源工具减轻偏见的方法。
米托:你见过的最酷的 Python 库之一
大蟒
下面是我对这个很酷的 Python 库的看法,以及为什么你应该尝试一下

几周前,我发布了一个关于 Bamboolib 的博客,这个博客变得非常受欢迎。该博客很受欢迎,第一周就有数万人浏览。在那之后,我计划写其他与数据科学相关的主题,并打算在一段时间内避免写 Python 库,除非我发现了令人惊叹的东西。我找到了,它叫米托。
我前阵子听说过米托,但从来没有机会去测试它,但最近我决定尝试一下,它令人印象深刻!但在此之前,有一点需要注意:这是而不是赞助的内容。我这么说是因为在过去的几周里,一些开发商联系我,问我是否愿意进行有偿合作,所以如果你是其中之一,我不会进行任何形式的有偿合作。然而,我接受关于写作主题的建议。现在,让我们回到米托。
米托—只需点击几下鼠标,节省数小时的工作时间
米托是一个 Python 库,可帮助您执行数据准备、数据清理、转换、探索性数据分析、创建图表等。通过 GUI,只需一两行代码和几次点击就可以完成很多工作。他们试图创造一种类似的体验,就好像你在使用 Microsoft Excel 一样。米托和 Bamboolib(另一个 Python 库)有很多相似之处,比如有一个 GUI 让人们更容易执行任务,他们都创建了 Python 代码,你可以在任何地方复制和使用,即使你没有安装米托。
如果你没有看过我在 Bamboolib 上的博客,你可以在这里找到它:
话虽如此,让我们把我们的手放在米托!
装置
安装过程很简单。出于安全考虑,我建议您为米托创建一个环境。他们的网站展示了如何创建 Python 环境。为此,您可以将两个代码中的一个复制并粘贴到您的终端中:
Mac:
python3 -m venv mitoenv
source mitoenv/bin/activate
python -m pip install mitoinstaller
python -m mitoinstaller install
窗口:
python3 -m venv mitoenv
mitoenv\Scripts\activate.bat
python -m pip install mitoinstaller
python -m mitoinstaller install
如果您决定使用 Conda 虚拟环境,您可以复制并粘贴以下代码:
conda create -n mitoenv python=3.8
conda activate mitoenv
python -m pip install mitoinstaller
python -m mitoinstaller install
完成这些步骤后,您应该可以开始工作了。请注意,米托将用起始代码为您创建一个 JupyterLab 笔记本文件。您也可以通过在终端中键入jupyter lab来启动 JupyterLab 笔记本。现在让我们把手弄脏。
第一步
发起米托再简单不过了。您只需要通过键入import mitosheet来导入它,并通过键入mitosheet.sheet()来启动它。这就是我们今天将使用的所有编码!

作者 GIF
现在,让我们导入将要使用的数据集。点击import,找到文件,我们就可以开始了。在这次演示中,我将使用 Kaggle 的1995–2021 顶级视频游戏元评论数据集。导入数据集后,米托向我们展示了几个你可以用它做什么的购物车。

作者 GIF
数据准备
更改数据类型
在米托更换datatype简直易如反掌。您需要做的就是单击列名正下方的数据类型,然后选择新的数据类型。在下面的例子中,我将把一个字符串改为一个datetime datatype。您会看到图标将从 Abc (这意味着datatype是一个字符串)变为一个日历图标。太简单了!

他们没有选项让你选择datetime datatype的格式,所以如果任何米托开发者准备好这个博客,我认为如果我们可以选择格式,这将是一个很好的额外功能。
另外,user_review列似乎是一个字符串。让我们通过将它改为 float 来解决这个问题。

作者 GIF
你看到米托图形用户界面下方的单元格中添加了一行代码吗?这是因为米托还会给你在另一台笔记本上使用它的代码,即使你没有安装米托。如果你正在学习编码,但不知道如何做某事,这是一个极好的工具。另一个很酷的事情是,它自动在单元格中添加评论,这样任何人都可以阅读。
简单比较一下,Bamboolib 可以自动识别出user_review列应该是浮点数而不是整数,但是米托不能,所以如果你试图把一个有数字的列改成整数,它会抛出一个错误。不过,这不是什么大问题。

作者 GIF
重命名列
更改列名再简单不过了。就像在 Excel 中一样,您只需单击列名并进行编辑。太棒了,对吧?前阵子做了一个项目,要改 200 个栏目的名字,花了我不少时间。我无法想象如果我认识米托,我会节省多少时间。

作者 GIF
删除列
删除列是米托做的另一件简单得可笑的事情。只需选择该列,然后点击DEL COL。就是这样!
假设有一个项目,您需要删除多个列。你不需要输入每个人的名字,只需点击几下就可以解决。

作者 GIF
撤消操作
米托让删除列变得如此简单,以至于有人可以删除比他们想要的更多的列。如果你用 Jupyter 笔记本没有米托,你会怎么做?再次运行所有代码?谁有时间做那个?只需点击UNDO,就可以撤销任何操作。是不是用UNDO有点多了?没问题!只要点击REDO,你就可以取消撤销。抱歉,我不得不说。

作者 GIF
使用多个数据帧
另一个非常酷的特性是你可以同时处理多个数据帧。有两种方法可以做到。您可以点击IMPORT并从您的计算机上传文件,或者,如果您的笔记本中有数据帧,您可以在括号中添加它们的名称,就像下面的代码一样。
mitosheet.sheet(df1, df2)

作者 GIF
对于上面的例子,我创建了两个数据帧。一个在user_review > 9,一个在user_review < 9。
数据转换
过滤数据
您可以通过单击漏斗图标并选择要过滤的内容来过滤数据。在下面的例子中,我只过滤了那些user_review大于 9.5 的游戏。您还可以创建过滤器组,从中可以选择多个条件。

作者 GIF
数据透视表
.groupby()对于数据分析非常有用,即使您是专业用户,您也需要承认在 Python 中对数据进行分组可能有点耗时。如果你创建一个或两个.groupby(),你可能还可以,但是每天做多次会变得很烦人。现在,米托没有选择。取而代之的是.pivot_table() ,其工作原理类似。但是它们之间有什么区别呢?
简而言之,.pivot_table()允许您将数据聚合成更多的形状。例如,您可以选择索引、列和行值。.groupby()将创建表格,其中给定的维度放入列中,并且将为这些维度的每个组合创建行。
现在我们已经了解了它们之间的区别,您可以创建一个数据透视表,方法是单击 pivot,选择您想要创建分组依据的列、您想要查看的统计数据,瞧!
在下面的例子中,我按平台分组,然后得到meta_score 平均值、名字计数和user_review平均值。

作者 GIF
这是米托为我们刚刚做的.pivot_table()创建的代码。这是相当多的代码,我们只需点击几下鼠标就能完成。
**# Pivoted all_games_csv into df2**
unused_columns = all_games_csv.columns.difference(set(['platform']).union(set([])).union(set({'name', 'user_review', 'meta_score'})))
tmp_df = all_games_csv.drop(unused_columns, axis=1)
pivot_table = tmp_df.pivot_table(
index=['platform'],
values=['meta_score', 'name', 'user_review'],
aggfunc={'meta_score': ['mean'], 'name': ['count'], 'user_review': ['mean']}
)
这是一个很好的功能,但对我来说真正酷的是,你可以在一个新的标签中获得分组。您可以通过选项卡来回导航,轻松比较结果。

作者 GIF
创建条件列
使用 IF,只需很少的步骤就可以基于条件创建列。在下面的例子中,我将创建一个新列,说明 user_review 列是否大于 9。为此,我单击 user_review 列中的任何值,单击 ADD COL,然后键入=IF(user_review,’Yes’, ‘No’)。它的工作方式是=IF(**column**, **Value_IF_True**, **Value_IF_False**),这与你在 Excel 中所做的非常相似。

现在,让我们来看看米托生成的代码:
all_games_csv.rename(columns={"new-column-1inv": "above_9"}, inplace=True)all_games_csv['above_9'] = IF(all_games_csv['user_review'] > 9, 'Yes','No')
我必须承认,我不知道我们可以像代码片段中那样使用IF。我通常会使用一个.lambda()函数。很高兴知道!
数据探索
米托让数据探索变得直观。只需点击列,点击过滤器图标,然后点击Summary。您将看到数据分布图等信息,以及平均值、标准偏差、空值数量等描述性统计数据。

作者 GIF
如果你想知道,它适用于数字和分类。

数据可视化
如果您需要创建一些基本的图表,您也可以这样做。例如,您可以通过几次点击来创建一个条形图,并且如果您想要编辑它,您可以生成用于生成图形的代码。只需点击GRAPH,然后选择图表类型以及 x 轴和 y 轴。

作者 GIF
最后的想法
米托是一个为你学习 Python 或者想快速完成一些任务的人准备的库。我真的很喜欢米托和 Bamboolib,因为它们可以让更多的人开始使用 Python。对于初学者来说,Python 可能有点棘手和令人沮丧,我相信米托可以帮助人们学习和发展他们的技能。
即使你已经使用 Python 很多年了,你也可以通过观察米托生成的代码来了解一二。现在,它对每个人都适用吗?这是一个棘手的问题,这将取决于你需要什么,但我认为即使是已经编码多年的人也可以利用其中的一些功能,而不是键入 15 行代码,也许你只需点击几下就可以实现同样的功能。尽管如此,尝试一下,让我知道你的想法。编码快乐!
你可能还喜欢:
</5-python-libraries-that-you-dont-know-but-you-should-fd6f810773a7> [## 5 个你不知道但应该知道的 Python 库
towardsdatascience.com](/5-python-libraries-that-you-dont-know-but-you-should-fd6f810773a7) 💔-awesome-python-libraries-that-you-should-know-about-e2485e6e1cbe>
米托:无需编码即可加速数据集操作
数据预处理
概述米托,一个用于交互式数据集操作的 Python 库,不需要任何编程技能。

作者图片
我一直在寻找新的工具来提高我的数据科学家活动。这次我遇到了米托,一个作为 Python 库提供的电子表格,它允许你以简单快速的方式操作数据集,最重要的是以交互的方式。
米托在 Jupyter 实验室环境中提供了一个图形界面,所以你可以操作任何数据集。
在实践中,米托结合了电子表格的经典功能(比如用户友好的可用性)和 Python 的所有潜力。事实上,界面内进行的所有操作都被自动翻译成 Python 代码,也可以在其他地方使用。
米托提供以下功能:
- 导入/导出数据集
- 向数据集中添加/删除列
- 构建数据透视表
- 合并两个数据集
- 绘图图表
- 筛选/排序列
- 列统计
在这篇文章中,我给出了米托的概述,以及一个实际使用的例子。
1 米托概述
米托是一个交互式电子表格,以 Python 库的形式提供,在 Jupiter Lab 中运行,必须提前安装。
可以通过以下命令轻松安装米托:
python3 -m pip install mitoinstaller
python3 -m mitoinstaller install
米托官方文档附带了对常见安装问题的详细描述,如果安装过程失败,可以帮到你。在我的情况下,安装是好的。
安装后,您必须重新启动 Jupyter Lab(如果它正在运行),以便启用米托。您可以通过以下命令从命令行运行 Jupyter Lab:
jupyter lab
为了运行米托交互界面,您可以创建一个新的笔记本并在单元格中编写以下代码:
import mitosheetmitosheet.sheet()
每次通过米托接口修改数据集时,米托都会在下面的单元格中生成等效的 Python 代码。
数据集存储为 Pandas 数据帧,也可以在单元格中直接操作。
第一次运行米托时,会打开一个新的弹出窗口,要求您提供一些信息,例如您的电子邮件和其他类似的内容:

作者图片
注册后,米托互动界面在笔记本电脑环境中启动。您可以通过点击右上角的按钮启用全屏模式。
1.1 菜单栏
该界面提供了以下菜单栏:

作者图片
从左侧开始,以下菜单项可用:
- 撤销— 清除数据集上的最后一次更改;
- 导入— 从文件系统加载新的数据集。将打开一个弹出窗口,允许您浏览文件系统的目录。支持的格式包括 Excel (XLSX)和 CSV

作者图片
- 导出 —将处理过的数据集以 CSV 格式下载到您的本地文件系统;
- 添加列 —向数据集中添加一个新列。您可以更改列名以及列值。在最后一种情况下,您可以手动输入值,也可以从其他列中计算值。双击该列的第一行即可插入公式;
- 删除列 —完全擦除一列;
- pivot —构建一个数据透视表。生成的表在新的选项卡中打开,因此可以单独操作;
- 合并 —合并两个数据集。您可以在以下合并类型中进行选择:

作者图片

作者图片
- 图表 —为探索性数据分析(EDA)绘制图表。您可以选择与 X 轴和 Y 轴相关联的列,然后选择一个受支持的图形(有关受支持的图形类型,请参见左图)。所有的图形都是在 Plotly 中生成的,这是最著名的数据可视化 Python 库之一。米托保存由 Plotly 为每个图形生成的 HTML 文件。您甚至可以复制生成图形的代码;
- 保存 —保存当前的有丝分裂表;
- 重放 —对新数据重新运行保存的操作。
1.2 色谱柱上的操作
如果单击列名,将会打开一个新的弹出窗口,如下所示:

作者图片
您可以选择列类型和顺序值(升序或降序)。您甚至可以添加一个或多个过滤器或一组过滤器,例如只选择符合某些特定标准的值。您也可以在一列中搜索特定值。
最后,在 Summary Stats 选项卡中,显示了您的列的分布,以及更具体的摘要信息,包括计数、唯一性、顶部和频率。
2 用法示例
作为用例,我利用了 Kaggle 上的 diamond.csv 数据集。首先,我创建了一个新的笔记本,在第一个单元格中,我编写了以下代码:
import mitosheetmitosheet.sheet()
2.1 下降柱
我选择第一列,名为Unnamed: 0,然后点击删除列按钮,如下图所示:

作者图片
因此,该列被删除,并且在下面的单元格中生成相应的 Python 代码:
# Imported /Users/angelica/mitosheet_test/source/diamonds.csv
import pandas as pd
diamonds_csv = pd.read_csv(r'/Users/angelica/mitosheet_test/source/diamonds.csv')# Deleted column(s) Unnamed: 0 from diamonds_csv
diamonds_csv.drop(['Unnamed: 0'], axis=1, inplace=True)
2.2 数据透视表
现在,我构建了一个数据透视表,它显示了每种颜色和切割类型的平均价格。我单击“Pivot”按钮,并在弹出窗口中选择以下选项:

作者图片
我也重命名列名,只需双击列名。通过一次操作,米托生成了下表:

作者图片
米托自动生成相应的代码:
# Pivoted diamonds_csv into df2
unused_columns = diamonds_csv.columns.difference(set(['color']).union(set(['cut'])).union(set({'price'})))
tmp_df = diamonds_csv.drop(unused_columns, axis=1)
pivot_table = tmp_df.pivot_table(
index=['color'],
columns=['cut'],
values=['price'],
aggfunc={'price': ['mean']}
)# Flatten the column headers
pivot_table.columns = [flatten_column_header(col) for col in pivot_table.columns.values]# Reset the column name and the indexes
df2 = pivot_table.reset_index()# Renamed price mean Fair to Fair in df2
df2.rename(columns={"price mean Fair": "Fair"}, inplace=True)# Renamed price mean Good to Good in df2
df2.rename(columns={"price mean Good": "Good"}, inplace=True)# Renamed price mean Ideal to Ideal in df2
df2.rename(columns={"price mean Ideal": "Ideal"}, inplace=True)# Renamed price mean Premium to Premium in df2
df2.rename(columns={"price mean Premium": "Premium"}, inplace=True)# Renamed price mean Very Good to Very Good in df2
df2.rename(columns={"price mean Very Good": "Very Good"}, inplace=True)
2.3 图表
给定前面定义的数据透视表,我构建一个条形图,显示每种颜色和切割类型的平均价格。米托允许在每个图表中最多添加三个系列。因此,在我的图表中,我包括了理想、优质和非常好。

作者图片
米托允许将生成的图形下载为 png 文件或复制图形代码:
# Import plotly and create a figure
import plotly.graph_objects as go
fig = go.Figure()# Add the bar chart traces to the graph
for column_header in ['Ideal', 'Premium', 'Very Good']:
fig.add_trace(
go.Bar(
x=df2['color'],
y=df2[column_header],
name=column_header
)
)# Update the title and stacking mode of the graph
# See Plotly documentation for customizations: [https://plotly.com/python/reference/bar/](https://plotly.com/python/reference/bar/)
fig.update_layout(
xaxis_title='color',
yaxis_title='',
title='color, Ideal, Premium, Very Good bar chart',
barmode='group',
)
fig.show(renderer="iframe")
由于图形是在 Plotly 中生成的,我可以按照 Plotly 的指导方针修改它。
2.4 合并
现在我加载第二个数据集,它包含每种切割类型的所有者:

作者图片
想法是将原始数据集与先前加载的第二个数据集合并。我点击合并按钮,配置如何合并两个数据集,如下图所示:

作者图片
结果,创建了一个新的数据集,它包括两个起始数据集的所有列:

作者图片
和前面的例子一样,米托为合并操作生成 Python 代码:
# Imported /Users/angelica/CNR/Git/mitosheet_test/source/cut_owners.csv
import pandas as pd
cut_owners_csv = pd.read_csv(r'/Users/angelica/CNR/Git/mitosheet_test/source/cut_owners.csv')# Imported /Users/angelica/CNR/Git/mitosheet_test/source/cut_owners.csv
import pandas as pd
cut_owners_csv_1 = pd.read_csv(r'/Users/angelica/CNR/Git/mitosheet_test/source/cut_owners.csv')# Merged diamonds_csv and cut_owners_csv
temp_df = cut_owners_csv.drop_duplicates(subset='cut') # Remove duplicates so lookup merge only returns first match
df5 = diamonds_csv.merge(temp_df, left_on=['cut'], right_on=['cut'], how='left', suffixes=['_diamonds_csv', '_cut_owners_csv'])
2.5 列统计
最后,我在单个列上执行一些操作。我可以通过单击列名旁边的漏斗图标来打开相关的弹出窗口。我考虑的是克拉列,是数字,我按升序对其排序,只需点击筛选/排序选项卡中的升序按钮。然后我创建一个新的过滤器,它只选择值大于 0.3 的行。

作者图片
同样,米托自动生成 Python 代码:
# Sorted carat in df5 in ascending order
df5 = df5.sort_values(by='carat', ascending=True, na_position='first')# Filtered carat in df5
df5 = df5[df5['carat'] > 0.3]
现在,我选择“Summary Stats”选项卡,米托在克拉列显示了以下统计数据:

摘要
在这篇文章中,我描述了米托,这是一个允许快速操作数据集的电子表格。米托可以很容易地在朱庇特实验室运行,因此你只需尝试一下:)
本文中使用的完整代码可以从我的 Github 资源库下载。
如果你读到这里,对我来说,今天已经很多了。谢谢!你可以在这篇文章中读到更多关于我的信息。
你愿意支持我的研究吗?
你可以每月订阅几美元,解锁无限的文章。
相关文章
https://alod83.medium.com/how-to-import-and-export-a-dataset-in-phpmyadmin-b108288a3ca3
免责声明 :这不是赞助文章。我与米托或其作者没有任何关系。这篇文章展示了该框架的一个公正的概述,旨在使数据科学工具能够为更多的人所使用。
两种风格图像的混合神经风格转换
实践教程
一种扩展神经类型转移的方法

图 1:我基于一个内容图像和两个风格图像实现混合神经风格转移(MNST)的结果。(C 内容图片由 Alexas_Fotos ,s 风格图片 1 由 Andreas Fickl ,s 风格图片 2 由 Europeana
简介
作为一个既是技术又是艺术爱好者的人,神经风格转移(NST)对我来说非常迷人:一种通过使用内容和风格模板来创建绘画的算法。在实现和试验最初的 NST 算法时,我有了在一幅图像中结合两种风格的想法。因此,我的故事的主题是关于原始神经风格转移的变体,这种方法在 Gatys 等人的论文“艺术风格的神经算法”中有所描述。混合神经风格转移(MNST)描述了原始算法的扩展,使用两个风格图像和一个内容图像。
在这个故事的过程中,我将解释如何将两幅图像的风格应用于一幅摄影作品,分析改进过程,并展示如何通过根据各自的损失对给定的风格进行加权来扩展 NST 优化过程。
由于有一些关于神经类型转移的非常好的教程和解释,我想把介绍留给他们,而是继续混合神经类型转移。(查看 Vamshik Shetty 的神经类型转移教程,它对 gram 矩阵做了很好的介绍。这个 TensorFlow 教程也可能是一个很好的实现起点。)
从神经类型转移到混合神经类型转移
作为 NST 核心要素的损失函数包括两个方面:内容损失和风格损失。虽然内容损失保证了新生成的图像不会偏离内容图像的主题太远,但是风格损失度量了风格图像的风格差异。当实现 NST 的原始方法时,我想知道如果我将 NST 的损失函数扩展第二个样式损失,以生成两个样式图像和一个内容图像之间的混合,会发生什么。

图 2: α和β对内容和风格损失进行加权。γ分别对风格损失 1 和风格损失 2 进行加权,而γ应该取 0 到 1 之间的值。c 表示内容图像,s 表示 NST 的样式图像,s1 和 s2 表示 MNST 的样式图像。图片作者。
在 NST 的原始损失函数中,α和β分别是内容损失和风格损失的权重。对于 MNST,α和β保持不变,但γ作为第二种类型的权重被引入。它用于定义个人风格影响整体风格损失的程度。例如,如果γ ist 设置为 0.7,则风格图像 1 的损失对整体风格损失的影响大于风格图像 2 的损失,其权重为 0.3。通过在 0 和 1 之间移动γ值,可以在两种风格图像的 NST 结果之间进行插值。所以γ基本上应该像样式的交叉渐变器一样工作。
对于以下示例,这些图像被选择为内容图像、样式图像 1 和样式图像 2:

图 3:图片加载自unsplash.com:风格图片 1 作者安德烈亚斯·菲克尔,s 风格图片 2 作者欧洲,c 内容图片作者 Alexas_Fotos
设置
- 模型:用 ImageNet 权重预训练的 VGG19 模型
- 内容输出层 : block5_conv2
- 样式输出图层: block1_conv1,block2_conv1,block3_conv1,block4_conv1,block5_conv1
- 层权重: 此外,我还为风格损失配置了层输出的权重,因此较高的卷积层比较低层获得相对较低的权重。这种加权背后的直觉是给予过滤器较低的影响,强调复杂的结构,而不是集中于简单的结构,定义图像的风格。[block1_conv1: 1.0,block2_conv1: 1.0,block3_conv1: 0.2,
block4_conv1: 0.3,block5_conv1: 0.01] - Gram 矩阵:Gram 矩阵的计算略有变化,因为它们的值没有除以输出尺寸。
- α =0.002, β =0.02
- 优化器: Adam 优化器,initial_learning_rate=12.0,decay_steps=100,decay_rate=0.6。
- 损失 : tf.reduce_mean 用于计算样式和内容损失
- 开始条件:优化从内容图像的副本开始。

图 4:基于图 3 所示的内容和风格图像,不同γ值的 MNST 结果。(C 内容图片由 Alexas_Fotos ,s 风格图片 1 由 Andreas Fickl ,s 风格图片 2 由 Europeana
结果评估
当比较不同γ值的风格混合时,可以清楚地看到风格图像 1 的风格如何日益影响整体结果。虽然对于接近 0 的γ值,样式图像 2 的褪色样式在生成的图像中占优势,但是随着γ向 1 移动,样式图像 1 中的涂鸦文本的曲线看起来更清楚。
尽管混合风格效果很好,但γ ≥ 0.5 的结果之间的差异初看起来并不明显。虽然较低的γ值明显影响整体结果,但对于大于 0.5 的值,需要良好的眼睛才能看到任何变化。一种解释可能是涂鸦丰富多彩、生动活泼的风格,这使得微妙的变化难以察觉。
观察这种损失会得出不同的解释。对于γ = 0.5 的平衡场景,在优化过程开始时,风格损失 1 是风格损失 2 的 3.6 倍。在优化过程结束时,风格损失 1 的相对改善(开始损失/最终损失)几乎是风格损失 2 的四倍。因为我的样式转换实现开始优化内容图像的副本,所以第一次迭代中的样式损失是内容图像和样式图像之间的样式损失。因此,在优化过程的第一次迭代中,x=c。
从内容图像开始校准 MNST 的损失
对于γ=0.5,样式 1 和样式 2 应该同样强烈地影响最终结果。因此,应该避免一个样式损失被优化而另一个保持不变甚至增加。找到正确的方法需要一些修补。我的直觉是,对于γ=0.5,两种风格损失应该取得相同的相对进展。如果风格损失 1 提高了 50%(与其起始损失相比),风格损失 2 也应该提高 50%。
相对改善的计算方法如下:

图 5: RI 表示优化过程中风格损失 1 和 2 的相对改善。它将第一次迭代的初始损失与最后一次迭代后的损失进行比较。c 代表内容图像,gen 是最后一次迭代后生成的图像。
为了了解不同γ值下 RI1 和 RI2 的变化情况,我让 MNST 运行 100 次迭代,以 0.1 为步长计算从γ=0 到γ=1 的相对改善。使用 RI1 和 RI2 之间的比率来找出与风格损失 2 相比,风格损失 1 改善了多少。

图 6:对不同γ值的 RI1 和 RI2 的观察表明,对于γ~0.34,风格损失 1 和 2 具有相同的相对改善,而对于 0.5 则不是预期的。图片作者。
结果表明,对于γ~0.34,RI1 和 RI2 是相同的,而对于γ≥0.34 的值,风格损失 1 改善得更好。这支持了图 4 中目视检查的先前印象,即对于γ=0.5,两种类型的影响不相等。
为了将注意力更多地转移到“较弱”的风格上,我最初使用γ=0.5 时风格损失的相对改善(RI1/RI2)之间的比率作为风格损失 2 的附加权重,称为σ。但是现在结果超过了 0.5 的值,因为γ~0.64 的风格发展得同样好。在检查了不同σ值的影响后,结果表明σ的变化不会线性影响 RI1/RI2 的偏移,而是通过平方根影响。
因此,总损失函数现在看起来如下:

图 7:除了γ,还引入了σ1 和σ2,以平衡损失的改善。图片作者。
通过使用称为σ1 和σ2 的新权重,对于γ=0.5,两种风格损失的相对改善是相等的(如图 8 所示)。当观察灰色虚线时,还可以看到,相对改善之间的比率在γ=0.5 附近对称地发展。

图 8:左:在没有西格玛权重的情况下,100 次迭代后,不同γ值的风格损失 1 和 2 的开始值与结束值的比率。右图:sigma 权重的情况相同。图片作者。
比较具有和不具有σ1 和σ2 的 MNST 的结果(图 4 与图 9),可以看出,样式图像 1 的样式连续淡入,并且从γ=0.5 开始不支配结果图像。

图 9:基于图 3 所示的内容和风格图像,对于σ1=1 和σ2~1.9 的不同γ值的 MNST 结果。(C 内容图片由 Alexas_Fotos ,s 风格图片 1 由 Andreas Fickl ,s 风格图片 2 由 Europeana
结论
在尝试不同的风格和内容图片时,我意识到,当风格图片的结构和颜色不同时,MNST 会产生好的结果。相比之下,如果选择了两种风格的图像,它们都非常丰富多彩并且纹理相似,则结果通常不是很有趣,因为每种风格对整体结果都有独特的贡献。同样值得思考的是,哪种样式可以强调内容图像的哪一部分,以增加新生成图形的表现力。
最后,我想再给你们看两个 MNST 的例子,它们是在我的实验中创建的。
享受:)

图 10: γ = 0.4,σ1 = 1,σ2 ~ 1.503 || 风格图 1 由安德烈亚斯·菲克尔,s 风格图 2 由丹-克里斯蒂安·pădureț,内容图由 Alexas_Fotos 。所有参考均从[unsplash.com](http://The images are loaded from unsplash.com)加载

图 11: γ = 0.5,σ1 = 1,σ2 ~ 1.150 || s tyle 图片 1 由尼古拉·波伊斯,s tyle 图片 2 由丹-克里斯蒂安·pădureț,c 内容图片由迈赫迪·拉马法。所有参考从[unsplash.com](http://The images are loaded from unsplash.com)加载
混合密度网络:不确定性估计的概率回归
不确定性无处不在。它存在于我们做出的每一个决定,我们采取的每一个行动中。在我们计划未来的商业决策中尤其如此。尽管如此,我们在商业中使用的所有预测模型都忽略了不确定性。

假设你是谷歌 Play 商店的经理,谷歌 Pixel 5a 即将发布。总部向您发送了他们 ML 模型的预测,并表示他们预计在发布的第一周将售出 100 台。但是你知道,从你的经验来看,总部的预测并不总是正确的,并希望通过采购超过 100 件来避免缺货。但是又有多少呢?你怎么知道 ML 模型的预测会有多错误?换句话说,这个 100 台的预测有多大把握?这个模型置信度的额外信息在做出这个决定时是至关重要的,而总部的 ML 模型不能给你这个信息。但是如果除了 100 个单位的预测之外,模型还给了你一个不确定性的度量,比如预期概率分布的标准偏差,会怎么样呢?现在,你可以根据自己的风险偏好做出明智的决定,决定囤积多少。
但是我们怎么做呢?通常,分类问题有一个额外的优势,因为我们在顶部添加了逻辑函数,它让我们对模型的置信度有了一些了解(尽管从技术上讲这不是一回事)。说到回归,我们的传统模型给我们一个点估计。
不确定性的类型
有两种主要的不确定性——认知的不确定性和任意的不确定性。
- 认知不确定性描述了模型不知道的东西。这是由于对模型的了解不够。这种不确定性可以通过增加数据或增加模型复杂性来降低。
- 随机不确定性是固有的不确定性,是数据生成过程的一部分。例如,由高精度设备发射的纸飞机,保持相同的释放程度、释放速度和一千个其他参数,每次试验仍然不会落在相同的地方。这种固有的可变性是任意的不确定性。
一个典型的监督机器学习问题可以写成如下:

这里认知的不确定性来自θ,任意性来自 x 。典型地,高认知不确定性存在于稀疏填充有数据示例的特征空间部分中。在这样的 n 维空间中,可能有许多参数可以解释给定的数据点,这导致了不确定性。
核心思想
这里的关键创新是这样的:
在普通的神经网络回归中,我们将在最后一层有一个神经元,它被训练来预测我们感兴趣的值。如果我们预测一个概率分布的参数会怎么样?例如,高斯分布由其均值(μ)和标准差(σ)来参数化。因此,在最后一层不是只有一个神经元,而是有两个神经元来预测高斯分布的均值和标准差。
厉害!经过适当的训练,我们有了均值和标准差,这意味着我们有了整个概率分布,因此通过扩展成为不确定性的估计。
但是,有一个问题。当我们训练模型来预测分布的参数时,我们在模型上施加了巨大的感应偏差。据我们所知,我们试图建模的目标变量甚至可能不遵循任何参数分布。

双峰分布(来源: Wiki )
让我们看看双峰的例子。看起来有两个高斯分布,挤在一起。如果我们做一个深入的实验,并将这种“两个高斯分布的挤压扩展到“N 个高斯分布的挤压”的情况,这种合成的混合物可以模拟各种各样的概率分布。这正是混合密度网络的编码思想。网络的最后一层由许多高斯成分(平均值和标准偏差)组成。并且具有决定如何混合这些高斯分量的另一个学习参数(潜在表示)。
混合密度网络
混合密度网络由两个组件构建而成——神经网络和混合模型。
神经网络可以是任何有效的架构,它接受输入 X 并转换成一组学习到的特征(我们可以将其视为编码器或主干)。
现在,让我们来看看混合模型。像我们之前讨论的那样,混合模型是一种概率分布模型,它是由多个简单分布的加权和构建而成的。更正式地说,它将概率密度函数( pdf ) p(x) 建模为由 j pⱼ(x) 索引的mpdf 与权重的混合

通过下面的等式:

,其中θⱼ是描述分布形状和位置的分布参数。
在他的论文[1]中,Bishop 使用高斯核,并解释说,只要正确选择混合系数和高斯参数,任何概率密度函数都可以近似为任意精度。通过使用上述等式中的高斯核。它变成了:


混合密度网络:神经网络的输出参数化高斯混合模型。来源【2】
充分条件
Bishop 还提出了一些限制和实现 MDNs 的方法。
- 混合系数(π或α)是概率,必须小于零,并且总和为 1。这可以通过将混合系数的输出通过 Softmax 层来轻松实现。
- 方差(σ)应该严格为正。Bishop[1]建议我们对 sigma 神经元的原始对数使用指数函数。他认为这与假设一个无信息的先验具有相同的效果,并避免了一个或多个方差变为零的病理配置。
- 中心参数(μ)代表位置参数,这应该是平均神经元的原始逻辑。
损失函数
使用标准反向传播对网络进行端到端训练。为此,我们最小化的损失函数是负对数似然,这相当于最大似然估计。
我们已经知道什么是 P(x) ,现在的问题是计算它并最小化负对数似然。
确保稳定性的实施和技巧
现在我们知道了模型背后的理论,让我们看看如何实现它(以及一些技巧和失败模式)。众所周知,实施和培训 MDN 非常困难,因为有很多事情可能会出错。但是通过采用先前的研究和大量的实验,我已经确定了一些技巧,使得训练相对稳定。实现将在 PyTorch 中使用我开发的库— Pytorch Tabular (这是一个高度灵活的框架,用于处理深度学习和表格数据)。
避免数值下溢
如果你还记得高斯混合的 pdf 公式,它包括所有 pdf 的混合系数加权总和。所以负对数可能性是:

我们知道这一点

如果你检查这个方程,我们会看到一个指数函数,然后乘以混合系数,然后取它的对数。该指数和随后的对数可能导致非常小的数字,从而导致数字下溢。因此,我们按照 Axel Brando Guillaumes [3]的建议,使用 LogsumExp 技巧,在对数域中进行计算。

因此负对数似然性变为:

现在,我们使用torch.logsumexp计算批次中所有样本的负对数似然,然后计算平均值。这有助于我们在训练中避免大量的数值不稳定性。
方差参数的激活函数
Bishop[1]建议对方差参数使用指数激活函数。这个选择有它的长处。指数函数趋向于正输出,在较低的一侧,它永远不会真正达到零。但实际上,它存在一些问题。指数函数增长得非常大非常快,并且在具有高方差的数据集的情况下,训练变得不稳定。
Axel Brando Guillaumes [3]提出了一种替代方案,这就是我们在实施中使用的方案。ELU 激活的修改版本。

ELU 激活功能(来源: ML 备忘单
对于更高的值,ELU 函数保留指数行为,并恢复为线性行为。唯一的问题是指数行为是在 x 为负的时候。但是如果我们将这个函数向上移动 1,我们得到一个近似指数行为的函数。所以阿克塞尔·白兰度·纪尧姆[3]建议使用

作为方差参数的激活函数。因为从技术上讲这也可以变成零,所以我们在修改后的 ELU 上增加了一个ε来确保稳定性。所以使用的最终激活是:

多重高斯和模式崩溃
当我们使用多个高斯分量并使用反向传播训练模型时,网络倾向于忽略除一个分量之外的所有分量,并训练单个分量来解释数据。例如,如果我们使用两个分量,当训练时,一个分量趋向于 0,另一个趋向于 1,并且对应于变成 1 的混合分量的均值和方差分量将被训练来解释数据。
Axel Brando Guillaumes [3]建议将π值削减到一个下限。但是这种方法有一个问题。
- 因为应用了 softmax,应用了 softmax 之后就不能裁剪π了;不然加起来也不是一个。在 softmax 之前,您必须对原始 logit 值应用裁剪,这非常不直观。
于是,通过实验,我们找出了另一套让网络运转起来的招数(虽然不能保证,但肯定鼓励网络远离模式崩溃)。
- 权重正则化 —将 L1 或 L2 正则化应用于计算均值、方差和混合分量的神经元权重。
- 偏置初始化 —如果我们预先计算两个高斯的可能中心,我们可以初始化μ层对这些中心的偏置。这已经显示出在训练期间对两个高斯核/分量的分离具有强烈的影响。
混合系数的 Softmax 替代方案
Bishop[1]建议使用 Softmax 层将混合系数(π)的原始对数转换为概率。但是我们使用了一个 Gumbel Softmax ,它提供了一个更加清晰的概率分布。这是可取的,因为我们希望我们的模型能够在不需要的时候有效地剔除一个或多个组件。Softmax 仍然会给这些分配一个小概率,而 Gumbel Softmax 使这个概率更小。

来源[4]
密码
下面是在 PyTorch 表格中实现的示例代码。前往 repo 查看完整的实现(此外,还可以访问 NODE、AutoInt 等其他模型的实现)。
MDN 头的代码
一个抽象类,它包含一个主干网络并使其成为一个 MDN
实验
从 Oliver Borchers 博士的这款出色的 Colab 笔记本中获得灵感,我使用该实现设计了自己的一组实验。还有一个关于如何在 PyTorch 表格中使用 MDN 的教程。
线性函数

作者图片
这是一个简单的线性函数,但会添加一些高斯噪声作为输入 x 的函数,常规前馈网络将能够用一条穿过中间的直线来近似此函数。但是如果我们使用一个单一成分的 MDN,它将逼近函数的平均值和不确定性

作者图片
非线性函数

作者图片
这是一个扭曲的非线性函数。对于 x 的每个值,都有两个 y 值——一个在正侧,另一个在负侧。让我们看看,如果我们在这些数据上安装一个正常的前馈网络,会发生什么。

作者图片
不太好不是吗?好吧,如果你考虑最大似然估计,它很可能会失败,因为正负点的概率是相等的。但是,当您在它上面训练 MDN 时,这两个组件会被正确地学习,如果我们绘制出这两个组件,它将看起来像这样:

作者图片
高斯混合

作者图片
这里我们有两个高斯分量,它们以一个比例混合,所有的分量都用 x 参数化。

作者图片
直方图还显示了顶部的两个小凸起,而不是正常高斯的单个平滑。
让我们看看标准前馈网络如何捕捉这个函数。

作者图片
很好,除了过渡部分。和往常一样,没有关于不确定性的信息。(注:网络如何弯曲自己来逼近阶跃函数,真的太棒了。
现在让我们根据这些数据训练一个 MDN。

作者图片
现在,那不是很好吗?这两个组件被很好地捕获——一个组件很好地捕获了第一部分,另一个组件捕获了第二部分。不确定性也估计得很好。让我们也来看看混合系数。

作者图片
分量 1(上图中的粉色分量)在过渡点之前具有较高的混合系数,之后很快降至零。
摘要
我们已经看到了不确定性对商业决策的重要性,也探索了一种使用混合密度网络的方法。PyTorch Tabular 以一种非常用户友好的方式提供了本文中讨论的所有技巧的实现,以及其他一些针对表格数据的先进算法。请点击这里查看:
如有任何疑问/问题请联系我 LinkedIn 。对于您在库上遇到的任何问题,请在 Github 上提出问题。
参考
- C.M. Bishop,混合密度网络,(1994 年)
- 沃森,朱利安&费龙,巴蒂斯特&蒙蒂,A..(2018).基于人工神经网络的家庭电力负荷概率预测。10.1109/pmaps . 18448 . 186486486616
- Guillaumes,A.B. (2017)。分布和不确定性估计的混合密度网络。巴塞罗那大学
- 张,顾,s .,普尔,B. (2017)。用 Gumbel-Softmax 进行分类重新参数化。 ArXiv,abs/1611.01144 。
原载于 2021 年 3 月 20 日【http://deep-and-shallow.com】。
规模合理的 ML 和 MLOps
在启动规模有效地做 ML;业内笔记
没有太多操作的 MLOps 第 2 集

斯蒂芬妮·勒布朗通过 Unsplash 拍摄的照片
虽然生产中使用的机器学习(ML)应用程序的数量正在增长,但我们没有一天不读到一些关于大多数企业仍然难以获得正投资回报的报道(见此处和此处)。
你可能会注意到一件事:从来没有人谈论大型科技公司如何努力在生产中获得 ML 的好处。那是因为他们不是。谷歌、脸书、网飞和其他顶级玩家都非常擅长操作 ML,他们最不担心的就是糟糕的投资回报率。
对于那些不得不应对大型科技公司所没有的限制的公司来说,这个门槛总是被认为太高。有趣的是,绝大多数公司都是那样的。大多数公司不像谷歌:他们不能雇佣他们梦想的所有人才,他们没有每天数十亿的数据点,他们不能指望几乎无限的计算能力。
在大型科技公司之外的所有最美好的形式中,我们特别感兴趣的是一个不断增长的——我们认为,服务不足的——与 ML 系统特别相关的部分。
我们把这个细分市场称为 ' 合理规模的公司 ' (相对于 FAANG 公司不合理的庞大规模)。

合理规模概念所涵盖的概念范围。作者图片。
在这篇文章中,我们将尝试充实我们所说的合理规模的公司。最重要的是,我们希望研究这些组织每天都必须处理的约束。如果这些限制中的一些(或全部)引起了你的共鸣,那么恭喜你:你可能正在一家规模合理的公司做 ML。
好消息是,对于处于合理规模的 ML 来说,这实际上是一个伟大的时代。历史上第一次,DataOps 和 MLOps 工具包变得足够丰富、可组合和灵活,可以构建可靠、可复制和可伸缩的端到端管道:不要担心,本系列即将发布的文章将向您展示如何实现。
在转向当前 MLOps 格局的绽放嗡嗡的困惑之前,重要的是阐明定义我们问题空间的硬约束,并制定一些设计原则。**
合理规模
合理规模(RS) 的定义是多方面的。它应该足够灵活,能够包含不同行业中的许多用例。在光谱中,我们可以发现数字本地创业公司正在飞速发展,以及大型传统企业在生产中为有限的用例开发 ML 应用程序。与此同时,这个定义意味着为现在大量的开源工具和 MLOps 产品捕捉一定的采用目标。
我们用来定义 RS 公司的维度如下:
****1。货币影响:RS 的 ML 模型每年创造数十万到数千万美元的货币收益(而不是数亿或数十亿美元)。
凭直觉,我们想说的是,RS 的成功模式很少有那种把 BERT 模型应用到 Google 搜索或者改进亚马逊的推荐的影响力。RS 公司的 ML 可以产生相当大的影响,但这种影响的绝对规模很少达到大数据公司的规模。
另一种稍微正式一点的思考方式是,看看 RS 公司投资 ML 的https://www.investopedia.com/terms/n/npv.asp(【NPV】)净现值状态。如前所述,RS 公司有不同的规模,并且在许多方面存在差异,从业务成熟度到运营效率,从财务杠杆到流动性。它们可能是蓬勃发展的公司,收入增长迅速,但不一定盈利,如直接面向消费者的品牌,如 Warby Parker 和 Casper 或数字本地玩家,如 Tubi 或Lyst;或者他们可以是已经实现盈利的老牌企业,比如零售商翠鸟、马克斯和斯潘塞。****
关键的一点是,所有这些组织都应该遵循资本预算的一个关键原则,即它们应该承担那些将增加其盈利能力的项目。
正的 NPV 通常表明应该进行投资,除非其他项目有更高的 NPV。对 ML 的投资也不例外,NPV 与 ML 应用对公司的影响以及公司的规模相关。当公司太小时,ML 投资的 NPV 可能是负的或适中的,这意味着该项目根本不应该进行,或者实际上不可能优先于具有可比 NPV 的竞争项目(例如,因为后者可能具有更短的回收期或通常被认为风险更小)。
RS 的概念旨在描述一系列公司,尽管它们之间存在差异,但面临类似的问题,并且都将从采用类似的原则中受益。如果你在这个范围的某个地方,你的 ML 投资的预计 NPV 可能有一个 100,000,000 美元的上限。
2。团队规模: RS 公司有几十个工程师(而不是几百或几千)。
FAANG 公司在获得和留住人才方面没有真正的困难(例如,网飞每年收到大约 35 万份工作申请)。遥感公司不能指望这样的流量。他们的 ML 团队由数十名数据科学家组成,ML 工程师和 rs 公司需要以一种确保生产力和最大化其产出的方式组织这些团队。这并不容易。这些公司中的 ML 人员面临许多挑战,因为他们的雇主通常是 ML 工具的后期采用者,并且在整个堆栈中往往不够成熟。
为了对较小的团队进行优化,RS 公司通常会努力将运营摩擦降至最低,即想办法让 ML 开发人员和数据科学家尽可能少地依赖其他团队来获取数据、调配 GPU、服务模型等。
这很有意义,因为 ML 系统的开发依赖于所解决问题的类型,所以数据科学家需要能够根据数据集、数据类型、算法和安全约束来选择工具、架构和建模。此外,ML 系统不是针对静态环境部署的,因此数据科学家需要了解数据中的变化、模型中的变化、敌对攻击等等。
与此同时,重要的是不要让数据科学家参与太多的辅助任务,因为这需要他们开发太多的补充技能:如果现在他们的工作是供应 GPU,我们只是转移了负担,而不是提高速度。取得恰当的平衡绝非易事。
3。数据量: RS 公司处理的是 TB(而不是 Pb 或 EB)。
FAANG 公司拥有来自数十亿用户的数据,以不断训练他们的模型。例如,WhatsApp 用户每天交换多达1000 亿条消息,亚马逊仅在美国就有1 . 53 亿会员。谷歌有 8 个(!)产品每个都有超过 10 亿用户。
相反,对于遥感公司来说,由于数据稀缺、隐私保护、法规遵从性或仅仅是规模等问题,收集大量训练集通常是不可行的。⁴
考虑一个相当数据密集型的空间,如电子商务。根据我们自己的数据,我们知道拥有网站的价值数十亿美元的零售商,在 Alexa 排名中排名在 25k 到 4k 之间,每月产生 150 万到 1000 万的独立访客。这令人印象深刻:1000 万人是墨西哥城的人口。但是…如果我们以亚马逊为例,每月的独立访问者数量级为 2 亿。这大约是加拿大人口的六倍。超大城市的规模到此为止!

作者图片
此外,96 %的企业在 ML 项目中遇到数据质量和标签挑战,对于某些用例,由于新冠肺炎疫情等中断,数据量挑战只会变得更加严重。⁵
遥感可用的数据量有限,这一事实会影响到最佳数据量。也许,过度关注顶级建模在战略上是不明智的。例如,就成本/收益比而言,同类最佳的模型可能不是最佳的,或者在许多情况下,如果它们过于渴求数据,甚至可能不是可行的选择。
正如最初支持以数据为中心的人工智能的人所建议的那样,专注于干净、标准化和可访问的数据对遥感公司来说有更多的边际收益。
4。计算资源: RS 公司的计算预算是有限的。
ML 模型的复杂性一直在稳步增长,DL 在计算基础设施方面的要求尤其苛刻。计算方面的支出普遍在上升,成本也是如此。
现在,即使大型科技公司对计算的需求在不断上升(只是一个例子),这些公司拥有几乎无限的计算资源。现实一点:亚马逊和谷歌实际上拥有他们的云提供商。
对于其他人来说,性能提升的成本可能会令人望而却步。例如, Strubell 等人最近根据美元对模型培训和开发成本进行了基准测试,对于机器翻译任务,他们估计使用神经架构搜索进行英语到德语翻译时,BLEU 分数增加 0.1 会导致计算成本增加 150,000 美元。
在 rs 公司中,影响计算效率最大的因素之一是从端到端的角度来看 ML 系统的低效设计(包括数据堆栈)。在 RS,关注点需要在尽可能降低成本和尽可能提高扩展效率之间平分秋色。
吊诡的是,谷歌从 1 GPU 到 1000 GPUs 比大多数 RS 公司从 1 GPU 到 3 GPU 更容易。例如,许多遥感公司使用分布式计算系统,如 Spark,这是不太可能需要的。高效的垂直设计可以实现很多目标,这种设计包含以最少的努力并且仅在需要时扩展计算资源的方式。
下一个帖子无耻吊人胃口
这四个维度定义了我们所说的 RS 公司。你不一定要同时找到与所有这些相关的严格约束,它们也不应该准确地描述你公司的情况。然而,如果您发现自己经常想知道如何从货币角度估计 ROI,或者如何在预算限制的情况下对计算效率建模,或者如何选择正确的策略来停止让您的数据科学家将大部分时间花在数据质量上,那么…您可能在 RS 公司。
在下一篇文章中,我们将详细介绍你可以做些什么,并讨论四个简单的支柱,它们构成了我们作为 as ML 从业者的精神和实践指南:

图片来自 Shutterstock
●数据>建模
● Log ≠变换
● PaaS/FaaS > IaaS
●垂直>分布式
不要担心,彻底地浏览这个框架需要时间,而且会有你可以实际使用的东西,比如大量的开源代码和文档。
笔记
请注意,我们将要介绍的维度是高度相关的,但也有一些例外:例如,在 Adtech 中,你会得到大量的数据,需要大量的计算资源,但可能不需要太多的工程师。
即给定利率(贴现率)的(贴现)未来净现金流(收益和成本之间的差额)的现值。
这一点的一个重要推论是,在 ML 采用周期的开始,组织应该总是从 RS 开始,不管它们的全球规模如何,因为如果没有至少一个实际的例子,很难估计 ROI。我们在本系列中描述的技术栈和实践也非常适合刚起步的大企业。
⁴关于 B2B 的注释:如果从整体上考虑,一些 RS 在处理的数据方面可能相当大,但是实际上数据是由客户存储的。例如,Coveo 就是其中之一,当我们进行产品开发时,我们必须特别强调这一点。
⁵:缓解数据匮乏的挑战有多种选择。例如,数据丰富允许 RS 公司添加第三方数据,以使模型更加准确。合成数据是另一种选择(即不是从直接测量中获得的数据),使用的方法包括从真实数据中进行严格的统计采样、生成对抗网络或创建模拟场景。这一领域的创新主要由早期创业公司引领,如 MostlyAI 或render。AI 而且值得关注。
这篇文章是关于启动阶段 MLOps 的迷你系列的第二篇。在这里可以找到第 1 集。
承认
没有我们的开源贡献者的承诺,这个系列是不可能的:**
- Patrick John Chia :局部流量和基线模型;
- 卢卡·比贡:通用工程和基础优化;
- Andrew Sutcliffe :远程流;
- Leopoldo Garcia Vargas :质量保证和测试。
使用 dstack.ai 的 ML 应用程序
在这篇文章中,我们将看到如何使用 dstack.ai 来创建 ML 应用程序。我已经得到了贷款预测数据集。这个特殊的贷款预测应用程序有助于根据不同的参数预测客户是否应该获得贷款。这是一个基于分类的问题,给出的输出是/否(1/0)。
dstack.ai 是一个 python 框架,无需编写任何前端代码即可创建 ai 和 ML 应用。在我们开始之前,我假设您已经在系统上安装了 dstack server,并且它正在运行。您可以通过以下命令启动服务器。如果您希望更改端口,也可以这样做。
dstack server start --port 8081
让我们现在开始吧。
我们将使用贷款预测数据集,并将其部署在 dstack 上。
导入一些库以及 dstack 和 dstack 控件
import dstack as ds
import plotly.express as px
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import dstack.controls as ctr
函数来获取训练和测试数据。ds.cache()用于快速检索数据,而不是在每次运行程序时加载数据
[@ds](http://twitter.com/ds).cache()
def get_data():
data= pd.read_csv("D:\\ML_projects\\dstack_proj\\train_ctrUa4K.csv")
return data[@ds](http://twitter.com/ds).cache()
def get_testdata():
return pd.read_csv("D:\\ML_projects\\dstack_proj\\test_lAUu6dG.csv")
我们将制作 3 个 ds.apps 即预测图、散点图和条形图,然后将它们放在一个框架上作为标签供用户访问(见下图)。

仪表盘
首先,我们看剧情,然后再看 ML 部分。让我们创建两个函数 scatter_handler 和 bar_handler。
def scatter_handler():
df = get_data()
return px.scatter(df, x="ApplicantIncome", y="LoanAmount", color="Education")def bar_handler():
df = get_data()
return px.bar(df, x="Gender", y="Loan_Amount_Term", color="Education", barmode="group")
这两个函数分别从 plotly.express 库中返回散点图和条形图
现在,我们需要将它们作为选项卡添加到框架中。为此,我们首先创建一个框架。您可以在上面的仪表板图像中看到这个框架。
frame = ds.frame(“Loan_Prediction”)
一旦完成,我们就把这些功能作为应用程序添加到框架中。
frame.add(ds.app(scatter_handler), params={“Scatter Plot”: ds.tab()})frame.add(ds.app(bar_handler), params={“Bar Plot”: ds.tab()})
我们将 function created 作为参数传递给 ds.app 函数,并以 tab 的形式提及它。
然后,我们将该帧推送到 dstack 应用程序。
url = frame.push()
print(url)
运行应用程序时,您现在可以查看 dstack 应用程序。
我们来看看带 dstack 的 ML 部分
train_data = get_data()
y = train_data.iloc[:,-1]
train_data = train_data.drop(['Loan_ID','Loan_Status'],axis=1)
test_data = get_testdata()
ids = test_data.iloc[:,0]
test_data = test_data.drop(['Loan_ID'],axis=1)def encoding(data):
data = pd.get_dummies(data, columns=["Gender","Married","Education","Self_Employed","Property_Area"],drop_first=True) return data
train = encoding(train_data)
test = encoding(test_data)
我们获得训练和测试数据,并将输出从训练数据中分离出来。然后我们进行编码,将我们的分类转换成数值。这是一个简单的数据预处理,确保在对数据使用任何 ML 模型之前完成所有必要的步骤。
from sklearn.ensemble import RandomForestClassifier
random = RandomForestClassifier()
random.fit(train,y)
我们正在训练一个来自 sklearn 的简单随机森林算法。
ds.push("Random_Forest", random, "Random Forest Loan Prediction")
一旦模型训练完毕,您就必须推送至 dstack 应用程序。它看起来会像这样。

作者图片
这是您所有模型的存储位置,您可以使用它。要使用该模型,您必须拉动它。
model = ds.pull('/dstack/Random_Forest')
一旦你得到了它,你就可以用它来预测你的测试数据。我们将在 dstack 中创建一个名为 Combobox 的下拉列表,它将包含一个名为“Predict”的值。
values = ctrl.ComboBox(data=['Predict'],label="Predictions",require_apply=True)
创建一个根据测试数据预测模型的函数。
def get_predicted(values: ctrl.ComboBox):
y_pred = model.predict(test)
y_pred = pd.DataFrame(y_pred)
y_pred = y_pred.rename(columns={0:'Prediction'})
y_pred['ID'] = ids
return y_pred
我们现在必须创建一个应用程序,并将该功能作为选项卡添加到框架中。
p_app = ds.app(get_predicted, values = values)
frame.add(p_app, params={"Predicted": ds.tab()})url = frame.push()
print(url)
就是这样。当您现在运行应用程序时,它会向您显示预测的选项卡。当你点击申请,它会显示你的贷款状态的预测值。

作者图片
在 dstack.cloud 上部署应用
您也可以在 dstack.cloud 上部署您的应用程序。以下步骤将帮助您完成此操作(您必须拥有 dstack.cloud 帐户)。
- 转到 C:/Users/
/。数据堆栈 - 在该文件夹中,在记事本中打开 config.yaml 文件,并添加以下内容。
---
profiles:
default:
user: <user-name>
token: <token-number>
server: [https://dstack.cloud/api](https://dstack.cloud/api)
- 请确保在 dstack.cloud 帐户设置选项卡中输入您的用户名和令牌号。
- 保存文件
- 回到你的代码
- 在代码中,转到编写 ds.pull()的部分,在这个函数中添加您的用户名。
model = ds.pull('/aniketwattamwar/Random_Forest')
- 一旦完成,就运行你的 python 文件 (现在不要本地启动你的服务器)
- 现在,您可以将应用程序链接分享给任何人。点击下面的链接打开 dstack 应用程序。
这里的是 dstack.cloud 上的文档链接
可以在我的 Github 上找到代码:https://Github . com/aniketwattamwar/Create-ML-apps-using-dstack . ai
希望这有所帮助。
和平。
垃圾邮件检测的 ML 分类器性能比较-第 1 部分
应用朴素贝叶斯、SVC 和随机森林进行邮件分类

作者图片
pam 邮件检测是机器学习算法的一个重要应用,用于过滤掉不想要的邮件。在自然语言处理领域,对于这种类型的分类有几种算法。通常垃圾邮件有一些典型的词,这使得这封邮件很明显是垃圾邮件。在本文中,我们将使用 nltk 包完成垃圾邮件和非垃圾邮件的文本处理。特别是,我们将看到 NLP 的词干化和词汇化过程。我们还将实现 NB 分类器以及 SVC 和随机森林分类器来检测垃圾邮件,并比较分类器的准确性。让我们开始吧。
根据 nltk 文档,“nltk 是构建 Python 程序来处理人类语言数据的领先平台”。使用 nltk 处理和标记文本是非常简单的,比如我们将在后面看到的词干化和词汇化。
首先,我们需要导入必要的包。
数据记录单
导入包含垃圾邮件和非垃圾邮件标签文本的 csv 文件后,我创建了两个数据框:一个用于真实电子邮件,另一个用于垃圾邮件,我们将利用它们进行分析。
词干和词尾
让我们先做词干,然后再把这袋单词做词条整理。根据斯坦福大学 NLP 小组的说法,“词干通常指的是一种粗糙的启发式过程,即砍掉单词的结尾,希望在大多数情况下正确实现这一目标,并且通常包括去除派生词缀。词汇化通常是指使用词汇和词的形态分析来正确地做事情,通常旨在只删除屈折词尾,并返回词的基本形式或词典形式,这就是所谓的词汇。这里,词干分析分别应用于所有数据、垃圾邮件数据和真实数据。
然后将该分类器分别应用于所有数据、垃圾数据和真实数据。
如果我们查看第一个数据文本的主干,我们会得到:
‘去句容点吧,去大世界吃自助餐,看电影’。
lemmatizer 提供了:
‘去句容点疯狂可用 bugis n 大世界 la e buffet cine more wat’
从第一个数据可以明显看出,词干和引理以不同的方式工作。例如,单词“availability”有词干“avail ”,但有引理“available”。
Wordcloud
用数值标注后,让我们创建 wordcloud 来查看最常用的单词。

作者图片
垃圾邮件中有很多吸引人的词语,而真实邮件中的文字则非常随意,如下所示。

作者图片
频数分布
我们可能有兴趣看到垃圾邮件中最常用的词的最高谱。它可以通过如下的频率分布获得
FreqDist({ ' . ':1004,“到”:608,“到”:542,',':371,' a': 358,'你':189,' call': 187,'你':187,' or': 185,' &': 178,...})
在其他时候,我们可能会关注垃圾邮件中最常见的重复句子部分。

作者图片
很明显,像“私人账户声明”或“秘密崇拜者”这样的句子是垃圾邮件中最吸引人的短语。
离差图
我们可以获得目标词的离差图来查看分布。它将提供特定单词在单词总数中出现的信息。我选择了像“免费”、“私人”、“帐户”、“联系”这样的词来演示。

作者图片
分类者
这里我从 scikit-learn 库中创建了一个分类器。我们需要将文本转换成令牌计数矩阵,scikit-learn 的 CountVectorizer()可以方便地完成这项工作。我们将首先尝试朴素贝叶斯函数,它实现简单,训练时间也较少。出于训练的目的,我选择了 80%的数据。

作者图片
我总是需要一个精确和回忆的复习。精度是 TP(真阳性)与 TP 和 FP(假阳性)之和的比值。召回率是 TP 与 TP 和 FN 之和的比值(假阴性)。如果一个真正的垃圾邮件被错误地识别为真正的电子邮件,那就是假阳性。另一方面,如果一封真实的电子邮件被识别为垃圾邮件,那就是假阴性。
报告显示,该模型在检测垃圾邮件方面表现良好,但在检测垃圾邮件方面表现不佳。垃圾邮件的精度约为 0.38,表明从该模型中获得了大量假阳性。尽管模型精度为 0.79,但这可能会产生误导,因为垃圾邮件的召回率很高,而精度却很低。这表明该模型偏向于垃圾邮件。它能够正确识别大多数垃圾邮件,但也会错误地将一些火腿识别为垃圾邮件。
array([[744, 224],
[ 12, 135]], dtype=int64)
混淆矩阵也显示了类似的情况。对角线没有最高的数字。这意味着朴素贝叶斯的性能不够好。
让我们试试支持向量分类以及随机森林算法。

作者图片
模型性能良好。火腿和垃圾邮件准确率和召回率都很高。最后让我们尝试随机森林作为一个分类器。

作者图片
随机森林在垃圾邮件检测案例中也是一个很好的分类器,对真实邮件和垃圾邮件都有很高的精确度和召回率。
结论
本文使用 nltk 库演示了自然语言处理的词干化和词汇化过程,并比较了几种二元分类算法。朴素贝叶斯产生较低的精确度,而 SVC 和随机森林提供较高的精确度、召回率以及精确度。交叉验证技术可以评估这些分类器的技能。现在,有许多开源平台可以进行训练和交叉验证,而不需要任何代码,我将在另一篇文章中讨论。
我的 github 页面提供了代码块。
垃圾邮件检测的 ML 分类器性能比较(下)
使用 TensorFlow 检测垃圾邮件并评估模型

图片来自 Unsplash
简介
对自然语言进行分类是当今自然语言处理的巨大挑战之一。它涉及到能够有效区分目标文本和正常文本的技术。聊天机器人等其他服务也严重依赖用户输入的文本。他们需要处理大量的数据,以确定用户的需求,并引导用户走上正确的道路。
使用张量流
在这个垃圾邮件分类器的第 1 部分中,我展示了如何使用 nltk 包对文本进行词干分析和词条分类,然后将其输入到分类器模型中进行训练,最后评估模型的性能。我已经展示了朴素贝叶斯、SVC 和随机森林作为电子邮件分类器的性能。
在本文中,我将演示如何使用 Tensorflow 对电子邮件进行标记和有效分类。让我们开始吧。
我已经包含了 pad_sequence,它将被用来使所有的文本数组大小相等。填充可以基于最大尺寸来完成,并且可以是后填充或前填充。接下来定义超参数
用户可以使用这些参数来防止过度拟合训练数据。例如,可以减少词汇大小,以最小化低频词的过度匹配。此外,嵌入维度越低,模型的训练速度就越快。我还包括了一个词汇以外的单词令牌。我将使用第 1 部分中使用的相同数据集。
我通常将整个数据集的 80%用于训练,剩下的将用于测试。测试数据集以前从未被模型看到过。
编码标签
由于数据集具有字符串形式的标签,因此它将通过编码为 0 和 1 (0 表示垃圾邮件,1 表示真实文本)来转换为整数。
接下来要做的是将文本和标签转换成 numpy 数组并输入到模式中。标记化也被启动并适应来自数据集的文本。
如前所述,需要进行填充以使数组长度相等。
型号定义
然后用双向 LSTM RNN 算法定义该模型。这里使用双向 LSTM 来获得 RNN 的最佳性能。
训练模型
在 20 个时期之后,该模型被很好地训练,并且验证数据(这里是测试数据)的准确度约为 98%

作者图片

作者图片
接下来,将仅使用 model.predict 方法来获得超出精度数的模型性能参数。

作者图片
该报告显示,该模型对于两种文本类别都具有非常好的精确度、召回率和 F1 分数(0 和 1: 0 表示垃圾邮件,1 表示真实邮件)。对于 0,召回率比精确度低一点,这表明存在一些假阴性。该模型错误地将一些真实的电子邮件识别为垃圾邮件。
我们可以识别任何样本文本,以检查它是垃圾邮件还是真实的。由于已经定义了记号赋予器,我们不再需要再次定义它。我们所需要的就是对样本文本进行标记,用 0 填充它,然后传递给模型进行预测。选择一些朗朗上口的词,如“赢家”,“免费”,“奖”,最终会使这个文本被检测为垃圾邮件。
结论
本文演示了如何使用 Tensorflow 来有效地训练一个具有高精度的 NLP 模型,然后评估模型的性能参数,如精度、召回率和 F1 分数。对于这个小数据集,20 个历元似乎可以生成一个验证准确率约为 98%的优秀模型。
垃圾邮件检测的 ML 分类器性能比较(三)
基于开源组件的 Orange 数据挖掘在垃圾邮件检测中的应用

图片来自 Unsplash
简介
人工智能长期以来一直掌握在高效的程序员和喜欢写代码和玩代码的语言书呆子手中。随着一些可视化拖放式 ide 的出现,新手不需要编写大量代码。相反,这些分析平台使广大普通人能够跳入机器学习的世界,而无需学习编写代码和处理繁琐的编程语法。
Widget 环境
Anaconda 提供 Orange 作为开源视觉分析平台,用户可以用最少的编码经验进行机器学习。在本文中,我将介绍用 Orange 编写的垃圾邮件分类器。下图是这个分类器问题的总体演示。我采用了朴素贝叶斯、随机森林和 SVM 算法,并评估了它们在这个数据集上的性能。
Orange 提供了一个拖放窗口小部件环境,不需要任何编码,如下所示。

作者图片
首先,我们需要在文件小部件中加载 csv 文件。当我们加载 csv 文件时,我们将忽略任何不需要的列。对于本文中的文本分类器,我们只需要文本和该文本的标签。在这里,标签列的角色应该是“目标”。

作者图片
特性和目标
要素是基础数据,模型将在此基础上接受训练,以正确标注目标中提到的数据。在 Select Columns 小部件中,我已经区分了目标列和特性列。我们只有一列文字,因此这里只有一个特性。

作者图片
可以在数据微件中可视化所需的数据,以确保不存在不需要的要素。

作者图片
接下来,所有三个分类器小部件都被放置在工作区中,并与数据表连接。我保留了所有分类器小部件中的默认设置,并更改了测试和评分小部件中的训练和测试过程。
测试分数

作者图片
在左边,很明显,随机抽样使用了 80%的数据进行训练。在这里,右边面板上数据的解释很重要。下表比较了所选三种算法的性能。AUC 代表“曲线下面积”,它被用作模型的能力量规以区分类别。AUC 越高,通常表明模型性能越好,但还有一些其他性能参数需要考虑。CA 是“分类准确率”。在第 1 部分的文章中讨论了精度和召回率的复习。F1 分数是另一个性能参数,在计算中考虑了精确度和召回率。一些模型可能产生高精度但低召回,反之亦然。因此,一些分析师对 F1 分数更感兴趣,F1 分数定义为

我们的表格显示 SVM 和随机森林都比朴素贝叶斯有更高的准确率。这里所有的分数都是班级的平均值。
最后,我们需要检查每个分类器的混淆矩阵。混淆矩阵部件提供了这些分类器的所有矩阵。

结论
在本文中,我展示了一种使用 Orange 实现简单文本分类器的简单方法。这个开源分析平台有很多超出这里讨论的东西。Orange 的窗口小部件环境非常吸引人,可以在没有任何编码的情况下高效地执行 ML 分类器。
交错的时间序列数据集
行业笔记
从参差不齐的时间序列数据中创建更好的数据集
为机器学习构建数据集是一个耗时的过程。我发现我倾向于在准备和清理数据集的项目上花费大约 70–80%的时间。
我处理数据集的方式通常遵循类似的路径。我过滤数据,只保留相关的样本和特征,然后进行清理。清理过程本身通常会根据数据类型而变化,但是一个简单的解决方案是,如果您有足够的数据来获得足够的样本大小,就删除脏行。
一旦数据被清理,我就开始将它处理到适当的级别。通常当我处理时间序列数据时,这种操作包括某种形式的量化。有时,时间序列数据很好,很干净,事件以固定的间隔发生,但在处理医疗保健相关数据时,情况很少如此。人们不会生病,也不会按照时间表去看医生,这种样本的随机分布就是我喜欢称之为“交错时间序列”的数据。这些数据的参差不齐意味着量化是唯一的选择。我用“量化”这个词来描述聚合/平均和输入值的过程,以将数据转换成均匀分布的格式。这可能意味着取一个月内所有测量值的平均值,并估算没有数据的月份的数据。
对于医疗保健环境中的 ML 模型来说,需要准备好数据以便将事件的绝对时间转换为彼此相对,这是相对常见的。这意味着第一时间步代表所有患者的给定疾病的发作或所有患者第一次入院。我通常把这个动作称为滚动,这来自滚动和轴的数字动作,对我来说,它很好地描述了这种情况。
需要注意的一件重要事情是,一旦所有的清理、量化(不包括插补)和滚动完成,我通常会将缺失数据的插补留到最后一步。这意味着,对于疾病进展这样的事情,插补可以以更直观的方式进行。与滚动发生之前的情况不同,在给定的时间步长,整个群体的平均值编码了一点有用的信息。

作者图片
这样做了很多次后,我意识到这将是构建一个简单的库来为我的许多项目处理这些标准函数的完美想法。除了简化构建过程,我意识到这个库可以处理版本数据集。这是以可脚本化和可配置的方式完成的,对于像 autoML 这样的概念非常有用,允许数据集构造的参数成为超参数优化空间的一部分!
我如何编写一个库来做这样的事情,并期望它可以用于其他数据集?很简单——EADV 格式。EADV 代表实体、属性、日期时间、值。实体表示某个事物(例如,一个人)的唯一实例,它可以有多个属性(例如脉搏率),并且这些多个属性可以有多个日期时间标记和值(它们的脉搏在 01/01/2008 14:21:04 上为 80bpm)。使用这种格式,基本上可以将非常复杂的数据表示为一个四列表。您可以将任何时间序列数据集转换成这种格式,正因为如此,这个库可以发挥它的魔力!
在我们生成数据集之前,需要对代码进行一些初始清理,以便稍后对数据集进行假设。这些事情包括将所有特性重命名为它们的小写对应项,如果您有两位数年份列的数据,则修复日期。
一旦我们有了干净的输入数据,我们就可以产生实际的 ML 数据集。这是使用下面的函数来完成的,该函数过滤、量化、滚动和估算!
这里有一个用 JSON 编写的示例配置文件,描述了我在工作中构建回归器所需的真实数据集的一些操作。
Ml 驱动的营销活动目标
为营销活动创建 ml 驱动的目标是一项困难的工作,而且通常很难理解。你怎样才能避免给你的公司带来损失?

使用机器学习模型来创建营销活动的目标可能是“在我的测试集上看起来不错”/“哦,它在现实世界中不起作用”比率最高的任务之一。
我们来设定场景。你被一家数码糖果店公司聘为数据科学家。你的任务是优化营销活动,特别是邮寄或推送通知活动,在这些活动中,你通常会奖励优惠券以换取购买产品。在一个虚构的例子中,假设这是一个精美的比利时巧克力盒,利润为 12 美元。你为产品提供 10 美元的优惠券。作为一名数据科学家,你已经建立了一个性能良好的机器学习模型,该模型可以识别更有可能购买产品的客户。
你的模型是一个二元分类器,它通过点击你网站上的横幅来查看在过去 12 个月中购买了盒子的客户,并将他们与那些曾经在你网站上但没有点击横幅的客户进行比较。给定一组特征 x ,它产生成为买家的概率 B ,然后将非买家从最不可能成为买家的顺序排列。这些模型通常被称为营销倾向模型[1]。
你负责第一次竞选。为了在真实场景中测试模型,您从所有概率十分位数中随机选择客户,并将他们分成 80%的目标组和 20%的对照组。目标将收到轻推(电子邮件+优惠券),而控制将被搁置。

图 1: 右边是我们的比利时巧克力倾向模型的输出。 P(B = 1 | x)是给定一些特征成为买家的概率,Rank 是顺序,probability decile 是基于概率所属的十分位数的聚类标签。左边是我们如何分成目标组和对照组。图片由作者提供。
活动结束后,你会急于查看结果。这个模型有效吗?根据图 2,似乎是这样的!您的模型正确地预测到,您认为更有可能购买的客户确实是成功率最高的客户(买家/总十分位数客户)。前十分之一的顾客购买的可能性是第三十分之一的顾客的 3 倍,以此类推。

图 2:x 轴上,概率十分位数客户所属;y 轴上的成功率定义为购买该产品的客户总数/该十分位数的客户总数。基线(水平灰线)是在目标群体中观察到的总体成功率。图片由作者提供。
让我们看看这在经济方面是如何实现的。考虑到成功率、优惠券成本和产品利润,我们的活动产生了大约 7200 美元的增量利润。确实不算太坏。如果你每个月都这样做,你可以预期获得 86,000 美元的额外利润。

图 3。 目标成功率 =总购买者/十分位数目标规模;以\(** 表示的活动成本:目标规模*目标成功率*单位优惠券成本。**以\) 表示的活动利润:目标规模目标成功率单一产品利润。增量利润(单位:):活动利润(单位:)—活动成本(单位:)图片由作者提供。
在这一点上,你绝对是房间里的英雄。但是之后你决定在给定十分位数的情况下检查对照组的表现。

****图 4:x 轴上,概率十分位数客户所属;y 轴上的成功率定义为购买该产品的客户总数/该十分位数的客户总数。基线(水平灰线)是在目标群体中观察到的总体成功率。图片由作者提供。
没错,你的模型很擅长预测谁会购买该产品,事实上,在对照组中也观察到了类似的表现。然而,与对照组相比,该运动在中间和最低概率十分位数中取得了最高的提升。看看这场运动的经济学会让事情变得更清楚(希望如此)。

图 5: 图 3 增加的计算列有:控制成功率 =控制组总购买者/控制规模;PP 中的成功率增量:目标成功率——控制成功率。美元无活动利润:控制成功率目标规模单一产品利润;美元中无活动利润:美元中的增量利润—美元中无活动利润。图片由作者提供。**
您包括关于控制组大小和控制成功率的数据。有了这个额外的部分,就有可能计算出一个以$ 为单位的无活动利润,这个利润相当于你在给定对照组成功率和给定没有奖励券的情况下观察到的利润。关键指标是净收益,这是增量利润和无活动利润之间的差额。正如我们所看到的,前十分位数的净收益为负,这意味着对于这些客户,我们最好不要开展活动。从第五十分位数开始,事情开始发生变化。总的来说,你从庆祝 7k 的利润变成了不得不解释 18k 的损失。****
简单地说,你在解决错误的事情。预测谁会买我们的比利时巧克力盒和估计谁会买我们的比利时巧克力盒并不一样。你偶然遇到了那些无论如何都会购买该产品的交易追逐者。事实上,这个任务应该被设计成一个因果问题而不是一个预测问题。****
借用拉德克利夫&萨里的话[1]:
当今最有针对性的营销活动;即使是在增量影响的基础上进行衡量,也是以非增量车型为目标。
你怎么解决这个问题?
如果你有足够大的客户群(有多大),为了最大限度地降低在无效活动中浪费资金的风险,你应该考虑分两步进行活动。开放式问题)和可以支持一定程度的定制的营销软件。
****第一步:从您的倾向十分位数中随机抽样进行试点。将您的目标分成大小相等的目标和控制组,以便获得足够的数据来获得具有统计意义的结果。
****第二步:试运行后不久,分析结果。找到与对照组相比有报酬提升的倾向十分位数(或使用你可能有的任何其他聚类方法)。
****第三步:在符合条件的客户群中开展活动,保存您为试点项目抽样的客户。这次你可以允许一个小得多的控制组。宣传活动应该在试播几周后开始。很明显,你不能把你在圣诞节做的一个试验的结论用于七月的活动(过度适应是你最大的敌人)。

图 6: 在开展活动之前,先进行一次小规模试点,以确定可能会购买你的产品的人群。作者图片
回到竞选经济学,这相当于为无利可图的概率十分位数(紫色的那个)挡住了优惠券。

****图 7。与图 5 相同的计算,但是这次前四个概率十分位数的单位优惠券成本被设置为 0(没有提供优惠券),使商业案例回到正。图片由作者提供。
如果你那样做了,你会看到 7000 美元的实际净收益。略低于你最初的预期,但肯定比你的损失大得多。
接下来是什么?
为一场运动找到合适的目标是困难的,有时甚至是麻烦的。一个关键的要点是,只要你的客户群发生变化并与你的产品互动,就必须把它当作一个持续的实验。当您受到预算和联系压力的限制时,这就变成了一项数据密集型活动,应该由正确的分析能力和营销自动化软件来支持。
机器学习可以成为一个强大的锦囊妙计,通过适当地使用监督或无监督的倾向模型,或利用因果推理技术,如增量提升模型[2]。这些可以介入试点和实际活动之间的评估阶段,取代我上面介绍的识别无优惠券客户的简单方法。
在下一篇文章中,我将介绍 X-Learner,这是一个因果推理提升模型,用于识别更有可能将您期望的行动作为您活动的效果的客户。也许你不会永远摆脱那些追逐交易的人,但你肯定会让他们不好过。
参考
[1] Radcliffe N .和 Surry P .“基于重要性的隆起树的真实世界隆起建模”白皮书 TR-2011–1,随机解决方案(2011 年)
[2] Rodrigues J .应用数据科学技术获得可操作的消费者洞察。Addision-Wesley (2020)。关于因果推理方法的部分是我第一次遇到隆起建模的想法的地方。
面向产品经理的 ML
运送公司下一件大事的五个步骤。

由 Charles Deluvio 在 Unsplash 上拍摄的照片
所以,你是一名项目经理,有很多关于人工智能如何变革行业,你的公司如何由数据驱动,或者你的产品如何具有深度分析焦点的讨论。但是这到底是什么意思呢?如果不了解数据驱动的应用程序是如何工作的,你的 PRD 将会是空话连篇。我们可以在圈子里谈论分析的价值,但如果不了解如何将机器学习产品化,我们将永远不会真正实现。
本文将向您介绍构建和部署机器学习模型的五个步骤,并使用时序异常检测来演示一个真实世界的示例。最好的部分:这种提炼的方法将适用于任何产品或功能(谈论迁移学习!)
概括地说,这是大规模销售 ML 的五个步骤。
- 确定 ML 方法
- 建立管道
- 特征工程
- 训练你的模型
- 展开
1。根据产品目标确定 ML 方法
产品目标决定了技术方法。通过首先理解我们的用例,我们可以探索它如何适应 ML 范例。
产品目标:假设你的产品是给一个应用程序所有者的,他负责向董事会报告他们的应用程序。他们只想知道他们的应用程序执行得如何,没有时间去考虑过多的技术细节。他们对应用程序的可靠性感到失望,并希望获得实时信息,这样他们就不会因中断而措手不及。
从这些客户对话中,我们发现了产品的目标:如果用户的应用程序停止工作,提醒用户。
神智检查:在潜入 ML 兔子洞之前,先问:
- 你真的在解决一个有价值的问题吗?
从新闻稿开始,确定项目是否值得投资。我们的解读是:通过消除手动监控应用程序性能的必要性,节省了数百个工时和无数次客户对话。最终,这种异常检测将使人们不必部署资源来主动监控他们的应用程序。这似乎很有价值。
2 。真的需要 ML 吗?
在许多情况下,基于启发式的方法会更好。对于我们的例子,这意味着基于 HTTP 响应代码和响应时间阈值的某种组合来创建一个决策树,以确定应用程序是否正在执行。需要为每个指标建立决策树和手动阈值,所有服务都有自己的“正常”定义。在这里,这种基于规则的方法无法扩展。为了对大规模应用性能进行分类,我们已经确认需要 ML。

机器学习使用数据来训练函数(图片由作者提供)
ML 框架:现在我们已经验证了这个想法,让我们根据机器学习来设计产品目标。机器学习使用数据来学习一个函数。我们已经收集的历史应用程序行为(数据)训练我们的模型来预测我们的应用程序行为是否正常(函数)。然后,我们可以通过这个训练有素的模型持续提供性能数据,以实时确定我们的应用程序是否正常工作。
重新表述我们的产品目标,我们希望使用性能信息来检测应用程序中的行为异常。
数据选择:数据为机器学习提供动力。为了选择一个模型并了解我们的结果是否可能,我们需要了解可用的数据。数据可以分为两类——特征和标签。标签是 ML 模型的输出,对我们来说,这是正常或异常应用程序行为的布尔结果。特征是负责这种分类的其他输入变量。

数据分类(按作者分类的图片)
因为我们的函数需要应用程序性能数据,所以我们可以使用计时信息(HTTP 响应时间)、性能数据(HTTP 响应代码)作为特性。这些数据是由一个综合监控系统生成的——定期 ping 一个特定的网址并记录响应。与该数据相关的是时间信息(从测量开始的时间戳,可用于季节性)。虽然我们的数据包含这些性能特征,但它不包含任何标签——没有人检查过所有过去的点并标记出哪些是异常的,哪些不是。图中的‘标签’栏对我们来说是不存在的,这就限制了我们可以使用的可能模型。
模型选择:不同的模型更适合不同的任务,都是对数据做出不同的假设。在选择模型之前,我们必须考虑许多会影响部署的因素:数据可用性、延迟和实现的容易程度。
数据可用性是指在训练和预测过程中有哪些数据可用。有监督和无监督这两种主要模型类型之间的选择取决于数据可用性。监督模型训练包含要素和标注的数据,而非监督模型只需要要素。我们有上面讨论的性能指标,但没有标签,这意味着我们需要使用一种无监督的方法。
潜伏期是产品对预测时间的要求。这里的两个考虑因素是流或批处理。流处理模型对数据立即运行,而批处理模型以固定的时间间隔对收集的数据运行分析(允许处理时间较长的方法)。仅仅向用户显示带有异常的标记图或让他们挖掘他们的指标不会减少操作员对屏幕的需求。从描述的用例来看,应用程序所有者希望最大限度地减少他们盯着原始指标看的时间。我们的产品需要一个指向流处理的近实时分类。
实现的容易程度影响我们开发模型的速度。训练一个复杂的端到端管道是一个微妙的过程。我们必须考虑性能-复杂性的权衡。虽然更复杂的模型可能会给出更好的结果,但它也会增加大量的开发时间。对于实现,我们将从一个简单的基础模型开始,它将支持快速开发、迭代和调试。
下面是来自领先的 python ML 库 scikit-learn 的模型选择流程图。有关不同型号的更多信息,请点击此处。

ML 型号选择流程图(来源: Scikit-Learn )
我们的数据中缺乏标签,这要求我们使用一种无监督的方法,特别是基于聚类的方法。因为我们要为已知数量的类别预测一个标签,所以我们使用 KMeans 。我们将运行 K 均值,其中 K=2,正常和异常。下面是 KMeans 如何将原始数据分组到已知聚类中的直观描述。

KMeans 解释道(来源:谷歌 AIHub )
为了使模型更加复杂,我们可以利用时间序列方面,使用 ARIMA (自回归综合移动平均)模型或 RNN(递归神经网络)。然而,我们将从 KMeans 开始,因为它易于实现,以便快速构建一个基础模型。如果我们需要额外的性能或功能,我们可以稍后更换为 ARIMA 型号。
创建指标:指标帮助我们跟踪产品进度,比较不同的实现。为了判断我们的模型,我们将考虑培训绩效、模型绩效和业务绩效的指标。虽然我们可能会在早期查看培训和模型度量来指导培训,但我们最终的生死取决于业务绩效。我们的产品目标应该由业务指标的端到端性能来判断。
培训绩效由作为 ML 流程一部分的优化指标来衡量。根据选择的型号,有许多正在使用的。对于监督方法,人们通常使用混淆矩阵、ROC 曲线或校准曲线(更多关于这些这里)。因为我们使用的是无监督方法,我们将使用轮廓系数。这是我们可以使用的仅有的聚类指标之一,因为它不需要训练数据中的标签。轮廓系数分数与具有更好定义的聚类(异常和正常)的模型相关。
模型性能是评估 ML 方法有效性的指标。对于许多产品来说,这意味着使用该模型的用户与能够从中受益的用户总数的百分比。因为我们的产品是一个警报系统,模型性能最终取决于我们检测所有异常的能力。这确保了我们的客户(应用程序所有者)不会被遗漏的错误弄得措手不及。用 ML 术语来说,我们正在优化召回,或捕获的异常百分比。
我们需要某种形式的用户反馈来了解我们的模型在生产中的表现。这通常看起来像我们前端的数据收集机制。用户反馈可以像用户在查看预测的异常时选择的拇指向上/向下那样明显,也可以像用户根据前端采取的行动直观地看到标签那样无摩擦。
基于用户交互隐式分配标签称为弱标签。这些弱标签的大量收集最终会让我们使用更强大的监督学习方法来代替。每个数据驱动的应用程序都必须将收集用户反馈作为重中之重。
业务绩效指标反映了模型相对于产品目标的成功程度。我们也可以创建护栏指标,它们不应该下降到某个点以下。
因为我们正在训练我们的模型不遗漏任何关键错误,所以我们可能会向用户呈现大量的警报。这种警报疲劳可能会让我们的模型在生产中用处不大,因为用户会停止关注。为了确保客户不会开始忽视我们,我们最终关心的是相关警报的百分比。判断这一点的一个隐含方法是被处理的警报的百分比。用 ML 术语来说,这叫做精度。当与作为护栏指标的召回相结合时,为了确保我们不会遗漏高重要性异常,我们有一个强大的系统来了解我们数据的性能。
其他需要考虑的因素还有速度和新鲜度。速度是推断需要多长时间,新鲜度衡量我们的训练数据与实时数据相比有多好。为了准确起见,训练数据需要与输入数据相似。当实时数据的分布发生变化时,模型也需要重新训练。
探索性数据分析(EDA): 在我们训练一个模型之前,我们需要对底层数据有一个很好的了解。使用像谷歌的数据工作室这样的可视化工具可以帮助我们测试关于值和分布的假设,识别关键变量,并检测数据中的结构。为了更深入地挖掘,我们可以使用一个 Jupyter 笔记本和 pandas ,一个 python 数据分析工具。
检查我们的数据,我们看到最大值比第 75 百分位大得多,这表明对其使用异常检测可能是一个有意义的数据集-异常应该会出现。
深入研究 http 请求时间,我们看到主要的左侧集群是正态分布的(图中的对数正态),右侧有几个点。这种探索性的数据分析让我们对我们的 KMeans 方法更有信心。事实上,我们可以开发一个简单的试探法,如果 http 请求时间大于 1.25 秒,那么它就是异常的。

http 响应时间的分布(图片由作者提供)
2。建立管道
端到端的管道便于迭代。我们的模型越快失败,我们就能越快取得进展。评估模型性能可以快速确定下一个需要改进的地方。
关于部署管道的技术信息,请参考来自谷歌云平台(GCP)或亚马逊网络服务(AWS)的 各种 教程 。

训练和推理管道(图片由作者提供)
ML 需要一个训练和推理管道。训练管道(稍后讨论)创建我们的模型,推理管道进行预测。
构建推理管道:从部署推理管道开始。通过快速交付基本模型,我们可以开始收集使用数据,以支持进一步的培训和性能提升。这个初始模型是基于来自主题专家(SME)知识的简单启发。
使用 1250 ms 的 HTTP 请求时间作为阈值,让我们通过这个基本模型运行我们的训练数据。由于数据的分散,我们将绘制请求时间的日志,以使结果更容易理解。

使用阈值对训练数据集进行异常检测(图片由作者提供)
测试工作流程:在我们可以服务我们的简单模型之后,让我们调查一下我们关于用户体验和模型结果的假设。
用户体验是一个重要的考虑因素,因为可用性将推动我们的模型的使用。询问展示我们产品的最佳方式是什么?我们如何确保结果被有效地呈现出来?哪些数据如果返回实际上会有帮助?迫使我们考虑我们的前端设计。向用户发送没有因果关系的异常信息会导致用户疲劳,并立即让人们远离我们的产品。相反,有用的结果包含关于异常的重要性和导致异常分类的特定因素的数据。
模型结果也是一个必要的考虑因素。提问:我们是否得到了重要的结果?训练数据是否准确,是否具有代表性?怎样才能消除偏见?最初的运行表明,严重的异常被发现,但是大部分的变化和细微差别仍然没有被检查。因为有许多其他指标构成了异常分类(不仅仅是 HTTP 请求时间),所以我们需要开发一个考虑这些特性的更好的模型。
3。特征工程
了解您的数据将带来最大的性能提升。探索要有效率,从容易处理的数据集开始。查看数据集以了解其特征是开发实体模型和特征工程管线的最简单方法。这通常会消耗大部分开发时间。
确保您的数据已格式化,具有在预测时可用的清晰的输入和输出。通过手动验证标签或特征值,确保数据字段没有丢失、损坏或不精确。检查数据量(确保您至少有 10k 个示例),如果需要,生成合成数据。查看集群和汇总统计数据。检查我们的训练集,我们可以丢弃所有没有有意义的(空的或 na)计时数据的例子。
从季节性、特征交叉或其他你观察到的有意义的模式中创建特征。我们可以向量化以从原始数据中创建新的特征,并且降低维度(在尽可能保留结构的同时以更少的维度表示向量)。这些特征使得检测神经网络中的非线性关系更加容易。确保保存用于创建新要素的变换和过程,因为它也必须在预测时对输入数据运行,否则模型将无法正常运行。
我们将为一天中的每一个小时和一周中的每一天创建特征,以及两者之间的交叉特征。此外,我们将创建一个名为“total”的新特性,它是 tlsHandshakeMs、httpRequestTimeMs 和其他计时指标的总和。
创建其他模型有助于培训过程。一个“错误模型可以被训练来检测你的模型在什么类型的例子上失败。如果您正在使用监督训练模型,则可以使用“标记模型”来查找下一步要标记的最佳示例。该模型检测有标签和无标签的例子,允许您标记那些与有标签的例子最不同的无标签的例子。因为我们坚持使用简单的、无人监管的方法,所以我们现在可以忽略创建这些模型。
4。训练你的模型
现在我们已经有了工程特征,我们可以通过拆分数据、判断预测性能和评估特征性能来训练我们的模型。
分割数据:为了确保我们能够验证训练好的模型的结果,我们需要留出数据。具体来说,我们 70%的数据用于训练,20%用于验证,10%用于测试。训练数据优化模型的权重,验证集调整超参数(网络深度、每层过滤器数量和权重衰减),测试集是我们评估模型的方式。
确保验证和测试数据集尽可能接近生产,以防止数据泄露(未来数据用作训练特征,数据集之间的重复意味着过度拟合带来的超大性能)。确保数据被正确地分割。
调试:异常高的性能通常表示数据泄漏或错误。谷歌已经写了大量关于人工智能系统潜在测试的文章。要调试模型,
- 首先,确保模型连接正确,以便数据从输入流向预测
- 接下来,确保模型能够符合训练数据
- 最后,假设数据在合理的范围内,检查模型是否能拟合看不见的数据
判断性能:选择一个合适的代价函数来优化你的模型。检查测试和训练数据的成本函数有助于估计模型中的偏差-方差权衡,即我们的模型在不仅仅记忆训练集的具体细节的情况下已经学习了有价值的概括信息的程度。具体来说,检查:

我们的异常检测用例的混淆矩阵(图片由作者提供)
- 混淆矩阵:输出可以是两个或两个以上类别的机器学习分类问题的性能度量。这是一个包含预测值和实际值的 4 种不同组合的表格
- ROC 曲线:显示一个模型区分不同类别的能力。产品经理受益于根据产品需求添加对应于允许的假阳性(FP)或假阴性(FN)率的垂直线或水平线
- 校准曲线:TP 相对于置信水平的分数
评估特征:通过检查分类器,检查哪些特征有助于分类。如果使用神经网络,使用黑盒解释器,如莱姆或 SHAP 。
因为我们使用的是无人监督的方法,我们可以忽略所有这些。由于我们的数据不包含标签,我们实际上无法用这些方法来验证模型。因此,我们将所有数据发送到培训中。相反,我们可以使用轮廓分数来检查我们的输出。在我们的 KMeans 模型中,这个指标返回 0.802(其中 1 是最好的,1 是最差的)。可视化分类标签表明,这种无监督的方法产生了合理的结果。

使用 KMeans 对训练数据集进行异常检测(图片由作者提供)
在我们的延迟指标中,简单模型的优势显而易见。在具有 4 个 vCPUs 和 15gb RAM 的机器上,训练平均花费了 0.516 秒。所有训练数据的推断花费了 0.004 秒。这远远低于任何合理的人类反应时间。
然而,我们的模型产生了很多警告。这强调了收集用户反馈的必要性。如果我们有关于哪些异常是有帮助的数据,我们可以使用监督方法进一步调整我们的模型,以便只提供可操作的警报——我们的用户实际需要的警报。
5。展开
现在模型已经训练好了,我们执行最终验证,构建生产环境,并开始监控。这里是必要组件的高级视图。

ML 生产环境的组成部分(图片由作者提供)
最终确认是将模型投入生产前的最后一次完整性检查。提问:你的模型根据训练数据做出了什么假设?训练数据收集的怎么样?这与生产数据有意义的不同吗?数据集的代表性是否足以产生有用的模型?
还需要考虑预期用途和范围。确认所使用的数据已被授权收集、使用和存储。通过确保没有测量错误、损坏的数据以及所有要素类的正确表示来消除训练偏差。通过采用反馈循环和对所有训练数据子集进行基准测试,消除系统偏差和建模问题。通过部署检测使用活动变化的监控系统来防御对手。
构建一个可靠的生产环境,以正确运行您的模型,通过工程实现:
- 失败 (I/O 检查):通过确保输入数据在训练数据的范围和分布内来净化输入数据。创建第二个“故障检测模型”来预测最有可能失败的输入,或者创建一个“过滤模型”来预先筛选推论。如果这些模型表明一个潜在的可疑的结果,退回到一个简单的模型或启发式提供一个合理的输出。
- 性能:如果适用的话,通过缓存来提高性能(在处理重复输入时,通常使用最近最少使用的缓存或 LRU 缓存)。确保管理模型和数据生命周期管理(何时根据漂移迹象重新训练模型)、可再现性、弹性和管道灵活性。
- 反馈:收集隐性反馈对判断绩效至关重要。考虑查看用户执行的操作,以推断模型是否提供了有用的结果。
- CICD: 为了实现机器学习的 CICD,考虑以影子模式部署(与现有模型并行,对照生产模型检查结果)。既可以使用 A/B 测试,也可以使用多臂土匪(在生产中探索/利用多个模型)。
监控模型以告知生产环境的弹性。具体来说,看一段时间内的准确率和使用情况。精度随时间变化,以通知刷新率,或何时重新训练模型。大多数模型需要定期更新,以保持一定的性能水平。监控可以检测模型何时不再新鲜,何时需要重新训练。使用情况可以显示滥用的模式(例如,异常的登录次数)。类似地,监控前面概述的性能和业务指标。
那么,你打算什么时候运送你的下一个大东西?
ML 从零开始:线性、多项式和正则化回归模型
通过在 python 中从头实现线性模型,深入了解回归中使用的线性模型。

封面照片——卢克·纽曼拍摄
在这个新的系列中,我通过编写干净的、可重用的、有良好文档记录的带有测试用例的代码来提高我的编码技能和习惯。这是我以面向对象的方式从头开始实现线性、多项式、脊形、套索和 ElasticNet 回归的系列文章的第一部分。
我们将从一个简单的 LinearRegression 类开始,然后在它的基础上以类似于 Scikit-Learn 的简单风格创建一个完整的线性模型模块。我的实现绝不是最优的解决方案,只是为了增加我们对机器学习的理解。
在这个库中,你会找到这个博客中的所有代码,包括每个类和函数的测试用例。
要查看 Github 资源库,请访问此处。
装置
为了使用类和函数进行测试,创建一个虚拟环境并 pip 安装项目。
$ pip install mlscratch==0.0.1
要从 Github 下载本地存储库中的所有源代码,请创建一个虚拟环境,并在您的终端中运行以下命令。
$ git clone https://github.com/lukenew2/mlscratch
$ cd mlscratch
$ python setup.py install
线性回归
通常,任何人在他们的数据科学之旅中学习的第一个模型是线性回归。简单地说,线性回归模型通过计算每个自变量的参数权重加上一个称为偏差项(也称为截距项)的常数,来表示因变量标量变量 y 和自变量 X 之间的关系。
我们通过将特征权重向量θ、乘以独立变量 X 来进行预测。
就是这样!这就是线性回归模型的全部内容。让我们看看我们是如何训练它的。

图一。线性回归——卢克·纽曼
训练模型意味着找到最适合训练数据集的参数。为此,我们需要能够衡量模型与数据的吻合程度。对于线性回归,我们找到最小化均方误差(MSE)的θ的值。有多种优化算法可以做到这一点,所以我们来看几个。
普通最小二乘法
我们要从头开始编码的第一种方法叫做普通最小二乘法 (OLS)。OLS 计算出 X 的伪逆,并将其乘以目标值 y 。在 python 中,这个方法很容易使用 scipy.linalg.lstsq() 来实现,Scikit-Learn 的 LinearRegression() 类使用的就是这个函数。
我们将尝试通过使用 fit() 和 predict() 方法,使我们的库类似于 Scikit-Learn 的库。我们还将赋予它属性 coef_ ,它是在训练期间计算的参数。
模块 1。回归. py
要编写 fit() 方法,我们只需向我们的特征数组添加一个偏差项,并使用函数 scipy.linalg.lstsq() 执行 OLS。我们将计算出的参数系数存储在我们的属性 coef_ 中,然后返回 self 的一个实例。 predict() 方法甚至更简单。我们所做的就是为偏差项的每个实例加 1,然后取特征矩阵和我们计算的参数的点积。
当特征的数量变得非常大时,普通的最小二乘法会变得非常慢。在这些情况下,最好使用另一种称为梯度下降的优化方法。
批量梯度下降
梯度下降是一种通用优化算法,通过对参数进行微调来搜索最优解。首先,用随机值填充θ(这种方法称为随机初始化)。然后,调整参数,直到算法通过向降低成本函数的方向行进而收敛到最小解。在我们的情况下,这意味着减少 MSE。
为了沿着降低成本函数的方向行进,你需要计算包含成本函数所有偏导数的梯度向量。

等式 1。成本函数的梯度向量
计算完梯度向量后,在梯度向量指向的相反方向上更新参数。这就是学习率 (η)发挥作用的地方。学习速度决定了你朝那个方向迈出的步伐的大小。

等式 2。梯度下降步骤
批量梯度下降是梯度下降的一个版本,我们在每一步计算整个数据集的梯度向量。这意味着批量梯度下降不适用于非常大的训练集,因为它必须加载整个数据集来计算下一步。如果你的数据集非常大,你可能想使用随机梯度下降或小批量梯度下降,但我们不会在这里涵盖这些。只要知道他们的存在。
让我们编辑当前模块,以包括使用批量梯度下降进行训练的选项。因为我们还将创建一个 Ridge、Lasso 和 ElasticNet 类,所以我们将创建一个所有回归变量都可以继承的基本 Regression()类。
模块 2。回归. py
现在,我们有了一个全功能的线性回归模型,能够使用批量梯度下降和普通最小二乘法进行训练。但是,如果我们的数据不是一条直线呢?如果是这样的话,我们就需要一个更复杂的模型来拟合非线性数据,比如多项式回归。
多项式回归
使用线性模型对非线性数据进行建模的一种简单方法是添加每个特征的幂作为新特征,然后在这个扩展的特征集上训练模型。这种技术被称为多项式回归。
当您有多个特性时,这种技术能够发现特性之间的关系,因为您将所有特性的组合添加到给定的程度。

图二。多项式回归——卢克·纽曼
好了,现在我们知道多项式回归与线性回归相同,只是我们在训练之前向数据集添加了多项式要素。我们不是创建一个单独的多项式回归()类,而是添加一个预处理类,它可以在训练之前转换您的数据。这样,一旦我们建立了正则化的线性模型,它们也将能够执行多项式回归。
我们将以类似于 Scikit-Learn 的预处理类的方式对其进行编码。它将有一个 fit() 、 transform() 和 fit_transform() 方法。
模块 3。预处理. py
如果此时尝试执行多项式回归,可能会在训练过程中出错。这是因为您使用梯度下降作为训练算法。当我们变换要素以添加多项式项时,如果我们使用梯度下降等迭代训练算法,则规范化数据非常重要。如果我们不这样做,我们就有遭遇爆炸梯度的风险。
数据标准化
有许多方法可以标准化数据,但是我们将坚持使用最简单的一种。通过减去平均值并将每个实例除以每个特征的标准偏差,我们有效地标准化了我们的数据。这意味着我们所有的特征都以平均值 0 和单位方差为中心。
让我们将一个名为 StandardScaler() 的类添加到我们的预处理. py 模块中。我们将在这里包含一个 inverse_transform() 方法,以防我们需要在数据被标准化后将其恢复到原始状态。
模块 4。预处理. py
使用我们的代码,多项式回归可以分三步实现。首先,向数据集中添加多项式要素。然后,使用 StandardScaler() 标准化我们的特征。最后,用 OLS 或批量梯度下降训练我们的线性回归模型。
正则化线性模型
使用多项式回归,我们很容易通过将 degree 参数设置得太高来过度拟合数据集。减少过度拟合的一种方法是正则化模型(即约束它):模型的自由度越少,它就越难过度拟合数据。
对于线性模型,正则化它们的一种方法是约束参数权重。让我们从零开始,通过编码脊、套索和弹性、网回归,来看看实现这一点的三种不同方式。
山脉
岭回归是线性回归的正则化形式,其中我们将正则化项添加到成本函数中,等于参数权重的 L2 范数的一半。使用岭回归,我们的成本函数看起来像:

等式 3。岭回归成本函数
其中α控制您希望添加到模型中的正则化程度。为了使用梯度下降从头实现这一点,我们还需要正则化项的偏导数。很简单,就是α乘以参数权重。
需要注意的是,正则项 sum 从 i=1 开始,而不是 0。这是因为我们没有正则化偏差项。
好了,我们知道了向 regression.py 模块添加 Ridge 所需的一切。首先,我们将创建一个计算 L2 范数和梯度向量的类。
模块 5。正则化. py
当我们调用这个类时,它将表现为一个函数并为我们计算正则项,当我们调用它的 grad() 方法时,它将计算梯度向量正则项。现在,我们只是使用我们的助手类来计算我们的基本回归类中梯度下降过程中的正则项。
模块 6。回归. py
在我们的基本回归类的 fit() 方法中,我们将正则化项添加到我们的误差度量 MSE 中,并将导数添加到梯度向量中。
接下来,我们更新了 LinearRegression 类,以包含 self.regularization 和 self . regulation . grad 属性,通过快速使用 lambda 函数,这些属性的值为 0。这意味着使用线性回归时没有正则化。
最后,我们创建我们的岭回归类,它继承了基本回归类,给它相同的属性和方法。我们唯一需要改变的是 init() 方法。
我们给它两个属性,alpha 和 self。正则化。Alpha 用于控制正则化和 self 的数量。正则化等于我们的 l2 _ 正则化类,它计算我们在梯度下降中使用的惩罚项。
套索
最小绝对收缩和选择算子(Lasso)回归的实现方式与岭回归完全相同,只是它增加了一个等于 L1 范数的正则化惩罚。

等式 4。Lasso 回归成本函数
因为 L1 范数在 0 处不可微,所以我们用等于符号函数的次梯度向量来计算梯度上的正则化罚值。

等式 5。Lasso 回归次梯度向量
要从头开始完成 Lasso 回归的实现,我们所要做的就是创建一个正则化类来帮助计算惩罚项,然后创建 Lasso 回归类。
首先,我们将 L1 正则化类添加到 regularization.py 模块中。
模块 7。正则化. py
其次,我们将创建一个新的 Lasso 类,它也将从我们的 regression.py 模块中的基本回归类继承。
模块 8。回归. py
Lasso 回归实现了一种要素选择形式,因为它倾向于消除最不重要的要素(即将其设置为零)。
弹性网
弹性网实现了对成本函数和梯度向量的脊和套索正则化项的简单混合。混合比 r 决定了每项包含多少。当 r = 0 时,弹性网相当于脊线,当 r = 1 时,相当于套索。

等式 6。弹性净成本函数
正如我们从头开始编写 Lasso Regression 时所做的那样,我们创建了一个正则化类来计算惩罚,并使 ElasticNet 类从基本 Regression 类继承。
模块 9。正则化. py
现在让我们编写 ElasticNet 回归类。
模块 10。回归. py
让我们快速地提一下你什么时候应该使用我们从头编码的每个模型。一般来说,使用稍微正则化的正则化线性模型几乎总是更可取的。
山脊线是一个很好的默认值,但是如果您预计只有少数要素有用,则应使用套索或弹性网,因为它们具有要素选择属性。当要素的数量大于训练实例的数量或者当多个要素高度相关时,Lasso 可能会表现不稳定。因此,通常优选弹性网。
包裹
这就完成了机器从头学习系列的这一部分。我们已经实现了用于回归的最常见的线性模型。
我怎么强调都不为过,从头开始编写机器学习模型将会成倍地增加你的 ML 知识,同时也增加你在 python 和面向对象编程方面的技能。
对于扩展这个项目的想法,我想包括小批量和随机梯度下降作为训练每个模型的选项。此外,实现某种形式的早期停止将进一步增加我们的机器学习知识。
一如既往,感谢阅读和所有的反馈非常感谢!
在本系列的下一部分中,我们将构建我们的库,从头开始编写用于分类的线性模型,包括 logistic 和 softmax 回归。
如果您喜欢这里的内容,请关注我!😃
ML 从零开始:逻辑和 Softmax 回归
通过以类似于 Scikit-Learn 的方式从头开始实现 logistic 和 softmax 回归,获得对它们的深入理解

封面照片——卢克·纽曼拍摄
在这个 ML From Scratch 系列中,我们使用面向对象编程创建了一个与 Scikit-Learn 风格相似的机器学习算法库。这意味着您可以 pip 安装该库,并以您已经熟悉的方式使用所有可用的模型。
如果你从未从零开始实现过任何学习算法,我会给你三个好理由,让你现在就跟随并开始:
- 您将详细了解每个模型中的特殊细微差别,加深您的理解。
- 您将通过记录代码和创建测试用例来提高您的编码技能,确保所有功能都按预期工作。
- 您将对面向对象编程有更好的理解,从而改进您的机器学习项目工作流程。
要查看 Github 回购,请访问此处。
这个项目旨在增加我们对学习算法的理解,而不是用于实际的数据科学工作。
装置
为了使用类和函数进行测试,创建一个虚拟环境并 pip 安装项目。
$ pip install mlscratch==0.1.0
要下载本地存储库中的所有源代码,请创建一个虚拟环境,并在您的终端中运行以下命令。
$ git clone https://github.com/lukenew2/mlscratch
$ cd mlscratch
$ python setup.py install
逻辑回归
Logistic 回归是分类中常用的回归算法。它估计一个实例属于一个特定类的概率。
如果估计概率大于或等于 50%,则模型预测该实例属于正类。如果概率小于 50%,则模型预测负类。
估计概率
逻辑回归就像线性回归模型一样工作。它计算特征数组的加权和(加上一个偏差项),但不是像线性回归模型那样直接输出结果,而是输出该结果的逻辑。

等式 1。逻辑回归估计概率
标为σ(*)的逻辑函数是一个 sigmoid 函数,它将其输入转换为 0 到 1 之间的数字。

等式 2。物流功能
一旦模型估计了一个实例属于正类的概率,它就简单地根据以下规则进行预测。

等式 3。逻辑回归模型预测
对于我们从头开始的实现,我们需要创建一个 sigmoid 函数,它可以将我们的输入转换成概率。让我们看看这是如何做到的。
模块 1。activations.py
这里我们创建了一个类,并给了它一个方法。这个特殊的 call 方法让我们的类在被调用时表现得像一个函数。我们将在创建逻辑回归类时使用这个属性。
培训和成本函数
既然我们已经知道了逻辑回归如何估计概率和进行预测的一切,让我们看看它是如何被训练的。
训练的目标是找到一组参数θ,使得模型预测正面实例的高概率和负面实例的低概率。这个想法被成本函数测井损失捕获。

等式 4。逻辑回归成本函数(对数损失)
由于我们将在实现中使用梯度下降,我们需要知道的是成本函数相对于模型参数θ的偏导数。

等式 5。对数损失偏导数
这个方程非常类似于线性回归的成本函数偏导数。对于每个实例,它计算预测误差并将其乘以实例的特征值,然后计算所有实例的平均值。
使用批量梯度下降实现逻辑回归时,我们不想计算所有实例的平均值。我们希望梯度向量包含所有的偏导数,允许我们在它们指向的相反方向更新我们的参数。
让我们进入实现。请阅读文档以理解每段代码的作用。
模块 2。物流. py
我们走吧!我们有一个全功能的逻辑回归模型,可以执行二元分类。让我们编写一个测试用例来确保它正常工作。
测试案例——虹膜二元分类
在这个测试案例中,我们使用著名的 iris 数据集,并将其转换为二分类问题,执行训练/测试分割,进行一些预处理,然后训练我们的逻辑回归模型,并在测试集上测试准确度大于 80%。
模块 3。test_logistic.py

图一。测试在 0.92 秒内通过
现在,无论出于什么原因,如果我们对我们的代码进行了更改,我们总会有一个测试用例来确保我们的更改没有破坏模型。相信我,这会省去很多麻烦,尤其是当我们的代码库增长的时候。
Softmax 回归
我们实现的逻辑回归模型只支持二进制分类,但是可以推广到支持多个类。这被称为 Softmax 回归。
想法很简单:对于每个实例,Softmax 回归模型计算每个类的分数,然后通过将 softmax 函数应用于分数来估计实例属于每个类的概率。

等式 6。Softmax 函数(非标准化)
在这个等式中:
- k 是类的数量。
- s(x)是包含实例 x 的每个类的分数的向量。
就像逻辑回归分类器一样,Softmax 回归分类器预测估计概率最高的类。
实际问题:数值稳定性
从头开始实现 softmax 函数有点棘手。当你划分可能非常大的指数时,你会遇到数值稳定性的问题。为了避免这种情况,我们使用了一个规范化技巧。请注意,如果我们将分数的上下部分乘以常数 C,并将其推入总和,我们会得到以下等价表达式:

方程式 7。Softmax 函数(归一化)
我们可以自由选择 C 的值,但常见的选择是将 log(C)设置为等于实例 x 的最大值的负值。这会移动这些值,因此最大值为零。让我们看看代码:
模块 4。activations.py
这里,我们使用 call 方法将 softmax 类添加到与 sigmoid 类相同的模块中,这样我们的类在被调用时就像一个函数一样。
培训和成本函数
现在让我们来看看训练 Softmax 回归模型及其成本函数。这个想法和逻辑回归是一样的。我们需要一个模型来预测目标类的高概率和其他类的低概率。这个想法被成本函数交叉熵捕获。

等式 8。交叉熵代价函数
在这个等式中:
- y 是实例属于目标类的目标概率。在我们的例子中,这个值总是等于 1 或 0,这取决于实例是否属于这个类。
- 在两个类别的情况下,交叉熵相当于对数损失。
因为我们将使用梯度下降实现 Softmax 回归,所以我们需要这个成本函数关于θ的梯度向量。

等式 9。交叉熵梯度向量
当计算梯度时,我们需要我们的目标类(y)与我们估计的概率(p)具有相同的维数。我们估计的概率将是(n_samples,n_classes)的形式,因为对于每个实例,我们将有一个与属于每个类的实例相关联的概率。
例如,如果我们有三个标记为 0、1 和 2 的类,我们需要将包含([1,2,0])的目标向量转换为如下所示的数组:
([[0, 1, 0],
[0, 0, 1],
[1, 0, 0]])
第一列表示类别 0,第二列表示类别 1,第三列表示类别 2。你可能已经注意到,我们可以通过一键编码我们的目标向量(y)来实现这一点。让我们看看代码:
模块 5。预处理. py
现在我们有了完成 Softmax 回归模型所需的所有帮助函数。代码有很好的文档记录,所以请阅读每个部分是做什么的解释。
模块 6。物流. py
就是这样!我们已经成功地将 Softmax 回归添加到我们的机器学习库中。让我们快速看一下测试用例,以确保它正常工作。
测试案例——虹膜多类分类
我们将使用 iris 数据集,但这次尝试以至少 85%的准确率对所有三个类进行分类。首先,我们加载数据集,对其进行训练/测试拆分,执行标准化,然后训练我们的 Softmax 回归模型并预测测试集。
模块 7。test_logistic.py

图二。测试在 1.83 秒内通过
我们完了!我们的测试用例通过了。我们成功地将 Softmax 回归添加到了我们的机器学习库中。
包裹
ML 从头开始系列的这一部分到此结束。我们现在有一个机器学习库,其中包含用于回归和分类的最常见的线性模型,包括:
- 线性回归
- 多项式回归
- 山脉
- 套索
- 弹性网
- 逻辑回归
- Softmax 回归
一如既往,非常感谢您的阅读和反馈!
在本系列的下一部分,我们将构建我们的库,并创建能够分类和回归的支持向量机。
如果你喜欢这里的内容,请关注我!😃
额外资源
[## ML 从零开始:线性、多项式和正则化回归模型
towardsdatascience.com](/ml-from-scratch-linear-polynomial-and-regularized-regression-models-725672336076)
ML 基础设施工具— ML 可观察性
ML 可观测性系列
第 1 部分:ML 可观测性系列

作者的 ML 可观察性平台图片
几乎每个行业的企业都在采用机器学习(ML)。许多企业都在期待 ML 基础设施平台,以推动他们在业务中利用人工智能的运动。了解各种平台和产品可能是一项挑战。ML 基础架构空间拥挤、混乱且复杂。
为了理解生态系统,我们将机器学习工作流大致分为三个阶段——数据准备、模型构建和生产。了解工作流程每个阶段的目标和挑战有助于做出明智的决策,确定哪些 ML 基础设施平台最适合您的业务需求。

按作者列出的 ML 工作流图像
在我们的上一篇文章中,我们深入研究了 ML 工作流程的数据准备和模型构建部分。我们开始深入生产 ML,并详细讨论了模型验证和模型部署。在本帖中,我们将深入探讨并开始一系列关于 ML 可观测性的讨论。
什么是 ML 可观测性?
作为一个机器学习社区,我们已经在构建模型方面取得了巨大的进步,这些模型使计算机能够完成惊人的智能壮举。现在,这些模型已经在世界上做出决策,我们如何确保这些技术实际上是可行的,我们持续地高质量地交付它们,我们积极地改进它们,并确保研究结果与生产相匹配?大多数数据科学家很快意识到 Jupyter 笔记本不是真实的世界。模型的可观测性是弥合这一差距的关键。模型可观察性是基础平台,它使团队能够从实验室到生产不断交付和改进结果。
在这个博客系列中,我们将讨论模型可观察性的主题。我们将深入研究模型在现实世界中遇到的问题。我们还将看一看有助于理解模型行为的工具。
模型可观测性只是 ML 监控的一个花哨词吗?
模型可观察性始于在诸如培训、验证和生产等环境中收集模型评估的过程,然后通过分析将它们结合在一起,允许人们将这些点连接起来以解决 ML 工程问题。这些推理存储在一个模型评估存储中(这个术语归功于 Josh Tobin ),该存储托管原始推理数据。评估存储保存模型的响应,模型决策的签名,在每个环境中对每个模型版本的每个输入数据的响应。

作者的 ML 可观察性平台图片
ML 可观测性平台允许团队分析模型退化,并找出任何出现问题的根本原因。这种通过连接验证和生产之间的点来诊断模型问题的根本原因的能力是模型可观察性与传统模型监控的区别。虽然模型监控包括针对关键模型性能指标(如准确性或漂移)设置警报,但模型可观察性意味着更高的目标,即深入了解任何性能回归或异常行为。我们对为什么感兴趣。监控只对聚合和警报感兴趣。可观察性感兴趣的是我们可以从模型的预测、可解释性洞察、产品特征数据和训练数据中推断出什么,以理解模型动作背后的原因并构建工作流来改进。

作者的 ML 可观察性图像
由评估库支持的 ML 可观测性;
- 在生产、培训和验证数据集环境之间无缝移动
- 按环境本机支持模型评估分析
- 旨在分析预测的性能方面/部分
- 为故障排除和法规分析而设计的可解释性归因
- 基于基本事实的性能分析——精确度,F1,MAE
- 没有基础事实的代理性能——预测漂移
- 数据集和环境之间的分布漂移分析
- 旨在回答性能变化背后的原因
- 集成验证
- 旨在迭代和改进
为什么 ML 的可观察性很重要
我们的团队曾亲自参与定价、预测、营销、信贷和交付时间估计等模型的开发工作。在所有情况下都有一个共同的故事线;我们会建立一个模型,部署它,它会在一个城市很好地工作,而不是在另一个城市。它在一组社区/客户/产品类型中很有效,而在其他产品中效果很差。这在一般情况下会很好,但在预测的结尾会很可怕。或者它在初始发布时工作得很好,然后模型会慢慢退化。其他时候,您可能会遇到由上游数据管道错误引起的瞬时变化,这将慢慢地损害训练数据。在所有这些案例中,很明显,当团队将模型部署到生产环境中时,ML 基础设施中缺少一个基础部分来帮助模型工作。
在机器学习的另一个常见用途中,如欺诈模型分析,理解绩效变化背后的为什么是极其重要的。在欺诈中,基于不良行为者的敌对行为,行为不断发生变化。这些不良因素会造成性能下降,可能会在监控系统中亮起红灯或绿灯。但是因果关系,“为什么”,通常是阻止欺诈计划的洞察力。
在为每个客户建立一个模型,根据客户特定数据进行训练的公司中,每个模型都接受不同的训练。团队可能会被显示数据/模型漂移的模型淹没,但是很难显示这种漂移是否是一个问题。你如何在规模上解决问题,你的 ML 团队如何随着客户的增长而扩展?团队希望快速了解问题在哪里大规模出现,快速将生产与验证进行比较,并满怀信心地快速解决问题。
没有工具来推理模型在野外犯的错误,团队在数据科学实验室投入了大量资金,但在现实世界中基本上是盲目的。下面的博客将强调团队如何自信地交付模型,持续改进并获得竞争优势。我们将深入探讨 ML 可观察性和 ML 监控。
下一个
我们希望您喜欢 ML 基础设施工具系列。接下来,我们将深入ML 的可观测性。关于人工智能的可操作性,我们将深入探讨许多讨论不足且极其重要的话题!
联系我们
如果这个博客引起了你的注意,并且你渴望了解更多关于机器学习可观察性和模型监控,请查看我们其他的博客和 ML 监控上的资源!如果您有兴趣加入一个有趣的 rockstar 工程团队,帮助模型成功生产,请随时联系我们,并在此处找到我们的空缺职位!
机器学习管道端到端解决方案
ML 实现往往很快变得复杂。本文将解释如何将 ML 系统分割成尽可能少的服务。

作者:安德烈·巴拉诺夫斯基
介绍
在实现了几个 ML 系统并在生产中运行它们之后,我意识到单片 ML 应用程序的维护负担很重。ML 应用程序代码复杂度呈指数增长。数据处理和准备、模型训练、模型服务——这些事情看起来很简单,但事实并非如此,尤其是在进入生产阶段之后。
数据结构在变化,这需要调整数据处理代码。新的数据类型正在出现,这需要保持模型最新并重新训练它。这些变化可能导致模型服务更新。当所有这些作为一个整体运行时,在不破坏其他东西的情况下修复一个东西变得如此困难。
性能是另一个重点。当系统被分成不同的服务时,就有可能在不同的硬件上运行这些服务。例如,我们可以使用 GPU 在 TensorFlow 云上运行培训服务,而数据处理服务可以在本地 CPU VM 上运行。
我做了研究,并检查了哪些选项可用于实施 ML 微服务。有各种各样的解决方案,但大多数对我来说都太复杂了。我决定实现自己的开源产品,它将依靠 Python、 FastAPI 、 RabbitMQ 和 Celery 来实现服务之间的通信。我叫它 Skipper,它在 GitHub 上。该产品正在积极开发中,不过已经可以使用了。
Skipper 的核心思想是为 ML 微服务实现提供一个简单可靠的工作流程,Web API 接口在前端。在接下来的阶段,服务将被打包到 Docker 容器中。我们将为在 Kubernetes 上运行服务提供支持。
解决方案架构

Skipper architecture,作者:Andrej Baranovskij
有两个主要模块——引擎和微服务。发动机本身可以当作微服,但我不叫它微服是有原因的。引擎部分负责提供 Web API 访问,从外部调用。它充当一组微服务的网关。Web API 是用 FastAPI 实现的。
Celery 用于处理通过 Web API 提交的长时间运行的任务。我们用 Celery 启动一个长期运行的异步任务,使用任务 ID 通过另一个端点检索结果。
通用逻辑封装到 Python 库,发布在 PyPI — skipper-lib 上。这个库的想法是允许用 RabbitMQ 任务代理发布/接收通用事件。在 Web API 引擎和微服务中使用相同的库。
微服务块更像是一个例子。它实现了一个样本服务,用于数据处理、模型训练和最终的模型服务。数据处理模型培训服务之间通过 RabbitMQ 队列进行通信。这个想法是,您可以将自己的服务插入到工作流中。
核心元素— RabbitMQ 代理。Skipper 使用 RabbitMQ RPC 调用在服务之间发送消息。没有协调器来管理通信。通信基于服务发送和接收的事件运行。
引擎和 Web API 服务
Web API 是用 FastAPI 实现的。有一个 router.py 脚本,其中实现了端点。
长时间运行的任务,比如模型训练是在异步模式下启动的,使用 Celery 分布式任务队列。我们调用进程 _ 工作流任务,并获取其 ID:
@router_tasks.post('/execute_async',
response_model=WorkflowTask,
status_code=202)
def exec_workflow_task_async(workflow_task_data: WorkflowTaskData):
payload = workflow_task_data.json()
task_id = process_workflow.delay(payload)
return {'task_id': str(task_id),
'task_status': 'Processing'}
任务状态通过另一个端点来检查,我们发送任务 ID 并查询 Celery API 来获得状态:
@router_tasks.get('/workflow/{task_id}',
response_model=WorkflowTaskResult,
status_code=202,
responses={202:
{'model': WorkflowTask,
'description': 'Accepted: Not Ready'}})
async def exec_workflow_task_result(task_id):
task = AsyncResult(task_id)
if not task.ready():
return JSONResponse(status_code=202,
content={'task_id': str(task_id),
'task_status': 'Processing'})
result = task.get()
return {'task_id': task_id,
'task_status': 'Success',
'outcome': str(result)}
应该快速完成的任务(例如,预测任务)会直接执行,而不会启动芹菜任务。事件被发送到 RabbitMQ 代理,结果以同步模式返回:
@router_tasks.post('/execute_sync',
response_model=WorkflowTaskResult,
status_code=202,
responses={202:
{'model': WorkflowTaskCancelled,
'description': 'Accepted: Not Ready'}})
def exec_workflow_task_sync(workflow_task_data: WorkflowTaskData):
payload = workflow_task_data.json()
queue_name = None
if workflow_task_data.task_type == 'serving':
queue_name = 'skipper_serving'
if queue_name is None:
return JSONResponse(status_code=202,
content={'task_id': '-',
'task_status': 'Wrong task type'})
event_producer = EventProducer(username='skipper',
password='welcome1',
host='localhost',
port=5672)
response = json.loads(event_producer.call(queue_name, payload))
return {'task_id': '-',
'task_status': 'Success',
'outcome': str(response)}
芹菜任务在 tasks.py 脚本中实现。它的工作是向 RabbitMQ 提交一个新事件,并等待响应。
Skipper 库
该库有助于封装通用逻辑,而无需重复相同的代码。我用一个叫做诗歌的工具在 PyPI 上建立并发布了一个库。
该库有助于简化与 RabbitMQ 的交流。它实现了事件生产者和接收者。
事件生成器使用 RPC 将任务提交到队列,并等待响应:
def call(self, queue_name, payload):
self.response = None
self.corr_id = str(uuid.uuid4())
self.channel.basic_publish(
exchange='',
routing_key=queue_name,
properties=pika.BasicProperties(
reply_to=self.callback_queue,
correlation_id=self.corr_id
),
body=payload
)
while self.response is None:
self.connection.process_data_events()
return self.response
事件接收器侦听来自 RabbitMQ 队列的消息,调用服务,并返回响应:
def on_request(self, ch, method, props, body):
service_instance = self.service_worker()
response, task_type = service_instance.call(body)
ch.basic_publish(exchange='',
routing_key=props.reply_to,
properties=pika.BasicProperties(
correlation_id=props.correlation_id
),
body=response)
ch.basic_ack(delivery_tag=method.delivery_tag)
print('Processed request:', task_type)
服务
实现的服务的主要目标是提供一个如何使用工作流的例子。如果你使用 skipper-lib 进行事件通信,你可以插入你自己的服务。
所有服务都遵循相同的代码结构。
培训服务
顾名思义,该服务负责运行模型培训。
业务逻辑在 training_service.py 脚本中实现。有一个方法叫做调用。该方法由来自 skipper-lib 的事件接收器自动执行。在此方法中,您应该读取输入数据,调用模型训练逻辑并返回结果:
def call(self, data):
data_json = json.loads(data)
self.run_training(data)
payload = {
'result': 'TASK_COMPLETED'
}
response = json.dumps(payload)
return response, data_json['task_type']
该服务发送事件来请求数据。我们也为此使用了 skipper-lib :
def prepare_datasets(self, data):
event_producer = EventProducer(username='skipper',
password='welcome1',
host='localhost',
port=5672)
response = event_producer.call('skipper_data', data)
要启动服务,运行 main.py 脚本:
from skipper_lib.events.event_receiver import EventReceiver
from app.training_service import TrainingServiceevent_receiver = EventReceiver(username='skipper',
password='welcome1',
host='localhost',
port=5672,
queue_name='skipper_training',
service=TrainingService)
训练服务被配置为监听 skipper_training 队列。
数据服务
该服务接收事件以准备数据并将其返回给调用者。一次返回多个数据集,例如,训练和验证数据、目标值。Numpy 数组被转换成列表并用 json.dump: 序列化
data = [norm_train_x.tolist(),
norm_test_x.tolist(),
norm_val_x.tolist(),
train_y,
test_y,
val_y]
response = json.dumps(data)
培训服务将使用 json.loads 函数反序列化数据,然后它将再次创建 Numpy 数组结构。这样,只对数据服务进行一次调用,所有数据结构都在一次调用中传输。
要启动服务,运行 main.py 脚本:
event_receiver = EventReceiver(username='skipper',
password='welcome1',
host='localhost',
port=5672,
queue_name='skipper_data',
service=DataService)
数据服务被配置为监听 skipper_data 队列。
发球服务
此服务加载由培训服务保存的模型。它加载由数据服务保存的用于数据标准化的数字。我们需要根据用于模型训练的相同统计数据来标准化数据。
服务方法实现:
def call(self, data):
data_json = json.loads(data)
payload = pd.DataFrame(data_json['data'], index=[0, ])
payload.columns = [x.upper() for x in payload.columns]
train_stats = pd.read_csv(
'../models/train_stats.csv',
index_col=0)
x = np.array(self.norm(payload, train_stats))
models = self.get_immediate_subdirectories('../models/')
saved_model = tf.keras.models.load_model(
'../models/' + max(models))
predictions = saved_model.predict(x)
result = {
'price': str(predictions[0][0][0]),
'ptratio': str(predictions[1][0][0])
}
response = json.dumps(result) return response, data_json['task_type']
要启动服务,运行 main.py 脚本:
event_receiver = EventReceiver(username='skipper',
password='welcome1',
host='localhost',
port=5672,
queue_name='skipper_serving',
service=ServingService)
服务服务被配置为监听 skipper_serving 队列。
结论
这篇文章的主要思想是解释如何将 ML 实现分成不同的服务。这将有助于管理复杂性,并提高解决方案的可扩展性。希望您会发现 Skipper 对您自己的实现很有用。尽情享受吧!
源代码
- GitHub 回购。按照自述文件获取设置说明
YouTube——观看演示
Scikit-Learn 中带网格搜索的 ML 管道
ML 管道是 Scikit-Learn 和 Spark MLlib 提供的一个重要特性。它将数据预处理、特征工程和 ML 模型统一在同一个框架下。这种抽象极大地提高了任何 ML 项目的可维护性,如果您真的想将您的模型投入生产,那么应该考虑这种抽象。
让我们先概述一下使用 ML 管道的一些主要好处。
- 用于数据转换和建模的统一 API
- 在模型服务期间不需要额外的预处理步骤
- 联合网格搜索可能包括数据预处理步骤/参数和模型选择/参数
- 兼容 Spark 等大数据框架
对一些人来说,最后一点可能不是很明显。想象一下,如果有一天你的模型需要对大量数据进行训练。您可能没有更好的选择,只能使用 Spark 这样的分布式框架。使用 ML 管道将使迁移到 Spark 变得轻而易举。一个原因是Spark MLlib有类似的 ML 管道特性。另一个更根本的原因是函数式编程。
ML pipeline 与函数式编程(Spark 也紧随其后)有许多相似之处,因为它将转换和估算器(模型)视为数据上的操作符。数据保持不变性(FP 的核心理念),因为它们只是通过管道的不同组件,在这个过程中被转换成新的格式。在管道的末端,我们得到一些预测结果。

示例管道(图片由作者提供,使用 scikit-learn 生成)
在示例管道中,我们有一个preprocessor步骤,它属于ColumnTransformer类型,包含两个子管道:
num_transform是用于数字列的子管道,它填充空值并将列转换为标准分布cat_transform是另一个用于分类列的子管道,它执行一键编码
在preprocessor转换器之后,我们有一个estimator,在本例中是默认的逻辑回归器。
通过这种管道,人们可以将数据预处理与建模结合在一起,甚至通过创建定制的转换器来包括更复杂的特征工程。
要创建自己的转换器,你需要创建一个从sklearn.base.BaseEstimator和sklearn.base.TransformerMixin继承的类,并实现fit和transform方法。
如何创建管道?
让我们来看一个正在运行的例子(完整的代码可以在 Github 上找到),它包含了泰坦尼克号上乘客的特征和标签,这些标签表明其中一人是否幸存。
我们想要建立一个分类器来预测一个给定的乘客是否会幸存。
from sklearn.datasets import fetch_openml
import numpy as npnp.random.seed(0)# Load data from [https://www.openml.org/d/40945](https://www.openml.org/d/40945)
X, y = fetch_openml("titanic", version=1, as_frame=True, return_X_y=True)
我们将考虑以下 4 个特征,其中age和fare是数字,而sex和pclass是分类的(注意:也可以将pclass视为数字,但这与这里的讨论无关)。

现在,让我们构建管道来进行前面提到的一些数据预处理。
numeric_features = ["age", "fare"]
numeric_transformer = Pipeline(steps=[
("imputer", SimpleImputer(strategy="mean")),
("scaler", StandardScaler())
])categorical_features = ["sex", "pclass"]
categorical_transformer = OneHotEncoder(handle_unknown="ignore")preprocessor = ColumnTransformer(transformers=[
("num_transform", numeric_transformer, numeric_features),
("cat_transform", categorical_transformer, categorical_features)
])pipeline = Pipeline(steps=[("preprocesser", preprocessor), ("classifier", LogisticRegression())])
pipeline
或者,有一个稍微紧凑一点的 API make_pipeline和make_column_transformer,其中我们不需要显式地传递每个步骤的名称。然而,下面的部分将基于之前的详细版本,因为我们喜欢按照我们喜欢的方式命名事物:)。
from sklearn.compose import make_column_transformer
from sklearn.pipeline import make_pipelinenumeric_transformer = make_pipeline(
SimpleImputer(strategy='mean'),
StandardScaler())categorical_transformer = make_pipeline(OneHotEncoder(handle_unknown='ignore'))preprocessor = make_column_transformer(
(numeric_transformer, numeric_features),
(categorical_transformer, categorical_features))pipeline = make_pipeline(preprocessor, LogisticRegression())
pipeline
一个方便的提示,通过包含下面的代码,可以在 Jupyter notebook 中可视化管道。它创建了一个很好的图表,如前所示。
from sklearn import set_config
set_config(display='diagram')
pipeline
如何培养一个模特?
训练一个模型和训练一个纯粹的评估者没有什么不同。你就叫fit。下面我们首先做一个标准的列车测试数据分割来测试模型性能。然后,你就在你的训练数据上调用pipeline.fit(),就这样。
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
pipeline.fit(X_train, y_train)
score = pipeline.score(X_test, y_test)
print(score)
0.7977099236641222
实际情况是,训练数据经过管道中定义的步骤(包括分类器估计器),就好像它是一个模型一样。
如何保存和加载模型?
在这一点上,有人可能想知道我是否也可以保存我的流水线模型,就像我过去保存独立模型一样?答案是肯定的。
将数据处理和建模放在一个保存的模型中还有一个好处。现在你不需要考虑在预测时间采取什么样的预处理步骤,它们已经是模型不可分割的一部分。这可以防止生产中出现奇怪的预测结果。
import joblib
joblib.dump(pipeline, 'your_pipeline.pkl')joblib.load('your_pipeline.pkl').score(X_test, y_test)
0.7977099236641222
我们使用joblib来保存和加载管道,并且它产生相同的分数,这验证了正确性,并且我们确实看到不需要预处理,就像承诺的那样。
如何在管道上进行网格搜索?
ML 管道使我们不仅能够搜索估计器的参数,而且能够搜索所有预处理步骤的参数。换句话说,我们现在可以一起调优 ML 模型和预处理选项。
出于论证的目的,比方说,我们不确定通过mean或median策略填充空值是否是更好的选择,我们也不确定LogisticRegressor需要多少正则化。因此,我们想找出最佳缺失值填充策略和最佳正则化权重。为此,我们可以按如下方式构建参数网格:
param_grid = [
{
"preprocesser__num_transform__imputer__strategy": ["mean", "median"],
"classifier__C": [0.1, 1.0, 10.0, 100.0],
"classifier": [LogisticRegression()]
}
]
注意: sklearn 使用双下划线作为分隔符。classifier key 与流水线定义中估计器的流水线名称相同,classifier__C基本上告诉网格搜索器我们想要尝试为C提供的这些值,T5 是分类器LogisticRegressor定义正则化权重的参数。
您可能会注意到,您还可以连接双下划线语法。preprocesser__num_transform__imputer_strategy为imputer步骤的strategy参数定义不同的试验值。
最后,我们可以开始网格搜索,因为我们有 2 个值用于strategy和 4 个值用于C,在搜索空间中总共有2*4=8个候选 to。
grid_search = GridSearchCV(model, param_grid, cv=10, verbose=1,n_jobs=-1)
grid_search.fit(X_train, y_train)
输出如下所示,因为我们对每个组合进行了 10 次交叉验证,所以我们总共需要拟合 80 个模型!人们可以看到网格搜索是多么容易爆炸。
Fitting 10 folds for each of 8 candidates, totalling 80 fits[Parallel(n_jobs=-1)]: Using backend LokyBackend with 4 concurrent workers.
[Parallel(n_jobs=-1)]: Done 80 out of 80 | elapsed: 4.5s finished
如果我们想尝试不同的分类器呢?我们可以构建更复杂的参数网格来实现这一点。
param_grid = [
{
"preprocesser__num_transform__imputer__strategy": ["mean", "median"],
"classifier__C": [0.1, 1.0, 10.0, 100.0],
"classifier": [LogisticRegression()]
},
{
"preprocesser__num_transform__imputer__strategy": ["mean", "median"],
"classifier__n_estimators": [10, 100, 1000],
"classifier": [RandomForestClassifier()]
}
]
grid_search = GridSearchCV(model, param_grid, cv=10, verbose=1,n_jobs=-1)
grid_search.fit(X_train, y_train)
在上面的代码中,我们使用参数网格中的两个字典来添加一组额外的搜索选项,它使用随机森林分类器而不是逻辑回归器。
有人可能会问,现在的组合数是多少?由于每个字典都定义了不同的集合,我们应该跨字典累加,在字典内相乘。这给了我们2*4*1 + 2*3*1 = 14个候选人。
这在输出中得到验证:
Fitting 10 folds for each of 14 candidates, totalling 140 fits[Parallel(n_jobs=-1)]: Using backend LokyBackend with 4 concurrent workers.
[Parallel(n_jobs=-1)]: Done 76 tasks | elapsed: 1.5s
[Parallel(n_jobs=-1)]: Done 140 out of 140 | elapsed: 26.6s finished
摘要
我们已经通过一个运行示例讨论了使用 ML 管道的主要好处。
我们也看到了管道概念是多么方便,同时又是多么强大。在我们看到进行联合网格搜索是多么容易之后,后一点就变得很明显了。
最后但同样重要的是,看到Spark MLlib与scikit-learn中的 ML pipeline 具有几乎相同的特性并不令人惊讶,感兴趣的读者可以查看:【https://spark.apache.org/docs/latest/ml-pipeline.html】,或者我可能会就此写另一篇博客。
笔记
- GitHub 上可用的代码:https://GitHub . com/wlongxiang/ml pipeline/blob/main/ml _ pipeline _ with _ grid _ search . ipynb
- 制作自己的变形金刚需要阅读的一些资料:https://sk learn-template . readthedocs . io/en/latest/user _ guide . html
ML 产品管理有一个翻译问题
行业笔记
Data/ML 产品中常见的翻译问题
构建 ML 产品是困难的。
作为一名数据科学家和一名产品经理,我经历了这个问题两方面的成长烦恼。

你如何确定你的人工智能项目成功了?照片由在 Unsplash 上拍摄
经常被引用的数字(在 ML/MLOps 推销中如此常见,这是一个陈词滥调)是 85%的人工智能项目未能给企业带来预期的结果。
斯隆指出,即使有正确的数据、人才和公司战略,也只有 20%的公司从人工智能计划中获得财务投资回报。
尽管成功率如此之低,人工智能/人工智能项目不会有任何进展——91.5%的公司都在持续投资人工智能。
ML 项目失败的原因很多,从最初的数据问题到监控不力。在这个系列中,我将强调我所目睹的困扰 ML 产品的三大问题。尽情享受吧!
翻译问题:业务需求和数据科学解决方案之间往往存在明显的脱节
野外翻译问题:
这里有两个夸张但令人惊讶的常见场景,涉及公司试图/未能启动 ML 计划。希望他们不要离家太近:
1.“让搜索变得更好”
Pria 是一名产品经理,她花了一个月左右的时间采访客户,寻找改善公司移动应用体验的方法。在筛选了她所有的对话后,她意识到人们不喜欢搜索功能,因为他们找不到新的有趣的内容来观看。
Pria 注意到的一个常见行为是,客户会访问搜索,对结果不满意,然后关闭应用程序。事实上,有些人甚至因为体验太差而转用了竞争对手的 app。

Pria 希望在她的产品中复制谷歌的成功……照片由 Unsplash 上的 Charles Deluvio 拍摄
Pria 灵光一现:也许新的数据科学家能够让搜索变得更好!
Pria 很快安排了一次与数据科学团队的会议,并传达了她的想法——“使用数据科学和机器学习来让搜索变得更好”。Pria 没有技术背景,不知道确切地DS/ML 能帮什么忙,但是假设他们有一些锦囊妙计。
数据科学家 Dave 很高兴收到一个基于客户的问题!接下来的几周,他阅读了一些关于推荐系统的新论文,实现了一些看起来很有前途的论文,并进行了离线测试。他使用 top5 推荐作为他选择的衡量标准,并发现尖端的深度学习模型表现最好。几次冲刺后,他向普里阿…
…很快发现这不是 Pria 想要的。
在接下来的 A/B 测试中,对比了 Dave 的模型和当前系统,客户确实更多地点击了 Dave 模型的推荐,但下降速度几乎相同。
这让 Dave 很兴奋,因为他的推荐算法在点击率方面明显优于现状,而 Pria 很失望,因为它没有解决保留的实际潜在问题。
这是一个假设的场景,但 Dave 的模型在点击率方面可能更强,但在保留率方面会更低,因为它只推荐最受欢迎的内容,所以一般人会感兴趣并点击,但那些试图找到特定内容的人可能会发现这些推荐令人沮丧
这看起来像是一个沟通不畅的问题(确实如此),但产品/业务计划与最终数据科学产出不一致的普遍现象清楚地表明,这里还存在更多问题。
业务目标和需求经常被误译为数据科学指标。
2.数据驱动与客户驱动
上述错位不一定来自产品到数据科学的管道;它也可能来自热情(但幼稚)的战略领导,试图将 ML 洒入每一项风险投资。如果你听到这个问题,
"利用现有的数据,我们可以做什么样的 ML 项目?"

更好的想法可能潜藏在您的数据中…但是您如何才能发现正确的想法呢?弗兰基·查马基在 Unsplash 上拍摄的照片
你的团队潜伏在危险的错位区域。
有无数 ML 相关的项目,一个数据科学家可以用一个健康的数据集炮制…但是这些项目中有多少可以被依赖来交付 ROI 呢?这些项目中有多少能够对当前的客户问题产生直接和积极的影响?
比你想象的要少。
没有清晰结构或与特定客户问题无关的数据科学项目通常无法交付商业价值。
定义问题
问题陈述:作为一个产品团队,我们希望确保团队中的每个人都在同一页上,这样我们就可以快速行动,正确地构建我们的产品。然而,在业务语言和数据语言之间经常会有一个脱节。我们需要一起决定“我们探索&试图解决的到底是什么问题?”和“我们能量化成功的标准吗?”
- 这在任何地方都没有记录,并在数据科学方面造成巨大的痛苦,因为 DS 经常不得不做出巨大的跳跃和假设才能进入业务
- 这可能是不言而喻的,因为 JIRA 正在打什么牌
可扩展解决方案
解决翻译问题最重要的部分是承认它,并为此做好准备。
以下是过去对我有效的两个应用策略。不要认为这些是唯一的前进道路,而是适合您团队当前产品发现过程的建议。
1.摆桌子
摆桌子只是为了确保一开始就达成完全一致。这看起来是显而易见的,但是拥有一份清晰的书面文件,概述以下关键问题的答案,并且得到技术和产品利益相关者的认同将极大地增加你实现价值的机会。

团队合作让梦想成真!照片由 krakenimages 在 Unsplash 上拍摄
需要一起回答的几个核心问题是:
我们正在解决的客户问题是什么?将这一点放在文档的顶部,可以确保数据团队做出的所有假设/决策都是为了解决这一特定问题,而不是解决其他可能存在的过多问题。
我们如何量化一个离线实验的成功?量化产品或业务需求的成功是一项重要的任务。老实说,这是一个强大的数据科学家的核心技能。但是正确地做到这一点,并让每个人都同意这一点,是推动 ML 产品价值的基础。
我们如何量化在线实验的成功?同上,但这是我们在 A/B 测试中寻找的特定内容——我们希望我们的系统结果是什么?某些指标,比如客户保持率,是无法在离线实验中捕捉到的。
是否有任何数据或模型阻碍来解决这一客户问题?该部分预计将在整个发现过程中经常更新,而不仅仅是在开始阶段。数据科学是一个迭代的过程。数据团队会遇到数据问题,这是不可避免的。有一个记录它们的地方可以解放数据和产品团队,因为它使延迟的原因可以接受,从失败中吸取的教训清晰明了。(在以后的博客中会有更多关于这个的内容!)
所有这些都可以打包成这样的东西:

Figma 模仿一个“摆桌子”的文档。作者制作
这不一定要在会议中完成,但这肯定是一种协作体验。
虽然当产品经理知道数据科学如何帮助时,这种结构是有帮助的(如场景#1),但我们可以扩展这种结构,以便在我们希望获得数据驱动的解决方案时也提供支持(如场景#2)。
这可以简单地为数据科学团队提供一份排名列表(excel 表格或其他形式),列出所有已确定的客户问题,以及您希望如何解决这些问题。这为数据团队提供了一个清晰的基础来开始他们的数据探索和概念验证,并确保所有对话都围绕客户问题展开。
您甚至可以创建一个空间,让人们在客户问题下添加评论、阻止程序和 POC 建议,从而更深入地阐明这些问题:

Figma 嘲笑如何解决数据驱动的 ML 发现。作者制作
2.婴儿学步
这似乎是一个很小的改变,但却是至关重要的: ML 不应该是你测试的第一个版本,即使 ML 绝对是你需要的技术。
ML 是一个强大的工具,但是伴随这种强大而来的是复杂性。尽可能长时间地保持简单。

绿野仙踪实验是验证 ML 是否适合您的客户问题的快速简便的方法。在 Unsplash 上由 Austin Distel 拍摄的照片
作为一名产品经理:试试《绿野仙踪》的 版本 ,由真人提供答案,看看你的客户是否会做出更加个性化/策划化的回应。如果他们这样做了,那就指望 ML 扩大你的绿野仙踪实验。如果 WOZ 不在考虑范围内,看看基于规则的方法是否足够,或者至少看看它有多有效。
作为一名数据科学家:始终以虚拟模型作为基线开始,并确保产品团队同意离线实验验证框架。对你和你的团队来说,将一个准确性分数与一个简单易懂的模型进行比较是非常有帮助的——如果你的模型有 89%的测试准确性,每个人都可以理解这是一个令人印象深刻的提高,超过了一个达到 51%准确性的假人……但没有达到 87%准确性的假人那么令人印象深刻。
作为一名数据科学家,这是迄今为止最难接受的建议:不要使用花哨的深度学习,尖端的 ML 模型,除非你没有其他选择。它们可能并不令人兴奋,但是回归模型会让你在几乎所有的商业问题上达到 80%的正确率。只有在你在产品中完成了几次成功的迭代之后,你才应该考虑更深层次的架构。
结论
如果你正在经历这种痛苦,作为一个项目经理或副主任,我深表同情,并为你的挣扎感到抱歉。作为一个老项目的 ML 领导,这种痛苦如此严重,我甚至考虑过构建软件来最好地处理这个问题(因此本文前面的 Figma 截图)
我意识到这不是一个技术问题,而是一个组织+流程问题,用一项新技术来解决这个问题非常具有挑战性。
你的公司需要的是有人(读作: YOU )对翻译问题采取内部立场,并开始让你的产品开发团队熟悉更新的流程。
否则,你的团队将和其他 85%的公司一样,努力从 ML 中获取价值。

亚历山大·辛恩在 Unsplash 上的照片
希望这是信息和有帮助的!
MLDrops —优化一个评估指标并满足所有其他指标

常见场景
在项目中应用特定的技术来实现预期的目标,但是没有获得令人满意的结果。技术团队在收到某个需求后,进行分析,得出以下结论:这是一个机器学习项目的好案例。
项目批准后,考虑到必要的时间和投资,提出要求。这是一个非常常见,但绝不是积极的,可能发生的情况。该调查构成了一个类似于以下内容的列表:
- 该模型应该达到可能的最佳结果;
- 模型应该具有尽可能最短的推理执行时间;
- 该模型应该具有尽可能小的内存占用。
有了这些定义,团队开始工作。我问读者:无论工作组多么有经验,你认为在从事一个有这样需求的项目时能取得什么样的结果?
共同结果
一次又一次的迭代,需求似乎并没有达到。该模型是准确的,但是不考虑执行时间。当针对所允许的最大时间进行优化时,它最终会消耗比最优值更多的内存,或者具有明显更低的准确性。在极少数情况下,所有的需求都有一个“ok”的性能,团队将会分裂以优化各种度量,并且上述情况会一次又一次地发生。
注意:一步一个脚印!
在定义我们的评估指标时,我们必须使用两个重要的概念:优化指标和满意指标。
优化度量在项目中必须是唯一的,并且模型改进的开发迭代必须将它作为改进的主要焦点。
令人满意的度量标准是我们应该用作指南的门槛,但是我们不会持续地工作来优化它们。我们使用这些指标作为不得超过的阈值。
在项目构思和每个迭代周期的开始,定义哪些度量是最佳的,哪些是令人满意的
非常重要的是,当开始项目或项目的新迭代以获得更好的结果时,团队有一个清晰的愿景,并且可以很容易地区分不同类型的度量标准。定义度量标准的工作与新项目的预期结果直接相关,或者类似地与先前生成的模型的结果分析中提出的问题相关。
有了这个定义,团队将会更加敏捷,将会实现更加符合期望的结果,并且也将会有一个清晰的路径来发展模型,并且,因此,相对于已建立的目标增加自信。
我希望你喜欢阅读这篇文章。
如果你喜欢这种快速、开门见山、机器学习的内容,可以考虑关注我的 Twitter 。
谢谢你的时间。保重,继续编码!
MLflow:跟踪模型的更好方法
在一个完美的世界里,你会得到干净的数据,你将输入到一个单一的机器学习模型,瞧,完成了。但幸运的是,现实比完美的世界有趣得多:一个性能良好的 ML 模型只有在一系列试验之后才能准备好。你将不得不尝试不同的算法以及一堆超参数、不同的训练数据变量,甚至可能是目标变量的不同变量。
这种复杂的混合和匹配使挑战变得有趣,但也是一个棘手的问题。解决方案?并非如此剧透警报:MLflow 来拯救
MLflow 是一个帮助我们管理机器学习生命周期的平台。它有四个不同的模块:跟踪、项目、模型和注册表。
在本文中,我们将从跟踪模块开始,您可以使用它来跟踪您的不同模型。
本教程源代码推送到 GitHub:
https://github.com/Deena-Gergis/mlflow_tracking
3 分钟后开始:
那么,你如何用三个步骤来管理不同的试验呢?
使用“pip install mlflow”或“conda install-c conda-forge Mlflow”在您的系统上安装 ml flow 后,继续执行这三个简单的步骤:
1.训练你的模型
我们的玩具模型将是非常简单的逻辑回归,之前有标准缩放和 PCA。我们将尝试 PCA 的方差截止值,看看哪一个是最好的。
2.跟踪您的模型:
为你的项目建立一个新的实验。对于每个实验,您可以跟踪多个模型,每个模型称为一次运行。
对于每次运行,您可以跟踪以下内容:
- 参数 :您为您的模型设置了哪些参数?例如,你的树的深度,你的学习率,估计数,等等。对于我们的玩具模型,我们将跟踪 PCA 的截断方差作为我们的参数。
- 指标 :你的模型执行的如何?您可以自由选择您选择的任何指标。您还可以跟踪多个指标。对于我们的玩具模型,我们将跟踪 MSE 作为我们的度量。
- 工件 :可以是任何可以导出到文件的文件。例如:Pickle 文件用于模型,CSV 文件用于数据,甚至 JPEGs 文件用于绘图。对于我们的玩具模型,我们将把 sklearn 管道存储为一个 pickle 文件,并将其作为一个工件进行跟踪
现在,更改每个新运行的 PCA 方差,MLflow 将处理其余的。
3.检索最佳:
在训练和跟踪您的不同模型之后,是时候看看您有什么,并为进一步的工作检索它们了。只需获得实验的 ID 并查询运行。

请注意,每次运行都被分配了一个唯一的标识符,您可以保存该标识符并在下次运行时使用。我们还可以自动选择 MSE 最小的模型,并将其加载回去。

恭喜你,你现在正式有资格解开跟踪混乱,如果你的模型!
那么幕后发生了什么呢?
MLflow 的跟踪机制其实相当简单: YAML +目录结构。
你会发现创建了一个名为` mlruns '的新目录,其中包含每个实验的子目录。每个实验还将包含一个带有配置的 YAML 文件和子目录,每个子目录包含一次运行。在运行的子目录中,您将找到另一个 YAML 文件,以及其他子目录,在这些子目录中可以跟踪参数、度量和工件。
其他后端:
尽管我们一直使用的本地存储后端的设置和使用非常简单,但它在多用户和功能性方面也是有限的。这就是为什么 MLflow 还为后端提供了多种选择,以防您决定在更大范围内采用该平台。MLflow 还支持分布式架构,其中跟踪服务器、后端存储和工件存储驻留在远程主机上。这里是他们奇特的图形用户界面的一个预览图,你也可以得到。

还有,快乐追踪!
MLflow:初级读本
为什么/如何将内部 ML 框架转变为统一的框架
一行摘要:
通过遵循这篇博文中的具体步骤,您将能够简单地将您的内部 ML 项目纳入 MLflow 框架。
1.MLflow 有什么用?
- 促进 ML 模型版本化,以简化生产中 ML 模型的性能调整和再培训工作。
- 显著增强数据科学/ML 团队跟踪所有 ML 模型库的能力。
- 将整个数据清理、处理、培训和注册生命周期统一到一个统一的框架中。
2.介绍
如果你曾经参与过生产级的基于人工智能的项目,你可能在处理 ML 模型和将更新的模型集成回 ML 管道的过程中经历过一定程度的脱节。为所有 ML 模型的不同版本建立一个统一的存储库,并跟踪模型度量的发展,这将是非常好的。此外,如果需要更新模型,只需点击一次即可在存储库中记录和注册新模型,随时可用。
我观察到,第一手的情况是,诸如 GitHub 之类的代码版本控制工具的出现如何在团队成员之间的代码开发过程中革新了协作的质量。MLflow 通过在一个地方维护 ML 模型的版本以及分数和调整参数,对机器学习模型做同样的事情。
在这篇博文中,我将展示如何将任何 ML 代码基础转换成 MLflow 框架的步骤。图 1 显示了这个案例研究的 MLflow UI。从图 1 中可以看出,这里显示了模型的版本以及调优参数和模型度量。因此,每当团队成员更新模型时,都会添加一个新行,并与指标一起显示。现在,说了这么多,让我们更深入地了解达到这一水平所需的步骤。

图一。ML 模型不同版本的 MLflow UI 视图。在每次再训练中,模型都要经历整个 ML 循环。这个用例将在下一节解释(图片由作者提供)。
3.一个测试用例项目的逐步配方
构建 MLflow 框架的一般路线图如下所示:
- 安装 MLflow 软件包。
- 重构 ML 代码库。
- ML 再培训。
- 通过 MLflow UI 跟踪 ML 版本。
下面,我详细解释每一个步骤。另外,在这里可以看到完整的代码库: GitHub
3.a 安装 MLflow 包
MLflow 是一个独立的包,受到许多云平台的支持,包括 Spark、GCP、Azure 等。它也可以在内部安装和使用。要安装该软件包,只需在您的终端(以及您的活动虚拟环境,如果您正在使用的话)中运行以下命令:
3.b 重构 ML 代码库
为了这篇博文的目的,我选择了我之前的一个分类器模型,它已经在另一篇博文(链接)中描述过了。总之,该模型使用自然语言处理(NLP)将 subreddit 帖子分类为二进制主题类。要深入阅读,我建议你参考上面的博文。
这个模型的一个特别有趣的方面是数据集(即 reddit 帖子)是实时更新的。这意味着随着数据集的发展,由于数据漂移等原因,模型可能需要更新。许多生产 ML 模型苦于从过时的数据集进行推断,因此往往表现不佳。
要使用 MLflow,最关键的一步是重构你的 ML 代码库。我准备了一个通用 ML 代码库的模板,以及如何将它包装成一个单独的模块,如下图和代码片段 1 所示:
- 导入数据。
- 清理数据集。
- 转换特征。
- ML 构建和评分。
- 在 MLflow 上注册模型、参数和指标。
代码片段 1。使用 MLflow 的通用代码结构。
在每一次再训练运行期间,都会为 ML 模型的新版本生成一个路径,其中包含了您的模型的度量、参数和 pickled 版本(当然这是一个简化的版本,您可以参考 MLflow 文档来了解更多的复杂情况)。

图二。在每次 MLflow 运行期间重新训练的模型的生成特征。这些特性在 MLflow UI 上的可视化过程中使用(见图 1)。
3.c ML 再培训
为了重新训练和注册您的模型,如果您已经适当地重构了您的 ML 生命周期(参见上面的代码片段 1),所需要做的只是简单地重新运行最终的函数。在每次重新训练运行期间,都会创建一个新文件夹,其结构如图 2 所示。
3d 通过 MLflow UI 跟踪 ML 版本
要激活 mlflow ui,请在当前文件夹中的“终端”中运行以下命令(或者您可以在笔记本中将其作为神奇命令运行):
并通过以下路径访问浏览器中的用户界面(或根据您的端口号而有所不同):
您将在类似于图 1 的 UI 中看到所有的 ML 版本。好了,你的模型已经在 MLflow 上注册了!
4.建议
- 如果你已经非常熟悉 ML 模型训练的步骤,MLflow 有一个非常浅的学习曲线(对于轻量级的使用)。
- MLFlow 目前集成并兼容多个云平台,包括 Spark、GCP、Azure 等。因此,这将是一个很好的候选杠杆 ML 项目在生产,在规模。
快乐学习!
感谢你阅读我的博文。对更多阅读感兴趣?请查看我的 LinkedIn 和 GitHub 简介。
MLflow 第 4 部分:与服务器客户端交互!

MLflow101
使用 MLflow 服务器客户端以编程方式与 MLflow 中的内容进行交互
大家好!好久不见,对吧?我在这个系列中的上一篇文章是在 2020 年 10 月发表的,巧合的是,这是一大堆新视频游戏推出的时间。因为我是在空闲时间写这些帖子的,我显然是在用我的空闲时间做其他事情。😂
这将是我们 MLflow 101 系列的第四篇,也可能是最后一篇。这是一个伟大的旅程,但所有美好的事情都要结束。如果你想了解以前的帖子,请点击以下链接查看:
如果你想跟随这篇文章中的代码,你可以在我的个人 GitHub 这里找到它。
简要回顾一下上一篇文章,我们最终将第一个模型登录到部署在 Kubernetes (Minikube)上的 MLflow 跟踪服务器中。我有意将模型记录为三个不同的版本,如果您在 MLflow 服务器界面中打开它们,您可能会记得看到以下内容:

作者捕获的 MLflow 屏幕截图
作为另一个快速复习,下图显示了我们如何部署该服务器的架构。简而言之,我们有一个 Postgres 实例来存放关于模型的元数据,我们有一个 Minio 服务器来存放 AWS S3 式接口中的模型工件。记住,Minio 是有意模仿 S3 的。这将是很重要的,在文章的后面要记住。

MLflow 架构图形;由作者创建
好了,现在让我们开始一些新的东西吧!虽然与 MLflow UI 的交互很棒,但为了一致性,如果我们能够以编程方式与服务器客户端进行交互,那就更好了。使用 Python 跟踪客户端,您可以做很多很酷的事情,并且您可以通过 MLflow 的文档了解所有关于它们的内容。在这篇文章中,我们将只讨论几件事。
首先,我们需要连接到客户端本身。为此,您需要回忆一下在本系列的第 2 部分中我们为跟踪服务器创建的 URI。在我的例子中,我的客户端的 URI 是[http://mlflow-server.local](http://mlflow-server.local.).用 Python 实例化 MLflow 客户端实际上非常简单。语法看起来是这样的:
# Importing our required libraries
import mlflow
import mlflow.sklearn
import pandas as pd
import os# Setting the MLflow client
client = mlflow.tracking.MlflowClient(tracking_uri = '[http://mlflow-server.local'](http://mlflow-server.local'))
在继续之前,我们必须对一些环境变量做另外一件特殊的事情,因为我们使用的是 Minio 工件存储。因为 Minio 模拟 AWS 和 S3,如果我们合法地使用 AWS,它将寻找与 AWS 相同的环境变量。您可以从命令行导出这些环境变量,或者如果您想在 Python 代码中直接导出,您所要做的就是:
# Setting the requried environment variables
os.environ['MLFLOW_S3_ENDPOINT_URL'] = '[http://mlflow-minio.local/'](http://mlflow-minio.local/')
os.environ['AWS_ACCESS_KEY_ID'] = 'minio'
os.environ['AWS_SECRET_ACCESS_KEY'] = 'minio123'
客户端实例化:检查!环境变量:检查!我们准备好了,伙计们!让我们从一些简单的东西开始。说实话,我不知道您希望在什么真实情况下执行下面的命令,但是知道它们在那里是很好的。第一个是迄今为止我们已经创建的所有模型实验的一个非常简单的列表,第二个显示了一个给定名称的特定模型的所有版本。下面是语法,下面显示了该语法的输出:
# Listing the MLflow experiments
client.list_experiments()# Getting the model versions for the wine-pyfile-model
results = client.search_model_versions("name='wine-pyfile-model'")for res in results:
print(f'name={res.name}; version={res.version}')

作者捕获的屏幕截图
对于client.list_experiments()函数,您可以看到我们有一个实验和关于该实验的其他细节,包括工件在“S3”(又名 Minio)中的存储位置。在下一个函数中,我们使用“wine-pyfile-model”名称进行搜索(正如我们在第 3 部分中完成的那样),它显示我在 MLflow 中有该模型的三个不同版本。
“好吧,”你可能会想,“我们什么时候去做一些真正有用的事情?”下面是我认为 MLflow 的客户端交互最有潜力的地方:模型部署。现在,对于那些熟悉 MLflow 的人来说,您会认识到这不是使用 MLflow 部署模型的推荐方式。MLflow 实际上有另一个推荐的部署模型的方法,但是说实话……我对此不感兴趣。在我看来,我觉得太死板了。它遵循一种非常精确的语法和打包方式,我只是发现它太麻烦了,没有用。
与 MLflow 鼓励他们的方式不同,我将使用我们一直在使用的同一个 MLflow 客户端向您展示我的首选路线。在这个模式中,我们可以使用 MLflow 客户端在“S3”(Minio)中找到模型工件的精确位置,并直接加载模型以在 Python 文件中使用。在本例中,我将获取模型的版本 2,只是为了说明我们可以获取我们喜欢的任何版本。下面是使用客户端查找和加载模型的语法:
# Getting the URI for version 2 of the Wine model
uri = (client.get_model_version_download_uri(name = 'wine-pyfile-model', version='2')) + '/'
print(uri)# Loading the model using the URI above
model = mlflow.sklearn.load_model(model_uri = uri)
就是这样!现在,您已经将您的模型加载到内存中,从现在开始,您可以使用它进行推理。我不打算在这篇文章中展示它,但我已经成功地为 Kubernetes 部署了一个 Flask API,它直接从 MLflow 预先加载模型,并为所有传入的推理请求将它保存在内存中。
作为向您证明这一点的简单方法,我将根据我们最初训练该模型的相同训练数据来运行该模型。在下面的屏幕截图中,您可以看到我已经加载了数据集,删除了预测值,并在模型中运行了剩余的列。它像做梦一样产生推理输出!

作者截图
从理论上讲,如果您有所有适当的安全和网络措施,这意味着您可以有效地将您的模型部署到任何平台。例如,如果我在 Kubernetes 本地训练我的所有模型,并希望在 AWS、GCP 或 Azure 中使用它们,我需要做的就是调用 Kubernetes 并动态加载该模型。再见,供应商锁定!
这就是 MLflow 系列的全部内容!再次道歉,这篇文章被超级延迟。我希望你在这个系列中发现了很多价值,我自己也通过写这些帖子学到了很多。我还没有决定我的下一个系列的帖子会是什么,所以如果你想看到一些内容,请联系我并提出你的意见。再次感谢阅读!
MLOps 101:致力于 AI 和 DevOps 之间的融合
对每个人都在谈论的闪亮新词的快速概述

凯文·Ku 在 Unsplash 上的照片
是的,所以:这是正式的趋势。IT 界闪亮的新品牌流行语。在谷歌趋势中快速搜索显示,这个词正在缓慢但肯定地获得关注。吴恩达本人,人工智能世界中最受尊敬的名字之一,刚刚放弃了一个专注于此的新专业。
但是…什么是 MLOps?让我们做一个快速的概述。

Google 中 MLOps 搜索的趋势
什么是 MLOps?
我会保持简单。引用 AWS re:invent 2020 事件,MLOps 是:
将实验机器学习模型引入生产系统的过程。
这和你可以在维基百科中找到的的定义是一样的。以自动化和无痛的方式将软件交付给生产的想法一点也不新鲜:这就是 DevOps 的意义所在。几年前,一些人开始告诉世界,如果所有的公司现在都是,至少部分是,软件公司,那么所有的公司都应该关心好的软件工程实践。
DevOps 不仅仅涉及开发和运营。这是关于整体的公司管理;这关系到整个企业文化,从看门人(承诺保持大楼清洁)到首席执行官(承诺保持公司资金和薪水的来源)。承诺理论已经成为支撑文化变革的知识框架。速度是我们讨论这些变化的地方。
(迈克·劳基德斯,奥赖利,2014 年)
有很多文章解释了 DevOps 和 MLOps 之间的差异(例如这一篇来自马可·苏西洛),所以我不打算深入探讨这个问题,但可以说现在同样的想法正在占据主导地位:越来越多的公司正在将机器学习或深度学习模型添加到他们的产品中,或者使用这些模型来帮助他们做出决策。因此,很明显,越来越多的公司需要担心如何获得处理此类任务的专业知识。
MLOps 应该担心什么?
主要有三大领域归劳动和社会保障部管辖:
- 数据:MLOps 不需要担心如何正确地转换或清理数据;这更像是数据科学家的任务。但他们确实需要担心 DS 如何获取数据,如何提取这些数据,如何处理这些数据,如果需要的话,等等。
- 模型:同样,MLOps 不需要担心如何建模,也不需要担心随机森林和线性回归之间的差异,因为数据科学家会做这些。MLOps 应该关心模型如何被运送到云中,如何在云中进行测试,选择什么样的指标以及如何记录或绘制它们,等等。例如,模型倾向于衰退(这被称为模型衰退),定义一种方法来监控模型性能以知道何时替换或重新训练它是至关重要的。
- 代码:这是关键。上述所有(或大部分)工作现在都可以通过 GUI 完成。无论是 AWS、GCP 还是 Azure 都提供了丰富的用户友好界面,你可以在那里管理你的工具。然而,这很难维持。如果项目增长了,那么它就不可维护了。所以,一切都需要代码。地形有印象吗?
MLOps 工作流与任何软件开发工作流都非常相似,是的,这是另一个提示:一切都是软件。
- 设计:第一步,总。收集需求,从基础设施的角度研究问题,等等。
- 开发:管道构建、集成,与构建模型的 DS 团队和构建利用模型和基础设施的应用的 SE 团队紧密合作。主要目标是最大化所有过程的自动化。
- 监测:次日。一切都很顺利,万岁!现在,MLOps 需要确保一切都在控制之下,并且如果某些东西不再在控制之下,它就被修复或至少尽快通知。
MLOps 主要原则
我想把这一段命名为 MLOps 戒律(相当史诗,对不对?),但我意识到戒律这个词可能太大了——我无耻地从那里偷来了这篇来自情妇玛侬·赛克斯的优秀文章,我无法与之竞争。假设下面的列表是 MLOps 的五个主要关注点:
- 自动化:对于 ML 和常规 CI/CD 软件管道,一切都应该尽可能自动化。
- 版本控制:检查变更,回滚到以前的版本等。适用于数据、模型和代码。
- 测试: Nuff '表示。
- 监控:模型衰退、架构瓶颈、负载峰值…
- 再现性:适用于 ML 侧。“但在我的笔记本电脑上有效”是不可接受的。数据预处理、特征工程、模型训练和评估——一切都应该是可重复的。
总而言之,可以说 MLOps 文化旨在融合 DevOps 和 ML 世界。没有人知道未来会有什么神秘,但我们可以期待它在不久的将来会保持趋势。
参考
迈克·劳基德斯,重温“什么是 devo PS”,奥赖利,2014 年
Sciforce, MLOps:综合初学者指南,TDS,2021
在家创建一个现代的、开源的 MLOps 堆栈
第 1 部分:开发框架

介绍
过去几年,许多组织发现,尽管他们培养了优秀的模型,但并不总能从中获得长期价值。这样做的原因是部署和监控。
部署模型并不总是那么容易。有时它们很大。推理需要多长时间?你可能需要一个图形处理器。要不要批量预测?
监控是关键。如果我们对一个模特进行与时尚相关的训练,由于快速变化的趋势,它可能在几个月内无效。
MLOps 是这个问题的解决方案,但是它还涵盖了更多内容。
目录
定义 MLOps
NVIDIA 将 MLOps 定义为…
企业成功运行人工智能的一套最佳实践。
ml-ops.org 建议…
MLOps 旨在统一机器学习和软件应用发布的发布周期。
谷歌给出了这个定义……
MLOps 是一种 ML 工程文化和实践,旨在统一 ML 系统开发(Dev)和 ML 系统运营(Ops)。
实践 MLOps 意味着您倡导 ML 系统构建的所有步骤的自动化和监控,包括集成、测试、发布、部署和基础设施管理。
因此 MLOps 是某种文化、框架或一套规则,允许我们更有效地执行和实现机器学习。它包括 DevOps 的许多功能,如自动化和测试,但通过改进部署和模型监控,它专注于机器学习和数据科学。
为什么你的副业要有 MLOps?
我相信您可以看到拥有数兆字节数据和大量数据科学家的大公司的好处,但在家从事小项目的普通数据科学家又如何呢?
我想说有几个好处...
- 相当一部分数据科学职位专注于部署,雇主通常希望在你的简历中看到部署经验。
- 通过自动化,它让你的生活变得更加轻松,并有助于从你的工作中创造更多面向外部的产出。
- 这使得你的工作可扩展意味着如果你的副业变成人们使用的东西,你可以很容易地扩大规模。
- 最后,它将你的项目提升到了其他项目中很少见到的水平。我从未见过有自动化再培训管道的副业项目!
开发工具框架
Google 有一个优秀的 MLOps 框架。他们定义了 3 个级别。让我们探索一下0 级并将其与1 级进行比较,然后看看我们如何将其与工具联系起来。
0 级:自动化程度低

0 级 MLOps。图片作者。
- 0 级看起来非常像投资组合或 Kaggle 项目。
- 数据存储几乎没有自动化,选项也很有限。
- 如果我们想重新训练我们的模型,我们必须再次重复所有这些步骤。这使得处理模型漂移变得困难。
- 如果我们想用不同的数据或笔记本重复这个过程,我们必须复制代码或修改笔记本。
- 在创建模型时,我们需要额外的工具来跟踪我们的实验。
级别 1:高度自动化

高度自动化。图片作者。
让我们从左到右消化这个。
- 这里的第一个新项目是一个功能商店。这是一个专门针对 ML 特性的数据库。我们在训练模型时会进行各种操作,如缩放、特征工程、编码和数学变换。这些特征中的许多对于典型分析是没有用的,因此特征库允许我们存储这些特征并更容易地访问它们。
- 在左边,我们有精心安排的实验。这是自动化数据科学工作流程中许多操作的概念。
- 左下角的红色方块是一个管道的创建。这是另一个自动化。当我们作为管道部署时,我们部署的是模型和从模型生成预测所需的转换。
- 许多验证步骤也可以自动化。
- 我们还允许通过收集新数据并将其存储在特征库中来持续监控和重新训练。
因此,这是一个伟大的流程图的 MLOps 过程。但是明面上呢?
在企业中,有许多人、移动的部件和快速变化的工具。因此,行业级 MLOps 应该是工具不可知的。然而,在国内,我们可以使用的工具受到成本和规模的限制,我们正在创建一个个性化的流程。我认为家中的 MLOps 不应该是工具不可知的。
MLOps 的一个关键规则是它应该是工具不可知的。我们可以在家里变通一下这条规则。
要从 0 级提升到 1 级,我们需要实施一些新技术。
选择我们的工具
下面的图表改编自MLOps.org&瓦罗海是一个很好的方法来帮助我们找到我们可以使用的工具。

为 MLOps 选择工具。图片作者。
您可以使用此图表中的标题对您找到的工具进行分类。我包含了一些可能适合每个 h 下的示例工具
一些可以探索的工具
这篇 reddit 帖子启发了我写这篇文章,我建议去看看。以下是我计划在未来一年左右研究的工具列表。
- 项目脚手架: CookieCutter , Kedro
- 文献 : 狮身人面像
- CI 与部署: 詹金斯、 Docker 、Gitlab
- 数据建模:DBT
- 数据探索和准备:熊猫(大的话 Pyspark)
- 测试 : 远大前程, Pytest
- 特色店: DVC ,盛宴
- 工作流引擎或协调器: 路易吉,提督,气流
- 模型注册表: MLFlow (使用 Kedro-MLFlow 或 PipelineX )
- 模特上菜:FastAPIBentoML皮质
- 模型监控: 詹金斯管道, MLFlow
一些工具在一个包中提供了许多这些特性
这里肯定有一些地方遗漏了——这将需要一些计算能力,我不认为这是免费的。除了特征存储之外,还需要有存储数据的地方。这可能缺乏可伸缩性,因为我不确定这将如何在多个集群上工作。
结论
在这篇文章中,我们着眼于开发一个 MLOps 框架。最棒的是,当我们找到合适的工具时,我们可以将工具插入到这个框架中。它也确实有助于评估新工具,因为我们可以了解它们如何适应我们的框架和任何现有的工具。
在接下来的几个月里,我计划将此付诸实践。我将试用工具,将它们连接在一起,并尝试在我的家庭工作站上获得一个真正的 MLOps 环境。如果您使用过本文中提到的任何工具,请留下您的评论!
了解更多信息
</5-project-ideas-91da0da17b31> https://medium.com/analytics-vidhya/advanced-sql-for-data-scientists-8e99a3c7f24c
联系我
https://www.linkedin.com/in/adamshafi/
想在家里实现 MLOps?重构您的代码
首先让您的代码可重用,然后看看工具

凯利·西克玛在 Unsplash 上的照片
介绍
MLOps 是目前机器学习和数据科学中最受欢迎的流行语之一,但也是在线课程、YouTube 视频和训练营最少涉及的领域之一。
你可以在这里阅读我是如何定义 MLOps 的:
[## 在家创建一个现代的、开源的 MLOps 堆栈
towardsdatascience.com](/mlops-at-home-part1-4c60db29d4a2)
自从写了上面的文章,我一直带着以下问题研究 MLOps
我如何在家里开发和学习 MLOps,而不用昂贵的软件,以一种转移到现实世界问题的方式?
我的解决方案是开发笔记本 MLOPs 的思路;从 Jupyter 笔记本电脑无缝过渡到可重复、有效的 MLOps 渠道。
第一步是重构或改变你的代码。
为了开发 MLOps 实践,您的代码应该是干净的和可重用的;使用有限硬编码的函数。
那么我们要怎么做呢?下面的流程图总结了这些步骤;第一步是重构您的代码,这将是本文的重点,以后的文章将依次处理每个阶段。我们正准备将代码加载到 Kedro 上,我已经测试了它,发现它是进入 MLOps 的一个很好的网关工具。

MLOps 家居流程图之旅。图片作者。
目录
为什么我们需要重构我们的代码?
MLOps 的一个关键优势是可重复性和自动化,然而我们在机器学习的实验阶段编写的大多数代码都是不可重复的。我们通常在 Jupyter 笔记本的单元格中编写代码,一个接一个地运行,每次按下 shift + enter 。
为了开发一个管道,我们需要使我们的代码更加可重复,这意味着将我们的预处理和建模阶段重写为 Python 函数。
第一步:从一个“普通”的笔记本开始
我用的是 Kaggle 的这个笔记本,它用的是这个房价数据集。这是 Kaggle 上最受欢迎的笔记本之一,理由很充分。它使用了一些非常有趣的整体建模技术,如果你以前没看过,绝对值得一读。
从 MLOps 的角度来看,这是不合适的。一些清理与 EDA 混合在一起,可重用的功能非常少。当然,这是有意义的,因为这样做的目的是为了在 Kaggle 竞争中取得好成绩,而不是为了持续更新的生产部署。
然而,因为我们有一些真正可靠的代码,所以复制和粘贴代码以获得更清晰的东西是很简单的。
步骤 2:清理
我已经创建了这个笔记本的简化版,它被清晰地分成了几个部分,减少了 ed a 并简化了一些代码。在最初的 Kaggle 笔记本中,清洁阶段分布在整个笔记本中,有时中间还有图表。因为我们是从别人那里拿笔记本,所以对我们来说很好地理解它在做什么是很重要的,给它一个结构有助于做到这一点。
您可以在这里查看笔记本:
https://github.com/AdamShafi92/mlops-at-home/blob/main/1-refactoring_code/2 Simplified.ipynb
这款笔记本的核心元素是:
- 加载数据
- 测试:通过检查前几行并查看一些统计和分布来检查数据
- EDA: 了解数据的统计属性,并查看要清理的区域
- 预处理:去除异常值、修正偏差、填充缺失值和编码分类变量
- 测试:确保预处理工作正常。
- 建模:训练几个模型,查看度量标准,挑选最佳模型
步骤 3:创建参数字典
MLOps 的另一个关键领域是跟踪。很容易训练一个模型并调整超参数,然后忘记哪个版本的模型表现最好。我们可以(也愿意)实现工具来帮助这个,但是在这个早期阶段,从字典中调用来访问我们的参数是一个很好的想法。参数字典还有助于我们减少硬编码,因为我们可以调用字典而不是硬编码一个值。
它应该包含(几乎)我们做出的每一个会影响模型的选择,所以如果我们移除任何异常值,它应该包含我们选择的值。删除列也是如此。它不需要不必要的选项。如果您已经缩放了数据,它不需要包括每个不同的缩放器选项。
您可以使用以下代码定义字典
parameters = {'outliers' : 4000}
然后,您可以通过调用字典来访问该值:
parameters['outliers']>>> 4000
你也可以嵌套字典
parameters = {'outliers' : {'feat1' : 4000
'feat2' : 50 }}
要提升一个等级,请尝试使用。yml 文件,可以作为字典加载到 Python 中。一旦我们开始使用 MLOps 工具,我们将不得不这样做。
旁注:代码我们保持不变
因为这不是我第一次这样做,我将把一些区域留在原来的笔记本中,这是因为有非常好的工具和解决方案,我将在以后的帖子中实现它们。
- EDA :探索性数据分析可以根据你正在做的事情而有很大的变化,也有一些软件包可以自动完成这个任务,比如 pandas profiling 。
- 测试:我们将使用远大前程来自动化测试。
- 建模:有很多工具,比如 MLFlow ,我们需要将它们集成到我们的管道中。
步骤 4:将脚本转换成函数
如果你曾经尝试过 Kaggle 数据集,那么拥有一个看起来像上面这样的笔记本是很常见的。为了实现良好的 MLOps 实践,我们需要使用一个可重用的管道来清理我们的数据。要做到这一点,我们需要将代码转换成函数。
每个功能应该相当小,我们将使用单一责任原则,因此每个功能只有一项工作。
单一责任原则是一项计算机编程原则,它规定计算机程序中的每个模块、类或函数都应对该程序功能的单一部分负责,并应封装该部分
—维基百科
这个笔记本在这里:
https://github.com/AdamShafi92/mlops-at-home/blob/main/1-refactoring_code/3 Refactored.ipynb
对于这个 MLOps 实现,我建议在函数中包含任何可能影响模型的关键字参数,这样我们就可以很容易地更改它们。我们还将使用一个参数字典来提供这些关键字参数。我在下面举了一个例子。
原码
train = train.drop(train[(train['GrLivArea']>4000) &
(train['SalePrice']<300000)]
.index)
功能+参数字典
parameters = {'outliers' : {'GrLivArea' : 4000,
'SalePrice' : 300000}} def remove_outliers(train, parameters):
train = train.copy()
train = train.drop(
train[
(train['GrLivArea']>parameters['outliers']['GrLivArea']) &
(train['SalePrice']<parameters['outliers']['SalePrice'])]
.index)
return train
这实际上并不是一个重大的改变,因为列名仍然是硬编码的,所以我们并没有在这里做很大的改进。然而,这个函数现在可以很容易地重用,更重要的是,以后可以在管道中使用。我们将参数字典作为一个参数包含在函数中,以方便以后的工作。
总之,我已经创建了 8 个新的预处理函数。比较 repo 中的笔记本 2 和笔记本 3,比较代码是如何变化的。
https://github.com/AdamShafi92/mlops-at-home/tree/main/1-refactoring_code
运行代码
为了实际运行我们的函数,我们需要运行一些代码。一旦我们转移到 MLOps,这些将被我们的工具定义为管道和编排,但现在我们将在笔记本上运行它们以确保一切正常。要运行代码,我们只需逐个调用每个函数。
要查看每个功能的细节,请查看 Github 上的笔记本 2。
train2 = train.copy()
train2 = remove_outliers(train, parameters)y_train = create_target(train2)train2 = drop_cols(train2, parameters)
train2 = fill_na(train2, parameters)
train2 = total_sf(train2)
train2 = to_str(train2, parameters)
train2 = fix_skew(train2, skewness)
train2 = one_hot(train2)
train2 = train2.astype(float)
第五步:建模
我们没有对这个笔记本的建模部分做太多的改动,因为稍后它会被 MLFlow 所取代。一个好的临时选择是在这里也使用参数字典。
这里我刚刚添加了一个功能,这个功能是训练和 pickles 模型,并输出一些图表;残差的直方图、训练数据和预测的散点图以及 QQ 图。
由于这是一个 Kaggle 数据集,我们没有带标签的测试数据。尽管我们使用交叉验证来挑选最佳模型,但为了简单起见,我们使用训练数据来绘制这些图。
def fit_model(model, X_train, y_train, filepath, n_folds=5):
score = rmsle_cv(model, X_train, y_train, n_folds) model = model.fit(X_train,y_train)
pickle.dump(model, open(filepath, 'wb'))
print("\nmodel score: {:.4f} ({:.4f})\n".format(
score.mean(), score.std()))
y_pred = model.predict(X_train)
fig,ax = plt.subplots(ncols=3,figsize=(20,4))
ax[0].hist(x=y_pred-y_train,bins=20)
ax[1].scatter(x=y_train,y=y_pred)
ax[1].axline([0, 0], [1, 1])
sm.qqplot(y_pred, ax=ax[2], fit=True,line="45")
plt.show()
return modellasso = make_pipeline(
RobustScaler(), Lasso(alpha=0.0005, random_state=1))lasso_fit = fit_model(lasso,train2,y_train,'lasso.pkl')
注意:我们必须缩放线性模型的数据,目前最简单的方法是使用 sklearn 的make _ pipeline函数,并在这一点上包括缩放器,而不是事先缩放所有数据。
就是这样!假设您已经理解了核心概念,那么您现在应该能够将一台以实验为重点的立式笔记本电脑转变为可用于所有重要开发部署阶段的产品。
结论
我们已经学习了如何准备在 MLOps 管道中使用的代码。关键是编写可重用的函数,而不是一次性使用的、根据命令运行的代码。另一个关键的收获是参数字典,这是一个跟踪参数和运行实验的好方法,即使你不打算将你的笔记本开发成 MLOps 流水线。
在本系列的下一篇文章中,我们将创建一个 Kedro 项目,并将我们的笔记本变成节点和管道,Kedro 可以根据命令运行。
了解更多信息
*
将我的内容直接发送到您的收件箱!
https://adamsh.substack.com/p/coming-soon *
培训之外的 MLOps:简化和自动化操作流程

源 Shutterstock(根据许可)
“MLOps”含义的演变
你说的“MLOps”是什么意思?随着围绕 ML 的技术生态系统的发展,“MLOps”现在似乎有(至少)两种截然不同的含义:
- “MLOps”的一个常见用法是指训练人工智能模型的周期:准备数据、评估和训练模型。这种迭代或交互模型通常包括 AutoML 功能,在训练模型范围之外发生的事情不包括在这个定义中。
- 我对 MLOps 的首选定义是指整个数据科学流程,从数据的摄取到在业务环境中运行并在业务层面产生影响的实际应用程序。在本文中,我将解释这两种方法之间的区别,以及为什么它对您的数据科学团队以及整个组织的成功至关重要。
MLOps 的研究优先方法
拥有数据科学用例和小型团队的组织的典型旅程是从他们认为是逻辑起点的地方开始:构建人工智能模型。选择基于数据科学的商业想法,并为数据科学家分配预算,以开始构建和训练机器或深度学习模型的工作。他们可以访问数据提取,搜索模式,并建立在实验室中工作的模型。
对于这个领域的资深人士来说,观察这个行业的变化是值得注意的。几年前,ML 模型用于为业务分析师生成静态或交互式报告,数据科学被视为一个筒仓,对历史数据进行批量预测,并将结果返回给其他人,以便手动整合到应用程序中。在这些条件下,对弹性、规模、实时访问或持续集成和部署(CI/CD)的需求非常少,但我们从模型中获得的价值也有限。
作为这种旧思维模式的残余,今天大多数数据科学解决方案和平台仍然从研究工作流开始,无法在将生成的模型转化为现实世界的人工智能应用时交付。甚至 CI/CD 管道的概念也经常被用来仅仅指训练循环,而不是扩展到包括整个操作管道。这种方法迫使 ML 团队重新设计整个流程,以适应生产环境和方法。这种重复工作的方式消耗了太多的资源和时间,并且经常导致不准确。
研究优先的方法:它是如何工作的,为什么它是不完整的
解决人工智能项目的常见方法是从开发模型开始。数据科学家接收从各种来源手动提取的数据,他或她以交互方式探索和准备数据(此阶段最常用的工具是笔记本),在跟踪所有结果的同时运行培训和实验,生成模型并进行测试/验证,直到获得满意的结果。有了最终的模型,不同的 ML 或数据工程师团队将弄清楚如何将其构建到业务应用程序中,如何处理 API 集成,构建数据管道,应用监控,等等。在许多情况下,原始数据科学将被搁置一旁,并以适合生产的健壮且可扩展的方式重新实施,但这可能不是数据科学家最初的意图。

图 1:交互式模型开发流程(图片由作者提供)
许多工具在研究和开发阶段提供了版本化数据和跟踪实验或模型的方法,一些工具还允许模型开发管道的自动化,并可以生成服务于模型的端点。然而,它们在模型开发流程之后就停止了,并且它们对从自动化数据收集和准备、自动化培训和评估管道、实时应用程序管道、数据质量和模型监控、反馈循环等开始的生产管道没有多大贡献。

图 2:全面生产部署(图片由作者提供)
正如您从上面的两个图表中所看到的,模型开发阶段和生产环境之间没有太多的重叠。
生产部署:
- 涉及更多需要一起工作和版本控制的组件
- 在数据处理、应用程序集成和监控任务上投入了大量精力
- 交互式开发被可管理的微服务所取代,微服务可以处理规模和自动化(重新)部署
- 侧重于弹性、可观察性、安全性和持续运营。
在数据科学筒仓中开发模型的常见方法导致了宝贵资源的浪费和更长的生产时间。
训练结束了!现在怎么办?
人工智能模型提供实时建议、防止欺诈、预测故障和指导自动驾驶汽车的现代应用带来了更大的价值,但也需要大量的工程努力和新方法来实现这一切。业务需求迫使数据科学组件变得健壮、高性能、高度可扩展,并与敏捷软件和开发运维实践保持一致。每花一个小时开发一个模型,就会有十几个小时花在工程和部署上。
通常,更广泛的团队只有在实验室中建立了模型后,才意识到他们面前的操作化人工智能的挑战。在这个后期阶段,构建实际的 AI 应用程序,部署它,并在生产中维护它变得非常痛苦,有时甚至不可行。然后意识到创建一个可重复和可再现的过程,以便更多的人工智能应用程序可以在持续的基础上构建和部署,这是一个完全不同的游戏。
采用生产第一的思维模式
在考虑所有业务需求的意义上,操作化机器学习经常发生,例如联合数据源、规模需求、实时数据摄取或转换/在线功能工程的关键影响、处理升级、监控等。—这是一种事后想法,使得用人工智能创造真正的商业价值变得更加困难。
这就是为什么我提倡心态转变。从最终目标开始:也就是说,采用生产第一的方法来设计一个连续的运营管道,然后确保各种组件和实践都映射到其中。使尽可能多的组件自动化,并使流程可重复,以便您可以根据组织的需求进行扩展。
生产优先的 MLOps 方法及其优势
取代这种孤立、复杂和手动的过程,从使用模块化策略设计生产管道开始,其中不同的部分提供了从研发到可扩展生产管道的连续、自动化和简单得多的方式,而不需要重构代码、添加粘合逻辑以及在数据和 ML 工程上花费大量精力。
每条生产管道都应考虑四个关键组成部分:
- 特征存储:收集、准备、编目和提供数据特征,以供开发(离线)和实时(在线)使用
- ML CI/CD 管道:使用生产数据(由特性库生成)的快照和来自源代码控制(Git)的代码,自动训练、测试、优化、部署或更新模型
- 实时/事件驱动的应用程序管道:包括 API 处理、数据准备/丰富、模型服务、集成、驱动和测量动作等。
- 实时数据和模型监控:监控数据、模型和生产组件,并为探索生产数据、识别偏差、异常或数据质量问题警报、触发再培训工作、衡量业务影响等提供反馈回路。

图 3:生产 ML 管道的集成方法(图片由作者提供)
虽然这些步骤中的每一步都可能是独立的,但它们仍然需要紧密集成。
例如:
- 培训作业需要从要素存储中获取要素,并使用元数据更新要素存储,这些元数据将在服务或监控中使用。
- 实时管道需要用存储在特征存储中的特征来丰富传入事件,并且可以使用特征元数据(策略、统计、模式等)。)估算缺失数据或验证数据质量。
- 监控层必须从实时管道收集实时输入和输出,并将其与来自特征存储的特征数据/元数据或由训练层生成的模型元数据进行比较,并且它需要将所有新的生产数据写回特征存储,以便它可以用于各种任务,例如数据分析、模型重新训练(对新数据)、模型改进。
MLOps 自动化和流程编排
当我们更新上面详述的一个组件时,它会立即影响特征生成、模型服务管道和监控,因此我们需要对每个组件应用版本控制,以及跨组件的版本控制和滚动升级。
因为这四个组件紧密相连,所以不能在孤岛中管理它们。这就是 MLOps 编排的用武之地。ML 团队需要一种使用相同的工具、实践、API、元数据和版本控制的协作方式。这种协作可以在定制的平台上进行,该平台由必须粘合在一起并由大型内部团队维护的单个组件构建,或者使用现成的自动化解决方案,如 Iguazio MLOps 平台或开源 MLOps 编排框架 MLRun 。
MLOps 应该提供真正的商业价值!
您的数据科学团队是否为您的组织防止了欺诈,减少了欺诈性交易,从而为您的组织节省了成本,为新客户提供了信用额度,并减少了法律纠纷?
您的组织是否能够预测哪些机器即将发生故障,并主动缓解问题以节省维修损坏机器或购买新机器的成本?
您的客户成功团队是否能够预测客户流失并产生留住客户所需的确切收益,从而直接影响您组织的底线?
如果这些问题的答案是否定的,并且软件/基础架构效率低下阻碍了您的组织看到您的数据科学团队可以提供的真正价值,那么现在是时候寻找使数据科学流程自动化、协调、加速和可重现的解决方案了。凭借坚实的 MLOps 基础,您将能够快速、持续地为企业提供新的人工智能服务,甚至是大规模和实时的。您将使您的数据科学、数据工程和 DevOps 团队能够更加高效地协作。最重要的是,整个组织将能够从您团队的创新解决方案中受益,并看到它们直接影响组织的目标和底线。
MLOps:建立一个功能商店?以下是需要记住的几件事
行业笔记
一个功能商店应该有 3 个主要构件,并提供 10 个主要功能。这使得要素库能够改进原始数据的处理和编目方式,从而加快数据科学家的周转时间。

图 1:特征存储数据流(作者图片)
在我们关于特性商店的第一篇文章中,我们定义了它是什么,为什么需要它,以及它如何填补 MLOps 生命周期中的一个重要空白。从上图中可以看出,特征存储有三层,转换(接收&过程数据并创建特征)、存储(存储创建的特征&的元数据)和服务(使存储的特征可用)。在本文中,我们将了解商店层应该具备哪些主要组件。我们还将了解在构建功能商店或评估供应商提供的任何现有功能商店时应该记住的主要功能。在下一篇文章中,我们将探讨如何使用各种技术来实现这三个层和功能。订阅此处的以获得相同的通知。
功能存储:主要组件
功能商店的商店层应该具有的三个主要组件是:
- 一个离线特征库,用于提供大批量特征,以(1)创建训练/测试数据集,以及(2)将特征用于训练作业和/或批量应用评分/推理作业。这些要求通常在超过一分钟的延迟时得到满足。离线商店通常实现为分布式文件系统(ADLS、S3 等)或数据仓库(红移、雪花、大查询、Azure Synapse 等)。例如,在优步的米开朗基罗调色板功能商店中,它从 lambda architecture 获得灵感,使用 HDFS 的 HIVE 实现。
- 一个在线特征存储库,用于提供单行特征(一个特征向量)作为个体预测在线模型的输入特征。这些要求通常以几秒或几毫秒的延迟来满足。在线商店理想地实现为键值存储(例如 Redis、Mongo、Hbase、Cassandra 等)。)用于快速查找,具体取决于等待时间要求。
- 一个特征注册表,用于存储所有具有血统的特征的元数据,供两个离线&在线特征存储使用。它基本上是一个实用程序,帮助了解商店中所有可用的功能以及它们是如何生成的信息。此外,用于下游应用/模型的一些特征搜索/推荐能力可以被实现为特征注册表的一部分,以使得能够容易地发现特征。

图 2:特征库框架(图片来自 ML 的特征库,2021)
FeatureOps:功能库的主要功能
在我们的上一篇文章中,我们将特性操作确定为当前 MLOps 生命周期中的缺口。FeatureOps 的缺失导致数据科学家一次又一次地重复创建相同的功能,从而大大增加了上市时间。FeatureOps 使数据科学家能够使用功能库执行下面列出的功能,从而缩短他们的开发时间,并促进团队协作。现在让我们来看看这些功能。

图 3:特征存储功能(作者图片)
1.特征数据验证
功能存储应该支持定义用于检查数据完整性的数据验证规则。这些包括但不限于,检查值是否在有效范围内,检查值是否唯一/不为空,以及检查描述性统计数据是否在定义的范围内。可以集成到功能商店中的验证工具的示例有远大前程(任何环境) TFX 数据验证(深度学习环境) Deequi (大数据环境)。
2.功能连接
对于不同的模型,需要重用不同训练/测试数据集中的特征。大部分在线下商店实施,而不是在线商店,因为这是一个昂贵的过程。
3.创建训练/测试数据集
数据科学家应该能够使用要素存储来查询、探索、转换和连接要素,以生成用于数据版本化系统的训练/测试数据集。
4.输出数据格式
特征库应该为数据科学家提供以适合 ML 建模的格式输出数据的选项(例如,TensorFlow 的 TFRecord,PyTorch 的 NPY)。数据仓库/SQL 目前缺乏这种能力。这一功能将使数据科学家在培训过程中多走一步。
5.时间操作
跨定义的时间粒度(每秒、每小时、每天、每周等)的自动特征版本化将允许数据科学家查询在给定时间点的特征。在下列情况下,这将很有帮助:
- 创建培训/测试数据时(例如,培训数据是 2010-2018 年的数据,而测试数据是 2019-2020 年的数据)
- 对功能进行更改(例如,回滚功能的错误提交)
- 比较功能的统计数据并查看它们如何随时间变化(功能监控)
- 回到过去并获取特征值。例如,由模型做出的预测也可以存储在特征存储中,这些预测的结果(基本事实)也可以存储在特征存储中。如果 6 个月前做出的预测的实际结果/标签现在可用,那么我们需要首先查找 6 个月前用于生成该结果的特征值,然后用该新结果/标签对其进行标记,以在需要时触发模型的重新训练。
6.特征可视化
要素存储应支持开箱即用的要素数据可视化,以查看数据分布、要素之间的关系和聚合统计数据(最小值、最大值、平均值、唯一类别、缺失值等)。这将有助于数据科学家快速了解特性,并做出使用特定特性的更好决策。
7.特征管道
要素存储应可选择定义具有定时触发器的要素管线,以便在将来自不同来源的输入数据存储到要素存储之前对其进行转换和聚合。这种特征流水线(通常在新数据到达时运行)可以以不同于训练流水线的节奏运行。
8.功能监控
特征存储应该能够监控特征以识别特征漂移或数据漂移。可以对实时生产数据计算统计数据,并与特征存储中的先前版本进行比较,以跟踪培训服务偏差。例如,如果特征的平均或标准偏差从生产/实况数据到存储在特征存储上的训练数据有相当大的变化,则重新训练模型可能是谨慎的。在我们之前关于模型监控的文章中强调的许多概念,在这里也可以用来支持特性监控。
9.特色搜索/推荐
一个特征库应该索引所有的特征及其元数据,并使其便于用户查询检索(如果可能,基于自然语言)。这将确保特性不会在堆中丢失,并提高它们的可重用性。这通常由特性注册中心来完成(如前一节所述)。
此外,通过基于项目中已包含功能的某些属性和元数据推荐可能的功能,功能存储可以增强现有功能的可见性。这让初级到中级的数据科学家具备了经验丰富的高级数据科学家通常具备的洞察力,从而提高了数据科学团队的整体效率。
10.特征治理
如果没有适当的治理,功能库很快就会变成功能实验室,或者更糟,变成功能沼泽。治理策略会影响使用功能存储的各个团队的工作流程和决策。特征存储治理包括以下方面:
- 实施访问控制,以决定谁有权使用哪些功能
- 确定功能所有权,即与某人一起维护和更新功能的责任
- 限制可用于训练特定类型模型的特征类型
- 通过审核模型结果以检查偏差/道德,提高对特征数据的信任和信心
- 通过利用功能沿袭来跟踪功能的来源、生成方式以及最终如何流入业务中的下游报告和分析,从而保持透明度
- 了解适用于功能数据的内部政策和法规要求以及外部合规性要求,并对其进行适当保护
数据科学家工作流程中的功能存储使用情况
下图描绘了前面提到的 3 个主要功能存储组件(红框)和上述 10 个主要功能(蓝框)在典型数据科学家的 ML 工作流程中不同时间点的使用情况。

图 4:DS 工作流程中的特性存储组件和主要功能(图片由作者提供)
结论
特征库通过抽象在 ML 训练和推理中获取、转换、存储和使用特征所需的大量工程工作,使数据科学家的工作更加方便和高效。它还支持组织的丰富数据发现和治理,从而释放其生成的每个数据字节的价值。希望利用这一点并决定购买/租赁/构建功能商店的组织应该考虑本文中提到的 3 个主要组件和 10 个主要功能,以从其实现中获得最大价值。
MLOps 可以帮助修复模型衰退
为什么您应该在发货后监控您的机器学习模型

来源:作者
机器学习团队通常会在测试集上获得很好的结果,将模型部署到现实世界中,花两周时间观察以确保它仍然获得良好的结果,然后转移到另一个项目。
然而,由于机器学习模型经常与现实世界的事件进行交互,而不仅仅是静态数据集,因此它们的准确性会随着时间的推移而下降。如果没有自动监控,很可能在相当长一段时间内没有人会注意到。
在某个时候,可能比理想状态晚得多,你可能会捡起来。您的团队将返回,重新训练模型,将更新推向生产,手动监控几个星期,然后再次前进。
虽然这种“假设事情按预期进行”的乐观心态可以在结果不那么重要的情况下发挥作用,但在影响更大的情况下可能是灾难性的。
以下是如何使用 MLOps 自动检测模型衰退、更新模型并将其部署到生产环境中。
如何看待自动化监控
自动化监控的第一步是检测问题。关键是主动识别模型衰退,而不是在问题出现时简单地做出反应。您可以防止许多问题,即使您需要投入一些时间来手动重新训练和重新部署模型。
理论上,这很简单。您设置了一个监控系统,当您的系统精度低于某个阈值时,它会向您发出警报。
实际上,这要困难得多。你可能有一个手动注释的“黄金”数据集用于训练,但是在生产中,你不知道你的模型是否出错了:如果你出错了,你首先就不需要这个模型了。
监控模型的三种策略

您可以直接监控您的模型,通过代理或追溯。来源:作者
要计算预测的准确性,您可以执行以下操作之一:
- 监控与准确性相关的指标。例如,如果一个垃圾邮件分类器通常预测 30%的邮件是垃圾邮件,但有一天预测了 75%,那么实际的垃圾邮件可能没有大幅增加;更有可能是模型坏了。
- 人在回路样本。例如,您可以手动检查所有预测的 5%,并监控这些预测的准确性..
- 回顾性监测。如果你对未来做出预测,比如一个病人是否会在一个月后康复,那么你就能在以后验证你的准确性。
一旦你有了一个可以作为合理质量检查的度量标准,你就需要一个工具来监控它。这可以是像 DataDog 这样的通用监控工具,也可以是专门的机器学习监控工具。一个专门的工具可以更容易地获得机器学习场景中更有用的指标,如误差分布或平均绝对误差。但是如果您的用例比较简单,并且您已经设置了另一个监控工具,那么从那个开始可能会更快。
自动重新训练和部署您的模型
检测衰变是最重要的一步,但它并不那么有价值,除非你能对此做些什么。手动重新训练一个模型并部署它是一种选择,但是你的团队需要不断地“待命”,等待警报触发。
手动再培训需要付出大量的努力,并且存在更大的人为错误风险,尤其是如果您需要经常这样做的话。
如果您已经建立了一个完整的 MLOps 架构,那么您应该已经准备好自动重新训练您的模型并部署它们——或者按照固定的时间表,或者当您检测到模型衰退时。
在软件工程中,CI/CD 或“持续集成/持续部署”框架很常见。这意味着您已经做好了准备,新代码可以自动集成到现有代码中,并进行测试和部署。
在 MLOps 中,你还想对你的模型进行再培训,所以你需要 CT/CI/CD,或者说“持续培训/持续集成/持续部署”。
您需要在前期投入时间来建立 CT/CI/CD 管道,但是根据我们的经验,大多数团队手工做事情的时间太长了。
可以用来对抗模型衰退的工具

您可以将您的机器学习框架(如 TensorFlow)与工作流工具、特征存储和模型服务框架相结合,以对抗模型衰退。来源:作者
有大量的 MLOps 工具选项,但是这里是我们用来自动重新训练我们的模型以避免模型衰退的:
- 提督作为工作流和数据流工具。这让我们可以定义要运行的任务图,然后自动执行它们,并在必要时进行监控和重新运行。你可以阅读更多关于的内容,了解我们对提督的喜爱。Prefect 不仅处理模型的自动再训练,还负责定期监控,以帮助我们分析和检测衰减。
- 盛宴作为特色店。使用 Feast,您可以跟踪数据的变化,并在模型之间共享特性。在我们的文章选择功能商店中了解更多关于功能商店的信息。
- 谢顿为模特服务。使用 Seldon,您可以轻松地将任何模型转换为 API 并大规模提供服务,而无需从头开始编写服务代码。
您需要帮助为您的研究团队设置 MLOps 吗?
经常让团队在投资一个合适的架构上犹豫的是未知的数量:他们不知道需要多长时间或者如何开始。我们以前做过,很乐意帮你跳过这种繁重的工作,所以如果你想聊天,请联系我们。
研究团队的 MLOps
确保您团队的工作是可重复的、负责任的、协作的和连续的

来源:作者
机器学习团队经常面临同样的挑战。MLOps 是大多数机器学习团队解决这些问题应该遵循的一套流程。
为了使 MLOps 更具体,我们将看看它为研究团队解决了什么问题。
为什么您的团队需要 MLOps?
许多机器学习团队专注于构建新颖的算法或训练高性能的模型。虽然这些是任何机器学习解决方案的基本组件,但与真实解决方案中所需的所有周围过程相比,它们相对较小,例如数据工程和监控。

算法代码只是任何机器学习解决方案的一小部分。来源:作者——受启发机器学习系统中隐藏的技术债务。
研究团队往往更关注机器学习代码,而不是周围的基础设施。随着协作、共享结果和重用在大型数据集上完成的工作的需求不断增加,研究团队很快就达到了这种方法的极限。MLOps 解决了这些问题,并允许研究团队实现他们的目标,尽管处理大型数据集、代码和机器学习模型会带来复杂性。

MLOps 的目标与研究团队的需求非常一致。来源:作者
如果没有 MLOps 架构,您可能会面临哪些挑战?
挑战#1:你不能重建现有的模型
你的团队有一个训练有素的模型,运行良好,但无法重建。数据和软件版本已经改变,或者在本地机器上建立培训管道的团队成员已经离开。您的团队可以直接共享训练好的模型文件,但是您不能改进或更新它,因为构建它的一些步骤现在已经丢失了。
挑战#2:您不能有效地审计或监控您的模型
在评估阶段,您团队的模型结果非常出色。但没有持续的审计或监控来确保模型的预测仍然有意义。您的团队需要盲目地相信模型正在按预期运行。
挑战 3:你不能轻易分享模型
您团队的每个成员都建立了自己的管道来管理数据和训练模型。很难进行协作或共享结果,因为您不能轻松地处理彼此的模型,或重用彼此的中间数据。
挑战#4:你的概念验证看起来很有希望,但是大规模培训会使你的硬件崩溃
您可以在笔记本电脑上的小数据集上训练您的模型,但很难扩展,无论是通过将其推到更强大的机器上,还是通过利用计算集群。
这意味着将概念验证转化为生产解决方案需要付出巨大的努力。
挑战 5:你发现错误太晚了
因为您的团队为每个项目从头开始构建基础结构和粘合代码,所以 bug 和其他问题经常发生,并且通常只在结果发布后才被发现。同样的问题会在每个项目中重复出现,因为您不容易重用代码。
MLOps 架构如何应对这些挑战?
实施 MLOps 实践和使用标准化架构有助于解决所有这些挑战。大多数机器学习团队的架构应该包括以下内容:

我们的 MLOps 架构由几个集成组件组成,共同解决大多数团队面临的困难。来源:作者
实验中心
实验中心让您的研究人员开发笔记本电脑,试验新的模型和架构,并验证假设。它帮助团队成员共享代码,并确保他们可以复制彼此的模型。
你只需要设置一次,因为你们都在同一个环境中工作。共享集线器防止了硬件特定问题带来的麻烦,比如团队成员使用不同的操作系统或开发机器性能不足。
我们使用 JupyterHub 。
模型注册和实验跟踪器
模型注册中心存储您的团队生产的每个模型,带有一个名称和一个或多个版本。实验追踪器与此类似,但工作在更高的层次:为团队的实验定义名称、版本和元数据。
通过存储和版本化您的团队产生的每一个模型和实验,您总是可以再现在特定实验中执行的结果。你不会因为不知道哪个模型产生了具体的结果,也不知道你用了什么参数做了一个历史实验而被屏蔽。
这也将有助于您遵守任何必要的法律要求,以证明您的结果。
我们使用 MLFlow 。
模型服务工具
模型服务工具自动将您的模型部署到阶段或生产环境中,并使您的团队和最终用户可以访问统一的 API。
跨所有模型的统一 API 和基础设施使得使用和监控它们变得更加容易。将您的模型自动部署到试运行和生产环境意味着最新版本可以立即投入使用。
我们用谢顿。
数据流工具
数据流工具跟踪管道中的每一个步骤,并可以根据需要监控和重新运行步骤。这有助于防止模型无法重建的情况,因为您可以轻松地重新运行每次使用的完全相同的步骤。
它还可以节省团队的时间,并防止任何重复性工作中的人为错误。
我们用提督。
功能商店
有时,您的团队需要用于训练特定模型的确切特征,但是基础数据已经改变。或者,您可能已经花时间构建了特定的功能,现在想要在新的模型中重用这些功能。功能库保存了每个功能的版本,供您的团队重用,从而更容易地进行协作。
我们用宴。
MLOps 是“值得拥有”还是必不可少?
虽然一些研究团队仍然没有 MLOps 工具或最佳实践,但我们相信 MLOps 已经成为几乎所有团队的重要组成部分。除非你的团队非常小或者只致力于琐碎的问题,否则你的机器学习研究也需要可重复、可问责、协作、和持续。如果没有 MLOps,实现这些目标将非常困难。
您需要帮助在您的研究团队中设置 MLOps 吗?
我们喜欢为机器学习研究团队找到合适的 MLOps 架构。联系我们了解更多信息。
MLOps 级—手动管线
MLOps 级别 0 是机器学习过程的基本成熟度级别。对于一些组织来说,这可能就足够了,特别是当模型不经常需要重新训练时。这一级别的 MLOps 的特点是工作流程中的高度手动步骤。为了解决 MLOps 级中普遍存在的挑战,可以利用持续集成、持续部署和持续培训组件。

在我之前的文章中,我回顾了组成机器学习过程的步骤(https://towards data science . com/steps-of-a-machine-learning-process-7 ACC 43973385)。这些步骤的自动化程度决定了机器学习过程的成熟程度。在这篇文章中,我将更详细地介绍 MLOps 的第一级(0 级),即成熟度的基本级别。
下图说明了将模型用作预测服务的各个步骤和管道(工作流),以便应用程序使用模型预测。

图片作者。MLOps 级别 0
m lops 0 级的特性
- 工作流程中的每一步都是手动的。每个步骤都是手动执行的,步骤之间的任何转换也是手动的。通常工作是在笔记本上完成的,如 Jupyter/Jupyter Lab/Zeppelin,代码仍然被认为是实验性的。
- 机器学习系统的机器学习和操作组件已断开连接。数据科学家通常会完成从数据源、数据提取和分析、数据准备、模型训练、模型评估和验证到最终注册模型的所有工作。然后,一个独立的工程团队从模型注册表中提取模型,并负责在生产环境中提供培训期间使用的功能,同时部署具有低延迟服务的模型。
- 模型发布很少。高度手工化的过程,模型训练很难有高速度。
- 代码的持续集成是不存在的。通常,测试是在笔记本电脑内或脚本执行期间完成的。用于训练和可视化的代码通常是源代码控制的。
- 连续部署是不存在的。由于模型重新训练和部署的频率很低,因此在此工作流中不考虑连续部署。
- 这个工作流的部署就是将模型放入预测服务,通常是一个带有 REST API 的微服务。该工作流不考虑整个机器学习系统的部署。
- 没有性能监控。没有跟踪模型在生产中的性能,因此,很难确定模型何时降级,必须进行重新训练。
m lops 0 级的挑战
对于一些组织来说,部署机器学习系统的这种高度手动的过程可能就足够了。然而,在现实中,模型经常在生产中断裂/变质。此外,数据会随着时间的推移而变化,过去的好模型将不再理想。
为了应对这些挑战,组织可以采取以下措施
- 监控生产中模型的质量。通过这样做,您可以检测模型性能的下降和模型的陈旧。这也有助于确定何时需要重新培训。
- 模型的频繁再训练。数据会随着时间的推移而变化,有时变化速度很快。因此,生产中的模型需要用生产中看到的最新数据来训练。
- 不断的实验。尝试各种特征工程变量、模型架构和超参数。
为了克服 MLOps 级的挑战,值得将持续集成(CI)/持续部署(CD)和持续培训(CT)工作流集成到机器学习系统中。通过部署机器学习管道,您可以启用 CT。使用 CI/CD,系统可以快速测试、构建和部署新的机器学习模型实现。
接下来,我将更详细地介绍 MLOps 级别 1,其中我们自动化了机器学习管道。
MLOps 级:持续培训
在 MLOps 级中,机器学习流水线是自动化的。通过这样做,您可以实现机器学习模型的持续培训以及模型预测服务的持续交付。

克里斯多夫·伯恩斯在 Unsplash 上拍摄的照片
推荐预读
为了从本文中获得最大收益,理解我将使用的术语是很重要的。为了让你自己熟悉这些术语,我在本文中非常详细地概述了机器学习服务的各个步骤:https://towardsdatascience . com/steps-of-a-machine-learning-process-7 ACC 43973385。
所讨论的一些概念与 DevOps 理念重叠,而另一些则是 MLOps 所特有的。要阅读更多关于这两种实践的比较和对比,请查看我的文章:https://towardsdatascience.com/mlops-vs-devops-5c9a4d5a60ba
如果你有兴趣了解更多关于这个级别的 MLOps 与 0 级有什么不同,请查看我的文章:https://towardsdatascience . com/MLOps-level-0-manual-pipelines-7278 b 9949 e 59如下:

图片作者。MLOps 级别 0
m lops 1 级的目的
MLOps 一级旨在实现机器学习模型的持续训练。为了实现这一点,机器学习管道在这一级是自动化的。这包括自动化使用新数据的训练过程、生产中的模型再训练、自动化数据验证和模型验证、引入触发器以启动管道以及机器学习模型元数据存储。下图展示了这一概念:

图片作者。MLOps 一级
m lops 1 级的特性
- 快速实验:由实验管道组成的步骤是完全自动化和协调的。通过自动化和协调这一管道,它使数据科学家能够快速试验不同的模型和数据。此外,将机器学习管道从开发/测试转移到生产变得更加容易。
- 生产中模型的持续训练:通过使用触发器,使用新数据对生产中使用的模型进行训练。
- 机器学习和系统操作对称:开发/测试和生产中使用的机器学习管道是对称的。这是 MLOps 实践中整合 DevOps 理念的关键组成部分。
- 组件和管道的模块化代码:机器学习管道中的组件应该在机器学习管道中可重用、可组合和可共享。一些组件,如探索性数据分析(EDA)仍然可以在笔记本电脑内,但其他组件必须是模块化的。理想情况下,部件也应装箱。
- 模型的持续交付:在生产中,机器学习管道应该持续部署新模型的预测服务,这些新模型已经过新数据的训练。将经过训练和验证的模型部署为预测服务的管道必须自动化。
- 管道的部署:在 MLOps 级中,只有经过训练的模型人工制品被部署为生产的预测服务。在 MLOps 级别 1 中,部署了整个训练管道,作为生产中的预测服务部署的模型在生产中的最新数据上进行训练。
m lops 1 级的新功能
为了实现 MLOps 级的持续培训,架构中需要以下组件。
数据和模型验证
在 MLOps 级中,机器学习流水线是自动化的,并且被自动触发。一旦触发,就没有人工干预的机会,因此,在生产管道中需要自动数据验证和模型验证。
数据验证步骤至关重要,因为它用于决定是否应该重新训练数据或停止执行管道。两种情况将决定做出何种决定:
- 数据模式偏斜:这些是异常的输入数据,这意味着下游处理和步骤可能会失败,因为它将接收到它不期望的数据。如果发生这种情况,必须停止管道执行,并调查异常数据,以确定是否需要更新管道。数据模式偏差的示例包括获取意外的模式、未接收所有模式以及接收具有意外值的要素。
- 数据值偏差:这是指当要素值发生显著变化时,数据的统计属性发生了变化。这表明模型需要重新训练,以捕捉数据中统计属性的变化。
模型验证步骤验证训练好的模型,以确定它是否应该被提升到生产中。对于离线模型验证,遵循以下步骤:
- 根据测试数据(培训过程中未使用的数据)生成培训数据的评估指标。
- 生产中使用的新模型和当前模型之间的评估度量的比较。
- 确保不同数据段的模型性能一致。
- 测试用于部署的模型,包括基础架构兼容性和与预测服务 API 的一致性。
除了离线模型验证之外,还需要一个在线模型验证步骤,包括 canary 部署或 A/B 测试。
功能商店
这是一个可选组件,是一个集中的存储库,用于存储和访问用于训练和服务管道的特征。理想的特征存储需要能够为训练和服务过程处理高吞吐量批量服务和特征值的低延迟实时服务。
元数据管理
记录关于由训练机器学习流水线执行的不同实验的信息和数据。这是用来帮助数据和工件血统,再现性和比较不同的实验。理想情况下,元数据管理存储记录以下元数据:
- 正在运行哪个版本的实验。
- 实验的开始和结束日期和时间以及组成实验的步骤。
- 管道中使用的参数。
- 指向管道每一步产生的工件的指针。示例包括:实验中使用的数据位置、识别的异常、计算的统计数据、分类特征的词汇。
- 正在管道中接受培训的模型的评估指标。
ML 流水线触发器
在管道中使用触发器来用新数据重新训练模型。触发管道的方法包括以下几种:
- 临时手动触发。
- 基于时间。例如,如果新数据按照固定的时间表到达系统,则可以在新数据到达之后执行管道。
- 当新数据到达时触发。当特定数据到达数据源时,它会触发管道根据新数据重新训练模型。
- 模型性能退化。如果生产中的模型恶化超过预定义的阈值,则应该触发模型的重新训练。
- 数据分布发生显著变化,触发了模型的重新训练。
挑战
在 MLOps 级中,您需要手动测试管道及其组件。部署新的管道实现也将是手动的。此外,还依赖 IT 团队将新的源代码部署到目标环境中。
为了克服这些挑战,需要一个持续集成(CI)和持续部署(CD)流程设置来自动执行 MLOps 级中的这些手动步骤。如果一个系统有 CI/CD 设置,那么它已经达到了 MLOps 级别 2,我将在以后的文章中更详细地讨论这个级别。
MLOps 级— CI/CD +持续培训
MLOps 级别 2 是关于利用自动化 CI/CD 对生产中的管道以及其他环境进行快速可靠的更新。

Anton Maksimov juvnsky 在 Unsplash 上的照片
推荐预读
本文是讨论各种级别 MLOps 的 3 部分系列的最后一篇文章。要了解各个级别之间的差异,请找到下面的链接和资源。
MLOps level 0(手动管道)可以在这里找到:https://towardsdatascience . com/MLOps-level-0-Manual-Pipelines-7278 b 9949 e 59

图片作者。MLOps 级别 0
MLOps 一级(连续训练)可以在这里找到:https://towardsdatascience . com/MLOps-level-1-Continuous-Training-B2 a 633 e 27d 47

图片作者。MLOps 一级
为了从本文中获得最大收益,理解我将使用的术语是很重要的。为了让你自己熟悉这些术语,我在本文中非常详细地概述了机器学习服务的各个步骤:https://towardsdatascience . com/steps-of-a-machine-learning-process-7 ACC 43973385。
所讨论的一些概念与 DevOps 理念重叠,而另一些则是 MLOps 所特有的。要阅读更多关于这两种实践的比较和对比,请查看我的文章:https://towardsdatascience.com/mlops-vs-devops-5c9a4d5a60ba
m lops 2 级的目的
MLOps level 2 旨在使用自动化 CI/CD 对生产中的管道以及其他环境进行快速可靠的更新。通过自动化 CI/CD 系统实现流水线自动化,可以快速实现新想法和实验,同时自动构建、测试和部署新的流水线工件和组件到适当的目标环境。下图展示了 MLOps 级的概念:

图片作者。MLOps 二级
m lops 二级特性
构成 MLOps 级别 2 的组件与 MLOps 级别 1 非常相似。不同之处在于添加了
- 测试和构建服务(持续集成)。
- 部署服务(持续部署)。
- 管道协调。
管道可以分为 6 个阶段:
- 开发和实验。这是尝试不同算法和建模技术的地方,每个实验步骤都是精心安排的。阶段 1 的最终输出是被推送到代码库的流水线步骤的源代码。
- 管道的持续整合。一旦代码被推送到代码仓库,它就会触发一个测试管道,并构建需要在后续阶段部署的必要组件(包、工件等)。
- 管道连续输送。阶段 2 中产生的工件被部署到目标环境中。这一阶段的输出是一个已部署的管道,其中包含来自阶段 1 的模型的新实现。
- 自动触发。生产中流水线的执行或者基于时间表或者响应于事件触发器而被自动触发。这一阶段的最终输出是被推送到模型注册中心的训练模型。
- 持续交付模型。最终训练好的模型被部署为服务,其中应用程序可以获得模型预测。此阶段的最终输出是一个已部署的模型预测服务。
- 模型监控。通过收集模型性能的统计数据,对生产中的模型进行实时监控。当模型偏离或恶化到可接受的阈值时,该阶段应该触发生产中的流水线的执行,以使用新数据重新训练模型,或者执行新的实验流水线。
MLOps 2 级的新功能
MLOps 级的新概念是流水线自动化。这是通过持续集成和持续交付实现的。
在持续集成阶段,当新代码被推送到代码库中时,管道和组成管道的组件被构建、测试和打包。此阶段的测试非常重要,可包括以下测试:
- 特征工程逻辑的单元测试。
- 模型中实现的单元测试方法。例如,测试是否将正确的数据类型传递给函数。
- 测试在训练期间模型收敛。
- 测试模型的输出是明智的,不会产生 NaN 值。
- 测试流水线中的组件输出预期的假象。
- 测试管道组件之间的集成。
在连续交付阶段,新的管道实现被部署到目标环境,目标环境进而输出提供新训练的模型预测的服务。此阶段要考虑的事项包括:
- 检查新模型在目标基础架构上的兼容性。是否有足够的资源,目标环境中是否安装了所需的包/库等。
- 测试预测服务的性能。
- 验证新模型满足所需的最低预测性能。
- 根据环境进行分阶段部署。例如,将管道自动部署到测试环境,半自动部署到预生产环境,在预生产环境中成功运行几次后手动部署到生产环境。
结论
将机器学习模型实施到生产中涉及几个阶段、组件和管道。您不必将所有进程从一个 MLOps 级别移动到另一个级别。一个团队可以从 MLOps 级逐步实现实践到他们当前的系统中,以提高自动化水平。
保持检查你的人工智能/人工智能模型预测
模型监控指标确保生产化的人工智能模型按预期执行。先验概率转移是一种现象,当在模型预测中检测到时,表明模型已经变得陈旧。
背景
先验概率转移

图 1:在人工智能系统监控的三个主要方面中,即数据、模型和预测,稳定性度量类别 I 监控人工智能系统的预测输出,以检测先验概率变化。(图片来自aif360.mybluemix.net)
先验概率偏移捕捉训练数据和生产数据之间或者生产数据的不同时间范围之间的预测输出的分布偏移(要了解关于这些时间范围的更多信息,请查阅本系列的第一篇文章)。在贝叶斯统计推断[2]中,不确定变量(预测产量)的先验概率分布是在考虑一些证据(新的生产数据)之前表达个人对该变量的信念的概率分布。因此,潜在的假设是,训练时预测输出的分布(即先验概率)不应在生产过程中发生显著变化(即有新的证据)。

图 2:训练和生产数据之间的先验概率转移。预测输出类别的分布在培训和生产之间有很大的不同。(图片由作者提供)
假设在贷款违约数据集中,训练数据具有相等的贷款违约的先验概率(即,贷款违约的概率是 0.5)。因此,我们预计 50%的训练集包含贷款违约者,50%包含良好贷款。然而,在现实中,只有 5%的贷款可能违约,然后贷款违约变量的先验概率发生了变化。数学上,我们可以将其表示为

稳定性度量标准类型 I
稳定性度量标准类型 I 帮助我们检测先验概率变化。它由两个主要 KPI 组成,即群体稳定性指数(PSI)和分化指数(DI)。
1.人口稳定指数
群体稳定性指数(PSI)比较两个数据集(训练和生产)的预测概率(分类)或连续预测输出(回归)的分布。因此,它有助于评估先验概率(即训练期间的预测产量分布)是否随着生产期间新数据的流入而发生了显著变化(新证据)。
计算 PSI 的步骤如下。这里,为了便于理解,我们考虑了训练数据和生产数据之间的比较(本系列第一篇文章中的场景 I这里)。但是这可以很好地应用于比较不同时间的两个生产数据实例(第一篇文章中的场景 II)。
- 按降序对训练数据集预测概率进行排序
- 将预测的概率分成 10 个桶(十分位数)
- 对于步骤(2)中形成的每个桶,找到概率值的最小值和最大值,即训练数据集中每个桶的分界点
- 计算落入每个这样的桶中的观察值的百分比(下表 I 中的预期百分比)
- 根据生产数据集中 10 个时段的概率分界点,计算生产数据集中每个时段的观测值百分比(下表 I 中的实际百分比)
- 最后,根据下面的公式计算 PSI。

上面的例子是在分类问题设置中使用预测概率。在回归的情况下,边界值将是输出预测值的连续值桶。

表一:人口稳定指数计算

图 3:分类问题中的 PSI。从训练数据到生产数据的预测输出概率分布变化。我们得到 PSI = 0.12,从而在预测输出中捕捉到这一微小的先验概率变化。(图片由作者提供)
如何作用于 PSI 值?
根据经验,以下是 PSI 的几个决策值点。
- 如果 PSI < 0.1 then there is not much prior probability shift in the predicted output and no change is required in the existing production model.
- However, if PSI > =0.1 且<0.2 then slight change may be required in the production model and it’s best to review some of the other KPIs as well to make a decision.
- If PSI> =0.2,则在预测输出中检测到显著的先验概率偏移,应根据最新数据重新训练生产模型。
关于什么是合适的决策点的进一步讨论可以从 Bilal Yurdakul 的作品中得到[1]
2.分歧指数
差异指数(DI)比较两个数据集(训练和生产)之间输出的聚合统计数据。与 PSI 一样,DI 也有助于评估先验概率(即培训期间的输出分布)是否在生产中发生了显著变化。但是 DI 是在总体水平上这样做的,并且使用实际的输出值(地面实况)而不是预测的输出。因此,DI 的一个限制是,由于采集生产数据地面实况可能会延迟或不总是可用,因此可能不总是能够进行计算。计算 DI 的步骤如下。
对于分类问题,我们计算训练数据集和生产数据集上的实际类别分布(对于每个类别)。我们确定预测类变量中唯一级别的数量,然后计算每个级别的交集并除以它们的并集。例如,如果训练数据集中的给定类变量有三个唯一级别,即良好、非常好和优秀,而生产数据集中有五个唯一级别,即差、一般、良好、非常好和优秀,则 DI 将计算为:
- DI = 1-(培训和生产中的普通级别/培训和生产中的级别的联合)= 1-(3/5) = 0.4
- 此外,在没有添加新水平的情况下,DI = 0,而对于两个数据集中完全不同的水平,DI = 1。
对于回归问题,我们简单地计算两个数据集之间的连续值输出的平均值的百分比变化。

图 4:分类问题中的 DI。生产数据有一个新类(黄色),该类在培训数据中不存在。因此,DI = 1-(2/3) = 0.33,这捕获了预测输出类别中的先验概率变化。(图片由作者提供)
如何作用于 DI 值?
前面针对 PSI 提到的相同的经验法则也可以用于 DI,以检测预测输出中的先验概率偏移的存在,并且因此可以采取模型再训练决策。
结论
机器学习模型预测提供洞察力并定义可操作性。监控模型以保持预测的有效性和相关性是至关重要的。第一类稳定性指标,如 PSI 和 DI,通过检测模型预测中的先验概率变化来帮助我们做到这一点。这些指标是人工智能系统中重要的监控工具。
参考
- Bilal Yurdakul,人口稳定指数的统计特性 (2018),WMU 的 ScholarWorks
- 约翰·克鲁施克等人,新人贝叶斯数据分析 (2017),施普林格
MLOps 监控市场回顾

卡观点,MLOps 监测公司市场评论 2021,https://stateofmlops.com,奥里科恩博士。
来自 stateofmlops.com 的
以下是 MLOps 监控领域公司的详尽列表,并按人物角色、支持的数据类型(表格、图像、音频等)、产品特性(数据完整性、数据质量、健康、漂移、偏见和公平、XAI 等)、产品关注点(以数据为中心或以管道为中心)、迄今为止的总资金(2021 年 8 月)、公司类型(创业、开源、公司)等进行了细分。我通过研究博客帖子、文档、产品演示和营销材料来收集数据。

图 1:2021 年 MLOps 监测公司市场回顾,【https://stateofmlops.com】T4,Ori Cohen 博士。
首先,如果您是数据科学家或 ML 工程师,并且正在问自己为什么我们需要一个专用的解决方案来监控 ML 系统,请阅读" Monitor!不要再做一个盲目的数据科学家。
如果你在问自己怎么没人监控依赖关系,那么我也写了一篇关于监控依赖关系的文章,由 O'Reilly 发表在《每个云工程师都应该知道的 97 件事》。
最后,如果您是一名高管或投资者,并且您想知道为什么模型监控对企业和企业高管很重要,请阅读以下文章。
分析

图 2:人物角色聚焦,MLOps 的状态,Ori Cohen 博士。
通过分析 MLOps 的当前空间(图 1)我们看到,这个空间中的大多数公司都是专注于数据科学家(图 2)、机器学习工程师和开发人员的初创公司,并且专注于表格数据(图 3);不足为奇的是,有相当多的人来自以色列。

图 3:数据类型焦点,MLOps 的状态,Ori Cohen 博士。
大多数公司专注于数据监控,少数公司专注于数据管道(例如 Dag,不要与传统的 APM 混淆)。

图 4:亚马逊,Azure,谷歌,MLOps 的状态,Ori Cohen 博士。
三大云提供商(图 4)正在提供相对基础到中级的功能,并且似乎没有成为同类最佳的计划。

图 5:卡维,https://stateofmlops.com,奥里·科恩博士。
AirTable 的一个很棒的特性是,您可以看到一个卡片视图,并按任意一个字段对其进行排序,如图 5 所示。
我们可以看到市场已经爆炸,有许多新的参与者分享相同的功能,但有自己的观点。投资于 MLOps 领域的金额高达 38 亿美元。我们看到小型但成熟的创业公司没有形成规模,因此过早退出。
我预见到该领域的整合,我相信最终,大公司会收购小公司,小公司会收购小公司,以吸引大公司。
最后,如果你问自己应该选择哪个解决方案,答案并不容易,尽管它们都有相同的基本功能,但现在还没有一个明确的赢家。每个公司都选择了自己的道路,无论是流程编排、洞察力、自定义指标、可配置性、集成、ML 特性等等。我现在的建议是了解您的模型监控需求,阅读他们的文档,请求演示,然后自己决定。
如果您发现任何不准确之处或想要添加您的公司,请通过以下方式联系我:LinkedIn|Medium|OriCohen.com|MLCompendium.com
Ori Cohen 博士拥有计算机科学博士学位,主要研究机器学习。他是新遗迹 TLV 的首席数据科学家,在 AIOps 领域进行机器和深度学习研究。
MLOps:部署后挑战
在生产中部署模型后,数据科学家面临的挑战
作为一名学生,当我过去学习和实现机器学习模型时,我认为部署是数据科学家的最后一步。但是当我开始职业生涯时,我的看法完全改变了。我很快意识到,在大多数实际的数据科学用例中,ML 模型的首次部署意味着工作只完成了一半。即使在部署之后,我们仍然面临着一些挑战。
它们可以分为两大类:
1)机器学习或统计学相关
2)软件工程问题
ML 相关:
数据漂移 : X 变化
数据漂移是数据分布随时间的变化。
例如:从 2020 年的 Q1 到 2021 年的 Q1,在线行为发生了怎样的变化。如果我们利用这些投入为营销团队开发了一些预算分配模型(如图所示),我们可能应该重新训练我们的模型,因为我们可以看到电影或电视流媒体服务随着时间的推移越来越受欢迎。

作者图片
概念漂移 : X- > Y 变化
当模型学习的模式不再适用时,就会出现概念漂移。
例如:如果我们部署了一个模型来预测客户流失,一段时间后,我们可以观察到不同功能的重要性随时间而变化。因此,模型需要重新训练。

作者图片
我们可以看到,随着时间的推移,装载时间变得越来越重要。
软件工程问题:
环境变化:一些使用的库可能会失去支持。最好密切关注它们,并在需要时更新它们
停止服务的云:假设我们在云上部署了一些 ML 模型,而云停止服务了一段时间。在这种情况下,我们可以在浏览器上部署一个可以离线运行的混合模型。人们可以检查这个链接以便更好地理解。
计算资源(CPU/GPU/内存):可能发生的情况是,我们已经使用非常强大的机器训练了我们的模型,但这对项目来说变得太昂贵了。
安全和隐私:这些都是我们不能妥协的领域。有许多与之相关的最佳实践。比如——在代码中不直接使用 ID 和密码等。如果你想深入了解,可以参考链接。
结尾注释:
如果您正在寻找或者刚刚在生产中部署了您的模型,我希望这篇文章能够帮助您预见挑战。数据科学世界正与日俱增。我很想知道您是否面临过任何其他部署后挑战。请写在评论区。
参考资料:-
1.https://evident lyai . com/blog/machine-learning-monitoring-data-and-concept-drift
2.https://towards data science . com/how-to-deploy-your-ml-models-to-the-browser-f 52 ea 62 BFA 60
MLOps 数据科学家最佳实践
构建生产机器学习系统
你可能已经听过很多了,但只有一小部分机器学习模型投入生产。对于大多数已经开始将 ML 应用于其用例的行业来说,部署和操作机器学习模型一直具有挑战性。在本文中,我将分享一些 MLOps 最佳实践和技巧,让您能够在生产中正确地使用您的 ML 模型。在开始之前,我们先来谈谈我们可能都知道的典型的 ML 项目生命周期。
ML 项目生命周期
一个典型的 ML 生命周期可以用下图来概括,主要由 3 个阶段组成。

ML 项目生命周期——受优步机器学习启发,来源:https://www . LinkedIn . com/pulse/do-Machine-Learning-Uber-way-five-lessons-from-first-Rodriguez/
在第一阶段,在我们深入研究数据之前,为成功做好准备是很重要的。因此,与业务专家一起,我们需要仔细定义我们的问题和业务目标!我们需要回答一些重要的问题,这些问题允许我们就模型和生产流水线的设计做出培训和服务决策。例如:
- 理想的结果是什么?
- 我们的评估标准是什么?我们如何定义 ROI?
- 成功和失败的标准是什么?
- 延迟要求是什么?我们能否在延迟要求范围内获得每项服务功能?…
在第二阶段,我们建立第一个 ML 模型的原型,或者换句话说,我们执行 ML 可行性研究。
因此,我们使用第一阶段定义的指标来证明 ML 业务价值。记住,ML 工程规则 1 的最佳实践是保持第一个模型简单,并获得正确的基础设施。第一个模型为我们的产品提供了最大的推动力,所以它不需要在一开始就是最花哨的模型。
在第三阶段,我们转向生产。这是本文的主题,因此我们将在接下来的章节中看到更多的细节。一旦我们的生产管道准备就绪并且设计良好,我们就可以更快更有效地收集见解和迭代新想法。
如今,数据科学家主要在做什么?
今天,大多数 ML 将机器学习模型投入生产的过程看起来都是这样的。作为一名数据科学家,它从一个 ML 用例和一个业务目标开始。有了这个用例,我们开始收集和探索来自不同数据源的相关数据,以理解和评估它们的质量。

如今,数据科学家主要在做什么?—受谷歌云技术启发,来源:https://cloud.withgoogle.com/next/sf/sessions?session=AI212
一旦我们对我们的数据有了感觉,我们就开始制作和设计一些我们认为对我们的问题有意义的特性。然后,我们进入建模阶段,并开始处理一些实验。在这个阶段,我们定期手动执行不同的实验步骤。对于每个实验,我们会做一些数据准备,一些功能工程和测试。然后,我们对我们认为特别有前途的任何模型或模型架构进行一些模型训练和超参数调整。
最后但并非最不重要的一点是,我们将评估所有生成的模型,根据维持数据集测试它们,评估不同的指标,查看性能,并将这些模型相互比较,看哪一个效果最好,哪一个产生的评估指标最高。整个过程是迭代的,反复手动执行,直到我们得到具有最佳性能的最佳模型。
一旦我们获得了性能最佳的模型,我们通常会将它放在某个存储中,然后将它扔给 it 和运营团队,他们的工作是将模型作为预测服务部署到生产中。不幸的是,我们认为我们的工作已经结束了。
ML 运营陷阱— 这种方法有什么问题?
以下是上述方法的错误之处。
手工:步骤高度手工化,每次都是从头开始写。每次数据科学家需要进行新的实验时,他都需要检查自己的笔记本,更新它们并手动执行。如果需要用新的训练数据刷新模型,数据科学家需要再次手动执行他的代码。
耗时:这种手动过程耗时且效率不高。
不可重用:写在笔记本上的自定义代码只能由作者自己理解,不能被其他数据科学家或跨其他用例重用或利用。甚至作者自己也可能在一段时间后发现有时很难理解他们的作品。
不可再现性:再现性是指被重新创造或复制的能力。在机器学习中,能够复制精确的模型非常重要。对于这里的手动过程,我们可能无法重现一个旧版本的模型,因为底层数据可能已经改变,代码本身可能已经被覆盖,或者依赖项和它们的确切版本可能没有被记录。因此,在出现问题的情况下,任何回滚到旧版本模型的尝试都是不可能的。
易错:这个过程会导致许多错误,如培训服务偏差、模型性能衰退、模型偏差、基础设施随时间崩溃…
- 培训服务偏差:当我们部署模型时,我们有时会注意到模型的在线性能完全低于我们在维持数据集上预期和测量的性能。对于操作机器学习模型,这种现象非常常见。训练和服务流水线之间的差异会引入训练服务偏差。训练服务偏差可能很难检测,并且可能使模型的预测完全无用。为了避免这个问题,我们需要确保对训练和服务数据都执行准确的处理功能,监控训练和服务数据的分布,监控模型的实时性能并将其与离线性能进行比较。
- 模型衰减:在大多数用例中,数据概要文件是动态的,并且随着时间而变化。当底层数据发生变化时,模型性能会下降,因为现有模式不再是最新的。静态模型很少继续服务于一个价值。我们需要确保使用新数据定期更新模型,并监控所服务模型的实时性能,以触发模型衰减。下图显示了一个已部署的模型是如何随时间衰减的,以及用一个新的模型更新模型的持续需求。

作者图片
- 模型偏差:人工智能系统的应用可能具有关键的性质,例如医疗诊断或预测,将人们的技能与工作配对,或者测试一个人是否有资格获得贷款。尽管这些应用看起来很实用,但这类系统中任何偏差的影响都可能是有害的。因此,未来人工智能系统的一个重要属性是对所有人的公平和包容。因此,对于任何机器学习模型来说,跨敏感特征(性别、种族……)衡量公平性都很重要。敏感特征取决于上下文。即使对于不敏感的功能,评估 AI 系统在不同子组上的性能也很重要,以确保我们在部署模型之前知道任何表现不佳的子组。

按作者分类的图片—由微软的 Fairlearn 工具生成的图片
- 可扩展性:可扩展性在机器学习中很重要,因为训练一个模型可能需要很长时间,因此优化一个需要数周训练的模型是不可行的。一个模型可能会太大,以至于无法放入训练设备的工作存储器中。即使我们决定纵向扩展,成本也会比横向扩展高。在某些情况下,数据量可能不大,因此在开始时可能不需要可伸缩性,但是我们应该考虑,随着持续的训练,我们预期接收的训练数据量是否会随着时间的推移而增加,并且可能会给我们设置的基础架构带来内存问题。
ML 系统的主要组件
在这一节中,我们将描述 ML 系统的主要组成部分和围绕它们的最佳实践,这将使我们避免上述缺陷。
交付集成 ML 系统并在生产中持续运行该系统的过程包括以下步骤:

ML 系统的主要组成部分—作者图片
让我们详细讨论一下每个管道组件。
数据摄取:
这个组件通常在我们用例的 ML 管道之外。在成熟的数据流程中,数据工程师应优化持续的数据接收和转换,以持续向组织内的不同数据分析实体提供最新数据,这些实体期待发现数据驱动的见解和更明智的决策。
数据验证:

作者图片
在这个组件中,我们的重点是验证提供给管道的输入数据。人们不能低估这个问题在 ML 系统中的重要性。不管采用什么样的 ML 算法,数据中的误差都会严重影响生成模型的质量。正如一个流行的数据科学概念所说的“垃圾进,垃圾出”。因此,及早发现数据错误至关重要。
无误差数据的另一个作用是在模型输出分析方面。该组件允许我们正确理解和调试 ML 模型的输出。因此,数据必须被视为 ML 系统中的一等公民,就像算法和基础设施一样。在每次执行 ML 流水线时,都必须对其进行持续监控和验证。
在模型训练之前也使用该步骤来决定我们是否应该重新训练模型(在数据漂移的情况下)或者停止流水线的执行(在数据异常的情况下)。
以下是数据验证组件的典型行为:
- 它计算并显示关于数据的描述性统计,它还可以显示连续数据范围(即,在当前管道执行 N 和最后管道执行 N-1 之间)的描述性统计,以查看数据分布如何变化。

- 它推断出代表使用中数据的数据模式。

- 它检测数据异常。它应该检查数据集是否匹配预定义的经验证的模式。它应该检测连续数据跨度之间(即,当前流水线执行 N 和最后流水线执行 N-1 之间)的数据漂移,例如不同天的训练数据之间的数据漂移。它还应该通过比较训练数据和在线发球数据来检测训练发球偏斜。

在生产中,通过不断的训练,下面是一个示意图,它生成关于新到达的数据的统计数据,对其进行验证,并生成异常报告:

数据转换

作者图片
在这一步中,数据是为 ML 任务准备的。这包括数据清理、过滤、数据转换和功能争论。它应该做一些事情,比如生成特征到整数的映射。此外,该组件准备训练器组件中可能需要的特征元数据(例如,这包括特征标准化的训练步骤中需要的元参数、分类变量编码所需的字典等)。这些被称为转换工件;他们帮助构建模型输入。
重要的是,无论生成什么映射,都必须保存并在服务时间重用(当训练好的模型用于进行预测时)。如果不能始终如一地做到这一点,就会导致我们前面谈到的培训服务不对称问题。

作者图片
模特培训

作者图片
模型训练组件负责训练我们的模型。在大多数用例中,模型可以训练几个小时,几天,甚至几周。优化一个需要数周训练的模型是不可行的。在其他情况下,用于训练模型的数据甚至不适合内存。
在这种情况下,模型训练组件应该能够支持数据和模型并行性;并且扩展到大量的工人。它还应该能够处理内存不足的数据。
理想情况下,我们的 ML 系统的所有组件都应该是可伸缩的,并且运行在支持可伸缩性的基础设施上。
这个模型训练组件还应该能够在训练时自动监控和记录一切。我们无法在很长一段时间内训练一个机器学习模型,而不看看它是如何做的,并确保它被正确配置以最小化迭代次数的损失函数。最后,训练组件还应该支持超参数调整。
模型分析

作者图片
在模型分析组件中,我们对训练结果进行深入分析,并确保我们导出的模型具有足够的性能,可以推向生产。
这一步有助于我们保证,只有当模型满足我们在框架阶段预设的质量标准时,它才会被提升用于服务。标准必须包括与以前部署的模型相比改进的性能,以及在各种数据子集/切片上的公平性能。在下图中,我们在特征切片 trip_start_hour 上显示了我们的训练模型的性能。

来源:https://blog . tensor flow . org/2018/03/introducing-tensor flow-model-analysis . html
这一步的输出是一组性能指标和一个关于是否将该模型推广到生产的决策。
模特上菜
与我们通常关心数据和模型复杂性的训练组件相反。在服务组件中,我们感兴趣的是通过最小化响应延迟和最大化吞吐量来响应可变的用户需求。

作者图片
因此,服务组件应该具有低延迟以快速响应用户,高效以便在需要时可以同时运行许多实例,水平伸缩,可靠并对故障具有鲁棒性。
我们还需要我们的服务组件能够轻松地更新到模型的新版本。当我们获得新数据或触发新的管道运行,或测试新的模型架构想法时,我们会希望推出新版本的模型,我们希望系统无缝过渡到这个新版本。
监控
正如我们前面提到的,由于不断发展的数据配置文件,我们部署的 ML 模型的性能会随着时间的推移而下降,我们需要确保我们的系统正在监控并响应这种下降。
因此,我们需要跟踪数据的汇总统计,并监控模型的在线性能,以便发送通知,在值偏离我们的预期时回滚,或者潜在地调用 ML 流程中的新迭代。

来源:https://ml-ops.org/content/mlops-principles
因此,在线监控是检测性能下降和模型过时的关键。它作为新的实验迭代和基于新数据的模型再训练的线索。
管道编排组件
我们刚刚描述的步骤的自动化水平定义了我们的 ML 系统的成熟度,它也反映了由模型衰减或给定的新数据触发的训练新模型的速度。
在当前的许多用例中,手动过程碰巧非常普遍。当模型由于静态数据分布而很少改变时,这可能就足够了。但在实践中,这种情况很少发生。数据通常是动态的,模型在现实世界中部署时会经常崩溃。静态模型肯定无法适应描述环境的数据的变化。
手动过程也可能是危险的,因为它会造成 ML 培训和 ML 服务之间的脱节。它将创建模型的数据科学家和作为预测服务操作模型的工程师分开。并且这个过程会导致训练服务偏斜问题。
编排组件的目标是连接系统的不同组件。它按顺序运行管道,并根据定义的条件自动从一个步骤移动到另一个步骤。这是自动化的第一步,因为我们现在可以使用基于实时管道触发器的新数据自动训练生产中的新模型。

作者图片
我们需要注意的是,在生产中,我们不会将一个经过训练的模型作为预测服务来部署。实际上,我们正在部署一个完整的训练管道,它会自动循环运行,为经过训练的模型提供预测服务。
管道元数据存储
管道元数据存储的作用是记录 ML 管道执行的所有细节。这对于保持组件之间的血统和在任何需要的时候重现部署的模型是非常重要的。它还帮助我们调试遇到的任何错误。
每次我们执行管道时,商店都会记录有关管道执行的所有详细信息,例如:

作者图片
- 执行的管道和组件源代码的版本。
- 传递给管道的输入参数。
- 由我们的管道的每个执行组件产生的工件/输出,例如到原始数据、转换数据集、验证统计和异常、训练模型的路径…
- 模型评估度量,以及关于模型部署的模型验证决策,它是在模型分析和验证组件中产生的…
CI/CD 流水线自动化
到目前为止,我们只讨论了如何自动化 ML 管道的连续执行,以根据触发器(如新数据的可用性或模型衰退)重新训练新模型,从而捕获新的新兴模式。
但是如果我们想要测试一个新的特性,一个新的模型架构,或者一个新的超参数呢?这就是自动化 CI/CD 管道的意义所在。CI/CD 管道允许我们快速探索新的想法和实验。它让我们能够自动构建、测试和部署新的管道及其组件到预期的环境中。
以下是 CI/CD 管道自动化对连续 ML 管道自动化的补充:
- 如果给定新的实现/代码(新的模型架构、特征工程和超参数…),一个成功的 CI/CD 管道部署一个新的连续 ML 管道。
- 如果给定新数据(或模型衰减触发器),成功的自动化连续管道部署新的预测服务。为了用新数据训练新的 ML 模型,对新数据执行先前部署的 ML 管道。

作者图片
完整的端到端自动化管道应该是这样的:

作者图片——灵感来自谷歌云技术,来源:https://cloud.withgoogle.com/next/sf/sessions?session=AI212
- 我们迭代地尝试新的 ML 想法,其中我们的一些管道组件被更新(例如引入一个新的特性将看到我们更新数据转换组件……)。这个阶段的输出是新的 ML 管道组件的源代码,然后这些组件被推送到目标环境的源存储库。
- 新源代码的出现将触发 CI/CD 管道,CI/CD 管道将反过来构建新的组件和管道,运行相应的单元和集成测试以确保所有内容都正确编码和配置,并且如果所有测试都已通过,则最终将新管道部署到目标环境。ML 系统的单元和集成测试本身值得一篇独立的文章。
- 新部署的管道在生产中基于时间表、新训练数据的存在或响应触发器而自动执行。此阶段的输出是一个经过训练的模型,该模型被推送到模型注册中心并被持续监控。
为什么是 Tensorflow?
在这最后一节,我想提到为什么 Tensorflow 是我开发集成 ML 系统的首选框架。当然,TensorFlow 可能并不适合所有用例,有时甚至可能是大材小用,尤其是在不需要深度学习的时候。然而,我倾向于尽可能使用 Tensorflow,原因如下:
- Tensorflow 附带 Tensorflow Extended (TFX)。TFX 允许我们专注于优化 ML 管道,而不去关注每次都重复的样板代码。像数据验证和模型分析这样的组件可以很容易地完成,而不必开发定制代码来读取我们的数据并检测 2 个管道执行之间的异常。使用 TFX,这可以用很少的几行代码来完成,从而节省了我们开发管道组件的大量时间。数据验证和模型分析组件中的屏幕截图来自 TFX。将来我会努力写一篇关于 TFX 的文章。
- 我们可以使用 TF layers API、TF losses APIs 等从层中构建定制模型。如果我们正在构建一些相当标准的东西,TensorFlow 有一套预制的评估工具,我们可以试用。Tensorflow 2 适用于 Keras 模型。
- 随着数据和训练时间的增长,我们的需求也会增加。检查点允许我们在需要的时候暂停和恢复训练,如果预设的次数不够,可以继续训练。
- Tensorflow 设计有 dataset API,可以很好地处理内存不足的数据集。
- 模特训练可能需要几个小时,有时甚至几天。我们不能在不检查模型是否按预期运行的情况下长时间训练我们的模型。Tensorboard 是 TensorFlow 的可视化工具包。TensorBoard 提供了机器学习实验所需的可视化和工具。它允许我们在训练过程中显示实时生成的 TensorFlow 关键指标,并在训练集和验证集上可视化它们,以查看我们的模型是否正确配置为收敛。如果不是这样,这将允许我们停止训练。
- 我们可以在集群上分布 TensorFlow,让它更快。从一台机器到多台机器可能听起来很复杂,但有了 Tensorflow,我们可以开箱即用地进行分发。TF 抽象出了用于训练和评估的分布式执行的细节,同时还支持跨本地/非分布式和分布式配置的一致行为。
参考
Eric Breck,Neoklis Polyzotis,Sudip Roy,Steven Euijong Whang,Martin Zinkevich,机器学习的数据验证 (2019),SysML 大会【https://systemsandml.org
使用 TFX、Kubeflow 管道和云构建的 MLOps 架构
MLOps:特性存储的作用
要素存储就像数据科学的数据仓库。他们的主要目标是使数据科学家能够缩短从数据摄取到 ML 模型训练和推理的时间。因此,它们填补了 MLOps 生命周期中的一个重要空白。
什么是功能商店?
特征库是一个数据管理系统,用于管理机器学习特征,包括特征工程代码和特征数据。它是一个中央保险库,用于存储文档化、管理化和访问受控的功能,这些功能可以在整个组织的许多不同 ML 模型中使用。它从各种来源获取数据,并执行定义的转换、聚合、验证和其他操作来创建特征。特征存储注册可用的特征,并使它们准备好被 ML 训练管道和推理服务发现和使用。
MLOps 生命周期

图 1: MLOps 生命周期(图片由作者提供)
我们在很高的层次上定义了 MLOps 生命周期,包括图 1 所示的开发(“Dev”)和生产(“Prod”)环境中的以下组件。
开发环境
- data ops:ml ops 生命周期的第一步涉及数据的所有方面,从构建数据接收管道到从各种来源获取数据。随后是数据验证&验证,在摄取管道中整合验证逻辑。接下来是数据准备,通过处理、转换,最后用适当的版本保持它。这些步骤构成了在开发环境中根据批处理或实时数据构建和测试的数据管道。
- ModelOps :获取数据后,进行探索性数据分析(EDA)以获得初步见解并生成报告。理解 EDA 后的数据有助于下一步——特征工程。然后使用这些特征开发 ML 算法。然后使用版本控制系统(例如 git)对算法代码进行理想的版本控制,并进行测试以确保代码按预期运行。最后,部署数据和模型管道,以便在开发环境中执行和测试。一旦部署了管道,就会使用不同的输入值和不同的模型架构进行各种实验。根据模型度量来评估结果。此外,还对模型进行了解释和误差分析,以便更好地理解模型并改进其性能。最后,从各种实验中选择最佳模型,部署到产品中。
生产环境
- DataOps & ModelOps :使用服务工具将内置于 Dev 中的数据和模型管道部署在 Prod 中。部署后,数据管道获取、验证和准备要进行推理的数据。然后,模型管道使用推断数据来生成预测/报告。
- 生产操作:跨生产中的数据操作&模型操作,持续监控数据&模型性能,以确保无漂移。各种度量帮助我们做同样的事情,我们可以从本系列的早期文章这里了解更多。这种监控启动了一个重要的反馈循环,返回到开发周期,用于进一步的模型重新训练和/或其他更改(如果需要),并完成我们的 MLOps 生命周期。此外,还处理各种治理(用户控制、道德、偏见等)和安全(数据、模型)方面。
MLOps 生命周期的差距
有人可能想知道为什么特性存储在上面的讨论中被忽略了。组织已经实现了各种风格的功能存储,并且在大多数情况下,它作为 ModelOps 的一部分。然而,一个特征库应该被单独划分出来(如下图 2 所示),因为它可以使特征操作,引导在模型操作期间加快&实验时间。它还减少了组织内多个用例中模型操作的冗余。

图 MLOps 生命周期中的差距—功能操作
差距:功能操作是当前 MLOps 生命周期中的差距。在没有 it 的情况下,组织&数据科学家做大量重复的工作,一次又一次地创建相同的功能,然后针对不同的用例验证它们。这大大加快了上市时间。进一步的初学者数据科学家可能无法在他们的建模中构思重要的特征,而这些特征可能已经由高级团队成员在更早的时候完成了。这阻碍了敏捷开发和有效的团队协作。
我们将在下一篇文章中讨论 FeatureOps 的更多细节,在那里我们将了解特性库的主要组件和功能。现在,重要的是要注意到,在 2020 年之前,没有一个大的 ML 平台提供商(AWS,GCP,Azure)有一个针对该功能的定义良好的产品。最近在 2020 年 12 月,亚马逊宣布了 SageMaker 功能商店,随后谷歌在 2021 年 5 月发布了他们的 MLOps 平台 VertexAI,该平台具有功能商店组件。最后,Databricks 在 2021 年 6 月发布了 Azure 平台支持的特性商店实现的公开预览版。除此之外,我们还有像 Hopsworks 和 Tecton 这样的初创公司,以及像 Feast 这样的开源项目,它们在功能商店中处于领先地位。Abzooba Inc .有一个名为 xpresso.ai 的 MLOps 平台,它建立在这里讨论的许多概念之上。因此,整个行业都在努力填补 MLOps 生命周期中的这一空白。
结论
人们常说,数据科学家 80%的时间都花在了数据争论上,包括获取数据、接收数据、清理数据和分析数据特征等任务。一个定义良好的功能库使功能可以在一个地方使用,从而缩短了时间。它还促进团队协作、重用,并导致更快的模型实验和推理结果。
MLOps:终极指南
一本关于 MLOps 和如何思考它的手册

来源:作者
What 是 MLOps?
你会在 MLOps 上发现相互矛盾的定义:它是一场运动,一种哲学,一个平台,还是一个职位?大多数要么太模糊——一种“哲学”——要么太具体,仅仅指一个特定的工具集。
以下是我们对 MLOPs 的定义:
MLOps 是一个行业公认的最佳实践的集合,用于管理您的机器学习团队中的代码、数据和模型。
这意味着 MLOps 应该在以下方面帮助您的团队:
- 管理代码: MLOps 鼓励标准的软件开发最佳实践,并支持持续的开发和部署。
- 最佳实践:指导方针确保您从创意到实验,再到在生产中部署可靠的模型,实现无缝过渡。
- 管理数据:框架和工作流有助于处理、保存和跟踪数据集的版本。
- 高效协作:团队可以共享代码、数据、模型和实验;跑一跑,了解一下对方的工作;并重复以前的工作。
- 管理模型:您可以轻松地训练模型,跟踪实验结果,并部署健壮的 API。
MLOps 是一个广阔的领域,因此我们将从较高的层面来看待这个领域,然后深入探讨采用它时会遇到的主题。
MLOps 前景
决定使用最好的工具以及如何开始可能很难。我们的机器学习工具页面提供了最有用工具的概述和简单解释。

概述了许多 MLOps 架构中最有用的机器学习工具和平台。来源:作者
这些 MLOps 工具涵盖了从数据争论、可视化和任务自动化到培训和部署的所有内容。我们专注于开源选项,但也有这么多专有平台可用,这值得吗?
MLOps:构建与购买
许多商业平台旨在使 MLOps 更简单,而“构建 vs 购买”是许多团队提出的问题。人们经常“购买”是因为他们对构建自己的工具或使用开源选项缺乏信心。昂贵的专有平台有望简化所有的复杂性。
但是专有工具经常不能兑现它们的承诺,最终反而会耗费和限制你的团队。他们保证的捷径往往是不可能的;您的团队仍然需要内部的 MLOps 专业知识来有效地使用它们。
这就是为什么我们强烈支持为 MLOps 开发开源工具的原因。免费的开源工具通常是正确的选择:它们导致更低的成本、更大的灵活性、更多的学习、和更容易入职——不管专有平台会让你相信什么。
如果您信任您的工程团队,我们建议从现有的开源组件构建您自己的解决方案,而不是试图将您的独特需求融入到其他人的专有平台中。
但是,如果你想建立自己的,你如何开始没有浪费几个月的时间研究所有的选项?我们寻找一个开源的 MLOps 蓝图,但找不到,所以我们建立了它。
现在,您可以跳过我们几个月的研究和工程工作,在几个小时内建立一个开源的、以生产为中心的 MLOps 框架。
开源、现成的 MLOps 架构

生产机器学习解决方案需要处理数据流、实验、特征和服务。来源:作者
如果您既想要自己解决方案的强大和灵活性,又想要托管专有解决方案的简单和快速,请看看我们的开放式 MLOps 架构。这是一套 terraform 脚本,用于设置我们在 Kubernetes 上内部使用的相同系统,您可以在不到一天的时间内完成设置。
以下是开放式 MLOps 的优势:
- 免费、灵活、开源:我们完全使用开源工具构建了开放的 MLOps。这意味着,如果您有其他需求,很容易适应:只需将组件换成其他工具或您自己的定制解决方案。
- 容易上手:很多 MLOps 工具都有很陡的学习曲线。我们已经编写了逐步指南,这样你可以在几个小时内完成一个示例项目,然后开始运行你自己的项目。
- 可扩展 : Open MLOps 运行在 Kubernetes 上,因此可以根据您的需求轻松扩展或缩小。如果您正在运行巨大的工作负载,您可以增加更多的计算能力。如果预算有限,可以在小型集群上运行。
开放式 MLOps 包括以下组件:
- JupyterHub ,一个供你的团队协作的共享笔记本环境。
- ml flow跟踪你的实验和模型。
- 提督 管理您的工作流程和预定任务。
- 谢顿 把你的模型生产化,变成 API。
您可以在现有的 Kubernetes 集群中设置一切,或者运行我们的简单设置脚本,使用 AWS EKS 创建一个新的集群。即使您使用预构建的解决方案,您可能仍需要构建 MLOps 的内部专业知识,因此我们列出了一些好的资源来帮助您开始。
MLOps 资源
我们已经写了很多关于 MLOps 的文章。以下是我们的最爱,在您 MLOps 旅程的不同阶段为您提供帮助。
MLOps 和机器学习入门
如果您希望采用 MLOps,并希望在开始构建之前了解更多信息,您应该从以下几点开始:
- 进行机器学习的四种方式:在高层次上理解采用机器学习的不同方式。
- 面向研究团队的 MLOps:理解为什么 MLOps 不仅面向行业,对研究团队也很重要。
- 机器学习架构组件:了解大多数机器学习解决方案的重要部分。
- 面向 MLOps 的开源软件:理解为什么开源通常比专有解决方案更好。
- 软件开发 vs 机器学习工程:理解为什么机器学习工程有额外的挑战,甚至会绊倒有经验的软件工程师。
- MLOps for Model Decay :理解为什么你不能“解雇并忘记”你的模型,即使它们表现得很好。
- 为什么模型注册中心很有价值? : 理解跟踪你的模型是如何以及为什么重要。
- 为什么您需要模型服务工具:了解如何在生产环境中服务您的模型。
选择工具来设置您自己的 MLOps 平台
一旦您选择采用 MLOps,您将需要一些特定的工具和平台。鉴于选项的广泛性,这可能是最难的部分,所以我们比较了我们最喜欢的,并删减了营销用语,以使您更容易理解。
- 选择特色店:为什么需要特色店,如何选择。****
- 为什么我们喜欢将 Prefect 作为一个数据和工作流平台:为什么你应该考虑将 Prefect 作为你的任务调度器和工作流工具。
- Kubeflow:还没准备好生产?:我们在切换到 Prefect 之前发现的 Kubeflow 的问题。
- 比较 MLOps 平台:data iku vs Alteryx vs SageMaker vs data bricks
- 比较仪表板解决方案:流线型与破折号与闪亮与瞧吧
- 比较工作流平台:air flow vs Luigi vs Prefect vs ml flow vs kube flow
- 对比数据角力工具:熊猫 vs Dask vs Vaex vs 摩丁 vs Ray
设置打开的 MLOps
一旦你选择了你的工具,你需要设置和配置它们。如果您想模仿我们在 Data Revenue 内部使用 Open MLOps 的设置,我们已经创建了一些分步指南来快速启动和运行 JupyterHub、Prefect、Seldon、Dask 和 MLFlow。
- 建立开放的 MLOps:一个用于机器学习的开源产品架构
- 在打开的 MLOps 上使用 JupyterHub、提督和 ml flow
- 使用 MLFlow、Prefect 和 Seldon 将您的模型部署为 API
获得帮助
如果你需要帮助建立你的 MLOps 团队,请随时联系。
MLOps 与 DevOps
本文介绍了 DevOps 和 mlop 之间的相似之处和不同之处,以及有助于实现 mlop 的平台

HalGatewood.com在 Unsplash 上拍照
随着近年来机器学习领域的成熟,将自动持续集成(CI)、持续交付(CD)和持续培训(CT)集成到机器学习系统的需求已经增加。DevOps 哲学在机器学习系统中的应用被称为 MLOps。MLOps 的目标是将机器学习系统开发(ML)和机器学习系统操作(Ops)融合在一起。
什么是 DevOps?
DevOps 是个人和团队在开发软件系统时使用的实践。个人和团队可以通过 DevOps 文化和实践获得的好处包括:
- 快速开发生命周期
- 部署速度
- 通过使用测试来保证代码质量。
为了实现这些好处,在软件系统的开发过程中使用了两个关键概念。
- 持续集成:将代码库合并到一个中央代码库/位置,自动化软件系统的构建过程,并测试代码库的组件。
- 连续交付:自动化软件部署。
机器学习系统与软件系统相似,但不完全相同。主要区别在于:
- 团队中存在的技能:开发机器学习模型/算法的人通常没有软件工程背景,并且主要关注概念证明/原型阶段。
- 机器学习系统本质上是高度实验性的。在没有首先尝试和做一些实验的情况下,不能保证算法会预先成功。因此,需要跟踪不同的实验、特征工程步骤、模型参数、度量等。
- 测试机器学习系统不仅仅是单元测试。您还需要考虑诸如数据检查、模型漂移、评估部署到生产中的模型性能之类的事情。
- 机器学习模型的部署是非常定制的,取决于它试图解决的问题的性质。它可能涉及多步骤管道,包括数据处理、特征工程、模型训练、模型注册和模型部署。
- 需要随着时间的推移跟踪数据的统计和分布,以确保模型今天在生产环境中看到的与它被训练的数据一致。
m lops 和 DevOps 的相似之处
- 开发人员、数据科学家和数据工程师之间代码库的持续集成。
- 机器学习系统代码的代码和组件的测试。
- 将系统持续交付生产。
【MLOps 和 DevOps 的区别
- 在 MLOps 中,除了测试代码之外,您还需要确保在机器学习项目的整个生命周期中保持数据质量。
- 在 MLOps 中,您可能不需要仅仅部署一个模型工件。机器学习系统的部署可能需要涉及数据提取、数据处理、特征工程、模型训练、模型注册和模型部署的机器学习流水线。
- 在 MLOps 中,还有 DevOps 中没有的第三个概念,即持续培训(CT)。该步骤完全是关于自动识别由于当前部署的机器学习模型/系统中的性能退化而需要模型被重新训练和重新部署到生产中的场景/事件。
如果你有兴趣了解部署机器学习模型的不同方式,请查看我的文章(https://towards data science . com/machine-learning-model-deployment-options-47 C1 F3 d 77626)。
协助 MLOps 的平台和工具
- ml flow(https://mlflow.org/):这是一个开源平台,它帮助和支持模型跟踪、模型注册和模型部署步骤。我也相信这是 Azure 机器学习在他们的平台上使用的。Databricks 已经将 mlflow 整合到他们的平台上。
- 版本控制系统如:
-github
-git lab
-Azure devo PS - 云服务进行实验和部署机器学习管道:
-AWS SageMaker(https://aws.amazon.com/sagemaker/)
-Azure 机器学习(https://docs . Microsoft . com/en-us/Azure/Machine-Learning/overview-what-is-Azure-ml)
-data bricks(https://databricks.com/)
如果你有兴趣了解更多关于 AWS SageMaker 和 Azure 机器学习之间的异同,请查看我的文章(https://towardsdatascience . com/AWS-sage maker-vs-Azure-Machine-Learning-3ac 0172495 da)
与 Docker 和 Jenkins 的 MLOps:自动化机器学习管道
如何用 Docker 将你的 ML 模型容器化,用 Jenkins 将管道自动化

在 Unsplash 上由 Bernd Dittrich 拍摄的照片
介绍
这篇文章的目的是提供一个例子,说明我们如何使用 Docker 和 Jenkins 这样的 DevOps 工具来自动化机器学习管道。在这篇文章的最后,你将知道如何用 Docker 封装一个机器学习模型,并使用 Jenkins 创建一个管道,该管道自动处理原始数据,训练模型,并在每次我们对我们的存储库进行更改时返回测试准确性。
这个帖子需要的所有代码都可以在 Github 中找到。
对于这项任务,我们将使用成人人口普查收入数据集。目标变量是收入:一个二元变量,表示个人年收入是否超过 5 万英镑。
📒注意:由于本文的目的是自动化机器学习管道,我们不会深入研究 EDA,因为这超出了范围。如果你对此感到好奇,你可以查看这个 Kaggle 笔记本,但这并不是强制性的,以便了解这里做了什么。
好,那我们开始吧!
制定战略
在开始编码之前,我认为重要的是理解计划是什么。如果你看一下 Github 库你会看到三个 python 脚本:通过查看它们的名字就很容易知道它们是做什么的:)。我们还有原始数据集:成人. csv 和一个 docker 文件(我们将在后面讨论)。但现在我想让你了解这个项目的工作流程,为此,我们需要做的第一件事是了解我们的 Python 脚本的输入和输出是什么:

作者图片
正如我们在图中看到的,preprocessing.py 将原始数据作为输入,输出处理后的数据,分成训练和测试两部分。train.py 将训练处理后的数据作为输入,并输出模型和一个 json 文件,我们将在其中存储验证准确性。test.py 将测试处理后的数据和模型作为输入,并输出一个具有测试精度的 json 文件。
考虑到这一点,现在我们有了一堆必须按一定顺序运行的脚本,它们创建了一堆我们需要存储和访问的文件。此外,我们希望自动化所有这一过程。
现在,处理这个问题的最好方法是使用 Docker:使用这个工具,你可以创建一个独立的环境,拥有运行你的代码所需的所有依赖关系(解决“它在我的机器上工作”的问题!)那就更容易了。一旦我们有了这些,我们就能自动处理 Jenkins 的所有流程。
Docker 基于 3 个概念:容器、图像和 Docker 文件。为了与 Docker 合作,了解他们的工作是不可或缺的。如果你不熟悉它们,这里有一个直观的定义:
- 容器:一个标准的软件单元,它打包了运行应用程序所需的一切(依赖项、环境变量……)
- Dockerfile:这是一个文件,你可以在其中定义你想在容器中包含的所有内容。
- 图片:这是运行容器所需的蓝图。您可以通过执行 Dockerfile 文件来构建映像。
所以,为了使用 Docker,你将遵循以下步骤:
- 定义 Dockerfile 文件
- 建立形象
- 运行集装箱
- 在容器内运行命令
我们一步一步来。
定义 Dockerfile 文件
在这里,我们必须定义运行管道所需的一切。你可以看一下库中的 Dockerfile,但是如果你不熟悉语法,一开始可能会有些不知所措。所以我们在这里要做的是谈论我们想要在它里面指定什么,并且一步一步地看语法。
首先,我们需要指定我们希望在哪里运行管道。对于大多数容器化的应用程序,人们会选择轻量级的 Linux 发行版,比如 alpine。然而,对于我们的管道,我们将只使用一个名为jupyter/scipy-notebook的 jupyter 图像。在 Dockerfile 文件中,我们指定以下命令:
FROM jupyter/scipy-notebook
然后,我们必须安装一些软件包。为此,我们使用命令RUN:
USER root RUN apt-get update && apt-get install -y jq RUN pip install joblib
📒注意:现在这可能没有多大意义,但是我们需要jq来访问 json 文件中的值,还需要joblib来序列化和反序列化模型。
接下来我们要设置的是容器中文件的分布。我们想建造一个内部具有这种结构的容器:

作者图片
📒注意:“工作”文件夹由 Docker 自动生成。我们不会在里面放任何东西。
首先,我们创建文件夹:
RUN mkdir model raw_data processed_data results
然后我们将目录设置为环境变量(这样我们就不会在整个代码中硬编码路径)
ENV MODEL_DIR=/home/jovyan/modelENV RAW_DATA_DIR=/home/jovyan/raw_dataENV PROCESSED_DATA_DIR=/home/jovyan/processed_dataENV RESULTS_DIR=/home/jovyan/resultsENV RAW_DATA_FILE=adult.csv
最后,我们设置从存储库中复制脚本和原始数据的顺序。一旦我们创建了容器,它们就会被粘贴到我们的容器中。
COPY adult.csv ./raw_data/adult.csv COPY preprocessing.py ./preprocessing.py COPY train.py ./train.py COPY test.py ./test.py
建立形象
一旦我们指定了 docker 文件,我们就可以构建映像了。执行此操作的命令是:
sudo -S docker build -t adult-model .
我们用-t adult-model (-t 代表标签)指定图像的名称,用.指定 Dockerfile 的路径。Docker 会自动选择名为“Dockerfile”的文件。
运行容器
现在我们有了一个图像(一个容器的蓝图),我们可以构建一个容器了!
📒注意:我们将只构建一个容器,但是如果你不知道,一旦我们有了一个图像,我们就可以构建任意多的容器了!这带来了广泛的可能性。
运行容器的命令如下:
sudo -S docker run -d --name model adult-model
其中-d 标志表示分离(在后台运行容器)。我们将其命名为“模型”,并指定我们使用的图像(成人模型)。
在容器内运行命令
现在我们已经运行了我们的容器,我们可以通过使用docker exec在里面运行命令。在这个项目中,我们需要按顺序执行脚本,然后显示结果。我们可以通过以下命令来实现:
- 运行预处理. py
sudo -S docker container exec model python3 preprocessing.py
- 运行 train.py
sudo -S docker container exec model python3 train.py
- 运行 test.py
sudo -S docker container exec model python3 test.py
- 显示验证准确性和测试准确性
sudo -S docker container exec model cat \
/home/jovyan/results/train_metadata.json \
/home/jovyan/results/test_metadata.json
📒注意:如果你足够好奇(我猜你是),你会想知道每个脚本实际上是做什么的。别担心,如果你熟悉基本的机器学习工具(这里我基本用的是熊猫和 SKlearn 库),你可以打开脚本看一下代码。没什么大不了的大部分台词都被评论了。如果你想深入了解或者你正在寻找比这里显示的更复杂的模型,你可以看看这个笔记本。
测试步骤
在构建管道时,通常会有一个专门的步骤来测试应用程序是否构建良好,是否足以部署到生产中。在这个项目中,我们将使用一个条件语句来测试验证准确性是否高于阈值。如果是,则部署该模型。如果没有,该过程停止。执行此操作的代码如下:
val_acc=$(sudo -S docker container exec model jq .validation_acc \ /home/jovyan/results/train_metadata.json)threshold=0.8
if echo "$threshold > $val_acc" | bc -l | grep -q 1
then
echo 'validation accuracy is lower than the threshold, process stopped'
else
echo 'validation accuracy is higher than the threshold'
sudo -S docker container exec model python3 test.py
sudo -S docker container exec model cat \ /home/jovyan/results/train_metadata.json \ /home/jovyan/results/test_metadata.json
fi
如您所见,首先我们设置两个想要比较的变量(验证准确性和阈值),然后我们通过一个条件语句传递它们。如果验证准确性高于阈值,我们将为测试数据执行模型,然后我们将显示测试和验证结果。否则,该过程将停止。
我们做到了!我们的模型是完全容器化的,我们可以运行管道中的所有步骤!
Docker 初学者的问题:我们所做的有什么意义?
如果你不熟悉 Docker,现在你可能会问:好吧,这些都是好东西,但最终我只有我的模型和我的预测。我也可以通过运行我的 Python 代码来获得它们,而不需要学习 Docker。那么,这一切有什么意义呢?
我很高兴你问了😎。
首先,将您的机器学习模型放在 Docker 容器中对于将该模型部署到生产环境中非常有用。举个例子,你有多少次在教程或库中看到你试图复制的代码,当在你的机器上运行相同的代码时,你的屏幕充满了红色?如果我们不愿意经历这些,想象一下我们的客户会有什么感受。有了 Docker 容器,这个问题就解决了。
Docker 真正有用的另一个原因可能与您阅读本文的原因相同:帮助自动化整个管道。
所以,事不宜迟,我们开门见山吧!
利用 Jenkins 实现 ML 管道的自动化
对于这一步,我们将使用 Jenkins,这是一个非常著名的开源自动化服务器,它提供了一系列插件来支持构建、部署和自动化任何项目。
这一次,我们将使用名为 jobs 的工具来构建管道的步骤。每项工作都将是我们工作流程中的一步。
📒注意:为了让事情顺利运行,您可能需要配置一些东西:
- 如果您在本地主机上使用 Jenkins,那么您在尝试将 Jenkins 与 Github 连接时可能会遇到一些问题。如果是这种情况,请考虑创建一个到本地主机的安全 URL。我发现最好的工具是 ngrok 。
- 由于 jenkins 使用自己的用户(称为 Jenkins ),您可能需要授予它权限,以便在没有密码的情况下执行命令。你可以通过用
sudo visudo /etc/sudoers打开 sudoers 文件并粘贴jenkins ALL=(ALL) NOPASSWD: ALL来实现。
话虽如此,我们还是看看有什么计划吧。我们将创造 4 个就业机会:
- “github-to-container”工作:在这项工作中,我们将 Jenkins 与 github“连接”,每次在 Github 中提交时都会触发这项工作。我们还将构建 Docker 映像并运行一个容器。
- “预处理”作业:在这一步中,我们将执行 preprocessing.py 脚本。该作业将由“github 到容器”作业触发。
- “train”作业:在这个作业中,我们将执行 train.py 脚本。该作业将由“预处理”作业触发。
- “测试”工作:在这项工作中,我们将通过我们的条件语句传递验证分数。如果它高于阈值,我们将执行 test.py 脚本并显示元数据(验证和测试准确性)。如果验证分数低于阈值,则该过程将停止,并且不会提供元数据。
一旦我们知道该做什么,让我们去做吧!
创造詹金斯就业机会
github 到容器的工作
对于 github 到容器的工作,首先我们需要在 github 和 Jenkins 之间创建一个“连接”。这是使用 webhooks 完成的。要创建 Webhook,请转到 Github 中的存储库,选择 settings 并选择 webhooks。选择添加 webhook。在有效负载 URL 中,传递运行 Jenkins 的 URL 并添加“//github-webhook/”。对于内容类型,选择“应用程序/json”。对于“您希望哪个事件触发这个 webhook?”,选择“仅推送事件”。在底部选择活动。选择添加 webhook。

作者图片
然后,您需要在 Jenkins 中创建一个凭证来访问 Github。在詹金斯,转到管理詹金斯

作者图片
然后选择管理凭据,

作者图片
在“Jenkins 范围内的商店”中,选择 Jenkins

作者图片
然后选择“全局凭证(无限制)”

作者图片
并添加凭据

作者图片
在这里,对于范围选择“Global (Jenkins,nodes,items,all child items,etc)”,对于用户名和密码填写您的 Github 用户名和密码。您可以将 ID 留空,因为它将自动生成。您也可以添加描述。最后,单击确定。
现在让我们建立第一个工作!
在 Jenkins 中,转到新项目,

作者图片
然后给它起个名字,选择自由式项目。

作者图片
下一步是设置配置。对于这一步,在源代码管理中选择 Git,然后粘贴您的存储库的 URL 和您的 Github 凭证。

作者图片
然后,在构建触发器中选择“GitHub hook trigger for git SCM polling”。最后在 build 部分选择 Add build step,然后执行 shell,然后编写构建映像和运行容器的代码(我们已经讨论过了):

作者图片
选择保存。
预处理作业
对于“预处理”作业,在源代码管理中将其保留为无。在“生成触发器”中,选择“在其他项目生成后生成”。然后在要监视的项目中输入第一个作业的名称,并选择“仅当构建稳定时触发”。

作者图片
在 Build 中,选择 Add build step,然后执行 shell,并编写运行 preprocessing.py 的代码:

作者图片
火车工作
“训练”作业与“预处理”作业的方案相同,但有一些不同。正如您可能猜到的,您需要在 Build triggers 部分写下第二个作业的名称:

作者图片
在构建部分编写运行 train.py 的代码。

作者图片
测试工作
对于“测试”作业,为构建触发器部分选择“训练”作业,

作者图片
并在构建部分编写以下代码:
val_acc=$(sudo -S docker container exec model jq .validation_acc \ /home/jovyan/results/train_metadata.json)threshold=0.8
if echo "$threshold > $val_acc" | bc -l | grep -q 1
then
echo 'validation accuracy is lower than the threshold, process stopped'
else
echo 'validation accuracy is higher than the threshold'
sudo -S docker container exec model python3 test.py
sudo -S docker container exec model cat \ /home/jovyan/results/train_metadata.json \ /home/jovyan/results/test_metadata.json
fi
sudo -S docker rm -f model
我已经写了,以防你想复制粘贴,但在詹金斯它应该是这样的:

作者图片
点击保存,我们就有了!我们的流水线现在完全自动化了!
您现在可以使用它了:尝试在 Github 中提交,看看每一步是如何自动进行的。最后,如果模型验证精度高于阈值,模型将计算测试精度并返回结果。
📒注意:为了查看每个步骤的输出,选择该步骤,单击左下方构建部分的第一个数字,然后选择控制台输出。对于最后一步,您应该看到验证和测试的准确性。
希望你学到了很多!感谢阅读!
参考
从 DevOps 到 MLOPS:使用 Jenkins 和 Docker 整合机器学习模型
使用 Kubernetes、RabbitMQ 和 FastAPI 的 MLOps
Skipper 是一个简单灵活的开源 ML 工作流引擎。它有助于在生产中运行和扩展 ML 服务。

作者:安德烈·巴拉诺夫斯基
你经常会听到人们说——许多 ML 项目在进入生产阶段之前就停止了。其中一个原因是,典型的 ML 项目从一开始就被实现为一个整体,当在生产中运行它们的时候,管理、转换和维护代码是不可能的。ML 项目代码被实现为几个甚至一个大笔记本,其中数据处理、模型训练和预测都被粘在一起。这使得在需要修改代码和引入用户请求时,很难维护如此繁琐的代码。结果,用户不高兴,这导致项目终止。
从一开始就构建 ML 系统并遵循微服务架构会有效得多。您可以使用容器来封装逻辑。可以有单独的容器用于数据处理、ML 模型训练和 ML 模型服务。当运行单独的容器时,不仅简化了代码维护,而且您还可以单独伸缩容器并在不同的硬件上运行它们。这可以提高系统性能。
问题来了,如何实现这些服务之间的通信。我正在研究可用的工具,比如 MLFlow 。这些工具很棒,但是对于任务来说,它们通常太复杂太庞大。特别是当你想简单地在不同的容器中运行 ML 逻辑时,差不多就是这样。这就是为什么我决定构建自己的小型简单开源产品 Skipper 来运行 ML 工作负载。
在本文中,我将解释如何使用 Skipper 在 Kubernetes 上扩展 TensorFlow 模型。同样的方法可以应用于 PyTorch 模型或任何其他与 ML 无关的功能。

Skipper structure,作者:Andrej Baranovskij
- 公共端口通过 Nginx 暴露
- FastAPI 正在服务 REST 端点 API。目前提供两个通用端点,一个用于异步请求,另一个用于同步
- 负责请求路由的工作流容器
- 记录器容器提供通用的日志记录功能
- 芹菜容器用于执行异步请求
- RabbitMQ 是一个消息代理,它支持 Skipper 容器之间基于事件的通信
- SkipperLib 是一个 Python 库,它封装了特定于 RabbitMQ 的 API 代码
- 创建了一组微服务作为样本容器,以展示 Skipper 如何处理特定于 ML(也可以是非 ML)的服务
REST API
您可以通过多种方式运行 Skipper 容器:
- 直接与 Python 虚拟环境在你的机器上
- 通过 Docker compose 在 Docker 容器上。按照自述文件中的说明进行操作
- 在库伯内特。按照自述文件中的说明进行操作
在生产环境中,您应该在 Kubernetes 中运行 Skipper,使用 Kubernetes 可以更容易地扩展容器。
Skipper REST API 通过 Kubernetes NGINX 入口控制器公开:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
spec:
rules:
- host: kubernetes.docker.internal
http:
paths:
- path: /api/v1/skipper/tasks/
pathType: Prefix
backend:
service:
name: skipper-api
port:
number: 8000
ingressClassName: nginx
---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: nginx
spec:
controller: k8s.io/ingress-nginx
入口重定向到/api/v1/skipper/tasks/
定义了两个通用端点。一个服务异步请求,另一个服务同步请求。异步请求执行对训练模型的调用,这是一个长期运行的任务。同步请求处理模型预测调用,并将请求路由到模型服务。所有请求都经过 RabbitMQ 消息队列。
所有对 RabbitMQ 的调用都是通过 SkipperLib 执行的。这允许将 RabbitMQ 特定的代码封装在库中并对其进行修改,而无需接触容器中的代码。
模特培训
用于模型训练的 Kubernetes Pod 运行两个容器。首先是一个主容器,它训练一个模型。第二个是边车容器,负责数据准备和处理。
我们在同一个 Pod 中运行这两个容器,因为不需要分别扩展它们,并且当它们在同一个 Pod 中运行时,在容器之间共享数据更方便。这对于预测逻辑来说是不正确的,我们在单独的 Pod 中运行预测逻辑,以便能够单独对其进行扩展,下一章将对此进行详细介绍。
模型训练和数据准备容器共享相同的 Kubernetes 卷用于数据存储。
模型培训容器:
volumeMounts:
- name: data
mountPath: /usr/src/trainingservice/models
数据准备容器:
volumeMounts:
- name: data
mountPath: /usr/src/dataservice/models
挂载路径不同,但目标位置相同。通过这些路径访问的数据将是相同的。因为两个容器使用相同的卷“数据”:
volumes:
- name: data
persistentVolumeClaim:
claimName: training-service-claim
当模型被训练并且模型文件被保存时,我们需要在模型预测容器运行时将它传输到服务 Pod。解决方案之一是使用外部云存储,并在那里上传模型文件。但是如果模型文件不太大,Skipper 允许将它直接从训练舱转移到服务舱。模型被存档、编码成一个字符串、与其他元数据一起打包成 JSON,并被发送到 RabbitMQ 队列以交付给 serving Pod。
模型结构被归档到一个文件中:
shutil.make_archive(base_name=os.getenv('MODELS_FOLDER') + str(ts),
format='zip',
root_dir=os.getenv('MODELS_FOLDER') + str(ts))
存档的模型文件被编码成 base64 字符串:
model_encoded = None
try:
with open(os.getenv('MODELS_FILE'), 'rb') as model_file:
model_encoded = base64.b64encode(model_file.read())
except Exception as e:
print(str(e))
在最后一步中,我们将所有东西都包装到 JSON 中:
data = {
'name': 'model_boston_' + str(ts),
'archive_name': 'model_boston_' + str(ts) + '.zip',
'model': model_encoded,
'stats': stats_encoded,
'stats_name': 'train_stats.csv'
}
content = json.dumps(data)
该消息被提交给 RabbitMQ 进行传递。模型通过 RabbitMQ 上的“扇出”交换发送,这允许一次向所有订户发送相同的数据。默认情况下,RabbitMQ 会一次向一个订阅者发送消息,这在集群中作为负载平衡非常有效。但在这种情况下,我们希望集群中的所有接收器都获得新模型,这就是我们使用“扇出”交换的原因。
这是消息通过 RabbitMQ 发布到“扇出”交换的方式:
credentials = pika.PlainCredentials(self.username, self.password)
connection = pika.BlockingConnection(
pika.ConnectionParameters(host=self.host,
port=self.port,
credentials=credentials))channel = connection.channel()
channel.exchange_declare(exchange='skipper_storage',
exchange_type='fanout')channel.basic_publish(exchange='skipper_storage',
routing_key='',
body=payload)
connection.close()
模型服务
用于模型服务的 Kubernetes Pod 运行两个容器。主容器负责使用 TensorFlow API 执行预测请求。当新的模型文件被发送时,边车容器监听来自 RabbitMQ 的消息,解码文件并提取模型。
两个容器共享同一个存储空间。
服务容器:
volumeMounts:
- name: data
mountPath: /usr/src/servingservice/models/serving
用于模型文件处理的边车容器:
volumeMounts:
- name: data
mountPath: /usr/src/servingservice/storage/models/serving/
存储被装载到同一个卷声明:
volumes:
- name: data
persistentVolumeClaim:
claimName: serving-service-claim
当负责模型文件处理的容器接收到模型时,它会执行与模型训练窗格中的容器类似的步骤,其中模型准备通过 RabbitMQ 发送:
data_json = json.loads(data)
model_name = data_json['name']
archive_name = data_json['archive_name']
stats_name = data_json['stats_name']
model_decoded = base64.b64decode(data_json['model'])
stats_decoded = base64.b64decode(data_json['stats'])
它解码字符串,提取文件。
模型服务单元可以扩展到多个实例。如果实例运行在不同的集群节点上,那么每个节点都会收到来自 RabbitMQ 消息的新模型。但是,如果几个实例将在单个节点上运行,它们都将尝试将模型写入同一个存储中。如果其中一个实例失败,我们将处理异常。
结论
本文的目标是介绍 Skipper。我们面向 MLOps 的开源产品。目前,该产品已准备好投入生产使用。我们的目标是进一步增强它,特别是添加 FastAPI 安全配置,添加更复杂的工作流支持和改进日志记录。我们计划测试 Skipper 的 Kubernetes 自动缩放功能。我们使用 Skipper 平台来实现我们的 ML 服务。
源代码
- Skipper GitHub repo。按照自述文件获取设置说明
YouTube 教程
使用 MLFlow 和 Amazon SageMaker 管道的 MLOps
将 MLflow 用于 SageMaker 项目的分步指南
今年早些时候,我发表了一篇关于在 AWS Fargate 上部署 MLflow,并在 Amazon SageMaker 上使用它的分步指南。这有助于简化 ML 项目的实验阶段。

在这篇文章中,我们将更进一步,使用 MLflow 和 Amazon SageMaker Pipelines 自动化端到端的 ML 生命周期。
SageMaker Pipelines 将 ML 工作流编排、模型注册和 CI/CD 结合成一把伞,这样您就可以快速地将您的模型投入生产。

作者图片
我们将为模型构建、培训和部署创建一个 MLOps 项目,以培训一个示例随机森林模型并将其部署到 SageMaker 端点中。我们将更新项目的 modelBuild 端,以便它可以将模型记录到 MLflow 模型注册表中,并更新 modelDeploy 端,以便它可以将它们发布到产品中。
演练概述
我们将分三步解决这个问题:
- 我们将首先在 AWS 上部署 MLflow,并在 SageMaker 中启动一个 MLOps 项目。
- 然后,我们将更新 modelBuild 管道,以便将模型记录到我们的 MLflow 模型注册中心。
- 最后,我将展示如何使用 modelDeploy 管道将 MLflow 模型部署到生产中。
以下是该项目的架构概述:

作者图片:架构概述
先决条件
要浏览此示例,请确保您具备以下条件:
- 如果您对 SageMaker Pipelines 听起来不熟悉,请访问介绍亚马逊 SageMaker Pipelines 。
- 熟悉使用 MLflow 和 Amazon SageMaker 及其示例实验室管理您的机器学习生命周期。
- 访问亚马逊 SageMaker 工作室环境并熟悉 T2 工作室用户界面。
- Docker 构建 MLFlow 推理容器映像并将其推送到 ECR。
- 这个 GitHub 库克隆到您的工作室环境中
步骤 1:在 AWS 上部署 MLflow 并在 SageMaker 中启动 MLOps 项目
在 AWS Fargate 上部署 MLflow
首先,我们需要设置一个中央 MLflow 跟踪服务器,这样我们就可以在我们的 MLOps 项目中使用它。
如果没有,可以按照说明和博客解释在 AWS Fargate 上部署 MLflow 的开源版本。

作者图片:在 AWS Fargate 上托管 MLflow,Amazon S3 作为工件商店,Amazon RDS for MySQL 作为后端商店。

图片由作者提供:一旦部署,确保你把负载平衡器 URI 放在某个地方。我们将在我们的 MLOps 项目中使用它,以便管道可以与 MLflow 通信。
启动您的 MLOps 项目
现在,我们需要启动一个基于 MLOps 模板的 SageMaker 项目,用于模型构建、培训和部署。
你可以按照朱利安·西蒙的视频来做:
项目模板将为 modelBuild 和 modelDeploy 创建 2 个 CodeCommit repos,为 CI 和 CD 创建 2 个 CodePipeline 管道,为打包和测试工件创建 CodeBuild 项目,以及运行项目的其他资源。

作者图片:您可以在您的环境中克隆 repos。
允许项目访问 MLflow 工件存储
我们使用亚马逊 S3 作为 MLflow 的工件存储,您需要更新 MLOps 项目角色,以便它可以访问 MLflow S3 存储桶。
这个角色叫做AmazonSageMakerServiceCatalogProductsUseRole,你可以像我下面做的那样更新它的权限:

作者图片:我在这个例子中使用了托管策略。您可以在您的环境中收紧权限。
步骤 2:更新 modelBuild 管道以将模型记录到 MLflow 中
在将 modelBuild 存储库克隆到您的环境中之后,您可以使用来自 model_build 文件夹的代码来更新代码。


作者图片:您的模型构建报告在更新前(左)和更新后(右)应该是什么样子
你可以在 pipeline.py 中找到 ML 管道的例子。它有两个简单的步骤:
- PrepareData 从 sklearn 获取数据集,并将其分成训练/测试集
- traineevaluateregister训练一个随机森林模型,将参数、度量和模型记录到 MLflow 中。
在 pipeline.py 的第 22 行,确保将您的 MLflow 负载平衡器 URI 添加到管道参数中。它将被传递给traineevaluateregister,这样它就知道在哪里找到 MLflow。
现在,您可以将更新后的代码推送到回购的主分支。

图片作者:您的管道将被更新,需要几分钟来执行。
从现在开始,管道将在每次执行时在 MLflow 中注册新的模型版本。

作者图片
为了进一步自动化,您可以使用 Amazon EventBridge 来调度管道,或者使用其他类型的触发器和start _ pipeline _ execution方法。
步骤 3:使用 modelDeploy 管道将 MLflow 模型部署到生产环境中
我们现在可以将新的模型版本引入 MLflow 模型注册中心,并将使用 MLOps 项目的 modelDeploy 端将它们部署到生产中。
将推理容器映像推送到 ECR
除了 ML 模型,我们还需要一个容器映像来处理 SageMaker 端点中的推理。让我们把 MLflow 提供的推送到 ECR。确保这与您的 MLOps 项目在同一个 AWS 区域中完成。
在我的例子中,我使用以下命令从我的笔记本电脑推送它:
pip install -q mlflow==1.23.1mlflow sagemaker build-and-push-container
更新模型部署报告
接下来,您可以使用来自这个文件夹的代码来更新 modelDeploy repo。


作者图片:modelDeploy repo 在更新之前(左)和之后(右)的样子
在 buildspec.yml 中,您可以定义部署到生产中的模型版本。您还需要输入您的 MLflow 负载平衡器 URI 和推理容器 URI。

作者图片
我更新了 build.py 以从 MLflow 获取所选的模型版本二进制文件,并将其 model.tar.gz 上传到 S3。
这是由 mlflow_handler.py 完成的,当模型通过 modelDeploy 管道时,它也转换 mlflow 中的模型阶段。
触发部署
现在可以将代码推送到 repo 的主分支,这将触发 CodePipeline 中的 modelDeploy 管道。一旦测试在试运行中成功,您就可以导航到 CodePipeline 控制台并手动批准端点投入生产。


作者提供的图片:模型阶段将在 MLflow 中过渡,因为它在管道中前进
当部署新版本时,管道将存档以前的版本。

作者图片
您可以看到您的 SageMaker 端点已准备好生成预测。

作者图片
结论
Amazon SageMaker Pipelines 将 MLOps 工具纳入一个保护伞中,以减少运行端到端 MLOps 项目的工作量。
在这篇文章中,我们使用了 SageMaker MLOps 项目和 MLflow 模型注册中心来自动化端到端 ML 生命周期。
更进一步,您还可以学习如何使用 Amazon SageMaker Pipelines 部署无服务器推理服务。
没有太多操作的 MLOps
在启动阶段有效地进行 ML
一部由西罗·格列柯和安德里亚·波洛尼奥利主演的迷你剧
“合理规模”的生活
如果你不在大型科技公司工作,比如谷歌、Facebooks、亚马逊,那么你很有可能在一家规模合理的公司工作。
合理规模的公司不像谷歌。他们无法雇用他们梦想的所有人,也无法通过他们拥有的云基础架构每天为数十亿用户提供服务。合理规模公司处理数百万个数据点,而不是数十亿;他们可以雇佣几十名数据科学家,而不是几百名,而且他们必须针对计算成本进行优化。
与此同时,规模合理的公司有大量有趣的商业问题,可以通过使用机器学习来解决。事实上,用机器学习来解决这些问题完全有意义,也许他们已经在尝试了。当人才、预算和数据量受到限制时,很难实施正确的流程。

合理规模用例所涵盖的概念领域。拥有小团队的先进人工智能初创公司(右下),以及开始制定 ML 路线图的较晚、较大的采用者(左上),是没有太多运营的 MLOps 的理想目标[图片由作者提供]。
事实是,除了大型技术和先进的创业公司,人工智能系统仍然远远没有产生承诺的投资回报率:人工智能项目从试点到生产平均需要 9 个月的时间。)让企业从试点转向运营。随着人工智能项目数量的不断增加,对成熟的 MLOps 方法的需求变得越来越明显:由于在这样的基础领域做出错误选择的机会成本可能会削弱甚至是最好的业务,因此高管和管理层理解合理的 MLOps 策略的含义至关重要。现在是研究它的最佳时机。
在过去的几年中,我们了解到,即使在合理的规模下,ML 也可以实现正的投资回报。我们之所以知道这一点,是因为我们在帮助数百家中大型企业实现数字化转型的过程中处于某种特殊的地位。最重要的是,
我们知道这一点,因为我们本身就是一家规模合理的公司。
我们决定在一个多部分的系列中分享我们在这一过程中所学到的东西,重点是如何构建和扩展 ML 系统,以在面对上述限制时更快地交付结果:小型 ML 团队,有限的预算,万亿字节的数据。我们的目标是为您提供一份经过验证的最佳实践手册,帮助您在设计端到端生产系统时成功驾驭快速发展的 MLOps 环境。
已经有很多关于工具 X 或者框架 Y (包括我们自己的!),但是(出于良好的教学原因)他们关注孤立的工具,通常是在玩具世界的场景中。我们决定走一条更长,但更有希望获得回报的路线:我们的讨论在设计上更加细致入微,包括来自学术论文、开源代码和第一手创业经验的证据。
我们(雄心勃勃的)目标是为你提供一个建立 AI 公司的模板,而不是一个微小的功能。
用开源和 SaaS 有效地构建人工智能
这个系列的核心思想可以非常简明地表述如下:
要想在合理的规模上实现大规模生产,你应该把时间投入到你的核心问题上(不管是什么),并购买其他所有东西。
虽然陈述主要原则很容易,但以合理的规模生活涉及到各种微妙的分歧,从争夺人才到控制 P&L。我们原则的推论是,我们应该尽我们所能从 ML 开发者那里抽象出基础设施。由于我们正在处理合理的规模,投入资源来部署和维护今天可以作为 PaaS/SaaS 解决方案找到的功能(例如雪花、元流、 SageMaker )没有太大的价值。
在合理的范围内生活涉及到各种微妙的分歧。但鉴于 MLOps 是一个全新的领域,高管和管理层并不总是完全了解它:我们在设计这个系列时考虑了许多不同的角色,允许我们从不同的角度探索 ML 生产力。作为一个小预告片,这些是一些重点领域:
- 面向所有人的 ML 工具: MLOps 是 20 年来致力于拥抱开源技术的种子。如今,开源在企业中的采用正在加速,这使得小团队越来越容易在大规模下获得极高的生产力。然而,使用开源工具的想法经常被团队领导和高管们所反对:我们将解释公司和企业领导如何降低他们的开放堆栈战略的风险,并从开源软件中获得最大收益。
- 少即是多:通过用少得多的人力来换取更多的计算,我们得出这样的结论:一个快乐的小型人工智能团队要比一个更大、更不专注的团队好得多。换句话说,可能更大的 AWS 账单通常会被更高的保留率和更高的 ML 生产率所抵消。其影响是深远的:例如,考虑一下研发人员数量等传统指标可能需要如何重新评估,以及可能需要不同的基准。现代 MLOps 方法可能会导致传统的研发指标和基准变得几乎过时。
- 被授权的开发人员成长得更好:在竞争激烈的市场中招聘和留住人才对公司来说是一个持续的挑战,尤其是在 ML 领域。事实证明,ML 从业者更替的主要原因之一是将相当一部分时间投入到低影响的任务中,如数据准备和基础设施维护。
好奇想了解更多?
在接下来的几个月里,我们将探索合理规模的 ML 生产力的所有方面。通过 Medium 或 Linkedin 关注我们的最新动态!
雅格布·塔利亚布埃 、 西罗·格列柯和安德里亚·波洛尼奥利。
常见问题解答
- 你有 TL;DR 版?不完全是,这就是为什么我们开始了一个(小)系列:我们真的试图把所有的东西都放进的一个 帖子里,但是我们的第一批读者似乎同意它太密集了,没有意义。如果你想在跑步的时候听点什么,这些主题中的一些已经在斯坦福 MLSys 的MLOps in Reasonable Scale讲座中被预见到了。
- 新锐 ML 真的能活在大 Tech 之外吗? 是的,它真的可以。为团队成员和外部合作者创建一个自助服务系统,以通过端点(不是幻灯片)旋转 GPU、执行查询、共享发现!)是快速进行产品开发和研究的好方法。
- 空谈不值钱,给我看看代码!如果你不耐烦,想直接跳到它的工程方面,我们共享了一个开源库来实现我们的原则,从解析原始数据到服务预测(注意:该项目在真实的数据负载下工作,这要感谢我们最近发布的一个大规模电子商务数据集)。
致谢
我们要感谢 Ville、Savin 和 Oleg 对项目先前迭代的宝贵反馈; Piero Molino 和斯坦福 MLSys 集团邀请我们参加一场精彩的会议;Mike Purewal ,他优雅地拒绝了我们的初稿,并推动我们变得更好;最后卢卡·比贡,他以合理的尺度生活和呼吸。
当然,没有我们开源贡献者的承诺,这个系列是不可能的:
- Patrick John Chia :局部流量和基线模型;
- Luca Bigon :通用工程和基础优化;
- Andrew Sutcliffe :远程流;
- Leopoldo Garcia Vargas : QA 和测试。
MLP 混合器:全 MLP 的视觉建筑
回到美好的老 MLP?
介绍
来自 Google Research &的研究人员 Google Brain 团队发布了一种新的架构 MLP 混合器,它没有 CNN 或注意力层,但仍然可以实现与 ViT(视觉变压器)、BiT(大传输)、HaloNet 和 NF-Net 等架构相当的性能。它在训练中的速度提高了 3 倍,并且随着训练数据量的增加而出色地扩展。作者将这种架构放在一起,因为他们觉得我们可以在没有非常重要的 CNN 和注意力层的情况下获得出色的结果,这些层需要大量的计算。在这个过程中,他们还展示了更高的吞吐量/秒/核,以及该模型比大多数当前最先进的模型更好地扩展到更大的数据集
背景
在这篇论文发表之前,CNN 和自我关注通常是图像分类和计算机视觉的首选机制。这在最近最先进的视觉变压器(ViT)中得到了展示,该变压器成功地将注意力层应用于计算机视觉任务。虽然这些操作是非常计算密集型的,需要花费数天时间在 TPU 上训练,但作者提出了深度学习中的一个基本概念——MLP(多层感知器)!该架构依赖于在空间和特征通道上重复的各种矩阵乘法。
这怎么可能呢?难道我们不是从 MLP、CNN、剩余 CNN(即 ResNets、DenseNets、NF-net、ViT 等等)发展而来的吗?现在,作者说回到 MLP?是的,你没听错,
虽然回旋和注意力对于良好的表现都是足够的,但它们都不是必需的
这些都是巨大的索赔,让我们理解为什么这个架构的工作,他们的索赔是正确的!
体系结构
谈到 MLP 混合器模型,有相当多的事情正在发生。我们有补丁形式的输入,就像 ViT 一样。然后是混合器层,其中发生两个操作,具有 GELU 激活的完全连接的层,以及最后的线性分类头。跳过连接和正则化,如 Dropout 和 Layer Norm 也进入了体系结构。

MLP 混频器架构。图片鸣谢——MLP 混合器论文
正如我们在该架构底部看到的,网络的输入是图像补丁的形式。这些面片被线性地投影到一个 H 维的潜在表示(它是隐藏的)中,并被传递到混合器层。这里需要注意的一点是,H 值与面片数量或面片大小无关,这使得网络能够线性增长,而不是像 ViT 那样呈二次方增长。这减少了计算参数,提高了吞吐量,约为 120 张图像/秒/核,几乎是 ViT 32 张图像/秒/核的 3 倍。
现在,事情变得有趣了。参考展示混合器层的图像的顶部,当投影时,补丁被转换成类似表格的形式,我们称之为 x。补丁在彼此之上分层。
混合器层由 2 个 MLP 块组成。第一块(令牌混合 MLP 块)作用于 X 的转置,即线性投影表(X)的列。所有补丁的每一行都具有相同的通道信息。这被馈送到 2 个完全连接的层的块。该块保存相同类型的信息——跨片识别图像中的特征,即聚集出现该特征的所有通道。权重在图中所示的 MLP 1 图层中共享。
第二个块(通道混合 MLP 块)在 x 的另一个转置之后作用于行。这里,获取每个面片并在该面片的所有通道上应用计算。这是仅在该补丁中寻找特征并将其与通道相关联,而在令牌混合块中,它在所有通道中搜索特征。
这种结构是作者引用的独特的 CNN,
我们的架构可以被视为一个独特的 CNN,它使用(1×1)卷积进行信道混合,使用单通道深度卷积进行令牌混合。然而,反之则不然,因为 CNN 不是混频器的特例。
此外,该架构使用变压器架构中常见的层规范。下图说明了层规范与批处理规范,以便快速理解。更多关于层定额这里。GELU 激活包括用于非线性的跳过连接和用于正则化的丢弃连接。

层规范化与批规范化。图片来源——power norm论文
基准和结果

跨数据集比较 Mixer 与其他模型。图片鸣谢——MLP 混合器论文
正如我们在表中看到的,与其他 SOTAs 相比,ImageNet top-1 验证准确性方面的性能相当不错。但是对于 MLP 混合器来说,真正的交易是双倍的——图像/秒/核心的吞吐量和 TPU 训练时间。混合器在图像/秒/核的吞吐量方面远远领先于 ViT,比 ViT 的 32 (ImageNet)高出 105 分,比 ViT 的 15(JFT-300)高出 40(图像/秒/核)。
当谈到 ImageNet 上的 TPU 训练时间时,ViT 胜过 Mixer,但不是 JFT-300。因此,当我们看到桌面上更多的数字时,我们可以说,“混音器 table 正在挑战其他索塔。不一定胜过他们,但仍然很有竞争力”。
到了“它随更多数据而扩展”的部分,下图说明了当训练集大小增加时,Mixer、ViT 和 BiT 的性能。

训练子集大小与 ImageNet top-1 val 准确性的关系。图片鸣谢——MLP 混合器论文
混合器在图的右侧紧密匹配,这表明 5 次拍摄 ImageNet top-1 精度。但是我们需要从左侧观察曲线,其中训练子集大小从 10M 开始,一直到 300M。在所有情况下,混合器具有更好的曲线缩放,并且不会在任何时间点使曲线变平。这意味着,它不会因为更多的训练数据而饱和,而 ViT 和 BiT 模型一旦超过 100M 的训练数据子集大小就会饱和(精度不会提高)。
此外,在该模型的所有变体与其他 SOTAs 的其他全面比较中,考虑到与性能相关的预训练方面,当有更多数据时,混合器会受益。预训练数据越小,其在验证准确性方面与 SOTAs 的差距就越大。一定要看看报纸上的这篇文章。报纸上还有一个非常有趣的部分——MLP 混频器的设计思想如何可以追溯到 CNN&变形金刚?。这就是为什么要确保阅读这篇论文,了解消融术、相关工作、不同的实验设置、重量的可视化以及没有成功的事情!
结论
MLP 混合器在吞吐量和训练时间等各种重要指标上都名列前茅。在 ImageNet top-1 验证准确性方面,它并不落后,在 JFT-300 上进行了预训练,得分为 87.78。就所提供的训练数据量而言,与 ViT & BiT 相比,该网络具有极佳的扩展性。但是,有一点需要深思——归纳偏差。根据本文中的训练子集大小与 ImageNet top-1 val 准确度图,ViT 在 300 米处变平,而 Mixer-MLP 仍在上升。如果数据的规模是 600M,可能会发生什么?密切关注预训练数据集大小和归纳偏差的作用。说到这里,作者的一个更有趣的评论是——“这种设计对 NLP 是否适用?”。时间会证明一切。
参考
[1]https://arxiv.org/pdf/2105.01601.pdfMLP-混频器:
[2]维特:【https://arxiv.org/pdf/2010.11929】T2
[3]位:【https://arxiv.org/pdf/1912.11370】T4
[4]光环网:https://arxiv.org/pdf/2103.12731
[5]层定额:https://arxiv.org/pdf/2003.07845
[6]NF-Nets:https://arxiv.org/pdf/2102.06171v1.pdf
[7]ImageNet:http://www.image-net.org/
你只需要 MLP 搅拌机。
🤖深度学习
从头到尾了解 MLP 混频器,带 TF Keras 代码

今年 5 月初,谷歌的一组研究人员发布了一篇论文“MLP 混合器:一个全 MLP 的视觉架构” 介绍了他们的 MLP 混合器(简称为混合器)模型,用于解决计算机视觉问题。该研究表明,MLP 混合器在图像分类基准测试中取得了有竞争力的分数,如 ImageNet 。
有一件事会引起每个 ML 开发者的注意,那就是他们没有在他们的架构中使用卷积。自从卷积能够有效地从图像和视频中提取空间信息以来,它就一直统治着计算机视觉。最近,最初用于 NLP 问题的变形金刚,在计算机视觉问题上也表现出了显著的效果。MLP 混合器的研究论文表明,
在本文中,我们表明,虽然卷积和注意力对于良好的性能都是足够的,但它们都不是必要的。
关于 MLP 混合器是否是“无 conv”的,曾经有过一些争议。从权重和偏见浏览此博客以了解更多信息,
我们将更多地讨论 MLP 混合器的架构和相关的底层技术。最后,我们提供了一个使用 TensorFlow Keras 的 MLP 混合器的代码实现。
此外,这个博客已经在谷歌开发库上展示。
你现在可以在 TensorFlow Hub,https://tfhub.dev/sayakpaul/collections/mlp-mixer/1上找到预训练的 MLP 混合器模型
我也使用 MLP 混合器进行文本分类,
https://www.kaggle.com/shubham0204/tweet-classification-with-mlp-mixers-tf-keras
📃 内容
- 👉 独霸卷积,变形金刚 问世
- 👉 【多层感知器】(MLP)和 的 GELU 激活功能
- 👉MLP-密炼机架构组件
- 👉 终局
- 👉 更多项目/博客/资源来自作者
👼回旋的优势,变形金刚的出现
在计算机视觉问题中使用卷积是由 Yann LeCun 推广的,从那时起卷积就成为了计算机视觉问题的支柱。每个滤波器在输入体积上进行卷积,以计算由神经元构成的激活图,如下所示。

图 1:内核大小=3,步长=1 的卷积运算(没有填充)。来源:卷积运算
输出图中的每一个神经元都连接到输入体积的特定部分,这可以在图 1 中清晰观察到。输出映射然后被传递给激活函数(例如 ReLU )。为了降低输出图的维数,使用了池操作。卷积用于学习图像的局部特征,这是计算机视觉问题的目标。几乎所有的架构像 MobileNets 、 Inception 、 ResNet 、 DenseNet 等等。使用卷积层(也就是卷积+激活)来学习图像特征。
Transformers 是为 NLP 问题而创造的,但在图像分类方面已经显示出相当大的成果。我将在这里为视觉变形金刚 ( ViTs)留下一些资源,
https://keras.io/examples/vision/image_classification_with_vision_transformer/ https://www.analyticsvidhya.com/blog/2021/03/an-image-is-worth-16x16-words-transformers-for-image-recognition-at-scale-vision-transformers/
🤠多层感知器(MLP)和 GELU 激活函数
多层感知器(MLP)
如果你是一个有经验的 ML 开发者,你可能在古代就已经学会了。


图 2:多层感知器。来源:多层感知器示例
多层感知器是一个人工神经网络,有一个输入层、多个隐含层和一个输出层。除了输入节点,每个节点都使用一个非线性激活函数。
在我们的例子中,研究论文提出了一个具有两个全连接(密集)层的 MLP,带有一个 GeLU (我们将在接下来的部分中对此进行更多讨论)激活函数,

图 3:混频器架构的 MLP。来源:《MLP 混合器:一个全 MLP 的建筑愿景》
每个混合器层将由两个 MLP 组成,一个用于 令牌混合 ,另一个用于 通道混合 。我们将在后面的章节中详细讨论 令牌混合 和 通道混合 。下面是我们将用来堆叠两个Dense层(带有一个 GELU)激活的代码,从而向现有的层(x)添加一个 MLP,
片段 1:MLP
注意,我们大多数人会认为Dense层接受形状为( batch_size , input_dims )的输入,输出形状为( batch_size, output_dims )的张量。但在我们的例子中,这些Dense层将接收三维形状的输入,即形状( batch_size , num_patches , channels )或其转置( batch_size , channels , num_patches )。

图 4:关于密集层的输入/输出形状的信息。来源:致密层张量流文件。
我们将在故事的后面部分了解更多关于num_channels和num_patches的内容。
高斯误差线性单位激活
现代但不太流行的激活功能

图 5:雷鲁、葛鲁和 eLU 的曲线图。来源:ArXiv上的高斯误差线性单位(GELUs)
高斯误差线性单位是一个激活函数,使用标准高斯累积分布函数对输入进行加权。在 ReLU(校正线性单位)的情况下,使用其符号对输入进行加权。


图 6: ReLU 和 GELU 激活函数。来源:作者创作。
随着 x 减少,输入很有可能被丢弃。 x 上的变换是随机的,但它取决于 x 的值。

图 7:作者提供的 GELU 的近似值。来源:ArXiv 上的高斯误差线性单位
选择正态分布的原因是当使用批量归一化层时,神经元的输出遵循正态分布。GELU 激活在变压器模型中广泛用于解决 NLP 问题。
正如在片段 1 中观察到的,我们将使用[tf.nn.gelu](https://www.tensorflow.org/api_docs/python/tf/nn/gelu)将 GELU 激活添加到 MLPs 中。如果你想要一个 Keras 层,在 TensorFlow Addons 包中有一个[tfa.layers.GELU](https://www.tensorflow.org/addons/api_docs/python/tfa/layers/GELU)层。
这里有一个很好的博客解释了各种激活功能(包括 GELU),
https://mlfromscratch.com/activation-functions-explained/#/
🔧MLP 混频器架构组件
我们将详细讨论每个组件,然后将它们集成到一段代码中。
⚙️Mixer 层

图 8:混合层。来源:作者创作
代码片段 2:混音器层
混频器层是 MLP 混频器架构的构建模块。每个混合器层包含两个 MLP,一个用于令牌混合,另一个用于通道混合。在 MLPs 旁边,你会注意到图层正常化,跳过连接和箭头上面写的【T】。指张量的 转置* ,保持批量维度不变。
转置 ***** :我们将使用
*tf.keras.layers.Permute*层,通过在该层的参数中设置*dims=[ 2 , 1 ]*来进行转置。我们不会在接下来的章节中详细讨论这个问题。
我建议您彻底理解该图,因为在讨论组件时,我会不时地引用它。在以下部分中,我们将讨论,
- 什么是补丁(混音器层的输入)
- 令牌混合 MLPs
- 通道混合 MLPs
- 图层归一化
🧽什么是补丁?
我们如何使用 RGB 图像(MLP 混合器模型的典型输入)来创建它们?
一个混合层将接受一个形状为( batch_size , num_patches , num_channels )的张量,并产生一个相同形状的输出。你可能想知道我们如何从 RGB 图像(这是 MLP 混合器模型的实际输入)中产生这样的张量?参考下图,

图 9:使用 2D 卷积从图像创建补丁。来源:作者创作
假设,给我们一个大小为 4 * 4 的 RGB 图像。我们使用 2D 卷积来创建非重叠* 的面片。假设,我们需要大小为 2 * 2 的正方形小块。如图 9 所示,我们可以从 4 * 4 输入图像中创建 4 个不重叠的面片(图中一个面片用阴影表示)。同样,使用 C 过滤器,我们将尺寸image_dims * image_dims * 3的输入图像转换为形状num_patches * num_patches * C的张量,其中num_patches = image_dims / patch_size。注意,我们假设image_dims可以被patch_size整除。考虑到我们的例子* ,num_patches = ( 4 * 4 ) / ( 2 * 2 ) = 4。
在上面的例子中,我们有num_patches=2。
我们的例子* :特别感谢我们的读者Abder-Rahman Ali博士指出了
num_patches计算中的错误。我们非常感谢他为改进这个故事所做的努力。不重叠* :为了创建不重叠的面片,我们在 Keras 的
Conv2D层设置了kernel_size=patch_size和strides=patch_size。

图 10:调整补丁大小。来源:作者创作
最后,我们调整形状为num_patches * num_patches * C到num_patches^2 * C的面片的大小。
🧱令牌混合 MLPs

图 11:令牌混合 MLP。来源:作者创作
如前所述,每个混合器层由一个令牌混合 MLP 组成。我们想了解记号的含义,这在论文中被强调为,
它[ MLP 混合器]接受形状为“面片*通道”表的一系列线性投影图像面片(也称为标记)作为输入,并保持这种维数。
下面是令牌混合 MLPs 的代码(从图 8 中可以看出LayerNormalization和Permute的作用)
代码片段 3:令牌混合 MLP
顾名思义,它混合令牌,或者换句话说,允许同一通道中不同补丁之间的通信。如图图 11 所示,通道 C 的数量没有被修改,只有P 即面片数量被扩展到某个维度(token_mixing_mlp_dims)并被带回 P 。
🧱通道混合 MLPs

图 12:通道混合 MLP。来源:作者创作
通道混合 MLP 的工作类似于令牌混合 MLP。它们混合信道信息,从而实现信道间的通信。
代码片段 4:通道混合 MLP
如图图 12 所示,面片 P 的数量没有修改,只有 C 即通道的数量扩展到某个维度(channel_mixing_mlp_dims)并恢复到 C 。
⚖️层标准化
这不同于批量标准化

图 13:批量标准化与层标准化。来源:图层归一化说明| PapersWithCode
批量标准化使用整批的平均值和方差来标准化激活。在层归一化的情况下(特别是对于 RNNs),使用一个神经元的所有求和输入的平均值和方差来执行归一化。正如论文中提到的,
在本文中,我们通过计算用于归一化的均值和方差,将批量归一化转换为层归一化,这些均值和方差来自单个训练案例中一层中神经元的所有求和输入。像批量归一化一样,我们也给每个神经元它自己的自适应偏置和增益,它们在归一化之后但在非线性之前被应用。
TF-Keras 团队提供了一个tf.keras.layers.LayerNormalization层来执行这个操作。以下是一些了解图层规范化的资源,
*https://leimao.github.io/blog/Layer-Normalization/ https://www.programmersought.com/article/62405248279/
现在,有了混频器层的完整知识,我们可以继续实现我们的 MLP 混频器模型进行分类。该模型将接受输入 RGB 图像并输出类别概率。
⚔️终结游戏
代码片段 5:组装模型
我们将逐行检查这段代码。
- 首先创建一个输入层,它接收一些所需大小的 RGB 图像。
- 实现一个创建补丁的
Conv2D(记住,我们几十年前讨论过这个问题)。此外,添加一个Reshape层来重塑令牌,并将其转换为 3D 张量。 - 在模型中添加
num_mixer_layers混合器层。 - 最后,一个
LayerNormalization层加上一个GlobalAveragePooling1D层。 - 最后,我们最喜欢的 softmax 激活的
Dense层。
下面是tf.keras.utils.plot_model的输出,描绘了一个单独的混合器层,

图 14:绘图模型的输出。来源:作者创作。
model.summary()的输出,

图 15:模型摘要()的输出。来源:作者创作
就这样,我们刚刚在 TensorFlow 中实现了一个 MLP 混合器模型!
💪🏼来自作者的更多项目/博客/资源
谢谢

皮特·佩德罗萨在 Unsplash上的照片
希望你喜欢这个故事!请随时拨打 equipintelligence@gmail.com 的*联系我。谢谢你,祝你有美好的一天!**
MMDetection 教程——最先进的对象检测库
MMDetection 为使用许多 SOTA 对象检测模型提供了巨大的潜力,例如 FasterRCNN、DETR、VFNet 等等!

在进行一次物体探测比赛时,我通过其他一些参赛者偶然发现了 MMDetection 。而且,我对网上缺乏教程感到很有趣。MMDetection 为大约 30 个对象检测模型的训练和推理提供了一个包装器!它提供了一个编码库和一个命令行界面,使得对象检测原型非常快速。实际上,我很惊讶地看到 VFNet(最近的 SOTA 物体探测网络之一)已经在那里实现了。如果您不熟悉 VarifocalNet,它提供了非常有竞争力的性能,尤其是与 YoloV5 相比(我见过它几次胜过 YoloV5)。
所有的 MMdetection 模型都基于 PyTorch,但是老实说,它使用的代码要少得多(我将在这里展示)。我发现使用这个库的最大好处是,一旦你完成了初始设置,你可以通过修改 1-5 行代码来非常容易地改变你正在使用的模型!所以它对快速实验非常有用。
以下是 MMdetection 支持的所有型号的列表:

来源: Github
让我们开始—安装所需的软件包
我们要做的第一件事是安装“mmcv-full ”,这是一个 mm 库,提供了我们需要的大部分东西。然后克隆 mmdetection Github 存储库并安装需求。请注意,这需要大约 12 分钟,所以有点耐心。
!pip install mmcv-full
!git clone [https://github.com/open-mmlab/mmdetection.git](https://github.com/open-mmlab/mmdetection.git)
%cd mmdetection!pip install -r requirements.txt
接下来要做的是打开存储库文件并导航到“configs”目录。在 configs 目录中,您将找到每个受支持型号的文件。选择其中一个模型后,您将需要下载其预训练重量。为此,打开模型目录,然后打开相应的自述文件。
在自述文件中,您将找到该模型各种实现的权重的所有链接。在本文中,我们将实现 VFNet。它的自述,看起来是这样的:

你可以找到不同骨干的细节和他们的表现。让我们先用 R-50,然后我们可以选择另一个。要下载预训练的重量,只需运行以下命令:
!mkdir checkpoints
!wget -c [https://openmmlab.oss-cn-hangzhou.aliyuncs.com/mmdetection/v2.0/vfnet/vfnet_r50_fpn_mdconv_c3-c5_mstrain_2x_coco/](https://openmmlab.oss-cn-hangzhou.aliyuncs.com/mmdetection/v2.0/vfnet/vfnet_r50_fpn_mdconv_c3-c5_mstrain_2x_coco/$CHECKPOINT\)vfnet_r50_fpn_mdconv_c3-c5_mstrain_2x_coco_20201027pth-6879c318.pth[\](https://openmmlab.oss-cn-hangzhou.aliyuncs.com/mmdetection/v2.0/vfnet/vfnet_r50_fpn_mdconv_c3-c5_mstrain_2x_coco/$CHECKPOINT\)
-O checkpoints/vfnet_r50_fpn_mdconv_c3-c5_mstrain_2x_coco_20201027pth-6879c318.pth
wget 将安装权重,O 标志指定它们的安装位置。这些是可可预训练的重量。
进口
from mmcv import Config
from mmdet.apis import set_random_seed
from mmdet.datasets import build_dataset
from mmdet.models import build_detector
from mmdet.apis import train_detector, init_detector, inference_detector
import torch
构建模型和数据集
接下来要做的是配置模型和数据集。这是大部分工作需要完成的地方,也是很多错误发生的地方。两种最常见的数据集格式是 COCO 和 PASCAL VOC。您当然可以实现自己的格式,但这超出了本文的范围。
由于 COCO 是更常见的一种,我们将在这里使用它。如果你不熟悉这些格式,我建议你看看这篇伟大的文章这里。如果你想看看我们将要使用的数据集,你可以在这里找到它
首先,我们需要指向我们模型的配置文件(来自“configs”的那个)。然后我们需要指向数据集并提供一些其他属性。
cfg = Config.fromfile('./configs/vfnet/vfnet_r50_fpn_mstrain_2x_coco.py')
cfg.dataset_type = 'CocoDataset'cfg.classes = ("Aortic_enlargement", "Atelectasis",
"Calcification", "Cardiomegaly",
"Consolidation", "ILD", "Infiltration",
"Lung_Opacity", "Nodule/Mass", "Other_lesion",
"Pleural_effusion", "Pleural_thickening",
"Pneumothorax", "Pulmonary_fibrosis")
# number of classescfg.model.bbox_head.num_classes = 14
接下来,我们需要指定每个训练、测试和验证数据集:
PREFIX = '/kaggle/input/vinbigdata-coco-dataset-with-wbf-3x-downscaled/vinbigdata-coco-dataset-with-wbf-3x-downscaled/'cfg.data.train.img_prefix = PREFIX
cfg.data.train.classes = cfg.classes
cfg.data.train.ann_file = PREFIX + 'train_annotations.json'
cfg.data.train.type = 'CocoDataset'cfg.data.val.img_prefix = PREFIX
cfg.data.val.classes = cfg.classes
cfg.data.val.ann_file = PREFIX + 'val_annotations.json'
cfg.data.val.type = 'CocoDataset'cfg.data.test.img_prefix = PREFIX
cfg.data.test.classes = cfg.classes
cfg.data.test.ann_file = PREFIX + 'val_annotations.json'
cfg.data.test.type = 'CocoDataset'
由于测试文件实际上没有注释,我们现在只将它指向 val 注释。
现在,我们几乎完成了数据集,让我们做模型,然后建立两者。这一部分是关于指定您想要使用的超参数,例如:
cfg.optimizer.lr = 0.02 / 8
cfg.lr_config.warmup = None
cfg.log_config.interval = 600# Change the evaluation metric since we use customized dataset.
cfg.evaluation.metric = 'bbox'
# We can set the evaluation interval to reduce the evaluation times
cfg.evaluation.interval = 3
# We can set the checkpoint saving interval to reduce the storage cost
cfg.checkpoint_config.interval = 3# Set seed thus the results are more reproducible
cfg.seed = 0
set_random_seed(0, deterministic=False)
cfg.gpu_ids = range(1) cfg.load_from = 'vfnet_r50_fpn_mdconv_c3-c5_mstrain_2x_coco_20201027pth-6879c318.pth'
cfg.work_dir = "../vinbig"cfg.runner.max_epochs = 12
cfg.total_epochs = 12
现在,我们可以构建数据集和模型:
model = build_detector(cfg.model)
datasets = [build_dataset(cfg.data.train)]
然后我们可以开始训练:
train_detector(model, datasets[0], cfg, distributed=False, validate=True)
推理
在 MMdetection 中,您可以通过命令行进行推断,也可以通过 inference_detector 进行推断。我发现命令行更简单,所以让我们在这里使用它。
但是首先,让我们创建之前忽略的测试注释文件。它本质上是一个 JSON 文件,带有空的注释和一个列出每个图像的图像属性的字典。
test_files = f"/kaggle/input/vinbigdata-512-image-dataset/vinbigdata/test"
IMG_SIZE = 512
ids = os.listdir(test_files)
img_infos = []for i, _id in enumerate(ids):
if '.png' in _id:
img_infos.append({
"license": 0,
"url": 'null',
"file_name": _id,
"height": IMG_SIZE,
"width": IMG_SIZE,
"date_captured": 'null',
"id": _id
})val_anno = '/kaggle/input/vinbigdata-coco-dataset-with-wbf-3x-downscaled/vinbigdata-coco-dataset-with-wbf-3x-downscaled/val_annotations.json'with open(val_anno) as f:
val_anno_json = json.load(f)val_anno_json.keys()
val_anno_json['annotations']=[]
val_anno_json['images'] = img_infos
with open('./test_ann.json', 'w') as outfile:
json.dump(val_anno_json, outfile)
现在,让我们运行推断命令。这个命令会有点长,因为我们需要指定配置。您可以通过将配置保存到一个文件并在命令行中使用该文件来缩短它,但我认为这样更容易快速更改。
classes_str='("Aortic_enlargement", "Atelectasis", "Calcification", "Cardiomegaly", "Consolidation", "ILD", "Infiltration", "Lung_Opacity", "Nodule/Mass", "Other_lesion", \
"Pleural_effusion", "Pleural_thickening", "Pneumothorax", "Pulmonary_fibrosis")'MODEL = './configs/vfnet/vfnet_r50_fpn_mstrain_2x_coco.py'
CHECKPOINT = 'checkpoints/vfnet_r50_fpn_mdconv_c3-c5_mstrain_2x_coco_20201027pth-6879c318.pth'!python mmdetection/tools/test.py\$MODEL\
$CHECKPOINT\
--cfg-options dataset_type='CocoDataset' classes=$classes_str\data.train.img_prefix=$cfg.data.train.img_prefix\
data.train.classes=$classes_str\ data.train.ann_file=$cfg.data.train.ann_file\
data.train.type='CocoDataset'\data.val.img_prefix=$cfg.data.val.img_prefix\
data.val.classes=$classes_str\ data.val.ann_file=$cfg.data.val.ann_file\ data.val.type='CocoDataset'\data.test.img_prefix=$cfg.data.test.img_prefix\ data.test.classes=$classes_str\
data.test.ann_file='../test_ann.json'\
data.test.type='CocoDataset'\
model.bbox_head.num_classes='14' evaluation.metric='bbox' work_dir='../vinbig_output' load_from=$cfg.load_from total_epochs='1' --out results.pkl
这与我们之前指定的训练配置基本相同,只是我们现在传递一个测试注释 JSON,运行 1 个时期(推断),并将输出结果保存到“results.pkl”。
就是这样!您可以从 pickle 文件加载预测,如下所示:
import picklewith open('./results.pkl', 'rb') as f:
data = pickle.load(f)
接下来的步骤就看你的了!
关于 MMdetection 最好的事情之一是,现在如果你想改变模型,你可以指向不同的配置文件,下载不同的检查点并运行相同的代码!如果你在其中一个步骤上卡住了,或者你想以不同的方式实现其中的一些,我建议查看他们的文档这里。
MNIST 手写数字分类从零开始使用 Python Numpy。

所以我最近用 PyTorch 为 MNIST 手写数字数据集做了一个分类器,后来,在庆祝了一段时间后,我对自己说,“我能用 vanilla python 重新创建同样的模型吗?”当然,我打算用 NumPy 来做这件事。我的目的不是试图复制 NumPy 漂亮的矩阵乘法,而是通过重新发明轮子来更好地理解这个模型。
我挑战自己在 numpy 中做一个类似的分类器,并在这个过程中学习深度学习的一些核心概念。你可以在我的 GitHub 库中找到代码。
你的工作流程应该是这样的:
- 从 MNIST 网站获取数据
- 将训练图像分成训练集和验证集
- 初始化权重
- 定义我们的激活函数及其导数
- 定义一个向前传递和向后传递的函数(费力!)
- 使用 SGD 批量训练我们的模型,更新权重,并在验证集上测试我们的模型
- 根据测试数据预测并打印精确度
对于这个模型,我决定使用 sigmoid 和 softmax 作为激活函数,以保持简单。
- 获取数据
现在让我们欢迎我们故事的主角,努皮。
定义一个函数来获取 utf-8 编码的数据,将其解压缩并转换为 NumPy 数组。这段代码摘自乔治·霍兹的一个笔记本,你可以在这里找到。
2.由于数据只包含一个训练集和测试集,让我们把训练集分成训练( X_train,Y_train )和验证( X_val,Y_val )。
3.现在让我们定义一个函数,它接受矩阵的大小作为输入,并返回初始化的权重。
神经网络将是一个简单的三层网络。输入图层由 784 个单元组成,对应于 MNIST 数据集中 28 x 28 影像中的每个像素。第二层(隐藏层)下降到 128 个单位,最后是最后一层,10 个单位对应于数字 0-9。

为了简单起见,只显示了每个单元的一个连接。图片作者。
因此,l1 是大小为(784,128)的矩阵,l2 是大小为(128,10)的矩阵。
4.函数可以概括为接受输入和输出的过程。激活函数是那些将神经元的加权和作为输入(在幅度上变化)并将其转化为可以容易理解或馈入下一层的有意义数据的函数。Sigmoid 和 softmax 是我们将要使用的两个这样的激活函数。
乙状结肠功能:
sigmoid 函数定义为:

图片作者。
它可以通过绘制一个取 f(x) = y 的图形来可视化:

图片作者。
它可以接受任何东西,然后吐出一个 0 到 1 之间的数字。在 python 代码中,sigmoid 和它的派生代码如下所示:
在我们的模型中,我们使用 sigmoid 函数将第 1 层给出的随机输出压缩成 0 到 1 之间的数字。
Softmax 功能:
Softmax 函数接受一个向量作为输入,并输出一个大小相同的向量,其元素总和为 1。输出向量中的每个元素都在 0 和 1 之间,因此这些值可以被解释为概率。

图片作者。
python 代码中的 Softmax 函数如下所示:
为了理解 softmax 是如何工作的,让我们声明一个简单的 numpy 数组并在其上调用 softmax 函数。
从第二个结果可以清楚地看出,虽然 out 之和不是 1,但其 softmax 之和确实是 1。因此,我们可以将 softmax 数组的值视为父数组中各个元素的概率。
[1,2,3]的分布中的 1 是最不可能的,因为它的 softmax 值是 0.090,另一方面,同一分布中的 3 是很可能的,具有 softmax 值 0.6652。
在我们的模型中,输出层吐出具有不同大小的形状 10 的向量。因此,我们使用 softmax 来标准化我们的结果。例如,让我们取一个看起来与我们的模型输出相似的向量,并将其提供给 softmax 函数:
现在,如果我们对 softmax 结果运行 NumPy 的 argmax 函数,我们将获得具有最大值的元素的索引:
为了防止溢出,我们使用简化版本的 softmax。现在让我们来定义 softmax 及其导数:
5.现在让我们定义一个函数,在迭代过程中执行向前传递和向后传递。
让我们一行一行地检查代码,并理解它是如何工作的。
Y_train 本质上是一个 50,000 个元素的向量,具有与 X_train 中的图像相对应的正确的数字。
另一方面,我们的模型为每个训练示例输出 10 个元素的向量。因此,我们的主要任务是将 Y_train 转换为向量,其中 1 对应于“正确的位,0 对应于其余的位。
如您所见,第五个位置中索引为' 4 的元素的值为 1,而其余元素的值为 0。
现在是正向传递,其中输入矩阵与权重相乘,并通过各自的激活函数。
在第一行中,x 是与第一层相乘的矩阵,并通过 sigmoid 函数进行归一化。

作者截图。
sigmoid 乘积稍后与第二层矩阵相乘,并通过 softmax 函数得到大小为 10 的输出向量,类似于我们的目标向量。

作者截图。
现在是时候反向传播并更新我们的权重了。我们的目标是找到与 l1 和 l2 形状相似的矩阵,可以从 l1 和 l2 中减去这些矩阵,通过最小化误差使更接近于理想答案。
但是到底是什么错误?
误差=2(出目标)*
这将计算当前历元中的误差及其方向。我们离实现理想的答案还有多远。
让我们看看'出来的'到底是什么:
out = softmax(x _ L2)
它是我们神经网络的产物。
我们现在需要找到一个实体来告诉我们,如果 x_l2 发生变化,那么 out 会变化多少。
没错。你猜对了。这个实体叫做导数!我们需要找到 softmax 在空间中的点 x_l2 处的导数,这样当我们通过(**误差 soft max 在 x_l2** 处的导数)改变* x_l2 时,我们的 out 移动得更接近理想目标,python 代码中的情况如下:
但是 x_l2 到底是什么?
x _ L2 = x _ sigmoid @ L2
我们现在知道需要改变 x_l2 多少,但是 x_l2 是第一层的 sigmoid 输出和 l2 的乘积,我们不能改变它。我们只能控制重量!因此,为了将之前的误差转化为可以从 l2 中减去的误差,让我们定义一个 update_l2。
唷!我们已经完成了第二层,现在让我们做同样的第一层。我们将对空间中的点 x_l1 处的 sigmoid 求导,而不是对 softmax 求导。
并定义了可以从 l1 中减去的 update_l1 。
这样我们就结束了向前和向后的传递。
6.现在是训练部分。我们将通过以 0.001 的学习率分批发送 128 个训练集来执行随机梯度下降。我们可以通过简单地创建包含从 0 到 50000(X_train 的大小)中随机选择的 128 个元素的样本集,并从具有各自索引的 X _ train 和 Y_train 中提取所有元素来做到这一点。
现在让我们通过 forward_backward() 函数发送 x 和 y,并将其输出分别存储在 out、update_l1 和 update_l2 中。
选择类别并计算批次的精确度。
使用均方误差损失计算损失。
更新权重。
最后在一个它还没有见过的数据集上测试该模型,即验证集。
将所有内容放在一个 for 循环中:
是的。从输出可以清楚地看出,模型已经学会了如何预测。
现在让我们来想象一下训练和验证的准确性。
训练精度波动,但验证精度最初增加,随后趋于平稳,表明该模型已经达到其极限。
现在让我们在提供的测试集上测试我们的模型。
它能应付理想的测试吗?受乔治·霍兹从他的 笔记本 中得到的启发,让我们在看起来像 7 的东西上测试我们的模型。
可能是 1?
好了,我们已经成功地训练和测试了一个深度学习模型,完全由 python NumPy 制成!
如果你是深度学习的新手,请查看我之前的博客,深度学习的初学者指南:
此外,请查看由普拉桑娜·德瓦迪加撰写的关于 AGI 的一些精彩内容:
GitHub 资源库:
https://github.com/karynaur/mnist-from-numpy
从统计学观点看模型不可知的局部解释模型
思想和理论
如果不理解局部解释方法的数学表达式,它们的属性可能是违反直觉的。这个博客文章系列介绍了最常用的局部解释方法,并讨论了它们的局部性。
1 导言
在最近的一篇论文 (Ghalebikesabi 等人,2021)中,我们已经表明,局部解释模型并不像人们可能假设的那样是局部的。随着越来越多的模型可解释性工具的开源实现变得可用,理解这些方法实际上在做什么以及为什么它们的本地解释是有限的变得越来越重要。在这个博客系列文章中,我们想要关注四个本地模型不可知的可解释性工具的统计分析和讨论:
- 切线近似
- 石灰
- SHAP
- SHAP 街区
如果你已经知道什么是模型可解释性、可解释人工智能或 XAI,那就等到第二部!
模型可解释性介绍
那么什么是模型解释呢?模型解释可以通过对黑盒模型的解释来模糊地概括。通常,它们为用户提供特征属性,即每个特征的重要性分数。既然“解释一个模型”可能意味着任何事情,一个更好的问题是问解释模型回答了哪些问题。有不同的原因,用户可能对解释黑盒感兴趣,我们现在讨论其中的一些。
- 理解。首先,用户可能会对为什么黑盒会做出某种预测感兴趣。为什么美国有线电视新闻网把哈士奇的图片归类为狼?
- 信任。解释可以让用户信任或不信任模型。Ribeiro 等人(2016 年)表明,如果解释与 Husky 的例子相反,用户就不太可能相信黑盒模型。
- 功能选择。模型可解释性工具也可用于选择特征子集,以防止过度拟合。
- 可行的建议。特征属性形式的模型解释可用于向用户提供关于改变什么以接收不同黑盒响应的建议。被拒绝信贷的信贷申请人的高债务的负面归因暗示减少债务可以导致信贷批准。
根据数据分析师有兴趣回答的问题,应该使用不同的解释模型。解释模型可以根据以下维度进行分类(参见 Tanner (2019))

图片复制自里贝罗等人(2016) 。
- 分析阶段。解释模型可以是固有的,也可以是事后的。内在解释模型是简单的模型,如基于规则的模型或加法函数,即以 GAM 的形式,代替黑盒并进行预测。事后解释模型用于解释已经训练过的黑盒模型。
- 模型特异性。有些解释模型是针对底层黑盒的。例如,显著图只对可微函数起作用,而模型不可知的可解释性工具可以应用于任何模型。
- 地点。最后但同样重要的是,可解释性工具可以根据它们的位置来区分。虽然它们中的一些解释了在感兴趣的实例 x 处的局部模型行为,但是其他的解释了黑盒的全局模型行为。
在这个博客文章系列中,我们关注用于事后可解释性的本地模型不可知的解释模型。
2 切线近似值
在当地解释任何模型最简单的方法是什么?在任何分析课程中学习的第一个概念是一阶泰勒近似法:可以在感兴趣的实例 x 处通过拟合围绕 x 的切线来解释黑盒函数 f(x) 。当我们假设对模型进行黑盒访问时,必须通过在 x 周围的小邻域中评估模型拟合来近似这条切线,就像在局部线性核回归中所做的那样。正切近似值的示例有 MAPLE (Plumb,2018 年)或 MeLIME (Botari,2020 年)用于表格数据集。Ribeiro 等人(2016 年)提出的石灰是切线近似法的概括。

图片来自里贝罗等人(2016) 。
图为
- 一个黑盒分类模型 f :粉色和蓝色区域
- 一个正在被解释的例子:加粗的红十字
- 局部采样的实例,并根据其接近程度进行加权:红叉和蓝圈
- 局部忠实解释 g :虚线
根据 Ribeiro 等人(2016 年)的说法,可解释性模型要做到局部忠实,它“必须对应于模型在预测实例附近的行为方式”。正如我们将在后面看到的,这个定义留下了很多解释的空间!数学上,LIME 解决了以下优化问题

其中 G 是解释模型族(即切线逼近情况下的线性模型), L 是损失函数,该损失函数确保 g 在围绕 x 的局部邻域中拟合到 f ,该局部邻域由加权核\(\pi_x\)标识,该加权核通常是距离函数的指数核,即欧几里德距离,具有固定带宽\(\sigma\)即\(\pi_x=exp(-|x^*-x|/\sigma)\).最终,$ \适马(g)$是一个惩罚项,即 l1 损失,它可以防止过度拟合并增加 g 的简单性。
为了说明的目的,现在让我们考虑由下式给出的黑盒函数

当 x_1 为正时,在 x_2 中表现为正,当 x_1 为非正时,在 x_2 中表现为负。

使用带宽为 1 的局部岭回归计算的特征属性(图片由作者提供)。
如果我们用上述方程局部拟合一条切线到 x=(x_1,2),特征-2 的属性对所有 x_1>0 都是正的,并增加较大的 x_1,这符合抛物线的递增梯度的性质。总而言之,切线逼近看起来很棒,不是吗?对于 x1 的绝对小负值,它们的归属是反直觉的。对于 x_1=-0.1,我们看到 x_1 的属性是正的,即使局部模型行为预期是负的!这是因为在 x 周围的邻居的 x_2 的积极影响超过了邻居的消极影响。
显然切线近似法丢失了黑盒模型中包含的信息。这并不奇怪:我们正试图用一阶泰勒近似来预测局部模型的行为。如果它在本地很合适,我们应该从一开始就使用本地线性黑盒。有关详细讨论,请参见 Rudin (2019)。
在下一篇博文中,我们将深入探讨沙普利价值观,并解释为什么 SHAP 和莱姆并不像他们经常宣称的那样本地化!
参考
[1] T. Botari,F. Hvilshø,R. Izbicki,A. C. de Carvalho (2020 年)。MeLIME:对机器学习模型有意义的局部解释。 arXiv 预印本 arXiv:2009.05818 。
[2] S. Ghalebikesabi、L. Ter-Minassian、k . Diaz-奥尔达斯和 C. Holmes (2021 年)。论局部解释模型的局部性。 arxiv 预印本 arXiv:2106.14648。
[3] M .里贝罗、s .辛格和 C. Guestrin (2016 年)。“我为什么要相信你?”解释任何分类器的预测。第 22 届 ACM SIGKDD 知识发现和数据挖掘国际会议论文集。
[4] C .鲁丁(2019)。停止解释高风险决策的黑牛机器学习模型,而是使用可解释的模型。自然机器智能1.5:206–215
[5] G .普拉姆,d .莫里托尔,a .塔尔沃卡尔。(2018).模型不可知论者监督的局部解释。 arXiv 预印本 arXiv:1807.02910 。
[6] G .坦纳(2019)。机器学习模型解释导论。https://Gilbert tanner . com/blog/introduction-to-machine-learning-model-interpretation 的博文
模式平均方法:如何和为什么建立集合模式
实践教程
平均预测的乐趣和利润,以及处理模型选择的不确定性。用 R 中的例子!

卡通组件是 CC-BY 4.0 Allison Horst(@ Allison _ Horst上 Twitter)
介绍
建立模型很难。选择构建什么样的模型可能更加困难。由于似乎有无限多种不同的建模方法可供选择(甚至有更多的独立实现),很难猜测哪种方法最适合您的数据,尤其是当您处理的数据会随着时间的推移而发生变化,新的观察值或预测值会加入其中。
通常,我们用置信区间和标准误差来揭示这种不确定性。然而,在选择单个模型时,我们通常不会讨论我们对该模型是正确的有多大信心——相反,我们只展示和报告我们的最终选择,就好像其他候选模型不可能一样好或者甚至更好。
集合模型证明了处理这种不确定性的一种方法。通过对少数候选模型的预测进行平均,集合承认可能有多个模型可以用来描述我们的数据——通过对平均值进行加权,我们可以传达我们对每个模型的世界观有多自信。当然,虽然这一切都很好,也很华丽,但它也需要工作——模型平均提供了,通常会减少预测误差,甚至超过最佳的单个组件模型。
模型平均有很多方法。本文的其余部分将介绍一些最简单的方法——等权重平均法、基于拟合的平均法和基于模型的组合法——您可以轻松实现这些方法,而无需担心减缓迭代时间或使建模代码过于复杂。
入门指南
我们将使用以下库进行数据操作和可视化:
[library](https://rdrr.io/r/base/library.html)([dplyr](https://dplyr.tidyverse.org))
[library](https://rdrr.io/r/base/library.html)([tidyr](https://tidyr.tidyverse.org))
[library](https://rdrr.io/r/base/library.html)([ggplot2](http://ggplot2.tidyverse.org))
[library](https://rdrr.io/r/base/library.html)([knitr](https://yihui.org/knitr/))
此外,我们将使用ranger和lightgbm来开发组件模型:
[library](https://rdrr.io/r/base/library.html)([ranger](https://github.com/imbs-hl/ranger))
[library](https://rdrr.io/r/base/library.html)([lightgbm](https://github.com/Microsoft/LightGBM))
最后,我们需要建模的实际数据。对于这个例子,我们将使用航班详细信息和天气数据来构建模型,预测nycflights13包中包含的航班的到达延迟。下一段代码将把我们的数据预处理成适合模型的格式:
flights <- nycflights13::[flights](https://rdrr.io/pkg/nycflights13/man/flights.html)
weather <- nycflights13::[weather](https://rdrr.io/pkg/nycflights13/man/weather.html) %>%
[select](https://dplyr.tidyverse.org/reference/select.html)(-wind_gust) # About 80% missing values
# combine the two data frames into one complete set
flight_data <- flights %>%
[left_join](https://dplyr.tidyverse.org/reference/mutate-joins.html)(weather,
by = [c](https://rdrr.io/r/base/c.html)("year",
"month",
"day",
"origin",
"hour",
"time_hour")
) %>%
[drop_na](https://tidyr.tidyverse.org/reference/drop_na.html)()
flight_data <- flight_data %>%
# Drop 37 pretty dramatic outliers
[filter](https://dplyr.tidyverse.org/reference/filter.html)(arr_delay <= 500) %>%
# Get rid of useless predictors --
# these each cause problems with at least one of our regressions
[select](https://dplyr.tidyverse.org/reference/select.html)(-year, -time_hour, -minute) %>%
# We'll only work with numeric predictors for now
[select_if](https://dplyr.tidyverse.org/reference/select_all.html)(is.numeric)
对于最后一个预处理步骤,我们将把数据分成训练集、验证集和测试集(20%用于验证和测试,其余的用于训练)。我们将根据验证集使用模型性能来确定平均值的权重。
[set.seed](https://rdrr.io/r/base/Random.html)(123)
# Generate a random sequence to subset our data into
# train/validate/test splits
row_index <- [sample](https://rdrr.io/r/base/sample.html)([nrow](https://rdrr.io/r/base/nrow.html)(flight_data), [nrow](https://rdrr.io/r/base/nrow.html)(flight_data))
# Testing gets the 20% of data with the highest random index values
flight_testing <- flight_data[
row_index >= [nrow](https://rdrr.io/r/base/nrow.html)(flight_data) * 0.8,
]
# Validation gets the next highest 20%
flight_validation <- flight_data[
row_index >= [nrow](https://rdrr.io/r/base/nrow.html)(flight_data) * 0.6 &
row_index < [nrow](https://rdrr.io/r/base/nrow.html)(flight_data) * 0.8,
]
# Training gets the rest
flight_training <- flight_data[
row_index < [nrow](https://rdrr.io/r/base/nrow.html)(flight_data) * 0.6,
]
# LightGBM requires matrices, rather than data frames:
xtrain <- [as.matrix](https://rdrr.io/r/base/matrix.html)([select](https://dplyr.tidyverse.org/reference/select.html)(flight_training, -arr_delay))
ytrain <- [as.matrix](https://rdrr.io/r/base/matrix.html)(flight_training[["arr_delay"]])
xvalid <- [as.matrix](https://rdrr.io/r/base/matrix.html)([select](https://dplyr.tidyverse.org/reference/select.html)(flight_validation, -arr_delay))
xtest <- [as.matrix](https://rdrr.io/r/base/matrix.html)([select](https://dplyr.tidyverse.org/reference/select.html)(flight_testing, -arr_delay))
这样一来,是时候开始训练我们的模型了!
组件模型
线性模型
让我们从一个简单的线性回归模型开始,使用航班数据集中的所有预测值来尝试和估计到达延误:
linear_model <- [lm](https://rdrr.io/r/stats/lm.html)(arr_delay ~ ., flight_training)
[summary](https://rdrr.io/r/base/summary.html)(linear_model)Call:
lm(formula = arr_delay ~ ., data = flight_training)
Residuals:
Min 1Q Median 3Q Max
-58.895 -9.133 -1.538 7.076 159.388
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -4.613e+00 5.992e+00 -0.770 0.441394
month 3.825e-02 1.117e-02 3.424 0.000618 ***
day 2.220e-02 4.109e-03 5.404 6.51e-08 ***
dep_time 9.509e-05 2.795e-04 0.340 0.733722
sched_dep_time -3.492e-03 1.894e-03 -1.844 0.065257 .
dep_delay 1.013e+00 1.068e-03 948.332 < 2e-16 ***
arr_time 8.816e-04 1.182e-04 7.460 8.68e-14 ***
sched_arr_time -4.713e-03 1.478e-04 -31.884 < 2e-16 ***
flight -4.692e-05 2.541e-05 -1.846 0.064863 .
air_time 7.563e-01 3.074e-03 246.006 < 2e-16 ***
distance -9.792e-02 3.925e-04 -249.500 < 2e-16 ***
hour 6.000e-01 1.871e-01 3.207 0.001341 **
temp 1.173e-01 2.232e-02 5.254 1.49e-07 ***
dewp 3.632e-02 2.405e-02 1.510 0.130928
humid 1.860e-02 1.229e-02 1.514 0.130053
wind_dir -6.076e-03 4.009e-04 -15.158 < 2e-16 ***
wind_speed 1.920e-01 7.538e-03 25.471 < 2e-16 ***
precip 2.688e+01 3.014e+00 8.920 < 2e-16 ***
pressure -1.634e-02 5.619e-03 -2.909 0.003631 **
visib -4.603e-01 3.239e-02 -14.212 < 2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 14.82 on 170687 degrees of freedom
Multiple R-squared: 0.8707, Adjusted R-squared: 0.8707
F-statistic: 6.048e+04 on 19 and 170687 DF, p-value: < 2.2e-16
酷!我们有了第一个模型——它看起来相当合适,R 为 0.87。我们或许可以通过更有选择性地使用我们的术语或加入一些交互术语来使这个模型变得更好——但作为我们将纳入我们平均值的模型的第一次尝试,这是非常好的。
当然,我们希望确保该模型能够在其训练数据之外进行推广,让我们也用它来针对我们的验证集进行预测:
flight_validation$lm_pred <- [predict](https://rdrr.io/r/stats/predict.html)(
linear_model,
newdata = flight_validation
)
[sqrt](https://rdrr.io/r/base/MathFun.html)(
[mean](https://rdrr.io/r/base/mean.html)(
(flight_validation$lm_pred - flight_validation$arr_delay)^2
)
)[1] 14.73962[summary](https://rdrr.io/r/base/summary.html)([lm](https://rdrr.io/r/stats/lm.html)(arr_delay ~ lm_pred, flight_validation))$r.squared[1] 0.8684178
r 保持在大约 0.87,RMSE 在大约 14.74 分钟——考虑到验证集范围从-75 到 485 分钟的延迟,对于一个天真实现的线性模型来说,感觉还不算太坏。
随机森林
所以我们已经排序了我们的第一个模型,但是我们需要更多来取平均值!虽然我们可以平均出许多具有不同参数的线性模型,但是将一些不同的算法组合成组件模型感觉更有趣。因此,让我们使用 ranger 实现一个随机森林来表示我们的数据——公平的警告,这个需要一点时间来训练!
ranger_model <- ranger::[ranger](https://rdrr.io/pkg/ranger/man/ranger.html)(
arr_delay ~ .,
data = flight_training
)[sqrt](https://rdrr.io/r/base/MathFun.html)(ranger_model$prediction.error)[1] 11.08573ranger_model$r.squared[1] 0.9276561
因此,这个模型的 RMSE 为 11.09,R 为 0.93,比我们的线性模型有所改进!虽然我们可以通过仔细的调整来弥补一些改进,但看起来这个版本足够适合作为我们合奏中的一个例子。和以前一样,我们希望通过使用该模型为我们的验证集生成预测来检验该模型的泛化能力:
ranger_predictions <- [predict](https://rdrr.io/r/stats/predict.html)(
ranger_model,
data = flight_validation
)
flight_validation$ranger_pred <- ranger_predictions$predictions
[sqrt](https://rdrr.io/r/base/MathFun.html)(
[mean](https://rdrr.io/r/base/mean.html)(
(
flight_validation$ranger_pred -
flight_validation$arr_delay
)^2
)
)[1] 10.96209[summary](https://rdrr.io/r/base/summary.html)([lm](https://rdrr.io/r/stats/lm.html)(arr_delay ~ ranger_pred, flight_validation))$r.squared[1] 0.9302306
我们的模型实际上在验证集上表现得(非常)好一点!
马恩岛
所以这是两个模型排序!为了完整起见,让我们实现第三个也是最后一个组件模型,这一次使用 LightGBM 包来安装梯度推进机器。与上两个相似,我们不会对这个模型进行太多的参数化,我对模型拟合默认值的唯一更改是使用 100 轮,让 boosting 算法达到与我们的其他两个模型相同的性能范围。
lightgbm_model <- lightgbm::[lightgbm](https://rdrr.io/pkg/lightgbm/man/lightgbm.html)(xtrain,
ytrain,
nrounds = 100,
obj = "regression",
metric = "rmse",
# Suppress output
force_col_wise = TRUE,
verbose = 0L)
lightgbm_model没有像我们的线性模型和随机森林那样简单的方法来评估包内性能。我们将直接跳到验证集:
flight_validation$lightgbm_pred <- [predict](https://rdrr.io/r/stats/predict.html)(
lightgbm_model,
xvalid
)
[sqrt](https://rdrr.io/r/base/MathFun.html)(
[mean](https://rdrr.io/r/base/mean.html)(
(
flight_validation$lightgbm_pred -
flight_validation$arr_delay
)^2
)
)[1] 10.4088[summary](https://rdrr.io/r/base/summary.html)([lm](https://rdrr.io/r/stats/lm.html)(arr_delay ~ lightgbm_pred, flight_validation))$r.squared[1] 0.9347398
所以看起来 LightGBM 模型的性能和我们的随机森林差不多(如果不是稍微好一点的话)!作为参考,以下是我们每个候选模型的 RMSE 值:
prediction_values <- flight_validation %>%
# Only select our y and y-hat columns
[select](https://dplyr.tidyverse.org/reference/select.html)([ends_with](https://tidyselect.r-lib.org/reference/starts_with.html)("pred"), [matches](https://tidyselect.r-lib.org/reference/starts_with.html)("arr_delay"))
prediction_plots <- prediction_values %>%
[pivot_longer](https://tidyr.tidyverse.org/reference/pivot_longer.html)(cols = -arr_delay) %>%
[mutate](https://dplyr.tidyverse.org/reference/mutate.html)(
name = [regmatches](https://rdrr.io/r/base/regmatches.html)(name,
[regexpr](https://rdrr.io/r/base/grep.html)(".*(?=_pred)", name, perl = TRUE)),
resid = value - arr_delay,
name = [factor](https://rdrr.io/r/base/factor.html)(name, levels = [c](https://rdrr.io/r/base/c.html)("lightgbm", "ranger", "lm"))
)
prediction_plots %>%
[group_by](https://dplyr.tidyverse.org/reference/group_by.html)(Model = name) %>%
[summarise](https://dplyr.tidyverse.org/reference/summarise.html)(RMSE = [sqrt](https://rdrr.io/r/base/MathFun.html)([mean](https://rdrr.io/r/base/mean.html)(resid^2)), .groups = "drop") %>%
[arrange](https://dplyr.tidyverse.org/reference/arrange.html)(RMSE)Model RMSE
lightgbm 10.40880
ranger 10.96209
lm 14.73962
当然,单个指标并不能说明全部情况——查看我们预测的诊断图,以尝试理解我们的预测与数据匹配的模式可能会有所帮助。例如,“线性模型平均差大约 4 分钟”在理论上是很好的,但是下图可以帮助我们看到,例如,线性模型在 0 分钟延迟(其中大部分数据是聚集的)左右表现稍差,而我们的随机森林在更高的极端情况下表现更差:
prediction_plots %>%
[ggplot](https://ggplot2.tidyverse.org/reference/ggplot.html)([aes](https://ggplot2.tidyverse.org/reference/aes.html)(value, arr_delay)) +
[geom_point](https://ggplot2.tidyverse.org/reference/geom_point.html)(alpha = 0.05) +
[geom_abline](https://ggplot2.tidyverse.org/reference/geom_abline.html)(slope = 1, intercept = 0, color = "red") +
[facet_wrap](https://ggplot2.tidyverse.org/reference/facet_wrap.html)(~ name) +
[theme_minimal](https://ggplot2.tidyverse.org/reference/ggtheme.html)() +
[labs](https://ggplot2.tidyverse.org/reference/labs.html)(x = "Predicted",
y = "Actual")

模型平均
有了我们的候选模型,我们现在已经完全准备好进入模型平均方法了!我们将介绍三种方法的基本实现(等权重、基于拟合的权重和基于模型的估计),然后在最后评估我们的整体。
同等重量
也许平均模型最明显的方法是取模型预测的简单算术平均值。这种方法预先假定您的每个模型都是您的底层数据的同样好的表示;因为这里不是这种情况,我们可能期望这种方法不会从整体上大大减少误差。
不过,这种方法的一个好处是实现根本不需要时间:
prediction_values <- prediction_values %>%
[mutate](https://dplyr.tidyverse.org/reference/mutate.html)(
equal_weight_pred = (lm_pred + ranger_pred + lightgbm_pred) / 3
)
基于拟合的权重
一个稍微复杂一点的方法是基于模型性能的一些度量来加权模型。几乎任何具有跨组件模型的标准定义的度量标准都可以使用(例如,AIC 或 BIC 的嵌套模型或 MSE 和 MAPE);到目前为止,我们一直在使用 RMSE,我们将使用它来衡量我们的错误。
在数据科学的宏大计划中,基于拟合统计的加权模型也相对容易。首先,计算每个模型的拟合统计数据:
model_rmse <- [vapply](https://rdrr.io/r/base/lapply.html)(
prediction_values,
function(x) [sqrt](https://rdrr.io/r/base/MathFun.html)(
[mean](https://rdrr.io/r/base/mean.html)(
(x - prediction_values$arr_delay)^2
)
),
[numeric](https://rdrr.io/r/base/numeric.html)(1)
)[1:3] # Only our 3 component models!model_rmselm_pred ranger_pred lightgbm_pred
14.73962 10.96209 10.40880
然后,根据您的统计数据,您可能需要取每个值的倒数,因为 RMSEs 越低越好,我们需要这样做:
rmse_weights <- (1 / (model_rmse))
最后,将您的权重计算为整个集合的比例-您可以将这些值视为每个组件贡献的集合预测的比例:
rmse_weights <- rmse_weights / [sum](https://rdrr.io/r/base/sum.html)(rmse_weights)
rmse_weightslm_pred ranger_pred lightgbm_pred
0.2659099 0.3575422 0.3765479
使用集合进行预测相对容易,只需将每个预测值乘以它们的比例,然后将结果相加:
prediction_values <- prediction_values %>%
[mutate](https://dplyr.tidyverse.org/reference/mutate.html)(
fit_based_pred = (
(lm_pred * rmse_weights["lm_pred"]) +
(ranger_pred * rmse_weights["ranger_pred"]) +
(lightgbm_pred * rmse_weights["lightgbm_pred"])
)
)
基于模型的权重
我们将走过的最后一个平均方法稍微复杂一点,但仍然很容易理解:取你的模型输出,转过来,用它们作为模型输入。

我们这里的玩具示例非常适合这种方法,我们已经在图表中看到,我们的预测和真实值之间存在很强的线性关系,这种关系对于每个模型都略有不同:

从该图中,我们可以猜测,将我们的组分预测作为特征组合的线性模型将非常适合对这些模型进行平均。为此,我们只需建立一个线性模型:
predictions_model <- [lm](https://rdrr.io/r/stats/lm.html)(
arr_delay ~ lm_pred * ranger_pred * lightgbm_pred,
data = prediction_values
)
然后用它来生成预测,就像我们最初的分量线性模型一样:
prediction_values$model_based_pred <- [predict](https://rdrr.io/r/stats/predict.html)(
predictions_model,
newdata = prediction_values
)
请注意,如果我们看到我们的预测和真实值之间的非线性关系,我们会希望依靠非线性方法来平均预测;碰巧的是,我们的模型已经非常适合基础数据,并且可以用简单的线性回归很好地表示。
我们表现如何?
现在我们已经准备好了我们的集合模型,是时候根据我们的测试集来评估我们所有的模型了!
第一步是使用我们的组件模型为测试集生成预测:
flight_testing$lm_pred <- [predict](https://rdrr.io/r/stats/predict.html)(
linear_model,
newdata = flight_testing
)
ranger_predictions <- [predict](https://rdrr.io/r/stats/predict.html)(
ranger_model,
data = flight_testing
)
flight_testing$ranger_pred <- ranger_predictions$predictions
flight_testing$lightgbm_pred <- [predict](https://rdrr.io/r/stats/predict.html)(
lightgbm_model,
xtest
)
我们可以使用这些预测来生成我们的集合预测。请注意,我们仍然使用根据验证数据校准的权重和模型——我们(理论上)不应该知道测试集的“真实”值,所以我们现在不能重新加权我们的平均值!
flight_testing <- flight_testing %>%
[mutate](https://dplyr.tidyverse.org/reference/mutate.html)(
equal_weight_pred = (lm_pred + ranger_pred + lightgbm_pred) / 3
)
flight_testing <- flight_testing %>%
[mutate](https://dplyr.tidyverse.org/reference/mutate.html)(
fit_based_pred = (
(lm_pred * rmse_weights["lm_pred"]) +
(ranger_pred * rmse_weights["ranger_pred"]) +
(lightgbm_pred * rmse_weights["lightgbm_pred"])
)
)
flight_testing$model_based_pred <- [predict](https://rdrr.io/r/stats/predict.html)(
predictions_model,
newdata = flight_testing
)
我们表现如何?让我们来看看每个型号的 RMSE:
prediction_values <- flight_testing %>%
[select](https://dplyr.tidyverse.org/reference/select.html)([ends_with](https://tidyselect.r-lib.org/reference/starts_with.html)("pred"), [matches](https://tidyselect.r-lib.org/reference/starts_with.html)("arr_delay"))
prediction_plots <- prediction_values %>%
[pivot_longer](https://tidyr.tidyverse.org/reference/pivot_longer.html)(cols = -arr_delay) %>%
[mutate](https://dplyr.tidyverse.org/reference/mutate.html)(
name = [regmatches](https://rdrr.io/r/base/regmatches.html)(name,
[regexpr](https://rdrr.io/r/base/grep.html)(".*(?=_pred)", name, perl = TRUE)),
resid = value - arr_delay,
name = [factor](https://rdrr.io/r/base/factor.html)(name,
levels = [c](https://rdrr.io/r/base/c.html)("lightgbm",
"ranger",
"lm",
"model_based",
"fit_based",
"equal_weight")
)
)
prediction_plots %>%
[group_by](https://dplyr.tidyverse.org/reference/group_by.html)(Model = name) %>%
[summarise](https://dplyr.tidyverse.org/reference/summarise.html)(RMSE = [sqrt](https://rdrr.io/r/base/MathFun.html)([mean](https://rdrr.io/r/base/mean.html)(resid^2)), .groups = "drop") %>%
[arrange](https://dplyr.tidyverse.org/reference/arrange.html)(RMSE)Model RMSE
model_based 9.492409
lightgbm 10.290113
ranger 10.968544
fit_based 11.057728
equal_weight 11.311836
lm 14.621943prediction_plots %>%
[ggplot](https://ggplot2.tidyverse.org/reference/ggplot.html)([aes](https://ggplot2.tidyverse.org/reference/aes.html)(value, arr_delay)) +
[geom_point](https://ggplot2.tidyverse.org/reference/geom_point.html)(alpha = 0.05) +
[geom_abline](https://ggplot2.tidyverse.org/reference/geom_abline.html)(slope = 1, intercept = 0, color = "red") +
[facet_wrap](https://ggplot2.tidyverse.org/reference/facet_wrap.html)(~ name) +
[theme_minimal](https://ggplot2.tidyverse.org/reference/ggtheme.html)() +
[labs](https://ggplot2.tidyverse.org/reference/labs.html)(x = "Predicted",
y = "Actual")

酷——我们基于模型的集合实际上比任何组件都表现得更好!虽然等权重和基于拟合的平均值相当中庸,但在其他情况下,这些方法也有助于减少预测中的偏差,并产生比任何组件模型方差都小的估计值。
结论
模型平均是一个强大的工具,可以减少模型偏差,并解决在试图选择“最佳”模型的情况下隐含的不确定性。虽然存在大量复杂且计算昂贵的平均方法,并且可以极大地提高模型性能,但更简单的集成方法可以提供相同的好处,而不一定会导致相同的成本。
最初发表于https://www . mm 218 . dev/posts/2021/01/model-averaging——参见该网站的文献引用和改进的语法突出显示。
利用提升和增益分析进行模型效益评估
行业笔记
人们经常忽视的基本分析

作者图片
如果你喜欢我的内容,并想获得更多关于数据或作为数据科学家的日常生活的深入知识,请考虑在这里订阅我的时事通讯。
作为数据科学家,开发机器学习模型是我们日常工作的一部分,也是我们最初被雇佣的原因。然而,我们开发的机器学习模型不只是为了展示,而是解决业务问题的实际工具。这就是为什么我们需要评估我们的机器学习模型,以衡量我们的模型如何影响业务。
许多数据科学家会用技术指标来衡量人们,比如准确度、精确度、F1 分数、ROC-AUC 等等。这是一个必要的度量标准,但是有时这些度量标准并不能反映该模型在商业上的表现。尽管如此,商业人士需要知道我们的模型与随机目标相比,在商业上有何不同。这就是为什么我们使用升力和增益分析— 来衡量我们的预测模型比没有模型时好多少。
事不宜迟,我们开始吧。
升力和增益分析
提升和收益分析是评估模型预测和业务收益的分析。它常用于营销目标分析,但不受限制。
在典型的升力和增益分析中,分析结果将显示在下图中。

作者图片
增益和提升图是评估分类模型性能的直观工具。与评估总体的混淆矩阵不同,增益和提升图评估部分总体的模型性能。这意味着我们根据在一部分人口中使用该模型可能获得的益处来评估该模型。
收益和提升分析的好处来自于我们 80%的收入来自于 20%的客户。这是增益和升力图计算中使用的十分位数分析的主要部分。十分位数分析如下图所示。

十分位数分析(资料来源:http://www.totalcustomeranalytics.com/decile_analysis.htm)
十分位数分析如何适用于增益和提升分析?让我们后退几步,解释一下如何从一开始就计算出增益和提升分析。
正如我前面提到的,增益和提升图用于评估分类模型。为了举例,让我们创建一个预测模型。在本文中,我将使用来自 Kaggle 的流失数据。
import pandas as pd
churn = pd.read_csv('churn.csv')

作者图片
在这个数据集中,我们有 21 列,目标是客户流失。这意味着我们将开发一个分类预测模型来预测客户流失。为了简单起见,我将清理数据用于建模目的。
#Drop Customer ID
churn = churn.drop('customerID', axis = 1)#Change Ordinal data to numerical
for i in ['Partner', 'Dependents', 'PhoneService', 'OnlineSecurity',
'OnlineBackup', 'DeviceProtection', 'TechSupport', 'StreamingTV', 'StreamingMovies', 'PaperlessBilling', 'Churn']:
churn[i] = churn[i].apply(lambda x: 1 if x == 'Yes' else 0)#OHE categorical data
churn = pd.get_dummies(churn, columns = ['gender', 'MultipleLines', 'InternetService', 'Contract', 'PaymentMethod'], drop_first = True)#Change object data into numerical
churn['TotalCharges'] = churn['TotalCharges'].apply(lambda x: 0 if x == ' ' else float(x))
清理完数据后,我们将尝试开发预测模型。对于本文,我将使用逻辑回归模型。
#Import the model
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression#Splitting the model
X_train, X_test, y_train, y_test = train_test_split(churn.drop('Churn', axis =1), churn['Churn'], test_size = 0.3,stratify = churn['Churn'], random_state = 101)model = LogisticRegression()
model.fit(X_train, y_train)
当我们的模型设定后,我们将开始进行增益和升力分析来评估这个模型。作为比较,我们将使用通常的度量来评估模型。
from sklearn.metrics import classification_report
predictions = model.predict(X_test)
print(classification_report(y_test, predictions))

作者图片
正如我们从上图中看到的,我们的模型预测客户(类别 1)的能力较低。如果我们在业务中应用我们的模型,它还会有好处吗?让我们用增益和升力分析来看看。
增益和升力分析的第一步是根据测试数据得到第一类的模型预测概率,按降序排列。
#Getting the prediction probability of class 1 and order it by descending orderX_test['Prob'] = model.predict_proba(X_test)[:,1]
X_test = X_test.sort_values(by = 'Prob', ascending = False)
X_test['Churn'] = y_test

作者图片
当我们获得概率并按降序排序时,我们会将数据分成十分位数。这类似于我在上面的图片中展示的十分位数分析;我们把数据分成 10 组,贴上标签。
#Divide the data into decile
X_test['Decile'] = pd.qcut(X_test['Prob'], 10, labels=[i for i in range (10, 0, -1)])

作者图片
将数据除以十分位数后,我们需要计算每个十分位数的实际流失率(实际类别 1,非预测值)。这个动议我称之为号响应。
#Calculate the actual churn in each decile
res = pd.crosstab(X_test['Decile'], X_test['Churn'])[1].reset_index().rename(columns = {1: 'Number of Responses'})lg = X_test['Decile'].value_counts(sort = False).reset_index().rename(columns = {'Decile': 'Number of Cases', 'index': 'Decile'})lg = pd.merge(lg, res, on = 'Decile').sort_values(by = 'Decile', ascending = False).reset_index(drop = True)

作者图片
在上图中,我们获得了病例数(十分位数的数据数)和应答数(每个十分位数的实际阳性数据数)。有了这个数字,我们就能够计算出增益数。
供参考。增益是每个十分位数的累计响应数(实际阳性)除以数据中阳性观察总数的比值。让我们试着在我们的数据中计算一下。
#Calculate the cumulative
lg['Cumulative Responses'] = lg['Number of Responses'].cumsum()#Calculate the percentage of positive in each decile compared to the total nu
lg['% of Events'] = np.round(((lg['Number of Responses']/lg['Number of Responses'].sum())*100),2)#Calculate the Gain in each decile
lg['Gain'] = lg['% of Events'].cumsum()

作者图片
从上面的增益图中我们可以看到,每十分位数的增益数都在增加,但随着十分位数的增加,累积总数会减少。增益的解释是什么?增益是在给定的十分位数水平上覆盖的目标(实际阳性)的百分比。例如,在十分位数 2 中,我们的收益为 50.44。这意味着基于该模型的前 20%的数据覆盖了 50.44%的目标。在客户流失模型中,我们可以说,我们可以通过瞄准总客户的 20%来识别和瞄准可能流失的 50%的客户。从业务角度来看,这意味着利用更少的资源,我们可能会避免 50%的客户流失事件。
接下来,我们需要计算升力。lift 将测量与没有模型相比,使用预测模型我们可以预期做得多好。
lg['Decile'] = lg['Decile'].astype('int')
lg['lift'] = np.round((lg['Gain']/(lg['Decile']*10)),2)

作者图片
提升可以被解释为在给定的十分位数水平上的增益比率百分比与随机百分比。用外行的话来说,在十分位数 2 中,我们有 2.52 意味着当基于模型选择 20%的数据时,我们可以找到的目标(实际阳性)是随机选择的 20%的数据(没有模型)的 2.52 倍。
让我们试着想象一下与随机选择相比的收益和提升图。

作者图片
在上图中,我们可以通过测量模型与随机模型相比的增益和升力来评估模型——面积越大,模型的越好。我们可以看到,我们的模型在预测方面很好,因为图表显示,该模型比随机选择允许更大的增益和提升。从商业角度来看,使用该模型可以更好地发现客户流失;意味着花费更少的资源。
结论
模型评估对数据科学家的工作很重要;然而,我们需要根据业务有用性来评估模型。仅仅依靠技术指标是不够的,还要依靠我们的模型如何影响业务。在本文中,我解释了提升和收益分析对于从业务的角度评估模型是有用的,使用了:
- 增益数(每十分位数覆盖的目标百分比)
- 提升数(在给定的十分位数水平上,增益率占随机百分比的百分比)
在我的LinkedIn**或 Twitter 上访问我。**
如果您没有订阅为中等会员,请考虑通过我的推荐订阅。
使用 TensorFlow Lite 进行模型压缩:降低模型大小的一种方法
理解大数据
无缝应用模型压缩必须知道的常见陷阱

为什么模型压缩很重要?
在生产更精确模型的军备竞赛中,一个重要的问题是复杂性,这导致了尺寸的问题。这些模型通常是巨大的和资源密集型的,这导致了更大的空间和时间消耗。(与较小的模型相比,占用更多的内存空间,预测速度较慢)
车型尺寸的问题
当试图在深度学习应用中预测看不见的数据时推动模型精度的极限时,大的模型大小是一个常见的副产品。例如,通过更多的节点,我们可以检测数据集中更细微的特征。然而,对于像在依赖快速预测的嵌入式系统中使用 AI 这样的项目需求,我们受到可用计算资源的限制。此外,主流边缘设备不具备联网功能,因此,我们无法利用云计算。这导致无法使用大规模模型,这将花费太长时间来获得有意义的预测。
因此,在设计我们的模型时,我们需要优化我们的性能。
对理论的直观理解
为了理解机器学习模型的要点而过度简化,神经网络是一组连接节点之间的具有权重(W)的节点。你可以认为这是一组我们优化的指令,用来增加生成我们想要的类的可能性。这组指令越具体,我们的模型规模就越大,这取决于我们的参数(我们的配置变量,如权重)的大小。

人工神经网络(图片由 Govind Bhattacharjee 在科学记者上提供)
让我们来看看我以前的一个项目
我们将在 Raspberry Pi(RPi)中的给定数据集的 3 个不同类之间进行分类。RPi 传统上不是嵌入式设备,然而,在我们的例子中,RPi 是向嵌入式设备迈进了一步。


作者的模型架构大小比较和模型准确性图表
有点难读?下面是一个更简单的视图。
这里我们有所有类别的平均预测准确度。

按作者列出的平均准确性图表
然而,如果我们将的性能与模型尺寸进行比较,似乎平均而言,变压器模型更胜一筹,因为它具有更高的平均精度。我们看到,转换器的总参数计数高于编码器-解码器 LSTM。


按作者分列的平均准确性表格和图表
对于变压器,0.1%的精度边际增加需要增加 1.39Mb 的参数,因此其精度相对于尺寸比较低。
因此,编码器-解码器模型是明显的赢家,然而,3.16MB 仍然是一个太大的模型,不允许嵌入式设备的有效使用。
重访我之前的文章在树莓 Pi3+/4 中安装 TensorFlow 2.3.0,

作者的原始模型基准表
使用我们的 autoencoder,平均需要 1760 毫秒才能在我们的嵌入式设备中进行预测,然而,由于我们的传感器每秒都在检测,这是不可能的。
太好了,现在怎么办?
这些是模型压缩方法,每种方法的实现难度都在增加。这里对这些技术中的每一个都做了一个极好的全面的调查。
- 量化
- 修剪
- 低等级近似
- 知识蒸馏
TensorFlow Lite 助您一臂之力!
TensorFlow Lite 处理前两种方法,在抽象模型压缩的困难部分方面做得很好。
TensorFlow Lite 外壳:
- 训练后量化
—降低浮动 16
—混合量化
—整数量化
2.训练期间量化
3.训练后修剪
4.训练后聚类
最常见和最容易实现的方法是训练后量化。量化的使用限制了我们的模型参数的精度,因此这减少了需要存储的数据量。


图片来自 TensorFlow
我将只讨论训练后混合/动态范围量化,因为这是最容易实现的,对以最小损失减少尺寸有很大影响。
参考我们早期的神经网络图,我们的模型参数(权重)指的是连接每个音符的线,可以被视为代表其文字权重(重要性)或节点的重要性,以预测我们期望的结果。
最初,我们为每个权重赋予了 32 位,称为[tf.float32](https://www.tensorflow.org/api_docs/python/tf#float32) (32 位单精度浮点),为了减小我们模型的大小,我们将根据所使用的量化类型,从 32 位削减到 16 位 ( [tf.float16](https://www.tensorflow.org/api_docs/python/tf#float16))或 8 位 ( [tf.int8](https://www.tensorflow.org/api_docs/python/tf#int8))。
我们可以直观地看到,随着模型越来越大、越来越复杂,节点数量越来越多,随之而来的权重数量也越来越多,这导致了更显著的尺寸减小,尤其是对于全连接神经网络,其每层节点都连接到下一层中的每个节点。
模型压缩的额外好处
- 更小的模型尺寸——模型实际上可以存储到嵌入式设备中(ESP32 有~ 4Mb 的闪存
- 更快的预测率—这加快了可操作性,为实时决策提供了可行性。
- 降低功耗——这是一个经常被忽视的功能,因为训练模型的环境通常具有恒定的电源供应,嵌入式设备通常依靠电池运行,因此这是最重要的功能。
模型压缩的隐藏好处
你可能很快会认为,减少我们为每个权重存储的信息量总是对我们的模型有害,然而,量化促进了泛化,这对防止过度拟合是一个巨大的好处——过度拟合是复杂模型的常见问题。
由梯度推进之父 Jerome Friedman 提出,经验证据表明许多朝着正确方向的小步骤会导致用测试数据做出更好的预测。通过量化,由于权重灵敏度的降低,有可能获得提高的精度。
想象一下,如果在我们的数据集中,我们很幸运,每次我们试图检测一个 cookie,我们的数据集都显示一个巧克力片 cookie,我们的 cookie 检测将获得很高的训练准确性,但是,如果在现实生活中我们只有葡萄干 cookie,它将具有很低的测试准确性。一般化就像模糊我们的巧克力片,这样我们的模型意识到只要有这个斑点,它就是一块饼干。
对于诸如修剪之类的其他压缩方法也是如此。正是在这种情况下,退出也可以提高看不见的准确性,因为在训练期间随机删除节点也有助于推广。
我们来试试吧!
我将使用 Tensorflow 2.3.0 。
要将训练后的模型转换为量化模型:
首先,将你的 tf.keras 模型保存为推荐的 SavedModel 格式 ( [tf.lite.TFLiteConverter.from_keras_model()](https://www.tensorflow.org/lite/api_docs/python/tf/lite/TFLiteConverter#from_keras_model) 也可以,但是我遇到了很多错误,要注意):
import tensorflow as tf
tf.keras.models.save_model(model, str(saved_model_dir))
其次,确保 TensorFlow Lite 支持操作(ops),在这里查看支持哪些操作。
截至 06/09/20 ,TensorFlow(TF)不支持 SELU 的激活功能,我费了好大劲才发现,根据我的经验, RELU 是一个很好的替代品,损失最小。
由于 TensorFlow Lite 内置运算符库仅支持有限数量的 TensorFlow 运算符,因此并非每个模型都是可转换的。
如果您的模型需要不受支持的 TensorFlow 运算符,则不会全部丢失,应尝试使用[converter.allow_custom_ops = True](https://www.tensorflow.org/lite/api_docs/python/tf/lite/TFLiteConverter#from_keras_model) & converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS]#, tf.lite.OpsSet.SELECT_TF_OPS,以允许自定义实现。
ALLOW_CUSTOM_OPS —允许自定义实现不支持的操作符。
TF Lite _ BUILTINS-使用 TensorFlow Lite 内置运算符转换模型。
SELECT _ TF _ OPS-使用张量流运算符转换模型。
我有一个带 2 个 LSTMs 的自动编码器模型,使用[allow_custom_ops = True](https://www.tensorflow.org/lite/api_docs/python/tf/lite/TFLiteConverter#from_keras_model) & tf.lite.OpsSet.TFLITE_BUILTINS,没有我自己的定制实现。这应该会跳过任何不支持的运算符。
第三个,我们将转换文件。
import pathlibconverter=tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
tflite_models_dir = pathlib.Path.cwd() / "tflite_model_dir"
tflite_models_dir.mkdir(exist_ok=True, parents=True)
converter.allow_custom_ops = True
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops=[tf.lite.OpsSet.TFLITE_BUILTINS]
#, tf.lite.OpsSet.SELECT_TF_OPS
tflite_model_quant = converter.convert()
注意:pathlib 是一个可选库,用于简化 windows 路径。
最后,代替使用我们的量化模型预测,我们将运行一个推理。
术语“推断”是指在设备上执行 TensorFlow Lite 模型,以便根据输入数据进行预测的过程。
要使用 TensorFlow Lite 模型执行推理,我们必须通过一个解释器来运行它。TensorFlow Lite 解释器的设计简洁而快速。解释器使用静态图排序和定制的(动态性较低的)内存分配器来确保最小的负载、初始化和执行延迟。
# Load the TFLite model and allocate tensors.
interpreter = tf.lite.Interpreter(model_path=path_to_tflite_model) interpreter.allocate_tensors()# Get input and output tensors.
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()#Predict model with processed data
input_shape = input_details[0]['shape']
input_data = np.array(x_3d_pca_processed, dtype=np.float32)
#x_3d_pca_processed is the input, change to your input
interpreter.set_tensor(input_details[0]['index'], input_data) interpreter.invoke()
prediction = interpreter.get_tensor(output_details[0]['index']) outcome = np.argmax(prediction.mean(axis=1),axis=1)
#outcome is my own implementation to get the class with the highest probability, remove if necessary
就是这样!
有关这些 TFlite 工具的更深入的解释,请单击此处的。
有关 TFlite 工具的更多信息,请点击此处。
结果分析
以我们的原始模型为基线,在我们的“嵌入式”设备中实现我们的模型之前,我们在我们的计算机上比较了:
-原始模型
-后量化模型
-后修剪模型
-后量化和后修剪模型
。


不同模型压缩工具的结果(表格和图表)
效果出奇的好!我们有一个明显的赢家,量化后的精度损失可以忽略不计,大小减少了 81%,延迟减少了 91%。
现在,我们将着手将 TFlite 模型集成到 RPi 中。我们的 RPi 将安装tensor flow 2 . 3 . 0,它必须运行在 Debian buster 上,更多细节在我之前的文章中。一旦您将 TFLite 文件拖到我们的 RPi 中,就运行推理。

作者的 TFLite 模型基准表比较
在我们的 RPi 中,0.60Mb TFlite 模型的预测速度约为 90 毫秒,而 3.16Mb 原始模型的预测速度为 1760 毫秒!尽管有我的“定制实现”, TFlite 能够带来如此大的性能提升,RPI 能够如此好地处理 TF 模型,这让我大吃一惊。
这与 TF 的估计相比如何?

图片来自 TensorFlow
如上所述,TensorFlow 的估计与我们的发现相符,与 Resnet V2 相似,我们经历了 0%的精度损失,甚至获得了 5.2 倍(3.16mb 至 0.60mb)的较小模型,超出了预期。
一定要考虑到,在扩展到模型压缩的深度学习中,没有任何问题的硬性解决方案。事实是,很难说我们会节省多少,我们能做的最好的事情是自己尝试,并分析模型大小是否有所改善,而准确性几乎没有损失。
也就是说,我认为尝试后量化是迈向模型压缩的第一步,因为它易于实现,显著减少,损失可以忽略不计。只有当其他方法都不起作用时,您才应该尝试更复杂的模型压缩方法。
TensorFlowLite 的利与弊
优点:
-最简单也是唯一的工具( 06/09/20) 实现模型压缩
-对准确性影响最小(取决于模型)
-预测速度大幅提升
缺点:
-需要最新的 Tensorflow 版本 2
-相对较新( 06/09/20),许多操作(ops)尚未得到支持,如 SELU
-需要转换可能会失败的模型
-与我们的好朋友.predict()相比,运行推理时可能会出现复杂情况,因为它更复杂。
结论
尽管有缺点,TensorFlow Lite 仍然是一个强大的工具,具有超出我预期的巨大潜力。我预见在不久的将来,随着嵌入式设备对人工智能的需求不可避免地增长,模型压缩将得到更广泛的应用,这给了 TFLite 提供更大操作覆盖范围的理由。TensorFlow Lite 的缺点可以通过定制实现来减轻,因此值得一试。
也就是说,模型压缩不应该被视为一种简单的方法,相反,它应该在我们试图将性能优化到模型大小,但无法在不显著降低精度的情况下减小模型大小之后使用。探索不同的可能模型并找到更好的模型架构通常是更好的解决方案,在选择我们的 Autoencoder 之前,我探索了 5 种不同的模型架构。找一个储物容器,而不是试图把冰箱塞进行李箱。
支持我的工作:
https://cawin-chan.medium.com/membership
参考资料:
https://www.tensorflow.org/api_docs/python/tf/dtypes/DType
https://www . tensor flow . org/API _ docs/python/TF/keras/layers/layers
https://www.tensorflow.org/lite/guide/ops_compatibility
https://www.tensorflow.org/lite/convert
https://www.tensorflow.org/lite/guide/inference
https://www . fatal errors . org/a/tensor flow-2.0-keras-conversion-TF lite . html
[https://www.youtube.com/watch?v=3JWRVx1OKQQ【T2](https://www.youtube.com/watch?v=3JWRVx1OKQQ&ab_channel=TensorFlow)
部署失败:成功的故事
我如何使用 Dash、Plotly 和 Heroku 部署我的第一个萨里玛新冠肺炎预测模型。

我没有使用 statsmodels 库部署了一个预测未来新冠肺炎感染率和死亡率的 SARIMA 时间序列模型。使用 Plotly 创建当前和预测的病例和死亡率的交互式图表,允许用户决定包括哪些统计数据,预测哪些国家或州,以及预测多远,我没有制作一个公众可访问的交互式预测网站。我努力工作,并在没有将这个模型部署到 Heroku 服务器上的过程中学到了很多。
下面是我的故事。
在本演练中,您将学习向 Heroku 部署一个不进行机器学习预测的网站。您将学习如何开始:
- 一个用于 Python 的交互式图形库
- Dash 开源,一个 web 部署库,它可以很好地将您的自定义 Plotly 图形放到 web 上,供您的用户进行交互。
- Heroku 免费版,一个有好处…也有限制的虚拟主机服务。
- 不太了解时间序列建模。我把这个留到以后再讲。
背景
我是一名初露头角的数据科学家,已经学到了很多关于数据和建模的知识,但是我想用一种方式向世界展示我能做什么,也许还能制造一些对其他人有用的工具。 Jupyter 笔记本非常适合与其他数据科学家交流,并展示您的发现的有效性和可重复性。但是,它们并不是与所有人分享你的作品的合适工具。我们必须部署!
从 Flatiron 数据科学集训营毕业后,我被邀请加入一个团队,参加 X-Prize 新冠肺炎疫情反应竞赛。稍后我会写下这段旅程的故事,但简短的是,我知道一些关于时间序列预测的基础知识,并决定加入团队以了解更多。我最成功的模型是一个 SARIMA 模型,它可以以 99%的准确率,只访问到 11 月的数据,预测美国 12 月份感染率的变化(我的验证数据)。
我还试用了脸书先知时间序列预测库。虽然它没有给我最好的结果,但我确实了解了他们的库和 Plotly 之间的集成。使用 Plotly,我能够制作我的第一个交互式图表来探索预测和趋势。
我花时间学习了更多关于 Plotly 的知识,并继续使用这个有趣而简单的库来学习如何让交互式的情节变得精彩。我熟悉 matplotlib,Plotly 有很多相同的功能,还有交互和动画选项。新的数据科学超能力!
当我使用 Plotly 的文档不断学习新技能时,该网站不断向我推送他们的 Dash Enterprise deployment 库。他们希望我为我的企业购买软件包和综合开发环境。嗯,我还没有一个企业,只有一种进取精神。我知道我想学习如何部署我的模型,使用 Dash 的免费版本部署一个新冠肺炎预测器似乎是一个不错的起点。我最终没有部署我的模型,但不是因为 Dash。机器学习 web 部署我绝对推荐 Dash!
我以前的编码经验主要集中在 Jupyter 笔记本和支持它们的定制库上。Dash 是为部署到 web 服务器而设计的,并且使用了一些对我来说是新的工具,比如函数包装器和回调。
我知道许多新数据科学家很想了解如何部署他们的工作,所以让我告诉你什么没有……什么对我有用!
阴谋地
Plotly 是一个数据可视化库,让您可以轻松地制作交互式图表,以鼓励您的观众探索数据。让一个基本的情节继续下去并不需要太多。请看下面,了解如何下载 COVID 数据,并在交互式绘图中按国家和地区绘制数据:
由作者创建
由作者创建
如果您想查看其他区域的图,请随意更改country和region变量。但是,请注意,只有美国、加拿大、巴西和英国有按地区划分的数据。
此外,如果您运行上面的代码,花一点时间与图形进行交互。您可以在感兴趣的区域上画一个框来放大,然后双击来缩小。你会注意到死亡和病例的比例非常不同,要查看累计死亡的详细信息,你必须放大很多倍!
萨里玛
时间序列预测有细微差别,做出好的预测超出了本文的范围。简而言之,您必须考虑数据中的许多方面和模式,以允许模型使用过去的值来预测未来的值。模型对数据的分布方式很挑剔,为了得到好的预测,通常需要一些转换。如果你想复制我的模型,代码如下。我不会真的解释它,也不会解释我是如何得到这个特殊的超参数的。请关注我关于时间序列预测的下一篇博客,以了解更多的事实。
以下是我的预测模型的代码:
由作者创建
由作者创建
破折号
这是在线部署您的模型和分析的关键要素。Dash 在后端使用 Flask 服务器,使部署变得简单直观。我用你在上面的列表中看到的功能创建了自定义模块,以集成到 Dash 应用程序布局中。Dash 的工作原理是创建一个Dash()应用程序对象,该对象包含生成 HTML 布局的方法、自定义函数与布局交互的回调装饰器,以及启动 Flask server 的.run_server()方法。在终端中运行代码会创建一个本地服务器,您可以使用浏览器连接到该服务器来预览您的新站点。一旦你理解了装饰者是如何与布局互动的,这真的很容易。
app = dash.Dash(__name__)
样式表
当Dash() app 对象被实例化时,可以给它一个样式表模板来开始。
app = dash.Dash(__name__, external_stylesheets=URL)
布局
app.layout是 app 控制网站布局及其组件的属性。布局控制你的访问者看到什么,它充满了被称为组件的东西。组件可以是文本图像、图形、交互式对象、输入等等。如果你熟悉 HTML,这将对你有所帮助,因为语法是熟悉的。每个组件都有一个id,回调使用它来帮助你的函数与它交互。
app.layout = html.Div([html.H2('Sample'), html.Div(id='first-division'), dcc.Dropdown(id='dropdown-1')]等。
一件可能不明显的事情是,html.Div对象具有children属性,这些属性是新组件,出现在所显示的 web 页面中的布局分区下。这就是对返回一个dcc.Graph()组件的display_value()的回调如何在html.Div(id='graph')组件下创建一个新图形。
装饰者和复试者
函数装饰器对我来说是新的,一开始我不明白它们是如何工作的,但我想分享一下我所学到的东西:
函数装饰者改变直接在他们下面的函数(没有空格!).
当用户与部署的站点交互时,Dash 使用@app.callback()装饰器在布局和函数之间移动值。它们是您的代码与站点访问者看到的和做的事情进行交互的一种方式。
@app.callback([<dependencies>..])
def function()
属国
当您阅读下面的代码时,请注意回调函数中的dash.dependencies.Input()和dash.dependencies.Output()对象如何通过第一个参数中的id和第二个参数中的组件的一部分来引用布局中的组件。正如您可能已经猜到的,Input()对象从组件中获取值,Output对象改变组件的一部分。
@app.callback(Output('first-division','children'),[Input('dropdown-1','value')])
def function(dropdown_input):
来自Input()对象的值按照它们在回调中出现的顺序被提供给包装器下面的函数,并成为该函数的参数。这是关键。在函数定义中调用什么变量并不重要,它们从Input()对象中接收值,顺序与回调方法中出现的顺序相同。
指导和清晰度
这是一个关于元素间稍微复杂的交互的简短指南。如需了解更多信息,或者如果我让您感到困惑,请访问 Dash 开源文档以了解详情。您也可以看看下面的代码,看看它们是如何组合在一起的。
警告!
虽然到目前为止,我在本文中给出的代码可以在 Colab 上运行,非常好的沙箱化,但下面的代码实际上不会让您在 Colab 上获得一个工作服务器,您必须在本地运行它。在你的电脑上运行别人的程序时,一定要非常小心。下面这个是安全的,其他的可能就不安全了。如果您有任何疑问,请仔细检查代码,研究导入的库,和/或咨询您的编码专家朋友。
您可以将代码作为笔记本来运行,或者将其复制到文本文件中并重命名为app.py以通过您的终端来运行。请确保您也获得了下面的定制 src 模块的源代码。
由作者创建
这里也是 SRC。你需要两者来让你的服务器工作。将下面的代码复制到一个文本文件中,并将该文件命名为src.py。然后把它放到上面 Dash app 文件所在的文件夹里。
好吧!我知道你要说什么。你已经注意到了。这里没有预测器。如果运行此代码,您将设置一个服务器和一个站点,您可以通过浏览器本地连接到该服务器和站点。但是,该网站只允许您探索当前的 COVID 数据图表,而不是像 SARIMA 代码那样的预测案例。我们已经到了我的“失败”了。
赫罗库
Heroku 提供一个免费的网站托管服务,让你连接一个正确设置的 git 库来创建你的网站。在您的 repo 中添加一些额外的文件(如下所示),上面要点中的代码,当部署在 Heroku 上时,将会很好地向世界展示您的新冠肺炎追踪器。不过,你需要在 Heroku 上开一个账户。
要部署上面的文件,你需要将它复制到一个文本文件中,并将文件重命名为app.py。然后,您将需要本演练末尾链接的 git repo 中的一些可用文件,requirements.txt、runtime.txt 和 Procfile。requirements.txt 告诉 Heroku 它需要安装什么包,runtime.txt 告诉它你的代码用什么编程语言,Procfile 告诉它如何开始部署你的代码。我不会说得更详细,您可以查看本文末尾的回购协议,了解这些文件需要包含什么。
失败!
我想告诉你为什么我不能把我的预测模型部署到 Heroku。Heroku 提供这种免费服务,这很棒,但免费服务器不是最快的,而且 SARIMA 模型需要一些马力来训练。在我写这篇文章的时候,我负担不起完整版的费用。
你可能会认为这将导致永远无法返回图表,但 Heroku 对后端应用程序响应用户请求的时间有 30 秒的硬性限制。免费提供的服务器无法在这段时间内训练模型并做出预测。因此,我们仅限于可爱的交互式 Plotly 图形和用户探索不同国家和地区的当前和历史数据的能力。不过,这还是很酷。
未来的成功
我的下一个学习目标是掌握亚马逊网络服务。AWS 目前为大量互联网提供动力,并拥有针对数据分析和机器学习进行优化的强大服务器。我怀疑通过他们的服务,我未来的 ML 部署可能会成功。然而……他们的免费服务是有限的,意外费用也不难产生。我将努力获得 AWS ML 认证,我会回来与大家分享我在未来学到的东西。
其他人找到的其他可能的解决方案是让服务器递增地返回信息。我可以为我的应用程序寻找一种方法来返回一些东西,比如“仍在训练”的消息或其他东西,以延长超时前的时间。这是由阿提什·纽帕内提出的,尽管他们指出这只能将超时延长到 55 秒。
另一个解决方案是添加更多的 dyno(Heroku 中的后台处理器)。免费帐号给你 2 个 dynos 在你部署的站点之间分配,所以这是一个很大的要求,基本上使这成为我唯一可以部署的站点。
最后,有人建议我可以根据每天的新数据对模型进行预训练。
这是我在新冠肺炎部署的数据站点的链接。它一开始加载很慢,因为每次加载应用程序都会下载所有最新的新冠肺炎数据。不过,一旦加载,它的反应会很快。Plotly 针对 Dash 和 Pandas 进行了优化,Pandas 针对数据进行了优化。有了这些很棒的库,网站可以很好很快地显示数据。
这里是完整回购的链接。请克隆好好玩!
让我知道什么适合你!
同时,享受代码。请以此为起点,推出自己的想法。要有创意,打破它,改变它,增加它,并在评论中发表你的见解,奋斗和成功。
真诚地,带着爱,
乔希·约翰逊
感谢:
特邀明星嘉宾:
蕾拉·龙威:我在这个项目中的合作伙伴,尤其是在网络部署方面。
大量数据:
牛津大学和布拉瓦尼克政府学校冠状病毒政府响应跟踪。
温和的 SARIMA 模型指导:
杰森·布朗利的用 Python 温和地介绍了用于时间序列预测的萨里玛
使用 Flask 的模型部署
制作可消耗的模型

来源:名词项目
介绍
无论你做了多么棒的模型,坐在 jupyter 笔记本上也不会有任何价值。
我们通常会在 EDA 模型开发部分投入很多精力,但是我们往往会忽略一个更重要的方面,即创建端到端的应用程序或部署模型,在您为开发模型付出了大量努力之后,以最终用户可以直接使用的可用形式提供模型也同样重要。此外,围绕您开发的代码构建应用程序有助于您使您的工作更有吸引力,并有助于您更好地展示您的工作。
瓶
Flask 是一个用 python 编写的 web 应用框架,简单来说,它帮助最终用户直接从他们的 web 浏览器与你的 python 代码(在这种情况下是我们的 ML 模型)交互,而不需要任何库、代码文件等。
Flask 使你能够非常容易地创建 web 应用程序,从而使你能够将精力更多地集中在 ML 生命周期的其他重要部分,如 EDA、特征工程等。在这篇博客中,我将向你展示如何用你的 ML 模型构建一个简单的 web 应用程序,并最终部署它。
目录结构
首先让我们看一下我们的目录结构,这将给我们一个整体项目的更广阔的画面,并且当你使用 flask 时知道它也是有用的,我已经将项目保存在一个名为 ML_Model_deployment 的主目录下

来源:作者图片
- 模板 :-这个文件夹包含 html 文件 (index.html,predict.html),我们的主文件 (app.py) 将使用这些文件来生成我们的应用程序的前端
- app.py :-这是主应用程序文件,我们所有的代码都驻留在这里,它将所有的东西绑定在一起。
- requirements.txt
- model.pkl :- 这是我们将使用的分类模型,在这种情况下,它是一个逻辑回归模型,我已经训练过了。
- vectorizer.pkl :- 这是一个矢量器文件,用于将文本转换成模型要处理的矢量,在本例中,我们使用了 tf-idf 矢量器
- Procfile :- 这是一个特殊的文件,当我们在公共服务器(heroku)上部署应用程序时,将需要这个文件
理解代码
我在 Kaggle 上举办了一个名为 Quora 不真诚问题分类的比赛,基本上这是一个分类问题,Quora 试图识别不真诚的问题,即那些建立在错误前提上的问题,或者那些打算发表声明而不是寻找有用答案的问题,你可以在[这里](http://those founded upon false premises, or that intend to make a statement rather than look for helpful answers)阅读更多关于问题声明的内容。
既然我们已经完成了所有的介绍部分,让我们来谈谈代码吧!
**from** flask **import** Flask
app = Flask(__name__)
就像任何其他 python 类对象一样,最终这个应用程序只不过是 Flask 类的一个对象,它将为我们完成所有繁重的工作,比如处理来自浏览器的传入请求(在我们的例子中是用户输入的问题),并使用 html、css 以某种良好的形式提供适当的响应(在我们的例子中是模型预测)。我们将逐渐看到所有这些事情,以及这个应用程序对象如何适合解决所有这些问题。
下一部分可能看起来有点吓人,但是不要担心,我们会试着一点一点地理解它。
[**@app**](http://twitter.com/app)**.route('/')** def index():
return flask.render_template('index.html')[**@app**](http://twitter.com/app)**.route('/predict', methods=['POST'])
def** predict():
to_predict_list = request.form.to_dict()
review_text = pre_processing(to_predict_list['review_text'])
prob = clf.predict_proba(count_vect.transform([review_text]))
if prob[0][0]>=0.5:
prediction = "Positive"
else:
prediction = "Negative"**return** flask.render_template('predict.html', prediction = prediction, prob =np.round(prob[0][0],3)*100)
第一行 @app.route ('/index') 是一个 decorator,简单地说,它只是将它下面定义的方法映射到 decorator 中提到的 URL,也就是说,每当用户访问那个 URL '/' (完整的地址也将有一个 ip 地址和一个端口号,类似 http://127.0.0.1:5000/) , index() 方法将被自动调用,而 index()将被调用
flask . render _ template()在我们在主目录中创建的 templates 文件夹中查找这个index.html文件,并为最终用户动态生成/呈现一个 HTML 页面,我稍后将解释动态部分
(注意:因为 predict 一词在多个上下文中使用,所以为了避免混淆,我将把 predict()称为我们代码中定义的预测方法,把 predict.html 称为存储在模板文件夹中的预测模板,把/predict 称为预测 URL )
现在我们有了另一个装饰器 @app.route ('/predict') ,它将 predict() 方法映射到/predictURL,顾名思义,这个 predict()方法接受用户给定的输入,进行所有的预处理,生成最终的特征向量,对其运行模型并获得最终的预测。现在让我们把重点放在呈现动态 HTML 页面部分,有时我们需要在 HTML 页面中动态地放置信息,就像在这种情况下,我们的predict.html页面将需要预测值来正确地呈现页面,只有当用户在我们的index.html页面中输入文本并点击提交按钮时才可用,这就是 render_template 出现的地方。 在 predict.html 页面中,我们已经为这些变量值创建了占位符(稍后将讨论这些占位符),这个 render_template 将模板文件夹中的 predict.html 和预测值放在一起,最终生成/呈现最终的 HTML 页面,在这种情况下,HTML 页面包含预测,对吗?**
现在让我们快速地看一下主代码中的 predict() 方法,方法中的第一行,即to _ predict _ list = request . form . to _ dict(),它接受用户输入的文本,当单击表单中的提交按钮时,返回一个 json,其中包含具有响应的键值对, 当点击 submit 按钮时,这个 /predcit URL 被调用,响应作为 json 被发送到对应于这个 URL 的页面,我们在前面也看到过,一旦这个/predict URL 被调用,相关的 predict()方法被自动调用,它将这个 json 和输出模板(predict.html)放在一起生成我们的最终输出。 (我们在最后有一个输出部分,我们将实际查看这里讨论的所有内容的结果)
现在,在进一步研究代码之前,让我们看看我们的index.html,它负责从用户那里获取输入文本,并通过调用 /predict URL 最终显示最终输出
**<form action="/predict" method="POST" target="_blank">
<textarea name="review_text" placeholder="Enter your question here..." rows="10" cols="15"></textarea>
<input type="submit" value="Submit">
</form>**
这里是 index.html 的一小部分,在这里表单被创建,我们可以看到一个叫做动作的字段,这意味着表单数据被发送到动作属性中指定的页面,而方法属性告诉我们这个表单数据是如何被共享的, 这里我们使用了 POST 方法,这意味着表单数据被共享为一个 json(以键-值对的形式),同时让我们也看看我们的第二个模板文件,即predict.html模板,基本上它接受预测输出并根据输出值显示结果,因此我们在 predict.html 模板中需要两个主要的东西来实现这一点
- 用于预测输出的变量/占位符****
- 根据预测值显示结果的基本 if/else 逻辑
下面是来自 predict.html 页面的一小段代码,它根据预测结果打印了一条消息和一幅图像
******{% if prediction == "Negative" %}**
<h1> THIS IS NOT A GENUINE QUESTION </h1>
<img src="[https://i.pinimg.com/564x/b0/41/75/b04175fd1ffbefdc02d90136c262a6e3.jpg](https://i.pinimg.com/564x/b0/41/75/b04175fd1ffbefdc02d90136c262a6e3.jpg)" width=250>
<progress value= **{{prob}}** max="100">****
在 html 页面中编写代码/变量非常简单,flask 使用一个名为 Jinja2 的 web 模板引擎,帮助你将代码/变量嵌入到 html 中
- {{}} 用于添加变量/占位符(这个 prob 变量已经由 predict()方法中的 render_template 传递)
- { % %} 写 if-else 条件/for 循环等语句
现在,回到 predict()方法中的原始代码,一旦我们从表单响应中获得输入文本,我们就将该文本传递给我们的预处理函数,在那里我们执行一些基本的 NLP 预处理,如转换为小写、一些标记化、单词词汇化。下面是预处理函数的定义
******def** pre_processing(text):
lemmatizer = WordNetLemmatizer()
text = text.lower()
text = re.sub('[0-9]+','num',text)
word_list = nltk.word_tokenize(text)
word_list = [lemmatizer.lemmatize(item) for item in word_list]
return ' '.join(word_list)****
在预处理之后,我们调用我们的矢量器和模型,我们已经在训练阶段定义并保存了它们,我们的 count _ vectorizer 将文本转换为数字矢量,模型给出预测概率,稍后这些值被提供给 render_template 以生成包含输出的整个 html 页面。
部署和输出
现在让我们看看最终的结果,一旦我们部署它,一切看起来如何,首先我将它部署在我的本地机器上,然后我们将它部署在公共服务器上。部署非常简单,您只需设置您的 python 环境并运行 app.py 应用程序,因为我已经在我的计算机上安装了 anaconda,我将从那里运行 app.py 文件,否则您可以创建您单独的虚拟环境,安装所有依赖项并从那里运行它,您不需要整个 Anaconda 包来运行它,您只需要 python 和代码中使用的库。

来源:作者图片
打开 anaconda 提示符后,只需转到主文件夹(本例中为 ML_Model_deployement ),运行 maint python 代码文件(app.py ),只需查看最后一行,上面写着 running onhttp://127 . 0 . 0 . 1:5000/这是我们的 web 应用程序正在运行的地方,您可以复制这个 URL 并将其粘贴到您的 web 浏览器中,然后查看 web 应用程序。 这里 127.0.0.1 是本地主机的 ip 地址(就像我们有一个住宅地址一样,ip 地址是在互联网或本地网络上识别你的机器的唯一地址),5000 是端口号,这是你的 web 应用程序驻留在服务器中的位置(把它看作是访问你的应用程序的入口)。 现在让我们在浏览器上复制这个 URL,看看主页是什么样子的(请原谅我的前端技能,我在这方面的经验非常有限)

来源:作者图片
现在让我们键入两个类中的一些问题,看看我们的预测页面看起来如何,首先让我们看一个真正的问题。

来源:作者视频
现在我们将键入一个不真诚/不真诚的问题,看看结果如何

来源:作者视频
就像我们部署在本地机器上一样,我们可以将它部署在公共 web 服务器上,让 web 上的每个人都可以访问我们的应用程序。这里我使用了 heroku 来部署我的应用程序。
Heroku 是 PAAS(平台即服务应用)它使开发者能够在网络上部署他们的应用程序,并且它确实提供了一个免费的计划!,在 heroku 部署很简单,你可以直接把你的 github repository 链接到 heroku(最后我会把 github repo 和 web 应用的链接都分享出来)。你只需要添加两个特殊的文件 requirements.txt 和 Procfile(没有任何扩展名),我们在讨论目录结构的时候已经讨论过需求文件,现在让我们再来讨论一下这个 Procfile。基本上,Procfile 告诉 heroku 需要预先运行的一组进程/命令,Procfile 看起来像这样
****web: gunicorn app:app****
web 命令告诉 heroku 使用 gunicorn(帮助您运行 python 应用程序的 web 服务器)启动 web 服务器,第一个应用程序引用我们的主 app.py 文件,第二个引用 flask 实例应用程序,它也可以有不同的名称,如果您对部署有任何疑问,您可以参考这个令人惊叹的视频教程,它包含在 hero 中部署的完整分步教程
结论
如果你觉得这个教程很有帮助,那么请与你的朋友分享,下面是 github 资源库和部署在 heroku 的 web 应用程序的链接。为了感受它,我强烈建议你自己尝试一次,如果你过去已经从事过一些 ML 项目,那么试着部署那些模型,这样你会有更好的理解。
****Github 资源库:-【https://github.com/ravi207/Model_Deployment ****
网络应用:-https://question-classification . heroku app . com/
(注意:我已经使用 tf-idf 矢量器在整个数据集的一个小子集上训练了一个非常基本的逻辑回归模型,因此模型预测不会太好,现在它能够识别特定类型的不真诚问题,这些问题具有它在训练期间从数据集学习的一些特定的仇恨词汇)
如果您有任何建议或疑问,请随时联系我
领英:https://www.linkedin.com/in/ravindra-sharma-832a04156
快乐学习!
机器学习中的模型漂移
理解和处理模型漂移

图片由 Aral Tasher 在 Unsplash 上拍摄
所有的事情都趋向于无序。热力学第二定律指出“随着时间的推移,任何孤立或封闭系统的净熵(无序度)将总是增加(或至少保持不变)”。因此,没有什么是永恒的。我们的青春不会永远,最好的会变成最差的,我们的机器学习模型会随着时间的推移而退化。
世界不是静止的,而是动态的,不断变化的。2000 年代的垃圾邮件和 2021 年的垃圾邮件不一样。2021 年用于检测欺诈性电子邮件的特征将与 2000 年的显著不同——人们变得更加聪明,包括骗子。如果我们试图使用 2000 年开发的模型来分类 2021 年的电子邮件是否是欺诈性的,我们可以预期看到该模型的预测能力比 2000 年的欺诈性电子邮件更差。这个范例描述了一个被称为模型漂移的概念。
模型漂移是由于环境变化导致的模型预测能力的衰减。如果事情保持不变,即环境和数据,我们应该期待我们的机器学习模型的预测能力保持不变。然而,我们都知道现实世界是不断变化的。真实世界环境的变化会影响变量之间的关系,从而使模型做出的预测变得更糟。例如,垃圾邮件呈现方式的变化将影响我们的机器学习模型检测垃圾邮件的能力。
本质上,我们应该预计机器学习模型将失去预测能力,因为随着时间的推移,事情会发生变化。如果我们没有及时发现漂移模型,其影响会对我们的管道、服务和/或整体业务造成严重损害。但是我们怎么知道一个模型什么时候在漂移呢?它从理解模型可能漂移的方式开始。
模型漂移的不同类型
为了知道如何处理模型漂移,我们必须首先了解我们正在处理的模型漂移的类型以及导致它的原因。
概念漂移
当我们在数据上训练机器学习模型时,该模型学习将特征映射到目标变量的函数。如前所述,如果所有的事情都是静态的,没有任何东西随着时间的推移而演变,那么我们会期望从特性到目标的关系是正确的,因此模型应该一如既往地执行。
然而,现实是事情确实在变。目标变量的统计属性发生了变化,意义也发生了变化。因此,由机器学习模型学习的映射不再适合新环境,即垃圾邮件发送者的概念已经随着时间的推移而演变。
数据漂移
随着概念漂移,垃圾邮件发送者的定义发生了变化——目标变量的统计属性以不可预见的方式发生了变化。相反,数据漂移指的是特征属性的变化。特征的潜在变化导致模型失败。
数据可能会因为以下原因而漂移:
- 季节性 —每个日历年重复出现的有规律且可预测的数据变化
- 消费者偏好— 我们的偏好会随着时间而变化。我们今天喜欢的不一定是明天喜欢的。
- 等等。
检测模型漂移
概念和数据漂移都是对数据统计变化的反应。因此,使用技术来监控统计属性、模型的预测以及它们与其他因素的相关性,似乎并不是一个坏主意。
例如,我们可以监控 F1 分数——取决于问题——以确定我们的模型是否仍然如它应该的那样运行。一旦模型下降到指定的 F1 阈值以下,我们可以推断我们的模型正在漂移。或者我们可以监视预测的输出以及其他特征。例如,如果我们看到欺诈性交易数量的增加或减少速度与活跃用户的增加或减少速度相差很大,那么我们可以推断可能发生了某种漂移。
克服模型漂移
检测模型漂移相当于承认你有问题。一旦你意识到存在问题,下一步就是找出解决问题的方法,并制定一个计划。在模型漂移的情况下,有两种流行的解决模型漂移问题的方案。
第一种方法包括定期重新训练你的机器学习模型。这个方法挺有效的。如果我们知道模型每 3 个月降级一次,那么每 2 个月重新训练一次模型是一个好主意,以确保模型的性能不会低于预定的阈值。
另一个解决模型漂移的方法是在线学习。在线学习是一种机器学习的方法,其中模型实时学习。其工作方式是,数据以连续的方式变得可用,并用于更新最佳模型,该模型在每一步都用于未来数据。
免责声明:执行在线学习是非常棘手的,但是如果做得正确,可以达到的结果是惊人的。
最后的想法
随着时间的推移,大多数东西都会变质;我们的骨骼变弱,香蕉变坏,机器学习模型失去预测能力。这是一个称为模型漂移的概念。两种类型的模型漂移都涉及数据中的统计变化,但是检测模型漂移仍然是一项困难的任务。多年来,许多初创公司涌现出来解决这个问题,世界各地的团队也想出了许多方法来检测和克服这个问题。
感谢阅读!
如果你喜欢这篇文章,请通过订阅我的免费 每周简讯与我联系。不要错过我写的关于人工智能、数据科学和自由职业的帖子。
相关文章
💔-mistakes-that-transformed-my-machine-learning-career-de76679e0084> </5-beginner-friendly-machine-learning-projects-3475ad85167a>
模型过载—我应该选择哪个 NLP 模型?
如何用 Sagemaker 实验评估和比较 Huggingface NLP 模型

杰森·登特在 Unsplash 上拍摄的照片
这是怎么回事?
当我写这篇文章时,Huggingface 上的模型库由 11256 个模型组成,当你读到这篇文章时,这个数字只会增加。有这么多模型可供选择,难怪许多人不知所措,不知道该选择哪个模型来完成 NLP 任务。
如果有一种便捷的方式来尝试相同任务的不同模型,并在各种指标上相互比较这些模型,那就太好了。Sagemaker Experiments 正是这么做的:它让你非常容易地组织、跟踪、比较和评估 NLP 模型。在本文中,我们将两个 NLP 模型进行对比,并比较它们的性能。
所有代码都可以在这个 Github 库中找到。
数据准备
这个项目文章的数据准备可以在这个 Python 脚本中找到。我们将使用来自 Huggingface 的 IMDB 数据集,这是一个用于二元情感分类的数据集。数据准备是相当标准的,唯一需要注意的是,我们需要分别对每个模型的数据进行标记。然后,我们将数据存储在 S3 文件夹中,每个模型一个。
我们在本文中比较的型号将是distilbert-base-uncase和distilbert-base。显然,Sagemaker 实验并不局限于两个模型,实际上允许跟踪和比较几个 NLP 模型。
度量定义
首先,重要的是要理解 Sagemaker 实验将如何度量我们随后用来比较模型的指标。这些指标的值是从模型训练期间生成的日志中收集的。这通常意味着训练脚本必须明确写出这些指标。
在我们的例子中,我们将使用 Huggingface 的 Trainer 对象,它将负责为我们将指标写入日志。我们所要做的就是在培训脚本中定义指标。然后,培训师对象会自动将它们写到培训日志中(注意,损失指标是默认写出的,所有指标都带有前缀“eval _”):
定义评估指标

培训日志中的评估指标
这意味着我们可以在培训工作期间通过正则表达式捕获这些指标,我们可以定义如下:
我们将把它们传递给我们将在下面进一步创建的估算器,以捕获这些指标,这将允许我们比较不同的 NLP 模型。
运行 Sagemaker 实验
为了组织和跟踪模型,我们需要创建一个 Sagemaker 实验对象:
一旦完成,我们就可以开始训练了。我们使用 ml.p3.2xlarge 进行 Sagemaker 培训工作,它将在大约 30 分钟内完成微调。注意,我们为每个培训作业创建了一个 试用 对象。这些试验与我们上面创建的实验相关联,这将允许我们跟踪和比较模型:
上面的代码并行启动了两个培训任务(每个模型一个)。但是,如果您的帐户不允许这样做(可能您的 AWS 帐户限制了培训作业实例的数量),您也可以按顺序运行这些培训作业。只要它们通过试验对象与同一个实验相关联,您就能够评估和比较这些模型。
比较模型
大约 30 分钟后,两个模型都已训练完毕,现在是检索结果的时候了:
得到的数据帧包含了比较两个模型所需的所有信息。例如,我们可以检索我们定义的所有指标的平均值,如下所示:

我们可以看到, distilroberta-base 在召回方面表现稍好,而distilbert-base-uncased在 F1 得分、精度和准确度方面表现更好。数据框架中还有更多列,我将留给读者进一步探索。
结论
在本文中,我们创建了一个 Sagemaker 实验来跟踪和比较 NLP 模型。我们为每个模型创建了试验,并收集了各种评估指标。在对模型进行微调之后,我们能够通过 Pandas 数据框架访问这些指标,并以一种方便的方式对模型进行比较。
模型选择和评估
讨论理解机器学习模型并从中获取更多信息的不同方法
本文是我正在撰写的关于机器学习关键理论概念的系列文章的延续。本系列其他文章在我的 页面 均有。

由 Roman Synkevych 在 Unsplash 上拍摄的照片
模型选择和评估是机器学习工作流中非常重要的过程。这是我们工作流程的一部分,我们将在其中分析我们的模型。我们查看其性能的更深入的统计数据,并决定采取什么措施来改进该模型。这一步通常是表现好的车型和表现非常好的车型的区别。当我们评估我们的模型时,我们可以更好地了解它预测什么好,什么不好,这有助于我们将模型从预测数据集的 65%准确性水平提高到接近 80%或 90%。
度量和评分
假设我们有两个任务假设,h(x)和 h’(x)。我们怎么知道哪个更好。从高层次的角度来看,我们可以采取以下步骤:
- 衡量两种假设的准确性
- 确定两个结果之间是否有统计学意义。如果有,选择表现更好的假设。如果不是,我们不能用任何统计上的确定性说 h(x)或 h'(x)哪个更好。
当我们有一个分类任务时,我们将通过将一个实例分配到其正确的类的能力来考虑我们的模型的准确性。从二进制的角度来考虑这个问题。我们有两个类,1 和 0。因此,当模型将类 1 实例分类为类 1,或者将类 0 实例分类为类 0 时,我们会将正确的预测分类。假设我们的 1 类是“正类”, 0 类是“负类”,我们可以建立一个表,列出我们的模型可能产生的所有可能性。

作者图片
我们也为这些分类命名。我们的真正真负是我们正确的分类,正如我们在两种情况下看到的,实际类和预测类是一样的。模型预测不正确的其他两个类别可以解释如下:
- 假阳性—当模型预测为 1,但实际类别为 0 时,也称为I 型错误
- 假阴性—当模型预测为 0,但实际类别为 1 时,也称为类型 II 错误
当我们获取一系列实例,并用我们观察每个分类的频率填充上表时,我们就产生了所谓的混淆 矩阵。这是开始评估一个比简单的准确率更进一步的假设的好方法。有了这个混淆矩阵,我们可以定义准确率,也可以定义一些其他指标来查看我们的模型执行得有多好。我们使用简称假阳性(FP)、假阴性(FN)、真阳性(TP)和真阴性(TN)。

作者图片
下面是一个 3 类混淆矩阵的例子,以及我们如何将它转化为每一类的回忆、精确和 f1 数字。这里需要提到的是,在超过 2 个类的情况下,我们的 0 类将是所有不是我们的 1 类的类。

作者图片
一个例子
这些额外的度量是有用的,因为它们让我们更深入地了解分类器的行为。例如,考虑以下两个分类器:

作者图片
通过简单的准确度测量,我们倾向于说分类器 B 是更好的分类器,因为它的准确度为 0.75 比 0.50 更高。然而,当我们引入我们的其他度量时,我们可以计算出分类器 B 的精度和召回率是 0。分类器 A 的准确率和召回率分别为 0.25 和 0.50。从这些信息中,我们看到了一幅完全不同的画面。分类器 B 是相对无用的,特别是因为它不能预测任何第 1 类例子,并且精度和召回反映了这一点,而精度数字不能。
另一个常用于理解分类器性能的度量是接收器操作者特征 (ROC)曲线。这些图在 y 轴上绘出了真阳性率,在 x 轴上绘出了假阳性率。我们通过调整我们的分类器的决策阈值来生成这些数据点(即,对于概率分类器,我们将阈值从默认的 0.5 改变为 0.6、0.7 等,其中如果预测值大于该阈值,我们分配类别 1,否则我们分配类别 0)。

我们通常认为曲线下的面积越大,模型就越大,因为它告诉我们这个模型有机会以正确的阈值返回高 TP 率。典型的 ROC 曲线也将绘制从(0,0)到(1,1)的虚线,其代表随机分类器的性能(预测正确类别的概率为 50%)。
过度拟合
在查看模型质量时,过度拟合是一个关键的考虑因素。当假设在训练数据示例中的表现明显好于在测试数据示例中的表现时,就会发生过度拟合。这是机器学习中一个极其重要的概念,因为过拟合是我们想要避免的主要特征之一。正如我在第一篇文章中所定义的那样,对于一个在“真实世界”中稳健有效的机器学习模型来说,它需要能够很好地预测看不见的数据,它需要能够概括。过度拟合本质上防止了一般化,并为我们提供了最初看起来很棒的模型,因为它非常适合我们的训练数据,但我们最终发现该模型将非常适合训练数据。该模型基本上没有确定数据中的一般关系,而是专注于计算如何准确预测这一个样本集。发生这种情况的原因有很多:
- 学习过程被允许持续太长时间
- 训练集中的示例不能准确代表测试集,因此也不能代表更广泛的情况
- 数据集中的一些要素没有提供任何信息,因此模型被分散了注意力,为这些没有实现任何正值的值分配权重
交互效度分析
在模型改进部分可以考虑交叉验证。对于较小的数据集,这是一种特别有用的方法。将我们的数据分为训练和测试数据将减少我们的模型接收到的用于训练的样本数量。当我们的实例数量相对较少时,这可能会对我们模型的性能产生很大影响,因为它没有足够的数据点来研究关系并构建可靠和稳健的系数。除此之外,我们还假设我们的训练数据与我们的测试数据在变量之间具有相似的关系,因为只有在这种情况下,我们才能真正生成一个合理的模型,该模型能够以较高的准确度对未知数据进行预测。
我们在这里可以求助的解决方案是一种叫做交叉验证的方法。在交叉验证中,我们在不同的数据子集上运行我们的建模过程,以获得模型质量的多个度量。考虑下面的数据集,分成 5 个部分,称为折叠。

作者图片
在实验 1 中,我们使用第一个折叠作为我们的测试集,其他的作为训练数据。在实验 2 中,我们使用来自第二次折叠的数据,我们也称之为“维持集”,并使用剩余的 80%来训练模型。我们重复这个过程,使用每个折叠一次作为维持集,以便 100%的数据在某个点被用作维持和训练。交叉验证模型的输出将是所有 k 个折叠的平均准确度(在上面的例子中 k 是 5),这将为我们提供模型实际性能的更具代表性的数字。
交叉验证可以为我们提供更准确的模型质量衡量标准,但是这可能需要很长时间,因为它需要评估多个模型。对于小数据集,额外的计算负担不是大问题,我们可以运行交叉验证。
这就是你对评估模型的总结。我希望在以后的文章中更详细地介绍更多的概念,所以请密切关注这些概念!如果你对我以前的文章感兴趣,也可以关注我的页面。在那之前,✌️.
机器学习中的模型选择
介绍过度拟合、超参数、使用 Python 实现的交叉验证

欠拟合(左)和过拟合(右)的图示。
内容
这篇文章是我将要发表的一系列文章的一部分。你可以点击这里在我的个人博客上阅读这篇文章的更详细版本。在下面,您可以看到该系列的概述
1.机器学习导论
- (一)什么是机器学习?
- (b)机器学习中的模型选择
- (c)维度的诅咒
- (d)什么是贝叶斯推理?
2.回归
3.分类
模型选择和验证
在我之前的文章中,我们讨论了多项式回归。在 Python 实现中,我们选择了一个 4 阶多项式。然而,对于多项式的阶数来说, M =4 可能不是“最佳”选择——但是什么是“最佳”选择,我们如何找到它呢?
首先,在训练过程开始之前设置我们的多项式的阶,我们将我们的模型中的这些特殊参数称为“超参数”。其次,计算出这些超参数值的过程称为超参数优化,是模型选择的一部分。第三,正如在之前的帖子中提到的,机器学习主要与预测有关,这意味着我们将“最佳”模型定义为概括未来数据的最佳模型,也就是说,哪个模型在未经训练的数据上表现最佳?
为了弄清楚这一点,我们通常想出某种评估指标。然后,我们将我们的训练数据集分成 3 个部分:一个训练,一个验证(有时称为开发,以及一个测试数据集。然后,我们在训练数据集上训练我们的模型,在验证数据集上执行模型选择,并在测试数据集上对模型进行最终评估。这样就可以确定泛化误差最低的模型。泛化误差是指模型在看不见的数据上的表现,即模型没有被训练过的数据。
让我们回到多项式回归的例子。在下图中,我绘制了来自前一篇文章的数据点,以及真实函数和 4 个不同阶的 4 个不同估计多项式:2、4、6 和 8。随着我们增加多项式的阶数,我们增加了模型的复杂性,这可以粗略地看作与模型中参数的数量相关。所以我们的模型参数越多,它就越复杂。

由于模型复杂性增加而过度拟合的图示。
随着多项式的阶数(模型的复杂性)增加,它开始更好地逼近数据点,直到它完美地遍历所有数据点。然而,如果我们完美地匹配训练数据集中的数据点,我们的模型可能不会很好地概括,因为数据并不完美;总有一点噪音,上面也能看出来。当我们最终将模型完美地拟合到我们的训练数据集时,我们说我们过度拟合了。
在上图中,当阶数设置为 8 ( M =8)时,我们肯定是过度拟合了。相反,当 M =2 时,我们可以说我们欠拟合,这意味着我们的模型的复杂性不够高,不足以在我们的数据中“捕捉丰富的变化”。换句话说,它没有发现数据中的模式。
交叉验证
如果数据有限,将数据分成几部分可能会有不利的影响,因为验证和测试集的大小很小,因此不能恰当地代表整个定型集。在只有 10 个数据点的极端情况下,如果您进行 80–10–10 分割,这将导致验证和测试集的大小为 1,这对于查看模型的表现是否良好来说并不是一个很好的样本大小!
相反,可以应用称为交叉验证的不同技术。最常见的是k-折叠交叉验证技术,将数据集分成 k 个不同的子集。通过选择 k 个子集中的一个作为验证集,其余的 k 个1 子集中的一个作为训练集,我们可以重复这个过程 k 次,每次选择不同的子集作为验证集。这使得重复训练-验证过程 k 次成为可能,最终遍历整个原始训练集作为训练和验证集。
Python 实现
按照我上一篇文章中的例子,我们可以试着找出多项式的最佳阶数。我们开始定义我们的评估标准;我们将使用流行的均方误差(MSE) ,它与我们在上一篇文章中简要介绍的平方误差之和(SSE)函数密切相关。MSE 被定义为我们的预测和真实值之间的平方差的平均值,形式上

其中 N 是数据点的数量, h 是我们的多项式, w 是我们的多项式的系数(模型参数),并且 x , t 是输入-目标变量对。
下面是 MSE 的一个简单 Python 实现,它将 NumPy 数组作为输入。确保true和pred数组长度相同——如果需要,可以通过断言来实现。
而下面是 k 的一个实现——折叠交叉验证。它产生每个折叠的训练和验证集的索引。我们只需确保n_splits不大于n_points。
摘要
- 超参数是在训练模型之前确定的模型中的参数。
- 模型选择是指选择最能概括的模型的过程。
- 训练和验证集用于模拟看不见的数据。
- 当我们的模型在我们的训练数据集上表现良好,但泛化能力较差时,就会发生过拟合。
- 当我们的模型在我们的训练数据集和看不见的数据上表现不佳时,就会发生欠拟合。
- 我们可以看看我们的模型是否能通过交叉验证技术很好地推广。
- 均方误差或 MSE 是一个常见的评估指标。
模型树:处理混合线性模型和决策树的数据转移
模拟和建模特征和标签偏移

康纳·乐迪在 Unsplash拍摄的照片
所有训练出来的模型都容易变得陈旧无用。众所周知,一段时间后,所有的模型都不再准确。这是正常的,并且是由于数据流中可能发生的时间偏移。特别是,大多数涉及人类活动建模的应用程序都必须被监控并不断更新。例如,一些需求或市场趋势的变化可能会影响客户的购买力。如果我们不能考虑客户习惯的变化,随着时间的推移,我们的预测就会变得不可信。
给定一个监督训练的模型,我们可能会遇到两种相反的情况,影响未来的表现。特征分布的变化或目标分布的变化。特征分布的变化是有害的,因为我们的模型对以前看不到的数据进行预测。相反,标签分布的变化是不好的,因为我们的模型被训练成接近不同的基本事实。
在本帖中,我们做了一个实验来测试一些模型随着时间的推移经受住数据变化的能力。我们考虑线性模型、决策树和线性树。我们想看看不同的制度变化如何影响未来测试数据预测的准确性。这使我们能够详细阐述这三种算法处理不可避免的数据偏移的能力。
线性树,正如我在之前的文章中介绍的,是模型树的一个特例。他们在传递的数据上构建一个树,评估适合线性模型的最佳分割。拟合的模型产生基于树的结构,在叶子中具有线性模型。换句话说,它根据简单的决策规则计算对数据集进行分区的多重线性回归。每个人都可以使用 线性树 包轻松实现线性树。它可以用作 scikit-learn BaseEstimator 来包装来自sklearn.linear_model的每个线性估计器,并构建一个最佳树结构。
实验设置
对于我们的实验,我们使用一些在分类环境中生成的人工数据。我们将数据集划分为一些时间块,用于训练(包括超参数搜索)和评估模型的性能。

训练测试时间分割策略(图片由作者提供)
正如上一节所介绍的,我们使用三种不同的模型:线性模型、决策树和线性树。我们通过改变测试起点来估算时间的流逝。我们考虑 4 个测试块,在不同的日期计算每个模型类型的评估。这使我们能够跟踪模型在一段时间内的行为。
我们提出了三种不同版本的实验:无偏移、特征偏移和标签偏移。
没有轮班
在“无变化”的背景下,我们假设未来会看到同样的数据分布。这是更完美也更不现实的场景。它是完美的,因为我们在相似的数据上训练和测试我们的模型,产生稳定的结果。这是不现实的,因为我们必须幸运地遇到这样的情况。我们生活在一个动态的世界中,一切都容易快速变化,破坏我们的模型性能。
但是,我们从这种情况出发来拟合我们感兴趣的模型。我们在选定的时间块上用超参数调谐来执行训练。对测试集的评估总结在下图中。

没有移位的表演(图片由作者提供)
正如所料,随着时间的推移,模型的精度保持不变。决策树和线性树达到相同的分数。
特征转移
在“特性变化”的环境中,我们可能会遇到一些特性分布随着时间的推移而变化。我们模拟两个不同时间段的两班倒。这可以简单地通过添加一些常量值来改变分布的中心位置。

多年来的特征分布(图片由作者提供)
预测看不见值可能会导致错误的预测。使用基于树的算法很容易成像,其中数据可能落在最未探索的分割部分。显然,在这种情况下,与“无变化”情况相比,我们必须预计性能会普遍下降。我们的模型没有经过处理分布变化的训练,所以我们记录的精度下降了至少 10 个点。我们观察到的是,关于决策树,线性树存活得更好。

功能变化的表演(图片由作者提供)
标签移位
在“标签变化”环境中,我们可能会遇到目标分布随时间的变化。我们模拟这种场景,保持相同的特征生成过程,但是在两个不同的时间段中改变标签平衡。

历年标签分布情况(图片由作者提供)
在这种情况下,我们使用一种算法,根据给定的标签分布进行训练,来预测具有不同标签平衡的数据。达到次优结果是正常的(特别是对于基于树的算法),因为模型不期望特定类出现这么多。我们记录的性能随着时间的推移保持不变,但与‘无变化’的情况相比有所恶化。和以前一样,线性树在决策树中存活得更好。

标签转换的表演(图片由作者提供)
在我们所有存在移位的实验中,我们记录了线性树的良好生存行为。与标准决策树和简单线性模型相比,它们的性能下降较少。它们更好地概括的能力是线性近似和树状结构中的数据分割的联合的结果。这使得当数据落入未探索的分割区域时,线性树更加准确,这在时间数据移动的情况下尤其突出。
摘要
在这篇文章中,我们模拟了一些时态数据移动的情况。当我们预期时间变化时,线性树被证明是测试和验证的好模型。尽管它们没有被广泛采用,但它们是非常简单而强大的预测算法,代表了经典决策树的有价值的替代品。
保持联系: Linkedin
模型验证和监控:ML 生命周期的新阶段
ML 模型的验证/测试和监控在过去可能是一种奢侈。但是随着关于人工智能的法规的实施,它们现在是机器学习管道中不可或缺的部分。

鸣谢:除了带有 AI 的欧盟符号外,作者自己的作品,摘自欧盟委员会网站:https://digital-strategy . EC . Europa . eu/en/library/ethics-guidelines-trust-AI
在过去的十年中,机器学习(ML)的研究和实践在建立使用机器学习模型设计系统和应用程序的通用框架方面取得了很大进展。主要使用软件工程的最佳实践,所谓的 MLOps 生态系统最近也开始出现。一切都好,期待已久。然而,设计 ML 应用程序和服务的生命周期中的两个关键步骤仍然没有被触及。即 ML 车型的测试/验证和监控。
在这篇文章中,我将阐述为什么我们需要在投入生产之前测试我们的 ML 模型,以及我们如何将质量 保证(QA) 实践带入 ML 领域。注意,我将在这里谈论的大多数概念也适用于人工智能(AI) 系统和应用。但是,由于人工智能在过去十年中推动了人工智能的发展,我将主要谈论人工智能。
在整篇文章中,我将首先讨论最近关于人工智能的欧盟委员会法规(在下面的讨论中,我将此称为欧盟法规),然后谈论什么是可信人工智能。在调查可信的人工智能时,我们会看到伴随着人工智能模型而来的问题和风险。然后,我将尝试解释为什么验证和监控是必不可少的,以及我们如何通过采用合适的概念 ML 管道来使我们的模型可信。
1.走向管制人工智能
欧盟委员会最近宣布,为了使人工智能应用更加值得信赖,将提出一些规定。对于人工智能从业者来说,这是一个非常重要的发展,因为人工智能迄今为止仍然没有受到监管,存在各种风险和威胁。我们只关注使用人工智能的好处,却忽略了与之相关的风险。根据这项法规,欧洲各地的公司将需要验证他们的【高风险】人工智能系统,并遵守这些法规。这可能会蔓延到全世界(事实上,在英国、加拿大和美国的一些州已经有了一些规定)。简而言之,欧盟法规根据人工智能系统的风险级别对其进行分类,并强制对高风险人工智能系统进行验证和监控。
如果人工智能系统打算用于附件文件第 4 页和第 5 页中列出的 8 个领域中的任何一个,或者如果该系统对健康和安全构成危害的风险,或者对基本权利造成不利影响的风险,则欧盟法规将该系统分类为“高风险”。我不会在这篇文章中深究监管的细节。感兴趣的读者可以在这里阅读完整的监管提案。本规范中提到的一些重要概念是这些系统的安全性、健壮性和可信度。尽管所有这些概念在许多方面都是相互交织和模糊的,但中心思想是明确的:让人工智能系统安全可靠。我更喜欢用“可信度”作为总括术语来研究这个问题。
2.什么是值得信赖的 AI?
我更喜欢把值得信赖的 AI 概念化为四大支柱。如下图所示,它有安全性、隐私、可靠性和可说明性等方面。

可信人工智能的四大支柱
所有这些都是相互关联的,我们将逐一讨论。但在此之前,请注意欧洲法规是如何提及其中一些内容的:
“高风险人工智能系统的设计和开发方式应使其根据预期目的实现适当水平的准确性、稳健性和网络安全,并在其整个生命周期内在这些方面保持一致。”(欧共体提案,第 51 页)
2.1.安全性:
机器学习模型展示出重要的安全漏洞,这些漏洞使黑客能够操纵这些模型、窃取私人训练数据或在模型中留下后门,这些后门稍后会被触发。过去 7 年的对抗性攻击文献清楚地表明,这些攻击非常强大,防御它们一点也不容易。所有这些安全问题都是可以实现的,并且会影响人们的健康和安全。
例如,在规避攻击中,对手(黑客)可以制造特殊的输入来操纵 ML 模型的预测。他们甚至可以手工输入,这样 ML 模型就可以预测这些攻击者想要什么!想象一下,如果一辆自动驾驶汽车看到停车标志就把速度提高到 150,那将是多么灾难性的一件事!欧盟法规明确表示,人工智能模型应该针对这些安全问题进行测试:
“解决人工智能特定漏洞的技术解决方案应包括,在适当的情况下,预防和控制试图操纵训练数据集(“数据中毒”)的攻击、旨在导致模型出错的输入(“对抗性示例”)或模型缺陷的措施。”(欧共体提案,第 52 页)
2.2.隐私:
更多的时候,我们使用我们拥有或收集的私人数据来训练我们的机器学习模型。这种数据通常包含敏感内容,提供真实的人和机构的信息。所谓的“成员推断”攻击揭示了机器学习模型(尤其是深度学习模型)倾向于记忆训练数据,并可能泄露它们!这意味着我们需要采取正确的措施来防止我们的 ML 模型泄露私人数据。因此,这是模型测试/验证步骤的一部分。
请注意,甚至在我们使用这些数据来训练我们的模型之前,就可以对这些数据应用一些方法。匿名化(!),假名化或应用不同的隐私技术可能对某些情况有效。即使在这种情况下,测试也可以在模型训练后进行,以确保一切都在预期的范围内。
2.3.可靠性和公平性:
你会毫不犹豫地相信自动驾驶汽车吗?你会依赖计算机视觉系统来检测癌症,而不咨询真正的医生吗?这些问题的答案与我们对这些人工智能系统的信任密切相关。我们希望他们是可靠的,这样他们就能在各种可能遇到的情况下持续工作。
理解我们的 ML 模型的优点和缺点使我们有信心信任它们。通过研究我们的模型在不同环境下的行为,我们可以了解我们的模型是如何表现的。这种情况的一个特例是调查 ML 模型决策过程中的偏差。了解 ML 模型是否有利于一个群体而不利于其他群体,可以帮助我们防止不公平的预测。因此,检查公平性和可靠性将在模型测试的议程中。欧盟条例是这样说的:
“为了确保对高风险人工智能系统进行偏见监控、检测和纠正,此类系统的提供商可以处理……[GDPR 相关法规]中提及的特殊类别的个人数据,但需遵守对自然人基本权利和自由的适当保护,包括对再使用和使用最先进的安全和隐私保护措施的技术限制,如匿名化或加密(匿名化可能会严重影响所追求的目的)。”(欧共体提案,第 48 页)
2.4.可解释性:
众所周知,深度学习模型是黑盒,以至于我们无法真正知道为什么一个模型会对给定的输入产生特定的结果(局部可解释性)。此外,我们不能完全解释我们的模型从训练数据中学到了什么(全局可解释性)。缺乏可解释性是在可解释性和可解释性至关重要的应用领域中使用这些模型的主要障碍。一组主要的例子是金融部门中使用的一些模型,其中现有的法规要求它们是可解释的。
虽然可解释性不是我们在每种情况下都要寻找的东西,但在其他情况下,我们可能需要它来解释为什么一个特定的结果是由 ML 模型预测的。在这些情况下,在将 ML 模型投入生产之前,应该调查可解释性和可解释性边界。欧盟法规还强制要求:
“高风险人工智能系统的设计和开发应确保其操作足够透明,使用户能够解释系统的输出并正确使用。”(欧共体提案,第 50 页)
3.我们如何测试/验证我们的机器学习模型?
这使我们开始讨论我们应该如何测试我们的模型,并验证它们的安全性、可靠性、健壮性等。不幸的是,目前的 ML 管道在将验证阶段放在适当的位置上还处于起步阶段。这在一定程度上是因为 ML 模型的安全问题到目前为止作为学术玩具而不是真实世界场景被忽视了。然而,越来越多的文献指出,使 ML 模型对安全问题更健壮也使它们对看不见的数据更一般化。因此,随着越来越多的人意识到测试 ML 模型的好处,将验证放在 ML 管道的正确位置是一种迫切的需要。
传统的 ML 管道建立了以下工作流程:

从收集数据到将最佳模型投入生产,这个工作流程是每个从业者或多或少都熟悉的。所以,我不会讨论这个过程背后的基本原理。我想谈的是,当我们合并模型验证和监控时,这个工作流会如何变化。具有验证和监控阶段的机器学习管道变成如下:

在上面的工作流程中,我们在将模型投入生产之前对其进行验证,在生产过程中监控它们的合规性。此外,对于高风险的人工智能系统来说,“T6”人工监督是必要的。现在,让我们试着简要解释一下它们:
3.1.验证模型:
正如我们之前提到的,机器学习模型容易受到对抗性攻击,当看到意外输入时,可能会表现出意外的行为。此外,公平性和偏差问题可能源于数据或算法本身。考虑到所有这些,在验证阶段,我们需要测试我们的模型,以确定这些漏洞有多重要,我们的模型有多健壮。这一事实带来了许多重要的观点。
首先,我们现在需要在评估我们的模型时使用健壮性指标,如果一个模型落后于健壮性基准,我们不应该将该模型投入生产使用。这与通常的性能指标类似,如准确度、F1 分数或其他指标。现在,我们不仅要考虑性能指标,还要考虑健壮性指标。这意味着在我们选择了性能指标方面的最佳模型之后,我们需要检查它的健壮性。如果一个模型通过了健壮性基准,这意味着它可以投入生产。但是,这些健壮性指标是什么,我们应该如何确定健壮性基准?这是一个至关重要的问题。然而,这超出了本文的范围,我将在后续文章中讨论这些内容。
其次,如果一个模型未能通过健壮性基准测试,那么首先要做的就是努力使它更健壮,这样它就能通过基准测试。我们可以尝试几种技术,例如对抗训练、差异隐私等。但是请注意,这些技术通常在狭窄的环境中有效,你可能需要利用许多技术来解决特定的问题。也就是说,如果我们的模型泄漏了私有数据,那么我们应该应用一种方法来防止数据泄漏。如果我们的模型容易受到规避攻击,那么我们应该使用对抗训练这样的技术来缓解这个问题。
第三,如果我们无法解决模型的健壮性问题,那么我们应该考虑选择另一个不太容易出现健壮性问题的模型。这需要使用性能指标返回到模型选择阶段,但这一次要消除以前的模型。事情可能会变得复杂,因为我们可能需要改变模型架构,甚至建模方法。
在这个阶段可以使用的技术和方法构成了一个很长的列表。因此,我不会在这里进一步挖掘它们,而是推迟到后面关于模型验证方法和技术的文章。话虽如此,我想补充一点,敌对攻击、数据隐私、人工智能的可解释性和公平性方面的文献在这个阶段都是相关的,并且一些最佳实践可用于启动。
3.2.监控模型:
故事并没有以将最好的模型投入生产而结束。没有完美的模型,因此每个模型都容易出错。为了能够对危险的或者至少是意想不到的事件做出反应,我们需要监控在生产中服务的模型。这需要记录模型预测并密切关注它们。正如欧盟法规所述,这也是高风险系统的强制性要求:
“高风险人工智能系统的设计和开发应具备在高风险人工智能系统运行时自动记录事件(“日志”)的能力。这些测井能力应符合公认的标准或通用规范。”(欧共体提案,第 49 页)
欧盟法规要求监控实时模型,并对不可接受的事件采取相应措施。这通常需要“人在回路中”的方法,由真人来决定可疑事件是否可接受。在某些方面,这类似于网络安全行话中的 SOC(安全运营中心)团队的职责。也就是说,有些人应该监控服务中模型的行为。这种监控:
- 这在实践中可能是困难的或者甚至是不可能的。
- 可以是定期的,在预先定义的周期内,如一天或一周。
此外,来自系统用户的反馈循环也应该到位,以便在收到投诉时,团队应该调查模型的行为。简而言之,我们需要一个具体的事件响应计划!
4.结束语
人工智能现在和将来都是现代技术的一个规范领域。这个事实迫使从业者保证他们的人工智能系统的质量,就像保证软件系统的质量一样。这需要将人工智能系统的测试、验证、质量保证和合规性等概念纳入服务/产品开发管道。机器学习实践者尤其应该采用一个包含这些阶段的概念框架。无论如何,模型验证和监控已经伴随着我们的今天,并将在未来继续。
使用 TimeGAN 建模和生成时间序列数据
使用具有 TimeGAN 高级实现的库生成时间序列数据

1.背景
在之前的一篇文章中,我们探索了生成人工或合成数据的想法,给出了有限数量的数据集作为起点。当时取的数据是表格形式的,就像我们平时遇到的常规数据集。然而,在本文中,我们将着眼于时间序列数据,并探索一种生成合成时间序列数据的方法。
2.时间序列数据—简要概述
那么,时间序列数据与常规的表格数据有什么不同呢?时序数据集有一个额外的维度—时间。把它想象成一个 3D 数据集。比方说,我们有一个包含 5 个要素和 5 个输入实例的数据集。

作者图片
那么时间序列数据基本上是该表在第三维中的扩展,其中每个新表只是新时间步的另一个数据集。

作者图片
主要区别在于,与表格数据相比,时间序列数据有更多的数据点实例。
3.案例研究:能源数据集
如果我们查看能源数据集(此处可用此处,最初取自此处的数据),它实际上看起来只是一个常规的表格数据集,每一行都意味着一个新的时间步长,并以特征的形式拥有其相应的数据点。根据数据栏,每个条目在 10 分钟后被记录。
但是我们在前面的章节中已经看到它看起来像是一个 3D 表格数据集,对吗?这是我们可以利用一种聪明的采样数据点的方式来创建第三维空间的地方。
我们取一个大小为 24 的窗口,并沿着数据集的行运行它,一次移动一个位置,从而获得一定数量的 2D 矩阵,每个矩阵的长度为 24,并具有我们所有的列特征。

作者图片
在能源数据集中,有 19736 行。通过每 24 行移位采样,我们得到 19712 个条目,每个条目有 24 行和 28 个特征。然后,我们当然可以随机混合它们,以使它们独立且同分布(IID)。因此,本质上我们得到了一个维度数据集(19712,(24,28)),其中 19712 个实例中的每一个都有 24 行(即时间步长)和 28 个特征。这个实现可以在这里找到。
4.使用 TimeGAN 生成时间序列数据
TimeGAN(时序生成对抗网络)是针对合成时序数据的一种实现。它基于同一作者的一篇论文。YData 的 ydata-synthetic TimeGAN 实现本质上使其在一个易于使用的库中可用,该库可以作为 Python 包索引(PyPi)安装。在本文中,我们将使用撰写本文时的最新版本 0.3.0。
pip install ydata-synthetic==0.3.0
关于这个的更多细节在ydata-synthetic repository上。在本节中,我们将研究如何使用能源数据集作为输入源来生成时间序列数据集。
我们首先读取能量数据集,然后以数据转换的形式应用一些预处理。这种预处理实质上是在范围[0,1]内缩放数据,并应用我们在上一节中看到的数据转换。
现在,从这个时间序列数据( energy_data )生成实际的合成数据是最简单的部分。我们基本上是在我们的 energy_data 上训练 TimeGAN 模型,然后使用训练好的模型来生成更多。
其中必须根据我们的要求适当地定义要提供给 TimeGAN 构造函数的参数。我们将 n_seq 定义为 28(特性),将 seq_len 定义为 24(时间步长)。其余参数定义如下:
现在我们已经有了生成的 synth_data ,让我们看看它与我们的原始数据相比,在视觉上有什么不同。我们可以为 28 个特征中的每一个绘制一个图,并观察它们随时间步长的变化。

作者图片
这些图只是稍微有点意思,可能对比较没有用处。显而易见,合成数据肯定会不同于原始(真实)数据,否则这样做没有任何意义。鉴于数据集有如此多的要素,也很难以直观的方式将它们一起可视化和解释。更有趣和更有帮助的是在一个对我们来说更容易理解和更直观的维度上可视化(和比较)这些生成的数据。
5.评估和可视化
我们可以利用以下两种众所周知的可视化技术:
- PCA —主成分分析
- t-SNE——t-分布随机邻居嵌入
这些技术背后的基本思想是应用降维,以便可视化那些具有大量维度(即大量特征)的数据集。主成分分析和 t-SNE 都能够实现这些,它们之间的主要区别在于,主成分分析试图保留数据的全局结构(因为它着眼于在整个数据集内全局保留数据集方差的方式),而 t-SNE 试图保留局部结构(通过确保原始数据中靠近的点的邻居在降维空间中也靠近)。可以在这里找到详细说明差异的精彩答案。
对于我们的用例,我们将使用 sklearn 中的 PCA 和 TSNE 对象。
既然我们要绘制的数据已经准备好了,我们可以使用 matplotlib 来绘制原始的和合成的转换。 pca_real 和 pca_synth 一起给我们 pca 结果,并且 tsne_results 包含原始的和合成的(由于连接)t-SNE 变换。

作者图片
这些图表告诉我们什么?它们向我们展示了如果将整个数据集转换为具有较少要素的数据集(两个轴对应于两个要素),它可能会是什么样子。主成分分析图可能不足以得出正确的结论,但 t-SNE 图似乎告诉我们,原始数据(黑色)和合成数据(红色)似乎遵循相似的分布。此外,特定于数据集的观察结果是,在整个数据集中有 7 个组(聚类),它们的数据点彼此(明显地)相似(因此聚类在一起)。
6.结论
我们简要地看了时间序列数据以及它与表格数据的区别。通过 ydata-synthetic 库使用 TimeGAN 架构是为了生成更多的时序数据。笔记本中的完整实现可以在这里找到。
使用 Memgraph 对交通网络进行建模、可视化和导航
实践教程
了解如何通过使用 Cypher 查询语言和简单的图形算法在伦敦找到自己的路。

威尔·H·麦克马汉在 Unsplash 拍摄的照片
介绍
如果骑自行车或步行不是一种选择,使用公共交通是在城市中旅行的最环保的方式。在公共交通系统中导航可能会令人困惑和复杂。
作为一名乘客,你通常想知道如何从一个车站到另一个车站,以及在哪里换车,以尽可能优化你的旅程。本质上,问题是在复杂的车站和线路网络中寻找最短路径。这种类型的问题是一个典型的图形用例。图表用于建模和导航各种领域的复杂网络问题,包括交通、网络安全、欺诈检测等等。
在本教程中,您将探索如何使用图表来建模和导航交通网络。您将学习如何将伦敦地铁数据集导入 Memgraph,并使用 Memgraph Lab 在地图上显示该数据集。接下来,您将学习如何使用 Cypher 查询语言和图形算法来帮助您探索伦敦地铁网络,并在城市中找到自己的路,而不会迷路或因交通堵塞而浪费时间。
先决条件
要完成本教程,您需要:
- 一个 Memgraph DB 的安装:一个本地的、内存中的图形数据库。要安装和设置 Memgraph DB,请遵循安装指南上的 Docker 安装说明。
- 一个 Memgraph Lab 的安装:一个用于导入数据、开发、调试和分析数据库查询以及可视化查询结果的集成开发环境。
- 你可以在这里下载伦敦地铁网络数据集。
定义图形数据模型
在本教程中,您将使用由 Nicola Greco 发布的伦敦地铁网络数据集。它包含 302 个站(节点)和 812 个连接(边)。节点lat和lng属性表示一个站点的坐标,对于在地图上可视化数据也很重要。
如果两个站相邻,则用:Connection类型的边连接。因为火车是双向行驶的,所以每个方向都有优势。
每条边都有一个表示在两个站点之间旅行所需时间(以分钟为单位)的time属性和一个line属性,在本教程稍后添加一个name属性后,这个属性会更有意义。
既然已经定义了方案,就可以导入数据集了。

作者图片
使用 CVS 导入工具导入数据集
第一步是将数据加载到 Memgraph 中。Memgraph 附带了将数据导入数据库的工具。对于本教程,您将使用 CSV 导入工具。
CSV 导入工具应该用于将数据初始批量摄取到 Memgraph 中。在接收时,CSV 导入程序会创建一个快照,数据库将使用该快照在下次启动时恢复其状态。你可以在这里了解快照。
格式化 CSV 文件
CSV 文件的每一行代表应该导入到数据库中的单个条目。节点和关系都可以使用 CSV 文件导入数据库。
要获得所有相关数据,您需要三个文件。第一个包含关于节点的数据(stations.csv),另外两个包含关于关系的数据(connections.csv和connections-reverse.csv)。每个 CSV 文件必须有一个描述数据的标题。
stations.csv有如下表头:
id:ID(STATION),lat:double,lng:double,name:string,display_name:string,zone:int,total_lines:int,rail:int
ID字段类型设置在创建关系时将用于节点的内部ID。这是可选的,没有指定ID值的节点将被导入,但不能连接到任何关系。
当导入关系时,START_ID字段类型将应该与关系连接的开始节点设置为带有END_ID的结束节点。必须指定字段,并且节点ID必须是节点 CSV 文件中指定的节点 id 之一。
文件connections.csv和connections-reverse.csv除了头之外是一样的。这是因为我们需要两个方向的连接,因此,与其在导入数据后创建缺失的关系,不如导入第二个文件,并在其中切换START_ID和END_ID。因此,您从第一个文件中获得了每个关系的副本,只是方向相反。
:END_ID(STATION),:START_ID(STATION),line:int,time:int:START_ID(STATION),:END_ID(STATION),line:int,time:int
使用 CSV 导入工具
【注意】如果您的 Memgraph 数据库实例正在运行,您需要在继续下一步之前停止它。
首先,您需要将 CSV 文件复制到 Docker 映像可以看到它们的地方。导航到包含数据文件的文件夹,并运行以下命令:
docker container create --name mg_import_helper -v mg_import:/import-data busybox
docker cp connections.csv mg_import_helper:/import-data
docker cp connections-reverse.csv mg_import_helper:/import-data
docker cp stations.csv mg_import_helper:/import-data
docker rm mg_import_helper
用于指定输入 CSV 文件的两个主要标志是--nodes和--relationships。
节点标签(Station)和关系类型(:Connection)需要在导入时设置,因为 CSV 文件不包含这些信息。
您可以使用以下命令运行导入程序:
docker run -v mg_lib:/var/lib/memgraph -v mg_etc:/etc/memgraph -v mg_import:/import-data --entrypoint=mg_import_csv memgraph --nodes Station=/import-data/stations.csv --relationships Connection=/import-data/connections.csv --relationships Connection=/import-data/connections-reverse.csv --data-directory /var/lib/memgraph true --storage-properties-on-edges true
现在,您可以通过运行以下命令来启动 Memgraph:
docker run -v mg_lib:/var/lib/memgraph -p 7687:7687 memgraph:latest --data-directory /var/lib/memgraph
如果您对 CSV 导入工具有其他问题,请查看我们的文档: CSV 导入工具。
用 Memgraph Lab 可视化图形
一旦你有了 Memgraph 中的数据,用 Memgraph Lab 可视化它就相当容易了。 Memgraph Lab 自动检测具有数字lat和lng属性的节点。要获得图表,请运行以下 Cypher 查询:
MATCH (n)-[r]-(m)
RETURN n,r,m;
如果一切正常,您应该会得到类似于下图的可视化效果。

作者图片
向边缘添加属性
属性没有多大意义,因为我们通常通过名称或颜色来识别伦敦地铁线。除非你在边上添加一个name属性,否则地图会更加混乱。
下面的 Cypher 查询使用了允许列出多个谓词的CASE表达式。匹配第一个计算结果为true的表达式,在THEN关键字之后提供的表达式的结果用于设置关系的name属性的值。
MATCH (n)-[r]-(m)
SET r.name = CASE
WHEN r.line = 1 THEN "Bakerloo Line"
WHEN r.line = 2 THEN "Central Line"
WHEN r.line = 3 THEN "Circle Line"
WHEN r.line = 4 THEN "District Line"
WHEN r.line = 5 THEN "East London Line"
WHEN r.line = 6 THEN "Hammersmith & City Line"
WHEN r.line = 7 THEN "Jubilee Line"
WHEN r.line = 8 THEN "Metropolitan Line"
WHEN r.line = 9 THEN "Northern Line"
WHEN r.line = 10 THEN "Piccadilly Line"
WHEN r.line = 11 THEN "Victoria Line"
WHEN r.line = 12 THEN "Waterloo & City Line"
WHEN r.line = 13 THEN "Docklands Light Railway"
END;
设计图表样式
虽然数据集现在更有意义,但地图的样式仍然有点过于统一。您可以使用 Memgraph Lab 中的样式编辑器根据自己的喜好设置地图的样式。你可以在本教程中阅读更多关于样式编辑器的内容。
首先,让我们把车站变小,做成正方形。一个带黑边的白色圆圈是为换乘站预留的(在那里你可以在线路之间切换)。为此,您将使用以下样式脚本:
@NodeStyle HasLabel?(node, "Station") {
size: 15
color: black
shape: "square"
color-hover: red
color-selected: Darker(red)
}@NodeStyle Greater?(Property(node, "total_lines"), 1) {
size: 30
shape: "dot"
border-width: 2
border-color: black
color: white
color-hover: red
color-selected: Darker(red)
}@NodeStyle HasProperty?(node, "name") {
label: AsText(Property(node, "name"))
}
现在你可以添加最重要的东西,标志性的线条颜色!为此,您将使用以下脚本:
@EdgeStyle HasProperty?(edge, "name") {
label: AsText(Property(edge, "name"))
width: 10
}@EdgeStyle Equals?(Property(edge, "line"),1) {color: #AE6017}
@EdgeStyle Equals?(Property(edge, "line"),2) {color: #F15B2E}
@EdgeStyle Equals?(Property(edge, "line"),3) {color: #FFE02B}
@EdgeStyle Equals?(Property(edge, "line"),4) {color: #00A166}
@EdgeStyle Equals?(Property(edge, "line"),5) {color: #FBAE34}
@EdgeStyle Equals?(Property(edge, "line"),6) {color: #F491A8}
@EdgeStyle Equals?(Property(edge, "line"),7) {color: #949699}
@EdgeStyle Equals?(Property(edge, "line"),8) {color: #91005A}
@EdgeStyle Equals?(Property(edge, "line"),9) {color: #000000}
@EdgeStyle Equals?(Property(edge, "line"),10) {color: #094FA3}
@EdgeStyle Equals?(Property(edge, "line"),11) {color: #0A9CDA}
@EdgeStyle Equals?(Property(edge, "line"),12) {color: #88D0C4}
@EdgeStyle Equals?(Property(edge, "line"),13) {color: #00A77E}
您的图表现在应该看起来像这样:

作者图片
探索图形网络
现在,您已经将图形数据加载到 Memgraph 中,并在 Memgraph Lab 中设置了可视化,您已经准备好开始使用图形遍历和算法来探索伦敦地铁网络。
假设你正在计划一次旅行,想在一个交通便利的车站附近找一家酒店。
通过使用以下密码查询,您将找到哪些工作站与其他工作站的连接最多:
MATCH (s1:Station)-[:Connection]->(s2:Station)
WITH DISTINCT s2.name as adjacent, s1.name as name
RETURN name AS Station_name, COUNT(adjacent) AS Total_connections
ORDER BY Total_connections DESC LIMIT 10;
对于每个桩号,查询的第一部分匹配所有相差一度的桩号。第二部分计算连接数,并返回 10 个连接最多的站的列表。

作者图片
现在你有你的清单,国王十字圣潘克拉斯是显而易见的选择。通过运行以下查询,您可以很容易地检查您可以从 King's Cross St. Pancras 访问哪些管道线路:
MATCH (:Station {name:"King's Cross St. Pancras"})-[r]-(:Station)
WITH DISTINCT r.name AS line
RETURN line;

作者图片
比方说,你正在进行一次预算旅行,你想留在第一票价区,以保持较低的交通成本。在这种情况下,您可以使用广度优先算法来查找在第一票价区停留期间您可以从圣潘克拉斯到达哪些车站:
MATCH p = (s1:Station {name:"King's Cross St. Pancras"})
-[:Connection * bfs (e, n | n.zone = 1)]-
(s2:Station)
UNWIND (nodes(p)) as rows
WITH DISTINCT rows.name as Station
RETURN Station LIMIT 10;
(e, n | n.zone = 1)被称为滤波器λ。这个函数采用一个边符号e和一个节点符号n,并通过返回true或false来决定该边和节点对在广度优先扩展中是否有效。在这个例子中,如果车站在第一收费区,lambda 返回true。

作者图片
或者,如果您更喜欢视觉,您可以通过运行以下查询来可视化您的结果:
MATCH p = (:Station {name:"King's Cross St. Pancras"})
-[:Connection * bfs (e, n | n.zone = 1)]-
(:Station)
RETURN p;

作者图片
假设你刚刚到达伦敦希思罗机场。你可能累了,急需洗个澡,打个盹儿,但是在去酒店的路上,你会想去位于威斯敏斯特地铁站的大本钟。
幸运的是,Memgraph 可以帮助你立刻找到最快的路线!
寻路算法是经典的图论问题之一,自 19 世纪以来一直被研究。最短路径算法计算图中两个节点之间的路径,使得边权重的总和最小。
这里的语法类似于广度优先搜索语法。代替一个过滤器λ,我们需要提供一个权重λ和总权重符号。给定一个边和节点对, weight lambda 必须返回使用给定边扩展到给定节点的成本(time)。
MATCH p = (s1:Station {name: "Heathrow Terminal 4"})
-[edge_list:Connection *wShortest (e, n | e.time) time]-
(s2:Station {name: "Westminster"})
RETURN *;

作者图片
这条路线将让你在 43 分钟内从希思罗机场到威斯敏斯特,但在环线和区线有延迟,所以你想把他们排除在搜索之外。您可以在最短路径查询中组合使用权重和过滤器λs:
MATCH p = (s1:Station {name: "Heathrow Terminal 4"})
-[edge_list:Connection *wShortest (e, n | e.time) time (e, n | e.name != "Circle Line" AND e.name != "District Line")]-
(s2:Station {name: "Westminster"})
RETURN *;

作者图片
新路线只多 3 分钟,但你会避免延误。
从威斯敏斯特出发的最短路线将在 10 分钟内带您到达酒店(不包括环线和区线)。
MATCH p = (s1:Station {name: "Westminster"})
-[edge_list:Connection *wShortest (e, n | e.time) time (e, n | e.name != "Circle Line" AND e.name != "District Line")]-
(s2:Station {name: "King's Cross St. Pancras"})
RETURN *;

作者图片
令人惊讶的是,伦敦有多少博物馆和美术馆是免费参观的。大英博物馆绝对应该是你的首选!虽然你可以在那里呆上几个小时,但是你想什么都看一点,所以让我们再增加几个选项。
托特纳姆法院路站离大英博物馆最近,这将是您的第一站。南肯辛顿站距离三个令人惊叹的博物馆只有几步之遥:科学博物馆、自然历史博物馆和维多利亚和阿尔伯特博物馆。要找到旅行的最佳路线,您可以使用以下 Cypher 查询:
MATCH p = (:Station {name: "King's Cross St. Pancras"})
-[:Connection *wShortest (e, n | e.time) time1]-
(:Station {name: "Tottenham Court Road"})
-[:Connection *wShortest (e, n | e.time) time2]-
(:Station {name: "South Kensington"})
RETURN p, time1 + time2 AS total_time;

作者图片
结论
在本教程中,您学习了如何使用 Memgraph 和 Cypher 对复杂的交通网络进行建模和导航。在图形算法的帮助下,您找到了最佳路线,并学习了如何使用 Memgraph Lab 可视化数据以充分利用数据。如果你有兴趣探索另一个图形路线规划教程,你可以在 Memgraph Playground 上查看探索欧洲路网演示。
一个朋友曾经告诉我:
在伦敦你永远不会迷路,你只要找到一个地铁站,就知道你在哪里了
我会说:
“你永远不会在伦敦迷路,你只需要用 Memgraph 找到最短的路径!”
记住,永远站在右边,别忘了注意空隙!
阿尔巴尼亚地拉那自行车共享服务模型
使用无监督学习为自行车共享服务的最佳城市场所建模

在 Unsplash 上由 Edin Murati 拍摄的照片
在这个故事中,我想从数据科学的角度来看看自行车共享服务。具体来说,这篇文章是关于使用各种建模技术来找出在阿尔巴尼亚地拉那放置自行车站的最佳地点。地拉那是我的家乡,最近我受到了它的新举措的启发,这些举措将骑自行车作为一种通勤和健康活动,特别是通过增加受保护的自行车道、特别的“无车日”和一般的道路安全措施。然而,这个城市没有一个主要的自行车共享平台,所以我想使用机器学习来解决这个问题。
首先,一些设置。当考虑最佳自行车站位置时,考虑诸如人口聚集、与其他公共交通的接近程度或人们通常通勤的商业区聚集等因素是有意义的。这是因为建立一个成功的自行车共享服务将涉及到确保尽可能多的人可以使用它,并且他们可以可行和可靠地每天使用它去工作或其他地方。市长办公室提供了一些关于每个行政区域的人口以及公共交通站路线的信息。我们将首先在这两个基础上构建,并使用 K-Means 聚类和叶子来可视化我们的预测。
数据
这是地拉那所有 11 个行政区的人口统计数据集(摘自本网站:【https://opendata.tirana.al/?q=popullsia-e-tiranës-2020】T4)。为了更清楚,我还使用 GeoPandas 内置的绘图功能添加了一张地图:

包括人口数量和多边形几何的数据框(图片由作者提供)

地拉那的密度(图片由作者提供)
上面的地图实际上是绘制区域的密度,以说明不同的区域大小,我选择了红色的连续调色板,使不同值之间的区别清晰。
此外,我们还可以访问公交车站数据,这些数据代表主要的公交线路和停靠点(摘自本网站:https://open data . Tirana . al/sites/default/files/Lin jat _ e _ autobus _ ve _ publik _ _ dhe _ stacionet . geo JSON)。地拉那没有地铁系统,所以大部分公共交通实际上是由公共汽车进行的。在下面,您可以找到公交车站地图的数据框和绘图:

地拉那的公交车站(图片由作者提供)
酷!现在让我们开始建模。首先,让我们用 K-Means 聚类模型来拟合我们的数据。模型将作为输入的要素是公交车站点的 x 和 y 坐标,并且它将输出多个聚类。以下是拟合和预测:

叠加了集群的公交车站地图(图片由作者提供)
现在我们有了一些预测,让我们在上下文中或者在地拉那的地理空间中可视化它们。为此,我们可以使用 glory,这是一个用于地图可视化的 Python 包,它允许我们添加如下弹出窗口:

星团的叶图(图片由作者提供)
然而,这种模式足够好吗?需要记住的一点是,我们当前的模型没有考虑这些站点所服务的人口规模,因此它可能不代表自行车共享服务需求最多的地方。幸运的是,我们可以向 K-Means 聚类传递一个“权重”参数,该参数在聚类数据时也会考虑人口规模:
现在让我们将两个聚类结果映射在一起,看看预测是如何变化的:

星团的叶图(图片由作者提供)
完善模型
从这一点出发,我们将继续使用加权 K 均值模型,并尝试对其进行改进。在我们开始之前,让我们看看如何对我们正在生产的模型进行评分。我们将使用剪影配乐和卡林斯基哈拉巴斯配乐。前者取-1(最差)到 1(最好)的值,计算为每个样本的平均类内距离和平均最近类距离。后者不受数值的限制,而是被定义为所有聚类的聚类间离差和聚类内离差之和的比值。
因此,我们可以看到,在第二个加权模型中,轮廓得分实际上是下降的,Calinski Harabasz 得分也是如此。
要使用多少个集群?
构建模型的另一个方面是考虑将点分成多少个聚类,因为这对于聚类的整体结构以及有多少个点进入其中有许多影响。以下是如何在代码中实现这一点,并在绘图中将其可视化:

肘法(图片由作者提供)
让我们将这一最佳聚类数应用于我们的模型,并更新我们的地图以显示它们,以绿色添加:

星团的叶图(图片由作者提供)
在这个小故事中,我们研究了如何建立一个模型来预测在大都市放置自行车站的最佳位置,在这个例子中是地拉那,我们探索并改进了我们模型的参数。在奠定基础之后,接下来的一些步骤将是让其他无人监管的模型适应数据,如自组织地图,并为数据集补充更多细节信息,如交通状况、在某个位置工作的人数,或许还有污染数据。这些可以为更复杂的模型提供信息,最终可以在现实世界中实现。
感谢您的阅读!
建模咖啡研磨机
咖啡数据科学
咖啡研磨机客观测量的首次尝试
当初买咖啡研磨机的时候,没有考虑好不好。它很便宜,而且在某种程度上很有效。这是一个 Hario 手动研磨机,它并不真的意味着浓缩咖啡。经过几年的其他几次升级,我终于准备出一些更大的账单来买一台真正好的电动研磨机。我的问题是不能区分好的磨床和坏的磨床。

我现在的研磨机。所有图片由作者提供
大多数研磨机评论集中在保留或与商业用途相关的使用方面。在过去的几年里,研磨咖啡已经被量化,但是这些并没有给出超过 500 美元的研磨咖啡质量更好的明确指标。通常,这些测试是用单个咖啡豆进行的,咖啡是拨入的。
谁说我不会拨入两个不同的磨床?冲泡方法、水温、水流、剂量或压力呢?量化分级研磨机的变量数量是巨大的。
我之前已经放弃了所有在互联网上容易找到的与浓缩咖啡相关的数据,但当它涉及到研磨机时,我等待了。最近,我又看了看,看看一个简单的模型能否预测两个研磨机将如何提取。我的模型只看表面面积和一个武断的理解研磨分布如何提取好的部分而不是坏的部分。
模型
当观察咖啡渣时,可用的表面积决定了提取咖啡的速度。理解表面积相对于体积的一个好方法是比值(表面积(SA)/体积(V)),如果我们假设咖啡颗粒是球形的,那么比值是 3/半径。对于其他形状,SA/V 通常是某个常数除以半径。

我们知道咖啡的最大提取率是 30%左右,所以我们要说的是,一旦你达到这个提取率,你就不能再提取了。此外,让我们假设只有前 22%的萃取物具有最好的味道。最后的 8%将被认为具有不太理想的更涩的味道。
现在,我们只需要一个任意的变量来封装流量、温度、压力和其他任何影响提取的因素。我们可以假设这些面积不变,即使当圆盘破裂时流量增加。但是为了测试的目的,我们可以扫描这个任意值的几个数字,看看它是如何影响提取的。然后我们可以看看几个不同的分布,真实的和模拟的,看看这个模型有多好地描述了我们的体验。
显然,在执行这种分析的同时还拥有一些真实世界的镜头提取数据将是惊人的,但这只是一小步。
样品
让我们从 EK43 研磨机的样本分布开始。粒子的百分比加起来并不是 100%,但是对于这些测试来说,这并不重要。所有的箱子都是相互关联的。幅度偏移会导致相同的结果。

首先,让我们看看提取 vs 建模率,我们也可以看看过度提取或糟糕的提取。随着总开采量的上升,过度开采最终将开始上升。请记住,更好的咖啡研磨将提取快,因此是第一个过度提取。

我们可以用这个糟糕的 EY 与总的进行比较,我们可以绘制出一个基本的 ROC 曲线。最好的研磨者会有较高的总 EY 或良好的 EY,较低的坏 EY。我添加了理论上的好的和坏的性能线,以给出性能需要根据模型如何变化的想法。

对于一些测试来说,这是一个很好的起点,在这些测试中,我们可以改变分布,扩大或缩小分布,严格限制分布,或者增加峰值。
粒子分布偏移
我们可以很容易地将分布向右或向左移动,以查看它们的效果。最精细的分布将给出跨速率的最高提取,但它也将给出最高的不良提取。最粗的实际上具有最高的提取能力,而不会得到提取的坏部分。这是有趣的,因为它类似于在高流速下较粗的研磨咖啡也可以实现高提取量的概念。

更窄/更宽
如果分布变得更窄或更宽会发生什么?对提取的影响很难看到,但是对于更窄的分布,不良提取减少。这在好的 EY vs 坏的中尤其明显。

这种想法与高端研磨机试图做的事情一致,将分布控制在更紧的范围内。
受限分发
我们可以通过将分布保持在三个箱来将这个概念变窄到更极端。这显示了 4 个变体之间的细微差别,但它们比原始版本有了很大的改进。

穗
钉鞋呢?我见过一些有尖峰的分布,粗面上的尖峰没有真正细的粒子伤害大。

这个测试对味道没有任何估计,但我们可以粗略地假设,没有坏 EY 的高 EY 会导致更好的味道。这还没有考虑到所用的浓缩咖啡机或任何其他方法。
我不确定这是查看研磨分布的最佳方式,还是模型过于简单。然而,我认为这是朝着正确方向迈出的一步,有助于回答这样一个问题,研磨者之间如何排名?
下一步是将该模型应用于可用的研磨机分布曲线,以进行比较和对比。
如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。也可以关注我中。
我的进一步阅读:
建模预期目标
用逻辑回归预测射击成绩

作者图片
在第一部分中,我们基于三个关键变量深入探究了镜头的数据和趋势;距离,角度和一个分类变量来识别头部射击。通过对数据的表示、转换和可视化,我们了解了与射门和进球相关的分布和概率。在这里,我们使用这些数据开发一个模型来预测目标。
分类
由于我们的响应变量(射击结果)是分类的,我们必须应用分类方法来创建一个预测模型。为了介绍这种方法,让我们看一个说明性的例子。假设我们从我们的数据中随机选择一些镜头,并根据它们的结果对它们进行分类,只考虑距离。

图一。作者图片
从上面我们可以看到蓝色和橙色星团之间有明显的区别。如果我们假设这是射击和距离之间关系的真实性质,并且我们想要基于该数据预测未来的射击结果,我们可以采用什么模型?
由于数据很容易分离,我们可以在两个集群之间画一条边界线。这条线将代表一个判别函数,我们将使用它来分类每一个射门结果是进球还是失误。在上面的例子中,判别函数映射出距离目标中心的距离 r。
这个以绿色绘制的判别函数是由𝑦=αx+β方程定义的,其中 x 是与目标的距离,y 是二元响应。具体针对上面的数据,y = x -12。我们可以通过将所谓的 Heaviside 函数应用于我们的判别式来将其转化为分类模型,该判别式将返回值 0 或 1,因为响应变量基于 y 的值:

Heaviside 函数是一个硬分类器,也是最简单的分类模型,但它要求数据是完全可分的。


图 2 和图 3。作者提供的图片
事实上,描述投篮的数据是不可分的,这使得进球很难准确预测。然而,亥维赛函数是进入更高级分类模型的自然进展。
如果我们取一个真实的数据样本,它可能类似于下图。

图 4。作者图片
在数据不能完全分离的情况下,我们必须对概率建模,而不是指定硬 0 和 1。我们在数据探索中已经看到,距离和角度变量的概率有明显的趋势。我们应该采用什么函数来模拟这些趋势?有许多函数可以绘制概率并拟合不可分的数据,但我们使用逻辑函数(也称为 sigmoid 函数)是因为它简单。

与 Heaviside 函数类似,逻辑函数接受我们的预测值(在这种情况下是距离),但输出 0 到 1 之间的值。

图 5: 逻辑函数取域中的任意值(∞,+∞)并产生范围(0,1)内的值。因此,给定一个值𝑦,我们可以将 G(y)解释为射门得分的条件概率,𝐺(𝑦)≡Pr[label 就是 1|𝑦].作者图片
逻辑函数是一条 S 形曲线,其斜率和轨迹根据系数值而变化。现在的问题是,我们如何使用逻辑函数来模拟我们的拍摄数据?嗯,对于我们使用的每个预测变量,我们优化相应的系数(α,β等。)来最好地拟合数据。我们优化什么?被称为对数似然法。最大化对数似然的过程超出了这里的范围,但是如果你对这个过程感兴趣,这里是一个很好的起点。对于节奏较慢的视觉演示,stat quest做得很好。
xG 模型
我们的目标是创建一个模型,尽可能准确地描述我们现有的数据,并最终预测未来的事件。在对整个数据集应用逻辑回归之前,我们必须将数据分成训练集和测试集。训练集服务于我们构建模型所依据的数据,而测试集是我们用来评估模型性能的数据。将训练数据拟合到逻辑函数将为我们的预测器产生系数(我将通过使用 Python 中的 Scikit-learn 库来完成)。如果我们仅从拟合距离变量开始,我们应该得到描述训练数据的最佳参数:

现在,如果我们将这个函数映射到图 4 中的样本数据上:

图 6。作者图片
虽然上面的图表证明了我们的系数产生了合理的拟合,但从图形上看,这种拟合有多好还不是很清楚,尤其是考虑到这只是我们训练数据的一个小样本。让我们来看看另一个图形,而不是用数字来衡量拟合优度。正如我们在第一部分中所构建的,我们可以按距离对数据进行分类,计算每个分类中导致进球的射门比率,然后将分类散点图绘制到图形上。如果我们现在将逻辑模型叠加到散点图上,我们可以看到函数对数据的映射有多好。

图 7: 请注意,我们选择的仓数量将影响数据在该图中的轨迹。图片作者
请记住,我们的模型并不符合图 6 和图 7 中的点,而是符合我们训练集中的 32,000 个镜头。这些图表的目的是衡量我们的模型在哪些地方表现良好,在哪些地方表现不佳。在图上绘制 32,000 个点对于可视化来说不是很好,所以我们决定绘制一个人口的样本表示。
我们可以从图 7 中看到,该模型对大于 6 米的数据预测良好,但它低估了近距离进球的概率。这是图形化方法将提供的一种优势。我们可以通过在逻辑函数中加入二次项来更好地预测更接近球门的射门。也就是说,


图 8。作者图片
这是一个进步!如果我们为距离变量添加一个二次项,我们在预测接近球门的射门方面会做得更好。数字评估不能给我们这种奢侈。这种分析更多的是一种艺术,而不是科学,所以尝试不同的选择是很重要的。当然,这仅仅是一种评估我们模型的特别方法,目的是用图形表示模型与数据的对比情况。我们将很快探索一种更具体的方法来评估模型的准确性,但让我们首先将角度预测器添加到组合中。我们将这两个变量与数据进行拟合,并通过等高线图将概率绘制到音高上:

图 9。作者图片
这代表了最简单的预期目标模型。如果我们翻回到第一部分,比较二维密度图和 xG 模型,我们看到我们的双参数模型在合理的程度上匹配概率分布。现在让我们仔细看看等高线图:

图 10。作者图片
这就是我们需要调用一些足球知识的地方。虽然中心位置的值的概率类似于第一部分的密度图,但是对于球门线附近的小角度位置来说,它们太大了。从 5 米开外射门并有效地与球门成零度角,这看起来合理吗?不要介意有 15%的机会!这是这个简化模型的一个明显缺陷。然而,这确实是一个尝试不同可能性和变量的机会。我们可以选择添加多项式项和相互作用项(如δ距离角度)或一起添加另一个变量。我尝试了许多选项,发现增加一个“到中心的距离”的螺距变量可以产生最合理的等高线图:

图 11。作者图片
增加这个新的变量减少了从靠近球门的小角度得分的机会,但对更远的距离几乎没有作用。这可能是因为从这些位置拍摄的镜头很少,而且在记录的镜头中,有些可能是错过的传中,导致了不太可能的进球。球员很少从这些宽阔的低角度位置投篮。如果我们有 10 个赛季的数据,我们会看到模型开始低估这些类型的镜头。我们也可以选择在球门线上以失误的形式添加人工数据来修复这个缺陷,但现在我们选择忍受它。请记住,虽然我已经尝试添加多项式因子和这个新变量来修复这个缺陷,但逻辑回归中的优化仍然会尽可能地适合训练数据。也就是说,尽管我想告诉模型低估这些类型的镜头,逻辑回归只知道如何最大化可能性。我只能给模型增加更多的灵活性,但我不能告诉它如何以及在哪里弯曲。
虽然我们已经可视化了这些模型,但我们仍然没有任何具体的东西来说明它们在预测未来数据方面的表现。如果我们还想添加更多的变量,比如分类标题变量,可视化将变得更加麻烦和困难。为此,我们需要进行一些统计分析。
模型评估
如果我们想评估我们的模型的准确性,我们必须测试它对未来事件的预测能力。但这引发了另一个担忧。我们如何使用新的预期目标模型对击球进行分类?与给出硬分类的 Heaviside 函数不同,逻辑回归模型返回射门导致进球的概率。
为了进行分类,我们必须定义一个阈值。这个阈值实质上分割了逻辑函数,为高于阈值的模型和低于阈值的模型分配目标。例如:

图 12。作者图片
在阈值为 0.3 的情况下,逻辑模型产生了四种可能的分类,可以在混淆矩阵中进行总结。

图 13。作者图片
从混淆矩阵中,我们可以收集模型对哪些分类给出了有希望的预测,对哪些没有。对于上面的例子,模型在预测失误方面做得很好,但在预测目标方面做得很差,如果我们检查阈值与模型的交叉点,这是可以理解的。我们可以用一个称为敏感度的指标来定义模型正确预测目标的能力:

模型正确预测缺失的能力由特异性给出。

对于我们上面检查的阈值,该模型将对整个测试数据产生以下混淆矩阵:

敏感性=19.6%特异性= 97.4%。作者图片
那么如果我们选择一个低得多的阈值呢?


敏感性= 96.7 %特异性= 19.0 %。图片作者
现在我们有了相反的效果。逻辑回归模型对目标进行了更好的分类,但对失误进行了大量的错误分类。如果我们分开差值并在前两者之间选择一个阈值:


敏感性= 77.8 %特异性= 61.0 %。作者图片
我们现在可以更平衡地预测目标和失误。我们已经看到,三个不同的阈值从我们的模型中产生了非常不同的预测。它们的不同之处在于它们的特异性和敏感性;也就是他们正确预测目标和失误的能力。这是一种权衡。那么,我们选择什么门槛呢?嗯,他们两个都不是,所有人都是。
让我们后退一步。如果我们使用逻辑回归模型来识别患者是否患有癌症,那么我们将采用高阈值,放弃高特异性率以支持高敏感性。我们宁愿给出假阳性结果,也不愿给出假阴性结果。我们不希望有人以为自己通过了癌症筛查,而实际上却患有癌症。当我们试图对目标概率建模时,我们没有这样的偏好。事实上,一开始就试图直接预测目标并不那么有用。正如我们将在下一部分看到的,预期目标模型的力量不在于对一次射门做出预测。那么研究阈值和混淆矩阵的目的是什么呢?我们可以用它们来比较不同的模型,使用一种叫做受试者操作者特征(ROC)图的东西。

作者图片
使用较小的步长,ROC 曲线绘制了模型正确预测目标的能力与其在不同阈值下错误预测失误的能力。当你沿着 y 轴向上移动时,模型更好地预测目标,当我们沿着 x 轴向左移动时,模型更好地预测失误。它本质上映射了预测目标和预测失误之间的权衡。虚线表示没有预测能力并且基本上无用的模型,因为对于每个正确的分类,它也预测不正确的分类。因此,我们的 ROC 曲线离 45 度线越远,它对测试数据分类的总体效果就越好。另一种方式是,曲线下的面积越大,我们的模型在描述测试数据方面就越好。这对我们很有用,因为我们可以用它来比较不同的模型,看看向我们的模型添加更多的变量是否有任何实质性的优势。

作者图片
现在我们在评估我们的模型时有了一些具体的东西。如果我们仔细观察,以距离和角度作为输入变量的模型与添加了“到中心的距离”参数的相同模型产生相同的曲线下面积(AUC)。因此,与我们之前所做的一些假设相反,“到中心的距离”预测值并没有给我们的模型增加多少性能,我们应该将其排除在外。虽然“到中心的距离”参数显示模型在接近球门线的区域有轻微的变化,但是这种变化的意义很小,因为从这些位置拍摄的镜头很少。AUC 因此告诉我们,将中心添加到距离变量是相当无用的。请注意,我们可以轻松地使用 p 值来了解中心到距离参数是否对我们的模型有用。
ROC 曲线的另一个优点是,如果我们选择使用其他分类技术,如 SVM、随机森林、神经网络等,我们可以将这些模型的性能与我们在这里创建的模型进行比较。我认为这对于手头的问题来说有些矫枉过正,但却是进一步探索的一个途径。虽然这些替代模型会产生类似的结果,但逻辑回归为我们提供了一种相当简单和易于理解的方法来描述镜头结果,而这些其他方法需要对机器学习技术有更深入和更复杂的理解。
在我们结束之前,我想简单地谈谈 R 平方值。在线性回归中,R 平方测量线性模型解释的响应变量变化的百分比。R 平方为零表示模型不能解释响应中的任何变化,R 平方为 1 表示模型能够完美地描述数据。R 平方不直接转化为分类和逻辑回归,但有许多模拟线性回归版本的伪 R 平方值。最流行的,也是我使用的,是 Mcfadden 的 R-squared,它衡量零模型(本质上只是一个截距值)相对于我们的模型的对数似然性。我们可以用类似于线性回归 R 平方的方式来解释它。接近 1 的值表示对数据进行完美分类的模型(例如我们用来引入 Heaviside 函数的示例)。对于我们基于 AUC(使用距离、角度和标题变量)的最佳模型,Mcfadden 的 R 平方约为 0.171。现在在你抛出 xG 模型之前,我们必须明白,什么值代表好模型,什么值代表坏模型,并没有确定的答案。正如我们前面所讨论的,目标大多是随机的。我们需要记住,每一个镜头都是独特的,由数百个不同的变量组成,我们试图只用三个变量来建模。因此,我们不应该期望确定地或接近确定地预测目标。这部分是由于我们无法模拟所有这些变量。考虑到虽然距离和角度使我们对射门得分的可能性有很好的感觉,但我们没有考虑守门员的位置、射门时是用弱脚还是强脚、接触点的射门高度、比赛状态、主场优势、球门和射门之间是否有许多人等等。这些只是少数有影响的可量化变量。还有一些不容易量化的变量:球场的状态如何(是崎岖不平还是地毯),疲劳是否在起作用,士气和信心,以及其他软因素。即使忽略了这些其他变量,也有一些内在的随机性。我们试图用一个简单的 3 参数模型来模拟一个非常复杂的情况;我们不应该期望如此高的 R 平方值。尽管如此,xG 并非一无是处,事实上它对我们玩游戏的方式来说是革命性的。
在下一个也是最后一个部分,我们将探索预期目标模型的应用,它的优点,缺点,外推的危险,并希望证明预期目标是值得的。在那之前。
对于所有用于建模和绘制这些发现的代码,请参考我的 github 。
使用政府数据模拟医疗保险欺诈
医疗保险/医疗补助系统中的数据挖掘在 2013 年成为合法。当局如何利用这些数据来抓坏人?

在美国,医疗保险是主要提供给 65 岁以上老年人的国家健康保险计划。这个项目很昂贵,部分原因是美国人口老龄化和医疗费用飞涨。其资金来源于政府总收入(43%)、工资税(36%)和受益人保险费(15%)。医疗保险计划有三个主要的部分:
- Medicare A 部分:主要承保住院和临终关怀。
- 联邦医疗保险 B 部分:主要承保医院门诊服务,以及住院期间由医护人员开出的处方。
- 医疗保险 C 部分 : (又名医疗保险优势)是由政府补贴的私人计划,需要提供与 A 部分和 B 部分相同的保险范围,有时由私人健康保险公司管理。
对于这个项目,我们还研究了医疗保险计划的一个方面,通常在医疗保险 B 部分下计费,称为耐用医疗设备、假肢、矫形器和用品(DMEPOS)。
特征数据
对于我们的功能,我们将使用 D 部分、B 部分、DMEPOS 的数据。这些数据集由医疗保险和医疗补助服务中心( CMS )每年发布一次,由特定的信息组成,这些信息被组织在一个唯一的 id 上,称为国家医疗服务提供者标识符(NPI) 。这三个数据集的信息各不相同,但包括以下特征:提交的费用、利用率、平均付款、提供商性别、平均提交的费用等等。
本文是对该项目的高级概述,所以我不会深入讨论数据处理细节。如果你对尝试复制这些结果感兴趣(或者如果你是一个未来的读者,想要合并在撰写本文时不可用的数据),请查看这个项目的 GitHub repo。我将在下面列出每个数据集的直接链接:
目标/标签数据
如前所述,上述所有数据集都可以加入 NPI,这是参与 Medicare 计划的每个提供者的唯一标识符。我们如何知道哪些 NPI 代表欺诈者?
幸运的是,监察长办公室公布了一份因各种原因(包括欺诈)被排除在医疗保险计划之外的供应商名单。).该清单被称为被排除的个人/实体清单(LEIE ),可在此处下载。该列表由 NPI 组织,包括排除日期、恢复日期和排除类型等信息。排除的类型可以在图表中找到。任何在被排除前一年活跃的医生都被认为是骗子。这段时间是他们被抓之前的时间,这意味着这段时间的可用数据将揭示哪些欺诈活动导致这些医疗服务提供商被抓。
组合数据集
除了 B 部分、D 部分和 DMEPOS 数据集。创建了第四个数据集,它结合了前面提到的每个数据集的要素,并在 NPI 上连接在一起。这 4 个数据集都分别进行了建模和评估,以了解哪一个表现最好。
模型
对于这个项目,我使用了两个模型来了解特性的重要性和一般性能。首先,我使用逻辑回归对所有 4 个数据集进行建模。

每个数据集的标准化混淆矩阵。
正如你从上图中看到的,这可能是一个需要良好判断力的情况。你认为以上哪一款最好?假设我们为监察长办公室工作,我们将使用这个模型来决定调查谁。对我来说,这是一个信息丰富的视觉效果,可以大致了解每个模型在不同数据上的表现。如果你遵循 DMEPOS 模型的预测,你会比其他模型抓到更多的诈骗犯。你也会浪费更多的时间和资源去调查无辜的医生。在做出任何最终决定之前,也看看 AUC 可能是一个好主意。

现在我们可以看到,在组合数据上训练的模型有 79%的机会能够区分FRAUD和NOT FRAUD类。虽然这接近于 DMEPOS 和 B 部分数据集的 AUC (.78),但使用这一指标,组合数据集显然是赢家。
特征重要性(逻辑回归)
当挑选出这些欺诈的医生时,模型认为什么是重要的特征?我们可以使用 eli5 包来显示在组合数据集上训练的模型的特征重要性。

有趣的是,提供者是否是男性似乎是他们是否参与欺诈活动的最佳预测者。我将在下面列出其他主要功能:
- total _ claim _ count _ max= Medicare D 部分索赔的最大次数,包括再配药
- total _ 30 _ Day _ fill _ count _ STD= 30 天填充次数的标准偏差,包括再填充
- bene _ unique _ count _ mean=唯一医疗保险受益人的平均人数
我们可以从这些结果中推断出,大多数退休的医生正因为过度开药而陷入困境。让我们来看看我使用的第二个模型的信息,一个随机森林。

随机森林模型的归一化混淆矩阵。
从这些混淆矩阵来看,在组合数据集上训练的模型似乎比任何其他模型都更容易将欺诈医生错误分类为合法医生。在 D 部分数据集上训练的模型稍微偏向于预测欺诈,这可能导致浪费资源调查合法的医生。让我们看看这些模型的 AUC。

现在我们可以看到,在组合数据上训练的模型有 85%的机会能够区分FRAUD和NOT FRAUD类别。
要素重要性(随机森林)

类似于逻辑回归,随机森林模型对医生的性别给予了很大的重视。此外,下面列出了确定欺诈的最重要特征的定义。
- line _ srvc _ CNT _ Mean/line _ srvc _ CNT _ STD=所提供服务数量的平均值/标准差。
- bene _ unique _ CNT _ mean/bene _ unique _ count _ Max=唯一医疗保险受益人的平均/最大数量。
- total _ 30 _ Day _ fill _ count _ median= 30 天填充次数的中位数,包括再填充
如果您查看逻辑回归模型中的要素重要性,并将它们与随机森林模型进行比较,您会发现逻辑回归模型似乎对其顶部要素的权重更大,而随机森林模型似乎更均匀地分布权重。
结论
在未来,我计划花更多的时间尝试在不同的模型上训练这些数据。我鼓励你也这样做!我相信随着时间的推移和更多数据的收集,国家打击欺诈的准确性只会提高。联邦法规只是在 2013 年才开始允许对医疗补助数据进行数据挖掘,就像该项目中描述的那样。随着每年收集的数据越来越多,我预计这些模型的预测能力会大幅提高——这有望让这些坏人在滥用系统之前三思而行。
来源
用于执行数据预处理的方法在本文中有所描述,这是本项目的基础。
请随意查看我的网站💻
电子商务中产品搜索相关性建模:家得宝案例研究
实践教程
利用机器学习预测 homedepot.com 搜索结果的相关性

照片由来自 Pexels 的 Anete Lusina 拍摄
目录
- 简介 摘要
问题陈述
商业问题的 ML 公式化
数据概述
ML 问题的目标和指标
现实世界的约束 - 文献综述
- 探索性数据分析:第 1 部分 ·相关性得分
·Product _ uid
·属性
·描述
·合并产品文本数据
·填充空值 - 数据清理
基本预处理
标准化单元
纠正搜索中的错别字
进一步预处理 - 探索性数据分析:第二部分 【基本文本统计】
【单词和双词分布】 - 特征工程 ·集合论特征
·基于 VSM 的特征
·概率特征
·使用 Word2Vec 的查询扩展 - 特性集的性能图
- 最终建模和评估 【堆叠回归器】
基础模型
元模型
ka ggle 上的性能(前 10%) - 扩展成现实世界的搜索引擎
- 全管道
- 最终测试和结果 测试
运行时间
网络应用程序演示 - 结论和未来工作
- 致谢
- 参考文献
介绍
摘要
一切都在向数字化转变,购物体验也是如此。电子商务在过去几年中发展迅速,因此,在线产品搜索已成为为客户提供满意购物体验的最重要因素之一。在这篇博客中,我提出了一种预测给定搜索查询所需产品的可靠方法,使用了涉及机器学习、自然语言处理和信息检索的技术。
问题陈述
这项任务很容易理解。对于客户输入的任何搜索查询,我需要找到最相关的产品,并按照相关性的顺序显示给用户。从商业的角度来看,有几点需要考虑。首先,需要对产品进行排序,因此,即使在最相关的产品中,我们也需要能够分辨出哪一个更相关。第二,有一个时间限制,即结果需要在几秒钟内显示。
商业问题的机器学习公式
该任务可以表述如下:给定一个搜索和一个产品,找出它们之间的相关性分数,即该产品与手边的搜索查询有多相关。假设我的机器已经学会了如何预测(搜索-查询,产品)对的相关性分数。现在,对于用户输入的任何搜索,我可以计算该搜索与数据库中所有产品匹配的相关性分数,并向客户显示(比方说)前 10 个结果。
因此,如果我有很多带标签的数据,即很多(搜索-查询,产品)对及其相关性分数,那么我可以将此作为监督 ML 问题。这正是我在这个案例研究中所做的。我使用的数据是家得宝为 Kaggle 比赛提供的家得宝产品搜索相关性
现在,在现实世界的电子商务搜索引擎中,计算给定搜索的每个产品的相关性分数是不可能的,因为,在任何典型的电子商务网站中,产品的数量都非常大,因此计算成本很高,非常耗时。
✦因此,首先,我们使用一个允许快速查询评估的更简单的检索模型来检索一些候选产品。在第二阶段,使用更精确但计算量大的机器学习模型对这些产品进行重新排序。
为了解释这一点,假设我们有一组 100,000 个产品。而搜索查询是“太阳能灯”。第一个更简单的检索模型将检索几个候选产品。这个模型可以简单到像搜索单词和产品文本之间的 AND 操作符。所以在这里,一个由 AND operator 管理的模型将得到所有带有“solar”和“lamp”字样的产品。假设它检索了大约 500 种产品。现在,在这 500 种产品的基础上,我们可以运行复杂的机器学习算法,并计算每个(搜索,产品)对的相关性分数,其中我们的搜索词“太阳能灯”保持不变,而产品有所不同。现在,我们根据它们的相关性分数对它们重新排序,并向用户显示排名靠前的产品。这叫做学习排名 (LTOR)。在这篇博客中,重点将放在系统的学习排名部分。最后,我还解释了我是如何扩展它,使之成为一个成熟的搜索引擎的。
数据概述
数据是四个 CSV 文件的形式;train.csv,test.csv,attributes.csv,product_description.csv,你可以从这里下载。
训练数据:我们在 train.csv 文件中总共有 74067 行用于训练。一部分数据如下所示,每行对应一对

train.csv 文件
(search_term,product_title)以及说明该产品与搜索相关程度的相关性分数。相关性分数是一个从 1 到 3 的实数(1 表示不相关,3 表示完全匹配)。对于每个产品,我们通常有多个行,即多个搜索查询及其相关性得分。product_uid 是每个产品的唯一标识符。
product_description.csv 包含每个产品的文本描述。attributes.csv 文件包含一些关于产品子集的附加信息。
搜索查询是字符串形式的,产品由文本数据表示,这些文本数据以标题、描述和一些属性的形式提供给我们。在现实世界中,我们还可能有产品的其他特征,如图像、评级等。但是这里我们只有文本可以使用。
数据的相关性分数是手动标记的。每一对(搜索、产品)由至少三名人类评价者进行评估,然后将最终的相关性分数作为平均值。
测试数据:由于这是 Kaggle 竞赛的一部分,我们还得到一个 test.csv 文件,其中包含大约 166,000 行,我们需要预测这些行的相关性分数。训练集中大约一半的产品出现在测试集中。这两组的文氏图如下所示。

ML 问题的目标和度量
目标:对于每一个给定的(搜索-查询,产品),预测相关性得分。这将被视为一个回归问题,因为我们可以更容易地对产品进行排序。
使用的度量: 比赛中给出的这个度量是均方根误差(RMSE) 。这是回归问题中最广泛使用的度量标准,在大多数情况下效果很好。它不同于 MAE(平均绝对误差),因为它更多地惩罚大误差。RMSE 的一个问题是它的价值很难解释;它的范围可以从零到无穷大。因此,一般来说,基线得分是使用一个简单的回归模型建立的,然后我们可以使用它作为其他模型的参考。

来源:https://S3-AP-south-1 . Amazon AWS . com/av-blog-media/WP-content/uploads/2018/05/RMSE . png
现实世界的约束
- 延迟限制:产品需要在几秒钟内展示。
- 产品排名是必要的。
- 可解释性是部分重要的:我们想知道为什么我们的模型向客户建议了一个特定的产品。
注: 在博客中,我曾交替使用过‘搜索查询’,‘查询’,‘搜索词’和‘搜索’这几个词。
文献评论
眼前的问题是搜索相关性,它大致属于信息检索领域。关于自由文本查询技术的大量研究已经发表。
传统的非机器学习模型集中于探索各种建模语言的方法。像布尔模型和模糊检索这样的集合论模型是基于表示查询并记录为单词集,然后应用一些基本的集合运算来获得相关性。像 VSM 这样的代数模型将文档和查询表示为向量(例如单词袋),然后将它们之间的相似度计算为标量。更受欢迎的模型如 Okapi BM25 、 Indri 和语言模型都是基于概率论。尽管所有这些模型都来自不同的底层理论,但它们中的大多数都使用了来自自然语言处理领域的非常相似的概念,如单词袋、余弦相似度等。
最近的方法包括学习排序,其中通常查询-文档对由其特征向量表示,然后在其上训练监督或半监督机器学习模型以获得排序。这里,通常直接使用传统的检索模型作为特征。 LSA (或 LSI)将术语-文档矩阵映射到一个“概念”空间,从而得到一个密集且更好的文本表示。 Word2Vec 算法使用神经网络从大型文本语料库中学习单词关联,并将单词映射到高维向量,这些向量可以用作特征。查询扩展是用语料库中存在的相似单词扩展查询的另一个有效想法。为了测量相似性,通常使用来自 Word2Vec 模型的单词向量。
探索性数据分析:第 1 部分
在这一节中,我将对手头的数据进行分析。我已经将 train.csv 文件保存为 train_df 变量中的数据帧。

火车 _df
id 列没有用。product_uid 是每个产品的唯一 id,在合并来自其他文本文件的数据时非常重要。训练数据中没有空值。
相关性分数
发现相关性分数本质上是分类的。这是因为大部分的分数是 3 个评分者的平均分数,每个评分者只能给出 1 或 2 或 3 的整数分。

大多数产品具有高相关性分数,即大多数产品与查询相关。由于相关性分数的分类性质,如果分类模型有助于最终排名,也可以在此基础上建立分类模型。得分{1.25,1.5,1.75,2.25,2.5,2.75}出现的频率非常低。
产品 _uid
许多产品在训练数据中出现多次,也就是说,我们有多个搜索术语和相同产品的相应相关性。

- 超过 10,000 种产品多次出现
- 74067 种独特产品的总数是 54667 种
- 产品出现的最大次数是 21 次
属性
对于产品,我们有产品标题、产品描述和属性形式的文本信息。产品标题在 train.csv 文件中。描述和属性分别从 product_description.csv 和 attributes.csv 文件中提取。
attributes.csv 文件的一个实例如下所示。字段“name”表示属性的名称,“value”表示属性文本。

属性. csv
attributes.csv 文件中的每个产品都有多个属性。产品的最小属性数为 5,最大属性数为 88。

属性数量与产品指数
但是在 attributes.csv 文件中并没有找到 train.csv 文件中的所有产品。总共 54667 个产品中有 16263 个没有属性。

train.csv 和 attributes.csv 文件中的产品集
然后,我研究了最常出现的属性,在 attributes.csv 文件的 86,250 个实例中,Brand 是最常见的属性。

最常见的属性
不仅如此,我们还观察到,大多数情况下,product_title 包含品牌信息。因此,我在我的 train_df 中添加了 brand 作为一个单独的特性。
描述
product_description.csv 包含产品的描述。与属性不同,我们在 train.csv 文件中有对所有产品的描述。
合并属性、描述和品牌特征
最后,我用 train_df 合并了所有属性、描述和品牌。产品的所有属性首先被连接起来,然后存储为“组合属性”特征。

合并后的 train_df
注意:从现在开始,代表产品的文本字段,即产品标题、品牌、组合属性和产品描述,统称为“产品-文本”或“文档-字段”或“文档”。
填充空值
在开始清理文本之前,我发现我们的 train_df 有很多空值。这些是在我合并属性时发生的,因为训练数据中的许多产品没有属性。

我们可以看到,空值只出现在 brand 和 combined_attr 特性中。有大量的空值,因此正确填充它们是清理过程中的重要一步。
品牌:据观察,产品的品牌一般出现在标题的前几个字里。例如,产品名称为“Simpson Strong-Tie 12-Gauge Angle”的产品的品牌是“Simpson Strong-Tie”。“Delta Vero 1 手柄淋浴专用水龙头”是“Delta”。此外,99%的品牌长度不超过四个单词。所以大多数情况下,品牌应该出现在标题的前四个字里。
所以,我首先将所有唯一的品牌存储在一个列表“unique_brands”中。现在,对于任何品牌为空的行,我检查该产品标题的前四个单词是否出现在 unique_brands 列表中。如果没有,那么我检查前三个单词,以此类推,直到第一个单词。这种方法非常有效,我能够填充 15003 个无效品牌。对于剩下的 2625 个值,我用标题的第一个字填充。
描述:为描述填充空值并不困难,因为我们发现描述文本与组合属性文本非常相似。因此,combined_attr 字段的所有空值都用相应的 product_description 值填充。
数据清理
这是整个案例研究中最重要的部分之一。大量的时间花在了观察文本和进行必要的预处理步骤以使其适合建模上。使用了许多自然语言处理技术。
为什么文本预处理在这个项目中如此重要,一个非常简单的动机是因为我们在某种程度上试图找到搜索查询和产品文本之间的相似性。考虑搜索为“5 加仑桶”,产品名称为“Leaklite 5 加仑黑桶”。如果我们使用一个像普通单词数这样的衡量标准,那么在这种情况下,相似度将为零,因为没有共同的单词。现在说一点预处理,我们的搜索和标题分别成为“5 加仑桶”和“leaklite 5 加仑黑桶”。现在我们的相似性得分为 3,因为现在我们有 3 个共同的单词。因此,很少的预处理就能产生巨大的差异。
基本预处理
基本的预处理步骤包括小写、删除特殊字符、删除停用词和词干。
标准化单位
- 许多产品名称包含产品的尺寸。一种非常常见的表示方式是“8 英尺”。x 5 英尺。x 8 英尺。”。在产品描述中也可以观察到同样的情况。
- 在 product_title 中,单位通常表示为{in。磅。sq。磅。奥兹。加仑 mph},但是在 product_description 和 search_term 中,单位以多种方式表示。例如,英寸等于英寸。英寸}和加仑{gal。加仑}。因此,这些单元需要标准化。
- 在搜索词中,指定测量值时有一些常见错误。例如“4 个架子”、“9x12”、“g135”、“5 加仑”、“1/2 英寸乘 12 英寸”。因此,需要把数字和单词分开。
预处理步骤:
- 将数字和单词分开:“9x 12”→“9x 12”,“5 加仑”→“5 加仑”…
- 将所有测量单位统一表示。对女孩来说。gals gallon}全部转换为“加仑”。类似地,{ ft fts feets foot 也转换为“英尺”。
纠正搜索词中的拼写错误
在浏览搜索词时,发现它包含许多拼写错误,如“割草机”被拼写为“mowe”,“厕所”被拼写为“toiled”等。这是可以理解的,因为这些搜索是由用户输入的,在在线搜索中,这样的拼写错误是常见的。但这是一个大问题,因为拼写错误的单词可能会使常用单词计数等功能变得非常无效。因此,为了纠正这一点,我使用了一个自定义的拼写纠正。结果非常积极。

不正确的搜索→正确的搜索
我使用的拼写校正器是由彼得·诺威格建造的。它基于概率理论,但是非常容易理解并且非常有效。最棒的是,你可以根据自己的数据进行调整。这非常重要,因为通用拼写校正器可能无法识别仅限于我们数据的品牌名称。我鼓励你参考这个链接并阅读更多相关内容。
我更正了所有的搜索词,出于实验目的,我还保留了原始/未更正的搜索。
进一步预处理
在基本的预处理中,我留下了停用词移除和词干部分,因为我想先修复不正确的搜索词。在这里,这两个步骤都被执行,清理后的文本被存储在 cleaned_df 中。
我们最终清理后的数据帧如下所示

已清理 _df
我还创建了一个 dataframe cleaned_df2,在那里存储没有词干的文本数据。这样做是为了以后能够使用像 Word2Vec 这样预先训练好的编码。
探索性数据分析:第 2 部分
清理之后,我能够从文本数据分析中做出更准确的发现。已经做了很多分析,我将在这一部分回顾最重要的发现/结论。
为了便于分析,我将相关性分数分为两类:分数> 2 的“高”和分数≤ 2 的“低”。它们也分别被称为正类和负类。虽然,这是一个回归问题,但是把分数分成两类在分析中帮助很大;一个有助于区分高等级和低等级的特征在最终的回归问题中也会很有帮助。
我们总共有 6 个文本字段,对每个字段分别进行了分析。
注意: 经过仔细检查,我发现描述和属性文本非常相似,因此,我没有在任何进一步的分析中考虑属性文本。
基本文本统计
对文本的一些基本数据进行了分析。即文本串的长度、字数和平均单词长度。对于 corrected_search,这些统计信息的分布如下所示。

基本统计分布
观察:
- 大多数搜索查询的字数在 2 到 5 个词之间。
- 这些统计数据的分布对于正类和负类都非常相似。
- 上面提到的最重要的统计数据是字数。

请注意,当我们向右移动时,正类与负类的高度/密度比率开始下降,或者我们可以说负类开始变得更加突出。这意味着随着字数的增加,相关性高的概率降低。因此,字数给出了关于相关性分数的一些信息。
4.通过观察字数与相关性的相关性,可以证明上述说法是正确的。有一点相关性,本质上是负面的。

5.所以字数统计可能不是一个坏的特性。
6.字符数和平均单词长度与相关性的相关性
分数非常低,分别为-0.092 和 0.084,因此可能没有帮助。
对所有这些统计数据进行了标题、描述和品牌文本分析。发现了同样的模式。唯一与相关性有显著相关性的统计数据是字数。
除此之外,我还试验了另外两个统计数据:可读性指数和情感分数。两者的相关分数分别为-0.035 和 0.019,非常低。他们没有帮助区分这两个阶级。
最常见的单字和双字
搜索文本:corrected _ Search 最常见的单字如下:

inch 和 x 是最常用的术语。还有,很多数字是非常频繁出现的。这意味着大量的搜索词包含了所需产品的维度。这些数字和单位很重要,因为它们可以很好地表明用户想要什么样的产品。因此,我没有删除数据清理时的数字,并将保留它们作为最终的特征部分。

每类最常见的单字
然而,这两个类别中最常见的单词非常相似。在左侧,您可以看到每个类别中搜索最频繁的单词及其频率。
甚至两个阶层最常见的二元模型也非常相似。

因此,从上面的观察中,对于搜索文本,我们看到两个类中最常见的单词和双词非常相似。因此,像 simple BOW 或 TF-IDF BOW 这样的矢量化方法可能效果不太好。
描述文字:即使在描述文字中,也发现最常见的词是 inch,数字比较突出。

两个类别中最常见的单字和双字被发现非常相似。

对品牌和标题文本进行了同样的分析,发现了类似的趋势。文本字段本身并没有给出很多关于相关性分数的信息。
来自 EDA2 的结论
- 在所有关于文本的统计数据中,字数统计是区分这两类数据最有效的方法。我们无法从其他统计数据中获得很多信息,如平均单词长度和字符串长度,这两个数据显示的相关值非常低。因此,在最后的特征化,我将只包括字数统计功能。
- 对于这两个类,所有文本字段(搜索、标题、描述、品牌)的 unigrams 和 bigrams 分布非常相似。因此,像 BOW 和 TF-IDF BOW 这样的简单矢量化方法可能无法单独使用。
特征工程
这个问题的特征主要来源于信息检索领域。注意,这里的特征需要是搜索和产品组合的任意函数,因为我们试图找到(搜索,产品)对的相关性。一些基本的检索模型被直接用作特征。我将这些特性分为四组:
- 集合 1:集合论特征
- 集合 2:基于向量空间模型(VSM)的特征
- 集合 3:概率特征
- 集合 4:使用 Word2Vec 进行查询扩展
特征集 1:集合论特征
这些特征来源于简单的集合论检索模型,如布尔模型、模糊检索等。其基于将查询和文档表示为单词集,然后对它们应用某种操作来获得相关性分数。这些功能包括:
➤搜索和文档字段(标题、品牌、描述)之间的常用词计数
搜索和文档字段之间的➤余弦系数和雅克卡系数

➤搜索长度和文档字段
➤搜索中的最后一个单词是否在文档字段中
最后一个特征很直观,因为在多词搜索查询中,最初的词通常描述了你需要的产品的属性,但最后一个词代表了产品的名词。就像在“黑色夹克”中,第一个词描述了夹克的颜色,但最后一个词是你需要的产品的名词。
以上特征是为 corrected_search 和 raw_search 创建的。

corrected_search 和 raw_search 的比较
- 可以观察到,在相当多的行中,num-common-words 特征更多地用于 corrected_search 而不是 raw_search。因此,更正搜索词确实有所帮助。
功能集 1 上的 EDA
这里总共创建了 29 个特征。下面给出了一些的 PDF(概率密度函数)图。
注:以“ST”为后缀的特征表示校正搜索与标题之间的特征,“SB”表示品牌,“SD”表示描述。对于 raw_search,我使用了“r_ST”形式的后缀。例如,“余弦 _ST”表示校正搜索和标题之间的余弦系数,而“余弦 _r_ST”表示原始搜索和标题之间的余弦系数。

集合 1 特征的 PDF 图

除非你仔细看,否则很难理解它们。以余弦 _SD 为例。一般来说,在所有的 pdf 中,我们看到正或“高”类的曲线高于负或“低”类。这显然是由于数据不平衡,因为属于正类的点比负类的多。你可以看到在 0 标记处,正类的曲线略高于负类。当您向右移动时,正曲线仍保持在上方,但正曲线与负曲线的高度比会增加。也就是说,随着 cosine_SD 增加,正类中的点与负类中的点的比率增加。因此,该点属于正类的概率增加。现在,如果你给我两个点,那么我可以说,具有较大余弦 _SD 值的点应该具有较高的相关性分数。
因此,从上面的观察,我们可以得出结论,余弦 _SD 确实提供了一些关于(搜索,产品-文本)对的相关性分数的信息。
对该集合中的所有特征进行了相同的分析,包括 raw_search 和的特征。主要观察结果是:
- 特征 num_common、余弦、Jaccard 在正负类分布上表现出相当大的差异。其中,余弦 _ST 是最有效的。
- 以 ST 为后缀的特征,即搜索和标题之间的特征,与 SD 和 SB 相比,在正类和负类的分布中显示出更多的区别。
- 长度特征在分布上没有显示出很大的差异。
- 最有趣的特色之一是 islast 特色。观察到分布有相当大的差异。例如,在 islast_ST 中,负类的密度在 0 和 1 值处似乎是相似的,但是正类的密度从 0 到 1 显著增加。
✦:根据观察,余弦和伊斯特特征是最重要的特征,其次是雅克卡。其中,ST 特征应该显示最好的结果。
还构建了这些特征的相关矩阵:

集合 1 特征的相关矩阵
并且找到它们与相关性的相关分数

与相关性分数最相关的集合 1 特征
注: 期望的特征集合将是这样的集合,其中所有特征具有高相关性分数和彼此低相关性分数。
观察和结论:
- 有几个特征彼此具有高度的相关性。这些相关特征可能是多余的,因此可以在建模时移除。
- 相关分数 wrt。相关性不是很高,但是确实存在一些相关性。因此,这些功能可能是有用的。
- 我们可以看到,cosine_ST、jaccard_ST 和 islast_ST 的相关性最高,与我们预测的完全一致。
- 这些值也是为 raw_search 计算的,一般来说,可以看出,修正的 _search 特征与相关性的相关性大于 raw_search 特征与相关性的相关性。
- 我还在这些特征上应用了 T-SNE,使它们在二维空间中可视化。我将嵌入的特征投影到二维空间中,并用它们各自的类给它们着色。我在之前的两个班级和七个班级中尝试过。但是它根本没有给出任何见解。没有一个类显示任何聚类。

分别用七个类和两个类可视化 TSNE 要素。
特征集 2:基于 VSM 的特征
基于 VSM 的特征通常基于将文档和查询表示为向量,然后将它们之间的相似度计算为标量。
这里,为了将文本转换成向量,我使用了潜在语义索引,其中截尾 SVD 被应用于术语-文档矩阵,这样查询和文档被转换成“概念空间”中的向量。基本上,这里我首先使用 TF-IDF BOW 对文本进行矢量化,然后使用 Truncated-SVD 来降低向量的维数,并创建一个更加密集和有意义的表示。这些功能包括:
➤分别对搜索、标题和描述的术语文档矩阵应用 LSI,并直接使用低秩近似作为特征。
➤在全文(标题和描述的组合)上应用 LSI。然后,将搜索查询转换到“概念”空间,并计算它们之间的余弦相似度。
功能集 2 上的 EDA
余弦相似性特征的 pdf 如下所示。

集合 2 余弦相似特征的 pdf
正如您所看到的,相似性特征在两个类之间提供了相当大的区别,非常有用。发现这三个中的每一个与相关性类的相关值是 0.32,这是相当好的。
特征集 3:概率特征
这些特征基于概率论中的核心概念,如贝叶斯定理。使用的功能有:
➤ 语言模型具有狄利克雷、绝对和杰利内克米勒平滑
➤ Okapi BM25 排名功能
➤查询和表示为 TF-IDF Word2Vec 的字段
每个文本字段中搜索查询的(tf,idf,tf-idf)的➤ (sum,min,max)
BM25 排名函数: 除了这些特性,我想重点介绍 BM25 排名函数,因为它本身在传统搜索引擎中用于文本查询,并且在简单的自由文本查询中仍然非常流行。对于给定的(查询,文档)对,它计算相似性得分,如下所示:

来源:https://en.wikipedia.org/wiki/Okapi_BM25

图片由作者提供,灵感来自 Victor Lavrenko 关于 IR 概率模型的讲座
以上是解释整个等式的一个尝试。它基本上是 TF-IDF 加权的修改形式,其中 TF 值已经被修改以结合文档的变化长度,并在评分 wrt 项-频率的增加中带来非线性。为了更好地理解它,我鼓励你们去听一听维克多·拉夫连科的讲座。
功能集 3 上的 EDA
进行了类似的分析,并且所有特征都具有与相关性分数相当的相关值,但是与集合 1 和集合 2 相比稍低。有趣的是,具有最高相关性的特征被发现是 min_tf_ST,即标题文本中搜索词的最小词频。直觉上,这应该是一个好的特性;min_tf_ST = 0 意味着在搜索中有没有在标题中出现的单词,min_tf_ST ≠ 0 意味着在标题中找到了搜索中的所有单词,因此表示高度相关。

相关性最高的第 3 组特征
ST 功能的 PDF 图,即搜索和标题之间的 PDF 图如下所示。


第三组功能的 pdf
观察和结论:
- 在 tf、idf、tf-idf 特征中,tf 特征即 min_tf、max_tf 和 sum_tf 是最有效的。而 idf 特征不能区分这两个类别,也没有显示出与相关性分数的高度相关性。
- bm25 和语言模型特性(JM,AD,Dir)显示了这两个类之间的区别。请注意,我设计的语言模型与相关性呈负相关,因此较低的值意味着高相关性
- 所有这些特征的唯一警告是,它们彼此具有非常高的相关值,因此存在大量冗余,这可能会影响线性模型,如线性回归和 SVM。为了解决这个问题,我们可以删除这些高度相关的特征中的一个。

AD_SB 和 Dir_SB 特征高度相关
功能集 4:查询扩展
查询扩展是一个很容易理解的概念。
- 首先,使用 Word2Vec 将搜索查询中的所有单词转换成单词向量。
- 然后,设 q 是一个由一包术语表示的用户查询,q = (t1,t2,.。。,t|q|)
- 为了扩展查询 q,我们遵循对于每个 t ∈ q,使用 w2v 收集与 t 最相似的 k 个术语。将新术语包含到查询集中,给出 q。
在查询扩展之后,我们可以使用扩展的搜索查询来重新计算来自特征集 1 的所有基本特征。
我确实在这个集合上执行了一点 EDA,结果观察非常类似于没有扩展的搜索,即扩展术语没有被证明是非常有帮助的。这可能是因为我在我的语料库上训练了一个定制的 word2vec 模型,该模型可能没有训练好。有了训练有素的模型,我相信查询扩展会非常有帮助。
功能集的性能图表
在这些特性集上,我用一些超参数调整训练了一个 XGBoost 模型。我不断添加特性集,检查是否有任何改进,结果非常好。训练是在具有 74067 个数据点的整个训练数据上进行的。为了评估,所用的指标是具有 5 重交叉验证的 RMSE。

功能集的性能图表
意见和结论:
- 随着我们添加更多功能,错误不断减少。
- f1_raw 的误差为 0.4805,在 f1_corrected 后略有改善。当我们结合使用这两个特性集时,它显著提高到 0.4763。因此,原始特征集和校正后的特征集确实提高了整体性能。
- 添加 f2_lsi 集后,误差也大幅下降至 0.471。
- 当我们添加特征集 3 时,观察到最大的改进,其中误差从 0.471 下降到 0.4547。不仅如此,特征集 3 单独表现得非常好,误差为 0.4569。因此这是最重要的一套。
- f4 的查询扩展特性并没有带来很大的改进。
- 在来自所有集合的组合的特征上观察到的最好分数是 0.4541。
最终建模和评估
堆积回归器
前一节中的 XGBoost 模型主要是为了观察和比较不同特性集的性能而构建的。对于最后的建模部分,我用 17 个基础模型和一个岭回归模型作为元模型构建了一个定制的堆叠回归器。为此,我进行了如下操作:
- 将 train.csv 文件中的全部数据(74067 点)分成训练集和测试集(80–20)
- 现在,我把 80%的火车分成 D1 和 D2。(50–50).所以 D1 和 D2 各有大约 29k 点。
- 在 d1,我做了替换取样,为我的 17 个基本模型创建了 17 个样品 D1、d2、D3…d17。所用的采样比在 0.85 和 0.9 之间。因此,为了训练每个基本模型,使用了大约 25k 个数据点。
- 然后,我创建了 17 个模型,并用这 17 个样本中的每一个来训练这些模型。
- 我将 D2 集传递给这 17 个模型中的每一个,并从每个模型中得到 17 个对 D2 的预测。
- 现在,使用这 17 个预测,我创建了一个新的数据集,对于 D2,我已经知道其相应的目标值,所以我用这 17 个预测作为特征来训练我的元模型。因此,用 29k 个点训练元模型,每个点具有 17 个特征。
- 对于模型评估,我使用我在开始时保留的 20%的数据作为测试集。我将该测试集传递给每个基础模型,获得 17 个预测,用这 17 个预测创建一个新的数据集,最后,将它传递给元模型以获得最终预测。使用这个最终预测和测试集的目标,我计算了模型的性能分数。
基础模型
我把基本模型分成三组 M1、M2 和 M3。主要使用基于树的模型和线性模型。
- M1 模型在总共 71 个基本特征上被训练。它们是 f1_comb(29) + f2_lsi(3) + f3(39)。各种模型(总共 7 个)在这个集合上被训练。
- M2 模型在总共 971 个特征上被训练。其中 71 个是基本特征。其他 900 个特性来自搜索、标题和描述的 Word2Vec 嵌入。
- M3 模型在总共 4071 个特征上被训练:71 个基本特征,其余 4000 个来自特征集 3 中的截断 SVD。对于这个集合,由于它的高维数,我只坚持线性模型。
对于交叉验证,我通常使用 5 重交叉验证。基本型号的性能报告如下所示:

基本型号的性能
元模型
对于元模型,我使用了一个简单的岭回归模型。

经过超参数调优,我在测试集上得到了最终的 RMSE0.4539。在火车上,我得到了 0.4531 的 RMSE,因此模型也没有过度拟合。
Kaggle 上的性能
我在 Kaggle 提供的包含超过 160,000 个数据点的测试集上测试了我的最终模型,它得到了 0.46525 的私人分数。在两千多支参赛队伍中,它名列前 10%****。

前 10%
延伸到现实世界的搜索引擎
正如本文前面提到的,在现实世界的搜索引擎中,对于任何搜索查询,我们首先会使用一个更简单的检索模型来检索一些候选产品。然后,使用机器学习模型对这些产品进行重新排序。
我们已经以堆叠回归器的形式建立了机器学习模型。对于最初的检索部分,我首先用 train.csv 和 test.csv 文件中的所有产品创建了一个数据库。该数据库包含 124428 个独特的产品,其字段为 product_uid、标题、描述和属性。对于模型,我使用 BM25 ,这也是我们机器学习模型的一个特征。给定一个搜索查询,BM25 模型根据它的自定义排名函数检索前 100 个产品。
现在,在这 100 个产品之上,我运行我的堆叠回归器,作为输入,我给出 100 对(搜索,产品-文本),其中搜索保持不变,但产品不同。因此,我的模型会对它们进行排名,然后我们可以向客户展示相关性得分最高的产品。
全面铺开
该模型的完整管道如下所示:

完整管道
最终测试和结果
所以现在整个搜索引擎完成了。我对它进行了一系列查询测试。结果非常有趣。我在下面附上一些结果的截图。
首先,一个简单的搜索“潘”和结果足够好。

然后我搜索“空调”,然后是一个拼写错误“AOR conditioner”。两者的结果完全相同。这意味着我们的拼写校正器工作正常!

'空调'

' aor condiotioner '

“温水”
对于“温水”,结果很有意思。没有一款产品的名称同时包含“温暖”和“水”两个词。在最上面的结果中,你可以看到所有的标题都有两个单词。那么第一个标题被赋予第一个排名的推理是什么?这是因为“Icicle”这个词的出现,它的定义是“由滴水冻结形成的一块悬挂的、逐渐变细的冰”。我们使用的单词 embeddings 一定是以类似于单词 water 的编码方式对其进行了编码。因此,我们的模型发现了“冰柱”与“水”的联系,并以这样的方式显示结果。另请注意,在接下来的三个结果中,我们得到了更多热水器方面的预期结果。在这里,单词 embeddings much 使模型知道单词“warm”与单词“heat”相关联,从而得出结果。
更多搜索的结果如下所示。




运行时间 不同的搜索运行时间不同。对于像“pan”这样的简单单词搜索,平均运行时间约为 5.09 秒,但对于长搜索和需要拼写纠正的搜索,运行时间在 5.5 秒和5.7 秒之间。
注意:本案例研究的主要目的是对产品搜索进行建模,主要关注于提出我们可以应用 ML 算法的有用特征。我们不太关注设计和优化部分。对于现实世界的使用,这个搜索引擎可以像疯了一样进行优化,并且运行时间不需要很长时间就可以减少到一秒钟以下。
网络应用演示
结论和未来工作
在这个案例研究中,我探索了信息检索领域的各种技术,以便从提供的数据中提取特征。这些技术从简单的集合操作符到潜在的语义索引。基于 LSI 的相似性和传统的检索模型如 BM25 和语言模型被证明是非常有效的。对于建模,我建立了一个具有 17 个基础模型的堆叠回归器,并将岭回归器作为元模型,在 Kaggle 上获得了 0.4652 的最终分数。此外,我通过添加 BM25 形式的初始检索模型,将其扩展为一个成熟的搜索引擎。
我的分数和排名第一的队伍的分数(0.4319)还是有很大差距的。因此,很明显,需要做更多的工作来改进模型。以下是一些建议:
- 我们拥有的文本数据与互联网上找到的一般文本非常不同。因此,只要有可能根据我们自己的客户数据训练一个模型,我们就应该探索这种可能性。比如我们可以训练自己的 Word2Vec 模型。有了这个,也可以探索 Para2Vec 和 Sent2Vec 模型。
- 可以实现更好的拼写校正器,因为这可以使模型更加健壮。
- 属性文本未在任何特征中使用。可以收录。
- 在预处理部分,可以进行词汇化。
- 一定有品牌具有相似类型的产品,因此品牌可以被聚类,然后聚类数本身可以被用作分类特征。
- 只有常用词的“计数”被用作特征。我们还可以尝试找到一种方法来嵌入这些常用词,以告诉我们的模型存在哪种常用词。
- 在查询扩展中,为了计算相似的单词,可以探索更好的矢量化技术
- 对于建模部分,可以探索 LSTMs、GRUs、BERT、CNN 等深度学习模型。
感谢
我要感谢整个应用课程团队在整个案例研究中对我的指导。
参考
- https://www . ka ggle . com/c/home-depot-product-search-relevance
- https://en.wikipedia.org/wiki/Information_retrieval
- 搜索引擎信息检索中的布尔模型
- sławomirzadrożny 和 KatarzynaNowacka 重新研究了模糊信息检索模型
- 将概率模型 BM25/BM25F 集成到 Lucene 中
- 由华金·佩雷斯-伊格莱西亚斯、何塞·佩雷斯-阿圭拉等人创作的《https://en.wikipedia.org/wiki/Learning_to_rank》。
- Indri:一个基于语言模型的复杂查询搜索引擎,作者 Trevor Strohman,Donald Metzler,Howard Turtle 和 W. Bruce Croft。
- 使用线性代数进行信息检索由 Michael W. Berry 等人完成。
- Saar Kuzi 等人的使用单词嵌入的查询扩展。
- 在微软学习排序数据集上的特征选择和模型比较森磊等。
- 信息检索中的向量空间模型:术语加权问题
- https://opensourceconnections . com/blog/2016/03/29/semantic-search-with-latent-semantic-analysis/
- http://www . CCS . neu . edu/home/jaa/CSG 339.06 f/Lectures/vector . pdf
- Victor Lavrenko 关于信息检索语言模型的视频讲座
- http://datasciencebar . github . io/blog/home-depot-produce-relevance-review/
- https://medium . com/ka ggle-blog/home-depot-product-search-relevance-winders-interview-2nd-place-Thomas-Sean-陈清-nima-68068 f 9 ffd
- https://arxiv.org/pdf/1803.05127.pdf
- 【http://billy-inn.github.io/papers/cmput690.pdf
- https://arxiv.org/pdf/2001.04980.pdf
- https://www.appliedaicourse.com
完整的代码可以在我的 Github 档案中找到:
https://github.com/kriz17/Home-Depot-Product-Search-Relevance
在 LinkedIn 上与我联系:
https://www.linkedin.com/in/kriz-moses/
PS :如果你认为他们可以改进这个博客,请随时提供意见/批评,我一定会努力做出必要的修改。
用原子卷积神经网络模拟蛋白质-配体相互作用
思想和理论
利用三维分子复合物的局部结构预测结合亲和力

图片来自 Unsplash 。
内森·c·弗雷
本文由 DeepChem 的 Bharath Ramsundar 合著。
ACNNs 从蛋白质-配体复合物的三维结构中学习化学特征。在这篇文章中,我们展示了如何使用 DeepChem 中的 ACNNs 和 PDBbind 数据集 的开源实现来预测蛋白质-配体结合亲和力。
一个 互动教程 伴随着这篇文章,可以通过 Google Colab 运行。
复杂的问题
药物发现的一个关键挑战是找到优先结合靶蛋白的小分子。我们可以使用分子对接或自由能微扰计算来预测候选分子的结合亲和力,但这些技术是计算密集型的,需要大量的专业知识才能应用。在这篇文章中,我们展示了如何将卷积神经网络或 ConvNets 的强大思想应用于 3D 原子坐标,以直接学习化学特征并预测结合亲和力。我们将介绍原子 ConvNet 如何工作,如何快速获得蛋白质-配体复合物的数据集,并训练一个模型来预测结合亲和力——所有这些都使用开源库 DeepChem 。

Thiago Reschützegger 的图片来自 DeepChem forums 。
什么是原子通讯网?
约瑟夫·戈麦斯、巴拉思·拉姆森达、埃文·范伯格和维贾伊·潘德在本文【1】中介绍了 ACNNs。首先,让我们来分析一下 ACNN 的架构。ACNN 的构造块是两个原始的卷积运算:原子型卷积和径向池。分子的笛卡尔原子坐标 X 用于构建距离矩阵 R 。 R 的元素是某个原子最大数量的最近邻的相邻原子之间的距离。还构建了一个原子类型矩阵 Z ,用于列出相邻原子的原子编号。
R 被送入深度为 Nat、的过滤器,其中 Nat 是复合体中唯一原子类型的数量。原子类型卷积核是一个阶跃函数,它对 R 进行运算,并为每种原子类型生成距离矩阵的独立副本。

3D 原子坐标和原子类型是原子类型卷积的输入。图片由约瑟夫·戈麦斯摘自 戈麦斯等人的 。
接下来,用径向池层对原子型卷积的输出进行下采样。这种形式的维数减少防止了过度拟合,并减少了学习参数的数量。径向池层应用具有可学习参数的径向过滤器(池函数)。我们可以把径向池看作是一个特征,它总结了一个原子和所有不同类型的相邻原子之间的成对相互作用。

原子型卷积的输出被馈送到径向池层。图片来自约瑟夫·戈麦斯 戈麦斯等人 。
原子卷积层(原子类型卷积+径向池)可以通过展平径向池层的输出并将其馈入另一个原子类型卷积来堆叠。最终的张量输出按行(每个原子)输入到一个完全连接的网络中,以预测每个原子的“能量”。通过将每个原子的能量相加,可以预测一个分子的总能量。

全连接层产生标量输出。图片由约瑟夫·戈麦斯从 戈麦斯等人的 。
这允许对输出进行有趣的解释,其中结合能被计算为 ACNN(复合物)- ACNN(蛋白质)- ACNN(配体)。也可以分解每个原子对结合能的贡献,取独立分子中的一个原子和复合物中的同一个原子的 ACNN 估算的能量之差。需要注意的是,输入维度仅取决于特征的数量(原子类型和径向过滤器的数量),而不是分子中的原子数量,因此 ACNN 可以推广到比训练集中的系统更大的系统。
来自 PDBbind 的蛋白质-配体复合物数据
既然我们已经了解了 ACNN 的基本原理,让我们得到一个分子数据集并开始训练模型吧!我们将使用来自pdb bind【2】的高质量蛋白质-配体复合物和测量的结合亲和力的“核心”集合。由于 MoleculeNet 中的加载器函数,我们可以用一行 python 代码从 PDBbind 中检索训练、验证和测试集。为了使计算负担最小化,我们只描述了蛋白质结合口袋(而不是整个蛋白质),并且我们只考虑了核心组中的大约 200 个复合物。如果我们想考虑来自 PDBbind 的更大的“细化”或“通用”集合,我们只需更改 load_pdbbind() 中的 set_name 。
tasks, datasets, transformers = load_pdbbind(featurizer=acf,pocket=True,set_name=’core’)
3D 原子坐标数据集使用atomicconfeaturezer进行特征化,其预先计算蛋白质、配体和复合物的特征(坐标矩阵、原子类型矩阵和邻居列表)。特征器控制每个分子中考虑的原子数量以及每个原子考虑的最大邻居数量。
在蛋白质-配体复合物上训练 ACNN
我们将建立一个 ACNN 模型,其超参数与原始论文中的模型相似。
acm = AtomicConvModel(n_tasks=1,frag1_num_atoms=f1_num_atoms,frag2_num_atoms=f2_num_atoms,complex_num_atoms=f1_num_atoms+f2_num_atoms,max_num_neighbors=max_num_neighbors,batch_size=12,layer_sizes=[32, 32, 16],learning_rate=0.003,)
这里,我们关注 layer_sizes ,它控制全连接网络中密集层的数量和大小。与原子数量相关的其他参数等于我们为atomicconvertizer指定的输入。我们将保持 15 个原子类型和 3 个径向过滤器的默认数量。
我们训练 50 个时期的模型,并可视化损失曲线。不出所料,它们不是特别光滑。使用更大的数据集可能对此有所帮助,但这是快速构建绑定亲和力预测模型的起点。

核心 PDBbind 集超过 50 个历元的训练和验证损失。图片作者。
预测结合亲和力
最初的 ACNN 论文在核心 PDBbind 数据集上使用了随机的 80/20 训练/测试分割。他们在训练集和测试集上分别显示了 0.912 和 0.448 的 Pearson R 值,用于预测结合亲和力。我们的简单模型在训练集(0.944)上实现了类似的性能,但在验证集(0.164)或测试集(0.250)上表现不太好。这是意料之中的,因为原始论文表明,尽管 ACNNs 成功地从小数据集学习化学相互作用,但它们容易过度拟合,并且无法在训练集之外进行推广。
有很多事情我们可以(也应该!)尝试建立一个更强大的模型:添加正则化/删除,使用更大的数据集和更大的全连接网络,改变原子类型和径向过滤器的数量,并尝试不同的分裂。我们甚至可以通过只研究配体和完全忽略结合口袋来做得很好!
非常值得注意的是,我们可以获得一个蛋白质-配体复合物的数据集,训练一个相当复杂的深度神经网络在不到 15 分钟的时间内预测像结合能这样复杂的物理量!然而,预测结合亲和力仍然是一个具有挑战性的问题,无论我们使用像 ACNNs 或基于物理的模拟这样的深度学习方法。关于蛋白质-配体相互作用预测的更多机器学习方法的概述,请查看这个有用的帖子。希望像 DeepChem 中的 ACNNs 这样的开源工具将使研究人员更容易进行深度学习实验,并开发出更好的方法来模拟蛋白质-配体相互作用。
取得联系
如果您喜欢本教程或有任何问题,请随时通过 电子邮件 或连接LinkedIn和Twitter联系内森。
你可以在内森的 网站 上找到更多关于他的项目和出版物的信息。
感谢 乔·戈麦斯教授 为本贴提供反馈。
参考文献
[1]约瑟夫·戈梅斯、巴拉思·拉姆松达、埃文·范伯格、维贾伊·潘德。预测蛋白质-配体结合亲和力的原子卷积网络。 arXiv:1703.10603 。
[2]王,方,陆,王。PDBbind 数据库:已知三维结构的蛋白质配体复合物的结合亲和力集合。药物化学杂志,47(12):2977–2980,2004。
模拟维也纳市的交通密度
网络分析/机器学习
将道路网的官方记录与优步数据连接起来

作为一个欧洲大城市的公民,我感觉城市地区的交通相当混乱和不可预测。然而,根据个人经验,我们每个人都建立了一些关于穿过我们家乡的某些繁忙街道和最快路线的直觉,甚至可能依赖于一天中的时间。
在本文中,我试图通过在粒度级别上分析推导维也纳市的交通密度来检验这些推测。为此,我结合了两个公开来源:
- 道路网:维也纳市道路网的官方记录,包含大约 30,000 个路段,每个路段都有各自的地理位置、长度和街道类型( https:\data.gov.at )。
- 优步:优步乘车的汇总数据,显示维也纳任意分区组合之间的行程时间(https://movement.uber.com)。
选定方法
作为第一步,道路网络能够根据每条街道上给定的最大速度限制模拟任意两点之间的最短路径。这样的路径导致一个假想的“无交通堵塞”的行驶时间。
接下来,这些“无交通”的旅行时间可以与来自优步乘坐的真实世界的“包括交通”的旅行时间进行对比。通过构造一个约束优化问题,从而获得代表网络每个路段的交通密度的系数。结果可以观察到不同情况下的交通密度和平均行驶速度。
本文中的 Python 实现可以从我各自的存储库中访问和派生。
https://bernhard-pfann.github.io/
1.模拟路径
1.1.公路网
道路网络的官方记录包含城市内的所有街道段和交叉口。经过几个步骤的清理,它们可以以数据帧的形式描绘出来。

交叉表示“网络节点”
由于将应用网络分析,数据将作为“节点”和“边”加载到网络图中。两个数据帧的索引是连接对象的标识符。

街道代表连接节点的“网络边缘”
在这个项目中,我依赖于 NetworkX 包中的几个功能,它是为分析复杂的网络图而设计的。见下图数据帧中节点和边的初始化。
数据帧中节点和边的图形初始化
通过绘制图表并为不同的街道类型添加颜色代码,完整的道路网络变得可见。数据源区分三种不同类型的道路:本地街道(绿色)、主要街道(黄色)和联邦街道(红色)。街道类型充当允许的最大值的代理。速度限制,尽管它可能不会在所有情况下都与现实 1:1 对应。

按街道类型进行颜色编码的网络图
因为我们将在稍后尝试寻找从网络的不同起点到终点的路径,所以我们必须确保所有节点都连接到主网络图。
nodes_main = list(max(nx.connected_components(self.G)))
nodes_all = list(self.G.nodes())
nodes_disconnect = set(nodes_all) - set(nodes_main)
通过所识别的断开的节点和相应的边来修剪网络留下了待分析的完全连接的网络。

由断开的节点和边进行颜色编码的网络图
1.2.优步地区
优步将维也纳市划分为 1370 个分区(“区域”),以收集个人乘车的敏感数据。根据实际乘坐情况,两个地区之间任意组合的平均旅行时间是综合得出的,结果是 1400 万英里。Q1 2020 期间每日出行时间的测量。有关优步汇总方法的更多详细信息,请参见官方手册。

2020 年 Q1 期间的优步旅行时间
无论如何,要将优步骑行的数据与引入的道路网络连接起来,网络的每个节点都需要映射到其所在的相应区域。区域边界以多边形的形式提供,也可以绘制。

优步地区分割了维也纳的城市区域
通过检查单个节点并迭代地检查所有多边形是否该节点位于其边界内,可以实现所有节点的映射。
将网络节点映射到区域
1.3.最短路径
将网络组件成功映射到优步定义的区域后,每条边的可用属性具有以下结构:

存储为字典的随机网络边的属性
行驶时间属性是距离(米)除以速度(千米/小时)的简单除法,并转换为秒。它代表在一条街道上持续以最高限速行驶的假设时间。因此,它也可以被称为“无交通”的旅行时间。
为了估计任何区域之间通过网络的最短路径,NetworkX 算法会查找使指定边属性最小化的路径。在这种情况下,我们最小化交通自由旅行时间。
因为路径只能在节点之间计算,而不能在区域之间计算,所以需要从源区域和目的区域中随机抽取一个节点。为了避免不吉利的抽奖带来的噪音,测试了这些随机节点中的几个,同时只选择中值旅行时间的路径作为输出。
下面可以看到这种最短路径的直观表示。深蓝色线标示实际路线,正在通过的区域以浅蓝色突出显示,并标有区域编号。

可以从选择的路径中提取各种统计数据。将作为优化问题的输入的相关度量是各区域在相应路径上行进的米距离。

按路径上的区域划分的行驶米距离
2.约束优化
2.1.识别系数
我们已经非常接近找出城市每个区域的交通状况。在遍历优步数据集中的所有源区域和目的区域并收集每条路径上区域的米距离后,我们可以将获得的数据矩阵与优步乘坐的实际行驶时间相关联。数据帧分为 X 和 y 变量,如下所示。

单位面积的米距离作为独立变量来估计平均旅行时间
用简单的数学符号表示这种关系,实际的优步传播时间(y)应该等于米距离(x)乘以特定区域系数(β)的总和。

逻辑由此表明,区域“j”的β项越高,分配给该特定区域的总行驶时间越多,意味着交通密度越高。此外,由于该等式的测量单位是秒(y)和米(X),β项表示为秒/米。这当然可以转换成 km/h,赋予它真实的意义。

2.2.运行优化
既然问题已经确定,就需要拟合一个模型,使给定函数的 RMSE 最小化。为了获得合理的β值,系数被限制在固定的范围内。转换成单位 km/h,这些对应于 5-120km/h 之间的带宽
经过 10 次迭代后,最小值似乎出现在大约 130 RMSE 处。通过在看不见的测试集上运行该模型,RMSE 仅略微增加到 134,这表明该模型的稳健性。

每次迭代的优化 RMSE
初始化为以 0.2 为中心的正态分布的 1370 个系数在第 10 次迭代后似乎也找到了平滑分布。

每次迭代的系数分布
在地图上绘制系数可绘制地理空间交通密度图。虽然较亮的区域表示交通流量较低,但较暗的区域与更繁忙的街道和较慢的交通流量相关。对于灰色区域,没有一条模拟路径与其边界相交。

按区域划分的交通密度
通过解释这些结果,两个观察结果与预期一致。首先,市中心的交通量最大。这种影响也扩展到了更西部的地区,那里的人口密度很高,街道也很小。第二,南部和多瑙河(流经城市的主要河流)沿线较大的明亮区域与现有的高速公路网络非常匹配,这使得旅行速度更高。
2.3.情况分析
虽然对整体交通密度建模已经可以识别城市中的热点,但比较一天中不同时间的交通状况或比较工作日与周末的动态也可能是有见地的。
为了获得场景结果,训练模型的优步游乐设施被简单地过滤为所选择的时间或天类别。见下面清晨(上午 0-7 点)与下午(下午 4-7 点)交通密度系数分布的比较。正如所料,早上的系数平均较低,对应于更少的交通流量和更高的平均行驶速度(如下面的子图所示)。

不同场景下系数和速度的分布
地图上两种情况下的系数差异图也在空间维度上支持了这一发现,因为红色区域意味着下午的交通密度高于清晨。

交通密度的差异(上午与下午)
3.最终考虑
使用所提出的方法,我们能够通过将街道网络映射到“优步定义的”城市分割,来组合两个不同的公共可用数据源。这使我们能够丰富每次优步骑行的源区域和目的区域,并获得骑行期间的路径信息。通过拟合一个将行驶路径与行驶时间联系起来的模型,可以得到交通密度的合理估计值。
然而,在此基础上,我想到了各种扩展,这些扩展可以使见解更具可操作性:
- 将单行道和红绿灯的数据纳入网络(至少后者是公开的)
- 引入人工交通堵塞,观察其对替代路线的影响
- 估计每个街道段的交通密度系数
- 比较 Covid 前后的交通动态
如果您发现任何其他有趣的应用程序,或者您想为自己的家乡重建项目,请随时使用我的工作。您可以在我各自的 GitHub 资源库中找到源代码。
https://bernhard-pfann.github.io/
[1]粗略地从德语翻译过来
[2]由于计算能力有限,1400 万中只有 10 万。路径已被模拟
用张量流概率模拟神经网络中的不确定性
实践教程
第 4 部分:完全概率化
本系列是使用张量流概率库对不确定性建模的简要介绍。我是作为我的PyData Global 2021 talk关于神经网络中不确定性估计的补充材料写的。
系列文章:
- 第一部分:一个简介
- 第二部分:任意不确定性
- 第三部分:认知的不确定性
- 第四部分:完全概率化

介绍
我们走了这么远的路!今天,是我们最后一节课的时间了。我们将使用我们获得的所有知识,并将其应用于一个新的更具挑战性的数据集。
让我们把手弄脏吧!
数据
本周,我们将建立一个非线性异方差数据集。这个问题比我们之前看到的问题更难建模。
我们将使用以下流程来生成数据:

我们的数据生成过程。真实的你的形象。
让我们导入库,构建数据集并绘制它。
导入库,创建非线性训练数据并绘制它。

训练数据。请注意, x 的值越高,方差就越大。这是异方差的一个例子。数据也是非线性的。真实的你的形象。
如你所见,数据确实是非线性的,并且是异方差的。我们需要在我们的模型中引入一些非线性,以便更好地拟合这些数据——这是扩展我们概率工具包的一个绝好机会!
建模任意的和认知不确定性
我们的数据集已经就位。为了建立一个能够模拟任意和认知不确定性的模型,我们需要结合变化层和分布层。为了对认知不确定性建模,我们将使用两个tfpl.DenseVariational层和一个非线性激活函数。
首先,让我们定义前和后函数。你可能还记得系列的前一部分,我们需要将它们传递给tfpl.DenseVariational以使其工作。
先从之前的说起吧:
先验生成函数。
先验生成函数必须以核大小、偏差大小和dtype为参数(后验也是如此)。我们按照与在第 3 部分中相同的方式定义先验母函数:
- 我们使用一个不可训练的
tfd.MultivariateNormalDiag分布对象作为我们的先验。 - 我们将它包装成
tfpl.DistributionLambda,将tfd.MultivariateNormalDiag分布对象转换成 Keras 兼容层。 - 我们将这一层传递给
tf.keras.Sequential以实现与后验生成函数的一致性。
类似地,我们按照第 3 部分的过程定义后验生成函数:
- 我们用
tf.keras.Sequential。 - 我们希望我们的后验是可训练的,所以我们使用生成可训练变量的
tfpl.VariableLayer来参数化tfpl.MultivariateNormalTriL。 - 我们利用第 2 部分中介绍的一种非常方便的方法
.params_size()来获得参数化tfpl.MultivariateNormalTriL所需的精确参数数,并将该层用作我们的可训练后验层。
我们有一切必要的参数化我们的变化层。
我们现在需要做的就是堆叠两个tfpl.DenseVariational层,添加一个非线性激活,并在它们上面放一个tfpl.MultivariateNormal层。
正如我们在第 2 部分中看到的,为了训练一个以分布层作为最后一层的模型,我们需要一个特殊的对数似然成本函数。
让我们将所有这些结合起来,定义我们的模型生成函数:
模型生成函数。我们的架构由两个“tfpl.DenseVariational”层和一个“tfpl.IndependentNormal”层组成。我们使用负对数似然成本函数来训练模型。
让我们打开它。我们使用普通的tf.keras.Sequential包装纸。我们将两层tfpl.DenseVariational放入其中,并将一层tfpl.MultivariateNormal放在上面。
第一个变化层有 8 个单元(或神经元)。首先,我们将输入形状定义为(1,)。那是因为我们的 X 是一维的。然后,我们将我们的先验和后验生成函数分别传递给make_prior_fn和make_postrior_fn。接下来,我们将 Kullback-Leibler 散度项归一化,通过训练样本数量的倒数对其进行加权。我们将kl_use_exact设置为False(在我们的例子中,我们也可以将其设置为True)。最后,我们指定非线性激活 function⁴.
第二层的参数化类似。尽管有一些不同之处。我们没有对单元的数量进行硬编码,而是使用.params_size()方法来确保我们获得了正确的单元数量来参数化我们的最后一个分布层。注意,我们在这里也不使用非线性。
我们的负对数似然损失函数与我们在第 2 部分中定义的函数相同。
我们现在准备好训练模型了!
让我们这样做,并绘制损失图:

我们的任意和认知不确定性估计模型的损失与时代数。真实的你的形象。
损失看起来不错,我们可以尝试进一步优化模型,以更快地收敛,但这不是我们在这篇文章中的目标。
结果
你的模型被训练。那是一个非常激动人心的时刻!我们准备好进行预测了。我们的预测将包含对 任意性 和 认知性 不确定性的估计。我们期望看到一组 最佳拟合线和一组 置信区间。为了使可视化更清晰,我们将稍微修改绘图代码,将置信区间表示为线条而不是阴影区域。与我们在第 3 部分中所做的类似,我们将计算 15 个独立预测:
我们为最佳拟合线及其置信区间生成 15 个预测,并绘制结果。

训练数据和 15 条最佳拟合线及其各自的置信区间。该图表示对任意(置信区间)和认知(15 组最佳拟合线及其 CIs)不确定性的估计。真实的你的形象。
如您所见,我们的模型在拟合数据方面做得非常好!🚀似乎非线性和异方差都建模得很好。置信区间似乎是可靠的,覆盖了大约 95%的点。
摘要
在这一集的用张量流概率 对神经网络中的不确定性建模中,我们看到了如何使用单一模型对任意性和认知不确定性进行建模。
我们生成了一个非线性和异方差数据集,并使用具有非线性激活的更深层次架构对其建模。我们更新了如何定义参数化变化层所需的先验和后验分布。最后,我们已经看到了如何使用一个变化层来参数化一个最终的概率分布层。
这是用张量流概率 系列对神经网络中的不确定性建模的最后一篇文章。祝贺你完成这个系列!🎉
谢谢大家!
如果您对本系列涵盖的材料有任何疑问,或者对您希望在未来阅读的主题有任何建议,请随时告诉我:
谢谢大家!
脚注
理论上,我们可以通过使用一个变分过程作为模型的最后一层来达到更高的概率。但是这超出了本系列的范围。
你可能还记得,我们将这些函数作为函数对象传递,而不调用它们。
请参考第 3 部分进行解释。
⁴:我们在例子中使用了 sigmoid 函数。如果我们使用 ReLU,模型拟合看起来会不同吗?如果有,为什么?欢迎在评论中告诉我。
❤️ 有兴趣获得更多这样的内容吗?使用此链接加入:
https://aleksander-molak.medium.com/membership
谢谢大家!
用张量流概率模拟神经网络中的不确定性
实践教程
第 2 部分:任意不确定性
本系列是使用张量流概率库对不确定性建模的简要介绍。我是作为我的PyData Global 2021 talk关于神经网络中不确定性估计的补充材料写的。
系列文章:
- 第一部分:一个简介
- 第二部分:任意不确定性
- 第三部分:认知的不确定性
- 第四部分:完全概率性

介绍
在本系列的第一部分中,我们讨论了建模不确定性背后的动机,并介绍了张量流概率 (TFP)的基础知识。在这一部分,我们将重点讨论 任意不确定性 。我们将从定义开始,然后看看如何使用张量流概率对随机不确定性建模。
什么是随机不确定性?
随机不确定性代表当我们用完全相同的设置多次重复相同的实验时,结果的不可预测的差异。它与生成数据的数据生成过程有着内在的联系。
任意一词来源于拉丁语alea/ˈaː.le.a/原意为“关节骨”或“枢轴骨”。这些类型的骨头被用作骰子——你可以把它们想象成伪随机数发生器的早期版本🤯随机不确定性的另一个——可能更直观——名称是(也称为统计不确定性 ) 。**
也许关于随机不确定性最重要的事实是,它不能通过增加更多的数据来减少
让我们看一个例子。

想象两个机器人。其中一个扔网球,另一个用球拍击球。让我们重复这个场景 1000 次,保持所有参数不变。即使我们一直使用同一个球,每次球的轨迹也会略有不同。这是因为有许多我们没有考虑到的小差异——球不是完美的球形,在电机控制中有小的不精确性,等等。观察到的轨迹差异是随机不确定性的本质。
我们说过随机不确定性代表我们多次重复相同实验时结果的差异。我们无法精确预测这些差异。尽管如此,还是有一些我们可以做的有价值的事情。让我们试着量化不可预测的。
用张量流概率模拟任意不确定性
在系列的前一部分中我们介绍了tfp.layers模块和各种分配方法,包括.log_prob()。今天,我们将在实践中使用概率层和.log_prob()。它们将是我们概率模型的重要组成部分。我们将保持例子非常简单,这样我们就可以专注于方法而不是复杂的问题或数据集。准备好了吗?🚀
首先,让我们生成一些数据。

我们的数据生成过程。请注意,如果您使用真实世界的数据,您可能希望在训练前对其进行缩放。真实的你的形象。
如你所见,我们的数据是嘈杂的。通过在基本线性方程中添加随机ε(ε)项来模拟其噪声。随机项正态分布,均值为 0,标准差为 1。我们将 ε 乘以 0.35 ,缩小其标准偏差。因为 ε ,所以 X 和 Y 之间的关系并不是完全确定的。
让我们使用上面的公式生成 200 个点,并绘制数据。
导入库,生成 X 和 Y 向量并绘制结果。

生成的数据。真实的你的形象。
现在,让我们建立一个简单的回归模型,并根据数据进行拟合。
为我们的数据建立并拟合一个简单的回归模型。
让我们在训练集上生成预测并绘制它们:
预测训练集上的值并绘制结果。

我们的训练数据和拟合的回归线。真实的你的形象。
拟合线似乎是对 X 和 Y 之间关系的一个很好的估计。给定 X 的值,该线代表 Y 的期望值的最佳(线性)近似值。
我们试着稍微延伸一下思路。我们说过我们正在估计给定 X 的 Y 的期望值。如果我们更进一步,尝试估计另一个参数——标准差,会怎么样?在我们设计的例子中,我们知道噪声项— ε — 通常是 distributed⁴.因此,我们可以对数据进行正态分布拟合,而不仅仅是直线拟合。这意味着我们想要修改我们的模型,这样它就不会返回一个标量值,而是返回一个分布对象。我们需要做一些修改才能使它工作。
到目前为止,我们使用均方误差(或'mse')作为损失函数。当对连续的目标进行操作时,均方误差是一个很好的选择,但是为了学习一个分布,我们将需要一些别的东西。
我们将创建一个对数似然函数,它接受一些数据和一个分布对象,并根据给定的分布计算数据的对数似然。
用作损失函数的负对数似然函数。
该函数采用真值y_true和预测值y_pred。这些论点实际上与任何张量流相容损失函数的论点相同。但是有一个转折。记住,我们现在要输出一个分布。因此,我们的预测y_pred将是一个分布对象(而不是一个标量值)。正如我们在第 1 部分中所讨论的,每个分布对象都有一个.log_prob()方法,在给定一些数据的情况下,计算分布的 log-PDF。我们将使用这种方法来计算对数似然性,然后我们将反转符号(或者将结果乘以-1,如果您愿意的话)。我们颠倒了符号,因为我们想最大化可能性。按照惯例,在深度学习中,我们宁愿最小化而不是最大化。最小化负对数似然和最大化似然是一样的。
我们的损失函数到位了。我们准备开始构建模型。
带有概率层的模型,允许我们模拟任意的不确定性。
让我们打开它。
从技术上讲,我们的新模型有两层。事实上,第一层的唯一目的是参数化第二层中的分布。从这个意义上说,我们的模型并不比我们之前建立的简单回归模型更深入。
第一层是一个普通的 T4 层。第二层是tfpl.IndependentNormal。TFP 中的概率层是分布对象的包装器,允许 TFP 分布与常规张量流层平滑集成。注意tfpl.IndependentNormal只接受一个参数— event_shape。在我们的例子中,事件形状等于 1,因为我们的输出是一维的。该图层将返回一维正态分布。
现在让我们回到第一层。您可能已经注意到,我们没有使用单个标量值来定义单位数,而是使用了下面的公式:tfpl.IndependentNormal.params_size(event_shape)。我们可以在这里传递一个数字,但我们是故意这样做的。静态分布方法.params_size()是一个非常方便的工具,尤其是在处理更复杂的模型时。这是您 TFP 工具箱中的一笔巨大资产!在给定事件形状的情况下,该方法返回参数化分布所需的多个参数。在我们的例子中,这个方法应该返回 2,因为我们想要参数化一个 1D tfpl.IndependentNormal,它有两个参数——均值和标准差。我鼓励你自己检查这个方法是否返回了我们期望的结果。
现在,让我们根据新模型进行预测,并绘制结果图。
获得模型预测并绘制结果。

训练数据、拟合回归线和置信区间。真实的你的形象。
让我们确保理解生成预测背后的过程。我们的新模型返回一个分布对象。为了从分布中获得参数估计值,我们可以从分布中取样并使用获得的样本计算感兴趣的统计数据,或者我们可以使用.mean()和.stddev()方法直接获得这些统计数据。我们选择了后者。
请注意,平均值给出了一条线,这条线实际上与我们之前从简单的线性回归模型中获得的线相同。这是意料之中的,如果我们的模型收敛,概率模型和非概率模型之间的这种比较可能是一个很好的检查。允许我们量化 随机不确定性 的额外信息来自标准差。我们使用经过训练的分布的标准差来生成置信区间,该置信区间量化了数据生成过程中随机项的可变性。
注意:你可能会问自己:如果我们可以通过分析获得这些置信区间,为什么还要为概率层和复杂的训练程序而烦恼呢?这是一个很棒的问题!答案要求我们超越今天的例子来思考:我们在本教程中使用的方法可以很好地推广到非线性和复杂的情况,在这些情况下,分析计算置信区间可能非常困难。也就是说,许多人告诉我,对于他们来说,通过非常简单的例子来理解这些概念比通过更复杂的例子要容易得多。我希望这对你也有用。如果您想看稍微复杂一点的东西,请等到第 4 部分!
摘要
在本集用张量流概率 对神经网络中的不确定性建模中,我们已经看到了如何对任意不确定性进行建模。
我们使用.log_prob()方法和一个概率层来实现这个目标。我们学习了如何创建负对数似然损失函数,该函数允许我们训练具有最终概率层的模型。
在下一部分,我们将关注 认知不确定性 。我们将看到更多的概率层在起作用,并将在第 4 部分为建立完全概率模型打下基础。
第三部再见!
— — — — — — — — — — — — — — — — —
脚注
⁰首先假设我们的样本足够大。理论上,如果我们有一个非常小的样本,我们可能会高估随机不确定性。在这种情况下,在增加样本量后,我们可能会经历随机不确定性的某种程度的降低。
这种情况与混沌理论和复杂性科学中对初始条件的敏感性密切相关。
如果你有哲学倾向,你可能会说我们可以测量物体在不同时间点的位置和速度,并使用这些测量来减少与结果相关的不确定性。这是一个伟大的言论。它揭示了随机不确定性的另一个方面:假设我们不增加新的变量,它不能通过增加更多的数据来减少。请注意,添加新的变量会将我们带到一个新的(更高维度)系统,其随机不确定性不同于原始系统的随机不确定性!根据您的场景,这可能有其局限性。尽管如此,探索它们超出了本材料的范围。****
我们遵循一个非常简单的流程。请注意,我们不使用训练测试分割、交叉验证或其他良好的数据科学实践。这样做的唯一原因是我们希望只关注不确定性建模。在现实世界中,您很可能会对遵循这些良好实践感兴趣。
真实世界场景中的⁴噪声项的分布可能是(并且经常是)未知的。
________________
❤️对获得更多这样的内容感兴趣吗?使用此链接加入:
**https://aleksander-molak.medium.com/membership
_______________**
用张量流概率模拟神经网络中的不确定性
第三部分:认知的不确定性
本系列是使用 TensorFlow 概率库对不确定性建模的简要介绍。我是作为我的PyData Global 2021 talk关于神经网络中不确定性估计的补充材料写的。
系列文章:
- 第一部分:一个简介
- 第二部分:任意不确定性
- 第三部分:认知不确定性
- 第四部分:完全概率性

图片由丽莎在https://www.pexels.com/photo/turned-on-led-bulb-1393363/拍摄
介绍
在本系列的前一部分中,我们谈到了 任意不确定性。我们已经展示了如何在实践中使用 TensorFlow Probability 的概率层和自定义损失函数对其建模,该函数允许我们使用概率最终层来训练模型。
本周,我们将关注 认知不确定性 。我们将探索更高级的概率层,并了解在神经网络中估计权重不确定性的技术。先说一个定义。
什么是认知不确定性?
在第 2 部分中,我们说过随机不确定性与数据生成过程有内在联系,不能通过添加更多数据来减少。
—另一方面,认知不确定性与我们对数据生成过程的了解(或无知)有关。这个名字来自古希腊语https://en.wiktionary.org/wiki/ἐπιστήμη*****/e . pisˇti . mi/——这个术语被亚里士多德或柏拉图等哲学家广泛使用,大致翻译为“知识”。认知不确定性的另一个名称是模型不确定性。*****
关于认知不确定性的一个重要事实是它可以通过增加更多的数据来减少*。*******
用张量流概率建模认知不确定性
先说一些数据。我们将使用与在第 2 部分中相同的数据生成过程,但这次我们将创建两个数据集,而不是一个。一个数据集较小( n=100 ),另一个较大( n=1000 )。这些数据集将帮助我们理解样本大小如何影响不确定性估计。
我们导入库,使用相同的数据生成过程生成两个数据集,但是样本大小不同( n=100 和 n=1000 )。最后,我们绘制数据。

我们两个数据集的散点图。真实的你的形象。
我们的数据准备好了。在我们开始建模之前,让我们了解需要做出什么架构决策来在我们的模型中启用认知不确定性估计。
从概念上讲,我们需要做的是用分布代替逐点权重估计。让我们打开它。在传统的神经网络中,每个权重由一个数字表示。在训练期间,我们最小化一些关于模型权重的损失函数。当模型表现足够好时,我们停止训练并存储权重。我们有多确定我们所学的权重是我们问题的好估计?
我们实际上不知道。
为了解决这个问题,我们需要一种方法来量化这种(不)确定性。我们将用一个分布替换每个权重,而不是学习每个权重的单个值,我们将学习每个分布的一组参数。

在一个常规的神经网络(左)中,所有的权重都是点估计值(或者也可以是一个数字)。当对认知不确定性建模时(右),每个权重被建模为分布而不是点估计。右图中的橙色线条代表发行版的 pdf。真实的你的形象。
要了解分布,我们需要从某处开始—我们需要一个初步分布,然后我们将更新它以反映最佳数据拟合。这种初步分配被称为优先。最终分布称为后验。
给定先验分布和我们的数据,我们将利用贝叶斯定理的力量来学习后验分布。但是有一个问题,在我们的例子中,计算精确的后验概率非常困难。
变分图层
幸运的是,TensorFlow Probability 提供了通过反向投影【1】实现 贝叶斯的tfpl.DenseVariational层——这是一种可用于神经网络中高效权重不确定性估计的方法。这是一种近似的方法——但绝对足够好,可以引导我们获得巨大的实际结果。
此外,tfpl.DenseVariational层将为我们完成大部分繁重的工作,包括证据下限( ELBO )的计算——我们将使用这个量在内部找到后验。**
我们使用tfpl.DenseVariational的方式类似于常规的tf.keras.Dense,但是我们需要指定几个额外的参数。最重要的两个是优先生成函数和po 优先 g 生成函数。这两个函数都需要带三个参数:kernel_size、bias_size和dtype,并且都应该返回一个分布。****
我们来定义一下!
我们将从一个先验分布开始。**
使用多元正态分布和“tfpl.DistributionLambda”包装器定义不可训练的先验。
让我们打开它。
我们使用平均值为零(loc=tf.zeros(n))和标准差为 1 ( scale_diag=tf.ones(n))的tfd.MultivariateNormalDiag。这个发行版被打包到tfpl.DistributionLambda中,它只是将一个发行版对象转换成一个 Keras 兼容的层。这一层然后被传递给一个标准的tf.keras.Sequential模型。这有点拗口,还有一个稍微简单一点的方法来定义这个先于。不过,我们将坚持上面介绍的方法,因为它更通用,更符合我们定义后验的方式。****
那就让我们看看后路吧!**
定义一个可训练的后验分布。 tflp.VariableLayer '生成一个可训练变量来参数化 tfpl.MultivariateNormalTriL'。
我们采用完全相同的一组参数——内核和偏差大小以及dtype。类似于我们之前所做的,我们使用tf.keras.Sequential。与之前的分布不同,我们希望我们的后是可训练的。我们使用生成可训练变量的tfpl.VariableLayer来参数化tfpl.MultivariateNormalTriL。正如在第 2 部分中,我们使用了一种静态便利方法.params_size()来获得参数化tfpl.MultivariateNormalTriL所需的精确参数数。****
建模认知不确定性
这是很多,但现在我们终于准备好定义模型了!
定义一个可以估计认知不确定性的概率模型。我们使用一个函数来定义模型,以便轻松地为每个数据集构建单独的模型。
让我们简单地研究一下变分层参数。第一个参数是单位数。这里我们只有一个单位——就像常规线性回归中一样。第二个参数— input_shape —看起来应该也很熟悉——它与规则密集层的情况具有完全相同的含义。接下来,我们传递先验和后验函数。注意,我们应该将这些函数作为对象传递,而不调用它们。****
最后,我们有两个与kull back–lei bler 散度 ( KL 散度)相关的参数。 Kullback-Leibler 散度是我们 ELBO 目标中的一个术语。因为这一项与 ELBO 中的第二项是在不同的数据子集上计算的,我们需要重新调整它以确保它是无偏的。这就是我们将x_train_shape作为参数传递给模型生成函数的原因。最后一个参数——kl_use_exact——控制我们是想通过分析计算 KL 散度还是使用经验近似法。在我们的例子中,我们可以解析地计算它,因为当我们有一个作为先验和后验的正态分布时,这很容易做到。然而,这并不总是可能的。我们将kl_use_exact明确设置为False,以证明该参数 exists⁴.
- 我们可以使用均方误差作为损失函数(尽管在内部,我们仍将使用 ELBO 来逼近后验分布),
- 当我们要求模型进行预测时,它几乎每次都会返回不同的输出;这是因为现在每次查询模型时,我们的权重都是从学习的分布中采样的。
我们准备好安装模型了。
拟合两个模型:一个在较小的数据集上,一个在较大的数据集上。
我们对每个模型训练 500 个时期,以确保两个模型都收敛。请注意,就参数而言,这些模型是迄今为止最大的——我们需要估计偏差项和权重项的均值和标准差以及它们之间的协方差,这样我们总共有 5 个参数。
让我们画一条学习曲线。

作为时代数的函数的损失。蓝线代表在较小数据集上训练的模型( n=100 )。红线代表在更大的数据集上训练的模型( n=1000 )。真实的你的形象。
正如你所看到的,在较小的数据集上训练的模型的损失( n=100 )要波动得多。这是有意义的,因为模型很难在较小的数据集上找到稳定的参数估计值。
太好了!我们的模型经过训练,我们最终可以生成和绘制预测!
为更小和更大的数据集生成和绘制预测。我们为每个模型生成 15 个预测。

我们两个数据集的散点图和每个数据集的 15 条拟合线。真实的你的形象。
我们为每个模型生成了 15 个预测。每次预测都是不同的,因为对于每次迭代,我们从学习到的后验分布中随机抽取权重。请注意,在较大数据集上训练的模型(右)在斜率和截距估计值上的方差比在较小数据集上训练的模型(左)小得多。这是一个很好的例子,说明如何通过添加更多的数据来减少认知不确定性。
摘要
在本集用张量流概率 对神经网络中的不确定性建模中,我们已经看到了如何对认知不确定性进行建模。
我们使用了更高级的概率层,比如tfpl.VariationalDense。我们定义了我们的先验和后验分布。最后,我们已经看到样本大小如何影响认知不确定性估计。
在下一部分,我们将介绍一个完全概率模型,我们将在一个更复杂的非线性数据集上训练它,看看如何使概率模型更深入。****
第四部见!
脚注
ELBO 有两个分量:似然分量和 Kullback-Leibler 散度分量。更多细节请参考[1]。
变量n表示层中参数的总数。
[tfpl.MultivariateNormalTriL](https://www.tensorflow.org/probability/api_docs/python/tfp/distributions/MultivariateNormalTriL)是tfp.distributions模块中多元正态分布的参数之一。
⁴这个参数的默认值被设置为False,如果你不知道的话,很容易忽略它。
参考
[1] C. Blundell,J. Cornebise,K. Kavukcuoglu,D. Wierstra,神经网络中的权重不确定性 (2015)。第 32 届机器学习国际会议论文集(ICML 2015)。
________________
❤️ 对获取更多这样的内容感兴趣吗?使用此链接加入:
*****https://aleksander-molak.medium.com/membership
谢谢大家!
_______________*****
用张量流概率模拟神经网络中的不确定性
实践教程
第一部分:引言
本系列是使用张量流概率库对不确定性建模的简要介绍。我是作为我的PyData Global 2021 talk关于神经网络中不确定性估计的补充材料写的。
系列文章:
- 第一部分:介绍
- 第二部分:任意不确定性
- 第三部分:认知的不确定性
- 第四部分:完全概率性

图像由杰曼特里https://www.pexels.com/photo/blue-body-of-water-5412/
为什么要对不确定性建模?
让我反过来问这个问题——为什么不对不确定性建模?
想象你正在建立一个医疗诊断系统。假设您的系统可以预测三类中的一类:(A)恶性肿瘤,(B)良性肿瘤,(C)无肿瘤。你的系统预测你的病人杰西患有恶性肿瘤。预测的 softmax 得分为 0.9 。你有多大把握你的预测是正确的?

我们设计的诊断系统的 softmax 分布。系统返回(A)类恶性肿瘤的高 softmax 值。
你会和杰西分享这个诊断吗?
在您回答这个问题之前,让我们将权重不确定性估计添加到我们的模型中。这种不确定性的另一个名字是认知不确定性,我们将在本系列接下来的几集里详细讨论。让我们再次看看我们模型的输出。

左图:我们设计的诊断系统的 softmax 发行版。右图:一个 softmax 分布加上 95%的置信区间,超过 10000 个来自我们模型的概率版本的预测。
刚刚发生了什么事?我们以前见过左边的情节,但是我们在右边看到了什么?在右边的图中,我们添加了 softmax 分数的 95%置信区间,该分数是通过从我们的模型的概率版本中抽取 10.000 个样本而获得的。
这样的结果有可能吗?尽管看起来有些违反直觉,简短的回答是肯定的。在关于认知不确定性的第集中,我们将看到真实世界系统以类似方式运行的例子。
回到我们最初的问题——为什么(不)对不确定性建模?我们将在本系列的最后一集尝试对这个问题给出一个有根据的答案。同时,我很想听听你的想法。请在下面的评论区分享它们。
现在,让我们看看一个工具包,它将帮助我们回答关于不确定性估计的问题——张量流概率。

张量流概率 API 参考页。你真实的照片。
张量流概率
TensorFlow Probability(TFP)是一个概率编程库,是更广泛的 tensor flow 生态系统的一部分。它不是 core TensorFlow 库的一部分,需要单独安装导入。安装指南可在文档中找到。
TFP 是一个综合库,有超过 15 个不同的子模块。在这个系列中,我们将关注其中的两个:
tfp.distributionstfp.layers
分布
顾名思义,tfp.distributions为我们提供了分配对象。目前(2021 年 11 月),你可以在那里找到超过 100 种不同的发行版。
让我们导入库,从简单的开始。我们将初始化一个均值为 100、标准差为 15 的正态分布:
导入张量流概率,初始化并检验正态【100,15】分布。
正如您在上面的代码输出中看到的,我们的分发对象有batch_shape和event_shape属性。批次大小告诉我们批次中分布对象的数量。例如,我们可能希望一批中有三个单变量高斯函数,以参数化网络中的三个独立输出层。事件形状传达不同类型的信息。您可以将事件形状视为分布的维度。让我们看一个例子:
初始化批量为 2 的正态分布。注意我们将一个两元素列表传递给构造函数的‘loc’。
正如你所看到的,现在我们得到了一个batch_shape为 2 的分布。批处理和事件形状也可能具有更高维的形状,即它们的形状可能是矩阵或张量。
现在,让我们将这一批两个正态分布转换成一个单一二维 分布:
将两个分批正态分布转化为一个 2D 分布。
在上面的代码中,我们将我们的normal_batched包装到一个特殊的tfd.Independent类的实例中。该类将由reinterpreted_batch_ndims参数指定的多个批量维度转换为事件维度。normal_2d的批次形状现在为空,分布的事件形状为 2。
您可能还注意到了normal_batched.mean()和normal_2d.mean()返回了几乎相同的数组。尽管它们的意思不同。在第一种情况下,我们得到了两个独立的均值——一个用于批次中的每个分布。在第二种情况下,我们得到了一个具有两个分量的单一均值向量——一个分量对应于我们的 2D 分布的每一个维度。
为了更好地理解这种差异,让我们看看每个 TFP 发行版提供的一些基本方法,并尝试将它们应用到我们的示例中。
每个 TFP 分发对象都有三种基本方法:
.sample().prob().log_prob()
.sample()允许你从一个分布中取样,.prob()返回一个分布的密度(概率密度函数— PDF,不是概率!)和.log_prob()返回您输入的 PDF 日志。
从批量分布和二维分布中抽取三个样本。在这两种情况下,示例数组的维数是相同的,但是它们的含义是不同的。
在上面的代码中,我们从normal_batched采样了三个样本,从normal_2d采样了三个样本。尽管两种情况下的样本数组大小相同,但它们的含义不同。
当我们评估两种分布的 PDF 时,我们应该更清楚地看到这一点。请想一想,使用形状样本(3,2)评估两种分布的 PDF 时,您期望得到什么尺寸。他们会一样吗?不一样?为什么?🤔
让我们看一个非常简单的例子。我们将取两个值——100 和 200——并使用我们的两个分布来评估这些点的 pdf。
对同一样本评估“normal_batched”和“normal_2d”的 PDF 会产生不同的输出形状。
如您所见,normal_batched.prob(sample)返回了两个值,而normal_2d.prob(sample)只有一个值。此外,注意normal_batched两次返回相同的值!你知道为什么吗?如果是这样,请在下面的评论中分享你的答案。
批处理分布返回两个值,因为事实上有两个独立的分布(只是包含在单个批处理元素中)。同时,我们的第二个分布是一个二维的单分布,我们需要两个数字来描述 2D 空间中的一个点。
说到这里,我们结束了本周的节目。在下一集,我们将讨论随机不确定性,并了解如何使用tfd.layers模块对其建模。

图片由 cotton brohttps://www . pexels . com/photo/a-white-line-on-the-asphalt-road-5319516/
摘要
恭喜你走了这么远!🎉
在本集用张量流概率对神经网络中的不确定性建模* 系列中,我们看到了一个示例,展示了建模不确定性如何为我们提供有关模型性能的附加信息。*
我们已经用 TFP 工具箱中的基本而强大的工具进行了实验。我们探索了分布基础子模块,我们看到了如何使用tfd.Independent将批量分布转换为多维分布。最后,我们探讨了基本的分配方法:.sample()和prob()。
在下一部分,我们将关注随机不确定性。我们将看到tfd.layers子模块的运行,我们将了解.log_prob()方法的威力。
感谢您的阅读,第二部分再见!
________________
❤️对获取更多这样的内容感兴趣吗?使用此链接加入:
*https://aleksander-molak.medium.com/membership
_______________*
通过模拟探索网络
使用基于代理的建模来理解网络的形状如何影响其行为
网络在我们的生活中无处不在。你可以在我们周围的能源和交通基础设施中,在为你提供计算能力和互联网的数字系统中,在你的社会关系和社区中找到它们。
许多重要的系统都可以用网络来表示,所以我们能够在我们的模拟建模工具中探索网络的行为,并在这个过程中获得视觉直觉是至关重要的。在这篇文章中,我们将使用 HASH ,一个基于代理的在线模拟平台,来尝试和理解意见是如何通过社交网络传播的。
级联传播
我们将构建并探索一个简单的“观点传播”模型。让我们创建一组代理,他们根据自己的颜色(绿色或蓝色)支持或反对某个想法。代理会将他们的意见与他们大多数邻居的意见相匹配(包括他们当前的意见)。他们的邻居将被定义为那些在网络中与他们相连的代理,他们的初始意见将被随机分配。

接管网络的绿色观点(图片由作者提供)
使用这样一个简单的模型,你可以提出许多不同的问题。首先,我们将尝试看看某些网络结构是否会导致两种情况之一:允许两种观点共存,或者最终导致一种“群体思维”的情况,即一种观点被所有代理采纳。考虑一下这两种情况在一个试图产生新产品想法的公司或一个试图改进政策的立法机构中可能反映出什么。这种“观点传播”也可能代表传染病的传播,或者电网的连锁故障。
我们将使用 NetworkX 库的内置函数来生成三种类型的随机网络,并探索我们最终是否会完全同质。你可以一边阅读一边试验模拟。
随机网络
我们将探索不同类型的随机网络,每一种都是用不同的启发式方法产生不同的形状。网络形状由节点数、边数以及这些边的位置决定,所有这些都会影响网络的行为。我们将探索三类不同的网络,每一类都以它们的发明者命名:Erdos-Renyi、Watts-Strogatz 和 Barabasi-Albert。
鄂尔多斯-仁义网络
在这种类型的网络中,生成个节点,然后创建它们之间所有可能的边的百分比 p 。这些边是随机选择的,这意味着在连接中没有出现特定的模式。一个 n=50 的随机网络可能看起来像这样:

(图片由作者提供)
您可以看到“意见”的分布似乎一开始就大致相等,我们有一个很好的混合节点,既有只有几条边的节点,也有有许多边的节点。我们可以通过运行模拟并绘制每种颜色的流行程度来观察网络中“意见”的变化:

(图片由作者提供)
这种类型的网络几乎总是收敛于 0.1,因为没有单独的“集群”或高度互联的组被创建。这阻止了任何种类的异质性的保存。当 p 很小时,很多节点最终没有任何边,没有真正形成一个连通的网络。
您可以在下面的嵌入中尝试在鄂尔多斯-仁义网络中传播。将network_type指定为"erdos_renyi",并修改erdos_renyi.p的值。您可以在 3D 查看器和分析选项卡之间切换,以获得查看模型的两种不同方式。
接下来的两个网络都将具有特定的特征,这些特征将它们与这个纯粹随机的网络区分开来。
瓦茨-斯特罗加茨网络
这些网络是随机生成的,但使用一种算法会产生特定的模式。它们是通过修改环形网格网络创建的,该网络由排列成圆形的 n 个代理组成,每个代理连接到其最近邻居的 k 。然后,边在代理之间以某种概率 p 被“重新连线”,这意味着边的一个端点被移动到一个随机代理。
该网络旨在更好地模拟现实生活中网络的一些属性。值得注意的是,我们世界中的许多网络表现出“小世界”特性,其中一个节点的邻居通常是彼此的邻居,大多数节点彼此不连接,并且网络中每个节点之间的距离相对较小。
当使用适当的 p 值时(通常在 0.01 和 0.1 之间),这种类型的网络表现出那些“小世界”特性。它们可能看起来像这样:

(图片由作者提供)
多次运行模拟,我们可以看到“小世界”特征似乎阻止了意见的同质收敛:

(图片由作者提供)
然而,如果太多的边被重新布线(在这个例子中, p 大于 0.2),网络开始在视觉上类似于 er 网络,并且实际上,趋向于收敛而不是保持分离。
巴拉巴斯-艾伯特网络
在我们最终的网络中,节点是按顺序添加的,并以基于该节点上现有边数的概率连接到其他节点。这种方法被称为“优先附加”。
简单观察一下,与 WS 网络中可见的链模式相比,Barabasi-Albert 网络可能看起来更接近 er 网络。然而,运行模拟将表明,它也保留了异质性,不像 er 网络。
这意味着 BA 网络和 ER 网络在形状上一定有显著的不同。仔细观察,我们实际上可以看到,大多数节点都有少量的边相连(2 或 3),极少数节点有大量的边(10+)。记住,在 er 网络中,这种分布要均匀得多。

(图片由作者提供)
每个新节点最初都连接到“m”个其他节点。如果我们运行一个扫过“m”值的实验,我们可以看到 m 的低值阻止了意见的完全同质性。同质性是以持有每种观点的代理人数量与代理人总数之差的比率来衡量的。当所有代理都有相同的意见时,同质性为 1,当有等量的代理持有两种意见时,度量为 0。

(图片由作者提供)
随着m的增加,网络变得无法保持异质性。网络中的节点变得过于紧密,破坏了任何集群,就像我们将 p 的值增加到阈值以上时 WS 网络发生的情况一样。
正如我们所见,网络中生成的同质性的数量与边的分布密切相关。当一组节点紧密互连时,它倾向于保持统一的意见,即使它连接到具有不同意见的节点。随着节点变得不那么集群化,并且更多地连接到其集群之外的网络(或者当整个网络变成单个集群时),同质性似乎是不可避免的。
对网络结构关键特征的洞察适用于广泛的领域,适用于我们在这里遇到的三种网络类型之外的网络类型。模拟这些网络的行为是计算一组网络描述性度量的另一种方法。可视化检查和轻松修改的能力允许您(和其他建模者)对所研究的系统发展直觉。
进一步探索
除了修改我们提到的所有不同的参数,你还可以在你的浏览器中打开模拟并将其分叉(创建一个副本)以继续你的探索。
如果修改控制模拟行为的 opinion_spread.js 文件会发生什么?你可以用一个更低的门槛来代替多数采纳,引入两个以上的“观点”,并添加随机的观点变化。你甚至可以让它适应一个完全不同的领域,比如电网故障或传染病。
数据科学中的建模和模拟
实践教程
即使没有数据可用,也要使用数据科学和机器学习。

介绍
机器学习和深度学习的当前状态的主要限制之一是对新数据的不断需求。但是,在我们没有任何可用数据的情况下,怎么可能做出估计和预测呢?事实上,这可能比我们通常认为的更常见。
例如,让我们考虑一个思维实验:我们是一个地方当局的数据科学家,我们被要求找到一种方法,以便在发生自然灾害(如火山爆发、地震等)时优化疏散计划。在我们的情况下,因为自然灾害往往不会发生得太频繁(幸运的是!),我们目前没有任何可用的数据。在这一点上,我们决定创建一个模型,能够在很大程度上总结我们现实世界的关键特征,并使用它来运行不同的模拟(从中我们可以获得我们需要的所有数据)。有两种主要类型的可编程仿真模型:
- 数学模型:利用数学符号和关系来总结过程。流行病学中的房室模型是数学模型的典型例子(如 SIR、SEIR 等……)。
- 过程模型:基于设计者手工制作的一系列步骤,以表示一个环境(例如基于代理的建模)。
建模和模拟被用于许多不同的领域,如金融(如投资组合优化的蒙特卡罗模拟)、医疗/军事训练、流行病学和威胁建模[1,2]。
模拟的一些主要用途是在创建任何物理实现之前验证分析解决方案、实验策略,以及理解组成系统的不同变量的联系和相对重要性(例如,通过修改输入参数和检查结果)。因此,这些特性使得建模和模拟范例成为预测未来趋势的白盒方法。
一旦运行了许多不同的模拟并测试了所有不同的可能场景,我们就可以利用生成的数据来训练我们选择的机器学习模型,以在现实世界中进行预测。
作为本文的一部分,我现在将向您介绍您可能想要采用的不同方法,以便开始使用 Python 进行建模和模拟。本文中使用的所有代码都可以在我的 GitHub 账户上找到。
基于主体的建模
在基于代理的建模中,我们利用面向对象的编程(OOP)方法,以便为我们希望在我们的人工环境中拥有的每个不同类型的个体创建一个类,然后我们实例化尽可能多的代理。这些代理最终被放置在一个虚拟环境中,并让它们相互之间以及与环境进行交互,以便记录它们的动作和模拟结果。用 Python 创建基于代理的建模仿真的两种可能方式是利用 Mesa 或 HASH 。对于非 Python 用户来说, AnyLogic 和 Blender 是两个很好的免费替代品。
平顶山
为了展示 Mesa 的一些关键功能,我们现在将创建一个模型来模拟森林中的火势蔓延。其他示例可在 Mesa 官方知识库中找到。
首先,我们需要导入所有必要的依赖项。
现在,我们准备创建一个 Python 类( 树 ),它将用于在模拟中创建我们的代理。在这种情况下,我们的树可能处于三种状态之一:健康、燃烧或死亡。我们将从森林边缘少量燃烧的树木开始我们的模拟,然后这个数字将根据其他树木与已经燃烧的树木的距离而变化。一旦一棵树让所有邻近的树都着了火,它就会死去。
然后,我们可以继续前进,设计我们的树将坐落的世界(森林模型)。使用一个概率值( prob ),我们可以额外改变每个像元上有一棵树的可能性(以调节森林的人口密度)。
最后,我们需要创建两个辅助函数,以便从模拟中获取统计数据,并为绘图创建数据框。
一旦设计好我们的类和函数,我们现在就可以运行我们的模拟并存储所有生成的数据。
现在有两种可能的方法来可视化我们的模拟结果。我们可以使用 Python 创建绘图工具(如图 1 所示),也可以利用 MESA 可视化功能。
用于创建图 1 的所有代码都可以在链接获得(请随意与下图互动!).
图 1: Plotly 时间序列结果
使用 MESA 可视化功能,我们可以创建相同的图(图 2 ),并在本地主机地址: http://127.0.0.1:8521/ 的网页上发布

图 2: MESA 时间序列图
此外,我们还可以创建一个 2D 表示法来描述森林中的火势如何蔓延(图 3)。对森林和树木密度进行不同大小的多次模拟,我们就能够创建足够的数据来执行机器学习分析。

图 3:大火蔓延森林的 2D 视图
混杂
HASH 是一个免费的平台,可以用 Python 或 Javascript 快速创建高度并行的基于代理的模拟。平台上有大量免费的例子,它们也可以作为你自己项目的基础。
例如,已经有了一个与我们刚刚在 MESA 编码的模型非常相似的模型(野火——再生,图 4)。调整模型的不同参数,就有可能得到与我们以前得到的结果相似的结果。

图 HASH 中的森林火灾模拟
如果你有兴趣学习更多关于如何用 HASH 创建模拟的知识,他们的文档是一个很好的起点。
数学模型
这些类型的模型通常使用常微分方程或随机元素来设计(例如图 5 中的 SIR 模型)。

图 5: SIR 模型方程
这种模型的图表表示对于理解模型方程如何工作以及不同允许状态之间可能的运动非常有帮助(图 6)。

图 6: SIR 示意图
流行病学中一些非常著名的数学模型是 SIR(易感-感染-康复)模型和所有其他可以从它导出的模型(如 SEIR、疫苗接种、限时免疫)。如果你有兴趣了解更多关于这种型号的信息,华盛顿邮报的这篇文章是一个很好的起点。
然后就有可能用普通的 Python 或者利用高级数学包,比如 Scipy 和 Simpy 来编码这种类型的模型。例如,你可以在下面找到一个我过去项目的视频摘要,在这个项目中,我创建了一个仪表板来分析过去几个月新冠肺炎的趋势。这个项目使用的所有代码都可以在我的 GitHub 账户上找到。
联系人
如果你想了解我最新的文章和项目,请在 Medium 上关注我,并订阅我的邮件列表。以下是我的一些联系人详细信息:
文献学
[1]国防—高级多域合成环境 simprobabble . io .访问:https://improbable.io/defense2020 年 8 月。
[2]威胁建模安全基础 Microsoft Learn。已访问:https://docs.microsoft.com/en-us/learn/paths/TM-威胁建模-基础/2020 年 8 月。
用 R 中的多模态模拟双峰分布
分析具有多个模式的分布

我们通常认为大多数发行版只有一种模式。这包括正态分布等分布,它是统计学中的标准参考。

来源:RStudio 输出
实际上,一个发行版可能有不止一种模式。在这种情况下,我们使用模式来指代系列中具有多个峰值的分布。
例如,如果要检查一天中的高峰时间交通,很可能会发现早晚高峰,因为车辆往返于工作/学校环境。
虽然查看简单的直方图可以为我们提供关于分布是否是双峰的视觉线索,但是最好能够正式测试这种情况。
让我们来看看如何使用 r 中的多模式库来实现这一点。
多模式用电量分析
在本例中,使用多模式软件包分析千瓦消耗。具体来说,从 2011 年 3 月 30 日到 2011 年 4 月 6 日的 15 分钟间隔内的消费模式使用爱尔兰都柏林市政局记录的数据进行分析。正在讨论的完整数据集可从 data.gov.ie 获得,并在撰写本文时获得知识共享署名 4.0 的许可。

来源:RStudio 输出
从上图中,我们可以看到消费有规律的季节性趋势。
也就是说,假设电力消耗可能是双峰的。例如,消费模式可能在一天中的特定时间达到峰值,或者一周中的特定日期可能显示比其他日期更高的消费模式。
让我们首先绘制数据的直方图。

来源:RStudio 输出
我们可以看到数据中存在两个峰值。消耗往往在 30-40 千瓦之间达到峰值,然后下降,在 70-80 千瓦再次达到峰值。
现在,我们可以正式测试分布是否确实是双峰的。
为此,我们将测试单峰性的零假设,即一个模式的存在。另一种假设认为数据有不止一种模式。
> library(multimode)> # Testing for unimodality
> modetest(data)Ameijeiras-Alonso et al. (2019) excess mass testdata: data
Excess mass = 0.148, p-value < 2.2e-16
alternative hypothesis: true number of modes is greater than 1Warning message:
In modetest(data) :
A modification of the data was made in order to compute the excess mass or the dip statistic
在上面的分析中,p 值实际上是 0——在 5%的显著性水平上支持数据中存在一种以上模式的替代假设。
假设数据中存在两种模式,可以获得这些模式的估计位置。此外,可以识别反模式,或两种模式之间最不频繁的值。
> locmodes(data,mod0=2,display=TRUE)Estimated location
Modes: 33.25836 71.55446
Antimode: 55.06092Estimated value of the density
Modes: 0.03056129 0.01474216
Antimode: 0.006843394Critical bandwidth: 7.378684

来源:RStudio 输出
根据以上,我们可以看到两个模式峰值在 33.25 和 71.55,反模式识别在 55.06。
检验双峰分布的目的
建立分布模型的全部目的首先是为了逼近总体的值。
不可能收集人们希望观察的每一种现象的数据。因此,有必要依赖数据样本。
然而,通过识别分布,这可用于识别特定数据样本中的潜在异常。
例如,假设在某一周的正常高用电期间,在某些情况下发生了假设的停电,并且电力消耗模式与上面观察到的模式显著不同。识别分布形状的变化可以提供在那一周中功耗会发生显著变化的线索,并且可以调查这种情况的潜在原因。
这种类型的分析在其他场景中也很有用。例如,假设特定产品的销售通常是正态分布的。然而,在特定品牌或公司中观察到双峰分布。这可能表明分布在更高模式中的购买者正在选择该产品的奢侈品——分析分布将允许进一步的调查。
结论
在本文中,您已经看到:
- 什么是双峰分布
- 这种分布所揭示的洞察力
- 如何用 R 中的多模式库测试双模式
- 模式和反模式值的识别
非常感谢您的阅读,非常感谢您的任何想法或反馈。
参考
免责声明:本文是在“原样”的基础上编写的,没有任何担保。它旨在提供数据科学概念的概述,不应被解释为专业建议。本文中的发现和解释是作者的发现和解释,不被本文中提到的任何第三方认可或隶属于任何第三方。作者与本文提及的任何第三方无任何关系。
将生理信号估计建模为深度学习问题
使用计算机视觉从面部视频中估计生命体征,如心率、呼吸率和 SpO2 水平。

瑞安·斯通在 Unsplash 上的照片
让我们从一个小练习开始。但首先我得请你戴上你的健身追踪器;你确实有一个,对吗?哦..你认为这个练习是精神上的?灾难性的。关键是,如果你此刻对你手腕上的健身设备有一点点观察,你会注意到背后有一个闪亮的灯(通常是绿色的)。现在,如果你很好奇或者更有观察力,你会知道这种光被用作一种媒介(双关语)来提取你手腕上的脉冲信号!这是所有重要的生理相关指标,如心率、呼吸率等。你在屏幕上看到的,是计算出来的。
在过去的一年里,我一直在生理信号估计领域工作,这是我的轶事介绍背后的巨大推动者。但愿,如今健身追踪器的无处不在让它变得有关联!
生理信号估计传统上被建模为更多的信号处理任务。然而,我们一直在通过深度学习的模式来评估这些生命体征;计算机视觉具体来说。首先,让我们用最简洁的方式来表达一些术语。
照片体积描记法(PPG)

光电容积描记术在起作用:利用反射光提取脉动 PPG 信号。图片来自维基百科( CC BY-SA 4.0 )
健身追踪器采用了一种叫做PPG的技术来“追踪”你的生命体征。通过手腕照射光线,这些追踪器可以通过皮肤下血管反射或吸收的光量的变化来测量心血管血容量脉搏【BVP】。通常,在医疗保健中,这是通过将接触式传感器放置在患者的指尖、胸部或足部来实现的。无论哪种方式,想法是提取体积描记信号,该信号可用于估计生理指标,如心率、呼吸率、氧饱和度等。你猜对了:photo-plethmography 中的 photo- 代表介质,也就是 light 。重要的是,PPG 是传统体积描记法的低成本替代方法,传统体积描记法涉及复杂的设备和训练有素的医疗保健专业人员来测量和理解患者的生命体征。

照片从左至右由蒂姆·谢尔曼·蔡斯(CC BY 2.0)和弗拉迪斯拉夫·比契科夫在 Unsplash 上拍摄
在概念化了光电体积描记术的支配性原理之后,我们现在着眼于使得从反射光中提取 PPG 信号成为可能的数学模型。
双色反射模型(DRM)
二色性反射模型(DRM)将【t】即从物体表面反射的光(在传感器处接收)描述为两个主要成分的线性组合。这两个分量是镜面(界面)反射和漫反射(体反射)。
镜面反射是在类似镜子的表面上观察到的规则反射,光线以与其入射角相等的角度反射。另一方面,在漫反射的情况下,光以多个角度散射。

建模反射光 C(t) 作为其组成项的组合。图片作者。
注:这些时变信号在传感器上连续记录一段时间,T =【0,T】。
根据 DRM,镜面反射的两个反射项 Vs(t) 和漫反射的 Vd(t) 由强度项 I(t) 调制。此外,我们还考虑了噪声项 Vn(t) ,它量化了传感器产生的噪声。
请注意,要实现这一切,测量设备(即使是相对便宜的健身可穿戴设备)仍需要与您(即患者)的身体接触。换句话说,PPG 是一种基于接触的方法,其中信号通常通过放置在指尖、手腕或脚上的接触传感器来测量。然而,这种类型的持续接触可能不适合于某些应用,例如运动和驾驶,具有引起运动受限、连续接触不便和/或注意力分散的问题。
在 Remote-PPG 中为“Remote-”加前缀
随着新冠肺炎疫情的爆发,当事情没有以前那么敏感,没有以前那么多接触时,我们都感觉更好。瞧啊。聚焦远程概念——光电容积描记,关键词远程。此后我们将把这个术语称为‘r-PPG’。
研究表明,脉动体积描记信号足够强,可以通过使用低成本的 RGB 照相机传感器从一系列图像中观察皮肤颜色的变化来捕获。

无接触地向移动。右边的照片由韦斯利·弗莱尔拍摄(CC BY 2.0)
扩展我在引言中提供的对光体积描记法的简要描述, r-PPG 通过允许我们从图像和视频中提取脉搏信号,而不是通过可穿戴设备上的接触传感器(见上图),提供了一种无接触、不引人注目和伴随的测量体积描记法信号的方法。
修正了之前的定义,r-PPG 允许远程通过皮肤下血管反射或吸收的光量变化来测量心血管血容量脉搏(BVP)。
因此,r-PPG 的前提使得能够从面部视频中提取脉动信息。这种远程估计 BVP 信号并通过扩展计算心率、呼吸率和氧饱和度等重要生理指标的能力,可以应用于世界上最普遍的相机传感器,即我们智能手机中的传感器!
因此,我们已经确定存在感兴趣的脉动信号,【p(t),其可以从包含皮肤像素的图像序列中提取;比如面部视频。现在让我们看看这个提取实际上是如何执行的。
为图像临时制作 DRM
图像由多个像素组成。将每个这样的像素视为用于提取脉动信号任务的独立传感器是有帮助的, p(t) 。跟随来自视频的一组连续图像帧的图像中的所有像素,我们得到视频时间轴上每个像素的像素强度(RGB)的时间序列。与我们之前基于信号处理的例子相关,这些时间序列数据中的每一个都可以被认为是一个单独的 C(t)。 我们现在下标【C(t)用一个 k 来表示属于k像素的像素强度的时间序列。DRM 可以针对图像输入进行如下修改:

为图像数据调整二色反射模型。图片作者。
注意:这些时变信号是在视频输入中的多个图像帧上获得的,即对于由 T 帧组成的视频来说T =【0,T】。我们从图像中的每个像素获得这样的信号。
注意到【C(t)的所有时变分量都可以写成静态 DC 分量(不是时间函数的项)和时变交流分量的组合。对感兴趣的信号 p(t) 有了一些了解,并给出了这个模型的状态,我们对 C(t)的定义做了两个小的重新安排。

Vn(t) 通过插值对像素进行空间平均来近似。图片作者。
首先,我们去除了考虑相机噪声和量化误差的【Vn(t)项。这是基于这样的假设实现的,即通过将足够多的非常接近的像素(即,它们的强度)分组(或平均)可以合理地消除相机噪声。在图像处理中,这被称为“空间平均”,通过使用插值对图像进行下采样来执行;为了简洁起见,省略了技术细节,我们有以下内容:

空间平均消除了相机量化误差的影响。图片作者。
其次,我们通过将它们分成它们的组成镜面反射和漫反射部分来重新排列【C(t)的分量,并基于它们的 DC/AC 性质(如前所述)对结果项进行分组。

作为简化,量化在第 k 个像素接收的颜色信号 C(t)。图片作者。
注意,为了简单起见,来自镜面反射和漫反射分量的与时间无关的 DC 项被收集到一个项中, Uc 。我们现在剩下一个集合 DC 反射项【Uc】和两个时变信号【t】和【p(t)的组合,所有这些都被强度项 i(t) 调制。虽然我知道这可能被认为有点过于简化,但基本的想法是从我们在相机传感器处接收的颜色信号【C(t)中提取感兴趣的脉动信号,即【p(t)。
如前所述,传统上,r-PPG 提取任务被视为信号处理问题。然而,使用基于深度学习的方法的最新进展已经显示出足够的价值来证明进一步研究的合理性。
进入深度学习
深度学习,特别是计算机视觉,赋予了我们开发端到端深度神经模型的能力,以解决许多复杂的任务,否则这些任务需要多级处理管道,通常涉及手工制作的特征操作。疫情给全世界的医疗保健带来了真正的变革。例如,在过去一年左右的时间里,通过远程医疗平台甚至视频会议进行远程咨询和诊断已经变得司空见惯。因此,一种用于恢复生理信号的端到端框架是合乎需要的,并且在给定的上下文中似乎是合乎逻辑的下一步。

可视化用于 r-PPG 提取的端到端深度学习管道。图片作者。
这是一个使用深度学习的端到端 r-PPG 管道的粗略可视化。在这个管道上进行了大量的研究和工作,以使其对受运动影响的视频(即,对象可能像在健身活动中一样移动)和受不良照明条件或不均匀照明影响的视频具有鲁棒性,但潜在的前提是从输入视频中最好地提取 r-PPG 脉冲信号【p(t)。
我建议你有保留地吸收所有这些信息(尤其是数学建模),因为为了简单起见,我特意省略了某些交互术语。我希望读者关注这样一个想法,即使是最基本的智能手机摄像头拍摄的视频也包含脉动信号【p(t)的痕迹,只要有一套正确的工具,就可以提取出来。该体积描记信号 p(t) 可用于确定关键的生命体征,如心率、呼吸速率和 SpO2 水平(血液中氧饱和度的测量)。
通过这篇介绍性文章,我希望读者能够理解深度学习在远程 PPG 估计任务中的前景和潜在应用;将其建模为一个计算机视觉任务。
嗨!感谢您完成这篇文章。大致说来,这是我的毕业论文的题目。为了介绍这个想法和建立前提,我抽象了一些技术细节。对于更倾向于技术的人,我添加了一些有用的文章列表。另外,如果你想知道更多,请打电话给我!更多内容请关注我:)
https://anweshrm.medium.com/ https://www.noldus.com/blog/what-is-rppg https://pubmed.ncbi.nlm.nih.gov/17322588/
参考资料:
- “光电容积描记图”,https://en.wikipedia.org/wiki/Photoplethysmogram
- “全息激光多普勒成像的光电容积描记法”,用户名:Micatlan,维基百科授权 CC BY-SA 4.0
- 基于视频的使用卷积注意网络的生理测量。2018 年欧洲计算机视觉会议(ECCV)会议录
- Tim Sheerman-Chase 的“EEG Brain Scan”由 CC 2.0 授权。要查看许可证副本,请访问 https://creativecommons.org/licenses/by/2.0/
- Wesley Fryer 的“Rachel on Facetime”获得 CC BY-SA 2.0 的许可。要查看此许可证的副本,请访问https://creativecommons.org/licenses/by-sa/2.0/
- https://www.noldus.com/blog/what-is-rppg,r-PPG 的科学博客
为金球奖做模特
实践教程
或者,为什么莎拉·保罗森在 2021 年被低估了?
随着今年金球奖奖的临近,我很想知道电视奖存在什么样的模式,相同的男演员、女演员和节目可以连续多年获得奖项提名(一个节目的多个季节)。电视剧会在一年内很热,但在接下来的几季里很难继续获奖吗?
为了找到答案,我从维基百科收集了 1972 年至 2020 年的金球奖提名和获奖数据,以及同期的艾美奖获奖数据,并使用 IMDbPY 收集了 IMDb 所有提名节目的节目长度和 IMDb 评分。我关注的是最佳剧情类和音乐/喜剧类电视剧,以及电视类的四位男女主角(剧情类各一位,音乐/喜剧类各一位)。我按季度对 IMDb 收视率进行了平均,并通过使用颁奖前两年开始的季度平均值来链接到颁奖数据(颁奖通常在 1 月举行,对于上一日历年的电视节目,这通常意味着上一年中期开始的上半季,以及上一年的下半季,如果有的话)。使用的代码可以在这里找到。
演出季节
平均而言,电视节目播出的哪几年最有可能获得奖项提名?作为参考,我采用了金球奖提名数据中所有节目的总长度分布(这种频率分布在单个季节有一个峰值,在 4-7 个季节的长度上有另一个更宽的峰值),并计算了如果提名者每年从这个集合中随机抽取,我们预计会看到的季节数组合:这显示为下面的黑线和黄线。

作者图片
情节显示,第一季,其次是第二季和第三季,被提名的次数比预期的要多,第一季被提名的次数是预期的两倍,而第六季到第十季没有被提名,也没有像它们在电视上出现的频率那样经常获奖。相当多的电视剧长达六季或更多季,但这些年很少被提名。
以前的提名
在这六个类别中,53%的获奖者是第一次被提名,超过了第一次被提名的 41%。这与前几季更有可能获奖紧密相关:第一季自然比后几季更有可能获得第一次提名。

作者图片
看看第二季及以上,我们发现第一次被提名的人有大约平均(20%)的机会获胜,对于重复被提名的人,这种机会取决于前一年发生的事情。对于系列剧来说,如果前一年的提名导致了获奖,这将获奖的可能性从 10%增加到 29%,但对于男演员和女演员来说,这种影响更加温和。
在表演类别中,拥有一个以前的胜利,无论它是否发生在过去的一年,事实上似乎是进一步成功的障碍。有过一次获奖经历的男女演员只有 12%的胜算,这在统计上大大低于样本量的预期:

作者图片
对于系列,可以看到类似但较弱的效果。然而,那些取得两次或更多胜利的人,平均有 20%的机会获胜,尽管这一提名通常是针对第三季或更久的剧集。也就是说,在过去的十年里,在表演类别中,有 18 位被提名者已经获得了至少两个奖项,只有两个获得了成功(凯尔塞·格拉玛在 2012 年获得了与他之前获得的奖项不同的奖项(老板),克莱尔·丹尼斯在 2013 年获得了与第二次和第三次(国土安全)不同的奖项(不包括她 2010 年获得的迷你剧奖)。
IMDb 收视率
一个节目在 IMDb(最相关的一季)上的平均评分只能略微预测获胜:获胜的节目比其他被提名者的平均评分高的情况只有 56%。

作者图片
表演奖的重要性甚至更低(只有 54%的时候,获胜者的节目比其他节目的平均收视率高)。
公布的 IMDb 收视率(衡量节目粉丝受欢迎程度的一个可能指标)确实显示了历史效应(收视率高的节目更有可能获胜),但这种效应自 2010 年以来几乎不存在,所以我认为这里发生的情况是,自 IMDb 存在以来,前几十年的经典(即获奖)节目近年来获得的收视率高于未获奖且不太被人记住的节目。
姓名字母顺序
这听起来可能令人惊讶,但成员投票时被提名人的名字出现的顺序重要吗?这种效应已经在的政治选举中被证明存在。

作者图片
在四个电视表演类别中,姓氏顺序确实有统计上的显著影响:出现在(通常)五个候选人名单上的第一个或最后一个姓氏比预期多赢得大约 25%,选票上第三个(中间)位置的候选人比预期少赢得大约 30%。位置 1 到 5 的中奖号码的对称性增加了人们对这确实是一个真实效应的信心:选民似乎对选择他们从候选名单中读到的名字或姓氏有心理偏见。
在类似的主题上,我也检查了男演员/女演员的名字长度,认为短或长的名字可能更容易记住;从历史上看,人们对名字最短或最长的候选人有轻微的偏好,但在统计上并不显著。
最佳剧集和最佳表演提名
在表演提名中,有一个优先选择是那些其表演也在当年被提名的人:

作者图片
这可能表明,表演的整体质量往往会增强人们对表演的看法。因果关系也可能反过来发生:可能是最佳表演“使节目更好”,因此获奖的男女演员比其他被提名者更有可能将他们的节目拖到提名。
为 2021 年建模
我们可以通过拟合一个模型来预测被提名者在给定的类别和年份中是否会成功,从而综合这些影响。我在一个逻辑回归框架中使用了上述特性和其他几个特性;我安装了两个独立的模型,一个用于表演类,一个用于连续剧类。这些模型的组合显示出大约 0.45 的准确度,和 0.20 的 Brier 分数;也就是说,该模型可以在 45%的情况下从五名被提名者中正确识别出最有可能的获胜者,这比随机猜测的 20%要高得多。表演类的表现比表演类的好一点。
上面没有提到的最重要的特征是候选人是否在六个月前获得了艾美奖:有时,但不总是,艾美奖是根据同一季的节目颁发的,在这种情况下,它提供了一个直接的“批评意见”,而这是该模式所缺乏的。通过拟合模型得出的一些观察结果:
- 六个月前获得艾美奖是这部电视剧成功的一个强有力的指标,但对男演员/女演员来说却不那么重要。同样,前一年赢得了同样的金球奖奖对这个节目来说是好事,但是不是对男演员/女演员有帮助吗?
- 随着剧集和男演员/女演员过去的提名或获奖次数的增加,他们获奖的可能性越来越小,但是,特别是,以前的一次获奖对提名者不利。
- 一部剧集在同一年获得表演类提名的几率会增加。男演员或女演员的机会因该系列提名而增加,但由于除了他们之外的其他表演提名的存在而减少。
- 成为名单上的第一个或最后一个名字对男演员或女演员的机会的好处大致相当于前一年获得艾美奖奖。
这对 2021 年金球奖电视奖意味着什么?我们的组合模型的准确性不是特别高,所以我们不应该期待谁是可能的赢家的深刻见解。尽管如此:
- 对于的电视音乐/喜剧,该模型更倾向于 Schitt 的 Creek (36%的可能性),因为它去年赢得了艾美奖奖,并且没有之前的获奖记录;然而,在第六季对它不利。博彩市场目前同意这一预测。
- 对于电视剧,这位模特更喜欢欧扎克(39%),因为他是第一次被提名,有两次表演提名,并且有很高的 IMDb 评分。由于其提名和获胜历史,皇冠几乎没有获胜的机会,但博彩显示皇冠是当前的热门,领先于欧扎克。
- 对于音乐/喜剧类男演员,模特青睐杰森·苏戴奇斯(特德·拉索;39%),因为节目也有提名,这是第一次提名,而且是针对第一季的。这个类别的赌注目前似乎在苏代基斯和尤金·利维之间分裂,因为 Schitt 的小溪:模型降低了他的机会,主要是因为该剧在第六季。
- 对于音乐/喜剧类最佳女演员,模特青睐莉莉·柯林斯(34%),凯瑟琳·欧·哈拉第三(20%)。人们似乎普遍认为奥哈拉会赢,因为史特克里克。两位女演员都是首次获得提名;该模型更重视柯林斯的节目在第一季中的重要性,而不是奥哈拉去年在艾美奖的胜利。
- 对于剧情片中的男演员,该模型对获胜者没有信心(去年的艾美奖奖得主杰瑞米·斯特朗不在候选名单上),但预测梅森探案集的马修·瑞斯略微被看好(30%),部分原因是他的名字按字母顺序排在最后,还因为他以前没有获奖。从博彩市场来看,这似乎是六个类别中最不确定的。
- 对于电视剧中的女演员,模特非常有信心(59%)莎拉·保罗森会赢,因为该剧(Ratched)也获得了提名,提名是针对第一季的,她以前没有赢过(忽略她在迷你剧类别中的胜利),她的名字按字母顺序排在最后。博彩市场认为奥莉薇娅·柯尔曼或艾玛·科林会赢得这个奖项,但由于同一部剧有两个表演提名者的“分裂投票”效应,该模型低估了他们两人的机会。
因此,该模型似乎在六个类别中的至少三个类别中与博彩市场保持了合理的一致,但可能会系统性地低估两个长期运营的节目,即 Crown 和 Schitt's Creek。这凸显了节目在第四或第六季获得成功是多么不寻常!)季节。结果将于 2 月 28 日在揭晓!
LSTM 可以用来预测太阳黑子的数量吗?
我将使用时间序列分析来预测每月太阳黑子的数量。将深入探讨的模型是 ARMA 和 LSTM 模型。

美国宇航局在 Unsplash 拍摄的照片
介绍
首先:什么是太阳黑子?太阳黑子是太阳光球层上的一种暂时现象,看起来比周围的区域更暗。我选择太阳黑子数据集进行时间序列分析的原因是,太阳黑子以 11 年的太阳周期出现,这意味着我们应该看到数据的季节性成分。我将使用两种不同的方法来模拟季节性趋势,ARMA 模型和 LSTM 模型。
太阳黑子的出现和消失周期为 11 年

图片来自[ 维基百科
将使用的数据是从 1749 年到 2013 年,是每个月的月平均值。用于分析的数据可从 kaggle 中找到。下面我有一小段数据。

图片由[ 作者 ]提供
时间序列分析的第一步是绘制数据和自动校正图。我们这样做是为了了解任何潜在的趋势或季节性,通过识别任何趋势,可以使用适当的数学模型。
太阳黑子图
我首先简单地从太阳黑子数据中画出一个 600 个数据点的区块。

图片由[ 作者 ]提供
从图中可以看出强烈的季节性成分。然而,每个波峰和波谷的大小不同,这将限制可以使用的数学模型。例如,使用正弦模型可能不会产生最佳结果。

图片由[ 维基百科 ]提供
移动平均或数据科学方法将产生最佳的建模结果
自相关
在我开始对时间序列建模之前,我首先制作自相关图,以了解数据中是否存在任何潜在的模式。对于时间序列建模,我希望看到一个没有模式的平稳时间序列。
请注意,自动校正是数据点与其自身滞后版本的相关性。

图片由[ 作者 ]提供
从自动校正图来看,存在很强的季节性成分。我们需要细分/分解季节性因素。
时间序列分解
对于任何给定的时间序列,可以分为三层。将任何时间序列[Xt]视为三个组成部分可能会有所帮助。下面我展示了一个加法模型的数学公式。


在我们的例子中,只有季节性因素。考虑到这一点,我们可以应用差分法。如果一个 11 年的太阳周期有季节性成分,那么我们可以通过减去 11 年前的值来消除它。
下面是减去季节性趋势的 python 代码。
from matplotlib import pyplot
X = df.values
diff = list()
cycle = 132
for i in range(cycle, len(X)):
value = X[i] - X[i - cycle]
diff.append(value)
pyplot.plot(diff)
pyplot.title('Sunspots Dataset Differenced')
pyplot.show()

图片由[ 作者
从差分图上可以看出,时间序列中仍然存在一定的季节性。接下来,我试图用一个 11 年循环的多项式来模拟季节性。

图片由[ 作者提供
二阶多项式在模拟太阳黑子时间序列方面表现不佳。
我们需要解决时间序列的一些关键问题:
- 每个 11 年周期的震级不同
- 这些周期平均相隔 11 年,但并不准确。
自回归移动平均线将很好地解决这些问题。一个不利因素是,未来的预测在展望未来的日子里会受到限制。本文将深入探讨的另一种方法是 LSTM。
LSTM 在正向预测中表现良好,并解决了 ARMA 模型的衰落问题。
自回归移动平均模型
ARMA 模型是时间序列建模的有力工具。ARMA(p,q)定义如下:其中 p 是自回归多项式的阶,而 q 是移动平均多项式的阶。
自回归移动平均(ARMA)模型如下所示

ARMA 模型
在 python 中,我们能够使用 ARIMA 函数来拟合参数( p,q )。
函数ARIMA()接受两个主要参数
x:单变量时间序列order:长度为 3 的向量,指定 ARIMA(p,d,q)模型的阶数
这里, d 表示时间序列的顺序。例如,如果存在底层线性趋势,则 d 将等于 1。
当拟合 ARMA 模型时,我们设置 d 等于 0。
from statsmodels.tsa.arima_model import ARIMA
y = np.array(RNN_data.value)
model = ARIMA(y, order=(1,0,1)) #ARMA(1,1) model
model_fit = model.fit(disp = 0)
print(model_fit.summary())
# Plot residual errors
residuals = pd.DataFrame(model_fit.resid)
fig, ax = plt.subplots(1,2)
residuals.plot(title="Residuals", ax=ax[0])
residuals.plot(kind='kde', title='Density', ax=ax[1])
plt.show()
# Actual vs Fitted
cut_t = 30
predictions = model_fit.predict()
plot = pd.DataFrame({'Date':date,'Actual':abs(y[cut_t:]),"Predicted": predictions[cut_t:]})
plot.plot(x='Date',y=['Actual','Predicted'],title = 'ARMA(1,1) Sunspots Prediction',legend = True)
RMSE = np.sqrt(np.mean(residuals**2))
ARMA(1,1)模型产生单日预测。换句话说,我们提前一天预测。
ARMA 模型的一个缺点是向前预测的天数。

图片由[ 作者 ]提供
LSTM 模型
LSTM 是 RNN 家族中的典范。它可以被认为是一个时间序列数据的神经网络。
LSTM 与神经网络的不同之处如下:LSTM 不是单次输入到神经网络中,而是用前 x 天进行输入。
你可能会开始理解为什么这对太阳黑子数据集有用。通过使用以前的数据作为输入,该模型将试图捕捉 11 年周期的季节性信息。
LSTM 背后的主要思想是遗忘之门,信息可以通过它传递给下一个神经网络。
这里的绿色块代表回望日,例如,如果我们的 LSTM 模型使用 3 天的回望期,下图将代表我们模型中的一天。

图片来自[ 维基百科
对于 LSTM 模型,模型结构由输入数据的结构和预测特征决定
用于回顾的前几个月的数量和输入特征的数量将决定需要权重和偏差的神经元的数量。
Python 中的 LSTM 建模
我将使用 Keras 库和包来创建 LSTM 模型。
创建输入数据结构是 Python 中 LSTM 建模的最重要步骤
第一步是简单地将我们的数据分成训练和测试数据集。
split = 0.7
#Split into test and training set (70/20 split)
length = len(dataset)
train_length = round(length*split)
test_length = len(dataset) - train_length
train = dataset[0:train_length,:]
test = dataset[train_length:length,:]
让我们介绍一下单日 LSTM 预报的预处理功能。
def preprocessing(training_set_scaled,n,train_length,days):
y_train = []
for i in range(days, train_length -1 ):
y_train.append(training_set_scaled[i, 0]) # note that our predictor variable is in the first column of our array
y_train = np.array(y_train)
X = np.zeros(shape=[train_length - (days+1),days,n],dtype=float)
for j in range(n):
for i in range(days, train_length-1):
X[i-days,:,j] = training_set_scaled[i-days:i, j]
return X, y_train

图片由[ 作者
这里我们的 numpy 数组的形状如下
- 我们训练数据中的天数
- 3: 我们回顾预测的天数
- 1: 用于预测的特征数量
接下来我介绍 LSTM 模型的结构
# Initialising the RNN
regressor = Sequential()
# Adding the first LSTM layer and some Dropout regularisation
regressor.add(LSTM(units = nodes, return_sequences = True, input_shape = (new.shape[1], new.shape[2])))
regressor.add(Dropout(0.2))
# Adding a second LSTM layer and some Dropout regularisation
regressor.add(LSTM(units = nodes, return_sequences = True))
regressor.add(Dropout(0.2))
# Adding a third LSTM layer and some Dropout regularisation
regressor.add(LSTM(units = nodes, return_sequences = True))
regressor.add(Dropout(0.2))
regressor.add(LSTM(units = nodes))
regressor.add(Dropout(0.2))
# Adding the output layer
regressor.add(Dense(units = t)) # this is the output layer so this repersetns a single node with our output value
# Compiling the RNN
regressor.compile(optimizer = 'adam', loss = 'mean_squared_error')
# Fitting the RNN to the Training set
model = regressor.fit(new, y_train, epochs = 50, batch_size = 22)
LSTM 单日远期预测

图片由[ 作者 ]提供
LSTM 模型预测得相当好,然而,这部分只是因为我们一次预测未来的一天。接下来,我们来看看多日远期预测。
Python 中的多日 LSTM 建模
让我们介绍一下多日 LSTM 预报的预处理功能。
- 回顾是 3 天
- 远期预测是 5 天
我用之前的 3 天来预测未来 5 天的时间。
def preprocessing_muli(training_set_scaled,n,t,train_length,days):
days_length = train_length - t
y_train = np.zeros(shape=[days_length-t,t],dtype=float)
#y_train[:,0] = training_set_scaled[t:days,0]
#y_train = np.zeros(shape=[days_length - t,days],dtype=float)
for i in range(t):
y_train[:,i] = training_set_scaled[i+t:days_length+i,0] # Here each column repersents a time step.
X = np.zeros(shape=[days_length-t,days,n],dtype=float)
for j in range(n):
for i in range(days):
X[:,i,j] = training_set_scaled[i:days_length-t+i, j]
return X, y_train
损失函数
我探索了拟合 LSTM 模型时的损失函数,以了解模型是否收敛以及收敛速度有多快。损失函数是我们的预测与响应变量之间距离的度量。下面是一个使用 MSE 拟合线性回归模型的例子。理解从每个蓝点到线的距离是测量的,然后平方,最后除以数据点的总数。

图片由[ 维基百科 ]提供
为了简单起见,我将使用均方差作为损失函数。均方误差(MSE)的计算公式如下

根据 MSE 公式,hat 代表变量 y 的预测值或估计值。在数学中,变量上方的 hat 代表给定随机变量的估计值。 n 代表我们估计的总数据点的数量。上图显示了如何计算二维简单线性回归模型的 MSE。
多日 LSTM 模型的 MSE 损失函数的结果如下所示。

图片由[ 作者
3 天回顾和 5 天预测的结果。

图片由[ 作者提供
Python 中的 LSTM -太阳黑子 11 年周期预测
接下来,我研究 LSTM 模型能否预测未来一个完整的周期。对于我们的数据集,每个数据点代表一个月,我们知道我们应该预期每 11 年一个太阳黑子周期,所以我们将向前预测未来的 132 个数据点。然而,为了捕捉下一个周期的一部分,我将使用 160 天进行预测。
- 回望是 200 天
- 远期预测是 160 天

图片由[ 作者 ]提供
LSTM 确实很好地捕捉了时间序列的季节性或周期性成分,但是它会偏移,并且波峰和波谷不对齐。

图片由[ 作者 ]提供
试图预测未来整个 11 年的周期并没有产生最好的结果,所以接下来我试图预测未来半个周期或大约 60 天。
Python 中的 LSTM 太阳黑子半周预测
最后,我研究了 LSTM 模型是否可以预测提前半个周期。对于我们的数据集,每个数据点代表一个月,我们知道我们应该预期每 11 年一个太阳黑子周期,所以我们将向前预测未来约 60 个数据点。
- 回望是 120 天
- 远期预测是 60 天

图片来自[ 作者
在这里,我创建了四个块,每个 60 天,用于预测测试数据集。
接下来我计划未来 60 天。

图片由[ 作者 ]提供
结论
本文中展示的一切都是使用真实数据来预测太阳黑子数据集的。ARMA 和 LSTM 都得到了深入的评估。在多日远期预测方面,LSTM 远远优于 ARMA 模型,ARMA 模型可以捕捉季节性成分,而 LSTM 不能正确识别新周期的开始和结束。在预测太阳黑子数量时,给 LSTM 模型增加额外的特征可能会产生更好的结果。
我建议使用 LSTM 模型来模拟太阳黑子数据集。
关于我自己的一点点
我最近在加拿大多伦多的瑞尔森大学获得了应用数学硕士学位。我是金融数学组的一员,专业是统计学。我深入研究了波动建模和算法交易。
我之前在庞巴迪宇航公司担任工程专业人员,负责与飞机维护相关的生命周期成本建模。
本人积极训练铁人三项,热爱健身。
如果您对以下主题有任何建议,请告诉我
- 数据科学
- 机器学习
- 数学
- 统计建模
领英:https://www.linkedin.com/in/ethanjohnsonskinner/
推特:@Ethan_JS94
模拟骰子的概率分布

统计分析和数学建模教程
如果你曾经玩过卡丹游戏,你会很快意识到当掷出两个骰子时,数字 7 是很常见的!这就引出了一个问题,内容如下:
用两个 6 面骰子掷出和 7 的真实概率是多少?而且,用 n 六面骰子掷出任意数字的和的概率是多少?
以下是我为寻找答案所做的工作。
实验(滚动骰子)
让我们先运行几个骰子实验(即。简单地掷骰子)。幸运的是,我们可以使用计算机在几分钟内产生数百万次骰子滚动模拟,而不是自己滚动几年。


图 1:每个分布运行 100,000 次滚动模拟得到的 1 个和 2 个骰子的概率分布(左上和右上)。通过对每个分布运行 100,000 次滚动模拟,动画显示 1 到 20 个骰子的概率分布(下图)。图片作者。
通过目测图 1 中的实验数据,我们可以看到随着骰子数量的增加,一个熟悉的形状出现了,一个 钟形曲线,也被称为正态或高斯分布。我们可以这样做,试着用高斯曲线来拟合实验数据。高斯分布在数学上表示为

。两个参数μ和σ对应于概率分布的均值和标准差,它们分别定义了分布的中心位置和宽度。但是符合我们的 n- dice 实验数据的这些参数的最佳值是什么呢?嗯,我们可以通过统计分析推断最有可能的参数值。从图 1 中我们可以看到,概率分布是对称的。利用这种对称性,我们可以通过简单地定位每个分布的最大位置来定义实验数据的平均值。下图 2 是随着骰子数量的增加,这些最大位置的曲线图。从图中可以看出,平均值 μ 与骰子数 n 之间存在一个线性相关关系,其中的线 μ=3.5n 。****

图 2:绘制每个概率分布的平均值(μ),作为骰子数量 n 的函数。该图显示了线性相关性,最佳拟合线 μ=3.5n 也用红色绘制。图片作者。
现在,有了平均值,我们可以使用最小二乘法的方法来找到与实验数据的最佳拟合高斯相对应的标准差** σ的值。**
最小二乘法(识别最可能的 σ值)
最小二乘法定义了一个度量标准来比较两组数据的相似程度,该度量标准称为均方误差 (MSE) ,其数学表达式为

。 MSE 是对于给定的σ值,在实验数据 (Xᵢ ) 和高斯拟合 (P(xᵢ)) 之间的均方差,其中 n 表示直方图数据中面元的数量, i 表示每个面元。因此,找到最小化 MSE 的σ值对应于最小化高斯拟合和实验数据 ie 之间的差异。最合适的。图 3 显示了识别四个骰子的最可能参数值(μ= 14,σ = 3.36)的最小化过程。

图 3:四骰子情况下实验数据的高斯拟合动画(左),高斯拟合的均方误差值的相应动画(右),其中红色标记表示左侧的当前拟合。最小化损失的拟合(最佳拟合)具有参数值μ = 14 和σ = 3.36。图片作者。
使用均方误差来推断一系列骰子最可能的σ值,我们可以绘制σ作为骰子数量的函数 n. 图 4 显示了σ( n),我们可以看到,实验数据描绘出了一条 √n 外观曲线。同样,使用最小二乘法我们可以找到与实验数据最佳拟合的 √n 曲线,这导致**σ(n)= 1.75√n**。

图 4:绘制每个概率分布的标准偏差(σ)作为骰子数量 n 的函数。该图显示了骰子数量与产生的标准偏差之间的相关性,确定了平方根关系。找到了σ( n) = 1.75√n 的最佳拟合。图片作者。
因此,给定n-骰子,我们现在可以使用 μ(n) = 3.5n 和*σ(n)= 1.75√n***来预测任意数量的骰子 n. 的全概率分布。下面的图 5 和图 6 显示了 n=1 到 n=17 的这些拟合。

图 5:骰子从 1 到 15 的最佳拟合(使用最小二乘法)。其中蓝色直方图数据是模拟结果,红线是高斯拟合。图片作者。

图 6:使用我们的近似值 μ(n) = 3.5n 和**【σ(n)= 1.75√n**的高斯拟合,用于 1 到 20 之间的 n 。图片作者。
到目前为止,我们已经表明,通过非常快速的经验努力,我们可以使用高斯函数非常好地预测数据。然而,尽管成功地拟合了更大的 n 种情况,我们的高斯拟合仍然只是一种近似。这就是为什么对于较小的 n 和前两种情况(1 和 2 个骰子)我们的高斯近似不能很好地捕捉,我们的拟合会更差。让我们看看我们是否能推导出给定总和的概率的精确解***n骰子**!*
警告:这将涉及一些数学…
推导精确解
让我们首先看一个这个问题的简单版本的例子。让我们考虑一个双面骰子,这是硬币的本质。所以对于硬币,我们有两种可能的结果正面或反面。现在,假设我们有三个硬币,我们会问如果我们抛这些硬币会有什么结果?可能的结果如下表 1 所示。

表 1:投掷 3 枚硬币的可能结果。h =正面,T =反面。
当硬币是可区分的时有 8 种可能结果,当我们认为硬币是不可区分的 4 种可能结果。这种不可区分的分布被二项分布所捕获

。这里,术语 H 表示 (H,H,H) 和 H T 表示观察到两个头和一个尾巴的情况,系数表示产生给定观察的组合的数量,即。1 对于 H 和 T,和 3 对于 H T 和 T H. 二项式的每一个系数都可以通过一个简洁的小方程来确定

其中 k 表示翻转的正面数量,而 (n-k) 是反面数量。例如,2 头 1 尾(n=3,k=2)

。将二项式系数与翻转每个组合的概率结合起来,就给出了观察的概率


**
,其中 H 代表翻 a 头的概率 (H=1/2) , T 代表翻 a 尾的概率 (T=1/2) 。 n 硬币和翻转 k 正面和 (n-k) 反面的一般二项式概率分布由下式给出

。n 个硬币的完全二项式 (H+T)ⁿ 可以表示为一个数列

。因此,有了这些知识,现在让我们回到考虑 6 面骰子,其中我们将每一面标记为 xⁱ (i 对应于该面上的数字)。与双面硬币类似, n 许多六面骰子可以用多项式来完全描述

。注意,我们可以用指数来表示每个可能结果的总和。例如,x x =x。这对我们很有用,因为现在我们有一种方法来确定每个组合的总和。比如xx⁶= x⁶x = x x⁵= x⁵x =x⁴x = x x⁴=x⁷。这些都代表7、的一个总和,因此用 2 个骰子掷出 7 的概率是掷出这些概率的组合:(1,6)、(6,1)、(2,5)、(5,2)、(4,3)、(3,4)。
如在硬币场景中,多项式 f(x,n) 的每个可能因子的系数乘以得到该因子的概率(即 (1/s)ⁿ,其中 s =骰子面数 ) 告诉我们掷出那个因子的概率。我们现在想用这个来告诉我们,作为骰子 n 的函数,得到任何给定总数 T 的概率是多少。我们可以将 f(x,n) 表示为

其中 s 表示骰子的边数 (s=6) 。注意,这里的和是一个几何级数,几何级数也可以表示为

所以让我们用这个替代表达式来代替总和

。由此产生的两项是二项式,并且可以将扩展为二项式级数。我们早在考虑硬币时就看到,二项式可以展开成级数形式,如下所示

所以(1-xˢ)ⁿ系列中的就是

。现在 (1-x)⁻ⁿ 展开有点不直观,因为指数为负。我们可以通过考虑函数的 Maclaurin 级数展开来展开它,这是围绕点 (x=0)的泰勒展开。一般函数 g(x) 的泰勒展开式为

。使用链式法则我们可以找到 g(x)=(1-x)⁻ ⁿ 的导数

。按照数列的模式,我们可以概括为lg(x = 0)的ᵗʰ导数**,

。所以,**全泰勒级数的g(x)* 就是***

并在 x=0 左右展开

。把从(1-xˢ)ⁿ和 (1-x)⁻ ⁿ 导出的级数表达式放在一起,我们就有了一个展开的级数表达式来表示完整的多项式

。回想一下, n- 骰子的总和是 x 的指数,所以收集这里所有的 x 项,总和 T 由 T = n+sk+l 给出。让我们将 T 代入到我们的级数表达式中来代替 f(x,n) ,目的是用 l 代替 l = T-n-sk

,清除分子以获得

。但是条件 T-sk-n > 0 必须永远为真也就是说T-nsk((T-n)/s)>k,这个不等式翻转过来就是 k < ((T-n)/s) ,所以这就是求和 ie 的上限。

。这个替换意味着第二次求和,

现在是多余的,因为在求和中唯一变化的参数是 k ,其已经在第一次求和中被考虑。
记住骰子是离散的,所以这意味着 ((T-n)/s) 必须只取整数实数值,所以我们必须应用底数函数⌊⌋ ,它将 ((T-n)/s) 的结果舍入到小于 ((T-n)/s)的最大整数。下面的等式允许我们确定系数集合,该集合产生 T 与n s 双面骰子的总和。

假设骰子是公平的,我们可以将掷骰子总数 T 的概率表示为多项式系数和每个骰子面的概率的的次方的 n、的乘积

。这是我们一直期待的功能!使用这个函数 P(n,s,T) 我们可以绘制出n-骰子的精确分布,这些在图 7 和图 8 中显示为绿线。

图 7:前三个概率分布,1-骰子(左)、2-骰子(中)和 3-骰子(右),由我们刚刚推导出的解析解精确捕捉。图片作者。

图 8:n = 1 至 n=15 骰子的绿色和高斯拟合理论预测(解析解)与实验数据的比较。这些图表明理论预测比高斯预测更好(虽然差不了多少!).图片作者。
从图 7、图 8 和图 9 中可以看出,我们的解析解比之前的高斯近似更符合该数据。我们现在可以捕捉精确的概率分布,甚至对于 n=1 和 n=2 的情况!

图 9:为 1 到 20 之间的 n 绘制的一组精确概率分布。图片作者。**
结论
有些人可能会说,骰子概率是普通的数学问题,他们很可能是对的!然而,我们在这里涵盖的内容包含了所有数学建模和数据科学问题的核心的三个重要教训。首先,我们展示了计算机产生和处理实验数据的能力。例如,我们在几分钟内制作了数百万个骰子滚动模拟。其次,我们论证了统计推断和回归分析的有效性。我们通过将实验数据估计为高斯分布(代理模型)来说明这一点。这种近似允许我们使用简单的回归形式(最小二乘法* ) 来找到最佳拟合高斯分布的参数。然后,我们扩展并抽象了我们的代理模型,以发现随着骰子数量的增加,参数是如何变化的。有了这些信息,我们可以在不运行模拟的情况下,推断预测大量骰子的分布。第三,最后,我们深入到问题的数学中,为概率分布导出了一个精确且通用的解。这些精确的概率分布和我们更简单的高斯近似之间的相似性进一步强调并证明了统计推断和回归分析在建模数据中的有用性。***
感谢阅读,现在回到卡坦的游戏!
骰子 python 笔记本的概率:
查看访问笔记本的链接:https://gist . github . com/tley sh/cdcef 64 cc 271 cc 8157969 b 22 a 54 aab 0e
参考文献
[1]比亚乔 HT。数学概率导论。JV Uspensky。第九卷,第 411 页。30 岁。1937.(麦格劳-希尔)。数学公报。1938 年 5 月;22(249):202–4.
[2] 魏斯斯坦,埃里克 W. 《骰子》来自 MathWorld —一个 Wolfram Web 资源。https://mathworld.wolfram.com/Dice.html
[3] 魏斯泰因,埃里克 W. “麦克劳林系列。”来自MathWorld—Wolfram 网络资源。https://mathworld.wolfram.com/MaclaurinSeries.html
用 Cytoscape 对 TigerGraph Spotify 数据建模
使用细胞图像可视化创建仪表板

记录,看起来与顶点相似(来自 Pixabay 的图像)
注:使用的 Spotify 数据可以在这个博客中找到。
概观
目标
有兴趣利用 TigerGraph 的图形数据库和 Cytoscape 令人惊叹的可视化功能吗?在这篇博客中,我们将使用 Cytoscape 对 TigerGraph 中的 Spotify 数据进行建模,特别是 Plotly Dash 的 Cytoscape 包。然后,我们将把这些整合到一个很酷的交互式仪表板中。Cytoscape 是为数据及其连接建模而构建的,让我们用 Cytoscape 对连接的 TigerGraph 数据建模,以便轻松地将我们的可视化集成到 Python 应用程序中。
工具
- tiger graph Cloud:tiger graph 是世界上“唯一可扩展的企业级图形数据库”我们将特别使用 TG Cloud,这是一种开始使用 TigerGraph(甚至通过 GraphStudio 提供无代码解决方案)的简单方法,并免费托管您的图形!
- Cytoscape : Cytoscape 是一个"开源软件平台,用于可视化复杂网络,并将这些网络与任何类型的属性数据集成在一起。具体来说,我们将使用与 Plotly Dash 的集成来建模我们互连的 TigerGraph 数据。
第一部分:准备你的图表
第一步:确保图表准备就绪
首先,让我们确保我们的图表已经准备好运行。转到https://tgcloud.io/app/solutions并确保您可以在“状态”栏下看到一个绿点和“就绪”。

确保状态为“就绪”!
如果它有一个带有“停止”字样的蓝点,请单击“解决方案操作”,然后从下拉列表中单击“启动”。
等到状态为绿色并显示“就绪”(这可能需要几分钟),然后您就可以继续前进了!
第二步:连接到您的解决方案
首先,让我们再次从我们的 Colab 笔记本连接到 TG Cloud 上的解决方案。首先,我们将安装并导入 pyTigerGraph。
!pip install pyTigerGraphimport pyTigerGraph as tg
接下来,我们将创建 TigerGraphConnection。记住用它们各自的值替换子域和密码。
conn = tg.TigerGraphConnection(host="https://SUBDOMAIN.i.tgcloud.io/", password="PASSWORD", graphname="SpotifyGraph")conn.apiToken = conn.getToken(conn.createSecret())
我的 TigerGraphConnection 将如下所示:
conn = tg.TigerGraphConnection(host="https://spotifygraph.i.tgcloud.io/", password="tigergraph", graphname="SpotifyGraph")conn.apiToken = conn.getToken(conn.createSecret())
一旦它运行,我们就准备好了!
第二部分:准备仪表板
第一步:导入库
首先,我们将安装并导入我们的库。第一个库将是 pandas,它允许我们将查询结果格式化为 cytoscape。
import pandas as pd
接下来,我们将导入所有的标准 dash 库。
!pip install -q jupyter-dashfrom jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
最后,我们将安装这个博客的专用库:dash_cytoscape,Plotly Dash 的 cytoscape 库。
!pip install dash_cytoscapeimport dash_cytoscape as cyto
太好了!现在让我们创建应用程序的布局!
步骤二:创建应用程序布局
首先,让我们创建应用程序,初始化一个 JupyterDash 仪表板。
app = JupyterDash(__name__)
接下来,我们将组件添加到我们的仪表板。
app.layout = html.Div([
html.H1("CytoTG + Spotify"),
html.H2("Style"),dcc.Dropdown(id = "layout", options=[
{"value": "preset", "label": "preset"},
{"value": "random", "label": "random"},
{"value": "grid", "label": "grid"},
{"value": "circle", "label": "circle"},
{"value": "concentric", "label": "concentric"},
{"value": "breadthfirst", "label": "breadthfirst"},
{"value": "cose", "label": "cose"},
], value="circle", clearable=False),html.H2("Data"),dcc.Dropdown(id = "opt", options=[
{'value': "Artist", 'label': "Artist"},
{'value': "Playlist", 'label': "Playlist"},
{'value': 'Genre', 'label': 'Genre'},
{'value': 'Song', 'label': 'Song'}
], value="Playlist", clearable=False),dcc.Dropdown(id = "artist", value="Altar", clearable=False),cyto.Cytoscape(id = "cyto", layout={'name': 'circle'}, style={'width': '100%', 'height': '400px'}),cyto.Cytoscape(id = "scape", layout={'name': 'circle'}, style={'width': '100%', 'height': '400px'}),])
我们来分析一下。我们将从标题和副标题开始。
html.H1("CytoTG + Spotify"),
html.H2("Style"),
然后,我们将添加不同布局类型的下拉列表。
dcc.Dropdown(id = "layout", options=[
{"value": "preset", "label": "preset"},
{"value": "random", "label": "random"},
{"value": "grid", "label": "grid"},
{"value": "circle", "label": "circle"},
{"value": "concentric", "label": "concentric"},
{"value": "breadthfirst", "label": "breadthfirst"},
{"value": "cose", "label": "cose"},
], value="circle", clearable=False),
接下来,我们将添加另一个副标题。接下来的两个下拉列表将是顶点类型和每个顶点类型的值。
html.H2("Data"),dcc.Dropdown(id = "opt", options=[
{'value': "Artist", 'label': "Artist"},
{'value': "Playlist", 'label': "Playlist"},
{'value': 'Genre', 'label': 'Genre'},
{'value': 'Song', 'label': 'Song'}
], value="Playlist", clearable=False),dcc.Dropdown(id = "artist", value="Altar", clearable=False),
最后,我们将添加两张细胞图。
cyto.Cytoscape(id = "cyto", layout={'name': 'circle'}, style={'width': '100%', 'height': '400px'}),cyto.Cytoscape(id = "scape", layout={'name': 'circle'}, style={'width': '100%', 'height': '400px'}),
完美!现在格式已经准备好了,让我们用回调来进行交互。
第三部分:撰写回访
回调 I:更新布局
我们的第一个回调很简单:我们将获取布局下拉列表的值,并将其分配给细胞图。因此,如果布局下拉列表的值为“预设”,细胞图的布局值将为{“name”:“preset”}如果布局下拉列表的值改变,细胞图布局的值也会改变。
@app.callback(
Output('cyto', 'layout'),
Output('scape', 'layout'),
Input('layout', 'value')
)def update_graph(lay):
return {"name": lay}, {"name": lay}
完美!现在,我们将能够使用下拉菜单修改细胞图的布局。
回调 II:更新下拉列表
接下来,我们将更新下拉列表。第一个下拉列表将决定我们要查看的顶点类型,然后我们需要用该顶点类型的值填充第二个下拉列表。该函数将根据 opt 下拉列表的值运行 getArtists、getPlaylists、getGenres 和/或 getSongs 查询。最后,它将以这种形式返回该顶点的所有选项(其中 name、name1、name2 等。是歌曲、艺术家、播放列表和流派的名称,取决于 opt 下拉列表的值)。
[{"label": name, "value": name}, {"label": name1, "value": name1}, {"label": name2, "value": name2}]
让我们把它组合成一个回调:
@app.callback(
Output('artist', 'options'),
Input('opt', 'value'),
)def update_graph(opt): if opt == "Artist": artists = conn.runInstalledQuery("getArtists")
artists = [{"label": i["attributes"]["name"], "value": i["attributes"]["name"]} for i in artists[0]["Seed"]]
return artists elif opt == "Playlist": playlists = conn.runInstalledQuery("getPlaylists")
playlists = [{"label": i["attributes"]["name"], "value": i["attributes"]["name"]} for i in playlists[0]["Seed"]]
return playlists elif opt == "Genre": genres = conn.runInstalledQuery("getGenres")
genres = [{"label": i["attributes"]["name"], "value": i["attributes"]["name"]} for i in genres[0]["Seed"]]
return genres elif opt == "Song": songs = conn.runInstalledQuery("getSongs")
songs = [{"label": i["attributes"]["name"], "value": i["attributes"]["name"]} for i in songs[0]["Seed"]]
return songs
回调 III:更新第一个细胞图
这个回调比较长,可以在笔记本里查看完整的功能。但是,我们将分解关键组件。
在标题中,我们将输出作为元素(顶点和边),接受 opt(顶点类型)和 artist(顶点类型的特定值)这两个下拉列表的输入。
@app.callback(
Output('cyto', 'elements'),
Input('artist', 'value'),
Input('opt', 'value'),
)
def update_graph(art, opt):
接下来,对于前三种顶点类型,它有一个 if 语句,使用 opt 检查顶点的类型。然后,运行 getSongsByXXX,其中 XXX 是顶点类型(艺术家、播放列表和流派)。将数据格式化如下:
{"data": {'id': XXX, 'label': XXX, 'classes': YYY}}
总的来说,它看起来像这样:
if opt == "Artist": res = conn.runInstalledQuery("getSongsByArtist", {"artist": art})[0]["@@songs"] # Runs query
cyto_data = [{'data': {'id': art, 'label': art, 'classes': 'artist'}}] # Adds artist vertex
for i in res:
cyto_data.append({"data": {'id': i['name'], 'label': i['name'], 'classes': 'song'}}) # Adds songs vertices
cyto_data.append({'data': {'source': art, 'target': i['name']}}) # Adds edges
return cyto_data
对艺术家、播放列表和流派重复此操作。然而,宋将略有不同。运行“getVerticesFromSongs”查询。然后为每种类型添加顶点和边。
if opt == "Song": res = conn.runInstalledQuery("getVerticesFromSongs", {"song_name": art})[0]["connectedVals"] # Runs query
cyto_data = [{'data': {'id': art, 'label': art, 'classes': 'song'}}] # Adds song vertex for i in res: if i['vertex_type'] == 'genre':
cyto_data.append({"data": {'id': i['name'], 'label': i['name'], 'classes': 'genre'}}) # Adds genres vertices if i['vertex_type'] == 'artist':
cyto_data.append({"data": {'id': i['name'], 'label': i['name'], 'classes': 'artist'}}) # Adds artist vertices if i['vertex_type'] == 'playlist':
cyto_data.append({"data": {'id': i['name'], 'label': i['name'], 'classes': 'playlist'}}) # Adds playlist vertices cyto_data.append({'data': {'source': art, 'target': i['name']}}) # Adds edges return cyto_data
回调 IV:更新第二个细胞图
太棒了。我们现在是在最后的回调!同样,这个查询对于博客来说太长了,所以它将被分段显示。
输入将是在第一个细胞图中点击的顶点,输出将是第二个细胞图的顶点和边。
@app.callback(
Output('scape', 'elements'),
Input('cyto', 'tapNodeData'),
)
def update_graph(main_data):
接下来,我们将只返回 main_data 存在的数据。接下来,我们将分配 art 和 opt 值,然后查询将与之前完全相同。
if (main_data):
art = main_data["id"]
opt = main_data["classes"]
最后,如果 main_data 不存在(没有点击任何东西),就返回一个空列表。
else:
return []
就这样,你完成了!现在让我们运行应用程序!
第四部分:运行应用程序!
最后,我们将运行一行代码来激活我们所有的辛勤工作:
app.run_server(mode='external')
您将看到一个链接,您可以点击该链接查看控制面板。

点击链接
然后…瞧!您的仪表板处于活动状态!

实时仪表板
如果单击某个顶点,下图将显示该顶点的连接。

两种细胞骨架
最后,您可以切换不同的选项来创建不同的图和子图。

更改使用的选项和数据
第五部分:祝贺+资源
恭喜你。您已经用 TigerGraph 数据正式创建了细胞图!
如果您有任何问题,请在 Discord 或社区论坛中提问。
https://discord.gg/gRHWBZNpxW https://community.tigergraph.com/
最后,这是对原始细胞景观论文的引用:
Shannon P, Markiel A, Ozier O, Baliga NS, Wang JT, Ramage D, Amin N, Schwikowski B, Ideker T.Cytoscape: a software environment for integrated models of biomolecular interaction networksGenome Research 2003 Nov; 13(11):2498-504
您可以在这个 Colab 笔记本中跟随:
https://colab.research.google.com/drive/1zXSVNFe1f26UdFIXZisfRZ4TWH9sbdX9?usp=sharing
接下来,使用您自己的数据创建您自己的 TigerGraph-Cytoscape 集成!一旦你这样做了,一定要分享它,让你的条纹在社区贡献计划!
https://www.tigergraph.com/community-contribution/
非常感谢你阅读这个博客,我迫不及待地想看到你创造的很酷的项目!
注:除非另有说明,所有图片均由作者(Shreya Chaudhary)创作。
用户生成内容的审核渠道
欧洲科技巨头如何利用 Toloka 的人群来调节其内容

图片由皮克斯拜的 Gerd Altmann 提供
简介
近年来,人工智能和机器学习的增长伴随着微软 Start 等自动饲料生成器的涌入。这些工具不断抓取网站,为每个用户提供最新、最合适的文章。分析更新和新闻的海洋,更不用说个性化了,是初创公司和科技巨头都在探索的想法。
这些平台中的许多已经使日常用户成为重要的内容创作者。但这并不是没有问题,包括需要快速和可扩展的内容审核。
在这篇文章中,你将了解到纳斯达克上市的科技巨头 Yandex Zen 如何利用大众来调节其内容。
Yandex Zen 和内容节制问题
作为欧洲最大的科技公司之一,Yandex Zen 处理来自媒体和用户内容的各种材料。每天有成千上万的作者注册,他们创作的内容可以达到六位数。
所有这些内容都需要被调节。让问题更加复杂的是,内容格式从普通文章到类似推特的消息、评论甚至视频。
Zen 使用预先训练的机器学习算法来检测不适当的内容,如仇恨言论、clickbait、不可靠的医疗建议、垃圾邮件和成人内容。但这还不够。
用人群标注数据
机器学习算法有时会错过它们的预测,过度依赖它们会使内容管理者容易受到向公众发布不适当内容的影响。
考虑到这一点,Yandex Zen 最初使用机器学习算法来调节内容,但将困难的案件交给真人处理。但是大量的数据意味着每天都有成千上万的内容需要人工处理。这对于内部员工来说是不可能的。
因此,Yandex Zen 使用了 Toloka ,这是一个旨在实现标签自动化的众包平台。在该平台中,他们要求用户执行一个简单的审核任务,类似于下图所示的截图,以换取少量报酬。

作者图片
因为 Toloka 在世界各地有数百万表演者,Yandex Zen 可以调节每天产生的大量内容。人群可用性甚至意味着内容在发布后几乎会立即得到调整。此外,该平台让用户可以通过选择他们说的语言、位置或他们拥有特定技能的事实来控制人群。
众包问题
从一开始,Yandex Zen 就将有效管理人群作为他们的首要目标。他们需要为他们的项目选择合适的执行者,以保持高质量的审核。
他们实施的一个解决方案是添加并定期更新控制任务。控制任务有一个已知答案,用于对执行者答案的质量评分。如果一个执行者给出了太多的错误答案,他们可能会被从项目中删除。得分高的表演者可以获得额外的奖金。
由于大量的数据和用户生成的内容不断变化的事实,Yandex Zen 团队发现自己不断提出控制任务。这使得正确管理和扩展变得困难。
阶梯管道解决方案
运行几个实验产生了下图所示的梯形解决方案。

作者图片
如果人工智能系统不确定标签,管道中的每一条内容都由机器学习算法进行审核,并发送给一般的 Toloka 人群。为了确保人群质量,Yandex Zen 团队建立了一个由更受信任的工作人员组成的较小池,称为版主,他们将这些任务标记为后来用于普通人群的控制任务。他们的标签也用于创建考试池,执行者需要通过考试才能被项目接受。
阶梯上的最后一级是专家版主,他们通过为普通版主创建控制任务来监控他们的工作。他们是极少数高度合格和值得信任的工作者,他们每周的回答让 Yandex Zen 团队检查版主的工作。
机器学习模型本身也在众包数据上进行训练。该团队实际上从每天的数据中抽取 1%的样本,并提交给人群进行标记,以确保机器学习模型总是最新的。
Yandex Zen pipeline 提供可扩展的结果,并让 Yandex 团队快速识别质量问题。以前的解决方案很晚才注意到质量控制问题,只是在性能显著下降时才标记出来。
上述解决方案帮助 Yandex Zen 有效地管理内容审核,并使其可扩展。您如何处理项目中大量且不断变化的数据?预先训练好的 ML 模型就足够了还是需要“人在回路中”?欢迎在评论中分享你的想法。
总结
本文展示了如何使用人群自动审核用户生成的内容。人工智能和机器学习管道通常会消耗大量的时间和精力。如果像 Yandex Zen 那样处理大量不断变化的数据,那就更是如此。
更多细节,请观看 Natalia Kazachenko 关于她和她的团队开发的阶梯方法的演讲。如果你想了解更多关于如何自动化数据标注管道的信息,你也可以加入这个数据赋能的社区。
PS:我正在 Medium 和aboutdatablog.com上撰写深入浅出地解释基本数据科学概念的文章。你可以订阅我的 邮件列表 每次我写新文章都会收到通知。如果你还不是中等会员,你可以在这里加入https://medium.com/@konkiewicz.m/membership。
下面还有一些你可能喜欢的帖子
* </9-things-you-did-not-know-about-jupyter-notebook-d0d995a8efb3> *
现代数据堆栈会议(MDSCON) 2021:你应该知道的 5 大要点
关于增加数据参与、保护您的数据、增加多样性等方面的专业技巧

图片由 Fivetran 提供,经许可后转载。
几周前, Fivetran 举办了 2021 年现代数据堆栈大会( MDSCON ),这是一个虚拟会议,旨在支持数据驱动的决策,从而转变企业、团队和职业。在为期两天的 40 多场会议中,我们在 Atlan 兴奋地参加并分享了来自我们最喜欢的会议的笔记。
从那时起,在仔细思考我们听到的故事和见解时,我们意识到有一些共同的主题。在不同的会议上,不同的演讲者以不同的方式探讨了同一个问题:我们如何提高数据参与度?我们如何保护数据团队及其数据?今天我们如何重新思考关于数据的传统观念?
对于那些错过会议的人,以及那些出席了会议但无法参加每一场会议的人,这里有 MDSCON 2021 的五个关键想法和要点。
提高数据参与度
多位发言人谈到了在您的组织内提高数据素养的重要性。每一家希望成为数据驱动的公司都知道这是他们需要解决的问题,但实际解决这一问题是一个巨大的挑战。
来自蒙特利尔分析的 Callie White 和 Jacob Frackson 强调了在考虑数据素养之前创建一个伟大的信息架构的重要性。在构建数据系统时,考虑不同类型的人对公司数据的需求。
- 用户体验如何?
- 维护者体验如何?
- 我们如何用信息架构来简化或改进那些?
将这些人物角色和他们的需求融入到信息架构的规划中,而不是在系统构建后试图将它们融合在一起。
来自 Analytic Vizion 的 Nelson Davis 也强调了超越数据知识思考的重要性。关注人们与数据互动并根据数据采取行动的能力,而不是关注理解组织数据的人数。
尼尔森指出,人们采用数据就像他们采用新技术一样。爱好者会很早就锁定数据,但对于一个真正精通数据的组织来说,它必须突破 16-18%的早期采用者。真正的数据参与需要瞄准剩下的大多数人,他们可能对数据更加犹豫不决。
“创造一种文化,让大多数人都使用数据来做出决策,而不仅仅是使用数据。”
–尼尔森·戴维斯,分析 Vizion
怎样才能接触到这些人?阿彻·纽维尔和劳伦·安德森分别从他们在 Fivetran 和 Okta 的经历中给出了一些很棒的想法:
- 添加数据登录页面,包含热门报告、新发布和重要链接。
- 演示新工具并向相关团队提供有针对性的培训。
- 促进和激励内部学习会议——例如,在办公时间帮助商务人士学习如何产生快速的洞察力。
- 与超级用户合作,他们可以在其团队中支持自助服务分析。
- 确定“公民数据分析师”,这是一个主题专家社区,他们将向其他利益相关者和团队公开他们的发现。

图片由 Atlan 提供,灵感来自 Gabi Steele 在 Fivetran 现代数据堆栈大会上的发言。
保护公司的数据
当发言者谈到开放组织的数据时,他们也强调了保护数据安全的重要性。给予团队权威和自主权是重要的,但是要确保它有护栏。
“我们希望集中治理,但要保持联合分析。”
正如 Nelson Davis 和 Rashmi Agrawal 解释的那样,不要只是让人们从仪表盘上下载数据。这是一个安全风险,因为人们可以将数据放入 Excel,并创建自己的数字。随着数据和数据用户的激增,这就产生了“真相”的多个版本。
相反,确保仪表板等数据工具设计良好,以便人们可以获得他们需要的洞察力。让人们拥有工具,让他们探索和创新,但这些工具与护栏配对,以确保安全性和一致性。
保护数据团队
来自 Asurion 的布列塔尼市讲述了分析师身兼数职的个人经历。从组织数据到创建仪表板,再到与其他利益相关者协调,分析师常常感觉像中间人。
来自 Netlify 的 Emilie Schario 称之为“服务陷阱”,团队陷入了永无止境的创造统计数据和证明他们工作的请求,而不是专注于推动影响力。为请求提供服务是数据团队工作的一部分,但这不是他们的核心使命——创造洞察力和推动影响力。
“如果我们把所有的时间都花在回答问题上,我们将永远无法提供真知灼见。”
艾米莉·夏里奥,奈特丽菲
这也是让每个人都成为分析师如此重要的部分原因——它保护了您的数据团队的时间。
不要成为公司人员和数据之间的中间人,而要专注于使数据易于访问,确定哪些数据可以自助,公开提出见解,并增长数据知识。只有这样,数据团队才应该处理这些流程无法解决的请求。

服务陷阱,来自 Emilie Schario 在 2021 年 Fivetran 现代数据堆栈大会上的发言。图片由 Atlan 提供。
扭转传统模式
多位发言者强调的另一件事是颠覆我们的数据挑战和假设的重要性。下面是 WW 国际的大卫·科恩和 Analytic Vizion 的 Nelson Davis 的三个很好的例子。
首先,不要忘记最简单的解决方案。大卫指出了我们总是寻找技术解决方案来帮助数据团队更快移动的讽刺之处,“忽视了人类相互对话的力量”。在他的团队中,他们决定每周会面一次,“只讨论数据”。该团队可能没有他们需要的所有数据,但这使他们能够立即就哪些决策可以做,哪些不可以做达成一致。
其次,大卫还指出了所谓的数据驱动团队的一个常见谬误——使用数据来确认他们已经想到的东西并强化现有的决策。相反,真正的数据驱动的组织需要放弃对他们想法的热爱,寻找“不舒服”的数据,因为不舒服是组织成长的原因。
第三,尼尔森解释了推动权威下降而非数据上升的价值。如今,典型的决策模式是分析师创建数据,通过指挥链向上传递,然后由领导者做出决策。这并不理想,因为它将那些对问题最了解的人(领导者)与那些对数据最了解的人(分析师)分开了。相反,领导者应该翻转这种决策模式,将权威和背景下推到那些实际拥有数据的人,而不是将数据和报告向上推。
“随着数据变得越来越多,无处不在,决策将变得越来越受数据驱动,无论我们作为决策者是否坚持这些决策。”
–尼尔森·戴维斯,分析 Vizion
永远不要忘记多样性
会议上最具影响力的演讲之一来自萨迪卡·穆萨和迪维娜·内姆巴德,他们都是分析师,也是黑色数据公司的联合创始人。他们的谈话非常个人化,基于他们在数据行业的多个群体中经常感受到的排斥。
他们提醒我们,数据行业的未来也将在员工身上找到,而不仅仅是在行业本身。团队的多元化已被反复证明是理想的、有利可图的业务成果的驱动力——从更好的团队到更好的财务业绩。

Sadiqah Musa 和 Devina Nembhard 在 Fivetran 的现代数据堆栈会议上发言。图片由 Atlan 提供。
没有“灵丹妙药”或一蹴而就的解决方案,但以下是他们打造更加多样化和包容的数据团队的一些技巧:
- 吸引多元化人才通过与专业招聘团队合作,公布您的种族薪酬差距和透明的工资级别,并在您的品牌和职业资料中建立包容性和代表性。
- 通过在整个招聘过程中整合多元化的面试小组,确保有色人种在接受工作机会之前与他们的团队会面,并创建客观的招聘实践和目标,来聘用多元化的人才。
- 通过建立一个热情的入职流程、营造一个真正包容的环境、开展真正的反种族主义培训、创建可衡量的以多元化为重点的业务目标,以及公开组织的种族构成和改进计划,留住多元化人才。
喜欢我们在这里没有提到的见解或会议吗?在大会上发表你最喜欢的评论吧!
觉得这个内容有帮助?在我的时事通讯《元数据周刊》上,我每周都写关于活动元数据、数据操作、数据文化和我们的学习建设的文章。 在此订阅。
现代数据堆栈,是你特写的时候了
最后一英里的分析如何利用我们最先进的数据堆栈并使其变得有用。
一个典型的数据消费者——一个使用运营仪表板并定期向数据团队索要报告和数据的人——可能会震惊地得知我们的行业正在经历一个重大而持续的转型时期。
我们的数据管道更加强大,我们的数据仓库现在速度惊人,使数据比以往任何时候都更加可信和可访问。然而,尽管有这些创新,我们的业务合作伙伴获取和应用见解的方式仍然没有改变。即使在数据驱动力最强的公司里,也有大量的数据,可悲的是,用这些数据来决定 T2 要做什么仍然是一场斗争。
在我看来,这是我们的行业现在面临的挑战——改进最后一英里的分析,使我们的数据不仅更容易访问,而且更具可操作性。要取得成功,我们需要从根本上转变态度、技术和领导力,但这确实是与更广泛的企业分享现代数据堆栈潜力的唯一途径。

扎克·卢塞罗在 Unsplash 上的照片
从问题到决定
近十年前,Gartner 预言我们将专注于分析连续体中的数据驱动决策。
虽然我不同意文章中对我们如何实现这一目标的大部分评估,但我完全同意整体观点— 每个现代分析团队的目标都是让企业做出更好的决策。
很容易听到这种说法,并认为我们今天确实支持决策。难道我们的仪表盘不能帮助用户区分工作的优先级吗?而且数据 app 不是帮我们评估取舍吗?但是如果我们诚实,我们知道答案充其量是有时是。
当我们收到项目团队的数据请求时,我们可能会认为(并希望)他们会使用这个仪表板(或笔记本,或图表)来挑战他们的假设,评估选项,并激发创造性的想法。这些数据可能是艰难决策风暴中的一盏明灯。
在 Count,当我们与数据消费者交谈时,他们讲述了一个不同的故事。
当问题很清楚时,我们的数据在决策过程的早期会很有帮助。我们的流失率有多糟糕?漏斗的哪个部分最糟糕?针对哪些用户?
但是随着决策的进展,事情会发生变化。企业用户告诉我们,在收到一些初始号码后,他们通常会:
- 将问题过滤给数据团队,因为他们担心会花太多时间,或者不确定这些问题是否足够有价值,或者
- 连问什么问题都不知道。
这就是问题的症结所在。我们的业务合作伙伴不会等我们达到 Gartner 的分析成熟度顶峰后再做决定。他们现在正在用他们所拥有的任何信息来做这件事,而且经常不包括数据。
我们的瀑布开发周期跟不上决策制定的快节奏和定制性质。我们的工具很少将数据整合到它所描述的问题的核心。也许最麻烦的是,我们与外界如此脱节,他们甚至不向我们寻求帮助。
全新的分析体验
为了从根本上改变这种动态,我们需要对我们的工作方式进行同样重大的改变。在过去的一年里,我一直与 Count 的更广泛的团队合作,思考现代数据难题的最后一块——“最后一英里”问题。根据我们的经验和更广泛的研究,我们希望提出以下三个原则:
1.让墙倒下
大多数数据团队有意或无意地在他们自己和业务之间建立了一堵墙。无论您是业务职能部门的分析师,还是中央数据团队的一员,都是如此。请求被“扔”给我们,我们扔回图表、仪表板和数字。
我记得在一家大公司工作时,我相信这面墙是必不可少的。这堵墙帮助我们维护适当的数据安全,防止团队提出他们自己的 KPI 的敌对版本,并且它使我们免于被大量的请求淹没。
但现在,我们的数据更加安全和可信,当我们考虑数据驱动的决策时,这堵墙不仅没有必要,而且完全是个问题。
拆除将我们与业务分开的那堵墙开启了一种全新的工作方式,如果我们要成功地将数据集成到业务中,这种方式是必不可少的。
2.超越图表
在最近的帖子中,Benn Stancil 认为数据在最有效的情况下,融入了它所描述的更大的问题。在这些情况下,“我们不会立即注意到我们使用了多少数据…因为数据并没有从体验的其余部分中分离出来。”
为了让我们的数据被看到和理解,我们将被要求以一种全新的方式呈现数据;一个植根于更大的故事背景,一个无情地关于我们的洞察力的影响。
当前的数据工具在这方面的不足令人尴尬。我们呈现数字,通常非常漂亮,但是解释、应用和处理这些数字的负担取决于我们的用户。当他们面临复杂的决策时,对他们的要求太多了。
我们需要新一代的商业智能工具来填补这一空白,并为我们提供一种更容易理解我们洞察力的价值的方式。
3.拥抱混乱
我们在数据方面所做的大部分工作都是为了减少熵—我们对数据建模以消除不准确性,我们将常见问题转化为自助报告,并将临时问题转化为正式的请求流程。这种态度是我们作为数据从业者的天性,在很大程度上对我们有好处。但在用数据驱动决策的情况下,我们需要挑战我们的本能,拥抱混乱。
如今,我们与数据合作伙伴互动的最有价值的方式也是最混乱的——在轻松的对话中,在肩扛式的演示中,或者在临时的电子邮件中,附带一个由几个要点组成的图表。
这项工作既快速又混乱,或者在任何其他情况下都可以称为敏捷。
然而,我们仍然以瀑布式的开发方式运作,这使我们远离了与业务伙伴的这些有价值的互动。这种工作方式未能抓住用数据解决问题的迭代本质,我们大多数人仍然认为这是一种需要根除的烦恼,而不是有待开采的黄金。
在未来,我们将与我们的商业伙伴更加紧密地合作,鼓励决策过程中固有的快速迭代。
我们将何去何从?
很明显,我们正处于这一旅程的早期,但我对最近的概念感到鼓舞,如数据网格和数据作为产品,它们分别强调消除数据和业务之间的障碍,以及更具同情心的数据共享方法。感觉我们这个行业正在慢慢把重心转移到最后一公里的问题上。
当然,仍有许多未解的问题——这种未来的数据组织是什么样子的?我们有吗?分析师的角色如何适应这些新的需求?等等。但这难道不是乐趣的一部分吗?
在接下来的几个月里,我会在更多的帖子中回答这些问题。你可以跟着到这里!
AtCount,我们正在为数据驱动的决策时代而努力。了解更多关于我们的协作数据平台的信息,或者订阅了解更多关于未来挑战和机遇的信息,而不仅仅是数字。
现代高斯过程回归
思想和理论

高斯过程回归可用于学习大量周期性和非周期性信号,如图所示。由瑞安·斯通在 Unsplash 上拍摄的照片
无限模型表达+现代计算
有没有想过如何用无限的表达能力创建非参数监督学习模型?只需看看高斯过程回归 (GPR),这是一种几乎完全从数据本身(在超参数的一点帮助下)学习做出预测的算法。将该算法与计算领域的最新进展(如自动微分)相结合,允许应用 GPRs 来近乎实时地解决各种受监督的机器学习问题。
在本文中,我们将讨论:
- GPR 背后理论的简要概述/回顾
- 我们可以使用 GPR 解决的问题类型,以及一些例子
- GPR 与其他监督学习算法相比如何
- 我们可以用来实现 GPR 的现代编程包和工具
这是我的 GPR 系列的第二篇文章。对于高斯过程回归的严格从头开始的介绍,请查看我以前的文章 这里 。
概述:高斯过程回归(GPR)概念
在我们深入研究如何实现和使用 GPR 之前,让我们快速回顾一下这种受监督的机器学习算法背后的机制和理论。关于以下概念的更详细的推导/讨论,请查看我以前关于 GPR 的文章 这里 。GPR:
一、以观察到的训练点为条件,预测测试点的条件后验分布:




二。将预测测试点目标的均值计算为观测目标值的线性组合,这些线性组合的权重由从训练输入到测试点的核距离决定:


三。使用协方差函数测量输入之间的核距离:



四。通过将每个新点视为高斯过程的一部分,从现有点中插入新点,即将新点参数化为高斯分布:

使用噪声正弦时间序列数据集的 1D 插值示例。图片来源:作者。
GPR 可以解决哪些问题?
GPR 可以应用于各种 有监督的 机器学习问题(并且在某些情况下,可以作为子程序应用于 无监督的 机器学习)。以下是可以用这种机器学习技术解决的几类问题:
A .插值/克里金法
插值是许多领域中的关键任务,例如信号处理、空间统计和控制。这种应用在利用空间统计的领域中尤其常见,例如地统计学。作为一个具体的例子,考虑在给定山上有限数量的定义点的情况下,生成对应于下面的山的表面的问题。如果你有兴趣看这个的具体实现,请点击这里查看我的文章。

克里金法和插值法常用于地统计学,可用于高维空间的表面插值!照片由马科斯·曼特在 Unsplash 上拍摄
B .时间序列预测
这类问题着眼于使用历史数据预测未来的时间序列。与克里金法一样,时间序列预测允许预测未知值。然而,这个问题不是预测不同位置的看不见的值,而是应用 GPR 来预测未来看不见的点的平均值和方差。这非常适用于预测电力需求、股票价格或线性动力系统的状态空间演化等任务。
此外,GPR 不仅预测未来某一点的平均值,而且还输出预测方差,使决策系统能够将不确定性因素纳入决策。

用有噪声的正弦曲线进行时间序列预测的例子。深蓝色线代表预测的平均值,而浅蓝色区间代表模型的置信区间。图片来源:作者。
预测不确定性
更一般地,因为 GPR 允许预测测试点的方差,GPR 可用于各种不确定性量化任务——即与估计期望值和与该期望值相关的不确定性或方差相关的任何任务。
你可能想知道:为什么不确定性很重要?为了激发这个答案,考虑为自主导航安全系统预测行人的轨迹。如果行人的预测轨迹具有高预测不确定性,则自动驾驶车辆应更加小心,以应对行人意图的低置信度。另一方面,如果自主车辆具有低的行人轨迹预测方差,那么自主车辆将能够更好地预测行人的意图,并且能够更容易地按照其当前的驾驶计划前进。
从某种意义上说,通过预测不确定性,决策系统可以根据他们预测这些期望值的不确定性来“加权”他们估计的期望值。

预测行人对自动驾驶汽车系统意图的不确定性是 GPR 的一个应用示例。照片由法伦·麦克在 Unsplash 拍摄
为什么 GPR 优于其他监督学习模型?
您可能想知道—为什么我应该考虑使用 GPR 而不是不同的监督学习模型?下面,我列举几个比较的理由。
- GPR 是 非参数 。这意味着它主要从数据本身学习,而不是通过学习一组广泛的参数。这是特别有利的,因为这导致 GPR 模型不像高度参数化的模型(如神经网络)那样渴求数据,也就是说,它们不需要那么多样本来实现强大的概化能力。
- 对于插值和预测任务,GPR 估计期望值和不确定性。这对于在做出决策时考虑到这种不确定性的决策系统尤其有益。**
- GPR 是一个 线性平滑器【5】——从监督学习的角度来看,这可以被概念化为正则化技术。从贝叶斯的角度来看,这相当于在你的模型上强加了一个先验,即测试点上的所有目标必须是现有训练目标的 线性组合 。这个属性有助于 GPR 推广到看不见的数据,只要真正的看不见的目标可以表示为训练目标的线性组合。**
- 有了自动差异化后端框架如
torch、tensorflow,通过gpytorch、gpflow等 GPR 包集成,GPR 就是闪电般快速、可伸缩。对于批量车型来说尤其如此。有关这方面的示例案例研究,请参见我以前关于批量多维 GPR 的文章 这里 !**
如何实现 GPR?
下面,我们为高斯过程回归的可扩展、高效和模块化实现介绍几个 Python 机器学习包。让我们逐一了解一下!**
1。sci kit-Learn【1】****
这是一个非常棒的 GPR 入门包。它允许一些模型的灵活性,并能够执行超参数优化和定义引擎盖下的可能性。要将sklearn用于您的数据集,请确保您的数据集可以用np.array对象用数字表示。通过sklearn使用 GPR 的主要步骤:
- ****预处理您的数据。训练数据(
np.array)可以表示为一个具有x_train形状 (N,D)y_train形状 (N,1)的(x_train, y_train)元组,其中 N 是样本数, D 是特征的维数。你的考点(np.array)可以用形状 (N,D) 的x_test来表示。 - 定义你的协方差函数。在下面的代码段中,我们使用径向基函数(RBF)内核
RBF和使用WhiteKernel的加性噪声。 - 使用你的协方差函数定义你的
GaussianProcessRegressor对象,以及一个播种你的 GPR 的随机状态。此random_state对于确保再现性非常重要。 - 使用方法
gpr.fit(x_train, y_train)调整gpr对象。这“训练你的模型”,并使用梯度方法优化你的gpr对象的超参数,例如lbfgs,一个基于二阶 Hessian 的优化程序。 - 使用
gpr.predict(x_test, return_std=True)方法预测你的测试点x_test上目标的均值和协方差。这给了你一个预测值,以及这个预测点的不确定性的度量。
使用pip为下面的示例安装依赖项:
*****pip install scikit-learn numpy matplotlib*****
下面是一个拟合并且使用sklearn预测一维正弦曲线的例子:
2。gpytrch【2】(py torch 后端)****
该软件包非常适合创建完全可定制的高级加速 GPR 模型。这个包支持从通过自动微分的 GPR 模型优化到通过 CUDA 和 PyKeOps 的硬件加速。
在使用 GPyTorch 之前,建议您对 PyTorch 和/或 python 中的自动微分包有所了解,但是 教程 使这个框架易于学习和使用。GPyTorch 中 GPRs 的数据表示为torch.tensor对象。以下是在 GPyTorch 中拟合 GPR 模型的步骤:
- 预处理您的数据。训练数据可以表示为一个具有
x_train形状 (B,N,D)y_train形状 **(B,N,1)的(x_train, y_train)元组,其中 B 是批量, N 是样本数, D 是特征的维数。你的测试点可以用形状为 (B,N,D) 的x_test来表示。****** - 通过子类化
gpytorch.models.ExactGP类来定义你的ExactGPModel。要创建这个模型的子类,您需要定义:(I)构造器方法,它指定了模型的均值和协方差函数,(ii)方法forward,它描述了 GPR 模型如何进行预测。要使用批处理,请点击 查看本教程 。要在您的超参数上使用之前的分布,请点击 查看本教程 。**** - 指定您的
likelihood函数,您的模型使用该函数将潜在变量 f 与观察目标 y 相关联。 - 使用您的
likelihood和训练数据(x_train, y_train)实例化您的model。 - 使用
pytorch自动微分对model进行超参数优化(“训练”)。一旦完成,确保你的model和likelihood与model.eval()和likelihood.eval()置于后模式。 - 通过调用
likelihood(model(x_test)),使用model计算测试点的均值和方差预测。内部函数从测试输入**x***预测潜在测试值**f***,外部函数从潜在测试值**f***预测均值和方差。
使用pip为下面的示例安装依赖项:
*******pip install gpytorch torch matplotlib numpy# (Optional) - Installs pykeops
pip install pykeops*******
下面是一个使用gpytorch拟合有噪声的一维正弦曲线的例子:
3。gp flow【3】(tensor flow 后端)
另一个支持自动微分的 GPR 包(这次是在tensorflow中),GPFlow 具有广泛的内置功能,用于创建完全可定制的模型、似然函数、内核以及优化和推理例程。除了 GPR,GPFlow 还为贝叶斯优化中的各种其他最新问题提供了内置功能,例如 变分傅立叶特征 和 卷积高斯过程 。
在使用 GPFlow 之前,建议您对 TensorFlow 和/或 Python 中的自动微分包有所了解。GPFlow 中 GPRs 的数据表示为tf.tensor对象。要开始使用 GPFlow,请查看 此示例链接 。
4。GPy【4】
这个包为大量 GPR 模型、似然函数和推理过程提供了 Python 实现。虽然这个包没有像驱动gpytorch和gpflow一样的自动区分后端,但是这个包的通用性、模块化和可定制性使它成为实现 GPR 的一个有价值的资源。
5。烟火 [6]
Pyro 是一个概率编程包,可以与 Python 集成,也支持高斯过程回归,以及高级应用,如深度内核学习。
6。创 [7]
Gen 是另一个建立在 Julia 之上的概率编程包。Gen 为高斯过程回归提供了几个优势:(I)它建立在建议分布中,这可以通过有效地对可能的解决方案集施加先验来帮助缩小搜索空间,(ii)它具有用于从拟合 GPR 模型中采样轨迹的简单 API,(iii)作为许多概率编程语言的目标,它能够容易地创建用于调整 GPR 超参数先验的分层模型。
7。斯坦[8]
Stan 是另一个概率编程包,可以与 Python 集成,但也支持其他语言,如 R、MATLAB、Julia 和 Stata。除了内置高斯过程回归功能,Stan 还支持各种其他贝叶斯推理和采样功能。
8。博特赫
BoTorch 由 GPyTorch 的创建者构建,它是一个贝叶斯优化库,支持许多与 GPR 相同的技术,以及高级贝叶斯优化技术和分析测试套件。
总结和回顾
在本文中,我们回顾了高斯过程回归(GPR)背后的理论,介绍并讨论了 GPR 可以用来解决的问题类型,讨论了 GPR 与其他监督学习算法的比较,并介绍了我们如何使用sklearn、gpytorch或gpflow来实现 GPR。
想看更多强化学习、机器学习、计算机视觉、机器人、教学方面的文章,请关注我!感谢您的阅读!
感谢
感谢 CODECOGS 的内联方程渲染工具,感谢 Carl Edward Rasmussen 开源教科书机器学习的高斯过程【5】,感谢 Scikit-Learn 、 GPyTorch 、 GPFlow 和 GPy 开源他们的高斯过程回归 Python 库。**
参考
[1] Pedregosa,Fabian 等,“sci kit-learn:Python 中的机器学习”《机器学习研究杂志》12(2011):2825–2830。
[3] Gardner,Jacob R .等,“Gpytorch:带 gpu 加速的黑盒矩阵-矩阵高斯过程推断。” arXiv 预印本 arXiv:1809.11165 (2018)。
[3] Matthews,Alexander G. de G .等《GPflow:使用张量流的高斯过程库》。j .马赫。学习。第 18.40 (2017)号决议第 1 至 6 段。
【4】GPy,“GPy。”http://github.com/SheffieldML/GPyT21。**
[5]卡尔·爱德华·拉斯姆森和克里斯托弗 K. I .威廉斯。2005.机器学习的高斯过程(自适应计算和机器学习)。麻省理工学院出版社。
[6] Eli Bingham、Jonathan P. Chen、Martin Jankowiak、Fritz Obermeyer、Neeraj Pradhan、Theofanis Karaletsos、Rohit Singh、Paul Szerlip、Paul Horsfall 和 Noah D. Goodman。2019. Pyro:深度泛概率规划。j .马赫。学习。第 20 号决议,第 1 段(2019 年 1 月),第 973 至 978 段。
Gen:一个具有可编程推理的通用概率编程系统。库苏马诺-汤纳,M. F。萨阿德公司;卢,a。和 Mansinghka,V. K,《关于编程语言设计和实现的第 40 届 ACM SIGPLAN 会议论文集》( PLDI '19)。
[8]斯坦开发小组。2021.Stan 建模语言用户指南和参考手册,版本。https://mc-stan.org。
[9] Balandat,Maximilian,等人,“BoTorch:一个有效的蒙特卡罗贝叶斯优化框架”神经信息处理系统进展 33 (2020)。**
现代便携式语音活动检测器发布

作者图片
目前,除了 WebRTC 语音活动检测器(链接)之外,几乎没有任何高质量/现代/免费/公开的语音活动检测器。然而 WebRTC 开始显示出它的年龄,它遭受了许多误报。
此外,在某些情况下,能够匿名化大规模口语语料库(即删除个人数据)至关重要。通常,如果个人数据包含(I)姓名(ii)某个私人 ID,则被视为私人/敏感数据。姓名识别是一件非常主观的事情,它依赖于地区和商业案例,但语音活动和号码检测是相当普遍的任务。
主要特点:
- 现代,便携;
- 内存占用低;
- 优于 WebRTC 的指标;
- 在庞大的口语语料库和噪音/声音库上训练;
- 比 WebRTC 慢,但对于 IOT / edge /移动应用足够快;
- 不像 WebRTC(大多是从语音中分辨出静音),我们的 VAD 可以从噪音/音乐/静音中分辨出语音;
- PyTorch (JIT)和 ONNX 检查站;
典型用例:
- 口语语料库匿名化;
- 可以和 WebRTC 一起使用;
- 针对 IOT / edge /移动使用案例的语音活动检测;
- 数据清理和准备、号码和一般语音检测;
- PyTorch 和 ONNX 可以用于各种各样的部署选项和后端;
入门指南
对于每个算法,您可以在提供的 colab 或repo本身中看到示例。对于 VAD,我们还提供了单个流和多个流的流示例。
潜伏
所有速度测试都在 AMD 锐龙 Threadripper 3960X 上运行,仅使用一个线程:
torch.set_num_threads(1) # pytorch
ort_session.intra_op_num_threads = 1 # onnx ort_session.inter_op_num_threads = 1 # onnx
流延迟取决于两个因素:
- num_steps —将每个音频块分割成的窗口数量。我们的后处理类将前一个块保存在内存中(250 ms),所以新块(也是 250 ms)被追加到它后面。由此产生的大块(500 毫秒)被分成 num_steps 个重叠窗口,每个窗口长 250 毫秒;
- ****音频流数量;
因此流式传输的批量大小为 num_steps *** 音频流数量。接收新音频块和获得结果之间的时间如下所示:

VAD 潜伏期
吞吐量
用于全音频处理的 RTS (每秒处理的音频秒数,实时速度,或 1 / RTF)取决于 num_steps (见上一段)和批量大小(越大越好)。

VAD 吞吐量以每秒处理的音频秒数来衡量
VAD 质量基准
我们使用随机的 250 ms 音频块进行验证。组块之间的语音与非语音比率大约是 50/50(即平衡的)。从四种不同语言(英语、俄语、西班牙语、德语)的真实音频中采样语音块,然后将随机背景噪声添加到其中一些语音块中(约 40%)。
由于我们的 VAD(只有 VAD,其他网络更灵活)是在相同长度的组块上训练的,模型的输出只是一个从 0 到 1 的浮点数— 语音概率。我们使用语音概率作为精确召回曲线的阈值。这可以扩展到 100-150 毫秒。小于 100-150 毫秒不能被区分为有把握的语音。
Webrtc 将音频分割成帧,每帧都有相应的编号(0 或 1)。对于 webrtc,我们使用 30 毫秒的帧,因此每个 250 毫秒的块被分成 8 帧,它们的平均值被用作绘图的阈值。

作者图片
原载于https://habr.com。**
现代推荐系统
入门
深入探讨脸书和谷歌等公司围绕其业务发展的人工智能算法。
就在 2019 年 5 月,脸书开源了他们的一些推荐方法,并引入了 DLRM (深度学习推荐模型)。这篇博客文章旨在解释 DLRM 和其他现代推荐方法如何以及为什么如此有效,方法是从该领域先前的结果中推导出来的,并详细解释了它们的内部工作方式和直觉。

图片由苏珊娜·尤策勒拍摄,suju-foto 在 Pixabay
基于人工智能的个性化广告是目前在线营销的游戏名称,脸书、谷歌、亚马逊、网飞等公司是在线营销丛林的国王,因为他们不仅采纳了这一趋势,而且基本上发明了这一趋势,并围绕这一趋势建立了他们的整个商业战略。网飞的“你可能会喜欢的其他电影”或亚马逊的“购买了该商品的顾客也购买了……”只是网络世界中许多例子的一部分。
所以很自然地,作为一个每天都在使用脸书和 Gooogle 的用户,我会在某个时候问自己:
“这东西到底是怎么运作的?”
是的,我们都知道基本的电影推荐例子来解释协同过滤/矩阵分解是如何工作的。此外,我不是在谈论为每个用户训练一个直接的分类器的方法,它输出用户是否喜欢某个产品的概率。这两种方法,即协同过滤和基于内容的推荐,必须产生某种性能和一些可以使用的预测,但谷歌,脸书和公司肯定有更好的东西,否则他们不会有今天的地位。
为了理解今天的高端推荐系统是从哪里来的,我们必须看一下解决以下问题的两种基本方法
预测某个用户有多喜欢某个项目。
在网络营销领域,这意味着根据评分、喜欢等明确的反馈来预测可能的广告的点击率。以及隐含的反馈,如点击、搜索历史、评论或网站访问。
基于内容的过滤与协作- 过滤
1。基于内容的过滤
宽松地说,基于内容的推荐是指利用用户的在线历史来预测用户是否喜欢某个产品。其中包括用户给出的喜好(例如在脸书上)、他/她搜索的关键词(例如在谷歌上),以及他/她对某些网站的简单点击和访问。总而言之,它侧重于用户自己的喜好。例如,我们可以想到一个简单的二元分类器(或回归器),它为该用户的某个广告组输出点击率(或评级)。
2。协同过滤
然而,协同过滤试图通过查看相似用户的偏好来预测用户是否会喜欢某个产品。在这里,我们可以考虑用于电影推荐的标准矩阵分解(MF)方法,其中评级矩阵被分解成一个用于用户的嵌入矩阵和一个用于电影的嵌入矩阵。
经典 MF 的缺点是我们不能使用任何辅助功能,例如电影类型、上映日期等。MF 本身必须从现有的交互中学习它们。此外,MF 还面临所谓的“冷启动问题”,即一部尚未被任何人评级的新电影,不能被推荐。基于内容的过滤解决了这两个问题,但是,缺乏观察相似用户偏好的预测能力。
这两种不同方法的优点和缺点非常清楚地提出了对混合方法的需求,在这种混合方法中,两种思想以某种方式结合到一个模型中。
混合推荐模型
1.因式分解机
Steffen Rendle 在 2010 年提出的一个想法是因子分解机。它掌握了将矩阵分解与回归相结合的基本数学方法

其中在学习期间需要估计的模型参数是:

和⟨ ∙,⟩是两个向量 vᵢ和 vⱼof 大小ℝᵏ之间的点积,它们可以被看作是 v 中的行
当看一个如何表示被扔进这个模型的数据 x 的例子时,很容易理解这个等式的意义。让我们看看 Steffen Rendle 在关于因式分解机器的论文中描述的例子:
假设有以下关于电影评论的交易数据,其中用户在某个时间对电影进行评级:
- 用户 U∈U =
- 电影(item)I∈I =
- 时间 t ∈ ℝ时的评级 r ∈

图 1,S. Rendle — 2010 年 IEEE 数据挖掘国际会议,2010-“因式分解机器”
查看上图,我们可以看到混合推荐模型的数据设置。表示用户和项目的稀疏特征以及任何附加的元信息或辅助信息(例如,在该示例中的“时间”或“最后评价的电影”)都是映射到目标 y 的特征向量 x 的一部分。现在关键是它们如何被模型处理。
- FM 的回归部分像标准回归任务一样处理稀疏数据(例如“用户”)和密集数据(例如“时间”),因此可以被解释为 FM 内基于内容的过滤方法。
- FM 的 MF 部分现在考虑了特征块之间的交互(例如,“用户”和“电影”之间的交互),其中矩阵 V 可以被解释为在协同过滤方法中使用的嵌入矩阵。这些跨用户-电影的关系为我们带来了如下见解:
与具有嵌入 vⱼ的另一个用户 j 具有相似的嵌入 vᵢ(表示他对电影属性的偏好)的用户 I 很可能喜欢与用户 j 相似的电影
将回归部分和 MF 部分的两个预测相加在一起并在一个成本函数中同时学习它们的参数导致混合 FM 模型,该模型现在使用“两全其美”的方法来为用户做出推荐。
乍一看,因子分解机器的这种混合方法似乎已经是一个完美的“两全其美”的模型,然而,正如 NLP 或计算机视觉等许多不同的人工智能领域在过去已经证明的那样:
“把它扔进神经网络,你会让它变得更好”
2.广度和深度,神经协同过滤(NCF)和深度因子分解机器(DeepFM)
通过查看 NCF 的论文,我们将首先了解如何通过神经网络方法解决协同过滤,这将引导我们到深度因式分解机器(DeepFM ),它是因式分解机器的神经网络版本。我们将看到为什么它们优于常规 FMs,以及我们如何解释神经网络架构。我们将看到 DeepFM 是如何作为对谷歌之前发布的 Wide&Deep 模型的改进而开发的,这是深度学习在推荐系统中的第一次重大突破之一。这将最终把我们带到前面提到的脸书在 2019 年发布的 DLRM 论文,这可以被视为对 DeepFM 的简化和轻微调整。
NCF
2017 年,一组研究人员发布了他们关于神经协同过滤的工作。它包含一个通用的框架,用于学习用神经网络在协同过滤中通过矩阵分解建模的函数关系。作者还解释了如何实现更高阶的相互作用(MF 仅是 2 阶)以及如何将这两种方法融合在一起。
总的想法是,神经网络可以(在理论上)学习任何函数关系。这意味着协同过滤模型表达的与其 MF 的关系也可以被神经网络学习。NCF 为用户和项目提出了一个简单的嵌入层(类似于标准的 MF),然后是一个直接的多层感知器神经网络,通过神经网络基本上学习两个嵌入之间的 MF 点积关系。

图 2 摘自何、廖、张、聂、胡、蔡志勇的《神经协同过滤》,2017 年第 26 届国际互联网大会论文集
这种方法的优点在于 MLP 的非线性。MF 中使用的简单点积总是将模型限制为学习 2 级交互,而具有 X 层的神经网络理论上可以学习更高级别的交互。想出三个相互作用的分类特征,比如男性、青少年和 RPG 电脑游戏。
在现实世界的问题中,我们不仅仅使用用户和项目二进制化向量作为我们嵌入的原始输入,而且显然包括各种其他可能有价值的元或辅助信息(例如,年龄、国家、音频/文本记录、时间戳等),因此实际上我们有一个非常高维、高度稀疏和连续分类的混合数据集。在这一点上,图 2 中的上述神经网络也可以很好地解释为简单二进制分类前馈神经网络形式的基于内容的推荐。这种解释是理解它如何最终成为 CF 和基于内容的推荐之间的混合方法的关键。该网络实际上可以学习任何函数关系,因此可以在这里学习 3 阶或更高阶的 CF 意义上的相互作用,例如 x₁x₂x₃,或者形式σ( … σ(w₁x₁+w₂x₂ + w₃x₃ + b)的经典神经网络分类意义上的任何非线性变换。
配备了学习高阶相互作用的能力,通过将神经网络与众所周知的学习低阶相互作用的模型(因式分解机)相结合,我们可以特别地使我们的模型容易地学习 1 阶和 2 阶的低阶相互作用。这正是 DeepFM 的作者在论文中提出的观点。这种同时学习高阶和低阶特征交互的组合思想是许多现代推荐系统的关键部分,并且可以在行业中提出的几乎每个网络架构中以某种形式找到。
DeepFM
DeepFM 是 FM 和深度神经网络之间的混合方法,两者共享相同的输入嵌入层。原始特征被转换,使得连续字段由其自身表示,而分类字段被一次性编码。由 NN 中的最后一层给出的最终(例如 CTR)预测被定义为:

它是两个网络分量的 sigmoid 激活和:FM 分量和深度分量。
FM 组件是一个以神经网络架构风格装扮的常规因式分解机器:

图 2 来自惠丰郭,唐瑞明,叶云明,,何秀强。基于因子分解机的神经网络用于 CTR 预测。arXiv 预印本 arXiv:1703.04247,2017。
FM 层的加法部分直接获得原始输入向量 x(稀疏要素层),并在将它们相加之前将每个元素与其权重相乘(“正常连接”)。FM 层的内积部分也获得原始输入 x,但是仅在它们已经通过嵌入层之后,并且简单地获得嵌入向量之间没有任何权重的点积(“权重-1 连接”)。通过另一个“权重-1 连接”将这两部分加在一起,得到前面提到的 FM 等式:

这个方程中的 xᵢxⱼ乘法只需要能够写出 i=1 到 n 的和,它并不是神经网络计算的一部分。由于嵌入层结构,网络自动知道 vⱼvᵢ的哪个嵌入向量之间取点积。
该嵌入层架构如下所示:

图 4 郭惠峰、唐瑞明、叶云明、、何秀强。基于因子分解机的神经网络用于 CTR 预测。arXiv 预印本 arXiv:1703.04247,2017。
其中 Vᵖ是具有 k 列和多少行的每个场 p={1,…,m}的嵌入矩阵,该场的二进制版本具有元素。嵌入层的输出因此被给定为:

需要注意的是,这不是一个完全连接的图层,也就是说,任何字段的原始输入和任何其他字段的嵌入之间都没有联系。可以这样想:用于性别的独热编码向量(例如(0,1))不能与用于工作日的嵌入向量(例如(0,1,0,0,0,0,0)原始二进制化的工作日“星期二”)有任何关系,并且它的嵌入向量具有例如:k = 4;(12,4,5,9)).
作为因式分解机的 FM 分量反映了 1 阶和 2 阶相互作用的高度重要性,它们被直接添加到深度分量输出中,并被馈送到最终层中的 sigmoid 激活中。
深度组件被提议为理论上的任何深度神经网络架构。作者特别关注了常规的前馈 MLP 神经网络(以及所谓的 PNN)。下图给出了常规 MLP:

图 3 来自惠丰郭,唐瑞明,叶云明,,何秀强。基于因子分解机的神经网络用于 CTR 预测。arXiv 预印本 arXiv:1703.04247,2017。
一个标准 MLP 网络,在原始数据(由于一个热编码分类输入而高度稀疏)和以下神经网络层之间具有嵌入层,如下所示:

σ表示激活函数,W 表示权重矩阵,a 表示来自前一层的激活,b 表示偏差。
这就产生了整个 DeepFM 网络架构:

使用参数:
- 潜在向量 Vᵢ,用于测量特征 I 与其他特征(嵌入层)交互的影响
- Vᵢ被传递给 FM 组件以模拟 2 阶交互(FM 组件)
- wᵢ加权原始特征 i (FM 分量)的 1 阶重要性
- Vᵢ还被传递到深层组件,以模拟所有高阶交互(> 2)(深层组件)
- Wˡ和 bˡ,神经网络的权重和偏差(深层组件)
同时获得高阶和低阶交互的关键是在一个成本函数下同时训练所有参数,特别是对 FM 和深度分量使用相同的嵌入层。
与 Wide&Deep 和 NeuMF 的比较
关于如何调整这种架构以使其变得更好,人们可以想象出许多变化。然而,在核心上,它们在如何同时模拟高阶和低阶相互作用的混合方法上都是相似的。DeepFM 的作者还提出用所谓的 PNN 交换 MLP 部分,这是一种深度神经网络,它将 FM 层作为初始输入与嵌入层相结合
NCF 论文的作者也提出了一个类似的架构,他们称之为 NeuMF(“神经矩阵分解”)。他们没有使用 FM 作为低阶分量,而是将常规矩阵分解输入到激活函数中。然而,这种方法缺乏由 FM 的线性部分模拟的特定的 1 阶相互作用。此外,作者特别允许模型学习矩阵分解以及 MLP 部分的不同用户和项目嵌入。
如前所述,谷歌的研究团队是最早提出混合推荐方法神经网络的团队之一。DeepFM 可以被认为是 Google 的 Wide&Deep 算法的进一步发展,如下所示:

图 1 来自郑恒泽、利万特·科奇、耶利米·哈姆森、塔尔·沙克德、图沙尔·钱德拉、赫里希·阿拉德耶、格伦·安德森、格雷戈·科拉多、柴蔚、穆斯塔法·伊斯皮尔、罗汉·阿尼尔、扎卡里亚·哈克、李潺·洪、维汉·贾恩、刘小兵和赫马尔·沙阿。推荐系统的广度和深度学习。进行中。第一届推荐系统深度学习研讨会,第 7–10 页,2016。
右侧是我们众所周知的带有嵌入层的 MLP,而左侧是不同的手动设计的输入,这些输入直接馈入最终的整体输出单元。点积运算形式的低阶交互隐藏在这些人工设计的特征中,作者说这些特征可以是许多不同的东西,例如:

其通过将 d 个特征彼此交叉相乘(如果 xᵢ是第 k 个变换的一部分,则指数等于 1)来捕获 d 个特征之间的交互(有或没有另一个先前嵌入)。
很容易看出 DeepFM 是一种改进,因为它不需要任何先验特征工程,并且能够从完全相同的输入数据中学习低阶和高阶交互,这些输入数据共享一个公共嵌入层。DeepFM 实际上将 FM 模型作为其核心网络的一部分,而 Wide&Deep 并不将点积计算作为实际神经网络的一部分,而是预先在特征工程步骤中进行。
3.DLRM——深度学习推荐模型
因此,从谷歌、华为(围绕 DeepFM 架构的研究团队)和其他公司的所有这些不同选择中,让我们看看脸书是如何看待事情的。他们在 2019 年发表了他们的 DLRM 论文,该论文非常关注这些模型的实用方面。并行训练设置、GPU 计算以及对连续和分类特征的不同处理。
下图描述了 DLRM 架构,其工作方式如下:分类特征各自由一个嵌入向量表示,而连续特征由 MLP 处理,因此它们与嵌入向量具有相同的长度。现在在第二阶段,计算嵌入向量和处理过的(MLP 输出)密集向量的所有组合之间的点积。之后,点积与密集要素的 MLP 输出连接,并通过另一个 MLP,最后进入 sigmoid 函数以给出概率。

由 DLRM 描述的 DLRM 网络。马克斯·贝克尔斯的数字。
DLRM 的建议在某种程度上是 DeepFM 的简化和修改版本,因为它也使用嵌入向量之间的点积计算,但它特别试图通过不直接强制嵌入的分类特征通过 MLP 层来远离高阶交互。这种设计是为了模仿因式分解机器计算嵌入之间二阶相互作用的方式而定制的。我们可以把整个 DLRM 系统看作是 DeepFM 的专门部分,FM 组件。DeepFM 的经典深度分量在 DeepFM 的最后一层中被添加到 FM 分量的输出中(然后被馈送到 sigmoid 函数中),可以被视为在 DLRM 设置中被完全省略。DeepFM 的理论优势是显而易见的,因为通过设计,它可以更好地学习高阶相互作用,然而根据脸书的说法:
“……在其他网络中发现的二阶以上的高阶相互作用不一定值得额外的计算/存储成本”
4.前景和编码
在介绍了各种深度推荐方法、它们的直觉以及优缺点之后,理论上,我在脸书的 GitHub 页面上查看了提议的 DLRM 的 PyTorch 实现。
我查看了实现的细节,并尝试了他们内置的预定义数据集 API,以直接处理不同的原始数据集。Criteo 的ka ggle display advertising challenge和他们的TB 级数据集都是预实现的,可以下载并随后用于训练一个完整的 DLRM,只需一个 bash 命令(参见 DLRM repo 的说明)。然后,我扩展了脸书的 DLRM 模型 API,以包括另一个数据集的预处理和数据加载步骤,即 2020 DIGIX 广告 CTR 预测。请点击查看。
在下载并解压缩 digix 数据后,以类似的方式,您现在也可以使用一个 bash 命令在该数据上训练一个模型。所有预处理步骤、嵌入的形状和神经网络架构参数都针对处理 digix 数据集进行了调整。带你浏览命令的笔记本可以在这里找到。该模型提供了一些不错的结果,因为我正在继续研究它,通过更好地理解原始数据和 digix 数据背后的广告流程来提高性能。具体的数据清理、超参数调整和特征工程都是我想进一步研究的内容,并在笔记本中提到。第一个目标只是拥有一个技术上可靠的 DLRM 模型 API 的扩展,它可以使用原始的 digix 数据作为输入。
总而言之,我相信混合深度模型是解决推荐任务最有力的工具之一。然而,最近出现了一些非常有趣和创造性的无监督方法,使用自动编码器解决协同过滤问题。所以在这一点上,我只能猜测大型互联网巨头今天使用什么来提供我们最有可能点击的广告。我假设它很可能是前面提到的自动编码器方法以及本文中介绍的某种形式的深度混合模型的组合。
参考
斯蒂芬·伦德尔。因式分解机。进行中。2010 年 IEEE 数据挖掘国际会议,995–1000 页,2010 年。
贺湘南,廖,汉王张,,聂,,蔡达生。神经协同过滤。进行中。第 26 届国际。糖膏剂环球网,第 173–182 页,2017。
郭慧峰,唐瑞明,叶云明,,何秀强。一个基于因子分解机的神经网络用于 CTR 预测。arXiv 预印本 arXiv:1703.04247,2017。
连建勋、周小欢、张辅政、陈忠霞、谢星和。xDeepFM:结合推荐系统的显式和隐式特征交互。进行中。第 24 届 ACM SIGKDD 知识发现和数据挖掘国际会议,第 1754–1763 页。ACM,2018。
Cheng-Tze Cheng、Levent Koc、Jeremiah Harmsen、Tal Shaked、Tushar Chandra、Hrishi Aradhye、Glen Anderson、Greg Corrado、、Mustafa Ispir、Rohan Anil、Zakaria Haque、Hong、Jain、和 Hemal Shah。推荐系统的广度和深度学习。进行中。第一届推荐系统深度学习研讨会,第 7–10 页,2016。
米(meter 的缩写))Naumov、D. Mudigere、H. M. Shi、J. Huang、N. Sundaraman、J. Park、X. Wang、U. Gupta、C. Wu、A. G. Azzolini、D. Dzhulgakov、A. Mallevich、I. Cherniavskii、Y. Lu、R. Krishnamoorthi、A. Yu、V. Kondratenko、S. Pereira、X. Chen、W. Chen、V. Rao、B. Jia、L. Xiong 和 M. Smelyanskiy【在线】。可用:http://arxiv.org/abs/1906.00091[39]
用漂亮的类型提示使你罪恶的 Python 代码现代化
成为拥有专业代码质量技能的顶级掠夺者

戴维·克洛德在 Unsplash 上的照片
您的旅程概述
- 设置舞台
- 了解动态类型和静态类型
- 变量的 Python 类型提示
- 用 Mypy 检查类型提示
- 函数的 Python 类型提示
- 如何添加列表作为类型提示
- 给你的代码加电的五个令人敬畏的类型提示
- 为什么所有酷小孩都用 Python 3.9+
- 总结和进一步的资源
搭建舞台
在由 Stack Overflow 发起的 2020 年开发者调查中, Python 在“开发者想学的编程语言”类别中拔得头筹。尽管如此,Python 也有让支持者和批评者恼火的缺点。经常提到的一个缺点如下:
Python 有 动态类型 。这增加了调试时间,并使变量和函数的行为不可预测。
尽管动态类型对于快速原型开发来说很方便,但是在大型项目中,它们可能会成为一种烦恼。这就是类型提示发挥作用的地方:自从 Python 3.5 发布以来,可以通过编写类型提示来模拟 Python 中的静态类型!❤️
编写类型提示的另一个好处是它们可以提高代码质量。当我说代码质量时,大多数人会想到描述性变量名和写得很好的文档字符串。编写类型提示是提高 Python 代码质量的一个新方法。
在本文中,我将向您展示如何使用类型提示来增强您的 Python 代码!
目标:我会教你 Python 中类型提示的基础知识。完成基础知识之后,我将向您展示五个更棒的类型提示来增强您的 Python 代码。最后,我将解释随着 Python 3.9 的引入,类型提示是如何变得不那么麻烦的。
先决条件:你应该熟悉 Python 的基础知识。确保您运行的 Python 版本高于 Python 3.5。对于动态类型、静态类型或类型提示没有必要的先决条件。
理解动态类型和静态类型
首先我要解释一下术语动态类型和静态类型。在 Python 中,您可以在整个程序中任意多次更改变量的数据类型:
在 Python 中,变量的类型可以动态改变。
请注意上面的代码片段:
- 我在定义变量
my_variable时没有指定它的数据类型。 - 我可以在整个 Python 程序中改变变量
my_variable的数据类型。
简而言之,Python 有动态类型。
在一些编程语言(例如 Java)中,你需要在创建时指定变量的数据类型。此外,变量的数据类型在程序的后期不能改变。我们说这样的编程语言有静态类型。
用静态类型的语言编程可能看起来是一种负担。然而,用静态类型调试编程语言通常不那么麻烦。使用静态类型,您可以更早地发现错误并以最小的努力修复它们。
简单来说:
- 这是你调试动态类型时的表情😢
- 这是你调试静态类型时的表情😎
我将向您展示如何使用类型提示在 Python 中模拟静态类型!
趣闻:动态分型有时也叫鸭子分型。术语来源于众所周知的说法:“如果它走路像鸭,叫声像鸭,那么它一定是鸭。
变量的 Python 类型提示
我将从以下类型提示的应用开始:
类型提示允许您指定变量的数据类型。
因为代码片段比文字更有说服力,所以看一下下面的例子:
类型提示的一个简单例子。
借助类型提示的力量,我已经指定my_variable应该是一个整数。那还不算太糟!😃
对于类型提示,您也可以使用标准数据类型,如float、bool和string 。以下代码片段记录了汽车的一些有用特性:
各种数据类型的类型提示。
如果您尝试在脚本中运行上面的代码片段,那么 Python 毫无怨言。一切看起来都很好!然而,这并不意味着 Python 会检查分配的类型是否正确。事实上,尝试运行以下代码:
错误的类型提示。
什么!?Python 不会用错误的类型提示给出任何错误😧
这是因为 Python 虽然理解类型提示的语法,但根本不关心它。

您将需要使用一个外部类型检查器来检查类型提示是否匹配所提供的变量。外部类型检查器通常是一个外部 Python 模块。我将向您展示如何使用 mypy 模块进行外部类型检查。放心吧, mypy 好用!
用 Mypy 检查类型提示
mypy 的一行描述如下:
如果在代码中加入类型提示,mypy 可以对代码进行类型检查,并找出常见的错误。
要安装 mypy ,使用 PIP(Python 的包安装程序)命令:
pip install mypy
如果这不起作用,则查看文档以获得安装帮助。使用以下代码行创建一个名为simple_type_hint.py 的文件:
简单类型提示。
打开你的终端,导航到你创建文件simple_type_hint.py的文件夹。然后运行命令:
python -m mypy simple_type_hint.py
这将显示以下消息:
**Success: no issues found in 1 source file**
看起来 mypy 对目前为止写的所有东西都很满意。我现在想故意破坏类型提示,看看会发生什么😈
将下面一行代码添加到simple_type_hint.py:
尝试中断到类型提示。
在上面的代码片段中,我用类型提示指定了my_variable应该是一个整数。尽管如此,我后来通过给my_variable赋值一个字符串来打破类型提示。
如果您将文件simple_type_hint.py 作为 Python 程序运行,那么不会像往常一样显示错误。但是,请尝试使用 mypy 命令:
python -m mypy simple_type_hint.py
这应该会显示类似下面的消息:
simple_type_hint.py:5: error: Incompatible types in assignment (expression has type “str”, variable has type “int”)Found 1 error in 1 file (checked 1 source file)
与 Python 不同,mypy 会检测出什么问题。上面的错误消息指定了行号(在我的例子中是 5)和发生的具体问题。
有用!!!

有一个工作实例感觉很棒,不管多简单🔥
有趣的事实:在 mypy 的 GitHub 页面上,有一个来自 2019 年的问题(问题#6740)建议 mypy 不要再把自己称为阿尔法软件。术语 alpha 软件是指处于预发布/早期版本阶段的软件。Python 的创始人吉多·范·罗苏姆介入了讨论,澄清了一些事情:
“我们在这里非常自嘲。我会毫不犹豫地称之为 mypy 产品质量,即超越测试版。”—范·罗森指南
函数的 Python 类型提示
大多数 Python 程序都有大量的函数。我现在将向您展示如何使用函数的类型提示。
在一个名为simple_function.py 的空文件中,我编写了下面这个简单的函数:
一个简单的函数检查整除 2。
函数both_divisible_by_two接受两个整数作为输入。然后检查它们是否能被 2 整除。很简单,对吧?现在我给函数both_divisible_by_two添加类型提示:
带有类型提示的简单函数。
请注意,只有both_divisible_by_two的第一行发生了变化:
- 我写了
a: int和b: int来表示both_divisible_by_two的输入a和b应该是整数。 - 为了表明
both_divisible_by_two的输出是一个布尔值,我使用了语法-> bool。
如果您现在运行通常的命令
python -m mypy simple_funciton.py
那么你应该得到令人满意的输出
**Success: no issues found in 1 source file.**
看起来棒极了!如果您有意做出错误的类型提示,检查 mypy 是否会有反应总是一个很好的明智之举。为了验证这一点,我将both_divisible_by_two 修改如下:
输出带有错误类型提示的简单函数。
如果您再次运行该命令
python -m mypy simple_funciton.py
那么您应该大致得到错误消息
simple_function.py:4: error: Incompatible return value type (got "bool", expected "str")Found 1 error in 1 file (checked 1 source file)
有用!😃
提示:如果您在
both_divisible_by_two中将输出的类型提示更改为int,那么 mypy 将不会给出错误。你明白为什么吗?这还得归功于 Python 中一个微妙的实现细节:布尔类bool是整数类int下的一个子类
0 <-> False1 <-> True。因此,在 Python 中,布尔值“是”整数。当您使用类型提示时,请记住这个细节!
如何将列表添加为类型提示
我们已经看到了如何给函数添加基本类型提示。如何输入变量应该是列表的提示?不幸的是,显而易见的方法(编写list)在 Python 版本 3.8 和更低版本中无效😒
您实际上需要进入内置模块typing 来获取列表的类型提示。让我打开一个名为list_type_hints.py 的新文件,并以下面一行开始:
从类型模块导入列表类型提示。
注意List是大写的。创建一个名为append_element 的函数,将一个值追加到给定的列表中:
一个简单的函数,向列表追加一个值。
到目前为止,一切顺利。如果为data参数传递一个字典,函数append_element将会崩溃。为确保这一点,您可以添加如下类型提示:
带有列表类型提示的简单函数。
如果用命令对文件list_type_hints.py 运行 mypy
**python -m mypy list_type_hints.py**
然后 mypy 报告以下内容:
**list_type_hints.py:5: error: “append” of “list” does not return a valueFound 1 error in 1 file (checked 1 source file)**
这是什么?好像 mypy 在我们的代码中发现了一个错误😱
有什么问题?在继续之前仔细检查 mypy 的回答。
解决方案:列表方法append 就地修改列表但返回None。因此有了这个声明
**return data.append(value)**
将始终返回与
**return None**
要修复代码,您应该进行以下简单的编辑:
修复了简单函数。
马上发现问题,太牛逼了!这里,类型提示发现了一个通常的 Python 解释器不会提醒您的错误😎
类型提示还有一个额外的好处,那就是它们可以更好地记录代码。现在在append_element中可以清楚地看到data应该是一个列表。我也可以使用比data更具描述性的变量名。描述性变量名和类型提示都可以提高代码的质量。
到目前为止,传入append_element的列表中的元素可以是任何类型。例如,它可以是整数列表、字符串列表或由整数和字符串组成的列表。如果我只想接受一个整数列表,那么我可以如下修改代码:
指定列表中元素类型的类型提示。
注意,我把类型提示List换成了更具体的类型提示List[int]。现在列表输入和列表输出只能包含整数!
提示:在
typing模块中,还有字典Dict和元组Tuple等其他容器数据类型。我邀请你在需要的时候看看这些。
增强你的代码的五个令人敬畏的类型提示

托马斯·凯利在 Unsplash 拍摄的照片
现在,您已经熟悉了 Python 中类型提示的基础知识。因此,是时候学习一些更高级的特性了。我将向您展示五个令人敬畏的类型提示,它们将增强您的 Python 代码🐍
可迭代的
要求变量或函数参数应该是一个List有时过于严格。事实上,只要迭代是可能的,类型通常是可以接受的。对于这种情况,您可以使用Iterable类型提示。考虑下面的print_everything功能 :
打印 iterable 中所有内容的函数。
现在可以对任何支持迭代的对象(例如列表、元组、字符串)调用函数print_everything。
提示:在 Python 中,没有显式 return 语句的函数默认返回
None。这就是我为函数print_everything的输出添加了-> None类型提示的原因。
请求即付的
Python 是一种支持 高阶函数 的编程语言。这样做的结果是,您可以将函数作为参数传递给其他函数。考虑下面的wrapper_print函数:
一个愚蠢的包装函数。
属性__name__给出了函数的名称。注意wrapper_print 的输入和输出都是函数。通过使用Callable类型提示,我可以将其指定为类型提示:
在包装函数中使用可调用类型提示。
当试图理解什么是可调用函数时,你经常会遇到这样的说法:“可调用函数是任何可以被调用的东西。”😑
如果你不确定,那么 Python 有内置的函数callable来拯救你。函数callable检查某个东西是否可调用。在日常编码中,当将Callable指定为类型提示时,你应该记住大部分函数。
提示:放心,函数(独立函数和类方法)都是可调用的。相反,请确保数据类型
*int*、*float*、*bool*、*str*、*None*、*list*、*dict*、*tuple*或*set*都是不可调用的。一个有趣的可调用实例是一个拥有 dunder 方法*__call__*的类的实例。
联盟
有时,要求变量或函数参数只能是单一类型过于严格。一个常见的例子是既可以是整数又可以是浮点数的变量。考虑类型提示
变量的浮点类型提示。
可以想见,hourly_wage 也可能是整数。您可以使用Union类型提示来指定许多类型提示:
使用联合类型提示。
在Union中,为变量指定每个适当的类型,用逗号分隔。请注意,这里有一个折衷的情况:通过使用Union添加许多类型提示,您可以获得更大的灵活性。然而,随着您的类型提示变得更加灵活,它们也变得不那么有用了。
提示:在 Python 3.10 中,有一种全新的语法可用:您可以使用运算符
|来分隔不同类型的提示,而不是使用Union。因此我们可以用 Python 3.10+中的int | float替换Union[int, float]。到目前为止,大多数公司还没有使用 Python 3.10。因此,我建议通过使用
Union来熟悉经典的语法。
可选择的
通常,函数中的某些参数默认设置为None。以下面的lowercase_playlist函数为例:
一种降低播放列表中歌曲音量的功能。
在lowercase_playlist中songs 参数默认设置为None。如果songs没有提供值,那么songs 将被设置为一个包含单曲的列表“一切都很棒!”。 最后,我们用列表理解的方式返回歌曲的小写版本。**
您可以编写Union[List, None]为lowercase_playlist提供输入类型提示。但是,这种情况非常普遍,因此有一种特殊的简写语法Optional[List]:
小写函数的可选类型提示。
提示:函数中的默认参数不应该使用可变对象(如列表)。这样做经常会导致恼人的臭虫。关于这个问题,我推荐艾尔·斯威加特的新书《超越蟒蛇的基本技能》第八章https://inventwithpython.com/beyond/****。********
任何的
最后,您可以通过Any提供一个包罗万象的类型提示。Python 文档简单地指出:
Any-特殊类型表示无约束类型。每种类型都与Any兼容。Any兼容各型。
在可以省略类型提示的情况下,为什么还需要Any?😕
在我看来,Any的主要用途是向其他开发人员表明,缺少限制性类型提示是一种有意识的选择。如果您只是没有指定类型提示,那么其他开发人员可能会开始怀疑:
马克忘记在这里添加类型提示了吗?他马虎了吗?嗯…
关注你的开发伙伴,在适当的时候使用Any类型的提示。
为什么所有酷小孩都用 Python 3.9+

丹尼尔·林肯在 Unsplash 拍摄的照片
在 Python 3.9 之前,如果想提供类型提示,需要从typing模块导入List和Dict。
不再有了!
在 Python 3.9 中,终于有了对列表和字典类型提示的原生支持。这意味着您不需要使用typing模块来导入List和Dict进行类型提示。在 Python 3.9+中,可以使用小写的list和dict作为类型提示:
使用列表和字典进行类型提示的新 Python 3.9+语法。
提示: 在 Python 3.9 之前,从
*typing*模块导入*List*和*Dict*时必须大写。这是为了避免覆盖内置的list()和dict()构造函数。在 Python 3.9+中,您可以安全地使用小写版本的list和dict作为类型提示,而不用覆盖任何内容。
啊!唯一比更少的导入更好的是语法一致性😍
总结和更多资源
在本文中,我介绍了 Python 中的类型提示。类型提示是在 Python 代码中加入一些静态类型的现代方法。
更多资源
对于求知欲极强的皮托尼斯塔来说,没有比 pep 484 T1 更好的起点了。此外,mypy 的酷人们还做了一份很棒的小抄。
提示:你可以在你的项目中一点一点地加入类型提示。将部分代码静态类型化,而将其余部分动态类型化是没有问题的。
更多类似内容?
如果你喜欢我的文章,那么看看我的其他博客文章,比如作为数据科学家学习一些前端 Web 开发和 5 个可以在紧要关头拯救你的牛逼 NumPy 函数。如果你对数据科学、编程或任何介于两者之间的东西感兴趣,欢迎在 LinkedIn 上加我,并向✋问好
Alexa 奖中的模块化梦想社交机器人
一个开源对话系统项目的介绍和演练
2019 年春天,在 Mikhail Burtsev 的带领下,一队来自莫斯科物理技术学院(MIPT)的学生被选中参加来自亚马逊的 Alexa Prize Challenge 3 。这是正式开始的梦想社交机器人的发展,它现在还活着,已经 2 岁了。我们在 Alexa Prize Challenge 3 的旅程在半决赛后于 2020 年 5 月结束,因为我们不幸没有被选中进入决赛。但是我们设法使用开源的 DeepPavlov 代理框架创建了我们的第一个 DREAM socialbot 版本。在半决赛后,我们花了 4 个月的时间添加对知识图(KGs)工作的支持,目标是在 2020 年下半年最终开源整个机器人。然而,在 9 月下旬,亚马逊宣布了 Alexa Prize Challenge 4,我们的应用程序再次自豪地被选中参与。竞争非常激烈,虽然我们进入了半决赛,但遗憾的是我们没有进入决赛。无论如何,我们从这 2 年在生产开发中运行 socialbot 中学到了很多,我们准备与感兴趣的读者分享我们的知识,所以我们正在开始一系列关于梦想 socialbot 的文章。

来自 Alexa 奖挑战 4 的梦之队。
对话系统主要有两种分类:
- 面向任务和闲聊,
- 开域和闭域。
面向任务的系统以完成某项任务为目标来进行对话,例如,预订机票或预定餐桌。聊天系统旨在进行没有特殊对话目的的聊天。对话系统的域决定了对话的话题限制,因此封闭域系统只覆盖一个或几个话题,而开放域系统可以就任何话题进行对话。
因此,由于参与 Alexa Prize Challenge 的 socialbots 应该能够支持各种流行话题的对话,根据定义,它们是开放域聊天对话系统。
对话系统的另一个重要特征是体系结构,它基本上分为整体式和模块化系统。整体(或端到端)对话系统是一种处理输入并返回与对话上下文相关的最终响应的模型。这样的对话系统可解释性低,几乎不可控。鉴于模型的规模受限于资源和最大响应时间,该模型最初不能很好地覆盖开放领域对话并同时具有丰富的词汇。因此,现在大多数对话系统都具有模块化架构,包含许多不同的基于规则、机器学习和深度学习的模型。
DREAM Socialbot 是一个模块化的对话系统,使用开源的 DeepPavlov Agent 框架实现。原始 DREAM socialbot 的整体结构如下图所示。

Alexa 奖挑战赛 3 结束时的梦想社会机器人架构。图片来自技术报告
对应于 DeepPavlov 代理架构有几个主要部分:标注器、技能选择器、技能、响应选择器、对话状态。最初,用户输入话语由许多自然语言理解(NLU)模型处理,这些模型也被称为注释器,包括不同的校正、分类和标记组件。考虑到对话状态和当前用户话语注释,技能选择器挑选出将尝试为当前上下文生成响应的技能列表。有超过 2 打检索和基于规则的技能。
有趣的是,我们试图集成一个生成序列到序列的神经模型,但由于其不可预测性和低控制机会,它没有向用户发布。
所有选择的技能都返回一个、几个或零个候选答案,这些答案将被标注做出最终选择所必需的信息。响应选择器过滤掉不合适的候选响应,应用手写试探法,并使用经验公式来选择最终响应。之后,可以使用用户名(如果知道的话)扩展响应,并使用特殊的链接问题来进一步展开对话。所有组件都可以访问对话状态,该状态存储了对话历史,其中包含每个话语的注释,甚至候选人响应,以及关于社交机器人和用户个性的结构化信息。模块化架构允许不同的开发人员分别开发组件,这加快了开发过程,并让所有团队成员都参与进来。
梦之队使用了 DeepPavlov 代理框架,而所有其他团队都在亚马逊的私有 CoBot 框架之上构建他们的 socialbots。一般来说,很多努力都致力于 DeepPavlov 代理的适应、部署和分析工具、检索中的基本内容和基于规则的技能。
在 Alexa Prize Challenge 3 之后,我们的团队打算开源我们的梦想社交机器人。我们花了整个 2020 年夏天来适当地启动这一进程,重点是构建 CoBot 服务的替代品,特别是构建一个开源机制,使用我们在 2020 年 5 月在 DeepPavlov 库中发布的全新 KBQA 组件来回答事实问题。虽然我们已经成功地只用开源组件构建了一个新版本的 socialbot,但我们只能在 2020 年 9 月 5 日之前在我们的 demo.deeppavlov.ai 网站上提供一个演示聊天机器人。虽然我们有更大的计划,但令人惊讶的是亚马逊宣布了 Alexa Prize Challenge 4,由于之前疫情推迟了之前的挑战,它的开始时间较短。因此,没有足够的时间来仔细重构 socialbot,我们决定将梦想 socialbot 的发布推迟到挑战 4 的结束。这个计划给了我们一个机会,让我们更专注于 socialbot 的内容和胆量,并开源了一个更好的版本,尽管比原计划晚了一年。
Alexa Prize Challenge 4 中的 DREAM Socialbot 因此基于原始 DREAM Socialbot 的最终版本,并在去年夏天进行了改进。虽然其中一些改进,如更换基本的 CoBot 分类器,在挑战赛期间被暂时取消,因为我们可以访问更新的服务,但我们在知识图(KGs)上的工作成为去年夏天对我们新的 DREAM Socialbot 的主要贡献。由于模块化架构,这很容易做到,所以 KGs 成为了注释器的一部分。前一年版本中的一些技能利用远程 API 来获取一些有用的信息。我们也将这个知识收集过程作为注释者之一。这给了我们一个在所有技能之间分享结构化知识的机会。
主要目标之一是扩展社交机器人的内容。此外,通过分析去年的技术报告,我们还得出结论,仍然没有比基于脚本的对话更好的对话一致性控制。因此,我们参与 Alexa Prize Challenge 4 的主要成果之一是开发和发布了对话流框架(DFF)——一个用于脚本对话系统开发的开源框架。DFF 给了我们一个机会,让我们摆脱特定主题的检索技能,转而使用脚本化的特定主题技能,这种技能至少能给人留下几轮连贯对话的印象。模块化架构允许技术人员利用来自标注器的所有可用信息,从用户话语分类到提取的实体和检索的知识。

Alexa 奖挑战赛 4 结束时的梦想社会机器人架构。图片来自技术报告
总之,在 DeepPavlov 代理框架内实现的对话系统的模块化架构允许我们:
- 由不同的开发者进行不同组件的单独开发,
- 利用来自所有组件中注释器的所有可用信息,
- 结合不同来源的独立技能(结构、框架),
- 在同一级别的并行组件中运行。
AI 助手模块的独立、单独开发和并行执行对比赛来说都非常重要,允许我们有效地使用所有可用的人力和计算资源。
顺便说一下,DeepPavlov 代理架构非常高效,能够承受生产中对话系统所必需的高负载。下一篇文章将专门讨论 DeepPavlov 代理的异步管道,它的特性、优点和缺点。稍后我们还将详细介绍 DREAM socialbot 的结构和组件、对话流框架、我们的开发流程和功能,以及许多见解和技巧。敬请期待!
你可以在我们的官方博客上阅读更多关于 DeepPavlov 生态系统的信息。此外,请使用我们的演示来测试我们基于 BERT 的模型。请在 Github 页面上为⭐️美国星。别忘了 DeepPavlov 有一个专门的论坛,欢迎任何关于框架和模型的问题。
使用 RDKit 和 Py3DMol 在 Streamlit 中进行分子可视化
第 1 部分:RDKit

Streamlit 中的 RDKit 和 Py3DMol。图片作者。
1.介绍
在我知道Streamlit【1】后不久,我的第一个问题【2】是,如何可能部署分子结构?在这篇文章和下一篇文章中,我将把我所学到的贡献出来,希望这对其他人有所帮助。
在这个由两部分组成的系列中,我将重点介绍两个流行的库,RDKit 和 Py3Dmol。第一种广泛用于分析化合物,并在 2D 用图形表示分子。第二个是渲染 3D 结构的极好选择,从分子到像蛋白质或病毒这样的大分子,还有电子密度的 3D 网格。
第一篇文章应该简短明了,我不打算深入探讨 Streamlit 更复杂的用法。为此,我建议访问论坛【3】并查看文档【4】。如果您不了解 Streamlit,它是一个很好的框架,可以从 python 脚本快速开发令人印象深刻的 web 应用程序。
2.安装必备库。
所以,让我们从安装库开始。我假设您已经安装了 Streamlit 如果没有,请执行以下操作:
pip install streamlit
这里我们将使用 RDkit 的 pip 可安装版本,我们可以这样安装它:
pip install rdkit-pypi
顺便提一下,在 google Colab 上安装 rdkit-pypi 也能很好地工作,这对于原型开发来说非常好。您可能还希望以 RDKit 处理 PIL 对象的相同方式安装 Pillow,尽管这不是强制性的。
3.构建应用程序。
使用 Streamlit 的最大优势是,只需几行 python 代码,您就可以拥有一个工作的 web 应用程序。下面是快速开始使用 RDKit 的最少代码:
对于不熟悉 RDKit 的,我简单解释一下。在第 6 行中,我们定义了一个微笑字符串,其中原子和键由 ASCII 字符表示。在这个例子中,分子是阿司匹林。其他例子有:
- 乙醇 CCO
- 乙酸 CC(=O)O
- 环己烷 C1CCCCC1
- 吡啶 c1cnccc1
第 7 行从微笑构建了一个 mol 对象,第 8 行创建了一个 PIL 对象,最后在第 10 行由 Streamlit 用 st.image() 显示。生成的最小 web 应用程序如下所示:

在 Streamlit 中使用 RDKit 的简单示例的屏幕截图。图片由作者提供。
或者,您可以将第 8 行替换为:
Draw.MolToFile(m,’mol.png’)
这将把 mol 对象写成 png 图像,您可以使用
st.image('mol.png')
当然,您可能希望通过使用 Streamlit 小部件在 web 应用程序中包含更多的交互性。尝试添加一个文本字段来输入微笑字符串,或者使用滑块来查看多个分子!!您还希望看到哪些其他用例??
4.在 Streamlit 云上托管它!
如果您使用的是 Streamlit Cloud [5】,那么在 github repo 中需要一个 packages.txt 文件,只有一行“libxrender1”。这是从经验中学到的东西,没有记录下来,但是最终人们会发现在日志文件中需要它。
此外,您可以使用 rdkit-pypi 和 Pillow 添加一个“requirements.txt”文件:
仅此而已。你可以在这里看到这个小项目的回购。这是共享应用程序的最佳方式。 Streamlit Cloud 提供社区层(免费!!)来托管您的 web 应用程序。因此,如果您打算使用 Streamlit,您将会爱上这项服务。请点击此处查看示例应用的发布版本:
https://share . streamlit . io/napoles-uach/medium _ mol/main/app 1 . py
5.结论。
这样,你就可以建立更复杂的分子可视化了。在本系列的第二部分,我将向您展示我在 Streamlit 中使用 Py3DMol 时学到的一些技巧。
最后,我邀请你来参加论坛,打个招呼,并使用 Streamlit 和 RDKit 向我们展示你的创作!!第二部见!!
6.致谢。
我要感谢伟大的数据教授Chanin Nantasenamat,感谢他在撰写这篇文章时的善意建议。
如果你喜欢这篇文章,可以考虑订阅邮件列表:
https://jnapoles.medium.com/subscribe
参考资料:
[2]https://jna poles . medium . com/is-it-possible-to-use-streamlit-visualize-molecules-b4e 29700 f61c
[3]https://discuss.streamlit.io/
[6]https://github.com/napoles-uach/Medium_Mol
分子网络第 1 部分:化学和生命科学中深度学习的数据集

从 Unsplash
思想和理论
迈向分子机器学习的“图像网络时刻”
本文由 DeepChem 的 Bharath Ramsundar 共同撰写。
基准数据集是机器学习进步的重要驱动力。与计算机视觉和自然语言处理不同,化学和生命科学中数据集的多样性和复杂性使这些领域在很大程度上抵制了制定社区广泛接受的基准的尝试。在这篇文章中,我们将展示如何向分子机器学习的MoleculeNet基准添加数据集,并通过deep chemAPI 以编程方式访问它们。

图片来自 moleculenet.ai 。
分子 ML 数据集策展
MoleculeNet[1]收集六个主要类别的数据集:量子力学、物理化学、蛋白质、生物物理学、生理学和材料科学。MoleculeNet 的“第一代”展示了分子 ML 基准可能的样子,并揭示了一些有趣的趋势,这些趋势涉及数据稀缺、类不平衡以及物理感知特征在一些数据集的模型架构上的力量。
涵盖分子 ML 的整个广度和深度并不容易,因此 MoleculeNet 正在发展成为一个灵活的框架,以标准化的方式贡献数据集和基准模型性能,由 DeepChem 提供支持。
我们为什么要关注性能指标评测?
图像和语音识别看起来像是巨大的任务,但与我们在物理、化学和生物中看到的问题相比,它们真的非常简单。这就是为什么相对来说很少看到有人声称物理或生命科学中的问题已经被机器学习“解决”了。更好的数据集、数据集生成方法和稳健的基准是分子机器学习进展的必要因素,甚至可能比发明新的深度学习技巧或架构更重要。
在深度学习的许多子领域,标准的进展途径是这样的
1.选择一个广泛使用的基准数据集 ( 例如ImageNet , CIFAR-10 ,或者 MNIST )。
2.开发并测试一个模型架构,该架构在基准测试的某些方面达到“最先进”的性能。
3.想出一个特别的“理论”解释,来解释为什么你的特定架构优于其他架构。
4.在顶级会议上发布您的结果。
如果你幸运的话,在下一个 SOTA 建筑问世之前,其他研究人员甚至可能会使用你的模型或在此基础上进行他们自己的研究。这种范式存在明显的问题,包括数据集中的偏差、分布偏移,以及古德哈特-斯特拉森定律 —当一个指标成为目标时,它就不再是一个好的指标。尽管如此,毫无疑问,基准测试提供了一种明确的目的,并激发了其他领域所缺乏的对机器学习研究的兴趣。
也许更重要的是,基准测试鼓励和奖励研究人员创建高质量的数据集,这在许多领域一直没有得到应有的重视。基准数据集能够实现惊人的突破,如 DeepMind 的 AlphaFold(阿尔法折叠)通过几十年的努力组装高分辨率蛋白质结构(T7)而成为可能。AlphaFold 代表了蛋白质折叠中的一种“ ImageNet moment ”,这意味着一个问题在某种意义上被“解决”了。
MoleculeNet 包含数十万种化合物和测量/计算的特性,所有这些都可以通过 DeepChem API 访问。它带来了 ML 会议中流行的传统评估框架的味道,但也提供了贡献和访问新数据集的标准化方法。
向 MoleculeNet 提供数据集
数据集贡献已得到显著简化,并且已记录。第一步是在 DeepChem repo 中的 GitHub 上打开一个问题来讨论您想要添加的数据集,强调该数据集涵盖哪些独特的分子 ML 任务,这些任务尚未成为 MolNet 的一部分。如果您自己创建或管理了一个数据集,这是一个与 molecular ML 社区共享它的好方法!接下来,您需要
- 写一个继承自
[deepchem.molnet.load_function.molnet_loader._MolnetLoader](https://github.com/deepchem/deepchem/blob/master/deepchem/molnet/load_function/molnet_loader.py#L82)的DatasetLoader类。这包括记录数据集的任何特殊选项和 ML 的目标或“任务”。 - 实现一个
create_dataset函数,通过应用可接受的特征、拆分器和转换来创建一个 DeepChemDataset。 - 编写一个
load_dataset函数来记录数据集,并为用户加载数据集提供一个简单的方法。
QM9 MolNet 加载器源代码是编写自己的 MolNet 加载器的一个很好的、简单的起点。
该框架允许数据集直接在 ML 管道中使用任何合理的特征组合(将 SMILES 字符串之类的原始输入转换为机器可读格式)、拆分器(控制如何构建训练/验证/测试集)和转换(,例如,如果目标需要在训练之前被规范化)。
分割线在这里尤为重要。在比较不同模型在相同任务上的表现时,每个模型“看到”相同的训练数据并根据相同的数据进行评估是至关重要的。我们还想知道一个模型在相似的样本上的表现如何(使用随机训练/val/测试分割),以及在不同的样本上的表现如何(例如,使用基于化学子结构的分割)。
使用 DeepChem API 访问数据集
MolNet 加载器使访问数据集和为 ML 预处理数据集成为可能,只需一行 Python 代码:
from deepchem.molnet.load_function.qm9_datasets import load_qm9tasks, (train, val, test), transforms = load_qm9()
要通过 DeepChem API 实际使用数据集,您只需向 DeepChem 开发人员提供一个 tarball 或压缩文件夹,他们会将其添加到 DeepChem AWS S3 存储桶中。最后,为您的加载器和数据集添加文档。
我们需要您的数据集!
在看了 MoleculeNet 中长长的数据集列表后,你可能会发现缺少了一些关键的东西。好消息是你(对,就是你!)可以贡献新的数据集!如果你对 Python 编程不太熟悉,你可以在 GitHub 上打开一个问题,包括为什么要将数据集添加到 MolNet 的信息,并向 DeepChem 开发人员寻求帮助。如果你喜欢编程,那就更好了——你可以按照上面概述的步骤做点贡献。
开源基准的真正力量在于任何人都可以做出贡献;这使得 MolNet 的发展和扩展超出了单个研究小组所能支持的范围。
下一步:分子 ML 模型性能
在下一篇文章中,我们将讨论如何使用 DeepChem 和 MolNet 脚本为 ML 模型添加性能指标。
取得联系
如果你喜欢这个教程或者有任何问题,请随时通过 电子邮件 联系内森,或者连接上LinkedIn和Twitter。
你可以在他的 网站 上找到更多关于内森的项目和出版物。
感谢吴对本文的反馈。
参考文献
[1] DOI: 10.1039/C7SC02664A(边缘文章)化学。Sci。, 2018, 9, 513–530
单子解释道

所以,我费了很大的劲才明白“单子”到底是什么!到处都有人用公式或长篇大论来解释它,以至于人们几乎不可能理解它。在这里,我将用简单的语言解释复杂的逻辑。另外,如果你有兴趣看这个主题的视频,请看看下面的视频。
此外,为了便于理解,我将用 Python 编码,这样 Haskell 的语法就不会把你吓跑。
在开始之前,让我首先向您介绍 Haskell 的主要特性,因为它首先需要单子——函数式编程。函数式编程是一种思维模式,在这种模式下,所有的设计都是根据纯函数来考虑的。
- 职能是一等公民。所以,所有简单的逻辑都是一个函数,所有复杂的逻辑都是通过函数间的运算来处理的。
- 函数必须是纯的。这意味着无论你给什么输入,输出都应该保持不变。与纯函数交互的唯一方式是只通过输入和输出。它不能访问全局状态,也不能打印任何东西,甚至不能抛出异常,除非在函数定义中进行了定义。
Monad 是一个通用的概念,它有助于在纯函数之间进行操作,以处理副作用。就是这样。让我用一个例子来解释一下,一会儿就明白了。
问题陈述
对一个数做多次平方运算。
def square(num: int) -> int:
return num * num;print(square(square(2)));**--------------------------------------------------------------------
Output**
16
引入副作用
现在,让我们通过打印输入的当前值来给函数添加一个副作用。
def sqaure_with_print(num: int) -> int:
print("Currrent num: ", num);
return num * num;print(sqaure_with_print(sqaure_with_print(2)));**--------------------------------------------------------------------
Output** Currrent num: 2
Currrent num: 4
16
有副作用的纯函数
由于 print 语句的引入,上述函数不再是一个纯函数。现在该怎么办?如何处理纯函数中的副作用?记住,与纯函数交互的唯一方式是通过输入和输出。我们需要在输出本身中获取日志。
def sqaure_with_print_return(num: int) -> (int, str):
logs = "Currrent num " + str(num);
return (num * num, logs);print(sqaure_with_print_return(sqaure_with_print_return(2)));**--------------------------------------------------------------------
Output** Traceback (most recent call last):
File "hello.py", line 21, in <module>
print(sqaure_with_print_return(sqaure_with_print_return(2)));
File "hello.py", line 17, in sqaure_with_print_return
return (num * num, logs);
TypeError: can't multiply sequence by non-int of type 'tuple'
自定义排版
哎呀!虽然我们能够把它变成一个纯粹的函数,但是为了把函数链接起来,同时输出副作用,破坏了程序。但是为什么会这样呢?该函数需要int,而我们通过了(int, str)。看起来只是期望不匹配。看起来需要做一些特殊处理。
我们可能需要修改square_with_print_return,,以便它可以接受(int, str)而不是int。我们是否应该改变一个函数的输入签名,以便它们可以组合?这是可扩展的吗?
相反,让我们添加一个定制的 compose 函数,它知道如何处理上述参数不匹配的情况。
def sqaure_with_print_return(num: int) -> (int, str):
logs = "Currrent num " + str(num);
return (num * num, logs);def compose(func2, func1, num: int):
res1 = func1(num)
res2 = func2(res1[0])
return (res2[0], res1[1] + res2[1]);print(compose(sqaure_with_print_return, sqaure_with_print_return, 2));**--------------------------------------------------------------------
Output** (16, 'Currrent num 2 Currrent num 4')
耶!成功了!现在,假设我们想要链接 3 个函数,必须重写这个组合函数,对吗?我们需要找到一种可伸缩的方式来编写这个函数,以便它可以处理任意数量的函数。
包装纯函数
更好的解决方案是使用其他包装函数,这有助于以所需的格式处理输入和输出参数。这个函数将知道如何在不改变纯函数参数的情况下处理副作用。
from typing import Tupledef sqaure_with_print_return(num: int) -> (int, str):
logs = "Currrent num " + str(num);
return (num * num, logs);def bind(func, tuple: Tuple[int, str]):
res = func(tuple[0])
return (res[0], tuple[1] + res[1])def unit(number: int):
return (number, "");print(bind(sqaure_with_print_return, (bind(sqaure_with_print_return, unit(2)))))**--------------------------------------------------------------------
Output** (16, 'Currrent num 2 Currrent num 4')
- 绑定函数:这只是
square_with_print_return上的一个包装函数,它接受一个元组,而不仅仅是 int。所以,所有这样的函数都可以用绑定函数来包装。因此,现在您的纯函数可以处理任何类型的输入参数 - 单元函数:但是我们的第一个参数是一个整数。有人应该把它转换成一个元组。这就是单位函数的作用。
这是我的朋友,叫莫纳德!
最后,我们可以说,单子只是处理纯函数中副作用的一种漂亮而通用的方式,并提供了一种通过使用绑定和单元概念来组合纯函数的可伸缩方法。
Mongo 数据库与 SQL 数据库:应该使用哪一个?
Mongo 和 SQL 数据库的应用比较

(src =https://pixabay.com/images/id-5015530/
介绍
数据库世界可能是一个非常复杂的世界。有难以想象的大量结构化和非结构化数据驻留在遍布全球的不同数据中心。这些数据库中的大多数都利用某种类型的数据库,以便允许工程师查询数据库以获得结果,并且实际上对存储在这些数据库中的数据做一些有价值的事情。
查询数据库领域最流行的解决方案是 easily SQL。大多数时候,SQL 查询是从一些 SQL 派生出来的,比如 MySQL 或 PostgreSQL。然而,还有其他一些鲜为人知的解决方案,其中包括您可能听说过的一个:MongoDB。基于 SQL 的解决方案和 MongoDB 之类的解决方案之间存在明显的区别,这也正是我今天想要讨论的内容。
结构化和非结构化
SQL 及其衍生物和 MongoDB 之间第一个也是最显著的区别是结构。SQL 数据库有一个非常特殊的结构,这是任何 SQL 数据库都需要的。如果没有这种结构,SQL 指针甚至无法查询 SQL 数据库,因此这无疑是 SQL 的一个重要部分。
MongoDB 的结构有点不同,通常被认为是非结构化数据库。然而,这不应该使你放弃,因为所有的数据在默认情况下都有一个结构——即使这个结构只是标签。MongoDB 的伟大之处在于,数据通常存储在其原生结构中。这意味着您可以更好地控制如何存储和查询您的数据,这是我非常喜欢 MongoDB 的一点。
非结构化数据的另一个显著区别是,非结构化数据是定性的,而结构化数据是定量的。结构化数据更容易理解和深入研究,因为它是以一种逻辑统一的方式存储的。另一方面,非结构化数据将需要一些工作和处理,但这些只是两种解决方案的一些权衡。由于结构化数据仅以定义好的格式存在,所以无论何时出现问题,寻求帮助也容易得多。某些非结构化数据库的信息可能非常缺乏,通常您可能需要依赖数据库的维护者来获得有关您正在查询的数据的基本信息。
简而言之,MongoDB 数据库很酷,因为它们在如何存储数据方面提供了更多的灵活性。然而,也有一个代价,那就是缺乏结构会使许多用 SQL 本来容易做的事情变得更加困难。MongoDB 数据库可以快速设置和运行,但是缺少在某些情况下轻松处理存储在其中的数据所需的结构。这开始揭示这些差异如何在数据世界中扮演不同的角色。非结构化的 MongoDB 解决方案更容易运行,而结构化的 SQL 解决方案更容易一致地运行。这就是为什么在原型阶段部署之前使用 MongoDB 非常流行的原因。
工业
在比较任何一种技术时,需要注意的一件重要事情是行业正在用这些技术做什么。我会第一个说我从来没有在行业内使用过 MongoDB。也就是说,我在业内工作过的大多数数据库都是成熟的数据库。也就是说,并非所有数据科学工作都是如此。
鉴于这些知识,我的建议是主要关注 SQL,但是一定要熟悉和了解 MongoDB。根据我的经验,MongoDB 是构建原型的流行选择。这对于构建数据库原型很有帮助,因为唯一的选择是在本地存储数据,并且根本不处理请求或查询。很容易理解为什么使用 MongoDB 数据库会越来越少。
那么,您应该使用哪一个呢?您应该最熟悉这些数据库中的哪一个呢?应该注意的是,数据库提供的最重要的东西在每种情况下都是独一无二的,但是与非结构化数据库相比,您很可能会更经常地使用结构化数据库。对于那些想雇佣你的公司来说也是如此,因为他们宁愿花时间提前建立一个,以便将来更容易访问数据。我的建议是学习或熟悉这两者,但在许多不同的应用中,这两者肯定都有潜在的用例。
结论
MongoDB 和 SQL +及其衍生工具都是存储和访问数据的优秀数据库和查询框架。然而,MongoDB 和 SQL 之间有一些关键的区别,在您过于认真地对待其中任何一个之前,您可能应该知道这些区别。MongoDB 是非结构化的,主要用于在放入结构化的、通常基于 SQL 的数据库之前进行原型开发。我个人最喜欢使用的一直是 PostgreSQL。感谢您阅读我的文章,我希望它是翔实的!
MongoDB:从安装到实现
第 5 部分介绍如何使用和充分利用 MongoDB

马库斯·斯皮斯克在 Unsplash 上的照片
MongoDB 前情提要
在第四部分中,我们为 MongoDB 集合设置了插入和删除端点。我们使用 pymongo 和 pydantic 的组合实现了这一点。在连接到数据库之后,我们使用 FastAPI 端点来传递新记录,以便插入到数据库中,或者传递要删除的 ID。为了测试,我们使用了 Postman。
这次在 MongoDB 上
考虑了一会儿之后,我决定如何创建更新端点。回忆更新比简单地选择要更新的项目要稍微困难一些,因为我们已经在几个集合中嵌入了集合。请注意,在嵌入式集合的情况下,这可能不是最简单的解决方案,甚至不是执行更新的正确方式。这只是我的研究和发现。如果你对如何比我的解决方案更有效地执行更新有想法,我很乐意了解更多,所以请在评论中分享。现在,我们必须先学习如何在 MongoDB 中更新。
更新 MongoDB 中的集合
如果你连接有问题,记得你必须停止并重新启动 mongod。我们在以前的部分中讨论过这个问题,所以如果您需要复习,请回顾一下。
使用我们的收据数据库,我们应该看一下食品杂货收集,因为它可以用于显示定期更新和嵌入文档发行的示例:

杂货收集已查询。
现在我们有了 ID,我们可以使用 update 命令来更改总数。为此,只需使用带有一系列参数的“update”命令。要发送的第一个集合将是一个 JSON 对象,它说明要更新应该满足什么条件。就关系数据库而言,这相当于 WHERE 语句。第二组是你想要改变的键,然后是你想要替换的值。对于我们的第一个示例,我们将只更新总数:
db.Grocery.update({_id:ObjectId(“<IdHere>”)}, {$set: “Total”: 9.77}})

更新结果。
我们的下一次更新将显示嵌入文档的问题。如果我们想使用这样的名称来明确指定,我们将不会收到任何更新:
db.Grocery.update({_id:ObjectId(“<IdHere>”), ProductName: “Cat Food Can”}, {$set: {“UnitPrice”: 0.70}})

更新失败。
没有找到文档,但是我们从数据中知道有一个文档存在。问题是在原始文档中,没有“产品名称”。只有一个包含“产品名称”的“项目”。
稍微复杂一点,我们可以在 Items 对象中使用元素匹配函数。这样,我们可以指定应该在条目中更新哪个产品。在 set 部分,我们将调用 Items 对象,然后声明应该更新哪个值:
db.Grocery.update({_id:ObjectId(“<IdHere>”), Items:{$elemMatch:{ProductName: "Cat Food Can"}}}, {$set: {"Items.$.UnitPrice": 0.70}})

更新成功。
现在,再次检查文档以验证:

更新的杂货项目。
回想一下,我们有另一个只包含一个商品的杂货文档。但是如果我们忘记添加一个项目到列表中呢?我们需要找到一种方法来更新整个项目列表。因此,首先我们将决定只更新一个商品的购物清单:

要更新的杂货项目。
为了在不损害任何现有项目的情况下进行添加,我们可以使用“push”命令。这类似于对列表进行的推送,只需将新项添加到末尾并维护现有项。现在我们可以试一试:
db.Grocery.update({_id:ObjectId("<IdHere>")}, {$push: {Items: {ProductName: "Bubble Gum", UnitPrice: 0.99, "Quantity": 1}}})

将新产品添加到项目列表中。
为了确保一切正常,请再次检查我们的条目:

已添加经过验证的产品。
如果您发现您不想添加该项目,有一种方法可以逆转我们的“推送”。我们可以用“拉”来代替。通过使用这个,MongoDB 将搜索指定的任何产品名称,然后删除嵌入的文档。
db.Grocery.update({_id:ObjectId("<IdHere>")}, {$pull: {Items: {ProductName: "Bubble Gum"}}})

从项目列表中删除产品。
现在再次检查我们的数据:

经验证的产品已被移除。
最后一个基本更新是在所有项目都需要替换的情况下。这可能是因为收据上有错误的项目,需要更新为正确的项目。对于这种类型,我们可以只使用一个“集合”。回想一下,设置一个值将覆盖任何以前的数据,所以请确保您正在更新您想要更改的文档。为了更改我们修改的最后一个杂货列表的元素,我们将把 Items embedded documents 设置为一组新的嵌入式文档。
db.Grocery.update({_id:ObjectId("<IdHere>")}, {$set: {Items: [{ProductName: "Bananas", UnitPrice: 0.58, Quantity: 2}, {ProductName: "Baby Food", UnitPrice: 0.87, Quantity: 1}]}})

覆盖项目。
当我们再次检查条目时,旧数据将不再存在。相反,新数据应该已经取代了它:

项目已被覆盖。
还可以选择一次更新多个文档,以及每个文档中的多个字段。但是,出于我们的目的,我们可以使用我们已经了解的基本更新。如果我们以后需要多次更新,我们可以在那时学习。现在,我们将能够转移到更新的 FastAPI 部分,因为 pymongo 将与传统的 mongo 有所不同。
结论
在能够转移到更新 FastAPI 端点之前,我们首先需要学习如何在 MongoDB 中执行更新。我们讨论了语法,包括几种不同的更新方法。
首先,我们学习了如何使用“set”语句进行标准更新。接下来,我们使用相同的语句来定位嵌入的文档元素。在此之后,我们讨论了一个更新问题,如果要保留原始文档,需要向嵌入的 JSON 对象中添加一个新文档。在这样做的时候,我们使用了一个“push”语句来维护现有的数据,同时添加新的条目。为了避免添加条目,我们还学习了如何通过使用“pull”命令删除指定的条目来删除嵌入的文档。这样,任何其他元素都将保留。最后,我们学习了如何重置项目列表中的每个文档,如果错误的项目被添加到特定的收据,或者如果您只想重新开始。这是通过再次使用“set”命令实现的。作为注释,我们讨论了更新多个文档或多个字段的可能性。然而,这暂时超出了我们的项目范围,所以我们跳过了这一部分。
所以这次我们没有到达任何 FastAPI 端点,但是我觉得我们学到了一些关于如何在 MongoDB 中更新的有价值的信息。下一次,我们可以使用我们关于更新如何工作的知识来确定如何正确设置我们的端点。在那之前,干杯!
用我的 每周简讯 免费阅读我的所有文章,谢谢!
想阅读介质上的所有文章?成为中等 成员 今天!
| 看看我最近的一些文章
https://medium.com/swlh/should-i-go-to-college-9640d121e1f1 https://python.plainenglish.io/i-fell-in-love-with-fastapi-but-i-made-some-mistakes-224219026aea https://levelup.gitconnected.com/trying-out-the-docker-hub-cli-tool-e33c56d289b3 https://medium.com/codex/a-journey-with-kubernetes-part-4-apis-19b311290a4f https://docs.mongodb.com/manual/reference/method/db.collection.update/ https://stackoverflow.com/questions/6758339/updating-embedded-document-property-in-mongodb https://specify.io/how-tos/mongodb-update-documents https://stackoverflow.com/questions/26967525/insert-an-embedded-document-to-a-new-field-in-mongodb-document https://docs.mongodb.com/manual/reference/operator/update/pull/ https://docs.mongodb.com/manual/reference/operator/update/set/


浙公网安备 33010602011771号