TowardsDataScience-博客中文翻译-2021-七十四-

TowardsDataScience 博客中文翻译 2021(七十四)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

量子计算——为什么高管必须抽出时间

原文:https://towardsdatascience.com/quantum-computing-why-executives-must-find-time-48a6f756d81d?source=collection_archive---------39-----------------------

量子计算:为什么你应该关心(不到 30 分钟)

围绕理解量子计算的潜力,向经理和高管们发出战斗号令

Unsplash 上的地图框拍摄

这是我最近关于量子计算商业相关性的电子书的第 3 部分。

第一部分(见此)简要概述了什么是量子计算。第二节(看这里)讲的是现实中的意思。本节讨论量子就绪性,并阐明为什么经理和高管需要今天就开始理解这一点,即使他们的第一步很小。

所以,我明白了,但是我有点忙!

在 2021 年第三季度,坐在这里很容易看到量子不可能成为优先事项。我的意思是,我们不仅担心未来的 Covid 浪潮,而且我们有十几个 ESG 优先事项,我们担心德克萨斯州停电向我们展示了二十世纪中叶的基础设施,我们有不确定的全球地缘政治局势,我们因苏伊士运河封锁而措手不及,我们受到来自各方的勒索软件的威胁,我们正在努力应对多样化、多代人工作场所的挑战,我们的社会动荡达到了一代人所未见的水平。

因此,很容易把这个话题抛到脑后。也许你在想“这个可以等”直到有更好的有现实价值的例子出现。人们也很容易反思最近的热门词汇,如人工智能、区块链、云,并考虑到尽管有所有董事会级别的会议,但大多数会议都是由数据和 It 人员处理的,所以为什么 quantum 应该有所不同?但我认为这个不同。这不是某个特定团队的新工具,这就像台式电脑、互联网和智能手机的到来一样重要。正如我们在开始时所说的,你不想在每一次革命中更加积极主动吗?

当时棘手的是,很难真正讨论这些技术将如何改变事情。但是我们现在处于 20 多岁,当我们学习新事物时,数据和通信渠道不再是限制因素。事实上,限制因素是高管的经验和大胆的决策,这是剔除主题中多余噪音中的重要内容所必需的。然而,你的工作不是完全理解这个话题,而是将不同的人聚集在一起,给他们一个框架来回答‘这对我们有多重要?’以及‘我们应该何时以及如何开始参与?’。这是每一位经理和高管每周都应该考虑的两个问题。对于 quantum 来说,这没什么不同,只是奖励的规模和不作为的风险要大得多。

因此,在我们开始总结的时候,让我们来掩盖一些很容易让你忘记这个话题的话题。

仅仅是泡沫吗?

如今的高管们已经经历了所有商业领袖中最广泛的一系列由技术驱动的变革。世纪之交互联网的到来改变了许多企业的工作方式,即使互联网泡沫破灭,情况仍然明显不同。智能手机和社交媒体的同时到来同样具有破坏性。在技术方面,最近的流行语是人工智能和区块链。前者宽泛而模糊,通常与“计算机是智能的”这一概念混为一谈。英国首相鲍里斯·约翰逊在 2020 年提到了一种与英国考试成绩相关的“变异算法,这玷污了这个词整整一代人。很可能只有当无人驾驶汽车真正起飞的时候(一语双关非有意),人们才会真正看到 AI 在生活中的真正影响。

有人会说区块链是一种明显的比较器技术。从一个“非技术”主管的角度来看,它的复杂性和深奥本质是表面相似的。2016 年至 2019 年期间,区块链在大多数主流媒体上受到尊敬,世界各地的高管团队和董事会为区块链大师支付了过高的价格,以举办关于“分布式账本技术”将如何彻底改变他们的业务的研讨会。

除了比特币会让一小部分人变得非常富有之外,区块链科技还没有带来任何实质性的东西。虽然它将在幕后变得越来越普遍,因为它在某些情况下确实有非常真实的用途,但它让许多高管不再认为他们应该投入任何时间(或金钱)来尽早接触复杂的新技术。

重要的是,量子计算不会因为变异算法或者毫无意义的区块链宣言的影响而受到冷遇。所谓的量子冬天,技术发展慢于预期,潜在买家不感兴趣,这不太可能,但也不是不可能,这是高管们认真考虑量子准备工作时机的真正原因

我能获得真正的竞争优势吗?

在某种程度上,最重要的问题是行动更快的人会获得不容置疑的领先优势吗?一般来说,量子算法在商业领域的应用将是渐进的——这既是因为量子计算机能力的不断进步,也是因为改变游戏规则的量子计算“杀手级应用”将需要数年才能出现。

因此,早期行动者可能会使用他们的算法在投资组合中生成几个基点,或者降低 0.1%的物流成本,或者减少能源消耗。这是无可争议的“真金白银”,但它不会从根本上改变竞争环境,也不能转化为有意义的长期竞争优势,因为它的竞争对手可以复制。

一个不同的思维领域表明,尽管网络优化问题的收益微不足道,但围绕建模的工作可能会导致专利保护这些发现的巨大不连续性,足以改变该行业的格局——如果一种新的汽车电池技术的效率提高 400%,并且工厂可以迅速转化为生产它们,会怎么样?如果化肥的生产成本只有竞争对手的 30%,而且可以快速实施流程变革,那会怎样?如果一家公司找到了在几个月而不是几年内开发新药的方法,会怎么样?这些都可能改变一个行业的动态,让新的领导者拥有不可撼动的地位。

最终,这些未知只是增加了高管的工作,即在 VUCA 世界中为他们的公司定义正确的方向和选择。波动性、不确定性、复杂性和模糊性已经存在了几个世纪,量子计算只是引发更多同样问题的最新导火索。与其他挑战一样,例如不断变化的竞争格局、地缘政治压力、不断变化的客户情绪或对可持续发展的预期,高管的工作是确保他们了解足够多的信息,以减少他们的选项集,使他们能够相对自信地做出正确的决定。

因此,在这一点上,任何高管都不应该真正试图决定他们的十年量子战略,但他们绝对应该考虑他们对量子准备的渴望,以及采取哪些措施来增加他们和他们的组织的知识,消除盲点,并确保当需要做出重要决定时,他们都尽可能地了解情况,而不是受到他们多年前没有做的事情的限制。

我的 IT 投资怎么办?

现在应该清楚的是,广泛的量子计算还有一段时间,在最初几年将非常专注于它将做什么。因此,您当前的硬件和软件投资没有快速过时的风险(除了提到的与加密相关的风险),您的程序员、IT 专业人员和数据科学家的技能组合也没有过时的风险。量子计算与收发电子邮件、制作 PowerPoint、运营工厂、运营电子商务网站、在呼叫中心接听电话或在 Excel 上进行分析毫无关系。

就 IT 的近期变化而言,对新数据分析工具的关注、云的使用、混合工作和敏捷交付模式的实施对您和您的员工的影响将远远超过量子计算。

量子计算在打破互联网中的作用。

正如我在开始时提到的,量子计算越来越多地在主流出版物中被讨论的原因之一是因为量子计算机已被证明有能力破解用于保持互联网安全的大部分加密。麻省理工学院的彼得·肖尔在 1994 年提出了一种算法,肖尔的算法至今仍是最著名的质量控制算法。许多现代加密技术的基础是计算机分解非常大的数字的速度很慢。Shor 认为量子计算机非常适合这项任务。

最近的分析表明,要在一天内破解 RSA 2048 位加密,需要一台数百万量子比特的量子计算机。即使我们离这种机器还有几年时间,也可能需要十年或更长时间来替换 it 和通信系统的核心元素,因此这个话题现在变得越来越重要。由于所涉及的成本和相关的战略问题,这个话题正从首席信息官& CISO(首席信息官&首席信息安全官)转移到首席运营官、首席财务官,并可能转移到高管团队和董事会。

在某些方面,令人沮丧的是,对于这些高管来说,第一次真正接触量子计算将是一场关于风险的讨论,以及以一种不创造新价值的防御性方式花费大量资金的讨论。如果我们能利用这些讨论让他们意识到 QC 对他们的行业和业务更广泛、更有益的方面,那将是一线希望。

今天收获,明天解密

可以说,最直接的担忧是“今天收获,明天解密”攻击(HTDT)的概念,在这种攻击中,数据在正常的网络攻击中被悄悄渗透(即黑客不会公布它已被窃取),并被保留,直到使用量子计算机解密变得经济划算。这可能是在 5 年、10 年或 15 年后,虽然大多数数据在这段时间内价值很小或没有价值,但某些类型的数据(例如健康数据、银行记录、政府和军事信息)可能有价值。这可能看起来不可思议,但越来越多的人认为,这种威胁正在增加,攻击可能已经发生。

量子冬天的风险?

许多 QC 实践者关心量子冬天的概念。这个短语,在 AI-winter 的上下文中使用得更广泛,是指在一个技术的快速和热情增长期之后,由于没有回报的期望,事情似乎变得缓慢。要获得发展一个新技术产业所需的投资,需要一种微妙的平衡,但不能过度承诺影响的速度(以及财务回报)。无论所有参与者如何意识到这种风险,这都是一个经典的博弈论问题,每个人(研究人员、初创企业等)都希望尽可能多地制造噪音,以获得关注,但更深思熟虑、近乎冷漠的方法会更好地服务于整体空间。

随着主流媒体和更广泛的投资圈越来越多地提到“量子计算”,这些担忧在 2020 年首次被提出。对于事情将如何发展,专家们有着强烈的不同观点,可能的道路是持续的、令人印象深刻的技术发展,但真正的业务影响有些滞后。

从高管思考量子就绪的角度来看,在 2021 年下半年考虑这个话题肯定比 18 个月前更容易。硬件、软件和算法的加速发展,以及顾问和咨询师生态系统的明显增长,意味着尽管预测时间表仍然是不可能的,但现在越来越适合考虑你的量子准备计划。

其他类型的量子技术

科学家和工程师正在利用物质的量子特性取得多项其他技术进步。这些基本上与本入门书中的量子计算讨论无关,提及它们会分散注意力,而且在大多数情况下,尽管在利基领域非常重要,但在执行层面上并不相关。

这些其他术语包括量子通信、量子光子学、量子传感器和量子随机数生成。

更令人困惑的是,虽然大多数技术都不相关,但其中一些技术是“后量子密码术”的关键,它降低了我们之前讨论的量子计算机入侵通信网络的风险。

我的建议?忽略这一切;对你们中 99%的人来说,唯一重要的量子事物是量子计算。

我就不能等到 QCs 正式可用吗?

尽管经典计算机可以更好地(也更便宜地)完成同样的工作,但公司开始开发和运行量子算法还有另一个很好的原因。这个原因是关于测试和证明,以及关于对正在开发的工具的信心

一旦我们有了真正的量子优势,就很难直接确认 QC 的计算结果。这种缺乏检查的情况让许多人感到不安——从将开发算法的数据科学家,到需要向监管机构确认他们的公司了解他们的技术如何运作的合规官。因此,通过开发和运行简单的量子算法,其输出可以与经典计算机的输出相比较,公司可以围绕他们的方法建立信心。

在许多情况下,公司会将这些早期量子算法的开发外包给第三方,因为他们没有内部技能。即使在这些情况下,尽早开始与这些公司合作也会让双方对合作充满信心,为未来更复杂的工作做好准备。

接下来呢?量子就绪和问自己的问题

所以,希望现在已经很清楚,没有人能准确预测什么时候会发生什么。因此,你的目标是找出那些让你以最小成本获得未来期权价值的无悔举措——如果你愿意,这是一个真正的优化和权衡问题!

我挑战你问这些相互关联的问题,这将有助于定义你的量子准备立场。

-哪些角色应该考虑这个问题?

-如果我要指定一个重点人物,那会是谁?

-这与哪些机会最相关?

-我所在的行业对新技术有什么看法?我们是领导还是跟随?

-我认为我的竞争对手会在多大程度上考虑这个问题?

-我应该采取什么步骤来确保我们稳步建立意识和知识?

-这在我们的风险规划、it 开发路径和创新路线图中处于什么位置?

最重要的是:

-明天我该做哪三件事?

就这样结束了……

(所有三章的链接请点击这里)

阑尾在哪里?

我特意没有阑尾。我希望读者对已经达到这一点感觉很好,并且希望已经完全理解了每一部分。以一个高的、想要更多的结尾要比感觉不够好得多,因为就在你已经“理解”它的时候,你面对的是一页页的“更深的技术”、“进一步的阅读”和“额外的例子”。

相反,在我们结束时,让我围绕“下一步是什么”提出几点最后意见。

这 30 分钟阅读的目的是帮助你决定你是否需要关心。如果你决定不,那绝对没问题。如果你认为是,那么你需要计划和执行你的下一步,努力增加你的知识。

在这样一个快速发展的领域,指向特定资源从来都不是一件简单的事情。对于那些从正式的学术和研究的角度来看,有通常的出版物。对于希望与时俱进的企业高管来说,没有一个地方可以去。

到目前为止,还没有明确的出版物专注于商业方面(尽管这正是我们为量子伦敦所努力的方向)。对于持续不断的简短、易于理解的文章,尽管大部分是关于技术和投资方面的,你可以看量子日报和量子计算报告。对于更不敬的观点,塞尔吉奥·加戈在量子海盗上发表了文章,而量子伦敦的量子计算出版物可以在媒体上为你们中的成员关注

一些以商业为重点的量子会议已经举办了几年。这些显然是在过去的 12 个月里上线的。看到他们决定在后疫情时代追求什么样的模式将会很有趣。

就定期网络研讨会而言,请务必关注我们在 Quantum London 以及众多合作伙伴的成果。对于保险行业的人来说,Quantum London 负责人 Anahita Zardoshti 的必读书是最近关于保险行业量子计算的报告,其中也包括对技术方面的稍微深入的了解。所有的链接都可以在 www.qubits.london 找到。

如果这本入门书吊起了你的胃口,那么我们也推荐第一本关于量子计算的商业影响的完整书籍。这是 Quantum London 的一个朋友写的,他也是一名普通的技术作家,Brian Lenahan。

关于量子伦敦的一个注记

量子伦敦 是一个由 伊曼纽·科隆内拉*阿纳希塔·扎多什蒂 和我、 保罗·库莫 经营的协会。在整个疫情,它举办了网络研讨会、量子 Stammtisch 讨论活动和在线编码会议。当我们回到一个更现实的世界,我们将继续我们的商业使命一个关注量子的商业影响的社区。这将通过我们的在线全球活动以及在伦敦、英国和意大利米兰的现场活动来实现。*

请看这里之前的章节:第一节(见这里)简要概述了什么是量子计算。Section 2 ( 见此处)讲的是现实中的意思。

关于作者

Paolo Cuomo 在保险行业工作,但一直对新技术如何影响传统行业感兴趣。

Paolo 坚信通过简短、清晰的解释让对话进行下去。这本初级读本旨在为量子计算做些什么,让企业高管理解“量子就绪”的必要性。

Paolo 的大部分见解来自于他采访的专家,并通过他共同主持的网络研讨会系列听取了这些专家的意见,这是量子伦敦计划的一部分。

Paolo 拥有伦敦帝国理工学院的电子工程硕士学位和芝加哥附近凯洛格管理学院的 MBA 学位。他是工程技术学院的研究员。

密码学新时代的曙光:量子密码

原文:https://towardsdatascience.com/quantum-cryptography-b3b940ea711b?source=collection_archive---------31-----------------------

为后量子时代加密做好准备

利亚姆·塔克在 Unsplash 上拍摄的照片

围绕量子计算和它可以快速解决的用例有很多讨论,经典计算机可能需要很长时间才能解决。

这是一个确定的理论事实(截至目前)。这在谷歌宣布实现量子霸权后获得了很大的动力。

但是量子计算可能带来的最大影响或破坏之一是密码学。

詹姆斯·图斯在 Unsplash 上拍摄的照片

当前密码系统

在未知的电子零售网站上输入信用卡号码之前,你总是会小心谨慎或持怀疑态度。原因很明显,你想远离任何形式的欺诈活动。

Bermix 工作室Unsplash 拍摄的照片

因此,任何严肃的道德网上卖家都会遵循加密协议,让顾客感到安全,并避免任何欺诈性的交易活动。

目前使用的加密代码是基于密钥加密系统。

就像当你被困在电梯里时,有人给了你一条如下的加密信息:

神秘的信息

这没有透露任何意义或传达任何信息!

但是,如果有人告诉你对消息中的每个字符都应用+1,那该怎么办呢?那就是取字母表中的下一个字符。

消息解码!

这有道理!

当前加密机制

它建立在信任之上。

信任因素是什么?

从技术上讲,这个信任因子的基础是 RSA 算法

但是为了简单起见,让我们把它称为信任因子,以便于理解这个概念。

在这里,信任因子的意思是,当前的计算机或经典计算机将花费大量时间来预测一个巨大数字的精确质因数。

反过来的情况很容易用传统的计算机来完成。也就是说,它可以很容易地计算或做两个非常大的素数的乘法。但是因式分解是一个棘手的问题!

micha Parzuchowski 在 Unsplash 上的照片

这是一个可以通过量子计算机或者一种叫做肖尔算法的算法来解决的难题。

因此,企业可以为后量子时代做准备。

那量子密码是怎么工作的?

你可能会问,量子密码或它的加密机制有什么特别之处,它们不能被破解吗?

答案在于物理学。

安德鲁·乔治在 Unsplash 上拍摄的照片

爱因斯坦的理论很棒,但在这里我们要感谢海森堡的测不准原理它指出,你试图测量一个粒子的动量或速度越精确,就越难获得它的位置,反之亦然。

爱丽丝-鲍勃安全通信信道

为了实现可靠的加密,窃听者不应该能够获得能够解密加密消息的密钥。

所以通信信道需要防篡改。

在量子加密的情况下,正如你从上图中看到的,Alice 试图向 Bob 发送特定方向的光子。Bob 放置了一个上下方向的滤光器(在中间),这样从滤光器出来的光子要么向上要么向下。即使光子在 Alice 的末端是对角倾斜的,它也是以向上或向下的方向从过滤器出来。

接下来发生的就是一通电话。什么?

杰米·霍顿在 Unsplash 上拍摄的照片

是啊!

Alice 和 Bob 通过安全通道进行连接,Bob 向 Alice 验证输出。对于爱丽丝和鲍勃来说,那些方向相同的光子被保留下来,其余的都被丢弃了。

那是你的防篡改钥匙。

答对了。

照片由布鲁斯·马尔斯Unsplash 上拍摄

又一个疑问..

如果窃听者试图测量或保持自己的光子过滤器在 Bob 的过滤器中间或之前会怎样?

没什么大不了的!

如果窃听者试图测量光子的状态,这个状态就会崩溃。感谢量子力学和亚原子粒子的核心性质。

Bob 将拥有自己的滤波器和测量值,然后在与 Alice 进行通信后决定密钥,因此窃听者将永远无法猜出用于加密的密钥。

为量子物理干杯!

蛋白质折叠的量子景观

原文:https://towardsdatascience.com/quantum-landscape-for-protein-discovery-62c0c86fc27e?source=collection_archive---------16-----------------------

思想和理论

量子计算如何有希望开发治疗神经退行性疾病的药物等等

学分:Unsplash

药物设计的计算方法已经研究了很多年。药物设计的一个关键方面是推断或预测蛋白质的折叠,这将导致识别蛋白质如何相互作用。例如,大多数神经退行性疾病都是基于在大脑中传播的错误折叠的蛋白质,设计能够阻断它们的互补蛋白质有望成为治疗方法。

学分:Unsplash

然而,尽管我们在技术上取得了进步,模拟蛋白质仍然具有挑战性。这种困难源于这样一个事实,即蛋白质由 21 种不同的氨基酸组成(人类的序列多达 400 种),这些氨基酸可以相互结合,并可以采用几万亿种形状中的一种。在这里,我将回顾目前的公式作为蛋白质折叠晶格,并报告一个例子的折叠可以发现使用 D 波量子计算机。以下部分是:

  1. 蛋白质折叠问题
  2. 量子退火
  3. 蛋白质折叠的实用量子退火

蛋白质折叠问题

蛋白质可以以多种方式折叠,因为它们由许多氨基酸链组成,这些氨基酸链组合起来可以呈现几种可能的形状。

据信,蛋白质在稳定状态下折叠自身,这与最低能量状态有关。此外,一些疾病被认为是由蛋白质引起的,这些蛋白质不能正确折叠,然后在器官中积累,导致阿尔茨海默氏症、亨廷顿氏症和帕金森氏症等疾病。下图显示了实现的折叠和相应的最小化能量。

蛋白质折叠由蛋白质探索者作者创建

已经尝试使用 NMR(核磁共振)、低温电子显微镜和 X 射线晶体学来精确定义蛋白质的折叠,尽管这些方法仍然非常昂贵和耗时。最近,DeepMind 在机器学习方面的进展导致了 AlphaFold :一个能够预测蛋白质属性的神经网络给出了它的基因序列。令人难以置信的结果已经实现(更多信息见视频和下面的链接)

https://medium.com/geekculture/5-cool-ai-powered-drug-discovery-tools-1d7e976ffc2a

然而,Alphafold(及其扩展)受到神经网络近似的限制。我们在这里可以提出的问题是,是否有其他方法可以更有效、更快速地预测蛋白质折叠。

有理论和实验证据表明,与其他经典方法相比,使用量子退火解决经典优化问题具有优势(参见桑托罗&托萨蒂 2006 )。

现有多项式映射和更先进的量子器件的结合允许模拟更大的晶格折叠和其他相关优化问题。在这种背景下, D-WaveIBM 已经率先实现了优于经典计算的量子退火和优化。例如,青霉素分子有 41 个原子。在经典计算机上对其建模需要 10 的 86 次方位,而在量子计算机上只需要 286 个量子位(可能定义为耦合团簇或 sto-ng 对额外轨道的估计)。这种(声称的)减少的比特数和计算时间使得量子计算机有希望完成这项任务。

蛋白质是一个格子

一个蛋白质的晶格代表了空间中的一个序列,如下图所示,其中的旋转一般用 0 和 1(或者更好的量子位)的序列编码。下面的例子代表了由脯氨酸-丝氨酸-缬氨酸-赖氨酸-甲硫氨酸-丙氨酸(PSVKMA)的氨基酸序列给出的蛋白质,其在空间中可能存在几个折叠。

致谢:作者

量子退火

量子退火机(例如 D-wave 计算机)不同于经典计算机和基于门的量子计算机(例如 IBM)。它们只执行一种计算(通过退火进行优化),而普通计算机和门模型量子计算机运行不同类型的算法。与门模型量子计算机相比,量子退火器更容易构建和放大,它们也没有那么大的灵敏度误差,误差可能会导致一点点次优的解决方案,但它们不会像基于门的量子计算那样破坏整个计算。尽管量子退火器的缺点是它只限于解决优化问题和相关的问题,比如采样。鉴于我们专注于蛋白质折叠预测,退火器是完美的。

但是术语“T1”退火“T2”从何而来?退火的概念来自冶金学。

这是一种众所周知的用于增加金属延展性或增加其导电性的方法:
首先将一块金属加热到高于其再结晶
温度的高温,当超过再结晶温度时,金属的原子点断裂,原子开始四处移动。第二个退火步骤只是让金属冷却到室温,因为这种冷却是以一种缓慢而平稳的方式发生的,当原子冷却下来并停留在它们的位置上时,它们可能会形成一个最佳的能量晶格。

从冶金转到计算机科学,20 世纪 80 年代模拟退火被引入解决优化问题,以受益于减少热振动的想法,如蒙特卡洛优化。从某个初始猜测解开始,反复随机改变候选解的值,希望找到一个最优解。与机器学习中使用的传统梯度下降相比,这已经是一项成就,传统梯度下降只能在凸地形中执行。

你可以把这个过程想象成一个在山峦和山谷中移动的点,它对应于函数值。有时你需要爬上一座山,才能到达山那边的一个山谷。

在 90 年代,日本物理学家提出了一个想法,即使用量子波动而不是这些经典的模拟热波动来驱动退火算法中的这些转变。如下图所示,与可以推动状态越过能量障碍的热波动不同,量子算法不会导致系统越过障碍,而是让它们出现在障碍的另一侧(量子隧道)。

致谢:作者

可能的解决方案(蛋白质折叠的最佳配置)的数量是指数级高的——对于 m 个氨基酸和每个氨基酸位置的 n 种可能性的蛋白质,存在 N^M 可能的配置。考虑到这个问题的本质(为了找到最优解),很明显量子力学对于找到一个合适的算法是必要的。

蛋白质折叠的实用量子退火

在 Qiskit 中,有一种东西叫做量子近似优化算法(QAOA) 在有限或可数无限的潜在解集合中搜索最优解。然而,Qiskit 运行在 IBM 量子计算机上,这些计算机是门,而不是定义上的“退火程序”。

D-Wave 系统是专门设计来通过量子退火寻找解决方案的。此外,人们已经在他们身上测试了著名的罗塞塔打包器的量子版本: QPacker

将我们到目前为止所讨论的内容整合在一起的一般过程总结如下:

  1. 创建一个格子网格,其中每个单元是一个量子位,编码有关氨基酸反应的信息
  2. 对蛋白质进行常规转辅助编码
  3. 基于该编码创建能量函数
  4. 使用量子退火找出能量函数最小的地方

氨基酸之间的键由两个量子位给出,这也定义了蛋白质折叠的方向。例如,指向右侧的键定义为“01”,指向下方的键定义为“00”,依此类推。对于一个有六个氨基酸的蛋白质,它的转折顺序可以表示为“0100101011”

可以通过 D-wave 系统 Leap 实现。通过使用 D-wave SDK,我们可以定义一个求解器,并定义一个氨基酸邻接图。可以按照本视频中的步骤进行配置:

您可以通过 D-wave (1 分钟免费,其余时间付费)或通过 Amazon Braket 请求访问:

第一步是导入必要的库

from dwave.system import DWevoSempler 
from dwave.system.compositos import EmbeddingComposite, FixedEmboddingComposite 
import dwave_networkx as dnx 
from minorminer import finde_embedding 
import dimod 
from sympy.parsing.sympy_pareer import parse_expr
import json
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt

然后,可以使用图形的求解器:

solver = DWaveSampler(solver=”DW_2000Q_5”)solver_G = nx.Graph(solver.edgelist)

前 3 个量子位必须是固定的,以避免同一图形中多个问题的任何冗余构象。

energy_expr_less_00 = preprocess_expr(energy expr.subs({‘q0000’:0, ‘q0001’:1,’q00002',0}), q) 
energy_expr_ot_00_func = lambdify(q[3:],energy_expr_less_00)energy_expr_ot_00 = energy_expr_less_00.as_ordered_terms()

下面的步骤是定义嵌入:

problem = x.Graph()
for k, v in dict(bqm.linear).items(): 
 problem. add_node(k) 
for k, v in dict(bqm.quadratic).items(): 
 problem.add_edge(*k)num_cores 12 
embedding_list = Parallel(n_jobs=num_cores)(delayed(find_embedding) (problem, solver_G) for jobid in range(num_cores))

现在可以求最优解的能量:

bst_00 ={}
for j, qj in enumerate(q):
 bst_00[qj] = results[best,j]smpl_00 = dict(zip(list(bqm_00.variables), [None]*len(bqm_00.variables 
for key in bqm_00.variables:
 if type(key) == str:
 k1, k2 = parse_expr(key).as_coeff_mul()[1]
 smpl_00[key] = bst_00[k1] * bst_00[k2] 
 else:
 ampl_00[key] = bst_00[key]smpl_spin_00= ampl_00.copy() 
for key, val in smp1_00.items(): 
 ampl_spin_00[key] — 2*val -1mene_00= bqm_00.energy(ampl_00) 
mene_spin_00 = bqm_spin_00.energy(empl_spin_00)

该代码将导致具有最低能量的折叠。

结论

蛋白质折叠预测仍然是一个正在进行的研究领域,量子计算的使用仍然是比较新颖的。

仍然需要研究错误折叠的蛋白质,这仍然涉及不完全理解的过程,事实上,在这个方向上关于阿尔茨海默氏病的大多数研究都不太成功。希望未来的研究可以进一步揭示蛋白质是如何错误折叠的,并帮助设计可用于治疗的对接对应物。

参考

Perdomo-Ortiz,Alejandro 等,“通过量子退火发现晶格蛋白质模型的低能构象”科学报告2.1(2012):1–7。

【李,】等,“量子退火与经典机器学习在简化计算生物学问题中的应用” NPJ 量子信息4.1(2018):1–10。

赖尔、马库斯等人“阐明量子计算机上的反应机制”美国国家科学院院刊114.29(2017):7555–7560。

想连接就连接

@ Dr _ Alex _ 克里米

@阿列克里米博士

亚历山德罗·克里米——YouTube

https://web.facebook.com/dralecrimi

量子线性函数

原文:https://towardsdatascience.com/quantum-linear-function-c86985f6118f?source=collection_archive---------32-----------------------

量子机器学习基础

量子机器学习要不要入门?看看用 Python 动手量子机器学习。

让一个平淡的话题变得有趣最简单的方法是什么?

正确!你只要在上面加上“量子”两个字。想看看吗?

线性函数怎么样?它描述了坐标系中的一条线——没什么大不了的。如果帖子是关于线性函数的,我会停止阅读。但是如果我们看看如何创建一个量子线性函数呢?

下图描述了由两个量子位组成的量子线性函数的结果。对于每一步,概率增加一个固定的量,即斜率。

作者图片

在继续用量子线性函数来创建这样的分布之前,让我们回顾一下我们在高中所学的线性函数。

线性函数的形式为 y=f(x)=ax+b。

从图形上看,a 表示图形的斜率。这是当 x 轴上的位置增加 1 时,图形中 y 的变化。b 是截距,即图表与 y 轴相交的点。

作者图片

我们看到的第一个显著区别是,经典线性函数是连续的,而它的量子对应物是离散的。第二是量子线性函数只对两个轴上的正值起作用。

让我们从量子线性函数的概念开始。我们有四个枚举状态。我们坚持二进制数字格式。

我们将每个位置的值分成大小相等的块。因为我们的功能在增加,所以我们每一步增加一个块。所以,我们从零开始。然后,我们在状态 1 添加一个块,在步骤 3 和 4 分别添加另一个块。

作者图片

现在,让我们创建相应的量子电路。我们使用两个量子位。第一个量子位代表状态的右(下)数,第二个量子位代表状态的左(上)数。

我们首先根据上量子位的值来分割状态,并计算块数。当上量子位为 0 时有三个块,当上量子位为 1 时有七个块。

作者图片

于是,我们把上量子位放入状态 1 的概率为 7/10 的状态。因此,它有 3/10 的概率处于状态 0。

接下来,我们只看这些上量子位为 1 的状态。然后,我们拆分这些状态,并计算块数。

然后,我们把较低的量子位放入一个状态,只有在较低的量子位为 0 的情况下,才有 4/7 的概率处于状态 1。

作者图片

我们做同样的事情来划分上量子位处于状态 0 的状态。

下面的清单描述了生成我们在顶部显示的发行版的完整代码。

我们首先从 Qiskit 导入我们使用的函数——这是 IBM quantum 开发工具包。prob_to_angle函数获取一个概率,并返回量子位状态向量的相应角度。这篇文章更详细地解释了这个功能。

然后,我们用两个量子位定义一个QuantumCircuit。电路的主要部分由 RY ( ry)和受控 RY ( cry)门组成。这些将指定的量子位置于对应于指定角度的状态。

我们遵循我们之前描述的步骤。门qc.ry(prob_to_angle(7/10), 1)将上部量子位(在位置 1)置于一种状态,其中角度对应于 7/10 的概率。

只有当位置 1 的量子位为 1 时,门qc.cry(prob_to_angle(4/7), 1, 0)将较低的量子位(在位置 0)置于角度对应于概率 4/7 的状态。

如果上部量子位是 1,门qc.cry(prob_to_angle(2/3), 1, 0)也将调整下部量子位的状态。因此,我们将它封装到 X 个门中,翻转这个量子位的状态 0 和 1 的概率。所以,我们只在上量子位为 0 的情况下,应用代表 2/3 概率的角度。

当您运行代码时,您会得到下面的结果。

作者图片

结论

如果你读过我之前关于多内核发行版的文章,你可能对这种方法很熟悉。量子部分其实也是一样的。但是将值分成块的概念方法是新的。我们将在下一篇文章中推广量子线性函数时利用这种方法。

量子机器学习要不要入门?看看用 Python 动手量子机器学习。

量子机器学习——是时候开始了

原文:https://towardsdatascience.com/quantum-machine-learning-its-time-to-start-now-1f5a1ee9bc31?source=collection_archive---------16-----------------------

图片由来自 Pixabay 的 Gerd Altmann 提供

QML ML 从业者的下一个逻辑步骤

在未来几十年,量子计算将在技术、科学和商业发展中发挥重要作用。随着谷歌声称“量子至上”和对这一主题的指数兴趣,我们离看到许多棘手的问题挑战用量子处理解决的经典计算机不远了。此外,有明确的迹象表明,机器学习和量子计算将在加强彼此的领域中发挥互补作用。

但主要的问题是,为什么现在人们应该深入到像量子机器学习这样看起来复杂而难以学习的复杂主题中。
幸运的是,量子计算和量子机器学习并没有看起来那么可怕。因此,人们可以快速启动,而不必过于担心量子力学中许多令人恐惧的“怪异”函数的复杂性。

一个人为什么要学量子机器学习?

以下是你应该认真考虑投身量子计算(QC)然后投身量子机器学习(QML)的几个理由。

作者图片

  1. 数学极其优雅,用数学理解量子计算很有趣。
  2. 优秀的免费资源和书籍等参考资料可供学习。
  3. 学习曲线非常陡峭,最好现在就开始,因为该领域的研究和进步正在爆炸式增长。随着知识领域呈指数级扩展,晚进入将使旅程更加艰难。参考上图;随着时间的推移,学习曲线会变得更陡。
  4. 社区每天都在变大;因此,当有人陷入困境时,帮助就在身边。
  5. 量子机器每天都在变得强大。任何超级计算机或 GPU/TPU 的任何组合都需要数千年才能实现的复杂计算看起来越来越光明。但是,与此同时,量子计算机将在几分钟内完成。
  6. 许多开源软件开发工具可用于量子计算、模拟,甚至连接到量子计算机进行概念验证。
  7. 早期研究表明,QML 将在许多复杂的机器学习任务中胜过 CML。例如,下图显示量子神经网络可以有更高的有效维度,更低的损耗更快,避开贫瘠的高原

图片来自 YouTube 视频 Amira Abbas

量子机器学习之前应该学什么

作者图片

  1. 复数上的线性代数
  2. 基础概率和数论
  3. 傅立叶变换及其量子版本
  4. Python 将是一个不错的选择,因为 python 中有许多量子计算开源库和框架。
  5. 经典机器学习将是理解许多等效或高级 QML 算法的先决条件。
  6. 量子计算的关键概念,算法和使用 python 编程的算法实现将为理解 QML 提供必要的基础。

一个人应该如何学习量子机器学习?

线性代数:

大部分量子计算书都涉及复向量空间中的线性代数。在这个过程中,我主要发现了迈克尔·洛切夫的一本书非常有帮助,因为它有助于建立自下而上的知识。我强烈建议保留这个作为参考。如果哪个题目听起来很复杂,可以回过头从基本定理去理解,然后明白症结所在。这本书还有一个配套的 YouTube 视频,以好莱坞风格呈现。最后,Qiskit 有一本在线教材,也是对必修的线性代数的极好介绍。来自 Springer 的用于量子计算的线性代数包含了理解量子计算所需的线性代数的关键领域的简明摘要。你也可以试试下面的 YouTube 视频。

布兰特·卡尔森的 YouTube 视频

概率和数论

我不会在这里详述,因为关于这些主题有很多资料。

傅里叶变换

我再次推荐阅读 Michael Loceff 的书,这本书非常全面,从傅立叶级数、离散和快速傅立叶变换到量子傅立叶变换。YouTube 上也有很多关于这个话题的资料。

经典机器学习

人们必须非常熟悉经典的机器学习理论、算法、不同的库和实现。媒体、MOOC、YouTube 视频等提供了大量材料。

量子计算

《量子计算圣经》是 Nielsen 和 Chaung 写的一本书。即使过了二十年,它仍然是关于这个主题的最好的书,尽管许多部分现在有点过时了。

https://www.amazon.com/Quantum-Computation-Information-Cambridge-Sciences/dp/0521635039

Loceff 书还涵盖了基本的量子计算概念和强大的算法。我还会推荐几门课程,比如 Edx 的 Umesh Vazirani 课程(你也可以在 YouTube 上找到视频)。

https://www.edx.org/course/quantum-mechanics-and-quantum-computation

我还会推荐 YouTube 上 John Preskill 的视频课程内容。

约翰·普雷斯基尔的 Youtube 视频

请参考下面的页面,里面有视频、书籍、MOOC、博客等精选列表。,用于量子计算。

https://qosf.org/learn_quantum/#massive-open-online-courses

https://github.com/desireevl/awesome-quantum-computing

Python 库

像 IBM、Google 这样的行业巨头支持许多软件工具和开发库。在这里,你可以找到一份全面的清单和详细信息。我发现 Qiskit 是所有软件中最受欢迎的,并且易于使用,但是像所有其他的软件库一样,观点不同,你可能喜欢 Google 的 Cirq 或者 Rigetti 的 PyQuil。Qiskit 有很多内容,包括一本教材和 YouTube 频道,很多很棒的视频。

图片来自 YouTube,作者 Qiskit

量子机器学习

最后,我们在量子机器学习领域。我们正处于 NISQ(嘈杂的中间尺度量子)时代,理解我们在短期内可以用不完美的量子机器做什么很重要。

Edx 提供了一个 MOOC,其中有量子计算和量子增强机器学习的实践介绍,以及代码报告

来自 YouTube 的量子机器学习 MOOC 视频

有两本书是关于 QML 的,但我发现没有一本真正有价值,因为这两本书都缺乏 QML 地区的深度,而且大部分时间都在解释 QC。

  • 量子机器学习:量子计算对数据挖掘意味着什么,作者彼得·魏特克
  • 玛丽亚·舒尔德用量子计算机监督学习

我还会推荐参考来自 Penny Lane 的 QML 内容和库。这是因为 PennyLane 支持所有主要的量子框架。

图片来自 PennyLane

这个 Github 库有一个完整的 QML 算法列表。

用于机器学习的 Qiskit 页面也包含有价值的实现信息。

我为 QML 创建了一个 YouTube 播放列表,在这个播放列表中,你会发现 YouTube 上大量免费的 QML 内容。

一个重要的建议是,在你熟悉基本概念和符号之前,不要进入研究论文,否则你会发现很难通读论文。

最后一个音符

QC 和 QML 都是蓬勃发展的领域,有很多炒作,希望,有时还有不合理的期望。我试着分享我的经历和我到目前为止的旅程。如果您有任何建议或遇到任何其他可以帮助我们蓬勃发展的社区的内容或信息,我希望收到您的来信。

感谢阅读。你可以在 LinkedIn 上联系我。

只需每月 5 美元,就可以无限制地获取最鼓舞人心的内容…点击下面的链接,成为媒体会员,支持我的写作。谢谢大家!
https://baijayanta.medium.com/membership

你可能也想看看下面这篇关于量子数据编码的文章。

https://baijayanta.medium.com/all-about-data-encoding-for-quantum-machine-learning-2a7344b1dfef

量子正态分布

原文:https://towardsdatascience.com/quantum-normal-distribution-cc6710c27879?source=collection_archive---------23-----------------------

量子机器学习算法的构建模块

量子机器学习要不要入门?看看 动手量子机器学习用 Python

“我的女王,农民没有量子正态分布!”——“让他们使用量子伯努利分布。”
“此后不久无头分析”的首席数据科学家玛丽·安托瓦内特

作者图片

发行版是任何数据科学家或机器学习工程师的面包和黄油。我们每天都在使用它们。没有他们我们无法生存。不像计算机科学家可以奢侈地使用定值变量,我们得到的只是统计描述。话说回来,用分布来表示变量是很有表现力的。它明确了我们信念的概率性质。

所以,我们不要抱怨太多。幸运的是,许多变量来自众所周知的分布。这让我们可以重用现有的函数来创建和处理这样的变量。

例如,高斯正态分布是最常见的分布之一。你到处都能看到它。血压、身高和智商得分遵循正态分布。它符合许多自然现象,因为它在平均值处有峰值,向两端变平。换句话说,更有可能看到接近平均值的值,而不是离群值。

作者图片

有两个参数用于自定义正态分布。这是它的平均𝜇和标准差𝜎.您可以通过密度函数计算曲线上任意一点𝑥的密度

如果你观察单个量子位,你会发现它遵循伯努利分布。如果你使用多个量子位,你也可以创建一个多量子分布。当然,对于离散正态分布是一般多正态分布的特例,我们可以用同样的方法创建它。但是创建多内核发行版相当麻烦。当然,这是因为它没有利用正态分布的任何特性。

那么,让我们来看看如何模拟一个量子正态分布。像往常一样,我们使用 IBM 的量子开发 SDK Qiskit。Qiskit 附带了相当多的实用功能。毫不奇怪,有一个函数为我们创建了一个正态分布。这是NormalDistribution

尽管NormalDistribution仍然是 Qiskit 主包的一部分,但它已经被弃用了。因此,我们从它的新位置导入它。这需要我们安装 Qiskit 的财务包。

现在,让我们看看这个函数能做什么。

正态分布函数为我们创造了一个完整的量子电路。我们必须提供量子位的数量作为参数。在这种情况下,我们使用三个量子位。

作者图片

此外,我们可以指定正态分布的常用参数𝜇 ( mu)和𝜎 ( sigma)。我们必须注意,参数sigma等于方差,𝜎,而不是标准差。

下图描述了该电路的输出。

作者图片

它看起来并不像正态分布,不是吗?这有一个简单的原因。函数NormalDistribution只表示-1 到 1 范围内的分布。因为我们将平均值(𝜇)设置为 1,所以我们在该范围的右侧看到了分布的峰值。但是,我们可以在分布的左下方看到更多细节。

你可能会奇怪为什么峰值的绝对值和以前不一样了。当然,我们为sigma指定了一个值。但是,实际上,我们把它设置为 1。这也是默认值。所以,我们什么都没改变。然而,我们的量子系统的所有概率的总和必须是 1。因此,我们不能看到绝对值,也不能在不同的分布之间进行比较,而只能看到分布中心和边缘值之间的相对差异。

在开始测试musigma的不同值之前,让我们看看可以放入函数的另一个参数。Qiskit 还允许我们指定分布的界限。

当我们指定一个比缺省值更大的值时,我们可以更清楚地看到正态分布的形状。

作者图片

只剩下一个问题:如果我想在其他门旁边使用正态分布怎么办?

答案是将正态分布函数创建的电路变成一个定制的量子门。然后,你可以在另一个量子电路中使用这个门,它可以做任何你想做的事情。

我们从我们的QuantumCircuit的定义开始。这一次,我们指定它有四个量子位和一个经典位。我们创建另一个电路(ndc)来保存我们的NormalDistribution。然后,我们将ndc附加到qc上,我们就完成了。然后,我们可以处理组成正态分布的量子位,就像它们是任何其他量子位一样。

在这种情况下,只有当我们将其他量子位中的一个测量为 1 时,我们才翻转最后一个量子位(不属于正态分布的一个)。

最后,我们测量这最后一个量子位,并将结果放入经典位。

作者图片

下图描述了该电路的示意图。

作者图片

我们看到正态分布像任何其他多量子位门一样工作。

量子机器学习要不要入门?看看 动手量子机器学习用 Python

在这里免费获得前三章。

量子相位反冲

原文:https://towardsdatascience.com/quantum-phase-kickback-bb83d976a448?source=collection_archive---------6-----------------------

我告诉你的是真的…从某种角度来看

本帖是本书的一部分: 动手用 Python 学习量子机器

量子纠缠是量子力学的惊人特征之一。两个纠缠的粒子共享一个叠加态——不管它们相距多远。

从实用的角度来看,我们可以利用纠缠让一个量子比特控制另一个量子比特的状态。例如,受控非门(CNOT-或 CX-gate)只有在控制量子位处于|1⟩.状态时,才会切换目标量子位的振幅如果控制量子位处于|0⟩.状态,什么也不会发生

这种受控量子门让我们能够精确地操纵多量子位系统。在这篇文章中,我们使用纠缠来创建一个细粒度的概率系统。

受控量子门的另一个实际特性是,它们不会触及控制量子位。

下图描述了 CNOT 门的真值表。

作者弗兰克·齐克特的图片

无论我们将哪种量子位值组合输入运算,控制量子位都不会改变。

图片由作者提供,灵感来自星球大战

当我们处理量子位元时,有一个实际可行的量子转换门概念是最重要的。然而,每隔一段时间,我们需要记住量子操作本质上是一种物理操作。CNOT 门也是。

对于物理学中的每一个作用,都有一个相反的反应。因此,我们甚至应该怀疑 CNOT 门的明显片面性。

每当我们在量子计算中挑战我们的直觉时,咨询基础数学是一个好主意。

CNOT 门是一个两量子比特的门。因此,它转换量子位状态,我们用一个四维向量来表示它的状态。

因此,CNOT 门具有 4×4 的变换矩阵。

如果控制量子位(在狄拉克符号中的左边位置)在|0⟩态,就像在|00⟩和|01⟩.态一样,则没有影响

但是如果控制量子位处于|1⟩状态,那么控制量子位就从|0⟩转换到|1⟩,反之亦然。

数学证实了我们的直觉。

当我们用数学公式描述量子态和量子操作时,我们使用|0⟩和|1⟩矢量作为基础。|0⟩和|1⟩表示标准或计算基础状态。这些状态对应于我们在观察量子位时可能获得的测量值。我们绝对确定地测量|0⟩态的一个量子位为0。因此,我们测量|1⟩态的一个量子位为1。虽然{|0⟩,|1⟩}基础便于数学处理,但它只是基础物理的一种表示。

就像有一个控制量子位嵌入了关于量子位状态的偏见,让我们认为操作是单方面的,我们选择的数学基础导致了 CNOT 变换的特定表示。但这不是唯一可能的表示。事实上,还有无限多其他可能的选择。我们的量子位并不局限于这两种状态。量子位可以是两种状态的叠加。如果你不熟悉量子叠加,这篇文章提供了一个介绍。

考虑一下布洛赫球。归根结底,它是一个球体——完全对称,没有一个点比其他任何点更特殊,也没有一条轴比其他任何点更特殊。标准基础也不是特别特别。

下图描述了两个基本状态|0⟩和|1⟩.

作者弗兰克·齐克特的图片

但是我们的两个量子位也可以处于任何其他状态。例如,有几个州

那是在基态上应用哈达玛门的结果。下图描述了这两种状态。

作者弗兰克·齐克特的图片

在数学上,下面的矩阵代表了哈达玛门在两个量子位上的应用。

所以,如果我们把这个矩阵应用在|00⟩态的两个量子比特上,它们会出现在|++⟩.态

输入州|01⟩产生州|+−⟩.

输入州|10⟩产生州|−+⟩.

最后,如果我们在|11⟩态的两个量子位上应用这个变换,我们把它们放入|−−⟩.态

让我们重温一下矩阵向量乘法。当我们将一个矩阵乘以一个列向量(我们的量子态)时,结果是另一个列向量,就像这样:

对于矩阵的每一行,我们将该行中的每个值(列)乘以向量的第 x 个值。如果向量中除了一个值之外的所有值都是 0,而这个值是 1,那么 1 的位置表示我们最终得到的矩阵的列。

现在,让我们把 CNOT 门应用在叠加态的量子位上。我们可以通过将 CNOT-gate 和 H⊗H 变换的矩阵相乘来计算总的变换矩阵。

眼尖的读者可能会注意到,非门切换了 H⊗H-matrix.的第二列和第四列

当我们将这种变换应用于基态的四种组合时,我们可以看到一个有趣的模式。

实际上,如果目标量子位(在右手边)处于|1⟩状态,控制量子位(在左手边)的状态就从|+⟩翻转到|−⟩,反之亦然。

简而言之,我们可以说:

|+⟩和|−⟩这两个州具有与|0⟩和|1⟩.相同的测量概率它们以 0.5 的概率产生任一值。因此,CNOT 门没有任何直接可测量的含义。然而,控制量子位改变了它的相位。它呈现出受控量子位的相位。

对于目标量子位的相位被踢到控制量子位,我们称这种现象为相位反冲

让我们回到实践中来。以下代码绘制了|+−⟩.的布洛赫球体

作者弗兰克·齐克特的图片

接下来,我们对这个状态应用 CNOT 门,位置 1 的量子位是控制量子位,位置 1 的量子位是目标。

作者弗兰克·齐克特的图片

根据数学,不是目标量子位,而是在位置 0 的控制量子位切换其相位。再来看看另一种情况。

下面的电路通过使用 RZ 门在两个量子位上应用哈达玛门,并在量子位 1 上应用相移。类似于围绕 Y 轴旋转量子位状态向量的 RY 门,RZ 门围绕 Z 轴旋转量子位状态向量,因此应用一个相位。

作者弗兰克·齐克特的图片

我们看到位置 0 的量子位位于 X 轴上。位置 1 的量子位位于 Y 轴上。我们也来看看这个两量子位系统的四个态的相对相位。

作者弗兰克·齐克特的图片

我们看到量子位 1(在 Qiskit 中,量子位位置从右向左读取)处于|1⟩.状态的那些状态的相移下一步,我们应用 CNOT 门,量子位 0 作为控制量子位,量子位 1 作为目标。

作者弗兰克·齐克特的图片

首先要注意的是相对相位。状态|00⟩和|11⟩处于与状态|01⟩和|10⟩.不同的相同阶段虽然对于控制量子位(Qiskit 中的右手量子位)是|1⟩的状态,相位似乎翻转了,但实际上对于目标量子位是|1⟩.的状态,相位翻转了由于相位是相对的,我们无法分辨哪个相位是原始的,哪个相位是移动的。

但更重要的是要注意转变的程度。颜色表示π/2 的相对位移。这是我们在量子位 1 上应用的相位。因此,当目标量子位是|+⟩或|−⟩.时,CNOT-gate 并不总是翻转半个电路的相位但是“非门”颠倒了|01⟩和|11⟩的状态。实际上,它将目标量子位的相位应用于控制量子位。

结论

通常,发展一种量子态和操作的非数学直觉是有好处的。尽管如此,我们总是需要考虑到我们正在处理一个量子力学系统。量子力学常常是反直觉的。我们可以用数学补充我们的直觉。但是数学也不是没有陷阱。

从经典计算和为了数学的方便,我们经常依赖于标准的基本向量|0⟩和|1⟩.但是当处理量子位时,我们需要记住它们并不局限于这些状态,而是可以处于叠加态。

我们知道 CNOT 门不是单方面的行动。它显然有可能影响控制量子位的状态。即使相位不可直接测量,也有办法利用状态之间的相位差异。事实上,突出的算法,如格罗弗的搜索算法,利用了这一效应。我们将来也会。

本帖是本书的一部分: 用 Python 动手做量子机器学习

在这里免费获得前三章。

量子编程——抽象层次 1:逻辑门

原文:https://towardsdatascience.com/quantum-programming-abstraction-level-1-logic-gates-486f941e831a?source=collection_archive---------25-----------------------

在量子计算机编程的大部分时间里,我们会考虑量子比特的叠加态。现在对我们的直觉有用的是,有一种方法可以将叠加可视化,这样我们就可以分析每个逻辑门对叠加的影响。

图片作者

  1. 从量子力学中抽象出我们自己的量子编程:抽象层次 0
  2. 量子编程——抽象层次 1:逻辑门 (this)

我们之前已经计算出量子计算机对量子位进行逻辑运算,量子位是经典位的量子对应物。我们已经知道,当我们对一个量子位进行操作时,它可以保持在叠加态,然后我们可以在测量时将它折叠到一个确定的状态。因此,在我们花在量子计算机编程上的大部分时间里,我们会考虑量子比特的叠加态。

现在对我们的直觉非常有用的是,有一种方法来观想这种叠加。进入布洛赫球。这个工具允许我们用一个(非常简单的)几何形状——单位球来表示单个量子位的整个状态空间。就当是三角学里的单位圆吧。

国家|ψ⟩更有可能崩溃到|0⟩,而不是|1⟩

让我们讨论我们感兴趣的事情。球体的两极(沿着 z 轴)我们任意定义为量子位在北极处于 0 状态,在南极处于 1 状态。量子位的当前叠加状态由黑色矢量∣ ψ ⟩.定义我们当前的状态是这样的叠加,它更接近 o 态,而不是 1 态。这仅仅意味着,当我们测量这个量子位时,我们将更有可能观察到它坍缩成 0,而不是 1。

现在,令人困惑的是,这是一个三维球体,然而我们的状态只能坍缩成一个一维数字——1 或 0。为什么我们甚至需要一个球体,为什么不只是一条线,一端是∣0⟩,另一端是∣1⟩?长话短说,我们的州实际上是由一个二维向量定义的,例如∣1⟩州,

二维向量中的分量(称为复振幅)实际上是复数,例如

总之,这需要 4 个坐标来进行几何描述(每个复数 2 个坐标),但出于实际原因我们可以忽略其中一个维度,所以我们剩下 3 个,我们可以将其映射到布洛赫球。

此外,如果我们仔细观察,我们会注意到我们只是用两种不同的方式写了两个相同的状态。这允许我们(为了我们建立直觉的目的)忽略复杂组件的虚部,因此我们可以暂时去掉一个维度,而留下一个圆。告诉过你,你可以想象单位圆。

为了展示坍缩的可能性,我们现在可以去掉第三维

X 门

那么,为什么我们最终需要一个圆,而不是一条线呢?

X 门相当于经典的非门——它否定了我们量子位的当前值。但它是通过交换状态向量的两个分量来实现的。例如,如果我们将它应用于 0 的确定状态

我们得到了 1 的确定状态。或者当作用于叠加态时,

当应用到我们的圈子时,看起来是这样的

新状态现在与旧状态相反——其向量的两个分量被交换了。

因此,在我们的例子中,应用 x 门后,我们的状态向量有相同的概率坍缩为 ∣1⟩ ,就像它之前坍缩为 ∣0⟩ (状态被否定)。

但是为什么我们最终需要一个圆,而不是一条线呢?

请注意,我们也有一个 Y 门和一个 Z 门,它们做同样的事情,但相对于其他两个坐标翻转向量的位置。让我们以 Z 门为例来看看。

Z 门没有改变我们量子位的概率

在我们的特定设置中,通过应用 Z-Gate(围绕 z 轴的否定)没有改变我们量子位的概率分布!但是为了表示量子位实际上发生了什么,我们需要圆提供给我们的额外维度,相对于简单的一条线。

我们说两个状态具有相同的概率分布,但是不同的状态向量仅在相位上不同。

哈达玛门

这是一个非常重要的门,从根本上说是量子门,在经典计算中没有对应的门。你可能已经猜到了,H 门是把一个量子比特从一个确定的状态放到一个叠加态的门。假设我们有一个量子位,有百分之百的几率为零。当我们应用 H 门时,它会把它变成 50/50 叠加态。

哈达玛门把一个确定的 0 态变成了叠加态。现在,它坍缩成两种状态的概率相等。

Rz 大门

这是一扇我想让你注意的门。这是围绕 z 轴门的旋转。它有两个优点——首先,它是一个参数化的门。这意味着它需要一个值,即某个角度,用来旋转向量。第二,我想说的是,这是世界上最具多样性的大门。

想想看,X-Gate 和 Hadamard gate 都做一件它们都不独特的事情——它们将状态向量旋转到球体上的某个位置。Rz 门正是这样做的,但它是参数化的——它将矢量旋转到我们希望它旋转的任何角度。当然,因为我们有 3 个维度,我们可以围绕这些轴旋转,以充分利用向量的能力。例如,阿达玛门实际上是绕着 y 轴旋转 90 度或 π/2 弧度,然后绕着 x 轴旋转 180 度或 π 弧度。X 门是绕 z 轴旋转 π 。所以可以说,

所有的门都是绕布洛赫球旋转的线性组合的特例。

围绕 z 轴将名为|+⟩的 50/50 状态旋转π弧度会产生相反的 50/50 状态|−⟩

I 门

如果你学过计算机科学或线性代数,你会知道存在所谓的身份门(矩阵)。这个操作符什么也不做,它保持状态向量不变。它的两个主要目的是

  1. 证明 X 门是它自己的逆。
  2. 用于指定“不做”操作,即不操作

其余可以作用于一个量子位的门要么是旋转门的特例,要么是作用于一个量子位的最普遍的门——通用 U 门。

现在让我们来看一个门作用于两个量子位的例子,即

CNOT 门

CNOT 门以两个量子位作为参数,一个作为控制 q0 ,一个作为目标 q1 。该门所做的是有条件地对目标量子位执行一个 X-Gate (否定(沿 x 轴)),如果控制处于∣1⟩.状态

当我们的量子位处于确定的状态(推而广之,如果我们在经典计算机上这么做),通过真值表来掌握这一点变得非常简单

但是如果我们有叠加,让我们提醒自己两件事:

  1. 在我们的量子电路中,当一个量子比特处于叠加态时,它同时处于 0 和 1 状态。
  2. 当我们改变 1 个量子比特的概率分布时,我们最终改变了整个量子比特系统的状态的概率分布。

这告诉我们什么?嗯,

  1. 如果说控制位处于叠加状态,那么它将同时处于 1 和 0 状态。
  2. 所以对于整个系统的某些状态,它将是 1。
  3. 在这些系统状态中,根据 CNOT 门的定义,目标位将被 X 门否定。
  4. 整个系统的概率分布会发生变化。

这就是仅仅通过想象来理解逻辑变得令人难以置信的困难的地方。因此,让我们分析几种使用叠加态 CNOT 的情况。

控制和目标都被 Hadamard-ed

正如我们在 Rz 门部分看到的,在我们的圈里,我们可以有两个位置,在那里量子比特有 50/50 的几率坍缩。我们将这两个状态指定为指向 x 的∣+⟩和指向 -x 的∣−⟩。因此,我们可以将我们的量子位初始化为它们中的任何一个,然后使用 q0 作为控制,使用 q1 作为目标,对它们应用 CNOT 门。经过相当多的计算,我们将看到下面的真值表

如果我们仔细观察,我们会注意到一些意想不到的事情。记住,在 CNOT 门中,控制位应该保持不变,而目标位应该随时翻转。但在我们的场景中,目标量子位始终保持不变,但控制量子位会根据目标位的状态翻转。

突然控制目标位的感觉颠倒了。

这个事实可以进一步扩展,并在构建量子算法时使用。突然新的现象开始出现,如 CNOT 电路同一性相位反冲。我们将在下一次更进一步的抽象中探索这些。

建立一个贝尔州

当谈到量子位的物理学时,还有一个更奇怪的现象需要探索,它直接来自量子力学——纠缠。

让我们取一个两个量子位的纯确定状态,使得∣00⟩,或者换句话说,两个量子位都绝对为零。我们可以把它写成一个全局状态向量

或者更清楚地说,在 probabilities⁴的一张桌子上

如果我们对控制位应用哈达玛门,使其进入∣+⟩状态,我们得到

我们可以理解为“ q0 处于叠加状态, q1 处于确定状态 0”。在这种情况下,我们的系统概率表如下所示

而且我们还可以分别对每个量子位进行推理,即 q1 不可能是 1, q0 两者都可以。

最后,将 CNOT 应用于目标位 q1 ,其仍处于∣0⟩.状态状态向量变成了

或者用我们可读性更强的概率表

我们可以看到,这个系统现在有 50%的几率是 00,50%的几率是 11。

但是,如果我们试图通过将状态向量分裂成两个独立量子位的状态向量来解读这一点,我们将会惊讶得不愉快。

  • 假设我们看状态 00。它有 50%的几率,这意味着如果我们只取 q1 ,我们会期望它自己至少有一些概率为 0。
  • 但是如果我们看状态 01,它有 0%的机会。这可能暗示我们认为Q0不可能是 1,因此 01 是不可能的。
  • 但是状态 11 也有 50%的概率。所以Q0T10 可以是 1。

这不再有意义(因为概率加起来不等于 1),并且具有有趣的含义。我们不能再认为这两个量子位是分离的。衡量一个会影响另一个。

这里只有一种解释——我们已经成功地将粒子纠缠在一起。如果我们只测量 q0 ,并且它显示为零,我们将确定 q1 也为零。

纠缠是我们需要解决的最后一个纯量子概念。现在我们已经建立了一个抽象机器,它允许我们同时操纵单个和多个量子位,并开始看到处理多个量子位如何对我们的计算技术产生极其怪异的影响。从现在开始,我们可以专注于利用这种怪异来构建下一个抽象层次,从而创建实际的代码——电路和算法。所以下次让我们这样做吧!

  1. 我想这里需要注意的是,我们上次讲了很多的概率是向量分量(复振幅)的平方。举个例子,如果我们的州是

那么量子位有(sqrt(3)/2) = 3/4 的几率坍缩为 0,有(-1/2) = 1/4 的几率坍缩为 1。当我们把它们加起来,我们得到 1,这是我们对概率的预期。

2.此外,状态由向量表示并不是巧合。我们在量子位上可以拥有的所有门实际上都是线性算子,我们可以用矩阵来表示。你永远不会知道太多的线性代数…

3.整个量子位电路的系统状态向量是通过将所有系统状态的概率放在一个 4D 矩阵中创建的。另一方面,使用每个量子位的状态向量的张量积来计算多个量子位的集体状态。

4.这两种符号实际上显示的是相同的东西,只是状态向量显示了系统状态的复振幅,而概率表对其求平方以获得与该系统状态相关的概率

其中 a 是两个量子位 q1,q0 的每个状态的复振幅。

原载于 2021 年 3 月 28 日 https://kblagoev.comhttps://kblagoev.com/blog/quantum-programming-abstraction-level-1-logic-gates/

量子编程-抽象层次 2:状态机和算法

原文:https://towardsdatascience.com/quantum-programming-abstraction-level-2-state-machine-and-algorithms-6afae40a20f?source=collection_archive---------30-----------------------

揭开相位反冲的神秘面纱,定义一个状态机,并重新创建一个实际的量子算法

在我们对相位反冲有了直觉之后,我们开始进入实际的计算机科学,通过建立一个状态机,并重新创建一个实际的量子算法。

作者图片

  1. 从量子力学中抽象出我们自己的量子编程:抽象层次 0
  2. 量子编程——抽象层 1:逻辑门
  3. 量子编程——抽象层 2:状态机和算法 (this)

到目前为止,我们一直在冒险。让我们回顾一下我们的进展。

  • 我们已经证明,通过将信息叠加编码到 n 个量子位上,我们只需改变一个量子位的状态,就可以操纵整个 2^n 态系统。
  • 我们用一个简单的 2D 向量来表示每个量子位的状态。
  • 我们已经确定,我们可以通过应用不同的逻辑门来改变单个(或两个)量子位的状态,类似于经典逻辑门。
  • 我们现在可以看到布洛赫球上量子位(实际上是整个系统)的叠加状态。
  • 我们知道如何纠缠两个量子位,使它们的状态相关。

现在是时候开始把所有这些放在一起了。把一些门串在一起,看看会发生什么,这不是很好吗?

好吧,为了看到发生了什么,我们可能希望能够可视化我们量子位的状态流,这样我们就可以看到每个门如何在每一步操纵每个量子位。

单位圆状态机

如果你还记得,在上一篇文章中,我花了相当多的时间试图将 Bloch 球简化成一个圆。对你们中的一些人来说,这似乎是一个没有意义的练习;毕竟,一个球体并不比一个圆形更复杂。但是,在这里它变得更有意义。我向你介绍 u nit 循环状态机:

现在我们可以把所有的状态从“布洛赫圈”放到一个单位圆中,这个单位圆是由……嗯,个状态组成的。我认为在这种特殊情况下,圆比球体更容易管理,特别是因为你可能会注意到,我们的状态是布洛赫圆的两倍。例如,我们现在不仅有(1 0)状态,还有(-1 0)。

这些是我们先前描述的具有不同的相位的状态。事实上,甚至更多,因为如果你还记得,在布洛赫循环中,我们已经有了状态[1/sqrt(2) 1/sqrt(2) ],它与[1/sqrt(2)1/sqrt(2)]相同,但相位不同。

这些只相差一个相位的状态仍然有相同的概率分布。只要我们只谈论单个量子位,它们就互为等价。但是我们很快就会看到,一旦我们的系统中有超过 1 个量子位,相位就变得非常重要。

先不说这个,让我们专注于我们新的强大工具。有了它,我们可以很容易地描述每个 门如何作用于每个状态。例如,让我们将 X 门和哈达玛门映射到它们各自的圆上:

通过应用 X 门来改变可能的状态。

通过应用哈达玛门可能的状态改变。

根据定义,我们的量子门是可逆的,我们可以在圆圈中来回移动,而不会丢失先前状态的信息。

量子电路

现在我们有了必要的工具和知识来开始组装这些门。我们寄存器中量子位的集合,加上我们应用于它们的门的组合,我们可以称之为量子电路

通过定义我们的状态机,我们可以看到我们系统的程序流程。我们可以从创建一个 1 量子位的简单量子电路和几个应用于它的门开始。

因此,通过我们的状态机,我们能够确定,在将门应用于量子位之后,它将以(-1) =1 的概率变成∣0⟩。但是那个负号是什么?

正如我们之前所讨论的,[1 0]和[1 0]是彼此相差某个相位的状态。由于我们的系统中只有一个量子位,我们可以说这两个状态相差一个全局相位。那种类型的相位在我们的系统中没有意义。

但是如果在我们的系统中有多个量子位,并且它们彼此具有不同的相位 w.r.t .,那么我们说是它们的相对相位 不同。

相对相位实际上是系统的一个属性,即具有不同相对相位的系统是物理上不同的系统。

相位反冲

还记得我们说过的将 CNOT 门应用于一对概率分布为 50/50 的量子位吗?我们发现,不是控制位改变了目标位,而是目标位翻转了控制位。

但最终这种变化并没有改变这些量子位的概率。两者都仍有 50%的概率坍缩为 1 或 0。改变的是它们的相对相位。

事实上发生的是,目标位的相位被应用到已经存在的控制位的相位上我们只需遵循上表中的状态符号,就可以很容易地看出其中的逻辑。

当目标位的相位为“+”时,它不会改变控制位的相位,因为

因此相位保持不变(见黑色行)。但是一旦目标位的相位为“-”,它的影响就变得明显了(见红色行),例如

这种从目标位到控制位的相位应用就是我们所说的相位反冲。

虽然不是算法,相位反冲是量子计算中的一个重要工具。例如,它允许我们跟踪另一个量子位的相位,可以说是复制相位。这种将一个量子位的相位复制到另一个量子位上的机制经常用于不同的算法中。

让我们在状态机中想象一下这个相位反冲。

CNOT 将目标位的混合状态相对相位应用于控制位的纯状态相对相位,最终将相位从 q1 复制到 q0。在应用最后的 Hadamard 门之后,系统继续崩溃到确定状态 11。

让我们打破这个,并解释我们的意图。

  • 首先,我们将 X-Gate 应用于目标位 q1

这里的想法是,通过将哈达玛门应用于状态 1 的量子位,我们将使它与负相位叠加。**

  • 相比之下,我们的控制位 q0 从 0 状态转换为叠加状态,因此为正相位。

这是为我们的实验做准备。下一步是我们的主要工作。

  • 通过在我们的控制位和目标位上应用 CNOT 门,我们实现了我们设定的目标——将目标位 q1 的相位应用到控制位的相位上。

并且由于 q0 的相位是正的,结果最终是简单地复制q1q0 的相位。这正是上面表格第二行中发生的情况。

  • 然后,我们再次对两个量子位应用哈达玛门,将它们从 50/50 的概率转换回确定的状态(记住,门是可逆的,所以哈达玛现在有相反的效果)。

就这样,我们演示了量子计算的临界性质,名为相位反冲。**

算法

算法是有特定目标的结构。它们的存在是为了解决一个问题,通常是在计算机科学本身之外的领域。事实上,算法通常是在能够运行它们的机器存在之前就被开发出来了。

算法通常是计算中真正的第一步,在这一步中,我们可以真正从物理现实中抽象出来,并开始应用更加程序化的思维模式。

此外,这是一个抽象层次,在这个层次上,我们可以定义后台程序,在这些后台程序中,流程可以在我们不关心细节的情况下发生,我们只能看到功能的结果(也许还有输入)。

因此,在量子编程中存在高度人为的问题,这些问题可以用我们的量子位来解决。我将向您展示一个被过度使用的例子,顺便提一下,我们一直在自然地构建这个例子。让我们来谈谈

多伊奇算法

正如我所说,这是一个真正的入门级算法。它通常是关于这个主题的任何书籍或课程中第一个展示的,人们可能会认为这只是因为它很容易展示。然而在现实中,它有两个重要的特征,不管它是否简单或如何做作,都应该知道:

  • 它表明,即使使用可逆门,我们仍然可以对我们的量子位应用不可逆函数。这样我们加强了量子计算和经典计算之间的联系。
  • 这是最简单的算法,它可以不可否认地表明,对于存在的问题,量子计算本质上比经典计算更快——更快的定义是需要更少的步骤来完成。

但是即使这些事情是真的,我现在也不想集中精力在这些事情上。我们的目标只是将我们目前收集到的片段连接成一个单一的流,并将其应用于一个易于理解的问题。

问题是

有一个函数 f01 为输入,以 01 为输出。

因此,该功能有 4 种可能的配置:

  • 作为输入的 0 和 1 都映射到作为输出的 1
  • 作为输入的 0 和 1 都映射到作为输出的 0
  • 输入不变,所以 0 到 0 和 1 到 1
  • 输入被翻转,所以 0 到 1 和 1 到 0

我们将把前两个函数称为“常数”(即我们输入什么并不重要,输出是相同的),把后两个函数称为“平衡的”。

因此问题是:如果我们把这个函数放在一个 黑盒 中,确定两个变量中的哪一个——常数或平衡,在黑盒中。

用闸门再现功能

让我们编程吧!是的,我们现在有简单的逻辑要求,我们必须用我们学过的语言来表达。这难道不令人兴奋吗?

无论如何,我们有一个紧迫的问题要处理。注意,常量功能是不可逆的。我们无法知道输入和输出。如我所说,我无意解释这背后的推理,但是:

如果我们通常需要将一个比特传递到 4 个函数中的任何一个,我们将需要 2 个量子比特来完成可逆门的同样工作。

也就是说,除了函数的输入之外,我们还需要传递一个空的(零)量子位来写入函数的输出。我们将这个空位称为备用位。

记住这一点,让我们重新创建四个功能配置。

该算法

好吧,我想让我们先定义它,就好像我们知道发生了什么,然后我们用单位圆状态机一步一步来,看看会发生什么。我声称,如果黑盒包含一个常量函数(来自上面的列表),下面的算法将吐出∣11⟩,如果它是一个平衡函数,输出将是∣01⟩.

多伊奇算法。BB 代表“黑盒”,我们的功能在这里执行。m 代表测量——这就是我们瓦解量子位系统波函数的地方。

预处理

首先,我们来看看量子位的初始准备,求反和哈达玛初始化。

到目前为止没什么令人惊讶的。现在让我们看看每个黑盒函数是如何改变输出的。

含量 0

我们将常数为 0 的函数定义为什么都不做。所以我们什么也不做,之后我们应用第二个哈达玛门,用测量门折叠波函数。

结果确实是∣11⟩.太好了!我们抓到一个。到下一个。

常数 1

我们将常量-1 定义为翻转备用位。在此之后,我们再次应用哈达玛门,然后测量系统。

我们又一次得到了∣11⟩!(记住,-1 =1 的概率得到 1)棒极了,半途而废。

身份

这就是我们应用相位反冲知识的地方。CNOT 门的行为与我们在上一节的例子中定义的完全一样。看我们的知识是怎么积累的?

正如我们预测的那样,结果就是∣01⟩.

否认

最后一个!它会成为决定性因素吗?我希望不会。请记住,与 identity 不同的是,在 CNOT 完成其相位反冲魔法后,我们有一个 X-Gate 作用于备用位。

所以毫不奇怪,它成功了!这的确是∣01⟩,正如我们对平衡函数的期望!

我们能够一次就识别出 4 个函数中的任何一个和全部!

顺便说一句,一台经典的计算机需要对这些函数中的任何一个进行两次处理,才能确定它是常数函数还是平衡函数。试着自己证明,这需要一个 if-else 语句。

因此,我们已经看到了我们的第一个量子计算算法在工作!凭借我们开发的工具和知识,这仅仅是个开始!

算法的确是迈向更高抽象层次的第一大步。我的希望是,通过我们在这里展示的工具,你已经发展了思考现有算法的直觉水平,以及尝试它们的好奇心。

谁知道呢,随着时间的推移,你可能会在这个想法中变得如此根深蒂固,以至于你偶然发现了量子编程领域中一个未被探索的角落,并创造出你自己的东西。

与此同时,我打算继续探索量子编程的世界,并希望在前进的道路上写更多关于这个主题的东西。

感谢阅读!

1.与全局相位相反,全局相位是系统中所有量子位都具有相同相位的情况,因此可以从观测计算中排除。具有全局相位的系统在物理上是相同的,而具有相对相位的系统则不同。在大多数情况下,它们可能仍然以同样的方式被测量,但这只是因为量子计算机中的测量通常沿着同一轴进行,与相位无关。

2.在量子信息中,可以说这个相对相位携带了关于系统的相干性的信息。

原载于 2021 年 4 月 1 日【https://kblagoev.com】

从量子力学中抽象出我们自己的量子编程:抽象层次 0

原文:https://towardsdatascience.com/quantum-programming-by-abstracting-ourselves-from-quantum-mechanics-abstraction-level-0-62a258b4def0?source=collection_archive---------18-----------------------

正如我们知道在经典计算机中我们处理逻辑 0 和 1 一样,我们也可以知道我们在量子计算机的最低逻辑层中处理的是什么。

作者图片

  1. 从量子力学中抽象出自己的量子编程:抽象层次 0 (this)
  2. 量子编程——抽象层 1:逻辑门

个人计算机的发明,以及利用它的数百种编程语言的进一步发展,使我们程序员能够完全从流过固体物质的电子世界中抽象出来,并进一步从通过开关门和晶体管来产生二进制数据的低级编程中抽象出来。

这似乎是我们作为一个技术社会的运作方式。我们喜欢在抽象的基础上构建抽象,直到我们能够以一种我们认为简单且实用的人类方式与我们的机器交流。

随着在量子机器上解决问题变得更加现实,我们将能够开始在相同的上下文中使用术语应用量子力学。这将是一个我们能够从量子力学的如何和为什么中抽象出来,并简单地在现实世界的应用中利用它的怪异之处的时代。

但是正如我们知道在经典计算机中,最终都归结为在内存网格中翻转的 1 和 0,我们必须知道我们在量子计算机的最低逻辑层中处理的是什么。当谈到量子计算时,我们的基本逻辑结构是什么,使我们能够建立和操纵数据。

量子位与比特

我们必须在这里建立一个逻辑时间线。在经典计算机中,我们有特定的计算步骤。让我们特别关注两个问题。

  1. 我们操纵一位的价值。我们可以将它的值从 0 翻转到 1 或从 1 翻转到 0,或者保持不变。
  2. 现在我们可以测量位的价值。我们现在将观察它是 1 还是 0。

我们还可以注意到,在测量之后,比特仍然是我们观察到它的价值之前的样子,测量的行为并没有改变这一点。

显而易见,如果我们跟踪正在发生的事情,我们甚至在测量它之前就已经知道该比特是 1 还是 0。正如我们马上会看到的,

在大多数情况下,在测量一个量子比特之前,我们无法知道它的值。

量子位

量子位也可以处于 1 或 0 的状态,但是它们也可以同时处于这两种状态。当它们处于这种所谓的叠加态时,0 和 1 的状态与一种概率相关联——也就是说,如果你拿一个量子位并测量它,它将根据这种概率选择是出现 0 还是 1。

值得注意的一点是,量子位只有在不被测量时才能保持叠加态。当它的值被提取出来的时候,定义它的概率的波函数塌缩了,它根据塌缩前的概率随机选择 0 或 1。

这里要指出的另一点是,我们可以以这样的方式处理这些位,它是 1 或 0 的概率是 100%。这样,我们可以像使用经典计算机一样使用量子计算机。但是我们也会失去量子计算机的一个优势——利用叠加(以及后来的纠缠)带来的量子奇异性。

将量子位聚集在一起

现在让我们考虑一下,我们的记忆库中有不止一个比特。假设我们有 3 个!让我们把它们可能的值 1 和 0,放在系统所有可能状态的表中

我们现在可以观察到,在这个 3 位系统中有 8 个状态。4 位有 16 个状态,8–256 个,以此类推,呈指数增长,因此状态数等于 2 的位数幂。换句话说,每增加一位,状态的数量就会翻倍。

这与在比特网格中保存信息没有什么不同。当我们试图访问这些状态来操纵或测量它们时,差异就出现了。当计算机对一个状态进行操作时,它把它载入寄存器。在传统计算机内部的单个 CPU 中,可以将单个状态加载到寄存器中并进行操作。有趣的是,这种量子寄存器同时存储所有 2^n 可能状态,并可以通过改变单个量子位的概率分布来同时改变所有这些状态。

让我们在那上面展开一点。把量子寄存器想象成一个简单的量子位物理网格,每个量子位都包含着它坍缩为 1 或 0 的概率信息。如果我们得到所有量子位的概率,我们就可以很容易地计算出当我们折叠整个寄存器时,上面列出的每个系统状态出现的概率。例如,假设所有 3 个量子位都有 50%的几率坍缩为 1 或 0,我们将看到所有状态也有相等的概率,并且我们知道这些概率加起来应该是 1:

因此,我们可以将量子寄存器中保存的信息重新定义为我们观察系统时出现的每一个状态的概率列表。因为这个列表与所有可能状态的列表长度相同,所以量子寄存器保存的信息等于 2 的比特数的幂。当然,问题是,当我们测量系统时,它会坍缩到其中一个状态,然后我们就有了等于位数的信息。

但是在我们测量这个系统之前,

如果我们仅仅改变一个量子位的概率,我们就立即改变了所有状态的概率分布。

假设我们改变了 q2 的概率,使其有 100%的几率为 0。然后我们得到

换句话说, q2 为 1 的所有状态都是不可能的,其他 4 个状态概率相等。

下一个问题是,当我们崩溃系统时,我们会得到上述状态之一。我们仍然不知道概率是多少,我们只知道其中一种状态是可能的。但是,

如果我们多次瓦解这个系统,在每次以同样的方式设置量子位之后,我们将获得关于每个状态发生的概率的统计信息。

逻辑门

所以,为了改变每个状态发生的概率,我们需要操纵每个量子位的概率。在经典计算机中,为了操作一个位,我们使用逻辑门如 NOT、SET1、SET0 和单位,以及 and、or、XOR 等。两个比特。

以类似的方式,我们在量子世界中有逻辑门。有些是对一个量子位进行操作,有些是对两个量子位进行操作,你可以让一个对三个量子位进行操作。可以为量子位定义许多逻辑门,实际上只要遵守量子力学的数学约束,可能性是无穷的。但令人欣慰的是,已经证明,量子计算机上的任何操作都可以有效地减少到有限数量的门,因此我们可以定义一个足够小的指令集,用它我们可以将量子比特操纵成我们需要的任何概率。

我们现在已经达到了量子编程的第一个抽象层次——指令集。我们现在基本上可以定义一种类似汇编的语言来为量子计算机编写代码。在完成这篇文章之前,让我们对这个抽象概念有一个技术上的定义。我想在另一篇文章中集中讨论真正的门,否则这会变得太笨拙。

量子抽象机

这是通用量子计算设备(量子计算机)的抽象表示。值得注意的是,QAM 定义了经典态和量子态的操纵技术。这意味着我们将同时拥有量子位和经典位来存储观测数据。因此,QAM 由一系列元素指定:

  • 量子位的数量
  • 许多经典比特(例如用于存储量子比特的折叠状态)
  • 用来操纵量子位的门的列表
  • 指定量子指令语言的指令序列
  • 整数计数器,指示要执行的下一条指令的位置

有了这个,我们现在可以自由定义门和类似汇编的指令,用它们来操纵单个或多个量子位的概率,最终改变量子位电路状态的概率分布。在下一篇文章中,我们将开始定义布洛赫球,以及在其上运行的一系列门——达到 抽象级别 1

  1. 请注意,这种说法没有什么神奇的,这只是一个我们在宏观世界中无法体验的自然事实——任何基本粒子都将保持其所有可能状态的叠加,直到它受到作用(观察),在这一点上,它包含所有状态概率信息的波函数崩溃,粒子随机选择这些可能状态中的一个。

2.我不想谈太多细节,只想指出一些事实:1。经典的计算机门大多是不可逆的,意思是门应用后,你无法弄清楚原来的输入是什么。由于量子力学的物理性质,量子都是可逆的。2.量子门由酉矩阵表示。极其有用。事实上,从线性代数的角度来看,我可能会写第二个版本,因为它看起来很令人满意。

原载于 2021 年 3 月 27 日 https://kblagoev.comhttps://kblagoev.com/blog/quantum-programming-abstraction-level-0/

全图麦:历史背景和现实世界的奇巧例子

原文:https://towardsdatascience.com/quantumai-historical-background-real-world-qiskit-example-99c21ae2a78a?source=collection_archive---------27-----------------------

对于寻求科学刺激的人来说

作者图片

量子计算已经成为一个热门话题。领先的研究机构、企业和专门的初创公司投入大量资源研究这项技术及其实际表现。对于几乎每一项创新技术,我们可能会问一些抽象的问题,比如它是什么或者为什么会流行?以及诸如“我们的组织能获得什么好处吗?”对于数据科学家来说,我们通常关心“对于这些算法,哪个 Python 包是最佳的?”。这篇文章旨在阐明这些问题。

量子对于计算机?怎么会这样

大约在 1850 年,德国物理学家克劳修斯发现了热力学第二定律。他发现越来越多的量与能量密切相关。起初他给这个量起了个名字Verwandlunginhalt**(内容转换)

后来,为了与能量的概念相一致并强调它们之间的紧密联系,他将这个概念修改为熵(“内在转化”)。从那以后,研究人员一直认为这两个量密切相关。当香农、维纳(控制论)、哈特利等人建立信息论时,他们使用了克劳修斯和玻尔兹曼为热力学开发的同样严谨的工具。

这些步骤之后是一些理论上的突破,如 Szilard 的发动机(阅读或请我亲自告诉你)。信息和现代物理学总是紧密相关,因此量子作为计算工具并不是一个牵强的想法

现在的电脑怎么了?

当观察当前一代计算机时,有两个主要障碍:

  • 物理边界——最近几十年是

小型化。处理器变得越来越小,使得计算机能够大幅提高性能。然而,当我们观察当前 pn 结二极管的宽度时,我们可以注意到它大约为 5–7nm。氢原子的半径约为 0.1 nm。这意味着我们离边界越来越近。因此,我们需要制造出大小约为一个氢原子的“芯片”。这样的单位会受到量子效应的影响。

  • 复杂性 — CS 理论大量研究难题(你们都很熟悉这个概念 P & NP )。这些问题的最重要的代表之一是量子系统的仿真,其中复杂性呈指数增长。

1982 年,Richard Feynam 推测也许量子粒子可以更好地模拟它们自己,并勾画出量子计算机的概念,要求工业界建造一台

QC 有什么酷的?

QC 技术允许加速计算过程,因为它可以更好地处理多种状态(更多信息)。随着理论的进步,它提供了一些突出的突破,如肖尔的 因式分解数的算法格罗弗的搜索算法。最近,一些基于量子的人工智能解决方案已经可用,用于解决我们在处理大型数据库时可能遇到的高复杂性问题

量子计算中的基本对象

在这一节中,我将描述量子计算中的几个基本概念。我假设你们都熟悉向量酉矩阵

偈空间

在量子力学中 ket 空间描述了物理状态的空间(例如速度或动量)。“Ket”这个概念是由狄拉克给出的,因为它表示⎢X 的列向量 X >。正如你所料,这里也有 bra 空间,我们用下面的< X⎥.来表示原始向量

量子测量

测量是量子力学中最基本的概念。在经典物理学中,当汽车在路上行驶时,很明显它有一个速度矢量。假设一个人想知道速度是多少,她简单地去测量它。很明显,我们不希望这个测量值改变速度。这是在我们测量之前和之后。测量只会丰富观察者的知识。

在量子力学中,事物是不同的。

一个粒子没有一定的速度,它有一个同时存在的速度集合,每个速度都是一个 ket 态(速度是一个矢量,因为它既有大小又有方向)。当我们测量粒子的速度时,它会坍缩到这些潜在状态之一:测量(和观察者)会影响结果。

这个概念很难理解。然而,它提出了一个想法,如果计算理论遵循量子力学,在某些情况下,计算过程可能会同时处理几个状态,从而大幅加速它们。这是我们需要从这一部分得出的主要结论

什么是量子比特

经典计算中最小的信息单位是一位。在接下来的部分中,我将描述它在量子计算中的类比>

我们能把一点想象成一个粒子吗?

如果 0 和 1 是一个比特的两个看似合理的值,但这两个值从来不会同时出现,那么我们可以直接把它们看作比特的电位状态。让我们来表示这些状态:

⎪0❯ 当该位的值为 0

当该位的值为 1 时,⎢1❯

考虑一点我们不知道它的价值。由于它可能在美国的 ⎪0❯⎢1❯ ,我们可以提供一个概率解释:

  • 有一个概率 P 成为 ⎪0❯
  • 有一个概率 1-P 成为 ⎪1❯

我们是否知道 P 这个问题现在不太重要

如果我们遵循量子力学,比特同时处于⎪0❯态和⎪1❯.态

一个“经典工程师”可能声称一个位可以是 0 或 1。我们能做的只是送他去测量。在那里量子力学和他会相遇。

整个过程遵循量子力学。我们没有创造不同的比特。我们只是改变了我们考虑和处理它们的方式。我们称这个旧比特为新方法量子比特

这种新方法可能会加速大量现在难以处理的计算过程

代数上,我们可以将 qubi t 写成如下:

作者图片

我们有

1=⎪α⎪ +⎪β ⎪

(即⎪α⎪得到⎪0❯的概率)

布洛赫球

一个很好的表示量子位的方法是布洛赫球

作者图片

我们将偈 ⎪0❯ 标为北极,将偈 ⎪1❯ 标为南极。用代数方法,我们写出状态:

作者

其中 𝛗 为 XY 平面与 𝛉.的角度关于 ZY

量子电路

在经典世界中,逻辑电路是接收位并执行逻辑运算符的实体,其输出是位。这些电路由逻辑门组成,如NOT。为了有一个好的计算模型,我们需要为量子计算开发一个模拟。我们需要开发用于构建量子电路的门。这些门的本质是什么?为了获得一些直觉,我们观察布洛赫球。回想一下,每一个量子位元都可以在布洛赫球上呈现为一个向量,因此量子位元有固定的数量级。幅度保持映射常用数学工具是酉矩阵。因此,我们有自然的候选人这样的大门。

事实上,我们可以将量子电路想像成一条链,它接收量子位元作为输入,并执行一系列的幺正运算和测量。在下面的章节中,我将介绍一些最常用的门。

哈达玛门

大概是最常用的。这种转换的结果是每个状态都有相等的概率。在 Qiskit 中,我们可以如下查看此门:

作者

CNOT

一个两个量子位的,如果第一个量子位是零,则保留第二个量子位,如果是 1,则改变第二个量子位。在 Qiskit 中,它表示如下

作者

相移(U1)

一个单量子位,它将 ⎪1❯ 旋转到ƛ角

作者

量子电路的一个例子如下:

它是做什么的?

  • 它在每个初始量子位上执行哈达玛门
  • 它不是在第一个量子位上而是在第二个量子位上执行控制
  • 它旋转第三个量子位
  • 它测量整个量子位

Qiskit 真实世界示例

我们讨论了一些量子计算的想法,现在是时候在真实世界的数据上测试这些概念了。为此,我将介绍两种量子人工智能算法:

QSVM——SVM 的类似物

量子贝叶斯网络

QVSM

SVM 有一个量子模拟。显然,它需要在数据预处理和提取结果方面进行一些修改。需要采取几个步骤:

我们需要决定量子位的数量。根据经验,我们采用数据维度。

然后我们设置一个量子电路来执行这个算法。

最后,我们必须设置提取信息的方法。

对于 SVM,我们通常考虑在 QSVM 中作为经典函数组合的问题,并且在它们的输出上遵循酉算子。这模仿了特征提取步骤。

QSVM 的这种优势取决于这些函数的复杂性。你可以阅读更多关于这些步骤的技术细节。

在培训阶段,我们面临两个挑战:

  • 寻找量子内核——使用量子计算。
  • 优化 SVM 的权重(这是使用经典方式完成的)

围绕这一训练和测试的过程是纯粹的经典训练。

在文本数据上运行它,我们得到如下结果

作者

一种概率方法

下一种方法依赖于经验概率方法。我们使用一组带有宁滨或特殊映射等聚合器的原始数据,作为我们引擎的特征。该引擎旨在估计给定数据项属于我们的一个聚类的概率。与 QSVM 相反,这里我们使用单个量子位,它的每个组成部分 ⎪0❯⎪1❯ 代表一个簇。量子算法旨在优化系数,以便实现相干测量..回想一下,一个量子位是由 ⎪0❯⎪1❯ 组成的

回想一下关于 Bloch 球面的部分,因为我们的数据是真实的,所以我们可以假设

𝛗 消失,系数跟随 βα 取决于 θ

我们使用数据的单变量属性来建立系数,并得到数据的向量

作者

将我们的数据与随机量子位进行比较的回忆图显示了这张图片

作者

有兴趣的可以在这里阅读更多关于这个技术的内容

我的想法

如果我们仅仅把这些结果作为人工智能实验来观察,结果是显而易见的。这些模型表现出较低的性能,无疑在商业上没有竞争力。然而,如果我们将其视为长跑运动员,这些算法相当年轻,用人工智能分类工具来衡量它们甚至更年轻。因此,采取进一步的研究步骤,在更大的数据量和不同的领域进行更多的试验是令人振奋的。从抽象的角度来看,量子计算提供了一种不同的方法,不仅可以解决棘手的问题,还可以用一种创新的方式思考那些我们可以思考的问题。我个人喜欢它不是因为 Shor,而是因为攻击概率云而不是某些状态的想法。

谢谢

Power BI 中的查询折叠—细节决定成败

原文:https://towardsdatascience.com/query-folding-in-power-bi-devil-is-in-the-detail-d564ab0cb32?source=collection_archive---------13-----------------------

在 Power BI 中查询折叠系列的第 2 部分中,了解为什么人们说细节决定成败!

https://unsplash.com/photos/fD0pgJCvypk

"这会破坏查询折叠吗?"“您的查询会折叠吗?”…也许有人问过你这些问题,但你会说:“问…什么?!"

或者,您可能听说过 Power BI 中的查询折叠,但不知道如何在现实生活中利用它。

如果你认识到自己处于(至少)上述两种情况中的一种,那么请继续阅读这篇简短的系列博文!

在本系列的前一部分中,我们解释了什么是查询折叠,哪些数据源支持它,以及数据源本身中的哪些转换。现在,让我们检查一下为什么实现这种行为很重要——或者,更好的说法是——为什么您应该关心查询是否折叠?

为什么您应该关心查询折叠?

当您在 Power BI 中使用导入模式时,无论是在刷新速度还是资源消耗方面,当查询折叠时,数据刷新过程的工作效率都会更高。

如果您正在使用 DirectQuery 或双存储模式,因为您直接面向 SQL 数据库,所以您的所有转换必须折叠——否则您的解决方案将无法工作。

最后,查询折叠对于增量刷新也非常重要——它非常重要,一旦 Power BI 确定无法实现查询折叠,它就会向您发出警告。它不会破坏您的增量刷新“本身”,但是如果没有查询折叠,增量刷新就无法实现其主要目的——减少数据模型中需要刷新的数据量——因为没有查询折叠,Mashup engine 需要从源中检索所有数据,然后应用后续步骤来过滤数据。

记住所有这些,您应该尽可能地实现查询折叠。

慢速报告—不要责怪查询折叠!

这里有一个重要的免责声明,这是这一系列博客文章的关键要点之一:如果你的报告很慢,或者你的视觉效果需要很多时间来渲染,或者你的数据模型很大,查询折叠与此无关!

只有当您的数据刷新或增量刷新速度慢且效率低时,您才应该更深入地研究您的超级查询步骤。

全有还是全无?

关于查询折叠,还有一些事情需要记住。这不是全有或全无的过程。也就是说,如果您在 Power Query 中有 10 个转换步骤,并且您的查询折叠到第 6 个步骤,您仍然可以从部分查询折叠中获得一些好处。但是,查询折叠一旦被打破,就再也无法实现了。

作者图片

简而言之,如果您有 10 个转换步骤,并且您的查询折叠在第 5 个步骤中被破坏,那么前面的所有步骤都将被折叠,但是一旦折叠被破坏,就不能再实现了,即使您在步骤 6 到 10 中有默认支持查询折叠的转换——就像在我们的示例中过滤应该是一个可折叠的步骤,这些步骤也不会被折叠。请记住这一点,并尽可能地将所有不可折叠的步骤推向流水线。

如何知道查询是否折叠?

好了,现在我们不再是菜鸟了。我们知道什么是查询折叠,为什么我们应该努力实现它,以及一些可以产生巨大差异的微妙技巧。

现在,是时候学习如何检查特定查询是否折叠了。第一种也是最显而易见的方法是右键单击该步骤,并检查 View Native Query 选项看起来如何。

如果它是灰色的,这一步(可能)不会折叠。另一方面,如果您能够点击这个选项,这意味着您的查询可能会失败。我猜你可能对“可能”这个词感到困惑。

作者图片

但是,这是一个合适的词,因为你不能 100%确定如果视图本地查询选项被禁用,你的查询不会折叠。稍后我将向您展示这个选项是如何欺骗我们认为查询折叠被破坏的,尽管实际上折叠确实发生了。

相反,当您想要确定您的查询是否折叠时,您可以使用 Power Query Editor 中的查询诊断功能,或 SQL Server Profiler ,就像一种古老而可靠的方法来检查 Power BI 引擎发送到数据库的查询。

此外,在 Power Query Online 中有一个很酷的新功能,其中每个步骤都标有图标,显示该步骤是否折叠、未折叠或未知。正如我所说,这个功能目前只在 Power Query Online 中可用,所以让我们希望 Power BI 团队很快在桌面版本中实现它。

作者图片

细节决定成败…

好吧……你可能听说过这样一句话:“细节决定成败”。现在,是时候了解微小的差异如何在我们的数据转换过程中产生巨大的影响了。

让我们从 Power 查询编辑器中最令人好奇的一个案例开始…

魔鬼#1 —合并联接

这一个非常有趣,因为你很难假设背景中正在发生什么。假设我想将两个查询合并成一个。我将使用 Adventure Works 示例数据库,并且我需要合并 FactInternet Sales 和 DimCustomer 表。

我将从事实表中删除一些列,只保留 CustomerKey 列和 Sales Amount 列,因为这是 DimCustomer 表的外键。我将按原样连接 DimCustomer 表,在合并之前不需要任何额外的步骤。

作者图片

合并表相当于 SQL 中的 JOIN 操作。本质上,我们选择要对其执行合并操作的列,以及连接的类型(left、outer 或 inner)。

作者图片

问题是,默认情况下,当您合并两个查询时,Power Query 将生成一个嵌套的 join 语句,这在 SQL 中无法正确翻译。

超级查询生成的嵌套连接操作

如果我转到 Tools 选项卡并单击 Diagnose Step,我可以看到 Mashup 引擎向我的底层 SQL Server 数据库发出了两个单独的查询——换句话说,这两个查询不能作为单个 SQL 语句执行,这意味着查询没有折叠!

作者图片

我们如何解决这个问题?让我们选择一个空的查询并手工编写我们的 M 代码,以达到完全相同的结果。

作者图片

关键的是,我们将使用一个类似的,但仍然不同的 M 函数: 表。加入

我们现在使用的是表格。连接功能

所有的函数参数都与之前完全相同,现在让我们来检查结果。

你记得我曾经告诉过你,当视图原生查询变灰时,你的查询可能不会折叠,但它不是 100%正确。这是一个很好的例子。如果你看一下视图原生查询,它仍然显示我们的查询没有折叠…

作者图片

…但是让我们去诊断一下,看看这是不是真的。

作者图片

哦,天哪,我们上当了——这一步确实失败了!如上图所示,我们生成了一个 SQL 查询,并发送到 SQL Server 源数据库执行。

因此,我们在这个例子中发现了两个难题——第一个是连接类型,我们可以通过调整自动生成的 M 代码来解决。另一个是视图本地查询选项的不正确行为。在本系列的下一部分中,我将再向您展示一个当视图本地查询存在时的例子。

结论

由于我们在本系列的前一部分中打下了关于查询折叠的坚实的理论基础,我们加深了对查询折叠概念的重要性的理解。

此外,我们已经看到了一些细微的差异如何对将 M 代码转换为 SQL 的过程产生巨大的影响,并了解了如何解决查询(非)折叠时最常见的挑战之一——那就是合并操作!

在本系列的下一部分,我将向您展示一些与查询折叠相关的更方便的技巧。

如果你迫不及待地想了解更多关于查询折叠的知识,我强烈推荐查看由 Alex Powers 创建的“30 天查询折叠挑战”,因为这可能是理解这个概念的最全面的资源。

成为会员,阅读媒体上的每一个故事!

订阅这里获取更多有见地的数据文章!

Power BI 中的查询折叠—技巧、谎言和最终性能测试

原文:https://towardsdatascience.com/query-folding-in-power-bi-tricks-lies-ultimate-performance-test-879cafcf6cf8?source=collection_archive---------15-----------------------

在查询折叠系列的最后一部分,学习更多技巧,找出视图本地查询所在的时间,最后找出(不)折叠的查询之间的差异,由实数支持

https://www . pexels . com/photo/ace-bet-black jack-business-534181/

"这会破坏查询折叠吗?"“您的查询会折叠吗?”…也许有人问过你这些问题,但你会说:“问…什么?!"

或者,您可能听说过 Power BI 中的查询折叠,但不知道如何在现实生活中利用它。

如果你认识到自己处于(至少)上述两种情况中的一种,那么请继续阅读这篇简短的系列博文!

我假设您现在已经熟悉了 Power BI 中查询折叠的概念,尤其是它对于数据刷新和增量刷新过程的重要性。我们也已经开始研究 Power Query 转换的一些有趣的行为,在这个系列的最后一部分,我将向您展示一些更有趣的发现。

最后,我们将用最终的性能测试来结束它——我将向您展示两个相同查询背后的确切数字——一个折叠,另一个不折叠!

更改数据类型

Power Query 中最常见的转换之一是更改数据类型。在您的数据模型中使用适当的数据类型是一个众所周知的最佳实践——例如,如果您的报告中不需要小时、分钟和秒级别的粒度,您应该更好地摆脱它们,并将该列的数据类型从日期/时间更改为仅日期。

然而,通往地狱的道路是由良好的意图铺成的:)…因此,让我向您展示一个细微的差异,它会导致您的查询变得非常慢,即使您坚持使用正确的数据类型的建议!

作者图片

如上图所示,我的 OrderDate 列属于日期/时间数据类型。我想把它切换到日期。有(至少)两个可能的选项可以做到这一点—第一个是右键单击该列,展开“更改类型”选项的下拉列表(就像我在插图中所做的那样),然后选择“日期类型”(就在日期/时间下方):

作者图片

这里发生了一些重要的事情,让我来解释一下:

  1. 在 Applied Steps 窗格中,您可以注意到我们的转换步骤已经被记录下来
  2. 在该列中,您可以看到时间部分消失了
  3. 当我打开 View Native Query 对话框时,您可以看到 Mashup 引擎很好地将我们的转换转换成了 T-SQL CONVERT() 函数
  4. 应用于这个转换步骤的 M 公式是:表。TransformColumnTypes()

现在让我们检查更改列的数据类型的另一个选项:

作者图片

就在我们之前的变更类型选项下面,有一个转换选项。展开下拉列表后,您可以看到“仅日期”转换。让我们点击它,看看会发生什么:

作者图片

看起来很相似,是吗?但是,让我们再次回顾一下现在发生的所有事情:

  1. 我们现在有一个称为提取日期的步骤,而不是更改类型的步骤
  2. 该列本身看起来与上一个示例完全相同,没有时间部分
  3. 不对不对,查询不再折叠了!正如您所看到的,查看本地查询选项是灰色的!
  4. 这个时候,M 公式适用的是:表。TransformColumns()

所以,M 公式(表中的一个不同的单词。TransformColumnTypes vs 表。TransformColumns )严重影响了我们的查询,以至于无法将其转换为 SQL!

从这个故事中吸取教训:当你选择改变数据类型的选项时,要小心谨慎!

编写本机 SQL 查询以获取数据

将数据导入 Power BI 时,您需要做出的最早决定之一是:您是否希望从 SQL 数据库“按原样”导入数据,然后在 Power Query 编辑器中应用必要的转换…或者,您是否希望手动编写 SQL 查询来检索数据…

如果您选择后一个选项,您应该意识到手写的 SQL 查询不能再折叠了!所以,在这个场景中,是 全有或者全无 !一旦您决定使用自定义 SQL 语句导入数据,在 Power Query 编辑器中应用的所有后续转换步骤都不会折叠,即使您正在应用一些基本的转换,如过滤或重命名列,这些步骤在“正常”情况下会折叠。

让我给你看一个例子:

作者图片

因此,我想从 DimDate 表中导入所有行和列。现在,假设我只想保留 2007 年 1 月 1 日之后的行:

作者图片

在正常情况下,当您“按原样”导入 DimDate 表时,可以使用 SQL 中的 WHERE 子句(WHERE FullDateAlternateKey > ' 2007–01–01 ')轻松转换该转换,并将其推送到 SQL 数据库源。

现在,如果我在这一步运行查询诊断工具,我可以看到这里没有发生查询折叠!

作者图片

如您所见,只有我们的初始查询是在 SQL 数据库端执行的。所以,这里发生的事情是,Mashup 引擎从 DimDate 表中提取了所有行,然后应用了我们请求的转换步骤!无需强调这对性能的影响,无论是在 CPU 还是内存消耗方面…

如果您决定采用这种方式并手工编写 SQL 代码,有两种可能的解决方案:

  • 创建一个数据库视图——这是最理想的场景!如果您能够在底层数据库中创建视图,那么您可以将整个转换逻辑封装在视图中,然后“按原样”导入视图,所有应用的转换都已经就绪!由于数据库视图在 Power BI 中被视为与数据库表完全相同,因此您仍然可以在视图上实现查询折叠!
  • 在单个 SQL 语句中应用所有必要的转换 —如果您没有足够的权限创建数据库对象,另一个选项是在您编写的单个 SQL 语句中包含所有必要的转换步骤。大概是这样的:

作者图片

最近,在编写原生 SQL 查询时,对查询折叠进行了一些改进。克里斯·韦伯写了一篇关于使用价值的优秀博文。NativeQuery()* 函数,并为 EnableFolding 参数提供显式值 TRUE 。*

然而,在撰写本文的时候,这个选项只适用于 PostgreSQL 连接器,这里的中记录了这个连接器。它也可以在 SQL Server 中工作,但不是以最直观的方式,因为您需要在高级编辑器中手动替换 M 代码。

因此,这更像是一种变通解决方案(确实很有帮助),但是让我们希望在不久的将来,我们能够“开箱即用”地明确指示 Power BI,我们希望在编写定制 SQL 时启用查询折叠…

骗子,骗子…

我在本系列的前一部分已经承诺,我将向您展示另一个例子,当查看本机查询选项可以欺骗您认为查询折叠被破坏时,即使实际上它不是真的…

假设我们只想保留表中的前 X 行。在我的例子中,我希望保留事实表中的前 2000 行:

作者图片

一旦我应用了这个步骤并检查了 View Native 查询,我就可以意识到我的查询折叠了,因为我的转换被转换成了 SQL 中的 TOP 子句:

作者图片

现在,假设我想对我的销售额列应用绝对值转换。通常,这种转换很容易折叠,因为 T-SQL 中有一个 ABS 函数:

作者图片

但是,如果我右键单击这一步,我会看到查看本地查询选项是灰色的,所以我会假设这一步破坏了我的查询折叠!

作者图片

让我们在查询诊断工具中对此进行检查:

作者图片

哦,我的天啊!这一步确实折了!所以,我们又被查看原生查询选项骗了!

这里的关键接管是:每当您假设特定的转换步骤可以折叠时(就像在这个例子中,当我们知道 SQL 有 ABS 函数来支持我们的转换时),请仔细检查实际发生了什么!

终极性能测试

好吧,如果到目前为止我还没有说服您为什么应该努力实现查询折叠,现在让我拿出我最后的王牌吧!

我想向您展示返回完全相同结果的查询之间的数据刷新性能差异——其中一个折叠,另一个不折叠!

测试#1 查询折上

对于这个测试,我将使用 Contoso 示例数据库中的 FactOnlineSales 表。这个表大约有 1260 万行,很好地展示了查询折叠概念的重要性。

在第一个示例中,我应用了 9 个不同的变换步骤,它们都是可折叠的,如下图所示:

作者图片

不要关注 Mashup 引擎生成的 SQL 代码:如果你是一名 SQL 专业人员,当然,你可以编写更优化的 SQL 代码——但是,请记住,使用 Mashup 引擎自动生成的脚本, 你不会得到最优的 SQL——你只是得到正确的 SQL

我将点击 Close & Apply 并打开秒表来测量我的数据刷新持续的时间。

作者图片

这个查询花了 32 秒在我的 Power BI 报告中加载了 280 万条记录。数据是以 100.000–150.000 条记录为一批加载的,这很好地表明查询折叠已经就绪。

测试#2 查询折掉

现在,我将返回到 Power Query Editor,并在第三步中故意中断查询折叠(记住上面将日期/时间类型更改为 Date 的示例),使用我知道不可折叠的转换:

作者图片

说实话,我将在这里实现部分折叠,因为前两步将折叠,但提取日期转换后的所有后续步骤将不会折叠!

让我们再次打开秒表,看看会发生什么:

作者图片

首先要注意的是:这个查询花了 4 分 41 秒加载到我们的 Power BI 报告中,这大约比我们之前的查询折叠时的多 10 倍。这一次,加载的数据批次在 10.000 到 20.000 条记录之间。**

但是,更值得关注的是—您可以看到加载的记录总数几乎达到了 1100 万条!!!而不是上例中的 280 万!为什么会这样?在之前的文章中,我解释了当 Mashup 引擎不能将 M 语言翻译成 SQL 时,它需要将所有数据(从查询折叠被破坏的那一刻起)拉进所有数据,然后然后对整个导入数据块应用转换!****

最终结果是完全相同的——我们的 Power BI 报告中有 2.830.017 条记录——但是,在查询折叠就绪后,所有必要的转换都在 SQL 数据库端执行,Mashup 引擎得到了已经准备好的数据集。而在第二个场景中,在我们中断了查询折叠之后,Mashup 引擎将所有剩余的行(大约。1100 万),只有在这之后,它才能应用其他转换步骤。

而且,这只是一个基本的例子,只有一个表,数据量没有那么大!想象一下包含多个表的大型数据集的影响有多大。

结论

嗯,我们在这个博客系列中讨论了很多。我们学习了数据整形概念,我们介绍了超级查询基础知识,我们还学习了什么是查询折叠以及为什么我们应该尽最大努力来实现它。

我还与您分享了一些基本的例子和在一些常见用例中如何实现查询折叠的巧妙技巧。

最后,请注意,查询折叠是一项正在进行的工作,Power BI 团队的人员正在不断改进这一特性。因此,我在这里向您展示的一些查询折叠问题可能会同时得到解决。因此,请确保了解最新的改进。

感谢来自微软的 Alex Powers 收集了关于查询折叠的不同资源,以及 Christopher Majka 分享了链接,Github 上有一个 repo,在这里您可以跟踪查询折叠功能的所有最新变化。

感谢阅读!

成为会员,阅读 Medium 上的每一个故事!

订阅此处获取更多有见地的数据文章!

用 SQL 查询熊猫数据帧

原文:https://towardsdatascience.com/query-pandas-dataframe-with-sql-2bb7a509793d?source=collection_archive---------1-----------------------

熊猫里可以用 SQL 吗?是的,这就是方法。

来自 Pexels.M.Q 黄摄影

动机

Python 熊猫库和结构化查询语言(SQL)是数据科学家工具箱中最重要的工具之一。虽然 Pandas 是一个强大的数据操作工具,但是有许多数据科学家熟悉并喜欢使用 SQL 进行数据操作。在本文中,我们将研究如何使用带有pandasql库的 SQL 来执行 Pandas 数据帧的数据操作。

Pandasql 是什么?

Pandasql 是一个 python 库,允许使用 sql 操作 Pandas 数据帧。在幕后,Pandasql 从感兴趣的 Pandas 数据帧创建一个 sqlite 表,并允许用户使用 SQL 从 SQLite 表中进行查询。

Pandasql 是如何工作的?

安装 Pandasql 包。

!pip install -U pandasql

导入必要的包。

from pandasql import sqldf
import pandas as pd
from sklearn import datasets

我们使用 iris 数据集作为例子。df_feature是包含特征的数据帧,而df_target是包含目标的序列。Pandasql 可以在熊猫DataFrameSeries上运行。

df_feature = datasets.load_iris(as_frame = True)['data']
df_target = datasets.load_iris(as_frame = True)['target']print (type(df_feature))
print (type(df_target))>> <class 'pandas.core.frame.DataFrame'>
>> <class 'pandas.core.series.Series'>

sqldf方法用于查询数据帧,它需要 2 个输入:

  1. SQL 查询字符串
  2. globals()locals()功能

典型的查询如下所示,其中q是 SQL 查询字符串。sqldf返回数据帧中的结果。

q = "SELECT * FROM df_target LIMIT 3"
sqldf(q, globals())

作者图片

globals()locals()是 python 中内置的函数,用来存储函数和变量。让我们来看看globals()函数是做什么的。

globals()

作者图片

globals()函数返回在该会话中创建的变量字典,如df_featuredf_target。字典的关键字是变量名,字典的值包含变量的实际值。

print (globals().keys())>> dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__builtin__', '__builtins__', '_ih', '_oh', '_dh', '_sh', 'In', 'Out', 'get_ipython', 'exit', 'quit', '_', '__', '___', '_i', '_ii', '_iii', '_i1', '_exit_code', '_i2', 'sqldf', 'pd', 'datasets', '_i3', 'df_feature', 'df_target', '_i4', '_4', '_i5', '_5', '_i6'])

由于globals()函数输出一个字典,我们可以通过以下方式使用globals()函数获取变量的访问值:

globals()['df_feature']

这将返回df_feature数据帧。

作者图片

例子

现在我们已经了解了globals()locals()函数如何与 Pandasql 一起工作,让我们来看一些例子。我们创建了一个名为pysqldf的新函数,以避免每个查询都传入globals()locals()

pysqldf = lambda q: sqldf(q, globals())

我们可以用下面的方式简单地查询数据帧。

query = 'SELECT * FROM df_feature LIMIT 3'
pysqldf(query)

作者图片

query = 'SELECT * FROM df_target LIMIT 3'
pysqldf(query)

作者图片

让我们连接两个数据帧df_featuredf_target。如果我们使用熊猫,可以使用pd.concat方法。

pd.concat([df_feature, df_target], axis = 1).head()

作者图片

如果我们使用 SQL,我们将需要创建一个运行行号列,并使用行号连接两个表。由于 Pandasql 在幕后使用了 SQLite,所以 SQLite 表会默认创建rowid列。该列包含从 1 开始的增量整数值。

query = 'SELECT rowid, * FROM df_feature LIMIT 3'
pysqldf(query)

作者图片

我们现在可以在rowid列上连接两个表。输出可以赋给另一个变量,这个变量以后可以再次用 Pandasql 查询。

query = 'SELECT * FROM df_feature INNER JOIN df_target ON df_feature.rowid = df_target.rowid'
df = pysqldf(query)
df.head()

作者图片

以下是我们可以执行的其他操作的示例。

寻找不同目标类的平均萼片长度。注意"sepal length (cm)"被引号包围了。只有当列名中有空格时,才需要这样做。

query = 'SELECT target, AVG("sepal length (cm)") AS mean_sepal_length FROM df GROUP BY target'
pysqldf(query)

作者图片

我们还可以利用 python 中的 f 字符串来创建动态 SQL 查询字符串。

COL_NAME = '"sepal length (cm)"'
ALIAS = 'sepal_length'
AGG = 'MAX'query = f"SELECT {AGG}({COL_NAME}) AS {ALIAS} FROM df"
pysqldf(query)

作者图片

Pandasql 的局限性

  1. 由于 Pandasql 使用 SQLite,它受到 SQLite 的所有限制。例如,SQLite 不实现右外连接或全外连接。
  2. Pandasql 只执行查询,它不能执行 sql 操作,如更新、插入或更改表。

结论

Pandasql 是对数据科学家工具箱的一个很好的补充,对于那些喜欢 sql 语法而不是 Pandas 的数据科学家来说。在本文中,我们讨论了如何使用 Pandasql 查询 Pandas Dataframe 以及它的一些局限性。

加入 Medium 阅读更多这样的故事。

使用 intake-salesforce 在 Python 中查询 Salesforce 数据

原文:https://towardsdatascience.com/query-salesforce-data-in-python-using-intake-salesforce-b80c7a714943?source=collection_archive---------32-----------------------

【salesforce 的进气驱动

杰克·沃德Unsplash 上的照片

Salesforce 数据库可能非常混乱。下图说明了 Salesforce 中一些数据表之间的关系。如您所见,数据表(即对象)之间的关系可能很复杂,很难处理。我之前写过一篇关于如何通过 Python API simple-salesforce 使用 Salesforce 对象查询语言(SOQL) 理解和查询 Salesforce 数据的博文。Salesforce 对象查询语言(SOQL)是一种类似 SQL 的语言,专门为 Salesforce 中的关系数据设计,对于不熟悉 Salesforce 数据库的人来说,它不是最容易理解和编写的。因此,我编写了一个导入驱动程序来加载 Salesforce 数据,以帮助人们方便地查询 Salesforce 数据。

作者创造的形象

什么是摄入?

Intake 是 Anaconda 在 2018 年开发的。这是一个轻量级的数据目录和数据访问库。它帮助用户更有效地访问数据和共享数据。具体来说,Intake 具有以下关键特征,这些特征令数据科学家们趋之若鹜:

  • 摄入加载来自各种数据源的数据,并在摄入目录中提供您的数据的描述。
  • 缓存有助于更快地加载数据。
  • Intake 允许用户轻松创建数据包。
  • 图形界面有助于轻松浏览数据。
  • Intake 从各种数据格式加载数据,并允许用户为新的文件格式创建插件/驱动程序。
  • 入口生态系统目前包括 30 多个入口驱动,帮助用户使用统一的 API 加载来自不同来源的数据。

关于如何使用进气的更多信息可在进气文件中找到。

如何使用 intake-salesforce?

在我们开始了解 intake-salesforce 之前,我们需要获得一些 salesforce 凭据信息:

  • 用户名
  • 密码
  • 安全令牌:可在“设置—个人—重置我的安全令牌”下找到
  • 实例:您的公司名称. lightning.force.com

要安装 intake-salesforce,请运行以下程序

conda install intake-salesforce -c conda-forge

或者pip install intake-salesforce

为了在 Salesforce 中获得表的目录,我们使用了intake.open_salesforce_catalog方法:

现在我们有一个包含 1003 个表的列表,我们可以使用catalog.TABLE_NAME.read()读取这些表,并在内存中返回 Pandas 数据帧。例如,让我们读入“账户”表(非真实数据):

帐户表中有 48 列。请注意,以 __c 结尾的列名都是 Salesforce 中的自定义对象,这意味着在这种情况下,我们的 Salesforce 管理员在帐户表对象中创建了一个名为“ARR__c”的变量对象。

类似地,我们可以读入其他表(例如 Opportunity)并将 Account 表与 Opportunity 表合并(同样,下面显示的不是真实数据)。为了明确哪些变量属于哪个表,我在帐户变量前面加了“acc_ ”,在机会变量前面加了“opp_”。这里我们看到合并后的表有 90 列。

把所有东西放在一起

我们如何以一致的方式从各种来源加载数据?我们将展示一个加载本地。csv 文件、存储在 AWS 上的一组拼花文件和 Salesforce 数据。

Intake 的关键特性和概念是目录文件,它是“一个 YAML 规范文件,包含一个描述如何加载数据源的命名条目列表”(https://Intake . readthedocs . io/en/latest/catalog . html)。在下面的“load_data.yaml”示例中,我们定义了三个数据源:example_csv_data、example_s3_data 和 salesforce_catalog。在每个数据源下,我们列出了它的引入驱动因素、参数和其他信息。请注意,我们将所有数据加载为 Pandas 数据帧,其他数据类型和分区/并行访问也可以使用 Intake。

要从这三个数据源加载数据,我们只需打开这个 yaml 文件作为目录cat,并按如下方式读入数据:

  • 阅读。csv 文件:cat.example_csv_data.read()
  • 阅读。拼花文件:cat.example_s3_data(year=xxx, month=xxx).read() 。注意,我们首先需要conda install intake-parquet s3fs python-snappy,还要注意,我们能够将参数作为参数传入。
  • 读取 Salesforce 数据:cat.salesforce_catalog显示 Salesforce 表的列表和指定表中的cat.salesforce_catalog.TABLE_NAME.read()读取。

总之,intake-salesforce 为用户提供了一种在 salesforce 中查看表格目录和读取特定数据表(如 Pandas dataframes)的简单方法。摄入目录允许用户一致地从各种来源加载数据。希望你喜欢使用摄入和摄入销售力量。谢谢!

鸣谢:特别感谢 Martin Durant、Albert DeFusco 和 Stan Seibert 在开发 intake-Salesforce 和撰写本文时提供的指导和反馈。

参考资料:

https://intake.readthedocs.io/https://www . anaconda . com/intake-taking-the-pain-out-of-data-access/https://martindurant.github.io/blog/flurry-of-drivers/https://medium . com/swlh/query-sales force-data-in-python-e 290 a 00 E3 CBAhttps://developer . sales force . com/docs/atlas . en-us . soql _ sosl . meta/soql _ sosl/sforce _ API _ calls _ calls

作者索菲亚·杨 2021 年 7 月 1 日

使用 Julia 查询亚马逊雅典娜

原文:https://towardsdatascience.com/querying-amazon-athena-using-julia-607250312232?source=collection_archive---------46-----------------------

实践教程

照片由拍摄于 Unsplash 上的

Julia 是一种相当现代的科学编程语言,它是免费的、高级的、快速的,并且捆绑了一系列令人敬畏的特性,使 Julia 能够再次出色地处理数据。该语言借鉴了 Python、MATLAB 和 R【1】等语言的灵感。如果你还没有读过我的文章“你应该学习朱莉娅的 10 个理由”,那就去看看吧!Amazon Athena 是一种交互式查询服务,它允许您使用您的老朋友 SQL 轻松分析您在亚马逊 S3 存储中收集的数据。Athena 很棒,因为它是无服务器的,这意味着不需要管理基础设施,您只需为运行的查询付费。

听起来很棒,对吧?Julia 非常适合处理数据,Athena 非常适合查询数据,我们如何将两者结合使用?与其手动导出 CSV 文件并使用CSV.jl在 Julia 中加载 CSV 文件,我将向您展示如何使用 Athena 直接从 Julia 中查询数据,使用DataFrames.jl将结果数据集加载到 DataFrame 中供您使用。

朱莉娅是新来的吗?继续从 Julia 网站这里获取你的特定操作系统(OS)的官方二进制文件。如果你对详细的文章不感兴趣,只是想看看完整的代码解决方案,请查看GitHub gist。

开放式数据库连接性

开放式数据库连接(ODBC)是用于访问数据库管理系统(DBMS)的标准应用程序编程接口(API)。由于它的开放标准性质,许多数据库都支持它,许多工具使用它来连接这样的数据库。这意味着这篇文章中展示的连接 Amazon Athena 的方法实际上也可以用于其他数据库,只需要进行一些配置更改。

AWS 为 Athena 提供了一个 ODBC 驱动程序,您可以使用它来访问您的数据。确保从官方的用户指南中为您的操作系统安装相应的 ODBC 驱动程序。如果你遇到任何问题和/或需要一些本文中没有提到的额外配置,你也可以选择阅读 ODBC 驱动程序的文档

配置

我们可以使用配置文件,简称 config,来配置 Julia 应用程序的参数和初始设置。这些参数的范围可以从机器学习模型参数到数据库凭证,如我们示例中的那些。配置文件在许多编程语言中广泛使用,它提供了一种无需更改任何代码就能更改应用程序设置的简洁方法。

对于这个例子,我们将使用Configs.jl【2】来加载我们的配置文件,该文件也支持基于配置位置、ENV变量映射和函数调用的级联覆盖。

数据帧

DataFrame 表示一个包含行和列的数据表,就像电子表格一样。Julia 包DataFrames.jl提供了一个 API,我们可以用它来处理 Julia 中的表格数据。类似于 Python 中的 Pandas,它的设计和功能非常相似,然而由于 Julia 的高度模块化,Julia 中的 DataFrames 与大量不同的包紧密集成,如Plots.jlMLJ.jl等等

雅典娜. jl

闲聊够了。让我们首先创建一个新目录来存放我们的代码,并将其命名为Athena.jl。我们还将添加一个来直接存储我们的数据库配置文件configs,另一个来存储我们的 Julia 代码src,还有一个来存储我们的 SQL 脚本sql到我们新创建的src目录中。最后,我们将创建三个文件,一个名为Athena.jl,这将是我们的 Julia 脚本,另一个名为query.sql,用于我们的 SQL 查询,最后一个名为default.yml,用于我们的数据库配置和机密。

~ $ mkdir Athena.jl 
~ $ cd Athena.jl 
~/Athena.jl $ mkdir src src/sql configs 
~/Athena.jl $ touch src/Athena.jl 
~/Athena.jl $ touch src/sql/query.sql 
~/Athena.jl $ touch configs/config.yml

继续在你最喜欢的 IDE 中打开Athena.jl目录,我的是 Visual Studio 代码【3】这是我用的。打开configs/default.yml文件,这样我们可以添加我们的配置。您需要添加您的s3 locationuidpwd。如果您的 AWS 云基础设施设置在不同的地区,您可能还需要更改region值。根据操作系统,您可能还需要更改数据库驱动程序的路径。如果你运行的是像我这样的 macOS,你可能不需要做任何改变,只需要确保文件存在于那个目录中。

database:
  name: "SimbaAthenaODBCConnector"
  path: "/Library/simba/athenaodbc/lib/libathenaodbc_sb64.dylib"
  driver: "SimbaAthenaODBCConnector"
  region: "eu-west-1"
  s3_location: ""
  authentication_type: "IAM Credentials"
  uid: ""
  pwd: ""

src/sqlquery.sql文件中添加您想要运行的 SQL 查询,并在您的 REPL【4】中打开src/Athena.jl Julia 脚本来运行以下内容。

import Pkg
Pkg.activate(".")

Pkg.add(["ODBC", "DataFrames", "Configs"])

using ODBC, DataFrames, Configs

如果你是第一次接触 Julia,你可能会问的第一件事是“importusing有什么区别?”嗯import的工作方式和 Python 中的一样,import MyModule会将【5】功能纳入范围,这些功能仍然可以使用MyModule来访问,比如MyModule.xMyModule.y,有点像在 Python 中使用import numpy,然后运行numpy.array([])。然而,Julia 中的using相当于在 Python 中运行from numpy import *,这将把 Numpy 的所有函数都纳入范围,允许您在 Python 中运行array([])。如果你想在 Julia 中只导入特定的函数到 scope 中,你需要做的就是import MyModule: x, y,这将使函数xy在 scope 中可访问。

Pkg是包管理器捆绑了 Julia。我们不仅可以用它来安装包,还可以创建虚拟环境【6】来运行我们的代码。通过运行Pkg.activate("."),我们告诉 Julia 的包管理器在当前直接激活一个新的虚拟环境,以便我们安装我们的 Julia 依赖项。这将自动创建两个新文件,project.tomlManifest.toml,前者将列出我们所有的直接依赖,而后者将列出我们的直接依赖所依赖的间接依赖。这两个文件将允许任何其他开发人员重新创建与我们相同的虚拟环境,这对于重现这个例子来说将是很好的。

接下来将使用Pkg.add函数添加我们想要安装的 Julia 包列表,并使用using命令将它们导入 scope。现在我们已经用运行这个例子所需的依赖项设置了我们的 Julia 环境,我们可以开始使用Configs.jlgetconfig函数从我们的配置文件加载配置。这个函数返回一个NamedTuple,它是一个 Julia 类型,有两个参数:一个给出字段名的符号元组,一个给出字段类型的元组类型。这意味着我们可以使用配置文件本身的字段名,通过使用.参数直接访问它们。在最后一步中,我们将把 SQL 脚本的内容读入 Julia,并把它们解析成一个字符串供我们以后使用。

database = getconfig("database")
name = database.name
path = database.path
driver = database.driver
region = database.region
s3_location = database.s3_location
authentication_type = database.authentication_type
uid = database.uid
pwd = database.pwd
sql = open("src/sql/query.sql" ) do file
    read(file, String)
end

在我们开始查询 Athena 之前,ODBC.jl要求我们通过将名称和路径传递给驱动程序来添加我们之前安装的 Athena 驱动程序。我们还需要构建用于连接 Athena 的连接字符串。Julia 拥有对字符串插值的本地支持,允许我们使用任何我们可能需要的变量来构造字符串,而不需要串联【7】。连接字符串是特定于您正在使用的数据库的,所以如果您不打算连接到 Athena,您必须查找您正在使用的驱动程序的文档来构造所需的连接字符串。

# locate existing ODBC driver shared libraries or download new
# then configure
ODBC.adddriver(name, path)

# build connection string
connection_string = """
Driver=$driver;
AwsRegion=$region;
S3OutputLocation=$s3_location;
AuthenticationType=$authentication_type;
UID=$uid;
PWD=$pwd;
"""

好吧,我保证有趣的部分来了。既然我们已经完成了枯燥的配置设置,我们可以继续建立连接并查询 Athena!

conn = DBInterface.connect(ODBC.Connection, connection_string)

# execute sql statement directly
# then materialize results in a DataFrame
df = DBInterface.execute(
    conn,
    sql
) |> DataFrame

297×3 DataFrame
 Row │ dt                       table_name  n_rows
     │ DateTime…?               String?     Int64?
─────┼────────────────────────────────────────────────
   1 │ 2021-05-08T06:46:24.183  Table_A     196040
   2 │ 2021-05-08T06:46:24.183  Table_B     28172242
   3 │ 2021-05-08T06:46:24.183  Table_C     27111764
   4 │ 2021-05-06T06:46:29.916  Table_A     196041
   5 │ 2021-05-06T06:46:29.916  Table_C     27080936
   6 │ 2021-05-23T06:46:26.201  Table_A     196034
  ⋮  │            ⋮                             ⋮
 293 │ 2021-03-03T14:47:56.910  Table_B     27421193
 294 │ 2021-03-03T14:47:56.910  Table_C     26379105
 295 │ 2021-04-27T06:46:34.887  Table_A     196046
 296 │ 2021-04-27T06:46:34.887  Table_B     28016354
 297 │ 2021-04-27T06:46:34.887  Table_C     26960853
                                286 rows omitted

简单对吗?Julia 还能够毫不费力地解析我们的数据帧中的列类型。您现在可以使用Plots.jl运行自由绘图,使用MLJ.jl训练机器学习模型,并将转换后的数据存储回 Athena。

# load data into database table
ODBC.load(df, conn, "table_nme")

结论

你能用 Python 完成所有这些吗?当然,事实上有很多这样的帖子,你可以找到如何做到这一点。然而,我认为 Julia 是一种有趣的语言,值得学习,并且可以提供比 Python 更多的好处。

然而,Julia 仍然是一种相当新的语言,尽管它的受欢迎程度稳步上升【8】【9】,但它仍然缺乏更广泛使用的语言(如 Python)所提供的工具和社区支持。看看 StackOverflow,关于 Python 的问题数量远远超过了关于 Julia 的问题数量。把这归因于 Julia 更容易使用可能很有诱惑力,但是——尽管我更看重 Julia 而不是 Python——事实并非如此。这是一个选择偏差的例子,Python 上的问题数量相对于 Julia 上的问题数量明显更高的实际原因仅仅是因为使用 Python 的人比使用 Julia 的人多。StackOverflow 上社区问题和资源的不足实际上导致了 Julia 更陡峭的学习曲线。

然而,这并不完全是悲观的。我确实认为作为一种语言,Julia 比 Python 更容易阅读,因为大多数 Julia 包都是用纯 Julia 编写的。尽管 Python 可能很流行,但它并不总是意味着它是每个人或每个工作的最佳工具。随着 Julia 的受欢迎程度和用户群的持续增长,工具和社区支持最终会赶上来。最终,哪种语言最适合您的需求取决于您。

但是,如果您发现自己想要使用 Julia 中没有的 Python 包,PyCall.jl是一个很好的包,它提供了从 Julia 语言直接调用 Python 并与 Python 完全互操作的能力【10】。这很简单,因为:

import Pkg
Pkg.activate(".")

Pkg.add("PyCall")

using PyCall

# create a variable 'x' of type Float64
x = 1
x = Float64(x)

# use Anaconda to install scipy and it's dependencies
so = pyimport_conda("scipy.optimize", "scipy")

# use scipy's newton function to find a zero of sin(x) 
# given a nearby starting point 1
so.newton(x -> cos(x) - x, 1) # 0.7390851332151607
  • [1] QuantEcon 有一个很棒的比较 MATLAB、Python 和 Julia 的表格。
  • [2] Configs.jl 是由 Michael Jonker 维护的开源 Julia 包。
  • [3] Visual Studio 代码有一个优秀的扩展来帮助你开发由 Julia 社区维护的 Julia 应用程序。
  • [4]一个读取-评估-打印循环(REPL),是一个简单的交互环境,接受代码输入,执行它们,并返回结果。
  • [5]变量的作用域是变量可见的代码区域。变量作用域有助于避免变量命名冲突。
  • [6]虚拟环境通过避免可能出现的版本冲突,帮助我们确保我们的应用程序及其依赖项独立于其他应用程序。
  • [7]在 Julia 中,字符串将通过使用*操作符"Hello " * "world"连接起来
  • [8] 朱莉娅被越来越多的人接受
  • 【9】茱莉亚更新:领养量持续攀升;是 Python 挑战者吗?
  • [10] Julia 不仅拥有与 Python 的出色互操作性,还拥有大量其他语言的互操作性。

原载于 2021 年 6 月 8 日 https://gabriel.gaucimaistre.com**的

QuestDB 与时标 b

原文:https://towardsdatascience.com/questdb-vs-timescaledb-38160a361c0e?source=collection_archive---------21-----------------------

照片由 Guillaume JailletUnsplash

数据工程,时间序列

如何使用时序基准测试套件比较 QuestDB 和 TimescaleDB 的数据库读写性能

介绍

在这个联系空前紧密的世界里,数十亿用户正在产生比以往更多的数据。从人类通信到我们创造的数字足迹,物联网传感器已经无处不在,金融交易也实现了数字化。我们有大量以时间为中心的数据,我们正在努力跟上这一切。时间系列数据库正在崛起。OSS 项目,如 QuestDB、InfluxDB、TimescaleDB,以及基于云的解决方案,如 Amazon Timestream 等。的需求量比以往任何时候都大。时间系列数据库已经正式成熟。

所有这些产品都在争夺时间序列领域的更大空间,在这样做的过程中,它们互相做得更好。本文将考察两个主要的时间序列数据库,并使用一个名为 TSBS 的开源基准测试工具——时间序列基准测试套件对它们进行比较。这个基准测试套件基于最初在 InfluxDB 开发的测试脚本,后来被其他主要的 timeseries 数据库增强,目前由 TimescaleDB 维护。

什么是时间序列基准测试套件(TSBS)?

对于 MySQL 和 PostgreSQL 等传统数据库,许多流行的选项如 HammerDBsysbench 都是衡量数据库读写性能的标准工具。不同类型的数据库都有类似的工具。当基准测试工具通过创建真实的突发和读取流来模拟真实场景时,性能测试是有意义的。时间序列数据库的访问模式与传统数据库非常不同——这就是为什么我们需要像 TSBS 这样的工具。

TSBS 目前支持两种负载:

  • 物联网 —模拟卡车运输公司的传感器生成的物联网数据。想象一下,使用车队中每辆卡车的实时诊断数据来跟踪一个货运车队。
  • DevOps —模拟服务器生成的数据使用情况,跟踪内存使用情况、CPU 使用情况、磁盘使用情况等。想象一下,查看 Grafana 仪表板上的这些指标,并获得违规警报。

先决条件

对于本教程,我们将使用 DevOps 选项。首先,您需要遵循以下步骤:

注: 该基准测试是在 AWS EC2 上的 16 核英特尔至强白金 8175M CPU @ 2.50GHz 和 128 GB RAM 上完成的。

使用 TSBS 测试时序数据库性能

我们将分四个阶段测试这两个数据库的性能:

  • 生成一天的 DevOps 数据,其中每 10 秒收集 200 个设备的 9 个不同指标。将根据 QuestDB 和 TimescaleDB 各自的格式分别为它们生成数据。为此使用**tsbs_generate_data**实用程序。
  • 加载生成的数据。使用**tsbs_load_questdb****tsbs_load** 实用程序分别将数据加载到 QuestDB 和 TimescaleDB 中。这使我们能够测试每个系统的接收和写入速度。
  • 生成查询,在 QuestDB 和 TimescaleDB 的加载数据上运行。为此,请使用**tsbs_generate_queries** 实用程序。
  • 分别使用**tsbs_run_queries_questdb****tsbs_run_queries_timescaledb**在 QuestDB 和 TimescaleDB 上执行生成的查询。

让我们一个接一个地检查每个步骤的脚本。

生成测试数据

在本教程中,我们将基准测试的**scale**限制在 200 台设备。如上所述,数据将在一天内生成,每 10 秒钟跟踪一百个设备中每一个设备的九个指标。使用**tsbs_generate_data**命令,您可以为任何支持的数据库和用例生成测试数据。

注意: 生成的数据会占用很多空间。您可以根据基准测试要求和磁盘空间的可用性来扩大或缩小规模。

注意: 由于 TimescaleDB 和 QuestDB 使用的格式不同,为数据生成的文件大小也会不同。QuestDB 使用的流入线协议格式比其他任何格式都要简单得多。

加载数据

加载数据甚至比生成数据更简单。对于 TimescaleDB,您可以使用通用实用程序**tsbs_load**。对于 QuestDB,您可以使用**tsbs_load_questdb**实用程序,因为它支持一些特定于 QuestDB 的标志,比如用于流入线路协议绑定端口的--**ilp-bind-to**和表示 QuestDB 的 REST 端点的**--url**。您可以使用以下命令将数据分别加载到 TimescaleDB 和 QuestDB 中:

注意: 请按照 TimescaleDB 的 config.yaml 文件的说明操作。

为了更好地了解负载性能,您可以尝试更改**--workers** 参数。请确保两个数据库的基准参数和条件是相同的,这样您就可以得到公平的比较。

时标 DB 加载/写入性能

./tsbs_load load timescaledb --config=./config.yaml
...
Summary:
loaded 174528000 metrics in 59.376sec with 8 workers (mean rate 2939386.10 metrics/sec)
loaded **15552000** rows in **59.376sec** with **8 workers** (mean rate 261925.49 rows/sec)

QuestDB 加载/写入性能

./tsbs_load_questdb --file /tmp/questdb-data --workers 8
...
Summary:
loaded 174528000 metrics in 18.223sec with 8 workers (mean rate 9577373.06 metrics/sec)
loaded **15552000** rows in **18.223sec** with **8 workers** (mean rate 853429.28 rows/sec)

在这种情况下,有八个工作线程的 QuestDB 的写性能比 TimescaleDB 快 ~3.2x 。关于这次基准测试的完整输出,请访问这个 GitHub 库

生成查询

TSBS 为 QuestDB 和 TimescaleDB 生成的数据集包含 200 台主机的指标。为了查询所有主机中某个指标高于阈值的所有读数,我们将使用查询类型**high-cpu-all**。要在一天内生成 1000 个不同时间范围的查询,您需要运行以下命令:

在本教程中,我们只运行一种类型的读取查询。您可以从中选择不同类型的查询来测试读取性能。

执行查询

现在我们已经生成了数据,并将其加载到 QuestDB 和 TimescaleDB 中,还生成了我们想要运行的基准测试查询,我们最终可以使用以下命令执行读取性能基准测试:

注意: 确保在运行命令之前已经正确生成了查询。为此,您可以运行 ***less /tmp/timescaledb-queries-high-cpu-all*** ***less /tmp/queries_questdb-high-cpu-all***

时标 DB 读取性能

Run complete after **1000 queries** with **8 workers** (Overall query rate **25.78 queries/sec**):
TimescaleDB CPU over threshold, all hosts:
min: 222.49ms, med: 274.94ms, mean: 308.60ms, max: 580.13ms, stddev: 73.70ms, sum: 308.6sec, count: 1000
all queries :
min: 222.49ms, med: 274.94ms, mean: 308.60ms, max: 580.13ms, stddev: 73.70ms, sum: 308.6sec, count: 1000
wall clock time: **38.827242sec**

QuestDB 读取性能

Run complete after **1000 queries** with **8 workers** (Overall query rate **70.18 queries/sec**):
QuestDB CPU over threshold, all hosts:
min: 92.71ms, med: 109.10ms, mean: 113.32ms, max: 382.57ms, stddev: 19.34ms, sum: 113.3sec, count: 1000
all queries :
min: 92.71ms, med: 109.10ms, mean: 113.32ms, max: 382.57ms, stddev: 19.34ms, sum: 113.3sec, count: 1000
wall clock time: **14.275811sec**

QuestDB 执行查询的速度比 TimescaleDB 快 2.7 倍。

QuestDB 与时标 b

总结一下读写基准测试的结果,我们可以说 QuestDB 的写速度明显快于 TimescaleDB,读速度也快得多。当我们谈论读取性能时,只使用一种类型的查询可能是不公平的,这就是为什么您可以尝试自己运行 TSBS 套件来处理这两个数据库的不同类型的查询。总结如下:

QuestDB 执行~ 对于写入/加载工作负载,比 TimescaleDB 快 320%

对于读取/分析工作负载,QuestDB 的执行速度比 TimescaleDB 快 270 %。

结论

TSBS 是时间序列数据库基准测试的事实标准。在本教程中,您学习了如何使用 TSBS 通过轻松生成测试数据和模拟实际的读写负载来比较两个 timeseries 数据库。如前所述,TSBS 目前支持 DevOps 和 IoT(车辆诊断)的测试数据生成。您可以创建您的测试数据生成脚本来创建更多的用例,例如,实时天气跟踪、交通信号、金融市场等等。

伯特问答

原文:https://towardsdatascience.com/question-and-answering-with-bert-6ef89a78dac?source=collection_archive---------5-----------------------

使用预先培训的变压器进行简单、先进的问答

照片由玛丽娜·维塔莱Unsplash 拍摄

在不太遥远的过去,向机器提问并得到答案一直是科幻小说的内容。现在,事情发生了变化,我们发现自己在任何地方都在使用 Q & A 系统——甚至没有意识到这一点。

谷歌搜索是最好的例子——虽然在大多数情况下,谷歌被用来查找信息,并会简单地为你指出正确的方向,但许多其他查询都是直接的问题——对此谷歌通常会提供直接的答案:

这是常识吗..?

谷歌轻松识别数学问题

我们甚至可以得到更具体的信息,并根据地点和时间询问具体的信息

当然,谷歌领先了。但这并不意味着我们不能产生伟大的问答系统——我们可以——我们甚至可以使用谷歌在搜索中使用的完全相同的模型。

在本文中,我们将学习如何使用最先进的 transformer 模型进行问答。我们将了解 Google 的 Bert 以及如何使用它。

简而言之,我们将涵盖:

**> HuggingFace's Transformers**
  - Installation**> Setting-up a Q&A Transformer
**  - Finding a Model  - The Q&A Pipeline
    1\. Model and Tokenizer Initialization
    2\. Tokenization
    3\. Pipeline and Prediction

如果你更喜欢视频,我在这里也涵盖一切:

拥抱脸的变形金刚

首先,现代 NLP 被这些不可思议的叫做变形金刚的模型所主宰。这些模型非常出色,是相对较新的发展(第一篇描述变压器的论文出现在 2017 年)。

第二,变形金刚在现实世界中的实现几乎完全是通过一个名为transformers的库来完成的,这个库是由一群自称为 HuggingFace 的人构建的。

变形金刚图书馆如此受欢迎有很多原因。但我特别想到了三个因素:

  1. 开源——这个图书馆是开源的,它有一个非常活跃的社区
  2. 易用性 —我们可以用大约五行代码开始使用来自谷歌、OpenAI 和脸书的最先进的模型
  3. 模型——该库有难以想象的大量可用模型,我们可以简单地下载并开始工作

装置

我们可以使用以下工具轻松地为 Python 安装变压器:

pip install transformers

然而,这个库也依赖于 PyTorch 或 TensorFlow 的安装——因为它们在后台使用。

因此,我们安装了 TensorFlow:

pip install tensorflow
*OR*
conda install tensorflow

按照此处提供的“安装 PyTorch”下的说明安装 PyTorch。

设置问答转换器

现在我们已经安装了 transformer,我们可以开始构建我们的第一个问答 transformer 脚本了。

寻找模特

HuggingFace 模型页面的屏幕截图—我们选择问题回答来过滤专门为 Q & A 训练的模型

我们先找一个模型来用。我们前往huggingface.co/models,点击左边的问答。

我们还可以搜索特定的模型——在这种情况下,我们将使用的两个模型都出现在 deepset 下。

之后,我们可以找到我们将在本文中测试的两个模型— deepset/bert-base-cased-squad2deepset/electra-base-squad2

这两个模型都是由 Deepset 构建的。艾——因此有了deepset/。他们都已经在 SQuAD 2.0 数据集上进行了 Q & A 的预训练,如最后的squad2所示。

第一个型号是bert-base,意思是伯特的基本(不大,也不小)版本。也就是说cased认为大写/小写字母是不同的。

第二个模型也是另一个叫做伊莱克特的模型的“基础”版本——我发现这是一个真正令人惊叹的问答模型

如果你对用伊莱克特替换伯特感兴趣,只需在整篇文章中用deepset/electra-base-squad2替换deepset/bert-base-cased-squad2。如果你有兴趣了解更多关于伊莱克特的工作方式,你可以看看这篇文章:

很好——我们现在需要的是模型名称deepset/bert-base-cased-squad2 —我们将很快使用它来加载我们的模型。

问答过程

我们的问答流程核心由三个步骤组成:

  1. 模型和标记器初始化
  2. 查询标记化
  3. 管道和预测

这些是基本要素,实际上,可能还有其他几个步骤,比如数据预处理或上下文检索。但是,最简单的形式是,我们只需要担心这三个步骤。

因为我们想让这个介绍尽可能简单,所以我们只担心这三个步骤。

1。 模型和记号化器初始化 —这是我们的第一步,这里我们将导入转换器并使用deepset/bert-base-cased-squad2初始化我们的模型和记号化器。

这是我们需要在模型和标记器中加载的唯一代码——非常简单。

2。 输入数据标记化 —我们已经初始化了我们的标记化器,现在是时候给它输入一些文本来转换成 Bert 可读的标记 id 了。

我们的 Bert tokenizer 负责将人类可读的文本转换成 Bert 友好的数据,称为 token IDs。

首先,记号赋予器获取一个字符串并将其分割成记号。看起来像是:

其中一些记号是新的,比如[CLS][PAD]——这些是特殊的记号,用于向 Bert 表示不同的事物。下面是 Bert 使用的特殊令牌的完整列表。

在这个初始标记化之后,我们将这个字符串列表(标记)转换成一个整数列表(标记 id)。这是通过一个内部字典来完成的,这个字典包含了 Bert 理解的每一个令牌。

这些标记中的每一个都映射到一个唯一的整数值,上面的特殊标记表中列出了其中的一些映射。正是这些令牌 id 被输入到 Bert 中。

3。 管道和预测 —现在我们已经初始化了我们的模型和标记器,并了解了我们的标记器是如何将我们人类可读的字符串转换成标记 id 列表的,我们可以开始集成和提问了!

from transformers import pipelinenlp = pipeline('question-answering', model=model, tokenizer=tokenizer)

我们可以这样开始提问:

现在,这看起来很简单——但这确实是我们编写问答模型和开始提问所需要的。

然而,让我们稍微分解一下这个管道函数,以便我们能够理解实际发生了什么。

首先,我们将经过questioncontext。当将这些输入到 Q & A 模型中时,需要一个特定的格式——如下所示:

[CLS] <context> [SEP] <question> [SEP] [PAD] [PAD] ... [PAD]

因此,在我们的示例中,合并后的输入看起来像这样:

然后将其转换为令牌 ID 格式:

这将会传递给伯特。在这一点上,Bert 做了一些语言上的魔法来识别答案在上下文中的位置。

一旦 Bert 做出决定,它将返回答案的跨度——即开始标记索引和结束标记索引。在我们的例子中,它看起来像这样(直到“Answer span =[22:24]”:

流水线过程

接下来,我们的管道将根据 Bert 的起始端令牌索引预测提取相关的令牌 id。然后,这些令牌 id 将被反馈到我们的令牌化器中,被解码成人类可读的文本!

这就是我们的答案!

就这样,我们建立了我们的第一个问答变压器模型!

当然,我们在这里所做的通常只是一个更大难题的一部分。最有可能的是,您还需要考虑其他一些事情,例如:

  • 上下文提取——你总是会有一两段文字包含你的答案吗?还是更有可能是几页甚至更多?(费斯+ DPR 在这方面很棒)
  • 微调你的模型——预先训练的模型很好,但在应用于语言/格式略有不同的 Q & A(如金融术语或推文)时可能会有困难。

这些是一些额外的步骤,在向前推进和开发真实世界的用例时可能会被证明是至关重要的,但是,它们并不总是必需的,我们在这里构建的内容代表了问答过程的核心。

如果你想了解更多关于问答变形金刚的知识——比如上下文提取和微调——我已经在 Medium 和 YouTube 上讨论过(并将继续讨论)。

否则,我希望你喜欢这篇文章!我很乐意听到你的反馈、问题或想法——所以欢迎在下面留下评论或通过 Twitter 联系。

感谢阅读!

🤖带变压器的 NLP 课程

*所有图片均由作者提供,除非另有说明

用微调过的 BERT 回答问题

原文:https://towardsdatascience.com/question-answering-with-a-fine-tuned-bert-bc4dafd45626?source=collection_archive---------1-----------------------

…在斯坦福大学的 CoQA 数据集上使用拥抱脸变形器和 PyTorch

泰勒Unsplash 上的照片

每当我想到问题回答系统时,我首先想到的是教室——一个老师提出的问题,一个或几个学生举手🙋来回答这个问题。也就是说,回答问题对人类来说可能是一项微不足道的任务,但对机器来说却不是如此微不足道。要回答任何问题,机器都需要克服许多不同的挑战,如词汇空缺、共指消解、语言歧义等。😏为此,机器需要大量的训练数据和智能架构来理解和存储文本中的重要信息。NLP 的最新进展已经释放了机器理解文本和执行不同任务的能力。👌

在本文中,我们将一起完成一项常用的任务——问答。我们将使用拥抱脸变形金刚库中已经可用的微调 BERT 模型来回答基于 CoQA 数据集的故事的问题。我确信,通过查看代码,您会意识到使用一个微调的模型来达到我们的目的是多么容易。😁

注意: 在本文中我们将不深入讨论 BERT 架构的细节。但是,在需要或可能的情况下,我会提供解释。

文中使用的版本:火炬- 1.7.1,变形金刚- 4.4.2

让我们首先回答与本文相关的几个重要问题。

什么是抱脸和变形金刚?🤔

拥抱脸是自然语言处理(NLP)技术的开源提供商。您可以使用拥抱脸最先进的模型来构建、训练和部署您自己的模型。变形金刚是他们的 NLP 库。我强烈推荐你去看看拥抱脸团队所做的惊人的工作,以及他们收集的大量预先训练好的 NLP 模型。

什么是 CoQA?🤔

CoQA 是斯坦福 NLP 于 2019 年发布的会话式问答数据集。这是一个用于构建对话式问答系统的大规模数据集。该数据集旨在测量机器理解文本段落并回答对话中出现的一系列相互关联的问题的能力。该数据集的独特之处在于,每个对话都是通过两个人群工作者以问答的形式就一段内容进行聊天来收集的,因此,这些问题是对话式的。要了解 JSON 数据的格式,请参考这个链接。我们将使用来自 JSON 数据集的故事、问题和答案来构成我们的数据框架。

伯特是什么?🤔

BERT 是来自变压器的双向编码器表示。它是最流行和最广泛使用的 NLP 模型之一。BERT 模型可以通过查看单词前后的单词来考虑单词的完整上下文,这对于理解查询背后的意图特别有用。由于它的双向性,它对语言的上下文和流程有更深的理解,因此,现在被用于许多 NLP 任务中。关于 BERT 的更多细节以及代码。🙃

变形金刚库有很多不同的 BERT 模型。很容易从这个库中找到一个特定于任务的模型并完成我们的任务。

那么,让我们开始吧,但首先让我们看看我们的数据集。😊

斯坦福的 JSON 数据

JSON 数据有很多字段。出于我们的目的,我们将使用来自“问题”和“答案”的“故事”、“输入文本”,并形成我们的数据框架。

安装变压器

**!pip install** transformers

导入库

**import** pandas **as** pd
**import** numpy **as** np
**import** torch
**from** transformers **import** BertForQuestionAnswering
**from** transformers **import** BertTokenizer

从斯坦福网站加载数据

coqa = **pd.read_json**('[http://downloads.cs.stanford.edu/nlp/data/coqa/coqa-train-v1.0.json'](http://downloads.cs.stanford.edu/nlp/data/coqa/coqa-train-v1.0.json'))
coqa**.head()**

加载的数据集

数据清理

我们将处理“数据”列,所以让我们删除“版本”列。

**del** coqa["version"]

对于每一个问答配对,我们将把链接的故事附加到它上面。

*#required columns in our dataframe*
cols = ["text","question","answer"]*#list of lists to create our dataframe*
comp_list = []
**for** index, row **in** coqa**.iterrows()**:
    **for** i **in** **range**(**len**(row["data"]["questions"])):
        temp_list = []
        temp_list**.append**(row["data"]["story"])
        temp_list**.append**(row["data"]["questions"][i]["input_text"])
        temp_list**.append**(row["data"]["answers"][i]["input_text"])
        comp_list**.append**(temp_list)new_df = **pd.DataFrame**(comp_list, columns=cols) *#saving the dataframe to csv file for further loading*
new_df**.to_csv**("CoQA_data.csv", index=**False**)

从本地 CSV 文件加载数据

data = **pd.read_csv**("CoQA_data.csv")
data**.head()**

这是我们清理后的数据。

清理的数据

**print**("Number of question and answers: ", **len**(data))

数据集有很多问题和答案,我们来数一下。

Number of question and answers:  108647

构建聊天机器人

使用这些预先训练好的模型的最大好处是,只需两行简单的代码就可以加载模型及其标记器。😲不就是单纯的哇吗?对于文本分类这样的任务,我们需要在数据集上微调 BERT。但对于问答任务,我们甚至可以使用已经训练好的模型,即使我们的文本来自完全不同的领域,也能获得不错的结果。为了得到更好的结果,我们使用了一个伯特模型,这个模型是根据小队基准进行微调的。

对于我们的任务,我们将使用变形金刚库中的 BertForQuestionAnswering 类。

model = **BertForQuestionAnswering.from_pretrained**('bert-large-uncased-whole-word-masking-finetuned-squad')
tokenizer = **BertTokenizer.from_pretrained**('bert-large-uncased-whole-word-masking-finetuned-squad')

预计下载需要几分钟,因为 BERT-large 是一个非常大的模型,有 24 层和 340M 参数,使其成为 1.34GB 的模型。

问问题

让我们随机选择一个问题编号。

random_num = **np.random.randint**(0,**len**(data))question = data["question"][random_num]
text = data["text"][random_num]

让我们将问题和文本标记成一对。

input_ids = tokenizer**.encode**(question, text)
**print**("The input has a total of {} tokens."**.format**(**len**(input_ids)))

我们来看看这个问题和文本对有多少个令牌。

The input has a total of 427 tokens.

为了看看我们的记号赋予器在做什么,让我们打印出记号和它们的 id。

tokens = tokenizer**.convert_ids_to_tokens**(input_ids)**for** token, id **in** **zip**(tokens, input_ids):
    **print**('{:8}{:8,}'**.format**(token,id))

带有 id 的令牌

BERT 有一种处理标记化输入的独特方法。从上面的截图中,我们可以看到两个特殊的令牌[CLS]和[SEP]。[CLS] token 代表分类,它代表句子级别的分类,在我们分类时使用。BERT 使用的另一个令牌是[SEP]。它用于分隔两段文本。你可以在上面的截图里看到两个[SEP]令牌,一个在问题后面,一个在正文后面。

除了“标记嵌入”,BERT 内部还使用了“段嵌入”和“位置嵌入”。片段嵌入有助于 BERT 区分问题和文本。在实践中,如果嵌入来自句子 1,我们使用 0 的向量,否则如果嵌入来自句子 2,我们使用 1 的向量。位置嵌入有助于确定单词在序列中的位置。所有这些嵌入都被馈送到输入层。

变形金刚库可以使用pretrainedtokenizer . encode _ plus()自行创建片段嵌入。但是,我们甚至可以创造我们自己的。为此,我们只需要为每个令牌指定一个 0 或 1。

*#first occurence of [SEP] token*
sep_idx = input_ids**.index**(tokenizer**.sep_token_id**)
**print**("SEP token index: ", sep_idx)*#number of tokens in segment A (question) - this will be one more than the sep_idx as the index in Python starts from 0* num_seg_a = sep_idx+1
**print**("Number of tokens in segment A: ", num_seg_a)*#number of tokens in segment B (text)*
num_seg_b = **len**(input_ids) - num_seg_a
**print**("Number of tokens in segment B: ", num_seg_b)*#creating the segment ids*
segment_ids = [0]*num_seg_a + [1]*num_seg_b#making sure that every input token has a segment id **assert** **len**(segment_ids) == **len**(input_ids)

这是输出。

SEP token index: 8
Number of tokens in segment A: 9
Number of tokens in segment B: 418

现在让我们把它输入到我们的模型中。

*#token input_ids to represent the input and token segment_ids to differentiate our segments - question and text*
output = **model**(**torch.tensor**([input_ids]),  token_type_ids=**torch.tensor**([segment_ids]))

查看最可能的开始和结束单词,并且仅当结束标记在开始标记之后时才提供答案。

*#tokens with highest start and end scores*
answer_start = **torch.argmax**(output.start_logits)
answer_end = **torch.argmax**(output.end_logits)**if** answer_end >= answer_start:
    answer = " "**.join**(tokens[answer_start:answer_end+1])
**else:**
    **print**("I am unable to find the answer to this question. Can you please ask another question?")

**print**("\nQuestion:\n{}"**.format**(question**.capitalize()**))
**print**("\nAnswer:\n{}."**.format**(answer**.capitalize()**))

这是我们的问题和答案。

**Question:**
Who is the acas director?
 **Answer:**
Agnes karin ##gu.

哇!伯特预测到了正确的答案——“艾格尼丝·卡琳古”。但是,回复里这个“##”是什么?继续读下去!📙

伯特使用词块标记化。在 BERT 中,生僻字被分解成子字/片段。单词块标记化使用##来分隔已拆分的标记。例如:“Karin”是一个常用词,所以 wordpiece 不会拆分它。然而,“Karingu”是一个罕见的词,所以 wordpiece 将其拆分为单词“Karin”和“##gu”。注意,它在古前面加了##,表示它是拆分后的第二个词。

使用单词块标记化背后的想法是减少词汇表的大小,从而提高训练性能。想想这些词,跑,跑,跑者。如果没有单词块标记化,模型必须独立地存储和学习所有三个单词的含义。但是,通过词块标记化,这三个单词中的每一个都将被拆分为“run”和相关的“# #后缀”(如果有后缀的话,例如,“run”,“##ning”,“##ner”)。现在,模型将学习单词“run”的上下文,其余的意思将被编码在后缀中,这将从具有类似后缀的其他单词中学习。

很有趣,对吧?我们可以使用下面的简单代码来重构这些单词。

answer = tokens[answer_start]**for** i **in** **range**(answer_start+1, answer_end+1):
    **if** tokens[i][0:2] == "##":
        answer += tokens[i][2:]
  **  else:**
        answer += " " + tokens[i]

上面的答案现在将变成: Agnes karingu

现在让我们把这个问答过程变成一个简单的函数。

**def** question_answer(question, text):

    *#tokenize question and text as a pair*
    input_ids = tokenizer**.encode**(question, text)

   * #string version of tokenized ids*
    tokens = tokenizer**.convert_ids_to_tokens**(input_ids)

  *  #segment IDs*
    #first occurence of [SEP] token
    sep_idx = input_ids**.index**(tokenizer**.sep_token_id**) *#number of tokens in segment A (question)*
    num_seg_a = sep_idx+1 *#number of tokens in segment B (text)*
    num_seg_b = **len**(input_ids) - num_seg_a

    *#list of 0s and 1s for segment embeddings*
    segment_ids = [0]*num_seg_a + [1]*num_seg_b **assert** **len**(segment_ids) == **len**(input_ids)

    *#model output using input_ids and segment_ids*
    output = **model**(**torch.tensor**([input_ids]), token_type_ids=**torch.tensor**([segment_ids]))

    *#reconstructing the answer*
    answer_start = **torch.argmax**(output.start_logits)
    answer_end = **torch.argmax**(output.end_logits) **if** answer_end >= answer_start:
        answer = tokens[answer_start]
        **for** i **in** range(answer_start+1, answer_end+1):
            **if** tokens[i][0:2] == "##":
                answer += tokens[i][2:]
            **else:**
                answer += " " + tokens[i]

    **if** answer**.startswith**("[CLS]"):
        answer = "Unable to find the answer to your question."

    **print**("\nPredicted answer:\n{}"**.format**(answer**.capitalize()**))

让我们使用数据集中的一个文本和问题来测试这个函数。😛

**text** = """New York (CNN) -- More than 80 Michael Jackson collectibles -- including the late pop star's famous rhinestone-studded glove from a 1983 performance -- were auctioned off Saturday, reaping a total $2 million. Profits from the auction at the Hard Rock Cafe in New York's Times Square crushed pre-sale expectations of only $120,000 in sales. The highly prized memorabilia, which included items spanning the many stages of Jackson's career, came from more than 30 fans, associates and family members, who contacted Julien's Auctions to sell their gifts and mementos of the singer. Jackson's flashy glove was the big-ticket item of the night, fetching $420,000 from a buyer in Hong Kong, China. Jackson wore the glove at a 1983 performance during \"Motown 25,\" an NBC special where he debuted his revolutionary moonwalk. Fellow Motown star Walter \"Clyde\" Orange of the Commodores, who also performed in the special 26 years ago, said he asked for Jackson's autograph at the time, but Jackson gave him the glove instead. "The legacy that [Jackson] left behind is bigger than life for me,\" Orange said. \"I hope that through that glove people can see what he was trying to say in his music and what he said in his music.\" Orange said he plans to give a portion of the proceeds to charity. Hoffman Ma, who bought the glove on behalf of Ponte 16 Resort in Macau, paid a 25 percent buyer's premium, which was tacked onto all final sales over $50,000\. Winners of items less than $50,000 paid a 20 percent premium."""**question** = "Where was the Auction held?"question_answer(question, text)*#original answer from the dataset* **print**("Original answer:\n", data**.loc**[data["question"] == question]["answer"].values[0]))

输出:

**Predicted answer:**
Hard rock cafe in new york ' s times square**Original answer:**
Hard Rock Cafe

一点也不差。事实上,我们的伯特模型给出了更详细的回答。

这是一个小函数,用来测试 BERT 对上下文的理解程度。我只是把回答问题的过程作为一个循环来玩这个模型。💃

**text** = **input**("Please enter your text: \n")
**question** = **input**("\nPlease enter your question: \n")**while** **True**:
    question_answer(question, text)

    flag = **True**
    flag_N = **False**

    **while** flag:
        response = **input**("\nDo you want to ask another question based on this text (Y/N)? ")
        **if** response[0] == "Y":
            question = **input**("\nPlease enter your question: \n")
            flag = False
        **elif** response[0] == "N":
            **print**("\nBye!")
            flag = False
            flag_N = True

    **if** flag_N == **True**:
        **break**

而且,结果!😎

**Please enter your text:** 
The Vatican Apostolic Library (), more commonly called the Vatican Library or simply the Vat, is the library of the Holy See, located in Vatican City. Formally established in 1475, although it is much older, it is one of the oldest libraries in the world and contains one of the most significant collections of historical texts. It has 75,000 codices from throughout history, as well as 1.1 million printed books, which include some 8,500 incunabula.   The Vatican Library is a research library for history, law, philosophy, science and theology. The Vatican Library is open to anyone who can document their qualifications and research needs. Photocopies for private study of pages from books published between 1801 and 1990 can be requested in person or by mail.   In March 2014, the Vatican Library began an initial four-year project of digitising its collection of manuscripts, to be made available online.   The Vatican Secret Archives were separated from the library at the beginning of the 17th century; they contain another 150,000 items.   Scholars have traditionally divided the history of the library into five periods, Pre-Lateran, Lateran, Avignon, Pre-Vatican and Vatican.   The Pre-Lateran period, comprising the initial days of the library, dated from the earliest days of the Church. Only a handful of volumes survive from this period, though some are very significant.

**Please enter your question:** 
When was the Vat formally opened?

**Answer:**
1475
 **Do you want to ask another question based on this text (Y/N)?** Y

**Please enter your question:** 
what is the library for?

**Answer:**
Research library for history , law , philosophy , science and theology

**Do you want to ask another question based on this text (Y/N)?** Y

**Please enter your question:** 
for what subjects?

**Answer:**
History , law , philosophy , science and theology**Do you want to ask another question based on this text (Y/N)?** N

Bye!

瞧啊。很好用!🤗

我希望这篇文章能让你了解我们如何轻松地使用来自拥抱面部变形库的预训练模型并执行我们的任务。如果你想把代码看做一个笔记本文件,这里有 Github 链接。

参考文献:

  1. https://huggingface.co/
  2. https://arxiv.org/pdf/1810.04805.pdf
  3. https://arxiv.org/pdf/1808.07042.pdf
  4. https://github.com/google-research/bert/issues/44

感谢大家阅读这篇文章。请分享您宝贵的反馈或建议。快乐阅读!📗 🖌

使用 PyTorch 回答关于预调变压器的问题

原文:https://towardsdatascience.com/question-answering-with-pretrained-transformers-using-pytorch-c3e7a44b4012?source=collection_archive---------10-----------------------

学会在几分钟内用任何语言建立一个问答系统

乔恩·泰森在 Unsplash 上的照片

介绍

问答 是在 信息检索自然语言处理(NLP) 中的一个任务,调查可以用自然语言回答人类提问的软件。在 抽取问题回答 中,提供了一个上下文,以便模型可以参考它并对答案在文章中的位置做出预测。

在这篇文章中,我们将向你展示如何使用hugging face Transformers 库提供的预训练模型来实现问题回答。由于实现非常简单,您可以在几分钟内让您的问答系统快速工作!

现在,让我们开始吧!

教程概述

  • 步骤 1:安装库
  • 步骤 2:导入库
  • 步骤 3:构建问题回答管道
  • 步骤 4:定义背景和要问的问题
  • 第五步:回答问题
  • 奖金:任何语言的问题回答

步骤 1:安装库

我们将使用变形金刚库来回答问题。要安装它,只需运行:

pip install transformers

注意:如果你还没有安装,记得去 PyTorch 官方网站看看!

步骤 2:导入库

成功安装转换器后,现在可以将库导入到 python 脚本中:

from transformers import pipeline

我们将只使用[pipeline](https://huggingface.co/transformers/main_classes/pipelines.html) 模块,它是一个抽象层,提供一个简单的 API 来执行各种任务。

步骤 3:构建问题回答管道

现在,我们可以开始建设管道。要构建问答管道,我们可以简单地做:

question_answering = pipeline(“question-answering”)

这将创建一个预先训练的问题回答模型,以及它的后台标记器。这种情况下使用的默认模型是DistilBERT-base,它是在小队数据集上微调的。你可以在其论文中了解更多关于 DistilBERT 的信息。

要使用您自己的模型和标记器,您可以将它们作为modeltokenizer参数传递给管道。

步骤 4:定义背景和要问的问题

现在,是时候创建我们的上下文和我们想问模型的问题了。让我们从维基百科中抓取一个快速的机器学习定义作为上下文:

机器学习(ML)是对通过经验自动改进的计算机算法的研究。它被视为人工智能的一部分。机器学习算法基于样本数据(称为“训练数据”)建立模型,以便在没有明确编程的情况下进行预测或决策。机器学习算法被用于各种各样的应用中,例如电子邮件过滤和计算机视觉,在这些应用中,开发常规算法来执行所需的任务是困难的或不可行的。

context = """
Machine learning (ML) is the study of computer algorithms that improve automatically through experience. It is seen as a part of artificial intelligence. Machine learning algorithms build a model based on sample data, known as "training data", in order to make predictions or decisions without being explicitly programmed to do so. Machine learning algorithms are used in a wide variety of applications, such as email filtering and computer vision, where it is difficult or unfeasible to develop conventional algorithms to perform the needed tasks.
"""

问它以下问题:

机器学习模型基于什么?

question = "What are machine learning models based on?"

第五步:回答问题

最后,是时候测试我们的模型来回答我们的问题了!我们可以通过将上下文和问题作为参数传递给实例化的管道来运行问答模型,并打印出结果:

result = question_answering(question=question, context=context)print("Answer:", result['answer'])
print("Score:", result['score'])

输出应该是:

答案:样本数据,
得分:0 . 46860 . 46866868661

从输出中,我们应该能够看到作为“样本数据”的答案,这是正确的,还可以看到它的置信度得分,在这种情况下,我认为它相当高。

奖金:任何语言的问题回答

等等,但是你可能会问,除了英语,我们如何实现其他语言的问答?在你走之前,我想这可能是你想知道的。

幸运的是,我们有一个由我们亲爱的社区发布的模型库,这些模型可能已经用你们的语言进行了问答训练。我们可以去 Huggingface 模特网站查看可供问答的模特。

假设我们想用中文回答问题。我们可以使用经过多种语言训练的多语言模型。例如,这个多语言 BERT 是在 Deepmind 的 xQuAD 数据集(一个小队数据集的多语言版本)上训练的,它支持 11 种语言:阿拉伯语、德语、希腊语、英语、西班牙语、印地语、俄语、泰语、土耳其语、越南语和中文。

现在,根据模型文档,我们可以通过指定其modeltokenizer参数直接在管道中构建模型,如下所示:

question_answering = pipeline("question-answering", model="mrm8488/bert-multi-cased-finetuned-xquadv1",
    tokenizer="mrm8488/bert-multi-cased-finetuned-xquadv1")

然后,让我们将上下文设置为:

机器学习是人工智能的一个分支。 是个人很热门的专业。

context = """机器学习是人工智能的一个分支。 是个人很热门的专业。"""
## Machine Learning is a branch of Artificial Intelligence. It is a popular major.

要问的问题是:

机器学习是什么的分支?

question = "机器学习是什么的分支?"
## What is Machine Learning a branch of?

之后,我们可以使用与前面相同的代码来运行系统:

result = question_answering(question=question, context=context)print("Answer:", result['answer'])
print("Score:", result['score'])

系统将输出:

Answer: 机器学习是人工智能的一个分支。
Score: 0.9717233777046204

懂中文的就知道输出答案翻译成“机器学习是人工智能的一个分支”,没错!

结论

今天就到这里吧!现在,您应该知道如何使用预先训练的模型实现任何语言的问答系统。如果你想一次性查看全部代码,我在下面附上了一个 Jupyter 笔记本:

如果你有任何问题或讨论,欢迎在下面评论!希望很快见到大家。如果你喜欢我的作品,请随意浏览我以前的其他帖子:

参考

[1] 变形金刚 Github ,拥抱脸

[2] 变形金刚官方文档,拥抱脸

[3] Pytorch 官网,艾研究

[4] Sanh,Victor,et al. “蒸馏伯特,伯特的蒸馏版:更小,更快,更便宜,更轻。” arXiv 预印本 arXiv:1910.01108 (2019)。

[5] 小队官网,斯坦福

[6] XQuAD 官方 Github ,谷歌 Deepmind

机器学习客户问我的问题

原文:https://towardsdatascience.com/questions-ive-been-asked-by-machine-learning-clients-e26f39e1e257?source=collection_archive---------25-----------------------

根据我作为初创公司和企业家的机器学习顾问的经验,简单回答一下

图片由 安德里亚·皮亚卡迪奥 来自 像素

在为一家初创公司经营 ML 年后,我决定拓展业务,为企业家提供咨询。

这不是为了钱,而是为了获得更多的经验。无论如何,早期创业公司都没有钱烧钱。

随着时间的推移,同样的问题会在最初的讨论中出现。

以下是其中的几个。

使用机器学习需要大量数据吗?

“比你想象的要少。”

听到这个消息,客户总是又惊又喜。

请注意,我们构建模型不是为了检测金融欺诈或预测天气。问题通常很简单(例如:为新产品定价或估计 X 的风险),解决问题的数据也很容易获得。

我见过用一百个数据点解决的分类和回归问题。

如果您的问题可以通过学习几个输入和一个输出之间的模糊关系来解决,并且您已经有了深厚的领域专业知识,那么 ML 解决方案就不远了。

在这种情况下,简单的统计或线性回归(如果你称之为 ML)通常就足够了。可能需要的数据很少。

机器学习应该用 AWS,Azure,还是 GCP?

“不是为了构建概念证明。”

第一个目标应该是尽快得到结果。即使他们很烂。

训练一个模型做出一个很差但不完全疯狂的预测。这成为你的基线,然后你迭代改进它。

90%的情况下,你可以在装有 Python、Jupyter、Pandas 和 Sklearn 的 MacBook 上完成这项工作。不需要什么花哨的东西。

从重型基础设施开始只会增加移动部件,而不会增加价值。您可以在以后将模型投入生产时添加这些内容。

跑 ML 车型贵吗?

“如果你做得对,就不会。”

但这取决于几个标准。

  • 输入示例的大小。一个输入的例子是几个数据点,还是一整部小说的文本?
  • 型号的大小。是逻辑回归还是深度神经网络?
  • 时间敏感性。预测需要即时进行吗?还是都可以在凌晨 2 点运行?

如果您足够幸运,能够使用回归和小例子,那么您可以在 AWS Lambda 上以接近免费的方式进行设置。

如果你正在运行一个神经网络,其中的数据需要大量的预处理,但在午夜批量预测是可以的,你也可以非常便宜地完成。

但是如果你需要一个复杂的模型来对毫秒级的通知进行分类,那可能会很昂贵。

我们可以用机器学习做什么?

“问问你自己:你在手工做哪些重复性的工作?从那里开始。”

例如,您是否对传入的数据点进行排序、标记图像,或者一次又一次地向客户提供类似的建议?

如果是的话,看看你能否用硬编码逻辑(又名。代码)。你会惊讶于传统的软件工程经常是解决方案。即使是资金雄厚的公司在谈论 ML 的同时也在幕后使用基于规则的逻辑。

如果因为关系或数据太模糊而比硬规则所能解决的更复杂,那么它可能是 ML 的一个很好的用例。

结论

考虑这是一个决定 ML 是否适合你的简短指南。

如果这里有一个主题,那就是尽可能使用最简单的解决方案。作为软件工程师,我们已经看到了不必要的复杂性。不惜一切代价避免这种情况。

你在你的组织中使用简单的机器学习来解决问题吗?

可以用指数分布回答的问题

原文:https://towardsdatascience.com/questions-that-you-can-answer-using-exponential-distribution-2af9da54dfd8?source=collection_archive---------16-----------------------

概率分布

指数分布有什么用?

丹尼尔·诺里斯在 Unsplash 上拍摄的照片

每个概率问题都依赖于一个概率分布。在何种情况下将发现概率决定了应该使用概率分布。例如,在掷硬币的实验中,有两种结果,正面和反面。假设这个实验除了投掷一枚硬币之外,没有其他条件。由于只有两种结果,即尾部和头部,这就引出了伯努利分布。然而,如果问题是“抛 30 次硬币后得到 10 个头的概率是多少”,我们需要重新考虑分布情况。这是一个可以用二项分布来回答的问题,因为二项分布由 n 个独立的伯努利试验组成。

虽然有些概率问题与试验次数有关,但其他问题则取决于时间。与试验次数相关的概率分布是离散的。然而,基于时间或间隔单位的分布是连续的概率分布。一种与时间有关的连续概率分布是指数分布。在这篇文章中,我将解释指数分布以及如何解决指数分布问题?

指数分布

怎么知道什么时候用指数分布?指数分布与特定事件发生前的时间量有关。时间可以是分钟、小时、天或您自定义的间隔。例如,它可以是公共汽车在两分钟等待后或在精确的第二分钟到达的概率。这些是我们可以用指数分布找到的概率。它是如何找到概率的,或者什么是指数分布的概率密度函数?指数分布的概率密度函数如公式 1 所示。

公式 1——指数分布的概率密度函数(图片由作者提供)。

该公式采用两个参数作为。值λ表示在一个时间间隔内发生的事件的平均数。x 代表事件发生的时刻。因此,当事件的平均发生率为λ时,f(x,λ)给出了事件在时刻 x 发生的概率。因为时刻不能为负,所以如果 x 小于零,则函数返回到零。概率基于图 1 中表示的λ值和 x 值而变化。

图 1—λ为 1/10、1/15 和 1/20 的指数分布(图片由作者提供)

例子

通过例子来理解概率分布的理论总是更好的。这也是我们要对指数分布做的。假设有一家咖啡店,顾客平均每小时点 15 次咖啡。问题应该是:“下一个咖啡订单在 5 分钟后到达的概率是多少?”基于这个问题,公式 1 中的 x 将是 5。因为 x 以分钟为单位,所以我们需要以分钟为单位找到λ值。一小时有 60 分钟。从 15/60 开始,咖啡订单每四分钟来一次(1/4)。所以,λ值是 1/4。λ= 1/4 且 x=5 的概率密度函数给出了在第五分钟获得订单的概率。我们把这些值代入公式,求第 5 分钟接单的概率值。

公式 2——计算第 5 分钟接单的概率(图片由作者提供)。

发现概率值为 0.07。然而,我们需要找到在五分钟内收到订单的可能性。当 lambda 值为 1/4 时,在所有可能的分钟内收到订单的概率值如图 2 所示。要回答这个问题,我们需要计算第 5 分钟后曲线下的面积。

图-2-基于λ1/4 P(x > 5)值的概率分布(图片由作者提供)

我们应该用积分来计算这条曲线下的面积。但是,我不想深究那么多。使用积分概率密度函数可以找到另一个公式。这个公式给出了指数分布的累积概率。这正是我们所需要的。

公式 3——指数分布的累积概率(图片由作者提供)

公式 3 给出了特定事件在单元 x 之前发生的概率,根据我们的例子,这个公式给出了 5 分钟之前接单的概率。我们还可以通过从 1 中减去 5 分钟后收到订单的概率。

公式 4—5 分钟后取单的计算(图片由作者提供)。

5 分钟后收到下一个订单的概率为 0.28。表示 5 分钟前收到下一单的概率值高于 5 分钟后收到。

这些操作也可以用 Python 和 r 来应用于下面的代码。

R: ---------------------------------# Order exactly at 5\. minute
dexp(5 , rate=1/4)# Order after 5 minutes
pexp(5 , rate=1/4, lower.tail=FALSE)Python: --------------------------from scipy.stats import expon# Order exactly at 5\. minute
expon.pdf(5 , loc=0, scale=4)# Order after 5 minutes
1 - expon.cdf(5 , loc=0 , scale=4)

结论

本文用指数分布回答了指数分布和概率问题。指数分布是基于泊松过程的概率分布。这就是为什么我强烈建议你也去寻找泊松分布。如果您有任何问题,请随时留下您的评论。

对于工程/应用科学家的角色,要问面试官的问题

原文:https://towardsdatascience.com/questions-to-ask-your-interviewer-for-engineering-applied-scientist-roles-1489d515269a?source=collection_archive---------11-----------------------

一份精选的问题清单

准备面试是找工作的一个方面,得到你想要的工作是另一个方面。我最近写了第一部分,这使得这篇文章更加重要。我见过很多人在选择哪份工作的问题上苦苦挣扎。由于大多数公司不能事先谈论他们正在做的实际工作,所以很难知道团队到底在做什么,以及加入公司后你的生活会是什么样子。

我知道在长达一小时的面试后,“那么你现在有什么问题要问我吗”这个问题可能会让人望而生畏。但重要的是,当你带着一份工作结束求职后,你的生活会是什么样子!

我整理了一份问题清单,我希望在我决定从多个工作机会中做出选择之前,我已经阅读了这些问题。大多数问题是一般性的,但也有一些针对数据科学家、ML 工程师、应用科学家、数据工程师和研究工程师角色的特定问题。请让我知道更多的问题,我会继续在这里添加(你可以给我发电子邮件或在 purvanshi.mehta11@gmail.com 上发表评论)

1.你在日常生活中使用什么技术?

这是一个重要的问题,因为大多数公司都有自己的内部技术用于生产。你会致力于这样的内部技术还是像 python/spark 或任何你喜欢的东西。从事内部技术工作实际上并不能创造可转移的技能,你可能需要付出更多努力才能跟上行业标准。

2。你的平均一天/一周是怎样的?

你会在什么时候投入最多?在这一部分可以提出几个后续问题

3。 你平均花多少小时开会?

这显然可能因不同的角色而异。比如,如果你是一名管理人员,你可能会把几乎所有的时间都花在会议上,但如果你的角色更多的是一名工程师,这将告诉你每天会与多少人互动,有多少时间花在沟通上,有多少时间花在实际工作上。第三部分将确定

4。 工作生活平衡会是什么样子。

研究相关问题

如何确定你的工作将会以研究为导向?这对我来说是很重要的一点。这些问题帮助我更好地分析-

5。你花多少时间阅读研究论文?你每周/每两周参加一次阅读小组吗?

即使在你的日常工作之后,你还能保持在 ML 疯狂的顶端吗?还是你只会记得 2018 年的伯特(是的,已经 3 年了!)

6。你的团队出版了吗?如果是,在什么会议上?

在您的团队中,发布是优先考虑的事情,还是只关注生产模型?出版被认为是晋升的一个因素吗?目前的出版物是什么类型的会议?这些是非常实用的还是已经回答了一些基本的研究问题?

7。你的团队更喜欢在内部会议/期刊上发表文章吗?

内部会议有助于熟悉公司其他小组的研究,建立联系,尤其是合作。

开源项目

8。团队有开源项目吗?

这有助于维护一个外部投资组合,并给出一个关于你的团队参与的工作类型的想法。

9。你能为其他开源项目做贡献吗?

有许多公司限制你从事工作之外的其他开源项目。如果您对此感兴趣,您可以向您的经理核实。

10.公司的核心价值观是什么?你的价值观和这些一致吗?

这很能说明公司将如何对待员工,基本上也包括你。他们会有多重视你!

11.团队做出了哪些多元化努力?

人们对此可能会有不同的看法,但这一直是我决定职业选择时的一个重要选择。

12.一个项目持续多长时间?

你会做几十个小项目,还是会拥有产品的一部分。

13。软件工程 vs 数据分析 vs 机器学习工作的比例是多少?

这是一个如此重要的问题。如此多的人对仅仅是数据清理或提取工作而不是实际的问题解决感到沮丧。看看这个比例是否适合你。

可以提出的另一个后续问题是- 14。 你有单独的研究/软件工程/数据工程团队吗?

15.在小组中提出新项目的程序是什么样的?

小组是否每月/每两周举行一次专门的会议来分享知识/想法?这让大家都听到了,也说明团队重视创新。

16.在你的团队中,成长是怎样的?

这与你的个人职业目标一致吗?另一个重点是看有多少人会转换到其他角色。举例来说,如果你喜欢产品管理这个职位,你会从现在的职位上跳下来吗?

17.你将从事的产品部件有多老了?

在 Debarghya Das 对 Jay Shah 的采访中,他提到现在大多数团队都在使用其他人的代码,大多数困难的问题都已经解决了。我非常同意这一点。如果你正在开发一个已经存在多年的产品,那么产品开发中最困难的部分已经解决了。它主要是在此基础上构建或维护当前的代码库。这归结为大量的增量工作。

在部署模型之前应该问的问题

原文:https://towardsdatascience.com/questions-you-should-ask-before-you-deploy-your-model-3465bca49fe5?source=collection_archive---------39-----------------------

机器学习部署策略介绍

泰芬·范·赞特沃特在 Unsplash 上的照片

完成一个运行良好的机器学习模型是一项巨大的努力,但这通常不是过程的终点。对于有影响的真实世界的应用程序,需要部署一个经过训练的模型并将其投入生产。

问题清单

假设你是一名有抱负的数据科学家,为一家视频流媒体网站工作。你被要求开发一个推荐系统,根据用户之前的选择向他们展示他们可能喜欢的视频。你坐下来,做你的工作,想出这个惊人的模型。你可能认为现在是时候休息一下,看看模型的工作了。但是,让我们退一步,问自己以下问题:

  • 如果我的模型在生产中没有达到要求的精度,会有什么负面后果吗?
  • 该模型在测试集上表现良好,但是我如何确保该模型在生产环境中工作良好,而不会在不工作的情况下失去客户呢?
  • 生产中是否已经存在将被替换的现有模型?
  • 停机是个问题吗?我如何保证在新模型部署期间没有/很少停机?
  • 如果新模型中有一个 bug,我是否有一个策略来回滚一个旧版本?

要考虑的事情比你最初想的要多,对吗?但是有什么策略来部署模型呢?

照片由罗布·施莱克希斯Unsplash 上拍摄

部署机器学习模型的策略

影子部署

图片作者(图标由来自www.flaticon.comFreepik 制作)

如果您不确定您的模型在生产中的表现如何,测试它的一种方法是使用影子部署。在影子部署中,您的新模型对您的用户没有任何实际影响,它只是与您的当前系统并行运行:您的模型进行预测,但不将预测交付给用户,而是交付给数据库或日志文件。影子部署的目的是收集和监控生产中模型的相关指标,而不会因新模型的错误预测而带来负面影响。

优点:

  • 风险最小化:不会因为(意外的)低模型性能而对用户产生负面影响。
  • 对生产没有影响,例如,由于请求被镜像,新模型中存在缺陷。

缺点:

  • 因为需要并行运行和监控两个系统,所以操作开销增加了。
  • 缓慢推出

金丝雀部署

图片作者(图标由来自 www.flaticon.com自由派制作)

金丝雀部署是另一种尝试降低新部署风险的策略。这也可以被视为影子部署之后的下一个逻辑步骤。不是向整个用户群推广该模型,而是只向一定比例的用户展示新模型。例如,一个典型的起始划分是 90/10,其中 90%的用户请求由旧模型处理,10%的用户请求由新模型处理。如果新模型包含一个错误或者预测不令人满意,不是所有的用户都会受到影响,而只是一小部分用户会受到影响——这将风险降到了最低。

这个想法也是为了收集新模型在一段时间内的关键性能指标。如果该模型能够稳健运行,那么新模型所服务的用户份额将会逐步增加。

优点:

  • 降低风险:减少由于(意外的)低模型性能对用户造成的负面影响。
  • 对生产的影响较小,例如,由于请求被镜像,新模型中存在缺陷。
  • 快速回滚:如果新模型意外失败,您可以通过简单地重定向所有请求,快速回滚到旧模型。
  • 无停机时间:两种模式并行运行

缺点:

  • 甚至比影子部署更高的运营开销,因为两个模型在生产中有效运行。
  • 缓慢推出

蓝色/绿色部署

图片由作者提供(图标由 Freepik 制作,来自www.flaticon.com

蓝/绿部署的结构与 canary 部署的结构相同(两个模型并行运行),但是,主要区别在于,新模型不仅处理一部分请求,而且一旦新服务启动并运行,所有请求都会被路由到新服务。其思想是简单地拥有两个尽可能相同的环境:如果新模型出现问题,您可以简单地将所有请求重新路由到旧模型(回滚)。如果部署应该进行得更快,这种部署方法通常是首选。这当然意味着该模型没有像其他部署策略那样经过彻底的测试。此外,较少数量的性能指标因此可用于调试和新模型的潜在重复。

优点:

  • 降低风险:减少由于(意外的)低模型性能对用户造成的负面影响
  • 快速回滚:如果新模型意外失败,您可以通过简单地重定向所有请求,快速回滚到旧模型。
  • 无停机:两种模型/环境并行运行
  • 快速推广

缺点:

  • 甚至比影子部署更高的运营开销,因为两个模型在生产中有效运行。
  • 较少的度量和信息可用于调试新模型。

更多材料:

Python 快速入门指南

原文:https://towardsdatascience.com/quick-fire-guide-to-apis-in-python-891dd98c8877?source=collection_archive---------6-----------------------

入门

网络交流简明指南

作者图片

使用 Python 迈出与 web 交互的第一步似乎令人生畏——但事实并非如此。这是一个非常简单的过程,有着完善的规则和指导方针。

我们将涵盖入门的绝对要点,包括:

- Application Program Interfaces (**APIs**)
- Javascript Object Notation (**JSON**)
- **Requests** with Python
- Real world **use-cases**

应用程序接口

我们可以把 API 看作是这些小的神奇的盒子,我们在这里发送数据,然后取回数据。

这个小盒子只不过是服务器上某个地方的一个脚本。它充当了我们——T2 的客户端(T3)和他们——T4 的服务器(T5)之间的中间人。我们可以告诉我们的中间人,我们想知道一些事情,改变一些事情,或者删除一些事情——如果我们的指示被正确接收,中间人会很高兴地忘记。

在构建 API 方面有很好的指导原则,它们都遵循相同的使用模式。这些 API 中的绝大多数(在 web 上)使用了一种叫做 REST 架构的东西。

休息

表述性状态转移(REST) API 架构应用了六条规则:

  • 所有请求必须使用一个 单一外向接口 来处理。
  • 客户端-服务器独立性——意味着客户端(美国)的变化不会影响服务器端的功能,反之亦然。
  • 每一个新的请求都被独立于其他请求处理 API 不保留任何关于我们的会话的信息,因此它是 无状态
  • 缓存—API 必须指定用户是否可以缓存响应。
  • API 由 分层系统 组成,形成模块化结构。
  • 如果适用于 API 使用,应根据用户要求 提供 可执行代码。

所有这些产生了一个单一的网关,具有可预测的行为,可以在没有太多意外的情况下进行通信。

请求的类型

当我们与 API 通信时,我们会倾向于提出以下请求之一:

  • 获取 —从 API 中检索信息。
  • POST —创建新的资源(例如,添加新的数据记录)。
  • PUT —更新现有资源(例如,更改现有记录中的特定值)。
  • 删除 —删除现有资源(例如,删除数据记录)。

最常用的方法是 GET 协议,它允许我们下载数据。

我们可以使用谷歌地图 API 来获取特定地址的经纬度坐标。

发布、上传和删除是不同的,因为它们仅在修改信息时使用。使用 GitHub API,我们可以用 POST 创建一个 repo,用 PUT 更新它,用 DELETE 删除它。

还有补丁 —这与 PUT 类似,但用于“部分更新”。我从未使用过这种方法,也没有在 API 文档中看到过这种方法——但它确实存在。

HTTP 代码

当我们使用 GETPOSTPUTDELETE 时,API 会向我们反馈信息,响应的一部分会包含一个 HTTP 代码,告诉我们请求的结果。这些通常是以下情况之一:

**Success Codes
200 OK** - success (common response to **GET**) **201 Created** - new resource created (**POST**/**PUT**) **204 No Content** - success but not content returned *(not an issue)****Client* Error Codes
400 Bad Request** - request not understood due to bad syntax (fix your JSON) **401 Unauthorized** - not allowed, you need authentication **403 Forbidden** - secret place, you're not allowed here **404 Not Found** - you're lost, this place doesn't exist ([or does it...](https://docs.github.com/en/rest/overview/other-authentication-methods#basic-authentication))***Server* Error Codes
500 Internal Server Error** - there's something wrong with the server**OP Codes
418 I'm a teapot** - teapot cannot brew coffee **420 Embrace Your Calm** - sending too many requests to Twitter

Javascript 对象符号

当与 API 交互时,我们使用一个标准化的模板来发送和接收数据,这样客户端(我们)和 API 都可以正确地处理数据——这种格式就是 Javascript 对象符号(JSON)。

JSON 用我们在 Python 字典中看到的键值对实现了相同的层次结构。我们可以使用这种结构嵌入列表、字符串,甚至其他字典。

让我们尝试一个例子,点击这里将使用我们的浏览器向 PokéAPI 发送一个请求——从 API 返回 JSON 响应的明文版本:

来自 PokéAPI 的 JSON 响应,在我们的浏览器中。

我们可以看到 JSON 响应的类似字典的格式。就结构而言,Python 字典和 JSON 之间没有真正的区别——然而我们不能在与 API 通信时直接使用 Python 字典。

Python 请求

我们已经探索了 API 的基础——但是我们如何开始用 Python 与它们交互呢?

我们可以使用非常受欢迎的图书馆。如果我们想发送 GET 请求,我们只需写:

import requestsrequests.**get**(*'https://api_address.com'*)

我们所需要做的就是输入我们向其发送请求的 API 的 URL 我们将收到来自 API 的 JSON 响应!

使用之前用于 PokéAPI 的相同“端点”(特定的 API 入口点),我们可以用 Python 返回相同的信息,如下所示:

这里有三点需要注意:

  • 我们的请求返回的响应对象显示请求成功——<Response [**200**]>——意思是 200 OK (参考前面的代码)
  • 我们使用 response json()方法访问返回的数据,这将输出 JSON 响应的 Python 字典版本。
  • 字典输出与我们通过浏览器访问 API 端点时看到的完全相同。

因为我们使用json()方法返回了一个字典,所以我们可以使用 Python 字典中通常使用的相同方法来访问响应的特定部分。如果我们想返回 Charizard 能力的名称:

对于所有神奇宝贝迷来说,我想这是文章的结尾——对于那些对返回每个神奇宝贝及其能力的综合列表不太感兴趣的人来说,让我们转移到一些可能更有用的 API 上…

谷歌地图 API

谷歌(当然)提供了大量的 API——其中一个特别的是他们的地理编码 API,它允许你返回任何地址的经纬度坐标。这也是我第一次使用 Python 的职业经历的很大一部分。

从那以后,它发生了很大的变化——但当我想到“API”时,这种体验是我首先想到的事情之一。它还很好地介绍了 API 的典型用法。

1。 授权 —大多数 API 需要我们在调用中包含一个授权密钥,如果我们错过了这一点,我们通常会返回一个 4xx 代码,告诉我们需要授权。

获取 API 密钥的步骤包含在地理编码 API 文档中,它非常简单,因此我不会在此重复该文档。

2。 API 参数 —我们将尝试返回罗马斗兽场的坐标。它的地址是Piazza del Colosseo,我们将把它作为参数包含在请求的 URL 中。

我们也可以使用相同的方法进行身份验证。

就在那里,在我们的 JSON 响应中,我们可以看到latlng——我们可以访问这两个值,就像我们之前使用 PokéAPI 一样。

这是我们斗兽场的经纬度坐标——简单!

GitHub API

我们已经看到获得请求,但是没有发布 , 放置,或者删除。这些请求往往略有不同,因为我们通常包含一个payload对象,它详细描述了我们使用 API 进行 POST ing、 PUT ting 或 DELET ing 的信息。

对于这个例子,我们将使用 GitHub API 向我们的 GitHub 帐户发布一个新的回购。同样,我们也需要一个授权密钥——你可以在这里找到一个的分步指南

不过有一点,当选择个人访问令牌的权限时——本教程需要的唯一权限是 repo 。我还建议远离 delete_repo 权限。

我们只需要回购权限。

一旦您有了授权密钥,我们将使用它来验证我们对 GitHub API 的所有请求,如下所示:

我们在这里所做的只是典型的 API 请求——但是使用了 API headers中包含的授权密钥(或令牌)。我们可以认为headers是我们在请求中包含的元数据

最后一步是描述我们想做什么。我们将使用请求中的data参数来实现这一点。在这里我们包含了我们的payload,它是一个使用json.dumps转换成 JSON 格式字符串的 Python 字典:

由此我们将返回 201 创建的响应代码——它告诉我们请求成功了,我们已经创建了一个新资源。

为了核实,我们可以去 Github。

我们应该在我们的档案中找到一个新的 api_test repo。

果然,有了我们新的api_test回购——我们可以用 GitHub API 做很多事情,如果你对更多感兴趣,看看文档

在这里,我们介绍了 API 的世界,以及如何在 Python 中与它们进行交互。从这里开始,还有大量的东西需要学习。

同样值得注意的是,这只是 API 生态系统的一个方面,当然还有构建和服务这些 API 的另一个方面——这是一个完全不同的游戏。

然而,掌握我们在本文中讨论的概念是继续深入到 API 交互和/或开发的其他途径的基础。

我希望你喜欢这篇文章!如果你有任何问题,请通过 Twitter 或在下面的评论中告诉我。如果你想要更多这样的内容,我也会在 YouTube 上发布。

感谢阅读!

如果您想直接进入 Python 中的 API 开发,请查看我关于使用 FastAPI 进行 API 开发的文章:

https://python.plainenglish.io/build-better-apis-with-python-5b82fabcf8b3

Jupyter 笔记本攻略

🤖《变形金刚》NLP 课程 70%的折扣

动态编程快速指南

原文:https://towardsdatascience.com/quick-guide-to-dynamic-programming-c1618698d341?source=collection_archive---------20-----------------------

通过将问题分解成更小的问题来寻找最优解并不困难

动态规划是一种解决优化问题的通用方法(找到最大/最小/最短/最长… ),通过将其分解为更小的问题(分而治之),并跟踪其解决方案(记忆化),以更有效地利用信息和资源。记忆化可以被看作是一种形式或递归。如果你觉得很困惑,很可能是因为你不明白为什么一开始就叫“动态”。原来名字是为了一个历史原因故意误导。

简单的例子

假设你要计算 6 x 2 。当然,你可以将乘法扩展成一系列加法,例如:

6 x 2 = 2 + 2 + 2 + 2 + 2 + 2

当然有许多方法来计算这个总数。例如:

6×2 =(2+2)+2+2+2 =(4+2)+2+2 =(6+2)+2+2 =【8+2】

这个解决方案没有错,但关键是,每一个添加都是一个新的案例(用粗体突出显示)。有没有其他方法可以让我们“回收”关于子问题的信息?事实上,我们可以将总和改写为…

6×2 =(2+2)+(2+2)+(2+2)=(4+4)+4 =8+4= 12

正如你所看到的,在中间步骤中,技巧基本上是只计算 (2+2) 一次,并将结果存储到内存中。每当我们再次遇到 (2+2) 的时候,我们不必计算结果,只需从内存中取出即可。对于这样一个简单的例子来说,这并不重要,因为在实际情况下,获得子问题的结果可能会耗费时间或内存,因此这种方法可能比其他方法具有巨大的优势。这就是动态编程的精髓。概括来说,这些步骤通常是:

  • 将问题分解成更小的子问题;
  • 找出一个公式,把小问题变成大问题;
  • 求最小的一个的解;
  • 创建一个表来记录较小的解决方案;
  • 从较小的解决方案,建立最终答案。

{0,1}背包问题

在这个优化问题中,有一个包含 N 个项目的列表,这些项目具有关联的权重W【I】和值v【I】,以及一个具有最大容量 W 的“袋子”。目标是找到项目的子集,使得总值最大,并且权重之和小于 W 。在这个问题的简化版本中,项目可以被拾取或不被拾取,但不能是它们的分数或倍数,因此定义中为{0,1}。在其他地方,你可能已经看到了窃贼闯入比尔·盖茨的房子,试图偷走尽可能多的东西的例子。一个不那么戏剧性的例子可能是准备一次徒步旅行:你想带一些有用的物品,但你也不想被它们的重量压垮。

背包问题简而言之:在徒步旅行中,你会带什么东西而不会被它们的重量压垮?

如果你不知道从哪里开始,问题有几项,蛮力或“穷举搜索”的方法可能会做到这一点。也就是说,尝试所有可能的项目组合,并计算所有子集的总权重和值。选择符合要求的一个。事实证明,这种方法的时间复杂度为 O ( 2^N )。然而,注意到许多较小的项目组合出现多次,这应该表明动态编程可能是解决该问题的更有效的方法。

逻辑遵循自下而上的方法。让我们拿起给定的项目 i 。如果它的重量W【I】大于背包的最大容量 W ,它就不能成为解的一部分。另一方面,如果W【I】<W那么它可能是解决方案的一部分。因此,我们需要探索两种可能性:

  • 一种是我们找到没有项的最优组合I .
  • 一个i,加其他项的最优组合。在这种情况下,剩余项目的总权重必须小于 W-w[i] ,否则将无法满足约束(太重)。

你可以看到递归包括在背包中保留项目 i 的同时查看更小的组合。还有,可能没有任何可行的解决方案!为了在实践中解决这个问题,我们需要一个 2D 表,其中行表示项目,列表示最大权重(在下面的实现中,权重被“量化”为整数)。该表需要一个额外的行来处理没有条目的情况。下面是 python3 中的一个简单实现:

预期结果是 25。此外,您会注意到表中的许多条目都是None,即的默认值。这是因为算法从来不会遇到权重与该列相对应的特定组合。如果你改变weightsvalues的初始值,你可能会看到不同的结果。

动态规划和强化学习

动态编程方法在一个名为强化学习 (RL)的人工智能分支中扮演着一个有趣的角色,其中一个实体必须学会探索环境,以找到最大化回报的路径。你可能见过电脑玩经典视频游戏的例子。虽然我们没有深入研究 RL 的复杂性,但您可以很容易地理解它的探索方面的一部分,很可能涉及到寻找最大化或最小化某些成本函数的最短路径。

一个简单的例子是一个生活在棋盘中的机器人,它的目标是从左上角开始,到右下角,只做直线运动,只做直角转弯。在我们采取行动之前,我们可能会问有多少独特的路径。一旦我们解决了这个问题,我们可能会决定只探索其中的一部分,但这超出了本文的范围。

探索周围环境的一部分可能是计算有多少条独特的路径可以到达目标。动态编程可以帮助解决这个问题。

为了解决这个问题,我们再次创建一个 2D 表来记录一个给定的方块被访问了多少次。在这种情况下,如果棋盘有 m 行和 n 列,则表格有尺寸( m+1 ) x ( n+1 )来说明基本情况。其想法是,对于每一行,机器人可以上到最后一列,然后转弯并继续一路到达目标,或者停在列 n-1 处转弯,然后继续下一行。但是在这一点上,可用的行和列的数量已经减少,所以这对应于一个更小的子问题。最终将只剩下一个方块(目标),这就是停止条件。

下面是一个与 python3 一起工作的简单实现:

一个简单的例子,展示了如何利用记忆来开发动态编程。

有了上面的输入( m =3, n =4),结果应该是 10。公平地说,这种情况相对简单,不需要递归就可以解决。这里有一个例子:

最后,更倾向于数学的读者可能也已经注意到,问题的答案可以通过简单的推理,根据重复 m 行和 n 列的组合数量来找到。机器人必须在 m -1 向右和 n -1 向下运动中到达目标,不考虑顺序。这是一个包含阶乘的单行程序:

结论

动态规划是一种非常强大的优化方法,应用非常广泛。我认为有趣的是,看到一种在计算资源极其有限的时代开发的方法仍然在诸如强化学习等领域找到越来越新的应用,这在当时被认为几乎是科幻的。

基于 R 的命名实体识别和地理编码

原文:https://towardsdatascience.com/quick-guide-to-entity-recognition-and-geocoding-with-r-c0a915932895?source=collection_archive---------16-----------------------

照片由 安德鲁·尼尔 发自 像素

在历史(或当代)文本中查找和地理定位地名的快速指南

数字人文学科在分析历史(和当代)作品的技术和 GIS 应用方面有许多用途。除了开源软件之外,开源数据为分析师提供了更大的可访问性。

在这篇文章中,我将介绍并提供一个命名实体识别(NER)和 DH 应用 Rstats 地理编码的简要指南。我将使用由优秀的 Gutenberg 项目提供的开放数据,以及使用来自优秀的 Rstats 社区的开源 Rstats 包的开放街道地图(OSM)。

本指南要求您对 R 编程有所了解。

什么是命名实体识别?

NER 是自然语言处理的一项任务,它识别并标记文本中的实体。这些实体可以是人、日期、公司,或者在本例中是位置。Rstats 有几个很棒的自然语言处理包,包括 openNLPspacyr

什么是地理编码?

地理编码是查找位置的空间坐标的过程。使用 R,可以通过编程方式进行地理编码,但这是一个不完美的过程,需要特别注意结果。

流程是怎样的?

我将这个过程分为五个步骤:获取数据、清理数据、注释数据、地理编码和评估数据。这个过程是一个迭代的过程。因为我们正在处理文本,所以更容易遗漏细节和错误。编程提供了一个非常有用的工具,但仔细阅读和熟悉文本是必不可少的。

安装

在这个项目中,我将使用 Lina Eckenstein 的《卡森蒂诺:给旅行者的提示》一书,该书是使用 gutenbergr 包从古腾堡项目下载的。

步骤 0:加载包。

第一步:获取数据

下载书籍

用 gutenbergr 下载书籍相当简单。通过找到 gutenberg id 并将其传递给 gutenbergr,可以很容易地下载书籍。

(注:我过去在使用默认下载镜像时遇到过一些麻烦,可以通过选择一个替代镜像来修复。你可以在这个线程中看到错误的解释。).结果给出了一个整齐的数据框,每行一行。这对于分析像 tidytextquanteda 这样的包非常有用。

步骤 2:清理和准备数据

现在你有了数据,你需要做一些整理。古登堡计划提供的书籍包括封面内容,如书名和出版物信息。这对于分析来说并不真正有用,所以您可以在实际文本开始之前删除任何数据。你也可以删除某些字符,或者编辑文本中的字符串。

将数据符号化

语篇分析的一个重要部分是标记化。这是将文本分解成更小的部分的程序过程,通常是单词、行或句子。现在数据基本上被标记成了行,但是我想创建句子标记以供 NER 注释器使用。

有许多不同的带有内置令牌化器的包。tidytext 包中内置的单词标记化器可以很好地处理单词标记,但是用 tidytext 进行句子标记化并不总是能提供最好的结果,所以我将使用 spacyr。Spacyr 是 Python 包 spacy 的包装器。对于自然语言处理来说,它是一个强大的包,但是它要求您安装了 python,并且需要从 r。

spacyr tokenizer 需要一个文本字符串或语料库,这意味着数据帧必须折叠,这样书就是一个字符串。之后,我们可以将生成的句子重新格式化为每行一句的数据帧。

(注意:您可以保持 gutenbergr 返回的当前格式的数据。结果可能不同,但原理是一样的。)

检索位置

接下来,我们将从文本中检索位置。R 中有许多不同的实体标签,但是我将使用实体包,它提供了 openNLP 标注器的包装器。我更喜欢这个工具,因为它不会占用太多的内存,并且是我的计算机可以处理的进程。

您可以用任何您喜欢的格式返回结果,但是我选择了在数据帧中的文本位置添加一个新的列表列。该函数返回一个列表列表,这意味着我们仍然为每个句子保留一行,以及一个在句子中出现两个位置的列表。

我还想创建一个单独的非嵌套位置的数据框架,这是一个更具可读性。这里我们选择 locations 列并取消嵌套,为文本中的每个位置实例提供一个观察值,同时从列表中删除空对象。产生的“locations”数据帧返回文本中找到的每个位置及其行号。我还将对位置进行分组和计数,给出每个位置的一行以及它在文本中出现的次数。这将有助于以后对数据进行地理编码和可视化。

此时,有必要对结果进行初步检查。实体定位器有时可能会发现不完整或错误的实体。通过在标记化的句子旁边创建一个位置行,我们可以试着指出这一半位置指的是什么。

在这种情况下,实体定位器找到了相当多的位置,但是如果您对照文本检查这些位置,我们可能会发现,除了不完整的实体之外,它还遗漏了在文本中其他地方找到的位置的一些实例。它还标记了实际上不是位置的实体。

通过保存带有行号的 location 实例,您可以很容易地检查特定的句子来推断实体所指的内容,并在上下文中查看实体。这个迭代是确保数据准确的重要部分。

清除位置数据

查看文本旁边的结果后,您可以删除一些不引用任何位置的实体。我还注意到文本中遗漏了一个位置——“Soci”。我将在 locations 数据帧中添加一行,然后更新 locs_dist 数据帧。

现在我有了一个在文本中至少出现一次的位置列表,我还可以检查一下是否有注释器遗漏的地方。我可以通过多种方式实现这一点,但是我包含了一个简化过程的函数,它需要一个数据帧、一个要查找的位置向量以及文本所在的列名。这个函数将在数据帧的整个文本列中搜索位置向量中的位置,并返回一个新的列表。结果列表将被附加到数据帧,并且将返回一个数据帧。

现在,我们可以看到 dataframe 中的原始 locations 列与 new_locs 列并排。如果我们打印一些示例行,我们会看到该函数已经挑选出了原始位置注释器遗漏的文本中特定位置的所有实例。此时,不再需要 text dataframe 中的 initial locations 列,因此我们可以删除该列,并取消嵌套 new_locs 列。这将为我们提供一个数据帧,每个位置观察一行,这意味着一些文本行将被复制。

步骤 3:注释数据

我们现在已经找到了文本中的位置,但我们不知道它们被使用的上下文。我们也不知道这是否是所有地点的完整列表。在这一点上,我们需要做更仔细的阅读。

对于这个项目,我对重现作者走过的路线感兴趣。也就是说我需要知道她到底去过哪些地方。如果我在 Excel 中打开数据,我可以扫描位置提及的上下文,并对每个观察结果进行编码。我可能会添加补充数据,如位置是否是一个城市,一个网站,地理特征等。我还可以添加一列来指示某个位置是否被访问过、浏览过,或者是否与分析无关。我还将添加访问地点的顺序。通过将位置与文本中出现的位置一起显示在单独的列中,这变得更加容易。

现在,我们可以将数据加载回 r 中。我们还可以创建单独的数据帧,这些数据帧只包含与下一步地理编码相关的位置。

步骤 4:地理编码

现在,我们有了一个过滤后的数据框架,其中包含了我们对此分析感兴趣的所有位置:已访问或已查看的点。我们可以使用此数据帧对使用 tidygeocoder 包的位置进行地理编码,该包使用 osm 数据对站点进行地理定位。为了提高准确性,我们可以添加一个国家列给 geocoder。

步骤 5:检查数据迭代

地理编码器没有返回所有点的结果,但它返回了相当多的结果。但是,我们需要检查地理编码结果的准确性。通过将它们与意大利地图一起绘制,我们看到地理编码器做得非常好,但是在意大利北部有一些点可能被错误识别。熟悉课文有助于了解你的观点应该落在哪个范围内。这也有助于将坐标数据框架与完整的句子数据框架连接起来,这样我们就可以将所有内容放在一起。

让我们把一切都标在地图上。

让我们也加入数据框架,这样我们就可以在全文旁边看到结果。

问题之一是麦当娜·德尔·萨索的位置。在上下文中,我们可以看到这是指 Corsalone 河附近的教堂。我们可以查看在线地图来核实位置并获得正确的坐标。结果是,地理编码器没有给出我们要找的位置。这里有一个由 Tyler Morgan Wall 编写的函数,它将简单地在一个数据帧中重新分配坐标,该数据帧有单独的纬度和经度列。

找到所有正确的坐标后,我们可以通过将带有坐标的数据框与其余数据连接起来来更新位置数据,并使用 lon 和 lat 列作为 X 和 Y 坐标,使用 st_as_sf 函数创建一个空间对象。

现在我们可以一起绘制我们的空间对象。

最后,我将筛选出我将用于我自己的分析的点——特别是访问过的网站。我将在 QGIS 中使用这些数据来运行最小成本路径分析,以估计作者在旅行中可能选择的路线。我计划在以后的文章中记录这些步骤。现在,我将创建过滤后的数据帧,并将其保存为 shapefile。

结论

r 是自然语言处理、地理信息系统和文本分析的好工具。结合 spacy python 包的 NLP 功能,R 可用于定位文本中的地理实体并对这些结果进行地理编码。这是一个有益的工具,在数字人文研究,以及 HGIS。我希望本指南有助于说明结合文本分析、NER 和 GIS 的迭代过程。如果您有任何让这个过程更顺利的想法,或者任何编写更好代码的想法,请发表评论。

图形遍历分析快速指南

原文:https://towardsdatascience.com/quick-guide-to-graph-traversal-analysis-1d510a5d05b5?source=collection_archive---------26-----------------------

分析图表来寻找最佳路径并不困难

图是由一组对象(节点)组成的数据结构,这些对象之间有连接()。如果连接从一个节点定向到另一个节点(例如爱丽丝欠鲍勃的钱),则图可以是定向的,或者如果定向不相关并且连接仅表示关系(例如爱丽丝和鲍勃是朋友),则图可以是无向的。如果一个图的所有节点都互相连接,那么这个图就是完整的。没有回路的有向图被称为无回路。是无向图,其中任意两个节点通过恰好一条边连接。树中的初始节点称为,末端节点(即没有下游连接节点的节点)称为**

图表的几个实际例子是友谊网络(社交媒体上的例如)家谱(家族)树分子大型强子对撞机产生的粒子,一张公司的组织图。你能说出它们属于哪一类图形吗?

主要的图遍历算法

遍历一个图意味着通过根据一些系统规则访问节点来探索它的结构。最有效的规则取决于图表的类型和手头的问题,所以不可能做出真正通用的陈述。由于计算机科学中的许多数据结构都有一个基础图,即树,所以值得花些精力来更好地理解探索这种图的策略,以便找到满足手头问题条件的路径或节点。

在许多情况下,问题可以归结为一种陈述,例如“找到节点 AB 之间的路径,使得某个量被优化”。该数量可以是例如边的数量(路径的长度),或者与每个节点相关联的某个特征(可能是二进制的)的总和。

两种主要的方法叫做深度优先搜索 (DFS)和广度优先搜索 (BFS)。

  • 在 BFS 方法中,相同深度级别的节点被顺序探测,直到满足停止条件。它利用一个队列通过迭代方法找到最短路径。BFS 通常会占用大量内存,因为需要维护一个额外的数据结构,其中包含有待探索的节点,通常以“已经访问过的”布尔变量的形式存在。当树的深度不平衡或者如果搜索节点离根不太远(“浅树”)时,BFS 通常是最佳方法。一个典型的例子是社交网络,在这里我们想要搜索与某个特定的人有相似兴趣的人。目标节点很有可能是直接的朋友或朋友的朋友,根节点可以有许多分支,但目标节点之间只有几条边。
  • DFS 算法通过从根到叶子来探索图形,并重复该操作,直到满足条件。它利用一个堆栈来寻找最短路径。如果一个给定的分支没有满足请求,算法“回溯”到更高的级别,然后沿着另一条路径前进。如果使用递归方法,则需要更少的内存,因为不必跟踪已经“访问”过的节点。在树代表一系列决策和/或目标叶远离根的情况下,DFS 通常是一种有效的方法。一个典型的用例例子是游戏的模拟,比如国际象棋,在每次迭代中,可能性很少,但每个决策在下一步中都走得很远。

代码示例

几行代码通常比大量文本更具说明性。在下文中,我们将探索一个由 6 个节点和 5 条边组成的简单图形。

以下示例中使用的图表结构

深度优先搜索

预期产出:

A
A -> B
B -> D
B -> E
A -> C
C -> F

广度优先搜索

预期产出:

A B C D E F

经验法则

  1. 简单图上的最短路径-> BFS
  2. 低宽度,大深度-> BFS
  3. 大宽度、低深度-> DFS(递归堆栈不会溢出)
  4. 扫描所有可能的结果-> DFS

AWS 帐户的快速设置指南

原文:https://towardsdatascience.com/quick-setup-guide-for-your-aws-account-423dadb61f99?source=collection_archive---------33-----------------------

现在就开始在云中构建 ML 和数据解决方案

Rafael Garcin 在 Unsplash 上的照片

云技术现在在行业中无处不在,被各行各业的技术专家使用,而不仅仅是 DevOps 工程师。越来越多的数据科学家和机器学习工程师正在云上运行工作负载,以训练模型和托管端点来进行实时推理。

有几个云提供商,但 AWS(亚马逊网络服务)是领导者,控制着超过 31%的市场——几乎是 Azure、谷歌云和阿里云的总和。由于亚马逊的主导地位,与其他任何云提供商相比,更多的技术职位将 AWS 列为必备或首选技能。

开始

本指南中的说明旨在帮助您快速启动并运行 AWS。这不是一个设置 AWS 帐户的全面教程,而是一个帮助您现在就开始在云中开发的简单指南。

我们将介绍如何:

  1. 创建一个 IAM 用户。
  2. 为特定服务创建 IAM 角色。
  3. 安装和配置 AWS CLI。
  4. 创建一个 S3 桶。
  5. 请求服务限制增加。

1.创建管理员 IAM 用户

IAM(或身份和访问管理)是 AWS 系统,用于管理各种 AWS 资源的访问和权限。首先,您需要一个管理员用户:

  1. 登录或注册 AWS 账户
  2. 搜索并选择 IAM 以创建管理员用户。在访问管理下,点击用户然后添加用户。填写用户名字段,选择程序化访问AWS 管理控制台访问,然后点击下一步:权限
  3. 选择创建组,填写组名字段,选择管理员访问策略。
  4. 点击下一步:标签,然后下一步:审核,最后创建用户
  5. 点击下载。csv 按钮,并将凭证保存在安全的地方。当我们稍后配置 AWS CLI 时,您将需要这些。

2.创建 IAM 角色

IAM 角色可以由授权用户担任,并允许该用户执行由附加到该角色的策略指定的任务。对于这个例子,我们将创建一个 SageMaker 执行角色,它将允许我们的用户执行各种 SageMaker 任务。

  1. 在控制台中搜索并选择 IAM 。在权限管理下,点击角色,然后创建角色
  2. 遵循指南,选择您想要为其创建角色的服务。在本例中,我们将选择 SageMaker 。点击下一步:权限然后下一步:审核。填写角色名称字段,点击创建角色
  3. 注意屏幕顶部附近的角色 ARN 字段。这是您将用于以编程方式承担此角色的值。
  4. 如果该角色需要额外的策略,您可以搜索角色名称。选择您的角色并点击附加策略。对于本例,让我们通过检查并单击附加策略来搜索并附加 AmazonS3FullAccess

3.安装和配置 AWS CLI

AWS CLI(或命令行界面)将允许您从本地机器或任何安装机器的地方以编程方式运行 AWS 工作负载。这里,我们将介绍如何安装和配置 AWS CLI 版本 2。注意:版本 1 也广泛使用,因此请注意您遵循的任何说明中使用的版本,以确保一切顺利运行。

  1. 按照此处的说明安装 AWS CLI 版本 2
  2. 安装完成后,在您的终端上运行$ aws configure。您将需要在 1 中生成的凭证文件。创建管理员 IAM 用户。填写您的 AWS 访问密钥 IDAWS 秘密访问密钥。设置您的默认地区(我使用 us-east-1,但您可以使用您帐户可用的任何地区)。点击回车键,将 json 作为您的默认输出格式。

4.创建 S3 存储桶

S3(简单存储服务)AWS 解决方案简单地在云中存储数据——顾名思义。设置一个存储数据的桶很简单——在控制台搜索栏中搜索 S3 ,然后点击创建桶。对于一个基本的私有 bucket,只需指定一个名称,其他的都使用默认值。

5.创建一个 EC2 密钥对文件

密钥对文件允许您使用私钥保护 EC2(弹性云计算)实例。您可以使用此凭据,而不是使用密码来访问您的实例。跟随我的逐步指导到这里

包扎

感谢您的关注!请务必关注我,并查看我关于在云中构建数据和机器学习产品的其他教程。如果你有兴趣和我打招呼,请在 LinkedIn 上联系我。

接下来阅读

了解如何使用以下工具在 AWS 上构建数据管道:

使用 Heroku 在自定义域上快速部署 Dash 应用程序

原文:https://towardsdatascience.com/quick-start-on-deploying-your-dash-application-using-heroku-on-a-custom-domain-123f25a4bff1?source=collection_archive---------21-----------------------

这是一个教程,介绍了如何使用 Heroku 在自定义域上部署带有身份验证的 python Dash 应用程序。

照片由卡洛斯·穆扎Unsplash 上拍摄

平台即服务(Paas)和身份验证平台使得应用程序部署比以前更容易。我们没有将 python Dash 应用程序打包并在本地运行,而是展示了如何使用 Heroku 将 Dash 应用程序部署到自定义域的快速入门指南。

设置 Heroku

首先设置一个免费的 Heroku 账户,下载 CLI 工具。在本地终端中,通过以下命令登录,

heroku login

设置本地 Dash 应用

在本地机器上,我们可以创建一个简单的 Dash 应用程序,包含以下文件:

.gitignore
app.py
Procfile
requirements.txt
runtime.txt

这是基于关于https://dash.plotly.com/deployment的教程,所以请参考那里的文档了解这个简单例子中的文件内容。唯一增加的是 runtime.txt,这个文件包含一行文本python-3.7.12。这将指示 Heroku 使用指定的 python 版本来用于应用程序-这可以是其他值,有关支持的 python 运行时,请参见https://dev center . Heroku . com/articles/python-support # supported-runtimes。否则,默认情况下,它将使用最新版本(在撰写本文时,python-3.9.9)。如果未指定,如果本地创建和测试的 venv 使用一个版本,而应用程序的运行时使用另一个版本,这可能会导致兼容性问题。

requirements.txt 文件是用pip freeze创建的。见下文,

设置 python 虚拟环境

pip install dash plotly gunicorn
virtualenv venv --python=python3.7
pip freeze > requirements.txt

部署应用程序

将应用程序部署到 Heroku 就像提交 git 并将提交推送到远程存储库一样简单(下面使用一个 Heroku 远程存储库)。

为了首次设置 Heroku 应用程序,我们将为该应用程序创建一个唯一的名称。heroku create [name]有效地创建了一个新的 Heroku 应用程序,并执行了一个 git remote add 来将 Heroku 远程存储库连接到本地 git 存储库。

heroku create testapp  # *testapp* is a unique name of app
git add .
git commit -m "initial commit"
git push heroku master

为了更新代码和重新部署,我们可以简单地提交新的更改并推送到 Heroku 远程存储库。这将自动触发全新的构建和部署。

git add .
git commit -m "new changes"
git push keroku master

如果之前已经创建了 app,可以手动将远程 Heroku 资源库与heroku git:remote -a [name of app]连接。

设置认证

我们还可以为我们的应用程序设置身份验证。最快的方法是在应用程序中实现这一点。查看 https://dash.plotly.com/authentication 的,通过基本认证找到一种方法。简而言之,这涉及到pip install dash-auth和在 app 中添加额外的 BasicAuth。有关完整的详细信息,请参见 dash 的认证文档。

auth = dash_auth.BasicAuth( 
         app,
         VALID_USERNAME_PASSWORD_PAIRS
)

这样,当我们访问应用程序时,我们将看到以下屏幕,要求登录。

使用基本身份验证的登录弹出窗口。作者截图

或者,您可以使用 Auth0。这可以作为附件安装。更多信息可以在https://devcenter.heroku.com/articles/auth0找到。

添加自定义子域

到目前为止,该应用程序被托管在 testapp.heroku.com。相反,我们可以很容易地将它链接到我们自己的定制域。首先,我们需要一个域名提供商(如谷歌域名)——确保你选择一个可以编辑 DNS 记录的提供商。

在 Heroku 中,转到应用程序的仪表板,在设置下,向下滚动到域并点击添加域。我们输入域名(如customdomain.com;或者我们也可以做子域,testapp.customdomain.com)。添加后,我们看到以下内容:

添加新域后的 Heroku 域视图。图片为作者截图。

记下 DNS 目标值,现在转到 DNS 提供商创建一个新的 CNAME 记录,该记录被路由到 DNS 目标。下面是创建一个 testapp 子域的例子。

示例 DNS 记录(使用 Google 域名)将子域路由到 Heroku。作者截图

有了这个,我们就可以在我们的客户领域快速发展了!

快速分析 Twitter 数据中的关系

原文:https://towardsdatascience.com/quickly-analyze-relationships-in-your-twitter-data-20f634c5160d?source=collection_archive---------33-----------------------

使用 R 找出哪些指标促使人们点击你的个人资料

作者图片

概述和设置

这篇文章使用了各种 R 库和函数来帮助你探索你的 Twitter 分析数据。首先要做的是从analytics.twitter.com下载数据。这里的假设是你已经是一个 Twitter 用户,并且已经使用了至少 6 个月。

在那里,您将点击Tweets选项卡,这将带您进入您的 Tweet 活动,并选择导出数据:

作者图片

点击导出数据后,您将选择“按天”,这将提供您每天的印象和参与度指标(您还可以在导出数据旁边的下拉菜单中选择时间段,默认为“过去 28 天”)。

注意:另一个选项是选择“通过 Tweet ”,这将下载每条 Tweet 的文本以及相关指标。我们可以用它来做有趣的文本分析,但是我们会把它留到下一篇文章。

为了这篇文章,我下载了五个月前所有可用的数据。

下载后,您将需要读取数据中的,在我们的例子中,将所有五个月的合并到一个数据帧中,我们将使用tidyverse中包含的readr包和read_csv()函数。然后我们将使用rbind()按行组合五个数据帧:

library(tidyverse)# load data from September to mid-January
df1 <- read_csv("./daily_tweet_activity/daily_tweet_activity_metrics_paulapivat_20200901_20201001_en.csv")
df2 <- read_csv("./daily_tweet_activity/daily_tweet_activity_metrics_paulapivat_20201001_20201101_en.csv")
df3 <- read_csv("./daily_tweet_activity/daily_tweet_activity_metrics_paulapivat_20201101_20201201_en.csv")
df4 <- read_csv("./daily_tweet_activity/daily_tweet_activity_metrics_paulapivat_20201201_20210101_en.csv")
df5 <- read_csv("./daily_tweet_activity/daily_tweet_activity_metrics_paulapivat_20210101_20210112_en.csv")# combining ALL five dataframes into ONE, by rows
df <- rbind(df1, df2, df3, df4, df5)

探索关系

Twitter analytics 跟踪几个指标,这些指标大致分为参与度,包括:转发、回复、喜欢、用户资料点击、url 点击、标签点击、详细信息扩展、媒体浏览量和媒体参与度。

还有其他指标,如“应用打开”和“推广参与”,这些是我没有使用过的服务,因此没有任何可用的数据。

引导性问题

有一个引导性的问题是有用的,因为它有助于集中你的探索。比方说,我感兴趣的是我的一条推文是否会促使读者点击我的个人资料。这方面的衡量标准是user profile clicks

我对这篇文章最初的指导问题是:

哪些指标与用户档案点击最相关?

你可以简单地使用带有基数 R 的cor.test()函数,在每个度量和User Profile Click度量之间一个接一个地移动。例如,下面我们分别计算三对变量User Profile Clicksretweetsreplieslikes之间的相关性。一段时间后,这可能会变得乏味。

cor.test(x = df$`user profile clicks`, y = df$retweets)
cor.test(x = df$`user profile clicks`, y = df$replies)
cor.test(x = df$`user profile clicks`, y = df$likes)

探索整个数据集中指标对之间关系的一种更快速的方法是使用相关图

我们从基数 r 开始,你需要限制你可视化的变量的数量,这样相关图就不会变得太混乱。以下是与User Profile Clicks相关度最高的四个变量:

# four columns are selected along with user profile clicks to plot
df %>%
    select(8, 12, 19:20, `user profile clicks`) %>%
    plot(pch = 20, cex = 1.5, col="#69b3a2")

这是一张图片:

作者图片

以下是另外四个具有中度关系的指标:

df %>%
    select(6:7, 10:11, `user profile clicks`) %>%
    plot(pch = 20, cex = 1.5, col="#69b3a2")

作者图片

从视觉上看,您可以看到中度关系散点图更加分散,方向更难识别。

虽然基数 R 是可靠的,但我们可以用GGally包获得更多信息。以下是与User Profile Clicks高度相关的四个变量:

library(GGally)# GGally, Strongest Related
df %>%
    select(8, 12, 19:20, `user profile clicks`) %>%
    ggpairs(
        diag = NULL,
        title = "Strongest Relationships with User Profile Clicks: Sep 2020 - Jan 2021",
        axisLabels = c("internal"),
        xlab = "Value"
    )

这是与user profile clicks高度相关的四个变量之间的相关图:

作者图片

以下是与User Profile Clicks适度相关的变量:

作者图片

正如你所看到的,它们不仅提供了散点图,还显示了每对变量之间相关性的数值,这比基数 r 提供了更多的信息。

现在,您的数据中的相关模式完全有可能是不同的,因为我们在这里看到的初始模式并不意味着推广到不同的数据集。

关于数据科学、机器学习、R、Python、SQL 等更多内容,在 Twitter 上找到我

Quo Vadis,大数据?

原文:https://towardsdatascience.com/quo-vadis-big-data-36f7842b578e?source=collection_archive---------30-----------------------

下一步是什么,如何获取智能数据?

乔希·卡特在 Unsplash 上的照片

大数据是指来自互联网、移动电话、金融行业、能源行业、医疗保健等领域的大量数据。以及智能代理、社交媒体、智能计量系统、车辆等来源。使用特殊解决方案对其进行存储、处理和评估[1]。术语“大数据”已经在科学和实践中出现了好几年。通常,大数据的挑战,尤其是 3v,与数据(量)的持续增长、种类和速度的增加以及数据变化的速度不断加快有关[2]。但是如何将大数据或处理大量数据的能力转化为智能数据并赢得洞察力

能够处理大数据

我们什么时候真正谈论大数据?必须收集多少数据才能被视为大数据?正常的数据存储技术通常以兆字节或千兆字节工作。当数据量达到 TB 或 Pb 时,我们就称之为大数据。这一经验法则的原因是,当处理这种数据量时,传统系统不再足够强大,而且成本也明显更高。大数据(存储)技术的典型特征是:

  • 分布式存储
  • 数据复制
  • 本地数据处理
  • 高可用性
  • 数据划分
  • 非规范化数据
  • 处理结构化和非结构化数据

用谷歌的 BigQuery 在几秒钟内查询大量数据——作者图片

从数据中获得洞察力(智能数据)

然而,这并不意味着已经从数据中提取了具体的知识,或者从数据中提取了相应的知识,或者在此基础上得出了相应的行动建议。为了将大数据转化为智能数据,从而为公司创造附加值,分析流程是必要的。
要做到这一点,公司通常需要三个常见领域之一的一种或多种分析能力:

  • 描述性分析—发生了什么?
  • 预测分析—会发生什么?
  • 规定性分析——我能做些什么来做得更好?

为了使 BI 工具或 ML 能够快速、灵活地分析和访问数据,需要正确的数据平台。

与亚马逊、谷歌等大型云提供商合作。像数据仓库、数据库等 IT 服务是通过即插即用来实现的。提供像 BigQuery [3]或 Redshift [4]这样的数据仓库服务需要每次点击。公共云也提供了比自托管计算机中心更多的计算能力。特别是对于小公司和初创公司来说,这是一个使用这些服务的有趣机会,因为它们具有成本效益并且易于设置。如果您感兴趣,您可以通过大多数大型云提供商提供的免费层轻松测试解决方案。

几十年来,数据工程师、软件工程师和数据分析师一直在用 ETL 过程构建数据仓库,并一直专注于实现严格遵循 Star 或 Snowflake 等数据模型的架构。此外,人们通常更关注技术细节,而不是业务需求。在数据湖中,所有数据都存储在临时区域中。之后,数据将被处理到数据仓库(数据仓库是数据湖的一部分的混合模型也很常见)、数据集市或用于分析和报告。这使得数据湖比数据仓库更加灵活。此外,它还支持机器学习等新的用例,并为非结构化数据提供存储功能。

集成的数据湖和分析平台—作者图片

如果我们观察简化的架构,就会清楚地看到,如果服务已经可以在云环境中相互通信,或者集成在一个服务中,那么就不需要第三方系统的进一步接口。这大大缩短了这些环境的设置和维护时间。另一个重要因素是数据科学流程可以显著简化。每个数据科学家和工程师都知道这一过程有多耗时,因此将您需要的一切都放在云环境甚至服务中的方法大大简化了这一过程。

摘要

在即用型云服务的帮助下,数据湖和自助 BI 工具等新模式以及敏捷方法公司(尤其是中小型公司)可以在更短的时间内构建数据分析平台,从而更专注于业务需求。

包含大数据的自助式 BI 控制面板示例—图片由作者提供

为了从大数据中生成智能数据,第一步应该是创建技术先决条件。第二步是在集成和可扩展服务的帮助下实现分析。在这里,数据可以被进一步处理,例如,通过用于描述性任务或用于机器学习服务的自助 BI 工具。由于这种互动,可以将重点放在更多与业务相关的活动上,而不是技术的操作上。

来源

[1]谷歌,https://cloud.google.com/what-is-big-data(2021)

[2]麦肯锡公司,人工智能下一个数字前沿? (2017)

[3]谷歌,,大查询 (2020)

[4] AWS,亚马逊红移 (2020)

r:衡量模型准确性的直观指标

原文:https://towardsdatascience.com/r²-an-intuitive-metric-to-measure-the-accuracy-of-a-model-7446c0d5d725?source=collection_archive---------12-----------------------

托尔加·乌尔坎在 Unsplash 上拍摄的照片

了解 R 指标背后的直觉,也称为决定系数

对数据建模可能是机器学习和数据科学中最频繁的任务。随着建模而来的不可避免的问题是对看不见的数据点的预测准确性。人们已经提出了各种各样的测量精度的方法,每种方法都有其优缺点。本文解释了 R 指标背后的直觉,也称为决定系数

给定具有特征 x 和目标 y 的长度为 n 的数据集。目标是拟合返回预测 P(X)=ŷ.的预测器 p 我们的预测器应该尽可能精确,从而最小化均方误差

MSE = 1/n∑(yᵢŷᵢ。

那么,你能想到的最简单的预测器是什么呢?嗯,我想忽略输入 X 是一个很好的起点。只有使用 y,才能估计目标的平均值

y̅ = 1/n ⋅ ∑ yᵢ

并为所有预测返回 。在本文的剩余部分,这个预测器被称为 Pˢ(其中 s 代表简单)。现在把 Pˢ的预测代入 MSE 方程,我们得到

MSE(y,pˢ(x))= 1/n∑(yᵢpˢ(x)= 1/n∑(yᵢy̅)

等一下。我以前见过这个等式。哦对了这正是方差的定义。因此,对于一个总是返回 y̅的常数预测器,MSE 等于 y 的方差。这是一个有趣的事实,但让我们继续。

Pˢ:是 y̅.最简单的预测者(图片由作者提供)

那么,这对于测量我的模型的性能有什么用呢?还有这个诡异的介绍和 R 有什么关系?引入“基线”预测值的原因是,将误差置于关系中会有所帮助。当两个或多个预测值在不同尺度的数据集之间进行比较时,这尤其有用。例如,假设一个数据集的 y 值在(-1,1)范围内,另一个数据集的 y 值在(-10000,10000)范围内。由于缩放,第二数据集的 MSE 预计会更高。因此,需要一个统一/正常化,否则,橙子就被比作苹果。

假设我们拟合了一个预测值 p,它提供了预测值ŷ = P(X)。那么 R 由下式定义

r = 1 MSE(y,p(x))/mse(y,pˢ(x))= 1mse(y,ŷ)/mse(y,y̅)

哇哦。这看起来是一个相对简单的等式。基本上,我们用预测值 p 的均方误差除以 Pˢ的均方误差,这样两个误差就有关系了。下图给出了线性预测因子 p 的示例。可以观察到 MSE(y,ŷ比 MSE(y,y̅).小因此,该分数将小于 1,导致 R 为正值。

r:将两个模型的误差设定成关系的准确性指标。(图片由作者提供)

让我们看看还会发生什么。

  • R =1.0: 假设我们已经找到 MSE(y,ŷ)=0.的最佳预测值那么,0 除以任何数都是零,1–0 = 1。这很酷,因为我们在这里展示的是,R 的最佳可能值实际上是 1.0
  • 更有可能的是,你的预测器会有一些误差。现在正 R 告诉我们,我们的预测器比总是预测平均值好多少。
  • 你可能会想,我的预测器怎么会比总是预测目标的平均值还要糟糕。嗯,你肯定听说过过度拟合?这正是可能发生的时间。

讨论了这三种情况,我们也知道 R 的值一定在区间(-∞,1.0)内。直观上,这是因为,一方面,r 没有下界,因为 MSE(y,ŷ没有上界。另一方面,MSE(y,ŷ)不能小于零,这导致上限为 1.0。

同样值得一提的是,因为分母等于var(y),所以也可以将该指标解释为解释的方差。或者,换句话说,预测器可以捕获的方差的百分比。最后,我想提一下,模型相对性能的思想是可以借鉴的,你可以创建你自己的指标。假设您想要使用一个 K-最近邻(KNN)预测器作为您性能的基线;然后你简单的除以 MSE(y,KNN(X))而不是 MSE(y,Pˢ(X)).还有维奥拉。现在你衡量与 KNN 相关的表现。

我希望你喜欢这篇文章,并发现它是有帮助的。欢迎在下面留下一些评论。

R 基础——开始使用 R 需要知道的一切

原文:https://towardsdatascience.com/r-basics-everything-you-need-to-know-to-get-started-with-r-10c8e566d7b3?source=collection_archive---------1-----------------------

向量、函数、循环、概率、数据可视化等等!

迭戈·PH 在 Unsplash 上的照片

作为公司大学拓展计划的一部分,我最近在新南威尔士大学(UNSW)的女性技术人员协会(WIT)做了一个关于 R 和 RStudio 基础知识的演讲。

为了将内容传播给更广泛的受众,我决定将这篇博客文章放在一起,来回顾一下那次演讲中讨论的内容。

简而言之,本次演讲的主题如下:

  • R 和 RStudio 简介
  • RStudio 接口和基础知识
  • 基本算术和变量赋值
  • 比较和逻辑运算符
  • 数据类型
  • 向量
  • 功能
  • 循环和条件句
  • 概率与统计
  • 数据帧
  • 数据可视化

要观看完整的演示或获取研讨会材料,请点击这里查看 WIT 网站

R 和 RStudio 简介

r 是一种流行的统计计算和图形编程语言。它被统计学家和数据挖掘者广泛用于开发统计软件和数据分析。

另一方面,RStudio 是一个用于 R 的集成环境(IDE ),有两种格式:RStudio 桌面,这是一个常规的桌面应用程序;RStudio 服务器,它运行在远程服务器上,允许通过 web 浏览器访问 R studio。

在本次研讨会中,我们将使用 RStudio Deskstop,您可以在此处找到并将其安装在您的本地计算机上。

那么,为什么要学 R 呢?

  • r 是开源的,这意味着它会被世界各地的其他合作开发者不断更新和改进
  • 它有许多适合不同用途的外部包,例如数据操作、文本清理、数据可视化等等
  • 一旦熟悉了基本语法,学习起来就相对容易和简单了
  • 几乎所有和数据打交道的人都了解并知道如何使用 R,所以你也应该知道!

RStudio 接口和基础知识

RStudio 接口

RStudio 分为 4 个象限:

  • 脚本(左上):编写、执行和保存命令的地方
  • 环境(右上角):列出当前工作区中的数据、变量和函数
  • 控制台(左下方):用于快速测试代码,显示命令和输出,除了绘图
  • 绘图(右下角):显示图形的地方

至于使用 RStudio 时需要知道的基本命令:

  • 清除控制台: Ctrl + L
  • 退出 RStudio: Ctrl + Qquit()
  • 从脚本运行代码:
  • 删除保存的变量: remove()
  • 清除工作区中的所有内容:
  • 访问上一条命令: Arrow up
  • R 等待下一个命令:
  • R 期待更多输入: +
  • 注释/取消注释脚本中的代码: Ctrl + Shift + C
  • 获取帮助: help()?

基本算术和变量赋值

R 最基本的用例之一是基本算术。此外,您还可以为变量赋值,以使您的计算更加灵活和稳健。

  • 添加:
  • 减去: -
  • 乘:
  • 分: /
  • 电源: ^**
  • 整数除法: %/%
  • 模(除法后的余数): %%
  • 变量赋值: =<-

比较和逻辑运算符

比较运算符比较一对值,并返回 true 或 false。

  • 等于: ==(注意用于比较的==和用于赋值的=的区别)
  • 不等于: !=
  • 大于: >
  • 小于: <
  • 大于等于: >=
  • 小于等于: <=

另一方面,逻辑运算符用于组合多个真和假语句。

  • &
  • 或者:

数据类型

R 中有 3 种主要的数据类型:

  • 数字:数字,例如 0、3.5
  • 字符:可包含字母、数字和特殊字符,如“你好,世界”
  • 逻辑:布尔值,即真或假

向量

Vector 是用于在 r 中存储数据的最基本的数据结构。Vector 是同一数据类型的一维有序数据集合。

为了在 R 中手动创建一个向量,我们使用c( )。或者,R 中也有专门用于创建数字向量的内置函数,如rep( )seq( )

我们还可以访问、添加、删除和改变任何给定向量中的元素。

功能

编程就像任何解决问题的场景一样,是将一个大问题分解成更小的组成部分。这不仅有助于更好地构建和组织我们的工作,更重要的是,它允许在需要时更容易地进行代码审查和调试。

函数是执行特定任务的一段代码,可以很容易地再次重用。一个功能包括一个简单的三步过程:输入、处理和输出。

输入有时被称为参数,也就是我们传递给函数本身的东西。过程是函数将对给定的输入执行的操作,它可以是任何形式的数据转换或数值计算。最后但同样重要的是,该函数将返回期望的输出。

除了使用 R 中的内置函数,我们还可以使用function( )构造自己的函数。

R 中的一些内置函数包括:

  • abs( )
  • sqrt( )
  • round( )
  • log( )
  • exp( )
  • sin( )

循环和条件句

循环重复运行一段代码给定的次数,或者直到满足某个条件。要在循环中定义条件,我们需要 if-else 语句。

R 中有两种类型的循环,这在许多其他编程语言中是一致的:

  • For 循环:多次运行一段代码
  • While 循环:一直运行一段代码,直到某个条件失败

概率与统计

r 主要用于统计分析和处理大量数据。

它有几个内置功能,可以轻松进行汇总统计、概率分布以及假设检验:

  • 汇总统计用于总结一组观察值,例如summary( )
  • 概率分布将概率分配给不同的结果,例如dbinom( )pnorm( )qchisq( )rexp( )
  • 假设检验提供了一种检验实验结果的方法,例如t.test( )prop.test( )chisq.test( )

数据帧

与向量类似,数据帧是用于在 r 中存储数据的数据结构。它是长度相等但数据类型不同的向量列表。

向量以列的形式呈现,每个列都有一个名称,代表变量。行代表观察值,可以命名或不命名。

虽然可以从头开始构建数据框,但数据框通常是通过导入数据集(例如 csv 或 Excel 格式的文件)生成的。

数据可视化

一张图胜过千言万语。一个优秀的数据科学家能够通过有效的数据可视化来交流发现并说服利益相关者。

尽管研讨会涵盖了更高级的可视化工具,即tidyverse中的ggplot包,但 R 中仍然有各种内置的可视化功能,不需要任何外部包。

因此,我建议您在探索其他外部包之前,先尝试一下以下内置函数:

  • 散点图或线图: plot( )
  • 在现有图的顶部添加图形: points( )
  • 在现有地块上画直线: abline( )
  • 方框图: boxplot( )
  • 直方图:
  • 柱形图: barplot( )
  • 饼状图: pie( )

至此,我们总结了使用 R 和 RStudio 的基本知识。

请注意,研讨会仅仅介绍了 R 的基础知识,对于 R 的实际能力还只是皮毛,尤其是与其他强大的外部包结合使用时。

尽管如此,我希望这个研讨会能够帮助你开始使用 R,但更重要的是,启发你继续发现你可以使用编程语言做什么。

感谢阅读,快乐学习!

相当于 7 只普通熊猫的行动

原文:https://towardsdatascience.com/r-equivalent-of-7-common-pandas-operations-76b632fc801b?source=collection_archive---------35-----------------------

熊猫和数据表库实践教程

诺德伍德主题公司在 Unsplash 上拍摄的照片

Python 和 R 是数据科学生态系统中的两个关键角色。这两种编程语言都提供了大量非常有用的库。

说到数据分析和操作,有两个库非常突出:R 的“data.table”和 Python 的 Pandas。

我一直在使用两者,但我不能真正宣布一个优于另一个。尽管我个人更喜欢“data.table ”,但我还没有遇到任何任务不能用这两者来完成。

在本文中,我将使用 Pandas 和“data.table”带您完成 7 个典型的数据分析操作。因此,这对双方都是一个好的实践。

在早先的文章中,我已经使用 NumPy 和 Pandas 生成了模拟销售数据。我将使用这些数据作为例子。让我们从导入库和读取数据集开始。

#Pandas
import numpy as np
import pandas as pdsales = pd.read_csv("/content/mock_sales.csv")
sales.head()

(图片由作者提供)

#data.tablelibrary(data.table)sales <- fread("~/Downloads/mock_sales.csv")
head(sales)

(图片由作者提供)

示例 1

查找列中的唯一值。

#Pandassales.ProductGroup.unique()
array(['D', 'A', 'C', 'B'], dtype=object)#data.tableunique(sales$ProductGroup)
[1] "D" "A" "C" "B"

示例 2

查找一列中唯一值的数量。

在我们的示例中情况并非如此,但是当列中唯一值的数量很大时,您可能会对唯一值的数量感兴趣,而不是显示所有的值。

我们可以通过添加一个字母“n”来完成这项任务。不过,对于熊猫和“data.table ”,添加的位置是不同的。

#Pandassales.ProductGroup.nunique()
4#data.tableuniqueN(sales$ProductGroup)
4

示例 3

计算每个产品组的平均日销售额。

对于这个任务,我们使用 Pandas 的 groupby 功能。用“data.table”稍微简单一点。

#Pandassales.groupby("ProductGroup").agg(
   avg_daily_sales = ("SalesAmount", "mean")
)#data.tablesales[, .(avg_daily_sales = mean(SalesAmount)), by=ProductGroup]

数据表输出(图片由作者提供)

实例 4

找出销售额最高的 3 家商店。

计算部分类似于前面的例子。我们只需更改列名和聚合函数。与上一个示例不同,我们按降序对结果进行排序,并选择前 3 名。

#Pandassales.groupby("StoreCode").agg(
   total_sales = ("SalesAmount", "sum")
).sort_values(
   by="total_sales", ascending=False
)[:3]#data.tablesales[, .(total_sales = sum(SalesAmount)), 
      by=StoreCode][order(-total_sales)][1:3]

熊猫输出(图片作者)

实例 5

创建一个名为“IsHigh”的新列,如果销售额大于 40,则取值 1,否则取值 0。

有几种方法可以完成这个操作。例如,我们可以如下使用 numpy 的 where 函数:

#Pandassales["IsHigh"] = np.where(sales["SalesAmount"] > 40, 1, 0)sales.head()

(图片由作者提供)

在 R 端,ifelse 函数可用于此任务。

#data.tablesales[, IsHigh := ifelse(SalesAmount>40,1,0)]head(sales)

(图片由作者提供)

实例 6

找出“IsHigh”列取值为 1 的不同天数。

我们已经介绍了如何使用一列中不同值的数量。该任务涉及一个额外的过滤操作。

#Pandassales[sales["IsHigh"]==1]["Date"].nunique()
731#data.tableuniqueN(sales[IsHigh==1]$Date)
731

例 7

查找产品组列中每个不同值的出现次数。

Pandas 的 value_counts 函数返回不同的值以及出现的次数(即行数)。

使用“data.table ”,我们可以根据产品组列对行进行分组,并使用 N 函数对行进行计数。

#Pandassales.ProductGroup.value_counts()
B    164475 
D    164475 
A    164475 
C    164475#data.tablesales[,.N,ProductGroup]
   ProductGroup      N
1:            D 164475
2:            A 164475
3:            C 164475
4:            B 164475

每组出现的次数是相同的,因为我是这样生成数据的。方法没有错😊

结论

R 和 Python 都有几个第三方库,对数据科学家帮助很大。在大多数情况下,一项任务可以使用两种语言的库来完成。因此,宣布一个优于另一个将是一个有争议的讨论。

在本文中,我们做了 7 个例子来演示一些任务的 R 和 Python 版本。

感谢您的阅读。如果您有任何反馈,请告诉我。

初学者:学习如何像专家一样可视化数据

原文:https://towardsdatascience.com/r-for-beginners-learn-how-to-visualize-data-like-a-pro-840d1828c09c?source=collection_archive---------10-----------------------

r 教程

基于案例研究的介绍,将引导您开始使用 ggplot2 库创建高质量的图形并了解世界

作者图片

如果你曾经想分析数据,并想在你的技能组合中添加一个新的工具包,供专业数据科学家使用,那么请继续阅读。

本教程不会给出一个枯燥的命令列表,而是使用一个具体的案例研究作为一个激励性的例子,向您介绍您需要了解的关于 ggplot2 的一切,gg plot 2 是在 r 中创建高质量图形的事实上的标准。它是一个由 tidyverse 生态系统支持的第三方库。虽然您可以使用基本库在 R 中绘图,但您最有可能在任何实际项目中使用 ggplot2。因此,我强烈建议您可以直接使用这个库进行绘图。

本教程适合任何希望为他们的数据科学项目学习 R 的人。它不需要任何编程或 R 方面的背景知识。唯一的先决条件是您有一台可以上网的计算机,并且已经安装了 R 和 RStudio。然而,如果他们还没有安装,你可以查看我的简短视频教程这里,这将帮助你设置 R,然后 RStudio 在任何时间。一旦你设置好你的电脑,回到本教程,让我们开始破解。

如果我是 Python 用户怎么办?

如果您习惯于 Python,您可能不太熟悉图形范式的语法,图形范式提供了一种更系统的可视化数据呈现方式。这类似于语言中的语法,我们通过结合名词、动词、冠词等来造句。使用特定的规则。因此,我仍然鼓励你通读并理解这个新的范例。你再也不会以同样的方式看待图形了。

ggplot2 是图形范例语法的一个 R 实现。它的对等物在 Python 中有一个不太出名的库, plotnine

案例研究动机

我想向你们介绍已故的瑞典统计学家汉斯·罗斯林,他最著名的演讲是他试图用实际数据来辩论普遍持有的世界观。

在他的全球健康课上,他让他的学生在给定的两个国家中找出哪个国家的死亡率更高。平均而言,学生们得到了 1.8 分的正确答案(满分为 5 分)。然后,他对一组精选的教授(他们有诺贝尔奖)重复了这个测试,他们的平均分数是 2.4 分。教授们比学生们做得稍微好一点,但是这个分数仍然比那些对世界一无所知的随机回答的人(例如黑猩猩)差。

为什么一只黑猩猩会比成绩优异的学生和少数教授做得更好?

这是因为我们的世界观被关注负面事件的耸人听闻的媒体所主导,给了我们一个扭曲的世界观。汉斯·罗斯林创立的 Gapminder 基金会的使命就是用数据来纠正这种扭曲的世界观。我们有包括联合国各部门在内的各种组织收集的关于世界的大量数据。然而,到目前为止的挑战是访问隐藏在不知名的服务器的表中的数据,并适当地呈现这些数据。

在本案例研究中,我们将使用 Gapminder 基金会的数据来回答 3 个关键问题:

1:人均寿命和人均 GDP 有关系吗?

2:在过去的 50 年里,全世界的预期寿命有什么变化?

世界真的分成了两个截然不同的群体吗,西方和其他国家(发展中国家)?

使用这个案例研究,我们将创建六种图:散点图、折线图、箱线图、直方图、密度图和条形图。

图形语法

在我们进入情节之前,这里有一个非常简短的关于 ggplot2 实现的图形语法的迂回,这是帮助你开始的最低要求。

图形是数据的可视化表示。它帮助我们将 数据 映射到 审美 属性的 几何对象 中。前面提到的每种图形类型都是一个“几何对象”。每个“几何物体”都会有一些美学属性。例如,折线图是一个几何对象,而 x 或 y 位置以及颜色是相关联的美感。每个数据图形都需要三个基本组件:

  • 数据
  • 几何对象(由 geom 指定)
  • 属性(由 aes 指定)

如果你有以上三个,你可以把它们结合在一起,形成一个有意义的图形对象。如果这感觉有点抽象,不要担心。一旦您看到相关的代码,并意识到通过组合这三个组件来绘制复杂的图形是多么简单,这一切都将变得有意义。

散点图

我们将从绘制散点图开始。散点图是帮助我们可视化两个数字量之间关系的基本绘图类型之一。我们将使用散点图来回答我们的第一个问题,人均 GDP 和预期寿命之间的关系。这两个量都是数字,因此这是一个合适的图,可以让我们了解两者之间的关系。

数据加载和检查

让我们从导入数据开始。我们将使用来自 Gapminder 基金会的数据。你可以直接从他们的网站上获取数据。然而,对于本教程,我们将使用一个名为 gapminder 的第三方库。(这个库是由 Jenny Bryan 开发的,她现在在 RStudio 的 tidyverse 团队工作)。

打开 R Studio,创建一个新的 R 脚本文件。让我们从安装第三方包开始。你只需要第一次这样做。

install.packages("gapminder")

安装后,加载软件包。每次打开新的 R Studio 会话时,您都需要这样做。

library(gapminder)

还要加载 tidyverse 包,它加载了几个包,作为包括 ggplot2 在内的整个生态系统的一部分(如果您愿意,也可以单独加载每个单独的包)。如果这是您的第一次,那么首先安装 tidyverse 包。

install.packages(tidyverse) # required only the first time
library(tidyverse)

gapminder 包中有几个数据集。我们将使用的是“gapminder”。继续检查数据集。有几个命令可以检查数据。您可以使用 head 和 tail 命令来提供数据集中的前几行或后几行。但是,我强烈推荐使用的命令是 summary 命令。

head(gapminder, n=8) # list the first 8 rows in the dataset
tail(gapminder, n=10) # list the last 10 rows in the dataset
summary(gapminder) # recommended method

summary 命令返回数据集中所有列的总体摘要。这为您提供了数据集的良好鸟瞰图。如下面的输出所示,我们可以看到 gapminder 数据集有 6 列,我们还可以看到分类列的每个类别的行数,以及汇总统计数据(平均值/中值/最大值/最小值等。)用于数字列

summary 命令的输出提供了数据集的鸟瞰图(图片由作者提供)

基本情节

现在让我们创建我们的第一个散点图。我们将使用 ggplot 函数,然后传递三个基本组件:

  • 数据(我们希望绘制的数据)
  • geom(我们期望在图中看到的对象类型,如线、条、点)
  • aes(几何对象的美学属性,如位置、颜色、形状和大小)

让我们将这三个组件传递给 ggplot 函数,并将结果对象赋给一个新变量, p

p <- ggplot( data=gapminder, aes(x = gdpPercap, y = lifeExp)) + geom_point()
p

这是你的第一个情节,p 应该是这样的。

你的第一个散点图(图片由作者提供)

我们现在将通过添加额外的层来逐步构建这个图形,很快你就会看到它用最少的代码带来了多大的变革。

将 x 轴缩放到对数刻度

虽然人均国内生产总值和预期寿命之间似乎确实存在联系,但 x 轴的刻度似乎被压缩了。我们可以简单地通过向现有的 p 对象添加一个额外的命令,将 x 轴刻度改为对数刻度。

p <- p + scale_x_log10()

将 x 轴转换为对数标度后的散点图(图片由作者提供)

这样看起来更好,我们现在可以看到一个清晰的关系。

分类数据的颜色属性

虽然散点图显示了数值之间的关系,但我们仍然可以通过使用颜色属性来包含分类数据信息。让我们分别代表各大洲。

p <- p + aes(color=continent)

散点图现在用不同的颜色显示每个大陆(图片由作者提供)

其他美学

有几种额外的美学可以用来将数据映射到视觉提示中。一般来说,我不会指望有人记得他们。你可以从 RStudio 网站上获得一份官方的备忘单。一旦你习惯了这一点并加以练习,它就会成为你的第二天性。您将记住您经常使用的美学,并且您将更有能力查看官方文档或备忘单来使用适合您的应用程序的任何美学。

在我们的例子中,让我们尝试使用另一种美学,size 属性,并使用它来表示人口数量。

p + aes(size=pop)

散点图现在显示大小属性(图片由作者提供)

添加抖动以显示重叠点

散点图的一个局限性是它不能区分一个位置的大量重叠点和具有少量点的不同位置。处理这类案件的两种方法是:

  • 更改散点图中每个数据点的透明度。这由名为 alpha 的变量控制,该变量可以传递给 geom_point()命令。默认情况下,alpha 的值为 1,对应于 100%的不透明度。您现在可以尝试更改 geom_point 中的 alpha 值,看看会得到什么。
Hint: geom_point(alpha = 0.1)
  • 向点添加抖动,这相当于给每个数据点一个小的推动,让它在随机方向上稍微移动。要创建抖动图,我们只需用 geom_jitter 替换命令 geom_point,并使用宽度和高度属性指定抖动量。下面是一个将抖动与指定给身高和体重的特定值结合使用的示例。继续使用这些值,看看当你在抖动中改变高度和宽度属性时会有多大的不同。请理解,添加抖动只是为了视觉外观。
p + geom_jitter(width=2,height=2)

添加趋势线

虽然您可以通过查看数据点来直观地了解趋势,但是您也可以绘制一条与数据点拟合的实际直线。你所需要做的就是简单地添加一个额外的层叫做 geom_smooth()。在下面的代码中,我们向“line width”参数传递了一个额外的值,这导致为每组数据点拟合一条粗线。

p+geom_smooth(lwd=3)

向每组数据点添加趋势线(图片由作者提供)

有了上面的散点图,我们可以清楚地回答我们的第一个问题。 人均寿命和人均 GDP 有很强的关系。 这对于所有 5 大洲都是如此。

折线图

像散点图一样,线图也显示两个数值之间的关系。当其中一个变量被排序时(例如时间线),它们通常更有用。我们将使用折线图来调查预期寿命在过去 40 年中是如何变化的。

我们需要做的就是使用 geom_line()参数,而不是 geom_point()。

p <- ggplot( data=gapminder, aes(x = year, y = lifeExp)) + geom_line()

这就给了我们下面的剧情。

没有任何分组并使用所有数据的线图(图片由作者提供)

但是,我们希望为每个国家分别绘制数据。因此,我们可以使用“组”的美学参数按国家对数据进行“分组”。

p<-p+aes(group=country)

线形图,每个国家一个(图片由作者提供)

这看起来更好,我们可以看到从 1950 年到 2010 年的增长。然而,我们希望看到每个大陆的趋势,因为图表目前看起来很混乱。

p <- p +  aes(color=continent)

每个国家的线图,按洲分开(图片由作者提供)

刻面

除了用颜色来区分不同的类别,我们还可以把它们分成小的支线剧情。我们可以通过刻面层做到这一点。下图是我们在原始命令中添加了一个小平面层后生成的。然后对每个由“大陆”分隔的子情节使用相同的 x 和 y,变量传递给 faceting。

p <- p +  facet_wrap(~continent)

多年来的预期寿命,每个大陆一个分图使用刻面(图片由作者提供)

您还可以使用“子集”功能对数据进行子集化,并查看选定的国家列表。让我们比较一组精选的国家,画出这些年来它们的预期寿命。我任意选择了阿富汗、中国、日本、科威特、马拉维、巴基斯坦、瑞典和美国。

ggplot(subset(gapminder, country=='Afghanistan' | country=='China' | country=='United States' | country=='Sweden' | country=='Japan' | country=='Kuwait' | country=='Malawi' | country=='Pakistan'), aes(x=year,y=lifeExp,colour=country))+geom_line(lwd=1.5)

过去 40 多年来一些选定国家的预期寿命(图片由作者提供)

上面的线图清楚地向我们显示,在过去的几十年里,各大洲所有国家的预期寿命都有所增加。

箱线图

箱线图给我们一种数值变量分布的感觉,通过并排绘制箱线图来比较多个变量是非常有用的。让我们用箱线图来形象化各大洲的预期寿命分布。

ggplot(data=gapminder, aes(x=continent, y=lifeExp))+geom_boxplot()

显示五大洲预期寿命分布的基本箱线图(图片由作者提供)

您可以使用 geom_point()或 geom_jitter()在箱线图顶部添加原始点。下面是一个使用 geom_jitter()函数并更改异常值颜色的示例,这样我们就可以将这些异常值与剩余的原始数据区分开来。

ggplot(data=gapminder, aes(x=continent, y=lifeExp))+geom_boxplot(outlier.colour = "red")+geom_jitter(width=0.2)

显示五大洲预期寿命分布的基本箱线图,以及带有抖动的原始点(图片由作者提供)

该图清楚地表明,虽然欧洲和大洋洲的平均预期寿命高于其他大陆,但其他大陆的一些国家的预期寿命比欧洲和大洋洲的一些国家要高得多。

柱状图

另一个能说明变量分布的图表是直方图。我们可以使用 geom_histogram 创建一个直方图,并为洲指定“fill”属性,这样我们就可以使用不同的颜色来比较洲。

ggplot(data=gapminder, aes(fill=continent, x=lifeExp))+geom_histogram()

显示各大洲预期寿命分布的柱状图(图片由作者提供)

与直方图密切相关的图是使用概率密度图。这里,我们将使用 geom_density 并指定一个 alpha 值来调整透明度,以便更好地查看重叠的地块。让我们也使用 xlab 和 ylab 函数来适当地标记这两个轴。

ggplot(data=gapminder, aes(fill=continent, x=lifeExp))+ geom_density(alpha = 0.2)+xlab('Life Expectancy')+ylab('Probability Density Function')

这将给我们以下情节:

显示各大洲预期寿命分布的概率密度函数图(图片由作者提供)

箱线图、直方图和密度图越来越清楚地表明,东西方之间似乎是一个连续体,而不是二分法。

条形图

箱线图和直方图都有助于我们可视化数值变量的分布。然而,我们有时可能需要了解分类数据的分布。这相当于计算给定分类变量中类别的数量。geom_boxplot()允许我们绘制条形图,默认情况下,它计算给定变量中类别的数量。让我们使用 continental 变量绘制一个条形图,它将给出每个洲的记录数。

ggplot(data=gapminder, aes(x=continent))+ geom_bar()

柱状图显示了我们在各大洲的记录总数(图片由作者提供)

最后,我们还可以修改上面的代码来查找给定年份(2007 年)的记录数,并翻转坐标,这样我们就可以看到水平的条形。

ggplot(data=subset(gapminder, year==2007), aes(x=continent))+ geom_bar() +coord_flip()

水平条形图显示了给定年份我们在各大洲的记录总数(图片由作者提供)

摘要

本教程向您介绍了 ggplot2 库,以帮助您用六种不同类型的图来可视化您的数据(下面提供了可视化摘要)。使用来自 Gapminder 基金会的数据向您提供了一个完整的演示。上面列出的所有代码在我的 github 中也有。

散点图、折线图、箱线图、直方图、密度图和条形图,每个的用途,以及相关的几何对象命令(图片由作者提供)

https://ahmarshah.medium.com/membership

初学者 r—第 1 部分:数据结构

原文:https://towardsdatascience.com/r-for-beginners-part-1-data-structures-2b49685ea2ed?source=collection_archive---------23-----------------------

数据科学教学

一篇以非常简单的方式学习 R 语言的交互式文章。

图片来自 PixabayTumisu

在这篇文章中,我为初学者开始了一系列的教程,以一种非常简单和交互式的方式学习 R 语言。互动?是的,真的互动:)

在完整的 R for 初学者第 1 部分教程中,您将学习如何处理最常见的数据结构:

  • 矢量
  • 矩阵
  • 因素
  • 数据帧
  • 列表。

这篇文章是关于向量的。完成第 1 部分教程后,您将能够使用这些数据结构:)。

1 创建一个向量

向量是相似事物的集合,它们必须具有相同的类型。例如,数字向量或布尔向量。可以通过函数 combine 创建一个向量:c()例如,下面一行代码创建一个包含四项的向量a

a <- c(12, 23, 4, 34)

现在,你可以尝试用三个项目分别创建两个向量populationcountries:100, 120, 30”Italy”, “France”, “Germany”。您可以使用以下控制台进行游戏:

解决方案是:

population <- c(100, 120, 30)
print(population)countries <- c("Italy", "France", "Germany")
print(countries)

如果你试着创建下面的向量v <- c('a', 10, TRUE)呢?由于所有项目必须属于同一类型,它们都被转换为字符串!

2 分配名称

您可以为向量的每个项目指定一个名称。这可以通过应用于矢量的names()功能来完成:

names(population) **<-** countries

或者,可以在构建向量时指定名称,如下所示:

population <- c('Italy'=100, 'France'=33)

你可以回到 R 控制台,试着给population向量的每一个项目分配一个名称。

解决方案是:

names(population) <- countries

如果您再次打印population向量,您会看到向量的每一项都有一个名称。

3 基本统计

您可以通过sum()函数计算一个向量的所有项的和:

sum(population)

以及通过mean()功能得到的各项的平均值。

mean(population)

你可以在之前的 R 主机上试试这些功能。

4 选择项目

可以通过以下方法访问项目:

  • 按索引
  • 名叫
  • 按条件

按索引选择

按索引选择非常简单:只需在方括号中传递项目的位置号。注意,索引从 1 开始,而不是从 0 开始,因为几乎所有其他编程语言都是这样。

population[1]

它返回值100

还可以访问 vector 的多个项目:例如,可以访问 vector 的元素 1 和元素 3。

population[c(1,3)]

或者,您可以通过:符号访问向量的一些连续项。例如,您可以访问从 1 到 3 的项目:

population[1:3]

按名称选择

您可以使用以下语法通过相关名称访问给定项目:

population['Italy']

您可以通过以下 R 控制台进行选择:

按条件选择

最终,您可以通过指定条件来访问项目。例如,您可以访问值等于或大于 100 的所有项目。首先,您指定条件:

condition <- population >= 100

然后将条件应用于向量:

sub_population <- population[condition]

有条件练吧。能否选择population向量中所有名称等于FranceItaly?的项目,尝试使用条件,而不是按名称选择。

您可以利用以下控制台进行练习:

https://replit.com/@alod83/Vectors#select_cond.r

前面练习的解决方案可能如下:

condition <- names(population) == 'Italy' | names(population) == 'France'
sub_population <- population[condition]

5 向量间的运算

向量之间的运算行为如下:运算应用于相同位置的项目。基本运算包括和、差、除、乘。例如,以下两个向量的和:

a <- c(1,2,3)
b <- c(4,5,6)
a + b

将返回以下向量:

5, 7, 9

如果两个向量有不同的名称,给定操作的结果保持第一个向量的名称。

我们来练习一下!计算两个向量之间的差。哪个名字有合成向量?

解决方案是:

result <- population1 - population2

一个非常有用的操作是连接,允许将一个向量附加到另一个向量上。这通过组合功能c()完成,如下所示:

c(a,b)

你可以尝试连接两个向量population1population2。你有哪个结果?

前面练习的结果如下:

result <- c(population1, population2)

摘要

在这篇互动文章中,我已经说明了如何在 r 中构建向量。如果你想通过另一篇互动文章学习矩阵,请继续关注:)

如果你想了解我的研究和其他活动的最新情况,你可以在 TwitterYoutubeGithub 上关注我。

相关文章

https://medium.com/analytics-vidhya/basic-statistics-with-python-pandas-ec7837438a62

初学者 r—第 2 部分:使用矩阵

原文:https://towardsdatascience.com/r-for-beginners-part-2-working-with-matrices-364dcaa6e601?source=collection_archive---------35-----------------------

数据科学教学

初学者快速入门指南,帮助他们开始使用非常流行的统计和数据分析软件。

作者图片

R 软件是一款非常流行的统计计算和图形软件。它提供了许多也可以用于数据科学,尤其是数据分析的包。

本文属于初学者 R 系列,试图帮助初学者入门 R 软件。在我的上一篇文章中,我处理了向量。在本文中,我将讨论矩阵,并特别关注以下几个方面:

  • 创建矩阵
  • 为行和列指定名称
  • 选择项目
  • 用新的行或列扩展矩阵
  • 基本统计。

1 创建一个矩阵

矩阵是一个多维向量,它包含相同类型的元素,如字符或数字。在 R 中,矩阵是通过matrix()函数定义的,该函数接收行数和如何对元素排序(按行还是按列)作为输入。

在下面的例子中,我们定义了一个包含 8 个元素的矩阵,并通过nrow参数指定将元素分布在两行中。我们也可以指定按行获取元素(byrow=TRUE):

m <- matrix(c(1,2,4,5,7,8), nrow=2, byrow=TRUE)

作者图片

或者按列(byrow=FALSE):

m <- matrix(c(1,2,4,5,7,8), nrow=2, byrow=FALSE)

作者图片

我们也可以用先前定义的向量定义一个矩阵,简单地连接相关的向量:

population2018 <- c(123, 345, 300)
population2019 <- c(100, 120, 30)
m <- matrix(c(population2018, population2019), nrow=3, byrow = FALSE)

2 分配名称

我们可以通过colnames()函数给矩阵的每一列设置一个名称:

colnames(m) <- c("Population2018", "Population2019")

以及行的名称:

rownames(m) <- c("Italy", "France", "Germany")

作者图片

3 选择项目

可以进行选择:

  • 名叫
  • 按索引

3.1 按名称选择

选择一行/一列

我们可以选择给定行的所有项目(注意行名后的逗号):

m['Italy', ]

作者图片

选择一列的所有元素(注意列名前的逗号) :

m[, 'Population2018']

作者图片

选择更多行/列

该程序也可用于从矩阵中删除行/列。选择部分行的所有项目:

m[c('Italy', 'Germany'),]

作者图片

选择部分列的所有元素:

m[,c('Population2018', 'Population2019')]

3.2 按索引选择

我们可以选择属于第一行的所有项目:

m[1,]

作者图片

或者,我们可以选择属于第二列的所有项目:

m[,2]

作者图片

4 扩展矩阵

新的列和行可以分别通过cbind()rbind()功能添加到矩阵中。

4.1 向矩阵中添加一列

我们可以添加一个新的列,以column_name=vector的形式将它传递给cbind()函数,如下所示:

m <- cbind(m, Population2021=c(123,23,125))

作者图片

注意到

  • cbind()接收要扩展的矩阵作为第一个参数
  • 新向量的长度必须与矩阵的行数相同

4.2 向矩阵中添加一列

rbind()函数与cbind()函数的语法相同。因此,可以添加一个新行,如下所示:

m <- rbind(m, Spain=c(123, 200, 12))

作者图片

5 基本统计

如果一个矩阵的所有行/列都是数字类型,可以计算一些基本的统计数据。要检查一个矩阵的类型,我们可以执行下面的函数:

str(m)

它给出了以下输出:

num [1:4, 1:3] 123 345 300 123 100 120 30 200 123 23 ...
 - attr(*, "dimnames")=List of 2
  ..$ : chr [1:4] "Italy" "France" "Germany" "Spain"
  ..$ : chr [1:3] "Population2018" "Population2019" "Population2020"

请注意,类型是数字(num),因此我们可以继续。我们可以按行或按列对元素求和。

5.1 按行求和

在这个具体的例子中,我们可以按国家计算总人口:

rowSums(m)

作者图片

我们可以将结果添加为新列:

enriched_m <- cbind(m, Total=rowSums(m))

作者图片

5.2 按列求和

与按行求和类似,按列求和可以通过以下函数实现:

colSums(m)

作者图片

在前面的示例中,我们已经按年份计算了总和,因此我们可以将其添加为新行:

enriched_m <- rbind(m, Total=colSums(m))

作者图片

摘要

在本教程中,我已经介绍了 r 中的矩阵。需要提醒的是矩阵的所有元素必须是同一类型。如果您想要管理不同的类型,您应该构建一个数据框架或列表。因此,请继续关注下一篇文章。

现在 Medium 提供了一个新特性,即它允许构建列表如果您喜欢这篇文章,您可以将其添加到您的收藏夹列表,只需点击按钮,放在文章右上角的按钮:

作者图片

如果你想了解我的研究和其他活动的最新情况,你可以在 TwitterYoutubeGithub 上关注我。

相关文章

https://medium.com/analytics-vidhya/a-gentle-introduction-to-descriptive-analytics-8b4e8e1ad238 https://medium.com/geekculture/the-top-25-python-libraries-for-data-science-71c0eb58723d

r 慢——都是你的错!

原文:https://towardsdatascience.com/r-is-slow-and-its-your-fault-2fcedacc7abb?source=collection_archive---------13-----------------------

教程| R |优化代码

了解您的工具对您的成功至关重要

卢卡·安布罗西Unsplash 上拍照

任何在数据科学领域工作的人都熟悉 R。您肯定遇到过有人认为 R 是一种缓慢的语言,无法处理更大的数据。事实并非总是如此。我在野外看到的许多 R 代码表明,对这种语言的工作原理缺乏基本的理解。让我们看一个例子,如何优化你的代码来与你合作,而不是与你作对。

https://realdrewdata.medium.com/membership

为什么循环很慢?

让我们从 R 编程语言是如何工作的开始。这就是所谓的解释语言。这意味着在运行代码之前你不需要编译任何东西,计算机只是解释并运行它,给你结果。这有助于加快您编写和测试代码的速度,但缺点是通常执行起来较慢。在处理过程中有很多开销,因为 R 几乎每次查看变量时都需要检查变量的类型。这使得改变类型和重用变量名变得容易,但是对于非常重复的任务,例如在循环中执行一个动作,会降低计算速度。

让我们以一个简单的编程问题为例:找出给定数字的所有因子。为此,我们可以查看从 1 开始一直到给定数字的每个数字,包括给定数字。如果我们的数字是 2048,这将是从 1 到 2048 的所有数字。我们将 2048 年除以这些可能的因素。如果余数是 0,则该数是一个因子。

对于初学编程的人来说,用 For 循环可以很容易地解决这个问题。我们将 2048 赋给一个变量x。然后我们可以创建一个空白向量来存储找到的因子。

x <- 2048factors <- c()

现在是 for 循环。我们从 1 到x。在 for 循环中,我们将创建一个 if 语句。if 语句的条件将是x模(%% ) i等于 0。模运算将给出每个数除以x的余数。当这个值为 0 时,我们将执行下一行,将i的值添加到我们的因子向量中。

for (i in 1:x) {
    if (x %% i == 0) {
        factors <- c(factors, i)
    }
}

这种方法没有任何问题。在一台相当标准的计算机上运行它根本不需要任何时间。您也将在因子向量中获得正确的值。那么这种方法有什么问题呢?

当 R 使用循环时,它必须在每次循环运行时检查xi的值。在我们看来,很明显这些总是数字,但是 R 仍然需要每次都检查。当你想找出一个更大的数字的因数时,比如 2,048,000,这就成了一个问题。在我的电脑上,同样的循环用了大约 26(!)秒求 2,048,000 的所有因子。

如果你想在你的机器上运行它,使用rbenchmark包。使用benchmark函数,给你的代码起一个名字,比如 Loop。将该名称设置为我们的循环,您将在输出的 elapsed 部分获得您的执行时间。请注意,代码运行了多次,运行时间是所有运行的累计时间。

library(rbenchmark)benchmark(
    "Loop" = {
        loop_factors <- c(1)
        for (i in 2:x) {
            if (x %% i == 0) {
                loop_factors <- c(loop_factors, i)
            }
        }
    }
)# Output
# test replications elapsed relative user.self sys.self user.child
# Loop          100    27.5        1     27.44        0         NA
# Sys.child
#        NA

还有另一种方法

现在我知道你在说什么了,用 python(或者另一种更快的语言)!但是如果你知道它是如何工作的,没有理由 R 不能更快地处理这个任务。我们需要做的就是消除每次循环时检查每个变量类型的开销。大概是这样的:

factors <- (1:x)[(x %% 1:x) == 0]

好了,问题解决了!运行速度快了 7 到 8 倍,并且给出了相同的正确答案。但这都是为了理解为什么我喜欢代码是可读的,所以当我们重写这个优化的语句时,让我们更深入一点。

首先,我们已经确定了 R 花很长时间运行循环的部分原因是因为它在每次循环时都要检查变量的类型。幸运的是,R 被设计成利用向量来避免这种情况。要求向量的每个元素都具有相同的类型,所以 R 在计算时不需要检查每个元素。因此,为了优化,我们将使用矢量化运算。

首先,我们将得到我们的数x,并使用模运算符(%%)来得到除以从 1 到x的每个数时的每个余数。我们可以使用1:x来创建从 1 到x的所有数字的向量。当我们用一个数字向量对单个元素x取模时,R 用向量的每个元素除x,返回余数的向量。

remainders <- x %% 1:x

接下来,我们要测试哪些余数是 0。类似于余数运算的工作方式,我们可以将整个remainders向量与 0 进行比较。这将返回一个向量TRUE / FALSE,其中TRUE表示余数为 0。

true_false <- remainders == 0

现在,我们可以使用true_false向量来过滤我们可能的数字,1 到x,给出我们最终的答案。

(1:x)[true_false]

让我们飙车吧

让我们再次检查我们的计算速度,比较所有三个版本:for 循环版本、单行向量优化版本和可读向量优化版本。我将再次使用benchmark函数,这次添加一个向量和向量可读。x再次设置为 2,048,000。

benchmark(
    "Loop" = {
        loop_factors <- c(1)
        for (i in 2:x) {
            if (x %% i == 0) {
                loop_factors <- c(loop_factors, i)
            }
        }
    },
    "Vector" = {
        loop_factors <- (1:x)[(x %% 1:x) == 0]
    },
    "Vector Readable" = {
        remainders <- x %% 1:x
        true_false <- remainders == 0
        loop_factors <- (1:x)[true_false]
    }
)# Output
#              test replications elapsed relative user.self sys.self
# 1            Loop          100   27.52    7.519     27.38     0.00
# 2          Vector          100    3.66    1.000      2.99    0.66
# 3 Vector Readable          100    3.77    1.030      3.24    0.50
#
#   user.child sys.child
# 1         NA        NA
# 2         NA        NA
# 3         NA        NA

在输出中,运行最快的代码的相对值为 1。每隔一个片段将显示相对于最快的片段所用的时间的多少倍。在我的例子中,单行版本比可读版本稍快。这很可能是因为我们存储了步骤之间的输出,使其更具可读性。您还可以看到,for 循环比 vector 版本慢 7.5 倍。向量确实有助于优化你的代码!

太棒了。r 用向量做任何事都快得多,对吗?嗯,R 还是有一些限制的。虽然使用向量可以大大加快计算速度,但 R 仍然在内存中完成大部分计算。所以一旦我们达到足够大的数字,R 将无法分配计算所需大小的向量,比如 2,048,000,000。事情是这样的:

结论

所以最后,了解你的工具是很重要的。r 是一种非常容易掌握的语言。学习一些技巧,比如利用向量,可以加快计算速度,并有助于保持 R 在工具箱中的相关性。你还需要知道你的工具的局限性。学习用向量优化你的代码是有用的,但是一旦你遇到一定规模的数据和计算,你可能要开始寻找一种新的语言。

R Shiny —支持从亚马逊 S3 高效下载文件

原文:https://towardsdatascience.com/r-shiny-enable-efficient-file-downloads-from-amazon-s3-f575bfb21244?source=collection_archive---------43-----------------------

理解大数据

了解如何使用亚马逊 S3 签名网址在 R Shiny 上高效下载文件。

保罗·花冈在 Unsplash 上的照片

尽管 R universe 中有一些优秀的软件包可以与 R 中的 S3 一起工作,尽管所有这些软件包都可以很容易地与 R shiny 无缝集成,但当我通过 R Shiny 处理大文件时,仍然会面临一些有趣的挑战。让我们先问自己一个简单的问题。

我们如何从 S3 存储桶中访问文件来执行数据分析?

从 R 访问 S3 数据

感谢我们提供的软件包,从 R 访问 S3 数据从来没有这么简单过。

aws.s3 包包含与 s3 REST API 集成的强大功能,允许用户以编程方式管理他们的 S3 桶。从个人经验来看,代码库的文档、可用性和可读性都非常好。

我不打算深入研究如何使用这个包,但是让我们来探索当我们试图从 S3 桶中获取数据时会发生什么。

作者图片,使用 draw.io 生成

函数调用向 S3 发出请求。该函数在幕后处理身份验证,最后将请求的数据返回给客户端。

上面描述的工作流最适合在本地执行探索性工作,因为正如您所料,数据是通过网络从 S3 传输到您的本地机器上的。

然而,在 R Shiny 中使用这种方法不再是最有效的方法,因为我们的代码不再是“客户端”

从 R Shiny 获取 S3 数据

当使用 R Shiny 时,重要的是要记住我们编写的代码要么在实例上执行,要么在由 shiny-server 启动的 worker 上执行。客户端现在是实际连接并使用我们的应用程序的最终用户。

上面描述的工作流在一个闪亮的应用程序中工作;然而,我们现在面临两个潜在的用例。

  1. 从 S3 获取数据,在闪亮的应用程序中使用这些数据或执行一些分析,并以可视化方式呈现这些数据。
  2. 以数据下载的形式将数据直接交付给用户。

用例#1 —查询、消费和呈现 S3 数据

作者图片,使用 draw.io 生成

在上图中描述的工作流中,目标是

  1. 从 S3 获取数据。
  2. 消费来自server(...)内部的数据来做一些有用的工作。
  3. 以可视化的形式呈现数据。(可选)

为了实现这一点,我们可以重用上面给出的代码。下面的代码摘录中提供了一个例子,sans 数据可视化代码。

用例 2 —提供直接数据下载

天真的方法

如果我们使用以前的方法作为直接数据下载来交付数据,这将给我们带来一个新的问题。下面是代码和工作流的样子。

作者图片,使用 draw.io 生成

查看上面的图表和代码,我们可以开始发现流程中的低效之处。工作进程首先请求数据。然后,一旦数据通过网络返回,它就被写入客户机的文件系统。数据在网络上有两次传输。如果我们能减少一半,那将大大提高我们系统的效率。

授权签名

授权签名允许 R Shiny 生成一个临时 URL,客户机可以用它来访问数据。这个 URL 使客户端能够直接从 S3 存储桶下载数据,基本上绕过了所有中间步骤。这将网络数据传输减少了一半,并大大缩短了下载时间。

作者图片,使用 draw.io 生成

如何生成经过身份验证的 URL

虽然使用签名来生成经过认证的 URL 并不是一种新颖的方法,但据我所知,R 中目前可用的包肯定没有提供一种简单的方法来实现这一点。我在亚马逊 AWS 官方文档的帮助下开发了一个助手函数来生成认证的 URL。

使用这个助手函数,从 S3 下载文件的闪亮代码将如下所示。

摘要

在上面给出的例子中,当向客户端提供数据下载时,使用aws.s3从 Shiny 中查询数据的简单方法是一种次优的方法。更有效的方法是使用aws.signature生成一个签名,然后用它生成一个签名的 URL。然后,客户端可以使用签名的 URL 直接下载数据。

对于未来的工作,将这一功能整合到aws.s3中会很有用。这样,该软件包将更接近于成为 R and R 闪亮 S3 所有相关事务的一站式商店。

一如既往,任何建设性的反馈,错误,或更有效的方法总是受欢迎的。你可以通过推特或者下面的评论联系我。

r 对一辆杜卡迪。哪个更快?

原文:https://towardsdatascience.com/r-vs-a-ducati-which-is-faster-3797c9a306c6?source=collection_archive---------46-----------------------

用 R 和传单绘制亨利·克鲁斯打破世界纪录的环球航行的美丽地图,只用了 8 行代码!

图片作者。我对杜卡迪扰频器的解读。

准备好来一次狂野之旅吧

2019 年,亨利·克鲁成为骑摩托车环游世界的最年轻的人。在一年的漫长旅程中,他骑着杜卡迪沙漠雪橇行走了 55,000 英里。

亨利的旅程始于英国伦敦的自行车棚俱乐部。从这里,他骑行穿过欧洲、俄罗斯、中东和东南亚。然后一架飞机向下(并穿过)澳大利亚。最后他飞到了南美洲,一路骑到了美国的东海岸。最终他回到了伦敦!

我知道你很想跳上你的摩托车,把所有的烦恼抛到九霄云外,跟随着亨利的脚步。但是…

  1. 生活似乎成了阻碍
  2. 所有这些数据都不会流向科学本身

为什么我们不试着通过 R 的视角来体验亨利的冒险呢?所以,拉起离合器,换到第一档,踩下油门,抓紧,因为我们出发了!

获取数据

要在地图上绘制点,您需要点。我知道,神魂颠倒。

这些分数没有我想象的那么容易获得。据我所知,没有一个地方简明地展示了亨利的环球旅行。那么当数据不存在的时候我们该怎么办呢?我们要不要继续做其他项目,让果实挂在藤蔓上更低的地方,触手可及?或者也许我们应该撇撇嘴,把拳头举到空中,诅咒这一天已经明确抛弃我们的数据之神?

那鸿我们只是制作自己的数据。

根据亨利的博客,我能够非常准确地把英国和印度联系起来。然而,他的博客在他的旅程进行到一半时就结束了,所以在旅行的剩余时间里,我从他的一些采访中收集了一些关于哪些国家先于其他国家的逻辑。下载我生成的数据这里

也就是说,我们离英国越远,我就越有可能错过一两步。但是我们并没有在火星上发射火箭,所以让我们顺其自然吧!

首先,我将展示我用 R 绘制的地图,然后我将展示产生这些地图的 8 行代码。

欧亚大陆

欧洲到俄罗斯

图片作者。

俄罗斯到缅甸

图片作者。

缅甸到澳大利亚

图片作者。

美洲

智利到哥伦比亚

图片作者。

哥伦比亚到伯利兹

图片作者。

伯利兹到美国,然后回到英国

图片作者。

r 和传单

传单是一个用 r 语言创建地图的极好的库。只需几行代码,你就可以创建漂亮的地图。首先,你将需要从这里抓取数据。

当在 R 中工作时,我更喜欢创建一个 R 项目,这样我就可以对我所有的代码使用相对路径。或者,您可以从上面的链接下载 csv 文件,并将“dfGeo”的路径更改为您保存文件的位置。代码如下:

我们从使用传单和 tidyverse 包装开始。如果您还没有这些软件,请安装它们:

install.packages("leaflet")
install.packages("tidyverse")

然后,我们将 csv 导入 tibble(数据帧的 tidyverses 版本)

dfGeo <- read_csv('Henry Crews Journey - Geo.csv')

如果您想查看我们的数据,请在控制台中键入“dfGeo ”:

作者图片

leaflet(data = dfGeo) %>%
  addTiles() %>%

这将调用传单库并传递 dfGeo 作为数据源。添加图块只会生成一张空白地图。

addMarkers(lng=~Long, lat=~Lat, label=~Label, labelOptions = labelOptions(noHide = T,  direction = "bottom",textsize = "17px")) %>%

添加标记在地图上标出我们的点。查看上面的数据(dfGeo),您可以使用波形符调用小叶参数内的列(aka ~Long 是“Long”列)。Label 是我们数据集中的国家名称。

LabelOptions 只是让文本稍微大一点,默认显示标签。否则,您必须单击标记来显示标签。方向只是说把标签放在标记的底部,而不是顶部或左侧等。

addProviderTiles(providers$Stamen.Watercolor) %>%

这将贴图设置为雄蕊。水彩地图,如果你问我,它看起来很酷。如果你不喜欢老式的水彩画,你可以在这里使用更多的地图模板。

addPolylines(data = dfGeo, lng = ~Long, lat = ~Lat, group = ~Label, color = 'red', dashArray = '10,20')

最后,我们添加连接每个标记的线条。它们也是基于纬度和经度的,因为我是按顺序将数据放入 csv 的,所以一切正常。dasharrray 只是把线设置成虚线,而且相当大。

仅此而已。您可以从 github 下载完整的 R 项目,其中包括代码和 csv 文件。你只需运行项目,它将全部工作!

在 GitHub 上下载完整的项目!

R vs Python,我们把答案框好一点。

原文:https://towardsdatascience.com/r-vs-python-lets-frame-the-answer-better-af7943b005c4?source=collection_archive---------52-----------------------

尽管有很多关于这个话题的文章,但是很少有文章提供帮助人们选择的论据。

图片由达比·布朗宁皮克斯拜拍摄

这个问题似乎挥之不去,有无尽的文章试图帮助人们选择数据分析和数据科学的“正确语言”。在我看来,我读过的文章没有一篇是有帮助的。大多数包括:

  • 关于每种语言创造者的历史介绍——虽然有趣,但对做决定没有帮助。
  • 广泛的比较包括差异像 Python 是面向对象编程,R 是函数式语言。这对这个领域的新手来说没有任何意义。不管怎样,这无关紧要。这就像比较西班牙语和英语的目的一样;了解这些差异对你选择有帮助吗?
  • 代码的演示,通常比较代码的长度和执行代码所花费的时间。通常结论是,对于某些事情,一种语言需要更多的代码行,而在其他情况下,情况正好相反,而执行时间的差异通常是以秒计的。但这两件事都不重要。如果你知道最长的德语单词是 42 个字母,而西班牙语单词是 32 个字母,这会帮助你选择语言吗?
  • 一张显示了最常见的语言,其中 Python 出现在列表中的更高位置。像这样的排名经常将 Python 放在首位。这是一种误导,因为编程语言不仅仅用于数据科学,Python 可以用于软件开发,这就是为什么它看起来更受欢迎,因为它有点像瑞士军刀。
  • 学习曲线比较,其中,取决于在哪里阅读,答案要么是 Python,要么是 R,这要简单得多。但是这个决定不应该基于简单,而应该基于目的。
  • 而我最喜欢的结论是,学两个都没坏处,因为多点技能更好。我可以看到懂得更多语言的吸引力,但这无助于迈出第一步。

这篇是一篇有用的文章,因为作者分析了招聘信息和最常与 R 和 Python 联系在一起的词。最大的结论是,如果你想从事研究工作,你最好学习 R。这是有道理的,因为 R 是由统计学家开发的,所以任何需要大量分析和研究的工作都使用这种语言。相反,Python 是由计算机科学家开发的。虽然这个技巧很有用,但是技术已经进步了,在用不同语言编码时,有种方式来协作。更重要的是,这里有一个使用 R 的公司列表(包括脸书、亚马逊、谷歌,他们也使用 Python,所以 R 不仅仅在学术界使用)。

总而言之,这些信息实际上对做出决定没有帮助,因为每当你认为一种语言不能做一些事情时,随着行业的发展,已经有一种解决方案提出来了。

我先学会了 R,但不是因为我有选择。回到 2018 年,巴塞罗那唯一一个教授数据科学的训练营在 R 和 RapidMiner 中授课。我毕业一年后,AllWomen 和 Ironhack 启动了他们的数据科学项目。两者都教授 Python,遵循一系列著名的学院,如 General Assembly、LeWagon、Metis,课程如由 Udacity 提供的 Nanodegree,巴塞罗那大学的大数据硕士,Kaggle 的 Python 课程以及许多其他课程。

我编写了数据科学课程,在 AllWomen 推出了这个项目,并选择用 Python 来做。我边走边学,这并不难,因为我知道我在寻找什么。我之所以做出这个决定,是因为我在数据科学领域发现的一切都在推广 Python,包括像皮拉迪斯·BCN 这样积极组织聚会的聚会,相比之下,当时没有举办活动的拉迪斯·BCN 则是积极组织聚会的。甚至连初级职位的工作都很难找到(我指的是巴塞罗那和欧洲)。

我的建议是,你应该选择你的社区所说的语言,这样你就有了一个支持团体,并且不会感到处于不利地位。一旦你学会了那种语言,如果你需要并且好奇,你可以花时间把另一种语言加入到你的技能中。

2019 年,我在 AllWomen 举办了一场活动,我们讨论了这个问题。我确实陷入了用最传统的方式比较这些语言的模式,直到我发现热沙玛谢赫的作品为什么女性在 R 中欣欣向荣而在 Python 中却落后。如果你想看的话,这里有幻灯片。它们是在知识共享署名共享的许可下使用的,这意味着你可以对这些幻灯片做任何事情,只要你给我信用。

你是 R 还是 Python 女?Violeta Mezeklieva 的发言

r 对 Python 对 Julia

原文:https://towardsdatascience.com/r-vs-python-vs-julia-90456a2bcbab?source=collection_archive---------0-----------------------

编写高效的代码有多容易?

澳大利亚八月在 Unsplash 拍摄的照片

TL;DR:直接跳到整体对比部分

如果你是一名数据科学家,你很有可能用 Python 或 r 编程。但是现在有一个叫 Julia 的新人,他承诺了类似 C 语言的性能,而不损害数据科学家编写代码和与数据交互的方式。

在我的上一篇文章 中,我将 R 与 Julia 进行了比较,展示了 Julia 如何为数据科学社区带来了令人耳目一新的编程思维。主要的收获是,有了 Julia,您不再需要向量化来提高性能。事实上,循环的良好使用可能会带来最佳的性能。

在这篇文章中,我将 Python 加入其中。数据科学家选择的语言有一句话要说。我们将解决一个非常简单的问题,其中内置实现可用,从头开始编写算法非常简单。目标是当我们需要编写高效的代码时,理解我们的选择。

通过线性搜索的成员测试

让我们考虑在一个未排序的整数向量上的成员测试问题。

**julia**> 10 ∈ [71,38,10,65,38]
true**julia**> 20 ∈ [71,38,10,65,38]
false

原则上,这个问题是通过线性搜索解决的。该算法遍历输入向量的元素,直到找到被搜索的值(成功搜索)或到达向量的末尾(不成功搜索)。目标是告诉一个给定的整数是否在向量中。

为了评估 R、Python 和 Julia 中的不同实现,我用 1 到 2.000.000 的 1.000.000 个唯一整数生成了一个数据集,并对 1 到 1.000 的所有整数执行了 1.000 次搜索。搜索成功的概率约为 50%,因此该算法有一半的时间会扫描整个向量以得出搜索不成功的结论。在剩余的情况下,算法应该需要( n+1)/2 次评估(平均)来找到元素,其中 n 是向量的长度。

我通过取 3 次运行的 CPU 时间中位数来衡量每个实现的性能。关于运行实验的硬件和软件的更多信息可以在这里找到。

请注意,这些实验的目标不是对不同的语言和实现进行精确的基准测试。目标是强调当性能很重要时,语言给数据科学家带来的障碍。

实施情况

我在 C 语言中实现了线性搜索,以掌握静态类型编译语言的性能,并设置基线。这个二进制可执行文件花费了 0.26 秒的 CPU 时间来执行 1000 次搜索。

r 实施

我在 R 中尝试了不同风格的成员测试,从专门的操作符(in)到使用循环的类 C 实现,绕过了矢量化方法。

当我们从in_search转移到for_search时,我们对算法有了更好的控制。然而,随着控制的增加,r 中的性能会下降。使用矢量化运算比遍历元素直到找到匹配项要快一个数量级,如在vec_search中,矢量化运算执行全数组扫描。就像我之前的文章一样,尽管需要更多的内存和(多余的)操作,矢量化还是取得了成功。不出所料,专门的操作符in具有最高的性能和更干净的代码。

我也尝试了 Map-Reduce 操作,但没有耐心等到它们完成…如果你追求性能,这不是一个选项。

Python 实现

老实说,最初的目标是只使用原生函数和原生数据结构,但是在使用 Python 的原生列表时,in操作符比 R 慢了大约 10 倍。因此,我还包括了 NumPy 数组的结果(它为 Python 带来了矢量化的操作)。CPU 时间从 9.13 秒到 0.57 秒,大约是基线的 2 倍。然而,当转向循环方法时,原生列表领先了一个数量级…我给了 NumPy 第二次机会,在 Numba 包中添加了 JIT 编译。Numba 有一些限制,但使用起来很简单:你只需要包含 Numba 包,并标记你想看到编译后 JIT 的函数(并仔细阅读手册)。带有 NumPy 加 Numba 的循环提供了与矢量化/专门化操作相当(或更好)的性能,但是实现这一点并不容易,因为其中有几个陷阱。说到陷阱,用 Numba 在原生列表上执行循环是令人失望的…再次,我停止了执行,因为它花了超过 5 分钟才完成。

Julia 实现

在 Julia 中,我加入了更多的风格来展示本地可用功能的多样性和性能。

除了向量化操作之外,性能非常接近 C 中的实现,性能下降了 20%-50%。矢量化的性能相当不错,大约是 4 倍 C 的 CPU 时间,但在矢量化操作上也是 2 倍 NumPy 的 CPU 时间。你得到的自由是难以置信的,因为你可以在 Julia 中编写几乎任何算法!为了在 For 循环上获得最佳性能,我使用提示告诉编译器不要检查索引是否在数组的范围内(inbounds宏),并告诉编译器它在执行迭代的顺序上有额外的自由(simd宏)。如果您想知道,不提供这些提示会使循环的性能接近于in_search

总体比较

(使用 ggplot2 和 ggrepel 在 R 中绘制的图)

看看这个简单问题的并列结果,我们发现:

  • Julia 的性能在实现上几乎独立地接近 C;
  • Julia 中的例外是在编写类似 R 的矢量化代码时,性能下降了大约 3 倍;
  • 当将 JIT 编译(Numba)添加到 Python 时,基于循环的实现接近了 Julia 的性能;尽管如此,Numba 对您的 Python 代码施加了约束,使得这个选项成为一种折衷;
  • 在 Python 中,在原生列表和 NumPy 数组之间做出选择,以及何时使用 Numba:对于经验较少的人来说,哪一个是最好的数据结构(性能方面)并不明显,也没有明确的赢家(特别是如果您包括动态添加元素的用例,这里不涉及);
  • R 不是最快的,但是你得到了与 Python 相比一致的行为:R 中最慢的实现比最快的慢了~ 24 倍,而 Python 中是~343 倍(Julia 中是~ 3 倍);
  • 原生 R 总是比原生 Python 表现更好;
  • 每当在 Python 或 R 中无法避免循环时,基于元素的循环比基于索引的循环更有效。

细节很重要…

我可以在这里停止这篇文章,写用 Julia 写高效的代码是多么的无缝。尽管如此,细节很重要,程序员需要注意 Julia 的内部结构。你能猜出最影响性能的代码是哪一行吗?这里有一个提示:你将不会在之前展示的任何片段中找到它…

这是:

map(line -> parse(Int, line), eachline(f))

这行代码解析输入文本文件 f ,每行包含一个数字(注意,读取文件不是基准的一部分)。那么,这一行代码有什么特别之处呢?简而言之,朱莉娅推断:

  • 匿名函数(map的第一个参数)返回的元素类型(始终)是整数;
  • 因此,映射的输出是一个整数数组。

因为 Julia 知道存储的是一个整数数组,所以它分配了一个连续的内存块,其中每一项都包含一个整数。这允许高效的读取操作。

怎么才能搞砸呢?这里有一种方法:

a = []
for line in eachline(f)
    push!(a, parse(Int, line))
end

好像差不多吧?然而:

> typeof(a)
Array{Any,1}

语句a = [] 虽然看起来很方便,但它创建了一个Any数组,这意味着您可以在数组的每个元素上存储任何类型的数据。在内部,Julia 在内存中存储了一个指针数组,以配合Any提供的灵活性。因此,在处理数组时,Julia 无法再处理连续的内存块。对性能有什么影响?大约慢 50 到 100 倍!

修复这段代码相当简单:a = Int[](而不是a = [])会完成这项工作,因为它指定了元素的类型。

结果

在本文涉及的所有语言中,Julia 显然是最容易编写高效代码的。尽管如此,你需要知道你在做什么。幸运的是,性能提示可以让你走上正轨。

编码快乐!

我经常使用这三种语言,而且我都很喜欢。每个人都有自己的位置。

代码可在github.com/dcmoura/blogposts获得

你可以在 TweeterLinkedInVimeo ( 查看我的数据 vizs!)

如果你喜欢这篇文章,你可能也会喜欢 把数据科学家从矢量化的魔咒中解放出来

R vs Python:初学者应该学什么?

原文:https://towardsdatascience.com/r-vs-python-what-should-beginners-learn-88fc62396bb2?source=collection_archive---------9-----------------------

意见

放下任何疑虑或困惑,做出正确的选择,然后专注并成长为一名数据科学家。

照片由AMIT·兰詹Unsplash 上拍摄

我目前领导着一个研究小组,小组成员是同时使用 R 和 Python 的数据科学家。我在这个领域已经超过 14 年了。这些年来,我见证了这两种语言的发展,现在这两种语言背后都有一个繁荣的社区。

我的旅程并不平坦,我经历了很多艰难的事情。然而,你可以避免犯我所犯的错误,走上一条更专注、更有收获的道路,比别人更快达到你的目标。

在我开始之前,让我们先弄清楚一些事情。r 和 Python 只是做同样事情的工具。数据科学。这两种工具没有一种天生就比另一种好。这两种工具都已经发展了很多年(并且很可能会继续发展)。

因此,关于应该学习 Python 还是 R 的简短回答是:视情况而定。

如果你能抽出几分钟的时间,更长的答案将帮助你专注于真正重要的事情,并避免大多数渴望成为专家数据科学家的热情初学者所犯的最常见的错误。

了解您的数据科学职业目标

首先,试着明确你的总体目标是什么。你为什么要读这篇文章?是因为你已经下定决心要成为数据科学的专家,或者至少要变得比现在的自己更优秀。你现在需要决定如何投资你有限的时间。也许你希望得到你在数据科学领域的第一份工作。或者你可能希望得到一份更好的工作。或者,您可能有数据,并且希望从事自己的数据科学项目。

无论您的环境如何,最终,您都需要在数据科学项目上拥有丰富的实践经验。要做到这一点,您应该对典型的真实世界数据科学项目的每个阶段都有经验(从数据收集到最终解释)。

一个 Kaggle 项目还不够好!

了解典型的真实世界数据科学项目需求

一个典型的、真实世界的数据科学项目包括收集数据、将数据放入平台、精确定义问题、处理和清理数据、可视化数据、应用各种模型、评估各种模型以及讲述一个故事。

为了实现您的任何数据科学职业目标,您需要清楚地了解完整的项目流程。你需要知道你选择的模型,以及为什么它们是最合适的。你需要清楚你使用的可视化图形。你需要讲一个有意义的故事!

所有这些任务都可以在 Python 和 r 中完成。

您可能选择了错误的模型,在 Python 和 r 中都犯了同样的错误。

如果我把你招进我的团队,我不在乎你使用的工具是 R 还是 Python。然而,重要的是你如何选择你的模型,你如何描述你的问题,以及你如何解释和证明你的方法。总会有新的工具出现。当项目需要这些工具时,我希望你继续学习它们。

然而,如果你不能告诉我一个令人信服的数据科学故事,并回答基本的语言无关的问题,我不会雇用你!

避免这个常见的错误

R 和 Python 都可能支持任何典型数据科学项目的 99%的用例。然而,许多有抱负的数据科学家犯了过分重视工具的错误。

为了提高效率,他们中的一些人尝试学习 R 和 Python 的语法。坦白地说,这不是你学习时间的最佳方式。试图成为这两方面的专家不会错的。但是把你有限的时间分配在两种工具上,让你在两种工具上拥有一般的技能,而在任何一种工具上都是专家,这是浪费时间。

当涉及到 Python 和 r 时,不要什么都不懂。

获得正确的心态

你需要保持开放的心态,把这些语言当成工具。你当然应该选择一门语言,在这门语言上获得全面的经验,这样它就会成为你的首选语言。

你需要这种语言来进行快速原型制作和快速初步分析。

然而,如果项目中有特定的方面需要你去做,不要犹豫去学习第二语言。例如,在部署模型方面,Python 将会更胜一筹。然而,对于统计分析来说,R 可能更优越。但是这些并不是一成不变的,工具也在快速发展。

你应该先学什么?

这是一个粗略的指南,并不是一成不变的,但是如果你完全没有头绪,你可以把它作为你的起点。

如果你是团队的一员,而他们正在使用某种语言,那么采用这种语言使你的生活更容易(为了共享代码和专业知识)是有意义的。

如果你正在学习一门特定的课程,选择这门课程支持的语言是有意义的。

如果你已经有了编程的背景,那么 Python 可能看起来更自然。

如果您可能主要关注统计分析,并且您正在从事研究,R 可能更适合作为第一选择。

如果你想要更详细的对比,这里有一张详细的信息图。不过,坦率地说,我不会把那里的任何事情都一成不变。随着工具的发展,许多这样的比较已经过时了!

做真实世界的项目

德国心理学家赫尔曼·艾宾浩斯在 19 世纪末对自己进行了记忆实验,产生了著名的“遗忘曲线”(在现代研究中被复制)。你会以指数速度忘记你所学的一切。一周之内,你很可能会忘记你所学的大部分内容。

然而,你可以通过使你的学习课程更有意义、更具互动性和相关性来补救这种情况。

在学习 R 或 Python 时,您如何做到这一点呢?通过做一个你觉得有意义和相关的实际项目。

你不需要为了学习语法而坐看几个小时的教程学习任何语言的语法(Google、Stack Overflow,甚至一个合适的 IDE 都可以纠正语法)。这是做真实世界项目的经验,这将有助于巩固你的知识,并使你有一种语言作为你的首选语言。

最后的想法

确保你真的精通一门语言,并且能够自如地使用它们的语法(不需要太多的搜索)和调试器。你想要达到一个水平,以便在需要时能够进行快速分析。

同时,随着您变得更加熟练,保持开放的心态,如果有特定的方面可以通过不同的语言或工具得到更好的服务,不要回避学习不同的语言。

归根结底,这些只是工具,而不是竞争。

https://ahmarshah.medium.com/membership

健康数据科学中的种族和民族

原文:https://towardsdatascience.com/race-and-ethnicity-in-data-science-138acf1c23b4?source=collection_archive---------34-----------------------

公平与偏见业内笔记

为什么它很重要,以及我们应该如何对待它

照片由乔恩·泰森Unsplash 拍摄

不可否认,考虑种族或民族(缩写为 R/E;在本文中用作单数名词,尽管本文中的陈述将统称为种族和民族)在定量研究医疗保健结果中是重要的。去问任何一位受人尊敬的统计学家/流行病学家/数据科学家,他们至少会告诉你这么多!我认为,虽然对 R/E 重要性的理解无处不在,但我们总是可以努力建立一个更强大的基础词汇,来解释为什么重要。我想写一篇文章,旨在从(相当成熟的)关于建模中的 R/E 的文献中总结结论。具体来说,我想在解释性和预测性的上下文中简要介绍一下 R/E(要了解更多关于这种差异的信息,请查看我以前的文章中关于这个主题的内容!).

下面是这篇文章的大纲。虽然我是根据我个人对 R/E (1)的理解程度来排列主题的。这个变量代表什么 2。我们如何记录这个变量的测量值 3。为什么这个变量重要 4。我们如何对这个变量做出统计结论),请随意跳到你最感兴趣的话题!

  • 关于种族和民族差异的几点说明
  • 在模型中编码 R/E
  • 关于预测模型中的 R/E 要知道的事情
  • 在解释(因果)模型中解释 R/E
  • 摘要

种族和民族之间的差异

一个被广泛接受的关于种族和民族的区别是,种族指的是身体特征的集合(被认为是),而民族包含了一个人身份的社会文化成分。这里有一个简单的例子来描述这两者:

一个女孩出生在中国,父母是中国人,但在婴儿时期,她被意大利的一个意大利家庭收养。在种族上,她从小就感觉自己是意大利人:她吃意大利菜,她说意大利语,她了解意大利的历史和文化。她对中国的历史和文化一无所知。但是当她来到美国,她被当作亚洲人来对待。

ThoughtCo 的一篇文章更详细地描述了这种差异,但我想让你注意的一点是,当我们考虑 R/E 时,无论是在我们的样本人群、目标人群中,还是在我们建立的任何模型中的协变量中,我们都必须确切地知道我们在处理什么。否则,例如,在一个模型中包括种族,然后在未考虑种族时解释或应用该模型可能会导致有偏见、不准确或不完整的结论。种族和族裔作为单独的数据变量被收集是很常见的,因此理解这种差异是每个人为了更好地理解他们的数据而可以采取的一个基本步骤。关于模型中 R/E 的含义的更详细讨论将在下面的“解释性模型中的 R/E”中讨论。

编码 R/E

种族和民族是所谓的名义分类变量。这意味着类别没有固有的顺序,不像冷、暖、热那样有基于温度的固有顺序。有许多不同的方法来编码分类变量(加州大学洛杉矶分校 IDRE 分校有一个不错的列表,但是两种常见的方法是简单虚拟编码。

在 R 中,可以用下面的方式对这个变量进行简单编码:

# simple encoding
# A tibble: 4 x 3
  personid desired_encoding race    
     <dbl>            <dbl> <chr>   
1        1                1 white   
2        2                2 black   
3        3                3 asian   
4        4                4 hispanicdf$race <- factor(df$race, levels = c("white", "black", "asian", "hispanic")

您可以在其中明确指定比赛的级别。在这种编码中,您从回归模型中获得的任何估计值都将反映某个种族的因变量(如血压)的平均值与白人的平均值的比较,因为白人被指定为参考水平。

或者,您可以像这样对此变量进行伪编码:

# A tibble: 4 x 5
  personid is_white is_black is_asian is_hispanic
     <dbl>    <dbl>    <dbl>    <dbl>       <dbl>
1        1        1        0        0           0
2        2        0        1        0           0
3        3        0        0        1           0
4        4        0        0        0           1

这些估计值将有所不同,因为它们现在反映了不同的比较(即,is_black比较黑人与非黑人的预期因变量)。你应该选择一个能为你的目标带来最有意义的比较的编码系统。

要避免的一个陷阱是:不要使用 R/E 作为连续变量!换句话说,在简单编码系统中,确保它被模型理解为一个因子变量。否则,它将生成 R/E 的估计值,假设 race 中的线性关系,这是无意义的。

此外,了解数据中的 R/E 是复杂的也是有用的,因此本文不会深入讨论如何处理交叉的或更具体的 R/E 标识。

预测模型中的 R/E

在预测建模中考虑 R/E 可能有许多微妙的方式,但在这一节中,我将重点关注“非 R/E 代表”数据集中潜在偏差的类型以及围绕 R/E 作为预测变量的争议。

偏差的类型

多年来,预测算法中的种族偏见在新闻(谷歌“大猩猩”种族貌相事件)、书籍(例如数学毁灭武器)和大量期刊文章中受到了大量关注。在医疗保健中,通常,算法的目标是产生告知健康需求的预测或测量(例如,预测疾病风险)。这一目标的关键是阐明该算法是针对谁的——预测 40-65 岁之间的美国男性的疾病风险?英国 18 岁以下儿童?如果我们不考虑世卫组织,我们就有产生偏见的风险。加州大学伯克利分校的医生研究员 Ziad Obermeyer 写了几篇文章和最近的算法偏差剧本,描述了两种主要类型的偏差:表示偏差测量偏差。

对于第一种类型, Sjoding et。艾尔。提供了一个突出的例子,说明使用脉搏血氧饱和度测量来预测患者血氧水平的算法在黑人患者身上表现不佳,因为该算法主要是针对白人患者进行训练的。在这个例子中,R/E 是重要的,因为在理想的情况下,你将在一个有代表性的数据集上进行训练,以便做出公共卫生决策(关于为什么在其他情况下,一个没有代表性的研究不应该总是被立即丢弃的切线)。如果数据不可用,那么至少,除了预测模型之外,还应该提出强烈而明确的警告。

对于第二种类型, Obermeyer 描述了一个场景,从开始,模型中预测的变量可能有偏差。例如,将医疗保健成本视为未来健康需求的代理变量(代理反映了我们在衡量某些方面的最佳尝试)。一个模型发现,黑人的医疗成本预计比白人低,因此一个天真的后续措施是将有针对性的政策努力集中在改善白人的健康结果上。然而,这一结果实际上来自于这样一个事实,即由于歧视和难以获得医疗保健,美国黑人的数据记录成本较低。

R/E 作为预测变量?

作为一个独立于反映与 R/E 相关的结构性不平等的数据的主题,我想提供一个 R/E 如何建模(或不建模)的简要概述。种族和族裔包括在一些流行的预后临床预测模型中,如心血管疾病的合并队列方程NIH 乳腺癌风险评估工具。然而,您可能会惊讶地听到,包含 R/E 并不完全常见,事实上,只有 3% (23/854) 的心血管预测模型包含 R/E。在预测模型中包含 R/E 在文献中存在很大争议,我将尝试提供一个简短的论点总结。

反对将它包括在内的主要原因是,一些人声称它唤起了种族敏感决策中种族定性的主要因素。主要的观点是,医疗领域的种族貌相与执法或保险领域的种族貌相有着根本的不同。塔夫茨大学的研究员杰西卡·保卢斯认为,尽管在执法等领域,模型中的种族偏见会导致明确的惩罚和奖励,但在医疗保健领域的影响却不太清楚。低估和高估风险各有利弊,因此将 R/E 作为一个变量可以提高预测的准确性,从而(平均而言)改善临床决策。相比之下,预测模型没有考虑种族因素(所谓的“种族盲”),这可能会降低所有 R/E 组(以及基于预测结果的决策)的准确性,尤其是那些在数据中代表性不足的组。

公平地说,R/E 是一个复杂的变量,虽然一些模型已经发现 R/E 的一些表现是一个统计上显著的预测器,但它的预测能力如何实际有效还有待观察,因为 R/E 是多么微妙和不稳定(在这篇文章中了解更多)。

解释性模型中的 R/E

在这一节中,我将简要介绍在因果语境中解释 R/E 的挑战。记住解释性模型的目标通常是建立因果机制。了解健康结果中的种族差异以改进有针对性的公共卫生政策和干预措施的目标由来已久。解释性模型通常用于理解与某些健康结果相关的种族差异的程度。围绕这一主题的一篇开创性文章讨论了如何深思熟虑地解释并将 R/E 作为一个变量包含在解释模型中,这篇文章来自哈佛大学教授 Tyler VanderWeele。

这是一个复杂的话题,就我个人而言,我建议在阅读本文之前,先了解一些因果推理的基础知识。

问题以下列常见方式出现:

你有一个结果要解释,并且你对 R/E 感兴趣作为你的主要曝光。你还记得从你的统计类调整混杂因素,所以你选择一些你觉得相关的变量。你建立模型,得出 R/E 的回归估计。

正是在这一点上,VanderWeele 建议你应该停下来,重新评估你迄今为止所做的事情。你如何解释回归估计?挑战在于,当你在模型中加入其他变量时,对 R/E 的解释将会改变(如果你真的想进入兔子洞,这里有一个全面但相对抽象的关于混杂因素含义的回顾)。基本上,下图说明了混杂因素的一些特征,因为它会影响暴露(如 R/E)和结果(如心血管风险)。

作者自己的形象

然而,如果目标是理解暴露如何导致结果背后的潜在因果机制,我们不得不质疑 R/E 真正包含了什么?是肤色的生物效应吗(比如深色皮肤可以抵御紫外线)?是因为对肤色的感知而产生的健康行为(比如医院里的歧视)?遗传背景?家庭社会经济地位?当我们想要对结果进行因果陈述时,我们想要控制尽可能多的变量(想想随机对照试验或科学实验)。然而,如果我们甚至不知道 R/E 效应的真正含义,将很难决定控制哪些变量以及如何解释 R/E。例如,VanderWeele 得出结论,对模型中 R/E 系数的一种可能解释是(在此转述):

如果您设置了 R/E 捕获的所有可想象方面的分布(即,身体表型、遗传学、SES 等),观察到的健康差异会发生什么?)表示一个 R/E 组等于另一个 R/E 组。

底线是在解释性模型中包含 R/E 不是一项简单的任务,您应该公开质疑您看到这种情况发生的地方。

总结和结论

建模中的 R/E 是一个复杂的话题,有很多讨论。许多杰出的专家都发表了自己的观点,至少,倾听这场对话让我个人成为了一名更有思想、更有知识的数据科学家和流行病学家。我将用一系列问题来结束本文,当您发现自己在自己的数据中处理 R/E 时,可以随时问自己一个问题。

  • 我可以向某人解释种族和民族的区别吗?
  • 我的数据中的 R/E 是如何编码的?编码符合我的研究问题吗?
  • 这个预测模型对谁有效?
  • 这个模型中是否有任何变量可能会受到历史或系统的种族偏见的影响?
  • 在这个解释模型中,R/E 是如何解释的?有意义吗?

简化的径向基函数神经网络

原文:https://towardsdatascience.com/radial-basis-function-neural-network-simplified-6f26e3d5e04d?source=collection_archive---------1-----------------------

深度学习

径向基函数神经网络简介

Zoltan·塔斯Unsplash 上的照片

R 径向基函数(RBF)网络的架构与大多数神经网络架构完全不同。大多数神经网络结构由许多层组成,并通过重复应用非线性激活函数来引入非线性。另一方面,RBF 网络仅由一个输入层、一个隐藏层和一个输出层组成。

作者图片

输入层不是计算层,它只是接收输入数据并将其输入到 RBF 网络的特殊隐藏层。隐藏层内部发生的计算与大多数神经网络非常不同,这就是 RBF 网络的强大之处。输出层执行预测任务,如分类或回归。

输入层

输入层只是将数据提供给隐藏层。

作者图片

因此,输入层中神经元的数量应该等于数据的维数。在输入层中,不执行任何计算,这与标准人工神经网络的情况不同。输入神经元完全连接到隐藏神经元,并向前馈送它们的输入。

隐蔽层

隐藏层接受模式可能不可线性分离的输入,并将其转换到更可线性分离的新空间。隐藏层比输入层具有更高的维数,因为不可线性分离的模式通常需要被变换到更高维的空间中,以便更可线性分离。这是基于 Cover 关于模式可分性的定理,该定理指出,通过非线性变换转换到更高维空间的模式更可能是线性可分的,因此隐藏层中的神经元数量应该大于输入神经元的数量。也就是说,隐藏层中神经元的数量应该小于或等于训练集中样本的数量。当隐层神经元的数量等于训练集中样本的数量时,可以认为模型大致等价于核回归、核支持向量机等核学习器

隐藏层中的计算基于与原型向量的比较,原型向量是来自训练集的向量。

作者图片

隐藏层中的每个神经元都有一个原型向量和一个带宽,分别用μ和σ表示。每个神经元计算输入向量与其原型向量之间的相似性。隐藏层中的计算可以数学地写成如下:

作者图片

使用:

  • x 条作为输入向量
  • μ bar 作为 iᵗʰ神经元的原型向量
  • σ为 iᵗʰ神经元的带宽
  • φ为 iᵗʰ神经元的输出

参数μ bar 和σ以无监督的方式学习,例如使用某种聚类算法。

输出层

输出层对分类或回归任务使用线性激活函数。

作者图片

输出层中的计算就像标准的人工神经网络一样执行,它是输入向量和权重向量之间的线性组合。输出层中的计算可以数学地写成如下:

作者图片

使用:

  • wᵢ作为重量连接
  • φ作为 iᵗʰ神经元从隐藏层的输出
  • y 作为预测结果

得到的预测可用于分类或回归任务,这取决于目标和损失函数。参数 w 以监督的方式学习,例如梯度下降。

虽然 RBF 的输出层可以作为最终输出,但是也可以将 RBF 网络与其他网络进行堆叠,比如我们可以用多层感知代替 RBF 网络的输出层,对网络进行端到端的训练。

结论

RBF 网络只包含一个单独的隐藏层,它有自己计算输出的方法。RBF 网络是基于覆盖定理,它利用其隐含层将数据投射到一个更高维的空间,因此隐含层的神经元数目应该大于输入层的神经元数目。输出层使用线性激活函数,或者可以认为没有任何激活函数。

参考

https://www.amazon.com/Neural-Networks-Deep-Learning-Textbook/dp/3319944622 http://vlabs.iitb.ac.in/vlabs-dev/labs_local/machine_learning/labs/exp3/theory.php https://www.sciencedirect.com/topics/engineering/radial-basis-function-network https://en.wikipedia.org/wiki/Radial_basis_function_network

Raft 算法讲解 2

原文:https://towardsdatascience.com/raft-algorithm-explained-2-30db4790cdef?source=collection_archive---------14-----------------------

第 2 部分—日志复制

介绍

Raft 是一种共识算法,用于以分布式方式编排副本。Raft 的设计考虑到了可理解性,它只有几个可移动的部分,很容易实现。在上一篇文章中,我讲了 Raft 的基础知识,解释了 leader-election 机制。在这篇文章中,我们将关注另一个重要的问题——日志复制。

基本原则

Raft 本质上是一堆复制的状态机。每当领导者节点接收到请求时,该请求都会被附加到日志中以获得持久性。相同的日志被复制到多台机器上以实现冗余。

图 0。木筏日志,作者图

例如,在图 1 中,当前领导者的日志中有四个条目。跟随者 1 完全同步,但跟随者 2 缺少最新条目。如果你不明白图 0 中的术语是什么,可以看看我之前的文章。

用于日志复制的 RPC

为了便于理解,Raft 只对所有通信使用两个 RPC 调用:

这个 RPC 由领导者发起,并携带最新接收到的命令。它也用作心跳消息。当一个追随者收到这个消息,选举计时器被重置。 AppendEntry 消息是这样的结构(我在这里使用 Golang)

如果你不明白每个字段的意思也没关系。我们将逐一检查它们。

RequestVote :此 RPC 用于领袖选举,由最后一个帖子覆盖。我将跳过它,因为我们只关心日志复制。

日志复制详细信息

让我们从一个普通的日志复制算法开始。每当领导者收到请求时,它会将日志条目转发给所有追随者,并等待大多数人的确认。

问题 1:日志排序

普通算法在跟随者一到达就向他们发送消息。为此,它只需要消息中的两个字段— leaderIDEntry 。一个主要问题是,由于潜在的消息延迟,日志顺序无法保留。考虑图 1 中的场景。

图一。日志排序问题,按作者分类

Raft 使用两个附加字段来确保日志的完整性— PrevLogIndexPrevLogTerm 。当发出一个新条目时,领导也发出之前条目的索引和术语号。在将新请求附加到其本地日志之前,接收方确保其最新条目具有相同的索引和术语。

图二。附录条目附加簿记,作者提供的数字

有了这两个额外的参数,我们可以实现一些了不起的东西:

  1. 给定日志的索引,如果来自两个日志(在两个不同的机器上)的条目共享同一个术语,则它们是相同的
  2. 如果不同日志中的两个条目具有相同的索引和术语,前面的所有条目都是相同的

第一个性质很容易证明。假设索赔是错误的。如果存在两个具有相同术语的不同条目,其中一个条目必须比另一个条目更晚被领导收到。由于日志是只追加的,其中一个条目将有一个更大的 PrevLogIndex 。但是,如果它们出现在两个不同日志的同一个索引中,它们必须有相同的 PrevLogIndex。(否则接收方拒收)矛盾!!

第二个主张可以用归纳法来证明:

图 3。权利要求 2 的归纳证明,图由作者提供

这两个保证一起构成了 Raft 的日志匹配属性

问题 2:条目冲突的关注者

如果领导者日志是群集中唯一的权威,它将覆盖追随者的任何冲突。有可能丢失一些日志条目吗?考虑以下情况:

图 4。日志冲突,按作者分类

在深入研究这个问题之前,我想让你相信上述场景确实会发生。索引 2 之前的日志是同步的。从那以后,可能会发生以下情况来创建这些日志:

1) node 2 becomes the leader (votes from itself, 1 and 0) with term 22) node 2 receives a request from the client but failed to sync with other nodes
3) node 0 becomes the leader (vote from 1 and itself) with term 3
4) node 0 receives requests from the client. but failed to sync

现在,如果节点 0 恢复与节点 2 的联系,它将尝试复制其日志,如图 5 所示

图五。覆盖冲突,按作者分类

如您所见,绿色条目确实被删除了。就目前的设计而言,这个问题是不可避免的。然而,这里的一个关键观察是绿色条目没有在大多数节点(至少两个节点)上复制。如果是,它将永远不会被删除,原因如下:

  1. 如果一个条目在多数节点上被复制,那么至少有 N/2 + 1 个节点拥有它
  2. 对于没有赢得选举的条目的节点,它需要来自其他节点的 N/2 张选票(候选人总是为自己投票)
  3. 由于候选人没有条目,有条目的 N/2 + 1 节点不会投票给它(第 1 部分解释了选举限制)
  4. 它不会获得足够的选票来赢得选举

这是 Raft 的第二个关键特性— 日志完整性属性。如果一个条目在多数节点上被复制,它将总是出现在未来的 leader 日志中,不管它可能是哪个节点。如果没有在大多数人身上复制,那么如果领导层发生变化,一个条目可能会被删除。

有了日志完整性属性,在大多数人拥有它之前不要确认客户机请求是很重要的。

问题 3:何时提交?

最后,我们到达最后一个问题——何时提交条目?首先,什么是提交,为什么提交?Raft 是一种低级的一致性算法,由键值存储等高级应用程序使用(例如 ZooKeeper)。当 Raft 安全地复制一个条目时,我们希望确保客户机可以通过应用程序看到它。因此,Raft 需要决定何时告诉上层应用程序一个条目已经准备好可以使用了(一个部分复制的条目,因为 leader 日志中的最后一个条目应该是不可见的!).

图六。提交,按作者列出的数字

到目前为止,我们的普通算法没有携带任何关于提交索引的信息。因为数据流是单行道,从领导者流向追随者,节点不知道一个条目是否在大多数节点上复制。

为了解决这个问题,我们可以向 AppendEntry 消息添加另一个字段,称为 LeaderCommit。如果多数人收到一个条目,则领导者增加提交指数。下面是跟踪 leader 节点使用的提交索引的实际代码。

领导者如何跟踪提交索引

摘要

在本文中,我们从一个普通的日志复制算法(广播条目,无需任何额外的簿记)开始,并通过考虑各种极端情况,将其发展为成熟的版本。日志复制中最重要的 RPC 是 AppendEntry RPC,它使用一个具有四个字段的结构:

  • Term :对于日志复制和领袖选举非常重要
  • LeaderId :显示候选人的身份。
  • 条目:领导者希望复制的条目列表。
  • PrevLogIndex :正好在条目[0] 之前的日志条目的索引。用于确保日志完整性和日志匹配属性
  • PrevLogTerm :紧接在条目【0】之前的日志条目的期限。用于确保日志完整性和日志匹配属性
  • LeaderCommit :对上层应用很重要。只有提交的条目才能应用于应用程序,并且对客户端可见。

Raft 算法,已解释

原文:https://towardsdatascience.com/raft-algorithm-explained-a7c856529f40?source=collection_archive---------6-----------------------

第 1 部分—领导人选举

介绍

Raft 是一种共识算法,用于以分布式方式编排副本。Raft 的设计考虑到了可理解性,它只有几个可移动的部分,很容易实现。在本文中,我们将探讨 Raft 的基本组件以及它的领导者选举机制。

基本原则

数据流

在最高级别上,Raft 基于具有半异步复制策略的单领导架构。要了解更多关于单领导复制的信息,您可以查看这篇文章。顾名思义,所有的写/读请求都要经过一个领导者,这个领导者再将请求传播给追随者。它等待多数并向客户机返回 ACK(图 1)

图一。木筏数据流,作者图

附带说明一下,Raft 是可线性化的(强一致性),因为它由同一个 leader 处理所有读/写操作。

故障转移

领导者显然是系统中的单点故障,必须实施节点故障转移以确保可用性。在 Raft 中,这种故障转移被称为领导者选举,我们将在后面详细讨论。总的想法是,当领导者活着且可联系时,它每隔 100 毫秒(可配置)向所有追随者发送心跳消息。当领导者不可达时(死亡或网络中断),一些追随者将向同伴发出消息并呼吁选举。

图二。领导人选举,按作者分列

RPC 定义

根据上面的描述,Raft 服务器之间似乎有许多类型的消息——广播命令、心跳、运行选举、提交日志等。然而,Raft 被精心设计,它只使用了两个RPC:

AppendEntry :这个 RPC 由领导者发起,携带最新接收到的命令。它也用作心跳消息。当一个追随者收到这个消息,选举计时器被重置。 AppendEntry 消息是这样的结构(我在这里使用 Golang)

RequestVote :当一个跟随者的选举定时器到时(长时间没有收到 AppendEntry,~300ms),它成为候选人,并调用其他所有节点为自己投票。其他人可以同意或拒绝投票。一旦大多数人同意,候选人就成为领导者。其他候选人回归追随者。

如果你现在看不到每个字段的用途,那也没关系,但是请记住,对于一个正在工作的 Raft 服务器来说,这是你所需要的。这篇文章聚焦于领导人选举,这意味着我们将更详细地研究的请求。

日志

Raft 将来自客户端的命令存储在一个只附加的日志中(保存在磁盘上)。每当接收到一个新命令时(例如,set key = val),就会将它和当前的术语编号一起添加到日志中(稍后解释)。

图五。木筏日志,作者图

领导者日志是集群中的唯一权威。如果一个跟随者的日志和领导者的日志有冲突,矛盾会被领导者覆盖。

选举详情

在分布式环境中,有许多可能的场景,其中领导者与其他节点失去联系。可能服务器死了;也许它的信息丢失了。与其分析 Raft 在每种情况下是如何工作的,不如让我们把自己放到一个追随者节点的角度。

选举伪代码

作为跟随者,节点保持一个选举计时器。如果在给定的时间内 rf.hearbeatEvent 通道中没有可用的消息,节点转换为候选节点并广播 RequestVote。select case文法是一个 Golang 特性,它阻塞线程,直到通道中有新消息可用。

问题 1 —多重选举

在没有任何选举协调的情况下,没有什么可以阻止 Raft 节点运行多个选举。由于每个节点都为自己投票(通过设计),因此可能没有一个选举能够获得多数票。在没有任何共识的情况下,节点陷入了一次又一次没有领袖的选举循环中。

为了避免并发选举,Raft 尝试了一个简单的解决方案——随机化超时,如图 6 所示。有人可能会说随机化不能解决问题,我同意。这里的关键思想是随机化降低了选举冲突的概率。如果发生分裂投票,我们可以简单地重试,很有可能在下一轮完成。

问题 2 —多个领导

多个领导者甚至可以存在于一个严格多数票的 Raft 集群中吗?答案既是肯定的,也是否定的。如果一个领导者(L1)与所有其他节点断开,追随者将进行新的选举,最终选出一个新的领导者(L2)。对 L1 来说,由于它没有办法探测到 L2 的存在,它仍然充当着领导者的角色。所以,是的,Raft 中可以有多个领导者。

但是,如果您还记得复制策略—当大多数副本回复 AppendEntry 调用时的半异步和 ACK。如果 L1 无法达到多数,它将无法处理任何写请求。所以,从当事人的角度来看,L1 就像死了一样。所以不,只有一个领导人(L2)可以在服务。

问题 3 —老领导重新加入集群

现在,如果网络是固定的,而 L1 突然可以到达所有节点,那会怎么样呢?它对 L2 政府会有什么反应?这就是 AppendEntryRequestVote 中的 Term 字段发挥作用的地方。

每个节点都有一个术语号,并用它标记所有发出的消息。每当接收到消息时,节点将发送方的术语号与其自身的进行比较。如果滞后,接收方自动更新其术语

当进行新的选举时,候选人将任期数增加 1。更新的术语将被传播到投票给 L2 的所有节点,这些节点保证是大多数。现在,如果 L1 重新加入这个组织,它会发现自己的任期更短了——是时候下台了。

图 8。状态转换,从原来的筏纸

问题 4 —落后的候选人

到目前为止,我已经忽略了选举的一个关键部分——日志健全性检查。在 Raft 中,投票是以先到先得的方式进行的。但是,这个简单的逻辑并不能阻止日志过期的节点成为领导者。考虑图 9 中的场景,其中过时的追随者 2 成为第一个要求选举的人。

图九。当前投票逻辑的问题,按作者分类

如果投票是先到先得,那么跟随者 1 将投票给跟随者 2。记得领队日志是 Raft 的唯一权威吗?如果跟随者 2 成为领导者,最后一个条目(z-=1)将被丢弃!

为了解决这个问题,Raft 实施了额外的选举限制,以确保只有更新日志的候选人才能参加选举。

  1. 如果候选人的任期数较低,则拒绝投票(它肯定错过了一些选举)
  2. 如果候选人的日志没有更新,拒绝投票,如下所示。

由于选举限制,图 9 中的跟随者 2 将不会获得跟随者 1 的投票,因为其日志虽然具有相同的 LastTerm,但比跟随者 1 的日志短。第一次选举后发生的情况是没有领导者被选举,跟随者 1 将最终超时并赢得选举(跟随者 1 将投票给它,因为所有标准都满足)。

摘要

在本文中,我们从一个普通的选举逻辑(先来先服务)开始,并通过考虑各种极端情况,将其发展为成熟的版本。领导者选举中最重要的 RPC 是 RequestVote RPC,它使用一个具有四个字段的结构:

  • Term :对陈旧领导检测非常重要。没有它,老领导就无法优雅地下台。
  • CandidateId :显示候选人的身份。
  • LastLogIndex :候选人日志的长度,用于实施选举限制。如果候选人的日志很短,就不会赢得选举。
  • LastLogTerm :最后一个日志条目的期限,用于选举限制。如果任期低,就不会赢得选举。

的下一篇文章中,我们将探讨 Raft 如何在领导层变动时保持复制日志的完整和同步。

暴力反抗机器:贝叶斯定理和抗议的本质

原文:https://towardsdatascience.com/rage-against-the-machine-bayes-theorem-and-the-nature-of-protest-4d7e12e297ee?source=collection_archive---------36-----------------------

科林·劳埃德在 Unsplash 上的照片

剥夺和平抗议权利的法律正在席卷美国各地的立法机构。传统上,宪法学者将这类政策视为言论自由和公共安全之间的平衡测试。认为前者在此时此刻威胁着后者,是否有实证依据?这就是这篇文章的内容。

今年春天,在通过了全国最严格的法案之一后,佛罗里达州州长自豪地庆祝了“全国最强的反暴乱、支持执法的措施”。

“如果你闹事,如果你抢劫,如果你伤害他人,特别是如果你在这些暴力集会中伤害了执法人员,你就会进监狱。”州长罗恩·德桑蒂斯

我不认为这有争议。然而,隐含的前提——暴力抗议如此普遍,以至于有必要进行有力的谴责——似乎并没有在数据中得到反映。

艾丽卡·切诺维特博士是美国非暴力抗议运动的主要学者之一。她的研究团队维护着一个关于美国抗议活动的公开数据集,我将在这里使用它。他们承认数据是不完美和不完整的——但我认为它对我们的分析很有用。我们将使用贝叶斯定理来形成我们自己的比例感。

但是,为什么是贝叶斯定理呢?我相信贝叶斯为这个问题提供了一个很好的工具,因为它以一种频率主义模型所不具备的方式保留了不确定性。与只能产生狭窄范围的输出的机械过程不同,贝叶斯提供了灵活性,以适应与我们自然处理问题的方式不相似的“完整画面”。

如果我明天去抗议,我不知道会发生什么。我接受这种不确定性——但这并不能阻止我下意识地思考可能会出什么问题。脾气可能会爆发。我旁边的人可以揍警察。我身后的那个可以砸碎办公楼的窗户。我们可能会遭到枪击——或者我们都可以和平地、平静地为我们关心的事业发声。都是似是而非的。

对于那些参加过大型示威游行的人来说,我们直觉地认为,虽然有些人可能会失控,但大多数人会坚持社会认可的行为,并有一定程度的克制。贝叶斯的有用之处在于,它被设计成将假设整合到分析中;从一开始的直觉,我们收集新的信息,将它与我们所知道的相结合,并集中在一个更接近现实的概率值上。我们可能会从完全不同的假设开始,但最终会非常接近。

这就是直觉。现在让我们用 Python 实现来更精确地定义它。

import pandas as pddf = pd.read_csv(
    '[https://raw.githubusercontent.com/nonviolent-action-lab/crowd-counting-consortium/master/ccc_compiled.csv'](https://raw.githubusercontent.com/nonviolent-action-lab/crowd-counting-consortium/master/ccc_compiled.csv'), 
    encoding='latin'
)df.columns
df.head()

columns 属性和 head 方法让我们感受到了什么是可用的。使用 shape 方法,我们还可以看到有近 72,000 个观察值(如本文所述)。在检查这些列时,请注意我们可以计算各种不同场景的概率。为了简单起见,我选一个。

我的猜测是,在典型的抗议活动中,最有可能发生的是财产损失。这是“表演”的最低成本形式。另一方面,考虑到后果的严重性,警察成为暴力目标的示威可能相当罕见。因此,我将计算我所猜测的介于这两个极端之间的概率:抗议者暴力伤害其他抗议者。以符号形式:

P(伤害|抗议)= P(抗议|伤害)* P(伤害)/ P(抗议)

在我的 import 语句之后,我声明了贝叶斯定理构建块的概率值。

# Unconditional probability of someone being injured in the crowd
injury = df.injuries_crowd_any.mean()# Unconditional probability of protest (as opposed to march, walkout, boycott, etc.)
protest = df.type.value_counts(normalize=True)[0]# Probability of protest given that there are injuries
injured = df[df.injuries_crowd_any == 1]
protest_given_injury = injured.type.value_counts(normalize=True)[0]
protest_given_injury# Calculate posterior probability
injury_at_protest = (protest_given_injury * injury) / protest

我的结果是 0.00975。这个值有一个非常具体的含义:在所有不同的公共表达方法的范围内——以及相关的结果——导致受伤人员出席的抗议形式的可能性略低于百分之一。就比例而言,将这一问题与其他政策问题进行比较会很有趣——比如家庭暴力或枪支暴力。

我将让读者来判断这种可能性是否证明我们所看到的政策反应是正确的。然而,将辩论建立在经验主义的基础上有望帮助人们形成有数据支持的自己的观点。

感谢阅读!

威尔·雷耶斯在 Unsplash 拍摄的照片

给你泼冷水:汤普森取样对 A/B 测试不好

原文:https://towardsdatascience.com/raining-on-your-parade-thompson-sampling-is-not-good-for-a-b-testing-e5a7cbed80fb?source=collection_archive---------38-----------------------

我的揭开纯粹探索系列的第 0 部分(a)

国家癌症研究所Unsplash 上拍摄的照片

如果批量统计不起作用,就让汤普森来解决你的问题🤡

大多数文章讨论在介质上进行 A/B 测试

  • 以批量统计开始和结束,或
  • 用一个停止规则来补充批量统计,一些软件使用一个完全不透明的过程神奇地产生这个规则,这个过程很容易被忽略,或者
  • 至少从经验上认识到顺序统计的优越性,但是简单地从上面的步骤 2 中剥离出“批处理”,坚持香草的“汤普森抽样”,并雄辩地讲述这将如何解决你所有的产品测试困境,并使你一夜之间成为亿万富翁。

虽然我已经解释了如何将顺序测试、一个合理的、可证明的 停止规则结合起来,比批量测试表现得好得多,但是最近第三类文章的大量涌现表明了对我打算在本文中消除的一个神话越来越多的信任。

误区:后悔和样本量效率本质上是同一个度量。

💡快速回顾定义

在我的上一篇文章中,我描述了样本量效率的含义,因此,为了保持文章简洁,我将只更详细地讨论遗憾(继续使用与我上一篇文章相同的基本符号)。遗憾在我们的多臂 Bandit (MAB)框架中的一个采样算法,本质上是测量奖励的损失,因为它一直没有拉最好的臂。

如前所述,我假设θ₁>θₖ,∀ k ∈ {2,3,…,K},并将δₖ=θ₁-θₖ定义为臂 k 的奖励缺口。在时间 s ∈ {1,2,…,T},MAB 算法π对臂 Iₛ进行采样,并接收奖励 X(s,Iₛ).它的遗憾被定义为

T 轮π算法的后悔定义(图片由作者提供)

⚡神话从何而来?(有点夸张)

我上面提到的许多困惑,在我看来,都可以归因于后悔的一个等价定义。让随机变量 Nₖ(T)表示在 t 轮中算法π采样臂 k 的次数。然后,

回报差距方面的遗憾(图片由作者提供)

上述等式表明,良好的后悔性能自动意味着采样次优 arm 的频率较低。作者(在我看来)看到上面的等式,并假设任何最小化遗憾的算法必然也能快速找到最佳臂。这不是完整的画面!

你看,这样的作者往往忘记了与𝔼Nₖ(T 有关的另一个术语,即δₖ.的差距因为在后悔最小化的过程中,失去奖励的代价是如此之高,所以为这种环境设计的算法往往会非常小心,在探索和利用之间徘徊。另一方面,快速找到最佳手臂需要算法非常积极的行为。由于回报不再是一个因素,困扰后悔最小化的探索-开发困境在后一种情况下就不那么突出了。

Vanilla Thompson Sampling(vTS)已经被开发用于最小化遗憾的明确目的,并且展示了当涉及到手臂选择时其同类的所有恐惧。这就是为什么上述第二类和第三类文章的说法非常具有误导性。

小遗憾糟糕的最佳行动识别🤯

再读一遍。注意我没说“小遗憾≠好的最佳动作鉴定!”我在这里提出的主张是更强,也就是说,任何在后悔最小化问题上表现良好的算法,必然会在最佳手臂识别上表现不佳。让我们更正式一点。

众所周知,最好的后悔最小化算法在后悔中显示出对数增加,即 O(log(T))。给定来自一些θ类实例的 bandit 实例θ = [θ₁,…,θₖ],后悔性能的下限是

后悔表现的下限(图片由作者提供)

这仅仅意味着对于任何 bandit 实例θ∈θ,对于类θ的遗憾最优算法将显示 O(log(T))的渐近性能。现在的问题是,对于任何这样的算法,总是在同一个类θ中存在至少一个【困难】实例,使得选择错误臂的概率随时间至多以多项式衰减!从形式上讲,对于任何ϵ > 0、

选择错误手臂的概率(图片由作者提供)

这使得π对于这项任务来说相当不可靠。这自然使我们得出结论

(免责声明:这个定理确实有一些限定条件,为了清楚起见,我省略了这些条件。更多详情请参考 Tor 的第 33 章和 Csaba 的代表作。)

这种下降率在应用中是完全不可接受的,因为(a)它导致巨大的样本量和(b)存在几种更好的替代方案!后一种说法是正确的,即使人们不希望求助于设计复杂的停止时间来决定实验何时收集了足够的数据来宣布一个行动是“最好的”

既然我已经(希望)让你相信了行星队长的“减少、再利用、回收”咒语不适用于 A/B 测试,我们可以继续讨论更有效的替代方案。

回收塑料不是算法(来源:维基共享)

前两个汤普森抽样

这是为了让汤普森取样探索更积极。简而言之,这种算法像普通 TS 一样进行,但在每一轮中,随机选择两个顶级动作(或手臂)中的一个,并对其进行采样。非常仔细地选择随机化的概率,并且通过所述选择,

Top-2 TS 迅速停止选择错误的手臂(图片由作者提供)

这里有几点值得注意。

  • 由于可以在一轮中选择两个顶端的武器中的任何一个,探索显然是被鼓励的。
  • 与我们早先关于选择错误臂的概率的界限相比,Top-2 TS 好几个数量级(从多项式减少到指数减少)!
  • 这个常数γ*决定了指数下降的速率,也是可以达到的最佳值。

然而,这个算法没有使用停止时间(在文献中,它属于固定预算类别)。许多其他人也自动提供一个停止时间,在此时间他们停止并宣布一个手臂是最好的。

输入舞台左侧的停止时间

纯粹的探索算法属于“固定置信度”算法的范畴,这些算法在看到足够多的证据后,还会决定什么时候采取“最佳”行动。它们旨在解决以下问题:

给定置信参数δ ∈ (0,1),使用尽可能少的臂拉动,找出概率至少为 1-δ的最佳臂。

因此,在我们的设置中,这些算法需要使用尽可能少的样本来找到 Arm 1。我现在将讨论这一类别中几个突出的例子。

跟踪并停止

加里维耶和考夫曼在他们的里程碑 2016 论文中表明,对于给定的 bandit 实例,任何固定置信度算法的停止时间τ满足来自某个类θ的θ = [θ₁,…,θₖ]

样本量的下限(图片由作者提供)

上述实例相关常数 c(θ)取决于对 K 个臂中的每一个进行采样的频率。假设 w(θ)=[w₁(θ),…,wₖ(θ]是频率。显然,这个量显然只有在θ已知时才能计算出来,这意味着实际上没有算法事先知道它。但是,可以估计随着时间的推移!

这就是“跟踪-停止”策略的作用。他们根据可获得的回报估计θ,在时间 t,他们得到 w(θ)的估计 wᵗ(θ。然后,他们对采样频率与 wᵗ(θ规定的频率相差最远的臂进行采样,并重新估计θ,然后重复该过程。这种情况会持续下去,直到收集到足够多的有利于 arm 的证据,算法停止并宣布该 arm 为最佳。

在高置信度状态下,即当δ接近 0 时,跟踪和停止策略在样本量上是可证明的最优的。再次注意,焦点是完全在探索上,最小化遗憾是而不是的目标!

(我将在本系列的后续文章中更详细地讨论这种方法背后的直觉。)

上下置信限算法

基于置信度的算法维护一个集合,称为“置信度集合”,该集合包含具有高概率的真实 bandit 实例θ。这个集合,对于每个臂 k ∈ {1,…,K},仅仅是当前奖励均值θₖ(t)加上探索奖金 Cₖ(t,δ).换句话说,概率至少为 1-δ,θₖ ≤ θₖ(t)+Cₖ(t,δ).

较低-较高置信度(LUCB)算法,如 Top-2 TS,保持两个臂(1)具有最大估计均值的臂和(2)具有最大估计较高置信度的臂,即,

LUCB 在每一轮中都保持两只手臂(图片由作者提供)

LUCB 算法在对回合中进行,在连续回合中采样这两个臂并更新它们的估计。当 hₜ的置信下限大于 lₜ的置信上限时,它们就停止了(并且很自然地宣布 hₜ是最好的)。

为了正确选择探索奖金 Cₖ(t,δ),这些算法可以被证明是在最佳值的对数因子内。设计这样一个奖金需要了解有限时间版本的LIL(T11)定律,这超出了本文的范围。需要注意的是,与普通 UCB 不同,LUCB 算法并不是简单地在每一轮都采样 lₜ!再一次,一个流行的后悔最小化算法的纯探索版本显示了强有力的修改以支持积极的探索

存在一些其他的设计纯探索算法的方法,但是我相信对于本文的目的来说,这三个说明性的例子已经足够了。在我的“揭开纯粹探索的神秘面纱”系列的未来部分,我们将遇到其他更奇特的生物,并分析它们的样本大小表现。

结束语

在不了解设计算法的基本原理的情况下回收算法通常是一个坏主意,尽管汤普森采样有很多优点,但也不例外。

一般来说,A/B 测试和纯探索形成了一类比后悔最小化更复杂和要求更高的问题(就数学成熟度而言),因为与后者不同,它们涉及可测量的设计(a)抽样规则(B)停止时间和(c)获胜者选择标准。通过这篇文章,我希望我已经说服了读者,这些问题必须作为它们自己的一类来对待。

这篇文章是我的“揭开纯粹探索的神秘面纱”系列中的一个快速弯路,其中的第 0 部分和第 1 部分已经可以在 TDS 上找到。本系列将继续分析如何提高动作消除的样本量性能。

所以下次之前,记住孩子们

后悔最小化算法在最好的 Arm 识别中被证明是坏的

线性模型中的随机效应

原文:https://towardsdatascience.com/random-effects-in-linear-models-15845b2540ac?source=collection_archive---------8-----------------------

线性模型的扩展

R 中关于在线性模型中使用随机效应的端到端分析示例。

蒂姆·莫斯霍尔德在 Unsplash 上的照片

T 简单的线性模型有一个重要的假设,即观测值的独立性。这一假设在大多数精心设计的实验中都成立,但在现实生活的数据集中却很少成立。

将相关数据假设为独立数据的最大风险之一是,由于大量的“独立”观察值,您的线性模型将始终为您提供漂亮的 p 值。

观察值的“相关性”通常来自同一组内数据点的一些共享特征。例如,如果你对家庭收入如何影响孩子的考试成绩感兴趣,你需要考虑来自同一所学校或班级的学生的成绩比来自不同学校或班级的学生的成绩更相似。

在这种情况下,使用随机效应是提高线性模型估计值的有效方法。一般来说,如果您在调查数据集中有一些与您要回答的问题不直接相关的分组结构,最好将它们作为随机效应包含在您的线性模型中。(注意,随机效果不能用于连续变量,最好超过 5 个级别。)

在本文中,我将通过一个小项目来展示如何在 r 的线性建模中使用随机效果。我将避免复杂的数学方程,并使想法和实现代码尽可能简单。希望你能从中学到一些东西!

这个问题

我的项目问题是" 投篮尝试次数如何影响投篮命中率?

NBA 比赛暂停期间,我们总能听到教练对着球员大喊,“继续投篮!

大多数 NBA 职业球员都相信,即使现在很冷,如果一个人坚持投篮,他最终会找到自己的节奏。实际上,潜在的假设是,投篮尝试次数的增加可以积极地影响投篮命中率(并最终增加投篮命中率)。

一些球员甚至抱怨说,他们在投篮命中率方面表现不佳,只是因为他们不允许像超级明星在球场上那样投篮。

这个小型项目的问题是用统计建模来解决上述现象。

数据集

数据集是 2020–2021 NBA 赛季 NBA 球员的场均数据,从网站 basketball reference 下载。

以下是 R 中用于数据清理的代码。

my_tab = read_excel("sportsref_download.xlsx")
dup_players = my_tab[with(my_tab, Tm == "TOT"),]$Player
my_tab_filtered = my_tab[with(my_tab,((Player %**in**% dup_players) & Tm == "TOT")| !(Player %**in**% dup_players)),] %>%
  mutate(Pos = replace(Pos, Pos == "SG-PG", "SG"))%>%
  mutate(Pos = replace(Pos, Pos == "SF-SG", "SF"))%>%
  mutate(Pos = replace(Pos, Pos == "PF-SF", "PF"))%>%
  mutate(Pos = replace(Pos, Pos == "C-PF", "C"))%>%
  mutate(Pos = replace(Pos, Pos == "PG-SG", "PG"))%>%
  mutate(Pos = replace(Pos, Pos == "SG-SF", "SG"))%>%
  mutate(Pos = replace(Pos, Pos == "SF-PF", "SF"))%>%
  mutate(Pos = replace(Pos, Pos == "PF-C", "PF"))
my_tab_filtered = my_tab_filtered[complete.cases(my_tab_filtered),]

首先,我从数据中删除了赛季内球队之间交易的重复球员。然后,我修改了玩家的位置到他们的主要位置,如果他们可以打多个位置的话。最后,我从数据中删除了 NA 值。

为了检查球员名字在数据中是否唯一,我使用了下面的代码。

length(unique(my_tab_filtered$Player)) == nrow(my_tab_filtered)

它给了我“真”,这意味着球员是独一无二的。

因为我试图评论投篮命中率对投篮命中率的影响,我在下面的散点图中绘制了它们之间的关系。

散点图 FG%与 FGA(图片由作者提供)

从图上看,投篮次数和投篮命中率之间似乎存在某种线性关系。接下来,我将使用统计方法来检查观察结果。

建模

如果不考虑数据内部任何可能的分组结构,可以简单的对其应用线性回归模型,就是把所有东西都装进一个线性模型。

null.lm = lm(`FG%` ~ FGA, data = my_tab_filtered)
summary(null.lm)

它给了我们,

## 
## Call:
## lm(formula = `FG%` ~ FGA, data = my_tab_filtered)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.42711 -0.04531 -0.00926  0.03243  0.32324 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 0.4254855  0.0084259  50.497  < 2e-16 ***
## FGA         0.0032440  0.0009531   3.404 0.000741 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.08477 on 358 degrees of freedom
## Multiple R-squared:  0.03134,    Adjusted R-squared:  0.02864 
## F-statistic: 11.58 on 1 and 358 DF,  p-value: 0.0007406

FGA 的 p 值(投篮次数)看起来不错,不是吗?

我也检查了残差,

plot(null.lm, which = 1)

零线性模型中的残差与拟合值(图片由作者提供)

plot(null.lm, which = 2)

零线性模型的 Q-Q 图(图片由作者提供)

这些诊断图确实显示了一些异常模式,但是大部分数据看起来是正常的。关于诊断线性模型的更多信息,请参见这篇文章

如果我停在这里,我可以得出结论,投篮尝试确实对投篮命中率有显著的积极影响。但是,我在线性模型中假设了观测值之间的独立性,这是完全错误的。

我们来看看球员在不同位置的 FG%吧,

boxplot(`FG%` ~ Pos, data = my_tab_filtered, las=2,xlab = "")

五个位置的投篮命中率(图片由作者提供)

我们可以从箱线图中看到,不同的位置确实有不同水平的投篮命中率。例如,中锋比其他位置有更高的投篮命中率,这是有意义的,因为他们离篮筐更近。

如果我们再次用玩家的位置作为颜色绘制散点图,我们也可以看到分组(聚集)的模式,

FG%对 FGA 进行颜色编码(图片由作者提供)

因此,我不能忽略“位置”变量,尽管这不是我对建模的兴趣。

在这种情况下,有必要在线性模型中引入固定效应和随机效应的概念。简单来说,固定效应是我们试图从数据中估计的未知常数,而随机效应是我们试图估计(farage,Julian J .,2016 )分布参数的随机变量。

对于固定效应,我们的目标是在线性模型中估计它们的系数,以便评论与因变量的关系。但是,我们对随机效应变量中每个具体水平的效应不感兴趣,我们只想检验随机效应的变异是否大于零。

因为我感兴趣的是 FGA 对 FG%的总体影响,而不是它在每个单独位置的表现,所以我在线性模型中使用位置作为随机影响,而不是固定影响。固定效果和随机效果之间更多的概念差异可以在这里找到。

要在 R 中执行混合(固定效果+随机效果)线性模型,需要 lme4 包。

然后,我扩展了前面的线性模型,把位置作为随机效应。

**library**(lme4)
mixed.lm = lmer(`FG%` ~ FGA + (1|Pos), data = my_tab_filtered)
summary(mixed.lm)

这给了我,

## Linear mixed model fit by REML ['lmerMod']
## Formula: `FG%` ~ FGA + (1 | Pos)
##    Data: my_tab_filtered
## 
## REML criterion at convergence: -859.3
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -5.2524 -0.5565  0.0096  0.5838  3.5555 
## 
## Random effects:
##  Groups   Name        Variance Std.Dev.
##  Pos      (Intercept) 0.003318 0.05760 
##  Residual             0.004878 0.06984 
## Number of obs: 360, groups:  Pos, 5
## 
## Fixed effects:
##              Estimate Std. Error t value
## (Intercept) 0.4178439  0.0266955   15.65
## FGA         0.0048149  0.0007998    6.02
## 
## Correlation of Fixed Effects:
##     (Intr)
## FGA -0.223

在上面的报告中,随机效应部分告诉我们在分组因素(这里是玩家的位置)和剩余方差中发现了多少方差。

固定效应部分与简单线性模型报告非常相似,它显示了系数的估计值以及系数的标准误差。

如果我们首先看上面报告的随机效应部分,我们可以看到来自“Pos”的方差与来自“Residual”的方差非常相似。计算完比例后,

0.003318/(0.003318 + 0.004878)

我得到 40.4%的方差不能用固定效应来解释。这些结果再次证实了将“位置”作为随机效应包括在内的必要性。

如果我们看看固定效应部分的 FGA 系数的估计值和标准差,我们可以看到标准差比估计值小得多,这表明 FGA 系数不为零。

您可能已经注意到,lme4 包的混合线性模型的报告中没有“p 值”。

“没有提供 p 值”是在线性模型中使用随机效应的一个主要缺点。实际上有很多方法来计算近似的 p 值,但是理论上没有一种方法是那么精确的,因为自由度的计算很复杂。这个特定主题的讨论可能会非常复杂和冗长,因此我们在这里不讨论细节。

计算随机效应的 p 值的一个实用方法是在 bootstrap 中使用似然比检验统计。似然比检验的基本思想是通过包含不同数量的变量来比较两个模型对观察数据的似然性。如果可能性比率明显不同于 1,那么包含额外变量是有用的。

检验似然比是否显著不同于 1 与检验对数似然比之间的差是否不为零是一样的。

测试统计量可以计算如下:

my_stat = 2*(logLik(mixed.lm) - logLik(null.lm,REML = TRUE))

然后,我们创建一个 bootstrap 方法来模拟零假设(简单线性模型)下的数据,然后生成大量的似然比检验统计量。

LRT_stat = numeric(1000)
**for**(i **in** 1:1000){
  y = simulate(null.lm)$sim_1
  null_md = lm(y ~ FGA, data = my_tab_filtered)
  mixed_md = lmer(y ~ FGA + (1|Pos), data = my_tab_filtered)
  LRT_stat[i] = as.numeric(2*(logLik(mixed_md) - logLik(null_md,REML = TRUE)))
}

然后,我们测试有多少模拟案例超出了我们数据中观察到的统计数据,

sum(LRT_stat > my_stat)

在我们的例子中,这个数字接近于零,这表明了随机效应的重要性。

同样的似然比检验可以用来检验线性模型中的固定效应。我不会在本文中重复这个过程,因为它与随机效应的测试非常相似。唯一的区别是,在计算固定效应不同的两个模型之间的似然比统计时,将 REML 设置为 FALSE。代码如下:

my_stat = 2*(logLik(mixed.lm) - logLik(null.lm,REML = FALSE))
LRT_stat = numeric(1000)
**for**(i **in** 1:1000){
  y = simulate(null.lm)$sim_1
  null_md = lmer(y ~ 1 + (1|Pos), data = my_tab_filtered, method = "ML")
  mixed_md = lmer(y ~ FGA + (1|Pos), data = my_tab_filtered, method = "ML")
  LRT_stat[i] = as.numeric(2*(logLik(mixed_md) - logLik(null_md,REML = FALSE)))
}
sum(LRT_stat > my_stat)

总之,我们发现,在考虑了球员位置的随机效应后,投篮尝试显著影响了球员的投篮命中率。

讨论

现实生活中的问题可能比我上面提到的更复杂。例如,如果你的随机效果中有不止一个分组因子,你需要考虑随机效果的类型。随机效应主要有两种类型,交叉效应和嵌套效应。如果受试者在随机效应的一个水平上没有出现在任何其他水平上,那么它就是嵌套效应,否则,它就是交叉效应。

例如,在前面提到的关于家庭收入和学生成绩之间关系的问题中,你把学校和班级都作为随机效应。学校和班级变量是嵌套效果,因为一个班级只能属于一个特定的学校。这种情况下的 sudo 代码可以是:

nested_mod = lmer(grades ~ income + (1|school) + (1|school:class), data = school_grades)

或者,

nested_mod = lmer(grades ~ income + (1|school/class), data = school_grades)

再举一个例子,如果你试图在大范围的山区测量鸟类的体型和体重之间的关系,你可能需要考虑两个随机影响,季节和山区位置。

捕捉鸟类的季节和地点都会影响鸟类的体型和体重之间的关系。因为每座山都有四个季节,这两个随机效应是交叉效应。sudo 代码就像是,

crossed_mod = lmer(grades ~ income + (1|season) + (1|mountain_id), data = birds_data)

线性模型中随机效应的上述用法已经涵盖了大多数可能的情况。希望这些对你有帮助。

如果你对什么时候使用随机效果感到困惑,关于这一点有很多争论和标准。有兴趣可以在这里 阅读更多 *。然而,我的建议是,只要你注意到数据集中任何分组因素的影响,就继续随机影响,这也是 Gelman,Andrew (2005)所推荐的。*

干杯!

照片由 kazuendUnsplash 上拍摄

参考

https://stats.stackexchange.com/questions/4700/what-is-the-difference-between-fixed-effect-random-effect-and-mixed-effect-mode

farage,Julian J .用 R 扩展线性模型:广义线性、混合效应和非参数回归模型。CRC 出版社,2016

安德鲁·盖尔曼。"方差分析——为什么它比以往任何时候都重要."统计年鉴 33.1(2005):1–53。

随机森林的非技术性指南

原文:https://towardsdatascience.com/random-forest-29cf337c68d4?source=collection_archive---------33-----------------------

今天最广泛使用的算法之一实际上很难解释

Geran de Klerk 在 Unsplash 上拍摄的照片

如果你还没有,你应该读一读我写的关于决策树的文章。

理解决策树及其缺陷对于理解随机森林为什么存在以及它为什么强大至关重要。

目录

介绍

根据 KDNuggets 数据科学家最常用的三种算法是线性/逻辑回归、决策树或随机森林以及梯度推进模型。78%的应答者使用决策树或随机森林,很明显这个模型是我们应该理解的。

如果你不能向商业受众解释一个模型,它不太可能得到利益相关者的认可或认同。本文旨在用大家都能理解的方式来解释随机森林。

随机森林既可以用于分类,也可以用于回归,但我在这里将重点讨论分类。

这个模型是一个集合方法,意味着我们一起使用许多模型。主要组合技术为装袋助推。随机森林属于装袋类别。这个后面会解释。

决策树缺陷

决策树的快速回顾。如果你不熟悉这些概念,你可以在这里阅读我的文章。

  • 我们希望通过我们的一个特征来分割我们的数据,这个特征是节点,结果数据是叶子
  • 我们希望最小化每个中的杂质
  • 杂质通过使用叶子中各等级的比例来计算。我们可以使用基尼系数或熵值。
  • 在每个节点选择的特征是基尼系数最小的特征

这种方法有一些缺陷

  • 该算法是贪婪的,这意味着我们基于该节点处的分裂而不是未来节点中的结果分裂来选择最佳特征
  • 当树木长得很深时,它们很容易过度生长。我们可以通过修剪该树来限制这一点,但这会降低预测能力。

答案是引入随机性,使用更多的树,因此得名;随机森林

随机森林

摘要

该算法可以分解为以下步骤,我将逐一解释:

  1. 从现有数据集创建新数据集(引导)。
  2. 在此数据集上拟合决策树。
    2a。在树的每个节点随机采样 X 个特征。
  3. 重复数百次。
  4. 合并每棵树的预测(聚合)。

拔靴带

Bootstrapping 是 20 世纪 80 年代流行的一种统计重采样方法。它用于计算置信区间和标准误差,在随机森林模型中起关键作用。它是这样工作的…

首先,我们取数据集,在这个例子中是 12 种水果。希望这些图片是不言自明的,但是在决策树的例子中,我们有不同大小和颜色的苹果、梨和葡萄。

我们的样本数据集。图片作者。

让我们创建第一个引导示例。这是通过取样和替换来完成的,这意味着每个样本可以被多次提取。我在下面突出了一个例子,黄梨被取样两次。

创建单个引导示例。图片作者。

值得注意的是,尽管我们可以通过这样做来创建一些极端的样本,但是中心极限定理表明我们的样本的平均值将围绕我们的原始数据集的平均值呈正态分布。

让我们为这个例子创建 5 个引导样本(scikit-learn 缺省值是 100)。

图片作者。

希望你能看到这样做会给我们的数据带来更多的随机性。考虑决策树在这些自举样本中的每一个上进行训练时的样子,以及哪些特征会产生最佳的基尼系数,从而导致不同的根节点。

随机化特征

我们还有另一个步骤来引入随机性。这是每次我们在决策树中创建一个节点时,从我们的数据集中随机选择固定数量的特征。

在我们之前的例子中,我们只有 3 个特性;颜色、形状和大小。为了更好地说明这个例子,让我们假设我们也有一个甜度分数,从 1 到 5。

默认情况下,scikit-learn 将最大特征设置为总数的平方根。在这个例子中,我们有 4 个特征,所以我们在每个节点使用的最大值是 2。

在创建决策树的每个节点时,我们随机选择 2 个特征。这在我们的引导样本中创建了非常不同的树。

特征可以在每个阶段重复使用,但是算法不会重新选择二进制特征,除非它在单独的分支中,因为分割已经完成。

拟合决策树

我们现在可以在每个引导数据集上安装一个决策树。

让我们看一个引导数据集 1 的例子。我在这里只是估算了基尼系数的近似值,所以如果你自己计算,你可能会发现一些差异。

我们在样本上拟合一个常规的决策树,但是有一个小的不同。在每个节点,我们只能从 2 个随机选择的特征中挑选。

对于根节点,两个随机选择的特征是大小形状。以标准方式选择根节点。我们计算基尼系数并选择最佳特征。在这种情况下,它的大小。

决策树的根节点。图片作者。

我们实现了左边叶子的纯度,但是右边的叶子仍然需要进一步分类。

从右边的叶子开始,在这个节点随机选择的特征是颜色甜度。像往常一样,我们使用基尼系数来选择最佳特征。颜色 =红色在这里给出了最好的分数,所以这里被用作下一个节点。

决策树的第二个节点。图片作者。

右边的节点还不是纯的,所以我们从另外两个随机选择的特征中创建另一个节点。在这里,我们得到形状尺寸。请注意尺寸可以再次使用,但我们不会选择 3 厘米,因为这个分支中的所有东西都已经超过 3 厘米了。

形状给了我们纯洁,所以这棵树是完整的

达到纯洁的最后一个节点。图片作者。

这是最后一棵树。

请记住,在每个节点,我们只能从 2 个功能中选择。这些是随机选择的,我们无法控制它们是什么。

自举样本 1 上的决策树。图片作者。

重复 100 次

我们在这里只使用了 5 个样本,scikit-learn 的缺省值是 100,但通常我们在建模时会使用几百个样本。增加树的数量将有利于模型,但会受到收益递减的影响-最终更多的树不会产生明显更好的结果,但会增加计算量。

我们现在有数百个不同的样本,每个样本都有不同的特征组合,每个样本都有一个决策树。

这是我们 5 个样本的一些示例树。

每个引导示例的示例树。图片作者。

我们如何用这些树做预测?

做预测

这可以简单地通过聚合投票来完成。每棵树都有同等的投票权。这样,不良预测和过度拟合的树可能会被其他树补偿。

我们简单地将每个类别的投票相加,计算出一个概率分数,该分数可用于分类。

在本例中,树 1、2、3 和 5 正确地将我们的新数据预测为葡萄。树 4 预测苹果。这给出了一个概率为 80%的葡萄结果。

图片作者。

得分

在分类中,我们可以用典型的度量来评估随机森林模型;准确度、精密度、召回率等等。然而,自举的使用也提供了一个独特的指标,称为外袋评分。

当创建自举样本时,通常 1/3 的原始样本将不被包括在内。这些可以被认为是每棵树的看不见的数据。

在下图中,我们可以看到每个自举样本的开箱样本。你可以看到这并不接近每棵树的 1/3,因为我创建样本的方法并不是随机的。

图片作者。

为了创建一个 out-of-bag 分数,我们获取每个样本,并从没有在该样本上训练的每个树中为该样本生成一个预测。

在上面的例子中,树 2 和树 4 没有看到大绿梨,所以我们可以根据这个样本从这些树生成一个预测,因为它们没有被训练过。

我们可以对每个样本都这样做,并计算正确预测的百分比;场外评分。

超参数

在这里,我不打算详细介绍所有参数,我只解释主要概念。如果您不熟悉超参数,可以跳过这一节。

大多数随机森林参数与单个决策树的深度有关。记住,如果树木的深度不受控制,它们会过度生长。然而,装袋应该防止这产生太大的影响。我们仍然可以通过修剪树来提高性能。有许多不同的方法来使用超参数修剪树,要记住的主要事情是一些超参数重叠,所以要确保你没有用一个参数限制你的树,而试图测试另一个。

考虑我们使用的独立决策树的数量总是很重要的。在强大的模型和你正在使用的计算机能力之间找到平衡是很重要的,记住在某个点之后,更多的树不会增加显著的模型改进。

超参数可以使用交叉验证的准确性甚至是开箱得分进行优化。

结束语

玛丽塔·卡维拉什维利在 Unsplash 上拍摄的照片

在本文的开始,我指出了标准决策树模型中的两个关键缺陷:

  • 该算法是贪婪的,这意味着我们基于该节点的分割而不是未来节点的分割来选择最佳特征
  • 当树木长得很深时,它们很容易过度生长。我们可以通过修剪树来限制这一点,但是这会降低预测能力。

Random Forest 通过以下方式解决这些问题:

  • 这些树仍然是贪婪的,但是我们现在正在测试数百种具有不同特征组合的树的排列。我们不需要找到最优的树,因为我们正在使用许多树的力量。
  • 通过聚集来自许多树的结果来限制过度拟合,并且在每个决策边界仅允许随机特征。引导样本也有助于过度拟合。如果需要,我们仍然可以修剪个别的树。

希望你能看到随机森林是如何通过引入 B ootstrapping 和 Agg regation 来改进决策树的。这些的组合被称为装袋

该算法仍然是高度可解释的,我们可以提取特征重要性。然而,我们不能深入算法并查看每一棵单独的树,所以我们不能像使用常规决策树那样绘制树形图和决策边界。

随机森林是当今最广泛使用的基于树的算法之一,但也是许多集成方法之一。

像往常一样,如果有任何问题,请随时直接联系我。

更多数据科学文章:

https://medium.com/analytics-vidhya/advanced-sql-for-data-scientists-8e99a3c7f24c

联系我:

https://www.linkedin.com/in/adamshafi/

Python 中的随机森林算法

原文:https://towardsdatascience.com/random-forest-algorithm-in-python-from-scratch-487b314048a8?source=collection_archive---------22-----------------------

使用(主要)数组和循环在 python 中编写强大的算法

随机森林简化。按作者分类的图表

本文旨在揭开流行的随机森林(random forest)算法的神秘面纱,并通过使用图形、代码片段和代码输出来展示其原理。

我用 python 写的 RF 算法的完整实现可以通过:【https://github.com/Eligijus112/decision-tree-python】T4 访问

我强烈鼓励偶然看到这篇文章的人深入研究代码,因为对代码的理解将使将来阅读关于 RF 的任何文档更加简单明了,压力也更小。

任何关于优化的建议都受到高度鼓励,并欢迎通过 GitHub 上的 pull 请求。

RF 的构建模块是简单的决策树。如果读者熟悉分类决策树的概念,这篇文章会更容易阅读。强烈建议在进一步阅读之前通读以下文章:

sci kit-learnpython 中有一个 RF 算法的实现,速度很快,已经审核了几百次:

https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html

在文档中,需要定义的第一个超参数是 n_estimators 参数,默认值为 100。这个参数的解释非常优雅:

森林中树木的数量。

这个解释非常浪漫,但是一个顽固的数学家第一次阅读时可能会觉得有点困惑:我们在谈论什么样的森林和什么样的树?

随机森林算法背后的直觉可以分成两大部分: 随机 部分和 森林 部分。让我们从后者开始。

现实生活中的森林是由一堆树组成的。一个随机森林分类器是由一堆决策树分类器组成的(这里和整个文本— DT )。组成整个森林的 DT s 的确切数量由前面提到的 n_estimators 变量定义。

一个 RF 算法中的每个 DT 完全相互独立。如果 n_estimators 变量设置为 50,则森林由 50 棵决策树组成,这些决策树彼此完全独立生长,不共享任何信息。

50 分钟。按作者分类的图表

二进制分类决策树可以被视为一个函数,它接受输入 X 并输出 1 或 0:

DT: X → {0,1}

RF的最终预测是从每个单独 DT 做出的预测的多数投票。

如果在 50 棵树中,有 30 棵树将新的观察标记为 1 ,20 棵树将相同的观察标记为 0 随机森林的最终预测将是 1

在关于简单分类决策树的文章中,很明显,对于相同的输入和相同的超参数,决策树将学习相同的输出和相同的规则。那么,为什么要种 50(或 100,或 1000,或 k 呢?这就是第二部分 RF 直觉的用武之地:随机部分。

RF 中的随机部分可以分为两部分。

第一部分是数据的自举。数据自举是其行的替换子样本。python 实现中创建引导示例的部分:

用于引导的代码。按作者

例如,如果整个数据集 d 有 10 行:

数据集示例。作者图片

响应变量是客户流失列,其他三列是特性。

d 的三个独立引导数据集可能看起来像:

3 个数据引导。作者图片

从上图可以看出,第一个样本中,第 5 排和第 8 排是一样的。此外,并不是所有的原始观察都存在。

如果我们定义 RF 具有 k 个决策树( n_estimators = k ),那么将会创建 k 个不同的引导数据集,并且每个树将会随着不同的数据集而增长。每个数据集的行数可以与原始数据集相同,也可以比原始数据集少。

因此,如果 RF 由 50 个决策树组成,那么 RF 的高层图将如下所示:

引导程序。按作者分类的图表

五十棵决策树中的每一棵都将随着唯一的数据集而增长。

随机森林中的随机第二部正在训练阶段。当在数据上拟合分类决策树时,在每个分割选择期间,特征保持不变。

在随机森林分类器创建过程中,每个决策树的增长与这里实现的算法略有不同

https://sci kit-learn . org/stable/modules/generated/sk learn . tree . decision tree classifier . html # sk learn . tree . decision tree classifier

唯一的区别是,在每个分割创建时刻,抽取一个具有特征的随机子样本,并利用这些特征计算最大基尼系数。

https://gist . github . com/eligijus 112/4c 47 be 2 f 7566299 bb 8 C4 f 97 c 107d 82 c6f

例如,假设我们有一个具有 100 个初始特征的自举数据样本 dX 矩阵。我们可以用参数X _ features _ fraction(sci kit-learn 实现中的 max_features )定义在每个分割阶段要留下多少个特征。让我们将其设置为 0.05,这意味着在每次分割时,将选择 5 个随机特征(或 5%)进行分割。

射频树示例。按作者分类的图表

在第一个节点中,有五个 X 特征:1、25、28、30 和 98。当用值 x 分割第 25 个特征时,找到最佳分割。下面两个节点具有另外 5 个随机特征,其中搜索最佳分割。

因此, RF 算法如下:

选择一组超参数。

对于 1 到k树做:

  • 生成一个随机引导数据样本 d
  • 对数据 d 拟合决策树。在生长阶段的每次分割过程中,选择要分割的要素的随机子样本。

预测时,获得每一个 k 树的预测,然后使用多数投票选择标签。

让我们在一些真实数据上安装一个 RF 分类器。完整的笔记本和数据可以通过以下方式访问:

https://github . com/eligijus 112/decision-tree-python/blob/main/randomforestshowcase . ipynb

我的 GitHub 存储库中的数据集有 3333 行,我们将使用以下各列:

数据样本。作者图片

目标是创建一个分类器,预测电信客户是否会流失。 流失 是客户不再使用公司产品的事件。如果 churn = 1,则意味着用户已经切换到不同的服务提供商,或者由于任何其他原因,没有显示任何活动。

让我们将数据拆分为训练和测试拆分。训练数据将有 75%的行(2500),测试集将有 25%的行(833)。

如何使用自定义 RF 类的示例代码。作者代码

生长 5 个决策树,每个分裂中具有 25%的特征,并且每个树的深度为 2,我们得到以下森林:

森林的生长。作者图片

------ 

Tree number: 1 

Root
   | GINI impurity of the node: 0.27
   | Class distribution in the node: {0: 2099, 1: 401}
   | Predicted class: 0
|-------- Split rule: DayCalls <= 113.5
           | GINI impurity of the node: 0.24
           | Class distribution in the node: {0: 1598, 1: 257}
           | Predicted class: 0
|---------------- Split rule: DayMins <= 287.7
                   | GINI impurity of the node: 0.21
                   | Class distribution in the node: {0: 1591, 1: 218}
                   | Predicted class: 0
|---------------- Split rule: DayMins > 287.7
                   | GINI impurity of the node: 0.26
                   | Class distribution in the node: {1: 39, 0: 7}
                   | Predicted class: 1
|-------- Split rule: DayCalls > 113.5
           | GINI impurity of the node: 0.35
           | Class distribution in the node: {0: 501, 1: 144}
           | Predicted class: 0
|---------------- Split rule: DayMins <= 225.0
                   | GINI impurity of the node: 0.25
                   | Class distribution in the node: {0: 431, 1: 76}
                   | Predicted class: 0
|---------------- Split rule: DayMins > 225.0
                   | GINI impurity of the node: 0.5
                   | Class distribution in the node: {1: 68, 0: 70}
                   | Predicted class: 0
------ 

------ 

Tree number: 2 

Root
   | GINI impurity of the node: 0.26
   | Class distribution in the node: {0: 2124, 1: 376}
   | Predicted class: 0
|-------- Split rule: OverageFee <= 13.235
           | GINI impurity of the node: 0.24
           | Class distribution in the node: {0: 1921, 1: 307}
           | Predicted class: 0
|---------------- Split rule: DayMins <= 261.45
                   | GINI impurity of the node: 0.18
                   | Class distribution in the node: {0: 1853, 1: 210}
                   | Predicted class: 0
|---------------- Split rule: DayMins > 261.45
                   | GINI impurity of the node: 0.48
                   | Class distribution in the node: {0: 68, 1: 97}
                   | Predicted class: 1
|-------- Split rule: OverageFee > 13.235
           | GINI impurity of the node: 0.38
           | Class distribution in the node: {1: 69, 0: 203}
           | Predicted class: 0
|---------------- Split rule: DayMins <= 220.35
                   | GINI impurity of the node: 0.13
                   | Class distribution in the node: {0: 186, 1: 14}
                   | Predicted class: 0
|---------------- Split rule: DayMins > 220.35
                   | GINI impurity of the node: 0.36
                   | Class distribution in the node: {1: 55, 0: 17}
                   | Predicted class: 1
------ 

------ 

Tree number: 3 

Root
   | GINI impurity of the node: 0.25
   | Class distribution in the node: {1: 366, 0: 2134}
   | Predicted class: 0
|-------- Split rule: DataUsage <= 0.315
           | GINI impurity of the node: 0.29
           | Class distribution in the node: {1: 286, 0: 1364}
           | Predicted class: 0
|---------------- Split rule: MonthlyCharge <= 62.05
                   | GINI impurity of the node: 0.18
                   | Class distribution in the node: {1: 144, 0: 1340}
                   | Predicted class: 0
|---------------- Split rule: MonthlyCharge > 62.05
                   | GINI impurity of the node: 0.25
                   | Class distribution in the node: {1: 142, 0: 24}
                   | Predicted class: 1
|-------- Split rule: DataUsage > 0.315
           | GINI impurity of the node: 0.17
           | Class distribution in the node: {0: 770, 1: 80}
           | Predicted class: 0
|---------------- Split rule: RoamMins <= 13.45
                   | GINI impurity of the node: 0.12
                   | Class distribution in the node: {0: 701, 1: 49}
                   | Predicted class: 0
|---------------- Split rule: RoamMins > 13.45
                   | GINI impurity of the node: 0.43
                   | Class distribution in the node: {0: 69, 1: 31}
                   | Predicted class: 0
------ 

------ 

Tree number: 4 

Root
   | GINI impurity of the node: 0.26
   | Class distribution in the node: {0: 2119, 1: 381}
   | Predicted class: 0
|-------- Split rule: DayCalls <= 49.5
           | GINI impurity of the node: 0.49
           | Class distribution in the node: {1: 8, 0: 6}
           | Predicted class: 1
|---------------- Split rule: MonthlyCharge <= 31.5
                   | GINI impurity of the node: 0.0
                   | Class distribution in the node: {0: 4}
                   | Predicted class: 0
|---------------- Split rule: MonthlyCharge > 31.5
                   | GINI impurity of the node: 0.32
                   | Class distribution in the node: {1: 8, 0: 2}
                   | Predicted class: 1
|-------- Split rule: DayCalls > 49.5
           | GINI impurity of the node: 0.26
           | Class distribution in the node: {0: 2113, 1: 373}
           | Predicted class: 0
|---------------- Split rule: DayMins <= 264.6
                   | GINI impurity of the node: 0.21
                   | Class distribution in the node: {0: 2053, 1: 279}
                   | Predicted class: 0
|---------------- Split rule: DayMins > 264.6
                   | GINI impurity of the node: 0.48
                   | Class distribution in the node: {1: 94, 0: 60}
                   | Predicted class: 1
------ 

------ 

Tree number: 5 

Root
   | GINI impurity of the node: 0.24
   | Class distribution in the node: {0: 2155, 1: 345}
   | Predicted class: 0
|-------- Split rule: OverageFee <= 7.945
           | GINI impurity of the node: 0.15
           | Class distribution in the node: {0: 475, 1: 43}
           | Predicted class: 0
|---------------- Split rule: AccountWeeks <= 7.0
                   | GINI impurity of the node: 0.28
                   | Class distribution in the node: {1: 5, 0: 1}
                   | Predicted class: 1
|---------------- Split rule: AccountWeeks > 7.0
                   | GINI impurity of the node: 0.14
                   | Class distribution in the node: {0: 474, 1: 38}
                   | Predicted class: 0
|-------- Split rule: OverageFee > 7.945
           | GINI impurity of the node: 0.26
           | Class distribution in the node: {0: 1680, 1: 302}
           | Predicted class: 0
|---------------- Split rule: DayMins <= 259.9
                   | GINI impurity of the node: 0.2
                   | Class distribution in the node: {0: 1614, 1: 203}
                   | Predicted class: 0
|---------------- Split rule: DayMins > 259.9
                   | GINI impurity of the node: 0.48
                   | Class distribution in the node: {0: 66, 1: 99}
                   | Predicted class: 1
------

每棵树都有点不同。具有所选超参数的测试集上的精度和召回分数是:

准确性结果。作者图片

让我们尝试通过种植更复杂的随机森林来提高准确性指标。

当生长一个有 30 棵树的随机森林,每个分割点有 50%的特征,max_depth 为 4 时,测试集中的精度为:

更复杂 RF 下的精度。作者图片

如果我们种植 100 棵树,具有 75%的特征,并且树的最大深度是 5,则结果是:

更复杂的射频结果。作者图片

scikit learn 实现给出了非常相似的结果:

# Skicit learn implementation
from sklearn.ensemble import RandomForestClassifier# Initiating
rf_scikit = RandomForestClassifier(n_estimators=100, max_features=0.75, max_depth=5, min_samples_split=5)# Fitting 
start = time.time()
rf_scikit.fit(X=train[features], y=train[‘Churn’])
print(f”Time took for scikit learn: {round(time.time() — start, 2)} seconds”)# Forecasting 
yhatsc = rf_scikit.predict(test[features])
test[‘yhatsc’] = yhatscprint(f”Total churns in test set: {test[‘Churn’].sum()}”)
print(f”Total predicted churns in test set: {test[‘yhat’].sum()}”)print(f”Precision: {round(precision_score(test[‘Churn’], test[‘yhatsc’]), 2) * 100} %”)
print(f”Recall: {round(recall_score(test[‘Churn’], test[‘yhatsc’]), 2) * 100} %”)

Scikit 学习结果。作者照片

请注意,sklearn 和定制代码结果之间的差异可能会有所不同,因为每棵树的自举数据的随机性质以及用于找到最佳分割的随机特征。即使使用相同的超参数运行相同的实现,我们每次都会得到稍微不同的结果。

总之,随机森林算法是由独立的简单决策树组成的。

每个决策树都是使用自定义引导数据集创建的。在每次分裂和每个决策树中,当搜索增加 GINI 增益的最佳特征和特征值时,考虑特征的随机子样本。

一个 RF 分类器的最终预测是所有独立决策树的多数投票。

随机森林分类器项目——预测在线新闻流行度

原文:https://towardsdatascience.com/random-forest-classifier-project-predicting-online-news-popularity-e99c79daa33?source=collection_archive---------22-----------------------

使用 scikit-learn 构建一个随机森林分类器并预测在线新闻文章的份额数量

假想场景:我是一名数据科学家,在一家网络新闻公司工作。营销经理想花一笔预算来推广具有高潜力的商品。因此,她要求我建立一个预测模型来预测一篇文章是否会获得大量的份额。

谢尔盖·阿库利奇在 Unsplash 上的照片

数据集概述:

该数据集中的文章由 Mashable(【www.mashable.com】)发布,其内容的版权归其所有。因此,该数据集不共享原始内容,而是共享一些与之相关的统计数据。使用数据集中提供的 URL 公开访问和检索原始内容。(随意从 Kaggle 下载数据集)

它包含大约 40k 行61 列(58 个预测属性,2 个非预测属性,1 个目标字段)。

属性信息。作者照片

我们开始吧!(参见此处的代码)

第一步:数据评估

在评估数据之后,我发现这个数据集非常干净、整洁,并且没有缺失值。然而,它唯一的问题是,除了“url”之外,每个列的名称开头都有空格。(请参见下面的列名。)

作者照片

第二步:数据清理

因此,我使用下面的代码来解决上述问题。

lambda 函数用于遍历每一列的名称,如果其中有一个"",则使用" "替换它。

第三步:探索性数据分析

在 EDA 阶段,我发现大部分份额都在 15000 以下,所以我把 15000 以上的文章筛选出来看大部分。我计划绘制经验累积分布函数(ECDF)和股票数量的分布,以了解如何将股票数量分为不同的组,以便我可以建立一个分类模型来预测它属于哪个组。

作者照片

作者照片

在看了 ECDF 和分布之后,似乎大多数文章都在 500 到 3000 股之间。所以,我决定把股数分成 3 个等级。(见此处代码)

  1. 极差:股数低于 500 股。
  2. 多数:股数在 500-3000 股之间。
  3. 极好:股数高于 3000。

作者照片

这显示了数据集中每个级别的百分比。作者照片

其次,我发现原始数据集包含两个特性,weekday 和 data_channel,这两个特性已经被一次性编码。

data_channel 已经是一键编码。作者照片。

工作日已被一次性编码。作者照片。

因为具有高基数的独热编码分类变量会导致基于树的集成的低效率。算法将给予连续变量比虚拟变量更大的重要性,这将模糊特征重要性的顺序,导致较差的性能(Ravi。"一键编码使基于树的集成变得更糟,这是为什么?》。

然而,在这个项目的最后一步,优化精度,我将测试是否不使用一热编码真的执行得更好。因此,在这里,为了准备测试,我将所有一次性编码的变量、与工作日相关的变量和与 data_channel 相关的变量恢复为单列。

weekday 和 data_channel 变量的转换版本如下所示:

作者照片

第四步:随机森林模型构建

我们到了最重要的部分,建立模型。在模型构建过程中,我们需要遵循一些小步骤来正确地执行它。

1.将分类列转换为数字列。

2.将数据集分为训练集(70%)和测试集(30%)

3.构建并测试模型

漂亮!其实比我预想的要高!

4.尝试不同数量的 n 估计量,看看我们应该使用多少棵树

作者照片

该图显示,在使用超过 60 棵树(n_estimatpr = 60)后,我们将获得大约 76%的准确率。

第五步:优化准确率

为了优化准确率,我有三个方向可以探索:

  1. 搞清楚用一键编码和不用哪个准确率更高?
  2. 找出哪个变量对因变量不太重要?放下它们,看看准确率有没有提高。
  3. 随机森林分类器、线性支持向量分类器和 RBF 支持向量分类器哪个模型效果更好?

作者照片

对于第一种情况,在计算了每种情况下不同 n _ estimaters 的精确度后,我们可以看到,没有使用独热编码的情况比另一种情况高出近 1%。因此,我决定继续不使用一键编码。

该图显示了 10 个最不重要的变量,并且“n_non_stop_words”具有最低的特征重要性分数。作者照片

对于方向 2,在得到特征重要性得分后,只有一个变量得分明显偏低,“n_non_stop_words”。而下图是去掉“n_non_stop_words”之后和去掉之前的对比。

作者照片

奇怪的是,去掉“n_non_stop_words”后,并没有增加准确率。反而下降了近 1.5%。因此,我决定不移除它。

对于最后一个,随机森林分类器,线性支持向量分类器,还是 RBF 支持向量分类器,哪个模型更好?

作者照片

经过 5 分钟计算其中三个的准确率,随机森林分类器以 76%的准确率胜出!

谢谢你看完!如果你对这个项目的完整代码感兴趣,请查看 my Github 。此外,我喜欢反馈,如果有任何不清楚或应该做得更好的地方,请联系我。这是我的 LinkedIn 或者通过我的电子邮件联系我(【johneeeeeeeeee3@gmail.com】T4

引用作品:

拉维拉克什。"一键编码让你的基于树的整体变得更糟,这是为什么?走向数据科学。2019 年 1 月 11 日。https://towards data science . com/one-hot-encoding-is-making-your-tree-based-ensembles-worse-heres-why-d64b 282 b 5769

兰登森林解释道

原文:https://towardsdatascience.com/random-forest-explained-6b4849d56a2f?source=collection_archive---------20-----------------------

决策树和随机森林的理解与实现

图片来自水花

决策树和随机森林是业界常用的健壮算法,因为它们易于解释和执行。该算法最强的属性之一是,它允许用户根据树的深度来查看哪些特征对预测贡献最大及其重要性。

本文将提供对决策树和随机森林算法的概念性理解。尽管这种算法对于基于分类和回归的问题都足够健壮,但本文将集中讨论基于分类的例子。对于基于回归的问题,您可以应用下面描述的类似的思维过程,但是这些算法将被其他算法(如逻辑回归)执行,这些算法专门关注那些任务。我还将向读者展示如何使用 iris 数据集上的sklearn库在 Python 中实现随机森林和决策树算法来进行花卉分类。下面我概述了本文将遵循的结构。

目录

  • 决策树
    -实现
    -优点
    -缺点
  • 随机森林
    -实现
    -优点
    -缺点
  • 摘要
  • 资源

决策图表

如果决策树的重点是结果的可解释性,那么它对企业来说是很有吸引力的模型。顾名思义,决策树是一种模型,它通过基于提出一系列问题的决策来分解给定的输入数据。想象一个有向无环图,其中每个节点表示一个决策,边连接一对决策。

例子

我们可以在下图中概括这一点,在下面的例子中,我们给定的问题是确定一个人是否应该购买一台新的笔记本电脑。这是一个分类示例,其中buydon't buy是我们给定的标签。

判定用户是否应该购买新笔记本电脑的决策树分类器示例(图片由作者提供)

基于我们训练树的特征,模型将学习一系列问题来推断给定数据的类别标签。同样的概念也可以用于非分类数据,如整数。该算法将从根节点(最顶端的节点——在我们的例子中为Is the Current Laptop Broken)开始,并在导致最大信息增益的特征上分割数据。有各种不同的方法来分割这棵树,另一种常见的方法是通过最小化基尼系数。我们可以在每个子节点上重复这个分裂过程,直到不能再分裂为止。正如你所想象的,这个过程容易过度拟合,因为它可能导致一个非常深的树有许多节点。解决这个问题的典型方法是修剪树以防止过度拟合。在大多数情况下,修剪过程非常简单,最简单的方法是限制树的最大深度。

履行

有关此决策树和 sklearn 库中正在使用的函数的更多详细信息,请参考 this [3]。

这个脚本将产生两个输出。第一个将是为分类虹膜数据集而形成的决策树的视觉表示,第二个将是混淆矩阵,其将概述分类器的准确性。下面的文章提供了关于如何使用准确度、精确度、召回率和 f1 分数的深入理解[4]。

优点

  • 容易理解
  • 易于实施
  • 针对基于分类的问题的强大性能
  • 非参数

不足之处

  • 数据的高方差、小变化会导致树结构和决策的大变化
  • 倾向于过度拟合
  • 尽管它在分类方面表现很好,但对于基于回归的问题,性能会急剧下降
  • 根据类的数量和数据集的大小,计算时间会急剧增加

随机森林

在过去的十年中,随机森林在机器学习领域获得了广泛的关注。这是因为它在分类、易用性和可扩展性方面的强大性能。随机森林是决策树的集合。集成学习是一种使用多种学习算法来提高预测性能的方法[1]。这种方法可以更好地概括模型,并且不容易过度拟合。

算法

  1. 随机抽取大小为 nbootstrap 样本(从有替换的训练集中随机选择 n 个样本)。
  2. 从引导示例中生成决策树。在每个节点:
  3. 随机选择 d 特性,无需更换。
  4. 根据目标函数,使用提供最佳分割的特征来分割节点,例如,通过最大化信息增益。
  5. 重复步骤 1 至 2 k 次。

这将聚集每个树的预测,以通过多数投票来分配类别标签。

随机森林分类可视化-许多决策树的集合,使用多数投票生成预测(图片由作者提供)

履行

有关随机森林分类器和 sklearn 库中正在使用的函数的更多详细信息,请参考this【2】。

这个脚本将输出一个与模型相关的混淆矩阵。该矩阵将通过与每个类别相关联的 f1 分数、精确度和召回率等指标来概述模型的准确性。

优势

  • 分类任务的强大性能
  • 不太可能过度适应数据(不需要修剪或大量超参数调整)

不足之处

  • 训练速度慢-处理大型数据集时,训练模型的计算复杂度非常高
  • 更难解释

摘要

总之,本文概述了决策树算法可以被视为一种模型,它通过基于提出一系列问题的决策来分解给定的输入数据。然而,随机森林算法使用集成学习来生成模型,该模型使用一组决策树一起通过多数投票来生成预测。一个重要的收获是,这两个模型在处理分类问题时都有很高的性能。

资源

[1]https://en.wikipedia.org/wiki/Ensemble_learning

[2]https://sci kit-learn . org/stable/modules/generated/sk learn . ensemble . randomforestclassifier . html

[3]https://sci kit-learn . org/stable/modules/generated/sk learn . tree . decision tree classifier . html

[4]https://blog . ex silio . com/all/accuracy-precision-recall-f1-score-interpretation-of-performance-measures/

您可能会对我写的其他文章感兴趣:

不平衡数据集的随机森林:法国阿尔卑斯山雪崩的例子

原文:https://towardsdatascience.com/random-forest-for-imbalanced-dataset-example-with-avalanches-in-french-alps-77ffa582f68b?source=collection_archive---------9-----------------------

如何评估和提高高度不平衡数据的随机森林的性能?让我们看看法国阿尔卑斯山雪崩的使用案例。

徒步旅行者眺望法国阿尔卑斯山勃朗峰,图片来源:卡米拉·哈马尔契科娃

目录

  • 1)不平衡数据集的性能指标简介
  • 法国阿尔卑斯山雪崩的随机森林(RF ):为什么回忆胜于精确?
  • 2.1)欠采样以提高 RF 模型的性能
  • 2.2)提高射频模型性能的特性选择
  • 2.3)平衡射频以提高射频型号的性能
  • 3)结果汇总

在大多数数据科学课程中,他们让你在“太完美”的数据集上工作,其中每个类别或类或多或少地被平等地表示。但是,当你穿过矩阵的另一边时(不是你在数学课上讨厌的那一边,是有 Neo 和很酷的慢速移动子弹的那一边),现实是混乱的,你会发现更多的例子,其中一个类比另一个类出现得更频繁。

多数类:具有太多观察值的类

少数类:仅代表整个数据集的一小部分的类

1.1 牛逼的准确度是误导

经典的例子是大多数疾病的临床研究。你会发现只有极少数人患有某种疾病(除非你关注发达国家的 Covid 或肥胖症)。因此,您正在处理不平衡的数据集,默认情况下,您将获得令人印象深刻的准确性

当只有 4.7 %的人口被诊断为白血病时,你对某人没有白血病的预测会有很高的准确率,你甚至不需要用机器学习算法弄脏你的手或键盘。你可以简单地运用常识。

1.2 F1 的分数统治了他们所有人

因此,当数据集不平衡时,您应该更多地关注其他指标,例如 F1 得分 。F1 的分数与刘易斯·汉密尔顿或迈克尔·舒马赫无关,它是精确度和召回率的加权平均值

雪崩预测经典二元分类问题 有很多 0 =否定 =多数类“无雪崩”的例子****少数 1 =肯定** =少数类“雪崩”的例子。**

回忆告诉您模型在一组真 1 和假 0(假阴性)中预测 1 的准确程度。精度告诉我们模型在真 1 和假 1(假阳性)组中预测 1 的准确程度。所有指标(F1 分数、精确度和召回率)从 0 到 1。当 0 表示完全错误的结果时,1 表示完美的预测。

通常,我们不得不满足于在精确度和召回率之间进行权衡。这取决于我们的用例,我们更关心最小化假阴性(优先考虑召回)还是假阳性(优先考虑精确度)?****

根据提到的指标,我将主要关注加权 F1 分数和雪崩日的回忆。

2 法国阿尔卑斯山雪崩的随机森林

我将使用超过 540,000 个条目的数据集,经过数据争论后,这些数据汇集了法国阿尔卑斯山 22 个不同地块从 2010 年 10 月到 2019 年 9 月每天不同的降雪和气象变量。这些数据是我的自变量(机器学习中的特征)。某一天某一地块是否发生雪崩是因变量(标签)。这是一个非常不平衡的数据集,因为雪崩只出现在 0.4%的案例中。

特征来源:法国山区 S2M 气象和积雪再分析(1958 年至今)选定年份法国阿尔卑斯山数据

标签来源:Data-Avalanche.org 发生雪崩

法国阿尔卑斯山白湖的日出,图片来源:Kamila Hamalcikova

回忆和精确:哪个更重要?

在 54 万个条目的不变样本上运行 Random Forest (RF)时,加权 F1 得分为 0.99793,确实不错。但是,仍然有精度和召回来更详细地研究。对我来说,我更看重回忆而不是精确。此外,我更看重少数类(有雪崩的日子)的回忆,而不是多数类(没有雪崩的日子)的回忆。为什么?

****误报是我预测雪崩的日子,但实际上并没有真正的危险。这一糟糕预测的代价可能是无聊的徒步旅行者或被困在家中观看 Youtube 上的猫视频的滑雪者,因为他们没有更好的事情可做。

假阴性是我预测没有雪崩的日子,但是对那些相信我的假预测的可怜的徒步旅行者和滑雪者来说,真的有雪团落下的危险。因此,这个糟糕预测的代价甚至可能是人命。这就是为什么回忆战胜了精确,我确实更关心雪崩时的回忆,而不是没有雪崩时的回忆。

用随机森林模型预测法国阿尔卑斯山雪崩的所有代码可以在我的 GitHub repo 这里找到。

在我的第一个 F1 得分为 0.99793 的 RF 模型中,我在没有雪崩的日子里得到的回忆值为 1,而在雪崩的日子里只有 0.546。这意味着我能准确预测没有雪崩的日子,但我只能准确预测每隔一天有雪崩的日子。山区救援人员可能不太喜欢这种模式。

2。1 欠采样技术获得更好的结果

在处理不平衡数据集时,可以尝试对数据进行重采样。数据科学家最喜欢的技巧是尝试过采样或欠采样方法。

****过采样:少数阶级的过度代表

****欠采样:欠多数类表示

幸运的是,我有相当大的数据集,所以我可以很容易地从样本中删除一些没有雪崩的日子。这是欠采样。但是如果你有一个小数据集,欠采样不是你的解决方案,因为很多重要的信息会在这个过程中丢失。传统上,过采样使用得更多。这发生在大数据时代之前,当时获得包含数千或数百万次观察的庞大数据集的成本非常高。

因此,我使用了欠采样来创建 50 %有雪崩情况和 50 %没有雪崩情况的样本。这导致数据集只有 4078 个观察值,而不是之前正常样本的 540 000 个观察值。加权 F1 分数下降到 0.9019,对没有雪崩的日子的回忆下降到 0.87,但对有雪崩的日子的回忆上升到 0.94。即使最后一个数字是我们最关心的数字,但我的模型离现实世界很远。

我使用了魔法或者更准确地说是数学技巧,将召回率从不令人满意的 0.548 提高到惊人的 0.940 。就数学而言,这完全没问题,但如果考虑到统计学或数据科学,就没那么好了。在法国阿尔卑斯山的真实山脉上,雪崩并不是每两天出现一次,即使我们认为其中一些没有被注意到,并从任何数据库中消失。

很难说,有多少次雪崩没有记录。冬季格勒诺布尔附近的森林,图片来源:卡米拉·哈马尔奇科娃

但是要注意,称欠采样方法完全是浪费时间,因为这不是终点。下一步是现在在正常样本上使用这个机器学习模型(在欠样本上训练)并比较结果。

第一个在正常样本上训练的模型在雪崩的日子里给了我们 0.546 的回忆。该模型(在欠样本下训练,但用于正常样本)略有改进;回忆 0.548 有雪崩的日子。是的,更好,但不是很好。我们还可以尝试什么来提高召回率?

2.2 特性选择和工程

另一种提高随机森林性能的方法是对独立变量做一些小的改动,从已经存在的变量中创建新的变量(特征工程)或者去掉不重要的变量(特征选择)。

基于探索性数据分析,我注意到雪崩在某些月份和某些高度比在其他月份和高度更频繁地出现。甚至在应用任何机器学习模型之前,小于 1500 米的海拔就已经从数据集中移除了。

2010 年至 2019 年法国阿尔卑斯山最常发生雪崩的高度,数据来源:Data-Avalanche.org,图表:Kamila Hamalcikova

现在,我还决定删除“较高的异常值”,海拔超过 3600 米(有限海拔)的条目删除夏季月份(无夏季)的条目,而不是将法国阿尔卑斯山的特征分成 22 个不同的山体,我试图忽略山体,只使用整个法国阿尔卑斯山的数据(无山体)

我分别运行了所有三个选项(有限海拔、无夏季、无山丘)的 RF 模型,后来还结合了这三个选项中的最佳选项。我在有雪崩的日子里获得的最好回忆是没有夏天的变体的 0.575,而没有山丘的变体实际上将回忆降低到 0.528。有限的海拔和没有夏天的结合导致了比单独选择更少的回忆。没有地块和没有夏天的组合以 0.559 的召回率结束。因此最受欢迎的模型是没有夏季月份的随机森林模型

山区救援人员可能也不会批准这个模型(我可以预测 10 次雪崩中的 6 次),但这是我在有限的时间内从我的数据集得到的最好的结果。特征的数据源(上面的链接)实际上更大,提供了更多的变量用于机器学习模型,但由于我的计算机的计算能力有限,我只使用了其中的一部分。

我没有谈论加权 F1 分数,因为所有具有功能选择的模型都设法获得了从 0.997 到 0.998 的相对较高的指标,因此仅仅基于 F1 分数而偏爱一个模型是没有意义的。

2.3 随机森林模型的修改

当然,你总是可以走出随机森林,尝试不同的机器学习模型。但是 RF 还有一招对付不平衡数据的锦囊妙计, 【平衡随机森林(BFR) 。文档上说这个模型随机地对每个助推器样本进行欠采样以平衡它。如果这个解释仍然有点模糊,我们可以说:

平衡随机森林是 RF 的修改,其中为每棵树构造两个大小相同的自举集,大小等于少数类的大小:一个用于少数类,另一个用于多数类。这两个集合共同构成了训练集。

对于这个模型,你需要先安装不平衡学习库,然后你就可以开始了。运行之后,我将雪崩日的召回率提高到了 0.91,但是精确度却急剧下降到了 0.03** 。此外,在没有特征选择的样本上,BFR 的加权 F1 分数下降到 0.939。大多数山地救援队可能会批准这种模式,因为它最大限度地减少了假阴性。**

另一方面,这个模型给了我们非常高的误报率。超过 19,000 个错误预测的完全没有威胁的雪崩日。所有其他模型的假阳性数量明显较低,只有 0 到 3 个假阳性。因此,我有一种感觉,当滑雪运动的所有者可以加入到与山地救援队的讨论中时,他们会强烈地推动没有夏天的 RF 模型,雪崩日的召回率为 0.58,F1 得分较高。

法国阿尔卑斯山中部格勒诺布尔和 Chamechaude 山附近的“随机”森林,来源:Kamila Hamalcikova

3 雪崩预测结果总结

回忆有雪崩的日子(为了减少假阴性的数量)是我评估雪崩预测性能的主要指标,加权 F1 分数紧随其后。我使用了 3 种不同的技术来解决高度不平衡的数据问题:

  • 1) 根据欠采样训练的 RF 模型,用于正常样本:结果是雪崩日召回率从 0.546(根据正常样本训练的模型)略微提高到 0.548。两种 RF 模型加权 F1 值均为 0.998。
  • 2) 特征选择:我对特征使用了 3 种不同的变化:A)没有夏季月份的样本 B)海拔有限(不超过 3600 米)的样本 C)没有地块划分的样本。我还检查了 A+B 和 A+C 的组合。所有五个模型都产生了类似的 F1 分数 0.997-0.998,但召回率最高的是选项 A。没有夏季月份的样本在雪崩日的召回率为 0.58,因此是最受欢迎的选项。
  • 3) 平衡 RF 模型:这个机器学习模型确实为雪崩日提供了 0.91 的召回率,但它的 F1 分数下降到了 0.939,雪崩日的精度是悲惨的 0.03(之前提到的所有模型的精度都是 1 或至少接近 1)。此外,该模型在某种意义上使用与欠采样类似的方法,仅在每个引导上使用,因此其在真实世界中的性能值得怀疑。这意味着我们对简单欠采样 50 %的反对意见:50 %也适用于平衡 RF 模型。

****结论:在没有夏季月份的样本上,我会选择 RF 模型,因为它对雪崩日有第二好的回忆 0.58 和可接受的 F1 分数。最佳召回率为 0.91 的平衡 RF 模型没有被选中,因为它的 F1 分数较低,精度很差,在现实世界中应用时可能会出现问题。

该项目是在Jedha公司组织的full stack 数据科学训练营期间完成的。**

参考文献和引文:

简单英语随机森林:为什么这么受欢迎?

原文:https://towardsdatascience.com/random-forest-in-simple-english-why-is-it-so-popular-3ba04d0374d?source=collection_archive---------17-----------------------

你想知道的关于常用算法的一切

法比奥·康帕雷利在 Unsplash 上拍摄的照片

随机森林是现实生活中数据科学项目以及数据科学竞赛中最流行和最常用的算法之一。这个故事背后的想法是用简单的术语来解释这个流行的算法。随机森林算法是一个集成模型,这意味着在进行预测的过程中建立了多个模型。具体来说,在随机森林的情况下,建立许多决策树模型,并基于这些独立决策树预测的平均值进行最终预测。

好的,但是这些决策树是如何构建的呢?他们使用相同的数据集吗?为了提高模型输出的性能,随机森林算法将为每个决策树使用不同的数据子集以及不同的特征子集。这确保了模型中没有偏差,因为没有特定的数据段或一组特征支配预测结果。它还确保了每个模型都是不同的,否则拥有这么多模型就没有什么好处了。如果单个模型是使用相同的数据集和相同的特征构建的,那么无论我们构建多少个不同的模型,预测结果都不会有太大的不同。随机森林算法避免了过度拟合,因为它使用来自数据和特征的不同子集,这有助于产生不同的不相关模型。

作者图片

现在说到它的名字,为什么叫随机森林?它被称为随机森林,因为我们使用随机数据和特征子集,最终我们构建了一个决策树(许多树)森林。随机森林也是 bagging 方法的一个经典例子,因为我们在每个模型中使用不同的数据子集来进行预测。

什么是决策树?

下面是一个决策树算法的简单例子。让我们在 iPhones、iPads 和 MacBooks 的分类中使用决策树算法。决策树是一种简单的基于规则的算法。它通过一系列问题对数据集进行分类,这些问题被称为决策节点。

在下面的例子中,屏幕大小首先用于将 iPhones 与 iPads 和 MacBooks 区分开来。然后,我们可以通过检查键盘和触控板的存在来区分 iPads 和 MacBooks。

作者图片

在现实生活中,如果出现分类问题,我们也会使用一系列问题将数据集分类到不同的类别中。在一个随机森林中,我们将构建许多这样的决策树来做出更好的预测。数据子集和特征的随机选择确保了随机森林中的各个决策树彼此不同。下面是不同的树在随机森林算法中的典型图像,

图片来自维基百科

为什么是随机森林?

一些特征使得随机森林算法在各种数据集以及不同类型的问题上表现得更好。是的,它既可以用于分类问题,也可以用于回归问题。以下是使它胜过许多其他型号的一些特征,

  • 随机森林中的最终模型预测基于来自所有个体决策树模型的聚集预测,因此避免了过度拟合。然而,这里重要的一点是,各个模型(决策树)不应该高度相关
  • 随机森林算法由许多决策树组成,决策树背后的机制也有助于识别在进行预测时很重要的特征,因此随机森林天生擅长变量选择
  • 与许多其他算法(如线性回归、SVM 等)不同。)random forest 对数据或其分布不做任何假设。因此,它通常需要最少的数据转换
  • 随机森林算法利用特征的随机子集,因此它可以很好地处理高维数据集(具有大量特征的数据集)

何时使用随机森林?

与其他算法相比,有些情况下随机森林是更好的选择。其中一些场景是,

  • 当数据集有离群值时,因为随机森林算法不受它们的影响
  • 当数据集中存在噪声时,许多算法会将噪声视为模式,但随机森林中使用的 bagging 方法可确保数据集中的噪声不会被错误地视为信号或模式
  • 当数据集中有缺失值时,随机森林具有有效的选项来估计缺失值并保持准确性,即使大部分数据缺失

因此,随机森林可能是处理含有异常值、噪声和/或缺失值的数据集的更好选择。

此外,随机森林算法可以有效地处理大型数据集,并且可以处理具有大量要素的数据集,而不会丢弃任何要素,也不需要降维

除了预测问题,随机森林可能是识别重要特征的一个好选择。

什么时候不用?

现在,我们来看随机森林并不是一个合适的选择。

  • 当预计测试数据集将不同于训练数据集时,随机森林不是一个好的选择。随机森林算法需要训练数据集包括所有或大多数可能的场景,因为它不能很好地处理测试数据集中出现的新模式
  • 如果需要建立特征和目标属性之间的关系,那么随机森林是不合适的。随机森林有助于识别重要要素,但无法解释它们与目标属性的关系
  • 当数据集使其成为线性回归或 SVM 等算法的明显候选对象时。例如,当要素和目标属性之间存在线性关系时,这对于任何线性模型都很有意义

使用 Python 的简单实现

作者编写的脚本

最后的想法

随机森林是数据科学家中最流行的算法之一。它可以灵活地用于不同种类的数据集,只需进行最少的数据转换。它适用于大型数据集和高维数据集。它适用于分类和回归问题。最后,因为它是基于规则的,所以很容易向业务涉众解释结果。这里有一个关于随机森林算法的简单教程——https://www.youtube.com/watch?v=_Wq_ZgEunqQ

保持联系

随机森林模型:为什么它们比单一决策树更好?

原文:https://towardsdatascience.com/random-forest-models-why-are-they-better-than-single-decision-trees-70494c29ccd1?source=collection_archive---------12-----------------------

机器学习

详细解释了随机森林机器学习算法的工作原理,它优于决策树的原因,以及如何用 Python 来构建它们

随机森林。图片由作者提供。

介绍

如果你想成为一名成功的数据科学家,你需要了解不同机器学习算法的细微差别。

这个故事是我深入研究这种算法如何工作的系列文章的一部分。这包括简单的示例、3D 可视化和完整的 Python 代码,供您在数据科学项目中使用。

故事涵盖以下主题:

  • 随机森林分类属于算法范畴
  • 解释随机森林分类如何工作,以及为什么它比单一决策树更好
  • Python 代码示例和可视化

它属于哪一类算法?

类似于其他一些算法,随机森林可以处理分类和回归。虽然在这个故事中,我关注的是分类,但同样的逻辑在很大程度上也适用于回归。

快速回顾一下分类和回归之间的区别:

  • 利用分类,我们试图预测一个类别标签。换句话说,它用于输出(目标变量)取一组有限值的问题,例如,明天是否会下雨。对应算法:[*sklearn.ensemble.RandomForestClassifier*](https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html#sklearn-tree-decisiontreeclassifier)
  • 同时,回归用于预测数字标签。这意味着您的输出可以采用无限多的值,例如,房价。对应算法:[*sklearn.ensemble.RandomForestRegressor*](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestRegressor.html)

这两种情况都属于机器学习算法的监督分支。下图是交互式的,所以一定要点击不同的类别来放大并展示更多的👇。

机器学习算法分类。由作者创建的互动图表。

如果你喜欢数据科学和机器学习 ,请 订阅 每当我发布一个新故事时,你都会收到一封电子邮件。

随机森林分类算法是如何工作的?

在引擎盖下,随机森林本质上是一个 CART 算法(分类和回归树),除了它创建了许多树的集合而不是一个。这提供了几个优点,例如:

  • 改进的性能(群体的智慧)
  • 改进的鲁棒性(不太可能过度拟合,因为它依赖于许多随机树)

如果你不熟悉 CART,你可以在我之前的故事里找到关于它的一切:

群体的智慧——它是什么,为什么好?

随机森林算法对类别标签使用多数一致预测,这意味着每棵树预测观察值是属于“类别 0”还是“类别 1”。如果 100 棵树中有 55 棵树预测为“1 类”, 45 棵树预测为“0 类”,那么最终的模型预测将是“1 类”。

多重决策树。图片由作者提供。

来自多个树的多数预测比单个树的预测更好,因为这些树相互保护免受它们各自错误的影响。然而,这取决于树之间相对不相关。

构建多棵树的过程

问题是,我们如何从同一个数据池中构建许多树,同时保持它们相对不相关?它有两个部分:

  1. Bootstrap 聚合(带替换的随机抽样)
  2. 特征随机性

自举聚合

决策树是所谓的 高方差估计器 ,这意味着样本数据的微小变化都会极大地影响树结构及其预测。

理想情况下,您希望通过创建许多树并在聚合中使用它们来进行预测,从而将它变成一个 低方差估计器

Bootstrapping 是一种技术,它不是采用整个训练数据来构建一棵树,而是使用替换采样来创建许多相同大小的样本。然后,您可以使用这些样本来构建单独的树。例如:

Whole data (10 observations): [1,2,2,2,3,3,4,5,6,7]Bootstrap sample 1 (10 obs): [1,1,2,2,3,4,5,6,7,7]
Bootstrap sample 2 (10 obs): [1,2,2,2,2,3,3,3,3,6]
...
Bootstrap sample n (10 obs): [2,2,2,3,4,5,6,6,7,7]*Note, how each of your samples consist of 10 observations.*

在这种情况下,您从整个样本中随机选取一个数字,并记录您选取的数字。然后重复这个动作,直到你记录了 10 次观察结果。

相当于从一桶红色和蓝色的球中随机选择一个球,记录它的颜色,然后在重复相同的动作之前将它放回桶中。

这种方法产生许多具有相同观察值但不同分布的样本。最后,这些树的聚集导致模型预测的方差减少,将其变成一个低方差估计器。

特征随机性

降低树之间相关性的第二部分来自于在为每个节点寻找最佳分割时使用随机选择的特征。

例如:

Full list of features: [feat1, feat2, ..., feat10]Random selection of features (1): [feat3, feat5, feat8]
Random selection of features (2): [feat1, feat5, feat10]
...The split in the first node would use the most predcitive feature from a set of [feat3, feat5, feat8]
The splint in the second node would use the most predictive feature from a set of [feat1, feat5, feat10]
...*Note, when building a Random Forest model, you can control the number/proportion of features that are used to find the best split for each node.*

此过程允许创建相关性较低的树,并与引导聚合相结合,生成一个随机森林。

随机森林分类模型的 Python 示例

抛开理论,让我们用 Python 构建一个随机森林模型。

设置

我们将使用以下数据和库:

让我们导入所有的库:

然后我们从 Kaggle 获取澳大利亚的天气数据,你可以按照这个链接下载:https://www . ka ggle . com/jsphyg/weather-dataset-rattle-package

我们接收数据并推导出一些新的变量用于模型中。

Kaggle 的澳大利亚天气数据的一个片段,做了一些修改。图片由作者提供。

接下来,让我们按照以下步骤构建一个模型:

  • 步骤 1-选择模型特征(自变量)和模型目标(因变量)
  • 步骤 2 —将数据分为训练样本和测试样本
  • 步骤 3-设置模型参数并训练(拟合)模型
  • 步骤 4-使用我们的模型预测训练和测试数据上的类别标签
  • 步骤 5-生成模型摘要统计数据

上述代码生成以下总结模型性能的输出。

随机森林模型性能。图片由作者提供。

正如我们所看到的,当与训练数据相比时,该模型在测试数据上具有相似的性能。估计值的数量是 1000,这意味着我们的随机森林由 1000 棵个体树组成。这是一棵树 552 的样子。如果您愿意,您可以通过更改索引来为它们中的每一个生成树形图。

决策树图。图片由作者提供。

奖金代码

虽然我们无法使用 17 个特征轻松地可视化所有模型预测,但当我们仅使用 2 个特征构建随机森林时,我们可以做到这一点。为此,我们首先需要使用上面的代码构建另一个模型,记住只选择 2 个特性:

X=df[['WindGustSpeed', 'Humidity3pm']]

建立模型后,我们使用以下代码用 Plotly 创建 3D 可视化:

上面生成了下图。

随机森林模型预测面。图片由作者提供。

从上面的图像中,我们可以清楚地看到,随着下午 3 点的湿度和阵风速度的增加,明天下雨的机会增加。此外,虽然不完全平滑,但与仅使用一个决策树相比,决策面的大步骤变化较少(见下图)。

单一决策树模型预测曲面。图片由作者提供。

结论

随机森林是性能最好的机器学习算法之一,已经被广泛采用。虽然它比单一的决策树模型更难解释,但它带来了许多优势,例如改进的性能和更好的泛化能力。

感谢您的阅读,您可以在自己的数据科学项目中随意使用上述代码和材料。

干杯!👏
索尔·多比拉斯

如果你已经花光了这个月的学习预算,下次请记得我。 我的个性化链接加入媒介是:

https://solclover.com/membership

此外,您可能有兴趣了解随机森林与 AdaBoost 的对比:

其他升压算法:

用一个真实的例子和一些 Python 代码解释了随机森林算法

原文:https://towardsdatascience.com/random-forests-algorithm-explained-with-a-real-life-example-and-some-python-code-affbfa5a942c?source=collection_archive---------2-----------------------

随机森林是一种机器学习算法,它解决了决策树 : 方差的最大问题之一。

图片作者。

这是致力于基于树的算法系列的第二篇文章,基于树的算法是一组广泛使用的监督机器学习算法。

第一篇是关于 决策树 。下一篇,也是本系列的最后一篇文章,探索梯度提升决策树。一切都用现实生活中的例子和一些 Python 代码来解释。

敬请期待!

随机森林是一种机器学习算法,它解决了决策树 : 方差的最大问题之一。

尽管决策树简单灵活,但它是贪婪算法。它侧重于优化手头的节点分裂,而不是考虑分裂如何影响整个树。贪婪的方法使决策树运行得更快,但也容易过度拟合。

过度拟合树被高度优化以预测训练数据集中的值,从而产生具有高方差的学习模型。

如何在决策树中计算方差取决于您正在解决的问题。

在回归任务中,您可以计算预测与真实目标相比的实际差异。如果树产生的结果与它的真实目标相差太远,那么它具有高方差,因此它是过度拟合的。

高度过度拟合的树具有高方差。这意味着它的预测与实际目标相去甚远。

但是在分类任务中,你不能使用同样的方法。检测决策树是否过拟合的方法是查看测试错误。如果树有很高的测试误差,意味着它不擅长对没有被训练过的观察值进行分类,那么它是过度拟合的。

在回归任务中,通过高方差来检测过度拟合,而在分类任务中,通过高泛化误差来检测过度拟合。

为了解决过度拟合,并减少决策树的差异, Leo Breiman 开发了随机森林算法【1】。这是一种创新的算法,因为它首次利用了自举的统计技术,并将多个模型的训练结果合并成一个更强大的学习模型。

但是,在您看到随机森林的运行和代码之前,让我们绕道探索一下随机森林的独特之处。

Bagging:引导聚合

Bagging ,是 Bootstrap Aggregation 的简称,是 Leo Breiman 开发的一种技术,目标是减少一个学习模型的方差。Bagging 也是模型不可知的,所以不管你使用什么类型的模型,过程都是一样的。

Bagging 的引导部分指的是从数据集[3]中抽取几个随机样本进行替换的重采样方法。因此,Bootstrapping 会从同一分布中创建多个更小的随机数据集。

每个自举数据集用于训练一个模型,然后输出被聚集成一个最终结果。

但是聚合也意味着不同的东西,这取决于你正在解决的问题的类型。

当你在处理一个回归问题时,聚合意味着对所有模型的每个观察结果进行平均。而在分类中,聚合意味着为每个观察选择最常见的类,就像进行多数投票

这令人印象深刻但是,这实际上如何帮助减少模型方差呢?

每个模型都在不同的数据集上训练,因为它们是引导的。所以不可避免地,每个模型都会犯不同的错误,并且有明显的误差和方差。误差和方差都在聚合步骤中减少,在回归的情况下,它们被平均化。

而且因为你最终得到的是一个单一的模型,它结合了多个模型的输出,Bagging 被称为集成技术

拔靴带

有了自举,你就遵循了少花钱多办事的座右铭。你把一个数据集乘以几个更小的随机数据集。

启动数据集的代码示例。

最初的数据集只有 10 个元素,但是最终产生了 5 个大小为 4 的采样数据集。

在某种程度上,乘以了数据集,当你合计单个自举数据集时,从仅仅 10 个观测值增加到 4x5=20 个观测值。

引导数据集的结果,生成 5 个样本,每个样本包含 4 个元素。

自举之所以有效,是因为你在用 替换****采样

您可以多次选择相同的数据点,就像在最后两个样本中一样,但是每个采样数据集与前一个略有不同。

如果没有替换,您将很快用完数据点,并且只能生成有限数量的样本。

这次绕道的重点是随机森林的独特之处。

让我们回到主题,随机森林如何减少模型方差。

随机森林

随机森林是专门为解决决策树中的高方差问题而开发的。顾名思义,你不是在训练一个单独的决策树,而是在训练整个森林!在这种情况下,一个袋装决策树的森林。

在高层次上,在伪代码中,随机森林算法遵循以下步骤:

  1. 获取原始数据集并创建大小为 nN 个袋装样本,其中 n 小于原始数据集。
  2. 使用每个 N 个袋装数据集作为输入来训练决策树。但是,在进行结点分割时,不要浏览数据集中的所有要素。从训练集中的所有特征中随机选择一个较小的数, M 个特征。然后使用杂质指标,比如基尼系数或熵,挑选出最佳分割。
  3. 将各个决策树的结果汇总到一个输出中。
  4. 如果您正在进行回归任务,请对每棵树生成的每个观察值进行平均。
  5. 如果你正在做一个分类任务,对每一个观察,在所有的树上进行多数投票。

虽然随机森林的森林部分指的是训练多棵树,但是随机部分出现在算法中的两个不同点。

装袋过程中存在随机性。但是,您也可以选择一个随机的要素子集来评估结点分裂。这保证了每棵树都是不同的,因此,确保了每个模型产生的结果略有不同。

虽然您可能认为在每次分割时随机采样特性会引入另一个您可能需要调整的超参数,但事实并非如此。模特帮你打理!

您当然可以调整这个超参数。然而,对于随机选取等于数据集中可用要素总数的平方根的多个要素,存在数学共识[2]。

为什么使用随机森林?

与训练单个决策树相比,随机森林有几个优点。

决策树的所有优点,但更强大

这个算法的核心是一个决策树,所以随机森林分享了它的所有优点。

这是一个数据健壮的算法,能够处理不同类型的数据,并且不需要任何数据预处理。

随机森林的真正潜力来自于组合不同决策树的结果。

每个型号都不一样

像决策树一样,该算法针对局部分裂进行优化。但是,它不是探索数据集中每个要素的所有可能分割,而是随机选取这些要素的子集。

这减少了算法在每次分割时需要评估的结果的数量,并且使得每个训练的树略有不同。

不需要维持集

在机器学习中,你通常将数据集分成训练集和测试集,努力用它从未见过的观察来评估模型性能。

当数据集很小,或者收集更多数据的成本和工作量很高时,这就成了一个具有挑战性的问题。

但是对于随机森林,您可以使用整个数据集来训练和评估模型。装袋过程会帮你搞定的!由于您正在生成 N 个较小的随机数据集,这些数据集是通过替换选取的,因此总会有一组点没有用于创建树。

看看前面的例子,它引导了数据集[1,2,3,4,5,6,7,8,9,10]。

引导数据集的结果,生成 5 个样本,每个样本包含 4 个元素。

如果你要使用这些样本中的每一个来训练一个决策树,有一些观察结果这些树根本不知道,因为它们不是它们训练集的一部分。

对于随机森林,您可以使用整个数据集来训练模型,并直接根据其结果计算测试误差。你不需要仅仅为了测试而留出数据集的一部分。

这种新类型的误差直接从训练树的结果中计算出来,被称为出袋误差

🛩🏝用随机森林减少模型方差

计划假期很有挑战性。这一次,你将把你的机器学习技能付诸实践,并获得关于你下一个度假目的地的算法意见。

每当你开始计划假期时,你总是会考虑到:

  • 假期的持续时间,
  • 个人预算,
  • 天气预报,
  • 如果你的大家庭要加入,
  • 如果你喜欢冒险,想要探索新的地方。

据说决策树是模仿人类做决定的方式。但是正如你在本系列的前一篇文章中看到的,一个单独的决策树并不是一个非常强大的预测器。

随机森林是对决策树的改进,但是它真的好很多吗?

只有一个办法可以知道!

但是首先,再看一下单个决策树的性能。

单个决策树的测试错误

如果只训练一个决策树,测试误差大约为 70%。

用于训练单个决策树来预测您的下一个度假目的地的代码示例。

测试误差,也称为泛化误差,相当高。高方差的标志。

决策树模型的测试错误。

好吧,你被说服了!决策树绝对不是帮助你选择下一个度假目的地的最佳算法。

让我们使用 ScikitLearn ,完全开箱即用并且没有任何超参数调整来训练一个随机森林模型。

代码来训练一个随机的森林来预测你的下一个度假目的地。

随机森林立即减少了 30%的测试误差!

与单个决策树的性能相比,这是一个令人印象深刻的结果。

随机森林模型的出袋误差,即测试误差。

你注意到的一件事是,默认情况下,该算法训练并结合了 100 袋树木的结果。这是您可以在随机森林中调整的众多超参数之一。

好奇心刚刚爆发了!

为什么是 100 棵树?

使用不同数量的树,算法的性能会更好还是更差?

可以减少多少泛化误差?

找出树木的最佳数量

您没有通过试凑法来调整这个超参数,而是决定将超出误差绘制为随机森林中树木数量的函数。

你决定训练任意数量的 150 棵树。但是你开始只有 5 个,慢慢地增加到 150 个,建立一个新的模型,每次增加一棵树。

将袋外误差绘制为随机森林中的树木数量的代码示例。

训练一大片森林并不能改善模型。

在这个过程中,你已经为你的任务找到了性能最佳点。你只需要随机森林中的 8 棵树就可以将泛化误差降低到 33%。

绘制作为随机森林中树木数量的函数的出袋误差的输出。

看一下这个图,很容易看出超出 8 棵树的最佳点后,这个误差变得越来越大。然后,当森林大于 50 棵树时,模型达到了性能稳定期

结论

您成功降低了模型的方差!

现在,您可以更加自信地使用随机森林来预测您的下一个度假目的地。而不是训练一个单独的决策树。

这是一个简单的例子,但是你可以看到泛化误差显著下降,从 67%下降到 33%。

随机森林是一个强大的算法,但它有一个明显的缺点。你无法看到模型如何决策。

决策树的一个很酷的方面是,在模型被训练之后,你实际上可以可视化模型如何做出决策。有了一棵足够小的树,你可以追踪整个决策过程,以获得新的观察结果。

但是对于随机森林,您需要牺牲可解释性来换取性能。

一旦你开始组合多棵树的决策,决策过程就会变得更加复杂,并且难以想象。

希望你喜欢学习随机森林,以及为什么它比决策树更强大。

敬请期待本系列的下一篇也是最后一篇文章!这是关于梯度增强决策树的。用一个真实的例子和一些 Python 代码来解释。

参考

  1. 随机森林。机器学习 45,5–32(2001)
  2. 加雷斯·詹姆斯,丹妮拉·威滕,特雷弗·哈斯蒂,罗伯特·蒂布拉尼。(2013).统计学习导论:在 r .纽约的应用
  3. Breiman,L. 装袋预测器马赫学24、123–140(1996)

随机森林和独热编码简介

原文:https://towardsdatascience.com/random-forests-and-one-hot-encoding-introduction-solving-kaggles-titanic-machine-learning-dataset-1607793f6988?source=collection_archive---------10-----------------------

解决 Kaggle 的巨大机器学习数据集

图片由 S.Herman 和 F. Richter 来自 Pixabay

Kaggle 的巨型机器学习数据集——机器学习领域的经典开源介绍。虽然这可能是一个初学者项目,但仍然有一个排行榜(当你继续编写代码时,看到自己的排名上升是很吸引人的)。

首先,您需要直接从 Kaggle 下载“train.csv”和“test.csv”文件。这个可以在这里下载:https://www.kaggle.com/c/titanic/data。请确保文件已放在指定的位置,以便以后提取。

在这场比赛中,一个大型火车数据集描述了那些因泰坦尼克号致命事故而缩短生命的人的各种特征,以及他们如何与生存相关。这包括分类数据,如性别、出发地点和各自的姓名。然而,也有数字数据,如年龄和票价。我们必须使用一次性编码算法来使整个程序和预测更适合分类数据。

我将在项目中使用的软件将是谷歌 Colab。然而,在这种情况下,这并不重要。我们不需要 GPU。使用没有 GPU 的 Jupyter 笔记本电脑也能正常工作。与计算机科学和机器学习中的许多其他项目类似,这个项目中要做的第一件事是对我们需要拉动的导入进行编程。

import pandas as pd from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier 

Pandas 将用于解析该任务的文件。第二个导入“train_test_split”将用于分割和分离培训和测试文件。我们需要的最后一个导入是 RandomForestClassifier,这是使用随机森林所需的大型总体导入。

正如我所说,我正在使用谷歌 Colab。因此,我需要将程序“安装”到 Google Colab 上来访问 Google Drive。我将用下面的代码做到这一点。

from google.colab import drive drive.mount('/content/gdrive')

现在,我们必须接受。csv 文件适当。这可以通过下面的代码来完成。

train_df = pd.read_csv('/content/gdrive/My Drive/train.csv') test_df = pd.read_csv('/content/gdrive/My Drive/test.csv') 

接下来,我将删除各种分类信息。这包括票、船舱和名字。这些名字已被删除,因为它们通常与存活与否无关。然而,如果这是一个广泛深入的分析,一些名字可能会更“重要”例如,基于姓氏或前缀。然而,在很大程度上,这将是一项徒劳无功的工作。此外,机票和客舱信息缺乏竞争力,因此最好忽略这些完整的数据集。

这可以通过下面的代码来完成。

train_df = train_df.drop(['Name'],  axis=1) test_df = test_df.drop(['Name'], axis=1) train_df = train_df.drop(['Cabin'],  axis=1) test_df = test_df.drop(['Cabin'], axis=1)train_df = train_df.drop(['Ticket'],  axis=1) test_df = test_df.drop(['Ticket'], axis=1)

要查看数据现在的样子,打印出列车数据集合可能是有益的。为此,请键入以下内容。

train_df

对于测试数据也可以这样做。

test_df

接下来,我们应该考虑数据集中的“已上船”类别。与机票和客舱数据集类似,缺少值。然而,并没有太多明显的差距。最常见的装载值是“S”,因此对于少数缺少的值,这些值将用“S”填充。这并不精确或完美,但每个值都必须填充。

common_value = 'S' data = [train_df, test_df] for dataset in data: dataset['Embarked'] = dataset['Embarked'].fillna(common_value) 

现在,我们必须使用一次性编码来组织分类数据。这将应用于剩余的两个分类数据集,即“sex”和“embark”这将应用于“训练”和“测试”数据集。

def clean_sex(train_df): try: return train_df[0] except TypeError: return "None"train_df["Sex"] = train_df.Sex.apply(clean_sex) categorical_variables = ['Sex', 'Embarked'] for variable in categorical_variables: train_df[variable].fillna("Missing", inplace=True)   discarded = pd.get_dummies(train_df[variable],prefix = variable)           train_df= pd.concat([train_df, discarded], axis = 1) train_df.drop([variable], axis = 1, inplace = True) 

以上应用于“列车”数据。现在,我们必须对“测试”数据集做同样的事情。

def clean_sex(test_df):try: return test_df[0] except TypeError: return "None"test_df["Sex"] = test_df.Sex.apply(clean_sex) categorical_variables = ['Sex', 'Embarked']for variable in categorical_variables: test_df[variable].fillna("Missing", inplace=True) discarded = pd.get_dummies(test_df[variable], prefix = variable) test_df= pd.concat([test_df, discarded], axis = 1) test_df.drop([variable], axis = 1, inplace = True) 

组织分类数据后,我们现在应该考虑数字数据。对于“年龄”和“费用”数据,存在未被考虑的缺失值。我们需要找到实现数据值的方法,而不是完全忽略这些数据集。为此,我们将取“年龄”和“费用”的平均值,并将其用于我们没有的值。我们必须对“训练”和“测试”数据都这样做。

train_df["Age"].fillna (train_df.Age.mean(), inplace = True)test_df["Age"].fillna (test_df.Age.mean(), inplace = True) train_df["Fare"].fillna (train_df.Fare.mean(), inplace = True)test_df["Fare"].fillna (test_df.Fare.mean(), inplace = True)

我们现在应该再次查看我们的数据,以再次检查所有适当的申请是否已经完成。

这将打印“列车”操纵的数据。

train_df

这将打印“测试”处理过的数据。

test_df

现在,因为年龄通常表示为整数,所以最好将其转换为基于整数的描述。我们同样需要对“训练”和“测试”数据集都这样做。

train_df = train_df.round({'Age':0})test_df = test_df.round({'Age':0})

此时,我们已经在“体面的水平”上操纵了我们的数据。虽然不完全精确,但我们可以开始使用随机森林算法来制定预测。

因此,对于我们的局部估计,我们将删除名为“X_train”的新变量的“幸存”值。然后,对于“Y_train”,我们将只包括“幸存”然后,我们直接让 X_test 等于“test_df”。最后,打印出形状。

X_train = train_df.drop("Survived", axis=1) Y_train = train_df["Survived"] X_test  = test_df X_train.shape, Y_train.shape, X_test.shape

为了仔细检查,打印出“x_train”可能是有用的同样,这可以简单地用下面的命令来完成。

X_train

接下来,我们应该初始化我们的随机森林。我们还应该使用各种参数来改进我们的算法。

random_forest = RandomForestClassifier(criterion='gini', n_estimators=700,    min_samples_split=10,     min_samples_leaf=1,     max_features='auto',     oob_score=True,     random_state=1, n_jobs=-1) random_forest.fit(X_train, Y_train)Y_pred = random_forest.predict(X_test) acc_random_forest = round(random_forest.score(X_train, Y_train) * 100, 2) acc_random_forest 

“n_estimators”值表示随机森林中使用的估计值的数量(森林中的树木数量)。在这种情况下,700 是一个运行良好的值。“Min_samples_split”是分离和拆分内部节点所需的样本数(最小值)。“Min_samples_leaf”表示成为叶节点所需的最小样本数。“Max_features”用于描述分割节点时考虑的随机特征子集的大小。设置为“自动”时,没有特征子集。“oob_score”设置为 true 时,将尝试使用“Out of Bag”分数来验证随机森林模型。当“n_jobs”设置为-1 时,将使用所有处理器来运行。

上述代码的最后一段符合模型,并生成一个与 X_test 数据一起工作的预测(y_pred)。随机森林的准确性将被打印出来。本地测试该数据产生大约 90%的准确度(91.81%)。然而,这只是基于从训练数据集中分离生存值的局部预测。为了得到准确的估计,我们需要直接提交给 Kaggle。为此,编写以下代码以获取“submission.csv”文件。

submission = pd.DataFrame({ "PassengerId": test_df["PassengerId"], "Survived": Y_pred})submission.to_csv('submission.csv', index=False)

结论:

Kaggle 产生的精度值为“0.79186”或“79.186%。”当我使用这个预测提交时,我在 19649 人中排名 1573,大约是前 8%。本地预测和 Kaggle 预测之间的区别在于,“训练”数据被修改为“测试混合”数据集,而 Kaggle 使用实际的“测试数据”。然而,在这两种情况下,都有一定程度的随机化。

总的来说,使用随机森林似乎产生了不错的结果。在很大程度上,这些预测是准确的。然而,为了简单起见,数据被丢弃了,这无疑损害了这一预测的整体实力。此外,虽然随机森林适用于低级别估计,但使用其他算法会产生更可靠、更准确的结果,并且通常与更长的训练时间相关。

NumPy 中的随机数

原文:https://towardsdatascience.com/random-numbers-in-numpy-89172d6eac16?source=collection_archive---------28-----------------------

如何使用 NumPy 的更新方法在 Python 中生成随机数

照片由 黑冰 发自 Pexels

按通常的理解,“1 2 3 4 5”不如“3 5 2 1 4”随机,当然也不如“47 88 1 32 41”随机,但“我们不能权威地说第一个序列不是随机的……它可能是偶然产生的。”— 维基百科

如果你在网上搜索如何在 Python NumPy 中生成随机数,你很可能会看到这样的代码:

作者 GitHub 要点

不久前,NumPy 已经更新了它生成随机数的方法,但是几乎所有的搜索结果都充斥着上述过时的代码,即使在今天也是如此。所以我决定写一篇小博文来解释这个更新的方法。过时的方法(在 NumPy 文档中称为 legacy RandomState )仍然有效,但是比新方法慢 2-10 倍。

那么,现在怎么产生随机数呢?

生成浮点数和整数

你可以复制粘贴代码并直接在 Jupyter 笔记本或 Python 解释器中运行它(在我的 Arch Linux 安装中,我默认使用了Python 3 . 9 . 2&NumPy 1 . 20 . 1)

作者 GitHub 要点

像往常一样, seed=(some number) 选项可以用来创建可重复的随机数,它工作得很好。在这里,前两个截图是在我的终端的两个不同的标签中同时拍摄的,而第三个截图是在几个月后在不同的 Python 版本中拍摄的。一个来自默认的 Python 解释器,另一个来自 IPython 解释器。检查随机方法调用的第二次运行是如何再现完全相同的数字的:

作者图片

作者图片

作者图片

你可以在第二张截图中看到发生器(PCG64) 。我们稍后会谈到这一点。首先,让我们探索更多的浮点数和整数随机数生成:

作者 GitHub 要点

这个可选参数" out" 并不适用于对 random 的每次调用。只对少数人开放。查看本文末尾的链接[4]

作者 GitHub 要点

下面是我的 Jupyter 实验室的截图,显示了两种方法在速度方面的比较:

作者图片

您在互联网上找到的基准测试通常依赖于上下文。尽管如此,这种更新的方法更快。

生成正态分布

如果我们不生成正态分布,任何关于随机数的对话都是不完整的。这是一个 Google 数据科学面试问题摘自面试查询( Jay Feng ):

编写一个函数,从正态分布中生成 N 个样本,并绘制直方图

它由两部分组成。首先,我们将使用更新的 NumPy 方法生成正态分布,然后我们将使用 matplotlib 绘制直方图。问题中缺少的一点是,问题陈述告诉你从正态分布中抽取 N 个样本,但没有告诉你面试官需要什么样的均值和标准差。所以我们可以想出两种方法:

  1. 编写一个函数,从标准正态分布中抽取 N 个样本(标准正态分布的平均值为 0,标准差为 1)
  2. 编写一个函数,它有三个参数:平均值、标准差和 N。它从正态分布中生成 N 个样本。

作者 GitHub 要点

同样,如果您使用相同的种子=(某个数字),那么您可以复制分布:

作者图片

现在让我们使用 matplotlib 绘制一个直方图:

这是它看起来的样子:

作者图片

我们可以使用正态分布绘制相同的图,其中有些值是平均值和标准偏差的值。你只需要用 _normal() 把函数替换成就可以了。

Matplotlib 直方图示例

Matplotlib.org 在其图库部分有一个直方图示例。要使用更新的方法,您只需更改 3 行代码:

matplotlib 的原始代码(作者修改)

这是输出结果:

作者图片

如你所见,直方图显示了正态分布。如果与 matplotlib 库中的原始输出和图形进行比较,即使我们使用了相同的种子编号,输出值和图形都发生了变化。为什么?

这是因为新旧方法使用不同的方式生成随机数。 Mersenne Twister 伪随机数发生器 (MT19937)就是老办法用的(现在还能用)。更新的方法使用排列同余发生器 (PCG-64)。不涉及技术细节:他们之间的主要区别是 PCG-64 有更好的(想一想“远面糊”)统计性能相比,梅森图斯特。它速度更快,也更节省空间。

这就是 NumPy 中产生随机数的新方法。如果您需要完整的技术细节,请查看以下内容:

[1]https://numpy . org/doc/stable/reference/random/bit _ generators/PCG 64 . html

[2]https://numpy . org/doc/stable/reference/random/bit _ generators/mt 19937 . html

https://www.pcg-random.org/

[4]https://numpy . org/doc/stable/reference/random/new-or-different . html

[5]https://en . Wikipedia . org/wiki/Permuted _ congential _ generator

https://en.wikipedia.org/wiki/Mersenne_Twister

随机投影及其在数据科学中的作用

原文:https://towardsdatascience.com/random-projection-and-its-role-in-data-science-f253dd66485b?source=collection_archive---------25-----------------------

随机投影概述及其在现代数据科学实践中通常扮演的角色

(图片由作者提供)

介绍

在数据科学中,通常很难处理非常高维的特征。这是因为计算机或人类无法分析高维数据,因为它太复杂了,无法从中得出结论。也就是说,在数据科学的奇妙世界中,有一系列技术可以用来降低数据的维度。

降低高维数据维数的最流行方法无疑是奇异值分解。奇异值分解很流行,因为它既高效又能为大多数数据科学应用产生相对合适的结果。然而,还有许多鲜为人知的分解方法。这种技术的一个例子是称为随机投影的技术。

随机投影是另一种用于降低高维数据维数的分解方法。这些技术因其强大、简单和惊人的低出错率而备受推崇。这是与许多其他分解方法相比较的,例如我们前面讨论的奇异值分解。这项技术的起源及其大多数实现的基础是一个叫做约翰逊-林登施特劳斯引理的东西。

约翰逊-林登施特劳斯引理

约翰逊-林登施特劳斯引理是数学家威廉·约翰逊和乔拉姆·林登施特劳斯首创的结果。该引理基本上迭代了高维数据可以被嵌入到低维数据中而几乎没有失真。这是通过在嵌入高维值的同时保留点之间的欧几里德距离来实现的。

这实际上是一个非常酷的数学公式,让它更有趣的是这个系统对于从数据中非常精确地减少维度是多么有效。此外,该引理相当简单,允许它非常容易地嵌入到一系列不同的应用程序中,同时由于利用 Johnson-Lindenstrauss 引理执行的操作的性质,仍然保持非常高的性能。

不用说,这种对高维数据进行分解的技术非常有用。随机投影有一定的优势和使用案例,它将显著优于奇异值分解等其他技术,我认为只要科学家想处理这些类型的高维特征,熟悉这项技术可能是非常重要的。如果您碰巧使用 Python 编程语言处理数据,您可能会很高兴地了解到,SkLearn 模块有一个非常古老的实现,您可以利用它。以下是如何导入它:

**from** **sklearn.random_projection** **import** johnson_lindenstrauss_min_dim

随机投影技术

虽然构成随机投影基础的引理非常棒,但更棒的是我们实际上可以对数据执行的技术。几乎可以肯定,这样的例子不止两个,但现在我们只关注以下两种技术:

  • 高斯的
  • 稀少的

高斯的

高斯随机投影通过将原始输入的维度空间投影到随机生成的矩阵上来降低高维数据的维度。这种方法非常有用,因为它是实现 Johnson-Lindenstrauss 引理的一种相对基本的方法,仍然非常有效。如果您使用 Python 语言,这在 SkLearn 中也是可用的,所以让我们来看看如何使用它:

**from** **sklearn** **import** random_projection
transformer = random_projection.GaussianRandomProjection()
X_ld = transformer.fit_transform(X)

稀少的

通过使用稀疏随机矩阵投影原始输入空间来降低维数。这可以用来代替高斯方法,以便节省一些内存并提高性能。这是因为稀疏矩阵中的值通常更分散,根据 Johnson-Lindenstrauss 引理,将这些数据随机投影到具有合理欧几里德距离的矩阵上总是会给我们低维数据。由于 SkLearn 模块的实现,这种技术也可以在 Python 编程语言中使用:

**from** **sklearn** **import** random_projection
transformer = random_projection.SparseRandomProjection()
X_ld = transformer.fit_transform(X)

结论

总之,我认为随机投影是一个非常重要但有时被完全忽略的概念,它对数学分析和机器学习都非常有价值。这当然是一个有趣的替代技术,将产生惊人的结果,同时仍然保持非常快。数学背后的思想是相对基础的,只包括将随机维度投影到矩阵中,同时保持一致的欧几里德距离,这可能是机器学习模型理解数据最重要的事情之一。

随机样本共识帮助你过滤那些讨厌的离群值

原文:https://towardsdatascience.com/random-sample-consensus-helps-you-filter-those-pesky-outliers-9dbfbb8b668f?source=collection_archive---------21-----------------------

实践教程

让观察决定哪些是一致的。

我们都应该感谢古希腊人给了我们民主。尽管存在缺陷,但在有人提出更好的想法之前,它仍然是最大化公共利益或最小化公共不满的最佳制度,这取决于你选择如何看待它。

来自 Unsplash阿米尔·汉纳的帕台农神庙图片

这种政治体系的核心思想是,越多的人将他们的重量放在天平上,大多数人错的可能性就越低。这是一种只适用于大多数时间的启发式方法。我相信你能想出几个大部分选民误入歧途的例子。尽管如此,大多数情况下,询问人们想要什么——比如通过全民公决——会产生不错的结果。

公投对于政策选择的意义,就像 RANSAC (随机抽样共识)对于模型拟合的意义一样。

用噪声数据自动拟合模型(直线、圆、平面、抛物线……)是计算机视觉中的常见问题。假设您有一个描述数据分布的参数化模型,并且希望使用一些数据来优化模型参数值,RANSAC 将提供一种自动方式来过滤掉数据中的异常值。RANSAC 算法的伟大见解是将这个问题作为一个公投,其中多个模型参数集为最大数量的观察值的一致而竞争。个人观察就是投票者。他们给候选参数集分配一个投票分数,作为候选模型参数与它们匹配程度的函数。主要思想是这样的事实,即内联者将同意相同的参数集,而外联者将被隔离在他们同意被破坏的参数集的情况下。内联者的一致性将允许一组参数来建立大量的投票,并作为获胜者出现。

在本文中,我们将通过代码 来检查 RANSAC 算法和 看看它的运行情况。

让我们考虑一个简单的例子。你收集了二维数据,并在平面上绘制了点云。这是你得到的:

2D 点云的分布

在一瞬间,你的内部模式匹配机制发现了一条对角线上的点的集中。你的数据有一个有趣的趋势!这不正是你所希望的吗?让我们用线性回归来描述我们模型的(ρ,θ)参数,使用我们所有的观测值。

存在异常值的线性回归给出了令人困惑的结果

进展不太顺利。您可能知道,最小二乘法(比如这里使用的方法)对异常值很敏感,在这个云中有很多异常值(就我们只对高密度的对角线区域感兴趣而言)。当然,您可以手动循环观察,决定它们是否是感兴趣区域的一部分,并仅对内联者运行线性回归。这将是乏味的,但在这种情况下是可行的。然而,在有些情况下,模型拟合是自动设计的算法中的步骤。过滤离群值必须自动化,这就是 RANSAC 的工作。

RANSAC 由 Fischler 和 Bolles 于 1981 年首次推出。他们的激励应用类似于我们刚刚描述的问题。该算法的基本思想是从多个随机选择的观察元组(在我们的情况下是观察对)中生成候选模型(在我们的情况下是候选线)。元组包含唯一确定模型参数所需的最小数量的观察值。然后,对于每个候选参数集,每个观察值必须通过将一些距离测量值与阈值进行比较来检查它们是否与候选模型一致。如果给定的观察结果与候选模型一致,它将为候选参数集贡献一个投票分数。如果距离低于阈值,投票分数可以是 1,否则是 0(二进制投票),或者随着距离从 0 到阈值,投票分数可以是连续单调递减的值。一个观察可以为一个以上的候选参数集贡献一个正的投票分数。在候选人创建试验结束时,累积最高投票分数总和的候选人模型是获胜者。另外,RANSAC 还可以输出内部和外部观察值的划分。有了这些信息,就可以使用所有的内嵌器计算出最终的模型。

让我们将 RANSAC 算法应用于我们的点云,拟合一个线模型。除了我们可以在这个点云中拟合线模型的假设之外,我们还需要两个参数:试验次数(即将要生成的候选参数集的数量)和可接受的误差(即候选线和给定观察值之间的阈值距离)。

建模者通过共识创建线条模型。作者代码。

如果我们绘制共识模型以及内点和外点之间的划分,我们会得到一个好得多的结果:

RANSAC 发现的线性回归

多少个候选参数集足够了?

由于 RANSAC 是基于随机抽样的观察,我们不能保证我们会找到满足最大数量的观察的参数集。如果我们假设每当我们采样至少一次 nₘᵢₙᵢₘᵤₘ内联时,找到足够好的候选模型,并且选择内联的概率是 pᵢₙₗᵢₑᵣ,则成功的概率将是:

…其中 nₘᵢₙᵢₘᵤₘ是一个常数,由我们拟合的模型类别决定。它是定义模型参数所需的最小数量的观察值。

对于我们的例子,pᵢₙₗᵢₑᵣ = 0.5,nₘᵢₙᵢₘᵤₘ = 2(我们需要两个点来定义一条线),nₜᵣᵢₐₗₛ = 100,我们得到

我们是安全的。我们也可以使用上面的等式来分离 nₜᵣᵢₐₗₛ,作为期望的成功概率和估计的内联体比例的函数。

RANSAC 仅限于线性模型吗?

一点也不!例如,你可以有一个点云,它应该被建模为一个圆。甚至可能在点云中隐藏不止一个圆,RANSAC 将返回满足最大数量观察的圆模型。

RANSAC 在噪声点云中找到的圆

如果您也对识别较小圆的参数感兴趣,您可以移除第一个找到的模型所满足的观察值(换句话说,您只保留异常值)并再次运行 RANSAC 算法。

RANSAC 算法已经有 40 年历史了。与古希腊的民主相比,这算不了什么,但它已经被证明非常有用,尤其是在计算机视觉领域。例如,边缘检测器可以产生一个对象的边缘像素的掩模,假设是一个线性段。由于不能期望掩模没有假阳性,RANSAC 将通过仅保留对齐的像素来帮助我们拟合对象边缘。

请随意使用代码进行实验。我很高兴听到你心目中 RANSAC 可以投入使用的应用,无论是在计算机视觉领域之内还是之外。

忽略了这样一个事实,即我们在选择一个观察值后不会替换它

random.seed 在 NumPy 中做什么

原文:https://towardsdatascience.com/random-seed-numpy-786cf7876a5f?source=collection_archive---------32-----------------------

理解在 Python 中使用 NumPy 生成伪随机结构时如何创建可再现的结果

安德鲁·西曼在 Unsplash 上的照片

介绍

随机性是一个基本的数学概念,通常也用在编程的上下文中。有时,当我们创建一些玩具数据时,或者当我们需要执行一些依赖于一些随机事件的特定计算时,我们可能需要引入一些随机性。

在今天的文章中,我们将首先讨论伪随机数和真随机数的概念。此外,我们将讨论 numpy 的random.seed以及如何使用它来创建可重复的结果。最后,我们将展示如何确保在整个代码中保持相同的种子。

理解伪随机性

伪随机数序列是使用确定性过程生成的序列,但是看起来统计随机的

即使由伪随机发生器(也称为确定性随机位发生器)产生的序列的性质近似于随机数序列的性质,但实际上它们并不是真正随机的。这是因为生成的序列由初始值决定,该初始值被称为种子

您可以将种子视为序列的实际起点。伪随机数生成器基于对先前生成的值执行的一些处理和操作来生成每个数字。由于要生成的第一个值没有生成的可以对其执行这些操作的先前值,因此种子充当要生成的第一个数字的“先前值”。

使用 random.seed 创建可重复的结果

同样,NumPy 的随机数例程生成伪随机数序列。这是通过使用位生成器(生成随机数的对象)和生成器创建一个序列来实现的,这些生成器利用创建的序列从不同的概率分布(如正态分布、均匀分布或二项式分布)中进行采样。

现在,为了生成可再现的伪随机数序列,BitGenerator 对象接受一个用于设置初始状态的种子。这可以通过如下所示设置[numpy.random.seed](https://numpy.org/doc/stable/reference/random/generated/numpy.random.seed.html)来实现:

import numpy as np**np.random.seed(123)**

在不同的用例中,创建可重复的结果是一个常见的需求。例如,当测试某项功能时,您可能需要通过将种子配置为特定值来创建可重复的结果,以便可以将生成的结果与预期的结果进行比较。

此外,在更广泛的研究领域中,产生可重复的结果是常见的。例如,如果您使用一个使用随机性的模型(例如一个随机的森林)并想要发布结果(比如在一篇论文中),那么您可能想要(并且可能已经)确保其他人和用户可以复制您所呈现的结果。

种子的局部效应

值得一提的是,NumPy 中的随机种子还会影响其他方法,例如[numpy.random.permutation](https://numpy.org/doc/stable/reference/random/generated/numpy.random.permutation.html),它还会产生局部效应

这意味着如果你只指定了numpy.random.seed一次,但是调用了numpy.random.permutation多次,你得到的结果将会不同(因为它们不依赖于同一个种子)。为了展示这个问题,让我们考虑下面的代码:

import numpy as npnp.random.seed(123)print(np.random.permutation(10))
***array([4, 0, 7, 5, 8, 3, 1, 6, 9, 2])***print(np.random.permutation(10))
***array([3, 5, 4, 2, 8, 7, 6, 9, 0, 1])***

如您所见,即使我们设置了随机种子,结果也是不可重复的。这是因为random.seed只有‘局部效应’。为了重现结果,您必须在每次调用np.random.permutation之前指定相同的随机种子,如下所示。

import numpy as npnp.random.seed(123)
print(np.random.permutation(10))
***array([4, 0, 7, 5, 8, 3, 1, 6, 9, 2])***np.random.seed(123)
print(np.random.permutation(10))
***array([4, 0, 7, 5, 8, 3, 1, 6, 9, 2])***

最后的想法

在今天的文章中,我们讨论了真或伪随机性的概念以及 NumPy 和 Python 中的random.seed的用途。此外,我们展示了如何在每次执行相同的代码时创建可重复的结果,即使结果依赖于一些(伪)随机性。最后,我们探讨了如何在需要时确保随机种子的效果在整个代码中得以维持。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。

你可能也会喜欢

https://betterprogramming.pub/11-python-one-liners-for-everyday-programming-f346a0a73f39

带重启的随机漫步解释

原文:https://towardsdatascience.com/random-walks-with-restart-explained-77c3fe216bca?source=collection_archive---------12-----------------------

理解带重启的随机游走算法及其 Python 实现

来自的图像未绘制

本文的范围是解释并着重于带重启的随机游走算法背后的概念理解。这里不会提供很强的数学理解,但是我留下了一些资源链接,感兴趣的人可以在那里进一步研究这个算法背后的数学。在文章的最后,我还将提供一个与该算法的实现相关的 Python 脚本文档。

目录

  • 介绍
  • 什么是随机漫步?
  • 重启随机漫步
  • 优势
  • 不足之处
  • Python 实现
  • 资源

介绍

这种算法在研究和工业中有很高的应用价值。如果您正在处理时间序列数据,或者能够将数据结构化为网络,那么可以对其实施随机行走/带重置的随机行走。在金融领域,人们通常认为股票价格的变化遵循随机游走规律。这种算法与其他随机过程如马尔可夫链蒙特卡洛模拟配合得很好。这种简单易懂的算法最初是由数学家卡尔·皮尔逊正式提出的,他毕生致力于围绕数理统计建立一个坚实的基础。

在下面的例子中,我们将对以下任何一个事物进行随机漫步。
1)具有顶点 V 和边 E 的连通图 G 将多个节点相互连接

2)n×n 矩阵
注:矩阵可以转换成图,反之亦然

应用该算法的常见问题有:

  • 推荐系统
  • 社区检测
  • 使聚集
  • 图象分割法

什么是随机漫步?

随机漫步是一种随机过程。对随机漫步最简单的解释就是漫步。想象你走的每一步都是由概率决定的。这意味着在每一个时间指数上,你已经基于一个概率结果朝着一个确定的方向前进。该算法探索了您将要采取的每一步的关系以及它与初始起点的距离。

图 1 : 2D 随机漫步图(图片由作者提供)

如果你从原点(上图中的大黑圈)开始,在每个时间步向任意方向移动的概率相等。上图描绘了 4 个时间步后的 3 种可能的行走,每一步都有一个不同的起点。如果这个过程用足够大的时间步长模拟了很多次,那么您可以创建一个与在特定位置结束的每个随机行走的结果相关联的概率分布。

在这个二维的例子中,经过多次随机行走的迭代后,分布将看起来像一个 3D 钟形曲线(如图 2 所示)。这可以在 N 维中复制。

图 2:2D 平面上随机游走的概率分布(图片由作者提供)

给定这个概率分布,它可以表示为一对位置之间的接近程度。

重启随机漫步

重启随机漫步与随机漫步完全一样,只是多了一个组件。这个分量可以表示为重启概率。本质上表明,对于在任何方向上迈出的每一步,都有可能回到最初的起始位置,即原点。在我们上面的任意方向随机移动的例子中,现在想象有一个机会,你会在每一步后基于这个重新开始的概率瞬间传送回原点。

这篇文章很好地解释了重启随机漫步算法背后的数学原理。

优势

  • 在加权和未加权图中提供两个节点之间相似性的强表示
  • 具有多种应用,并且可以与其他随机模型(如马尔可夫链)结合使用

劣势

  • 对每个节点使用相同的重启概率
  • 没有系统的方法来选择每个应用程序的重启概率

履行

在这里,我在一个给定的网络 g 上实现了带重启的随机漫步算法。注意,这个算法有很多变体,可以根据你要解决的问题进行实质性的改进。该脚本的结果输出可用于其他 ML 算法和模型,如 kNN 和 k means。

这个算法的一个非常简单的 1 行实现可以通过一个名为 n etworkX 的库来实现。他们有一个名为 PageRank 的内置函数,本质上是一个带重启的随机漫步。下面我在 networkX 文档中为 PageRank 添加了一个简单的例子。

import networkx as nxG **=** nx**.**DiGraph(nx**.**path_graph(4))  # create the network
pr **=** nx**.**pagerank(G, alpha**=**0.9)    # apply PageRank 

资源

如果您喜欢这篇文章,那么您可能也会喜欢我的其他文章,在这些文章中,我解释了蒙特卡罗方法和马尔可夫链之类的东西。

[## 贝叶斯 A/B 测试解释

towardsdatascience.com](/bayesian-a-b-testing-explained-344a6df88c1a)

随机化、分块和重新随机化

原文:https://towardsdatascience.com/randomization-blocking-and-re-randomization-f0e3ab4d79ca?source=collection_archive---------13-----------------------

实验和因果推理

在线实验的三个基本支柱

照片由盒装水更好Unsplash

介绍

A/B 测试在业内越来越受欢迎。几乎所有的顶级科技公司都会分配大量资源,开发支持数百甚至数千个并行 A/B 测试的内部实验平台(ExP)。

每当有疑问时,A/B 就测试它。

在很大程度上,主动测试策略非常有效,因为它帮助公司减轻任何潜在的风险,并随着时间的推移产生稳定的收入。A/B 测试的美妙之处在于它能够以可扩展的方式客观地评估想法。

任何测试策略的成功都来自多个组成部分,因为存在避免的常见陷阱,遵循最佳实践,确保数据质量,以及防止用户干扰

影响大规模实验成功的各种因素不胜枚举。在今天的博文中,让我们来讨论促成在线实验高度有效性的三个基本统计概念:随机化、分块和再随机化。

玩具的例子

让我们探索一个典型的 A/B 测试用例:我们想检验一个新产品特性将如何影响由每日活跃用户(DAU)测量的用户参与度。现有的用户体验研究表明,一小部分人(例如,<2%) of heavy users who engage with the product differently from the rest of the population.

How would you design a study that assesses the new product performance?

The remainder of the post aims to answer this question from the sampling perspective. 第 1 部分解释了为什么简单的随机化过程会导致任务失败;第 2 部分介绍了作为尝试性解决方案的阻塞及其局限性;第 3 部分说明为什么再随机化是首选。

亚当·库尔在 Unsplash 上的照片

第一部分:为什么我们不把它随机化呢?

在数据科学社区内外,人们对随机化及其功能有着广泛的误解。我经常看到产品经理、数据工程师,甚至数据科学家天真地假设随机化总是导致协变量平衡,不幸的是这是不正确的。

这里有一个详细的演练,为什么这是一个弱假设。对于在线实验,数据科学家收集代表整个客户群(即总体)的随机样本。因此,我们不必担心研究结果的泛化,也就是外部效度。

收集样本后,我们将数据随机分为两组,并对一组(治疗组)进行处理,而不对另一组(对照组)进行处理。由于其随机性,我们预计治疗组和对照组之间的协变量没有统计学差异,平均。这是 A/B 测试的秘密力量,因为它能够实现除治疗条件外的协变量平衡,这使得治疗效果可以识别。

相反,如果我们没有随机化,一些用户可能会自行选择进入治疗组或对照组,导致选择偏差。如果出现这种情况,我们不能天真地将两组的差异归因于干预,而应该求助于准实验来寻找可行的替代方案。查看这个中间列表(链接)来全面回顾主要的准实验设计。

回到最初的问题:

我们能依靠随机化来保证治疗组和对照组之间的协变量平衡吗?

可惜,答案是否定的!随机化平衡协变量在** 平均值上,但不能保证每个实现都有平衡的协变量。这是一个快速的数学演练。让我们假设在一个协变量中有 5%的机会观察到统计上显著的差异,并且在 10 个协变量中至少有一个协变量不平衡的概率(即,家庭误差率)是 1—(1–0.05)⁰= 0.4 或 40%。换句话说,如果需要调整 10 个协变量,我们将在 40%的时间里观察到至少一个不平衡的协变量。这是一个相当高的数字。**

假设我们的业务场景中有一小部分重度用户,简单的随机化过程很可能无法将重度用户平均分配到实验组中。

最后一次,随机化旨在平均平衡协变量,但不保证任何特定样本的平衡。****

为什么简单随机化是次优选择的另一个密切相关的原因。重度使用者的不平等分布将扩大方差,使中小规模的治疗效果不被注意。 之所以如此,是因为方差与样本量的平方根成正比,其他因素保持不变。 例如,如果方差增加 10 倍,我们将需要多 100 倍的用户。如果方差增加,而样本大小保持不变或者跟不上变化的速度,那么测试就会力不从心,无法检测出任何差异。

我自己的截图

其中:

  • σ:样本方差。
  • 𝛿:治疗组和对照组的区别

如果我们将α设为 0.05,β设为 0.2(或幂设为 0.8),则上述公式是最小样本量的近似值。关于以上公式的更多信息,请参考这篇文章

下一节将介绍阻塞以及它为什么对我们有利。

照片由 v2oskUnsplash 上拍摄

第 2 部分:为什么阻塞更好?

如上所述,简单的随机化过程可能会导致协变量不平衡。幸运的是,实验设计的统计理论提出了一种称为分块的解决方案,即,将实验单元分成块(例如,设备类型、年龄、性别等)。)是彼此同质的。

在我们的例子中,我们的主要兴趣是新产品特性的效果。因此,消除使用频率等其他变量的影响(例如,重度用户与轻度用户)将会非常有用。在实践中,我们可以设置一个切割阈值,并根据使用频率创建三个等级:低、中和高。然后,我们在每一层将用户随机分为治疗组和对照组。

与单独随机化相比,分组随机化策略更有可能达到协变量平衡。此外,阻塞减少甚至消除了阻塞变量的估计误差。从方差减少的观点来看,分块可以减少块之间的方差,从而增加测试灵敏度。

然而,这里有一个例子:阻塞会遭受维数灾难。也就是说,当有多个具有多个级别的协变量时,阻塞会失败。让我们再次使用前面的例子,假设有 10 个变量,分为 4 个级别。要完全部署阻塞,我们需要至少 4 个⁰,或者至少 100 万个可能的地层,这是一个令人生畏的数字。

你有足够的数据/用户来填充每个阶层吗?

数量随维度呈指数级增长,这限制了阻塞的使用情况。

实际上,我们封锁了最重要的变量(例如,2-3),并依靠随机化来处理剩余的有害变量。实验设计中的名言来了:

能挡就挡;

随机化你不能随机化的。

—乔治·博克斯

有了阻塞,我们就可以回答开头问的问题。如果我们事先知道访问者是重度或轻度用户(例如,通过登录信息),我们可以创建块(层)并将它们放入不同的层。这样可以减少方差,让实验更灵敏有力。

但是,对于登录信息不可用或数据以实时流方式到达的其他情况,我们无法阻止;或者在其他情况下,我们只知道事后的协变量,不可能事先创建块。例如,在电话营销活动中,我们不知道随机回答者的性别。总的来说,一个好的经验法则是屏蔽掉最重要的因素,把剩下的因素随机化。

在最后一节中,我们讨论了另一个叫做再随机化的概念。

戴维·马尔库在 Unsplash 上的照片

第 3 部分:重新随机化

阻断和随机化都不能总是保证协变量平衡,更不用说当阻断不可用时。如上所述,实验的高内部效度在于其严格控制混杂变量和避免选择偏差的能力。如果随机化不能实现协变量之间的平衡,它的高有效性将受到损害。

倒霉随机化后怎么办?

重新随机化。

从字面上看,这意味着我们应该为任何不幸的实现重新运行随机化过程。关键是在我们运行实验之前,设置一个阈值来衡量协变量(im)平衡的水平。一种典型的度量选择被称为治疗组和对照组的协变量平均向量之间的马氏距离

关于再随机化和 【马哈拉诺比斯距离】 *杨等,2021,调查实验中拒绝抽样、再随机化和回归调整 的更多信息,请查阅本文***

Medium 最近进化出了自己的 作家伙伴计划 ,支持像我这样的普通作家。如果你还不是订户,通过下面的链接注册,我会收到一部分会员费。

*****https://leihua-ye.medium.com/membership *****

结论

数据科学家对在线实验非常有信心,希望测试每一个产品决策,而不质疑其有效性。总的来说,这是一个很有前途的努力,有很高的投资回报率。然而,我们应该记住随机化并不能自动保证协变量的平衡。例如,实验团队@ Bing 报告称,随机化后,四个实验中有一个存在协变量失衡。在这种情况下,我们需要应用辅助工具,如分块和重新随机化,以确保协变量平衡。

喜欢读这本书吗?

请在 LinkedInYoutube 上找到我。

还有,看看我其他关于人工智能和机器学习的帖子。

随机化加速奇异值分解

原文:https://towardsdatascience.com/randomized-svd-with-power-iterations-for-large-data-matrices-264e470bb2b4?source=collection_archive---------26-----------------------

思想和理论

Twitter 的 WTF 和谷歌的 PageRank 的秘密调料之一

几天前,我在一个论坛上偶然发现了一个问题。有人在寻求帮助,关于如何对一个非常大的矩阵进行奇异值分解(SVD)。总而言之,这个问题大致如下

“我有一个大小为 271520*225 的矩阵。我想从中提取奇异矩阵和奇异值,但我的编译器说这需要半兆字节的内存……”

半兆字节的内存。这里不开玩笑。然而,这个特殊的问题正变得日益重要,因为我们现在正经历着大量的数据。例如,我们智能手机摄像头中改进的传感器或 YouTube 中的流媒体高清视频。在有限的计算资源或严格的延迟要求下,我们越来越多地面临需要实时处理数百万次测量甚至更多数据点的情况。除此之外,还有两个问题。第一个问题是:通过这些测量获得的固有等级或信息是否也随着测量次数的增加而以同样的速度增加?答案通常是否定的。那么下一个问题是:如果固有秩很小,我们能以一种计算高效的方式进行奇异值分解吗?

SVD 被誉为线性代数的瑞士军刀。我个人认为这是一种错误的宣传方式。它经常使人们不知道它所需要的开销,并导致如上的情况。在数值上,SVD 是通过求解一个计算量很大的底层优化问题来计算的。在这篇博客中,我们将研究在超大数据集上应用 SVD 的瓶颈。然后我们将会看到,来自随机矩阵理论的优雅定理如何为我们提供有效处理这些瓶颈的工具,并引导我们获得高度精确但计算效率高的数据矩阵近似值。

让我们考虑一个高大的矩形数据矩阵

其中p<q。这实质上意味着我们有了 p 特征的 q 数据点。SVD 将这个可能庞大的矩阵分解成三个矩阵,其中两个分别包含左和右奇异向量,另一个包含奇异值,以降序对角排列。

其中, Up 左奇异向量的矩阵, Vq 右奇异向量的矩阵, E 是奇异值按降序排列的对角矩阵。上标代表转置。如果 S 是满列秩矩阵,那么它只有 q 非零奇异值,其余 p-q 奇异值全为零。如果的情况,那么计算所有左奇异向量的确切意义是什么,因为它们无论如何都要乘以零?答案是——没有意义。

现在,我们有两条路可走。首先,我们计算整个 SVD。然后把 p-q 左奇异向量扔掉。这可能是最没有价值的路线,因为我们正在毫无理由地进行所有这些计算负担。为了追求第二条道路,我们可以更新我们的目标如下

"我们如何能够只计算 q 个左奇异向量而不计算整个奇异值分解?"

我们求助于随机矩阵理论来寻找这个问题的答案。它告诉我们——如果我们通过随机投影对原始数据矩阵 S 的列空间进行随机采样,投影极不可能丢弃数据矩阵所讲述的故事的任何重要部分。随机投影的想法很大程度上是基于著名的约翰逊-林登斯特劳斯引理。如果我们固定一个目标秩 r ≤ q ,那么我们可以对我们的数据矩阵进行随机投影:

在哪里

是一个随机投影矩阵。例如,它可以是高斯随机矩阵。随机矩阵的选择本身是一个不同的研究,因此我在这里省略了细节。现在,投影矩阵 T 几乎可以肯定地逼近原始数据矩阵 S 的列空间(感谢 Johnson-Lindenstrauss 引理)。所以,如果我们想找到一组 S 的正交基,对 T 而不是 S 进行 QR 分解就足够了!

注意,现在我们有了 r- 秩近似的S 的标准正交基。现在我们取 S 的另一个投影,但是这次是在正交矩阵 Q 所跨越的子空间上。

SVD 提供的一个重要优势是,它可以推广到任何维度的数据矩阵,不像特征分解只适用于方阵。这还不是故事的结尾。以下也成立。

因为 Q 是正交的,所以我们可以写

这意味着,如果我们取γ,的奇异值,那么相应的奇异值和右奇异向量将分别与 EV 提供的相同!

现在,我们都准备好寻找我们的近似 U,,而不需要对海量数据矩阵 S 进行任何基于 SVD 的重型优化。这可以通过下面简单的矩阵向量乘法来实现。

这是一个很好的例子,说明随机化提供了计算上更简单的方法来解决数值上复杂的问题。

幂迭代的作用

对于那些熟悉推荐系统如何工作的人来说,Power Iterations 是一个非常著名的框架。像随机化 SVD 一样,幂迭代也源于随机矩阵理论。在用单位范数随机向量初始化时,它迭代地计算方阵的主特征值。它在谷歌的 PageRank 算法中起着至关重要的作用。此外,Twitter 的 WTF(跟随谁)算法也与幂迭代有着显著的联系。

然而,在我们的冒险中,我们对计算主要特征值不感兴趣。还记得上一节中我们的目标等级 r 吗?我没有具体说明任何关于选择目标等级的策略,因为这取决于环境。如果我们选择的目标秩 r 小于固有秩,并且我们最终放弃了大量的方差,该怎么办?描述这种现象的另一种方式是——如果我们的数据矩阵 S 的奇异值衰减非常慢会怎么样?

在这种情况下,我们可以使用幂迭代对数据矩阵进行预处理。幂迭代是一种将方差压缩为前几个奇异值的方法。让我们定义如下

根据特征分解的定义,我们知道

上标 (i) 表示相应矩阵的幂已经被提升到 i 。现在,如果我们做以下事情

那么很容易证明以下成立

上面处理过的数据矩阵现在有了更快的衰减奇异值谱。然而,它可以在近似传统 SVD 方面给我们带来巨大的改进,代价是一些额外的计算。实际上,通过矩阵乘以自身的幂来进行幂迭代绝对是自杀(也是无意义的),就像这样-

提高矩阵幂的主要方法之一是首先执行特征分解,然后简单地提高其特征值的幂。但是如果你看上面等式的左手边,你会意识到这些就是我们要找的特征向量!避免这种令人困惑的场景的一个聪明的方法是首先采用随机投影,然后交替地用 S* 和 S 乘以它。

随机化 SVD 最美妙的事情之一是存在逼近误差的闭合形式下界,它是目标秩 r 和幂迭代次数 i. 的函数。因此,您可以轻松地在逼近性能和计算要求之间进行权衡。关于这个话题的更多内容可以在这篇惊人的论文中找到。

示例

在该图中,我们可以看到,对于给定数量的数据点,随着测量值(或特征)数量的增加,随机化 SVD 可以逼近我们的数据矩阵。这里,我们将数据点的数量固定为 3000,测量的数量从 9000 到 24000 不等。我们还将目标等级设置为测量数量的 10%。而且,在这个例子中, i 是幂迭代的次数。显然,随机化 SVD 几乎可以达到传统确定性 SVD 的低秩逼近性能。

接下来,我们有上面图中显示的算法运行时。仅使用 3 次幂迭代,我们就可以使用确定性 SVD 所需计算资源的一半来获得数据矩阵的真正良好的近似!

我在 github 中创建了一个 gist 来分享这个例子中使用的可复制代码。

SVD 无疑是使用最广泛的分解之一。虽然它不早于主成分分析(PCA),但它实际上推广了它。在本文中,我们看到了如何将 SVD 和随机化结合起来,以及它如何导致 SVD 的超快速计算,而不是炸毁您的计算机。享受随机和实验目标等级的乐趣吧!

深度范围

原文:https://towardsdatascience.com/range-over-depth-the-value-of-a-generalist-in-your-data-team-174d4650869d?source=collection_archive---------34-----------------------

通才在您的数据团队中的价值

帕斯卡尔·斯威尔在 Unsplash 上的照片

不要被一个充满专家的团队抓住,当事情不如预期时感到惊讶。

人们不能毫无保留地简单宣布这一点。让我们看看我是如何得出这个结论的…

在谷歌上搜索这些术语,通才和专家,会导致相互矛盾的观点,所以为了讨论起见,谨慎的做法是给出我的定义。

数据通才是数据专业人士,他们的工作范围涵盖任何数据管道的不同方面。从收购到 AI。他们注重广度。典型的技能包括对数据集(跨行业)的特别分析,以回答一些业务问题。他们确实主动报告——“我们有这个问题——发生了什么?”。他们在预测建模和识别趋势方面也有一定程度的能力。他们可以用前端工具一起破解一个可视化工具,但也可以在整个数据生命周期中发现问题。例如,在数据质量、数据仓库技术或性能下降方面,我每天都会遇到一些重复的问题。听起来像独角兽?不尽然——他们可能在这些领域都没有足够的深度,但是他们有足够的广度。我认为这些人可能有很多名字或头衔,但关键是他们学习速度很快,不会特别局限于某个特定的领域。他们可以快速解读数据并讲述故事。他们是有大局观的人。

专业的数据专家,相反,他们对这些领域中的一个或几个领域有深入的了解。当问题已经被很好地定义时,他们通常表现得最佳,但是当问题仍然需要在更大的画面中被发现时,他们就会挣扎。

当然,我强调的是技能或角色,而不是头衔。你可以找到我认为的拥有数据工程师、数据分析师、业务分析师、数据建模师或数据科学家头衔的通才。基本上任何数据专业人士。

现在,让我支持我的主张。

1 —接受我们在恶劣的学习环境中工作

在书 Range 中,大卫·爱泼斯坦详细阐述了坏的与好的学习环境的概念,但是我将不得不给你速成版的,为了在这句话之后不失去你。

下表给出了这两种对立的学习环境的含义。

当我第一次读到这个定义时,我把它和简单的问题和困难的问题混淆了,并做出了反应:“你怎么敢说国际象棋是一个简单的问题!”(我听说你们这些最近的象棋爱好者都是看了《女王的策略》之后才开始下象棋的)。然而,它让我明白了一件困扰我很久的事情。为什么世界上有这么多天才,我们却不能解决最紧迫的问题?

我现在的结论,简单来说,就是因为我们的世界不是一个善良的学习环境。生活比我们能控制的闭环更复杂。我们首先面临的挑战是定义问题,更不用说解决问题了。我们可以共同解决难以定义的问题,但在一个黑天鹅往往会随机出现的环境中,解决一个没有很好定义的问题要困难得多。我们真的只能选择更好的而不是更差的——让明天比昨天好一点的渐进变化。

现在想象一下你的工作环境。你们使用的技术,现有的平台,新的平台,你们无畏的领导者想要进入的雄心勃勃的新方向。我是从数据专业人员的角度来写这篇文章的,但是这个概念肯定适用于更广泛的专业社区。

在定义的数据集上创建机器学习算法可能是 ,但

从源中获取数据,可能是 容易

构建一个数据管道,组合不同的数据集,并在粒度和语义上一致,开始变得,并潜在地变得不那么。**

看到我是如何用几个大词来说服你我的结案陈词了吗?

现在再加上时间、人们的个性、自我、职业道路、组织的复杂性,你肯定会看到一个糟糕的学习环境。

#2 —由于信息如此容易获得,对高度专业化的需求减少了。

注意,需求减少了,而不是消除了。有了现成的信息,通才就可以为了分析而拼凑一些东西。有了像 stackoverflow 这样的网站,如果你精通谷歌搜索的艺术,任何人以前尝试过的东西通常不到 10 分钟就能找到。

#3 —协调努力是真正的杀手

协调工作是指协调任何工作产品交付的工作。有些人可能知道它的街名——“赶猫”。

杰夫·贝索斯为他的理想规模团队创造了著名的“两个披萨团队”规则。随着团队规模的扩大,需要维护的关系开始变得重要。理查德·哈克曼有很好的关于团队和团队绩效的文献,详细介绍了团队动力以及为什么应该避免大型团队。⁴

努力与完成任何事情需要维持的关系数量有关。作为人数(n)的函数,关系的数量增加 n(n-1)/2。

简单的数学方法是,你有越多的节点(即你需要咨询的人)来做决定,你的协调努力就会增加,如下图所示:

按作者分类的图表

  • 4 人左右的小团队只需要维护 6 种关系
  • 一个更大的组织程序,比如说 40 个人,将有 780 个链接需要管理

当然,并不是每个人都需要和其他人互动,但是增加的程度是一样的。我还认为 n 可能不仅代表人数,还代表抽象节点,如论坛,即有权做出决策的人群。当一些人需要在团队中与一对一的情况下做决定时,他们也会有不同的反应。所以曲线图的曲线部分可能比图示的更陡。因此,如果您在各种大型决策论坛上有大型团队,您的协调工作将随之而来。

团队内专家和跨团队通才的绝对必要数量将显著降低这种复杂性。

#4 —企业不想要花哨的算法,他们希望自己的问题得到解决

我们可能处于人工智能能力增强的时代,但重要的是要注意,问题陈述并没有改变太多。我如何增加收入?我如何向现有客户追加销售,我如何…尝试和解决这些问题的方法已经变得更加复杂,但是核心问题陈述仍然存在。

基于坏数据的算法不会解决任何问题。我已经看到对数据科学家和人工智能专家的需求大幅增加,但我担心,如果业务问题得不到解决,这种热情将会落空。很快,高管们将开始透过表象看到真相。

那么我为什么提倡数据通才呢?

  • 恶劣的学习环境中,他们会定义问题的范围(也就是找出真正的问题是什么)并在友好的环境中将其交给专家,这样他们就可以更快更有效地解决问题。太多的工作时间花在解决错误的问题上。多面手能更快地找到正确的问题,如果有困难,专家会解决。多面手倾向于从相似的情况和方法中获得更多的类比,以帮助更快地解决问题,这在恶劣的学习环境中是关键。****
  • 随着大量信息的出现,外行人更容易找到一个新问题的足够背景。通才无需花费太多时间深入研究就能迅速拓宽视野,这是的促成因素。****
  • 通才通过从本质上消除不必要的关系来减少协调工作,因为它们跨越了所有的关系。他们需要被授权做决定,从而减少对附加关系的管理。

你在一艘船上,当你的引擎发生故障时,你几乎回到岸上,你想,我希望我有一个船机械师在附近,但实际上你不需要机械师,你只需要有人告诉你,湖是 3 英尺深,所以你可以爬出来,推最后一段。

如果我们继续不解决或过度解决业务问题,我们将被更高效的问题解决者所取代。简单的解决方案,而不是背离最终目标的庞然大物。在瞬息万变的环境中,我们需要更快地确定问题,更快地决定前进的方向。我们需要大局通才。

**1\. Epstein D. *RANGE Why Generalists Triumph In A Specialized World*. 1st ed. MACMILLAN; 2020.
2\. Two-Pizza Teams: The Science Behind Jeff Bezos' Rule | Inside Nuclino. Blog.nuclino.com. https://blog.nuclino.com/two-pizza-teams-the-science-behind-jeff-bezos-rule. Published 2019.
3\. J. Richard Hackman. *Leading Teams: Setting The Stage For Great Performances*. Harvard Business Review; 2002.
4\. Coutu D. Why Teams Don’t Work. Harvard Business Review. https://hbr.org/2009/05/why-teams-dont-work. Published 2009\.** 

用 OpenStreetMap 对欧洲的可循环性进行排名

原文:https://towardsdatascience.com/ranking-cyclability-in-europe-with-openstreetmap-198d3b17463c?source=collection_archive---------19-----------------------

斯德哥尔摩的尖峰时刻, 杰玛·埃文斯,昂斯佩什

我使用 OpenStreetMap 的众包数据对欧洲城市的自行车基础设施进行了衡量和排名。这些城市要么人口超过 40 万,要么是欧盟的首都。

源代码

动机

今年,我一直在坚持一项个人挑战,将自行车作为我在市内的主要交通方式。这与整个欧洲自行车运动的大规模复苏相吻合,因为像巴黎或柏林这样的城市在禁行后已经进行了大量投资来提高自行车的模式份额。甚至在我的家乡塔林市,据说只有 1%的出行是骑自行车的,但这却成了今年地方选举的主要话题。正如在任何政治化的辩论中,都有从事实回归到合适的叙述的趋势,我开始想知道是否有一种基于数据的客观观点,可以帮助评估和排列不同城市在使其环境更适合自行车方面的努力。毕竟,在这方面,每个城市都可能向哥本哈根或阿姆斯特丹看齐,但其他城市做得如何呢?世界上最大的开源地理数据库 OpenStreetMap 可能会提供一些答案。

类似项目

我可以找到一些类似的排名。例如,哥本哈根指数对街景、文化和抱负等多个领域进行主观评分。城市列表包括全球 600 个城市,拥有 60 万居民。另一个是数字保险公司 Coya 的自行车城市指数。城市列表的选择是任意的,但测量是根据互联网上的一些客观指标进行的,如道路基础设施、自行车使用率、死亡人数等。

开发另一个的原因是:

  • 哥本哈根指数和自行车城市指数上次更新是在 2019 年
  • 我们希望公平对待城市选择(基于人口规模)和指标测量,避免主观的专家打分或从不同来源手动查找数据
  • 使用基于 OpenStreetMap 的标准化方法将允许在不同的时间重复实验,并将其扩展到任意数量的城市,几乎不需要额外的人工努力

方法学

为了衡量城市的可循环性,我计算了在 OpenStreetMap (OSM)上明确标记为自行车友好的道路基础设施(自行车道或自行车道)的比例。考虑整个通航长度,因此单行道的长度是双行道的一半。OSM 被认为是一个相当可靠的数据来源,无论是 T2 总体数据还是 T4 自行车数据。

测量自行车道长度背后的动机有两个:首先,已经证明与自行车运动的普及程度相关;其次,专用自行车道的存在与安全性的提高相关,这将进一步增强自行车运动的普及程度。虽然仅仅建造许多自行车道还不足以促进向广泛自行车运动的过渡,但这可能是一个必要条件。

要成为自行车道,开放街道地图道路必须:

  • 有自行车专用车道或公共汽车共用车道(算作车道)
  • 有明确允许骑自行车的人行道(算作车道)
  • 为骑自行车的人开辟一条单独的道路,一条自行车道(主要是比利时/荷兰)或一条自行车道(主要是德国)
  • 是由标志指定给骑车人的道路或人行道

值得注意的是,这种自行车道的定义比 OSRM T2 或谷歌地图等路由器要严格得多。这是因为,为了实现高连通性,并实际计算任意点 A 和 B 之间的合理路线,路由器通常会穿过普通的、潜在不安全的街道。事实上,本研究中考虑的道路网络部分仅占 OSRM 所有可循环道路的大约 10%。

警告

由于自行车道和自行车道(包括隔离车道)之间的分离是我们从 OpenStreetMap 数据中做出的唯一区分,因此不可能量化这些道路的所有细微差别。例如,自行车道可以设计得很好:附近有更窄的车道,使用限速或其他减速方法,禁止在车道上停车等。但也可能只是人行道上的一些红色油漆碰到了电线杆和公交车站。即使这些细粒度的细节中的一些可以从 OSM 捕获,也很难说它们会在整个欧洲以类似的质量被记录下来。此外,OSM 数据模型中未包含的文化因素可能会影响道路网络和骑行便利性。

空间加权

上述方法的一个可能的警告是城市的拓扑结构。从理论上讲,在空间充裕的城市郊区可能会有许多休闲自行车道,但在市中心却没有。这不利于将自行车作为一种可行的交通方式。为此,我将指数衰减权重应用于道路长度,其中权重是道路与市中心距离的函数。更准确地说,加权公式是:

exp(decay_coef * min(max(distance_from_centre - t_min), t_max))

其中,t_min =距中心道路距离的第 10 个百分点,t_max = max(距中心距离的第 90 个百分点,15km)。我校准了衰减系数,因此上述公式在 t_max 时等于 0.1,在 t_min 时等于 1.0。这保证了两件事:

  • 市中心的道路的权重是距离市中心第 90 百分位的道路的 10 倍。
  • 如果城市多边形非常大,在 15 公里处审查 t_max 可以确保我们不会在远处的道路上增加重量。

下面以塔林为例,展示了一些带权重的自行车道示例,其中半径为 8km(第 90 个百分位数)的质心周围的圆表示一条道路的权重比市中心的一条道路小 10 倍的距离。

作者提供的塔林道路权重示例

其他指标

虽然主要排名是基于上述内容,但我也计算了一些辅助指标:

  • 隔离自行车道的份额(所有自行车道的子集)。这应该是自行车道的黄金标准。
  • 自行车道的份额(相对于赛道)。
  • 每平方公里停车点的数量。

虽然这些出现在完整结果中,但由于两个原因,它们被排除在排名计算之外:

  • 在如何构建 bike infra 并将其记录到 OpenStreetMap 之间存在一些区域性的特质。虽然整体可导航自行车道份额似乎是一个相当普遍的指标,但其他指标产生的城市排名并不直观。
  • 我没有资格衡量不同类型的道路——自行车道和隔离车道的价值是什么?

使用的软件

底图文件下载于 2021 年 9 月 19 日至 10 月 5 日之间。

结果

哥本哈根的邻居马尔默这次拔得头筹,整个可通航道路网络的 17.3%(加权 16.2%)明确用于自行车。名单上的大多数其他城市——来自荷兰、比利时和斯堪的纳维亚——也不令人惊讶。我对德国的自行车运动并不熟悉,但是汉诺威的这个消息来源声称汉诺威、不来梅和慕尼黑是自行车运动模式比例最高的城市,它们都名列前茅。在较大的城市中,上面已经提到了巴黎。因此,这个排名似乎抓住了我们所期望的。

30 强

按作者排名的前 30 个城市

完整的结果可以在这里找到,包括辅助指标。

讨论

按城市、作者划分的自行车道份额

这里点的大小与自行车道的比例成正比。地图显示,至少恶劣天气似乎与骑行基础设施几乎没有(甚至是反向)关联:)

对于与人口规模的关系,可以提出一个更有说服力的理由。事实上,大多数顶级自行车城市似乎都是较小的城市。使用来自维基百科的欧洲城市的人口,并对照我们的指标绘图,显示出超过 12%(加权)份额的最大城市的人口只有 13.8 = 100 万。然而,在整个数据集上,人口和自行车道份额之间似乎没有关系(虚线是最佳线性拟合)。即使有一些自然比例因素降低了少数几个非常大的城市的自行车道比例,但显然有许多自行车道很少的较小城市仍然可以更好地实现其潜力。

按作者分列的人口与自行车道份额

为了查看排名是否抓住了预期的概念,在地图上可视化自行车基础设施并将其与我们的定量测量进行比较是非常有用的。我用的是 CyclOSM ,这似乎是最好的工具。

哥本哈根 vs 索非亚

首先,将排名靠前的城市与排名靠后的城市(在 CyclOSM 地图中,蓝色越多越好)进行比较可以看出,基础设施的总体水平确实得到了体现:

哥本哈根,作者截图

作者 Sofia,CyclOSM 截图

南特和里昂 vs 巴塞罗那

南特是一个有趣的例外——它整体排名第一,但其中近一半来自自行车道。事实上,它明显有占主导地位的自行车道(用虚线标出),而不是在 CyclOSM 中也有单独的自行车道。

南特,CyclOSM 作者截图

里昂似乎也采用了同样的城市规划模式。

里昂,CyclOSM 作者截图

与南特形成鲜明对比的是巴塞罗那——任何骑自行车去那里的人都知道他们指定的自行车道的便利,这些自行车道与汽车交通几乎没有重叠。虽然巴塞罗那在总排名中表现不太好,但以隔离自行车道衡量,它名列前茅。

巴塞罗那,CyclOSM 作者截图

米兰 vs 萨拉戈萨

最后,为了形象化道路空间加权的好处,我们可以看看如果我们不进行任何加权,在排名中获得或失去最多的城市的例子。例如,米兰将获得 29 个名次(从 86 位上升到 57 位),这显然是因为米兰市中心几乎没有自行车道。

米兰,CyclOSM 作者截图

与此同时,萨拉戈萨只有在正中心有自行车道,如果没有加权,它将失去 35 个位置(从 57 位到 92 位)。

萨拉戈萨,作者截图

原载于 2021 年 11 月 6 日【https://mlumiste.com】

推荐系统的排序评价指标

原文:https://towardsdatascience.com/ranking-evaluation-metrics-for-recommender-systems-263d0a66ef54?source=collection_archive---------4-----------------------

各种评估度量用于评估推荐器的有效性。我们将主要关注排名相关的指标,包括 HR(命中率)、MRR(平均倒数排名)、MAP(平均精度)、NDCG(归一化折扣累积收益)。

推荐系统最终输出项目的排序列表,而不管不同的建模选择。因此,重要的是研究如何直接评估排名质量,而不是像均方差等其他代理指标。

命中率

在推荐器设置中,命中率只是正确答案包含在长度为 L. 的推荐列表中的用户比例

可以看出, L 越大,命中率就越高,因为正确答案被包括在推荐列表中的可能性就越大。因此,为 L. 选择一个合理的值很重要

MRR(平均倒数排名)

MRR 是的缩写,意思是倒数排名。又称平均互反命中率(ARHR)。

注意,计算 RR(u) 有不同的变化或简化。对于隐式数据集,对于未购买或已购买(未点击或已点击等)的项目,相关性得分为 0 或 1。).

另一种简化是只看推荐列表中一个最相关的项,而不是对所有的进行汇总。在隐式数据集的情况下,相关性本身没有排序,只考虑列表顶部的任何一个相关项目就足够了。

有人可能会认为命中率实际上是 MRR 的一个特例,当 RR(u) 是二进制时,因为如果列表中有相关项,它就变成 1,否则就变成 0。

平均精度

让我们首先在精确度和召回率上刷新我们的记忆,特别是在信息检索方面。

什么是精准和召回?

简而言之,精度是所有被检索项目中相关项目的分数。它用于回答所有推荐中有多少项是正确的。

而回忆是相关条目在所有相关条目中所占的分数。这是为了回答覆盖率问题,在所有那些被认为相关的项目中,有多少是在推荐中捕获的。

精度来自维基百科

回忆来自维基百科

什么是 precision@k?

在此基础上,我们同样可以定义precision @ krecall@k 。Precision@k 是前 k 个推荐中相关项目的分数,recall@k 是前 k 个推荐中相关时间的覆盖范围

什么是平均精度?

现在回到地图。

MAP 是平均精度的平均值。 如果我们有每个用户的 AP,只需对所有用户进行平均就可以计算出 MAP。

通过计算文档排序序列中每个位置的精度和召回率,可以绘制精度-召回率曲线,绘制精度 p(r) 作为召回率 r 的函数。平均精度计算从 0 到 1 的区间内 p(r) 的平均值。

这是精确回忆曲线下的重要区域。以离散的方式,它可以计算如下

我们最终可以计算 MAP,它只是 AP 对所有用户的平均值。

NDCG (N 归一化贴现累计收益

NDCG 代表归一化贴现累计收益。我们将通过逆向回答以下问题来建立这个概念:

  • 什么是增益?
  • 什么是累积收益?
  • 怎么打折?
  • 如何正常化?

条目的增益本质上与相关性分数相同,相关性分数可以是数字评级,如谷歌中的搜索结果,可以从 1 到 5 进行评级,或者在隐式数据的情况下是二元的,其中我们只知道用户是否消费了某个条目。

自然地,累积增益被定义为直到推荐列表中位置 k 的增益总和

CG 的一个明显缺点是它没有考虑到排序。通过交换任意两个项目的相对顺序,CG 将不受影响。当排列顺序很重要时,这是有问题的。例如,在谷歌搜索结果上,你显然不喜欢把最相关的网页放在底部。

为了惩罚放置在底部的高度相关的项目,我们引入了 DCG

通过按等级划分收益,我们可以推动算法将高度相关的项目放在最上面,以获得最好的 DCG 分数。

DCG 分数仍然有一个缺点。DCG 分数与推荐表长度之和。因此,我们不能一致地比较推荐前 5 和前 10 项的系统的 DCG 分数,因为后者将具有更高的分数,不是因为其推荐质量,而是纯粹的长度。

我们通过引入 IDCG(理想 DCG)来解决这个问题。 IDCG 是最理想排名的 DCG 分数,即根据项目的相关性自上而下排列,直到位置 k

而 NDCG 只是通过 IDCG 对 DCG 分数进行归一化,使得它的值总是在 0 和 1 之间,而不管长度如何。

笔记

  1. 维基百科上的 https://en.wikipedia.org/wiki/Discounted_cumulative_gain 很不错:
  2. 维基百科有一个很好的信息检索评估指标列表:【https://en.wikipedia.org/w/index.php? title =信息检索&oldid = 793358396 #平均精度
  3. 这篇文章在红外和目标检测两方面都很好的解释了 MAP:https://towards data science . com/breaking-down-mean-average-precision-MAP-AE 462 f 623 a 52

快速原型人工智能应用

原文:https://towardsdatascience.com/rapid-prototyping-artificial-intelligence-applications-899199f3028f?source=collection_archive---------39-----------------------

我如何使用快速原型法在 10 天内构建 5 个人工智能应用程序

照片由瑞德·卫斯理Unsplash

2019 年 2 月 11 日,白宫发布行政命令,启动“美国人工智能倡议”,这一倡议随后于 2019 年 3 月 20 日发布了www.AI.gov。美国人工智能倡议鼓励联邦机构探索和发展人工智能能力。美国不是唯一一个探索人工智能技术的国家。根据中国国务院的数据,到 2030 年,中国的人工智能产业可能价值 1500 亿美元。(拉森,2018 年)

发展人工智能技术

开发人工智能技术可能是一项具有挑战性的工作。有效的人工智能解决方案是劳动力、内部治理和技术创新的结合。建立人工智能劳动力可能非常昂贵,因为仍然没有足够的数据科学家、数据工程师和分析师来填补职位。重组政府机构的遗留数据架构以有效支持机器学习模型开发可能需要很长时间,并且需要大量的前期投资。领导者可能不得不将其数据从本地服务器转移到基于云或混合的解决方案,这些解决方案可以由 Hadoop 集群高效地提供服务。还有一个挑战是让当前的员工跟上时代,以便他们能够充分利用新的人工智能技术。将有大量的劳动力培训投资,以使行政领导、经理和产品开发人员知道何时开始开发和部署机器学习模型,以最大化业务驱动力,节省运营成本,并开发新产品。开发一个强大的人工智能技术组织不仅仅是购买一个平台并让它工作。这将是投资、内部业务流程和组织战略的转变。

敏捷人工智能解决方案

做出任何重大改变都需要时间。通过尽早取得胜利,这一过程会变得更加顺利。有许多廉价或免费的人工智能工具,如 bot builders、预训练的机器学习模型和 AutoML 解决方案,可用于快速、廉价地构建人工智能应用原型和生产级解决方案。联邦机构 IT 解决方案的实施因过于昂贵和耗时而臭名昭著。在当今世界,时间是一种奢侈品,不能想当然。政府创新者和技术专家必须开发新的方法和流程,以快速高效地实施人工智能和机器学习技术解决方案。这是政府机构在人工智能领域保持竞争力,并恰当地为美国公民服务的唯一途径。

我的个人政府人工智能应用挑战

作为一名自豪的老兵,我热爱为我的国家服务。虽然我已经远离了在军队的日子,但我仍然强烈地需要为我的国家服务。我觉得我仍然可以利用我的技术知识和技能为国家服务。我正在挑战自己,用 Google Cloud Dialogflow 和 AutoML 在 10 天内创建 5 个政府 AI 应用程序原型。我这样做是为了说明创建原型应用程序并不需要很长的时间或昂贵的采购过程。这些应用将基于公开信息。我将展示政府机构如何使用人工智能来帮助美国公民更容易地浏览他们的政策、常见问题和其他数字知识。

人工智能 App 1:

智能兽医应用

描述:

该应用程序将帮助退伍军人快速获取有关退伍军人医院、项目和提交残疾索赔的信息。我用 Dialogflow 的多模态功能构建了这个应用程序。这款应用可以在智能显示器和手机上使用。

来源:作者视频

人工智能 App 2:

汽车座椅检测器

描述:

这个应用程序将帮助父母确保他们已经将孩子正确地放在汽车座椅上。父母将能够通过 Twilio 拍摄孩子的照片,并将其发送到一个应用程序,该应用程序将告诉父母孩子是否被正确放置在汽车座椅上。我用 Google Cloud AutoML 建立了一个机器学习模型,并对汽车座椅上儿童的不正确和正确图像进行了训练。

来源:作者视频

人工智能 App 3:

退伍军人医院应用导航器

描述:

该应用程序将帮助退伍军人在预约前阶段、预约阶段和预约后阶段导航退伍军人医院。我用 Dialogflow 的 Twillio 集成构建了这个应用程序,它将只基于内容丰富的文本。

来源:作者视频

人工智能 App 4:

GSA 常见问题应用程序

描述:

该应用程序将在谷歌助理设备上运行,并将帮助公民、合作伙伴和供应商浏览由美国总务署维护的超过 87,000 个页面。我用 Dialogflow 构建了这个原型。

来源:作者视频

人工智能 App 5:

退伍军人机会应用程序

描述:

这个应用程序将帮助新过渡的退伍军人找到退伍后就业的机会。任何兽医都可以通过电话号码呼叫人工智能,以确保即使是非技术兽医也可以访问这一资源。我用 Dialogflow 的电话集成构建了这个原型应用程序。

来源:作者视频

结束语

在政府机构中开发和部署人工智能解决方案在技术上极具挑战性,而且成本高昂。然而,可以通过快速原型化 AI 产品和服务来实现买入、支持和产品方向。我高度鼓励所有政府创新者探索这些技术,将人工智能引入他们的机构。

5 款 AI 政务 app 各一篇,链接如下:

智能兽医 App

汽车座椅检查器 App

退伍军人医院助理

GSA 常见问题 App

老兵机遇 App

来源:

(1)克里斯蒂娜·拉森(2018 年 02 月 08 日)。中国对人工智能的大量投资有一个阴险的负面影响。检索于 2021 年 1 月 10 日,来自https://www . science mag . org/news/2018/02/China-s-massive-investment-artificial-intelligence-has-invisible-down

谷歌云平台中的大规模快速变压器推理

原文:https://towardsdatascience.com/rapid-transformer-inference-at-scale-in-the-google-cloud-platform-95f39fa332c6?source=collection_archive---------21-----------------------

使用转换器以最少的基础设施设置对数百万个字符串进行并行推理

照片由 Guillaume JailletUnsplash

变形金刚席卷了 NLP 这个词。每一个与文本相关的任务都受益于变形金刚。使用变形金刚最好的库是 HuggingFace 库,而开始使用 HuggingFace 最简单的方法是通过简单变形金刚。网上有无数的教程会教你如何为分类、翻译、命名实体识别等微调 transformer 模型。

最近,在我的雇主 Truebill 主办的内部黑客马拉松上,我不得不使用简单的转换器对数百万个字符串进行快速推理。事实证明,这个过程并不完全直观,所以这篇文章描述了如何使用谷歌云平台,使用简单的变压器(只需进行最小的调整,就可以与拥抱脸一起工作)快速建立大规模的推理。它假设您已经有了一个模型,现在正在寻找一种方法来快速大规模使用它。它还假设您拥有一个 GCP 帐户,并对该平台有一些基本的了解。这可能不是在生产中运行东西的最便宜或最有效的方式,但是如果您需要在紧要关头处理一批数百万个字符串,本指南将帮助您以最少的设置时间完成这项工作。

资源供应

首先,您需要提供一些资源:一个笔记本实例来控制管道集群,一个集群来完成繁重的工作。两人都住在 GCP 的人工智能平台区。

首先创建一个笔记本实例。如果您不使用它来训练模型,类型并不重要,但如果您要在笔记本实例中训练模型,您将需要带有 GPU 的 PyTorch one。如果您选择 GPU 选项,请确保选中安装 NVIDIA GPU 驱动程序框。

然后,调配群集。进入 AI 平台->管道,点击新建实例。然后单击 Configure,并为管道创建一个新的集群。

选中允许访问云 API 复选框!

作者图片

单击 create cluster 按钮,完成管道创建过程,然后转到 Kubernetes Engine->Clusters。选择新创建的集群,然后单击顶部的 Add Node Pool 按钮。选中启用自动缩放按钮,以便机器在不使用时关闭。将最小节点数设置为 0,将最大节点数设置为您需要同时运行的机器数。如果您有一个模型推理需要多长时间的基准,这真的很有帮助——将帮助您分配正确数量的节点,以便在您拥有的时间内完成工作。

请注意,GCP 有一个默认限制,允许在单个区域中使用 8 个单一类型的 GPU,因此如果您需要使用 8 个以上的 GPU,您必须选择多个区域或请求为您的项目提高该限制。请注意,并非所有地区都有所有类型的 GPU,因此请查看哪个区域有您需要的类型此处

以下是截至 2021 年 1 月美国中央区域提供的 GPU。作者图片

打开左侧菜单中的“节点”选项卡。您将需要 N1 节点(它们支持 GPU)。选择你的项目需要的 CPU/内存,然后点击“CPU 平台和 GPU”按钮,选择你需要的 GPU。选中“我了解限制”复选框。

您很可能还需要选中“启用可抢占节点”复选框。它极大地降低了计算成本,但不能保证正常运行时间,并且在每个节点上设置了 24 小时的限制。节点设置如下所示:

作者图片

之后,打开左边的安全选项卡,选择允许完全访问所有云 API(除非你想手动管理访问,这总是很痛苦)。创建节点池。

回到你的集群。如果它有如下图所示的可用升级选项,请单击它,并更新到最新选项。集群完成升级后,转到“节点”选项卡,升级节点。如果你的 GPU 节点版本低于 1.18,变形金刚就不会在上面工作,因为驱动不兼容

更新完节点后,单击集群页面顶部的 connect 按钮。在云壳中点击运行,在壳加载后按回车键运行连接命令。根据需要授权。运行以下命令在您的节点上安装 NVIDIA 驱动程序:

kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/master/nvidia-driver-installer/cos/daemonset-preloaded.yaml

瞧啊。您已经完成了资源调配。现在到了有趣的部分。

设置 Docker

去 AI 平台-> GCP 管道。打开管线的管线操控板。复制它带你到的网址的开头,以*googleusercontent.com/结尾

现在转到您在最开始创建的 Notebook 实例,并打开 JupyterLab。粘贴您刚才复制的 URL,并将其分配给一个变量。然后将 GCP 项目的 ID 赋给另一个变量,并导入一些与管道相关的东西。您的第一个单元格将如下所示:

PROJECT_ID=’YOUR-GCP-PROJECT-ID'
HOST = ‘[https://YOUR-PIPELINES-URL-LOOKS-LIKE.pipelines.googleusercontent.com/](https://1be8db11da5d2e5d-dot-us-central2.pipelines.googleusercontent.com/)'import kfp.gcp as gcp
import kfp.dsl as dsl
import kfp.compiler as compiler
import kfp.components as comp
import kubernetes as k8s
import kfpclient = kfp.Client(host=HOST)
client.list_pipelines()

最后两行不是必需的,但是运行它们会检查您的笔记本是否能够连接到您的集群。如果成功的话,你会得到一堆关于集群的信息。如果没有,可能是某个地方缺少权限。

运行手机。

现在,您需要在笔记本电脑中训练您的模型,或者将您现有的模型上传到笔记本电脑。将其放置在管道文件夹内的文件夹中。

记住模型文件夹的名称。作者图片

利用这一时刻检查您的 model_args.json(右键单击,用编辑器打开)。确保使用 _ 多重处理使用 _ 多重处理 _ 解码都设置为假。PyTorch 使用了大量的共享内存,这与 Kubeflow 管道不兼容。可能有一种方法可以在集群中使用多处理,但是我还没有找到(如果你找到了,请在评论中发布!)确保 n_gpu 匹配你为你创建的节点选择的任何东西。

下一个单元格为您的节点创建一个 Docker 文件。它看起来是这样的(下面的例子有导入来处理 BQ 和 GCS 桶中托管的数据):

%%bash
mkdir -p pipeline
cat > ./pipeline/Dockerfile <<EOF
FROM nvidia/cuda:10.2-runtime-ubuntu18.04
RUN apt-get update && apt-get install -y \
python3 \
python3-pip
RUN pip3 install --upgrade pip
RUN pip3 install numpy
RUN pip3 install pandas
RUN pip3 install torch==1.6.0 torchvision==0.7.0
RUN pip3 install simpletransformers
RUN pip3 install pandas-gbq
RUN pip3 install google
RUN pip3 install google-cloud
RUN pip3 install google-cloud-bigquery
RUN pip3 install google-cloud-storage
RUN pip3 install gcsfs
WORKDIR /app
COPY . /app
EOF

随意改变一些东西(特别是——确保你有你的模型需要的所有 python 库)。但是,您可能希望保持相同的基础映像。NVIDIA 的设置是与 Kubeflow 管道一起工作,如果你选择不同的图像,你的设置可能会变得更加复杂。运行这个单元。

您的下一个单元使用 Google Cloud Build 来构建您的 Docker 映像。这里您可能想要更改的唯一内容是图像名称(它不需要匹配任何内容),以及 ' — timeout' 之后的数值。它设置了云构建需要多长时间来构建您的映像的限制;如果你的图像非常复杂,可能需要的时间会超过这里指定的 20 分钟。

IMAGE_NAME=”YOUR_IMAGE_NAME”
TAG=”latest”GCR_IMAGE=”[gcr.io/{PROJECT_ID}/{IMAGE_NAME}:{TAG}](http://gcr.io/%7BPROJECT_ID%7D/%7BIMAGE_NAME%7D:%7BTAG%7D)”.format(
 PROJECT_ID=PROJECT_ID,
 IMAGE_NAME=IMAGE_NAME,
 TAG=TAG
)APP_FOLDER=’./pipeline/’import subprocess
# ! gcloud builds submit — tag ${IMAGE_NAME} ${APP_FOLDER}
cmd = [‘gcloud’, ‘builds’, ‘submit’, ‘ — tag’, GCR_IMAGE, APP_FOLDER, ‘ — timeout’, ‘1200’]
build_log = (subprocess.run(cmd, stdout=subprocess.PIPE).stdout[:-1].decode(‘utf-8’))
print(build_log)

运行手机。这可能需要一段时间,这个过程应该会产生一个很长的输出,并以成功结束。

创建和运行管道

我们现在到了有趣的部分。下一个单元将具有每个节点实际运行的功能。该功能有 4 个部分:

  1. 进口
  2. 读取数据
  3. 运行推理
  4. 存储数据

确切的代码将取决于您的数据存储在哪里,您的模型是什么,您是否需要做任何预处理,等等。这是我的最后的样子:

def predict(file_number,model_folder='YOUR_MODEL_FOLDER'):
  #Imports
  import pandas as pd
  import re
  from simpletransformers.t5 import T5Model
  #Read Data
  GCS_BUCKET='gs://MY-BUCKET'
  file='{}/input/batch_{}.csv'.format(GCS_BUCKET,file_number)
  df=pd.read_csv(file)
  #Run Inference
  model = T5Model("t5",model_folder)
  predictions = model.predict(df["text"].tolist())
  df['output']=pd.Series(predictions)
  #Filter out special characters (application logic)
  df['output'] = df['output'].apply(lambda x:re.sub("[^A-Za-z0–9 '-]+",'', str(x)))
  #Store the results
  df.to_csv('{}/output/batch_{}.csv'.format(GCS_BUCKET,file_number))#This line is not a part of the function and is needed for the pipeline.
predict_container=comp.func_to_container_op(func=predict, base_image=GCR_IMAGE)

你的会有所不同,所以要记住几件事。首先, model_folder 应该匹配您的模型在笔记本实例中所在的文件夹的名称(只有模型文件夹名称,没有“pipeline/”)。其次,应该有一些参数来定义要处理的数据块。在我的例子中,它是 file_number ,因为输入数据被分成许多文件。你的可能是 BQ 查询中的一个参数,或者其他什么。

正如你在上面看到的,谷歌云存储得到了很好的支持——你不需要做任何认证。大查询也是如此: pd.read_gbq 将开箱即用(如果您使用它,我强烈推荐将 use_bqstorage_api 设置为 True ,这样会快得多)。

您的模型代码最终会有所不同,这取决于您所使用的转换器,您可能也有自己的定制应用程序逻辑。不要忘记存储结果!单元格的最后一行将您的函数添加为管道中的一个步骤。实现完逻辑后,运行单元。

下一个单元格定义了管道。我的看起来像这样:

[@dsl](http://twitter.com/dsl).pipeline(
    name='My Pipeline',
    description='What it does'
)
def my_pipeline():  
  predict_memory = '12G'
  for i in range(0,10):

    predict_task1 = predict_container(file_number="00"+str(i))\
    .set_memory_request(predict_memory).set_gpu_limit(1).apply(gcp.use_preemptible_nodepool())

    predict_task2 = predict_container(file_number="01"+str(i))\
    .set_memory_request(predict_memory).set_gpu_limit(1).apply(gcp.use_preemptible_nodepool())
    predict_task2.after(predict_task1)

    predict_task3 = predict_container(file_number="02"+str(i))\
    .set_memory_request(predict_memory).set_gpu_limit(1).apply(gcp.use_preemptible_nodepool())
    predict_task3.after(predict_task2)my_pipeline_name = my_pipeline.__name__ + '.zip'
compiler.Compiler().compile(my_pipeline, my_pipeline_name)

注意一些事情。有一个内存请求—它应该略低于您之前选择的节点内存。有一个 GPU 限制,它应该等于你在一个节点中使用的 GPU 数量。。如果您在集群设置中选择了可抢占的节点,那么 apply(GCP . use _ preemptable _ node pool())位应该存在。

还要注意,这里有一个循环,在这个循环中有几个任务。循环的每一次迭代都是一个启动节点的请求(循环中的迭代次数比您提供的节点数多没关系,集群会在节点完成早期任务时让它们开始工作)。在我的例子中,我已经分配了 0–10 个节点,所以当我启动这个管道时,集群将一次启动 10 个节点。然后,每个节点将按顺序执行循环迭代中的任务。因此,在我的例子中,10 个节点中的每一个都将依次执行 3 项任务,然后关闭。没有必要有多个任务,你可以只有一个。我最喜欢每个节点有多个任务,这样我可以直观地监控作业的进度。

下一个单元格是最后一个,它在您的管道中创建和启动了一个作业!看起来是这样的:

client = kfp.Client(host=HOST)
arguments = {}
experiment = client.create_experiment(“experiment name”)run_name = “test run”
run_result = client.run_pipeline([experiment.id](http://experiment.id/), run_name, my_pipeline_name, arguments)

你不需要在这里改变任何东西,除非你想命名你的实验并运行。就是这样!当您运行单元时,笔记本将在集群中运行您的作业,并为您提供监控作业的链接。

成功!作者图片

在监视作业时要记住一点:随着集群节点的增加,它会显示类似“资源不可用”的内容,作为管道任务的状态。没问题,随着节点旋转,状态应该会在几分钟内改变。

一如既往,不要忘记在使用完云资源后取消对它们的配置!你可以在 GCP 的 Kubernetes 引擎->集群和人工智能平台->笔记本部分完成。

树莓派园艺:使用树莓派监控菜园—第 1 部分

原文:https://towardsdatascience.com/raspberry-pi-gardening-monitoring-a-vegetable-garden-using-a-raspberry-pi-part-1-fd231c7c6275?source=collection_archive---------29-----------------------

实践教程

介绍

项目之旅;CC BY-SA 3.0/【chollinger.com/blog

在 2020/2021 年左右的某个时期,像许多其他人一样(如果你在 3000 年你的“古代美国历史”课上,在北极的一个拱顶上读到这封全息磁带,我说的是新冠肺炎),我和我的 SO 都在园艺和种植蔬菜中找到了乐趣。

虽然种植植物的爱好在理论上可能是一种快乐,但我认为我听到的对园艺的最佳描述是“80%的痛苦,15%的担忧,5%的胜利和满足,以及 100%的等待”。你需要考虑很多事情——阳光照射、土壤的 pH 值、土壤中的养分、害虫、真菌、松鼠、鹿、鸟(以及各种各样的动物)——当然,每种植物的所有这些都是不同的。

因为这种内在的复杂性,已经有了很多与成长事物的斗争。萝卜,我没有适当地薄,产生悲伤的小棍子,而不是性感的球茎;黄瓜,西红柿和南瓜被枯萎病吃掉,松鼠攻击并挖出种子,蚜虫像圣经中的瘟疫一样降临到所有多叶和绿色的东西上,霜冻损害了原本不应该对可怜的芦荟造成任何损害的地方,猴草(短葶山麦冬)占据了凸起的床,植物生长成奇怪的形状,最终由于缺乏阳光而死去。

悲伤的灯泡(作者作者

对于其中的许多问题,我们已经找到了模拟解决方案:在 PVC 管框架上用扎带、阿兰·杰克逊歌曲和廉价啤酒建造一个鸟笼(信不信由你,它实际上是有效的,你可以在上面的“悲伤灯泡”图片中看到)。螳螂卵对抗蚜虫。针对山麦冬和其他杂草的杂草屏障。抗真菌铜杀真菌剂。

然而,有些事情仍然困扰着我,让我夜不能寐。自然,对此只有一个解决方案:在不该使用技术的地方使用技术,在不需要收集数据的地方使用技术,当然,尽可能以最过度设计的方式完成所有这些工作。

范围

那我们要做什么?

总体思路

我抓住了这个半真实需求的机会,最终在物理电子和微控制器的世界里把手弄脏了。这对我来说是一个全新的话题,鉴于我是你能想到的最糟糕的“边做边学”型的人,没有比这更好的机会来随意地将一些电路焊接在一起以收集数据。

因此,如果你在阅读这篇文章时,至少在电子、微控制器或园艺领域有一点经验,就此而言——我的投诉邮件可以在我的网站上找到。

不过,如果你更像我一样,根本没有涉足过电子学的现实世界,你可能会觉得这很有趣。

体系结构

无论如何,数据是我在这里追求的——关于阳光照射、土壤湿度和温度的数据。这实际上是一个有点有趣的问题,因为这些指标是高度本地化的,可能在几英尺之外就完全不同。

因此,我们将收集土壤湿度、阳光照射紫外线等指标,以及设备 ID 和时间戳等元数据,使用 REST API ( bigiron.local ,当然)将其发送到网络上的服务器,将其存储在 MariaDB 中,并使用 Grafana 进行分析。

建筑(作者)

数据点

以下是我们总共 6 张加高床中的 3 张的布局(完全不按比例):

真实世界布局(作者)

这些床在南边,但也位于两辆车、一个甲板和一个工具棚之间,两侧是树木。虽然从理论上讲,这些床应该得到充足的阳光,但很难估计它们实际得到了多少阳光。去年,我们在那里种植了“半日照”的草本植物,比如薰衣草,它们很快就死掉了。不过,你可以种植甜椒——就像辣椒中的辣椒一样,它们实际上也需要相当多的阳光。

土壤湿度也令人惊讶地难以测量:你可能会把手指伸进土壤,但这并不一定意味着它实际上是“年轻的胡萝卜”——湿的。可能只是“柑橘树不在乎”——湿了。量化这一点将是一个额外的收获。

温度和湿度是非常明显的——实际上,我在 iPhone 上用“快捷方式”应用程序做了一个小东西来提醒我们冷锋的到来:

iOS 快捷方式(按作者)

但是当然,这种方式太简单了

成分

这个想法非常简单:一个 Raspberry Pi 驱动的(仅仅因为我有几个)机器可以捕获这些指标并将其发送到服务器,这样我们就可以使用良好的 ole 数据工程工具箱来了解关于遥远的真实世界的所有信息。

为此,我选择了以下组件:

当然,这里有大量的选择——但通过最少的设置,我们可以以大约 25 美元一个的价格(当然,如果批量订购,价格会更低)制造一个这样的传感器。我将在下面介绍不同类型的传感器。

我个人并不太关心这一迭代的成本——尽管一直部署一个会很有趣,但这也需要有线电源连接(或锂电池)、互联网连接和防水外壳。所有这些事情可能使我值得购买一台 3D 打印机(并从中挤出另一篇文章),但就本文的范围而言,我将专注于实际构建它。

模拟电路

但是在我们做这些之前:我个人从中学开始就没有涉足过电子,所以已经有一段时间了。

很自然地,我“回归基础”,开始构建一个非常简单的电路:给 LED 供电。

一个简单的 LED 电路(作者)

这是一个记住一些基本原则的好练习。标准 LED 电路如下所示:

简单的 LED 电路图(https://en.wikipedia.org/wiki/LED_circuit)

因为我们需要用一个电阻来限制到达 LED 的电流,以防止它烧毁。

为了找到正确的阻力,我们可以使用:

R = (V_power - V_led) / I_led

其中R是以欧姆为单位的电阻,V_power是以伏特为单位的电源电压,V_led是以伏特为单位的 LED 正向压降,I_led是以安培为单位的 LED 期望电流。

对于红色 LED,我们将在 20mA 电流下观察 2–2.2V,即在 5V 电源下(如上所示),一个 93ω电阻就可以了(我最接近的电阻是一个 100ω电阻)。对于蓝色 LED,我们会看到 3.2–3.4V,因此电阻为 54ω(以此类推)。

玩这个很有趣——但为了记录,我对你将看到的大多数图片都使用了 330ω。

此外,你在图片上看到的小电源非常漂亮(一个 HW-131 售价 2 美元),因为它可以通过 PSU、电池或 USB 获得 3.3V 和 5V 的电源。

万用表(作者)

使用树莓和 GPIO

一旦我们开始通过树莓控制这个东西,真正的乐趣就开始了。然而,在“控制”部分开始之前,我们可以简单地将 a 3.3 或 5V 引脚连接到同一电路。

引脚布局

对于这个 pinout.xyz 是一个方便的资源:它显示了你各自的覆盆子的布局。一般来说,看起来是这样的:

引脚排列 xyz

Adafruit 看到了一个非常方便的小连接器, T-Cobbler ,它可以扩展这些引脚,并将它们标记在试验板上使用。

有了这方面的知识,我们可以连接第 1、2、4 或 17 针以及我们选择的地线,获得漂亮的灯光:

数字输入/输出(按作者)

然而,这仍然是模拟的,只是从计算机中获取电源。为了通过软件控制它,我们将使用 GPIO,或“通用输入/输出”。这个名副其实的库可以寻址所有的 GPIO 引脚。

解决这些问题的最简单方法是给出两个输入之一:GPIO.HIGH (3.3V)或GPIO.LOW (0V)。

练习:莫尔斯电码

我用这些知识做过一个有趣的小练习,就是用 GPIO 和下面的电路写一个莫尔斯电码发送器:

莫尔斯电码电路(作者)

这与 LED 显示器几乎相同,只是增加了一个扬声器和一个 UV LED,用于指示完整的“发送”。下面 gif 中的版本还包括一个毫无意义的“发送完毕”信号 LED。

结果是这样的:

莫尔斯电码在行动(作者)

垃圾代码在这里,如果你关心的话:

这真的没有意义,但看到它的运行很有趣——它只是将莫尔斯字母表翻译成定义的长度和中断,并发送一个GPIO.HIGH信号,然后是一个睡眠定时器,和一个GPIO.LOW信号来关闭它。

现在,我们从给 LED 供电发展到通过代码控制 LED 和扬声器——非常简单!

分线板和 I2C

有了这些基础知识,我们终于可以看看分线板了——带有数字或模拟输出的小电路,我们可以通过软件与之对话(而不仅仅是说“通电”/“断电”)。从软件的角度来看,事情变得更有趣了。

我们将看看这一系列传感器中最简单的传感器,MCP9808温度传感器。

焊接

无论您选择哪种电路板,第一步都应该将电路板焊接到(通常提供的)插头连接器上,否则就不可能获得稳定的连接。

焊料#1(作者)

焊料#1(显然是作者写的)

我对此没有任何建议——我得到的最好的建议是使用比你需要的更多的焊丝,并确保工件放在稳定的底座上(如试验板)。这符合“PVC 管和防松鼠的鸟网”式的生活方式,但它确实有效。

然而,Adafruit 上面有一个很棒的指南

I2C

从那里,我们实际上可以开始使用I2C:

(Inter-Integrated Circuit),读作 I-squared-C,是飞利浦半导体(现为恩智浦半导体)于 1982 年发明的同步、多主、多从、分组交换、单端、串行通信总线。它广泛用于在短距离板内通信中将低速外设 IC 连接到处理器和微控制器。或者,I2C 有时被拼写为 I2C(发音为 I-two-C)或 IIC(发音为 I-I-C)。

https://en.wikipedia.org/wiki/I%C2%B2C

简单地说,I2C 能做的不仅仅是告诉我们的电路“通电”和“断电”。这是一个低级的消息总线。

I2C 使用 2 条线— SCLSDASCL同步数据,而SDA发送和接收数据。除此之外,另外 2 根导线承载电源和接地。作为一条消息总线,I2C 也遵循主从模式:我们可以使用许多连接到一条消息总线的设备。设备可以使用寄存器,这些寄存器可以是用户可寻址的。

从头开始实施 MCP9808 通信

物理通信由各个芯片的供应商概述。例如,小的 MCP9808 在其规范中精确地解释了它是如何工作的。

我们需要先连接它:

资料来源,Kattni Rembor,CC BY-SA 3.0

或者身体上:

意大利面(作者)

使用i2cdetect,我们可以观察连接的 i2c 设备:

*pi @ 4g-raspberrypi ➜  ~  sudo i2cdetect -y 1

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- 18 -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --*

如果我们现在想从获取温度,我们知道默认的地址0x18,寄存器映射如下:

引出线(按作者)

其中TA0x05是当前温度的寄存器(该寄存器是只读的)。

电路物理连接后,我们可以通过软件检查状态:

如果我们只是使用 smbus2 库Python中读取它们:

*from smbus2 import SMBus
import time

registers = {
    'CONFIG': 0x01,
    'TUPPER': 0x02,
    'TLOWERL': 0x03,
    'TCRIT': 0x04,
    'TA': 0x05,
    'MANUFACTURER_ID': 0x06,
    'DEVICE_ID': 0x07,
    'RESOLUTION': 0x08,
}

bus = SMBus(1)
address = 0x18

for k in registers:
    dat = bus.read_word_data(0x18, registers[k])
    print('{} -> {}'.format(k, dat))*

我们会看到:

*CONFIG -> 0
TUPPER -> 0
TLOWERL -> 0
TCRIT -> 0
TA -> 26817
MANUFACTURER_ID -> 21504
DEVICE_ID -> 4
RESOLUTION -> 259*

这显然还不是有用的值。

现在,自然地,我们需要解析这些数据来理解它。

数字字载入 16 位只读环境温度寄存器(TA ),该寄存器包含 13 位二进制补码格式的温度数据

……

要将 TA 位转换为十进制温度,必须输出高三位边界位(TA < 15:13 >)。然后,确定符号位(位 12)以检查正或负温度,相应地移位这些位,并合并 16 位寄存器的高字节和低字节。

高位字节包含温度高于+32°C 的数据,而低位字节包含温度低于+32°C 的数据,包括分数数据。

组合高位和低位字节时,高位字节必须右移 4 位(或乘以 2⁴),低位字节必须左移 4 位(或乘以 2⁴).

将移位值的结果相加得到十进制格式的温度数据(见等式 5–1)。

(https://ww1 . microchip . com/downloads/en/device doc/MCP 9808-0.5C-Maximum-Accuracy-Digital-Temperature-Sensor-Data-Sheet-ds 20005095 b . pdf)

如果我们想保持简单,我们需要做的就是遵循文档:

*# Parse temp
v = bus.read_word_data(0x18, 0x05)
# Split in 2-complements
ub=v & 0xFF
lb=(v >> 8) & 0xFF
# Clear flags / take lower 13
ub = ub & 0x1F
# Check for negative values
if (ub & 0x10) == 0x10:
    ub = ub & 0x0F
    t = 256 - (ub * 16 + lb/16)
else:
    t = ub * 16 + lb / 16
print('Current temp: {}C'.format(t))*

我们得到22.6875C,72.8 华氏度,和房间里的其他温度计一致。

现在,如果你和我一样,从大学开始你就没有做过很多移位,但这里是我最好的尝试来解释在这里做什么,在哪里做,为什么:

  • 原始读数是十进制的27585,所以是二进制的110 1011 1100 0001
  • v & 0xFF是相对于1111 1111的位掩码(XOR),用于获取高 8 位(因为我们得到一个包含整数和小数部分的 16 位寄存器)。&是一个逻辑XOR(异或)运算,用于位屏蔽(提取位,如果你愿意的话)。
*1	1	0	1	0	1	1	1	1	0	0	0	0	0	1 XOR 1	1	1	1	1	1	1	1 1	1	0	0	0	0	0	11	1	0	1	0	1	1	1	1	0	0	0	0	0	1 >> 8 0	1	1	0	1	0	1	1 XOR 1	1	1	1	1	1	1	1 0	1	1	0	1	0	1	11	1	0	0	0	0	0	1 XOR 1	1	1	1	1 1*

我们现在可以用它乘以 2⁴=16,得到一个浮点结果:t = ub * 16 + lb / 16

或者使用更多的按位逻辑并将结果作为整数:(lb >> 4) + (ub << 4)

Tadaa!只用了一个的逐位数学来读取电路板的传感器。当然,我们也可以写入寄存器,但我离题了。

使用图书馆

现在,虽然这无疑对我有教育意义,让我了解供应商如何实现这些芯片,如何解决它们的“低级”问题,并自己编写代码,自然,几乎总是有可用的驱动程序和/或库。

这里的是 C++中的一个,这里的这里的是 Adafruit 当前的 Python 实现,它简化了很多:

*from board import *
import busio
import adafruit_mcp9808

# Do one reading
with busio.I2C(SCL, SDA) as i2c:
    t = adafruit_mcp9808.MCP9808(i2c)

    # Finally, read the temperature property and print it out
    print(t.temperature)*

这就是我们未来将使用的工具。

光传感器

上面的练习涵盖了温度——无论我们自己做数学还是依靠图书馆,我们都可以得到温度。阳光是一个不同的问题。

紫外线指数与光照水平和总日射表

因为我们在研究植物维护,所以我们在寻找“阳光照射”这一抽象指标,从家得宝到当地苗圃的每一包种子和每一株植物都提到了这个指标。

这个指标的问题在于,它非常主观且不科学,至少对于面向消费者的产品来说是如此。一般来说,我们想看看W/sqm的太阳辐照度,但我们马上就会明白为什么这并不简单。

我们能不能把紫外线,一种经常被测量和报道的东西(因此可以作为便宜的电路板)作为一种替代物?

紫外线指数是一个与地球表面某一点产生晒伤的紫外线辐射强度成线性关系的数字。

它不能简单地与辐照度(以 W/m2 为单位测量)相关,因为最受关注的紫外线占据了 295 至 325 纳米的波长光谱,当它们到达地球表面时,更短的波长已经被吸收了很多

https://en.wikipedia.org/wiki/Ultraviolet_index

https://en.wikipedia.org/wiki/Ultraviolet_index

我们所知道的是:植物确实会对紫外线或紫外线辐射做出反应,特别是 UV-B(280-320 纳米)和 UV-A(320-400 纳米),与人类一样——它会导致细胞损伤,并且有人假设植物可以预测紫外线模式并做出相应的反应。[1] [2] [3]**

紫外线指数从 0 到 12 不等,实际上是为了让我们了解晒太阳对人类有多大风险。

也就是说,紫外线是植物生长所需的阳光的副作用(或者子集)——紫外线-A 约占太阳能的 4.9%,而紫外线-B 约占 0.1%,在冬季几乎无法测量。[4]

我们真正想知道的是太阳强度。为了测量太阳强度,人们使用一种称为日射强度计的设备,这是一种(昂贵的)机器,使用硅光电池(想想光伏电池),补偿太阳角度,并产生一种确定太阳辐照度的确定性方法,通常以 W/sqm 为单位。[5]

日射强度计(顺便说一下,它的意思是“天空火测量”,这是一种很好的金属)的问题是它们的超级利基用例,因此成本巨大——即使是业余气象学家最便宜的也要超过 100 美元。我只想种些辣椒。

[1]https://www . frontiersin . org/articles/10.3389/fpls . 2014.00474/full【2】https://www . NCBI . NLM . NIH . gov/PMC/articles/PMC 160223/pdf/041353 . pdf【3】https://www . RS-online . com/design spark/the-importance-of-ultraviolet-of

紫外线传感器

正如我之前一直回避的那样,对于紫外线传感器,我看了两个选项: SI1145VEML6070 ,前者是更贵的选项。

VEML6070包含一个实际的 UV-A 传感器。

另一方面,SI1145可以计算紫外线指数,但是不包含实际的紫外线传感器。根据产品说明,它使用可见光和红外光来计算紫外线指数。**

SI1145有趣的是,我们可以用它来绕过它的卖点,即紫外读数,获得可见光和红外(IR)光读数。

使用,我们可以收集度量如下:

*import SI1145.SI1145 as SI1145

sensor = SI1145.SI1145()
vis = sensor.readVisible()
IR = sensor.readIR()
UV = sensor.readUV()
uvIndex = UV / 100.0
print('Visible:  {}'.format(vis))
print('IR:       {}'.format(IR))
print('UV Index: {}'.format(uvIndex))*

遗憾的是,这些都是相对指标,本身并没有太大意义——但这是收集紫外线指数(主要针对园丁)和某种可见光和红外光指标的开始。

勒克斯传感器

对于种植植物来说,W/sqm 的“下一个最好的东西”(至少是我能想到的)是 SI 单位 lux:

它等于每平方米一流明。在光度学中,这被用作人眼所感知的、撞击或穿过表面的光的强度的量度。它类似于辐射单位瓦特每平方米,但每个波长的功率根据光度函数加权,光度函数是人类视觉亮度感知的标准化模型。在英语中,“lux”既有单数形式也有复数形式。

T3【https://en.wikipedia.org/wiki/Lux】T5

虽然这测量的是可见光,而不是用于光合作用的“原始”能量,但除了观察“相对”可见光和紫外线之外,它仍然是一个很好的指标。换句话说,虽然它可能会给我们一个稀释的指标(因为它与可见光的光谱一致),但它仍然可以与其他指标结合使用。

为此,我找到了 3 个传感器: Adafruit VEML7700 (最高 120k lux)Adafruit tsl 2591(最高 88k lux),以及 NOYITO MAX44009 (最高 188k lux)。当然,还有其他选择。

明亮的阳光可以达到 100,000 勒克斯以上,所以列表中的第一个和最后一个传感器可能更好。

注意 : 在撰写本文时,两个 lux 传感器都还没有到达。您将在下面读到的大多数内容因此仅支持来自 UV 传感器的可见光的相对测量。

模拟传感器和土壤湿度

我们以前使用的传感器都是带有数字输出的 I2C 传感器。但是模拟呢?

嗯,不用担心——我买的电阻式土壤湿度传感器提供数字和模拟输出。然而,亚马逊的评论抱怨说,“手动和数字输出是有限的,模拟工作还好”。那么,如何读取模拟数据呢?

当然是用 ADC!对于那些对高保真感兴趣的人来说,你可能拥有一个 DAC(顺便说一下,Schiit 生产的很棒的 DAC 起价为 99 美元)——一个数模转换器。然而,在这个用例中,我们需要一个模数转换器。

我使用的是带 SPI 接口的 MCP3008 8 通道 10 位 ADC。它使用 SPI 或串行外设接口,而不是 I2C。虽然我不是这两方面的专家,但正如本文所证明的,SPI 和 I2C 是相似的,但 SPI 使用更多的引脚:

来源,迈克尔·斯克拉尔,CC BY-SA 3.0

这意味着我们可以这样连接它:

连接,它看起来像这样,使用这个非常可爱的长颈鹿锅作为主题:

长颈鹿博士(作者)

(请忽略电工胶带,我在高度专业的标准下操作)

我们试图读取的是电阻——如果两个探针之间的电阻上升,土壤就更干燥。当然,这种测量有点主观,所以我们必须校准它。

对于从板 I/O 引脚 25 上的引脚 0 数字读取原始数据(持续 60 秒),我们可以使用以下代码片段:

*import busio
import digitalio
import board
import adafruit_mcp3xxx.mcp3008 as MCP
from adafruit_mcp3xxx.analog_in import AnalogIn

# create the spi bus
spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)

# create the cs (chip select)
cs = digitalio.DigitalInOut(board.25)

# create the mcp object
mcp = MCP.MCP3008(spi, cs)

# create an analog input channel on pin 0
chan0 = AnalogIn(mcp, MCP.P0)

readings = []
i = 0
for x in range(0,60):
    i += 1
    val = chan0.value
    volt = chan0.voltage
    readings.append(volt)
    if int(val) != 0:
        print('Raw ADC Value: {}'.format(val))
        print('ADC Voltage: {} V'.format(chan0.voltage))
    time.sleep(1)

avg = statistics.mean(readings)
print('Avg voltage: {}'.format(avg))*

这给了我们每秒一个原始读数。

在一个相当潮湿的长颈鹿罐子里,我们得到的读数大约是

*Raw ADC Value: 30144
ADC Voltage: 1.5146715495536736 V*

或者平均来说是 1.5144。

现在,请记住,如果你没有连接一个东西,这也会给你读数:

*# Reading on an unconnected channel P2
Raw ADC Value: 448
ADC Voltage: 0.0 V
Raw ADC Value: 512
ADC Voltage: 0.0 V
Avg voltage: 0.004619211108567941*

但是你可以忽略那些读数。我认为这是任何 ADC 的自然产物——电流的自然波动+ Python 固有的float不精确性。

如果在不同的通道上连接一个电位器(上图可以看到!),您实际上可以看到这些波动,从技术上讲,电压应该保持不变,但事实并非如此:

*readings = [3.2968276493476765, 3.280714122224765, 3.280714122224765, 3.280714122224765, 3.2839368276493475, 3.280714122224765, 3.280714122224765, 3.2936049439230946, 3.2839368276493475, 3.2839368276493475, 3.277491416800183, 3.280714122224765, 3.280714122224765, 3.277491416800183, 3.2936049439230946, 3.28715953307393, 3.280714122224765, 3.277491416800183, 3.2936049439230946, 3.277491416800183, 3.2839368276493475, 3.28715953307393, 3.280714122224765, 3.280714122224765, 3.2839368276493475, 3.280714122224765, 3.2839368276493475, 3.28715953307393, 3.280714122224765, 3.280714122224765, 3.280714122224765, 3.2839368276493475, 3.280714122224765, 3.277491416800183, 3.2936049439230946, 3.2710460059510185, 3.2839368276493475, 3.280714122224765, 3.290382238498512, 3.280714122224765, 3.277491416800183, 3.280714122224765, 3.28715953307393, 3.2839368276493475, 3.280714122224765, 3.280714122224765, 3.28715953307393, 3.2839368276493475, 3.2839368276493475, 3.2839368276493475, 3.2839368276493475, 3.2839368276493475, 3.2742687113756004, 3.290382238498512, 3.2936049439230946, 3.28715953307393, 3.277491416800183, 3.280714122224765, 3.28715953307393, 3.290382238498512]
statistics.stdev(v)
# 0.0051783580637861605*

stdev 只有 0.005178,但是它在那里。湿度传感器正以 0.002305 的标准偏差引入同样强的波动。

在任何情况下,同样的实验在干土罐上产生的平均电压为 2.6。

所以我们可以得出结论:

  • 干土 : 2.6V
  • 潮湿土壤 : 1.5V
  • 一杯水:0.77 伏

把所有的放在一起

总装!

最终试验板布局

连接所有传感器后,布局如下:

最终布局(作者)

一个简单的 REST API 就可以做到

我们可以通过网络发送收集的数据。有人可能会想出一个奇特的消息队列,但我认为一个简单的本地化 REST API 目前就能解决问题——AWS Greengrass、Meme-Microservices、Kafka等其他应用程序还需要等待。我们每 10 秒钟发送一个数据包,并将其存储在这样的数据库中:

*CREATE OR REPLACE TABLE `sensors`.`data` (
    `sensorId`	            TEXT,
    `TempC`	                FLOAT,
    `VisLight`	            FLOAT,
    `IrLight`               FLOAT,
    `UvIx`                  FLOAT,
    `RawMoisture`           FLOAT,
    `VoltMoisture`          FLOAT,
    `MeasurementTs`         TIMESTAMP,
    `lastUpdateTimestamp`   TIMESTAMP
);*

REST 端点用go编写,并为x86而不是ARM编译,并部署在远程服务器上,这样我们就可以支持许多只需要偶尔发送数据的小型微控制器。

像往常一样,你可以在 GitHub 上找到完整的代码,所以我只说一些值得注意的点(这些是通用的go主题,但值得一提)。

首先,使用github.com/jmoiron/sqlxgithub.com/Masterminds/squirrel,其设置类似于一个大型CloudSQL实例所使用的设置类型,这让我感觉很好- sqlx极好地扩展了go的常规sql接口,并且squirrel可以用来自动执行一些数据库 I/O 任务。

*type Sensor struct {
	SensorId     string
	TempC        float32
	VisLight     float32
	IrLight      float32
	UvIx         float32
	RawMoisture  float32
	VoltMoisture float32
    MeasurementTs string
}

func (a *App) storeData(sensors []Sensor) error {
	q := squirrel.Insert("data").Columns("sensorId", "tempC", "visLight", "irLight", "uvIx", "rawMoisture", "voltMoisture", "measurementTs", "lastUpdateTimestamp")
	for _, s := range sensors {
        // RFC 3339
		measurementTs, err := time.Parse(time.RFC3339, s.MeasurementTs)
		if err != nil {
			zap.S().Errorf("Cannot parse TS %v to RFC3339", err)
			continue
		}
		q = q.Values(s.SensorId, s.TempC, s.VisLight, s.IrLight, s.UvIx, s.RawMoisture, s.VoltMoisture, measurementTs, time.Now())
	}
	sql, args, err := q.ToSql()
	if err != nil {
		return err
	}

	res := a.DB.MustExec(sql, args...)
	zap.S().Info(res)
	return nil
}*

我也开始越来越多地使用go.uber.org/zap,因为go的标准日志记录接口让有很多需要改进的地方。

*// Logger
logger, _ := zap.NewDevelopment()
defer logger.Sync() // flushes buffer, if any
zap.ReplaceGlobals(logger)*

除此之外,代码是一个非常标准的 REST API——Appstruct 保持一个指向 DB 连接池的指针(跨线程共享),秘密通过环境变量处理,而不是必须实现配置文件和viper(可能在将来)。

*type App struct {
	Router *mux.Router
	DB     *sqlx.DB
}

func initTCPConnectionPool() (*sqlx.DB, error) {
	var (
		dbUser    = mustGetenv("DB_USER")
		dbPwd     = mustGetenv("DB_PASS")
		dbTCPHost = mustGetenv("DB_HOST")
		dbPort    = mustGetenv("DB_PORT")
		dbName    = mustGetenv("DB_NAME")
	)

	var dbURI string
	dbURI = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true", dbUser, dbPwd, dbTCPHost, dbPort, dbName)

	dbPool, err := sqlx.Open("mysql", dbURI)
	if err != nil {
		return nil, fmt.Errorf("sql.Open: %v", err)
	}

	configureConnectionPool(dbPool)
	return dbPool, nil
}*

如此构建:

*# For x86
GOARCH=amd64 GOOS=linux go build -o /tmp/sensor-server *.go 
sudo mkdir -p /opt/raspberry-gardener
sudo cp /tmp/sensor-server /opt/raspberry-gardener
sudo cp ./.env.sh /opt/raspberry-gardener/
sudo sed -i 's/export //g' /opt/raspberry-gardener/.env.sh*

然后我们可以安装一个systemd服务:

*[Unit]
After=network.target
Wants=network-online.target

[Service]
Restart=always
EnvironmentFile=/opt/raspberry-gardener/.env.sh
WorkingDirectory=/opt/raspberry-gardener
ExecStart=/opt/raspberry-gardener/sensor-server 

[Install]
WantedBy=multi-user.target*

并部署:

*# Install `systemd` service:
sudo cp raspberry-gardener.service /etc/systemd/system/
sudo systemctl start raspberry-gardener
sudo systemctl enable raspberry-gardener # Autostart*

并检查:

*christian @ bigiron ➜  raspberry-gardener  sudo systemctl status raspberry-gardener
   Loaded: loaded (/etc/systemd/system/raspberry-gardener.service; disabled; vendor preset: enabled)
   Active: active (running) since Sat 2021-04-24 14:12:56 EDT; 17min ago
 Main PID: 12312 (sensor-server)
    Tasks: 7 (limit: 4915)
   Memory: 3.0M*

我们将对下面的传感器客户端做同样的事情——详见 GitHub。

一个脚本来统治他们所有人

为了将发送部分放在一起,我们需要做的就是组合每个传感器的所有单独脚本,混合 SPI 和 I2C,然后发送数据。

导入和收集数据非常简单:

*from board import *
import busio
import adafruit_mcp9808
import SI1145.SI1145 as SI1145
import os
import time
import busio
import digitalio
import board
import adafruit_mcp3xxx.mcp3008 as MCP
from adafruit_mcp3xxx.analog_in import AnalogIn
import platform
import getmac
import requests
import logging
import json
import argparse

# mcp9808
def read_temp() -> int:
    with busio.I2C(SCL, SDA) as i2c:
        t = adafruit_mcp9808.MCP9808(i2c)
        return t.temperature

# SI1145
def read_uv(sensor: object) -> set:
    vis = sensor.readVisible()
    IR = sensor.readIR()
    UV = sensor.readUV() 
    uvIndex = UV / 100.0
    return (vis, IR, uvIndex)

# HD-38 (Aliexpress/Amazon)
def read_moisture(spi_chan: object) -> tuple:
    val = spi_chan.value
    volt = spi_chan.voltage
    return (val, volt)*

由于我们希望每个传感器都有一个唯一的 ID(如果我们部署了多个传感器),因此我们将主机名和网络 MAC 地址组合成一个字符串:

*def get_machine_id():
    return '{}-{}'.format(platform.uname().node, getmac.get_mac_address())*

收集循环创建所有访问对象,缓冲数据,过滤无效数据,并将其发送给 REST API:

*def main(rest_endpoint: str, frequency_s=1, buffer_max=10, spi_in=0x0):
    buffer=[]
    ## UV
    uv_sensor = SI1145.SI1145()

    ## SPI
    # create the spi bus
    spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)
    # create the cs (chip select)
    cs = digitalio.DigitalInOut(board.CE0)
    # create the mcp object
    mcp = MCP.MCP3008(spi, cs)
    # create an analog input channel on pin 0
    chan0 = AnalogIn(mcp, spi_in)

    while True:
        try:
            # Read
            temp_c = read_temp()
            vis, ir, uv_ix = read_uv(uv_sensor)
            raw_moisture, volt_moisture = read_moisture(chan0)

            # Write
            # temp_c,vis_light,ir_light,uv_ix,raw_moisture,volt_moisture
            reading = {
                'sensorId': get_machine_id(),
                'tempC': temp_c,
                'visLight': vis,
                'irLight': ir,
                'uvIx': uv_ix,
                'rawMoisture': raw_moisture,
                'voltMoisture': volt_moisture,
                'measurementTs': datetime.now(timezone.utc).isoformat() # RFC 333
            }

            # Only send if we have all 
            # UV sensor sometimes doesn't play along
            if int(vis) == 0 or int(ir) == 0 or int(volt_moisture) == 0:
                logger.warning('No measurements: {}'.format(reading))
                continue

            buffer.append(reading)
            logger.debug(reading)
            if len(buffer) >= buffer_max:
                logger.debug('Flushing buffer')
                # Send
                logger.debug('Sending: {}'.format(json.dumps(buffer)))
                response = requests.post(rest_endpoint, json=buffer)
                logger.debug(response)
                # Reset
                buffer = []
        except Exception as e:
            logger.exception(e)
        finally:
            time.sleep(frequency_s)*

尽管有缓冲区,它使用了大约 16MB 的内存,所以我们应该准备好了!

物理部署

通常,当我写软件时,它在这里结束:我写了软件。现在运行它。走开。

然而,在这个小实验中,还涉及到实际部署它的组件。由于必须在室外,PI 和电路板的外壳至少需要半防水,即使只是来自地面的环境湿气。

愚蠢的问题需要一个 Dremel

由于我没有 3D 打印机(甚至没有更小的试验板),但我有旧的宜家塑料盒和 dremel,所以解决方案很明显:移除 Pi 鹅卵石,直接连接一些杜邦电缆,将它们放在塑料盒中,钻孔,使用电工胶带“密封”它,然后从以太网转换为 WiFi。简单而优雅(需要引用)。我还连接了相机,尽管我还没有对它进行编程。

雄伟的盒子#1(作者)

雄伟的盒子#2(作者)

第一项户外测试:甲板

然后,在一个阳光明媚的日子里,我把它搬到外面的甲板上(在一场暴风雨后),迎来了它在野外的第一个大日子(四月!):

部署#1(按作者)

紫外线传感器(作者)

对于相机拍摄的照片(我是通过 SSH 触发的):

天空照相机(作者)

然后我启动了两个systemd服务,等了一会儿。

分析初步数据

因为这实际上只运行了很短的时间,所以以下查询就足够了:

*SELECT *, TIMESTAMPDIFF(MINUTE, startTS, endTs) as totalMinMeasured,
CASE WHEN avgVoltMoisture <= 0.90 THEN "wet" 
WHEN avgVoltMoisture > 0.9 AND avgVoltMoisture <= 1.7 THEN "moist"
ELSE "dry"  END AS avgWetness
FROM (
	SELECT AVG(TempC) AS avgTemp, AVG(VisLight) AS avgVisLight, AVG(UvIx) as avgUvIx, AVG(VoltMoisture) as avgVoltMoisture,
	MAX(MeasurementTs) as endTS, MIN(MeasurementTs) as startTs
	FROM `sensors`.`data` as d
	WHERE d.MeasurementTs <= TIMESTAMP('2021-04-25 18:25:00')
) e*

这产生了意料之中的结果:

部署#1 结果(按作者)

下午 2 点左右,甲板上阳光明媚,经过前一天晚上的暴雨,植物是湿的。

但这只是为了确保它的工作!

第二项户外测试:花坛

一旦确认一切正常,就该上花园床了。

部署#2(按作者)

相机现在也有了一个更有趣的视角:

天空和一棵树(作者)

分析:第 2 部分(w/ Grafana)

公平地说,对这个数据集的分析看起来不会有太大的不同——让它运行得更久一点,让我们能够使用 Grafana 来可视化它:

Grafana #1(作者)

(14:20 左右传感器从阳光充足的甲板上移到花园的花坛上)

我也曾通过 SSH 手动拍照:

天空和树#2(作者)

这张照片是在格拉夫纳图的尾端拍摄的(大约 1800 小时),而上面这张照片是在大约 1430 时拍摄的。毫无疑问,你可以在图表上找到这种模式。

另一个光线指数和紫外线(以及温度)的峰值可以在 1700 小时左右观测到——这是太阳没有被树木遮挡的时候。

另一张拍摄于 19:30 的照片——你可以在照片的左下方看到太阳在植物上的角度发生了变化,但在上面的图表中,其他指标没有太大变化。

天空和树#3(作者)

尽管缺少 Lux 传感器,我们也可以在这里对“部分阴影”与“全日照”进行一些有根据的猜测——在这里比较光线和紫外线指数:

Grafana #2(作者)

知道传感器在 1420 小时左右被移动,我们可以比较明显阳光充足的甲板和床之间的峰值——两者的峰值都在 1.65-1.5k”光单位(“来自传感器的可见光”)。

这也与温度相关:

Grafana #3(作者)

在下午晚些时候的“高太阳”期间,您可以看到高达约 29 摄氏度(84.2 华氏度)的峰值,甲板上的峰值可达 40 摄氏度(104 华氏度)。

有趣的是,温度与 iPhone 天气应用程序记录的温度不符——那应该是恒定的 72 华氏度(或 22.2 摄氏度),而在非高强度时期,床徘徊在 18-19 度左右。c(64.4–67F)!

我认为可以公平地说:至少这个特殊的花园床可以称得上是“部分遮阴”,不会像房子的其他部分那样受到强烈的阳光照射。

当然,理想情况下,你会想在不同的条件下观察几天——但令人惊讶的是,我现在还不太相信我的特百惠电工胶带盒。但这是一个开始!

后续步骤和结论

这是第一部分——虽然它对我来说非常有趣(看到最后的图表真是令人愉快),我甚至不会假装这个项目非常有用、经济,甚至是必需的(我的办公室窗户朝向花园……),但我们仍然有很多事情可以做得更好!

  • 使用便宜得多的电路板,例如 Raspberry Zero
  • 焊接连接,构建一个紧凑的封装
  • 3D 打印出一个真正防水的外壳
  • 部署其中几个并收集一周左右的数据
  • 实现摄像机代码
  • 实施勒克斯传感器(见上文)
  • 部署更长的时间以收集更多的数据
  • 微调一些收集参数——目前每小时收集 3600 个数据点,这可能是不必要的
  • 对来自摄像机的结构化+非结构化数据运行某种形式的 ML 模型

所以第二部分可能是合适的——但是我觉得第一部分已经够长了。

无论如何,我希望您喜欢这个小小的旅程,从复习中学物理,到 I2C 和 SPI 的基础知识,到无缘无故地手动移位,到编写 REST API 和 SQL 查询,到将一个边上有孔的塑料盒搬到院子里,最后使用一些更普通的数据争论工具集来研究它。我知道我很开心!

所有代码在 GitHub 上都有。 所有的开发和基准测试都是在 GNU/Linux 下完成的【PopOS!在 2019 System76 Gazelle 笔记本电脑上使用 12 个英特尔 i7–9750h v cores @ 4.5 GHz 和 16GB RAM,以及 Raspberry Pi 4 Model B,使用bigiron . local*作为端点*

原载于 2021 年 4 月 25 日 https://chollinger.com的* *

树莓派园艺:使用树莓派监测菜园—第 2 部分:3D 打印

原文:https://towardsdatascience.com/raspberry-pi-gardening-monitoring-a-vegetable-garden-using-a-raspberry-pi-part-2-3d-printing-40471bd061dc?source=collection_archive---------12-----------------------

向我花园里的胡椒植物扔覆盆子馅饼的第二部分:关于 3D 打印、更多糟糕的焊接工作、I2C、SPI、Python、go、SQL 和 CAD 失败的话题

项目之旅(作者)

介绍

在这个项目的最后一次迭代中,我向你们展示了我的旅程,我把一个树莓派扔进了我的花园,以监测阳光和水的暴露。在本文中,我们将对原型进行改进,使之可以在室内和室外使用。

正如上一篇文章一样,第 2 部分将回顾我经历的所有步骤,并记录我的一些学习经历。

你隐藏了什么秘密?(作者)

版本 1 的问题

版本 1 很有趣——而且很有效!但是它也不是没有问题。让我们快速回顾一下这里需要改进的地方。

费用

一个相当令人望而却步的事情是成本——作为原型,它使用了一个结实的树莓 Pi 4 (4GB),一台零售价为 55 美元的机器。然而,实际上,我们希望以大约 30 美元(按消费价格计算)的价格制造同样的盒子!),所以我们可以从里到外部署多个。

这是一个简单的解决办法——Raspberry Pi Zero W配有 WiFi(这将比[PoE]以太网【0】更便宜、更普遍),只需 10 美元(如果你能找到那个价格的话)。廉价的微型 SD 卡零售价为 3 美元,廉价的微型 usb 电缆和电源插头也是如此,但我认为每个人都有许多电缆和适配器。我们将使用小的试验板,但是如果你想玩短路游戏,你也可以把铜焊接在一起。为可焊接试验板增加大约 2 美元——继续阅读,看看为什么这可能是一个好主意。

回到Zero W:这是一个神奇的小棒:1Ghz,一个核心,512MB 内存,40 PIN GPIO 头,摄像头连接器,BLE /蓝牙 4.1 和 802.11 b/g/n WiFi。10 美元。不带 WiFi 5 美元。这大约是我第一台个人电脑的 CPU 能力的 7.7 倍,体积大约是它的 1/1000!

公平地说,它很小,大约是空格键的一半大小(如果这是一个测量单位的话?):

树莓零 W 在鸭子空格键上,在樱桃 MX 蓝调床上(作者)

当然——一些 ESP32 可能会更便宜——但树莓也有明显的优势,它不是一个微控制器,而是一台完全成熟的计算机。对于不完全是嵌入式工程师的人来说,学习曲线没有那么陡峭。小 RasPis 还支持 USB,很利落!

【0】这里的是一个有趣的阅读,它依赖于 PoE,因此可以使用更简单的设计。

功率消耗

使用一个Raspberry和一个ESP32确实有它的缺点,尽管功耗是其中之一。假设我们想无线收集 48 小时的数据:

以前使用的Raspberry Pi 4在怠速时将消耗大约500-600 毫安。在 5V 电源下,我们可以看到 48 小时约 3W 或约 144Wh 的电流。

提醒:

功率(如Watthour)=电压(如Volts)/电流(如AMPs)

W =电压安培小时

WiFi 上的一个Raspberry Pi Zero W在 3.3V 上应该在 150 - 180 mA 左右,板载稳压器支持 5V PSUs 所以 48 小时大约 0.55W 或 26.64Wh(大约一个 Pi 4 的功耗的 18.5%!).

然而,另一方面, ESP32-WROOM-32 在主动发送数据时,在 2.2 至 3.6V(48 小时内低至 10.5 瓦时)下的范围约为 100 毫安,而在空闲时几乎没有:

https://www . espressif . com/sites/default/files/documentation/esp32 _ data sheet _ en . pdf

客观地说,一个 AA 锂电池的时钟大约为~ 3000ma,尽管只有 3.7V。当然,电源组配备了电源转换器——根据Pi Zero W的估计消耗和 48 小时的最低运行时间,一个 10,000mAh (10A)和 3.7V(这是锂的典型电压)的小型电源组可以为一个 Pi 供电,在没有转换损耗和低效率的理想情况下,大约为67 小时 (37Wh 假设有 50%的效率,10Ah 可以让我们用大约 1.5 天,这并不惊人。

成本也是一个令人望而却步的因素——即使是廉价的(或多或少)电源组或直接的锂电池也几乎是一个单位成本的两倍,最终几乎没有什么好处。例如, PiSugar 项目的 1200 毫安时电池价格为 39.99 美元,预计运行时间为 5-6 小时——这与上面的计算结果相符(1.2A*3.7V=4.44Wh4.44/0.55Wh*.5=4.3hrs,效率为 50%)。

因此,尽管这并不理想,我们将采取效率增益,但现在放弃电池的话题。

传感器和新零件清单

上一个项目也使用了许多不同的传感器来找出哪些效果最好——这一次,我们已经知道我们需要哪些传感器。

这是更新后的零件清单:

这使得我们的开发板总成本从将近 100 美元增加到大约 31.60 美元。如果你想要一个更坚固的设计,可焊接的试验板大约需要 2 美元,一些跳线可能需要 0.10 美元。

我会称之为成功,因为我可以从全球速卖通以大约一半的价格获得所有零件(不包括 Pi ),比如 1.50 美元的 MCP3008。但即使在国内,从 Adafruit、Microcenter 和 Amazon 这样的地方,每单位的总成本似乎也是合理的。

尺寸、外壳和布局

然而,上一次迭代的更大和更明显的限制是尺寸:由于我的天才计划使用 Dremel 在宜家的一个巨大的(尽管很薄)盒子上钻孔,最终的盒子既巨大又不实用。一个经典的双输局面,正是我喜欢的。

原始案例(作者)

它还依赖于一个试验板,大量的杜邦电缆,并且通常没有经过深思熟虑——其中的所有东西都应该适合一个小得多的占地面积。

改善布局应该是一个相对简单的任务:因为我们知道我们需要的传感器,我们可以优化空间。我们将在下一步做这件事。

然而,这是另一个不同的话题。我们现在就来谈这个。

简介:3D 打印机

我上次已经提到过了,我正在利用这个机会购买一台 3D 打印机。我选择了 Creality Ender 3 V2 ,这可能是 PLA 和 ABS 最推荐的预算 3D 打印机,价格在 250 美元左右。

这是我的第一台 3D 打印机,至少可以说是一次有趣的学习经历。

你会发现所有照片的背景都发生了很大变化,因为我们在房子里移动了这个大约 19x19 英寸的东西——我最后为它买了一个大约 22x22 英寸的宜家 Lack 拉克桌子,以防你好奇。

这座房子以前被称为“漂亮部分”的部分(作者)

构建 3D 打印机

Creality 自己的网站(!)提供了以下建议:

当然,它远非完美。

但在这个较低的价格下,它提供了一个体面的打印量,易于组装和改进,并可以产生高质量的打印。

()https://www.creality.com/goods-detail/ender-3-v2-3d-printer)

因此,在网上阅读后,我很清楚一个“标准”的 Ender 3 v2 无法满足它——太多关于人们在基本功能上挣扎的报道。所以,我在开始第一次打印之前安装了一些更新。那不是一个非常聪明的举动——我应该先试印一份。

铝挤压机、0.4 毫米喷嘴和 Capricorn PTFE 波顿管

第一批更新属于“预防性维护”,即我可能不会注意到的事情,直到我用了一堆打印机,但它似乎是一个很好的更换,因为打印机不是组装好的。我很快意识到那是一个错误。

第一次升级是铝挤出机:与塑料挤出机相比,这是一个更坚固的版本,可以将细丝移动到热端(熔化细丝的部分),具有更强的弹簧,以在挤出过程中保持更好的张力。这确保了细丝总是均匀地流动。它也不太可能破裂。

升级的挤出机(作者)

然而,我没有意识到的是,这个过程可能会搞乱打印机的校准,其中要求的细丝长度和挤出的细丝长度不同步,导致温床上的汇集和畸形的打印:

印刷错误#1(作者)

多亏了一个真正了解 3D 打印的朋友(因为我只是一个卑微的新手),我们能够按照这个神奇的网站上的步骤重新校准它。尽管这不是唯一的根本原因,但在许多失败的印刷之后,还是发生了这种情况。无论如何,听从他的建议,把这个网站加入书签吧——这会节省你很多时间。

出于同样的原因,我还安装了更新的管道、喷嘴和连接器——我不相信我会很快遇到问题,但这些升级是最容易做到的,因为其他一切都已经被拆开了。

自动调平

3D 打印最重要的事情之一是找平——这是像上面这样的混乱打印的主要问题之一——手动找平一些东西,特别是在 1949 年的房子里(直到 1972 年才发明水平地板,有趣的事实)肯定是痛苦的,如果不是完全不可能的话。

因此,售价约 40 美元的 Creality BLTouch V3.1 是一个了不起的小发明:它不是依赖人类手动移动 Z 轴,而是为你探测表面。

安装它相对简单:你用 BLTouch 替换常规的 Z 限制器电缆(这需要你移除主板盖),沿着管道布线,并用螺栓将其连接到打印头。

此时,建议记下主板版本,因为我们需要在下一步进行固件升级:

v 4.2.2(作者)

安装后,它看起来像这样:

BLTouch(作者)

我安装的另一件东西是一套更新的弹簧,所以床一旦被调平就能保持水平。

新固件

安装完这些更新后,下一步是安装固件——否则,BLTouch 什么也做不了。Ender 3 v2 对此有一个有趣的过程——你把一个.bin文件放到一个空的 SD 卡上,它就会安装它。

我使用了一个预编译的马林固件来完成这个任务。

然而,如果你选择了一个错误的固件,显示器会一直黑屏或者机器会发出可怕的声音。

我想知道它如何验证真实性,如果它只是执行随机代码。我认为是后者。

八字印刷

虽然所有这些改进都很棒,但在 Ender 3 上打印任何东西的过程如下:

  1. 查找或设计 CAD 模型
  2. 使用切片器,如 Cura ,将你的 CAD 转换成你的打印机可以理解的 g 代码切片
  3. 把它移到 SD 卡上
  4. 将 SD 卡插入打印机
  5. 打印,每 10 分钟带着打印机跑一趟房间,检查一下不可避免的故障

然而,有一个很棒的软件,叫做 Octoprint ,它可以用来通过树莓 Pi 远程控制你的打印机(或者说,从本地网络)!

首先,我打印了一个树莓派的附件(使用迷人的“彩虹丝”,这解释了,嗯,有趣的颜色组合):

附件打印(按作者)

并安装了它:

八字印刷案例(作者)

然后使用docker-compose安装 Octoprint,告诉 DHCP 服务器和 Pi 在 WiFi 上保持静态 IP:

version: '2.4'

services:
  octoprint:
    image: octoprint/octoprint
    restart: always
    ports:
      - "80:80"
    devices:
      - /dev/ttyUSB0:/dev/ttyACM0
      - /dev/video0:/dev/video0
    volumes:
     - octoprint:/octoprint
    # uncomment the lines below to ensure camera streaming is enabled when
    # you add a video device
    environment:
      - ENABLE_MJPG_STREAMER=true

volumes:
  octoprint:

(在这里你真正需要检查的是你的devices——容器认为它在和一个串口通信,而实际上,它是第一个 USB 设备)。

一旦它开始运行,就可以上传 g 代码并实时监控指纹:

Octoprint 用户界面(按作者)

您还可以实时查看打印机的各个步骤:

Octoprint GCode 查看器(按作者)

找平和埃尔默胶水

前面,我提到了调配是需要做的关键事情之一,否则,他们会做出意大利面条:

印刷错误#2(作者)

幸运的是,Octoprint 有一个插件我们可以用来支持这一点。

我使用了以下 GCODE:

G28
M155 S30
@ BEDLEVELVISUALIZER
G29 T
M155 S3G28
M155 S30
@ BEDLEVELVISUALIZER
G29 T
M155 S3

这是之前手动调平后的结果:

八行体水平(按作者)

然而,这个过程花了我很长时间——对于你在上面的图片上看到的一些印刷品,我使用了埃尔默胶水来增加附着力,以避免印刷品移动。我并不是说我推荐它——然而,它确实会使没有支撑的印刷品粘在不太平整的床上。

测试打印和进度

这是第一张照片:

印刷错误#3(作者)

这是后来的一个模型——你可以看到我试图找出的不完美之处——但它比第一次尝试的混乱要好很多:

巨大的龙猫考验(摘自《龙猫》/ となりのトト) / Thingverse 4515916

到目前为止,一切顺利!

新的组件布局

现在我们可以打印任何东西,从动漫人物的测试立方体到围栏,让我们再来看看最初原型的布局。

原始布局(作者)

设计新布局

这种布局自然浪费了大量空间。使用与之前相同的布线,但使用 20 引脚试验板(因为我不会蚀刻 PCB,所以这是肯定的),而不是 60 引脚试验板,去掉了补钉,我们只剩下这个 9x6.5cm 的小布局:

新布局(按作者)

我承认,在这种外形下保持这种状态并不容易,拼接 5V 通道可能也不太明智,但不要让它过载。许多串联设备可能会有问题(由于同一总线上的电阻),但最终,我不认为这将是一个问题。这不是超级优雅的,但它应该工作。

在任何情况下,在这个烧结图中,布局是二维的——一旦我们为Pi Zero W设计了外壳,我们就可以开始堆叠组件,甚至节省更多空间。我们将在下面看到。

新布局汇编(早期版本)(作者)

新组件

我们还用一个新的勒克司传感器,微型NOYITO MAX44009取代了紫外线。该传感器照常连接到 I2C(电源、GND、SCL、SDA),这就是为什么我们在板上重复使用相同的通道。它也确实很小,对于紧凑的设计来说很好。

焊接后的 MAX44009(作者)

软件更新

接下来要重访的是软件。

MAX44009和对0x4B的追求

自然,我们需要以某种方式集成新的传感器。这个东西的文档可以在这里得到,但是没有现成的pip3依赖。

人们拿到文档,算出了转换数学公式这里这里——如果你有兴趣从头开始做,请看一下第 1 部分

不过,这款传感器有一个缺点——每份文档都提到它使用 I2C 地址0x4A,但在我的例子中,它使用0x4B但不一致:

sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- 18 -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- 4b -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --

亚马逊评论是这样说的:

这些有可选的 I2C 地址 0x4a 或 0x4b。但是请注意,我收到的模块上的地址不是由制造商设置的。

因此,你需要以某种方式将跳线焊盘焊接在电路板上,否则地址将不是确定的,可能会在不同的使用之间改变。(我吃了苦头才发现)。

官方文档上的 CTRL+F 不会产生0x4B的结果,示例代码也默认为 0x4A:

class MAX44009 : public i2cSensor
{

    /** ######### Register-Map ################################################################# */

#define MAX_ADDRESS 				0x4a

#define INT_STATUS_REG				0x00
#define INT_ENABLE_REG              0x01
#define CONFIGURATION_REG           0x02
#define   CONFIG_CONT_MASK          (bit(7))  // CONTINOUS MODE
#define     CONFIG_CONT_ON          (1<<7)
#define     CONFIG_CONT_OFF         (0)
#define   CONFIG_MANUAL_MASK        (bit(6))  // MANUAL Set CDR and TIME

那不是好玩吗?!(╯ □ )╯︵ ┻━┻

无论如何,我只是创建了一个定制版本,它使用了第一次迭代的现有代码库和我奇怪的地址屏蔽:

from smbus2 import SMBus
import time

class MAX44009:
    # Thanks to https://github.com/rcolistete/MicroPython_MAX44009_driver/blob/master/max44009.py
    # With slight adjustments by chollinger93 for Python3 etc.
    MAX44009_I2C_DEFAULT_ADDRESS = 0x4A
    MAX44009_I2C_FALLBACK_ADDRESS = 0x4B

    MAX44009_REG_CONFIGURATION = 0x02
    MAX44009_REG_LUX_HIGH_BYTE = 0x03
    MAX44009_REG_LUX_LOW_BYTE  = 0x04

    MAX44009_REG_CONFIG_CONTMODE_DEFAULT     = 0x00    # Default mode, low power, measures only once every 800ms regardless of integration time
    MAX44009_REG_CONFIG_CONTMODE_CONTINUOUS  = 0x80    # Continuous mode, readings are taken every integration time
    MAX44009_REG_CONFIG_MANUAL_OFF           = 0x00    # Automatic mode with CDR and Integration Time are are automatically determined by autoranging
    MAX44009_REG_CONFIG_MANUAL_ON            = 0x40    # Manual mode and range with CDR and Integration Time programmed by the user
    MAX44009_REG_CONFIG_CDR_NODIVIDED        = 0x00    # CDR (Current Division Ratio) not divided, all of the photodiode current goes to the ADC
    MAX44009_REG_CONFIG_CDR_DIVIDED          = 0x08    # CDR (Current Division Ratio) divided by 8, used in high-brightness situations
    MAX44009_REG_CONFIG_INTRTIMER_800        = 0x00    # Integration Time = 800ms, preferred mode for boosting low-light sensitivity
    MAX44009_REG_CONFIG_INTRTIMER_400        = 0x01    # Integration Time = 400ms
    MAX44009_REG_CONFIG_INTRTIMER_200        = 0x02    # Integration Time = 200ms
    MAX44009_REG_CONFIG_INTRTIMER_100        = 0x03    # Integration Time = 100ms, preferred mode for high-brightness applications
    MAX44009_REG_CONFIG_INTRTIMER_50         = 0x04    # Integration Time = 50ms, manual mode only
    MAX44009_REG_CONFIG_INTRTIMER_25         = 0x05    # Integration Time = 25ms, manual mode only
    MAX44009_REG_CONFIG_INTRTIMER_12_5       = 0x06    # Integration Time = 12.5ms, manual mode only
    MAX44009_REG_CONFIG_INTRTIMER_6_25       = 0x07    # Integration Time = 6.25ms, manual mode only

    def __init__(self, bus=None) -> None:
        if not bus:
            bus = SMBus(1)
        self.bus = bus
        self.addr = self.MAX44009_I2C_DEFAULT_ADDRESS
        self.configure()

    def configure(self):
        try:
            self.bus.write_byte_data(self.addr, 
                self.MAX44009_REG_CONFIGURATION, 
                self.MAX44009_REG_CONFIG_MANUAL_ON)
        except Exception as e:
            print(e)

    def _convert_lumen(self, raw) -> float:
        exponent = (raw[0] & 0xF0) >> 4
        mantissa = ((raw[0] & 0x0F) << 4) | (raw[1] & 0x0F)
        return ((2 ** exponent) * mantissa) * 0.045

    def read_lumen(self)-> float:
        data = self.bus.read_i2c_block_data(self.addr,
            self.MAX44009_REG_LUX_HIGH_BYTE, 2)
        return self._convert_lumen(data)

    def _switch_addr(self):
        if self.addr == self.MAX44009_I2C_DEFAULT_ADDRESS:
            self.addr = self.MAX44009_I2C_FALLBACK_ADDRESS
        else:
            self.addr = self.MAX44009_I2C_DEFAULT_ADDRESS

    def read_lumen_with_retry(self):
        # To avoid 121 I/O error
        # Sometimes, the sensor listens on 0x4A,
        # sometimes, on 0x4B (ಠ.ಠ)
        try:
            return self.read_lumen()
        except Exception as e:
            print(f'Error reading lumen on {self.addr}, retrying')
            self._switch_addr()
            self.configure()
            return self.read_lumen_with_retry()

if __name__ == '__main__':
    # Get I2C bus
    bus = SMBus(1)
    # Let I2C settle
    time.sleep(0.5)
    MAX44009 = MAX44009(bus)
    # Convert the data to lux
    luminance = MAX44009.read_lumen_with_retry()

    # Output data to screen
    print(f'Ambient Light luminance : {luminance} lux')

重写客户端

除此之外,我对主代码进行了一点更新,使其更加灵活,使用了一个类层次结构,允许来自库、自定义代码、SPI 等的各种传感器实现。

这并不完全是工程学上的奇迹——但比以前要好。像往常一样,完整的代码在 GitHub 上。

from board import *
# ...

class Sensor():
    def __init__(self, sensor: object) -> None:
        self.sensor = sensor

    def read_metric(self) -> dict:
        """Implemented in each sensor

        Returns a `dict` of readings, mapping to the SQL schema.

        Return None if no reading. Equals NULL in DB.

        Returns:
            dict: Column -> Reading
        """
        pass

# Temp
class MCP9808_S(Sensor):
    def read_metric(self):
        with busio.I2C(SCL, SDA) as i2c:
            t = adafruit_mcp9808.MCP9808(i2c)
            return {
                'tempC': t.temperature
            }

# UV
class SI1145_S(Sensor):
    def read_metric(self):
        vis = self.sensor.readVisible()
        IR = self.sensor.readIR()
        UV = self.sensor.readUV() 
        uvIndex = UV / 100.0
        # UV sensor sometimes doesn't play along
        if int(vis) == 0 or int(IR) == 0:
            return None

        return {
            'visLight': vis,
            'irLight': IR,
            'uvIx': uvIndex
        }

# Moisture: HD-38 (Aliexpress/Amazon)
# pass spi_chan
class HD38_S(Sensor):
    def read_metric(self):
        raw_moisture = self.sensor.value
        volt_moisture = self.sensor.voltage
        if int(raw_moisture) == 0:
            return None
        return {
            'rawMoisture': raw_moisture, 
            'voltMoisture': volt_moisture
        }

# Lumen: pass MAX44009
class MAX44009_S(Sensor):
    def read_metric(self):
        return {
            'lumen': self.sensor.read_lumen_with_retry()
            }

__sensor_map__ = {
    'uv': SI1145_S,
    'temp': MCP9808_S,
    'lumen': MAX44009_S,
    'moisture': HD38_S
}

def get_machine_id():
    return '{}-{}'.format(platform.uname().node, getmac.get_mac_address())

def main(rest_endpoint: str, frequency_s=1, buffer_max=10, spi_in=0x0, disable_rest=False, *sensor_keys):
    if disable_rest:
        logger.warning('Rest endpoint disabled')
    buffer=[]

    # Create sensor objects
    sensors =  {}
    for k in sensor_keys:
        if k == 'uv':     
            # UV
            sensor = SI1145.SI1145()
        elif k == 'temp':
            # TODO: library behaves oddly
            sensor = None 
        elif k == 'lumen':
            sensor = m4.MAX44009(SMBus(1))
        elif k == 'moisture':
            ## SPI
            # create the spi bus
            spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)
            # create the cs (chip select)
            cs = digitalio.DigitalInOut(board.CE0)
            # create the mcp object
            mcp = MCP.MCP3008(spi, cs)
            # create an analog input channel on pin 0
            sensor = AnalogIn(mcp, spi_in)
        else:
            logger.error(f'Unknown sensor key {k}')
            continue
        # Create
        sensors[k] = __sensor_map__[k](sensor=sensor)

    while True:
        try:
            # Target JSON
            reading = {
                'sensorId': get_machine_id(),
                # Metrics will come from Sensor object
                'tempC': None,
                'visLight': None,
                'irLight': None,
                'uvIx': None,
                'rawMoisture': None,
                'voltMoisture': None,
                'lumen': None,
                'measurementTs': datetime.now(timezone.utc).isoformat() # RFC 3339
            }

            # Read all
            for k in sensors:
                try:
                    sensor = sensors[k]
                    logger.debug(f'Reading {sensor}')
                    metrics = sensor.read_metric()
                    if not metrics:
                        logger.error(f'No data for sensor {k}')
                        continue
                except Exception as e:
                    logger.error(f'Error reading sensor {k}: {e}')
                    continue
                # Combine 
                reading = {**reading, **metrics}

            # Only send if its not disabled       
            if not disable_rest:
                buffer.append(reading)
                logger.debug(reading)
                if len(buffer) >= buffer_max:
                    logger.debug('Flushing buffer')
                    # Send
                    logger.debug('Sending: {}'.format(json.dumps(buffer)))
                    response = requests.post(rest_endpoint, json=buffer)
                    logger.debug(response)
                    # Reset
                    buffer = []
            else:
                logger.info(reading)
        except Exception as e:
            logger.exception(e)
        finally:
            time.sleep(frequency_s)

if __name__ == '__main__':
    # Args
    parser = argparse.ArgumentParser(description='Collect sensor data')
    # ...

    # Start
    logger.warning('Starting')
    main(args.rest_endpoint, args.frequency_s, args.buffer_max, args.spi_in, args.disable_rest, *args.sensors)

整个if / else的切换当然可以避免,例如用一个插件模型,就像我在这里为另一个 RasPi 项目 [scarecrow-cam](https://github.com/chollinger93/scarecrow/tree/master/scarecrow_core/plugins)写的。但这比我现在愿意付出的努力要多一点。

无论如何,我们可以在本地测试:

python3 monitor.py --disable_rest --rest_endpoint na --sensors temp lumen moisture

并更新systemd服务。

更新后端

当然,传感器的添加需要模式更新,这在go中很容易,因为gostructs并且不讨厌你:

type Sensor struct {
	SensorId      string
	TempC         float32
	VisLight      int32
	IrLight       int32
	UvIx          float32
	RawMoisture   int32
	VoltMoisture  float32
	Lumen         float32
	MeasurementTs string
}

func (a *App) storeData(sensors []Sensor) error {
	q := squirrel.Insert("data").Columns("sensorId", "tempC", "visLight", "irLight", "uvIx", "rawMoisture", "voltMoisture", "lumen", "measurementTs", "lastUpdateTimestamp")
	for _, s := range sensors {
		// RFC 3339
		measurementTs, err := time.Parse(time.RFC3339, s.MeasurementTs)
		if err != nil {
			zap.S().Errorf("Cannot parse TS %v to RFC3339", err)
			continue
		}
		q = q.Values(s.SensorId, s.TempC, s.VisLight, s.IrLight, s.UvIx, s.RawMoisture, s.VoltMoisture, s.Lumen, measurementTs, time.Now())
	}
	sql, args, err := q.ToSql()
	if err != nil {
		return err
	}

	res := a.DB.MustExec(sql, args...)
	zap.S().Info(res)
	return nil
}

这同样适用于SQL接口/DDL——一个相当标准的练习。

添加新功能

除了收集数据,另一个有用的功能是通知园丁任何问题。为此,我在go实现了一个简单的通知系统,也就是在服务器上。

实现很简单,我们为阈值定义常数:

// Hard coded alert values for now
const MIN_TEMP_C = 5
const MAX_TEMP_C = 40
const LOW_MOISTURE_THRESHOLD_V = 2.2

func (a *App) checkSensorThresholds(sensor *Sensor) bool {
	if sensor.VoltMoisture >= LOW_MOISTURE_THRESHOLD_V || sensor.TempC <= MIN_TEMP_C || sensor.TempC >= MAX_TEMP_C {
		return true
	}
	return false
}

使用阈值作为 *const* 的蚂蚁,平心而论,就像重新编译你的内核进行一次 *ulimit* 的调整——但我在努力避免 *config* 的文件。

为了不在每次传感器触发时发送通知,我们还实现了一个定时器,为了简单起见,它使用了一个全局map和一个mutex:

// Simple mutex pattern to avoid race conditions
var mu sync.Mutex
var notificationTimeouts = map[string]time.Time{}

const NOTIFICATION_TIMEOUT = time.Duration(12 * time.Hour)

func (a *App) validateSensor(sensor *Sensor) error {
	// Check the values first
	if !a.checkSensorThresholds(sensor) {
		zap.S().Debugf("Values for %s are below thresholds", sensor.SensorId)
		return nil
	}
	// Otherwise, notify
	mu.Lock()
	// If we already have the sensor stored in-memory, check whether its time to notify again
	if lastCheckedTime, ok := notificationTimeouts[sensor.SensorId]; ok {
		if time.Now().Sub(lastCheckedTime) < NOTIFICATION_TIMEOUT {
			// Not time yet
			zap.S().Debug("Timeout not reached")
			return nil
		}
	}
	// Reset the timer
	notificationTimeouts[sensor.SensorId] = time.Now()
	// Otherwise, notify
	err := a.notifyUser(sensor)
	// Release the mutex late
	mu.Unlock()
	return err
}

通过smtp发送通知,与前面的迭代类似,我们将秘密保存在环境变量中,而不是配置文件中——为了简单起见,再一次:

type SmtpConfig struct {
	smtpUser              string
	smtpPassword          string
	smtpAuthHost          string
	smtpSendHost          string
	notificationRecipient string
}

func NewSmtpConfig() *SmtpConfig {
	return &SmtpConfig{
		smtpUser:              mustGetenv("SMTP_USER"),
		smtpPassword:          mustGetenv("SMTP_PASSWORD"),
		smtpAuthHost:          mustGetenv("SMTP_AUTH_HOST"),
		smtpSendHost:          mustGetenv("SMTP_SEND_HOST"),
		notificationRecipient: mustGetenv("SMTP_NOTIFICATION_RECIPIENT"),
	}
}

发送部分可以在go中本地完成:

func (a *App) notifyUser(sensor *Sensor) error {
	header := fmt.Sprintf("To: %s \r\n", a.SmtpCfg.notificationRecipient) +
		fmt.Sprintf("Subject: Sensor %v reached thresholds! \r\n", sensor.SensorId) +
		"\r\n"
	msg := header + fmt.Sprintf(`Sensor %s encountered the following threshold failure at %v:
	Temperature: %v (Thresholds: Min: %v / Max: %v)
	Moisture: %v (Thresholds: Min: %v / Max: N/A)`, sensor.SensorId, sensor.MeasurementTs,
		sensor.TempC, MIN_TEMP_C, MAX_TEMP_C, sensor.VoltMoisture, LOW_MOISTURE_THRESHOLD_V)
	zap.S().Warn(msg)
	// Get config
	// Auth to mail server
	auth := smtp.PlainAuth("", a.SmtpCfg.smtpUser, a.SmtpCfg.smtpPassword, a.SmtpCfg.smtpAuthHost)
	err := smtp.SendMail(a.SmtpCfg.smtpSendHost, auth, a.SmtpCfg.smtpUser,
		[]string{a.SmtpCfg.notificationRecipient}, []byte(msg))
	if err != nil {
		zap.S().Errorf("Error sending notification email: %v", err)
		return err
	}
	return nil
}

它会发出这样的信息:

传感器单元测试在 2021–07–01t 10:00:00 遇到以下阈值故障:温度:4(阈值:最小值:5 /最大值:40)湿度:1.5(阈值:最小值:2.2 /最大值:不适用)

有用!正如我们将在下面看到的,40 摄氏度是相当低的,但总是可以调整的。

重新部署Pi Zero W

现在我们所有的软件都是最新的,是时候设置一个新的Raspberry Pi Zero W了。

物理装配

这个很简单:我在它上面放了一个铝制散热器(可能没有意义,但我确实有这些——我不知道外面会有多热),并将 I2C 接头焊接到一些引脚上。

Pi Zero如此便宜的部分原因是你需要自己焊接引脚——为了节省最终设计的空间,我将Pi Zero倒过来焊接(就像将Pi放在试验板上一样)。这里的想法是将试验板拍在板的背面,而不妨碍 APU,因此不会给小 Pi 造成热噩梦。

圆周率为零——我用完了所有的针(作者)

当然,人们可以直接在 Pi 上焊接电缆,但我想为将来可能的调整留有余地。你马上就会看到,这是一个明智的选择。

软件部署

当然,现在我正在考虑编写一个实际的部署管道,或者甚至构建 Docker 映像——但是当面临“在湖上钓鱼而无所事事”和“从头配置 CI/CD”之间的冲突时,前者(幸运地)胜出。

首先,使用 Raspi 成像仪安装一个Raspbian操作系统。这将把bootroot安装到你的电脑上。

*chroot* ,如果你不熟悉的话,就像 *docker* ,只是不像潮人。😃

首先,通过在boot的挂载点上执行touch ssh来启用 ssh。

然后,要启用 WiFi,将您现有的/etc/wpa_supplicant/wpa_supplicant.conf复制到同一个分区。如果您没有或使用 macOS/Windows,请对此进行自定义:

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=US

network={
       ssid="YourNetworkSSID"
       psk="Password"
       key_mgmt=WPA-PSK
    }

然后,chroot主分区:

sudo proot -q qemu-arm -S /media/$USER/rootfs

并运行以下安装脚本:

#!/bin/bash

if [[ $EUID -ne 0 ]]; then 
    echo "This script must be run as root"
    exit 1
fi

if [[ -z "${PI_IP}" || -z ${PI_DNS} || -z ${REST_ENDPOINT} || -z ${PI_ROUTER} ]]; then 
    echo "Usage: PI_IP=192.168.1.2 PI_ROUTER=192.168.1.1 PI_DNS=8.8.8.8 REST_ENDPOINT=http://server.local:7777 ./setup_pi_zero.sh"
fi 

# Install dependencies
apt-get update
apt-get install git python3 python3-pip python3-venv i2c-tools vim ntpdate

# Enable I2C, SPI
echo "Enable I2C and SPI manually by running raspi-config over ssh"
#raspi-config 

# Static IP
echo "interface wlan0" >> /etc/dhcpcd.conf
echo "static ip_address=${PI_IP}/24" >> /etc/dhcpcd.conf
echo "static routers=${PI_ROUTER}" >> /etc/dhcpcd.conf
echo "static domain_name_servers=${PI_DNS} 1.1.1.1 fd51:42f8:caae:d92e::1" >> /etc/dhcpcd.conf
echo "nameserver ${PI_DNS}" >> /etc/resolv.conf 

# Time
timedatectl set-timezone America/New_York
timedatectl set-ntp True

# Clone code 
cd /home/pi
mkdir -p /home/pi/workspace
cd /home/pi/workspace
git clone https://github.com/chollinger93/raspberry-gardener
cd raspberry-gardener/client
SENSORS="temp lumen moisture" # Customize

# Install 
mkdir -p /opt/raspberry-gardener
touch /opt/raspberry-gardener/.env.sensor.sh
echo "REST_ENDPOINT=$REST_ENDPOINT" > /opt/raspberry-gardener/.env.sensor.sh
echo "SENSORS=$SENSORS" >> /opt/raspberry-gardener/.env.sensor.sh
cp monitor.py /opt/raspberry-gardener/
cp -r max44009/ /opt/raspberry-gardener/

# Install packages as sudo if the sensor runs as sudo
pip3 install -r requirements.txt

# Systemd
mkdir -p /var/log/raspberry-gardener/
cp garden-sensor.service /etc/systemd/system/
systemctl start garden-sensor
systemctl enable garden-sensor # Autostart

# logrotate.d
cat > /etc/logrotate.d/raspberry-gardener.conf << EOF
/var/log/raspberry-gardener/* {
    daily
    rotate 1
    size 10M
    compress
    delaycompress
}
EOF

并运行:

export PI_IP=192.168.1.2 
export PI_DNS=8.8.8.8 
export REST_ENDPOINT=http://server.local:7777 
./setup_pi_zero.sh

当然,您也可以通过 SSH 来实现这一点——这样会更容易,但速度会更慢。然而,raspi-config必须在实际设备上运行,因为它加载内核模块。

raspi-config(作者)

您可能还想定制/etc/logrotate.d中的logrotate配置:

/var/log/raspberry-gardener/* {
    daily
    rotate 1
    size 10M
    compress
    delaycompress
}

我们只需要运行一次,然后将整个 SD 卡dd到另一个Pi上,或者在每个Pi上运行它——这实际上没有什么区别。

如果你想dd,我建议你在启动Pi并运行sudo raspi-config之后再做:

sudo umount /media/$USER/rootfs
sudo umount /media/$USER/boot

sudo dd of=$HOME/$(date --iso)_raspberry_rootfs.img if=/dev/mmcblk0p2 bs=4M
sudo dd of=$HOME/$(date --iso)_raspberry_boot.img if=/dev/mmcblk0p1 bs=4M

拥有一个图像是很有用的,所以一旦它起作用了,你可能就想这么做了。

快速测试

此时,建议测试整个设置,例如查看负载:

Pi 空载(作者)

也许还可以看看 Grafana,因为一旦你通过raspi-config启用I2CSPI,服务就会开始运行。

建造新的围墙

让我们开始建立一个新的案例!

最初,我想“重新组合”(派生)一个现有的 Pi Zero 外壳,就像这个一样,但很快发现我的 CAD 技能太新,无法在实际功能模型上工作。所以我从零开始。

测试组件

在做任何事情之前,我们必须在没有外壳的情况下组装整个组件,以了解所有组件是如何组合在一起的:

试验板组件(作者提供)

进行测量

接下来,我用一些相当精确的卡尺来测量所有的尺寸,并在纸上画出一些粗略的想法。

测量值(按作者)

我不会让你看到草图的。

涉足 CAD

当谈到 CAD 时,有很多选项可用——但我选择了 Autodesk 的免费 Tinkercad ,因为它非常简单。Fusion 360 确实有个人使用的免费版本,但是不能在 Linux 上运行。

虽然我不能忍受基于浏览器的工具(电子网络应用程序)的 T4,但它们有时比试图强迫其他软件与 Linux 和谐相处更容易。就像他们说的,事情就是这样。

不管怎么说,Tinkercad 是针对业余爱好者和学生的,它有一个丰富多彩的教程,连我都懂。

Tinkercad(作者)

设计(1.0 版)

我的设计非常非常简单——但这可能是因为这是我的第一个 CAD 设计——我通常是那种测量一次,切割两次的类型。

有趣的事实:在我的棚子里有一小块 2x4 的木板,上面用 sharpie 写着尺寸。它们与 2x4 的尺寸完全不匹配。这就是你应该知道的全部——但是,它通常会解决。毕竟,我在这片土地上建造了所有的高架床!

事实证明,计算机辅助设计比圆锯需要更高的精确度。这种设计背后的想法与其他商业保护套没有太大的不同:4 个大头针粘在一起将保护套固定在一起。小切口支持我需要的电缆的准确放置——只是比商业案例的切口更少。此外,该案件有更多的浪费空间比它需要的,以支持杜邦电缆,而不需要焊接的东西直接到头部。

一旦我有了基于这些想法和测量的类似模型的东西,我就导出.stl文件,将其加载到 Cura 并开始打印。

很自然地,我弄乱了打印设置,高估了打印机的能力(这最终取决于切片机的设置)——小小的支撑梁变成了意大利面条。

正在打印(按作者)

我还意识到,我测量的公差对于我在 Cura 中设置的打印设置来说有点太详细了,我完全搞乱了其他测量——换句话说,设计不是很好。

那不符合(作者)

设计(2.0 版)

第二个设计遵循了一个不同的理念:更大的公差可以在以后填充。

它还避免了脆弱的支撑梁,依靠一个比底部稍小的顶部。它还将湿度传感器的切口移到了顶部,给 fit 更多的空间。

最后但同样重要的是,它为码头本身增加了两个支撑金字塔,所以它可以安全地坐落在围栏的底部。

第二次设计(作者)

防水问题

不幸的是,这种设计没有解决一个问题:防水(或防风雨)。

现在,虽然一个专门制造的箱子无疑比任何商业箱子都更合适(如果你能找到适合所有组件的话),但设计和打印需要密封并且公差很小的东西不在我目前的 CAD 技能范围内。

然而,还有其他选择——也就是指甲油和电工胶带,我上次已经大量使用了。指甲油不导电,能形成良好的密封,但不耐热——据说要到 120 摄氏度(248 华氏度)左右。鉴于这将只适用于 2 个外部传感器,我不太担心这一部分——我不认为我的辣椒是热安全的。

虽然我不打算将整个 Pi 浸在指甲油中,但我们可以在某种程度上防止传感器受天气影响,这些传感器在外壳外面,以进行精确测量。传感器和外壳之间的连接足够精确,可以密封,你猜对了!,电工胶带。

已经进行了许多聪明的实验,将指甲油视为保形涂层的替代物——据我所知,它是有效的,但由于没有标准的指甲油配方(正如我在当地的 Kroger 发现的那样),一些添加剂可能会干扰 PCB 上的塑料。

当然,如果你预见到再也不用接触 Pi,你也可以把整个东西浸在树脂或硅树脂里——但是我们现在坚持用指甲油。

装配

随着第二个设计打印出来,组装相对简单:只需把所有东西都扔进去,暂时接受一些设计缺陷,并使用更多的电工胶带来保持它的位置,并将传感器放入顶部切口。

很自然,试验板上的所有东西都会引起一个问题,这是每个以前做过台式电脑的人都知道的——电缆管理。退一步说,标准长度的电缆很紧:

第一次装配(按作者)

这里的选择包括自己卷曲一些微小的电缆或购买更小的电缆。或者,当然,红脖子工程学院总是有帮助的:解开电缆,用烙铁在末端镀锡,直到它适合试验板的插槽(信不信由你,这是可行的)。一卷 24AWG 电线也可以。

虽然我不想告诉你如何生活,但我也许可以提供一句忠告:不值得花一个小时试图将电缆组装在一起,并在这样做的同时吸入大量有害气体,只是为了节省 10 美元买一把电线。我听说是这样。下面的 3.0 版本注意到了一些建议。

一个相关的问题是湿度传感器——我没有考虑到长杜邦端件在这一点上。所以我做了任何正常人都会做的事情,只是解开一些电缆,直接焊接到 PC 上,而不是再打印 4 个多小时~ 5 毫米。

It works(作者)

户外测试

所有这些都解决了,新的后端服务也部署好了(停止systemd服务,从git重新编译,复制新的二进制文件,重新启动服务——如果你想要电子邮件,调整.env.sh文件),我们已经准备好测试了!

寻找一个地方

在这个测试中,我首先把这个小盒子搬到了甲板上,以观察我们的一些辣椒植物,即一种非常快乐的中国辣椒“Habanero”(如果你是一个正常人,也称为“Habanero Pepper”)。

剩下的唯一问题仍然是供电问题——我没有防水的 USB 电源插头。

首次室外测试(作者)

(忽略胶带—见上面的电缆注释—下一次迭代就不需要了)

无论如何,我在中午时分把盒子扔了出去,然后去买杀虫剂,试图挽救大约 30 棵花椰菜免受虫害,以及 10 立方英尺的覆盖物,因为显然,花园之旅永远不会停止。

不过,在离开之前,对数据库的快速浏览显示,这个小盒子一通电就开始收集数据,这要归功于systemd服务(我称之为“即插即用”)。

大约 2 小时后,我们可以找到光线和温度的良好数据:

甲板上的卢蒙(作者)

甲板上的温度(作者)

我们可以在这里看到一些非常令人印象深刻的峰值——在阳光直射下超过 60 摄氏度(140 华氏度)(远远高于我们在后端配置的 40 摄氏度峰值!).这并不令人惊讶——停在阳光下的汽车可以达到类似的温度(事后看来,这可以解释为什么我的卡车似乎每天都试图煮沸我的太阳镜)。

然而,我们可以看到 ADC 没有收集数据:

破损的湿度传感器(作者)

结果证明,这是MCP3008上的无效引脚布线——所以我们得到电压波动读数作为数据。请参见第 1 部分深入了解——总结就是,ADC 将始终报告值,即使它们并不真正基于任何东西。

微调和故障排除

在修复了这个错误之后,我还将顶部的外壳再加高了 10 毫米,尽管这并没有解决所有问题——外壳仍然非常紧,一些印刷缺陷使得将两部分装配在一起变得不容易。

在组装过程中,一些杜邦线缆脱落的情况也经常发生——只有当整个过程都在发送数据时,你才会意识到这一点。

I2C 和 SPI 在这方面也特别有魅力,因为一旦数据或同步线路上有电流,它们就会显示为连接,尽管传感器上可能没有电源(因为电缆决定脱落)。10-20 厘米电缆的巨大尺寸经常碍事。

这不行。

3.0 版和部署

这将是本文的最终版本。

装配

我可以用来优化这一点的唯一方法是直接用常规标准 24 AWG 导线将所有东西焊接在一起,并使用大约 50 毫米的杜邦电缆,减少不在试验板本身上的所有连接周围的导线长度和密度(因为这里很容易使用跳线)。

最终装配(由作者完成)

稍后会详细介绍。

室内最终测试

最后,这个小盒子已经准备好部署在(文字)领域了。不过,在这之前,我让它连着厨房里的一个弱光设备运行了一夜,以确保最后一次一切正常:

室内测试(作者)

我很高兴我这么做了,因为如果你短了SDASCL,这就是i2detect所做的:

pi@raspberrypi:/var/log/raspberry-gardener $ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 
10: 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 
20: 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 
30: 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 
40: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 
50: 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 
60: 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 
70: 70 71 72 73 74 75 76 77

哦好吧。ヽ( ー` )┌

室内监控(作者)

在室内,尤其是在晚上,唯一有点相关的指标是温度——25C/77F 在晚上关闭空调的情况下似乎是合理的——和土壤湿度,后者不会变化那么快。上一篇文章我们还记得,1.5V 是“湿土”。有用!

然而,流明始终为零——请参见下面中的试图解释这一点。

户外部署和数据收集

第二天早上 8:30 左右,我终于把这个绿色的小盒子放在了我的胡萝卜和黄瓜之间:

室外部署#1(作者)

一路顺风,小家伙!

数据分析和提醒

这是多么美好的一天!在和许多神奇的萤火虫度过了一个美妙的夜晚后,我在晚上 10:30 左右关掉了 RasPi:

小派上床睡觉前不久(作者)

因此,让我们来看看全天的所有指标!

流明/光

这个真的很有趣——你可以看到峰值在 900 勒克斯左右。

灯光(作者)

现在看维基,900 勒克斯大致是个“阴天”。

https://en.wikipedia.org/wiki/Lux

整个刻度是对数的,所以看数值<= 0 is a bit of a challenge with Grafana — we can, however, find those values on the database. However, looking at the sensor’s 规格表,我不认为数值< = 0 应该被认为是有效的(因为Pi上的python程序以相当标准的设置运行)。

还有-

光源在垂直于光源方向的表面上提供的照度是从该位置感知的光源强度的量度。

例如,一颗视星等为 0 的恒星在地球表面提供 2.08 微勒(μlx)。

一颗几乎察觉不到的 6 星等恒星提供 8 纳勒克斯(nlx)。无遮挡的太阳在地球表面提供高达 100 千勒克斯(klx)的照度,确切值取决于一年中的时间和大气条件。

这种直接法向照度与日光照度常数 Esc 相关,等于 128000 勒克斯(见日光和日光常数)

所以角度——平行于地面——可能起了相当大的作用。由于 I2C 传感器的任何测量本质上都是相对的,所以让我们来看看…

温度

….温度,看看我们能否将两者关联起来,以补偿不太理想的校准。iOS 天气应用程序显示白天温度为 84–86 华氏度(29–30 摄氏度),这在这里是一个相当普通的夏天。

查看传感器的度量,我们会发现高达 50C (122F)的峰值,这与流明中的峰值相关,但远不如它们在甲板上(如果您还记得第 1 部分,甲板就在观察到的凸起床上)时那么强:

温度(作者)

因此,我们可以合理地得出结论:被观察的胡萝卜、黄瓜和甜椒并没有得到每天 6 小时的全日照(通常的“全日照”标准),而是接近 3 小时,这将被称为“部分日照”。然而,所有的辣椒都要应对 60 摄氏度以上和 1.5 千流明的高温——这可能解释了为什么它们以目前的速度生长,与其他植物只有几英尺的距离。

水分

这个很无聊——虽然我在下午 5:30 左右给花坛浇水,但考虑到表土的巨大体积(毕竟它们是凸起的花坛),几加仑 50 平方英尺的水可能不会有什么影响:

水分(按重量)

除非你给叉子所在的特定位置浇水,否则测量结果不会有太大变化,除非整个床都干了,此时你可能不应该看着 Grafana,而是拿着喷壶或水管出去。

然而,它们在小盆上工作得很好——在高架床上,也许一种不同的方法可能是合适的。

批评和下一步措施

在所有这些改进之后,让我们再来一次回顾——或者至少是类似的东西。

新的外壳有点像弗兰肯斯坦——我确实有一些可焊接的迷你试验板,但在撰写本文时,我还没有——所以它是焊接电缆、半固定连接和电工胶带的混合。连接丢失并导致问题,很难找出原因,因为一根铜线就可以短路。尤其是车道一直都很松。

但盒子最终关闭,由于缺乏精度(PLA @ 210C 的 0.2/0.4 毫米印刷),粘在一起非常非常紧(这很好!).不过,很难判断它是否开着——一个状态 LED 将是一个很好的补充。

我这里也只有一个MCP9808,所以除了将引脚或焊线拆下来,我不得不在这里使用更多的杜邦线缆。虽然它确实工作得相当好,因为它们将传感器保持在适当的位置,而没有特殊的外壳。

除了电缆的组合之外,新的 Pi 还有点摇晃,需要用胶带固定,因为我的小金字塔间隔器只能从底部施加压力,我的精确测量、设计和打印技能太差,无法完成任何复杂的工作。然而,有更多经验的人已经建立了一些惊人的案例——所以有些事情需要努力。

总的来说,这离完美还很远,但考虑到我的起步位置,还是相当不错的。但是就像许多事情一样,我们总是可以越来越远地优化。

在这种情况下(嘿),我可能会尝试开发一个 4.0 版本,但主要是为了我自己的享受。就功能而言,它已经存在:它适合在一起,它可以工作,它很小,只有 75.8 x 54.7 x 57.5 毫米,在传感器点周围有一点密封剂,我很确定它可以抵御天气。

说到软件,我其实没什么好抱怨的(除了代码质量,这不是这个项目的重点)。我们之前构建的 SD 卡上的基础映像非常好用(我用一个新的 Pi 对焊接版本进行了测试),Pi 在启动时启动服务,不会热运行,因此不会使用大量资源,只是做它被告知的事情。

所以,总的来说,最终的 4.0 版本应该-

  • 有点地位的领导
  • 无论在哪里,都不要依赖杜邦连接器
  • 使用焊接连接,不使用试验板
  • Pi号安全就位
  • 可能会为传感器本身提供更多专用外壳
  • 并且整体更加坚固

结论

嗯,我必须说,我很喜欢这个项目,可能比以前的项目更喜欢。诚然(我以前说过),我不会确切地把它描述为必要的——但我也敢打赌,种植那么多蔬菜,然后把它们变成泡菜(因为两个人吃不了那么多),或者乳酸发酵辣椒变成辣酱(出于同样的原因),或者熏制我自己的培根(因为商店买的培根相当平淡无奇)是必要的,特别是如果你在离家 10 分钟的地方有一家杂货店的话。

然而,如果我们忽略花在它上面的时间和金钱,最终的绿色盒子实际上是非常有用的:它确实从园艺工作中去掉了许多猜测。当然,它没有解决许多其他问题,比如可怜的糖豌豆被真菌杀死了——但它确实提供了有趣的数据。

比如,看看这些人:

小胡萝卜(作者)

在这张照片拍摄前 10-20 天,胡萝卜应该已经成熟,即大约 5 英寸长,1 1/2 英寸宽。胡萝卜享受充足的阳光——而在它们被播种的地方,它们可能得不到充足的阳光。凭借科技的力量,我可能不会再把它们种在那里了,因为我现在知道,它们得不到它们需要的 6 小时的充足阳光。

然而,从更广泛的层面来看,这也是一次令人惊叹的学习经历。如果你的日常工作是软件工程师(或任何模糊相关的工作),无论是初级还是高级,你可能会和我一样:你不知道电子、计算机辅助设计、焊接、中学物理、I2C/SPI 或以上任何东西是如何工作的。这根本不是日常工作的一部分,尽管从技术上来说与日常工作密切相关。

然而,正如本文最后几节所展示的,整个过程也非常(我讨厌自己使用这个词)敏捷。产品永远不会完成,但是在某一点上(在我的案例中,接近我的居家度假结束时),一个 MVP 必须被交付——即使我有很多要抱怨的(即,很多要改进的)。至少在我看来,拥有这种特殊的心态是一名优秀工程师的核心特征。

在阅读本文时,你可能会注意到这种迭代方法,因为我在从事这项工作后不久就写了一些章节,而不是在最后提供一个总结——所以如果你愿意,你可以继续这个旅程。

经历这样一个项目,意味着从零开始,随着事情的进展把事情搞清楚,一直到把它与已知的技术集成(从数据工程的角度来看,特别是goSQLpython)只是……整洁。这是一种使用现有技能、增强现有技能并在此过程中学习许多全新事物的有趣方式,比“仅仅”一个软件项目要有趣得多(除非是TempleOS,也许)。

无论如何——我希望你也学到了一些东西,或者至少,我发现阅读这段旅程和建造它一样有趣。

如果你想自己构建一个:所有代码都可以在 GitHub 获得。你也可以在那里找到所有的 CAD 文件。

所有的开发和基准测试都是在 GNU/Linux [PopOS!2019 System76 Gazelle 笔记本电脑上的 12 个英特尔 i7–9750h v cores @ 4.5 GHz 和 16GB RAM,以及一个 Raspberry Pi Zero W,使用bigiron . local作为端点

原载于 2021 年 7 月 3 日 https://chollinger.com**T21

使用 Streamlit 的 Rational UI 设计

原文:https://towardsdatascience.com/rational-ui-design-with-streamlit-61619f7a6ea4?source=collection_archive---------13-----------------------

数据可视化

从一个角度来看,Streamlit 是 web 开发中的一个倒退,因为它让你将应用程序的逻辑与它的呈现方式混合起来。但是从另一个角度来看,它大大简化了网页设计。

演示应用——作者图片

当蒂姆·伯纳斯·李第一次发明网络时,网页只是文本。当像 Mosaic 这样的第一个图形浏览器出现时,网页也有了图片。

后来,它们变成了应用程序,服务器端程序提供数据来构建网页。这通常需要在 HTML 中用 PHP、Java 或 Visual Basic 编写代码,服务器执行这些代码来构造页面。

结果往往是一顿真正的狗餐——分散在整个 HTML 中的代码并不清晰,因为它是支离破碎的,最重要的是,它还模糊了页面布局。

那些深入思考这些事情的人建议,我们应该将应用程序的逻辑与它的呈现方式分开。一个想法是模型、视图、控制器方法(MVC)。这里的模型是应用程序逻辑,通常是服务器上的一些数据和程序代码;视图是网页在浏览器中的外观,HTML 和 CSS 控制器是将模型连接到视图的代码,从 web 页面获取用户输入,将其提供给模型,并将结果返回到视图中显示。

如果这听起来很复杂…嗯,我能说什么呢-是的,有点复杂。

复杂是因为尽管 MVC 的想法足够简单,但是所需的技术和技巧意味着需要一大堆技能。对于视图,HTML 和 CSS 对于模型,像 Python、Java 或 C#这样的语言(可能还有一些 SQL);对于控制器来说,可能是 Javascript,HTTP 的知识,现在还有 Ajax。

也许你从来没有听说过 MVC,但是许多 web 应用程序框架都是基于这个想法的。

MVC——图片由注册公司提供,公共领域,通过维基共享

因此,Streamlit 将代码和布局混合在一个实体中,这是倒退。但是,虽然这是真的,但它也试图让生活变得更容易,因为我上面提到的所有技术都不再是必要的——您需要的只是 Python 和 Streamlit 库。

那么,我们如何获得简单 Pythonic 方法的好处,而不陷入混合应用程序逻辑和页面设计的复杂性呢?

解决方案是牢记 MVC 方法,将程序逻辑从它的表现方式中分离出来。

Python 和 Streamlit 都可以帮助我们。

我们可以使用 Python 类和函数模块化应用程序,将视图从模型中分离出来,并且我们可以使用 Streamlit 函数构建视图。(Streamlit 最近增加了一个不错的功能:你可以将with 语句用于像st.containerst.columns这样的容器对象,这允许你显示页面的结构。)

MVC 的控制器部分由 Streamlit 负责,并被合并到视图中。

让我告诉你我的意思。

一个简单的模板

这是一个简单的模板,我们可以用它在 Streamlit 中构建一个 MVC 风格的应用程序。

##############################################
# A simple model/view template for Streamlit #
##############################################import streamlit as st################ Model ################
class Model:
   caption = "This is some text"################ View  ################
def view(model):
   st.write(model.caption)################ Start  ################
view(Model())

如你所见,这里有四个部分:导入,一个名为Model的类,一个名为view 的函数,最后一行调用带有参数Model 实例的view 函数。

这再简单不过了。Model 类只有一个属性caption,view 函数用它来写屏幕。

为了清楚起见,最后一行相当于创建一个类型为Model的对象,并将其传递给view函数。大概是这样的:

m = Model()
view(m)

真正的应用程序

现在,让我们假设我们想要设计一个简单的应用程序,看起来像本文顶部的图像。这已经成为人均国内生产总值与预期寿命之间关系的经典例证,并基于 Gapminder 已故汉斯·罗斯林的工作(如果你不知道 Gapminder 或汉斯·罗斯林,一定要关注链接)。

应用程序的布局如下所示:

应用程序布局—按作者分类的图片

这是我们视图的基础:一个标题,下面是两列;右边的列包含我们想要显示的图表;左边一栏包含一些描述页面的文本,下面是一个滑块,允许用户选择要在图表上显示的年份。

下面是 Streamlit 视图的伪代码

# Header
st.header(*caption*)# Two columns: 
#  1\. Commentary and slider
#   2\. Main chart
commentaryCol, chartCol = st.columns(*dimensions*)with commentaryCol:
   # Show commentary text
   # Show year Slider
with chartCol:
   # Display Main Chart

你可以看到,我希望代码能够反映布局图——我们只需要填写详细的代码。

在此之前,我们还需要考虑视图需要什么数据。这是四件事:标题的标题,评论文本,滑块需要的参数,最后是要显示的图表。该数据将由模型提供。

让我们开始看代码。

进口

进口情况如下:

import streamlit as st
import pandas as pd
import plotly.express as px

我们需要 Streamlit(当然),Pandas 来保存 Gapminder 数据,并 Plotly 来绘制图表。

模型应该出现在程序文件的下一个位置,但是我们现在要把它放在这里,看看我们是如何开发视图的。

景色

视图是一个名为view的函数,它包含创建网页的所有 Streamlit 代码。我们将模型的一个实例传递给它,Streamlit 需要的所有数据都将来自那里。例如,标题标题保存在模型中一个名为header的属性中,因此编写标题的 Streamlit 代码将是st.header(model.header)

这是完整的代码,是我们之前看到的伪代码的发展。

################ View  ################
def view(model):
   st.set_page_config(layout = 'wide') # Header
   st.header(model.header)

   commentaryCol, spaceCol, chartCol=st.columns((2,1,6)) # Description
   with commentaryCol:
      st.write(model.description) # Year Slider  
   year=st.slider(model.sliderCaption,
      model.yearStart, model.yearEnd,
      model.yearStart, model.yearStep) #Chart
   with chartCol:
      st.plotly_chart(model.chart(year), 
         use_container_width = True)

注意,我使用了with语句来显示下面的代码是针对哪个 UI 元素的。这是最近发布的 Streamlit 库的一个很好的改进,使代码更容易阅读。

代码中的第一件事是将页面布局设置为。这不是必要的,但我认为最终结果看起来更好。

接下来是主要内容:

  • 页眉:这只是在页面顶部显示model.header
  • 一组列:有三列,右边的一列保存图表,左边的一列保存描述和滑块,中间的一列在另外两列之间留出一点空间。
  • 描述进入commentaryCol并写出model.description
  • 我们将用来选择年份的滑块也放在描述下方的commentaryCol中。滑块需要 5 个参数,一个标题,起点和终点,初始值和一个步长。这些由代码中所示的model属性提供。
  • 图表放在chartCol中,是对st.plotly_chart的调用。要显示的实际图表是从名为chartmodel中的方法返回的。该方法需要将年份作为参数传递。

最终的代码和我们之前看到的一样:它调用了view,并将Model的实例化作为参数。

################ Start  ################
view(Model())

模型

在模型中,我们需要提供视图需要的所有数据。大部分工作是在构造函数方法__init__中完成的。当从Model创建一个对象时,调用该方法。首先要做的是将 Gapminder 数据加载到 Pandas 数据框架中——Gapminder 数据包含在 Plotly 库中,因此我们正在使用它;在真正的应用程序中,这些数据可能是从数据库或 API 中加载的。

作者的 Gapminder 数据图片片段

从 Gapminder 数据中,我们设置了许多属性,一个独特年份的列表(我们将其从原始的 Numpy 类型转换为 Streamlit 似乎更喜欢的普通旧 Python int)。

然后,我们设置滑块将使用的开始、结束和步长值。

接下来是chart方法,它可以绘制一个散点图。

最后,我们为视图将使用的各种字符串设置属性。

################ Model ################
class Model:
   def __init__(self):
      self.df = pd.DataFrame(px.data.gapminder())
      self.ylist = [int(i) for i in self.df['year'].unique()]
      self.yearStart = self.ylist[0]
      self.yearEnd = self.ylist[-1]
      self.yearStep = self.ylist[1]-self.ylist[0] def chart(self,year):
      return px.scatter(self.df[self.df['year'] == year],
         x = 'lifeExp', y = 'gdpPercap', 
         title = f'Year: {year}',
         color='continent',size='pop') header = 'Global Statistics from Gapminder' description ='''
      See how life expectancy changes over time 
      and in relation to GDP.
      Move the slider to change the year to display.
   ''' sliderCaption='Select the year for the chart'

如果您现在将这些代码部分按照正确的顺序放在一起,您将拥有一个完整的 Streamlit 应用程序。

我们得到了什么

我们现在有了一个具有独立视图和模型的应用程序。如果我们想改变应用程序的外观,那么根本不需要修改模型代码,只需要修改view功能。

这使得更新和维护代码更容易,也使得代码更容易阅读和理解。如果出于某种原因,我们决定从其他地方获取数据,那么需要改变的只是模型代码,而不是视图。

这是一个相当小的例子,但是我希望你能看到这个 rational 方法将扩展到更复杂的应用程序,并使它们更容易编写和维护。

对于我给出的例子,我有一个疑问:滑块需要的值应该在视图中计算,还是应该放在哪里?在模型里?我这样做是为了一个更清晰的视图,但有一个论点是,这些值只是因为我们使用滑块的方式而计算的,所以在那里推导它们可能更合适。

我很高兴听到你对这种方法的看法,所以请在下面随意评论。

一如既往地感谢阅读,如果你想知道我什么时候发表新文章,请考虑在这里注册一个电子邮件提醒

这篇文章的代码很容易从这里剪切和粘贴,但也可以在我的 GitHub 页面上找到。这里还有一个演示页面——从下拉菜单中选择 Rational UI 设计

如果你不是一个媒体订阅者,那就注册吧,这样你就可以每月花 5 美元阅读尽可能多的文章。在这里注册我会赚一小笔佣金。

https://alanjones2.github.io

Python 中的原始图像处理

原文:https://towardsdatascience.com/raw-image-processing-in-python-238e5d582761?source=collection_archive---------14-----------------------

机器学习流水线的原始图像预处理

UnsplashShareGrid 拍摄的照片

几乎所有现代相机都捕捉原始格式的图像,并以一种通常称为 sRGB 的格式进行处理,这种格式适合人类观看。然而,有人可能想知道所有的技术是用来转换成 sRGB 格式的原始图像是什么?为什么有必要?此外,人们可能想知道如何使用原始图像或以某种方式处理它们,以在一些机器学习任务中获得更好的性能。除了为每个过程提供一步一步的 python 代码之外,本文还试图回答所有这些问题。

Snapchat、Instagram 等社交媒体 app 上的大部分滤镜。,使用机器学习。这些过滤器背后的机器学习算法使用原始图像来处理过滤器的图像,以给出实时结果。因此,在设计使用原始图像的算法时,了解什么是原始图像以及相机如何处理它变得越来越重要。

到底什么是 raw 图像?

原始图像可以被定义为由照相机捕获的最低限度处理的图像。还需要通过软件方法来处理背景噪声、对比度、黑电平等。在大多数情况下,原始图像对人眼来说是不愉快的,并且需要被处理以令人愉快地观看。相机是如何捕捉原始图像的,相机传感器是如何工作的?

图像传感器是如何工作的?

图像传感器可以被认为是一个电路,由一个表面组成,用于捕捉相机快门的电磁波或当传感器暴露在光线下时。传感器的表面捕捉电磁波的强度,也称为光,在捕捉时入射到表面上。该表面可以被认为是一个 2D 阵列,其中每个元素存储入射光的强度。但是,由于只存储光的强度,传感器无法理解光的颜色。那么,传感器如何检测场景中的颜色呢?

为了检测传感器中的颜色,使用了各种技术;其中最常见和最广泛使用的是拜耳过滤传感器并在此讨论。

彩色滤光片的拜耳排列(来源:https://en.wikipedia.org/wiki/Bayer_filter)

Bayer 滤波器用于通过使用滤波技术将输入的电磁信号映射到 RGB 空间。在光到达传感器之前,使用波长滤波器将入射光过滤成红色、绿色和蓝色。使用这种技术,可以知道特定颜色(在这种情况下,红色、绿色和蓝色)的强度。如图所示,红色、绿色和蓝色强度交替存储在 Bayer 滤波器中。一些相机上还使用了其他滤镜图案,但拜耳滤镜图案是使用最广泛的一种。

如何从图像中获取颜色通道

原始图像是 2D 阵列,包含各种波长/颜色的光强度信息。为了获得一个颜色通道,我们需要分离每种颜色的像素,然后将它们组合成一幅图像。然而,很容易看出绿色像素的数量是彩色像素的两倍。在这种情况下,相邻绿色像素的值被平均以获得单个值。因此,对于 H×W 大小的原始图像,获得的最终 RGB 图像是 H/2×W/2×3。

原始图像包含什么?

原始图像文件通常包含在从 Bayer 滤波器传递之后记录在图像传感器上的 2D 阵列形式的图像。该文件包含大量关于相机、光圈、照明条件等的元数据。这有助于图像的后处理。一些常见的元数据类型是黑电平、白电平、方向、色彩空间变换等。,这将在本文中讨论。所有这些步骤都需要在图像上完成,以将其转换为所需的格式来保持质量。

现在,我们将详细讨论一些步骤以及 python 代码:

黑色电平:

黑电平定义为图像最不强烈/最暗部分的强度。有必要在后处理过程中校准图像的黑电平,以获得原始原始图像中不存在的完美黑色像素。各种算法用于校正图像中的黑电平,超出了本文的范围。

方向:-

在某些相机中,图像是垂直反转存储的,因此元数据的方向信息有助于在这种情况下校正图像。相机中的镜头将图像以倒置的形式投射到传感器中。有时候,也是左右翻转。镜头的方向效应通常在相机内部进行校正,不需要在后处理过程中进行校正。

色彩空间转换:-

这几乎是所有图像处理流程中的最后一步。将处理后的图像转换到所需的颜色空间,如 sRGB、YCrCb、灰度等。,然后存储在磁盘中。最常用的颜色空间是 sRGb 颜色空间。执行色彩空间转换后,图像以形式存储在磁盘上。png,。jpeg 等。,图像存储格式。

相机管道比我们在这里讨论的要复杂得多,但本文讨论的细节足以在机器学习管道中使用原始图像数据。

欢迎在评论区提问。

基于 Python 的食品生产原料优化

原文:https://towardsdatascience.com/raw-materials-optimization-for-food-manufacturing-with-python-fbf2be4a74?source=collection_archive---------15-----------------------

使用线性规划创建一个满足特定营养需求的廉价餐吧的最佳食谱

优化餐吧制造的配方—(图片由作者提供)

如何用 Python 优化原材料?

作为一家大型食品制造商的 R&D 经理,你需要设计一家新的便宜又健康的 餐吧的食谱。

餐吧必须起到 T4 餐的作用,而不仅仅是填补空缺的小吃。

照片由 Unsplash 上的sprouted拍摄

这是一个带有目标和约束的线性规划问题陈述。

在这篇文章中,我们将看到如何建立一个模型来设计这个最佳食谱。

💌新文章直接免费放入你的收件箱:时事通讯

如果你喜欢看,看看这篇文章的视频版本

一.问题陈述

方案

7 种配料可供选择

  • 肉类:鸡肉、牛肉、羊肉
  • 非肉类:大米、玉米、麦麸、花生

这些成分每克含有不同的营养成分(以克计)

每种成分的营养成分—(图片由作者提供)

它们也有不同的成本(美元/克)

每种成分的每克成本—(图片由作者提供)

目标

最小化每根棒(重量:120 克)的总成本。

约束条件

蛋白质和纤维的最低值

  • 蛋白质: 20g
  • 纤维: 6g

脂肪、盐和糖的最大值

  • 脂肪: 22g
  • 盐: 3g
  • 糖: 30g

结果

  • 场景 1
**Cost per Bar = 7.91 $**Qty_Beef = 48.56 g
Qty_Chicken = 0.0 g
Qty_Corn = 0.0 g
Qty_Mutton = 0.0 g
Qty_Peanuts = 34.09 g
Qty_Rice = 0.0 g
Qty_Wheat_bran = 37.36 g

你只需要放入牛肉、花生和麦麸,总成本为每条 7.91 美元(120 克)。

  • 情景 2: 将蛋白质水平降至 12 克
**Cost per Bar = 0.78 $**Status: Optimal
Qty_Beef = 0.0 g
Qty_Chicken = 0.0 g
Qty_Corn = 0.0 g
Qty_Mutton = 0.0 g
Qty_Peanuts = 43.15 g
Qty_Rice = 55.19 g
Qty_Wheat_bran = 21.66 g

你的酒吧现在是纯素食,而且相当便宜。

  • 场景 3: 将糖减少到 20 克
**Cost per Bar = 10.32 $**Status: Optimal
Qty_Beef = 65.32 g
Qty_Chicken = 0.0 g
Qty_Corn = 0.0 g
Qty_Mutton = 0.0 g
Qty_Peanuts = 30.96 g
Qty_Rice = 0.0 g
Qty_Wheat_bran = 23.72 g

为了保证低糖,你需要多吃肉。因此,你会看到你的成本每酒吧增加。

http://samirsaci.com

二。构建您的模型

Pulp 是一个线性(lp)和整数规划(IP)问题的建模框架,用 Python 编写,由 COIN-OR Foundation(运筹学计算基础设施)维护。

你可以在这个 Github 库中找到完整的代码: 链接
我的投资组合与其他项目:
小萨奇

1.导入参数

您也可以在我的 GitHub 存储库中找到这些数据集。

营养知识

配料成本

2.声明您的变量、参数和模型

  • 你的目标是最小化你的酒吧的成本
  • 下限 =0:配料数量不能为负值

3.定义目标并添加约束

4.求解模型并分析结果

三。后续步骤

关注我,了解更多与供应链数据科学相关的见解。

这个解决方案并不完美,你可以很容易地改进它。

  • 如果你的顾客想要玉米怎么办?

您可以通过添加玉米最小数量的限制来将玉米添加到此食谱中。

  • 我们有无限的可能性吗?

让我们试着把数量从 120 克换成 100 克,结果是什么?

Cost per Bar = 11.08 $**Status: Infeasible**
Qty_Beef = 71.27 g
Qty_Chicken = 0.0 g
Qty_Corn = 0.0 g
Qty_Mutton = 0.0 g
Qty_Peanuts = 30.26 g
Qty_Rice = 0.0 g
Qty_Wheat_bran = -1.53 g

对于这种约束组合,100 克的杆没有最优解。

要获得最小量,首先列出限制(营养事实),然后尝试找到可行的最小量。

关于我

让我们连接上 LinkedinTwitter ,我是一名供应链工程师,正在使用数据分析来改善物流运作和降低成本。

如果你对数据分析和供应链感兴趣,可以看看我的网站

https://samirsaci.com

参考

[1]运筹学计算基础设施,优化与纸浆(文档),链接

RBO v/s Kendall Tau 比较项目清单

原文:https://towardsdatascience.com/rbo-v-s-kendall-tau-to-compare-ranked-lists-of-items-8776c5182899?source=collection_archive---------7-----------------------

在解决排名问题(提要排名、搜索排名等)时。),我经常遇到需要比较不同系统生成的排序列表的情况——例如生产 v/s 和 A/B 测试。比较两个或更多排名列表的需要比你想象的更普遍。让我们考虑一个简单的例子:你和几个朋友决定列出流行电视连续剧《老友记》中你最喜欢的 5 集。每个人都写下他们最喜欢的 5 集标题,第一项表示他们最喜欢的,最后一项表示他们最不喜欢的集。你们每个人现在可能都想知道你的清单与房间里的其他两个朋友有多相似或不同。

图 1 由 3 个不同的人排列的情节

我们如何着手比较这些列表?形式上,这些排名列表被称为不完整的排名,因为它们没有对《老友记》中的所有剧集进行排名,而是只选择了前 5 名。我们将讨论一些在解决这个特殊问题时流行且相当普遍的方法。

方法一:肯德尔·陶

肯德尔τ度量也称为肯德尔相关性,是用于检查两个排序列表是否一致的常用方法。

图 2 按人 1 和人 3 排列的情节的并排比较

肯德尔相关性(𝛕)可以通过首先计算一致对的数量(c)和不一致对的数量(d)来计算。如果一对在它们的排序列表中以相同的顺序出现,则称它们是一致的。

M = (C -D)

𝛕 = M / (C + D)

在我们的示例(图 2)中,两个联合排名列表之间不同对的最大数量是= 5 Choose 2 = *(5 *(5–1))= 10。如果所有对都一致,那么 C = 10,D = 0,这意味着 max(𝛕)=(10–0)/(10+0)= 1。如果所有对都不一致,那么 C = 0,D = 10,这意味着 min(𝛕)=(0–10)/(0+10)=-1。

让我们来计算一下𝛕的实际价值

和谐对:

(S6E17,S2E10) (S6E17,S3E24) (S6E17,S3E25) (S6E17,S3E15) (S2E10,S3E24) (S2E10,S3E25) (S2E10,S3E15) (S3E24,S3E25)

不和谐对:

(S3E24,S3E15) (S3E25,S3E15)

𝛕 = (8–2) / (8 + 2) = 0.6

解读肯德尔·陶

请注意,𝛕可以采用的最大值和最小值分别是+1 和-1。+1 表示完全同意,-1 表示完全不同意。值为 0 表示排名之间没有相关性。我们为图 2 计算的值 0.6 向我们显示了等级具有中等相关性

另一种解释𝛕的方式是,它的统计量 M 是排列一个排序列表使其与另一个相似所需的相邻两两交换的数量。这是否让你想起了一个流行的排序算法?当然是泡泡排序啦!

您还可以测试肯德尔τ指标的统计显著性[8]。

履行

修改的 Kendall Tau 可以应用于包含平局的排名——例如,如果一个人非常喜欢一集,以至于他们将同一集列在他们列表中前 3 名的所有 3 个位置中。排名中的联系并不少见。我最近在网飞实现了一个定制的 O(NLogN) Kendall Tau 算法来处理平局。以下是该算法的一些开源实现:

  • [Java] Apache commons
  • 【Python】SciPy

习惯

Kendall Tau 是一种流行的方法,用于信息检索任务,如搜索引擎和推荐系统。像雅虎[1]和微软[3]这样的公司已经在各种用例中使用了这种方法。

缺点

尽管 Kendall Tau 是一种流行的等级相关性度量,但它也有缺点:

  1. 它要求两个排名列表是相连的(两个列表中的元素相同)
  2. 它是不加权的,也就是说,它对列表底部的分歧和顶部的分歧同样重视(在流行的搜索引擎中,“头部”的结果比“尾部”的结果更重要)
  3. 单个不和谐对的贡献随着排序深度的增加而减少,即𝛕值与排序列表的深度有内在联系。

为了证明这些缺点,考虑来自图 1 的人 1 对人 2 的排序列表。请注意,两个列表之间只有两集是相同的:S2E10 和 S3E24。Kendall 相关性不能应用于这些列表,因为它们是不相连的(不包含完全相同的项目)。将 Kendall 相关性应用于这两个列表的一个潜在方法是用特殊字符“#”替换两个列表中不存在的项目。然后,人物 1 排名变为“# S2E10 # S3E24 #”,人物 2 排名变为“# S2E10 # # S3E24”。即使 S3E24 在人 1 的列表中位于位置 3,在人 2 的列表中位于位置 4(基于零的索引),计算这些变换列表的𝛕也将得到值 1.0。这个值 1.0 表示两个列表之间完全一致—显然这里不成立。

方法 2:秩偏重叠(RBO)

与 Kendall Tau 距离度量不同,RBO 是一种相似性度量。RBO 通过使用每个等级位置的权重解决了在 Kendall Tau 中观察到的 3 个缺点。权重是从一个收敛的序列中导出的。下面的等式描述了两个无限排名列表 S 和 T 的 RBO:

参数 p 是范围(0,1)内的可调参数,其可用于确定顶部 d 等级对 RBO 相似性度量的最终值的贡献。注意 SUM( p ^( d -1))的求和项是收敛的,因为它是一个几何级数。它的和由 1/(1- p 给出,由于 0 < p < 1,所以和是有限的。

为了得到一个单一的 RBO 分数,我们可以从现有的信息中进行推断,假设深度为 k 的协议在两个列表中无限延续。单一 RBO 分数可以使用以下公式计算:

以下是 Python 3 中外推的单一得分 RBO 测量的简单(未经彻底测试)实现:

如何选择‘p’的值?

在 RBO 度量中, p 值的选择决定了最终的 RBO 度量所表现出的最高加权度。根据 RBO 论文,以下等式可用于计算最高 d 级对 RBO 计算的总权重:

下面是上述内容的一个简单(未经彻底测试)实现:

对于 p = 0.9 和 d = 10 的值(正在检查前 10 个等级),WRBO[1:d]是 0.8556,即前 10 个等级对最终的 RBO 度量贡献了 85.56%。因此,根据您希望从顶部 d 结果中获得的贡献量,您可以使用上面的 WRBO[1:d]等式相应地选择 p 的值。

解读 RBO

如上所述,RBO 取范围[0,1]中的值,其中 0 表示不相交,1 表示相同的排序列表。

让我们回到 Kendall Tau 未能识别由人 1 和人 2 生成的明显不相似的排序列表的例子:

人物 1 的排名是“# S2E10 # S3E24 #”

人物 2 的排名是“# S2E10 # # S3E24”

我将选择 p = 0.6,因为我希望前 3 个位置承载最大的权重。在 p = 0.6 的情况下,在最终的 RBO 度量中,前 3 个位置承担 91.26%的权重。代入人 1 和人 2 的值,我们得到 RBO(人 1,人 2,p,k) = 0.24144,即,RBO 值指示两个列表大部分不同,在前 3 个位置有一些轻微的相似性。将 p 的值增加到 0.9 导致更高的 RBO 值 0.352655,但是该值能够显示两个列表是不同的。

总之,RBO 是判断不一定相连的不定排名列表的相似性的更健壮的度量,并且它能够被调整用于最高加权。要进一步阅读 RBO 及其背后的理论,请参考 RBO 的论文[2]。

进一步阅读

我们通过一些方法来比较两组不同的排名。以下是进一步阅读的一些其他方法:

  • 皮尔逊相关[5]
  • Kolmogorov-Smirnov 检验[6]
  • 比较前 k 名列表[7]

一些值得思考的问题

最后,我想留给你们一些思考的东西:

想象一下,比较列表中的任意两个项目并不那么简单。通过拍摄足球世界杯的照片,说出您对该赛事最喜爱的 5 大赛事的排名。例如,图 3 显示了两幅来自同一事件的图像,当时马里奥·格策打进了决定性的加时赛进球,但它们是相隔几秒钟拍摄的不同图像。如果我们想计算排名表上的 RBO 或肯德尔τ,能够分辨两幅不同的图像是相同的是很重要的。我们如何比较两幅图像并判断它们来自同一事件?

图 3 2014 年德国 V/s 阿根廷世界杯同一赛事两幅图[4]

参考

[1]库马尔、拉维和谢尔盖·瓦西维茨基。"排名之间的广义距离."第 19 届国际万维网会议录。2010.

[2]威廉·韦伯、阿利斯泰尔·莫法特和贾斯汀·佐贝尔 2010 “不确定排名的相似性度量”。 ACM Trans。Inf。Syst。28,4,第 20 条(2010 年 11 月),38 页。DOI:https://DOI . org/10 . 1145/1852202 . 4666666666

[3] Bachrach,Yoram,Ralf Herbrich 和 Ely Porat。"在协同过滤系统中近似等级相关性的草图算法."字符串处理与信息检索国际研讨会。斯普林格,柏林,海德堡,2009。

[4]足球世界杯图片归属:

[左图]Danilo Borges/Portal da Copa copa2014.gov.br licensa Creative Commons a tripbui o 3.0,CC BY 3.0<https://creativecommons.org/licenses/by/3.0>,通过 Wikimedia Commons

[右图]Danilo Borges/Copa 2014 . gov . BR licensa Creative Commons a tripbui o 3.0 Brasil,CC BY 3.0 BR<https://creativecommons.org/licenses/by/3.0/br/deed.en>,通过 Wikimedia Commons

[5] Benesty,Jacob,等.“Pearson 相关系数.”语音处理中的降噪。斯普林格,柏林,海德堡,2009。1–4.

[6] Massey Jr,Frank J .“拟合优度的 Kolmogorov-Smirnov 检验”美国统计协会杂志46.253(1951):68-78。

[7]费金、罗纳德、拉维·库马尔和达克希纳穆尔西瓦库马尔。"比较排名前 k 的名单." SIAM 离散数学杂志17.1(2003):134-160。

[8]肯德尔τ显著性检验https://en . Wikipedia . org/wiki/Kendall _ rank _ correlation _ coefficient #显著性检验

重新评价卡夫卡:实时的问题和选择

原文:https://towardsdatascience.com/re-evaluating-kafka-issues-and-alternatives-for-real-time-395573418f27?source=collection_archive---------4-----------------------

行业笔记

卡夫卡的挑战让许多工程师在通往成功数据流的道路上筋疲力尽。如果有更简单的方法呢?

图片由穆罕默德·哈桑皮克斯拜拍摄

与大多数使用 Apache Kafka 的数据工程师交谈,他们会有很多话要说。他们可以列出这个平台让他们感到沮丧的所有事情,但是以他们喜欢这个平台的事实作为结论。

这是怎么回事?

Apache Kafka 是现代数据基础设施的基石。在许多方面,它都很出色,但它也带来了一些复杂的问题,对一些用户的影响比其他用户更大。在过去十年左右的时间里,我们已经把卡夫卡视为同类作品中唯一的选择,我们也接受了它将不可避免地挑战我们的观点。

但是随着数据领域的迅速扩大,我们不应该继续认为这是理所当然的。可行的、方便用户的替代品正在出现。为了节省时间和精力,值得重新评估最成熟的系统(如 Kafka)是否是您组织的最佳选择,或者更新的系统是否更适合。

什么是阿帕奇卡夫卡,它是做什么的?

Apache Kafka 是业界领先的开源事件流媒体平台。由 LinkedIn 员工于 2010 年创立,后来捐赠给了阿帕奇基金会。Kafka 被誉为一个极其强大的平台,为数千家公司提供数据基础设施基础,包括网飞、Airbnb 和 Twitter 等知名公司。

卡夫卡被用作事件总线。应用程序将事件或消息生成到 Kafka 中,Kafka 将它们记录到有序的消息历史中。其他应用程序按顺序使用这些消息,并在产生新消息时得到实时通知。传统数据库非常适合针对当前状态进行查询和更新,相比之下,Kafka 擅长于必须对导致当前状态的各种变化做出快速反应的应用程序。

Kafka 运行在一个容错服务器集群上。被称为生产者的客户端系统向 Kafka 服务器发布或写入事件。另一组叫做消费者的客户订阅或阅读这些事件。消息包含在主题中,可以进一步划分。

Kafka 在一些重要的方面提供了一个替代旧消息队列(如 RabbitMQ)的选择,主要是:

  • 通过向集群添加更多的节点和向单个主题添加分区,它可以很容易地进行水平扩展
  • 它可以在一段可配置的时间内保存消息,而不是在消息到达消费者时立即删除它们

卡夫卡有什么好?

正如我们所看到的,Kafka 是第一个此类事件流平台,现在已经有超过十年的成熟度和庞大的用户群。随着对良好管理、低延迟数据流的需求变得越来越明显,即使是最传统的公司也注意到了这一点,并且经常转向 Kafka

对于建立大型、高度定制的数据管道的大型企业,如网飞,卡夫卡可以提供一个支柱。许多其他开源项目都是基于 Kafka 构建的。如果你有一个致力于 Kafka 的工程师团队,或者你有很多时间,你可以在各种各样的用例中获得很好的结果。

简而言之:卡夫卡快速、高效、可定制、强大,并且在管理得当的情况下非常可靠。

卡夫卡的问题是什么?

作为一个中小型组织的商业领袖,你可能会看到卡夫卡在 2010 年取得的突破以及随后网飞世界的成功,并感到兴奋。Kafka——开源且维护良好——能解决所有数据孤岛和延迟问题吗?

不一定。

Kafka 天生就很难实现和维护,以至于许多组织都无法实现他们的目标。作为数据基础设施的主干,Kafka 本身就是一个庞大而复杂的系统。最重要的是,当与客户端系统集成时,它会带来额外的复杂性。

这些特有的挑战造成了很高的准入门槛和持续的维护难题。让我们进入更多的细节。

运行 Kafka 部署是一项庞大而复杂的工作。从本质上来说,你的 Kafka 部署肯定是一个大规模的项目。想象一下,运行一个由多个关键应用程序使用的同样大规模的 MySQL 数据库。几乎可以肯定,您需要雇用一名数据库管理员(或一整个团队)来管理它。卡夫卡也不例外。这是一个大而复杂的系统,往往由多个客户端应用程序共享。当然不好操作!

Kafka 管理员必须从一开始就回答困难的设计问题。这包括定义消息如何存储在分区主题、保留期以及团队或应用程序配额中。我们不会在这里讨论细节,但是您可以将这个任务看作是设计一个数据库模式,但是增加了时间维度,这增加了复杂性。您需要考虑每条消息代表什么,如何确保它以正确的顺序被使用,在哪里以及如何执行有状态转换,等等——所有这些都要非常精确。

卡夫卡是一个低级工具。像 MySQL 或任何其他数据库一样,Kafka 是一个开放式工具,它不会提供一条简单的成功之路。它本身并不能解决实际的业务问题。

为了从 Kafka 中获得任何价值,您需要读写数据的客户端应用程序。卡夫卡本身并不关心这是怎么做到的。相反,它优先考虑处理大量不同用例的灵活性。这是 Kafka 如此强大的部分原因,但是为了使用 Kafka 集群做任何有用的事情,您的客户端应用程序需要提出自己关于如何使用这些灵活的 API 的意见。

你可能听说过“流媒体很难”这句话,这就是为什么。

Kafka 可能是开源的,但其固有的困难导致了纯粹为了管理它而存在的公司的激增——包括由创建 Kafka 的工程团队创建的 Confluent。除了专业的工程师团队之外,它根本不是为处理任何事情而设计的。

除了逻辑上的困难,Kafka 还有一些其他的限制,使得它对于某些用例来说不太理想。

  • 即时转型极具挑战性。 Kafka 提供了一个消费者框架,可用于在运行时支持流媒体应用,但具体细节取决于您。 KsqlDB 或者 Confluent 可以有所帮助,但是自己做起来不太直观。
  • 历史数据是有局限性的。尽管 Kafka 的优势之一是可以持久保存事件,但扩展可能会变得非常困难,并且会受到可用磁盘空间、服务器和持续数据迁移的限制。Kafka 最初是为本地存储而设计的,因此在云上部署它(存储几乎不是问题)需要额外的跑腿工作和不断的妥协。公司应该使用速度较慢但更具可扩展性的网络连接永久磁盘,还是速度较快但短暂的固态硬盘(可能需要更多数据迁移)?
  • 卡夫卡不是为 批量数据 而设计的。实时数据为许多更新的工作流程提供了动力,但批处理范式远未过时。Kafka 不处理 batch 而不进行一点黑客攻击,用户必须特别小心不要让他们的代理过载大规模的批处理读取——同样的代理负责记录他们新的实时数据

用新鲜的眼光评价卡夫卡

想象一下,经过漫长的过程并克服了无数令人沮丧的障碍,您已经成功地将 Kafka 部署为您组织的数据管道的中心元素。你也已经弄清楚谁将管理系统的不同方面,如何管理组成新团队的人

你觉得很有成就——这是理所当然的。你做了一些有挑战性的事情,建立了一个有价值的系统。到目前为止,你和卡夫卡已经一起经历了很多,你可以可靠地识别和安抚它的大多数怪癖。

但是有几个假设支撑着这个场景:

  • Kafka 是解决数据基础设施问题的唯一解决方案
  • 您的系统的价值值得大量的(时间和精力)成本

我们已经接受了卡夫卡的现状,因为多年来它提供了同类中唯一的解决方案。如果获得一个快速、健壮、可扩展的流媒体管道需要一些麻烦,那就这样吧。这是我们必须付出的代价。

在某种程度上,这是有意义的。我们都习惯性地认为,提供高价值的东西也必然伴随着高成本。对于 Kafka 部署来说,这种成本表现为时间和精力——对于一个公司来说,这基本上与金钱是一回事。

对于实物商品,随着时间的推移,制造业的进步和竞争的加剧会降低成本。就技术而言,给定解决方案所需的时间和精力同样会减少。说到创建可扩展的实时数据管道,可能是时候重新评估价值和成本了。

在上面这样的场景中,也许我们可以重新定义我们对成功的概念:不是战胜系统固有的复杂性,而是选择一个解决方案,用最少的时间和精力满足我们的需求。

重新思考数据管道的成本和价值

许多技术系统,包括用于数据基础设施和分析的系统,正处于类似于工业革命的增长时期。

我们创建复杂系统的能力有了显著的提高,我们关于数据基础设施可以和应该是什么的想法也发生了变化。初创公司正在为高级分析和数据存储开发新产品和服务。或许更重要的是,正如 Bessemer Venture Partners 在本文中描述的那样,他们正在使数据集成变得更加简单。

作为其中一家初创公司——河口公司的员工,很容易看到这种转变将在未来几年产生的影响。

但是这对你意味着什么呢?这意味着你可以比以往任何时候都更自由地评估 Kafka(或任何其他大牌数据平台)是否适合你。您可以定义什么使系统对您的组织有价值,并使用它来评估您不断增长的选项库。

对于卡夫卡来说,几个备选方案包括阿帕奇脉冲星小熊猫StreamSets ,以及河口的新产品 Flow

现在,您完全可以期待系统以更少的努力和混乱提供可伸缩性和灵活性。随着技术呈指数级发展,对您的数据基础架构有更高的期望是合理的!

所以问自己一些问题,比如:

  • 什么对你更重要:成熟度和用户社区,还是易用性和新的增值?
  • Kafka 的特定架构是否与您的业务目标保持一致,以至于物流挑战是值得的?
  • 或者你只是在寻找一个灵活、可靠、实时的数据管道——不一定是卡夫卡?
  • 如果您已经有一个 Kafka 部署,您的团队目前花费在管理它上的一些时间和精力可以用在其他项目上吗?

经过这一切,Kafka 很可能是你的组织的最佳选择;你可能会发现时间和努力的代价是值得的。新的替代方案虽然发展迅速,但可能仍处于让您的组织感到不舒服的早期阶段。

无论哪种方式,实时数据基础设施空间都是一个值得以客观的心态在一致的基础上重新评估的空间。直截了当、无摩擦的数据基础设施是可能的,随着数据在我们现代世界的重要性,我们不应该期望任何东西减少。

这个故事的一个版本最初发布在 河口博客 上。

重新想象时尚行业-第一部分

原文:https://towardsdatascience.com/re-imagine-the-business-of-fashion-part-1-cc8037cbef6d?source=collection_archive---------23-----------------------

时尚品牌和零售商如何通过高级分析重新想象时尚?

安德里亚·皮亚卡迪奥像素上拍摄的照片

我对数据分析的兴趣始于 2020 年,当时我偶然发现了一篇关于时尚行业不同分析案例的文章。老实说,这是一篇很长的阅读,总共约 60 页,但我喜欢它的每一点,远远超过我读过的任何其他时尚技术相关的文章。

这激发了我对时尚分析的兴趣,尤其是在非洲时尚行业。

遗憾的是,我在非洲时尚界找到的关于这个主题的信息很少,每次我向人们提到我在探索时尚界的分析时,他们都觉得有点奇怪,但很有趣。

我发现,没有多少时尚专业人士意识到数据分析在时尚业务中的应用。

我没有独自启发身边的人,而是得出结论 【非洲的时尚需要被重新想象】 并且我开始思考我可以如何为此做出贡献,这就是我创办时尚科技博客的原因。

每篇文章都旨在启发和教育时尚技术爱好者、年轻专业人士、设计师、零售商和学生,让他们了解高级分析和机器学习对时尚行业的破坏。

如果你属于上面提到的任何一类,这篇文章肯定适合你!

在深入研究高级分析的应用之前,让我先介绍一下新冠肺炎疫情对时尚零售的影响。

照片由蒂姆·道格拉斯派克斯拍摄

新冠肺炎·疫情对时尚界的影响

根据 Fashion United 的数据,时尚产业的价值超过 3000 万亿美元,约占全球国内生产总值(GDP)的 2%。

2019 年,全球服装和鞋类市场约为 2650 亿美元,仅耐克一家就创造了超过 390 亿美元的收入,预计到 2030 年,该市场将达到 3.3 万亿美元。

尽管 covid 19 大流行带来了前所未有的惊喜,时尚专家仍然预测 2021 年在线业务将增长 20%或更多。

有趣吧?

不幸的是,并不是所有的企业都能迅速适应疫情带来的变化。虽然一些企业很快接受了分析(表现更好 68%,来源:麦肯锡&公司报告),但其他坚持传统营销或晚于采用技术和创新的企业开始失去市场份额,一些企业倒闭了。

这进一步证明了销售统计预测,即今年拥有数字渠道的品牌将获得至少 20%的收入。

尽管有增长预测,数字化转型仍然是时尚产业的悖论。

有趣的是,麦肯锡报告称,144%的行业利润是由领先的 20%的全球时尚品牌产生的。这意味着一个时尚品牌要想获得可观的利润,它需要在这 20%中。

和我一样,你可能想知道这些时尚品牌是如何保持行业领先地位的

继续阅读……

描述这种增长的简单概括就是【数字化转型与创新】

例如,Zalando 报告 2020 年第二季度商品总量(GMV)增长了 32-34 %, 2021 年第一季度 GMV 增长了 50%。(资料来源:Zalando 出版物)。

这是一个很大的增长,尤其是考虑到全球疫情期间的时尚现状。

你同意吗?

在新冠肺炎危机之前,已经建立并采用数字化转型的公司包括:

  • 高级分析。
  • 机器学习模型。
  • 人工智能
  • 强大的数字/电子商务平台等

已经开始超越没有利用上述任何一种数字化转型的竞争对手。

因此,可以肯定地说,数字化转型并非始于 2020 年,相反,新冠肺炎危机加速了转型,也扩大了上述两类公司之间的差距。

就本文而言,我将重点关注高级分析以及顶级时装公司如何在业务中利用它。

分析的真正含义是什么?

来自像素像素的照片

分析是指发现和交流数据中存在的有意义的观察结果和模式的科学方法。它倾向于根据在给定的原始数据中发现的模式来回答问题。

它本身不是一种独立的方法,它依赖于计算机编程、统计、商业智能以及研究的应用,以便从原始数据中创建解释、量化和获得洞察力。

时尚界的分析已经成为品牌和零售商最常用的工具之一,其应用不仅限于销售预测。其他一些领域包括:

  • 趋势分析
  • 数字分析
  • 风险分析
  • 网络分析
  • 预测建模
  • 销售优化
  • 营销和广告
  • 物流等

根据业务挑战、技术支持和可用资金,在上述任一领域应用分析不仅会增加公司的销售额,还会增加其长期竞争优势。

这是一篇很长的阅读,但我很高兴我们已经涵盖了分析的基础。为了更好地理解利用高级分析的好处,强调时尚品牌和零售商面临的一些挑战是很重要的。

拿起你的咖啡,让我们开始吧!!

时尚公司有哪些挑战?

来自 PexelsRODNAE Productions 的照片

时尚界是一个相当复杂的行业,时尚消费者的偏好变化非常快。

随着对高级分析和其他技术的更多了解,时尚品牌和零售商正在寻求优化数据分析的方法,以便通过针对目标客户的精确个性化来改善产品供应。反过来,更多的销售,更多的营业额,更多的利润!

以下是一些时尚商业挑战:

  • 客户偏好的复杂性和寿命:业务中采用的高级分析方法需要考虑尺寸、颜色、合身程度、风格、地点、季节、生活方式等。这些属性正在迅速变化,因此,仅仅根据前几年的销售数据或直觉来分析你的业务是不够的。
  • 缺乏数据整合:将公司不同部门创建的数据隔离开来,阻碍了数据在分析中的有效使用。
  • 减少产品退货的困难:由于顾客偏好的复杂性,例如尺寸,零售商对退货数量几乎没有控制。回报越多,管理费用就越多。
  • 无法优化可用数据:虽然一些时尚公司拥有大量数据,但不知道如何使用它们,一些品牌没有数据驱动的文化。
  • 无法准确预测趋势:由于上述挑战,品牌和零售商无法准确预测设计和销售趋势。这反过来又会导致生产过剩(最终可能会被填埋)、不必要的折扣/清仓销售、商店停业等。

你期待已久的部分来了……

兴奋?

我也是!!!

高级分析如何重塑时尚产业?

照片由 Liza SummerPexels 拍摄

  1. 分析消费者行为偏好:利用时尚行业的高级分析可以帮助公司深入了解客户行为和需求。这反过来又可以指导回答的问题,例如:
  • 投放什么广告,给谁,在什么时间?
  • 谁有可能在获得折扣后转化?
  • 有哪些适合的产品推荐?
  • 我们如何改善客户体验?等等。

应用数据驱动的情绪和行为分析可以帮助公司更好地了解市场趋势,从而提高转化率。

例如,Zara 收集店内数据,将其发送给市场分析师,让他们理解这些数据,并洞察顾客的需求。

因此,这些见解会被发送给设计团队,由他们来生产服装并投放到 Zara 商店。

另一个例子是消费者数据分析在拉夫·劳伦趋势预测中的应用。通过高级分析,拉尔夫·劳伦可以了解顾客的偏好,如颜色、面料、价格等。

从而提高趋势预测的准确性。

2。预测库存和销售分布:

借助预测分析,时尚公司可以将他们的价格和库存与竞争对手的数据进行比较,这可以帮助他们更好地改善营销活动,并提高销售和需求预测的准确性。

一些时装零售商还应用自然语言处理(NLP)来更好地理解特定目标市场的行话,以更好地了解消费者的购买力、市场趋势以及为正确的受众制作适当的广告。

调整预测分析也有助于通过识别高需求或新兴产品来防止库存过多和库存不足。因此,减少了优化库存所花费的时间和金钱

Zara 是一家成功实施这一技术的时尚零售商,从而优化了供应链规划,确保正确的库存总是在正确的时间被送到正确的商店

3。帮助顾客找到合适的产品

寻找合适的衣服是顾客面临的挑战之一,尤其是在网上购物时。通过预测分析(结合增强现实(AR)和图像处理)等高级分析,客户可以通过使用虚拟试衣间轻松找到适合他们的时尚产品。

这三项技术的结合用于分析顾客的身体尺寸,预测哪件衣服最适合他们,并向他们展示他们穿起来的样子。

听起来很迷人对吧?

照片由来自 PexelsErik Mclean 拍摄

Rent The Runway (RTR)的数据团队分析客户对其身体数据和服装合身程度的评论,以便为拥有类似身体的客户提供更多有用的见解。

这些身体数据包括体型、体重、胸围、服装尺寸、身高、体重等,有时还包括衣服出租的活动、利弊。然后,环球旅行利用这些信息为进一步购买创造有价值的见解。

高级分析可以帮助客户在更短的时间内找到更合适的产品,从而让他们更开心,减少退货,并增加客户继续购买的可能性。

4。增加建立可持续时尚企业的可能性:

高级分析可以帮助时尚品牌和零售商在从设计阶段到库存和交付的价值链的每个步骤中减少浪费。通过提供;

  • 对市场趋势的更好理解。
  • 更好的价格选择。
  • 更好的商品和库存。
  • 更好的采购选择。
  • 趋势预测的准确性更高。
  • 销售分销策略。
  • 更符合建议。
  • 更好的广告和折扣等。

从长远来看,高级分析确实可以让时尚行业变得更加可持续和有利可图。

时尚品牌采用数据分析的例子

  • 德国零售巨头- Zalando
  • 西班牙一品牌
  • 海恩斯莫里斯
  • 亚马孙
  • 租用跑道
  • 缝合固定
  • 拉尔夫·劳伦
  • net-搬运工

提供高级分析服务的科技公司示例:

  • Heuritech
  • WGSN
  • 雷塔隆
  • 趋势分析
  • 净收益

结论

由于高级分析仍在发展和进步,它已经展示了许多前景,并为时尚品牌和零售商创造了一条更有利可图的途径,同时具有可持续发展意识。

尽管有这些承诺和前景,一些时尚公司仍然没有采用这些技术,或者是由于;

  • 资本不足。
  • 缺乏强大的数据技术支持团队。
  • 难以从现有数据中获得深刻见解。
  • 不了解这些技术。
  • 或者完全缺乏数字和数据驱动的文化

虽然高级分析的最终结果是改进营销活动、更好的产品推荐、规划优化、改进分销和网络以及减少浪费,时尚公司需要了解他们的业务挑战,以便选择合适的技术来应用。

点击此链接了解高级分析如何帮助时尚行业变得更加可持续和有利可图。

我希望你喜欢阅读这篇文章?

你能想出更多在时尚界采用高级分析的方法吗?

让我们在聊天环节讨论这个问题。

请点击下面的链接,为我的时尚研究调查做贡献。
👇🏼👇🏼👇🏼👇🏼👇🏼
时尚研究调查
谢谢🤗。

LinkedInTwitter上联系我或者发邮件给 info@data4fashion.org

干杯,
时尚数据女王(Kiitan)

重新想象时尚行业:第二部分

原文:https://towardsdatascience.com/re-imagine-the-business-of-fashion-part-3-efcc6d277d8c?source=collection_archive---------26-----------------------

数据科学可以让时尚产业变得更可持续。真的还是假的?

照片由 Pexels罗恩·拉赫拍摄

时尚行业的对话无疑正在发生变化。随着越来越多的人意识到时尚活动对环境的有害影响,以及在经营一个成功的时尚企业时面临的其他挑战。

其中一些挑战围绕着在保持以客户为中心的业务模式的同时,平衡可持续性与盈利能力的尝试。

继续阅读…

术语 “可持续发展” 已经成为行业中的流行语,一些品牌真正知道它的意思,但却选择遵循 “绿色洗涤之路”。 而其他人不知道它需要什么,假设它主要是关于使用棉花和大麻,忘记考虑浪费的水量,过度生产,产生的烟雾,恶劣的劳动工资和工作条件等。点击此处查看由于棉花消费增加而带来的水资源挑战统计数据

人们对 【可持续时尚】 已经有了很多了解,所以我想告诉你更多关于数据科学如何帮助时尚公司建立更可持续的业务。

走吧!!!

数据科学是一个应用多个学科的研究领域,例如:

  • 机器学习算法
  • 商业智能
  • 探索性数据分析
  • 数据产品工程等

以便从原始数据中提取可操作的见解。它涉及数据清理、分析、处理、高级分析,以及向利益相关者最终呈现见解和观察/模式,以便做出数据驱动的决策。

数据科学所需的一些技能包括:

  • 软件编程
  • 数学
  • 统计数字
  • 批判性思维等。

现在来谈谈这个问题…

数据科学能否帮助时尚行业更可持续发展?

答案是是的!

照片由energepic.com派克斯拍摄

时尚公司只会根据非结构化数据做出设计和销售决策,而不会听取数据对客户偏好和旅程的说明,这样的日子已经一去不复返了。

随着数据科学的采用,一些时尚企业已经能够在实现可持续发展的同时获得更多利润。

数据科学的 4 种独特方式让时尚产业更具可持续性

1。从社交媒体和其他在线渠道获得见解: 例如,Heuritech 利用社交媒体数据来获取特定的服装细节,以便全面了解时尚产品和市场。

Heuritech 抓取数百万公共社交媒体内容,应用计算机视觉技术、机器学习预测算法、市场智能平台,精准预测流行趋势。(来源:Heuritech )

从而减少因生产过剩或分配不当而产生的浪费。

这反过来又有助于时装业更加有利可图和可持续发展。

2预测要生产的产品的正确数量:准确预测未来趋势是不够的,了解不同季节对每种产品的正确需求将确保有效的库存管理,降低生产和分销效率,并帮助企业主做出更加明智和数据驱动的决策。

由伯吉斯·米尔纳在 Unsplash 上拍摄的照片

Zara 成功整合了不同的数字战略和技术,以提高销售额和需求的准确性。

例如,

品牌服装中智能芯片技术的集成产生了实时数据,可用于识别消费者接触最多的时尚产品。

查看下面的文章,深入了解 Zara 如何在供应链管理中利用大数据。

https://olaoluwakiitan-o-olabiyi.medium.com/fashion-and-big-data-application-cb946dd76844

一些时尚品牌在新的生产季节之前通过调查和问卷直接与顾客接触。

这种生产方式有助于控制库存,降低生产成本,减少废物产生,从而减少衣服被填埋的机会。

3。供应链优化:也许设计和销售预测已经非常准确,但仍然需要创建正确的推荐产品并在正确的时间配送到正确的商店。

没有一个合适的分布,再好的趋势预测又有什么意义?

这就是人工智能技术的用武之地。这种方法用于消除时装店记录/数据或电子商务商店买家账户中观察到的购买模式的神秘性。

因此,确保在正确的时间向正确的顾客或商店提供正确的产品分类、供应和推荐。

有趣的是,

在此阶段生成的数据也可以反馈到系统中,以推动需求感知算法并整合价值链不同阶段的数据。

4。有效的促销策略:以某个零售品牌 ABC 为例,它决定为一组特定的商品做一个销售广告。然后才能够全部卖掉!!!。

听起来很棒吧?

照片由 Artem BeliaikinPexels 拍摄

但是我们真的能说 ABC 品牌做了正确的决定来张贴销售广告吗?

如果零售商可以预测广告将如何运作,也许更多的衣服可以更早生产出来。

或者也许折扣就没有必要了。从而提高品牌认知度、销售额和利润。

听起来更可持续对吗?

我同意!

时尚品牌别无选择,只能在一年的不同时间进行销售,而不一定知道结果会是什么的日子已经一去不复返了。借助正确的人工智能技术,时尚品牌可以优化产品推荐,了解消费者行为,从而知道如何保留和重新定位正确的受众。

时尚产业具有影响力已经不是什么新闻了,我们只需要解读如何让它变得积极。

根据一些学派的观点,时装业是全球变暖的最大元凶之一,据报道,约 20%的世界工业水污染是由时装业造成的。(来源:TechRepublic )

其他一些报告称,这种说法是由最少的证据支持的,关于时尚浪费的数据和统计有很多不准确之处。

另一方面,如果有希望让这个行业变得更可持续,对环境产生更积极的影响,那么时尚行业就有必要量化这些影响。

Cherie Birkner 在 Unsplash 上拍摄的照片

无论是产生的废物量,还是机器产生的烟雾,还是车辆排放的废气(物流)。也许是噪音污染和时装公司使用发电机释放的碳量。

所有这些都需要研究,以便准确理解时尚行业的影响以及数据科学如何发挥作用。

这里有一些关于时尚的事实和一些不可持续的做法。

总之,作为一个时尚品牌或零售商,这里有 10 个问题数据科学可以帮你解答:

  1. 谁是我的客户?
  2. 他们在哪里?
  3. 他们想要什么产品?
  4. 我如何联系他们?
  5. 我应该生产多少?
  6. 我应该什么时候重新进货?
  7. 我应该在何时向谁提供折扣?
  8. 他们什么时候可能会购买?
  9. 他们还在哪里购物?
  10. 谁是我的竞争对手?

鉴于本文关注的是时尚行业,所讨论的技术也适用于其他行业和企业。

我希望你喜欢读它?

你是否也认为数据科学可以让企业变得更可持续?

聊天区评论

重新想象时尚的商业 - 第一部分(时尚品牌和零售商如何通过高级分析重新想象时尚?)

重新想象时尚的生意——第二部分(数据科学可以让时尚产业变得更可持续。真的还是假的?)

用熊猫和 Plotly 重新想象 NFL 角卫的表现

原文:https://towardsdatascience.com/re-imagining-nfl-cornerback-performance-using-pandas-plotly-21e86fe2c445?source=collection_archive---------32-----------------------

从 Kaggle 的 Data Bowl 2021 数据构建我们自己的防御性统计数据故事

理解数据问题:并非所有的捕获都是平等的

在美式足球中,角卫的工作极其艰难,他们要防止外接手——通常是场上最快最高的球员——接到传球。

你可以想象,今天用来衡量角卫表现的统计数据包括yards_allowedcompletions_per_target,它们被广泛用于展示强弱角卫球员之间的差异

但是这些传统的目标 DB 度量未能捕捉到游戏背景的关键元素。以下是这方面的一些具体例子:

第 3 洞和第 2 洞的 5 码完成比第 2 洞和第 22 洞的 5 码完成差得多,即使统计表会将计算为相等。

此外,将 Julio Jones(2018 年前 10 名接球手)保持在 30 码内只有 3 次接球,这比将 Russell Gage(2018 年不是前 10 名接球手)保持在相同的数字上要令人印象深刻得多。

正是考虑到这个体育分析的缺点,我们将创造一个新的视角来看待我们的 CBs: 游戏影响

Riley McCullough 在 Unsplash 上拍摄的照片

以下是指导我们流程的一般规则:

  • 获得进攻里程碑的码数(第一次进攻,上篮,红区码数)应该受到处罚
  • 关键的不完整应该得到奖励
  • 势均力敌的游戏(好的和坏的)比井喷游戏更有价值

记住这些规则,让我们生成新的“游戏影响”分数的第一个版本!

:由于这是一个足球分析问题,我假设你对足球术语有基本的了解,所以我可以专注于数据分析过程

如何通过游戏情境 权衡游戏胜负

我们可以首先从 Kaggle 的 NFL Databowl 2021 加载数据:

这给了我们看起来像这样的原始数据集:

我们的第一个观点是增加对导致进攻里程碑的完整传球的处罚。

熊猫快速评论:

我们可以使用.loc函数,后跟一个条件语句,有条件地修改单个内联列。

例如,如果 X.col 列值大于 5,则test_pd.loc[test_pd.Xcol>5,’Ycol’] = 10会将 Ycol 列值更改为 10。

这是一个非常有用的函数,因为它比.apply()或您计划用来迭代 DF 的任何 for 循环都要快

为此,我们需要…

我们还需要清除无意义的码,在那里通过是完整的,但 EPA 是阴性的(completed_pd.loc[(completed_pd.epa <= 0), ‘epa_yards’] = 0)

我们现在已经用熊猫生成了一个新的游戏影响分数!🚀

然后,我们可以使用Plotly来创建一个交互式数据可视化,显示我们新的游戏影响得分和传统的“增加的码数”指标之间的差异

快速回顾:

改进图表的一个有效方法是添加多个变量。由于 Plotly 的交互组件,我们可以用 X 轴值、Y 轴值、标记颜色、悬停信息讲述一个引人注目的故事。

我们可以通过将跟踪中的text= field分配给悬停信息来修改悬停信息,并在跟踪构造函数中包含标志hoverinfo=’text’

作为一名数据科学家,理解并阐明数据可视化的价值非常重要。通常,数据项目的价值在从数据人员到产品/业务人员的转换中丢失了。

我发现有效的数据交流最好是用清晰的视觉效果,以及适合非技术性叙述的具体数据示例。

为了进一步帮助我们讲述我们的数据故事,添加一些注释来帮助阅读我们的图表将是明智的。我们可以通过下面的代码片段做到这一点:

所有这些为我们提供了以下图表:

图表分析&数据故事

下面是我对上图的解读。在向非技术运动爱好者解释产品时,它很受欢迎。希望这个例子能帮助你解释数据

上面的图表量化了游戏环境和完成的影响。

虽然一般随着绝对码数的增加而线性增加,但当比赛导致触地得分或第一次触地得分时,特别是在第三次或第四次触地得分时,比赛影响会达到峰值。

在比分接近的比赛中,第三回合的大规模进攻对防守来说是痛苦的,所以《游戏影响》很好地捕捉到了这种效果。相反,对游戏没有影响的大块码被降低到 0 游戏影响,以确保垃圾时间码数不会惩罚一个 DB。

举例说明这一点:

第一场比赛(在上面的图表中用红色标记,在 50,150 坐标附近)是第四场比赛的 47 码触地传球,比赛还剩不到 2 分钟。放弃绝对是一场可怕的比赛,因为停止很可能会结束比赛;因此,这场比赛对游戏的影响是重大的。

第二场比赛(在上面的图表中以绿色标记,坐标约为 55,0)虽然比第一场比赛稍微多了,但在时间到期前没有到达端区,这是孤注一掷。这出戏的 DB 通过阻止 TD 完成了他的工作,因此游戏的影响被中和了。

根据游戏环境对游戏进行加权的行为将有助于我们稍后的分析将总体良好的数据库与那些几乎从未放弃重要游戏的数据库区分开来……并有望揭示一些角落,这些角落的表现实际上比它们的统计数据乍一看要差得多

我们可以不完整地重复熊猫的预处理。这次的不同之处在于,我们将计算未完成的奖励,而不是惩罚。

对代码的唯一更改是将我们的修饰符添加到 EPA,而不是减去它:

和生动的视觉效果:

要为未完成部分生成类似的图表:

我们的未完成比赛影响得分与码到端区的比较

我们将 Y 轴从“增加的码数”切换到“到端区的码数”的原因是因为所有这些比赛增加的码数都是 0。距离端区 100 码的距离为分析我们的数据提供了一个更好的视角

图表分析&数据故事

正如一些完成比其他的覆盖更差,一些未完成比其他的是更好的防守。

虽然所有这些比赛都是不完整的,但是在红区中的近距离比赛中出现的不完整被拦截,或者以其他方式影响对手获胜的机会,则被奖励以负的比赛影响分数。

注意有多少第四次向下击球有很大的向下重量——好的 DBs 确保第四次向下转换不会发生

上面的截图突出了一个例子:汤姆·布拉迪在比赛还剩 7 分钟的时候,第 4 局没有完成。这是一次重要的防守行动,因此应该得到奖励!一个持续这样打球的角后卫应该被认为是精英角后卫,我们的比赛影响力得分反映了这一点。

结论和后续步骤

争论一个新的指标是一项有趣的工作,但它不仅需要数据分析技能,还需要强有力的有说服力的论据。通常,模型和度量标准可以提供对行为/性能的一些洞察,但显然没有什么度量标准是万能的。

这就是为什么数据分析在体育运动中发挥着如此重要的作用,以及数据人如何在不够运动的情况下接触球场🤓,仍然可以通过提供衡量场上表现的标准来产生影响。

有很多理由说明这个新的比赛影响力分数是当前角卫指标的一个很好的发展…也有很多理由说明它不是。例如,足球是一项团队运动,因此将比赛结果完全归因于一个球员是不公平的。

但是这篇文章希望向您展示(1)如何使用 Pandas 作为 Plotly 来构建一个可解释的数据可视化,以及(2)如何围绕可视化来形成一个数据故事,以促进所需的对话。

即使 NFL 不采用我的新指标(Goodell 打电话给我),我也希望这些指标至少成为谈论球员表现的一部分。

照片由 Unsplash 上的铝烟灰拍摄

我希望你学到了一些东西,并喜欢阅读。如果你想看到更多这样的文章,请告诉我!

使用 Python 读取和编辑图像元数据

原文:https://towardsdatascience.com/read-and-edit-image-metadata-with-python-f635398cd991?source=collection_archive---------4-----------------------

使用 Python 的 exif 库提取和修改数字图像文件的元数据

JESHOOTS.COMUnsplash 上拍照

对于每张照片,都有比看上去更多的东西。数码相机和智能手机拍摄的图像包含丰富的信息(被称为元数据),超出了可见像素。

这种元数据在许多商业案例中很有帮助。例如,保险索赔的欺诈检测系统分析提交的照片的元数据,以检查索赔人是否在事故之前拍摄了这些照片。

在本文中,我们探索如何使用 exif 库来读取和编辑数字图像的元数据。

内容

(1)什么是元数据和 Exif?(2)读取图像元数据
【3】修改图像元数据(4)保存修改后的图像元数据

请随意查看随附的 GitHub repo 中的所有代码。

什么是元数据和 Exif?

元数据是指描述数据的数据集,你可以把它想象成关于数据的数据。照片的元数据由相机型号和拍摄日期等信息组成。

这些元数据存储在 Exif(可交换图像文件格式)中,这是一种针对数码相机和智能手机等设备拍摄的各种类型媒体(例如图像、视频、音频)的格式标准

本项目使用的 Python 库是exif,恰好是 Exif 格式的同名。

读取图像元数据

我们首先用这个命令安装 exif :

pip install exif

对于本次演示,这是我们将要处理的照片:

作者图片

我们通过在检查图像是否包含任何元数据之前读取二进制格式的图像来实例化一个 exif Image类。如果是这样,has_exif方法将返回真值

不同的照片有不同的元数据属性(又名 Exif 标签),这是由于用于捕捉它们的设备不同。我们可以用list_all()方法查看图像的现有属性。

按作者列出的属性列表|图像的子集

要读取特定属性的值,我们可以使用get()方法。虽然还有其他方法,但我更喜欢get(),因为它通过返回 None (而不是抛出错误)来优雅地处理属性不存在的情况。

get()方法的输出|作者的图像

附:查看Image _ Metadata _ Extraction _ EXIF . ipynb笔记本,了解将一张图片的所有元数据提取到熊猫数据帧中的功能。

照片由米琳·特雷莫耶Unsplash 上拍摄

修改图像元数据

除了读取元数据,我们还可以执行一系列的修改,比如添加、更新和删除属性。

㈠添加元数据

可以添加当前不存在的新属性来进一步丰富元数据。

需要注意的一点是,添加的属性必须是一个可识别的 EXIF 标签。否则,添加将不会发生。您可以在此 找到已识别图像属性 的完整列表。

比如我们可以添加公认的版权属性。在给版权属性赋值( Kenneth Leung 2021 )后,get()方法会给我们这个新值,而不是 None。

https://kennethleungty.medium.com/membership

㈡更新元数据

我们还可以更新图像元数据属性的现有值。

元数据更新后的输出|按作者排序的图像

㈢删除元数据

如果我们想删除特定的属性而不是更新它们,我们可以用.delete()来完成。

元数据删除后的输出|按作者排序的图像

保存修改后的图像元数据

在所有的修改之后,最后一步是将带有修改后的元数据的图像保存为一个新文件。

如上所述,我们使用前缀为' modified_' 的文件名保存了修改后的图像,这样原始图像就不会被覆盖。

包装它

  • 还有许多其他有趣的属性需要探索,您可以在 exif 文档页面上找到更多细节。
  • 到目前为止,我们所做的是处理一个单一的图像。 exif 包的值是通过批处理实现的,其中元数据的提取和修改是在一组大的图像上完成的。要查看批处理的运行情况,请看一下 GitHub repo 中的batch_process_metadata.py脚本。
  • 请记住,在使用此图库之前,请备份您的照片,以防止任何意外的数据丢失。

在你走之前

欢迎您加入我的数据科学学习之旅!点击这个媒体页面,查看我的 GitHub ,了解更多令人兴奋的数据科学内容。同时,享受阅读和修改图像元数据的乐趣!

痴迷地阅读和写作:创作伟大作品没有真正的捷径

原文:https://towardsdatascience.com/read-and-write-obsessively-there-are-no-real-shortcuts-to-producing-great-work-4ea1f3421eec?source=collection_archive---------27-----------------------

作者聚焦

在公共场合工作,追求内在动机,在写作中冒险

在 Author Spotlight 系列中,TDS 编辑与我们社区的成员谈论他们在数据科学领域的职业道路、他们的写作以及他们的灵感来源。今天,我们很荣幸地邀请到埃利奥特·冈恩https://medium.com/u/aad1101621dd?source=post_page-----4ea1f3421eec--------------------------------*与马克·萨鲁菲姆* 进行对话。

照片由马克·萨鲁菲姆提供

Mark 是 PyTorch 团队的 ML 合作工程师。在过去的生活中,马克曾在 Graphcore,他自己的公司 yuri.ai 和微软担任 ML 工程师和产品经理。马克对人们打造自己的教育和公司的未来持乐观态度。

你在军情六处走了一条非常规的职业道路。从离开微软的工作开始自己的游戏开发 AI 公司,到在 Graphcore 工作,现在是脸书的 PyTorch 工程师。你能分享一下你是如何通过这些角色进入这个领域的吗?

*哈哈,我经常被问到这个问题。所以我真的开始在加州大学圣地亚哥分校深入研究机器学习,我全身心投入到计算机科学理论中,我觉得这是了解世界的唯一真正的方法。它给了我现在的信心,让我拿起任何一本数学教科书读一读,但也有机会成本,因为我可以在深度学习领域开始爆发的时候开始。

我离开学校后的第一份工作是在微软做产品经理,我对此很糟糕。当我转行成为一名应用科学家后,我明白了产品经理的角色是什么。如果有的话,我觉得学科是人工构造,任何人都会从成为更好的设计师、程序员、数学家、作家和演说家中受益。

那段经历促使我创办了自己的公司 yuri.ai,这是一家为游戏开发者提供强化学习服务的公司。做老板让你做的事是一回事,但让人们付钱是完全不同的另一回事——尤其是对于一个半生不熟的项目。我的积蓄正在蒸发,我需要找到一种更好的方式来引起人们的注意,这就是为什么我最终写了《机器人霸王手册》。这是我第一次看到公开工作的好处;这本书最终比我的创业公司更受欢迎。从那以后,在 Graphcore,我是一名 ML 工程师,直接与研究人员合作,将他们的模型移植到 IPU,这让我对如何快速运行模型有了惊人的见解。能够接触到任何客户,向他们推销我们硬件的 POC,然后一起写一篇论文,这真是太棒了。即使在一个公司内部,影响力也不需要局限于一个组织,而是可以通过互联网在全球范围内实现。是的,现在我在 PyTorch,坦白地说,这太棒了。我是我去过的所有房间里最笨的人,这很令人兴奋。我仍在思考我到底应该做什么,但同时你可以在 GitHub 上看到我所有的作品。*

你特别自豪的一个项目是什么?

我最喜欢的项目总是从很小的地方开始——最初只有我一个人,然后兴趣自然而然地增长了。

第一个也是最令人难忘的例子是微软的内部遥测工具,它告诉我们谁在使用哪个内部数据工具。

开始时,我只是手动运行一些 SQL 查询,并制作一个每周使用情况仪表板,但最终它变得非常自动化,我们可以深入了解哪些团队的使用情况下降了,联系他们,然后在他们悄悄放弃产品之前解决他们的问题。然后,我们向其他人开放,采用爆炸式增长。

我们通过劫持 Power BI dashboards 的全局名称空间,创建了一个名为“!!"因此,无论何时公司中的任何人打开任何 Power BI 仪表板,他们都会首先看到我们的产品页面。我们确实收到了法律团队措辞严厉的电子邮件,要求我们停止。

那真是一段有趣的时光。我们为自己制造了一些我们喜欢的东西,然后它有机地成长为其他人喜欢的东西,并依赖于这些东西来判断他们的产品是否对其他人有用。

我在 Graphcore 的图形神经网络工作中也有类似的经历,最初只有我一个人。一旦基准有了希望,人们就会主动帮助我。内在动机胜过外在动机,我希望我能把这变成我工作中的一种模式。

你已经写了许多文章,涵盖了各种各样的主题,从病毒式的"机器学习:大停滞",到"非学校化社会",以及一部真正有趣的视觉数学史。你还出版了关于机器人和机器学习的免费教科书,并在 TwitchYouTube 上发布了流解释器。你创作如此多产的过程是怎样的?关于这种写作,你最喜欢的是什么?

你需要一个好的渠道,让你消费的内容慢慢变成你可以分享的精致内容。

很多书。关键是要接受戒掉坏习惯。之后我做了很多笔记,其中一些在 GitHub 上公开,一些在 Google doc 上公开。

当我选定一个有趣的想法时,我会在 Twitter 上进行 A/B 测试。如果没有人参与,这可能不是一个独特的见解。但是如果很多人都这样做了,那么这是一个好的迹象,我应该在博客中充实它。我的 HuggingFace 文章是从一条推文开始的。

写作是迄今为止我参与的最耗时的社交媒体活动。我发现没有真正的捷径,你只需要坐下来写很多,但有帮助的只是选择独特的和我着迷的主题。

YouTube 和 Twitch 我也是出于自私的原因开始的——当我现场解释东西时,这是我第一次阅读有关这个主题的内容,所以对我来说这很棒,因为我可以将我学到的东西转化为资产。当你已经向全世界宣布你将要教他们一些东西的时候,你不能拖延。

不过,平心而论,我在 ML 职业生涯的前六年确实痴迷于阅读和写作,但从未在公共场合,也从未有过我想要的产出,无论是论文、博客还是图书馆。我已经准备好在公共场合工作很长时间了,但在这一点上,我认为我永远不会停止,我通过这种方式遇到了太多有趣的人。

你有非常独特的写作哲学。随着越来越多的工程师开始写作,你对希望提高写作水平并在网上脱颖而出的读者有什么建议?你希望更多地看到 DS/ML 中的哪种写作?

每个作家都会经历一个慢慢找到自己风格的演变过程。最初,你没有风格,很容易过度思考你的风格应该是什么。不要!在这个阶段,重要的是你要写很多东西来养成清楚地解释事情的习惯。一旦你有了几本食谱教程,是时候冒点风险,开始在你的帖子里注入更多你的个人生活、课程和领域知识了。

这有帮助的原因是因为在一件事情上成为世界上最好的真的很难;我不想和 Yann LeCun 竞争。所以我意识到,当人们阅读时,他们不仅仅是出于学习的动机。没有人说字典是他们最喜欢的书。

人们想笑,想被激励,想被挑战,害怕——所以多想想阅读你的帖子是什么感觉,以及你如何给你的读者提供独特的体验。讨论如何用 Y 技术解决 X 问题的食谱教程没有持久的生命力。作为一种选择,向官方文档投稿要好得多。

我看过的最好的技术帖都不是纯技术帖;他们往往是技术岗位和其他的。其他的东西可以是抽象的总结、插图、商业见解、个人细节、几个技术领域的结合等等。写作本来就是自省的。你需要挖掘出看似不相干的优点,让它们大放异彩。

我能给出的最好的写作技巧之一就是用你最喜欢的作者的声音写一篇技术文章。我最喜欢的是纳西姆·尼古拉斯·塔勒布的《不敬的热点》,乔治·奥威尔的《反乌托邦的未来》的评论,还有 H. P .洛夫克拉夫特,他帮助我认识到人类的影响实际上是多么小。如果你借鉴你所有的兴趣,你会很快找到自己独特的风格。

就我而言,我做过产品经理、工程师和科学家,并尝试至少从这三个角度来讨论一些事情。我也是一名业余喜剧演员,让它通过我自己创造的迷因来实现。我想让人们只看迷因就能轻松浏览我的文章。我希望他们玩得开心,感觉聪明,并通过说一些他们的同事不会说的话来挑战他们。

你对未来几年的 DS 社区有什么希望?有没有什么事件或趋势让你对 ML 的未来感到比较乐观?

我在《大停滞》中提到过,虽然核心 ML 已经停滞,但与其他领域的交集正在蓬勃发展。我看到创新爆炸的最明显的领域是生产 ML 模型,那里似乎每天都有新的创业公司出现。这是一件好事,这意味着有这么多的创新,社区还没有决定做 ML 的最佳抽象,我将密切关注每个人,看看我能在哪里提供帮助。

建模方面的一个例外是图形神经网络,我对此非常兴奋,因为它们在模型本身中编码不变量。数据扩充永远是一个骗局;事实证明,随着现代数学的发展,我们可以增加建筑的表现力。

最后,我对游戏模拟情有独钟——游戏本质上是一次生成一个观察数据集。强化学习正在慢慢成为游戏开发者社区的主流,但令人惊讶的是,很难找到既有 ML 技能又有游戏开发技能的人(如果你是这样的人,请联系我,我很乐意和你聊天)。生成新观察的成本基本上是免费的,随着自我监督学习的日益普及,我们可以超越雅达利,将游戏模拟变成主流。

想了解更多关于 Mark 的工作和数据科学兴趣的信息吗?你会在他的媒体简介,他的子栈打破停滞,以及他的推特账户上找到他的文章。他还在他的 Twitch 频道(T7)上播放他的写作和教程,并将视频讲解上传到他的 YouTube 频道(T9)。以下是我们最近最喜欢的一些。

  • 机器学习:大停滞 ( TDS ,2020 年 12 月):马克的病毒式走红让他一跃成为黑客新闻的榜首。他解释了激励机制如何创造了一种文化,在这种文化中,ML 研究人员追逐增量研究,而不是通过更具风险的途径来实现突破。
  • 外部产品 ( TDS ,2019 年 11 月):一篇深入探讨几何代数如何可能成为深度学习和机器人未来更好的数学工具的文章。
  • 如何把物理变成优化问题 ( TDS ,2019 年 11 月):马克提出了有多少物理问题可以用机器学习工具来解决。

请继续关注我们即将推出的下一位专题作者。如果你对你想在这个空间看到的人有建议,请在评论中给我们留言!

read_csv 让我失望!

原文:https://towardsdatascience.com/read-csv-let-me-down-956acd1cee91?source=collection_archive---------47-----------------------

为什么不相信你最喜欢的数据导入功能

来源:苏珊·尹https://unsplash.com/photos/2JIvboGLeho

作为一名数据科学家,我最不担心甚至想不到的事情之一就是将我的数据导入我的 Jupyter 笔记本或 RStudio 会话的方法是否可行。我完全相信这两个下划线单词:“阅读”“CSV”。不管是用 Python 还是用 R,我的工作就是清理、剖析和分析数据。除了用什么定界符来分隔之外,我很少考虑读数。然而今天,我在使用 R 的 Tidyverse 包中的 read 函数时遇到了两个问题。事情是这样的。

一个客户给了我几个 tsv。我继续用上面提到的 R 函数打开每一个,将它们附加到一个列表中,然后将它们连接成一个大的数据帧。我做了一些检查和检验,一切似乎都合情合理。柱子很清晰。已填充行。我准备好工作了。

我继续工作,一旦我完成了(或者至少认为我已经完成了),我通过一个报告运行我的数据,该报告给出了一些关于数据和分析质量的高级信息。只是在阅读报告的输出时,我才意识到似乎有一些缺失的数据。很多数据缺失!

然后,我检查了保存到列表中的每个数据帧的内容,发现了两个异常。

可以看出,两个数据帧的行数要少得多。我通过手动打开它们来检查它们,并意识到它们确实在我的会话中丢失了许多数据。

然后,我给一台可靠的 Jupyter 笔记本电脑加电,用熊猫和 T1 一起玩 T0。

df_list = []
for file in files:
    df = pd.read_csv(file, sep = '\t')
    df_list.append(df)

data = pd.concat(df_list)

我连接了所有的数据帧并检查了行数,大约有一百万多行数据。

两种技术,两种基本功能做着同样的事情。但是有一个让我丢失了大约 100 万行数据。

但事情并没有就此结束。在我的 RStudio 笔记本中,我有很多好的代码要处理,所以我将数据从 Python 导出为一个大文件,并重新导入到我的会话中,这次是用read_csv(因为我将它从 Python 导出为 CSV)。谢天谢地,丢失的百万行又回来了。

然后,我做了一些健全性检查,并意识到许多列现在充满了空值!因此,虽然我现在有了正确的行数,但其中的许多数据都丢失了。

我不知道该责怪什么,R 中的导入还是 Python 中的导出。这个问题很容易回答,用 Python 重新导入即可。如果没有丢失数据,问题就不在导出。事实正是如此。当重新导入 Python 时,所有数据都在那里。

所以我返回 R,尝试不使用 Tidyverse 中的函数导入数据,而是使用 data.table 包中的fread函数。它也只是一个普通的数据导入包,就像read_csvread_tsv一样。

令我高兴的是,我不仅有了我需要的所有行,而且所有数据都被正确地填充到其中。但令我痛苦的是,这意味着我之前对任何文件导入功能的信任需要消失。

摘要

这两个问题都是在使用 Tidyverse 中的函数时出现的。当使用 R 或 Python 时,打开原始文件的方法没有区别,R 返回的行数仍然少了大约一百万。在 R 中使用 Tidyverse 或 data.table 时,我重新导入数据的方式也没有什么不同,但是 Tidyverse 引入了 nulls,而 data.table 没有。我不确定 Tidyverse 中的函数是否有问题,但这种不一致令人担忧,也很难理解。

我猜想这样做的原因是为了警告您,您首选的数据导入方法有时可能不如您需要的那样可靠。不要把你所有的信心都放在功能上。进行尽职调查,确保您最终得到的数据帧与您告诉函数导入的数据相同。

在 Julia 中将 CSV 读取到数据框

原文:https://towardsdatascience.com/read-csv-to-data-frame-in-julia-programming-lang-77f3d0081c14?source=collection_archive---------2-----------------------

参数说明。使用 CSV.jl,DataFrames.jl,处理 StringEncodings。

在 Julia 中读取 CSV 到数据帧。作者图片

任何数据分析的第一步都是获取数据。毫无疑问,没有比将 CSV 文件加载到数据框中更简单的方法了。在本教程中,我们将探索如何在 Julia 编程语言中做到这一点。

python 的 pandas [read_csv](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html)不同,Julia 中的函数被分成两个模块——CSV . JLDataFrames.jl ,并且有多种方式来组合它们。在本指南中,我们将看到:

完整的教程代码可以通过GitHub-Read _ CSV . ipynb获得。

CSV.jl 来解析 CSV 文件

作为第一步,您必须声明您将使用的库。在我们的例子中CSVDataFrames

using CSV
using DataFrames

如果您还没有将它们安装到您的 Julia 环境中,运行 REPL(命令行界面通常通过键入julia启动)并运行import Pkg; Pkg.add("CSV"); Pkg.add("DataFrames")

CSV.jl

让我们从阅读非常简单的逗号分隔值文件(CSV)开始我们的旅程,该文件有 3 列,每列包含不同的数据类型(stringintfloat)。

col1,col2,col3
A,12,2.0
B,22,5.1

要解析一个 CSV,只需将它传递给CSV.File(path)命令。Path 参数包含文件的路径。

[In]:
# reading the file
csv_reader = **CSV.File(**"file.csv"**)**
println(typeof(csv_reader))[Out]: CSV.File{false}

结果是一个CSV.File 对象,可以通过迭代得到CSV.Rows

[In]:
for row in csv_reader
    println(typeof(row))
end[Out]:
CSV.Row
CSV.Row

CSV.Row对象中,您可以使用列名来访问值。

[In]:
for row in csv_reader
    println("values: $(row.col1), $(row.col2), $(row.col3)")
end[Out]:
values: A, 12, 2.0
values: B, 22, 5.1

您可能已经注意到,CSV 正确地猜到了我们的文件在第一行包含一个标题,并且“,”(逗号)被用作分隔符。这是因为header参数的默认值为 1。Julia 从 1 开始索引(不像 python 的 0)。Delim参数的默认值是逗号,但是如果 CSV 无法解析它,它将使用前 10 行来猜测其他合适的分隔符,如分号、制表符、空格或竖线。

您总是可以手动指定这些参数。Julia 约定说关键字参数用分号分隔,但是逗号也可以。

# to separate kwargs by semicolon is a Julia standard
CSV.File(path**;** header=1, delim=",")# comma works as well
CSV.File(path**,** header=1, delim=",")

将 CSV 读入数据帧的 3 种方法

为了将CSV.File转换成数据帧,你必须将其传递给DataFrames.DataFrame对象。在 Julia 中至少有三种方法可以做到这一点。

选项 1 —传递到数据帧

你可以把数据框绕在CSV.File(path; kwargs)上。

df = DataFrame(CSV.File("file.csv"))

CSV 加载到 Jupyter 笔记本中显示的 Julia 数据帧。图片作者。

您可以看到 Julia 表示(与 python pandas 不同)显示了列的数据类型,无论是stringint还是float

选项 2 —连接到数据帧的管道

第二种可能性是使用 Julia'a 管道操作符|>CSV.File传递给DataFrame

df = CSV.File("file.csv") |> DataFrame

结果是一样的。

选项 3 — CSV.read()

为了使代码与其他语言相似,Julia 的设计者决定添加一点语法糖,并允许第三种选择。CSV.read()将文件的 path 参数作为第一个参数,将 DataFrame 对象作为第二个参数。其他参数可以跟随。

df = CSV.read("file.csv", DataFrame; kwargs)

这些方法在 Julia 版本 1.4.1 中有效,我认为尽管 Julia 还在发展,但它会很稳定。一些旧的教程显示了其他方法,例如 CSV.read 不带 DataFrame 作为参数,这不再有效。

编码

Julia 希望所有的字符串都是 UTF 8 编码的。如果不是这样,您必须首先使用其编码打开文件,然后将其传递给 CSV。文件,然后到数据框。您可以使用open(read, path, encoding)read(path, encoding)的快捷方式。编码以编码对象的形式出现,您可以在带有编码名称的字符串前使用enc轻松创建编码对象,例如enc"windows-1250"

DataFrame(CSV.File(open(read,"file_encoding.csv", enc"windows-1250")))#orDataFrame(CSV.File(read("file_encoding.csv", enc"windows-1250")))

在 Julia 中处理非 UTF-8 编码。作者图片

CSV 阅读器参数

Julia CSV parser 非常擅长猜测基本参数,尽管有时您需要手动指定参数。让我们概述一下 CSV 解析器最基本的参数。当然,所有参数都在文档中有解释— CSV.jl

Delim —分隔符

默认情况下,分隔符设置为逗号","。如果 CSV 解析不正确,Julia 会尝试使用前 10 行来猜测不同的分隔符。你总是可以硬编码它。

# pipe separator
CSV.read(path, DataFrame; delim="|")

分隔符可以超过 1 个字符。比如delim="::"

页眉

有时候你的数据不是从第一行开始的。其他时候你根本没有任何标题。也有可能不止一行包含标题。朱莉娅能处理所有这些案件:

  • 没有标题— header=0header=false,则自动生成列名
  • 使用字符串或符号向量指定自己的名称
  • 首行标题— header=1
  • 第二行标题— header=2,也可用于跳过某些行(跳过也可通过skiptodatarow完成)。
  • 多行是按范围排列的标题— header=1:2(第一行和第二行构成标题)
  • 多行是列表标题— header=[1,2]header=[1,3] (在后一种情况下,将跳过第 2 行)

如果选择多行作为标题,那么列名就是这些行的串联。

选择并删除参数

您并不总是需要所有的列。您可以使用select=[1,2,3]仅设置您需要的列,或者使用drop=["col1",:col5]指定您想要跳过的列。

这些列可以通过它们的顺序1,5,25来识别。记住 Julia 从 1 号开始索引,所以 1 是第一列。您也可以使用字符串 id"col1", "customer_name"或符号。符号使用冒号前缀(:)[:col1, :customer_name]或者你可以显式声明它们[Symbol("col1"), Symbol("customer_name")]

类型和类型参数

CSV 阅读器将推断类型,但它可能会猜错。如果您知道数据类型,您可以使用typetypes参数指定它们。

  • type —为整个数据框设置单一类型
  • types —指定每列的确切类型

您应该知道,如果数据不能被转换成特定的类型,值就会被转换成missing,这相当于 python 中的NaN

解析日期和日期格式参数

处理日期是数据解析的另一个重要部分。Dateformat参数允许您为所有被识别为日期的列设置日期格式,或者专门为每列设置日期格式。如果该列未被自动识别为date,在dateformat参数中提及它会将其转换为日期。

如果解析为定义的格式失败,值被转换为与Nan等效的missing类型。在 Julia 文档中查看更多关于日期的信息。

Truestrings 和 falsestrings 参数

这些参数允许您将输入列表设置为 true 和 false 布尔值。

[In]:
data = """
b01|b002|c1|c2|c3|c4|d1
**"t"**|**"fa"**|"XY"|2|c|1.5|2020-01-05
**"f"**|**"tr"**|"AB"|16|x|2.33|2021-01-05
"""CSV.read(IOBuffer(data), DataFrame; 
    truestrings=["t","tr"],
    falsestrings=["f","fa"])

确定真值和假值时的最终数据帧。图片作者。

缺少字符串参数

有些值意味着没有可用的数据。它们通常是-1 或 999。您可以指定这些值的列表,这些值将被解释为缺失(N/A)。

[In]:
data = """
m01|b002|c1|c2|c3|c4|d1
**999**|"fa"|"XY"|2|c|1.5|2020-01-05
-1|"tr"|"AB"|16|x|2.33|2021-01-05
"""CSV.read(IOBuffer(data), DataFrame; 
    missingstrings=["999"])

使用 Missingstrings 参数。作者图片

注释参数

有些文件包含注释或批注。您可以使用comment参数排除它们。

# comment lines start with single character
DataFrame(CSV.File(path; comment="~"))# comment lines can start with some string
DataFrame(CSV.File(path; comment="!!")# but you cannot specify more than one string
DataFrame(CSV.File(path; comment=["~","#"]) --> error

转置参数

当您可以转置数据并将列变为行时,您可以设置transpose=true

使用 transpose=true 会将行转换为列。作者图片

池参数

汇集提醒category输入熊猫。它允许将只有几个值的列映射到ints,并显著减少列的大小和一些操作的成本。

我用pool参数做了一些实验,但没有发现它到底是如何计算出来的。与 pandas 不同,您不能共享特定的列。您只能设置阈值,所有唯一字符串数低于该阈值的列将被合并。

解析固定宽度文件

固定宽度文件类似于 CSV,只是每一列始终具有相同的宽度(字符数)。如果内容较短,其余部分通常用空格填充。

虽然有些工具,如许多数据库引擎,使用带分隔符的固定宽度文件将数据导出为文本,以获得更好的阅读体验。使用CSV.jlignorerepeated=true无法读取这样的格式,你必须做进一步的操作将这样的文件转换成数据帧。

安装 Julia

你对朱莉娅的容貌印象深刻吗?你想自己试试吗?在你的电脑上安装 julia 很容易。你进入下载 Julia 页面,选择适合你的平台(Win、OS、Linux)的文件或者使用 docker 镜像。

然后从命令行输入julia启动朱莉娅·REPL。

在 Jupyter 笔记本中运行 Julia

如果你已经安装了 Jupyter (例如你的 python 项目),你可以通过运行朱莉娅·REPL(在命令行中键入julia)来允许朱莉娅脚本,然后通过以下方式添加IJulia包:

  1. using Pkg
  2. Pkg.add("IJulia")

添加 IJulia 包,这样你就可以在 Jupyter 笔记本中运行 Julia 脚本。图片作者。

然后,您可以简单地在 Jypyter 中创建新的 Julia 项目。图片作者。

结论

我希望您已经学会了如何将 CSV 文件加载到 Julia 的 DataFrames 模块中,并且您将在您的下一个项目中尝试它。你可以自己比较 Julia 的上传速度和进一步的数据处理选项,并在评论中让我知道你对 Julia 的看法。

github — Read_CSV 上看到笔记本里的所有代码。

If you liked this article, maybe you will enjoy the following as well: 
* [Comprehensive tutorial to plotly express in python](/visualization-with-plotly-express-comprehensive-guide-eb5ee4b50b57)
* [Data persistance alternatives in pandas](/stop-persisting-pandas-data-frames-in-csvs-f369a6440af5)
* [Cross-Validation in python](/complete-guide-to-pythons-cross-validation-with-examples-a9676b5cac12)

用 Python 将 ERA5 直接读入内存

原文:https://towardsdatascience.com/read-era5-directly-into-memory-with-python-511a2740bba0?source=collection_archive---------8-----------------------

如何使用气候数据存储 API

附身摄影Unsplash 上拍照

ERA5 是欧洲中期天气预报中心( ECMWF )制作的第五代气候再分析。该产品提供了许多大气、地表和海洋状态参数的每小时输出,以及不确定性的估计。然而,除非您在中工作,否则存储原始每小时输出会占用大量空间。

这篇文章概述了如何安装气候数据存储(CDS) API 来读取和分析 Python 中的 ERA5 输出。

CDS API 简介

CDS API 允许访问数据集的 ERA5 系列,其中包括 ERA5 和唯一的 land 补集 ERA5-land。ERA5 有四个主要子集:每小时每月产品,每个产品都基于压力水平(包括高空区域)和单一水平(大气、海洋和陆地表面数量)。ERA5-land 仅包括单级

四个 ERA5 子集

  • ERA5 单层每小时数据
  • ERA5 单层月平均数据
  • ERA5 压力水平的每小时数据
  • ERA5 压力水平的月平均数据

两个 ERA5-land 子集

  • era 5-陆地每小时数据
  • era 5-土地月度数据

ERA5 再分析在本手稿中有详细描述。

安装 CDS API 密钥

要开始使用 CDS API,你需要做的第一件事是用 Copernicus 创建一个帐户,并将你唯一的 API 密钥存储在一个点文件中。以下是执行此操作的步骤:

  1. 在此 注册 与哥白尼创建账户。
  2. 一旦您有了帐户,请在此处 登录您的 Copercius 帐户 ,并记下页面底部的UIDAPI key
  3. 将下面的代码片段粘贴到您的终端中,用步骤 2 中的代码替换<UID><API key>:
{
  echo 'url: https://cds.climate.copernicus.eu/api/v2'
  echo 'key: UID:API_KEY'
  echo 'verify: 0'
} > ~/.cdsapirc

上面的命令用您的 API 密匙创建了文件~/.cdsapirc,这是使用 CDS API 所必需的。作为健全性检查,使用more ~/.cdsapirc来确保一切看起来都是正确的。一个正确的文件将类似于以下内容:

url: [https://cds.climate.copernicus.eu/api/v2](https://cds.climate.copernicus.eu/api/v2)
key: 12345:a99b9c9d-9e99-9999-9999-fg99h9i99j9k
verify: 0

不要把上面的复制到你的 **~/.cdsapirc** 文件中,这只是为了举例说明。

安装 CDS API 客户端

一旦在~/.cdsapirc中有了 API 密匙,就可以通过condapip安装cdsapi客户端:

pip install cdsapi
or
conda install cdsapi

安装后,确保您可以在 Python 脚本中导入并启动客户端,而不会出现任何错误。

import cdsapi
cds = cdsapi.Client()

使用 CDS API

下面的代码显示了如何在一个日期范围内请求指定压力水平的月平均温度。输出作为xarray数据集ds存储在内存中。下面的代码也允许将输出保存到磁盘,该操作可以使用download_flag打开或关闭:

import cdsapi
import xarray as xr
from urllib.request import urlopen# start the client
cds = cdsapi.Client()# dataset you want to read
dataset = "reanalysis-era5-pressure-levels-monthly-means"# flag to download data
download_flag = False# api parameters 
params = {
    "format": "netcdf",
    "product_type": "monthly_averaged_reanalysis",
    "variable": "temperature",
    "pressure_level": '1000',
    'year':['2019','2020'],
    'month':['01','02','03'],
    "time": "00:00", 
    "grid": [1.0, 1.0],
    "area": [90, -180, -90, 180],
    }# retrieves the path to the file
fl = cds.retrieve(dataset, params)# download the file 
if download_flag:
    fl.download("./output.nc")# load into memory
with urlopen(fl.location) as f:
    ds = xr.open_dataset(f.read())

这是一个更复杂的请求示例,我请求 2020 年的 1 月、2 月和 3 月。另外,我请求每个月的第一天中午,12:00。我需要四分之一度的数据grid和美国大陆的数据area:[49.38, -124.67, 25.84, -66.95。注意,area参数指定了一个坐标为[north, west, south, east]的边界框。

import cdsapi
import xarray as xr
from urllib.request import urlopenc = cdsapi.Client()# dataset to read
dataset = 'reanalysis-era5-pressure-levels'# flag to download data
download_flag = False# api parameters 
params = {
    'format': 'netcdf',
    'product_type': 'reanalysis',
    'variable': 'temperature',
    'pressure_level':'1000',
    'year':['2020'],
    'month':['01','02','03'],
    'day': ['01'],
    'time': ['12:00'],
    'grid': [0.25, 0.25],
    'area': [49.38, -124.67, 25.84, -66.95],
    }# retrieves the path to the file
fl = c.retrieve(dataset, params)# download the file 
if download_flag:
    fl.download("./output.nc")# load into memory
with urlopen(fl.location) as f:
    ds = xr.open_dataset(f.read())

代替yearmonthdaytime参数,您也可以指定一个日期列表:

'date':['2020-01-01 12:00', '2020-02-01 12:00', '2020-03-01 12:00'],

或者,您可以像这样指定一个datetime:

'date':['2020-01-01', '2020-02-01', '2020-03-01'],
'time: '12:00',

上面的代码片段都产生相同的请求,说明了 API 的灵活性。这些请求非常具体,大多数时候你会希望请求一个连续的范围。

指定日期范围

有几种方法可以做到这一点。最简单的方法是用斜杠分隔所需的日期,该斜杠指定所请求范围的开始和结束日期:

'date':'2020-01-01 12:00/2020-03-01 12:00'

另一个选择是用熊猫date_range。但是,请注意,API 的输入必须是字符串列表。这里有一个使用熊猫的例子:

‘date’: list(pd.date_range(‘2020–01–01 12:00',’2020–03–01 12:00', freq=’M’).strftime(‘%Y-%m-%d %H:%M’)),

不幸的是,在请求月平均值时不能使用date参数,它只适用于高时间分辨率数据。对于月平均数据,您必须使用yearmonth参数,它们也接受斜线符号:start/end

cdsapi非常适合复杂的请求,并且非常灵活,但是有时您会得到一个难以诊断的错误消息。

错误消息

no data is available within your requested subset. Request returned no data是一个常见的错误信息,它没有太大的帮助。这通常表示关键字值超出了可用范围。例如,只能请求一组特定的压力水平。另外,time参数必须是使用 24 小时制的最接近的小时。以下是我看到的常见错误以及如何修复它们:

  • 压力水平必须包含在:[1,2,3,5,7,10,20,30,50,70,范围(100,1000,25)]内,其中range(100,1000,25)表示 100 到 1000,增量为 25。如果出现错误,请检查您请求的压力是否包含在此列表中。
  • 时间必须是 24 小时制中最接近的小时:00:0001:00、…、23:00。如果出现错误,请检查时间是否精确到小时。如果您请求月平均值,请确保将time参数设置为‘00:00’

证明文件

我不知道任何关于 CDS API 的文档,这使得它很难使用,因为允许的关键字和变量名因数据集而异。以下链接会有所帮助:

您也可以在 处查看表格 中的示例脚本。

另一个选择是使用 web 界面进行选择,并查看底部的“显示 API 请求”。 这里的 是“月平均压力水平”的形式其他数据集可以在 这里 找到。单击您想要访问的数据集,然后单击“下载数据”选项卡进行选择。

使 API 更易于使用

一旦你理解了它是如何工作的,你就会意识到 CDS API 并不可怕,而是非常强大。我写了一个函数来抽象出我所描述的一些细微差别。这个函数是开源的,所以你可以随意复制和修改它。

使用函数

下面是一个使用get_era5()函数创建下图的例子:

2021 年 2 月 ERA5 的平均温度(图片由作者提供)

结束语

向 ECMWF 团队致敬,他们发布了cdsapi,并使 ERA5 再分析产品变得容易获得。该 API 具有清晰的语法,并且能够在服务器端重新划分输出,这是天赐之物。ECMWF 还在开发一个软件包,旨在进一步简化对这些数据集的访问,这样我们就可以更多地关注科学,而不是编码。在撰写本文时,它仍在开发中,但我鼓励您继续关注 climetlab 。然而,climetlab 确实在幕后下载数据。希望 climetlab 中的一个特性是无需下载就能将输出读入内存。然而,我们总是可以使用 CDS API 来实现这一点。

希望这篇文章能帮助你分析 ERA5 的输出,并简化你的数据管道。编码快乐!

相关职位

**https://medium.com/analytics-vidhya/best-free-api-for-weather-records-era5-59c0e204829a https://medium.com/planet-os/digging-into-the-texas-freeze-using-python-and-era5-zarr-data-fa3c8ff562bb https://medium.com/planet-os/era5-reanalysis-data-on-s3-cee2f22422ae

感谢你花时间阅读我的帖子。这个功能始于与我的同事 Jashvina Devadoss 的对话,她当时正在开发一个 类似的功能

如果你有问题,建议,或者发现这篇文章有用,请在下面评论。

感谢阅读和支持媒体作者

https://lukegloege.medium.com/membership **

用 Python 读 Excel 文件吗?有一个快 1000 倍的方法。

原文:https://towardsdatascience.com/read-excel-files-with-python-1000x-faster-407d07ad0ed8?source=collection_archive---------0-----------------------

理解大数据

在本文中,我将向您展示用 Python 加载数据的五种方法。实现了 3 个数量级的加速。

来源:https://www.hippopx.com/,公共领域

作为一名 Python 用户,我使用 excel 文件加载/存储数据,因为商务人士喜欢以 excel 或 csv 格式共享数据。不幸的是,Python 处理 Excel 文件特别慢。

在本文中,我将向您展示用 Python 加载数据的五种方法。最终,我们将实现 3 个数量级的加速。会快如闪电。

编辑(18/07/2021):我找到了一种方法,可以让进程 快 5 倍 (结果加速了 5000 倍)。我在文末补充了一下作为加分项。

实验装置

假设我们要加载 10 个 20000 行 25 列的 Excel 文件(总共大约 70MB)。这是一个典型的案例,您希望将事务性数据从 ERP (SAP)加载到 Python 来执行一些分析。

让我们填充这个虚拟数据并导入所需的库(我们将在本文后面讨论 pickle 和 joblib)。

import pandas as pd
import numpy as np
from joblib import Parallel, delayed
import timefor file_number in range(10):
 values = np.random.uniform(size=(20000,25))
 pd.DataFrame(values).to_csv(f”Dummy {file_number}.csv”)
 pd.DataFrame(values).to_excel(f”Dummy {file_number}.xlsx”)
 pd.DataFrame(values).to_pickle(f”Dummy {file_number}.pickle”)

在 Python 中加载数据的 5 种方法

想法 1:用 Python 加载一个 Excel 文件

让我们从加载这些文件的简单方法开始。我们将创建第一个 Pandas 数据框架,然后将每个 Excel 文件追加到其中。

start = time.time()
df = pd.read_excel(“Dummy 0.xlsx”)
for file_number in range(1,10):
 df.append(pd.read_excel(f”Dummy {file_number}.xlsx”))
end = time.time()
print(“Excel:”, end — start)>> Excel: 53.4

用 Python 导入 Excel 文件的简单方法。

运行大约需要 50 秒。相当慢。

想法 2:使用 CSV 而不是 Excel 文件

现在让我们假设我们将这些文件保存为。csv(而不是。xlsx)从我们的 ERP/System/SAP。

start = time.time()
df = pd.read_csv(“Dummy 0.csv”)
for file_number in range(1,10):
 df.append(pd.read_csv(f”Dummy {file_number}.csv”))
end = time.time()
print(“CSV:”, end — start)>> CSV: 0.632

用 Python 导入 csv 文件比 Excel 文件快 100 倍。

我们现在可以在 0.63 秒内加载这些文件。快了将近 10 倍!

Python 加载 CSV 文件比 Excel 文件快 100 倍。使用 CSV。

缺点 : csv 文件几乎总是大于。xlsx 文件。在这个例子中。csv 文件为 9.5MB,而。xlsx 都是 6.4MB。

想法 3:更聪明的熊猫数据框架创建

我们可以通过改变创建熊猫数据框架的方式来加快我们的进程。不是将每个文件附加到现有的数据帧,

  1. 我们在一个列表中独立加载每个数据帧。
  2. 然后将整个列表连接到一个数据帧中。
start = time.time()
df = []
for file_number in range(10):
 temp = pd.read_csv(f”Dummy {file_number}.csv”)
 df.append(temp)
df = pd.concat(df, ignore_index=True)
end = time.time()
print(“CSV2:”, end — start)>> CSV2: 0.619

用 Python 导入 csv 文件的更智能的方法

我们把时间减少了百分之几。根据我的经验,当你处理更大的数据帧(df >> 100MB)时,这个技巧会变得有用。

想法 4:用 Joblib 并行化 CSV 导入

我们想用 Python 加载 10 个文件。与其一个接一个地加载每个文件,为什么不同时并行地加载它们呢?

我们可以使用 joblib 轻松做到这一点。

start = time.time()
def loop(file_number):
 return pd.read_csv(f”Dummy {file_number}.csv”)
df = Parallel(n_jobs=-1, verbose=10)(delayed(loop)(file_number) for file_number in range(10))
df = pd.concat(df, ignore_index=True)
end = time.time()
print(“CSV//:”, end — start)>> CSV//: 0.386

使用 Joblib 并行导入 Python 中的 CSV 文件。

这几乎是单核版本的两倍。然而,一般来说,不要指望使用 8 个内核就能让你的处理速度提高 8 倍(在这里,我在 Mac Air 上使用 8 个内核,使用新的 M1 芯片,获得了 2 倍的速度提升)。

用 Joblib 在 Python 中实现简单的并行化

Joblib 是一个简单的 Python 库,允许你在//中运行一个函数。在实践中,joblib 作为一个列表理解。只是每次迭代由不同的线程执行。这里有一个例子。

def loop(file_number):
 return pd.read_csv(f”Dummy {file_number}.csv”)
df = Parallel(n_jobs=-1, verbose=10)(delayed(loop)(file_number) for file_number in range(10))#equivalent to
df = [loop(file_number) for file_number in range(10)]

像智能列表理解一样思考 joblib。

想法 5:使用 Pickle 文件

将数据存储在 pickle 文件(Python 使用的一种特定格式)中可以(快得多)而不是。csv 文件。

****缺点:你将无法手动打开一个 pickle 文件并查看其中的内容。

start = time.time()
def loop(file_number):
 return pd.read_pickle(f”Dummy {file_number}.pickle”)
df = Parallel(n_jobs=-1, verbose=10)(delayed(loop)(file_number) for file_number in range(10))
df = pd.concat(df, ignore_index=True)
end = time.time()
print(“Pickle//:”, end — start)>> Pickle//: 0.072

我们刚刚削减了 80%的运行时间!

一般来说,处理 pickle 文件比处理 csv 文件要快得多。但是,另一方面,pickles 文件通常在您的驱动器上占用更多的空间(不是在这个特定的例子中)。

实际上,您将无法直接在 pickle 文件中从系统中提取数据。

我建议在以下两种情况下使用泡菜:

  1. 您想要保存来自 Python 进程之一的数据(并且不打算在 Excel 上打开它)以便稍后/在另一个进程中使用。将数据框保存为泡菜,而不是。战斗支援车
  2. 您需要多次重新加载相同的文件。第一次打开文件时,将其保存为 pickle,以便下次能够直接加载 pickle 版本。
    示例:假设您使用事务性月度数据(每个月您加载一个新月份的数据)。您可以将所有历史数据保存为。pickle 并且,每次你收到一个新文件,你可以把它作为一个. csv 文件加载一次,然后下一次把它作为一个. pickle 文件保存。

好处:并行加载 Excel 文件

假设您收到了 excel 文件,并且您没有其他选择,只能按原样加载它们。您还可以使用 joblib 对此进行并行处理。与上面的 pickle 代码相比,我们只需要更新循环函数。

start = time.time()
def loop(file_number):
    return pd.read_excel(f"Dummy {file_number}.xlsx")
df = Parallel(n_jobs=-1, verbose=10)(delayed(loop)(file_number) for file_number in range(10))
df = pd.concat(df, ignore_index=True)
end = time.time()
print("Excel//:", end - start)>> 13.45

如何在 Python 中使用并行化加载 excel 文件?

我们可以将加载时间减少 70%(从 50 秒减少到 13 秒)。

您还可以使用这个循环动态创建 pickle 文件。这样,下次你加载这些文件时,你就能达到闪电般的加载速度。

def loop(file_number):
    temp = pd.read_excel(f"Dummy {file_number}.xlsx")
    temp.to_pickle(f"Dummy {file_number}.pickle")
    return temp

概述

通过并行加载 pickle 文件,我们将加载时间从 50 秒减少到不到十分之一秒。

  • Excel: 50 秒
  • CSV: 0.63 秒
  • 更智能的 CSV: 0.62 秒
  • //: 0.34 秒内 CSV
  • //: 0.07 秒
  • //: 13.5 秒

好处 2:并行速度提高 4 倍

Joblib 允许更改并行化后端以消除一些开销。你可以通过给 prefer="threads"Parallel 来做到这一点。

使用 prefer="threads "将允许您更快地运行流程。

我们获得了大约 0.0096 秒的速度(2021 MacBook Air 运行了 50 多次)。

将 prefer="threads "与 CSV 和 Excel 并行化一起使用会产生以下结果。

正如你所看到的,使用“线程”后端在读取 Excel 文件时会导致更差的分数。但是对于 pickles 来说性能惊人(逐个加载 Excel 文件需要 50 秒,而在//中加载读取 pickles 文件的数据只需要 0.01 秒)。

👉让我们在 LinkedIn 上联系吧!

关于作者

icolas Vandeput 是一名供应链数据科学家,擅长需求预测和库存优化。他在 2016 年创立了他的咨询公司 SupChains ,并在 2018 年共同创立了 SKU 科学——一个快速、简单、实惠的需求预测平台。尼古拉斯对教育充满热情,他既是一个狂热的学习者,也喜欢在大学教学:自 2014 年以来,他一直在比利时布鲁塞尔为硕士学生教授预测和库存优化。自 2020 年以来,他还在法国巴黎的 CentraleSupelec 教授这两门课程。他于 2018 年出版了 供应链预测的数据科学(2021 年第 2 版)和 2020 年出版了 库存优化:模型与模拟

阅读评论:分析 Wayfair 产品的 NLP 信号

原文:https://towardsdatascience.com/read-the-reviews-analyzing-nlp-signals-of-wayfair-products-7c31b63cd369?source=collection_archive---------35-----------------------

作者:亚历克斯·斯皮里德艾琳·杨张杰朴相润

4 分钟的海报展示在这里找到。

我们的 Github repo,包含公开源代码和演示示例,这里是。

完成哈佛的 AC297r 顶点课程 。与来自 Wayfair 的 纳撒尼尔·伯班克 合作。感谢课程导师 克里斯·坦纳 和助教 黄家仪

免责声明:某些细节,包括某些图形和数字,在本分析之前已经进行了转换。本文中表达的观点仅是作者的观点,并不反映哈佛大学https://www.wayfair.com/*或* 高级计算科学研究所(IACS) 的观点。

背景

Wayfair 是一家销售家具和家居用品的在线零售商。该公司是电子零售领域的最大玩家,但电子零售仅占更大的家具市场的一小部分。

来自公司各自财务文件的数据。作者图片

质量控制对电子零售商来说是一个挑战,因为他们对库存的控制不如实体零售商。预测未来的退货率在这方面很方便:确定预计未来退货率高的产品为 Wayfair 提供了一种系统解决其库存质量问题的替代方法

对于像 Wayfair 这样的电子商务公司来说,一个丰富的数据来源是文字评论。在最近的一项调查中,92%的受访者称在做出购买决定前至少阅读了一篇产品评论。由于购物者使用这个数据源来评估产品质量,Wayfair 应该利用评论提供的大量数据。这个项目的目标是通过检查自由文本评论是否包含改善回报率预测的信号,来帮助公司朝着这个方向迈出一步。

这种基于语言的方法背后的一些假设包括:

  1. 顾客评论包含了评论者是否退回产品的有意义的信号。
  2. 评论者相当能代表产品的整体客户体验。
  3. 顾客在一个时期对产品的体验为他们在下一个时期的体验传递了有意义的信号。

具体来说,我们使用各种自然语言处理(NLP)技术从评论中提取了几个新特征,并比较了使用这些 NLP 特征的模型与不使用这些特征的模型的性能。通过使用 2017 年的数据来训练所有模型,以预测 2018 年的比率;通过使用 2018 年的数据预测 2019 年的比率,对它们进行了测试。

训练集和测试集定义。作者图片

我们使用两种类型的任务来比较具有 NLP 特征的模型和没有 NLP 特征的模型。第一个任务应用回归模型直接预测下一年的回报率。第二个任务是对产品在下一年是否会有异常回报率进行分类。每项任务代表一个独立的业务用例,测试这两项任务的目的是为了更全面地了解 NLP 功能如何帮助改善 Wayfair 的数据驱动决策和实践。

在这个项目中,我们使用了 way fair 2017 年、2018 年和 2019 年的产品级和审查级数据。产品级数据包括市场类别(如照明)、平均重量、订单量(分类)和退货率(按比例调整;它不反映 Wayfair 产品的实际回报率)。评论级数据包括评论(文本)、评级(1 到 5;分类)、产品退货状态和退货原因。

NLP 功能

我们的假设是,评论文本包含关于客户体验的额外信息,这些信息无法通过评级等传统方法完全捕捉到。为了测量评论文本中多样化和丰富的信息,我们探索了不同的提取方法,包括预训练模型(情感分析)、迁移学习(BERT 可返回性)和无监督学习(主题建模)。

情感分析

自然语言工具包(NLTK)包含预先训练的情感分析和可以应用于文本数据的主观性模型。为了测量情感的强度,NLTK 实现了 VADER。Valence Aware 字典和情感推理器(VADER)是一个基于规则的情感分析器,是为社交媒体情感而创建的。从我们的分析中,我们发现评论通常是非正式的,这使它们更像社交媒体上的帖子,而不是冗长的散文。此外,VADER 可以返回从 0 到 1 的积极、消极和中性情绪的强度,以化合价衡量,以及从-1 到 1 的复合得分

在我们对个人评论的主观分析中,我们得出结论,VADER 比 NLTK 的情感分析器有更好的表现。例如,VADER 的文档显示,它可以正确地将“我喜欢讨厌迈克尔·贝电影,但我不能挑这部电影的错”这句话归类为积极的,而 base NLTK 情绪分析器却在努力解决这个问题。基于我们自己对情感的主观解释,我们同意 VADER 在 NLTK 上的配价特征。

作者图片

我们的情感特征包括来自 VADER 的正面、负面、中性和复合评分,以及 NLTK 的主观性评分。这些分数是为每个非空文本评论计算的,并通过数据集的简单平均值进行汇总。还增加了每个产品的评论数量。

总的来说,上面的图表明,情感分数与我们对它们与评级的关系的预期相符。高评级产品有正面和主观评价,而低评级产品在负面上得分高,倾向于更客观。

伯特返回能力

BERT 是 Google 预先训练的语言模型。基于具有注意机制的 transformer 架构,它能够学习单词之间的上下文关系,并在许多下游任务(如文本分类)中表现出令人印象深刻的性能。因此,我们决定利用 BERT 对人类语言的复杂理解,从综述数据中提取预测信号。

对于迁移学习,我们使用我们的定制前馈神经网络(2 个隐藏的密集层和一个 softmax 输出层)扩展了现有的 BERT 基础架构,以构建一个分类器来确定评论是否导致产品退货。我们使用 2017 年经过验证的文本评论(约 450 万)来训练/微调整个模型(即包括 BERT 中的权重)。

作者图片

由于训练数据的类别高度不平衡(只有 1.6%的验证文本评论导致产品退货),我们对较大的类别(即未退货)进行子采样,使其大小与较小类别(即退货)的大小大致匹配,并在模型训练中应用类别权重。

然后使用训练好的模型来计算每个评论被返回的概率,然后通过取平均值将这些评论级别的概率聚集到产品级别。我们将这个 NLP 特性命名为可返回性

由于单个数字(可退货性)可能无法捕捉 BERT 提取的丰富信息,我们决定也使用一个与退货相关的评论质量的多元表示。具体来说,我们从基于 BERT 的分类器的最后一个隐藏层中提取了 16 个值,并使用它们的产品级聚合作为与产品回报相关的附加 NLP 特征(下文中称为“嵌入”)。

作者图片

虽然我们知道这 16 个新功能共同编码了与产品退货相关的信息,但每个功能的解释并不简单。因为基于 BERT 的分类器的最后一层本质上执行逻辑回归,所以相应的权重可以被视为逻辑回归中的系数。这有助于我们更好地理解每个嵌入的作用。例如,对应于第 6 个嵌入(即分类器的最后一个隐藏层中的第 6 个节点)的权重是正的,这表明嵌入与输出(即可返回性)正相关。

话题建模

主题建模的基本思想是,研究给定年份中的所有客户评论,可以让我们收集一组单个评论通常涉及的共同主题。如果我们能够提取与导致高或低回报率的因素(以前未捕捉到的)相关的主题,我们可能能够改进回报率的预测。由于这是一种无监督的方法,我们不能指定主题模型应该查看哪些特定的主题。因此,我们的目的是使用这种无监督的超参数调整方法,以提取携带有用信号的主题簇。

主题建模的预处理阶段包括去除停用词和空格,应用词汇化来检索每个词的原始形式,以及对每个处理后的词应用分词器。我们没有猜测主题的数量,而是使用分层的狄利克雷过程(HDP)来生成数量。以下是 20 个主题特征中最有用的两个主题的关键词。通过检查这些关键词,我们可以知道 topic_6 与组装家具的总体积极体验相关;而 topic_18 是关于产品的颜色以及是否与网站上的图片匹配。

密度图显示主题分数在所有评论中的分布,图片由作者提供

回归

一旦这些产品级的 NLP 特征被提取出来,我们就将它们聚合并合并到我们的产品级设计矩阵中。数据是“左连接的”,意味着我们保留了“当前”(相对于“预测”)年销售的所有产品;未来回报率中的缺失值用每个相应产品的当年回报率来估算。产品级特征中的缺失值(即预测值)用各自的类别级中位数进行估算。

使用这个扩充的数据矩阵,我们然后建立了预测回报率的回归模型,以检验这些新功能是否在预测未来回报率方面给我们带来了性能提升。我们使用 2017 年的数据进行训练(即预测 2018 年的费率),使用 2018 年的数据进行测试(即预测 2019 年的费率)。模型性能使用我们的自定义指标进行评估,稍后将对此进行解释。

由于不同市场类别的产品在回报率方面可能有不同的模式,我们决定为每个市场类别分别构建模型,以进一步检验其表现。

不对称度量

由于这是一个回归问题,我们最初使用 RMSE 作为我们的评估指标。然而,在这种情况下,对 Wayfair 来说,高估一种产品的未来回报率比低估它的成本更高。为了将这种保守的方法正式化,我们设计并使用了如下非对称 RMSE:

我们将𝛼设置为 0.3,这样,如果预测回报率高于实际回报率,该函数将对我们的模型进行更多惩罚。另一方面,如果𝛼 < 0,我们的不对称损失会因预测不足而受到更多惩罚。Wayfair 可以根据他们的业务目标微调价值。

作者图片

神经网络 vs 随机森林

由于我们不确定哪种模型类型会对我们的数据表现良好,我们尝试了随机森林回归器和前馈神经网络。对于随机森林回归,模型是通过改变每片叶子的最小样本和最大深度的 k-fold 交叉验证来选择的。由于模型包(scikit-learn)不允许我们在拟合模型时更改损失函数,因此我们使用 MSE 标准优化了随机森林,但使用我们的自定义非对称指标评估了其性能。对于神经网络,我们为每个市场类别创建了单独的前馈神经网络,并使用我们的非对称度量作为损失函数直接优化它们。性能比较如下所示:

作者图片

这里的 x 轴代表市场类别,y 轴上的测试分数来自我们的自定义不对称损失。虽然在大多数市场类别中,神经网络似乎优于随机森林回归模型,但与随机森林回归模型优于神经网络的类别相比,其幅度相当小。神经网络显示了一些缺点:与随机森林回归相比,它们更难解释,也很难微调,而且它们在数据量小的市场类别中存在较大的差异。考虑到这些因素,我们决定选择一个随机森林作为回归分析的最终模型类型。

NLP 能提高回归性能吗?

功能总结。作者图片

为了回答 NLP 是否能提高回归性能的问题,我们使用随机森林回归器来检验 NLP 特性是否能提高我们预测回报率的性能。我们比较了两组模型:一组用非 NLP 和 NLP 特征训练,称为完整模型,另一组只用非 NLP 特征训练。我们将他们的表现与一个简单的基线模型进行了比较,该模型预测下一年的回报率与上一年相同:如果一种产品的 2017 年回报率为 10%,基线将在 2018 年为该产品输出 10%的回报率。

紫色条

我们可以看到完整模型(具有 NLP 特性)确实提高了测试集的性能,但幅度很小。这就提出了一个问题,即这种改善是否具有统计学意义,或者我们的模型只是在测试集上过度拟合。因此,我们使用了 bootstrap 方法,对测试集进行重新采样,然后重新比较完整模型和非 NLP 模型的性能。

因为分数δ是正的,NLP 特征有意义地改进了预测。作者图片

该图的 X 轴表示通过引入 NLP 特征,测试集上不对称 RMSE 分数的降低。x 轴上的正刻度表示 NLP 有帮助。因此,该图显示了完整模型的性能提升是稳健的。虽然改善的幅度很小,但这些 NLP 特性确实有助于预测未来的回报率。

分类

鉴于回报率预测通常是在识别“高风险”产品的背景下进行的,我们还对异常值检测任务的模型进行了实验和比较。我们将异常值定义为每个市场类别中回报率最高的 10%的产品。为了确保这一标准是合理的,对每个市场类别的回报率阈值进行了如下目测检查:

每个市场类别的回报率分布。作者图片

使用三种模型类型来比较该异常值检测任务的模型性能:

  1. 基线:我们创建并检查了一个不涉及预测因素的基线绩效,我们简单地假设在某一年是异常值的产品在下一年仍然是异常值。
  2. 回归变量+排名:我们使用了回归任务中的同一个回归变量,但将其输出(即预测回报率)转换为类别内排名,用于指定异常值。我们的期望是回归变量可以利用其预测回报率的能力来为异常值生成更好的预测。
  3. 分类器:由于离群点检测是一个分类问题,我们还构建了一个简单的分类器,它在与回归器相同的设计矩阵上进行训练,但具有二元离群点基础事实。

如前所述,我们宁愿以容忍有限数量的假阴性为代价来避免假阳性。因此,我们选择精度作为离群点检测的评估标准。

随机森林分类器击败了基线,NLP 功能提高了性能。作者图片

结果表明,基于精度,分类器击败基线模型,具有最佳性能。更重要的是,我们观察到,在相同的模型类型中,包含 NLP 特征始终会导致更好的模型性能。换句话说,NLP 特征在检测异常值时确实揭示了一些有用的信号。

NLP 功能提高了大多数市场类别的精确度分数。作者图片

由于我们的数据包含超过 40 个市场类别,我们希望检查当从非 NLP 分类器切换到包含 NLP 功能的分类器时,每个市场类别内的精度分数如何变化。下面的散点图显示了使用非 NLP 功能和使用完整功能集的精确得分之间的比较,每个点都是一个市场类别。如果数据点落在灰色对角线上(y=x),则意味着在加入 NLP 特征时没有改善。当使用 NLP 功能时,落在红线以上的数据点表示精度提高。通过使用非 NLP 和完整分类器计算所有类别的精度分数,我们观察到包含 NLP 特征导致大多数类别的精度提高。事实上,添加 NLP 功能可以带来高达 30%的改进。仅在一个类别中观察到精度下降(当使用 NLP 特征时)。

虽然我们有点估计来说明完整模型对非 NLP 模型的改进,但重要的是要确保这种改进不仅仅是对测试集的过度拟合。因此,我们使用 bootstrapping 对测试集中的产品进行重新采样,并根据评估指标(即精度)重新评估模型:

分数δ显示 NLP 特征显著地提高了分类性能。作者图片

上面的图表明,当添加 NLP 特征时,模型的测试精度提高了,并且这种提高对于测试集中的变化是稳健的。因此,虽然我们的改进幅度很小,但我们的 NLP 功能很可能会带来有意义的改进。

结论

这个项目的目标是确定自由文本评论是否能提高回报率的预测。基于我们的回归和分类结果,我们认为答案是肯定的。随着 NLP 特性的增加,我们的随机森林和神经网络回归器不断改进。类似地,回归器+秩和离群分类器也随着 NLP 特征的增加而改进,并且我们检查了这些改进对测试集中的变化的鲁棒性。

然而,性能的提高虽然持续,但幅度仍然很小。这就提出了统计意义与实际意义的问题。统计显著性是衡量一个事件在零假设下是否不太可能发生的指标,无论差异的大小有多小。而实际意义则要求效果尺寸大。虽然我们没有进行正式的假设检验,但基于我们的 bootstrap 和回归分析,NLP 的改善似乎在统计意义上持续存在。然而,实际上,改进的幅度仍然很小。

考虑到这一点,我们对 Wayfair 的建议取决于预测回报率的业务用例。在某些情况下,预测未来回报率的任何改进都是有价值的。在这些情况下,上面讨论的方法可以作为一个重要的新工具。在其他情况下,考虑到它们产生的预测准确性的小增益,合并 NLP 特征的额外复杂性可能是不值得的。

感谢

我们要感谢来自 Wayfair 的 Nathaniel Burbank 对这个项目的指导。黄家仪是一个无价的教学伙伴,给了我们非常宝贵的反馈。最后,感谢 Chris Tanner 帮助我们理解 NLP,并带领我们学习这门课程。

与 Wayfair 和 IACS 合作

参考

[1] D. Blei,潜在狄利克雷分配 (2003),机器学习研究杂志

[2] J. Devlin 等人, BERT:用于语言理解的深度双向转换器的预训练 (2018),arXiv 预印本

[3] C.J .休顿和 e .吉伯特,【VADER:一种基于简约规则的社交媒体文本情感分析模型】 (2014),第八届 AAAI 网络博客和社交媒体国际会议论文集

选择梦想数据科学项目的 10 个技巧

原文:https://towardsdatascience.com/read-this-before-going-to-grad-school-for-data-science-890fd05d4127?source=collection_archive---------37-----------------------

提示 3:关注中位数,而不是平均数

我在国外展示 UMich 装备的照片(是的,我穿着短裤)。

当我申请研究生数据科学项目时,我觉得自己基本上是在瞎猜。我对我申请的学校的声望有所了解(也就是说,如果我听说过或者没有听说过的话),可以在网站上的某个地方找到费用,并且通常知道当我不可避免地投身于就业市场时,标有“机器学习”的课程会很好(谢谢,新冠肺炎)。

不过,网站上有很多比实际项目信息更有光泽、更有笑容的股票照片。对于一年 5 万美元以上的投资(至少在美国),老实说,有时我们很少被告知一个学位的价格相当于一栋小房子的价格,这令人震惊。

如果你选择通过传统的教育途径进入数据科学领域,在你开始之前,这里有一些值得思考的东西。有些事情我事先就明白了(因为我很节俭,以至于 2 美元一杯的咖啡会让我有负罪感),有些事情我是通过艰难的方式发现的。

当然,每个人的数据科学之旅都是独一无二的,如果你能从这篇文章中获得 1-2 条建议,我希望它是值得的!

照片由乔丹·恩卡纳卡奥Unsplash 拍摄

就业统计📈

如果你从这篇文章中得到一条建议,在选择去哪所学校之前,一定要查看就业统计数据。选择一所就业率高的学校会提高你的基本工资,这是决定你一生收入潜力的主要因素。对于数据科学/分析而言,任何研究生的基本工资为 9 万至 9.8 万美元,签约工资为 5 万至 1 万美元的硕士项目都是非常正常的。

你可能会惊讶有多少学校达到了这些数字;例如, NC State ,一所我从未听说过来自中西部的学校,有一个分析硕士项目,平均底薪为99,800 美元,签约费为 10,400 美元,83%的毕业生获得了不止一份工作。先决条件也不太过分,而且只有 10 个月!

北卡罗来纳州也是发布统计数据的明星,我希望其他学校也能效仿。此外,这是一个在分析项目的硕士学位,它已经成功地将毕业生安置到数据科学家的工作岗位上;无论你最终选择成为数据科学家、数据分析师还是数据工程师,研究这两种类型的计划都将有助于扩大你的搜索范围,找到最适合你的位置。

最后,学校通常会在他们的就业统计中报告平均而不是中值基本工资。这是为什么呢?

坦率地说,这些项目的平均值通常要高得多;如果几个毕业生在硅谷找到了 15 万美元的工作,这将提高整个群体的平均水平,让学校看起来非常非常好。

这让我在之前的薪资谈判中焦头烂额——如果可能的话,把工资中位数放在你的口袋里,因为这些数字对普通毕业生来说更现实,因此找出这些数字很重要。

提示汇总列表:

  1. 从一个好的项目中,你可以期望大约 9 万到 9.8 万美元的平均基本工资和 5 万到 1 万美元的签约费,这可能会因你所在的美国地区而异。
  2. 为了扩大你的搜索范围,同时申请数据科学硕士和分析/商业分析硕士学位课程。
  3. 起薪中值将比学生的平均起薪更现实。
  4. 一个好的就业统计数据的例子是这样的。

照片由哈利·菲尔普斯Unsplash 上拍摄

节目长度📊

好了,你已经缩小了你想申请的项目的范围。也许你已经进入其中的一两个了;如果是这样,恭喜你!🎉

如果你像我一样对金钱有责任感,这就是真正的计算开始的地方。我想优先考虑四件事:

  1. 程序长度
  2. 学费
  3. 生活费
  4. 平均(或者更好,中位数)起薪

对我来说,这些是我离开的主要限定条件,因为一个叫做机会成本的小东西。

我先解释一下我自己的思考过程。按照你们典型的书呆子气、神经质、非 STEM 本科生的说法,去年我真的很担心我会进不了任何技术硕士项目。但是我和 T2 被研究生院录取了,包括芝加哥大学的一个很棒的项目。我认识的每个人,尤其是我的家人,都为我的成就欣喜若狂,并鼓励我在这样一个享有盛名的地方求学。

我承认我也开始有点忘乎所以了,想象着金色拱门,想象着我可能在浴室里碰到的诺贝尔奖获得者… 然后我收到了账单。

即使在“奖学金”之后,我每年需要承担的学生贷款估计约为 90,000 美元,或 180,000 美元,这在很大程度上是因为在芝加哥这样的地方生活的费用,而且这是一个为期 2 年的项目(😧 ).相比之下,我去的那所学校预计总费用约为 5 万美元。更奇怪的是,两所学校的平均起薪相差无几!

从中得到的教训是,好好想想你愿意为声望付出多少。对我来说,我不愿意花将近 4 倍的价钱买一个更花哨的名字,因为我觉得外表看起来并没有给我带来任何明显的终生收入差异。单从利息来看,180,000 美元比 50,000 美元更难还清,项目时间延长了一倍;如果我生活节俭,在第二年末,我知道我可以无债一身轻,而不是负债累累。

提示,总结如下:

5.如果去最有声望的学校会让你负债累累,那就好好想想吧。这并不是因为奶奶高兴才做出的举动——她不会在未来 10 年里以固定的速度承受后果。

6.在我的公立大学经历中(密歇根大学→弗吉尼亚大学),我知道有学生已经收到了脸书、麦肯锡、波士顿咨询公司和亚马逊的工作邀请。我甚至最近接受了麦肯锡的采访,才意识到这不是我的强项。如果你正在寻找一份 FAANG 或咨询工作,一些学校有足够的运气被那些大人物注意到——即使没有常春藤要求的大量首付。

7.学校通常会公布学生之前被安排的公司名单,所以如果你真的对工作充满热情,我建议你打电话给招生办公室,看看最近几年有多少校友收到了他们的邀请。

肯·泰默在 Unsplash 上拍摄的照片

数据决定一切📉

这最后一部分是我在申请时遇到的无数问题的大杂烩,也是我建议你在职业生涯中迈出这一重大步骤时为自己提出的问题。当然,当你不确定是否可以进行有根据的猜测时:

  • 这个项目是独立的,还是计算机科学/统计/数学系的混合体?
  • 是否有动手实践的顶点项目,顶点项目是否为学生的劳动提供任何形式的学费补偿(麻省理工学院提供【22k 美元学费补偿 ,而大多数地方不提供任何东西)。
  • 该项目中是否有任何专业,或者至少有一些课程可以让你进行“软专业”,从而成功地向雇主传达你的领域兴趣?
  • 即使这所学校不是常春藤盟校,你最感兴趣的地方(如硅谷)的公司会对这所学校有正面的印象吗?
  • 职业资源在帮助你获得数据科学工作方面有多大帮助?
  • (仅限留学生)这个项目有没有 STEM 分类状态

我的建议是收集一个类似这样的问题列表,制作一个 Excel 表格,并开始为你正在考虑的每所新学校填写一行。这种方法帮助我进行直接比较,监控截止日期,并跟踪谁为哪些学校写了 lor(如果你有兴趣看看我去年疯狂的 Excel 表格,请在下面的评论中告诉我)。

如果你正在申请数据科学学校,我猜你也可能是一个数据爱好者——这是一个完美的时间来展示你的热情,将严格的数据收集整理到一个干净整洁的 Excel 表格中。

最后,临别提示:

8.数据决定一切— 您收集的指标越多,您就能做出最明智的决策。

9.一项统计数据没有公布并不意味着没有记录。在这种情况下,录取通知书将再次成为你最好的朋友。他们通常把统计数据藏在某个地方——即使网站上没有列出来——而且几乎总是愿意随时接听电话(尤其是如果你已经被录取了,那么他们真的想接你的电话)。

10.通过 LinkedIn 与现在的学生联系可以帮助获得 T4 真正喜欢参加什么活动的内幕消息。

照片由 Joseph ChanUnsplash 上拍摄

结论

“你不必看到整个楼梯,只需迈出第一步”——MLK

希望这 10 个技巧能对你有所帮助,并祝你在数据科学之旅中好运。如果你对更多关于申请数据科学学校的帖子感兴趣,这篇帖子实际上是我关于这个主题系列的第三篇(下面列出的第一篇和第二篇)。再次感谢,并保持安全!🍁

在编写下一个 SQL 查询之前,请阅读这篇文章。⌨️

原文:https://towardsdatascience.com/read-this-before-you-write-your-next-sql-query-️-de6b3b2c449a?source=collection_archive---------3-----------------------

小窍门

或者:为 Airbnb 的首席执行官写即席查询的经验教训。

不要用你糟糕的问题毁掉你的公司。(图片由作者提供)

当我在 Airbnb 的时候,我有一个极好的机会在一个向 Brian Chesky 汇报的新团队中工作。这很令人兴奋——我们正在研究一个新的产品线,所以我们每天都必须做出改变游戏规则的决定。但是作为团队的数据科学家,我开始负责获取数据来指导我们的产品方向,这意味着大量的分析工作。

第一周对我的上下文切换能力是一次严峻的考验:我不仅要找到晦涩的表格,编写大量的查询,还要通过beautifulsoup搜索正则表达式,并对 API 请求进行质量控制。到了第三周,我开始变得疲惫(而且邋遢!),所以我需要一个系统来跟上我的速度。我意识到,在处理数据时,只有两种方式会出错:

  1. 使用错误的数据。
  2. 用错了数据。

这两个问题都可以通过在您的数据周围有更好的上下文来解决。

所以我给自己做了一个清单来减少这些错误,确保我不会让产品被遗忘。我将在此分享我的观点,但具体是什么样的可能取决于您公司的特定堆栈。以此为例,你可以找到一些好的背景来降低使用桌子的风险,但我鼓励你把它变成你自己的。

那么我需要什么样的上下文,我如何得到它?🤔

嗯,你需要任何和所有的信息来减少错误使用数据或使用错误数据的机会。根据我的经验,你只需要三张支票就可以得到合理的保险:

  1. 🗃检查基础表元数据。 例如,列名,分区信息,它是如何产生的,等等。
  2. 🔭检查你的假设。
    这一栏是什么?这是空列吗?有哪些鲜明的价值观?自从我最后一次运行这个查询以来,这些改变了吗?
  3. 💏向别人咨询。
    其他人拿这张桌子做什么?如果你有问题,你会问谁?

🗃 1.检查基本表元数据

第一步是找到一个表,并弄清楚如何查询它。

在你的桌子泄露秘密之前,你必须向它求爱。(经 Olya Tanner 许可转载)

对于最基本的信息,比如列名、索引信息、分区信息、视图定义,通常可以查询系统表。将这些表列在手边,以便于查询。例如,对于 ANSI SQL 兼容的数据库(大多数),记住下面的表通常是有帮助的:

  • 列名,分区信息,列类型,可空性。
  • information_schema.tablesinformation_schema.views 漂亮的所有表格和视图列表。对于视图,通常也可以得到DDL语句。

您通常还可以获得其他人编写的查询历史,这有助于了解如何使用表。您甚至可以按语句类型(如createinsertselect)过滤这些语句,以确定如何创建表格:

  • INFORMATION_SCHEMA.JOBS_BY_PROJECT(big query)
    table(information_schema.query_history())(雪花)

🔭 2.检查你的假设。

写下你的假设并运行查询来检查它们。

一个人制作清单的很好的例子,如果你以前没看过的话。

此时,您想看看数据是否如您所想。虽然我的典型方法是随意遍历select *select distinct语句,但这并不是最优的。更好的方法是首先弄清楚:

我需要回答什么问题,我在做什么假设?

写出这些问题,然后写出回答这些问题/验证这些假设的问题。这听起来可能微不足道,但是如果你做了错误的假设,你将不得不重新开始。我们在处理数据时都会做出假设——如果你对这些假设不明确,就会导致灾难。

最近一个项目的一些例子:

  • 每个事件只有一行吗?
  • 该字段的可能值是什么?
  • 这个列曾经是空的吗?
  • 如果为空,这些空值是否有系统的模式?

我个人使用 whale (CLI 工具,如果我感到不耐烦的话)或 Hyperquery 来运行(甚至安排)这些快速检查,但是不管你使用什么,只要确保将它们保存在某个地方。

最后,是的,很好——继续运行一个 **select *** 有时候你只需要看一段数据。

💏 3.找别人核实一下

现在您已经对数据的形状有了一个概念,很容易就开始构建您需要构建的东西。不要。你需要获得尽可能多的关于这些数据的社会背景和部落知识——在大型组织中尤其如此。

现在是时候收集部落知识了。

我知道这些人没有脸,但在获得一些额外的社会背景后,你就不能感受一下右边那个人的喜悦吗?

不幸的是,仅仅通过挖掘数据你能得到的就只有这么多了。你需要和一个真实的人交谈(或者找一些最新的文件)。

查看查询日志(见上文),苦读 Github(如果您的查询是受版本控制的),或者检查谁是表的所有者(您通常可以在数据上下文/发现工具中这样做,如 Hyperquery )—只需找个人放松一下。

一般来说,我会问以下问题:

  • 这种情况还在持续吗?
  • 这是用于{{ your_use_case }}的最佳数据吗?

公开你目前所做的事情。你可能在错误的桌子上,但是人们欣赏一些尽职调查。

结束语

如果这一切听起来有点难以承受,请查看Hyperquery我们正在构建一个数据环境平台,让查找所有这些信息变得更加容易,而且是免费的。在一个地方搜索表格、获取基本元数据、浏览数据和获取社会背景。好了,推销结束。抱歉,我控制不住自己——我只是对我们的产品感到非常兴奋。😁

我想让你从这篇文章中得到的一个信息是:对待 你的即席分析工作要像对待其他任何事情一样严谨。在你的职业生涯中,最尴尬的事情之一就是中途意识到你的结果可能是错的。围绕不可协商的上下文设置一些最佳实践,以便在每次编写查询时获得是一种很好的方式,至少可以减轻一些负面影响。

还是自己体会吧。然后回到这里再读一遍。🙂

推文@ imrobertyi/@hyperquery来问好。👋
关注我们
LinkedIn。🙂 要了解更多我们在 Hyperquery 所做的事情(并注册我们数据上下文平台的私有测试版),请访问Hyperquery . ai (如果你有兴趣帮助构建这个下一代工具,我们正在招聘——查看我们的 空缺职位 。)

如果你正在考虑从事云计算职业,请阅读这篇文章

原文:https://towardsdatascience.com/read-this-if-youre-considering-a-career-in-cloud-computing-1eca26239c51?source=collection_archive---------5-----------------------

一份完整的职业指南,涵盖角色和职责、技术技能、一般薪资范围和相关认证

图片由 Freepik 上的 pressfoto 提供

“我不想在云计算上浪费时间;我专注于数据科学。”

这是我对我的一个朋友说的话,当时他建议拥有云计算方面的技能变得越来越重要。我是多么无知啊!

众所周知,行业证明我错了。如果你身处(或渴望成为)科技领域,无论你的角色如何,云技能都至关重要。所以我尝试了一下,让自己获得了 Azure 认证。

最近,我的一位学员询问我对专注于云的职业的看法。虽然我有一些行业知识,但我不知道在专注于云的职业生涯中会有什么期待。所以我花了一些时间从云学院EdurekaGCPAWSAzuresimple hired、等等进行了研究。

在我的研究中,我意识到有很多角色是特定于某些公司的。问题是,作为一个初学者,关注这些角色是没有意义的。相反,你最好学习大多数公司中更常见的角色所需的技能。

在本文中,我将列出大多数专注于云的公司中常见的 5 种角色。但是,不仅仅是这些,让我们来讨论一下角色和责任,所需的技能,工资范围,以及如何通过认证来展示自己。

我们开始吧,好吗?

1.云工程师

云工程师是一个初学者友好的角色,主要围绕着成为一个专注于云的软件工程师。

他们的工作主要是编写代码,编写和测试新功能,发布部署代码,构建移动、web 应用程序和 API,以及正在进行的软件项目的日常维护。

技术

你需要所有的软件工程技能,还要熟悉至少一个云平台,最好是目标公司使用的平台。更具体地说,云学院概括为:

  • 前端:HTML、CSS、JavaScript、Node.js、Python 和 Ruby
  • 后端:Golang,Node.js,Python,Ruby
  • 测试驱动开发、单元测试和集成测试
  • 数据库(关系数据库和 NoSQL 数据库)
  • 软件部署的基本技能
  • Linux 内部、服务器和云应用程序开发

不要被所有的技巧淹没;这是经过多家公司调研。理想情况下,你只需要一两种目标公司特有的语言。

一般薪资范围

云工程师的工资很高,平均来说,比传统软件工程师挣得多。根据 simply hired,的平均年薪为 117,380 美元,根据你的经验水平,年薪从 78,000 美元到 175,000 美元不等。

相关认证

认证在云计算领域很受欢迎,并且肯定会向潜在雇主展示你的云计算技能。

你不必让自己在所有 3 个方面都获得认证;其中一个会给你一些急需的可信度。

2.数据工程师

数据工程师是通向商业洞察力的大门。他们帮助构建收集和存储数据的数据架构,以及使利益相关者更好地理解业务的分析工具。

请记住,在更成熟的组织中,这些更有可能存在。如果你有软件工程背景,数据工程可能是你进入数据科学世界的大门。

数据工程师是一个对初学者友好的角色,但是你需要掌握一些特定的技能。

技术

你基本上需要熟悉数据库周围的一切——这是你的核心技能。根据云学院,需要的技能有:

  • 与不同数据库和软件系统(Python 或 Java)通信的编程技能
  • SQL,NoSQL
  • 数据仓库和分析工具
  • Hadoop、Spark、Storm 或 Kafka
  • 基础设施工程
  • 提取-转换-加载过程
  • 云提供商及其相关服务

一般薪资范围

数据工程师的工资很高,平均来说,甚至比云工程师还高。根据 simple hired,的平均年薪为 123,465 美元,根据你的经验水平,年薪从 82,000 美元到 183,000 美元不等。

相关认证

从这三个认证中选择一个来展示你的技能。

3.系统管理员

系统管理员是组织中的无名英雄。他们在幕后工作,防止任何 IT 灾难的发生,同时确保其 IT 基础架构始终运行。

这是每个公司都必须具备的重要角色,但有些公司将这些角色外包给第三方。现在,在云的环境中,虽然这可能意味着自动化云基础设施,但是需要一些编程来将事物组织在一起。

技术

本科计算机科学学位一般不教授所需的技术技能。然而,你可以从专业硕士学位、外部认证或网络课程中获得这些技能。

根据云学院,需要的技能有:

  • 了解不同种类的操作系统
  • Bash 或 shell 脚本
  • Linux 管理员应该知道像 Red Hat 或 Debian 这样的主要发行家族。
  • Windows 管理员应该配备 Powershell
  • 虚拟化(例如,VMWare)
  • 集装箱(如码头工人)
  • 配置管理(例如,Ansible、Chef 或 Puppet)

一般薪资范围

据《T2》报道,系统管理员的平均年薪为 80,890 美元。然而,值得注意的是,根据系统的复杂程度和你的经验水平,不同公司的薪酬有所不同,从 5 万美元到 13 万美元不等。

相关认证

这里有 4 个证书可以最好地展示你的技能。

4.DevOps 工程师

DevOps 工程师了解软件开发生命周期和用于开发 CI/ CD 管道的各种自动化工具。

Edureka 将 DevOps 定义为一种软件开发策略,它在工程师和 IT 团队之间架起了一座桥梁。然而,角色大多是模糊的,因为它们因公司而异,有时在同一公司的团队之间也是如此。

尽管是桥梁,他们的核心技术能力是构建、部署和运行软件。作为一个初学者,一旦你掌握了所需的技能,这是一个可以实现的角色。

技术

云学院概述了行业所需的通用技能:

  • 选择编程语言:Golang、Ruby、Python 或 Node.js
  • Linux shell 脚本(BASH)
  • 容量规划
  • 构建、测试和部署管道
  • 生产测试基础设施
  • 发布工程
  • 持续交付、部署渠道
  • 配置管理:Ansible、Chef 或 Puppet
  • 监控、记录和警报基础设施
  • 生产问题的诊断和解决
  • 测试驱动开发
  • 容器编排:Docker Swarm 还是 Kubernetes

一般薪资范围

数据工程师的工资很高,平均来说,甚至比云工程师还高。根据 simple hired,的平均年薪为 115,941 美元,根据你的经验水平,从 8 万美元到 16.8 万美元不等。

相关认证

从这三个认证中选择一个来展示你的技能。

5.云架构师

云架构师是推动云转型的人,是涉及云系统整个架构的决策的关键。

架构师所做的决策对公司的发展有着长远的影响;因此,这些高级人员通常会与领导层以及技术团队合作,以说明业务需求和技术可行性。

除了组织范围的转变之外,他们还帮助设计技术团队承担的每个新项目。

我不建议初学者这么做;通常情况下,一旦你在工作中展示了相关的技术技能,你就会得到晋升。

技术

云学院概述了行业所需的通用技能:

  • 软件工程:编程、数据库设计、测试方法
  • 云基础设施:对所选平台(AWS、GCP 或 Azure)的透彻理解
  • 技术债务管理
  • 强大的业务领域知识,有助于做出关键决策

一般薪资范围

由于这一职位需要高级经验,因此薪酬是所有专注于云计算的工作中最高的。一些公司甚至通过奖金和津贴进一步推动这一趋势。

根据 simple hired的数据,的平均年薪为 136,990 美元,尽管它可能会从 98,000 美元波动到每年 190,000 美元。从长远来看,这绝对是收入最高的工作之一。

相关认证

从这三个认证中选择一个来展示你的技能。

这些认证很有挑战性,需要动手实践,所以我建议使用这些云平台中的一个,然后考虑参加认证考试。

最后的想法

显然,在云计算中有更多具体的角色,这取决于你所关注的公司。但大多数初学者最好专注于这些常见角色中的一个,大多数公司都有。这些角色是:

  1. 软件工程师
  2. 数据工程师
  3. 系统管理员
  4. DevOps 工程师
  5. 云架构师

除了云架构师——一个高级职位,在获得晋升之前,你可能必须扮演其他角色之一,其余的角色都是初学者友好的。

我希望你能很好地理解这个行业的角色和期望。我在这个平台上广泛地写下了我在数据科学之旅中的学习和经历。如果你有任何问题,请随时联系我。

如今,随着大多数公司转向云计算,机会是无限的。真正的问题是:你会利用它们吗?

要获得更多关于进入数据科学、真实体验和学习的有用见解,请考虑 加入我的电子邮件好友私人列表

如果你看重这类文章,想支持我这个作家,可以考虑 报名成为中等会员 。每月 5 美元,你可以无限制地阅读媒体上的故事。

https://arunnthevapalan.medium.com/membership

阅读和解释汇总统计数据

原文:https://towardsdatascience.com/reading-and-interpreting-summary-statistics-df34f4e69ba6?source=collection_archive---------7-----------------------

照片由哈利·鲍尔斯Unsplash 拍摄

如何从描述性统计中提取信息的指南

典型的数据科学项目始于数据争论。它是清理杂乱数据并将其转换为适当格式以供进一步分析和建模的过程。

该过程的下一步是探索性数据分析或 EDA。这是您发现数据中隐藏的问题和异常的地方。最重要的是,这是发现信息和见解的开始。

在本文中,我将讨论 EDA 中最有用但未被充分利用的工具之一——描述性统计。

描述性统计充满了信息和见解。一双有经验的眼睛会仔细观察每一个数据点,并从汇总表中提取有价值的信息。然而,我在数据科学中见过许多人,他们在以有意义的方式解释统计摘要时面临困难。有些人会简单地浏览一下,然后尽快进入下一部分。这篇文章的目的是向你介绍如何阅读描述性统计数据并提取有用的信息。

让我们首先看看给定数据集的汇总统计表是什么样子的。我将使用 Python 中的seaborn库自带的内置数据集。

import seaborn as sns
import pandas as pd
df = sns.load_dataset('tips')
df.head()

该数据集中的每个观察值(行)代表在餐馆就餐。这里的列名是不言自明的。在数字列中,“总账单”指的是用餐者支付的账单金额,“小费”代表他们支付的小费金额。

只需一个简单的方法调用df.describe()就可以得到数字列的汇总统计数据(我将在最后谈到分类列)。

那么如何解读这份汇总统计数据呢?事实上,您可以从该表中提取 3 种信息:

  • 变量的统计分布
  • 数据中的异常现象
  • 其他兴趣点

统计分布

的意思是

在汇总统计数据中,您可能首先会看到的是均值— 集中趋势的一个关键衡量指标。

对于平均值,您试图了解平均数据点是什么样的。你也要把它和你的期望和经历进行比较。例如,客户支付的平均账单是 19.7 美元。你觉得这正常吗?对于一个人的平均餐来说,这有点高,不是吗?但是如果你知道这是两个人的账单(看看“大小”栏的意思),那么这个账单听起来是合理的。

“小费”的平均值是 2.9 美元,大约是“总账单”的 15%。在我看来这很正常。

平均值与标准偏差

你应该检查的下一个数字是标准差(STD)。

标准偏差是数据点相对于平均值的变化/离差的度量。较小的标准差表示数据主要集中在平均值附近,而较大的标准差表示数据点相当分散。

在上面的例子中,total_bill 的标准是 8.9 美元。这告诉我,人们在餐馆的花费有所不同。这种变化并不是完全出乎意料的,可以通过以后的统计数据进一步证实。

平均值与中位数(50%)

接下来是第 50 百分位(50%栏),也称为中位数。像均值一样,这是集中趋势的另一种度量。如果数据中存在异常值或高可变性,中值是优于平均值的首选指标。

如果均值和中位数的差很小,可以推断数据是对称分布的。如果中值高于平均值,则数据在分布上可能是左偏的。

import matplotlib.pyplot as pltsns.displot(df['total_bill'])
plt.axvline(x = df['total_bill'].mean(), linewidth = 3, color='r')
plt.axvline(x = df['total_bill'].median(), linewidth = 3, color='y');

总账单分布:黄色=中间值,红色=平均值

最小值和最大值

最小值和最大值分别表示数据集中变量的下限和上限。

我们数据集中的总账单介于 3 美元到 50 美元之间。餐馆账单的上限似乎是合理的,尽管在大型数据集中,我期望更高的值。餐馆账单的最低金额(3 美元)似乎有点低,尤其是当平均金额为 19 美元的时候。但这并非不合理。

异常和其他关注点

使用描述性统计,您可以了解数据集中的异常值、异常值和其他感兴趣的点。怎么会?

离群值

第 75 个百分位数和最大值之间的巨大差异表明存在潜在的异常值。最小值和第 25 百分位之间的差值也是如此。

为了绝对确认异常值,您可以创建一个箱线图进行目视检查。

sns.boxplot(y=df['total_bill']);

箱线图:数据集中可能的异常值

危险信号

有时描述性统计可能会引发危险信号。我们在这个数据集中没有任何危险信号,但是,我在其他地方看到了意想不到的最小值(0 或负值)或绝对疯狂的最大值(例如,某人的年龄是 120 岁!).这些明显表明数据中存在问题,需要进一步调查。

描述分类数据

到目前为止,我们已经研究了数字变量的描述性统计。Python pandas也提供了分类变量的总结。

如何解释此表:

  • “计数”是指数据集中的观察总数。
  • “性”栏有两个独特的类别
  • “顶级”指的是占主导地位的类别(在这种情况下是男性)
  • “频率”是指占主导地位的类别的比例(有 157 名男性顾客支付了账单)

摘要

描述性统计对数据集进行了强有力的综合,简明扼要,可用于提取有价值的信息作为探索性数据分析的一部分,包括变量的统计分布(最小值、最大值、平均值、中值、百分位数)、潜在异常和其他感兴趣的点。除了数字数据,描述性统计还综合了分类变量的信息,显示了每一列中类别的唯一数量、主要类别以及该类别的比例。

感谢您的阅读,如果您有任何意见和想法,请随时写在下面,或者通过媒体推特、LinkedIn 与我联系。

熊猫在亚马逊 S3 上读写文件

原文:https://towardsdatascience.com/reading-and-writing-files-from-to-amazon-s3-with-pandas-ccaf90bfe86c?source=collection_archive---------0-----------------------

使用 boto3 库和 s3fs 支持的 pandas APIs

内容

  • 在 S3 上将熊猫数据帧写入 CSV 文件
  • 使用 boto3

  • 使用 s3fs 支持的 pandas API

  • 将 S3 的 CSV 文件读入熊猫数据框
  • 使用 boto3

  • 使用 s3fs 支持的 pandas API

  • 摘要

⚠请在继续之前阅读

要继续学习,您需要安装以下 Python 包

  • boto3
  • s3fs
  • 熊猫

当 boto3 和 s3fs 都被指定为项目中的依赖项时,存在一个关于依赖项解析的突出问题。如果你对细节感兴趣,请看这期 GitHub。幸运的是,这个问题已经解决了,你可以在 GitHub 上了解更多。

在问题解决之前,如果你需要两个包(例如,在相同的环境中运行下面的例子,或者更一般地,使用 s3fs 进行熊猫到 S3 的便捷交互,使用 boto3 进行与 AWS 的其他编程交互),你必须将你的 s3fs 固定到版本“≤0.4”,作为解决方法(感谢马丁·坎贝尔)。

在问题解决之前:

python -m pip install boto3 pandas "s3fs<=0.4"

问题解决后:

python -m pip install boto3 pandas s3fs

💭在下面的例子中你会注意到,虽然我们需要导入 boto3 和 pandas,但是我们不需要导入 s3fs,尽管我们需要安装这个包。原因是我们在代码中直接使用了 boto3 和 pandas,但不会直接使用 s3fs。尽管如此,熊猫还是需要它来连接 S3 亚马逊。

熊猫现在使用 s3fs 来处理 S3 连接。这不会破坏任何代码。然而,由于s3fs不是必需的依赖项,您需要单独安装它,就像以前版本的 pandas 中的boto。( GH11915 )。

熊猫 0.20.1 版发行说明

在 S3 上将熊猫数据帧写入 CSV 文件

使用 boto3

使用 boto3 库将熊猫数据框写入 S3 上的 CSV 文件的演示脚本

使用 s3fs 支持的 pandas API

使用 s3fs 支持的熊猫 API 将熊猫数据框写入 S3 上的 CSV 文件的演示脚本

将 S3 的 CSV 文件读入熊猫数据框

使用 boto3

使用 boto3 库将 S3 的 CSV 文件读入熊猫数据框的演示脚本

使用 s3fs 支持的 pandas API

使用 s3fs 支持的 pandas APIs 将 CSV 文件从 S3 读入 pandas 数据框的演示脚本

摘要

如果您在已经有 boto3 的环境中使用 pandas,并且还必须与其他 AWS 服务交互,那么您可能希望使用 boto3。

然而,使用 boto3 需要稍微多一点的代码,并且使用了[io.StringIO](https://docs.python.org/3/library/io.html#text-i-o) 【文本 I/O 的内存流】和 Python 的上下文管理器([with](https://docs.python.org/3/reference/compound_stmts.html#the-with-statement)语句)。这是另外两个你可能还不知道,或者不想学习或者想去“简单地”读/写一个文件到亚马逊 S3 的事情。

不过,我还是建议学习它们;他们经常出现,尤其是with声明。但是,pandas 满足了我们这些“简单地”想从亚马逊 S3 读写文件的人,通过使用 s3fs 来实现这一点,其代码即使是 pandas 的新手用户也会感到熟悉。

aws_credentials = { "key": "***", "secret": "***", "token": "***" }
df = pd.read_csv("s3://...", storage_options=aws_credientials)

或者

aws_credentials = { "key": "***", "secret": "***", "token": "***" }
df.to_csv("s3://...", index=False, storage_options=aws_credentials)

感谢您的阅读!

你接下来应该读的文章

更多计算资源

OnelTalksTech.com观看涵盖各种计算主题的视频

与 Julia 一起阅读 CSV 文件

原文:https://towardsdatascience.com/reading-csv-files-with-julia-e2623fb62938?source=collection_archive---------20-----------------------

了解如何使用 CSV.jl 读取各种逗号分隔的文件

见作者页面,CC 由 4.0<https://creativecommons.org/licenses/by/4.0通过维基共享

你曾经收到过用分号(;)作为分隔符的.csv文件吗?还是一个没有头文件的文件?或者也许你在欧洲有一些同事用,而不是.来表示小数?哦,使用 CSV 文件的乐趣…

继续阅读,学习如何使用CSV.jl在 Julia 中阅读各种分隔符分隔的文件格式

要获得所有媒体文章的完整访问权限,包括我的文章,请考虑在此订阅。

生成数据

我们将自己生成所有示例,因此您可以轻松下载代码,并在自己的环境中试验结果。我们开始吧!首先,我们需要加载将要使用的包。即CSV.jlDataFrames.jl:

下一步是生成一些我们可以实际读取的数据。在现实世界中,您可能不需要这样做,因为您已经有了想要读取的数据,但是通过提供一个易于复制的示例,您可以自己处理代码!

我将使用"""将文件的内容设置为多行字符串。然后,我们可以用传统的方式写入文件,打开一个连接,将字符串的所有内容转储到该连接中。

您可以通过从 REPL 使用;跳转到 shell 模式并键入head animals.csv来检查文件的内容

使用默认参数读取文件

结果是一个“漂亮的”.csv文件,因为它是由逗号分隔的,有标题,并且没有奇怪的引用或其他不寻常的事情发生...默认的CSV.read()函数应该没有问题。

4×3 DataFrame
│ Row │ Animal │ Colour │ Cover    │
│     │ String │ String │ String   │
├─────┼────────┼────────┼──────────┤
│ 1   │ bunny  │ white  │ fur      │
│ 2   │ dragon │ green  │ scales   │
│ 3   │ cow    │ brown  │ fur      │
│ 4   │ pigeon │ grey   │ feathers │

因为我们将多次执行这一步骤(将字符串写入文件),所以我准备了一个方便的函数供以后使用:

不同的分隔符

解决了这个问题,让我们尝试一些更复杂、不那么标准的东西…自定义分隔符!您仍然可以使用CSV.read(),您只需要指定delim关键字参数就可以了!

更好的是,CSV.jl足够聪明,可以为我们猜出分隔符。尝试在没有delim参数的情况下读取管道分隔文件!

对于 **tab** 分隔的文件,只需使用 **delim=\t** 即可。

无头文件

另一个常见的场景是我们要读入的文件没有头文件。让我们准备这样一个例子:

在这种情况下,标题看起来与正文的其余部分没有太大的不同,所以CSV.jl在这里不能做任何巧妙的事情,会认为第一行是标题。做CSV.read("no_headers.csv", DataFrame)会给你:

3×3 DataFrame
│ Row │ bunny  │ white  │ fur      │
│     │ String │ String │ String   │
├─────┼────────┼────────┼──────────┤
│ 1   │ dragon │ green  │ scales   │
│ 2   │ cow    │ brown  │ fur      │
│ 3   │ pigeon │ grey   │ feathers │

我们不希望我们的列被称为bunnywhitefur...那是不明智的!你有两个选择来解决这个问题。第一,您可以只设置header = false,在这种情况下,您的结果将以通用列名结束。或者,您也可以在同一参数下手动设置列名:

您将得到以下结果:

julia> CSV.read("no_headers.csv", DataFrame, header=false)
4×3 DataFrame
│ Row │ Column1 │ Column2 │ Column3  │
│     │ String  │ String  │ String   │
├─────┼─────────┼─────────┼──────────┤
│ 1   │ bunny   │ white   │ fur      │
│ 2   │ dragon  │ green   │ scales   │
│ 3   │ cow     │ brown   │ fur      │
│ 4   │ pigeon  │ grey    │ feathers │julia> CSV.read("no_headers.csv", DataFrame, header=["Animal", "Colour", "Cover"])
4×3 DataFrame
│ Row │ Animal │ Colour │ Cover    │
│     │ String │ String │ String   │
├─────┼────────┼────────┼──────────┤
│ 1   │ bunny  │ white  │ fur      │
│ 2   │ dragon │ green  │ scales   │
│ 3   │ cow    │ brown  │ fur      │
│ 4   │ pigeon │ grey   │ feathers │

请对同事好一点,用选项 2!😉

引用字符

分隔文件的另一个典型的奇怪之处是字段本身可以包含分隔符。在这种情况下,通常引用字段,这样解析器可以很容易地忽略分隔符。

该死的不合格的帕伽索斯!|信用: pxfuel

让我们在列表中添加一个飞马来演示这一点:

事实上,我们对"没有任何问题,因为这些是默认的报价,所以CSV.read("animals_quoted.csv", DataFrame)会工作得很好:

julia> CSV.read("animals_quoted.csv", DataFrame)
5×3 DataFrame
│ Row │ Animal  │ Colour │ Cover        │
│     │ String  │ String │ String       │
├─────┼─────────┼────────┼──────────────┤
│ 1   │ bunny   │ white  │ fur          │
│ 2   │ dragon  │ green  │ scales       │
│ 3   │ cow     │ brown  │ fur          │
│ 4   │ pigeon  │ grey   │ feathers     │
│ 5   │ pegasus │ white  │ feathers,fur │

好吧,那么引用默认工作,但如果我们有一些疯狂的工作呢🤪为我们准备文件,他们决定使用&作为引用字符??疯狂降临在我们身上...

运行这个会给我们一个警告!

julia> CSV.read("animals_&.csv", DataFrame)
┌ Warning: thread = 1 warning: parsed expected 3 columns, but didn't reach end of line around data row: 6\. Ignoring any extra columns on this row
└ @ CSV ~/.julia/packages/CSV/CJfFO/src/file.jl:606
5×3 DataFrame
│ Row │ Animal  │ Colour │ Cover     │
│     │ String  │ String │ String    │
├─────┼─────────┼────────┼───────────┤
│ 1   │ bunny   │ white  │ fur       │
│ 2   │ dragon  │ green  │ scales    │
│ 3   │ cow     │ brown  │ fur       │
│ 4   │ pigeon  │ grey   │ feathers  │
│ 5   │ pegasus │ white  │ &feathers │

看看最后一行变得多么奇怪,第三个逗号之后的所有内容都被忽略了。你应该总是注意你得到的警告!

你应该总是注意你得到的警告!它们可能包含一些关于您正在处理的数据的重要信息。

解决这个问题的正确方法是用quotechar='&'指定一个自定义引用字符:

那么在动物王国一切都很好:

julia> CSV.read("animals_&.csv", DataFrame, quotechar='&')
5×3 DataFrame
│ Row │ Animal  │ Colour │ Cover        │
│     │ String  │ String │ String       │
├─────┼─────────┼────────┼──────────────┤
│ 1   │ bunny   │ white  │ fur          │
│ 2   │ dragon  │ green  │ scales       │
│ 3   │ cow     │ brown  │ fur          │
│ 4   │ pigeon  │ grey   │ feathers     │
│ 5   │ pegasus │ white  │ feathers,fur │

笔记

注意,对于文件的编写,我们也可以使用CSV.jl,但是我觉得那样会违背本教程的目的。为了完整起见,下面是编写文件的片段:

大卫·坦尼尔斯《更年轻的公共领域》,维基共享

结论

在这篇文章中,你学会了如何:

  • 将简单的 csv 文件读入数据帧
  • 指定分隔符
  • 读取无头文件
  • 指定引用字符

你没有学到的:

  • 如何处理不同的列类型,例如解析布尔值、日期类型。
  • 处理货币格式,如$1,320.23。

如果您想了解这些,请继续阅读第 2 部分:

关于 Julia 的其他帖子,您可能会感兴趣:

与 Julia 一起阅读 CSV 文件—第 2 部分

原文:https://towardsdatascience.com/reading-csv-files-with-julia-part-2-51d74434358f?source=collection_archive---------45-----------------------

货币、布尔值等的自定义格式

让·奥诺雷·弗拉戈纳,公共领域,通过维基共享

之前,我已经展示了如何读取基本的分隔文件——即文件中的值由常用字符(如逗号、分号或制表符)分隔。现在是时候升级我们的游戏,使用CSV.jl处理一些更奇特的边缘情况了。

我们将重点了解如何正确解析数据类型,以便我们的数据帧从一开始就尽可能干净。

这是与 Julia 一起阅读 CSV 文章的第 2 部分,所以如果您是新来的,请查看第 1 部分:

和以前一样,我们从导入包和模拟一些虚拟数据开始。我将重用上一篇文章中的write_string函数。

读取布尔值

我们的第一个挑战将是处理布尔值。布尔值为真或假,通常用于从数据框中选择行的子集。为了增加难度,我们将在 csv 文件中使用Y/N作为布尔值:

这给出了以下内容:

julia> animals_table = CSV.read("animals_like.csv", DataFrame)
5×4 DataFrame
│ Row │ Animal  │ Colour │ Cover        │ Liked  │
│     │ String  │ String │ String       │ String │
├─────┼─────────┼────────┼──────────────┼────────┤
│ 1   │ bunny   │ white  │ fur          │ Y      │
│ 2   │ dragon  │ green  │ scales       │ Y      │
│ 3   │ cow     │ brown  │ fur          │ N      │
│ 4   │ pigeon  │ grey   │ feathers     │ N      │
│ 5   │ pegasus │ white  │ feathers,fur │ Y      │

正如我们所看到的,我们的Liked列的数据类型为String。我们也可以通过致电eltypes(animals_table)来确认这一点。

因为我们希望将Y/N的值转换成布尔值以便于索引,所以让CSV.jl通过使用truestringsfalsestrings参数将它们解析为布尔值——注意这些必须是数组:

现在,我们的最后一篇专栏文章将把Bool读为数据类型,我们可以直接做一些有趣的索引:

julia> animals_table2[animals_table2[:Liked], :]
3×4 DataFrame
│ Row │ Animal  │ Colour │ Cover        │ Liked │
│     │ String  │ String │ String       │ Bool  │
├─────┼─────────┼────────┼──────────────┼───────┤
│ 1   │ bunny   │ white  │ fur          │ 1     │
│ 2   │ dragon  │ green  │ scales       │ 1     │
│ 3   │ pegasus │ white  │ feathers,fur │ 1     │

货币和小数

我已经受够了我们心爱的动物,所以让我们看看我们的储蓄账户:

现在,如果你注意的话,你会注意到Amount列使用的是,而不是小数点。这在世界的某些地方很常见,所以如果你正在处理来自东欧的数据,你很可能会看到类似上面的东西。多么快乐!🙌
如果我们试着正常读取,得到的Amount列将会变成String:

一个简单的解决方法是指定用于表示小数的字符:

现在我们可以随心所欲地加减了。例如,我们可以看到我们每个月存了多少钱:

另外,请注意,我们的解析器成功地判断出第一列是一个日期。如果您的日期列有其他格式,您可以通过设置(surprise,surprise…) dateformat参数:CSV.read("my_file.csv", DataFrame, dateformat="Y-m-d")来手动指定日期格式

更多的钱,更多的问题

照片由达里奥·马丁内斯-巴特列Unsplash 上拍摄

纯粹出于运气,我们刚刚中了彩票💸。让我们将新的总数添加到电子表格中:

现在我们的十进制字符串不再能帮助我们:

4×3 DataFrame
│ Row │ DateTime   │ Amount       │ Currency │
│     │ Dates.Date │ String       │ String   │
├─────┼────────────┼──────────────┼──────────┤
│ 1   │ 2001-01-01 │ 10,56        │ €        │
│ 2   │ 2001-02-01 │ 12,40        │ €        │
│ 3   │ 2001-03-01 │ 6,50         │ €        │
│ 4   │ 2001-04-01 │ 1.000.006,57 │ €        │

这是因为用作千位分隔符的.混淆了CSV.jl,使其认为这是一个字符串列。

在早期版本的CSV.jl中,您可以在读取时通过指定一个transforms参数来直接修复这个问题,但是这个参数似乎已经被移除了,所以我们必须在数据被读入后改变它。

为了解决这个问题,我们需要一个函数:

  1. 接受字符串作为输入。
  2. 将字符串中的.替换为空。
  3. ,替换为.
  4. 然后将字符串解析为浮点数。

让我们为此创建一个函数,并将其映射(按元素方式应用)到该特定列:

这将为我们提供预期的正确数据类型:

4×3 DataFrame
│ Row │ DateTime   │ Amount    │ Currency │
│     │ Dates.Date │ Float64   │ String   │
├─────┼────────────┼───────────┼──────────┤
│ 1   │ 2001-01-01 │ 10.56     │ €        │
│ 2   │ 2001-02-01 │ 12.4      │ €        │
│ 3   │ 2001-03-01 │ 6.5       │ €        │
│ 4   │ 2001-04-01 │ 1.00001e6 │ €        │

💰终于,我们发财了…

好吧,也许不是,但至少我们可以很容易地阅读各种疯狂,这都要感谢朱莉娅和CSV.jl。现在,您知道如何将字符串解释为布尔值,读取以,为小数的数字,甚至可以按列应用任意函数来整理您喜欢的任何内容。

最后一个提示:如果你不确定数据的结构,你可以设置CSV.read()limit参数,只读取文件的前n行。👀

如果你正在寻找更多的朱莉娅文章,看看这些:

读取 Node.js 中的 Python 加密数据

原文:https://towardsdatascience.com/reading-python-encrypted-data-in-node-js-5b47003dda0?source=collection_archive---------17-----------------------

有时我们希望将受密码保护的数据传递给节点桌面或 web 应用程序。在本教程中,我们用 python 加密一个 JSON 文件,并在 Node.js 中解密它。

照片由 Luemen RutkowskiUnsplash 上拍摄

背景

当开发具有许多潜在用途的软件时,我们可能会遇到这样的情况:一些用户使用潜在的敏感数据集,但其他用户不应该使用这些数据集。更有效的方法是对数据集进行密码加密,这样只有拥有专用密钥的人才能访问它,而不是依赖我们的用户来进行细致的文件权限管理。

本教程涵盖了用 python 对信息进行编码,然后用 nodejs / electron 对其进行解码。

数据

我们的数据可以是任何可序列化的对象——无论是字符串还是熊猫数据框。

在 JavaScript 中,我们会使用JSON.stringify函数。在 python 中,如果我们有一个数据框,我们可以尝试使用.to_json()函数。

在这个过程中,我们产生了一个我们想要加密的对象的字符串形式。

如果我们正在字符串化一个 JSON 对象,我们需要记住在节点应用程序中使用 *JSON.parse* 将其转换回来。

节点 JS 加密周期

在 NodeJS 中,我们使用“加密”库。这个可以用npm i g crypto全局安装。有了它,就有了一系列可用的加密算法。在本例中,我们选择了 AES-256-CBC ( 密码拦截器链接 ) block cypher 加密,这是一种对称加密算法,意味着同一密钥可用于我们数据的加密和解密。

作为“加密”的一部分,我们有createCipheriv加密功能和createDecipheriv解密功能。由于设置这些对象所需的大部分信息都是相同的,我们可以创建一个返回 encryptor 或 decrypter 对象的函数:

function get_crypto(key,encode){ // Create hashed key from password/key
    var m = crypto.createHash('md5').update(key)
    var key = m.digest('hex'); m = crypto.createHash('md5').update(key + key)
    var iv = m.digest('hex').slice(0,16);

    return encode? crypto.createCipheriv('aes-256-cbc', key, iv) : crypto.createDecipheriv('aes-256-cbc', key, iv)

}

加密

要在节点中加密,我们可以将数据字符串转换为二进制形式:

var data = new Buffer.from(value, 'utf8').toString('binary');

然后将其添加到密码中

cipher = get_crypto(key,true)encrypted = cipher.update(data, 'utf8', 'binary') + cipher.final('binary');var encoded = new Buffer.from(encrypted, 'binary').toString('base64');

解码

为了解码,我们遵循类似的过程,使用加密的字符串作为我们的输入:

decipher = get_crypto(key,false);decoded_text = (decipher.update(edata, 'binary', 'utf8') + decipher.final('utf8'));

使用 Python 加密

在 python 中加密和解密的过程非常相似,除了我们使用“pycrypto”库。这是用pip install pycrypto安装的。

我们首先用以下公式定义我们的加密算法:

def get_aes (password):
    m = md5()
    m.update(password.encode('utf-8'))
    key = m.hexdigest() m = md5()
    m.update((password*2).encode('utf-8'))
    iv = m.hexdigest()

    return AES.new(key, AES.MODE_CBC, iv[:BLOCK_SIZE])

以及填充/去填充函数的集合:

BLOCK_SIZE = 16def pad (data):
    data = ' '*BLOCK_SIZE + data
    pad = BLOCK_SIZE - len(data) % BLOCK_SIZE
    return data + pad * chr(pad)def unpad (padded):
    pad = ord(chr(padded[-1]))
    return padded[BLOCK_SIZE:-pad]

加密

为了加密,我们使用 aes 类的加密函数:

def _encrypt(password,data):
    data = pad(data)
    aes = get_aes(password) encrypted = aes.encrypt(data)
    return base64.urlsafe_b64encode(encrypted).decode('utf-8')

[通信]解密

同样,解密功能可以逆转这一过程

def _decrypt(password,edata):
    edata = base64.urlsafe_b64decode(edata)
    aes = get_aes(password)

    return unpad(aes.decrypt(edata)).decode('utf-8')

把所有的放在一起

现在我们有了加密和解密这两个程序的工具,我们可以把这两个结合起来。

注意:在提供的代码中,加密密钥在 python 脚本中作为明文提供,并作为参数传递给 node。这通常不是一个好主意。更好的方法是在每个目录的 *.env* 文件中设置一个关键变量,并通过 python 和 node 各自的‘dotenv’库进行访问。

为了测试代码,我们导入 python 库,使用我们的密码(key)对我们的信息(value)进行编码,然后将其作为参数传递给我们的解密节点程序‘node decrypt’。然后,我们使用 os 库生成一个 shell,运行命令并读取返回的结果。

output = pe._encrypt(key,value) 
print('\nPython encrypt:\n', output)decrypt = pe._decrypt(key,output)
print('\nPython decrypt:\n', decrypt)cmd = 'node nodedecrypt ---key "%s" --estr "%s"'%(key,output)
# print(cmd)
node = os.popen(cmd).read()
print('\n\nNode Decrypt:\n', node)

如果一切正常,我们将从 python 解密和节点解密中获得两个相同的文本。

示例代码

可在应用程序中使用的模板代码位于:

https://github.com/wolfiex/python-node-encryption

自述文件包含所有相关的安装命令,并将用户指引到运行该示例的communicate.py文件。

黑客快乐!

读老鼠的心

原文:https://towardsdatascience.com/reading-rats-mind-2e0b705d5e08?source=collection_archive---------61-----------------------

哺乳动物海马体的位置细胞能记住它过去的位置并预测未来的位置吗?

图片来源:pexels.com

简介和背景

2019 年,来自爱沙尼亚塔尔图大学计算机科学系和伦敦大学学院的 Tampuu 等人发表了一篇论文,利用深度递归网络对自我定位进行有效的神经解码,其中他们能够利用 RNNs(更具体地说是 LSTM)和海马神经元的记录来预测大鼠的位置。他们表明,在预测 2D 环境中老鼠的位置时,他们的方法比贝叶斯解码器更好。特别是,他们开发了一种解码器,可以从部分重叠的时间窗口中接受神经元棘波计数,然后预测大鼠在最终时间窗口中间的位置。原始论文的作者从 UCL 的 Caswell Barry 实验室获得了数据集。

这篇博文的作者(见下文)旨在通过使用原始论文的 GitHub 知识库中可用的相同神经元尖峰数据来扩展他们的方法,并尝试预测不同时间的位置。我们特别感兴趣的是,在未来多远的时间里,一只老鼠的位置细胞拥有关于它运动的信息,以及这些细胞能记住多少过去的事情。

我们的第二个目标是调查最初的研究作者在检查他们的神经网络中的梯度强度时注意到的异常。这篇博文的最后一部分也提供了一个简要的概述。

通过神经网络预测未来

在我们变得过于严谨之前,让我们用神经网络做一些简单的训练和预测。这一节并没有过多地探究细节,所以如果你已经深入其中,你可能会觉得下一节更有趣。

该方法

在最初的文章中,研究人员使用了具有 LSTM 节点的神经网络来预测老鼠的当前位置。LSTM(或长短期记忆)是一种递归神经网络。LSTM 节点也可以在任意的时间内“记住”信息,这可能与大脑的行为类似。

当大鼠在 2D 环境中移动时,每隔 200 毫秒测量其定位细胞的神经元锋电位计数,即所谓的时间步长。基于 LSTM 的 RNN 输入由 100 个这样的连续时间步长的测量形成。然后要求网络预测老鼠在最后一个时间窗口中心的位置。通过这种方式,研究人员获得了老鼠当前位置的一个非常好的近似值。我们可以用下图来说明这个过程。

预测未来的简单方法是使用相同的架构和输入数据,但改变我们预测的位置。例如,如果我们有 200 毫秒的时间步长,我们可以要求网络从最后的尖峰数据预测未来的 200 毫秒、400 毫秒、600 毫秒等等。

请注意,我们不能使用类似的方法来估计过去的位置。如果我们要估计老鼠在第 99 个时间窗口中心的位置,我们仍然会有关于老鼠在第 99 个时间窗口的过去位置的数据。因此,我们需要一种不同的方法来测试位置细胞是否记得过去的任何位置。我们将在下一节更详细地讨论这一点。

所以如果我们用这种方法预测未来,上面的例子会变成这样:

网络体系结构

神经网络本身由一个输入层、两层 LSTM 节点和一个输出层组成。两个 LSTM 层也有一个附加的辍学,以避免过度拟合。该模型总共有大约 300 万个可训练参数。你可能知道,训练大型神经网络可能会变得相当慢。尤其是当你使用普通的 CPU 时。如果你想在合理的时间内完成一些事情,你真的应该使用一些 GPU 加速。幸运的是,Google Colab 是进行这种实验的完美场所,这就是我们所使用的。

结果呢

在对这些模型进行了 40 个纪元的训练后,这些就是我们得到的结果。x 轴代表我们预测的未来有多远,而 y 轴代表我们预测的均方误差(观察老鼠的 2D 环境的大小为 1 米×1 米)。

结果看起来和我们预期的差不多。我们预测的未来越遥远,误差就越大。请注意,我们在这里并不真正关心模型有多精确,只关心我们预测的未来有多远(时间 T,单位为毫秒)的误差是如何增加的。这里没有明确的分界点,我们可以说给定的老鼠没有计划到这一步。

注意,这个情节并不完美。可能会发生一些过度拟合,并且给定更多的训练时期,一些模型可能会表现得更好。然而,我们不关心这里的准确性。我们对趋势线感兴趣,即使我们用大量的交叉验证和训练时期来平滑这些图,趋势线也可能不会改变。

虽然这些实验很酷,但它们并没有真正给我们一个很好的洞察力,让我们知道一只老鼠能记住多少,或者它对未来有多少计划。我们需要开发一种更聪明的方法。

从当前运动信息中分离老鼠的记忆和未来计划

该方法

我们想测量导航细胞是否有关于老鼠位置的信息,以及在过去和未来有多远。乍一看,一个简单的想法是只训练一个模型,该模型检测神经元尖峰数据与记忆的过去位置或计划的未来位置之间的关系。这基本上就是我们在第一节中所做的。

然而,正如我们从原始论文中了解到的,这些导航细胞在任何给定时刻都在编码关于老鼠当前位置的非常可靠的信息。这意味着,如果我们想真正区分这些神经元编码了多少关于过去记忆或未来计划的额外信息,我们必须消除仅通过知道老鼠的当前位置、移动速度和方向就能猜测过去和未来位置的影响。一种方法是训练两个机器学习模型:

  1. 基线模型 —仅根据大鼠的当前位置、移动速度和方向(即完全不使用神经元尖峰数据)预测过去或未来的位置。为此,我们决定使用 scikit-learn 随机森林回归学习算法。
  2. 高级模型 —使用与基线模型相同的学习算法和相同的超参数,但使用当前位置和运动信息以及神经元尖峰数据作为输入。

想法:如果上面的高级模型的错误率小于上面的基线模型的错误率,我们可以肯定地说,神经元尖峰数据正在编码更多的数据,而不仅仅是老鼠的当前位置、运动方向和速度。通过在过去或未来的不同时间间隔内以这种方式训练和测试一系列模型(每个时间间隔一个模型),我们可以确定大鼠记得过去多长时间它去过哪里或大鼠计划未来多长时间它的运动的下限。

对于神经元发放解码器,我们选择了能够从神经元发放数据中检测位置信号的 ML 算法。我们想让事情变得合理简单。我们的目标不是尽可能精确,只是足以看到信号存在,因此我们可以区分基线模型和高级模型结果。这就是使用具有默认元参数的简单随机森林回归学习算法而不是复杂的神经网络的基本原理。Random forest 相当容易使用,同时在其默认设置下,如果数据中存在复杂的信号,它也能够检测到这些信号。

初始数据

在原始论文的 GitHub 知识库中提供的神经元尖峰数据很容易在多个不同的故障中进行预处理。在单独的数据文件中,存在对 2 个不同实验的多个大鼠的测量,并且对于每个大鼠,测量被预处理到不同的时间窗口(对于大于 200 毫秒的时间窗口是重叠的)中,用于计数神经元尖峰。

我们专注于 2D 实验——即关于老鼠在二维环境中导航的数据。在 2D 实验中,我们重点关注了在 200 毫秒非重叠时间窗口内提供神经元峰值计数的数据集。在该数据集中,实验数据集中的一个数据点包括每个神经元的棘波计数作为 200ms 期间的特征,以及大鼠在观察时间帧中间的位置作为标签

在 20 分钟的时间跨度内进行神经元尖峰数据和大鼠位置的测量。

不同时间间隔的数据预处理和模型

为了训练一个模型来猜测老鼠的位置在过去或将来会在哪里,我们必须沿着时间轴移动标签(老鼠的位置)。比方说,我们想知道老鼠的位置细胞是否对过去 10 秒钟的位置有记忆。然后我们需要移动原始数据集中的标签,这样对于神经元尖峰数据的每个数据点,我们都有从过去 10 秒开始的对应位置。然后,我们可以使用这些数据来训练我们的模型,并验证在预测过去 10 秒时的平均绝对误差。

我们想为不同的时间间隔生成这样的模型,而不仅仅是 10 秒钟,看看老鼠的位置细胞在过去或未来是否知道自己的位置。我们决定扫描所有的时间间隔,从过去 1 分钟开始,到未来 1 分钟结束,时间步长 200ms。因此,对于每只老鼠,我们生成了 2×601 个不同的模型:

  • 300 个模型,用于猜测每个模型类型(基线和高级)的过去位置,
  • 每个模型类型(基线和高级)的当前位置 1 个模型,
  • 每个模型类型(基线和高级)的未来位置有 300 个模型。

因此,总共为基线模型生成了 601 个模型,为高级模型生成了 601 个模型(如上面在“方法”小节中所描述的)。

结果呢

下图显示了以上述方式对一只代号为 R2192 的特定大鼠的所有模型进行训练和测试的结果。X 轴反映从过去 1 分钟到未来 1 分钟的时间范围。X 轴上的 0 点是“当前时间”-这是获取预测的输入要素的位置,然后使用所有不同的模型相应地做出不同时间的位置预测。

上图。上图中的蓝点是给定时间范围内基线模型的平均绝对误差。橙色的点显示了高级模型的相同情况。请记住,基线模型无法访问神经元尖峰数据,只能访问位置、速度和运动方向。

下图。下图显示了任何给定时间范围内基准模型和高级模型的平均绝对误差的差异(即基准模型的平均误差减去高级模型的平均误差),以及指示我们正在寻找的信号的“方框”区域。进一步的解释如下图所示。

为了更好地理解那些图表上发生了什么,让我们在这里解释一些观察结果。

首先,由于基线和高级模型都包括当前位置、运动方向和速度作为输入特征,所以期望两个模型的 MAE-s 在时间点 0(“当前时间”)下降到零。对于我们的模型来说,通过将当前位置作为输入特征来学习预测当前位置实在是微不足道,不是吗?这通过运行模型测试来确认。当然,如果两个模型都准确无误地预测了当前位置,那么它们的误差差也将为 0。

应该引起我们兴趣的是,随着我们沿着时间轴远离点 0,无论是过去还是未来,我们可以观察到,对于某些时间范围,长达数秒,基线和高级模型之间的 MAEs 差异比沿着时间轴更远的其他区域要大得多。这些是加在下方图表中的方框区域,以便更好地定位这些 MAE 差异。这就是我们一直在寻找的信号。这给了我们一个提示(有一些否认呼吁谨慎做出自信的结论),老鼠的定位细胞可能确实记得它过去的位置一段时间,也可能“知道”老鼠打算去哪里。这个结果很有趣,应该对这个问题进行更深入的研究。

然而,在这些图表上还有一些其他的观察结果需要解释:

问题 1:为什么我们在主要市场价格和主要市场价格差异中都看到这样的振荡趋势?这是如何为每个时间距离(过去或未来)生成训练和测试数据集的工件。如上文“数据预处理”小节所述,我们查看的时间范围从过去一分钟到未来一分钟,分为 200 毫秒时间步长,这意味着 600 个不同的时间间隔(过去 300 x200 毫秒,未来 300 x200 毫秒)。对于每个这样的时间间隔,我们都在训练一个单独的模型。为了做到这一点,我们采用特征集,并从中删除前 300 行和后 300 行,以确保在每个可观察的时间间隔内,我们都有相同特征集的标签。然而,这意味着当我们查看所有 600 个时间步长时,标签的数量(我们预测的位置)正在一个时间步长接一个时间步长地移动。这意味着标签的统计分布(大鼠所在的位置)在不同的时间间隔内也略有变化,并且是以渐进(即趋势/振荡)的方式变化——这是 MAEs 轻微可观察到的振荡趋势的来源(以及两个不同模型的 MAEs 的差异)。幸运的是,我们正在寻找的信号,在“当前时间”的邻近区域,仍然强于大多数大鼠的振荡噪声(对于四只大鼠中的一只大鼠来说,未来计划的信号,如果存在的话,被 MAE 振荡淹没而无法突出——见下面大鼠 R2336 的图表)。假设是这种情况,拥有更多数据(覆盖相同 rat 的更多时间)将解决这种情况,因为这将降低从测量窗口开始和结束的 300 个时间步长(目前约为 20 分钟)的 rat 位置的重要性。

问题 2:为什么在远离 t = 0 的区域,高级模型得到的结果比基线差?从第二张图中我们可以看出,很明显,离 t=0 较远的 MAE 差异大部分都是负值。这意味着,平均而言,基准模型的 MAE 略小于高级模型的 MAE。问题是为什么?正如我们从一般的机器学习智慧中所知,我们堆积的特征数据越多,而没有相应地增加训练实例的数量,我们就越有可能过度拟合。由于高级模型通过将神经元尖峰数据添加到特征集而不同于基线模型(在上面可视化的大鼠 R2192 的情况下,有 63 个神经元的尖峰数据,即 63 个额外的输入特征),我们预计会有一些神经元为 ML 算法添加更多的噪声,从而导致一定量的过拟合。

问题 3:为什么对未来的预测似乎比猜测关于过去地点的记忆要弱(图表上“黄框”比“蓝框”高)?这个观察结果并不令人惊讶,因为过去是一个确定的事实,因此老鼠应该有关于它的具体记忆。同时,未来的计划可能会在途中改变,从而在未来位置的信号较弱时给我们输入数据。

同样的分析也在另外三只老鼠身上进行。以下是它们各自的图表:

我们可以观察到与第一只老鼠相似的模式。

梯度分析

我们的第二个目标是理解原始模型的渐变行为。使用梯度下降的某种变体来训练神经网络,其中网络的权重被迭代地修改。在每次迭代中,我们计算损失或预测误差,然后在梯度的相反方向更新权重,以尽可能减少损失。

原始论文的作者训练了一个 2 层 LSTM 网络,根据过去 100 个时间步长的神经脉冲数据来预测大鼠的当前位置。问题是,神经元如何解码这些信息,这是否反映在神经网络的参数中。一种可能性是查看相对于模型输入(在这种情况下是神经元的尖峰数据)的损失梯度。这个渐变的绝对值(渐变强度)越高,输入的微小变化对输出的影响就越大。

我们还期望最后一个时间步是最具信息性的,具有最高的梯度强度。毕竟,在最后一个时间步长的中心的大鼠的位置是预测目标,并且在最后一个时间步长期间的尖峰数据是最后的输入。由于尖峰数据是从位置单元收集的,我们可以预期最后一个时间步长包含关于最后一个时间步长中心的大鼠位置的最多信息。

然而,神经网络的梯度显示了不同的情况。也就是说,在上一个时间窗口中,梯度强度有非常明显的下降。一种假设是,这是 LSTM 网络结构的一个产物——也许 LSTM 有其内部的细胞状态向量,忘记门可能会避免过度优先考虑最后一个时间步。

为了研究这种可能性,我们训练了不同的循环模型。如果梯度强度的下降在其他模型中持续存在,这将是对数据中这种行为起源的一个暗示——也就是说,在大鼠实际到达观察位置之前一点点,位置细胞的尖峰可能确实与位置最密切相关。

我们总共训练了九个模型,每个模型接收前 100 个时间步中的神经尖峰计数作为输入,并预测最后一个时间步的中心位置。这些模型要么具有 1 个循环层,2 个循环层,要么具有 2 个循环层和在循环层之上的一个致密层。循环层架构是简单的 RNN、LSTM 或 GRU。共有 9 种不同的模型。结果图如下所示。每个子图用于不同的网络层架构,并且在每个子图中,一行对应于一个单元架构。

每个子图上的每条线对应于模型的平均梯度强度,其中平均值取自所有神经元和所有测试数据点。

从图中可以看出,无论我们使用哪种网络,梯度下降行为都会存在。如果我们单独观察神经元,上一个时间步长的梯度下降仍然存在。然而,对于只有一层的简单模型,下降不太明显。

由于这似乎不是特定网络或蜂窝架构的产物,因此进行进一步的研究来调查这种影响将是有趣的。一种选择是使用注意力模型并分析注意力权重。一种更简单的方法是使用数据剔除,我们研究网络的预测精度如何依赖于从输入中移除的数据点。在这种情况下,我们会将最后一个时间步长消除时的损失与第 99 个时间步长从输入数据中消除时的损失进行比较。

结束语

总之,我们想强调的是,这里所有的结果和解释都应该被认为是推测性的。这篇博客文章绝不是同行评审的科学论文。我们希望这里提出的想法能为类似领域的进一步研究提供启发。

作者

这篇博文的作者是:

  • Asko Seeba,塔尔图大学计算机科学研究所博士生,moomscode的数据分析师/工程师和企业家。
  • Alfred Saidlo,塔尔图大学计算机科学研究所硕士学生,数学爱好者,魔方 speedsolver。
  • Carel Kuusk,Carel Kuusk,塔尔图大学计算机科学研究所硕士学生。

这篇博文是作为塔尔图大学计算神经科学导论课程的一个课程项目的成果而写的。课程项目和博客写作由以下人员监督:

准备好重温您的 Python 技能了吗?

原文:https://towardsdatascience.com/ready-to-brush-up-on-your-python-skills-110e4f51992c?source=collection_archive---------11-----------------------

python——一种有 30 年历史的编程语言——仍然是数据科学最大的共同点之一。从我们每天发布的关于它的功能、怪癖和不断扩大的库的数量来看,它不会有任何发展。(对不起,茱莉亚粉丝们!)

在本周的《变量》中,我们给了这个学科的核心支柱一些应得的爱。以下是探索其深度的最新杰出帖子。

  • 发现一个将熊猫和 SQL 结合在一起的 Python 库 。“如果能使用 SQL 查询熊猫数据帧[……],或者在 SQL 查询中使用 Python 函数,那该多好啊,” Khuyen Tran 问道 Khuyen 继续介绍了 FugueSQL,这是一个精确地促进这种来回转换的库,并包括多个展示如何充分利用其特性的实践示例
  • 用几行代码 加速多种数据工作流。Ismael Araujo 最近带着米托图书馆兜了一圈(或者说:兜了几圈),并且一直很喜欢这种体验。在他对初学者友好的介绍中,他向我们介绍了这个库的一些核心功能——包括代码片段!

Jarrett TanUnsplash 上拍摄的照片

还没有足够的 Python?或者你可能正在寻找一些其他主题的顶级读物?我们乐意效劳:

感谢您加入我们为期一周的学习和探索。

直到下一个变量,
TDS 编辑器

真正的数据科学家是不存在的

原文:https://towardsdatascience.com/real-data-scientists-dont-exist-3434107a0892?source=collection_archive---------9-----------------------

我们都只是隐藏在华丽术语背后的骗子

汤姆·罗伯茨Unsplash 上拍照

你有没有看过这样一篇文章:

“现在每个人都称自己为数据科学家。操纵数据和使用 Python 库的能力并不能让你成为数据科学家。线性代数和统计的知识并不能让你成为数据科学家。使用数据获取商业价值的能力并不能让你成为数据科学家。别再自称数据科学家了。你不是科学家。”

在阅读了大约五篇提出相同观点的文章后,我发现自己在想:

"如果是这样,谁是数据科学家?一个人需要具备什么资格才能称自己为数据科学家?”

这些文章的作者似乎过于沮丧,他们认为不应该自称为数据科学家,以至于他们忘记了提及应该是谁。

这些作者中没有一个人提到一个真正的数据科学家应该具备的资格。他们也没有提及 “真正的数据科学家”的日常工作

那么…谁是真正的数据科学家?

我花了一整天阅读数据科学这个词的定义。

让我与你分享我的发现。

维基百科将数据科学定义为“一个跨学科领域,它使用科学方法、流程、算法和系统从结构化和非结构化数据中提取知识和见解,并在广泛的应用领域中应用来自数据的知识和可行的见解。”

《牛津词典》将数据科学家定义为“受雇分析和解释复杂数字数据(如网站的使用统计数据)的人,尤其是为了帮助企业做出决策。

Datarobot 将数据科学定义为"结合领域专长、编程技能以及数学和统计知识,从数据中提取有意义见解的研究领域。

读完这些定义后,我看了看一些数据科学的工作清单。公司希望数据科学家具备哪些技能?

我看到的第一份工作清单是一家财富 500 强公司提供的,我将在这里附上工作描述的截图:

AIA 数据科学职位描述

大多数数据科学职位描述与上面的类似。

因此,如果数据科学家的定义和工作描述与一个人的技能相匹配,我们为什么不允许他们自称为数据科学家呢?

数据科学是一个非常松散的术语。

它被用来描述许多不同的角色,这个术语已经变得如此模糊,几乎没有意义。

我曾与来自非技术背景的跨国公司数据科学团队领导共事过。

从事统计工作的人通常称自己为数据科学家,从事数据工程或分析相关工作的人也是如此。

这个术语太宽泛了,涵盖了许多不同人的工作范围。这没关系。

谁应该称自己为数据科学家?

我的职位名称中有“数据科学”几个字,我的技能与上面描述的完全一样。

如果您定期处理数据以获得洞察力并得出商业价值,您就是(根据定义)数据科学家。

清理和操纵数据、创建数据管道和构建预测模型的能力有资格称自己为数据科学家。

数据科学已经被广泛接受。

数据科学这个术语现在已经非常宽泛,它定义了全世界数百万人的工作范围。

老实说,纠结于条款是没有意义的。

除非“数据科学家”一夜之间神奇地被一个不同的词取代,否则人们不会停止称自己为数据科学家。

真脸还是 AI 生成的假?

原文:https://towardsdatascience.com/real-face-or-ai-generated-fake-d95b30c1f86f?source=collection_archive---------9-----------------------

你能说出这两者的区别吗?

图片由生成.照片奥米德阿明上的 Unsplash

在过去的几年里,我们已经看到了人工智能的巨大进步。它在医疗保健、时尚、教育和农业等行业都有应用,并被预测为下一个大的数字颠覆之一。

正如吴恩达所说:

正如 100 年前电力几乎改变了一切一样,今天我实际上很难想到一个我认为人工智能在未来几年内不会改变的行业。

然而,AI 是一个可以被误用的工具。这是一个强大的工具,如果它落入坏人之手,可以用来扰乱日常生活。

在这篇文章中,我将介绍人工智能的一个应用,它被用来欺骗人们和传播错误信息。

人工智能技术的应用

在过去的几年里,一个名为“deepfake”的人工智能应用程序在互联网上掀起了风暴。Deepfakes 是由神经网络创建的极具说服力的虚假视频。

名人和政治家的脸被贴在不同的身体上,虚假的视频被制作出来。

2020 年,deepfakes 成为主流。

它们已被广泛用于广告和电视节目中,并越来越多地被用来传播虚假信息。

2018 年,有人制作了一个视频,描绘巴拉克·奥巴马(Barack Obama)称唐纳德·特朗普(Donald Trump)是一个诅咒。视频开始几分钟后,人们发现奥巴马从未真正说出这些话。更确切地说,是《滚出去》的导演兼编剧乔丹·皮尔说的。

他的声音被数字化地插入到正式总统的视频中,以制造一个假的。该视频展示了 deepfakes 的威力有多大,以及它们如何被用来传播错误信息。

深度学习模型也可以用来创建假图像。

人工智能模型能够生成看起来与真人几乎一样的假脸。这些年来,他们变得非常擅长于此,几乎不可能区分真实的脸和人工智能生成的假脸。

你能分辨出这些脸哪个是真的吗?

图片由生成.照片奥米德阿明上的 Unsplash

右边的脸是真实的。左边的照片是由人工智能应用程序生成的。

虚假图像背后的技术

照片由 DocuSignUnsplash 上拍摄

Deepfakes 和假图像是由一类叫做“GANs”的机器学习模型生成的。

GANs 代表生成对抗网络,由研究员 Ian Goodfellow 和他的同事在 2014 年设计。

这个想法

absolute visionUnsplash 上拍摄的照片

这一切都始于人工智能研究员伊恩·古德菲勒的一个想法。他想创建一个能够生成虚假数据的深度学习模型。

他的想法包括使用两个神经网络,并让它们相互竞争。

第一个网络将用于基于现有数据集生成假图像。第二个网络将学会辨别真假图像的区别。

第一个网络称为发生器,第二个网络称为鉴别器。

生成器的工作是欺骗鉴别者,让他们相信生成的图像是真实的。这样,随着鉴别器识别假图像的能力越来越强,生成器就会开始生成更真实的图像来欺骗鉴别器。

这个生成器和鉴别器都是对手。随着一个网络变得更好,另一个也变得更好。

伊恩是在和几个朋友在酒吧时想出这个主意的,这些朋友告诉他这是不可能的。他们认为不可能在第一个网络的内环中训练第二个神经网络,并且他们假设该网络不会收敛。

那天晚上回家后,伊恩觉得这个想法值得一试。他写了一些代码并测试了他的软件。他第一次尝试就成功了!

他发明的东西现在被称为 GAN,是深度学习领域最令人着迷的创新之一。事实上,脸书首席人工智能科学家 Yann LeCun 称 GANs 为“过去 20 年中深度学习领域最酷的想法”

真实图像与 GAN 生成的图像

作者图片

正如我上面提到的,GAN 生成的图像非常有说服力。神经网络已经惊人地擅长创造逼真的人脸。

这可能是危险的,因为 GANs 可以用来创建虚假的约会资料,鲶鱼人,并传播虚假信息。

对我们来说,能够辨别真假是非常重要的。

我创建了一个应用程序来测试你是否能区分一个真人的脸和一张假脸。

你可以参加我的测验,试着识别哪些图像是真实的脸,哪些是 GAN 生成的。

在这个项目中,我使用了 100 万个假脸数据集来获得 GAN 生成的图像,并使用 Kaggle 的 UTKFace 数据集来获得真实图像。

我的测验是这样的:

作者图片

你需要做的就是猜测你在页面上看到的图片是假的还是真的。随着你不断练习,你会更好地识别计算机生成的图像。

本质上,你在我的测验中所做的和一个鉴别者所做的完全一样。

GAN 中的鉴别器学习伪图像和真实图像之间的区别。随着时间的推移,它会更好地识别假图像。这正是你在参加测验时要做的。

你可以在这里进入我的测验。这个测验应用程序的灵感来自于一个叫做 thispersondoesnotexist.com的网站,你可以去看看。每次刷新网站,都给你看一张假的 GAN 生成的脸。

结论

这篇文章介绍了人工智能生成的图像和 deepfakes 背后的技术。如果你想进一步了解这个话题,我推荐以下资源:

如果你对 GANs 的想法很感兴趣,并且想玩玩假图像,那么你可以随意参加我的测试,想玩多久就玩多久。

如果你已经走了这么远,感谢你的阅读!我希望你喜欢我的文章。

人工智能是用更复杂的技术取代人类决策——falgu ni Desai

现实生活中的元学习:教与学会学习

原文:https://towardsdatascience.com/real-life-meta-learning-teaching-and-learning-to-learn-186877376709?source=collection_archive---------15-----------------------

思想和理论

本·怀特Unsplash 上拍摄

是我们可以培养的两个最重要的技能,让自己和身边的人变得更好。虽然我们可能认为这些技能只适用于我们在学校的时候,但在这篇文章中,我希望说明在你的余生中每天积极使用这些技能是多么重要。

不管喜欢与否,我们总是 ——但是 影响 ,无论是积极的还是消极的,我们通过这些活动留给自己和他人的很大程度上取决于我们在这些能力方面的技能[1]。通过学习我们教和学的最好方法(元教学和元学习),我们的教和学的努力将为每个人做更多的事情

在这篇文章中,我的目标是通过三个主要观点给你一个关于终身学习和教学的新视角:

  1. 学习教学 是我们一生中至关重要的活动,远远超出了课堂。
  2. 学习 我们如何学习 (元学习)对于最大化学习的有效性至关重要。
  3. 思考教学的一种方式是,我们是 在教别人如何学习

我们将从概念上谈论这些想法开始,之后,我们将深入研究如何将这些想法公式化为优化问题。如果你想看我相应的关于这些优化的文章,你可以在这里 查看

准备好了吗?让我们开始吧!

1.教学和学习是终身的活动

什么是学习?

当我们学习时,我们的大脑会形成或加强脑细胞之间的物理回路。Robina Weermeijer 在 Unsplash 上的照片

想想你最近一次学到的东西。你从 5 年前或高中的课上学到的最后一件事是什么?当然不是——作为人类,我们不断学习新的概念、技能,更新我们对世界的信念。我们不需要去学校或参加会议来学习新的东西——部分由于互联网的力量,我们有很多一生都可以获得的知识,等待我们去学习。除了在学校学习代数、几何和物理,我们还学习我们如何学习

我不是神经科学专家,但我知道每当我们学习新的东西时,我们大脑中的神经元就会连接到新的神经元回路中。随着我们了解更多,定义这些神经元回路的连接变得更强[2],这表明学习本身就是一个正反馈循环:我们现在了解得越多,未来就越容易学习。从某种意义上说,是大脑的肌肉记忆。

那么,这与终身学习有什么关系呢?嗯,如前所述,我们学得越多,将来就越容易学。养成学习的习惯,不管你学的是什么,都会让你在以后的生活中更好地准备有效的学习。我认为重要的是要强调一个习惯可以是 5 分钟或 5 小时的学习——无论你在生活中能抽出时间做什么。

什么是教学?

照片由 NeONBRANDUnsplash 上拍摄

就像学习一样,我们不需要有头衔 老师教授 才能在某种意义上成为“老师”。虽然我们应该从这些职业中获得教导他人的灵感,但我们也应该认识到我们都有教导他人的责任。考虑以下场景:

  1. 一名高级数据科学家向一名实习生展示如何使用熊猫数据帧对象。
  2. 一位家长帮助他们的孩子第一次学习骑自行车。
  3. 在球队输掉冠军赛后,一名教练和一名球员谈论面对逆境时的应变能力。

在所有三个案例中,一个人为另一个人的学习贡献了。无论是某人展示一个新的视角,指导他们学习一项新技能,还是通过一则轶事讲述一堂人生课,这些都是我们称之为教学时刻。这些情景说明了教学的另一个重要方面:自主学习。

要成为一名好老师,就要给你的学生学习的统治权,同时向他们展示如何有效地使用这些统治权。这让他们能够优化自己的学习,而不必完全控制自己的学习方式。根据我的辅导和教学经验,有时对我的学生最有帮助的不是教他们一个概念,而是教他们如何自学一个概念。随着通过数字学习媒体获得高质量教育的机会增加,所有年龄、背景和领域的学生都将能够以我们从未想过的方式指导自己的学习。

事实上,这种信息获取渠道的增加使得教授如何学习变得更加重要:考虑到我们可以获得的所有信息,我们如何确保我们和我们的后代能够有效地学习和使用这些信息?这就是元学习的用武之地。

2.等等,等一下。元学习?

我可能是一个强化学习的书呆子,过度使用了“元”这个词,但元学习实际上是我们可以概念化的一种方式,教我们自己如何最优地学习新概念和新想法。

直观地说, 元学习 是关于学会如何学习,或者更具体地说,学会如何更有效地学习。从强化学习的角度来看,一个 元学习者 是利用 学习者的表现作为反馈机制来训练的——如果学习者在学习一项任务中是有效的,元学习者就被 奖励 ,如果学习者是无效的,元学习者就被 奖励这导致不仅有一个有效的学习者,而且有一个有效的元学习者,从而使学习者在未来的任务中取得成功。

作为人类,我们以大致相同的方式学习如何学习。考虑一下你是否正在学习一门新语言——如果你参加了语言评估并且表现良好,你很可能会保持相同的学习技巧。但是如果你在评估中表现不好,你可能会运用这种学习技巧在下次评估中表现得更好。“学会学习”这种技能对我们个人和技术的成长至关重要。

在强化学习的背景下,元学习者的图解。信不信由你,这个过程类似于我们如何自学!图片来源:[3]。

通过更加注意我们如何学习,以及我们如何适应我们的学习,我们成为更有效的元学习者,这反过来使我们成为更有效的学习者。考虑以下问题(如果你真的有这种感觉,把它们写下来):你学习的最好方法是什么?哪些方面不学最好?如果你发现你是凭经验得出这些结论的——也就是说,你观察到了什么样的学习技巧导致好的表现,什么样的学习技巧导致坏的表现,那么你已经完成了现实生活中的元学习!

3。教授元学习:你如何教授如何学习?

当我们教学时,我们是在帮助人们学习他们如何学习。通过使他人能够更有效地学习,我们通过向他们展示如何学习来训练不同的元学习者。虽然我们最终不能为他们决定学习方法,但通过给学生探索各种学习方法的机会,我们在某种意义上训练他们为自己确定一套最佳的学习方法。这种训练“算法”在原理上类似于元学习[3]。

下面,我有一些针对这种元学习为主的教学风格的有效教学策略的小技巧。首先,我要承认这个列表远非详尽无遗——相反,它只是为了给你提供一些例子。如果你也有例子可以在这里添加,我将非常感谢你在评论中添加它们!以下是一些高层次的教学方法:

  1. ****展示工具。工具是你应用和建立你所学主题的具体联系的手段。如果不了解行业工具,建立这些联系会困难得多。展示工具的应用和设计将允许学生自学如何最好地学习新的工具集。
  2. 让他们主宰自己的学习。考虑让你的学生向你解释概念。让他们引导你通过一段文字、一段伪代码或一个框图。问他们认为概念 X 可以应用于什么。
  3. ****告诉他们在哪里可以找到他们想要的东西。根据我作为学生、教师和导师的经验(和其他人一样,也有自己的偏见),当学生能够自己确定答案时,他们会比别人给他们答案时更积极地参与进来。如果你的学生被卡住了,考虑给他们看答案的地图,而不是答案本身。
  4. 认识到你的学生是聪明的。如果说我从《我们如何学习》这本书中学到了什么的话,那就是人类通常是周围最有效率和能力的学习者。认识到这一点,并利用这一点将你的学生推向他们的极限。当你的学生远远超出你的预期时,不要感到惊讶:)

本节结束时有一个有趣的提示——如果你在将教学概念化为训练元学习者时考虑如何优化你的教学风格,那么你真的在使用元元学习

从最优化的角度看教与学

在整篇文章中,我们一直在讨论如何提高我们的学习教学能力。让我们从优化的角度来看看我们如何做到这一点,使用元学习和强化学习的原则来指导我们。以下优化问题中形式化的目标可以通过元学习方法和梯度上升方法来优化。我们将把优化和训练算法例程留给读者,并把我们的重点放在正式定义目标上。

如果你想看我相应的关于这些优化的文章,你可以在这里 查看

优化学习者参数

在强化学习中,我们的目标通常是找到一个策略,它可以优化一些回报的度量——在最简单的情况下,这可以被计算为随着时间的推移而打折的回报。

我们将从定义学习者为给定任务优化的随机目标开始。优化目标被形式化为折扣奖励的期望总和。抽象而简单地说,为了捕捉策略 π 对收益 J()的影响,我们简单地将奖励 r 写成策略 π 的函数,将它们写成 r( π):

其中γ是 0 到 1 之间的常数,表示未来奖励的折扣。这个目标被公式化为一个期望,因为我们考虑了奖励是随机的一般情况。策略通常由一组参数 θ 来参数化。因此,我们可以根据 θ 将上述目标改写为:

最优参数集 θ 使这个学习者目标最大化,因为使这个目标最大化使学习者的期望折扣奖励最大化:

请注意,θ代表我们的假设空间——我们可以优化参数集的所有学习者模型的空间。上述优化为学习者代理找到了一组最佳学习参数。在现实生活中, θ 对应的是我们为了在给定的任务 J()中表现出色而学习的特定技能。现在,让我们更上一层楼。

优化元学习者参数

在学习者之上的一个层次,我们有元学习者,它决定了我们学习的能力。假设我们用一组参数φ∈φ对元学习器进行参数化,其中φ是我们的元学习器的假设空间。

由于元学习者决定了学习者能够在给定任务上学习得多好,我们可以捕捉这种依赖性的一种方式是通过用元学习者的参数 φ 来参数化学习者的假设空间θ和参数 θ 。这使得学习者参数集 θ 和学习者可以学习的可能模型的空间θ都依赖于元学习者的当前参数。以这种方式参数化,我们可以把元学习者的目标写成:

注意,在这个优化目标中,随机返回函数 J()不依赖于元学习者的参数,即学习者代理的目标不能被元学习者修改。这防止了元学习者使某些任务变得更容易或更困难。

这种优化为元学习者定义了最佳的参数集。在现实生活中,这种优化对应于我们如何学会学习。然而,上面的优化目标仅仅定义了如何为单个任务学习。在现实生活中,我们学习不仅仅是为了一个任务,而是很多任务。

为了优化超过 N 个不同的任务,我们可以用 i 来索引每个任务,用某个因子ωi > 0 来加权每个任务,并将每个任务的随机回报记为 Ji()。因此,这一目标采取以下形式:

上述优化解决了参数集,该参数集优化了如何在 N 个所选任务的加权和上学习。在现实生活中,这类似于我们如何学习优化我们的学习。

到目前为止,我们已经讨论了学习——教学在这方面发挥了什么作用?具体来说,我们如何利用目前介绍的目标来提高教学能力?

优化元元学习者参数

学习如何成为一个更好的老师,从学习的角度来看,可以概念化为元-元学习。为什么?因为我们通过观察哪些教学方法能让学生更有效地学习,哪些方法不能,来改进我们的教学。在这种情况下,对于我们的元元学习者,我们有一个更抽象的参数集,ψ∈ψ,其中ψ是元元学习者的假设空间。这些参数用于优化元学习者,与元学习者的参数用于优化学习者的方式相同。从某种意义上说,目标是不变的,只是我们针对这种变化优化了参数:

优化参数 ψ 相当于要么提高我们学会学习的方式,要么提高别人学会学习的能力,又名

如果我们试图教一个人或我们自己,这个目标是成立的——这对许多人来说是如何工作的?假设有 M 个我们要优化学习的个体,索引为 j ,我们用ρj > 0 对优化每个个体学习的重要性进行加权。那么我们的元学习目标就变成了:

由于我们是生活中许多人的老师和导师,也许这个公式是我们可以用来改进教学的最具代表性的目标。

以上这些优化仅仅是制定学习和教学的一种方式,但我希望它们提供了另一种视角来看待我们如何优化我们的学习,既为了我们自己,也为了他人

总结

这篇文章并不掌握成为一个完美的学习者和教师的关键,但我希望它为你提供了关于终身学习和教学对你意味着什么的新视角。

随着我们可以获得的信息比以往任何时候都多,我们尽可能地用它来教和学是至关重要的。我会让你来决定你学习和教学的最佳方式。没有一个教学或学习的时刻可以浪费!

非常感谢您的阅读!如果你想看更多关于强化学习、计算机视觉、机器人和机器学习的故事,请在 Medium 上关注我

感谢

感谢 CODECOGS 提供了有用的内联方程渲染工具,也感谢菲利普·伊索拉教授在麻省理工学院讲授 6.882 (体现智能),这门课启发了我写这篇文章。

参考

[1]普罗泽斯基博士《教与学》社区眼睛健康第 13 卷,34(2000):30–1。

[2]德阿纳,斯塔尼斯拉斯。我们如何学习:为什么大脑比任何机器学习得都好……目前来说。企鹅,2020。

[3]Tejas d . Kulkarni、Karthik R. Narasimhan、Ardavan Saeedi 和 Joshua B. Tenenbaum。2016.分层深度强化学习:整合时间抽象和内在动机。《第 30 届国际神经信息处理系统会议录》(NIPS'16)。美国纽约州红钩市柯伦联合有限公司,邮编 3682–3690。

机器学习的实时聚合特性(第 1 部分)

原文:https://towardsdatascience.com/real-time-aggregation-features-for-machine-learning-part-1-ec7337c0a504?source=collection_archive---------28-----------------------

为什么实时 ML 特性对 ML 应用程序至关重要并且难以实现

作者:凯文·斯坦普夫(泰克顿),迈克·伊斯特汉姆(泰克顿),尼基尔·辛哈(Airbnb)

(图片作者)

介绍

机器学习特征源自组织的原始数据,并向 ML 模型提供信号。一种非常常见的特征变换类型是滚动时间窗聚合。例如,您可以使用信用卡的滚动 30 分钟交易计数来预测给定交易具有欺诈性的可能性。

在 SQL 查询中使用窗口函数对您喜欢的数据仓库进行离线计算滚动时间窗口聚合非常容易。然而,为生产中的实时预测提供这种类型的功能带来了一个困难的问题:如何有效地以非常高的规模(> 1000 QPS),以低服务延迟(<< 100ms), at high freshness (<< 1s) and with high feature accuracy?

These types of features are incredibly important for countless real-time ML applications whose predictive power is very sensitive to a user’s behavior up until the very moment of the prediction. Common examples where every event counts are real-time recommendation, fraud detection, spam detection, personalization, and pricing predictions.

In this two-part blog (第二部分)提供这样一个聚集了大量原始事件(> 1000)的功能,我们将讨论这种功能类型最常见的技术挑战,以及 Airbnb 和 Tecton 用来解决这些挑战的经过战斗考验的架构。

技术挑战

对于上面提出的问题,一个简单的实现可能是在每次进行实时预测时,简单地查询生产中的事务数据库(如 MySQL ):

架构 1:为每个 ML 预测查询生产数据库(作者的图片)

这在小范围内工作得很好,如果您的数据库中有原始事件,这是一个很好的开始方式。然而,在高规模下,和/或一旦您的聚合依赖于大量的原始事件,您的服务延迟将开始激增,您的 MySQL 数据库将最终崩溃。

扩展上述架构的常见下一步是在新的原始数据可用时实时预计算聚合,并在针对低延迟服务优化的可扩展 KV-store 中提供这些功能(如 Dynamo 或 Redis):

架构 2:使用流处理器预先计算特性

这带来了一系列技术挑战,其中一些我们将在接下来的几节中探讨。

长时间运行的时间窗口聚合的内存限制

团队经常使用 Apache Spark 或 Flink 来运行流时间窗口聚合。Spark 或 Flink 作业的内存需求是时间窗口大小以及流的事件密度的函数。

想象一下,一个信用卡处理器的欺诈检测应用程序使用用户 4 周的交易计数作为一个重要特征。流处理作业现在需要将 4 周内发生的所有事务放入内存。

对于大数据量,如果没有正确配置,这将很快导致 OutOfMemoryExceptions。作为一种解决方案,团队通常求助于减少时间窗口、减少事件体大小、对流式作业进行分区,或者使用像 RocksDB 这样的状态存储,它可以将状态刷新到磁盘或冷存储。

回填挑战

一旦您对流式作业进行了微调以使其可靠运行,您可能会意识到您选择的流式服务并不包含足够的历史数据来回填新要素。例如,AWS Kinesis 最多只能保存 2 周的数据。如果您现在想要一个 6 周的时间窗口聚合,您的流聚合将提供不准确的特征值,直到它已经运行了 4 周。

在这些情况下,公司通常会将其流式传输时间窗口限制在流式传输基础架构中可用的数据持续时间内,通常会将重要信号留在桌面上。其他人可能会手工设计一个批处理管道,该管道预计算来自历史批处理源的数据,并尝试将其与由流管道向前填充的数据相结合。还有一些人可能会求助于 Kafka 等流基础设施,其保留期更具可配置性,并接受这样一个事实,即处理 Kafka 的大量历史数据比针对针对大规模批处理而优化的数据存储(如数据仓库或数据湖)运行批处理作业需要更长的时间。

保持高功能新鲜度

假设您已经解决了流时间窗口聚合的回填和内存挑战。几个 ML 应用程序的另一个常见挑战是它们依赖于非常新的特性(< 1 s)。理想情况下,在最大似然预测时,当您获取特征值时,您希望考虑到直到预测时刻发生的每一个事件。然而,实现实时的新鲜度是具有挑战性的,并且会变得非常昂贵。

如果您使用 Apache Spark 这样的流处理器来实现这些时间窗口聚合,控制特性新鲜度的一种常见方法是使用滑动时间窗口聚合。通过滑动时间窗聚合,您可以选择特定的滑动间隔,该间隔决定了流式作业发出更新的要素值的频率(并因此设置要素新鲜度的上限)。此外,我们发现流处理器在高数据量下可以支持的最小滑动间隔通常在几分钟的范围内,这限制了您的最大特性新鲜度。

其他流处理器,如 Apache Flink,允许您在没有滑动间隔的情况下运行聚合。每当在流中发现新事件时,它们可以发出特性更新。现在,您的要素与处理最近事件时一样新鲜。不幸的是,这意味着如果您不小心的话,您的特性的陈旧性将会变得无止境:如果没有新的事件被处理,那么您在产品中提供的特性值将不会得到更新,除非您还配置了一个超时,当经过了一定量的处理时间时强制进行更新。因此,滚动时间窗口聚合不会聚合预测发生之前特定时间窗口内发生的所有事件,而是聚合在找到流中最后一个事件之前发生的所有事件。

生成训练数据集

让我们假设你已经掌握了上面列出的所有挑战。您可以将这些特性应用到您的生产模型中!

但是,你仍然需要训练一个模型。这意味着您需要维护要素值的历史日志,以便了解过去不同时间点的世界状况。您可能不会存储和提供生产键值存储中的那些历史特征值,生产键值存储针对低延迟和大规模请求进行了优化。相反,您可能会选择像数据仓库或数据湖这样的离线存储,这是为偶尔处理大量数据而优化的。您的架构现在需要发展成这样:

架构 3:架构 2 有一个离线商店用于训练和批量预测(作者的图片)

你必须确保你用来训练你的模型的离线特征值的计算与你的生产路径一致,你的离线商店提供历史上准确的特征值而不引入数据泄露——否则你引入了可怕且难以调试的 train-serve-skew

结论(第一部分)

在本文中,我们讨论了许多技术挑战,这些挑战使得在大规模生产中提供新的滚动时间窗口聚合变得困难。第二部分将讨论泰克顿和 Airbnb 所采用的应对这些挑战的解决方案。

[1]如果提到的任何一个特征和要求的保证可以放松,一个不同的解决方案可能更好

[2]这并不总是正确的——对于一些 ML 应用程序来说,特性新鲜感很快接近递减/无回报

[3]两种常见的方法是记录生产服务的特征值,以及使用单独的计算路径从离线数据源回填

机器学习的实时聚合特性(第二部分)

原文:https://towardsdatascience.com/real-time-aggregation-features-for-machine-learning-part-2-fe9fd42522c0?source=collection_archive---------32-----------------------

为什么实时 ML 特性对 ML 应用程序至关重要并且难以实现

作者:凯文·斯坦普夫(泰克顿),迈克·伊斯特汉姆(泰克顿),尼基尔·辛哈(Airbnb)

(图片作者)

介绍

本博客系列的第 1 部分讨论了时间窗聚合对于 ML 用例的重要性。在接下来的章节中,我们将描述一种解决这些挑战的方法,这种方法已经在泰克顿大规模验证过,并且已经在 Airbnb 和优步的生产中成功使用了几年。讨论的方法解释了 Tecton 的实现完全依赖于开源技术。

解决方案:平铺时间窗口聚合

在较高层次上,该解决方案将一个全职窗口内的时间窗口聚合分解为 a)多个较小时间窗口的压缩分块,用于存储分块间隔内的聚合,以及 b)聚合时间窗口头部和尾部的一组预计原始事件。压缩的图块是预先计算的。在特征请求时,最终特征值是通过组合存储在图块中的聚合以及原始事件上的聚合按需计算的:

实时服务路径(作者的图片)中平铺式时间窗聚合的架构

功能配置

这些平铺时间窗口功能的配置需要以下详细信息:

  • 从一个或几个原始事件源中选择和投射原始事件
  • 这些计划事件的汇总定义

原始事件的选择和投影

Tecton 允许用户使用 SQL 选择和设计事件。该查询允许用户选择原始源,去除所有不必要的数据,并执行内联转换。转换示例可能如下所示:

select
    user_uuid,
    amount,
    timestamp
from
    Transactions

这个 SQL 通常可以由 Apache Spark 或 Flink 之类的流处理器本地执行。默认情况下,泰克顿在连续模式下使用火花流。

聚合定义

Tecton 允许用户使用简单的 DSL 定义聚合信息。按照上面的示例,用户可能希望在金额列上创建几个 sum 聚合:

aggregation_type: sum
aggregation_column: amount
time_windows: 2h, 24h, 48h, 72h

您可以浏览我们的文档来查看 Tecton 中这种特性定义的真实例子。上面的定义足以编排一组摄取作业,这些作业将新定义的功能加载到在线和离线功能存储中。

在线商店的流媒体摄取

在线商店中基于 Spark 流的功能摄取(作者图片

Tecton 使用 Spark 结构化流将来自流数据源的最新数据写入在线要素存储。用户的 SQL 转换应用于流输入,以投射事件并去除所有不必要的数据。结果被直接写入键值存储,没有进一步的聚合。

默认情况下,我们使用 DynamoDB 作为在线商店(支持 Redis 作为替代)。

批量摄取到在线和离线商店

基于 Spark Batch 的功能摄取到线下和线上商店(图片由作者提供)

串流摄取仅将最近的事件从串流来源向前填充到在线商店。其他一切都是离线批处理摄取作业的责任。这包括:

  • 回填和正向填充线下商店
  • 回填网店

批处理摄取也使用 Spark,但是是在批处理模式下,而不是在流模式下。批处理作业从批处理源读取,该批处理源是流式处理源的离线镜像(例如,配置单元或 DWH 表),并且应该包含流式处理事件的完整历史日志。Tecton 使用 Delta Lake 作为默认的离线商店(数据仓库作为未来受支持的替代方案)。

将原始事件压缩到图块

到目前为止,我们只讨论了将原始事件直接写入在线或离线商店。当然,这种幼稚的实现本身可能会导致在线服务路径中不可接受的高查询延迟。为一个聚合提供服务可能需要大量数据,特别是在长聚合窗口或具有大量更新的数据源的情况下。这可能会对服务路径的延迟产生严重后果,类似于在生产中直接查询事务存储。为了缓解这个问题,你需要周期性地压缩原始事件到不同间隔长度的瓦片中。

结果是,在线和离线要素存储将包含两种不同类别的数据:

  • 未压缩的数据:原始事件的投影
  • 压缩数据,这是使用用户配置的聚合操作压缩一个压缩间隔的数据的结果。在实践中,典型的压缩间隔是 5 分钟,也是一个“长”间隔,它跨越给定聚合窗口的最长完整多日切片

未压缩的数据是作为上述批处理/流摄取部分中描述的常规流的一部分产生的。压缩数据由一个单独的定期批处理 Spark 作业生成,该作业从流源的离线镜像中读取数据:

基于火花批的压实工艺(作者提供图片)

在离线或离线服务时,必须从存储中提取未压缩和压缩数据的混合,如下图所示:

利用图块和原始事件的实时服务路径(图片作者

从图中可以看出,压缩行是在聚合范围的中间提取的。一般来说,在聚集的头部和尾部会有时间范围,这些时间范围只是压缩行的一部分,对于这些时间范围,我们提取未压缩的行来完成聚集。这种优化减少了必须从脱机或联机存储中提取的最坏情况下的行数。

这个博客到目前为止已经解决了提供超新鲜特征值的问题(<1s). As a result, it’s required that you stream a projection of raw events into the store. The curious reader may have noticed that you could already write pre-computed tiles, instead of raw events, to the online and offline store. In this case, the tile length defines the ultimate freshness of the feature: The larger the interval, the lower the guaranteed feature freshness. Further, the tile length controls the write-frequency to the store: The larger the interval, the lower the write-frequency. At the limit, you can choose a tile size of “None”, which results in a write to the store for every event, resulting in maximal freshness and the highest write-load. Therefore, the minimal tile size really gives the user a knob to tune the cost/freshness-tradeoff of a given feature.

Online Serving

During online serving, the Feature Server receives a request containing the join keys to be retrieved. From these join keys Tecton generates an online store query for all of the data for the given join keys in the time range [Now — (aggregation time window), Now). This results in a series of compacted tiles as well as raw events, which are then aggregated by Tecton’s Feature Server to produce the final feature value.

Offline Serving

Offline Serving requests consist of a DataFrame with the following columns:

  • 连接键值
  • 一个时间戳。与始终从当前时间开始聚合的在线服务不同,离线服务请求能够从每行的不同时间开始聚合。这是生成训练数据集和通过时间旅行提供时间点正确性的重要功能。

离线服务流程类似于在线服务流程,只是它是作为批处理 Spark 查询来执行的。

优势总结

总之,所讨论的实现提供了以下好处:

  • 回填现在解决了,因为讨论的解决方案允许您从批处理源回填和从事件流向前填充
  • 长时间运行的时间窗口很容易支持
  • 支持超新鲜功能,因为在请求时,您可以始终考虑到直到预测的那一刻已经流入商店的事件
  • 计算和内存-经济高效的处理:
  • 只有当新事件到来时,以及当您向存储区写入切片时,您才会向存储区写入更新
  • 您只计算切片,而不是完整的时间窗。前者更便宜,需要更少的内存
  • 计算和存储高效处理多个时间窗口:您可以将切片重新组合成不同长度的时间窗口:例如,您可以存储 1h 切片,您可以获得 2h、12h、24h、7d 等。不具体化存储中的全职窗口的聚合
  • 快速特征检索:无论是离线还是在线,您都不需要在请求时聚集(仅仅)原始事件。您可以在预先计算好的切片上聚合,从而加快整个操作

深入探讨高级性能优化:锯齿窗口

Nikhil Simha (Airbnb)

如上所述,将预计算的图块与投影的原始事件相结合的概念是由 Airbnb 的 Zipline 背后的团队首创的。这种方法的下一个迭代是他们所谓的锯齿窗。

上述方法的一个含义是,在线商店必须在窗口的尾部以及整个窗口保持原始事件,以便于窗口随着时间向前滑动。随着窗口变大(180d 以上),这成为存储可扩展性的瓶颈。

为了避免这个瓶颈,你可以滑动窗口的头部,但是跳过窗口的尾部(一个一个的平铺)。这意味着窗口大小随着时间的变化而随着跳数的变化而变化。并非所有的工作负载都可以容忍这种变化,但在 Airbnb 的经验中,机器学习应用程序可以很好地容忍这种变化——甚至可能会因为特征数据中这种噪声的正则化效应而略有受益。

具有 1 天跃点的 30 天窗口只需存储 30 个每日部分聚合(切片)和 1 天的原始数据。此前,Airbnb 存储了 30 天的原始数据,以方便窗户尾部的滑动。这将原始数据的存储需求降低了一个数量级。

因此,实际的窗口长度在 30 天到 31 天之间变化。我们称这些窗口为“锯齿窗”:一种对空间要求低、新鲜感高的跳跃滑动窗口的混合体。只需允许窗口的长度按照其定义大小的一小部分变化,就有可能在不影响新鲜度的情况下同时改善存储大小和延迟。

结论

总之,我们已经展示了如何通过以高性能、经济高效以及在线/离线一致的方式将预计算的数据资产与按需转换相结合,来解决极其常见和具有挑战性的 ML 特征类型。好奇的读者可能已经意识到,这种预先计算与按需计算相结合的模式可以推广到时间窗口聚合之外,并扩展到完全由用户定义的转换步骤。这种一般化的方法允许您最终表达跨越离线分析世界和在线操作世界的计算 Dag:

跨越在线和离线环境的通用计算 DAG(图片作者

在未来的博客系列中,我们将讨论这个强大的 DAG 概念的好处和可能性,以及如果您想在 ML 应用程序中利用它,现在如何使用 Tecton。如果你想了解更多关于这个实现的技术细节,请随意给我们发一条松弛消息这里

[1]高效的压缩实现还可以将较小粒度的瓦片压缩成较大粒度的瓦片

关于其他技术的说明:我们上面描述的,当然不是一个完全新颖的实现。我们描述了“视图物化”的一般过程,并展示了如何使用开源流/批处理技术来实现它,这些技术可以直接插入组织的现有键值存储以及现有的离线存储(如数据湖或数据仓库)。像 timescaledb、ksqldb、materialize 或 Druid 这样的工具利用了类似的策略。根据您的现有堆栈、您引入新存储技术的意愿、您对一致的离线和在线商店的需求以及您对从批处理源高效回填要素的需求,您可能能够直接在这些工具的基础上构建您的应用程序。

还值得注意的是,上述解决方案解决了引言中列出的约束,这些约束在高价值的操作性 ML 应用中常见:在大量原始事件(> 1000s)上的特征聚合,这些事件需要以非常高的规模(> 1000s QPS)、低服务延迟(< < 100ms)、高新鲜度(< < 1s)、以及高特征准确性(例如,有保证的而非近似的时间窗口长度)来提供服务。如果其中一些约束不适用于您的用例,可以使用上面讨论的架构的更简单版本。让我们在 Slack 上讨论更多细节。

航空工业中的实时分析第一部分

原文:https://towardsdatascience.com/real-time-analytics-in-the-aviation-industry-part-1-f90418cd7dc3?source=collection_archive---------43-----------------------

我们的挑战引入对飞行数据的实时分析以提供有价值的见解

约瑟夫·布拉德肖在 Unsplash 上拍摄的照片

Spidertracks ,我们致力于通过提供实时飞机跟踪以及将飞行数据转化为有价值的见解来帮助航空业变得更加安全。作为该公司成为数据驱动型组织之旅的一部分,我目前正在参与一个项目,该项目授权客户定义典型的飞行是什么样子的,并允许我们的平台负责触发事件——也称为安全事件——如果意外发生的话。

这个项目有一些有趣的问题需要解决,我将把这 2 篇文章集中在事件生成引擎的技术方面:我们的需求;选择流处理技术;挑战和局限。

首先,需要注意这个难题的四个方面:

洞察数据管道

  • 数据源是由飞机姿态和位置等飞行度量组成的,在飞行过程中从飞机上获取并摄取到 AWS。
  • 客户配置(例如在救援飞行期间,在给定高度下,可接受的 俯仰 值是多少)存储在 NoSQL 数据库中。
  • 事件生成引擎将客户配置与输入的飞行指标结合起来,以决定是否必须生成事件。
  • 目的地是放置生成的事件以供消费的地方。

在项目开始之前,数据源已经就位,我们对如何在数据库中存储客户配置有了清晰的理解。这个项目架构的复杂性来自于构建事件生成引擎。

事件生成引擎

要求

  • 从数据被接收到 AWS 到事件提供给我们的客户之间的低延迟;
  • 引擎必须是 有状态的 有两个主要原因:
    a) 事件应该只在当前传入行满足配置标准而前一行不满足的情况下生成。这保证了当阈值已经达到时,不会产生事件**。
    b)在飞行过程中,飞机可能没有被完全覆盖,这意味着数据可能以稀疏的方式被摄取。但是,管道的行为方式应该与数据实时可用的方式相同。
  • 该架构是面向未来的,但我们知道产品应该尽快提供给一小部分客户,以验证我们的假设。

选择流处理技术:选项

根据定义的需求,我们开始了一项调查,以确定什么样的流处理技术能够满足我们的需求。从技术角度来看,我们寻求的是:

  • 在 AWS 上运行
  • 使我们能够快速交付并迭代我们的方法
  • 要求低维护
  • 可靠吗

经过一番研究,我们发现了几个不错的候选人:

  • Spark Streaming :具有良好文档和社区支持的近实时流处理框架。在 AWS 上,我们可以通过 AWS Glue (无服务器)或 AWS EMR (托管集群)来运行它。
  • Apache Flink :实时流处理框架,相比 Spark Streaming 这是一个优势,但在我看来社区支持较弱。Apache Flink 也运行在 AWS EMR (托管集群)上,但其在 AWS 上的无服务器产品需要通过 AWS Kinesis Analytics
  • SQL的 Kinesis Analytics:使用 SQL 实时分析流数据的服务。这项服务与其他两个候选服务的一些关键区别是,SQL 的 Kinesis Analytics 不是一个框架,而是一个云服务,流处理是通过 SQL 而不是使用编程语言来完成的。

Kafka Streams 是唯一可行的选择,如果我们已经通过 Kafka 运行我们的摄取管道。

选择流处理技术:决策

我们决定使用 SQL 的 Kinesis Analytics。我将首先解释为什么我们在第一次迭代中放弃了 Spark Streaming 和 Apache Flink:

  • 我们意识到开发 Apache Flink 应用程序并对其进行故障诊断的学习曲线非常陡峭,考虑到我们的截止日期,这是不可行的。更具体地说,Apache Flink 应用程序主要是用 Java 开发的,我们的堆栈是用 Python 构建的。Pyflink 是一个支持使用 flink 开发 python 的库,但它不能通过 AWS 上的无服务器服务获得(在撰写本文时,Apache Flink 的 Kinesis Analytics 仅支持 Java)
  • Spark Streaming 是一个更好的选择——我们确实有 Apache Spark 的内部经验——但 AWS 通过 AWS Glue 提供的无服务器产品似乎不如通过 Kinesis Analytics 运行流媒体应用程序划算。

为什么 Kinesis Analytics for SQL:

  • 无服务器:考虑到交付我们第一次迭代的最后期限,我们明白我们的努力应该放在生成事件的逻辑上,而不是学习如何维护平台。事实证明这对我们非常重要。
  • 凭借团队中良好的 SQL 经验,我们认为学习流 SQL 是可以实现的;
  • 自动缩放:Kinesis Analytics 自动缩放以匹配容量和吞吐量。

接下来是什么?

在本系列的第 2 部分中,我将深入探讨 SQL 的 Kinesis Analytics:它如何融入我们的数据管道,在这个过程中我们有哪些惊喜,以及我们的事件生成引擎的未来可能会是什么样子。

航空业中的实时分析——第 2 部分:利用 SQL 的 Kinesis 分析

原文:https://towardsdatascience.com/real-time-analytics-in-the-aviation-industry-part-2-leveraging-kinesis-analytics-for-sql-64ae7240cfe?source=collection_archive---------38-----------------------

SQL kine sis Analytics 如何融入我们的数据管道,以及我们面临的挑战

Avel Chuklanov 在 Unsplash 上拍摄的照片

这是我设计和构建事件生成引擎系列的第二部分,也是最后一部分。第 1 部分中,我解释了为什么我们在项目的第一次迭代中选择了AWS kine sis Analytics for SQL,现在我将分享它如何适应我们现有的架构,以及我们为确保它为我们工作所做的调整。

运动学分析:运动部件

数据源

在此项目之前,数据从飞机上捕获,发送到数据管道进行浓缩,并存储在数据湖中进行分析。

我们的目的是通过添加一个并行步骤来修改该数据管道,该步骤可以将源数据发送到数据湖,还可以发送到 Kinesis Analytics 以生成事件。我们发现 Kinesis Analytics 可以从两个流源读取数据: Kinesis 数据流Kinesis 数据消防软管。我们决定将数据管道扩展到 Kinesis 数据流,然后自动转到 Kinesis Analytics 进行分析。

为什么是 Kinesis 数据流?Kinesis 数据流提供了比 Kinesis 数据消防软管更低的延迟,我们可以在未来聪明地使用分片来平衡工作负载。

目的地

Kinesis Analytics 可以将数据输出到 Kinesis 数据流、Kinesis Data Firehose 或一个 Lambda 函数(通常用于进一步丰富数据)。在我们的例子中,我们希望将事件推送到数据湖,所以 Kinesis Data Firehose 是合适的选择,因为它与 S3(我们的数据湖所在的地方)连接,可以在将数据刷新到 S3 之前缓冲数据(保持文件大小在我们的控制之下)。

转变

Kinesis Analytics 负责运行我们的转换步骤。这就是我们的习俗事件世代逻辑生活的地方。

在一个或多个 Kinesis 分析应用中,我们将输入数据映射到一个应用内流。这个对象(让我们把它命名为 input _stream) 从我们的 Kinesis 数据流中提取数据,并可以使用 SQL 进行查询。然后,我们检查是否必须通过读取客户配置来生成一个事件(关于我们如何做的更多信息,请参见本文后面的,我们将输出写入另一个。第二个流(让我们把它命名为 output_stream )被映射到一个 Kinesis Firehose 交付流,负责写入数据湖。

以下 SQL 代码让您体验了 Kinesis Analytics 中的一个简单查询:

-- Output Stream Definition: linked to Kinesis Firehose
CREATE OR REPLACE STREAM "OUTPUT_STREAM"
(
   "column_1" varchar(100),
   "column_2" varchar(100)
);-- Output pump: responsible for generating an event
CREATE OR REPLACE PUMP "OUTPUT_PUMP" AS
INSERT INTO "OUTPUT_STREAM"
SELECT STREAM
"input"."value_1",
"input"."value_2"
FROM "INPUT_STREAM" "input"

简单地说,下图显示了添加到现有管道中的内容。

Kinesis Analytics 加入数据管道

管道挑战

现在我们已经了解了 Kinesis Analytics 如何融入管道以及服务内部发生了什么,我将分享我们在更新管道中面临的挑战。

1 -管道负载

Kinesis 数据流通过添加碎片进行缩放。每个碎片可以处理高达 2MB/s 的读取和 1MB/s 的写入。你添加的碎片越多,你支付的服务费就越多。

作为负载测试的一部分——为了重现产品的样子——我们超过了 1MB/s 的写入限制。最初的想法是通过增加碎片数量来解决。然而,我们决定先尝试一些替代方案来控制成本:

  • 压缩:我们尝试使用一个名为 zlib 的 Python 库来压缩负载。我们惊喜地发现压缩率提高了 85%。
  • 分块:我们没有一次将全部有效载荷写入 Kinesis 数据流,而是将其分块,以便在一次调用中发送更少的记录。

这两项改进都让我们有信心用单个碎片运行 Kinesis 数据流。此外,我们设置了警报,让我们知道何时达到 80%的限制,这样我们就可以在看到节流错误之前采取行动。

然而,我们确实面临着压缩方面的挑战。Kinesis 数据分析无法读取压缩数据,所以我们需要在 Kinesis 数据流和 Kinesis 数据分析之间的一个步骤来解压缩有效载荷。幸运的是,Kinesis 数据分析有一个特性,允许调用一个预处理 lambda 函数。我们让这个 lambda 解压缩有效载荷,并将其返回到 Kinesis 数据分析进行消费。下图展示了更新后的管道。

预处理添加到数据管道中的 lambda

2 - Kinesis 数据分析-读取客户配置

这是作为事件生成引擎的一部分要克服的最大挑战。首先,让我们介绍 Kinesis 数据分析应用程序中可用的数据对象:

  • 应用内流:本文已经提到过,用于引用来自数据源的数据的对象。例如:从 Kinesis 数据流或 Kinesis 消防软管输送流中读取。
  • 参考数据源:存储在 S3 上的文件,用于丰富/加入输入数据流。

我们的最终目标是让事件生成引擎读取客户配置并将它们与传入的流数据结合起来,以决定是否生成事件。明确的前进方式是将客户配置存储在 S3 文件中,并使用引用数据源对象来应用我们的逻辑。然而,这也带来了我们希望避免的问题:

  • 维护 S3 文件:客户配置从前端不断更新,并存储在 NoSQL 数据库中。保持 S3 文件最新将会解决其他挑战,比如并发处理和 S3 最终一致性(顺便说一下,这将不再是问题了)。
  • Kinesis 数据分析更新:为了反映应用于 Kinesis 数据分析中 S3 文件的更改,我们需要运行一个更新命令,将流置于更新模式。除了在更新时使流不可用之外,它还破坏了应用程序的状态。我这么说是什么意思?这意味着,如果你正在使用窗口,应用程序会失去对先前记录的跟踪,并且可能不会像你预期的那样运行。

我们是怎么解决的? 老实说,我不认为 SQL 的 Kinesis Analytics 提供了一种体面的方式来管理这种类型的场景,其中用于“丰富”流数据的数据经常发生变化。

我们知道 S3 文件引用根本不起作用,由于没有其他可用的数据对象,我们最终追加了客户配置作为流数据的一部分,我们使用 SQL 从 Kinesis Data Analytics 内的应用内流中提取配置。

这种方法难道没有把管道负载的问题带回来吗? 不,这是因为在发送到 Kinesis 数据流之前,我们没有在源中丰富客户配置,而是利用预处理 lambda 为我们完成了这项工作。现在,这个 lambda 除了解压缩有效负载之外,还从 NoSQL 数据库中读取客户配置,并将它们附加到流中。

缩放管道

展望未来,我们可能必须扩展这一管道,以容纳更多的并发航班和额外的预配置事件定义。

我们可能需要扩展的地方:

  • Kinesis 数据流的附加碎片(数据加载);
  • 额外的 Kinesis 分析应用(更多事件定义);

为了确保上述工作符合预期,我们进行了一些实验,我们验证了即使我们只在一侧扩展,管道行为仍然保持一致。总之,多个 Kinesis 数据流碎片将与单个 Kinesis 分析应用一起工作,并且多个 Kinesis 分析应用也从单个 Kinesis 数据流碎片中读取,而没有并发问题。

事件生成引擎的未来

本文中解释的设置可以成功地处理这个项目的当前需求以及预期的工作量。然而,有一些因素可能会导致我们在未来迭代并潜在地取代 SQL 的 Kinesis 数据分析:

  • 增加客户定义事件配置的能力:在这个时候,我们——spider tracks——定义事件配置的样子,并为我们的客户提供输入阈值的能力。如果该产品发展到客户完全控制事件配置的地步,我们可能无法用纯 SQL 做到这一点,我们将使用 Spark Streaming 或 Apache Flink 来寻找编程方法。
  • 成本:目前我们正在使用无服务器服务,从低维护的角度来看这很好,但与我们通过运行自己的集群所能执行的优化相比,它的成本更高。

就是这样。感谢您阅读我们的旅程!

基于 Apache Kafka 和 Python 的实时异常检测

原文:https://towardsdatascience.com/real-time-anomaly-detection-with-apache-kafka-and-python-3a40281c01c9?source=collection_archive---------8-----------------------

了解如何使用 Python 对来自 Kafka 的流数据进行预测。

照片由阿隆在 Unsplash 上的视觉效果拍摄。

在这篇文章中,我将讨论如何使用 Apache Kafka 的传入流数据进行实时预测;我们将要实现的解决方案如下所示:

解决方案图。图片由作者提供。来自平面图标的图标。

这个想法是:

  • 使用无监督机器学习训练异常检测算法。
  • 创建一个新的数据生成器,将事务发送到 Kafka 主题。
  • 阅读 Kafka 主题中的数据,使用训练好的 ml 模型进行预测。
  • 如果模型检测到事务不是内联者,就把它发送到另一个 Kafka 主题。
  • 创建最后一个读取异常并向松弛通道发送警报的消费者。

本文假设您了解 Apache Kafka、机器学习和 Python 的基础知识。

交易可以表示任何相关信息,以便实时分析并预测是否有异常情况,如信用卡交易、GPS 日志、系统消耗指标等。

1。项目结构:

我们的项目结构将是这样的,你可以在这里得到完整的代码:

git clone [https://github.com/rodrigo-arenas/kafkaml-anomaly-detection.git](https://github.com/rodrigo-arenas/kafkaml-anomaly-detection.git)

首先查看 settings.py 它有一些我们需要设置的变量,比如 Kafka broker 主机和端口;您可以保留默认设置(监听本地主机以及 Kafka 和 zookeeper 的默认端口)。

streaming/utils.py 文件包含创建 Kafka 消费者和生产者的配置;它有一些默认选项,如果需要,您也可以更改。

现在安装要求:

pip install -r requirements.txt

2。训练模型

为了说明如何设置这个解决方案,我们将生成随机数据;它会有两个变量,它们看起来像这样:

异常检测数据。图片由作者提供。

接下来,我们将使用一个隔离森林模型来检测离群值;简而言之,该模型将通过跟踪(采样)变量轴上的随机线来尝试隔离数据点,并在几次迭代后,测量隔离每个观察值的“难度”,因此在 train.py 文件中我们有:

运行这个之后,应该会创建 isolation_forest.joblib 文件;这是经过训练的模型。

3.创建主题

我们将使用两个主题;第一个称为“事务”,生产者将发送新的事务记录。让我们使用以下命令从终端创建它:

kafka-topics.sh --zookeeper localhost:2181 --topic transactions --create --partitions 3 --replication-factor 1

第二个主题将被称为“异常”,检测到异常的模块将在这里发送数据,最后一个消费者将读取数据以发送一个松弛通知:

kafka-topics.sh --zookeeper localhost:2181 --topic anomalies --create --partitions 3 --replication-factor 1

4.交易生成者:

现在,我们将生成第一个向 Kafka 主题“transactions”发送新数据的生成器;我们将使用合流-卡夫卡包;在文件 streaming/producer.py 中,我们有:

有了这段代码,一个生产者会向一个卡夫卡主题发送数据,概率为 OUTLIERS _ GENERATION _ PROBABILITY;数据将来自“异常值生成器”,将发送自动增量 id、机器学习模型所需的数据和 UTC 中的当前时间。

让我们检查到目前为止一切都是正确的,运行 producer.py 文件,并作为消费者从终端登录到主题:

kafka-console-consumer.sh --bootstrap-server 127.0.0.1:9092 --topic transactions

您应该会看到这样的传入消息:

交易生成者。作者 Gif。

5.异常值检测器消费者:

数据来了!现在,我们必须从消费者那里读取它,将其传递给机器学习模型以进行预测,并过滤离群值。这是在如下所示的 streaming/anomalies _ detector . py 文件中完成的:

消费者阅读来自“交易”主题的消息,并且消费者向“异常”主题发送离群值;除了我们已经拥有的数据之外,它还将通过模型给出的分数来丰富记录,这是一种衡量数据“有多少”被视为异常值的方法。

请注意,只有那些预测输出为-1 的消息才会转到新主题;这就是这个模型将数据归类为内联者的方式。

另外,注意这个主题有三个分区,所以在最后,我使用多重处理来模拟三个独立的消费者,并加快这个过程;它们都属于同一个 group_id。在生产中,这些消费者可能会在不同的服务器上运行。

让我们检查这一步,确保生成器正在运行并运行 anomalies_detector.py 文件,现在在终端中,让我们打开异常主题,我们应该看到模型预测为异常值的传入事务,它应该如下所示:

异常检测。作者 Gif。

这里有一个事务生产者和异常值检测如何同时运行的可视化;顶部窗口是事务生产者主题,底部窗口是发送到异常主题的离群值。

实时异常检测。作者 Gif。

5.时差通知:

最后一步,我们希望对这些检测到的异常值采取一些措施;在现实生活中,它可以阻止交易、扩展服务器、生成建议、向管理用户发送警报等。

这里,我们将向一个松弛通道发送一个警报;为此,请确保创建一个 slack 应用程序,将该应用程序添加到 slack 通道,并注册一个名为 SLACK_API_TOKEN 的环境变量来验证 SLACK。这里是相关文档。

现在我们使用文件 streaming/bot _ alerts . py;代码如下:

所以在这里,脚本创建了一个新的消费者,但是它订阅了“异常”主题;一旦消息到达,它将使用 Slack API 发送消息;对于这个演示,我发送了相同的原始消息(尽量使它更漂亮!).传入的消息如下所示:

宽限通知。作者 Gif。

原来如此;解决方案已经启动并运行!我希望这对你有用。请记住,完整的代码在这里:

https://github.com/rodrigo-arenas/kafkaml-anomaly-detection

我想留下一些关于这个特定实现的最后考虑:

  • 我在本地机器上进行所有的设置和运行,选择多少分区、消费者、代理、zookeeper 服务器和要设置的副本(以及其他配置)是您必须根据您的业务特征、数据生成速率、可用资源等进行分析的事情。我在演示中使用了足够小的数字。
  • 我使用 scikit-learn 和“纯”Python 来处理数据流,但根据消息量/生产率,可能有必要使用流处理功能,如 spark streaming
  • 你必须知道的还有对松弛 API 的限制。

使用深度学习的实时艺术品生成

原文:https://towardsdatascience.com/real-time-artwork-generation-using-deep-learning-a33a2084ae98?source=collection_archive---------20-----------------------

用于任意内容样式图像对之间的样式转换的自适应实例标准化(AdaIN)。

AI 生成的艺术品。图片来源:[6]

在这篇文章中,我们将会看到黄等人的论文《实时任意风格转换与自适应实例规范化》(AdaIN)。艾尔。我们之所以关注这篇论文,是因为它比当时或发布时的其他最先进的方法有一些关键优势。

最重要的是,这种方法一旦经过训练,就可以用于在任意内容样式的图像对之间转换样式,甚至是在训练期间没有看到的图像对。而 Gatys 等人提出的方法。艾尔。也可以在任何内容样式图像对之间转换样式,与此方法相比,它非常慢,因为它在推断过程中对样式化图像进行迭代优化。AdaIN 方法也是灵活的,它允许控制风格化图像中转换的风格的强度,还允许扩展,如风格插值和空间控制。

接下来,我们将首先熟悉实例规范化、自适应实例规范化,然后深入研究 AdaIN 论文的工作。最后,我们将看到一些输出,并查看实现自适应实例规范化和训练风格转移网络的代码。

实例规范化

图 1:不同的物化技术。图片来源:[3]。

批次归一化使用整批的平均值和方差对整批固定大小的特征图进行归一化。另一方面,实例标准化(也称为对比标准化)对每个样本的每个通道进行标准化。

情商。1:实例规范化

来自 Eq。1,我们可以清楚地看到,每个样本的每个通道都是单独归一化的。与批处理规范的另一个区别是,与批处理规范不同,实例规范也应用于推理过程中。

实例规范对输入图像的对比度进行标准化,使风格化图像独立于输入图像对比度,从而提高输出质量[2]。[1]的作者还指出,实例标准化通过匹配特征统计(均值和方差)来充当样式标准化的一种形式。这里,图像标准化的风格由可学习的仿射参数γ和β[1]定义。

要了解更多关于实例规范和其他规范化技术,请点击查看 T2 的帖子

条件实例规范化

条件实例标准化在[4]中介绍,其中作者建议为每种风格的图像学习不同的γs 和βs。

情商。2:条件实例规范化。

虽然这种方法工作良好,但它需要 2xCxS 个额外参数(C: # channels,S: # styles),从而增加了网络的规模。因为实例 norm 执行一种形式的样式转换[1],为每种样式设置一组参数允许我们将图像标准化为每种样式。

自适应实例标准化

作者扩展了仿射参数γ和β定义实例范数的规范化风格的思想,提出了自适应实例规范化。在建议中,实例标准化被修改以适应不同的内容风格的图像对。

AdaIN 将内容特征和风格特征 xy 作为输入,并简单地将内容特征的统计量(均值和方差)与风格特征 y 的统计量进行匹配。这里没有可学习的参数,转换如下获得

情商。3:自适应实例规范化。

其中,σ (⋅是标准偏差,σ(⋅)是方差,μ(⋅是平均值,所有这些都是按照实例规范中的空间维度计算的。

亚当式传输网络

体系结构

图 2:提议的风格转移网络架构/图片来源:[1]

AdaIn StyleNet 遵循编码器-解码器架构(图 2)。编码器 f(⋅) 是 VGG19 网络的前几个预训练层(直到 relu4_1)。编码器是固定的,没有经过训练。

对编码器的输出执行自适应实例标准化,如下所示

情商。4:阿丹混合。

其中 f(c)和 f(s)分别是由编码器为内容和风格图像产生的特征图。混合系数α∈[0,1]控制风格化图像中风格的强度。α在训练期间被设置为 1。

解码器 g(⋅) 与编码器相反,池被 2x 最近邻向上扩展所取代。解码器用随机权重初始化,并学习其权重。通过将变换后的特征图 t 传递到生成器中来获得风格化图像。

情商。5:风格化图像生成

损失函数

损失目标是风格和内容损失的组合。

情商。6:全部损失

内容损失是从 VGG 编码器获得的风格化图像的特征图和 AdaIN 输出 t 之间的欧几里德距离:

情商。7:内容丢失

AdaIN 输出 t 用作内容目标,因为它有助于模型更快地收敛。风格损失的计算公式如下:

情商。8:风格丧失

每个 ϕ i 都是 VGG 19 网络的一层。这里使用 relu1_1、relu2_1、relu3_1 和 relu4_1。

输出样本

这里有一些由模型生成的艺术作品。

风格转移在行动。图像来源:(a)[7],(b)[8],(c )[6]

风格转移在行动。图片来源:(a)[9],(b)[10],(c )[6]

使用α在推断期间控制风格化图像上的风格强度。图像来源:[1]

代码和预训练模型

实现自适应实例规范化的代码片段:

训练 AdaIN 型传输网络的代码可在此处找到:

https://github.com/aadhithya/AdaIN-pytorch

预训练模型(pytorch 和 onnx 格式)的权重可在此处找到:

https://github.com/aadhithya/AdaIN-pytorch/releases/tag/1.0

结论

这篇文章介绍了实例规范化以及自适应实例规范化如何用于任意内容样式图像对之间的样式转换。通过训练生成器网络和使用自适应实例标准化,我们能够在任何内容样式图像对之间转移样式。我们还能够在运行时控制生成图像的样式强度。该方法提供的另一个优势是推理速度超过当时的其他模型。最后,展示了一些输出,代码和预先训练的推理权重可供免费、非商业使用。

如果您发现帖子中有任何错误,请留下评论,我会修复它们!

如果你喜欢这篇文章,请考虑关注作者, Aadhithya Sankar

参考

[1]黄、荀、贝隆吉。“通过自适应实例规范化实时传输任意样式。”IEEE 计算机视觉国际会议论文集。2017.

[2] D.Ulyanov,A.Vedaldi 和 V.Lempitsky .改进的纹理网络:在前馈风格化和纹理合成中最大化质量和多样性。2017 年 CVPR

[3]吴、庾信、何。“分组规范化。”欧洲计算机视觉会议(ECCV)会议录。2018.

[4] V. Dumoulin、J. Shlens 和 M. Kudlur。艺术风格的学术表现。在 ICLR ,2017。

[5]李彦宏,王,刘军,侯。揭开神经类型转移的神秘面纱。 arXiv 预印本 arXiv:1701.01036 ,2017。

[6]https://github.com/aadhithya/AdaIN-pytorch

[7]https://en . Wikipedia . org/wiki/File:Side _ profile,_Brihadeeswara.jpg

[8]https://en . Wikipedia . org/wiki/Olive _ Trees _(梵高系列)#/media/File:梵高 _The_Olive_Trees..jpg

【9】https://en . Wikipedia . org/wiki/Taj _ Mahal #/media/File:Taj _ Mahal _ in _ India - Kristian _ bertel . jpg

【10】https://en . Wikipedia . org/wiki/The _ Starry _ Night #/media/File:Van _ Gogh - Starry _ Night - Google _ Art _ project . jpg

作者的其他作品

如果你喜欢这篇文章,这里有一些你可能会喜欢的文章:

Python 中的实时仪表板

原文:https://towardsdatascience.com/real-time-dashboard-in-python-970e3c32b3d7?source=collection_archive---------1-----------------------

实践教程

流和刷新

摄莎莎故事Unsplash

数据科学家使用数据可视化来交流数据和产生见解。数据科学家必须知道如何创建有意义的可视化仪表板,尤其是实时仪表板。本文讨论了用 Python 获得实时仪表板的两种方法:

  • 首先,我们使用流数据并创建一个自动更新的流仪表板。
  • 第二,每当我们需要刷新仪表板时,我们使用“刷新”按钮来刷新仪表板。

出于演示的目的,图和仪表板是非常基本的,但你会得到我们如何做一个实时仪表板的想法。

本文的代码可以在这里找到: realtime_dashboard.ipynbrealtime_dashboard.py 。这两个文件的内容完全相同,只是格式不同。

数据源

为了展示我们如何使用真实世界的 API 创建这个仪表板,我们在本文中使用来自 OpenWeather API 的天气数据作为示例。

如果您想尝试本文中提到的代码,您需要访问 OpenWeather 天气 API。你可以在开放天气网站注册,然后获得 API 密匙

建立

在进入代码之前,让我们安装需要的包:

conda install datashader holoviews hvplot notebook numpy pandas panel param requests streamz

导入包

在这里,我们导入用于本文的包:

流式数据和仪表板

首先,我们使用 OpenWeather API 创建一个函数weather_data来获取一系列城市的天气数据。输出是一个熊猫数据帧,每行代表一个城市。

其次,我们使用streamz来创建一个基于旧金山天气数据的流数据帧。函数streaming_weather_dataPeriodicDataFrame函数用作回调函数,以创建streamz流数据帧dfstreamz文档记录了PeriodicDataFrame的工作原理:

streamz 为此提供了一个高级便利类,称为 PeriodicDataFrame。PeriodicDataFrame 使用 Python 的 asyncio 事件循环(在 Jupyter 和其他交互式框架中用作 Tornado 的一部分)定期调用用户提供的函数,收集结果并供以后处理。

https://streamz . readthedocs . io/en/latest/data frames . html # streaming-data frames

streamz流数据帧df看起来像这样,每 30 秒更新一次值(从我们设置interval=’30'开始)。

第三,我们使用hvPlot绘制streamz数据帧来制作一些非常基本的图形,然后我们使用panel来组织图形并将所有内容放入仪表板中。如果您想了解更多关于如何使用hvPlot绘制streamz数据帧的信息,请参见 hvPlot 文档

这是仪表板的样子。由于流数据帧每 30 秒更新一次,该控制面板也将每 30 秒自动更新一次。这里我们看到温度发生了变化,而湿度和风速没有变化。

很好,现在你知道如何制作一个流数据框架和一个流仪表板了。

如果你想了解更多,我的朋友吉姆·贝德纳尔博士在流媒体仪表盘上有一个很棒的视频教程。请检查一下!

刷新仪表板

有时候,我们并不真的需要一个流媒体仪表盘。相反,我们可能只想在看到仪表板时刷新它。在本节中,我将向您展示如何在您的控制面板中创建一个“刷新”按钮,并在您单击“刷新”时用新数据刷新控制面板。

首先,让我们创建一个简单的函数weather_plot在一张给定城市名称的地图上绘制天气数据,并返回绘图和数据。

有了绘图功能,我们就可以开始制作可刷新的仪表板了。每当我们点击按钮Refresh时,我使用param.Action函数来触发更新。此外,我使用param.ObjectSelector函数创建我们感兴趣的数据框列的下拉菜单,并且每当我们在下拉菜单中选择不同的选项时param.ObjectSelector触发更新。然后@param.depends('action', 'select_column')装饰器告诉get_plot函数每当我们点击Refresh按钮或选择另一列时重新运行。

以下是刷新控制面板的外观:

最后,我们可以使用panel来合并我们创建的两个仪表板。

部署

我们最后的仪表板pane是一个panel对象,可以通过运行:

panel serve realtime_dashboard.py

或者

panel serve realtime_dashboard.ipynb

关于panel部署的更多信息,请参考panel 文档

现在你知道如何使用hvplotpanelstreamz在 python 中制作实时流仪表盘和可刷新仪表盘。希望这篇文章对你有帮助!

参考

https://anaconda . cloud/tutorials/4038 AE 58-286 a-4 FDC-b8bf-b4b 257 e 2 EDF 3

https://openweathermap.org/api

https://panel.holoviz.org/gallery/param/action_button.html】

https://streamz.readthedocs.io/en/latest/dataframes.html

https://hvplot.holoviz.org/user_guide/Streaming.html

https://panel.holoviz.org/user_guide/Deploy_and_Export.html

作者索菲亚·杨 2021 年 2 月 7 日

媒体推特LinkedinYouTube :)上关注我

IOS 中的实时数字识别

原文:https://towardsdatascience.com/real-time-digit-recognition-in-ios-2a982f32fcce?source=collection_archive---------21-----------------------

在 swift 中使用 torchscript 处理现场摄像机馈送

作者演示 gif

计算的未来包括能像我们一样看东西的计算机。手机识别我们的脸,相机看书,汽车自动驾驶。我们只会有更多的这些。

很难不好奇这些算法是如何工作的。对于像我这样的人来说,建筑是最好的学习方式。在这一努力中,我围绕计算机视觉的 hello world 问题开发了一个 ios 应用程序。这篇文章记录了我和我的橡皮鸭在开发应用程序时的对话。源代码是 github 上的。我希望你觉得这是有用的。

维基百科上看到的我的橡皮鸭(汤姆·莫瑞斯——自己的作品,CC BY-SA 3.0 )

范围

识别手写数字是计算机视觉的基石问题。最好通过神经网络来解决。对于这个问题,有大量关于训练神经网络模型的资源。我不会重复模型构建和训练的细节。因此,我假设在这些方面有一些先验知识。在这个练习中,我的重点是将一个经过训练的模型移植到移动环境中。

我正在寻找答案的问题:

  • 如何将模型从 python 导出到 ios?
  • 我如何在 swift 中运行推理?
  • 我如何从实时摄像头捕捉图像?

技术堆栈

我使用 pytorch 来构建模型,因为我想尝试一下 torchscript。对于 ios 应用程序,我使用 swift 和 swiftUI。

创建模型

通过设计,我选择了一个被广泛研究的问题。Amitrajit 的帖子是解决这个问题的好资源之一。我遵循它的指导来选择网络架构和超参数。

MNIST 数据库图片来自维基百科

我选定的模型有两个卷积层和两个全连接层。它使用 LogSoftmax 作为输出层激活。

另一个重要的细节是优化器、损失函数和训练参数。

通过这种配置,该模型在 mnist 数据集上的 3 个时期的训练中实现了 97%的准确度。

模型输入

该模型根据训练数据进行训练。我想让它在来自相机流的图像上工作。这被称为生产数据。应用程序必须对生产数据进行预处理,以匹配训练数据的形状和语义。否则它给出的结果将是次优的,如果不是垃圾

为了正确地进行预处理,必须对训练数据和生产数据以及模型返回的输出数据有全面的了解。因此,我和我的鸭子花了很长时间讨论这些数据。

让我们从训练数据集开始。在没有任何变换的情况下,数据集包含 PIL 图像及其标签的元组。

我们将对该数据集应用两种转换。一个是将数据从 PIL 格式转换成张量。另一个运行规范化逻辑。

第一次变换后,我们得到了形状为(1, 28, 28)的张量。这是因为图像是单通道(灰度),28×28 像素的图像。这些张量保存浮点数,值在01之间。这些值反映了像素的亮度;0是黑色,1是白色。

第二个转换计算张量中每个元素的标准分数(也称为 z 分数)。标准分数告诉我们一个值离平均值有多少标准差。这个数据集的均值是0.1307,标准差是0.3081。因此,在这个变换之后,张量的值将在-0.42422.8215之间展开。

这些转换步骤背后有许多有趣的信息。为了简洁起见,我只给出几个链接。首先,源代码说明了很多问题。看看 ToTensor 类和它的底层实现。其次,阅读关于标准分数,以及我们为什么将它应用于训练数据集。然后看规范化类的代码和它的实现。最后, Compose 类将展示这些步骤如何简单地链接在一起

形象化

我们了解了输入格式。接下来,我们应该检查语义。神经网络的一个挑战是它们处理的大量数据。输入的原始打印输出不能被人类消化。

我们必须使这些数据易于理解,这样人们就可以对其进行推理。一种常见的方法是使用可视化技术。有许多复杂的工具可以用来可视化大型数据集。在我们的例子中,数据集很简单。只有打印的声明能让我们走得更远。我们知道数据代表一个 28 乘 28 的图像。让我们相应地打印它。

输出中突出显示的-0.42显示了我们期望看到的模式。

在这个输入中,正数代表亮像素,而负数属于黑色背景。这个输入的标签说这个数据代表数字三。我们看到的模式证实了这一点。让我们更改代码,使模式更加清晰可见。

这个 ascii 艺术的尝试给了我们一个很好的洞察,我们用什么来喂养这个模型。这种原始可视化的优点是可移植性。我们稍后将在 ios 环境中使用相同的逻辑。

不是每个数据集都像这个一样容易可视化。对于更复杂的数据集,有复杂的工具。一个例子是 Matplotlib 。

输出

这个模型正在解决一个分类问题。它预测了 10 个类别中每个类别的置信度得分。这反映在模型架构中。输出层是具有 10 个节点的全连接层。每个节点代表十个数字中的一个。

置信度得分是由激活方法决定的。在我们的例子中,这是 LogSoftmax (阅读更多关于 softmax 函数为什么我们要添加对数)。这个函数给我们负数。数字越接近零,置信度越高。

在这个例子中,模型在第三类中具有最高的置信度,表明这个图像最有可能代表数字 3。

摆脱 Python

Torchscript 是迈向非 python 环境的第一步。简单来说,torchscript 给了我们一个可以在 c++中使用的模型。一旦模型被训练,我们就可以将其转换成 torchscript 模块

Torchscript 提供了几种检查模块的方法。其中之一就是.code属性。

这个表示捕获了我们在模型中定义的前进方法的步骤。进一步的细节在火炬脚本介绍教程中解释。我们还可以优化移动模块

最后我们序列化模块并保存到磁盘。此时,模块就可以导出了。

进入 IOS

我们训练了一个模型,将像素数据(图像)转化为数字预测。我们可以从 c++中调用它。我们如何在 ios 中运行它?这是我们将在本节中发现的内容。

在 ios 中使用 torchscript 的先决条件是 libtorch 库。它允许我们反序列化我们在 python 环境中序列化的模型。这个库可以在 cocoapods 中获得。

下一个障碍是从 swift 调用这个库。Swift 需要手握才能与 c++对话。Swift 可以和 objective-c 对话,objective-c 可以和 c++对话。因此,我们需要一个围绕 libtorch api 的 objective-c 包装器。

包装包括三部分结构:

桥接头包括模块头的单个导入。模块头包含声明。只有模块实现是有趣的。它提供了两种方法。

initWithFileAtPath在给定的文件路径反序列化模型。一旦模型被反序列化,它就将其设置为评估模式

predictImage方法是整合的核心。这是我们对模型进行推理的地方。

首先我们需要一个张量来进行推理。我们使用 torch::from_blob 函数将图像缓冲区转换为张量。

at::Tensor tensor = 
  torch::from_blob(imageBuffer, {1, 1, 28, 28}, at::kFloat);

第二个参数{1, 1, 28, 28},表示我们想要创建的张量的大小。该值显示批处理大小,后跟单个训练图像的大小。即使我们在单个图像上运行推理,pytorch 模块也总是期望输入是批处理的。因此批量大小为一。最后一个参数表示张量中元素的类型。

接下来的两行是我缺乏 c++经验的地方。在高层次上,我知道我们禁用梯度计算,因为我们正在运行推理而已。我找不到这些电话的具体细节。

torch::autograd::AutoGradMode guard(**false**);
at::AutoNonVariableTypeMode non_var_type_mode(**true**);

最后,我们调用正向方法,并将结果转换为张量。

**auto** outputTensor = _impl.forward({tensor}).toTensor();

其余的逻辑是自我描述的。我们处理输出以返回一个数字数组。

运行推理

在我的第一次迭代中,我对来自 mnist 数据集的静态图像进行了推理。我们的模型是在这个数据集上训练的。对于这些图像,我们应该从模型中获得高精度。

我将数据集中的一些图像添加到资产目录中。然后我可以访问这些图像。

接下来,我们需要一个模型实例来调用推理方法。我们使用之前实现的 api 来实例化模型。文件路径应该指向一个序列化的 torchscript 模型。在我们的例子中,在将模型转换为 torchscript 之后,这是我们之前生成的文件的副本。

我们有了输入,我们有了模型实例,我们应该准备好调用predict方法。然而,我们不是,因为我们需要预处理输入图像,以将其转换为我们的模型理解的结构。这就是我们对模型输入的理解变得重要的地方。

我们的输入是一个UIImagepredict方法期望的是一个浮点数数组。我们不仅希望将图像像素转换为浮点数,还希望对这些数字进行规范化。如果您还记得我们在训练集上运行的数据转换,其中之一就是规范化。

torchvision.transforms.Normalize((0.1307,), (0.3081,))

再次强调它;我们希望生产输入数据尽可能接近训练数据。否则我们会得到垃圾结果。

我们需要采取一系列步骤来将输入数据转换成模型期望的格式。一旦确定,这些步骤很容易实现。最多需要熟悉一下 swift 标准库和苹果的核心图像框架。

输入管道

我再重申一下目标:UIImage -> Byte Array -> Float Array。我将这条管道分解为以下几个步骤:

  • 将 UIImage 转换为 CGImage
  • 读取 CGImage 字节
  • 使标准化

UIImage 到 CGImage

在 ios 中处理图像有些复杂。一个恰当的例子是读取字节。为了访问一个UIImage的字节,我们需要先把它转换成一个CIImage,然后再转换成一个CGImage。之后,我们可以读取图像的字节。关于这些图像类的介绍,请查看 hackingwithswith swift 上的集成核心图像帖子。

该逻辑可能会失败,因此该方法返回一个可选结果。在错误处理之后,我们可以访问 CGImage。

读取字节

我们回到了读取字节的轨道上。在这一步中,我们将分配一个内存空间,并在该内存中重新绘制图像。在这个过程之后,我们感兴趣的字节将在分配的内存中结束。

我们通过初始化一个变量来分配内存。

**var** rawBytes: [UInt8] = 
  [UInt8](repeating: 0, count: 28 * 28) // 1 bytes per pixel;

有几件事需要注意。我们使用 8 位无符号整数。任何 8 位数据类型都可以在这里工作。其次,数组的大小与训练图像的像素数相匹配。我们使用灰度图像进行训练,我们只需要一个字节来表示每个像素。

接下来,我们将创建一个位图图形上下文。这个结构将使我们能够重新绘制图像。上下文的构造函数需要一个指向内存空间的指针。我们将使用 swift 标准库方法和 unsafmutablebytes来获取一个指针。

构造函数参数对于正确理解非常重要。data指向分配的内存。widthheight是自我描述的。根据文档,每个像素在存储器中都有分量。在我们的例子中,一个像素只有一个字节大小的分量。因此,bitsPerComponent就是8space表示色彩空间。在我们的例子中,这是灰度。bitmapInfo表示该位图没有 alpha(透明)通道。文档有关于参数的更多信息。

在必要的错误处理之后,我们可以继续绘制图像。首先,我们定义想要绘制图像的区域。然后我们调用绘制方法

当这个回调函数执行时,它将用我们在cgImage中的图像的像素数据覆盖rawBytes数组的值。

使标准化

预处理的最后一步是归一化。首先,我们用255除这些值,得到介于01之间的数字。然后,我们将应用应用于训练数据集的相同逻辑。如果你之前看过规格化方法的源代码,你就会知道移植起来很简单。

我们得到了在-0.42422.8215之间的浮点数。和我们在训练环境中的完全一样。

可视化 II

作为最后一项检查,让我们运行 ascii art 方法,这次是在 swift 中。

输出看起来与输入图像非常相似。

运行推理 II

早期我们实例化了这个模型。我们还对输入进行了预处理。我们准备调用predict方法。

我们再次使用了带有不可替换字节的方法。这是因为模块对象上的predict方法需要一个指针。在错误处理之后,我们得到一组预测结果。

正如我们之前所研究的,输出有 10 个数字。我们感兴趣的是最高的置信度得分。我们运行一个排序逻辑来识别这些。

排序结果的第一个元素是具有高置信度的3。有用!🥳

插曲

我们刚刚设法让 ios 应用程序识别一个数字的静态图像。令人惊讶的是,即使对于这样一个被广泛研究的问题,构建解决方案的细节也是如此之多。同样令人满意的是看到所有的部分走到一起并交付结果。

到目前为止,我们实现了两个目标:

  • 如何将模型从 python 导出到 ios?
  • 我如何在 swift 中运行推理?
  • 我如何从实时摄像头捕捉图像?

在第二部分,我们将发现现场视频处理隐藏的细节。敬请关注。

浏览器中的实时边缘检测

原文:https://towardsdatascience.com/real-time-edge-detection-in-browser-ee4d61ba05ef?source=collection_archive---------22-----------------------

使用 OpenCV.js 查找视频中的边缘

我的猫周围检测到的边缘。作者制作的图像。

边缘检测是计算机视觉中的一个基本工具。通过使用鲜明的亮度对比,可以在图像中找到大致的轮廓和形状。边缘检测通常用作一种过滤器来找出照片中项目之间的区别,它为对象识别提供了第一块垫脚石。

OpenCV 以 C++、Java、Python 和 JavaScript 提供了广泛的计算机视觉工具。其中包括使用 Canny 方法的边缘检测算法。OpenCV 的边缘检测实现以性能为出发点,可以与实时视频接口,几乎没有明显的延迟。

虽然 Python 中有很多例子和教程,但 JavaScript 中的例子和教程很少。随着 web 技术可移植性的增加,现在占据了桌面应用程序甚至电话应用程序的空间,介绍一下在 web 应用程序中使用 OpenCV 的基础知识是有意义的。

Canny 边缘检测

Canny edge detection 以其创新者 John Canny 的名字命名,它试图找到这样的边缘:

  1. 它在图像中找到尽可能多的边缘,
  2. 边缘点应该位于边缘的中心,并且
  3. 边缘仅被标记一次,并且不包括背景噪声

Canny 边缘检测是在计算机视觉的早期(1986 年)开发的,至今仍是前沿应用的黄金标准。对于那些性能优于 Canny 的少数边缘检测算法,它们通常需要更多的时间和计算能力,这使得它们不适合实时检测。

幸运的是,没有必要从头开始写一个算法。OpenCV 用几行简单的代码提供了 Canny 边缘检测的有效实现。

Canny 方法如何找到边的数学问题超出了本文的范围,但是更多的细节可以在这里找到。

HTML 设置

在跳转到 JavaScript 应用边缘检测之前,必须在 HTML 页面上设置适当的元素。为了进行实时检测,OpenCV 需要一个视频标签和两个画布。

<!DOCTYPE html>
<html>
    <head>
        <Title>
            Real Time Edge Detection with OpenCV.js
        </Title>
    </head>
    <body>
        <video id="video" playsinline autoplay></video>
        <canvas id = "streamCanvas"></canvas>
        <canvas id = "edgeDetectionCanvas"></canvas>

        <script type="text/javascript" src="lib/CV2/opencv.js"></script>
        <script type="text/javascript" src="js/edgeDetection.js"></script>
    </body>
</html>

包含两个画布的原因是因为 OpenCV 不能从视频标签中读取帧。相反,将使用一种变通方法将视频标签中的帧呈现到第一个画布上。这幅画布将被称为 streamCanvas。然后,OpenCV 可以读取和修改第一个画布上的帧,并将其呈现在第二个画布上,名为 edgeDetectionCanvas。

请注意,opencv.js 库包含在第一个脚本标记中。可在“https://docs . opencv . org/{ VERSION _ NUMBER }/opencv . js”找到一份副本。本文使用了 4.5.1 版本,可以在这里找到。

浏览器中的实时边缘检测

// Set a variable for the video tag
const video = document.getElementById("video");// Function to stream video from the web cam
async function streamVideo() {
    // Set video parameters
    const constraints = {
        // no audio is required
        audio: false,
        // Set the video size
        video: {
            width: 800, height: 400
        }
    };

    try {
        // Get the video stream

        const stream = await navigator.mediaDevices.getUserMedia(constraints);
        window.stream = stream;
        video.srcObject = stream; 

    } catch (e) {
        alert("An error occurred while accessing the web cam.");
    }   
}

第一行只是为视频标记建立了一个变量,这个变量将在程序中定期使用。

接下来,定义一个异步函数,将视频从网络摄像头传输到视频标签。在该功能中,定义了一组视频参数。音频被设置为 False,因为这纯粹是一个视觉演示,宽度和高度是相应定义的。

try/catch 实际上封装了用于流式传输的代码。如果在访问网络摄像头时出现问题,或者用户根本没有问题,就会有警报通知他们。否则,来自摄像机的视频将直接流入视频标签,而 getUserMedia 方法不会再有任何问题。

// Function to draw a video frame onto the canvas
function drawCanvas() {
    // Get context and draw frame from video element
    var canvas = document.getElementById("streamCanvas")
    var ctx = canvas.getContext('2d');
    ctx.drawImage(video, 0, 0); 
}

另一个函数被定义为将视频标签中的一帧绘制到其中一个画布上。在其中,定义了“streamCanvas ”,并将画布指定为 2D 画布(与 3D 渲染相反,3D 渲染也是可能的)。

最后,从视频标签中提取图像。drawImage 方法中的一对 0 只是指定在画布上的坐标(0,0)处绘制图像。

function edgeDetection() {

    // Start video stream
    streamVideo();

    // Set interval to repeat function every 42 milliseconds
    setInterval(() => {
        // Draw frame to the intermediate canvas
        drawCanvas();

        // Get the current frame from the intermediate canvas
        var src = cv.imread("streamCanvas");
        cv.cvtColor(src, src, cv.COLOR_RGB2GRAY, 0);
        cv.Canny(src, src, 50, 100, 3, false);
        cv.imshow("edgeDetectionCanvas", src);
        src.delete(); }, 42);
}

这里定义了边缘检测的实际功能。在执行任何操作之前,都会调用 streamVideo 函数,这样视频就会立即传输到视频标签。

setInterval 函数以定义的时间间隔重复其内容。在这种情况下,它被设置为 42 毫秒,因为它对应于每秒大约 24 次重复。当调用 drawCanvas 时,它以每秒 24 帧的速度绘制视频。

最后使用 OpenCV,从 imread 命令开始,它只是捕获 streamCanvas 上的当前帧。下一行将彩色图像转换成黑白图像。下面一行实际上应用了 Canny 边缘检测。

注意 Canny 函数参数中的 50 和 100。这些分别被定义为最小值和最大值。强度高于最大值的任何边都会立即被分类为边。任何强度低于最小值的边缘都会立即被归类为非边缘。基于连通性来判断两个值之间的强度,以确定其作为边缘的状态。

最后,imshow 方法将新修改的帧呈现到 edgeDetectionCanvas,并删除该帧的源以释放内存。

更详细的解释可以在官方文档这里找到。

// main function to clean up
function main() { // Hide the video tag
    video.style.display = "none";
    // Run edge detection
    edgeDetection();
}// Load main
main();

在最后的清理中,定义了一个主函数,该函数隐藏视频标签并运行边缘检测。这将使两个画布并排,以便可以清楚地看到常规流式视频和边缘检测之间的差异。

视频流和边缘检测的并排比较。作者制作的图像。

结论

OpenCV 为计算机视觉提供了非常强大的工具集。由于强调实时应用,OpenCV 为任何对从图像中获得洞察力感兴趣的人提供了宝贵的资产。

Canny 边缘检测的使用是许多其他计算机视觉应用的基础,例如背景去除和对象检测和识别。理解基本原理将为更复杂的计算机视觉任务提供基础。

使用 Pytorch 和多任务级联卷积神经网络(MTCNN)识别 Sonora 大学计算机科学学生的实时面部识别应用程序

原文:https://towardsdatascience.com/real-time-facial-recognition-application-to-identify-computer-science-students-at-the-university-of-253bc43d7c32?source=collection_archive---------35-----------------------

解释和使用,关于我们如何完成我们的工作

来自: Link de la imagen

戴安娜·劳拉·巴列斯特罗斯·巴伦苏埃拉、埃利乌德·吉尔贝托·罗德里格斯·马丁内斯、乌戈·弗朗西斯科·卡诺·贝利斯、马丁·何塞·维加·诺列加

索诺拉大学,埃莫西约,索诺拉

mjvnor@outlook.comdballesterosvalenzuela@gmail.com,slabxcobra@gmail.com,eliud.giroma@gmail.com。

github:https://github . com/MJVNOR/Real-time-face-recognition-mt CNN/tree/main

抽象。这个项目的目标是检测埃莫西约索诺拉州索诺拉大学的每个计算机科学学生,以便能够在能够识别他们面部的基础上开展其他项目。在这个项目中,我们使用带有神经网络的图像处理进行面部识别。我们实现了 MTCNN,这是一种检测图像中的人脸和面部标志的神经网络,是当今最准确的工具之一。

关键词:面部识别,神经网络,pytorch

1。简介

使用面部识别及其实现的机构的数量每天都在增加,这是因为它有许多好处,例如识别迷路的人,识别可能的小偷,识别哪些工人在某一天去工作,等等。面部识别是:给定一张“未知”人脸的图像,在一组“已知”图像中找到同一张人脸的图像。

面部识别已经经历了几个取得进展的阶段。我们可以看到以前它只是一个虚构的东西,而今天我们可以在许多应用和地方找到它。

伍德罗·威尔逊·布莱索被认为是这项技术的先驱,因为他在 1960 年研究了一个对人脸特征进行分类的系统,尽管这个过程是非常手工的,但他使用触笔和坐标来精确定位人的眼睛、鼻子或嘴。

今天,我们可以看到面部识别是如何成为人工智能的一部分,使我们能够检测和识别人脸。

在这项工作中,使用了人工智能领域的工具,能够实时识别人脸,并且如果该人脸是我们学生数据集的一部分,则能够识别该人

2。提议的工件

实时面部识别应用。

2.1 内容建模:

我们做的第一件事是使用 MTCNN 网络来生成学生面部的数据集,并让它返回面部确实是面部(每张图像只有一个面部)和面部是张量形式的概率。

mtcnn0 = MTCNN(image_size=240, margin=0, keep_all=False, min_face_size=40)

我们使用一个默认模型(vggface2)来输入我们的张量数据集,并返回每个人脸的最重要的数据(以矩阵形式),这称为嵌入。

resnet = InceptionResnetV1(pretrained='vggface2'.eval()for img, idx in loader:
    face, prob **=** mtcnn0(img, return_prob**=**True)
    if face **is** **not** None **and** prob**>**0.92:
        emb **=** resnet(face.unsqueeze(0))
        embedding_list.append(emb.detach())
        name_list.append(idx_to_class[idx])data **=** [embedding_list, name_list]
torch.save(data, 'data.pt')

在一个循环中,我们访问设备的摄像头以开始我们的识别。我们使用 MTCNN 类型的第二个网络,该网络用于检测是否有人脸(如果检测到人脸,它会在每个人脸周围打印/绘制一个方框),并给出它确实是人脸的概率,但这一次该网络的输入将是我们相机的帧(澄清该网络可以在每个帧中一次检测多个人脸)。我们再次使用 vggface2 模型来嵌入我们检测到的人脸。

使用保存的人脸嵌入和使用我们相机的帧生成的嵌入,我们计算学生的人脸组与我们在我们的帧中检测到的人脸之间的距离,并且使用最小的距离,我们说这个人脸是某个学生的。

load_data **=** torch.load('data.pt')
embedding_list **=** load_data[0]
name_list **=** load_data[1]cam **=** cv2.VideoCapture(0)while True:
    img **=** Image.fromarray(frame)
    if img_cropped_list **is** **not** None:
        boxes, _ **=** mtcnn.detect(img)
        for i, prob in enumerate(prob_list):
            if prob**>**0.90:
                emb **=** resnet(img_cropped_list[i].unsqueeze(0)).detach()
                dist_list **=** [] 
                for idx, emb_db in enumerate(embedding_list):
                    dist **=** torch.dist(emb, emb_db).item()
                    dist_list.append(dist)min_dist **=** min(dist_list)
                min_dist_idx **=** dist_list.index(min_dist) 
                name **=** name_list[min_dist_idx]box **=** boxes[i]
                original_frame **=** frame.copy()if min_dist**<**0.90:frame **=** cv2.putText(frame, name**+**' '**+**str(min_dist), (box[0],box[1]), cv2.FONT_HERSHEY_SIMPLEX, 1, (63, 0, 252),1, cv2.LINE_AA)frame **=** cv2.rectangle(frame, (box[0],box[1]) , (box[2],box[3]), (13,214,53), 2)cv2.imshow("IMG", frame)

3.MTCNN 是如何工作的

MTCNN 模型是由张、张和智峰在论文“使用多任务级联卷积网络的联合人脸检测和对准”中提出的。该模型的主要目标是检测人脸及其特征,它由三级卷积网络组成。

该模型基于利用多任务学习,以便划分面部和特征的检测任务,因为以多任务方式这样做通过利用包含在相关任务的信号训练中的域特定信息来提高概括,并且这是通过在使用信息的共享表示的同时并行训练任务来完成的。

开始阶段之前的第一步是获取图像,并以不同的比例调整其大小,以建立图像金字塔,这是网络第一部分的入口。

第一阶段包括拥有一个全卷积网络(FCN),我们用它代替 CNN,因为它的架构中没有密集层。这个建议的网络被用于获得面和它们各自的包围盒。这个网络的输出给了我们所有可能的候选人脸。

在网络的第二阶段,第一阶段的输出被用作 CNN 网络的输入,这被称为“改进网络(R-Net)”,这减少了可能的候选的数量,校准边界框,并且进行非最大抑制(NMS)来联合我们的可能的候选。作为输出,它告诉我们输入是否是一张脸。

最后,第三个非常类似于 R-Net,它给出了候选人眼睛、鼻子和嘴巴的位置。

4.我们如何计算距离

我们使用欧几里德距离。欧几里德距离代表平面上两点之间的最小距离,它是两点之间的直线。

法师作者

这个距离的计算方法是两个向量的元素之间的平方差之和的平方根,如前面的公式所示。

dist = torch.dist(emb,emb_db).item()

我们给这个函数我们正在检测的人脸,我们从我们的数据库中获取每个人脸的距离,从那里我们保存所有的距离并取最短的一个。

4.1.检测结果

这个人并不存在,我们把他从 thispersondoesnotexist.com 带来,我们把他放在我们的数据库里,叫他 Andrew 来展示这个程序工作的例子。

“安德鲁”(不存在的人)

正如我们所看到的,程序检测到“Andrew”的距离大约为 0.6237。距离值越低,那个人就越有可能是那个人。

5.我们犯的错误和发现的错误

我们发现,当我们在数据库中创建同事面部的嵌入时,一个同事的计算机内存不足,而另一个没有,换句话说,你必须有 1gb 以上的可用内存(ram)。

我们也尝试过实现多线程,结果惨败。

6.结论和未来工作

我们可以说,使用 MTCNN 并利用嵌入来获得距离给了我们非常精确的结果(即使每个人的人脸数量很少),但负面的一面是,当它检测到人脸时,FPS 会降低,当它检测到你是“确定”的人时,FPS 会降低更多。

我们计划更改应用中的某些内容,以尽可能降低 FPS(例如使用除计算距离之外的另一种方法),并能够为项目添加更多功能,以便与我们自己的同事一起改进和利用技术。

参考

  1. 张,张,张,李,乔,于(2016)。基于多任务级联卷积网络的联合人脸检测和对齐。IEEE 信号处理快报,23(10),1499–1503。李普舒茨,s,拉斯李普森,m:集合论:离散数学。麦格劳-希尔公司,第 1–22 页(2007 年)
  2. 多任务学习。机器学习 28,41–75(1997)。https://doi.org/10.1023/A:1007379606734

无界数据的实时直方图

原文:https://towardsdatascience.com/real-time-histogram-plots-on-unbounded-data-9948892c29a4?source=collection_archive---------23-----------------------

使用动态图表在笔记本电脑中监控您的流数据

阿迪·戈尔茨坦在 Unsplash 上拍摄的照片。作者的所有其他数字。

每个人的数据科学工具箱都包含一些基础工具。我们系统地使用它们,直到我们认为它们的使用是理所当然的。直方图就是其中之一。我们在探索阶段、在选择模型之前验证数据分布类型期间以及许多其他事情中使用它们进行可视化(有时甚至没有意识到)。不幸的是,对于大多数库来说,对实时数据使用直方图是不可能的。

人们通常对像 CSV 数据集这样的有界数据使用直方图。但是计算直方图的传统方法不适用于无界/流数据。这是因为算法需要遍历数据集的所有元素。在本文中,您将学习如何动态计算和更新直方图。实时发出的数据。

这里的实际例子是监视一段时间内系统的 CPU 使用情况。我们将实时计算并绘制 CPU 使用率的直方图,可能是在无限长的时间内。不过,正如您将看到的,这需要很少的内存。

为此,我们将在一个笔记本中使用三个 python 包:

  • psutil 检索机器的 CPU 使用率
  • 散景绘制直方图并更新
  • 制作计算直方图

让我们首先用 pip 安装它们:

pip install psutil, bokeh, makinage

对于本文的所有代码,我们需要几个导入:

from collections import namedtuple
import time
from datetime import datetime
import ipywidgets as widgets
from bokeh.plotting import figure, ColumnDataSource
from bokeh.io import output_notebook, push_notebook, show

import psutil
import rx
import rx.operators as ops
import rxsci as rs

生成 CPU 使用流

我们首先需要生成要分析的数据。我们将创建包含时间戳和每 100 毫秒测量的 CPU 使用率的项目。我们首先为这些项目定义一个数据结构:

CpuMeasure = namedtuple ( "CpuMeasure" , [ 'timestamp' , 'value' ])

然后我们可以将这些项目作为数据流发出。我们为此使用制造方法。Maki-Nage 是一个基于react vex的流处理框架。为了生成源流,我们直接使用 ReactiveX:

def create_cpu_observable(period=.1):
    return rx.timer(duetime=period, period=period).pipe(
        ops.map(lambda i: CpuMeasure(
            int(datetime.utcnow().timestamp()),
            psutil.cpu_percent()
        ))
    )

这个函数的结果是一个流对象。这就是所谓的可观测量。这个可观察对象每 100 毫秒(0.1 秒)发出一个 CpuMeasure 对象。

绘制和更新条形图

下一步是准备实时图。我们想要绘制一个已经计算过的直方图,所以我们需要一个柱状图。所以我们在这里使用一个散景窗口小部件。这个数字是刚刚初始化的,暂时没有数据。

source_cpu_total = ColumnDataSource(
    data={'edges': [], 'values': []}
)

p_cpu_total = figure(
    title="CPU usage total distribution",
    plot_width=500, plot_height=150
)
p_cpu_total.vbar(
    x='edges', top='values', width=1.0,
    source=source_cpu_total
)

outw = widgets.Output()
display(outw)

with outw:
    h_cpu_total = show(p_cpu_total, notebook_handle=True)

稍后,我们将通过将 source_cpu_total 对象设置为其他值来更新绘图。边缘字段对应于直方图仓,字段对应于每个仓中的项目数。

我们可以将图形更新步骤封装在一个专用函数中:

def update_histogram(graph_id, source, histogram):
    edges, values = zip(*histogram)
    source.data = {
        'edges': edges,
        'values': values,
    }
    push_notebook(handle=graph_id)

这里 graph_id 是散景图对象, source 是散景数据源, histogram 是预先计算的直方图。

我们现在可以通过使用假值来测试这段代码:

update_histogram(
    h_cpu_total, source_cpu_total,
    [(5, 3), (7, 12), (12, 5), (23, 3), (50, 17)]
)

结果看起来像这样:

计算直方图

既然数据源和图表已经可用,我们就可以计算实际的直方图了。Maki Nage 实现了 Ben-Haim 等人在论文A Streaming Parallel Decision Tree Algorithm中定义的分布式压缩算法。这也是在 Apache Hivehistogram _ numeric函数上实现的算法。

该算法的原理是将数据分布压缩为动态直方图:当新数据进入时,该直方图的条块边缘被动态调整,但是条块的数量在创建时被设置。从这个压缩的表示中,可以非常精确地计算不同度量的近似值:

  • 意思是
  • 方差,标准偏差
  • 分位数
  • 柱状图

由于压缩分布的大小是固定的,所以它非常节省内存,并且完全独立于分布的基数:无论输入数据集的大小如何,您只需要 100 个字节来压缩分布!

为了填充压缩的分布,我们使用了 math.dist.update 操作符。

虽然这种压缩表示是直方图,但它不能直接用作可显示的直方图:每个条柱都有唯一的宽度,并且条柱的数量可能比您想要显示的要多得多。所以我们使用另一个操作符从压缩的分布中计算直方图: math.dist.histogram

整个计算和显示由 10 行代码组成:

create_cpu_observable().pipe(
    rs.state.with_memory_store(pipeline=rx.pipe(
        rs.ops.map(lambda i: i.value),
        rs.math.dist.update(),
        rs.math.dist.histogram(bin_count=20),
        rs.ops.map(lambda i: (h_cpu_total, source_cpu_total, i))
    )),
).subscribe(
    on_next=lambda i: update_histogram(*i),
)

让我们分解这些线。

首先,我们创建 CPU 使用率的可观察值。然后,我们对其应用操作管道。这些是按顺序应用于每个项目的数据转换。有状态操作需要状态存储。这是存储所有中间计算的位置。我们用 with_memory_store 操作符进行配置。

下一步是提取每个项目的价值。在第一个例子中,我们不需要时间戳,所以我们通过映射操作符来处理字段。

接下来的两个步骤包括更新分布和为每个输入项目计算 20 个箱的直方图。

最后,用绘制直方图所需的参数创建一个元组。在 on_next 回调中,直方图被绘制为该管道的接收器。

运行此代码会显示 CPU 利用率的实时直方图:

双重监控

现在,如果我们想从一开始就监控我们的系统,并查看其最近的状态(如最近 3 分钟),该怎么办?我们可以通过计算两个分布来做到这一点:一个是全局分布,另一个是最近 3 分钟内的分布。

为此,我们首先将计算转移到一个专用函数,以便我们可以重用它:

def compute_histogram(graph_id, source):
    return rx.pipe(
        rs.ops.map(lambda i: i.value),
        rs.math.dist.update(),
        rs.math.dist.histogram(bin_count=20),
        rs.ops.map(lambda i: (graph_id, source, i))
    )

那么我们必须并行计算这两个直方图。我们在这里使用了两个以上的操作符。

第一个是 tee_map 操作符。该操作符将一个源可观测数据转发给几个管道,以便几个计算并行发生。

第二个是拆分操作符。它将源可观测性分割为 3 分钟的窗口。这允许每三分钟重置一次直方图,并且只显示最近的数据。

disposable = create_cpu_observable().pipe(
    rs.state.with_memory_store(pipeline=rx.pipe(
        rs.ops.tee_map(
            # unbounded distribution
            compute_histogram(h_cpu_total, source_cpu_total),
            # bounded distribution of 3 minutes
            rs.data.split(
                predicate=lambda i:
                    i.timestamp - (i.timestamp % 180),
                pipeline=compute_histogram(
                    h_cpu_recent, source_cpu_recent
                ),
            ),
            join="merge",
        )
    )),
).subscribe(
    on_next=lambda i: update_histogram(*i),
)

随着图形创建的更新,这将实时更新两个直方图。

这些例子的全部代码可在这里获得。

更进一步

这里提出的算法也适用于分布式系统:分布的压缩表示是可合并的。当您将一个计算分配给几个工作人员时,这尤其有用。人们可以通过合并所有部分表示来聚集最终结果。

也可以在任何 python 环境中使用该算法。Maki Nage 实现在一个名为 distogram 的专用包中,并且没有依赖性。

最后,如果您想了解更多关于流处理的知识,您可以阅读我在 Maki-Nage 上写的另一篇介绍文章。

原载KDnuggets

基于 VkFFT 库的 GPU 实时图像配准

原文:https://towardsdatascience.com/real-time-image-registration-on-gpu-with-vkfft-library-c4e47f8050a0?source=collection_archive---------35-----------------------

如何用快速傅立叶变换检测图像的平移、旋转和缩放

文章中所有图片均由作者根据赛博朋克 2077 游戏截图制作

图像配准问题概述

图像配准是计算机视觉和图像处理中的一个重要问题。如果我们有同一个物体的两个图像,这个问题可以表述为:我们如何确定一个坐标系变换,使我们能够匹配显示的物体?

有多种方法可供选择。基于相关性的方法通过比较像素值来匹配整个图像或子图像。基于特征的方法检测和匹配在两个图片上表示对象的相同部分的控制点。本文介绍了一种相关方法的 GPU 实现,该方法在快速傅立叶变换后在频域中操作,在论文[1]中提出。通过使用 GPU 开发的最新进展和定制的高度优化的 FFT 库[2],有可能将匹配所需的时间从几分钟减少到几毫秒,从而实现算法的实时使用。

基于 FFT 的图像识别是如何工作的?

傅立叶域中的操作允许利用它的许多奇妙特性。例如,让我们定义两个图像,其中一个是另一个的简单平移(x0,y0):

在傅立叶域中,这个关系看起来像:

这一事实被称为傅立叶变换定理,允许计算两幅图像的交叉功率谱,在每一点定义为:

这里*表示复共轭。如果我们对此频谱进行逆 FFT,我们将在位置(x0,y0)获得一个单一峰值,这可以通过应用最大化程序轻松确定。上述方法也称为相位相关法。

文献[1]提出了这种方法的扩展,以涵盖旋转和缩放变换。它基于傅立叶变换的另一个特性——傅立叶变换的幅度是平移不变的:

现在让我们定义两个图像,其中一个是另一个图像的平移(x₀,y₀)、旋转(θ₀)和缩放因子 s 的组合:

如果我们现在切换到极坐标:

图像傅立叶变换的幅度可以改写为:

如果我们将对数变换应用于ρ:

我们得到对数极坐标系统中的量值的以下表示:

可以看出,在这个坐标系中,通过θ₀旋转和通过因子 s 缩放变成了简单的平移。因此,应用已经定义的相位相关过程,我们可以确定这两个参数。预先从幅度切换到幅度的对数也是有利的。

如果我们以更高的分辨率旋转和缩放图像,我们可以用另一个相位相关步骤来确定平移。注意:这种方法不能区分θ₀角和 180° +θ₀.为此,我们简单地用 180°+θ₀角进行另一步平移匹配,并选择具有最高峰值的角度。

这种方法目前的局限性是缩放在两个轴上都是一致的,并且图像必须是正方形的(或者填充到正方形)。

GPU 能带来什么?

GPU 在计算能力、数据传输速度和可用内存大小方面的最新进展使其对通用用途极具吸引力,扩展了其原始渲染和可视化目标任务。GPU 中使用的单指令-多线程(SIMT)设计方法已被证明在数据级并行任务中极其有效。其中一个任务是多维 FFT,它是相位相关方法的核心。

这个项目是通过 Vulkan API 的方式实现的(与 Nvidia 的 CUDA 相反,后者通常用于数据科学)。它大量使用了vkft 库(也是作者开发的)。VkFFT 是 Vulkan 中的一个开源的跨平台快速傅立叶变换库,性能优于专有的 Nvidia 的 cuFFT 库。

由于不依赖于专有的黑盒解决方案,优化应用程序布局的每一个小部分都是可能的,这将在下一节讨论。

算法的 GPU 实现

平移、旋转和缩放检测的完整算法可以总结为以下命令列表:

  1. 两幅图像的正向 FFT
  2. 对数量值到对数极坐标系统的转换
  3. 两个图像的对数幅度的对数极坐标表示的正向 FFT
  4. 互功率谱计算
  5. 对数极坐标互功率谱的逆 FFT
  6. 最大化减少例程,以确定角度和比例因子
  7. 具有最高分辨率的图像的旋转和缩放
  8. 旋转图像的 FFT
  9. 互功率谱计算
  10. 互功率谱的逆 FFT
  11. 最大化器减少例程以确定翻译
  12. 步骤 7–11,180°+θ₀角,解决角度的 180°不确定性
  13. 输入图像的最终旋转、缩放和平移

操作总量:图像大小的 6 倍 FFT 和对数极坐标系统大小的 3 倍 FFT,3 倍交叉幂计算,3 倍最大化器减少例行启动,3 倍旋转/缩放/平移转换合并在一次操作中。所有操作都在三个颜色通道上执行。也可以在对数极坐标系统中使用不同的系统尺寸。

我们来测试一下算法性能。测试将包括四个部分。首先,我们将分别测试平移、旋转和缩放,以确定正确检测的范围。然后,我们将继续进行所有的转换,并结合额外的外部过滤器应用于输入图像。

测试将在 Nvidia 1660Ti GPU 上进行。对数极坐标系统大小设置为 512 px 512 px。参考图片是游戏《赛博朋克 2077》中一个 1024 像素 x 1024px 像素的截图:

平移变换

该测试检查图像可以沿对角线移动多远的范围,以便仍然与参考匹配。rspeak 和 tpeak 是 maximizer reduce 例程检测到的峰值,如果我们将参考图像与其自身进行匹配,则它们等于 1.0。可以看出,基于 FFT 的图像识别可以精确匹配翻译系统,直到重叠小到图像大小的 30%。

GPU 执行所有 13 个匹配步骤所需的时间约为 3.3 毫秒。偏差主要受旋转/缩放/平移例程的影响,因为将颜色设置为黑色(针对缺失数据)不需要额外的内存传输。

GPU 上的 FFT 是带宽受限的问题。这使得所有旨在减少从 GPU 内存转移到芯片的内存量的优化变得非常重要。图像 FFT 的 6 倍步长是利用 VkFFT 中实现的实数到复数优化来执行的,这将存储图像所需的内存减少了一半。FFT 以单精度完成。每个 FFT 仍然需要两次上传到芯片,两次从芯片下载到 GPU 存储器,因为我们有两个轴。我们还有三种颜色,分别处理。每个图像 FFT 在芯片和 VRAM 之间传输的内存总量等于 50MB。对数极坐标 FFT 的分辨率较小,需要 24MB 的存储器传输。传输的总内存为 6x50+3x24=372MB。1660Ti 的理论带宽是 288GB/s,如果只按这个速率传输数据,至少需要 1.26ms。的确,如果我们只用 VkFFT 做 FFT(不考虑算法的其他部分),总用时会是~1.70ms,接近得到的理论值。

通过使用 Vulkan 中的子组功能,可以有效地进行缩减。减少所需的内存传输量接近系统大小(我们通过比较 1024 个线程存储的 1024 个数字来减少,因此在每次启动例程时,比较的数字量会减少 1024 倍)。传输的内存总量约为 32MB,以理论带宽传输需要 0.11 毫秒。测量的计时显示,计算算法中的所有缩减需要 0.14 毫秒。

计算一个互功率谱需要两次系统上传(带颜色)和一次从芯片下载(不带颜色)。该算法有两个这样的计算来确定平移,一个用于对数功率谱。这总共需要 75MB,可以在 0.25ms 内完成。测量的时间表明,在该算法中计算互功率谱需要 0.36ms。

对数极坐标系统转换的计算也需要两次上传系统(带颜色)和两次下载(带颜色)。这总共需要 50MB,可以在 0.17ms 内完成。测得的计时显示,在算法中计算转换需要 0.22ms。

旋转/缩放/平移例程占用剩余的时间(0.5-0.9 毫秒),并使用双线性插值。这个例程没有进行优化以最小化内存传输和合并,因此它的时间在不同的角度和规模之间会有很大的不同,正如在接下来的测试中可以看到的那样。

总的来说,中程 GPU 上的 3 毫秒范围适用于现实生活中的图像处理,并有望与 GPU 的带宽成线性比例关系。关于上一代 AMD GPUs 的 L3 缓存,还有一点值得注意。整个系统可以在执行期间存储在那里,充分利用其速度(这在 VkFFT 基准测试中得到验证)。

旋转变换

该测试检查算法是否可以检测 0°到 360°范围内的所有角度。可以看出,基于 FFT 的图像识别可以检测整个范围内的旋转。在这里,未优化的双线性插值甚至比 FFT 需要更多的时间,这表明优化对 GPU 编程是多么重要。

缩放变换

该测试检查算法可以检测的缩放变换的范围。可以看出,基于 FFT 的图像识别可以在参考图像的 0.4x 到 3.2x 的大范围内检测缩放。时间上的差异再一次被解释为,将颜色设置为黑色(对于丢失的数据)不需要额外的内存传输。

平移+旋转+缩放+过滤

该测试通过应用除外部过滤器之外的所有支持的检测转换,将算法推向极限,使图像失真。它将用于确定 rspeak 和 tpeak 的有效条件。

首先,在没有滤镜的情况下,图像经历了以下变换:对角平移 200px(左上方向),然后围绕中心顺时针旋转 30 °,缩放因子为 1.6。

然后应用以下测试:

  1. 高斯噪声—高达图像的 50%
  2. 高斯模糊—直到 4px 模糊
  3. 灰尘和刮痕滤镜(不同的模糊滤镜)—工作到 5px 模糊
  4. 波纹滤镜(以类似波浪的方式扭曲图像)—工作到 200%
  5. 旧图像过滤器(更改调色板)

可以看出,基于 FFT 的图像配准提供了非常好的噪声和模糊鲁棒性。测试还表明,该算法是成功还是失败的一个好的度量可以是 tpeak 与 180°旋转图像的 tpeak 的比较,用于消除角度的 180°不确定性。在所有成功的测试中,正确的 tpeak 至少比另一个值大两倍(通常大一个数量级),而在所有失败的测试中,它们的值是相当的。

结论

本文提出了一种基于 FFT 的图像配准算法(在论文[1]中首次提出)的 GPU 实现,该算法可以匹配平移、旋转和缩放的图像。该算法对噪声和模糊具有鲁棒性,可以在中程 GPU 上在 3 毫秒内执行两个 1024px x 1024px 图像的匹配,这允许实时使用。该算法是在 Vulkan API 的帮助下编写的,不依赖于专有软件。它使用 VkFFT 库[2]作为核心模块。Vulkan API 支持的平台非常广泛,使得算法实现可以跨平台。

配准精度已经在许多不同的变换和滤波器上进行了测试。到目前为止,该算法实现支持重叠小至图像大小的 30%的平移、完整的 360°旋转范围以及从参考图像大小的 0.4 到 3.2 倍的缩放。

一种基于 tpeak 比较确定结果有效性的新方法在各种过滤器上进行了测试。

通过应用不同的频率滤波器、开窗、使用子像素检测技术、不同的插值技术以及对数极坐标转换的进一步实验,可以进一步提高基于 FFT 的图像配准的精度。

参考

[1] B. Srinivasa Reddy 和 B. N. Chatterji,一种基于 FFT 的平移、旋转和尺度不变图像配准技术 (1996),IEEE 图像处理汇刊。5 (8): 1266–1271

[2] D .托尔马切夫, VkFFT — Vulkan 快速傅立叶变换库 (2020),GitHub

使用 5 行代码的实时图像分割

原文:https://towardsdatascience.com/real-time-image-segmentation-using-5-lines-of-code-7c480abdb835?source=collection_archive---------3-----------------------

实践教程

使用 PixelLib 在图像和视频中执行准确快速的对象分割

原始视频来源

目录

  1. 图像分割在计算机视觉中的应用
  2. 图像分割
  3. 批量图像分割
  4. 视频分割
  5. 摄像机画面分割

图像分割在计算机视觉中的应用

计算机视觉是计算机看到并分析他们所看到的东西的能力。图像分割是计算机视觉的一个方面,它处理将计算机可视化的对象的内容分割成不同的类别以便更好地分析。图像分割过程的一个很好的例子是图像中对象的前景-背景分离,这是一种将对象从其背景中分离出来以分析对象及其背景的技术。图像分割实现前景和背景分离的能力使其在解决许多计算机视觉问题中具有不可估量的价值,例如医学图像分析、背景编辑、自动驾驶汽车中的视觉和卫星图像分析。

PixelLib 库是一个使用几行 python 代码就可以轻松集成图像和视频中的对象分割的库。它支持许多惊人的功能,例如:

  1. 图像和视频中对象的语义和实例分割。
  2. 细分模型的定制训练。
  3. 图像和视频中的背景编辑。
  4. 图像和视频中物体的提取。

对实时图像分割应用的需求

计算机视觉中最大的挑战之一是在实时应用的准确性和速度性能之间保持平衡。在计算机视觉领域,存在计算机视觉解决方案要么更精确和更慢,要么不太精确和更快的两难境地。之前版本的 PixelLib 使用 tensorflow 深度学习库作为其后端,该库采用 Mask R-CNN 来执行实例分割。Mask R-CNN 是一个很好的架构,但是无法在实时应用的准确性和速度性能之间取得平衡。我太兴奋了😊宣布 PixelLib 现在支持 pytorch 后端使用point rende分割架构对图像和视频中的对象进行更快、更准确的分割和提取。 PointRend亚历山大·基里洛夫等人提出的对象分割架构用于代替 Mask R-CNN 进行对象的实例分割。 PointRend 是一个用于实现对象分割的优秀的艺术级神经网络。它生成精确的分割模板,并以高推理速度运行,以满足日益增长的对精确和实时计算机视觉应用的需求。

多操作系统支持

PixelLib 是一个为不同操作系统提供支持的库。我将 PixelLib 与 python 实现的point rend by detectron 2集成,后者只支持 Linux OS。

我对最初的 Detectron2 PointRend 实现进行了修改,以支持 Windows 操作系统。我很高兴告诉大家一个好消息,用于 PixelLib 的 PointRend 实现支持 Linux 和 Windows 操作系统。

**

面具 R-CNN vs 点趋势 ( 来源)

**

面具 R-CNN vs 点趋势 ( 来源)

上面的样本图像是 点趋势 的分割结果与掩模 RCNN 相比的差异的例子。很明显,右边的 点趋势 图像结果与左边的掩模 R-CNN 结果相比是更好的分割输出。

注意:本文基于使用 pytorch 和 PointRend 执行实例分割。如果您想了解如何使用 tensorflow 和 Mask R-CNN 执行实例分割,请阅读下面的这篇文章:

* [## 用 5 行代码实现图像分割

towardsdatascience.com](/image-segmentation-with-six-lines-0f-code-acb870a462e8)

下载 Python

PixelLib pytorch 版本支持 python 版本 3.7 及以上。下载兼容的 python 版本。

https://www.python.org/

安装 PixelLib 及其依赖项

安装 Pytorch

PixelLib Pytorch 版本支持 Pytorch 的这些版本(、T21【1 . 6 . 0、1.7.1、1.8.0 和 1.9.0 、T23)。

注意:Pytorch1 . 7 . 0不受支持,请勿使用任何低于 1.6.0 的 py torch 版本。安装兼容的 Pytorch 版本。

https://pytorch.org/

安装 Pycocotools

  • pip3 安装 pycocotools

安装 PixelLib

  • pip3 安装 pixellib

如果已安装,使用升级至最新版本:

  • pip3 安装 pixellib —升级

图象分割法

PixelLib 使用五行 python 代码,用 PointRend 模型在图像和视频中进行对象分割。下载 点趋势模型 。这是图像分割的代码。

第 1–4 行: PixelLib 包被导入,我们还从模块PixelLib . torch backend . instance中导入了类instance segmentation(从 pytorch 支持中导入实例分割类)。我们创建了该类的一个实例,最后加载了我们已经下载的point rende模型。

第 5 行:我们调用函数 segmentImage 对图像中的物体进行分割,并为函数添加了以下参数:

  • image_path: 这是待分割图像的路径。
  • show_bbox: 这是一个可选参数,用边界框显示分割结果。
  • 输出图像名称: 这是保存的分割图像的名称。

用于分割的样本图像

来源

*ins.segmentImage("image.jpg",output_image_name="output.jpg")*

分割后的图像

*The checkpoint state_dict contains keys that are not used by the model: proposal_generator.anchor_generator.cell_anchors.{0, 1, 2, 3, 4}*

如果您运行分段代码,上面的日志可能会出现!这不是一个错误,代码将正常工作!

获取分割结果

*results, output = ins.segmentImage("image.jpg", show_bboxes=True, output_image_name="result.jpg")
print(results)* 

分割结果返回一个字典,该字典具有许多与图像中被分割的对象相关联的值。打印的结果将采用以下格式:

*{'boxes':  array([[ 579,  462, 1105,  704],
       [   1,  486,  321,  734],
       [ 321,  371,  423,  742],
       [ 436,  369,  565,  788],
       [ 191,  397,  270,  532],
       [1138,  357, 1197,  482],
       [ 877,  382,  969,  477],),'class_ids': array([ 2,  2,  0,  0,  0,  0,  0,  2,  0,  0,  0,  0,  2, 24, 24,2,  2,2,  0,  0,  0,  0,  0,  0], dtype=int64), 'class_names': ['car', 'car', 'person', 'person', 'person', 'person', 'person', 'car', 'person', 'person', 'person', 'person', 'car', 'backpack', 'backpack', 'car', 'car', 'car', 'person', 'person', 'person', 'person', 'person', 'person'], 'object_counts': Counter({'person': 15, 'car': 7, 'backpack': 2}), 'scores': array([100., 100., 100., 100.,  99.,  99.,  98.,  98.,  97.,  96.,  95.,95.,  95.,  95.,  94.,  94.,  93.,  91.,  90.,  88.,  82.,  72.,69.,  66.], dtype=float32), 'masks': array([[[False, False, False, ..., False, False, False],[False, False, False, ..., False, False, False],'extracted_objects': []*

上面打印的结果值包括:

*{'boxes': array([[5.790e+02, 4.620e+02, 1.105e+03, 7.050e+02],[1.000e+00, 4.870e+02, 3.220e+02, 7.340e+02],[1.142e+03, 3.640e+02, 1.161e+03, 4.060e+02]], dtype=float32),*

框:这些是被分割对象的边界框坐标。我没有显示所有盒子的坐标,这是因为列表太长了。

*'class_ids': array([ 2,  2,  0,  0,  0,  0,  0,  2,  0,  0,  0,  0,  2, 24, 24,2,  2,2,  0,  0,  0,  0,  0,  0], dtype=int64),*

类标识:这些是被分割对象的类标识。

*'class_names': ['car', 'car', 'person', 'person', 'person', 'person', 'person', 'car', 'person', 'person', 'person', 'person', 'car', 'backpack', 'backpack', 'car', 'car', 'car', 'person', 'person', 'person', 'person', 'person', 'person'],*

类名:这些是被分割对象的类名。

*'object_counts': Counter({'person': 15, 'car': 7, 'backpack': 2}),*

object_counts: 这些是图像中分割的每个类别的计数。我使用了 python 内置的计数器来计数对象。在这种情况下,图像中分割出了 15 个人7 辆汽车* 和 2 个背包 。*

*'scores': array([100., 100., 100., 100.,  99.,  99.,  98.,  98.,  97.,  96.,  95.,95.,  95.,  95.,  94.,  94.,  93.,  91.,  90.,  88.,  82.,  72.,69.,  66.], dtype=float32),*

分数:这些是每个被分割对象的置信度分数。

*'masks': array([[[False, False, False, ..., False, False, False],[False, False, False, ..., False, False, False],*

遮罩:这些是每个被分割对象的遮罩值。我没有显示所有的掩码值,这是因为列表太长了。

注:返回的掩码默认值以 bolean 为单位。通过使用新的参数 mask_points_values,可以获得掩模的坐标。

*ins.segmentImage("sample.jpg", show_bboxes=True, mask_points_values=True,  output_image_name="output.jpg")*

mask _ points _ values参数被添加到 segmentImage 函数并设置为 True ,新的遮罩值将为:

*[[array([[295, 497]])
  array([[422, 114], 
       [421, 115],   
       [417, 115],
       ...,
       [436, 115],
       [433, 115],
       [432, 114]])]]*

extracted_objects: 如果我们提取对象,这是提取对象值的容器列表。它是空的,因为我们没有提取任何东西。我们将在本文后面讨论如何提取这些分割对象。

*results, output = ins.segmentImage("image.jpg", show_bboxes=True, output_image_name="result.jpg")*

访问由分割结果呈现的值

包围盒坐标值

*results["boxes"]*

类 id 值

*results["class_ids"]*

类名值

*results["class_names"]*

对象计数值

*results["object_counts"]*

屏蔽值

*results["masks"]*

检测阈值

PixelLib 使得确定对象分割的检测阈值成为可能。

*ins.load_model("pointrend_resnet50.pkl", confidence = 0.3)*

置信度:这是 load_model 函数中引入的一个新参数,设置为 0.3 来阈值检测 30% 。我为检测阈值设置的默认值是 0.5 ,可以使用 置信度 参数增加或减少。

速度记录

PixelLib 使实时对象分割成为可能,并增加了调整推理速度以适应实时预测的能力。使用 4GB 容量的 Nvidia GPU 处理单幅图像的默认推理速度约为 0.26 秒

速度调整:

PixelLib 支持速度调节,有两种速度调节模式,分别是快速 模式:

1 快速模式

**ins.load_model("pointrend_resnet50.pkl", detection_speed = "fast")**

load _ model函数中,我们添加了参数detection _ speed并将值设置为 fast快速 模式处理单幅图像达到 0.20 秒**

快速模式检测的完整代码

2 快速模式

**ins.load_model("pointrend_resnet50.pkl", detection_speed = "fast")**

load_model 函数中,我们添加了参数detection _ speed,并将值设置为 rapid快速 模式处理单幅图像达到0.15 秒。**

快速模式检测的完整代码

点趋势模型

用于对象分割的点趋势模型有两种,分别是 resnet50 变体resnet101 变体 。本文通篇使用的是 resnet50 变体 ,因为它速度更快,准确性更好。 resnet101 变型resnet50 变型 更精确但速度慢。根据 Detectron2 上的官方报道车型 resnet50 变种 在 COCO 上实现 38.3 贴图resnet 101 变种* 在 COCO 上实现 40.1 贴图 。*****

**resnet 101 的速度记录:默认速度为 0.5 秒快速 模式为 0.3 秒快速 模式为 0.25 秒

resnet 101 变体的代码

**ins.load_model("pointrend_resnet101.pkl", network_backbone = "resnet101")**

resnet101 模型执行推理的代码是一样的,除了我们在 load_mode l 函数中加载了point rendresnet 101 模型 之外。从这里下载 resnet101 型号。我们还增加了一个额外的参数network _ backbone,并将值设置为 resnet101

注:如果想达到高推理速度和良好的准确性,使用point rende resnet 50 变体 ,但如果更在意准确性,则使用point rende resnet 101 变体 。所有这些推断报告都是基于使用 4GB 容量的 Nvidia GPU。**

图像分割中的自定义对象检测

使用的 PointRend 模型是一个预训练的 COCO 模型,支持 80 类对象。PixelLib 支持自定义对象检测,这使得过滤检测和确保目标对象的分割成为可能。我们可以从支持的 80 类对象中进行选择,以匹配我们的目标。这些是 80 类受支持的对象:

**person, bicycle, car, motorcycle, airplane,bus, train, truck, boat, traffic_light, fire_hydrant, stop_sign,parking_meter, bench, bird, cat, dog, horse, sheep, cow, elephant, bear, zebra,giraffe, backpack, umbrella, handbag, tie, suitcase, frisbee, skis, snowboard,sports_ball, kite, baseball_bat, baseball_glove, skateboard, surfboard, tennis_racket,bottle, wine_glass, cup, fork, knife, spoon, bowl, banana, apple, sandwich, orange,broccoli, carrot, hot_dog, pizza, donut, cake, chair, couch, potted_plant, bed,dining_table, toilet, tv, laptop, mouse, remote, keyboard, cell_phone, microwave,oven, toaster, sink, refrigerator, book, clock, vase, scissors, teddy_bear, hair_dryer,toothbrush.**

我们想要过滤我们的样本图像的检测,以便只检测图像中的

**target_classes = ins.select_target_classes(person = True)ins.segmentImage("image.jpg", segment_target_classes = target_classes, output_image_name="output.jpg")**

调用函数select _ target _ classes选择要分割的目标对象。函数 segmentImage 获得了一个新参数segment _ target _ classes目标类 中选择,并根据它们过滤检测。

太棒了!!!PixelLib 只检测图像中出现的人。

自定义对象检测的完整代码

图像中的目标提取

PixelLib 使得提取和分析图像中分割的对象成为可能。

对象提取代码

用于图像分割的代码是相同的,除了我们在segment image函数中添加了额外的参数来执行对象提取。

**ins.segmentImage("image.jpg", extract_segmented_objects = True, save_extracted_objects = True, output_image_name="output.jpg")**
  • ****extract _ segmented _ objects:这是处理分段对象提取的参数。使用以下方式访问提取的对象值:
**results, output = ins.segmentImage("image.jpg", show_bboxes=True, output_image_name="result.jpg")#access the extracted objects from the results
results["extracted_objects"]**
  • save _ extracted _ objects:这是将每个提取的对象保存为图像的参数。每个被分割的对象将被保存为segmented _ object _ index例如segmented _ object _ 1。对象是根据提取的顺序保存的。**
**segmented_object_1.jpg
segmented_object_2.jpg
segmented_object_3.jpg
segmented_object_4.jpg
segmented_object_5.jpg
segmented_object_6.jpg**

******

从遮罩坐标中提取物体**

****注意:图像中的所有物体都被提取出来,我选择只显示其中的两个。

从包围盒坐标中提取

默认提取方法从遮罩的坐标中提取对象。提取只给我们关于物体本身的信息,而排除了它的周围环境。考虑一个问题,其中我们想要分析对象和它在图像中所处的区域。PixelLib 能够通过使用边界框的坐标提取分割的对象及其在图像中的远程位置来解决这个问题。

修改了提取代码

**ins.segmentImage("image.jpg", extract_segmented_objects = True, extract_from_box = True, save_extracted_objects = True, output_image_name="output.jpg")**

我们引入了一个新的参数 extract_from_box 来提取从包围盒坐标中分割出来的对象。每个提取的对象将被保存为object _ extract _ index例如object _ extract _ 1。对象是根据提取的顺序保存的。

**object_extract1.jpg
object_extract2.jpg
object_extract3.jpg
object_extract4.jpg
object_extract5.jpg
object_extract6.jpg**

******

从边界框坐标中提取**

使用边界框坐标提取对象的完整代码

****图像分割输出可视化

PixelLib 可以根据图像的分辨率来调节图像的可视化。

**ins.segmentImage("sample.jpg", show_bboxes=True, output_image_name= "output.jpg")**

来源

可视化效果不可见,因为文本大小 和框粗细太细。我们可以调整文本大小、粗细和框的粗细来调整可视化效果。

为了更好的可视化而进行的修改

**ins.segmentImage(“sample.jpg”, show_bboxes=True, text_size=5, text_thickness=4, box_thickness=10, output_image_name=”output.jpg”)**

segmentImage 函数接受了调节文本和边界框厚度的新参数。

  • text_size: 默认的文字尺寸是 0.6 ,中等分辨率的图片也可以。对于高分辨率的图像来说,它太小了。我把数值增加到 5
  • 文本 _ 粗细: 默认的文本粗细是 1 。我把它增加到 4 来匹配图像分辨率。
  • box _ thickness:默认的盒子厚度是 2 我把它改成了 10 以匹配图像分辨率。

输出视觉效果更好的图像

注: 根据您的图像分辨率调节参数。如果您的图像分辨率较低,我对这个分辨率为 5760 x 3840 的样本图像使用的值可能会太大。如果您有分辨率非常高的图像,您可以增加参数值,使其超过我在这个示例代码中设置的值。text _ thicknessbox _ thickness参数的值必须是整数,不能用浮点数表示。 text_size 值可以用整数和浮点数表示。

批量图像分割

PixelLib 可以对同一文件夹中的一批图像进行预测。

批量分段代码

**ins.segmentBatch("inputfolder",  show_bboxes=True, output_folder_name = "outputfolder")**

批量分割的代码非常类似于单个图像分割,除了我们用 segmentBatch 函数替换了 segmentImage 函数。我们将以下参数添加到 段批次:

  • folder_path: 这是包含我们要分割的图像的文件夹。
  • ****输出文件夹名称:这是我们将保存所有分割图像的文件夹名称。

样本文件夹结构

**--input_folder
    --test1.jpg
    --test2.jpg
    --test3.jpg

--output_folder 
    --test1.jpg
    --test2.jpg
    --test3.jpg**

批量图像分割对象提取代码

**ins.segmentBatch("inputfolder", show_bboxes=True, extract_segmented_objects=True, save_extracted_objects=True, output_folder_name="outputfolder")**

我们在 segmentBatch 函数中添加了extract _ segmented _ objectssave_extracted_objects 参数,分别对提取和保存提取的对象。输入文件夹中每幅图像的提取对象将保存在一个单独的文件夹中,其名称为imagename _ extracts例如,如果图像名称为这意味着提取的对象将保存在一个名为test1 _ extracts的文件夹中。**

注意:**提取对象的文件夹在图像的相同输入文件夹中创建。样本文件夹结构

**--input_folder
    --test1.jpg
    --test1_extracts     --test2.jpg
    --test2_extracts     --test3.jpg
    --test3_extracts--output_folder   
    --test1.jpg
    --test2.jpg
    --test3.jpg**

从边界框坐标中提取对象的代码

**ins.segmentBatch("inputfolder", show_bboxes=True, extract_segmented_objects=True, extract_from_box=True,save_extracted_objects=True, output_folder_name="outputfolder")**

我们在 segmentBatch 函数中添加了 extract_from_boxsave_extracted_objects 参数,分别对提取并保存提取的对象。

注意:**从边界框坐标中提取的对象的文件夹也在图像的相同输入文件夹中创建。样本文件夹结构

**--input_folder
    --test1.jpg
    --test1_extracts --test2.jpg
    --test2_extracts --test3.jpg
    --test3_extracts--output_folder   
    --test1.jpg
    --test2.jpg
    --test3.jpg**

批量图像分割中自定义对象分割的代码

**target_classes = ins.select_target_classes(person = True)
ins.segmentBatch("inputfolder", show_bboxes=True, segment_target_classes = target_classes, output_folder_name="outputfolder")**

我们调用函数 select_target_classes 来选择要分割的目标对象。函数 segmentBatch 函数获得了一个新参数segment _ target _ classes以从 目标类 中进行选择,并根据它们过滤检测。

批量图像分割中的快速模式检测代码

**ins.load_model(“pointrend_resnet50.pkl”, detection_speed = "fast")**

load _ model函数中,我们增加了参数detection _ speed并将值设置为 fast快速 模式处理单幅图像达到 0.20 秒**

批量图像分割快速模式检测代码

**ins.load_model(“pointrend_resnet50.pkl”, detection_speed = "rapid")**

我们将用于推断的检测速度设置为 快速 模式。

load _ model函数中,我们添加了参数detection _ speed并将值设置为 【快速】快速 模式处理单幅图像达到0.15 秒。****

注:除了 segmentImage 函数被 segmentBatch 替代之外,PixelLib 所有支持特性的批量图像分割的代码实现与单个图像分割相同。

视频和相机馈送中的对象分割

PixelLib 使实时摄像机输入和视频文件中的实时对象分割成为可能。

视频分割

用于视频分段的代码

第 1–4 行: PixelLib 包被导入,我们还从模块PixelLib . torch backend . instance中导入了类instance segmentation(从 pytorch 支持中导入实例分割类)。我们创建了该类的一个实例,最后加载了point rende模型。如果模型尚未下载,请从这里下载。****

第 5 行:我们调用函数process _ video对视频中的对象进行分割,该函数增加了以下参数:**

  • video_path: 这是要分割的视频的路径。
  • show_bboxes: 这是一个可选参数,用边界框显示结果中的分割对象。
  • ****每秒帧数:该参数将为保存的视频设置每秒帧数。
  • ****输出视频名称:这是输出分段视频的名称。
**ins.process_video("sample_video.mp4", show_bboxes=True, frames_per_second=3, output_video_name="output_video.mp4")**

视频中物体提取的代码

**ins.process_video(“sample_video.mp4”, show_bboxes=True,  extract_segmented_objectsframes_per_second=5, output_video_name=”output_video.mp4")**

process_video 函数新增了参数extract _ segmented _ objectssave _ extracted _ objects分别用于提取和保存分段对象。

**

提取的对象**

提取视频中的包围盒坐标

修改了提取代码

**ins.process_video(“sample.mp4”, show_bboxes=True, extract_segmented_objects=True, extract_from_box=True,save_extracted_objects=True, frames_per_second=5, output_video_name=”output_video.mp4")**
  • extract _ from _ box被添加到从包围盒坐标提取分割对象的功能中。

**

方框摘录**

自定义视频中的对象分割

PixelLib 可以在视频中执行自定义对象分割,以过滤未使用的检测和分割目标类别。

视频自定义检测代码

**target_classes = ins.select_target_classes(person = True, bicycle =True)ins.process_video("sample_video.mp4", show_bboxes=True, segment_target_classes = target_classes,frames_per_second=5, output_video_name="output_video.mp4")**

调用函数select _ target _ classes选择要分割的目标对象。函数process _ video得到了一个新的参数segment _ target _ classes目标类 中选择并根据它们过滤检测。

视频分割中的快速模式检测代码

**ins.load_model(“pointrend_resnet50.pkl”, detection_speed = "fast")**

我们将视频处理的检测速度设置为 快速 模式。

视频分割中的快速模式检测代码

**ins.load_model(“pointrend_resnet50.pkl”, detection_speed = "rapid")**

我们将视频处理的检测速度设置为 快速 模式。

实况摄像机馈送中的对象分割

PixelLib 为实时摄像机输入的实时分段提供了出色的支持。

现场摄像机画面分割代码

**import cv2 capture = cv2.VideoCapture(0)**

我们导入了 cv2 并包含了捕捉相机帧的代码。

**segment_video.process_camera(capture,  show_bboxes = True, frames_per_second= 5, check_fps=True, show_frames= True,frame_name= "frame", output_video_name="output_video.mp4")**

在执行分段的代码中,我们替换了要捕获的视频文件路径,也就是说,我们正在处理摄像机捕获的帧流。我们添加了额外的参数来显示相机的帧:

  • show_frames : 这是处理分段摄像机帧显示的参数。
  • frame_name: 这是显示的相机帧的名称。
  • check_fps: 该参数将在相机进给处理结束时每秒打印出帧数。
  • 这是一个可选参数,显示带有边界框的分段对象。
  • ****每秒帧数:该参数设置保存的视频文件每秒帧数。在这种情况下,它被设置为 5,即保存的视频文件每秒将有 5 帧。
  • ****输出视频名称:这是保存的分段视频的名称。

实时摄像处理的速度调整

默认速度模式达到 4fps快速 速度模式达到6 fps快速 速度模式达到 9fps 。这些报告基于使用 4GB 容量的 Nvidia GPU**

相机进给中的快速模式检测代码

**ins.load_model(“pointrend_resnet50.pkl”, detection_speed = "fast")**

我们将处理实况摄像机馈送的检测速度设置为 快速 模式,推断速度将为 6fps

快速模式检测代码

**ins.load_model(“pointrend_resnet50.pkl”, detection_speed = "rapid")**

我们将处理实况摄像机馈送的检测速度设置为 快速 模式,推断速度将为 9fps

现场摄像机馈送中的自定义对象分割代码

**target_classes = segment_video.select_target_classes(person=True)segment_video.process_camera(capture,  show_bboxes = True, frames_per_second= 5, segment_target_classes = target_classes,show_frames= True,frame_name= "frame", output_video_name="output_video.mp4")**

调用函数select _ target _ classes选择要分割的目标对象。函数process _ camera得到了一个新参数segment _ target _ classes目标类 中选择并根据它们过滤检测。

现场摄像机输入中的对象提取代码

**segment_video.process_camera(capture,  show_bboxes = True, frames_per_second= 5, extract_segmented_objects=True, save_extracted_objects=True,show_frames= True,frame_name= "frame", output_video_name="output_video.mp4")**

process _ camera函数有新的参数extract _ segmented _ objectssave _ extracted _ objects来分别提取和保存分段对象。

  • extract _ from _ box被添加到从包围盒坐标提取分割对象的功能中。

从现场摄像机输入的框坐标中提取目标对象的代码

**segment_video.process_camera(capture,  show_bboxes = True, frames_per_second= 5, extract_segmented_objects=True, extract_from_box=True,save_extracted_objects=True, show_frames= True,frame_name= "frame", output_video_name="output_video.mp4")**
  • extract _ from _ box被添加到从包围盒坐标提取分割对象的功能中。

我们在本文中详细讨论了如何使用 PixelLib Pytorch 版本在图像和实时摄像机馈送中执行准确快速的图像分割和对象提取。我们还描述了使用 PointRend 网络架构添加到 PixelLib 的升级,这使得该库能够满足日益增长的需求,以平衡计算机视觉中的准确性和速度性能。

****注意:PixelLib py torch 版本不支持自定义训练,使用 PointRend 的自定义训练即将发布。

访问 PixelLib 官方 github 知识库

访问 PixelLib 的官方文档

通过以下方式联系我:

电子邮件:olafenwaayoola@gmail.com

Linkedin: 阿尤拉·奥拉芬娃

推特: @AyoolaOlafenwa

查看这些关于如何利用 PixelLib 在图像和视频中进行语义分割、实例分割、对象提取和背景编辑的文章。

** [## 用 5 行代码改变任何图像的背景

towardsdatascience.com](/change-the-background-of-any-image-with-5-lines-of-code-23a0ef10ce9a) ***

CPU 上的实时目标检测

原文:https://towardsdatascience.com/real-time-object-detection-on-cpu-9f77d32deeaf?source=collection_archive---------12-----------------------

利用 OpenVino 在 CPU 上实现实时目标检测。听起来很神奇,对吧?好吧,让我们真的去相信它。

Marc-Olivier Jodoin 在 Unsplash 上拍摄的照片

第一次有人问我是否有可能在 CPU 上进行实时物体检测,我对这个问题不屑一顾,心想:“一定是在开玩笑吧?”。

嗯,有可能!我试着去相信它。我们将介绍在 CPU 上以 40 帧/秒的速度实现对象检测。

我建议你读一点以下术语的意思,然后继续学习教程。你仍然可以在不理解这些术语的情况下,仅仅通过盲目地复制教程来完成,但这有什么乐趣呢,不是吗?

这篇博客假设你对上述术语的含义有所了解,并对深度学习和物体检测有一个大致的了解。我们将使用 yolov3-tiny 使用 darknet 进行对象检测。让我们开始吧!

我们需要预先训练好的yolov 3-小权重,它可以从这里下载,它的配置文件可以从这里复制。

接下来的步骤是将预训练的权重转换为 onnx,然后转换为 OpenVINO 的 IR 格式,这是 OpenVINO 进行推理所需的 xml、bin 和映射文件。我们将通过 4 个连续的步骤来完成这项工作。

  1. 将 YOLO 重量转换为 ONNX
  2. 使用 Netron 查找输出图层的名称
  3. 将 ONNX 转换为 IR 格式
  4. 使用 OpenVINO 运行推理

步骤 1:将 YOLO 重量转换为 ONNX

我将使用 jkjung-avt 的 tensorrt_demos 库将 yolo 权重转换为 onnx。这是我的个人偏好,因为我以前曾用它进行 TensorRT 转换。有许多其他存储库可以做同样的事情。我们将按照自述文件中提到的步骤将 yolov3-tiny weights 转换为 onnx。我们开始吧!

$ git clone [https://github.com/jkjung-avt/tensorrt_demos.git](https://github.com/jkjung-avt/tensorrt_demos.git)$ pip3 install onnx==1.4.1$ cd tensorrt_demos/yolo/

将权重文件和 cfg 文件复制到这个目录,即 tensorrt_demos 存储库中的 yolo 目录。

  1. 将权重文件重命名为 yolov3-tiny-416.weights
  2. 将 cfg 文件重命名为 yolov3-tiny-416.cfg
  3. 用这里给出的类名创建一个名为 labels.txt 的新文件。

这些步骤是必要的,因为存储库需要它们来为特定的模型和输入大小进行转换。

注意:我们使用 416x416 尺寸作为输入。如果您有自定义尺寸,可以对其进行修改。

移动并重命名文件后,让我们运行脚本来进行实际的转换。

$ python3 yolo_to_onnx.py -m yolov3-tiny-416

这将创建一个名为 yolov3-tiny-416.onnx. 的输出文件。让我们执行下一步,找到需要转换为 IR 格式的模型输出层的名称。为了找到输出层的名称,我们将使用 netron 来帮助可视化模型图/架构。

步骤 2:使用 Netron 查找输出层的名称

$ pip3 install netron$ netron
Serving at [http://localhost:8080](http://localhost:8080)

打开链接并上传 onnx 文件。它将显示如下所示的模型架构。

向下滚动架构到输出节点并点击它。右侧将出现一个详细信息框,显示输出节点名称,如下所示。

复制输出节点名称。我们需要名字来将 onnx 文件转换成 IR 格式。节点名称为:016_convolutional,023_convolutional

步骤 3:将 ONNX 转换为 IR 格式

我们需要设置 OpenVINO 及其依赖项来使用它。使用此连杆安装 OpenVINO 2021.1.110

注意:安装开发版本,而不是运行版本,因为它不包括模型转换脚本。

注意:我建议使用虚拟环境来安装这些包,因为它很容易管理依赖关系。

您可能已经安装了这些软件包,但是如果您还没有安装,请使用 pip 安装下面给出的软件包。

$ cd /opt/intel/openvino_2021/python$ source /opt/intel/openvino_2021/bin/setupvars.sh$ pip3 install networkx defusedxml test-generator==0.1.1$ cd /opt/intel/openvino_2021.1.110/deployment_tools/model_optimizer/install_prerequisites/$ ./install_prerequisites_onnx.sh

最后,设置和安装完成后,让我们运行脚本将 ONNX 文件转换为 IR 格式。用本地路径替换model_diroutput_dir

$ python3 /opt/intel/openvino_2021.1.110/deployment_tools/model_optimizer/mo.py --input_model /**path_to_model_dir**/yolov3-tiny-416.onnx -s 255 --reverse_input_channels --output 016_convolutional,023_convolutional --output_dir /**path_to_output_dir**/

这将输出 3 个文件,即。绑定文件、xml 文件和映射文件。这些文件是 OpenVINO 推理的中间表示。

步骤 4:使用 OpenVINO 运行推理

随着所有的转换最终完成,让我们在我们的网络摄像头或图像上运行一个演示,以检查推理速度。

$ git clone [https://github.com/Chen-MingChang/pytorch_YOLO_OpenVINO_demo.git](https://github.com/Chen-MingChang/pytorch_YOLO_OpenVINO_demo.git)**# activate environment that you used to install the above packages before running below commands.**$ cd /opt/intel/openvino_2021/python$ source /opt/intel/openvino_2021/bin/setupvars.sh$ cd /**path_to_cloned_repository**/pytorch_YOLO_OpenVINO_demo$ python3 yolo_openvino_demo.py -m /**path_to_model_directory**/yolov3-tiny-416.xml -i 'cam' -at yolov3 --labels /**path_to_labels**/labels.txt

你会看到比实时推断更快的输出视频。

如果您没有网络摄像头,请使用下面的命令在视频上尝试一下。

$ python3 yolo__openvino_demo.py -m /**path_to_model_directory**/yolov3-tiny-416.xml -i /**path_to_video_dir**/video.mp4 -at yolov3 --labels /**path_to_labels**/labels.txt

我们已经成功地将 yolov3-tiny 转换为在 CPU 上进行实时推理。嗯,我会说 40 FPS 几乎是实时的两倍。如果我需要阐述任何观点或需要任何更正,请评论下来。

参考

https://pjreddie.com/darknet/yolo/ https://docs.openvinotoolkit.org/latest/index.html https://github.com/jkjung-avt/tensorrt_demos https://github.com/Chen-MingChang/pytorch_YOLO_OpenVINO_demo https://github.com/lutzroeder/netron

如何用 Python 执行实时语音识别

原文:https://towardsdatascience.com/real-time-speech-recognition-python-assemblyai-13d35eeed226?source=collection_archive---------7-----------------------

使用 Python 中的 AssemblyAI API 执行实时语音转文本

杰森·罗斯韦尔Unsplash 拍摄的照片

介绍

在我最近的一篇文章中,我们探讨了如何用 AssemblyAI API 和 Python 执行离线语音识别。换句话说,我们将所需的音频文件上传到托管服务,然后使用 API 的转录端点来执行语音到文本的转换。

在今天的指南中,我们将展示如何使用 AssemblyAI API 的实时转录功能来执行实时语音到文本转换,该功能让我们能够以高精度实时转录音频流。

我们开始吧!

安装 PyAudio 和 Websockets

为了能够建立实时语音识别,我们需要一个工具,让我们记录音频。PyAudio 是一个 Python 库,为跨平台音频 I/O 库 PortAudio 提供绑定。使用这个库,我们可以在几乎任何平台上实时播放或录制音频,包括 OSX、Linux 和 MS Windows。

首先我们需要安装portaudio。在 OSX,你可以通过自制软件来实现:

brew install portaudio

然后从 PyPI 安装 PyAudio:

pip install pyaudio

如果你在 Windows 上,你可以通过一个基于你的 Python 版本的 wheel 文件来安装 PyAudio,你可以在这里找到这个文件。

此外,我们需要安装 websockets

pip install websockets

如果你想跟随这个教程,你所需要的就是一个 API 密匙,如果你注册了一个 AssemblyAI 账户,你就可以得到它。一旦你这样做了,你的密钥应该在你的账户部分可见。此外,您需要升级您的帐户(转到“计费”进行升级)才能使用高级功能。

使用汇编 API 的实时语音转文本

AssemblyAI 提供了一个 语音转文本 API ,它是使用先进的人工智能方法构建的,有助于视频和音频文件的转录。在今天的指南中,我们将使用这个 API 来实时执行语音识别!

现在,我们需要做的第一件事是通过指定一些参数,如每个缓冲区的帧数、采样率、格式和通道数,使用 PyAudio 打开一个流。我们的流将如下面的代码片段所示:

用 PyAudio 打开音频流—来源:作者

上面的代码片段将打开一个音频流,从我们的麦克风接收输入。

现在我们已经打开了流,我们需要使用 web 套接字将它实时传递给 AssemblyAI,以便执行语音识别。为此,我们需要定义一个异步函数来打开一个 websocket,这样我们就可以同时发送和接收数据。因此,我们需要定义两个内部异步函数——一个用于读入数据块,另一个用于接收数据块。

下面是通过 websocket 连接发送消息的第一种方法:

通过 websocket 连接发送数据—来源:作者

下面是通过 websocket 连接接收数据的第二种方法:

通过 websocket 连接接收数据—来源:作者

请注意,在上面的方法中,我们不处理任何特定的异常,但是您可能希望根据您的特定用例的要求适当地处理不同的异常和错误代码。有关错误条件的更多详细信息,您可以参考官方汇编文件中定义关闭和状态代码的相关章节。

现在,使用上述两个函数的主要异步函数定义如下。

通过 websocket 连接发送和接收数据—来源:作者

完整代码

下面的要点包含了我们将要使用的完整代码,以便使用 AssemblyAI 的 API 执行实时语音到文本转换。

使用 AssemblyAI API 执行语音到文本转换的完整代码—来源:作者

示范

现在我们有了一个完整的代码,它能够打开一个音频流,从我们的麦克风发送输入,并从 AssemblyAI API 异步接收响应。

为了运行代码,我们需要做的就是将我们的异步函数传递给asyncio.run():

asyncio.run(speech_to_text())

现在,您应该能够通过麦克风说话,并转录流音频。

出于本教程的目的,我已经上传了从我的计算机上流式传输的音频,以便实时执行语音到文本转换。

使用上述音频流运行我们刚刚编写的程序后的输出如下所示:

示例输出—来源:作者

最后的想法

在今天的文章中,我们探讨了如何通过打开音频流和 websockets 来实时执行语音识别,以便与 AssemblyAI API 进行交互。注意,我们只涉及了 AssemblyAI API 提供的全部特性的一小部分。请务必点击查看他们的完整列表

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

https://gmyrianthous.medium.com/membership

马尔可夫决策过程的现实应用

原文:https://towardsdatascience.com/real-world-applications-of-markov-decision-process-mdp-a39685546026?source=collection_archive---------2-----------------------

理解大数据

从实例中学习,将问题公式化为马尔可夫决策过程,以应用强化学习

马尔可夫决策过程(MDP)是强化学习(RL)的一个基本要素。MDP 允许顺序决策的形式化,其中一个状态的行为不仅影响直接的回报,还影响随后的状态。这是一个非常有用的框架,通过采取一系列行动来模拟最大化长期回报的问题。由萨顿和巴尔托[1]所著的《强化学习——简介》一书的第三章对 MDP 做了很好的介绍。这本书也可以免费在下载

将问题表达为 MDP 是通过动态编程或其他 RL 技术解决问题的第一步。玩电脑游戏或执行任务的机器人通常会自然地映射到 MDP。但是许多其他现实世界的问题也可以通过这个框架来解决。然而,现实世界中的例子并不多。本文提供了一些有限 MDP 的真实例子。我们还展示了相应的转换图,它有效地总结了 MDP 动力学。这样的例子可以作为很好的动机,学习和发展的技能,以制定问题,如 MDP。

MDP 基础知识

在 MDP 中,主体通过采取行动与环境交互,并寻求主体从环境中获得最大回报。在任何给定的时间戳 t,该过程如下

  1. 环境处于 St 状态
  2. 代理在以下时间采取行动
  3. 环境基于 St 和 At 生成奖励 Rt
  4. 环境移动到下一个状态 St+1

代理的目标是使一段时间内收集的总报酬(σRt)最大化。代理人需要在给定的状态下找到最优的行动,使总回报最大化。从一个状态 St 采取行动的概率分布称为策略π(At | St)。求解 MDP 的目标是找到最优策略。

为了用 MDP 表达一个问题,需要定义以下内容

  • 环境的状态
  • 代理可以对每个状态采取的动作
  • 对给定状态采取行动后获得的奖励
  • 状态转移概率。

动作只能依赖于当前状态,而不能依赖于任何先前的状态或先前的动作(马尔可夫属性)。奖励的产生仅取决于(当前状态,行动)对。行动和回报都是随机的。

一旦问题被表示为 MDP,就可以使用动态规划或许多其他技术来寻找最优策略。

在 MDP 有更多的细节,这将是有益的回顾萨顿的 RL 书的第 3 章。

MDP 的真实例子

1。今年是否捕捞鲑鱼

我们需要决定在一个特定的区域里,一年要捕获多少比例的鲑鱼,以获得最大的长期回报。每条鲑鱼产生固定数量的美元。但是如果大部分的鲑鱼被捕获,那么下一年的产量将会降低。我们需要找到鲑鱼的最佳比例,以实现长期回报的最大化。

这里我们考虑上述问题的一个简化版本;是否捕捞一定量的三文鱼。这个问题可以表示为如下的 MDP

州:当年该地区可获得的鲑鱼数量。为简单起见,假设只有四种状态;。这四种状态定义如下

- >没有三文鱼可用; - >可用鲑鱼数量低于某个阈值 t1; 中等 - >可用鲑鱼数量介于 t1 和 T2 之间; - >可用鲑鱼数量均超过 T2

动作:为了简单起见,假设只有两个动作;落鱼未落鱼。鱼意味着捕捉一定比例的鲑鱼。对于空的状态,唯一可能的动作是不捕鱼。

奖励:在某个状态钓鱼会产生奖励,假设在低、中、高状态钓鱼的奖励分别是$5K、$50K 和$100k。如果一个行动需要清空状态,那么奖励是非常低的-20 万美元,因为它需要重新繁殖新的鲑鱼,这需要时间和金钱。

状态转换:在一个州捕鱼有较高的概率转移到鲑鱼数量较少的州。类似地,不捕鱼行动有更高的概率移动到鲑鱼数量更高的州(州高除外)。

图 1:MDP 鲑鱼捕捞的过渡图

图 1 显示了这个 MDP 的转换图。有两种节点。大圆圈是状态节点,黑色实心小圆圈是动作节点。一旦采取了行动,环境就会以奖励作为回应,并转换到下一个状态。这种状态转换由从动作节点到状态节点的箭头表示。每个箭头显示<跃迁概率,奖励>。例如,从状态媒体动作节点有 2 个箭头过渡到 2 个不同的状态;i) (概率=0.75,奖励=$10K) 或者 ii)回(概率=0.25,奖励=$10K)。清空的状态下,唯一的动作是重新繁殖并通过转换到状态 Low(概率=1,奖励=-$200K)。

2.智力竞赛节目

在一个智力竞赛节目中,有 10 个级别,在每个级别都会问一个问题,如果回答正确,就会根据当前级别给予一定的金钱奖励。级别越高,问题越难,但奖励也越高。

在每一轮游戏中,如果参与者正确回答了测验,那么他/她将赢得奖励,并可以决定是继续玩下一关还是退出。如果退出,则参与者可以保留到目前为止获得的所有奖励。在任何一轮中,如果参与者未能正确回答,那么他/她将失去迄今为止获得的“所有”奖励。游戏在 10 级停止。目标是决定玩或退出的行动,使总回报最大化

状态:

动作:玩下一关或退出

奖励:在 1 级、2 级、…、10 级玩分别产生奖励$10、$50、$100、$500、$1000、$5000、$10000、$50000、\(100000、\) 100000、$50000、$500000,概率 p = 0.99、0.9、0.8、…、0.2、0.1。这里的概率是给出正确答案的概率。在任何级别,参与者以概率(1- p)输掉,并输掉迄今赢得的所有奖励。

图 2:问答游戏 MDP 的转换图

在图 2 中我们可以看到,对于动作,有两种可能的转换,i) 赢了以概率 p 转换到下一个级别,并且当前级别的奖励金额 ii) 输了以概率(1-p)结束游戏,并且失去了到目前为止赢得的所有奖励。动作退出以概率 1 结束游戏,无奖励。

3.减少交通路口的等待时间

我们想要决定十字路口交通灯的持续时间,以最大化不停车通过十字路口的汽车数量。为简单起见,让我们假设这只是一个双向交叉路口,即交通只能在两个方向流动;;而红绿灯只有两种颜色红色绿色。还假设系统可以通过传感器或仅仅一些估计来访问接近十字路口的汽车数量。

假设 MDP 的每个时间步长代表几(d=3 或 5)秒。在每个时间步,我们需要决定是否改变交通灯。

状态:这里的状态表示为以下各项的组合

  1. 每个方向上交通灯的颜色(红色、绿色)
  2. 相同颜色交通灯的持续时间
  3. 每个方向接近十字路口的汽车数量。

动作:是否改变红绿灯

奖励:下一时间步通过路口的车数减去另一方向堵车的某种折扣。折扣应该随着流量被阻塞的持续时间呈指数增长。请注意,持续时间是作为当前状态的一部分捕获的,因此马尔可夫属性仍然保留。

奖励 =(下一时间步预计通过的车数)— α * exp(另一方向红灯亮的β *时长)

状态转换:转换是确定性的。操作要么改变交通灯的颜色,要么不改变。对于任何一个动作,它都会改变到一个新的状态,如下面的转换图所示。

图 3:转换图;仅显示了许多可能状态中的 3 种

4.有多少病人入院

医院有一定数量的床位。它每天接收随机数量的病人,并需要决定它可以接纳多少病人。此外,医院里每天都有一部分病人康复出院。医院希望在很长一段时间内使康复的人数最大化。

状态:假设医院有 100 个床位,可用床位数{1,2,…,100}

行动:医院每天都会收到基于泊松随机变量的入院请求。行动就是接纳多少病人。因此,该动作是 0 到(100-s)之间的数字,其中 s 是当前状态,即被占用的床位数。该操作需要少于医院当天收到的请求数。

所以 action = {0,min(100-s,请求数)}

奖励:奖励是当天康复的患者数量,是当前状态下患者数量的函数。我们可以将此视为具有均值 s 的泊松分布。

图 4:在这个转换图中只显示了一个动作和状态转换。它根据带有均值 s(当前患者数量)的激情分布获得奖励(康复患者)

结论

在这篇文档中,我们展示了一些现实世界中可以建模为马尔可夫决策问题的例子。这样的现实世界的问题显示了这个框架的有用性和力量。这些例子和相应的转换图可以帮助开发使用 MDP 表达问题的技巧。作为进一步的探索,可以尝试使用动态规划来解决这些问题,并探索最优解。

参考

[1]强化学习:由理查德 s 萨顿和安德鲁 g 巴尔托介绍

符号回归的现实应用

原文:https://towardsdatascience.com/real-world-applications-of-symbolic-regression-2025d17b88ef?source=collection_archive---------6-----------------------

符号回归的主要思想,即寻找与变量相关的方程,已经存在很长时间了。但直到最近十年,它才开始对物理、化学、生物和工程的实际研究产生影响。在那里可以找到关键的新方法,一些你可以使用的现代程序,以及一些应用实例。作者 Luciano Abriata 图。

对数据的分析性描述,而不仅仅是黑匣子

符号回归(即找到与变量相关的方程)不像常规神经网络那样是一个黑箱,并且提供了不仅可以预测数据而且可以合理实现预测的模型,因此远不如其他机器学习和数学建模技术受欢迎。然而,正如我在这里介绍的,越来越多的符号回归应用正在成为现实,特别是随着新方法的发展,这些新方法集成了物理学启发的约束,数据投影和预处理,以及收敛到简单、有意义的方程的新方法。

简而言之的索引

简介

符号回归包括识别符合输入和输出值数据集的数学表达式。有许多不同的方法来处理该问题并实现将输入连接到输出的这种解析表达式,一些方法更多地与机器学习技术相关,一些方法利用遗传算法,一些方法基于预先设定的规则,而另一些方法更广泛地利用可能方程的空间,等等。寻找连接变量的解析方程远非易事,尤其是当人们寻找那些看起来像通常通过理论手段推导出来的那样“优雅”的简单方程时。例如,一个物体经历恒定加速度,从零速度开始运动,所走过的距离就是 d = a t / 2。或者只是再举一个例子,放射性衰变导致一种物质的辐射强度呈指数下降:I = I(0) exp(-kt)。在这些例子中,给定一组在增加的时间行进的距离,或者一组辐射强度对时间,符号回归将被期望检索相应的方程。但是当然,其他函数也可能符合数据,不一定描述数据背后的简单物理。

与使用常规回归、神经网络或其他数学工具建模数据相比,使用符号回归建模数据具有一些优势。对我来说,两个最重要的优势是:

  1. 不仅可以像其他建模方法一样对数据进行建模并能够从新输入预测新输出,还可以实际掌握变量以给定方式相关的原因。为什么的答案最终可以帮助提出什么是物理的、生物的、或化学的、金融的等等。(视题目而定)联想背后。例如,指数衰减通常与单步、一级事件相关,其速率与密度(辐射强度、培养物中的细胞数量等)成比例。).随着时间的推移,随着起始密度的降低,其变化率也随之降低,这以综合的形式导致了基于指数的方程。同样,从加速度作为位置对时间的二阶导数的初等积分,可以获得以恒定加速度行进的距离的方程。
  2. 如果获得的方程是正确的,该模型可能比大多数其他数学模型具有更大的外推能力,尤其是那些基于神经网络的模型,神经网络通常很适合插值,但只能在训练域之外进行外推。

换句话说,来自一个好的符号回归的结果更容易理解,更少的黑箱,可能更强大。

为了真正实现这些优势,重要的一点是最大化获得物理上合理的方程的机会。为此,许多方法结合了“方程搜索空间”的物理约束导航。另一方面,人们需要对方程搜索空间进行大量采样,特别是对于传统符号回归更容易失败的高维问题。为了最大限度地提高采样、简单性和物理准确性,科学家们现在正在通过将机器学习和进化算法与基于物理学的方程约束相结合来构建新的符号回归器。

我不会深入讨论符号回归的细节,因为在这里的 TDS 编辑(例如这一篇作者拉菲尔·鲁杰罗)和维基百科(这里)以及其他资源中都有精彩的文章。相反,我在这里关注符号回归的科学应用的实际、现代例子,正如 Rafael 文章的标题所说,它有点像一种“被遗忘”的方法,可能被常规的机器学习方法所掩盖:

我首先回顾两篇重要的近期文章,它们已经展示了应用,但主要集中在从数据中发现方程的最现代方法背后的理论。在接下来的部分,我将介绍一些非常有趣的应用,其中一个实际上来自我自己的研究。

首先,两种新的物理约束符号回归方法具有广泛的应用

第一篇文章:Udresku 和 Tegmark 2020

我要强调的第一篇文章是这篇发表在 2020 年科学进展(此处文章,开放访问)的文章。它的标题是“AI Feynman:一种受物理学启发的符号回归方法”,正如你所看到的,它也引用了著名物理学家理查德·费曼……这里开发的方法和软件可以发现该作者的一本书中的所有 100 个物理方程。

这项工作的核心思想相对简单:为了建立他们新的符号回归算法,他们将神经网络拟合与一组物理学启发的约束和方程特征相结合。这项工作的一个关键组成部分是认识到出现在物理学中的函数(本质上是大多数科学应用)通常具有某种限制方程搜索空间的属性。这在论文中有详细的描述,但是让我们概括一下:变量和系数有必须一致的物理单位(例如,对数经常应用于单位被抵消的比率或类似的论点)。多项式通常达到低阶(但不一定限于平方幂:见平方根,光散射对 wavelength/10⁴的依赖性等。).大多数方程由一个或几个项组成,每个项通常不超过两个自变量。这些方程是连续的和光滑的,至少在它们合理的范围内是这样;并且它们通常相对于它们的一些或所有变量是对称的。最后,变量通常被分成小组,或者用不同的术语单独分组。

本文提出的算法考虑了不同块中的所有这些点,并通过将问题分成更小的问题并分别在这些块上运行来递归地工作。该方法的一些重要亮点:

首先,值得注意的是,该方法确实涉及强力方程搜索的阶段;然而,通过对更大问题的分割部分进行运行,它可以更好地对搜索空间进行采样。

第二,该作品还使用了一种特殊的方式来减少过拟合的机会,即像以前的作品一样定义获胜函数(“获胜”指的是各种备选答案的竞争)。

第三,在运行时,该程序利用常规神经网络来创建插值数据,这有助于评估方程所需的拟合程序,以及测试对称性、平滑度和项可分性的程序。

第四个有趣的地方是,该方法基于正规方程中常见的数学方法,在输入中使用数据转换(例如,输入 x 而不是仅仅输入 x ),以加速方程发现。

本文描述的程序是 GitHub 提供的。在作者报告的测试中,程序需要几十秒到几十分钟来收敛少量数据点上的符号回归。该程序能够恢复费曼书中的所有 100 个方程,仅使用初等函数(+、∫、/、sqrt、exp、log、s in、cos、arsin 和 tanh)和小有理数以及 e 和π。通过向数据集添加模拟噪声,作者还测试了该方法和程序的稳健性。

以下是在科学进展的原文:

https://www.science.org/doi/10.1126/sciadv.aay2631

通过阅读丹尼尔·夏皮罗博士撰写的这篇 TDS 文章,你可以对这个项目更加了解:

第二篇文章:Reinbold 等人 2021

我要强调的第二篇文章发表在 2021 年的自然通讯(此处开放获取文章)。像 AI-Feynman 一样,本文描述的方法也利用真实物理方程的各种特征作为方程搜索问题的约束,同时寻求平衡准确性和简单性的简约模型。此外,由于作者解决了导数非常重要的问题,他们实现了微分方程的弱公式,以降低噪声敏感性并消除对不可访问变量的依赖。采用弱公式的主要原因是将微分方程化为积分方程,以避免计算导数的负担。关于弱形式的更多信息,请看这个伟大的博客

在他们的论文中,作者展示了这种方法在湍流速度场实验测量中的应用。根据这一仅有的输入数据,该方法允许重构不可接近的变量,例如驱动流动的压力。尽管如前所述,它只适用于这个测试用例,但该方法可能也适用于其他类型的问题。

这是发表在自然通讯上的原始论文:

https://www.nature.com/articles/s41467-021-23479-0

现在它们在这里:符号回归在化学和生物科学中的现代实际应用

截至 2021 年 9 月 30 日,在 PubMed(世界上最大的自然科学在线图书馆)的所有论文标题和摘要中寻找“符号回归”会返回 76 个结果。这不包括只在正文中提到这个术语的文章;并且仅限于生物学和化学领域的论文,而忽略了计算机科学领域的论文,因此它是符号回归代表所报道工作的重要应用的论文的良好代理。

有一篇发表于 1997 年的文章,谈到符号回归是一种发现“一元函数”的方法。然后一直是一片空白,直到 2011 年,一篇论文使用符号回归找到了一个描述肾小球滤过率的方程,肾小球滤过率是衡量肾功能的一个指标,对肾移植很有用。得到的方程被证明优于当时存在的其它方程。

从 2011 年起,标题或摘要中带有“象征性回归”的出版物数量开始平稳增长,截至 9 月 30 日,2021 年有 16 种:

PubMed 主办的标题和/或摘要中提到“符号回归”的文章数量。作者 Luciano Abriata 的数据来自 PubMed data。

现在让我们来看一些我觉得最有趣的案例,它们展示了现代符号回归方法的实际应用。

**2020 年Nature communication s**中的论文对各种钙钛矿的析氧活性数据进行了符号回归,以了解哪些变量是活性的最佳预测因子,通过哪些方程。通过这项工作,作者可以确定一个简单的描述符,即该研究领域中常用的表征钙钛矿组成的两个因子的比率,以及根据该描述符模拟活性的方程。这一符号模型导致了一系列具有改进活性的新型氧化物钙钛矿催化剂的发现,作者合成并表征了这些催化剂以证实它们的高活性。符号回归是用 gplearn 实现的,这是一个 Python 库,用这个功能扩展了 scikit-learn。论文在这里:

*https://www.nature.com/articles/s41467-020-17263-9

这是在 python 中用于符号回归(和其他非常有趣的方法)的 gplearn 库:

https://gplearn.readthedocs.io/en/stable/

下一篇论文 Phys Rev E 2021 ,处于符号回归的方法开发和从失真视频中发现物理规律的实际应用的接口。本文提出了一种对原始视频中未标记对象的运动方程进行无监督学习的方法。想象一个相对静止的场景,一个物体在上面运动,你想要得到这个物体的运动方程,甚至不需要标记或者有目的地跟踪它。我选择这篇文章在这里展示,因为它很好地集成了图像分析、预处理、低维投影和符号回归本身。

在这种方法中,自动编码器首先将视频的每一帧映射到简化运动的低维潜在空间中。这作为一个预回归,然后被送入帕累托最优符号回归,以找到描述对象运动的微分方程。预回归步骤可以对未标记的移动对象的坐标进行建模,即使视频失真,就像现实生活中的视频一样。潜在空间维度的使用有助于避免拓扑问题,并且可以在以后通过主成分分析来去除。最后,通过最小化整体运动,该方法还可以自动发现惯性框架,从而减少最终运动的失真(例如由于移动的摄像机或背景而发生的失真),并因此便于获得简单的方程。

这篇论文提出了一种非常新颖的分析视频的方法,在这里:

**https://pubmed.ncbi.nlm.nih.gov/34005960/

或者这是 arXiv 的预印本:

https://arxiv.org/abs/2005.11212

下一篇论文生物鉴定 2019 年,提出了生态动力学的建模,即共享一个栖息地的各种物种的种群如何随着时间的推移相互之间以及与非生物因素之间的关系而进化。这项工作将符号回归与一组合理的生态功能反应结合起来,根据生物丰度的时间相关数据对生态系统动态进行逆向工程。给定输入数据,该过程返回描述它的候选微分方程组,然后分析它们在生态学概念方面的意义。正如作者所讨论的,我们可以确定使用符号回归的两个主要优点。首先,所得的微分方程可以潜在地被解释以理解生态系统的潜在生态机制,例如物种之间的生态相互作用的类型,例如成对的捕食者和被捕食者物种之间的生态相互作用。作者强调的第二个要点是,即使在数据有限或信息不充分的情况下,该方法似乎也表现良好,这可能是因为它们提供了方程本身的候选起始部分,从而将搜索限制在只有低质量或稀疏数据才能拟合的有意义的方程。

这篇论文,采用了这种有趣的方法,在这里:

**https://pubmed.ncbi.nlm.nih.gov/31617228/

另一篇 论文,由我在 分子生物技术 2021 年发表,使用符号回归对突变对蛋白质热稳定性的影响进行建模,同时了解不同因素如何调节对稳定性的影响。正如论文所示,这个问题建模起来非常复杂,部分原因是可用数据量有限。但是一个特定的突变,从氨基酸缬氨酸到氨基酸丙氨酸,在数据集中有 47 个记录条目。使用野生型(缬氨酸)氨基酸在其结构背景下的三个因子,即其相对溶剂可及性、二级结构和从原子 B 因子量化的柔韧性,对该数据进行符号回归,得到该方程:

δTm(C)= SS–SS/(8.58 RSA–0.89)+13.56 RSA–7.35

该方程拟合的相关系数 r 为 0.68,平均误差 RMS 为 3.3℃。该方程没有显示出灵活性的相关影响,因为该模型仅与二级结构(SS)和相对溶剂可及性(RSA)一起工作良好。事实上,该模型的关键术语是-7.35 的偏移,这意味着全局不稳定效应,以及对 RSA 的+13.56 倍的强烈调节:暴露的氨基酸越多,其对稳定性的积极作用越强。这具有完美的物理意义,因为缬氨酸是一种疏水性氨基酸,因此它更喜欢隐藏在水中,所以当它高度暴露(高 RSA)时,它被疏水性较低的丙氨酸取代导致稳定。

本文还证明了符号回归比在相同因素上使用多元线性回归产生更好的结果,特别是在相关图的形状(斜率)方面:

符号回归(左)与多元线性回归(右)来模拟缬氨酸到丙氨酸突变对蛋白质热稳定性的影响,来自这项工作。作者编制的图表。

这篇以简单符号回归应用为特色的论文如下:

https://link.springer.com/article/10.1007/s12033-021-00349-0

这项工作中的符号回归是用 TuringBot 进行的,这是一个易于使用的程序:

https://turingbotsoftware.com/

一些额外的有趣作品

开发 AI Feynman 的同一小组的最后一篇论文之一描述了 AI Poincaré ,这是一个符号回归系统,它使用来自动态系统的轨迹数据自动发现守恒量。通过对五个物理哈密顿量的测试,该程序可以发现它们的守恒量、周期轨道、相变和击穿时标,而不需要任何领域知识,甚至不需要轨迹是如何产生的物理模型。

本着引导符号回归搜索的精神,像上面的一些例子一样,这篇论文从激子能量学的理论方程中输入术语,以模拟激子结合能的解析表示。

这篇综述提供了应用于材料科学的符号回归的概述,以及该领域和工程问题中的一些例子。此外,这篇评论还提到了 2009 年科学杂志的一篇论文,该论文将符号回归作为一种从实验数据中发现自然规律方程的方法,这可能是我在这里介绍的所有其他作品的基础。

如果你有一些使用符号回归的有趣文章要分享,请在评论中告诉我。我希望很快看到更多这种令人兴奋的、非常有用的技术。

喜欢这篇文章,想给我提示?[Paypal]-谢谢!

我是一个自然、科学、技术、编程、DIY 爱好者。生物技术专家和化学家,在潮湿的实验室和计算机里。我写我广泛兴趣范围内的一切。查看我的 列表 了解更多故事。 成为媒介会员 访问我和其他作家的所有故事, 订阅通过邮件获取我的新故事 (平台原创附属链接)。*****

构建现代云数据湖的真实最佳实践

原文:https://towardsdatascience.com/real-world-best-practices-for-building-modern-cloud-data-lakes-a1efa2bc44a5?source=collection_archive---------37-----------------------

从真实世界云数据湖部署中吸取的经验教训。

Jong MarshesUnsplash 上拍照

今年 7 月,Dremio 赞助了行业首次云数据湖会议 Subsurface 。该活动探讨了云数据湖的未来,包括与网飞、Expedia、Preset、Exelon、微软和 AWS 等公司的开源和技术领导者讨论他们在引领开源项目和构建现代数据湖方面的实际经验。

在活动期间,发言者分享了他们构建现代云数据湖之旅的详细信息。他们谈论了他们面临的挑战,什么有效,什么无效,最重要的是,他们是如何成功的。以下是关键要点。

云数据湖的基本原则

简单

云数据湖成为数据落地的第一站的能力是它们变得越来越受欢迎的原因之一。云数据湖成功的一个基本原则是让它们可以被许多团队出于多种目的而接近和访问。这使得公司能够从他们的数据中收集洞察力,否则不可能发现。

可量测性

不牺牲成本的高性能和大规模可伸缩性是成功的数据湖的另一个基本要素。云平台最引人注目的特性之一是它们高度可扩展,这使它们成为寻找最佳平台来托管其数据湖的企业的理想选择。此外,在构建云数据湖时,有多种方法可以平衡增长,而不会倾家荡产。

数据可访问性

数据消费者面临的一个主要问题是,当他们需要数据时,无法访问他们需要的数据。数据可访问性的限制导致分析延迟和潜在的商业机会损失。如果存储在数据湖中的数据不能立即供分析师使用,那么在构建云数据湖方面投入的时间、精力和预算都不会产生任何回报。

挑战和如何克服挑战

微软 Azure 团队项目经理 Raji Easwaran 在她的演讲“从在微软运营一个 EB 级数据湖中获得的经验教训”中解释说,创建云数据湖时最常见的挑战与上面强调的基本原则密切相关。

据 Raji 称,当微软构建其数据湖时,它变得越来越受欢迎,他们面临的主要挑战之一是试图跟上组织中多个团队的持续需求。他们的数据湖如此受欢迎,以至于其大小从数百 Pb 增加到了 EB 级,为了满足用户需求,他们不得不将集群中的节点数量增加到数千个。这之所以成为可能,是因为他们在构建数据湖时已经将“可扩展性”确立为核心战略原则之一。

与增长密切相关的另一个挑战是成本管理。Raji 解释说,当数据湖的速度和规模加快时,保持实施的成本效益只能通过使用工作负载智能来实现。这意味着了解工作负载并找出最常使用的数据集,以便在创建数据集时,多个用户可以利用它,而不必多次重新创建它。

最佳实践

保持开放

工程师的工具箱里总是有合适的工具来做合适的工作。数据工程师也不例外——一旦他们能够访问数据,他们就需要各种工具来处理多种情况。不幸的是,在数据湖中使用专有格式会给生产率造成障碍。例如,将所有数据迁移到云数据仓库,将数据转换为数据湖中任何其他处理引擎都不使用的格式,这限制了数据的价值和用途。

利用开源数据格式不仅可以帮助数据工程师使用业内最流行的数据格式,还可以消除供应商锁定。它为组织提供了探索和试验其他技术和平台的自由和灵活性。

尽早让你的用户参与进来

S7 Airlines 的企业数据平台产品负责人 Areg Azaryan 在他的演讲“利用 S7 Airlines 自助服务数据平台缩短上市时间”中,将云数据湖基础设施的成功归功于他们在云数据湖设计的早期阶段就考虑到了最终用户的需求。在流程的早期让最终用户参与进来,可以让他们熟悉平台,并帮助团队在投入生产之前发现潜在的问题或缺陷。这也有助于在组织内传播对新实现的认识,从而增强协作和采用。

实施安全和治理—但不要做得太多!

不幸的是,许多防范数据泄露的系统增加了内部人为安全错误的风险。安全系统有时非常复杂,用户试图绕过它们。为了管理这种类型的风险,企业需要关注简单性,并为用户提供足够的工具,这样他们就不会试图在现有的系统之外工作。

为了防止数据湖变成数据沼泽,并阻止用户寻找更少管理的替代方案,Areg 建议企业为数据共享提供一种受管理的机制,以减少产生不连续的副本,并避免不必要的数据访问限制。

在 S7,观察到数据工程师与新平台交互,他们的行为用于实施有效的治理和安全策略。

尽可能启用粗粒度所有权也是一种很好的安全措施。云的可伸缩性和弹性使得为不同的团队创建单独的资源变得更加容易。完全资源隔离正在成为数据湖和数据仓库的一种常见模型,允许数据团队使用他们的资源,而不与其他组织单位共享。此外,访问控制更易于设置和维护。

寻求反馈

跟踪新实施的云数据湖为您的组织提供的价值非常重要。开发 KPI,使您能够识别工作负载模式、流行数据集等。,并要求您的最终用户提供反馈,以便您可以衡量项目的成功以及确定需要改进的地方。这样做将确保您的数据计划持续成功并获得投资回报。

结论

云数据湖因其灵活性、可扩展性和成本效益而变得越来越受欢迎,这也是数据驱动型公司如此快速地采用它们的原因。简单性、可扩展性和数据可访问性等基本原则,加上利用开源技术、实施安全和治理标准以及最终用户的早期参与等最佳实践,将确保成功并扩大云数据湖在您组织中的价值。

真实世界的破折号和情节

原文:https://towardsdatascience.com/real-world-dash-and-plotly-96970b8b1005?source=collection_archive---------43-----------------------

用一对很棒的工具让我的生活变得更简单

“是的,问题就在这里——这不是触摸屏……”照片由 Pexels马体·米罗什尼琴科拍摄

我是数据分析的浪子。我上一次做任何繁重的分析工作是在将近十年前。这是科技的永恒。但是,通过一些生活的改变和视角的重新调整,我决定我要回到我的根源。

对于那些还年轻,刚刚开始在这个领域工作的人,我只能说,你的那一天会到来。我认为最令我惊讶的是,45 岁之后追求纯技术角色所带来的挑战。

我不会拿出一张关于年龄歧视之类的卡片。这些平台充满了它。这也不是这篇文章的主题。我要说的是,它改变了我的看法。如果我要规划一条路线,我将不得不迈出更大的一步,自己去做。

然而,无论你的年龄或经历,这里都有教训。数据科学(广义上的标签)是一个竞争激烈的领域。聪明、有抱负、有创业精神的候选人将获得奖励。这就是这个故事的内容。

无论是在各种规模的企业中,还是在高管层周围,我都远离纯粹的技术。我学到了一些东西。主要是因为它不适合我,但那是另一回事了。我学到的最重要的东西是如何量化价值。

如果我想成功地利用剩余的工作时间做我喜欢的事情,价值就是我必须证明的。我喜欢数字。我喜欢分析它们,并从中得出结论。我也喜欢教别人如何做我做的事情。

所以,我做了任何一个正常的技术人员在这种情况下都会做的事情。我决定创建一个关于金融分析的 YouTube 频道。

YouTube 频道?真的吗?!?

没错。让我花点时间解释一下其中的逻辑。

我需要一个地方来吸引观众,并展示我的能力。这不仅包括构建有用技术的技术方面,还包括解释背后的逻辑。

这是一个一石二鸟的策略。

直接面对招聘经理或人力资源人员会让我进入他们的舒适区。他们会测试我他们想要什么,以及他们如何去做。

如果没有人提到它,大多数人力资源招聘流程都被打破了。我知道,在过去的生活中我咨询过一些。他们太可怕了。

所以,最好的决定是展示我自己的设备。一些会导致技术讨论以及突出我对教学和谈话的热情。对于招聘经理来说,没有什么比不仅能创造技术,还能解释技术的人更性感的了。再加上关于用途、含义和现实世界适用性的讨论,你就进入状态了。

因此,对我来说,合理的选择是 YouTube 频道,它让我有时间谈论我热爱的事情。在这种情况下,它恰巧是经济和商品市场。

更不用说,由于我在过去的 18 个月里大部分时间都没有工作,而且独自一人呆在家里,使用我的声音是很好的练习。

让我们认真对待 Dash 和 Plotly

你可能会问自己的第一个问题是为什么 Dash 和 Plotly?

鉴于我的渠道的格式,可视化将是重点,而不是我。我需要一些既灵活又适合既定工作流程的东西。

你看,每天,我都用我的渠道提供最新的市场动向和经济状况。我不是明星,但数据是。因此,我需要一个能在这方面与我合作的平台。

我从一开始就决定要加倍努力把 Python 作为主要语言来使用。这是我刚刚熟悉的东西,但我想变得更好。我想,既然它在大多数调查中评价很高,这是一个不错的选择。

起初,我使用 Matplotlib 等可靠的方法来创建图表,但感觉很费力。我个人并没有看到一个能完成我的目标的 get 工作流。

然后我开始用 plotly 做实验。

我不会用不必要的解释来烦你。有大量的教程和其他参考资料。事实上,如果你在我的文章中发现了自己,你可能已经知道了。

我首先将我在 Matplotlib 中完成的许多工作复制到 plotly 方法论中。我只想说这太残忍了。我有很多分析图表,而且对 plotly 来说是新手,要熟悉这种新语言并弄清楚格式的细微差别有点困难。

我实际上夸大了这一点,但我想让它看起来像是有我的灰质参与了一些工作。事实上,它非常简单和直观。我最大的挑战是以前端可消费的方式做事,因为我从未自称是前端开发人员或设计师。然而,我确实发现默认设置实际上运行得非常好。

完成这些工作后,我有了一整套在浏览器中看起来很棒的图表。但是,我需要一个框架来把它们放在一起。

虽然单独的图表有其吸引力,但没有人希望看到一个人在浏览器上点击一百个标签来表明自己的观点。我也不确定有多少人真的想看一个人点击一个框架来阐述一个观点,但我感觉前者的人数要多得多。

随着大任务的完成(他天真地认为),我需要选择一些东西来保持它在一起。我看了很多框架,但最终还是选择了 Dash。

为什么是 Dash?

我不会说 Dash 是最简单或最能干的。那就太夸张了。但是,有两件事立刻吸引了我:

  1. 它与普洛特利携手合作。由同一家公司开发,有一些不能忽视的协同作用。
  2. 文档。虽然许多更复杂的特性需要从各种来源的大量文档中筛选,但使其工作的基本材料就在那里。

你可以说我懒,但是拥有一条通往运营状态的简单道路对我来说是很有价值的。达什做到了。

公平地说,如果你对图表关注的不是 Plotly,你可能会认真评估它的好处,但如果你已经在使用 Plotly,那就一定要试试 Dash。

前进的道路

有了完整的图表目录和框架决定,我开始工作。这里有一个学习曲线。Dash 在很大程度上是友好的,但是有多友好将取决于你对前端概念的熟悉程度和 CSS、bootstrap(如果你走那个方向)的细微差别,以及从 UI/UX 的角度思考。你的年龄可能会有所不同。

我的第一个目标很简单。我能弄点东西来运行吗?

在几行代码中,我的答案是肯定的。没有什么比“hello world”项目更有成就感的了。

我的下一步是弄清楚如何将我的一个图表放到界面中。这也很简单。它只需要定义一个数字,并将其设置为与定义的图表相等。

吼吼!我是我领域的主人。

但是,我需要更多。你好世界很好,但没有风格。没有派头。我需要一个主题。

幸运的是,我在做一些类似“Dash Bootstrap Themes”的随机搜索时,偶然发现了一些中等功能的 Dash Bootstrap 主题。很疯狂,对吧?

这个特殊的主题使用了一些我非常喜欢的特性,比如卡片从逻辑上划分了空间,模态构建了图表显示。

像任何好的排骨店一样,我开始把它切成我需要的东西。令人高兴的是,一切都布置得相对较好。

—卡片被定义为保存内容和激活模式。

—回调被设置为正确触发模式。

—所有内容都定义在一个变量中,该变量包含每行一到三列。

—每个变量在布局部分都有明确的定义。

—每个布局部分都被添加到允许在不同页面之间导航的回调中。

—所有这些都是为 app.layout 配置的,以便在正确的时间显示正确的内容。

从那时起,它主要是即插即用。

我现在已经过了那个简单的开始的 10 个版本了。一些变化在本质上是功能性的,比如某些流程没有为我需要完成的事情工作。有些版本很有美感。我发现我需要切换到一个黑暗的主题,以提高可见度。有些仅仅是数据模型的改变或者是为了清理。但是,对于每个主要的版本变更,我只花了不超过几个小时就完成了。其中大部分只是处理一些没有经过深思熟虑的代码。

经验教训

撇开我自己的坏习惯不谈,我从这次经历中总结了几点。

Dash 有一些强大的功能。不幸的是,有些事情是板上钉钉的。其中之一是主文件的格式。虽然可以将一些文件分解成其他子文件,但这更像是艺术而不是科学。某些事情需要以特定的顺序执行,试图在这些规则内定义有组织的代码是我失去耐心的事情。

面对这种情况,我只是在时间和麻烦中减少损失,尽我所能组织起来。因为只有我在维护它,我对东西在哪里有感觉,所以这没什么大不了的。多成员团队应该对此进行规划,并努力遵循相关规则。如果让我再做一次,我很可能会这样做,我会花更多的时间在一些更好的组织上。

如果初始化过程中出现错误,Dash 将不会启动。这似乎很明显,但它强制执行了一些通常是良好实践的编码规则。

例如,本周早些时候,我试图从一个 API 在线获取数据。由于各种条件的限制,服务器受到了流量的冲击,显然已经不堪重负。这是属于“最好拥有”和“必须拥有”类别的数据,但检索失败使应用程序无法工作。

我应该从一开始就这样做,但是我必须编写一些关于连接的测试,并提供应急措施来解决这个问题。我不会责怪 Dash,因为没有测试可能的故障点是我自己的错,但这是需要注意的事情。

从好的方面来说,我发现当我想以不同的方式探索一些不同的数据时,简单地旋转另一个类似的配置并改变显示的特定图表并没有什么。对于我的用例来说,这非常有效。它让我专注于特定的主题和内容,而不是花费大量的时间或麻烦从头开始制作新的东西。

结论

Dash 和 Plotly 为我提供了一个解决问题的方法,这个问题在很大程度上对我来说是独一无二的。然而,如果我仍然在教室里教书,或者在公司环境中做高度技术性的演讲,我会探索这种类型的解决方案。

当谈到数据演示时,仪表板不是大多数人脑海中出现的第一个东西,但我可以说,像我这样使用它提供了一个新的维度,这是我使用 powerpoint 或类似工具所没有的。

虽然它并不适合所有人(因为这是一个非常小众的话题),但如果你想看仪表盘运行的现场演示,请查看我的一个视频(此处),用它来剖析市场。我推荐一个更新的样本——毕竟我不是凯西·奈斯塔特。

我希望您能发现这些信息,并对我们经常使用的工具的一些替代用例有所启发。如果你有任何问题,请给我留言,我会很乐意尽我所能给予回复。

商业中机器学习的真实世界课程

原文:https://towardsdatascience.com/real-world-lessons-for-machine-learning-in-business-3144c22b553c?source=collection_archive---------41-----------------------

2021 年 AWS 机器学习峰会主题演讲要点

马里奥·梅萨利奥在 Unsplash 上的照片

最近,机器学习似乎得到了所有的关注和炒作,有些人甚至说它将成为主流。甚至还有专门针对 ML 的会议和峰会,就像 2021 AWS 机器学习峰会。在我看来,为了让 ML 成为主流,我们仍然需要现实世界的经验来将 ML 转化为商业产品,我希望从这次峰会中获得一些收获。我在这里列出了一些对我影响最大的部分。希望当您计划应用 ML 时,您会发现这些很有用:

  • 比萨饼与适量的奶酪例子与 Swami Sivasubramanian
  • 与 Yoelle Maarek 的计算幽默对话
  • 弄脏你的手和吴恩达聊天

由于该演讲由 AWS 组织,峰会倾向于使用他们的 ML 服务,但这里的要点也可以应用于其他云计算平台,如 GCP,他们提供自己的 ML 服务。

配有适量奶酪的披萨

ML 正在走向主流的一个可能的标志是它在各种行业中的使用,包括比萨饼行业。Swami Sivasubramanian 展示了dafgrds作为一个即使在有限的训练数据集下也能应用 ML 的企业的例子。他们希望使用 ML 来提高在每个披萨面团中放置适量奶酪的质量和效率。是的,从我喜欢的东西开始多好啊:ML 和披萨。

大卫·贝内什Unsplash 上拍摄的照片

他们使用视觉质量检查与少数镜头学习可以学习一个特定的任务,只有几个例子。我认为这是一个具有有限训练数据集的 ML 应用程序的相关用例。数据收集——获得正确的种类和数量——通常是 ML 项目开始时的一个难点。更进一步,我对 AWS 展望愿景很感兴趣,这是从客户反馈和这几个镜头学习想法中概念化出来的。

也有你提到的其他公司和企业,你可能听说过,像宝马和纽约时报,但比萨饼的想法仍然存在。

计算幽默

为了让 ML 成为主流并坚持下去,关注用户体验也很重要。而且,非常有趣的是,AWS 正在认真研究计算幽默,并在这项研究中花费了大量时间。我确实喜欢他们将 ML 团队融入产品团队的方法。在这个例子中,计算幽默小组一直与亚马逊 Alexa 购物团队合作。

在寻找取悦客户的方法时,我不禁想起了最近人工智能聊天机器人的一项令人耳目一新的进步。在谷歌的 LaMDA 项目中与冥王星和纸飞机等无生命物体对话!

用谷歌的 LaMDA 程序与冥王星和纸飞机对话

现在,回到计算幽默,团队不只是让人工智能有趣,而是想知道客户是否有趣,以及人工智能应该如何反应。在这种情况下,客户是采取主动的一方。

为了检测和可能的反应,该团队甚至试图查看文化背景、讽刺或顾客是否有心情开玩笑。我喜欢这个演讲还提到了混合主动性理论,救济理论等等。

例如,对于任天堂 Switch·格雷的欢乐大会产品,一位顾客在回复中提到了一些文化背景:“我可以用它来侵入黑客帝国并拯救人类吗?”

马库斯·斯皮斯克Unsplash 上拍摄

另一位顾客在一个非常昂贵的饮水机上讽刺道:“这东西能让我飞起来吗?这似乎是由于价格,必须做一些特殊的事情。”

是的,现在的 ML 状态已经可以做这么多了,但是还有很多可以探索的,还有新奇的使用方法。

弄脏你的手

然后,还有吴恩达和斯瓦米·西瓦苏布拉马尼安的炉边谈话。在那次不到 30 分钟的聊天中,有很多有趣的地方,但对我来说,最精彩的其实是最初的几分钟!

第一部分是为公司提供一些说明,针对他们的领导者,如首席执行官和首席技术官,他们希望启动他们的机器学习。当你第一次向大联盟迈进时,你必须把手弄脏。

花太长时间去计划和开始可能是一个错误。对于第一个项目,数据可能很乱,但可能有解决方法,企业应该能够从小的试点项目或概念证明开始,并迅速取得成功。从这些项目中学习成长和扩展项目。

我确实喜欢在形成机器学习的长期战略之前尝试一个试点项目的想法。从 POC 中获得的知识将有助于制定与公司目标和人员文化更加一致的战略。

我在之前的一篇关于强化应用于商业问题的文章中提到过,RL 的最大障碍在于问题公式化,接下来的步骤将由公式化的问题决定。所以,在问题的表述上要小心。但即使这样说,也不应该阻止人们弄脏自己的手,仍然尝试。从长远来看,可能会有一个 ML 平台,但总是那些第一次 POC 和快速成功提供了对 ML 旅程方向的见解。

现在,即使对于非领导角色,当开始一个 ML 项目时,对于一个全新的应用程序,可能很难创建目标指标来检查 ML 是否成功,直到 ML 团队完成了概念验证,或者文献中是否有相关项目来帮助定义基本级别的性能。这里的关键很可能是去快速和肮脏的原型系统,迭代,并从这些经验中学习。

离别笔记

越来越多的公司采用 ML,但这种增长不会很快放缓。它可能始于科技公司,但 ML 项目现在渗透到其他公司,包括那些披萨公司。峰会的关键部分和反复出现的主题是将 ML 转化为产品。

解决像拥有大型训练数据集这样的棘手问题至关重要,因此拥有像少数镜头学习这样的技术是一个受欢迎的变化。为了这些 ML 项目的成功而不仅仅是炒作,客户体验应该被放在首位。投资于解决客户体验的研究,比如那些具有计算幽默的研究,不仅仅是一个附带项目,而是一个人工智能产品的重要方面。而且在 ML 的世界里时不时的看到不同的视角也只是让人耳目一新。考虑工作方式,例如将 ML 团队嵌入到产品或开发团队中,可能是项目成功的关键部分。

将 ML 转化为产品也意味着超越理论,尤其是对于那些刚刚开始 ML 之旅的公司来说,尝试小规模的试点项目并取得立竿见影的效果有助于为公司的 ML 长期战略提供一些指导。

现实世界的 ML:仓库识别系统

原文:https://towardsdatascience.com/real-world-ml-warehouse-recognition-system-7b0e8dee373c?source=collection_archive---------43-----------------------

理解大数据

真实 ML 项目的逐步描述

图片来源于知识共享 CC0 许可下的 pxhere 。左上角的小图片是作者的。

作为 ICL 服务公司快速人工智能解决方案原型团队的团队领导,我最近有机会参与的一个项目是实现一个仓库系统来识别存储的工厂零件。这个问题很容易理解:仓库工作人员(尤其是新手)经常不能识别新到的物品,也不能足够快地找到他们的存储位置。当您有成千上万种不同的商品类型时,任务就变得不简单了,变成了翻阅目录和浏览仓库行,希望找到您感兴趣的商品,这可能要花上半个小时,显然是浪费时间。

一个立即想到的解决方案是建立一个由计算机视觉驱动的系统,告诉你感兴趣的物品的名称,并显示类似物品在仓库中的位置。

直接的解决方案是拍摄每个零件的数十张照片(大约 1 万张),并使用这些照片来训练零件分类器。然后随着新零件不断被添加到目录中,拍摄更多照片并重新训练系统。这种解决方案是可行的,但是……创建这样一个训练数据集需要几个月的时间,之后还需要对不断增长的数据集进行永久控制。但是客户通常希望更快、更便宜地得到结果,并且不需要定期支付昂贵的系统手动再培训费用。

我们能做些什么吗?

我们很幸运,我们的答案是肯定的。一个细微的差别使我们能够显著降低项目的成本和持续时间,在我们的案例中,我们处理一个工厂仓库,储存的零件都是平的,尽管它们是由不同厚度的钢板制成的。更重要的是,所有零件都有 CAD 模型。

图一。Blender 中的 CAD 模型示例2

因此,我们提出的最佳解决方案是在 CAD 模型生成的合成图像上训练系统,然后在真实照片上使用它。这种方法消除了从真实物体的照片中收集大量数据集的必要性。在我们的例子中,这是可能的,因为所有的部分都是平的。

为了实现这一目标,我们使用了两种模型:

  • 分割模型,其将为对象的输入照片给出遮罩(平坦对象的遮罩唯一地定义了一个部分;掩模的例子可以在下面的图 5 中找到);
  • 分类模型,查看作为输入的对象的掩码,并识别零件。

分类模型是经典的 ResNet-50 [3],在 ImageNet 上预先训练[4]。数据集是以一种相当简单的方式创建的。使用 Blender [2]的可用 CAD 模型和脚本,我们使用从场景中心的各种偏移和相对于垂直方向的不同相机角度来渲染我们的部件的遮罩(这是必要的,因为尽管部件是平的,相机可以在不同的角度下拍摄它们;我们允许偏离垂直方向最多 30 度)。类别的数量等于目录中零件的数量。当新零件添加到目录中时(即在专用网络共享上检测到新 CAD 模型时),模型会自动重新训练,这需要几个小时,并在并行用于推理的同一本地 GPU 上进行。

细分模型有点复杂。有必要训练模型来使用合成数据分割这些部分,以便此后分割将在不同照明水平下对真实照片精确地起作用,容忍这些部分的材料纹理的变化、背景的变化以及阴影。

分割模型包括一个经典的 U-Net 5,基于相同的 ResNet-50 [3],在 ImageNet 上预先训练[4]。虽然并非不重要,但用于训练分割图像的这种合成数据集是通过模拟我们从以下内容的随机组合中组成的部件的各种可能外观来构建的:

  • 零件口罩(严格来说任何口罩都行,不仅仅是工厂零件的口罩);

图二。用于生成零件合成图像的遮罩示例(图片由作者提供)

  • 背景纹理(来自任何免费剪贴画集合);

图 3。用于生成零件合成图像的背景纹理示例。(左)照片由来自 Unsplash杰森·登特拍摄。(右)作者照片

  • 部件材料纹理(来自任何免费剪贴画集合);

图 4。用于生成零件合成图像的材质纹理示例。(左)照片由 Malik SkydsgaardUnsplash 拍摄。(右)来自 Unsplash安妮·斯普拉特的照片

  • 边缘上的眩光可能看起来像什么(参见下面图 6 中的生成示例);
  • 零件边缘如何将阴影投射到背景上(参见下面图 6 中的生成示例);
  • 阴影如何投射到整个场景上(见图 6 中的生成示例)。

这个主题相当复杂,但是一个像样的计算机图形专家将很容易解决在合理的时间内生成用于分割的合成示例的问题。我不打算在这里给出公式,而只给出掩模的例子(图 5),它们相对容易从 CAD 模型中获得,以及从这种掩模程序性地生成伪照片的例子(图 6)。

图 5。用于生成零件合成图像的零件遮罩示例(图片由作者提供)

图 6。从遮罩生成的零件的合成图像示例(图片由作者提供)

让我们增加一些补充。现在训练分段器的例子看起来像这样。

图 7。用于训练分段模型的增强示例(图片由作者提供)

训练数据准备好之后,剩下的事情就简单了。我们训练分割模型来分割图像,并且训练分类器模型来对部分遮罩进行分类。然后,我们在测试集上检查分类的准确性(在我们的例子中,我们有 177 张真实零件照片)。

图 8。测试集的几个例子的分类结果(图片由作者提供)

我们得到了:

正确预测:100.00% (177 个中的 177 个)

好吧,我们很幸运,整个测试集被 100%正确地识别,尽管每次运行的结果可能略有不同。随机性来自测试时间增加(TTA) [8]技术,我们用于分割和分类,因为 TTA 可以减少约 10%的误差。因此,分类过程是非确定性的,并且依赖于 TTA 的随机种子。如果我们查看 10 次运行的平均准确度,那么结果是大约 99%(稍后我们将获得更客观的数字,届时我们将获得大量真实图像并组成一个全面的测试集)。

剩下的就是把它包装在一个简单的用户界面中,在第一个版本中是这样的。

图 9。该系统第一版的用户界面(图片由作者提供)

在这里,我们看到来自摄像机的图像(在这种情况下,测试台сamera,我们在它下面放置打印的零件黑白照片),通过分段模型和前 16 个模型预测处理零件的结果(如果模型偶尔在前 1 个预测中出错,则有超过 99.9%的概率在前 16 个列表中找到正确的零件)。对于预测零件,我们可以在仓库地图上看到它的名称和位置,以及货架号的附加指示。

因此,仓库工人现在总是有办法快速了解他们遇到了哪种零件以及这些零件存放在哪里。为此,他们只需将零件放在仓库入口处摄像机的视野范围内。

PS:

本文是计划中的“真实世界的 ML”系列的第一篇,我计划在这里分享创建涉及 ML 的产品的故事,这些产品是我们与我们的数据科学家团队一起构建的。

我还开了一个微博https://twitter.com/AiParticles,在里面我概述了机器学习领域最近的前沿著作中的关键点和想法。

请在评论中留下您的反馈。祝大家好运!

参考文献

[1]https://icl-services.com/eng ICL 服务—

[2]搅拌机—https://www.blender.org

[3],何,,,任,,等.深度残差学习用于图像识别.https://arxiv.org/abs/1512.03385

[4] Olga Russakovsky,Jia Deng,,Jonathan Krause,Sanjeev Satheesh,Sean Ma,,Andrej Karpathy,Aditya Khosla,Michael Bernstein,Alexander C. Berg,李菲菲等. ImageNet 大规模视觉识别挑战赛.https://arxiv.org/abs/1409.0575

[5] Olaf Ronneberger,Philipp Fischer,Thomas Brox,等. U-Net:用于生物医学图像分割的卷积网络.https://arxiv.org/abs/1505.04597

[6] Fausto Milletari,Nassir Navab,Seyed-Ahmad Ahmadi,等. V-Net:用于体积医学图像分割的全卷积神经网络.https://arxiv.org/abs/1606.04797

[7]数据扩充—https://en.wikipedia.org/wiki/Data_augmentation

[8]测试时间增加—https://stepup.ai/test_time_data_augmentation

使用深度学习的草图中的真实人脸图像

原文:https://towardsdatascience.com/realistic-face-images-from-sketches-using-deep-learning-700952c01c7b?source=collection_archive---------22-----------------------

DeepFaceDrawing 的环境设置和模型实现快速指南

从模型生成的摘要图像。来自深面绘制的草图

你有没有想过深度学习和人工智能的极限?8 年前, AlexNet 在 ImageNet 2012 挑战赛中取得了 15.3%的前 5 名误差,这在当时是不可思议的,这要归功于 CNN 和 GPU 训练。3 年后的 2015 年, ResNet-152 显示只有 3.57%的误差,比这项任务 5%的人为误差要好。同样在 2015 年, DCGAN 向世界展示了深度学习算法不仅可以对物体进行分类,还可以创造新的物体。2017 年末,Google 推出的transformer architecture不仅带来了比 NLP 领域的 LSTMs 更快的训练和更好的结果,还通过带来注意力模块挑战了计算机视觉世界。

感谢所有为深度学习领域做出贡献的人,我们有越来越多以前想不到的应用,这就引出了今天的话题,从手绘草图生成逼真的人脸图像。首先,功劳归于研究人员,感谢开源代码。我将简要介绍模型架构,然后我们将亲自动手在本地部署这个模型!

模型架构

来自论文的架构

如上面的架构所示,该模型分为三个部分,即组件嵌入(CE)模块、特征映射(FM)模块和图像合成(is)模块。尺寸为 512×512 的手绘人脸图像的输入首先被分解成五个部分:“左眼”、“右眼”、“鼻子”、“嘴”和“余数”。“眼睛”、“鼻子”和“嘴”通过取大小为 128、168 和 192 的热窗分开,而“剩余部分”实际上是草图的剩余部分。然后使用 5 个具有 512 维潜在描述符的自动编码器对这五个分量进行特征编码。组件的特征向量被认为是底层组件流形的点样本,并通过使用 K 最近邻将其各个部分投影到相应的组件流形,用于细化手绘草图,如流形投影部分所示。

组件的单个特征向量被投影到流形上以增加它们的可信度。在 FM 模块中,作者没有将每个分量向量解码回图像,然后在分量级别上进行合成,而是选择将向量的草图融合到一个完整的面部,然后生成完整的图像,因为这有助于在局部细节和全局风格方面获得更一致的结果。给定组合的特征向量图,IS 模块使用条件 GAN 架构将它们转换成真实的人脸图像。他们还应用了两阶段训练方法,其中阶段 1 仅 CE 模块中的五个单独的自动编码器使用不同的组件草图进行训练。然后在阶段 2 中,固定阶段 1 中的参数,FM/IS 模块协同工作,通过训练 GAN 生成人脸图像。

履行

在此之前,如果你只是在寻找一个用应用快速测试绘图的话,一定要去看看项目主页,在那里原作者创建了一个基于 web 的 测试界面

开始这个博客的动机来自于我试图实现最初的 GitHub 库。由于 Jittor 需要 Ubuntu > = 16.04,而大部分人都在用 MacOS/Windows,那么最简单的方法之一就是使用 Docker 从头开始搭建工作环境。

先决条件

  • 系统:MacOS/Windows/Linux(任何支持 docker 的 OS)
  • RAM:最低 8 GB,运行模式将占用大约 6 GB
  • 磁盘空间:最小 5 GB
  • GPU:没有都可以,除非你想做进一步的训练

1.下载 Docker

在你的操作系统中下载并安装 Docker

2.打开您的终端/CMD 或您选择的任何命令行界面

在这里,我们将为运行模型构建 docker 映像。首先创建一个 Dockerfile 文件,包含以下内容。

vim Dockerfile

然后构建 docker 映像并运行它。在 Dockerfile 的同一个目录中,

# Build the docker image called ubuntu_for_deepface
docker build -t ubuntu_for_deepface .# Start a container called ubuntu_env and memory limit of 8GB
docker run -it --name ubuntu_env --memory=8g ubuntu_for_deepface

或者,你可以拉我创建的预编译 docker 镜像,避免创建和编译 Dockerfile。

# Pull the docker image from Docker Hub
docker pull frank0124/drawing-to-face

# Start a container called ubuntu_env based on the pulled image
docker run -it --name ubuntu_env --memory=8g frank0124/drawing-to-face

您应该能够看到终端/CMD 提示符更改为

root@xxxxxxxxxxxx:/#

这意味着您在正在运行的 docker 容器中。Docker 一开始可能会很难处理,但如果你利用搜索引擎并通读一些文档,你就能掌握它。

3.下载预先训练的重量

原来的回购已经在集装箱里了。在这里,我还通过从我的 GitHub 中删除不必要的特性,提供了一个简化版本的代码。在容器中,

git clone [https://github.com/franknb/Drawing-to-Face.git](https://github.com/franknb/Drawing-to-Face.git)
cd /Drawing-to-Face/Params# If you are using the original code, simply
cd /DeepFaceDrawing-Jittor/Params

然后下载预先训练的重量,

wget https://www.dropbox.com/s/5s5c4zuq6jy0cgc/Combine.zip
unzip Combine.zip && rm Combine.zip
wget https://www.dropbox.com/s/cs4895ci51h8xn3/AE_whole.zip
unzip AE_whole.zip && rm AE_whole.zip
cd ..

4.试运行并检查结果

cd /Drawing-to-Face
python3.7 example.py

在它运行之后,您应该期望在运行容器的 /results 目录中生成图像。很难看到结果。jpg 文件,这需要你在运行 docker 之前设置 X11 socket。这里我推荐简单的“复制粘贴”方法。

在您自己的操作系统上打开一个新的终端/CMD,

# This line of code copies the results folder to your current directory in your OS
docker cp ubuntu_env:/Drawing-to-Face/results ./

然后,您可以简单地转到运行终端/CMD 的当前目录,并找到粘贴的图像。

或者,如果运行的是原来的 test_model.py ,查看本页获取详细说明。

5.后续步骤

现在您知道了模型可以在您自己的环境中运行,是时候绘制和运行更多的测试了。

首先,您想要创建一些 512 乘 512 大小的草图 jpg 文件。你可以把它画在任何你想画的地方,在 iPad 上,甚至在纸上,然后给它拍照。然后确保它有正确的格式和大小,我推荐 Photoshop 来处理这个过程,以便于控制大小/格式。如果你还没有准备好自己制作人脸草图,也可以参考这个测试界面。它有一个基于阴影的画板,可以给你一些绘画的参考。把所有你想测试的图像放在一个名为 test 的目录/文件夹中。

现在,在 docker 中,您想要删除现有的测试图像以避免再次运行它们。

# In the running container
rm /Drawing-to-Face/test/*

最后,再次打开 terminal/CMD 并找到您创建的测试目录,其中包含您想要测试的映像。

# In your own OS
docker cp test ubuntu_env:/Drawing-to-Face/

你的下一步是什么?也许通读模型部分的代码以更好地理解它是如何工作的,或者构建一个利用该算法的应用程序。

模型性能

实际产出 1

让我们看看上面的一些模型输出。所有案例都是在所有优化参数都设置为 0.5 的情况下生成的,这是该模型的秘方以实现更好的图像生成。总共有 6 个参数(性别、眼睛 1、眼睛 2、鼻子、嘴和余数)可以为生成的一张图片设置。在我提供的 example.py 中,我将所有细化参数设置为 0.5,性别设置为 0(男性),而在原 test_model.py 中,作者给出了 5 张测试图片 30 个参数的矩阵。

左边的第一张草图有一点卡通风格,特别是大眼睛,略高的眉毛和奇怪的鼻子。在生成的图像中,我们可以看到模特努力地把眼睛和眉毛放上去。对于头发、脸、嘴和耳朵,模型在使结果尽可能真实方面做了出色的工作。左起第二张草图也是如此,有夸张的眼睛/嘴巴形状,模型确实在草图和现实之间取得了平衡。

与前两张相比,左起的草图 3 到 5 更“现实”。我们也最终得到了更好(更真实)的生成图像,接近前面预告图像的质量。

实际产出 2

上面的模型输出是基于上面的第五张草图,其中性别设置为女性。我们对所有优化参数集进行了从 0 到 1 的完全转换。如论文所述(求精参数= 1-置信度参数),

𝑤𝑏𝑐 = 1(置信度参数)意味着充分利用输入草图进行图像合成,而通过设置𝑤𝑏𝑐 = 0,我们完全信任用于插值的数据。此混合功能对于创建与任何现有样本或其混合非常不同的面特别有用。

我们可以看到,草图发型(带有刘海)无法从现有的样本或其混合中获得,但我们可以通过将参数设置为 0.5 左右来生成带有刘海的图像,介于忠实和真实之间,这使我们能够获得类似草图但真实的输出。

你可以在我的 GitHub 查看所有的源代码。

posted @ 2024-10-17 11:36  绝不原创的飞龙  阅读(304)  评论(0)    收藏  举报