TowardsDataScience-博客中文翻译-2022-三十六-
TowardsDataScience 博客中文翻译 2022(三十六)
机器学习系统的成熟度
原文:https://towardsdatascience.com/maturity-of-machine-learning-systems-358bab34bf69
机器学习系统现有成熟度框架综述

机器学习系统成熟度。图片由作者提供
低质量的机器学习系统对业务不利。它们很难适应和改进来满足业务需求。同时,它们也很贵。他们在错误修复和调试上消耗了大量的操作资源。随着时间的推移,他们倾向于积累技术债务和熵。它们脆弱、不可靠,并导致糟糕的客户体验。因此,不断提高我们机器学习系统的质量和成熟度至关重要。
然而,根据我的经验,机器学习行业普遍存在低质量的系统。为什么?在我看来,缺乏结构化的指导是最重要的因素。没有这样的指导,改进活动通常是稀少的和不系统的。机器学习团队常常不得不为了更切实的商业目标而牺牲它们。或者他们被一个完全成熟的环境的要求所淹没,而不知道实现它的渐进步骤。
在本文中,我试图解决这个问题,并提供三个现有框架的概述,这些框架可以指导您的 ML 团队构建成熟的生产就绪的机器学习系统:
- Google Cloud MLOps 成熟度级别和 Azure MLOps 成熟度模型 —专注于采用 CI/CD 和持续培训等 MLOps 实践的通用框架。
- ML 测试分数 —专注于测试和监控的 Google 框架。它提供了一个量化机器学习系统成熟度的单一指标,以及一系列改进它的具体活动。
机器学习成熟度
当我们谈论机器学习的现实应用时,我们需要考虑机器学习系统。不仅仅是机器学习模型。在我之前的一篇文章中,我概述了这些概念之间的区别,并提供了一些例子。前者包含后者,并定义了我们如何训练和使用它。我喜欢将术语机器学习系统定义为使用机器学习方法向客户交付价值的所有活动及其工件。
为什么谈论机器学习系统比仅仅关注提高我们的模型质量更有帮助?因为创建可靠的、生产级的机器学习系统暴露了许多小玩具示例或研究实验中没有发现的新问题。机器学习系统很难构建和维护。
- 机器学习系统很脆弱。我们在建立它们的时候做了很多隐含的假设。数据分布或契约的微小变化都可能使整个系统行为失常。与传统的软件系统相比,这些错误很难被发现。
- 他们积累技术债务。与传统软件相比,机器学习系统有许多额外的债务来源。技术债务很难量化和区分工作的优先次序。随着时间的推移,它会消耗越来越多的运营成本,并减缓任何改进。
- 他们需要激励不一致的孤立团队的协作。默认设置中的数据科学家、ML 工程师和数据工程师具有相互矛盾的目标和 KPI,这在系统中造成了内部冲突。
为了让机器学习系统可以投入生产,它必须处理这些问题。这就是我们所说的成熟。机器学习成熟度是机器学习系统的属性,而不是机器学习模型。它反映了:
- 您的数据科学团队改进模型的速度。
- 生产中发生的错误数量。
- 我们识别这些错误并从中恢复的速度有多快。
机器学习成熟度是机器学习系统的属性,而不是机器学习模型。
让我们深入现有的机器学习成熟度框架。
Google Cloud MLOps 成熟度级别
这个框架在这个谷歌云架构中心文档中定义。它将重点放在 MLOps 上。
MLOps 是一项将 DevOps 实践和文化适应于机器学习系统的运动。它旨在通过调整他们的激励和打破孤岛来统一数据工程师、ML 工程师、软件工程师和数据科学家。DevOps 在软件工程方面取得了巨大的成功。它有助于解决不同团队(开发人员、运营人员、业务人员和安全人员)之间的长期冲突。它还表明,敏捷性和健壮性并不是敌人,可以在不牺牲一个的情况下实现两者。通过 MLOps,我们试图在 ML 工程领域复制 DevOps 的核心理念。
提高 ML 系统稳健性的实践
该框架强调了提高 ML 系统稳健性的三大实践:
- 不断尝试 ML 管道的新实现
- 监控生产中模型和数据的质量
- 经常重新培训生产模型
我们通过向我们的机器学习系统引入持续集成、持续交付和持续培训来实施这些实践。
连续累计
持续集成(CI)是一种 DevOps 实践,它提倡每个团队成员的工作成果必须频繁地合并到一个单一的真实来源中,这个来源被称为版本控制系统(VCS)中的主干。后者通常是 Git。经常意味着每天。主干必须随时准备好部署到开发、试运行或生产环境中。这意味着我们必须保证主干是稳定的,同时鼓励频繁的变化。听起来很矛盾。这个冲突的解决方案是测试自动化。持续集成就是实现各种各样的自动化测试,并将它们集成到您的管道中,减少破坏主干的机会。
ML 项目的持续集成意味着数据科学家和 ML 工程师使用 VCS 来共享和协作他们的功能。他们不断地将 Jupyter 笔记本的关键功能重构和卸载到适当的编程语言源文件(Python 包)中。他们对他们的训练和预测管道和数据进行测试。它们持续与主干同步。
由于一些团队成员缺乏工程技能,并且数据科学代码库的一大块具有一次性使用的实验性质,CI 在 ML 项目中经常是棘手的。
连续交货

连续 ML 模型交付。图片由作者提供。
持续交付是 CI 之后的下一个逻辑步骤。CI 确保代码库在一个版本中拥有所有最新的变更,并能正常运行,而持续交付确保它是可部署的。可部署意味着无需人工操作即可快速部署。理想情况下,它应该只是一个按钮点击。连续交付通过自动化部署工作流中的所有步骤(构建包和映像、运行集成和验收测试、安全测试以及部署到不同的环境)再次使这成为可能。
与传统的软件开发相比,ML 项目的连续交付也增加了另一层复杂性。我们需要交付训练管道、预测管道和训练好的模型。
CI/CD 允许我们以更低的成本更快地试验和部署新的 ML 模型。这意味着我们可以在有限的时间内更快地迭代并产生更好的结果。
持续培训
连续训练(CT)是 ML 系统独有的新特性。它涉及自动再训练和服务模型。在真实的生产场景中,ML 模型可能会经常过时。需要模型再培训程序的情况示例:
- 模型性能下降(我们能够在生产中跟踪它)
- 非分布样本累积
- 数据/概念漂移
- 新的训练数据可用,这可以改进我们当前的模型性能。
在这种情况下,DS 和 ML 工程团队需要在更新的训练数据集上训练模型,验证其性能,并部署新版本。如果这些活动成本很高或者锁定在特定的团队成员身上,这可能会有问题。持续的培训使这一过程自动化。
持续训练是机器学习系统检测再训练需求、再训练模型、验证模型、有时在很少或没有人工干预的情况下自动部署模型的能力。

持续训练。图片由作者提供。
这使得机器学习系统更具适应性,因此也更健壮。
CT 通常至少需要实现以下组件:
- 自动化数据收集:新生产数据的 ETL,以更新培训和验证数据集。
- 自动化训练管道:端到端的模型训练过程,获取训练数据并产生训练的模型参数。
- 自动化模型验证:这允许 ML 系统自动确定模型是否是可部署的。
- 模型存储:存储经过训练的模型参数和元数据,以跟踪它们的沿袭和性能。允许唯一的模型识别和查询。
- 再训练触发:启动再训练程序的触发信号。它可以是数据漂移警报、cron 作业或新数据可用性。
Google Cloud MLOps 成熟度框架提倡在 ML 系统中实现这三种能力:持续集成、持续交付、持续培训。他们的自动化水平反映了一个 ML 系统的成熟度。
该框架区分了三个成熟度级别:

Google Cloud MLOps 成熟度级别。图片由作者提供。
- 0 级:在构建和部署 ML 模型时没有自动化。这是基本级别,构建和部署 ML 模型完全是手动的。
- 第一级:持续训练。这是我们实施自动化持续培训的下一个成熟度级别。这增加了我们 ML 系统的健壮性和适应性,并允许我们根据新数据频繁地重新训练生产模型。
- 第二级:持续集成、持续交付和持续培训。最高级的成熟度。为了实现这一目标,我们通过 CI/CD 自动交付 ML 管道。这允许我们更快地试验新的模型实现。数据科学和软件工程团队是一致的。ML 系统具有高度的适应性和鲁棒性。
这些级别为改进您的 ML 系统提供了方向和高级里程碑。但是要使用这个模型,您需要将它视为一个通用模板。它需要根据您的组织和 ML 系统进行填充和调整。你需要发展出一套系统的练习方法。一些 DevOps 工具可以帮助你做到这一点。其中包括持续改进、丰田 Kata 和价值流图。
让我们看看下一个成熟度模型,它与 Google Cloud MLOps 成熟度级别非常相似。
Azure MLOps 成熟度模型
该框架在随后的文档的中定义。它还侧重于采用 MLOps 原则和实践来指导团队开发生产级机器学习系统。
它定性地评估人员、流程和技术,并强调您的 ML 系统的两个方面:
- 软件工程师、数据科学家、数据工程师和机器学习工程师之间的协作。
- 整个机器学习生命周期的自动化。
它区分了五个成熟度级别:

Azure MLOps 成熟度级别。图片由作者提供。
- 0 级:无 MLOps。数据科学、ML/数据工程和软件工程团队各自为政。工作流涉及大量的移交,几乎没有自动化。
- 1 级:DevOps,无 MLOps。应用的软件组件通过自动化 CI/CD 管道交付。机器学习模型和管道仍然通过移交手动交付。团队之间的协作水平较低。
- 第二级:自动化训练。数据科学家与 ML 工程师合作,将实验脚本转化为可重复使用和可复制的培训程序。实验结果正在被追踪。
- 第三级:自动化模型部署。这是通过 CI/CD 管道自动部署 ML 模型的级别。数据科学、ML 工程和软件工程团队保持一致。
- 第 4 级:全 MLOps 自动化再培训。持续培训的水平。
到目前为止,我们讨论的两个模型都有一个缺点。它们是通用的,不能直接操作。它们描述了 ML 系统的一般理想状态和模式,但并没有为你提供实现它们的具体可行的方法。最重要的是,它们是不可量化的。
让我们看看本文中的最后一个模型。
ML 测试分数
谷歌团队在他们的论文“ML 测试分数:ML 生产准备和技术债务减少的指标”(E.Breck 等人,2017) 中介绍了这一框架。它的目标是指导团队如何在 ML 系统中识别、优先化和对抗技术债务。这些系统可以是任何规模和成熟度的:从学生论文研究项目到高可用性、可伸缩和低延迟的生产系统。
对抗软件系统(尤其是 ML 系统)技术债务的主要武器是测试和监控。ML 系统需要更广泛地使用这两种实践,因为它们固有的更高的复杂性。下面这张纸上的图片显示了这一点。

传统软件系统与 ML 系统的测试。图像来源:E.Breck 等人的 ML 测试分数:ML 生产准备和技术债务减少的一个标尺。
但是缺乏系统的方法导致了两个问题。测试很少具有足够的优先级来实现,因为总是有“更重要的事情要做”当他们这样做时,不清楚测试什么才能获得最大的好处。
因此,为了克服这两个问题,该框架的作者做出了两个主要的设计决策。要发挥作用,框架必须:
- 可操作的(Actionable):它必须提供一套具体的方法来规划团队活动的待办事项。该框架由具体的、可操作的测试组成,这些测试可以转化为团队待办事项中的标签或史诗。
- 可量化:它必须提供一个单一的评分标准,以允许团队在彼此之间以及在其他任务上区分这些活动的优先级。该框架提供了计算这种度量的算法。
ML 系统断言
ML 测试分数由 28 个可操作的测试组成,分为 4 类:
- 功能和数据测试
- 模型开发测试
- ML 基础设施测试
- ML 的监控测试

ML 系统断言。图片由作者提供。
每个测试充当一个要检查的断言。它的违反表明 ML 系统中可能存在故障。健壮的 ML 系统必须经常检查这些断言。我鼓励你阅读原始论文,以获得每个测试的详细描述。
ML 系统评分系统
除了测试之外,该框架还提供了一个带有单一标量指标的评分系统。最终测试分数计算如下:
- 对于上面的每个测试,手动执行
测试,并记录和分发结果,可获得半分。 - 如果有一个系统
在重复的基础上自动运行该测试,则得满分。 - 分别合计 4 个部分的得分。
- 最后的 ML 测试分数是通过取 4 个部分中每个部分的分数总和的
最小值来计算的。
基于测试分数的值,ML 系统有 6 个成熟度级别:

ML 测试分数。E.Breck 等。图片来源:ML 测试分数:ML 生产准备就绪和技术债务减少的指标。
您可以根据这个标准评估您当前的成熟度级别。然后,您可以为您的 MLOPs / ML 工程团队设定下一个里程碑阶段的目标 KPI。该框架允许您确定差距,并计划必要的活动以达到计划的 KPI。
假设你已经根据这个标准确定了你当前的 ML 成熟度等级低于 1。您设定的目标是在本季度末将其提高到 2。您已经发现,监控 ML 测试是导致 ML 测试分数表现不佳的最薄弱环节。因此,您有一个可能要实现的监控测试的待办事项,您可以根据项目的具体细节对其进行优先级排序。
你不仅有一个具体的算法来提高你的 ML 成熟度,而且你有一个具体的 KPI 来在设置优先级时捍卫这些活动。你向管理层传达了一个明确的信息,询问你的贡献和成果。
结论
上述框架可以帮助您的 ML 工程团队建立一个系统的持续改进实践,以提高您的 ML 系统的成熟度。
您可以使用它们作为指南:
- 确定 ML 成熟度的当前差距。
- 估计新活动的工作范围,以弥补这些差距。
- 为他们建立现实的成功标准。
- 确定作为此类活动的结果,您将交付的可交付成果。
跟我来
如果你觉得我与你分享的想法很有趣,请不要犹豫,在媒体、推特、 Instagram 或 LinkedIn 上联系我们。
张量流概率的最大似然估计
概率深度学习
介绍
本文属于“概率深度学习”系列。这个每周系列涵盖了深度学习的概率方法。主要目标是扩展深度学习模型,以量化不确定性,即知道他们不知道的东西。
我们使用张量流和张量流概率开发我们的模型。TensorFlow Probability 是一个构建在 TensorFlow 之上的 Python 库。我们将从能在张量流概率中找到的基本对象开始,并理解我们如何操纵它们。我们将在接下来的几周内逐步增加复杂性,并将我们的概率模型与现代硬件(如 GPU)上的深度学习相结合。
迄今发表的文章:
- 张量流概率简介:分布对象
- 张量流概率简介:可训练参数
- 张量流概率中从零开始的最大似然估计
- tensor flow 中从头开始的概率线性回归
- 使用 Tensorflow 进行概率回归与确定性回归
- Frequentist 与 Tensorflow 的贝叶斯统计

图 1:我们今天的口头禅:最大化对数似然和最小化负对数似然是一样的(来源
像往常一样,代码可以在我的 GitHub 上找到。
最大似然估计
让我们回忆一下上一篇文章最后分享的关于最大似然估计的内容。
最大似然估计是深度学习模型中常用的训练程序。目标是在给定一些数据的情况下,估计概率分布的参数。简而言之,我们希望最大化我们在一些假设的统计模型下观察到的数据的概率,即概率分布。
我们还引入了一些符号。连续随机变量的概率密度函数粗略地表示样本取特定值的概率。我们将这个函数表示为 𝑃 ( 𝑥 | 𝜃 ),其中 𝑥 是样本值, 𝜃 是描述概率分布的参数:

tfd.Normal(0, 1).prob(0)
<tf.Tensor: shape=(), dtype=float32, numpy=0.3989423>
当从同一分布(我们通常假设)中独立地抽取多个样本时,样本值 𝑥 1,…, 𝑥𝑛 的概率密度函数是每个个体 𝑥𝑖 的概率密度函数的乘积。正式书写:

我们可以通过一个例子很容易地计算出上述内容。假设我们有一个标准的高斯分布和一些样本:𝑥1 = 0.5, 𝑥 2=0, 𝑥 3=1.5。正如我们上面定义的,我们只需要计算每个样本的概率密度函数,然后将输出相乘。
X = [-0.5, 0, 1.5]
np.prod(tfd.Normal(0, 1).prob(X))
0.01819123
现在,我想以某种方式给出一些关于概率密度函数和可能性函数之间差异的直觉。他们本质上是在计算相似的东西,但是观点相反。
从概率密度函数开始,我们知道它们是我们样本的函数 𝑥 1,…, 𝑥𝑛 。注意参数 𝜃 被认为是固定的。所以,当参数 𝜃 已知时,使用概率密度函数,我们的兴趣是找出相同样本 𝑥 1,…, 𝑥𝑛 的概率。简而言之,当我们知道产生某个过程的分布,并且我们想从中推断出可能的值时,我们就使用这个函数。
相反,在似然函数的情况下,我们已知的是样本,即观察数据 𝑥 1、…、 𝑥𝑛 。这意味着我们的独立变量现在是 𝜃 ,因为我们不知道哪个分布产生了我们观察到的这个过程。因此,当我们知道某个过程的样本时,即我们收集了数据,但我们并不真正知道最初是什么分布产生了该过程时,我们就使用这个函数。既然我们知道这些数据,我们有兴趣对它们来自的分布做出推论。
让我们引入更多的符号来帮助连接这些点。对于可能性函数,惯例是使用字母 𝐿 ,而对于概率密度函数,我们引入了上面的符号。我们可以这样写:

我们准备用参数 𝜇 和 𝜎 定义高斯分布的似然函数:

作为一个获得更多关于可能性函数的直觉的练习,我们可以生成足够的样本来直观地一瞥它的形状。请注意与我们在上一篇文章中计算的概率密度函数相比,计算可能性函数的不同之处。我们对从概率分布中生成样本不感兴趣,我们感兴趣的是生成使观测数据的概率最大化的参数 𝜃 ,即 𝑃 ( 𝑥 1,…, 𝑥𝑛 | 𝜃 )。
我们使用与上述相同的样本:𝑥1 = 0.5,𝑥2 = 0,𝑥3 = 1.5。
X
[-0.5, 0, 1.5]
为了能够构建 2D 可视化,我们可以创建在一个间隔上均匀间隔采样的潜在参数的网格,从[-2,2]和[0,3]之间的𝜎采样𝜇。由于我们对每个参数的 100 个值进行了采样,我们得到了 𝑛 ^2 可能的组合。对于每个参数组合,我们需要计算每个样本的概率,并将它们相乘(按照我们上面分享的过程)。
μ = np.linspace(-2, 2, 100)
σ = np.linspace(0, 3, 100)
l_x = []
for mu in μ:
for sigma in σ:
l_x.append(np.prod(tfd.Normal(mu, sigma).prob(X)))
l_x = np.asarray(l_x).reshape((100, 100)).T
我们现在准备绘制可能性函数。注意,它是观察样本的函数,回想一下,这些是固定的,参数是我们的自变量。
plt.contourf(μ, σ, l_x)
plt.xlabel('μ')
plt.ylabel('σ')
plt.colorbar()
plt.title('Likelihood');

图 2: 样本的高斯似然函数𝑥1 = 0.5, 𝑥 2=0, 𝑥 3=1.5
正如我们已经分享的,我们对最大化我们数据的概率感兴趣。这意味着我们想要找到似然函数的最大值,这可以借助微积分来实现。事实上,函数相对于参数的一阶导数的零点应该足以帮助我们找到原函数的最大值。
我们现在遇到一个新问题,这个问题我们在之前的文章中已经介绍过了——将许多小概率相乘在一起可能在数值上不稳定。为了克服这个问题,我们可以使用相同函数的对数变换。自然对数是一个单调递增的函数,这意味着如果 x 轴上的值增加,y 轴上的值也会增加。这很重要,因为它确保了概率对数的最大值出现在与原始概率函数相同的点上。它为我们做了另一件非常方便的事情,它将我们的乘积转化为总和。
让我们执行转换:

我们就快到了,现在我们可以着手解决优化问题了。最大化我们数据的概率可以写成:

如上所述,可以对上面导出的表达式进行求导以找到最大值。扩展我们的参数,我们有 log(𝐿(𝑋|𝜇, 𝜎 )。由于它是两个变量 𝜇 和𝜎的函数,我们使用偏导数来找到最大似然估计。
让我们专注于 𝜇 ̂(帽子表示它是一个估计量,即我们的输出),我们可以使用以下公式计算它:

为了找到最大值,我们需要找到临界值,因此我们需要将上面的表达式设置为零。

然后,

这是数据的平均值,你应该不会感到惊讶。
我们可以计算出样本的μ和σ的最大值:𝑥1 = 0.5,𝑥2 = 0,𝑥3 = 1.5,并与真实值进行比较。
idx_μ_max = np.argmax(l_x, axis=1)[-1]
print(f'μ True Value: {np.array(X).mean()}')
print(f'μ Calculated Value: {μ[idx_μ_max]}')
print(f'σ True Value: {np.array(X).std()}')
print(f'σ Calculated Value: {σ[np.nanargmax(l_x[:,idx_μ_max], axis=0)]}')
μ True Value: 0.3333333333333333
μ Calculated Value: 0.3434343434343434
σ True Value: 0.8498365855987975
σ Calculated Value: 0.8484848484848485
张量流概率的最大似然估计实现
让我们首先创建一个正态分布的随机变量,并从中抽取样本。通过绘制随机变量的直方图,我们可以看到分布的形状。
x_train = np.random.normal(loc=1, scale=5, size=1000).astype('float32')[:, np.newaxis]
plt.hist(x_train, bins=50);

图 3:参数 𝜇 =1、 𝜎 =5 的正态分布随机变量直方图。
我们可以计算随机变量的平均值,这是我们想要使用最大似然估计学习的值。
x_train.mean()
0.85486585
正如我们在上一篇文章中看到的,我们可以将 TensorFlow Variable对象定义为分布的参数。这向 TensorFlow 发出信号,表明我们希望在学习过程中学习这些参数,无论我们使用哪个参数。
normal = tfd.Normal(loc=tf.Variable(0., name='loc'), scale=5)
normal.trainable_variables
(<tf.Variable 'loc:0' shape=() dtype=float32, numpy=0.0>,)
下一步是定义我们的损失函数。在这种情况下,我们已经看到了我们想要实现的目标——最大化我们的可能性函数的对数变换。然而,在深度学习中,我们通常会最小化我们的损失函数,如果我们将我们的可能性函数的符号改为负值,这可以很容易地实现。
def nll(x_train):
return -tf.reduce_sum(normal.log_prob(x_train))
最后,我们可以建立我们的培训程序。我们将使用一个定制的训练循环,以便我们可以自己定义过程细节(即使用我们的定制损失函数)。
正如我们在之前的文章中所分享的,我们使用 APItf.GradientTape()来访问 TensorFlow 的自动微分特性。接下来,我们简单地指定要训练的变量,最小化和应用梯度的损失函数。
@tf.function
def get_loss_and_grads(x_train):
with tf.GradientTape() as tape:
tape.watch(normal.trainable_variables)
loss = nll(x_train)
grads = tape.gradient(loss, normal.trainable_variables)
return loss, grads
optimizer = tf.keras.optimizers.SGD(learning_rate=0.001)
我们已经准备好运行我们的训练程序了。
@tf.function
def get_loss_and_grads(x_train):
with tf.GradientTape() as tape:
tape.watch(normal.trainable_variables)
loss = nll(x_train)
grads = tape.gradient(loss, normal.trainable_variables)
return loss, grads
optimizer = tf.keras.optimizers.SGD(learning_rate=0.001)
Step 000: Loss: 13768.004 Loc: 0.855
Step 001: Loss: 13768.004 Loc: 0.855
Step 002: Loss: 13768.004 Loc: 0.855
...
Step 1997: Loss: 13768.004 Loc: 0.855
Step 1998: Loss: 13768.004 Loc: 0.855
Step 1999: Loss: 13768.004 Loc: 0.855
我们应该为此欢呼。我们已经通过最大化我们首先生成的采样数据的概率,计算了参数 𝜇 的最大似然估计。正如我们在下面看到的,这很有效,我们能够得到一个非常接近原始值的𝜇值。
print(f'True Value: {x_train.mean()}')
print(f'Estimated Value: {normal.trainable_variables[0].numpy()}')
True Value: 0.8548658490180969
Estimated Value: 0.8548658490180969
结论
本文从理论和实践两方面介绍了使用张量流概率的最大似然估计 T2 过程。我们从陈述概率密度函数和可能性函数之间的差异开始。第一种方法是固定参数 𝜃 ,让样本成为自变量。相反,在可能性函数的情况下,数据是固定的(即观察到的),参数 𝜃 是我们想要学习的变量。然后,我们通过一个简单的例子直观地了解了可能性函数的形状。最后,我们通过定义张量流Variable、负对数似然函数和应用梯度,使用张量流概率实现了一个定制的训练程序。
下周,我们将开始构建我们的第一个算法。到时候见!
保持联系: LinkedIn
参考资料和材料
[1] — Coursera:深度学习专业化
[2] — Coursera:深度学习的 tensor flow 2专业化
[3] — 张量流概率指南和教程
[4] — TensorFlow 博客中的 TensorFlow 概率帖子
使用 SQL 的最大子数组和
原文:https://towardsdatascience.com/maximum-subarray-sum-using-sql-e1befa75d055
如何用声明式查询语言实现解决方案
最大和子数组和问题要求你找出整数数组中元素和最大的那个子数组。这是一个流行的计算机科学编程问题,通常用命令式编程语言(如 C++、Java、Python 等)来解决。然而,看看如何在声明式查询语言(如 SQL)中实现它是很有趣的。

卡斯帕·卡米尔·鲁宾在 Unsplash 上的照片
我希望这是尝试使用声明性 SQL 语法解决传统编程问题的一系列文章中的第一篇。为什么?因为好玩!
问题陈述
给定一个整数数组,找出具有最大元素和的整数连续子数组(至少有一个来自原始数组的元素)。
这个问题的讨论可以在很多地方找到,包括:
上面所有的链接都讨论了传统编程语言的 O(n)时间实现。然而,我们着眼于完全用 SQL 构建一个 O(n)时间的解决方案(没有任何过程扩展)。
输入表模式
CREATE TABLE array_data(index SERIAL PRIMARY KEY, value INTEGER NOT NULL);
INSERT INTO array_data(value)
VALUES(2), (4), (-3), (0), (-7), (4), (1), (-2), (7), (3), (1), (-6), (3);
SELECT * FROM array_data;

表 array_data 的内容(图片由作者提供)
第一种解决方案:O(n)
显而易见的第一种解决方案是生成子数组可以拥有的每一对有效索引(这是 O(n ),因为每个索引都与其后的一个索引成对出现),然后对于每一对索引,计算该范围内的值的总和。
计算总和需要输入(每对索引)的线性时间,因此该解决方案的总成本为 O(n)
WITH all_pair_sums AS (
SELECT
lhs.index AS begin_idx,
rhs.index AS end_idx,
SUM(for_values.value) AS array_sum
FROM
array_data lhs INNER JOIN array_data rhs
ON lhs.index <= rhs.index
INNER JOIN array_data for_values
ON for_values.index BETWEEN lhs.index AND rhs.index
GROUP BY 1, 2
ORDER BY lhs.index ASC, rhs.index ASC
)
SELECT * FROM all_pair_sums
WHERE array_sum = (SELECT MAX(array_sum) FROM all_pair_sums);

SQL 中 O(n)解的结果(图片由作者提供)
估计成本:对于一个 13 行的输入表,这个查询的估计成本(根据解释)是 108M 。
第二种解决方案:O(n)
我们可以执行的第一个优化是避免从给定的数组索引(begin_idx)开始重复计算每个数组的。相反,我们可以为从给定索引开始的每个子阵列维护一个运行总和。
这需要使用窗口函数,并且将成本降低到 O(n),因为我们正在处理它之前的每个数组索引的每个索引。
WITH all_pair_sums AS (
SELECT
lhs.index AS begin_idx,
rhs.index AS end_idx,
SUM(rhs.value) OVER (PARTITION BY lhs.index ORDER BY rhs.index ASC) AS array_sum
FROM
array_data lhs INNER JOIN array_data rhs
ON lhs.index <= rhs.index
ORDER BY lhs.index ASC, rhs.index ASC
),
with_max AS (
SELECT
begin_idx,
end_idx,
array_sum,
MAX(array_sum) OVER() AS max_subarray_sum
FROM all_pair_sums
)
SELECT begin_idx, end_idx, array_sum
FROM with_max
WHERE array_sum = max_subarray_sum;
这里, array_sum 列存储从索引 begin_idx 开始到 end_idx 结束的数组的运行总和。

SQL 中 O(n)解的结果(图片由作者提供)
估计成本:这个查询在一个 13 行的输入表上的估计成本(根据解释)是 371k 。与之前的方法相比,减少了 99.65%。
第三种解决方案:O(n)
观察:要找到从索引 I 开始到索引 j 结束的子数组的和,我们可以找到子数组[0..j]并从中减去子阵列[0..i-1]。换言之, sum[i..j] = sum[0..j] — sum[0..i-1] 。

索引为[2]的子阵列元素之和..9]通过执行 sum([0..9]) — sum([0..1])(图片由作者提供)
上面的列 array_sum 存储子数组[0]的元素总和的值..【begin _ idx】。
这个解决方案的最终代码如下所示。
WITH running_sum AS (
SELECT
index AS begin_idx,
value,
SUM(value) OVER (ORDER BY index ASC) AS array_sum
FROM
(SELECT 0 AS index, 0 AS value
UNION ALL
SELECT * FROM array_data
) AS temp
ORDER BY index ASC
),
running_sum_with_min AS (
SELECT
begin_idx,
value,
array_sum,
MIN(array_sum) OVER(ORDER BY begin_idx ASC) AS min_sum_so_far
FROM running_sum
),
sum_of_subarray AS (
SELECT
begin_idx,
value,
array_sum,
min_sum_so_far,
array_sum - LAG(min_sum_so_far, 1) OVER(ORDER BY begin_idx ASC) AS subarray_sum
FROM running_sum_with_min
),
max_sum_of_subarray AS (
SELECT
begin_idx,
value,
array_sum,
min_sum_so_far,
subarray_sum,
MAX(subarray_sum) OVER() AS max_subarray_sum
FROM sum_of_subarray
)
SELECT *
FROM max_sum_of_subarray
WHERE subarray_sum = max_subarray_sum;
如果打印中间表 max_sum_of_subarray ,就是这个样子。以红色突出显示的行是保存具有最大总和的连续子数组的值的行。

max_sum_of_subarray 表(图片由作者提供)
这种解决方案的一个缺点是,虽然我们有结束索引,但我们没有开始索引。通过在中间表上运行另一个查询来搜索值-4 的第一次出现,可以很容易地获取这个值。
这个子数组是索引[6..11].这个子数组中元素之和的值是 10 — (-4) =14。
请注意,我们插入了一个带有(index,value) = (0,0)的虚拟行,以确保第一个有效行(即 index = 1 的行)的延迟操作不会最终生成空值,从而导致不正确的结果。
估计成本:对于一个 13 行的输入表,这个查询的估计成本(根据解释)是 695 。与第一种方法相比减少了 99.99%,与第二种方法相比减少了 99.81%。
尽管在这个解决方案的 SQL 中的多个 cte 中有一个 ORDER BY 子句,为什么这个解决方案被称为 O(n)?
原始输入表的索引列是主键列(因此是唯一的)。PostgreSQL 的默认索引类型是 B 树,允许进行排序的全表扫描,这是我们在本解决方案的所有 cte 中基本上正在做的事情。因此,我们希望优化器会注意到这一点(加上中间表也是经过排序的),并完全避免排序操作。即使无法推导出这一点,运行时间回归到 O(n log n) ,但不会比这更差。
SQL 小提琴
这篇文章中所有解决方案的 SQL 链接可以在这里找到。
结论
我们看到了用 SQL 解决同一问题的多种方法,效率各不相同。尽管最后一个解决方案拥有最多的代码,但它在运行时间和 CPU 利用率方面是最高效的。将解决方案分解成更小的 cte 有助于代码的可读性和可维护性。
如果你想看到更多的使用声明性 SQL 语法解决的常规编程问题,请添加评论,如果可能的话,我会尽力去做,或者写下为什么很难做到。
五月版:设计可用的仪表板
原文:https://towardsdatascience.com/may-edition-designing-usable-dashboards-85bd73b75f97
如何构建能激发良好决策的工具

照片由 Unsplash 上的Darwin veger拍摄
仪表板解决了——最好是防止——好数据的浪费。对于希望分享其辛勤工作成果的数据科学家,以及需要做出基于数据的业务和产品决策的其他利益相关者来说,它们已经成为一种至关重要的媒介。然而,在成功协作的道路上仍然存在许多障碍:如何决定共享哪些信息,以及如何组织这些信息?你如何阻止你的光滑仪表板从你的非 DS 同事的雷达上消失?
我们选择了几个最近的帖子,耐心地解释了开发人们实际使用的仪表板的理论和实践,并解决了数据专业人员在创建仪表板时遇到的一些常见挑战。对于那些想探索其他话题的人,请继续阅读,发现我们上个月最受欢迎的帖子,以及一些我们非常自豪的原创功能。
祝阅读愉快,感谢您对我们作者工作的支持。
TDS 编辑亮点
- 如何构建有效(有用)的仪表板Marie lefe vre介绍了一种简化的四步法来构建仪表板,并以她自己的实际经验为基础。(2022 年 3 月 7 分钟)
- (2022 年 3 月 7 分钟)
- 使用 Python 推进到专业仪表板,使用 Dash如果你已经是一个经验丰富的仪表板创建者,考虑通过遵循 Kay Jan Wong 的简明、循序渐进的教程来进一步提高你的技能。(2022 年 1 月,6 分钟)
- 用 Python、Dash 和 Plotly 创建一个更好的仪表板对于第一次使用仪表板的人来说,布拉德·巴特莱姆的指南特别全面——它将从头开始引导你完成整个过程。(2021 年 12 月 16 分钟)
原始特征
从作者问答到播客片段,我们的团队每周都会发布新文章,介绍我们蓬勃发展的社区的最新消息和想法。以下是最近的几个亮点,以防你错过:
- 在机器学习中,失败和不确定性有时是成功的必要成分 ,阿尼·马杜尔卡专访。
- 你写得越多,你就越擅长解释你的作品 :与Varshita Sher博士关于她的职业道路和写作策略的对话。
- 与人工智能 生成维基百科文章,这是 TDS 播客上的一段精彩对话,由主持人 Jeremie Harris 和人工智能研究员 Angela Fan 主讲。
- 发现数学背后的美 ,作者聚焦系列的最新作品,由汉娜·鲁斯主演。
热门帖子
人们永远不应该低估群体的智慧——尤其是当讨论的群体是我们敏锐而忠实的 TDS 读者你时。以下是过去一个月阅读量最大的一些帖子。
- 如何构建可读性和透明度的数据科学项目 ,作者 Khuyen Tran
- 你应该用这个来可视化 SQL 连接,而不是文氏图 ,作者安德烈亚斯·马丁森
- 2022 年你应该读的 5 本最好的数据科学书籍 ,作者 Terence Shin
- 如何自学数据科学所需的所有技术知识 ,作者弗兰克·安德拉德
- 成为“真正的”数据分析师 ,作者凯西·科济尔科夫
- 我是一名自学成才的数据科学家。以下是我对新人 的三点建议,作者Soner y ldr um
在我们结束之前,请和我们一起向我们在四月份欢迎的一些新作者表示热烈的欢迎,我们很高兴与你们分享他们的作品。(如果你想加入他们的行列,我们很乐意收到你的来信。)他们包括 Adrienne Kline 、 Riccardo Andreoni 、 Aine Fairbrother-Browne 、William foot、Mario Nam Tao shian ti Larcher、 Boriharn K 、 John Willcox 、Ali soleimani、 Veronica Villa 、 Carlo H 【T21AI 、 Aydin Schwartz 、 Sankar Srinivasan 、 Kishan Manani 、 Hanzala Qureshi 、 Tara Prole 、 Ariel Jiang 、bi GL Subhash、 Sascha Kirch 、 Dan Pietrow 、【t5t Charlotte Tu 、Jack chi-Hsu Lin、 Pieter Steyn 、 Joe Sasson 、 Anushka Gupta 、 Louis Casanave 、 Robin Thibaut 、 Mario Hayashi 、Maya mura
用 Python 评估机器学习分类器的 McNemar 测试

艾萨克·史密斯在 Unsplash 上拍摄的照片
了解如何使用 Python 通过显著性测试比较 ML 分类器
介绍
在我的上一篇文章中,我谈到了使用统计工具正确比较不同模型的重要性,以便在选择阶段选择最佳模型。
在这篇文章中,我想特别关注一个统计测试,作为一名数据科学家或机器学习工程师,你需要知道这个测试。您可以使用此测试来确定两个分类器之间是否存在统计上的显著差异,以便您实际上可以只使用最好的一个。
麦克内马试验
当我们有匹配对时,需要比较两个分类器的性能时,可以使用 McNemar 测试。如果在两个分类器 A 和 B 之间有许多不同的预测,那么如果我们有许多数据,那么测试工作得很好。
通过该测试,我们能够比较两个分类器在具有单个测试集的 N 个项目上的性能,这与您过去在配对 t-test 中所做的相反。
假设
- 随机抽样调查
- 独立性ˌ自立性
- 相互排斥的群体
相依表
McNemar 的测试旨在主要关注两个分类器之间的差异,因此关注他们以不同方式预测的情况。所以我们要做的第一件事就是去计算下面的值。

列联表(图片由作者提供)
- n00:A 和 B 误分类的项目数
- n01 :被 A 误分类但未被 B 误分类的项目数
- n10:B 误分类但 A 未误分类的项目数
- n11:A 和 B 都正确分类的项目数
- 零张力 : n01 = n10
零假设:A 和 B 的错误率相同。
麦克纳玛检验基本上是成对卡方检验的一种形式,所以接下来我们需要使用以下公式计算 X 值。

卡方(图片由作者提供)
示例

列联表(图片由作者提供)
X = 7.55
假设我们希望显著性水平 p = 0.05。如果 X > X (0.05) (在双尾检验中),我们可以拒绝无效假设。
所以我们用一个自由度= 1 的卡方表。

X 表(来源:https://en . wikibooks . org/wiki/Engineering _ Tables/Chi-Squared _ distribution)
由于 7.55 > 3.84,我们可以拒绝零假设并声明分类器 A 和 B 之间存在显著差异!
让我们编码
最后的想法
假设检验为做出关于感兴趣人群的数据决策提供了一个可靠的框架。它有助于研究人员成功地将数据从样本外推至更大的人群。
比较一次在不同模型上得到的结果来选择哪一个是最好的,从来都不是一个好方法。统计测试允许我们客观地陈述一个模型是否表现得更好。每次你运行实验,统计测试是用来显示你所做的比以前存在的更好,去看看任何机器学习论文就知道了!
结束了
马赛洛·波利蒂
McNemar 的测试,使用 Python
原文:https://towardsdatascience.com/mcnemars-test-with-python-e1bab328d15c
执行麦克纳玛测试的完整初学者指南(带代码!)

塞尔吉奥·罗塔在 Unsplash 上拍摄的照片
在本文中,我将介绍麦克纳玛检验的基本原理,这是一种用于比较两个相依总体比例的统计方法。[1]
- 这是一种配对卡方检验(χ2),但这一次,两个群体都是依赖。
- 仅用于成对的名义数据。
1。前后对比数据,以揭示营销活动、药物测试等中认知、态度、行为的任何变化。
2。配对病例对照 :
一、每个病例都有配对对照,即年龄、性别、种族等匹配。
二。双胞胎研究,即配对是双胞胎。[2]
与χ2 检验类似,在计算麦克内马统计量之前,需要将数据排列在 2×2 列联表中。[1]

表 1:对同一个样本进行两次测试(之前和之后)的结果的 2x2 列联表。图片作者。
麦克纳玛检验中的零假设(H₀)是边际同质性,即每个结果的两个边际概率是相同的( R₁总数= C₁总数类似地 R₂总数= C₂总数),如果我们扩展方程→ A + B = A + C → B = C [1]
检验统计量具有一个自由度的近似χ2 分布:

麦克内马的测试统计。图片作者。
A.麦克内马前后数据检验
日本汽车制造商丰田和三菱都致力于增加他们在中东的市场份额。在总共 100 个客户中,60 个首选丰田,其余的在看到任何东西之前首选三菱。然而,在观看完丰田新发布的电视广告首映后,相同的受访者被要求再次考虑这两个品牌,并选择他们现在更喜欢的品牌,结果如下表所示。在 1%的显著性水平上,有证据表明顾客在广告后改变了主意吗?

表 2:调查摘要 2x2 列联表。图片作者。
根据假设检验的五步流程:
让π₁ =广告筛选前喜欢丰田的真实客户比例
π₂ =广告筛选后喜欢丰田的真实客户比例
h₀:π₁=π₂
h₁:π₁≠π₂
α= 0.01
根据麦克纳玛的检验统计:

结论:我们没有足够的证据表明广告筛选前后偏好丰田的客户的真实比例是不一样的,在 1%的显著水平上。
B.配对病例对照资料的麦克内马检验
进行了一项实验来比较两种哮喘治疗方法在消除急诊室就诊方面的有效性。200 对哮喘患者按疾病严重程度配对。每对中的一名患者被分配治疗 A,另一名被分配治疗 b。实验将记录患者是否至少因呼吸困难而去过一次急诊室。在 1%的显著性水平上,是否有证据表明这两种治疗方法不是同样有效?

表 3:实验总结 2x2 列联表[3]
根据假设检验的五个步骤:
让π₁ =治疗 a 的患者中去急诊室的真实比例
π₂ =治疗 b 的患者中去急诊室的真实比例
h₀:π₁=π₂
h₁:π₁≠π₂
α= 0.01
根据麦克纳玛的检验统计:

结论:我们有足够的证据表明,在 1%的显著性水平上,这两种治疗方法不是同等有效的
结论
麦克内马检验是卡方检验的一种。然而,与用于检验两个变量之间关系的卡方检验(独立性的χ2 检验)不同,麦克内马检验用于检查两个相关群体的感知、态度和行为是否有任何变化。每当相同的个体被测量两次(在调查之前和之后)、配对(双胞胎或已婚夫妇)、配对对照(即在一些变量上匹配(在上述例子中根据疾病的严重程度对哮喘患者配对))时,使用麦克内马检验。
推荐阅读
https://levelup.gitconnected.com/how-to-perform-one-sample-hypothesis-tests-with-python-308eae8789fc https://levelup.gitconnected.com/two-sample-hypothesis-tests-with-python-43e1b8c52306
参考
[1] 麦克内马检验—统计库文本
[2] 麦克纳玛的测试——维基百科
[3] 威斯康星大学麦迪逊分校 BMI541/99 讲座:生物统计学和医学信息学,2022
均值、中值和众数:它们是什么,什么时候应该使用它们?
统计 101
你可能还记得高中统计课上的均值、中值和众数,但它们经常被误用。我们看看应该如何使用它们——以 Python 和 Pandas 为例。

作者的各种图像
18 岁英国男性的平均身高是多少?还是马德里的房子均价?还是英格兰高中生达到的平均成绩?
这些都是很好的问题,但它们各自的含义略有不同。
第一种情况的平均值是用平均值计算出来的,房价用中位数和学校成绩用模式来表示会更好。
我们将尝试并解决何时使用哪种测量方法。
平均水平
平均值是集中趋势的度量,是一组数据的一种总结或概述。通常,我们希望通过将一组值相加,然后除以这些值的个数来计算它。但这只是“平均”一词的三种基本解释之一。
我刚刚描述的一个是平均值,这对于像身高这样的正态分布的数据非常适用。中值是中间值,意味着该值的两侧有相同数量的测量值。
正如 Alberto Cairo 告诉我们的,“你需要记住的是,均值对极值非常敏感,而中值则不敏感。中位数是一个有抵抗力的统计数字。”
也就是说,相对少量的异常值不会从根本上改变中值,而平均值会。在某些情况下,这可以使中位数成为一个更好的衡量标准,我们稍后在研究房价时会看到这一点。
第三个“平均值”是模式,它是集合中最常见的值,通常是处理分类数据时的唯一选项。
让我们来看一些如何使用它们的例子。
平均值
你可以合法地追踪 18 岁英国人的身高,把他们的身高加在一起,然后除以 18 岁英国人的数量。(这是一项艰巨的任务,所以你可能想取一个有代表性的样本——你还想把他们分成两组:男性和女性)
如果你追踪这个时间,你会得到如下图。

图片由作者提供—数据来源: 我们的世界在数据中 ,知识共享许可
这很好,因为人类的身高遵循正态分布。
在正态分布中,值均匀分布在中心点周围,类似于下图所示的左侧和右侧逐渐变小。

正态分布曲线-作者图片
如果我们观察一组人的身高,我们可以看到测量结果遵循着几乎相同的模式。
在下图中,我们使用了有影响力的统计学家弗朗西斯·高尔顿爵士(1822-1911)在他的著名实验中记录的身高数据,该实验阐释了回归均值的统计概念(他指出,较矮的人的成年后代往往比他们的父母高,而较高的父母的后代往往比他们的父母矮)。
我们使用这里的数据只是为了探索 500 多名 18 岁成年男性的身高范围。高尔顿的数据是在公共领域,可以在许多网站上找到。在这里,我从哈佛大学的 Dataverse 下载了它。
在下面的代码中,我们过滤了高尔顿的数据,创建了一个所有男性身高的密度图。
gal = pd.read_csv('galton-stata11.csv', delimiter='\t')
gal['height'][gal['male']==1].plot.density();

高尔顿的身高数据——资料来源:哈佛数据世界的公共领域数据
如你所见,图表非常接近正态分布,平均值约为 70 英寸。
Pandas 给了我们一种方便的方法,可以用来求一系列值的平均值
gal['height'][gal['male']==1].mean()
从中我们得到了结果:
69 . 430108
中间值
在正态分布的情况下,平均值和中值是相同的值,但对于其他类型的分布则不是这样。
拿房价问题来说。世界上大多数主要城市都有各种各样的房产和价格,但也有相对较少的非常昂贵的房产。这些昂贵的房产扭曲了平均值。
因此,与其衡量马德里一套公寓的平均价格,不如衡量一套普通公寓的价格更有意义。
听起来是一回事吗?让我们看一个简单的虚构例子。想象一下,一个公寓楼有不同大小的物业:它们大多是三居室公寓,但在大楼的顶部有一个豪华阁楼,占据了大楼的整个顶层。
该公寓楼位于马德里市中心的热门区域,距离 Retiro 公园不远。没有一套公寓会便宜。
假设有 4 层。每层楼都有两个三居室公寓,底层价格为 70 万€,下一层为 72 万€,上一层为 75 万€,顶层是顶层,我们说过是顶层公寓,有五个卧室,价值 150 万欧元。
如果我们对价格进行算术平均,我们将得到大约 834,285€的平均值。但没有人支付公寓的价格,除了一个人之外,所有人都低于这个数字,因为只有一个人不得不为他们的公寓支付 150 万美元。
因此,即使是平均价格,平均数也不是一个非常有用的数字。衡量平均值的更好方法是中值,即 720,000€的中间值,它更好地代表了大多数人支付的价格。
下面是说明该示例的一小段代码:
d = {'Price':[1500000, # Penthouse
750000,750000, # Floor 3
720000,720000, # Floor 2
700000,700000 # Floor 1
]}df = pd.DataFrame(d)
df.plot.bar(legend=False)print(f"Mean {df['Price'].mean()}")
print(f"Median {df['Price'].median()}")
它打印出以下内容:
意思是说 8360 . 488888868686
中位数 720000.0
观察柱状图可以清楚地看出,平均价格并不能很好地说明公寓价格,中位数是一个更好的衡量标准。

这是一个简单的例子,但它说明了任何大城市的房地产价格范围,在这些城市中,普通房屋或公寓的价格远低于相对较少的非常昂贵的房地产。
因此,平均值是所有房地产价格的平均值,中位数是平均房地产价格,在这种类型的例子中,这是一个更有用的数字。
模式
该模式可用于数值数据,但通常用于查找分类数据的集中趋势。
让我们来看看我们的第三个问题,英语高中生的平均考试成绩。下面是 Ofqual⁴提供的 2021 年 18 岁学生“A level”考试成绩表(a level 基本上是英国学生进入大学的途径)。

作者图片——来源:Ofqual⁴
成绩分为 A、A、B、C、D 和 E,成绩按性别分列,并给出了每个成绩的百分比。所以你可以看到 18.4%的男生获得了 A,19.7%的女生获得了相同的成绩,总的来说,所有学生获得 A*的比例是 19.1%。
显然,我们无法通过计算平均值来找到平均值,因为数据不是数字,但我们可以清楚地看到哪个是最受欢迎的等级。这里有一些柱状图可以说明这一点:


由此我们可以看出,获得“B”级的男性比其他任何人都多,但对于女性来说,却是“A”级。
这是最受欢迎的等级,即模式,这是显示平均等级的一种清晰方式。
Pandas 也给了我们一个计算模式的有用方法。这是一组虚构的数据,或多或少遵循上述模式。它显示了 33 名学生及其成绩的列表:6 名学生获得了 A*,8 名获得了 A,9 名获得了 B,6 名获得了 C,3 名获得了 D,1 名获得了 E。
我们将其转换为数据帧,并按如下方式计算模式:
res = {'Grade':[ 'A*','A*','A*','A*','A*','A*',
'A','A','A','A','A','A','A','A',
'B','B','B','B','B','B','B','B','B',
'C','C','C','C','C','C',
'D','D','D',
'E']}r = pd.DataFrame(res)
r.mode()
我们从熊猫身上得到的结果是一个数据框架:

数据框中的第一行给出了值“B ”,这是该列表的模式,因为“B”等级比任何其他等级都多。
为什么是数据帧?因为可以有不止一种模式。让我们改变数据,使“A”级和“B”级的数量相同。
res = {'Grade':[ 'A*','A*','A*','A*','A*','A*',
'A','A','A','A','A','A','A','A','A',
'B','B','B','B','B','B','B','B','B',
'C','C','C','C','C','C',
'D','D','D',
'E']}r = pd.DataFrame(res)
r.mode()
现在我们有两种模式-数据是多模式的-这反映在生成的数据框中。

作者图片
有趣的是,如果我们从上面的房价数据中找出模式,我们会得到下面的结果。

作者图片
在这种情况下,因为三种价格下的公寓数量相同,所以我们有三种模式,这不是很能说明问题,因此强化了中位数是该数据的更好衡量标准的观点。
代码和数据
您可以在我的 Github 页面上找到一个 Jupyter 笔记本的链接,上面有代码(除此之外还有更多)和文章中使用的所有数据文件。
一如既往地感谢您的阅读,如果您想了解我发表的新文章,请考虑注册下面的电子邮件提醒或订阅我偶尔在 Substack 上发布的免费简讯。
https://technofile.substack.com https://medium.com/membership/@alan-jones
笔记
- 这句话出自大卫·施皮格尔哈尔特的优秀著作《统计学的艺术:如何从数据中学习》 ,大卫·施皮格尔哈尔特,2021
- 来自:真实的艺术:用于交流的数据、图表和地图,阿尔贝托·开罗,2016
- 高尔顿的数据可以从很多地方获得(尝试谷歌“高尔顿身高数据”),但这个版本是从哈佛大学的 Dataverse 下载的:Francis Galton,2017,“高尔顿身高数据”,https://doi.org/10.7910/DVN/T0HSJ1,哈佛 Dataverse,公共领域许可证 CC0 1.0
- A-level 数据是 qualhttps://analytics.ofqual.gov.uk/apps/Alevel/Outcomes/的发布的结果的子集,根据开放政府许可 v3.0 使用
(本文包含代销商链接,这意味着如果你购买了某样东西,我会得到一小笔佣金,但你不会再为该产品支付任何费用)
均值还是中值?基于决策而不是分配来选择
即使对于有偏差的数据,平均值有时也会导致更好的决策

雷米·吉林在 Unsplash 上拍摄的照片
当我面试数据科学申请者时,我最喜欢的问题之一是,“什么时候使用平均值而不是中位数更好?
这个问题不仅帮助我评估候选人的统计学基础,还让我一瞥他们是如何处理问题的。他们强调数据的本质还是决策?
大多数候选人会这样回答:
当数据遵循对称分布时,平均值通常更好。当数据有偏差时,中位数更有用,因为平均值会被异常值扭曲。”
这个答案抓住了我在统计课程中学到的传统智慧。它侧重于数据的性质,而不考虑分析的目标。
决策分析课程教会了我解决这个问题的不同方法。决策分析引导我们关注我们的目标,而不是关注数据的本质。
事实上,在许多应用中,即使数据有偏差,平均值也比中值更有用。
让我们从三个经典的例子开始,这些例子都涉及到有偏差的数据,在这些数据中,平均值或中值都出现了惊人的错误。
示例 1:货币彩票
假设你可以选择玩下面的一个机会游戏:
游戏甲:
- 1/3 的机会赢得 1 美元
- 1/3 的机会赢得 2 美元
- 1/3 的机会赢得 3 美元
游戏 B:
- 1/3 的机会赢得 1 美元
- 1/3 的机会赢得 1.90 美元
- 赢得 100 万美元的三分之一机会
你会选哪一个?
大多数人会选择玩游戏 B,即使它的中位数较低(1.90 美元对 2.00 美元)。在这种情况下,使用平均值更有意义,即使分布是偏斜的。
中位数的论点在这里没有意义:
“我应该把我的决定建立在一个不受事实影响的标准上,即我有 1/3 的机会通过 b 游戏成为百万富翁。”
100 万美元的 1/3 概率是一个极端值,但它是分布的一部分,与决策相关。
例 2:比尔·盖茨走进一家酒吧
一个常见的笑话是这样的:
"比尔·盖茨走进一家酒吧,里面的每个人都成了百万富翁."
这个笑话凸显了均值是如何具有误导性的。没有上下文,人们可能会认为酒吧里全是百万富翁,而实际上可能是零个百万富翁。在这种情况下,中位数给出了一个大多数人会觉得更直观的结果。
这个例子的一个重要特征是,它是关于数据通信而不是决策。这个例子是唯一一个不涉及明确决策的例子。
关于数据通信的观点无疑是正确的;研究人员在报告家庭收入时使用中位数是有充分理由的。中位数通常能更好地说明什么是“典型”值,这有助于提供关于数据集的直觉。
给出“典型”值的最直观感觉的度量不一定是导致最佳决策政策的度量。
示例 3:一家考虑扩张的公司
一家公司正在考虑扩张。扩展将涉及雇用 300 名新员工,因此公司需要估计该计划的总成本。
工资和收入一样,倾向于向右倾斜。我们可以预计,新工作的平均工资将高于中位数。
公司的一名分析师决定使用中位数,因为工资的分布是向右倾斜的。他们用中位数乘以 300 来估算雇佣新员工的总成本。
事实证明,实际成本比他们的估计要高得多。为什么?因为他们本该使用卑鄙的手段。
在这种情况下,公司感兴趣的是总数。平均数与总数有直接关系(总数=平均数 x N),但中位数没有。
一般来说,当分析的目的是估计总体时,无论分布的形状如何,平均值都比中值更有用。
两家竞争公司的模拟
评估统计方法有效性的最佳方法之一是对合成数据进行蒙特卡罗模拟。
因此,让我们模拟一家基于均值做出决策的公司与一家基于中位数做出决策的公司竞争。
我们可以模拟的一个相对简单的商业决策是 A/B 测试。在 A/B 测试中,一个公司面临着发布产品的两个不同版本(变体)的决定——A 和 B。
为简单起见,我们将这模拟为“赢家通吃”测试,这意味着公司选择测试中出现更好的变体,而不计算统计显著性。当没有控制组时,这种方法是合理的,并且商业问题的风险相对较低。
在向整个客户群发布变体 A 或 B 之前,公司用客户样本测试变体 A,用不同的客户样本测试变体 B。
基于变体 A 和 B 在测试中的表现,公司选择向整个客户群发布变体 A 或变体 B。
这里是设置的摘要。在每次模拟中:
- 我们将随机生成两个偏态分布。这些分布代表变量 A 和 b 的每用户收入的真实分布。
- 为了决定发布哪个变体,这些公司将分析一个数据集,该数据集由变体 A 的发行版中的 1,000 个和变体 B 的发行版中的 1,000 个组成。这些绘图组成了“测试数据集”
- 为了隔离决策方法的影响,两家公司分析相同的 A/B 测试数据集。
- 每个公司将根据测试数据集选择 A 或 B。
- 在选择了一种变体之后,每家公司都会向其整个客户群“大规模”推出其首选变体。我们将通过从每个公司选择的发行版中生成 100,000 个抽奖来模拟这一步骤。这些抽奖代表了从每家公司的 100,000 名客户那里获得的收入。
- 业务目标是最大化总收入,所以我们会在每个公司大规模发布其首选变体时,根据每个公司的总收入来评估每个公司的决策政策。
现在让我们进入 Python 实现。完整的实现可以在 GitHub 上获得。我鼓励读者尝试它,以理解不同的决策框架如何导致不同的业务结果。
首先,我们需要编写一个生成随机偏态分布的函数。我选择对数正态分布,因为它们是偏斜数据的常见模型。
回想一下,如果变量 x 具有对数正态分布, log(x) 具有正态分布。我发现根据基础正态分布的均值( mu )和标准差( sigma )来表征对数正态分布的参数是很直观的。
在这个函数中,我们从 0 到 0.5 之间的均匀分布中随机抽取 mu ,从 1 到 1.5 之间的均匀分布中抽取 sigma :
该函数生成如下所示的分布:

图片作者。模拟中使用的随机生成的偏斜分布的示例。
在每次模拟中,公司将得到两个这样的分布,他们需要从每个分布中随机抽取 1000 个样本,从中选择一个。
以下是测试数据集的一个示例:

图片作者。公司在给定模拟中分析的测试数据示例。
看着这些数据集,这两家公司就应该如何做出决策得出了不同的结论:
- 中值最大值注意到分布非常偏斜。事实上,看起来有些数据点可能是异常值。为了减少异常值的影响,他们选择测试数据集具有更高中位数的分布。
- 均值最大化者意识到潜在的目标是最大化总收入,所以他们选择测试数据集均值更高的分布。
我们可以用以下函数来表示两家公司的决策政策:
运行 5,000 次模拟后,两家公司的收入分布如下所示。

图片作者。5,000 次模拟中每家公司的收入分布。
虽然两家公司的总收入在模拟中有所不同,但很明显,平均最大者的收入分配明显更高。
我们也可以看一个柱状图来回答这个问题:平均最大值超过中值最大值的时间百分比是多少?

图片作者。两家公司的胜率。
在大约 60% 的模拟中,平均最大值优于中值最大值。
这个结果很有意思。凭直觉,这两家公司似乎应该经常得出相同的结论,因为具有较高平均值的分布通常应该具有较高的中值。
基于我们使用的对数正态参数,这些公司在大约 70%的模拟中选择了相同的变量。当这两家公司选择相同的变体时,有 50%的可能性其中一家会胜过另一家。
然而,当公司选择不同的变量时,在大约 86%的模拟中,平均最大值优于中值最大值。
这些数字取决于我们使用的确切模拟设置,但关键是这代表了一种相当常见的业务情况,其目标是最大化总价值。有了这个目标,基于中间值而非平均值做出决策会导致战略劣势。
什么时候我们应该使用中位数(或者,更一般地,分位数)?
需要说明的是,我并不是说中间值从来没有帮助。有时中间值与分析的目标一致。
在有些应用中,中位数(或分位数)比平均数更有优势。以下是一些例子:
- 数据讲故事。当手头没有明确的决策时,中位数通常更有助于报告有偏差的数据。它给出了分布中“典型”值的更直观的感觉。然而,对于为公司工作的数据科学家来说,手头几乎总是有一个决定。
- 有时候一个问题直接用中位数来回答。如果我们需要知道给定的工资是否高于部门工资的一半,我们需要中位数。
- 当我们关心确保一定水平的服务时。例如,我与消防部门合作时,他们关心的是以高概率提供一定的响应时间。因此,他们经常使用第 90 个百分位数的响应时间来评估自己的表现。如果他们使用平均响应时间,他们可以用非常短的响应时间来补偿长的响应时间,这与公共安全目标不一致。
- 当我们受到激励去最小化预测的绝对误差时。正如本文中所讨论的,分布的中值是使平均绝对误差最小的值。
日志转换呢?
处理倾斜数据的一种常见方法是使用数据转换,例如取对数。这些转换在许多应用中是有帮助的;它们可以减轻右倾,强制数据为正,并提高预测模型的性能。
然而,我们还需要小心,不要以无意中改变决策政策的方式使用它们。
您更喜欢以下两个选项中的哪一个:
- 选项 1:一张 10 美元钞票和一张 1 美元钞票
- 选项 2:两张 5 美元的钞票
大多数人会选择选项 1,因为$10 + $1 大于$5 + $5。如果在求和之前进行对数变换,您会更喜欢选项 2,因为$5 x $5 大于$10 x $1。日志转换改变了决策策略。
这个变化不必然不好。在某些情况下,将决策建立在乘积而非总和的基础上是有意义的。
当我们努力使统计变得更容易时,问题就出现了,这无意中把我们的利益相关者引入了一个次优的策略。
在上一节的模拟中,我考虑添加第三家公司,即 Log Transformers ,它将在取平均值之前取测试数据集的对数。然后他们会选择在对数尺度上具有更大平均值的变量。该策略相当于选择具有较高几何平均值的分布。
中值等于对数正态分布的几何平均值,因此对数转换器将具有与中值最大值几乎相同的策略。他们的策略也不如平均最大化者。
离群值呢?
当最优决策政策建议我们应该使用平均值而不是中位数时,我们需要小心异常值,因为它们会扭曲平均值。
但答案不是用一个导致次优决策的指标来代替均值。
相反,我们应该首先理解为什么数据集中有离群值。
它们是测量误差的结果吗?如果是这样,我们应该考虑将他们排除在分析之外。如果离群值是对现实的准确测量,我们需要使用合理的判断。
离群值可能是侥幸的结果。例如,在一个电子商务网站上,一小部分客户可能是订单异常大的企业。如果您正在运行一个 A/B 测试,并且一个测试组包括一个像这样的用户,它可能会以一种不希望的方式扭曲结果。
根据具体情况,其他方法,如 winsorized 手段可能会有所帮助。这里没有通用的解决方案。最好的建议是专注于分析的目标,发展领域知识以理解异常值的原因,然后从那里使用良好的判断。
在处理离群值时,首先要关注理解它们为什么存在。之后,问问自己,他们是否是分布中有意义的一部分。
我们应该使用汇总统计数据吗?
有些人认为汇总统计在某些情况下会产生误导,我们应该关注“整个分布”。
这个想法有一些优点。查看整个分布可以揭示标准汇总统计无法捕捉的洞察力。
例如,观察 A/B 测试中的分布可以揭示不同的影响——这意味着一种治疗对一些用户的影响可能不同于其他用户。
然而,我们的目标通常是将数据转化为决策,这需要以可操作的方式总结分布。
定量决策框架要求用单个数字概括一个分布,量化它与决策者偏好的一致程度。
没有这一点,我们就无法量化一种分布是否优于另一种分布。决策分析有助于解决这个问题;它的中心目标之一是用一个单一的数字——行动的效用——来捕捉偏好和分布。
所以,是的,我们需要汇总统计数据。我们应该解释是什么驱动了汇总统计数据,但重要的是我们知道如何以一种捕捉决策者偏好的方式来汇总分布。
同样重要的是要注意,还有许多其他的方法来总结分布,比如调和平均法、几何平均法和某些等价法。根据决策的性质,它们在某些情况下都是有用的。
结论
既然我们已经深入探讨了这个问题,我们可以对“什么时候使用平均值而不是中位数更好?”给出一个更深思熟虑的答案
我会这样回答这个问题:
使用平均值或中值的选择应该主要由分析的目标决定。当潜在的业务决策取决于总额(例如总收入或总销售额)时,平均值通常是更好的指标,因为与中位数不同,它与总额有直接关系。平均值对极值很敏感,因此应注意确保它们是在干净、有意义的数据集上计算的。当分布是偏斜的时候,中位数可以提供一个“典型”值的更直观的感觉,但这并不一定意味着它是最优决策政策的基础。
这个主题还强调了数据科学家需要将决策分析的主题与统计、机器学习和编程一起纳入他们的学习计划。
感谢你阅读这篇文章。如果您对更多决策分析和统计内容感兴趣,请在 Medium 上关注我,或者在 LinkedIn 上联系我。
推荐资源:
- Ronald a . Howard 的《决策分析基础》。这本书为决策分析的核心概念提供了一个极好的概述。这些概念帮助我成长为一名数据科学家。
- 从决策角度看均值、中值和众数梅林达·米勒·霍尔特和斯蒂芬·m·斯卡利亚诺 。本文概述了使均值、中值或众数成为决策最佳衡量标准的数学条件。一些描述的激励结构在实践中并不常见,但本文为选择汇总统计数据提供了一个有趣的数学框架。
- 你能解开这个射箭谜语吗?by FiveThirtyEight(YouTube 视频)。这个谜语概括了一种情况,在这种情况下,均值不是决策的理想衡量标准。一些人指出这个谜语是均值无用的一个例子,因为分布是偏斜的。相反,我认为这是关于分析的目标。目标是最大化获胜概率,这不同于最大化期望总分。
平均因果效应与中位数因果效应
原文:https://towardsdatascience.com/mean-vs-median-causal-effect-37057a6c54c9
因果数据科学
介绍 A/B 测试中的分位数回归

封面图片,由作者使用nightcafe生成
在 A/B 测试中,也称为随机对照试验,我们通常估计平均治疗效果(ATE) :治疗(药物、广告、产品……)对感兴趣的结果(疾病、公司收入、客户满意度……)的影响,其中“平均值”取自测试对象(患者、用户、客户……)。ATE 是一个非常有用的量,因为它告诉我们,如果我们用同样的方法治疗一个新的受试者,我们可以预期的效果。
然而,有时我们可能对不同于平均值的数量感兴趣,例如 中值 。中位数是对中心趋势的另一种度量,它对异常值更稳健,并且通常在偏斜分布的情况下提供更多信息。更一般地说,我们可能想要估计结果分布的不同分位数的影响。一个常见用例是研究 UI 变化对网站加载时间的影响:一个稍微大一点的网站可能对大多数用户来说是一个察觉不到的变化,但对少数连接非常慢的用户来说是一个很大的变化。另一个常见的用例是研究产品变化对很少有人购买的产品的影响:是现有客户购买更多,还是我们在吸引新客户?
这些问题很难用估计平均治疗效果的线性回归来回答。更合适的工具是分位数回归,它可以替代估计中位治疗效果。在本文中,我们将简要介绍分位数回归和分位数处理效应的估计。
忠诚卡和消费
假设我们是一家网上商店,我们想增加销售额。我们决定向顾客提供一张优惠卡,当他们在商店增加消费时,可以享受折扣。我们想评估忠诚卡是否能有效增加销售额,所以我们进行了一个 A/B 测试:我们随机地只向一部分顾客提供忠诚卡。
我从[src.dgp](https://github.com/matteocourthoud/Blog-Posts/blob/main/notebooks/src/dgp.py)导入数据生成过程dgp_loyalty()。我还从[src.utils](https://github.com/matteocourthoud/Blog-Posts/blob/main/notebooks/src/utils.py)引进了一些绘图函数和库。为了不仅包括代码,还包括数据和表格,我使用了 Deepnote ,一个类似 Jupyter 的基于网络的协作笔记本环境。
现在,我们来看看数据。我们有 10,000 名客户的信息,我们观察他们的spend以及他们是否被提供了loyalty卡。我们还观察了一些人口统计数据,比如age和gender。
有趣的是,我们注意到感兴趣的结果spend,似乎有很多零。让我们深入了解一下。
平均值与中位数
在分析我们的实验之前,让我们看看我们的结果变量spend。我们首先使用中心性度量来检查它。我们有两个主要的选择:平均值和中间值。
首先,它们是什么?平均值捕捉平均值,而中值捕捉分布中心的值。一般来说,平均值在数学上更容易处理和解释,而中位数对异常值更稳健。你可以在网上找到很多文章来比较这两种方法,并建议哪一种更合适以及何时使用。我们来看看均值和中位数spend。
我们如何解读这两个数字?人们在我们商店平均消费 28 美元。但是,超过 50%的人什么都不花。正如我们所看到的,这两项措施都提供了大量信息,并且在一定程度上是互补的。通过绘制其直方图,我们可以更好地理解spend的分布。

支出分布,按作者分类的图像
从平均值和中值来看,spend的分布非常偏斜,超过 5000 名顾客(总共 10000 名)没有消费任何东西。
一个自然的问题是:我们对平均spend或中位数spend的loyalty卡片的影响感兴趣吗?第一个将告诉我们客户是否平均花费更多,而第二个将告诉我们客户是否平均花费更多。
线性回归可以告诉我们loyalty牌对平均spend的影响。然而,如果我们对loyalty卡片对中位数spend(或其他分位数)的影响感兴趣,我们能做什么呢?答案是分位数回归。
分位数回归
使用线性回归,我们尝试估计一个结果变量 Y (在我们的示例中为spend)相对于一个或多个解释变量 X (在我们的示例中为loyalty)的条件期望函数。

条件期望函数,作者图片
换句话说,我们想找到一个函数 f 使得 f(X) = 𝔼[ Y|X 。我们通过解决以下最小化问题来做到这一点:

条件期望函数的估计,作者图片
可以看出,解决这个极小化问题的函数 f 确实是 Y 的条件期望,相对于 X 。
由于 f(X) 可以是无限维的,我们通常估计 f(X) 的一个参数形式。最常见的是线性形式 f(X)=βX ,其中 β 通过求解相应的最小化问题来估计:

线性回归系数,作者图片
线性形式不仅仅是方便,还可以解释为 f(X) 的最佳局部逼近,参考泰勒展开式。
使用分位数回归,我们做同样的事情。唯一的区别是,我们不是估计 Y 相对于 X 的条件期望,而是要估计 Y 相对于 X 的 q - 分位数。

条件分位数函数,作者图片
首先,什么是分位数?维基百科的定义说
在统计学和概率学中,分位数是将概率分布的范围划分为具有相等概率的连续区间的切割点,或者以同样的方式划分样本中的观察值。常见的分位数有特殊的名称,如四分位数(四组)、十分位数(十组)、百分位数(100 组)。”
例如,0.1 分位数表示位于分布质量的 10%右侧的值。中位数是 0.5 分位数(或者,等价地,50ᵗʰ百分位或 5ᵗʰ十分位),对应于分布中心的值。让我们看一个简单的例子,使用对数正态分布。我绘制了三个四分位数,将数据分成四个大小相等的区间。

对数正态分布的分位数,按作者分类的图像
如我们所见,三个四分位数将数据分成四个大小相等的区间。
那么,分位数回归的目标是什么呢?目标是找到一个函数 f 使得 f(X) = F⁻ (y_q) ,其中 F 是 Y 的累积分布函数,而 y_q 是 Y 分布的q-分位数。
我们如何做到这一点?用一点线性代数就可以表明,我们可以获得条件分位数作为下面的最小化问题的解:

条件分位数函数估计,作者图片
其中 ρ_q (上面隐式定义)是具有以下形状的辅助加权函数。

Rho 加权函数,图片由作者提供
目标函数背后的直觉是什么?
这个想法是我们可以这样解释这个等式

目标函数解释,图像由作者提供
所以,当 f(X) 对应分位数 y_q 时,目标函数值为零。
与之前完全一样,我们可以估计出 f 的一个参数形式,并且与之前完全一样,如 Angrist、Chernozhukov 和 Fernández-Val (2006) 所示,我们可以将其解释为最佳局部近似。

分位数系数,作者图片
我们写了 β̂ _q 来表示这是条件 q 分位数函数的最佳线性近似系数。
我们如何估计分位数回归?
估计
statsmodels 包允许我们用[quantreg()](https://www.statsmodels.org/dev/generated/statsmodels.regression.quantile_regression.QuantReg.html)函数估计分位数回归。我们只需要在拟合模型时指定分位数 q 。我们用 q=0.5 ,对应中值。
分位数回归估计loyalty的正系数。这个估计与线性回归相比如何?
线性回归的估计系数较高。这是什么意思?稍后,我们将在分位数回归系数的解释上花费更多时间。
我们能以其他变量作为分析的条件吗?我们怀疑spend也会受到客户人口统计的影响,我们希望通过对age和gender进行条件分析来提高我们估计的精确度。我们可以将变量添加到quantreg()模型中。
当我们对age和gender进行条件分析时,loyalty的系数增加。线性回归的情况并非如此。
有几件事我们还没有提到。第一个是推理。在分位数回归中,我们如何计算估计值的置信区间和 p 值?
推理
累积分布 F 的 q- 分位数的估计值 y 的渐近方差由下式给出

分位数渐近方差,作者图片
其中 f 是 F 的密度函数。这个表达式可以分解成两个分量 : q(1-q) 和 f⁻ (y) 。
第一个分量 q(1-q) 告诉我们,分位数越接近分布的中心,分位数的方差就越高。为什么会这样呢?首先,我们需要考虑一个点的分位数何时响应第二个点的值的变化而变化。当第二个点从第一个点的左侧交换到右侧(反之亦然)时,分位数会发生变化。如果第一个点位于分布的中间,这在直觉上是非常容易的,但是如果它位于极值,这就非常困难了。
第二个成分, f⁻ (y) ,告诉我们,如果点是孤立的,那么方差更高,因此我们对它的了解更少。
重要的是,估计分位数的方差需要对 Y 的整个分布进行估计。这是通过近似来完成的,并且计算量非常大。如果这个过程变得计算量太大,像 bootstrap 或贝叶斯 bootstrap 这样的替代程序总是可用的。
第二个我们还没有谈到的是估算系数的解释。我们通过中值回归得到了spend的较低系数loyalty。这是什么意思?
解释
线性回归系数的解释很简单:每个系数都是条件期望函数𝔼[ Y|X 相对于 X 一维的导数。在我们的例子中,我们可以将loyalty的回归系数解释为平均spend从获得loyalty卡开始增加。重要的是,这里的“平均”是指平均而言,每个客户都是如此。
然而,分位数回归系数的解释是很棘手的。之前,我们很想说loyalty卡增加了中位顾客 3.4 美元的消费。但是是什么意思?是同一个中位客户花费更多,还是我们有不同的中位客户?这可能看起来像一个哲学问题,但它对报告分位数回归结果有重要意义。在第一种情况下,我们正在做一个声明,至于线性回归系数的解释,适用于一个单一个体。在第二种情况下,我们正在对分布进行陈述。
Chernozhukov 和 Hansen (2005) 表明一个强有力但有用的假设是秩不变性:假设处理不改变分布的相对组成。换句话说,如果我们在实验前按spend对人进行排名,我们假设这个排名不受loyalty卡的引入的影响。如果我以前花的比你少,我以后可能会花更多,但仍然比你少(对任何两个人来说)。
在这种假设下,我们可以将分位数系数解释为位于结果分布不同点的单个个体的边际效应,如上面提供的第一种解释。此外,我们可以报告许多分位数的治疗效果,并将每个分位数解释为不同个体的局部效果。让我们绘制不同分位数spend的治疗效果分布图。

分位数因果效应,按作者分类的图像
这个情节极有见地:对于几乎一半的客户来说,loyalty卡没有任何作用。另一方面,已经在消费的顾客最终会消费更多(大约多 10/12 美元)。这是一个非常强大的洞察力,我们可能会错过一个估计平均影响为 5 美元的线性回归。
我们可以重复同样的练习,根据gender和age进行分析。

条件分位数因果效应,作者图片
对其他协变量的调节消除了治疗效果的异质性。这张卡增加了大多数人的支出。
结论
在本文中,我们探讨了一个不同的因果估计:中位数治疗效果。与我们通常估计的平均治疗效果相比如何?利弊与中位数的利弊密切相关相对于作为集中趋势衡量标准的均值。中位治疗效果更能说明对普通受试者的影响,对异常值也更稳健。然而,它们在计算上要求更高,并且它们需要用于识别的强假设,例如秩不变性。
参考
[1] R. Koenker,《分位数回归》 (1996),剑桥大学出版社。
[2] R. Koenker,K. Hallock,分位数回归 (2001),《经济展望杂志》。
[3] V. Chernozhukov,C. Hansen,一个分位数处理效应的 IV 模型 (2005),计量经济学。
[4] J. Angrist,V. Chernozhukov,I. Fernández-Val,错误设定下的分位数回归,应用于美国工资结构 (2006),计量经济学。
相关文章
密码
你可以在这里找到 Jupyter 的原始笔记本:
*https://github.com/matteocourthoud/Blog-Posts/blob/main/notebooks/quantile_reg.ipynb *
感谢您的阅读!
真的很感谢!🤗如果你喜欢这个帖子并且想看更多,可以考虑 关注我 。我定期发布与因果推理和数据分析相关的主题。我尽量让我的帖子简单而精确,总是提供代码、例子和模拟。
还有,一个小小的 免责声明 :我写作是为了学习所以错误是常态,尽管我尽力了。当你发现他们的时候,请告诉我。也很欣赏新话题的建议!
使用模拟的 A/A 和 A/B 测试测量 A/B 测试平台的运行状况
我们如何通过模拟来确保我们的 A/B 测试平台的可靠性

A/B 测试在数据驱动型公司的决策中起着至关重要的作用。这通常是新产品设计、商业策略、机器学习模型等的成败的最终决定权。A/B 测试平台中的不准确性降低了从 A/B 测试中得出的所有商业决策。在本文中,我们分享了不断评估 A/B 测试平台可靠性的方法,以确保实验平台值得信赖。
连续评估的目标是测量I 型误差率和功率,等于 1-II 型误差率。第一类错误率和功效决定了假设检验的质量。类型 I 错误(误报)通常是当新功能对业务指标没有重大影响时,对有益/有害产品功能的错误声明。发布一个没有影响的特性是一种浪费,可能会导致在一个没有结果的领域的进一步投资。第二类错误(假阴性)导致得出有益/有害产品特征是中性的结论。当我们基于错误的 A/B 测试结果,扼杀一个可能需要几个月来构建的产品特性时,这是一个机会的错过。
模拟 A/A 测试测量第一类错误率
为了测量 I 型错误率(假阳性率),我们每天使用过去七天积累的数据运行 500 次模拟 A/A 测试。每个模拟的 A/A 测试将用户随机分为控制组和处理组,并以与生产指标计算管道相同的方式计算两个实验组的指标。
它需要大量的 A/A 测试来精确地测量 I 型错误率,但是用实时用户流量运行所有的重复测试成本太高。模拟 A/A 测试离线运行,因此对应用/网站延迟没有影响。
我们发现在 I 型错误率测量中,500 次重复在计算成本和精度之间取得了平衡。在标称 p 值阈值为 0.05 的情况下,对于可靠的 A/B 测试平台,预期的 I 型错误率(假阳性率)为 5%。假设我们的假阳性率确实是 5%,那么蒙特卡洛模拟中的每次重复都是一次独立的伯努利试验,成功概率为 5%。因此,我们的假阳性测量的 95%置信区间(CI)为

CI 随着重复次数的平方根缩小( n )。下表显示了不同重复次数下计算的 CI。
表 1。CI 随着重复次数的平方根( n )而缩小
图 1 展示了一个仪表板,用于监控一段时间内的误报率。

图一。连续假阳性率评估
此外,我们不断评估来自模拟 A/A 测试的 p 值的分布。当零假设为真,并且满足所有其他假设时,p-值应均匀分布[1]。零假设为μ1=μ2,其中μ1 和μ2 分别是对照桶和处理桶的总体均值。

图二。A/A 测试的 p 值分布
模拟 A/B 测试测量功率
统计功效,等于 1-类型 II 错误(假阴性)率,同样重要,但由于缺乏基本事实,评估起来更具挑战性。我们通常没有能够提高指标的产品特性。否则,我们早就实施了。另一方面,使用劣质版本的产品来评估 A/B 测试平台的假阳性将是昂贵的,有时甚至是不道德的。模拟 A/B 测试使我们能够在不影响用户体验的情况下评估各种业务场景中的能力。
我们与模拟 A/A 测试类似地进行模拟 A/B 测试,除了我们在处理组中引入合成差异,即,为处理组中的用户增加 1%的度量值。在模拟 A/B 测试中,我们知道由于综合引入的差异,零假设(即,无效果)不成立。模拟 A/B 测试中的任何中性结果(没有统计意义的结果)都是第二类错误(假阴性)。
此外,通过模拟,我们可以在各种场景中评估 A/B 测试的能力。一些说明性的场景是:
- 对照和处理之间不同水平的度量差异:0.1%、0.2%、0.5%等。
- 不同模式的差异:新产品功能主要影响重度用户,新产品指标的有效性取决于用户属性(如年龄、地理位置、语言)等。
注:典型的功效分析相当于具有一致差异的模拟 A/B 测试——处理桶中的总体平均值一致移动 X 量。通过模拟的 A/B 测试,我们可以理解具有更复杂业务假设的 A/B 测试的统计能力。
图 3 显示了不同高度的模拟 A/B 测试的结果,其中有一个< b < c < d. When the lift reaches c%, most metrics have sufficient powers. Also, even when the lifts are the same, different metrics have different levels of power due to the difference in the variances of the metrics.

Fig 3. Continuous false negative rate evaluation
Conclusion
The more we improve our A/B testing platform, the more we realize that devil is in the details. It is surprisingly hard to get your A/B testing right. Something seemingly straightforward can lead to 严重缺陷,看似合理的方法可能会产生非常不准确的结果。对 A/B 测试平台的健康状况进行系统评估至关重要。模拟 A/A 和 A/B 测试持续测量平台的健康状况。
我们的目标是在未来的工作中使我们的评估更加全面和方便。尽管由于使用来自所有用户的数据,我们当前的模拟 A/A 测试是通用的,但我们计划通过按需安排 A/A 测试来启用特定于实验的模拟 A/A 测试,仅让用户参与特定实验。此外,我们计划在模拟的 A/B 测试中添加更复杂的场景,以代表各种各样的业务案例。
感谢
感谢齐超对这个项目的贡献。我们也感谢 Pai Liu 的支持,以及 Pavel Kochetkov、Lance Deng 和 Delia Mitchell 的反馈。
Wish 的数据科学家热衷于构建一个值得信赖的实验平台。如果你对解决这一领域的挑战性问题感兴趣,我们正在为数据科学团队招聘。
参考
[1] Murdoch,Duncan J .,蔡玉玲,和 James Adcock,《P 值是随机变量》 (2008),美国统计学家,62.3:242–245。
用水测量咖啡粉末
原文:https://towardsdatascience.com/measuring-coffee-fines-using-water-aa17595526c9
咖啡数据科学
受强制微粒迁移启发的有趣方法
在思考我的强制微粒迁移实验时,我认为这将是一种测量整个圆盘中微粒的有趣方法。您可以使用成像或筛选来测量微粒,但这两种方法都有一些挑战。对于成像,你不能解决最小的罚款。对于筛分来说,不花大力气筛分 200um 以下甚至 300um 以下是有挑战性的。
通过我的强制微粒迁移实验,我能够推出所有比过滤篮孔更细的微粒。我想我可以干燥样品,更好地测量粉末。
https://youtube.com/shorts/G1VYGoUsDo4
冰球准备
我在一个 20 克的 VST 篮子里用了 22 克,我用 Atago 测量了 8:1 的 EY,所以大部分可提取的咖啡都被提取出来了。然后我让冰球冷却。

所有图片由作者提供
后来,我把它切成了片。我的目标是超过 3 片,但是很难。我最后是底部一片,中间一片,顶部分成两个样本。
我把切片放在一个冰块托盘里,这是我储存粉末的首选方式。

然后,我将一些水推入,迫使较细的颗粒通过过滤篮。

这里有一个视频回顾了我是如何强迫微粒迁移的,这本质上是一个湿筛。
在这个过程中,有很多悬浮颗粒。

我让它们沉淀下来,然后把水移走。我在低温下烘干了样品。

最终的细粒比用细筛筛过的普通咖啡要细得多。

最后,我对四个样品分别进行了粗选和精选。

然后我用 Acaia Pyxis 秤称了每个样品的重量,并计算了每个样品中细颗粒的百分比。

我预计,如果在原始镜头中微粒迁移,底部的微粒百分比会比其他层高。然而,事实并非如此。
我不确定为什么顶层的细屑更多,这可能是测量、切割或强制迁移过程中的错误。一些细颗粒可能已经迁移出了注射液,但是注射液中出来的细颗粒的量最终小于 0.01 克(即将进行实验)。
最终,这个实验是反对微粒迁移理论的又一个标志,并且它提供了一个容易重复的实验。之前实验的问题是要知道你是否真的得到了所有更细的粒子。湿筛似乎有助于测量最细的粉末。
如果你愿意,可以在推特、 YouTube 和 Instagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以关注我在中和订阅。
我的进一步阅读:
工作和学校故事集
测量嵌入漂移
原文:https://towardsdatascience.com/measuring-embedding-drift-aa9b7ddb84ae
测量非结构化数据的嵌入/向量漂移的方法,包括计算机视觉和自然语言处理模型

作者图片
本文是与 Arize AI 的首席执行官兼联合创始人 Jason Lopatecki 合作撰写的
图像等非结构化数据中的数据漂移很难测量。通常用于结构化数据漂移的指标,如群体稳定性指数 (PSI)、库尔贝克-莱布勒散度( KL 散度)和詹森-香农散度( JS 散度)以及 Kolmogorov Smirnov 检验),允许对结构化标签进行统计分析,但不能扩展到非结构化数据。测量非结构化数据漂移的一般挑战是,您需要了解非结构化数据本身内部关系的变化。简而言之,你需要更深入地理解数据,才能理解漂移。
非结构化漂移的目标是检测两个非结构化数据集是否不同,如果不同,则让工作流了解数据集不同的原因。这篇文章推荐了一种非结构化漂移的全局测量和方法。一旦发生漂移检测,用于故障排除的根本原因工作流程将在之前的 文章中介绍。
非结构化数据中的漂移意味着什么?
图像漂移的例子
当团队强调图像数据中的问题时,通常存在各种各样的问题。这些问题包括图像模糊、有斑点、变亮、变暗、旋转和裁剪。

图像变暗、模糊或有斑点的问题(图片由作者提供)
当然,用于计算机视觉模型的图像面临各种形状和大小的挑战。要解决这些问题,您需要一种方法,在这种方法中,您不需要先验地知道您需要检测的所有问题的组合,因为在实践中通常不可能知道。
团队想要检测的更常见的数据漂移事件之一与图像质量问题无关,它实际上是找到不在原始训练集中的对象的示例。在生产数据中观察到的、在训练集中缺失的独特情况、事件、人员或对象是什么?

来自训练集的单个苹果(图片由作者提供)
例如,训练集可能包括单个苹果的图片,但不包括多个苹果或其他水果的图片。

生产中见到的多种苹果和混合水果(图片由作者提供)
与第一张图相比,上面的水果篮包含了相当多的其他关系和对象。如果原始训练集只包括苹果本身的图片,则类似上面的图片可能会导致模型出现问题。
文本漂移的例子
考虑到语言不断进化的本质,自然语言处理(NLP)模型的潜在问题也同样多种多样。例如,随着时间的推移,数据中术语的任何变化或者单词或短语的上下文或含义的变化都会导致漂移。资源匮乏的语言和言语中的文化差异也会加剧 ML 团队的困难。
文本漂移最常见的一种情况是,在生产中出现了一个在训练数据中不存在的单词、类别或语言。例如,如果在生产中第一次遇到西班牙语评论,对数百万英语服装产品评论进行训练的情感分类模型的性能可能会下降。如果突然要求这种模型预测(比如)专业医疗设备评论的情绪,它也会陷入困境。

作者图片
提取嵌入
为了理解所有的结构关系并监控它们何时改变,我们需要利用嵌入来捕捉非结构化数据中的潜在结构。
嵌入可以被认为是表示通过非结构化数据学习的潜在结构的向量。

嵌入矢量表示(图片由作者提供)
一组嵌入,其中每个嵌入代表一个图像或预测,映射出高维空间中的曲面(流形)。

高维曲面示例(图片由作者提供)
上面的例子显示了由高维向量数据表示的表面和关系。有苹果的图像将与其他有苹果的图像有关系。有多种水果的图像将与苹果和其他水果的图像都有关系。他们会在高维空间中彼此靠近。
随着卷积神经网络(CNN)的深入,嵌入表示图像内部特征或信息的复杂组合。嵌入包含了决定图像结构所需的所有信息,从模糊到物体的存在。
从那里,很容易从任何神经网络中提取嵌入。
案例 1:拥有模型
理想的情况是,团队可以访问原始模型,并可以在产品中提取嵌入内容,以便观察。在这种情况下,从生产中的当前模型中提取嵌入。这种方法的优点是,监控发生在模型内部观察到的结构上,因此更有可能捕捉到模型是否在数据上有问题。

选项 1:从模型中提取嵌入内容(作者图片)
在嵌入漂移分析的情况下,建议在分类之前提取最后一个完全连接的层来创建图像嵌入。例如,它是包括所有复合潜在结构信息的层,以做出图像级决策。除了与训练集中的图像相关的一般质量信息之外,潜在结构将包含关于图像中的结构的信息,例如对象和动作。
在使用视觉转换器(ViT) 的情况下,建议您提取多层感知器(MLP)正在作用的嵌入,以做出图像级决策。对于如何从一个众所周知的拥抱脸模型中提取嵌入的例子,这里有一个的分步指南。
案例 2:使用全局模型

选项 2:从数据中提取嵌入内容(图片由作者提供)
第二种选择是使用全局的、预训练的模型来从数据中提取嵌入。使用像 BERT 这样的基础模型来提取嵌入是有利的,因为不需要对生产模型进行修改。对于测试和运行内部数据,这也是一个简单的选择。一个缺点是,它只查看数据本身,而不查看内部模型决策。
一旦我们生成了嵌入,我们现在可以查看嵌入漂移度量来比较嵌入组,以了解它们作为一个组是如何漂移的。
嵌入漂移度量
比较不同时期之间的嵌入是我们检测漂移是否发生以及漂移发生在哪里的方法。

嵌入漂移—引入新数据(图片由作者提供)
在上面的示例中,灰色点是生产环境中的预测,而蓝色点是训练中的预测。人们可以看到,在培训和生产的相似时期,这些点重叠得相当紧密。在这个例子中,一组新的有问题的数据(模糊的图像)被添加到数据集,产生了一组与其他非常不同的预测。
有许多漂移测量可以应用于嵌入。这些包括超盒 IOU、欧几里德距离、余弦距离和基于聚类的组纯度分数。在这篇文章中,我们将深入探讨欧几里德距离和余弦距离及其功效。

作者图片
上图显示了两组向量——一组用于基线,另一组用于生产。当两个向量相距较远时,欧几里德距离和余弦距离都较大。这被监控为嵌入漂移。
欧氏距离是如何计算的?
简单的欧几里德距离检查确定生产数据组的平均质心是否已经远离基线组。

嵌入平均值和质心(图片由作者提供)
上述计算将表示单组数据(即生产矢量),对所有矢量求和,并创建质心。
一旦计算出生产向量和基线向量的平均值,就可以得到欧几里德距离。为了计算,取两个向量之间的差,然后取差之和的平方根。

作者图片
余弦距离是如何计算的?
余弦距离本质上是 1 —余弦相似度。两个向量越相似,距离越小。他们越不相似,距离就越大。
为了计算余弦距离,我们再次需要两个向量,在本例中,我们取一个生产向量和一个基线向量。余弦距离的计算:

作者图片
换句话说:

作者图片
测试欧几里德距离的功效
在测试了大量相当复杂的漂移测量方法后,我们发现与其他方法相比,监控嵌入集的简单欧几里德距离是稳定的、灵敏的和可扩展的。
当比较数据集之间的欧几里得距离和余弦距离时,当漂移增加时,余弦更加敏感和显著。欧几里得仍然检测到这种变化,但在实验中更加稳定。
在下图中,左边的 Y 轴是欧几里德增量,右边的 Y 轴是余弦增量。它们的方向相似。

作者图片
下一节重点介绍了几个引入漂移的真实实验,欧几里德距离精确地获得了数据漂移。
实验结果# 1——将培训之外的新价值引入生产

缓慢增加的漂移数据(图片由作者提供)
上图显示了一个混合在生产数据中的漂移数据示例,该数据不在训练数据集中。即使许多向量在流形中重叠并且彼此靠近,左上角的欧几里得距离度量也能检测到差异。
实验结果#2 —将完全不同的数据引入生产数据

缓慢增加的漂移数据(图片由作者提供)
这里的数据集示例有一个向量空间,对于漂移的数据来说,它看起来非常不同,在右边底部的图像中显示了一个黄色组。随着越来越多的漂移数据混入生产数据集中,欧氏距离会增加并检测到数据差异(左上角)。
其他技术
为了在欧几里德距离之上添加更健壮的警报统计,可以对嵌入度量使用 KS 2 样本测试。可以从嵌入集中提取多个样本,分别计算每个样本集的欧几里德距离度量,并且可以使用 KS 检验来确定是否发生了漂移。
结论
随着 CV 和 NLP 模型的激增,越来越多的 ML 团队正在寻求嵌入漂移来帮助检测和理解他们的非结构化数据中的差异。在测试了大量不同数据的不同场景后,我们发现利用欧几里德距离是一种敏感、稳定且可扩展的测量方法,有助于完成这项任务。
联系我们
如果这个博客引起了你的注意,并且你渴望了解更多关于机器学习可观察性和模型监控,请查看我们的其他博客和资源!如果您有兴趣加入一个有趣的 rockstar 工程团队来帮助制作成功的模型,请随时联系我们,或者注册一个免费账户,或者在这里找到我们的空缺职位!
衡量食物的相似性
原文:https://towardsdatascience.com/measuring-meals-similarities-3edb8a861f16
在食品领域应用 Word2Vec 生成食品嵌入
DayTwo 拥有超过 80000 名会员的超过 1100 万份记录餐(并且每天都在增加)的庞大数据库,这些会员的口味广泛多样。这些食物从早餐到晚餐,从单一食物到复杂的膳食,从小吃到美食,等等。我们的会员投资于记录他们的膳食,以便他们可以跟踪他们的日常食物消耗,并获得关于他们定期糖反应水平的总体消耗的更广泛的概述。
记录的餐食(和会员)越来越多,这让我们能够帮助顾客根据他们平时的饮食不断寻找新的菜肴。通过查看历史数据和描述他们接下来可能想吃的潜在食物来减轻这种负担,对用户体验和我们服务的满意度有着巨大的影响。

作者图片
在这篇文章中,我们探索了来自 NLP 领域的机器学习技术如何帮助我们测量食物的相似性。这些技术的主要优势是双重的。首先,它们利用了大量的历史数据,这些数据不断增长,反映了我们用户的真实食物口味。第二,它们可以被分成不同的区域,已知这些区域具有不同的食物偏好。例如,我们知道西方饮食以碳水化合物和脂肪为基础,而东方饮食更喜欢鱼和藻类。因此,通过了解不同地区不同食物的相似性,我们可以针对特定人群定制我们的推荐引擎,并增加我们的个性化体验。
膳食数据
(使用的数据集不是开源的,属于 DayTwo)
为了计算两餐之间的相似性得分,我们首先需要生成餐嵌入(也称为向量)。这使得我们可以使用一个通用的距离度量来检索从 0 到 1 的相似性得分,其中相似的饭菜得分较高。
但是在能够生成膳食嵌入之前,我们首先为每个食物项目计算一个单个嵌入向量。然后,我们可以认为膳食矢量是膳食中所有食物矢量的平均值。你可以考虑去掉一些基本的东西,比如香料或者酱汁,因为它们不会给你的嵌入添加太多的信息(出于同样的原因,当训练单词嵌入时,停用词会被去掉)。
我们使用了一组 1100 万份记录的膳食(针对每个群组),其中每份膳食 m 被表示为一列 k 独特的食物。

从下面的柱状图中可以看出,食物种类的数量变化很大(只有一种食物的膳食已经被剔除)。从有几个项目的膳食到有几十个项目的膳食,显然,为了过滤不合理的膳食,对数据的深入研究是必要的。总的来说,似乎每个用户都在不同程度上详细记录了自己的饮食。

作者图片
使用机器学习来学习食物向量
为了训练 food2vec 神经网络并获得每个食物项目的嵌入向量,每顿饭被视为用单个空格分隔的食物项目 id 的句子。

这使得可以使用现有的 word2vec 库来训练网络。我们使用 skip-gram word2vec 模型,其窗口大小 w 等于最大用餐时间,以捕获所有用餐中所有可用的食物项目对。由于食品领域与地理属性高度相关,我们为每个群组分别训练我们的嵌入(包括超参数优化)。

Word2Vec 神经网络模型图解—图片由作者提供
衡量相似性
一旦在高维空间中有了每个食物项目的嵌入向量,其中相似的项目相对于空间距离度量(例如欧几里德距离)看起来更接近,我们就可以使用该嵌入图来识别共享共同元素的食物项目或膳食的聚类。
值得注意的是,关于什么被认为是相似的,没有“基本事实”。嵌入可以在不同的方面进行优化,这将产生不同的相似性。

食物和膳食矢量空间插图-作者图片
收场白
找到相似的食物很难,特别是由于人类的口味往往非常个性化。幸运的是,这篇文章中讨论的食物嵌入帮助我们实现了这一点,并服务于其他几个目的。首先是,用从数百万历史记录中学习到的元数据为推荐系统提供支持。第二个,作为许多机器学习模型的构建模块,取代了天真的特征,并且在我们测试的各种分类任务中明显优于它们。第三个,它们封装了我们可以从所有食物中提取的所有区域信号,允许 ML 模型基于特定位置更加准确。
如果你想了解更多关于我们正在开发的最新最棒的产品,请随时联系我。
测量咖啡折光仪的精确度
咖啡数据科学
更好的取样,以更好地了解 TDS 与 Brix 和 Groundtruth
我每天使用一台折光仪(一台数字 Atago)来测量总溶解固体(TDS),然后用它来计算提取率(EY)。我假设 Atago 是准确的,但没有 Atago 或任何折光仪生产商提供的数据。他们只给出准确的数字。我已经做了一些其他的测试,但是我想看看我如何能更好地测试 groundtruth。
我将 Atago 与 Brix 视觉折射仪进行了比较,然后将这两者与一些基本事实进行了比较。我用了两种物质来制作 groundtruth: 浓缩咖啡粉和糖。我把这些溶于水中,做了一些试验。

Atago 和视觉白利,所有图片由作者提供
将白利糖度转换为 TDS 的通用公式为 TDS = 0.85 *白利糖度。我想看看这个常数有多精确,或者二阶多项式是否更合适。
视觉白利折光仪的一个挑战是它们有时难以读取。您可以在两个散列标记之间进行分辨,这样您就可以达到 0.1°白利糖度的精度,这对于浓缩咖啡读数来说很好,但对于 pourover 来说,读数较低时就不那么好了。
浓缩咖啡粉或咖啡的白利糖度读数有点模糊,但纯糖的线条非常清晰。通常,如果你采取足够的读数,你可以获得更好的阅读咖啡的一致性。通常,正确的读数是颜色下降的边缘。

现实生活中的图像更清晰。
以前的数据
我刚拿到 Atago 的时候收集了一些数据。我在上半场和下半场各投各拉。绘制这些数据点显示,0.9074 的直线非常符合数据。

所以我把数据分成了前半部分和后半部分。较高的 TDS 值在 0.8322 处更接近于 0.85,但是较低的值是 0.902。仍然有一点噪音。我想知道白利糖度和 TDS 之间的联系在多大程度上取决于从照片中提取的内容和时间。


浓缩咖啡粉作为研磨的真理
我从浓缩咖啡粉开始,我知道浓缩咖啡粉和水的重量,根据经验知道 TDS。然后,我使用这些样品和随后稀释的起始溶液来获得曲线上的多个点。
数据没有太多噪音,趋势有很好的相关性。TDS 对 Brix 的趋势线斜率为 0.88,但仍不是 0.85。

当将 TDS 或 Brix 与 groundtruth 进行比较时,Brix 的趋势线是 0.994,这意味着浓缩咖啡粉不像我想象的那样具有代表性。它比咖啡更接近糖,因为 TDS 测量值的斜率为 0.894。

糖水是真理
我用糖水做了同样的实验,但 TDS 和 Brix 之间的关系仍然不是 0.85。最佳拟合线的斜率为 0.8711。

如果移除(22,21)处的一个异常值,最佳拟合的斜率为 0.85。

当我查看测量的 TDS 与 groundtruth 时,它更接近 0.85,为 0.8492,这意味着 Atago 中有一个偏移,可以用来测量咖啡而不是糖水。

在较低的值上发生了一些奇怪的事情。我关注的是小于 4 的 TDS,这些点的最佳拟合线非常接近 y = x 或斜率 1。

我也对比了白利糖度和地面真实度,在下端发生了一些奇怪的事情。

我发现的问题比我想回答的要多:
- 我需要一个更好的研磨真理或者一个改良的咖啡研磨真理。
- 较低 TDS 的解决方案行为古怪,这可能是由于一些最佳拟合方程。
- 需要更多的数据来理解 TDS 对于镜头的不同部分是如何准确的。因此,用多种掺水样品拍摄的意大利腊肠将是一个有趣的体验。
如果你愿意,可以在推特、 YouTube 和 Instagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以关注我在中和订阅。
我的进一步阅读:
使用 SQL 度量 BigQuery 中的字符串相似性
原文:https://towardsdatascience.com/measuring-string-similarity-in-bigquery-using-sql-33c490638c89
使用 Levenshtein 距离来发现相似或重复的值,清理您的数据,等等

照片由 S uraj Kardile 在 Unsplash 上拍摄
使用 Levenshtein 距离方法
该方法可用于(Soundex,LIKE statement,Regexp)执行字符串相似性或字符串匹配,以识别两个相似但不相同的元素(文本、字符串、输入)。
此方法可用于各种应用程序,包括识别重复项、处理拼写错误的用户输入数据、清理客户数据等等。
Levenshtein 方法的思想是计算两个字符串之间的最小编辑距离。
但是什么是“编辑”距离呢?它由三个动作组成:插入、替换、、删除。这三个动作被算法赋予相等的权重,基本上是 1。
为了帮助理解这些机制,这里有几个例子:
- Bigquer→Bigquery:字母
y加在最后,所以距离会是 1(我们做了 1 个加法)。 - 音乐→ mujic : 字母
j是来自音乐的字母s的替换,距离也是 1(我们做了 1 次替换)。 - 薯条→ frij : 字母
french和空格被追加,字母j是代替e,字母s被删除**。(我们增加了 7 处,替换了 1 处,删除了 1 处)**
这种方法在比较一个完整字符串和另一个完整字符串时特别有效(在比较一个句子中的关键字或比较一个句子和另一个句子时效果较差)。
你可以在维基百科上找到关于该算法的更多信息。
现在我们已经学习了理论和应用,让我们看看如何在 SQL 中应用它。
创建持久函数
您可以将 UDF 定义为持久的或临时的。主要区别在于持久函数可以被多个查询重用和访问(就像在项目中创建数据集一样),而临时函数只适用于您当前在编辑器中运行的查询。
就个人而言,我喜欢使用我称之为实用程序的数据集,因为它允许我存储表和函数,我可以在其他数据集和查询中重用。
下面是我们将在 BigQuery 中用到的 Javascript UDF 函数:
作为 Javascript UDF 的 Levenshtein 距离函数
当执行此查询时,它将出现在您的项目结构中,如下所示:

函数出现在您指定的数据集中的例程下(图片由作者提供)
请注意,该函数不是返回版本数量的实际差异(如果我们做了 1 个加法,则返回 1),而是将返回一个 0 到 1 范围内的值。其中 1 表示完全相似,0 表示完全不相似。
在我们的第一个理论示例中,big quer→big query:添加了字母y,表示距离为 1。我们的函数将首先计算版本数除以最长的输入字符串,在本例中,8 个字符的big query(1 个加法/ 8 个长度)是距离。****
为了得到相似度,我们简单的反过来,用 1-(1/8) = 0,875
但是首先,让我们看一个真实的 SQL 查询!
在 SQL 查询中使用函数
既然这个函数已经被保存为一个持久的 UDF,您可以简单地通过写下它的位置来调用它。在我们的示例中,将使用以下方式调用它:
``datastic.utility.levenshtein(source,target)
我们准备了几个案例来测试我们的方法产生的相似性得分。
评估 Levenshtein 距离函数的测试案例
该查询将输出以下结果:

我们每个测试案例的 Levenshtein 距离的结果(图片由作者提供)
第一种情况,多了一个字母,导致两个词的相似度为 0.88!
这两种极端情况也被证明:对于相同的单词组合 medium | medium ,结果是相似度为 1,而对于相反的单词组合 france | PIzz ,结果是相似度为 0。
公开可用数据的真实示例
我们想要解决的用例是从我们的 CRM 中更正用户国家数据。我们注意到有很多拼错的数据,我们将使用 BigQuery 公共数据来帮助我们。
该表包含 BigQuery 提供的数据,
并包含以下格式的所有国家名称:

BigQuery 公共国家数据表(图片由作者提供)
现在,我们的 CRM 数据表包含以下信息,一封电子邮件和一个国家(显然有一些拼错的单词)

我们的客户数据表(图片由作者提供)
让我们尝试解决我们现实世界的用例。为此,我们将执行以下查询,该查询由四个步骤组成:
- 从 BigQuery 加载国家数据
- 正在加载我们的 CRM 数据
- 应用一个
CROSS JOIN语句并计算我们的 Levenshtein 函数 - 对结果进行排序,找出最相似的国家
还有,塔达姆🎉,结果如下:

Levenshtein 函数在我们的 CRM 数据集上的结果(图片由作者提供)
正如你所看到的,这个方法有一些注意事项,因为我们有时会返回多行,因为两组字符串非常相似(这是西班牙的情况,西班牙与西班牙相似,巴林和台湾
我们决定使用一个CROSS JOIN语句来连接我们的两个数据表。这意味着我们将把 CRM 表中的每一行与 country 表中所有可能的国家进行比较。在我们的示例中,我们的 CRM 数据有 7 行,我们的国家数据集有 250 行,这将产生结果 7x250=1750 行。
通过使用 QUALIFY 子句对最相似的国家进行排名,可以减少这一结果,但是根据您的使用情况,CROSS JOIN解决方案将需要大量的计算。
利用更大的数据集走得更远
对于我们的例子,我们使用了一个小的 CRM 数据集(7 行)。相比之下,我在一个拥有800 万客户的数据集上重用了同一个 country 表(250 行),使用 BigQuery 花费了大约 33 秒,这还是相当快的。如果您要使用更大的数据集(可能有数十亿行),那么我会推荐使用数组而不是使用连接,在 BigQuery 中使用连接会减少所需的资源。
要解决的第二个困难是当我们的 Levenshtein 距离函数返回多个结果时选择最佳相似性。我的建议是将 Levenshtein 方法与其他方法结合起来,如正则表达式或 Soundex,这将为相似性得分提供额外的权重,并可能允许更好的选择。
测量数据可视化的认知过载因子
计算 COF 的简单经验方法,举例说明

在 Unsplash 上拍摄的 ThisisEngineering RAEng
TL;提出了一种使用评价量规计算数据可视化认知过载因子的经验方法。解释了量规使用的评估标准和评分方法。一个数据可视化的例子被用来演示该方法。建议进一步扩展和定制评估标准。
数据可视化
数据可视化将原始数据转换为图形和图表等可视化表示,以有效地将感兴趣的信息传达给目标受众,否则很难直接从原始数据中解读这些信息。以可视形式表示原始数据通过数据发现、汇总数据视图、综合信息、定量和定性趋势以及从数据中挖掘的其他模式向目标受众讲述故事。
引人入胜的讲故事的一个重要方面是减少认知超载。当信息供应量超过个人的信息处理能力时,就会出现认知超载。本质上,当呈现的信息引发试图综合和理解信息的个人的过度思考时,认知超载就发生了。这种精神能力的超负荷会导致严重的不适、沮丧和低效的决策。最重要的是目标受众在认知上处理视觉信息以理解它的难易程度。视觉中杂乱的存在、视觉不能清楚地呈现预期的信息、缺乏精心设计的美感以及其他视觉缺陷会导致消费者的认知负担过重,从而破坏视觉化的目的。
初级挑战
主要的挑战是缺乏可视化数据的认知质量测量。这种客观方法的缺乏增加了解释认知质量的主观性,因为感知的认知过载因人而异。
提议的方法
所提出的方法是一种经验方法,使用一个简单的评价标准来定量计算认知超载。随之而来的经验认知超载因子(COF)是一个介于 0 和 1 之间的数值。
评价标准
量规是一种评估和评分工具,它定义了具体的评估标准、衡量达到评估标准的程度的标记以及对衡量标记进行分级的评分尺度。量规是使用特定的和高度相关的评估标准来评估感兴趣的对象的可靠工具。因此,量规中的评价标准对量规使用的有效性至关重要。
开发的量规包含 9 个跨行的评估标准和 5 个跨列的测量标记,形成一个 9x5 的评估网格。五列标记衡量评估标准是否符合预期——未达到、未达到预期、接近预期、达到预期和超出预期。每个评估标记都有一个 4 分制的分数。0 分为“未解决”,1 分为“未达到预期”,2 分为“接近预期”,3 分为“达到预期”,4 分为“超出预期”。应用于每个评估标准的每个测量标记的解释在标题的相应单元格中定义,如图 1 所示。

图 1:评估数据可视化认知过载因素的规则
评定标准
如图 1 第一栏所示,九个主要标准有助于评估视觉感知格式塔原则的数据可视化输出:相似性、接近性、闭合性、封闭性、连接性、连续性、视觉中杂乱的存在、视觉清晰呈现预期信息的能力以及视觉表现的设计美学。
格式塔相似性原则标准
格式塔相似性原则标准评估可视化是否使用相似的大小、形状和颜色对相似的元素进行分组。类似元素的这种分组有助于人类容易地识别视觉表示中的相似性和差异。
格式塔邻近原则标准
格式塔邻近原则标准评估视觉是否呈现彼此更靠近的相同组元素。这种接近有助于人类将更接近的元素归类为属于同一组。
格式塔封闭原则标准
格式塔封闭原则标准评估视觉的元素是否使用完整的形状呈现。过去的研究表明,人类更喜欢完整的形状来理解一个元素。如果该元素使用不完整的形状来表示,人类倾向于填补空白来解释该元素,从而导致意想不到的认知超载。
格式塔封闭原则准则
格式塔封闭原则准则评估视觉是否呈现封闭或绑定在一起的同一组元素。这种外壳有助于人类容易地感知属于一起的元素,从而简化认知理解。
格式塔连接原则标准
格式塔连接原则标准评估视觉是否呈现连接在一起的同一组元素。这种联系帮助人类观众容易地将属于同一组的元素分类。
格式塔连续性原则标准
格式塔连续性原则标准评估当视觉中的元素看起来是其他元素的延续时,它们是否形成完整的图像。这种可视化帮助人类观众感知由连续元素形成的完整图像。任何不连续性都会导致人们试图完成图像,从而导致不必要的认知超载。
杂乱存在标准
该标准评估视觉是否过于杂乱。由于不必要的图表边框、网格线、数据标签、标记、嘈杂的轴标签、不一致的配色方案等,视觉效果会变得混乱。杂乱会导致认知超载,分散观众对主要信息的注意力。
清晰传达预期信息
这一关键标准评估可视化是否通过突出重要内容、减少噪音和消除干扰以及创建清晰的信息流层级来呈现数据。
此外,视觉效果应该强调主要信息主题的“谁”、“什么”和“如何”属性。“谁”属性有助于在过程的早期识别目标受众,并有助于理解数据可视化应该解决的那些目标受众的特定需求。目标受众越窄,叙述的数据故事就越具体,越有说服力。“什么”属性有助于理解目标受众期望什么信息,以及通过可视化数据期望目标受众做出什么行动和决策。这样的“是什么”方面有助于实现可视化数据的真正目的,符合目标受众的期望。“如何”属性有助于解决如何处理和转换可用数据,以通过数据可视化突出显示受众的兴趣点。
美学设计的视觉效果可以对目标受众产生愉悦的影响,减少认知负荷。
认知超载因子(COF)
数据可视化的 COF 可以基于其在标题上的安全总分来计算。任何数据可视化可以获得的最高分数是 36。为 COF 提出的经验公式为:
COF = 1 - (Secured Total Score/Maximum Score) => 1 - (S/36)
接近 0 的 COF 意味着可以忽略不计的认知超载。COF 接近 1 表示认知负荷最严重。大约 0.3 的 COF 可能是最佳的,因为认知过载可能无法完全消除。需要进一步的实验评估来确定更精确的最佳 COF。
COF 评估者
建议由特定的目标受众或消费者来评估数据可视化的 COF。目标受众更适合,因为他们知道可视化的“谁”、“什么”和“如何”属性以及整体可视化上下文。来自目标群体的个体可以独立地评估 COF,并且所有评估值的平均值可以被认为是估计的 COF。
示例评估
图 2 显示了一个数据可视化输出的例子,比较了伊利诺伊州芝加哥的月温度和年温度。可视化有两个部分。第一部分是一个气泡图,显示 2019 年平均气温最高的月份。第二部分是一张热图,显示了 2009 年至 2019 年期间气温最高的月份。

图 2:伊利诺伊州芝加哥的月气温和年气温对比
显然,示例可视化的目标受众是对芝加哥的历史气温特别感兴趣的数据科学家。它们拥有这种温度信息的特定上下文,因此非常适合更有效地评估示例数据可视化的 COF。
使用标题的评估
属于目标受众群体的数据科学家可以使用标题对上述聚类可视化进行评分,如图 3 所示。这样的数据科学家评估的假设总分是 23。同样,其他数据科学家的假设分数可能是 18、27、21、24 和 20。平均分 22.17。因此,使用经验公式估计的 COF 是 0.3843,这意味着认知超负荷接近最优,并且可以增强可视化以进一步改善 COF。

图 3:医学数据科学家的假设评估
推荐
本文中建议的 COF 评价标准使用了视觉感知的格式塔原理、视觉中杂乱的存在、视觉清晰呈现预期信息的能力以及视觉表现的设计美学。该标准形成了一个简单的 9x5 评估网格,每行有九个评估标准,每列有五个测量标记。评分方法有意保持简单,以帮助构建 COF 量化模型和演示概念。建议感兴趣的读者使用更多更好的标准进一步扩展这个模型,并根据特定的需求对其进行定制。
参考
数据分析报告生命周期的最佳实践:报告构建和数据验证的质量。(2018).《AHIMA 日报》, 89 (9),40–45。
m .卡特(2013 年)。设计科学演示:数字、论文、幻灯片、海报等的视觉指南。爱思唯尔科技&科技。
北卡罗来纳州纳弗利克(2015 年)。用数据讲故事:商业专家数据可视化指南。威利。
维特尔,T. R. (2017)。描述性统计:报告五个基本问题的答案:谁,什么,为什么,何时,何地,第六,那又怎样?美国国家医学图书馆, 125 (5),1797–1802。
使用群组分析测量用户参与度
原文:https://towardsdatascience.com/measuring-user-engagement-with-cohort-analysis-96c46b386862
SQL 用于举重,熊猫用于深潜

威廉·沃比在 Unsplash 上的照片
公司需要能够测量用户参与度和保留度,以量化各种信息,例如(I)它如何随时间演变,(ii)它对外部事件和季节性的敏感程度,以及(iii)不同的用户群如何参与他们的产品。该产品通常是一个应用程序,用户可以与之交互,以获得一些服务。因此,参与度可以简单地定义为用户在应用上的活动。
1-简介
在这篇文章中,我将向你展示一个如何用“群组分析”调查用户参与度的例子。
"一群人是指在一定时期内具有共同特征的一群人."
为了让事情更简单,让我们假设这个共同特征是用户第一次打开公司应用的那一周。这会将该用户的状态设置为活动用户。如果用户下周没有打开应用程序,那么它的状态将是不活动的。在此基础上,可以为活动和非活动用户提供以下状态定义,这将有助于我们跟踪不同的群组。

作者图片
按照这个逻辑,我们可以看到,在任何给定的一周,
- 保留用户、重新获得用户和流失用户的总数等于前几周至该周所有新用户的累计总和。
- 留存率为(留存用户总数)/(留存用户总数+流失用户总数)
步骤
双流程
让我们生成一个虚拟用户活动数据库,并开始处理它。
2.a)虚拟数据库
通常,公司都有自己的数据库来收集用户活动数据。然而,为了便于说明,我们将生成自己的用户活动表(使用 pandas 和 NumPy)并将其推送到 SQL 数据库,这样我们就可以使用 SQL 查询来完成繁重的工作。我们的 activity 表将有 3 个基本列:(i) user_id,(ii) activity_day,以及(iii)在那一天为该用户打开的 app_opens。
from matplotlib import pyplot as plt
import numpy as np
import pandas as pd
import string
import seaborn as snspd.set_option('display.max_rows', 400)
pd.set_option('display.max_columns', 200)
pd.set_option('display.max_colwidth', 150)# ['A', 'B', ..., 'AA', 'AB', ..., 'ZY', 'ZZ']
ALPHABETICAL_LIST = (list(string.ascii_uppercase) +
[letter1+letter2 for letter1 in string.ascii_uppercase
for letter2 in string.ascii_uppercase])%matplotlib inline
plt.rcParams["figure.figsize"] = (40,20)
虚拟用户活动表将是
userid_list = [letter+'{:02d}'.format(i) for letter in ALPHABETICAL_LIST
for i in range(1,21)][:14000]np.random.seed(0)app_opens = np.concatenate((
np.random.choice(range(10,13), size=len(userid_list)),
np.random.choice(range(8,11), size=len(userid_list)),
))user_ids = np.concatenate((
userid_list,
userid_list,
))app_activations = np.datetime64('2020-01-06') + np.random.choice(np.arange(0, 180), len(userid_list))activity_days = np.concatenate((
app_activations,
app_activations + 7,
))random_user_ids = [x for x in userid_list]for days_passed in range(14,106, 7):
user_id_sample = int(np.floor(len(random_user_ids) * 0.98))if user_id_sample!=0:
random_user_ids = [random_user_ids[x] for x in
np.random.choice(range(len(random_user_ids)),size=user_id_sample,replace=False)]user_ids = np.concatenate((user_ids, random_user_ids))
app_opens = np.concatenate((app_opens,
np.random.choice(range(3,7),
size=user_id_sample,
p=[0.2, 0.3, 0.3, 0.2]
),
))
activity_days = np.concatenate((activity_days,
[app_activations[userid_list.index(x)]+days_passed for x in random_user_ids]
))df_user_activity = pd.DataFrame(zip(user_ids, activity_days, app_opens),
columns=['user_id', 'activity_day', 'app_opens'])df_user_activity = df_user_activity.groupby(['user_id'],
group_keys=False
).apply(lambda df: df.sample(len(df)-2)
).sort_index().reset_index(drop=True)df_user_activity['count'] = df_user_activity.groupby(['user_id'])['activity_day'].transform(len)df_user_activity.loc[df_user_activity['count']==1, 'app_opens'] = np.minimum(5, df_user_activity['app_opens'])df_user_activity = df_user_activity.drop(['count'], axis=1)df_user_activity

作者图片
让我们将这个表写入 SQL 数据库。
from sqlalchemy import create_engine
import osSQL_ENGINE = create_engine('sqlite:///'+ 'medium_project.db',
connect_args={'check_same_thread': False}).connect()df_user_activity.to_sql(name='user_activity',
index=False,
con=SQL_ENGINE,
if_exists='replace')
2.b) SQL 查询
下面的查询帮助我实现了我想要进行队列分析的观点。对于上述时间段(42 周),我创建了一个表,显示 user_id、week 和 weekly_flags(比如 new、retained、recreated 和 churned)。该表有 13,720 个唯一的 user _ ids * 42 周= 576,240 行。
在查询上,
我创建了一个周周期表来连接 user_activity_daily 数据,为每个用户带来缺失的周。我的目标是实现一个表格视图,其中,对于每个用户 id,我们有 42 周的行和他们每周打开的应用程序总数。我首先将每个活动日转化为一年一周的视图,为用户应用分组(应用打开总数)。后来,我将它加入到周期表中,对于不相关的周,我将 app_opens 转换为 0,最后,我计算生命周期(累计)、前一周(滞后)和前一生命周期(滞后累计)weekly_app_opens,以分配新的、保留的、活动的、搅动的、重新获得的标志。注意:row_number()列将重复相同的 daily_activity 行 42 次。
按作者对用户活动的 sql 查询
在我将这个表保存为 CSV 之后,我返回了 Pandas 用于我的剩余分析。
虽然这不在本文的讨论范围之内,但是如果需要的话,这个表可以进一步与其他 SQL 表连接,以便将与用户段相关的信息显示在视图中。

作者图片
2.c)回归熊猫
下表显示了每周新增、保留、活跃、恢复和流失的用户总数。
df_overall_summary = df_weekly_activity.groupby(
['year_week']
)['new_flag',
'retained_flag',
'active_flag',
'churned_flag',
'regained_flag'].sum().reset_index()df_overall_summary

作者图片
例如,如果我们查看上述数据框的前 3 行,我们可以看到,在 2020 年的第一周,435 名用户首次打开了该应用程序。接下来的一周,在这 435 人中,除了 523 名新加入者之外,还有 72 人(在那一周)和 363 人仍然活跃。
现在我们已经有了基于用户的每周活动视图,让我们进一步分析和可视化群组。
2.d)表格转换
我将通过将同一周的订阅者分组来查看所有用户的活动旅程。我的目标是确定第 n 周之后每个人的活动量是否下降。我们将在第 X 周(100%活跃)、第 X+1 周、第 X+2 周、第 X+3 周、第 X+4 周等时间段调查在第 X 周拥有新标志的用户的平均活跃用户数。
df_weekly_activity['new_flag_cumsum'] = df_weekly_activity.groupby(['user_id'])['new_flag'].cumsum()# Filterin out the rows where the user didn't started its journey
# (they weren't even a new user yet).df_cohort_prep = df_weekly_activity[df_weekly_activity['new_flag_cumsum']>0].reset_index(drop=True)
df_cohort_prep = df_cohort_prep.merge(
df_cohort_prep[df_cohort_prep['new_flag']==1][['user_id', 'year_week']].rename(columns={'year_week': 'joined_on'}),
how='left', on=['user_id']
)df_cohort['Nth_Week'] = (df_cohort['year_week'].str[:4].astype(int) * 52 +
df_cohort['joined_on'].str[:4].astype(int) * 52 * -1 +
df_cohort['year_week'].str[-2:].astype(int) +
df_cohort['joined_on'].str[-2:].astype(int) * -1
+ 1
)
2.e)时间群组
第一周总是 100%,因为这时候用户是公司的新用户。
df_heatmap = pd.pivot_table(
df_cohort[df_cohort['Nth_Week']<20],
index=['joined_on'],
columns=['Nth_Week'],
values=['active_flag'],
aggfunc=lambda x: np.sum(x) / len(x)
)df_heatmap.loc[''] = np.nandf_heatmap = df_heatmap.sort_index(axis=0)df_heatmap.loc[' '] = np.nanfig2 = plt.figure(figsize=(24,16))ax1=sns.heatmap(df_heatmap.values,
xticklabels=[b for a,b in df_heatmap.columns],
yticklabels=df_heatmap.index.tolist(),
vmin=df_heatmap.min(axis=1).min(),
vmax=df_heatmap.max(axis=1).max(),
annot=True,
fmt=".2%",
cmap='YlGnBu',
linewidths=.005,
linecolor='#029386',
cbar=False,
)ax1.set_title('Active Users %')plt.subplots_adjust(hspace=0.35, wspace=0.2)ax1.set_yticklabels(ax1.get_yticklabels(), rotation=0)ax1.set_xlabel('Nth Week at [Company X]')ax1.set_ylabel('Joined On Week')fig2.savefig("Weekly Active Users % Medium.png", bbox_inches = 'tight')

作者图片
可以看到,用户活跃度在第 2 周下降到 80–85 %,并继续下降,对于许多群组来说,活跃度在第 16 周下降到 0%。
df_heatmap = pd.pivot_table(
df_cohort[df_cohort['Nth_Week']<20],
index=['joined_on'],
columns=['Nth_Week'],
values=['weekly_app_opens'],
aggfunc=lambda x: np.mean(x)
)df_heatmap.loc[''] = np.nandf_heatmap = df_heatmap.sort_index(axis=0)df_heatmap.loc[' '] = np.nanfig2 = plt.figure(figsize=(24,16))ax1=sns.heatmap(df_heatmap.values,
xticklabels=[b for a,b in df_heatmap.columns],
yticklabels=df_heatmap.index.tolist(),
vmin=df_heatmap.min(axis=1).min(),
vmax=df_heatmap.max(axis=1).max(),
annot=True,
#fmt=".2%",
cmap='YlGnBu',
linewidths=.005,
linecolor='#029386',
cbar=False,
)ax1.set_title('App Opens Avg.')plt.subplots_adjust(hspace=0.35, wspace=0.2)ax1.set_yticklabels(ax1.get_yticklabels(), rotation=0)ax1.set_xlabel('Nth Week at [Company X]')ax1.set_ylabel('Joined On Week')fig2.savefig("Weekly App Opens Avg Medium.png", bbox_inches = 'tight')
平均而言,用户在第一周打开应用程序 10-11 次,第二周打开 6-7 次。在完全搅拌之前,他们每周打开应用程序 2-2.5 次。

作者图片
2.f)行为群组
找到用户永远无法恢复的行索引。
df_churned_forever_index = df_cohort[
df_cohort['active_flag'].eq(1)[::-1].astype(int).groupby(df_cohort['user_id']).cumsum().eq(0).sort_index(axis=0)].groupby('user_id', sort=False
)['Nth_Week'].idxmin().reset_index().rename(columns={'Nth_Week': 'index_value'})
仅保留用户之间的行是新的,并且在我们的时间窗口的剩余时间内会发生变化。
df_cohort = df_cohort.merge(
df_churned_forever_index,
how='left', on=['user_id']
)df_cohort_churned_forever = df_cohort[df_cohort.index<=df_cohort['index_value']].reset_index(drop=True)
对于所有流失的用户,有多少是在 2 周、3 周、… N 周后流失的,他们的每周应用打开次数是多少?
df_users_churning_week = df_cohort_churned_forever.groupby(
['user_id']
)['Nth_Week'].max().reset_index()list_to_append_leaving_users = []for Nth_WEEK in range(2,15):
list_of_users_leaving_n_week = df_users_churning_week[(df_users_churning_week['Nth_Week']==Nth_WEEK)]['user_id'].tolist()list_to_append_leaving_users.append(df_cohort_churned_forever[
df_cohort_churned_forever['user_id'].isin(list_of_users_leaving_n_week)
].groupby(['Nth_Week'])['weekly_app_opens'].mean().reset_index().rename(
columns={'weekly_app_opens': '{} Users Leaving in Week {}'.format(
len(list_of_users_leaving_n_week), Nth_WEEK)}).set_index(['Nth_Week']))
df_churned_users_weekly_app_opens = pd.concat(list_to_append_leaving_users, sort=False, axis=1)df_churned_users_weekly_app_opens.reset_index()

作者图片
如上图所示,在第二周流失且从未恢复的用户是那些在第一周平均打开应用 4.7 次的用户。

作者图片
3-结论
如果不增加用户的活跃度,公司也会想保持用户的活跃度。此外,如果有任何用户参与度可能下降的危险信号,他们希望通过观察趋势来了解这一点。在这篇文章中,我分享了一些 Python 和 SQL 代码,来演示如何从用户活动表中分析保留。
例如,创建了多个群组:
一次性群组(第 2.e 节,根据一段时间内的活动对用户进行分组)
2-行为群组(第 2.f 节,找到被搅得一蹶不振的用户)
媒体混合建模:如何用 Python & LightweightMMM 测量广告的效果
媒体混合建模、实现和实用技巧

安德里亚斯·米在 Unsplash 上的照片
TLDR:
媒体组合建模,也称为营销组合建模(MMM),是一种帮助广告商量化几项营销投资对销售的影响的技术。LightweightMMM 是考虑到媒体饱和度和广告库存的 MMM 的 python 库。然而,当你尝试 MMM 的时候,你可能需要反复试验。对于实际的见解和行动,不断尝试更好的数据,做出更好的模型,做更好的实验。
1.介绍
当你听到广告这个词时,你会想到什么?
让我给你举几个例子。电视广告是一种常见的方法。社交媒体广告比如当你在社交媒体平台上查看朋友的帖子或视频时,你可能会看到许多广告。另外,如果你用谷歌搜索某样东西,你通常会在搜索结果的顶部看到一些广告。此外,公共汽车、机场、火车、出租车、建筑物上的广告,即所谓的户外广告也相当普遍。

图像由作者,由 unsplash.com 缩略图
长期以来,媒体优化一直是一项挑战。你们中的一些人可能熟悉营销先驱约翰·沃纳梅克。据说他说过,“我花在广告上的钱有一半都浪费了;问题是我不知道是哪一半。”

作者图片,维基百科缩略图
解决这个问题的统计方法被称为媒体组合建模或营销组合建模。一般简称为 MMM。MMM 的目的是了解每种营销媒体对销售的贡献有多大,每种媒体应该花多少钱。
几十年来,在饮料、消费品、汽车和时尚行业拥有巨额广告预算的公司一直致力于改进 MMM。此外,广告技术公司,如谷歌和 Meta,这些天一直在积极关注 MMM。
2。什么是 MMM?
MMM 是统计模型,有助于量化几种营销投入对销售的影响。
粗略来说,有三个目标。
- 第一个目标是“理解和衡量投资回报(ROI)”。比如,模型会告诉你你去年在电视上的投资回报率。
- 第二个目标是模拟。例如,有了这个,你可以回答一个商业问题,比如“如果明年在电视上花更多或更少的钱,我们的销售额会是多少?如果第二年花在电视上的钱多了或少了,你就能知道你的销售额会是多少。
- 第三是优化媒体预算。这一步将帮助你优化预算分配,这将有助于最大限度地提高销售额。

媒体优化的主要挑战
你可能想知道为什么衡量投资回报率如此困难,或者为什么不只是检查每个媒体发布的报告的投资回报率。
这些都是好问题。但是现实要复杂一点。
第一个原因是,最终用户有多个媒体接触点,媒体渠道的影响相互交织。
其次,如今跟踪精度并不总是正确的。线下媒体渠道影响力难以追踪。例如,对于报纸或杂志等印刷媒体,我们无法跟踪有多少人实际上看到了这种媒体形式的广告。更糟糕的是,即使在数字世界,隐私法规,如 GDPR 和苹果公司的 IDFA 贬低已经影响了跟踪的准确性。
第三,被称为提升测试的随机实验是不切实际的。
回答因果问题的黄金标准是进行随机实验,将人群随机分为测试组和对照组,测试组没有广告。然而,这是不切实际的,因为公司不喜欢长期限制广告,因为这可能导致失去机会。

作者图片
3。数据准备
3.1 输入数据
我们使用时间序列数据,不使用任何隐私相关的数据。如你所见,我们有一个周、销售、媒体支出和其他数据栏。

作者图片
3.2 需要哪种数据?
第一部分是最重要的指标,也就是你企业的 KPI,这将是一个因变量。如果你是零售商或者制造商,销售是常见的选择。然而,如果你是一家移动应用公司,安装的应用数量就是 KPI。其次,解释变量是影响销售的潜在因素。媒体数据是必需的,因为我们希望优化这些分配。价格、促销或产品分销等非媒体营销数据也会影响销售。季节性、节假日、天气或宏观经济数据等外部因素对于提高模型的准确性也很重要。

作者图片
3.3 数据的粒度应该有多细?
就时间而言,mmm 通常需要两到三年的周级数据。但是,如果您没有那么多数据,日常数据也是可以接受的,但是在这种情况下,您需要更加小心地检查异常值。接下来是业务粒度。常见的方法是收集品牌或业务单位级别的数据。比如宝洁的护发类有潘婷、海飞丝、草本精华。每个品牌团队都有不同的销售、营销和媒体策略。确保根据产品线、组织和决策过程来确定数据粒度。在查看媒体支出数据时,一个常见的粒度是媒体渠道级别,如电视、印刷、OOH 和数字。但这取决于你在每种媒体上的花费。例如,如果你在数字广告上花了很多钱,最好将数字渠道细分为更具体的群体,如谷歌搜索广告、谷歌展示广告、YouTube 广告、脸书广告等。,因为谷歌搜索广告和 YouTube 广告的漏斗和作用不同。
4。建模
4.1 简单的传统方法——线性回归
首先,让我们从简单建模开始考虑。
对观测数据进行线性回归是一种传统的常用方法。

作者图片
这里,销售额是客观变量,媒体支出因素和控制因素是解释变量。这些系数意味着对销售的影响。所以,β_ m 是媒体变量的系数,β_ c 是控制变量的系数,如季节性或价格变化。这种方法最显著的优点是每个人都可以快速运行它,因为即使是 Excel 也有回归功能。还有,包括非技术高管在内的所有人都很容易直观地理解结果。然而,这种方法并不基于被营销行业广泛接受的关键营销原则。
4.2 广告的两个原则
有两个广告原则需要考虑:饱和度和广告存量。

作者图片,维基百科图表
饱和:随着支出的增加,一个媒体渠道的广告效果会下降。让我换一种方式说:你在一个媒体渠道广告上花的钱越多,效果越差。饱和度也称为形状效应。
广告库存:广告对销售的影响可能会滞后于最初的曝光,并延续几周,因为消费者通常会记住广告很长一段时间,但他们有时会推迟行动。有几个原因:如果消费者已经有库存,他们不会立即购买这些商品。或者,如果他们计划购买昂贵的物品,如电脑、家具或电视,他们可能需要几天到几周的时间来考虑购买这些物品。这些例子导致了遗留效应。
【Google 研究员金等提出的 4.3 模型
谷歌的研究人员在 2017 年提出了一种反映这两个特征的方法。下面的公式是反映遗留效应和 ad 饱和度的最终模型。

作者图片
基本方法与我之前分享的简单模型相同。销售可以分解为基线销售、媒体因素、控制因素和白噪音。而在这个公式中,系数β代表了各个因素的影响。这里的变化是将两个转换函数应用于媒体支出的时间序列:饱和度和广告存量函数。
4.4 有用的 MMM 库(轻量级 MMM vs Robyn)
在这里,让我介绍两个伟大的 OSS 库,它们将帮助你尝试 MMM : LightweightMMM ,一个主要由 Google 开发者开发的基于 Python 的库,和 Robyn ,一个由 Meta 开发的基于 R 的库。
LightweitMMM 使用 Numpyro 和 JAX 进行概率编程,这使得建模过程快得多。在标准方法之上,LightweightMMM 提供了一种分层方法。如果您有州级或地区级的数据,这种基于地理的等级方法可以产生更准确的结果。
而 Robyn 则利用 Meta 的人工智能图书馆生态系统。Nevergrad 用于超参数优化,Prophet 用于处理时间序列数据。
5。样本代码
让我向您展示它实际上是如何使用 LightweightMMM 的。完整的代码可以在下面我的 Github 上找到。我的示例代码基于 lightweight_mmm 的官方演示脚本。
首先,让我们使用 pip 命令安装 lightweight_mmm 库。大约需要 1-2 分钟。如果你得到错误“重启运行时”,你需要点击“重启运行时”按钮。
!pip install --upgrade git+https://github.com/google/lightweight_mmm.git
另外,让我们导入一些库,如 JAX,numpryro,以及库的必要模块。
# Import jax.numpy and any other library we might need.
import jax.numpy as jnp
import numpyro
# Import the relevant modules of the library
from lightweight_mmm import lightweight_mmm
from lightweight_mmm import optimize_media
from lightweight_mmm import plot
from lightweight_mmm import preprocessing
from lightweight_mmm import utils
接下来,我们来准备数据。官方示例脚本使用一个由库函数生成的模拟数据集来创建虚拟数据。然而,在这次会议中,我将使用更真实的数据。我在一个 GitHub 仓库上找到了一个很好的数据集:sibylhe/mmm_stan。我不确定这个数据集是真实的、虚拟的还是模拟的数据,但对我来说,它看起来比我在互联网上找到的任何其他数据都更真实。
import pandas as pd
# I am not sure whether this data set is real, dummy, or simulated data, but for me, it looks more realistic than any other data I found on the internet.
df = pd.read_csv("https://raw.githubusercontent.com/sibylhe/mmm_stan/main/data.csv")
# 1\. media variables
# media spending (Simplified media channel for demo)
mdsp_cols=[col for col in df.columns if 'mdsp_' in col and col !='mdsp_viddig' and col != 'mdsp_auddig' and col != 'mdsp_sem']
# 2\. control variables
# holiday variables
hldy_cols = [col for col in df.columns if 'hldy_' in col]
# seasonality variables
seas_cols = [col for col in df.columns if 'seas_' in col]
control_vars = hldy_cols + seas_cols
# 3\. sales variables
sales_cols =['sales']
df_main = df[['wk_strt_dt']+sales_cols+mdsp_cols+control_vars]
df_main = df_main.rename(columns={'mdsp_dm': 'Direct Mail', 'mdsp_inst': 'Insert', 'mdsp_nsp': 'Newspaper', 'mdsp_audtr': 'Radio', 'mdsp_vidtr': 'TV', 'mdsp_so': 'Social Media', 'mdsp_on': 'Online Display'})
mdsp_cols = ["Direct Mail","Insert", "Newspaper", "Radio", "TV", "Social Media", "Online Display"]
让我们快速浏览一下。该数据包含四年每周级别的数据记录。为简单起见,我使用七个媒体渠道的媒体支出数据,以及假日和季节信息的控制变量。
df_main.head()

接下来,我将对数据进行预处理。我们将数据集分为训练和测试两部分。在这种情况下,我只留下最后 24 周进行测试。
SEED = 105
data_size = len(df_main)
n_media_channels = len(mdsp_cols)
n_extra_features = len(control_vars)
media_data = df_main[mdsp_cols].to_numpy()
extra_features = df_main[control_vars].to_numpy()
target = df_main['sales'].to_numpy()
costs = df_main[mdsp_cols].sum().to_numpy()
# Split and scale data.
test_data_period_size = 24
split_point = data_size - test_data_period_size
# Media data
media_data_train = media_data[:split_point, ...]
media_data_test = media_data[split_point:, ...]
# Extra features
extra_features_train = extra_features[:split_point, ...]
extra_features_test = extra_features[split_point:, ...]
# Target
target_train = target[:split_point]
此外,这个库提供了一个用于预处理的 CustomScaler 函数。在此示例代码中,我们将媒体支出数据、额外功能数据和目标数据按其平均值进行划分,以确保结果的平均值为 1。这允许模型不知道输入的规模。
media_scaler = preprocessing.CustomScaler(divide_operation=jnp.mean)
extra_features_scaler = preprocessing.CustomScaler(divide_operation=jnp.mean)
target_scaler = preprocessing.CustomScaler(divide_operation=jnp.mean)
cost_scaler = preprocessing.CustomScaler(divide_operation=jnp.mean, multiply_by=0.15)
media_data_train = media_scaler.fit_transform(media_data_train)
extra_features_train = extra_features_scaler.fit_transform(extra_features_train)
target_train = target_scaler.fit_transform(target_train)
costs = cost_scaler.fit_transform(costs)p
下一步是训练。我们可以从 3 个选项中为建模选择广告库存函数:Hill-广告库存、广告库存和结转。通常建议比较所有三种方法,并使用最有效的方法。
mmm = lightweight_mmm.LightweightMMM(model_name="hill_adstock")
mmm.fit( media=media_data_train, media_prior=costs, target=target_train, extra_features=extra_features_train, number_warmup=number_warmup, number_samples=number_samples, media_names = mdsp_cols, seed=SEED)
一旦训练完成,您可以检查您的跟踪的摘要:这里重要的一点是检查所有参数的 r hat 值是否小于 1.1。这是运行贝叶斯建模时的一个检查点。
mmm.print_summary()

作者图片
我们可以看到媒体效应的后验分布。
plot.plot_media_channel_posteriors(media_mix_model=mmm, channel_names=mdsp_cols)
现在,让我们做一个合适的检查。还可以使用 plot_model_fit 函数来检查模型与训练数据的拟合度。图表中显示了 r 平方和 MAPE,即平均绝对百分比误差。在这个例子中,R2 是 0.9,MAPE 是 23%。一般来说,如果超过 0.8,R2 就被认为是好的。此外,MAPE 的目标是 20%或更低。
plot.plot_model_fit(mmm, target_scaler=target_scaler)

作者图片
这是预测结果的可视化。R2 是 0.62,MAPE 是 23%。老实说,这里的 R2 和 MAPE 价值观并不理想。然而,我没有任何额外的数据,而且——我甚至不确定——这个数据集是真实的还是虚假的。也就是说,我仍然会使用这个数据集和模型来向你展示我的见解。稍后,我将详细讨论如何改进这个模型。
plot.plot_out_of_sample_model_fit(out_of_sample_predictions=new_predictions,
out_of_sample_target=target_scaler.transform(target[split_point:]))

作者图片
结果
通过使用这个函数,我们可以快速地可视化估计的媒体和基线贡献。下图显示,大约 70%的销售额是基线销售额,用蓝色区域表示。其他颜色显示媒体对剩余销售额的贡献。
media_contribution, roi_hat = mmm.get_posterior_metrics(target_scaler=target_scaler, cost_scaler=cost_scaler)
plot.plot_media_baseline_contribution_area_plot(media_mix_model=mmm,
target_scaler=target_scaler,
fig_size=(30,10),
channel_names = mdsp_cols
)

作者图片
plot.plot_bars_media_metrics(metric=roi_hat, metric_name="ROI hat", channel_names=mdsp_cols)
此图显示了每个媒体渠道的预计投资回报率。每个条形代表媒体的投资回报率有多高。在这种情况下,电视和网络展示比其他媒体更有效率。

作者图片
我们可以将优化的媒体预算分配可视化。该图显示了以前的预算分配和优化的预算分配。在这种情况下,应该减少直邮和广播,增加其他媒体。
plot.plot_pre_post_budget_allocation_comparison(media_mix_model=mmm,
kpi_with_optim=solution['fun'],
kpi_without_optim=kpi_without_optim,
optimal_buget_allocation=optimal_buget_allocation,
previous_budget_allocation=previous_budget_allocation,
figure_size=(10,10),
channel_names = mdsp_cols)

作者图片
6。如何提高模型的准确性?
为了更好地洞察和行动,需要一个量身定制的模型,因为没有“一刀切”的模型,因为每个企业的情况都不同。
那么,我们如何提高模型的准确性以获得更好的洞察力和行动呢?
更好的数据:你需要根据你的业务选择影响你销售的控制变量。一般来说,销售额随着促销、价格变化和折扣而波动。缺货信息对销售也有很大影响。谷歌研究人员发现,相关查询的搜索量可以用于 MMM,以适当控制付费搜索广告的影响。
如果你在一个特定的媒体渠道上花费很多,最好把这个媒体渠道分解成更具体的群体。
更好的模型:下一个建议是改进建模。当然,超参数调整很重要。除此之外,尝试地理级别的等级方法是获得更高精度的好方法。
更好的实验:第三个建议是与你的营销团队合作,做实际的实验,也就是所谓的提升测试。如前所述,在所有媒体上做随机实验是不现实的。然而,在关键点上的实验对于获得基本事实和改进模型是有用的。Meta 最近发布了 Geo Lift,这是一个开放源码软件解决方案,可用于基于地理的实验。

作者图片
7.结论
让我们总结一些要点。
- MMM 是统计模型,有助于量化几种营销投入对销售的影响。
- 在广告中,饱和度和广告存量是关键原则。可以使用转换函数对它们进行建模。
- 如果您熟悉 Python,LightweightMMM 是很好的第一步。
- 为了更好的洞察和行动,不断尝试更好的数据,做更好的模型,做更好的实验。
感谢您的阅读!如果您有任何问题/建议,欢迎随时在 Linkedin 上联系我!此外,如果你能跟随我学习数据科学,我会很高兴。
8.参考
- 金,杨,王,杨,孙,陈,丁,&克勒,J. (2017)。具有遗留和形状效应的媒体混合建模的贝叶斯方法。谷歌公司
- 陈博士,&佩里博士(2017)。媒体组合建模的挑战和机遇。
- https://github.com/google/lightweight_mmm
- 罗宾:https://github.com/facebookexperimental/Robyn
- 嗯 _ 斯坦:https://github.com/sibylhe/mmm_stan
使用 SQL 的 BigQuery 中的中值、众数和平均顺序值
了解客户的订购习惯,选择最佳策略增加订单价值

为什么使用中位数、众数或平均顺序值?
这些指标可以帮助您了解您的客户如何在【订单级别】进行购买。不同的维度,如你的产品组合的价格范围、获取渠道和位置,都会对这些指标产生影响。你也可以将它们与其他行业进行比较。
这样做的目的是在尝试新的销售策略来增加订单价值之前,充分了解客户的购买行为。
让我们从一个例子开始,假设你销售下列物品:
- 一件 t 恤(15€)
- 一件连帽衫(35€)
- 一顶太阳帽(10€)
您观察到您的平均订单价值( AOV )是 13,75€。这个可能意味着您的客户在每个订单中购买少量商品以及低值产品。
更准确地说,我们必须查看我们的订单的价值分布,并使用额外的指标,如中值或众数、或标准差,以给出一个比平均订单价值更广泛的图景。
使用附加维度
您还可以查看按国家或采购渠道、等细分的订单价值的这些指标(平均值、中值、众数)
目标是发现不同的行为或不同的趋势。也许通过电子邮件渠道获得的客户使用了更多折扣 (导致订单价值降低),而法国客户喜欢你的产品,因此订购了更多数量,或者在每个订单中组合了多个项目(导致订单价值增加)。
使用基准
基准可以用来比较你的公司和其他公司的表现。如果时尚界的基准显示,AOV 60€而你有 13,75€ AOV ,也许你的销售策略可能不会那么有效。
但是,请记住,您的平均订单价值取决于几个方面,如您的定价点、地区/位置、细分市场等等…
一个网站的例子,提供不同行业的 AOV 基准。
https://marketing.dynamicyield.com/benchmarks/average-order-value/
增加订单价值的策略
当我们在这里关注 SQL 时,我推荐阅读这些不同文章中关于你可以使用什么策略来增加你的平均订单价值的内容。
https://www.shopify.com/blog/average-order-value https://medium.com/@Bold_Commerce/9-upselling-techniques-to-explode-your-average-order-value-99b727585b77
让我们计算和分析我们的指标
让我们定义我们的三个指标,并解释我们将使用哪些销售数据。
- 平均值:是客户在一个订单上花费的总金额,是所有订单的平均值。它可以告诉你,你预计客户在一个订单上会花多少钱。
- 中值:是您所有订单的中间值。这意味着 50%的订单值小于或等于中值,50%的订单值大于或等于中值。
- 模式:是所有订单中最频繁出现的值。也许你有一个明星销售产品,你的许多客户都在做同样的订单。
为了计算这些指标,我们使用来自官方 Google 商品商店 的销售数据,我们的表中每行都有一个产品、一个数量、一个收入和相关的订单 id。我们将把这个表称为我们的“基表”。

我们将用于计算指标的基础表(图片由作者提供)
计算每个订单的价值
我们的第一步是获取每个订单的值。在我们的例子中,产品收入一栏已经考虑了购买的数量。这意味着我们只需对每个订单 ID 的产品收入求和,即可获得订单价值。
获取按 order_id 分组的每个订单的总收入
这将简单地返回每个订单产生了多少欧元收入(我们的订单价值)。

显示每个订单收入的两栏表格。(图片由作者提供)
在我们的案例中,我们有大约11,000 个订单,每个订单都有一个价值(以欧元/€计算的收入)。
订单值直方图
当我们知道中位数、平均值和平均数在整个数据集中的位置时,它们就更容易解释了。直方图可以用来说明这一点。
这将导致 Datastudio 中的以下图表,这将帮助我们更好地了解我们的客户是如何购买的。

订单的价值分布和占订单总额的百分比。(图片由作者提供)
浅蓝色条显示了我们每个订单的价值分布。我们可以观察到我们的大部分订单都在 15–50 欧元和之间,也就是说我们有一个超过 100 欧元的非常长的尾巴。
蓝色的实线向我们展示了与订单总数相比有多少订单(在我们的例子中是 11k)。我们在这里看到,我们 80%的订单价值在 0 到 140 欧元之间。这有助于你知道什么时候你看到了一条长长的订单尾巴。
我们没有在图表的右轴上达到 100%,因为我们没有绘制所有的数据点。一些订单产生了巨大的收入(我们确实有从 2 欧元到 47 000 欧元的订单),这可能被认为是异常值。
我们可以使用桶来更好地绘制后面的整个数据集,并查看如何删除异常值。
在我们的案例中,我们将删除所有超过 690 欧元的订单,因为仅代表我们订单的 3% 。
提示 : 要在 SQL 中构建存储桶,并将您的值转换成可读性更好的直方图,您可以使用以下技术。
*TRUNC(order_value,-1)*: 这将返回最近的下 10 个值。
*ROUND(order_value/5,0)*5*: 这将每 5 步返回最近的值。

使用桶的示例(图片由作者提供)
平均订单值
因为我们有一个包含每个订单值的表,所以我们可以简单地使用AVG()聚合函数,如下所示:
是的,就是这么简单!该查询将返回一个数字,表明我们商店的订单平均价值为 93 欧元。

平均订单值(红色垂直线)绘制在我们的直方图上。(图片由作者提供)
订单中值
为了计算中值,我们将使用 PERCENTILE_CONT(order_value, 0.5)函数。
percentile 函数将遍历我们数据集的每一行,并返回order_value列的中值(这就是为什么我们使用 0.5 参数,这意味着 50%的值高于或低于该点)。
在之前绘制的直方图中,我们可能已经使用深蓝色线检查了中值的位置。
在我们的情况下,这个查询返回的值是 52。这表明我们一半的订单价值不到 52 欧元,另一半价值更多。

订单中值(黄色垂直线)绘制在我们的直方图上。(图片由作者提供)
因为这个函数是一个导航函数,所以我们应用一个LIMIT 1来只返回 1 行。
https://cloud.google.com/bigquery/docs/reference/standard-sql/navigation_functions#percentile_cont
模式顺序值
为了获得模式,我们将使用APPROX_TOP_COUNT(order_value,1)功能。这将返回数据集中最常见的值。
我们使用 1 作为参数,所以这个函数只返回一个结果。
我们系列中最常见的价值是 24 欧元196 个订单。这是我们数据集的模式。
请记住,函数APPROX_TOP_COUNT()返回一个数组,而不是一个整数。
视觉上,在我们的直方图上也很容易看到。

模式阶数值(紫色垂直线)绘制在我们的直方图上。(图片由作者提供)
标准偏差
为了完成这篇文章,我们将把最后一条信息添加到直方图中。
标准差衡量数据集相对于其均值的离差,用更简单的术语来说,它将帮助我们了解什么是标准订单值,或者换句话说,当有人说“这是一个大订单”或“这是一个小订单”时,这意味着什么。
在我们的案例中,一份订单平均是 93 欧元。
根据我们的查询,我们的标准偏差是 112 欧元。所有在 93 欧元(我们的平均值)+112 欧元(205 欧元)和-112 欧元(-19 欧元)范围内的订单都被视为标准订单。当然,在这种情况下,我们将边界标在 0,而不是-19。

我们订单值的标准偏差(黑色垂直线)绘制在直方图上。(图片由作者提供)
在我们的案例中,205 欧元以上的订单可以被认为是特殊的,或者是我们可以研究的边缘案例。了解它们对我们的业务意味着什么也很有意思。
此外,来自 0 至 205 欧元的订单占订单的 85%。这意味着这是我们观察到的范围,也是我们可以预期的范围。
https://cloud.google.com/bigquery/docs/reference/standard-sql/statistical_aggregate_functions#stddev
结论
我们确实对客户的购买方式有了更全面的了解。似乎有一些订单(大于 205 欧元)可能是异常值,或者可能与业务的某个特定方面相关(可能是大公司的订单、技术活动的采购等……)。
我们一半的订单仍处于低值(2-54 欧元),如果我们想增加订单价值,查看我们的产品组合和交叉销售/追加销售策略可能会很有意思。
根据模式并查看直方图,我们的大部分订单都在20-24 欧元之间,我们可能会在这些订单中出现畅销商品或低价商品组合。
希望这可以帮助你定义和决定在订单层面应用的策略,比如组合产品、改变运费、折扣优惠(买二送一),甚至更有创意的策略。
认识一下 BERTopic——BERT 的堂兄,学习高级主题建模
原文:https://towardsdatascience.com/meet-bertopic-berts-cousin-for-advanced-topic-modeling-ea5bf0b7faa3
使用转换器进行自动主题发现的综合概述

善良好奇的在 Unsplash 上拍摄的
介绍
我们生活在一个被大量文本信息包围的时代,如调查回复、社交媒体评论、推文等。找到满足个人需求的合适信息是一项挑战,尤其是在处理大量不同的数据时。
多亏了主题建模,一个自然语言处理的时代用来通过将大 未标记文本 数据分组/聚类成主题来高效地分析它们。最近,我遇到了 BERTopic,这是一个用于主题建模的简单而强大的库。所以,我决定试一试,结果很厉害!
伯托皮—凯扎科?
该工具由 Maarten Grootendorst 于 2020 年开发,根据文档:
BERTopic 是一种主题建模技术,它利用了🤗transformers 和 c-TF-IDF 创建密集的集群,允许轻松解释主题,同时在主题描述中保留重要的单词。
从模型创建到各种可视化功能,它都非常简单且易于操作。
BERTopic 的主要组件
下图显示了该算法的三个主要组件:

图 1:算法文档页面上由马腾·格罗腾德斯特制作的 BERTopic 主要部件
- 文件嵌入
第一步是通过利用句子转换器来执行的,这是一个多语言框架,提供了一种简单的方法来为我们的数据语料库中的每个文档生成密集的向量表示。
2。文档聚类
根据之前的嵌入,我们可以使用 UMAP 方法对维度执行聚类,结果被传输到 HDBSCAN 以聚类语义相似的聚类(文档集)。
3。主题表示
这最后一步是通过使用基于类别的 TF-IDF 方法为每个聚类提取最相关的词来执行的,以便获得主题的表示。公式如下所示:

图 2:算法文档页面上 Maarten Grootendorst 的 c-TF-IDF 公式
既然我们已经了解了 BERTopic 是什么以及它是如何工作的,我们现在就可以准备用 Python 实现一个端到端的实现了。
如果您喜欢视频,可以观看本文的视频演示:
关于数据
- 这是澳大利亚广播公司历时八年发布的新闻, 可免费获得 上的 Kaggle 和 不得用于商业用途 。它有两个主要栏目:
- 发布日期 :文章以 yyyyMMdd 格式发布的日期。
- headline _ text:英文标题的文字。 这是主题模型将使用 的信息。
为了能够使 BERTopic 训练更快,我使用了数据集的一个子集,你可以在我的 GitHub 页面上访问它。
头条分布
这里的目标是检查标题的长度分布,以便使用更适合数据集的模型。
数据信息. py

图 3:标题长度分布:最大长度为 15(图片由作者提供)
我们可以看到,最长的标题有 13 个标记,下面是 3 个随机标题:
random _ headlines.py

图 4:数据集中的 3 个随机标题
BERTopic 实现
与本文相关的所有代码都可以通过我的 GitHub 访问。
模型结构
这一步与 BERTopic 模型的实现相关,对默认参数进行如下微调:
BERTopic_model.py
verbose到True:使模型初始化过程不显示消息。paraphrase-MiniLM-L3-v2是性能和速度权衡最好的句子变形金刚模型。min_topic_size设置为 50,默认值为 10。该值越高,分类/主题的数量越少。.fit_transform()在头条数据集上训练 BERTopic 模型。
主题抽取和可视化
一旦模型经过训练,就可以执行许多操作,例如主题提取和可视化。
top_topics.py

图 5:前 5 个“热门”话题信息(作者图片)
上表有 3 个主要列,按照主题大小/数量的降序提供了所有 54 个主题的信息。
- Topic 是主题号,一种标识符,离群值标记为-1,那些对应的主题应该被忽略,因为它们没有带来任何附加值。
- 计数是题目中的字数/术语数。
- 名称是赋予主题的名称。
对于每个主题,我们可以检索出最热门的词及其对应的 c-TF-IDF 得分。分数越高,这个词就越能代表主题。
topic_info.py

图 6:第一个热门话题的术语(top #1)及其 c-TF-IDF 得分(图片由作者提供)
从这个话题中,我们观察到所有的单词对于潜在的话题都是连贯的,这个话题似乎是关于⚽️.足球的
主题可视化
主题可视化有助于更深入地了解每个主题。BERTopic 提供了几种可视化的可能性,例如术语可视化、主题间距离图、主题层次聚类等等,我们的重点将放在那些已经被引用的方面。
主题术语
每个话题最相关的词可以用 c-TF-IDF 评分出来的柱状图的形式可视化出来,直观对比话题很有意思。下面是主题 6 主题的相应可视化。
主题词得分(按作者)
话题 1 的热门词汇是男人,被控谋杀入狱,这显然是一个与犯罪有关的话题。同样的分析可以很容易地从每个剩余的主题中得出。横条越长,与主题的相关性越大。
主题间距离图
对于熟悉潜伏狄利克雷分配 LDAvis 库的人来说,如果不是 下面是我关于它的文章 。这个库为用户提供了一个交互式仪表板,显示每个主题对应的单词及其分数。BERTopic 用 visualize_topics()函数做了同样的事情,甚至更进一步,给出了主题之间的距离(越低越相似),所有这些都用一个函数visualize_topics().
主题间距离图(作者)
可视化主题层次结构
正如您在 Interdistance 主题仪表板中看到的,有些主题非常接近。我能想到的一件事是,我如何减少话题的数量?好消息是,这些主题可以分等级,以便选择适当数量的主题。可视化有助于理解它们之间的关系。

图 7:前 30 个主题的层次聚类(图片由作者提供)
通过查看树状图的第一层(第 0 层),我们可以看到具有相同颜色的主题被分组在一起。例如:
- 主题 7(健康、医院、精神)和主题 14(死亡、车祸、死亡)因为相近而被归为一组。
- 主题 6(农民,农场,农民)和 16(牛,羊,牛肉)也必须以同样的方式分组。
- 等等。
所有这些信息可以帮助用户更好地理解为什么主题被认为是相似的。
搜索主题
一旦训练了主题模型,我们就可以使用find_topics函数搜索与输入查询词/术语语义相似的主题。在我们的例子中,我们可以搜索与单词“”相关的前 3 个主题。
topic _ search.py
similar_topics相似主题包含从最相似到最不相似的主题索引。similarity包含降序排列的相似性得分。
show_top_similar_topic.py
我们可以从下面的输出中看到,最相似的主题是显示术语“选举”、“特朗普”、“奥巴马”等。这显然与政治有关。
*Most Similar Topic Info:
[('election', 0.08389822503224101), ('trump', 0.0512571921683764), ('party', 0.034052442556456154), ('donald', 0.03268734381432749), ('obama', 0.030983388040422003), ('liberal', 0.02869493503505037), ('bush', 0.022854654022153992), ('liberals', 0.022814234525823825), ('vote', 0.02273902178387999), ('presidential', 0.02256653331627359)] Similarity Score: 0.7048206256962174*
模型序列化和加载
当您对模型的结果感到满意时,可以使用以下说明保存它以供进一步分析:
模型 _ 序列化. py
结论
我真的很喜欢试验 BERTopic,结果非常令人鼓舞。请不要犹豫,在您的业务案例中尝试一下。然而,重要的是要注意,BERTopic 的结果在运行之间并不一致,这是由于用于降维的 UMAP 的随机性质。所以,在 UMAP 使用 random_state 参数任何随机行为。
在 YouTube上关注我获取更多互动会话!
额外资源
BERTopic 算法
BERTopic FAQ
BERTopic Github
ka ggle 上的数据集
再见🏃🏾
认识 histgradientsboostingclassifier
原文:https://towardsdatascience.com/meet-histgradientboostingclassifier-54a9df60d066
计算机编程语言
更加灵活和可扩展的梯度增强分类器
Scikit-learn 的GradientBoostingClassifier(GBM 从这里开始)是最受欢迎的集成算法之一,在许多数据集上表现良好。histgradientsboostingclassifier(从这里开始的 HGBM),一个基于直方图的 GBM 替代实现,在 v0.21.0 中作为实验估计器引入。从 v1.0.0 开始,这个估计量已经成为一个稳定的估计量。在本帖中,我们将探讨使用 HGBM 相对于 GBM 的两个主要优势。
还有回归等价:histgradientsboostingregressor。然而,由于同样的逻辑适用,为了避免重复,我们将不涉及它。

📦 1.处理丢失的数据
该估计器可以处理缺失数据,因为它内置了对缺失值的支持。让我们来看看实际情况。我们将从导入库开始,并创建一个带有缺失值的样本数据:
import numpy as np
import pandas as pd
from time import perf_counter
pd.options.display.max_columns = 6from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import (GradientBoostingClassifier,
HistGradientBoostingClassifier)
from sklearn.metrics import accuracy_score, roc_auc_score, f1_scoreimport matplotlib.pyplot as plt
import seaborn as sns
sns.set(style='darkgrid', context='talk', palette='rainbow')n = 10**4
X, y = make_classification(n, random_state=42)
X = pd.DataFrame(X, columns=[f'feature{i}' for i in range(X.shape[1])])# Randomly add missing data for all columns
for i, col in enumerate(X.columns):
np.random.seed(i)
X.loc[np.random.choice(range(n), 1000, replace=False), col] = np.nanprint(f"Target shape: {y.shape}")
print(f"Features shape: {X.shape}")
X.head()

作者图片
我们现在将对数据进行分区,并尝试适应 GBM:
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
print("========== Training data ========== ")
print(f"Features: {X_train.shape} | Target:{y_train.shape}")
print("========== Test data ========== ")
print(f"Features: {X_test.shape} | Target:{y_test.shape}")gbm = GradientBoostingClassifier(random_state=42)
gbm.fit(X_train, y_train)
gbm.score(X_test, y_test)

作者图片|部分输出
像大多数 Scikit-learn 的估计器一样,试图用缺失值来拟合模型会触发ValueError: Input contains NaN, infinity or a value too large for dtype('float32')。
现在,让我们看看如果我们使用 HGBM 会发生什么:
hgbm = HistGradientBoostingClassifier(random_state=42)
hgbm.fit(X_train, y_train)
hgbm.score(X_test, y_test)

作者图片
太棒了,这非常有效,因为估计器本身就可以处理缺失数据。这是 HGBM 相对于 GBM 的一个优势。
📊 2.能够很好地适应更大的数据
HGBM 是一种实现速度更快的 GBM,可以很好地适应更大的数据集。让我们来看看这两种估计量如何对不同大小的样本数据进行比较:
n_samples = 10**np.arange(2,7)
tuples = [*zip(np.repeat(n_samples,2), np.tile(['gbm', 'hgbm'], 2))]
summary = pd.DataFrame(
index=pd.MultiIndex.from_tuples(tuples,
names=["n_records", "model"])
)models = [('gbm', GradientBoostingClassifier(random_state=42)),
('hgbm', HistGradientBoostingClassifier(random_state=42))]for n in n_samples:
X, y = make_classification(n, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(
X, y, random_state=42
)
for name, model in models:
start = perf_counter()
model.fit(X_train, y_train)
end = perf_counter()
summary.loc[(n, name), 'fit_time'] = end-startstart = perf_counter()
y_proba = model.predict_proba(X_test)[:,1]
end = perf_counter()
summary.loc[(n, name), 'score_time'] = end-start
summary.loc[(n, name), 'roc_auc'] = roc_auc_score(y_test,
y_proba)
y_pred = np.round(y_proba)
summary.loc[(n, name), 'accuracy'] = accuracy_score(y_test,
y_pred)
summary.loc[(n, name), 'f1'] = f1_score(y_test, y_pred)
summary

作者图片
这里,总记录的 75%用于训练,剩余的 25%用于测试。我们可以看到,随着训练数据的增长,HGBM 的训练时间要快得多。数据越大,HGBM 的速度就越令人印象深刻。HGBM 通过将数据粗化为分箱的要素来实现其惊人的速度。让我们更仔细地看一下摘要:
fig, ax = plt.subplots(2, 1, figsize=(9,6), sharex=True)
sns.lineplot(data=summary['fit_time'].reset_index(),
x='n_records', y='fit_time', hue='model', ax=ax[0])
ax[0].legend(loc='upper right', bbox_to_anchor=(1.3, 1))
sns.lineplot(data=summary['score_time'].reset_index(),
x='n_records', y='score_time', hue='model',
legend=False, ax=ax[1])
ax[1].set_xscale('log')
fig.tight_layout();

作者图片
随着训练样本数量的增加,GBM 的训练时间显著增加,而 h GBM 在更大的数据集上仍然相对较快。两者的得分时间非常接近。
fig, ax = plt.subplots(3, 1, figsize=(9,9), sharex=True)
sns.lineplot(data=summary['roc_auc'].reset_index(),
x='n_records', y='roc_auc', hue='model', ax=ax[0])
ax[0].legend(loc='upper right', bbox_to_anchor=(1.3, 1))
sns.lineplot(data=summary['accuracy'].reset_index(),
x='n_records', y='accuracy', hue='model',
legend=False, ax=ax[1])
sns.lineplot(data=summary['f1'].reset_index(),
x='n_records', y='f1', hue='model',
legend=False, ax=ax[2])
ax[2].set_xscale('log')fig.tight_layout();

作者图片
总的来说,两者之间的预测性能非常相似,尽管当训练数据在 75 和 750 处较小时有一点差异。
正如您现在所知,HGBM 的第二个好处是,与 GBM 相比,它可以很好地扩展大数据集。
这就是这篇文章的全部内容!希望您喜欢了解这个灵活且可扩展的估计器,并乐于进一步探索它。如果您想了解更多,HGBM 也有对分类特性的本地支持。本文档展示了一些关于此功能的优秀示例。

您想访问更多这样的内容吗?媒体会员可以无限制地访问媒体上的任何文章。如果你使用 我的推荐链接 ,成为会员,你的一部分会费会直接去支持我。
感谢您阅读这篇文章。如果你感兴趣,这里有我的一些其他帖子的链接:
◼️️ 从 ML 模型到 ML 管道
◼️️ 解释 scikit-用 SHAP
◼️️ 4 个在 Python 中绘制多个图形的简单技巧
◼️ 美化熊猫数据帧
◼python 中的简单数据可视化你会发现有用的 ️
◼️ 6 个简单技巧
再见🏃 💨
认识 Julia:数据科学的未来
原文:https://towardsdatascience.com/meet-julia-the-future-of-data-science-52414b29ebb
下一个大事件,还是被过分夸大了?

照片由 Fotis Fotopoulos 在 Unsplash 上拍摄
作为一个数据爱好者,你可能听说过 Julia,数据科学的未来编程语言。“有人声称 Julia 将在数据科学领域取代 Python 和 R,因为它在性能、效率和易用性方面提供了显著的优势。
在本文中,我们将探讨 Julia 是什么,它的应用,以及是否值得为数据科学学习这种语言。
朱莉娅是什么?

作者图片
创建 Julia 是为了提供 Python 的简单性、R 的统计能力以及 C 和 C++ 的速度。
Python 是一种高级语言,语法简单,大多数程序员都觉得很容易使用。它允许数据科学家花更少的时间编写代码,专注于模型构建。Python 中的许多库允许用户构建统计算法和执行科学编程。
然而,Python 的一个主要缺点是速度慢。虽然它易于使用并且提供了高层次的抽象,但是这种语言有很高的延迟。
当构建大规模的机器学习应用程序时,Python 往往会慢很多,并且使用该语言运行预测模型可能需要几天时间。
另一方面,像 C++这样的语言提供了快速的执行,但是低级别的抽象使得编写代码不切实际、复杂且耗时。
Julia 通过提供易用性和效率来解决这个问题。
它旨在像 C++一样为用户提供低延迟,这意味着它比 Python 的 Pandas 库快 1.5 到 5 倍,即使没有启用多线程。
此外,与 Python 类似,Julia 是一种通用编程语言,也提供统计功能。它的语法很容易掌握。
因此,Julia 正越来越多地被数据科学家、经济学家、统计学家、科学家和数学家所使用。
朱莉娅的应用
如上所述,Julia 因其简单和快速的性能而被许多专业人士使用。以下是这种语言在现实世界中应用的一些例子:
1.数据科学
Julia 经常被数据科学家用来建立预测模型。该语言提供了机器学习包,如 ScikitLearn.jl 和 Tensorflow.jl ,它们是 Python 中 ScikitLearn 和 Tensorflow 库的包装器。
许多数据科学家更喜欢 Julia 而不是 Python,因为它更快,更容易调试,简单,并且有更好的包管理。
2.Web 开发
Julia 的 Genie.jl 框架类似于 Python 中的 Django 库,可用于全栈 web 开发。
如果您正在寻找创建一个端到端的动态 web 应用程序,但是发现 Python 太慢,并且不想花时间学习更复杂的语言,Julia 是一个很好的选择。
如果您希望构建一个仪表板应用程序来展示可视化,可以使用 Dash.jl ,它相当于 Python 中的 Plotly Dash 库。
3.数据分析
所有数据专业人员都会发现需要从数据中分析和提取有意义的见解。Julia 为用户提供了一系列专门为数据分析和可视化而设计的库。
Julia 有一个名为 DataFrames.jl 的包,可以让你加载和操作表格数据。你可以认为这相当于 Python 中的熊猫。使用多线程,Julia 的速度可以比熊猫快 20 倍。
Julia 还允许用户使用 Plots.jl 可视化数据,这类似于 Python 中的 Matplotlib。
例如,您可以在一行代码中使用 Plots.jl 创建一个简单的散点图:

图片来自 Julia 的文档
你甚至可以在 Julia 中创建这样的动态动画情节:

图片来自 Julia 的文档
您可以在 Julia 中构建的其他一些图表包括热图、条形图、气泡图、小提琴图和点状图。
4.科学计算
Julia 广泛应用于化学、物理、天文学和生物信息学等领域。
这些行业的专业人员经常需要对大量数据进行统计分析,Julia 提供了大量的软件包来帮助他们完成这些工作。
BioSequences.jl 、 Molly.jl 和 QuantumLab.jl 是一些可以用来解决 Julia 中特定领域问题的科学软件包的例子。
根据评论,Julia 的科学图书馆生态系统比 Python 的要广泛得多,并且允许用户解决问题,而不必从头开始编写解决方案。
我和朱莉娅的经历
我学习的第一门编程语言是 Python,这几乎总是我在处理数据科学任务时选择的语言。
我第一次遇到朱莉娅是在解决一个线性规划问题的时候。我不得不构建一个优化器来基于一组约束最小化一个函数。
然后,这个求解器必须自动化,以便我团队的其他人可以使用它,这意味着它必须接受约束和函数作为用户输入。
我在 Python 中找到了几个库来完成这项工作,但对输出的呈现方式并不满意。我使用的软件包效率也很低,所以我向 Julia 寻求替代解决方案。
Optim.jl 库比我用 Python ( Scipy 、 Gekko )试过的任何库都要快得多,并且节省了团队至少 4-5 个小时的时间。
因为我熟悉 Python 语言,所以我仍然用 Python 执行大量的数据分析和模型构建工作流,但是如果我发现 Python 太慢或者再次遇到类似上面的问题,我会切换到 Julia。
所以……数据科学要不要学朱莉娅?
数据科学家、分析师、开发人员和经济学家等专业人士可以在日常工作中使用 Julia。因为这种语言易于使用,速度快,并且有大量的科学库,所以它是 Python 和 r 的一个很好的替代品。
在过去的三年里,Julia 越来越受欢迎,目前正在被苹果、亚马逊、谷歌、IBM 和微软等大型机构使用。
虽然 Julia 目前没有像 Python 和 R 那样被很好地记录或广泛使用,但一些专家声称该语言是数据科学 的 。
根据他们的说法,现在学习 Julia 是一个好主意——这样当该语言在未来 10 年成为“下一件大事”时,你就可以保持在该领域的领先地位。
其他数据科学家持更保守的观点,认为最好先学习 Python 或 R,因为它们是目前数据科学中使用最广泛的编程语言。
最终,只有时间能告诉我们朱莉娅的未来。
我个人认为,如果你有空闲时间,学点新东西总是个好主意。学习 Julia 可以帮助你加快机器学习工作流程,展示让你与众不同的投资组合项目。
数据科学如何学习 Julia?
以下是学习 Julia for data science 的 3 个步骤:
步骤 1:设置您的 Julia 环境
- 首先,下载 Julia 的最新版本并运行可执行文件。
- 一旦 Julia 安装完毕,点击开始并搜索 Julia。点击它。
- 这应该会打开 Julia 命令行。键入以下内容并按 enter 键:
using Pkg
Pkg.add(“IJulia”)

作者图片
4.一旦安装完成,下载Anaconda 的最新版本,并按照可执行文件中的说明进行操作。
5.导航到开始-> Jupyter 笔记本。这将打开一个如下图所示的选项卡:

作者图片
6.单击新建,并从下拉列表中选择 Julia。这将打开一个笔记本,您可以在其中编写 Julia 代码:

作者图片
如果您已经使用 Jupyter 运行 Python 程序,那么您应该熟悉这个接口。
第二步:学习基础知识
现在你已经有了 Julia,参加一个在线课程来学习编程语言的基础。这包括变量、数据结构、运算符、方法和函数等概念。
Julia 以一个名为多重分派的特性而闻名,该特性使用函数的所有参数来选择应该调用哪个方法。
一旦掌握了 Julia 的基础知识,就可以了解该语言为统计、数据分析、机器学习和可视化提供的不同软件包。
最后,使用这些包来分析数据和执行预测建模。
我建议参加一个关于 Julia 的介绍课程来学习这门语言,因为它会更详细地涵盖上面提到的所有概念。
步骤 3:创建个人项目
一旦你理解了 Julia 的基本原理,你就可以开始使用这种语言创建个人项目了。这不仅会提高您的编程技能,还会极大地增加您的数据科学投资组合的价值。
以下是一些带有源代码的项目想法,您可以使用 Julia 来实现:
Julia 中的内存和缓存
原文:https://towardsdatascience.com/memoize-and-cache-in-julia-48aa98f6549
使用动态编程让你的函数更快
表情化是编程中的一个简单技巧,通过记住一些间歇的结果来减少计算量。在这里,我将向你展示如何用最简单的方法在 Julia 中做到这一点。
🙏非常感谢在这里查看回购的人们。

照片由 Fredy Jacob 在 Unsplash 上拍摄
天真些
我们将从一个简单的斐波那契函数开始,看看对于较大的数字它会变得多慢。斐波纳契数列由以下简单规则定义:
F(1) = 1,F(2) = 1,F(n) = n — 2 + n — 1
其中n为正整数。
让我们用 Julia 来编码:
这是一个递归函数,它调用它自己,这本身并不是一个罪过,但是对于这个特殊的函数来说,代价非常高。如果你想了解为什么这很贵以及记忆化是如何工作的,看看这个由 freeCodeCamp 制作的精彩视频:
由freecodecamp.org进行动态编程
😉我喜欢 Julia 的一点是你可以简洁地定义简单的函数。上面和这个小俏皮话一样:
让我们通过使用方便的@show宏来检查上面的内容是否有效:
julia> for i in 1:10
[@show](http://twitter.com/show) i, fibonacci(i)
end
(i, fibonacci(i)) = (1, 1)
(i, fibonacci(i)) = (2, 1)
(i, fibonacci(i)) = (3, 2)
(i, fibonacci(i)) = (4, 3)
(i, fibonacci(i)) = (5, 5)
(i, fibonacci(i)) = (6, 8)
(i, fibonacci(i)) = (7, 13)
(i, fibonacci(i)) = (8, 21)
(i, fibonacci(i)) = (9, 34)
(i, fibonacci(i)) = (10, 55)
我们还可以通过使用BenchmarkTools包来检查上述操作有多快:
julia> [@btime](http://twitter.com/btime) fibonacci(30)
2.280 ms (0 allocations: 0 bytes)
832040
想看看朱莉娅的其他作品吗?别害羞,跟我来😅
https://blog.devgenius.io/make-a-command-line-game-with-julia-a408057adcfe </jupyter-notebooks-can-be-a-pain-but-i-️-pluto-f47913c5c16d>
保持天真
我们现在拥有的代码可以工作,但是它非常慢,尤其是对于较大的数字。所以本着保持天真的精神,让我们试着实现我们自己的这个函数的记忆版本。
记忆只不过是使用字典(或类似的缓存)来存储结果,而不是重新计算F(3)无数次,而是从字典中查找。这是如何工作的:
这些步骤是:
- 检查我们的内存中是否已经有了
n的结果,如果有,快乐的日子。 - 如果没有,计算它并立即存储在内存中。
- 返回计算结果。
结果更好:
julia> [@btime](http://twitter.com/btime) fibonacci_memory(30)
1.630 μs (7 allocations: 1.97 KiB)
832040
这是 0.00163 ms vs 之前的 2.28ms。那就是 1400x 加速!😜
不要多此一举

乔纳森·肯珀在 Unsplash 上拍摄的照片
尽管编写这个版本并不太复杂,但是有更好的方法。也就是说,Memoize.jl包确实如其名所言。使用起来超级简单。用Pkg.add("Memoize")或] add Memoize以通常的方式安装它,然后你就可以用一个很棒的小宏来帮你记忆你的功能:
这和我们在第一部分中的函数是一样的,除了它现在被包装在@memoize宏中。这是多么容易。
让我们看看它有多快:
julia> [@btime](http://twitter.com/btime) fibonacci_easy(30)
58.189 ns (0 allocations: 0 bytes)
83204
等一下!这比以前快了 30 倍。这怎么可能呢?🧐
实际上,这个记忆函数存储了所有以前的结果,所以现在它根本不做任何计算。从字面上看,这就是从字典中查找结果。您可以使用包装中的memoize_cache功能查看字典:
julia> memoize_cache(fibonacci_easy)
IdDict{Any, Any} with 30 entries:
(7,) => 13
(29,) => 514229
(25,) => 75025
(21,) => 10946
(15,) => 610
(4,) => 3
(13,) => 233
(26,) => 121393
(18,) => 2584
(10,) => 55
(2,) => 1
(27,) => 196418
(20,) => 6765
(16,) => 987
(5,) => 5
(8,) => 21
(28,) => 317811
(24,) => 46368
(12,) => 144
⋮ =>
如你所见,它包含 30 个条目,所以它已经学习了前 30 个斐波那契数列。
如果您的函数需要大量不同的输入,并且您担心内存可能是一个问题,您可能想要将缓存切换到最近最少使用的(LRU)缓存。Memoize.jl 库的主自述文件中有一个这样的例子。
为了完全不同的东西… 🥁
为了更好地衡量,这里有一个斐波那契生成器,没有任何记忆,也没有递归:
以下是一些证据,证明这也能实现所有其他功能:
julia> for i in 1:10
[@show](http://twitter.com/show) i, fibonacci(i), fib(i)
end
(i, fibonacci(i), fib(i)) = (1, 1, 1)
(i, fibonacci(i), fib(i)) = (2, 1, 1)
(i, fibonacci(i), fib(i)) = (3, 2, 2)
(i, fibonacci(i), fib(i)) = (4, 3, 3)
(i, fibonacci(i), fib(i)) = (5, 5, 5)
(i, fibonacci(i), fib(i)) = (6, 8, 8)
(i, fibonacci(i), fib(i)) = (7, 13, 13)
(i, fibonacci(i), fib(i)) = (8, 21, 21)
(i, fibonacci(i), fib(i)) = (9, 34, 34)
(i, fibonacci(i), fib(i)) = (10, 55, 55)

夏洛特·科内比尔在 Unsplash 上的照片
哦,天啊,太快了:
julia> [@btime](http://twitter.com/btime) fib(30)
2.000 ns (0 allocations: 0 bytes)
832040
这比在内存中查找结果快 30 倍。哦,茱莉亚,你是头野兽!😆
想要更多的斐波纳契还是渴望更多的茱莉亚?看看这些:
摘要
如果您有一个函数可以从记住自己计算的中间结果中受益,那么使用同一个包中的@memoize函数。它会让你的功能运行得更快,而且超级容易使用。
如果你想要一种有意义的、易于使用的、快速的编程语言——即使是 for 循环——就用 Julia 吧!
这里用到的所有代码都可以在 GitHub 上【https://github.com/niczky12/medium】https://github.com/niczky12/medium/blob/master/julia/memoize.jl下找到**
要获得所有媒体文章的完整访问权限,包括我的文章,请考虑在此订阅。
人工智能中的记忆和概括
原文:https://towardsdatascience.com/memory-and-generalization-in-artificial-intelligence-35e006ca0a9a
"对看不见的数据进行归纳的能力是机器学习的核心."
当前许多人工智能研究的核心是如何让算法推广到看不见的数据的问题。
在机器学习的背景下,大多数模型都是根据遵循 i.i.d. (独立同分布)假设的数据进行训练和评估的,这意味着给定任务的训练数据和测试数据是从相同的分布中采样的。泛化意味着只从训练数据中提取这个共享的底层分布。
然而,在环境不断变化的现实世界中,i.i.d .假设经常失败,o.o.d(“非分布”)学习对于生存至关重要。
人类目前比机器更擅长概括:我们可以快速识别环境中的分布变化,并且是“少量学习者”,从少量例子中推断出规则。我们可以更灵活地调整我们的推理模型,以适应与我们之前看到的不同的数据。对于许多经典的 ML 模型来说,这不是真的:灾难性遗忘是一个常见的问题,指的是神经网络模型在接受新的、看不见的数据训练时突然忘记他们所学的一切的现象。

Dall-E 所设想的记忆和人工智能。
一般化与过拟合与训练数据欠拟合的问题密切相关,其中过拟合是指通过本质上拟合过多噪声与过少信号来过度表达数据。解决过度拟合的标准方法是低参数模型、修剪模型和正则化技术(剔除、L2 规范等)。).然而,这些直觉中的一些受到了像双重下降这样的现象的质疑(这个 Twitter feed 用一个简单的例子解释了双重下降及其与正则化的关系),其中高容量模型比低容量模型概括得更差,因为它们过度拟合,但是使用更高容量的模型会导致它们比低容量模型概括得更好。
大规模基于变压器的模型的泛化性能也对过度拟合的直觉提出了质疑,这始于 GPT-3 解决未经训练的任务的不可思议的能力。

原 GPT-3 论文的标题。
DeepMind 的新 Flamingo 更进一步,将语言与视觉模型相结合,可以集成广泛的视觉和语言组合任务:

在看到数以百万计的狗和猫的标签样本后,以概括任务的方式表示知识的能力在直觉上似乎比神经网络对狗和猫进行分类要聪明得多。
因此,这些模型令人惊讶的成功提出了一些有趣的问题:泛化意味着什么,以及如何实现:这些模型到底学到了什么?随着模型规模越来越大,参数数量接近人脑中神经元的数量,这个问题并没有变得更容易回答。鉴于其巨大的容量,这些模型只是以一种聪明的方式记住所有的训练数据,还是有更多的东西?
概括以重要的方式与记忆相互作用:这个想法是,如果我们从数据中提取理解,我们就可以获得比我们仅仅记住它更灵活、更浓缩的知识表达。这是许多无监督学习设置中的一项基本任务,例如,解开表征学习。因此,归纳未知数据的能力不仅是机器学习的核心,也是许多智能定义的核心。
根据 Markus Hutter 的说法,intelligence 与无损压缩有许多相似之处,Hutter price 因压缩特定版本的英文维基百科的前 1.000.000.000 个字母的文本文件而获奖。和他的同事 Shane Legg 一起,他们从心理学、机器学习和哲学的广泛定义中提炼出了一个智力的定义,并将其浓缩到这个公式中:

不太正式地说,智能是一个代理从所有环境的空间中提取值的能力,用各自环境的复杂度加权。Kolmogorov 复杂度函数被用作复杂度的度量:它是一个对象有多复杂的信息论度量。它对应于产生它所必需的最短代码行,这与智能作为压缩的思想相联系,对应于它的最优压缩、内存高效表示(我在关于混沌理论和计算不可约性的文章中更详细地讨论了类似的思想)。当过拟合噪声时,我们必须记住它,因为在信息论意义上,噪声是不相关的,没有有意义的解释,因此不包含关于过去或未来的相关信息。
然而,尽管每个人似乎都同意泛化对机器学习很重要,并且在某种程度上与复杂性有关,但它仍然很难衡量,这篇谷歌论文汇编了 40 多种旨在描述复杂性和泛化的衡量标准,结果大相径庭。
神经网络泛化能力如何的问题关系到它们能记住多少,以及它们能学会忘记多少。Pedro Domingos 最近的一篇题为“通过梯度下降学习的每个模型都近似是一个内核机器”的论文为这一讨论带来了一个有趣的新角度:
“深度网络……事实上在数学上近似等同于内核机器,这是一种学习方法,只需记住数据,并通过相似性函数(内核)直接用于预测。这极大地增强了深层网络权重的可解释性,因为它们实际上是训练样本的叠加。”佩德罗·多明戈斯
根据 Domingos 的说法,神经网络中的学习与基于内核的方法(如支持向量机)有许多数学相似之处。

核机器通过非线性变换将数据点嵌入特征空间。在变换的空间中,样本可以例如被线性分离。原文:Alisneaky 矢量:Zirguezi,CC BY-SA 4.0
简单地说,在基于核的方法中,训练数据首先通过非线性变换嵌入到一个新的空间,即所谓的特征向量空间。特征(嵌入空间的维度)可以具有对我们有直观意义的属性(例如,一部电影有多快乐或多恐怖,或者一只猫有多毛茸茸),但是在更一般的意义上,嵌入空间的度量捕捉数据点之间的相似性(例如,两部电影在快乐维度上彼此有多接近)。一旦特征被嵌入,它们可以被线性分离,或者例如用于例如 k-最近邻分类,其中测试数据与特征空间中的 k 个相邻数据点进行比较,并且例如基于这些相邻数据点的最常见标签进行分类(例如,可以通过查看相似电影有多快乐来计算出电影有多快乐)。
深度度量学习的领域处理类似的问题:它的目标是找到数据的嵌入空间,在这些空间中,样本之间的相似性可以很容易地测量(例如人脸识别任务中看不见的人脸图像之间的相似性)。另一方面,神经正切核已被用于导出对应于无限宽神经网络的核函数,这反过来又被证明是一个有用的核函数,并为神经网络如何学习提供了新的理论见解。
Domingo 的论文揭示了使用梯度下降和基于内核的技术学习的模型之间有趣的相似之处:在训练期间, 训练数据隐含地记住了网络权重中的 。在推断期间,“记住的”训练数据和由神经网络表示的非线性变换一起工作,以将测试点与先前看到的数据进行比较,并以类似于核方法的方式对其进行分类。
虽然这一点的含义还没有被完全理解,但它们可以解释为什么用梯度下降训练的神经网络一直在 o.o.d .学习中挣扎:如果它们确实依赖于记忆训练,那么按照前面讨论的逻辑,当它们没有被教会有时遗忘(即正则化)时,它们在概括方面应该更差。因此,这种观点也可以为如何更好地规范化模型以进行推广提供一些启示。

DALL-E 所设想的记忆。
记忆与信息在时间上的存储和检索有关,因此记忆问题在时间序列分析领域中同样重要。递归神经网络(RNNs)和长短期记忆网络(LSTMs)是两种最流行的时间序列数据建模模型。
序列模型中记忆的经典基准是加法问题:模型的任务是将在时间点 t1 和 t2 显示的两个数字相加,并且在时间点 t 输出正确的和。因此,模型需要在更长的时间范围内保留信息,如果 T1 和 T2 之间的时滞增加,则使用基于梯度的方法训练变得越来越困难。这与消失和爆炸梯度问题有关,这是通过序列模型反向传播时,同一层重复应用 t 次造成的(对于来自混沌系统的时间序列,这甚至必然会发生)。这经常导致它们爆炸或灭绝,使得循环模型要么昂贵,要么甚至不可能在某些任务上训练。
保持记忆的难度与学习慢时标的难度有关:已经表明,加法问题可以通过在 RNN 的子空间中启动慢动态的子空间(所谓的线吸引子)来解决,其中信息可以被稳定地保持,而不受网络其余部分的动态的影响。
LSTMs 已成为 20 世纪引用最多的神经网络架构,它通过显式添加一个单元状态来解决存储问题,该单元状态可以在任意时间内保留信息,并添加一个输入、输出和遗忘门来调节流入单元的信息流。因此,LSTMs 在跨越数千个时间步长的“记忆”信息方面,以及在解决加法问题之类的任务方面,优于普通的 rnn。
但是正如前面所讨论的,在这种情况下,记忆也有它的缺点:它更容易通过记住信息来“填充”信息,而不是通过理解信息来压缩信息。
动力系统的语言是物理学家谈论时间现象的方式。从牛顿定理到薛定谔方程,对世界的动态描述是大多数物理理论的核心:

薛定谔方程由微分方程组描述。
这些通过微分方程对现实的描述的特点是它们是无记忆的。给定一个初始状态和系统的时间演化算符的完整描述(即其哈密顿量),系统的时间演化直到无穷大都是已知的(而且是偶时间反转对称的,所以没有信息丢失)。因此,不需要记忆:如果描述确实是完整的,它在 Kolmogorov 复杂性的意义上是完全压缩的。
在动态系统重建中,机器学习领域涉及从时间序列中恢复动态系统,具有记忆的模型实际上可能是有害的,因为它们冒着无法通过找到最佳的、无记忆描述来推广到底层系统的风险,而是通过记住训练数据中的虚假模式来过度拟合。这对于复杂(动态)系统的学习模型来说是一个持续的挑战,例如大脑或气候,其中概括出捕捉其长期行为的系统的适当描述具有许多重要的实际意义,例如对于预测临界点后的动态。这些可以在预测极端天气事件或气候变化的长期影响等方面发挥重要作用。然而,大多数真实世界的系统都是嘈杂、混乱的,并且只能被部分观察到,因此将信号从噪声中分离出来仍然是一个巨大的挑战。
在许多实际应用中,我们并没有对我们观察到的周围世界的完整描述和完整知识。利用记忆,尤其是当没有更压缩的现实描述可用或可行时,仍然是构建实用智能系统的关键因素,也是我们自身智能的定义特征。尽管如此,我认为这是一个富有成效的角度来思考归纳和记忆是如何相互作用的,以及它如何帮助我们设计出更好的归纳算法。
每个程序员都应该知道的内存管理
原文:https://towardsdatascience.com/memory-management-every-developer-should-know-4f4911d8268b
这可能是你最不能错过的编程知识

作者图片
本文将介绍一个非常重要的编程基础知识——内存管理。你可能对它有一个模糊的概念,或者经常忽略它。但是作为一个开发者知道它,可以开阔我们的视野,提高我们的认知,更快更安全地解决记忆问题。所以来和我一起看看吧!
问题
我先问你一个问题:
什么数据放在栈上,什么数据放在堆上?
如果你擅长 JavaScript/Python/Java 等具有自动内存管理的语言,你可能会说下面这个答案:
基本类型存储在堆栈上,对象存储在堆上,闭包变量存储在堆上,等等。
这个答案对吗?没问题,但这只是表面,不是本质。那么本质是什么呢?
我们先分析一下程序中的栈和堆,然后给出答案。
堆
栈数据结构的特点是先进后出。因为这个特性,非常适合记录程序的函数调用,也叫函数调用栈。然后看一下下面的简单代码示例:
fn test() {}
fn main() {
test()
}
我们来分析一下。首先,我们编写的代码将作为一个“entry”执行。请参见下图:

作者图片
函数调用栈从上到下增长。在这个简单的代码示例中,调用流是entry -> main() -> test()。
接下来,每当一个函数执行时,一个连续的内存块被分配在栈顶,这被称为一个帧。
这个“帧”存储当前函数的通用寄存器和当前函数的局部变量的上下文信息。
在这个例子中,当 main()调用 test()时,它会暂时中断当前的 CPU 执行进程,并在堆栈中存储一个 main()通用寄存器的副本。test()执行后,原寄存器上下文将恢复根据之前的拷贝,好像什么都没发生。
酷,这就是通用寄存器的神奇之处!
然后随着函数被一层一层的调用,堆栈会一层一层的扩展,调用结束后,堆栈会一层一层的回溯,每一帧占用的内存会被一一释放。
但是等等,我们好像漏了点什么!它需要连续的内存空间,这意味着程序在调用下一个函数之前必须知道下一个函数需要多少内存空间。
那么程序怎么知道呢?
答案是编译器为我们做了这一切。
在编译代码时,函数是最小的编译单位。每当编译器遇到一个函数,就知道当前函数使用寄存器和局部变量所需的空间。
因此,不能在编译时确定大小或大小可以改变的数据不能安全地放在堆栈上。
许多
如上所述,数据不能安全地放在堆栈上,所以最好放在堆上,例如下面的可变长度数组:
fn main() {
let mut arr = Vec::new();
arr.push(1); // length: 1, **capacity: 4**
println!("length: {}, capacity: {}", arr.len(), **arr.capacity()**);
}
当我们创建一个数组时,如果我们没有事先指定数组的长度,程序会使用libc提供的malloc()函数向请求一个系统调用来获取内存大小。通常 Rust 中为这个数组预留的大小是 4,所以当我们添加的元素比它多的时候,程序会在系统中寻找一块新的内存,复制之前的值并添加新添加的值,最后释放旧的内存。
请求系统调用和寻找新内存然后逐个复制的过程是非常低效的。
因此,这里的最佳做法是提前预留阵列真正需要的空间。
另外,需要跨栈引用的内存也需要放在堆上,这一点很好理解,因为一旦一个栈帧被回收,其内部的局部变量也会被回收,所以在不同的调用栈中共享数据只能使用堆。
但这又带来了一个新的问题,堆上占用的内存什么时候释放?
碎片帐集
各大编程语言都给出了答案:
早期的 C 语言把这一切都留给开发人员手动管理,这对有经验的程序员来说是一个优势,因为对程序内存的控制更好。但是对于初学者来说,记住那些内存管理的最佳实践是很重要的。但与机器不同,总会有一些疏忽,这会导致内存安全问题,导致程序运行缓慢或彻底崩溃。
以 Java 为代表的一系列编程语言使用Tracing GC(Tracing Garbage Collection)自动管理堆内存。这种方法通过定期标记不再被引用的对象并清理它们来自动管理内存,从而减轻了开发人员的负担。但它需要在标记和释放内存时执行额外的逻辑,这导致了 STW(停止世界),就像程序卡住了一样,那些时间也是不确定的。所以如果要开发一些实时性要求高的系统,一般不会使用类似 GC 的语言。
苹果的 Objective-C 和 Swift 使用 ARC ( 自动引用计数 ) ,在编译时为每个函数插入 retain/release 语句,自动维护堆上对象的引用计数。当对象的引用计数为 0 时,release 语句可以释放该对象。但是它增加了许多额外的代码来处理引用计数,这使得它比 GC 效率更低,吞吐量更小。
Rust 使用 所有权 机制,默认绑定堆上数据的生命周期和栈帧的生命周期。一旦栈帧被破坏,堆上的数据也会被丢弃,占用的内存也会被释放。Rust 还为开发人员提供了 API 来改变这种默认行为或定制发布时的行为。
结论
栈上存储的数据是 静态的,大小固定,生命周期固定,不能跨栈引用。
堆上存储的数据是 动态的,不固定大小,不固定生命周期,可以跨栈引用。
今天就到这里。我是 Zachary,我会继续输出与 web 开发相关的故事,如果你喜欢这样的故事并想支持我,请考虑成为 中级会员 。每月 5 美元,你可以无限制地访问媒体内容。如果你通过 我的链接 报名,我会得到一点佣金。
你的支持对我来说非常重要——谢谢。
使用 Python 合并 PDF 文件
原文:https://towardsdatascience.com/merge-pdf-files-using-python-faaeafe4926c
在本教程中,我们将探索如何使用 Python 合并 PDF 文件

戴维·布鲁诺·席尔瓦在 Unsplash 上的照片
目录
- 介绍
- 示例 PDF 文件
- 使用 Python 合并两个 PDF 文件
- 使用 Python 合并多个 PDF 文件
- 结论
介绍
在扫描多页文档或将多页文档保存为计算机上的单个文档后,合并 PDF 文件通常是一项必需的操作。
有几个软件可以帮助快速完成这项任务,比如 Adobe 和在线工具。然而,他们中的大多数要么是付费的,要么可能没有提供足够的安全功能。
在本教程中,我们将探索如何用几行代码在您的计算机上使用 Python 合并 PDF 文件。
为了继续学习本教程,我们需要以下 Python 库:PyPDF2。
如果您没有安装它们,请打开“命令提示符”(在 Windows 上)并使用以下代码安装它们:
pip install PyPDF2
示例 PDF 文件
为了继续学习本教程,我们需要一些 PDF 文件。
以下是我们将在本教程中使用的三个 PDF 文件:
这些 PDF 文件将驻留在 pdf_files 文件夹中,该文件夹与包含我们代码的 main.py 在同一个目录中。
下面是我的文件的结构:

作者图片
使用 Python 合并两个 PDF 文件
为了在 Python 中执行 PDF 合并,我们需要从 PyPDF2 库中导入 PdfFileMerger() 类,并创建这个类的一个实例。
在本例中,我们将合并两个文件: sample_page1.pdf 和 sample_page2.pdf 。
在这种情况下,可以将两个文件路径放入一个列表中,然后我们将对其进行迭代并追加一个到另一个:
您应该会看到 merged_2_pages.pdf 创建在与 main.py 文件相同的目录中,代码如下:
使用 Python 合并多个 PDF 文件
在这一节中,我们将探索如何使用 Python 合并多个 PDF 文件。
合并多个 PDF 文件的一种方法是手动将每个 PDF 文件的文件名添加到一个列表中,然后执行与上一节相同的操作。
但是如果我们的文件夹里有 100 个 PDF 文件呢?使用操作系统库,我们可以将给定目录中的所有文件名作为一个列表进行访问,并对其进行迭代:
您应该看到 merged_all_pages.pdf 创建在与 main.py 文件相同的目录中,代码如下:
结论
在本文中,我们探讨了如何使用 Python 合并多个 PDF 文件。
如果你有任何问题或对编辑有任何建议,请随时在下面留下评论,并查看我的更多 Python 编程教程。
原载于 2022 年 5 月 17 日https://pyshark.com。
合并排序解释——数据科学家算法指南
原文:https://towardsdatascience.com/merge-sort-explained-a-data-scientists-algorithm-guide-2f79d5034ba3
用 Python 实现的合并排序算法的解释

阿诺·弗朗西斯卡在 Unsplash 上的照片
介绍
数据科学家每天都与算法打交道。然而,数据科学学科作为一个整体已经发展成为一个不涉及复杂算法实现的角色。尽管如此,从业者仍然可以从建立对算法的理解和保留中受益。
在本文中,介绍、解释、评估和实现了排序算法 merge sort。这篇文章的目的是为您提供关于合并排序算法的可靠背景信息,这是更复杂算法的基础知识。
虽然合并排序并不复杂,但是理解该算法将有助于您认识到在选择最有效的算法来执行数据相关任务时需要考虑哪些因素。创建于 1945 年的约翰·冯·诺依曼使用分治法开发了合并排序算法。
分步解决
要理解合并排序算法,您必须熟悉分治范式,以及递归的编程概念。计算机科学领域中的递归是指为解决某个问题而定义的方法在其实现体中调用自身。
换句话说,该函数反复调用自身。

图一。递归的可视化说明—作者提供的图像
分治算法(合并排序是其中的一种)在其方法中使用递归来解决特定的问题。分而治之算法将复杂的问题分解成更小的子部分,其中定义的解决方案递归地应用于每个子部分。然后,每个子部分被单独求解,解被重新组合以解决原始问题。
算法设计的分治法结合了三个主要元素:
- 将较大的问题分解成较小的子问题。(除)
- 递归利用函数来解决每个更小的子问题。(征服)
- 最终的解决方案是一个大问题的更小的子问题的解决方案的组合。(联合收割机)
其他算法使用分治模式,如快速排序、二分搜索法和斯特拉森算法。
合并排序
在对列表中的元素进行升序排序的上下文中,merge sort 方法将列表分成两半,然后遍历新的一半,继续将它们进一步分成更小的部分。
随后,对较小的一半进行比较,并将结果组合在一起以形成最终的排序列表。
步骤和实施
合并排序算法的实现分为三个步骤。分而治之,再而三。
分而治之方法的 divide 组件是第一步。这个初始步骤将整个列表分成两个较小的部分。然后,列表被进一步分解,直到它们不能再被分割,在每个对半的列表中只留下一个元素项。
merge sort 第二阶段的递归循环关注的是以特定顺序对列表元素进行排序。对于这种情况,初始数组按升序排序。
在下图中,您可以看到合并排序算法中涉及的除法、比较和组合步骤。

图二。合并排序算法的分割组件图-按作者分类的图像

图 3。征服和组合组件-作者图片。
要自己实现这一点:
- 创建一个名为 merge_sort 的函数,它接受一组整数作为它的参数。以下给出的所有指令都在此函数内。
- 从将列表分成两半开始。记录列表的初始长度。
- 检查记录的长度是否等于 1。如果条件评估为真,则返回列表,因为这意味着列表中只有一个元素。所以没有要求分榜。
- 获取元素数量大于 1 的列表的中点。使用 Python 语言时,//执行无余数除法。它将除法结果舍入到最接近的整数。这也被称为地板划分。
- 使用中点作为参考点,将列表分成两半。这是分治算法范式的划分方面。
- 在这一步利用递归来促进将列表分成两半。变量 'left_half' 和 'right_half' 被分配给' merge_sort' 函数的调用,接受初始列表的两半作为参数。
- 函数的作用是:返回一个函数的调用,这个函数合并两个列表,返回一个合并的排序列表。
def merge_sort(list: [int]):
list_length = len(list)
if list_length == 1:
return list
mid_point = list_length // 2
left_half = merge_sort(list[:mid_point])
right_half = merge_sort(list[mid_point:])
return merge(left_half, right_half)
- 创建一个‘merge’函数,该函数接受两个整数列表作为其参数。该函数包含分治算法范式的征服和合并方面。以下所有步骤都在该函数体内执行。
- 将一个空列表分配给保存已排序整数的变量“output”。
- 指针‘I’和‘j’分别用于索引左列表和右列表。
- 在 while 循环中,对左列表和右列表的元素进行比较。在每次比较之后,输出列表被填充到两个被比较的元素中。追加元素列表的指针递增。
- 要添加到排序列表的剩余元素是从当前指针值到相应列表末尾获得的元素。
def merge(left, right):
output = []
i = j = 0
while (i < len(left) and j < len(right)):
if left[i] < right[j]:
output.append(left[i])
i +=1
else:
output.append(right[j])
j +=1
output.extend(left[i:])
output.extend(right[j:])
return output
unsorted_list = [2, 4, 1, 5, 7, 2, 6, 1, 1, 6, 4, 10, 33, 5, 7, 23]
sorted_list = merge_sort(unsorted_list)
print(unsorted_list)
print(sorted_list)
性能和复杂性
大 O 符号是一种标准,用于根据算法的空间需求和执行时间来定义和组织算法的性能。
合并排序算法的时间复杂度在最佳、最差和平均情况下是相同的。对于大小为 n 的列表,合并排序算法完成的预期步骤数、最小步骤数和最大步骤数都是相同的。
正如本文前面提到的,合并排序算法是一个三步过程:划分、征服和合并。“除法”步骤涉及列表中点的计算,无论列表大小如何,只需一个操作步骤。因此,该操作的符号表示为 O(1) 。
“征服”步骤包括划分和递归求解子数组——符号 log n 表示这一点。“组合”步骤包括将结果组合成最终列表;这个操作的执行时间取决于列表的大小,表示为 O(n) 。
其平均、最佳和最差时间复杂度的合并排序符号是 log n * n * O(1) 。在大 O 符号中,低阶项和常数可以忽略不计,这意味着合并排序算法的最终符号是 O(n log n) 。有关合并排序算法的详细分析,请参考这篇文章。
估价
合并排序在对大型列表进行排序时表现很好,但在对较小的列表进行排序时,其操作时间比其他排序解决方案要慢。合并排序的另一个缺点是,即使初始列表已经排序,它也会执行操作步骤。在排序链表的用例中,合并排序是最快的排序算法之一。合并排序可用于外部存储系统(如硬盘)中的文件排序。
关键要点
本文描述了合并排序技术,从它的组成操作和一步一步的过程方面对它进行了分解。
合并排序算法是常用的,与其他排序算法相比,该算法背后的直觉和实现相当简单。本文包括 Python 中合并排序算法的实现步骤。
您还应该知道,在不同的情况下,合并排序方法的执行时间的时间复杂度,对于最好、最差和一般的情况都是一样的。建议在下列情况下应用合并排序算法:
- 当处理较大的数据集时,使用合并排序算法。与其他排序算法相比,合并排序在小数组上的性能很差。
- 链表中的元素引用了链表中的下一个元素。这意味着在合并排序算法操作中,指针是可修改的,使得元素的比较和插入具有恒定的时间和空间复杂度。
- 以某种形式确定数组是未排序的。Merge sort 甚至会在已排序的数组上执行其操作,这是对计算资源的浪费。
- 当需要考虑数据的稳定性时,请使用合并排序。稳定排序包括保持数组中相同值的顺序。与未排序的数据输入相比,稳定排序的数组中相同值的顺序在排序后的输出中保持在相同的位置。
我希望这篇文章对你有用。
要联系我或找到更多类似本文的内容,请执行以下操作:
- 成为推荐媒介会员,支持我的写作
- 订阅我的 YouTube 频道 获取 AI 播客和即将推出的内容。
- 订阅我的 电子邮件列表 获取我的简讯
将道路速度与 OSMnx 城市网络融合在一起
原文:https://towardsdatascience.com/merging-here-road-speeds-with-osmnx-urban-networks-960bcddd011c
OSMnx 是一个用于道路网络的强大 Python 包,但不包含当前的交通状况-让我们使用 HERE traffic API 来估算 OSMnx 中的实时交通信息

华盛顿 DC 公路网速度从这里 API 和 OSMnx |塞犍陀维韦克
OSMnx 是一个强大的 Python 包,用于处理取自 OpenStreetMap(一个免费的可编辑的世界地理数据库)的静态道路网络信息。下面是一个简短的代码片段,展示了如何使用 OSMnx 来获取华盛顿州 DC 的道路网络:
place_name="Washington DC, USA" G = ox.graph_from_place(place_name,network_type='drive', simplify=True) fig, ax = ox.plot_graph(G,node_size=0)

来自奥斯蒙斯的华盛顿 DC 公路网|塞犍陀·维韦克
但是,OSMnx 缺乏动态信息。特别是,OSMnx 目前根据速度限制,从单个道路穿越时间计算最短时间路径。这可能不具有代表性,尤其是在高峰时间,一些常用的道路可能会因拥堵或事故而完全堵塞。如果有一种方法可以根据当前的交通状况计算出最短的路径,那就太好了。那么我们如何获取 OSMnx 边缘的实时交通信息呢?
HERE API 使得从某个边界框内的路段获取速度成为可能。我写过一篇关于如何使用 HERE API 获得道路速度的文章:
很容易设置一个 HERE API 帐户。


在此获取 REST API 密钥|塞犍陀·维韦克
给定一个来自 OSMnx 的城市道路网络 G,您可以将该道路网络输入到一个自定义函数中,该函数会计算出适当的边界框来从中提取速度。get_speeds.py 模块中的函数 speeds 就是这样做的:
from get_speeds import *
speeds(G,API_KEY)
因为 HERE API 给出了交通速度和速度限制的信息,所以我绘制了一个图表来可视化道路上的相对速度。

速度从这里 API |塞犍陀维韦克
然而,有一个问题。这里提供了边界框中的速度。但是请注意,OSMnx 只提供来自华盛顿 DC 地区的道路网络,这对应于边界框的右半部分。我们可以通过将从 HERE 路段数据获得的速度与 OSMnx 边缘相结合来解决这个问题。为此,我们需要为 OSMnx 中的每条边分配来自 HERE 路段数据的最近路段的速度。计算最近的 OSMnx 边和与单点的相应距离需要几秒钟的时间。由于 here 数据中有 40,000+路段和速度,预计这需要一段时间!
API_KEY='BLAH'
get_edge_speeds(G,API_KEY)

从这里获取交通位置的最近 OSMnx 边缘 API |塞犍陀维韦克
我使用 joblib 利用了所有 16 个 CPU 内核,这花了 10 多个小时才完成!过滤距离 OSMnx 边缘的阈值距离内的所有路段,得到以下图像:

通过接近奥斯门克斯边缘过滤交通速度段|塞犍陀·维韦克
正如您所看到的,它看起来好多了——而且比原来的 OSMnx 图形更有可比性!
最后,基于截止值(这里是 0.01),你可以得到网络的边和速度!
df_es=pd.read_csv('df_es.csv')
edges1=add_edge_speeds(G,df_es,0.01) #change cutoff as necessary
就是这样-现在您可以执行各种网络算法,如最短时间路径,考虑交通模式!不过有一点需要注意的是,HERE API 不提供某些较小道路上的速度信息,而 OSMnx 提供了更细粒度的道路网络信息。这将导致在较小的道路上对速度的不正确估计。根据您的使用情况,这可能是一个大问题,也可能根本不是什么大问题。
代码可以在 GitHub repo 中找到:
https://github.com/skandavivek/osmnx-edge-speeds
如果你还不是中会员,想支持我这样的作家,可以通过我的推荐链接随意报名:【https://skanda-vivek.medium.com/membership】
关注我 如果你喜欢这篇文章——我经常在数据科学、物理和社会的界面上写作。
原载于 2022 年 4 月 8 日 https://chaoscontrol.nethttps://chaoscontrol.net/real-time-traffic-speeds-in-osmnx-urban-road-networks/。
大规模合并多个数据集
原文:https://towardsdatascience.com/merging-multiple-datasets-at-scale-6a2424d5ea57
在现代数据堆栈上连接数据

从事数据科学工作时,数据科学家很少能够在一个文件中找到项目所需的所有数据。即使数据总是在单个文件中可用,特征工程技术也经常创建额外的数据集,这意味着数据将需要结合在一起。在现代数据堆栈上,这可以使用 SQL 来完成,如这里的和这里的所述。对于使用 Python 的数据科学家来说,这可以通过 pandas 中的merge命令来完成。
使用 AdventureWorks 数据(在麻省理工学院许可下发布,更多文档见此处),考虑理解促销对互联网销售的影响的问题。一个 Jupyter 笔记本展示了如何将所有的冒险工作数据读入熊猫数据帧,可以在这里找到。检查 internet_sales 数据框架(在笔记本中称为 import_sales ),它看起来很有希望,因为它包含购买信息、显示促销的促销关键字、购买的折扣金额和总销售额。可惜数据好像不好。无论是否有促销活动,折扣金额总是为零,尽管有各种促销活动,销售额也是相同的。
在真实数据中遇到这类数据错误并不罕见。虽然这可能是最初使用 AdventureWorks 数据时产生的错误,但在现实世界中,这通常是由于软件中的问题以及在保存发票之前未能正确应用促销详细信息而导致的。正如数据科学中经常出现的情况,需要找到一种变通方法。幸运的是,存在一个促销数据框架(在上面的笔记本中称为 import_promotion ),其中包含关于每次促销和折扣百分比的信息。这个数据将需要加入到销售数据和实际销售价格计算。
熊猫合并
将 AdventureWorks 销售和促销数据分别提取为互联网销售和促销数据的import_sales和import_promotion。首先,我们可以创建一个简化的促销数据集,只包含PROMOTIONKEY和DISCOUNTPCT。
promotiondf = import_promotion[['PROMOTIONKEY',
'DISCOUNTPCT']].copy()
此促销数据框架可与原始销售数据框架结合,如下所示:
sales = pd.merge(import_sales,
lean_promo,
on='PROMOTIONKEY',
how='left')
或者,这可以计算为:
sales = import_sales.merge(import_sales,
lean_promo,
on='PROMOTIONKEY',
how='left')
dataframe sales现在包含了折扣前的价格和应该应用的折扣百分比。实际销售价格可以计算为:
sales['ACTUALSALESAMT'] = (1 - sales['DISCOUNTPCT']) *
sales['SALESAMOUNT']
现在,可以用实际销售额来完成对折扣影响的进一步分析。
虽然以这种方式使用熊猫既普遍又有效,但它也不是没有缺点。特别是,使用 pandas 需要将数据从数据仓库中提取出来,并移动到可以进行计算和合并的机器上,然后再发送回数据仓库进行存储。在海量数据集的情况下,这可能是不可行的。即使是中等大小的数据适合服务器的内存,将数据提取到 pandas 并将其推回仓库的时间也可能超过进行分析本身的时间。
使用 RasgoQL 在数据仓库中执行此操作
不用移动数据来使用 pandas,同样的连接和计算可以在数据仓库中完成。并不是所有的数据科学家都喜欢使用 SQL 或者愿意继续使用 Python。这是因为有更好的 ide 可用,或者在这项工作中能够快速分析数据。开源包 RasgoQL 有助于弥合这一差距,它允许数据科学家在 Python 中工作,但在数据仓库中执行代码。
首先,RasgoQL 需要云中的表的句柄。这是通过dataset功能完成的:
salesds = rql.dataset('ADVENTUREWORKS.PUBLIC.FACTINTERNETSALES')
promotionds = rql.dataset('ADVENTUREWORKS.PUBLIC.DIMPROMOTION')
接下来,将促销数据与销售数据连接起来:
sales = sales_ds.join(join_table=promotion_ds.fqtn,
join_type='RIGHT',
join_columns={'PROMOTIONKEY':'PROMOTIONKEY'})
可以应用math函数来创建实际销售额:
sales = sales.math(math_ops=['(1 - DISCOUNTPCT) * SALESAMOUNT'],
names=['ACTUALSALESAMT'])
可以通过运行preview将这些数据下载到 python 环境中,作为数据帧中十行的样本:
sales_sample_df = sales.preview()
或者可以下载完整数据:
sales_df = sales.to_df()
为了让每个人都可以在数据仓库上查看这些数据,可以使用save发布这些数据:
sales.save(table_name='<Actual Sales Data Tablename>',
table_type='view')
或者,将此保存为表格,将 table_type 从“视图”更改为“表格”。如果您想检查 RasgoQL 用来创建这个表或视图的 SQL,运行sql:
print(sales.sql())
最后,如果您在生产中使用 dbt,并且希望将这项工作导出为 dbt 模型,供您的数据工程团队使用,请致电to_dbt:
sales.to_dbt(project_directory='<path to dbt project>'
这种方法导致在数据仓库上生成并执行 SQL 连接,从而允许连接所有数据,而无需将数据移动到工作站。此外,由于这会创建在仓库上运行的 SQL,因此这些特性会自动准备好用于生产,而无需将 Python 代码转换为 SQL。
当处理少量数据时,pandas 会是一个有用的工具。随着数据集变得越来越大,如果不是不可能的话,继续使用 pandas 会很困难。大量的 Python 包会有所帮助,但是利用数据仓库中可用的处理能力,最好的解决方案可能是在那里执行尽可能多的计算。
将数据准备过程迁移到数据仓库是一项挑战,通常需要数据工程的帮助。使用开源包 RasgoQL,数据科学家可以在数据仓库中利用 SQL 的强大功能,而不需要额外的资源。
如果你想查看 RasgoQL,文档可以在这里找到和库这里。
融合科学和设计,为每个人创造人工智能

我们在构建人工智能系统方面参与得越多,它们看起来就越不可怕,破坏性也就越小。附身摄影在 Unsplash 上拍照。
到目前为止,每个人都听说过人工智能(AI)和它的小表弟机器学习。人工智能的各个子领域,如机器学习、计算机视觉等等,可能是当今使用最多但最不为人所知的技术之一。
事实上,许多在该领域工作的人实际上称人工智能系统为【黑匣子】,因为他们可以看到输入和输出,但他们实际上并不了解它们内部发生了什么。这对于某个实验室中的一些实验技术来说已经够麻烦的了,但请想想这样一个事实,即许多这些机器学习系统负责做出影响许多人生活的决策。

谁真正知道里面是什么,或者是什么让一个人工智能滴答作响?Sam Moghadam Khamseh 在 Unsplash 上拍摄的照片。
其中一些决定包括:
- 社会和司法系统的决策,如决定是否拘留刑事被告或是否将儿童保护请求标记为可信。
- 筛选改变人们生活的申请,如职业申请、保险申请和贷款申请。
- 通过审查社交媒体和生成原创内容来影响人们的信息和媒体消费。
- 通过面部识别监控侵犯公众隐私。
呀呀对吧?!
然而,这并不完全是悲观的。最近,科技&科学界和设计界一直在努力让人工智能系统变得更加可解释和透明。换句话说,让人们了解这些系统内部发生的决策过程,以及输入数据是如何被分析并影响结果的。虽然这些努力无疑是朝着正确方向迈出的一步,但它们往往是事后的反应性干预。相反,预防性措施可能更有价值,确保这些系统从一开始就利用受其影响最大的人群的洞察力来构建。

基于技术和设计主导的干预正在帮助揭示黑盒人工智能系统的内部工作原理。Hassan OUAJBIR 在 Unsplash 上拍摄的照片。
提示:合作设计
协作设计,或简称为合作设计,是和人一起设计的行为,与传统的为人设计相对。这种设计方法最初在斯堪的纳维亚的设计实践中很受欢迎,近年来因为它给设计过程带来的价值而大受欢迎。
通过让用户和其他受设计影响的利益相关者参与进来,设计团队可以在过程的早期了解他们的需求、观点和经验。这有助于团队从一开始就考虑这些信息,而不是构建一些东西,然后在最后的测试阶段发现它不合适,这是用户和利益相关者通常会参与的阶段。与仅仅采访或调查用户相比,联合设计的另一个好处是,它有助于克服所谓的用户知识的“粘性”:即用户在实际说出他们需要什么时的困难。通过让他们参与有趣的互动设计会议,参与者往往会感觉更舒服,并有不同的渠道来表达自己,而不仅仅是口头解释。

协同设计可以通过非语言渠道释放用户信息和体验。照片由印度尼西亚 UX在 Unsplash 上拍摄。
科学与设计:一枚硬币的两面
你可能会想:我们怎么能让一个毫无技术知识的用户来设计一个大多数有技术背景的人都难以理解的人工智能系统呢?
而事实是,这比你想象的要容易!以前也有人这样做过。
越来越多的研究聚焦于将不同的协同设计实践应用于人工智能设计过程。这些研究使用了像角色扮演和纸牌这样的技术来帮助不同的非利益相关者设计和决策人工智能系统的不同功能和行为。
使用这些技术并让更多的利益相关者参与进来有很多好处:
- 它利用“社区专业知识”使产品更具同理心和人性化,最终增加用户的接受度、信任度和认同度。
- 它增加了关键决策的包容性和参与性,有可能导致偏见更少、目光更狭隘的结果,对某些人群产生危险的影响。
- 它允许更多跨学科的人从事产品设计,这有数不清的好处——我以后会更多地谈到这一点。
事实上,设计的几个分支对技术项目,尤其是人工智能系统非常有价值,我将在以后的另一篇文章中介绍这些分支及其价值。
前进的道路
虽然现有的项目将设计技术应用于技术领域,特别是人工智能,是有益的,但它们还不足以产生深远的影响。这些努力中有很多是孤立的、单独的研究,其结果往往不会用于其他应用或更大范围。这些项目和几家公司已经朝着正确的方向迈出了大步,但现在缺少的是:
- 为了跨领域和应用程序复制和测试研究,以查看它们的概括程度,
- 创建一个完整的方法或过程,在整个人工智能生命周期中,而不仅仅是在最初的构思阶段,正式化和标准化不同利益相关者的参与,
- 关注对利益相关者最重要的价值,以及如何尊重和维护正在创造的技术中的价值。
将协同设计应用于创造人工智能系统肯定是有希望的,但是为了实践的成熟还需要采取下一步措施。

新的令人兴奋的工作正在设计和人工智能的交叉点上发生,但前面还有很长的路要走。 Jukan Tateisi 在 Unsplash 上拍摄的照片。
我适合的地方
当前的现实是,人工智能系统悬而未决,有可能变得更加孤立、排外和复杂;或者开放,变得更容易接近和包容,这是我博士项目的灵感来源。通过查看上面的第 2 点和第 3 点,我正在努力创建这个统一的过程,以及一个支持它的工具包,以系统地让人们参与到人工智能的整个生命周期中来——重点关注价值敏感性。
你可以在伦敦帝国理工学院的网站上查看我的项目的官方页面。你也可以看看我写的另一篇文章,解释我的博士项目的细节。
我建立了这个媒体账户来发布我在博士项目中的有趣发现,希望以一种任何人都可以理解的方式传播关于人工智能系统的新闻和信息。这篇文章是我计划的许多文章中的第一篇,它解释了各种各样的概念,以及我已经举办的研讨会的一些更新和我的文献综述中的一些很酷的主题。如果你喜欢这第一篇文章,那么请考虑跟随我发布新的东西,并请喜欢和分享!
元形式:视觉的事实需要?
原文:https://towardsdatascience.com/metaformer-de-facto-need-for-vision-93476cc9fec5
《视觉变形金刚》成功的关键是什么?

照片由 Unsplash 上的 Aditya Vyas 拍摄
视觉变压器
最近,自从阿列克谢·多索维茨基在他的论文中提出视觉变形金刚(ViT)后,《变形金刚》开始流行起来。ViT 已被证明能够在图像分类任务中胜过卷积神经网络(CNN ),同时需要较少的计算资源。
在过去的十年中,CNN 一直在计算机视觉领域独占鳌头,一个年轻的 ViT 第一次踏入这个领域,出人意料地击败了 CNN。这就产生了一个大问题:
ViT 成功的真正原因是什么?

图片来自论文
上图显示了作为 ViT 核心的变压器编码器的结构。几年后,许多后续工作都专注于通过用多层感知(MLPs)等其他结构完全取代多头注意力来提高性能,并且他们在图像分类基准上取得了有竞争力的性能。这一轨迹后来吸引了许多研究人员去寻找新的注意力模块或令牌混合器。
元格式器

图片来自纸张
或者,新加坡国立大学的于等人认为,变形金刚在计算机视觉领域的成功主要依赖于它的总体架构,而不是令牌混合器的设计。为了验证这一点,他们已经提出使用一种令人尴尬的简单非参数操作,平均池作为令牌混合器,并且仍然在各种计算机视觉任务上实现了最先进的性能,如图像分类、对象检测和实例分割。
在论文中,他们将 Transformer 的一般架构称为元形成器,并研究了池形成器的效能,其中平均池操作被用作令牌混合器。令人惊讶的是, PoolFormer 优于采用注意力和空间 MLP 等其他复杂模块的 Transformer 架构。
前池

图片来自纸
PoolFormer 的整体架构如上图所示。该模型有 4 个主要阶段,它们具有相同的设计,一个补丁嵌入层,随后是 PoolFormer 块。

作者图片
每个阶段的细节可以如上图所示。补丁嵌入通过使用一个 3x3 的 Conv 层实现。定额图层可以批量归一化、图层归一化、分组归一化;在本文中,作者应用了组归一化,因为它在他们的实验中显示了更好的结果。信道 MLP 模块采用两个 1x1 的 Conv 层,中间是一个路斯激活层。官方代码可以在:https://github.com/sail-sg/poolformer找到

图片来自论文
结果
PoolFormer 已经在计算机视觉任务的各种基准上与其他最先进的模型进行了比较,包括图像分类、对象检测和实例分割。结果总结如下:
图像分类

图片来自论文

图片来自纸
对象检测和实例分割

图片来自纸

图片来自纸

图片来自纸
PoolFormer 块的 Pytorch 类代码

图片来自纸

图片来自纸张

图片来自纸
结论
在这篇文章中,我简要回顾了 MetaFormer,这是一种基于变形金刚的通用架构,它是变形金刚及其变体在计算机视觉中取得成功的真正原因,作者说。作者基于一个假设提出了元形式,即变形金刚或类似 MLP 的模型的能力是通过它们的一般结构而不是令牌混合器获得的。为了证明这个假设,一个令人尴尬的简单的非参数操作符,池,被用作令牌混合器,并且仍然优于其他高级模块,例如 attention。MetaFormer 的能力也已经在图像分类、对象检测和实例分割任务的基准上得到验证。
欢迎读者访问我的脸书粉丝页面,分享关于机器学习的事情:深入机器学习。我的其他著名帖子也可以在这里找到:
- 约洛夫 4–5D 评论
- 黑暗时代
- EFPN:扩展特征金字塔网络
- 数据扩充
- 数据提炼
- 而其他人在我的页面。
感谢您抽出时间!
Julia 中的元编程:全面概述
原文:https://towardsdatascience.com/metaprogramming-in-julia-a-full-overview-2b4e811f1f77
在 Julia 中用几种不同的技术看元编程

(图片由 Pixabay 上的 3Lloi_KoteikA 提供)
介绍
传统函数式编程语言最著名的事情之一可能是一个叫做元编程的概念。元编程本质上只是用数据编程,而不是用代码——或者更确切地说,是从代码中创建数据或从数据中创建代码。历史上,元编程的实现一直围绕着列表和符号,Julia 的元编程实现与这些历史实现既相似又不同。
尽管有一些细微的差别,并且缺乏关于 Julia 元编程的全面教程,Julian 系统实际上真的很棒。如果你碰巧有过其他语言的元编程经验,这个实现可能会让你感到惊讶和兴奋,因为我认为 Julia 处理这个的方式非常棒,就像 Julia 中的许多其他特性一样!如果您想更深入地了解我在本文中使用的代码和数据代码,这里有一个链接指向我在本文中使用的笔记本!:
https://github.com/emmettgb/Emmetts-DS-NoteBooks/blob/master/Julia/Julia metaprogramming.ipynb
顺便说一下,这个存储库非常庞大,现在总共包含 132 个项目。那可是好多笔记本啊!
一切都是象征
在开始元编程之前,你需要了解的第一件事是,Julia 中的一切都是一个符号。也就是说,everything 的类型不是 Symbol(),而是对 Julia 内部的每个现有名称都有一个查找。我们实际上可以通过符号以及它们的别名来索引单个作用域,别名通常只是转换成字符串的符号。正如我刚才所说的,这可以在语言的任何单独范围内完成。换句话说,如果我们使用一个构造函数来创建一个新的类型,那么这个类型现在就有了自己的作用域,在这个作用域中,我们可以访问新定义的符号。获得这样的值的一个很好的方法是通过 Julia 保守得最好的秘密之一 getfield()方法。
我们可以在 Julia 中任何有作用域的东西上使用 getfield 方法。这可以是我们的全局环境,在 Julia 中称为 Main,一个模块,一个类型——任何可以存储作用域的东西——当然没有属性的作用域除外,比如方法,循环等等。考虑以下值 x:
x = 5
这个 x 现在被全局定义为 64 位整数 5。我们可以通过调用 Main 中的 5 来访问它,正如人们可能期望的那样:
println(x)5
当然,我们也可以直接调用它作为主模块的子模块:
Main.x5
查看一个 get 字段,我们只需将名称更改为其“字符串”别名的符号表示。我们还需要提供类型、模块或我们正在处理的任何东西作为第一个位置参数,这样方法就知道从哪里获取字段:
getfield(Main, :x)5
我有一整篇关于 getfield 和 Julian 自省主题的文章,如果这不是很有意义,或者可能有人想了解更多关于这种方法和其他类似方法的信息,那么它可能值得一读:
公式
数据类型的字段也不是唯一可以用符号表示的东西。一个表达式本质上是一个可以被求值的符号,它比一个典型的符号有更多的属性。我们可以用碱。Meta.parse 以便将字符串转换为表达式,例如:
helloworld = Base.Meta.parse("println(\"Hello World!\")"):(println("Hello World!"))
这将成为 Expr 类型:
println(typeof(helloworld))Expr
最后,我们可以使用 eval()方法计算这样的表达式。
eval(helloworld)Hello World!
表达式也有自己的字段,这些字段比我们想象的要方便得多,我们将在后面的例子中详述。不过现在,让我们用一种内省的方法来看看这些字段,我在前面提到的文章中也提到了这种方法。
fieldnames(Expr)(:head, :args)
我们可以看到这些如何适合我们刚刚使用循环和 getfield 评估的示例 helloworld 表达式:
for field in fieldnames(Expr)
println(getfield(helloworld, field))
endcall
Any[:println, "Hello World!"]
head 字段通知 Julia 我们正在使用哪种表达式。如果我们是元编程正则 Julia,就像我们在这里一样,这将永远是:call。在大多数情况下,很可能是:call,所以现在我们将忽略这一点,转到:args 字段。:args 是 arguments 的缩写,后面是作为符号的方法调用,例如:println,后面是它们的参数。求值器知道方法的参数何时结束,因为它遇到了一个新的符号,这个符号也是一个方法调用。换句话说,我们可以把这个符号包装成“Hello World!”就像我们上面写的那样。
println("Hello World!")
宏指令
从最终用户的角度来看,将常规代码与这些表达式联系起来的是宏。宏允许我们计算 10,或\n,或 return 之前的所有内容,无论你想在运行中调用什么,然后处理这个表达式。这里有一些有趣的细微差别,老实说,有些你无法从阅读 Julia 文档中找到——我认为这是不幸的,所以我将为你提供一些关于宏的信息,以及一种有趣的方法来查看 Julia,如果这是使用方法错误的情况。此外,如果不把这一节写得很长,我就不可能表达关于宏语法和插值的每一件事情,所以如果你想了解更多关于宏的知识,这里有一个关于宏的 Julia 文档的链接:
无论如何,让我们定义一个我们可以使用的宏。如你所料,我们只需使用关键字宏就可以做到。除此之外,宏的结构通常很像函数,尽管你绝对不应该使用宏作为函数,我们将在创建一个宏之后解释为什么会这样。我们的宏将被用来打印出一个循环要进行多长时间。这样,我们可以很好地了解如何使用宏来修改语法,但也不会太深入,以至于在我们进入下一个更复杂的代码部分之前我会失去你。为了证明我之前所说的,我们无法从文档中获得的信息,我将通过提供宏无法接收的参数来故意导致方法错误。
macro howmany(s::String, b::Int64)
end
请注意,这些并不是我想要使用的实际参数,我只是想说明有时方法错误实际上可以成为学习方法的很好的输出。我将使用我们之前在 getfield 中使用的小循环来定义一个循环来使用这个宏。
[@howmany](http://twitter.com/howmany) for field in fieldnames(Expr)
println(field)
endLoadError: MethodError: no method matching var"@howmany"(::LineNumberNode, ::Module, ::Expr)
Closest candidates are:
var"@howmany"(::LineNumberNode, ::Module) at In[12]:1
var"@howmany"(::LineNumberNode, ::Module, ::String, ::Int64) at In[13]:1
in expression starting at In[14]:1
这里文档没有告诉你的是::LineNumberNode 和::Module 在默认情况下总是会被提供。我们实际上从来不需要使用 LineNumberNode,因为我怀疑这更像是一个内部的东西,因为我们确实收到了宏之后的其余行作为表达式,这实际上应该是传递给我们的唯一参数。但是,还要注意提供了模块,这是调用宏的模块。我们可以使用 module 在代码中访问它,这是一个全局定义,正如你所看到的:
__module__UndefVarError: __module__ not defined
但是,如果我们仅将表达式作为参数来修改宏,我们可以将该参数打印出来:
macro howmany(exp::Expr)
println(__module__)
end[@howmany](http://twitter.com/howmany) for field in fieldnames(Expr)
println(field)
endMain
也就是说,论点是完全不可见的——你不会从阅读文档中知道它的存在,这也可能支持我关于行号的理论——因为也许这也是更内在的。
一般来说,编写宏时的最佳实践是做任何你必须做的事情,然后引用一个函数。出现这种情况有很多原因,这在过去确实让我感到困惑——人们告诉我这是使用宏的一种更好的方式,从而帮助了我,所以现在我将这一信息传递给你。现在,如果我们在 args 字段中打印出可索引的第一个参数,我们会得到
macro howmany(exp::Expr)
println(exp.args[1])
endfield = fieldnames(Expr)
由于这个表达式的性质,假设它是一个 for 循环,处理这个问题的最好方法可能是拆分一个字符串——这并不总是如此,但在某些情况下,重新解析是必要的,否则就没有办法获得这些参数名。记住这一点,我将继续传递这个表达式的字符串版本,通过一个新的方法,它将为我们计算这个值的长度。
macro howmany(exp::Expr)
statement = exp.args[1]
howmany(string(exp))
end
在我们新的 howmany()方法中,我们将首先把我们的字符串分割成子字符串,然后因为我们的参数在这个例子中的位置保持一致,我们不需要做任何进一步的工作。
function howmany(exp::String)
d = split(exp, ' ')
end
非常简单的东西,我将获取我们的值的位置,然后解析它并在 length()方法周围求值,就像这样:
function howmany(exp::String)
d = split(exp, ' ')
value = d[4]
n = eval(Base.Meta.parse("length(" * value * ")"))
println("COUNT: " * string(n))
end[@howmany](http://twitter.com/howmany) for field in fieldnames(Expr)
endCOUNT: 2
然而,有一个问题仍然没有答案;为什么我决定采用这种方法?在宏内部计算一个返回将会导致很多作用域的问题。尽管模块被传递到宏中,但是从外部调用方法要简单得多,因为在所有诚实的宏中,在 Julia 中有一些处理范围的有趣方法。也就是说,你也可以查看下面的替代方案,它比系统化的方法要多得多的代码来完成一项任务。
"""
## [@each](http://twitter.com/each) exp::Expr -> ::Bool
Determines whether each element of an iterable meets a certain condition. Returns a boolean, true if all of the elements meet a condition, false if otherwise.
Used in conditional contexts.
### example
x = [5, 10, 15, 20]
if @each x % 5 == 0
println("They are!")
end
They are!
if @each x > 25
println("They are!")
end
if @each x < 25
println("They are!")
end
They are!
"""
macro each(exp::Expr)
x = exp.args[2]
xname = ""
if contains(string(x), '[')
xname = eval(x)
else
xname = getfield(__module__, Symbol(x))
end
if length(exp.args) == 2
for value in xname
state = eval(Meta.parse(string(exp.args[1], "(", value, ")")))
if state != true
return(false)
end
end
endfor value in xname
state = eval(Meta.parse(string(value," ", exp.args[1], " ", exp.args[3])))
if state != true
return(false)
end
end
return(true)
end
结论
Julia 中有很多关于元编程的内容,但是希望这个基本介绍非常适合那些对这个主题感兴趣但是可能不知道从哪里开始的人。我认为 Julian 实现可能是我使用过的最好的实现之一,除了像 Lisp 这样评估数组的语言。也就是说,像 Lisp 这样的语言有它们自己的缺点,并且没有 Julia 那样漂亮的语法。我发现在 Lisp 中计算括号有点烦人。也就是说,这肯定是元编程在一种语言中较好的实现之一,其伟大之处就在于它允许您做多少事情。这些宏中的许多被用来在一个全新的层面上与代码进行交互,通过提高性能、计时等类似的事情,这使得许多事情有始有终。这真的非常非常酷。我喜欢在一个符号下有一个固定名称的概念,类似于利用这些元编程概念的其他语言。感谢您阅读我的文章,我希望这是对朱莉娅的一个有趣的方面的一个有趣的观察,我以前没有太多接触过。感谢您的阅读!
推断因果关系的方法
原文:https://towardsdatascience.com/methods-for-inferring-causality-52e4144f6865
匹配,倾向加权,双重稳健最大似然,回归不连续设计

在 Unsplash 上由 Austin Distel 拍摄的照片
在我们之前的文章第 1 部分:因果推理入门中,我们介绍了因果推理的基础知识,并对回归给予了很多关注。我们还讨论了回归不是因果估计设计中关闭后门的唯一方法。在这篇文章中,我们将讨论一些其他方法,都旨在实现同样的事情,即,使治疗组和对照组在所有方面都相似,除了在治疗方面。
相称的
匹配的目的是通过为每个治疗单位找到一个(或多个)具有相似可观察特征的非治疗单位,并抵消协变量,从而减少观察数据研究中估计治疗效果的偏倚。如果存在一些影响治疗和结果的混杂因素,比如年龄,从而使治疗组和对照组不可比,我们可以通过将每个治疗单位与对照组的相似单位进行匹配来使它们具有可比性。在我们的例子中,来自治疗组和对照组的年龄相似的人被放在一起比较,最终结果是所有这些的平均值。

如上计算用于匹配的 ATE,其中 Yj(i)是来自与治疗组最相似的对照组的样本。2Ti -1 用于双向匹配。
这有助于消除一些偏差,但如果匹配的治疗和控制单位的 Y₀差异不为 0 呢?可能会发生这样的情况,因为匹配的差异,我们仍然有偏见。幸运的是,在老朋友的帮助下,我们有办法进一步改善我们已经开始的减少偏见之旅。再一次回归拯救!

₀(X i)是未经处理的处理单位 I 的结果值,即这是处理单位的 Y₀。
₀(X j(i))是对照单位 j 与处理单位 I 匹配的结果,即这是对照单位与处理单位匹配的 Y₀。因此,在上面的 ATE 方程中,我们确保处理过的和匹配的单位的 Y₀不会对 ATE 产生影响。
假设我们想要测量药物治疗对恢复天数的影响,但是在我们的实验设计中,我们有像患者的年龄、严重程度和性别这样的混杂因素。

ATE = 16 . 48667 . 48868688667
如果我们直接计算 ate 而不添加任何对照,看起来药物治疗增加了恢复天数,但我们知道由于混杂,治疗组和对照组没有可比性。让我们看看,如果我们使用 KNN 匹配处理和控制单元,并使用回归进一步减少偏差(偏差校正),会发生什么,如上所示

ATE = 7.62666061414365
瞧啊。这肯定更有意义。一旦我们通过匹配控制了混杂因素,由于药物的原因,恢复期减少了 7 天。
倾向得分分层:我们可以根据倾向得分进行匹配,而不是直接或使用一些距离度量来匹配协变量,这是给定所有协变量的条件治疗概率 P(T|X) ,表示为 P(x)。分层使用倾向评分的分位数对个体进行细分,在每个层内计算治疗效果,并使用权重(每个层中单元的比例)进行组合,以获得最终估计值。已经表明,分成 5 层可以减少高达 90%的偏差。
让我们使用微软**的 dowhy 库来实现这一点。**

当有许多控制单元要与治疗单元匹配时,匹配效果很好。在倾向术语中,它被称为共同支持,治疗和未治疗之间的倾向分布应该有一个可靠的估计良好的重叠。

倾向得分加权
我们在上面讨论过,我们可以根据称为倾向得分的单个值来决定条件,而不是根据协变量来决定条件,这是在给定协变量 P(T|X)的情况下获得治疗 T 的条件概率,因此
****
倾向得分(作者图片)
x(混杂因素)通过函数 P(x)影响 T(治疗),因此对 P(x)的控制间接控制了 x。我们可以使用该倾向得分进行匹配,也可以在线性回归中直接使用该倾向得分来控制偏倚,而不是根据所有混杂因素对其进行调节。我们现在将使用 P(x)作为缩放参数,这种方法被称为治疗加权的逆概率(IPTW)****

对 X 进行简化和积分,我们得到。

根据上面的等式,对于治疗组,我们通过倾向的倒数来衡量结果。当实际上在治疗组中时,对于具有低治疗概率(看起来未治疗)的人给予较高的权重,对于对照组反之亦然。这就产生了一个群体,其中每个单位都由倾向的倒数来衡量,从而控制了倾向所依赖的所有 X。

拉隆德数据集
我们将使用 Lalonde 数据集来发现治疗对 78 年实际收入的影响,首先通过使用回归并将所有混杂因素包括在回归方程中,然后通过使用回归方程中的倾向得分而不是混杂变量,最后我们将使用 DoWhy 包中的倾向得分加权方法。

用所有混杂变量回归后,ATE 约为 1671。现在让我们使用逻辑回归得到倾向得分,并在线性回归中使用,控制 P(x)而不是 x。

****Dow why包中的倾向得分加权函数

关于 DoWhy 的附加注释
DoWhy 软件包为我们提供了一些让我们的结果更加可信的方法,叫做反驳方法。让我们用上面的例子来理解这一点。
****随机共因反驳器:将随机生成的协变量添加到数据中,并重新运行分析,以查看因果估计值是否改变。因果估计不会因为一个随机变量而改变太多。

****安慰剂治疗反驳者:随机分配任何协变量作为治疗。因果估计应该趋向于零。

****数据子集反驳者:类似于交叉验证,如果我们的因果估计在子集之间变化,它会创建数据和测量的子集。我们的估计应该不会有太大的变化。

通过这些反驳测试并不意味着我们的因果估计是正确的。它只是用来为我们的因果估计提供更多的信心。记住,我们在上一篇文章中讨论过,观察数据的因果推断需要内部有效性,我们必须确保我们已经控制了导致偏差的变量。这不同于预测模型所需的外部有效性,我们使用训练测试分割来实现预测模型,以获得可靠的预测(非因果估计)
如果您想了解更多关于 DoWhy 包的信息,参考资料中提供了文档的链接。继续前进。
双重稳健估计
我们使用了回归模型和基于倾向评分的方法来控制混杂因素,但也有一种方法可以同时使用这两种方法来确保我们的因果估计更加稳健。它被称为双重稳健估计。****



这里先看第一部分,It 1 (X)是正确的,P(x)是错误估计的,那么 E[Ti (Yi - 1 (Xi))]=0,因为 Ti 只选取治疗过的病例,对于那些(Yi - 1 (Xi))非常接近 0 的病例。所以在 1 (X)中是正确的,消除了 P(x)是正确的必要性。重新排列这些术语,我们还可以证明,当 P(x)正确时,我们不需要(X)是正确的。
因果估计= 1619.51
回归不连续设计
只要有明确的阈值将治疗与对照分开,就可以使用回归不连续设计。基于阈值,我们可以通过将刚好低于阈值的人群识别为对照组,将刚好高于阈值的人群识别为治疗组来减少偏倚。例如,在移动游戏中,当分数导致级别改变时,刚好低于某个分数的玩家可以被识别为控制,而刚好高于该分数的玩家可以被识别为治疗。我们可以确定水平对其他技能的影响,因为刚好低于和高于阈值的人可能具有相似的技能。
实施 RDD 非常简单,只需创建一个虚拟变量,其值低于阈值为 0,高于阈值为 1。让我们实现它来研究游戏中等级变化对参与度的影响,我们的阈值是 100 分(等级变化)。该数据包含游戏分数和参与度分数。
****
参与度增加了 7.66 个单位,等级变化为 100 分。由于我们对阈值处的效果更感兴趣,因此与远离阈值的数据点相比,更好地拟合阈值周围的数据点是有意义的。我们可以使用线性回归中的权重来做到这一点。我们将使用下面的内核作为线性回归模型的权重
****
可以看出,通过使用在阈值附近具有较高权重的加权线性回归,水平变化的影响增加到 8.3。

这里和上一篇文章中讨论的所有这些方法都是有用的,但不是完全可靠的。我们可能无法完全消除偏倚,因为总有一些无法观察到的混杂因素影响我们的设计,但通过使用不同的方法控制混杂因素来减少一些偏倚是可能的,而且肯定比预测性推断更可靠。因果洞察力可以引发 A/B 测试,这是因果推理的黄金标准。
在上一篇文章中,我们介绍了因果推断的基础,在本文中,我们介绍了获得因果估计的方法。这些文章都旨在获得平均的治疗效果,但更好的是在治疗单元上获得更个性化的治疗效果,也称为异质治疗效果。例如,如果平均治疗效果告诉我们是否应该推出电子邮件营销活动,而异质治疗效果告诉我们应该将哪些客户作为营销活动的目标,以提高我们的投资回报率。在我们的下一篇文章中,我们将讨论异质性治疗效果和评估方法。
如果你喜欢这些文章,不要忘了慷慨鼓掌,订阅我的下一篇文章发表时会收到通知。
参考
- 【https://matheusfacure.github.io/python-causality-handbook/ 号
- https://www.bradyneal.com/causal-inference-course
- https://theeffectbook.net/
- **【https://www.aeaweb.org/articles?id=10.1257/app.1.1.164 **
- https://academic.oup.com/ejcts/article/53/6/1112/4978231
- https://www.masteringmetrics.com/
- https://www . Amazon . in/Applied-Data-Science-Transforming-Actionable/DP/0135258529
- https://mixtape.scunning.com/
- https://microsoft.github.io/dowhy/
面向数据科学家和商业领袖的指标设计
原文:https://towardsdatascience.com/metric-design-for-data-scientists-and-business-leaders-b8adaf46c00
公制设计最难的部分是什么?
为了做出好的数据驱动的决策,你需要三样东西:
需求#2 和#3 已经写了很多(包括由 me 写的),但是需求#1 呢?
现在收集数据比以往任何时候都容易,许多领导感到有压力要把数字拖到每个会议上。不幸的是,在喂养狂潮中,他们中的许多人没有给予 公制设计 应有的重视。在那些愿意付出努力的人当中,大多数人都是边走边补,就好像这是全新的一样。
它不是。
心理学——对思维和行为的科学研究——已经花了一个多世纪的时间来应对试图测量尚未正确定义的模糊数量的危险,因此该领域已经学到了一些坚实的金块,商业领袖和数据科学家在设计指标时借鉴这些金块是明智的。
如果你不相信公制设计很难,拿起笔和纸。我挑战你写下幸福的定义,它是如此的铁,以至于没人会反对你衡量幸福的方式…

很棘手,对吧?现在,试着用人们日常使用的一些抽象名词,比如“记忆”、“智力”、“爱”和“注意力”等等。我们中的任何一个人都了解自己,这简直是不可思议的,更别说相互了解了。
然而,这恰恰是心理学研究者为了取得科学进步而必须清除的第一个障碍。为了研究心理过程,他们必须创造出精确的、可测量的替代物——度量标准。那么,心理学家和其他社会科学家是如何看待公制设计的?

图片来源: Pixabay 。
像心理学家一样思考
你如何严谨、科学地研究那些你无法轻易定义的概念?像注意力、满意度、创造力这样的概念?答案是……你没有!相反,你操作。为了这个例子的目的,让我们假设你对测量用户幸福度感兴趣。
什么是操作化?
什么是操作化?我已经为你写了一篇关于它的介绍文章这里,但结果是当你操作时,你首先对自己说“我永远不会去衡量幸福,我已经平静地接受了这一点。”哲学家已经研究这个问题几千年了,所以你不可能突然提出一个让所有人都满意的定义。**
*
接下来,你将你的概念的可测量的本质提取到一个代理中。
永远记住,你实际上并不是在衡量幸福。或者记忆。或者注意力。或者智力。或者任何其他诗意的模糊词语,不管它听起来有多伟大。
既然我们已经接受了我们永远无法衡量幸福和它的朋友的事实,那么是时候问问我们自己为什么我们会首先考虑这个词了。这个概念是什么——以模糊的形式——看起来与我们想要做出的决定相关?什么混凝土(和可获得的!)信息会让我们选择一个行动方案,而不是另一个?(当你在开始之前心中有了动作,度量设计就容易多了。如果可能的话,在试图设计一个指标之前,考虑一下可能的决策。)

然后,我们提炼出我们所追求的核心理念,以创建一个可衡量的代理——一个捕捉我们所关心的核心本质的指标。
在命名之前,先定义您的指标。
现在有趣的部分来了!我们可以用任何我们喜欢的名字来命名我们的指标:“博客工作”或者“用户快乐”或者“X”或者其他什么。
我们被语言警察逮捕没有意义的原因是,无论我们多么努力地设计它,我们的代理将而不是 成为用户快乐的柏拉图式形式。
虽然它可能适合我们的需求,但重要的是要记住,我们的指标不太可能适合其他人的需求。这就是为什么在关于我们的衡量标准是否捕捉到真正的幸福的无用辩论中争论不休是愚蠢的。并没有。如果你迫切需要某种标准来统治所有人,那么有一首迪士尼歌曲适合你。

让·维默林在 Unsplash 拍摄的照片
我们创建的任何度量标准都只是一个适合我们自己需求的代理(可能没有其他人的需求)。这是我们达到个人目的的个人手段:做出明智的决定或总结一个概念,这样我们就不必每次提到它都写一整段。我们可以在没有语言警察介入的情况下相处得很好。
最难的部分
到目前为止,一切顺利。你只需确定你的决策需要哪些信息,然后你想出一种方法,以一种对你的需求有意义的方式总结这些信息(ta-da,这是你的度量标准),然后给它起你喜欢的名字。对吗?对,但是…
那里的 是 这一切中最难的一部分。继续到下一期来找出它是什么…
关于指标设计的视频
如果你渴望了解更多,请观看我的《与机器交朋友》学习课程中的课程039–047。它们都是几分钟长的短视频。从此处开始,并在附加的播放列表中继续:
感谢阅读!人工智能课程怎么样?
如果你在这里玩得开心,并且你正在寻找一个为初学者和专家设计的有趣的应用人工智能课程,这里有一个我为你制作的娱乐课程:
在这里欣赏课程播放列表,它被分成 120 个单独的一口大小的课程视频:bit.ly/machinefriend
https://kozyrkov.medium.com/membership
附言:你有没有试过在 Medium 上不止一次点击这里的鼓掌按钮,看看会发生什么? ❤️
喜欢作者?与凯西·科兹尔科夫联系
让我们做朋友吧!你可以在 Twitter 、 YouTube 、 Substack 和 LinkedIn 上找到我。有兴趣让我在你的活动上发言吗?使用此表格取得联系。
寻找动手 ML/AI 教程?
以下是我最喜欢的 10 分钟演练:
用于异常检测的度量学习
原文:https://towardsdatascience.com/metric-learning-for-anomaly-detection-7bde550cfa56
如何使用度量学习来检测异常:只有 200 个标记样本的咖啡豆质量评估

图片作者。
异常检测是一项迫切而又具有挑战性的任务,在各行各业都有大量的使用案例。这种复杂性主要是由于任务本身就缺乏数据。
同样,根据定义,异常也是经常变化的,它们可能会以意想不到的形式出现。因此,基于监督分类的方法有:
- 数据饥渴——需要相当多的标记数据;
- 昂贵——数据标注本身就是一项昂贵的任务;
- 费时——你会试图获得必然稀缺的东西;
- 难以维护-您需要反复重新训练模型,以响应数据分布的变化。
如果您想在快速变化的环境中将您的模型投入生产,这些并不是您想要的特性。此外,尽管存在上述所有困难,但与替代产品相比,它们并不一定能提供卓越的性能。在本帖中,我们将详细介绍从这样一个用例中学到的经验。
咖啡豆
Agrivero.ai 是一家为生产商、贸易商和烘焙商提供人工智能解决方案的公司。他们已经收集并标记了超过 3 万张带有各种缺陷的咖啡豆图片——潮湿的、破损的、有缺口的或者有虫子的样本。该数据用于训练分类器,该分类器评估作物质量并突出可能的问题。

咖啡中的异常现象。图片作者。
我们应该注意到异常是多种多样的,因此列举所有可能的异常本身就是一项具有挑战性的任务。在工作过程中,新的缺陷类型出现,拍摄条件发生变化。因此,一次性标记的数据集是不够的。
让我们看看度量学习如何帮助解决这一挑战。
度量学习方法
在这种方法中,我们的目标是在 n 维向量空间中编码图像,然后在推理过程中使用学习到的相似性来标记图像。
最简单的方法是 KNN 分类法。该算法检索给定查询向量的 K 个最近邻,并基于多数投票分配标签。
在生产环境中,kNN 分类器可以很容易地用 Qdrant 矢量搜索引擎替换。

生产部署。图片作者。
这种方法具有以下优点:
- 我们可以从未标记的数据中受益,考虑到标记既耗时又昂贵。
- 相关的度量,例如,精度或召回率,可以在推理期间根据变化的要求进行调整,而无需重新训练。
- 标记有高分的查询可以作为新的数据点被动态地添加到 KNN 分类器中。
为了应用度量学习,我们需要一个神经编码器,一个能够将图像转换为矢量的模型。
从头开始训练这样的编码器可能需要我们可能没有的大量数据。因此,我们将培训分为两步:
- 第一步是训练自动编码器,用它我们将准备一个能够表示目标域的模型。
- 第二步是微调。其目的是训练模型来区分所需的异常类型。

模型训练架构。图片作者。
步骤 1 —未标记数据的自动编码器
首先,我们通过将标签放在一边,在普通的自动编码器架构中预处理了一个类似 Resnet18 的模型。Autoencoder 是由编码器和解码器组成的模型架构,后者试图从前者的低维瓶颈输出中重建原始输入。
没有直观的评估标准来指示该设置中的性能,但是我们可以通过直观地检查重新创建的样本来评估是否成功。

用自动编码器模型重建图像的例子。图片作者。
然后,我们使用编码器将数据的子集编码成 128 维向量,并在这些嵌入和相关标签的基础上创建 KNN 分类器。
虽然结果很有希望,但我们可以通过度量学习进行微调来做得更好。
步骤 2 —使用度量学习进行微调
我们从随机选择 200 个标记样本开始,没有替换。
在此步骤中,模型由自动编码器的编码器部分组成,随机初始化的投影层堆叠在其上。我们应用了来自冻结编码器的迁移学习,并且仅训练具有三重丢失的投影层和在线批量所有三重挖掘策略。
不幸的是,该模型在这种尝试中很快就过度拟合了。在下一个实验中,我们使用了一个在线批量硬策略和一个防止向量空间崩溃的技巧。我们将在后续文章中描述我们的方法。
这一次,它顺利地收敛了,我们的评估指标也有了相当大的改进,以匹配监督分类方法。

具有 KNN 分类器的自动编码器模型的度量。图片作者。

使用 KNN 分类器的微调模型的度量。图片作者。
我们用 500 个和 2000 个样本重复了这个实验,但它只显示出轻微的改善。因此,我们决定坚持 200 个样本——原因见下文。
监督分类方法
我们还想将我们的结果与传统监督分类模型的指标进行比较。为此,Resnet50 模型用大约 30k 标记的图像进行了微调,可用于训练。令人惊讶的是,F1 分数在 0.86 左右。
请注意,我们在度量学习方法中仅使用了 200 个标记样本,而不是监督分类方法中的大约 30k。这些数字表明,在性能没有显著下降的情况下,节省了大量成本。
结论
我们通过度量学习仅使用 0.66% 的标记数据,获得了与监督分类方法相当的结果。这种方法既省时又节省资源,还可以进一步改进。下一步可能是:
- 收集更多未标记的数据,并预训练更大的自动编码器。
- 获取少量图像的高质量标签,而不是成千上万的图像进行微调。
- 在微调步骤中使用超参数优化和可能的逐步解冻。
- 使用向量搜索引擎为生产中的度量学习服务。
我们正在积极研究这些问题,并将继续在本次挑战和其他度量学习用例中发布我们的发现。
如果你想享受这种关于度量学习、相似性学习和向量搜索的有趣讨论,请加入我们的 Discord:
度量是不够的——您需要对 NLP 进行行为测试
原文:https://towardsdatascience.com/metrics-are-not-enough-you-need-behavioral-tests-for-nlp-5e7bb600aa57
通过行为测试跟踪模型的系统性问题
数据科学从业者学到的第一个概念是不同的指标。很早的时候,你就明白准确性不能用于所有场景,优化一个错误的度量标准弊大于利。但是一个好的度量标准足以知道一个模型的行为吗?

不正确度量的典型例子。图片由作者提供。
想象一下下面的场景:你的公司每个月都会收到成千上万份求职申请,实际上不可能阅读其中的每一份。此外,雇佣数十名人力资源员工只是为了筛选他们,成本太高。因此,您希望创建一个 NLP 模型来筛选简历,并丢弃那些明显不适合的简历。
你收集一个数据集(让我们假设你可以访问历史申请和招聘决定)并建立模型。对不同指标(精度/召回率/等)的评估显示了不错的性能。
问题 1 :您认为使用公司内部数据建立模型有问题吗?
部署几周后,您注意到一些可疑的事情——该模型强烈偏好计算机科学专业毕业的候选人,而不是计算机工程专业。尽管它们都完全有效。你做一个快速测试——随机抽取几个应用程序,用计算机科学代替计算机工程,反之亦然。结果很明显:同样的简历,只是用计算机科学取代了计算机工程,得分要高得多。您刚刚发现您的系统存在系统性问题,并开始担心——还有更多这样的问题吗?

相同的工作申请除了同等学历使模型出错——图片由作者提供。
多加点数据就行了,兄弟/姐妹。
解决问题的第一个想法是添加更多的数据。但是你没有。即使你有——你怎么知道他们解决了问题?
事实上,现实是收集/注释新数据可能非常昂贵,甚至是不可能的。作为一名数据科学家,你必须意识到这一点,并提出其他解决方案。
鲁棒性标准
我们刚刚发现了模型的一个严重问题。在我们投入工作去解决它之前,让我们深吸一口气,想一想——我们可能还有什么其他问题?
- 性别/种族偏见——你的模型是否歧视男性/女性或特定的国籍?
- 对等词/同义词—如果候选人将“良好的 python 知识”替换为“良好的 python 3 知识”,您的模型会有什么反应?
- 技能分级——您的模型是否会给“非常好的知识”、“好的知识”和“基础知识”分配更高的分数。形容词理解充分吗?具有“特殊技能”的候选人不应被评为低于具有“基本技能”的候选人。
- 句子排序—如果我们颠倒工作经验的顺序,模型预测是否一致?
- 错别字——我见过很多模型,一个完全不重要的单词中的错别字完全改变了模型预测。我们可能会认为工作申请不应该包含错别字,但我们都同意,一般来说,这是 NLP 中的一个问题
- 否定——我知道这很难。但是如果你的任务需要理解它们,你会度量它吗?(例如,“我没有犯罪记录”对“我有犯罪记录”或“我完成了对我没有完成”。双重否定怎么样?
你可能会说这些标准很难满足。但另一方面,想象一下你在面试中被拒绝,而公司在给你的反馈中说,“我们预计你的工作经历会被颠倒过来”。我会不高兴的。
人工智能将对我们的日常生活产生越来越大的影响,我们这些从业者需要非常认真地对待我们的工作。
行为测试
所以我们需要行为测试。度量仅仅显示了模型质量的一部分,并且仅仅是一个高度聚合的数字。他们对内部模型假设、偏见和推理只字不提。即使你仍然进行交叉验证(这对深度学习来说是不切实际的),你也不知道你的模型有多可靠。行为测试是一种评估影响模型预测的因素的系统方法。在继续之前,让我们看看更多的例子。
这个概念是由 Riberio 等人在论文“超越准确性:NLP 模型的行为测试与清单”中引入的。作者将我们关于模型能力的讨论形式化,并提出了一个系统的测试框架。让我们来看看所选择的子集。
方法学
清单论文介绍了三个主要贡献:
- 能力——我们可能想要检查的特定行为(测试什么)
- 测试类型——定义明确的测试类型(如何测试)
- 模板和抽象——用于执行行为测试的 python 包
好的一面是,我们仍然可以使用作者提出的方法,而不使用他们的库(如果出于任何原因我们不想这样做)。在这篇文章中,我将着重于定义行为测试本身;如何使用清单库是第二部分的主题。

今天,带你的狗去看有执照的行为学家是很正常的。是时候用人工智能模型做同样的事情了。由命运威恩斯在 Unsplash 上拍摄的照片
模型能力
一个模型的能力是一个特定的对抗性设置,模型很容易受到它的攻击。我们对不同的任务使用不同的能力。功能的具体选择将取决于模型正在解决的问题,但是一些通用的功能可以应用于大多数模型。
- 词汇/分类法:模型如何处理用同义词/反义词替换单词。在情感分析中,我们期望用“惊人的”代替“伟大”不会显著改变预测。另一方面——用“糟糕”代替“伟大”应该会在特定的方向上产生影响。
- 鲁棒性:在次要单词中添加小的错别字应该不会显著影响预测。在我们的招聘例子中,以前工作场所的洗牌顺序也不应该有影响。这一点在处理非正式文档时显得尤为重要,因为在非正式文档中,打字错误和不相关的信息更为常见。
- NER:如果我们改变一个人的名字,应该不会影响预测。比如“迈克是个很棒的服务员”vs“史蒂夫是个很棒的服务员”。我们不想选择麦克而不是史蒂夫或其他方式。位置也是如此。想想“去纽约的机票太贵了”和“去凤凰城的机票太贵了”。
- 时态:模型理解一系列的动作吗?它能区分过去和未来吗?

该模型错误地假设动作序列与作者在文本图像中的顺序相匹配。
- 否定:这是一个大问题。“食物很糟糕”和“食物还不错”是一个简单的例子。但是双重否定呢?
p的真值等于not(not(p))。可以处理吗?即使对人类来说,这也很有挑战性! - 共指:模型是否正确地链接了不同的主题?“约翰是一名管道工。凯特是一名会计,她每小时挣 50 美元。他每小时挣 20 美元。问题:“约翰挣多少钱?”
- 逻辑:模型是否理解基本的逻辑规律?“猿是哺乳动物。哺乳动物是动物。猿是动物吗?”
- 还有一些,但足够了:)
哇,我们怎么能测试它们呢?他们似乎很复杂!对于救援来说,CheckList 附带了预定义的测试类型和构建它们的框架。让我们仔细看看考试类型。
测试类型
为了有助于系统测试,作者还提出了三种不同类型的测试。每种测试类型都可以与任何功能一起使用(但是,当然,它们中的一些可能更适合/不太适合)。定义测试类型有助于我们决定如何测试一项功能。不充分的测试弊大于利!
最低功能测试(MFT)
它们可以与单元测试相提并论——在隔离环境中执行的单个小功能。对于行为测试,我们可以将 MFT 定义为一个小型的、合成的、有针对性的测试数据集。对于每条记录,我们都知道预期的答案,因此我们可以轻松地将其与模型预测进行比较。
让我们看一个用于情感分析的词汇能力的例子。

简单的 MFT (vocab 功能)。图片由作者提供。
您可以注意到一些事情:首先,测试结果被报告为失败测试的一部分——失败率。在大多数现实场景中,我们并不期望我们的模型是 100%正确的。可接受的误差量会有所不同——所以这个框架不会把任何东西强加给我们。此外,如果我们对现有模型进行行为测试,结果很有可能会很差。在这种情况下,我们不希望只报告失败——如果修复模型需要时间,那么最好跟踪一段时间内的失败率。
其次,测试用例仅仅是为了行为测试而产生的。它们不是来自训练/val/测试数据集。当然,可能偶然会有一些重叠,但原则上,只为 MFT 生成案例。
第三,有很多冗余。六分之五的案例遵循“食物是 ____”的模式。它看起来像是大量的手工工作。但是情况并没有那么糟糕——check list 为快速案例开发提供了工具,而不需要手动输入所有内容。
问题二:
MFT 可以测试哪些能力?我们已经看过词汇了。你能想到更多吗?
不变性测试
不变性测试检查引入测试用例的修改是否没有改变预测。我们从单个输入例子(合成的或真实的)开始,并引入各种扰动。最简单的方法是在文本中引入一个随机的逗号。这种修改不应该影响模型预测。
INV 的可爱之处在于我们不需要带标签的数据。我们检查模型的一致性和稳健性——我们不关心实际预测是否正确——只要它是稳定的。这与 MFT 不同。
让我们看一个例子。我们取一个样本(用蓝色突出显示)并计算一个预测。它是正的(同样,我们不关心它是否是一个实际的类)。现在我们通过用不同的值替换一个国家名称来引入扰动。显而易见,该模型改变了韩国和伊朗的行为——因此它对原产国并不稳健。我们得出结论,该模型对特定国家有偏见,因此不公平。

简单的 INV 测试。更换国家不应该改变模型预测——因为情绪应该通过“是经典的”短语来识别——作者的图像。
问题 3:
INV 可以测试哪些能力?
方向期望
最后一种测试是方向性期望测试。在这里,我们预计预测将在一个特定的方向上变化。举个例子:如果加上一句“我看过更好的电影。”对一个影评来说,预测的情绪应该不会更好。当我们知道特定的修改应该将模型推向特定的方向时,这样的测试是有益的。
回到工作申请的例子:让我们考虑一个拥有 3/5 python 技能的候选人。他从模型中得到的分数是 0.53。假设我们用 4/5 代替;预测值变为 0.52。从技术上来说,它更少,但差异如此之小,以至于我们可以说它是一个噪音——这就是为什么我们定义一个余量——“我们认为多少变化是真正的变化”。假设边距设置为 0.1。
这非常类似于早期停止中的 delta 参数。
更进一步,5/5 的替换将分数变为 0.67——因此该模型确实更倾向于高技能开发人员。

将边距设置为 0.1 意味着我们期望预测在范围[0.43;1] —作者图片。
测试矩阵和定义测试
我们定义了功能和测试它们的不同方法。正如我们已经说过的,特定的能力可以通过一种或多种类型的测试来测试,从而创建一个矩阵。
问题 4:
尝试为工作应用示例定义一个测试套件。考虑应该测试哪些功能,哪些类型是合适的。如果你不确定分配能力/测试类型,只列出具体的主题作为开始是可以的;)此外,还要考虑可接受的错误率。您可以从以下内容开始:
- 这个模型不应该支持计算机工程胜过计算机科学(反之亦然)。能力:NER,测试类型:发票
- 模型应该拒绝所有 python 技能 1/5 或 2/5 的候选人。能力:词汇/分类,测试类型:MFT,目录
- …
结束和进一步阅读
今天到此为止。在下一篇文章中,我们将使用 CheckList 为一个模型构建一个真实的测试套件。定义行为测试是非常有趣和令人满意的,并且可以显著提高对模型性能的理解。可以在这里查阅论文原文:https://homes.cs.washington.edu/~marcotcr/acl20_checklist.pdf
敬请期待!
我在 medium.com 的所有收入中,有 1.5%捐给了 Stripe 的气候倡议。
回归问题中不确定性评估的度量
原文:https://towardsdatascience.com/metrics-for-uncertainty-evaluation-in-regression-problems-210821761aa
如何用有效性、锐度、负对数似然和连续排序概率得分(CRPS)度量来评估不确定性

圣地亚哥·拉卡尔塔在 Unsplash 上拍摄的照片
许多现实世界的问题需要对未来结果进行不确定性估计,以便更好地做出决策。然而,大多数最新的机器学习算法只能估计单值预测,该预测通常是假设与真实结果匹配良好的条件分布的平均值或中值。单值预测不能暗示的是预测有多有把握。因此,最近提出了更复杂的方法,如 NGBoost 、 PGBM 、贝叶斯方法和基于深度学习的方法,可以估计每个数据点的条件分布。由于这些方法估计条件分布,就产生了如何评价这些方法的问题。显然,常用的准确性指标,如单点预测的 RMSE 或 MAE 并不适合。因此,在本文中,我展示了几种评估估计条件分布的模型的方法,以及如何评估不直接估计条件分布但提供预测区间的分位数回归。具体来说,我评估了三种算法:
- 带分位数回归的 LightGBM
- NGBoost——一种具有概率预测的梯度推进算法
- 模拟平均值和标准差的概率回归神经网络
并使用四个度量标准进行不确定性评估:
- 有效性——评估概率环境中分位数和偏差的可靠性
- 锐度—估计概率的集中程度(预测区间)
- 负对数似然(NLL)-给定条件分布的推断参数时,观察数据出现的似然性
- 连续等级概率得分(close 估计条件分布与观察点的接近程度
数据
我正在使用由 scikit-learn 提供的加州住房数据集。这是一个小数据集,有 20640 条记录,只有 8 个数字特征。目标变量是加州各区的房价中值,以十万美元计。
from sklearn.datasets import fetch_california_housingcalifornia_housing = fetch_california_housing(as_frame=True)#get the data
data = california_housing.frame
data

数据快照。作者图片
#get the target
target = california_housing.target
target

目标变量快照。作者图片
像往常一样,数据被分成训练集和测试集。我将使模型适合训练集,并在测试集上对它们进行评估。因为本文的重点是评估指标,所以我没有执行任何超参数调优,也没有应用算法的默认参数。
X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.2, random_state=42, shuffle=True)
我将深度学习解决方案的功能标准化
x_scaler = StandardScaler()
X_train_scaled = x_scaler.fit_transform(X_train)
X_test_scaled = x_scaler.transform(X_test)
算法
我选择了三种算法进行评估。
带分位数回归的 LightGBM
LightGBM ,一种梯度推进算法,广泛应用于机器学习社区。使用现有算法获得预测区间的最直接方法可能是构建至少两个分位数回归模型,以针对一些低和高条件分位数。例如,90%的预测区间需要拟合两个具有 5%和 95%分位数水平的分位数回归。幸运的是,LightGBM 实现了分位数回归,但是任何其他支持分位数回归的算法也可以。
我定义了 13 个分位数水平,建立了 14 个模型。一个模型预测中值价格的预期平均值,其余 13 个模型预测给定分位数水平的中值价格:
quantiles = [0.05, 0.1, 0.15, 0.2, 0.3,
0.4, 0.5, 0.6, 0.7, 0.8,
0.85, 0.9, 0.95]#save quantile predictions
quantile_predictions = {}train_data = **lgb.Dataset**(X_train,
label=y_train,
free_raw_data=False)#first model that predicts expected mean
params = {'objective': 'regression'}
lgb_model = **lgb.train**(params=params,
train_set=train_data,
num_boost_round=100)lgb_prediction = **lgb_model.predict**(X_test)#train models on quantiles
for quantile in **quantiles**:
print(f"modeling quantile {quantile}")
params = {'objective': 'quantile', 'alpha': quantile}
lgb_model = lgb.train(params=params,
train_set=train_data,
num_boost_round=100)
pred = **lgb_model.predict**(X_test)
quantile_predictions[quantile] = pred
NGBoost
NGBoost 是一种估计条件(正态)分布参数的梯度推进算法。
ngb = **ngboost.NGBoost**(Base=learners.default_tree_learner, Dist=distns.Normal, Score=scores.LogScore, natural_gradient=True, verbose=True)
ngb.fit(X_train, y_train)**#predicted mean**
**ngb_mean_pred** = ngb.predict(X_test)**#predicted distribution parameters**
**ngb_dist_pred** = ngb.pred_dist(X_test)
回归神经网络
回归神经网络使用张量流概率框架估计条件(正态)分布的参数。
下面是神经网络的架构:
损失函数被定义为基础正态分布的负对数似然性:
def **nll_loss**(y, distr):
return -distr.log_prob(y)
以下函数接收由两个节点组成的输入,一个节点用于平均值,另一个节点用于标准差:
def **model_distribution**(params):
return tfd.Normal(**loc**=params[:,0:1],
**scale**=tf.math.softplus(params[:,1:2]))
均值和标准差分别建模。
平均:
inputs = layers.Input(shape=((len(X_test.columns),)))hidden1 = layers.Dense(100, activation = "relu", name = "dense_mean_1")(inputs)
hidden2 = layers.Dense(50, activation = "relu", name = "dense_mean_2")(hidden1)
**output_mean** = layers.Dense(1, name = "mean_output")(hidden2)
标准偏差:
hidden1 = layers.Dense(100,activation="relu", name = "dense_sd_1")(inputs)
hidden1 = layers.Dropout(0.1)(hidden1)
hidden2 = layers.Dense(50,activation="relu", name = "dense_sd_2")(hidden1)
hidden2 = layers.Dropout(0.1)(hidden2)
hidden3 = layers.Dense(20,activation="relu", name = "dense_sd_3")(hidden2)
**output_sd** = layers.Dense(1, name = "sd_output")(hidden3)
两者被连接并传播到分布层:
**mean_sd_layer** = layers.Concatenate(name = "mean_sd_concat")([output_mean, output_sd])
**dist** = tfp.layers.DistributionLambda(model_distribution)(mean_sd_layer)
最后一步是编译分布模型,并使用分布模型分别预测均值和标准差。
dist_mean = **tfp.layers.DistributionLambda**( make_distribution_fn=model_distribution, convert_to_tensor_fn=tfp.distributions.Distribution.mean)(mean_sd_layer)
dist_std = **tfp.layers.DistributionLambda**( make_distribution_fn=model_distribution, convert_to_tensor_fn=tfp.distributions.Distribution.stddev)(mean_sd_layer)**model_distr** = **models.Model**(inputs=inputs, outputs=dist)
**model_distr**.compile(optimizers.Adagrad(learning_rate=0.001), loss=nll_loss)#the model for mean
**model_mean** = **models.Model**(inputs=inputs, outputs=dist_mean)#the model for std **model_sd** = **models.Model**(inputs=inputs, outputs=dist_std)history = **model_distr.fit**(X_train_scaled, y_train,
**epochs**=150,
verbose=1,
**batch_size** = 2**7,
**validation_data**=(X_test_scaled,y_test))#mean predictions
dl_mean_prediction = **model_mean.predict**(X_test_scaled).reshape(-1)
dl_mean_prediction#stand deviation predictions
dl_sd_prediction = **model_sd.predict**(X_test_scaled).reshape(-1)
dl_sd_prediction
韵律学
下面描述的四个指标是研究中最常用的指标。基于预测区间直接估计的方法,如分位数回归或共形分位数回归,通常使用覆盖率和区间长度(锐度)指标(论文),估计条件分布的方法使用负对数似然(NLL) ( 论文 1 、论文 2 )或 CRPS ( 论文)
有效性/覆盖范围
对于某个水平为 0 < α < 1 的分位数预测,我们期望覆盖(100*α)%的观察值。例如,如果α是 0.8,我们期望分位数预测覆盖 80%的观察值。
为了更直观,让我们使用正态分布生成一些数据,并检查某个分位数级别覆盖的数据比例,比如 10%或α为 0.1
r = stats.norm.rvs(size=1000)
我们需要找出 10%分位数的 Z 值:
quantile = 0.1
percent_point = stats.norm.ppf(q = quantile)
#-1.281552
现在,让我们找出 10%分位数覆盖生成的观察值的情况的经验比例:
len(r[r < percent_point])/len(r)
#10.5%
无效条件分位数的一个明显标志是经验分位数偏离名义分位数太多。
让我们计算 LightGBM 分位数回归的有效性:
empirical_quantiles = []
for quantile in **quantiles**:
empirical = (**quantile_predictions[quantile] >= y_test**).mean()
empirical_quantiles.append(empirical)
并将结果可视化:
plt.figure(figsize=(16, 10))
sns.set_context("notebook", font_scale=2)
sns.lineplot(x = quantiles, y = quantiles, color = "magenta", linestyle='--', linewidth=3, label = "ideal")
sns.lineplot(x = quantiles, y = empirical_quantiles, color = "black", linestyle = "dashdot", linewidth=3, label = "observed")
sns.scatterplot(x = quantiles, y = empirical_quantiles, marker="o", s = 150)
plt.legend()
plt.xlabel("True Quantile")
plt.ylabel("Empirical Quantile")
_ = plt.title("Reliability diagram: assessment of quantile predictions")

作者图片
经验分位数(黑线)非常接近理想分位数(洋红色线),这表明 LightGBM 生成的分位数预测是有效的。
清晰度/间隔长度
完美的概率预测应该为单个值估计 100%的概率。实际上,条件分布将为每个可能的结果分配不同的概率。锐度是预测密度紧密程度的量度。区间长度越窄,我们对预测就越有信心。
锐度计算的程序如下:
- 选择用于评估的预测间隔
- 迭代预测每个观察值的平均值和标准差(NGBoost 和神经网络)
- 对于测试集中的每个观察值,计算区间的边界
- 计算间隔的长度
- 计算所有间隔的平均值
为了更直观,让我们举两个预测的例子:
- 预测 1:均值= 0.5,标准差= 0.08
- 预测 2:均值= 0.5,标准差= 0.15
并比较它们对应的覆盖 60%概率的预测区间的宽度:

作者图片
绿色虚线是间隔的边界。60%的概率集中在两条线之间。第二预测比第一预测具有更高的预测不确定性,第一预测的区间更窄。
知道了分布的参数,我们可以使用 scipy 包计算区间宽度:
probability = 0.6
interval = **stats.norm.interval**(probability, loc = 0.5, scale=0.08)
interval
#(0.43267030131416684, 0.5673296986858332)width = interval[1] - interval[0]
#0.135
在分位数回归的情况下,过程略有不同。我们有每个分位数的预测,因此为了计算对应于 60%的间隔宽度,我们取 20%和 80%的两个分位数,并使用预测中的差异计算这两个分位数之间的间隔。
最后,我们可以在不同的时间间隔使用清晰度指标来比较三种模型:

作者图片
随着区间覆盖率从 60%增加到 90%,LightGBM 分位数回归的平均预测区间以比其他算法高得多的速度增加。对于非常高的区间覆盖率(低分位数水平),分位数回归器可能不会给出最佳结果。NGBoost 在预测密度方面表现出最高的可信度。
也有可能某个算法对预测很有信心,但仍然是错误的。我们如何衡量这一点?我们可以计算不在给定区间的预测边界内的真实观测值的比例。
过程与上面几乎相同,但是我们不是计算平均间隔宽度,而是计算间隔内缺失观测值的数量。

作者图片
随着时间间隔的增加,缺失观测值的比例下降,但是正如我们所看到的,LightGBM 分位数回归具有最多的有偏观测值,其次是 NGBoost。
负对数似然
似然函数将观测数据的联合概率描述为所选统计模型参数的函数
该指标适用于生成条件概率分布的算法。NGBoost 和回归神经网络都使用 NLL 作为损失函数。
正态分布的 NLL 定义为正态分布概率密度函数的负对数:

使用(正态)分布的参数在每次观察时评估 NLL。为了比较算法之间的 NLL,我们对所有观测值的 NLL 进行平均。(平均)NLL 越低,拟合越好。
以下是回归神经网络和 NGBoost 的第一次观察及其相应 NLL 的预测分布的直观比较:

让我们看一个例子。观测值为 4,正态分布的均值和标准差为 3 和 1。这个观察的 NLL 是什么?
#first let's calculate the likelihood#using the formula
L1 = (1/np.sqrt(2 * np.pi * 1**2)) * np.exp ( - (4 - 3)**2/(2 * 1**2))#using the scipy norm pdf function
L2 = stats.norm.pdf(4, loc = 3, scale = 1)
#0.2419707assert(L1 == L2)#NLL
-np.log(L2)
#1.418939
NGBoost 的 NLL 可以用几种方法计算:
NGBoost 提供了函数 pred_dist 返回一个对象,该函数提供了一个 logpdf 函数来计算每个观察的对数似然性。得到 NLL 只是否定结果。最终 NLL 是单个 NLL 的平均值
ngb_dist_pred = **ngb.pred_dist**(X_test)
nll_ngboost = **-ngb_dist_pred.logpdf**(y_test)
**nll_ngboost.mean()**
**#-4.888**
第二种方法是从 ngb_dist_pred 对象获取每个条件分布的参数,并手动计算 NLLs。例如,可以如下提取对应于第一观察的条件分布的参数:
**ngb_dist_pred[0].params**
#{'loc': 0.47710276090295406, 'scale': 0.0022844303231773374}
所有观测值的参数都可以这样提取:
**ngb_dist_pred.params**
#{'loc': array([0.477,...,1.517]),
#'scale': array([0.002,...,0.002])}
在回归神经网络的情况下,我们迭代所有观察值及其相应的分布参数:
#extract mean predictions
dl_mean_prediction = **model_mean.predict**(X_test_scaled).reshape(-1)#extract standard deviations
dl_sd_prediction = **model_sd.predict**(X_test_scaled).reshape(-1)#iterate over all observations
nll_dl = []
**for** (**true_mean, mean_temp, sd_temp**) in zip(**y_test**,
**dl_mean_prediction**,
**dl_sd_prediction**):
nll_temp = **-stats.norm.logpdf**(**true_mean**,
**loc** = mean_temp,
**scale** = sd_temp)
nll_dl.append(nll_temp)**#NLL**
**np.mean(nll_dl)**
#**-1.566**
就 NLL 而言,NGBoost 得到的值(-4.888)低于回归神经网络(-1.566),因此我们可以得出结论,NGBoost 具有更好的拟合性。
连续分级概率得分
连续排序概率得分,也称为 CRPS,是一种在不知道数据真实分布的情况下,衡量建议分布如何逼近数据的得分
下面是链接,它很好地解释了 CRPS 背后的公式。简单地说,CRPS 测量对应于观察值的预测分布和理想分布之间的平方距离。
我用 properscoring 软件包计算了 CRPS
#**Computes the CRPS of observations x relative to normally distributed with mean, mu, and standard deviation, sig.**import properscoring as ps
**crps_ngboost** = **ps.crps_gaussian**(y_test,
ngb_dist_pred.params["loc"],
ngb_dist_pred.params["scale"]).mean()
**crps_dl** = **ps.crps_gaussian**(y_test,
dl_mean_prediction,
dl_sd_prediction).mean()(crps_ngboost, crps_dl)
#(0.001, 0.0266)
NGBoost 具有比回归神经网络小得多的 CRPS,这再次表明 NGBoost 具有更好的适应性。
结论
在本文中,我介绍了评估回归问题中预测不确定性的四个指标。有效性和锐度最适合直接估计预测区间的方法,如分位数和保形回归。负对数似然和 CRPS 用于比较模拟条件分布的算法。
完整的代码可以从我的 Github repo 下载
感谢阅读!
推荐系统的度量:综述
原文:https://towardsdatascience.com/metrics-of-recommender-systems-cde64042127a
推荐系统的度量不同于传统的度量。在这里,我们来了解一下 RecSys 领域中广泛使用的 9 个此类指标。

推荐系统的度量不同于传统的度量,如准确性,因为这些度量主要是在预测的排序列表上累积工作,而不是单个预测的分数。这是因为,对于推荐系统的场景,我们需要优化的业务需求几乎总是与作为一个整体的一组预测联系在一起,而不是单独的。也就是说,让我们来看 9 个对这个领域非常重要的指标。
1.精度@K
这与普通精度非常相似,只是当按照您想要的方式排序时,它计算前 k 项的精度。通过这种方式,您可以灵活地改变 K,并查看精度分数是如何变化的。

它在检索领域有很多用途。一个非常常见的用途是根据某个查询的前 10 个结果来衡量搜索引擎的性能。
2.回忆@K
类似于通常的召回度量,并且非常类似于上面的 Precision@K 度量。与上面的公式相比,它只有一个小的改动。

这在只有几个相关条目的情况下很有用,我们当然希望这些条目在最前面。一个很好的例子是通过用户点击一列项目来衡量性能。用户在商品上的大部分点击意味着推荐是有效的,并且会有很高的召回率。
3.地图@K
MAP@K 或 Mean Average Precision @ K 是 Precision @ K 的高级版本。它擅长对精度进行整体测量,而不是仅基于 K 的一个值进行度量。我们先来看看 Average Precision @ K。

这里, N 表示前 K 个项目中相关项目的总数。 rel(i) 是在第个位置的项目的相关性。对于一个简单的例子,它要么是 1(相关),要么是 0(不相关)。MAP@K 只是所有 AP@K 在所有查询中的平均值。它是上述两个指标的更好替代方案。
4.维护、修理和更换
MRR 或平均倒数排名是每个查询的第一个相关项目排名的倒数的平均值。写成一个公式,它是这样的

这里, rank_i 表示第 i 个查询的第一个相关项目的等级。这是一个非常简单的指标。也许太简单了,以至于不能被广泛采用。然而,对于有 1 个完美/正确答案的推荐系统来说,这是完美的。对于像电子商务这样有多个相关项目的场景来说,这可能不是一个好主意。
5.r 分数
现在我们转移到一个稍微不同的领域,在这里我们不直接评估排名列表。直接评估列表有时会让我们错过列表中第一项和第二项有多接近(就置信度而言)这样的信息。决定系数,也称为 R,对于回归型问题是一个非常方便和有用的度量。

这里,分子表示残差的和(真实预测),分母表示平方和(N *方差)。r 可以提供一个模型的建议与地面实况或一些其他模型的建议有多接近的信息。
6.皮尔逊系数
皮尔逊相关系数遵循与 R 相同的性质,尽管有不同的基本方法,但它有助于我们测量两组数据之间的相似性。其核心是两个变量之间的归一化协方差——它们的协方差和它们的标准差的乘积之间的比率。

这里,x 和 y 是我们的两组数据。n 是数据的大小。皮尔逊评分在过去被广泛用于老派的协同过滤来计算用户或项目之间的相似性。
7.斯皮尔曼系数
假设您正在使用皮尔逊系数计算两个模型的推荐集之间的相似性,并稍加改动。我们不是计算原始项目分数的相关性,而是计算项目在集合中的排序。这就是斯皮尔曼系数的本质。它是两个不同集合之间项目等级的相关性。

这里, R_x 和 R_y 是每个I’项在集合 x 和 y 中的等级。平均秩显然是 N/2。它可用于比较任何两个排序的项目列表。
8.NDCG
NDCG 或归一化折扣累积增益是信息检索中广泛使用的度量。它用于计算一组有序项目的累积分数。该度量的“折扣”部分惩罚出现在较低等级的较高相关性的项目。归一化的 DCG 取 DCG 直到某个等级“p ”,并将其除以通过对列表排序可获得的更高可能的 DCG(通过相关性分数对列表排序将最相关的项目放在顶部,这将给出最佳的 DCG)


这里 rel_i 是第I’位置的项目的相关性。 IDCG_p 是理想的或最高可能的 DCG,直到秩 p
9.肯德尔·陶
Kendall Tau 是另一种等级相关性度量,在它测量什么的意义上类似于 spearman 系数。但它的公式与我们迄今所见的任何公式都大不相同。肯德尔τ系数定义为

为了解释一致,让我们取两个列表 X 和 y 中的一对(item i,item j)设 X_i 是列表 X 中第 i' 项的秩,那么(I,j)是一致的,如果以下任一项为真,否则不一致:
- X _ I<X _ j和Y _ I<Y _ j
- X _ I>X _ jY _ I>Y _ j
基本上,它期望所有的配对在两个集合中遵循相同的顺序。
显然,我们没有涵盖所有内容。还有许多其他指标,尤其是在其他领域,这些指标对于 recsys 应用中的不同使用情形都很有价值。在试图解决问题之前,为特定问题最终确定完美的评估标准是重要的一步。
F1 分数的微观、宏观和加权平均值,解释清楚
用简单的插图理解 F1 分数在多类分类中的微观平均、宏观平均和加权平均背后的概念

图片作者和 Freepik
F1 分数(也称为 F-measure)是一种用于评估分类模型性能的流行指标。
在多类分类的情况下,我们采用平均的方法计算 F1 得分,从而在分类报告中产生一组不同的平均得分(宏观、加权、微观)。
本文着眼于这些平均值的含义,如何计算它们,以及选择哪个进行报告。
内容
(1) 基础知识回顾(可选)(2)设定激励范例*宏观平均 *
(1)概述基础知识(可选)
注意:如果您已经熟悉精确度、召回率和 F1 分数的概念,请跳过这一部分。
精确
外行定义:在我做出的所有正面预测中,有多少是真正正面的?
计算:真阳性数(TP)除以真阳性总数(TP) 和假阳性总数(FP)。

作者的精确公式|图像
回忆
外行定义:在所有实际的正面例子中,有多少是我正确预测为正面的?
计算:真阳性数(TP)除以真阳性总数(TP) 和假阴性总数(FN)。

作者回忆|图片的方程式
如果你比较精确和召回的公式,你会发现两者看起来很相似。唯一的区别是分母的第二项,它对精度是假阳性,但对召回是假阴性。
F1 分数
为了全面评估模型的性能,我们既要考察的精度,也要考察的召回率。F1 分数是考虑这两者的有用指标。
定义:精确和召回的调和平均值,用于模型性能的更平衡的概括。
计算:

F1 得分公式|作者图片
如果我们用真阳性(TP)、假阳性(FP)和假阴性(FN)来表示,我们得到这个等式:

F1 分数的替代等式|作者图片
(2)树立激励的榜样
为了说明平均 F1 分数的概念,我们将在本教程的上下文中使用以下示例。
假设我们已经在包含三个类的图像的多类数据集上训练了一个图像分类模型:AIR plane、 B oat 和 C ar。

图片由宏向量——【freepik.com】宏向量
我们使用这个模型来预测十个测试集图像的类别。以下是原始预测:

我们的演示分类器的样本预测|作者图片
运行sklearn.metrics.classification_report后,我们得到以下分类报告:

分类报告来自 sci kit-学习包|作者图片
带有每个班级的分数(即每个班级的分数)和平均分数的列(橙色)是我们讨论的焦点。
从上面我们可以看到,数据集是不平衡(十个测试集实例中只有一个是‘Boat’)。因此正确匹配比例(又名准确度)在评估模型性能时是无效的。
相反,让我们来看看混淆矩阵,以全面了解模型预测。

困惑矩阵|作者图片
上面的混淆矩阵允许我们计算真阳性( TP )、假阳性( FP )和假阴性( FN )的临界值,如下所示。

根据混淆矩阵计算出的 TP、FP 和 FN 值|图片由作者提供
上表很好地设置了我们来计算三个类中每个类的精度、召回和 F1 分数的每类值。
重要的是要记住,在多类别分类中,我们以一对多(OvR) 方法计算每个类别的 F1 分数,而不是单一的总体 F1 分数,如二进制分类所示。
在这个 OvR 方法中,我们分别确定每个类的度量,就好像每个类有一个不同的分类器。以下是每个班级的指标(显示 F1 分数计算):

然而,与其有多个每级 F1 分数,不如平均它们,以获得一个单一数字来描述整体表现。
现在,让我们讨论导致分类报告的三个不同 F1 平均分数的平均方法。
(3)宏观平均
宏平均可能是众多平均方法中最简单的一种。
宏观平均 F1 分数(或宏观 F1 分数)是使用所有每级 F1 分数的算术平均值(又名未加权平均值)计算的。
这个方法平等地对待所有的类,不管它们的支持值。

宏 F1 分数的计算|作者图片
我们上面计算的值 0.58 与我们分类报告中的宏观平均 F1 分数相匹配。

(4)加权平均
加权平均的 F1 分数是通过取所有班级 F1 分数的平均值,同时考虑每个班级的支持*来计算的。*
ssupport指的是该类在数据集中实际出现的次数。例如,船中的支持值 1 意味着只有一个实际标签为船的观察值。
“权重”实质上是指每一类的支持度相对于所有支持度值的总和的比例。

按作者计算加权 F1 分数|图片
使用加权平均法,输出平均值会考虑到每个类别的贡献,并根据给定类别的实例数量进行加权。
计算出的值 0.64 与我们分类报告中的加权平均 F1 分数相符。

(5)微观平均
微平均通过计算真阳性( TP )、假阴性( FN )和假阳性( FP )的和来计算全局平均 F1 分数。
我们首先对所有类别的 TP、FP 和 FN 值求和,然后将它们代入 F1 方程,得到我们的微观 F1 分数。

微 F1 分数计算|作者图片
在分类报告中,你可能想知道为什么我们的微 F1 分数 0.60 显示为“准确性”,为什么没有一行说明 ' 微平均值'。

这是因为微平均本质上是计算所有观察值中正确分类的观察值的比例。如果我们思考这个问题,这个定义就是我们用来计算总体精度的。
此外,如果我们对精确度和召回率进行微平均,我们将得到相同的值 0.60 。

计算所有微平均指标|作者图片
这些结果意味着,在每个观测值具有一个单标签的多类分类情况下,微 F1 、微精度、微召回、和精度共享相同的值(即,在本例中为 0.60 )。
这解释了为什么分类报告只需要显示单个准确度值,因为微 F1、微精度和微召回也具有相同的值。
微 F1 =准确度=微精度=微召回
*https://kennethleungty.medium.com/membership *
(6)我应该选择哪个平均数?
一般来说,如果您正在处理一个所有类都同等重要的不平衡数据集,使用宏 average 将是一个不错的选择,因为它平等地对待所有类。
这意味着,对于我们涉及飞机、船只和汽车分类的例子,我们将使用宏 F1 分数。
如果您有一个不平衡的数据集,但希望将更多的样本分配给数据集中有更多样本的类,那么最好使用加权平均值。
这是因为,在加权平均法中,每个类别对 F1 平均值的贡献按其大小进行加权。
假设您有一个平衡的数据集,并且想要一个易于理解的总体性能指标,而不考虑类。在这种情况下,您可以选择准确性,这实质上是我们的微 F1 分数。
在你走之前
欢迎您来到和我一起踏上数据科学学习之旅!关注我的 Medium 页面,查看我的 GitHub ,了解更多精彩的数据科学内容。同时,享受解释 F1 分数的乐趣!
* https://kennethleungty.medium.com/membership *
去除微小异常值:使用这种技术,以最小的努力爬上 Kaggle 的高处
我是如何用最小的努力在 Kaggle Titanic challenge 排行榜上取得巨大进步的

微小异常值(作者图片)
我经历了一些不可思议的事情。我在 10 分钟内尝试了几次,在 Kaggle Titanic competition 排行榜上爬了 8000 级。
还有更惊人的消息。我不费吹灰之力就做了这件事。我没有做过特征工程。我没有填写缺失的值。我只用了几根柱子。我没有使用任何复杂的机器学习算法。只是一个简单的决策树。
在这个故事中,我将告诉你让这一切发生的神奇技术。
在我上次提交的时候,我在 14404 个团队中排名第 4057(前 30%)
这个故事的目标不是开发最好的模型,而是如何以最小的努力爬上排行榜。
施展神功前——等级为 12616

Kaggle 排名在前(图片由作者提供)
施完神功——等级 4057!哇哦。

Kaggle 排名(图片由作者提供)
让我为我所使用的技术创造一个术语
去除微小异常值
瞧,这个术语听起来不错。这个术语还不存在。如果你正在读这个故事,那么你可能是第一次看到这个术语。
微小异常值去除技术背后的动机
即使我们有许多技术来改进机器学习模型,但有时你会感觉缺少了什么。你可能会说我们拥有一切——超参数优化、网格搜索,甚至自动 ml。那么到底会少什么呢?
对我来说,缺少的是一种基于直觉的视觉方法。用一种基于直觉的视觉方法来增强所有机器学习优化技术,真的可以给你带来超乎寻常的优势。

左图由马库斯·斯皮斯克在 Unsplash 拍摄,右图由阿里·哈坚在 Unsplash 拍摄
所以让我们看看微观异常值是什么样子的。
定位微小异常值
首先,这里有一些关于我使用的泰坦尼克号数据的模型训练的背景信息。为了简单起见,
- 我只按原样选择了以下领域:PClass、Sex、SibSp、Parch、Fare、Embarked。
- 字段 age 未被采用,因为它包含许多缺失值。
- 没有特征工程
- 所使用的机器学习算法是一个基本的 5 层决策树,具有 70–30 的训练测试分割
这里显示的是基于训练数据集和决策树算法的决策边界。下图中的图例说明了下图中颜色的含义。

决策表面和训练数据(图片由作者提供)
我们可以做出如下观察:
预测生存的决策面(绿色区域)大多位于中间。预测非存活(红色区域)的决策面主要位于侧面。
通常,没有幸存的乘客(蓝点)被分组在一起。类似地,幸存的乘客(绿点)被分组在一起。
微小异常值可以如下直观定位:
- 一群非幸存者中的幸存者
- 幸存者群中的非幸存者
下图显示了带有白色箭头的微小异常值。

识别微小异常值(图片由作者提供)
现在让我们来分析微观异常值。
分析微小异常值
为了更好地理解微异常值,让我们分析位于左上角的微异常值。下面的动画图像显示了可视化的分析方式。当我们将鼠标悬停在该点上时,它会显示每个点的列的雷达图。

分析微观异常值(图片由作者提供)
您将观察到所有的点都与男性乘客、具有高 PCLass(即 3 等)的乘客以及从 s 港上船的乘客相关。除了微小异常点,所有这些乘客都没有幸存。
这里的微观异常者是乘客尤金·帕特里克·戴利。你可以在这里的链接中读到他幸存的消息
他是位于下层甲板的三等舱乘客,他跳进了冰冷的水里。他没有生还的机会。然而,他声称他大衣的厚度归功于他的生存,这件衣服他穿了很多年,他把它命名为“幸运大衣”

微离群示例来源—https://www . encyclopedia-titanica . org/titanic-survivor/Eugene-Patrick-Daly . html
虽然我们为他高兴,但他对机器学习没什么好处!由于大衣厚度等模糊原因而幸运幸存的人是离群值,这扰乱了机器学习模型。我们也不知道谁跳下去了,也不知道每位乘客外套的厚度。所以最好的办法就是把它从训练数据中去掉。
微异常视觉技术在泰坦尼克号数据中自动识别这些“幸运”的人!使用任何经典的离群点检测算法都无法做到这一点。
我去除了 6 个微小的异常值,训练了模型,并提交了我的报告。与没有微异常技术的提交相比,领导层有了很大的提升。
结论
微异常值去除是一种很好的基于直觉的可视化方法,可以提高您的机器学习模型的准确性,而无需大量复杂的编码。在某种程度上,我们删除了可能使模型不必要地复杂化的数据点,从而获得了整体模型的准确性。
请订阅,以便在我发布新故事时随时获得通知。
*https://pranay-dave9.medium.com/subscribe
你也可以通过我的推荐链接加入 Medium
https://pranay-dave9.medium.com/membership
额外资源
网站(全球资讯网的主机站)
你可以访问我的网站进行零编码分析。https://experiencedatascience.com
Youtube 频道
这是我的 YouTube 频道
https://www.youtube.com/c/DataScienceDemonstrated的链接*
微软数据科学家 SQL 面试问题
原文:https://towardsdatascience.com/microsoft-data-scientist-sql-interview-questions-172baceb5fce
我们将回顾一个示例面试问题,以帮助您准备申请微软数据科学家的职位,该问题选自最近的数据分析师面试

作者在 Canva 上创建的图片
微软的数据分析师角色可以设计和构建数据模型,为他们的团队提供有意义的数据和模型。下面是一个真实世界的例子,你可能会在微软的面试中遇到。下面,我们将向您展示如何分解出现的问题,逐步完成用于解决问题的逻辑,并讨论如何优化解决方案以获得更好的性能。
Microsoft Data Scientist SQL 面试问题中测试的概念

作者在 Canva 上创建的图像
以下 Microsoft 数据科学家访谈问题中测试的主要概念包括:
- 聚合函数—求和/计数/分组依据
- 连接
- 限制
微软数据科学家 SQL 面试问题
员工项目预算
根据项目中分配给每个员工的预算金额,找出前五个最昂贵的项目。排除员工数为 0 的项目。假设每个员工只参与一个项目。输出应该是项目标题和分配给每个员工的预算(即预算与员工的比率)。首先显示预算与员工比率最高的前 5 个项目。

截图来自 StrataScratch
- *请注意,此问题已在 StrataScratch 平台上更新,请点击此处。在这篇文章和视频中,我们讨论这个问题的另一个版本。
数据集

截图来自 StrataScratch
假设
从上述问题中得到线索,作出以下初步假设:
- 一个项目可以有多个员工,但是每个员工只能参与一个项目
- 只有前 5 名的结果需要显示,所以结果需要按成本排序,然后限制在 5 个最昂贵的项目
我们的数据集提供了以下细分:
- id —每个项目的个人 id
- 标题—项目的名称
- 预算——每个项目的总成本
- emp_id —雇员 id 的整数值
- project_id —每个项目的唯一 id,与 ms_projects 表中的 id 值相同
在查看 ms_projects 中的预算列时,可以看到我们正在处理整数,因此也假设我们正在处理整数除法。

截图来自 StrataScratch
方法
所需的 SELECT 语句需要返回项目的标题(这样就很容易识别)和项目中每个员工的预算。选择的开始很简单:
SELECT title AS project,
但是现在我们必须决定如何返回每个员工的项目成本。对于这种方法,我们将使用整数除法。首先,我们将测试每个项目可能有多名员工的理论。为了测试这一点,我们使用 COUNT()函数。将它放入一个快速查询中(这里显示的连接将在下面进一步解释),结果显示这个假设是正确的:
SELECT title AS project, COUNT(emp_id) AS emps_per_project
FROM ms_projects p
INNER JOIN ms_emp_projects e
ON p.id = e.project_id
GROUP BY 1, budget

截图来自 StrataScratch
为了计算每个项目的雇员成本,我们将 ms_projects 的预算列除以每个项目的雇员总数。这是使用 COUNT(emp_id)完成的:
SELECT title AS project, budget/COUNT(emp_id) AS budget_emp_ratio
为了收集这个 SELECT 语句所需的所有列,ms_projects 和 ms_emp_projects 表必须连接在一起。
与上面的解决方案非常相似,为了产生期望的结果,必须在这个查询中使用一个 JOIN 语句。这里,我们将对 ms_projects 和 ms_emp_projects 进行内部连接,其中 ms_emp_projects 中的 project_id 列相当于 ms_projects 中的 id 列:
SELECT title AS project, budget/COUNT(emp_id) AS budget_emp_ratio
FROM ms_projects p
INNER JOIN ms_emp_projects e
ON p.id = e.project_id
由于 COUNT()是一个聚合函数,因此必须在项目标题和预算中与 GROUP BY 子句结合使用。
SELECT title AS project, budget/COUNT(emp_id) AS budget_emp_ratio
FROM ms_projects p
INNER JOIN ms_emp_projects e
ON p.id = e.project_id
GROUP BY 1, budget
以下是当前查询的结果:

截图来自 StrataScratch
最后,由于我们的微软数据科学家的采访问题是询问前 5 个最昂贵的项目,我们将它们按降序排列,并添加了一个限制子句,因此结果集中只返回前 5 行。
SELECT title AS project, budget/COUNT(emp_id) AS budget_emp_ratio
FROM ms_projects p
INNER JOIN ms_emp_projects e
ON p.id = e.project_id
GROUP BY 1, budget
ORDER BY 2 DESC
LIMIT 5
以下是最终结果:

截图来自 StrataScratch
最佳化
通常,受访者会被问及解决方案是否可以优化。如果不是,什么使解决方案最优?这里的查询已经过优化,所以我们将集中讨论为什么没有改进建议:
- 该解决方案符合预期
- 使用的连接方法
- 限制返回的结果
我们这里的解决方案提供的结果符合摘要的要求:不多也不少。在优化过程中首先要问的问题之一是当前的解决方案是否能产生预期的和准确的结果。
接下来,这里使用的连接方法是内部连接。内部联接检查被联接的两个表,并且只返回左侧表中的联接条件在右侧得到满足的行。这排除了不必要的数据,并处理空条目,因此在以后的查询中不需要考虑它们。如果在这个实例中使用了左连接,优化建议应该是将连接方法改为 INNER。为什么?左连接实际上执行两个连接:首先完成一个内连接;然后,对于表中参数左侧的其余行,如果在右侧没有匹配项,则向结果集中添加一行,该行包含左侧行的数据,并在右侧插入 NULL 值。这不仅需要额外的时间来运行查询,还必须在查询本身中考虑空值。因此,这里内部连接是正确的方法。
最后,限制查询返回的结果不仅满足赋值的参数,还节省了处理时间。需要注意的是,只有首先使用 ORDER BY 子句对结果进行排序时,限制结果才是最佳的。这是因为,如果不首先对结果进行排序,内置的 postgreSQL 查询优化器在生成查询计划时就没有排序约束,这意味着您的查询将产生无序的、不可预测的结果。
结论
现在,您已经看到如何将常见的 SQL 面试问题分解为基本概念、用于解决方案的逻辑以及可用于优化结果的技术。我们主要关注连接和聚合函数,这是作为数据科学家或分析师在日常生活中使用的。您还看到了如何使用 COUNT 和 ORDER BY 来组织数据。在许多情况下,您会看到多个从中提取和组织数据的表,所以熟悉连接及其不同的技术是必须的。如果你喜欢这篇文章,看看我们以前的文章“ 微软 SQL 面试问题 ”和“ 微软数据科学面试问题 ”,它们可以帮助你准备更多在面试中经常出现的概念。
原载于https://www.stratascratch.com。
大型时序数据的 Midimax 压缩
原文:https://towardsdatascience.com/midimax-data-compression-for-large-time-series-data-daf744c89310
基于 Python 的时序曲线轻量级快速压缩算法

尼古拉斯·卡佩罗在 Unsplash 上拍摄的照片
动机
可视化是用我们的数据进行推理的强大而关键的一步。然而,绘制大型时间序列数据会产生很大的文件大小,这会降低用户交互速度,并使 RAM、磁盘、网络等计算资源紧张。在我的工作中,来自机械监控传感器的时间序列数据可能以 1 Hz(每秒 1 个点)到数千 Hz 的速率记录,每个传感器每天轻松生成 100k+个点,几天内生成数百万个点。除了数据长度的挑战之外,为了观察相关性,绘图通常需要多个变量,从而加剧了资源限制。
本文介绍了一种称为“Midimax”的数据压缩算法,该算法专门用于通过减少数据大小来提高时间序列图的性能。该名称表示从原始时间序列数据中分离出窗口的最小值、中间值和最大值点的基本方法。开发 Midimax 算法时考虑了以下三个目标:
- 它不应该引入非实际数据。这意味着只返回原始数据的一个子集,因此没有平均、中值插值、回归和统计聚合。在绘图时避免对底层数据进行统计操作是有好处的,但是我们在这里不讨论它们。
- 它必须速度快,计算量小。它必须在消费者电脑和笔记本电脑上运行。理想情况下,它应该在不到 3 秒的时间内处理 100k 个点,从而允许扩展到更长的数据和更多的变量。
- 它应该最大限度地获取信息。这意味着它应该尽可能多地捕捉原始数据中的变化。这就是需要最小点和最大点的地方。
- (与第 3 点相关)由于单独取最小值和最大值可能给出夸大方差的错误观点,因此取中间点来保留关于信号稳定性的信息。
工作原理
为了理解 Midimax 的工作原理,这里有一段伪代码:
- 向算法输入时间序列数据(例如 Python 熊猫序列)和压缩因子(浮点数)。
- 将时间序列数据分割成大小相等的非重叠窗口,其中大小计算如下: *window_size = floor(3 压缩因子)。3 代表从每个窗口获取的最小值、中间值和最大值。因此,要实现 2 倍的压缩系数,窗口大小必须为 6。更大的系数需要更宽的窗口。
- 按升序对每个窗口中的值进行排序。
- 选择最小和最大点的第一个和最后一个值。这将确保我们最大化差异并保留信息。
- 选择中间值,其中中间位置定义为med _ index = floor(window _ size/2)。因此,即使窗口大小为偶数,也不会进行插值。在 Python 中,索引从 0 开始。
- 根据原始索引(即时间戳)对选取的点进行重新排序。
下图 1 中的示例显示了 1 秒采样率的原始数据(蓝线)和系数为 2 的压缩输出(绿点)。这意味着压缩后减少了 50%的点,因此在 50 秒处只有 25 个绿点。

图 Midimax 的输出示例。蓝线是原始数据。绿点是压缩数据。(图片由作者提供)。

图 1 放大到前 6 个数据点。(图片由作者提供)。
完整代码
用 Python 编写的完整代码可以从 GitHub 获得。
您可能会注意到 Python 代码中的一些算法改进,这些改进在上面的伪代码中没有提到。这些增加减少了处理空值或常量重复值时的计算量。当窗口包含常量值时,代码仅返回 1 个点,而不是 3 个冗余点。空值和冻结值的存在将导致更高的最终压缩因子。
表演
进行了性能测试,以生成具有一百万个点的模拟时间序列数据的散景图。作为基准测试的参考,测试的计算环境设置为:Windows 10 64 位笔记本电脑,16 GB RAM,SSD 磁盘,英特尔酷睿 i5 1.7 GHz 处理器,Python 3.7.10,Pandas 0.25.1,Bokeh 2.3.2。
输入数据由正弦波加 100,000 个数据点的高斯噪声生成,并在传递到压缩函数之前保存为 Pandas 系列。数据的开始部分绘制在上面的图 1 中。
演示绘图性能的 Python 代码如下:
import time
import numpy as np
import pandas as pdn = 100000 # points
timesteps = pd.to_timedelta(np.arange(n), unit=’s’)
timestamps = pd.to_datetime(“2022–04–18 08:00:00”) + timestepssine_waves = np.sin(2 * np.pi * 0.02 * np.arange(n))
noise = np.random.normal(0, 0.1, n)
signal = sine_waves + noise
ts_data = pd.Series(signal, index=timestamps).astype(‘float32’)# Run compression
timer_start = time.time()
ts_data_compressed = compress_series(ts_data, 2)
timer_sec = round(time.time() — timer_start, 2)
print(‘Compression took’, timer_sec, ‘seconds.’)> Compression took 0.45 seconds.
如上面的代码片段所示,该算法在 1.42 秒内完成了将 100k 个点压缩 2 倍的。由于要处理的窗口数量较少,压缩因子越高,完成得越快。对相同数据运行 4 和 8 的压缩因子分别在 0.74 秒和 0.44 秒内完成。通过因子 2 压缩 100 万个点在 14.87 秒内完成。

图 2)压缩系数为 2 和 4 的 Midimax 算法运行时间。(图片由作者提供)。
图 2 描绘了不同压缩因子和数据大小的算法运行时间。我们可以观察到运行时间随着数据点的数量线性增长。这种线性行为允许在应用程序纵向扩展时预测性能。
衡量性能的另一个标准是绘图的最终文件大小。100 万点的散景图 HTML 文件大小在 2X 压缩之前为 15.6 MB,在 2X 压缩之后为 7.8 MB。
实用用法指南
在图 1 中,我们可以看到,压缩因子为 2 导致了原始模式的良好重建。信号重建的质量高度依赖于数据的原始采样率。如果原始数据没有被充分采样,那么压缩只会恶化最终质量。建议用户首先在数据子集上尝试不同的压缩因子,以在大小减少和丢失细节之间找到适当的平衡。
Midimax 算法旨在提高视觉性能,而不是帮助使用大量数据进行建模。尽管该算法提供了具有良好趋势重构的压缩,但输出数据的分布与原始数据相比有很大的不同。使用它进行进一步的分析必须仔细考虑。
结论
Midimax 是一种简单的轻量级算法,用于减少数据大小,以便在消耗较少计算资源的同时快速绘制大型时间序列图。与任何压缩方法一样,细节会丢失,但在视觉分析的情况下,用户更感兴趣的是观察总体趋势,而不是小噪声。该算法被证明可以使用较少数量的点捕捉原始数据的变化,并在几秒钟内处理大量数据。
GitHub 库:https://github.com/edwinsutrisno/midimax_compression
从 SaaS git lab 迁移到自托管
原文:https://towardsdatascience.com/migrating-from-gitlab-saas-to-self-hosted-34b99bac0147
使用自动备份部署您自己的 GitLab 实例

茱莉亚·克莱斯在 Unsplash 上的照片
几个月前, GitLab 宣布了对免费层的修改,包括将私有名称空间限制为 5 个用户。由于我的团队将受到这些变化的影响,我们决定是时候尝试一下自托管了!更新将很快上线,所以我在这里分享一些关于我遵循的步骤的指导,收集和综合我使用的文档。

在本指南中,我将详细介绍如何:
- 使用 Docker 部署自托管 GitLab 实例
- 从 SaaS git lab 迁移到自托管实例
- 迁移 GitLab 容器注册表
- 配置上传到 S3 兼容存储的计划备份
实现相同配置所需的条件:
- 虚拟机实例
- 一个 DockerHub 帐户(仅迁移您的容器注册中心)
- 具有读写权限的 S3 存储桶(用于存储备份档案)
- 对 Docker 启动 GitLab 服务器的基本了解
评估你的需求
第一步是确定部署服务器所需的资源。你可以遵循 GitLab 的需求,这取决于你的用户数量、存储库存储以及容器存储等等。您可以选择将数据直接存储在虚拟机存储上以获得更快的数据传输速度,或者存储在基于云的存储上以获得可扩展性。
部署 GitLab
GitLab 的官方文档为自托管服务器提供了多种部署方法。这里我选择了 Docker 方法,因为我熟悉 Docker 部署。其他方法包括 Omnibus 包,或 Kubernetes 集群的 Helm,并且与本指南兼容。

请注意,Docker 部署实际上将 Omnibus 包部署在一个容器中,许多配置选项都是相似的。
配置您的虚拟机
如果您正在一台全新的机器上部署,您可能希望首先检查一些先决条件,尤其是对于初学者。
- SSH:一些提供商提供带有一组默认用户和密码的机器,更改它以保护您的服务器。你可以创建一个无密码的用户,例如用 ssh 密钥连接(见本教程)。
- 安装 docker 等工具:选择适合你操作系统的服务器版本。此时,您可能还想安装一个文本编辑器。
使用 Docker compose 部署
GitLab 文档提供了一个Docker 组合配置示例。在这里,我分享了我的版本,并做了一些修改,解释如下:
version: "3.6"
services:
web:
container_name: gitlab
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'https://X.X.X.X'
gitlab_rails['gitlab_shell_ssh_port'] = '2222'
gitlab_rails['omniauth_providers'] = {
name: "gitlab",
app_id: "###",
app_secret: "###"
}
gitlab_rails['backup_keep_time'] = '86400'
gitlab_rails['backup_upload_connection'] = {
provider: 'AWS',
region: '###',
aws_access_key_id: '###',
aws_secret_access_key: '###',
host: '###',
endpoint: '###',
path_style: '###',
aws_signature_version: '2'
}
gitlab_rails['backup_upload_remote_directory'] = 'backup/gitlab'
image: gitlab/gitlab-ee:latest
ports:
- 2222:22/tcp
- 443:443/tcp
- 80:80/tcp
restart: always
volumes:
- $GITLAB_HOME/logs:/var/log/gitlab
- $GITLAB_HOME/data:/var/opt/gitlab
- $GITLAB_HOME/config:/etc/gitlab
大部分配置在 GITLAB_OMNIBUS_CONFIG 变量中提供,该变量可以包含任何 gitlab.rb 参数。这里我使用了:
gitlab_shell_ssh_port:映射 GitLab 使用的端口,保持标准的 SSH 端口 22 空闲用于 SSH 连接。与对接器端口配置相匹配。omniauth_providers:遵循此配置以实现从 GitLab.com 的简单项目导入管理。backup_*:上传备份档案到 S3,更多详情见下文备份部分。volumes:访问并保存位于容器内的配置和数据目录。注意$GITLAB_HOME变量,指定虚拟机本地存储中卷的根文件夹。
要启动,执行以下命令:docker compose up -d
更新您的服务器
您以后可能需要更改服务器的配置。为此,您可以编辑合成文件,然后删除容器以创建新的容器。
您还可以通过修改$GITLAB_HOME/config文件夹中的配置文件将更改应用到当前服务器,并使用以下命令进行更新:docker exec gitlab gitlab-ctl reconfigure。这在名为gitlab的容器内执行重新配置命令。
迁移您的项目
现在您的服务器已经准备好,应该在https://<your-host>/可用。要连接到root用户,您可以在文件$GITLAB_HOME/config/initial_root_password中检索密码并登录到您的服务器。现在,您可以开始用您想要迁移的所有数据填充您的服务器。
首先,您可以创建一个新的群组或从您的 GitLab.com 帐户导入现有的群组。请注意,导入组仅检索子组结构,项目将在下一步导入。此处的是通过组导入迁移的所有项目的列表。

创建组
然后,您可以前往create project页面并选择您的导入方法。

导入项目
如果您配置了omniauth_providers选项,您可以访问 GitLab.com 导入页面,轻松地一次导入多个项目,这是推荐的导入大量项目的方法,方法是指定目标组并一次对多个导入进行排队。

从 GitLab.com 进口
请注意,您也可以通过 URL 导入从 GitLab.com 导入项目。在此检查随着项目导入而迁移的项目列表。
迁移容器注册表
如果您广泛使用 GitLab.com 容器注册中心,将映像迁移到您的自托管实例可能会占用大量空间,可能会超出您的虚拟机的处理能力。我个人选择用一个 pro 帐户将我的图像迁移到 DockerHub,以获得无限的私有存储库。无论您选择哪种解决方案,您都需要考虑以下几点:
- 图像需要被推送到新的容器注册表,
- 如果 CI/CD 管道推/拉映像,您需要更新它们。我建议使用 GitLab 变量来轻松地更改您的注册表 url 和登录名,
- 您需要更新任何与注册表交互的服务:例如,Kubernetes 的docker-registrysecrets,或者您的服务器和开发人员计算机上的 docker login 。
配置备份
可靠部署的最后一步是确保您的数据是安全的,并且可以从存储故障中恢复。GitLab 自托管版本提供内置备份功能来创建和上传服务器数据的档案。我使用这个功能自动对我的所有存储库进行每日备份,并将存档文件上传到 S3 兼容的存储中。
自动化备份
GitLab 提供了一个内置函数来创建服务器数据的档案(存储库、构建、工件……)。它还将生成的归档文件上传到 docker-compose 文件中指定的云存储中。我使用了以下配置:
gitlab_rails['backup_keep_time'] = 86400
gitlab_rails['backup_upload_connection'] = {
'provider' => 'AWS',
'region' => '###',
'aws_access_key_id' => '###',
'aws_secret_access_key' => '###',
'host' => '###',
'endpoint' => '###',
'path_style' => '###',
'aws_signature_version'=> 2
}
gitlab_rails['backup_upload_remote_directory'] = 'backup/gitlab'
注意:
backup_keep_time:归档文件在虚拟机上保留的时间(秒),超过此阈值的归档文件将在下次执行备份时被删除。它不处理上传到云存储上的归档。- 如果是 S3 兼容的存储,使用带有
endpoint的'provider'=>'AWS'作为您的服务的 url,一些提供商需要以下版本以兼容'aws_signature_version'=>2。
但是 GitLab 内置的备份功能并不备份配置文件,所以这里我分享一下用来备份我的服务器的 bash 脚本。它使用 MinIO 客户端来保存内置函数中不包含的文件,并删除旧的档案,并假设 S3 兼容存储器的别名为my-s3。它只存储当前的配置,并将数据存档长达一周,然后通过邮件通知该脚本的输出。
#!/bin/bash
# move old config files
mc mv --recursive my-s3/backup/gitlab/config my-s3/backup/gitlab/old
# copy files to s3
{ mc cp $GITLAB_HOME/config/gitlab-secrets.json my-s3/backup/gitlab/config/gitlab-secrets.json ; \
mc cp $GITLAB_HOME/config/gitlab.rb my-s3/backup/gitlab/config/gitlab.rb ; \
mc cp --recursive $GITLAB_HOME/config/ssl my-s3/backup/gitlab/config/ssl ; \
mc cp --recursive $GITLAB_HOME/config/trusted-certs my-s3/backup/gitlab/config/trusted-certs ; \
} 1> /tmp/mc_logs.txt 2>&1
# auto backup with gitlab
if docker exec gitlab gitlab-backup create skip=builds,artifacts,registry CRON=1 1> /tmp/backup_logs.txt 2>&1
then
status="Success"
# remove old files
echo 'Removing old config files'
{ mc rm --recursive --force my-s3/backup/gitlab/old ; \
mc rm --recursive --force my-s3/backup/gitlab/* --older-than 7d ; \
} 1>> /tmp/mc_logs.txt 2>&1
else
status="Failed"
fi
cat /tmp/mc_logs.txt | mail -s "GitLab Backup $status" my-mail@company.com -a FROM:backup@gitlab-server -A /tmp/backup_logs.txt
请注意:
- 第一步是将当前保存在 S3 上的配置移动到一个
old文件夹中,而不是在复制失败的情况下覆盖它, - mc 步骤在列表中执行,然后是
1>> /tmp/mc_logs.txt 2>&1将日志保存在临时文件中,以便稍后在邮件通知中发送。在这里阅读更多关于 UNIX 重定向的信息, - 您可以从不存在的地址发送邮件通知,但是它可能会以垃圾邮件文件夹结束,尤其是带有附件的邮件。有多种邮件客户端可供选择,你可以向现有账户认证发件人,或者在收件箱中创建过滤器以防止通知被标记为垃圾邮件(Gmail 的示例)。
我选择每天早上用下面的 cronjob 作为 CRON 运行这个脚本:0 7 * * 5 /bin/sh /home/admin/self-host/backup.sh。点击阅读更多关于 cronjobs 的信息。
恢复您的数据
GitLab 在这里提供官方恢复指南。但是,在执行恢复功能之前,我们需要从 S3 检索文件。以下脚本收集了下载备份文件、停止服务和启动恢复功能的所有步骤。
#!/bin/bash
backup_name=$1_gitlab_backup.tar
# download config files
mc cp my-s3/backup/gitlab/config/gitlab-secrets.json ./config/gitlab-secrets.json
mc cp my-s3/backup/gitlab/config/gitlab.rb ./config/gitlab.rb
mc cp --recursive my-s3/backup/gitlab/config/ssl ./config/ssl
mc cp --recursive my-s3/backup/gitlab/config/trusted-certs ./config/trusted-certs
sudo cp -r ./config $GITLAB_HOME/config
sudo rm -rf ./config# download archive
mc cp my-s3/backup/gitlab/$backup_name $backup_name
sudo mv ./$backup_name $GITLAB_HOME/data/backups/$backup_name# auto backup with gitlab
docker exec -it gitlab gitlab-ctl stop puma
docker exec -it gitlab gitlab-ctl stop sidekiq
docker exec -it gitlab gitlab-ctl status
docker exec -it gitlab gitlab-backup restore --force BACKUP=$1
docker restart gitlab
docker exec -it gitlab gitlab-rake gitlab:check SANITIZE=true
用法:./restore.sh <BACKUP_NAME>
注意事项:
BACKUP_NAME是备份归档文件的名称,不带_gitlab_backup.tar扩展名。- MinIO 将 S3 存储别名存储为依赖于用户的配置。虽然写入我的 GitLab 配置文件夹需要 root 权限,但是运行这个脚本需要 sudo 配置
root的 MinIO 别名。我宁愿不使用 sudo 执行这个脚本,而是将下载操作分成两部分:首先下载到一个临时文件,然后sudo将文件移动到所需的路径。
结论
在本教程结束时,您应该能够部署一个具有自动备份功能的 GitLab 自托管服务器。如果容器注册表占用太多空间,您还可以使用外部工具来管理它们,并在存储失败时恢复您的服务器。一旦您迁移了您的项目,您就可以通过在本地克隆您的存储库并邀请服务器上的其他用户来开始使用您的实例。
你在设置你的服务器时有困难吗?你发现其他有用的方法了吗?不要犹豫分享任何反馈和快乐的编码!
注意你的 I 和 Q:I/Q 数据的基础
原文:https://towardsdatascience.com/mind-your-is-and-q-s-the-basics-of-i-q-data-d1f2b0dd81f4
使用相位调制示例了解同相和正交分量
I/Q 数据出现在许多数据科学设置中:RF(射频)数据、时间序列分析、音频处理等等。
另外,我最喜欢的几何演示之一是将一个角度调制正弦波分解成两个正交的幅度调制正弦波。作为未来的预告片…

图 8:预览图…这就是我们要去的地方!图片作者。
作为激励,我将使用一个无线电示例。从业余无线电到雷达成像,我对所有射频技术都充满热情。在本文中,我们将探讨如何使用“相移键控”在波中编码信息,以及如何使用两个振荡器的和来合成任意所需相位的波。让我们开始吧!
本文中的所有图片都是我创作的。在任何上下文中随意重用,无需注明出处(不保留任何权利)。

图 1:三个相同的正弦波相位偏移。图片作者。
如果你戴上工程学的帽子,你可能会想“如果我能改变一个东西,我可以用它来编码信息。”的确,你可以!我们可以采用简单的正弦波(即“载波”)并调制(即改变)相位,以将信息传递给接收器。
二进制相移键控(BPSK)
BPSK 是通过操纵载波相位来传递信息的最简单的机制。透射波的相位在两种状态之间变化。

图 2 :二进制序列的简单 BPSK 编码。图片作者。
小菜一碟!在 BPSK 下,我们可以交流两个二元状态。我们只是将每个状态编码为不同的阶段。通常,两个相位状态尽可能相互偏移(180°)。上面(图 2 ),我已经用 BPSK 方案编码了序列 0,0,0,1,1,0,1,0,0,1,0,1,1,1,0,0,0。
回到工程帽!如果我们可以用两个相位值对两个状态进行编码,那么如果我们使用额外的相位会怎么样呢?如果我们使用四个相位值来传输每个比特周期四个可能状态之一,以增加我们传输的信息密度,会怎么样?我们可以!这种扩展被称为“正交相移键控”(QPSK)。
不过,这提出了一些实际问题……随着我们继续引入额外的相位要求,我们是否需要引入额外的振荡器?
从同相和正交分量合成
相反,让我们想想如何利用简单的构建模块来创建许多不同相位的正弦波。

图 3 :余弦和正弦参考图像。图片作者。
在我的无线电工具包中,我有很多方法可以从余弦波产生正弦波。例如,如果我有一个产生纯余弦的电子振荡器,我可以添加一个简单的功率分配器和一个延迟线来产生正弦。余弦和正弦只是彼此偏移 90°的相移。从另一个产生一个在硬件上很简单:我只需要一个时间延迟来转换信号。
不过,现在我同时使用了这两种方法,继续称它们为“余弦”和“正弦”可能会有点令人困惑。由于它们只是彼此的相移,如果我在未来的任意时间点开始测量信号,我的“余弦”可能看起来像正弦,我的“正弦”可能看起来像余弦!相反,让我们用余弦作为参考,称之为“同相”我们称正弦为“正交”分量。太棒了。我从一个阶段开始,现在只使用一个功率分配器和一个可调延迟线就有两个阶段了。
我的“同相”和“正交”分量似乎有一些有趣的特性。如果我把余弦信号和正弦信号相乘,然后求和,结果是零。更正式地说同样的事情,如果我取这两个函数乘积的积分,我的结果是零。这种特性被称为“正交性”它们的相关性为零!这是一个极好的消息,因为它使得建立坐标空间变得容易。如果我将同相度放在 x 轴上,将正交度放在 y 轴上,我知道它们是不相关的,因此沿一个轴增加不会影响另一个轴上的值。

图 4 :纯余弦的 I/Q 空间。作者图片
让我们画出这个同相,正交空间。在 x 轴上,我们画出我们想要多少余弦(即余弦函数上的“同相”权重)。在 y 轴上,我们画出我们想要多少正弦(即正弦函数上的“正交”权重)。我们可以将两个加权分量和加权和可视化(图 4 )。我们称这个新空间为 I/Q,用来描述两个轴:(i)n 相位和(Q)u 相位
用蓝色,我用一条到原点的线画出了坐标同相权重 1,正交权重 0。到原点的距离是 1: √(1 + 0)。不出所料,当正弦权重为零时,我们的加权和正好是余弦!

图 5 :纯正弦的 I/Q 空间。图片作者。
让我们移动到坐标同相 0,正交权重 1 ( 图 5 )。这是从我们之前的位置(红线)旋转 90 度。同相权重为零时,我们只能得到正弦波。我们开始看到一些有趣的东西…在这个坐标中旋转 90 °,在我们的加权和中产生 90°的相位。以前是余弦,现在是正弦。我们空间的几何编码了这个属性。非常酷!
我们也可以在坐标轴上移动到负值。围绕 y 轴有效翻转(即反转)我们的余弦或正弦分量。

图 6 :反余弦的 I/Q 空间。图片作者。
跳到坐标同相权重-1,正交权重 0 ( 图 6 ),我们把原来的余弦分量反过来!同样,如果我们回头看看原始余弦基准电压,这个翻转相当于第一个加权和的 180°相位偏移。这也是我们在这一领域构建的第一个“新”阶段。通过混合使用 I 和 Q 基函数,我们构建了一个前所未有的正弦相位。

图 7 :反正弦的 I/Q 空间。图片作者。
最后,完成空间直觉,我们在坐标同相权重 0,正交权重-1 处反转我们的原始正弦函数(图 7 )。这是我们的第二个“新”阶段:一个我们以前没有的阶段的信号。同样,加权和与我们首先考察的纯余弦值有 270°相位偏移。在我们看到的每个位置,角度(绿色)反映了应用于加权和的相位偏移。
只要我们可以任意混合这两种成分,似乎我们可以生成任何我们想要的相位!我们希望保持我们到原点的距离不变(这里是单位长度),以确保我们不改变总功率(即幅度,更多内容见我的其他文章),但是当我们扫过两个分量的权重时,我们可以合成任何我们想要的相位的正弦曲线。这太棒了。从单个振荡器开始,添加一个功率分配器和一个延迟线,现在引入一个混频器,我们可以组成任何相位的正弦波,频率和幅度保持不变。
我们有所有我们需要的直觉!现在我们可以重温我们最初的视觉化。

图 8 :将一个角度调制正弦波分解成两个正交的幅度调制正弦波。作者图。
如果这不是一个令人满意的几何结果,我不知道什么是。这是催眠和有用。下次你在数据科学项目中遇到 I/Q 值时,我希望它们看起来不那么吓人!
如果你希望看到其他概念的解释,请留下评论。感谢阅读!
挖掘和模拟字符网络——第一部分
原文:https://towardsdatascience.com/mining-modelling-character-networks-part-i-e37e4878c467
思想和理论
挖掘& 建模字符网络—第一部分
图论中讨论从文本中挖掘字符和模拟交互网络的论文的文献综述

这是一篇由两部分组成的文章,第一部分将讨论相关的研究论文,包括从各种文本中挖掘和建模字符网络的主题。接下来的第二部分将介绍挖掘角色的 python 实现,识别角色交互,然后在这些角色之间创建交互网络。
https://vatsal12-p.medium.com/mining-modelling-character-networks-part-ii-a3d77de89638
以下是这篇文章的提纲。
目录
- 介绍
- 文献评论
- 设计和方法
-数据&网络创建
-建模 - 结果
- 讨论和评论
- 资源
简介
数学在现实世界中有许多应用,数学中一个定义明确的分支叫做图论,它是对图形的研究。图是一种通过边来模拟节点之间成对关系的数学结构,它对于定义和理解节点之间的关系及其连接有着不可思议的应用[1]。图论的许多应用之一在于理解字符网络。这些人物网络产生于小说、故事、电影、视频和其他形式的媒体。根据网络的结构,人们可以选择节点对的关系类型。这些关系可以通过对话、性别、共现、主题、故事情节、冲突、争论、朋友、敌人、种族等来描述。[1].这些网络可以带来基于结构的新观点,也就是说,我们可以基于对网络的观察和数学分析来剥离和强化文本主体中显示的现有主题和属性。
在 Bonato 等人的“挖掘和建模字符网络”中,分析结果可能因网络结构而异。他们根据 15 个单词范围内文本中字符的接近度创建了一个加权字符网络。如果在文本中提到了两个或更多字符,并且它们之间的距离在 15 个单词以内,那么将会创建与该组字符的组合相关联的加权边。
文献综述
在字符网络的分析方面已经做了大量的工作。在“提取和分析虚构人物网络:一项调查”中,Labatut 和 Bost 概述并整合了人物网络主题的所有学术文献。从识别字符、检测字符交互、提取图形,到分析和应用[2]。Labatut 和 Bost 概述了识别字符和检测这些识别字符之间的相互作用的各种方法。字符识别方法包括从人工识别到使用命名实体识别(NER)工具,该工具基于预定义的类别来定位和分类文本主体中提到的命名实体。当处理视频、漫画、漫画和电影等视觉形式的媒体时,文本通过计算机视觉使用人脸检测来勾勒轮廓。Labatut 和 Bost 指出了 6 种不同方法来识别字符之间相互作用[2],即:
- 字符共现
- 人物之间的直接言语互动
- 人物间的显性提及
- 非语言互动(打架、接吻、杀戮等。)
- 角色之间的联系(同事、家人、朋友等。)
- 1–5 的混合方法
图形提取过程包括基于先前为媒体主体识别的字符和交互来创建网络。Labatut 和 Bost 声称,许多作者只是简单地使用单个字符作为节点,然而有些人在他们的网络创建过程中使用字符/组织的组合作为节点。边是基于作者在它们的指定节点之间使用的交互来创建的。许多作者选择创建他们的网络作为加权或多边,而少数人创建他们的图形作为指导。Labatut 和 Bost 对这些网络的分析涵盖了各种中心性测量、图形统计、社区结构、聚类等。
在 Ramakrishna 等人的“电影角色刻画差异的语言学分析”中,试图通过心理语言学和网络分析来检验角色的差异[4]。性别、年龄和其他元数据定义了角色之间的差异,这些元数据是通过角色之间的电影对话提取的。从 945 个剧本中,他们创建了一个解析器来识别与剧本相关的元数据。他们已经确定了与角色相关的性别、种族和年龄,以及电影元数据,包括电影上映日期、编剧、导演等。他们概述了在他们创建的网络上运行各种实验,即心理语言学规范和语言调查和字数统计(LIWC)。心理语言规范提供了对说话者的各种情绪和心理结构的测量,如唤醒、效价、具体性、可理解性等。而且完全是从语言使用中计算出来的[4]。LIWC 是一个文本处理应用程序,它处理原始文本并输出文本中属于语言、情感、感知和其他维度的一定百分比的单词。[4].最后,他们发现电影和电影场景中的性别、年龄和种族等级有明显的区别。特别是,他们注意到,如果制作团队中有女性工作人员,他们在整个演职人员中的性别比例会更平衡,但是如果制作团队以男性为主,那么演职人员也以男性为主。他们还注意到,混血人物在电影中有更多的性词汇。与老角色相关的对话的复杂程度明显高于年轻角色。
设计&方法论
Bonato 等人的“挖掘和建模字符网络”是本文的重点,它强调了创建合成网络的实验,这些实验密切捕捉了字符网络的属性和相似性。
数据&网络创建
博纳托对从以下文本主体创建的网络进行了深入分析,暮光之城,看台,哈利波特与火焰杯以及从http://moviegalaxies.com/拍摄的电影中的 800 个角色网络。他们构建了一个解析器,可以扫描输入文本,识别唯一的字符名称及其相关别名。在识别字符名称后,另一个解析器再次检查文本主体,以识别字符在 15 个单词范围内的共现。这些共现是网络的代表性边。Bonato 等人决定创建一个如图 1 所示的加权网络,而不是将其作为一个多边图。

图 1:从文本创建网络(图片由作者提供)
然后,在创建的网络之上计算网络统计数据,以推断对文本主体的字符动态的更多见解。Bonato 等人在网络上运行了 Louvain 的算法来识别字符社区。还在网络上运行了以下中心性测量,并且这些结果中的一些后来被输入到机器学习模型中:
- 页面排名:该算法统计与角色的质量交互的频率,以生成对角色重要性的估计。预期是在网络中具有较大重要性的字符将获得较大的页面排名分数。
- 特征向量:该中心性度量指示某个字符在网络中的影响水平,较大的特征向量中心性得分反映了较高的字符重要性。潜在的假设是,具有高重要性的节点将与同样具有高重要性的其他节点高度连接。
- 中间性:这一衡量标准基于角色将其他角色“连接”在一起的能力来确定角色对网络的影响。它通过识别和记录网络中所有角色之间的最短路径来做到这一点。经常落在所识别的最短路径中的字符将具有较大的介数,这是因为该算法认为该字符是连接其他字符的良好“桥梁”。
- 度:这个度量反映了连接节点对的边的数量。具有高连通性的节点将具有很大程度的中心性。
- 接近度:该度量基于网络中字符的接近度来识别字符与其他字符的接近度。它可以被推断为一个角色向其他角色传播信息的能力。
造型
建模组件的目标是识别一个随机图形模型,该模型与观察到的字符网络的属性平行[3]。Bonato 等人导出了与网络相关的 k 分布和特征值分布。图的 k-profile 是在 G [3]的诱导子图中出现的 k 个节点上的每个图的频率。特征值分布是归一化拉普拉斯矩阵的特征值通过等间距仓的直方图[3]。有 4 种不同的随机图形模型用于合成原始字符网络。
- 二项式随机图:图是由随机连接的节点构成的。每条边以独立于所有其他边的概率 p 连接。他们设置 p = |E| / nC2 来匹配原字符网络的平均度[3]。
- 配置模型:他们创建了一个近似的网络,与原始字符网络具有相同的度分布。这是通过从精确匹配目标度分布的图形集中一致地选择一个图形来完成的[3]。
- 优先连接:给定一个图 G,在每个时间步长 t,添加一个新的节点 n 和 m 条边,将这个新的节点 n 连接到时间步长 t-1 的现有节点。他们指定通过以下公式选择边 2/n + 2m = 2|E| / n [3]。
- Chung-Lu : 由该模型创建的图通过字符网络度分布来参数化[3]。它创建概率 p 与产品期望度数 wi 成比例的边[3]。边缘概率可以由下面的公式表示:pij = 1/C * wi * wj
为了评估这些随机模型中哪一个最好地反映了原始字符网络,Bonato 等人决定从每个模型中生成 100 个随机图。然后,他们试图通过在生成的每个随机图上训练各种分类器来确定上述 4 个模型中的最佳随机图模型,以查看分类器是否能够正确预测原始字符网络的类别标签[3]。本实验使用了 3 种不同的 ML 模型,即:
- 随机森林:一种集成学习算法,该算法使用多个决策树的结果来做出关于该数据点将被标记为什么类的总体决定。最终类别将基于生成的每个决策树的聚合的多数投票来预测。
- SVM:SVM 分类背后的直觉是生成一个超平面,该超平面将数据点精确地分成它们适当的训练类。如果数据被映射到一个三维空间,一个二维超平面,它最大化超平面和所有其他数据点之间的边界。类似地,如果数据被映射到 2 维空间中,1 维超平面将被用于将数据分成它们适当的类。
- Ada Boost:AdaBoost 背后的直觉是通过使用许多弱学习器相互结合,将弱学习器(决策树)转换为强学习器,这是一个集成类型的模型。本质上,先前迭代的错误被用于在当前迭代中做出准确的预测。
结果
这种方法不仅能够准确地预测字符的重要性,而且能够准确地预测在进行分析的 3 本书中形成的社区。具有最高中心性等级的字符(如页面等级)确实在正文中扮演着重要的角色;这在所有的中心性测量中都是决定性的。由 Louvain 算法创建的相应社区准确地描绘了文本主体中概述的社区。例如,在《暮光之城》中,有 3 个显著的社区形成,第一个社区对应于吸血鬼,第二个对应于高中学生,第三个对应于查理的朋友。当查看其他小说的相应图表时,可以得出类似的结论,参见[3]了解更多信息和跨 3 个文本主体的社区网络的可视化表示。
在本实验中,在分类器上使用了交叉验证,这是一种在机器学习中常用来查看机器学习模型的结果概括得如何的度量。每个分类器都获得了非常高的交叉验证分数以及大的 F1 分数。模型的性能可以在表 1 中看到。对应于每个模型,使用不同的精度测量,对于 Ada boost,使用最终决策函数,对于随机森林,使用软决策概率,对于 SVM,使用分离超平面的距离[3]。显然,根据下表所示的结果,Chung-Lu 模型在每本书的所有类别中都优于所有其他模型。

表 1:摘自 Bonato 等人[3]的文章,概述了使用图形剖面和特征值直方图作为特征时所有最大似然模型的性能
类似地,在图 2 中,我们看到,当使用图形轮廓和特征值直方图作为特征时,Chung-Lu 模型是所有电影星系数据中性能最好的模型[3]。

图 2:摘自 Bonato 等人[3]的文章,概述了 Chung-Lu 模型在研究电影星系数据集时优于所有其他模型的强大性能
讨论&备注
Bonato 等人从 3 部大型小说和 800 部电影中对加权人物网络进行了网络分析。这些网络是基于文本中字符的共现而创建的。在生成网络时,进行了各种统计分析,包括 Louvaine 的社区检测算法,以及字符的中心性度量(页面等级、特征向量、介数、接近度、程度)。该分析成功地提取了准确的文学结论,并确定了网络中有影响力和无影响力的人物[3]。通过使用各种 ML 算法,如 SVM、随机森林、ada boost、Bonato 等人,将随机模型与来自复杂网络的其他模拟数据进行了比较。他们考虑了 Chung-Lu 模型、配置模型、PA 模型和二项式随机图[3]。结果清楚地表明,Chung-Lu 模型是精确描述原始字符网络的最佳表现模型。
Bonato 等人介绍的方法在许多方面都可以改进。首先,这些模型的性能很可能是网络规模的指示。由于原文正文(《暮光之城》、《看台》、《哈利·波特与火焰杯》和 800 个人物网络)一开始就很大,因此对应于该正文建立的相应人物网络也会很大。同样的方法和结果可能不适用于短篇故事、视频和小电影。其次,当自主识别字符和字符交互时,可以进行改进。在“提取和分析虚构人物网络:调查”、Labatut 和 Bost 中介绍的通过 NER 进行人物识别的方法和识别人物互动的混合方法将产生令人信服的论点,有助于当前提出的方法和更通用数据集的结果。
当前网络创建方法的局限性在于定义角色交互的方式。他们使用任意数字 15 来表示文本主体中字符之间的共现,该数字没有被导出,也没有被实验来查看如果增加或减少某个值 k,结果会如何不同。此外,基于当前的字符交互定义,如果一个人引用一组/组织的字符,则他们自动具有与该组/组织中的每个成员相关联的高加权边。这将对使用中心性分数、社区检测等进行的分析产生重大影响。
除了上面提到的讨论点之外,对于与该主题相关的未来实验,创建和探索在某个时间步长 t 上临时创建的图表将产生有趣的结果,以查看哪个模型最适合数据。这将让我们看到随着故事的进展,网络中的变化,相关的网络统计数据将非常有趣,因为我们可以看到角色动态如何随着时间的推移而演变。这允许引入各种时间序列分析和预测模型,这非常有趣。
资源
[1]陈,安迪."用… 建模电影人物网络" snap.stanford.edu,斯坦福,2017 年 12 月,https://snap . Stanford . edu/class/cs 224 w-2017/projects/cs 224 w-32-final . pdf .
[2]文森特·拉巴图特和泽维尔·博斯特。2019.虚拟人物网络的提取和分析:一项调查。美国计算机学会计算调查 52(5):89。https://doi.org/10.1145/3344548
[3] Bonato,a .,D'Angelo,D. R .,Elenberg,E. R .,Gleich,D. F .,& Hou,Y. (2016)。"挖掘和建模字符网络"网络图的算法和模型:第 13 届国际研讨会,WAW 2016,加拿大魁北克蒙特利尔,2016 年 12 月 14-15 日,会议录 13(第 100-114 页)。斯普林格国际出版公司
[4]罗摩克里希纳,阿尼尔等.电影刻画差异的语言学分析…《2017,https://aclanthology.org/P17-1153.pdf.
如果你喜欢这篇文章,下面是我写的一些你可能会感兴趣的文章:
[## 贝叶斯 A/B 测试解释
towardsdatascience.com](/bayesian-a-b-testing-explained-344a6df88c1a)
字符网络的挖掘和建模—第二部分
原文:https://towardsdatascience.com/mining-modelling-character-networks-part-ii-a3d77de89638
本文将介绍挖掘和建模字符网络的 Python 实现

如果您还没有,我建议您阅读本系列的第一部分。这是一篇研究论文的文献综述,我们将在本文中尝试用代码实现它。下面的文章我已经链接了。
我深入研究了挖掘字符的文本数据的 Python 实现,创建了基于字符交互的字符网络,并通过 Chung-Lu 模型对这些字符网络建模。所有这些概念之前都在 Bonato 等人的“挖掘和建模人物网络”和“提取和分析虚构人物网络:调查”、Labatut 和 Bost [2]中讨论过。
本文将分析的文本主体是爱丽丝梦游仙境,然而,代码的结构方式是任何一段文本都可以被运行以产生它自己的结果。请注意,《爱丽丝梦游仙境》在 1923 年 1 月 1 日之前出版,因此属于公共领域,在全球范围内属于公共领域,因为卡罗尔于 1898 年去世,距今已有 100 多年[4]。
目录
- 数据
-安装要求
-下载爱丽丝梦游仙境 - 干净的数据
- 挖掘角色名字
-方法 1:正则表达式
-方法 2:名字语料库 - 寻找角色互动
- 创建角色网络
- 钟-卢模型
- 真实世界的应用
- 结论
- 资源
数据
下面将概述用于执行本文的 my Python 和库的版本。
安装要求
Python=3.8.8
networkx=2.5
pandas=1.2.4
numpy=1.20.1
scipy=1.6.2
matplotlib=3.3.4
nltk=3.6.1There are other requirements like re, collections, string and random but they come pre-installed along with Python.
下载爱丽丝梦游仙境
导入 NLTK 后,可以非常轻松地下载《爱丽丝梦游仙境》的 txt 文件。只需在您的 python 环境中运行下面的代码,如果已经下载了,那么继续本文的下一部分。
干净的数据
在尝试识别字符之前,最好对原始文本数据进行一点清理。我们至少可以去掉停用词、标点符号和整个正文的小写字母。这将使交叉引用字符名称从文本与姓名语料库执行得更好。
挖掘角色名
有各种方法来识别角色的名字。在这里,我将介绍两种相似的方法来识别文本中的字符名称。第一个是查找字符名称的正则表达式,第二个是使用英语中已知名称的列表,并将这些已知名称与文本主体进行交叉引用,以确定在该文本主体中也找到了哪些已知名称。第二种方法将通过安大略省政府提供的开源数据集来完成:安大略省婴儿名字 1917–2019。
总的来说,让我通过陈述显而易见的事实来警告这一部分,这些方法中没有一个会对任何给定的文本体产生 100%准确的结果。即使在 NER 尝试 NLP 解决方案也不会给出太强的解决方案,因为机器/算法很难识别别名、组和其他只有人类才能识别的细微差别。最好的方法是让用户创建一个字典,其中的键是文本主体中的已知名称,值是与该已知名称相关联的别名列表。例如在《哈利·波特》中,已知的字典看起来像这样:
known_dct = {
‘Harry’ : [‘Harry Potter’, ‘The Chosen One’, …],
‘Voldemort’ : [‘He Who Must Not Be Named’, ‘Voldything’, …],
…
}
方法 1:正则表达式
这种实现虽然有效,但在角色名中产生了许多误报。很难构造一个在任何情况下都适用于任何文本主体的正则表达式。以下代码产生的一些误报名称如下:
'Uglification Derision', 'Mouse Alice', 'Hatter Dormouse', 'William Conqueror', 'Caucusrace Caucusrace', 'Time Hatter', 'Elsie Lacie', 'Mad Tea', 'Soup Soup', 'Edgar Atheling', 'Gryphon Turn', 'King Queen', 'Alices Lewis'
方法 2:人名语料库
人名大全由安大略政府提供。政府发布了两个数据源,其中包括 1917 年至 2019 年的男性和女性婴儿的姓名。这一数据来源共产生了 164 034 个名字。由于安大略省是一个相对多样化的省份,名字涵盖了不同的种族背景,这对于不同的文本主体来说更加通用。您可以从这里下载这些名字:
或者你可以参考我上传到 GitHub 的这个 CSV,它是男性和女性名字的连接结果。这个 CSV 是本文代码中引用的内容。
总的来说,方法 2 产生了更好的结果,因为如果用户输入姓名字典,它考虑了姓名的别名。方法 2 的结果将输入到本文的以下部分。
挖掘角色名字的其他来源也进行了探索,如使用 NER,我没有继续展示这一点的原因是因为只有高端解决方案产生任何有用的结果。通过小规模的解决方案,如在 NLTK 和 Spacy 中使用内置的 NER 模型,我们最终得到了太多的假阳性,尽管上面的结果也会产生假阳性,但它们不会达到 NER 解决方案的程度。通过 NER 的一个解决方案确实产生了有用的结果,那就是在这个文本语料库上用带标签的命名数据训练 BERT。然而,尽管这产生了很好的准确性,但是在时间和金钱上也是非常昂贵的。我没有时间或资源来标记数千本关于姓名数据的书籍并重新训练 BERT 模型,也没有大量资金分配给 GPU 来帮助训练过程。因此,找到名字的简单方法就是我现在展示的方法。
寻找角色互动
这个部分是由你定义角色互动的方式决定的。从本系列的第 1 部分,我们发现有许多方法可以识别角色交互。从 Labatut 和 Bost 那里,他们强调了 6 种不同的方法来识别角色之间的互动[2],即
1)角色的共现
2)角色之间的直接言语互动
3)角色之间的明确提及
4)非言语互动(打斗、接吻、杀戮等)。)
5)角色之间的从属关系(同事、家人、朋友等。)
6)1–5 的混合方法
就本文的目的而言,我们将关注角色交互的第一种方法,即角色的共现。如果两个字符的名字出现在 N 个单词之间,我们可以定义这两个字符共现。在 Bonato 等人的“挖掘和建模字符网络”中,用户可以确定 N 的值。他们选择 N 等于 15。为了我们的目的,我们可以使用相同的 N 值。
创建角色网络
为此,我们将创建一个加权网络。节点将是字符本身,边将由我们上面发现的共现形成。边的权重将由给定正文中的共现频率来确定。例如,如果爱丽丝和王后在文本正文中有 15 次交互,则与连接爱丽丝和王后的边相关联的权重将是 15。

以圆形格式可视化的网络,如果节点的度数高,则节点的尺寸较大(图片由作者提供)

这是与《爱丽丝梦游仙境》中前 10 个角色相关的网页排名(图片由作者提供)
在网络中可以看到,爱丽丝、国王、王后都是非常突出的角色,边缘权重很高。同样,我们可以看到,最重要的人物(通过节点的页面排名捕捉到的)确实是书中的重要人物。我们看到爱丽丝、三月兔、红心皇后和红心国王是前 4 个最重要的角色。
钟-卢模型
根据 Bonato 等人写的论文,Chung-Lu 模型在捕获原始字符网络的结果方面是表现最好的模型,因此我们将在这里实现该模型。Dario Fasino、Arianna Tonetto 和 Francesco Tudisco*【5】的论文 用 Chung-Lu 随机图模型生成大型无标度网络概述了该模型,他们已经通过他们的 Github 库【3】将与该模型相关的 Python 和 MATLAB 源代码开源。我们可以对输入参数做一些轻微的重构,以匹配我们网络的度分布。*
真实世界的应用
现在你可能会想,这是一个有趣的实验,但在现实世界中它能真正应用到哪里呢?今天讨论的论文中介绍的方法的应用只是一个你如何构造问题的问题。例如,如果您在法律行业工作,创建一个法律网络可能会很有用,其中节点是法律,连接节点的边是基于原告和被告违反的法律。一旦创建了这个网络,你基本上就有了一个强大的图形数据库,它将各种法律与法官/陪审团已经做出裁决的各种犯罪和案件联系起来。这个网络也可以是暂时的,随着时间的推移,你可以看到新的节点被添加,新的边开始形成,并对未来的边和节点可能如何被创建/交互有一个想法。这些网络可以使用 Chung-Lu 或其他模型进行建模,以得出商业决策。
结论
总的来说,本文回顾了在上一篇文章的文献综述中介绍的各种主题和概念的实现。它涵盖了文本挖掘、网络分析和图形建模的各个方面。这个系列的目的不仅仅是涵盖一篇研究论文,而且是去实现这篇论文,这是大规模公司中许多研究科学家和研究工程师做的一项任务。
一个好的问题解决者能够运用他们所读到和学到的东西,并应用到他们所面临的问题中。当涉及到法律行业时,类似于此的基于网络的方法可能会产生强大的结果。
这个领域的研究还有很多方法可以进一步发展。在未来关于这个主题的研究论文中,我希望看到一种时间网络创造的感觉,我们可以看到随着故事的进展,网络是如何变化的。这可能是流行的长时间运行的书籍/电影/电视节目,如哈利波特,辛普森一家,速度与激情。我还希望看到未来的研究论文涵盖这样一个事实,即这种方法不会在短篇故事或小说中产生强大的结果。
下面的脚本是上面显示的所有代码,它们被包装成一个主函数。你也可以在这里参考与这篇文章和我的许多其他文章相关的 GitHub 库。
资源
- [1] Bonato,a .,D'Angelo,D. R .,Elenberg,E. R .,Gleich,D. F .,& Hou,Y. (2016)。"挖掘和建模人物网络"网络图的算法和模型:第 13 届国际研讨会,WAW 2016,加拿大魁北克蒙特利尔,2016 年 12 月 14-15 日,会议录 13(第 100-114 页)。斯普林格国际出版公司
- [2]文森特·拉巴图特和泽维尔·博斯特。2019.虚拟人物网络的提取与分析:一项调查。美国计算机学会计算调查 52(5):89。https://doi.org/10.1145/3344548
- https://github.com/ftudisco/scalefreechunglu
- [4]https://www . legal current . com/Alice-in-wonderlands-adventures-in-case-law/#:~:text = Note % 20 that % 20% E2 % 80% 9 calice % 20 in % 20 wonderland,more % 20 than % 20 100% 20 years % 20 force。
- https://arxiv.org/pdf/1910.11341.pdf
如果你喜欢这篇文章,这里还有一些你可能会喜欢的:
* *
数据科学面试中要避免的错误
原文:https://towardsdatascience.com/mistakes-to-avoid-in-the-data-science-interview-7a79308872be
你将来如何学习和避免这些错误

照片由 LinkedIn 销售解决方案在 Unsplash 上拍摄
D 数据科学是科技行业发展最快的领域之一。如果你正在寻找一份大学刚毕业的入门级数据科学家(DS)或机器学习工程师(MLE)的工作,了解一些面试中的常见错误是很重要的。一个简单幼稚的错误会降低你入围的机会。
如果你一直被拒绝 DS 或 MLE 的角色,你需要分析并找出你努力的不足。这个博客是关于候选人在 DS 或 MLE 面试中常犯的错误。
我很幸运地面试了 70 多名不同职位的候选人,他们帮助我分享了我在 Medium 上的经验(感谢 AlgoAnalytics )
错误 1: GitHub 储存库没有 README.md 或 readme . MD 不完整
许多入门级 DS 候选人认为在 GitHub 上共享 jupyter 笔记本可以对他们的个人资料产生重大影响!然而,HR/非技术招聘人员可能不知道什么是 jupyter 笔记本文件或者如何打开 jupyter 笔记本文件?!
为了展示您的辛勤工作,请花些时间在自述文件中写一份项目的高级描述。理想的自述可以有(但不限于):
- 介绍您试图解决的问题
- 数据集的来源
- 如果数据被抓取,你是怎么做到的?
- 考虑或使用了哪些基线模型?(稍后将详细介绍)?
- 用的是什么算法?取得了哪些成果?
- 如何重现结果?
- 如果 app 需要 Docker,如何运行容器?
- 如果应用程序已部署,应用程序的链接(奖励)
错误二:简历上断开的超链接!
这似乎是每个人都会做的普通检查,但我在简历上看到过一些断开的超链接。你不希望你的面试官看到页面找不到 404 📛并且在面试前制造一个软弱的印象!
错误 3:你的机器学习模型没有部署
机器学习的目标是解决一个问题。当模型在生产中,用户/服务正在使用预测时,我们可以这样做。

因此,在现实环境中部署一个 ML 模型是值得学习的。它可以帮你展示:
错误 4:在个人项目中直接跳到最先进的(SOTA)深度学习

不要在第一次迭代中就跳出来尝试 SOTA 算法,因为它很吸引人,看起来也很酷。从基线模型开始。例如,预训练的嵌入将为 NLP 任务提供强大的基线。
基线模型可以是启发式模型,甚至是非 ML 模型!找出基线模型没有捕捉到的东西,这将有助于你为尝试新的实验设定方向。
错误五:没有练习 Python/DSA 题
即使一些公司没有 DSA 轮次,DS 候选人也应该对 Python 基础和数据结构有很好的了解。在我作为 DS 面试官的短暂经历中,我看到候选人知道 python 内置,但由于缺乏实践,他们很难应用/解决简单的问题,如合并两个排序数组。
所以我强烈推荐我们用基本的 DSA 问题(我们无法逃脱leet code)!💻)并提高我们解决问题的技巧。
错误 6:没有练习关于项目的常见问题
尽管这是面试中最常见的问题之一,告诉我一些关于这个项目的情况?许多候选人在项目介绍和指标上花了更多的时间,但很少有人谈论他们克服的影响和挑战!
这是可以帮助你的最重要的问题之一:
- 推动面试讨论向有利于你的方向发展
- 展示你的专业领域
- 展示你的沟通和讲故事技巧
所以,面试官希望你分享的一个理想答案是:
- 您试图解决的业务问题的初始背景
- 谁是该解决方案的最终用户?他们是如何使用模型预测的?
- 数据来源
- 预处理步骤
- 基线模型和其他实验
- 用于评估的指标
- 模型部署和挑战
我强烈建议读者写下并练习常见问题的答案。这会让你在回答面试中的问题时感到舒服。
错误 7:建立一个坚实的基础
这是我在初级 DS 考生中观察到的最被低估的建议和错误之一。很多考生错过了基础,跳到了高级概念。例如,忽略递归神经网络(RNN)和长短期记忆(LSTM)模型,直接跳到变形金刚!请避免这样做,因为基础知识会帮助你打下高级概念的基础。
把基本面放下来,你做任何事的水平都会上升——迈克尔·乔丹。
有许多免费资源可以帮助你更好地理解这些概念。我比较喜欢的几个资源是 斯坦福在线 和 用 Scikit-Learn 动手机器学习和 TensorFlow 书。
然而,在应用机器学习概念时,请确保遵循标准且经过验证的资源。例如,一个常见的误解是使用标准缩放器分别对训练和测试数据进行特征缩放📛
# Example of how NOT to scale features!
# We should NEVER use fit_transform() on the test data.from sklearn.preprocessing import StandardScalersc = StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.fit_transform(X_test)
结束语💡
数据科学面试极具挑战性,而且没有赢得面试的灵丹妙药。每个公司都有评估候选人的标准和程序。在面试过程中帮助我的一些想法是:虚心接受反馈,分析你本可以做得更好的地方,永远不要因为失败而放弃!
我希望这篇博客分享一些真知灼见,以避免错误,帮助你在未来的面试中脱颖而出。一定要和你的网络分享这个故事,并评论你最近的 DS 面试经历。
请随时关注或联系 LinkedIn 。到那时,在下一个帖子里再见:)感谢阅读。保重!
用数据减轻你的模型
原文:https://towardsdatascience.com/mitigate-your-model-with-data-3c4216580dae
用这些简单的技巧让你的模型脱离生命支持系统,让你的数据更优秀

介绍
预测建模的世界可能是复杂而可怕的。机器学习模型可能有点复杂,了解什么破坏了模型的输出可能是一个非常难以解决的问题。通常,预测模型的问题实际上根本不是模型本身的问题。相反,这个模型建立在一个糟糕的基础上——糟糕的数据。
修复数据并准备将其放入模型中有很多细节和细微差别。这通常包括探索特征、移动、删除、清理和数据测试等任务。准备训练和测试集,工程特性,标准化,以及更多的东西。今天,我想从一个绝对混乱的模型开始,它有糟糕的数据,糟糕的功能选择,然后试图通过操纵测试和训练数据来扭转这个模型。在这样做的时候,我也将展示人们可能用来解决这类问题的各种技术。我还将根据我在这里概述的一步一步的建模过程来概述这个项目:
此外,如果您想让笔记本跟随或自己尝试这些代码,那么可以在 Github 上找到这个笔记本,它还克隆了一个完整的示例数据存储库,包括这个模型中使用的数据集:
第 1 部分:我们的模型
对于我们的模型,我去了五点三十八分去获取关于航空安全的数据。FiveThirtyEight 也是一个很好的平台,可以获得一些数据来进行实践,所以对于那些正在学习或不断改进的人来说,这些数据集足够干净,可以使用,但又不至于干净到不需要任何工作。也就是说,我认为它们是很好的练习。
数据争论
对于我们的第一个实例,我们不会按照我在那篇文章中概述的过程进行建模。这只是为了让我们可以得到一个特别糟糕的模型,并通过修改特性使它起死回生。现在让我们开始争论步骤,这是我们在这里实际要做的几个步骤之一,从导入我们的依赖项和通过 Bash 用 Git 获得我们的 CSV 文件的克隆开始:
# deps
import pandas as pd
import numpy as np!git clone [https://github.com/fivethirtyeight/data](https://github.com/fivethirtyeight/data)
这些数据是由 FiveThirtyEight 根据知识共享——归属许可授权的,所以你可以在任何地方免费使用。
现在,我们将继续通过 read_csv()读取熊猫的 CSV 文件:
df = pd.read_csv("data/airline-safety/airline-safety.csv")
df.head(5)

(图片由作者提供)
好吧,我不知道 Aroflot*是谁,但是
我不会上他们的飞机。
预处理
这个处理步骤更像是预处理,因为我们的任务只是让数据帧足够可读。一旦它足够易读,我们将跳过我将采取的下几个步骤,因为我们将看到这些步骤对于做出一个伟大的预测是多么重要和有效。对于这个数据框,我要做的第一件事就是通过获得空值的观察值的总和来检查缺失值。
df.isnull().sum()airline 0
avail_seat_km_per_week 0
incidents_85_99 0
fatal_accidents_85_99 0
fatalities_85_99 0
incidents_00_14 0
fatal_accidents_00_14 0
fatalities_00_14 0
dtype: int64
多么干净方便,没有缺失值。也就是说,我将直接进入功能选择,但是我不会根据它们的优势来分离这些功能,因为我们将在获得针对此数据的模型的初始“轻而易举”指标后进行分离。
特征和目标选择
既然我们知道我们实际上可以将这些特征通过一些数据处理,让我们直接选择我们的特征和我们的目标。由于这是我的低努力示例,我们没有关注数据,所以我不会在这里花太多精力。相反,我会选择一个目标,然后继续前进。
所以…我控制不住自己,因为这里的特色很吸引人。

(图片由作者提供)
根据 README.md,以及我对该数据的假设,我们有事故、致命事故(导致 1 人或更多人死亡的事故),然后我们有死亡人数。我的看法是,我们可以制作一些不同的建模示例,这将非常酷。我最初的一个想法是,我们可以根据我们的飞机平均每周飞行多少公里来预测我们可能遇到的死亡人数。我的另一个很酷的想法是提供一些其他值,并让一个二元分类器尝试预测它是否发生在 1985-1999 年或 2000-2014 年之间,鉴于 1999 年是我出生的年份,我认为找出飞机以前是否是死亡陷阱会很有趣。
2001 年 9 月还发生了一个现实世界的变化,我认为这很有可能改变政府对这些航空公司的监管方式。我认为,随着对这些公司更多的安全、更多的责任和更密切的关注,想象一下这样的事件对不相关和不相关的事情有多大的影响可能会很有趣。我认为在 2021 年的类似数据中也可以观察到同样的现象,我认为这将是一项有趣的研究。
看看我的遭遇。
这就是数据科学如此有趣的原因,但让我们现在只选择一个目标来研究,也许将来当我有空时,我会在这个数据集上尝试不同的目标,也许我会在我的博客上的另一篇文章中使用它!无论如何,今天我走的是安全路线,因为这个数据集的一个不幸之处是…
len(df)56
这么少的观测数据,分散在两个独立的年份中,这将有一个严重的风险,与我们在这里保留的较少特征不匹配。考虑到这一点,我认为这里最好的方法可能是二进制分类器。二进制分类器将要求我们设计一个目标,然而,这是有问题的,因为这应该是一个非常低工作量的模块。然而,正如我在许多类似文章的结论中所讨论的那样,数据永远不会是相同的——所以总有一些情况下我们需要做一些不寻常的事情。这是做这件事如此吸引人的部分原因。然而,对于这个观察长度,我们只有 56 个总观察计数,所以我们可能想要以不同的方式处理事情。例如,如果我们对此进行训练/测试/val 分割,训练数据将是
len(df) * .75 * .7531.5
长时间观察。换句话说,我们将训练 31 个观察值,预测 11 个验证观察值和 14 个测试观察值,同时用少量数据训练我们的模型。更好的选择可能是在这里进行常规的训练/测试。让我们首先设计我们的目标,它基本上只是一个布尔值,但是我们将把它存储为一个整数。我们还需要设计一些功能来配合它。这将有助于我们的无数据问题的实质。鉴于我们的长度如此之短,通过选择这个目标,我们可以有效地将数据翻倍。首先,我将把我们的功能放入一系列标签中:
features = ["incidents", "fatal_accidents", "fatalities"]
请注意,这些功能不包括年份。这是因为我现在将使用一行 for-loop 来连接每个特性的所有内容,这样特性名称中就不再包含年份。我们需要将这些名称转换成数据框中的一个新列。这个简洁的 for 循环可以做到这一点,你也可以同时使用 lambda 和 map 来达到同样的目的。
z = [list(df[feature + "_85_99"]) + list(df[feature + "_00_14"])for feature in features]
现在,当我们把这些放在一个数据框架中时,我们有效地加倍了我们的观察:
len(z[1])112 features = pd.DataFrame({"indicidents" : z[0], "fatal_accidents" : z[1],
"fatalities" : z[2]})
我写了一个简单的函数来生成 y,这只是一个简单的条件,根据它所代表的值返回一个或另一个数字集合。因为所有超过原始数据帧长度的值(没有变化)都是我们附加的值,我们可以利用这一点。不幸的是,这意味着许多函数对此不起作用,因为我们必须知道值的索引。考虑到这一点,我编写了这个初等函数来完成所有这些工作:
def yearcategory(x):
z = []
for count, i in enumerate(x):
if count > len(df):
z.append(2014)
else:
z.append(1985)
return(z)years = yearcategory(features["fatalities"])
现在我们有了建模这个目标所需要的一切!一路走来,我们也做了一些光特征工程。我们会回来,并在这方面进行迭代,以使我们的模型在未来变得更好。另一个我确实去掉了的特性是 km traveled 特性,但可能不应该去掉,因为在这个例子中我们可能根本不需要去掉任何特性。然而,这显然不重要,就像航空公司的标签一样。它们对研究不重要,因此我们不妨把它扔掉。
对我来说,不处理数据很难建立一个模型。很可能完全不可能做到完全诚实,但希望这只是进一步证明,即使做这么多也不总是足够的。
随机元素
每一步的大纲中有一些元素通常做得有点乱。例如,分割数据通常被认为是特征工程的一部分,但是通常将训练 X 和测试 X 一起处理是有意义的,这样它们接受相同类型的处理。我们将从把年份放入我们的特征数据框开始。
features["years"] = years
假设我们的其他特征都是连续的,我们可以很容易地将所有这些值放入一个模型中并得到一个预测,不需要任何处理。因为这是我们的演习,我们当然要这样做。这是我们的训练测试分割,然后将这些值转换成适当的格式,以便我们的模型进行解释:
from sklearn.model_selection import train_test_splittarget = 'years'
train, test = train_test_split(features)
trainX = train.drop(target, axis = 1)
trainy = train[target]
testX = test.drop(target, axis = 1)
testy = test[target]
建模
好消息是。我们仍然不太了解这些特征。甚至不知道这些价值观的含义是什么,并在我们说话的时候模拟它,这似乎很奇怪,但不管怎样,这是我选择的人生道路。我们需要一个分类器模型,在预测像这样的一组二元特征时,它将非常有效。我们基本上可以使用任何分类器,但是如果它是一个通用分类器,例如通过随机森林分类器的基尼指数,求解器会有过度思考的倾向。在整个过程中,特别是由于我们的数据量较小以及我们对目标的各种限制,我们可能希望选择一个更适合具有二元集的目标的模型。
from sklearn.linear_model import LogisticRegression
我最终选择了逻辑回归。我之所以选择这个,是因为它们相当擅长预测二进制值。
model = LogisticRegression
model.fit(trainX, trainy)
yhat = model.predict(testX)
现在让我们看看这个模型做得有多好:
from sklearn.metrics import accuracy_score
accuracy_score(testy, yhat)0.4642857142857143
哇,这个模型做得太糟糕了。
这并不奇怪,但我们现在可以通过演示这种情况下的一些处理和缓解技术来提高该模型的准确性!
第 2 部分:减轻我们的模型
现在我们有了一个表现不佳的模型的例子,让我们通过在数据中投入更多的工作来扭转这种局面。当然,最初我们这里的数据形式非常有限,所以我们的偏见总是会做出弱假设,在这种情况下,我们对此真的无能为力。有一种潜在的方法可以帮助解决这个问题,那就是使用 boosting 算法,甚至改变我们的模型来解决这个问题。让我们从分析致命事故特征开始。获得 Pandas 中连续值的良好汇总的一个好方法是使用 Series.describe()方法:
features["fatal_accidents"].describe()count 112.000000
mean 1.419643
std 2.236625
min 0.000000
25% 0.000000
50% 1.000000
75% 2.000000
max 14.000000
Name: fatal_accidents, dtype: float64
我已经立刻发现了一些东西。75%的数据低于 3,这意味着最大值 14 可能是这个数据中的一个巨大的异常值。我们可能应该将这样的值映射为平均值。除此之外,为了让我们的数据在数字上更接近,我们可以通过一个标准的定标器来运行它。然而,在我们做任何事情之前,我们可能想要检查这个值对于我们的模型在统计上有多重要。当我们有这样的数据时,我们将使用一种典型的方法来这样做,即来自 SciPy.stats 的标准独立 t-test。另外,如果您希望学习 Python 中的一般科学知识,这也是一个很好的学习库。
一些测试
要在 Python 中设置一个测试,我们需要从数据帧中提取一些值,以找到符合某些标准的样本。要做到这一点,最好的方法可能是条件屏蔽。条件屏蔽允许我们使用迭代或映射创建一个位数组,然后我们可以用这些条件索引我们的数据帧,在我们的位数组中过滤掉错误值。
from scipy.stats import ttest_indmask = [val > np.mean(features["fatal_accidents"]) for val in features["fatal_accidents"]]
这里我只是创建了一个迭代循环。这个循环的每次调用都会将 false 返回到我们的 mask 类型中,这是一个通过这个语法构造的数组。现在,我们只需将该掩码放在数据框上,这将删除掩码数组中的假值。
onlyabovemu = features[mask]
现在我们可以使用 scipy.stats ttest_ind()方法来测试我们的数据。我们会想要比较我们的目标之间的差异。
ttest_ind(features["years"], onlyabovemu["years"])
现在我们将检查 P 值:
Ttest_indResult(statistic=1.538691906112917, pvalue=0.12605892096661567)
我的天啊。
这里有一个潜在的因素可能会影响这些值,我们将在查看数据本身后检查这一点。我有一个痛苦的怀疑,也许我们的平均值被一些异常值打乱了,这甚至给这个测试带来了问题,因为我们所有的样本都在平均值的上半部分,谁知道我们整个样本是基于什么样的数据。让我们检查一下长度:
len(onlyabovemu)
37
与我们数据中的 112 个值相比,这是相当低的。我们真正的计数应该是观测值的一半左右。这表明该数据在一个方向上有异常值。数据需要比这集中得多,所以我们必须去除这些异常值。不过,在此之前,我们先来看看其他特性。这是对其他特征的描述,至少看起来甚至不如有统计学意义。
features["fatalities"].describe()count 112.000000
mean 83.964286
std 132.741701
min 0.000000
25% 0.000000
50% 3.500000
75% 109.250000
max 537.000000
Name: fatalities, dtype: float64features["incidents"].describe()count 112.000000
mean 5.651786
std 8.540006
min 0.000000
25% 1.750000
50% 3.000000
75% 7.000000
max 76.000000
Name: incidents, dtype: float64
Name: fatalities, dtype: float64
在死亡的例子中,致命性通常为 0,这可能是一个完全无用的特征。由于 fatal_accidents 特性,这也是部分正确的。我们可能希望完全摆脱这个特性,因为它给我们带来的问题比解决方案更多。这是因为观察结果主要保留在数据的上层,它并不真正影响每年有多少人死于飞机故障——仅此而已。不是愤世嫉俗,但通常如果一架飞机坠毁,每个人都会死。至于事件,这可能是我们见过的最一致的,尽管在这种情况下,0 可能会提交很多对我们的均值的权重。记住我们测试和获得显著性的整个方法就是这个意思。也就是说,如果我们想让这个模型很快脱离生命维持系统,我们肯定需要修复这些装置。
我还对以下特性进行了 T 检验:
ttest_ind(features["years"], onlyabovemu["years"])
mask = [val > np.mean(features["fatalities"]) for val in features["fatalities"]]
onlyabovemu = features[mask]
ttest_ind(features["years"], onlyabovemu["years"])mask = [val > np.mean(features["incidents"]) for val in features["incidents"]]
我只是一遍又一遍地运行相同的代码,如果你真的想这样做,你可以有条不紊地进行自动化测试,尤其是当你有很多特性的时候。以下是事件的 P 值:
Ttest_indResult(statistic=1.1895326000264201, pvalue=0.23614792418482572)
这是死亡人数的 P 值:
Ttest_indResult(statistic=0.9387567984962254, pvalue=0.3494167340587091)
正如我所怀疑的,死亡特性真的一点也不重要。
在我们移除一些值之后,我们将标准地缩放所有这些连续的特征。这将在数字上缩小差距,并使我们更容易过滤值。此外,它还会为我们的模型做类似的事情,所以我们为使数据更好而做的处理也在改进模型,
谁能想到呢?
预处理
为了消除这些明显高于我们真实平均值的值,我们将根据 z 值对数据进行归一化处理。为此,我们将利用 scipy.stats 中的 zscores 函数:
z_scores = stats.zscore(features)
为了做到这一点,我们将使用分位数定标器。分位数定标器相当于标准定标器,但用于分位数。这只是看待数据的不同方式。我们可以利用分位数,而不是只关注平均值。这可以减少离群值的影响,并且我们在扩展数据后仍然可以删除它们。所有这些工作的现实是,如果我们一开始就这样做,现在就会容易得多。现在,我们将只使用绝对距离,确保绝对距离小于 3,小于平均值的 3 个标准偏差就是这意味着什么。
abs_z_scores = np.abs(z_scores)
filtered_entries = (abs_z_scores < 3).all(axis=1)
df = features[filtered_entries]
这就是为什么条件掩蔽是不可思议的。
无论如何,我们还可以做更多的事情,但是这篇文章很快就会成为一本书,所以让我们看看这些小小的变化对我们的准确性产生了什么影响。
结论
现在我们几乎已经完成了这个项目,让我们在得到最终预测之前最后一次反思我们的冒险和我们模型的缺点。真的,这么多的数据很难处理。这个功能的数据翻倍确实表现不错,但是,我们的模型显然还需要更多的观察。我希望我选择了一个更大的数据集,但是所有的数据都是不同的,你不可能总是得到你想要的数据。这是我们最后的预测:
target = 'years'
train, test = train_test_split(features)
trainX = train.drop(target, axis = 1)
trainy = train[target]
testX = test.drop(target, axis = 1)
testy = test[target] model.fit(trainX, trainy)accuracy_score(testy, yhat)0.7857142857142857
仅仅使用这些简单的技术,我们就将这个模型的精确度提高了近一倍。您仍然可以帮助处理这些数据的一些方法包括某种增强算法,或者添加更多的数据。另一个可能有帮助的方法是设计一个新的特性或者放弃不太重要的特性。我不确定它会对诚实产生什么样的影响,但我认为这是一个有趣的项目。
谢谢你读我的文章,它对我来说意味着整个世界。我希望这很好地证明了立即关注你的数据是多么好。如果我一开始就把时间花在数据上,有很多事情可以做得更好。祝数据科学快乐!
利用分类模型减轻 DDoS 攻击
原文:https://towardsdatascience.com/mitigating-ddos-attacks-with-classification-models-aa75ea813d85
使用 Dask、XGBoost 和 Stacked Ensembling 抵御无处不在的网络攻击

由 Unsplash 上的 Kvistholt 摄影拍摄
Metis 的数据科学和机器学习训练营的模块#4 已正式结束!本模块重点关注分类,这是监督学习的一个子集。在我们的期末项目中,我们被要求找出一个可以用分类方法解决的问题,并评估用我们制作的分类模型解决这个问题的可行性。
对于我的项目,我选择关注组织面临的一个越来越常见的问题:分布式拒绝服务(DDoS)攻击。我想构建一个解决方案,让组织能够识别恶意流量和合法流量,并防御 DDoS 攻击。
这个项目是一次宝贵的经历,原因有几个。如果我想使用我选择的数据集,它迫使我熟悉 Dask、RAPIDS/cuML 和 XGBoost 的 GPU 支持等新工具。在此之前,我也没有从纯预测能力的角度处理过建模问题,并且发现在追求更好性能的过程中进行调优和迭代是令人愉快的。最后,我正在处理的数据很难理解。我在一定程度上了解网络安全,但肯定没有到了解原始流量日志的程度。使用这样一个外来数据集并从中获得洞见,这本身就是一个有益的挑战。因为我在这篇文章中学到了很多,我希望其他人也能从这篇文章中学到东西。让我们开始吧!
基本原理
什么是 DDoS 攻击?
分布式拒绝服务(DDoS)攻击是一种常见的、几乎无处不在的网络攻击形式。其目的是通过用垃圾流量淹没目标周围的网络、互联网连接服务或技术基础设施来破坏组织的运营。定向到目标的流量会严重限制可用性,或者使其完全离线。
DDoS 攻击之所以有效,部分原因是它们利用了已经被恶意软件入侵的互联网连接设备。攻击者利用数十、数百、数千甚至数百万台设备中已经存在的漏洞来获得远程控制。这些“僵尸”设备(或“僵尸”)然后被编排成一个“僵尸网络”,并被攻击者导向目标服务。这个远程编排过程就是分布式拒绝服务的“分布式”部分。随着联网家用物品的激增,如冰箱、T2 恒温器和 T3,攻击者有了更多加密较弱的设备来参与他们的攻击。

为什么 DDoS 攻击是一个值得解决的问题?
DDoS 攻击是一个值得优先考虑的问题,原因有很多。它们是最常见的网络攻击形式之一,因为发起和维护它们相对便宜——一项估计发现,在黑暗网络上维持一次中等规模的 DDoS 攻击一个月的成本是 311 美元。
此外,民族国家、黑客组织和网络罪犯采用 DDoS 攻击的原因从政治、意识形态、金融到“因为我能”。攻击的矛头直指互联网巨头,如谷歌、亚马逊、金融机构、游戏公司、联邦机构、媒体和非营利机构。
就 DDoS 攻击可能造成的损失而言,很难量化。但是,组织必须考虑各种后果的成本,例如:
- 延长服务停机时间
- 员工的生产力限制
- 数据泄露
- 名誉损害
- 搜索引擎优化排名的损失
- 基于赎金/勒索的袭击
2017 年卡巴斯基调查发现,中小型企业忍受 DDoS 攻击的成本为 12 万美元。对于企业级组织来说,这个数字上升到 100 万美元 2M .此外, 2018 年的一项研究估计大型组织的停机成本在 30 万美元到 54 万美元之间。一份 2020 年 IBM 报告发现,数据泄露的全球平均成本为 386 万美元,而美国的平均成本为惊人的 846 万美元
可以说,DDoS 攻击对各种各样的组织来说是一种无处不在的威胁。它们成本极高,对运营具有破坏性,并有可能造成长期损害。组织的 IT 安全团队不仅应在战术层面关注这一问题,主管人员也应考虑 DDoS 攻击对其组织构成的战略威胁,并考虑他们有什么工具来防范这些攻击。

由 Lars Kienle 在 Unsplash 上拍摄
辖域
项目目标
考虑到我们希望解决的问题,我对这个项目的目标如下:构建一个二进制分类模型,通过正确识别合法/良性流量和恶意流量来减轻 DDoS 攻击对组织运营的影响。如果流量是合法的,它将被传递给某个组织的服务。如果是恶意的,就会被屏蔽。这将确保真实用户的服务可用性,并防止上述 DDoS 攻击造成的损害。

交通警察分类模型。图片作者。
成功指标
在业务方面,我们的成功指标有两个方面:
- 将模型将合法流量误识别为恶意流量的比率降至最低(理想情况下为 0,否则我们将为其执行攻击任务!)
- 通过以 90%或更高的正确识别率来减轻 DDoS 攻击的影响(我们愿意接受服务性能的微小降低,只要它们保持在线并且合法流量不被阻止)。
从统计角度来看,我们的成功指标如下:
- 回忆:尽可能接近 1.0
- 精度:大于等于. 9
- F2 :由于我们对召回率的加权比对精确度的加权更重要,我们将使用 F2 分数作为告知模型选择的北极星度量。
数据
这个项目的数据集是在得到位于新不伦瑞克大学的加拿大网络安全研究所的许可后使用的。该研究所提供的 DDoS 评估数据集包含各种 DDoS 攻击类型的最新数据。出于这个项目的目的,我们主要关注一种类型:DrDoS_LDAP。这个特殊的数据集包含了 210 万个以上的观察值、和 80 多个特征。它还严重偏向恶意流量,99.7%的观察结果被标记为恶意,而只有 0.03%被标记为良性。这显然在建模过程中提出了一些挑战,但它符合我们在现实世界场景中预期看到的情况——DDoS 攻击通过将大量垃圾流量定向到目标来工作,因此与少量良性日志相比,数百万条恶意日志对于组织在攻击期间预期会看到的情况是有意义的。
该数据集是由 CIC 在内部测试平台上创建的,这意味着它是在受控环境中发生的模拟攻击。良性背景流量也被自动化。从原始数据中提取特征。PCAP 文件是使用 CIC 内部开发的工具CIC 流量计完成的。所有的感谢和赞扬都应归功于 UNB/加拿大公民和移民部的学生和工作人员,他们使得这个数据集可以公开使用。
代码、建模和结果
数据争论
就清理这个数据集而言,没有大量的工作需要做。我在处理这些数据时面临的最大障碍是它的庞大规模!
我第一次尝试在我的 Macbook 上把这些数据读入 Pandas,结果崩溃了,所以我转到了我的 PC 上。它有 32 GB 的内存,16 核英特尔 9900K CPU 和 NVIDIA 2080ti GPU。当我试图加载这个数据集时,它蓝屏了。
接下来是一个漫长而有启发性的兔子洞之旅,讲述了熊猫对单个 CPU 内核的限制,以及如何解决这个问题。输入https://dask.org/****。** Dask 允许您在多个 CPU 内核上并行化流行的 Python 库,如 NumPy、Pandas 和 scikit-learn。它将工作负载“分块”成更小的块,将它们传递给不同的 CPU 内核并行执行工作,重新组合结果,并以预期的格式返回。下面是我如何使用 Dask 将这个数据集读入熊猫:**
**from** dask **import** dataframe **as** ddddf **=** dd**.**read_csv(r"FILEPATH", dtype**=**{'SimillarHTTP': 'object'},blocksize**=**'64MB')df **=** ddf**.**compute()
就这么简单!
读入数据帧后,我必须清理列名:
df**.**columns **=** df**.**columns**.**str**.**replace(' ', '')
删除所有在现实世界中不相关的功能:
df**.**drop(columns**=**['FlowID', 'SourceIP', 'DestinationIP', 'Timestamp', 'SimillarHTTP', 'SourcePort', 'DestinationPort'], inplace**=True**)
去掉任何 NaN 或 Infinity 值:
pd**.**options**.**mode**.**use_inf_as_na **=** **True**df**.**dropna(inplace**=True**)
并将标签编码:
labels **=** pd**.**get_dummies(df['Label'])
建模
清理完数据集后,我准备开始评估模型。因为数据集是不平衡的,所以在进行训练/测试分割时,我很注意分层。这确保了相同比例的观察值出现在每个数据分割中。
X **=** df**.**drop(columns**=**["Label"], axis**=**1)
y **=** labels**.**BENIGNX_train_val, X_test, y_train_val, y_test **=** train_test_split(X, y, test_size **=** .2, stratify**=**y, random_state**=**1)X_train, X_val, y_train, y_val **=** train_test_split(X_train_val, y_train_val, test_size**=**.25, stratify**=**y_train_val, random_state**=**1)
由于数据集如此之大,我很快了解到拟合模型是一项耗时的工作。幸运的是,Dask 在这里也掩护了我!
**import** joblib
**from** dask.distributed **import** Client
client **=** Client(processes**=False**)
Joblib 是 Python 的流水线库,提供了一些并行化能力。scikit-learn 构建在 joblib 之上。可以传递给许多 scikit-learn 模型的 n_jobs 关键字参数由 joblib 支持。
Dask 与 joblib 完美集成,可用于为 scikit-learn 模型提供另一个 joblib 后端:
**with** joblib**.**parallel_backend("dask"):
lr**.**fit(X_train_scaled, y_train)
这在本地机器上提供了额外的并行化能力,或者扩展到基于云的计算集群的能力(如果您有这种能力的话)。
我使用逻辑回归模型作为起点,并使用初始结果来通知进一步的迭代:
y_val_preds **=** lr2**.**predict(X_val_scaled)print("Precision: {}, Recall: {}, F2 Score: {}"**.**format(precision_score(y_val, y_val_preds), recall_score(y_val, y_val_preds), fbeta_score(y_val, y_val_preds, beta**=**2.0)))group_names **=** ['True Neg', 'False Pos', 'False Neg', 'True Pos']group_counts **=** ["{0:0.0f}"**.**format(value) **for** value **in** lr_confusion**.**flatten()]group_percentages **=** ["{0:0.4%}"**.**format(value) **for** value **in** lr_confusion**.**flatten()**/**np**.**sum(lr_confusion)]labels **=** [f"{v1}\n{v2}\n{v3}" **for** v1, v2, v3 **in** zip(group_names,group_counts,group_percentages)]labels **=** np**.**asarray(labels)**.**reshape(2,2)sns**.**heatmap(lr_confusion,cmap**=**'rocket_r', annot**=**labels, fmt**=**'', square**=True**, xticklabels**=**['DrDoS_LDAP', 'Benign'], yticklabels**=**['DrDoS_LDAP', 'Benign'])plt**.**xlabel('Predicted Traffic Type')plt**.**ylabel('Actual Traffic Type')plt**.**title('Logistic Regression Confusion Matrix');
基线逻辑回归

图片作者。
- F2: .921
- 召回:. 921
- 精度:. 918
第一次通过还不错。
因为我想考虑类的不平衡,所以我尝试将“balanced”传递给 class_weights 参数,作为逻辑回归的第二次迭代。这使得我们的标签中存在的类的相对权重与该类中的观察数量成反比(在本例中,恶意类的权重为 0.03,良性类的权重为 0.97)。这有效地惩罚了模型在少数类(良性)上的错误,比在多数类(恶意)上的错误更严重。混淆矩阵结果证实了这一点:
加权逻辑回归

图片作者。
F2: .954
回忆:. 990
精度:. 831
这两个模型让我对逻辑回归在这个问题上的表现有了很好的理解。虽然对参数进行更多的调整可能会得到更好的结果,但我也想评估不同方法的可行性。
基线随机森林
**with** joblib**.**parallel_backend("dask"):
rf**.**fit(X_train, y_train)

图片作者。
F2: .990
回忆:. 990
精度:. 990
在没有任何参数调整的情况下,随机森林在这个问题上表现得非常好!因此,我进行了一些额外的迭代,探索处理数据中类别不平衡的不同方法,以及这将如何影响随机森林结果。我尝试了平衡类权重,在引导级别平衡类权重,并使用不平衡学习包对大多数(恶意)类进行欠采样。这些方法要么根本没有多大作用,要么提高了模型正确识别良性流量的速度,同时严重阻碍了其识别恶意流量的能力:
具有引导类权重的随机森林

图片作者。
F2 .984
召回:. 984
精度:. 987
与基线相差不大。
带数据重采样的随机森林(不平衡学习)

图片作者。
F2: .871
召回:1.0
精度:. 574
对良性流量表现完美,对恶意流量表现不佳。值得归档。
因为随机森林表现如此之好,我想尝试的下一个方法是梯度增强树。梯度增强通常可以超越现成的随机森林,在 XGBoost 库中找到的梯度增强实现是许多数据科学家的最爱。
XGBoost 还有一个额外的好处,就是对 GPU 的支持。图形处理单元(GPU)主要用于玩 PC 游戏,但因为它们旨在以高帧速率实时渲染复杂的三维空间,GPU 非常适合计算密集型过程,如在大型数据集上拟合机器学习模型。NVIDIA 开发了一整套软件库,用于完全在 GPU 上执行数据科学和分析管道,称为 RAPIDS 。据我所知,RAPIDS 目前只能在 Linux/Ubuntu 机器上使用,但是有一些方法可以让 T4 在 Windows 上运行。然而,对于这个项目,我并没有陷入那个兔子洞,很高兴 XGBoost 中有原生 GPU 支持,这将我的模型拟合时间从使用 scikit-learn 的 Random Forest 的大约 8 分钟减少到使用 XGBoost 的 20 秒。
gbm **=** xgb**.**XGBClassifier(
n_estimators**=**100000,
max_depth**=**6,
objective**=**"binary:logistic",
learning_rate**=**.1,
subsample**=**1,
scale_pos_weight**=**99,
min_child_weight**=**1,
colsample_bytree**=**1,
tree_method**=**'gpu_hist',
use_label_encoder**=False**
)eval_set**=**[(X_train,y_train),(X_val,y_val)]
fit_model **=** gbm**.**fit(
X_train, y_train,
eval_set**=**eval_set,
eval_metric**=**'auc',
early_stopping_rounds**=**20,
verbose**=True**
)
基线 XGBoost

图片作者。
F2: .986
回忆:. 996
精度:. 946
除了 XGBoost 模型的出色速度之外,开箱即用的结果也同样出色。以至于随后重新调整参数的迭代几乎没有改变结果。
作为一个额外的好处,XGBoost 还为我们提供了相对容易地绘制特性重要性的能力:
fig,ax2 **=** plt**.**subplots(figsize**=**(8,11))
xgb**.**plot_importance(gbm, importance_type**=**'gain', ax**=**ax2)
当 importance_type 设置为 gain 时,将输出每个要素在信息增益方面的重要性,即当给定要素位于其子集中时,模型获得了多少预测能力。如您所见, MinPacketLength 特性具有极高的重要性:

图片作者。
肯定是值得进一步探索的东西,但是可解释性不是这个项目的目标,所以我们就此打住。
组装
我尝试的三种方法都表现得很好,但是它们都没有得到我想要的结果。为了从我的模型中挤出一点点预测能力,我转向了集成。****
集合获取我已经创建的模型的输出,并使用它们进行最终预测

图片作者。
集合有三种风格:硬投票、软投票和堆叠。****
硬投票集合
model_names **=** ["lr2", "rf", "imb_rf3", "gbm"]
model_vars **=** [eval(n) **for** n **in** model_names]
model_list **=** list(zip(model_names, model_vars))voting_classifier **=** VotingClassifier(estimators**=**model_list,
voting**=**'hard',
n_jobs**=-**1)
硬投票是指每个模型对输入的数据进行二进制分类,集合模型对投票进行统计并输出最终结果。以下是硬投票组合的表现:

图片作者。
F2: .993
回忆:. 993
精度:. 993
软投票集合
soft_voting_classifier **=** VotingClassifier(estimators**=**model_list,
voting**=**'soft',
n_jobs**=-**1)
软投票是指每个模型输出恶意/良性分类的概率。然后,集合模型汇总这些概率,并根据哪个概率高做出最终预测。以下是软投票组合的表现:

图片作者。
F2: .974
召回:1.0
精度:. 883
堆叠系综
stacked **=** StackingClassifier(estimators**=**model_list, n_jobs**=-**1)
堆叠集成是指每个模型的输出被用作元分类器中的特征。在 scikit-learn 中默认情况下,元分类器是逻辑回归,但它可以是您选择的任何方法。堆叠集成利用了所有模型的智慧,并输出最终的预测。最好给出一个堆叠的分类器模型来“覆盖”彼此的弱点,而不是为了获得最佳结果而相互对抗。堆叠是一种非常强大的集合方法,但也是非常计算密集型的。即使使用 Dask 进行并行化,这个模型也需要大约 60 分钟才能在我的台式机上安装 16 核英特尔 9900K CPU @ 5.0 GHz。然而,结果是值得的:

图片作者。
F2: 1.0
召回:1.0
精度:1.0
没有比这更好的了。堆叠集成分类器胜出。让我们看看它在最终维持集上的表现如何:

图片作者。
虽然不完美,但仍然是经过测试的最佳模型,也是我们提出的解决方案的最终选择。
结论
总结一下这个项目的主要收获:
- 分类被证明是解决这一问题的可行方案。
- Random Forest 和 XGBoost 开箱即用性能都很好。
- 具有逻辑回归、随机森林和 XGBoost 的堆叠集成总体表现最佳。
- MinPacketLength 特性值得进一步分析和理解。
解决方案验证
实际考虑如何验证您提议的解决方案始终是最佳实践。在这种情况下,很难说。我们的数据集是在一个受控的环境中创建的,因此我们预计真实世界的数据会更加混乱。但是,除了在 DDoS 攻击期间将其投入生产之外,我们如何在真实世界的数据上测试这个模型呢?
首先,我们可以在正常情况下测试它,看看它在识别常规合法流量方面表现如何。我们还可以对合成攻击进行测试,就像数据集是如何创建的一样。我们还可以根据供应商解决方案(如 Cloudflare、Akamai)对模型进行基准测试,最后一步是实际部署模型并评估其在攻击期间的性能。

照片由this engineering RAEng在 Unsplash 上拍摄
进一步的工作
与所有项目一样,这里也有需要改进和进一步迭代的地方。首先,这个项目只研究了一种类型的 DDoS 攻击。有许多种攻击(CIC 有许多攻击的数据集)具有不同的流量特征。添加更多类型的恶意数据并评估性能将是值得的,并可能使模型对现实世界的场景更加健壮。
第二个需要改进的地方是模型本身。我想从一开始就尝试集成,所以我没有尽可能多的时间来调优超参数。使用 GridSearchCV 来优化我的模型可能会将性能提升到不需要集成的程度。此外,我可以尝试不同的数据重采样方法,比如使用不平衡学习包进行过采样,或者尝试不同的分类方法,比如朴素贝叶斯。
第三个需要改进的地方是减少代码运行时间。总的来说,运行这个笔记本需要大约 90 分钟,我有兴趣探索简化它的可能性,无论是通过额外的计算资源,巧妙的代码操作,还是减少数据集中的维数。
结束语
希望您喜欢学习 DDoS 攻击,以及如何使用各种分类方法和 Python 库来解决这个问题!我非常喜欢这个项目,并且对结果非常满意。如果你有任何问题或意见,请留言!
米托:JupyterLab 扩展,无需代码即可轻松操作数据
操纵新冠肺炎疫苗数据的交互式软件包概述

Marc-Olivier Jodoin 在 Unsplash 上拍摄的照片
如果您在 Python 中处理过真实数据,您肯定会注意到数据操作和数据清理会占用您大部分时间,甚至高达 70%的时间。当我开始我的数据科学之旅时,我依赖 Pandas 上的这些类型的任务,Pandas 是一个强大而完整的库,可以处理您的数据集。
突然,我发现了米托,这是一个 JupiterLab 扩展,可以通过交互式电子表格自动化和加速数据工程,其中许多枯燥的操作都得到了简化,如导入数据集,应用过滤器,创建透视,删除重复项。
这个工具的美妙之处在于你不需要写任何一行代码。您可以像在 excel 中一样手动执行操作,但不同的是,这些操作一旦被应用,就会被翻译成下面的 Python 代码。在这篇文章中,我将通过一个真实的例子——来自约翰·霍普金斯大学的新冠肺炎疫苗数据——来概述米托。
先决条件
米托本质上是一个电子表格界面,只要你编辑和可视化数据集,它就会生成 Python。在安装米托之前,你需要检查你是否已经安装了 Python 3.6 或者更新的版本以及 JupiterLab 环境,因为米托是一个 JupiterLab 扩展。
一旦满足了这些先决条件,您就可以在浏览器上打开 JupiterLab 并创建一个新的终端,您可以在其中复制以下命令:
python -m pip install mitoinstaller
python -m mitoinstaller install
在你必须重启内核之后,你就可以开始使用这个神奇的 Python 包了!为了确保米托运行良好,创建一个新的笔记本并复制这两行代码:
import mitosheet
mitosheet.sheet()

作者插图
当您运行这些代码行时,您应该在注册后获得这样的输出。
米托概况
让我们从导入两个数据集开始,time _ series _ covid 19 _ vaccine _ global . CSV和world _ pop _ by _ country . CSV,这两个数据集可在 GitHub 存储库中获得,GitHub 存储库由约翰·霍普金斯公民影响中心为冠状病毒资源中心(CRC)创建[1]。一旦加载了两个数据集,相应的 Python 代码就会神奇地出现:

作者 GIF
正如您可以在上面的输出中看到的,有一个菜单栏允许许多功能。最重要的行动是:
import从文件系统加载数据集add/delete列undo到到删除最后编辑的数据集pivot哪一项对分组和汇总数据有用merge两张桌子dedup删除表格内的重复项graph生成绘图steps可视化对数据集执行的操作的时序
稍后将显示的其他选项是位于右上角的搜索栏,在这里您可以查找其他功能,如米托的文档。此外,每一列都有一个过滤器图标,它允许改变类型、编辑格式、排序,当然还有过滤。
在这篇文章中,我将重点介绍以下操作:
1.更改数据类型、排序和过滤
第一部分关注过滤器图标允许的三种操作。首先,我展示了一个最让我恶心的操作,那就是当我需要更改日期列的数据类型时。米托允许快速完成:

作者插图
还可以通过按降序排列日期来查看最新结果。

作者插图
我们还可以在 Country_Region 列上添加一些过滤器。很难同时分析很多国家,我更愿意关注一个较小的子集:

作者插图
在所有这些操作之后,出现以下代码行:
# Sorted Date in time_series_covid19_vaccine_global in descending order
time_series_covid19_vaccine_global = time_series_covid19_vaccine_global.sort_values(by='Date', ascending=False, na_position='last')# Changed Date from object to datetime
import pandas as pd
time_series_covid19_vaccine_global['Date'] = pd.to_datetime(time_series_covid19_vaccine_global['Date'], infer_datetime_format=True, errors='coerce')# Changed Date from datetime64[ns] to datetime
time_series_covid19_vaccine_global['Date'] = time_series_covid19_vaccine_global['Date']# Sorted Date in time_series_covid19_vaccine_global in descending order
time_series_covid19_vaccine_global = time_series_covid19_vaccine_global.sort_values(by='Date', ascending=False, na_position='last')# Sorted Date in time_series_covid19_vaccine_global in descending order
time_series_covid19_vaccine_global = time_series_covid19_vaccine_global.sort_values(by='Date', ascending=False, na_position='last')# Filtered Country_Region in time_series_covid19_vaccine_global
time_series_covid19_vaccine_global = time_series_covid19_vaccine_global[time_series_covid19_vaccine_global['Country_Region'].apply(lambda val: any(s in val for s in ['Italy', 'France', 'United Kingdom', 'Bulgaria', 'Romania', 'France', 'Germany']))]
用米托完成这些操作可以节省很多时间,你不觉得吗?注释沿着 Python 行出现也很好。
2.添加和删除列
另一个重要的操作是创建/删除列。删除字段非常简单:你只需要选择列,然后点击菜单栏上的“删除列”。

作者插图
我们还可以基于日期字段 Year 创建一个新列,这对于以后的深入了解很有用:

作者插图
另一个有用的列可以是月,它总是基于日期字段:

作者插图
这两个操作是如此的直观,立竿见影。下面我展示了相应的 Python 代码:
# Added column new-column-1cwn to time_series_covid19_vaccine_global
time_series_covid19_vaccine_global.insert(2, 'new-column-1cwn', 0)# Renamed new-column-1cwn to Year in time_series_covid19_vaccine_global
time_series_covid19_vaccine_global.rename(columns={'new-column-1cwn': 'Year'}, inplace=True)# Set new-column-1cwn in time_series_covid19_vaccine_global to Year(Date)
time_series_covid19_vaccine_global['Year'] = YEAR(time_series_covid19_vaccine_global['Date'])# Added column new-column-a24i to time_series_covid19_vaccine_global
time_series_covid19_vaccine_global.insert(3, 'new-column-a24i', 0)# Renamed new-column-a24i to Month in time_series_covid19_vaccine_global
time_series_covid19_vaccine_global.rename(columns={'new-column-a24i': 'Month'}, inplace=True)# Set new-column-a24i in time_series_covid19_vaccine_global to MONTH(Date)
time_series_covid19_vaccine_global['Month'] = MONTH(time_series_covid19_vaccine_global['Date'])
让我们进入下一部分来合并表!
3.合并表格
开始时,我还导入了 world_pop_by_country.csv。我们希望合并这两个表,以便更完整地了解新冠肺炎的全球情况。这个合并操作可以非常容易地完成:

作者插图
该选项还提供了不同的方式来合并两个数据集,例如左连接和右连接。我们选择选项 lookup,它包括疫苗数据的所有行,并且只包括人口文件中匹配的行。关于国家的列被认为是一个键,允许合并两个表。现在,我们有了一个名为 df3 的新数据帧。下面是米托生成的代码:
# Merged time_series_covid19_vaccine_global and world_pop_by_countrytemp_df = world_pop_by_country.drop_duplicates(subset='Country Name') # Remove duplicates so lookup merge only returns first matchdf3 = time_series_covid19_vaccine_global.merge(temp_df, left_on=['Country_Region'], right_on=['Country Name'], how='left', suffixes=['_time_series_covid19_vaccine_global', '_world_pop_by_country'])
4.数据透视表
pivot 选项相当于 pandas 函数 groupby,这真的很烦人,因为它要花很多时间,而且不是很直观。另一方面,米托通过其电子表格避免了这种耗时的操作。例如,我们想知道每年、每月和每个国家完全接种疫苗的人数。让我们单击“PIVOT”选项,将会打开一个包含以下选项的窗口:

作者插图
现在,我们可以看到创建这个数据透视表后的相应代码:
# Pivoted df3 into df4
unused_columns = df3.columns.difference(set(['Year', 'Month', 'Country_Region']).union(set([])).union(set({'People_fully_vaccinated'})))
tmp_df = df3.drop(unused_columns, axis=1)
pivot_table = tmp_df.pivot_table(
index=['Year', 'Month', 'Country_Region'],
values=['People_fully_vaccinated'],
aggfunc={'People_fully_vaccinated': ['max']}
)
pivot_table.columns = [flatten_column_header(col) for col in pivot_table.columns.values]
df3_pivot = pivot_table.reset_index()
要配置数据透视表,需要做一些选择。选项、行和列都指定了用于分组和聚合数据的变量,值部分允许决定如何聚合特定的列。为了让您更好地理解,让我们尝试使用列部分,而不是行:

作者插图
这一次,每个国家完全接种疫苗的人数显示在每一栏中。但是通常最好是将接种疫苗的人数放在不同的栏目中。因此,我将把 Country_region 列放在 Rows 部分。我们还添加了人口一栏,以便更全面地了解情况:

作者插图
生成的相应 Python 代码如下所示:
# Pivoted df3 into df4
unused_columns = df3.columns.difference(set(['Country_Region']).union(set([])).union(set({'People_fully_vaccinated', 'Population'})))
tmp_df = df3.drop(unused_columns, axis=1)
pivot_table = tmp_df.pivot_table(
index=['Country_Region'],
values=['Population', 'People_fully_vaccinated'],
aggfunc={'Population': ['max'], 'People_fully_vaccinated': ['max']}
)
pivot_table.columns = [flatten_column_header(col) for col in pivot_table.columns.values]
df3_pivot = pivot_table.reset_index()
瞧啊!现在,我们不费吹灰之力就获得了这些统计数据!
5.可视化绘图
最后,我们显示一些基本的图表来深入了解我们的数据,这些数据是上一次透视操作的结果。

作者插图
在这种情况下,米托不会为下面的图形生成任何代码。无论如何,它允许通过右下角的“复制图形代码”按钮来复制 Python 代码。
从该图中,我们可以注意到,该国人口需要对该国总人口中有多少人接种了疫苗有一个完整的了解。否则,我们可能会得出棘手的结论。值得注意的是,生活在法国、德国、意大利和英国的大多数人完成了前两剂疫苗接种,而在保加利亚和罗马尼亚,不到 50%的人口完全接种了疫苗。
最终想法:
我希望这个教程对你有用。选择新冠肺炎数据集是为了更好地了解米托如何有助于处理现实生活中的数据集,这些数据集在大多数情况下是不完整的,您需要添加新列来进行更详细的分析,如果可能的话,应用机器学习模型。感谢阅读。祝您愉快!
更多相关文章:
https://betterprogramming.pub/how-to-deal-with-time-series-using-pandas-and-plotly-express-45b6848432df https://betterprogramming.pub/3-pandas-functions-to-group-and-aggregate-data-9763a32583bb
参考文献:
[1]https://github.com/CSSEGISandData/COVID-19
免责声明:该数据集由约翰·霍普金斯大学代表其工程系统科学中心在知识共享署名 4.0 国际协议(CC BY 4.0)下授权。版权所有约翰·霍普金斯大学 2020 年
你喜欢我的文章吗? 成为会员 每天无限获取数据科学新帖!这是一种间接的支持我的方式,不会给你带来任何额外的费用。如果您已经是会员, 订阅 每当我发布新的数据科学和 python 指南时,您都会收到电子邮件!
米托:最好的数据可视化 Python 库之一
大蟒
让我们探索一下用于数据分析的最酷的 Python 库之一

来自 Unsplash 的蔡斯·沙佩尔的图片
几个月前,我写了一篇关于米托的博客,这是一个带有图形用户界面(GUI)的便捷 Python 库,让数据分析变得像使用 Microsoft Excel 一样简单。对于那些正在学习 Python 或者每天都在使用它的人来说,这是一个非常有用的工具。但是,在我的第一篇博客中,我主要关注的是如何完成数据分析、数据清洗、数据修改任务等。现在,是时候回顾一下米托大放异彩的任务之一了:数据可视化。
什么是米托?
米托是一个带有 GUI 的 Python 库,它允许我们通过几次点击来进行数据清理、数据操作、分析和可视化。
你可能会问自己:有用户界面的 Python?我们为什么要考虑它?嗯,对于学习 Python 的人来说是一个极好的工具。这使得完成需要多行代码的任务变得更容易,即使你使用的是 GUI,米托也向我们展示了完成任务所生成的代码,这可以提高学习速度。对于专业人士来说,这也是一个很好的工具,因为你可以在几秒钟内完成需要几分钟的任务。
好得不真实(或免费)?
每当有些事情听起来好得不像是真的,我就问自己是否好得不像是免费的。好消息是米托是开源的,可以免费使用!他们有付费计划,但我将在这个博客中使用开源版本。
既然他们有付费计划,我需要说我不写赞助内容,对于这个博客来说也没什么不同。我没有得到任何报酬。我的真实想法是基于我对这个图书馆的体验。
入门指南
安装很容易,如果你在安装米托时遇到任何问题,他们的 GitHub 页面会提供非常丰富的信息。为了简化这个过程,您只需在您的终端上复制并粘贴以下代码,就可以开始了:
在 Mac 上:
python3 -m venv venv;
source venv/bin/activate;
pip install -r requirements.txt;
python -m mitoinstaller install
在 Windows 上:
python3 -m venv venv
venv\Scripts\activate.bat
pip install -r requirements.txt
python -m mitoinstaller install
现在,你可以从你的终端打开 Jupyter 实验室。要开始使用米托,您只需要导入它,并键入以下命令开始一个新的工作表:
import mitosheet
mitosheet.sheet()

作者 GIF
酷毙了。让我们暂停一下,探索一下米托的用户界面。如果你熟悉微软的 Excel,你会看到许多熟悉的米托。这个库很酷的一点是,您一眼就能看出列的数据类型。我们还可以看到许多功能,如导入文件、添加列、删除列、更改数据类型、更改数字类型(整数、浮点、添加或删除小数)、旋转数据、创建图表。我在我的第一篇关于米托的博客中提到了其中的一些特色。今天,我们来重点构建一些图表。

作者 GIF
现在我们已经熟悉了米托的 UI,让我们使用超市销售数据集,它包含多种数据类型,这将允许我们创建一些有趣的可视化。你可以在这里找到数据集。将文件导入米托再简单不过了。只需点击导入文件,我们就可以开始了。
值得一提的是,您可以在列名下看到特性的数据类型,这已经使我们不必为一些数据探索而键入代码。

作者 GIF
首先,我们为什么不把总销售额按日期可视化呢?为此,单击图形,会出现一个侧菜单。选择图表类型,然后选择 x 轴(我将在这个可视化中使用日期),y 轴(总计,即该客户的总销售额),然后完成。我们刚刚在几秒钟内创建了一个图表。

需要注意的一点是,为图表创建了一个新的选项卡。这有助于我们组织笔记本的内容。
现在,我们可以对图表进行一些更改。让我们将图表标题更改为“按日期销售”,将 y 轴更改为“总销售额”。

作者 GIF
我们还可以将轴转换为线性、对数、类别等。我们今天不会进行这些转换,但是您可以在自己的终端上尝试一下。还有其他付费功能来编辑图表,但由于我今天只使用开源软件,所以让我们跳过这些。另外,你可以自己修改代码,而不必付费。
我创建的图表只能告诉我们一点。让我们探索一些更多的选项,使它更有见地。首先,我们可以很容易地改变图表类型——如散点图、折线图、直方图、密度热图、小提琴图、带状图等。我们还可以通过单击“按列显示颜色”来将颜色更改为不同的类别。轻松微风!

作者 GIF
如果你注意我们到目前为止的图表,你会发现日期需要更有条理和有序。这是因为日期数据类型是字符串。我们需要将其更改为 DateTime 类型,这非常简单。
转到 dataframe 选项卡,找到您想要更改数据类型的列,单击数据类型图标(在本例中为 str ,并选择您想要更改的数据类型。看下面的图表,你会看到日期正在上升。

作者 GIF
需要注意的一点是,米托生成了用于更改数据类型的代码。另一件很酷的事情是,任何人都可以使用米托生成的代码运行这台笔记本,即使他们没有安装米托。

作者图片
回到我们的图表,您会注意到我们现在有两个选项卡。让我们重命名“图表”选项卡,使事情更有条理。只要点击标签,你就可以改变它的名字了。

作者 GIF
我们目前掌握的图表很有趣,但它并没有告诉我们太多。让我们通过查看哪些城市的销售额最高来更深入地了解一下。
为此,我们将按列将颜色更改为城市,然后单击直方图以更改图表类型。完成后,我们可以导出图表。让我们复制代码,看看能否运行它。要复制代码,点击 Export,并选择选项复制显示图形的代码。现在我们可以将代码粘贴到 Jupyter 笔记本的任何单元格中。

作者 GIF
厉害!我们创建了一个漂亮的图表,生成了代码,并在笔记本上运行。让我们再来看看米托生成的图形和代码。
import plotly.express as px
# Construct the graph and style it. Further customize your graph by editing this code.
# See Plotly Documentation for help: https://plotly.com/python/plotly-express/
fig = px.histogram(supermarket_sales, x='Date', y='Total', color='City', histfunc='count')
fig.update_layout(
title='Sales by Date',
xaxis = dict(
showgrid=True,
rangeslider = dict(
visible=True,
thickness=0.05
)
),
yaxis = dict(
title='Total Sales',
showgrid=True
),
legend = dict(
orientation='v'
),
barmode='group',
paper_bgcolor='#FFFFFF'
)
fig.show(renderer="iframe")

作者图片
这是一个用 Plotly 创建的好看的图形,只需要几秒钟就可以完成。如果我们看一下代码,它需要多行代码来实现相同的结果,例如,图表可以添加到演示文稿中。
好了,让我们再制作一个图表,按性别检查哪些产品线卖得最多。点击 Graph,然后选择 histogram 作为数据类型,性别作为 x 轴,产品线作为 y 轴,产品线作为 Color by Column,这就完成了。让我们复制并粘贴生成的代码,看看能否将结果打印在笔记本上。

又一次成功了,只用了几秒钟。很酷,对吧?
最后的想法
今天我们讨论了如何使用米托进行数据可视化。米托可以完成更多的任务,他们的团队正在不断增加功能,使其更加有用。最大的问题是:你应该使用它吗?即使你已经使用 Python 很多年了,你也应该尝试一下。它可以在几秒钟内完成繁琐耗时的任务,这样你就可以把时间用在更具挑战性的任务上。
我相信每个人都应该接受教育,米托帮助那些开始新的职业生涯或者正在考虑学习 Python 但不知道从哪里开始的人。最重要的是,它可以帮助使用 Python 并且需要每天编写重复代码的人。因此,他们不需要输入几十行代码,只需点击 3 到 4 次就可以获得相同的结果。
你可以尝试一下,看看米托在哪里可以为你申请。每个人都可以找到这个不可思议的 Python 库的用例。如果你决定测试它,让我知道它怎么样。编码快乐!
您可能还喜欢:
</4-amazing-python-libraries-that-you-should-try-right-now-872df6f1c93>
米托—第 1 部分:介绍一个 Python 包,它将改进和加快您的分析
米托是一个 Python 包,它允许用户以更快的速度用 Python 完成他们的分析!用户将创建一个 Mitosheet,然后在处理该表后为他们生成 Python 代码。今天,我们将简单地看看什么是米托,以及如何用 Python 安装它,并给出一个简短的例子。这是一系列帖子中的第一篇,旨在理解如何使用有丝分裂表及其在数据集中的交互作用。

什么是米托?
米托是一个 Python 包,可以帮助数据科学家执行更快的分析。它可以在一个 Jupyter 笔记本中实现,用户将在其中创建一个米托电子表格。 关于米托最好的部分?用户只需操作 Mitosheet,python 代码就为他们创建好了!告别经常为项目中的各种任务搜索堆栈交换或代码文档!
如何安装米托?
我在 Jupyter 笔记本上使用了以下代码来安装米托。安装前,确保您阅读并理解了服务条款和隐私政策。首先,您必须确保您运行的是 Python 3.6 或更高版本。
import sys
sys.version
我用 Python 3.8.3 安装了米托。在验证了 Python 版本之后,我通过我的 Anaconda 命令提示符安装了这个包。您将希望确保您处于安装米托的正确虚拟环境中(在此了解如何安装虚拟环境)。在正确的环境中工作后,使用以下代码安装米托。
python -m pip install mitoinstaller
下载完软件包后,运行安装程序。
python -m mitoinstaller install
代码运行完成后,打开你的 Jupyter 笔记本,导入米托!
import mitosheet
例子
正如我前面提到的,当你操作电子表格时,米托包将为你提供 Python 代码。这个示例将简单地向您展示代码是如何生成的,稍后将提供对米托功能的更深入的审查。使用的数据集是来自kaggle.com的动画工作室数据集列表。首先,我们要创建一个有丝分裂表。
mitosheet.sheet()
运行此代码将在 Jupyter 笔记本中创建以下弹出窗口(在接受条款和条件之后)。

图片:Jupyter 中的 Mitosheet(图片来自作者)
接下来,单击“import”按钮将数据集导入笔记本。

图片:导入按钮(图片来自作者)
既然数据集已经导入,我们就可以开始操作电子表格来生成代码了。请注意代码已经为我们启动了!

图像:带数据的 Mitosheet(图像表单作者)
现在,为了补充这个例子,假设我们想要按国家过滤数据。首先,单击电子表格国家列中的 delta 符号。

图像:选择列(来自作者的图像)
单击国家栏中的 delta 符号,我们会看到各种选项,包括过滤/排序、值、和汇总统计。在这个例子中,我们将使用过滤/排序。

图片:过滤/排序弹出框(图片来自作者)
通过“丹麦”过滤,Mitosheet 给出了丹麦所有的动画工作室。此外,还为整个示例生成了以下代码!

图片:米托生成的代码(图片来自作者)
就这样,我们可以快速地为我们的分析创建一个基线!
结论
今天只是对我最近接触的一个新的 Python 包米托的一个快速总结。使用米托的主要好处是它会为你提供 Python 代码并且用户所要做的就是修改数据表!这将为数据科学家带来更快的分析速度,并减少用户查找他们希望对数据集采取的特定操作的代码所需的时间。如果你曾经陷入如何编写特定实例的困境,米托可以成为帮助你完成下一个项目的向导和救星!这是我计划在米托上发表的一系列文章的第 1 部分,以展示该库提供的全部功能。在我的下一篇文章中,请继续关注更多的动手分析。
如果你喜欢今天的阅读,请关注我,并告诉我你是否还有其他想让我探讨的话题!另外,在LinkedIn上加我,或者随时联系!感谢阅读!
米托:用熊猫做电子表格
原文:https://towardsdatascience.com/mito-using-pandas-as-a-spreadsheet-within-jupyter-lab-6a6bd04310a8
查看 mitosheet python 包

电子表格是一个很好的解决方案,可以检查新数据,初步掌握其内容,并与它们快速、直观地进行交互。然而,当这些任务需要频繁执行时——就像数据科学项目中的典型工作流一样——基于电子表格的工具(如 Excel 或 Google Sheets )的优势被手动多次重复相同操作的负担所限制。
编程语言为后一部分的自动化提供了一个方便的选择,尽管根据语言和用户的熟练程度,在第一次传递数据时可能需要一些麻烦。
在这篇文章中,我们将探索 米托 ,一个基于 python 的熊猫数据帧的类似电子表格的界面。特别是,我将指导你通过它的安装和主要功能,评估完整性,有效性和易用性的米托的功能从 1 到 5 的规模。
免责声明 :本帖赞助。然而,它提出了一个关键的和诚实的评价图书馆及其表现。
什么是米托?
米托是一个开源的 python 库,它将电子表格的直观和可视化方面与编程的好处相结合,以处理更大的数据集,提供及时的阐述并轻松创建可重复的流程。
简而言之,米托是一个 Jupyter Lab 接口,它允许用户处理数据,就像在电子表格中一样,同时在下面的单元格中自动生成相应的 python 代码。
给谁的?
对于老派的电子表格用户来说——例如 Excel 从业者——米托是向面向编程的范式过渡的完美工具。
然而,我也向像我一样的 python 程序员推荐米托,他们厌倦了复制粘贴相同的样本代码来进行探索性数据分析。我相信这是一个用于探索性数据分析、的便利工具,也是一个快速设置大部分代码,然后在各处应用一些定制调整的良好起点。
装置
安装非常简单,只需要文档中详细描述的几个步骤,例如对于 conda :
# this article uses mitosheet version '0.1.392'
conda create -n mitoenv python=3.8
conda activate mitoenv
python -m pip install mitoinstaller
python -m mitoinstaller install
我在我的 ubuntu 20.04 平台上试了一下,几乎一眨眼就成功了。实际上,我无法立即完成python -m pip install mitoinstaller,因为有些依赖关系没有解决:
(mitoenv) luca@ASUS-ROG:~/workspace/blog_mito$ python -m pip install mitoinstaller
Collecting mitoinstaller
Downloading mitoinstaller-0.0.122.tar.gz (14 kB)
Preparing metadata (setup.py) ... done
Collecting analytics-python
Downloading analytics_python-1.4.0-py2.py3-none-any.whl (15 kB)
Requirement already satisfied: colorama in /home/luca/.local/lib/python3.8/site-packages (from mitoinstaller) (0.4.4)
Collecting termcolor
Using cached termcolor-1.1.0.tar.gz (3.9 kB)
Preparing metadata (setup.py) ... done
Collecting monotonic>=1.5
Downloading monotonic-1.6-py2.py3-none-any.whl (8.2 kB)
Collecting backoff==1.10.0
Downloading backoff-1.10.0-py2.py3-none-any.whl (31 kB)
Requirement already satisfied: python-dateutil>2.1 in /home/luca/.local/lib/python3.8/site-packages (from analytics-python->mitoinstaller) (2.8.1)
Collecting six>=1.5
Using cached six-1.16.0-py2.py3-none-any.whl (11 kB)
Collecting requests<3.0,>=2.7
Downloading requests-2.27.1-py2.py3-none-any.whl (63 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 63.1/63.1 KB 605.1 kB/s eta 0:00:00
Collecting certifi>=2017.4.17
Downloading certifi-2021.10.8-py2.py3-none-any.whl (149 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 149.2/149.2 KB 1.1 MB/s eta 0:00:00
Collecting urllib3<1.27,>=1.21.1
Downloading urllib3-1.26.8-py2.py3-none-any.whl (138 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 138.7/138.7 KB 1.4 MB/s eta 0:00:00
Collecting charset-normalizer~=2.0.0
Downloading charset_normalizer-2.0.12-py3-none-any.whl (39 kB)
Collecting idna<4,>=2.5
Downloading idna-3.3-py3-none-any.whl (61 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 61.2/61.2 KB 1.6 MB/s eta 0:00:00
Building wheels for collected packages: mitoinstaller, termcolor
Building wheel for mitoinstaller (setup.py) ... done
Created wheel for mitoinstaller: filename=mitoinstaller-0.0.122-py3-none-any.whl size=19809 sha256=68b55bfd4dbc4f916f6cd1a19500aefdfab6f25f86737c2ae7c258264eff410f
Stored in directory: /home/luca/.cache/pip/wheels/46/c6/8a/8b77ea33ddac40eff0b3e1847f84789d316e557e48bb0bc110
Building wheel for termcolor (setup.py) ... done
Created wheel for termcolor: filename=termcolor-1.1.0-py3-none-any.whl size=4848 sha256=84532fa74eca34fbe56efd5665ff9c8ebce197dc2a32254a19f145f97d6da515
Stored in directory: /home/luca/.cache/pip/wheels/a0/16/9c/5473df82468f958445479c59e784896fa24f4a5fc024b0f501
Successfully built mitoinstaller termcolor
Installing collected packages: termcolor, monotonic, certifi, urllib3, six, idna, charset-normalizer, backoff, requests, analytics-python, mitoinstaller
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
requests-oauthlib 1.3.0 requires oauthlib>=3.0.0, which is not installed.
pandas 1.2.2 requires pytz>=2017.3, which is not installed.
oauth2client 4.1.3 requires httplib2>=0.9.1, which is not installed.
moto 1.3.16.dev122 requires MarkupSafe<2.0, which is not installed.
moto 1.3.16.dev122 requires more-itertools, which is not installed.
moto 1.3.16.dev122 requires pytz, which is not installed.
moto 1.3.16.dev122 requires zipp, which is not installed.
google-api-core 1.26.3 requires packaging>=14.3, which is not installed.
google-api-core 1.26.3 requires pytz, which is not installed.
Successfully installed analytics-python-1.4.0 backoff-1.10.0 certifi-2021.10.8 charset-normalizer-2.0.12 idna-3.3 mitoinstaller-0.0.122 monotonic-1.6 requests-2.27.1 six-1.16.0 termcolor-1.1.0 urllib3-1.26.8
这导致我在运行python -m mitoinstaller install时出现以下错误:
(mitoenv) luca@ASUS-ROG:~/workspace/blog_mito$ python -m mitoinstaller install
Starting install...
Create mito user
Upgrade mitoinstaller
Check dependencies
Remove mitosheet3 if present
Install mitosheet
Create import mito startup file
Creating a Mitosheet starter notebook
Start JupyterLab
Traceback (most recent call last):
File "/home/luca/anaconda3/envs/mitoenv/bin/jupyter-lab", line 5, in <module>
from jupyterlab.labapp import main
File "/home/luca/anaconda3/envs/mitoenv/lib/python3.8/site-packages/jupyterlab/labapp.py", line 13, in <module>
from jupyter_server.serverapp import flags
File "/home/luca/anaconda3/envs/mitoenv/lib/python3.8/site-packages/jupyter_server/serverapp.py", line 38, in <module>
from jinja2 import Environment, FileSystemLoader
File "/home/luca/.local/lib/python3.8/site-packages/jinja2/__init__.py", line 12, in <module>
from .environment import Environment
File "/home/luca/.local/lib/python3.8/site-packages/jinja2/environment.py", line 25, in <module>
from .defaults import BLOCK_END_STRING
File "/home/luca/.local/lib/python3.8/site-packages/jinja2/defaults.py", line 3, in <module>
from .filters import FILTERS as DEFAULT_FILTERS # noqa: F401
File "/home/luca/.local/lib/python3.8/site-packages/jinja2/filters.py", line 13, in <module>
from markupsafe import soft_unicode
ImportError: cannot import name 'soft_unicode' from 'markupsafe' (/home/luca/anaconda3/envs/mitoenv/lib/python3.8/site-packages/markupsafe/__init__.py)
然而,这可能依赖于我的本地配置,并且很容易通过单独安装缺失的依赖项来修复:
pip install mitoinstaller
pip install requests-oauthlib
pip install oauth2client
pip install pandas
pip install moto
pip install google-api-core
比率:4 / 5 ⭐⭐⭐⭐
功能
米托以非常直观和高效的方式提供了大多数(几乎所有)电子表格功能。
输入数据
米托支持从 csv 和 xlsx 文件导入数据。导入本身就像导入 mitosheet 包并在用户第一次被要求注册并选择想要的计划时运行mitosheet.sheet(). 一样简单。之后,您可以简单地遵循一个简洁的图形界面来选择要导入的数据文件。

数据导入。图片作者
或者,米托也支持每个工作表的熊猫数据帧mitosheet.sheet(df1, df2)。
一个小的限制是您不能在导入时为 csv 文件定制数据类型——但是您可以在以后通过数据格式化来修复它。
比率:4 / 5 ⭐⭐⭐⭐
检查值和分布
每个数据科学项目的第一步是探索数据,检查值并探索变量的分布。有了米托,这真是又快又直观。

数据值和分布。图片作者
对于不同类型的数据,我的体验非常流畅,涵盖了我能想到的所有用例。这是一个很好的工具!
比率:5 / 5 ⭐⭐⭐⭐⭐
过滤和分类
过滤和排序也是处理数据时非常常见的操作。米托允许基于条件和直接排除不需要的值进行过滤。

过滤。图片作者
排序也是有效的,尽管基于多列的排序本身不被支持。一种可能的解决方法是连接这样的变量,并按结果列进行排序。
比率:4 / 5 ⭐⭐⭐⭐
格式化
良好的数据格式是可视化探索的关键。米托提供了大量的选项来安排他们的内容,以满足任何需求。

数据格式化。图片作者
比率:5 / 5 ⭐⭐⭐⭐⭐
数据类型
在预处理过程中经常需要处理数据类型,以便为下面的分析管道准备适当的数据。米托在这方面也做得很好,平衡了数据导入过程中最初缺乏定制的问题。

数据类型。图片作者
除了像int、float、str和bool这样的标准数据类型之外,我还针对数据科学家最大的噩梦之一:日期对米托进行了测试。从字符串到datetime的转换是无缝的,您可以使用 Excel 公式进行更多的定制。做得好!
比率:5 / 5 ⭐⭐⭐⭐⭐
数据操作
米托支持几种利用最常用的 Excel 公式的数据操作。添加和删除列,以及编辑单个单元格或整个变量只需点击几下鼠标。

数据操作。图片作者
我真的很欣赏你输入命令时有建议的漂亮界面,还有右上角的Search Functionality框。
但是,我没有成功复制 Excel 的一个杀手级特性:拖动公式。这很遗憾,我认为这应该是未来发展的首要任务之一。
此外,添加的列是不可按单元格编辑的,这意味着您可以批量编辑新列。同样,这错过了 Excel 的便利性。
比率:3 / 5 ⭐⭐⭐
合并
米托支持合并工作表的多种选项,包括左/右/内/外连接,以及查找,所有这些都在下拉菜单中有很好的解释。

合并。图片作者
另一个很棒的特性是能够快速地从每个表中选择关键变量和要保留的列。我真的很喜欢!
不利的一面是,多键合并不被本地支持。但是,文档涵盖了这种使用情形:
如果希望使用多列作为合并键,请尝试创建一个新列,并使用 CONCAT 函数将所有列合并为一列。然后,选择新列作为合并键!
此外,我没有设法垂直合并两个工作表——类似于熊猫.concat()——这需要一个外部通道。
比率:4 / 5 ⭐⭐⭐⭐
重复数据删除
这是我需要的时候总要谷歌一下的东西之一。用米托删除重复行只需几次点击。它支持多列组合,并为要保留的值提供不同的选项: first,last,none 。

重复数据删除。图片作者
比率:5 / 5 ⭐⭐⭐⭐⭐
图表
这可能是我在这样的工具中寻找的顶级特性之一。米托在这个功能上做得很好,支持多种绘图类型,包括条形图、线形趋势图、箱线图、小提琴图、热图等等。

折线图。图片作者

分布图。图片作者
通过界面的流程非常流畅,结果也很不错,使用 plotly 制作交互式图表还有额外的好处。不幸的是,改变它们的外观是不可能的,但对于探索性分析来说,这不是一个大问题。此外,生成的代码可以作为快速适应所需外观的基础。
但是米托有令人惊叹的因素吗?
单变量图很好,很实用,绝对值得称赞!
不过,这个结果对多变量图的效果稍差。米托处理一些功能,但是不能通过组来表示相同的信息。

多组箱线图。图片作者
在上面的例子中,自动生成的代码只是连接了 X 轴上的值,在我看来这有些不直观:
我认为更好的处理方式是添加第二个 x 变量作为颜色参数:
总的来说,这是一个很好的快速探索解决方案,适用于大多数常见用例。对于更清晰的需求和风格,还有一些改进的空间。
比率:4 / 5 ⭐⭐⭐⭐
数据透视表
数据透视表是一种通过汇总统计数据来总结调查结果的方式。米托允许以类似 Excel 的方式非常顺利地做到这一点。

数据透视表。图片作者
界面非常直观,支持你能想到的所有聚合函数。然后可以使用格式化实用程序来更好地可视化结果或优化输出,以便以后导出和共享。
对于未来的开发来说,增加一个如何处理缺失值的选项将是一个不错的附加功能。
比率:5 / 5 ⭐⭐⭐⭐⭐

摄影:谢尔曼杨在 Unsplash
最后的想法
总而言之,米托是一个成熟的工具,做得很好,使用户能够直观地检查数据,并与他们进行直观有效的互动。它几乎涵盖了探索性数据分析的所有最常见用例,如检查值和分布、编辑和格式化数据、合并工作表、创建数据透视表以及各种绘图。所有的操作都被跟踪并自动转换成 python 代码,在底层的 pandas 数据帧上执行操作。
我很欣赏生成代码的顺序,以及它包含注释来引导用户浏览的事实。此外,它被很好地优化以利用快速 pandas 实现——例如,它使用.at()而不是.loc()来根据 pandas 文档有效地设置单元格的值。
此外,还有一个额外的奖励,那就是简单安装、现场和综合 文档——我在探索图书馆时发现这些很有帮助——以及一个 不和谐频道 的可用性——我甚至在周末就收到了回复!
就局限性而言,我遇到了两个主要的缺失特性:Excel 风格的公式拖动和编辑创建的列的可能性。
作为进一步的扩展或丰富,我会喜欢在多个键上合并表的可能性,而不是为此目的创建一个连接的键。
此外,在过滤后有一个图形或数据透视表的自动刷新会很好。
最后,多元图还是不完整。我认为它们的增强可能是最终确定米托内部端到端数据探索工具的缺失部分。
资源
为了尝试一下,这里有一些更多资源链接:
网址:https://www.trymito.io/
YouTube:https://www . YouTube . com/channel/ucn 9 o _ 0 m1 fwcjigfipnkr 0 OA/featured
Github:https://github.com/mito-ds/monorepo
这次到此为止!如果你喜欢这篇文章并且你对更多数据分析内容感兴趣,那么查看我的 故事 和让我知道你的想法!
混合整数线性规划:简介
原文:https://towardsdatascience.com/mixed-integer-linear-programming-1-bc0ef201ee87
如何解决具有离散变量的复杂约束优化问题

为复杂问题设计和实现算法是困难的。有趣,但很难。如果我告诉你,你可以解决某些优化问题只使用他们的数学规格?和我一起踏上混合整数线性规划的奇妙世界之旅,它在护士排班、换肾程序、生产调度、机器人细胞能量优化、自动数独求解以及许多许多其他程序中都有应用!这些问题的一个共同特性是它们有离散的解空间。通常,解空间也受到约束的限制,因此只有值的子集表示有效的解。因此,广为人知的人工智能优化算法(如梯度下降)不适合这类问题。

混合整数线性规划的解空间及其线性松弛和最优解。这些线对应于约束,这些约束对解空间进行编码。填充的蓝点代表可行的解决方案,而填充的绿色点是最优的解决方案。
在这一系列的文章中,我们将涵盖 Python 中离散优化问题的实际建模和幕后的理论机器。本系列面向没有深厚计算机科学背景的程序员和机器学习人员,对于他们来说,混合整数线性编程可能是他们熟悉的数据驱动方法的替代工具。为了能够理解内容,火箭科学的博士学位不是必要的,尽管关于数学(矩阵代数)和 Python 编程的基础知识是假定的。
在这第一篇介绍性的文章中,我们将遇到一个经典的优化问题,我们使用 Python 中的混合整数线性编程来解决这个问题。
最优化问题
想象你是一家公司的经理。公司积累了大量预算,你想把它投资到各种资产中,以最大化未来利润。有不同种类的资产,如机器或车辆,如果资产被购买,每种资产都有一个固定成本和一个估计未来利润。你可能会问的问题是:你今天应该购买哪些资产来最大化预计未来利润,这样你就不会超出预算(假设你不能购买资产的一部分)?
作为一个聪明的程序员,你想出了一个简单明了的贪婪的算法:根据资产的预计未来单位成本利润(即每项资产的利润除以其固定成本)对资产进行递减排序,并按照这个排序列表购买资产,直到预算用完。容易,虽然不能保证这样选择的资产就是未来总利润最大化的资产。你损失了多少?自然,你可能想知道你可以购买的资产的最优选择是什么,这样就不会有其他集团产生更高的利润。
所以你决定实现一个更复杂的启发式算法,例如,基于遗传算法。经过几天的代码编写和调试,你想出了一个具有不错性能的算法,它所获得的解将比那些由贪婪算法构造的解有更高的利润。但是如果问题需求发生变化,比如有些资产不能一起买,会怎么样呢?你将不得不回去编码和调试。如此循环往复…

开发努力与解决方案质量(例如,利润):从很差的解决方案到一般的解决方案需要一点努力,但是改进一个高质量的解决方案需要更多的努力。
混合整数线性规划解决了这个问题。你不用编写算法,而是用一种兼容的数学语言来描述你的问题。一旦问题被数学形式化,你就把它传递给现成的混合整数线性规划求解器库来获得解。因为这些解算器是由非常聪明的人编写的,他们有很深的数学背景,所以很有可能你会得到一个比手工算法更好的解。如果问题需求发生变化,您只需修改问题描述,然后重新运行同一个求解器。听起来好得难以置信,对吧?所以让我们调查一下。
问题的数学形式化
混合整数线性规划(简称 MILP)被称为线性是有原因的。那就是:一个问题的数学描述就是一堆线性不等式和线性表达式。例如,线性不等式

有了变量 x₁、x₂ 和固定参数 a₁、a₂、b₁ 就是出现在 MILP 公式中的野兽之一。另一方面,

在 MILP 中不是有效的公式,因为有二次项 x₁x₂ 。
所以我们知道 MILP 由线性不等式组成,这些不等式以某种方式编码了手头的问题:我们称这些不等式为约束。给定固定参数,MILP 求解器将尝试将值插入变量中,以满足约束条件。满足约束的变量值统称为可行解。然而,MILP 远远不止找到任何满足约束的解决方案。我们可以寻找一个优化目标的可行解,目标是变量的线性函数:优化是根据目标值找到一个最佳可行解。
MILP 中的变量可以是有理数、整数或二进制数(实际上是整数,但被限制为 0 或 1);这就是为什么名字里有混的原因。如果一个公式中的所有变量都是合理的,我们得到线性规划,它有很好的计算特性(但在另一篇文章中会有更多)。
为了更好地理解我们的预算问题,让我们将它形式化。首先,考虑固定参数。假设有 𝑛 ≥1 种不同的资产可供选择。每项资产 i∈{0,1,2,…,𝑛-1} 都有固定成本 𝑐ᵢ 和预计未来利润 pᵢ 。最后,我们还得到了可用于资产的预算 𝐵 。
现在,考虑解决方案。资产 𝑖 可以购买也可以不购买,决定资产 𝑖 是否购买应该是 MILP 求解者的工作。因此,我们将为每项资产 𝑖 设置一个二元变量 𝑥ᵢ ,如果资产 𝑖 被购买,则该变量为 1,如果没有被购买,则该变量为 0。模型中变量值的解释是由我们决定的,这种解释决定了问题形式化的其余部分应该是什么样子。
我们的问题中的约束是什么?我们只有一个,即解决方案中购买的资产的总成本不能超过预算。我们可以用不等式来写这个

这个约束的解释如下:如果资产 𝑖 没有被购买,那么它的成本 𝑐ᵢ 将不被包括在总成本中(不等式的左手边),因为 𝑐ᵢ𝑥ᵢ=𝑐ᵢ0=0 。另一方面,如果资产 𝑖 被购买,那么它的成本包括在总成本中。
最后,我们指定我们的目标,即从购买的资产中获得的估计总利润的最大化

这就是你的第一个 MILP 公式!
用 Python 解决 MILP 问题
一旦有了 MILP 公式,就可以将其传递给现有的 MILP 求解器来获得解。现在,有一些商业甚至开源的 MILP 解算器,我将在以后的文章中介绍它们的不同之处。现在我将使用 Python 包 Python-MIP ,它捆绑了 CBC 开源解算器。您可以使用下面的命令从 PyPi 安装它(有针对 Windows、MacOS 和 Linux 的预构建包)
pip3 install mip --user
Mac M1 用户注意:你可能需要做一些 额外的步骤 。
之后,开除你最喜欢的代码编辑器,开始写吧!
MILP solution
Solver status: OptimizationStatus.OPTIMAL
Bought assets: [3, 4]
Total cost of the bought assets: 10
Estimated future profit: 180
在这里,我们创建了一个有 5 个资产可供选择的问题实例。在解决了 MILP 模型之后,我们得到了一个最优解,估计未来利润为 180 英镑。相比之下,贪婪算法会给我们 160 英镑的小利润。
Greedy algorithm solution
Bought assets: [0]
Total cost of the bought assets: 8
Estimated future profit: 160
看起来 180–160 = 20 是一个很小的利润差额,但是考虑到利润是以百万美元计的,这就开始变得有趣了,不是吗?还要注意,在 MILP 解决方案中没有选择利润成本比最高的资产 𝑖 =0(有更好的资产组合可以带来更高的利润)。
顺便问一下,我们如何合并两项资产 𝑖 、 𝑗 不能一起购买的附加要求?通过包含附加约束
m += x[i]+x[j] <= 1
在模型中。只有当两个 𝑖 、 𝑗 一起买的时候,你才能检查出这个约束被违反了,因为这时左手边是 2,明显大于 1。就这样,只有一行代码,你就能适应这个问题的额外需求。唷。
总结
在这第一篇介绍性的文章中,我们简要讨论了什么是混合整数线性规划(MILP)以及它为什么有用。它允许我们不用写算法就能解决最优化问题。相反,我们将问题描述写成数学公式,然后由众多可用的 MILP 求解器中的一个来求解。与机器学习方法相比,求解 MILP 不是数据驱动的,因为它利用了组合优化的理论基础。我们探索了一个购买资产的简单优化问题,并用 Python 建模(顺便说一句:这个问题其实叫背包问题!).
在下一篇文章中,我想更正式地定义什么是 MILP,它的解空间看起来是什么样的,计算复杂性是什么(MILP 是一个强大的工具,但它有其局限性)。
所有图片,除非特别注明,均为作者所有。
混合精确训练—内存更少,速度更快
原文:https://towardsdatascience.com/mixed-precision-training-less-ram-more-speed-e0d5ebd0c1d1
最佳化
用两行代码加速你的模型

图片作者作者
对于大型复杂模型,尽可能减少模型训练时间并有效利用可用硬件至关重要。即使每个批次或时期的小增益也是非常重要的。
混合精度训练既可以显著降低 GPU RAM 的使用,也可以加快训练过程本身,而不会损失结果的精度。
本文将展示(通过代码示例)实际可以获得的收益,同时还将介绍在您自己的模型中使用混合精度训练的要求。
介绍
本文的前半部分旨在概述什么是混合精度,以及何时、为什么以及如何使用它。
第二部分是对一组虚拟图像进行“正常”和混合精度训练的比较结果。这些图像通过 TensorFlow 中的多层 Conv2D 神经网络进行训练,RAM 使用和执行速度都受到全程监控。
与比较相关的所有代码都可以在 colab 笔记本上找到
混合精度到底是什么?
在我们深入什么是混合精度之前,在这个特定的上下文中,当我们说“精度”时,概述一下我们指的是什么可能是一个好主意。
在这种情况下,精度基本上是指浮点数如何存储,即它在内存中占用多少空间。内存占用越小,数字越不准确。基本上有三种选择:
- 半精度— 16 位(float16) —用于表示数字的低级存储,低精度
- 单精度— 32 位(float32) —用于表示数字的中等存储水平,中等精度
- 双精度— 64 位(float64) —用于表示数字的高级存储,高精度
通常使用机器学习/深度学习和神经网络,您将处理单精度 32 位浮点数。

然而,在几乎所有情况下,计算都可以使用 16 位浮点数而不是 32 位浮点数来运行,不会降低模型的精度。
混合精度
理想且最简单的解决方案是混合使用 16 位和 32 位浮点数。使用较低精度的 16 位浮点数可以尽可能快地运行计算,然后输入和输出可以存储为 32 位浮点变量,以确保保持较高的精度,并且输出没有兼容性问题。
这种组合被称为“混合精度”。
为什么我应该使用混合精度?
有两个主要原因:
- GPU RAM 的使用会有很大的提升。差别可能是 GPU RAM 利用率降低 50%
- 运行模型所需的时间可能会显著加快
在 TensorFlow 中使用混合精度可以:
在现代 GPU 上性能提高 3 倍以上,在 TPU 上性能提高 60%
单单 RAM 使用量的减少就是一件大事。这将允许利用更大的批量,或者为在相同的硬件上实现更大和更密集的模型打开大门。
在本文后面的比较中,我们当然会看到这两个因素的实际结果。
使用混合精度有什么要求?
要使混合精度训练成为一项优势,您需要具备以下条件之一:
- 计算兼容性为 7.0 或更高的 Nvidia GPU(你可以在我的上一篇文章这里中获得更多关于“计算兼容性”以及为什么选择 Nvidia 的详细信息。)
- TPU(张量处理单元)

虽然你可以使用其他 GPU 的混合精度,它会运行。如果没有上面详述的项目,你将不会获得任何真正的速度提高。然而,如果你只是想增加内存的使用,那么还是值得的。
旧的 GPU 使用混合精度不会带来数学性能优势,但是节省内存和带宽可以实现一些加速。
-张量流或 g
什么时候应该使用混合精度?
这个问题的简单答案是几乎所有的时间,因为在大多数情况下优点大大超过缺点。
唯一要注意的是,如果你的模型相对简单和小,你可能不会意识到差异。模型越大越复杂,混合精度的优势就越显著。
如何使用混合精度?
在 TensorFlow 中,这非常容易,我对 PyTorch 不太熟悉,但我无法想象实现起来会有多难。
from tensorflow.keras import mixed_precision
mixed_precision.set_global_policy('mixed_float16')
…就是这样。
对上述内容的唯一警告是,您应该确保模型的输入和输出总是浮动的 32。无论如何,输入都可能在 float32 中,但是为了确保您可以隐式地应用 dtype。例如:
images = tf.random.uniform(input_shape, minval=0.0, maxval=1.0, seed=SEED, dtype=tf.float32)
为了确保模型的输出在 float32 中,您可以将模型最后一层的激活分离出来。例如:
# Simple layer stack using the funcitonal API with separated activation layer as output
layer1 = tf.keras.layers.Conv2D(128,2)(inputs)
layer2 = tf.keras.layers.Conv2D(128,1)(layer1)
layer3 = tf.keras.layers.Conv2D(128,1)(layer2)
layer4 = tf.keras.layers.Flatten()(layer3)
layer5 = tf.keras.layers.Dense(1)(layer4)
output_layer = tf.keras.layers.Activation('sigmoid', dtype=tf.float32)(layer5)
自定义训练循环
将混合精度应用到您的模型真的很简单,如前一节所述。
但是,如果您因为正在实施自己的训练循环而没有使用“model.fit ”,那么还需要注意几个步骤,因为您必须手动处理损失比例。
如果您使用
[tf.keras.Model.fit](https://www.tensorflow.org/api_docs/python/tf/keras/Model#fit),损失缩放已经为您完成,因此您不必做任何额外的工作。如果您使用定制的训练循环,您必须显式地使用特殊的优化器包装器[tf.keras.mixed_precision.LossScaleOptimizer](https://www.tensorflow.org/api_docs/python/tf/keras/mixed_precision/LossScaleOptimizer),以便使用损失缩放。
这一点很重要,因为与 float32 相比,float16 的可用存储空间更小,因此其值容易“下溢”和“上溢”。所有这一切的基本意思是:
高于 65504 的值将溢出到无穷大,低于 6.0×108 的值将下溢到零。
为了避免这种情况,一种称为损失比例的策略被用来缓解这一问题。为了更深入的了解,我建议看一看 tensorflow.org 上的混合精度指南。
TPUs
如果你足够幸运,能够使用专用的 TPU(张量处理单元),那么值得注意的是,你应该使用数据类型“bfloat16”而不是“float16”。
实现起来并不困难,也不会遇到上一节提到的损失比例问题。
from tensorflow.keras import mixed_precision
mixed_precision.set_global_policy('mixed_bfloat16')
实际例子
作为潜在收益的一个例子,我提供了一个 colab 笔记本:
这样你就能看到它的好处。笔记本的开头有一些与您必须使用的 GPU 相关的注意事项,因此请确保您阅读了这些内容,以充分利用笔记本。
在下面的小节中,我将回顾笔记本中的结果。
数据
数据是被格式化成一批图像形状的随机均匀噪声。
# create dummy images based on random data
SEED = 12
tf.random.set_seed(SEED);
total_images = 800
input_shape = (total_images, 256, 256, 3) # (batch, height, width, channels)
images = tf.random.uniform(input_shape, minval=0.0, maxval=1.0, seed=SEED, dtype=tf.float32)
需要注意的是,我已经显式地将数据类型设置为 float32。在这种情况下,这没有什么区别,因为这是该函数的默认值。然而,根据数据的来源,情况可能并不总是如此。
一个示例图像如下:

图片作者作者
我还创建了随机的二进制标签,以便模型可以是二进制分类模型。
labels = np.random.choice([0, 1], size=(total_images,), p=[0.5,0.5])
模型
该模型被选择为简单,但足够复杂,以使用合理数量的 RAM,并有一个体面的批处理运行时间。这确保了混合精度和“正常”运行之间的任何差异都是可区分的。模型的层次如下:
layer1 = tf.keras.layers.Conv2D(128,2)
layer2 = tf.keras.layers.Conv2D(128,1)
layer3 = tf.keras.layers.Conv2D(128,1)
layer4 = tf.keras.layers.Flatten()
layer5 = tf.keras.layers.Dense(1)
output_layer = tf.keras.layers.Activation('sigmoid',dtype=tf.float32)
再次注意,输出激活层被转换为 float32。这对“正常”运行没有影响,但对混合精度运行至关重要。
测试
上一节提到的模型使用以下参数运行:
- 图像总数= 800
- 图像尺寸= 256 x 256
- 批量= 50
- 纪元= 10
总运行时间和纪元运行时间
然后使用 timeit 模块运行一次图像,以获得总的运行时间。
还会打印纪元运行时间。
GPU RAM 使用情况
要获取 GPU RAM 使用信息,使用以下函数:
tf.config.experimental.get_memory_info('GPU:0')
这将输出当前和峰值 GPU RAM 使用情况。在每次运行之前,峰值使用被重置并与当前 GPU RAM 使用进行比较(因此它们应该是相同的)。然后在运行结束时,进行相同的比较。这允许计算运行期间实际使用的 GPU RAM。
结果呢
单精度(float32)型号:
Epoch 1/10
16/16 [==============================] - 10s 463ms/step - loss: 90.4716 - accuracy: 0.5038
Epoch 2/10
16/16 [==============================] - 8s 475ms/step - loss: 9.1019 - accuracy: 0.6625
Epoch 3/10
16/16 [==============================] - 8s 477ms/step - loss: 1.6142 - accuracy: 0.8737
Epoch 4/10
16/16 [==============================] - 8s 475ms/step - loss: 0.2461 - accuracy: 0.9488
Epoch 5/10
16/16 [==============================] - 8s 482ms/step - loss: 0.0486 - accuracy: 0.9800
Epoch 6/10
16/16 [==============================] - 8s 489ms/step - loss: 0.0044 - accuracy: 0.9975
Epoch 7/10
16/16 [==============================] - 8s 494ms/step - loss: 7.3721e-05 - accuracy: 1.0000
Epoch 8/10
16/16 [==============================] - 8s 497ms/step - loss: 1.4208e-05 - accuracy: 1.0000
Epoch 9/10
16/16 [==============================] - 8s 496ms/step - loss: 1.2936e-05 - accuracy: 1.0000
Epoch 10/10
16/16 [==============================] - 8s 490ms/step - loss: 1.1361e-05 - accuracy: 1.0000
RAM INFO:
Current: 0.63 GB, Peak: 9.18 GB, USED MEMORY FOR RUN: 8.55 GB
TIME TO COMPLETE RUN: 79.73
混合精度(mixed_float16)模型:
Epoch 1/10
16/16 [==============================] - 15s 186ms/step - loss: 71.8095 - accuracy: 0.5025
Epoch 2/10
16/16 [==============================] - 3s 184ms/step - loss: 15.2121 - accuracy: 0.6000
Epoch 3/10
16/16 [==============================] - 3s 182ms/step - loss: 4.4640 - accuracy: 0.7900
Epoch 4/10
16/16 [==============================] - 3s 183ms/step - loss: 1.1157 - accuracy: 0.9187
Epoch 5/10
16/16 [==============================] - 3s 183ms/step - loss: 0.2525 - accuracy: 0.9600
Epoch 6/10
16/16 [==============================] - 3s 181ms/step - loss: 0.0284 - accuracy: 0.9925
Epoch 7/10
16/16 [==============================] - 3s 182ms/step - loss: 0.0043 - accuracy: 0.9962
Epoch 8/10
16/16 [==============================] - 3s 182ms/step - loss: 7.3278e-06 - accuracy: 1.0000
Epoch 9/10
16/16 [==============================] - 3s 182ms/step - loss: 2.4797e-06 - accuracy: 1.0000
Epoch 10/10
16/16 [==============================] - 3s 182ms/step - loss: 2.5154e-06 - accuracy: 1.0000
RAM INFO:
Current: 0.63 GB, Peak: 4.19 GB, USED MEMORY FOR RUN: 3.57 GB
TIME TO COMPLETE RUN: 42.16
我认为这是相当确凿的:

表作者作者
在上述结果中,您可能会注意到,混合精度运行的初始历元比后续历元要长 5 倍,甚至比 float32 运行还要长。这是正常的,这是由于 TensorFlow 在学习过程开始时进行了优化。然而,即使有这个初始赤字,混合精度模型也不需要很长时间就可以赶上并超过 float32 模型。
混合精度的较长初始历元也有助于说明为什么较小的模型可能看不到好处,因为需要克服初始开销才能实现混合精度的优势。
这也恰好是过度拟合的一个很好的例子。这两种方法都成功地在带有完全随机标签的完全随机数据上实现了 100%的准确率!
今后
较低精度计算的趋势似乎越来越明显,随着 Nvidia 最新一代 GPU 的出现,现在已经有了诸如 TensorFloat-32 这样的实现,它们:
在某些 float32 操作中自动使用较低精度的数学运算,如
[tf.linalg.matmul](https://www.tensorflow.org/api_docs/python/tf/linalg/matmul)。
还有一种情况是:
即使默认的数据类型策略为 float32,TPU 也会在 bfloat16 中执行某些操作
因此,随着时间的推移,实际上可能没有必要直接实现混合精度,因为这一切都将得到妥善处理。
然而,我们还没有到那一步,所以现在仍然值得努力考虑利用混合精确训练。
结论
得出的唯一结论是,混合精度是一个加速训练的优秀工具,但更重要的是释放 GPU RAM。
希望本文能帮助您理解混合精度的含义,我鼓励您试用一下 colab 笔记本,看看它是否符合您的特定要求,并感受一下它可能带来的好处。
如果你觉得这篇文章有趣或有用,记得关注我,或者注册我的时事通讯获取更多类似的内容。
如果你还没有,你也可以考虑订阅媒体。你的会员费不仅直接支持我,也支持你所阅读的其他作家。你还可以完全不受限制地访问媒体上的每个故事。
使用我的推荐链接注册会给我一点回扣,对你的会员资格没有影响,所以如果你选择这样做,谢谢你。
https://medium.com/@maclayton/membership
将艺术融入模型解释的科学中
原文:https://towardsdatascience.com/mixing-art-into-the-science-of-model-explainability-312b8216fa95
可解释 Boosting 机综述及一种将 ML 解释转换为更人性化解释的方法。

图 1 —我桌子上的乐高玩具,作者拍摄。
1.最大似然解释科学
1.1 可解释性与准确性的权衡
在传统的表格机器学习方法中,数据科学家经常处理 b/w 可解释性和准确性之间的权衡。

图 2:可解释性/可理解性和准确性的权衡,作者图片
如上图所示,我们可以看到像逻辑回归、朴素贝叶斯和决策树这样的玻璃箱模型是简单的解释模型,这些模型的预测并不十分准确。另一方面,黑箱模型像提升树、随机森林和神经网络很难解释,但却能带来高度准确的预测。
EBMs 简介
为了解决上述问题,微软研究院开发了 EBMs(可解释增压机)模型[1]。“可解释的助推机器(EBM)是基于树的、循环梯度助推广义加法模型,具有自动交互检测。EBM 通常与最先进的黑盒模型一样精确,同时保持完全可解释性。虽然 EBM 的训练速度通常比其他现代算法慢,但 EBM 在预测时非常紧凑和快速。”[2]

图 3: EBMs 打破了可解释性与准确性的悖论,作者图片
从上面的图表中我们可以看到,EBM 帮助我们打破了这种权衡悖论,并帮助我们建立高度可解释和精确的模型。为了进一步理解 EBMs 背后的数学,我强烈建议观看这段 12 分钟的 YouTube 视频
1.3 玻璃盒子 vs 黑盒模型。选什么?
**Tip:** *The answer to every complex question in life is “It depends”.*
与黑盒模型相比,使用玻璃盒模型有一些利弊。选择一个模型而不是另一个模型没有明显的赢家,但是根据情况,DS 可以对选择什么模型做出有根据的猜测。

图 4:玻璃盒模型与黑盒模型,图片由作者提供。
选择玻璃盒或黑盒模型时需要考虑的两个因素如下-
1)可解释性需求 —在不需要解释的领域,或者数据科学家或技术观众出于直觉/检查目的需要解释的领域,在这些情况下,DS 最好使用黑盒模型。在由于业务或法规要求而需要解释的领域中,或者在这些解释是提供给非技术观众(人)的领域中,玻璃盒模型占了上风。这是因为来自玻璃盒子模型的解释是exact和global。
**Note:** *Exact* *and* *global* *just means that a value of a particular feature will always have the same effect on each prediction explanation. For example, in the case of the prediction of income of a particular individual being above $50k with age as one of the predictors, if the* *age* *is 40 and it will impact the target variable with the same proportion let us say 5% in each observation in the data where the age is 40\. This is not the case when we build explanations through LIME and Shapely for black box models. In black-box models,* *age* *with the value 40 for example can have a 10% lift in an individual probability of their income being above 50k for one observation and -10% lift in the other.*
2)计算要求 — DS 需要注意各种计算要求,以便根据其用例测试和训练模型。EBM 在训练阶段特别慢,但是提供了带有内置解释的快速预测。因此,在每小时都需要训练模型的情况下,EBMs 可能无法满足您的需求。但是,如果模型的训练是每月/每周进行的,并且分数生成的频率更高(每小时/每天),那么 EBMs 可能更适合这个用例。此外,在可能需要对每个预测做出解释的情况下,EBMs 可以节省大量计算,并且可能是用于数百万次观察的唯一可行的技术。请看下文,了解 b/w EBMs 和其他基于树的集成方法的操作差异。

图 5: EBMs vs XgBoost/LightGBM,作者图片。
2.动手使用 EBMs
2.1 数据概述
对于这个例子,我们将使用来自 UCI 机器学习库【3】的成人收入数据集。该数据集中的问题设置为二元分类问题,根据各种人口普查信息(教育程度、年龄、性别、职业等)来预测某个人的收入。)超过 5 万美元/年。为了简单起见,我们只使用美国个人的观察结果和以下预测值-
Age:continuous变量,个人年龄Occupation:categorical变量,技术支持,工艺维修,其他服务,销售,执行管理,专业,处理-清洁,机器-操作-检查,行政-文书,农业-渔业,运输-搬家,私人-房屋-服务,保护-服务,武装部队。HoursPerWeek:continuous变量,每周花在工作上的小时数Education:categorical变量,学士,一些学院,11,HS-grad,Prof-school,Assoc-acdm,Assoc-voc,9,7-8,12,硕士,1-4,10,博士,5-6,学前。
**Note —** To render the blog with both code correctly, you can read the same on my [quarto powered blog](https://aayushmnit.com/posts/2022-09-23-Explainability/2022-09-23-Explainability.html#the-art-of-ml-explainability).
## Importing required libraries
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn import metrics
from interpret.glassbox import ExplainableBoostingClassifier
from interpret import show
import warnings
import plotly.io as pio
import plotly.express as px
warnings.filterwarnings('ignore')
pio.renderers.default = "plotly_mimetype+notebook_connected"
## Loading the data
df = pd.read_csv( "https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data", header=None)
df.columns = [
"Age", "WorkClass", "fnlwgt", "Education", "EducationNum",
"MaritalStatus", "Occupation", "Relationship", "Race", "Gender",
"CapitalGain", "CapitalLoss", "HoursPerWeek", "NativeCountry", "Income"
]
## Filtering for Unites states
df = df.loc[df.NativeCountry == ' United-States',:]
## Only - Taking required columns
df = df.loc[:,["Education", "Age","Occupation", "HoursPerWeek", "Income"]]
df.head()

让我们看看目标变量分布。

图 1 —目标变量分布,作者图片。
print(df.Income.value_counts(normalize=True))

在我们的数据集中,约 24.6%的人收入超过 5 万美元。数据看起来不错,我们有我们需要的列。我们将使用教育、年龄、职业和每周工作时间列来预测收入。在建模之前,让我们执行一个 80–20 的训练测试划分。
## Train-Test Split
X = df[df.columns[0:-1]]
y = df[df.columns[-1]] seed = 1
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=seed)
print(f"Data in training {len(y_train)}, Data in testing {len(y_test)}")

2.2 拟合循证医学模型
EBM 有一个 scikit 兼容的 API,因此拟合模型和进行预测与任何 scikit 学习模型是一样的。
ebm = ExplainableBoostingClassifier(random_state=seed, interactions=0)
ebm.fit(X_train, y_train)
auc = np.round(
metrics.roc_auc_score(
(y_test != ' <=50K').astype(int).values,
ebm.predict_proba(X_test)[:,1]),
3)
print(f"Accuracy: {np.round(np.mean(ebm.predict(X_test) == y_test)*100,2)}%, AUC: {auc}")

我希望上面的代码块显示了 interpret-ml API 与 scikit learn API 是多么相似。基于验证集的 AUC,我们可以说我们的模型比随机预测更好。
**Tip:** *In practice, if you are dealing with millions of observations, Try doing feature selection using LightGBM/XGboost and only train your final models using EBMs. This will save you time in feature exploration.*
2.3 来自 EBMs 的解释
解释包提供了全局和局部解释,并有各种可视化工具来检查模型正在学习的内容。
2.3.1 全球解释
全局解释提供了以下可视化效果-
- 总结 —特征重要性图,该图提供了每个预测因子在预测目标变量中的重要性。
- 特征与预测的相互作用 —该图表与 EBM 在进行实际预测时使用的查找表相同。这可以帮助您检查特征值对预测的贡献。
ebm_global = ebm.explain_global()
show(ebm_global, renderer='notebook')

图 6: EBMs 全球解说,作者图片。
当地的解释
局部解释是我们每个观察层次的解释。EBM 有一个很好的内置可视化来显示这些信息。
ebm_local = ebm.explain_local(X_test.iloc[0:5,:], y_test) show(ebm_local, renderer='notebook')

图 7: EBMs 本地解释,作者图片。
让我们举一个例子来解释指数为 0 时的观察结果
explainDF = pd.DataFrame.from_dict(
{
'names': ebm_local.data(0)['names'],
'data':ebm_local.data(0)['values'],
'contribution':ebm_local.data(0)['scores']
})
explainDF

正如我们从数据中看到的,我们可以看到列的名称、实际值以及该值对实际预测得分的贡献。对于这个观察,让我们看看模型在学习什么
- 本科教育有助于收入超过 5 万英镑
- 年龄值 47 也有利于> 50K
- 职业是“?”对超过 5 万英镑的收入有负面影响
- 每周 18 小时对超过 5 万英镑的收入有负面影响(美国每周平均工作时间约为 40 小时,所以这是有道理的)
您还可以对整个数据集执行此操作,并收集每个要素的重要性。下面是一个执行同样操作的示例代码。
scores = [x['scores'] for x in ebm_local._internal_obj['specific']]
summary = pd.DataFrame(scores)
summary.columns = ebm_local.data(0)['names']
summary.head()

现在我们可以提取测试集中所有数据行的重要性。
2.4 缺点和问题
这种解释仍然非常抽象,即使在观察层面上,推理也不是人类(非技术)友好的。当特征数量增加时,这甚至变得对人类不友好。你的模型的典型商业消费者可能不擅长阅读这样的图表,并且回避尝试模型给他们的洞察力/预测。毕竟,如果我不理解某件事,我就不信任它。这就是艺术的用武之地,让我们看看如何在上面的观察的基础上,让它更容易理解。
3.ML 解释的“艺术”
**Warning -** *The ideas I am going to share now are more marketing than real science.*
要让人们按照你的模特的建议行事,你必须建立信任。一个想法是建立信任,用数据轶事来支持你的解释。

数据轶事到处存在,这个想法是我在看一个股市工具时想到的(下图)。注意他们强调的两件事-
- 什么事? —这个工具出色地展示了刚刚发生的事件。例如— “微软公司因股息宣布或“达美航空 14 日 RSI 跌破 70 水平”。
- 为什么很重要? —然后工具指向历史数据,并告知该事件的意义。在微软的例子中,当事件发生时“历史上,MSFT 的价格平均上涨了 11.9%”。
如果我们能对我们的模型做同样的事情呢?

图 8。快照取自我的保真工具,图片由作者提供。
我们可以从训练数据中建立一个历史赔率表。让我们试着建造一个。
odds_data = X_train.copy()
odds_data['income'] = (y_train == " >50K").astype(int)
## Converting continous variables in buckets
odds_data['AgeBucket'] = (odds_data.Age // 5)
odds_data['HoursPerWeekBucket'] = (odds_data.HoursPerWeek // 5)
# Creating placeholder for odds dictionary
odds_dict = {}
# Columns for which we need odds
columns = ['Education', 'AgeBucket', 'HoursPerWeekBucket', 'Occupation']
for colname in columns: #iterating through each column
unique_val = odds_data[colname].unique() # Finding unique values in column
ddict = {}
for val in unique_val: # iterating each unique value in the column
## Odds that income is above > 50 in presence of the val
val_p = odds_data.loc[odds_data[colname] == val, 'income'].mean()
## Odds that income is above > 50 in absence of the val
val_np = odds_data.loc[odds_data[colname] != val, 'income'].mean()
## Calculate lift
if val_p >= val_np:
odds = val_p / val_np
else:
odds = -1*val_np/(val_p+1e-3)
## Add to the col dict
ddict[val] = np.round(odds,1)
## Add to the sub dict to odds dict
odds_dict[colname] = ddict
print(odds_dict)

现在使用这个赔率表,我们可以用预先填充的模板生成预测。参考下图。

图九。由作者输出到人类可读的文本、图像。
使用之前为行索引 0 生成的explainDF数据,我们可以使用上面的框架将其转换为文本。让我们看看输出是什么样子的-
def explainPredictions(df, pred, odds_dict):
reasons = []
if pred == 0:
sdf = df.loc[df.contribution < 0, :].sort_values(['contribution']).reset_index(drop=True).copy()
else:
sdf = df.loc[df.contribution > 0, :].reset_index(drop=True).copy()
for idx in range(sdf.shape[0]):
col_name = sdf.names[idx]
data = sdf.data[idx]
if col_name in odds_dict:
odd_value = odds_dict[col_name][data]
else:
odd_value = odds_dict[col_name+'Bucket'][data//5]
s1 = f"This individual have {col_name} value '{data}'."
s2 = f"Historically, people with this behavior have {odd_value}x likely to have income over $50k."
reasons.append(s1+s2)
return reasons
explainPredictions(explainDF, ebm_local.data(0)['perf']['predicted'], odds_dict)
["This individual have Education value ' Bachelors'.Historically, people with this behavior have 2.0x likely to have income over $50k.",
"This individual have Age value '47'.Historically, people with this behavior have 1.8x likely to have income over $50k."]
如果这是一个超过 50k 的预测呢?
explainPredictions(explainDF, 1, odds_dict)
["This individual have Education value ' Bachelors'.Historically, people with this behavior have 2.0x likely to have income over $50k.", "This individual have Age value '47'.Historically, people with this behavior have 1.8x likely to have income over $50k."]
看起来棒极了!我们可以为超过 50K 和 LinkedIn(领英)生成可读的推荐信,或者发邮件给我,地址是 aayushmnit@gmail.com。您也可以在 Medium 和 Github 上关注我,了解我将来可能会分享的博客帖子和探索项目代码。
参考
“InterpretML:机器学习可解释性的统一框架”(H. Nori,S. Jenkins,P. Koch 和 R. Caruana 2019)
Dua d .和 Graff c .(2019 年)。UCI 机器学习知识库[http://archive . ics . UCI . edu/ml]。加州欧文:加州大学信息与计算机科学学院。该数据集根据知识共享署名 4.0 国际版(CC BY 4.0)许可协议进行许可。
- “InterpretML: A Unified Framework for Machine Learning Interpretability” (H. Nori, S. Jenkins, P. Koch, and R. Caruana 2019)
- “Interpret ML — EBM documentation”
- Dua, D. and Graff, C. (2019). UCI Machine Learning Repository [http://archive.ics.uci.edu/ml]. Irvine, CA: University of California, School of Information and Computer Science. This dataset is licensed under a Creative Commons Attribution 4.0 International (CC BY 4.0) license.
本机支持缺失值的 ML 算法
原文:https://towardsdatascience.com/ml-algorithm-that-natively-supports-missing-values-40b42559c1ec
不需要显式处理缺少的值

现实世界的数据集通常包含大量缺失值,这可能是由于数据损坏或未能记录数据而导致的。数据中缺失值的存在可能会影响训练模型的稳健性。数据科学家需要在预处理管道中显式地处理丢失的值。
有多种技术可以显式处理数据中的缺失值。在我以前的一篇文章中,我们讨论了在预处理管道中处理缺失值的 7 种技术:
</7-ways-to-handle-missing-values-in-machine-learning-1a6326adf79e>
大多数机器学习算法无法处理数据集中的缺失值,因此必须在建模管道之前处理缺失值。在本文中,我们将讨论不需要显式处理缺失值的机器学习算法:
- 基于直方图的梯度增强分类器/回归器
上述估计器可以支持数据集中的缺失值,并且不需要在建模之前显式处理 nan。
基于直方图的梯度增强:
梯度提升是一种集成机器学习技术,它将 AdaBoost 等提升算法推广到一个统计框架,该框架将训练过程视为一个重复使用先前网络的任意损耗的加性模型,以提高估计器的能力。
梯度提升将树模型顺序添加到集成中,其中每个基于树的模型试图校正前一个模型的误差。
Scikit-learn 库提供了支持直方图技术的梯度增强的实验实现。它提供了histgradientsboostingclassifier和histgradientsboostingregressor类,分别实现分类和回归任务。
根据 scikit-learn 文档:
该估计器对缺失值(nan)有本机支持。在训练模型时,树基于潜在的增益,在每次分裂时学习缺失样本应该跟随左孩子还是右孩子。在推断过程中,缺失的样本记录被相应地分配给左边或右边的孩子。如果对于训练样本的特征没有遇到缺失记录,则这些样本被映射到具有最多样本的子代。
梯度推进算法在计算上是昂贵的,因为每个树是顺序训练和连接的,因此不能并行训练。对于较大的数据集,基于直方图的梯度增强的实现相对来说比[**GradientBoostingClassifier**](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.GradientBoostingClassifier.html#sklearn.ensemble.GradientBoostingClassifier)快得多。
实施:
数据集来源:我将使用从openml.org下载的开源 titanic 数据集来演示实现。根据知识共享协议的法律条款,数据集是公开和免费的。
Scikit-learn 包提供了[***HistGradientBoostingClassifier***](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.HistGradientBoostingClassifier.html) 类,实现了基于直方图的梯度增强分类器。titanic 数据集包含大量不需要显式估算或处理的缺失值。

(图片由作者提供),Titanic 数据集的缺失值计数
泰坦尼克号数据集有 891 个实例,特征:“年龄”、“上船”有缺失值。
在对分类特征进行编码并将数据样本分成训练样本和测试样本之后。
(作者代码),Titanic 数据集预处理
请注意,我们没有在预处理阶段处理丢失的值,训练和测试数据已经有了丢失的值。
现在让我们实现[***HistGradientBoostingClassifier***](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.HistGradientBoostingClassifier.html) 算法
(作者代码),基于直方图的梯度推进分类实现
Scikit-learn 还使用类[***HistGradientBoostingRegressor***](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.HistGradientBoostingRegressor.html)***.***提供了基于直方图的梯度增强的回归实现
本身支持缺失值的其他算法:
k-NN 和随机森林算法也可以支持缺失值。k-NN 算法通过取 K 个最近值中的大多数来考虑缺失值。不幸的是,k-NN 和 RandomForest 的 scikit-learn 库实现不支持缺失值的存在。
结论:
在本文中,我们讨论了基于直方图的梯度增强算法,该算法受 LightGBM 的启发,对于较大的数据集来说比[**GradientBoostingClassifier**](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.GradientBoostingClassifier.html#sklearn.ensemble.GradientBoostingClassifier)快得多。
在我的上一篇文章中,我们讨论了显式处理缺失值的 7 种技术:
</7-ways-to-handle-missing-values-in-machine-learning-1a6326adf79e>
如果样本数据有很多缺失值,我们可以使用剩余的数据来预测缺失的记录。我在以前的一篇文章中简要讨论过这个问题:
</7-ways-to-handle-missing-values-in-machine-learning-1a6326adf79e>
参考资料:
[1] Scikit-learn 文档:https://sci kit-learn . org/stable/modules/generated/sk learn . ensemble . histgradientsboostingclassifier . html
[2]泰坦尼克号数据集(公开发布):https://www.openml.org/d/40945
感谢您的阅读
ML 基础(第一部分):回归——机器学习的入门方法
简单而全面地介绍线性、非线性和逻辑回归

图 1:线性、非线性和逻辑回归的例子(来源:作者)
前言:
人们对机器学习中基本主题的介绍性帖子越来越感兴趣。因此,我将从这篇文章开始,在接下来的文章中讨论这些话题。这篇文章基本上是独立的,但是,它需要对线性代数和微积分有基本的理解。
回归
回归是估计因变量( Y )与一个或多个自变量( Xi )之间关系的过程。它主要用于在给定的一组数据样本中寻找模式,并在给定一组其他变量的值的情况下预测一个变量的值。它有广泛的应用,从预测天气预报到预测房价。这些变量的值通常是连续的,但是,也有回归方法的子集,其中的值可以是离散的(例如,名义值/序数)。一会儿我们会看到不同类型的回归方法。
线性回归
最简单的形式是,回归模型是一个线性函数,通过对自变量的值进行线性组合,可以获得因变量的值。例如,如果数据是二维的,这种模型可以写成直线方程(图 2)。一个直线方程有三个系数( a 、 b 和 c )。如果我们把因变量 y 写成 y = f( x ),那么我们看到只需要一个斜率( m )和一个截距' c '来表示任意一条线。这意味着,如果我们想找到一条最适合一组数据点的线,那么我们只需要找到这两个变量的最佳值。

图 2:直线方程的例子(来源:作者)

图 3:一组随机的数据点(来源:作者)
现在,让我们看一组数据点的例子,如图 3 所示。我们看到,大多数点正在形成类似于直线的模式,因此,我们可以将该数据建模为线性回归问题。更具体地说,我们感兴趣的是找到最适合这些数据点的直线的斜率和截距。
通过求解线性方程组进行直线拟合
如果我们把每个点写成一个线性组合( y = mx + c ),我们最终会得到一组线性方程组。为了找到' m' 和' c' 的值,我们至少需要两个方程。我们看到我们有两个以上的方程,因此,我们的问题是可解的。更一般地说,我们可以写出一组具有‘n’个样本和’d’维的线性方程,如图 4 所示。

图 4:线性方程组的解(来源:作者)
我们可以把所有的斜率和截距组织成一个组合向量,叫做' w' 。此外,我们可以在 X 中添加一列 1,以使乘积成为可能,并获得值的适当线性组合。然后我们就可以通过矩阵求逆,求解权值' w' 来求解这个线性方程组( Y = X w )。该解决方案为给定的数据样本集提供了最佳直线拟合的斜率和截距,如图 5 所示。

图 5:一组数据点上的直线拟合(来源:作者)
梯度下降直线拟合
线性方程组的矩阵逆解适用于噪声最小且无奇点的问题。然而,在大多数大型真实数据中,这些约束可能不成立。所以,一种更稳健、更通用的方法常被用来求解线性方程组,称为梯度下降。这是一种迭代优化方法,通过逐渐向输出解移动来寻找目标函数的最佳点(最小值/最大值)。更具体地,计算目标函数的导数(例如,在这种情况下,dy/dw=X),并且在最佳点的方向上采取一小步(即,J =|y-y’|->-y’的 T42 每一步后的权重更新为w’=w——alpha*J**其中 alpha 为学习率。使用梯度下降的直线拟合的输出示例如图 6 所示。**

图 6:随机梯度下降法直线拟合(来源:作者)
非线性回归
线性回归适用于变量之间的关系是线性的情况。然而,在许多现实世界的应用程序中,情况并非如此。当因变量和自变量之间的关系不能表示为线性组合时,我们需要使用非线性回归。在图 7 中可以看到一组非线性形式的数据点。

图 7:一组随机生成的数据点(来源:作者)
多项式回归
非线性回归可以是不同类型、曲线、闭合形状或任何其他形式。当变量之间的关系可以用曲线的形式表示时,它就被表示为一阶大于一的非线性方程。例如,我们在下面的等式中看到一个 k 次多项式。

多项式方程(来源:作者)

图 8:一个示例多项式函数(来源:作者)
我们可以用线性方程相乘来构造多项式。图 8 给出了一个四次多项式的特例。我们通过将截距分别为 1、5、6 和 12 的四个简单线性方程相乘,创建了一个复杂的多项式。乘数 0.01 提供了一个比例项,而负号反映了整个曲线。这只是为了说明多项式可以有不同的形状。学习这种曲线的主要目的是发现可能存在于真实世界数据集中的复杂模式。例如,数据可能是来自传感器的具有多种模式的连续信号,您可能希望使用非线性回归模型对其进行预测。
如果我们对之前的非线性数据应用多项式,我们会发现它可以很好地表示数据,如图 9 所示。对于该数据,二阶多项式应该足够了。非线性回归的问题表述与线性回归建模相同。然而,在形成权重矩阵时,必须计算高阶项的系数。一般来说,基于多项式的回归最适合于数据分布不广且似乎是连续进行的应用。这种数据的示例类型可以是来自传感器或测量设备的时间序列数据。

图 9:一组数据点的多项式曲线拟合(来源:作者)
高斯过程回归
在有些情况下,我们希望模型不仅预测高度精确的函数值,例如直线/多项式拟合,而且还应该考虑余量。在这种情况下,我们学习每个点的分布(即平均值和方差),而不是单一的权重值。它可以直接应用于信号有噪声或有少量湍流的情况。将回归问题建模为高斯过程可以准确预测感官数据。这种高斯过程回归的核心是一个核函数。可以有多种适用于不同类型的数据和应用的内核函数。最常见的核函数是指数函数(例如径向基核),其提供自变量和因变量之间的非线性关系。然后,回归过程的目标是优化问题,由此每个输入数据点的最合适的均值和方差值将被优化。对于不同类型的内核,这种高斯过程回归的样本输出可以在图 10 中看到。曲线周围的条带预测模型在预测某一点的值时的置信度。范围越窄,输出值越严格。


图 10:不同核函数的基于高斯的回归(来源:作者)
正则化逻辑回归
到目前为止,我们已经处理了连续回归问题,其中预测输出变量的值是一个连续数。然而,在许多实际应用中,情况并非如此。例如,在对物体/特征进行分类的情况下,我们想要学习每个类别的离散标签,而不是数值。这个问题可以用一种特殊的回归方法来处理,叫做逻辑回归。它实际上是一种回归方法;但是,它修改了输出值和目标函数,使得对于给定的类,输出是 0 到 1 之间的概率值。

这可以通过在 Sigmoid 函数中包装输入变量值“x”来实现。Sigmoid 函数是一个非线性指数函数,它将“x’的任何值映射到 0 和 1 之间的正值。回归问题的其余部分保持不变。逻辑回归和基于 Sigmoid 函数的输出计算的目标函数在上面的等式中给出。训练集和测试集的数据点可以在图 11 中看到。

图 11:分别用于训练集和测试集的一组示例数据点(来源:作者)
分类问题需要对一组数据进行训练,而模型需要预测另一组数据的值。因此,重要的是,该模型不要仅对于训练集具有太紧的界限,并且应该对于训练集中不存在的输入值工作良好。这个问题被称为“过拟合”,通过一种被称为“规则规则化”的方法来解决。正则化是优化过程中的松弛项,它给模型拟合一个喘息的空间。这是通过在目标函数中增加一个附加项来实现的,如上面的等式所示。这种正则化项的系数λ是一个加权因子,其提供了准确性和概化性之间的折衷。这种回归拟合的结果可以在图 12 中看到。

图 12:二元分类问题的回归拟合结果(来源:作者)
结束语
在本文中,我们已经讨论了回归拟合及其类型。线性、非线性和逻辑回归方法是您在实际应用中会遇到的三种主要的回归问题。回归是一种简单而有效的方法,可以解决许多应用领域中的一些常见问题。它也构成了更复杂的机器学习方法的基础,我们将在以后的文章中讨论这些方法。
如需代码,请点击链接:
https://www.github.com/azad-academy/MLBasics-Regression
在 Patreon 上支持我:
https://patreon.com/azadacademy
在子栈上找到我:
关注 Twitter 更新:
https://www.twitter.com/@azaditech
ML 基础(第 2 部分):支持向量机
原文:https://towardsdatascience.com/ml-basics-part-2-support-vector-machines-ac4defba2615
什么是支持向量机,以及如何为监督学习制定、构建和应用支持向量机

图 1:战争中士兵的例子中支持向量机模型的描述(来源:作者)
在之前的帖子中,我们学习了回归方法。在本文中,我们将介绍一种类似但稍微先进的机器学习方法,称为“(SVM)”。除非你已经很熟悉了,否则建议你在阅读这篇文章之前先看看之前的文章。
简介
如果您已经学习了前面的课程,您现在应该知道机器学习模型的目标是找到最适合数据的模型参数。在线性模型(例如,一条线)的情况下,这意味着找到穿过所有数据点的线的最佳系数。然而,不可能用一条线来拟合以群集形式分布的所有数据点,因此,需要找到一条与大多数点的距离最短的线。这就是线性回归的工作原理。然而,这些基于多数的方法的最大缺陷是它们倾向于过度适应训练样本。这意味着它们倾向于在训练集上工作良好,但是不能预测训练集中不存在的新例子。这种普遍性的缺乏导致了一类新的方法,它们在概念上以相反的方式工作。这些方法不是为大多数人找到最佳的拟合,而是试图对两个类别之间的分隔空间进行建模。这样的分离空间称为“超平面”。SVM 就是这样一种技术,它试图学习一个超平面,该超平面能够最好地分离两组数据点。

图 2:支持向量、边缘和超平面的描述(来源:作者)
SVM 通过在存在于这种超平面边界的两个数据集群中找到那些“离群值来完成这项任务。让我们看看图 2 中的示例图像;如果我们使用基于多数的分类器,它会试图找到一条单独的线来区分蓝色和红色的簇。但是,这种分类是不正确的,因为它会忽略远离聚类平均值的点,从而导致绘制错误的边界。然而,当更多的数据变得可用时,均值偏移和那些更远的点看起来不再是异常值。因此,这种基于多数的方法会失败,并且不能产生适用于新样本的通用解决方案。
SVM 方法通过找到数据中最重要的点(或从原点画出的向量)来解决这个问题。这些点位于超平面的边界上。这些点被称为支持向量。它们之所以这样命名,是因为它们的存在占据了分类边界。如果将它们移除,两个集群之间的分隔线会发生显著变化。支持向量机试图从两个相反的类别中找出它们之间距离最短的支持向量。这个距离被称为边缘,该边缘最末端的线形成决策面的边界。
线性 SVM
支持向量机的主要优势在于其发现非线性决策边界的能力。然而,它们也非常适合线性可分的数据。在本文前面,我们已经看到了线性可分数据的一个简单例子(例如,战争中的士兵)。如果你阅读了回归文章,你会注意到这样的例子也可以用逻辑回归建模。然而,当样本数量较少且存在导致稀疏的缺失数据时,使用 SVM 进行线性分类任务表现更好。在这种情况下,支持向量机表现得更好,因为它们不模拟大多数,而是支持边界点。
SVM 目标函数
前面提到过,SVM 模型的目标是找到使裕度最大化的超平面,裕度是两个相反类别的决策边界之间的距离。因此,我们需要定义一个目标函数,该函数找到最佳的支持向量,从而找到决策边界,同时也最大化两个决策边界之间的余量。更具体地说,我们想找到一个权重向量' w' ,它的每个元素都是一组数据点的系数。从支持向量到决策边界的最短距离(即点到线的距离)是 1/|| w ||并且两个决策边界之间的总距离(即余量)是 2/|| w ||。所以,如果我们想最大化利润率,我们需要最小化|| w ||。图 3 中的函数 f(x) 显示了这个目标函数。

图 3:支持向量机优化函数的推导(来源:作者)
除了目标函数,我们还需要确保决策边界得到遵守。为此,我们必须对目标函数施加约束,使得当点属于一类时,输出> =+1,而当它们属于另一类时,输出为上述方程组中的<=-1. This constraint is represented by the function g(x) 。现在为了解决这个问题,我们必须把它写成拉格朗日形式——约束优化问题的数学优化公式。我们通过遵循上述等式中的目标函数公式化的拉格朗日格式来编写新的目标函数。目标函数‘L’由两部分组成,原最小化函数 f(x) 和约束函数 g(x) —决策边界约束。阿尔法矢量是我们想要优化的一组参数,但是我们也没有 w 或 b 。我们可以通过对偶拉格朗日公式进一步简化这个拉格朗日公式。

图 4:对偶拉格朗日公式(来源:作者)
我们通过替换早期目标函数公式中的“w”和“b”来实现对偶拉格朗日公式。我们可以通过分别取 L 相对于 w 和 b 的梯度并将它们设置为零(回想一下,在最佳点梯度为零)来获得' w' 。这给了我们一个简化的目标函数,它只取决于数据集中每个点的α参数向量。
如果你一直在观察,你也应该注意到,除了 Xᵢ.,我们还引入了另一个向量 Xⱼ这是因为我们想要区分两个向量:一个代表数据点,另一个代表权重。但是,请注意,两者都来自同一组数据点。这样,这个乘积就是一个向量和另一个向量的点积。两个向量的点积给出了相似性的度量。换句话说,我们想要从不同的类中找到两个相似的向量,这两个向量使裕度最大化。

图 5:使用点积描述支持向量的相似性计算(来源:作者)
我们将在数据集中的两个数据点之间进行这种成对的相似性计算,然后找到 alpha 向量的最佳值,这将是一个稀疏向量——一个大多数条目为零且只有少数条目非零的向量。这样,我们将能够在优化过程结束时,通过使用来自 alpha 向量的非零系数来挑选支持向量。
使用 Python 进行线性 SVM 分类
在前面的章节中,我们已经了解了支持向量机的概念。我们可以使用 python 的 scikit 库来构建线性 SVM 模型,并将其应用于示例数据集。我们首先创建一个随机数据集,并将其分为训练集和测试集,如下图所示。

图 6:一组随机的数据点被分成训练集和测试集(来源:作者)
然后,我们可以用线性 SVM 模型来拟合训练样本,并用它来对测试样本进行分类。生成的超平面如下图所示。

图 7:线性 SVM 模型拟合训练集的决策边界(来源:作者)
图 7 中的极端线条显示了决策边界,靠近这些线条的点是图 8 中描述的支持向量。请注意,这些几乎是异常点,但是它们是最关键的点,因为它们的存在/不存在会改变决策边界。

图 8:线性 SVM 模型的支持向量的描述。(来源:作者)
测试集的分类结果如下图所示。阴影颜色区域显示了特定类别的决策边界。这些点离决策边界越远,它们被分类到相应类别的可能性就越大。位于相应颜色的较亮色带上的点是支持向量。

图 9:测试集上线性 SVM 模型的结果(来源:作者)
非线性 SVM
在上一节中,我们成功构建了一个线性 SVM 模型,并使用它对数据集进行分类。线性支持向量机一直工作到我们遇到不可线性分离的数据。然而,大多数真实世界的数据不是线性可分的。这就是我们需要非线性 SVM 模型进行分类的地方。
非线性目标函数公式
在下图中,你可以看到一个非线性数据的例子。

图 10:非线性数据的例子(来源:作者)
如果我们在这个数据集上使用线性 SVM 模型,我们将得到如下图所示的结果。

图 11:线性支持向量机在非线性数据上的应用(来源:作者)
正如你所见,线性 SVM 模型未能准确分类数据点。是因为线性模型不适合这样的数据。这并不好,因为我们已经提到,支持向量机的最大优势是能够准确分类非线性样本。我们需要为非线性支持向量机重构我们的目标函数。这是我们在考虑了数据样本中的非线性后得到的结果。

图 12:非线性支持向量机的目标函数(来源:作者)
请注意,我们的主要变化是我们引入了一个内核函数。如果你已经学习了回归课程,你将会熟悉内核函数。它们是将数据投射到多维空间的函数。这些核函数可以是不同类型的,一些例子是多项式核、径向基核和 sigmoid 基核函数。
采用核函数的非线性分类
现在,如果我们在非线性数据集上使用这个新公式,我们将得到如下图所示的输出。我们对这个数据集使用弧度基核,因为数据样本清楚地显示了圆形模式。正如你所看到的,它已经成功地对所有的例子进行了分类。

图 13:使用 RBF 核的非线性 SVM 分类(来源:作者)
如前所述,不同类型的数据需要不同类型的内核函数。我们看到了一个 RBF 核函数的例子。有些情况下,不同的内核函数更适合。例如,让我们以下面的数据为例。该数据中的样本没有形成放射状模式。

图 14:随机生成的数据样本(来源:作者)
这两个集群几乎是线性可分的,直到它们在中心重合并混合。我们可以画一条分割曲线,将这两个集群分开。这是我们可以利用多项式核的地方,它使用多项式函数作为决策边界。我们在下图中看到,使用这种核的分类成功地对大多数样本进行了准确的分类。

图 15:使用多项式核的 SVM 分类(来源:作者)
结束语
在本文中,您已经了解了支持向量机。您已经学习了如何为支持向量机制定目标函数,以及如何为线性可分数据构建 SVM 模型。您还学习了如何通过使用核函数来处理数据中的非线性。支持向量机是许多现实世界应用的伟大工具,在某些情况下甚至可以胜过复杂的神经网络模型(取决于数据)。在接下来的课程中,我们将探索更多的机器学习概念。
如需代码,请点击链接:
【https://www.github.com/azad-academy/MLBasics-SVM 号
在 Patreon 上支持我:
*https://www.patreon.com/azadacademy
在子栈上找到我:
关注 Twitter 更新:
https://www.twitter.com/@azaditech*
ML 基础(第 3 部分):人工神经网络
原文:https://towardsdatascience.com/ml-basics-part-3-artificial-neural-networks-879851bcd217
人工神经网络的简易指南和用于理解和学习概念的交互式可视化工具

图 1:用于可视化和学习的人工神经网络交互工具(来源:作者)
在之前的帖子中,我们已经讨论过 回归 和 支持向量机(SVM) 作为机器学习中的两种重要方法。SVM 与回归分析有一些相似之处,然而,有一种方法是逻辑回归的直接派生。称为“人工神经网络(ANN) ”。在本文中,我们将从零开始构建一个 ANN ,您将能够通过一个交互式工具来理解这个概念。
简介
人工神经网络的历史可以追溯到 20 世纪初。这是从人脑中形成网络的生物神经元得到的灵感。人工神经网络(ANN)是多层连接的神经元的集合,当输入层中的神经元被激励时,它产生输出。下图显示了人工神经元的一个非常简单的描述。

图 2:人工神经元输入和输出的定义。(来源:作者)
一个人工神经元有许多输入 Xⱼ 和权重 Wⱼ 。根据神经元在输入端接收到的信息,它输出一个单一值 Z ,它是矢量 Wⱼ 与 Xⱼ 的点积。还有一个偏置项 b ,作为线性组合添加到点积中。如果你从我们之前的课程中回忆起回归,那么你就会明白为什么会这样。它是代表线性回归函数的线性组合,其权重为 Wⱼ 和输入 Xⱼ 。通过应用激活函数(例如,Sigmoid)将神经元的输出映射到范围上。这种激活功能增加了输出的非线性,并将其保持在一个范围内(如 0–1)。
网络建设
上一节中解释的人工神经元是相互连接的,并排列成一组多层。一个非常简单的 ANN 由三层组成:输入层( ⁰L ),隐藏层( L ),输出层( L )。输入层由值 Xⱼ 组成,输出层提供网络的结果,即给定输入的输出类的概率。输出层中神经元的数量取决于数据集中类的数量。下图显示了这样一个简单的 3 层 ANN 。

图 3:具有一个输入层、一个隐藏层和一个输出层的三层神经网络的例子(来源:作者)
学习发生在各层之间。更具体地,学习每一层“l”的权重 ˡW ,使得当这些权重被插入 ANN 时,输出类似于真实的类标签。权重 ˡW 以矩阵的形式排列,每个层转换一个。权重矩阵 ˡW 的维数取决于层“ l 中神经元的数量和前一层中神经元的数量。输入的数量被认为是层“0”的神经元数量。
所有层中的神经元都有两个组成部分:从激活函数的输入和输出的权重的点积计算的输出值。在图 3 所示的例子中,我们只有一个输出,这意味着它只产生一个类的概率。最后一层中的激活函数(即 Sigmoid)确保值保持在 0 和 1 之间。重要的是要注意这个简单网络与逻辑回归的相似性。事实上,一个简单的三层网络,只有一个隐藏层,如图 3 所示,不过是一个线性逻辑回归,它只对线性可分数据有效。为了给系统增加非线性,必须增加多个隐藏层。
目标函数公式
ANN 提出了一个优化问题,像任何其他机器学习方法一样,它寻求在一组参数上优化目标函数,以获得期望的输出。更具体地说,我们试图找到权重的最佳值,这样当我们将它们插入到一个 ANN 中时,它应该为输入 Xᶦ.输出真正的类标签我们通过最小化误差(ŷ — y)来实现这一点,其中ŷ是由 ANN 预测的分类概率,而 y 是真实的分类标签。在实践中,我们通过取ŷ的对数并将其排列成线性组合来计算这种误差,以适应“1”标签和“0”标签。目标函数如下图 4 所示。

图 4:人工神经网络的目标函数公式(来源:作者)
如果您注意的话,您会注意到目标函数中还添加了另一项 JREG 。这是正则项,以确保模型避免过度拟合。它是所有层中所有权重的平方和。λ参数是训练集上正则化和准确性之间的折衷。较低的值可能会使模型在训练集上形成紧密的决策边界,从而可能无法适应测试集中的新变体示例,而较大的值可能会导致模型不太适合数据。
前馈过程
神经网络由两种类型的过程组成:前馈过程和反向传播过程。前馈过程负责基于相应的输入和神经元的权重来计算网络中单个神经元的输出。更具体地,通过取相应权重ˡ w 、来自前一层ˡ⁻ a 的输入激活和偏置ˡ b 的线性组合,为网络中的每个神经元计算ˡ z 的值。然后,通过应用激活函数,ˡ z 的值被转换成激活ˡ a 。在前馈过程中执行的操作的完整列表如图 5 所示。

图 5:前馈过程的方程式。(来源:作者)
反向传播过程
也许学习的关键在于一个 ANN 的反向传播步骤。它是前馈步骤之后的步骤,负责将误差分配给网络中的每个权重。反向传播步骤从计算网络最后一层的误差(ŷ — y)开始。这个误差然后被用于计算每一层的权重的变化。这是通过应用偏导数的链式法则来实现的。回想一下你的微积分 f(g(x)) = f'(g(x))。g '(x)。现在从输出回溯到输入,以这样一种方式,你最终计算目标函数 w.r.t .权重的变化(∂J/∂ w )。这可以通过回溯当前层中的输出的偏导数 w.r.t 并将它们与前一层的输出相乘来计算。偏导数 w.r.t 输出可以使用链式法则进一步分解,直到我们到达第 0 层。这些偏导数在图 6 中被描述为增量。计算出的增量然后从最后一层开始递归地乘以每个先前层的激活。您可能还会注意到,在计算 delta 时,sigmoid 函数的导数也会相乘,这是因为我们通过对神经元的输出应用 sigmoid 函数来计算激活(即 a = h( z ))。

图 6:反向传播过程的推导(来源:作者)
还要注意我们是如何计算输出zw r t 权重w(∂j/∂w)的变化的。我们使用了 z 的定义,并对zw . r . tw进行了求导。我们做了同样的事情来计算当前层的 z 对前一层的 a 激活的偏导数(∂ˡ z /∂ˡ⁻ a )。唯一不同的是,这次我们对ˡ⁻a 求导。
我们在每一层计算 J w.r.t. w 的这些偏导数,并为每个权重矩阵ˡ w 获得梯度矩阵ˡw’。这些梯度矩阵表示每个权重的值在 J 的最优值方向上的变化。优化器(例如梯度下降)然后使用这些梯度来更新权重(例如wₙ=w—αw’),其中α是学习速率。
用 Python 实现
在这节课中,我们仅使用 python 中的 numpy 从头开始构建一个简单的神经网络。网络由三层组成:输入层、一个隐藏层和一个输出层。输入采用二维数据(为了便于理解这个概念,保持简单)。隐藏层中有三个神经元。输出层有一个神经元,输出输入点来自该类的概率。完整的 ANN 与每个神经元的权重、偏差、输出和激活一起显示在图 3 中。
线性分类
所构建的网络可用于对属于两类(0/1)的一组数据点进行分类。我们构建一组随机的数据点,然后将其分为训练和测试两部分。这可以从图 7 中看出。

图 7:分别用于训练和测试的两组数据(来源:作者)
通过运行优化算法(即梯度下降)在训练集上训练 ANN 模型,该优化算法为 ANN 的权重找到最佳值。然后,我们对这些数据点应用 ANN 模型,并获得测试集中每个数据点的分类标签。这种分类的输出可以在图 8 中看到。

图 ANN 在线性可分测试集上的应用(来源:作者)
非线性分类
正如前面提到的,一个简单的 3 层 ANN 只不过是线性逻辑回归,这意味着它只能对线性可分的数据进行分类。然而,如果我们把它应用于一个不可线性分离的数据,比如图 9 中给出的数据,那么它会失败得很惨。

图 9:非线性数据的示例训练和测试集(来源:作者)
这意味着我们必须构建一个具有多个隐藏层的网络。为此,我们修改了之前的 ANN 并增加了两个隐藏层,每层 5 个神经元,如图 10 所示。

图 10:解决非线性分离问题的大型网络(来源:作者)
我们将这个更大的网络应用于图 9 中的训练集,并获得这个新网络的一组优化权重。然后,我们将该网络应用于相应的测试集,并获得分类概率。可以通过对概率应用阈值(例如,> 0.7)来获得类别标签。这种网络的结果如下图 11 所示。

图 11:使用人工神经网络进行非线性分类的结果(来源:作者)
结束语
在本文中,您已经学习了什么是人工神经网络,如何用 python 从零开始构造一个人工神经网络。您还学习了如何应用神经网络来预测线性可分数据和非线性可分数据的类别标签。您可以通过交互式可视化工具进一步了解,您可以在您的 Jupiter 笔记本上运行该工具,并查看权重是如何更新的。

图 12:人工神经网络的交互式可视化工具(来源:作者)
代码:
https://www.github.com/azad-academy/MLBasics-ANN
成为帕特里翁的支持者:
https://www.patreon.com/azadacademy
在子栈上找到我:
关注 Twitter 更新:
https://www.twitter.com/azaditech
宏观经济学 ML # 1
原文:https://towardsdatascience.com/ml-for-macroeconomics-1-cb7fccf93ccf
低质量数据中随机森林的变量选择

卡洛斯·阿兰达在 Unsplash 上的照片
虽然机器学习 (ML)席卷了许多应用科学,但经济学对因果关系的强调使其很难找到相关的应用。在过去的几年里,苏珊·艾希成功地开创了微观经济学的各种应用,然而,宏观经济学的学生们还没有找到类似的榜样来效仿。这就是为什么在这篇文章中,我想用变量选择的问题来说明一个更小的领域,ML 可以很容易地补充经典技术。
鉴于宏观分析的聚合性质,如跨国研究,高维(也称为“大”)数据是研究人员面临的最常见的情况。尽管最小绝对收缩和选择算子 ( 套索)在经济学家中一直很受欢迎,但像随机森林 ( RF )这样的非参数替代物却没有得到类似的关注。因此,我们将一起 i) 进行模拟研究,比较 Lasso 和 RF 在低质量数据下的行为,并将 ii) 应用于 Sala-I-Martin 著名的“百万”经济增长数据集。
免责声明 :本文基于GitHub上的一个课程项目。所有的材料,进一步的解释和额外的参考资料都可以在那里找到。
变量选择复习
变量选择——在 ML 文献中称为“特征选择”——是处理高维数据(即预测值数量相对于观察值数量较高的数据)的最常见方法之一。其思想是,如果研究人员知道只有所有协变量的子集影响因变量,正确识别这些可以减轻一些“维数灾难”,如预测准确性(范和吕,2009)。在实践中,这被称为稀疏假设。
套索和随机森林的应用是多年来发展起来的各种可变选择技术的两个例子。重要的是,就像在统计学中一样,不存在单一的最优过程。正如 Hastie 等人(2017 年)解释的那样:
不同的过程具有不同的操作特性,即,当我们改变它们各自的调谐参数时,产生不同的偏差-方差权衡。事实上,根据问题设置,最佳子集选择提供的偏差-方差权衡可能比 lasso 提供的权衡更有用或更不有用。(第 582 页)
现在,Hastie 等人所指的是“最佳子集选择”自然具有非常低的偏差,但是,在有噪声的数据中,Lasso 等方法通常可以提供更好的结果,因为它们以一些偏差换取方差的大幅减少。事实上,这种类型的权衡是本文的中心动机,因为我们将 Lasso 与另一种需要有效减少方差的技术相比较:RFs。在进行模拟研究之前,让我简单介绍一下 Lasso 和 RF。
套索属于收缩法的一类,具有特殊的性质,即对于罚项的适当选择,系数被精确地收缩到零。由于惩罚项缩小了所有系数的大小,因此开发了一种松弛套索,其中非零系数的控制与整体收缩分开。
RFs 的定义特性是去相关决策树的聚合,这对于减少方差特别有效。与 Lasso 不同,RF 并不自然地执行变量选择,Genuer 等人(2010)为此开发了一种逐步上升的变量引入策略,并在 R-CRAN 库上提供的非常好的 VSURF 包中实现了该策略。
模拟研究
考虑到偏差-方差权衡对问题结构的依赖性,我们的模拟研究模仿我们的真实世界应用(即经济增长的跨国研究)是关键。除了(非常)小的样本量(世界上大约有 100 个国家),这还与我们任何一年的数据质量密切相关:人口普查很少进行,没有全球标准化,严重依赖数据估算——这些问题在不太富裕的国家更为严重。
我们将数据质量作为比较的重点,并通过信噪比 ( SNR )对其进行评估。直觉上,如果噪声(误差)比信号(我们的解释变量)多得多,就很难识别信号的影响。我们遵循 Hasties 等人(2017 年)的方法,考虑了对数标度上从 0.05 到 6 的十个 SNR 值,为了进行比较,还报告了“解释的方差比例”的相应值。重要的是,正如作者强调的那样,研究往往对信号清晰度过于乐观,并认为在现实世界中,观测数据往往具有 log(SNR) < 1 !

图片作者。
作为这种 SNR 标度的结果,模拟研究计算了 10 个 x #sim 合成数据集,并将套索、松弛套索和 RF 应用于每个数据集。数据集是稀疏的,因此 50 个协变量中只有 5 个影响结果。Toeplitz 矩阵用于方差-协方差矩阵,因此变量的相对位置决定了相关结构。请注意,我们选择了线性 DGP,在比较参数方法和非参数方法的结果时,需要考虑这一点。最后,模拟次数设置为 100。
现在我们已经熟悉了模拟研究的概念设置,我们可以看看结果了。如果您对代码或更详细的解释感兴趣,包括对 SNR 的控制,我诚挚地邀请您在 GitHub 上查看原始项目。
模拟 1 —基线

作者图片
- RF 始终更接近真实稀疏度,在所有 SNR 水平上的方差都非常低
- Lasso 方法的良好保留频率是过度密集估计的副产品,并隐藏了无效模型的数量
在基线模拟中,一般的相关程度是中等的(0.5 到直接邻居),在五个真正的预测值之间没有特殊的相关性。左上方的图描绘了保留频率,即鉴定了多少真预测因子。毫不奇怪,当信号变得更清晰时,所有三个模型在识别重要变量方面都变得更好。然而,RF 在 SNR 0.25 和 1.22 之间的相对表现不佳具有误导性:查看右上方的图表说明了模型预测的非零系数的数量,换句话说,它的估计密度。这里我们可以看到,套索方法的高保留频率是其高度高估密度的结果。
事实上,虽然松弛的套索由于其分离收缩和稀疏控制的能力,最终更接近真正的稀疏,但对于信噪比的实际水平,它仍然选择平均两倍的协变量。查看右下方的图表,该图放大了“真实世界窗口”,我们看到 RF 密度牢牢地集中在真实数字周围,而在 SNR 的下端,即使是松弛的套索也选择了许多零模型(即模型没有识别出显著的协变量)。
最后,按照惯例,还报告了 MSE(左下角)。我们可以看到,对于低水平的 SNR,MSE 是天文数字,并且随着数据质量的提高而迅速降低。重要的是,我们可能想知道为什么非参数方法明显优于线性方法,尽管事实上这些方法是正确指定的。然而,对于 RF,我们只报告了 VSURF 的出袋均方误差,这是向下偏置的,因为排名和选择是基于相同的观察结果(Genuer 等人,2015)。从该图中可以看出,RF 和 Lasso 的 MSE 下降斜率相似。
模拟 2-高协方差

作者图片
- RF 对高共线性非常稳健,不像 Lasso
由于高度共线性是宏观经济分析中的一个常见问题,因此更好地理解这些方法的相对性能对于解释应用结果非常重要。这就是为什么在第二次模拟研究中,我们让所有真正的预测者成为邻居,从而给他们一个相对高相关性的结构。重要的是,鉴于我们的托普利兹矩阵的性质,这也意味着现在还存在一组无关紧要的变量,这些变量与所有真实预测值密切相关。这种设置的目标是众所周知的弱点套索选择正确的变量,如果相关性高(见,例如,詹姆斯等人,2013 年)。相比之下,对于 RF,Genuer 等人(2010 年)发现,可变重要性排序对共线性的存在非常稳健。
事实上,RF 的性能与第一项研究相比几乎没有变化,小提琴图实际上表明,在实际 SNR 窗口的低水平上,方差有所降低。另一方面,我们看到标准套索生成了更多的无效模型,并且保留频率的增加较低。类似地,松弛的套索也生成许多空模型,但也演示了它用多达 20 个变量膨胀模型的情况。因此,尽管其保留频率和平均密度与 RF 非常匹配,但其性能仅在高信噪比下才会收敛。
原始项目 包含几项附加研究,进一步说明了这些技术的偏差-方差权衡。
应用
有了这些见解,我们现在用 Sala-I-Martin (1997) [ 此处可用 ]著名的“百万”数据集将这三种技术用于这项任务。它由 20 世纪 70 年代 134 个国家的 63 个宏观变量组成,与我们模拟研究的观测变量比相匹配。标准化并用样本平均值替换缺失值后,我们剩下 119 个国家和 62 个预测变量。
Sala-I-Martin (1997 年)以标志性的标题“我刚刚运行了两百万次回归”着手开发一种比当时非常流行的“极限界限测试”更快速、更灵活的方法来识别相关的宏观经济增长变量。他自己提出了基于累积分布函数来估计和选择变量,我们也将累积分布函数包含在下面的结果表中。

百万数据结果表(图片作者提供)。
在这四种方法中,选择了 20 个不同的变量作为 20 世纪 70 年代 GDP 增长率的预测值。虽然乍一看,我们可能会发现这种实质性的分歧令人惊讶,但仔细检查估计结果会发现存在高度共线性(在原始项目中详细讨论)。记住第二次模拟研究的结果,尽管所有四种方法的回收率都相对较低,我们应该对 RF* 估计的密度相对有信心。因此,尽管 CDF 和 relaxed Lasso 的节俭看起来很有吸引力,但这表明他们的估计过于保守。*
为了便于比较,Lasso 方法的结果按估计系数的绝对值排列。我们可以看到,四个变量不仅在 all 或 Lasso&RF(以绿色标记)之间共享,而且通常被赋予高度的重要性:虽然随机森林方法只赋予宗教一个次要角色,但它也将“设备投资”(即机械化投资)计算在经济增长的一组非常重要的驱动因素中。对经济开放度的衡量也是如此,这四种方法都非常重视经济开放度。相比之下,Sala-I-Martin 的 CDF 方法没有选择小学入学人数,而 Lasso 方法将小学入学人数排在首位,在 RF 方法中排在第三位。然而,结果的明显不稳定性提醒人们不要过于相信这些排名:对于套索,变量很难选择,以至于交叉验证过程中的随机性导致“预期寿命”包含在放松的套索中,而不是标准的套索中。
退一步说,我们看到存在一小部分(相对)强的预测因子,但有许多弱的。从宏观经济的角度来看,在比较四个子集选择时,突出的是缺乏有利于社会和经济的政治变量:虽然 CDF、standard Lasso 和 random forest 都选择了一个政治变量,但没有一致性。有趣的是,即使是与暴力冲突相关的“革命和政变”,也只被标准套索选择,并且仍然只被分配最低的等级——这与非常成功的制度经济学文献相反。
结论
谢谢你坚持到最后。我希望这篇文章为您提供了一个将 ML 简单应用于宏观经济学的实例。当然,虽然我们的“数百万”数据集的简单例子不能保证任何因果解释——显而易见的下一步将是时间序列数据——但我们能够看到随机森林的变量选择对于经验宏观经济学的一些问题来说可能更加稳健。如果你能想到更多的宏观经济应用,或者曾经做过一些,我很想听听,所以请在下面分享。
保持安全&保持联系!
参考
- 樊军、吕军军(2009) 。高维特征空间变量选择的选择性综述(特邀综述文章)。中国统计,20 (1),第 101–148 页。
- (2010 年)。使用随机森林的变量选择。模式识别字母,31 (14)* ,第 2225–2236 页。【在线】。可在:https://doi:10.1016/j . patrec . 2010 . 03 . 014获取。*
- Genuer,r .,波吉,J.-M .和 Tuleau-Malot,C. (2015 年)。VSURF:一个使用随机森林进行变量选择的 R 包。《R 日报》,第 7 卷第 2 期,页 19–33。【在线】。可在:https://doi:10.32614/RJ-2015-018获取。
- Hastie,t .,Tibshirani,r .和 Tibshirani,R. (2017) 。最佳子集,向前逐步还是套索?基于广泛比较的分析和建议。统计科学,35 卷 4 期,第 579–592 页。【在线】。可在:https://doi:10.1214/19-STS 733获取。
- 詹姆斯,g .等人(2013) 。统计学习入门。斯普林格。
- 萨拉伊马丁,X. X. (1997) 。我刚刚做了两百万次回归分析。《美国经济评论》,87 卷 2 期,第 178–183 页。
ML 追求化学和材料科学——使用和开发模型的每个人都感兴趣的综述的要点
人工智能在生物学之外的科学领域的应用
科学家们刚刚为人工智能应用于化学和材料科学的未来制定了一个路线图,在一篇文章中,沿着人工智能的几个前沿追踪了艺术的状态

就像 ML 用于生物学一样,分子和材料的性质也可以用从数据库和量子计算中训练出来的 ML 模型来计算。作者的图片,由自己的照片和图画组成。
在过去的十年里,我们见证了机器学习(ML)和人工智能(AI)领域的一场革命,并且在过去的 5 年里,它对其他科学产生了前所未有的影响。虽然最著名的例子可能是 AlphaFold 及其对生物学的影响,但其他领域也感受到了这些新计算技术的潜力。特别是,计算化学和计算材料科学已经开始转变可能很快就会发生的类似于 AlphaFold 的范式转变,由此执行计算的传统方法正在被更快、更容易、通常更精确的方法所取代,这些方法源于 ML 模型的直接应用。
就在刚才,一组致力于将 ML 应用于材料科学和化学的科学家发表了一篇关于新兴子领域及其可能立即发展的评论/意见(或“路线图”)文章。文章讨论了在计算化学和材料科学中 ML 的几个正在进行和即将进行的应用中现存的和即将出现的困难的观点。
即使你使用的是 ML 模型,而不是化学和材料,你肯定会发现这篇文章和我的博客摘要很有趣,因为这篇文章探索了更广泛领域的许多方面。
五个主要标题,每个标题都讨论了几个要点
文章讨论了 5 个主要问题,包括更具体的要点,如创建更好更快的基于 ML 的力场,这些力场基于数据或复杂精确但计算缓慢的量子计算,计算密度泛函理论的交换相关泛函的新方法,多体问题的基于 ML 的解决方案,以及处理 ML 模型正确训练所需的大量数据的方法。
简而言之,这篇文章涵盖了这些要点,其中我强调了一两个要点:
1。预测材料特性
- 利用机器学习加速计算材料设计
在 AlphaFold 的成功推动下,我们现在正经历着将 ML 应用于蛋白质设计的热潮——现在这是一个热门话题,非常有前景(示例见)。
事实证明,ML 也可以用于设计新材料,正如文章开头所述。正如文章的第一部分所讨论的,这不仅仅是设计原子在空间中的位置,还包括由此产生的电子属性。
- 用于材料性能预测的机器学习
就像在 ML 产生影响的任何其他科学领域一样,它可以帮助预测比常规模型更好的属性,或者具有相同的准确性,但速度要快得多。
事实上,用于预测物理或化学性质的 ML 已经存在了几十年,尤其是使用相当简单的神经网络。举例来说,看看早期计算分子核磁共振化学位移的程序,或者其中记载的几个应用程序(顺便说一句,太棒了,我家里就有!)1999 年的书:
https://www.amazon.com/Neural-Networks-Chemistry-Drug-Design/dp/3527297790
当然,我们现在有更好的网络架构、激活功能和训练方法;同样重要的是,现在的数据库比 23 年前这本书出版时大了几个数量级,也更容易访问。所有这些方面都将在本文的其他部分讨论。
- 预测热力学稳定的物质
当然,材料科学的重要实际成果之一是发现和开发具有有用特性的新材料。一个非常理想的特性是耐久性,或者说是稳定性。科学家们对预测可以在实验室合成的稳定材料的成分和晶体结构感兴趣,ML 模型可以很好地帮助这一点。
- 材料性能和功能的学习规则
例如通过可解释的神经网络或通过进行符号回归的网络,如这里的应用于量子计算或更广泛地应用于科学中的。
关于可解释的 ML 模型的具体问题也将在文章的后面和这篇博客中提到。
- 光谱学深度学习
光谱学研究辐射与物质的相互作用,特别是如何从描述物质对辐射的影响的光谱中检索出有关物质的信息。获取这些信息,或者模拟交互,并不简单,ML 当然可以在这方面有所帮助。
- 无序系统的机器学习
无序材料是那些以极端的结构和化学无序为特征的材料。玻璃、塑料和无定形晶体是无序材料的例子,这当然是许多研究和开发的重要焦点。
2。精确力场的构建及超越
- 用于分子量子模拟的机器学习
在分子模拟中,力被传播为加速度和运动,以创建一个系统的原子可能如何一起移动的“电影”,从而赋予一片物质的属性,例如蛋白质的功能或材料的柔性。(要了解更多关于模拟的一般知识,请查看这篇文章的介绍。)
使用经典方程计算力场需要大量的参数和密集的计算,而神经网络可以极大地简化(和加速)。例如,参见这篇关于计算小分子的势能及其导数(即力)的“通用”网络的论文。
许多类似的网络已经存在,更多的网络正在出现。
- 微观交互的贝叶斯机器学习
贝叶斯 ML 允许自适应模型在模拟中更好地描述系统。由于可靠和有效的量子计算软件包的存在,微观数据可以大量产生,然后用于拟合灵活的原子间潜在模型,而不是宏观的可观测数据。这种想法并不新鲜,因为它已经被应用于非 ML 方法,但与 ML 方法一起,这种想法被推向了最大化,其中 ML 模型表现为非参数回归器,对交互的数学形式施加很少或没有约束,并直接依赖于实际数据。然后,贝叶斯技术以函数分布的形式强加先验,并使用数据和 ML 模型来提供预测。
- 来自机器学习的光谱精确势能面
如上所述,光谱学研究辐射与物质的相互作用,并且通常使用光谱学方法来检索关于一种物质的信息。从光谱中提取这种信息的主要方法之一是模拟它。在最基本的层面上,这需要求解薛定谔方程,但这很难从第一原理中实现。对于一些计算来说,这甚至更难,因为人们需要知道所谓的势能面,这是一个超维面,它量化了组成系统的原子和电子的不同配置(位置和状态)的势能。今天,一些 ML 方法可以辅助小分子的这些计算,目标是改进这些方法,使它们适用于更大的系统。
- 化学和材料科学中的高维神经网络势能面
根据上一点,计算势能的神经网络有许多更广泛的应用,例如快速(甚至可能交互地)测试分子和分子组的能量和构象。
- 可转移神经网络力场
如上所述,分子模拟是关于将力传播为加速度和运动,以创建系统原子可能如何一起移动的“电影”,从而为我们感兴趣的属性让位。量子计算已经成为计算有机化学的主力,是最精确的从头计算模拟,即不依赖于特定原子、键等的参数化。这些计算的问题在于它们极其昂贵,肯定比经典分子力学昂贵得多——代价是后者需要参数化。
原来产生势能的 ML 模型也能提供力,因为作用在原子上的力是能量面沿空间维度的梯度。有一个新兴的领域利用这一点来产生新的力场,其运行速度接近经典计算的速度,但几乎和量子计算一样精确。这里有一个最著名的例子:
https://pubs.rsc.org/en/content/articlelanding/2017/sc/c6sc05720a
- 集成机器学习模型:超越局部势的电子结构精度
随着方法的进步,考虑电子的原子尺度系统的建模越来越具有预测性。然而,可获得的模拟时间和长度比例受到昂贵的计算机要求和模拟中包括的电子数量的急剧缩放的限制。一个相对灵活的函数形式和少量的参考计算可以用来拟合结构-性质的相互作用使用 ML 方法。许多研究领域试图缩小电子结构计算能力和数据驱动能力之间的差距。他们遵循的主要策略是调整用作输入的原子特征,或者调整模型本身的数学结构,以反映问题的潜在物理特性和目标属性的特定结构。
3.用机器学习解决多体问题
- 统一机器学习和电子结构方法
上面评论的路线图的一部分是关于用 ML 预测势能面,然后直接用 ML 强制模拟其力学和/或化学性质。这些模型没有明确地模拟系统的电子,也就是说,它们没有考虑量子力学——但是显然 ML 有潜力在这方面帮助科学。
正如文章的这一部分所描述的,最近出现了一股将 ML 方法应用于量子化学的热潮:预测电子密度、分布、自旋、哈密顿量和波函数。如果 ML 对量子计算的影响与它对蛋白质结构预测或分子力学力场的影响相同,我们可以期待它在实际应用中的巨大进步。
- 利用机器学习寻找新的密度泛函
密度泛函是量子计算的一个重要部分,ML 现在正在影响他们的计算。我最近讨论了甚至谷歌和 Deepmind 都在处理的工作:
- 机器学习 Kohn–伪交换–相关电位
这正是上述文章所要解决的问题,因为使用 ML 可以大大加速这些计算。特别是,上面 Google 的文章利用了一种基于符号回归的有趣方法,这使得整个计算更容易解释,也更容易嵌入到其他软件包中。
- 针对分子的深度学习量子蒙特卡罗
如上所述,分子和材料的性质原则上可以用薛定谔方程来描述,但是这非常昂贵,并且需要近似。随着系统中电子数量的增加,主要的挑战是找到在精确度和计算成本之间取得良好平衡的近似值。我们之前已经看到,许多 ML 方法试图直接预测量子计算的结果,另一方面,还有旨在直接简化量子计算本身的从头计算 ML 方法。这一部分是关于后者的,包括像这样的例子,它通过在优化参数和从波函数取样之间交替产生数据来接近薛定谔方程的解:
- 无序量子系统
上面已经介绍过的无序系统也可能从量子计算的研究中受益,同样,通过 ML 方法加速无序系统也是有益的。
这些系统中缺乏规律性带来了额外的挑战,因为训练集很可能会错过可行的安排——只是它们太多了,无法解释所有的安排。尽管如此,还是有进步的;例如,该文章描述了在包括许多随机实现的数据集上训练的一些 ML 模型,然后可以为以前看不见的实例生成准确的预测。
4.机器学习的大数据
- 异构数据集合的互操作性和重用的挑战和前景
对于结构生物学,大数据分析和 ML 方法听说得更多,它们正越来越多地应用于化学和材料科学中的各种问题。即使是高通量筛选,通常与发现生物功能分子相关,也越来越多地用于在大规模数据集上发现化学物质和材料。
文章的这一部分讨论了 NOMAD(新材料发现)实验室,这是欧洲的一项努力,旨在为化学家和材料科学家的整个社区提供一个共享数据的开放平台。NOMAD 允许用户上传大多数量子计算程序产生的结果,托管研究人员贡献的和从其他数据库收集的超过 1 亿次计算:
这种资源显然用于为化学和材料科学训练新的 ML 模型,并且还用于比较不同方法的性能,发现数据中的趋势等。
文章进一步讨论了当处理如此大量的数据时,特别是当来自不同来源时,有限的互操作性,这些问题与再现性有内在联系,再现性不仅是实验科学的问题,也是计算科学的问题。
- 计算材料数据和设计的流程框架
本节讨论另一个名为 AFLOW 的大型数据存储库,它位于美国,与上面的不同之处在于它执行自己的计算。给定从实验观察到的材料生成的输入结构,或者从晶体原型生成的输入结构,然后用不同的元素装饰这些原型以生成相关假设材料的大型库,AFLOW 然后计算量子计算并存储结果以供以后检索。不同的流程子模块进一步计算各种属性,这些属性也会被存档。
就像 NOMAD 中的数据一样,AFLOW 可以训练 ML 模型,发现趋势等。目前它有超过 350 万个条目,每个条目有超过 200 个计算属性;所有数据都可以通过 API 以编程方式访问。
5.材料科学中机器学习的前沿进展
- 电子结构计算的自适应学习策略
自适应学习旨在实现通常与 ML 模型的训练相关联的巨大参数空间的快速和有效导航。难怪它也在化学和材料信息学中出现。
自适应学习的基础是使用一种算法来执行学习过程,该算法能够以一种最佳方式从广阔的未探索或未知区域中自主地选择数据点,这种最佳方式减少了收敛步骤的数量和/或所使用的训练点的数量,而不牺牲模型的预测和概括能力。当处理作为训练数据的量子计算时,自适应学习可以提供巨大的帮助,因为产生这样的数据是昂贵的。文章的这一部分讨论了在使用量子计算时实现精确适应学习的方法,特别强调了实现这一点的一些方法,以及最重要的是,如何准备输入数据。
- 强化学习
就像动物的行为可以通过反复应用奖励和惩罚来塑造一样,强化学习包括在奖励正确预测的环境中反复训练模型。环境因此“选择”最大化回报的 ML 模型。这种学习更适用于必须在后续步骤中实现目标的程序的调整,例如,你可能已经在训练必须学会走路的虚拟机器人的方法中看到过。
到目前为止,强化学习在物理学、化学和生物学中还没有太多的应用,但是本节的作者提出了一些潜在的应用。特别是,由于强化学习擅长控制动态,它可以应用于预测行为的 ML 模型,如温度变化、施加电场等的影响。
- 物理科学中机器学习模型的可解释性
文章的最后一部分处理了一个实际上是所有 ML 方法的核心问题:可解释性。经过训练的模型可以完美地重现训练数据并正确预测测试数据,它甚至可以正确地做出新的预测。但是,它真正了解了我们人类可以利用的潜在物理和化学知识吗?
这篇文章非常有趣的部分,我指的不仅仅是对科学家,而是对所有使用 ML 模型的人,解释了关于可解释性的文献非常多,但是还没有完全的共识。特别是,对于需要问哪些确切的基本问题,还没有明确的共识,更不用说可以测量的明确数量来推断模型学到了什么。文章接着解释了与可解释性相关的两点:透明性和可解释性。
透明性直接关系到这样一个事实,即在科学中,当一个预测性的数学定律被公式化时,一个现象被认为是完全可以理解的,至少在一个给定的应用领域中原则上可以毫无例外地工作,这就是 ML 通过符号回归可以提供最大帮助的地方。此外,这样的定律通常被认为是相对简单的,因此我们可以将它与基础物理或化学联系起来。我在本文中讨论了一些这样的例子:
另一个方面,可解释性,指的是至少检查一个模型的可能性,该模型通常太复杂而不能被人脑理解(作为一个“黑盒”)来研究,并理想地揭示输入和输出在其中是如何连接的,例如通过测试哪些输入在更大程度上影响输出。
理想情况下,科学家需要达到深度透明和/或可解释,以便像信任简单的分析模型一样真正信任 ML 模型。文章最后讨论了当前和未来的工作和挑战。因此,对每个从事 ML 模型工作的人来说,这又是非常有趣的。
参考资料和相关阅读
整篇文章中有 47 位作者暴露了他们的想法:
这里是我之前的两篇关于 ML 在量子力学计算中的应用的文章:
这里是我关于 AlphaFold 和 ML 应用于蛋白质结构预测和蛋白质设计的关键文章:
https://lucianosphere.medium.com/guide-to-my-blog-articles-on-alphafold-39c10a7dad7f
www.lucianoabriata.com我写作并拍摄我广泛兴趣范围内的一切事物:自然、科学、技术、编程等等。 成为媒介会员 访问其所有故事(我免费获得小额收入的平台的附属链接)和 订阅获取我的新故事 通过电子邮件 。到 咨询关于小职位 查看我的 服务页面这里 。你可以 这里联系我 。
不再有 ML 延迟
原文:https://towardsdatascience.com/ml-latency-no-more-9176c434067b
将最大似然预测延迟降低到亚 X 毫秒的常用方法

减少最大似然预测延迟的常用方法。作者图片
机器学习(ML)系统在部署之前是不存在的。
不幸的是,预测延迟是造成严重伤害的边缘之一。
而且,它在产品周期中伤害得太晚了。
停止优化那个模型!首先关注 ML 服务延迟。
首先关注 ML 服务延迟。
这是客户首先看到的。
那么有哪些减少 ML 延迟的常用方法呢?
这里有一种方法来组织低延迟 ML 服务的已知模式,我希望它有所帮助:

对抗 ML 预测延迟的架构模式。图片作者。(高分辨率示意图此处)
目录
- 一、并非罕见的 ML 潜伏期过长案例
- 二。在线与离线以及实时与批量
- —二. a .离线预测管道长什么样?
- — II.b .在线预测是延迟真正伤害的地方
- 三。异步在线预测
- —三. a 选项 1:推送
- —三. b 选项 2:投票
- IV 同步在线预测
- —四. a .标准 ML API
- —四、b 服务与构建预测
- 五、优化模型本身只能做到这么多
- — V.a .支持模型组件
- — V.b .核心模型组件
- 六。特性是整个操作中真正的阻力
- — VI.a .具有静态参考特征的“简易”外壳
- —六、b .具有动态实时特征的“不太容易”案例
- 七。不要忘记预测!
- —七. a .预计算预测
- — — VII.a.i 如果你能使用实体,那就用吧!…
- —VII.b .缓存预测
- — — VII.b.i 实时相似性匹配的非常特殊的情况
- — — VII.b.ii 记住,预测特征值的组合很快就会变得昂贵
I .并不罕见的 ML 潜伏期过长的情况
延迟是 ML 系统中一个非常普遍的问题。不要让这扼杀你的产品。如何最小化 ML 系统的预测服务延迟?
在开始你的下一个 ML 项目之前,这里有一些关键的问题要问你自己:
- 是否需要<100ms or offline?
- Do you know your approximate “optimizing” and “satisficing” metrics thresholds?
- Did you verify that your input features can be looked up in a low-read-latency DB?
- Could you find anything that can be precomputed and cached?
The image below presents several ways to answer these questions. The following sections then discuss specific applications of them.

Common ways to reduce ML prediction latency, “ranked”. Image by author
II. Online vs Offline and Real-time vs Batch

ML models come in two flavors:
- 离线预测: 当你需要对一整批数据条目进行评分,并且你在必须为预测服务之前有相当多的时间时,就使用这个。在这种情况下,您只根据历史数据生成预测。比如为我们认为很快会从我们的服务中流失的客户“线下”开展促销活动。这并不容易,但是在你需要返回预测之前,你还有一些时间。
- 在线预测: 这是用来动态生成预测的。随着新请求的到来,服务使用当前上下文+历史信息来生成预测。也就是说,上下文是一个宽泛的术语。可以将它看作是当前日期+时间、会话中查看的最后 N 个项目、新购物篮的内容、用户设备类型/位置以及历史数据仓库中没有整齐组织的任何其他有用信息的组合。
II.a .离线预测管道长什么样?
在离线用例中,你不会一次得到一个数据点。相反,您可以在合适的存储位置收集许多数据点,并立即为所有目标数据点生成预测。这通常采用预定批处理作业的形式,其频率与业务需求相匹配。
受益于这种操作模式的一些使用案例:
- 你需要优化商店库存。您的预测作业在早上 6:45 运行。陀螺餐厅会卖多少鸡肉,这样你就可以去当地的山姆会员店挑选一些了。
- 你需要通过建立每周一次的客户细分来了解受众,围绕有益的特征对用户进行分组,从而找出谁是你最好的客户。是的,团体。
- 你需要确定你的顾客是否满意。因为人们谈论你的三明治,它们变得越来越油腻。所以你进入社交媒体寻找答案。
生成离线批量预测的一种方法是使用“标准”ETL 管道,该管道包含生成预测的智能。
看下一张图:

“标准”离线评分 ETL 管道。作者图片
上图是这样的:
- 将要评分的数据上传到您的存储器。
- 将数据预处理成模型可以使用的东西。
- 对预处理后的数据进行评分。
- 将分数存储在最终用户可以访问的地方。
对于上面概述的这种处理,没有人关心它需要 1 小时还是 10 小时。然而,由于在线预测是受延迟影响最大的预测,让我们更深入地研究一下。
II.b. 在线预测 潜伏才是真正伤人的地方

作者图片
对于在线预测,呼叫者向我们发送一个数据点进行评分。打电话的人预计预测会在 10 毫秒内返回。以下是一些有延迟要求的使用案例:
- 当浏览器加载页面时,为广告请求生成广告推荐。
- 在竞争性实时竞价广告市场中优化竞价。
- 预测关键设备是否会在接下来的几秒钟内发生故障(基于传感器数据)。
- 基于订单的大小、当前交通状况和订单的其他上下文信息来预测杂货交付时间。
三。异步在线预测
异步预测表示调用者将请求预测,但生成的预测将在以后交付的场景。调用者不必阻塞来等待预测返回。

作者图片
执行异步在线预测的两种主要方法:
三. a .方案一:推。调用者发送生成预测所需的数据,但不等待响应。例如,当使用您的信用卡时,您不希望每次交易都等待欺诈检查响应。通常情况下,如果银行发现欺诈交易,他们会向你发送一条信息。
例如,在一个客户流失预测系统中,ML 系统将试图留住那些容易退出我们付费送餐订阅服务的用户:
- 来电者:“嘿,我有这个登录用户。她目前很活跃,但正在修改她的订阅设置。我们是否应该在她退出我们令人惊叹的服务之前,向她发送一封新的挽留促销电子邮件?”
- 模特:“让我想想。如果需要,我会向您发送推送通知。”
- …时间…流逝…
- 模型:“嗨,打电话的人,把这封关于 3 顿免费餐的促销邮件发给顾客,这样她就可以继续订阅我们的服务了。”
三. b .备选方案 2:投票。调用者发送所需数据,然后定期检查预测是否可用。这些模型用于生成预测,并将预测存储在读取优化的低延迟数据库中。
- 来电者:“嘿,我有这个登录用户。她目前很活跃,但正在修改她的订阅设置。我们是否应该在她退出我们令人惊叹的服务之前,向她发送一封新的挽留促销电子邮件?”
- 模特:“让我想想。我将更新预测数据库中的预测。”
- 打电话者:“有什么消息吗?”
- 打电话者:“有什么消息吗?”
- 打电话者:“有什么消息吗?”
- 致电者:“啊,好的,我认为我们应该向此人发送一封挽留促销电子邮件。谢谢。”
对于异步在线预测,典型的数据流如下所示:

使用事件消息传递和流处理生成异步预测的管道模式。作者图片
在异步情况下,消息传递系统帮助编排流程,如下所示:
- 客户端与外观预测 API 对话。
- 预测 API 服务将可用数据发送到输入消息传递系统。
- 然后,事件处理服务会清理并动态丰富该事件。则预测请求被转发给评分服务。
- 事件处理系统接收预测。在对预测进行格式化、过滤和后处理之后,它会将预测写入一个输出评分事件消息系统。
- 然后,要么预测 API 继续轮询以获取分数,要么有一种方法将通知推送到预测 API。
四。同步在线预测
标准 ML API
同步在线预测比异步模式对延迟更敏感。同步在线预测的基本模式是:

使用 ML 网关生成同步预测的服务模式。作者图片
- 该模型被部署为一个 HTTP REST API。
- 在线应用程序向模型发送一个 HTTP 请求,然后阻塞,等待预测“立即”返回(例如,<10ms).
- The model starts generating the prediction and sends it back to the caller as soon as the prediction is available.
- If the model does not respond within the latency budget (it takes too long), the caller times out, and the online application says something like “prediction took too long, try again later”.
But that’s not all of course. The ML service has to do more tasks.
Namely, generate the prediction, preprocess and enrich the input request, and post-process the output prediction before giving it back to the caller. Even all that would still be manageable with a standard ML gateway that would orchestrate the whole thing.
However, because the ML gateway can be hit by spikes in traffic, you must also do the following tasks. The following becomes part of your job:
- Securing the endpoint
- Load balancing the prediction traffic
- Auto-scaling the number of ML gateways
Have fun.
IV.b. Serving vs Constructing Predictions

Image by author
We expect synchronous online predictions to return immediately. However, it is essential to realize that reducing latency relies on optimizing two distinct levels:
- 预测构建 -这是减少模型从一个完全形成的、行为良好的、丰富的和经过处理的预测请求构建预测所需时间的地方。
- 预测服务 -这是其余延迟的所在。这包括输入预测事件的任何预计算、预处理、丰富、消息传递以及输出预测的任何后处理、缓存和优化传递。
在预测构建级别,优化侧重于以最少的额外成本构建更小的模型,并选择适当的硬件以在正确的价格/延迟点生成预测。
在预测服务级别,重点是在足够快的数据存储中构建支持的历史数据集,并计算实时上下文动态特征。这通常是在痛苦地意识到减少预测构造的等待时间不会移动指针之后发生的,因为在提供全功能预测服务之前和之后涉及十个其他步骤。
最后,正如我们将要讨论的,如果您正在预先计算组合特征的预测并缓存它们以供在线服务,那么将模型预测延迟减少 50%将不如将特征和预测提取延迟减少 50%有价值。
动词 (verb 的缩写)优化模型本身只能做这么多

作者图片
支持模型组件
减少模型级延迟的第一步是移除任何额外的模型 cruft。在模型开发、实验和调优期间,通常会添加许多支持组件,如日志记录、挂钩、多头、监控、集成和管道转换器代码路径,以帮助调试模型。该工具在模型训练、评估和调试过程中很有帮助,但是会意外地增加核心模型的复杂性。不言而喻,删除这些不会损害模型的性能,但会改善预测的延迟。
核心模型组件
下一步是查看模型的核心组件,并决定需要删除哪些组件。但是在这个过程中有什么可以指导你呢?关键是要理解模型的优化指标与满意度指标之间的权衡。
我们通常关心开发阶段的优化指标:模型的预测能力。那是你通常的地图,MRR,准确度,精度,MSE,对数损失等..在每个指标对应的方向上,每个新高或新低都是线下世界的胜利。
然而,优化指标必须与满意指标相平衡。满意度指标关心模型运行的环境。例如:
- 就存储大小而言,该型号是否适合我的设备?
- 该型号可以在设备上运行该类型的 CPU 吗?需要 GPU 吗?
- 特征预处理能在特定的时间范围内完成吗?
- 模型预测是否满足我们的用例所要求的延迟限制?
这里的想法是为满足性度量选择一个上限,比如 50 毫秒的延迟,并使用它来过滤掉需要更多时间的模型。
接下来是你开始摆弄模型的部分。主要指针是:
- 模型越小,响应时间越快。
- 输入特征的数量越少,响应时间越快。
要减小模型的大小,有几个选项可用:
- 修剪树模型中的层数
- 修剪随机森林中的树木数量和梯度增强树模型
- 修剪神经网络中的层数
- 削减逻辑回归模型中的变量数量
您在这里的任务是平衡预测有效性和延迟需求。以下是如何做到这一点的一些指导:
1.设置满意度度量阈值。
2.增加模型的复杂性,直到达到满意的度量界限。
3.如果模型延迟满足要求,但预测有效性低于优化指标要求,那么问问自己您的应用程序是否可以接受这一点。
4.如果预测性能不可接受,那么要么尝试更轻的模型类型,要么重新评估应用程序的优化指标要求。
更令人困惑的是,还需要评估两个计算维度:
- 尽量使用定制硬件,比如 GPU 或者特定的推理芯片。
- 尝试使用自定义编译方法来优化模型组件。
但是,优化模型只能到此为止,因为迟早您会意识到,模型构建延迟方面 10%的改进将被用于预处理输入要素、后处理预测和交付预测的分布式 I/O 操作所摧毁。例如,如果您的总延迟来自预测构造的 10 ms,来自预测服务的 90ms,那么在 10ms 上节省 10%是没有帮助的,因为在预测服务中您仍然有一个巨大的 90ms 要处理。
减少预测服务延迟是我们接下来要讨论的内容。
不及物动词特征是整个操作中真正的阻力

作者图片
功能是模型的生命线。但不幸的是,调用者可能不会向您发送一个完整的请求。取而代之的是,他们会发送当时任何可用的信息。这和你线下用来训练模型的相差甚远。与模型的预期相差甚远。
例如,对于杂货交付估计,模型将只接收 order_id。但是模型可能需要的不仅仅是 order_id。它需要获取关于订单、客户和送货人的信息。此外,它可能需要送货地址周围的当前交通状况,以及与该邮政编码中以前的送货时间、当前商店中以前的购物持续时间等相关的一系列历史值。因为所有这些都包含在模型中,所以获取和处理要素成为一项高风险的操作,大部分预测服务延迟将来自于此。
作为考虑特性的心理模型,我们可以将输入特性分成三个阵营:
- 用户提供的特性:这些特性直接来自请求。
- 静态参考特征:这些是不经常更新的值。
- 动态实时特征:这些值将来自其他数据流。当新的上下文数据到达时,它们被处理并持续可用。
VI.a .具有静态 Ref 特性的“简单”案例

作者图片
静态特性有两种类型:单个和集合。单个是单个实体的属性。房子中房间的数量,或者与活动相关联的广告商的 ID。总量是指邮政编码中的房价中值或针对特定受众群的广告活动的平均预算。
这些静态特性有助于预测用例,例如:
- 根据邮政编码、里程、车型年、车型类型和该车型/年份组合的中间价格预测二手车的最终销售价格。
- 推荐与用户以前看过的电影相似的电影。
- 基于先前的购买和人口统计信息对向用户显示哪些广告创意进行排名。
问题是原始静态特性最初存在于企业数据仓库中。在预测时,ML 网关将需要检索特征并创建符合 ML 模型需求的请求。不幸的是,典型的数据仓库并没有针对低延迟查询进行优化。相反,数据仓库针对大量星型模式上的大型聚合、连接和过滤进行了优化。这不适合低延迟应用。
静态特性的 ML gateway 获取模式是:“对于客户 x 的每个特性,我需要一行一列。”
低延迟静态特性的标准方法是定期提取特性和集合,并将它们放在针对单例查找操作优化的数据存储中。

使用 ML 网关、要素查找数据库和离线批处理作业处理静态要素的标准服务模式。图片作者。
这两股平行的水流是这样的:
离线:
批处理作业执行以下操作:
- 从数据仓库中读取。
- 生成单个和聚合静态要素。
- 加载特征查找 API/DB 中的特征。
上线:
- 客户端发送需要预测的实体 ID。比如给 user_id="x "推荐一个电影列表。
- 该实体通过特征查找 API 中存在的属性来丰富/水合。
- ML 网关然后将输入特征合并到转发给 ML 模型 API 的预测请求中。
- 当 ML 模型 API 返回预测时,ML 网关对它们进行后处理,并将它们返回给客户端。
静态特性的好处是它们…..嗯…..静态;而不是实时变化的。
标准模式是建立一个批处理作业来更新静态要素。不幸的是,如果每隔 15 分钟运行一次,那么这个批处理作业就要花费相当多的钱。因此,您成倍地降低了更新的频率,直到模型的优化指标开始抱怨。然后你把频率提高到之前的值。实现自动化。完成了。
我们需要一张照片!我们有一张照片…

使用 ML 网关、要素查找数据库和离线批处理作业处理静态要素的标准服务模式。图片作者。
下面是上面数据流中发生的情况:
- 从你的数据中获取数据。使用能够处理仓库级查询的东西。
- 处理数据以完成所有常见的争论(连接、过滤、聚合一些数字,等等)。
- 运行特征工程(这是你的工作):提取你的模型需要的最便宜的特征。继续“去除”昂贵的特性,直到优化指标低于您的阈值。然后自动化。
- 存储您要预测的每个实体。希望是查找时间最短的地方。选择一个能够提供最佳查找延迟的数据库,但是一旦模型预测延迟性能表现良好,就停止优化数据库。
现在您已经优化了上述内容,您可以在您的组织中扩展要素生成。例如,假设您创建的特性可以在一个单独的用例中被随机重用。与其让每个 ML 管道重新生成相同的特征并浪费时间和金钱,不如构建一个具有以下特征的特征库:
- 企业范围的
- 集中的
- 发现的
这是你要做的。这个计划完美无缺。将特征生成工作流分为两部分:
- 产生 ML 特征(给予者)
- 发现 ML 特性(接受者)
生成要素在概念上类似于上图。
为了发现,ML 团队中的每个人都需要关于客户、产品和渠道的信息。从那里开始。首先是单打,然后是总数。看烟火。
我认为发现比产生特征更复杂。功能越复杂,就越难重用。客户的最小、最大和平均年龄比产品图像嵌入或目录分类法更容易共享。
首先关注 ML 服务延迟。
这是客户首先看到的。
六. b .具有动态实时特征的"不太容易"的情况

这个选项的“动态”本质来自于使用事件。

实时功能和输出预测管理。
这什么时候会发生?当您希望将最近的事件用作模型中的要素时。例如,想象以下任何一种情况:
- 您正在使用浏览器会话中的用户交互作为功能,客户与“N”个项目进行了交互,您想要推荐下一个要观看的项目。
- 你预测你工厂的哪个“饼干制造机”会因为黄油的温度不正确而失败。
- 你预测食品杂货的送货时间,这样你就可以告诉顾客什么时候在附近,这样他们的东西就不会被偷。
这些实时功能通过事件流处理管道传递。与批处理的情况相比,您需要找到一种方法,一旦传入的数据可用,就立即更新现有的聚合值。例如,包含 1+$1+$1 的购物篮不同于包含 1+$1+$1000+$1 商品的购物篮。
为此,您需要一个流管道来做两件事:
- 动态生成要素。管道任务在一端接收事件,并尽快生成所需的统计数据。
- 动态生成预测。管道任务获取新特征并调用部署的模型来生成预测。
建议将生成的要素放在读写性能良好的低延迟数据库中。输出预测应落在两个位置:
- 输出预测数据库
- 输出预测流

实时功能和输出预测管理与步骤。图片作者。
所以总结一下:
- 新鲜事件登陆你最喜欢的信息系统。然后,它们被流式管道拾取。生成的要素(可能在时间窗口内聚合)位于低延迟要素存储中。使用新值更新现有要素。
- 流式管道使用特征和模型 API 生成预测。
- m1 网关接收客户端预测请求。然后,网关检查数据库或消息系统中是否有任何预测。然后网关将它们返回给客户端。最后,如果下游的其他系统感兴趣,它可以选择将它们推送到消息传递系统。看看你的治理团队。
七。不要忘记预测!

作者图片
如果到目前为止,这篇文章中的所有技术仍然不能使你的预测延迟足够低,那么你需要的下一个优化是预计算和缓存预测。
您设置了一个批处理评分作业,将预测存储在一个低读取延迟的数据库中。最完美的记忆。则客户端不必调用任何实时预测服务。客户端直接从数据库中提取预先生成的预测。
这里有一个图表来直观显示预计算过程:

用步骤预先计算和缓存输出预测。图片作者。
上图中概述的要完成的任务如下:
- 获取要评分的数据。
- 在批处理作业中离线评分。
- 将预测存储在专门存储键值记录的数据库中。
- ML 网关获得预测请求,然后获取并返回预测。
- 客户得到预测并继续前进。

作者图片
您可能会问:“这很好,但是查找键怎么办?那里应该用什么?”
让我们把问题分成两种类型:
- 对一个 实体 的预测
- 特征值组合的预测
对于实体的情况,预测服务接收一个已知的实体 ID。这个 ID 将在您的用例中代表一个域实体。这可能是产品标识、电影标识、订单标识、设备标识等。例如,预测下一个显示给刚刚加载了我们的产品页面的 user_id 的广告。这将是实体层面的预测。
对于特征值的组合情况****,预测服务接收特征值的组合。例如,假设你正试图预测用户是否会购买某样东西,而我们想展示一个促销来推动他们。然而,我们可能只能得到位置、当前购物车大小、细分信息和产品类别的组合。对于这种情况,您可能希望选择前 N 个最常见的要素组合,并离线生成这些组合的预测。然后,当有一个客户端请求时,您可以首先检查是否可以为该组合特性获取一个预测。
七. a .预计算预测
VI.a.i 如果可以用实体,就用吧!…
小心预计算预测!实体基数是这里的杀手。
如果您正在为低基数实体生成预测,那么您的情况会很好。例如,预测一个工业车队中数百辆汽车的维护需求可能不会倾家荡产。
但是,如果您正在为高基数实体生成预测,那么祝您好运。例如,如果产品目录是一个 1 亿件商品的怪物,你就需要降低期望值,使用一些技巧。最受欢迎的一种方法是为前 N 名最受关注的产品生成预测。然后,对于剩余产品的剩余长尾,您让客户端等待您直接调用模型,而不是从预测存储中提取预测。
以下是该工作流程的可视化表示:

使用实体预计算和缓存预测。图片作者。
VI.b .缓存预测

作者图片
实时相似性匹配的非常特殊的情况
假设你有太多的实体。您尝试了直接预测、完全预计算预测或部分预计算预测,但仍然没有效果。在这种情况下,相似性匹配值得一试。
类似下面的内容将改善预测延迟:
- 使用产品-用户交互或产品-产品协同定位来训练产品相似性的模型。
- 提取产品的嵌入。
- 使用近似最近邻方法建立嵌入的索引。
- 在 ML 预测服务中加载索引。
- 在预测时使用索引来检索相似的产品 id。
- 定期更新索引以保持内容的新鲜和相关性。
"但是如果索引太大,或者预测延迟太高,我该怎么办呢?"减少嵌入大小以获得更小的索引,直到优化指标开始抱怨。如果你不能得到一个可接受的优化+满意的权衡,那就去别处看看。
VII.b.ii 记住,预测特征值的组合会很快变得昂贵
如果实体对于您的用例不可用,因为人们通常喜欢他们的隐私,尝试使用特征值组合。您将需要一个静态散列方法来为每个值的组合生成一个密钥。
比如说你有三个特征:国家,性别,歌曲 _ 类别。然后生成一个 hash (county,gender,song_category)作为关键字。顺序在这里很重要:一个散列(国家,性别,歌曲类别)将不同于一个散列(歌曲类别,国家,性别)。所以选择一个特定的顺序,并坚持下去。
小心基数。类别越多,生成的预测数量就越多。如果你服务 10 个国家,2 种性别,40 个歌曲类别,那就意味着你要做 10x2x40=800 个预测。记住这一点。
在决定了键之后,您需要预先计算每个键的预测。将每个键值存储在一个低读取延迟的数据库中,然后就可以开始了。同样,请记住,即使有了可靠的键值存储,您仍然需要通过减少可能的键的数量来减少存储的预测的数量。在这里也使用优化与满意度度量方法。在模型的预测性能提高的同时,不断添加类别、功能和键。但是当预测延迟开始抱怨时就停止了。
要记住四件事:
- 数据库将有许多行,但只有几列。选择一个能够很好地处理单键查找的数据库。
- 留意类别的基数和生成的键的数量。如果您有一个批处理作业在做这件事,那么监视基数,如果您在要计数的新类别中得到一个峰值,就发出警报。这将防止增加数据库查找延迟。
- 连续值需要分桶存储。这将是一个你需要调整的超参数。
- 任何可以用来降低类别基数的技术都是你的朋友。在优化指标允许的情况下,尽可能降低基数。
总结
就是这样,伙计们!多棒的旅行啊!
理解在低延迟实时在线 ML 推理产品上工作时可用的选项具有优势:
- 首先,当你和你的产品经理聊天时,你要听起来很聪明。
- 然后,您可以通过探索 ML 延迟优化的正确类型来节省自己的时间。
- 当你的工程团队说你的模型生产时间太长时,你也不要失去希望。
- 最后,你的产品可能会成功。谁知道呢,对吧?
以下是模式图,供将来参考:

对抗 ML 预测延迟的架构模式。图片作者。(高分辨率示意图此处)
这是一张探索地图,供将来参考:

减少 ML 预测延迟的常用方法,“排名”。作者图片
我希望你对 ML 潜伏期有所了解。我确实做了:)
最后一件事!
所有这些复杂性可能会使您的 ML 代码更难维护和发展。因此,请查看我的最新课程,该课程围绕这篇博文的主题,包含代码示例、深入讨论以及更多有助于解决所有这些问题的内容!
作者图片
参考
- https://cloud . Google . com/architecture/minimizing-predictive-serving-latency-in-machine-learning # handling _ static _ reference _ features
- https://developers . Google . com/machine-learning/guides/rules-of-ml/
- https://cloud . Google . com/architecture/building-real-time-embedding-similarity-matching-system
- 《ML 特色店:一次休闲之旅》; 第一部; 第二部分; 第三部分;
ML 模型部署策略
原文:https://towardsdatascience.com/ml-model-deployment-strategies-72044b3c1410

让我们制定战略(图片由作者提供)
ML 工程师部署策略图解指南
你好。
这篇文章是为那些想了解 ML 模型在生产中是如何部署的,以及在部署这些模型时可以使用什么策略的人准备的。
我将举例说明部署 ML 模型的一般方法,部署时可以采用的不同策略,以及这些策略通常在哪里实现。每个数据科学团队都有不同的需求,所以要有所保留。
我们开始吧。
了解 ML 模型部署
与软件或应用程序部署不同,模型部署是一种不同的野兽。一个简单的 ML 模型生命周期有几个阶段,如范围界定、数据收集、数据工程、模型训练、模型验证、部署和监控。

ML 生命周期(图片由作者提供)
当我们部署 ML 模型时,我们需要考虑一些因素,如:
1。模型尺寸和包装— 模型尺寸在我们计划如何包装中扮演着重要角色。较小的模型通常可以包装在 FastAPI 服务器中,并装入 Docker 容器中。然而,较大的模型可能需要在部署期间加载——它们可能从远程存储中取出,并通过模型服务器(如 TFServing 或 TorchServer)运行。
2。模型再培训和版本化— 您再培训模型的频率会影响您的部署策略。你经常需要比较你的模特表演吗?您在生产中多久更新一次模型?您会在生产中维护不同版本的模型吗?
3。流量和请求路由— 根据流量和模型类型,您必须决定是实时推理还是批量模型部署。您希望每个版本的模型分流多少流量?有多少用户可以访问特定的模型版本?
4。数据和概念漂移— 在一段时间内,真实世界的数据不断变化,可能不会反映在模型中。比如说,购买力与工资的关系,工资可能每年或每月都在变化。或者消费者购买模式在新冠肺炎疫情期间如何变化,但模型大多依赖于历史数据。这影响了我们必须如何设计部署架构:我们应该重新培训和重新部署吗?我们现在应该只重新培训和展示模型吗?这个因素在数据科学团队的长期部署战略中发挥作用。
记住这些因素,我们有大约六种模型部署的通用策略。这些方法大多借鉴自 DevOps 和 UX 的方法,在 ML 场景中非常适用。
通常,在技术层面上在生产中部署模型涉及 API 端点网关、负载平衡器、虚拟机集群、服务层、某种形式的持久数据存储和模型本身。

通用模型部署(按作者列出的图像)
部署策略通常在负载平衡器和服务级别进行配置,主要是配置路由和接收规则。
让我们以动物识别和分类系统为例。 我们从一个简单的猫-狗分类器开始。这将是我们的第一版模型。 假设我们已经训练了一个模型的副本来识别考拉,那么 我们的版本 2 是一个猫-狗-考拉分类器 。我们将如何部署我们模型的新版本?

型号版本(按作者列出的图像)
模型部署策略
大爆炸——重现
什么— 这种部署形式是一种“从头开始”的部署方式。您必须为要部署的新部署拆除现有部署。
WHERE — 在开发环境中通常是可以接受的—您可以使用不同的配置根据需要多次重新创建部署。通常,部署管道会分解现有资源,并在其位置创建新的更新。

重新创建部署(按作者创建映像)
这种部署会导致一定程度的停机。在我们目前的组织 ML 开发速度下,这是不可接受的。
在我们的例子中,我们将用版本 2 替换版本 1;这包括替换所有相关的基础设施和库设置。
滚动更新
他们看到我滚…他们讨厌…🎵
什么— 滚动更新包括逐个更新模型/应用程序的所有实例。假设你有 4 个吊舱的应用程序目前正在运行,你用一个新版本的模型激活了一个滚动更新。旧的豆荚一个接一个地被新的豆荚取代。这种方法没有停机时间。
WHERE — 当您希望用新版本快速更新整个产品线时,此选项非常有用。还允许您在需要时回滚到旧版本。主要用于团队需要测试新版本模型的测试或准备环境。

滚动更新(按作者列出图像)
它通常不会是生产系统中的唯一实现,除非您只需要全面部署模型的特定版本。在我们的示例中,我们将只替换模型应用程序窗格,保持基础架构的其余部分不变。
🔵Blue/Green🟢
什么— 这种部署形式本质上是一种服务器交换。有两个相同的系统可用。用户请求被路由到其中一个系统,较新的更改和更新在另一个系统上完成。一旦更新经过测试和验证,用户的请求就会被发送到新的系统,从本质上来说,就是用新的系统替换旧的系统。
其中— 多用于应用或 web app 场景。也可以应用于模型部署,包括批处理和实时推理部署。基本上没有停机时间,因为我们只是将负载平衡器指向一组不同的机器。

蓝绿色部署(图片由作者提供)
正如您在这里看到的,我们使用模型的更新版本创建了一个新的相同系统,然后将流量切换到新系统。
但是,我们必须考虑维护两个相同基础设施系统的成本。根据基础设施的规模和承受能力选择这种方法。
金丝雀🐦
什么— 在 Canary 部署中,我们将更新部署到现有系统,并向用户部分展示新版本。这意味着我们的一小部分用户将能够访问更新的模型,其余的将仍然使用旧版本。
这种类型的部署主要用于测试新版本的工作情况,或者是否工作正常。通常,一小部分用户(大约 5%-30%)会接触到更新,因为这有助于 ML 工程师和开发人员理解哪些特性可能需要推出,哪些需要重构。
其中— Canary 部署通常在试运行和生产阶段进行,此时团队需要了解新更新型号的性能。这可以通过两种方式实现:
- 金丝雀滚动部署
- 金丝雀并行部署

金丝雀部署(图片由作者提供)
滚动部署 补丁更新到同一集群内的少量实例,一组用户请求被发送到这些 pod。
并行部署 在现有设置的基础上创建一个较小的新实例集,并将一定比例的用户请求发送到这些 pod。
根据流量负载和基础设施可用性,您可以选择希望设置哪个 Canary 实现。
用户请求通常通过标头进行标记,然后负载平衡器设置被配置为将它们发送到适当的目的地。含义 选择一组用户 查看更新, 同一组用户每次都会看到更新。用户请求不会被随机发送到新的 pod。Canary 部署具有会话关联性。
在我们的示例中,假设 10%的精选用户可以将他们的图像提交给模型,模型会使用考拉选项对他们进行分类,其余的用户只能使用二元分类器。
A/B 测试
WHAT — 这种方法最常用于用户体验研究,用来评估用户的偏好。在 ML 场景中,我们可以使用这种部署风格来了解用户喜欢什么,以及哪种模型可能更适合他们。
其中— 主要用于全球推荐系统的部署。根据人口统计数据,假设一个在线市场网站使用两种不同类型的推荐引擎,一种可能服务于一组普通用户,另一种服务于特定的地理位置——提供更多的本地语言支持。工程师可以在一段时间后确定哪个引擎为用户提供更流畅的体验。

为什么我们需要 A/B 测试(图片由作者提供)
在我们的例子中,假设我们在全球部署了猫狗分类器,但是我们在澳大利亚—太平洋岛屿地区部署了版本 1 和版本 2,在那里用户请求被随机发送到版本 2。想象一下重新训练版本 2 来识别更多的本地动物品种并部署它,你认为澳大利亚的人民会更喜欢哪个版本?
**NOTE:**
You might wonder what's the **difference between Canary and A/B Testing**. The main differences are:- Canary is session affinity-based, mostly the same set of users will see the updated model whereas, in A/B Testing, the users are randomly sent to different versions.- Canary is specifically to test if the app or model is working as expected, A/B is more to understanding user experience.- The Canary user ratio never goes beyond 50, a very small percentage of the user request (less than 35% ideally) is sent to the newer testing version.
阴影👻
什么— 影子部署在生产系统中使用,用生产数据测试新版本的模型。用户请求的副本被制作并发送到您的更新模型,但是现有系统给出了响应。
其中— 假设您有一个高流量的生产系统,为了验证更新后的模型如何处理生产数据和流量负载,我们以影子模式部署更新后的模型。每次向模型发送请求时,都会向更新的版本发送一个副本。响应仅由现有模型发回,而不是更新后的模型。

影子部署(图片由作者提供)
这种类型的部署用于了解生产负载、流量和模型性能。主要用于大量预测服务。这种部署增加了体系结构和设计的复杂性。我们必须包括服务网格、请求路由和基于用例的复杂数据库设计。
在我们的示例中,我们可能将版本 2 部署为影子部署,以了解版本 2 如何处理生产负载,也了解我们从哪里获得更多的考拉分类请求😉或者任何特定类型的模型请求模式。
现在,我们对如何在各种用例中部署模型有了很好的基本理解。根据需求,数据科学团队可能会结合使用这些方法。各有所好。
目前就这些。保重。再见!😇
ML &神经科学:2022 年 2 月必读
原文:https://towardsdatascience.com/ml-neuroscience-february-2022-must-reads-72c7603d98d2
本月:增强脑-机接口的可变自动编码器模型🧠💻人类功能磁共振成像扫描感知图像重建🧱and 容错神经网络❌.

图片由莫在的 Unsplash
通过我的推荐链接加入 Medium 来支持我的写作和项目:
https://stefanobosisio1.medium.com/membership
我最近关于 ML 和神经科学的报道:
- 神经科学:2022 年 1 月必读书目
- 与神经科学保持同步:2021 年 11 月必读文章
- 与神经科学保持同步:2021 年 10 月必读文章
更多的故事可以在我的简介中找到
为什么要关心神经科学?
神经科学是当今人工智能🧠的根源🤖。阅读并意识到神经科学中的进化和新见解不仅会让你成为一个更好的“人工智能”的家伙😎而且还是一个更好的神经网络体系结构的创造者👩💻!
在这篇文章中,我正式用一种新的方法重新发布我的“与神经科学保持同步”文章。在这个系列中,我将涵盖 3 篇主要论文,在 arxiv.org的评论中,它们涉及机器学习和神经科学。特别是,我将涉及以下几个方面:
- ML 研究能否帮助神经科学更深入地了解大脑的动力学和活动?
- 神经科学如何用新的生物启发模型帮助增强 ML?
- ML 和模型如何通过新的成像和信号技术给我们带来新的临床神经科学?
本月的三篇论文致力于:
- ML 如何帮助神经科学?第一篇论文展示了变分自动编码器如何帮助神经科学家更准确地理解神经潜在空间和真实行为。这增强了未来的脑机接口场景;
- ML 如何帮助理论神经科学?第二篇论文实现了一种新的 GAN 模型 IC-GAN,以从 fMRI 扫描中检索感知图像并定义视觉皮层神经区域携带什么信息;
- 在神经科学和计算机科学的边界,是否有可能从大脑中的生物误差补偿中获得灵感,并将其应用于人工神经网络?
通过无监督域适应的行为的神经群体活动的跨会话记录的鲁棒对准
贾斯汀·杰德,马修·g·佩里希,李·e·米勒,马蒂亚斯·h·亨宁, 论文
这篇论文是一个例子,说明 ML 如何帮助神经科学达到一个全新的水平。来自爱丁堡大学(❤)、西奈山伊坎医学院和范伯格医学院的作者开发了一种新的无监督 ML 模型,可以帮助解码神经群体记录,从潜在动态中推断行为。这项工作与脑机接口(BCIs)密切相关,这是一项通过解码神经记录中的行为信息来帮助严重残疾患者的新技术。
从文献来看,众所周知,可以将神经群体转化为行为的模型非常不稳定。首先,模型训练需要数天时间,涉及不同的神经活动,以及大量不可避免的神经漂移过程(例如,想想神经元动力学如何在一周的时间尺度内发生变化)。因此,作者决定从初级运动皮层开始分解这个问题。在这里,神经元活动可能与低维流形有关。这种流形可以完全被 ML 模型捕获。从那里有可能设计一个完整的系统,它可以用来自 ML 模型的潜在动力学洞察来补偿神经记录,以返回运动皮层神经元如何工作的完整图像。
该模型是一种变分自动编码,结合 seq2seq 编解码器(如乔 RNN 编解码器)——我这里说的就是这个模型。一方面,编码器从输入数据中学习不变的潜在动态。输入数据来自不同的记录会话,以便考虑神经可变性。因此,解码器同时迫使编码器生成不同行为的潜在表示。然后,解码器返回对结果行为的预测。最终的模型被称为通过棘波不变潜在编码的行为稳定排列(SABLE)。已经对一只猴子连续训练了 13 天,对另一只猴子连续训练了 6 天。
Gallego 等人在 2020 年发表了一篇关于从看不见的神经活动中预测行为的文章,对 SABLE 进行了测试。在这个实验中,两只猴子被训练完成一个向外伸向八个外部目标的任务。


图 1:左边是实验装置。一个随机选择的目标方向,用一个彩色圆圈表示,出现在屏幕上。一只猴子被指示在出现的方向上移动光标。该模型在每个时间步从神经尖峰预测手的位置。右边是神经潜在空间表示,通过 T-SNE 算法。模型的编码器返回神经元看到的目标方向的潜在表示。与其他历史文献模型(如 LFADS、RAVE+、SABLE-noREV)相比,SABLE 能够更好地利用潜在维度。
在实验中,屏幕上用彩色圆圈显示一个点。这个点指的是一个方向(例如,上、下、左、右),一只猴子已经被训练向出现的方向移动光标。该模型对猴子初级运动皮层的神经尖峰进行编码,并作为预测返回猴子在每个时间步的手位置。此外,从模型的编码器,有可能检测神经潜在空间。潜在空间是神经元在每个时间步“看到”的东西。该尺寸可以被视为出现在屏幕上的目标点。
结果非常令人满意。SABLE 可以重建运动轨迹,并利用潜在的神经表示返回最终预测。此外,黑貂似乎也是一种可转让的模式。与这些任务中许多文献模型呈现的不稳定性相比,这是一个显著的结果。
基于 fMRI 模式的感知图像重建和语义脑探索
富尔坎·奥兹切利克,哈文·乔克西,米拉德·莫扎法里,莱拉·雷迪,鲁丁·范鲁伦, 论文
本文展示了一个伟大的场景,其中 ML 可以帮助理论神经科学并增强大脑理解。这项工作的关键点是从 fMRI 信号中重建感知的自然图像,例如狗、猫。这是一个多么激动人心的话题!来自图卢兹大学、CerCo、IRIT、CNRS 和 ANITI 的作者提出了实例条件 GAN,IC-GAN,作为破解这个问题的可能工具。
该模型将作为输入。来自五个人的数据集 fMRI 记录。功能性磁共振成像会议分为“训练”和“测试”会议。在培训课程中,患者观看了一次来自 ImageNet 数据集的 1200 个样本。测试环节向患者展示了 50 个样本,在屏幕上重复了 35 次。fMRI 遵循以下设置:33 秒的注视,50 幅图像以 2Hz 闪烁呈现 9 秒,另外 6 秒的注视。每个图像的大脑表示被计算为 9 秒图像呈现窗口中每个体素的值变化的平均值。

图 2:从功能磁共振成像数据重建集成电路氮化镓。“测试图像”是呈现给受试者的图像。IC-GAN rec 是作者模型的结果。1/2/3/4/5 是其他 5 个对象的图像重建。这些重建仍然捕捉语义属性。
图 2 显示了 IC-GAN 重建(IC-GAN Rec)的出色重建结果。第一栏“测试图像”报告了在患者面前展示的真实图像。IC-GAN 可以重建测试图像,尽管丢失了一些视觉细节,例如第三张图片中丢失的车辆,图六中玻璃杯中的啤酒。潜艇。列代表每个受试者的 fMRI 解码潜变量的等效重建。一般来说,重建与地面真实图像是一致的,尽管缺少一些低层次的细节。
IC-GAN 模型可以超越这一点,它让我们看到当图像显示时大脑是如何工作的。图 3 根据神经网络权重(“回归权重的差异”)报告了每个受试者的视觉皮层如何工作。视觉皮层分为 V1 区、V2 区、V3 区、V4 区、外侧枕叶复合体(LOC)、梭形脸区(FFA)和海马旁区(PPA)。较低的大脑区域(V1、V2 和 V3)更能提供模型捕捉到的高级别潜在特征的信息,而较高的大脑区域(V4、LOC、FFA 和 PPA)携带关于低级属性潜在特征的更多信息。

图 3: a)较低大脑区域(V1、V2、V3)和较高大脑区域(V4、LOC、FFA、PPA)之间的权重对比。前者携带更多的高级潜在特征信息,而后者的低级属性 b)逐个体素的图,其显示了在视觉皮层中高级和低级属性潜在特征检测之间的差异,在 5 个对象上平均。
生物纠错码产生容错神经网络
亚历山大·兹洛卡帕,安德鲁·k·谭,约翰·m·马丁,马克斯·泰格马克,艾萨克·l·庄, 论文
这篇论文反映了计算机科学和神经科学之间的接口问题。众所周知,在哺乳动物的大脑皮层中存在误差校正,即所谓的网格编码,它允许针对神经尖峰噪声实现成功的计算。有没有可能利用这些生物代码创造一个通用的容错神经网络?也就是说,仅仅使用不可靠的神经元就能实现计算吗?
麻省理工学院的研究人员从生物学的对应部分开始研究。在哺乳动物的大脑中,神经表征通常受到噪声的影响。生物轴突输出的神经尖峰受到加性高斯噪声的影响。这种噪声被进一步补偿,使得神经系统的最终输出可能以极小的概率失败。为了将相同的概念应用于人工神经网络,作者通过布尔公式设计了神经网络容错的含义。布尔公式是布尔门与、或、或非、异或、与非的组合,是任何计算的基础。此外,作者发现了一个比例定律,它决定了需要多少神经元才能达到容错神经系统。

图 4: (A)这是显示如何实现通用容错神经网络的相变图。(B)对执行容错计算所需的神经元数量的模拟,以证明 dervied 分析标度。
图 4 A 显示了主要结果。可以导出一个相变图,该图给出了一个容错神经网络的分析阈值。在蓝色的曲线区域下,有可能存在人工神经网络,可以进行任意长的计算,误差很小。另一方面,红色区域,任何神经结合都会导致错误。此外,图 4 B 报告了对给定生物高斯噪声执行两位乘法所需的神经元数量的数值模拟。可以从分析曲线中提取的幂律证实了作者对容错网络的神经元数量的预期缩放行为。
如果你有问题或好奇,请发邮件至:stefanobosisio1@gmail.com 或在 Instagram 上关注我,支持我的新人工智能项目:https://www.instagram.com/bosiartai/
ML &神经科学:2022 年 1 月必读
原文:https://towardsdatascience.com/ml-neuroscience-january-2022-must-reads-faf91e8a1d32
机器学习能为神经科学做些什么?在这个新系列中,我们将探索 ML 和神经科学之间的关系。这个月,牛津、史丹福、UCL、麻省理工、富士通和哈佛医学院的研究人员将他们的发现发表在 ML 和神经科学上

图片由 Unsplash 上的 Milad Fakurian 拍摄
通过我的推荐链接加入 Medium 来支持我的写作和项目:
https://medium.com/@stefanobosisio1/membership
为什么要关心神经科学?
神经科学是当今人工智能🧠的根源🤖。阅读并意识到神经科学中的进化和新见解不仅会让你成为一个更好的“人工智能”的家伙😎而且还是一个更好的神经网络体系结构的创造者👩💻!
在这篇文章中,我正式用一种新的方法重新发布我的“保持神经科学的更新”文章。在这一系列中,我将涵盖 3 篇主要论文,在 arxiv.org的评论中,它们涉及机器学习和神经科学。特别是,我将涉及以下几个方面:
- ML 研究能否帮助神经科学更深入地了解大脑的动力学和活动?
- 神经科学如何用新的生物启发模型帮助增强 ML?
- ML 和模型如何通过新的成像和信号技术给我们带来新的临床神经科学?
这个月我们将看到变形金刚的大量使用。在第一篇论文中,变形金刚可以帮助神经科学获得更好的海马体模型,更深入地了解大脑中的空间表征。第二篇论文提出了关于深度神经网络以及如何处理对称性概念的问题。最后,论文使用一个变压器来创建一个新的模型,以增强 MRI 成像技术。享受:)
将变压器与海马结构的模型和神经表示相关联
詹姆斯 C.R 惠廷顿,约瑟夫·沃伦,蒂莫西 E.J 伯伦斯,论文
本文从数学的角度比较了变压器和海马的空间表征。从语言处理到计算机视觉,变形金刚肯定是最有前途的人工智能架构,但是,信不信由你,这种布局不是由大脑功能激发的。因此,惠廷顿、沃伦和伯伦斯在这篇论文中想知道变压器与海马功能有什么相似之处,特别是,我们是否能找到与海马位置细胞相似的细胞空间表征。
Transformer 模型接收输入的观察数据点,并致力于预测列表中缺失的元素。变压器的核心机制是一个“自我关注”层,其中每个输入元素与所有其他输入元素进行比较,强调与预测更相关的元素。
作者训练了一个类似变压器的模型来学习空间表示,图 1,显示了产生的变压器层“细胞”图复制了海马细胞正在做的事情。

图 1;a)空间导航学习任务 b)变压器架构。在这种情况下,作者强调了变压器模型中的递归位置编码,以便获得在海马体中发生的精确排序 c)实际网格细胞速率图 d-f)从速率图的角度用线性或递归激活函数从变压器模型的预测。
这种相似性允许作者进一步推动神经科学模型的发展,重写了一个众所周知的神经科学模型的数学基础。Tolmen-Eichenbaum 机器(TEM)模型作为序列学习器捕获海马现象。作者通过修改记忆提取的内部过程,获得了 TEM-transformer (TEM-t)模型,从而证明了 TEM 可以与 Transformer 模型完全一样地被训练。人工智能世界和大脑世界的结合改善了传统的神经科学模型,减少了 TEM 训练时间,解决了一个更大的问题,因为与标准 TEM 相比,TEM-t 可以检索和存储更多的记忆。
神经科学的含义是什么?正如我们所说,透射电镜复制了海马现象。如果 TEM-t 可以做得更好,这意味着变压器的自我注意机制可以隐藏一些关于神经元如何在海马体水平上工作的解释。特别是,从注意力层数学中,有可能识别出两个特定的神经元池。第一个池报告功能,而第二个池充当内存。特征池可以进一步分为两个子群体,一个将神经元映射到不同的大脑区域,另一个在不同的环境中跟踪这种映射。然后,存储层充当 softmax 操作,就像在变压器中一样,稀疏地激活神经元,这使得这些神经元成为每个环境的空间调谐激活器,类似于海马位置细胞。
这是一篇很棒的论文,它展示了人工智能模型不仅可以增强现有的神经科学模型,还可以打开关于大脑功能的不同解释。总结文章发现:
- 变形金刚确实重复了海马的空间表征
- 自我注意层的数学表示可以移植到现有的神经科学模型(TEM-t)
- 人工智能和神经科学可以在数学上融合,为大脑海马细胞的表达提供新的假设。
深度网络的对称感知:前馈结构的不足和循环连接的改进
Shobhita Sundaram,Darius Sinha,Matthew Groth,Tomotake Sasaki,Xavier Boix,论文
深度神经网络(DNN)在计算机视觉任务中表现出惊人的性能。DNN 可以轻松地对给定的输入进行分类,增强医学成像,生成类似人类的绘画,它们是当今智能手机硬件的基础。然而,似乎对称的概念可能是一个问题,其中大多数 DNN 无法捕捉长程空间依赖性,也无法满足“简单”的人工任务。在本文中,作者调查了 DNN 的长期空间依赖性,并为 DNN 设计了新的解决方案。
作者在对称性检测任务上初步测试了以下模型:DenseNet、Xception、InceptionResNetV2、InceptionV3、ResNet101、ResNet50 和 RCNN-SAT。对称感知具有两个挑战:首先,对称性是抽象特征,并且仅由像素关系来指示,其次,这些像素关系是长距离的。所有常见的模型都失败了,因为它们主要是为图像识别而训练的,并且模型本身的架构不能编码长程相关性。
因此,研究人员在更“对称”的调谐模型上提出了同样的问题。特别是,他们使用了扩张卷积神经网络、三个堆叠卷积 LSTM (LSTM3)和一个变压器。从结果来看,LSTM3 是唯一捕捉长程关系的网络。LSTM3 扩大了其感受野的大小,并且能够将长距离依赖性分解成一系列局部操作。LSTM 在许多时间步长上应用前馈架构,在各层之间共享权重。这扩大了感受域并控制了网络的复杂性,避免了过拟合,如每个扩张卷积神经网络或变压器。
神经科学的含义是什么?从文献来看,对称性检测似乎主要源于递归成分。这些结果表明,鉴于 LSTM3 的循环性质,循环可能是对称性感知的关键术语。这可以通过 EEG 分析来研究,在这种情况下,考虑到对称性计算的循环性质,EEG 可能需要少量时间来显示用于检测对称性的固定大脑活动。
基于变压器神经网络的扩散张量估计
达伍德·卡利米,阿里·格里波尔,论文
本文是 ML 在临床神经科学中的实际应用。扩散加权磁共振成像(DW-MRI)是一种广泛使用的医学成像技术。它利用水分子的扩散来进行医学成像,使这项技术成为大脑研究的最佳非侵入性工具。这项技术主要用于研究从新生儿到老年人的不同人群的大脑发育和退化。如果我们看看下面的数学理论,最终的成像是通过计算水分子的扩散张量来进行的。这种计算相当复杂,并且仍然依赖于过时的估计方法,在过去二十年中大多没有改变(例如 CWLLS)
在本文中,作者提出并测试了一个变压器模型,以进一步改善扩散张量估计。注意力模型是序列建模中最先进的。与基于 CNN 的模型不同,注意力提供了高度的灵活性,其中网络权重以依赖于输入的方式进行调整。此外,信息可以从序列中的一个位置传播到另一个位置。图 2 示出了所提出的模型,其中具有六个通道的输入体积 3D 图像由两个子模型处理。第一个模型,模型 S,使用扩散信号作为输入,并估计扩散张量。第二子模型,模型 ST,试图从模型 s 的输出提供扩散张量的更精确的估计。这样,模型的第一部分利用了扩散信号和相邻体素之间的相关性。模型 ST 通过输入扫描和模型 S 的预测的更广泛的视图产生最终的估计。

图 2:提议的模型。输入的六通道体素图像由两个子模型处理。模型 S 利用局部相关性提供扩散系数的初始估计,而第二模型,模型 ST,合并体素输入和第一估计,以产生更鲁棒和精确的结果。
图 3 显示了估计的扩散张量和张量导出变量(FA、MD 和主要特征向量的方向)的结果。在 40 多次测试中,作者的方法实现了最低的估计误差,将 MD 的平均绝对误差减少了 2.6-9.8,FA 的平均绝对误差减少了 1.5-2.2。此外,与 CWLLS 技术相比,当前的方法显示了很大的改进,仅使用了 6 次测量,获得的图像与参考图像非常接近。

图 3:用普通 CWLLS 模型和作者提出的模型得到的张量图像的结果。该表报告了 40 名受试者的扩散误差,而图像是扩散系数 FA、MD 和 color-FA 的重建。
这显示了简单的直觉,即使用最先进的 ML 模型,可以很容易地适用于开发临床神经科学的准确估计。使用具有挑战性的 dHCP 新生儿数据集,作者能够通过仅使用六个数据点而不是大量测量来减少估计误差并获得有价值的输出图像。
如果你有问题或好奇,请发邮件至:stefanobosisio1@gmail.com 或在 Instagram 上关注我,支持我的新人工智能项目:https://www.instagram.com/bosiartai/
ML &神经科学:2022 年 3 月必读
原文:https://towardsdatascience.com/ml-neuroscience-march-2022-must-reads-4d5d32dc80ed
本月:PDE 和深度学习构建大脑的🧠连接体🔗脑机💻假手的接口,最后是对大脑动力学理解的隐马尔可夫模型的新看法。

图片由莫在的 Unsplash
通过我的推荐链接加入 Medium 来支持我的写作和项目:
https://stefanobosisio1.medium.com/membership
为什么要关心神经科学?
神经科学是当今人工智能🧠的根源🤖。阅读并意识到神经科学中的进化和新见解不仅会让你成为一个更好的“人工智能”的家伙😎而且还是一个更好的神经网络体系结构的创造者👩💻!
在这篇文章中,我正式用一种新的方法重新发布我的“保持神经科学的更新”文章。在这一系列中,我将涵盖 3 篇主要论文,在 arxiv.org的评论中,它们涉及机器学习和神经科学。特别是,我将涉及以下几个方面:
- ML 研究能否帮助神经科学更深入地了解大脑的动力学和活动?
- 神经科学如何用新的生物启发模型帮助增强 ML?
- ML 和模型如何通过新的成像和信号技术给我们带来新的临床神经科学?
这个月我们将看到 ML 如何帮助我们更深入地理解大脑系统,并成为临床神经科学应用的关键。第一篇论文展示了深度学习在生成大脑连接体和解决偏微分方程问题方面的奇妙用途。第二篇论文将我们直接带入未来,对计算机-大脑接口进行了大量研究,给截肢者带来了巨大的希望。最后,我们将看到马尔可夫状态模型与图形模型的伟大结合,以图形形式创建大脑动力学新的可能的神经科学应用。享受:)
深度学习大脑连接体的形状
戴浩成,马丁·鲍尔,p·托马斯·弗莱彻,萨朗·c·乔希,论文
人工智能如何帮助神经科学更深入地了解大脑的动力学?
脑连接体是大脑的神经连接图,是一种接线图,有助于解释神经系统的构建和沟通。很难获得一个好的图,通常的设置是扩散加权磁共振成像(DW-MRI)和追踪成像的结合,这里是关于 DWMRI 重建方法的 ML 文章。追踪成像计算 DW-MRI 矢量场的积分曲线,为每个体素找到最可能的纤维束。然而,这种技术对噪声非常敏感,这在 DWMRI 测量中是不可能避免的。为此,作者成功地将深度学习模型与黎曼流形结合起来。黎曼流形是一个实的、可微的数学流形,即一种我们可以执行一些微分/应用 ML 技术的域。黎曼流形的形状可以推断白质路径,再加上深度学习,就有可能有一个模型来找到适合塑造连接体的完美曲线。
如果连接体存在于黎曼流形中,这意味着我们可以有黎曼度量。黎曼度量可以告诉我们如何找到满足和创建连接体的正确路径。找到完美路径的方法是解一个偏微分方程(PDE)。这个偏微分方程的解是测地线,一族满足黎曼度量的曲线。由于这个问题很难解决,作者颠倒了问题的观点。我们需要的是在流形上找到一个黎曼度量,它最小化一个给定的能量泛函形式。当达到最小化时,我们找到了正确的测地线,即正确的路径/连接体,这符合我们的数学领域。
如今,这个问题可以通过深度学习模型来解决,如 PINN(物理学信息神经网络),它可以估计 PDE 问题的解决方案。特别是,作者采用了 PINN 的扩展,即卷积编码器-解码器神经网络(CEDNN),它从高维输入中构建多尺度特征。

图 1: CEDNN 基于 103818 名受试者生成的连接体。左上:冠状面视图,中上:矢状面视图,右上:横断面视图。右下角和中间:交叉光纤区域的建议放大视图。右下:提议的跟踪成像为缩放区域生成测地线。图片来自报纸。
该方法首先在合成数据集上进行了验证。最后,用于估计 103818 名受试者 DWMRI 输出中的 3D 交叉纤维区域。图 1 示出了使用 CEDNN 的全脑度量的完整估计,对于每个密集块具有 55、30、55 个密集层,学习速率为 0.0003,迭代次数为 90,000 次。结果可以清楚地显示深度学习如何通过黎曼度量来建模人类连接体的形状。未来的工作将集中在普遍性上。
人工智能能够通过神经接口对假肢进行实时和直观的控制
陆迪奎、阮安俊、姜鸣、马库斯·w·德莱兰、徐健、吴桐、谭永健、赵文锋、林志宏、辛西娅·k·奥弗斯特里特、赵奇、乔纳森·郑、爱德华·w·基弗、杨志
ML 如何帮助临床神经科学?
作者提出了一种神经假体系统,该系统利用人工智能代理通过外周神经接口来翻译人类运动。
神经假体系统的核心挑战是神经接口。首先,直接大脑植入提供了最全面的人机互联。然而,这种解决方案是高度侵入性的,并且会产生长期问题,例如神经组织损伤、感染等。另一方面,外周神经接口是一种侵入性较小的解决方案,然而,它必须处理要实时处理的高维输入数据,以有效地转化为假体运动。作者通过采用基于卷积神经网络(CNN)和递归神经网络(RNN)的人工智能神经解码器,研究了第二种方法。已经证明,在神经通信/翻译中,CNN 和 RNN 优于其他最大似然技术。
该实验是通过束靶控制灵巧手(DEFT)的临床试验的一部分,其中人体实验由机构审查委员会(IRB)审查。在截肢后的三个不同时间(9 个月、4 年和 10 年)对三名受试者进行了测试。每个受试者接受 2-4 个束特异性靶向纵向束内电极(快速寿命)微电极植入物,靶向正中神经和尺神经的单个束,持续 3-16 个月。

图 2: A):人工智能设置概述。尺神经和正中神经与微电极阵列相连。从电路中,执行 AI 计算来预测运动意图。b):实现了 RNN 架构。图片来自报纸
图 2 显示了整个人工智能系统。神经数据通过带有高性能 Neuronix 神经接口芯片的神经调节系统获得。在信号采样和处理之后,记录具有高信噪比的位置,并从中提取特征。从那里,人工智能代理预测当前的运动意图。RNN 架构用 PyTorch 编码,有 160 万个可训练参数,部署在 NVIDIA Jetson Nano 上。

图 3: A):实验概述。神经数据(蓝色箭头/路径)分别从受伤的手和健全的手获得。该信号被实时处理。B)在治疗过程中拍摄受试者的照片。图片来自报纸。
图 3 显示了对象的实际实现。该系统记录受伤和健全手的神经数据。数据是通过镜像双边范例获得的。受试者被要求用双手做 10 次手势。从那里,数据通过人工智能代理进行处理,并通过蓝牙将预测映射到虚拟手和假手上。该测试要求每个受试者从静止位置摆出想要的姿势。
AI 代理可以准确地实现直观的实时控制和鲁棒的长期性能。此外,记录的反应时间短,超过 16 个月的使用没有恶化的迹象。无需重新训练,模型预测准确率在 90%以上。
大脑动力学的多重时空模型中的社区排序
James B. Wilsenach,Catherine E. Warnaby,Charlotte M. Deane,Gesine D. Reinert,论文
在本文中,作者展示了如何融合两个主要领域:一方面是网络神经科学,另一方面是马尔可夫模型和图 ML 模型的结合。这个想法是使用一个新的模型,命名为隐马尔可夫图模型(HMGMs)来给出大脑动力学的新解释。
焦点的出发点是这样的假设:
健康受试者在休息时的大脑活动通常被用作…病理条件下的基线…一种条件下的活动被建模为单一的静态活动模式,忽略了大规模的动态变化
最近,神经研究人员已经开始使用神经成像时间序列来提供一种新的方法来理解人类大脑中的空间模式和电活动(功能活动)。大脑区域之间的活动交换被称为功能连接。功能活动和连接性之间的关系可以用来以图表的形式理解大脑系统。大脑作为一个图形将大脑与马尔可夫毯联系起来,在马尔可夫毯中,我们可以识别解剖节点或区域,并通过与其他节点交换的一系列信息水平来表征它们。在这项研究中,大脑被研究为一个具有模块化结构的复合图。特别地,处理了隐马尔可夫模型(HMMs)的静态视图。在 hmm 中,大脑动力学可以被参数化为忽略跨节点和大脑区域的动力学切换的有限状态。通过将 HMM 转换成动态图模型,作者可以阐明层间网络(马尔可夫信息矩阵)。

图 4:工作的整体模式。答:首先,获得受试者的 fMRI 扫描。变分贝叶斯推理用于训练 hmm。b)隐藏状态的数量通过主题交叉验证的最大熵来确定 C)层间时间转换的邻接矩阵 D)将每个状态表示为图形,从相关活动中导出。e)每个子状态的分析 F)内部群体的排名 G)结果。
使用 NeuroSynth 数据集,作者运行了新提出的模型的基准。从数据集中,HMM 被训练以获得多重时空脑状态图。然后,可以从每个马尔可夫状态的分数占据分布中直接获得时空分量。虽然我们在这里处于研究的非常阶段,但通过这种无监督的方法,作者能够获得丰富的互补社区阵列,它们共同作用,给出静息觉醒期间的神经行为模式。大脑状态与模块化概念相关联。在深度麻醉或意识改变的背景下,理解大脑模块性的含义,阐明有意识大脑活动中复杂的网络动力学,将是非常重要的。
如果你有问题或好奇,请发邮件至:stefanobosisio1@gmail.com 或在 Instagram 上关注我,支持我的新人工智能项目:https://www.instagram.com/bosiartai/
ML &神经科学:2022 年 4 月必读
原文:https://towardsdatascience.com/ml-nuroscience-april-2022-must-reads-fea9594f4c9d
本月:微软、INRI 和 IIIT 攻克脑机💻解决大脑编码问题的接口和视觉语言转换器🧮和探索大脑结构和功能网络的第一个公共图形神经网络框架🕸️。

图片由莫在的 Unsplash
通过我的推荐链接加入 Medium 来支持我的写作和项目:
https://stefanobosisio1.medium.com/membership
为什么要关心神经科学?
神经科学是当今人工智能🧠的根源🤖。阅读并意识到神经科学中的进化和新见解不仅会让你成为一个更好的“人工智能”的家伙😎而且还是一个更好的神经网络体系结构的创造者👩💻!
在这篇文章中,我正式用一种新的方法重新发布我的“保持神经科学的更新”文章。在这一系列中,我将涵盖 3 篇主要论文,在 arxiv.org的评论中,它们涉及机器学习和神经科学。特别是,我将涉及以下几个方面:
- ML 研究能否帮助神经科学更深入地了解大脑的动力学和活动?
- 神经科学如何用新的生物启发模型帮助增强 ML?
- ML 和模型如何通过新的成像和信号技术给我们带来新的临床神经科学?
这个月我将与你分享两篇在 ML/神经科学领域引起我注意的论文。前者来自微软印度公司与印度自然资源研究所和 IIIT 的合作,研究强大的深度学习技术来解决大脑编码问题是一项了不起的工作。脑编码是脑-机接口系统需要解决的核心问题。作者在该领域首次研究了视觉语言转换器在解耦 fMRI 信号和大脑区域激活中的应用。
第二篇论文是埃默里大学和匹兹堡大学的合作成果。作者测试并向公众发布了 BrainGB,这是一个大型大脑图形神经网络系统,允许研究人员寻找临床结果的预测以及大脑生物标记。这是一项关键的工作,它铺平了道路,并覆盖了神经科学和深度学习之间的差距,以从图形神经网络角度获得越来越多的对大脑结构的见解。简直太神奇了。
视觉语言大脑编码
Subba Reddy Oota,Jashn Arora,Vijay Rowtula,Manish Gupta,Bapi Raju Surampudi,论文
ML 研究能否帮助神经科学更深入地了解大脑的动力学和活动?
这篇论文来自 INRIA、IIIT 和微软印度公司的合作,解决了当前最热门的研究课题之一:脑-机接口和大脑编码。为了实现完全有效的脑机接口系统,我们确实需要了解人脑是如何对刺激进行编码的。给定一个视觉或语言刺激,大脑执行一种“神秘”的编码,将信号转化为大脑活动。在这项工作中,作者研究了基于图像和多模型的变压器是否可以在整个大脑上编码 fMRI 信号,以及编码的准确性如何。

图 1:提出的大脑编码方法。视觉和文本刺激是多模型变压器的输入。从这里开始,模型预测不同大脑区域的功能磁共振成像活动。然后用 2V2 准确度和皮尔逊相关系数(R)评估大脑编码结果。
到目前为止,大脑编码研究仅限于卷积神经网络(CNN)和两个单一的大脑区域,即视觉皮层 V4 和前额叶 IT。CNN 显示可以有效地对视觉刺激的语义进行编码,然而,对于大脑作为一个整体是如何编码的还没有了解。此外,在得到有意义的结果之前,被调查的 CNN 经过了大量的人工处理。
为了研究这些问题,作者采用了两个神经科学数据集:BOLD5000 和 Pereira。前者是从观看 5254 幅自然图像的受试者收集的 fMRI 扫描数据集,它指出了 5 个视觉区:早期视觉区(EarlyVis),对象相关区,如外侧枕叶复合体(LOC),枕叶位置区(OPA),海马旁位置区(PPA)和脾后复合体(RSC)。后一个数据集报告了受试者的 fMRI 扫描,这些受试者将概念词与图片一起可视化。该数据集的主要焦点是默认模式网络(DMN)、语言网络、任务积极网络、视觉网络
型号(所属 CNN—vgnet 19,ResNet50,INceptionV2ResNet,EfficientNetB5,预训练文本转换器 — RoBERTa,图像转换器 — ViT,DEiT,BEiT,后期融合型号 — VGGNEt19+RoBERTa,ReNet50 + RoBERTa,InceptionV2ResNet+RoBERTa,efficient net b5+RoBERTa,多型号)在 fMRI 扫描上被训练,具有岭回归损失。每个模型的主要目标是编码和预测给定刺激的每个大脑区域的 fMRI 体素值。
结果清楚地表明,VisualBERT 优于所有其他模型。对于 BOLD5000,VisualBERT 在 2V2 准确性和皮尔逊相关性方面都优于所有模型(图 2)。特别是 VisualBERT 显示出 OPA 和 LOC 区域的高 Pearson R,与视觉处理层次一致——有一个联合编码(视觉和语言信息)。在 Pereira 上,数据集 VisualBERT 显示了与语言区域、DMN 和 TP 的高度相关性。

图 2:真实和预测的 fMRI 体素之间的 MAE:a)bold 5000 受试者 1 的 VisualBERT 的 V2 和 V3 区域。b)佩雷拉数据集 subject 2 上的 VisualBERT。
我们可以得出结论,多模型视觉语言转换器优于当前用于解决大脑编码问题的 ML 方法。这些新的见解为 fMRI 分析和脑机接口铺平了道路
BrainGB:基于图形神经网络的大脑网络分析基准
崔和杰,,朱彦桥,宣侃,,安东尼奥敖东,,乔舒亚卢米雷,梁湛,,何,杨颖,卡尔,论文

图 BrainGB 框架。图形神经网络接收 fMRI 扫描、sMRI、dMRI 作为输入,并创建网络结构。从这里,用户将能够获得临床结果的预测以及生物标记。
ML 和模型如何通过新的成像和信号技术给我们带来新的临床神经科学?
这是一篇关键的论文,因为它填补了大脑连接体和图形神经网络(GNN)之间的空白。特别是,作者提出了 BrainGB,这是一个统一的、模块化的、可扩展的和可重复的框架,用于使用 GNNs 进行大脑网络分析。
理解结构、功能和大脑动力学是神经科学中引人入胜的研究。从这里,我们可以获得各种目标的洞察力,如精神障碍治疗或实现通用人工智能。此外,不同脑区之间的相互作用是神经发育或障碍的驱动因素。如果我们能把大脑转化成一个有节点和边的图形系统,我们就能开始对一些神经机制有更深刻的理解。多年来,不同的实验技术被用来将大脑映射到一个图形上。来自磁共振成像(MRI)或脑电图(EEG)或正电子发射断层扫描(PET)或扩散张量成像(DTI)的数据已被用于和研究,以绘制一个大脑网络,该网络可以通过灰质区域的连通性来描述跨大脑区域信号的相关性。已经进行了不同的尝试来使用机器学习创建完整的网络。一些研究试图通过学习大脑网络图结构来预测大脑疾病。然而,许多提议的实验协议局限于本地数据集,或者由于伦理原因,不公开可用。通常,成像处理的细节不被公开,使得评估结果和再现结果更加困难。出于这些原因,作者创建了 BrainGB,一个公共基准平台,来评估用于大脑网络分析的深度图模型。
图 1 报告了最终的 BrainGB 结构,它由以下 3 点组成:
- 该框架是统一的、模块化的、可扩展的和可复制的。该设计确保了公正的评估、公开可用的数据集、超参数、设置和基线
- 功能性和结构性大脑网络都被用来弥补神经成像和深度学习之间的差距
- GNN 方法被细分为 4 个步骤:1)节点特征,2)消息传递,3)注意机制,4)汇集策略
项目主要代码可在:https://github.com/HennyJie/BrainGB获得,教程可在 https://brainnet.us 找到。

图 4a:用于构建大脑网络的 fMRI 数据预处理,以及用于提取信息的一系列软件

图 4b:提取大脑结构信息的 dMRI 数据处理以及用于提取信息的软件。
为了建立大脑网络,从 fMRI 和 dMRI 扫描中提取了功能和结构信息。图 4a 和 4b 报告了预处理协议以及用于提取所有相关信息的输入软件。
一旦原始信息不同的 GNN 模块化设计已被应用。第一种方法是纯图形神经网络,具有节点特征构造。该首过特征记录了每个感兴趣的大脑网络区域(ROI)的高维身份。第二种方法采用消息传递,通过本地连接聚合相邻要素来迭代更新节点表示。第三步是基于注意力机制,模型更新大脑区域
以数据驱动的方式表示。最后一步是使用池策略。为了测试 BrainGB 的预测,作者对 fMRI 和 dMRI 扫描建立的四个数据集(艾滋病毒、PNC、PPMI 和 ABCD 数据集)进行了一系列验证。对于每个数据集,记录并评估 4 种图表策略的准确性、F1 和 AUC 评分。有一点令人惊讶,但与其他策略相比,基于良好节点连接的注意机制返回了最佳结果(表 1,仅针对 HIV 的结果)
表 1:艾滋病毒数据集的 BrainGB GNN 策略的结果,在准确性、F1 和 AUC 评分方面。为了清楚起见,我没有报告 PNC、PPMI 和 ABCD 的结果。
这里的作者提供了第一个。使用脑网络分析与图形神经网络融合的统一、模块化、可扩展和可复制的框架。从这里,研究人员可以访问可重复的数据集、基线和教程,并要求模型对临床结果进行预测,这些结果可以每天得到改善。
如果您有任何疑问或好奇,请发送电子邮件至:stefanobosisio1@gmail.com 或通过 Medium 关注我:)
使用 Azure 机器学习的 ML Ops
原文:https://towardsdatascience.com/ml-ops-with-azure-machine-learning-72ebc37ee3cd
ML DevOps 管道实施框架的高级概念概述
介绍
Azure 机器学习服务(AML)提供端到端的能力来管理 ML 生命周期。MLOps(机器学习操作)、框架无关的互操作性、与 ML 工具和平台的集成、安全性和信任以及可扩展性和性能是关键特征。
在 Python 或 PowerShell 中的 Azure 机器学习 SDK 提供了一个开发自动化 ML 管道的机会,该管道由 Azure DevOps 或 GitOps 的编排功能提供支持。因此,ML 工程师和数据科学家可以专注于他们的任务,而 DevOps(构建、部署和管理)则通过自动化流程卸载。
在这篇文章中,我们将讨论使用 Azure 平台服务的 MLOps 的一般流程。当然,这个框架可以很容易地扩展到其他公共云提案(AWS 或 GCP ),只需稍加调整,就可以映射到它们的本地服务产品。

Gerrie van der Walt 在 Unsplash 上拍摄的照片
https://linkedin.com/in/p-jainani
解决方案框架
开发和构建

环境建设管道(图片作者提供)
以下是创建 ML 工作空间和环境构建的高级步骤:
- 利用 Azure DevOps (ADO)作为编排平台,使用 IaC 方法自动化基础架构
- Azure IAM 将数据科学团队(数据科学家、ML 工程师、数据工程师等)分配到 ML 工作区。
- 出于培训和测试目的,团队在工作区内分配计算资源。
- 团队搅动 ML 管道实验,每个人可以在一个竖井中工作,为管道开发一个可重用的组件。因此,实验包括数据提取、数据探索、数据预处理、模型训练、评估和测试。
- ML 工程师然后建立源代码控制和测试工作台来支持模型开发和测试框架。
- 环境供应还使包、ML 框架和模块能够支持开发。
培训渠道

使用 Azure ML 自动进行模型训练和实验
Azure ML 使 ML 团队能够利用 Azure DevOps 功能,通过自动化培训渠道完成端到端的模型实验:
- 通过执行管道步骤,自动化数据预处理和模型训练。
- 跟踪来自各种模型训练实验的度量的结果有助于评估来自许多运行的模型。这样就选出了最佳模型。
- 模型注册步骤用其元数据注册所选择的模型。
- 因此,它从 Azure ML 服务的能力中提供了模型管理的能力。
部署管道

通过使用 Azure DevOps 实现自动化部署(图片由作者提供)
Azure DevOps 提供了自动化 ML 操作所需的 CI 和 CD 步骤的能力,一步一步地将最终评估的模型容器化,使其部署到试运行环境集群,让端点执行 QA 和测试管道。开发人员从遥测和度量中学习关于测试数据上部署的模型的质量和性能。
发布经理负责触发经验证的模型到生产集群的部署。有各种各样的策略可以达到同样的目的;canary deployment 就是一个这样的例子,这对于密切监视较小的传入数据实例子集上的模型性能特别有用。
模型监控和再培训

模特监控&再培训(图片由作者提供)
设置操作、可观测性和控制塔,以连续监控模型漂移和数据漂移。如果模型性能指标低于阈值,DevOps 管道将被触发,并使用 ML 训练管道重新训练模型。
日志和应用遥测是实施端到端指标驱动的自动化 MLOps 的重要组成部分。
结论
使用 Azure DevOps、tooling 和 Azure Machine Learning service 实现 MLOps 的高级解决方案架构将类似于下图。该解决方案概述了如何设置整个 MLOps 生态系统的开发、培训、测试和部署组件。可观察性实现是捕获遥测和指标数据的核心,通过利用 Azure DevOps 管道为整个 MLOps 流程实现事件驱动的自动化。

Azure 机器学习— MLOps 解决方案架构(图片由作者提供)
https://linkedin.com/in/p-jainani
参考
使用 Kafka 流对流数据进行 ML 预测
原文:https://towardsdatascience.com/ml-prediction-on-streaming-data-using-kafka-streams-1e4ebd21008
通过在 Scala 应用程序中的 Kafka 流平台上提供经过 Python 训练的 ML 模型,提高它们的性能

埃姆雷·卡拉塔什在 Unsplash 上拍摄的照片
1.介绍
假设您有一个基于 Kafka 的健壮的流媒体平台,它在将客户的事件数据写入某个仓库之前对其进行清理和丰富。一天,在一次偶然的计划会议上,您的产品经理提出了对传入数据使用机器学习模型(由数据科学团队开发)的要求,并为模型标记的消息生成警报。“没问题”,你回答。“我们可以从数据仓库中选择我们想要的任何数据集,然后运行我们想要的任何模型”。“不完全是”,首相回答。“我们希望尽可能实时地运行。我们希望在收到事件后不到一分钟,ML 模型的结果就可以在 Kafka 主题中使用。
这是一个普遍的要求,而且只会越来越流行。对于许多必须对模型结果做出时间敏感决策的客户来说,对流式数据进行实时 ML 推断的要求变得很重要。
看起来大数据工程和数据科学配合得很好,应该有一些简单的解决方案,但通常情况下并非如此,使用 ML 对繁重的数据工作负载进行接近实时的推理涉及到相当多的挑战。例如,在这些挑战中,Python(ML 的主流语言)和 JVM 环境(Java/Scala )(大数据工程和数据流的主流环境)之间存在差异。另一个挑战与我们用于工作负载的数据平台有关。如果你已经在使用 Spark,那么你可以使用 Spark ML 库,但是有时候它不够好,有时候(就像我们的例子)Spark 不属于我们的栈或者基础架构。
诚然,生态系统意识到了这些挑战,并正在慢慢地用新功能来解决它们,尽管我们特定的通用场景目前留给您几个通用选项。例如,一种方法是将 Spark 添加到您的堆栈中,并编写一个 pySpark 作业,将 ML 推理阶段添加到您的管道中。这将为您的数据科学团队提供更好的 Python 支持,但也意味着您的数据处理流程可能需要更长时间,并且您还需要向堆栈中添加和维护 Spark 集群。另一种选择是使用某个第三方模型服务平台,该平台将基于您的模型公开推理服务端点。这可能有助于保持您的性能,但也可能需要额外的基础架构成本,同时对于某些任务来说是多余的。

常见的解决方案是在堆栈中添加一个 Spark 集群来运行 ML 推理
在这篇文章中,我想展示使用 Kafka Streams 完成这项任务的另一种方法。使用 Kafka Streams 完成这项任务的优势在于,与 Flink 或 Spark 不同,它不需要专用的计算集群。相反,它可以运行在您已经在使用的任何应用服务器或容器环境上,如果您已经在使用 Kafka 进行流处理,那么它可以非常无缝地嵌入到您的流中。
虽然 Spark 和 Flink 都有他们的机器学习库和教程,但使用 Kafka Streams 来完成这项任务似乎是一个不太常见的用例,我的目标是展示它是如何容易实现的。具体来说,我展示了我们如何使用 XGBoost 模型——在 Python 环境中训练的生产级机器学习模型,对 Kafka 主题的事件流进行实时推理。
这是一篇非常实用的文章。在第 2 节中,我们在欺诈检测数据集上训练一个 XGBoost 分类器。我们在 Python 环境中的 Jupyter 笔记本中这样做。第 3 节举例说明了如何将模型的二进制文件导入并包装到 Scala 类中,第 4 节展示了如何将其嵌入到 Kafka 流应用程序中,并对流数据进行实时预测。在文章的最后,你可以找到一个回购协议的链接,里面有完整的代码。
(注意,在很多情况下,我以一种非常不习惯的方式使用 Scala。我这样做是为了清晰,因为惯用的 Scala 有时会令人困惑。)
2.Python 中的 XGBoost 分类器训练
对于这个例子,我们首先基于 Kaggle 信用欺诈数据集训练一个简单的分类模型。你可以在这里找到完整的模型训练代码。重要的一点是,当我们(或我们的数据科学家)对我们模型的结果感到满意后,我们简单地将其保存为简单的二进制形式。这个二进制文件就是我们在 Kafka Streams 应用程序中加载模型所需的全部内容。
3.Scala 中的 XGBoost 分类器预测
在这一节中,我们开始实现我们的 Kafka Streams 应用程序,首先将我们的机器学习模型包装在一个 Scala 对象(singleton)中,我们将使用它对传入的记录进行推理。这个对象将实现一个 predict() 方法,我们的流处理应用程序将在每个流事件上使用这个方法。该方法将接收一个记录 id 和一个字段或要素数组,并将返回一个由记录 ID 和模型给它的分数组成的元组。
Scala 中的 XGBoost 模型加载和预测非常简单(尽管应该注意的是,较新的 Scala 版本中的支持可能是有限的)。在初始导入之后,我们开始将经过训练的模型加载到一个 Booster 变量中。
实现 predict() 方法也相当简单。我们的每个事件都包含一个由 10 个特征或字段组成的数组,我们需要将它们作为输入提供给我们的模型。
XGboost 用来包装输入向量以进行预测的对象类型是一个 DMatrix,,它可以通过多种方式构建。我将使用密集矩阵格式,它基于提供一个表示模型特征或字段的扁平的浮动数组;每个向量的长度(nCols);以及数据集中向量的数量(nRows)。例如,如果我们的模型用于在具有 10 个特征或字段的向量上运行推理,并且我们希望一次预测一个向量,那么我们的 DMatrix 将使用长度= 10、nCols = 10 和 nRows = 1 的浮点数组进行实例化(因为集合中只有一个向量)。
这将为我们的分类器对象完成工作,该对象包装了一个经过训练的 XGboost ML 模型。将有一个带有 predict() 方法的分类器对象,每个记录都将被调用。
4.基于实时数据的 Kafka 流推理
在我们进入流应用程序的代码和细节并展示如何在流数据上使用我们的分类器之前,强调在这样的系统中使用 Kafka 流的优势和动机是很重要的。
以 Spark 为例,计算的分配由集群管理器完成,集群管理器从驱动程序应用程序接收指令,并将计算任务分配给专用集群中的执行器节点。每个 Spark 执行器负责处理一组数据分区。Kafka Streams (KS)的强大之处在于,尽管它同样通过并行性实现了规模,即通过运行流处理应用程序的多个副本,但它并不依赖于专用集群,而是只依赖于 Kafka。换句话说,计算节点的生命周期可以由任何容器编排系统(如 K8S)或任何其他应用服务器管理,而将协调和管理留给 Kafka(和 KS 库)。这似乎是一个小优势,但这正是 Spark 最大的痛苦。
事实上,与 Spark 不同,KS 是一个可以导入任何基于 JVM 的应用程序的库,最重要的是,它可以在任何应用程序基础设施上运行。KS 应用程序通常从 Kafka 主题读取流消息,执行转换,并将结果写入输出主题。Kafka 保存和管理状态和有状态转换,如聚合或窗口计算,只需运行应用程序的更多实例即可实现扩展(受限于主题的分区数量和消费者策略)。
KS 应用程序的基础是拓扑结构,它定义了应用程序的流处理逻辑或者输入数据如何转换为输出数据。在我们的例子中,拓扑将如下运行

这里的拓扑相当简单。它首先从 Kafka 上的输入主题中读取流记录,然后使用 map 操作对每个记录运行模型的 predict 方法,最后它分割流,并将从模型中获得高分的记录 id 发送到“可疑事件”输出主题,其余的发送到另一个主题。让我们看看这在代码中是怎样的。
我们的起点是从 Kafka 上的inputTopic主题开始读取消息的builder.stream方法。我将很快对此进行更多的解释,但请注意,我们将每个 kafka 记录键序列化为字符串,将其有效载荷序列化为类型为PredictRequest的对象。PredictRequest 是一个 Scala case 类,对应于下面的 protobuf 模式。这确保了与消息生成者的集成是直接的,同时也使得生成我们在处理定制对象时需要提供的反序列化/序列化方法变得更加容易。
message PredictRequest{
string recordID = 1;
repeated float featuresVector = 4;
}
接下来,我们使用map()在每个消息携带的数组上调用我们的分类器的predict()方法。回想一下,这个方法返回一个 recordID 和 score 的元组,它是从 map 操作流回的。最后,我们使用split()方法创建流的两个分支——一个用于高于 0.5 的结果,另一个用于其他结果。然后,我们将流的每个分支发送到它们自己指定的主题。订阅输出主题的任何消费者现在都将收到一个可疑记录 id 的警报(希望如此),接近实时
关于序列化的最后一点意见:
在用 Scala 编写的 KS 应用程序中使用定制类或对象,无论是 Kafka 记录的键还是值,都需要为类型提供一个隐式的Serde[T](包括它的序列化器和反序列化器)。因为我使用了一个原型对象作为消息有效载荷,所以大部分繁重的工作都由scalapbc来完成,它将一个原型模式“编译”成一个 Scala 类,这个类已经包含了解序列化/序列化这个类的重要方法。让这个隐式 val 对流方法可用(在范围内或通过导入)可以实现这一点。
implicit val RequestSerde: Serde[PredictRequest] = Serdes.fromFn( //serializer
(request:PredictRequest) => request.toByteArray, //deserializer
(requestBytes:Array[Byte]) =>
Option(PredictRequest.parseFrom(requestBytes)))
5.结论
实时最大似然预测的需求变得越来越普遍,并且经常给数据流管道带来相当多的挑战。最常见和最可靠的方法通常是使用 Spark 或 Flink,主要是因为它们支持 ML 和一些 Python 用例。然而,这些方法的一个缺点是,它们通常需要维护一个专用的计算集群,这有时会过于昂贵或大材小用。
在这篇文章中,我试图基于 Kafka Streams 勾勒出一种不同的方法,这种方法除了您已经在使用的应用服务器和流平台之外,不需要额外的计算集群。作为一个生产级 ML 模型的例子,我使用了 XGBoost 分类器,并展示了如何将在 Python 环境中训练的模型轻松地包装在 Scala 对象中,并用于对流数据进行推理。当 Kafka 被用作流平台时,那么使用 KS 应用程序在所需的开发、维护和性能工作方面几乎总是有竞争力的。
希望这将有所帮助!
所有图片,除非特别注明,均为作者所有
笔记
[1] 信用卡欺诈检测数据集,由机器学习小组(ULB 布鲁塞尔自由大学)收集。在开放数据共享下许可公共使用。作者在这里提到了 Kaggle 的出版物:Yann-al Le Borgne,Gianluca Bontempi 用于信用卡欺诈检测的可复制机器学习——实用手册。该小组的更多工作和关于数据集的信息可在这里获得。
(*)完整回购可在这里找到
(*)模型训练代码可以在这里找到
(*)xdboost JVM 包文档可以在这里找到
今天的 ML 故障排除太难了(但也不一定是这样)

ML 可观测性的演变(图片由作者提供)
随着 ML 从业者将更多的模型部署到生产中,模型性能的风险比以往任何时候都高——错误的代价也更大。
第一部分:从无监控到有监控
套用一句常见的智慧,如果机器学习模型在生产中运行,没有人抱怨,这是否意味着该模型是完美的?不幸的事实是,生产模型通常被搁置,除非它们导致负面的业务影响。
让我们看一个今天可能发生的例子:
作为一家金融科技公司的机器学习工程师(MLE ),你维护着一个欺诈检测模型。它已经投入生产一周了,当您正在享用早晨的咖啡时,一位产品经理(PM)急切地抱怨说,客户支持团队发现投诉欺诈交易的电话显著增加了。
这使公司在按存储容量使用计费交易中损失了一大笔钱。公司每小时要花好几万美元,你现在就要解决它。
大口喝。是你的模型吗?软件工程师告诉你问题不在他们这边。

传统的 ML 故障排除工作流程(图片由 Arize AI 提供)
您编写一个自定义查询,从您的模型在过去三天中所做的最后一百万次预测的日志中提取数据。运行查询需要一些时间,导出数据,做一些简单的预处理,导入到 Jupyter 笔记本中,最后开始计算您提取的样本数据的相关指标。
整体数据似乎没有问题。您的项目经理和客户仍在抱怨,但您所看到的可能只是欺诈活动略有增加。
更多的指标,更多的分析,更多的与他人的交流。有些事情正在发生,只是不太明显。因此,您开始挖掘数据,寻找模型所遗漏的欺诈交易的共同模式。您正在编写临时脚本来分割数据。
这需要几天或几周的全力以赴。在这个问题解决之前,你所做的其他事情都暂停了,因为:1)你最了解这个模型;2)每一个坏的预测都在消耗公司的收入。
最终你会发现一些奇怪的事情。如果你按地理位置划分,加州的表现似乎比几天前要差一些。您过滤到加利福尼亚,并意识到一些商家 id 属于您的模型没有发现的诈骗商家。你在这些新的商家身上重新训练你的模型,然后转危为安。
这个例子有助于我们了解今天如何对机器学习模型进行故障诊断。它比传统软件的故障排除复杂许多倍。我们装运人工智能盲板。
对于传统的软件工程来说,有许多监控工具和技术——比如 Datadog 和 New Relic——可以自动发现性能问题。但是机器学习模型的监控是什么样子的呢?
ML 性能监控
首先,让我们确定一下什么是监控:监控,在最基本的层面上,是关于您的系统如何运行的数据;它要求数据能够以某种合理的方式存储、访问和显示。
做性能监控需要哪些数据?
为了监控机器学习模型的性能,你必须从一个预测和实际开始。
一个模型必须做出一些预测。这可以是在拼车应用程序中预测顺风车何时到达的估计到达时间(ETA)。也可以是给某个人多少贷款额度。一个模型可以预测周四是否会下雨。在基本层面上,这就是机器学习系统所做的事情:它们使用数据来进行预测。
既然你想要的是预测真实世界,并且你希望那个预测是准确的,那么看看实际值(也称为地面真实值)也是有用的。真实的才是正确的答案——它是真实世界中实际发生的事情。你的车五分钟后就到了,不然周四就下雨了。如果不与实际情况进行比较,在客户抱怨之前,很难量化模型的表现。
但是获取实际数据并不是一件小事。这里有四种情况:
1.快速实际值:在最简单的情况下,每个预测的实际值都会呈现给你,并且在预测和实际值之间有一个直接的链接,允许你直接分析你的模型在生产中的性能。例如,这可能发生在预测你的行程的预计到达时间的情况下。在某个时刻,车会到达,你会知道花了多长时间,以及实际时间是否与你的预测相符。

快速现实:图片由阿里泽艾
2.延迟的实际值:在下图中,虽然我们看到模型的实际值最终被确定,但对于所需的分析来说,它们来得太晚了。
当这个实际延迟足够小时,这个场景与快速实际没有太大区别。对于模型所有者来说,仍然有一个合理的节奏来度量性能指标并相应地更新模型,就像人们在实时实际场景中所做的那样。

延迟的现实:图片由阿里泽艾
然而,在接收实际数据有明显延迟的系统中,团队可能需要求助于代理指标。代理指标是与您试图估算的实际值相关的替代信号。
例如,假设您正在使用一个模型来确定哪些消费者最有可能拖欠信用卡债务。在这种情况下,一个潜在的代理指标可能是您贷款的客户中延迟付款的客户的百分比。
3.对实际值的因果影响(有偏差的实际值):并非所有的实际值都是相同的。在某些情况下,团队会收到实时的实际数据,但模型的预测会极大地影响结果。举一个贷款的例子,当您决定向某些申请人提供贷款时,您将收到这些申请人的实际金额,但不会收到您拒绝的申请人的实际金额。因此,你永远不会知道,你的模型是否准确地预测到被拒绝的申请人会违约。

有偏见的现实例子:图片由阿里泽艾
4.没有实际值:对于建模团队来说,没有实际值来连接回模型性能是最糟糕的情况。获取基本事实数据的一种方法是雇佣人类注释者或标注者。监控输出预测中的漂移也可用于发出异常模型行为的信号,即使实际值不存在。

没有现实:图片由阿里泽艾
要深入了解这些场景,请参见“行动手册,以监控您的模型在生产中的性能”
收集你的预测和实际情况是第一步。但是为了进行任何有意义的监控,你需要一个公式来比较你的预测和你的实际情况——你需要正确的衡量标准。
我的模型的正确指标是什么?
监控任何模型的正确指标取决于您的模型的用例。让我们看一些例子。
欺骗
一个欺诈模型特别难以用简单的准确性衡量标准来评估,因为数据集是极度不平衡的(绝大多数交易都不是欺诈)。相反,我们可以测量:
- 回想一下,或者您的模型识别出的欺诈示例中有多少部分是真的。
- 假阴性率衡量模型未能准确预测的欺诈。这是一个关键的性能指标,因为就直接财务损失而言,它是组织最昂贵的,会导致退款和其他被盗资金。
- 假阳性率——或模型预测实际上并非欺诈的交易欺诈的比率——也很重要,因为给客户带来不便有其间接成本,无论是在医疗保健领域(患者的索赔被拒绝)还是在消费信贷领域(客户延迟购买食品杂货)。
请求预报
需求预测预测给定时间段内的客户需求。例如,销售电脑机箱的在线零售商可能需要预测需求,以确保他们能够满足客户需求,而不会购买过多库存。像其他时间序列预测模型一样,它最好用 me、MAE、MAPE 和 MSE 等指标来描述。
- 均值误差 (ME)是平均历史误差(偏差)。正值表示预测过高,负值表示预测过低。虽然平均误差通常不是模型在训练中优化的损失函数,但它测量偏差的事实对于监控业务影响通常是有价值的。
- 平均绝对误差 (MAE)是模型预测值和实际值之间的绝对值差,在整个数据集中取平均值。这是对模型性能的一个很好的初步观察,因为它没有被一些预测的极端误差所扭曲。
- 平均绝对百分比误差 (MAPE)衡量模型产生的平均误差幅度。这是模型预测准确性的一个更常见的指标。
- 均方误差 (MSE)是模型的预测值和实际值之间的差值,对数据集进行平方和平均。MSE 用于检查预测值与实际值的接近程度。与均方根误差(RMSE)一样,这种方法对较大的误差给予较高的权重,因此在企业可能希望严重惩罚较大的误差或异常值的情况下可能很有用。
其他使用案例
从点击率到终身价值模型,有很多机器学习用例以及关联的模型度量。关于使用案例的模型指标的其他资源可用。
什么是正确的阈值?
现在你有了自己的衡量标准,你面临一个新问题:多好才算足够好?什么是好的准确率?是我的假阴性率太高了吗?什么才算是好的 AUC ?
绝对量度很难定义。相反,机器学习实践者必须依赖相对度量。特别是,您必须确定一个基线性能。当您训练模型时,您的基线可以是您已经产品化的旧模型、来自文献的最先进的模型或人类行为。但是一旦模型投入生产,它就会成为自己的基准。如果你第一天有百分之三的假阴性率,然后今天有百分之十的假阴性率,你应该叫醒你的工程师!
通常,初始性能不是实际使用的性能;相反,你可以使用连续 30 天的表现。
当模型发生显著变化(标准差或更大)时,必须触发警报。这应该是一个基于基线数据集的自动设置,以便您可以主动收到警报。
结论&下一步是什么
在过去十年中,机器学习模型被迅速采用,解决了具有巨大商业影响的非常复杂的问题。机器学习系统通常建立在数据管道和其他复杂的工程系统之上,这些系统为模型提供进行预测所需的数据。但是预测仅仅是开始。为了在生产中可靠地运行这些系统,您需要性能监控来持续评估您的模型的准确性和质量。
当然,光靠监控是不够的。如果您处于这一阶段,您可能会在客户抱怨欺诈性交易增加之前收到警报,但您仍然不知道如何轻松地修复它。在本博客系列的第二部分中,我们将介绍一种现代的、不那么痛苦的评估和排除模型性能故障的方法:带有 ML 性能跟踪的全栈 ML 可观测性。
联系我们
如果这个博客引起了你的注意,并且你渴望了解更多关于机器学习可观察性和模型监控,请查看我们的其他博客和关于 ML 监控的资源!如果您有兴趣加入一个有趣的 rockstar 工程团队,帮助模型成功生产,请随时联系我们,并在此处找到我们的空缺职位!
没有 Ops 的 ML:在 AWS 上用 Ploomber 进行大规模实验
行动中的 MLOps
用 Ploomber 从 Jupyter 到云
在过去的几个月里,我们与许多数据科学和机器学习团队聊天,以了解他们的痛点。当然还有很多这些。然而,最让我惊讶的是,让一些简单的端到端工作流工作起来是多么困难,部分原因是供应商经常将团队锁定在需要大量设置和维护的复杂解决方案中。
这篇博客文章将描述一个简单的架构,您可以使用它在云中开始构建数据管道,而不会牺牲您最喜欢的工具或经常性的高维护成本。解决方案包括使用我们的开源框架和 AWS 批处理。我们开始吧!

Ploomber 允许您在 AWS 上轻松运行作业。图片作者。
什么是 AWS 批次?
AWS Batch 是一个托管服务,允许用户提交批处理作业。AWS 提供资源(例如启动 EC2 实例)而没有额外成本(您只需为使用的 AWS 资源付费)。
AWS Batch 是一个很好的服务,但是它仍然需要一些设置来封装您的代码并提交作业。我们的开源框架允许您自动化这一过程,因此您可以快速地从您喜欢的交互环境(Jupyter、VScode、PyCharm 等)迁移到云。
Ploomber & AWS Batch = ML 不带 Ops
如果你没有听说过我们,Ploomber 是一个开源框架,允许数据科学家和机器学习工程师从他们最喜欢的编辑器中构建模块化管道。我们为用户提供了一种简单的方法来模块化他们的工作,而不会干扰现有的工作流程。
当您准备运行更大规模的实验或部署时,您可以通过一个命令快速转移到 AWS Batch(我们也支持 Kubernetes、Airflow 和 SLURM 作为后端)。我们的框架自动构建 Docker 映像,将其推送到容器注册中心,在任务之间传递数据,向 AWS 批处理提交作业,缓存结果,并将工件上传到 S3。成功执行后,您可以下载结果并在本地进行分析。
看起来怎么样?
在 Ploomber 中,用户通常在一个pipeline.yaml文件中声明他们的任务(尽管 Python API 也是可用的)。这些任务可以是函数,笔记本,或者脚本(注意:我们与 Jupyter 集成,允许用户将.py作为笔记本打开)。这里有一个例子:
一旦声明了pipeline.yaml,您就可以使用以下命令在本地编排执行:
只需几个额外的步骤,您就可以将本地管道转移到 AWS 批处理;以下是 11 个模型训练任务(并行执行)在 AWS 上完成执行后的截图:

AWS 批处理控制台上的计划任务。图片作者。
这是输出。模型文件:

在 S3 上输出模型文件。图片作者。
以及每个模型的 HTML 报告:

输出关于 S3 的模型评估报告。图片作者。
增量构建的快速迭代(此外,节省您的云账单💰!)

Ploomber 只执行自上次运行以来发生变化的任务。图片作者。
数据科学和机器学习是高度迭代的。例如,您可能希望调整预处理参数,调整一些特征工程转换,或者添加一些诊断图表。在每个小的迭代中重新运行端到端的管道是浪费你的时间(和金钱)。为了帮助你,Ploomber 跟踪源代码的变化。因此,下次执行工作流时,它将只提交自上次执行以来源代码已经更改的作业(以及所有下游任务),从而显著加快迭代速度并减少云计算时间。
现在就试试吧!
运行 Ploomber 示例:
祝贺你,你运行了你的第一个 Ploomber 管道!🎉
在 AWS 批处理中运行管道只需要几个额外的步骤:
在提交到 AWS 批处理之前,您需要从 AWS 控制台配置服务,然后配置您的项目。查看我们的完整教程,了解更多信息。
完成后,您可以使用以下命令提交到云:
Ploomber + AWS Batch 是在没有基础设施管理的情况下开始运行数据科学和机器学习管道的最精简的方式。由于它允许您使用自己喜欢的文本编辑器构建数据管道,因此无需重构代码即可在云中运行工作流,从而缩短开发周期,让您能够更快地进行实验。
你有什么问题吗?在松驰上 Ping 我们!
最初发布于ploomber . io
MLflow 模型服务
原文:https://towardsdatascience.com/mlflow-model-serving-bcd936d59052
使用 MLflow 在本地托管模型

在之前的文章中,我们讨论了如何用 MLflow 来跟踪和注册模型。MLflow 还涵盖了 ML 生命周期的另一个重要步骤,即模型托管和部署。虽然跟踪训练模型的不同迭代很重要,但最终你需要从你选择的模型中进行推理。
使用 MLflow 模型我们可以打包 ML 模型用于本地实时推理或 Apache Spark 上的批量推理。在本文中,我们将探索如何训练一个 Sklearn 模型,然后使用 MLflow 在本地部署它进行推理。我们将使用 MLflow 存储库中的以下示例作为参考。具体这个例子的代码可以在这里找到。
目录
- 设置
- 模特培训
- 部署和推理
- 其他资源和结论
1.设置
这里的设置非常简单,因为 MLflow 是一个开源包,您可以使用它。
安装 MLflow
如果你想完全访问 MLflow 文档,从他们的资源库开始吧,那里也有一大套示例,涵盖了我们讨论过的所有主要组件。
现在,对于我们的 MLflow 模型的设置,我们需要以模型服务将理解的某种结构定义几个文件。我们将捕获我们将在脚本中部署的模型,为此,我们可以创建一个 train.py 来进行模型训练,并创建 log 我们的模型工件。
模型服务脚本
我们可以在根目录下的一个 MLproject 文件中指向这个脚本。在这个文件中,我们可以提供模型服务所需的元数据。在这里,我们可以在 train.py 中捕获我们的入口点脚本,这将基本上让 MLflow 知道这是我们可以在项目中捕获我们的模型工件的地方。
ml 项目已定义
您可以在这个文件中以 yaml 格式定义更多的参数。MLflow 项目的本质是以可重用的方式打包您的数据科学代码以供部署。例如,在上面的 yaml 文件中,如果您正在部署 conda 环境,您也可以定义一个 conda 环境。
在这个例子中,我们不会使用 conda 环境,但是您可以在这个文件中指定您的依赖项。我还附上了一个示例 conda.yaml 文件,仅供本例的代码库中参考。在我们开始编写培训脚本之前,您的文件结构应该如下所示。

文件结构(作者截图)
2.模特培训
在开始部署我们的模型之前,我们将使用 Sklearn 框架用波士顿住房数据集训练一个简单的线性回归模型。
模特培训
我们可以使用 MLflow 提供的 log_metric API 调用来捕获我们的 RMSE 度量,以跟踪不同的训练迭代。
MLflow 运行的日志度量
更重要的是,我们需要捕获我们的模型工件/数据,我们可以通过 log_model API 调用来完成。对于 sklearn 来说更好的是,MLflow 提供了一个为框架定制的特定的 sklearn.log_model 调用。
捕获模型工件
现在,如果我们运行训练脚本,我们应该看到最后一行中省略了一个运行 ID。我们在前面要点的最后一行获取运行 ID,这样我们就可以指定我们想要部署哪个模型。

模特训练+跑步 ID(作者截图)
请务必保存此运行 ID,我们将需要它为我们的模型服务。
3.部署和推理
使用这个运行 ID,我们可以将 MLflow 模型作为一个公共 REST API。使用下面的命令,我们的模型将作为 REST API 用于推理。
请注意,我们为 no conda 环境提供了一个环境变量,因为我们没有针对这种情况的环境变量,但是如果您在 conda 环境中,请确保忽略它。

模特上菜(作者截图)
现在,我们可以通过对/调用路径的 POST 输入来调用本地 REST API 端点。我们可以获取一个样本数据点,并指定我们的输入数据点以及内容类型(JSON、CSV)。
引起
您可以通过模型签名进一步添加更多定制来定义您的输入和输出。您可以将模型签名添加到 log_model 调用中,以便根据定型数据自动推断输入数据,或者指定您的列/输入数据功能。
在这个例子中,我们将直接调用 REST API 和一个示例数据点,如上面的 shell 命令所示。在另一个 shell 中运行该命令,您应该会看到推论。

推论(作者截图)
4.其他资源和结论
https://github.com/RamVegiraju/mlflow-serving
MLflow 是一个非常强大的工具,涵盖了 ML 生命周期的每一步。您可以使用它来构建端到端的 ML 工作流,但更重要的是,它还为您提供了选择 ML 项目中您想要关注的步骤的灵活性。
我希望这篇文章是对使用 MLflow 托管模型的一个很好的介绍,我们将在接下来的文章中探索该平台的不同方面。
额外资源
如果你喜欢这篇文章,请在LinkedIn上与我联系,并订阅我的媒体 简讯 。如果你是新手,使用我的 会员推荐 报名。
用于批处理的 MLOps:在 GPU 上运行气流
原文:https://towardsdatascience.com/mlops-for-batch-processing-running-airflow-on-gpus-dc94367869c6
阿帕奇气流限制的简单变通方法

有很多 MLOps 平台本身就处理 GPU 访问,但我们喜欢我们的一些任务的简单性。本文着眼于一种允许同时使用 PyTorch 和 Tensorflow 堆栈的方法。
已经有一些关于这种类型的实现的文章,但是 为 Batch 创建自己的 AWS AMI 或者使用其他依赖于特定于云的平台的方法并不是解决这个看似简单的问题的最佳方式。
背景和动机
如果你已经花了很多时间将你的模型打包到一个漂亮的小容器中,并将它们绑定到你的 CI/CD 管道,那么偶尔你的模型会需要原始的 GPU 能力。对于大批量作业来说尤其如此,这是我们在 AuditMap 内部审计和企业风险客户中经常看到的情况。此外,我们在大多数 NLP 项目中使用大量基于 BERT 的模型进行数据预处理和清理,因此在 GPU 上进行批处理推理工作很有意义。
但是,有一些现成的限制,我们将在本文中讨论和解决。
气流
Apache Airflow 是一个开源的任务调度器和工作流管理器。与端到端 MLOps 解决方案相反,它只做一件事,即运行良好的作业。Airflow 自带多种任务类型,包括 PythonOperator 和 DockerOperator 。这些特定的操作符意味着您可以分别运行 Python 函数或旋转容器。
在这些条件下,相信 DockerOperator 能够提供所有本机 Docker 功能(包括目标设备分配)是明智的。然而,截至 2021 年 10 月,仍没有关于该特定能力的重大开发活动。现在,关于 Airflow Github 页面上的 device_request 参数已经有了很多讨论,但是无论是他们的文档还是他们的源代码都没有启用这个特定的参数。
我仍然希望我的团队在 GPU 上运行气流 Dag,所以我们在这里。
变通办法
虽然 Airflow 已经在内部使用了docker-py,但是我们将把它作为一个外部依赖项来安装,并通过 Python 以编程方式调用 Docker 守护进程。从那里,外部容器将在目标 GPU 上以单次运行脚本的形式执行它们的代码。
这需要一个包含 python 脚本的外部容器,该容器可以访问目标数据(下面将详细介绍)。然而,考虑到一些结构上的最佳实践,我们现在有了一个可以作为气流任务运行的容器。

呼叫逻辑的图表视图。
所以我们正在做的是在一个外部容器中运行一个 Python 脚本,这个脚本本身被一个 Airflow Python 任务调用。这避开了 Airflow 的 DockerOperator 限制,并从本机 Docker 引擎进行调用。
来自 Giphy 的 Spotify 拍摄的《侏罗纪公园心灵爆炸》GIF。
示例:基本 DAG
为了让我们熟悉一下,下面是一个简单的 nvidia-smi 测试,它将在一个外部容器中运行(在本例中,tensor flow:2 . 7 . 0-GPU):
让它工作
假设您的起点是 Airflow 的docker-compose . YAML文件,下面是要做的更改:
- 添加 docker-py 作为 PIP 要求:
x-airflow-common:
...
environment:
...
_PIP_ADDITIONAL_REQUIREMENTS:
${_PIP_ADDITIONAL_REQUIREMENTS:-**docker==5.0.3**}
- 安装包含 Docker 插座的卷:
x-airflow-common:
...
volumes:
...
**- /var/run/docker.sock:/var/run/docker.sock**
(注意:如果您最终使用 DockerOperator,那么它可以作为一个参数包含进来。)
- 确保设置您的 AIRFLOW_UID 。
从那里开始,假设有适当的隧道并且没有端口冲突,您可以运行docker-compose up来让气流运行。主 web 服务器现在运行在端口 8080 上。如果您从我们的 Github repo 中获取了代码,那么将会出现两个新的 Dag:

作为我们回购的一部分。
运行gpu_test DAG 给了我们很多信心。
结果
正如所料,我们有一个不错的 DAG 运行成功和一个完成日志与一个 nvidia-smi 打印输出:

成功的 DAG 运行图。太美了。

nvidia-smi 从 check_gpu()调用中记录。
高级脚本
下一步是创建一个容器,它可以:
- 持有生产模型;
- 运行推理代码;和
- 加载目标数据并保存结果。
如果您从容器化设计开始,那么将所有推理任务分配给一个脚本会变得更容易。让我们创建一个简单的多任务推理脚本,它可以接受参数。我们将从 Helsinki-NLP 创建一个简单的翻译管道。我们还将使用来自 Kaggle 的一些样本数据来启动测试过程。
这个脚本现在可以执行一个带有参数的推理任务。让我们更新我们的 DAG 来进行呼叫:
结果
成功!

又一次成功的气流运行。

确认成功运行的日志。
在这个操作过程中,GPU 确实受到了影响:

nvidia-smi 打印输出。
后续步骤
在成功运行之前,您需要清理一些基础设施项目:
- 映射数据文件夹和数据源。
- 加载正确的模型。
- 确保 nvidia-docker 和 GPU 访问在你的目标机器上可用。
您还应该使用 MLFlow、PMML 或任何其他高质量工具以编程方式添加模型,作为您的 CI/CD 管道的一部分。
讨论
虽然有点复杂,但这种设计模式允许各种推理活动之间的大量可互换性。它还允许将模型作为 CI/CD/MLOps 管道的一部分进行分离,因为容器在启动时将始终采用最新的模型(如果模型是从外部卷加载的)。
防止 GPU 资源争用需要一点警惕,但我们在 BERT 模型上的经验告诉我们,您可以在每个模型 6–8gb 的内存预算下轻松运行多个模型。(但是,确保为 Tensorflow 设置增量内存增长。)
笔记
- 我们确实在一个 Tensorflow 容器中运行了一个 Torch 示例。这是因为 Tensorflow 容器倾向于非常整洁地预安装 CUDA 库。
- 确保根据气流安装说明设置您的气流 _ 流体参数。
- Github 资源库可从这里获得:https://github.com/lemay-ai/airflow_gpu
这篇文章的灵感来源于Aquater上的stack overflow。干得好阿奎特。
流水线作业快乐!
马特。
如果您对本文或我们的 AI 咨询框架有其他问题,请通过LinkedIn*或通过* 电子邮件 联系。
你可能喜欢的其他文章
- 数据集偏差:制度化的歧视还是足够的透明度?
- 人工智能如何创造价值?
- 实施企业人工智能战略
- 离群点感知聚类:超越 K 均值
- 深度学习图像分类器的罗夏测试
MLOps:如何运营电子商务产品推荐系统

介绍
电子商务业务中最常见的挑战之一是建立一个性能良好的产品推荐和分类模型。产品推荐器用于向用户推荐相似的产品,从而增加每个用户在平台上花费的时间和金钱。还需要有一个模型来对产品进行正确分类,因为在这些平台上可能会有一些错误分类的产品,特别是在大多数内容是由用户生成的情况下,如分类网站。产品分类模型用于捕捉这些产品,并将它们放回正确的类别,以改善平台上的整体用户体验。
本文有两个主要部分。在第一部分中,我们将讨论如何建立一个电子商务产品推荐系统,并将通过一些动手编码练习进行产品分类。在第二部分,我们将讨论如何在一个名为层的 MLOps 平台的帮助下,通过几个步骤来操作这个项目。
简要方法

方法简单明了(图片由作者提供)
我相信大多数电子商务平台收集用户的点击流数据,这基本上是一个简单的表格,由 3 列组成:会话 id、产品 id 和时间戳。实际上,这个表是为您的企业创建本文中描述的产品推荐模型所需的唯一数据。在整个教程中,我们将使用一个公共 Kaggle 数据集 ( CC0:公共域),这是一个电子商务商店的点击流数据,您可以在下面找到链接。[1]
https://www.kaggle.com/datasets/tunguz/clickstream-data-for-online-shopping
Word2Vec 算法是这种方法的核心,用于生成产品嵌入。单词 2Vec 主要在 NLP 和文本上下文中使用。在这个上下文中使用单词 2Vec 有一个类比。产品将被视为一个单词,一系列产品视图(会话)将被视为一个句子。Word2Vec 算法的输出将是乘积的数字表示向量。
在下一步中,这些产品向量被输入 K-Means 算法,以创建任意数量的产品聚类。这些聚类代表相似产品的分组(分类)。
在最后一步,我们将从给定产品所属的集群中随机选择一些产品推荐。
本文更像是一个包含一些编码示例的教程。要了解更多关于这个方法的信息和了解这个项目的故事,我们强烈建议你也阅读这篇文章。
目录
第一部分:实践示例
第一步:将 csv 文件加载到熊猫数据框中
第二步:将点击流数据转换成产品视图序列
第三步:使用 Word2Vec 算法生成产品向量(嵌入)
步骤四:在产品向量上拟合 K-均值模型(嵌入)
步骤五:将聚类保存为数据框
第六步:获取给定产品的类似产品推荐
第二部分:MLOPS
层安装和登录
图层数据集装饰器
层模型装饰器
层运行环境模式
集成了层的全笔记本
第一部分:实践示例
第一步:将 csv 文件加载到熊猫数据框架中
定义一个名为的简单函数 raw _ session _ based _ click stream _ data,该函数从 csv 文件所在的位置读取该文件并返回一个 Pandas DataFrame 。
功能#1:基于原始会话的点击流数据()
raw_clickstream = raw_session_based_clickstream_data()raw_clickstream.head(5)

样本数据记录(图片由作者提供)
第二步:将点击流数据转换成产品视图序列
定义一个名为generate _ sequential _ products的函数,该函数从前一个函数的输出中提取数据帧 raw_clickstream ,并应用一些数据清理,例如重命名一些列并删除只有一个产品视图的会话。之后,它按照 session_id 列对数据进行分组,并为每个会话创建产品列表。在按会话对产品视图进行分组时,使用数据中的 order 列很重要,因为产品视图的序列必须按时间顺序排列。如果您的数据中只有产品视图的时间戳,您应该首先使用时间戳列创建这样一个单独的订单列。
还有一个名为 remove_consec_duplicates 的帮助函数,它从产品视图序列中删除任何连续的重复产品。这尤其重要,因为我们将在下一部分使用 Word2Vec 算法来生成产品嵌入。您的数据中很可能会有许多连续的重复产品视图,这可能会扭曲算法。
函数# 2:generate _ sequential _ products()
session_based_product_sequences = generate_sequential_products()session_based_product_sequences.head(5)

样本数据记录(图片由作者提供)
第三步:使用 Word2Vec 算法生成产品向量(嵌入)
定义一个名为create _ product _ embeddings的函数,该函数从先前函数的输出中获取数据帧session _ based _ product _ sequences,并通过将参数窗口大小设置为 5 并将嵌入大小设置为 10 来训练 Gensim 的 Word2vec 模型。此函数返回一个两列数据集,其中第一列是产品 id,另一列是从 Word2Vec 模型返回的 10 维数值向量。
函数#3:创建产品嵌入()
product_ids_and_vectors = create_product_embeddings()product_ids_and_vectors.head(5)

样本数据记录(图片由作者提供)
步骤四:在产品向量上拟合 K-均值模型(嵌入)
定义一个名为 fit_kmeans 的函数,该函数使用上一步生成的产品向量数据帧 product_id_and_vectors 来训练 k 均值模型。在下面的代码片段中,我们将聚类数设置为任意数字 10。但是,您可以根据平台上应该存在的类别总数来决定集群的数量。
我们还创建了两个不同的图,作为另外两个辅助函数的结果:plot _ cluster _ distribution和 plot_cluster_scatter。第一个创建了一个可视化图,以条形图的形式显示了集群成员数量的分布,第二个创建了一个散点图,显示了集群在 2D 空间中是如何形成的,并用黑点标记了它们的质心。
函数#4: fit_kmeans()
model = fit_kmeans()

看看这些图在图层上的样子:https://app . Layer . ai/Layer/Ecommerce _ Recommendation _ System/models/clustering _ model # Product-Distribution-over-Clusters(图片由作者提供)
步骤五:将聚类保存为数据框
定义一个名为save _ final _ product _ clusters的函数,该函数创建一个数据帧来存储每个集群的成员列表。该函数使用前一函数的模型和 create_product_embeddings 函数输出的 product_ids_and_vectors 数据帧。因为我们之前将聚类数设置为 10,所以在我们的例子中,数据集中总共有 10 行。我们已经知道,在这种情况下,一个集群成员就是一个产品 id。
函数#5:保存最终产品群集()
cluster_members_df = save_final_product_clusters()cluster_members_df.head(10)

样本数据记录(图片由作者提供)
第六步:获取给定产品的类似产品推荐
现在,让我们编写一个代码块,为特定的产品 id“A13”获取一些类似的产品推荐。
为此,首先我们需要从 product_ids_and_vectors 数据帧中获取该产品的代表性数值向量,并将其提供给模型以获得其分配的集群编号。然后,我们将获取产品“A13”所属的集群的成员列表。最后一步,我们将从该集群中随机选择 5 个相似的产品,瞧,我们完成了!
演示的代码片段
输出将类似于:
5 Similar Product Recommendations for A13: ['C17', 'P60', 'C44', 'P56', 'A6']
第二部分:MLOPS
层是一个协作的机器学习平台,自带一些预定义的功能装饰器。作为用户,你所要做的就是根据你的函数的返回数据类型,用一个层装饰器(数据集 & 模型装饰器)包装你的 Python 函数。例如,如果您的函数返回一个数据集,并且您希望 Layer 跟踪它,那么用 Layer dataset decorator 包装它,Layer 将自动开始对您的数据集进行版本控制。模型的步骤也是一样的。如果你的函数返回一个 ML 模型,那么用层模型装饰器包装它,层将在你每次运行同一个笔记本时自动开始对你的模型进行版本控制。
层安装和登录
让我们从用几行代码安装并登录到层开始。然后,使用‘Layer . init(your _ project _ name)’在图层上初始化你的项目。
!pip install layer
import layer
from layer.decorators import dataset, modellayer.login()
layer.init("Ecommerce_Recommendation_System")
图层数据集装饰器
让我们将本教程中的第一个函数raw _ session _ based _ click stream _ data用图层数据集装饰器' @ dataset(dataset _ name)]'包装起来,并为您的图层数据集命名为:“raw _ session _ based _ click stream _ data”。您还可以使用' layer.log()' 记录其他类型的数据和数据集,如下面的代码片段所示。
从现在开始,Layer 将跟踪从函数返回的数据集,记录其他数据类型以及数据集,并自动对其进行版本控制。这意味着每次运行这个函数,它都会创建一个新版本的数据集。这样,Layer 将使您能够在 Layer Web UI 上看到相同数据集的整个旅程,如下图所示。

图层数据集页面截图(图片由作者提供)
您可以在页面的左侧看到数据集版本的列表,在右侧看到一些数据配置文件信息。在名为“Logged data”的选项卡下,您将看到与数据集一起记录的所有其他数据。
图层模型装饰器
现在,让我们为模型做同样的过程。这一次,你要用图层模型装饰器' @ model(model _ name)]'包装你的模型函数: fit_kmeans() ,并给你的图层模型起个名字: "clustering_model" 。您还可以使用'layer . log()'【T25]记录其他类型的数据,如下面的代码片段所示。
上一节第四步中的代码块和下面的代码块之间唯一的区别就是多了 3 行特定于层的代码。
从现在开始,层将跟踪和版本化你的模型以及记录所有其他数据。它将使您能够比较模型的不同版本,在失败的情况下转换回任何以前的模型版本,并持续监控您的模型性能。这里有一张截图,取自 Layer WebUI 上的一个模型页面。

图层模型页面截图(图片由作者提供)
层运行环境模式
该层有两种运行环境模式:本地和远程。
本地模式:在本地模式下,您将像往常一样按照您希望的顺序调用您的函数,代码将使用您自己的计算能力在您的本地计算机上运行。此模式仍会将所有数据记录到图层的远程主机,例如运行过程中创建的数据集或模型。
# LAYER LOCAL MODEraw_session_based_clickstream_data()
generate_sequential_products()
create_product_embeddings()
fit_kmeans()
save_final_product_clusters()
远程模式:在远程模式下,你将把你所有的 Python 函数名放入‘Layer . run()’中,这将利用层的资源远程运行你的代码。通过这种方式,您可以轻松地利用层机器和 GPU 的巨大计算能力来运行您的深度学习项目。
# LAYER REMOTE MODElayer.run([raw_session_based_clickstream_data,
generate_sequential_products,
create_product_embeddings,
fit_kmeans,
save_final_product_clusters],debug=True)
如果在远程模式下使用 Layer,建议在装饰签名中显示数据集或模型之间的依赖关系。例如,在下面的示例代码中,模型“clustering_model”依赖于数据集“product_ids_and_vectors”,而数据集“final_product_clusters”依赖于数据集“product_ids_and_vectors”和模型“clustering_model”。
#MODEL DECORATOR WITH DEPENDENCIES@model("clustering_model",dependencies=[Dataset("product_ids_and_vectors")]) #DATASET DECORATOR WITH DEPENDENCIES@dataset("final_product_clusters", dependencies=[Model("clustering_model"), Dataset("product_ids_and_vectors")])
这只是对层的一个快速介绍。有关 Layer SDK 和其他功能的更多信息,请访问:
集成了层的全笔记本
让我们将所有代码块放在一个 python 笔记本中。下面是完整版的电商产品推荐系统笔记本,带层集成:
您也可以通过单击下面的链接查看此项目在图层上的外观:
感谢阅读!您的反馈很有价值。请在下面的评论区和我们分享你的想法。
参考文献:
- apczy ski m .,Bia ow s……s .(2013)发现用户在电子商店中的行为模式——波兰和其他欧洲国家消费者购买行为的比较,《经济研究》,第 151 期,《信息社会:欧洲和全球视角:公民和消费者的互联网使用和风险》,第
10 分钟后起飞
原文:https://towardsdatascience.com/mlops-in-10-minutes-165c746a9b8e
MLOps 如何在 ML 项目的所有阶段提供帮助

MLOps 是 ML 的 DevOps(图片由作者提供)。
一个常见的误解是,MLOps 仅仅是我们用来部署模型和准备基础设施的工具。部分是,但这不是全部——还有更多。在这篇文章中,我将把机器学习项目分成几个阶段,并解释 MLOps 如何在每个阶段提供帮助。
MLOps 是一个新的话题,对于它是什么或不是什么还没有一致的意见。在这篇文章中,我将分享我个人对此的看法。你不必同意它,但我希望它仍然有用。
所以我们开始吧!
MLOps
MLOps 是一套将机器学习投入生产的实践。让我们看看它们是什么。
为了做到这一点,我们将从 ML 项目的典型过程的直升机视图开始。最简单的形式是,它包括 3 个阶段:
- 设计
- 火车
- 操作

我们可以把一个 ML 项目分解为三个阶段:设计、培训、运营(图片由作者提供)。
它从设计阶段开始:我们理解问题是什么,并决定 ML 是否是正确的解决方案。
如果我们认为我们需要 ML,我们训练模型。这是火车状态。
准备就绪后,我们需要定期将模型应用于新数据。这是操作阶段。
火车
在训练阶段,我们用不同的模型进行实验,并试图找到模型的最佳参数和特征集。

训练模型并寻找最佳参数(图片由作者提供)。
大多数数据科学家都是在 Jupyter 笔记本上做的。
做实验的典型流程是“改变一个参数→重新执行单元格→看看结果有没有提高”。在一些迭代器之后,它使笔记本变得一团糟:你不能容易地跟踪变化、参数和结果。此外,以后重现这些结果也变得非常重要。
两件事有助于我们处理这些问题:
- 实验跟踪
- 培训渠道
实验跟踪
在实验跟踪中,我们建立了保存每个实验结果的过程。每次我们试验一组新的参数,或者一个新的特性,我们都用一个试验跟踪工具记录参数和结果。稍后,我们可以使用它来查看哪些设置导致了最佳模型,以及不同的参数如何相互比较。

跟踪有助于在试验不同的特性、模型和参数时保持理智(图片由作者提供)。
跟踪实验的工具有很多。最受欢迎的是 MLFlow 。
培训渠道
实验跟踪并不能解决我们所有的训练问题。我们仍然有杂乱的 Jupyter 笔记本,其中的单元格需要按照特定的顺序执行才能得到最终的解决方案。
为了解决这个问题,我们将笔记本分解成一组一个接一个执行的构建块。我们将这种块的序列称为“机器学习流水线”。

机器学习管道是需要按照特定顺序执行的一系列步骤(图片由作者提供)。
一旦我们将训练过程表示为一个管道,我们只需点击一下鼠标,就可以执行它并根据新数据重新训练模型。或者根本没有任何点击。
在实践中,您可以通过将笔记本转换成 Python 脚本,然后将其分解成几个函数来实现。对于更高级的管道,您可以使用通用的工作流编排工具,如 Airflow 。也有专门的面向 ML 的工具,如 Kubeflow Pipelines 。
总之,在训练阶段,MLOps 有助于再现性和自动化模型训练和评估。

实验跟踪有助于自动化评估,培训管道有助于自动化培训(图片由作者提供)。
操作
ML 管道的输出是一个模型——通常是一个 pickle 文件。现在,我们需要采用该模型,并开始将其应用于新数据。
这个过程被称为模型部署。
部署
根据一个用例,我们可以用两种方式部署我们的模型:批处理和在线。
在批处理模式中,我们不需要立即对所有新数据做出反应。相反,我们定期分批处理数据。例如,我们可以每小时、每天或每周执行一次。

以批处理模式应用模型的一种典型方式是为昨天生成的数据运行它(图片由作者提供)。
我们也以批处理模式运行训练管道,这就是为什么批处理部署通常成为 ML 管道的扩展。我们通常只使用简单的 Python 脚本,这些脚本可以作为 Kubernetes 作业或 AWS 批处理作业来执行。或者有时我们可能会使用 Spark。
批处理模式是使用 ML 模型最常见的方式。对于很多情况来说,这很简单,也足够了。
与批处理相反,在在线模式下,我们需要在新数据出现时立即做出反应。它通常比批处理更复杂,因为模型服务需要始终启动和运行,随时准备处理新数据。

部署 ML 模型的两种方式:批处理和在线(图片由作者提供)。
我们可以进一步将在线案例分解为两种部署 ML 模型的方式:web 服务和流。
在第一个示例中,我们将模型部署为 web 服务。我们模型的用户发送带有特性的 HTTP 请求(通常以 JSON 格式)并获得预测。

作为 web 服务部署的模型总是准备好用预测来响应(图片由作者提供)。
为了将模型部署为 web 服务,我们使用像 Flask 或 FastAPI 这样的库,并在 Kubernetes 或类似的系统上运行它们。也有更专业的面向 ML 的解决方案,如 KServe 。
在流的情况下,我们的模型成为事件流的消费者。
每当有新事件发生时,服务就会对其做出反应并应用该模型。预测被保存到另一个流中。我们的用户现在可以订阅输出流,并根据这些预测做出决策。

在流的情况下,服务之间的通信通过流进行。模型服务对一个流中的事件做出反应,并将预测放到另一个流中(图片由作者提供)。
通常,我们使用 Kafka 这样的消息代理进行流式传输,并作为 Kafka 消费者实现模型服务。除了卡夫卡,我们可以使用其他流媒体平台,如 Kinesis。
模型监控
模型部署后,我们的工作并没有结束。当它上线时,我们需要密切关注它,确保它保持正常运行。
首先,我们监控传统的 DevOps 指标:
- CPU 单元化
- 记忆
- 网络使用
- 每秒的请求数
- 和其他人。
如果某个指标超过某个阈值,我们会向支持团队发送警报,支持团队会迅速对这些问题做出反应并修复它们。
但这还不够。我们还需要确保我们的模型的预测仍然是好的,并且模型不会变得陈旧。这就是为什么我们需要模型监控。

监控有助于我们发现模型行为的变化。松弛图标来源。
如果我们检测到性能下降,我们可以触发训练管道,并根据新数据对其进行重新训练。
总之,在操作阶段,MLOps 帮助我们部署模型并监控其性能。

在操作阶段,我们选择部署模型并关注它(图片由作者提供)。
人员、流程和最佳实践
到目前为止,我们讨论了培训和操作的实践和工具。这已经很好了,但还不够。
首先,我们需要确保我们了解我们想要解决的问题,并确保 ML 是正确的解决方案。我们需要有条不紊:从目标开始,提出一个基线,然后逐步改进。我们并不总是需要在 Kubeflow 中部署完整的培训管道,在 KServe 中部署模型和最先进的监控系统。相反,我们通常需要从简单开始,首先显示项目中的价值——然后迭代。
流程可以帮助我们做到这一点。有像 CRISP-DM 、 ML Canvas 和 MLOps Canvas 这样的工具和框架帮助我们一起工作并解决问题。

CRISP-DM 是运行 ML 项目的框架之一。它突出了 ML 的迭代性质:我们从简单开始,逐步改进(图片来源)。
MLOps 是“数据科学的 DevOps”。这意味着所有 DevOps 实践仍然适用:
- 用单元测试和集成测试来测试我们的服务
- 自动化一切
- CI/CD
- 使用模板快速入门(例如 cookie-cutter)和 makefiles
- 撰写全面的文档
过程和最佳实践都在项目的所有三个状态中帮助我们。
还有更多
在本文中,我们仅仅触及了表面。这里还有更多我没有涉及的概念:
- 功能商店
- 示范登记册
- 实验平台
可能更多。可以多看看他们,看看他们解决了哪些问题,属于哪个阶段(提示:有的可以属于多个)。
摘要
我们讨论了什么是 MLOps,并从直升机上观察了该过程。我们将流程分为三个阶段:设计、培训和运营。
对于这些阶段中的每一个,我们都看到了 MLOps 是如何帮助我们的:
- 流程帮助我们一起工作,并确保我们带来价值
- 实验跟踪帮助我们在尝试不同的模型参数时保持理智
- 训练管道使得只需几个命令就可以更容易地重现结果和重新训练模型
- 模型部署负责以最佳方式使用模型——无论是以批处理模式、作为 web 服务还是作为流的一部分
- 当模型过时,我们需要重新训练它时,模型监控会提醒我们
- 最佳工程实践保持我们的代码干净可靠
动物园营地

想要获得 MLOps 工具的实践经验吗?(图片由作者提供)
你对学习 MLOps 感兴趣吗?在数据会谈上。俱乐部我们推出了免费的在线课程。我们将涵盖这里讨论的所有主题,并教你如何在实践中应用所有这些概念。
更多信息请点击这里:【https://github.com/DataTalksClub/mlops-zoomcamp】T2。
球场上见!
实践中的 MLOps 将 ML 解决方案架构分解为 10 个组件
全面介绍端到端 ML 解决方案的 10 个关键组件
在我以前的博客中,我谈到了三个关键管道(1)数据和特征工程管道,(2) ML 模型训练和重新训练管道,(3) ML 模型推理和服务管道,以及构建可靠和可扩展的 MLOps 解决方案所需的底层基础设施。你可以在这里找到博客的详细信息:
从这篇博客开始,我将重点解释端到端 MLOps 解决方案的详细实现,为您提供一个实用的剧本,供您在为您的组织设计和实现 ML 驱动的系统时参考。本实施手册包括一个普遍适用的 ML 解决方案架构,对每个关键架构组件的深入研究,以及生产 ML 驱动系统的最佳实践。
让我们从解释一般适用的 ML 解决方案架构看起来像什么开始。

迈克·科诺诺夫在 Unsplash 上的照片
普遍适用的 ML 解决方案架构
ML 解决方案架构描述了一个解决方案在高层应该是什么样子的蓝图,以及为了构建一个可伸缩的和可靠的 ML 驱动的系统所需要的关键组件。当我说一个普遍适用的 ML 解决方案架构时,我的意思是这个架构可以应用于大多数 ML 驱动的系统或用例。从 ML 生命周期的角度来看,这种通用架构涵盖了关键的 ML 阶段,从开发 ML 模型,到部署培训系统和服务系统,再到生产。您可能需要定制其中的一些,但是总体工作流程应该大致相同。
下图描述了端到端 MLOps 解决方案的关键架构组件。总共有 10 个关键组件,下面是每个组件的概要。

普遍适用的解决方案体系结构的 10 个组件|作者图片
- 数据和特征工程管道——在解决方案要求的时间内交付高质量的数据,并以可扩展和灵活的方式产生有用的 ML 特征。一般来说,数据管线可以与特征工程管线分开。数据管道是指提取、转换和加载(ETL)管道,其中数据工程师负责将数据传送到存储位置,例如建立在 S3 桶顶部的数据湖,特征工程管道可以从该处开始。特征工程管道专注于将原始数据转换为 ML 特征,这些特征可以帮助 ML 算法更快更准确地学习。特征工程通常包括两个阶段。在第一阶段,特征工程逻辑通常由数据科学家在开发阶段通过各种实验创建,以便找到最佳的特征套件,而数据工程师或 ML 工程师负责特征工程管道的生产,以便为模型训练和生产环境中的服务提供高质量的特征。
- 特征存储-存储和版本管理的 ML 特征,用于发现、共享和重用,并为模型训练和服务提供一致的数据和 ML 特征,这提高了 ML 驱动的系统的可靠性。特征存储是用于保存由特征工程管道创建的 ML 特征的存储解决方案。特征存储支持模型训练和服务。因此,它是一个非常重要的部分,是端到端 ML 解决方案的重要架构组件。
- ML 模型训练和再训练管道-以简单和可配置的方式使用不同的参数和超参数进行 ML 训练实验,并使用丰富的参数和模型性能指标记录这些训练运行。自动评估、验证、选择性能最佳的模型并将其记录到 ML 模型库中。
- 培训和模型 Metastore —存储和记录 ML 运行的工件,包括参数、指标、代码、笔记本、配置和培训模型的结果,并提供模型生命周期管理、模型注释、模型发现和模型重用等功能。对于一个全面的 ML 解决方案,可能会从数据中生成大量的元数据,并以工程、模型训练到模型服务为特征。所有这些元数据对于获得系统如何工作的更多可见性非常有用,提供了从数据->特性->模型->服务端点的可追溯性,以及当模型停止工作时用于调试的有用信息。
- ML 模型服务管道—根据服务吞吐量和延迟,为在生产环境中使用 ML 模型提供适当的基础设施。一般来说,有 3 种类型的模型服务——批量服务、流服务和在线服务。每种服务类型需要完全不同的基础设施。此外,基础设施应该是容错的和可自动扩展的,以响应请求和吞吐量波动,特别是对于业务关键的 ML 驱动的系统。
- 监控生产中的 ML 模型——当检测到数据和模型漂移和异常时,提供数据收集、监控、分析、可视化和通知功能,并使用必要的信息协助系统调试。
- 机器学习管道-与特定的 ML 工作流相比,ML 管道提供了一个可重用的框架,允许数据科学家更快地开发和迭代,同时保持高质量的代码并减少生产时间。一些 ML 管道框架也提供编排和基础设施抽象功能
- 工作流程编排—集成端到端 ML 驱动系统的所有关键组件。编排和管理所有这些关键组件的依赖关系。工作流编排工具还提供日志记录、缓存、调试和重试等功能。
- 持续集成/持续培训/持续部署(CI/CT/CD) —持续测试和集成代码更改,持续使用新数据培训新模型,并在需要时升级模型性能,以及以安全、敏捷和自动化的方式持续为生产环境提供服务并部署模型。
- 数据和模型的端到端质量控制—在端到端 ML 工作流程的各个阶段嵌入强大的数据质量检查、模型质量检查、数据和概念漂移检测,以确保 ML 解决方案本身可靠可信。这些质量控制检查描述性统计数据、整体数据形状、缺失数据、重复数据、恒定(或几乎恒定)要素、统计测试、距离度量和模型预测质量。
摘要
这些是普遍适用的解决方案体系结构的 10 个组件。到目前为止,您应该对 ML 驱动的解决方案应该是什么样子有了一个高层次的理解。在以后的文章中,我将深入研究每个组件,并具体讨论:
- 每个组件的作用以及为什么这个组件是必需的
- 每个组件的关键设计考虑事项
- 可用于实施每个组件的工具、框架和服务
如果你想在这些文章发表时得到通知,请随时关注我。我一般每周都会发表 1-2 篇关于数据和 AI 的文章。
如果想看到更多围绕现代高效数据+AI 栈的指南、深度潜水、见解,请订阅我的免费简讯— 高效数据+AI 栈 ,谢谢!
注:以防万一你还没有成为一个媒体成员,你想获得无限制的媒体,你可以使用我的推荐链接注册!
非常感谢你的支持!
利用 AWS SageMaker、Terraform 和 GitLab 的 MLOps
原文:https://towardsdatascience.com/mlops-leveraging-aws-sagemaker-terraform-and-gitlab-e7d97eaa6dce
创建可扩展的、可重复的、健壮的和受到良好监控的机器学习管道

这个项目的目标是使用数据驱动的方法和机器学习来为 Ruter 的客户提供个性化和情境化的服务以及顺畅的旅行体验。我们希望我们的客户知道他们计划的旅程中车辆的载客量,以便他们能够根据我们提供的信息做出更明智的决定。
Ruter 通过公共汽车、电车、地铁和船只在挪威奥斯陆和 Viken 地区提供公共交通服务。它每天服务约 50 万次出发,覆盖约 30 万公里,每天运送约 100 万名乘客。我们从公共汽车、电车和地铁上的物联网传感器收集原始数据。这些传感器持续报告不同类型的数据,包括上下车乘客的数量、车内/车外温度以及车辆的实时速度和位置。所有这些来自整个 Ruter 车队的实时流数据由内部开发的后端系统实时接收、处理和存储。

Ruter 的一辆电动公交车上的物联网传感器布局(图片由 Ruter 提供)
收集到的大量数据只讲述了故事的一半,即过去发生了什么。为了实现我们提供个性化和情境化服务以及顺畅旅行体验的目标,我们还必须能够告知顾客未来可能发生的事情。我们利用大数据、机器学习和人工智能来预测和预测这些未来事件。
Ruter 在多个领域使用人工智能,例如,预测容量,乘客数量和旅行模式。人工智能还用于处理客户查询,并为我们的按需服务优化交通路线。为了最大限度地利用人工智能,我们应该能够超越 PowerPoint 演示和可行性报告,必须能够迅速将想法转化为创造价值的应用。
AWS —我们的应用和机器学习平台
我们使用 AWS 作为我们的公共云提供商以及应用和机器学习平台。我们利用卡夫卡和 EKS(AmazonElastic Kubernetes Service)作为我们的实时系统,利用 S3、Glue、Athena 和 SageMaker 作为我们的历史、分析和人工智能用例。我们将深入研究后者。
据 VentureBeat 报道,大约 87%的机器学习模型从未投入生产。许多成功的公司没有在生产中呆很长时间,因为他们不能足够快地适应变化的环境。这种限制的原因之一是缺乏一个健壮的机器学习(ml)平台。在 Ruter,我们投资了一个基于现代云(AWS)的机器学习平台,使我们能够以高质量的速度交付我们的 ml 模型。它围绕 MLOps 原则构建,使我们能够生产稳健、可扩展、可重复和受监控的 ml 管道。
在本文中,我们将讨论我们典型的机器学习管道是什么样子的。
使用 AWS 阶跃函数的机器学习流水线
Ruter 的机器学习团队负责其机器学习模型的端到端生命周期,从数据摄取到模型部署和推理,并通过 Kafka 将这些见解和预测提供给其他团队。我们遵循“我们建设它,我们运营它”的思维模式。因为我们是开发这些机器学习模型的人,所以我们知道应该如何监控和修复它们,以及应该如何衡量它们的性能。因此,我们最好自己操作和管理机器学习管道,而不是将它们交给外部 it 团队进行部署。这样我们可以避免添加多余的依赖项。
如前所述,我们使用 AWS 作为我们的机器学习平台。我们使用额外的工具来实现我们的 ml 管道:GitLab、Terraform、Python 和 GreatExpectations 等等。我们使用 GitLab 进行版本控制、开发和 CI/CD,使用 Terraform 实现基础设施即代码,使用 Python 定义管道定义,并对数据质量监控寄予厚望。
我们在 2021 年第一季度开始了我们的第一个机器学习管道。管道将剥离 AWS EC2(AWS 虚拟机)实例,用于模型训练、批量预测和在 Kafka 上发布这些预测。你可能已经注意到了,这条管道是最小的。它不可复制、不可扩展、不稳定或不受监控。我们需要比这更好的东西来提高速度,增加健壮性并增强开发人员的体验。在回到绘图板并经历了几次迭代之后,我们得出了如下所示的管道。

使用 AWS Step 函数的机器学习管道(图片由作者提供)
管道由以下部分组成。
事件桥: Amazon EventBridge 是一个无服务器的事件总线,它使得使用应用程序生成的事件来大规模构建事件驱动的应用程序变得更加容易。我们用它在特定的时间触发我们的 ml 管道。根据设定的时间,它启动一个λ。
AWS Lambda: AWS Lambda 是一种无服务器、事件驱动的计算服务,它通过自定义输入参数触发 AWS step 函数。
AWS Step Functions:AWS Step Functions 是一个低代码的工作流服务,我们用它来编排机器学习管道中的步骤。
SageMaker 处理和培训作业:我们使用 SageMaker 处理作业进行各种数据处理作业。例如,从 S3 和我们的数据仓库加载数据,数据清洗和特征提取,数据质量控制,批量推理和在 Kafka 上发布结果。我们使用 SageMaker 培训工作进行模型培训。
S3: 我们用 S3 来储存人工制品。在那里,我们存储了原始输入数据、提取的特征、数据质量结果以及报告和模型的快照。
迁移到 AWS SageMaker 管道
虽然 AWS Step Functions 是编排一般工作流和状态机的好工具,但我们发现它对于机器学习管道来说并不理想,因为
- AWS 步骤函数使用 Amazon Stage 语言实现。它是一种基于 JSON 的结构化语言,用于定义状态机。这意味着我们必须在我们的工具集中增加一种语言来实现和维护使用 AWS Step 函数的管道。由于 Amazon State Language 降低了用户的复杂性,它也降低了灵活性和控制力。
- AWS 阶跃函数对管道故障的处理是次优的。如果管道中的一个步骤失败了,您不能只是重新运行失败的步骤,并从您停止的地方开始。你必须重启整个管道。
- AWS step 函数不提供机器学习特定的功能,如实验跟踪、模型注册和开箱即用的端点。
我们将管道迁移至 SageMaker 管道,该管道提供了缺失的功能并解决了阶跃函数的限制:
- 您可以用 Python 定义整个管道,这让数据科学家和 ML 工程师感到如鱼得水。
- 如果管道中的某个步骤失败,您可以重试管道,它将从失败时停止的地方开始。
- SageMaker pipeline 提供了实验跟踪、模型注册和终端开箱即用等功能。
我们当前使用批处理推断的典型管道如下所示。

机器学习管道利用 SageMaker 管道,与 Slack、Kafka 和 S3 集成。(图片由作者提供)
这些管道中的每一步都使用 AWS CloudWatch 发出日志和指标,这是 AWS 的默认监控和可观察性服务。根据这些日志和指标,生成警报并转发给特定于项目的松弛通道。这确保了我们是第一个知道任何停机或故障的人。我们要么在下游用户注意到问题之前修复它,要么如果修复需要时间,我们可以提前通知他们。这些指标也显示在仪表板中(我们使用 Cloudwatch 和 DataDog),这有助于我们有效地监控管道和调试问题。此外,每一步都配置了指数补偿和重试。如果一个步骤失败,它将重试可配置的次数,然后放弃并使整个管道失败。
使用 SageMaker 管道的另一个好处是 SageMaker 工作室。它提供了一个基于 web 的可视化界面,您可以在其中执行所有 ML 开发步骤,从而提高数据科学团队的工作效率。使用工作室,我们可以对我们所有的管道有一个概览。我们获得当前和所有以前执行的状态,以及它们的开始和运行时间。我们还可以找到管道参数、容器版本等。,用于每次执行。我们还可以重试失败的管道执行,开始新的执行或调查以前的运行。每个管道执行被锁定到用于训练和推断的数据的快照。利用 SageMaker 和数据快照,我们可以使用完全相同的数据、模型和参数重新运行之前执行的管道。

来自 SageMaker 工作室的片段(图片由作者提供)

管道的详细概述(图片由作者提供)
值得一提的是,尽管 SageMaker 提供了许多功能,但我们仍然清醒地使用 SageMaker 管道,只使用我们认为能够为我们的服务和用例增加价值的功能。从它提供的一整套功能来看,还有很多需要探索和尝试的地方。
与 Terraform 和 GitLab 集成
我们使用开源基础设施 Terraform 作为代码(IAC)工具,用于我们所有的云基础设施和管道实施。我们遵循标准的 DevOps 实践,Terraform 使我们能够在开发、测试、试运行和生产环境中拥有基础架构的精确副本。
不幸的是,AWS Terraform 提供商(截至 2022 年 8 月)没有 SageMaker 管道资源。为了避免这个问题,我们使用 AWSCC Terraform 提供者来部署我们的管道,它有一个 SageMaker 管道资源。
如上所述,我们使用 Python 定义管道,即 SageMaker 管道 Python SDK。我们创建一个带有管道定义的 Python 脚本,它在运行时以 JSON 的形式返回一个管道定义。这个脚本由 Terraform 通过外部数据源执行,返回的管道定义被发送到 AWSCC SageMaker 管道资源。这样,我们可以让我们的 SageMaker 管道由 Terraform 控制,同时允许我们使用 Python 编写管道定义。

(图片由作者提供)
我们使用 GitLab CI/CD 和 Terraform 来构建、更改和管理我们的基础设施和管道。我们以这样的方式设计设置,当开发人员创建一个新的开发分支时,GitLab CI/CD 会自动为该项目创建一个完全隔离的沙盒开发基础设施。然后,开发人员可以使用这个开发基础设施进行开发和实验,而不用担心资源或影响任何其他服务。一旦开发完成,dev 分支被合并到 test 分支(我们使用 test、stage 和 main 分支作为基础设施),dev 基础设施及其所有的资源和数据都会被 GitLab CI/CD 销毁。这为我们的开发人员提供了以模块化方式工作所需的灵活性和速度,并降低了实验和尝试新想法的门槛。

使用 GitLab 的 CI/CD 和 Terraform 创建和删除开发基础设施。(图片由作者提供)
模型性能监控
到目前为止,一切顺利。我们完全控制我们的管道和工作流程。如果出现问题,无论是外部 API 故障、数据质量问题还是失败的作业,我们都有系统通过 Slack 频道和电子邮件立即通知我们。但是,一旦模型得到部署,并在野外,我们如何知道它是否产生高质量的预测和有价值的信息?
为了回答这个问题,我们使用了另一个 AWS 服务。我们使用 Amazon QuickSight 来支持我们的模型性能监控仪表板。这些仪表板显示由我们的 ml 模型产生的预测和基本事实(当它到达时)以及定制生成的指标,这有助于我们评估 ml 模型在实际运行中的性能。例如,使用这个仪表板,我们可以深入到单个出发,并检查模型是否预测了这些出发的正确机上乘客人数。我们还监控模型漂移,如果模型漂移超过定义的阈值,QuickSight 就会触发警报。由于这些仪表板的所有后端处理都是在 Terraform 中实现的,我们可以通过为我们的开发分支自动生成开发仪表板来执行 A/B 测试。这给我们的开发人员带来了很大的力量,他们可以进行多个实验,每个实验都有相关的仪表板来监控他们的 ml 模型在生产中的性能。

模型性能监控仪表板。(图片由作者提供)
未来计划和改进
我们对平台和渠道的现状相当满意,但仍有改进的空间。我们目前没有充分发挥 SageMaker 的潜力,我们认为仍有一些功能可以为我们现有的管道和工作流程增加价值。例如,我们希望更积极地使用 SageMaker 进行实验跟踪。我们想尝试的另一个功能是使用 Amazon SageMaker 功能库,这是一个完全托管的、专门构建的存储库,用于存储、共享和管理机器学习(ML)模型的功能。增强开发人员的体验(提高速度和降低复杂性)是我们未来开发和增强的主要焦点。
我于 2020 年 6 月加入 Ruter,从那时起,我就在数据科学部门管理机器学习和数据工程团队。 在大奥斯陆地区,我们尝试利用先进的统计学和 ML 结合现代数据堆栈来改善公共交通和客户体验。
感谢 David sklid Amundsen、Daniel Haugstvedt、Erlend Fauchald 和 Simen W Tofteberg 让这一切成为可能。
机器学习生命周期综述
原文:https://towardsdatascience.com/mlops-machine-learning-lifecycle-b168cc39e52f

在藏传佛教中,永恒结象征着生、死、再生的无尽循环,也象征着智慧和方法的结合,以及万物的最终统一。图片来源:维基共享资源
MLOps
ML 生命周期从资源受限的批量数据挖掘到云规模的 MLOps 的演变
一年多来,每个人都在谈论 MLOps。我四处寻找生命周期和过程是如何发展的。
从数据中寻求洞察力的学科已经存在了 25 年。当时,它被称为数据挖掘。在这篇文章中,我介绍了 ML 生命周期过程的概况,并以我对它的看法作为结束。所以如果你很着急,跳到最后一节找TL;博士。
数据挖掘/科学的主要步骤大致保持不变:
- 了解领域或业务问题
- 从各种来源收集所需的数据
- 整理数据,清理并贴上标签
- 转换数据,协调数据,塑造数据
- 探索并可视化数据
- 训练一个模型,验证它,并调整超参数
- 使用或部署模型
但是数据、处理和应用程序的性质已经发生了显著变化:
- 规模:分析的数据量增加了流形。
- 广泛使用: ML 驱动的应用程序是我们日常生活的一部分,我们非常依赖它们。
- 批处理与在线:这些模型在批处理模式中被更早地使用,以获得洞察力并指导业务决策。现在,更多的模型被用于大规模的推理。
演变可以大致分为 3 个时代(时间线重叠):
- 批处理时代: ETL 管道将数据从运营系统带到数据仓库和数据集市,之后进行数据挖掘。
- 大数据时代:数据对于当时的仓库来说变得太大了。数据在数据湖中流动,而数据湖经常变成沼泽。只有少数组织部署了在线模型。
- MLOps 时代:让每个人都可以轻松的持续部署线上模型。
总有机碳
批量时代
你可以称之为古代。互联网仍处于萌芽阶段。大多数企业应用程序批量生成和处理数据。
应用程序和数据分散在组织的各个部门。面临的挑战是将所有这些整合在一起,使整体大于部分之和。
数据建模“写模式”非常重要。批处理 ETL 数据管道将数据带到一个集中的数据仓库。它被聚合并存储在数据集市中,每个数据集市都针对特定的业务线、部门或主题领域。
数据不是很大。具有面向列的索引的 RDBMS 很方便,OLAP 立方体统治了时代。
数据挖掘主要是一种幕后操作。它的作用是提取商业决策所需的洞察力。所以当时的过程反映了这种数据挖掘的批处理模式。
KDD:数据库中的知识发现
从数据中提取洞察力先于大数据。KDD 过程(知识发现和数据挖掘:走向统一的框架)。艾尔。,1996)是最早定义数据库中数据挖掘框架的人之一。 KDD 流程有 5 个阶段,有反馈:
- 选择:选择数据集、变量子集或数据样本
- 预处理:清洗数据,处理缺失值等。
- 变换:特征选择和维度投影,减少有效变量数。
- 数据挖掘:应用特定的挖掘方法,例如汇总、分类、回归、聚类。
- 解释&评估:提取模式/模型,将其与数据可视化一起报告。
现代的数据管道有着几乎相同的步骤。

数据库中的知识发现(KDD)过程。图片由作者提供。
CRISP-DM:数据挖掘的跨行业标准过程
然后出现了 CRISP-DM 过程(CRISP-DM 模型:数据挖掘的新蓝图,由 Colin Shearer 于 2000 年出版),这一过程直到今天仍然具有影响力( CRISP-ML(Q) )。它描述了“数据分析师”应该如何从业务需求出发,挖掘数据,并“部署”模型。它将数据挖掘过程分为六个主要阶段:
- 商业理解:确定商业目标。评估资源、需求、限制、风险和意外情况。定义数据挖掘目标并制定项目计划。
- 数据理解:收集初始数据,发掘数据,验证数据质量。
- 数据准备:选择并清洗数据。添加派生属性和生成的记录。合并数据,并根据所需的模式对其进行整形。
- 建模:建立模型,并评估其质量。
- 评估:评审模型的构建,以确保它实现了既定的业务目标。
- 部署:生成报告,或者在整个企业中实现可重复的数据挖掘过程。计划数据挖掘结果的监控,以及数据计时过程的维护。
“部署”是它领先于时代的地方。没有办法将模型部署为推理功能。说明(这里的客户是指分析师的客户,即业务组织/经理):
获得的知识必须以客户可以使用的方式组织和呈现,这通常涉及在组织的决策过程中应用“实时”模型,如网页的实时个性化或营销数据库的重复评分。
根据需求的不同,部署阶段可以简单到生成一个报告,也可以复杂到在整个企业中实现一个可重复的数据挖掘过程。尽管执行部署步骤的通常是客户,而不是数据分析师,但是让客户预先了解为了实际利用创建的模型必须采取什么行动是很重要的。

CRISP-DM 流程。图片由作者提供。
SEMMA:采样、探索、修改、建模和评估
SEMMA 代表取样、探索、修改、建模和评估。它是由 SAS Institute 开发的一系列顺序步骤,用于指导数据挖掘应用程序的实施。
- 采样:采样并选择数据进行建模。
- 探索:可视化数据,以发现数据变量之间预期的和未预期的关系,并识别异常。
- 修改:选择并转换数据变量,为建模准备数据。
- 模型:应用各种建模技术准备数据。
- 评估:评估和比较模型的有效性。
SEMMA 阶段似乎介于 KDD 和 CRISP-DM 之间。
大数据时代
数据有一种不可思议的能力,超越了任何存储和处理技术。大数据到来了,而数据仓库不足以处理企业产生的大量数据。因此,我们发明了数据湖(blobs repository)来存储任何规模的原始数据文件。这导致了从“写模式”到“读模式”的转变。
很快,每个人都开始以他们喜欢的格式/模式将他们喜欢的任何数据转储到数据湖中。数据湖变成了数据沼泽。数据的丰富与可用数据的稀缺并存。数据清理成了一件事。
你可以称之为中世纪。数据分析和商业智能的规模不断扩大。数据科学家成为最性感的工作。
数据收集和管道是自动化的,并且基本上每天都在运行。通常,数据分析仪表板会在实时加入批处理和流数据处理时进行更新。但是,大多数组织以批处理模式使用预测模型来指导他们的业务决策和产品,只有少数组织在生产中部署 ML 模型来进行在线实时推理。
生命周期和流程经过调整,包括数据管道、模型训练、验证,甚至(手动)部署的明确步骤。
OSEMN:Ob ain, S crub, E xplore, M odel,I N terpret
希拉里·梅森和克里斯·维金斯在“数据科学的分类学”博文(2010 年 9 月 25 日)中描述了 OSEMN 过程。它有 5 个步骤:Ob 获取, S crub, E xplore, M odel,I N 企业。
- 获取:点击不缩放。
抓取或使用 API 自动收集数据。 - 擦洗:这个世界是个乱七八糟的地方。除非你清理和规范化数据,否则你的模型也会很乱。
- 探索:看可以看到很多。
这就是我们在探索性数据分析(EDA)中所做的事情。 - 车型:总是不好,有时候很丑。
对选择的损失函数进行优化,通过交叉验证选出最佳的。 - 解读: 计算的目的是洞察,不是数字。与算术不同,统计结果需要细致入微的解释。
这个博客现在已经不存在了,但是你可以在网络档案上阅读它。如你所见,它仍然非常像 KDD/克里斯普-DM,但对步骤的解释反映了网络规模的大数据现实。

OSEMN 数据科学过程。图片由作者提供。
TDSP:微软团队数据科学流程生命周期
微软的团队数据科学流程(TDSP)生命周期有四个阶段:
- 商业理解
- 数据获取和理解
- 建模
- 部署
“数据获取和理解”和“建模”阶段被进一步分解为更详细的步骤。它被设想为一个瀑布模型,以客户接受为结束,但不需要太多的想象力就可以扩展到交互。
这几乎是大多数公司目前有意或无意遵循的。

微软团队数据科学流程(TDSP)生命周期。图片由作者提供。
在 ICSE-SEIP 2019 年会议上的一篇论文中,微软研究院的 Saleema Amershi 等人描述了机器学习工作流程的 9 个阶段,这与 TDSP 不同:
一些阶段是面向数据的(例如,收集、清理和标记),而其他阶段是面向模型的(例如,模型需求、特征工程、培训、评估、部署和监控)。工作流程中有许多反馈循环。较大的反馈箭头表示模型评估和监控可能会返回到前面的任何阶段。较小的反馈箭头示出了模型训练可以循环回到特征工程(例如,在表示学习中)。

图片来源:微软研究院 Saleema Amershi 等人的 ML 工作流
MLOps 时代
DevOps 的崛起是现代的特征。当组织定期在生产中部署 ML 模型作为其软件应用程序/产品的一部分时,他们需要一个符合持续集成和持续交付(CI/CD)的 DevOps 最佳实践的数据科学流程。这就是助长 MLOps 炒作的原因。
大多数公司还没有做到这一点,而且我敢说目前需要它。目前,只有 FAANG 这样的大公司需要每小时部署数千种型号,为数百万最终用户提供服务。但是随着 ML 渗透到更多的应用中,公司将开始采用一个持续培训、集成和交付 ML 模型的过程。
ML 遇到 DevOps: 3 个循环
将 ML 融入 DevOps 的一个显而易见的方法是,通过将 ML 添加到 DevOps 无限循环中,并调整 Dev 和 Ops 以适应数据科学,来制作 MLOps 循环。
注意这个循环是如何将数据和模型作为表征数据处理和模型构建的单个步骤的。另一方面,到目前为止讨论的过程只精确地处理了和这两个步骤。在我看来,Ops 在流程中的主导地位是一种倒退。

ML-DevOps 循环。图片由作者提供。
也有其他三环 MLOps 循环的尝试。例如,下面列出了 3 个主要阶段。迭代增量 MLOps 过程有三个主要阶段(仍然是粗略的 IMO):

图片来源:ml-ops.org迭代-增量 MLOps 过程
数据-ML-开发-操作循环
Danny Farah 的一篇博客文章描述了 MLOps 生命周期的 4 个循环,数据、ML、Dev 和 Ops 各一个。我喜欢它有两个原因:
- 它保留了数据和 ML 步骤的细节
- 感觉更熟悉 w . r . t . devo PS 无限循环。
和 DevOps 差不多,但感觉还是不一样。这是一个错过的将开发人员、数据工程师和数据科学家聚集在一起的机会。我提高 ML 项目成功率的 3 个经验是巩固所有权,早期整合,经常迭代。重要的是要有一个单一的生命周期过程,为所有 3 个生命周期以不同的节奏执行提供灵活性。对所有利益相关者的整体可见性减少了意外,从而提高了成功率。

数据-ML-开发-操作循环。图片由作者提供。
谷歌云
如果不讨论拥有大量 ML 服务堆栈的三大云提供商,关于 MLOps 的讨论就不完整。
Google 凭借 Vertex AI MLOps 平台,可以说是最早也是最大的机器学习商店。它在 2021 年 5 月发布了一份名为《MLOps 从业者指南》的白皮书。我引用白皮书中的 MLOps 生命周期部分,该部分描述了以下部分:
- ML 开发:试验和开发一个健壮的、可重复的模型训练程序(训练管道代码),它由从数据准备和转换到模型训练和评估的多个任务组成。
- 培训操作化:自动化打包、测试和部署可重复且可靠的培训管道的过程。
- 连续训练:响应于新数据、代码变化或按时间表重复执行训练管道,潜在地具有新的训练设置。
- 模型部署:将模型打包、测试并部署到服务环境中,用于在线实验和产品服务。
- 预测服务:服务于部署在生产中用于推理的模型。
- 持续监控:监控已部署模型的有效性和效率。
- 数据和模型管理:一个中央的、横切功能,用于治理 ML 工件,以支持可审计性、可追溯性和遵从性

图片来源:谷歌 MLOps 白皮书
亚马逊网络服务
亚马逊是第一个提供端到端 MLOps 平台的公司: SageMaker 。它在 2022 年 6 月发布了白皮书“ MLOps:数据、代码、
和基础设施的新兴趋势,AWS 白皮书”,定义了更简单的生命周期。如此简单,不言自明。它看起来更像 KDD 和克里斯普-DM 而不是 DevOps 循环。

微软 Azure
微软还在 2021 年 8 月发布了一份 MLOps 白皮书“使用 Azure 机器学习的 MLOps”,定义了 ML 生命周期和 MLOps 工作流。它类似于 AWS:简单明了。

来源:微软 MLOps 白皮书
我对 ML 生命周期的看法
这篇文章比我想象的要长得多。所以谢谢你耐心看完。如果这里跳了 TL;博士,那么也谢谢你的关心阅读我的采取。
一、MLOps 时代 ML 生命周期的关键特征是什么?
- 进化,而非革命。对于目前一直关注 CRISP-DM、OSEMN 或 TDSP 的数据科学家来说,这应该很熟悉。跟随 DevOps 无限循环的工程师也应该感觉很熟悉。
- 针对召回而非精度进行优化。易于记忆的流程更容易被遵循,并成为团队词汇的一部分。比如 DevOps 无限循环就不精确。每一步都有几个隐式的返回箭头指向前面的步骤。并不是测试之后的一切都会导致发布。失败可以追溯到代码甚至计划步骤。
- 便于将 ML 模型投入生产。它应该提高开发人员、数据科学家和数据工程师团队的可见性和协作,特别是我的哲学巩固所有权、早期集成和经常迭代。
- 灵活多变。它应该允许团队的部分成员选择他们的节奏。数据科学和软件开发本质上是不同的。数据科学家不可能每天都产生增量结果。生命周期和过程的目标是可见性和内聚性,而不是三足赛跑。
模型开发循环
如果您忽略了部署,模型开发就像 DevOps 循环一样有它的无限循环。

模型开发和 DevOp 无限循环。图片由作者提供。
想想 CRISP-DM 塑造成一个无限循环。模型开发循环中的步骤是:
- 用 ML 术语表述一个商业问题。
- 从内部应用程序和外部来源收集必要的数据。
- 策展数据。清理它,删除重复项,填充缺失值,标记它,等等。,最后编目存储。
- 转换数据。计算附加特征、改变结构等。
- 验证数据。实施质量检查、日志数据分发等。
- 探索数据。探索性数据分析、特征工程等。很可能会导致添加更多的转换和数据验证检查。
- 培养一个模特。运行实验、比较模型性能、调整超参数等。
- 根据业务目标评估模型特征。任何反馈都可能导致对 ML 问题的不同调整和表述。
把所有的放在一起
数据科学和软件开发都是为商业目标服务的。在 ML 辅助的应用中,模型设计必须注意它将如何影响用户体验和生产环境的约束。同样,软件设计必须包括不显眼地收集对模型改进至关重要的用户反馈。
在 ML 辅助产品中,模型设计和软件设计有着共生的关系。产品设计,计划步骤,必须整体考虑这两个方面,这是连接这两个循环的统一步骤。

MLOps 生命周期:数据、ML、开发、运营是一个永恒的结。图片由作者提供。
单个 MLOps 生命周期循环让所有成员可见,而不是开发人员将模型视为数据科学家以某种方式训练和折腾的黑匣子,以及数据科学家开发不服务于生产中预期业务目标的模型。
Data、ML、Data-ML 和 DevOps 循环可以不同的节奏运行。我总是试图首先用基于规则的或虚拟的模型构建一个端到端的应用程序,完全切断 Data-ML 循环。它作为基线工作,帮助收集数据,并为数据科学家提供一个环境,以了解他们的模型将如何被使用。
摘要
本文描述了 ML 生命周期在数据仓库、大数据湖和 MLOps 时代的演变。它还解释了如何通过加入模型开发和 DevOps 循环来修改它,以及这样做的好处。
参考
- Usama Fayyad、Gregory Piatetsky-Shapiro 和 Padhraic Smyth 在《KDD 96:第二届知识发现和数据挖掘国际会议论文集》第 82-88 页上发表的文章。
- CSP-DM 模型:数据挖掘的新蓝图,作者 Colin Shearer,数据仓库杂志,第 5 卷,第 4 期,第 13–22 页,2000 年
- SEMMA:取样、探索、修改、建模和评估。
- 希拉里·梅森和克里斯·维金斯于 2010 年 9 月发表的《数据科学分类》。
- 微软团队数据科学流程(TDSP) 生命周期。
- 机器学习的软件工程:案例研究,Saleema Amershi 等人,ICSE-SEIP '19:第 41 届软件工程国际会议论文集:实践中的软件工程,2019 年 5 月。( PDF )
- 什么是 MLOps?里克·梅里特于 2020 年 9 月 3 日在英伟达博客上发表。
- ml-ops.orgMLOps 原则,2022 年 9 月 8 日检索。
- Danny Farah,2020 年 8 月 24 日,现代 MLOps 蓝图。
- MLOps 从业者指南:机器学习的连续交付和自动化框架。谷歌云白皮书。2021 年 5 月。
- m lops:AWS 上机器学习的连续交付。AWS 白皮书。2020 年 12 月
- MLOps:数据、代码和基础设施的新兴趋势。 AWS 白皮书。2022 年 6 月。
- 采用 Azure 机器学习的 MLOps】。微软白皮书。2021 年 8 月。
如果您喜欢,请:
原载于https://www.ml4devs.com/articles/mlops-survey-of-machine-learning-life-cycle/。
MLOps 或如何大规模部署数据科学
原文:https://towardsdatascience.com/mlops-or-how-to-deploy-data-science-at-scale-b33921b4f17f

在行业中扩展人工智能和人工智能
几年来,我们一直听到很多关于人工智能(AI)和机器学习(ML)的说法。这通常与数学和模型化有关。因此,由于数据的炒作,公司雇佣了如此多的数据科学家,导致了行业中无数的概念证明(POC)或最低可行产品(MVP)。
这些公司现在面临着一个新的挑战:如何将这些人工智能 POC 和 MVP 部署到生产中?最重要的是,如何以低成本高效地制造出?
由于 ML 实验涉及代码和数据,经典的 DevOps 达到了它的极限,MLOps 作为一种新的范例被引入。 MLOps 或许可以理解为“机器学习”、“开发”和“运营”之间的缩写。
在本帖中,我们将试着从三个方面来理解隐藏在这个词背后的需求和概念:
- MLOps 动机—部署 PuzzleNet 的示例
- MLOps 理论——主要概念
- MLOps 成熟度级别—公司和项目的立场
1.MLOps 动机
你见过艾米丽吗?Emily 是一位出色的数据科学家!她处理数据,创建机器学习和深度学习模型!

图二——你见过艾米丽吗?
现在,艾米丽在亚特兰大 Puzzle&Co 的总部工作!她正在训练一个用于质量目的的分类模型:给定一条拼图生产线的图片,她必须找出生产质量低的图片!
由于工厂团队的合作,她设法收集了许多拼图中正确和错误部分的图片。因此,她可以训练一个有效的深度神经网络,她现在有一个很棒的模型在她的笔记本上运行: PuzzleNet
是时候投入生产了!
首次部署
Emily 将她的数据准备和评分笔记本保存为 python 文件,并将她的模型导出为 pickle。她把这些都存在她的 u 盘里。她去机场,乘飞机,飞往工厂所在地波特兰。
不出所料,当她到达时,摄像机已经在生产线上的正确位置,并且 Python 已经安装在与之相连的计算机上。精彩!她解压她的文件,保存在本地计算机上,做一些测试,然后…它工作了!太好了!
安装成功后,她飞回亚特兰大!
在接下来的一周,她收到了经理和工厂团队的祝贺,他们对预测的质量非常满意,并且有更多的时间专注于生产流程的另一部分。

图 3克拉肯 images 在 Unsplash 拍摄的照片
两周后
既然模型的准确性已经被证明是真实的,Paul,工厂经理决定将质量预测应用到第二条生产线上,生产更大尺寸的婴儿拼图。你猜怎么着?这真是一场灾难!
他立即打电话给艾米丽:“嘿!我们在我们的第二条生产线上试用了你们的型号,但是根本不能用!我给你发了一组图片,请你重新培训你的模型并重新部署?”
经过几天的努力工作,Emily 在她的新数据上获得了正确的结果!她重新压缩它,把它放在她的 u 盘上,带上飞机,安装她的模型,它就可以正常工作了!
她回到家,在飞机上她想着这个小小的改变有多痛苦!

图 4—Elisa Ventur 在 Unsplash 上拍摄的照片
“这是多么讲究和复杂啊!”—艾米莉
2.MLOps 理论
艾米丽的情况有点夸张,但也不算夸张。在现实中,将机器学习模型投入生产需要许多技能。幸运的是,在过去的几年中,这些已经被理论化,许多公司正在创造工具来缓解这一痛苦的步骤。
什么是 MLOps?
我们可以找到不同的定义,但我绝对喜欢维基百科的措辞:
“MLOps 是一套工具和实践,旨在可靠有效地在生产中部署和维护机器学习模型”。
AWS 将其定义为随着时间的推移不断进化人工智能模型的迭代和重复过程。我也认为非常准确。
当谈到数据科学的可操作性时,有 3 个组件需要考虑和关注:
- 代码:不考虑数据科学部分,任何代码都需要测试,版本&打包。因为我们是用 Python 工作的,所以我们必须处理这个问题。
- 数据:数据的质量可能会随时间而变化。然后,有必要确保数据源不被延迟,以不同的容量或格式。数据质量评估非常重要。
- 模型:处理数据的方式必须非常谨慎地定义,不能有有偏差的模型。一旦经过训练,预测的质量可能会随着时间的推移而变化。
当你想要实现一个 ML 项目时,你将不得不并行地管理这 3 个组件。正如在介绍中提到的,MLOps 可以被认为是 DevOps 到 ML 的扩展。

图 5 —来自 Nvidia 博客的 MLOps/DevOps 方案
目的是在合理的上市时间内将可靠的代码、数据和模型部署到产品中。
在生产过程中,关于数据处理的不同阶段,必须考虑许多因素。除其他事项外,我们还将关注:
- 数据:它是 ML 流程的核心。为了进行准确的预测,数据必须是高质量的、可用的和持续的。如何确保这一点?
- 培训:如何连接数据流?如何训练、验证和存储模型以便进一步重用?如何记住哪个模型是用哪个代码和哪个数据集创建的?
- 预测:你会批量给你的数据打分还是把你的模型公开为 API?您将如何运行该服务,并让具有适当需求的适当人员可以使用该服务?
- 评价:你的数据或者你的预测有漂移吗?你如何发现它?
- 编排:如何处理这些不同的服务?如何考虑到可能随时间发生的变化?
- 本地基础设施:如何确保环境保持稳定?它涉及与建模没有直接关系但与环境相关的一切。例如照相机、生产线、服务器、路由器等。
所有这些因素都是至关重要的,必须在 ML 项目的一开始就考虑到。
的确,如果一个完美的模型不能在现实生活中被真实的用户部署和使用,那么创建它还有什么意义呢?
3.MLOps 到期
在一个机器学习项目的开始,我们做手工。这是探索阶段。模型在笔记本中进行本地训练和验证。然后是部署阶段。
我们可以根据 3 个成熟度级别划分项目或公司,如下所述:
- 初学者, ML 无操作:一切都是手动管理。产品在生产,但每一步都很痛苦。这是 Emily 与 PuzzleNet 的案例。
- 中级, ML OOops :已经有了意识,正在努力。代码被打包。数据访问和模型训练是自动化的。它们可以通过 CI/CD 链跨多个环境部署。部署方法更加健壮。
- 高级、 ML op-Bo-ss : MLOps 大佬。随着时间的推移,对数据和预测进行评估,从而发出警报并开始自动重新训练。这被称为连续训练(CT)。最后,对模型进行比较,选择最佳模型进行部署。
成熟度等级 1/3: ML 无操作🐣
当艾米丽开始为 Puzzle&Co .工作时,她首先收集数据。经过短暂的探索,她很快联系了一位商业专家。他向她解释如何找出有瑕疵的部分,并给一些图片贴上标签。然后她能够准备数据——经过几周的工作——并创造出第一个成功的模型。这几乎是数据科学家的工作。然后,她带着她的 u 盘飞去部署她的模型。

图 6 —成熟度等级 1(作者图片)
优点:她的模型在生产中运行,最终用户可以用它来改进拼图的生产!
缺点:使一个新版本可用的时间非常高。由于服务器的配置与她的不同,她经常遇到部署困难。
成熟度等级 2/3: ML OOops🐒
缩短 ML 产品上市时间的第一步是认知。当你对自己说:“哦,妈的,我得给自己找个更好的解决方案”的时候。
首先,有必要从 DevOps 标准开始。使用代码版本控制工具 (git)和安装 CI/CD (持续集成&持续部署)。管道。通过使用这样的服务,可以随时监控代码中的修改、数据准备和建模步骤。

图 7 —成熟度等级 2(作者图片)
创建单元测试和集成测试也是重要的一步。尽管这是软件工程中的标准,但在数据科学项目中,这种类型的实践仍然很少遵循。为了保证适当的质量,可以使用 linter 或代码覆盖率等指标。如果测试没有通过验证,或者代码质量不够好,应用程序的构建将被阻止,报告将被发送给数据科学家。
为了方便应用程序的部署,推荐使用容器化解决方案,比如 docker。这将允许复制模型运行的环境,从而方便在新机器上的安装,以及随后在工厂的服务器上的安装。
优势:在这个成熟度级别,数据科学家已经集成了 DevOps 部署的最佳实践。代码被打包,进化更加健壮,部署可以远程自动化。上市时间大大缩短。
缺点 : 然而,一些至关重要的砖块还是不见了。例如,在这里,Emily 能够以自动化的方式部署 PuzzleNet,但是对她的模型的性能没有反馈。她不可能评价它的可靠性。在漂移的情况下,只有当用户发现预测毫无意义时,她才会被警告。
成熟度等级 3/3: ML op-Bo-ss🦁
既然 Emily 能够在眨眼之间重新部署新模型,她想知道应该多长时间重新训练一次。的确,虽然"我如何部署?“问题解决了,首要问题依然存在:
随着时间的推移,我如何保持我的模型的性能?
她越想越是告诉自己,在度假的时候,一切自己管理自己,还有很长的路要走!
思考的下一个重要步骤如下:
- 如何测量模型的漂移?
- 如何提高我的模型的性能?或者至少如何确保不会变得更糟?
- 我如何自动化这种监控?
- 在新模型性能不佳的情况下,如何确保回滚?

图 8 —成熟度等级 3(作者图片)
1。监控
这是关于超越预测:我们现在将保留预测的历史,以便能够用实际发生的真实值来评估预测。
在受监督的项目中,这个过程可以自动化,而不会带来业务复杂性。在无人监管的项目环境中,经常会要求应用程序的用户或业务专家不时地进行标记。这就是反馈概念发挥作用的地方。
在任何情况下,预测、实际值和反馈都可以存储在数据库中,并允许使用评分标准来评估模型性能。这被称为可观察性。
2.连续训练
为了完全专注于她的下一个项目,Emily 使用她所掌握的所有信息来触发自动再训练。这些可以由不同的触发器触发:
- 模型退化。如果性能下降,请使用最新的信息来重新训练模型。
- 一个时间段。每周或每月的新数据。
- 大量新数据。每 5GB 的数据,或者业务专家的每 1000 个注释。
- 任何外部事件。
当重新训练自动进行时,Emily 还需要对她的输入数据(图像大小、像素颜色分布)进行自动质量和合规性测试。
但是,如果一些数据在短时间内遭到破坏,并且新模型的性能不如以前的模型,会发生什么情况呢?最后一步是模型追溯和回滚!
3。模型注册表
与我们在 git 中发布代码的方式一样,我们希望在训练模型时跟踪模型及其元数据。诸如相应代码的提交、训练数据集的元数据或输出度量之类的信息都是允许您做出明智选择并对训练过程的自动化有信心的信息。
例如,比较评估指标将允许自动决定该模型是否应该替换先前的模型。很有可能想象一个保持不变的评分 API,其后端模型会随着数据的发展而改变为更好的版本。
优点:结果是良性循环,可观察性高,几乎自主行动。艾米丽将可以去享受阳光假期,而不用再担心她的模特☀️了
关于成熟度级别
当然,那些实践只是指导方针,应该适应你的成熟度和你的项目。
- 如果您处于第 1 级,从版本控制和设置 CI/CD 和单元测试管道开始。想要从第一级快速升到第三级不会带来任何好处
- 这也取决于项目:注意不要设置在不满足项目需求的情况下使项目更加复杂的工具。如果每月一次批量处理,就没有必要部署过于高级的监控。始终对好的实践保持批判的眼光,只保留你需要的。
“吸收有用的,拒绝无用的,加入本质上属于自己的”——李小龙
结论
MLOps 是一套工具和实践,旨在尽可能快地将代码、数据和模型变更引入生产。它继承了其老大哥 DevOps 的概念,构建了人工智能产品特性的集成,如模型性能进化和持续培训。
在数据科学的后 POC 时代,MLOps 正在发挥关键作用,许多公司都对它感兴趣。这些公司仍然有不同的成熟度级别,从在生产服务器上以 pickle 格式存放模型到监控、自动化和自主的再培训。
特斯拉人工智能高级总监安德烈·卡帕西(Andrej Karpathy)谈到了“度假行动”(Operation Vacation):如果他的所有数据科学家都去度假,人工智能系统就会不断自我完善。这有点极端,但我们可以把这种理想视为一种趋势。
资源
- MLOps Youtube 视频由 Aurélien Massiot 、 Pierre Baonla Bassom 和来自 Octo Technology 的 Baptiste Saintot (法属🇫🇷)
- 关于在特斯拉的运营假期
如果你喜欢这篇文章并想了解更多,你可以在 中 关注我,成为 会员 ,在LinkedIn上与我联系,或者访问我的 网站 。
希望你喜欢它,再见🤠
简而言之,MLP 搅拌机
原文:https://towardsdatascience.com/mlp-mixer-in-a-nutshell-eccffb68e4fc
视觉变压器的资源节约和性能竞争替代方案

里卡多·戈麦斯·安吉尔在 Unsplash 上的照片
T 他的文章旨在对 I .托尔斯泰欣、n .霍尔斯比、a .科列斯尼科夫、l .拜尔等人的论文 MLP 混频器:一个全 MLP 架构的愿景中介绍的 MLP 混频器进行简要概述【1】。此外,我想根据我的经验提供一些进一步的背景知识,以便快速理解 MLP 混频器的主要特性。
概述
介绍
由 Vaswani 等人在论文“注意力是你所需要的一切】【2】中引入的 transformer 架构已经彻底改变了机器学习领域。虽然它最初应用于 NLP(自然语言处理)任务,如将句子从英语翻译成德语,但它的架构很快被其他学科采用和改编,如计算机视觉,产生了 ViT(视觉转换器)【3】等模型。其中,变换器的主要优势是(1)其通过注意机制捕获全局上下文的能力,(2)其用于训练的高并行化能力,以及(3)与例如旨在复制人类感知图像的行为的卷积神经元网络相比,它是具有较少归纳偏差的更一般化的操作。
在我们深入 MLP 混合器之前,让我们首先通过回顾变压器的弱点来理解新架构的动机。然后让我们总结一下 MLP 密炼机论文的贡献,最后换个话题来回顾一下 MLP 密炼机。
MLP 混频器的动机及其贡献
不管 transformer 架构的强度及其变化如何,有一个主要问题:计算和内存需求与输入序列长度成二次方关系。换句话说,在 NLP 问题中,词汇表中的单词越多,或者计算机视觉中图像的分辨率越大,就需要更多的资源来训练和部署模型。这种约束是注意机制的结果,其中一个集合(或序列)中的每个元素都注意第二个集合中的其他元素,而第二个集合可能与初始集合相同。
MLP 混合器通过替换注意力机制来解决这个问题。根据【1】,MLP 混频器的主要贡献是:
- 在 ViT 中引入一种有竞争力的(但不是更好的)简单架构,它不使用卷积或自我关注,而只使用多层感知器(MLP)。
- 仅依赖于基本的矩阵乘法、整形和转置以及标量非线性。
- 计算复杂度与输入面片数量的线性比例关系(与 ViT 不同,ViT 是二次比例关系)
- 位置嵌入的去除
MLP 混频器架构及其与以前模型的比较
在本节中,我们将讨论 MLP 混频器的模型架构,并将其与以前的模型进行比较。我们从高层架构开始,逐渐揭示细节。
正如论文的标题所示,MLP 混合器是一个视觉模型的架构。这是一个高层次的观点,非常类似于 ViT 模型,如图 1 所示。

图 1:(左)视觉变换器【3】和(右)MLP 混频器【1】的高层架构对比。
ViT 和 MLP 混合器都是分类网络。他们输入一幅图像,输出一个类别概率。在高层次上,两个模型都将图像块线性投影到嵌入空间中。当 ViT 用步进卷积执行这个步骤时,MLP 混合器使用全连接层,因为它的目标之一是表明既不需要卷积也不需要注意层。嵌入,也称为令牌,在两个模型中都被输入到它们各自的主构建块中进行计算。就 ViT 而言,它基于 transformer 编码器层,而 MLP 混频器引入了一种新的架构,我们将在稍后详述。值得注意的是,MLP 混合器不需要额外的位置嵌入,因为它对输入嵌入的顺序很敏感,这与注意力层不同。在几个主要的计算层之后,信号被馈送到分类头,在那里模型预测给定输入图像的类别。
现在,让我们仔细看看图 2 中描绘的主要构建模块。

图 2:ViT【3】(左)和 MLP 混频器层(右)的变压器编码器模块对比。作者插图,灵感来自【2】。
左侧描述了 ViT 中使用的变压器编码器,右侧说明了由【1】提出的 MLP 混频器层。两层都重复几次(分别是 L 次或 N 次),并且它们遵循各向同性设计,这意味着其输入和输出具有相同的形状。在这个抽象层次上,唯一的区别在于注意力机制。ViT 依赖于多头自我注意机制,这需要三个线性投影嵌入;键、值和查询。这一层为查询的每个值分配一个重要性因子给关键字的每个值,反之亦然,从而产生一个注意力图。注意力图捕捉嵌入的全局依赖性,而不像卷积那样只考虑局部邻域(全局上下文通常由几个卷积层捕捉,这通常会减小空间宽度并增加通道的数量)。然后,注意力图乘以嵌入的值,以加强重要的值,而不重要的值被抑制。另一方面,MLP 混合器通过封装在两个矩阵转置操作之间的 MLP 块来取代自我注意机制,以捕捉全局上下文。为了理解这是如何工作的,我们进一步深入图 3 所示的混合器层的详细架构。

图 3:MLP 混频器【1】的混频器层。
这种架构是建立在【1】作者的简单观察之上的:现代视觉架构混合了它们的特性 (1)在给定的空间位置跨越通道和 (2)在不同的空间位置。CNN 在一层内实现(1 ),但通常通过连续的卷积层实现(2 ),卷积层通过应用越来越多的滤波器来减小空间宽度并增加频道数量。基于注意力的架构在每一层中都允许(1)和(2)。MLP 混合器的目的是清楚地区分(1)和(2 ),作者分别称之为信道混合和令牌混合。
首先,我们考虑由第一 MLP,即 MLP1 执行的令牌混合。MLP1 将作用于输入矩阵的每一行,利用权重共享。在第一标准化层之后,数据以[通道,面片]形式的矩阵表示。通道(或嵌入的隐藏维度)是一个可以变化的超参数。补丁指的是输入图像被分成的补丁的数量。为了混合来自每个标记化面片的数据,在将 MLP1 应用于每一行之前,对输入进行转置。MLP1 的输出再次被转置,以获得其初始形式。通过将每个斑块的信息输入 MLP,可以感知全球背景。
第二 MLP,即 MLP2,执行信道混合。MLP2 的权重不同于 MLP1,但也使用权重共享。MLP2 从单个补丁的所有通道接收数据,允许来自每个通道的信息彼此交互。
每个 MLP 块由一个完全连接的层组成,接着是一个 GELU 激活,接着是另一个完全连接的层。
实验
一个重要的问题还没有答案:它的实际表现如何?回答问题。【1】的作者对不同规模和不同数据集的模型进行了多次实验。要了解更多的细节,我建议你阅读报纸。我只讲主要结果。
为了进行实验,首先在不同的数据集上对几个模型进行预训练,然后在不同的下游任务上进行微调。已经分析了三个参数:
- 下游任务的准确性
- 预培训的总计算成本
- 测试时吞吐量
表 1 显示了主要结果,并对各列进行了如下解释:
第 1 列:测试模型
第 2 列:带有原始标签的 ImageNet 下游任务的前 1 名准确率
第 3 列:带有已清理真实标签的 ImageNet 下游任务的前 1 名准确率
第 4 列:所有五个下游任务(ImageNet、CIFAR-10、CIFAR-100、Pets、Flowers)的平均性能的前 1 名准确率。
第 5 列:视觉任务适配基准
第 6 列:TPU-v3
上的图像/秒/核吞吐量第 7 列:TPU-v3 加速器上的预训练总时间

表 1:与来自文献【1】的最新模型相比,MLP 混合器的传输性能、推理吞吐量和训练成本。
就精度而言,混合器模型在所有执行的下游任务中与所有其他测试模型具有竞争力。就测试期间的吞吐量而言,混频器优于 ViT 和 BiT。
可以观察到,MLP 混合器在大型 JFT-300M (300M 图像,18k 类)数据集上的预训练时间明显优于其竞争对手,而对于较小的 ImageNet-21k (14M 图像,21k 类)数据集,其训练实际上比其竞争对手的模型更慢。
似乎特别是对于大型数据集,MLP 混合器是其他最先进模型的有竞争力的替代方案,因为它几乎达到了最先进(SOTA)的性能,同时在训练和测试期间更有效。人们必须决定在各自的应用中哪种度量更重要。
结论
MLP 混合器通过引入由 MLPs 和转置组成的简化架构来解决注意力层的计算资源的二次缩放问题。它实现了接近 SOTA 的性能,同时降低了对计算资源的需求。这是一种权衡,对于给定的应用程序必须做出决定。
[1]托尔斯泰欣,伊利亚,尼尔·霍尔斯比,亚历山大·科列斯尼科夫,卢卡斯·拜尔,翟晓华,托马斯·安特辛纳,杰西卡·容等.《混音器:一个全的建筑视觉》ArXiv:2105.01601【Cs】,2021 年 6 月 11 日。http://arxiv.org/abs/2105.01601。
[2] Vaswani、Ashish、Noam Shazeer、Niki Parmar、Jakob Uszkoreit、Llion Jones、Aidan N. Gomez、Lukasz Kaiser 和 Illia Polosukhin。“你需要的只是关注。”ArXiv:1706.03762【Cs】,2017 年 12 月 5 日。http://arxiv.org/abs/1706.03762。
[3] Dosovitskiy,Alexey,Lucas Beyer,Alexander,Dirk Weissenborn,Xiaohua Zhai,Thomas Unterthiner,Mostafa Dehghani,等人《一幅图像抵得上 16x16 个字:大规模图像识别的变形金刚》ArXiv:2010.11929【Cs】,2021 年 6 月 3 日。http://arxiv.org/abs/2010.11929。
移动优先可视化
原文:https://towardsdatascience.com/mobile-first-visualization-b64a6745e9fd
神秘的魔法
创建一个清晰和惊人的可视化,完全适合移动

Mikael Blomkvist 来自 Pexels 的原始照片
你如此努力地清理数据,在周末花费了无数次来寻找洞察力,最后,你设法在你的文章中发布了你的可视化,但是没有人理解你的可视化,因为他们不能正确地查看它。你不觉得你所有的努力都白费了,而且很没有动力吗?

一个坏图的例子。不要绘制这样的图表。作者图片
这是因为你没有意识到世界的技术已经改变了,你设计的重点也应该改变了。供您参考,移动设备在 2020 年驱动了美国网站 61%的访问量, 从 2019 年的 57%[1]。来看你文章的大部分受众都在用手机。你应该在网上出版物的每一个方面都有一个移动优先的设计。
在本文中,我将向您介绍移动优先可视化的概念——通过对您的可视化进行一些调整,它将极大地增强您的可视化在移动用户中的展示和吸引力。
注意:本文将主要关注如何使用 Plotly 来实现,但是这个概念适用于您正在使用的任何一个可视化工具
1.去杂乱
消除混乱是移动优先设计中最重要的概念,如果你看着你的移动屏幕,你会注意到它非常小,任何出现在你的可视化中的额外项目都会影响你的观众的感知。
您必须做的第一件事是从默认设置中删除所有不必要的元素,如网格、不必要的图例和轴。例如,下面的条形图显示了具有默认背景的各国药剂师的工资。

默认 Plotly 输出条形图。作者图片
在我将背景颜色更改为白色并删除 x 轴标题后,图表看起来更加清晰。

去杂乱条形图。作者图片
2。比率问题
Plotly 下载的默认width和height是 983 乘 525,它的比例为 1.87:1,在手机屏幕上显示时太小。大多数网站会在它们的 CSS 代码中使用 100%的宽度,以应对上传的每张图片和屏幕大小。这意味着您的图像将覆盖阅读窗格 100%的宽度。然而,它移动的宽度很短,相同长宽比的图片将覆盖屏幕的很小一部分。

不同设备视图中图像外观的比较。作者图片
就我个人喜好而言,我通常会选择 4:3 或 1024 x 768 像素的宽高比。下面是我改变上图比例后的结果:

已更改纵横比。作者图片
在你的手机屏幕上看起来更好吗?
3。字体,字体,字体!
如果您在桌面上显示文本,最小可读字体大小是 12,对于移动视图,最小可读字体大小是 16。让你的受众一眼就能读懂你的文字,而不是放大图片来读懂你的文字,这一点非常重要。这样会让他们的经历大打折扣。通常我会选择 22 的字体来显示我的大部分图表。
还有一点要提醒的是,你的图的字体族,Plotly 默认的字体族(或字样)是 Open Sans 而最现代的网站都不使用它,它会让你的图在没有融入你的文章时显得很别扭。
例如,如果您在 Medium 上发布文章,您可以选择 Sans-serif、等字体;如果您在材料设计网站上发布,您可以选择 Roboto 等字样。这些字体会很好地融入你的文章。

字体大小和字体系列已更改。作者图片
4.颜色;色彩;色调
大多数现代网页设计都是黑色和黑灰色的,大多数手机屏幕都有有机发光二极管功能,可以增强颜色的对比度。这意味着当您选择更鲜明的色调时,由于移动色彩系统的增强,它将最能吸引观众的注意力。
试着给你的图表添加一些鲜明的颜色,比如:

在目标国家使用颜色。作者图片
鲜明的色彩将立即抓住你的观众的眼睛,这是进一步加强了移动屏幕的有机发光二极管功能。
5.垂直思考
利用手机,你应该纵向扩展你的信息,而不是横向。例如,如果您正在绘制条形图,建议您垂直绘制图表,而不是水平绘制。垂直图的优点是你可以在不牺牲棒线厚度的情况下扩张。
以上面的图表为例,如果我想在我的图表中再添加 5 个国家,这将使我的条形越来越细,并且无法读取标记的国家:

新添加国家的图表。作者图片
如果你把你的条形图改为水平条形图,你的读者就不需要低头阅读你的文本了。您可以通过调整纵横比轻松增加图表的高度,而不会影响可读性。

带有新添加国家/地区的图表(水平)。作者图片
代码:
本文的代码如下:
作者的话:
数据故事很有趣,可视化中的每一个元素都会增加观众的脑力负荷。因此,在你把每一个元素放入或从你的视觉化中取出之前,考虑它是非常有趣的。
我仍在实践数据讲故事的艺术,我希望我的小小分享能帮助你将你的工作成果有效地传达给你的听众。当人们不理解我们的视觉化时,我理解失望的感觉。相信我,只是需要更多的迭代和反馈。
最后,感谢您阅读我的文章!
https://manfyegoh.medium.com/membership
参考资料:
[1]https://www . perficient . com/insights/research-hub/mobile-vs-desktop-usage
[2] Plotly 布局文档
[3]参考图来自 AIPharm.xyz
厄瓜多尔移动网络运营商的覆盖范围
原文:https://towardsdatascience.com/mobile-network-operators-coverage-in-ecuador-9fdd78d3b4fb
2G、3G 和 4G 蜂窝天线发展的地理可视化

图片由作者提供。厄瓜多尔从 2008 年开始开放细胞识别数据。2G 蓝色,3G 粉色 4G 黄色
自 2017 年以来, OpenCell ID 组织一直在向世界各地的社区收集蜂窝天线的位置。这个开放数据项目包含大约 3.3GB 的全球压缩数据,包括位置属性、移动网络运营商代码、覆盖范围和国家代码。该数据集有助于公共/私人机构了解天线覆盖的模式。如今,美国是覆盖范围最广的国家(就小区数量而言),约有 700 万个,其次是德国,约有 200 万个。
为电信开发的最新技术是 5G,它覆盖了数据速率高达 20Gbps 的移动宽带(比传统移动技术快得多)。第一个将 5G 技术商业化的国家是 2019 年的韩国,如今是这一发展的领导者。[ 1 到 2021 年,5G 发展排名前 10 位的国家是韩国、挪威、阿联酋、沙特、卡塔尔、科威特、瑞典、中国、中国台湾和新西兰。
厄瓜多尔仍然未能达到这一技术,仍然严重依赖 2G 和 3G 连接,样本超过 20%。[ 2
蜂窝天线是干什么用的?
在移动电话上进行的每一次连接都记录在近距离天线中。与移动数据连接相关的通话、信息和操作都存储在使用手机的蜂窝天线中。在国家和全球层面,存储了来自作为移动网络运营商客户的数百万用户的大量数据。数据是匿名的,主要反映用户不同位置的 pings。
一些公司在分析开发如此庞大的数据量方面很聪明。分析的洞察力可以揭示内部人口、国际旅游和网络统计的流动情况。令人惊讶的是,这一分析已被联合国证实是可靠的,并可被世界各国统计局用于官方统计。
爱沙尼亚公司Positium是移动定位数据分析领域的领导者。出席 2021 年世界数据论坛的这家私营公司讲述了他们为支持新冠肺炎紧急情况而采取的行动,为人口流动提供了一个创新的仪表盘。

如果你愿意了解移动定位数据的分析结果及其应用,我建议你阅读这个故事:
*
目标
这个故事旨在展示如何操作来自 OpenCell ID 的数据以及如何将其可视化。就类别而言:
- 移动网络运营商
- 通过 2G、3G(厄瓜多尔使用最多)和 4G 技术
- 靠你自己的创造力
数据许可证
- OpenCell ID 的数据库是在知识共享署名-共享 4.0 国际许可下许可的。
地理可视化实践
找到您感兴趣的国家,并从 Open Cell ID 下载数据。然后你解压数据,就像这样:
**from** datetime **import** datetime
**import** pandas **as** pd**# read with Pandas**
data = pd.read_csv(r’740.csv.gz’, header=None, compression=’gzip’)
国家数据的第一个问题是它不包含列名。为了帮助你,你可以像这样添加它们:
**# change columns of OpenCell** col_names = [‘radio’, ‘mcc’, ‘mnc’, ‘lac’, ‘cid’, ‘changeable_0’,
‘long’, ‘lat’, ‘range’, ‘sample’, ‘changeable_1’,
‘created’, ‘updated’, ‘avgsignal’]data.columns = col_names
再加上 2G,3G,4G 这一代,还有运营商。在本练习中,我将只使用 Movistar、Tuenti 和克拉罗。
**# add generation**
data[‘radio’] = data[‘radio’].replace(‘UMTS’, ‘3G’).replace(‘GSM’, ‘2G’).replace(‘LTE’, ‘4G’).replace(‘CDMA’, ‘3G’)**# add operator**
data = data.loc[data['mnc'].isin([0, 1, 3])]
data['mnc'] = data['mnc'].replace(0, 'Movistar').replace(1, 'Claro').replace(3, 'Tuenti')
因此,它可能看起来像:

图片由作者提供。桌子准备好了。
操作员的覆盖范围:

图片由作者提供。按操作员代码排列的天线。

图片由作者提供。加拉帕戈斯群岛的运营商覆盖范围
一般来说,所有运营商都连接到天线。尽管 Tuenti 展示了少量天线(根据覆盖范围确定大小),但它实际上连接了整个蜂窝天线网络。克拉罗似乎拥有更多基础设施。
世代覆盖率:

图片由作者提供。各代天线的覆盖范围。
似乎 2G 和 3G 是之前所说的主导者。4G 还在发展中。可以看出,这主要发生在城市地区,如基多、瓜亚基尔、昆卡、曼塔或马查拉。

图片由作者提供。加拉帕戈斯群岛的世代覆盖率
在加拉帕戈斯群岛,2G 和 3G 是大陆地区的主导。
时间性覆盖:
我喜欢绘制混乱的地图。

图片由作者提供。蜂窝天线的发展
建议
开放细胞 ID 是开放数据的来源,可以帮助分析细胞天线在世界范围内的分布。但是不要想当然。一些天线似乎位于海洋中或街道中间。是这样吗?嗯,我相信由于它是一个开源数据集,对真实位置的保护是可以改变的。但是,有一个参考还是很好的,特别是如果我们在国家层面上使用地理可视化。
这种做法可以支持不同国家的移动网络运营商的空间分布及其蜂窝天线的技术发展。因此,只需使用这些材料就可以更好地了解电信发展的现状。
参考文献
[1] 美洲开发银行。(2020) 。“5G 是拉丁美洲和加勒比地区下一代数字社会的驱动力”本作品采用知识共享 IGO 3.0 归属-非商业-非衍生(CC-IGO BY-NC-ND 3.0 IGO)许可协议(https://Creative com—mons.org/licenses/by-nc-nd/3.0/igo/legalcode),可在归属 IDB 的情况下出于任何非商业目的进行复制。不允许衍生作品
[2] Ookla (2021) 。“增长和放缓:2021 年全球 5G 的状况”。https://www . ookla . com/articles/state-of-world wide-5G-2021 #:~:text = 5G % 20 slowed % 20 down % 20 at % 20 global % 20 level&text = Over % 20 the % 20 past % 20 year % 20 from,Mbps)% 20 during % 20 same % 20 period%20during%20the%20same%20period)。*
手机:一个图像分类问题
原文:https://towardsdatascience.com/mobile-phones-an-image-classification-problem-8dd207205c0d
使用监督学习算法的背景和介绍
分类
分类是识别数据集中趋势的最广泛使用的方法之一。这些类型的算法致力于在不使用其他数据操作技术的情况下对数据集进行可靠分类的目标。分类算法要么是有监督的,要么是无监督的。监督学习使用有标签的数据来做决策,而后者使用无标签的数据。例如,有监督的学习可以处理线性关系,而无监督的学习必须处理未知关系(通常是聚类)。因此,根据数据集的类型,分析师将不得不决定对问题采用适当的算法。对于这项研究,监督学习算法已被选为数据集被标记。
图像分类
图像分类是机器学习过程,通过该过程,开发者能够将一组图像分类成不同的类别或主题。使用模型(训练的或未训练的),可以提供图像,使得该模型使用具有深度学习技术的神经网络来准确地分类所有图像。该模型是一组预编码的指令,供计算机应用于一组图像,以便它能够将数据集分类/归类。
神经网络
神经网络是模拟大脑的一种尝试。大脑包含数万亿个神经元连接,而开发人员只能对人工神经网络中节点之间的有限数量的连接进行编程。人工神经网络由输入层、输出层、激活层和几个其他计算/隐藏层组成,是一个大型决策树,它使用数学方法来计算数百万个结果,以便算法在训练、验证甚至测试阶段取得进展。
卷积神经网络(CNN)
不一样的,相信我!
“卷积”是指使用 2 个函数创建新的第三个函数的过程。“卷积神经网络”是一种神经网络,它使用两个给定的输入,并在移动到连续隐藏层、激活层或输出层中的节点时产生输出。此外,CNN 还用于剩余学习的环境中。残差学习是神经网络能够重新使用它在连续层中丢失的任何输入的过程,这使得算法产生最小的损失和更高的精度。
好吧…我想这需要消化很多背景知识。
收集数据
形象
我从无版权来源获得了我所有的图片,所以在项目后期不会有任何问题。
- 正面案例:手机
- 反面案例:洗衣机/洗碗机/平板电脑/笔记本电脑
主要(软件端)工具
- 语言:Python
- ML 框架:PyTorch
- 预训练模型:Resnet18
PyTorch 和 Resnet18:一个词
Pytorch 是开源的,包含预先训练和未训练的学习模型,这使得它适合大多数 ML 开发者。R esnet18 是深度学习算法的机器学习模型。“18”指的是 CNN 的 18 层。该项目利用了预训练版本 Resnet18,因此该过程可以简化为给定图像的实时训练和验证。
我是怎么做到的?
- 确保足够的电源和内存
2.使用命令行工具安装 Python 和 PyTorch
3.运行几个命令来测试 Python 和 PyTorch 是否在机器上运行
4.如果机器没有足够的处理能力(功率、内存或其他):遵循步骤 5 至 8
5.联系业内专业人士或者用 GPU 设置桌面
6.将桌面配置为服务器(可以通过 ssh 连接到它)
7.使用“ssh”或电缆连接来连接到桌面
8.在命令行上运行几个命令,确保桌面服务器正在响应
9.记录培训、验证准确性和时间(两者)的值
10.迭代算法并收集必要的数据,在每次迭代结束时改变“lr”和“epochs”的值(这是为了最大化精度)
实验后数据
结构
- 8 次试验
- 每个试验都有不同的#时期和#lr 值
- 中位数:培训+ Val 准确性和完成时间(注明)

数据汇总(图片由作者提供)
所有关于模型性能的数据都以“xlsx”格式上传到 Github 存储库中。任何电子表格编辑器都应该能够打开它。
分析:数据告诉我什么?
试验 8 在训练和交叉验证数据集上都产生了最少的损失和最高的总体准确性。试验 8 的组合可以在其他数据集上产生相同的测量。试验 8 已经被进一步详细说明,因为该组合使得该算法与其他算法相比执行得最好。

试验 8 总结(图片由作者提供)
(ACC 是准确度,Val 是验证)
试验 8:可视化

图 4: Trial_8 训练损失(图片由作者提供)

图 5: Trial_8 训练精度(图片由作者提供)

图 6: Trial_8 验证失败(作者图片)

图 7: Trial_8 验证准确性(图片由作者提供)
洞察力:这些图表背后的意义
图 4,5
与数据集上模型的定型阶段相关。如图 4 所示,训练损失在图表的第一部分急剧下降。这可能指示模型的“预热”阶段,以便理解特征向量和比较基础。对于训练精度来说,这可能是类似的情况,但是,在训练精度的情况下,精度值很快达到峰值,这表明模型没有使用大量迭代来实现更高的精度值。虽然,该图的一个方面是使用现有选择的限制,即在 0.99 处的可察觉的渐近线,用于 90 次迭代的精度值的波动次数。这是选择学习率和 epoch#时要考虑的一个方面,因为这些值的微小差异可能会在准确性和模型完全分类数据集所需的时间方面产生显著差异。这可以通过比较上表中试验 7 和试验 8 的测量值的绝对值看出,该表包含所有测量值的中值。
图 6,7
交叉验证的可视化描绘了与训练阶段相似的趋势。然而,在交叉验证中,模型的性能稍好。这表明训练阶段是有效的,因为训练集的性能通常为开发人员提供了关于交叉验证数据集的性能的关键信息。在这种情况下,交叉验证被视为测试集,这意味着在大多数迭代中获得 100%的准确性表明模型成功,因为它表明它能够以高精度解决问题。
结束语
概括来说,问题陈述是“给定一张产品图片,以 x%的置信度确定该产品是否属于特定类别(本练习中为手机)”。该算法对图像进行正确分类,总体准确率高达 99.82168%。这表明试验 8 的组合是成功的。然而,当应用于更大的数据集时,仍然存在几乎 0.28%的误差率,这意味着显著的绝对误差。因此,这些结果不能用于工业环境。
改进/扩展:我在某个地方出错了!
- 操纵 CNN 的步长和动量。
- 使用不同的框架。例如张量流。
- 应用不同方向的图像来测试不可预测的数据。例如旋转
- 将更大的数据集应用于模型以改进数据收集
感谢您阅读我的项目总结!。
我对这个领域很陌生,所以,在我的写作中可能会有不一致/混乱,我可能已经错过了。如果您有任何批评或需要进一步澄清,您可以通过下面列出的电子邮件直接联系我。
电子邮件:rahulr280623@gmail.com
Github 储存库:https://github . com/ra hulr 2003/Mobile-Vs-Non-Mobile-Classification
参考
巴蒂亚,理查。"神经网络不像人脑那样工作——让我们揭穿这个神话." Analytics India Magazine ,2018 年 10 月 31 日,analyticsindiamag . com/neural-networks-not-work-like-human-brains-let-拆穿-myth/#:~:text = b)% 20 artificial % 20 neural % 20 networks % 20 vs,有% 20millions % 20of %个连接。
邦纳,安妮。"深度学习完全初学者指南:卷积神经网络."中,走向数据科学,2019 年 6 月 1 日,走向数据科学. com/wtf-is-image-class ification-8e 78 a 8235 ACB。
脸书人工智能研究实验室。火炬视觉模型。Torch vision . models—py Torch 1 . 7 . 0 文档,Torch 贡献者,2019,pytorch.org/docs/stable/torchvision/models.html.
冯,文森特。" ResNet 及其变体概述."介质,走向数据科学,2017 年 7 月 17 日,走向数据科学. com/an-overview-of-resnet-and-its-variants-5281 e2f 56035。
贾恩,普拉奇。"电子商务中的图像分类[上]. "中,走向数据科学,2019 年 2 月 13 日,走向数据科学. com/product-image-class ification-with-deep-learning-part-I-5b C4 E8 dccf 41。
麦克唐纳康纳。"神经网络(NN)图."走向数据科学,中,2017 年 12 月 22 日,走向数据科学. com/machine-learning-fundamentals-ii-neural-networks-f1 e 7 B2 CB 3 eef。
Python.org。“欢迎来到 python . org”。Python.org、Python.org、www.python.org/doc/.
鲁伊斯帕布罗。“理解和可视化结果”中,走向数据科学,2019 . 04 . 23,走向数据科学. com/understanding-and-visualizing-RES nets-442284831 be 8。
周,维克多。"初学者的机器学习:神经网络导论."中型,走向数据科学,2019 年 12 月 20 日,走向数据科学. com/machine-learning-for-neural-networks-d 49 f 22d 238 f 9。
基于癌症数据的模型学习
原文:https://towardsdatascience.com/model-based-decision-making-for-health-data-581bd58a1a3
基于模型的学习;实践教程
基于癌症数据的模型学习
关于在不平衡和缺失的健康数据上部署基于模型的人工智能的教程

在本文中,我将从头到尾概述如何将基于模型的学习应用于真实世界的健康数据——在我的例子中是癌症数据。在这篇文章的最后,我希望你能了解为什么基于模型的学习是一个有用的通用框架,可以应用于许多不同的数据集,以及它如何帮助识别基于医疗保健等数据领域做出的具体分类决策。
例如,在医疗保健领域,预测某人是健康的,而实际上他们可能需要手术或治疗——戴夫致命的会付出代价吗,因为他们的潜在状况可能不会被人类医生发现,直到为时已晚,因为他们已经被自动系统认为是健康的。然而,在病人实际健康的情况下预测癌症的模型并不是一件坏事,因为病人可以通过他们的保险进行定期检查。如果病情恶化,他们的医生会第一个知道。
我将使用的数据集是来自葡萄牙科英布拉医院和大学中心的真实数据,由 165 名“肝细胞癌”患者组成,这是一种常见但致命的肝癌,通常与甲型肝炎有关
初始数据分析
修复缺失的健康记录数据
首先,我们需要从 UC Irvine 机器学习库的这个链接下载数据。您会注意到,在链接中,它说大约 10%的数据是缺失,只有8 个患者记录有完整的数据。所以我们的第一步是解决这个问题。

图片鸣谢:作者。缺少的值标有问号“?”。
在我们进行任何特征选择或机器学习之前,我们首先必须修复缺失的数据。通常, pandas 库会提供某种类型的选项,比如pandas.fillna,它基本上是用均值、中值或最近邻(如果列是数字)或模式(如果列是分类的)来填充每个特征列中的缺失值。
然而,在这种情况下,对于我们的癌症数据,该论文的作者(他们在 UCI 上免费发布了该数据集)概述了一种基于患者相似性度量的缺失数据方案,称为“异构欧几里德重叠度量”(HEOM),根据作者的说法,该方案对于填充具有连续和离散类型的数据集中的缺失值非常有效。该代码包含 Python 中关于如何实现这一点的细节;你可以在这里查看。
寻找最佳功能
现在我们已经填充了缺失的数据,让我们来看看如何选择我们的特征。我决定不在特性选择上花太多时间。有很多方法可以做到这一点:使用递归特征消除(RFE),互信息,卡方检验独立性,等等。我通常不会推荐 RFE,因为有了足够多的特性,你需要测试的组合数量会增长得非常快。
在这种情况下,对于健康记录,我将完全做其他事情:我将使用随机森林来确定特征。你可能会问,“我为什么要用一个随机的森林来计算呢??"但事实证明,随机森林有一个名为 feature_importances 的内置属性,这是由所使用的决策树集合决定的。(顺便说一句,如果你想要一篇关于 Random Forests 的快速评论文章,看看我以前关于它们的帖子,以及对现实世界糖尿病数据的应用!)
使用随机森林,我们发现预测患者存活的前 3 个特征依次是:甲胎蛋白(ng/dL)、血红蛋白(g/dL)和碱性磷酸酶(U/L)。(这些健康记录属性也在数据集所在的 UCI 数据库中列出。)

图片鸣谢:作者。前 3 个特征对应于最高的 3 个条。
基于模型的学习
现在到了我想投入精力和注意力的有趣部分:基于模型的学习。
我们已经填充了缺失的数据,并找到了最能提供信息的特征,现在让我们来看看数据集的形状。我们总共有 165 个数据记录,其中 102 例患者存活,63 例患者死亡。我们已经遇到了两个主要的“问题”——我们的数据集样本量对于机器学习来说不是那么大,我们有类别不平衡。
现在你可能会说,“Raveena,你为什么不对较小的类进行过采样,或者对较大的类进行过采样?”的确,我可以!我可以用称为 SMOTE 的合成采样对“病人死亡”类进行过采样。(这里有一个来自 sci-kit learn 的关于它的很棒的教程)然而,我正试图结合基于模型的学习,这意味着我将走更贝叶斯的路线——我将在下面简要解释。
基本的想法就像它的名字一样:我要做的是为幸存的患者创建一个具有前三个特征的模型,为那些不幸从癌症中死去的患者创建一个模型。希望是,一旦我有了这两个模型,对于一个新的测试患者,我可以比较每个模型的判断,并决定测试患者是否会生存,死亡,或由于巨大的不确定性而犹豫不决。
在最终决定之前,贝叶斯步骤还包括一件事:我将结合我们对病人的先验知识,并将其乘以模型判断。你会问,先验知识是什么?嗯,在训练集中有 73 名患者存活,42 名患者死亡——因为我之前提到的“阶级不平衡”。这听起来可能是件坏事。但是在基于模型的学习中,我们实际上可以利用它!我要做的是——结合存活和死亡患者之间的先前差异,以及模型判断——并使用这两者来做出最终决定。
创建模型
为了创建实际的模型,我将使用一种称为高斯混合的模型。用最简单的方式来描述它,高斯混合模型查看一堆数据,并试图将数据的分布分解为各个钟形曲线的基本总和,或高斯正态分布。每个更简单的正态分布都有一个与之相关联的特定权重,该权重告诉模型对该特定高斯分布的重视程度。这是高斯混合模型拟合数据的 GIF 图,链接到这里,下面是一个截图:

图片来源:维基百科。高斯混合模型及其组件的屏幕截图。您可以看到单独的红色、黄色、紫色、绿色和蓝绿色正态分布,每一个都添加了特定的权重,以给出整体较细的深蓝色曲线
高斯混合模型比核密度估计器更不容易过度拟合训练数据,核密度估计器通常试图最大化分布对训练数据的拟合,从而产生过度拟合的风险。
# Gaussian Mixture model for patients who survived carcinoma
life_model_2feature **=** GaussianMixture(n_components**=**6, random_state**=**0)**.**fit(X_train_2life)# Gaussian Mixture model for patients who died from carcinoma
death_model_2feature **=** GaussianMixture(n_components**=**4, random_state**=**0)**.**fit(X_train_2death)
我们将基于前 2 个特征来看存活和死亡患者的模型;我的代码中详细介绍了前 3 个特性。

图片鸣谢:作者。这是计算机为幸存病人准备的内部模型。这是通过根据它的模型生成 100 个幸存病人的合成样本来实现的

图片鸣谢:作者。这是电脑为死去的病人准备的内部模型。这是通过根据它的模型生成 100 个死亡病人的合成样本来实现的
模型预测
最后,是时候看看该模型将如何根据测试患者数据预测患者存活率了。不过,在开始之前,我想讨论一件重要的事情。
我们不能冒误报的风险
如果我们根据患者的甲胎蛋白和血红蛋白含量来预测患者是否会在这种特定的癌症中存活,那么意外预测患者会在癌症中存活是极其危险和不道德的,因为他们可能有很高的死亡几率。这在统计学中被称为“假阳性”,在这种情况下,阳性=存活,阴性=死亡。如果我们要对测试患者做出最终决定,如果我们做出了假阳性选择,我们需要更加重视——我们需要“惩罚”这个决定,这样它就不会发生,我们不会无意中对患者撒谎,告诉他们他们会在癌症中幸存下来,而他们在没有适当治疗的情况下死亡的几率要高得多。
假设我们想要做出假阳性决策的权重是假阴性决策的 5 倍。如果我们想惩罚 5 倍的假阳性决定,那么我们只能接受这样的决定,即如果最终的优势比高于某个数量,则预测患者“存活”。更具体地说:

图片鸣谢:作者。这基本上是贝叶斯定理,为了更重地惩罚 5 倍的假阳性决策,我们使决策赌注更高。
(只是作为一个重要的提醒,在这个项目中,Y=1 表示患者会存活,Y=0 表示患者不会存活于癌。)
(这方面的细节在代码中,所以你可以在这里自由地自己查看)。但是如果您感兴趣,这里有一个代码示例:
threshold = 5**for** odds **in** log_posterior_odds: **if** odds **>** np**.**log(threshold):
decisions**.**append(1)
**else**:
decisions**.**append(0)
那么……这个模型预测了什么?
实现这个决策规则,我们得到下面的混淆矩阵结果。(混淆矩阵只是一个 2x2 表格的花哨术语,它显示了模型在测试数据上的表现,有假阳性,也有假阴性— 不仅仅是准确性。准确性有时会误导人!)

模型做得不算太差!––50 道题中有 36 道正确,准确率为 72%。右上角的深紫色方框是假阳性——该模型预测了 5 名测试患者,他们将是健康的,但实际上不会在癌症中存活。
但是我想让你注意到,对于这个基于模型的学习框架来说,比准确性更重要的东西——因为记住,准确性不是一切。还记得我说过,我们将通过将比率阈值设置为 5 来惩罚犯有更多假阳性错误的模型(该模型对患者撒谎说他们将在癌症中存活)吗?
当我将阈值增加到 10 时,请注意右上角紫色框中的数字会发生什么变化,因此我们增加了 10 倍的权重:

通过将阈值增加到 10,模型少犯了一个假阳性错误,代价是犯了一个假阴性错误。
但是如果门槛很高,比如 20 岁,看看会发生什么。所以我们对假阳性分类错误的重视程度是假阴性的 20 倍。

现在这个模型真的开始最小化假阳性,这很好——但是这是以增加假阴性的巨大成本为代价的。这意味着太多可能不需要检查的患者被模型送到他们的医疗提供者那里进行检查。除非他们的保险可以涵盖定期检查,否则这对病人来说是不划算的。所以你可以看到,设置阈值是模型的一个平衡游戏。
最后的话
我希望这篇文章能让您了解基于模型的学习的灵活性,以及如何将它应用到健康领域的真实数据中,
在我看来,基于模型的学习的主要优势之一是这个框架可以应用于许多类型的表格数据,而不仅仅是健康记录。这种类型的机器学习甚至可以应用于视觉和语音识别领域,只要从数据中提取正确的特征类型,并使用有效的概率生成模型。
我认为的另一个优势是,我们对假阳性、假阴性的最终分析,以及根据我们数据的域来补偿任一错误,使用基于模型的学习是非常简单的。我们所要做的就是决定我们对惩罚假阳性或假阴性的重视程度,并像我们所做的那样设定一个阈值。在 Python 中只需要几行代码,但是这种分析在例如医疗保健领域是非常重要的,在那里,不管准确性如何,出现假阳性错误可能比假阴性更昂贵或更致命。**
非常感谢您的阅读,我们将在下一篇文章中再见!
如果你想了解更多关于我的信息,我对机器学习和数据科学感兴趣,我想从事什么,你可以在这里查看我的 LinkedIn。
Scikit-learn 中的模型评估
原文:https://towardsdatascience.com/model-evaluation-in-scikit-learn-abce32ee4a99
机器学习
关于如何使用 scikit-learn 计算最常见的回归和分类指标的教程。

Scikit-learn 是最流行的用于机器学习的 Python 库之一。它提供了模型、数据集和其他有用的功能。在本文中,我将描述 scikit-learn 为模型评估提供的最流行的技术。
模型评估允许我们评估模型的性能,并比较不同的模型,以选择最佳模型投入生产。模型评估有不同的技术,这取决于我们想要解决的具体任务。在本文中,我们重点关注以下任务:
- 回归
- 分类
对于每个任务,我将通过一个实际的例子来描述如何计算最流行的指标。
1 加载数据集
作为一个示例数据集,我使用由 UCI 机器学习库提供的葡萄酒质量数据集。要使用此数据集,您应该正确引用来源,如下所示:
- Dua d .和 Graff c .(2019 年)。UCI 机器学习知识库[http://archive . ics . UCI . edu/ml]。加州欧文:加州大学信息与计算机科学学院。
- 页(page 的缩写)科尔特斯、塞德伊拉、阿尔梅达、马托斯和雷伊斯。
通过物理化学特性的数据挖掘建立葡萄酒偏好模型。在决策支持系统中,爱思唯尔,47(4):547–553,2009。
我下载了数据文件夹,其中包含两个数据集:一个是红酒数据集,另一个是白酒数据集。我构建了一个数据集,它是两个数据集的串联,如下所示。
我将两个数据集都作为熊猫数据帧加载,然后合并它们:
import pandas as pdtargets = ['red', 'white']
df_list = []
df = pd.DataFrame()
for target in targets:
df_temp = pd.read_csv(f"../Datasets/winequality-{target}.csv", sep=';')
df_temp['target'] = target
df_list.append(df_temp)
print(df_temp.shape)df = pd.concat([df_list[0], df_list[1]])
我添加了一个新列,它包含原始数据集名称(红色或白色)。数据集如下表所示:

作者图片
数据集包含 6497 行和 13 列。
现在,我定义一个函数,它对所有分类列进行编码:
from sklearn.preprocessing import LabelEncoderdef **transform_categorical**(data):
categories = (data.dtypes =="object")
cat_cols = list(categories[categories].index)
label_encoder = LabelEncoder()
for col in cat_cols:
data[col] = label_encoder.fit_transform(data[col])
我还定义了另一个函数,它缩放数字列:
from sklearn.preprocessing import MinMaxScalerdef **scale_numerical**(data):
scaler = MinMaxScaler()
data[data.columns] = scaler.fit_transform(data[data.columns])
2 回归
为了评估回归模型,最常用的指标是:
- 平均绝对误差 —实际值与预测值之差的平均值。它衡量预测与实际产出的差距。MAE 越低,模型越好。
- 均方根误差 —均方误差的平方根(MSE)。MSE 计算实际值和预测值之差的平方的平均值。
- R2 分数—Y 中可以用 x 解释的方差比例
作为一项回归任务,我希望在给定其他特征的情况下,预测每个记录的 pH 值。我将 X 和 y 变量定义如下:
X = df.drop("pH", axis = 1)
y = df["pH"]
然后,我将类别转换成数字,并对数字进行缩放:
transform_categorical(X)
scale_numerical(X)
因此,我有以下输入数据集:

作者图片
现在,我将数据集分为训练集和测试集:
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = **train_test_split**(X, y, test_size=0.20, random_state=42)
我定义了一个辅助函数,它接收模型作为输入,然后对其进行训练和测试:
from sklearn.metrics import mean_absolute_error,r2_score,mean_squared_error
import numpy as npdef **run_experiment**(model):
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print("R^2 : ", r2_score(y_test, y_pred))
print("MAE :", mean_absolute_error(y_test,y_pred))
print("RMSE:",np.sqrt(mean_squared_error(y_test, y_pred)))
现在我建立一个线性回归模型,并测试它的性能:
from sklearn.linear_model import LinearRegression
model = **LinearRegression**()
run_experiment(model)
作为输出,run_experiment()函数返回以下结果:
R^2 : 0.6508427991759342
MAE : 0.07476031320105749
RMSE: 0.09761343652989583
我还建立了另一个回归模型,基于随机梯度下降:
from sklearn.linear_model import SGDRegressor
model = SGDRegressor()
run_experiment(model)
作为输出,我得到了以下结果:
R^2 : 0.6243269738606405
MAE : 0.07703814197219305
RMSE: 0.10125211591520658
两个实验的比较表明,线性回归器具有较低的 RMSE,因此它优于另一个模型。
3 分类
最常见的分类评估指标有:
- 精确——在所有积极的预测中,数一数有多少是真正积极的。
- 回忆 —在所有真正的阳性病例中,统计有多少被预测为阳性。
- 准确性 —在所有案例中,计算有多少案例被正确预测。
作为分类任务,我考虑目标类(红/白)的预测。因此,我构建 X 和 y 变量如下:
X = df.drop("target", axis = 1)
y = df["target"]
现在,我将分类列转换为数字列,然后缩放所有数字列:
transform_categorical(X)
scale_numerical(X)
我还对目标类进行了编码:
label_encoder = LabelEncoder()
y = label_encoder.fit_transform(y)
现在,我将数据集分为训练集和测试集:
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=42)
现在,我定义一个辅助函数,它接收模型作为输入,通过计算之前定义的指标来训练和测试它:
def **run_experiment**(model):
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
plot_confusion_matrix(model, X_test, y_test, cmap='GnBu')
plt.show() print('Precision: %.3f' % precision_score(y_test, y_pred))
print('Recall: %.3f' % recall_score(y_test, y_pred))
print('F1: %.3f' % f1_score(y_test, y_pred))
print('Accuracy: %.3f' % accuracy_score(y_test, y_pred))
我构建了一个随机森林分类器,然后计算它的性能:
from sklearn.ensemble import RandomForestClassifiermodel = RandomForestClassifier()
run_experiment(model)
该函数返回以下输出:
Precision: 0.994
Recall: 0.999
F1: 0.996
Accuracy: 0.995
我还训练了一个决策树分类器:
from sklearn.tree import DecisionTreeClassifiermodel = DecisionTreeClassifier()
run_experiment(model)
该函数返回以下输出:
Precision: 0.992
Recall: 0.985
F1: 0.988
Accuracy: 0.983
就准确性而言,随机森林分类器的性能优于决策树分类器。
摘要
恭喜你!您刚刚学习了如何在 scikit-learn 中为分类和回归执行模型评估。所描述的技术没有考虑参数优化,因为本文的目的是展示最常见的评估指标。
如果你读到这里,对我来说,今天已经很多了。谢谢!你可以在这篇文章中读到更多关于我的内容。
相关文章
从社区收集的文章

评估指标
View list2 stories


数据分析
View list4 stories


模型解释不是弱监督分割
原文:https://towardsdatascience.com/model-explanation-is-not-weakly-supervised-segmentation-68610987420

特色图片(天空拼图)来自维基百科(知识共享许可)。
在这篇文章中,我们将比较三个相关但不同的计算机视觉任务,它们可以用卷积神经网络来处理:图像分类模型解释、弱监督分割和完全监督分割。我们将考虑目标、训练数据和方法的异同。请注意,我将把重点放在这些任务的神经网络方法上,尽管所有这三项任务都可以通过非神经网络方法来解决。
如果你想引用这篇文章,它是基于我的论文“使用 HiResCAM 而不是 Grad-CAM 来忠实地解释卷积神经网络”的摘录。
本帖将使用一个基于该船图像草图的运行示例:

作者图片
目标
首先,让我们考虑一下目标:
- 模型解释:突出显示区域用于预测对象的模型
- 弱监督或全监督分割:识别作为对象一部分的所有像素
目标的哲学差异对判断任务的成功有重要的影响,接下来会详细讨论。
模型解释的目标
模型解释的目的是演示模型在图像中的哪些位置用于进行特定预测— ,即使这意味着突出显示相关对象之外的区域。例如,如果一个模型使用轨道来识别一列火车,解释应该突出轨道。如果模型使用水来识别一艘船,解释应该突出水。

作者图片
因此,评估解释正确性(“解释质量”)的任何性能度量都必须根据“模型用于每个预测的位置”这一基本事实来计算,这反过来只能通过模型和解释方法的数学属性来揭示。例如,在 HiResCAM 的论文中,我们证明了 HiResCAM 准确地解释了 CNN 使用的位置,这些位置以一个完全连接的层结束。
“模型使用的位置”可能与对象分割图没有任何关系,这些位置也不能由人类手动创建,因为如果人类能够足够好地理解模型以限定用于每个预测的区域,那么首先就不需要解释方法。
分割的目标
分割的目标是识别作为对象一部分的所有像素。
在弱监督分割中,这个目标必须仅使用整个图像标签来实现。在完全监督分割中,地面真实分割图可用于训练。

作者图片
因为从图像分类器导出的弱监督分割图是“模糊的”,并且通常倾向于集中在对象的小的区别部分,所以弱监督分割方法包括阈值处理和后处理步骤,旨在将原始“模糊”分割图变换/扩展成更清晰和更准确的图(例如,通过使用条件随机场)。

作者图片
借据不得用于评估解释正确性
分割任务的一个常见性能指标是交集/并集(IoU ),当一个类的预测分割与该类的基本事实分割完全重叠而没有扩散到其他区域时,该指标最高。
IoU 是通过将模型的预测分割中的像素与真实分割中的像素进行比较来计算的。真阳性“tp”(预测和实际情况中的像素)、假阳性“fp”(预测中不在实际情况中的像素)和假阴性“fn”(不在预测中但存在于实际情况中的像素)用于计算 IoU,如下图所示,这是假设医学图像分类任务的肺和心脏的卡通图:

作者图片
虽然 IoU 是判断分段性能的合理指标,但 IoU 永远不应该用于评估解释的正确性。不幸的是,一些论文试图通过实验来评估解释的正确性,使用根据基本事实对象分段计算的 IoU,或使用密切相关的设置,要求人类主观判断解释与对象的对应程度(这实际上对应于人类估计类似 IoU 的数量)。这些实验中不言而喻的假设是,分类模型总是使用相关对象来预测类别,因此“好的”解释方法将实现高 IoU。然而,如前所述,模型并不保证总是使用相关的对象来预测类,事实上,不期望的模型行为的可能性是解释方法开发背后的主要动力。
任何时候,一个模型的行为出乎意料或利用虚假的相关性,真实模型解释的 IoU 将会很低,但如果得出低 IoU 意味着解释质量差的结论,那就错了。要知道一个解释方法是否忠实于一个特定类型的模型,唯一的方法是根据模型的定义和解释方法的定义来证明它。
忠实解释方法的 IoU 提供了对模型的洞察
虽然 IoU 不能用于评估解释方法的质量,但是基于具有保证属性的解释方法(例如 CAM 或 HiResCAM )计算的 IoU 可以用于评估特定的型号。在这些情况下,高 IoU 表示该模型倾向于根据需要使用感兴趣对象内的区域进行预测,而低 IoU 表示该模型倾向于使用感兴趣对象外的区域进行预测,从而利用背景或相关对象。
方法
既然我们已经概述了解释和分段的不同目标和评估,让我们考虑一下用于每项任务的方法。
模型解释可以通过显著图、 CAM 、 HiResCAM 等技术完成;显著图通过了完整性检查,CAM 和 HiResCAM 都具有保证的忠实性。 Grad-CAM 通常被用作解释技术,但事实上并不适合用于模型解释,因为它有时会突出显示模型并未实际用于预测的区域。其他模型解释方法已被证明更像边缘检测器而不是解释,因此在选择模型解释方法时,选择一个具有保证属性的方法非常重要,以避免产生混淆模型实际工作的误导性解释。
有些令人困惑的是,弱监督分割方法通常建立在 CAM 家族解释方法的基础上,因此许多弱监督分割方法的名称中都有“CAM”,尽管它们的目标是分割而不是模型解释:例如, Mixup-CAM 、子类别 CAM 和 Puzzle-CAM 。重要的是要记住,即使这些弱监督的分割方法利用了模型解释方法,它们也有一个根本不同的目标。
像 U-Nets 或 Mask R-CNNs 这样的完全监督分割模型利用了基础真实分割掩码。全监督分割模型通常优于弱监督分割模型,因为全监督模型具有更多可用信息。如上所述,弱监督分割图是从仅在存在/不存在标签上训练的分类模型中导出的——例如,对于整个图像,“boat=1”或“boat=0”。全监督分割模型在像素级标签上训练;典型的 RGB 自然图像可能具有大约 256×256 = 65,536 个像素。因此,与弱监督分割模型相比,全监督分割模型在数量级的更多信息上被训练。完全监督分割的缺点是获得像素级标签的费用,这限制了训练样本的数量和可以考虑的不同类别的数量。
总结
下表总结了这些任务之间的相似之处和不同之处:

作者图片
快乐解释和分段!
原载于 2022 年 7 月 2 日 http://glassboxmedicine.comhttps://glassboxmedicine.com/2022/07/02/model-explanation-is-not-weakly-supervised-segmentation/。
机器学习模型的可解释性和可解释性
原文:https://towardsdatascience.com/model-interpretability-and-explainability-27fe31cc0688
SHAP,石灰,可解释的助推机器,显著图,TCAV,蒸馏,反事实,和解释

艾蒂安·吉拉尔代在 Unsplash 上拍摄的照片
ML/AI 模型变得越来越复杂,越来越难以解释和说明。简单、易于解释的回归或决策树模型不再能够完全满足技术和业务需求。越来越多的人使用集成方法和深度神经网络来获得更好的预测和准确性。然而,那些更复杂的模型很难解释、调试和理解。因此,许多人将这些模型称为黑盒模型。
当我们训练一个 ML/AI 模型时,我们通常专注于技术细节,如步长、层、早期停止、丢失、膨胀等等。我们真的不知道为什么我们的模型会以某种方式运行。例如,考虑一个信用风险模型。为什么我们的模型会给个人分配一定的分数?我们的模型依赖于什么特性?我们的模型是否严重依赖一个不正确的特征?即使我们的模型没有将种族和性别作为输入特征,我们的模型是否会从其他特征中推断出这些属性,并引入对某些群体的偏见?利益相关者能理解并信任模型行为吗?该模型能为人们提供如何提高信用评分的指导吗?模型解释和说明可以提供对这些问题的洞察,帮助我们调试模型,减少偏差,并建立透明度和信任。
人们对机器学习模型的可解释性和可解释性越来越感兴趣。研究人员和 ML 实践者已经设计了许多解释技术。在这篇文章中,我们将提供八种流行的模型解释技术和工具的高级概述,包括 SHAP、Lime、可解释的助推机器、显著图、TCAV、蒸馏、反事实和解释。
SHAP

图一。SHAP 解释机器学习模型输出(来源:https://shap.readthedocs.io/,麻省理工许可)
“SHAP(SHapley Additive exPlanations)是一种博弈论方法,用来解释任何机器学习模型的输出。它使用博弈论及其相关扩展中的经典 Shapley 值,将最优信用分配与本地解释联系起来。
图 1 显示了 SHAP 工作的要点。假设基本速率,即预测值 E[f(x)]的先验背景期望值是 0.1。现在我们有了一个新的观察结果,特征年龄=65,性别=F,血压=180,身体质量指数=40。这个新观察的预测值是 0.4。我们如何解释 0.4 的产值和 0.1 的基础率之间的这种差异?这就是 Shapley 值发挥作用的地方:
- 先说基础率 0.1。
- 加上身体质量指数 Shapely 值 0.1,我们得到 0.2。
- 加上 BP Shapely 值 0.1,我们得到 0.3。
- 加上性感匀称值-0.3,我们得到 0。
- 加上年龄匀称值 0.4,我们得到 0.4
基于 Shapely 值的加性,这个新观察的模型预测是 0.4。SHAP 值提供了对每个要素重要性的深入了解,并解释了模型预测的工作原理。
我们如何计算 Shapley 值?SHAP 绘制了线性模型的部分相关图,其中 x 轴代表特性,y 轴代表给定特性时输出的期望值(见图 2)。特征的 Shapley 值是在该特征的给定值,即图中红线的长度,预期模型输出和部分相关性图之间的差。

图二。部分依赖情节(来源:【https://shap.readthedocs.io/】T2,麻省理工学院许可)
Shapley 值的计算可能很复杂,因为它是所有可能的联盟排列的平均值。shap库使用采样和优化技术来处理所有复杂的计算,并为表格数据、文本数据甚至图像数据返回直观的结果(参见图 3)。通过conda install -c conda-forge shap安装 SHAP 并试一试。

图 3。https://shap.readthedocs.io/,麻省理工学院许可,SHAP 解释图像分类(来源:
石灰
模型可以是全局复杂的。Lime(局部可解释模型不可知解释)不是关注整体复杂模型行为,而是关注局部区域,并使用线性近似来反映预测实例周围的行为。
图 4 展示了 Lime 的工作原理。蓝色/粉色背景表示原始复杂模型的决策函数。红十字(我们称之为 X)是我们想要预测和解释的实例/新观察。
- X 周围的采样点
- 使用原始模型预测每个采样点
- 根据样本与 X 的接近程度对样本进行加权(图中权重较大的点对应于较大的尺寸)
- 在加权样本上拟合线性模型(虚线)
- 用这个线性模型来解释 X 附近的局部

图 4。石灰直觉(来源:https://github.com/marcotcr/lime,BSD 执照)
使用 Lime,我们可以在本地解释表格数据、文本数据和图像数据的模型行为。下面是一个使用 Lime 来解释文本分类器的例子。我们可以看到这个分类器正确地预测了实例,但是出于错误的原因。

图 5。石灰解释文本分类器示例(来源:https://github.com/marcotcr/lime,BSD 许可)
要了解更多关于 Lime 的信息,请查看 Github 页面并通过pip install lime安装。
可解释的助推器
“可解释的助推机 (EBM)是一个基于树的、循环梯度助推广义加法模型,具有自动交互检测功能。EBM 通常与最先进的黑盒模型一样精确,同时保持完全可解释性。”
EBM 的工作原理如下:
- 在每次迭代中,我们以循环方式一次一个特征地训练一个装袋和梯度提升树。我们首先在第一个特征上训练,然后更新残差并在第二个特征上训练,并继续直到我们完成所有特征的训练。
- 然后我们重复这个过程很多很多次。
- 因为 EBM 逐个特征循环,所以它可以显示每个特征在最终预测中的重要性。
EBM 是在 interpret.ml 中实现的,我们将在本文后面介绍。
显著图
显著图方法广泛用于解释神经网络图像分类任务。它测量每个像素的重要性,并突出显示哪些像素对预测很重要。在高层次上,显著性图采用每个类别相对于每个图像输入像素的梯度或导数,并将梯度可视化(参见图 6)。

图 6。显著地图(来源:https://pair-code.github.io/saliency/#guided-ig,阿帕奇许可)
PAIR 显著性项目提供了“最新显著性方法的框架不可知实现”,包括引导集成梯度、XRAI、SmoothGrad、香草梯度、引导、反向传播、集成梯度、遮挡、Grad-CAM、模糊 IG。
要了解更多关于显著性方法的信息,请查看 Github 页面并通过 pip 安装显著性。
TCAV
TCAV 主张用概念激活向量(CAV)进行定量测试。TCAV“量化了用户定义的概念对分类结果的重要程度——例如,斑马的预测对条纹的存在有多敏感”(金,2018)。

图 7。用概念激活向量进行测试。(资料来源:Kim,2018 年)
TCAV 执行以下步骤来确定一个概念是否重要:
- 定义概念激活向量(图 7 步骤 a-d)
TCAV 使用概念图像(带有条纹物体的图像)和随机图像的例子作为输入,并检索层激活。然后,它训练一个线性分类器来分离激活,并采用与超平面决策边界正交的向量(CAV)。CAV 代表图像中的条纹。
- 计算 TCAV 分数(图 7 步骤 e)
TCAV 分数是通过对 CAV 取方向导数来计算的。它代表了模特对条纹等特定概念的敏感度。
- CAV 验证
测试一个概念是否有静态意义。同样的过程可以在随机图像和随机图像中进行。我们可以将概念对随机图像 TCAV 分数分布与随机对随机图像 TCAV 分数分布进行比较。可以进行双侧 t 检验来检验 TCAV 分数分布差异。
要了解更多关于 TCAV 的信息,请查看 Github 页面并通过pip install tcav安装。
蒸馏
“在机器学习中,知识提炼是将知识从大模型转移到小模型的过程。”
在模型解释上下文中,大模型是黑盒模型,也是教师模型。较小的模型是解释者,学生模型。学生模型试图模仿教师模型的行为,并且是可解释的。
例如,可以构建一个决策树来近似原始的复杂模型(Bastani,2019)。Bastani 的论文“提出了一种用于学习决策树的模型提取算法——为了避免过度拟合,该算法通过主动采样新输入并使用复杂模型标记它们来生成新的训练数据。”

图 8。用决策树解释黑盒模型。(来源:通过模型提取解释黑盒模型。奥斯伯特.巴斯塔尼,卡罗琳.金,哈姆萨.巴斯塔尼。2019
反事实的
反事实描述了改变模型预测所需的最小输入特征变化量。反事实提出了很多假设问题。如果我们增加这个功能或减少那个功能会怎么样?例如,根据黑盒模型,约翰有很高的心脏病风险。如果约翰一周锻炼 5 天会怎么样?如果约翰是素食者呢?如果约翰不抽烟会怎么样?这些变化会导致模型预测的变化吗?那些反事实提供了易于理解的解释。
有很多关于生成反事实的研究和方法。例如,骰子(不同的反事实解释)为同一个人生成一组不同的特征扰动选项,该人的贷款被拒绝,但如果收入增加 10,000 美元或收入增加 5,000 美元并有 1 年以上的信用历史,则该人会获得批准。DiCE 在支持用户特定要求的情况下,对原始输入的多样性和接近度进行了优化。
要了解更多关于 interpretML 的信息,请查看文档并通过conda install -c conda-forge dice-ml安装。

图 9。骰子(来源:https://github.com/interpretml/DiCE,麻省理工学院许可)
解释性语言
" InterpretML 是一个开源包,在一个屋檐下集成了最先进的机器学习可解释性技术。"
interpretML 提供了对 glassbox 模型的解释,包括
- 可解释的助推器,
- 线性模型,
- 决策树,
- 和决策规则。
它还解释了使用
- Shapley 加法解释,
- 本地可解释的模型不可知的解释,
- 部分相关图,
- 和莫里斯敏感度分析。
interpretML 的结果可以显示在一个带有良好交互界面的 Plotly 仪表盘中:

图 10。interpretML 仪表板(来源:https://interpret.ml/docs/getting-started.html,麻省理工学院许可)
要了解更多关于 interpretML 的信息,请查阅文档并通过conda install -c interpretml interpret进行安装。
结论
总的来说,我们已经对一些流行的模型可解释性技术和工具进行了高层次的概述,包括 SHAP、Lime、可解释推进机、显著图、TCAV、蒸馏、反事实和解释。每种技术都有自己的变化。我们将在以后详细讨论每种技术。
参考资料:
- https://shap.readthedocs.io/
- “我怎么忽悠你?”:通过误导性的黑箱解释操纵用户信任。奥斯伯特.巴斯塔尼。AAAI/ACM 人工智能、伦理和社会会议(AIES),2020 年。
- 对黑盒模型的忠实和可定制的解释。Himabindu Lakkaraju,Ece Kamar,Rich Carauna,Jure Leskovec。AAAI/ACM 人工智能、伦理和社会会议(AIES),2019 年。
- https://explainml-tutorial.github.io/neurips20
- 【https://christophm.github.io/interpretable-ml-book
- https://homes.cs.washington.edu/~marcotcr/blog/lime/
- “我为什么要相信你?”:解释任何分类器的预测。马尔科·图利奥·里贝罗,萨姆尔·辛格,卡洛斯·盖斯特林。2016 年第 22 届 ACM SIGKDD 知识发现和数据挖掘国际会议论文集。
- 对黑盒模型的忠实和可定制的解释。拉卡拉朱、卡马尔、卡鲁阿纳和莱斯科维奇。AIES,2019。
- 通过模型提取解释黑盒模型。奥斯伯特.巴斯塔尼,卡罗琳.金,哈姆萨.巴斯塔尼。2019
- 特征归因之外的可解释性:概念激活向量的定量测试(TCAV)。被金,马丁瓦滕伯格,贾斯汀吉尔默,卡莉蔡,詹姆斯韦克斯勒,费尔南达维加斯,罗里塞尔。2018.
- https://www.youtube.com/watch?v=Ff-Dx79QEEY
- pair-code.github.io/saliency/
- https://interpret.ml/
- 通过不同的反事实解释来解释机器学习分类器。陈豪·谭·阿米特·夏尔马·拉马鲁文·k·莫西拉尔 2019.
. . .
最初发表于anaconda.org
作者索菲亚杨 2022 年 8 月 23 日。
Sophia Yang 是 Anaconda 的高级数据科学家。在 LinkedIn 、 Twitter 和 YouTube 上与我联系,并加入 ds/ml❤️读书俱乐部
用火花进行模型预测和分配
原文:https://towardsdatascience.com/model-prediction-and-distribution-with-spark-9710c3065e62
如何用 Spark 实现和分发机器学习模型——py Spark 实现

由于 Apache Spark,任务处理可能是分散的。通过将内存中的属性与 Spark SQL 功能结合使用,它增强了这一过程。Spark 的分布式数据记录和处理方法是通过包括分布式脚本、数据处理、数据工作流的创建和具有 MLlib 函数的机器学习技术在内的功能实现的。
Spark 可以根据平台以不同的方式安装。在本节中,我们将重点关注本地安装。
Apache Spark 可以在任何安装了 Python、Scala 或 Java 的环境中运行。本文将关注 Python 语言。紧凑快速地安装所需的 Python 包和 Jupyter Notebook 的最简单方法是使用 Anaconda 。
探索性数据分析
“ny T2”数据集将在整篇文章中使用。你可以通过 Kaggle 网站从这个链接下载。
数据集的格式具有使用“JSON”函数的基本要求。
tr_df = spark.read**.**json('dataset/nyt2.json')ts_df = spark.read**.**json('dataset/nyt2.json')
新特征生成:特征工程
通过使用特征工程,可以从数据集的当前变量中收集更多的信息数据。
个人的头衔也包含在 Titanic 数据集的“姓名”列中。该模型可以从该信息中受益。然后创建一个新的变量。可以使用' withColumn '方法添加新的标题列。
tr_data = tr_df.withColumn("writer", regexp_extract(col("author"),"([A-Za-z]+)\.", 1))
可能有一些重复的作者姓名。“替换”功能可用于替换它们。
feature_dataframe = tr_data.\
replace(["Jane Greenn",
"Stepheniei Meyer",
"Jimmy Buffett"],
["Jane Green",
"Stephenie Meyer",
"Jimmy Buffett"])
标题的分布看起来比先前遵循替换过程更精确。
用 Spark MLlib 建模
最终的建模数据集必须将所有字符串格式的列转换为正确的数字类型,因为预测算法需要数字变量。
from pyspark.ml.feature import StringIndexerwriterIndexer = StringIndexer(inputCol="writer", outputCol="writer_Ind").fit(feature_dataframe)descriptionIndexer = StringIndexer(inputCol="published_date", outputCol="published_ind").fit(feature_dataframe)
数据帧中包含所有数字变量,因为以前的字符串格式操作已被删除并编入索引。我们可以使用数据帧中的列来创建特征向量,因为每一列都具有非字符串格式。
from pyspark.ml.feature import VectorAssemblerassembler = VectorAssembler(
inputCols = ["writer","price","published_ind"],
outputCol = "features")
当创建流水线时,分类器的参数可以在` ParamGridBuilder 的帮助下进行优化。网格搜索后将创建相应的参数。
from pyspark.ml.tuning import ParamGridBuilder
pg = ParamGridBuilder().build()
评估指标
精确度可用作模型评估的统计数据。“精确度”数学公式。
带 MLFlow 的 MLOps】
对于 PySpark 模型,MLFlow 可用作模型服务库。根据官方文档中的说明,可以为 PySpark 编程安装该库。
pip install mlflow
“mlflow.pyfunc”函数可用于填充相关的模型推断。为此,独立分配模型和数据集路径至关重要。然后,模型路线可用于创建火花 UDF。接下来是读取它们并将其注册到数据帧中。先前建立的火花 UDF 在最后阶段用于构造新特征。
在我的 GitHub repo 中可以找到这个模型预测和分发脚本的完整实现示例版本!
非常感谢您的提问和评论!
参考
使用 TensorFlow API 在深度神经网络中剪枝模型

艾蒂安·德洛里欧在 Unsplash 上拍摄的照片
机器学习中最常见的问题之一就是过拟合。这可能是由多种原因造成的[1]。为了解决这个问题,一个常见的解决方案是在模型中添加正则化项。另一种方法是通过减少参数的数量来降低模型的复杂性。要获得最佳解决方案,应结合使用这两种方法。在本文中,我们将探讨后者,更具体地说,如何使用 Tensorflow 模型优化 API 在 Keras Tensorflow 模型中合并模型修剪(包括删除模型中多余的权重)。
什么是修剪,为什么选择修剪?
修剪机器学习模型包括移除(永久设置为 0)某些权重。通常被修剪的权重是那些已经接近 0(绝对值)的权重。这阻止了模型过度拟合,因为在训练开始时被认为无用的权重不能再次被重新激活。
有不同的方法来修剪模型。人们可以在开始时修剪一些随机数量的权重。人们可以在训练结束时进行修剪,以简化模型并使其更轻。在这里,我们将看到如何建立一个修剪调度程序,它将在每批学习结束时慢慢修剪一些权重,直到达到所需的稀疏度(权重百分比设置为 0)。
有人可能想知道为什么模型应该被修剪,而不是从一开始就用较少的可训练参数初始化。这样做的原因是,为了增加模型容量,您可能希望保持一个相当复杂的模型架构,在特性之间有许多可能的交互,但是限制它们的数量。此外,微调哪些层的大小应该减少,哪些层的大小应该增加,以及哪些功能应该保留,往往是一项乏味而徒劳的工作。在训练过程中,为了一点一点地去除多余的权重,对模型进行修剪要简单得多。这种方法的一个优点是,它允许人们训练多个不同的模型,这些模型可能已经被不同地修剪,然后人们可以使用各种集成学习技术来组合这些模型[2]。这些集合模型通常比单一模型更加稳健。我们现在将看到如何实现这一技术。
张量流模型优化工具包
目标是在每个训练步骤(批次)结束时消除最弱的重量。虽然人们可以实现自己的回调来做到这一点,但幸运的是,已经存在一个名为 tensor flow Model Optimization(TF mot)的 Tensorflow API 来做这件事[3]。这个工具允许用户定义一个修剪调度程序,它将自动处理权重消除。调度程序遵循多项式衰减调度。一个提供初始稀疏度、最终稀疏度、开始修剪的步骤、结束修剪的步骤,最后是多项式衰减的指数。在每一步,该工具包将消除足够的权重,以使实现的稀疏度为:

多项式衰减调度程序的公式
其中 S 为稀疏度, S ₑ为最终稀疏度, S ₀为初始稀疏度, t 为当前时间步,tₑ为结束步, t ₀为开始步,α为指数。α的默认值是 3。为了找到最佳值,需要考虑其他超参数。理想情况下,应该在相当长的时间内一点一点地削减权重,以便网络有时间适应权重的损失。事实上,如果权重修剪得太快,就有删除权重的风险,这些权重可能仅仅是因为它们被初始化为接近 0,而网络没有时间学习。
为了更好地理解这些概念,我们将通过一个例子来说明如何在您的模型中应用修剪。
修剪:一个例子
修剪最适用于具有许多参数的模型(如 MLPs ),并且我们怀疑许多参数是无用的。当我们的数据集有许多特征(几百个),但并不是所有的特征都是相关的时,就会出现这种情况。自然地,如果知道哪些特征是需要的,哪些是不需要的,那么在过程的特征工程步骤中,应该使用降维技术(例如 PCA、NMF [4]等)来消除不相关的特征。).然而,情况并非总是如此。
对于我们的示例数据集,我们将使用 sklearn.datasets 库的 make_regression 函数。此函数生成一个回归数据集,用户可以在其中指定所需的观测值数量、要素数量、相关要素数量以及数据的噪声程度。
**import** pandas **as** pd
**import** numpy **as** np
**from** sklearn.model_selection **import** train_test_split
**from** sklearn.datasets **import** make_regression*# Parameters of the data-set*
**n_samples** = 10000
**n_features** = 1000
**n_informative** = 500
**noise** = 3*# Create dataset and preprocess it*
x, y = **make_regression**(**n_samples**=n_samples, **n_features**=n_features, **n_informative**=n_informative, **noise**=noise)
x = x / **abs**(x).**max**(axis=0)
y = y / **abs**(y).**max**()
x_train, x_val, y_train, y_val = **train_test_split**(x, y, **test_size**=0.2, **random_state**=42)
现在我们有了我们的例子,让我们建立我们的模型。因为有大量的特征,并且为了我们的示例的目的,我们将设计一个具有许多参数的非常大的 MLP:
**import** tensorflow **as** tf
**from** tensorflow.keras.models **import** Sequential
**from** tensorflow.keras.layers **import** Dense, ReLU**model** = tf.keras.Sequential()
**model**.add(Dense(1024, kernel_initializer="he_normal", input_dim=n_features))
**model**.add(ReLU())
**model**.add(Dense(1024))
**model**.add(ReLU())
**model**.add(Dense(1))
这种架构产生了一个超过 2,000,000 个参数的模型,这是一个很大的数目,尤其是因为我们知道有一半的特性实际上是没有用的。我们如何整合修剪?这很简单。我们必须首先在字典中设置各种修剪参数。然后,我们使用 tfmot 使用修剪参数基于原始模型定义一个新模型。最后,我们添加一个修剪回调函数。
**import** tensorflow_model_optimization **as** tfmot**initial_sparsity** = 0.0
**final_sparsity** = 0.75
**begin_step** = 1000
**end_step** = 5000**pruning_params** = {
'pruning_schedule': tfmot.sparsity.keras.PolynomialDecay(
**initial_sparsity**=initial_sparsity,
**final_sparsity**=final_sparsity,
**begin_step**=begin_step,
**end_step**=end_step)
}**model** = tfmot.sparsity.keras.prune_low_magnitude(model, **pruning_params)
**pruning_callback** = tfmot.sparsity.keras.UpdatePruningStep()
然后,我们像往常一样编译和拟合模型,不要忘记在拟合步骤中添加回调:
**model**.compile(
loss="mse",
optimizer=tf.keras.optimizers.Adam(learning_rate=0.001)
)**model**.fit(
x_train,
y_train,
epochs=200,
batch_size=1024,
callbacks= pruning_callback,
verbose=1
)
选择超参数
我们现在有了修剪所需的所有元素。但是效果如何呢?你如何设置参数?我通常总是将初始稀疏度保持在 0,除非你有目的地想要用非常不同的随机初始化来创建多个稀疏模型,以便聚集它们。我设置了 begin 步骤,以便在几个时期后开始修剪。我通常设置的最后一步是在几个时期内进行修剪(大约 10 个时期)。
根据我的经验,这些参数没有最终的稀疏度参数重要,最终的稀疏度参数实际上取决于手头的问题。在过拟合的情况下,模型在训练集上的性能将比在验证集上好得多。在模型中引入一些稀疏性很可能会降低训练集的性能,但会提高验证集的性能。进一步增加稀疏度将继续提高验证集的性能。显然,当最终稀疏度太大时,模型变得太简单,训练集和验证集的性能都将开始下降,这意味着我们的模型现在对数据拟合不足。因此,作为最终稀疏度的函数,验证集上的性能存在最小值。
结果
在下图中,我采用了前面提到的例子,并用不同的最终稀疏度训练了不同的模型。模型的性能是用不同的随机初始化在 10 个不同的训练上平均的:

[0,1]中最终稀疏度的训练集和验证集的平均损失
对于非常低的最终稀疏度,模型明显过拟合,如上图所示。验证集的损失比训练集的损失大几个数量级。随着最终稀疏度的增加,训练集的损失单调增加,而验证集的损失在大约 95%稀疏度时具有最小值,这导致模型具有大约 100,000 个参数。这在下图中可以看得更清楚:

[0.8,1]中最终稀疏度的训练和验证集的平均损失
结束语
在这篇短文中,我们看到了使用 Tensorflow 模型优化工具包在 Tensorflow 模型中实现修剪非常简单,只需添加几行代码。修剪是减少过度拟合的另一种方法,与其他常用方法(如正则化、剔除和批量归一化)一起使用。修剪在模型有大量参数和数据集有很多特征的情况下表现出色。根据奥卡姆剃刀原理,它允许一个人在保持节俭的同时训练大容量的模型。然后,可以通过集成学习技术将这些模型结合起来,以构建更健壮的模型。
感谢
我要感谢尼古拉斯·莫里泽和克里斯托夫·盖斯勒对本文写作的帮助和支持。
参考文献
[1]https://hastie.su.domains/ElemStatLearn/(第七章)
https://link.springer.com/article/10.1007/s10462-009-9124-7
https://www.tensorflow.org/model_optimization/
https://hal.archives-ouvertes.fr/hal-03141876/document
https://www.jmlr.org/papers/volume22/21-0366/21-0366.pdf
关于我们
Advestis 是一家欧洲合同研究组织(CRO),对统计和可解释的机器学习技术有着深刻的理解和实践。Advestis 的专长包括复杂系统的建模和时间现象的预测分析。
领英:https://www.linkedin.com/company/advestis/
用 Python 实现亚马逊 Kindle 书评的模型选择和超参数调整
基于模型选择和超参数优化的书评情感分析

Emil Widlund 在 Unsplash 上拍摄的照片
介绍
这篇文章旨在选择和部署最佳的机器学习模型,对亚马逊 Kindle 商店的书评数据集进行情感分析。
在之前的一篇文章中,我们在一个 IMDB 电影评论数据库上优化了一个支持向量机算法。虽然 SVM 是分类问题的一个很棒的算法,但它也是最好的选择吗?对于这个新项目,现在的目标是包含模型选择机制。
通过管道化多个模型,我们可以从不同的方面比较它们,包括准确性和效率。
数据
你可以在下面的链接中找到这个数据集,它由大约 1,000,000 篇书评组成[1][6]。目标是通过对数据集的一部分进行训练,产生一个高性能的情感分析器。如果你想复习什么是情绪分析,我可以建议快速阅读这篇文章,它涵盖了所有的基础知识。
csv 文件的结构包括 10 列:
asin-产品的 ID,如 B000FA64PKhelpful-评价的有用性等级-例如:2/3。overall-对产品的评级。reviewText-审查文本(标题)。reviewTime-审查时间(未加工)。reviewerID-审核人的 ID,如 A3SPTOKDG7WBLNreviewerName-审核人的姓名。summary-评审总结(描述)。unixReviewTime- UNIX 时间戳。
对于该算法,我们只需要两个:评论语料库及其评级。
以下是一个示例评论:
I enjoy vintage books and movies so I enjoyed reading this book. The plot was unusual. Don't think killing someone in self-defense but leaving the scene and the body without notifying the police or hitting someone in the jaw to knock them out would wash today. Still it was a good read for me.
正如您所注意到的,该文本呈现出很少的“特殊”字符,这使得它更容易清理。最重要的是,我们应该删除所有不增加价值的介词和代词,因为它们太复杂了。
文本处理
尽管文本预处理不是本文的重点,我还是要简单解释一下,如果你想了解更多细节,请点击下面的链接,链接到我以前的文章之一。
在自然语言处理领域,文本包含各种各样对 ML 算法没有用的符号和单词。因此有一个阶段,称为文本处理,指的是文本的分析、处理和生成[2]。
这使得 ML 模型更容易训练,因为它减少了“噪声”,即对于预测无用的信息量。
模型
对于这个特殊的分类问题,我考虑了 7 种流行的高性能机器学习算法。每种方法都有其独特之处,很可能会返回与其他方法不同的结果:
- 线性判别分析(LDA) 关注组间方差的最小化以及组间方差的最大化。换句话说,该算法跟踪保证最大类别可分性的超平面(帮助分类数据点的决策边界)。
- k 个最近邻居(kNN) 创建多个 k 个最近邻居的数据记录 t 的“邻域”。然后,多数表决机制决定该记录 t 是否属于该类。该方法不考虑基于距离的加权。
- 高斯朴素贝叶斯 是一种概率分类器。简单和高效是它最显著的特点。贝叶斯定理是分类器的基础。它计算一个记录属于某个类别的概率[3]。
- 利用给定的独立变量数据集,逻辑回归计算事件发生的概率。为此,首先使用一个函数计算赔率,该函数返回一个介于 0 和 1 之间的数字,其中 0 表示不太可能发生。最后,应用 logit 变换将结果精确分类为 0 或 1。
- 决策树分类器(CART) 是一种通过“询问”一系列问题来分离数据的模型,因此决定将一个新记录放在哪个桶中。这会创建一个树形图,其中每个节点代表一个问题,每个叶子代表一个用于预测的输出变量。
- 【SVM】支持向量机是非概率分类器。与 LDA 类似,SVM 采用了超平面的概念。假设两个类是线性可分的,它们可以被超平面所除。在此基础上,该算法旨在通过在称为“向量”的超平面上的两条线来最大化类别 1 和类别 2 之间的空间。
超参数
在定义超参数之前,定义一个“标准”参数很重要。当一个模型收敛时,我们可以说它找到了描述它所训练的数据的一般行为的最佳参数组合。
每个机器学习模型都有一个隐喻性的架构。超参数让用户有机会使用实验方法找到模型“结构”的最佳配置。假设有 5 个超参数,每个超参数有 10 种可能的设置,当专业人员尝试各种设置组合直到性能达到峰值时,就会发生“调整”。例如,超参数 1 可能等于 5,超参数 2 可能等于 0.2,依此类推。
我想强调参数和超参数之间的根本区别。前者也意味着系数或权重,这是学习过程的结果。后者由用户在训练前手动设置。
代码部署
数据可视化
代码部署的第一部分侧重于数据探索和可视化。与文本评论相关的分数分布可以告诉我们有价值的信息。
**#Importing libraries**
import matplotlib.pyplot as plt
import pandas as pd**#Reading Dataset**
df = pd.read_csv('kindle_reviews.csv')**#Counting values within each review bucket**
category_dist = df['overall'].value_counts()**#Defining chart**
plt.figure(figsize=(8,6))
category_dist.plot(kind='bar')
plt.grid()
plt.xlabel("Scores", fontsize = 12)
plt.ylabel("Number of Reviews by Rating", fontsize = 12)
plt.title("Distribution of Reviews by Rating", fontsize = 15)**#Generating chart**
plt.show()

按评级列出的评论分布—按作者列出的图表
绝大多数评论都集中在 4 星和 5 星之间,这通常是不好的。数据集是不平衡的,因此当我们训练它时,算法可能会有偏差。
最重要的是,研究表明,与多类分类相比,二元分类总体上更准确[4]。由于我们要有更高的精度,我们需要将这个项目转化为一个二元分类问题。
进行改变的一种可能方式是将所有高于 3 分的评论视为“正面的”,而将所有低于 3 分(包括 3 分)的评论视为“负面的”。接下来的步骤是仅对数据集的一部分执行模型选择。
在将近 1,000,000 行上确定最佳模型可能会大幅增加处理时间,而不会增加任何越来越有价值的性能信息。
在从原始数据集中选择 50,000 行并将检查分数分成两类后,最终结果应该如下所示:

仅分析 50,000 条记录的评论分布(按情感分类)——按作者分类的图表
文本清理
代码部署的第二部分侧重于文本清理。如前所述,句子中的单词越多,复杂度越高,但对准确性的贡献却微乎其微。
解决方案在于消除停用词。在英语中,、【the】、、、等都可以作为停用词。在文本挖掘中,停用词被定义为携带很少有用信息的不重要的词。
最重要的是,单词需要统一,全部小写,特殊字符需要消除。如果我们在前面看到的例句上部署代码,结果类似如下:
enjoy vintage books movies enjoyed reading book plot unusual think killing someone self defense leaving scene body without notifying police hitting someone jaw knock would wash today still good read
你会同意我的观点,现在消极和积极的词更容易识别,例如,“享受”,“享受”和“好”标志着潜在的积极评价。检查后,评论家给这本书打了 5 颗星。在项目结束时,我们的模型应该将此归类为“积极”。
最后一步是增加负面评论的数量,直到它们达到相同数量的正面评论。“上采样”人工生成少数类的数据点,并将它们添加到数据集中。该过程旨在使两个标签具有相同的计数,并防止模型偏向多数类。
型号选择
代码部署的第三部分侧重于为我们的数据选择性能最佳的模型。
- Scikit-learn 和 matplotlib 是唯一需要的两个库。Sklearn 包括对数据执行机器学习所需的所有功能。每个模型需要分配一个“子库”:逻辑回归、KNeighborsClassifier、决策树分类器、SVC、GaussianNB 和线性判别分析。
- 在上一节中,我们使用了一种称为“上采样”的技术来匹配负面书评和正面书评的数量。在这个阶段,我们可以从数据中定义输入和目标变量。输入变量 x 为“ reviewText ”,包含评论的语料库;输出变量 y 为“”,包含标签“ 正 ”和“ 负 ”。
- 我们知道,一些用于分析的模型,包括决策树分类器,需要一个密集的矩阵。密集矩阵大多包含非零值。密集转换器类确保所有矩阵都是密集的,以避免任何错误。
- 然后创建一个名为“模型”的列表,然后将分配给每个模型的对象添加到列表中。另一方面,列表“结果”将包含与其名称相关联的所有不同的模型分数。
- kfold 参数表示我们需要多少 k 倍。这引入了交叉验证的概念。交叉验证旨在更好地估计模型的准确性。它被定义为 k 重交叉验证,k 是数据被划分的子组数。该模型在一个子组上训练,并在剩余的 k-1 上测试。准确度是根据平均分计算的。
- 管道在 k-fold of choice 上应用计数矢量器、密集转换器和选择模型,并执行交叉验证。然后它在控制台上打印结果。
- matplotlib 最后生成一个箱线图,这样我们就可以更好地解释结果。
为我们的数据寻找性能最佳的模型

模型选择过程后算法的准确性比较—作者提供的图表
箱线图为您提供了关于我们的模型在精确度方面的五个临界值的信息:最小值、第一个四分位数(25%)、中值(第二个四分位数)、第三个四分位数(75%)和最大值。
在 x 轴上,有所有用于交叉验证分析的模型。相反,在 y 轴上,我们有准确度分数。例如,逻辑回归模型是表现最好的模型,其最小精度为 0.82,最大精度为 0.86,但是支持向量机保证了最一致的性能,其最小值和最大值彼此接近。KNN 是表现最差的,表现出很少的一致性。LDA、CART 和朴素贝叶斯也表现不佳。
由于上述原因,超参数优化现在将集中在两个性能最高的模型上:逻辑回归和支持向量机。
支持向量机超参数调整
代码部署的第四部分关注支持向量机模型的超参数调整:
- 在前一个代码单元中已经导入了库,但是我决定重新导入它们以使这个代码片段独立
- 这次的流水线只包括计数矢量器和支持向量机模型,不需要密集转换器
- 转到参数列表,这些参数包含 grid_search 将为管道的每个组件尝试的所有可能的超参数组合。例如,计数矢量器的参数 max_df 负责模型的泛化。Max_df 删除出现太频繁的单词,max_df 为 0.7 会忽略出现在 70%以上文档中的术语。在一个场景中,“ vect__max_df ”将使用内核 poly 和 C 参数 10,将 0.7 的 max_df 与(1,1)的 ngram_range 组合起来。因为每个交叉验证进行了 5 次,所以总“适合”(组合)是 405。
- 在启动 grid_search.fit 之后,最后一部分代码开始在控制台上打印计算结果
为计数矢量器和 SVM 模型寻找最佳参数
结果如下:
*Best: 0.840734 using {'SVM__C': 10, 'SVM__kernel': 'rbf', 'vect__max_df': 0.7, 'vect__ngram_range': (1, 2)} 0.730129 (0.038366) with: {'SVM__C': 10, 'SVM__kernel': 'poly', 'vect__max_df': 0.7, 'vect__ngram_range': (1, 1)} 0.692791 (0.049500) with: {'SVM__C': 10, 'SVM__kernel': 'poly', 'vect__max_df': 0.7, 'vect__ngram_range': (1, 2)}*
性能最佳的超参数设置是:
- C = 10
- 核类型= rbf
- max_df = 0.7
- ngram_range = (1,1)
如果我们对 90,000 个向上采样的行应用上面找到的具有优化的超参数的管道,并且我们计算精确度方面的性能,结果是相当惊人的。针对 kindle 评论优化的支持向量机模型在分类新数据方面的效率为 99%。大约 1 小时的处理时间后显示结果。
*precision recall f1-score support Negative 0.98 1.00 0.99 8924
Positive 1.00 0.98 0.99 9109 accuracy 0.99 18033
macro avg 0.99 0.99 0.99 18033
weighted avg 0.99 0.99 0.99 18033*
逻辑回归超参数调整
代码部署的第五部分关注逻辑回归算法的超参数优化。所有代码都与上一节极其相似,只有很少的重要区别。
逻辑回归模型需要一个参数来表示它可以达到的最大迭代次数,没有这个参数,模型就不会收敛。最重要的是,与 SVM 模型相比,求解器的类型发生了变化。考虑用于分析的解算器是“牛顿-cg”、“lbfgs”和“liblinear”。
为计数向量机和逻辑回归模型寻找最佳参数
结果如下:
*Best: 0.857362 using {'LR__C': 1.0, 'LR__solver': 'newton-cg', 'vect__max_df': 0.7, 'vect__ngram_range': (1, 3)}
0.825643 (0.003937) with: {'LR__C': 100, 'LR__solver': 'newton-cg', 'vect__max_df': 0.7, 'vect__ngram_range': (1, 1)} 0.852704 (0.004520) with: {'LR__C': 100, 'LR__solver': 'newton-cg', 'vect__max_df': 0.7, 'vect__ngram_range': (1, 2)}*
性能最佳的超参数有:
- C = 1
- 求解器=牛顿-重心
- max_df = 0.7
- ngram_range = (1,3)
同样,如果我们对 90,000 个向上采样的行应用具有优化超参数的逻辑回归模型,结果非常接近 SVM 模型。在任何与机器学习相关的场景中,都很难达到 98%的准确率。这次的处理时间大致是 3 分钟。
*precision recall f1-score support
Negative 0.97 1.00 0.98 8924
Positive 1.00 0.96 0.98 9109 accuracy 0.98 18033
macro avg 0.98 0.98 0.98 18033
weighted avg 0.98 0.98 0.98 18033*
测试算法
SVM 和逻辑回归在准确性方面都表现出色。如果我们只根据这个参数来选择算法,那么支持向量机模型将是最佳选择。然而,需要考虑的另一个重要方面是:处理时间。SVM 模型在 70,000 行上训练需要一个小时,而逻辑回归只需要 3 分钟。当涉及到模型部署时,准确性之上的计算效率是基础。
在这个阶段,需要进行最后的检查。“测试包含一个肯定句,而“测试 _ 1”包含一个否定句。**
*##Testing Algorithm on single sentences
test = ['The book was really good, I could have not imagined a better ending']test_1 = ['The book was generally bad, the plot was boring and the characters were not original']test = count_vect.transform(test).toarray()
test_1 = count_vect.transform(test_1).toarray()#Printing prediction
print(LR.predict(test))
print(LR.predict(test_1))*
下面的输出显示,经过训练的分类器正确地预测了一篇书评是正面的还是负面的。
*Output:['Positive']
['Negative']*
结论
本文展示了模型选择和超参数调整如何显著提高精度,并提供了其他方面的全貌。我们从 6 个模型开始,4 个被过滤掉了,剩下的两个有很好表现的模型中,只有一个有可能进入生产。多亏了来自亚马逊 Kindle 商店的非常结构化的数据,除了单独确定情绪之外,还会有如此多的未来项目。从人工智能开始,它可以在一本书出版前对其进行评论和评级,或者在书籍被添加到商店后立即了解哪些书会做得很好。尤其是机器学习,可能性是无穷无尽的。
最后一点,如果您喜欢该内容,请考虑添加一个关注,以便在新文章发布时得到通知。如果你对这篇文章有什么要考虑的,写在评论里吧!我很想读读它们:)谢谢你的阅读!
PS:如果你喜欢我写的东西,如果你能通过 这个链接 订阅一个中等会员,那对我来说就是全世界。这是一种间接的支持我的方式,你会得到媒体文章提供的惊人价值!
参考
[1]麦考利,J. (2018)。亚马逊评论数据。检索于 2022 年 7 月 31 日,来自 Ucsd.edu 网站:http://jmcauley.ucsd.edu/data/amazon/
[2]阿佩尔,o .,奇克拉纳,f .,卡特,j .,&藤田,H. (2016 年 5 月 19 日)。句子级情感分析问题的混合方法。检索于 2022 年 7 月 30 日,来自 ResearchGate 网站:https://www . ResearchGate . net/publication/303402645 _ A _ Hybrid _ Approach _ to _ the _ opinion _ Analysis _ Problem _ at _ the _ Sentence _ Level
[3] Raschka,s .,& Mirjalili,V. (2014 年)。朴素贝叶斯和文本分类 I 导论和理论。从 https://arxiv.org/pdf/1410.5329.pdf取回**
[4]杰哈,a .,戴夫,m .,,马丹,S. (2019)。使用不同数据挖掘分类技术的二分类器和多分类器的比较。 SSRN 电子杂志。https://doi.org/10.2139/ssrn.3464211
[5]何,r .,&麦考利,j .(未注明)。沉浮:用一类协同过滤对流行趋势的视觉演变建模。https://doi.org/10.1145/2872427.2883037**
正确选择模型:嵌套交叉验证的温和介绍
实用机器学习
了解如何使用这一重要的机器学习技术

由 Unsplash 上的 Edge2Edge 媒体拍摄
本文将回顾机器学习中最重要的技术之一:嵌套交叉验证。当部署一个模型时,我们从几个选择(例如,随机森林与支持向量机)中选择具有超参数的最佳组合(例如,具有 50 棵树的随机森林与具有 5 棵树的随机森林)的最佳选择。但是,我们还需要估计泛化误差(模型在生产中的表现)。嵌套交叉验证允许我们找到最佳模型并正确估计其泛化误差。
在文章的最后,我们提供了一个用 Ploomber 开发的示例项目,您可以重用您的数据,立即开始运行嵌套的交叉验证!
为什么我们需要交叉验证?
让我们想象一下,我们正在进行一个机器学习项目,我们已经完成了清理数据和获得一些训练集的繁重工作,并且我们训练了它。我们准备好部署它了吗?
不完全是。在部署之前,我们想知道该模型在生产中的表现如何。为此,我们可以计算一个评估指标,比如说准确性。
我们使用哪些数据来计算指标?一种方法是为我们的训练集中的所有观察值生成预测,并计算评估度量:

图片作者。
这是它在代码中的样子:
控制台输出:(1/1):
Accuracy: 0.97
这种方法在方法上是不正确的:我们不应该用相同的数据进行训练和评估,因为我们的模型已经看到了训练示例。因此,它可以简单地记住标签,并具有完美的准确性。
我们将数据分为两部分:模拟生产条件的训练和测试(根据看不见的数据进行预测)。用第一部分拟合我们的模型,用第二部分评估。

图片作者。
下面是如何用代码实现的:
控制台输出:(1/1):
Accuracy: 0.91
我们可以看到这次的估计值更低了(0.91 vs . 0.97);较高的估计是对我们的模型将如何表现的过于乐观的估计。
因此,这种基本的训练/测试方法比第一种方法更好。尽管如此,它还有一个问题:对测试集的选择是明智的。
我们可以幸运地选择一个简单的测试集导致一个过于乐观的泛化错误(或者不幸地有一个过于悲观的估计)。因此,为了覆盖我们的地面,我们可以这样做:重复相同的过程多次,平均结果;这个过程被称为交叉验证:

图片作者。
总之:交叉验证为我们提供了一种可靠的方法来评估我们的模型在生产中的性能。
让我们来实现它:
控制台输出:(1/1):
Cross-validated accuracy: (0.98 + 0.92 + 0.96) / 3 = 0.95
所以现在我们得到了一个结果(0.95),介于第一种(方法错误)方法(0.97)和第二种训练/测试方法(0.91)之间。
型号选择
我们有一种通过交叉验证来估计泛化误差的方法,但是我们还没有完成。有很多模型可用:随机森林、XGBoost、支持向量机、逻辑回归等。因为我们不知道哪一种效果最好,所以我们尝试了几种。
所以假设我们尝试随机森林和支持向量机。我们已经有了随机森林的结果,现在让我们在支持向量机上运行交叉验证。
控制台输出:(1/1):
Cross-validated accuracy: (0.96 + 0.98 + 0.94) / 3 = 0.96
让我们总结一下结果:

图片作者。
根据结果,我们应该使用支持向量机。
然而,我们并没有改变模型的参数(例如,一个拥有大量树木的随机森林)。那么,有没有可能我们做出了一个次优的决定?
让我们尝试几个实验,这一次,用几个超参数训练两个模型(使用我们的交叉验证程序):
控制台输出:(1/1):
SVC(kernel='linear', random_state=0):
Cross-validated accuracy: (1.00 + 1.00 + 0.98) / 3 = 0.99SVC(kernel='poly', random_state=0):
Cross-validated accuracy: (0.98 + 0.94 + 0.98) / 3 = 0.97 RandomForestClassifier(n_estimators=2, random_state=0):
Cross-validated accuracy: (0.98 + 0.92 + 0.96) / 3 = 0.95RandomForestClassifier(n_estimators=5, random_state=0):
Cross-validated accuracy: (0.98 + 0.94 + 0.94) / 3 = 0.95
基于这些结果,我们得到了一个更好的模型!0.99 精度的线性核的支持向量机!
不完全是。这里有一个方法上的错误。
我们强行找到了最好的模型。本文证明了当我们使用(普通的)交叉验证方法优化超参数和模型选择时,可能会出现过于乐观的泛化误差(由于过度拟合)。
输入嵌套交叉验证
我们使用嵌套交叉验证来修复上述错误,这提供了一种更准确的方法来估计泛化错误,同时还优化了超参数。
它之所以得名,是因为我们有效地嵌套了两个交叉验证过程。让我们看看它在实践中是怎样的:

图片作者。
我们嵌套了两个交叉验证循环。看第三个作为参考。在进行初始剥离后,我们再次剥离(2/3 的数据)。内部循环优化超参数:我们为每个超参数配置(在这种情况下,n_estimators = 2和n_estimators = 5)进行训练,计算交叉验证的度量(平均值)并挑选最佳超参数配置(在这种情况下,n_estimators = 5),然后,我们进入外部循环,使用最佳配置(n_estimators = 5)重新训练并报告准确度。我们重复同样的过程,直到完成。
在这个过程结束时,我们将有一个表格,报告每个模型的估计泛化误差;让我们运行它:
控制台输出:(1/1):
GridSearchCV(estimator=RandomForestClassifier(random_state=0), param_grid={'n_estimators': [2, 5]}):
Cross-validated accuracy: (0.98 + 0.92 + 0.96) / 3 = 0.95 GridSearchCV(estimator=SVC(random_state=0), param_grid={'kernel': ['linear', 'poly']}):
Cross-validated accuracy: (1.00 + 0.94 + 0.98) / 3 = 0.97

模型性能总结。图片作者。
有了这些信息,我们可以自信地选择支持向量机作为我们的赢家模型。我们还可以说,它的估计泛化误差为 0.97(看,这低于我们之前没有使用嵌套交叉验证时的估计值 0.99)。
作为最后一步,我们运行最后的交叉验证程序,以找到最佳参数。注意这是一个普通的交叉验证过程:

图片作者。
代码如下:
控制台输出:(1/1):
SVC(kernel='linear', random_state=0):
Cross-validated accuracy: (1.00 + 1.00 + 0.98) / 3 = 0.99SVC(kernel='poly', random_state=0):
Cross-validated accuracy: (0.98 + 0.94 + 0.98) / 3 = 0.97
所以现在我们已经准备好部署:我们将使用kernel = 'linear'部署支持向量机!
但是考虑一个关键点:我们将报告 0.97(通过嵌套的交叉验证过程)作为我们的泛化估计,而不是这里得到的 0.99。
就是这样!现在,您已经具备了嵌套交叉验证技术,可以选择最佳模型并优化超参数!
示例代码

图片作者。
为了您的方便,我们为您准备了一个示例 Ploomber 管道,让您快速上手;以下是获得它的方法:
一旦执行了管道,检查一下output/report.html文件,它将包含嵌套交叉验证过程的结果。编辑tasks/load.py来加载你的数据集,再次运行ploomber build,你就可以开始了!您可以编辑pipeline.yaml来添加更多模型并并行训练它们。
警告
在我们的示例中,我们只评估了两种配置的两个模型。然而,由于我们嵌套了两个交叉验证过程,培训程序的数量像雪球一样越滚越大。如果我们添加更多的模型和超参数,我们很快就会被要求运行数百个训练程序来找到最佳模型。所以,为了加快结果,你可能想要并行运行所有这些实验;用 Ploomber 在一台机器上就可以轻松做到。不过,如果你想在多台机器上分配工作,你必须建立一些云基础设施。或者,您可以尝试 Ploomber Cloud ,它将快速启动必要的资源,在云中执行您的代码(并将作业并行化),获得结果,并关闭基础架构,这样您就不会让那些昂贵的服务器继续运行。不用离开你的笔记本。
最后的想法
在我作为数据科学家的日子里,我花了一些时间来完全掌握嵌套交叉验证的概念。所以希望这篇帖子对你有帮助。你有什么问题吗?请联系我们的社区,我们很乐意为您提供帮助。
最初发表于 ploomber.io
模型测试对于构建领域知识至关重要
原文:https://towardsdatascience.com/model-tests-are-critical-for-building-domain-knowledge-8d5fde2660d8
测试可以防止退化。但是测试最重要的产品是领域知识和强大的能力。

嵌入空间中两个分布之间边界的选择。图片作者。
在机器学习中,测试是一项关键但经常被忽视的实践。在没有测试的情况下建立一个机器学习系统很可能在最糟糕的时候产生糟糕的结果——当所有的盒子都被检查时,当模型被投入生产时,当真实的人受到影响时。
这是一个典型的(真实的)机器学习测试故事。但是这个故事只有大棒没有胡萝卜。它完全忽略了测试数据团队取得进步的最重要的产品:领域知识。
随着机器学习项目从开始阶段走向成熟,它们的复杂性也在增加。开发过程中的每一次迭代都会产生新的见解,触发诸如数据清理、模型调整或更多标记之类的活动。这些行为可能是净增值,但系统中的小口袋会出现倒退,有时是看不见的。测试可以防止这些退化。
测试经常给人一种乏味的感觉,这是很好的实践,但并不总是必要的。围绕测试的经典问题出现了,比如测试什么是必要的,这些测试应该有多复杂。但是将测试视为领域知识的构建和系统化,显示了测试超越棘轮以防止倒退的力量。
将测试与其他知识构建活动(如错误分析和数据注释)结合起来,可以鼓励科学进步和良好的工程设计。注释、错误分析和测试可以被交叉利用,从而产生复合收益以获得更好的系统性能和对建模内容的更深入的理解。自动驾驶汽车为理解这一点提供了一个有趣的视角,特斯拉自动驾驶部门的方法论展示了这一视角的力量。
通过测试积累知识需要平台支持。平台团队必须在测试和其他活动中实现无缝体验。MLOps 领域的工具构建者应该在这方面支持他们,将集成工作放在第 n 个特性之上。如果我们关心平台用户的长期价值,仅仅解决工作流的一小部分是不够的。
测试是知识的积累
模型测试是一种断言,即模型应该在某些场景中以某种方式运行。从技术上讲,场景是由模型输入指定的,可以是一个样本,也可以是多个样本。
在你进行测试之前,你可能甚至不知道一个场景的存在。以一个自动驾驶汽车模型为例,它可以检测视野中的行人。你可能不会想到行人会有翅膀、不正常的长腿或闪闪发光的衣服,直到在有许多盛装行人的游行中进行路试。

一个非常令人困惑的行人的例子。来源:维基共享资源。执照: CC BY-SA 4.0
(自动驾驶汽车是测试对人工智能安全有多重要的一个很好的例子。)
了解这些奇怪的案例是通过经验,或隐性知识。为了利用隐性知识,需要将其编码到系统中。
知识的隐式编码是最普遍的,因为它很方便。方程中一个奇怪的无法解释的项,或者一个碰巧稳定模型训练的“神奇”配置,都是隐性知识的例子。他们的作者可能知道他们在做什么,但没有人知道。这就产生了所谓的部落知识。
隐式编码不仅对协作有害,而且还阻碍了技术固有的累积杠杆作用。显式编码使其他人能够学习和建立该知识。在神奇的训练配置示例中,我们可以将训练配置存储在一个数据库中,其中包含链接到训练结果的作者注释。队友们以后可以在许多实验中查询配置,以找到那些效果最好的。通过组织和链接元数据,学习和获得新的见解成为可能。
在一些 python 文件中编写test_for_winged_pedestrian()测试函数错过了一个复合知识收获的重要机会。有翅膀的步行者,如果在步行者之下的概念分类中被适当地组织,产生跨越测试之外的多个工作流程步骤的杠杆作用。这种利用通过构建更高级别的数据查询来体现。盛装行人是一个首先必须与其他概念联系在一起的概念:
Road obstruction
├── Stationary
│ ├── Traffic cone
│ └── ...
└── Non-stationary
├── Pedestrian
│ ├── Average pedestrian
│ ├── Pedestrian with walking aid
│ └── Costumed pedestrians
├── Bicyclist
└── ...
现在想象一种情况,其中“行人”作为模型的目标标签可能在性能上遭受损失。穿着服装的行人以及手持拐杖或步行者、骑摩托车者和其他移动障碍物的行人可能是在查询中使用的有价值的概念,也许是为了理解行人检测模型可能失败的原因,或者是为了进一步标注而显示未标注的示例。实现这一点的第一步是认识到测试用例、标记和错误分析是同一件事情的不同方面:构建领域知识。
工作流程中的测试
在我们的例子中,一名技术人员在进行道路测试时,可能会注意到笔记本电脑屏幕上的错误。或者科学家在模型上做错误分析时可能会注意到。标注者可能会在标记来自路测的新数据时发现它。它甚至可能在数据准备管道中的不同测试失败时出现。

机器学习开发工作流程。图片作者。
有许多工具可以处理每个工作流程步骤,但是这些工具通常不能很好地集成。预期体验没有考虑之前和之后的步骤,这会导致摩擦并抑制整体效率。一个好的平台将融合不同步骤之间的接缝,在开发人员工作时为他们提供统一的体验。
如果在注释中发现一个数据点的问题,想象一下直接在注释工具中放置一个“添加到测试套件”的按钮的能力。

一个分类标签界面的简单模型。“添加到测试套件”按钮使得向测试套件添加例子变得容易。图片作者。
如果问题出现在数据收集或数据准备管道中,开发人员应该准备好任何东西,从 pytest 到像Great expects这样的成熟工具,以便快速添加测试用例。

测试函数示例。像这样的代码将与管道代码一起存储在 git repo 中。注意将测试与概念联系起来的装饰器。图片作者。
这些接口使得开发人员可以很容易地添加测试,降低了模型回归的可能性,并为知识共享奠定了基础。
案例研究:特斯拉自动驾驶仪
Andrej Karpathy 描述了内部平台上的工作流和功能,实现了自动驾驶汽车的开发,这在很大程度上依赖于在同一视图中查看标签、测试和错误分析。

“Operation Vacation”的描述,特斯拉自动驾驶团队通过标记和测试来收集和改进他们的数据的过程。摘自 Andrej Karpathy 的 2020 年演讲,“特斯拉全自动驾驶的人工智能”。图片作者。
对于一个新的对象检测任务,他们建立了一个图像种子集,并选择一些作为单元测试。随着产品和标签团队的迭代,他们改进他们的测试并创建新的测试。该过程的其余部分循环进行,直到测试通过。
正如 Karpathy 提到的,他的团队的北极星目标是“度假行动”——即使整个计算机视觉团队都去度假,自动驾驶任务也应该得到改善。实现该过程意味着该平台由数据模型支持,该数据模型将围绕测试、标记和模型预测的元数据联系在一起。
知识积累的复合收益
当测试用例被公式化时,它们也被标记。如果测试场景显著影响性能,可以挖掘类似的样本进行额外的标记。如果标记工具使得添加新的测试用例变得简单,那么我们就有了一个有效的扩展测试场景知识的过程。
当在大量测试场景中考虑这个过程时,这些收益是复合的。如果我们的模型曾经对光照条件敏感,我们可能会有“日光”或“一天中的时间”的概念。基于直觉,我们也可以想象,检测骑自行车的人是检测穿着特殊服装的行人的一个有用的步骤。然后,我们可以为有用的行人检测样本构建查询:

使用 Python 中的高级概念的示例查询。图片作者。
或者想象一个可视化查询组件,使上述查询的一部分更容易构建:

组合查询界面的可视点选择和代码的并排视图。图片作者。
两件事带来了复合收益:消除可用性的缝隙,花时间明确地组织知识。特斯拉自动驾驶团队通过“触发器”的概念来查询客户数据以进行标记,从而展示了这些收益。

特斯拉自动驾驶团队用来获取标签数据的触发器示例。摘自安德烈·卡帕西在 2021 年 CVPR 奥运会上的演讲。图片作者。
开发这些查询需要明确编码的领域知识。一些触发因素需要理解自动驾驶汽车开发中的概念,如雷达和视觉之间的差异或边界框的定义。其他触发器利用现实世界的概念,如刹车灯、道路和构成车顶的东西。许多人同时依赖这两种类型的概念来实现触发器。如果没有这些低级概念的定义,为这些高级概念(触发器)构建查询将是相当具有挑战性的。
知识发展平台
跨不同开发活动构建知识需要一个具有统一数据模型的平台。统一是实现领域知识复合收益的关键。
但是由于更基本的原因,建立一个平台是具有挑战性的。所需功能的广度是巨大的,并且不同用例、团队和公司的特性优先级是不同的。MLOps 的开源工具和供应商在一定程度上有所帮助,但最近的情绪指向了一种分裂的感觉。平台团队需要争论大量的工具,管理多个供应商,并长时间(有时几年)进行构建,直到他们的努力完全实现。
平台团队有时会在生产培训和部署中优先考虑大型基础设施项目,以尽量缩短上市时间。虽然这可能是一个合理的短期策略,但它有可能朝着次优的方向发展。
平台团队也可以从思考知识构建中受益。机器学习系统的最大收获往往可以追溯到对问题的更好理解。新知识是通过迭代发现的,所以一个平台的中心目标应该是端到端的效率。只有当开发引擎运行时,工作流中容易摘到的果实才是可见的。平台团队应该通过任何必要的手段来启动这个引擎,以免陷入过早优化的陷阱。这可能会排除强大的基础设施。
首先考虑框架和数据模型意味着可以适当地添加基础设施以获得最大的利用。如果性能问题的根源在于数据或模型误解,那么拥有可扩展的 GPU 计算或亚毫秒级服务延迟是行不通的。
MLOps 工具也可以发挥作用。MLOps 行业的工具构建者倾向于关注开发或部署用例的垂直部分。虽然这降低了产品起步的风险,但长期来看,用户会因为给平台团队施加集成压力而遭受损失。开销是巨大的——每个工具都有自己的语言,通过 UI、API 和数据格式来表达。
从长远来看,易于互操作并努力获得一致体验的工具为他们的客户(平台团队和他们所服务的开发人员)提供了更多的价值。整合正是平台团队必须反复做的工作,排除了有助于公司竞争优势的定制功能。如果工具构建者关心向他们的客户交付长期价值,他们必须优先考虑集成工作,而不是增量特性开发。
在可预见的未来,平台仍将难以构建。但像特斯拉这样的公司建立了世界级的人工智能,因为他们的平台使人工智能开发者能够了解他们的领域,理解他们的模型,并将他们新发现的知识编码到他们的系统中。不管我们在构建人工智能系统中的角色如何,有一点是明确的:知识构建是人工智能发展的核心。乍看起来,测试似乎不重要,但将它理解为构建知识的一流使能器表明,它对人工智能的成功开发至关重要。
觉得这些想法有趣吗?来和我们在 Twitter 上聊天吧!@ hoddieot@ skylar _ b _ Payne,或者订阅* 数据鸿沟 。*
消费者决策建模:联合分析
原文:https://towardsdatascience.com/modeling-consumer-decisions-conjoint-analysis-f4eda531ecf6
产品开发的基于选择的联合分析指南

人们花费大量时间对他们购买的一些产品和服务进行决策。事实上,最近的一项研究表明,平均每个人每年要花大约 130 个小时来决定去哪里吃饭。一般来说,消费者通过基于产品提供给他们的效用在产品的各种属性之间做出权衡来做出购买决定。作为营销人员或产品经理,了解消费者如何做出这些权衡以及每个属性提供的效用至关重要。
为了理解属性和效用的概念,让我们考虑购买新智能手机的例子。人们在决定购买时可能会考虑的因素有 RAM、存储容量、相机规格、屏幕尺寸和分辨率、品牌、价格等。这些被考虑的因素被称为属性,消费者从这些属性中的每一个得到一些效用。从每个属性中获得的效用也称为部分价值。每个消费者都是不同的,他们可以从产品的属性中获得不同的效用。例如,摄影爱好者可能会从相机规格属性中获得比 RAM 和存储容量等其他属性更多的效用。相比之下,游戏发烧友将从 RAM、存储容量和屏幕尺寸/分辨率中获得更多的效用。为了开发成功的产品,营销人员/产品经理必须了解其客户群的属性偏好,并量化客户从属性中获得的效用。联合分析是一种统计方法,用于了解属性的相对重要性/偏好,并量化消费者从产品的每个属性中获得的效用。因此,它可以用来模拟消费者在做出购买决定时可能做出的权衡。
在进行联合分析时,我们有两个基本假设:
- 消费者购买给他们最高总效用(单个属性效用的总和)的产品
- 消费者遵循补偿性决策过程。简单来说,这意味着产品的一个正面属性可以弥补一个负面属性,即客户愿意做出取舍。
市场研究设计
联合分析的第一步是设计一个市场调查研究。本研究的参与者通过分层随机抽样选出,代表产品的人口或目标受众。
让我们再次考虑购买智能手机的例子。(产品团队花费大量时间集思广益产品的属性,并经常组织焦点小组从消费者那里获得更多见解)为了简单起见,让我们假设仅有的属性是 Ram、存储、摄像头、屏幕、品牌和价格。
本研究的问卷设计如下:

问卷样本(图片由作者提供)
研究的参与者被给予多个选择集,并被提示从每个选择集中选择一个选项。(为了简单起见,我只提供了两个随机选择集。在实际的调查中,根据产品属性的数量,参与者会得到 10 到 20 个选择集。这些选择集的设计本身是一项复杂的任务,因此我在本文中不会深入探讨。这些问题以所示的方式设计,以模拟消费者将经历的实际决策过程。每个参与者对每个选项集的回答都被记录下来并进行建模处理。
统计模式
记录并处理每个参与者的响应。结果数据集的示例如下所示:

样本响应数据(图片由作者提供)
在创建模型之前,我们需要确保我们正确地编码了连续变量和分类变量。在本例中,我将把除“品牌”之外的所有属性都视为连续属性。然后我们运行一个逻辑回归,选择作为因变量,属性作为自变量。对于这个模型,将截距强制为 0 也是有用的,因为当所有的因变量都为 0 时,技术上产品的效用应该为 0。这可以在 R 中使用以下代码来完成:
model <- glm(Choice ~ 0 + Ram + Storage + Camera + Screen + Brand + Price, Data = Data, Family = Binomial)
模型结果和解释
在我们运行回归之后,我们获得每个属性的系数。这方面的一个例子如下所示:

样本系数表(图片由作者提供)
这些系数可以解释为常规逻辑回归。在这种情况下,我们使用逻辑回归建模的对数优势表示消费者从属性中获得的效用。因此,对于我们的客户来说,“RAM”每增加 1GB,平均会增加 2.1 个单位的效用。同样,“价格”每增加 1 美元,我们客户的平均效用就会减少 0.08 个单位。我们也明白,与品牌‘A’和‘B’相比,客户更看重品牌‘C’。(品牌“D”不包括在系数表中,因为它被作为系数为 0 的参考)
最后,我们可以根据产品的属性来计算产品的总效用和购买概率,如下所示:(这些结果和计算基于我创建的随机数据,而不是实际数据。这可能会使一些结果看起来不合逻辑)

具有给定属性的产品的示例计算(图片由作者提供)
有了模型的结果,我们可以测试产品属性的多个规格,并得出目标客户的总效用和购买概率。这在设计新产品投放市场时特别有用。
市场模拟
我们也可以使用这种方法来模拟市场,并估计新产品的市场份额。我们不是对市场调查研究的所有参与者的全部数据进行逻辑回归,而是对每个参与者的回答进行逻辑回归。这给出了产品的总效用和样本中每个参与者的购买概率。由于样本被选择来代表总体,因此样本的结果可以外推至整个总体,以得出估计的市场份额。更复杂的方法,如分层贝叶斯模型,也可以用来达到更有统计学意义的结果。
结论
联合分析是了解特定环境下消费者偏好的产品属性的有力方法。它可以用于设计各种产品甚至服务。从专业角度来说,我在一家快餐店用这种方法来了解顾客的食物偏好。它也常用于基于属性的定价。虽然营销决策是艺术和科学的结合,但这种方法是在设计产品或服务时消除主观性和个人偏见的有力工具。不用说,这是一个让每个营销分析师受益的方法。
用生命周期模拟客户生命周期价值
原文:https://towardsdatascience.com/modeling-customer-lifetime-value-with-lifetimes-71171a35f654
用几行代码评估客户的价值并制定积极的行动

来源: Unsplash
Tβ-几何负二项分布(BG-NBD)模型是一种有影响力的描述客户行为和预测客户终身价值的概率模型(CLV)。在本系列的上一篇文章中,我们探讨了这个模型的直觉、假设和数学推导。如果你没有看过,我建议你看一看!
正如你可能从那篇文章中推断的那样,手动编写所有这些 BG-NBD 方程并不容易。幸运的是,有一个名为 的 Python 库可以为我们完成所有繁重的工作。 寿命抽象出 BG-NBD 的内部复杂性,使我们能够专注于从模型中获得洞察力和商业价值。
这个令人敬畏的库是由 Cam-Davidson Pillon 创建的,他也是《概率编程&黑客的贝叶斯方法 》一书的作者,这是所有贝叶斯爱好者的必读之作。
这篇文章探究了寿命的来龙去脉。这是我们的议程:
- 首先,我们将看看在建模之前数据集需要的必要的表格式。
- 其次,我们将看到如何使用寿命来训练和评估 BG-NBD 模型。
- 第三,我们将理解寿命促成的各种见解和分析。
- 最后,我们将讨论几个由寿命带来的实际商业应用。
我们开始吧!
数据准备
数据转换
通常,企业在“交易”表中记录他们的交易。这样的表具有代表单个交易的行和代表交易的不同方面的列,例如涉及的客户、交易的时间戳及其值。
由生存期提供了一个示例事务表。这张表记录了 CDNow 的交易,这是一家网络时代的公司,销售 CD 和音乐产品。它包含从 1997 年 1 月 1 日至 1998 年 6 月 30 日(共 78 周)期间 2,357 名客户的 6,919 笔交易。我们将在整篇文章中使用它。下面是如何加载它:

交易
在拟合 BG-NBD 模型之前,我们需要首先将该表重组为规范的“RFM”格式。RFM 格式的表包含代表各个客户的行和以下列:
- 频率 ,表示客户重复购买的次数。
- 最近度,代表客户最近一次购物的“年龄”。
- T ,代表观察期结束时客户的“年龄”。
- monetary_value,可选列,其中表示给定客户购买的平均值,不包括第一次购买。

作者图片
寿命提供了一个实用函数,方便了从“交易”格式到 RFM 格式的转换。

‘RFM’
我们解释 RFM 数据帧第一行的方式如下:
- 在观察期内,顾客 4 总共购买了 4 次(因此重复购买了 3 次)。
- 他第一次和最后一次购买的时间跨度是 49 周。
- 他第一次购买和观察期结束之间的时间跨度是 78 周。
- 除了第一笔交易,他的平均交易金额是 23.73 美元。
请注意,当频率、新近度和货币值列的值为零时(例如对于客户 18 ),表示客户在观察期内仅购买了一次。
校准-维持分离
将表格重新格式化为 RFM 格式时,我们还可以选择执行校准-保持分离,这与我们熟悉的训练-测试分离程序在精神上是相似的。校准-保持分离将我们的事务分成两部分,具体取决于它们是属于校准期还是观察期。校准期间发生的交易用于训练模型,而观察期发生的交易(交易)用于验证模型。

作者图片
在本例中,我们将校准和观察期分别设置为 52 周和 26 周。下面是我们如何使用生存期进行分割:

'rfm_cal_holdout'
我们看到,现在每个客户都与用于模型训练的校准功能和用于模型验证的维持(观察期)功能相关联。
训练、预测和评估
适合的
在生存期中,模型拟合遵循熟悉的 scikit-learn 步骤,即实例化模型对象(可选地包括超参数设置)并将其拟合到校准(训练)数据:
就这样——两条线,我们就有了一个可用的 BG-NBD 模型!
你可能还记得从 第一篇 开始,一个 BG-NBD 模型是由一个伽玛和一个贝塔分布组成的。γ分布的参数 r 和 α 以及β分布的参数 a 和 b 可通过访问。拟合模型对象的概要属性:

拟合模型的参数
在下一节中,我们将从这些参数中获得一些业务见解。
通过比较模型生成的人工数据和真实数据来评估模型拟合度
“垃圾进,垃圾出”的格言适用于 BG-NBD 模型:依赖一个不合适的模型会导致灾难性的商业决策。因此,在使用我们的模型进行预测和解释之前,我们应该首先评估模型的性能。
寿命提供了几种评估我们模型的方法。第一个是比较我们的真实校准数据和从 BG-NBD 模型生成的分布中采样的人工数据之间的频率。函数plot _ period _ transactions在一行中完成:

该图显示,训练数据中的频率分布与我们拟合的模型人工生成的频率分布基本一致。这表明我们的模型很好地捕捉到了我们假设生成数据的物理过程。
做预测
拟合 BG-NBD 模型的主要目的之一是进行预测。在本节中,我们将了解如何生成预测,并随后用于进一步评估模型的性能。
当给定具有特定频率、新近度和、的客户时,拟合的模型对象可以生成两种类型的预测:
- 他将在未来几个时期购买的数量
- 他在观察期结束时活跃的概率。
让我们来看一个如何做到这一点的例子。首先,我们将选择一个样本客户,检查他在校准和观察期的频率、新近度和 T :

我们看到,在观察期内,客户进行了 2 + 1 = 3 笔交易(“ + 1”来自于将不包括第一笔交易的重复交易的数量转换为总交易)。
现在,让我们将这个“真实”数字与英国地质调查局-NBD 所做的预测进行比较。以下代码得出了客户在未来 26 周(观察期的长度)内将进行的交易的预期数量:
我们看到预测的事务数(0.76 个事务)低于实际的事务数(3 个事务)。
我们可以类似地预测某个客户在校准期结束/观察期开始时仍然活跃/活着的概率)。
由于顾客在观察期内确实进行了一些购买,我们完全确定他在校准期结束时是活跃的。因此,0.57 的预测概率是一个低估。
实际交易数和预测交易数的比较
在了解了如何预测一个人的交易数量后,我们可以将这个过程扩展到我们的整个客户群。然后,可以将得到的预测与真实的交易数据进行比较,以评估我们模型的准确性。

如果需要更严格的评估,这两列可以采用典型的回归指标,如 RMSE:
见解和解释
可视化最大似然伽玛和贝塔分布
一旦我们对模型的性能感到满意,我们就可以继续从中获得洞见。
一个好的起点是提取和可视化估计的伽马和贝塔参数。如前一篇文章所述,Gamma 和 Beta 分布具有重要的商业意义。伽玛分布告诉我们客户群交易率的分布,而贝塔分布反映了停用概率的分布。
以下代码显示了如何从模型中提取 Gamma 和 Beta 参数,并绘制分布图:

从该图中,我们可以看到伽马分布相对健康,大多数𝜆位于 2 附近。这意味着我们的顾客预计每周购物 2 次。

贝塔分布看起来也很健康,大部分的 p 接近 0。这意味着我们的客户不太可能很快停用。
频率/最近/未来购买矩阵
除了所描述的模型拟合评估之外,拟合的模型对象也可以用于一些解释性分析。
例如,我们可以查看它的频率/最近/未来购买矩阵。该矩阵显示了不同的频率-新近组合如何产生不同的未来购买预期数量。

频率/最近/未来购买矩阵
正如我们所看到的,我们最好的客户在右下角——这些人最近经常购物,所以我们对他们的回访抱有很高的期望。
相反,我们最不看好的客户在右上角——他们经常购买,然后就不再来了,我们已经几个月没见到他们了。他们很可能找到了另一家商店(即他们已经停用)。
频率/最近/概率存活矩阵
类似地,我们也可以产生频率/最近/概率活矩阵。这个矩阵与前一个矩阵共享相同的轴,但是现在每个单元的阴影告诉我们具有各种频率-新近组合的客户的存活概率。

频率/最近/概率存活矩阵
毫不奇怪,我们可以看到类似的模式,其中最好和最差的客户分别位于矩阵的右上角和右下角。
商业应用
使用 BG-NBD 的 CLV 估计
我们已经看到了如何使用 BG-NBD 模型来预测生存概率 p 以及下一个 k 周期的购买数量。这两个预测可以反过来用于计算下一个 k 周期的客户价值的粗略估计。该估计值可以计算如下:

这一估计依赖于以下两个简单的假设:
- 存活概率 p 在接下来的 k 周期中保持不变
- 下一个 k 周期的平均采购价值等于观察期的平均采购价值。
请注意,这些天真的假设理论基础薄弱——在现实中, p 通常会在每次购买后发生变化,未来的购买价值可能会与过去的价值相差很大。因此,得出的估计是粗略的。如果您感兴趣,还有更复杂的概率模型,如 Gamma-Gamma 模型,可以在更严格的 manner⁴中计算未来的购买价值。
这是我们如何在熊猫身上进行上述计算的:

得到的 value_10_pred 列概括了我们的客户在未来 10 周的估计价值。然后,我们可以使用这一估计来衡量我们业务的健康状况。例如,我们可以获得 value_10_pred 的汇总统计数据:

我们看到,平均而言,我们预计客户在未来 10 周内的支出约为 5.18 美元。
我们还可以绘制一个直方图:

我们看到我们的大部分客户的 value_10_pred 接近于 0。最终的平均值 5.18 美元是由一些异常值(少数具有非常高的价值 _10_pred 的客户)驱动的。在下一节中,我们将看到如何进一步利用这个结果。
主动干预
这三个新的栏目, alive_prob 、 n_transactions_10_pred 和 value_10_pred ,将我们客户以前看不到的方面具体化和量化。除了提供信息之外,这些新特性还可以用来推动具体的行动。例如,我们可以利用这些新功能有选择地主动接触不同的客户群体,以提高他们的预期 CLV 。
我们之前已经得出结论,我们的总体客户价值很大程度上归因于少数高价值客户。由于这些客户是我们的收入来源,我们希望特别关注他们。一个想法是偶尔给他们发贺电/代金券,鼓励他们继续保持忠诚。

另一个想法是联系高翻盘概率的客户(即存活概率低的客户),劝阻他们放弃我们的业务。
需求预测
正如我们之前所看到的, value_10_pred 估算了我们的客户在接下来的十个时间段内的购买量。利用这一点的另一种方法是将该值视为客户的预测需求。这种预测可以反过来支持我们的供应链决策(如补货或生产计划)。
警告:BG-NBD 并不是作为一个时间序列模型来设想的;它不具备时间序列功能,如季节性和趋势的会计。因此,当谈到预测时,明智的做法是不要仅仅依赖 BG-NBD 公司的预测,而是将它与 ARIMA 等时间序列模型结合起来。
结论
我们已经看到了生命周期如何让我们方便地了解我们客户群的购买行为,并从中获得强大而可行的见解。
虽然这很棒,但请记住,对寿命的估计是最大似然估计,完全是根据我们提供的数据推断出来的。
在本系列的第三篇文章中,我将向您展示实现 BG-NBD 的另一种方法,这次使用由 PyMC3 支持的贝叶斯分层建模。我们将看到这个实现提供了更多的灵活性,因为它允许我们将领域知识引入到建模步骤中。
我希望在那里见到你!
参考
[1]“计算你的顾客”的简单方法:帕累托/NBD 模型的替代方案(布鲁斯·哈迪 et。阿尔,2005 年)
【2】这个函数来自 BG-NBD 论文(资料来源【1】)的方程(10)
【3】使用 BG/NBD 模型计算 P(alive )( Bruce Hardieet。阿尔,2008)
[4]货币价值的伽玛-伽玛模型(Bruce Hardie et。阿尔,2013)
注 :所有图像、图表、表格、方程式均由本人创作,除非另有说明。
如果你对这篇文章有任何意见或者想联系我,请随时通过 LinkedIn 给我发一个联系方式。此外,如果你能通过我的推荐链接支持我成为一名中级会员,我将非常感激。作为一名会员,你可以阅读我所有关于数据科学和个人发展的文章,并可以完全访问所有媒体上的故事。
用 PyTorch 建模 DNA 序列
原文:https://towardsdatascience.com/modeling-dna-sequences-with-pytorch-de28b0a05036
适合初学者的教程

作者图片
DNA 是一个复杂的数据流。虽然它可以用一串 ACTGs 来表示,但它充满了复杂的模式和结构上的细微差别,人类很难通过查看原始的核苷酸序列来理解。近年来,利用深度学习对 DNA 数据建模取得了很大进展。
研究人员应用了卷积神经网络(CNN)、长短期记忆网络(LSTMs)、甚至变压器等方法,直接从 DNA 序列预测各种基因组测量值。这些模型特别有用,因为有了足够多的高质量训练数据,它们可以自动拾取与预测任务相关的序列模式(或基序),而不需要专家事先指定要寻找哪些模式。总的来说,人们越来越热衷于在基因组学中使用深度学习来帮助将 DNA 序列映射到它们的生物功能!
作为一名对使用计算方法解决可持续性和合成生物学挑战感兴趣的研究生,我一直在学习如何使用 PyTorch 研究 DNA 序列模式。不缺少关于如何开始使用 PyTorch 的教程,但是许多教程倾向于关注图像或语言输入数据。对于使用 DNA 作为输入,有许多伟大的项目已经开发了 PyTorch 框架来模拟各种生物现象[ 1 、 2 、 3 ],但是它们可能非常复杂,对于初学者来说很难钻研。
我在为 PyTorch 新手寻找那些也关注 DNA 数据的初学者例子时遇到了一些困难,所以我编写了一个快速教程,以防将来的 DNA 建模者发现它对入门有帮助!
教程本身可以作为一个 Jupyter 笔记本 交互式运行,或者您可以跟随本文剩余部分中的关键概念和 Github 要点摘要。
建立 PyTorch 模型来预测 DNA 序列的得分
本教程展示了 PyTorch 框架的一个示例,它可以使用原始 DNA 序列作为输入,将这些输入到神经网络模型中,并直接从序列中预测定量标记。
教程概述:
- 生成合成 DNA 数据
- 为 PyTorch 培训准备数据
- 定义 PyTorch 模型
- 定义训练循环函数
- 运行模型
- 在测试集上检查模型预测
- 可视化卷积滤波器
- 结论
它假设读者已经熟悉 ML 概念,如:
- 什么是神经网络,包括卷积神经网络(CNN)的基础知识
- 跨时代的模型训练
- 将数据分成训练/值/测试集
- 损失函数和比较列车与 val 损失曲线
它还假设对生物学概念有所了解,如:
- DNA 核苷酸
- 什么是调控基序?
- 可视化 DNA 基序
注意: 下面的方法不一定是 最优 的方法!我相信还有更好的解决方法,这只是我在学习中的尝试。但是,如果您刚刚开始使用 PyTorch,并且也在使用 DNA 序列作为输入,那么本教程可能是一个有用的例子,说明如何在 DNA 序列分析的背景下“将一些 PyTorch 管连接在一起”。
1.生成合成 DNA 数据
通常,科学家可能对预测结合分数、表达强度或转录因子结合事件的分类感兴趣。但在这里,我们将保持简单:本教程的目标是观察深度学习模型是否可以学习检测 DNA 序列中非常小、简单的模式,并对其进行适当的评分(同样,这只是一个练习任务,以说服我们自己我们实际上已经正确设置了 PyTorch 片段,以便它可以从看起来像 DNA 序列的输入中学习)。
因此,假设给定一个八聚体 DNA 序列,给它每个字母的分数如下:
- A = +20 分
- C = +17 点
- G = +14 点
- T = +11 点
对于每一个 8-mer,合计其总点数,然后取平均值。举个例子,
AAAAAAAA会得分20.0
mean(20 + 20 + 20 + 20 + 20 + 20 + 20 + 20) = 20.0
ACAAAAAA会得分19.625
mean(20 + 17 + 20 + 20 + 20 + 20 + 20 + 20) = 19.625
这些核苷酸的值是任意的——这里没有真正的生物学!这只是为了我们 PyTorch 练习的目的而给序列分配分数的一种方式。
然而,由于最近的许多论文使用类似 CNN 的方法来自动检测“基序”,或 DNA 中可以激活或抑制生物反应的短模式,让我们在我们的评分系统中再增加一项。为了模拟诸如影响基因表达的基序之类的东西,假设一个给定的序列,如果TAT出现在八聚体中的任何地方,就会得到一个+10凸起,如果其中有一个GCG,就会得到一个-10凸起。同样,这些图案在现实生活中没有任何意义,它们只是一种模拟简单激活或抑制效果的机制。

一个简单的八聚体 DNA 序列评分系统。图片作者。
下面是这个简单评分系统的实现:
绘制 8 聚体序列的分数分布图,我们看到它们分成 3 组:
- 带有
GCG的序列(分数= ~5) - 没有基序的序列(得分= ~15)
- 带
TAT的序列(分数= ~25)

八聚体分数的分布。图片作者。
我们现在的目标是训练一个模型,通过观察 DNA 序列来预测这个分数。
2.为 PyTorch 培训准备数据
为了让神经网络做出预测,你必须把你的输入作为一个数字矩阵给它。例如,为了根据图像是否包含猫来对图像进行分类,网络将图像“视为”像素值的矩阵,并学习像素的相对排列中的相关模式(例如,对应于猫耳朵或长有胡须的鼻子的模式)。
我们同样需要将我们的 DNA 序列(ACGTs 的字符串)转换成一个数字矩阵。那么我们如何假装自己的 DNA 是猫呢?
一种常见的策略是一次性编码 DNA:将每个核苷酸视为长度为 4 的向量,其中 3 个位置为 0,一个位置为 1,具体取决于核苷酸。

这种一次性编码方案有一个很好的特性,它使你的 DNA 看起来就像计算机看到的猫的照片一样!图片作者。
有了这个一次性编码方案,我们就可以准备好我们的训练集、val 集和测试集。这个quick_split只是在 pandas 数据帧中随机选择一些指数来分割(sklearn 也有一个函数来做这个)。
注意:在真实的/非合成的任务中,根据你的预测任务,你可能需要更聪明地使用分裂策略:通常论文会根据染色体或其他基因组位置特征创建训练/测试分裂。
为 PyTorch 准备数据的一个重要步骤是使用 DataLoader 和 Dataset 对象。我花了很多时间在谷歌上搜索来找出一些东西,但这是我能够通过大量梳理文档和堆栈溢出帖子来炮制的解决方案!
简而言之,数据集将数据包装在一个对象中,该对象可以顺利地将正确格式化的 X 示例和 Y 标签提供给正在训练的模型。DataLoader 接受数据集和其他一些有关如何根据数据形成批次的详细信息,并使迭代训练步骤变得更加容易。
这些数据加载器现在可以在训练循环中使用了!
3.定义 PyTorch 模型
我感兴趣尝试的主要模型是卷积神经网络,因为这些已经被证明对从基因组数据中学习基序是有用的。但是作为比较,我包含了一个简单的线性模型。以下是一些模型定义:
注意:这些不是优化的模型,只是一些开始的东西(同样,我们只是在 DNA 环境中练习连接 PyTorch 管)。
- 线性模型试图通过简单地加权出现在每个位置的核苷酸来预测分数。
- CNN 模型使用 32 个长度为(
kernel_size) 3 的过滤器来扫描 8-mer 序列以获得信息性的 3-mer 模式。
4.定义训练循环功能
接下来,我们需要定义训练/体能循环。我承认我在这里并不超级自信,并且花了很多时间费力地解决矩阵维度不匹配错误——可能有更好的方法来解决这个问题!但也许这样就可以了?- 耸肩 -(有反馈就给我发消息🤓)
在任何情况下,我都是这样定义函数堆栈的:
*# adds default optimizer and loss function*
run_model()
*# loops through epochs*
fit()
*# loop through batches*
train_step()
*# calc train loss for batch*
loss_batch()
val_step()
*# calc val loss for batch*
loss_batch()
5.运行模型
首先,让我们试着在我们的八聚体序列上运行一个线性模型。
在收集了 train 和 val 损失之后,让我们在一个快速的图中查看它们:

线性模型训练和验证损失曲线。图片作者。
乍一看,似乎没有学到多少东西。
接下来让我们试试 CNN,画出损耗曲线。

CNN 和线性模型的损耗曲线。图片作者。
从损失曲线可以清楚地看出,CNN 能够捕捉到数据中的一种模式,而线性模型却不能!我们来抽查几个序列,看看是怎么回事。
从上面的例子可以看出,线性模型实际上是低估了含有大量 G 的序列,而高估了含有大量 T 的序列。这可能是因为它注意到GCG制造的序列具有异常低的分数,而TAT制造的序列具有异常高的分数。然而,由于线性模型没有办法考虑到GCG与GAG的不同上下文,它只是预测具有 G 的序列应该更低。从我们的评分方案中我们知道事实并非如此:不是 G 一般都是有害的,而是特别是 GCG是有害的。
CNN 更能适应 3-mer 基序之间的差异!它对有和没有基序的序列都有很好的预测。
6.在测试集上检查模型预测
在任何机器学习任务中,一个重要的评估步骤是检查你的模型是否能在测试集上做出好的预测,这是在训练中从未见过的。这里,我们可以使用奇偶图来可视化实际测试序列分数与模型预测分数之间的差异。

测试集序列的实际分数与预测分数的比较。图片作者。
奇偶图对于可视化模型预测单个序列的效果非常有用:在完美的模型中,它们都落在y=x线上,这意味着模型预测正是序列的实际值。但如果它偏离了y=x线,这意味着模型预测过高或过低。
在线性模型中,我们可以看到它可以在一定程度上预测测试集序列的趋势,但确实会被分布的高和低区域中的这些序列桶(具有基序的序列)所混淆。
然而对于 CNN 来说,它更擅长预测接近实际值的分数!这是意料之中的,因为我们的 CNN 架构使用 3-mer 核来扫描序列中有影响的基序。
但是 CNN 并不完美。我们也许可以训练它更长时间或者调整超参数,但是这里的目标不是完美——相对于实际的监管语法,这是一个非常简单的任务。相反,我认为使用 Altair 可视化库来交互式地检查模型出错的序列会很有趣:
请注意,不在对角线上的序列往往有多个基序实例!在评分函数中,如果序列至少有一个基序,我们只给它一个+/-凸起,但如果基序出现多次,决定增加多个奖励肯定是合理的。在这个例子中,我任意地只增加了至少 1 个模体出现的奖励,但是我们可以使用不同的评分函数。
无论如何,我认为这个模型注意到了多次出现并预测它们是重要的,这很酷。我想我们确实愚弄了它一点,虽然 0.95 的 R2 是相当可观的:)
7.可视化卷积滤波器
当训练 CNN 模型时,可视化第一层卷积滤波器以尝试了解更多关于模型正在学习的内容可能是有用的。对于图像数据,第一层卷积滤波器通常会学习边界、颜色或纹理等模式,这些基本的图像元素可以重新组合,以形成更复杂的特征。
在 DNA 中,卷积过滤器可以被认为类似于模体扫描仪。与用于可视化序列标志的位置权重矩阵类似,卷积过滤器就像一个显示特定 DNA 模式的矩阵,但它不是一个精确的序列,它可以保留一些关于哪些核苷酸出现在模式的哪个部分的不确定性。一些位置可能是非常确定的(例如,在位置 2 中总是有一个 A;高信息含量),而其他位置可以以大约相等的概率容纳多种核苷酸(高熵;信息量低)。
发生在神经网络隐藏层中的计算可能会变得非常复杂,并且不是每个卷积滤波器都是明显相关的模式,但是有时滤波器中的模式确实会出现,并且可以提供信息来帮助解释模型的预测。
下面是一些功能,以可视化的第一层卷积滤波器,既作为一个原始的热图,也作为一个主题标志。

好吧,也许这有点帮助,但通常人们喜欢将带有一些不确定性的序列可视化为模体标志:x 轴是模体中的位置,y 轴是每个核苷酸出现在每个位置的概率。通常,这些概率被转换成比特(也称为信息内容),以便于可视化。
为了将原始卷积滤波器转换成位置权重矩阵视觉效果,通常收集滤波器激活:沿着一个独热编码序列应用滤波器的权重,并测量滤波器激活(也称为权重与序列的匹配程度)。
对应于与给定序列紧密匹配的过滤权重矩阵将被强烈激活(产生更高的匹配分数)。通过收集产生最高激活分数的 DNA 子序列,我们可以为每个过滤器创建“高度激活序列”的位置权重矩阵,因此将卷积过滤器可视化为基序标志。

对于给定的卷积滤波器,如何收集强激活子序列并将其转换为 motif 徽标的示意图。图片作者。

从这个特定的 CNN 训练中,我们可以看到一些过滤器选择了强烈的 TAT 和 GCG 主题,但其他过滤器也专注于其他模式。
关于卷积滤波器可视化与模型可解释性的相关性存在一些争论。在具有多个卷积层的深度模型中,卷积滤波器可以在隐藏层内部以更复杂的方式重新组合,因此第一层滤波器本身可能不会提供足够的信息( Koo 和 Eddy,2019 )。这个领域的大部分已经转向注意力机制和其他可解释的方法,但是如果你好奇的把你的过滤器想象成潜在的主题,这些函数可以帮助你开始!
8.结论
本教程展示了一些基本的 PyTorch 结构,用于构建处理 DNA 序列的 CNN 模型。本演示中使用的练习任务不能反映真实的生物信号;相反,我们设计了评分方法来模拟非常短的序列中调控基序的存在,这对于我们人类来说很容易检查和验证 PyTorch 的行为符合预期。从这个小例子中,我们观察到具有滑动过滤器的基本 CNN 如何能够比仅考虑绝对核苷酸位置(没有局部上下文)的基本线性模型更好地预测我们的评分方案。
要阅读更多关于 CNN 应用于野外 DNA 的信息,请查阅以下基础论文:
- 深度绑定:阿里帕纳西等人 2015
- 深海:周与特洛扬斯卡娅 2015
- 巴塞特:凯利等人 2016
我希望其他对解决生物学问题感兴趣的新手可以发现这有助于开始使用 PyTorch 对 DNA 序列建模:)
9.脚注
脚注 1
在本教程中,CNN 模型定义使用了 1D 卷积层,因为 DNA 不是二维图像,Conv1D 足以沿着长度维度滑动,而不是上下扫描。(事实上,上下滑动过滤器并不适用于一键编码的 DNA 矩阵:将A和C行与G和T行分开是没有意义的——你需要所有 4 行来精确地表示一个 DNA 序列。)
然而,我曾经发现自己需要使用一个用 keras 构建的分析工具,并找到了一个 pytorch2keras 转换脚本。转换脚本只知道如何处理 Conv2d 层,并给出了带有 Conv1d 层的模型的错误:(
如果您遇到这种情况,以下是如何使用 Conv2D 重新格式化 CNN 定义的示例,同时确保它仍然像 Conv1D 一样扫描 DNA:
脚注 2
如果你正在做一个分类任务而不是回归任务,你可能想要使用CrossEntropyLoss。然而,CrossEntropyLoss期望的格式与MSELoss略有不同——试试这个:
loss = loss_func(xb_out, yb.long().squeeze(1))
使用 PyMC3 建模营销组合
原文:https://towardsdatascience.com/modeling-marketing-mix-using-pymc3-ba18dd9e6e68
实验先验、数据标准化,并将贝叶斯建模与 Robyn(脸书的开源 MMM 包)进行比较

杰里米·贝赞格在 Unsplash 上的照片
在这篇文章中,我将贝叶斯方法应用于评估不同媒体渠道的广告支出对收入的影响的营销问题。我涵盖了贝叶斯建模的几个方面,这些方面对 MMM 从业者应该很重要:
- 因变量和自变量的标准化以及先验的选择
- 响应变量的标准化对估计效果的影响
此外,我将结果与 Robyn 框架进行了比较,遵循其方法论,使用岭回归的 MMM 建模开源包,以及用于超参数优化的无梯度优化框架。
之前关于营销组合建模和贝叶斯推理的出版物
有不少关于使用贝叶斯编程对营销组合建模的文章,涵盖了建模的不同方面,例如:
- 贝叶斯框架及其揭示数据真实参数的稳健性:具有遗留和形状效应的媒体混合建模的贝叶斯方法,媒体混合建模中的遗留和形状效应:论文综述
- 倍增 MMM: 倍增营销组合模型的 Python/STAN 实现
- 股票和饱和效应建模:通过 PyMC3 在 Python 中进行贝叶斯营销组合建模
- 贝叶斯模型的实际用法: HelloFresh
文章的结构如下
- 营销组合建模 —我简单介绍一下 MMM 背后的理论。
- Adstock/carry Effect—我介绍了几何 ad stock 函数的差异,提供了 Robyn 框架中使用的几何函数的 ano 实现
- 收益递减/饱和效应 —我涵盖了可用于收益递减建模的各种函数
- 建模 —这是本文的主要部分,我在其中探索了数据规范化对结果的影响。我使用 Robyn 团队提供的演示数据,并遵循 Robyn 的数据处理方法。最后,我将结果与 Robyn 的建模进行比较。
营销组合建模
我花在广告上的钱有一半都浪费了;问题是我不知道是哪一半(约翰·沃纳梅克)
MMM 的目标是了解销售的驱动因素,衡量所有可能影响销售的因素的影响。这些因素可分为两大类:一类是对销售只有间接影响的因素(也称为基准),如经济形势、节假日、天气、竞争;另一类是对销售有直接影响的因素(也称为营销贡献),如在不同媒体渠道(如电视、广播、在线平台或价格)上的广告支出(ad spend)以及促销。
建模器的目标是定义可能影响销售的相关组件,准备营销活动和其他(控制)变量的历史时间序列数据,并建立一个统计模型来估计每个组件对销售的影响。这通常是使用多元线性回归来完成的。
整个过程因两种营销效应而变得复杂:结转效应和收益递减效应。广告对销售的影响可能会有延续效应,也就是说,在广告之后的几天或几周内,销售会受到广告的影响。此外,由于消费者反应迟缓,广告效果可能不会立竿见影,在这种情况下,我们谈论的是滞后效应。
收益递减表明,从特定的媒体支出开始,支出和销售之间的关系不是线性的,而是达到了一个饱和点,在这个点上,额外的广告投入不会导致销售的增加。
MMM 面临的挑战是对每个媒体渠道的遗留和饱和效应以及基线和媒体成分进行建模,将一些约束应用于从营销角度来看有意义的建模。其中一个限制是,媒体支出对销售有积极影响,这要求线性模型估计媒体渠道的正系数。
解决这一挑战需要一个建模框架,能够根据不同的约束和先验知识优化各种参数。这可以通过使用一些通用的超参数优化框架或使用贝叶斯编程来实现。在本文中,我使用 PyMC3,一个贝叶斯框架,来建模营销组合。
库存/结转影响
我们可以使用几个 adstock 变换函数来模拟遗留效应。常用的变换是所谓的几何衰减的 adstock。然而,它有两种口味。第一个在这篇论文中描述,有三个参数需要估计:
- 广告效应的衰减率:0<α<1,简单来说就是如果我们今天投资 100 欧元,α为 0.5,那么明天的预期效应将是 50 欧元
- 假设媒体频道的最大效果持续时间(以天、周为单位)
- ****峰值效应θ (0 ≤ θ ≤ L-1)的延迟,对不会立即开始的广告支出可能产生的滞后效应进行建模。
这个版本转换的实现可以在这里找到。基于该版本的其他实现,但是具有稍微不同的延迟权重计算,可以在这里的和这里的和中找到。
第二个版本更容易实现。它只有一个参数——衰变率α。Robyn 团队也在他们的框架中使用这个版本。

其中,α是衰减率,x(t)是当前广告花费,y(t-1)是时间 t-1 的累积广告效果,y(t)是时间 t 的最终广告。
python 中的实现有几行代码:
def **adstock_geometric**(x: float, alpha: float):
x_decayed = np.zeros_like(x)
x_decayed[0] = x[0] for xi in range(1, len(x_decayed)):
x_decayed[xi] = x[xi] + alpha* x_decayed[xi - 1] return x_decayed
示例:
x = np.array([1.0, 2.0, 3.0, 4.0, 5.0 , 6.0])
**adstock_geometric**(x, 0.5)#output: 1.0, 2.5, 4.25, 6.125, 8.062, 10.031
- 第 2 天的库存量为 2.5: 2.0 + 1.0 * 0.5
- 第三天的库存量为 4.25: 3.0 + 2.5 * 0.5****
- 第 4 天的库存为 6.125: 4 + 4.25 * 0.5
- ….
当应用于时间序列时,adstock 效应的结果可以在下图中看到:

作者图片
因为我们要使用 PyMC3,所以我们必须重写 no 中的几何衰减函数。
def **adstock_geometric_theano_pymc3**(x, theta):
x = tt.as_tensor_variable(x)
def adstock_geometric_recurrence_theano(index,
input_x,
decay_x,
theta):
return tt.set_subtensor(decay_x[index],
tt.sum(input_x + theta * decay_x[index - 1])) len_observed = x.shape[0] x_decayed = tt.zeros_like(x)
x_decayed = tt.set_subtensor(x_decayed[0], x[0]) output, _ = theano.scan(
fn = adstock_geometric_recurrence_theano,
sequences = [tt.arange(1, len_observed), x[1:len_observed]],
outputs_info = x_decayed,
non_sequences = theta,
n_steps = len_observed - 1
)
return output[-1]
收益递减/饱和效应
有各种函数可以用来模拟广告支出和销售之间的非线性关系,例如幂函数、负指数或逻辑。Robyn 团队使用本文中描述的 Hill 函数。

其中 α 控制曲线形状, γ 控制拐点。 α 越大,收益递减越有 S 形。 α 越小,C 形越多。下面的图展示了不同的饱和度曲线作为α和γ的函数。

作者图片

作者图片
由于饱和希尔函数是在 adstock 变换后应用的,因此在 ano 中不需要特殊处理。输入 x 已经是一个张量。
def **saturation_hill_pymc3**(x, alpha, gamma):
x_s_hill = x ** alpha / (x ** alpha + gamma ** alpha)
return x_s_hill
建模
数据
我使用由 Robyn 提供的演示数据集,并遵循相同的数据准备方法步骤,以获得相同的比较基准。为了与 Robyn 解决方案进行比较,我运行了演示。Robyn 包自带的 R 文件,用 R 写的,设置没有任何改动。
该数据集包含 208 周的收入,包括:
- 5 个媒体消费渠道:电视、网络、印刷品、facebook、搜索
- 2 个也有曝光信息(印象,点击)的媒体渠道:facebook_I,search_clicks_P
- 无支出有机媒体:时事通讯
- 控制变量:事件、节假日、竞争对手销售额(competitor_sales_B )
建模窗口为 2016 年 11 月 21 日至 2018 年 8 月 20 日的 92 周。为了使其通用,我定义了两个索引变量来引用这个时间窗口:
START_ANALYSIS_INDEX = 52
END_ANALYSIS_INDEX = 144
数据准备
我们必须应用 Robyn 中描述的两个准备步骤:
Robyn 利用 Prophet ,脸书的开源 ML 库进行时间序列预测。我们使用 Prophet 直接从响应中自动分解趋势、季节性和节假日影响,作为进一步建模的输入变量。这种能力通常会提高模型拟合度,减少残差中的自回归模式。
此外,我们可以使用 Prophet 分解将分类变量如事件转换成数值。
第二步准备:
当使用曝光变量(印象、点击、GRPs 等)而不是花费时,Robyn 在曝光和花费之间拟合了一个带有米氏门登函数的非线性模型,以建立花费-曝光关系
第二步将允许我们将曝光度转换为最终支出份额与效果份额比较的支出。
让我们先加载数据:
data = pd.read_csv("./data/data_raw_2015-11-23__2019-11-11.csv", parse_dates = ["DATE"])
data.columns = [c.lower() if c in ["DATE"] else c for c in data.columns]
data

作者图片
假日数据是 Prophet 最初附带的一个单独的文件。原始假日数据具有每日粒度,因此应该首先在每周级别上进行聚合。Robyn 在他们的演示中使用了德国假日。
holidays = pd.read_csv("./data/prophet_holidays_daily.csv", parse_dates = ["ds"])
holidays["begin_week"] = holidays["ds"].dt.to_period('W-SUN').dt.start_time
#combine same week holidays into one holiday
holidays_weekly = holidays.groupby(["begin_week", "country", "year"], as_index = False).agg({'holiday':'#'.join, 'country': 'first', 'year': 'first'}).rename(columns = {'begin_week': 'ds'})
holidays_weekly_de = holidays_weekly.query("(country == 'DE')").copy()
holidays_weekly_de

作者图片
先知分解
现在,我们准备将 Prophet 拟合到我们的数据中,包括假期,一个分类变量,并使用每年的季节性。重要的是要注意,我们将分解应用于所有可用的数据,而不是建模窗口。
prophet_data = data.rename(columns = {'revenue': 'y', 'date': 'ds'})
#add categorical into prophet
prophet_data = pd.concat([prophet_data, pd.get_dummies(prophet_data["events"], drop_first = True, prefix = "events")], axis = 1)prophet = **Prophet**(**yearly_seasonality**=True, holidays=holidays_weekly_de)
prophet.**add_regressor**(name = "events_event2")
prophet.**add_regressor**(name = "events_na")prophet.fit(prophet_data[["ds", "y", "events_event2", "events_na"]])
prophet_predict = prophet.**predict**(prophet_data[["ds", "y", "events_event2", "events_na"]])

作者图片
让我们提取季节性、趋势、假日和事件组件:
prophet_columns = [col for col in prophet_predict.columns if (col.endswith("upper") == False) & (col.endswith("lower") == False)]
**events_numeric** = prophet_predict[prophet_columns].filter(like = "events_").sum(axis = 1)final_data = data.copy()
final_data["**trend**"] = prophet_predict["trend"]
final_data["**season**"] = prophet_predict["yearly"]
final_data["**holiday**"] = prophet_predict["holidays"]
final_data["**events**"] = (events_numeric - np.min(events_numeric)).values
支出风险评估
在这一步中,我们使用米氏门登函数来估计支出-敞口的非线性关系
支出-风险函数定义如下:

作者图片
其中 Vmax 和 Km 是我们需要估计的两个参数。这两个参数稍后将被用于找到暴露-花费的反向关系。为建模窗口估计参数。
#define the function
**spend_to_exposure_menten_func =** lambda spend, V_max, K_m**: V_max * spend / (K_m + spend)**media_exposures = ["facebook_I", "search_clicks_P"]
media_spends = ["facebook_S", "search_S"]media_spend_exposure_df = pd.DataFrame()
for (media_exposure, media_spend) in zip(media_exposures, media_spends):
V_max = final_data[media_exposure].values[START_ANALYSIS_INDEX : END_ANALYSIS_INDEX].max()
K_m = V_max / 2
spend = final_data[media_spend].values[START_ANALYSIS_INDEX : END_ANALYSIS_INDEX]
exposure = final_data[media_exposure].values[START_ANALYSIS_INDEX : END_ANALYSIS_INDEX]
best_values, _ = **optimize.curve_fit**(f = spend_to_exposure_menten_func, xdata = spend, ydata = exposure, p0 = [V_max, K_m])
media_spend_exposure_df = pd.concat([media_spend_exposure_df, pd.DataFrame({'spend': [media_spend], 'exposure': [media_exposure], 'V_max': [best_values[0]], 'K_m': [best_values[1]]})]).reset_index(drop = True)
media_spend_exposure_df

作者图片
贝叶斯建模
现在我们已经准备好建模了。我们将我们的响应变量(收入)建模为加性线性回归,可以用以下等式描述:

作者图片
其中 b_0 对应于基线收入, b_m 系数对应于通过 adstock 和 saturation 函数转换的媒体变量, b_c 系数对应于控制变量,ϵ是一些噪声。所有提到的系数、噪声以及吸附和饱和函数的参数都应该由模型来估计。
在建模之前,我们必须做出的第一个决定是,我们将如何标准化我们的因变量和自变量。通过标准化独立变量,我们可以将我们的模型推广到其他数据源,因为我们可以对大多数独立变量使用相同的先验。此外,很难对非标准化数据设定先验。因此,我对独立变量应用 0–1 归一化,并对响应变量进行两种不同的归一化实验:
- 扩展 100K
- 0–1 标准化
缩放 100K
最初的收入范围是 672250–3827520,因此通过将收入扩大 100K,我得到了以下范围:6.72–38.27,这使得试验先验更容易。
首先,我定义我的输入变量:
data = final_data
**transform_variables** = ["trend", "season", "holiday", "competitor_sales_B", "events", "tv_S", "ooh_S", "print_S", "facebook_I", "search_clicks_P", "newsletter"]**delay_channels** = ["tv_S", "ooh_S", "print_S", "facebook_I", "search_clicks_P", "newsletter"]**media_channels** = ["tv_S", "ooh_S", "print_S", "facebook_I", "search_clicks_P"]**control_variables** = ["trend", "season", "holiday", "competitor_sales_B", "events"]**target** = "revenue"
然后,我使用最小最大缩放器将自变量标准化,并将因变量缩放 100K
data_transformed = data.copy()numerical_encoder_dict = {}for feature in transform_variables:
scaler = **MinMaxScaler**()
original = data[feature].values.reshape(-1, 1)
transformed = scaler.fit_transform(original)
data_transformed[feature] = transformed
numerical_encoder_dict[feature] = scalerdependent_transformation = None
original = data[target].values
data_transformed[target] = **original / 100_000**
建模部分包括几个步骤:
- 我首先通过媒体通道(延迟通道)进行迭代,并定义 adstock 和饱和度变换的先验。我试验了不同的先验,但最终我使用了那些在贝叶斯方法论文中描述的先验
- 我对所有可用数据应用 adstock 变换,以允许使用历史数据建立结转效应
- 我在由 START_ANALYSIS_INDEX 和 END_ANALYSIS_INDEX 定义的建模窗口上应用饱和度变换
- 我使用半正态分布强制延迟通道的回归系数为正
- 接下来,我遍历其余的变量,对系数的符号没有任何限制
- 我将截距的先验定义为从收入平均值开始的正态分布。我遵循罗宾的方法,将截距限制为正值。
response_mean = []
with pm.Model() as model_2:
for channel_name in **delay_channels**:
print(f"Delay Channels: Adding {channel_name}")
x = data_transformed[channel_name].values
**adstock_param** = pm.Beta(f"{channel_name}_adstock", 3, 3)
**saturation_gamma** = pm.Beta(f"{channel_name}_gamma", 2, 2)
**saturation_alpha** = pm.Gamma(f"{channel_name}_alpha", 3, 1)
x_new = **adstock_geometric_theano_pymc3**(x, adstock_param)
x_new_sliced = x_new[START_ANALYSIS_INDEX:END_ANALYSIS_INDEX]
saturation_tensor = **saturation_hill_pymc3**(x_new_sliced, saturation_alpha, saturation_gamma)
**channel_b** = pm.HalfNormal(f"{channel_name}_media_coef", sd = 3)
response_mean.append(**saturation_tensor * channel_b**)
for control_var in **control_variables**:
print(f"Control Variables: Adding {control_var}")
x = data_transformed[control_var].values[START_ANALYSIS_INDEX:END_ANALYSIS_INDEX]
**control_beta** = pm.Normal(f"{control_var}_control_coef", sd = 3)
control_x = **control_beta * x**
response_mean.append(control_x)
**intercept** = pm.Normal("intercept", np.mean(data_transformed[target].values), sd = 3)
#intercept = pm.HalfNormal("intercept", 0, sd = 3)
**sigma** = pm.HalfNormal("sigma", 4)
**likelihood** = pm.Normal("outcome", mu = **intercept + sum(response_mean)**, sd = sigma, observed = data_transformed[target].values[START_ANALYSIS_INDEX:END_ANALYSIS_INDEX])
我从以前的分布中生成样本,以检查我对以前的选择是否合理:

作者图片
并绘制先验分布:





作者图片
现在我们可以拟合模型了:
with model_2:
trace = pm.sample(1000, tune=1000, step=None, **target_accept = 0.95**, return_inferencedata=True)
trace_summary = az.summary(trace)
我将 target_accept 参数增加到 0.95,因为我得到了一些带有默认值的收敛警告。
当拟合完成时,我从后面采样数据
with model_2:
ppc_all = **pm.sample_posterior_predictive**(
trace, var_names=["outcome"] + list(trace_summary.index), random_seed=42
)
az.plot_ppc(az.from_pymc3(posterior_predictive=ppc_all, model=model_2), var_names = ["outcome"])

作者图片
观察到的数据的分布不是正态的,而是严格为正的,而我定义的可能性是正态分布的,这就是为什么我们在最低收入水平上看到不匹配。这也可能是一些收敛问题的原因,但使用标准缩放器或 PowerTransformer 并没有带来任何改善,所以我决定坚持使用更直观的标准化。
有了后验样本,我们现在可以测量拟合优度,绘制各种辅助图,如残差图,并执行收入分解和测量渠道贡献。
拟合优度
Robyn 用来测量预测误差的指标之一是归一化均方根误差( NRMSE )。
def **nrmse**(y_true, y_pred):
return np.sqrt(np.mean((y_true - y_pred) ** 2)) / (np.max(y_true) - np.min(y_true))
预测收入是后验样本的平均值乘以 100K:
y_true = data[target].values[START_ANALYSIS_INDEX:END_ANALYSIS_INDEX]#restore the original revenue by multiplying back 100K
y_pred = ppc_all["outcome"].mean(axis = 0) * 100_000print(f"RMSE: {np.sqrt(np.mean((y_true - y_pred)**2))}")
print(f"MAPE: {np.mean(np.abs((y_true - y_pred) / y_true))}")
print(f"NRMSE: {nrmse(y_true, y_pred)}")
分解
为了按渠道分解收入,我们必须使用模型估计的参数对媒体渠道应用库存和饱和度,然后乘以相应的估计系数。
模型估计的参数和系数汇总:

作者图片
我绘制了实际收入、预测的后验收入以及通过合计每个组成部分的收入贡献计算的收入,以比较分解的收入是否与预测的后验收入匹配。

作者的形象
分解收入的 NRMSE:0.058,MAPE: 0.067
最后一步是计算媒体渠道支出份额,并将其与收入份额(效果份额)进行比较
我使用原始数据计算支出份额。我找到了使用其曝光信息(facebook_I,search_clicks_P)建模的变量的花费,使用曝光与花费的关系:
**exposure_to_spend_menten_func** = lambda exposure, V_max, K_m: **exposure * K_m / (V_max - exposure)**spend_df = pd.DataFrame()
for media_channel in **media_channels**:
temp_series = data[media_channel].iloc[START_ANALYSIS_INDEX:END_ANALYSIS_INDEX].values
#exposure to spend should
if len(media_spend_exposure_df[media_spend_exposure_df.exposure == media_channel]) > 0:
**vmax** = media_spend_exposure_df[media_spend_exposure_df.exposure == media_channel]["V_max"].iloc[0]
**km** = media_spend_exposure_df[media_spend_exposure_df.exposure == media_channel]["K_m"].iloc[0]
**spends** = **exposure_to_spend_menten_func**(temp_series, V_max = vmax, K_m = km)
spends_total = spends.sum()
else:
spends_total = temp_series.sum()
spend_df = pd.concat([spend_df, pd.DataFrame({'media': [media_channel], 'total_spend': [spends_total]})]).reset_index(drop=True)spend_df["**spend_share**"] = spend_df["total_spend"] / spend_df["total_spend"].sum()
spend_df
然后,我用分解的信息找出这些变量的影响份额。
response_df = pd.DataFrame()
for media_channel in **media_channels**:
response = data_transformed_decomposed[media_channel].iloc[START_ANALYSIS_INDEX:END_ANALYSIS_INDEX].values
response_total = response.sum()
response_df = pd.concat([response_df, pd.DataFrame({'media': [media_channel], 'total_effect': [response_total]})]).reset_index(drop=True)
response_df["**effect_share**"] = response_df["total_effect"] / response_df["total_effect"].sum()response_df
最后,绘制支出份额与效果份额的对比图:

作者图片
0–1 归一化
让我们进行同样的实验,但是这次将响应变量归一化到 0 和 1 之间
dependent_transformation = **MinMaxScaler**()
original = data[target].values.reshape(-1, 1)
transformed = dependent_transformation.fit_transform(original)
data_transformed[target] = transformed
截距和系数的先验现在应根据响应范围进行调整:
response_mean = []
with pm.Model() as model_3:
for channel_name in **delay_channels**:
print(f"Delay Channels: Adding {channel_name}")
x = data_transformed[channel_name].values
adstock_param = pm.Beta(f"{channel_name}_adstock", 3, 3)
saturation_gamma = pm.Beta(f"{channel_name}_gamma", 2, 2)
saturation_alpha = pm.Gamma(f"{channel_name}_alpha", 3, 1)
x_new = adstock_geometric_theano_pymc3(x, adstock_param)
x_new_sliced = x_new[START_ANALYSIS_INDEX:END_ANALYSIS_INDEX]
saturation_tensor = saturation_hill_pymc3(x_new_sliced, saturation_alpha, saturation_gamma)
**channel_b** = pm.HalfNormal(f"{channel_name}_media_coef", sd = 0.1)
response_mean.append(saturation_tensor * channel_b)
for control_var in **control_variables**:
print(f"Control Variables: Adding {control_var}")
x = data_transformed[control_var].values[START_ANALYSIS_INDEX:END_ANALYSIS_INDEX]
**control_beta** = pm.Normal(f"{control_var}_control_coef", 0.1, sd = 0.1)
control_x = control_beta * x
response_mean.append(control_x)
**intercept** = pm.HalfNormal("intercept", 0.1)
**sigma** = pm.HalfNormal("sigma", 0.15)
**likelihood** = pm.Normal("outcome", mu = **intercept + sum(response_mean)**, sd = **sigma**, observed = data_transformed[target].values[START_ANALYSIS_INDEX:END_ANALYSIS_INDEX])
事先分配似乎是合理的:

作者图片
建模和后验预测检查与之前相同,因此让我们检查拟合优度,并绘制花费与效果份额的对比图:
NRMSE: 0.058,MAPE: 0.065

现在比较这两种模型:

作者图片
在估计的效果份额上有微小的差异,但是我可以得出结论,在估计效果份额与花费份额的相对大小上,两个模型是一致的
与罗宾的比较
Robyn 生成一组模型,并允许建模者选择与业务最相关的模型。对于几个最佳解决方案,它会生成支出份额与效果份额的对比图,并将它们保存在文件中。
在最好的模型中,我主观地挑选了一个或多或少与 PyMC3 生成的模型相当的模型:

Robyn 生成的图像
我将 Robyn 与两款 PyMC3 车型进行了比较:

作者图片
比较表明,在 PyMC3 中生成的两个模型的结果彼此更相似,而不是与 Robyn 更相似。一个可能的原因是,与 PyMC3 生成的解决方案类似的解决方案是首选,但不是首选。另一个原因可能与 Robyn 选择顶级候选人的方式有关:Robyn 使用两种方法进行多目标优化:旨在减少模型预测误差的 NRMSE 和 RSSD(分解均方根距离):
该距离说明了花费份额和渠道的系数分解份额之间的关系。如果距离太远,其结果可能太不现实——例如,花费最小的媒体活动获得最大的效果
结论
在本文中,我试验了自变量和因变量、先验和后验分布的标准化。我使用了 Robyn 提出的数据准备方法,该方法允许在真实场景中重用本文中的代码。同样,我将贝叶斯建模的结果与 Robyn 进行了比较。由于模型的最终选择与业务相关,如果没有额外的业务背景和校准,在这些实验中很难确定 Robyn 或 PyMC3 生成的模型是否更好。如果只比较 NRMSE,罗宾选择的顶级模特 NRMSE 更低,因此更适合。由于 Robyn 额外优化了与业务相关的指标,因此它很少有机会生成一个在统计上准确但从营销角度看不切实际的模型。我相信贝叶斯 MMM 解决方案还可以有进一步的改进。一些对我来说仍然开放的问题是如何改善先验参数化以解决收敛警告,以及什么样的非正态概率分布可以用于非正态分布的正响应变量。
完整的代码可以从我的 Github repo 下载
感谢阅读!
使用平滑样条建模营销组合
原文:https://towardsdatascience.com/modeling-marketing-mix-using-smoothing-splines-98dc8e84c367
在不明确转换媒体变量的情况下捕捉非线性广告饱和度和收益递减

Pawel Czerwinski 在 Unsplash 上的照片
T 营销人员建立营销组合模型的方法是应用线性回归模型,该模型假设广告支出等营销活动与响应变量(销售额、收入)之间的关系是线性的。在建模之前,媒体支出变量应该经过两个必要的转换,以正确捕捉广告支出的结转效应和饱和效应。众所周知,广告支出相对于响应变量不是线性的,而是遵循收益递减规律。然而,饱和曲线的函数形式事先并不知道。因此,建模者应该首先假设应用于每个媒体活动渠道的可能转换函数,以匹配真实的花费-响应关系。在本文中,我展示了一种使用平滑样条对营销组合进行建模的替代方法,这是在线性模型框架内对自变量和因变量之间的非线性关系进行建模的方法。通过遵循这种方法,该模型将建立媒体活动变量和响应变量之间的非线性关系,而不需要转换那些独立变量来说明非线性关系。
媒体活动变量转换背后的直觉
让我们通过例子来理解为什么媒体活动变量的转换是线性模型框架中的必要步骤。
我生成一个包含广告支出的媒体变量,其饱和效应由 hill 函数描述,该函数包含两个参数 γ、控制拐点,以及 α 曲线的形状。
import numpy as np
spend = np.random.rand(1000, 1).reshape(-1)
noise = np.random.normal(size = 1000, scale = 0.2)#hill transformation
alpha = 2
gamma = 0.1spend_transformed = spend**alpha / (spend ** alpha + gamma ** alpha)response = 1 + 3 * spend_transformed + noise
有关可用于建模饱和效应和收益递减的不同转换函数的概述,您可以查看以下文章:
让我们绘制支出与响应的关系图:

作者图片
现在,当我们拟合一个线性回归模型而没有首先转换我们的自变量时,会发生什么?
import statsmodels.api as sm
spend_with_intercept = sm.add_constant(spend)
ols_model = sm.OLS(response, spend_with_intercept)
ols_results = ols_model.fit()
ols_results.params

作者图片
正如所料,线性回归不能捕捉非线性关系。现在,在现实生活中,此时此刻,我们必须决定转换函数及其参数。让我们使用 hill 函数来转换我们的支出变量,该函数具有用于模拟响应变量和拟合线性回归模型的略微不同的参数。
**alpha** = 2
**gamma** = 0.3#hill transformation
**spend_transformed** = spend**alpha / (spend ** alpha + gamma ** alpha)X_hill_transformed = sm.add_constant(spend_transformed)
ols_model_hill_transformed = sm.OLS(response, X_hill_transformed)
ols_results_hill_transformed = ols_model_hill_transformed.fit()
ols_results_hill_transformed.params

作者图片
该模型现在捕捉到了非线性,但在支出较低的地区,存在明显的不匹配。让我们用不同的参数值再试一次。

作者图片
当我们将 gamma 值移至更接近用于生成响应的原始值时,我们会得到越来越好的拟合。当然,建模者不会手动尝试不同的参数。相反,这个过程是由超参数优化框架自动完成的。然而,变换步骤仍然需要努力就适当的变换函数和计算时间达成一致,以找到近似的变换参数,从而更好地将模型拟合到数据。
有没有办法省略转换步骤,让模型找到非线性关系?是的。在我以前的一篇文章中,我描述了一种实现这一目标的机器学习方法:
尽管使用基于任意树的机器学习算法的优点是提高了线性回归方法的准确性并处理了非线性,但是一个重要的缺点是这些算法不能像线性模型那样保持很好的可解释性。因此,需要像 SHAP 这样的额外方法来解释媒体表现,这对营销人员来说可能不是直观的。第二种方法是扩展线性模型,以允许:
- 保持可解释性
- 模拟非线性效应
这种扩展被称为广义加法模型,其中非线性效应的建模是通过使用所谓的平滑样条来实现的。
广义可加模型(GAM)和光滑样条综述
标准(加性)线性模型采用以下形式

作者图片
其中,β是系数,ε是误差项。
广义加性模型用以下形式扩展了线性回归:

作者图片
其中 f 是一个光滑函数。本质上,GAM 将平滑项的和与线性项的和相加。有很多平滑函数的方法,其中惩罚平滑样条因其计算效率而被推荐。
惩罚平滑样条线的工作方式如下:
首先,数据变量的范围被分成 K 个不同的区域,具有 K 个区域边界,称为结。第二,在每个区域内,低次多项式函数拟合数据。这些低次多项式被称为基函数。样条是加权基函数的总和,根据数据值进行评估。向最小平方损失函数添加惩罚来控制模型的平滑度。平滑参数λ控制估计平滑函数 f 的平滑度和波动度之间的权衡。λ的低值产生未补偿的样条,而λ的高值产生线性线估计。
Python 中的广义加性模型
让我们回到上一个例子,用惩罚平滑样条拟合支出响应数据。
在 Python 中, pyGAM 是用于构建广义加法模型的包
pip install pygam
我使用 LinearGAM 函数对线性 GAM 建模。样条项通过使用期望变量索引的 s 函数来定义。 n_splines 控制应该安装多少个基函数。我将 lambda 设置为非常低的值 0.1,这意味着我几乎不会惩罚样条曲线的摆动
from pygam import LinearGAM, s
gam_hill = LinearGAM(s(0, n_splines=10), lam = 0.1).fit(spend, response)
让我们来理解模型的输出:
gam_hill.summary()

作者图片
值得一提的重要部分是:
- 秩-样条或基函数的数量
- λ-平滑参数
- EDoF——有效自由度。更高的 EDoF 意味着更复杂的样条。当 EDoF 接近 1 时,表明下划线项是线性的。R(pyGAM 的等价物)中的 mgcv 包的文档建议了以下选择若干样条的经验法则:
与所有模型假设一样,非正式地检查*n_splines*的选择是有用的。如果模型项的有效自由度估计比*n_splines-1*小得多,那么这不太可能是非常值得的,但是随着 EDoF 接近*n_splines-1*,检查可能是重要的:增加*n_splines*并改装原始模型。如果这样做没有统计上的重要变化,那么*n_splines*就足够大了。当*n_splines*增加时,平滑度选择标准和/或有效自由度的变化,为拟合是否发生实质性变化提供了明显的数字度量。
让我们提取支出部分:
XX = gam_hill.generate_X_grid(term=0, n = len(response))
YY = gam_hill.partial_dependence(term=0, X=XX)
响应的期望值是包括截距在内的所有单项的总和:
intercept_ = gam_hill.coef_[-1]
response_prediction = intercept_ + YY
让我们绘制最终的花费-响应关系图:

作者图片
该模型可以在不转换自变量的情况下捕捉非线性花费-响应关系。
为了完整起见,让我们来看看样条的基函数:
basis_functions = pd.DataFrame(gam_hill._modelmat(spend).toarray())
basis_functions["spend"] = spend#plot
ggplot(pd.melt(basis_functions, id_vars="spend"), aes("spend", "value", group="variable", color = "variable")) + geom_line() + ggtitle(f"{len(basis_functions.columns) - 2} Basis Functions plus Intercept")

作者图片
生成的样条是基函数乘以相应系数的加权和:
basis = gam_hill._modelmat(spend).toarray()
coefficients = gam_hill.coef_#omit intercept
spline = basis[:,:-1] @ coefficients[:-1]

作者图片
我省略了截距的添加,因此该图显示了支出变量的分解平滑效果。
用广义加法模型模拟营销组合
现在,当我们理解了 GAM 是如何工作的,我将展示如何将它整合到 MMM 框架中。我将重用我在下面的文章中介绍的框架:
变化很小。我用的是 GAM,而不是随机森林。解释黑箱模型所必需的 SHAP 成分不再需要了。由于 GAM 中成分的线性可加性,我们分析每个平滑媒体变量的影响几乎类似于线性回归模型。
数据
正如在我以前关于 MMM 的文章中一样,我继续使用由 Robyn 在麻省理工学院许可下提供的数据集进行基准测试,并通过应用 Prophet 来分解趋势、季节性和假日,遵循相同的数据准备步骤。
该数据集包含 208 周的收入(从 2015 年 11 月 23 日到 2019 年 11 月 11 日),包括:
- 5 个媒体消费渠道:电视、网络、印刷品、facebook、搜索
- 2 个也有曝光信息(印象,点击)的媒体渠道:facebook_I,search_clicks_P(本文未使用)
- 无支出有机媒体:时事通讯
- 控制变量:事件、假期、竞争对手销售额(competitor_sales_B )
分析窗口为 2016 年 11 月 21 日至 2018 年 8 月 20 日的 92 周。
建模
建模步骤与我在上一篇文章中描述的完全相同:
我将提到关于 GAM 的重要变化。
pyGAM 期望输入特性有一个非常非标准的定义。我已经编写了一个助手函数,它采用两种类型的特征:应该建模为线性的特征和应该建模为平滑样条的特征。此外,样条线的数量作为参数提供
def build_gam_formula(data,
base_features,
smooth_features,
n_splines = 20):
#set the first spline term
channel_index = data.columns.get_loc(smooth_features[0])
formula = s(**channel_index**,
**n_splines**,
**constraints**='monotonic_inc')
for smooth_channel in **smooth_features**[1:]:
channel_index = data.columns.get_loc(smooth_channel) #smooth term
formula = formula + s(channel_index,
n_splines,
**constraints**='monotonic_inc') for base_feature in **base_features**:
feature_index = data.columns.get_loc(base_feature) #linear term
formula = formula + l(feature_index)return formula
应用于 6 个媒体通道和 5 个控制变量的结果公式如下所示:
s(5) + s(6) + s(7) + s(8) + s(9) + s(10) + l(0) + l(1) + l(2) + l(3) + l(4)
例如,s(5)意味着对数据集中列索引为 5 的变量应用平滑样条。
注意值为 monotonic_inc. 的约束参数在线性回归的情况下,我们通过将线性系数约束为正来应用一些与饱和效应和收益递减相关的商定业务逻辑,以便广告支出的增加不会导致响应的减少。
查看这篇文章了解更多细节:
在 GAM 的上下文中,我将平滑样条约束为单调递增。
参数最优化
连同 5 个 adstock 参数,我为所有变量在 0.1 和 1000 之间的范围内优化平滑参数 lambda。注意:我不太清楚为什么 pyGAM 需要为明确定义为线性的变量设置平滑参数。
最终模型
我用 92 周的时间建立了最终模型。该模型的摘要如下所示:

作者图片
该摘要示出了 6 个媒体频道中的 3 个获得了高平滑λ,并且它们对应的接近 1 的 EDoF 表明了几乎线性的关系。
如果将误差指标与评估进行比较,GAM 获得了比贝叶斯方法更高的准确性,但略低于随机森林

作者图片
收益递减/饱和效应
让我们画出由此产生的花费-反应关系图。垂直线对应于渠道中的平均花费。x 轴是媒体支出。y 轴是支出对响应变量的影响

作者图片

作者图片

作者图片

作者图片

作者图片
正如在审查最终模型后所预期的那样,几个媒体渠道(search_S,ooh_S)具有线性效应。
以 facebook_S,为例,我们观察到收益递减的强烈 S 形,这表明广告支出从接近 20 万美元开始不会带来任何额外的反应增加。
取 print_S,其响应速度在大约 125K 花费时下降
观察 search_S,它的响应率几乎线性增加,但速度非常慢。
结论
在本文中,我展示了另一种营销组合建模方法,即在媒体渠道上应用惩罚平滑样条。这种方法不需要转换媒体支出变量来解释收益递减的非线性。最后,与线性回归模型相比,平滑样条导致更好的模型拟合,这增加了分解效果的可信度。
完整的代码可以从我的 Github repo 下载
感谢阅读!
用约束系数模拟营销组合
原文:https://towardsdatascience.com/modeling-marketing-mix-with-constrained-coefficients-234b23190ee2
如何拟合一个 SciPy 线性回归并使用 RPy2 接口从 Python 调用 R 岭回归

威尔·弗朗西斯在 Unsplash 上的照片
T 营销组合建模(MMM)中最常用的方法是使用多元线性回归,该方法在因变量(如销售额或收入)和自变量(包括媒体广告渠道,如电视、印刷品)以及其他变量(如趋势、季节性、假期)之间建立线性关系。营销人员可能会问的一个问题是每个媒体渠道对结果有什么影响。线性回归估计每个独立变量的系数和截距,提供以下指示:
- 截距表示媒体广告支出为零时的平均结果
- 系数显示自变量单位变化时结果的变化幅度,以及
- 变化方向:系数可以是正的、负的或零
然而,营销组合中的假设是媒体广告渠道应该具有非负面效应。也就是说,随着广告支出的每一个单位的增加,销售或收入应该增加,减缓,或者至少保持为零(饱和效应)。这意味着媒体通道的结果系数应该是正的。
那么如何约束系数呢?
在我的上一篇文章中,我使用了基于树的算法,而不是线性回归,所以根本不存在系数的问题
正如我在下一篇文章中所做的,我们可以在贝叶斯环境中定义媒体通道系数的正先验
在本文中,我展示了约束系数的两种方法:
- 通过使用 Python SciPy 包中的非线性最小二乘法 curve_fit 函数拟合线性回归,作为一般解决方案
- 通过使用 Ridge,使用用 Python 包装的 R glmnet 包和 RPy2 进行正则化回归
数据
我继续使用由 Robyn 在麻省理工学院许可下提供的数据集,正如在我以前的文章中一样,用于实际和重要的例子,并通过应用 Prophet 来分解趋势、季节性和假日,遵循相同的数据准备步骤。
该数据集包含 208 周的收入(从 2015 年 11 月 23 日到 2019 年 11 月 11 日),包括:
- 5 个媒体消费渠道:电视、网络、印刷品、facebook、搜索
- 2 个也有曝光信息(印象,点击)的媒体渠道:facebook_I,search_clicks_P(本文未使用)
- 有机媒体无花费:简讯
- 控制变量:事件,节假日,竞争对手销售(竞争对手 _ 销售 _B)
无约束系数线性回归
让我们首先定义我们的自变量和因变量:
**target** = "revenue"
**media_channels** = ["tv_S", "ooh_S", "print_S", "facebook_S", "search_S"]
**organic_channels** = ["newsletter"]
**control_features** = ["trend", "season", "holiday", "competitor_sales_B", "events"]
**features** = control_features + media_channels + organic_channels
我们总共有 11 个独立变量,其中 5 个是媒体支出渠道,1 个是有机渠道,5 个是控制变量。让我们使用 statsmodels 软件包应用一个经典的多元线性回归,然后检查使用 curve_fit 的曲线拟合是否对无约束线性回归给出相同的结果
from statsmodels.regression import linear_model
from statsmodels import toolsdata_with_intercept = tools.add_constant(final_data[features])
ols = **linear_model.OLS**(endog = data[target],
exog = data_with_intercept)
ols_res = ols.fit()
print(ols_res.summary())
print(ols_res.params)

OLS 系数
curve_fit 函数的文档表明它需要一个模型函数:
它必须将自变量作为第一个参数,并将参数作为单独的剩余参数
模型函数将我们的线性关系描述为:

模型函数应该采用我们定义的尽可能多的参数,并且没有办法使这个函数适应不同数量的参数。因为我们有 11 个系数加上 1 个截距,所以我们必须提供 12 个参数:
def linear_function(data, a, b1, b2, b3, b4,
b5, b6, b7, b8, b9, b10, b11):
return a + b1 * data[:, 0]\
+ b2 * data[:, 1]\
+ b3 * data[:, 2]\
+ b4 * data[:, 3]\
+ b5 * data[:, 4]\
+ b6 * data[:, 5]\
+ b7 * data[:, 6]\
+ b8 * data[:, 7]\
+ b9 * data[:, 8]\
+ b10 * data[:, 9]\
+ b11 * data[:, 10]
调用 curve_fit ,提供模型函数,自变量为 NumPy 矩阵,目标变量为 NumPy 数组,明确设置方法为“lm ”,使用线性最小二乘优化
**curve_fit_coefs**, _ = **curve_fit**(**f** = linear_function,
**method** = "lm",
**xdata**=final_data[features].values,
**ydata** = final_data[target].values)#print coefficients
pd.DataFrame(curve_fit_coefs, index=["const"] + features, columns=["coefficient"])

曲线拟合系数
我们看到,在两种情况下,我们得到了相同的系数。intercept (const)和 search_S 媒体通道为负。让我们将截距和所有媒体通道约束为非负。Robyn 的 Github 上有一些有趣的讨论,关于截距应该是非负的还是无约束的。检查这根线。
带约束系数的线性回归
首先,我们需要定义一个助手函数来快速生成约束,其格式为 curve_fit 接受的格式:一个长度等于参数数量的数组
界限:数组 _like 的二元组,可选: 参数的上下界限。默认为无限制。元组的每个元素必须是一个长度等于参数数量的数组,或者是一个标量(在这种情况下,所有参数的界限都相同)。使用带有适当符号的
np.inf禁用所有或部分参数的界限。
def **prepare_bounds**(**intercept_value**,
**control_length**,
**media_length**,
**control_value**,
**media_value**):
lower_bounds_array = []
lower_bounds_array.append(intercept_value) for i in range(control_length):
lower_bounds_array.append(control_value) for i in range(media_length):
lower_bounds_array.append(media_value)
return lower_bounds_array
让我们准备约束条件:
- 截取和媒体通道的下限是 0,控制变量的下限是-无穷大。
- 所有变量的上限都是无穷大。
**lower_bounds_array** = \
**prepare_bounds**(**intercept_value** = 0,
**control_length** = len(control_features),
**media_length** = len(media_channels) + len(organic_channels),
**control_value** = -np.inf,
**media_value** = 0)
**upper_bounds_array** = \
**prepare_bounds**(**intercept_value** = np.inf,
**control_length** = len(control_features),
**media_length** = len(media_channels) + len(organic_channels),
**control_value** = np.inf,
**media_value** = np.inf)
下限是:
**lower_bounds_array**
#[0, -inf, -inf, -inf, -inf, -inf, 0, 0, 0, 0, 0, 0]
上限是:
**upper_bounds_array**
#[inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf]
让我们应用带约束的曲线拟合
**curve_fit_bounded_coefs**, _ = \
**curve_fit**(**f** = linear_function,
**xdata**=final_data[features].values,
**ydata** = final_data[target].values,
**bounds** = (tuple(lower_bounds_array),
tuple(upper_bounds_array)))#print coefficients
pd.DataFrame(**curve_fit_bounded_coefs**, index = ["const"] + features, columns = ["coefficient"])

SciPy 曲线拟合约束系数
这一次模型的所有参数都是正的。
这种解决方案有几个缺点:
- 模型函数需要预先定义参数的确切数量,这使得当我们想要试验不同数量的参数时很困难
- 经典的线性回归容易过度拟合,不适合相关或冗余变量,也不适合推广。在营销组合建模中,变量相互关联或数据有限时,通常需要更好的建模方法,如岭回归。岭回归已用于现实生活中,通过使用正则化技术(通过降低系数的大小来惩罚系数)来克服过拟合和多重共线性的挑战。惩罚的程度由 lambda 参数控制。
基于 R-glm net 的约束系数岭回归
为什么我决定在 Python 中使用 R glmnet?我简直找不到任何更好的解决办法。我可以用 Python 找到 glmnet 包装器,但是由于 Fortran 的依赖性,我不能在我的 Windows 机器上编译它们。此外,glmnet 自带了两个方便的函数:cv . glm net执行交叉验证并确定最佳 lambda 参数,以及 glmnet 函数构建最终模型。这两个函数都执行数据标准化,并允许控制系数的符号和截距。我只需要弄清楚如何从 Python 中调用 R 代码。幸运的是,有一个 RPy2 包,R 语言的 Python 接口,正是为了这个:
rpy2 正在运行一个嵌入式 R,使用 R 自己的 C-API 通过以下任一方式从 Python 提供对它的访问:
一个高级接口,使 R 函数像 Python 函数一样成为对象,并提供到 numpy 和 pandas 数据结构的无缝转换
更接近 C-API 的底层接口
安装 R 和 RPy2
当然你要装 R。然后安装 RPy2:
*pip install rpy2*
通过导入 RPy2,检查 RPy2 是否识别您的 R 安装:
*from rpy2 import robjects as ro
from rpy2.robjects import pandas2ri
from rpy2.robjects.packages import importr
import rpy2.situation
base = importr("base")
glm = importr("glmnet")*
如果导入顺利,获得一些关于 R 安装信息:
*for row in rpy2.situation.iter_info():
print(row)*
如果在导入过程中出现一些错误,您可以尝试以下步骤:
- 检查 etc/Rprofile 文件是否没有加载任何库
- 用 R 安装的路径创建一个 R_HOME 环境
包装 glmnet 包装
包装过程包括三个步骤
- 用 R 编写一个函数,调用岭回归并返回我们感兴趣的输出
- 为 Pandas DataFrames、Boolean 和 Float 类型编写 Python-R 类型转换器
- 在一个准备数据类型、调用 R 代码并返回结果的 Python 函数中组合所有内容
步骤 1:编写 R 代码:在 R 中拟合岭回归
RPy2 提供了rpy 2 . R objects . R函数,该函数接受任意字符串并将其计算为 R 代码:
***run_glmnet_in_r** = \
**ro.r**("""
#pure R code
*function(x_train,
y_train,
x_test,
lambda,
is_intercept,
lower_limits,
upper_limits,
alpha = 0){
mod = glmnet(x_train,
y_train,
family = 'gaussian',
alpha = alpha,
lambda = lambda,
intercept = is_intercept,
lower.limits = lower_limits,
upper.limits = upper_limits,
type_measure = 'mse')**coefs = as.data.frame(as.matrix(coef(mod)))
names(coefs)[1] = "value"
y_pred = predict(mod, s = lambda, newx = x_test)
return (list(coefs = coefs, y_pred = y_pred))*
}""")*
我定义了一个适合岭回归的纯 R 函数,期望 8 个参数:
- x_train —训练集
- y_train —响应/目标变量
- x_test —我们从拟合模型中获得其预测的测试集
- λ—我们从 cv.glmnet 得到的λ值
- is _ intercept 我们希望截距是拟合的(无约束的)还是设置为 0
- 下限,上限-每个变量的下限/上限向量
- alpha-glm net 适合由 alpha 控制的脊、套索或弹性网回归。当α0-岭回归拟合时。
返回的是系数和测试集预测的列表。
步骤 2:为 Pandas 数据帧、布尔和浮点类型编写 Python-R 类型转换器
以下代码将 Pandas 数据帧转换为 R 数据帧:
*with ro.conversion.localconverter(**ro.default_converter** + **pandas2ri.converter**):
r_df = **ro.conversion.py2rpy**(x_train)*
然而,glmnet 期望矩阵作为输入。我必须编写自己的转换器,将 R 数据帧转换成矩阵:
*data_frame_to_r_matrix = **ro.r**('*function(x) as.matrix(x)*')*
使用 RPy2 转换目标值、下限和上限的向量:
*r_y_train = **ro.vectors.FloatVector**(y_train)
r_lower_limits = **ro.vectors.FloatVector**(lower_limits)
r_upper_limits = **ro.vectors.FloatVector**(upper_limits)*
lambda 的浮点标量值被转换为单值向量:
*lambda_best_float = **ro.vectors.FloatVector**([lambda_best])*
布尔变量 is_intercept 被类似地转换:
*is_intercept_bool = **ro.vectors.BoolVector**([is_intercept])*
从 R 列表到 Python 字典的转换需要一个定制的解决方案:
*def **convert_r_list_into_dictionary**(r_list):
py_dict = {}
#for name in coefs.names:
keys = r_list.names
for i in range(len(keys)):
if isinstance(r_list[i], ro.vectors.DataFrame):
with ro.conversion.localconverter(ro.default_converter + pandas2ri.converter):
py_dict[keys[i]]=r_list[i]
elif isinstance(r_list[i], ro.vectors.FloatVector):
array = np.array(r_list[i])
if len(array) == 1:
array = array[0]
py_dict[keys[i]] = array
else:
py_dict[keys[i]] = r_list[i]
return py_dict*
步骤 3:将所有步骤组合到一个 Python 函数中,该函数准备数据类型,调用 R 代码并返回结果
*def **run_glmnet**(x_train,
y_train,
x_test,
lambda_best,
is_intercept,
lower_limits,
upper_limits,
alpha = 0):
#type conversions
with ro.conversion.localconverter(ro.default_converter + pandas2ri.converter):
r_df = ro.conversion.py2rpy(x_train)
r_test_df = ro.conversion.py2rpy(x_test)
r_x_train = data_frame_to_r_matrix(r_df)
r_x_test = data_frame_to_r_matrix(r_test_df)
r_y_train = ro.vectors.FloatVector(y_train)
r_lower_limits = ro.vectors.FloatVector(lower_limits)
r_upper_limits = ro.vectors.FloatVector(upper_limits)
lambda_best_float = ro.vectors.FloatVector([lambda_best])
is_intercept_bool = ro.vectors.BoolVector([is_intercept])
#call glmnet
mod = run_glmnet_in_r(r_x_train,
r_y_train,
r_x_test,
lambda_best_float,
is_intercept_bool,
r_lower_limits,
r_upper_limits,
alpha)
#prepare the results
mod = convert_r_list_into_dictionary(mod)
mod["coefs"] = mod["coefs"].reset_index()
return mod*
拟合岭回归
让我们定义下限和上限:
*# Set lower and upper bounds for feature coefficients
**lower_limits** = np.array([-np.inf for _ in range(len(control_features))] + [0 for _ in range(len(media_channels) + len(organic_channels))])
**upper_limits** = np.array([np.inf for _ in range(len(control_features))] + [np.inf for _ in range(len(media_channels) + len(organic_channels))])print(lower_limits)
print(upper_limits)#[-inf -inf -inf -inf -inf 0\. 0\. 0\. 0\. 0\. 0.]
#[inf inf inf inf inf inf inf inf inf inf inf]*
这次我们只为变量定义约束。截距由函数参数明确控制。
接下来,我们交叉验证以找到最佳的 lambda 参数:
*cv_glmnet = **run_cv_glmnet**(**x_train** = final_data[features],
**y_train** = final_data[target],
**is_intercept** = True,
**lower_limits** = lower_limits ,
**upper_limits** = upper_limits)print(**cv_glmnet["lambda_best"]**)
#166018.4312548283*
最后,我们使用找到的λ拟合岭回归:
*results = **run_glmnet**(**x_train** = final_data[features],
**y_train** = final_data[target],
**x_test** = final_data[features],
**lambda_best** = **cv_glmnet["lambda_best"]**,
**is_intercept** = True,
**lower_limits** = lower_limits,
**upper_limits** = upper_limits)results.keys()
#dict_keys(['coefs', 'y_pred'])*
最终系数为:
*results["coefs"]*

岭系数
结论
在某些情况下,按照营销组合建模中的业务逻辑,我们希望强制线性回归找到一个具有正系数的解决方案。然而,并不是所有的统计软件包都支持这种功能,因此需要一些变通方法。
我展示了两种约束系数的方法:使用 SciPy 非线性最小二乘优化函数 curve_fit 和 R glmnet 软件包中的岭回归。后一种方法需要使用 RPy2 接口包装 R 代码,并允许从 Python 调用任意 R 函数。
完整的代码可以从我的 Github repo 下载
感谢阅读!
使用马尔可夫链对星巴克等待时间建模,用 Python 编写
以下是如何使用马尔可夫链来确定你等待星巴克咖啡的时间

乔恩·泰森在 Unsplash 上的照片
我来自意大利,可以肯定地说,对我们来说,咖啡是一种 T4 宗教。我们喝咖啡是为了社交,早上喝咖啡是为了提神,午饭后喝咖啡,晚饭后喝咖啡。当你有一段时间没见朋友时,我们会说
"我在一家咖啡馆"
这意味着
“过来,我们喝杯咖啡”
我住在美国,美国人对咖啡的态度是不同的。我上班时喝咖啡带走。我工作的时候喝咖啡。我一边看电影一边喝咖啡。美国人不喝“浓缩咖啡”,但他们喜欢花很长时间喝完一大杯咖啡。还有一点:咖啡有多种!
如果你走进一家星巴克,你可能会看到 100 种可能的咖啡。可以是黑咖啡,可以是玛奇朵,可以是拿铁,可以是星冰乐,可以是其他很多我都不知道的东西。😅
有一些非常容易制作的咖啡,也有一些比较复杂的,需要更多的时间来制作。假设你在星巴克排队买咖啡。如果你前面有 3 个人,他们都点了黑咖啡,你可能要等大约 3 分钟才能点。
尽管如此,如果他们点了一份“额外的奶油焦糖玛奇朵,加糖屑和肉桂,加豆奶”……嗯,那可能会让你的等待时间延长一倍,或者至少你得多等几分钟。
所以问题是…
“我要等多久才能得到我的咖啡,我可以写一篇关于我要等多久才能得到我的咖啡的文章?”
当然,我们不知道其他人会点什么,所以这是一个概率问题(或者如果你想要一个随机过程)。那么我们怎么解决呢?
一个可行的方法是建立一个马尔可夫链。特别是,我们将需要一个依赖于时间的马尔可夫链。
我们来建题吧!
1.理论介绍
让我们先从理论上介绍一下这个问题,然后把它纠正过来。
先说最简单的情况。我们走进星巴克,点了咖啡。假设从数学上来说,我们可以处于这三种状态之一。
第一个状态(O)是我们点咖啡的状态。第二家( M )是我们已经点了我们的咖啡,正在等着拿的那家。因为他们正在做一杯咖啡。然后你得到了咖啡,并朝着离开州的方向转移。这意味着你的咖啡准备好了,你可以走了。

作者图片
太好了。现在,可能的转变是什么?
- 你肯定会从订购走向制作。( O 到 M )。
- 可以从 M 到 M(保持等待)。
- 最终,你会从米到米

作者图片
现在我们如何形式化它?
1.1 关于马尔可夫链
马尔可夫链的假设是什么?假设如下:
“处于下一个状态的概率只取决于你当前的状态”
例如:
在时间步长 t = 5 中处于状态 L 的概率只取决于你在时间步长 t = 4 中处于状态Mmake 的事实。
让我们把它形式化:

作者图片
在上面的符号中,我们知道,在时间 t,在空间 s_t (O,M,L)中的概率只取决于我们在时间 t-1 的状态。
在我们的具体实验中,我们需要记住的是,这个概率也必须依赖于时间。这是事实,因为,当然,如果我等了 5 分钟,下一分钟离开的概率比我只等了 1 分钟就离开的概率高。
这意味着:

作者图片
这正是我们之前谈论的概念。
现在,当然,不仅仅是我们在星巴克。还有很多其他的顾客!所以我们一会儿会把这个设置复杂化。但是你在上面看到的是我们将要做的一切的起点。
让我们开始吧!🤩
2.一个顾客一杯饮料的例子
让我们尽可能举一个最简单的例子。我们知道要喝什么,咖啡店里只有我们。假设我们想要一杯焦糖玛奇朵。 所有食谱都提示需要 5 分钟。假设点餐和支付需要 30 秒。所以总等待时间是 5.30 分钟。但是让我们更进一步。假设 5 分钟是平均开始时间。我们还可以说,我们可以在 4 分钟或 6:

作者图片
太好了。现在假设我们的时间刻度是 30 秒(0.5 分钟)。30 秒后,非常不幸的是,我们点了焦糖玛奇朵。8 分钟后,非常不幸的是,我们仍然在等待。
2.2 实际实施
让我们实现这一点。让我们从导入一些库开始:
让我们定义我们的状态:
太好了。现在让我们运行上面描述的整个过程。我们将使用这个函数来实现:
这是一个例子:
3.一个顾客多杯饮料的例子
现在,让我们增加模型的复杂性,使它更加真实。
假设我们不知道特定客户想要什么。这更现实,因为我们在星巴克有 150 多种饮料,它们可能需要不同的等待时间。
现在马尔可夫链看起来像这样

作者图片
与前一个不同的是,我们有一个概率分布,我们称之为:

作者图片
特别是,这是我们能得到的所有可能饮料的概率分布,我们称之为:

作者图片
例如:

作者图片
所以我们可以说:

作者图片
3.2 动手实施
从这个 Kaggle 数据集 ( CC0:公共域)我们可以导入所有的星巴克饮料:
我们不需要其他的柱子。
现在,我们不知道最常选择的饮料是什么,让我们为我们的目的创建一个假的分布:
所以我们提取 50 到 100 之间的值,然后用总和归一化,这样我们就有了一个概率向量。
所以我们有了概率分布。我们该拿它怎么办?这个概率分布将为我们排序:我们将从分布中抽取样本。那么我们必须假设每种咖啡需要不同的时间来制作。
现在,客观地说,没有那么大的区别。假设“最简单”的咖啡(假设这是一杯 guess…☕️黑咖啡)需要 3 分钟(平均值),最复杂的咖啡需要 6 分钟(平均值)(假设额外的搅打奶油焦糖玛奇朵加糖屑和肉桂加豆奶🙃).
让我们玩不同的变化(从 0.1 到 1)。

正如我们之前看到的,我们有从 3 到 6 的平均值,所以你可以有 3 到 6 分钟的平均等待时间,有时,你会比其他时间有更多的不确定性。例如,当你喝咖啡“A”时,你可能有一个最大的方差。
让我们实现以下情况:
现在,我相信在这一点上我们可以提取一些有意义的信息。比如说…
"在星巴克喝一杯咖啡通常需要多长时间?"
这个问题非常笼统,所以我们不妨用一些统计数据来回答*:
我把所有的“打印”**都去掉了,这样我的电脑就不会爆炸了。🥲*
4.多个客户多种饮料示例
太好了。现在我们有多个客户。我们该怎么办?
情况看起来是这样的:

作者图片
现在,认为我们必须等一杯咖啡煮好了,他们才开始煮你的咖啡,这是不现实的。通常你在星巴克有 3,4 个咖啡师。
这使代码变得有点复杂,因为我们需要跟踪所有咖啡师实际上忙不忙的事实。为此,我们将考虑的函数是顾客数量和咖啡师数量的函数。
这个函数就是这样做的:
好的,让我们测试一个案例,我们有 4 个顾客和 2 个咖啡师。
正如你在前两个案例中看到的,我们有免费的咖啡师,他们分别等待 7 分钟和 6.5 分钟。
** 注!这不是一个错误!只是意味着第二杯咖啡比第一杯咖啡快+点餐时间。想想也有道理。如果你点了一杯复杂的咖啡,而我只点了一杯拿铁,即使你比我先点,我等的时间也可能比你少。*
然后咖啡师就忙了。所以我们必须再等 6 分钟,我才能真正点菜。(我可以让它更真实,减去 1 分钟的点餐时间,但你明白了吗?)
当第四个顾客点餐时,有一个免费的咖啡师,所以他不用等那么久。他只等了 5.5 分钟。
注意这个模型会变得多么复杂,情况会变得多么有趣。这是一个有 10 名顾客和 5 名咖啡师的案例:
5.考虑
在这篇文章中,我们讨论了一个队列中等待时间的一般性问题。当然有多种情况:
- 我们可能是店里唯一一个知道自己想要什么的人
- 我们可能是店里唯一一个不知道自己到底想要什么的人
- 我们可能是商店里很多人不知道我们到底想要什么
这三个场景当然越来越复杂。由于这个原因,马尔可夫链变得越来越复杂,就像我们之前看到的那样。
- 在第一种情况下,唯一的概率分布是过程中“制作咖啡”的那一部分。例如,制作焦糖玛奇朵平均需要 5 分钟,高斯分布和方差为 1
- 在第二种情况中,我们得到了我们采摘的咖啡的概率分布加上咖啡制作位的概率分布,这就是之前的那个
- 在第三种情况中,以上两种情况我们都有,咖啡师的数量成为另一个需要考虑的事情
我们当然可以把这个模型复杂化很多。我们可以考虑机器会被损坏的事实。我们可以考虑咖啡师的短缺。我们可以考虑咖啡师的数量随时间变化的事实。我们可以考虑一些咖啡师比其他人更快。以及更多的可能性。
这个起点可以用来以几乎所有你想要的方式使模型复杂化。请记住,我们使用的工具是依赖于时间的马尔可夫链,并从那里开始构建😎
6.结论
如果你喜欢这篇文章,你想知道更多关于机器学习的知识,或者你只是想问我一些你可以问的问题:
A.在 Linkedin 上关注我,在那里我发布我所有的故事
B .订阅我的 简讯 。这会让你了解新的故事,并给你机会发短信给我,让我收到你所有的更正或疑问。
C .成为 推荐会员 ,这样你就不会有任何“本月最大数量的故事”,你可以阅读我(以及成千上万其他机器学习和数据科学顶级作家)写的任何关于最新可用技术的文章。
用卫星图像和激光雷达数据模拟城市热浪风险
利用 GIS、统计技术和开放 python 库进行环境风险建模的方法
最近,世界资源研究所在巴西的办公室在城市 4 森林项目下给我分配了一项咨询任务,为与气候变化相关的几种灾害建立风险的模型,例如坎皮纳斯市城市规模的洪水、山体滑坡和热浪,坎皮纳斯是巴西大都市地区之外人口最多的城市,拥有 120 多万居民。
风险建模是 g is 软件最传统的用例之一,所以我认为它不会有什么突破性的东西。然而最终,评估暴露在城市热浪中的程度被证明是值得注意的。这是一个关于我们用高空间分辨率和地面真实值来建模风险指数的快速技术说明。
风险建模复习让我们开始
你可能以前读过这个,但是回忆一些关键概念也无妨。当危险事件(如热浪)发生,引发生命损失和基础设施损坏时,它会突出显示暴露于此类危险的地理区域;以及社会及其基础设施、资产和其他易受特定危害影响的过程。这就是为什么 IPCC 声明风险是危害面对给定暴露和给定脆弱性的产物。

风险等于危险乘以暴露加上脆弱性。图片作者。改编自 2014 年 IPCC 第五次评估报告。
计算危险强度
危险被定义为造成损害的现象,并以其位置、强度、频率和概率为特征。在这里,我们将使用 Landsat 8 热波段来获取过去 7 年夏季的地表温度。
为此,我们将使用 Google Earth Engine 的 Python API 来访问集合,将值转换为摄氏度,然后计算每个像素的中值。这里我使用的是 Jupyter 笔记本,但是您可以在任何 python IDE 中这样做。
一旦我们定义了我们感兴趣的区域,我们继续使用它的边界框来运行对 Google Earth 引擎的查询。我们将主要使用波段 10(用于地表温度),但我们也加入了波段 4、3 和 2(用于自然颜色合成,以防万一)。在地球引擎的传感器的 USGS 页面上有更多的信息。
现在,我们可以开始处理我们选择的图像集合(在所选区域的时间段内共有 78 幅图像)。我们将获得每个像素的中值,并重新调整原始值,以获得地表温度(LST),单位为摄氏度,遵循美国地质调查局的建议。
太棒了。现在我们可以看看最终的乐队是什么样子。我将在我的 Jupyter 笔记本中使用 lyum,但是你可以使用任何映射库。

温度从 8 摄氏度到 50 摄氏度不等。图片作者。
由于一切看起来都很好,剩下的就是将结果导出到一个光栅文件,我们接下来可以在 GIS 软件中访问它。
用激光雷达数据和土地利用分类处理暴露
接下来我们的名单是曝光。暴露定义为位于危险易发区域的人员、基础设施和其他有形人力资产的情况。为了得到这个,我们将计算潜在温度——也就是说,有多少热量表面可以潜在储存或反射——。
为了做到这一点,我们将使用由法国城市规划办公室创造的方法。这个最近成为 QGIS 插件的工具通过模拟建筑物和树木的阴影以及材料的热属性来计算地面温度。
第一步是获取建筑物和树木的体积(来自坎皮纳市政厅收集的点云 激光雷达数据)。这些反过来又被馈送到 QGIS 的 UMEP 插件,该插件将为一天中的每个小时生成一个由建筑物和树木投射的阴影的栅格,以及一个像素范围从 0 到 1 的最终栅格,其中 0 是太阳根本照射不到的地方,1 是一整天都被阳光照射到的地方。

图片作者。
接下来,我们将获取土地覆盖数据(同样由市政厅提供,但使用任何卫星图像监督分类工具都可以轻松完成),并为它们分配一系列指标,如反射率和反照率。ICEtool 提供了一个数据库,其中包含常见材料的值,在 Campinas 找到的大多数材料都完全符合该集合。

图片作者。
有了这两个输入,ICEtool 现在就能够生成一个点网格,并计算每个点的潜在温度,同时考虑到它获得了多少阳光以及它在反射或储存热量方面将如何表现。我们使用的是 ICEtool 的 1.0 版本,所以必须通过 QGIS 中的 Python IDE 来完成。事情应该更容易了,因为它已经成为一个真正的插件。

图片作者。
最重要的是脆弱性指标
最后,我们可以处理脆弱性指标。脆弱性被定义为由物理、社会、经济和环境因素决定的条件,这些因素增加了社区对灾害影响的易感性。这里我们将使用 2010 年人口普查提供的社会指标。所选择的变量是老年人的比例、可获得饮用水的人口比例和家庭平均收入。
我不会详细讨论如何获取巴西地理统计局 2010 年人口普查的数据,因为这在其他地方有很好的记录。相反,我将提供下面生成的三个地图。请注意,与其他两个变量不同,平均收入图的色标必须反转,因为较小的值代表较高的脆弱性。

图片作者。
用标准化的尺度把它们放在一起
你可能已经注意到,到目前为止,我们使用了几种不同的音阶。危险变量的像素为 30x30m,范围为 8 到 50 摄氏度。曝光变量的像素为 5x5m,范围为 10 到 33 摄氏度。而脆弱性变量由普查区域多边形表示,范围从 0 到 100%和从 45k 到 0 雷亚尔。这意味着为了能够将它们混合起来,我们需要使它们在空间和数字上兼容,所以我们不是在比较苹果和橘子。
第一个很简单:我们用 Create Grid 命令在 QGIS 中创建了一个 150x150m 的新网格,它成为我们所有图层的新公共参考。接下来,我们需要将数据从不同的层迁移到这些新的单元中。这可以通过一个简单的“按位置连接要素”来完成,但它不太擅长处理原始数据中叠加了多个要素的像元。出于这个原因,我们宁愿使用 面积加权平均插件,它会将原始单元格中的值按相交面积按比例分配给新单元格。
第二次增容是常规的 归一化的 的不同系列。这基本上是一个公式,其中我们取系列的最大值和最小值(如 10–33°C ),以便相应地将所有值缩放到范围从 0 到 1 的系列中。明智的做法是在这样做之前从系列中删除异常值。

图片作者。
一旦完成,我们准备将风险公式应用到新生成网格的每个单元,即危险乘以暴露加上脆弱性。

图片作者。
这将创建我们的最终风险指数。标度现在从 0 到 2 变化,因此我们可以再次归一化值,然后乘以 5,得到 5 个等级,我们称之为低、中、高、非常高和极端。

图片作者。
有趣的是,这一结果否定了城市热岛与更高的建筑密度有关的概念。我们清楚地看到,即使内城显示出一些高风险集群,风险最大的区域是城市周边地区,那里没有植被,水资源和收入都较低。
这是所有的乡亲
这一块就到此为止!如果你有问题或建议,不要犹豫,随时给我写信!
如果你喜欢这篇文章,可以考虑请我喝杯咖啡,这样我就可以继续写更多的文章。
如果你还不是中会员,想支持我这样的作家,可以随时通过我的推荐链接报名:
https://guilhermeiablonovski.medium.com/membership
用 Python 建模动态系统
原文:https://towardsdatascience.com/modelling-dynamic-systems-in-python-92c14d4b35d3
Python 中平移机械振动问题运动方程的数值积分
介绍
本文旨在为建模 物理系统的动态响应提供基础。
动态系统包含时变变量,意味着激励和响应随时间变化。除了任何外部输入或干扰之外,系统的响应通常取决于初始条件,例如储存的能量。
模型是代表物理现象的数学抽象,包括微分方程(DEs) 。
高保真模型非常详细地描述了这个世界。然而,解决这些问题在计算上可能是昂贵且耗时的。因此,根据目的不同,模型通常包含假设和简化。

帕特里克·罗伯特·道尔在 Unsplash 上拍摄的照片
理论
平移机械系统沿直线移动。一级方程式赛车的悬架就是一个例子。描述这些机械系统动态行为的基本变量是:**
- x ,位移米(m)
- v ,速度以米每秒(m)为单位
- **加速度以米每秒平方(m)为单位
- F ,力牛顿(N)
图 1 展示了三个基本元件,通常包括振动问题、弹簧、惯性元件和减震器。

图 1 —基本振动元件(图片由作者提供)
- 弹簧 : 胡克定律指出弹簧的变形与作用力的方向和大小成正比,如等式 1 所示。弹簧储存势能。弹簧常数、 k ,以牛顿每米(N/m)* 为单位测量弹簧的刚度。*

等式 1——胡克定律。弹簧力与位移成比例 x ( 图片由作者提供)
- 惯性:质量为 m 的正方形块代表一个惯性元件。正如牛顿第一定律所描述的,惯性是物体抵抗运动变化的固有属性。这些类型的元素储存动能*。*
- 减震器/阻尼器:减震器通过粘性摩擦对抗运动。在缓冲器 b 上施加一个力会产生一个与质量速度成正比的反作用力,如等式 2 所示。粘性阻尼系数 b 以牛·秒/米*为单位测量。这是一个描述摩擦能量耗散的理论参数”。⁴*

等式 2 —阻尼力与惯性元件的速度成比例(图片由作者提供)
将方程 1 和 2 以及方程 3 ( 牛顿第二定律)应用于平移系统,允许形成运动的控制方程。请注意,为了让牛顿定律成立,动量和加速度必须相对于一个惯性系来测量。**

等式 3 —牛顿第二定律(图片由作者提供)
每个惯性元素需要一个微分方程。
知道了物理元素和相应的控制方程,就有可能推导出机械系统的数学模型。然后可以使用软件定义的函数对模型进行数值求解。比如 Matlab 和 Simulink 都有模拟振动问题的强大工具。
复习本 篇 对数值积分进行必要的介绍。
*https://python.plainenglish.io/how-is-numerical-integration-done-using-python-4585344e5800 *
振动问题示例
图 2 示出了示例性的质量弹簧阻尼器配置。运动为一维,正 x 方向水平向右。
假设结构处于平衡状态。 x₁ 和 x₂ 分别描述了 m₁ 和m₂从平衡状态的位移。因此,最初 x₁ = x₂ = 0 。
目标是求解 x₁和 x₂**的运动方程****

图 2-双质量动力系统示例(图片由作者提供)**
- 质量块 1 通过弹簧 k₁ 和减震器 b₁ 并联连接在固定壁上。它依靠无摩擦轴承。**
- 质量块 2 通过弹簧 k₂ 与 m₁ 连接,位于固定地面上。当 m₂ 运动时,其自身与地板之间的摩擦力倾向于对抗运动( b₂ )。
如上所述,每个惯性元件都需要自己的 DE 。因此,有两个描述系统 1 的运动方程。**
关键挑战之一是确定作用在物体上的每个力的方向。
下列方法确定 m₁:上的力的方向
- 孤立 m₁,这是感兴趣的惯性元素
- 保持所有其他惯性元件固定
- 在 x 轴正方向移动 m₁ ,在这种情况下是向右
- 分析作用在 m₁ 上的弹簧和阻尼力
应用这个策略为的自由体图(FBD) 如图 3 所示。******

图 3 —质量 1 自由体图(图 2 系统) (作者图片)**
总的来说,四种力量正在作用于 m₁.
- 代表来自弹簧₁.的回复力当 m₁向右移动时,弹簧拉伸并试图将 m₁拉回到平衡状态。
- ******bẋ₁是阻尼力,同样,它也是反运动的
- k₂(x₁ — x₂) 是弹簧 ₂ 施加在 m₁ 上的力。保持 m₂ 不动并正向移动 m₁,弹簧 ₂ 从两端向外压缩和推动。因此,这往往会将 m₁向后推。必须认识到弹簧 ₂ 长度取决于 x₁ 和 x₂ 。为了发生压缩, x₁ 必须大于 x2。因此,这个力向左,大小等于k₂(x₁——x₂).****
- 最后, F(t) 是系统的外部输入或扰动。
接下来,将同样的方法应用于 m₂ 。这一次保持 m₁ 固定,并正向移动 m₂ ,这导致图 4 中 m₂ 的 FBD。

图 4 —质量 2 自由体图(图 2 系统) (作者图片**
最后,应用等式 3 中的牛顿第二定律并将作用在 m₁ 和 m₂ 上的力相加,得到两个运动方程,等式 4 和 5。

等式 4 和 5——系统 1 的二阶运动微分方程(作者提供图片)**
方程 4 和 5 是二阶方程,如前所述,它们可以简化为一系列一阶常微分方程。
复习这篇 文章 对化一个高阶微分方程为一个一阶常微分方程组进行必要的介绍。
*******https://medium.com/geekculture/runge-kutta-numerical-integration-of-ordinary-differential-equations-in-python-9c8ab7fb279c *******
Python 实现
要点 1 显示了 Python 函数定义了为odeint sympy解算器准备好的 ODEs。
有不同类型的强制功能可用。外力模拟时变力的响应行为。在这种情况下,一个步进输入力干扰 m₁.
阶跃激励在 t = 0 时将输入从零变为有限值。
要点 1 —用 Python 定义的系统 1
Gist 2 包含要模拟 100 秒的 Python 代码。初始位置和速度对应的所有初始条件均为零。
要点 2 —用于模拟执行的 Python 代码
图 5 显示了参数值m1=1.2, m2=1.2, k1=0.9, k2=0.4, b1=0.8, b2=0.4的模拟结果。 m₁、m₂、k₁、k₂、b₁、和 b₂、的值是任意的。
改变这些值将导致不同的响应;尝试查看输出变化。然而,受迫弹簧质量阻尼器系统的稳态响应与初始条件无关。********

图 5 —系统 1 的稳态响应(图片作者)**
图 5 是稳定响应。 m₁ 在 稳态 中向前移动约 1.1 cm ,m2 在 瞬变 衰减 后也向右移动了 1.1 cm 。
示例振动问题 2
下图 6 是另一个平移振动示例系统。上面应用了相同的程序来确定其时变响应。****

图 6 —示例 2 —双质量动力系统(图片由作者)
首先,绘制两个质量的 FBD,如图 7 和图 8 所示。回想一下,力的方向是通过隔离感兴趣的惯性元件并以正位移移动,同时保持其他质量固定来确定的。

图 7 和图 8——图 6 系统中 m1 和 m2 的自由体图(图片由作者)
从 FBDs,数学模型变得明显。等式 5 表示 m₁ 的运动,而等式 6 控制 m2 的响应。

等式 5 和 6——图 6 系统的数学模型(作者的图像)
Gist 3 展示了 Python 代码,ODEs 实现为 Numpy 数组。强制函数类似于本模拟中的狄拉克δ分布。使用的参数值是m1=2.2, m2=2.5, k1=1.1, k2=1.5, b=1.4。****
要点 3 —平移系统 2 脉冲模拟 Python 代码
图 9 描述了稳定的系统脉冲响应。两个物体都以正弦模式运动。这些正弦波衰减,最终,物体回到平衡状态 x₁ = x₂ = 0。

图 9 —系统 2 脉冲响应(图片由作者提供)
示例振动问题 3
最后一个例子如图 10 所示。该设计包含三个质量块以及连接各元件的弹簧和减震器网络。****

图 10 —三质量动力系统(图片作者
上面概述的相同程序用于确定惯性元件 FBDs 以及随后的运动控制方程。图 11 显示了每个感兴趣的组件的图表。方程 7、8 和 9 是相应的方程组。

图 11-三质量系统中 m1、m2 和 m3 的自由体图(作者提供图片)

方程 7、8 和 9——三质量系统的运动方程(图片作者
三个惯性元件构成了机械装置。所以现在有三个微分方程,也就是说会有六个初始条件。
图 12 显示了系统的单位脉冲响应,运行时间为 300 秒,参数值为m1=1.2, m2=2.0, m3=1.1, k=1.2, b=1.2。

图 12 —系统 3 单位阶跃响应(图片由作者提供)
结论
所有模拟都是从初始条件设置为零开始的。但是,尝试不同的初始条件,看看结果。显然,受迫弹簧质量阻尼器系统的****稳态响应与初始条件无关。通常,动力学的关键方面是稳态响应。****
上面给出了三个用于模拟动态平移系统的例子。这篇文章仅仅触及了机械振动和动态系统建模的表面。
********https://levelup.gitconnected.com/the-two-body-problem-in-python-6bbe4a0b2f88 https://medium.com/@andrewdaviesul/membership
找到 Gist 4 中的 Python 代码来运行本文中的模拟。
要点 4——用 Python 建模动态系统
参考
[1]Charles m . Close
【2】动力学系统建模与分析—Rick Hill
【3】动力学与振动导论 —工程学院(布朗大学
【4】分数欠阻尼振荡器中粘性阻尼系数的实验评估 — Escalante-Martínez J 等《机械工程进展》(2016 年 4 月)********
使用广义线性模型模拟纽约市自行车数量
在 Python 中拟合广义线性模型的简单项目演练

介绍
尽管我写过几篇关于这个主题的文章,也在保险行业工作过,但实际上我从未从头开始拟合过一个【GLM】广义线性模型。
我知道很震惊。
所以,我决定展开我的翅膀,开展一个小项目,在那里我可以把我所有的理论知识付诸实践!
在本文中,我想带您完成一个简单的项目,使用 GLMs 对纽约市的自行车穿越量进行建模。我们还将简要介绍 GLMs 背后的主要技术细节以及使用它们的动机。
本项目使用的数据来自纽约市交通部门,可通过 CC0 许可在 Kaggle 上的 此处 获得。Kaggle 实际上从纽约开放数据中获得了这个数据集,你可以在这里找到。
广义线性模型概述
为了完整起见,我将在本文中讨论 GLMs 背后的主要概念。然而,为了更深入地理解,我强烈建议您查看我以前的文章,这些文章真正深入了它们的技术细节:
动机
广义线性模型 字面上的‘广义’线性回归 到一个目标变量即非正态。
例如,这里我们将模拟纽约市的自行车交通流量。如果我们要将此建模为一个线性回归问题,我们将假设针对我们的特征的自行车数量将遵循一个 正态分布 。
这有两个问题:
- 正态分布是连续,而单车计数是离散。
- 正态分布可以是负,但是单车计数是正。
因此,我们使用 GLMs 来克服这些问题和常规线性回归的局限性。
数学
线性回归的一般公式是:

作者在 LaTeX 中生成的方程。
其中 X 为特征, β 为系数其中 β_0 为截距,【Y | X】为 期望值 (平均值)
为了将这个线性回归公式转换为包含非正态分布,我们附加了一个叫做 的链接函数 , g() :

作者在 LaTeX 中生成的方程。
链接功能字面上的'链接'您的线性组合输入到您想要的目标分布。
对于每个分布,可以通过经验或数学方法找到关联函数。在我上面链接的文章中,我为一些发行版推导了链接函数。
并不是所有的分销都在 GLM 的保护伞下。这些分布必须是 指数族 的一部分。但是,你常见的分布大多: 【伽马】泊松二项式伯努利 都是这个家族的一部分。
对于正态分布(线性回归)的链接函数称为 恒等式 。
泊松回归
为了模拟我们的自行车数量,我们将使用泊松分布 。这种分布描述了在给定时间范围内一定数量的事件以平均发生率发生的概率。
要了解更多关于泊松分布的知识,请点击这里查看我的文章。
对于泊松回归,链接函数是 自然对数 :

作者在 LaTeX 中生成的方程。
正如你所看到的,我们的输出现在总是正的,因为我们使用的是指数。这意味着我们将避免任何可能的无意义的结果,不像如果我们使用线性回归,输出可能是负的。
同样,我还没有对 GLMs 进行全面彻底的分析,因为这将是详尽的,而且我以前已经讨论过这些主题。如果你有兴趣了解更多关于 GLMs 的知识,请务必查看我在上面链接的文章或我提供的任何超链接!
建模演练
包装
我们将首先下载基础数据科学包,以及用于 GLM 建模的 statsmodels 包。
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import statsmodels.api as sm
from statsmodels.formula.api import glm
数据
读入并打印数据:
data = pd.read_csv('nyc-east-river-bicycle-counts.csv')
data.head()

图片来自作者。
这里我们有四座桥上的自行车数量:布鲁克林、曼哈顿、威廉斯堡和昆斯伯勒,它们的总和在“总”特征下。
“日期”和索引有重复的列,所以我们来清理一下:
data.drop(['Unnamed: 0', 'Day'], axis=1, inplace=True)
data.head()

图片来自作者。
注意温度有两列:高和低。取它们的平均值,使其成为一列:
data['Mean_Temp'] = (data['High Temp (°F)'] + data['Low Temp (°F)'])/2
data.head()

图片来自作者。
降水列包含一些字符串,所以让我们删除它们:
data['Precipitation'].replace(to_replace='0.47 (S)', value=0.47, inplace=True)
data['Precipitation'].replace(to_replace='T', value=0, inplace=True)
data['Precipitation'] = data['Precipitation'].astype(np.float16)
data.head()

图片来自作者。
可视化
影响自行车数量的两个主要独立变量是温度和降水量。我们可以将这两个变量与目标变量“Total”相对照:
fig = plt.figure(figsize=(22,7))
ax = fig.add_subplot(121)
ax.scatter(data['Mean_Temp'], data['Total'], linewidth=4, color='blue')
ax.tick_params(axis="x", labelsize=22)
ax.tick_params(axis="y", labelsize=22)
ax.set_xlabel('Mean Temperature', fontsize=22)
ax.set_ylabel('Total Bikes', fontsize=22)
ax2 = fig.add_subplot(122)
ax2.scatter(data['Precipitation'], data['Total'], linewidth=4, color='red')
ax2.tick_params(axis="x", labelsize=22)
ax2.tick_params(axis="y", labelsize=22)
ax2.set_xlabel('Precipitation', fontsize=22)
ax2.set_ylabel('Total Bikes', fontsize=22)

作者用 Python 生成的图。
系统模型化
现在,我们可以通过 statsmodel 软件包使用平均温度功能构建一个模型来预测“总量”。由于这种关系是泊松关系,我们将使用自然对数链接函数:
model = glm('Total ~ Mean_Temp', data = data[['Total','Mean_Temp']], family = sm.families.Poisson())
results = model.fit()
results.summary()

图片来自作者。
我们对 GLM 使用了R-style 公式 ,因为这在后端提供了更好的性能。
分析
从上面的输出中,我们看到平均温度的系数是 0.0263 ,截距是 8.1461 。
使用上面给出的泊松回归公式,我们的直线方程是:

作者在 LaTeX 中生成的方程。
x = np.linspace(data['Mean_Temp'].min(),data['Mean_Temp'].max(),100)
y = np.exp(x*results.params[1] + results.params[0])plt.figure(figsize=(10,6))
plt.scatter(data['Mean_Temp'], data['Total'], linewidth=3, color='blue')
plt.plot(x, y, label = 'Poisson Regression', color='red', linewidth=3)
plt.xticks(fontsize=18)
plt.yticks(fontsize=18)
plt.xlabel('Mean Temperature', fontsize=18)
plt.ylabel('Total Count', fontsize=18 )
plt.legend(fontsize=18)
plt.show()

作者用 Python 生成的图。
找到了。我们已经安装了 GLM!
对于感兴趣的读者来说,statsmodels 用来拟合 GLM 的算法称为 迭代重加权最小二乘 。
完整的代码/笔记本可以在我的 GitHub 上找到:
https://github.com/egorhowell/Medium-Articles/blob/main/Modelling/GLM_Bike_Volume_NYC.ipynb
结论
在本文中,我们简短地讨论了线性回归的缺点,以及 GLMs 如何通过为回归模型提供更广泛的通用框架来解决这个问题。然后我们拟合一条基本的泊松回归线来模拟纽约市的自行车数量作为日平均温度的函数。
和我联系!
- 要在媒体上阅读无限的故事,请务必在此注册! 💜
- /subscribe/@egorhowell😀
- 【领英】 👔
- 碎碎念 🖊
- github🖥
- https://www.kaggle.com/egorphysics🏅
(所有表情符号都是由 OpenMoji 设计的——开源的表情符号和图标项目。许可证: CC BY-SA 4.0
现代数据栈:Spark 何去何从?
原文:https://towardsdatascience.com/modern-data-stack-which-place-for-spark-8e10365a8772
一年前,一些人已经预测 dbt 有一天会超过 Spark ,2021 年证明他们是对的:dbt 已经变得非常受欢迎,有传言称 dbt-labs 可能会以 60 亿美元的估值再次融资。按照这种速度,他们将很快赶上 2021 年 9 月估值达到 380 亿美元的 Databricks。
尽管如此,今年 Spark 给我留下最深刻印象的是,几乎所有关于现代数据堆栈的博客帖子中都没有 Spark,现代数据堆栈是围绕两个关键组件构建的:
- 一个大规模并行 SQL 引擎(大查询、红移、雪花)
- 还有… dbt
上游:无代码提取/加载工具(Fivetran、Stitch、Airbyte、Hevo)。下游:BI 工具(Tableau、Looker、Power BI、Metabase)和反向 ETL 工具,用于将数据导出到专门的数据库(客户数据平台等)。

Dataiku 的现代数据堆栈:https://blog . Data iku . com/challenges-to-the-Modern-Data-Stack
我只需在 Google Images 中键入“Modern Data Stack ”,就会注意到数据市场中的所有公司都在提出自己的技术列表,因为他们通常会试图将自己包括在列表中。
但我也注意到,这种现代数据堆栈通常完全没有 Spark,Databricks 生态系统通常被视为它的完整替代方案。当然,Databricks 完全意识到了这一点,并且像许多其他人一样,尝试加入可以放在堆栈中心的 SQL 引擎小圈子:他们在 12 月发布了与 dbt Core 和 Databricks SQL 的完全集成。

来源:https://lakefs . io/thinks-on-the-future-of-the-data bricks-ecosystem/
最近,我回复了另一家公司的一个人,他询问将 Spark 添加到他们的现代数据堆栈中是否值得。由于我的团队目前同时使用 pySpark、BigQuery 和(一点点)dbt,我自己对这个问题想了很多。所以我用一长串支持和反对的理由来回答他们,这些理由激发了我现在的思考,我在这里分享一下:
基础设施: BigQuery 完全由 Google 管理,你什么都不用做。相比之下,掌握 Spark 要复杂得多,即使这往往会变得更容易(Spark-serverless 在 GCP 的预览版中可用,即将在 Databricks 和 Databricks SQL 中推出)。
学习曲线:同样,在 BigQuery(只有 SQL)上比 Spark 更容易找到或形成有技能的人。我的建议是:在 Scala、Java 或. Net 中,更喜欢 pySpark 而不是 Spark。Python 更容易理解,例如,已经被 Airflow 使用。我认为在 Spark 中使用 Python 之外的另一种语言的唯一有效理由是:“用 RDD 做非常高级的事情”和“重用一些公司已经用 Java 编写的代码,而不必重新实现它”。

非常感谢 XKCD 网络漫画公司明确允许我在本文中使用他们的漫画:【https://xkcd.com/1409/
代码组织: dbt 展示了组织 SQL 转换管道的正确方式(我知道这一点:从 2014 年到 2017 年,我开发了一个工具,它做了与 dbt 相同的事情,但针对 Apache Hive )。据我所知,目前还没有工具能为 pySpark 做同样的事情(dbt 确实支持 spark-sql,但没有完整的 pySpark)。这就是为什么我们开发了一个内部工具,我希望有一天可以开源。如果没有这样的工具,很容易回到 dbt 帮助我们停止的那些糟糕的做法。
表现力:我热爱 SQL,甚至超过 BigQuery,但是我只是不能只用 SQL 做我需要的一切。此外,我认为使用 Jinja 模板不是正确的解决方案:正如 Maxime Beauchemin 在他的博客文章中所说,这将导致大量的模板化 SQL 和 YAML。就我个人而言,我认为这与第一批调度器中的错误是一样的: config-as-code 有许多限制,而 Airflow 通过证明 code-as-config (感谢 Python)工作得更好而扭转了局面。是的,JINJA 确实解决了一些 config-as-code、的刚性问题,但是我仍然觉得 JINJA 相当沉重和冗长,可读性不强,而且对 JINJA 代码进行单元测试似乎相当乏味。

得到 xkcd 的明确许可:https://xkcd.com/1409/
SQL 的限制:和 Maxime Beauchemin 一样,我相信(至少,我希望)当 BigQuery、Snowflake 或 Redshift 提供类似于 pySpark 提供的 DataFrame API 时,事情会变得更好。雪花实际上已经这么做了:他们最近发布了 Snowpark,其数据帧 API 显然是借鉴了 Spark 的 one 。我最近开始为 big queryPOC 一个 DataFrame API,以展示使用这样的东西可以做更多的事情(我承认,其中一些已经可以用 JINJA 宏来完成,但我觉得这种方式不够优雅,也更难维护)。我将在下一篇文章中更多地讨论这个 POC。
UDF:SQL 的另一个重要限制:有些逻辑用 UDF 实现要比用 SQL 代码容易得多。在 BigQuery 中,UDF 必须用 SQL 或 Javascript(!!!).Idem 带雪花,也支持 Java。去告诉一个只懂 Python 的数据工程师/分析师/科学家写一个 Javascript UDF 吧……PySpark 允许我们用 Python 写 UDF,我等不及 BigQuery 也允许了。

经 xkcd 明确许可:https://xkcd.com/1409/
Extract-Load (E/L) :我对大量的人似乎使用定制气流操作器来执行 E/L 任务感到非常惊讶,而不是 Spark。我认为这是 Spark 最大的优势之一:它有大量的连接器来读取/写入任何东西。它还可以毫不费力地对 json 和 xml 执行自动模式检测。并且,正如 Ari Bajo 所指出的,最好通过一个中央状态(Spark)并拥有 O(n) 连接器,而不是为每个源-目的地对编写 O(n ) 连接器。Spark 可以做到所有这些,我认为它的运行成本比 Fivetran 要低得多(尽管我必须说开源工具 Airbyte 可能是一个很好的选择)。Spark 的设置成本可能会更高,但是一旦支付了费用,复制和添加新的源/目的地并不需要很长时间。另一个优势是:Spark 可以同时完成 ETL 和反向 ETL。诚然,它确实需要开发人员,而非开发人员可以使用图形界面。但是我也有一种感觉,一个没有 GUI 的开发人员不太能够调查和解决潜在的问题(但是我可能错了)。
实时:在我的团队中,我们开始将 pySpark 用于简单的实时案例(将原始数据摄取到 BigQuery 中),但我对这个主题还不够了解,无法将其与其他替代方案(如 lambda 函数)进行比较。我们会注意到,由于它的微批量模式,Spark 允许我们非常容易地拥有恰好一次的保证。

得到 xkcd 的明确许可:https://xkcd.com/1409/
结论
用几句话总结一下,我认为这些考虑大多围绕一个中心点,这是 SQL 最大的优点,也是它最大的缺点:简单。正是由于 SQL 的简单性,像 BigQuery 和 Snowflake 这样的平台才如此易于使用,并被广泛采用,同时降低了被供应商锁定的风险。相反,这种简单性也是它最大的缺点,因为 SQL 很快就达到了极限,开发最佳实践更难应用,也更不普及。多亏了 dbt 和 JINJA,这些缺点中的一些可以得到缓解,但我相信行业将不得不走得更远,用 DataFrames 或其他类似的 API 来帮助数据技术人员编写更通用和高级的转换,并更好地满足不断增长的数据需求。

得到 xkcd 的明确许可:https://xkcd.com/1409/
在我的团队中,一个主要的挑战是我们倾向于在 pySpark 和 BigQuery 之间交替使用,以从每种工具的优势中获益。这使得数据血统更加困难,因为 dbt 只让我们可视化 BigQuery 部分,而我们的内部“用于 pySpark 的 dbt”工具只让我们看到 pySpark 部分。从长远来看,我希望 BigQuery 能够添加与 pySpark (DataFrame API 和 Python UDFs)相比所缺少的特性,这将允许我们有一天将当前的 pySpark 转换迁移到 BigQuery。唯一留在 pySpark 这边的是 E/L 和实时数据处理。

得到 xkcd 的明确许可:https://xkcd.com/1409/
(此贴有法文版 此处 )
现代 LLMs:NLG 山,龙猫,地鼠和更多
原文:https://towardsdatascience.com/modern-llms-mt-nlg-chinchilla-gopher-and-more-5053cfea8178
更大的模型能解决我们所有的问题吗?

明白了吗?是一只地鼠!(照片由卢克亚什·vaňátko在 Unsplash 上拍摄)
在这个概述中,我们将看看 GPT 协议 3 之后的大型语言模型(LLM)的产生[7]。GPT-3 令人难以置信的结果清楚地表明,增加语言模型(LMs)的规模是非常有益的。然而,问题是,这种趋势什么时候会趋于平稳?随着参数数量的不断增加,模型性能是否会继续呈指数级提高?**
这个问题很快被后续的 LLM 工作所回答,这些工作探索了包含多达 5300 亿个参数的模型。虽然在这项工作中有许多有趣的发现,但主要的收获是,仅仅把模型做得更大是不够的。超过某一点后,LLM 的性能开始趋于平稳(也就是说,并不比 GPT-3 好多少)。
然而,我们可以使用其他技术来提高 LLM 的性能。首先,我们可以不再增加模型的大小,而是更多地关注预训练语料库。如果这个预训练语料库的大小和质量增加,它往往有利于模型性能。简而言之,让 LLMs 变得更好似乎是增加模型和数据规模的共同努力。

(来自[1]和[4])
语言建模入门
语言建模背后的基本概念已经在我最近写的关于 LLM 的前几篇文章中广泛讨论过了:
概念。简单地说,LMs(和 LLM)是深度神经网络,专门解决一个单一的任务:预测文本序列中的下一个单词。尽管这个过程比这个过程多了一点和,但一般概念真的就是这么简单。

未标记文本上的语言模型预训练(由作者创建)
为了训练一个 LM,我们必须首先获得大量的未标记文本。然后,我们可以通过迭代以下步骤来执行自我监督学习:
- 一些文本样本
- 试着预测下一个单词
- 基于“正确的”下一个单词更新你的模型
这个过程称为语言模型预训练,如上图所示。有趣的是,这个训练过程允许(L)LM 从大量数据中学习,因为我们学习的文本数据不需要人工注释!我们可以从网上下载大量的原始文本数据,用于预训练。从如此庞大的语料库中学习对于发展对语言的多样化的、全面的理解是非常有益的。
创建基础模型。如果我们预先训练一个 LLM(顺便说一下,这是一个昂贵的过程),我们就可以进入一个神经网络,给定一些文本,它可以准确预测下一个单词。最初,这可能看起来没什么用,但是这些 LLM 拥有令人难以置信的自然语言知识基础。
为了理解为什么会这样,我们需要首先认识到预测文本序列中的下一个单词是一个困难的问题——即使作为人来做这件事也不是小事!准确地选择下一个单词实际上需要模型对语言进行深入细致的理解。这种理解非常有益,因为它可以重新用于解决其他类型的语言任务!
换句话说,这些 LLM 是一种基础模型——一个通用术语,指的是可以重新用于解决各种任务的大型神经网络。这些基础模型的学习过程分为两个阶段:预培训和情境学习。预训练过程如上所述,而上下文学习指的是使用通用 LLM 来解决更具体的下游任务的过程。
等等……我们该怎么做?我们可以通过多种方式重新利用 LLM 来解决下游任务。目前,很多研究都在研究零推理和少镜头推理技术来解决 LLMs 的各种任务。在高层次上,这些技术通过将下游任务重构为下一个单词预测问题来解决它。例如,我们可以将以下提示传递给 LLM:
- “总结以下文档:
- 把这句话翻译成法语
然后,使用下一个单词预测,我们可以为这个提示生成一个文本响应,它(理想情况下)应该回答我们想要的问题。我们通过提示/请求 LLM 为我们解决任务来解决任务!以上提示都是零拍学习的例子。我们还可以执行少量学习,其中我们在提示中额外提供了几个正确输出的例子;下面是一个例子。

(摘自[7])
GPT-3 [7]推广了 LLM 的少量学习,它表明使用这种技术一定规模的语言模型表现相对较好。然而,这种性能仍然落后于通过监督学习或微调解决下游任务的基线技术。

(摘自[7])
我们可以只对 LLM 进行微调(即,基于对输入和期望输出的训练来更新模型的参数),而不是执行少量推理;见上图。这种方法执行得相当好,但是它也有一些缺点:
- 需要进一步培训(可能很贵)
- 每个下游任务都需要一个专门的模型
用一个模型就能准确解决所有任务,那就太好了。事实上,这是基金会模型的最终目标。然而,就目前而言,似乎有必要进行微调以实现最佳性能。尽管如此,我们将在这个概述中看到,大多数当前的研究使用零/少镜头推理来测量 LLM 性能。
这一概述
到目前为止,希望 LLM 的概念和它们是如何工作的已经比较清楚了。在这个概述中,我们将重点关注 (i) 训练更大的 LLM 和 (ii) 使用更大的数据集进行预训练。现代 LLM 基于纯解码器变压器架构。这些模型的大小可以通过增加更多的层或增加每层的宽度来增加。为了获得更多的数据来训练这些模型,我们通常只是使用像 Common Crawl 这样的工具从网络上抓取文本,或者使用像 Pile dataset [5]这样的大型文本数据源。
我们将研究四篇探索现代 LLM 的论文,并试图改进 GPT-3 的结果。训练更大模型的最初尝试在某种程度上没有达到 GPT-3 设定的期望— 我们获得的性能改进没有我们希望的那么好。后来的工作发现,让 LLM 成功不仅仅是简单地扩大规模——我们还必须提高预训练语料库的规模和质量。这导致了更有效的 LLM 的提议,其通过在更多数据上训练更小的模型来实现显著的结果。我们来看看吧!
用 DeepSpeed 和威震天训练威震天——图灵 NLG 530B,大规模生成语言模型 [1]
尽管 LLM 已经相当大了(例如,GPT-3 [7]包含 1750 亿个参数),MT-NLG 530B,在[1]中提出,把它带到了一个新的水平。Nvidia 和微软的合作,这项工作训练了具有 5300 亿个参数的 LLM。像 GPT-3 这样的模型由于它们的尺寸已经很难训练,所以训练 MT-NLG 530 b——另一个解码器专用变压器,参数增加了>3 倍,如下图所示——显然相当困难。事实上,MT-NLG 需要一个专用的计算集群和几项分布式培训创新,以使培训易于处理且高效。

(来自[1])
使用数据、模型/管道和张量并行性的组合来训练模型;参见这里对这些概念的快速讨论。基于 Megatron-LM 库的分布式训练方法是这项工作的主要贡献——简单地设计一个能够训练具有 5300 亿个参数的 LLM 的系统是非常重要的。
当我们执行分布式训练时,有两种基本方法可以向训练过程添加更多的 GPU:
- 为您的计算机添加更多 GPU
- 添加更多的机器,每个机器都有 GPU
MT-NLG 的培训程序针对每种情况采用不同的分布式培训方法。在每台单独的机器中,我们使用张量切片(tensor-slicing)将训练分配给不同的 GPU,张量切片是一种模型并行的形式,它将单个层分成多个不相交的参数“切片”,每个切片都分配给单独的 GPU。然后,流水线并行被用于在不同的机器或计算节点之间分配训练。要了解有关这些技术的更多信息,请查看以下链接:
这种混合分布式训练方法是必需的,因为当在不同的机器上执行时,通信要昂贵得多。因为同一台机器上的 GPU 之间的通信非常快,张量切片在这种情况下工作得很好。然而,不同机器之间增加的通信时间使得在跨几个计算节点进行分布式训练时,流水线并行成为更有效的选择。
MT-NLG 有 105 层,隐藏维度 20K,每层有 128 个注意头。该模型在使用普通爬行和桩数据集【5】导出的大型文本语料库上训练。类似于先前的工作,执行大量的去重复和匹配,以从预训练语料库中移除副本和下游训练或测试数据。
执行这些过滤程序是因为我们不想“夸大”模型的测试性能。如果来自某个下游数据集的测试数据存在于预训练语料库中,那么我们的模型将通过简单地记忆数据来容易地解决这个任务。但是,这并没有真正反映模型的概括能力。

(来自[1])
经过预训练后,MT-NLG 类似于 GPT-3 进行评估,在大量不同的基准上使用任务不可知的零、一和少量推理;见上文。当评估这个庞大的模型时,我们看到的结果与 GPT-3 非常相似,但稍好一些。例如,MT-NLG 略胜一筹(即<1%) than GPT-3 on language modeling, and similar results are seen on common sense reasoning tasks (i.e., maximum of ~3% improvement across all tasks and shots).
On word-sense disambiguation, natural language inference, and reading comprehension tasks, MT-NLG improves upon the performance of GPT-3 more significantly. Thus, we see that 增加模型比例可能会使某些任务比其他任务受益更多)。例如,MT-NLG 能够将 GPT-3 的零命中率词义消歧从 0%提高到 48.59%。然而,我们应该记住,在所有情况下,MT-NLG 的结果仍然低于监督基线。简单地增加模型规模(至少目前是这样)不足以使 LLM 达到人类水平的任务无关性能。
总的来说,MT-NLG 的贡献主要集中在工程方面。MT-NLG 改进了 GPT-3 的性能,但幅度不大。然而,就训练和利用模型而言,训练这种规模的模型确实带来了显著增加的复杂性。仅仅存储这种规模的模型的优化器的状态在单个 GPU 上是不可能的!随着这些 LLM 变得越来越大,驱动它们的核心概念保持不变,但是处理这种大型模型的工程挑战变得越来越困难。
缩放语言模型:方法、分析和来自训练 Gopher 的洞察力 [2]

(来自[2])
继续训练甚至比 GPT-3 [7]更大的 LLM 的趋势,[2]的作者简单地扩大了用于 LLM 预训练的参数数量、数据集大小和计算量。他们训练了许多 LLM,其大小从 4400 万到 2800 亿个参数。然后,通过分析这些模型在 152 个不同任务的大规模套件中的表现,对它们进行比较。上图中详细描述的评估基准比之前的工作更全面(例如,之前的工作只研究了这些任务中的 120 个)。

(来自[2])
为了预先训练他们的 LLM,作者构建了一个新的海量文本语料库,其中包含超过 2.3 万亿个标记;见上表。相比之下,用于训练 GPT-3 的基于 CommonCrawl 的语料库包含不到 500 个单词。因此,在[2]中用于预训练的数据集比我们在以前的工作中看到的任何语料库都大得多。
[2]中使用的确切训练策略取决于被训练的 LLM 的大小,但是作者采用了数据、模型和管道并行训练的不同组合来最大化预训练吞吐量。除了使用相对位置编码和 RMSNorm [8](而不是 LayerNorm )之外,LLM 使用的底层架构与 GPT-3 相同。

(来自[2])
与 MT-NLG [1]不同,[2]中的结果向我们展示了使用更大的 LLM 可以获得显著的好处。然而,为了实现这些性能优势,大型 LLM 必须在更大、更高质量的语料库上进行预训练。当评估[2]中最大的 LLMs 一个名为 Gopher 的 2800 亿参数模型——时,我们看到 152 个考虑的任务中有 81%有性能改进。上图提供了这些性能改进的更详细的概述。
在语言建模任务上,Gopher 的性能与 GPT-3 相似。在其他任务上,我们可以看到,最大的绩效改善发生在知识密集型任务上,如阅读理解和事实核对;见下文。

(来自[2])
尽管 Gopher 在事实检查方面优于最先进的基线,但我们应该注意到,对于阅读理解,LLM 的少数镜头性能再次远远落后于人类和监督学习的性能。简单地缩放模型和语料库规模(不幸的是)不足以让基础模型超越特定任务技术的性能——监督学习仍然占主导地位。
需要推理的任务(如数学、逻辑推理、常识推理等。),我们看到较大的模型没有提供任何好处。事实上,Gopher 在这类任务上甚至优于以前的 LLM(以及一些正在考虑的较小的 LLM)。与基于知识的任务相比,推理密集型任务似乎从模型规模中获益更少。

(来自[2])
[2]中的作者广泛研究了 Gopher 是否倾向于有偏见或有毒的行为。有趣的是,这样的结果向我们揭示,当提供给模型的提示本身有毒时,Gopher 经常发出有毒的文本;见上文。此外,这种影响随着规模而增加,其中较大的模型对毒性提示的反应更大。

(来自[2])
Gopher 还对某些社会群体的少数族裔有偏见,详见下图。然而,尽管有这样的发现,作者强调目前评估 LLM 偏倚和公平性的方法是有限的。相对于现有的社会规范,分析和改善 LLM 的行为是一个活跃和流行的研究领域。
侏罗纪-1:技术细节和评估 [3]
除了增加类似 GPT-3 的 LLM 的尺寸,我们可以考虑不同形状的类似尺寸的模型。例如,研究人员在[3]中研究了一个名为 Jurassic-1 的 1780 亿参数解码器专用 LLM。该模型与 GPT-3 非常相似,但它略大,层数更少(即 76 层而不是 96 层)。考虑到层的减少,每层的宽度(即,每个自关注头的隐藏尺寸)增加,产生在参数数量方面类似大小的模型。

(摘自[6])
Jurassic-1 的改进架构遵循了之前研究 LLM 深度和宽度之间权衡的工作[6]的建议。这项工作检查了不同深度的 LLM,并分析了模型深度和参数总数方面的性能。有趣的是,我们在这个分析中看到,LLM 的最佳深度随着它的大小而变化。只有当模型足够大,并且可以根据参数总数准确预测最佳深度时,使用更深的 LLM 才有意义;见上图。
[3]中的作者根据[6]中的经验预测选择侏罗纪-1 的深度;下面是该模型与 GPT-3 的结构比较。

(来自[3])
[3]中的作者还探索了使用多词标记,并增加了底层标记器的词汇量。这一变化极大地提高了标记的效率,意味着给定的句子或文本片段可以使用更少的标记进行编码。这里的基本思想是,减少模型的输入令牌数可以提高其效率——我们只是在处理一个更短的输入序列!要了解更多关于记号赋予器的信息,请点击这里的文章。
另外,更好的令牌效率意味着我们可以在提示中提供更多的上下文示例!这是因为像 Jurassic-1 和 GPT-3 这样的模型有一个最大的上下文长度,或者可以包含在输入中的标记数。如果我们有更好的令牌效率,我们可以将更多的数据放入相同的上下文长度中。下图说明了利用更多上下文相关示例的影响。

(摘自[3])
在执行文本生成时,令牌效率的提高带来了最大的不同——提高了 23%,文本生成需要以连续的方式单独生成每个令牌。训练和批量推理(例如,对一批示例运行一次正向传递)也分别加快了 1.5%和 7%。
侏罗纪 1 号的训练程序与 GPT 3 号非常接近。正如我们在之前的工作中所看到的,用于训练模型的优化器的状态(即,在优化过程中使用的所有模型参数及其相关统计数据)必须跨多个 GPU 和计算节点划分,因为由于模型的大小,该状态太大而不能存储在中央位置。

(来自[3])
使用随出版物一起发布的公开测试套件对模型进行评估。这些设置中的大多数都是从 GPT-3 的评估中采用的,我们在上面显示的结果中看到,侏罗纪-1 在大多数情况下与 GPT-3 的表现相似。然而,[3]中的作者大多考虑零炮情况下的评估,声称设置比少炮评估更简单和确定。
总的来说,这项工作的主要价值似乎在于它对底层记号赋予器的修改。我们似乎并没有从训练一个肤浅但广泛的模型中获得任何巨大的好处。虽然使用更大的令牌词汇表会增加模型的内存使用量(即,因为我们必须存储更大的嵌入层的所有参数),但提高的令牌效率确实很有价值,因为它允许使用更多的上下文示例,并在几个方面提高了 LLM 效率。
培训计算优化 LLMs [4]
为了最大化 LLM 的性能,之前对缩放趋势的分析[9]表明,我们应该尽可能地扩大模型的大小(即,非嵌入参数的数量),同时缩小基础预训练数据集的大小(特别是通过 N^{0.74}的因子,其中 n 是我们模型中的参数数量)。这种对大规模 LMs 行为的分析启发了后来的工作,如 GPT-3,它在任务无关的少数镜头性能方面实现了突破性的改进。
由于 GPT-3 令人难以置信的效用,最近的研究,如我们在本帖中看到的工作,已经探索了更大的 LLM(例如,在 NLG 山高达 530B 的参数!).这些模型倾向于遵循[9]的建议——它们使用一个非常大的模型,但不会将底层数据集的大小增加到类似的程度。
有趣的是,[4]中的研究发现,这种扩展 LLM 的方法是次优的。相反,为了以计算优化的方式训练 LLM(即,在固定量的计算成本下实现最大性能),我们在[4]中看到,LLM 和底层预训练语料库的规模应该相等地增加。简单地说,这意味着,相对于现有的工作,我们应该在更多的数据上训练 LLMs 见下文。

(摘自[4])
直观上,这种方法是有意义的,正如我们在 Gopher 出版物[2]中看到的那样,与 MT-NLG [1]等主要关注模型规模的模型相比,使用更大的预训练语料库和更大的模型可以产生更明显的性能优势。
为了使这种缩放过程更加具体,[4]考虑了不同大小的 LLM(即,从 7000 万到 160 亿个参数)N 和用于训练它们的令牌数 d。这里,我们应该记住,由于预训练数据的原始量,现代 LLM 是针对< 1 个时期(即,没有单个示例出现两次)进行训练的。在预训练期间观察到的记号的数量等于数据集的大小。
通过使用 N 和 D 的许多不同组合来训练 LLM,我们可以遵循类似于[8]的方法,尝试发现一个幂律,该幂律可以预测 LLM 的测试损耗,作为 N 和 D 的函数。在[4]中,作者训练了 400 多个 LLM,并做到了这一点。通过对这些模型的分析,我们可以找出 N 和 D 的什么组合最适合不同的计算预算。

(摘自[4])
有趣的是,我们在这些实验中看到,训练的最佳方法与训练令牌的数量相等地缩放模型的大小。这与之前的分析相矛盾,之前的分析认为数据集的比例应小于模型大小[9]。然而,作者通过三种不同的分析方法验证了这些发现,这三种方法通过不同的技术研究结垢行为(参见[4]中的第 3.1-3.3 节)。所有这些研究都预测数据和模型大小应该同等缩放;见上文。
总的来说,这些发现告诉我们,现代 LLM 是 (i) 超大的和 (ii) 没有足够的数据训练。例如,[4]中的作者预测,具有与 Gopher 相同数量参数的模型应该用比>多 20 倍的数据来训练,以达到计算优化。因此,如果我们想要正确地训练 LLM,我们将需要更多的数据!
“预计需要的训练数据量远远超过目前用于训练大型模型的数据量,这凸显了数据集收集的重要性,以及允许模型缩放的工程改进。”
—来自[4]
为了验证这些发现,作者训练了一个 700 亿参数的 LLM,称为 Chinchilla。与以前的模型相比,龙猫更小,但它在预训练期间观察到更多的数据;见下文。数据集和评估策略与 Gopher 出版物[2]相同。

(摘自[4])
在对 Chinchilla 的评估中,我们看到该模型优于 Gopher 之类的大型 LLM,尽管包含的参数少了 4 倍!

(摘自[4])
该模型在大范围的任务中进行评估,并与其他几种现代 LLM 进行比较;见下文。在所有情况下,它的性能相当于或优于其他最先进的 LLM,表明模型比例可能没有我们最初认为的那么重要-预训练数据集的大小也很重要!

(摘自[4])
外卖食品
随着 GPT 协议 3 的提出,我们看到了通过增大 LLM 可以获得很多好处。然而,我们在这个概述中提出的问题是,模型比例是否是我们所有问题的答案。总的来说,我们已经知道,使 LLM 更大并不是实现改进的任务无关性能的唯一必要组成部分。另外,它也有一些缺点。主要的要点总结如下。
更大的 LLMs =更多的工程工作。随着 LLM 尺寸的增长,它们变得越来越难以处理。我们已经在 GPT-3 中看到了这一点的证明——训练一个具有 1750 亿个参数的模型需要不同分布式训练技术的组合,这是一项重大的工程壮举。对于更大的模型,如 MT-NLG,训练过程变得更加复杂。最近的努力已经降低了训练 LLM 的成本,但是训练和部署这些模型所需的工程努力仍然是巨大的。
数据很重要。扩大 LLM 的规模最初似乎是实现更好性能的首选方法。GPT 3 太棒了,为什么不把它做得更大一点呢?然而,一旦我们看到随着模型变大,性能平台有所改善,我们就知道对更多数据进行训练也是非常重要的。LLM 性能的最大改进(例如,Gopher 和 Chinchilla [2,4])是通过模型和数据集缩放的组合实现的(比例大致相等)。
深度还是宽度?这是一个不太重要的发现,但似乎当前的研究[6]告诉我们,go-to LLM 架构可能比它们需要的更深入一些。在某些情况下,可能更有意义的做法是使它们变浅一点,并将保存的参数投资到每层的宽度中。
监督下的表演至高无上。尽管我们观察到 LLM 带来了难以置信的与任务无关的性能优势,但我们必须正确看待这些结果。我们从这篇综述的研究中看到,这些技术仍然达不到监督训练的效果。在很多情况下,我们仍然可以通过特定于任务的微调来获得显著的性能优势。尽管与任务无关的基础模型是一个很好的想法,但在我们能够在实际应用中利用这些模型而不执行任何特定于任务的调整之前,可能还需要一段时间。如果能让我们的表现好得多,为什么不稍微调整一下呢?
结束语
非常感谢你阅读这篇文章。如果你喜欢它,请在 twitter 上关注我,或者订阅我的深度(学习)焦点时事通讯,在那里我挑选了一个关于深度学习研究的单个两周一次的主题,提供了对相关背景信息的理解,然后概述了一些关于该主题的流行论文。我是 Cameron R. Wolfe ,ale gion的研究科学家,莱斯大学的博士生,研究深度学习的经验和理论基础。你也可以看看我在 medium 上的其他著述!
参考书目
[1] Smith,Shaden 等,“利用 deepspeed 和威震天训练威震天-图灵 nlg 530b,一个大规模生成语言模型。”arXiv 预印本 arXiv:2201.11990 (2022)。
[2] Rae,Jack W .等人,“缩放语言模型:方法、分析和来自训练地鼠的洞察力。”arXiv 预印本 arXiv:2112.11446 (2021)。
[3] Lieber,o .等人,“侏罗纪-1:技术细节和评估,白皮书,AI21 实验室,2021 年。”
[4] Hoffmann,Jordan 等人,“训练计算机优化的大型语言模型”arXiv 预印本 arXiv:2203.15556 (2022)。
[5]高,李奥等,“一个 800gb 的多元文本语言模型集”arXiv 预印本 arXiv:2101.00027 (2020)。
[6] Levine,Yoav 等人,“自我注意深度效率的限制”神经信息处理系统进展 33(2020):22640–22651。
[7]布朗、汤姆等人,“语言模型是一次性学习者。”神经信息处理系统进展 33(2020):1877–1901。
[8]张和森里奇。均方根层归一化。arXiv 预印本 arXiv:1910.07467,2019。
[9]卡普兰,贾里德等人,“神经语言模型的标度律。”arXiv 预印本 arXiv:2001.08361 (2020)。
现代与否:什么是数据堆栈?
原文:https://towardsdatascience.com/modern-or-not-what-is-a-data-stack-e6e09e74ae7f
数据架构
“现代数据堆栈”简单地解释为一个堆栈

不是一个数据堆栈——由 Unsplash 上的 Andrew Draper 拍摄
去年,当我发现自己在寻找一份比我当时的职位(运营数据分析师)活动范围更广的工作时,我开始对公司的数据架构主题感兴趣。特别是,我想更好地理解如何在我自己的数据分析范围之外构建和管理数据。
这就是我如何进入(仍在进行中)关于“现代数据堆栈”的讨论:
这是什么?
我们应该尝试“捆绑”还是“拆分”?
这些融资数百万美元的数据公司都是些什么人?
围绕这个话题还有很多我并不总是完全理解的问题。
对于我们数据分析师来说,我们角色的主要部分是将业务用户的需求转化为可操作的数据,以帮助他们做出更好的决策。为此,我们的日常工具侧重于数据分析(例如任何 SQL 控制台,如 Google BigQuery 的控制台)和数据可视化(例如 Google DataStudio)。
在公司“数据光谱”的另一端,数据的原始提取及其转换使数据可供数据分析师使用更多是数据工程师的责任。至少我以前是这么看的。

拓宽我的“数据架构”视野—来自 Pexels 的 Nurlan Tortbayev 拍摄的照片
为了从更广的角度了解什么是数据堆栈,我进行了自己的研究。在这篇文章中,我想与你分享这次反思的结果。这里的目标是保持简单,因此当您想要开始探索现代数据堆栈的概念时,以下解释应该被视为“从哪里开始”的指南。
从个人的角度来看,我经常回到下面的两个图表来阐述一个特定的观点,或者就数据架构这个广泛的主题与同事进行交流:
- 现代数据堆栈的核心和基础模块
- 现代数据堆栈中的公司示例
什么是现代数据堆栈?
首先,业务环境中所谓的“数据堆栈”是多种技术的组合,允许公司利用数据进行决策。
添加形容词“现代”是指近年来的发展,特别是:
- 云平台的崛起提供更便宜、更灵活的定价方案来存储大量数据
- 新数据公司的出现,它们在公司数据堆栈的特定部分提供更高水平的专业知识
这些趋势意味着通常被理解为术语“现代数据堆栈”的两个主要变化。首先,针对大量数据的更便宜、更高效的存储解决方案倾向于从 ETL(提取>转换>加载)向 ELT(提取>加载>转换)转变。
其次,“一体化”解决方案往往比组合几种数据工具具有更差的性价比,因为与这些新兴数据公司相比,它们具有更通用的方法。这意味着,如果企业选择不采用“一体化”解决方案,就需要将几种数据技术结合起来。
现代数据堆栈由哪些块组成?
要在公司执行所有必要的数据管理任务,任何数据堆栈都必须包括以下模块:

图 1 —现代数据堆栈的核心和基础模块
让我们从核心数据堆栈的 4 个组件开始:
1.提取
要开始,必须从各种来源提取数据。例如,这可以通过用 Python 编写的脚本来完成,或者通过服务提供商提供的本地连接器来完成。最合适的解决方案将取决于所做的技术选择,但也取决于原始数据的提取方式:通过 API(可能是最简单的方式),通过安全文件传输协议(SFTP),通过 web 报废等。
2.负荷
提取的数据应该存储在适当的基础设施中。在这个模块中,公司可以使用数据湖和/或数据仓库在下一步之前加载数据。有趣的是,一些提供商,如 Databricks 或 Snowflake,倾向于提供第三种创新的数据平台,结合数据湖(非结构化和大数据)和数据仓库(结构化和有管理的数据)的优势。
3.改变
一旦数据在一个中心位置可用(对于更复杂的数据体系结构,可能有几个位置),就需要对其进行转换,然后才能用于进一步的分析。这是通过几个层处理数据的地方,有两个主要目标:使数据干净(例如,避免错误的值,标准化格式)和使数据可用于步骤 4(例如,通过合并来自多个来源的数据,将其聚合)。
4.杠杆作用
如果不使用,数据会是什么?这是产生数据输出的地方,因此对于技术团队之外的利益相关者来说,这是数据堆栈最可见的部分。数据输出可以是报告和交互式仪表板,也可以是临时分析、数据发现工具等。
这四个数据块构成了现代数据堆栈的核心元素。然而,如果数据堆栈中缺少了的两个基本元素,那么它们将是徒劳的:
A.商店
在“加载”和“转换”模块中,我们假设存储数据和将转换应用于数据的基础设施进展顺利。只有当存储配置正确,并且在容量、刷新频率、使用类型等方面适应整个数据堆栈的需求时,才会出现这种情况。
B.管理
在构建数据堆栈时,我们倾向于关注构建部分,而忘记了维护部分——许多应用领域都是如此。然而,数据治理工具和最佳实践是保持上述所有数据块的数据质量的关键,也是确保数据从其来源到最终目的地得到正确处理的关键。
现在怎么办?自带设备:构建您自己的数据堆栈!
我不想讨论哪种工具最适合每一个用例,我想展示一些与每个模块相对应的技术和数据公司的例子。
当您进行自己的研究并对解决方案提供商进行基准测试时,这个视图应该允许您更好地对他们进行分类。请随意补充以下模板,并根据您自己的情况进行调整:

图 2—现代数据堆栈中的公司示例
虽然所有数据块都是构成您公司的数据堆栈所必需的,但是必须做出一些权衡来选择最适合的技术组合。为了帮助您做到这一点,在绘制您的目标数据堆栈时,您应该问自己一些关键问题,当然还有相关的利益相关者:
- 您希望将光标放在“全部在一个地方”与“每个特定任务一个工具”之间的哪个位置?
- 您希望将光标放在“自己做”(可能使用开源技术)与“将实现委托给提供商”(可能涉及供应商锁定)之间的什么位置?
- 就可用性和能力而言,你的内部人员能力是什么?
- 你有多少预算?
- 你在时间方面的约束是什么?
结论
这是对现代数据堆栈的基本概述。正如我之前提到的,这不是什么新东西,而是这个广泛主题的简化视图。如果您想了解更多,我邀请您向下滚动到“参考资料”部分。
在介绍现代数据堆栈的概念并与同事讨论时,我发现这些自制的图表对自己有宝贵的帮助,所以我希望它们可以为其他人提供很好的介绍。不要犹豫,分享你的想法!
参考
- 现代数据基础设施的新兴架构作者马特·博恩施泰因、李昕晢、马丁·卡萨多
- 基线数据栈——超越现代数据栈(第一部分及以后)本·罗戈扬
- Danilo Drobac的现代数据堆栈
- …以及在谷歌图片和 LinkedIn 上广泛搜索“现代数据堆栈”
你喜欢读这篇文章吗? 成为会员 加入一个不断成长的充满好奇心的社区吧!
https://marie-lefevre.medium.com/membership
基于神经网络的现代推荐系统
原文:https://towardsdatascience.com/modern-recommendation-systems-with-neural-networks-3cc06a6ded2c

作者图片
使用 Python 和 TensorFlow 构建混合模型
摘要
在本文中,我将展示如何使用 Python 和 TensorFlow 构建具有神经网络的现代推荐系统。

亚历山大·沙托夫在 Unsplash 上拍摄的照片
推荐系统 是预测用户对多个产品偏好的模型。它们被用于各种领域,如视频和音乐服务、电子商务和社交媒体平台。
最常见的方法利用产品特征(基于内容)、用户相似性(协作过滤)、个人信息(基于知识)。然而,随着神经网络的日益普及,公司已经开始尝试将它们结合在一起的新的混合推荐系统。
在本教程中,我将展示如何使用传统模型,以及如何从头开始构建一个现代推荐系统。我将展示一些有用的 Python 代码,这些代码可以很容易地应用于其他类似的情况(只需复制、粘贴、运行),并通过注释遍历每一行代码,以便您可以复制这个示例(下面是完整代码的链接)。
我将使用由 GroupLens Research 创建的 MovieLens 数据集,该数据集包含数百名用户评价的数千部电影(链接如下)。
https://grouplens.org/datasets/movielens/latest/
特别是,我将经历:
- 设置:导入包、读取数据、预处理
- 冷启动问题
- 使用 tensorflow 和 numpy 的基于内容的方法
- 使用 tensorflow/keras 的传统协同过滤和神经协同过滤
- 具有 tensorflow/keras 的混合(上下文感知)模型
设置
首先,我将导入以下包:
**## for data**
import **pandas** as pd
import **numpy** as np
import **re**
from **datetime** import datetime**## for plotting**
import **matplotlib**.pyplot as plt
import **seaborn** as sns**## for machine learning**
from **sklearn** import metrics, preprocessing**## for deep learning**
from **tensorflow**.keras import models, layers, utils **#(2.6.0)**
然后我将读取数据,包括产品数据(本例中为电影)和用户数据。
dtf_products = pd.read_excel("data_movies.xlsx", sheet_name="products")

作者图片
在 product 表中,每行代表一个项目,右边的两列包含它的特性,这些特性是静态的(您可以将其视为电影元数据)。让我们读取用户数据:
dtf_users = pd.read_excel("data_movies.xlsx", sheet_name="users").head(10000)

作者图片
这个表格的每一行都是一对用户-产品,显示用户对产品的评分,这是目标变量。显然,并不是每个用户都看过所有的产品。事实上,这就是为什么我们需要推荐系统。他们必须预测用户会给新产品什么样的评价,如果预测的评价是高/积极的,那么就推荐它。此外,这里还有一些关于目标变量上下文的信息(当用户给出评级时)。
让我们做一些数据清理和特征工程来更好地理解我们拥有什么以及我们如何使用它。
**# Products**
dtf_products = dtf_products[~dtf_products["genres"].isna()]dtf_products["product"] = range(0,len(dtf_products))dtf_products["name"] = dtf_products["title"].apply(lambda x: re.sub("[\(\[].*?[\)\]]", "", x).strip())dtf_products["date"] = dtf_products["title"].apply(lambda x: int(x.split("(")[-1].replace(")","").strip())
if "(" in x else np.nan)dtf_products["date"] = dtf_products["date"].fillna(9999)
dtf_products["old"] = dtf_products["date"].apply(lambda x: 1 if x < 2000 else 0) **# Users**
dtf_users["user"] = dtf_users["userId"].apply(lambda x: x-1)dtf_users["timestamp"] = dtf_users["timestamp"].apply(lambda x: datetime.fromtimestamp(x))dtf_users["daytime"] = dtf_users["timestamp"].apply(lambda x: 1 if 6<int(x.strftime("%H"))<20 else 0)dtf_users["weekend"] = dtf_users["timestamp"].apply(lambda x: 1 if x.weekday() in [5,6] else 0)dtf_users = dtf_users.merge(dtf_products[["movieId","product"]], how="left")dtf_users = dtf_users.rename(columns={"rating":"y"}) **# Clean**
dtf_products = dtf_products[["product","name","old","genres"]].set_index("product")dtf_users =
dtf_users[["user","product","daytime","weekend","y"]]

作者图片
请注意,我从时间戳列中提取了 2 个上下文变量:白天和周末。我将把它们保存到数据帧中,因为我们以后可能需要它们。
dtf_context = dtf_users[["user","product","daytime","weekend"]]
关于产品,下一步是创建产品-特性矩阵:
tags = [i.split("|") for i in dtf_products["genres"].unique()]
columns = list(set([i for lst in tags for i in lst]))
columns.remove('(no genres listed)')for col in columns:
dtf_products[col] = dtf_products["genres"].apply(lambda x: 1 if col in x else 0)

作者图片
矩阵是稀疏的,因为大多数产品没有所有的功能。让我们把它形象化,以便更好地理解情况。
fig, ax = plt.subplots(figsize=(20,5))
sns.heatmap(dtf_products==0, vmin=0, vmax=1, cbar=False, ax=ax).set_title("Products x Features")
plt.show()

作者图片
对于用户-产品矩阵,稀疏性变得更糟:
tmp = dtf_users.copy()
dtf_users = tmp.pivot_table(index="user", columns="product", values="y")
missing_cols = list(set(dtf_products.index) - set(dtf_users.columns))
for col in missing_cols:
dtf_users[col] = np.nan
dtf_users = dtf_users[sorted(dtf_users.columns)]

作者图片

作者图片
深入模型之前的最后一步是预处理。因为我们将处理神经网络,所以缩放数据总是好的实践。
dtf_users = pd.DataFrame(preprocessing.MinMaxScaler(feature_range=(0.5,1)).fit_transform(dtf_users.values),
columns=dtf_users.columns, index=dtf_users.index)

作者图片
最后,我们将数据分成训练和测试组。我将垂直拆分数据集,这样所有用户都将参加培训和测试,而 80%的产品用于培训,20%用于测试。像这样:

作者图片
split = int(0.8*dtf_users.shape[1])
dtf_train = dtf_users.loc[:, :split-1]
dtf_test = dtf_users.loc[:, split:]
好了,现在我们可以开始了…也许吧。
冷启动
想象一下,拥有一个全新的类似网飞的应用程序,第一个用户订阅。我们需要能够提供建议,而不依赖于用户以前的互动,因为还没有记录。当一个用户(或一个产品)是新的,我们就有了 冷启动问题 。这个系统无法在用户和产品之间建立任何联系,因为它没有足够的数据。
为了解决这个问题,主要的技术是基于知识的方法:例如,询问用户的偏好以创建初始简档,或者使用人口统计信息(即青少年的高中节目和儿童的卡通)。
如果只有几个用户,可以使用基于内容的方法。然后,当我们有足够的评级(即每个用户至少 10 个产品,总用户超过 100 个)时,可以应用更复杂的模型。
基于内容
基于内容的方法 是基于产品内容。例如,如果用户 A 喜欢产品 1、和产品 2 与产品 1 相似,那么用户 A 可能也会喜欢产品 2 。如果两个产品具有相似的特征,则它们是相似的。
简而言之,这种想法是用户实际上对产品的功能而不是产品本身进行评级。换个角度说,如果我喜欢音乐和艺术相关的产品,那是因为我喜欢那些特性(音乐和艺术)。基于此,我们可以估计我有多喜欢具有相同功能的其他产品。这种方法最适合于已知产品数据但不知道用户数据的情况。

作者图片
让我们从数据中挑选一个用户作为我们第一个已经使用了足够多产品的用户的例子,让我们创建训练和测试向量。
**# Select a user**
i = 1
train = dtf_train.iloc[i].to_frame(name="y")
test = dtf_test.iloc[i].to_frame(name="y")**# add all the test products but hide the y**
tmp = test.copy()
tmp["y"] = np.nan
train = train.append(tmp)
现在我们需要估计用户给每个特性的权重。我们有用户-产品向量和产品-特性矩阵。
**# shapes**
usr = train[["y"]].fillna(0).values.T
prd = dtf_products.drop(["name","genres"],axis=1).values
print("Users", usr.shape, " x Products", prd.shape)

通过将这两个对象相乘,我们获得了一个用户特征向量,其中包含了用户给每个特征的估计权重。这些权重应重新应用于产品-功能矩阵,以获得预测评级。
**# usr_ft(users,fatures) = usr(users,products) x prd(products,features)**
usr_ft = np.dot(usr, prd)**# normalize**
weights = usr_ft / usr_ft.sum()**# predicted rating(users,products) = weights(users,fatures) x prd.T(features,products)**
pred = np.dot(weights, prd.T)test = test.merge(pd.DataFrame(pred[0], columns=["yhat"]), how="left", left_index=True, right_index=True).reset_index()
test = test[~test["y"].isna()]
test

作者图片
如你所见,我使用简单的 numpy 开发了这个简单的方法。只使用 raw tensorflow 也可以做到这一点:
import tensorflow as tf**# usr_ft(users,fatures) = usr(users,products) x prd(products,features)**
usr_ft = tf.matmul(usr, prd)**# normalize**
weights = usr_ft / tf.reduce_sum(usr_ft, axis=1, keepdims=True)**# rating(users,products) = weights(users,fatures) x prd.T(features,products)**
pred = tf.matmul(weights, prd.T)
如何评价我们的预测推荐?我通常使用精确度和平均倒数排名(MRR) 。后者是一种统计度量,用于评估任何按正确概率排序的可能响应列表。
def **mean_reciprocal_rank**(y_test, predicted):
score = []
for product in y_test:
mrr = 1 / (list(predicted).index(product) + 1) if product
in predicted else 0
score.append(mrr)
return np.mean(score)
请注意,指标会根据我们推荐的产品数量而变化。因为我们将预测的前 k 个项目与测试集中的项目进行比较,所以顺序也很重要。
print("--- user", i, "---")top = 5
y_test = test.sort_values("y", ascending=False)["product"].values[:top]
print("y_test:", y_test)predicted = test.sort_values("yhat", ascending=False)["product"].values[:top]
print("predicted:", predicted)true_positive = len(list(set(y_test) & set(predicted)))
print("true positive:", true_positive, "("+str(round(true_positive/top*100,1))+"%)")
print("accuracy:", str(round(**metrics**.**accuracy_score**(y_test,predicted)*100,1))+"%")
print("mrr:", **mean_reciprocal_rank**(y_test, predicted))

作者图片
我们得到了 4 个正确的产品,但订单不匹配。这就是为什么准确性和 MRR 低。
**# See predictions details**
test.merge(
dtf_products[["name","old","genres"]], left_on="product",
right_index=True
).sort_values("yhat", ascending=False)

作者图片
协同过滤
https://en.wikipedia.org/wiki/Collaborative_filtering协同过滤是基于相似用户喜欢相似产品的假设。例如,如果用户 A 喜欢产品 1 ,并且用户 B 与用户 A 相似,那么用户 B 可能也会喜欢产品 1 。两个用户喜欢相似的产品,就是相似的。

作者图片
这种方法不需要产品特性就能起作用,相反,它需要来自许多用户的许多评级。继续我们平台的例子,想象我们的第一个用户不再孤单,我们有足够的用户来应用这个模型。
当网飞在 2009 年举办了一场最佳算法的公开比赛,人们提出了几种实现方式时,协同过滤开始流行起来。它们可以分为两类:
- 基于记忆— 用相关度寻找相似用户,余弦相似度,以及聚类。
- 基于模型— 通过应用监督机器学习和矩阵分解来预测用户将如何评价某个产品,矩阵分解将大的用户-产品矩阵分解为两个较小的因子,分别代表用户矩阵和产品矩阵。**
在 Python 中,对用户最友好的包是 surprise ,这是一个简单的库,用于构建和分析具有显式评级数据的推荐系统(类似于 scikit-learn )。它既可以用于基于记忆的方法,也可以用于基于模型的方法。或者,可以使用 tensorflow/keras 为更复杂的基于模型的方法创建嵌入,这正是我要做的。
首先,我们需要以下形式的数据:
train = dtf_train.stack(dropna=True).reset_index().rename(columns={0:"y"})
train.head()

作者的图片(对测试集做同样的事情)
主要思想是利用神经网络的嵌入层来创建用户和产品矩阵。重要的是要理解输入是用户-产品对,输出是评级。当预测一对新的用户-产品时,该模型将在用户嵌入空间中查找用户,在产品空间中查找产品。因此,您需要提前指定用户和产品的总数。
embeddings_size = 50
usr, prd = dtf_users.shape[0], dtf_users.shape[1] **# Users (1,embedding_size)**
xusers_in = layers.Input(name="xusers_in", shape=(1,))xusers_emb = layers.Embedding(name="xusers_emb", input_dim=usr, output_dim=embeddings_size)(xusers_in)xusers = layers.Reshape(name='xusers', target_shape=(embeddings_size,))(xusers_emb) **# Products (1,embedding_size)**
xproducts_in = layers.Input(name="xproducts_in", shape=(1,))xproducts_emb = layers.Embedding(name="xproducts_emb", input_dim=prd, output_dim=embeddings_size)(xproducts_in)xproducts = layers.Reshape(name='xproducts', target_shape=(embeddings_size,))(xproducts_emb) **# Product (1)**
xx = layers.Dot(name='xx', normalize=True, axes=1)([xusers, xproducts]) **# Predict ratings (1)**
y_out = layers.Dense(name="y_out", units=1, activation='linear')(xx) **# Compile**
model = models.Model(inputs=[xusers_in,xproducts_in], outputs=y_out, name="CollaborativeFiltering")
model.compile(optimizer='adam', loss='mean_absolute_error', metrics=['mean_absolute_percentage_error'])
请注意,我通过使用平均绝对误差作为损失,将此用例视为回归问题,即使我们毕竟不需要分数本身,而是需要预测产品的排序。
utils.plot_model(model, to_file='model.png', show_shapes=True, show_layer_names=True)

作者图片
让我们训练和测试模型。
**# Train**
training = model.fit(x=[train["user"], train["product"]], y=train["y"], epochs=100, batch_size=128, shuffle=True, verbose=0, validation_split=0.3)model = training.model**# Test** test["yhat"] = model.predict([test["user"], test["product"]])
test

作者图片
我们可以通过比较为我们亲爱的第一个用户生成的推荐来评估这些预测(与前面的代码相同):

作者图片
目前,所有最先进的推荐系统都利用了深度学习。特别地,神经协同过滤** (2017)结合了来自神经网络的非线性和矩阵分解。该模型不仅用于传统的协同过滤,还用于完全连接的深度神经网络,从而充分利用嵌入空间。额外的部分应该捕获矩阵分解可能遗漏的模式和特征。**

作者图片
用 Python 的术语来说:
embeddings_size = 50
usr, prd = dtf_users.shape[0], dtf_users.shape[1]**# Input layer**
xusers_in = layers.Input(name="xusers_in", shape=(1,))
xproducts_in = layers.Input(name="xproducts_in", shape=(1,)) **# A) Matrix Factorization
## embeddings and reshape**
cf_xusers_emb = layers.Embedding(name="cf_xusers_emb", input_dim=usr, output_dim=embeddings_size)(xusers_in)
cf_xusers = layers.Reshape(name='cf_xusers', target_shape=(embeddings_size,))(cf_xusers_emb)**## embeddings and reshape**
cf_xproducts_emb = layers.Embedding(name="cf_xproducts_emb", input_dim=prd, output_dim=embeddings_size)(xproducts_in)
cf_xproducts = layers.Reshape(name='cf_xproducts', target_shape=(embeddings_size,))(cf_xproducts_emb)**## product**
cf_xx = layers.Dot(name='cf_xx', normalize=True, axes=1)([cf_xusers, cf_xproducts]) **# B) Neural Network**
**## embeddings and reshape**
nn_xusers_emb = layers.Embedding(name="nn_xusers_emb", input_dim=usr, output_dim=embeddings_size)(xusers_in)
nn_xusers = layers.Reshape(name='nn_xusers', target_shape=(embeddings_size,))(nn_xusers_emb)**## embeddings and reshape**
nn_xproducts_emb = layers.Embedding(name="nn_xproducts_emb", input_dim=prd, output_dim=embeddings_size)(xproducts_in)
nn_xproducts = layers.Reshape(name='nn_xproducts', target_shape=(embeddings_size,))(nn_xproducts_emb)**## concat and dense**
nn_xx = layers.Concatenate()([nn_xusers, nn_xproducts])
nn_xx = layers.Dense(name="nn_xx", units=int(embeddings_size/2), activation='relu')(nn_xx) **# Merge A & B**
y_out = layers.Concatenate()([cf_xx, nn_xx])
y_out = layers.Dense(name="y_out", units=1, activation='linear')(y_out)**# Compile**
model = models.Model(inputs=[xusers_in,xproducts_in], outputs=y_out, name="Neural_CollaborativeFiltering")
model.compile(optimizer='adam', loss='mean_absolute_error', metrics=['mean_absolute_percentage_error'])

utils.plot_model(model,to_file='model.png ',show_shapes=True,show_layer_names=True)
您可以使用与以前相同的代码运行它,并检查它是否比传统的协同过滤执行得更好。

作者图片
混合模型
让我们先回顾一下现实世界提供了什么样的数据:
- 目标变量 —评级可以是显式的(即用户留下反馈)或隐式的(即如果用户看完整部电影,则假设是正面反馈),无论如何它们都是必要的。
- 产品特征 —项目(即电影类型)的标签和描述,主要用于基于内容的方法。
- 用户资料 —关于用户的描述性信息可以是人口统计信息(即性别和年龄)或行为信息(即偏好、在屏幕上的平均时间、最频繁的使用时间),通常用于基于知识的推荐。
- 上下文 —关于评级情况的附加信息(即时间、地点、搜索历史),通常也包含在基于知识的推荐中。
现代推荐系统在预测我们的口味时,会将它们结合起来。例如,YouTube 推荐下一个视频时,使用了谷歌知道的关于你的所有信息,而谷歌知道的很多。
在这个例子中,我有产品特性和关于用户何时给出评级的数据,我将把它们用作上下文(或者,它也可以用来建立用户简档)。
features = dtf_products.drop(["genres","name"], axis=1).columns
print(features)context = dtf_context.drop(["user","product"], axis=1).columns
print(context)

作者图片
让我们将额外信息添加到训练集合中:
train = dtf_train.stack(dropna=True).reset_index().rename(columns={0:"y"})**## add features**
train = train.merge(dtf_products[features], how="left", left_on="product", right_index=True)**## add context**
train = train.merge(dtf_context, how="left")

作者图片
请注意,您可以对测试集做同样的事情,但是如果您想要模拟真实的生产,您应该为上下文插入一个静态值。简单来说,如果我们在周一晚上为我们平台的用户做预测,上下文变量应该是白天=0 和周末=0 。
现在我们拥有了构建上下文感知混合模型的所有要素。神经网络的灵活性允许我们添加任何我们想要的东西,所以我将采用神经协同过滤网络结构,并尽可能多地包含模块。

作者图片
尽管代码看起来很难,我们只是在已经使用的基础上增加了几层。
embeddings_size = 50
usr, prd = dtf_users.shape[0], dtf_users.shape[1]
feat = len(features)
ctx = len(context) **################### COLLABORATIVE FILTERING ########################
# Input layer**
xusers_in = layers.Input(name="xusers_in", shape=(1,))
xproducts_in = layers.Input(name="xproducts_in", shape=(1,))**# A) Matrix Factorization
## embeddings and reshape**
cf_xusers_emb = layers.Embedding(name="cf_xusers_emb", input_dim=usr, output_dim=embeddings_size)(xusers_in)
cf_xusers = layers.Reshape(name='cf_xusers', target_shape=(embeddings_size,))(cf_xusers_emb)**## embeddings and reshape**
cf_xproducts_emb = layers.Embedding(name="cf_xproducts_emb", input_dim=prd, output_dim=embeddings_size)(xproducts_in)
cf_xproducts = layers.Reshape(name='cf_xproducts', target_shape=(embeddings_size,))(cf_xproducts_emb)**## product**
cf_xx = layers.Dot(name='cf_xx', normalize=True, axes=1)([cf_xusers, cf_xproducts])**# B) Neural Network
## embeddings and reshape**
nn_xusers_emb = layers.Embedding(name="nn_xusers_emb", input_dim=usr, output_dim=embeddings_size)(xusers_in)
nn_xusers = layers.Reshape(name='nn_xusers', target_shape=(embeddings_size,))(nn_xusers_emb)**## embeddings and reshape**
nn_xproducts_emb = layers.Embedding(name="nn_xproducts_emb", input_dim=prd, output_dim=embeddings_size)(xproducts_in)
nn_xproducts = layers.Reshape(name='nn_xproducts', target_shape=(embeddings_size,))(nn_xproducts_emb)**## concat and dense**
nn_xx = layers.Concatenate()([nn_xusers, nn_xproducts])
nn_xx = layers.Dense(name="nn_xx", units=int(embeddings_size/2), activation='relu')(nn_xx) **######################### CONTENT BASED ############################
# Product Features**
features_in = layers.Input(name="features_in", shape=(feat,))
features_x = layers.Dense(name="features_x", units=feat, activation='relu')(features_in) **######################## KNOWLEDGE BASED ###########################
# Context**
contexts_in = layers.Input(name="contexts_in", shape=(ctx,))
context_x = layers.Dense(name="context_x", units=ctx, activation='relu')(contexts_in) **########################## OUTPUT ##################################
# Merge all**
y_out = layers.Concatenate()([cf_xx, nn_xx, features_x, context_x])
y_out = layers.Dense(name="y_out", units=1, activation='linear')(y_out)**# Compile**
model = models.Model(inputs=[xusers_in,xproducts_in, features_in, contexts_in], outputs=y_out, name="Hybrid_Model")
model.compile(optimizer='adam', loss='mean_absolute_error', metrics=['mean_absolute_percentage_error'])

utils.plot_model(model,to_file='model.png ',show_shapes=True,show_layer_names=True)
这种混合模型需要更多的输入,所以不要忘记输入新数据:
**# Train**
training = model.fit(x=[train["user"], train["product"], **train[features]**, **train[context]**], y=train["y"],
epochs=100, batch_size=128, shuffle=True, verbose=0, validation_split=0.3)model = training.model**# Test**
test["yhat"] = model.predict([test["user"], test["product"], **test[features]**, **test[context]**])

作者图片
与其他方法相比,对于该特定用户,混合模型获得了最高的准确性,因为三个预测产品具有匹配的订单。
结论
这篇文章是一个教程,展示了如何用神经网络设计和构建推荐系统。我们看到了基于数据可用性的不同用例:对单用户场景应用基于内容的方法,对多用户产品应用协作过滤。更重要的是,我们了解了如何使用神经网络来改进传统技术,并构建可以包含上下文和任何其他附加信息的现代混合推荐系统。
我希望你喜欢它!如有问题和反馈,或者只是分享您感兴趣的项目,请随时联系我。
👉我们来连线👈
本文是系列用 Python 进行机器学习的一部分,参见:
** **
修改单个浓缩咖啡篮
原文:https://towardsdatascience.com/modifying-the-single-espresso-basket-dcb4d87f592f
咖啡数据科学
添加弹簧和金属网
浓缩咖啡在一杯咖啡的过程中会膨胀,但同时,由于萃取,它们会失去咖啡的质量。因此,在整个拍摄过程中,它们会失去密度,并被水取代。我很好奇,是否可以用弹簧将圆盘屏幕保持在一定深度,或者甚至可以压缩圆盘以保持相同的密度。
到目前为止,这些实验表现不佳,因为它们在开始时会产生较高的夯压。随着射击通过机械机构而不是水进行,需要新的硬件来减少顶部空间。我第一次看到这种可能性是因为我的天赋,尽管这不是我设计的。
然而,我在一个春天的单篮子里看到了一个机会。我对单个篮子(7g)的一个问题是有太多的顶部空间。通常,咖啡会从篮子的凹陷区域流出。唯一的补偿方法就是把篮子装满差不多 14g。


顶部有金属网且无弹簧的前后,所有图片由作者提供
理论:如果你用一个弹簧更好地将粉末装在一个篮子里,击球效果会更好。
弹簧设置
我用一个淋浴屏风坐在上面,我在上面放了一个弹簧来控制地面。弹簧来自 Happy Tamper ,因为它的大小和张力都是正确的。

拍摄后,咖啡留在正常的篮子区。


然而,味道和提取指标显示几乎没有影响。
设备/技术
咖啡研磨机:小生零
咖啡:家庭烘焙咖啡,中杯(第一口+ 1 分钟)
镜头准备:断奏夯实
预灌注:长,约 25 秒
输液:压力脉动
过滤篮 : 7g VST
其他设备: Atago TDS 计, Acaia Pyxis 秤
绩效指标
我使用两个指标来评估技术之间的差异:最终得分和咖啡萃取。
最终得分 是评分卡上 7 个指标(辛辣、浓郁、糖浆、甜味、酸味、苦味和余味)的平均值。当然,这些分数是主观的,但它们符合我的口味,帮助我提高了我的拍摄水平。分数有一些变化。我的目标是保持每个指标的一致性,但有时粒度很难确定。
使用折射仪测量总溶解固体量(TDS),这个数字结合咖啡的输出重量和输入重量用于确定提取到杯中的咖啡的百分比,称为提取率(EY)** 。**

使用弹簧有助于更清晰的拍摄,但它无助于解决根本问题。这个简短的实验让我重新思考顶空,因为这个实验显示顶空并没有像我想象的那样影响拍摄。
我也意识到我不像以前那样喜欢喝一杯了。
如果你愿意,可以在推特上关注我,在 YouTube 上关注我,在 Instagram 上关注我在不同机器上拍摄的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。也可以关注我的中和订阅。
我的进一步阅读:
工作和学校故事集
将你的笔记本改编成脚本
原文:https://towardsdatascience.com/modularise-your-notebook-into-scripts-5d5ccaf3f4f3
将您的代码从笔记本转换为可执行脚本的简单指南

ello 世界!在这篇文章中,我将介绍一个简单的指南,如何将你的笔记本变成可执行脚本。
之前 Geoffrey Hung 分享了一篇关于如何将你的 Jupyter 笔记本转换成脚本的极其全面的文章。然而,在我寻求生产模型的过程中,我发现在将笔记本从.ipynb升级到.py并运行整个脚本管道的模块中存在一些缺口。
如果您在分析领域工作,您可能会发现自己在某个时间点在笔记本上编写 python 代码。您可能遇到过这种方法的问题,也可能没有,但是如果您正在寻找一种用脚本执行笔记本的方法,那么这篇文章就是为您准备的。
我不会关注用脚本编写代码的好处,也不会试图比较这两种方法,因为笔记本和脚本各有利弊。如果你想知道为什么你应该作出改变,这篇文章可能会提供更多的清晰度。
</5-reasons-why-you-should-switch-from-jupyter-notebook-to-scripts-cb3535ba9c95>
我已经创建了一个演示库来对从 Kaggle 获得的信用卡数据集执行聚类分析。我将在整篇文章中使用这个存储库来分享示例片段。
目录:
- 项目和代码结构
- 抽象和重构
- 执行管道
项目和代码结构
拥有适当的存储库结构是必不可少的。我们不需要一个巨大的笔记本,或者多个包含从数据提取到建模的整个流程的不同模型的笔记本,我们首先必须通过将它们分解成不同的目的来划分这种复杂性。
典型的数据分析工作流程大致包括 3 个部分:提取、转换/预处理和分析/建模。这意味着你已经可以将笔记本分成至少 3 个独立的脚本——extraction.py、preprocessing.py和model.py。
在我的演示存储库中,extraction.py缺失,因为数据集是从 Kaggle 获得的,所以提取脚本是不必要的。但是,如果您正在利用 API、web 抓取或从数据湖中转储数据,那么拥有一个提取脚本将会非常有用。根据您的团队采用的数据模型的类型,您可能会发现自己必须编写一系列查询来提取数据,并执行join语句来将它们合并到一个单独的表或数据帧中。
项目的典型结构可能如下所示。要查看项目结构,请执行以下操作:
$ tree.
├── LICENSE
├── README.md
├── config.yml
├── data
│ ├── CC_GENERAL.csv
│ └── data_preprocessed.csv
├── main.py
├── notebooks
│ ├── dbscan.ipynb
│ ├── kmeans.ipynb
│ └── preprocessing.ipynb
├── requirements.txt
└── src
├── dbscan.py
├── executor.py
├── kmeans.py
├── preprocessing.py
└── utility.py
在这个项目存储库中,我们将管道分解成它的关键组件。让我们来看看它们的每一个目的。
/notebooks:这个文件夹是一个游戏场,在这里你的原始代码是以一种扁平的结构编写的,以便更简单地显示输出和遍历代码块。- 这个文件夹包含了你的脚本将要使用的各种数据文件。这里存储的一些流行的数据格式包括
.csv、.parquet、.json等。 - 这个文件夹存储了你所有的可执行脚本。
main.py:运行整个管道的主脚本,代码从笔记本中抽象和重构。config.yml:一个人类可读的文件,存储用于运行脚本的可配置参数
抽象和重构
一旦你有了你的项目结构,下一步就是重构和抽象你的代码来降低复杂性。将代码抽象成函数和类(加上适当的变量命名)有助于区分复杂性,而不是编写迫使读者理解如何操作的代码。
下面的文章提供了一个如何重构你的笔记本的惊人总结。
https://www.thoughtworks.com/en-sg/insights/blog/coding-habits-data-scientists
预处理
提取数据后,在将数据用于分析或模型之前,通常会对其进行清理。一些常见的预处理包括输入缺失值、去除异常值和转换数据等。
演示存储库中涉及的一些预处理包括移除异常值和输入缺失值。这些特定的任务可以被抽象成函数并存储在一个utility.py脚本中,该脚本稍后可以被导入到preprocessing.py中。
例如,用中位数输入缺失值的函数被放在了utility.py文件中。
型号
如果您必须对同一组预处理数据使用不同的模型,我们也可以创建类来创建一个模型实例。在演示存储库中,我研究了执行集群时的两种算法,每种模型都被分成可执行的脚本。例如,kmeans 被抽象为kmeans.py,而 DBSCAn 被抽象为dbscan.py。
让我们导入必要的包并为 kmeans 模型创建一个类。
如果我们想要创建一个模型实例,我们可以简单地定义一个对象来初始化一个模型并存储 kmeans 模型实例。
kmeans = kmeans_model(df) # instantiate kmeans modelkmeans_models = kmeans.kmeans_model(min_clusters=1, max_clusters=10) # run multiple iterations of kmeans model
由 Sadrach Pierre 撰写的这篇文章对如何在构建模型时利用类进行了广泛的阐述。
执行管道
随着分析管道的各种关键组件被抽象成函数和类,并转换成模块化的脚本,我们现在可以简单地运行整个管道。这是使用两个脚本实现的— main.py和executor.py。
主
主脚本main.py将在执行时运行整个管道,接收已加载的必要配置。在演示存储库中,我利用一个配置文件来存储参数,并点击与它交互。
执行者
一旦加载了模型选择及其相应的参数,我们就可以解析这些模型输入,并使用执行脚本executor.py来执行它。实例化模型、优化模型和拼接集群标签的步骤将在 executor 函数中展开。
要运行整个管道:
# execute entire pipeline with default model
python3 main.py# execute entire pipeline using DBSCAN
python3 main.py --model=dbscan# execute entire pipeline using another config file and DBSCAN
python3 main.py another_config.yml --model=dbscan
结论
将它们放在一起,我们现在有了一个逻辑项目结构,每个模块脚本都执行其特定的目的,底层代码被抽象和重构。然后,可以使用存储模型输入参数的配置文件来执行管道。
请留下评论💬如果有更多要添加的,我会很高兴将它们编辑在一起!
感谢阅读!😃
参考
烘焙前保湿的绿色咖啡
原文:https://towardsdatascience.com/moisturizing-green-coffee-before-roasting-4225cc949914
咖啡数据科学
探索水分
一些烘焙师已经控制绿豆水分很长时间了。我想自己尝试一下,收集一些关于它如何影响味道和提取的数据。据我所知,绿豆中 16%的水分含量是最佳的,所以这是我的目标。
水分协议
Chris Feran 建议如下:
- 测量青豆的水分
- 添加所需量的水,以达到所需的水分含量(建议 16%)
- 让豆子吸收最多 16 个小时,吸收最少的水分
- 烤肉
添加水分
我不想购买湿度计,因为它们很贵。我没有数据表明这是值得的,所以我在华氏 180 度烘干我的生咖啡豆,直到它们的重量不再变化。

所有图片由作者提供。3 颗青豆
晒!

我观察了两种混合物(四次烘烤),它们分别保湿,但混合烘烤:


以下是 2021 年 10 月 26 日烘焙的咖啡豆外观:


左:普通烘焙,右:保湿烘焙
我查看了颗粒分布,保湿咖啡会导致颗粒分布变得更粗糙。所以我用磨得更细来补偿。我不确定粒子的形状是如何变化的。

绩效指标
我使用两个指标来评估技术之间的差异:最终得分和咖啡萃取。
最终得分 是评分卡上 7 个指标(辛辣、浓郁、糖浆、甜味、酸味、苦味和回味)的平均值。当然,这些分数是主观的,但它们符合我的口味,帮助我提高了我的拍摄水平。分数有一些变化。我的目标是保持每个指标的一致性,但有时粒度很难确定。
使用折射仪测量总溶解固体量(TDS),该数字结合咖啡的输出重量和输入重量,用于确定提取到杯中的咖啡的百分比,称为提取率(EY)** 。**
设备/技术
浓缩咖啡机:金快线
咖啡研磨机:小生零
咖啡:家庭烘焙咖啡,中杯(第一口+ 1 分钟)
咖啡准备:后烘焙 5 天的湿度处理。
镜头准备:断奏捣音
预灌注:长,25 秒
输液:压力脉动
过滤篮 : 20g VST
输入/输出:21g 输入,约 24g 输出
其他设备: Atago TDS 计、 Acaia Pyxis 秤
表演
对于这两次烘焙,我看了 22 对照片,每对照片的参数对于普通和保湿都是一样的。我看到了味道和提取的性能改善,但提取不太清楚,因为有几个镜头有所下降。
****
我可以通过烘烤来分解这些,但是没有太多的模式。有一些异常值。
****
在双尾配对 t 检验中,味道的变化似乎具有统计学意义,但 EY 和 TDS 只是错过了 0.05 的阈值。这对拍摄时间也有影响。增湿的喷射覆盖过滤器(覆盖过滤器的时间(TCF))更快,总喷射时间更短。

我打算在烘焙之前继续研究保湿的青咖啡豆。主要的挑战是在烤前保湿和烤后保湿之间,我不得不将壁龛调至零。这导致研磨机很难停下来,所以我不得不慢慢地将咖啡豆倒入研磨机,而不是一下子全部倒入。
我还想研究水分是如何影响颗粒形状的,尤其是对于较粗的颗粒。控制水分是全年保持一致性的关键,因为青豆中的水分受储存条件中湿度的影响。
如果你愿意,可以在推特、 YouTube 和 Instagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以关注我在中和订阅。
我的进一步阅读:
个人故事和关注点
发霉的数据和仪表板
原文:https://towardsdatascience.com/moldy-data-and-dashboards-25d498e6979f
我用一张发霉食物的图片开始向我们的领导团队做演示,描述 data 当时的感受。
每个人的第一个表情是:“eww”接着是“啊是的,就是这种感觉!”。

资料来源:Unsplash.com
发霉的数据是什么时候
- 仪表板具有过时的指标定义
- 由于基础数据模型损坏,仪表板只能部分加载
- KPI 目标是从过时的电子表格中导入的
- 周一早上,由你不知道的 Looker 提供的部分完整的预定 PDF 报告会在闲暇时发给数百人
- 一位高管将你去年匆忙制作的一个仪表板加入书签,现在还在用它做决策
有时候发霉的数据是无辜的。但更多时候不是。这意味着人们使用错误的信息来做决定。
发霉的数据会损害对数据的信任,并导致会议中更多的时间花在讨论数据的意义和是否正确上,而不是根据数据做出明智的决策。
霉菌导致更多的霉菌向下游流动。你闻不到它,也许你看不到它,但你肯定不想要它。

作者图片(灵感来自彼得·扬达的文章:为什么数据分析师的角色从未如此艰难
仪表板应该受到额外的指责,因为它们最常把霉菌带到公司无辜的人面前。
问题是仪表板看起来和感觉起来都像真正的软件程序。一些永恒的东西,一些你可以信任的东西。
我不怪人们认为仪表盘是对的。毕竟,他们应该预料到这一点。
电子表格的美好往昔
15 年前,远在现代数据堆栈诞生之前,这些人依赖于从分析师汇总的电子表格中获取数据。这并不完美。
- 电子表格是一项繁重的工作: 它们必须手动更新——我清楚地记得,在之前的一份工作中,每周五早上,在执行 KPI 更新之前,我必须更新一份电子表格
- 电子表格很脆弱 : 如果仪表板感觉像是用实心砖砌成的房子,那么电子表格就是用胶带粘在一起的细条做成的。一些临时的东西,从来不是 100%准确的,经常会有一些计算错误
- 电子表格过期 : 它们不会自动更新,只在特定的地点和时间有效。谁不记得“年度预算 _v37.xls”
换句话说,电子表格感觉不像真正的软件。最终用户之间的合同是明确的;这是为你解决特定问题的东西,但使用时风险自担。
但是这份合同也意味着电子表格不会发霉。没有人期望挖掘出一个两年前的电子表格,并自动更新到自那时以来的业务发展情况。
我们将何去何从?
发霉的数据和仪表板在无意中将其呈现给错误的人方面扮演的角色是一个重要的问题。仪表板是人们看到的层,是确定数据信任的冰山一角和顶峰。
关于如何做得更好,我有一些建议
- 仪表板应该自毁
- 电子表格在现代数据堆栈中占有一席之地
- 我们需要新的工具
仪表板应该自毁
在《碟中谍》中,汤姆·克鲁斯从《碟中谍》中得到的信息在被阅读后会自动销毁。如果仪表板也是这样会怎么样?

资料来源:Unsplash.com
如果仪表板工具迫使分析师填写“自毁时间”字段会怎样?在那之后,仪表板会消失,完全蒸发,眼不见心不烦,砰。
肯定,有些人会发火。波兰办公室销售团队中的一个人曾经在 Slack 上给你发了一份定制仪表板的 DM,并且每天都看着它,他会不高兴的。
当然,一些人会觉得他们进行“自助式根本原因分析”的能力将被剥夺,因为他们一直依赖于一个三年前离开加入脸书的人创建的仪表板。
当然,你每六个月向经理汇报的“可自助解决问题的百分比”分数将会下降。
但是作为一个从事数据工作并最终负责数据质量的人,你不可能支持以上所有内容(我知道我做不到)。
你的工作是让这些人心烦意乱,但你这么做是有充分理由的——建立并维护数据信任文化。
仪表板应该只是表示层。如果你的仪表盘充满了业务逻辑,以至于你不能在 30 分钟之内重新创建一个,你应该三思是否你已经得到了正确的设置。
开始的几个月,我检查了我们所有的仪表板,并在 80%的仪表板上添加了“☠️将在 30 天内被删除”。一个月内,我们的仪表板从 175 个增加到 30 个。— 数据总监卡尔·约翰
我敢说电子表格吗?
有一段时间,我认为一切都应该自助。分析师不应在电子表格中快速提取数据,而应多花 30%的时间在 Looker 中实现自助访问,这样每个人在未来几年都可以获得相同的数据。我告诉他们,所有数据都将是真正的自助服务。后来我改变了主意。
我现在相信,电子表格通常是有意义的数据工作共享的正确工具。这是最接近大众化的数据语言,人人共享。
就像 Figma 邀请整个公司参与设计过程一样,电子表格对数据也是如此。有了电子表格,没有数据凭证、认为 dbt 是一种心理疗法的人可以参与进来。想要将客户类型的列添加到潜在客户列表中吗?执行 VLOOKUP,并将您自己的数据带入。
这与现代数据堆栈形成鲜明对比,在现代数据堆栈中,类似的任务需要从更改 LookML 代码到创建 pull 请求,再到更新数据模型,同时保持 YML 文件不变,以便指标的定义在您的数据目录中保持一致。祝你向数据团队以外的人解释这一点时好运。
我们需要新的工具
我们应该停止使用仪表盘吗?绝对不行。拥有准确和最新的顶级公司 KPI,任何人都可以在仪表板中访问,这真是太棒了。几乎不管是什么投资,拥有这个并把它做好都是值得的。我的牛肉和发霉的仪表板在一起。
但是发霉的仪表盘只是冰山一角。通常情况下,发霉的仪表板是上游某个东西坏掉的结果。这种模式不应该被仪表板带到表面上。
我们需要工具来告诉我们什么时候出了问题,这些工具不应该是全公司的仪表盘。
告诉我们数据何时过时的东西。
介于捕获的所有数据和最终到达最终用户手中的数据之间的东西。
在仪表板的数据点上创建一个✅,让每个人都充分相信它好用。
知道发霉数据并对通过仪表板暴露的内容持批评态度的东西。
在后台运行的东西会自动将坏数据带到表面——无论它是来自错误的业务逻辑、后端系统中的故障还是不再执行它们应该执行的任务的电子表格导入。
适合数据从业者工作流程的东西,通过工作流程推动他们做高质量的数据工作,而不是大量的数据工作。
不要误解我。我喜欢 ETL → ELT 的转变,和其他数据呆子一样,可以无限制地对所有数据进行探索性分析。我绝对不希望这件事不了了之。我只是认为,这和所有这些都应该在仪表板中对业务最终用户可用的期望之间的界限已经太远了。
如果你有一些关于如何处理发霉的仪表板和数据的建议,请告诉我。
使用 RDKit 和 Py3DMol 在 Streamlit 中进行分子可视化(第 2 部分)
Py3DMol 和 stmol 组分

艺术品。图片由作者提供。
1.介绍
W 当我问 Adrien Treuille 如何在 Streamlit 上部署 3D 结构时, TC Ricks 很快联系了我,他建议我开发一个 Streamlit 组件,当时组件还没有向公众发布。我致力于此,最终开发出了stmol组件。这对我来说很紧张,但我确实学到了很多。
在这篇文章中,我想和你分享三个简单的例子,如何使用 stmol 、 py3Dmol、和 RDKit 轻松制作分子查看器。我真的希望这能对其他人做化学相关领域的优秀 web 应用有所帮助。
你可以在这里找到这篇博文的回购。
2.安装必备库。
由于我们将使用 py3Dmol 和 stmol (版本 0.0.7),我们必须从安装这些库开始:
*pip install py3Dmol
pip install stmol==0.0.7*
(我们也需要 RDKit,所以去看看之前的相关帖子!)
现在我们准备开始举例了!
3。第一个例子:展示蛋白质!
下面的代码应该是简单的,同时也是简洁的。生成的 web 应用程序允许选择一些用于呈现 3D 蛋白质结构的蛋白质标识符:
现在,让我解释一下代码,
- 第 1–3 行。调用图书馆。除了 streamlit 之外,显然我们还要调用 Py3Dmol ,但是我们也会从 stmol 中调用一个名为
showmol的函数。如果你熟悉 py3Dmol ,函数showmol是渲染 3D 模型的show方法的替代。 - 四号线。我们在侧边栏中添加一个标题。
- 第[5,6]行。我们使用标准化的标签来识别 py3Dmol 稍后可以查询的一些蛋白质。
- 第 7–10 行。这里,我们在侧边栏中有三个小部件,一个用于设置背景颜色,另外两个选择框用于选择第 4 行给出的蛋白质标签,另一个用于选择分子渲染的样式。对于分子来说,更常见的是
stick和sphere,而对于蛋白质来说,首选的是cartoon。第 10 行定义了一个复选框,如果我们想让分子旋转(感谢 Avra !). - 第 11-18 行。在这几行中,我们制作了 py3Dmol 对象
xyzview。在第 11 行,我们对第 8 行选择的蛋白质进行查询,在第 13 行,我们设置第 7 行选择的背景颜色。从第 14 行到第 17 行,自旋被激活,这取决于复选框中的值。第 18 行放大到整个分子。 - 第 19 行。最后,在第 19 行,我们调用
showmol来呈现最终的分子视图。这是这段代码中更重要的一步,因为没有这一步就不可能查看对象。我不得不说showmol接受 py3Dmol 对象作为参数,并且可选地接受窗口的宽度和高度,默认情况下窗口有width=500和height=500。
下面的 gif 展示了应用程序的运行情况:

运行中的 app2 的屏幕截图。作者 Gif。
链接:https://share . streamlit . io/napoles-uach/medium _ mol/main/app 2 . py
4。第二个例子:上传本地文件。
如果你有自己的某种已知格式的分子文件,比如' xyz '并且你想在你的应用程序中渲染这些分子,那该怎么办?这里,我们可以利用file_uploader小部件上传一些本地文件。因此,我们可以对前面的代码稍加修改来实现这一点。也许在这里建立一个函数是很方便的,它可以让分子在任何我们需要的时候使用它。因此,在下面的代码中,我们定义了执行此类任务的函数render_mol(第 5–12 行)。我们将通过在第 14 行设置accept_multiple_files=True让file_uploader接受多个文件。最后,我们将迭代所有这些,并每次都应用render_mol(第 15–17 行):
就是这样。同样,在不到 20 行代码的情况下,我们在 Streamlit 中有一个非常不错的分子查看器,正如您在下面的 gif 中看到的:

app3 运行中的屏幕截图。作者 Gif。
链接:https://share . streamlit . io/napoles-uach/medium _ mol/main/app 3 . py
在这种情况下,分子是来自“纳米管位置”的一些美丽的富勒烯。如果你有兴趣探索一些其他的,这里有链接:
*https://nanotube.msu.edu/fullerene/fullerene-isomers.html
我邀请你下载一些富勒烯文件,然后尝试用这个应用程序将它们可视化。
5。第三个例子:RDKit + Py3Dmol。
在最后一个例子中,我将结合我们所学的知识制作一个应用程序,将微笑字符串转换为 3D 分子结构。请看一下代码,我相信您会认出一些部分:
注意,函数makeblock返回一个 mol 对象,该对象包含从 SMILES 字符串生成的 3D 结构。第 14 行是我们用MoltoMolBlock方法从 2D 表示转换到 3D 的地方。还要注意的是,render_mol函数与之前使用的是一样的,唯一不同的是在第 19 行,我改变了给予addModel方法的格式。这次我需要的是 mol 格式(其他还有 pdb 、 sdf 、 xyz 等。).因此,您可以根据所使用的格式进行更改。你可以看到下面的应用程序工作:

app4 运行中的屏幕截图。作者 Gif。
链接:https://share . streamlit . io/napoles-uach/medium _ mol/main/app 4 . py
如果您想深入了解将 RDKit 对象转换成 3D 结构的更多细节,您可以遵循 RDKit 文档,因为我在这里做的是最简单的例子。
6。结论
Py3Dmol 允许在 streamlit 网络应用程序中漂亮地渲染 3D 分子模型。为此,来自 stmol 组件的 showmol 函数很有帮助。当然,可能有一些解决方法可以达到同样的效果,但是我相信直接调用库并在 py3Dmol 代码中使用 showmol 会更容易。如果这个组件对你有用,请告诉我!
7.感谢
我想再次感谢 Chanin Nantasenamat 和 Avra 对这篇文章的善意评论。他们是生物化学专家和流线型忍者,我非常自豪能与他们合作。谢谢各位!
帮助我通过使用下面的链接成为媒体成员来继续创建这样的内容。只需 5 美元/月,你就可以在媒体上看到所有的故事,还可以用 2.27 美元支持我,谢谢!
https://medium.com/@jnapoles/membership*
链接:
https://github.com/napoles-uach/Medium_Mol
https://share . streamlit . io/napoles-uach/medium _ mol/main/app 2 . py
https://share . streamlit . io/napoles-uach/medium _ mol/main/app 3 . py
https://share . streamlit . io/napoles-uach/medium _ mol/main/app 4 . py
https://github.com/napoles-uach/streamlit_3dmol
https://pypi.org/project/py3Dmol/
https://www.rdkit.org/docs/GettingStartedInPython.html
https://nanotube.msu.edu/fullerene/fullerene-isomers.html
https://streamlit.io/components
增强现实中的分子,来自对日常物体和手绘分子结构的 ML-powered 识别
一篇新论文介绍了一款智能手机应用,它可以识别日常用品中手绘的化学结构和分子
斯坦福大学托德·马丁内斯(Todd Martinez)的团队刚刚发表了一篇新论文,展示了一款令人印象深刻的新智能手机应用程序,该应用程序可以识别照片中的化学物质或解析手绘分子结构,然后在你的手机上启动增强现实中的 3D 视图。这款应用名为 MolAR,你可以在苹果商店找到它(显然截至本文撰写之日,还没有针对 Android 的版本)。
我自己试过,效果非常好:


Sakshuwong 等人的臼齿在行动。左图:MolAR 刚刚扫描了丙醇的手绘结构,并在图纸顶部显示了该分子在 AR 中的 3D 结构。右图:MolAR 刚刚检测到一个杯子,并在 AR 中显示了咖啡因的 3D 结构,咖啡因是咖啡、茶和其他饮料的活性分子。作者根据 MolAR app 截图合成的图。
除了该应用程序在化学教育中的明显应用之外,该论文和相关作品(如下所述)肯定会引起对数据科学的读者的兴趣,因为它们说明了机器学习方法的具体应用,这里与视觉识别有关。最重要的是,该应用程序利用了商品增强现实,这是现代的另一个关键技术里程碑,尤其是随着元宇宙的发展。
https://medium.com/age-of-awareness/metaverse-not-sure-but-webxr-hell-yes-12af5b302e08
臼齿及其工作原理概述
用于分子可视化的 AR 和 VR
观察 3D 分子结构对于理解原子水平上的世界是如何运作的至关重要。但是分子本质上是 3D 物体,很长一段时间以来,我们一直停留在平面屏幕上的 2D 视图。尽管通过增强和虚拟现实(AR/VR)的 3D 可视化多次试图做出改变,但它们大多无效且非常昂贵,直到最近手机中的 AR 变得可用,甚至高端 VR 耳机也降低了成本。我在 EPFL(EPFL tech 4 impactEPFL 推广学校 智能系统中心 CIS EPFL EPFL 企业家俱乐部)工作的很大一部分实际上是致力于开发廉价的 AR 和 VR 在化学领域应用的新工具:
https://medium.com/geekculture/chemistry-and-biology-education-using-commodity-web-augmented-reality-in-any-device-9fa9fdc4ab35 https://lucianosphere.medium.com/molecularweb-a-brief-practical-introduction-5738771b224d
摩尔的关键:化学与日常生活的无缝输入和连接
在 AR 和 VR 中观察分子的优势越来越明显,技术也越来越容易使用。然而,马丁内斯的团队意识到了一个重要的问题:如何以一种更具互动性的方式将分子结构输入到应用程序中?到目前为止,工具要么包括一个预设的分子列表,要么允许你在电脑上手动绘制 2D,或者通过键入分子名称来搜索它们,然后在 ar 或 VR 中启动它们。 MolAR 提出了一种完全不同的方法,允许更简单、直接地输入分子结构。
MolAR 使用深度学习技术以两种方式输入分子:
- 通过识别和分析手绘在一张纸上、板上等的分子。这是基于该小组之前开发的一个名为 ChemPix 的系统。
- 通过识别日常生活中的物体,然后从数据库中检索其最重要的分子,例如咖啡因,如果用户将手机摄像头对准一杯咖啡,如上例所示。
作为免费的应用程序,无需任何外部硬件或辅助软件就可以在智能手机上运行,并且通过这种简单的方法来引入分子,MolAR 提供了一个平台,以高度沉浸式的方式可视化 3D 分子结构并与之交互,几乎不费吹灰之力。
MolAR 应用程序是用 Swift 编写的,带有一个专门针对 iPhones 和 iPads 的软件开发工具包(SDK),所以很可能不会很容易看到这个很酷的应用程序移植到 Android 上。为了实现 AR,MolAR 使用了 SDK 的 ARKit 模块,该模块包括处理视觉场景、运动跟踪和锚点上的图形显示的功能。在这种 AR 模式下,对象遮挡的效果非常好,所以你可以用手指指向分子的一部分,并在屏幕上清楚地看到这一点——很少有 AR 系统支持并正确渲染这一点。
有趣的是,与其他工具不同,对象和化学结构的识别和解析是在服务器上进行的,我将在接下来的两节中对此进行概述。
基于 MolAR 的手绘结构解析
为了解析手绘结构,服务器通过神经网络运行从应用程序接收的图像,该神经网络将图像转换为 SMILES 代码,这是分子结构的简化纯文本表示。这个名为 ChemPix 的神经网络也是由 Martinez 小组在之前的一项工作中开发的,如果你对深度学习在图像分析中的应用感兴趣,那么你必须阅读这份工作,如果是化学方面的话,就更是如此:
https://pubs.rsc.org/en/content/articlehtml/2021/sc/d1sc02957f
简而言之,ChemPix 首先用卷积神经网络对图像进行编码,然后用产生微笑代码的长短期记忆网络对其进行解码。在 MolAR 的服务器中,这个 SMILES 代码然后通过使用翻译器转换成 3D 坐标,翻译器是美国国家癌症研究所的许多计算化学终点之一,就像我们的 MoleculARweb 的虚拟建模工具包一样。
3D 原子坐标最终通过球体和圆柱体图元转换为 3D 模型,非常类似于任何其他 AR/VR 系统中的分子显示。
解析对象
为了识别物体,MolAR 的服务器使用谷歌云视觉 API 和亚马逊 Rekognition。像这样的 API 提供了简单的方法来利用其他人构建的优秀资源,这些人拥有单个用户可能没有的专业知识,或者可能需要单个终端用户无法获得的计算能力。API 简化了新的酷工具的开发,并且在这个过程中展示了不同的包和不同的人的工作的无缝集成,即使他们有不同的特定最终目标。当然,他们展示了知道外面有什么可以使用的力量。对我来说,API 是计算机技术中最酷的发明之一,尤其是基于网络的编程。
回到 MolAR 及其解析对象的方式,它结合了来自 Google Cloud Vision API 和 Amazon Rekognition 的输出,以提取可能出现在网络摄像头馈送中的对象列表。这些对象是诸如“牛奶”、“番茄”或“杯子”的关键字,在数据库中映射到与对象相关的分子。比如“牛奶”配“乳糖”,“番茄”配“番茄红素”,“杯”配“咖啡因”。这确实是一个非常简单的方法,但是如果物体-分子对的数据库足够大的话,这是非常实用的。
当然,该工具不会知道图像中的饮料实际上是脱咖啡因咖啡而不是普通咖啡;同样,它可能会正确地选择红葡萄酒作为“葡萄酒”,而不是白葡萄酒(白葡萄酒实际上可能会与其他饮料混淆)。
这似乎是一个显而易见的问题(顺便说一句,很难解决),但如果该工具用于化学教育,它实际上非常重要。如果一个学生天真地想比较一杯普通咖啡和无咖啡因咖啡的摩尔说什么呢?我的观点是,无论工具看起来有多酷,在课堂上使用时都必须非常小心。
结论和进一步阅读
技术发展非常快,最棒的是,它们变得足够实用,可以用于具体的应用,帮助我们更好地教和学。正如你所看到的,通过视频分析,甚至是带有位置感知姿态估计的增强现实,将网络技术与机器学习相结合,实现了物体识别。
仅仅在十年前,这一切都是不可能的,而现在,它已经出现在软件开发者的工具箱和用户的智能手机中。
进一步阅读
同行评议论文:【https://aip.scitation.org/doi/abs/10.1063/5.0090482
或者,您可以在此免费获取预印本:
https://chemrxiv.org/engage/chemrxiv/article-details/613081028e38a3bd644709da
要了解更多这个小组令人兴奋的研究和软件开发,请看它的主页:https://mtzweb.stanford.edu/
www.lucianoabriata.com我写作并拍摄我广泛兴趣范围内的一切事物:自然、科学、技术、编程等等。 成为媒介会员 访问其所有故事(我为其获得小额收入的平台的附属链接,无需您付费)和 订阅获取我的新故事 通过电子邮件 。到 咨询关于小职位 查看我的 服务页面这里 。你可以 这里联系我 。
MolKGNN:将卷积扩展到分子
原文:https://towardsdatascience.com/molkgnn-extending-convolution-to-molecules-b94a4d51f39f
了解在 AAAI2023 上接受的为药物发现量身定制的可解释 GNN

图一。类似于(A)图像卷积和(B)提出的分子卷积。图片来自原纸。
本博客介绍了我们的最新模型MolecularKernelGraphNeuralNNetwork(MolKGNN)摘自论文
可解释手性图神经网络用于药物发现中的定量结构活性关系建模
该作品在 AAAI2023 (口头和海报展示),以及图形学习会议(非档案海报)上被接受
简介:
阐明分子结构及其药理活性是药物发现史上的一个长期问题。1859 年,德国化学家 Carl Stahlschmidt 证明了在马钱子碱和马钱子碱中加入碘甲烷显然破坏了它们的生理作用[1]。他的工作促使两位苏格兰科学家 Alxander Crum Brown(1838-1922)和 Thomas r . Fraser(1841-1920)对一系列化合物进行了实验。这些实验帮助他们确认了存在结构活性关系(SAR) [1]。当时,人们相当乐观地认为,将会发现描述分子结构与其药理活性之间关系的一般规律。从那时起,已经开发了几种数学/统计和机器学习方法,试图预测这种关系。这个过程被称为定量结构活性关系(QSAR)建模。QSAR 尝试的例子包括多元线性回归、偏最小二乘法、判别分析、决策树、遗传算法等[2]。人们对 QSAR 研究的治疗应用寄予厚望。然而,直到今天,预测小分子的生物活性仍然是一项具有挑战性的任务。
任务:
QSAR 建模,即从分子结构预测二进制标签 0(无活性)或 1(活性)。分子被表示为图形,其中节点是原子,键是边。
该文件的重点:
- 本文介绍了一种新的 SE(3)/构象不变性模型,命名为 MolKGNN ,它是为药物发现中的 QSAR 任务量身定做的。
- MolKGNN 的特点在于其新颖的分子卷积、轻量级的手性计算以及可解释性
- 一个现实的药物发现实验证明了提议的 MolKGNN 的实用价值
MolKGNN 模型架构

图二。(A)在 2D 图像卷积中,较高的卷积值表示较高的视觉相似性模式。(B)图像内核提供了可解释性的好处。图片由作者提供。
MolKGNN 从 2D 图像卷积中获得灵感(图 2)。在 2D 图像中,卷积运算可以看作是计算图像块和图像核之间的相似性。较大的输出值表示较高的视觉相似性模式,如边缘、条带和曲线[3]。然而,由于其不规则性,2D 图像卷积不能容易地扩展到 3D 分子图。因此,新的分子卷积被设计成在分子邻域(1 跳邻居)和分子核(1 跳)之间卷积,类似于用图像核卷积的图像补丁。分子卷积具有以下特性:
- 像图像卷积一样,分子邻域与分子核越相似,分子卷积值应该越高。
- 与图像卷积不同,分子卷积应该是旋转不变的
- 分子内核可以提供可解释性。
接下来的问题是,我们如何设计分子卷积使其具有上述性质?
分子邻域 S 与核s’之间的相似性通过相似性得分 ϕ(S,s’)来量化。这个分数是三个子分数 ϕ_ cs、 ϕ_ ns 和 ϕ_ es 的组合,这三个子分数分别捕获中心相似性、邻近节点相似性和边缘相似性。这些的计算如下所示(符号在最后有完整的描述):

图 3。相似性得分计算的图示。图片来自原文。
从三个子得分的组合中计算邻域子图和核之间的相似性得分。子分数量化了邻域子图和核之间不同方面的相似性。中心相似性子分数 ϕ _cs 捕获中心节点属性(v 和 v’)之间的相似性。邻近节点相似性子分数 ϕ _ns 捕获邻近节点(u1 和 u1’、u2 和 U2’、u3 和 u3’)的属性的相似性。邻域边缘相似性子分数 ϕ _es 捕捉相邻边缘属性的相似性。
因为节点/边属性是向量,所以使用相似性函数 sim(⋅来计算向量相似性。我们在实现中使用了余弦相似度。如图 3(a)所示,在中心节点 v 和 v’的属性之间计算ϕ_cs 。
注意,为了计算 ϕ _ns,有多种匹配邻居的方法,每种匹配都给我们一个分数。我们列举所有匹配并将给出最高分数的一个定义为最佳邻居匹配χ*(例如,图 3(b)右侧的 u1 和 u2’、U2 和 u3’、u3 和 u1’)。这种匹配的枚举是可行的,因为在类药物分子中至多有四个邻居。
对于 ϕ_ es ,b 由于相邻节点与相邻边一一对应,我们可以根据最优邻居匹配χ(图 3(c))找到最优边匹配χ^{e,}。
接下来,我们想整合手性计算。想法是用内核作为参考的锚点。然后将分子邻域与核进行比较,以查看它是否与核具有相同的邻域顺序。我们利用矢量形式的四面体体积计算来捕捉邻居排序[4]。请参见下图。

图 4。手性计算图解。图片来自原纸。
在邻域 1 中,三个向量 a1、b1 和 c1 由任意选择的邻居构成而不失一般性。四面体的体积可以计算为 1/6* a1×b1⋅c1.请注意,该音量可以是正的或负的符号,表示音量方向。对于最佳匹配中的相应邻居节点,可以在内核中执行相同的计算。如果邻域 1 的四面体体积的符号与核中的相同,我们知道它们具有相同的相邻节点排序。在上面的邻居 2 的情况下,它的体积具有不同的符号,并且我们知道邻居 2 具有不同的邻居节点顺序。还要注意,常数 1/6 在符号确定中是微不足道的,在实际实现中可以省略。
最后,我们利用消息传递神经网络(MPNN)框架[5]来获得更大的感受域。其思想是用分子邻域和核之间相似性的聚集来代替传统的邻近节点属性的聚集。请参见下图。

图 5。MolKGNN 模型概述。关键思想是用一个分子邻域和一组核之间的相似性来代替传统的相邻节点属性的聚集。图片来自原纸。
最后的原子嵌入可以通过重复几次计算分子卷积和传播消息的过程来学习。最终的分子包埋可以通过各种汇集技术获得。下游预测可以通过在分子嵌入的顶部附加诸如多层感知(MLP)的分类器来进行。
实验
来自药物发现的真实数据集被用于测试 MolKGNN [6,7]。这些数据集经过精心筛选,以消除药物研发活动中常见的假阳性信号。数据集统计如下所示,可在 FigShare 上获得。

表 1。数据集统计。它们的特点是大尺寸,高度不平衡的标签分布和不同的蛋白质目标。图片来自原纸。
下面的两个表格显示了结果。logAUC_[0.001,0.1]在这里用于偏向具有高预测分数的化合物。这与现实世界的药物发现场景相对应:只有那些预测活性得分高的药物才会被购买或合成。因此,更感兴趣的是查看这些化合物的模型性能,而不是一般的模型性能。更多结果和实验细节见原文。

图 7。结果表。图片来自原创论文。
此外, MolKGNN 能够捕捉与领域知识一致的模式。下面是一个从学习过的内核翻译过来的模式的例子。这种模式也被称为药物化学中的一种重要结构,称为三氟甲基。

图 6。一个学习过的内核显示了一个重要的亚结构模式,被称为三氟甲基。图片来自原纸。
此外,关于 MolKGNN 表达能力的实验证实了其能够区分手性分子。使用 CHIRAL1 数据集[8]。其对于具有一个手性中心的单一 1,3-环己基丙烷骨架支架包含 102,389 个对映异构体对。数据被标记为 R 或 S 立体中心,我们使用精度来评估性能。为了比较,我们使用 GCN [9]和我们的模型的修改版本,MolKGNN-NoChi,它删除了手性计算模块。我们的实验观察到 GCN 和 MolKGNN-NoChi 达到 50%的准确率,而 MolKGNN 达到近 100%,这从经验上证明了我们提出的方法区分手性分子的能力。
消融研究
这项研究的详细信息可以在原始论文中找到。
ϕ的分量 (S,s’)

图 7。 φ(S,S’)组件的 alation 研究结果。图片来自原纸。
移除任何元件都会对 logAUC[0.001,0.1]产生负面影响。事实上,就性能变化的百分比而言,logAUC[0.001,0.1]的影响大于 AUC。请注意,在某些情况下,如移除φes,根据 AUC,性能会有所提高,但这会显著妨碍 logAUC[0.001,0.1]度量。
内核号

图 8。不同内核数量的性能。图片来自原创论文。
当核的数量太少时(< 5), it greatly impacts the performance. However, once it is large enough to a certain point, a larger number of kernels has little impact on the performance.
Discussion of Computation Complexity
It may seem to be formidable to enumerate all possible matchings described above. However, most nodes only have one neighbor (e.g., hydrogen, fluorine, chlorine, bromine and iodine). Take AID 1798 for example, 49.03%, 6.12%, 31.08% and 13.77% nodes are with one, two, three
和所有节点中的四个邻居,分别为。对于具有四个邻居的节点,由于手性,24 个匹配中只有 12 个需要被枚举[8]。由于分子图的邻接矩阵是稀疏的,大多数 gnn 的时间复杂度为 O(|E|)。如上所述,排列由多达四个邻居(12 个匹配)限定。因此,寻找最佳匹配的时间复杂度为 O(1)。分子卷积的计算与 K 个核的数量成线性关系,因此时间复杂度为 O(K)。总的来说,我们的方法需要 O(|E|K)的计算时间
结论
在这篇文章中,我们引入了一个新的 GNN 模型来解决 QSAR 模型的问题。MolKGNN 利用新设计的分子卷积,其中分子邻域与分子核进行比较,以输出相似性得分,该得分用作下一层的新原子嵌入。进行全面的基准测试来评估 MolKGNN。使用由来自不同蛋白质目标类别的实验 HTS 数据组成的精确数据集进行评估。数据集是高度不平衡的,这凸显了这个现实世界问题中积极信号的稀缺。为了评估,我们不仅使用传统的 AUC,而且使用 logAUC[0.001,0.1]来评估该方法在高截止条件下的性能。这种高截止条件是真实世界应用的典型条件,证明了 MolKGNN 在药物发现中的可应用性。此外,本文提供了一个理论证明和实验证明,MolKGNN 能够区分手性分子,同时为其结果提供了可解释性。

在 DC 华盛顿举行的 AAAI2023 会议上口头和海报展示这项工作
参考文献:
[1]约翰·帕拉斯坎多拉。"结构-活动关系——早期的海市蜃楼."历史上的药学13.1(1971):3–10。
[2]沃穆思,卡米尔·乔治,编辑。药物化学的实践。学术出版社,2011 年。
[3]林、王志浩、黄胜玉、王玉强。"用于点云分析的三维图形卷积网络的学习." IEEE 模式分析与机器智能汇刊44.8(2021):4212–4224。
[4] Sliwoski,Gregory,等,“BCL::EMAS-三维 QSAR 的对映选择性分子不对称描述符。”分子17.8(2012):9971–9989。
[5]吉尔默、贾斯汀等人,“量子化学的神经讯息传递”机器学习国际会议。PMLR,2017。
[6] Butkiewicz,Mariusz 等,“用 PubChem 数据库对基于配体的虚拟高通量筛选进行基准测试。”分子18.1(2013):735–756。
[7] Butkiewicz,Mariusz 等人,“来自 pubchem 数据库的高通量筛选分析数据集。”化学信息学(特拉华州威尔明顿。) 3.1 (2017)。
[8] Pattanaik,Lagnajit 等人,“四面体手性分子的信息传递网络”arXiv 预印本 arXiv:2012.00094 (2020)。
[9]基普夫、托马斯·n 和马克斯·韦林。"图卷积网络的半监督分类." arXiv 预印本 arXiv:1609.02907 (2016)。
这部作品由王禹、奥安武、罗科·莫雷蒂、鲍比·博登海默、延斯·梅勒和泰勒·德尔合著。
刘云超“兰斯”感谢英伟达学术硬件资助计划的支持。

表 1。符号。作者做的。
统计分布的矩和矩母函数
统计矩的解释以及如何使用矩母函数导出它们

马库斯·斯皮斯克在 Unsplash 上的照片
介绍
在本帖中,我们将讨论统计学中的矩的概念(从物理学的角度来看,这很难接受!)以及它们为什么重要。接下来,我们将展示一种使用力矩生成函数计算力矩的简单方法。
什么是瞬间?
长话短说,时刻描述了的位置、的形状和大小的一种概率分布。
以下是前 4 个时刻的列表:
还有一个叫零阶矩的东西,基本上说的是任意概率分布下的面积为 1。
你们大多数人应该知道以上四个参数指的是什么分布。如果你不太熟悉,我提供了解释它们的链接,因为它们相当容易理解,特别是对于正态分布。
第一个力矩(平均值)被认为是非中心力矩,而其余三个是中心力矩,因为它们与平均值有关。这并不重要,但它确实会导致一些复杂的细微差别。
高阶矩也存在,只是提供了关于分布的偏度和峰度的更多信息。奇次幂矩推断关于偏斜度的信息,偶次幂矩推断关于峰度的信息。这里联系的是一个关于交换的大讨论,解释为什么会这样。
计算力矩
第一个矩, E[X] (一个随机变量 X 的期望值,平均值)可以计算如下:

作者在 LaTeX 中生成的图像。
其中 f(x) 是某些概率分布如正态、泊松、伽玛等的概率密度函数。这个公式来自无意识统计学家的 定律。
上面的积分看似简单到可以积分,那二阶矩呢?方差,可以计算为:

作者在 LaTeX 中生成的图像。
峰度和偏斜度可以使用期望值积分来进一步计算,但是它很快变得复杂。
一般来说,计算偏心力矩的公式为:

作者在 LaTeX 中生成的图像。
又过了一个以为中心的瞬间:

作者在 LaTeX 中生成的图像。
方差积分看起来相当简单,尽管比一阶矩更复杂。因此,可以看到,当积分变得太复杂而不容易计算时,最终会出现问题。因此,我们需要另一种选择…
矩母函数
这就是力矩生成函数介入的地方!这些从字面上产生力矩,并定义为:

作者在 LaTeX 中生成的图像。
其中 t 是一些 哑变量 便于我们计算矩。
现在,为了找到期望值(矩),我们简单地找到 MGF 的导数,并设置 t = 0:

作者在 LaTeX 中生成的图像。
由此得出结论:

作者在 LaTeX 中生成的图像。
为什么会这样呢?
我们可以用指数(欧拉数)的 麦克劳林级数 (泰勒级数 的子集 围绕 0 )来证明上述公式:

作者在 LaTeX 中生成的图像。
然后将该方程代入力矩生成函数:

作者在 LaTeX 中生成的图像。
现在对 t 进行微分,并设置 t = 0 :

作者在 LaTeX 中生成的图像。
这里我们展示了一阶导数等于平均值(期望值)!计算(手工和计算机)导数比 n 次积分 要容易得多。
正态分布示例
让我们看一个使用正态分布的例子。概率密度函数由下式给出:

作者在 LaTeX 中生成的图像。
其中 μ 为给定正态分布的均值σ为给定正态分布的标准差。
正态分布的力矩生成函数可表示为:

作者在 LaTeX 中生成的图像。
我没有在这篇文章中包括推导,因为它是详尽的,但是你可以在这里找到它。
取一阶导数并设置 t = 0 :

作者在 LaTeX 中生成的图像。
我们找到了正态分布的均值,正如我们所料,它就是。
结论
矩描述了概率密度函数的位置(均值)、大小(方差)和形状(偏斜度和峰度)。矩母函数允许我们用导数来计算这些矩,导数比积分更容易处理。这一点特别有用,因为概率密度函数可能很复杂,用矩母函数进行计算通常更容易。
和我联系!
- 要在媒体上阅读无限的故事,请务必在这里注册!T49💜
- 当我在这里发布注册邮件通知时获得更新!T55😀
- LinkedIn👔
- 碎碎念 🖊
- GitHub 🖥
- https://www.kaggle.com/egorphysics🏅****
(所有表情符号都是由 OpenMoji 设计的——开源的表情符号和图标项目。许可证: CC BY-SA 4.0
板球摇钱树:使用重复条件反射的概率为 100
板球得分≥ 100 分的概率分析

自从我小时候看我的巴基斯坦队比赛,我就对 100 分着迷。我相信许多人会希望他们最喜欢的击球手最终得分一个世纪。然而,每个人都知道,不是每个击球手敲 100 分的结果。这个事件是罕见的,这使得它成为一个作为概率学生学习的完美主题。在这篇文章中,我将探讨击球手得分 100 的概率。然后使用条件概率的规则深入探究概率是如何变化的。
数据&方法论
请查找有关数据来源和方法的重要信息:
- 数据来源:所有数据均来自cricsheet.org。他们提供 ODI、t20 和测试赛的详细数据。我不拥有这些数据,但是 cricsheet 数据在开放数据共享属性许可下是可用的。在本许可证下,每个人都可以自由地使用、构建和再分发数据,并赋予其适当的所有权。点击阅读关于许可证的信息。
- 数据验证:CRIC sheet 的创始人在验证数据源方面做得很好,误差极小。我使用汇总数据验证了这些数据,并将其与 ESPNcricinfo 等主要板球网站上的汇总数据进行了比较。
- 数据维度&时间:数据集包含从 2004 年 1 月 3 日到 2022 年 4 月 16 日的 1998 个 ODI 匹配。几乎囊括了期间所有主要的男性 ODI 玩法。数据集包含打了 1,059,669 球,&34,466 击球手敲了&3900 局。
- 方法论:这篇文章的核心目的是分析击球手击球得 100 分的概率。该方法将在后面详细解释,但主要使用概率规则,如全概率定律、条件概率和贝叶斯规则。
从基础开始
概率问题通常可以分解成计数问题。在计算掷骰子数(骰子是六面的)的经典教科书示例中,可以很容易地模拟骰子,计算骰子落在 6 上的次数,然后除以掷骰子的总数。给定足够数量的掷骰子,人们可以找到经验观察到的掷出 6 的概率。如果是公平骰子,那么掷出 6 的概率是 1/6。
类似地,如果你要找到得分 100 或更高的概率,你可以计算总分≥ 100 的击球手击球次数,然后除以击球手击球的总次数。(击球手击球是指一场比赛中一名击球手的击球动作)。

图片由作者提供。击球手得分总数的直方图/经验概率密度函数。红色区域突出显示了导致 100 次或以上的所有敲击。— Contains information from cricsheet which is made available under the ODC License
上面的图在 x 轴上显示了击球手得分,在 y 轴上显示了相应的概率密度。将导致得分≥ 100 的击球手击球次数相加,我们得到 1,090 次击球手击球。在我们的数据集中击球手击球的总数是 34,466。除以两个 we 1090/34466 ≅ 3.16% ,也就是说 10000 次敲击中只有大约 316 次产生一个世纪或更高的分数。
一个世纪当然是非常罕见的事件。作为一名统计建模师,我发现对不太可能发生的事件建模既具有挑战性,同时又令人着迷。这个事件可以建模为一个二元分类问题。然而,非常低的流行率使得模型很难以高准确度进行预测。为了建立好的模型,你应该根据数据中其他变量的变化来观察目标是如何变化的。
什么是概率条件作用?
虽然这个概念用术语来描述似乎很深奥,但我相信每个人都有一个直观的想法。条件概率由公式 P(A|B) = P(A & B)/P(B)决定。为了得到给定事件 B 已经发生的概率,你取两个事件一起发生的概率— P(A & B) 然后除以 B 发生的总概率— P(B) 。
假设我们要计算巴基斯坦队在与澳大利亚队比赛的情况下得分≥ 200 的概率。根据我们观察到的巴基斯坦队在与澳大利亚队比赛时总分达到 200 或更高的次数,除以巴基斯坦队与澳大利亚队比赛的总次数,可以很容易地根据经验估计出这种概率。

图像由作者,' | '的意思是有条件的。∩的意思是&
我们可以继续增加更多的条件,比如比赛是在巴基斯坦主场还是在澳大利亚主场,巴基斯坦队是追赶还是进攻(设定比分)等等。您可以添加条件来查看在不同的场景下期望的概率是如何变化的。
团队训练

在数据集中,球队打出的总击倒次数中,按球队分组的得分超过 100 的击倒次数。不包括小球队。图片由作者提供。— Contains information from cricsheet which is made available under the ODC License
在第一部分中,观察到 3.16%的击球手击球得分≥ 100 分。合乎逻辑的下一步是根据哪个队击球来看一个世纪或更高分数的可能性有多大。在上面的图表中,每支球队都计算了导致 100 分的击球手击球次数,我们用每支球队击球手击球的总次数除以这个次数来得到这个频率。
在我们的数据集 3379 中,印度是敲门次数第二多的国家,也是百夫长敲门次数最多的国家。然而,它没有 100+的最高频率,该奖项由南非获得,南非的敲击次数为 5.08%100+次。巴基斯坦在 100+敲击总数(99)和频率( 3.33% )方面都处于中间位置。略高于团队中立率 3.16%。津巴布韦在这方面的表现最差,在 2321 次敲门中,只有 30 次得分为 100+,得分率为 1.29%。
喜欢这个故事吗?需要编码帮助?请考虑成为赞助人!
https://www.patreon.com/user/membership?u=78117613 [## 阿尔斯兰·沙希德|帕特里翁
www.patreon.com](https://www.patreon.com/user/membership?u=78117613)
对球的调节存活了&局

条件概率函数,对击球手和击球局幸存的球应用条件。图片由作者提供。— Contains information from cricsheet which is made available under the ODC License
基线是中立局,进攻是第一局的敲打,追逐是第二局的敲打。“存活的球数”代表击球手到目前为止打出的球数。在每个数据点,该图显示击球手以 100+分结束击球的概率。当然,击球手存活的时间越长,他们积累的分数就越多,最终达到 100 分。费率根据局数分开(不包括对方球队得分低于 100 的追逐局)。击球手在第一局击球时更有可能得 100 分,而不是在追逐时。当击球手在 60 个球后幸存下来,他们可能会在第一局打出 100 多分 21% ,在底线得分 19% ,在第二局 16.5% 。注意:当击球手在 120 个球以上幸存下来时,剩下的击球次数非常少,所以这些数字很可能因为样本少而有偏差。
在第一局得分 100+的可能性更高,击球手存活的时间越长,他们获得 100 分的机会就越大,但必须注意的是,在此图中没有考虑累积得分。在对这个问题建模和预测概率(另一篇文章的主题)时,将存活的球数和累积的跑垒数组合起来似乎很直观。
以累计得分为条件&局

条件概率函数,对击球手和击球局的累积得分进行调节。图片由作者提供。— Contains information from cricsheet which is made available under the ODC License
该图显示了得分≥100 的可能性,取决于击球手已经跑了多少次以及他们正在击球的局数。该图显示,当击球手已经跑了半个世纪(50 分)时,他们有 21.5% (第一局) 19.7% (底线)& 17.3% (第二局)至少跑 100 分的概率。在累计得分 75 & 80 之间,概率穿越 50%。有趣的是,即使累积了 99 次跑步,也只有 97.8%的机会达到 100 次。在从 99 达到 100 之前,有 2.2%的敲击导致出局/匹配结束。
不出所料,击球手跑得越多,获得至少 100 分的几率就越高。特别感兴趣的是概率变化多少,这可以帮助构建统计模型来预测 100+的结果。该曲线作为一个整体不是线性的,在 50 次运行后,概率增加的速度比 50 次运行前快得多。表明随着运行次数的增加,事件变得越来越容易预测!
球员和累积跑分的条件

条件概率函数,对球员和击球手的累计得分施加条件。图片由作者提供。— Contains information from cricsheet which is made available under the ODC License.
基线曲线反映了所选 4 名球员的“平均值”。100+的玩家级别的样本量非常小,顶级百夫长是 Virat Kholi,只有 43 次击倒,导致超过 100 次。所以这些数字应该以适度的怀疑态度来看待。尽管如此,可以看出 Babar Azam 具有最高的曲线,表明表现更好,但他的样本量是最小的,在数据集中只有 84 局。Virat Kholi 得 100 分的倾向比 Martin Guptill & AB De Villiers 上升得更快。有趣的是,马丁·古普提尔和巴巴尔·阿扎姆都没有达到 99 分,而科利·德·维利耶都达到了。
文章到此结束。我希望你喜欢它,请通过电子邮件订阅和关注我了解更多内容。在接下来的文章中,我将尝试在这个基础上建立一个几个世纪的预测模型。敬请期待!
以下是我的一些其他文章,你可能会喜欢:
- Money-Balling Cricket-Statistically evaluate-a-Match:https://medium . com/mlearning-ai/Money-Balling-Cricket-Statistically-evaluate-a-Match-9 CDA 986d 015 e
- 谎言、弥天大谎、数据科学:https://medium . com/mlearning-ai/Lies-Big-Lies-and-Data-Science-6147 e 81 FB 9 fc
- Money-Balling Cricket—Babar Azam 的平均跑垒:https://medium . com/@ arslanshahid-1997/Money-Balling-Cricket-Averaging-Babar-Azam-Runs-ADB 8de 62d 65 b
谢谢大家!
自动气象站上的监测分类模型
原文:https://towardsdatascience.com/monitor-classification-models-on-aws-bf20346fe06
遵循本逐步指南,立即了解您的分类器性能,并在性能下降时得到通知。
当您向客户交付预测时,您希望了解模型的表现。无需等待真/假阳性/阴性,通过计算分类率,您可以立即了解您的模型的性能!
本文主要讨论如何结合 AWS 服务来计算分类率,并在模型漂移时得到通知。

一只眼睛在黑暗的背景下通过镜子观察大脑——由 DALL-E 生成。
分类率计算为有效预测的总和除以预测总数。
分类率=有效预测/总预测
在推断时监控分类率对于以下方面至关重要:
- 知道是不是和训练时的分类率差不多。
- 当分类率下降时检测模型性能下降。
什么是有效的预测?
有效的预测是高于某个阈值的概率。例如,我们可以将所有高于 95%阈值的预测视为有效,将所有低于 95%阈值的预测视为无效。
如果 100 个预测中有 90 个高于 95%的阈值,这意味着 90%的预测是有效的,分类率为 90/100=0,9。
你可以使用阈值来获得正确的分类率,并优化你的模型的性能。
现在我们知道了什么是有效预测,什么是分类率,让我们使用 AWS Cloudwatch 的功能来洞察分类率,并在分类率下降时触发警报。
1.将您的预测作为 JSON 记录到 Cloudwatch
在推断时间记录预测是否有效。下面是一个带有简单示例的 Python 片段:
# prediction_valid = True | False logger.info( json.dumps({
“prediction_valid”: prediction_valid
}))
在 Cloudwatch 日志中,您应该会看到“prediction_valid”出现:

您的 JSON 对象在 Cloudwatch 日志中的样子示例。
Lambda 或 Sagemaker 等许多 AWS 服务都与 Cloudwatch 进行了本机集成,因此如果您的角色拥有正确的 IAM 策略,日志记录将开箱即用。
如果你的服务没有像在 EKS 上运行的 pod 那样与 Cloudwatch 集成,那么看看这个关于如何使用 boto3 登录 Cloudwatch 的 StackOverflow 答案。
2。为有效/总预测定义度量过滤器
我们在上一步将 JSON 数据转储到 Cloudwatch 是有原因的。我们将通过 Cloudwatch 度量过滤器在我们的 Cloudwatch 日志组中查询 JSON 数据,并跟踪有效的和总的预测。
有效预测
- 转到您的 Cloudwatch 日志组>指标过滤器>创建指标过滤器。
使用以下“过滤模式”获得所有有效预测:
{ $.prediction_valid IS TRUE }
更多关于过滤模式语法的信息请点击。

度量过滤器可以过滤日志组中的 JSON 数据。
- 使用“测试模式”按钮根据日志流测试您的过滤器模式。
- 单击下一步。
- 过滤器名称“有效 _ 预测”。
- 度量命名空间“分类率”。
- 度量名称“有效 _ 预测”。
- 度量值“1”,像这样我们计算有效预测的每一次出现。

- 单击下一步,然后单击“创建指标过滤器”

我们为有效的预测创建了一个度量过滤器。同样,我们将为总预测创建一个。
总预测
在步骤 1 中,执行与有效预测完全相同的步骤,但使用以下过滤模式来获得所有预测:
{ $.prediction_valid IS TRUE || $.prediction_valid IS FALSE}
这将查询所有出现的 prediction_valid、true 或 false。
在步骤 2 中,执行
- 过滤器名称“total_predictions”。
- 度量命名空间“分类率”。
- 指标名称“total_predictions”。
- 度量值“1”。
创建 total_predictions 指标过滤器后,您应该会看到以下内容:

3.计算分类率
度量过滤器仅使用创建时的数据,历史数据被忽略!在指标收集到一些数据之前,您可能需要等待片刻。
- 转到云监控>报警>所有报警>创建报警。
- 选择度量。
- 选择名称空间“分类率”。
- 选择“没有维度的度量”。
- 选择复选框“有效预测”和“总预测”。
- 转到图表化指标选项卡。
- 将统计值设置为总和。

总预测和有效预测每 5 分钟绘制一次,我们通过求和进行合计。
- 添加数学>常用>百分比。
- 确保将 valid_predictions 的 id 除以 total_predictions 的 id。

我们计算了分类率,乘以 100 得到一个百分比。
在这个例子中,分类率是 100*(m2/m1) 。其中,m2 是有效预测的 id,m1 是总预测的 id。我们乘以 100 得到一个百分比。
- 编辑标签>分类 _ 比率。
- 仅选择 classification_rate >点击按钮"选择单个指标继续"。
4.配置云监控警报
我们为分类率创建了一个 Cloudwatch 指标。
接下来,我们将向分类率附加一个警报,当分类率低于某个阈值时将触发该警报,我们将收到一封电子邮件。

配置每当超过阈值时触发的警报。
- 将周期设置为 1 小时。
- 将阈值类型设置为“静态”。
- 每当分类率… 低于/等于… 90 时。
我们使用 90 的阈值,这意味着当分类率下降到 90 以下时触发警报。通常,阈值比训练时的分类率要低一点。
- 选择附加配置。
- 报警数据点>将其设置为“1 选 1”。
将闹钟设置为“1/1”会让你收到垃圾信息。我们在教程中这样做是为了更容易触发警报并在我们的收件箱中收到电子邮件。
为了避免收到垃圾邮件,您可以将警报配置为不那么“紧张”,只在多个时间段超过阈值时触发警报。
例如,当您想要在连续 3 个周期违反阈值时触发警报,您将它设置为“3/3”。请记住,在我们的示例中,我们将周期设置为 1 小时,这意味着连续 3 小时阈值被突破。
或者,你可以玩这些数字,例如设置为“5 中 3”。这意味着当 5 个周期中有 3 个周期超过阈值时,将触发警报。
- 缺失数据处理>将其设置为“将缺失数据视为良好(不超过阈值)”。
如果您没有稳定的数据流,您可以考虑将它设置为将丢失的数据视为良好(不超过阈值),这样,在某段时间内数据丢失时,您就不会收到垃圾消息。
- 单击下一步。

- 警报状态触发器>创建新主题。
- 将 SNS 主题的名称设置为“分类 _ 费率 _ 报警”。
- 填写一个电子邮件地址,该地址将在警报触发时接收消息。
- 点击“创建主题”。
- 您将收到一封电子邮件,确认您的订阅。请确保单击该链接。您应该看到这个:

- 返回 AWS 控制台,然后单击下一步。

- 设置警报名称并给出描述。
- 单击下一步。
- 在预览界面中,点击“创建报警”按钮。
在概述中,您应该会看到警报,可能是“数据不足”状态。如果是这种情况,请不要担心,过一会儿,警报已经处理了一些数据,状态将变为“正常”。

当您单击警报时,您应该看到蓝色的分类率和红色的阈值,当它被突破时将触发警报。

指标概述+我们配置的警报。
要测试警报,您可以进入编辑>设置阈值为 100,这样警报就会被触发。几分钟后,您应该会收到一封电子邮件。

分类率低于阈值时发送的电子邮件示例。
我们测试了警报,您可以将阈值设置回原始值。
就是这样!现在,您可以监控分类模型的性能,并在性能下降时得到通知。
文森特·克拉斯
👋在 Medium 、 Linkedin 和 Twitter 上关注我,阅读更多关于 ML 工程和 ML 管道的内容。
脚注
[1]设置 IAM 策略以记录到 cloud watchhttps://docs . AWS . Amazon . com/Amazon cloud watch/latest/logs/IAM-identity-based-access-control-cwl . html
[2]如何在 AWS Cloudwatch 中触发警报https://docs . AWS . Amazon . com/Amazon cloud watch/latest/monitoring/alarmthatsendsemail . html







浙公网安备 33010602011771号