admin管理员组

文章数量:1645532

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

诊断分析—如何进行根本原因分析

原文:https://towardsdatascience/diagnostic-analytics-how-to-conduct-a-root-cause-analysis-4e92583cf008

主动提供对指标变化的真实见解

注意:术语根本原因分析通常在 IT 和数据工程中用作识别故障或问题根本原因的过程。本文重点关注诊断分析,以理解业务指标变化的驱动因素。

rawpixel 在 Freepik 上的图片

为什么收入下降了?为什么转化率会飙升?为什么平均订单值持平?

根据您的行业和业务目标,您的关键指标可能会有所不同。但是如果你关心回答“我如何改进我的关键指标?”,你需要先了解他们为什么先改变。

我看到许多团队努力回答,以确定业务度量变化背后的根本原因。根据他们的诊断分析成熟度,原因会有所不同(参见诊断分析差距)。

这有什么关系?

为了能够提出改进性能的建议,团队首先需要理解他们的度量标准中发生的变化。如果不深入观察,就不可能找到真正的洞察——不仅仅是观察——供团队采取行动(参见文章“如何交付真正的数据驱动洞察”)。

理想情况下,他们应该定期这样做,而不是在观察到指标急剧下降或上升时进行调查。否则,团队将陷入被动的分析循环,这可能会由于延迟的见解和行动而代价高昂。

现状

根据与数十个数据和业务团队的对话和协作,人们可以根据诊断分析的方法、分析彻底性和洞察时间,将诊断分析成熟度分为 4 种状态。

(诊断分析的状态—图片由 Kausa 提供)

卡在什么里面

团队主要描述正在发生的事情(例如,指标正在下降),并将点与高层次的定性事实(例如,业务中发生的事件,如网站更新)联系起来。该流程是非结构化的,不是数据驱动的。他们大多处于救火模式,调查来自业务团队的特别请求。

结果:团队甚至没有意识到的未开发的潜力,削弱的数据文化

常见的疑点(即“业务主导”的方法)

团队非常倾向于只测试通常的怀疑或业务团队提出的假设。虽然从业务单元接收上下文和方向是有用的,但是团队应该不仅仅是测试这些假设。这可能会引入明显的偏见,并使团队忽略宝贵的机会。

结果:决策过程中的重大偏差,导致错失良机。缺乏真正的洞察力。

速度的需求

团队处于诊断分析的更成熟状态,他们认识到深入探究原因的价值。虽然他们有更结构化的方法,但使用现有工作流执行全面的根本原因分析过于复杂和耗时。他们通常不能像业务要求的那样快速获得洞察力。

结果:洞察力发现得太慢,无法产生真正的商业影响和改善决策。

理想状态——全部力量

全面的诊断分析需要基于面向影响的全面根本原因分析的主动方法。

(作者的幻灯片—在诊断分析中发挥全部力量)

快速而全面

团队使用机器学习来增强诊断分析。通过这种方式,他们可以在几分钟内测试变化背后所有可能的驱动因素,而不会牺牲速度或全面性,也不会消除人为偏见。

积极主动的

团队不仅关注剧烈的/令人惊讶的变化,而且根据业务的发展速度,每天/每周保持他们度量的脉搏。他们参加绩效会议时,已经准备好了推动对话的潜在变革驱动因素。每天/每周主动分享这些见解,而不是对业务问题做出反应。

面向影响

在一个最大的市场中,一个小的变化可能仍然很大,但在一个小群体中的重大变化可能会显得更加突出,除非你将真正的影响/实际贡献考虑在内。(此处查看更多)。

当观察一个特定的子群时,它对全局指标变化的贡献必须来源于两个影响:

  • 此子组的给定度量的变化
  • 亚组数量的变化(该亚组在全球人口中的份额)

假设其他条件相同(所有其他条件相同),这两种效应可以单独计算。通过将两种效应相加,你可以得到这个子群对全局度量变化的贡献。

让我们假设您正在查看每周的平均订单价值(AOV)变化。

全球平均订单价值从 50€增加到 52€。

你想了解每个国家对全球变化的贡献。让我们以法国为例:

  • 公制变更: 法兰西的 AOV 从 60€增加到 70€
  • 数量/体积法国订单的份额一直保持在 10%
  • 实际影响:由两种影响之和得出
  • *指标变化:**法国 AOV 订单的增加对全球 AOV 有积极贡献。假设其他条件不变(即法兰西的规模将保持 10%),效果是(70 个€-60 个€)10% = 1 个€
  • 体积变化: 0(假定尺寸保持不变)
  • 考虑到这两种影响,如果其他因素保持不变,全球 AOV 将上移 1 个€(即,这是法国对全球 AOV 变化的贡献)

进行根本原因分析的最佳方法

最佳实践方法包括 3 个关键步骤:将变更缩小到最具影响力的子群体,分析相关/依赖指标,以及将点与所采取的行动、交互和外部事件联系起来。然后,关键是要确保这些见解得到适当的传达和呈现,以推动行动。

1)将变更缩小到最具影响力的子群体

在测试了所有潜在的相关因素以避免人为偏见之后,将变更缩小到驱动大部分变更的子群体。这是人力资源无法完成的部分。即使你花了几天/几周的时间在一个改变上,你也不太可能调查所有的因素并根据影响给它们打分。

使用机器学习/增强分析,可以在几分钟内检查数千甚至数百万个组合,并且只有重要的组合可以根据前面解释的实际影响进行评分。

2)分析相关/依赖指标

在将变更缩小到最有影响力的子群体之后,查看这些子群体的相关/依赖指标是如何演变的。通常,一个指标是其他指标的函数(例如,游戏中的 ROAS 是预期收入、成本和安装的函数)。在这些情况下,记住这个度量树有助于进一步理解事情为什么会发生变化(例如,由于成本激增,ROAS 下降)

3)把这些点连接起来

与业务团队协作,将点点滴滴联系起来,并推动可操作的见解。集体讨论采取了哪些行动,以及交互、事件和可能影响您正在研究的业务指标的外部因素。相关事实可分为 3 组:

  • 内部(即公司采取的行动)
  • 外部和行业特定 —竞争对手采取的行动(例如,新的营销活动)或行业的总体趋势(例如,夏季季节性)会影响您正在寻找的业务指标
  • 外部和特定地理区域 —影响特定区域的更广泛行动(如巴西的选举)

4)有效地展示和交流见解

提前解释关键事实和驱动因素,陈述你的假设并量化影响。开发一致的方法来展示结果(例如,瀑布图、如下所示的备忘录)

例子

让我们回到平均订单价值的例子——AOV

AOV 追踪顾客每次在网站或手机应用上下单时的平均消费金额。产品/市场团队旨在通过测试不同的活动,并随着时间的推移对网站/应用程序进行更改,从而最大化 AOV。

在这种情况下,AOV 从 62.4€增加到 63.7€哇。增长了 2.1%。

(图片由 Kausa 提供—公制变化快照)

由于这不是一个大的峰值或下降,许多团队很可能会忽略这一点。在全员层面,每周都要对 AOV 等关键业务指标进行检查,以发现每一个机会并最大化业务影响。

那么,这 2%从何而来?

通过扩大工作流程,您可以测试影响 AOV 的所有因素——在这个案例中有超过 500,000 种组合——并查看哪些驱动因素具有最大的影响。

(图片由 Kausa 提供—主要驱动因素的优先顺序)

几秒钟之内,我就能看出国家、活动和客户年龄是最大的影响驱动因素。有趣的是,从高层次来看,有相当多的因素相互影响,相互抵消。

让我们看看哪些国家做出了消极和积极的贡献。

(图片由 Kausa 提供——各国的实际贡献)

有趣的是,德国的表现非常好,而法国、美国和韩国表现不佳。

你很好奇为什么德国的表现比其他国家更好。因此,您需要进一步挖掘,以确定活动 ID 是导致德国 AOV 增长的子因素。但是哪一个或哪些活动?

(图片由 Kausa 提供——根据实际贡献优先考虑德国的主要驱动因素)

在德国有一个活动为整个 AOV 贡献了 3.18€。有意思…

(图片由 Kausa 提供——德国的活动根据实际贡献进行优先排序)

现在,您可以查看相关的指标以获得更多的上下文信息。看起来营销团队增加了此次活动的广告支出,从而推动了订单价值和订单量的增长。子群的体积相当小。你可能想要通知我们也可以在其他部分测试的团队。

(图片由 Kausa 提供—查看相关指标)

在你联系营销团队之前,你想看看你是否能收集到任何其他见解。年龄似乎是一个重要的次要因素。该运动在 18-20 岁年龄组中表现尤为突出。

(图片由 Kausa 提供——一起看三维)

太好了!现在,您已经准备好与营销团队交流这些发现,以获得更多的业务背景。

快速签到后,你发现他们正在为这次活动测试新的创意视觉效果。有了这些信息,营销团队开始在其他地区测试该活动,数据团队将密切关注是否在其他国家观察到相同的趋势。您可以将时间分配给更有趣的数据分析项目,而不是花费数小时/数天进行深入研究。

双赢。

底线:影响驱动的方法

从现状来看,提高诊断分析和实现商业价值最大化的机会很大。目前,处理诊断分析的方式有时会让人感觉像是故意伤害(从持续救火到错过最后期限等等)。但这种疯狂是有可能带来逻辑和结构的。从哪里开始?取决于您的起点,但请考虑以下几点:

  • 发展您的现代数据堆栈,使其包含决策智能/诊断分析平台,从而能够扩展工作流
  • 开始测试手头的所有假设,发现被忽视的机会
  • 根据影响指标的驱动因素的实际影响对其进行评分,以便能够关注真正重要的因素
  • 主动与相关利益相关方交流调查结果

想法?伸出手去 若昂索萨处长成长处 考萨 。敬请关注更多关于如何确定诊断分析和增加数据价值的帖子&分析。

Python 中作为代码的图

原文:https://towardsdatascience/diagrams-as-code-python-d9cbaa959ed5

用 Python 创建云系统架构图

照片由玛丽奥拉·格罗贝尔斯卡在 Unsplash 上拍摄

在过去的几年里,我使用了许多不同的工具来绘制系统架构图,包括数据平台和云架构,包括 draw.ioExcalidraw

尽管这些平台提供了各种各样的工具,可以帮助你画出想要的图,但我一直在为一些事情而挣扎。首先,对我来说,与组织中的其他人分享图表并不容易,这样他们就有可能更新或修改它们。其次,对我的图进行版本控制几乎是不可能的,在大多数情况下,我必须保存一个包含元数据的文件,该文件可以用来重新加载一个旧的图。

最后,我的图缺乏一致性——在我看来——这是非常重要的,因为你需要创建几个不同的图,并且必须呈现给用户和同事。我所说的一致性是指能够使用各种图表组件——包括边、节点等。—始终如一。

最近,我遇到了一个 Python 包,它可以让你在没有任何设计工具的情况下,用 Python 代码绘制云系统架构。换句话说,它以代码的形式提供了图表,您可以通过编程的方式绘制图表,同时能够对它们进行版本控制。

图表包

Diagrams 是一个 Python 包,可以用来创建云系统架构图,支持亚马逊 Web Services (AWS)、微软 Azure、谷歌云平台(GCP)、Kubernetes、阿里云、甲骨文云等六大云提供商。此外,它还支持其他常用的技术,如特定的编程语言、框架、聊天系统和更多的节点。同时,您还可以选择创建定制节点,以服务于您的特定用例。

该包需要 Python 3.6 或更高版本,因此在尝试之前,请确保您的主机上有兼容的 Python 版本。此外,您还必须安装Graphviz——一个开源的图形可视化软件,Diagrams package 使用它来呈现图表。

如果你使用的是自制软件,macOS 用户可以通过brew install graphviz下载 Graphviz。同样,安装了 Chocolatey 的 Windows 用户也可以运行choco install graphviz

—图表文档

现在你已经仔细检查了你的 Python 版本并安装了 Graphviz,你可以继续通过pip安装这个包了:

$ pip install diagrams

用 Python 创建云架构图

在将我们的第一个图创建为代码之前,让我们探索一下这个包的一些最基本的组件。

第一个是Diagram,它是表示全局图上下文的主要对象。你可以使用Diagram作为上下文管理器

**from** diagrams **import** Diagram
**from** diagrams.gcp.analytics **import** Bigquery **with** Diagram('My Diagram'):
    BigQuery('Data Warehouse')

上面的代码片段将创建一个由单个 BigQuery 节点组成的图表,该节点是 Google 云平台上的托管数据仓库服务。

这个包的第二个基本组件是Node,它是一个抽象的概念,代表一个单一的系统组件对象。典型的Node由三个基本部分组成;提供方资源类型和**名称。**例如,我们在前面的代码片段中使用的 BigQuery 节点是由gcp提供者提供的,属于analytics资源类型(显然 BigQuery 对应于节点的名称)。请注意,您甚至可以创建自己的自定义节点:

**from** diagrams **import** Diagram, Node**with** Diagram('My Diagram'):
    Node('This is a custom node')

使用这个库创建图的第三个关键组件叫做Cluster,它允许多个节点以一种方式组合在一起,这些节点都与集群中不包含的任何其他节点相隔离。

例如,考虑下面由三个节点组成的图表;一个用于 Google 云存储(这是 Google 云平台上的托管对象存储服务),一个云 SQL 节点(这是 GCP 上的托管 Postgres 服务)和一个本地 MongoDB。

**from** diagrams **import** Cluster, Diagram
**from** diagrams.gcp.database **import** SQL
**from** diagrams.gcp.storage **import** GCS
**from** diagrams.onprem.database **import** MongoDB**with** Diagram('My Diagram', direction='TB'):
  gcs = GCS('Google Cloud Storage')**with** Cluster('Databases'):
  cloud_sql = SQL('Cloud SQL')
  mongodb = MongoDB('MongoDB')

带有数据库集群的示例图—来源:作者

最后,图的最后一个基本组件是Edge——一个表示两个Node对象之间的边的对象,有三个属性;标签、颜色和样式。

**from** diagrams **import** Diagram, Edge, Node **with** Diagram('My Diagram', direction='TB'):
    n1 = Node('n1')
    n2 = Node('n2')
    n3 = Node('n3')
    n4 = Node('n4')
    n5 = Node('n5')
    n6 = Node('n6')   

    n1 >> n2
    n3 - n4
    n5 >> Edge(label='This is a label', color='red') >> n6

不同类型边的示例—来源:作者

创建架构图

现在我们已经了解了用 Python 构建一个图所需的基本对象,让我们使用前面提到的组件创建一个更真实的流程。

在下面的例子中(摘自官方文档),我们创建了一个与 Google 云平台上的消息收集系统相对应的图表。

**from** diagrams **import** Cluster, Diagram
**from** diagrams.gcp.analytics **import** BigQuery, Dataflow, PubSub
**from** diagrams.gcppute **import** AppEngine, Functions
**from** diagrams.gcp.database **import** BigTable
**from** diagrams.gcp.iot **import** IotCore
**from** diagrams.gcp.storage **import** GCS**with** Diagram("Message Collecting", show=False):
    pubsub = PubSub("pubsub")**with** Cluster("Source of Data"):
        [IotCore("core1"),
         IotCore("core2"),
         IotCore("core3")] >> pubsub**with** Cluster("Targets"):
        with Cluster("Data Flow"):
            flow = Dataflow("data flow")**with** Cluster("Data Lake"):
            flow >> [BigQuery("bq"),
                     GCS("storage")]**with** Cluster("Event Driven"):
            with Cluster("Processing"):
                flow >> AppEngine("engine") >> BigTable("bigtable")**with** Cluster("Serverless"):
                flow >> Functions("func") >> AppEngine("appengine")pubsub >> flow

Google 云平台上的消息收集图—来源:文档

最后的想法

架构图非常重要,因为它们清楚地展示了组织中各种组件的工作方式。因此,重要的是让他们正确,并以一种良好、直观和一致的方式呈现他们。

此外,能够容易地共享这些图也很重要,通过这种方式,它们可以容易地被不同的人修改,同时被版本控制。

当涉及到绘制和共享架构图时,作为代码的图是一种可以帮助您朝着这个方向前进的方法。在今天的教程中,我们展示了如何利用diagrams包来用 Python 编程创建图表。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium/membership

相关文章您可能也喜欢

Dickey Fuller 直接评估—测试统计计算速度提高 50 倍

原文:https://towardsdatascience/dickey-fuller-direct-estimation-speed-up-to-50x-test-statistic-computation-af3cb28b9803

通过相关系数直接估计 Dickey-Fuller 检验统计量,避免不必要的回归和矩阵求逆。

图片作者。

Dickey-Fuller 检验可能是时间序列分析中最著名的平稳性(单位根)检验。测试的计算程序依赖于具体统计公式的线性回归结果。然而,线性回归需要矩阵求逆,这可能是计算密集型的,甚至是数值不稳定的。

在这个故事中,我们将探索 OLS(普通最小二乘法)背后的数学,并使用这样的分析来推导 Dickey-Fuller 检验统计量的闭合形式表达式(使用 1 个时滞和一个常数)。所得表达式仅使用相关系数,没有矩阵求逆或计算密集型操作。这可以将计算速度提高 50 倍。

目录

  • 封闭形式表达式推导
  • 封闭形式的表达式结果
  • 健全性检查
  • 转速试验
  • p 值
  • 最后的话

封闭形式表达式推导

如果你想跳过数学细节,滚动到下一节,没有伤害。

对于那些还在这里的人,让我们首先正式阐明这个问题。最多一个滞后和一个常数的 Dickey-Fuller 检验(非扩充)自回归模型规格为:

使用ε i.i.d,该等式可以转换为时间序列增量显式的形式:

通过 OLS 估计α、β及其方差。

迪基-富勒检验统计量定义为:

我们可以用数值方法进行 OLS 回归,得到β和它的方差,然后就到此为止。这将涉及计算量很大的矩阵求逆和矩阵乘法。所以我们不会这么做。

我们可以走的另一条路是做数学。似乎现在我大部分时间都在电脑上捣鼓数字,几乎没有时间在黑板上做实际的数学运算。在这种情况下,做数学确实会有回报。

首先,我们将用矩阵和向量来表示我们的回归

其中 S _d 是由 S 的差组成的维度为 T 的向量,

并且 X 是一个 T x2 矩阵

S _L 为滞后时间序列 ST 维向量,则以下关系成立:

  • S _L 的含义:

  • S _L 的方差:

  • Sd 的意思是:

  • S _d 的方差:

  • S _L 和 S _d 的协方差:

请注意,我们不使用贝塞尔校正,因为由此产生的方程会有更多的项。当有疑问时,就去寻找能产生最佳数学表达式的结果。你可以自己试试,按照下面的步骤使用贝塞尔的方差修正。

矩阵形式的 OLS 估计量为:

其中 T 上标表示矩阵转置。我们有

它的反义词:

因此,

其中,ρ是滞后序列和差异序列之间的皮尔逊相关系数,以及

请注意,即使我们对方差使用贝塞尔校正,α和β的结果也将保持不变。

现在我们需要得到β的方差。根据 OLS,我们得到δ的协方差矩阵为:

那么β的方差为:

在哪里

是回归残差的方差。注意,我们在分母中使用了 T -2,而不是 T ,因为在 OLS,残差只有 T -2 个自由度,因为这两个约束成立:

即,通过构造,残差的平均值为零,回归量和残差之间的协方差为零。

然后,展开残差方差的等式,注意时间序列差异的估计为:

我们知道:

如果我们使用贝塞尔对方差的修正,这个结果会更混乱。

那么我们可以将β的方差表示为

最后,经过我们所有的努力,我们可以写出 Dickey-Fuller 检验统计量的封闭形式:

封闭形式的表达式结果

Dickey-Fuller 检验统计量的封闭表达式的结果是:

其中 T +1 是我们数据的样本量,ρ是滞后时间序列(样本量 T )和差分时间序列(样本量 T )之间的相关系数。

我们唯一需要计算的是相关系数,这比计算 OLS 更有效。这在优化例程和时间序列的实时分析中变得很方便,因为每一毫秒都很重要。

健全性检查

在本节中,我们将把我们的结果与从 Statsmodels (Python)库中获得的结果进行比较。让我们为 Dickey-Fuller 检验统计量定义我们的函数:

我们将使用带有标准正态增量(布朗运动)的 AR(1)单位根过程进行测试:

现在,我们得到我们的直接 Dickey-Fuller 估计和 OLS 方法(Statsmodels)的相对平均误差的估计,即| DF _ stat models—DF _ direct |/| DF _ direct |。这正是下一个函数要完成的。它用随机布朗运动运行“n_tests ”,并返回一个有差异的数组。

运行测试和绘图:

请注意,您的结果会有所不同,因为这个测试是随机的。但对于样本量为 10,000 的时间序列,相对平均误差约为 1%。所以我们的理智检查确实是成功的。然而,在某些试验中,这两种估计方法之间存在微小的差异,这是由于估计方法中的单位根过程引起的数值不稳定性。然而,考虑到计算效率的提高,这是我们可以忍受的。

速度测试

现在是最精彩的部分。在本节中,我们将比较 OLS 方法(Statsmodels)和 Dickey-Fuller 检验的直接估计的速度。下面的代码对这两个函数(方法)中的任何一个进行时间测试。

对 100 到 100,000 的样本大小范围进行测试,并绘制:

我们可以看到,对于大样本量的时间序列,速度提高了约 50 倍,但即使对于较小的样本量,速度也提高了约 10 倍。所以,的确,做数学得到了回报。

p 值

在本节中,我们将编写一个类来获取 Dickey-Fuller 测试直接估计的 p 值。没有 p 值的统计工具是不完整的。我们将使用上面描述(和编码)的 AR(1)单位根过程进行蒙特卡罗模拟。

注意,在这个类中,我们使用了前面章节中的“get_DF”函数和“get_unit_root_proc”。

例如,我们使用 DFProbTable 对象来获得 T=500 的 P 值:

最后的话

至少可以说,这个故事中的数学有点长,但最终,我们得到了一个值得的结果。这里提出的 Dickey-Fuller 统计量公式不仅有助于优化计算效率,而且有助于以另一种方式理解统计量。

还有一个教训需要吸取:有时作为数据科学家,在没有太多底层数学知识的情况下,使用库和建模一切是非常容易的。然而,深入研究数学是一个好主意,不仅仅是作为学习练习,也是获得新的不同见解的一种方式。一知半解是一件危险的事情。

参考

[1] M. L. de Prado,D. Leinweber,协整和子集相关套期保值方法的进展 (2012),《投资策略杂志》,第 1 卷第 2 期,第 67–115 页

[2]https://web . Stanford . edu/~ mrosenfe/SOC _ meth _ proj 3/matrix _ OLS _ NYU _ notes . pdf

[3]http://web . vu . lt/MIF/a . buteikis/WP-content/uploads/PE _ Book/3-2-ols . html

我希望这个故事对你有用。如果我错过了什么,请让我知道。如果你想知道更多这样的故事,请关注我。

https://medium/subscribe/@diego-barba

喜欢这个故事吗?通过我的推荐链接成为媒体会员,可以无限制地访问我的故事和许多其他内容。

https://medium/@diego-barba/membership

Dickey-Fuller 优化:正面解决时间序列协整问题

原文:https://towardsdatascience/dickey-fuller-optimization-tackle-time-series-cointegration-head-on-f924f7c51477

不要介意演习;直接最小化 Dickey-Fuller 统计量以获得平稳的时间序列。完整的 Python 代码。

图片作者。

有许多方法可以找到协整向量。一些方法利用 OLS,其他矩阵特征分解。不管采用哪种方法,通常都要对结果时间序列进行平稳性测试,以此来进行健全性检查。

工作流程是这样的:

  1. 找到协整向量(你选择的方法)
  2. 对步骤 1 的向量生成的时间序列进行平稳性测试,并丢弃虚假结果

参加迪基-富勒测试。通常情况下,我们将在步骤 2 中使用的平稳性检验正是 Dickey-Fuller 检验统计量。

如果我们最终检查 Dickey-Fuller 统计量是否足够小,以说明我们的协整方法是否成功,为什么不首先简单地最小化 Dickey-Fuller 统计量?

本文将通过直接最小化 Dickey-Fuller 检验统计量来解决协整问题。通常,在使用普通函数优化作为算法的主要驱动力之前,我会三思而行。当然,我们可以将 Python 中许多可用库中的任何函数扔给优化器,看看会发生什么。我以前做过很多次了。这通常不是一个好主意。

函数优化可能会出错,尤其是在函数相对未知的情况下;其中包括:

  • 函数调用可能很昂贵
  • 该函数可能有噪声
  • 该函数可以是非凸的

即使优化效果很好,也可能会非常慢。

在这个故事中,我们将解决所有这些问题。我们将编写一个在速度上与约翰森方法和 BTCD 相当的方法,并且具有相同甚至更高的可靠性。然而,会涉及到一些数学问题,所以要小心。为了成功地优化函数(而不是永远这样),我们将计算 Dickey-Fuller 检验统计量的梯度和 Hessian 矩阵。

故事结构

  • 协整,问题设置
  • 迪基-富勒统计,直接估计
  • 梯度
  • 黑森矩阵
  • 轻视
  • 代码摘要
  • 最后的话

如果你想跳过数学,跳过故事的渐变和粗麻布部分。完整的代码将出现在代码摘要部分。

协整,问题设置

实际上,协整是指多个时间序列(过程)的一阶整合。也就是说,这些时间序列(另一个时间序列)的线性组合是平稳的。谁不喜欢平稳的时间序列?

数学上,给定 N 个时间序列 *P_i,i=1,2,…, .n,样本大小为**T+1,*我们试图找到一个向量 w 与分量 w_i 这样的线性组合:

是静止的。

给定向量w(“w _ vec”)和进程 P_i ,编码为矩阵(“p_mat”),每列一个进程,得到 S 非常简单:

迪基-富勒统计,直接估计

我们要解决的第一件事是优化函数调用速度。通常,为了计算迪基-富勒检验,我们会做 OLS 回归。那是昂贵的。相反,我们对测试统计数据使用直接估计:

其中 ρ_SL,Sd 是滞后 S 序列( S_L )和差分 S 序列(S_d)的相关性(Pearson)。注意 S_LS_d 的样本量都是 T

如果你想知道更多关于上述结果的细节,请查看我的之前的故事和数学证明。

使用这种形式的 Dickey-Fuller 统计更快,因为计算相关系数在计算上比 OLS 矩阵求逆和乘法更有效。此外,它还有另一个优点;统计量对向量 w 的依赖性是清楚的。因为相关性是:

并且 S_LS_d 的协方差和方差可以使用协方差矩阵和 w 向量写成二次型,即

其中 V_L 为滞后 P_i 时间序列的协方差矩阵, V_d 为差分 P_i 时间序列的协方差矩阵, V_L,dV_d,L 为滞后差分时间序列的互协方差矩阵,定义为:

具有以下属性:

上标表示矩阵转置。

对谈话进行编码时,我们用计算中需要的基本变量初始化“_SInit”对象:

然后是另一个对象“_ PCovMatrices”,它包含我们需要的协方差矩阵:

最后,滞后和差异 S 系列的标准偏差对象:

有了编码的初始对象,我们现在可以陈述我们的目标函数,Dickey-Fuller 统计直接估计:

梯度

DF (迪基-富勒)相对于 w 的梯度为:

相关性的梯度是:

使用二次型的梯度,标准差和协方差的梯度为:

因此,

编码渐变:

黑森矩阵

为了得到 Hessian 矩阵,我们将使用张量微积分,因为我们需要处理矩阵对向量的导数,以及这类有趣的数学,所以张量微积分是最适合的。

我们将使用爱因斯坦的重复指数求和约定;因为我们有许多指数,所以求和符号会使一切变得更加复杂。此外,我们将使用以下简化:

在协方差矩阵中,我们将下标字段用于索引:

索引将用希腊字母书写,以区别于其他下标和上标。

黑森是:

在哪里

是相关的梯度,用向量微积分符号表示

假设梯度是列向量。

我们需要计算ρ的海森数。也就是说,让我们定义我们将使用的两个矩阵:

和一个 4 张量:

那么ρ的梯度(在前面的部分中已经在向量微积分符号中)在张量微积分符号中是:

很难不喜欢张量微积分的优雅。

最后,ρ的黑森式是:

虽然它看起来像广义相对论中坍缩黑洞的方程(开玩笑),但用 Python 编写这个非常简单,这要感谢 NumPy 的“einsum”:

轻视

一旦我们有了目标函数、梯度和 Hessian,最小化代码块就非常简单了。本质上,我们用协整问题的参数来包装 SciPy 的最小化函数:

  • “p_mat”是 P_i 过程矩阵。
  • “w_vec_init”是对 w_vec 的初始猜测。不供一个也不用担心;将生成一个随机猜测。
  • “方法”只能取两个值,要么是“信任-克雷洛夫”,要么是“信任-精确”。

注意:这不是一个全局优化器,所以给定一个初始的 w 向量对于收敛是必不可少的。在某些问题中,随机选取的向量足以收敛。但是,最好是针对您的特定情况生成一个更好的初始猜测。

我们将生成离散采样的相关布朗运动(单位根过程),并使用该矩阵作为“p_mat”来测试算法。如果你不知道布朗运动或如何产生它们,不要担心;看看我之前关于这个话题的报道。我们将使用那个故事中的代码来生成“p_mat ”,因此将来自布朗运动故事的代码保存为“brownian_motion.py ”,并将其放在运行以下代码的同一个目录中。

最小化并绘图:

图片作者。

一切都按计划进行。

代码摘要

为了完整起见,这里是故事中开发的所有代码。通过最小化 Dickey-Fuller 检验统计量来获得协整向量所需的代码:

最后的话

开发的优化算法按预期工作。速度不如特征分解算法,但不相上下。它非常健壮和可靠。

然而,唯一的缺点是协整向量的初始猜测的选择。我已经看到该方法对于初始猜测的错误选择是非常宽容的;大多数时候,随机的初始猜测非常有效。然而,做出好的选择可以提高解决方案的速度和可靠性。记住,这是局部优化器,不是全局优化器。

最后,使用梯度和 hessian,很容易将此代码扩展为约束优化,这更适合于现实世界的问题。可以很容易地将约束添加到该方法中。特征分解方法必须重写以适应约束。

参考

[1] M. L. de Prado,D. Leinweber,协整和子集相关套期保值方法的进展 (2012),《投资策略杂志》,第 1 卷第 2 期,第 67–115 页

我希望这个故事对你有用。在 Medium 上关注我,如果你想要更多这样的故事,请订阅。

https://medium/subscribe/@diego-barba

如果我错过了什么,请让我知道。对于任何质疑、批评等。,留言评论。

喜欢这个故事吗?通过我的推荐链接成为媒体会员,可以无限制地访问我的故事和许多其他内容。

https://medium/@diego-barba/membership

如何将字典转换成熊猫数据框架

原文:https://towardsdatascience/dict-to-pandas-df-29ba731fc256

使用 Pandas 将 Python 字典转换为数据帧

照片由大卫·舒尔茨在 Unsplash 上拍摄

[pandas](https://pandas.pydata/)是 Python 生态系统中最受欢迎的库之一,它通过提供直观而强大的 API 让开发人员与数据进行交互,以快速有效的方式用于数据分析和操作。

使用 Python 和 pandas 时,最常见的任务之一是将字典转换为数据帧。当您想要对当前存储在字典数据结构中的数据进行快速分析甚至可视化时,这将非常有用。

在本文中,我们将探讨如何以几种不同的方式将 Python 字典转换成 pandas 数据框架,这取决于数据最初在 dict 中是如何构造和存储的。

从 Python 字典创建熊猫数据框架

现在,为了从 Python 字典中创建一个 pandas 数据帧,我们可以使用pandas.DataFrame.from_dict方法,该方法用于从类似数组的 dict 或 dicts 对象中构造数据帧。

让我们用一些虚拟值创建一个示例 Python 字典,我们将在接下来的几节中使用这些虚拟值来演示一些将其转换成 pandas 数据帧的有趣方法。

users = {
  'fist_name': ['John', 'Andrew', 'Maria', 'Helen'],
  'last_name': ['Brown', 'Purple', 'White', 'Blue'],
  'is_enabled': [True, False, False, True],
  'age': [25, 48, 76, 19]
}

在这个示例字典中,键对应于数据帧的列,而列表中的每个元素对应于特定列的行值。因此,我们可以(可选地)指定orient等于'columns'

数据的“方向”。如果传递的字典的键应该是结果数据帧的列,则传递’ columns '(默认)

— 熊猫文档

import pandas as pd 

users = {
  'fist_name': ['John', 'Andrew', 'Maria', 'Helen'],
  'last_name': ['Brown', 'Purple', 'White', 'Blue'],
  'is_enabled': [True, False, False, True],
  'age': [25, 48, 76, 19]
}

df = pd.DataFrame.from_dict(users)

我们刚刚使用 Python 字典创建了一个熊猫数据框架!

print(df)

  fist_name last_name  is_enabled  age
0      John     Brown        True   25
1    Andrew    Purple       False   48
2     Maria     White       False   76
3     Helen      Blue        True   19

这种方法仅适用于字典中的数据是以每个键对应于 DataFrame 列的方式构造的情况。如果我们有不同的结构会怎么样?

将字典关键字填充为行

现在让我们假设我们有一个字典,它的键对应于我们想要创建的数据帧的行。

users = {
  'row_1': ['John', 'Brown', True, 25],
  'row_2': ['Andrew', 'Purple', False, 48],
  'row_3': ['Maria', 'White', False, 76],
  'row_4': ['Helen', 'Blue', True, 19],
}

在这个场景中,我们必须使用下面代码片段中展示的orient='index'选项,这样字典中的每个键值对都被解析为一个 DataFrame 行。但是请注意,当使用orient='index'时,我们必须在调用from_dict()方法时显式指定列名:

import pandas as pd

users = {
  'row_1': ['John', 'Brown', True, 25],
  'row_2': ['Andrew', 'Purple', False, 48],
  'row_3': ['Maria', 'White', False, 76],
  'row_4': ['Helen', 'Blue', True, 19],
}

cols = ['first_name', 'last_name', 'is_enabled', 'age']
df = pd.DataFrame.from_dict(users, orient='index', columns=cols)

我们又一次设法用 Python 字典构造了一个 pandas 数据帧,这次是通过将每个键值对解析为一个数据帧行:

print(df)

      first_name last_name  is_enabled  age
row_1       John     Brown        True   25
row_2     Andrew    Purple       False   48
row_3      Maria     White       False   76
row_4      Helen      Blue        True   19

您可能已经注意到,每个键也成为新填充的数据帧的索引。如果您希望删除它,可以通过运行以下命令来实现:

df.reset_index(drop=True, inplace=True)

现在应该重置索引:

print(df)

  first_name last_name  is_enabled  age
0       John     Brown        True   25
1     Andrew    Purple       False   48
2      Maria     White       False   76
3      Helen      Blue        True   19

紧密定向选项

从 pandas v1.4.0 开始,当从 Python 字典构造 pandas 数据帧时,您也可以使用orient='tight'选项。该选项假设输入字典有以下按键:'index''columns''data''index_names''column_names'

例如,以下字典符合此要求:

data = {
  'index': [('a', 'b'), ('a', 'c')],
  'columns': [('x', 1), ('y', 2)],
  'data': [[1, 3], [2, 4]],
  'index_names': ['n1', 'n2'],
  'column_names': ['z1', 'z2']
}

df = pd.DataFrame.from_dict(data, orient='tight')

print(df)
z1     x  y
z2     1  2
n1 n2      
a  b   1  3
   c   2  4

在构建多索引数据框架时,最后一种方法通常很有用。

最后的想法

将 Python 字典转换成 pandas 数据框架是一个简单明了的过程。通过根据原始字典的结构方式使用pd.DataFrame.from_dict方法和正确的orient选项,您可以很容易地将数据转换成 DataFrame,这样您现在就可以使用 pandas API 执行分析或转换。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium/membership

相关文章你可能也喜欢

金钱跟着球走了吗:分析金钱球前后棒球击球统计的重要性

原文:https://towardsdatascience/did-the-money-follow-the-ball-analyzing-the-importance-of-baseball-batting-statistics-pre-144d7d452e1f

在这篇文章中,我们探讨了击球统计数据的重要性,如 OBP,SLG 和 AVG 是如何随着球员工资的变化而变化的

图片由吉米·科诺弗在 Unsplash 上拍摄

介绍

在迈克尔·刘易斯*的《金钱球:赢得不公平比赛的艺术》中,*奥克兰运动家队总经理比利·比恩因发现了棒球运动员市场的低效而受到赞誉。这本获奖的书中反复出现的一个故事是关于棒球中的保送。

传统上,棒球教练、球探和评论家主要根据击球手的击球能力,即击球率(SLG)来识别击球天赋。然而,比恩和他的团队,通过各种数据和性能分析技术,意识到画保送的能力是一项被其他棒球组织严重低估的技能。当 Beane 领导下的奥克兰运动家队开始雇佣基于保送能力的球员时,他们踏上了未知的领域,因为这是一项通常被棒球界低估的技能。比利·比恩和他的统计学家能够更准确地识别击球人才,并设法形成一个获胜的团队,尽管资金紧张。

《Moneyball》的出版分散了整个棒球圈的这种明显的低效率,并对球队策略和球员招募产生了广泛的影响。钱球的故事中有一个微妙的经济基础,它与市场的运作有关,两位经济学家 Jahn K. Hakes 和 Raymond D. Sauer 强调了这一点。他们指出,如果畅销故事确实是正确的,那么以下两个论点应该成立:

  1. 在 *Moneyball 之前,*保送的能力(由 OBP 的上垒率反映)应该对球队的输赢率有影响,但球员的工资不应受到影响,因为在 Moneyball 之前,保送被认为是一项被低估的技能。
  2. 有能力获得更多保送(更高的 OBP)的球员应该有更高的薪水,因为在这本书出版后,球队应该改变他们的策略来掩盖他们早期方法中的低效率。

Hakes 和 Sauer 决定看看他们是否真的能证明这个假设。在一篇名为 对钱球假说 (2006)的经济评估的研究论文中,他们实际上证明了书中提出的论点确实站得住脚,并得到了数据的支持。

因此,在这篇文章中,我们将着手重现 Hakes 和 Sauer 论文中的表 3 (附后),两位作者在论文中测试并展示了假设的结果,即我们应该预期观察到工资与 OBP 的强相关,而不是与 SLG 的强相关。他们通过对 OBP、SLG 和他们认为可能影响球员工资的其他因素运行击球手工资的回归模型来实现这一点:本垒板出场次数、仲裁资格和自由代理权,以及击球手是担任捕手还是内野手。除了这些因素,我们还将把击球率纳入我们的回归分析。

作者图片

定义

SLG: (单打+ 2x 双打+ 3x 三垒+ 4 x 全垒打)/ At bats
OBP: (安打+保送+被投球击中)/(At bats +保送+被投球击中+牺牲飞人)
log(薪水)= B0+B1 * OBP+B2 * SLG+B3 * PA+B4 * Arb+b5 *自由人+B6 *捕手+B7 *内野手

数据

为了在本文中进行分析,我们将使用肖恩·拉赫曼的棒球数据库来收集球员的工资和击球数据,并使用数据库中的出场文件为仲裁/自由代理和防守位置创建数据。

薪资数据

作者图片

一旦导入了所有需要的包,我们就将薪水数据加载到一个名为 Salary 的数据框架中。我们有 26,428 行关于每支球队球员个人工资的数据,这些数据从 1985 年持续到 2016 年。

Salary = Salary[Salary['salary'] > 0]
Salary['log_salary'] = np.log(Salary['salary'])
Salary = Salary.rename(columns = {'yearID':'salary_year'})
Master = Salary

然后,我们检查薪水栏中缺失的值,以确保在特定赛季没有薪水记录的球员从数据框中删除。接下来,考虑到我们可以支配的工资范围很广,我们将通过取其对数来转换工资变量。这样做是为了确保非常大的工资值不会最终扭曲预测模型。我们还将把 yearID 变量重命名为 salary_year。这将有助于我们稍后的分析,那时我们将在这个专栏上合并不同的数据框架,并且我们将分析球员在特定年份的表现和他们在相应年份的工资。最后,我们将把薪水数据帧复制到数据帧中,这将是我们的核心数据帧,我们将在整个分析过程中从这里进行操作。

击球数据

作者图片

**注:**拉赫曼数据库中的击球档案涵盖了自 1871 年以来的所有大联盟球员,并且每年更新一次。

击球数据帧中有 102816 行,由每个球员的原始棒球统计信息组成。此外,在一个赛季中转换球队并为不止一个球队打球的球员将为他的第一队获得一个名额以及相应的击球次数,为第二队获得两个名额以及该队的相应统计数据。因此,为了考虑球员在整个赛季中的表现,我们通过按球员和年份分组来汇总各阶段的数据。

Batting = Batting.groupby(['playerID','yearID']).sum()
Batting.reset_index(inplace=True)
Batting = Batting[(Batting.yearID >= 1998) & (Batting.yearID <= 2006) & (Batting.AB >= 130)]

我们还将我们的数据限制在 1998 年到 2006 年之间,以便充分分析金钱球年前后的年份。此外,我们只包括那些在职业棒球赛中至少有 130 次击球的球员。分数低于这个数字的玩家被认为是新手。

**注:**在拉赫曼击球数据框架中,单打和板的外观没有单独给出,但它们可以定义如下:

**单打:**安打——全垒打——三垒打——双打
**板出场:**击球+保送+投球+安打+牺牲安打+牺牲飞人

Batting['PA'] = Batting['AB'] + Batting['BB'] + Batting['HBP'] + Batting['SH'] + Batting['SF']Batting['OBP'] = (Batting['H'] + Batting['BB'] + Batting['HBP'])/(Batting['AB'] + Batting['BB'] + Batting['HBP'] + Batting['SF'])Batting['SLG'] = ((Batting['H'] - Batting['Doubles'] - Batting['Triples'] - Batting['HR']) + 2*Batting['Doubles'] + 3*Batting['Triples'] + 4*Batting['HR'])/Batting['AB']Batting['AVG'] = Batting['H']/Batting['AB']Batting['salary_year'] = Batting['yearID'] + 1

为了结束这一小节,我们将使用现有的击球数据计算击球出场数(PA)、上垒率(OBP)、击球率(SLG)和击球率(AVG)。我们还创建了一个 salary_year 变量,方法是在 yearID 变量上加 1,以便将今年的薪水与去年的击球统计数据相匹配——逻辑是薪水是基于击球表现的,因此球员的表现应该是可以观察到的。

和以前一样,我们将把我们的击球数据帧复制到数据帧中,这是我们正在维护的核心数据集。这里是在击球统计数据被复制后的一瞥。

作者图片

球员数据

继续,我们现在创建另外两个数据帧:和*外表。*我们希望考虑球员在工资谈判方面的地位,通常有三种球员类别:新秀(0-2 年的经验,他们讨价还价的能力有限,他们没有谈判能力),符合仲裁条件(如果球员有 2-6 年的经验,并且对他们的工资不满意,他们可以向独立仲裁人提出争议,仲裁人将为球员决定公平的工资), 和自由代理(如果玩家的经验超过 6 年,他们实质上成为自由的,可以向最高出价者出售他们的服务/与最高出价者签订合同)。

Debut = People[['playerID','debut']].copy()
Debut['debut_year'] = Debut['debut'].astype(str).str[0:4]
Debut = Debut[['playerID','debut_year']]

为了将每个玩家归入这些类别中的一个,我们首先要找出每个玩家的经验。我们可以通过找出每个球员首次亮相的年份,然后计算从那以后的年份来做到这一点。

Master = pd.merge(Master, Debut, on=['playerID'], how = 'left')
Master['experience'] = Master['yearID'] - Master['debut_year'].astype(int)
Master['arbitration'] = np.where((Master['experience'] <= 6) & (Master['experience'] >= 3),1,0)
Master['free_agent'] = np.where(Master['experience'] > 6, 1, 0)

现在,我们将出道年份变量合并到我们的主数据框架中,并为三个玩家状态类别中的每一个创建三个二进制变量。

为回归分析准备数据的最后一步是确定每个球员的防守位置。虽然在棒球中有 8 个守备位置,Hakes 和 Sauer 使用了两个类别:接球手和内野手被他们定义为二垒、三垒或短停,默认类别被给予剩余的守备位置。出场的防守位置如下:

(G_c):接球手
(G_1b):一垒
(G_2b):二垒
(G_3b):三垒
(G_ss):短停
(G_of):外场手
(G_dh):指定击球手

def Position(df):
    if (df['max_games'] == df['G_c']): return "C"
    elif (df['max_games'] == df['G_1b']): return "1B"
    elif (df['max_games'] == df['G_2b']): return "2B"
    elif (df['max_games'] == df['G_3b']): return "3B"
    elif (df['max_games'] == df['G_ss']): return "SS"
    elif (df['max_games'] == df['G_of']): return "OF"
    elif (df['max_games'] == df['G_dh']): return "DH"Appearances = Appearances.groupby(['playerID','yearID'])['G_c','G_1b','G_2b','G_3b','G_ss','G_of','G_dh'].sum()                                       
Appearances.reset_index(inplace=True)
Appearances['max_games'] = Appearances[["G_c","G_1b","G_2b","G_3b","G_ss","G_of","G_dh"]].max(axis=1)
Appearances['position'] = Appearances.apply(Position, axis = 1)

再说一次,我们必须注意限制,因为一个球员可能在一个赛季中为不止一个球队效力,因此,他在两个球队的上场次数都需要考虑。因此,我们通过对球员和年份 id 进行分组来计算每个位置的总和。

这里要注意的另一件重要的事情是,虽然大多数球员主要在一个位置上比赛,但也有一些球员在一个赛季中在不止一个位置上比赛。因此,为了“定义”一个特定球员的防守位置,我们将假设该球员在整个赛季中的位置是他参加比赛次数最多的位置。因此,我们需要将出场中的数据转换成 Hakes 和 Sauer 在他们的研究论文中使用的格式。

Appearances = Appearances[Appearances['max_games'] > 0] 
Appearances = Appearances[['playerID','yearID','position']]
Appearances['catcher'] = np.where(Appearances['position'] == "C", 1, 0)
Appearances['infielder'] = np.where((Appearances['position'] == "2B") | (Appearances['position'] == "3B") | (Appearances['position'] == "SS"), 1, 0)Master = pd.merge(Master, Appearances, on=['playerID','yearID'], how = 'left')

最后,我们将排除非位置球员,只保留必要的变量用于演示。我们还将为我们的数据帧定义捕手和内野手虚拟变量,使用与 Hakes 和 Sauer 在将其合并到数据帧之前使用的相同定义,现在看起来是这样的:

作者图片

回归分析

在将我们所掌握的数据组织成一个统一的数据框架后,我们现在能够像 Hakes 和 Sauer 在他们的研究论文中所做的那样定义和运行我们的回归模型。如上所述,我们将使用以下公式作为回归方程:

日志(工资)= B0+B1 * OBP+B2 * SLG+B3 * PA+B4 * Arb+b5 *自由人+B6 *捕手+B7 *内场手

首先,我们将把我们的数据帧分成数据集: MB_Data_1MB_Data_2。前者包括《Moneyball》出版前(2000-2003 年)的季节性数据,后者涵盖该书出版后的三年(2004-2006 年)。

MB_Data_1 = Master[(Master.salary_year >= 2000) & (Master.salary_year <= 2003)]
reg1 = smf.ols(formula = 'log_salary ~ OBP + SLG + AVG + PA + arbitration + free_agent + catcher + infielder', data=MB_Data_1).fit()
reg1.summary()

作者图片

如果我们现在将回归模型返回的系数与 Hakes 和 Sauer 论文的表 3 中给出的系数进行比较,我们可以观察到,总的来说,这些系数非常相似。即使是标准误,表 3括号中的数字也和我们回归模型返回的数字差不多。根据 R 平方值,69.1%的对数工资方差由自变量解释。我们还可以看到,在《金钱球》出版之前的几年里,OBP 和 AVG 在统计上并不显著。

MB_Data_2 = Master[(Master.salary_year >= 2004) & (Master.salary_year <= 2006)]
reg2 = smf.ols(formula = 'log_salary ~ OBP + SLG + AVG + PA + arbitration + free_agent + catcher + infielder', data=MB_Data_2).fit()
reg2.summary()

作者图片

在《Moneyball》出版后的三年里,观察回归模型的结果,第一个值得注意的事情是,OBP 和 AVG 现在在统计上是显著的。不仅如此,OBP 系数已经飙升至 5.4004,几乎是 SLG 系数的两倍。这反过来表明,在后金钱球时代(2004 年至 2006 年),OBP 在决定球员薪酬方面变得更加重要了约 45%。

SLG 在 Moneyball 之前和之后仍然具有统计意义,但它没有 OBP 在 2004 年之后那么重要。另一件要注意的事情是,在这本书出版之前和之后,这两个守备位置在决定球员工资方面都没有统计学意义。最后,有趣的是,在后金钱球时代,球队并不看重平均击球率来决定球员的薪水,这似乎与传统的平均击球率观点相反。

MB_Data_2000 = Master[(Master.salary_year == 2000)]
MB_Data_2001 = Master[(Master.salary_year == 2001)]
MB_Data_2002 = Master[(Master.salary_year == 2002)]
MB_Data_2003 = Master[(Master.salary_year == 2003)]
MB_Data_2004 = Master[(Master.salary_year == 2004)]
MB_Data_2005 = Master[(Master.salary_year == 2005)]
MB_Data_2006 = Master[(Master.salary_year == 2006)]reg_2000 = smf.ols(formula = 'log_salary ~ OBP + SLG + AVG + PA + arbitration + free_agent + catcher + infielder', data=MB_Data_2000).fit()
reg_2001 = smf.ols(formula = 'log_salary ~ OBP + SLG + AVG + PA + arbitration + free_agent + catcher + infielder', data=MB_Data_2001).fit()
reg_2002 = smf.ols(formula = 'log_salary ~ OBP + SLG + AVG + PA + arbitration + free_agent + catcher + infielder', data=MB_Data_2002).fit()
reg_2003 = smf.ols(formula = 'log_salary ~ OBP + SLG + AVG + PA + arbitration + free_agent + catcher + infielder', data=MB_Data_2003).fit()
reg_2004 = smf.ols(formula = 'log_salary ~ OBP + SLG + AVG + PA + arbitration + free_agent + catcher + infielder', data=MB_Data_2004).fit()
reg_2005 = smf.ols(formula = 'log_salary ~ OBP + SLG + AVG + PA + arbitration + free_agent + catcher + infielder', data=MB_Data_2005).fit()
reg_2006 = smf.ols(formula = 'log_salary ~ OBP + SLG + AVG + PA + arbitration + free_agent + catcher + infielder', data=MB_Data_2006).fit()Header = ['2000','2001','2002','2003','2004','2005','2006']
Table_3 = summary_col([reg_2000,reg_2001,reg_2002,reg_2003,reg_2004,reg_2005,reg_2006,],regressor_order =['OBP','SLG','AVG','PA','arbitration','free_agent','catcher','infielder'],stars=True,float_format="'%.3f'",model_names = Header)
print(Table_3)

我们可以格式化和呈现我们的回归结果的另一种方式是如何在表 3 中完成的。为此,我们需要有一个每年(2000-2006)的回归模型。使用 statsmodels 的 summary_col 功能,我们能够说明每个季节中每个变量的系数和标准误差。因此,下面所附的表格实质上是我们对 Hakes 和 Sauer 表 3 的复制。

作者图片

因此,我们能够重申 Moneyball 故事中的关键叙述,并得到 Hakes 和 Sauer 出版物的证实:在 Moneyball 之前的时期(2000-2003 年),slugging 百分比对工资的影响几乎是 on-base 百分比的两倍,而后者在那个时期是微不足道的。然而,在 Moneyball 之后(2004-2006),上垒率不仅在统计上变得显著,而且在决定球员工资方面,它几乎是击球率的两倍重要,这是比利·比恩和他的统计学家支持的观点。

关于这个话题的更多信息

体育分析中的毕达哥拉斯期望,以及不同体育项目的例子

英超(EPL)毕达哥拉斯预测者

参考文献

  1. Jahn K. Hakes 和 Raymond D. Sauer 对钱球假说的经济评估
  2. 金钱球和超越由密歇根大学
  3. 肖恩·拉赫曼的数据库许可证

机器学习中偏差和方差的区别

原文:https://towardsdatascience/differences-between-bias-and-variance-in-machine-learning-7f79110626da

茱莉亚·佐洛托娃在 Unsplash 上的照片

机器学习数据科学在最近十年里获得了很多关注。我们看到无人驾驶汽车、垃圾邮件过滤、检测制造单位的缺陷和人脸识别等众多应用。再者,如果成功实现机器学习和人工智能,估计公司可以达到高身价。然而,有时候机器学习模型并没有被 ML 的从业者彻底理解,这常常会导致困惑和沮丧。我们现在要讨论的概念分别是偏差方差。这些主题在大量的在线课程中有所涉及,但是值得记下它们之间的差异,并理解必须采取的正确步骤来克服它们。事不宜迟,让我们开始更详细地了解这些主题。

什么是偏见?

在 Unsplash 上由 Pablo Arroyo 拍摄的照片

当我们使用机器学习模型时,我们做的第一件事就是使用我们的数据并训练我们的模型,以在他们以前没有见过的数据上获得预期的结果。为了做到这一点,我们不断地训练我们的模型,并更新权重和参数,直到我们得到预期的结果。在这样做的时候,可能会有这样的情况,我们没有完全训练好模型,它们对于我们的机器学习预测任务来说太简单了。换句话说,我们只是使用简单的模型,而不是调整那些参数或改变模型来反映数据集中显示的复杂性。在这种情况下,我们看到模型(用于训练)有很高的偏差。因此,如果数据集存在高偏差,它就不能很好地理解数据集。

如何克服偏高?

在我们没有充分探索这些算法的潜力的机器学习中,具有高偏差可能是一个问题。因此,我们可以看看可以在很大程度上减少偏差的各种方法。

不太复杂的模型 —高偏差的主要原因是模型不够复杂,无法捕捉数据集中的复杂性以及输入和输出之间的关系。我们可能必须采取正确的措施来克服这种情况,并利用机器学习的全部力量。克服较高偏差的最佳方法是添加更复杂的预测模型。

交叉验证数据 —克服高偏倚的第二种方法是使用交叉验证数据,以便它可以帮助识别高偏倚问题。当我们在测试我们的模型之前将数据分为训练和交叉验证时,我们正在调整超参数并改变它们,以确保它们在交叉验证数据上表现良好。

寻找正确的数据 —另一个方便的方法是使用正确的数据而不偏向模型。尽管我们的模型过于复杂,并且能够理解数据中的错综复杂,但也有可能仅仅因为数据不包含输入和输出之间的关系或包含最小的关系,它们就失败了。因此,在确定模型是否具有高偏差之前,您可能还希望检查输入与输出的相关程度。

什么是方差?

CALIN STAN 在 Unsplash 上拍摄的照片

可能存在模型在训练数据上表现异常出色的情况。但是,当我们试图在它以前没有见过的数据上判断性能时,它失败了,或者至少没有在测试数据上给出预期的性能。在这种情况下,我们可以说模型过度拟合,并且具有很高的方差。换句话说,模型只是从训练数据中学到了很多,而不能对以前没有见过的数据进行很好的概括。这与我们日常生活中学生准备考试的情形非常相似。如果学生只从课本中学习,而不去探索其他来源,很有可能学生不能很好地理解课本中没有的新例子或信息。在这种情况下,学生在准备考试时过于强调课本,而不能对题目有一个大致的了解或了解。用外行人的话来说,这就是所谓的过度适应。

过度拟合可能是机器学习中的一个问题,在这种情况下,从业者得到的是 ML 模型性能的夸大图,并假设该模型可以对其之前未见过的数据执行类似的操作。然而,在大多数情况下,这远非事实。由于我们正在通过获取训练数据来优化和修改我们的算法,因此它在这些数据上表现良好也就不足为奇了。然而,当我们将新数据(测试数据)放入我们的模型时,它有时可能会失败,并且在训练数据和测试数据上的表现之间有很大的差异。因此,一个从业者必须花时间去了解和知道模型是否过拟合。以下是一些有助于在很大程度上减少过度拟合的方法。

如何克服高方差?

可以有各种步骤来帮助我们的 ML 模型减少过度拟合(高方差)。现在让我们来探索下面的每一种方法。

正则化— 这是一种技术,用于惩罚高度复杂的 ML 模型,并且权重从训练数据中捕获大量趋势。使用正则化可以确保通常分配的权重减少,并且在基于输入确定输出时不会起很大作用。换句话说,这使得模型能够很好地概括,而不需要过多地关注训练数据,并且还能够对他们以前没有见过的数据给出良好的结果。

集成— 当我们过于依赖一个 ML 模型而不考虑一个群体时,它就有可能过度拟合数据。另一方面,从模型(集合)列表中获取输出可以确保在决定模型的最佳结果之前考虑不同的可能输出。

减少特征— 数据中包含大量特征(高维度)有时也会导致模型从训练数据中学习过多。更糟糕的是,在训练数据中容易获得的特性,如果在测试阶段不可用,可能会使这些看不见的数据的结果不太准确。因此,减少特征并赋予模型对测试数据进行良好概括的能力是一种简便的方法。

添加训练数据— 如果我们的训练数据较少,这意味着 ML 模型没有那么复杂**,它只是使用输入和输出之间的关系来对以前没有见过的数据进行预测。添加更多的数据会使它变得更加复杂,模型可能需要付出额外的努力来确定输入和输出之间的正确关系,然后再对看不见的数据进行预测。**

进行交叉验证 —在实时数据上进行性能测试之前,将数据分为训练数据和交叉验证数据两部分的过程。最初,使用训练数据来训练 ML 模型,然后使用超参数的适当调整在交叉验证数据上观察性能。一旦 ML 模型在训练和交叉验证数据上的性能之间没有巨大差异,从业者可以停止训练过程,并使用这个被超参数调谐到的模型来测量其在实时数据上的性能,以减少高方差的问题。

使用提前停止 —人工智能的一些最受欢迎的应用是在深度学习领域。有大量的定制可用于网络,还有一个流行的机制叫做“迁移学习”,它正在彻底改变这个领域。有时,这些网络可能变得太复杂,并且从训练数据中学习得太好,而没有能力对测试数据进行很好的概括。因此,在训练阶段使用早期停止等方法会很方便,并导致高方差的减少。

结论

通读完这篇文章后,希望你发现这篇文章在执行任何与 ML 相关的查询或任务时是有帮助的并且是可操作的。在诊断模型和获得正确的解决方案之前,理解高方差和高偏差之间的区别是有用的。此外,用 ML 克服这些挑战对整个数据周期的发展有很好的影响。

如果你想获得更多关于我的最新文章的更新,并且每月只需 5 美元就可以无限制地访问中型文章,请随时使用下面的链接来添加你对我工作的支持。谢了。

https://suhas-maddali007.medium/membership

以下是您联系我或查看我作品的方式。

GitHub: 苏哈斯·马达利(Suhas Maddali)(github)

****YouTube:https://www.youtube/channel/UCymdyoyJBC_i7QVfbrIs-4Q

****LinkedIn:(1)Suhas Maddali,东北大学,数据科学| LinkedIn

中等: 苏哈斯·马达利——中等

使用 SQL 的 BigQuery 中编号函数之间的差异

原文:https://towardsdatascience/differences-between-numbering-functions-in-bigquery-using-sql-658fb7c9af65

了解如何使用等级、密集等级、行号、累积分布、百分位数等级、四分位数、百分位数等等

澳大利亚摄影师在 Unsplash 上拍摄的照片

什么是编号功能?

编号功能为表中的每条记录分配一个数字(或小数)。它们主要用于对数据进行排序或分配序号,以便进一步处理(重复数据删除、过滤、分组)。

它们通常需要按特定维度排序(日期、收入、薪水、ID 等)。

它们可用于回答以下问题:

  • 收入最高的国家有哪些?
  • 我如何按赛区和工资给排球运动员排名?
  • 按产品类别划分,表现最佳的国家有哪些?
  • 根据摄取日期复制了哪些行?

本文将分为**两节。**第一部分将介绍RANK()DENSE_RANK()ROW_NUMBER()的机制,因为它们的目的非常相似,但输出和机制略有不同。

第二部分将涵盖PERCENT_RANKCUME_DISTNTILE,它们具有不同的目的、机制和输出。

我还建议阅读伟大的谷歌文档。

https://cloud.google/bigquery/docs/reference/standard-sql/numbering_functions

为了更好地理解这些函数之间的区别,我们将查询以下数据集,该数据集包含来自谷歌商品商店的不同国家和产品类别的销售额。

我们简单分析用例的基础表。(图片由作者提供)

行数

函数ROW_NUMBER()将总是返回一个唯一的数字,从 1 开始按顺序递增(1,2,3,4…,8,9…)。不需要指定顺序,即使行或值相似,输出编号也总是唯一的。

如果没有使用ORDER BY子句,结果将是不确定的,这意味着即使输入数据相同,结果也会不同。

让我们看两个例子:

ROW_NUMBER()返回一个连续且唯一的数字。(图片由作者提供)

在本例中,我们使用一个空的OVER()子句,这意味着函数将遍历表并为每一行随机分配一个数字(即使 BigQuery 如何分配它可能有一些逻辑)。

让我们看看在我们的第二个例子中,当我们向 revenue 字段添加一个ORDER BY子句时会发生什么。

ROW_NUMBER()返回一个按收入排序的连续且唯一的数字。(图片由作者提供)

行号现在按收入排序,我们希望结果按降序排列,这就是为什么我们添加了一个DESC命令(默认情况下,它是升序)。

对于具有相同值、IndonesiaTaiwan两行,功能输出数保持递增。

隐含地,还有一个字母顺序,这可以通过手动添加另一个排序参数来改变。

ROW_NUMBER() OVER(ORDER BY revenue DESC, country DESC)

假设我们想要分解每个产品类别的行号。为此,我们可以使用一个PARTITION BY子句,并按收入值降序排序。

ROW_NUMBER()为每个分区返回一个从 1 开始的连续且唯一的数字。(图片由作者提供)

这对于在数据集中可用的不同组或类别中排列/分配顺序非常有益。

秩和稠密 _ 秩

函数RANK()DENSE_RANK()的行为与ROW_NUMBER()相同,但有两个例外:它们如何序列号以及它们如何管理相似的值

对于RANK(),相似的行将获得相同的等级号,但是函数将在两个或更多相同的行之后留下一个间隙。

对于DENSE_RANK(),相似的行将接收相同的等级编号,但是等级编号总是递增 1,并且在我们的编号序列中不会有间隔。

让我们举例说明一个查询中的三个功能:

这三个功能按收入排序。(图片由作者提供)

这是我们不同函数的输出。您可以关注国家/地区Venezuela(重复)的第 5 行和第 6 行上的函数如何工作,以及之后:

  • ROW_ NUMBER()保持其递增顺序(1,2,3, 4,5,6,7
  • RANK()给出相同的输出值(5,5),但随后丢失其增量序列(1,2,3, 4,5,5,7 )
  • DENSE_RANK()给出相同的输出值(5,5),但保持其递增顺序(1,2,3, 4,5,5,6

第 10 行和第 11 行的机制相同,因为我们是按收入排序的,它们的收入相同。这种增量序列机制对于任意数量的对等值都是一样的。

为什么不用 ROW_NUMBER 代替 RANK 或者 DENSE_RANK?

您可以使用ROW_NUMBER()作为排名函数,但是有时为所有相似/对等行保持相同的排名值是很有趣的。

对于某些用例,用DENSE_RANK()保持数字序列总是递增 1 可能是一个好的选择。

CUME_DIST

函数CUME_DIST()计算数据集或分区内值的累积分布。它返回从 0 到 1 的值(> 0 且≤1)。

这个函数需要一个ORDER BY子句来对值进行排序。

根据 Google 的文档,它是使用公式计算的: NP/NR。我们可以这样来解释它:

  • NP位于当前行之前或与当前行相似的行数
  • NR是(整个数据集或一个分区的)总行数

它将向您展示数据集中的值是如何分布的。例如,基于收入的数据集行分布:

按收入排序的所有行的累积分布。(图片由作者提供)

我们使用ROUND()函数和乘法*100将数据转换成可读性更好的百分比格式。

让我们手动计算。对于我们的第一行,只有 1 个值,并且没有低于 **1323 的收入。**这给了我们 1/12 = 8% (我们的数据集中有 12 行)。

现在,让我们看看第 4 行,Nigeria,有 3 行的值在当前行的 3314 + 下。这给了我们 4/12 = 33%

有趣的部分是针对第 2 排和第 3 排(或第 7 排和第 8 排)。它们具有相同的收入值。因此,如果我们查看第 2 行,我们可以预期计算结果为 **2/12 = 16%。**但是,由于第 3 行是相似的,所以两行的结果都是 3/12 = 25%。

恩蒂莱

NTILE()函数允许您将一组已排序的数据点分成均匀分布的桶。你可能知道这就是分位数,它可以有不同的类型:

  • 四分位数 (4 个分位数)
  • 十分位数 (10 个分位数)
  • 百分位数 (100 个分位数)

例如, quartiles 将数据集分成四个大小相等的桶。这意味着第一个四分位数(Q1)包含 25%的数据点。

让我们将四分位数应用到我们的表中:

我们的行根据收入分成 4 个大小相等的桶。(图片由作者提供)

在这种情况下,我们根据有序收入将我们的数据点分成四个相等的桶(每个桶有 3 行,即 3/12 = 25% )。

请记住**这不是总数的百分比。**如果我们将第四个四分位数国家(法国、日本和美国)的收入相加,它确实占总收入的 90%。

使用此功能时,您必须提供一个输入号码,例如:NTILE(4)。你不能让这个参数为空,给一个 0 或负值的输入数而没有看到一个错误。

您可以使用这种方法在您的数据中找到离群值(高于 95%百分点的数据点)根据他们的购买价值(前 25%)对您的客户进行分类,等等。

百分比排名

函数PERCENT_RANK()计算一组值中值的百分比分布。它返回从 0 到 1 的值。

这个函数需要一个ORDER BY子句来对值进行排序。

所有行按收入排序的百分位数排名(图片由作者提供)

同样,我们使用ROUND()函数和乘法*100将数据转换成可读性更好的百分比格式。

看产出,Germany收入最低(不大于其他任何国家),所以百分位排名为零。

另一方面,United States拥有所有国家中最大的收入(大于任何其他国家),因此百分位等级为 1(或 100%)。

对于France,百分位数排名为 82%。这意味着它的收入高于所有其他国家的 82%。

结论和想法

从实际经验来看,最常用的功能有ROW_NUMBER()RANK()DENSE_RANK()NTILE()

例如,我的一项任务需要使用DENSE_RANK()来识别收购产品,基本上是对客户订单中的产品进行排序,以识别哪些是最先购买的产品。这个函数允许我们将序列递增 1,在一个订单中处理多个产品,并且仍然能够计算客户订单总数的正确数量。

在另一项任务中,NTILE()帮助将客户分类为近期和频繁购买者类别(如 RFM 模型(近期、频率、货币)),这将用于在我们的电子邮件服务提供商系统中进行细分。

您可以在其他数据库系统(Amazon Redshift、MySQL、Postgres、Snowflake 等)中找到这些函数,因为它们是流行的 SQL windows 函数(至少对于排名和行号)。

引用和数据集

本文中使用的数据集来自于 BigQuery 公共数据,是在 CC BY 4.0 许可下的 Google analytics 数据样本。

https://developers.google/analytics/bigquery/web-ecommerce-demo-dataset

LDA、QDA 和高斯朴素贝叶斯分类器的区别

原文:https://towardsdatascience/differences-of-lda-qda-and-gaussian-naive-bayes-classifiers-eaa4d1e999f6

深入研究建模假设及其含义

在挖掘经典分类方法的细节时,我发现了关于高斯朴素贝叶斯(GNB)、线性判别分析(LDA)和二次判别分析(QDA)的异同的稀疏信息。这篇文章集中了我为下一个学习者找到的信息。

总结:所有这三种方法都是贝叶斯分类器的一个具体实例,它们都处理连续高斯预测器,它们在预测器之间和类之间关系的假设上有所不同(即,它们指定协方差矩阵的方式)。

贝叶斯分类器

我们有一组 X 个 p 个 预测值,以及一个离散响应变量 Y(类),取值 k = {1,…,K},用于样本的 n 个 观察值。

我们遇到了一个新的观察值,我们知道预测值 X 的值,但不知道 Y 类的值,所以我们想根据我们拥有的信息(我们的样本)对 Y 进行猜测。

贝叶斯分类器将测试观测值分配给具有最高条件概率的类,由下式给出:

贝叶斯定理

其中:pi_k 是先验估计,f_k (x)是我们的似然。为了获得类 k 的概率,我们需要定义先验和似然的公式。

**先验。**观察到类别 k 的概率,即我们的测试观察值属于类别 k,但没有关于预测值的进一步信息。查看我们的示例,我们可以将类 k 中的情况视为具有二项式分布的随机变量的实现:

在一组试验次数 n(样本量)中成功次数(k 类中的观察值)的分布。

其中,对于n 次试验,在每次试验中,观察值属于(成功)或不属于(失败)类别 k。可以看出,相对成功频率——试验总数中的成功次数— 是 pi_k 的无偏估计量。因此,我们使用相对频率作为观察值属于类别 k 的概率的先验。

***可能性:*可能性是看到 X 的这些值的概率,假设观测值实际上属于类别 k。因此,我们需要找到类别 k 中预测值 X 的分布。我们不知道“真实”的分布是什么,所以我们无法“找到”它,我们宁愿对它的样子做一些合理的假设,然后使用我们的样本来估计它的参数。

如何选择合理的配送?离散的和连续的预测值之间有一个明显的区别。这三种方法都假设在每个类中,

预测值具有高斯分布(p=1)或多元高斯分布(p>1)。

k 类条件下多元高斯分布的一般形式。

因此,只有当我们有连续的预测器时,才能使用这些算法。事实上,高斯朴素贝叶斯是一般朴素贝叶斯的一个特例,具有高斯似然性,这就是为什么我在这篇文章中将它与 LDA 和 QDA 进行比较。

从现在开始,我们将考虑能够展示三种方法之间差异的最简单的情况:两个预测器(p=2)和两个类(K=2)。

线性判别分析

LDA 假设跨类的协方差矩阵是相同的。

这意味着类别 1 和类别 2 中的预测值可能具有不同的均值,但是它们的方差和协方差是相同的。这意味着预测值之间的“分布”和关系在各个类别中是相同的。

基于 LDA 假设的类别分布可视化

上面的图是从每种形式的分布中生成的:

LDA 第 1 类和第 2 类预测因子的分布

我们观察到协方差矩阵是相同的。如果我们预期预测值之间的关系在不同类别之间不会发生变化,并且如果我们只是观察到分布均值的变化,那么这种假设是合理的。

二次判别分析

如果我们放松 LDA 的恒定协方差矩阵假设,我们有 QDA。

QDA 没有假设跨类的协方差矩阵不变。

基于 QDA 假设的类别分布可视化

上面的图是从每种形式的分布中生成的:

QDA 第一类和第二类预测因子的分布

我们观察到这两个分布的所有参数都可以变化。如果我们预期不同类别的预测者之间的行为和关系非常不同,这是一个合理的假设。

在这个例子中,甚至两个预测值之间的关系的方向也从类别 1 到类别 2 变化,从正协方差 4 到负协方差-3。

高斯朴素贝叶斯

GNB 是朴素贝叶斯的一个特例,其中预测值是连续的,并且正态分布在每个 k 类中。一般的朴素贝叶斯(因此,GNB 也是)假设:

给定 Y,预测因子 X 是条件独立的。

独立意味着不相关,即协方差等于零。

基于 GNB 假设的类别分布可视化

上面的图是从每种形式的分布中生成的:

GNB 第一类和第二类预测因子的分布

使用朴素贝叶斯,我们假设预测值之间没有关系。在实际问题中很少出现这种情况,然而,正如我们将在下一节中看到的那样,这大大简化了问题。

假设的含义

选择模型后,我们估计类内分布的参数,以确定我们的测试观察的可能性,并获得我们用来分类它的最终条件概率。

不同的模型导致不同数量的参数被估计。提醒:我们有 p 预测器和 K 总类。对于所有的模型,我们需要估计预测值的高斯分布的平均值,这在每一类中都是不同的。这就产生了一个基数,pK为所有方法估计的参数。*

此外,如果我们选择 LDA,我们将估计所有 p 个预测值的方差和每对预测值的协方差,从而得出

用 LDA 估计的参数数量

参数。这些是跨类的常数。

对于 QDA,由于它们在每一类中都不同,我们将 LDA 的参数数量乘以 K,得到下面的估计参数数量的等式:

与 QDA 一起估计的参数数量

对于 GNB,我们只有每一类中所有预测因子的方差: **pK** 。*

很容易看出对于 p 和/或 K 的大值使用 GNB 的优势。对于经常出现的二进制分类问题,即当 K=2 时,这是三种算法的模型复杂度如何随着 p 的增加而发展。

那又怎样?

从建模的角度来看,当应用一种方法时,知道你正在处理的假设是很重要的。需要估计的参数越多,最终分类对样本变化越敏感。同时,如果参数的数量太少,我们将无法捕捉到类之间的重要差异。

谢谢你的时间,我希望它是有趣的。

除特别注明外,所有图片均为作者所有。

来源:

  • 机器学习第三章——汤姆·m·米切尔,2017
  • 《统计学习导论》第 4 章—James,Witten,Hastie,Tibshirani,2013

将 Google Drive 连接到 Google Colab 笔记本的不同方式!(第二部分)

原文:https://towardsdatascience/different-ways-to-connect-google-drive-to-a-google-colab-notebook-part-2-b867786aed55

使用 Google Colab 处理数据的协作方式

照片由戴恩·托普金在 Unsplash 上拍摄

继续讨论将 Google Drive 连接到 Google Colab 笔记本的不同方式这一次我想分享仅通过文档名称调用 google sheet 文件并使其成为熊猫数据框的方法。此外,我想分享 2 种不同的方式,我用来发送数据帧到一个新的谷歌表文件或更新现有的一个。

照片由 Maxime Horlaville 在 Unsplash 上拍摄

Google 协作代码片段

谷歌合作实验室有一个很棒的地方,那就是他们的代码片段可以帮助你做很多事情。从使用 Altair 库的可视化到使用 python 的数据库连接,甚至是使用网络摄像头捕捉图像以便在运行时处理的代码片段(Colab 内核)。

Google Colab 代码片段 Altair 库可视化—作者图片

要访问此 Google 协作片段:

  1. 去你的 Google Colab 笔记本。
  2. 点击笔记本左下方的代码符号。

代码片段—作者图片

3.单击显示代码片段列表后,它会在笔记本上自动提示为一个新选项卡。

代码片段 2 —作者图片

4.您可以使用过滤器部分来搜索您需要的代码片段。

Google Sheets 代码片段——作者图片

如果您忘记了连接到 Google Drive 或将数据保存到 Google Sheet 的代码,这可能会对您有所帮助,我将在本文中向您展示这一点。

连接到一个特定的 google 工作表并使其成为一个数据框架

在第一部分中,您将学习如何通过使用文档名将 google sheets 数据连接到 google colab 笔记本。

  1. 你将认证
  2. 然后将带来谷歌证书进行连接
  3. 授权连接
  4. 使用 google 工作表的名称连接到该工作表(输入名称,替换“”中的{}。并指定文档的工作表(选项卡)。
  5. 导出所有数据值。
  6. 使用 pandas 将其转换为 Pandas 数据框架
  7. 搞定了。开始你的分析。

代码和示例—作者图片

更新现有的 Google 表单

下一段代码将帮助您获取已经存在的 google sheet,并用 Google Colab 中的新数据更新它。

因为在上一点中,我们学习了使用名称打开 Google Sheet,所以让我们使用 Google Sheet 键(URL 中的一系列数字和字母,给出了唯一的键标识符)。

  1. 让我们从 google collaboratory 中的 vega_datasets、 datasets 中获取数据。

Czars 数据集-按作者分类的图像

2.现在我们已经有了数据集,让我们确保它不包含任何 Nan 值,因为这在更新过程中不会被识别,并且会给我们带来错误。
-我们可以使用下面的代码来查看任何包含空值的列,以及显示了多少个空值:

cars.isnull().sum()

由于数据中出现的空值是数值,我们可以用 0 填充这些值。由于这只是一个练习,我们并没有对数据进行深入的分析,所以我将用 0 来填充这些数据,但是在现实世界中,如果我们想要有好的数据,您应该更好地处理这些空值。
-我过去用过的一些处理空值的方法:
1。计算您的列中的空值百分比,如果它很大,您应该考虑删除该列,如果基本上整个列都是 Nan,它不会给您任何洞察力。
2。如果%根本不重要,并且你认为它可以处理,看看你是否可以使用平均值或使用 0。
3。使用数据估算器(参见我的文章用 Plotly 在地图中显示布宜诺斯艾利斯属性,其中我展示了如何使用估算器来填充经度和纬度数据列中的空值)。

3.用 0 填充空值

cars.fillna(0, inplace=True)

4.在用 0 填充这些空值之后,让我们使用键连接到 google sheet。在这里,您需要将{key}更改为可以在 URL 中找到的 google sheet key

谷歌工作表关键字——作者图片

5.现在,我们需要将数据转换成列表,以便在更新过程中可以识别数据,这是为了显示数据应该在哪里(单元格)以及每个值的列名。
在我们进行更新之前,我们应该确保我们所有的数据都是以字符串或 int/float 数据类型分配给更新列表的。

代码示例更新—作者图片

使用 Google Colab 创建新的电子表格并添加数据

最后,我们正在使用 Google Colab 笔记本创建一个新的电子表格,并添加新数据。如果您正在使用 google colab 对您带来的一些数据进行分析,并希望使用 google sheets 对其进行分析,但不想下载并上传到 Google sheets,或者不想更新您已经拥有的电子表格,并希望使用新的电子表格进行输出,这可能会对您有所帮助。你可以在不同的场合使用这个。

与我们之前使用的代码有些类似,但这次只是为了从头开始创建一个新的电子表格。

创建和添加数据-按作者分类的图像

结果—作者提供的图像

这样,我最终确定了一些将 Google Drive 连接到 Google Colab 笔记本的不同方式。这些是我过去在协作环境中使用过的一些东西,对我帮助很大,最重要的是,您不需要将代码或 Jupyter 笔记本发送给某人来运行、安装库并为他们创建一个运行它的环境,这也有助于避免发送文件,这需要时间,只需在同事或团队成员之间共享即可。

希望这对 yall 很有用!

一如既往,我将很高兴知道您的任何反馈!如果你有任何与此相关的新话题,想知道如何去做,请告诉我,我会写下来的!

PD:不要忘记所有这些代码片段都可以在 google colab 笔记本的代码片段特性下找到!

马特·琼斯在 Unsplash 上拍照

将 Google Drive 连接到 Google Colab 笔记本的不同方式!(第一部分)

原文:https://towardsdatascience/different-ways-to-connect-google-drive-to-a-google-colab-notebook-pt-1-de03433d2f7a

使用 Google Colab 处理数据的协作方式

约书亚·阿拉贡在 Unsplash 上拍摄的照片

如今,我们生活在一个协作的环境中,我们的项目与我们的同事共享,以供审查或协作。此外,我们每次都越来越多地使用我们的云存储,如果它必须用于任何数据目的(。csv,。json,。xlsx 文件等。)更好的是,最重要的是,如果我们在一个协作的环境中,任何可以访问这些信息的人都可以在我们的项目中合作或共享更多数据。

因此,在本文的第 1 部分,我将与您分享我在数据路径和协作工作中学到的一些技巧!

连接 Google Drive 的特定文件夹

假设我们想要分析 Google Colab 笔记本中的一些数据。最近 Google Drive 分享给我们的 csv 文件。但是,我们不想从 Google Drive 下载所有数据,然后上传到我们的笔记本电脑,这将需要时间,也许那些。csv 文件可能会在未来发生变化,我们将不得不重新制作整个过程。

为此,Google Colab 有一种使用以下代码挂载我们的 Google Drive 的方法:

运行此代码后,将会提示一些消息:

连接到 Google Drive —图片由作者提供

选择您想要安装的 Google Drive 帐户——按作者分类的图片

选择“允许”就可以了。—作者图片

安装 Google Drive 后,您可以浏览所有 Google Drive 文件夹,并导航到您想要读取和分析已共享文件的文件夹。为此,您需要使用以下代码导航到 Google Drive 的主目录:

%cd gdrive/MyDrive

最后,使用 %cd 继续浏览您的文件夹和文件。

如果你想从一个文件夹中读取很多文件,这是我发现的最好的选择。

我通常使用下面这段代码来读取和连接单个数据帧中的所有文件(仅当所有文件具有相同的模式时;相同数量的列和名称):

你可以查看我的 bikes 笔记本 我用这些代码挂载我的 Google Drive 的地方,导航到我所有的。托管 csv 文件。然后,我读取、清理、处理、转换和连接所有这些文件到一个单一的数据框架。

将文件保存在您访问的同一文件夹中

在导航到所需的文件夹并获得所有要分析的数据后,您还可以将最终结果保存在同一文件夹中,或者导航到驱动器中的任何其他文件夹,并使用以下保存代码保存数据。csv 文件:

df.to_csv("name_file.csv")

或将数据保存到您希望保存的文件类型中的任何其他代码。

将特定文件从 Google Drive 上传到我的 Google Colab 笔记本

也许有时我们只需要/想要从我们的 Google Drive 上传单个文件到我们的运行时/内核/环境,而我们不想挂载整个驱动器。

我们可以通过使用以下代码将我们的文件下载到 Google Colab 中来实现:

首先,我们需要导入必要的库:

然后,您可以运行下面的代码:

在运行上面的代码之前,您需要先找到您的文件 id,为此,在 Google Drive 中找到您的文件,右键单击它以找到文件链接:

点击“获取链接”——作者图片

一旦你得到链接,你会看到这样的内容:

[https://drive.google/file/d/{"ID"}/view?usp=sharing](https://drive.google/file/d/1F758TadSiQtX5iew2UdUfS5IUu3CBVe-/view?usp=sharing)

红框=文件 id-作者图片

红框中包含数字和字母的整个字符串将是您的 id,需要插入代码的这一部分:

file_id = '{insert your id here}'

然后会提示一条消息:

点击链接并按照步骤操作——作者图片

点击该链接后,它会要求您登录您的谷歌帐户,并提供访问谷歌 colab 以访问您的谷歌驱动器,最后它会给你一个代码,复制并粘贴到“ 输入验证码:

复制粘贴代码,并将其插入输入框——作者图片

最后,你可以对这些文件做任何你想做的事情。

结论

我向你展示了两种不同的方式来上传或访问你的谷歌硬盘上的文件。

  1. 将你的 Google Drive 安装到你的 Google Colab
    ——你可以通过浏览文件夹将文件保存到你的 Google Drive 中。

优点:

  • 你把你的整个 Google Drive 安装到你的 Google Colab 笔记本上。
  • 如果您的项目需要来自不同位置的多个文件,您可以浏览 Google Drive 中的文件夹。

缺点:

  • 您保存的任何文件都将保存在您所在的确切文件夹中,因此,如果您不确定您在 Google Drive 中的确切位置,那么在保存之前运行以下表达式会很有帮助:
%pwd
  • 有时,如果你不能完全确定你在哪里,文件的保存可能会在你的 Google Drive 里变得一团糟,然后你就需要自己整理一切了。

2.上传文件,无需安装整个 Google Drive。

优点:

  • 您不需要挂载整个驱动器,因此您将始终位于 Google Colab 运行时内的相同路径/目录中。
  • 保存的文件将保存到您的运行时中,而不是您的 Google Drive 中,这将是一个 Pro 如果您不想将文件保存到 Google Drive 中,或者不需要浏览文件夹,您可以在完成项目后随时将文件下载到您的 Pc 中。

缺点:

  • 这对于多个文件来说不是最佳的,你需要多次将它们下载到你的运行时(运行相同的代码),每次下载不同的文件。

最后,这是我将要写的一系列 Google Drive 和 Google Colab 连接的第 1 部分。希望这对您的协作环境有所帮助!

我很高兴知道您的任何反馈!如果你有任何与此相关的新话题,想知道如何去做,请告诉我,我会写下来的!

照片由刘汉宁·内巴霍在 Unsplash 上拍摄

在 Python 文件中运行 Bash 脚本的不同方式

原文:https://towardsdatascience/different-ways-to-run-bash-scripts-in-your-python-file-8aeb721bc3d1

利用子流程、OS、Glob 等

图片来自 Unsplash 由Michael Dziedzic

对于数据科学家来说,理解 Shell 脚本的基础知识至关重要。通过命令行(CLI)的强大功能,许多流程可以实现自动化和简化。有时,当您开始构建更复杂的模块/类时,您会发现自己需要将 Shell 命令合并到 Python 代码中。

幸运的是,我们有几个 Python 模块可以让它变得非常简单。我们将主要关注子进程、操作系统和 glob 模块。他们每个人都有自己的额外津贴,我只是想给一个如何利用他们的快速概述。

子过程

正如他们的文档所示,这个模块有三个主要方面。

  1. 可以产生新的过程
  2. 连接到它们的输入/输出/错误管道
  3. 获取他们的返回代码

让我们直接进入主题,通过几个简单的片段快速了解一下如何执行 Shell 命令。让我们先导入我们的模块。

制作目录

让我们首先使用 mkdir Linux 命令创建一个名为“code”的目录。

创建一个名为 code 的目录

我们现在可以看到在我们的文件结构中创建的目录。

目录已创建(作者截图)

建立档案

让我们继续尝试一些更基本的 Linux 命令。让我们创建一个文件,稍后我们可以将它移动到我们的目录中。

建立档案

我们现在可以在同一个目录中看到我们的 Python 文件。

作者截图

您可以继续使用这个 调用方法 来执行您选择的不同命令。

要生成一个新流程,请检查子流程。Popen 方法。当您开始处理必须执行的更复杂的命令和程序时,这将特别有用。Subprocess 是一个功能强大的库,具有最新的特性,查看他们的文档这里。

操作系统(Operating System)

顾名思义,模块可以用来执行许多操作系统任务。它涵盖了子流程模块提供的许多相同功能。让我们重复上面对子流程所做的相同任务。

创建目录

使用操作系统模块创建目录

现在,您应该能够看到另一个目录被创建。

作者截图)

假设我们想要列出或解析当前路径的所有文件和目录,您可以使用下面的命令来完成。

获取文件路径

使用操作系统获取路径

我们可以将该路径传递给 listdir 命令,该命令将返回该目录中的所有目录和文件。

列出文件/目录

获取路径中的文件/目录

我们应该会看到我们在当前目录中创建的所有目录和文件。

作者截图

使用操作系统模块,您可以处理大量文件来执行您可能需要执行的任何标准操作或解析。

一团

具体来说, glob 模块可以真正用于文件名匹配。通常,您会希望解析具有特定模式或常见类型的文件。

让我们快速看一下如何列出我们目录中的所有 Python 文件。

列出所有 python 文件

这应该会返回我们已经创建的两个 Python 文件。

作者截图

随着您的用例变得越来越复杂,您可以搜索越来越复杂的模式,并加入正则表达式来过滤您的文件。这里有一篇关于 TDS 的优秀的文章,深入探讨了 glob 模块。

结论

通过使用这三个模块,甚至更多的模块,比如 Pathlib ,您可以将 Shell 命令合并到您的 Python 脚本中。本文中的简单示例可能不会显示直接的用例,但是随着您对更深层次的代码库的深入研究,有必要与 CLI 进行交互。

如果你喜欢这篇文章,请在LinkedIn上与我联系,并订阅我的媒体 简讯 。如果你是新手,使用我的 会员推荐 报名。

差分进化:非线性凸优化的替代方案

原文:https://towardsdatascience/differential-evolution-an-alternative-to-nonlinear-convex-optimization-690a123f3413

了解差分进化的基础知识及其在 Python 中的应用

阿曼德·库利在 Unsplash 上拍摄的照片

优化是一种决策制定,或者更具体地说,是决策制定机制中的主要量化工具之一,其中必须做出决策,以在一些规定的情况下优化一个或多个目标(Bilal 等人,2020)。

差分进化(DE) (Storn & Price,1997)是一种进化算法(EA),最初设计用于解决连续域上的优化问题。它实现简单,但解决问题的质量很好,这使它成为最受欢迎的基于群体的算法之一,有几个成功的应用报告。

从最初的概念来看,DE 的设计是为了满足一些使其特别有用的要求:

  1. 处理不可微、非线性和多模态成本函数的能力。
  2. 处理计算密集型成本函数的并行性。
  3. 易于使用:很少的控制变量来控制最小化。这些变量也应该是稳健的和易于选择的。
  4. 良好的收敛性:在连续的独立试验中一致收敛到全局最小值。

在整篇文章中,我们将看到差分进化的基础知识以及对单目标优化问题的应用——尽管它对多目标优化也有一些扩展。它将与传统的基于凸梯度的算法进行比较,以评估每种算法何时更合适。所有这些都将使用 scipy.optimize Python 模块来执行。感兴趣的可以在我的示例笔记本中找到实现细节。

如果你对非线性优化不熟悉,建议阅读我之前的文章 非线性规划:理论与应用

下坡过程在当时是一个很好的类比…然而,搜索空间在本文中可能会变得更加混乱。

算法解释

就像自然界一样,进化算子对进化算法种群进行操作,试图产生适应度越来越高的解。与这些算法相关的三个主要算子是突变、重组(交叉)和选择(存活) (Coello 等人,2007)

下面表示了 DE 算法的基本结构,接下来将描述其中的主要算子和它们各自的控制参数。

差异进化的基本结构。(图片由作者提供)。

该算法首先基于用户指定的个体数量 N 和问题的每个决策变量的边界来初始化群体。每个个体对应一个优化变量的向量。在 5 到 10 倍的决策变量之间选择 N 可能是一个好的开始。

基于个体对应的目标函数值和可能的约束值,个体被分配一个适应值。最初,DE 没有约束处理的规则,这是后来几篇文章的焦点。Lampinen (2002 年)提出了一种有用的方法,因为它表现出有竞争力的性能,并且在实施时不需要额外的控制参数。这是在 scipy DE 实现中采用的方法。

然后,种群在连续的世代中迭代,直到满足某些停止标准。在每一次迭代中,新的试验向量通过称为突变交叉的操作产生。然后,将试验向量与其对应的相同索引的父向量进行比较,并将每对中的最佳向量传递给下一代。停止标准通常基于目标函数和代数的改进。

已经为 DE 提出了几种复制方案。通常用 DE/x/y/z 表示,其中 x 对应变异亲本选择方案, y 对应差异向量个数, z 对应交叉策略。

可能最流行的突变方案是 DE/rand/1,由下面的等式表示。

差异进化的突变。(图片由作者提供)。

其中, v 对应一个索引为 i 的突变载体, r1r2r3 为互不相同且与 i 不同的三个索引。参数 F 是用户自定义的控制参数,表示为突变参数或比例因子

一些策略可能会强制开发超过探索,这取决于如何选择父向量。加强利用的一种常用策略是最佳/1 策略,其中基本向量对应于种群中具有最佳适应值的个体。根据我的经验,这种策略通常会导致过早收敛,这也是我通常避免这种策略的原因。

开发探索之间的权衡也存在于突变参数 F. 的选择中。例如,当需要强调探索时,例如在具有不连续决策空间的问题中,使用更高的值可能是有用的。相反,为了强调利用,使用较低的限制值可以改善结果。

此阶段通常执行的其他操作有抖动抖动抖动根据用户指定值的范围,为每个创建的突变向量单独随机化 F 。一开始,一个好的选择可以是[0.3,1.0]。抖动将每个差向量的每个分量乘以一个随机值,从而加上旋转。

最常见的交叉策略是二项式交叉或仅 bin ,这在下面的等式中描述。

差分进化中的二项式交叉。(图片由作者提供)。

其中对应于结合相应突变载体 v 和目标 x 的元素而创建的试验载体。参数 CR 控制从每一个继承一个属性的概率,并且附加的规则规定的至少一个属性必须从 v 继承以避免重复。**

根据 Price 等人(2005)的观点,在低 CR 值下表现良好的目标函数是可分解的——可以写成一维函数的和,而那些需要接近 1 的值的目标函数是不可分解的。Zaharie (2009)对差分进化算法中交叉算子的影响进行了详细的研究,我建议对算法细节感兴趣的人进一步阅读。

以我的经验来看,大部分非线性现实问题都是不可分的,其中高 CR 值,比如 0.7-0.9,是个不错的选择。例外情况包括具有周期性项和强多模态的问题,其中沿着独立坐标轴的搜索可能是有利的。在这类问题中,选择 0.2–0.5 可能会产生更好的结果。

接下来让我们看一些应用。在我们的实现中,将使用来自 scipy.optimizedifferential_evoluton 函数。

凸问题

在第一个例子中,让我们尝试在 上一篇文章 中使用的相同目标函数。

凸问题的目标函数。(图片由作者提供)。

用 Python 代码。

**# Defining the objective function
def obj_fun(x):
    return (x[0] - 0.5) ** 2 + 0.7 * x[0] * x[1]\
        + 1.2 * (x[1] + 0.7) ** 2

# Defining the gradient function
def gradient_fun(x):
    return np.array([2 * (x[0] - 0.5) + 0.7 * x[1],\
        0.7 * x[0] + 2 * 1.2 * (x[1] + 0.7)])**

看起来像这样。

决策空间与凸问题的真最优。(图片由作者提供)。

我们将使用拟牛顿 BFGS 方法和 DE 来解决这个问题。

**# Optimization using BFGS (gradient-based method)
sol_cvx = minimize(obj_fun, [2.5, 0.0], jac=gradient_fun,
                   method="BFGS")

# Otimization using Differential Evolution
sol_de = differential_evolution(
    obj_fun,
    bounds=[(-5., 5.), (-5., 5.)],
    popsize=50, strategy="rand1bin",
    mutation=(0.3, 1.0),
    recombination=0.7,
    tol=1e-8,
    maxiter=200,
    updating="deferred",
    polish=False
)**

请注意,第一个区别是指定决策变量的界限,而不是初始估计值 x0 。这些界限用于创建初始群体。

自变量 popsize 对应于 N 人口数量;策略为 DE/x/y/z 策略;突变F 参数;重组CRtol 对停止准则方面的改进;和 maxiter 到最大代数。通过将上升设置为“延迟”,我选择使用最初的策略,即每一代只更新当前群体一次。参数 polish 定义了在当前最佳解决方案的每次迭代中是否使用局部优化器,我选择不使用。

在真正的最优解中,这些解基本上是重叠的。

凸问题的结果。(图片由作者提供)。

主要缺点是 DE 使用 50 的种群大小花费了 56 代来收敛,因此它进行了几个目标函数评估,这可能是计算上昂贵的。相反, BFGS 进行了 6 次迭代和 7 次函数评估。

理解问题是至关重要的,这样选择的算法才是有效的,而不会导致不必要的函数计算。在第一个问题中,基于凸梯度的算法显然是比 DE 更有效的选择,尽管两者导致完全相同的最终结果。

让我们看看它会如何改变…

多模态问题

如果搜索空间有几个局部最优怎么办?基于凸梯度的算法是局部搜索方法,因此它们很可能陷入这些点。当局部最优不够时,差分进化是一个更有效的工具。

让我们给我们的目标函数添加一些多模态术语。

**# In the nonconvex problem we add some periodical terms
def obj_fun_2(x):
    A = np.array([[1, 0.3],
                 [0.3, 0.7]])
    xt = x.dot(A)
    rugosity_1 = (xt[0] * np.sin(15 * xt[0]) - 0.5) ** 2\
        + (xt[1] * np.sin(15 * xt[1]) - 2) ** 2
    rugosity_2 = np.sin(15 * x[0]) ** 2 + np.sin(25 * x[1]) ** 2
    convex = obj_fun(x)
    return 5 * rugosity_2 + rugosity_1 + convex**

非凸例子。(图片由作者提供)。

我觉得这张图片很漂亮,尽管相当混乱,而且基于梯度的搜索方向肯定不会带我们去任何地方。让我们试着解决这个问题来看看。

**sol_cvx_2 = minimize(obj_fun_2, [2.5, 0.0], method="BFGS")

sol_de_2 = differential_evolution(
    obj_fun_2,
    bounds=[(-5., 5.), (-5., 5.)],
    popsize=50, strategy="rand1bin",
    mutation=(0.2, 1.0),
    recombination=0.7,
    tol=1e-8, maxiter=200,
    updating="deferred", polish=False)**

使用预先指定的初始估计,已经实现了 12.12 的目标函数,比 DE 实现的 1.613 差得多(这很可能是全局最优)。

非凸问题的结果。(图片由作者提供)。

我们可以尝试几种不同的初始估计,在二维问题中,可能会导致我们满意的结果。在这个问题中,我尝试了 500 种不同的初始估计,得到的最高结果是 1.855。然而,高维搜索空间的稀疏性使得这样的过程非常无效。

工程压力容器设计

下一个问题由 Sandgren (1990)提出,它使压力容器设计的成本最小化。由于材料供应标准,该问题有两个离散变量 x1x2 ,因此在其原始公式中是不可微的。然而,在本节中,我们将首先解决问题的连续实值变体,然后解决原问题。

目标函数由下面的等式描述。

压力容器设计的目标函数。(图片由作者提供)。

和边界约束由下面的等式来表示。

压力容器设计的功能限制。(图片由作者提供)。

压力容器设计的界限。(图片由作者提供)。

用 Python 代码。

**def f_vessel(x):
    return 0.6224 * x[0] * x[2] * x[3]\
        + 1.7781 * x[1] * x[2] ** 2\
        + 3.1611 * x[0] ** 2 * x[3]\
        + 19.84 * x[0] ** 2 * x[2]

def c1_vessel(x):
    return - (0.0193 * x[2] - x[0])

def c2_vessel(x):
    return - (0.00954 * x[2] - x[1])

def c3_vessel(x):
    return - (750 * 1728 - \
        np.pi * x[2] ** 2 * x[3]\
        - 4/3 * np.pi * x[2] ** 3)**

第一次尝试是使用 SLSQP 解决问题。注意,我提到了目标函数的梯度,我已经在示例笔记本中实现了。这里我决定省略这些定义,因为它们太长了。

**sol_cvx_vessel = minimize(
    f_vessel, [2, 2, 50, 50],
    jac=grad_vessel,
    bounds=bounds_vessel,
    constraints=cons_vessel,
    method="SLSQP"
)**

不幸的是,由于第三个约束的非线性性质, SLSQP 无法找到合适的解决方案。

**message: 'Positive directional derivative for linesearch'**

然而,差分进化算法并不是解决连续变量问题的唯一合适算法… 信赖域方法也是线搜索方法的替代方法,两者都使用在由某个规则定义的搜索方向上迭代地采取步骤的概念。并且两者都可以使用梯度信息来定义搜索方向。这可以通过仅仅改变方法参数来完成。这将导致一个可行点,具有很大的目标函数值。

**sol_tr_vessel = minimize(
    f_vessel, [2, 2, 50, 50],
    jac=grad_vessel,
    bounds=bounds_vessel,
    constraints=cons_vessel,
    method="trust-constr"
)**

现在,让我们实现 DE,看看它的性能如何。

**sol_de_vessel = differential_evolution(
    f_vessel, bounds=bounds_vessel,
    constraints=cons_vessel,
    popsize=50, strategy="rand1bin",
    recombination=0.7, mutation=(0.3, 1.0),
    maxiter=300, seed=12,
    init='latinhypercube', polish=False
)**

虽然找到了可行解,但是 DE(没有 finding)并不能产生低至trust-const的目标函数值,仍然取了更多的函数调用。

因此,在 DE 之前尝试其他算法仍然是有用的,特别是如果没有多模态或不可微项的证据。

添加离散变量

增加离散变量使得这个问题不可微。因此,在这种情况下,预期 DE 是必要的。使用基于树的模型来映射目标函数的情况在实践中并不罕见。这些也是不可微的问题,DE 为解决它们提供了一个有用的选择。

让我们修改我们的目标函数,它将连续的实变量 x1x2 解释为 0.0625 的离散倍数。

**def integer_x(x):
    x[0] = int(x[0] / 0.0625 + 1) * 0.0625
    x[1] = int(x[1] / 0.0625 + 1) * 0.0625
    return x

def f_vessel_minlp(x):
    x = integer_x(x)
    return f_vessel(x)**

以及实施…

**sol_de_minlp = differential_evolution(
    f_vessel_minlp, bounds=bounds_vessel,
    constraints=cons_vessel,
    popsize=50, strategy="rand1bin",
    recombination=0.7, mutation=(0.3, 1.0),
    maxiter=300, seed=12,
    init='latinhypercube', tol=1e-8, polish=False,
)**

这与 Lampinen & Storn (2004)报告的结果完全相同,因此可以认为是成功的。

**constr_violation: 0.0
fun: 7197.731584692868
Real x: [ 1.125       0.625      58.29011889 43.69286662]**

进一步阅读

差分进化是众多基于种群的算法中的一种,这种算法在解决优化问题时非常有效。我建议感兴趣的读者探索粒子群优化遗传算法作为两个强大的替代方案。

此外,在本文中,我们已经看到了差分进化算法在单目标问题中的一些应用。但是如果我们追求多个相互冲突的目标呢?DE 有几个扩展来处理多目标优化,在这些情况下非常有用。感兴趣的人会发现 python 库 pymoode 非常有用。在另一篇媒体文章中有一个概述:

* *

结论

在本文中,使用 scipy 实现,用实际的实现例子解释和说明了差分进化的基本理论方面。提出了凸的、多峰的和不可微的问题,其中 DE 与其他优化方法相比具有其性能。所使用的代码在这个 GIT 资源库中对读者完全可用。

参考

Bilal 等人,2020 年。二十多年研究的回顾。英国工程师。应用程序 Artif。智能。,第 90 卷,第 103479 页。

Lampinen,2002 年。差分进化算法的约束处理方法。2002 年进化计算大会会议录。CEC’02,第 2 卷,第 1468–1473 页。

兰皮宁,j .和斯托恩,r .,2004 年。差异进化。工程中的新优化技术。柏林,海德堡:施普林格出版社,第 123-166 页。

普莱斯,K. V .,Storn,R. M .和 Lampinen,J. A .,2005 年。差分进化:一种实用的全局优化方法。第 1 版。柏林。

桑德格伦,1990 年。机械设计优化中的非线性整数和离散规划。《机械设计学报》,* 112(2),第 223–229 页。*

*storn r .和 Price k .,1997 年。差分进化——连续空间全局优化的简单有效的启发式算法。 *J。Optim。,第 11 卷第 4 期,第 359—341 页。

扩散模型

原文:https://towardsdatascience/diffusion-models-91b75430ec2

它们是什么,它们是如何工作的,为什么是现在?

来源:由稳定扩散生成…因为我当然必须这么做。

这篇文章旨在帮助你推导和理解扩散模型。如果你读完这篇文章后的第一个想法是,“为什么我没有想到这个?!?"那好吧,我成功了🎉。如果没有,好吧,还是谢谢你🤝希望你旅途愉快。

我们不会重建稳定扩散,但我们会在这一集结束时制作一些玩具模型来展示一切是如何工作的。除了这篇文章,我还创建了一个配套的 Github 库来收集所有与扩散相关的东西。在几个点上,我会参考回购的更多细节。截至 2022 年底,有两件事你会想看看:

  1. 在玩具数据集上实现扩散模型的简单代码(参见 DDPM 类和这个玩具脚本)。
  2. 全教程,含数学。填补了这个帖子所掩盖的任何空白,也有一些有趣的物理东西。如果你觉得这个帖子有意思,我推荐通读一下笔记!

强制性的非技术性介绍

我不想撒谎。出于尴尬,我开始写这篇文章。

感觉像是上辈子以前我曾在统计物理领域工作,这可以用一个想法来概括,“是的,一个粒子很酷,但你知道什么更酷吗?其中 10 个。”统计物理是一个广阔的领域,但毫不夸张地说,非平衡物理的研究在其中占有突出的地位。非平衡物理学是现代物理学中令人兴奋的领域之一,我们仍然没有真正理解,但仍然包含大量有意义的发现的机会。

然而,如果你要我准确描述什么是非平衡物理,我真的不能告诉你。如果你随便选一个物理问题,问我“这是非平衡的吗?”我可能会回答,"不,这是帕特里克……"然后,在尴尬的沉默中坐下来,决定是笑还是退缩。但是,有一种现象我可以说确实属于非平衡物理,因此应该属于我的“专业知识”领域:扩散。

如果你已经跟上了 2022 年的 ML 流行文化,那么“扩散”这个词应该会触发你的人工智能流行词警报⏰。当我第一次注意到人们开始随便折腾非平衡物理术语时,我感到胃里一阵剧痛。我的思绪淹没在漫漫长夜中,苦读着范·坎彭的 物理和化学中的随机过程 。但是,如果有一个足够强大的动机来克服数学引起的恶心,那就是尴尬。由于害怕有人会问我扩散模型是如何工作的,我开始尽快研究它们。这篇文章不是关于我试图自我证明我的教育或抱怨缓慢(lol ok *快)*忘记我在研究生院学到的一切。不,这是关于分享一些我来之不易的知识,关于扩散模型如何在基本水平上运作。谢天谢地,原来这些东西都挺酷的!

什么和为什么

什么是扩散模型?

  1. 设计用于从分布 p(x)中有效抽取样本的模型。
  2. 生成模型。他们学习一些数据的概率分布 p(x)。
  3. 自然无监督(这与整个生成部分密切相关),尽管您可以对它们进行调节或学习有监督的目标。
  4. 不是真正的模特。扩散模型泛指调度程序、先验分布和转移核(通常由神经网络参数化)的集合。结合起来,这些片段可以从 p(x)生成样本。

为什么扩散模型很酷?

扩散模型并不是人们发明的第一个生成模型,问我们为什么特别关心这些模型是公平的。为了说明原因,让我们回顾一些历史。

你可能会很自然地想到,在深度学习时代,学习概率分布 P(x)是微不足道的:加载你最喜欢的神经网络,制作一个参数化的函数 E_𝝑(x,并通过最小化| p(x)-e_𝝑(x)|.来学习值𝝑然而,这是行不通的。原因是正常化。P(x)不是我们想要学习的任意函数。它必须服从∫ P(x) dx = 1 的约束,也就是说如果我们要学习一个无约束函数 E_𝝑(x),我们实际上需要优化|P(x) — E_𝝑(x) / ∫ E_𝝑(x) dx|,这个现在因为积分完全拧了。归一化常数 Z(𝝑) = ∫ E_𝝑(x) dx 也称为配分函数,通常用大写字母 z 表示。遵循学习无约束函数 E_𝝑(x 方法的模型称为基于能量的模型 (EBMs)。

值得注意的是,有几种方法可以克服归一化常数问题。冒着错过大量研究的风险,我建议读者参考 2015 年论文使用非平衡动力学的深度无监督学习的第 1.2 节,以获得更好的概述。我将简单记下一些比较流行的方法:

  1. 通过用随机样本近似积分的通常方法,在训练期间估计归一化常数 Z(𝝑。抽取一个好的随机样本是这里最难的部分。这被称为对比发散训练。
  2. 了解一些简单的、固有的归一化函数 q_𝝑(x 的参数,其中近似于 P(x)。这就是变分法。
  3. 仔细构建一组可逆的、可训练的变换,这些变换采用简单的标准化概率分布(像正态分布)并将其转化为更复杂的东西。这是规范化流程的方法。
  4. 求解导数𝛁_x P(x)而不是 P(x)。由于𝛁_xz(𝝑= 0,这就完全消除了归一化常数。这很好,但是还不清楚如何利用这一点。这种方法被称为分数模型,事实证明,对于某些培训目标,它可以被证明为等同于扩散模型(参见倒数第二节注释的基于分数的模型和扩散模型的等效性)
  5. 将自己从标准化的暴政中解放出来,只需学习一个直接生成样本的函数。让你不稳定的模型对着你的耳朵说一些关于博弈论的甜言蜜语,就像你慢慢忘记学习 P(x)的必要性一样。这是甘斯的做法。

扩散模型很有趣,因为它们把自己作为条目(6)添加到上面的列表中。它们提供了处理规范化问题的不同方式。

基本的见解

这个想法

您可以将扩散模型方法视为我们之前列出的避免归一化常数的方法中的方法(3)和(4)的混合。扩散模型源自一个简单的想法:

与其直接尝试对分布 P(x)进行建模,不如我们可以找到一些操作,采用一个蹩脚的答案 P_crap(y),并将其转换为稍微好一点的答案 P_better(x)怎么样?

为什么这会有帮助?假设我们发现了这样一个操作。如果这个操作可以采用任何旧的猜测 P_crap(x)并使其更好,即使是无穷小的,那么理论上我们可以一遍又一遍地重复这个操作,直到我们达到正确的分布 p(x)!现在让我们把第(6)项加入我们的清单

6.求解一个取归一化分布 q_t(x)的转换核,把它变成稍微好一点的归一化分布 q_{t+1}(x)。这暗示着 p(x) = q_{∞}(x)。这就像一个连续的正常化流程。

如果你想要一个有趣的,尽管不完全类似的,正在发生的事情的视觉表现,有一种叫做镍钛诺的令人着迷的材料,它能够记住自己的形状。这里有一个用这种材料制成的回形针的视频。成型的回形针可以认为是我们要学习的分布 p(x)。向前(扩散)的过程将相当于拉直回形针,使其形成一个漂亮而简单的均匀分布。逆向(生殖)过程是将它倒入热水中,看着它卷曲回原来的形状。

抛开对阿松的生财怜款

过渡核以一个 D 维 x_t 作为输入,返回另一个 D 维向量 x_{t+1}作为输出,意味着核产生一个 向量场 。这里有一个展示向量场的演示。确保关闭等电位曲线。

观察到我们正在学习一个向量场是很有帮助的。它允许我们问这样一个问题,“如果我们什么都没学到,只是直接计算出如果训练数据集中的每个点都被认为是一个微小的质量时会产生的引力场,会怎么样?”你可以的。您刚刚创建的是一个函数,它取空间中的任意点,并将其映射到训练数据中的现有点。这本身并不坏,但我们真正想要的是插值,即映射到不在训练数据集中的点的能力。我可以想到两种方法来实现这一点。第一种方法是在这个映射过程中添加一些随机噪声。这可以做得很严谨,并且是扩散理论的基础。这就是我们在这篇文章中探索的方法。

第二种方法,是仔细构建先验分布和向量场,这样就存在一个确定性的方程来映射 p(x)的先验。这是泊松生成流模型背后的中心思想。还有更多的空白要填,但这就是它的主旨。它们简单、强大,相对于扩散模型有很多好处。我将在以后的文章中讨论这些问题。

方程式

在这一小节中,我们将把我们的想法(使用一些运算将垃圾转化为更少的垃圾)转化为数学。从贝叶斯方程中,我们可以知道我们可以引入一个辅助变量 y,这样 p(x) = ∫ p(x,y) dy = ∫ p(x | y) p(y) dy。现在,让我们将时间离散为步长 t ϵ ℕ,并将 p(x)定义为某个最终时间 t 的 p(x_t)。使用贝叶斯方程,我们可以写出对任何分布 p(x)都有效的一般马尔可夫序列:

我们将调用函数 p(x _ t | x _ { t-1 };𝜗)的跃迁内核。我们已经通过一些参数𝜗将其参数化,这正是我们要训练我们的模型学习的。我们还可以引入一个连续的版本,p _∏t(x | y ),它取决于一个小的时间步长 t。直观地说,转移核 p _∏t(x | y)表示,在时间 t+t 的一个点 x 的概率密度 p(x)等于在时间 t 的所有其他点 y 的概率密度之和,乘以从 y 跳到 x 的转移概率 p(x | y)。换句话说,到达某个地方的唯一方法是要么从那里开始,要么从某个地方开始将所有这些方法相加,根据旅行的可能性进行加权,你就得到了新的概率。

在这一点上,你可能会尝试采用形式极限 t -> 0,并创建一个∂p/∂t 项。这样做不会对我们有太大帮助,但我可以告诉你这条路上会有什么。你实际上是试图重新推导出 主方程 (链接指向 Kramers-Moyal 展开式,因为它有我想要的主方程版本)。这个方程增加了一个我在直觉中没有提到的部分,即建立一个一般的积分微分方程,你还必须考虑在 t=0 时处于正确位置的一些概率在 t =∏t 时移动到错误位置的可能性,无论如何,这里不需要主方程的完整机制。

我们现在有了给出数据点 x_t 的对数似然的基本方程,并且我们有一些想要学习的参数。为了通过梯度下降来学习这些参数,我们需要找到一个损失函数来最小化。不过这并不是最难的部分。困难的部分是处理所有的积分和变量 x_0,…,x_t,我们刚刚介绍过的。

物理学视角

这一节对于继续推导扩散模型算法是不必要的,但是它将从不同的角度使事情变得更清楚。

首先,我要写下三个方程,并声称这三个方程描述了同一个物理过程:受某个势 V(x)支配的粒子系综的演化。这些方程是朗之万方程、福克-普朗克方程和翁萨格-马赫卢普泛函。

对于翁萨格-马赫卢普泛函,我能找到的最好的资源是 646 页的奥尔特兰和西蒙斯凝聚态物理。注意,在任何固定时间,η(t)都是从正态分布中得出的!

让我们从中线开始,福克-普朗克(FP)方程。它的定态解出现在∂p/∂t = 0 时,解为 p ~ e^{-V / D}。重新排列,我们发现对于选择 V(x) = -ln q(x),定态解将是 q(x)。换句话说,我们可以写出一个 FP 方程,它在很长时间内会给出我们想要的精确分布。

如果将我们巧妙选择的 V(x)代入朗之万方程(第一行),然后引入小时间ϵ并离散化,我们可以找到朗之万马尔可夫链蒙特卡罗(MCMC)的定义方程

只要我们知道我们想要取样的分布的梯度,我们就可以用上面的方程模拟一堆粒子 x 的随机动力学,从而抽取样本。另外,注意我们只需要 q(x)的梯度。听起来熟悉吗?这正是我们在第(4)节“为什么扩散模型很酷”中定义的分数。这意味着我们不需要一个归一化常数来从 q(x)中采样🎉。

我们现在知道如何定义一个分布,并在已知分数的情况下从中抽取样本。最后,我们可以使用路径积分表示来定义帮助我们学习分数的训练目标。注意如果我们离散化这个动作会发生什么(指数中的东西);我们得到类似 e^{-(x_{t+1} - x_t -ϵ F_t) / 4ϵ}的东西,其中 F_t = -𝛁 V(x_t)。这只是一个正态分布,它的形式和我们之前的转移核公式一样。我们发现,有一个非常令人信服的理由为什么我们应该选择高斯核,除了它是物理学中唯一一个知道如何处理的函数🤷。‍:如果我们也离散化这个测度,我们会发现一个无限的转移核乘积,它再现了我们之前的马尔可夫序列方程。

先不说这个总结:

  1. Fokker-Planck 方程告诉我们,存在一个稳定的分布,具有我们想要的值 p(x ),和由分数函数给出的力。
  2. 朗之万方程可以从 p(x)中抽取样本。
  3. 离散化路径积分再现了马尔可夫序列,该序列可用于制作优化我们的模型的损失函数。

抛开朗之万不谈

请随意跳过这一步。仅供参考:有更好的方法来采样高维函数,许多聪明人已经花了很多时间来思考它们。在这里,我只想证明朗之万·麦克公司的这种方法确实有效。这里有一些代码将使用 Pytorch 来计算我制作的随机函数的对数梯度,然后用 20k 个样本运行 Langevin MCMC 1000 步

import torch
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import quad

# pick any random distribution to sample from
numpy_fn = lambda x: (0.2 + np.cos(2 * x) ** 2) * np.exp(- 0.1 * x ** 2)
norm = quad(numpy_fn, -np.inf, np.inf)[0]
torch_fn = lambda x: ((0.2 + torch.cos(2 * x) ** 2) * torch.exp(- 0.1 * x ** 2) ) / norm
# Run Langevin MCMC
samples, steps, eps = 20000, 1000, 5e-2
x = (2 * torch.rand(samples, requires_grad=True) - 1) * 10
for _ in range(steps):
    potential = torch.autograd.grad(torch.log(torch_fn(x)).sum(), [x])[0]
    noise = torch.randn(samples)
    x = x + eps * potential + np.sqrt(2 * eps) * noise

# Plot against the true distribution
y = torch.linspace(-2 * np.pi, 2 * np.pi, 100)
plt.plot(y, torch_fn(y), 'k--')
plt.hist(x.detach().numpy(), density=True, bins=300)
plt.xlim(-2 * np.pi, 2 * np.pi)

如果您运行代码,您应该会得到类似这样的结果:

我不想撒谎,这个例子是精心挑选的。使用马尔可夫链蒙特卡罗(MCMC)方法进行适当的采样本身就是一项精细的技能,但是希望你可以放心,这至少在理论上是可行的!

训练扩散模型

导出损失函数

当 t 趋于无穷大时,我们希望生成分布 p(x_t)与观测分布 p_data(x)相匹配。然而,由于无穷很难处理,让我们做一些奇怪但符合文献的事情。假设在时间 t=0,我们有期望的分布 p(x,t=0) = p_data(x)。然后,不失一般性地,我们假设在未来的某个时间 t=T,我们已经充分破坏(扩散)了我们的分布,使得 p(x,t=0) = N(x |0,1)。最后,我们需要一个损耗来推动观察到的和参数化的分布彼此更接近。我们可以通过最小化它们的KL-散度来做到这一点,这给了我们看似简单的目标L =-∫p _ data(x _ 0)LNP(x _ 0;𝜗) dx_0。

通常,p_data 是不可积的(我们不知道它,但我们有样本,即数据集)。这可以通过通常的方法来克服,用随机小批量观察数据集的求和来代替积分(积分<=>求和交换)。对数项是困难的一项。使用完全展开的马尔可夫形式,以及随之而来的所有烦人的积分,我们可以这样写:

这里我们用的是简写 dx_1:T = dx_1…dx_T

从这里开始,我们需要去掉积分并简化,直到我们有了可以编码的东西。这样做是一项艰巨的任务,所以我将概述您需要的主要见解。完整的推导过程可在随附注释中找到。

用重要性抽样去除积分

首先我们需要处理那些讨厌的积分。为此,我们将使用一个古老的技巧:重要性抽样。如果你有一些积分,比如∫ f(x) dx,给你带来麻烦,你可以把它近似为其他分布 q(x)的期望值 E_{x ~ q(x)}[f(x)/q(x)]。如果你想听起来很新奇,你可以说你从退火重要性抽样和 Jarzynski 等式中获得了这个想法的灵感。我们称分布为 q(x_0,…,x_T)。任何分布都可以,但问题是,我们选什么?答案是我们能找到的最便宜的。

幽默旁白:有一次我去海外旅行,觉得从当地的一家酒店带几瓶葡萄酒回家会很不错。那时我还是一个穷学生,不用说,我正经历着轻微的贴纸休克。我不知所措,轻轻地走到店主面前,用平静的声音礼貌地问最便宜的瓶子是什么。接下来,我所知道的是,这位老店主睁大了眼睛,用手掌拍着桌子,大声斥责道:“我们不卖便宜的酒!”我想指出的是,作为一名学生不仅仅意味着你破产了,还意味着你很笨拙。因此,我对这位大声嚷嚷的店主的反应是眯着眼睛,好像我在重演 Futurama Fry meme ,并回答说,“好吧……但根据定义,至少像这些葡萄酒中的一种是最便宜的,对吗?”店主感到不安,开始从桌子上抬起她的手,把她的指关节转向我,用拇指捏她的手指。然后她俯下身子,用平静得多的声音向我解释道:“最便宜的。不便宜。我可以卖给你最便宜的酒。直到今天,我都确保从来没有要求过便宜的东西。我总是要求最便宜的🤌.

这就是扩散模型的扩散部分发挥作用的地方。一个基本的扩散过程几乎是我们能为 q(x_0,…,x_T)构造的最简单的马尔可夫分布,它允许一个解析表达式。现在让我们为我们的两个发行版命名:

正向(破坏性/扩散性)过程 : q(x_0,…,x_T)。这就是我们将分析解决的用于重要性抽样的扩散。

逆向(生成式)过程: p(x_0,…,x_T)。这是包含我们的可学习参数和转换内核的东西,将用于生成样本。

现在的损失是:

使用证据下限(ELBO)

技巧#2 是另一个熟悉的技巧(在这种情况下近似)。请使用詹森不等式来优化 ELBO。这意味着你可以将对数移过积分和 q(x_1:T | x_0):

做一些删除,进一步简化,最终你可以用 KL 散度来表示

情商。 DDPM 纸业的 a . 21。没有 KL 分歧。

KL 的分歧。

这是将军 组建

把正向过程变成扩散过程

到目前为止,除了它们是马尔可夫过程之外,我们还没有说任何关于前向或后向过程的东西。我们现在为了易处理而牺牲一般性,把正向过程变成扩散过程。我们将遵循去噪扩散概率模型 (DDPM)论文中的方法,并做出如下选择(诀窍#3!)对于正向转换内核

注意,这个选择是特定于 DDPM 扩散的。它不是所有扩散模型的通用方程。

其中⍺_t’s 是固定的、依赖于时间的值。我们可以看到,通过方差βt = 1 —⍺_t.,它们很容易与不同时间的噪声方差相关联

以后选择更大的方差

我们对扩散核的选择看起来完全是随机的,但它实际上是非常聪明的。请注意,扩散过程是由其方差定义的,这里我们决定方差应该是时间相关的。之前的调度会奇迹般的让 q(x_t | x_0)以封闭形式解析(注释中显示的),并且保证方差不会随时间爆炸。

我还想说一句重要的话。随着样本离 p_data(x)越来越远,选择逐渐变大的方差对于扩散模型的性能至关重要。直觉上,你可以这样想。

假设你远离分布的中心。对你来说,它可能看起来像天空中的星星——所有的形状、凸起和特征都模糊成远处某处的一个焦点。在这个阶段,进行大幅度跳跃是有意义的,直到你足够接近以更好地分辨距离,然后才减小跳跃的幅度。否则,你将花费大量时间来优化随机的东西,这些东西与空间旅行的正确方向毫无关系。

将反向(生成)核定义为高斯核

信不信由你,到目前为止,生成过程仍然是通用的。如果你把物理学放在一边看,下一步的动机应该是清楚的。如果没有,就把它当成一个容易的选择。我们将通过具有可学习的均值和固定方差的正态分布来参数化后向核:p(x_t | x_{t-1}) = N(μ(x,t;𝜗),β(t))。这将把大量的积分转换成两个高斯函数之间的 KL-散度,一般来说,使数学变得更简单。最终,你会发现总损失是每个可能的时间步长损失的总和,每个损失都有相同的形式。结果是:

根据定义的方差,我们定义β_t = 1 — ⍺_t.。我们还将⍺_t 棒线定义为截至时间 t: ∏_0^t ⍺_t.的所有阿尔法的乘积

其中,L_{t-1}是第(t-1)个时间步长的损耗,最后一步是仅用噪声ϵ来重写,因此我们得到:

第一个等式是ϵ_𝜗的定义,第二个等式是 x_t 的定义,第三个等式是期望损耗

μ项只在采样时才需要记住。

最终结果,一个去噪目标

损失表达式非常简单,也非常直观。它说,在任何给定的时间步长,都有一些高斯噪声应用于输入,我们的任务是预测它(因此去噪)。这表明了下面的简化,这在实践中证明是更有效的:降低损失函数中的系数。我们终于达到了扩散算法。在这里,我将从原始论文中复制/粘贴算法,而不是重新键入它

资料来源:Ho、Jonathan、Ajay Jain 和 Pieter Abbeel。"去噪扩散概率模型."神经信息处理系统进展33(2020):6840–6851。

σ_t 只是标准差,由√(1-⍺_t 给出的)(老实说,我不知道他们为什么不能写出来🤷‍♂️).

抽样

我忽略了这一点,但是由于我们有正态分布,采样很容易得到。上面的推导来自于再参数化技巧,即可以通过首先采样 z ~ N(0,1),然后计算 x = + σ z,从 x_T 开始采样,然后反向采样,我们看到 x_{T-1} ~ p(x_{T-1} | x_T)容易采样,这意味着 x_{T-2} ~ p(x_{T-2} | x_{T-1})容易采样等等。这也被命名为祖先采样,它也包含更复杂的图条件依赖。

为了完整起见,如果您查看算法 2 中的步骤(4 ),请注意它具有 Langevin 方程的形式,正如我们之前讨论的那样。

解决纷争

不良投入的问题

当我第一次读到最终的 DDPM 算法时,老实说,我对它的工作感到非常震惊。为什么我们的模型能够从撤销一些应用于它的随机高斯噪声中得到任何信息?当我们的数据点 x 远离分布的中心时,这将如何工作呢?当我们实际上只得到一张时间快照时,我们怎么能学会动态变化呢?

对于第一个问题,答案是如果看情商。(5)在训练算法中,我们实际上是将噪声输入到模型中。这里的关键点是,我们也在时间中进食。这些额外的信息似乎会产生影响。我的猜测是,它允许模型推断它有多接近真实分布,并使用它来更好地过滤噪声。

对于第三个问题,我认为答案是组装。如果平衡分布已知,写下福克-普朗克方程就很简单了。不管怎样,这就是它们最初的设计目的。一般来说,我们可以通过(a)长时间模拟一些粒子或者(b)短时间模拟大量粒子来确定平衡。随着你获得越来越多的数据,我认为你越来越接近选项(b ),这就是为什么这是可行的。

对于第二个问题,答案是不会。lol。我不想伤害研究过这个的 OG 作者,所以我尊重他自己的关于这个的博客。在标题为“天真的基于分数的生成模型及其缺陷”的章节中,你会找到要点。简而言之,低密度区域完全陷入了损失函数。这是学习左下角和右上角的两个高斯流的视觉效果:

来源:https://yang-song/blog/2021/score/

我认为这个问题在泊松流模型中得到了一定程度的解决,因为这些模型偏向于复制单极场。但是,我没有证据证明这一点。

高差异

最糟糕的感觉是看着你的扩散模型慢慢地越来越接近一个相当好的图像,只是在最后一刻大喊 skrt skrrrrtttt,然后转向一个看起来像 90 年代的电视调到 100 频道。在步长和方差之间有一个折衷。更小的方差+更多的步数总是随机微分方程的更精确的表示,但是它也更加计算密集。这里一定要注意。

编码

让这些东西在实践中工作实际上有点烦人,主要是由于上一节中关于低密度区域的问题(2)。您需要确保在能够通过扩散从真实目标分布到达的区域中对先验分布进行采样。此外,您需要调整扩散步骤的数量和变化时间表。其他一些论文(参见改进的去噪扩散概率模型)着手优化这些东西,发现用一些简单的时间 1D 函数代替我们推导的花哨的离散方差时间表可以做得更好。

至于编码,实际上非常简单。你需要:

  1. 将向量 x 和时间 t 作为输入,并返回与 x 维数相同的另一个向量 y 的模型。具体来说,该函数类似于 y = model(x,t)。根据您的变化计划,对时间 t 的依赖可以是离散的(类似于变压器中的令牌输入)或连续的。如果是离散的,你可以使用老式的变换位置编码(不要脸的自插),如果是连续的,你可以使用高斯随机特征。
  2. 一个 Mixin,处理特定模型的所有调度、采样和去噪损失计算。

为了说明这是如何工作的,我们将尝试使用扩散模型来学习一个简单的 2D 螺旋模式。这是最初的 2015 论文中使用的玩具数据集,所以我们将使用 DDPM 重做它。

作为调度器的例子,你可以在这里查看 DDPM 类。为方便起见,我复制/粘贴以下内容:

import torch
from torch import nn

class DDPM(nn.Module):
    """Dataclass to maintain the noise schedule in the DDPM procedure of discrete noise steps
    Mathematically, the transition kernel at time $t$ is defined by:
    $$
    q(x_t|x_{t-1}) = \mathcal{N}(x_t| \sqrt{\alpha_t} x_{t-1}, 1 - \alpha_t)
    $$
    We further define quantities $\beta$ and $\bar \alpha$ in terms $\alpha$:
    $$
    \beta_t \equiv 1 - \alpha_t
    $$
    $$
    \bar \alpha_t = \prod_{t' < t}\alpha_{t'}
    $$
    which will be useful later on when computing transitions between non adjacent times.
    """

    def __init__(self, n_steps: int, minval: float = 1e-5, maxval: float = 5e-3):
        super().__init__()
        assert 0 < minval < maxval <= 1
        assert n_steps > 0
        self.n_steps = n_steps
        self.minval = minval
        self.maxval = maxval
        self.register_buffer("beta", torch.linspace(minval, maxval, n_steps))
        self.register_buffer("alpha", 1 - self.beta)
        self.register_buffer("alpha_bar", self.alpha.cumprod(0))

    def diffusion_loss(self, model: nn.Module, inp: torch.Tensor) -> torch.Tensor:
        device = inp.device
        batch_size = inp.shape[0]

        # create the noise perturbation
        eps = torch.randn_like(inp, device=device)

        # convert discrete time into a positional encoding embedding
        t = torch.randint(0, self.n_steps, (batch_size,), device=device)

        # compute the closed form sample x_noisy after t time steps
        a_t = self.alpha_bar[t][:, None]
        x_noisy = torch.sqrt(a_t) * inp + torch.sqrt(1 - a_t) * eps

        # predict the noise added given time t
        eps_pred = model(x_noisy, t)

        # Gaussian posterior, i.e. learn the Gaussian kernel.
        return nn.MSELoss()(eps_pred, eps)

    def sample(self, model: nn.Module, n_samples: int = 128):
        with torch.no_grad():
            device = next(model.parameters()).device

            # start off with an intial random ensemble of particles
            x = torch.randn(n_samples, 2, device=device)

            # the number of steps is fixed before beginning training. unfortunately.
            for t in reversed(range(self.n_steps)):
                # apply the same variance to all particles in the ensemble equally.
                a = self.alpha[t].repeat(n_samples)[:, None]
                abar = self.alpha_bar[t].repeat(n_samples)[:, None]

                # deterministic trajectory. eps_theta is similar to the Force on the particle
                eps_theta = model(x, torch.tensor([t] * n_samples, dtype=torch.long))
                x_mean = (x - eps_theta * (1 - a) / torch.sqrt(1 - abar)) / torch.sqrt(
                    a
                )
                sigma_t = torch.sqrt(1 - self.alpha[t])

                # sample a different realization of noise for each particle and propagate
                z = torch.randn_like(x)
                x = x_mean + sigma_t * z

            return x_mean  # clever way to skip the last noise addition

对于一些样本模型在离散和连续时间的情况下,你可以在这里查看代码。

训练过程与普通训练过程非常相似,除了在您的批处理中没有目标,您必须使用 DDPM 调度程序中的 diffusion_loss 方法来计算损失。你可以在 DDPM 包的 main.py 脚本中找到训练循环。

如果训练成功,您会发现生成的分布如下所示:

从随机的高斯斑点到整齐有序的螺旋。

包装东西

在过去的几年里,扩散领域的事情发展得非常快,这是有充分理由的。至少可以说,最近(2022 年)的一些模型,如 Dalle-2、StableDiffusion 和 Midjourney 的结果令人惊讶。结合当前对生殖人工智能的狂热,你有新的快速发展。因此,除了这篇文章之外,还有很多令人兴奋的东西可以探索。

首先,我没有提到任何流行的文本到图像的模型。这些都是大型的、复杂的、多模态的架构,许多 GPU 超出了本文的范围。进一步研究的其他途径可能包括实际训练扩散模型的更好方法。我只是简单地提到了一些关于优化这些的研究,但是当然你可以做得更深入。另一个有趣的途径是观察条件模型。我提到的一些论文也涉及到这一点,但我不想说得太远。已经有很多有趣的工作来引导这些扩散模型走向特定的结果,无论是阶级条件作用还是其他。其他领域的研究着眼于新颖性和保真度之间的权衡,以及如何以其他方式调整输出,如通过负采样。

对我来说,在开始写这篇博客之前,我想回答的最大问题是,“这些东西实际上是如何工作的,为什么?”我希望这篇文章至少给出了一些部分令人满意的答案😄!

本文标签: 中文翻译博客TowardsDataScience一百三十七