检查悖论无处不在
检查悖论是一种你可能从未听说过的统计错觉。这是一个常见的混淆来源,一个偶然的错误原因,也是一个聪明的实验设计的机会。
一旦你了解它,你会发现它无处不在。
多少学生?
我最喜欢的一个例子是班级规模的明显矛盾。假设你问大学生他们的班级有多大,然后平均回答。结果可能是 90。但是如果你问学校平均班级人数,他们可能会说 35 人。听起来有人在撒谎,但他们可能都是对的。
当你调查学生时,你对大班进行了过度抽样:如果一个班有 10 个学生,你有 10 次机会对那个班进行抽样;如果有 100 个学生,你就有 100 个机会。一般来说,如果班级人数是 x,那么它在样本中的代表性将是 x 倍。
那不一定是错误。如果你想量化学生的经验,学生的平均水平可能比班级的平均水平更有意义。但你必须清楚你在衡量什么,以及如何报告。
顺便说一下,这个例子中的数字不是我编的。它们来自普渡大学报告的 2013-14 学年本科生班级规模的数据。
根据他们的报告,我估计了班级规模的实际分布(用猜测来填补缺失的数据)。然后,我计算出你通过对学生抽样得到的“有偏”分布。图 1 显示了结果。
Figure 1: Undergraduate class sizes at Purdue University, 2013–14 academic year: estimated distribution as reported by the University and biased view as seen by students.
学生样本不太可能包含小于 40 人的班级,而更可能包含较大的班级。
反过来说,如果给定的是有偏分布,那么你可以反过来估计实际的分布。如果实际分布不可用,或者更容易运行有偏采样过程,则可以使用这种策略。
同样的效果也适用于客机。航空公司抱怨说他们在赔钱,因为很多航班几乎都是空的。与此同时,乘客抱怨飞行很痛苦,因为飞机太满了。他们可能都是对的。当航班几乎空无一人时,只有少数乘客享受额外的空间。但是当航班满员时,许多乘客会感到压力。
我的火车在哪里?
当你在等公共交通工具时,检查悖论也会发生。公共汽车和火车应该以固定的时间间隔到达,但实际上有些时间间隔比其他时间间隔长。
随着你的运气,你可能会认为你更有可能在很长的时间间隔内到达。你是对的:一个随机的到达更有可能落在一个很长的区间,因为,嗯,它更长。
为了量化这种影响,我收集了波士顿红线的数据。通过使用 MBTA 的实时数据服务,我记录了几天来下午 4 点到 5 点之间 70 趟列车的到达时间。
火车之间的最短间隔不到 3 分钟;最长的超过 15 年。图 2 显示了列车之间的实际时间分布,以及乘客可能观察到的偏差分布。
Figure 2: Distribution of time between trains on the Red Line in Boston between 4pm and 5pm; actual distribution as seen by the train operator and biased distribution seen by passengers.
火车之间的平均时间是 7.8 分钟,但有偏分布的平均值是 9.2 分钟,几乎长了 20%。
在这种情况下,两个分布之间的差异是适中的,因为实际分布的方差是适中的。在下一个例子中,当方差较高时,检验悖论的影响会大得多。
你受欢迎吗?
1991 年,斯科特·费尔德提出了“友谊悖论”:观察到大多数人的朋友比他们的朋友少。他研究了现实生活中的社交网络,但同样的效果也出现在在线网络中:如果你随机选择一个脸书用户,然后随机选择他们的一个朋友,那么这个朋友有更多朋友的几率约为 80%。
友谊悖论是检验悖论的一种形式。当你选择一个随机用户时,每个用户都有同等的可能性。但是当你选择他们中的一个朋友时,你更有可能选择一个朋友很多的人。具体来说,有 x 个朋友的人代表了 x 倍。
Figure 3: Number of online friends for Facebook users: actual distribution and biased distribution seen by sampling friends.
为了演示这种效果,我使用了来自斯坦福大型网络数据集集合的数据,其中包括大约 4000 名脸书用户的样本。我计算每个用户的朋友数量,以及他们朋友的朋友数量。图 3 显示了这两种分布。
区别是实质性的:在这个数据集中,普通用户有 44 个朋友;普通朋友有 104 个,是两倍多。而你的朋友比你更受欢迎的概率是 76%。
路怒症
检查悖论的一些例子更加微妙。其中一个是我在新罕布什尔州参加 209 英里接力赛时想到的。我为我的团队跑了第六段,所以当我开始跑的时候,我跳到了比赛的中间。几英里后,我注意到一些不寻常的事情:当我超过跑得慢的人时,他们通常要慢得多;当跑得更快的人超过我时,他们通常要快得多。
一开始我以为跑者的分布是双峰的,跑得慢的多,跑得快的也多,中间像我这样的跑者很少。然后我意识到我被检查悖论愚弄了。
在长距离接力赛中,不同速度的跑步者最终分散在整个跑道上;如果你站在一个随机的地方,看着跑步者经过,你会看到一个有代表性的速度样本。但是如果你中途跳入比赛,你看到的样本取决于你的速度。
无论你跑得多快,你都更有可能超过跑得慢的人,更有可能被跑得快的人超过,而且不太可能看到任何人和你跑得一样快。具体来说,看到另一个跑步者的机会与你和他们的速度之差成正比。
我们可以用传统公路赛的数据来模拟这种效应。图 4 显示了马萨诸塞州 10K 比赛 James Joyce Ramble 的实际速度分布。它还显示了跑步者在 7 英里/小时时看到的有偏分布。
Figure 4: Distribution of speed for runners in a 10K race, and biased distribution as seen by a runner at 7 mph.
实际分布中,7 mph 附近的跑者很多。但是如果你以那个速度跑,你不太可能看到它们。观察到的分布有两种模式,快速和慢速跑步者过采样,中间的跑步者较少。
即使你不是一个跑步者,你可能也注意到了高速公路上同样的效果。你更可能看到开得太快或太慢的司机,而不太可能看到像你这样安全、通情达理的司机。
桔子你高兴你问了吗?
当我读到派珀·科曼的回忆录《橙色是新的黑色》时,我想到了检查悖论的最后一个例子,他在联邦监狱呆了 13 个月。克尔曼对她的狱友服刑的时间长度表示惊讶。她感到惊讶是正确的,但事实证明,她不仅是不人道的监狱系统的受害者,也是检查悖论的受害者。
如果你在随机的时间到达监狱,随机选择一个囚犯,你更有可能选择一个刑期很长的囚犯。同样,被判 x 刑的囚犯被过采样 x 倍。
但是如果你观察一个监狱超过 13 个月会发生什么?算出来,如果你的刑期是 y,和一个刑期是 x 的犯人重叠的几率是和 x + y 成正比的。
使用来自美国联邦监狱局的数据,我估计了法官看到的联邦囚犯的实际刑期分布,一次性访问者看到的有偏分布,以及被判 13 个月徒刑的囚犯看到的部分有偏分布。图 5 显示了三种分布。
Figure 5: Distribution of federal prison sentences as seen when sentenced, when observed by a random visitor, and when observed by a prisoner, like Piper Kerman, with a 13-month sentence.
在不偏不倚的分布中,几乎 50%的囚犯服刑不到一年。但是短期囚犯比终身囚犯更不容易被观察到。对于一次探访者来说,不到 5%的囚犯刑期不到一年。
一个短期服刑的囚犯所看到的分布只比一个一次性来访者所看到的偏差稍微小一点。实际分布的平均值为 3.6 年;有偏分布的平均值几乎是 13 年。对一个 13 个月的观察者来说,平均值大约是 10 年。
我在这个例子中使用的数据集是 2019 年 7 月监狱人口的快照。所以报道的分布是有偏差的;我不得不“不偏不倚”它来估计实际分布。
此外,联邦囚犯通常服刑 85%的名义刑期。我在计算中考虑到了这一点。
够了吗?
总之,检查悖论出现在许多领域,有时以微妙的方式出现。如果你没有意识到这一点,它可能会导致统计错误,并导致无效的推论。但在许多情况下,这是可以避免的,甚至可以作为实验设计的一部分来使用。
你可以在我的书中读到更多关于这些例子的内容,这些书可以从绿茶出版社免费获得,也由奥赖利媒体出版。
- 我在 Think Stats 第二版 ( 附属链接)中讨论班级规模的例子,
- 中的红线示例认为贝叶斯 ( 附属链接),
- 以及 中的友情悖论认为复杂,第二版 ( 附属链接)。
我用来分析这些例子并生成图表的代码在这个 Jupyter 笔记本里,这个笔记本在GitHub的这个资源库里。并且你可以自己在 Binder 上运行代码。
本文中的图形和数字是基于随机抽样和我对分布所做的一些假设,所以它们应该被认为是近似正确的。
进一步阅读
约翰·艾伦·保罗斯在 2011 年 1 月 18 日的《科学美国人》中提出了友谊悖论。
关于该主题的原始论文可能是斯科特·费尔德,“为什么你的朋友比你有更多的朋友”,《美国社会学杂志》,第 96 卷第 6 期(1991 年 5 月),第 1464–1477 页。
Amir Aczel 在 2013 年 9 月 4 日的《发现》杂志博客文章《论坏运气(和好运气)的持久性》中讨论了其中的一些例子,以及一些不同的例子。
在Python 漫游中,Jake Vanderplas 展示了 Python 代码来探索永恒的问题“为什么我的公交车总是晚点?》,2018 年 9 月。
关于作者
艾伦·唐尼是马萨诸塞州奥林学院的计算机科学教授。他是一名赛跑运动员,10K 最高速度为每小时 8.7 英里。
A/B 测试背后的直觉
A/B 测试基础
新产品经理入门
Photo by freestocks.org on Unsplash
目的
假设检验如 A/B 检验的意义是什么?就此而言,我们为什么要测试新事物?A/B 测试的结果告诉我们什么?你应该对 A/B 测试的结果有多少信心?你如何真正理解 A/B 测试中发生的事情?产品经理如何避免那些刚刚踏上征途的人常见的错误?
这篇文章的目的就是要回答这些问题。
背景
当所提供的内容发生变化时(无论这种变化是引入了一种新药,还是对一个网站进行了更改),我们都想知道
- 变革是否达到了预期的效果。
- 观察到的任何影响都是您引入的变化的结果。
例如:在一种新的糖尿病药物的情况下,你想知道这种药物是否有预先估计的影响,而不是给药。在一个试图销售某种商品的在线网站的情况下,你想知道你的新想法是否会使最终采取某种行动(或者你的衡量标准可能是什么)的人的百分比从当前状态增加 X%。
让我们用一个例子来说明这一点:
想象一下,你现在有一个卖海报的 PostersRCheap.com 网站。用户将海报添加到他的购物车中,然后结账。在购物车页面上,用户当前会看到一个带有文本“Check Out”的按钮。让我们想象以下对话发生在你(产品经理)、Anna(你团队中的一名工程师)和 Victor(你团队中的一名测试人员)之间。
背景:你的一名团队成员安娜观察了一个竞争对手的网站,发现在他们的网站上,购物车页面上写着“给我发送我的海报”。【我们称之为“观察”】安娜心想,“真有趣。我认为,告诉人们发送海报的按钮文本更有吸引力,会吸引更多人结账。”— 我们称之为另类假设。
Control vs. Variant for the A/B Test
安娜:我看到了 PostersRUs.com 正在做的一件有趣的事情。在他们的购物车页面上,按钮上写着“给我发送我的海报”,我认为我们应该在我们的网站上这样做,这将提高我们的退房率。
维克多:维克多是一个怀疑论者。维克多认为做出这种改变不会有任何影响。我们称之为“零假设”。
你:作为产品经理,你要决定怎么进行。你的选择是 a)相信胜利者,什么也不做 b)相信安娜,做出改变 c)科学一点,测试这个假设。你刚刚学习了 A/B 测试,你认为这是一个尝试的好地方。
你 : “那么安娜,我们将测试你的假设。你觉得会对退房率有多大改变?”
安娜 : “我不知道。这不就是测试的目的吗?”
你 : 嗯…不完全是。测试的目的是确定你所声称的改进是否存在。另外,我需要这个数字来设计我的测试。例如,如果你说,这个测试将使我们的结帐率从 20% (我们称之为基本比率) 增加到所有去购物车的用户的 40%,那么既然这是一个如此大的变化,直觉上它应该更容易(更快)观察到。如果你说,这项测试将把检出率从 20%提高到 20.4% (2%的相对变化),那么这种变化是如此之小,可能很难发现这种变化。”直觉上你知道这是有道理的。
安娜:好吧,它不能像 100%那样剧烈,否则其他公司也会这么做。所以我会想象这是微不足道的影响。让我们测试一下它是否至少是 10%。因此,我认为,鉴于我们上周的退房率为 20%,新的转换率将为 22%。我计算过,这一变化将使我们在今年增加 85,000 美元的收入。
你 : 好吧。让我们试试这个。我们把这些数字代入 埃文·米勒的 A/B 样本量 估计量。我发现每个变量需要 6347 个样本(或者总共大约 12700 个)才能检测到至少 10%的影响。我们通常在您的购物车页面上有 800 个客户,因此您大约需要 16 天的时间来完成测试。
你 : 安娜,我们能不能实施 A/B 测试,让最终进入购物车的一半人看到“结账”,另一半人看到“把我的海报发给我。”然后让我们等 16 天。【你也读过 Evan Miller 的文章 如何不进行 A/B 测试 (尽管你并没有真正完全理解。)所以你不用看接下来 16 天的结果。】
幕后是怎么回事?让我们看看一些基本的想法。
Screenshot from the excellent A/B sample size calculator tool by Evan Miller
为了理解正在发生的事情、这些计算是如何工作的以及它们意味着什么,首先理解两个基本概念是非常重要的:总体和样本。
- 人群:这是未来可能访问你的网站的潜在海报购买者的全部人群。你想要确定的是,你的改变平均会使退房率提高 10%。**现实是,你永远不会知道这个问题的真正答案。**你得通过更小的样本来估计。
- 样本:该样本代表您测试中的参与者。这些人会在这 16 天里访问你的网站并结账。你的 A/B 测试试图做的是通过对人口样本进行测试来评估整个人口的行为。[实际上,您的测试有 2 个样本集。一个样本集包含所有看到“结帐”按钮的用户,另一个包含所有看到“给我发海报”按钮的用户。]通过让两组中的人除了按钮之外都有相同的体验,你可以确保两个样本中的差异是由变化引起的,而不是由变化引起的(假设测试设置正确)。)
最后,通过观察样本上的结果,使用统计学对整个总体进行推断。
可能的测试结果
这里有 4 种可能的结果(两种是好的结果,两种是你不希望的结果)。
良好的结果:
- 事实上,10%的差异是存在的,你的测试能够检测到它。 恭喜恭喜!
- 实际上,10%的差异并不存在,您的测试也没有检测到任何差异。 哦好吧,至少你知道!
不良结果:
- 事实上,10%的差异是存在的,但是你的测试没有检测到它。 错失良机!
- 事实上 10%的差异是不存在的,但是你的测试认为发现了一个。 哎呀小子!
为什么会这样?
这种差异的存在是因为我们使用样本来估计人口。(这方面的技术术语是抽样误差)例如,如果你从 2019 年 1 月 3 日到 1 月 19 日运行这个测试,那么你就断言这个样本集代表了总体。
这永远不会完全正确,但是统计过程(以及仔细的测试设计和实现)可以确保样本在您想要的程度上具有代表性,因此您可以依赖测试结果。
好的 A/B 测试设计如何防止不期望的结果并增加期望的结果?
你永远无法消除不良影响或保证预期结果,但你能做的是管理它们。
如果你记得使用样本大小计算器,记得你必须指定一些叫做显著性和功效的东西。这些数字(以及基本速率和最小可检测效应)确保您在实验中使用的样本大小将不良效应限制在可管理的范围内。
显著性(或 p 值):这是您指定的一个数字,用于限制误报的大小。也就是说,它确保如果对照品和变体之间不存在 10%的差异(您在测试计算中指定的),那么您将不会错误地检测到它。当您指定 p 值为 0.05 时,这意味着如果您运行此测试 100 次,并且不存在真正的差异,则 100 次中有 95 次您将不会检测到假阳性。这给了你一定程度的信心,相信测试没有误导你。
Power(a alpha):这是您指定的一个数字,当差异确实存在时,您的测试能够在一定程度上检测到它。当您指定 alpha 值为 0.80 时,您是说如果您运行 100 次,并且 100 次中有 80 次存在真正的差异,您将检测到该变化。
当您指定测试的显著性和功效,以及基本比率和估计变化时,它决定了您需要的样本集的大小。(然后根据用户访问测试的比率来决定测试的持续时间)这确保了你不会过早地结束测试。它还确保您不会以大量假阴性结束(即,变体和对照之间存在差异,但您没有检测到它,因此将测试标记为阴性测试。)
或者,如果你有一个预先确定的测试持续时间(如“我们想在 2 周内知道一些事情。”)因此,您的样本大小、功效、显著性和基本速率将决定您的测试在指定功效和显著性的限制内能够确定的最小效果。
如果你在想“让我们只是增加能量和重要性”,记住这是以你需要检测的最小可检测效应的样本集的大小为代价的。
将统计功效从 80%更改为 90%,将显著性水平从 5%更改为 1% →每个变异的测试样本大小翻倍,从 6,347 增加到 12,046。
Changing Statistical Power to 90% from 80%, Changing Significance Level from 5% to 1%
关键点
- 你的 A/B 测试的结果是一个关于真实真实性的陈述。这是对现实的估计。
- 作为一名测试设计者,你需要理解并内化显著性、功效、基础率、最小可检测效应和样本量的含义,并且这些值是相互关联的。
- 作为一个测试设计者,你需要在头脑中有一个最小的可检测的效果(在另一篇文章中有更多关于这个主题的内容),或者在头脑中有一个测试持续时间。
- 计划您的测试的一种方法是从您想要的测试的重要性和功效值开始(根据您的业务环境和测试的实际情况),然后结合您的基本速率和最小可检测效应,以达到样本大小和粗略的测试持续时间(给定速度的估计值)
- 规划测试的另一种方法是从测试的重要性和测试的功效开始(反映业务环境和测试的实际情况),然后结合样本大小(和大致的测试持续时间)来确定测试可以检测到的最小影响大小。(任何较低的影响都无法用您的样本量可靠地检测出来)
- 改变功效(alpha)和显著性(p 值)并不能改变现实。它改变了测试对现实的看法。
- 最后,如果你的公司有一个数据科学家或者统计学家,在测试设计和测试结果解释上与他们紧密合作。不要仅仅假设你用于 A/B 测试的打包软件能给你 100%准确的结果解释。
总结
我做产品经理第一份 A/B 的时候,对这些概念的理解都是很肤浅的层面。结果,我们最终设计并运行了事后看来有缺陷的测试(大多数时候,我们过早地停止了测试)。我们花费了大量的时间和精力来实现和重新实现这些测试,但从中学到的很少(更不用说有很多赢家了),直到我们更好地理解了它们背后的概念。
我发现大多数关于这个主题的教育材料都是由统计学家撰写的。除了他们使用技术术语和解释方式之外,这没有什么不对,在我看来,这也适合其他技术人员。这使得没有统计学背景的产品经理很难理解这些概念并付诸实践。
我写这篇文章的目的是让这个话题通俗易懂,不要做不准确或误导的简化。
如果你发现这篇文章有用、错误、误导或不完整,请留下你的评论。
相关性背后的直觉
Source: Global Shark Attack File & The World Bank
两个变量相关到底意味着什么?我们将在本文中回答这个问题。我们还将对皮尔逊相关系数的方程有一个直观的感受。
当你潜入数据科学的知识海洋时,你发现的第一条鱼是相关性,它的表亲是自相关。除非你花些时间去了解他们,否则在数据科学领域是不可能有所作为的。所以让我们来了解一下他们。
在最一般的意义上,两个变量之间的相关性可以被认为是它们之间的某种关系。即当一个变量的值改变时,另一个变量的值以可预测的方式改变,大多数情况下。
在实践中,相关性一词通常被用来描述变量之间的线性关系(有时是非线性关系)。
我一会儿会谈到线性方面。
同时,这里有一个两个可能相关变量的例子。我们说“可能”,因为这是一个必须被检验和证明的假设。
Relationship between City and Highway fuel economy of passenger vehicles. Source: UC Irvine ML Repository (Image by Author)
让我们起草几个非正式的定义。
线性关系
线性相关:如果两个相关变量的值相对于彼此以恒定速率变化,则称它们彼此具有线性相关。
记住线性相关性,让我们重新看看我们的例子:
Possibly linearly correlated variables. Source: The Automobile Data Set, UC Irvine ML Repository (Image by Author)
如果这种情况下的相关性是线性的,那么线性回归模型(即直线)在拟合到数据后,应该能够充分解释该数据集中的线性信号。以下是该数据集的拟合模型(黑线)外观:
A Linear Regression Model fitted to 80% of the data points in the City versus Highway MPG data set (Image by Author)
在上面的示例中,您现在可以使用拟合模型来预测与城市 MPG 值相对应的公路 MPG 值,该城市 MPG 值是模型没有看到的*,但是在训练数据集*的范围内。
这是拟合线性模型对保留数据集的预测图,该数据集包含模型在拟合过程中未发现的 20%的原始数据。
Actual versus predicted Highway MPG on the 20% hold-out set (Image by Author)
对于有编程倾向的,以下 Python 代码产生了这些结果。
你可以从这里得到例子中使用的数据。如果你在工作中使用这些数据,一定要向加州大学欧文分校 ML 知识库的人们大声疾呼。
非线性关系
现在让我们看看非线性关系。
非线性相关性:如果相关变量的值相对于彼此不以恒定速率变化,则称它们彼此具有非线性关系或非线性相关性。
这是一个看起来像是非线性关联的例子。
Example of a nonlinear relationship (Image by Author)
除非转换因变量(在我们的例子中,它是公路 MPG)以使关系线性,否则线性回归模型将无法充分“解释”这种非线性关系中包含的信息。
正负相关
正相关:对于两个相关的变量,当一个变量的值增加(或减少),然后大部分时间如果也看到另一个变量的值分别增加(或减少),那么这两个变量可以说是正相关。
这里有一个例子,表明这两个变量之间存在正相关关系:
Two variables that appear to be positively correlated. Data source: The Automobile Data Set, UC Irvine ML Repository (Image by Author)
负相关:对于两个相关的变量,当一个变量的值增加(或减少)、然后的时候,大多数时候如果看到另一个变量的值分别减少(或增加),那么这两个变量就称为负相关。
这里有一个表明负相关的例子:
Two variables that appear to be negatively correlated. Data source: The Automobile Data Set, UC Irvine ML Repository (Image by Author)
如何衡量相关性
我们来看下面两个散点图。
(Image by Author)
这两个图似乎表明了各自变量之间的正相关关系。但是在第一个图中相关性更强,因为这些点沿着穿过这些点的假想直线更紧密地聚集在一起。
两个变量之间的相关系数量化了两个变量相对于彼此的运动紧密程度。
皮尔逊系数的公式
具有线性关系的两个变量之间的相关系数公式为:
Formula for the coefficient of correlation between variables X and Y (Image by Author)
分母中的两个 sigmas 是各自变量的标准差。我们将详细分析一下协方差。
同时注意,当使用上述公式计算时,相关系数被称为皮尔逊相关系数。当用于样本时,用符号’ r 表示;当用于整个总体值时,用符号 rho 表示。
如果您想使用此公式的“人口版本”,请确保使用协方差和标准差的“人口公式”。
解释 r 的值
—r(或 rho )的值从 [-1.0 到 1.0] 平滑变化。
—变量负相关时 r=[-1,0) 。
— r=-1 暗示完全负相关。
—正相关时 r=(0,+1】。
— r=+1 暗示完全正相关。
——当 r = [0] 时,变量不是线性相关的。
现在让我们回到理解分子中的协方差项。
对皮尔逊系数公式的直觉
要真正理解皮尔逊公式中发生的事情,首先必须理解协方差。就像相关性一样,两个变量之间的协方差衡量两个变量的值耦合的紧密程度。
当用于测量线性关系的紧密度时,使用以下公式计算协方差:
(Image by Author)
让我们逐项分解这些公式:
如前所述,协方差衡量变量的值相对于彼此变化的同步程度。因为我们想要测量价值的变化,所以这种变化必须相对于一个固定的价值来锚定。该固定值是该变量数据系列的平均值。对于样本协方差,我们使用样本均值,对于总体协方差,我们使用总体均值。使用平均值作为目标也使每个值以平均值为中心。这解释了从分子中各自的平均值中减去 X 和 Y 的原因。
分子中居中值的乘法确保当 X 和 Y 相对于它们各自的平均值都上升或下降时,乘积为正。如果 X 上升,但是 Y 下降到低于各自的平均值,则乘积为负。
分子中的求和确保了如果正值乘积或多或少地抵消了负值乘积,则净和将是一个很小的数字,这意味着在两个变量相对于彼此移动的方式中没有占优势的正或负模式。在这种情况下,协方差值将会很小。另一方面,如果正乘积超过负乘积,那么和将是一个大正数或大负数,表示两个变量之间的净正或净负移动模式。
最后,分母中的 n 或 (n-1) 对可用的自由度进行平均。在样本中,样本平均值用完了一度,所以我们除以 (n-1)。
协方差很奇妙,但是……
协方差是量化变量相对于彼此的运动的一种很好的方法,但是它有一些问题。
**单位不同:**当两个变量的单位不同时,协方差很难解释。例如,如果 X 以美元为单位,而 Y 以英镑为单位,那么 X 和 Y 之间的协方差单位就是美元乘以英镑。人们怎么可能解释这一点呢?即使 X 和 Y 有相同的单位,比如美元,协方差的单位也变成……*美元乘以美元!*还是不太好理解。真扫兴。
**尺度不同:*还有音域的问题。当 X 和 Y 在一个小的区间内变化时,比如说【0,1】*,即使 X 和 Y 移动得很紧,你也会得到一个看似很小的协方差值。
比较困难:由于 X 和 Y 可以有不同的单位和不同的范围,所以往往无法客观地比较一对变量与另一对变量之间的协方差。比方说,我想比较一下与燃油经济性和整备重量之间的关系相比,车辆的燃油经济性和其车辆长度之间的线性关系比强或弱多少。使用协方差来做这个比较将需要比较两个不同单位和两个不同范围的两个值。至少可以说是有问题的。
如果我们能重新调整协方差,使范围标准化,并解决它的“单位”问题就好了。输入“标准偏差”。简单来说,标准差衡量的是数据与其均值的平均偏差。标准差还有一个很好的特性,它和原始变量有相同的单位。让我们用协方差除以两个变量的标准差。这样做将重新调整协方差,使其现在以标准偏差的倍数表示,并且还将从分子中抵消测量单位。协方差的所有问题都在两个简单的除法中解决了!下面是生成的公式:
(Image by Author)
我们以前在哪里见过这个公式?当然是皮尔逊相关系数!
自相关
自相关或自相关是变量与该变量在过去 X 个单位(时间)所取值的相关性。例如,一个地方的气温可能与同一地方 12 个月前的气温自动相关。自相关对于被索引到可以排序的标度(即顺序标度)的变量具有意义。时间刻度是顺序刻度的一个例子。
就像相关性一样,自相关可以是线性的也可以是非线性的,可以是正的也可以是负的,也可以是零。
当用于变量与其自身的 k 滞后版本之间的线性自相关关系时,自相关公式如下:
Formula for k-lagged auto-correlation of Y (Image by Author)
让我们通过观察另一组数据来进一步理解自相关:
Monthly average maximum temperature of Boston, MA from Jan 1998 to Jun 2019. Weather data source: National Centers for Environmental Information (Image by Author)
上图显示了波士顿市的月平均最高温度。它是通过对气象站在该月记录的每日最高温度(从 1998 年 1 月到 2019 年 6 月)进行平均来计算的。
让我们针对各种滞后绘制温度相对于其自身的时间滞后版本。
Monthly average maximum temperature of Boston, MA plotted against a lagged version of itself. Data source: National Centers for Environmental Information (Image by Author)
滞后 12 曲线显示了一个月的平均最高温度和一年前同月的平均最高温度之间的强正线性关系。
在相隔六个月的数据点之间,即在滞后 6 时,也有很强的负自相关。
总的来说,在这些数据中有一个强烈的季节性信号,正如人们可能期望在这类天气数据中发现的那样。
下面是自相关热图,显示了 T 和 T-k 的每个组合之间的相关性。对我们来说,感兴趣的列用蓝色标出。
Correlation heat map (Image by Author)
在第一列中,感兴趣的正方形是(月平均最大值,TMINUS12)处的正方形,也可能是(月平均最大值,TMINUS6)处的正方形。现在,如果你回头参考散点图,你会注意到所有其他滞后组合的关系是非线性的。因此,在我们将尝试为该数据构建的任何线性季节模型中,为这些非线性关系(即热图中的剩余方块)生成的相关系数值的效用受到严重限制,即使其中一些值较大,也不应使用它们*。*
请记住,当使用前面提到的公式计算(自动)相关系数时,只有当关系是线性的时才有用。如果关系是非线性的,我们需要不同的方法来量化非线性关系的强度。例如,斯皮尔曼等级相关系数可用于量化具有非线性、单调关系的变量之间的关系强度。
以下是绘制温度时间序列、散点图和热图的 Python 代码:
Python code for plotting the temperature series, auto-correlation scatter plots and correlation heat map
这是数据集。
提醒一句
最后,提醒一句。两个变量 X 和 Y 之间的相关性,无论是线性还是非线性,都不会自动暗示 X 和 Y 之间的因果关系(反之亦然)。即使在 X 和 Y 之间有很大的相关性,但 X 可能不会直接影响 Y ,反之亦然。也许有一个隐藏的变量,称为混杂变量,它同时影响着 X 和 Y ,因此它们的上升和下降彼此同步。为便于说明,请考虑下图,该图显示了两个数据集的相互关系。
Correlation plot of total labor force with access to electricity (Data source: World Bank) (Image by Author)
这里的 X 是一个时间序列,范围从 1990 年到 2016 年,包含了这几年中每年用上电的世界人口比例。变量 Y 也是一个时间序列,范围从 1990 年到 2016 年,包含这些年中每一年的全球劳动力的强度。
这两个数据集显然高度相关。有没有因果,你来判断吧!
我撰写数据科学方面的主题,特别关注时间序列分析和预测。
如果你喜欢这篇文章,请关注我的Sachin Date获取关于时间序列分析、建模和预测主题的提示、操作方法和编程建议。
打破面部识别:维奥拉-琼斯算法
直到最近,我们的智能手机才能够使用人脸作为密码来解锁设备。就像指纹一样,面孔是独一无二的,有数百万个微小的特征来区分彼此。对于我们人类来说,这可能并不总是显而易见的,但机器会综合并评估每一个小数据,从而导致更客观的准确性。
像其他基于数据的模型一样,面部检测也不是 100%完美的。虽然,它已经到了我们日常生活中商业上可以接受的阶段。面部检测嵌入在我们的设备中,可以用于许多方面,从简单地解锁手机到汇款和访问个人数据。
维奥拉-琼斯算法
由 Paul Viola 和 Michael Jones 于 2001 年开发的 Viola-Jones 算法是一种对象识别框架,允许实时检测图像特征。
尽管是一个过时的框架,Viola-Jones 还是非常强大的,它的应用已经被证明在实时人脸检测方面非常显著。
1.它是如何工作的
Viola-Jones 算法有两个阶段:
- 培养
- 侦查
训练先于检测,但是为了便于解释,我将首先讨论检测。
2.侦查
Viola-Jones 是为正面人脸设计的,因此它能够最好地检测正面,而不是侧面、向上或向下看的人脸。在检测人脸之前,图像被转换成灰度,因为它更容易处理,需要处理的数据更少。Viola-Jones 算法首先在灰度图像上检测人脸,然后在彩色图像上找到位置。
Viola-Jones 勾勒出一个方框(如您在右侧所见),并在方框内搜索人脸。它本质上是在寻找这些 haar-like 特征,这将在后面解释。在遍历完图片中的每一个方块后,方块向右移动一步。在这种情况下,我使用了一个大的框大小和大步长进行演示,但一般来说,您可以根据需要更改框大小和步长。
通过更小的步骤,许多盒子检测类似人脸的特征(Haar-like features ),所有这些盒子的数据放在一起,帮助算法确定人脸的位置。
3.类哈尔特征
haar-like 特征以 19 世纪匈牙利数学家 Alfred Haar 命名,他发展了 Haar 小波的概念(有点像 Haar-like 特征的祖先)。下面的特征显示了一个有亮侧和暗侧的盒子,这是机器如何确定特征是什么的。有时一边会比另一边亮,比如眉毛的边缘。有时中间部分可能比周围的盒子更亮,这可以解释为鼻子。
维奥拉和琼斯在他们的研究中发现了三种类似哈尔的特征:
- 边缘特征
- 线特征
- 四边特征
这些特征帮助机器理解图像是什么。想象一下一张桌子的边缘在黑白图像上会是什么样子。一边会比另一边亮,创造出你在上面的图片中看到的黑白边缘。
在人脸检测的两个重要特征中,水平和垂直特征分别描述了眉毛和鼻子在机器看来的样子。此外,当检查图像时,每个特征都有自己的值。计算起来相当简单:从黑色区域减去白色区域。比如看下图。
想象一下我们的 haar-like 特征被转换成一个网格。每个方块代表一个像素。为了演示,我选择了一个 4 x 8 的网格,但实际上,对于某个特性来说,会有更多的像素和更大的网格。方框中的数字代表特征的暗度。它越高,像素越暗。因此,你可以看到右边的数字比左边的高。现在,如果您将左侧(白色)两列的数字相加,然后从右侧两列的总和中减去,您将获得特定特性的值。
所以在这种情况下,我们这个特征的值是→
(0.5+0.4+0.5+0.6+0.4+0.7+0.5+0.4+
0.4+0.5+0.6+0.8+0.5+0.7+0.6)-
(0.1+0.1+0.2+0。2+0.2+0.1+0.2+0.2+
0.2+0.3+0.2+0.1+0.2+0.3+0.2+0.2)
B-W = 8.7-3
= 5.7
4.整体图像
在上一节中,我们计算了一个特性的值。实际上,这些计算可能非常密集,因为在一个大的特征中像素的数量会大得多。
积分图像让我们能够快速执行这些密集计算,从而了解多个特征中的一个特征是否符合标准。
Regular Image
现在想象一下,用红色突出显示的是我们针对某个特性的网格,我们正在尝试计算该特性的值。通常我们只是把盒子加起来,但是因为这可能是计算密集型的,我们将创建一个完整的图像。
为了计算积分图像中单个盒子的值,我们取其左边所有盒子的总和。下图显示了一个示例:
积分图像中的绿框计算为常规图像中高亮区域的总和。如果我们对每个盒子都这样做,我们将有一个序列通过网格,它可能看起来像下面的图像。
让我们看看我之前选择的例子的价值:
我们所要做的就是查看我们特征的四个角,加上紫色,减去绿色。
→168–114+79–110 = 23
那么我们为什么要使用积分图像呢?
因为 Haar-like 特征实际上是矩形的,并且积分图像过程允许我们非常容易地找到图像中的特征,因为我们已经知道特定正方形的和值,并且为了找到常规图像中两个矩形之间的差异,我们只需要减去积分图像中的两个正方形。因此,即使您的网格中有 1000 x 1000 个像素,积分图像方法也会使计算变得不那么密集,并且可以为任何面部检测模型节省大量时间。
5.训练分类器
现在我们已经讨论了检测,让我们继续培训。那么,我们所说的培训到底是什么意思呢?我们正在训练机器识别这些特征。我们给它输入信息,然后训练它从信息中学习预测。因此,最终,该算法将设置一个最小阈值来确定某个事物是否可以被归类为一个特征。
该算法将图像缩小到 24 x 24,并在图像中寻找经过训练的特征。它需要大量的面部图像数据,以便能够看到不同和不同形式的特征。这就是为什么我们需要向算法提供大量面部图像数据,以便对其进行训练。Viola 和 Jones 向他们的算法输入了 4960 张图像(每张都是手动标记的)。对于某些图像,您可以输入特定图像的镜像,这对计算机来说是全新的信息。
您还需要提供算法非面部图像,以便它可以区分这两个类。Viola 和 Jones 为他们的算法提供了 9544 张非面部图像。在这些图像中,一些图像可能看起来类似于人脸的特征,但该算法将理解哪些特征更可能在人脸上,哪些特征显然不会在人脸上。
6.自适应增强(AdaBoost)
该算法从我们提供的图像中学习,能够确定数据中的假阳性和真阴性,使其更加准确。一旦我们考虑了所有可能的位置和这些特征的组合,我们将得到一个高度精确的模型。训练可能会非常广泛,因为你必须检查每一帧或每一幅图像的所有不同的可能性和组合。
假设我们有一个用于确定成功率的特征方程(如图所示),其中 f1、f2 和 f3 为特征,a1、a2、a3 为特征各自的权重。每个特征被称为**弱分类器。方程 F(x)的左边称为强分类器。由于一个弱分类器可能不太好,当我们有两个或三个弱分类器的组合时,我们得到一个强分类器。随着你不断添加,它变得越来越强大。这被称为合奏。**你想确保你前面有最重要的特征,但问题是你如何找到最重要或“最好”的特征?这就是适应性增强发挥作用的地方。
例如,假设你有 10 张图片:5 张人脸图片和 5 张非人脸图片。
所以你找到一个重要的特征,根据你的最佳特征,并用它来做一个预测。
该模型给出了 5 个真阳性中的 3 个和 5 个真阴性中的 2 个。
它对这些图像的预测是正确的,但也有一些错误:3 个假阳性和 2 个假阴性。所以它没有发现这两张图片上的特征,它们实际上是脸。然而它在 3 张非面部图像中发现了该特征。
在下一步中,自适应增强使用另一个功能,这是对我们当前最强功能的最佳补充。因此,它不会寻找第二好的特性,而是一个补充当前最佳特性的特性。因此,它增加了被误认为假阴性的图像的重要性,并找到适合这些图像的下一个最佳特征,在某种程度上,增加了这些图像在整个算法中的权重。因此,随着新功能的增加,我们会在最后选择一个权重较高的图像。一旦算法得到优化,能够正确计算所有的积极和消极,我们就进入下一步:级联。
7.级联
层叠是另一种提高我们模型的速度和准确性的“技巧”。因此,我们首先选择一个子窗口,在这个子窗口中,我们选择最重要或最好的特征,看看它是否出现在子窗口中的图像中。如果它不在子窗口中,那么我们甚至不看子窗口,我们只是丢弃它。然后,如果它存在,我们查看子窗口中的第二个特征。如果它不存在,那么我们拒绝这个子窗口。我们继续寻找具有的特征的数量,并拒绝没有该特征的子窗口。评估可能需要几秒钟的时间,但是因为你必须对每个特性都进行评估,所以可能会花很多时间。级联大大加快了这一过程,并且机器能够更快地交付结果。
8.结论
所以只是为了补充一些关于 Viola-Jones 算法的结束语:
-该算法是由 Paul Viola 和 Michael Jones 在 2001 年开发的,是同类算法中的第一个,主要用于面部检测应用。算法有两个步骤:面部和非面部图像的训练,然后是实际的检测。
-我们有 2 个训练步骤:训练分类器和 Adaboost
-我们有 2 个检测步骤:检测 haar-like 特征和创建积分图像
- Viola-Jones 是当时最强大的算法之一,即使今天有更好的模型,Viola-Jones 也为它在面部检测领域奠定了基础。
9.来源
- 使用简单特征的增强级联的快速目标检测:琼斯和维奥拉;
https://www . cs . CMU . edu/~ efros/courses/lbmv 07/Papers/viola-cvpr-01 . pdf - 目标探测的一般框架;CP Papageorgiou 等人https://www . research gate . net/publication/3766402 _ General _ framework _ for _ object _ detection
- 增强图像检索;viola & Tieuhttp://www . ee . Columbia . edu/~ SF Chang/course/SPR/papers/boosting-image-retrieval . pdf
图像/gif:
- LCR 拍摄的 https://dribbble.com/shots/3903645-Face-recognition Gif 图片
- https://static . packt-cdn . com/products/9781789343731/graphics/652408 EB-95ce-4a 92-9975-0732 a 903284 b . png(类哈尔特征)
泊松分布公式的直觉
这是二项式的极限。但这是为什么呢?
在我之前的文章中,我讨论了关于泊松过程的几个话题。我没有提到的一个话题是,为什么泊松分布的 P 概率 M ass F 函数看起来像这样的直觉:
Probability Mass Function for the Poisson Distribution (Image by Author)
换句话说,如果单位时间内发生 λ 事件,为什么上面的公式会得出k事件在时间tt发生的概率?****
关于泊松过程的各种文本解释了泊松分布是二项式分布的极限情况,即当 n → **∞,**时,二项式分布的 PMF 变成了泊松分布的 PMF。至少数学是这样的。
但是 PMF 不仅仅是数学。它可以用来模拟我们生活中发生的真实事件。如果我是一个餐馆老板,我可以使用泊松 PMF 来确定安装多少摊位和雇佣多少厨师和服务员。我可以使用泊松 PMF 为我的业务做容量规划。
那么在真实现象的背景下,如果 k 事件发生在t 的概率是我们想要知道的,为什么泊松 PMF,它结构化的方式,能够如此好地回答这个问题呢?
这是我们将在本文中回答的问题。
一个非常简单的实验
让我们从一个简单的场景开始。假设你是苹果检查员。你的工作是拿起一个苹果,目测,闻一闻,然后决定是把它标为好的还是坏的。你很擅长你的工作,你一小时能测试 60 个苹果,也就是一分钟一个苹果。假设你的每次检查持续一个小时。我不知道你怎么样,但是我的鼻子在嗅了一个小时后需要休息一下。
通过许多这样的会议,你已经发现,对于任何给定的苹果,它是坏苹果的几率是的 5%,而它是好苹果的几率相应地是 的 95% 。苹果的检验是一个简单的伯努利试验。**
一般而言,遇到坏苹果的概率为 p 的伯努利试验将为相应的伯努利分布产生以下 P 概率MaF函数:
PMF for the Bernoulli distribution (Image by Author)
从伯努利到二项式
有一天,当你去上班时,你的主管要求你提交数据,说明在你一个小时的苹果测试会议中,发现 1、2、3、…、k 个坏苹果的几率。
这样的请求可能会让苹果检查员困惑,但不会让你困惑,因为你只是一个白天的苹果检查员。到了晚上,你就是数学忍者了。所以,你毫不犹豫地用下面的二项分布将结果交给你的主管:
PMF of a Binomial distribution (Image by Author)
在我们的例子中, p=0.05,n=60。
在时域中重新解释二项分布
随着你在苹果测试生涯中的进步,你推断出另一个事实。平均来说,在你长达一小时的苹果测试过程中,你似乎遇到了 λ 个坏苹果。
由于假设这些 λ 坏苹果在 60 分钟的测试中均匀分布,在该小时的任何特定分钟遇到一个坏苹果的概率是 λ /60。这是理解泊松分布公式的一个简单但关键的见解,所以在继续之前让我们记住它。
我们现在能够在时间的背景下解释二项分布 PMF,而不是根据伯努利试验的次数
用( λ/60 )代替伯努利分布的 PMF 中的概率 p ,我们可以说在 60 分钟的测试中遇到 k 坏苹果的概率如下:
Probability of running into k bad apples in 1 hour of testing (Image by Author)
久而久之,你检验苹果的效率变得很高,每小时可以检验 360 个苹果,也就是每 10 秒钟检验一个苹果。由于 λ 是 3600 秒内遇到的坏苹果的平均数,而这 3600 秒内有 360 个十秒长的间隔,所以在任何一个十秒间隔内遇到一个坏苹果的概率为( λ/360 )。
不出所料,考虑到小得多的时隙,这是一个很小的概率。
通过将检验次数 n 设置为*【360】,*** ,p =(λ/360),我们可以将二项分布改写如下:**
(Image by Author)
让我们更上一层楼。如果(但愿不会如此)你被一个每秒能检查 1 个苹果,也就是一小时能检查 3600 个苹果的机器人检查员取代,那会怎么样?在组成一个小时的 3600 秒中的任何一秒中找到一个坏苹果的概率变得更加罕见。因此,检查次数 n = 3600, 和p**=**(λ/3600)二项分布的 PMF 可以写成:
(Image by Author)
一路走来
让我们结束这一思路。机器人检查员 2.0 版已经发布。2.0 版本可以连续检测苹果,即无论它们以什么速度从传送带上下来,检测人员都不会错过任何一个苹果。因此对于 2.0 版本,一小时内的检查次数 n 趋于无穷大,二项分布最终趋于泊松分布:
(Image by Author)
求解极限以显示二项分布如何收敛于泊松 PMF 公式涉及一组简单的数学步骤,我不会用这些步骤来烦你。
对我们来说,重要的是回忆我们是如何在二项分布的思路中走到这一步的。
以下一组图表显示了随着(每小时检测次数)从 60 增加到∞,二项分布的 PMF 如何向泊松分布的 PMF“滑动”。我们保持 λ 固定在每小时 3。**
(Image by Author)
下表包含上图中 k 的前 15 个值的概率值。最后一列是通过简单地使用泊松分布的 PMF 公式生成的,其中 λ 设置为 3 ,即泊松(3) 。**
P(k bad apples in 1 hour) as we vary number of inspections per hour n from 60 to infinity. (λ=3) (Image by Author)
*请注意最后两列(红框)中的值是多么相似。还要注意对于任何给定的 k (绿框)值,得到那个值 k 的概率逐渐趋向于最后一列中的*泊松(3)’s PMF 值。
到目前为止,我们所有的推理都基于一个持续 1 小时的检查会议。但是我们所有的推理对于 1 毫秒、1 分钟、1 天都是一样的,实际上对于任何单位时间间隔都是一样的。
按时间缩放一切 t …
**作为最后一步,我们将放宽这个单位时间限制,假设我们的机器人检查员将在持续 t 小时的会话期间检查苹果。这种概括不会打断我们的推理。我们将简单地将每小时观察到的坏苹果的平均数量 **λ、乘以 t ,这样( λ t) 就是在小时观察到的坏苹果的平均数量。
实际上,我们正在缩放我们的观察窗口,我们正在通过 t 缩放所有相关的公式。
因此,如果预计在时间 t 期间看到一个平均的 λt 坏苹果,并且苹果检查员可以及时检查n 个苹果 t ,在这些检查中的任何一个检查中看到坏苹果的概率是(即概率p*=(λt/n)。 因此,及时观察到 k 坏苹果 t 的概率由二项分布给出:*****
Probability of running into kbad apples in t hours of testing (Image by Author)
再次进入,超高效的机器人检查员可以连续取样苹果。随着 n、 时间内的检查次数趋于无穷大,二项分布变成泊松分布:**
(Image by Author)
这当然是我们想要深入了解的公式!
并学习如何使用 Python 来模拟它
towardsdatascience.com](/the-poisson-process-everything-you-need-to-know-322aa0ab9e9a) [## 泊松回归模型图解指南
和使用 Python 的泊松回归教程
towardsdatascience.com](/an-illustrated-guide-to-the-poisson-regression-model-50cccba15958)
感谢阅读!如果您喜欢这篇文章,请关注我的Sachin Date以获得关于回归、时间序列分析和预测主题的提示、操作方法和编程建议。*****
从我们自己的数据中产生价值的问题
我们的数据通常是有价值的,因为它是更大的大数据块的一部分。如果我们想将自己的数据货币化,就必须有所改变。
Photo by Patrick Lindenberg on Unsplash
有很多关于亚马逊、脸书和其他公司从我们的数据中获利的讨论,也有很多关于改变数据存储方式的讨论,这样我们就可以完全控制,甚至可以从我们自己的数据中获利。这些想法听起来不错,但将它们付诸实践将比一些人似乎意识到的要困难得多。
考虑购物习惯。你的购物习惯并不那么有价值。没人在乎你已经买了什么或者不买什么。企业想知道的是,如果让你选择,你会买什么样的*。为了知道你会买什么,企业必须了解消费者的行为,而不仅仅是你的行为。这就是大数据的用武之地。*
基本推荐系统
Photo by Victoriano Izquierdo on Unsplash
通过汇总和分析成百上千甚至上百万人的购物习惯,像亚马逊这样的公司可以预测你会买什么,他们可以为你量身定制购物体验和广告体验。
通常这种聚合和分析是通过某种机器学习来完成的。寻求基于现有选择对个人偏好进行评级的算法被称为推荐系统(RS)。推荐系统使用了许多方法。
RS 中使用的一种方法是协同过滤。在这种方法中,单个用户的口味与其他具有相似口味的用户进行匹配,然后如果匹配者喜欢某样东西,则假设该用户也会喜欢。这些信息通常存储在用户-条目矩阵中,这并不是最有效的选择,因为矩阵可能非常大,并且有很多空隙。但这是一个基本的方法。
协同过滤中的用户控制是可能的。由于每个用户的数据在 user-item 矩阵中都有一行,所以这些行可以是分散的,或者至少我们能够根据用户的请求删除给定的行。我们还会知道某一行何时被利用,因此有可能给出某种支付。但是这种方法效率低得离谱。
神经网络和黑盒
一种更复杂但更有效的方法是使用神经网络。该网络接受消费者行为模式的训练,并根据给定用户的行为学习推荐产品。这些系统可能非常强大,但它们以一种无法解构的方式聚合用户数据。分离或控制单个用户的数据是不可能的。
所以在这种情况下,第一个问题是,你如何控制信息?这就像教某人一条关于你的信息,然后告诉他们忘记你刚刚告诉他们的一切!神经网络只是不能以一种我们可以删除我们教给它们的数据的方式运行。还需要有一个附加层来指示给定用户的数据何时被利用,以便他们可以接收支出,但在某些方面,该选项减少了聚合数据的匿名性。
问题是神经网络通常是黑箱。在计算机科学中,黑盒就是任何一个接受输入并产生输出的设备,我们不知道它的功能是什么。黑匣子工作了,但我们并不真正理解为什么。这些设备在理论讨论中使用了很多,但神经网络实际上是黑匣子。我们可以输入数据,并得到一个结果,但我们并不真正理解为什么会出现这个结果。
David Weinberger 关于机器学习的文章相当详细地讨论了这个话题。实际上,我有点担心某些人越来越习惯于让机器学习试图为他们建立现实模型,但分析很重要,因为它不仅说明了机器学习对于预测未来事件变得多么有用和重要,而且还说明了对这些模型进行逆向工程有多么困难。
给了我们下一步进化的工具
onezero.medium.com](https://onezero.medium.com/machine-learning-might-render-the-human-quest-for-knowledge-pointless-5425f8b00a45)
有人甚至认为机器学习是如此强大,解开这些黑盒是如此复杂,以至于它们代表了从理解到简单地使用这些训练好的模型进行预测的范式转变。如果这项技术具有如此大的破坏性,以至于威胁到科学研究的方式,我们如何控制它的内部运作并打破它们?
有一些方法可以获得对网络所学内容的一点点洞察力,解开这些信息可以在未来给我们提供进一步的洞察力,但我们不能只是将神经网络完全解开,恢复到它的训练信息中,并提取决策的给定原因,或删除训练数据的给定子集,除非我们存储所有数据并不断复制整个神经网络,这不仅效率低下,还会产生更多的隐私问题。
以这种方式展开的神经网络提供了更大的攻击风险。“白盒”神经网络可以通过提供关于训练信息的太多信息的方式进行探测(对黑盒神经网络进行反向工程)。但是,即使这些方法也是探测模型以获取信息的简单形式。它仍然不允许我们真正理解模型中的决策是如何得出的。
法律问题
GDPR 和其他寻求让个人完全控制其数据的法律协议与机器学习相冲突,尤其是这种机器学习。个人对其数据的法律保护越多,非法神经网络就变得越多,这仅仅是因为它们的黑箱性质。GDPR 本身已经为机器学习打开了许多问题,因为它无法对决策结果给出详细的评论。
这个问题在 中解决了 GDPR 会让机器学习非法吗? 虽然这篇文章已经有一年的历史了,但是我感觉这些问题的很多答案仍然不存在。GDPR 在许多方面仍然比某些人想要的要弱得多。
ReadWrite 暗示区块链是给我们普遍基本收入的一种方式,我同意,但不同意这种方式。在他们的文章中, 是普遍基本收入 的缺失成分,作者建议法律提供“对产生数据的个人的完整和不容置疑的数据所有权……”这样的做法将使当前基于神经网络的解决方案完全非法。我不认为区块链是解决这个问题的可行方案。
最后一点
另一个问题,正如我在之前的讨论中提到的,这些企业之所以对我们的数据如此感兴趣,是因为他们被期望免费为我们提供一切。我们要为脸书提供的服务付费吗?不。我知道,因为我是脸书的广告客户。我是他们的消费者之一。因此,脸书等人必须尽他们所能让消费者满意,并为他们的利益服务。这不仅是道德问题,也是法律问题。*
进一步阅读
为什么心理学、人类学和认知神经科学的理论需要应用于发展…
towardsdatascience.com](/the-psychology-of-artificial-intelligence-cb03d060288c)*
下兔子洞的快乐,第二部分
上一次,我们讨论了如何仅利用公平掷硬币固有的随机性来产生一个概率为 1/3 的事件。我们甚至从数学上证明了这一点。现在,我们深入兔子洞。我们现在表明,给定可以表示为分子/分母的任何概率,其中分子和分母都是整数(整数),我们可以使用公平抛硬币来定义一个事件,其“成功”概率为 n/d 。
我们通过本质上扩展我们对特定情况所做的工作来做到这一点,其中 n = 1 和 d = 3。回想一下,我们只是简单地抛硬币两次,这样我们就有了四种同样可能的结果。然后我们制定了一个规则,如果一个特定的结果出现,我们只需再扔两次,重复直到我们结束。那么,我们能为更大的分母做些什么呢?多掷硬币!假设 n = 7 且 d = 2 0。然后我们想要一组 20 个独特的掷硬币结果序列,其中 7 个被(任意地)定义为成功,其余 13 个被定义为“失败”。我们可以像以前一样,通过生成一个附加序列,简单地丢弃剩余的序列。我们应该掷多少次硬币?一次投掷产生 2 个结果,两次投掷 4,三次投掷 8,四次投掷 16,五次投掷 32。所以,抛五次硬币就足够了。现在,我们可以写出五次投掷硬币的所有 32 种可能的序列,将“成功”分配给前七次,“失败”分配给接下来的十三次,将剩下的十二次作为“再掷五次”扔掉。
[source: me]
现在我们可以用之前用过的方法证明它确实有效。
[source: me]
再次,重新排列
[source: me]
数学再次变得非常方便。现在我们来概括一下。如果我们有一个形式为 n/d 的概率,我们还必须将 k 定义为可能的最小整数,使得 2 的 k 的幂至少与 d 一样大。这不是最直观的定义,所以我们来讨论一下。这里是前几个 2 的幂。
[source: me]
使用该表的方法是向下遍历右手列,直到找到一个大于或等于 d 的值,调用这个数字 k ,然后查找 2 被提升到的相应幂*,代表投掷次数。一般的解决方案包括投掷一枚公平硬币 k 次,并查看所有可能的序列。任意取其中任意 n ,定义为“成功”。*取其中的下一个 d-n ,定义为“故障”。剩下的 k-d 就干脆忽略了,硬币的投掷次数和之前一样。然后根据需要重复多次,直到我们终止。
进程是否保证终止?是的。我们选择 k 使得它严格小于两倍 d 。因此,而非在一次迭代后终止的概率严格小于 1/2 。所以不在 m 轮这么多次投掷后终止的概率严格来说小于 1/2 的 m 次方,很快趋于零。
现在,数学:
[source: me]
我们现在有一个程序,我们已经从数学上证明了它有期望的成功概率,并且保证终止。
在第 3 部分中,我们将用 Python 对此进行编码,敬请关注。
朱莉娅阶乘问题
大约一周前,我在从车床软件包中提炼统计模块时,无意中发现了 Julia 语言中一个我认为非常有趣和可笑的*【事情】*。Julia 是一种编程语言,以其在眨眼之间解决复杂的机器学习问题的统计能力和有效性而闻名。考虑到朱莉娅的名声,这让我遇到的问题变得更加有趣。
二项分布
二项式分布是一种很好的分布,可以用来确定布尔值的有效性。这种分布在数学上是无数算法的核心,可以结合推理和贝叶斯统计,使其具有难以置信的通用性。不仅如此,二项式分布可以应用于更进一步的学习,如准确性验证以及分类模型。
因此,出于显而易见的原因,人们会希望二项分布在他们的武器库中用于各种机器学习和统计任务。在这个特殊的例子中,我正在准备一个征兆测试。符号测试是一个很酷的小推理测试,涉及到两个数组的点减来检测统计显著性。虽然我不能说这是统计学家最强大的推理测试,但我绝对喜欢它的陪伴。
所以在我的符号测试中,我写了一个类似这样的小函数:
function sign(var1,var2)
sets = var1 .- var2
positives = []
negatives = []
zeros = []
for i in sets
if i == 0
zeros.append!(i)
elseif i > 0
append!(positives,i)
elseif i < 0
append!(negatives,i)
end
end
totalpos = length(positives)
totallen = length(var1) + length(var2)
ans = binomialdist(totalpos,totallen)
return(ans)
end
不用担心,我会一如既往地分解函数。第一行是我上面讨论的;两个数组的点减法。在此之下,观察到三个空数组。在我看来,数组附加是从迭代循环中获得私有 dim 的最好方法之一,所以我的程序经常会在 for 循环上看到空数组。
至于带条件的 for 循环,当然整个测试都围绕着一个数字是正还是负。记住这一点,我们可以根据与零相关的值将数组附加在一起,下面是负数,上面是正数。我想提到的另一件事是,在大多数情况下,否定列表是不必要的,我可能会在某个时候完全放弃它,因为零和正是符号测试中唯一需要的计算。
这个函数的问题在底部,在这里我们遇到了二项式分布函数。当然,这个功能来自我的大脑,所以我们来看看这个。
二项式分布用上面的公式计算,X 的概率等于样本大小的阶乘除以阳性的阶乘…等等。
在最初编写函数来执行这个数学运算时,我回想起我编写平方根函数的时候。由于车床的意图是保持低进口,我认为它只是去重新发明轮子,以避免需要许多包。这被证明是一个错误,因为我在编写函数后意识到,Julia 在基础语言中加入了他们自己的平方根函数。没有从 math 导入 sqrt,没有使用 math: sqrt,只有 sqrt()。我被带了回来,事实上得知这样的事情被烤成了朱莉娅,我感到非常兴奋。
所以这一次,我没有痛苦地编写自己的函数来做简单的数学运算,如阶乘或平方根,而是决定首先检查 Julia 是否有这样做的基函数。果然,
确实如此。
阶乘表???
因此,有了这个阶乘函数的新知识,我开始写我的二项分布函数,最后结束了这个符号测试。
*function binomialprob(positives,size)
# p = n! / x!(n-x!)*π^x*(1-π)^N-x
n = size
x = positives
factn = factorial(n)
factx = factorial(x)
p = factn / factx * (n-factx) * π ^ x * (1-π)^n - x
return(p)
end*
当然,从符号测试中获得 P 值并不需要分布,所以实际的分布测试被排除在函数之外,只返回推断概率。嗯,这个作品。我像往常一样测试了我的函数,定义了一些我可以在几秒钟内输入的小数组:
*array1 = [1,4,3,5,4]
array2 = [5,6,8,4,5]*
然后将它们放入函数中:
*p = sign(array1,array2)*
回报为:
*P - 2.082207943758608e11*
那么问题出在哪里?
几个月后,我不情愿地再次去使用 signs test,部分原因只是为了让它摆脱压抑和孤独的存在,令我惊讶的是,这个函数现在有了一个重大问题。
人口(n)的长度太大,Julia 的阶乘函数无法处理。这是因为 Julia 的阶乘函数实际上是使用阶乘函数的阶乘表。
什么?!
这相当于做一个函数,给一个数加 1,而不是只给这个数加 1,我们在一个数的列表中查找这个数和 1 的和是多少,然后返回!那么阶乘函数的上限在哪里呢?解决这个问题有多容易呢?
经过一些快速测试后,阶乘函数只在 1-20 的范围内有效。当然,毫无疑问,你可以用 1-20 的阶乘范围做很多事情,但是对于统计学来说,考虑到 Julia 是一种统计语言,这几乎是没有用的,因为测试人口规模在 20 以内是无效的。这在应用于像符号测试这样的事情时尤其成问题,因为每个数字都对返回的 P 值有直接影响。
计算
有了这些新发现之后,我决定更深入地研究一下 Scipy 是如何解决这个问题的,但我仍然不愿意用自己的函数来解决这个问题,因为你越想解决这个问题,它就变得越复杂。
Scipy 的阶乘函数包含在 scipy.special 中,并且需要尽可能多的挖掘才能找到…
考虑到这一点,我很快决定跳进一个谷歌 Colab 笔记本,看看 Scipy 的功能是否像 Julia 的一样受到限制。
没有。
结论
我知道开发一门语言需要做很多事情,在这个丰富的过程中,很多事情都被忽略了。我敢肯定,阶乘在朱莉娅的眼里只是一个小问题,因为它们在统计学中的作用还不足以证明自己花了大量时间用朱莉娅语言编写阶乘,而 没关系 。
然而,考虑到 factorial 实际上是 base Julia 中的一个函数,而这个函数恰好完成了一半,所以任何想要使用 factorial()这个名字的适当的 factorial 数学函数都做不到。当然,如果常规函数没有问题,这就不是问题。
我怎么称赞朱莉娅都不为过,因为我认为她是如此美丽和清新。事实上,Julia 是我最喜欢的编程语言,比起其他语言,我更喜欢使用它。同样,我认为反馈对于像 Julia 这样的语言的发展是绝对重要的。这是我对朱莉娅的各种批评之一。要明确的是:我爱茱莉亚爱得要死,我也可以对任何语言做出同样的评论。
树袋熊、熊猫、擎天柱和火花的丛林
从 Databricks(考拉)、Optimus framework 和 Apache Spark 3.x 的最新库可以期待什么
如果你和我一样对数据科学感到兴奋,你大概知道 Spark+AI 最新峰会昨天(2019 年 4 月 24 日)开始了。而且有很棒的事情可以聊。但我会用衍生产品来做。
如果你一直在关注我,你会发现我参与创建了一个叫做 Optimus 的框架。如果您想了解更多信息,请查看以下文章:
用 Python、Spark 和 Optimus 分解数据科学。
towardsdatascience.com](/data-science-with-optimus-part-1-intro-1f3e2392b02a) [## 擎天柱的数据科学。第 2 部分:设置您的数据操作环境。
用 Python、Spark 和 Optimus 分解数据科学。今天:数据科学的数据操作。…*第 1 部分在此…
towardsdatascience.com](/data-science-with-optimus-part-2-setting-your-dataops-environment-248b0bd3bce3)
我在这里用 Optimus 和其他开源工具解释了整个数据科学环境。这可能是第 3 部分,但我更感兴趣的是向您展示其他内容。
擎天柱蜜蜂
https://github.com/ironmussa/Optimus
起初,Optimus 是作为一个清理大数据的项目开始的,但突然我们意识到这个框架有更多的机会。现在,我们已经创造了许多有趣的东西,如果你是一个使用熊猫或 spark 的数据科学家,你应该去看看。
现在我们有这些 API:
- Spark 数据帧的改进版本(更适合清理和管理数据)。
- 使用 Spark 简化机器学习。
- 使用 Spark 和 Spark 深度学习更轻松地进行深度学习。
- 直接从火花数据帧绘图。
- 描绘火花数据帧。
- 数据库连接(像亚马逊红移)更容易。
- 丰富与外部 API 连接的数据。
还有更多。你甚至可以直接从网上读取数据到 Spark。
因此,您可以看到,我们一直在努力改善数据科学家的世界。我们关心的事情之一是创建一个简单和可用的 API,我们并不喜欢 Pandas API 或 Spark API 本身,但它们的组合加上一点点令人惊叹的东西,创建了你今天可以称为我们的框架。
考拉 vs 擎天柱 vs 火花 vs 熊猫
今天,Databricks 宣布,通过增强 Apache Spark 的 Python DataFrame API 以兼容熊猫,Koala 项目是与大数据交互时更有效的方式。
如果你想试试这个 MatrixDS 项目:
[## MatrixDS |数据项目工作台
MatrixDS 是一个构建、共享和管理任何规模的数据项目的地方。
community.platform.matrixds.com](https://community.platform.matrixds.com/community/project/5cc0de62b8f4a97f2912aabf)
还有这个 GitHub 回购:
[## FavioVazquez/考拉 _ 擎天柱 _ 火花
与火花和朋友一起震撼世界。。为 FavioVazquez/koalas _ Optimus _ spark 开发做出贡献,创建一个…
github.com](https://github.com/FavioVazquez/koalas_optimus_spark)
因此,我没有用复制粘贴树袋熊的文档来烦你,而是创建了一个树袋熊、Optimus 和 Spark 之间联系的简单示例。
你需要安装擎天柱
pip install --user optimuspyspark
还有考拉
pip install --user koalas
我将使用这个数据集进行测试:
https://raw . githubusercontent . com/databricks/考拉/master/data/sample _ stocks . CSV
让我们首先用 Spark vanilla 读取数据:
from pyspark.sql import SparkSession
spark = SparkSession.builder.getOrCreate()df = spark.read.csv("sample_stocks.csv", header=True)
为此,我之前需要上传数据集。让我们看看擎天柱:
from optimus import Optimus
op = Optimus()
df = op.load.url("[https://raw.githubusercontent.com/databricks/koalas/master/data/sample_stocks.csv](https://raw.githubusercontent.com/databricks/koalas/master/data/sample_stocks.csv)")
这简单了一步,因为有了 Optimus,你可以直接从网上读取数据。
考拉呢?
import databricks.koalas as ksdf = ks.read_csv("[https://raw.githubusercontent.com/databricks/koalas/master/data/sample_stocks.csv](https://raw.githubusercontent.com/databricks/koalas/master/data/sample_stocks.csv)")
这段代码会失败,那会发生在熊猫身上吗?
import pandas as pddf = pd.read_csv("[https://raw.githubusercontent.com/databricks/koalas/master/data/sample_stocks.csv](https://raw.githubusercontent.com/databricks/koalas/master/data/sample_stocks.csv)")
不,那会有用的。那是因为你可以直接从网上读取熊猫的数据。好吧,让我们让考拉代码工作:
import databricks.koalas as ksdf_ks = ks.read_csv("sample_stocks.csv")
这看起来很简单。顺便说一下,如果你想用 Optimus 从本地存储器中读取数据,几乎是一样的:
from optimus import Optimus
op = Optimus()
df_op_local = op.load.csv("sample_stocks.csv")
但是,让我们看看接下来会发生什么。这种数据帧有哪些类型?
print(type(df_sp))
print(type(df_op))
print(type(df_pd))
print(type(df_ks))
结果是:
<class 'pyspark.sql.dataframe.DataFrame'>
<class 'pyspark.sql.dataframe.DataFrame'>
<class 'pandas.core.frame.DataFrame'>
<class 'databricks.koalas.frame.DataFrame'>
所以除了 Spark 本身,唯一能创造 Spark DF 的框架就是 Optimus。这是什么意思?
让我们看看当我们想要显示数据时会发生什么。为了在 Spark 中显示数据,我们通常使用*。秀()法,而对于熊猫则为。*头()【法。
df_sp.show(1)
会按预期工作。
df_op.show(1)
也会起作用。
df_pd.head(1)
也会起作用。但是我们的考拉 DF 呢?你需要使用 pandas API,因为这是这个库的目标之一,让从 pandas 的过渡更容易。所以:
df_ks.show(1)
会失败,但是
df_ks.head(1)
会管用的。
如果你和我一起运行这段代码,如果你点击 spark 的 show ,你会看到:
+----------+------+------+------+------+----------+----------+----------+-------+-------+------+--------+----------+------+
| Date| Open| High| Low| Close| Volume|ExDividend|SplitRatio|AdjOpen|AdjHigh|AdjLow|AdjClose| AdjVolume|Symbol|
+----------+------+------+------+------+----------+----------+----------+-------+-------+------+--------+----------+------+
|2018-03-27|173.68|175.15|166.92|168.34|38962839.0| 0.0| 1.0| 173.68| 175.15|166.92| 168.34|38962839.0| AAPL|
+----------+------+------+------+------+----------+----------+----------+-------+-------+------+--------+----------+------+
only showing top 1 row
这有点糟糕。每个人都喜欢那些漂亮的 HTML 大纲表来查看他们的数据,熊猫也有,所以考拉从熊猫那里继承了它们。但是记住,这不是火花 DF。如果你真的想看到一个更漂亮的带有 Optimus 的 Spark DF 版本,你可以使用*。表()方法。*
df_op.table(1)
你会看到:
它更好地向您显示了数据以及相关信息,如列的类型、DF 中的行数、列数和分区数。
选择数据
让我们用我们的数据做更多的事情。比如切片。
我将选择日期、开盘价、盘高、盘低和交易量。可能有更多的选择数据的方法,我只是使用常见的。
带火花:
# With Spark
df_sp["Date","Open","High","Volume"].show(1)# or
df_sp.select("Date","Open","High","Volume").show(1)
带擎天柱:
df_op["Date","Open","High","Volume"].table(1)# or
df_op.select("Date","Open","High","Volume").table(1)# or with indices :)df_op.cols.select([0,1,2,5]).table(1)
与熊猫:
df_pd[["Date","Open","High","Volume"]].head(1)#or df_pd.iloc[:, [0,1,2,4]].head(1)
带考拉:
df_ks[["Date","Open","High","Volume"]].head(1) # will workdf_ks.iloc[:, [0,1,2,4]].head(1) # will faildf_ks.select("Date","Open","High","Volume") # will fail
所以正如你现在看到的,我们用 Optimus 很好地支持了不同的东西,如果你喜欢熊猫的[[]]风格,你也可以用它来处理考拉,但是你不能通过索引来选择,至少现在还不能。
与考拉和擎天柱不同的是,你在底层运行 Spark 代码,所以你不必担心性能。至少现在不是。
更多高级内容:
让我们得到一列的频率:
熊猫:
df_pd["Symbol"].value_counts()
考拉:
df_ks["Symbol"].value_counts()
他们是一样的,这很酷。
火花(一些坏零件):
df_sp.groupBy('Symbol').count().show()
擎天柱(你可以像在 Spark 里那样做):
df_op.groupBy('Symbol').count().show()
或者你可以使用*。cols* 属性获得更多功能:
df_op.cols.frequency("Symbol")
让我们用 One-Hot-encoding 来转变我们的数据:
熊猫:
# This is crazy easy
pd.get_dummies(data=df_pd, columns=["Symbol"]).head(1)
考拉:
# This is crazy easy too
ks.get_dummies(data=df_ks, columns=["Symbol"]).head(1)
火花(足够相似的结果,但做起来很恐怖):
# I hate this
from pyspark.ml.feature import StringIndexer,OneHotEncoderEstimatorindexer = StringIndexer(inputCol="Symbol", outputCol="SymbolIndex")
df_sp_indexed = indexer.fit(df_sp).transform(df_sp)encoder = OneHotEncoderEstimator(inputCols=["SymbolIndex"],
outputCols=["SymbolVec"])model = encoder.fit(df_sp_indexed)
df_sp_encoded = model.transform(df_sp_indexed)
df_sp_encoded.show(1)
擎天柱(好一点但我还是更喜欢考拉这个):
from optimus.ml.feature import string_to_index, one_hot_encoderdf_sp_indexed = string_to_index(df_sp, "Symbol")
df_sp_encoded = one_hot_encoder(df_sp_indexed, "Symbol_index")
df_sp_encoded.show()
在这种情况下,更简单的方法来自熊猫,幸运的是它在考拉身上实现了,这种类型的功能在未来会增加,但现在这几乎是我们所有的功能,正如你在这里看到的:
[## 一般功能-考拉 0.1.0 文档
编辑描述
考拉. readthedocs.io](https://koalas.readthedocs.io/en/latest/reference/general_functions.html)
但是他们在火花中运行,所以它摇滚。
情节:
绘图是数据分析的重要部分。对于熊猫,我们习惯于非常容易地绘制我们想要的任何东西,但是对于 Spark 来说,就没有那么容易了。我们很高兴地宣布,在最新版本的 Optimus (2.2.2)中,我们已经创建了一种直接从 Spark 数据帧创建绘图的方法,不需要子集化。
熊猫:
df_pd.plot.scatter("Open","Volume")
df_pd.boxplot("High")
df_pd.hist("Low")
考拉:
df_ks.hist("Low")
这样不行。
火花:
没有。
擎天柱:
df_op.plot.boxplot(“High”)
df_op.plot.hist(“Low”)
df_op.plot.scatterplot(["Open","Volume"])
Apache Spark 3.x:
databricks.com
以下是对 Spark 3.x 的一些期望:
- Scala 2.12 支持(列出两次)
- 非实验性连续加工
- Kubernetes 支持非实验性
- 数据源 API v2 的更新版本
- Hadoop 3.0 支持
- 提高 Spark 深度学习管道的可用性
还有更多!
databricks.com
结论
Spark 它正呈指数级增长,如果你现在没有使用它,你肯定应该使用它。通常你会来自熊猫或类似的地方,所以利用像考拉和擎天柱这样的伟大的库来改善你在数据科学世界中的生活。
如果您有任何问题,请在此写信给我:
[## 法维奥·瓦兹奎——science y Datos | LinkedIn 创始人/首席数据科学家
加入 LinkedIn ‼️‼️重要提示:由于 LinkedIn 技术限制,我现在只能接受连接请求…
www.linkedin.com](https://www.linkedin.com/in/faviovazquez/)
在推特上关注我:
Favio Vázquez 的最新推文(@FavioVaz)。数据科学家。物理学家和计算工程师。我有一个…
twitter.com](https://twitter.com/faviovaz)
祝学习愉快:)
卡尔曼滤波器和外部控制输入
使用状态偏移处理外部控制输入
Block diagram of a control system by Wikimedia user Orzetto (CC BY-SA 4.0). This article features a “controller” to the system, though not one designed to match a reference value.
在本文中,您将
- 使用
statsmodels
Python 模块实现带有外部控制输入的卡尔曼滤波器模型, - 使用最大似然估计卡尔曼滤波器模型矩阵中的未知参数,
- 在运动表现的体能-疲劳模型的背景下,了解如何通过卡尔曼滤波器对累积影响进行建模。
具有控制输入的卡尔曼滤波器
以下是具有外部“控制”输入 B u _t 的卡尔曼滤波器模型的说明:
其中qt∾n(0、𝐐)和rt∾n(0、𝐑).模型矩阵 A 、 B 、 H 、 Q 和 R 可能包含未知参数,并且通常被允许随时间变化。外部“控制向量”输入, u _ t ,必须知道到目前为止的所有时间点,如果任务需要预测未来的多个时间步,也必须知道。
在许多预测环境中,外部控制项是不使用的。标准的时间序列模型,其中内部系统动力学是唯一发挥作用的力量(如 ARIMA),不需要它们。因此,在statsmodels
中缺乏明确的控制规范机制并不奇怪。幸运的是,statsmodels
确实提供了一个状态截取的接口,它足以包含外部控制输入。
将控制输入用于 stats 模型
花点时间熟悉一下statsmodels
状态空间表示法,它对状态方程使用了与本文开头略有不同的符号:
本规范中没有的“state _ intercept”c_t在statsmodels
中默认为零。描述为“c:state _ intercept(k _ States x nobs)”,这意味着用户可以在每个时间点自由指定不同的状态截距值。(对于所有的statsmodels
卡尔曼滤波器模型矩阵都是如此。)但是设置
对于 t =1… T,,我们有一个带有控制输入的卡尔曼滤波器模型。
示例:状态空间形式的体能-疲劳模型
建模累积冲击第一部分 中的体能-疲劳模型为:
其中 p_t 为(运动)表现, w_t 为时间 t 的训练“剂量”(时间加权训练强度)。在我以前探索该模型的文章中,训练历史与其他函数的卷积是表示训练课程累积影响的机制。本文将通过保持一个系统状态来做一些不同的事情。为了做到这一点,我们必须将体能-疲劳模型置于状态空间形式,以训练剂量作为外部控制输入。
在上面的等式中,第一个卷积和代表运动健康,我现在称之为 h_t 。以下是滞后一个时间步长的结果:
分离定义 h_t 的卷积和中的最后一项,我们得到递归:
对于疲劳的卷积和,该论证以相同的方式进行,此后称为 g_t ,并且健康和疲劳的递归关系可以用下面的“状态空间形式”表示:
我们可以继续使用矩阵来表达模型的第二个“测量”阶段:
其中 r_t ~ N(0,σ)。控制输入滞后一个时间周期,这是我们必须考虑的,但除此之外,我们有一个带有外源控制输入的健康疲劳模型的典型状态空间公式。结合给定健康和疲劳的当前状态的性能测量模型,卡尔曼滤波器工具包(状态估计、简单插补和可能性评估)由我们支配。
本节将使用可从 建模累积影响第二部分 中使用的 R 要点中复制的模拟数据,也可作为 csv 文件提供给读者。要运行以下代码,请更改以下 Python 代码块中的文件路径:
import numpy as np
import pandas as pd
import matplotlib.pyplot as pltimport statsmodels as sm
from statsmodels.tsa.statespace.mlemodel import MLEModeltrain_df = pd.read_csv("<your location>/train_df.csv")
train_df.head()
这个块加载所需的依赖项,并打印几行输入数据集train_df
:
day day_of_week period w perf
0 1 0 build-up 10 489.197363
1 2 1 build-up 40 500.545312
2 3 2 build-up 42 479.886648
3 4 3 build-up 31 474.226865
4 5 4 build-up 46 459.322820
为了在statsmodels
中创建一个卡尔曼滤波器模型,我们必须扩展MLEModel
基类(从[mlemodel](https://www.statsmodels.org/dev/_modules/statsmodels/tsa/statespace/mlemodel.html)
模块扩展到)。
class FitnessFatigue(MLEModel): start_params = [500, .1, .3, 60, 15, 10]
param_names = ['p_0', 'k_g', 'k_h', 'tau_g', 'tau_h', 'sigma'] def __init__(self, p, w_lag1):
super().__init__(endog=p, k_states=2, exog=w_lag1)
self.initialize_approximate_diffuse() def update(self, params, **kwargs):
params = super().update(params, **kwargs) self['obs_intercept', 0, 0] = params[0]
# state space model ------
self['transition'] = (
np.array([[np.exp(-1.0 / params[3]), 0],
[0, np.exp(-1.0 / params[4])]])
)
self['state_intercept'] = (
np.array([[np.exp(-1.0 / params[3])],
[np.exp(-1.0 / params[4])]])
* self.exog
)
# measurement model
self['design', 0, 0] = params[1]
self['design', 0, 1] = params[2]
self['obs_cov', 0, 0] = params[5] ** 2
关于上面的类,需要注意一些事情:
- 我们必须在
start_params
中输入起始值 - 我们必须指定一个接受数据的
__init__
方法。在这种情况下,它必须接受性能测量和训练(即控制)数据。 - 请注意
__init__
内滞后训练变量的创建。体能-疲劳模型特别指定了在训练事件影响体能或疲劳之前的一段时间间隔。 - 我们能够避免在
[statsmodels](https://www.statsmodels.org/dev/generated/statsmodels.tsa.statespace.representation.Representation.html)
表示中指定预乘状态误差项的选择矩阵。这默认为一个零矩阵,我们通常会遇到麻烦,因为没有设置,但健康疲劳模型,当直接转换为状态空间形式时,没有状态误差项。
接下来,用数据实例化对象,并使用最大似然估计未知参数。请注意,我们确实需要延迟训练输入,以便匹配模型的规范(在文章发表期间,我已经反复讨论过这个问题)。
train_df['lag_w'] = train_df['w'].shift(1)
train_df = train_df.iloc[1:]ffm_kf = FitnessFatigue(train_df.perf, train_df.lag_w)mle_results = ffm_kf.fit(method = 'bfgs', maxiter = 1000)
mle_results.summary()
最后一个命令产生以下输出(间距已被修改):
输出显示卡尔曼滤波器在恢复仿真中使用的参数值方面做得很好。在 累积影响建模第三部分 中需要自定义非线性近似的标准误差,现在已经“开箱即用”另一方面,最大似然法在这种情况下确实需要一些调整;增加最小迭代次数并选择 BFGS 方法得到稳定的拟合。
鼓励读者重复上面的练习,用已知的初始化代替“近似漫射”初始化(目前已被注释掉)。与卡尔曼滤波器和最大似然法不同,使用已知初始化的结果有些不同,尤其是标准误差。当使用已知的初始化时,来自卡尔曼滤波器的标准误差估计类似于使用非线性近似的估计。对于近似漫射初始化,对于某些参数(尤其是指数中的“时间常数”)来说,它们要大得多。
卡尔曼滤波器使我们能够访问滤波状态估计和平滑状态估计,前者仅使用特定时间点之前的可用数据,后者将所有可用数据合并到每个时间点的状态估计中。下面我们将可视化过滤后的状态估计,这自然会经历一个粗略的开始:
fig = plt.figure(figsize = (12, 8))
plt.rcParams.update({'font.size': 18})plt.plot(mle_results.filtered_state[0, :], color='green',
label='fitness')
plt.plot(mle_results.filtered_state[1, :], color='red',
label='fatigue')
plt.title("Filtered estimates of state vector (Fitness and " +
"Fatigue) over time")
plt.xlabel('Time')
plt.ylabel('Filtered state estimate')
plt.legend()
plt.show()
假设真实的适应状态被初始化为 0,并逐渐变高,那么最初的几个过滤状态估计值会偏离很多,这解释了为什么likelihood_burn
被设置为 15。将图形代码中的filtered_state
替换为smoothed_state
显示的图片与 建模累积影响第一部分 中的图片非常相似。
讨论
在写这篇文章之前,我认为获得带控制输入的卡尔曼滤波器的唯一方法是购买 MATLAB 或 SAS/ETS 。虽然statsmodels
可以使规范更加简单,但使用时变状态截距仍然可以轻松地将控制输入添加到卡尔曼滤波器例程中。
用于定义模型的子类派生,就像在statsmodels
中使用的,是圆滑的,但是使得调试不那么透明。(有时这些例子不起作用。)记住结果对象包含它们的模型矩阵;将它们打印出来是调试的好方法!不要忘记statsmodels
中乘以状态误差的“选择矩阵”——它默认为零。我们在这里不需要它,因为适应性疲劳模型没有指定状态错误,但它会导致令人沮丧的调试场景,其中代码更改不会影响输出。
尽管由于statsmodels
默认值,缺少状态误差项使得当前任务更简单,但是添加状态误差项的能力是卡尔曼滤波器优于原始适应性-疲劳模型的一个优点。很容易想象训练之外影响潜伏体能和疲劳的事情,包括睡眠质量和营养。将状态误差项添加到原始的体能-疲劳模型是另一个要探索的变化。
与 中的卡尔曼滤波器和 中的最大似然不同,“近似扩散”初始化导致了与状态的已知初始化不同的结果。在那篇文章中,也许平稳 ARMA(1,2)模型太像垒球了,初始状态的不确定性只有在没有均值的非平稳情况下才真正重要。然而,这让我好奇如果在statsmodels
中实现的话,“精确扩散”初始化会有多么不同。
卡尔曼滤波器是一个非常强大的时间序列分析和建模工具。它不仅能够计算经典时间序列模型的困难可能性,而且能够处理具有外源控制输入的非平稳模型,甚至引导下一架航天飞机飞往月球。作为数据科学家,我们很幸运在statsmodels
有一个卡尔曼滤波器的免费开源实现,对于统计和工程目的都足够灵活。
卡尔曼滤波器和(最大)似然
了解估计卡尔曼滤波器矩阵中未知参数的可能性的能力和局限性
Figure 1 from Guillera-Arroita et al (2014) depicting a log-likelihood surface. CC BY 4.0
在本文中,您将:
- 使用 Python 模块
[statsmodels](https://www.statsmodels.org/stable/index.html)
估计卡尔曼滤波器模型矩阵中的未知参数 - 根据卡尔曼滤波模型计算单个观测值的对数似然
- 在
statsmodels
中探索不同状态初始化选项的影响
介绍
在 Kaggle 的网络流量时间序列预测比赛后,我立即意识到了卡尔曼滤波器的威力,这场比赛要求对数千个维基百科页面的未来网络流量进行预测。在这场竞赛中,像“中位数”这样简单的启发式算法很难被击败,我花在 ARIMA 和预言家模型上的精力毫无进展。我想知道传统的预测工具箱中是否有适合这项任务的东西。然后我看了一个帖子,作者是一个只知道叫“ os、”卡尔曼滤波器 第 8 名的用户。
自从这篇文章第一次被写出来, os 的身份现已被揭露为 Otto Seiskari ,他对这篇文章留下了一个启发性的回应。自从最初写这篇文章以来,我的观点也发生了变化,我将在最后解释这一点。
虽然 Otto 确实使用了中位数作为后备解决方案,但其核心方法是一个 8 态卡尔曼滤波器,对当地水平和每周季节性进行编码。Otto 还专门为这次比赛写了一个定制的 python 包, simdkalman 。型号规格直接来自 simdkalman 文档,粗体字体稍有变化:
其中 q_t ~ N (0, Q ) 和 r_t ~ N (0, R ) 。在本文中,度量 y _ t 是一个标量;r_ t 的方差由 1x1 矩阵R 表示。这里, x _ t 是未观测的“状态”向量, y_t 是时间段 t 的观测测量。这些系统共同构成了许多时间序列数据集的有用表示。采用这种框架的一个好处是卡尔曼滤波器机制的可用性,用于计算可能性和创建一步预测等(在 R 的 stats.arima 文档中搜索“卡尔曼”以查看其使用示例)。
实际上, A 和 H 可以来自每周季节性、本地趋势等的标准“食谱”。、 Q 和 R 未知的可能性更大。Otto 没有对它们进行估计,而是使用默认选项来选择 Q 和 R,然后使用称为“平滑参数”的标量来调整一个矩阵与另一个矩阵的比例。
尽管我不能否认这种方法的有效性,但作为一名统计学家,“构建”Q 和 R 并不适合我。然后我深入研究了 Roger Labbe 的基于 Jupyter 的文本,Python 中的卡尔曼和贝叶斯滤波器,发现它也在卡尔曼滤波器数学部分建议了一个类似的过程:“在实践中,”文本说,“我们挑选一个数字,对数据进行模拟,并选择一个工作良好的值。”
我听到了 15 年前教室里的另一个声音。一位名叫大卫·迪基的教授问道,他的话被反复引用,
你喜欢数字七吗?三个怎么样?”
[暂停以示强调]
让我们达成一致,在不知道数据在估算什么的情况下,不要从数据中计算任何东西。”
最大似然估计,对于它可能具有的任何错误,是估计未知量的原则方法,并且似然是卡尔曼滤波操作的“副产品”。在下一节中,我们将用状态空间形式的 ARMA(2,1)探索 Python 的statsmodels
中这些计算的中间过程。最后,我们将使用最大似然法,利用这种似然性得到对 A、H 和 Q (R 将是已知的)的估计。
卡尔曼滤波器和似然
顺序到达数据的可能性
以下联合 pdf 的产品扩展在时序情况下特别有用:
在我们的情况下,有模型矩阵 A 、 H 、 Q 和 R ,它们都有潜在的未知成分。设θ包含构造这些矩阵所需的所有未知参数。那么可能性的对数变成:
因此,有两个分析任务:
- 得到初始点的对数似然, y_0,
- 以过去为条件得到当前点的对数似然,y _ t | y _(t-1),…,y _0 。
回想一下,卡尔曼滤波器的“滤波”操作给出了未知状态向量x_t的条件分布,给出了到当前时间点 t 的所有观察值 y_t (脚本 Y_t ):
在的另一篇卡尔曼滤波器说明文章中,该分布被表示为时间 t 时未知状态的贝叶斯后验分布,但在这里,它被表示为潜在随机向量 x _t 的采样分布。
对可能性的条件贡献
一次解开测量模型会导致:
由此推出 y_t | y_{t-1},…,y_0 一定是正规的,因为 x {t-1} | *y{t-1},…,y_0 是。它也给了我们一个计算这个条件正态的均值和方差的起点。条件意义仅仅是:*
条件方差是使用随机向量的标准方差公式得到的:
回想一下 Q 、 R 、 A 和 H 都是由θ中的参数完全指定的。这些参数需要数值最大化例程中的起始值,因此需要在似然最大化的每个阶段进行上述计算。
初始点似然贡献
初始测量值的分布需要初始化状态均值向量和方差矩阵。在静态模型的情况下,存在基于系统长期运行行为的状态向量的正确初始(先验)均值和方差(参见华盛顿大学的笔记第 2 页了解更多细节)。
对于非静态模型,初始化更具挑战性。statsmodels
提供的解决方案是“近似扩散”初始化,即零均值和非常大的协方差矩阵。统计学家不喜欢这个(“这提出了数字问题,并且没有回答扩散结构 s 存在的问题”——Piet de Jong,1991 )。虽然我们不会在这里处理它, R 包 KFAS 确实提供了“精确的漫射初始化方法”为了简单明了,我们将在下面的例子中使用一个“已知的”初始化。
示例:状态空间形式的 ARMA 模型
因为本文将使用一个特定的 ARMA(1,2)模型:
其中 ϵ_t ~ N(0,1.2)为 t = 1,…,1000。
生成 ARMA(1,2)数据
使用statsmodels
模拟来自 ARMA(1,2)的数据很简单,但请注意 AR 和 MA 参数系数的输入方式:
import numpy as np
from statsmodels.tsa.arima_process import ArmaProcessnp.random.seed(20190529)
ar1ma2 = ArmaProcess(ar=np.array([1, -.9]),
ma=np.array([1, .2, -.1]))
y = ar1ma2.generate_sample(nsample=1000, scale=1.2)
以下是一些观察结果:
In [6]: print(y[0:5])
[1.41505527 1.38650169 1.54085345 2.53833794 3.9467489 ]
statsmodels
ARMA
类有一个用于拟合 ARMA 模型的fit()
方法:
from statsmodels.tsa.arima_model import ARMAmodel = ARMA(y, (1, 2))
model_fit = model.fit(trend='nc')
这导致输出(略有修改):
In [13]: model_fit.summary() Out[13]: <class 'statsmodels.iolib.summary.Summary'> """ ARMA Model Results ==================================================================
Dep. Variable: y No. Observations: 1000
Model: ARMA(1, 2) Log Likelihood **-1629.051**
==================================================================
coef std err z P>|z| [0.025 0.975]
------------------------------------------------------------------
ar.L1.y **0.9008** 0.017 51.919 0.000 0.867 0.935
ma.L1.y **0.1474** 0.037 4.036 0.000 0.076 0.219
ma.L2.y **-0.1360** 0.037 -3.715 0.000 -0.208 -0.064
注意样本的对数似然以及系数估计值(粗体)。
状态空间形式的 ARMA(1,2)模型
为了在状态空间框架中得到这个 ARMA(1,2)模型,我们有许多选择。“哈维”表示法的好处(如沃顿商学院课堂讲稿第 8 页所述)在于它直接结合了 AR 和 MA 系数。对于我们的模型,这种表示是:
其中, r _t 已被明确替换为 0,以表明所有误差可变性均在状态项中处理,且
尽管 R 为零,这个 ARMA(1,2)模型适合卡尔曼滤波框架。下面是这个模型在statsmodels
中的精确编码,尽管它比必要的更冗长:
from statsmodels.tsa.statespace.mlemodel import MLEModel
from statsmodels.tsa.statespace.tools import (constrain_stationary_univariate, unconstrain_stationary_univariate)class AR1MA2_verbose(MLEModel):
start_params = [.8, 0.24, -.11, 1.3]
param_names = ['ar1', 'ma1', 'ma2', 'sigma2'] def __init__(self, endog):
super().__init__(endog, k_states = 3) # Using the Harvey Representation
self['transition', 0, 1] = 1.0
self['transition', 1, 2] = 1.0 self['design', 0, 0] = 1.0 self['selection', 0, 0] = 1.0
self['selection', 1, 1] = 1.0
self['selection', 2, 2] = 1.0 self.initialize_known(np.array([0, 0, 0]), np.eye(3))
#self.initialize_stationary()
#self.initialize_approximate_diffuse() def transform_params(self, params):
phi = constrain_stationary_univariate(params[0:1])
theta = constrain_stationary_univariate(params[1:3])
sigma2 = params[3] ** 2
return np.r_[phi, theta, sigma2] def untransform_params(self, params):
phi = unconstrain_stationary_univariate(params[0:1])
theta = unconstrain_stationary_univariate(params[1:3])
sigma2 = params[3] ** 0.5
return np.r_[phi, theta, sigma2]
def update(self, params, **kwargs):
params = super().update(params, **kwargs) self['transition', 0, 0] = params[0] self['state_cov', 0, 0] = params[3] * 1.0
self['state_cov', 1, 0] = params[3] * params[1]
self['state_cov', 2, 0] = params[3] * params[2]
self['state_cov', 0, 1] = params[3] * params[1]
self['state_cov', 1, 1] = params[3] * params[1] ** 2
self['state_cov', 2, 1] = params[3] * params[1] * params[2]
self['state_cov', 0, 2] = params[3] * params[2]
self['state_cov', 1, 2] = params[3] * params[1] * params[2]
self['state_cov', 2, 2] = params[3] * params[2] ** 2
pass # adding this to ease some cpaste issues
statsmodels
模型表示的文档在这里是。下面是关于上述实现的一些注意事项。
- 注意“选择”矩阵被设置为等于
__init__
中的恒等式。按照statsmodels
状态空间表示的顺序,这个矩阵被状态空间误差预乘,默认情况下是零——如果你想要状态误差,你必须显式编码!。选择矩阵可以设置为(1 θ _1 θ _2)^T,使上述表示更简单。 start_params
变量将初始化最大似然例程,但是我们也将在接下来的演示中使用它。注意数值接近真实。- 参数转换对于似然性演示来说不是必需的,但是对于似然性最大化例程来说却是必需的。
- 如前所述,我们将从潜在状态均值和协方差矩阵(零均值和恒等方差矩阵)的已知初始化开始,以演示可能性计算。
计算几个点的对数似然
我们将首先创建一个卡尔曼滤波器模型的实例,并用初始值对其进行初始化。由于参数值包含了重建 A 、 H 、 Q 和 R 的所有必要信息,statsmodels
的卡尔曼滤波器可以在任何进一步优化之前立即开始滤波。
kf_model_verbose = AR1MA2_verbose(y)
filtered = kf_model_verbose.filter(kf_model_verbose.start_params)
在 iPython 中交互运行,很容易看到对于所有 1000 个时间点,filtered_state
包含一个 3 维状态向量,而filtered_state_cov
包含一个 3×3 协方差矩阵。
In [25]: filtered.filtered_state.shape
Out[25]: (3, 1000)In [26]: filtered.filtered_state_cov.shape
Out[26]: (3, 3, 1000)In [27]: kf_model_verbose.loglikeobs ... [0:3]
Out[27]: array([-1.92012925, -1.34946888, -1.37622846])
最后一行显示了根据statsmodels
的前三次测量的对数似然贡献。这些都是我们想要搭配的。
我们现在将使用第节中为顺序到达数据建立的公式。矩阵 A 、 H 、 Q 和 R 易于访问,矩阵乘积 HA 被计算并存储在下面的HA
中:
A = kf_model_verbose['transition', 0:, 0:]
H = kf_model_verbose['design', 0:, 0:]
Q = kf_model_verbose['state_cov', 0:, 0:]
R = kf_model_verbose['obs_cov', 0:, 0:]HA = np.matmul(H, A)
为了匹配似然向量中的第一个数字-1.9201,首先注意 μ 和σ在零向量和单位矩阵处被初始化。测量向量只取状态向量的第一个元素,没有额外的误差项( R 为零),因此第一个观察值y[0]
与一个 N (0 , 1)分布进行比较。
*from scipy.stats import norm
np.log(norm.pdf(y[0], 0, 1)) Out[36]: **-1.9201**292483430195*
由于 python 从零索引时间开始, μ _0 和σ*_ 0 实际上是给定初始数据测量的第一个更新的状态均值和方差矩阵。在可能性推导中的计算之后,***
*mu_0 = filtered.filtered_state[0:, 0]
Sigma_0 = filtered.filtered_state_cov[0:, 0:, 0]E_alpha_1 = np.matmul(np.matmul(H, A), mu_0)
V_alpha_1 = (np.matmul(np.matmul(HA, Sigma_0), np.transpose(HA)) +
np.matmul(np.matmul(H, Q), np.transpose(H)) + R)np.log(norm.pdf(y[1], E_alpha_1, np.sqrt(V_alpha_1)))*
结果为array([[-1.34946888]])
,匹配第二次测量的对数似然。再来一轮应该能让人相信我们已经掌握了模式。
*mu_1 = filtered.filtered_state[0:, 1]
Sigma_1 = filtered.filtered_state_cov[0:, 0:, 1]E_alpha_2 = np.matmul(np.matmul(H, A), mu_1)
V_alpha_2 = (np.matmul(np.matmul(HA, Sigma_1), np.transpose(HA)) +
np.matmul(np.matmul(H, Q), np.transpose(H)) + R)np.log(norm.pdf(y[2], E_alpha_2, np.sqrt(V_alpha_2)))Out[46]: array([[**-1.37622846**]])*
还是那句话,匹配。如果我们对 1000 次测量的对数似然性求和,我们得到的结果会比我们在 ARMA 拟合中看到的结果稍小(即更负),这是意料之中的,因为我们的似然性评估不是最大似然估计。
*np.sum(kf_model_verbose.loglikeobs(kf_model_verbose.start_params))
Out[47]: -1655.0364388567427*
未知参数的最大似然估计
上面创建的 Kalman Filter 对象一直在等待最大似然估计(否则它为什么会继承一个名为“MLEModel”的类)。它只需要运行对象的fit()
方法:
*kf_model_verbose_fit = kf_model_verbose.fit()
kf_model_verbose_fit.summary()*
摘要提供了(略有修改的输出):
*Statespace Model Results ==================================================================
Dep. Variable: y No. Observations: 1000
Model: AR1MA2_verbose Log Likelihood **-1629.327**
==================================================================
coef std err z P>|z| [0.025 0.975]
------------------------------------------------------------------
ar1 **0.9016** 0.018 50.742 0.000 0.867 0.936
ma1 **0.1472** 0.038 3.900 0.000 0.073 0.221
ma2 **-0.1366** 0.037 -3.686 0.000 -0.209 -0.064
sigma2 **1.5219** 0.071 21.542 0.000 1.383 1.660*
请注意,参数估计和对数似然接近 ARMA 估计,但不精确。这是由于已知的初始化。作为练习,运行以下两种变体,这将在讨论部分讨论:
- 注释掉已知的初始化,取消静态初始化的注释,然后重新运行。
- 注释掉静态初始化,取消注释“近似扩散”初始化,然后重新运行。
讨论
从上面的练习中,您应该已经观察到,您可以将 ARMA 模型的对数似然和参数估计与平稳初始化精确匹配。这并不奇怪,因为针对ARMA
类的fit()
方法的文档说,它通过卡尔曼滤波器使用精确的最大似然来“f 它的 ARMA(p,q)模型。”
在这种情况下,近似的漫射初始化导致与其精确的和已知的对应物相似的估计,但是它是一个固定的模型。初始对数似然值要小得多(< -7) so the total log-likelihood of the sample is a bit more negative. A “burn in” option exists to skip these first few observations in computing the likelihood by setting 【 in the class’s 【 function.
Once the machinery is in place to get the filtered state mean and variance, the likelihood of each measurement consists of only a few linear operations. This does mean a double loop will be necessary when maximizing the likelihood numerically, which explains why fitting even a simple ARMA(1, 2) model slows down when the number of observations gets moderately large.
Otto’s response
My original conclusion to this article concerned the practical methods of constructing a filter that do not involve formal estimation like presented here. I said that I might use one of these methods, if I could ever stop hearing the question: “你喜欢数字 7 吗?”
从那以后,最初的操作系统做出了回应,解释了为什么一种更基于工程的方法可能优于最大似然法。作为一个固执的人,我不得不在自己的工作中经历最大似然估计的困难(就像这个最近的竞赛)来充分理解这些论点。奥托·塞斯卡里解释道:**
“MLE 方法找到的解决方案的质量取决于模型描述数据的准确程度。卡尔曼滤波器通常适用于理论上模型假设非常糟糕的问题:噪声可能是非高斯的、非独立的,并且选择的隐藏状态模型与它应该建模的相比可能非常简单。然而,在选择某些参数的情况下,滤波器仍然可以很好地工作,但是实际中的最佳参数可能与最大似然估计有很大不同。”
我仍然喜欢使用最大似然估计来估计卡尔曼滤波器模型的未知参数,但是我也接受使用基于维持的调优,特别是当主要目标是预测时。在这两种情况下,我希望读者已经了解了更多关于卡尔曼滤波器的潜在概率基础,以及在没有使用最大似然估计的验证集的情况下什么是可能的。
所有数据科学家的关键问题是:“那又怎样?”
如何撰写假设和进行分析,让你的老板刮目相看
我之前公司的一位主管有一句口头禅,他因此在公司的分析师圈子里声名狼藉。
“那又怎么样?”
给这个人看一张图表,一张幻灯片,一份的完整报告,他的反应会是一样的。所以。什么?这两个词可能会给人以明显的敌意,而不是特别有用的管理指导。
“那又怎样?”
当然,一旦你有了一些时间来克服对你在过去六个小时里不知疲倦地制作的美丽散点图的不和谐反应,你就开始明白他们的观点了(尽管不情愿)。
Quoth the Director, “SO WHAT?” (Image: Pixabay)
数据科学家可能对他们的工作非常珍视。这不是批评——这种特征通常源于健康的激情。优秀的数据科学家是一群充满求知欲的人(在我的时间里,我已经为了分析而做了大量的分析工作!)
然而,在专业环境中,分析本身很少是最终目标。即使是世界上最大的分析公司——你的 Facebooks、Googles 和 Amazon——也在处理数据,以某种方式改善他们业务的某个方面。
在我以前做顾问的时候,我的分析应该是为商业决策提供信息,或者帮助形成客户建议的基础。因此有一句口头禅,“那又怎样?”我很快学会了自我评估我的工作。*两个客户群之间的差异具有统计学意义。*那又怎样?我的客户能用这些信息做什么有用的事情吗?
我在管理咨询行业工作了五年,把“有趣”的事情和“重要”的事情混为一谈,这是我经常看到的错误。我想委婉地说,在所有在客户面前使用过“高级分析”或“机器学习”这两个词的麦肯锡顾问中,只有不到一半的人真正知道这些东西是什么,更少的人知道它们如何实际帮助解决手头的业务问题。
只见树木不见森林
因此,如果面对一个数据的海洋(或者实际上是一个湖泊),我们应该如何找到保证是重要的分析,而不仅仅是一个智力上的杂耍?在这种情况下,好的分析源于一个构造良好的假设,而一个构造良好的假设是一个构造良好的问题树的果实。
问题树是任何顾问工具包中的经典工具。它允许将一个宏大的、包罗万象的主题分解成多个(和小得多的)“问题”,然后可以分别解决。创建问题树的黄金法则很简单:BeMECE!
MECE(发音为 Mee-See,最初由麦肯锡咨询公司的芭芭拉·明托在 20 世纪 60 年代开发)代表:
- MM最终 E 排他
- C 完全Exhaust
换句话说,从问题树的每个“节点”出来的分支应该将问题分解成不重叠的部分(ME),并且覆盖所有的可能性(CE)。最好用一个例子来说明这一点:假设我们的业务经历的在线销售比我们预期的要少。我们如何用问题树来调查这个问题?
Here we follow just the top branch, for the sake of visual clarity. Each grey box could, of course, have their own further branches
从左手边的第一个盒子开始。在线订单可以被认为是人们访问网站的次数乘以访问转化为销售的次数。因此,我们可以将流量产生和流量转化视为在线订单的两个“子问题”,然后我们可以孤立地考虑它们。
从数学上讲,这种分裂就是 MECE:
- A =在线订单
- B =网页流量
- *那么我们有:**A = B (A/B)
- 或者换句话说: 在线订单=网页流量流量转化*
网络流量的产生及其随后的转化是客户旅程的两个不同部分,我们可以独立地进行分析(因此,我们从数学上证明了我们的两个新分支可以在不需要第三个因素的情况下处理在线订单(因此,我们的两个新分支完全可以考虑 T42 的因素)。
注意——这不是唯一可能的 MECE 分裂。例如,您可以将在线订单表示为 【订单总数】 和 【在线订单份额】 的乘积。你可以用这些分支作为起点,写第二棵树,看看你是否会得出不同的结论。
创建了第一个分支后,你就可以从左到右,将每个问题分解成更多的子问题,越往下越好。
创建问题树的一些一般提示:
- 尝试用数学方法来表达:这是保持 MECE 的一个好方法。总要考虑如何将事物分解成乘法因子,就像我们对网上订单所做的那样。如果你试图分解一些商数 KPI (A/B),那么尝试第三个数据点(A/B = A/C * C/B)。
- **加上“其他”可以使一个分支完全详尽:**知道除了你能识别的因素之外,还有其他潜在的因素在起作用,可以帮助你避免做出无效的假设。
- **通常不止一个正确答案:**这也适用于树的顺序——我们可以先将流量生成分为付费/自然流量,而不是按设备划分流量。
实际应用
假设我们为 north wind Traders 工作或咨询,这是一家由微软创建的虚构公司,向世界各地的客户进出口食品和饮料。我们可以访问他们的 SQL 数据库,我们的任务是提出(然后测试)一些假设来帮助制定业务战略。
我们从哪里开始呢?
The SQL Schema for Northwind Traders
甚至不用深入研究数据,我们就可以得出一个基本的问题树来帮助构建我们的思维。我们从一个简单的没有人会反对的使命陈述开始: 增加 Northwind 的利润!
An example issue tree for Northwind. We could easily add more layers, but we’re already at a point where we can start thinking about potential hypotheses to test.
接下来是一个相对简单的问题树—增加收入,降低成本,等等。然而,到了第三层,我们已经处于可以开始问实质性问题的位置,这些问题可以构成假设的基础。
当然,在设计测试时,我们仍然需要做所有通常的好事情——确保我们有:
- 无懈可击的无效假设和替代假设
- 满足我们想要使用的统计检验所需的任何假设的样本(例如,正态分布的 T 检验)
- 明智的阿尔法水平拒绝零假设
让我们来看图表中的第三个注释。Northwind 可以通过鼓励每位客户更频繁地下订单,名义上增加每位客户的收入。然而,情况可能是这样的,订购较少的客户这样做是因为他们的订单更大——例如,他们每两个月购买 200 件商品,而不是每个月 100 件商品。销售主管可能有兴趣在大规模销售之前知道这是否属实!
明智的第一步是查看数据,看看它是否包含任何关于我们应该如何确切定义我们的假设的线索。让我们创建一个 Northwind 客户的散点图,根据订单的平均规模绘制出他们的平均订单间隔天数。
与我们最初预期的相关性相去甚远,似乎更频繁订购的客户实际上会做出更大的订单。我们的分析现在有了不同的框架——如果我们可以证明这种关系在统计上是显著的,那么鼓励更高阶频率的商业案例就会突然变得非常有力。
我们根据这一点定义我们的假设。设第 1 组是平均每两个月至少订购一次的客户,设第 2 组是平均每两个月订购不到一次的客户。然后,我们有:
- 零假设,第 1 组客户的 H₀:平均订单项≤第 2 组客户的平均订单项
- 另一个假设是,第 1 组客户的 Hₐ:平均订单项>第 2 组客户的平均订单项
然后我们可以运行蒙特卡洛模拟来得出 p 值(见我的以前的博客中关于如何做的完整指导)。
使用 Python 进行排列测试和蒙特卡罗模拟的介绍
towardsdatascience.com](/https-towardsdatascience-com-the-difficult-third-album-655d7a710412)
碰巧的是,我们发现这个测试的 p 值趋向于极小的 0.000005(或者 200,000 分之一)。鉴于我们的标准 alpha 水平为 0.05,这意味着我们可以断然拒绝零假设——更经常订购的客户会做出更大的订单,Northwind 应该尽一切努力增加其现有客户群的订购频率。
当然,如果我们不能拒绝零假设,这也是有价值的。通过证明这种关系的缺失,我们将能够建议反对增加订单频率的高强度措施。
这就是使用问题树方法定义假设的美妙之处——不管实验的结果如何,你总是能够进行分析,并对问题做出有力的回应: “那又怎样?”
感谢你一直读到博客的结尾!我很乐意听到任何关于上述分析的评论,或者这篇文章涉及的任何概念。欢迎在下面留言,或者通过 LinkedIn 联系我。
100 行代码中深度学习的关键
用神经网络预测肿瘤的恶性程度。用 Python 从头开始构建。
在这篇由 3 部分组成的文章中,您将:
- 用 Python 从零开始创建神经网络。用梯度下降算法训练它。
- 将基础网络应用于威斯康辛癌症数据集。根据 9 种不同的特征预测肿瘤是良性还是恶性。
- 深入探索反向传播和梯度下降是如何工作的。
- 复习基础知识,探索高级概念**。在第 1 部分的中,我们探索了我们网络的架构**。在第 2 部分的中,我们用 Python 编码,深入到 back-prop &渐变下降**。在第 3 部分,我们将**应用于威斯康辛癌症数据集。
让我们一起走下深度学习的亏损版图。准备好了吗?
Navigating the Loss Landscape within deep learning training processes. Variations include: Std SGD, LR annealing, large LR or SGD+momentum. Loss values modified & scaled to facilitate visual contrast. Visuals by Javier Ideami@ideami.com
对于那些对深度学习的奥秘和可能性充满热情的人来说,现在是激动人心的时刻。
该领域的许多英雄通过视频和文章分享他们的专业知识。他们包括 fast.ai 的杰瑞米·霍华德、Coursera 的吴恩达、Andrej Karpathy、Yann Lecun、Ian Goodfellow、Yoshua Bengio、Lex Fridman、Geoffrey hint on、Jürgen Schmidhuber 以及其他许多人。
他们中的许多人建议的关键事情之一是尽快动手,自己编写深度学习的关键原则。
如今,我们拥有令人惊叹的图书馆。包括 Tensorflow,PyTorch,Fast.ai,Keras,Mxnett,Nctk,DL4J 等。但是如果你只使用那些强大的库,你可能会错过一些重要的东西。有机会深入思考这些过程中一些最重要的部分。
在这个迷人的冒险中,自己编写一个网络迫使你面对面地面对关键问题和障碍。还有,深度学习背后隐藏的奇迹。
想想深度学习中所有新奇的架构和最新发展。回旋,循环网络,甘等等。真正令人着迷的是,在这个领域几乎每一次成功的背后,我们都找到了同样熟悉的朋友。反向传播和梯度下降。
Navigating the Loss Landscape. Values have been modified and scaled up to facilitate visual contrast.
如果你深刻理解这两个概念,天空才是极限。然后,您可以从更深的角度与强大的库一起工作。你也要准备好思考你自己的新方法来改进这些工具。
准备开始了吗?这将会是一场精彩的旅程!
Navigating the Loss Landscape. Values have been modified and scaled up to facilitate visual contrast.
寻找神秘的功能
宇宙中发生的很多事情都可以用函数来表达。函数是一种接受输入并产生输出的数学结构。因果。输入输出。
当我们审视这个世界及其挑战时,我们看到的是信息,我们看到的是数据。我们可以从这些数据中学到很多。
我们对数据进行的学习可以是不同种类的 T21。让我们强调深度学习中 3 种非常常见的类型:
- **监督学习:**当我们从一组带标签的训练数据中学习神秘函数时。将输入连接到其相应的正确输出的一组线对。
- **无监督学习:**当我们从没有以任何方式标记或分类的数据中学习神秘函数时。
- **强化学习:**当我们通过最大化代理人收到的报酬来学习神秘函数时。该代理在一个环境中采取行动。
在本文中,我们将重点关注监督学习,这是迄今为止深度学习最成功的领域。
因此,我们有一些数据,一些输入,我们也有一些与输入相对应的输出。我们想了解输入和输出是如何通过一个神秘的函数连接起来的。
事实是,当涉及的数据达到一定的复杂性时,找到那个函数变得非常困难。进入神经网络和深度学习。
Navigating the Loss Landscape. Values have been modified and scaled up to facilitate visual contrast.
在其核心,一个神经网络通过一系列中间“权重”连接你的输入数据和你想要的输出**。这些重量实际上只是数字。**
通过它们的架构和我们即将探索的优化算法,神经网络成为通用的近似器。他们最终能够计算连接其输入和输出的任何函数(当拥有正确的架构和参数时)。要对此进行扩展,请参见人工神经网络数学理论的通用逼近定理)。
理解神经网络的最佳方式是…去建一个!是的,从零开始,在这种情况下使用 Python 编程语言。所以让我们开始吧,在这个过程中,我们将探索许多有趣的话题和概念。
理解神经网络的最佳方式是…建造一个
在这一段下面你可以看到我们将要建立的网络。它有 2 层(输入层从不计算)。
- 输入:网络的输入包含我们的源数据。神经元的数量与源数据的特征数量相匹配。下图使用了 4 个输入要素。当我们稍后处理威斯康星癌症数据集时,我们将使用 9。
- 第一层:我们的第一个隐藏层,它有许多隐藏的神经元。这些神经元与它周围各层的所有单元相连。
- **第二层:**第二层也是最后一层有 1 个单个单元,网络的输出。
我们可以增加更多层,建立一个 10 层或 20 层的网络。为了简单起见,我们将在本文中使用 2。一个两层的神经网络可以做很多事情,我们很快就会知道。
那么在这个网络中,学习将在哪里进行?
让我们回顾一下。在我们网络的输入层中,我们放入一些数据。我们还将向网络显示什么输出对应于该输入,什么结果应该出现在网络的输出(第二层)。
网络各层中的每个单元都有一个关联权重(还有一个偏差,稍后会详细介绍)。这些权重只是在学习过程开始时随机初始化的数字。
神经网络执行一些计算 将输入数据与那些权重相结合。这些计算在网络中传播,直到在输出端产生最终结果。
这些计算的结果表达了将输入映射到输出的函数。
我们想要的是网络学习那些权重的最佳可能值。因为通过网络执行的计算,结合不同层使用这些权重,它能够近似不同类型的函数。
现在让更深入地了解我们正在寻找的这个神秘函数。为了做到这一点,至关重要的是我们准确地澄清我们任务中涉及的所有变量的名称。
- X 将代表输入层,即我们提供给网络的数据。
- Y 将代表与输入 X 相对应的目标输出,在网络完成计算后,我们应该在网络末端获得输出。
- Yh (y hat)将代表我们的预测,即我们将 X 输入网络后产生的输出。因此, Y 是理想的输出, Yh 是网络在输入数据后产生的输出。
- W 将代表网络各层的权重。
让我们先说第一层,我们的隐藏层,执行这个计算:W X(W 和 X 的乘积)
它执行加权求和:
- 层中的每个单元都连接到前一层中的每个单元。
- 这些连接中的每一个都有一个权重值。
- 图层中每个单元的新值是每个先前单元的值乘以该先前单元与我们当前正在分析的单元之间的连接权重的结果之和。
在某种程度上,权重表达了连接的强弱,即网络不同单元之间的链接强度。
现在我们要给这个乘积增加一些额外的东西,一个偏差项:
WX+b
添加偏置项为网络提供了更多的灵活性。它允许它“移动”单元的线性计算,增加了网络更快学习那些神秘函数的潜力。
b :表示机组的偏置项。
我们有了:WX+b。这就是我们所说的一个线性方程。线性,因为它通过一个乘积和一个和来表示输入和输出之间的线性关系(这种关系可以用一条线来表示)。
记住,神经网络可以有多层。在我们的示例中,我们将有 2 个,但我们也可以有 20 个或 200 个。
因此,我们将使用数字来表示这些术语属于哪一层。定义我们的隐藏层,也就是我们的第一层的计算的线性方程是: W1 X+ b1
我们还要给这个计算的输出取一个名字
- Z 将代表一层计算的输出
因此, Z1 = W1 X+ b1
请注意,应该对每层的每个单元进行这种计算。当我们对网络编程时,我们将使用矢量化实现。这意味着我们将利用矩阵在一次数学运算中组合一层的所有计算。
对于本教程来说,深入理解矩阵并不是必不可少的,但如果你想刷新对它们的理解,你可以查看 YouTube 上的 3Blue1Brown 和他的 线性代数系列 的精华视频。
到目前为止,一切顺利。现在,想象一个有许多层的网络。每一层都执行如上所述的线性计算。**当你将所有这些线性计算链接在一起时,**网络就能够计算复杂的函数。
然而,有一个小问题…
太线性,太无聊
世界是复杂的,世界是混乱的。现实生活中的输入和输出之间的关系一般不能用一条线来表达。它趋向于混乱,趋向于非线性。
复杂的函数通常是非线性的。如果一个神经网络的架构仅由线性计算组成,那么它很难计算非线性行为。这就是为什么神经网络会在每一层的末尾增加一些额外的东西:一个激活功能。
激活函数是非线性函数,其在层的输出中引入非线性变化。这将确保网络能够计算各种复杂的函数,包括那些高度非线性的函数。
现在,有很多不同种类的激活功能。让我们快速介绍 4 种最典型的。
为了解释这些激活函数,我需要**快速介绍梯度的概念,**我们将在后面深入探讨。函数在某一点的梯度也叫导数,表示函数在该点输出的变化率。
响应于特定输入变量的变化,函数输出的变化程度、方向和强度如何?
当梯度(导数)变得非常小时(函数的输出变得非常平坦),我们就谈到了**消失梯度。**稍后,我们将了解深度学习中大量使用的反向传播算法,通过使用梯度来了解网络的每个参数如何影响网络的输出,从而决定如何调整网络的权重值(该参数的变化会使网络的输出增加还是减少?)
消失梯度是一个问题,因为如果一点的梯度变得太小或为零,就很难理解系统输出在该点变化的方向。
我们也可以谈谈相反的问题,爆炸渐变。当梯度值变得非常大时,网络会变得非常不稳定。
不同的激活函数可以具有不同的优点。但是它们也可能遭受消失和爆炸梯度问题。
下面快速介绍一下最流行的激活功能。
s 形 1/(1+e**-x)
- 它的输出从 0 到 1。
- 它是非线性的,将输入推向输出范围的极限。例如,这对于将输入分为两类非常有用。
- 它的形状是平缓的,所以它的梯度(导数)将得到很好的控制。
- 主要的缺点是,在极端情况下,函数的输出变得非常平坦。这意味着它的导数、它的变化率将变得非常小,并且使用该函数的单元的计算效率和速度可能变慢或完全停止。
- 因此,Sigmoid 在网络的最后一层非常有用,因为它有助于将输出推向 0 或 1(例如,将输出分为两类)。在以前的图层中使用时,它可能会遇到渐变消失的问题。
*Tanh
(2/(1+e * -2x))-1
- 它的输出从-1 到 1。
- 它非常类似于乙状结肠,是它的缩小版。
- 这个函数更陡,所以它的导数也更强。
- 缺点类似于 Sigmoid 函数的缺点。
ReLU(整流线性单位) max (0,x)
- 如果输入大于 0,则输出为输入。否则,输出为 0。
- 它的范围从 0 到无穷大。这意味着它的产量可能会变得非常大。分解渐变可能会有问题。
- ReLU 的一个好处是,它可以保持网络更轻,因为一些神经元可能输出 0,防止所有单元同时活跃(过于密集)。
- ReLU 的一个问题是它的左侧完全是平的。这可能再次产生梯度,即变化率为 0,这可能会阻止该单元执行有用的计算。
- ReLU 计算简单,计算成本低。
- 目前,ReLUs 是神经网络内层最常用的激活函数。
soft max e * * x/Sum(e * * x)
- 输出范围在 0 和 1 之间。
- Softmax 将输入归一化为概率分布。它将输入压缩到 0 到 1 的范围内,就像 Sigmoid 一样,但它也会对结果进行除法运算,使所有输出之和为 1。
- 它通常用于多分类场景中的输出层,此时您必须将输出分类到多个类中。Softmax 将确保与每个类别关联的概率总和的值始终为 1。
在本文中,我们将在输出层使用 Sigmoid 函数,在隐藏层使用 ReLU 函数。
好了,现在我们理解了激活函数,我们需要给它们一个名字!
- 一个将代表激活功能的输出。
因此,在我们的隐藏层,我们执行的计算将是:
A1 = ReLU(Z1) 且 Z1=W1 X+b1
**在我们的第二层,我们的输出层,**计算将是:
A2 =乙状结肠(Z2) 且 Z2=W2 A1 + b2
注意 Z2 的等式中 A1 的使用,因为第二层的输入是第一层的输出,也就是 A1。
最后,注意 Yh=A2 。第 2 层的输出也是网络的最终输出。
原来如此。现在,如果我们将这些计算放在一起,如果我们将这些函数链接起来,我们会发现神经网络的总计算是这样的:
Yh = A2 = Sigmoid(W2 ReLU(W1 X+B1)+B2)
就是这样。这就是我们两层神经网络执行的全部计算。
因此,实际上,神经网络是一个函数链,有些是线性的,有些是非线性的,它们共同产生一个复杂的函数,这个神秘的函数将把你的输入数据与你想要的输出联系起来。
在这个阶段,注意到在那个方程的所有变量中,W 和 b 的值是很大的未知数。这里是学习必须发生的地方。
不知何故,网络必须学习 W 和 b 的正确值,这将允许它计算正确的函数。
因此,我们将训练我们的网络,找出 W1、b1、W2 和 b2 的正确值。但是在我们开始训练之前,我们必须首先初始化这些值。
如何初始化网络的权重和偏差本身就是一个完整的主题,我们将在后面深入探讨。现在,我们将使用随机值对它们进行初始化。
在这个阶段,我们可以开始编码我们的神经网络。让我们**用 Python 构建一个类,初始化它的主要参数。**然后我们再看看如何训练它学习我们的神秘功能。
因此 让我们直接进入本文第 2 部分的代码 ,我们将在旅途中学习和探索。
链接到本文的 3 个部分:
第 1 部分 | 第 2 部分 | 第 3 部分
发球之王:硒网球刮网
硒、Excel 和熊猫的力量
Josh Calabrese via unsplash
介绍
网球比赛中的发球往往决定了比赛的结果。在一盘比赛的关键时刻,发球的中断或发球的保持将最终决定是否能赢得奖杯。
通过将测量的网络抓取与适当放置的显式等待相结合,可以提取网球服务数据,将其导出到 Excel,并使用 Pandas 库进行相应的过滤。
这篇教程将演示如何通过一些巧妙的部署,让抓取脚本平稳运行,并以一种方便、易于分析的格式返回数据。
由于网球运动员的发球比赛可以决定在球场上获得荣耀或提前出局的区别,我将收集当前参加 ATP 巡回赛的前 20 名男子网球运动员的数据。具体来说,我想收集数据,这将使网球运动员之间的区别仅基于他们的发球。
寻找数据
要提取数据,我首先需要找到一个来源,这是最新的数据。ATP 巡回赛网站有超过 1000 名网球运动员的全面信息,似乎是一个声誉良好的来源。
最重要的是,当点击每个球员的链接文本时,可以看到特定于发球的数据,如下图的下半部分所示。正是这些信息,可以被提取出来用于比较前 20 名网球运动员。
代码工作流
在提取之前,确定代码工作流是很有用的。具体来说,我想:
遍历 ATP 巡回赛上排名前 20 位的男子网球运动员,提取他们的名字,他们转为职业选手的年份,以及上面显示的“单打服务记录”中的表格数据。
导入硒类
和tqdm
首先,我将包含 selenium 类的各种导入语句。我需要导入 webdriver 来导航到网页,导入 By 类来帮助查找 web 元素定位器,导入 options 类来实例化 options 对象,这样我就可以拥有一个无头浏览器。
除此之外,我还需要导入 WebDriverWait 类和 expected_conditions 类,它们协同工作。关于所有这些 selenium 类的信息可以通过这个链接找到。它们的使用示例如下所示。
最后,虽然不是绝对必要,但我将导入 tqdm,这样我可以在我的集成开发环境(IDE)的控制台中实时跟踪我的脚本的进度。
将提取的输出写入 Excel
在每次迭代中,数据被传输到一个逗号分隔值(CSV)文件中。首先,我创建了一个名为“Tennis _ player _ details _ scrape . CSV”的文件,并以“w”模式打开这个新创建的文件。
在获取数据并将特定于服务的信息添加到 CSV 文件之前,我为 CSV 文件创建了一些合理命名的标题(列标题),并包含了一个换行符。
查找数据并将其添加到不断增长的 CSV 文件中
第一个目标是找到第一个网球运动员。通过右键单击,然后检查,可以在 HTML 中找到网球运动员。使用 Chrome 中 developers 选项卡中的控制台工具,可以检查这个 Xpath 是否对应于诺瓦克·德约科维奇。Xpath 返回一个包含一个链接的列表。索引这个元素(第零个元素),并使用 innerText 函数提取文本,返回网球运动员诺瓦克·德约科维奇。
找到模式,找到所有玩家
如图所示,特定于诺瓦克·德约科维奇的 Xpath 对应于第 1 行。通过简单地将这个数字改为 3,参数 sack 的(tr[3])应该返回罗杰·费德勒。控制台中的结果证实了这一点。进一步验证;如果表排是 5,tr[5],网球选手应该是 Stefanos Tsitsipas。可以利用这种关系来收集前 20 名网球运动员的名字。
这可以通过创建一个范围为 1 到 21 的 for 循环并用 I 替换数字来实现。
为了进一步强化代码并防止任何超时异常,在网页加载缓慢的情况下,我将使用 WebDriverWait 类设置一个 20 秒的显式等待。在这里,浏览器和时间是这个类的必需参数。此外,我将一直等到 web 元素可见,然后使用带有 visibility_of_element_located 方法集的 expected conditions 类提取文本。
此方法将定位器作为必需的参数。这是使用“By”指定的。xpath,后跟引号中的 XPATH。
我把玩家名字写到 CSV 文件中,点击每个玩家的链接。然后,我提取球员转为职业球员的时间,并从这个数字中提取 2019 年,以确定他们迄今为止的职业生涯长度。同样,我将这些信息写入 CSV 文件。
在所有情况下,当我想要提取信息时,我会等到我感兴趣的特定元素是 web 页面上的可见性。这可以防止引发超时异常。
玩家统计
为了收集球员的统计数据,我点击了球员统计选项卡,与该球员的“单一服务记录”相关的数据被返回。搜集这些数据很简单。同样,唯一改变的部分是表格行[]中的数字,例如 tr[1]告诉我德约科维奇发球得分多少,而 tr[10]告诉我德约科维奇赢得的发球得分总数。
我简单地遍历这些有组织的表格数据,提取所有球员的统计数据,并使用嵌套的 for 循环将它们写入 excel。为了使后面的工作更容易,我替换了所有的逗号字符或%符号字符,这样数字数据操作就更容易了。
在这些数据被抓取并写入 Excel 后,我导航回到 singles 页面开始另一个迭代。
为了让我了解脚本的进度,我导入了 tqdm ,它打印了一个动态更新的进度条,并在我的范围内包装了这个函数。在下面的例子中,我可以看到进度是 40 %, 20 次迭代中有 8 次已经完成,还剩 1:17 分钟。
在 Excel 中打开 Tennis _ player _ details _ scrape . CSV 文件,显示已经获得了所有数据。现在可以使用 Pandas 库过滤数据。
熊猫服务数据过滤
单打发球数据现在可以修改进行分析。首先,我读入 Dataframe,检查它的形状和列,并使用 head()方法打印前 10 行。
为了确认所有数据都正确对齐,我执行了一些验证。举个例子,让我们以亚历山大·兹维列夫为例,检查一下他的一发率是否真的是 63%,在他的网球生涯中,到目前为止他已经面临了 1815 个破发点。事实上,这些信息结合在一起,如上面和下面突出显示的部分所示。
按获胜的发球游戏排序
当决定谁是最好的发球者时,一个关键的决定因素是根据发球得分对网球运动员进行分类。
为此,我将从抓取的 Dataframe 中提取两列,名为 df,创建一个名为 rankings 的新列,然后使用 sort_values 方法根据“Service_Games_won”列中的值对网球运动员进行排序。
返回的名为“网球发球”的数据显示,尽管目前世界排名第 15 位,但约翰·伊斯内尔的发球胜率最高。Milos Roanic 的发球胜率位居第二,但世界排名第 19 位。
世界排名第一的诺瓦克·德约科维奇排名第五,而罗杰·费德勒和大卫·戈芬各自的世界排名与他们在发球比赛获胜百分比表中的位置相匹配。
服务指数
虽然发球胜率最高的网球运动员可能是 ATP 巡回赛男子最佳发球者的最佳决定因素,但让我们使用一些其他收集的数据,创建一个简单的发球指数
这个指数将合并和平均;第一发球、赢得的第一发球得分、赢得的第二发球得分和挽救的破发点百分比。我在原始数据帧 df 中为此创建了一个新列,然后获取球员列和新创建的发球指数列,添加排名列,最后根据发球指数对前 20 名网球运动员进行排序。
约翰·伊斯内尔再次位居榜首,世界排名第一,诺瓦克·德约科维奇排名第六。显然,德约科维奇比赛的其他方面肯定让他成为目前的 ATP 世界第一。
摘要
这个抓取教程演示了如何将抓取的数据制成数据帧,以便使用 Pandas 库进行查询。在这里,洞察最好的网球发球在男子比赛中可以确定。
简单之吻
做得更少但做得更好
Photo by Kyle Cesmat on Unsplash
想象一个男人出现在一个正式的舞会上。他穿着一套剪裁完美的西装,夹克是亮粉色,带有红色方格图案。他的裤子是绿色的,带有紫色的竖条纹。这套衣服是一位著名设计师的作品,花了不少钱。这个人的脖子上还挂着一条金项链,双手戴着红宝石戒指。然而,在他身后,是一个穿着黑色礼服的女人,除了一对珍珠耳环之外,没有任何配饰。想一想这两位客人。
你认为这两者中哪一个更优雅?
IT 行业非常了解第一种类型。穿西装的人相当于一个花费了公司数百万美元的系统。该系统由来自不同提供商的几个组件组成,这些组件通常无法正常通信,而且往往只是在你需要向老板传递紧急信息的时候才会停止工作。
科技中的优雅
Unix 哲学更接近穿黑裙子的女士,专注于使用工具或编写程序,做一件事,但做得完美。
当我们问谷歌什么是“优雅”它提供了两个。
1.“外表或举止优雅时尚的品质.”
2.“令人愉快的巧妙和简单的品质;整洁。”
维基百科显示了类似的东西,
“优雅是一种美,表现出不同寻常的有效性和简单性.”
为什么我会在一个科技博客上进入优雅?因为正是我们这些工程师应该为过于复杂的系统、超出预算的项目以及围绕在我们工作周围的普遍丑陋(更好的说法是“技术债务”)而受到指责。我们大多数人都知道 KISS 原则,但是每当需要在代码中多实现一个特性时,我们经常会忘记它。
是什么阻碍了优雅?
紧张的时间限制是优雅解决方案的主要杀手。简单不等于“快”或“容易”通常情况下,是相反的。简单的设计需要对所有预期的用例进行仔细的思考和分析,以提出一个清晰简洁的想法。
虽然您可以在没有适当设计和没有对可能的功能范围进行太多考虑的情况下构建软件,但是维护代码将是一件痛苦的事情。因为每次出现一个 bug,它很可能是由您缺乏适当的边缘情况处理引起的。如果没有一个函数应该或不应该做什么的明确范围,您很可能会在代码的某个地方添加一个条件子句。
但是在十次这样的情况之后,你的代码看起来不再像最初发布时那样了。现在是一堆模糊的条件,没有人知道应用程序的预期行为是什么。有时候,乍一看完全不相关的东西的变化会导致不同子系统的错误。然后每个人都抓抓头,悄悄地恢复最后一次提交。是时候采用另一种方法了。
为什么复杂更容易实现
优雅的解决方案需要专注和观察。他们需要分析、与客户的良好沟通以及深思熟虑的范围。如果你没有时间弄清楚所有可能的输入参数,或者如果你只是因为缺乏适当的设计而不能弄清楚它们,并且客户不确定他将如何使用产品,那么你最终会在解决问题的过程中跳过一些步骤。
但是还有另一个复杂性的来源。而这一个更难克服。它是大量现成的组件和抽象等着你去使用。前端开发尤其脆弱。使用普通的 HTML5、CSS 和 Javascript 编写一个吸引人的 web 应用程序并不是一件容易的事情。
这就是为什么我们决定委托第三方来实施所有的基本细节,这样我们就可以专注于重要的事情。我们选择一个框架,寻找更多的模块,最终得到臭名昭著的 3GB node_modules
目录。只要这些模块中的抽象层符合我们使用它们的方式,一切都应该没问题。通常情况下,我们无法就框架中的某些内容达成一致,因此我们最终会编写一个糟糕的解决方案或一个特例来让它工作。
我希望我能分享一个可行的方法来处理泄漏的抽象,但是我不能。我没有!但是我知道我们不能停止使用它们。这将是巨大的资源浪费。
Photo by John Barkiple on Unsplash
如何保持简单
**你写手机应用?**与其创建自己的后端,不如使用后端即服务,比如 Firebase 。
**你想创建一个登陆页面或博客吗?**使用静态站点生成器(如 Jekyll 或 Gatsby )和静态文件托管(如 Netlify )。
你想要一个 CMS 吗? 检出 Contentful 或 DatoCMS。
厌倦了跟踪 web 服务的 SSL 证书吗?使用自动复习器,如 AWS ACM 、 Traefik 、 Caddy 或僵尸 Nginx 。
**希望您的代码“按需”运行,而不是支付固定价格的 VPC 实例?**尝试功能即服务(或无服务器)解决方案。
哦,对了,除非你有非常精致的品味,否则使用 AWS RDS/Cloud SQL/Azure SQL 这样的托管数据库服务是个不错的主意。
实现简单就是减少组件的数量而不是积累。
保持简单的好处
更多的移动部件通常意味着更多的错误。在一个经典的博客引擎示例中,您可以预期至少会有以下问题:
- 数据库性能差。
- 博客引擎性能差(内存/CPU 不足)。
- 网络吞吐量不足。
- 磁盘空间不足。
- 您为最悲观的用例过度配置,因此您在其余时间超额支付了费用。
- 部署失败。
- 数据库迁移可能会失控。
- VPC 可能会随着备份一起消失得无影无踪。
我不是在这里卖给你一个保护费,但是如果你选择一个静态站点生成器和一个静态文件托管,这些问题大部分都会消失。您还获得了一个免费的连续交付管道,其中每个源代码更改都会导致页面上可见的更改。既简单又功能齐全!
寻找灵感
有些人非常认真地对待 KISS 原则,并将其应用到他们的项目中。如果你这样做了,我很高兴听到你关于通过简单来保持解决方案优雅的建议。请随意使用下面的评论部分来分享一些优雅项目的伟大范例,简单减少的建议,或者关于应该避免什么的警告。
如果你喜欢我创造的东西,考虑订阅 Bit 更好。这是一个社区时事通讯,推荐书籍、文章、工具,有时还有音乐。
原载于 2018 年 8 月 24 日https://www.iamondemand.com。
又一个 AI 冬天?
如何部署更多的 ML 解决方案——五种策略。
Photo by Annie Spratt on Unsplash
TLDR:许多人担心又一个人工智能冬天。我们不缺 ML 试点,但企业只在部署其中的 10%左右。我们必须通过五种战术解决方案来降低部署成本。希望这篇帖子能帮助 ML 的高管、管理者、从业者思考得更深,行动得更快。我们是对抗另一个人工智能冬天的最后一道防线。最后,你可以找一个实时调查,看看别人是如何看待这个问题的。
这是一个密集的帖子。这里有一个目录来帮助你导航:
- 一个故事
- 大图:对人工智能的兴趣和供给
- 小图:对人工智能的需求
- AI Winters 的(非常)简史&今天的核心问题
- 五个子问题及战术解决方案
- 针对社区意见的实时调查
特别感谢至希斯克利夫·路易斯的宝贵意见。他的团队正在加拿大做一些不可思议的事情!
免责声明 :这篇文章没有得到我工作的任何公司或我提到的任何工具的支持或赞助。我交替使用人工智能、数据科学和 ML 这三个术语。
**喜欢你读的东西?**在媒体、 LinkedIn 或 Twitter 上关注我。还有,作为一名数据科学家,要不要学习商业思维和沟通技巧?查看我的“对机器学习的影响”指南。
如果你喜欢这篇文章,请分享,这样其他人就可以加入对话。
1.故事
在阅读了我的“数据科学很无聊”之后,我和 Michelle 最近进行了一次非常好的讨论,她是加拿大一家顶级银行的高管,有着积极的 ML 议程。
Michelle 负责监督 ML 概念证明(POC)的投资组合。每个 POC 都旨在了解特定的 ML 技术在 4-6 个月内是否对业务有价值。她的目标是部署,而不仅仅是完成每年更多的概念验证。她目前的部署率在 13%左右。
归结为两个问题:为什么我们不能部署更多的 ML 解决方案?又一个 AI 冬天来了吗?
我的简短回答是这样的:是的,如果你不部署更多的 ML 解决方案,另一个 AI 冬天将会到来。你和你的数据科学团队是抵御人工智能寒冬的最后一道防线。你需要解决五个关键挑战来保持势头。否则,你和你的数据科学团队将失去 21 世纪最性感的工作(显然我没这么说)。
2.大图:对人工智能的兴趣和供给
自 2012 年以来,我们一直在经历一个“人工智能的春天”(例如,许多关于人工智能的兴奋)。这是由于技术突破、深度学习的商业化和廉价计算。对人工智能兴趣的上升很大程度上是由Alex Krizhevsky(Geoff hint on 的学生和我的同事)的工作以及谷歌和英伟达等公司的投资推动的。
自 60 年代以来,我们每十年都有类似的艾泉水。然而, 艾温特斯 ,被 1)怀疑和 2)削减经费定义,每次都跟着。
人们会怀疑吗?似乎是这样的(或者至少开始是这样的)。今天市场上有各种各样的观点。总结的一个方法是看看谷歌搜索的趋势。虽然过于简单,但我们可以看到大趋势:整体兴趣仍然很高,但似乎趋于平缓。
Google Trend, searched on October 18, 2019; Author’s analysis
基金被削减了吗?还没有。有两股重要的潮流:风险投资和企业融资。根据毕马威(KPMG)的一份报告,如果我们比较 Q1 在 2018 年和 2019 年的投资以及历史交易,整体风险投资市场有所降温。但是,仍然有很多风险投资的钱。人工智能仍然是最热门的领域(直到风投找到更好的机会)。从供应的角度来看,人工智能初创企业和人才可能会保持增长势头。
Souce, captured on October 28, 2019
另一方面,企业定义了人工智能的真正需求和命运,因为 1)它们是许多人工智能初创公司的客户,2)它们雇佣了最多的人工智能人才。不幸的是,关于企业如何在内部资助人工智能计划的公开数据并不多。我们可以通过观察基本面来推断:**部署人工智能解决方案的企业是为了*实现,而不仅仅是为了说明,*承诺的价值观吗?**如果是这样,考虑到利润驱动的目标,他们会保持或增加资助。
3.小(呃)图:对人工智能的需求
让我们放大来看看近年来企业是如何采用和部署人工智能功能的。
- N = 11,400 organizations in North America, Europe, and Asia; 2) International Institute of Analytics; 3) Forbes, 2019; Author’s Analysis.
注意事项 : a)调查不代表全貌。有的公司当然部署 10%以上;我见过部署了 25–40%的公司,但它们通常是较小的公司。b)我们不知道 10%的部署是否足够。公共数据有限,无法显示 ML 与非 ML POCs 的部署率,或者 10%的部署回报是否涵盖 POC 计划的总成本;但普遍的看法是,“我们可以做得更好”。c)每次调查涵盖不同的公司,但通常代表北美的大型企业。
我的关键要点是:如果企业不部署更多的 ML 解决方案,内部对 AI 的需求将会减少。这将产生连锁反应。ML 人才会失去耐心,离开;风投会将投资转移到其他更有前景的机会;高管们将失去信心,并削减对人工智能计划的资助。历史会重演:又一个 AI 冬天一定会到来。我能感觉到寒意。
4.AI Winter(非常)简史&当今企业的核心问题
有许多原因导致了冬天的到来;它们可能是政治的、技术的和社会的。利比·金赛写了一篇文章,分析了今天的不同。好消息是:过去的许多限制因素,如数据(有更多的服务和工具来提供良好的培训数据)、处理能力、商业就绪性和数字化的整体水平都有所改善。坏消息是:我们仍然需要克服一个很大的障碍(一些老问题仍然存在,但相对而言,它们可以得到更好的管理)。
在我所观察的企业中,核心问题是**部署人工智能的经济性,**就像采用任何其他技术一样。这是我们整个行业必须克服的关键障碍。如果我们现在采取行动,许多经济因素都是可以解决的。
我最喜欢的作家琼·狄迪恩说:“生活瞬息万变。平凡的瞬间”。我们无法预测事情何时会发生。无论艾冬天,我们都要时刻保持正念,积极主动,做好准备。
所以,让我们更深入地思考一下,为什么企业只部署了大约 10%的 ML POCs 以及我们现在能做些什么。
5.让我们变得具体而有策略
简而言之,部署 ML 解决方案仍然过于昂贵。我们可以把它分解成五个子问题,了解核心问题,并相应地解决每一个问题。
**1)流程:从 POC 到部署的路径并不清晰。**大多数企业在整个组织中寻找 POC 想法,对一些有前途的想法进行优先排序和投资。试点完成后,人们会打开香槟,展示一些花哨的演示,然后沉默。许多团队不知道下一步是什么;他们不知道从哪里获得资金;他们不知道与谁合作来将概念验证增强为生产级解决方案。这本身就是一个问题,见第 3 点)。
**核心问题:**如何从概念验证转向生产系统?
解决方案 :为前期部署预留资金。设定明确的部署标准以触发资金发放(例如,比旧模型至少提高 2%的准确性)。有一个封闭的方法来释放后续资金。建立一个吸纳流程,让 IT 和运营专家尽早参与咨询。如果 PoC 最终进入部署阶段,要有一个资源规划流程。
**2)激励因素:POC 项目的 KPI 不正确。**通常,ML POC 计划是更大的企业创新任务的一部分。根据定义,这些想法需要有点“出格”。目标是学习而不是部署。这设置了错误的激励和期望。因此,数据科学团队专注于尝试前沿技术,而不是平衡创新和工程;他们交付的解决方案是可演示的,而不是可集成的;他们分享关于技术的学习,而不是将技术融入核心业务运营的计划。激励驱动行为;行为决定结果。
**核心问题:**如何让团队构建更可部署的解决方案?如何打造这样的团队?
解决方案 :将 KPI 从“学习”切换到“可部署创新”。使用我的 MJIT 方法 来平衡创新和可部署性😎*。强调深思熟虑的工程设计(仅够部署,而不是在证明价值之前过度工程设计)。将可交付成果标准化,以包括部署就绪的应用程序(这应该已经是可演示的)、集成计划以及关于学习、利弊和风险的业务案例。*
团队:许多 POC 团队没有合适的技能组合。很多数据科学团队只想建模型;他们不想做工程,不想做运营。如 2)中所讨论的,激励和一般期望起着关键作用。没有结合正确的工程实践,团队增加了部署的障碍。想象一下这样一个场景:您花了 4 个月的时间创建了一个很棒的 PoC,主管们都很喜欢它。但是你意识到你需要花至少 18 个月的时间来重新设计,组建合适的团队,并通过适当的工程尽职调查进行重建。这破坏了投资回报。
**核心问题:**如何让团队构建可部署的解决方案?如何打造这样的团队?
解决方案 :聘请有经验、对工程有热情的数据科学家。鼓励数据科学家学习全栈 ML ( 这个 是一个很好的起点)。如果你找不到他们或者他们太贵,通过利用来自工程和操作团队的专家创建一个混合团队。如果这些选项都不行,在LinkedIn上 DM 我,我很乐意聊天😉。
科技:基础设施差距很大。开发(DEV)和生产(PROD)环境有不同的数据和工具。因此,将解决方案从开发转移到生产时,需要进行大量额外的重构和测试。从数据的角度来看,大多数生产数据不能在开发中使用(有充分的理由)。当使用生产数据时,ML 性能可能会有很大差异。从工具的角度来看,在 DEV 中有许多新工具可用于创新目的,但是 PROD 仍然使用优化稳定性和可伸缩性的遗留工具(这不是一件坏事)。
**核心问题:**实现创新和稳态运行的最佳技术组合是什么?如何整合和简化它们?
解决方案 :创建一个沙盒环境来托管经过清理的最新数据。有一个指导方针来帮助团队在 ML 工作流程中选择正确的工具(例如,如果 PROD 不支持 Python Pandas,在开发中总是使用好的旧 SQL 进行数据管道传输;为如此关键的组件切换语言是一件非常痛苦的事情)。允许并鼓励团队使用 Dockers 架构,以允许更高级别的应用程序堆栈的灵活部署,即使一些基础架构&安全团队可能不喜欢它。将 ML DevOps 的做法( 埃里克布洛达 写好的 件 本 本 经 马丁福勒 )。
5)政治:对变革的阻力很大。我对此争论了很久,因为它看起来很普通,而且过于明显,但我认为它仍然值得一提。像任何新思想、工具或流程的引入一样,它会由于怀疑、不熟悉或误解而产生一定程度的不确定性。对失败的恐惧阻碍了重要而理性的决策。结果,团队花额外的时间在内部政治上导航;好的 POC 错过了发布窗口。
**核心问题:**如何获得利益相关者的认同?
解决方案:让价值观和利益一致。有正确的用例,有清晰有力的价值主张。让高管和运营利益相关者尽早参与上下游流程。与他们共同设计解决方案。通过 2)中提到的流程考虑他们的专家意见,尽早获得认同。采用分阶段展开的方法;这不是一个新的想法,但值得重申。雇佣那些不太受内部政治束缚的优秀顾问来敲门(也对冲风险😉).查看我在人工智能的最后一英里问题中概述的方法。
6.实时调查
以上是我的观察和建议。它们并不详尽,并且受我的经验和偏见的影响。我想借此机会听取社区的意见。我邀请您参加一个 10 秒钟的调查。一旦你分享了你的意见,你就能看到别人的想法。和往常一样,如果你有任何我错过的反馈或想法,请留下评论。
我将在几周后发表另一篇帖子来分享调查结果(以及对“其他”的回答)。在媒体上跟随我,这样你会得到通知。
综上
如果我们不部署更多的 ML 解决方案,人们将失去信心,企业将把注意力转移到更有前景的机会上,就像过去的 AI Winters 一样。我认为许多问题可以立即解决。有些是 ML 技术的具体问题,但许多是企业中永恒的挑战。虽然这听起来可能很无知,但让我们驾驭历史的进程,避免另一个人工智能的冬天!ML 的高管、经理和从业者,我们是抵御人工智能寒冬的最后一道防线。
**谢谢你能走到这一步。喜欢你读的吗?**关注我中、 LinkedIn ,或者 Twitter 。如果你喜欢这篇文章,请分享,这样其他人就可以加入对话。
还有,作为一名数据科学家,要不要学习商业思维和沟通技巧?查看我的“对机器学习的影响”指南。
你可能也会喜欢我的其他作品:
每个懒惰的全栈数据科学家都应该使用的 5 套工具
towardsdatascience.com](/the-most-useful-ml-tools-2020-e41b54061c58) [## 12 小时 ML 挑战
如何使用 Streamlit 和 DevOps 工具构建和部署 ML 应用程序
towardsdatascience.com](/build-full-stack-ml-12-hours-50c310fedd51) [## 越狱
我们应该如何设计推荐系统
towardsdatascience.com](/how-to-design-search-engines-24e9e2e7b7d0) [## 数据科学很无聊
我从事数据科学的正常(无聊)日子以及我如何应对
towardsdatascience.com](/data-science-is-boring-1d43473e353e) [## 人工智能的最后一英里问题
许多数据科学家没有充分考虑的一件事是
towardsdatascience.com](/fixing-the-last-mile-problems-of-deploying-ai-systems-in-the-real-world-4f1aab0ea10) [## ML 和敏捷注定的联姻
Udacity 的创始人巴斯蒂安·特龙毁了我的 ML 项目和婚礼
towardsdatascience.com](/a-doomed-marriage-of-ml-and-agile-b91b95b37e35)