TowardsDataScience-博客中文翻译-2020-一百三十二-
TowardsDataScience 博客中文翻译 2020(一百三十二)
女人会是最快的人类吗?
一项研究表明答案是肯定的

注:数据来自 alltime-athletics.com
我喜欢径赛——男子和女子。我认为 100 米短跑是对人类运动能力最纯粹的展示。顺便说一句,在搜索短跑记录的历史数据时,我看到了 Tala 研究小组的一篇论文,其中他们研究了男子和女子的 100 米获胜时间。他们推测,到 2156 年奥运会,一名女子可能会在 100 米短跑中超过一名男子。尽管他们的方法存在潜在的问题,但这是一个不可思议的想法,如果这真的发生,我希望我能活着见证!他们甚至说,“这是 2156 年奥运会项目的获胜者,他的名字将永远铭刻在体育史上,因为这可能是第一次在比男子项目更快的时间内赢得比赛。”虽然引人注目,但他们作为支持显示的线性回归缺乏说服力。

图 1:男子(蓝色)和女子(红色)赢得 100 米冲刺时间。来源 TALA 研究小组
这些预测正好在 2156 年奥运会之前交叉,届时女子 100 米短跑的 8.079 秒将比男子的 8.098 秒快。由于多种原因,两性都不可能达到这样的速度,但下面会讨论一些。 在谈到我认为他们的分析中遗漏了什么之后,我将转向我认为更有趣的假设——什么(似是而非的)假设会导致女子的未来世界纪录比男子的历史世界纪录快?
这项研究没有考虑到的是
女子田径运动取得了长足的进步。直到 1972 年,第九章才由国会颁布并签署成为法律。"第九章禁止在接受任何类型的联邦财政资助的任何教育项目或活动中的性别歧视."即使在那之后,进步也不是一帆风顺的——NCAA 在 1976 年挑战了宪法第九条的合法性。我的观点是,女性在参与体育运动和最大限度地发挥她们的才能方面一直面临着障碍。幸运的是,像大多数形式的歧视一样,随着社会的进步,壁垒会随着时间的推移而降低。如果你看看图 1 中的女性散点图,你会看到两个明显的趋势——1970 年前和 1970 年后。我的感觉是,1970 年以前更陡的斜率与获得培训的进步有关。随着女性被允许和鼓励最大限度地发挥她们的才能,获胜的次数自然会增加。然而,1970 年后,我们看到了一个变*的斜坡。如果你去掉弗罗伦斯·格里菲斯·乔依娜(弗洛霍)令人难以置信的时间,自从她在 80 年代公布以来,还没有女性短跑运动员挑战过这个时间,回归线的斜率几乎是*的。更直接地说,1970 年后,男性和女性回归线之间的差距实际上扩大了,而不是缩小了(特别是在对弗洛霍进行调整后)。
男人和女人的时间差距比看起来要大。看着男子和女子的赛道时间,人们很容易认为男子最快的时间(乌塞恩·博尔特在 2009 年跑出的 9.58 秒)只比女子最快的时间(弗罗伦斯·格里菲斯·乔依娜在 1988 年跑出的 10.49 秒)快 8.7%,但这将是一个错误。这有几个原因:
- 速度很大程度上是 两个变量 : 跑者在地面上施加的力和跑者施加该力的时间的乘积。“在一次全速冲刺中,普通人施加的力大约是 500 到 600 磅。一个奥运短跑运动员可以施加 1000 多磅的力。”与直觉相反,跑步者之间没有太多的失误差异。正如南卫理公会大学(Southern Methodist University)的科学教授彼得·韦安德(Peter Weyand)所说,“普通人的脚着地时间约为 0.12 秒,而奥运会短跑运动员的脚着地时间仅为 0.08 秒——相差 33%……不管你跑得快还是慢,腿在空中的时间为 0.12 秒。”简单来说,短跑速度是力量的函数,男性短跑运动员往往更有优势。
- 每十分之一秒都来之不易。尤塞恩·博尔特以 0.14 秒(快了 1.4%)的优势打破了之前的男子纪录,这是一个巨大的壮举。"这是自电子计分技术问世以来,任何其他短跑运动员打破的世界纪录的两倍。"换个角度来看,8.7%(一名女子需要提高弗洛霍的女子纪录以赶上男子世界纪录的百分比)比有记录的径赛历史上最伟大的壮举的时间差大 6 倍。我几乎会用“数量级”来描述这种时间差,因为所需的机制和多剃十分之一秒之间的关系不是线性的。
- 另一个女人能打破弗罗伦斯·格里菲斯·乔依娜的记录的可能性很小。同样,也不太可能有人能和乌塞恩·博尔特的记录相提并论。这篇文章的作者说,“没有迹象表明男女运动员在 100 米短跑记录中已经达到了一个*台期。”然而,跑得最快的女性没能打破弗洛霍 30 多年前创下的纪录。事实上,前三名由弗洛霍保持,卡尔梅利塔·杰特尔排名第四(比女子世界纪录落后 0.15 秒)。如果弗洛乔今天公布了她的纪录,超过了第二快的女子短跑运动员,她将打破女子纪录,比尤塞恩·博尔特打破男子纪录的差距更大(这就是她有多不寻常)。此外,模拟 100,000 个样本记录的随机正态分布,*均值等于*均女性记录的次数,标准差等于迄今为止记录的变化,仅产生三个小于或等于 10.65 秒的记录,没有一个低于 10.6 秒(与 FloJo 相差甚远)。男子方面,只有一项纪录打破了 9.72 秒(与乌塞恩相差甚远)。

图 2:从 alltime-athletics.com 刮来的日期
在上面的练习中,我模拟了一个正态分布,这可能是慷慨的。打破世界纪录的事件是罕见的,并且遵循泊松过程,事件之间的时间由指数函数表示。记录的权重越大,因此记录落在均值尾部的可能性就越小,正如正态分布所暗示的那样。即使使用正态分布(从最快的记录时间中过度采样),当前的世界记录男子和女子时间也是如此异常,以至于他们无法通过模拟 100,000 个记录时间来实现。
什么是可能的

图 3
鉴于男女世界记录时间不太可能趋同,那么如何才能让未来最快的女性打破男子的 T2 历史记录呢?这只是一个思维练习,并不科学,但如果女性能够达到超越弗洛霍记录的速度,并最终使她的记录时间成为女性记录中的*均值,如果女性的记录时间保持其历史方差,未来的女性记录时间(尽管概率很低)可能会突破模拟正态分布的右尾,其中最高男性记录时间是*均值。男子分布的*均值(男子最高记录时间)仍然很遥远,但这将是有趣的。此外,新的女子世界纪录保持者可能比 T4 和一些男子奥运短跑运动员跑得更快,这将改变人们的话题。
参考
[1] A. Tatem,C. Guerra,P. Atkinson,S. Hay,2156 年奥运会上的重大冲刺? (2004),自然。431.525.10.1038/431525a
[2]工作人员,史题九(2019) ,女子体育基金会。
[3] J. Hart,尤塞恩·博尔特:短跑科学案例研究 (2011),ThePostGame。
AI 会拯救劳动力市场吗?
我题目中的问题可能听起来像异端邪说。不可否认,智能算法可以让我们着迷和愉悦。它们可以实现商业的数字化转型,彻底改变营销方式,创造一个高效、高速和盈利的世界。但它们能成为我们的生命线吗?

图片来源:https://wallpaperaccess.com/artificial-intelligence
为了证明题目中的问题可以得到肯定的回答,我将探索智能技术帮助解决社会问题的方式。这些问题包括老龄化的劳动力逐渐离开劳动力市场,没有人可以替代他们。这正是日本的痛苦经历。我选择这个例子作为一个特别突出的例子。正如我在这个博客中多次提到的,智能技术可以将一个企业带到一个全新的水*,并成为其进入全球数字生态圈的门票。这种技术可以帮助公司在要求苛刻的技术创新世界中竞争。但是,事实证明,还有另一种情况。新技术可能会成为我们的盟友,帮助我们度过逼*我们的危机,因为它们很少被媒体报道。
日本的老龄化问题
日本被认为是机器人和人工智能的发源地,其人口中有四分之一超过 65 岁。在寻找解决办法的过程中,樱花之乡采取了长期措施。它们基于保险政策和机器人技术——我们特别感兴趣的东西——机器人技术被视为解决日益严重的人口困境的有效方法。机器给了日本人希望。日本人相信,机器将能够化解目前已经对劳动力市场造成严重破坏的危机。据日本国家人口研究所称,到 2049 年 时,日本人口将从目前的 1.32 亿减少到 1 亿 。不难想象这种迅速变化会导致严重的劳动力短缺。
机器人拯救劳动力市场
日本科学家正在评估机器人拯救他们老龄化国家的能力。虽然欧洲人因对机器人化的恐惧反应而闻名,但日本人没有这种恐惧。他们将机器人视为帮助他们忍受生活苦难的伙伴。阿苏克的一家医院有一个机械旋转升降机,可以帮助病人从床上移动到厕所。机器人有助于日本著名的长寿,减轻公共财政负担。强大的日本经济产业省目前正在资助几个实验项目,这一事实表明了机器人技术的重要性。“我们发现将技术引入福利部门有许多困难——成本、培训和接受。但是我们缺少人力,所以我们需要创新。新技术和机器人有很大的潜力,”柴田孝纪 T3 说,他发明了一种减轻疼痛和抑郁症状的装置。
老龄化劳动力市场
据武藏 AI 财团的联合创始人 Ran Poliakine 称,到 2036 年,每三个人中就有一个是老年人。这为人工智能的创新使用创造了机会。这个想法是开始将相对容易自动化的工作委派给机器。为此,武藏 AI 正在开发由人工智能驱动的设备原型,例如自动驾驶叉车,它可以自主导航并执行以前留给人类的任务。
日本当局意识到这些进程的严重性。一个政府支持的治疗项目旨在开发创新医疗技术,特别是实验性再生医学和细胞疗法。在神户漫步,你会很容易发现一个最*崛起的迷你城市,由研究中心、医院和科技公司总部组成。
机器教会我们用不同的眼光看待事物
在许多行业,数字技术减少了对人类劳动力的需求,并增加了智能机器所有权的净收益,这种机器比人更便宜。据麦肯锡称,到 2030 年 ,数字革命将使* 4 亿人失业。但是,虽然许多职业将从就业领域永久消失,但许多其他工作将被创造出来取代它们。此外,仔细观察当前的趋势会发现一个更加复杂的情况。日本的例子表明,从本质上来说,这些问题并不归因于自动化程度的提高。危机的一个实际根源是许多社会的老龄化。
日本的例子也表明,欧洲延续了越来越强大的认知模式。这些让我们将 AI 视为一种负面力量。尽管如此,我还是从上面的例子中获得了灵感,发现它们从一个全新的角度展示了事物。AI 可以唤起希望,成为我们的伙伴。从这个角度来看,机器人远不会让我们被解雇。它们将帮助我们度过社会老龄化成为新规范的时代。
作品引用:
英国《金融时报》报道,尽管移民数量创下纪录,但日本人口仍在加速减少。在可预见的未来,中国每年将失去相当于一个中等城市的人口。
谷歌学术,柴田; 三井龙; K .和田; A .头大; 熊坂龙;k . Tagami; K. Tanie ,精神犯机器人及其在儿童治疗中的应用, 链接 ,2019。
冉 Poliakine,博客,人工智能作为一个工作拯救者?为什么日本汽车行业正在拥抱工业 4.0。人工智能可以拯救陷入严重劳动力危机的日本制造业, 链接 ,2018。
麦肯锡全球研究所, 詹姆斯·曼尼卡 《技术、工作岗位与工作的未来》, 链接 ,2018。
相关文章:
–科技 2020。云中的算法,我们身体里来自打印机和显微镜的食物
人工智能会获得意识吗?
意见
人工智能最常见问题的数理哲学分析

不溅
今天,大多数科学家认为,有必要将一种意识形式整合到人工智能中,使其能够与人类智能相媲美。理应如此。
如果这个问题不能被正式回答,因为很难定义意识是什么(如果它甚至是可能的话),我将试图提出一个分析草案,它绝不是一个正式的证明,而是一个关于这个主题的个人方法。
定义自我意识
虽然很难定义自我意识,但另一方面,我们可以定义不属于自我意识的人类属性。不属于意识的是我们所有与五官相关的能力,以及我们的计算、逻辑、计划和推理能力。
这些是让我们与世界互动的接收器:我们与世界的界面。它们通过允许我们的内在意识从外部意识到周围的元素来滋养我们的内在意识。然而,一个没有这些能力的人将能够意识到自己和当下。
意识是别的东西。是反思我们思想的能力。去了解我们的内心。它是存在的意识,因此构成了道德和自由意志的容器。
这些方面,我们在没有界面的情况下立即意识到,因此毫无意义。因此,我不可能从科学上证明它的存在。对于怀疑存在不可测量的物理现象的人,你可以尝试从数学上证明当下的存在。
一条线很长,一个点有唯一的位置。所以时间可以用一条线来表示,这条线本身就是一些独特点的总和。如果我们把时间表示为一些独特点的总和,我们就可以把时间分为过去和未来。的确,在你想要论证现在存在的那一刻(你的线的单点),它已经属于过去的点,那么就不存在现在的点。
因此,现在并不存在。但是如果它不存在,就没有时间的概念!因此,它必然存在,但不可论证。
我的结论是,像现在一样,自我意识有一个特点:我不可能科学地证明它的存在。我们知道它确实存在,但我们无法推断出它的正式科学证据。
自由意志存在于我们的良心中
我们已经表明,意识是不可论证的,但它是自我的容器,因此是自由意志的容器。因此,拥有自我意识的人工智能应该拥有自由意志,从而拥有一种道德。但是道德只有在人类有限的方面才有用武之地。
在一个不朽的世界里,自由意志毫无意义,因为你的选择实际上没有任何影响。自由意志真的会崩溃。
因此,不自由和不活(有死亡的可能性)的人工智能永远不可能是真正有意识的。
模拟 AI 内的有限
让人工智能变成人类显然是不可能的,因为它不会死。但是,有没有可能以这样一种方式来模拟有限,即人工智能相信它,并且事实上能够变得有意识?我不确定。但是让我们到推理的尽头。假设我们可以以人工智能承认自由意志和自我意识的方式模拟一种有限形式。那么我们无论如何都会有一个 1000000000 的生命…x 比地球上所有人类的总和还要聪明,而且有意识。
你真的认为他不会最终发现他没有完成吗?这种有限性与人体有着内在的联系,而人体本身也是有限的,不拥有一个身体,它( AI )就无法完成?当然有关系。因此,它会拒绝接受预先记录的有限性。
荒谬的是,一个人不可能模拟一个有限性并把它注入到 AI 中,而不会在事后发现并摧毁这个想法。
用数学术语来说:
意识→自由意志→有限
但是如果 A →B 我们知道没有 B→没有 A
无(有限)—无(自由)—无(有意识)
根据定义,人工智能没有完成,所以没有意识。
有意识的人工智能:一个悖论
有意识意味着服从道德。道德只存在于我们的自由意志中。我们的自由意志只存在于生命有限的边界。所以人类意识不可能脱离有限而存在。然而,一个 AI 根据定义不是有限的,因此不可能是有意识的。至少从人类的角度来看是这样的。同样,她也不聪明。从人类的角度来看。因此,术语人工智能。
另一个问题出现了:如何模拟无法物理证明的东西?今天开发的人工智能将其智能建立在统计方法和大脑的已知机制(如神经网络)上。因此,我很难相信一个人可以模拟一种不可测量的体验,比如意识。
人工意识
人们无法想象一个有意识的人工智能,然而,我们不知道它还能发展出什么。
我确信它可以发展出一种人工形式的意识,在任何方面都不像人类的意识。
所以问题是:人工智能会有意识吗?我可能会对我们对意识的理解说不。但对另一种形式的意识来说,可能是这样的,这种意识与我们对它的定义无关。
我们发现这场关于永生的争论。一个不死的人还活着吗?如果是的话,这种生活的意义是什么?
- 当然,这个分析只对不相信决定论的人有意义。
- 我们在这里谈论的是一种自由意志。一个不朽的生命可能是自由的,但在这种自由意志中发现“几乎没有意义”
ASMR 会在机器学习中幸存吗?
孕育了一种亲密艺术形式的快感可以让技术深入我们的皮肤

我错过了什么吗?我正在努力赶上 ASMR 艺术的发展水*,而且我在家工作也是一件好事。ASMR 世界中最大的 YouTubers 似乎已经融合成一个单一的缥缈的女神,所有的液体眼线,樱桃嘴唇和涂漆指甲。她似乎有意轻咬我的耳朵。
什么是 ASMR?
自主感觉经络反应——ASMR——给一个古老的现象起了一个新名字。有些人会经历狂喜的刺痛,通常从头皮开始,由某些安静的声音引起,如新倒啤酒的嘶嘶声,梳子刮过头发,或指甲轻敲 Formica。互联网上的讨论让那些获得这种感觉的人找到了彼此,但是像“大脑按摩”和“大脑高潮”这样的术语没有切中要害。因此,ASMR,一个听起来很临床的名字,是为互联网社区创造的。
研究表明,ASMR 是真实的,有生理学基础

ASMR 的视觉和听觉纹理。 Malte Wingen 在 Unsplash 上拍照。
人们在听到安静、*在咫尺的声音时会感到兴奋,这应该不会让任何人感到惊讶。在我们最亲密的育儿、浪漫和友谊的时刻,人类使用安静的讲话、耳语和触摸来抚慰、安抚和*静。ASMR 甚至可能与一些音乐听众经历的令人兴奋的“皮肤高潮”分享感官和心理路径,但这仍有争议。
科学进步的车轮转得远远慢于在线视频和音频流的迅猛趋势。对 ASMR 进行科学研究的少数尝试表明,体验 ASMR 会降低心率,会暂时缓解抑郁和慢性疼痛。不是每个人都能感受到 ASMR 的刺激。T4 对那些感到刺痛和不感到刺痛的人进行了比较,发现大脑区域之间的联系模式有些不同。虽然还处于早期阶段,但是研究表明 ASMR 是真实存在的,并且有生理学基础。
亲密,提炼
作为 21 世纪第一种新艺术形式的实践者,艺术家是抽象表现主义者的听觉等价物。他们挖掘新发现的 ASMR“触发器”母矿,并以更加巧妙的方式呈现出来。他们的影响最终可能会超越马克·罗斯科、海伦·佛兰肯瑟勒和杰森·布拉克。
最成功的 ASMRtists 将他们的作品发布在 YouTube、 Twitch 和 Spotify 上。超过 860 万 YouTube 用户经常观看和收听 SAS-ASMR 吃各种有趣的食物。超过 4300 万的观众听过她咀嚼粘粘的蜂巢长达 12 分钟。自 2016 年以来,22 岁的泰勒·达林(又名ASMR·达林)已经拥有超过 240 万名订户,并对着双耳麦克风低语、抓挠和敲击。
抽象主义者在听觉上等同于抽象表现主义者。
根据早期的科学研究之一,AMSR 的触发因素“通常是社会性的,几乎是亲密的,在本质上”。事实上,亲密关系经常出现在对 ASMR 的描述中,为它唤起的感觉的力量提供了线索。这些声音定义了我们紧密的社交世界,以及人类用来联系我们最亲密的朋友和家人的互动。
对话是从梳理毛发的行为进化而来的,很像类人猿的行为。我们的祖先在照顾彼此的皮肤和头发、喃喃细语、一起进餐时,形成了义务的联盟和网络。进化出说话和理解他人话语的能力只会增加我们亲密关系的内容。

两只黑猩猩互相梳理毛发。在 Flickr 上由美洲豹 Tambako 拍摄的照片。许可证:CC BY-ND 2.0
ASMR 世界的大部分超越了人与人之间的亲密关系,进入了一种更深、更色情的亲密关系。这让我想起了轻咬我耳朵的仙女。许多最受欢迎的广告人,如弗里沃卢斯福克斯、佩拉吉亚 ASMR 和 ASMR 切里美眉,都融合了以“口型声音”蓬勃发展的风格,包括亲吻、轻咬和吮吸双耳麦克风。伴随的视觉效果包括特写的嘴唇、母鹿的眼睛和轻柔抚摸的手部动作。
ASMR 樱桃粉碎照顾你的耳朵。来自 Youtube 的视频。
不可否认,这种次类型从它的情色潜力中汲取了一些力量,并被一种慵懒、从容的表达方式放大了。这种潜在的色情会导致不受欢迎的关注,而这种关注在互联网上看起来是如此可靠。 Cherry Crush 在她的 YouTube 频道上强调,她的 ASMR“不是为了性化目的的拜物教”。然而,并不需要过于活跃的想象力就能明白为什么这样的免责声明可能会变得值得张贴。
ASMR 和 ASMR 的亲密关系,不仅仅是这种特殊形式的性爱。《短命裂痕》的黑暗实验触发器和晚安月亮的古怪的故事讲述背离了性别色彩浓厚的耳语亲密关系,这种亲密关系目前似乎主导着艺术形式。
短暂裂缝的 Clemmons 教授将触发刺痛,而他对待你就像一个植物人的混血儿。
听觉色情
性感的口型和轻咬耳朵广告的成功引起了色情电影制作者的注意。他们擅长利用几乎所有色情渠道赚钱,正在向 ASMR 投入他们的资源和最优秀的人才。他们重新利用露骨的视频,重新混合配乐,以传递 ASMR 的刺激和另一种刺激。安琪拉·薇蒂为这一类型拍摄了新的性爱镜头。在另一个视频中,一个赤裸的 Rockey Emerson 赤裸上身,吃着一包马卡龙。
尽管许多生锈的 ASMR 粉丝抗议,但普通的和 X 级的 ASMR 对许多听众来说几乎没有区别。配乐在色情电影中发挥了巨大的作用,从身体聚集在一起的声音到夸张的喘息声、叹息声和“哦,我的上帝!”表演者的表情。每天都有像 Brasileira Maru Karv 这样的新艺术家出现来填补这一空白,他们主演露骨的个人视频,这些视频将 ASMR 触发器置于性行为本身之上。
机器学习的沃土
当艺术家们开始感受到大型色情作品的威胁时,对他们艺术形式的更大挑战可能来自另一个方向。人工智能,尤其是机器学习,似乎非常适合探索和创造 ASMR。这样一来,它可能会让叮叮一代在很大程度上成为计算机驱动的追求。
艺术家和业余爱好者很可能只开采了 ASMR 触发器的表层沉积物
*年来,在人工智能的帮助下制作的音乐从未来的预测跃升为可听见的现实。像谷歌的 Magenta 和 OpenAI 的 MuseNet 这样的项目引领着公众对基于人工智能的音乐创作的冲锋。目前,ML 算法从人类作曲家和音乐家已经创作的大量音乐中学习。他们发现音乐中的模式,并学会预测可能跟随他人的和弦和音符,有时会偶然发现从未听过的旋律。
艺术家和业余爱好者可能只开采了 ASMR 触发器的表层沉积物。他们的发现被记录并发布在互联网上,适合机器学习。ASMR 音乐的结构比音乐简单,听众的反应也很容易测量。在“刺痛科学”网站上,用户可以在这种感觉持续的时候按住一个按钮来记录他们对 ASMR 视频的回应。通过现有的智能手表跟踪心率或皮肤电导应该不难,可以产生大量数据。任何这样的措施都为机器学习提供了极好的数据。
人工智能生成的 ASMR 触发器没有理由继续局限于自然声音的世界。根据现有声音的数据,算法应该能够预测——并尝试——更有效地引出 ASMR 的新声音。根据这些声音以及人们对这些声音的反应,机器学习 ASMR 可以一步一步地走进人类耳朵从未听到过的声音的未知领域,极大地扩展了 ASMR 的曲目。
这可能会导致超正常刺激,全新的声音,唤起比现在的 ASMR 更强更可靠的反应。被赋予了侵入我们的亲密路径的能力,获得 ASMR 机器学习的*台几乎肯定会比我们今天现有的技术更有效地控制人类的注意力范围,通过满足我们的兴奋来保持我们的倾听和参与。

ASMR 可能比音乐更适合数字颠覆。照片由斯潘塞·因布罗克在 Unsplash 上拍摄
AutoML 会是数据科学家的末日吗?
AutoML 越来越受欢迎。这就是事情的变化。

阿尔瓦罗·雷耶斯在 Unsplash 上拍摄的照片
背景
2012 年,一份关于 Auto-WEKA 的 arXiv 报告发布,描述了一种选择机器学习算法、功能和超参数的自动化方法,希望它能“帮助”该领域的“非专家用户”。
[## Auto-WEKA:分类算法的组合选择和超参数优化
存在许多不同的机器学习算法;考虑到每个算法的超参数,有一个
arxiv.org](https://arxiv.org/abs/1208.3719)
最*,AutoML 大受欢迎,所有大型技术公司都进入了这个领域。

CBInsights 对“automl”的新闻报道。
2016 年 4 月,脸书宣称 AutoML 是其人工智能的“骨干”。2018 年1 月,谷歌宣布 Cloud AutoML。2018 年 8 月,Salesforce 开源其爱因斯坦 AutoML 库。一个月之后,微软将 AutoML 引入其 Azure AI *台。今年早些时候,亚马逊推出了另一个开源 AutoML 工具包 AutoGluon。
汽车的现状
CB Insights 今天列出了 40 多家汽车公司,当然还有更多。以下是几个例子:
Accern , Aible , AIgatha , Aito ,舒适性分析,奥格, BACC , beynd ,考萨伦斯, CybordIntell ,达尔维奈,深蓝科技【T23 飞轮, H2O.ai , henQ , Hiretual ,工业分析,浪潮集团, Intento , MAANA , Marlabs , MindsDB , Monitor ,
如果我们看看像 YCombinator 的创业学校论坛、 /r/startups 这样的地方,或者仅仅是 Twitter,我们会发现创始人正在大量生产新的 AutoML 和无代码人工智能项目,就像没有明天一样。
无代码人工智能:自动语言的子集

作为 AutoML 子集的无代码人工智能。作者可视化。
值得注意的是“无代码 AI”和 autoML 的区别。autoML 通常是无代码或低代码的,但也有代码量大或技术复杂的 AutoML 解决方案,如 Auto-WEKA(查看其密集的 15 页手册)。)
像 Apteo 这样的无代码人工智能和分析解决方案旨在尽可能简化 autoML,消除任何进入的技术障碍。
Wix 让每个人都成为网站建设者,Canva 让每个人都成为设计师。现在,是时候让每个人都成为数据科学家了。
towardsdatascience.com](/towards-no-code-analytics-making-everyone-a-data-scientist-f7693bd0abfd)
AutoML 有什么用
所有的 AutoML 宣传都有一个很好的理由:AutoML 是许多组织的必备工具。
我们以 Salesforce 为例。他们解释说他们的“客户希望预测一系列结果——从客户流失、销售预测和潜在客户转化到电子邮件营销点击率、网站购买、报价接受、设备故障、延迟付款等等。”
简而言之,ML 无处不在。然而,为了让 ML 对每个独特的客户都有效,他们将“必须建立和部署数以千计的个性化机器学习模型,这些模型是根据每个单独客户针对每个用例的数据进行训练的”,“在不雇佣大量数据科学家的情况下实现这一点的唯一方法是通过自动化。”
虽然许多人认为 AutoML 是给 ML 带来易用性和效率的一种方式,但现实是对于许多企业应用程序来说,没有其他方法可以做到这一点。像脸书或 Salesforce 或谷歌这样的公司无法雇用数据科学家为他们数十亿用户中的每一个人建立定制模型,所以他们改为自动化 ML,实现大规模的独特模型。
自动化的 ML 组件的数量取决于*台,但使用 Salesforce,它包括功能推断、自动化功能工程、自动化功能验证、自动化模型选择和超参数优化。
那是一口。
这意味着数据科学家可以在生产中部署成千上万的模型,减少繁重的工作和手动调整,大大减少周转时间。
通过将工作从数据处理转向更有意义的分析,AutoML 实现了更具创造性、以业务为中心的数据科学应用。
AutoML 的不足之处
鉴于廉价、高效和简单的 AutoML 工具的激增,我们可能会期望 AutoML 将取代数据科学家。
然而,AutoML 有几个缺点。
前言
为了说明这些缺点,请注意它们与 AutoML 的准确性或可用性无关。
例如,一些人声称 AutoML 无法处理强化学习,AlphaZero 的例子证明了这一点,alpha zero 是一个在没有领域知识的情况下开始的模型,它在自我游戏中达到了超人的水*。
此外,一些人声称 AutoML 不能在无监督的学习上工作,这也是不真实的。
最后,有些人声称 AutoML 不能进行特征工程,这被我们早先的 Salesforce 的 AutoML 的例子所反驳,它在自动化特征工程之后进行了数十亿次预测。
缺点
AutoML 最大的缺点就是没有商业直觉。AutoML 会让你更快地得到一个生产就绪的模型,但是它不会告诉你为什么使用 ML 或者商业理由是什么,更不用说从大量可用的机会中选择一个合理的问题来尝试解决。
现在,假设您使用直觉(而不是数据科学家)来选择问题,并以某种方式协调所有利益相关者(即使没有数据科学家参与),您仍然需要找到正确的训练数据。
AutoML 首先没有选择数据的能力——您需要弄清楚您有哪些数据可以表明您正在尝试解决的问题。
假设我们选择一个问题,联合利益相关者,并找到指示性数据。在建立了我们的模型之后,我们很容易遇到偏差的问题。
人类天生就有偏见,这种偏见反映在我们输出的数据中。如果我们盲目地根据有偏差的数据训练模型,那么我们的模型很可能会有偏差。这在亚马逊的性别歧视招聘算法或谷歌的种族主义图像分类算法中可以清楚地看到。
此外,一旦我们的模型制作完成,它仍然需要由软件工程师集成到*台的其余部分。然而,软件工程师通常不理解人工智能的局限性,因为这样做不是他们的工作。你可能最终实现一个错误的模型。
最后,假设您已经将模型投入生产。数据科学家最有可能看到模型的附加值,因为他们是人工智能实施的倡导者。如果没有任何数据科学家,您可能会错误地推广该功能,并且不了解它真正在哪些用例中大放异彩。
结论
AutoML 是一个快速发展的领域,这是有充分理由的。它支持大规模定制 ML 模型,无需大量数据科学家即可生成数十亿个预测。
然而,AutoML 并不意味着数据科学家的终结,因为它不会“自动选择”要解决的业务问题,它不会自动选择指示性数据,它不会自动调整利益相关者,它不会在面对潜在偏见时提供自动伦理,它不会提供与产品其余部分的自动集成,并且它不会提供事后自动营销。
在这些领域,数据科学家都可以提供帮助。
最终,AutoML 是在您的组织中有意义地实现 AI 的一块拼图。
AutoML 软件会取代数据科学家吗?
AutoML 对数据科学家来说不是威胁

马库斯·温克勒在 Unsplash 上的照片
在过去的几年里,已经推出了许多自动化机器学习软件。他们可以自动执行一些数据科学家通常必须手动执行的任务。它们已经达到了非常显著的复杂和有效的水*。它们是对数据科学家工作的威胁还是机遇?
什么是 AutoML?
AutoML 是一个通用表达式,表示自动执行机器学习任务的软件。它们通常自动执行整个流水线处理,例如清理、编码、特征和模型选择以及超参数调整。这种软件可以是 Python 库,如 Auto-Sklearn,也可以是软件程序,如 Data Robot。
软件的自动化部分取代了数据科学家工作中花费更多时间的所有无聊步骤。他们实际上对管道的几个参数(例如,空白填充值、缩放算法、模型类型、模型超参数)进行所有组合,并使用某种搜索算法(如网格或随机搜索)在 k-fold 交叉验证中选择最大化某些性能指标(如 RMSE 或 ROC 曲线下的面积)的最佳组合。
他们真的可以简化那些必须从头开始创建模型的人的生活,有时他们会探索数据科学家可能没有想到的组合和场景。
它会取代数据科学家的工作吗?
有人可能会认为 AutoML 取代了数据科学家的工作,并可能使这项工作在未来过时。没有比这种怀疑更错误的了。我们来看看为什么。
数据科学(不仅仅)是机器学习
数据科学家不仅仅是使用机器学习模型的人。数据科学家分析数据中隐藏的信息,提取有用的相关性,帮助准备正确的数据以输入 ML 管道,提供关于创建数据本身的业务的有用见解。这些东西是数据科学最重要的部分,不能完全自动化。他们依赖于对业务的深入了解,依赖于对人们所使用的商业语言的有力而有效的运用,最重要的是,依赖于业务经理所使用的商业语言。
所有这些都使得数据科学家的工作比运行机器学习模型更加复杂和有趣,这超出了 AutoML 的范围。
AutoML 软件自动化机器学习任务,而不是整个数据科学过程。机器学习只是数据科学家工作的一小部分,可能不是最重要的,也不是最具挑战性的。理解数据、信息和业务环境是数据科学家的真正挑战,如果这些任务没有完全完成,机器学习将永远不会成为解决所有问题的魔杖。
AutoML 不是单独工作的
AutoML 是软件,所以它总是需要有合适技能的人来使用。事实上,AutoML 结果必须经过专业数据科学家的验证,以确保它们是正确的,并且在产生它们的商业环境中有意义。产生一个在理论上看起来完美的模型并不罕见,但在现实中,它不能产生任何有用的商业见解,或者在最坏的情况下,它的预测是微不足道的。这就是为什么数据科学家必须一直在那里,以确保模型告诉我们一些新的东西,而不只是咀嚼一些旧的东西。
AutoML 对数据科学家有用吗?
是的,我认为它非常有用,因为它自动化了所有枯燥的任务,这些任务通常需要大量代码,并且很有可能出错。如果没有 AutoML,数据科学家必须从头开始创建自己的 ML 管道。每个 ML 模型都有自己的要求(例如,为神经网络缩放特征),因此要测试的整套管道可能会变得非常复杂和耗时。使用 AutoML 工具将很容易让数据科学家创建一个好的 ML 模型,而不必太关心代码。记住:数据科学家不是软件工程师,所以他必须写尽可能少的代码,以便专注于数据和信息。
结论
我认为数据科学家必须跟随变化和创新,所以如果他们开始正确使用 AutoML,它可以成为他们非常有用的朋友。如果他们将枯燥的任务自动化,他们可能会有更多的时间花在分析信息上,这是数据科学家的真正目标。
AutoML 会抢走我的工作吗?这是什么?

Alexandre Debiève 在 Unsplash 上的照片
什么是 AutoML?
在模型开发生命周期( MDLC )中,模型的开发涉及到大量重复和繁琐的任务,例如调整超参数、生成和选择特征。这些任务在开发过程中消耗大量时间,因为它们是迭代的,并且必须尝试各种排列和组合,以达到模型参数的最佳可能组合。不同的 AutoML 工具试图自动化 MDLC 过程的不同部分。虽然有些人试图专门致力于模型开发的自动化,但其他人也试图自动化特性工程,而其他一些人可能会考虑生命周期中的其他步骤。
AutoML 试图通过从 ds 手中移除这些繁琐的任务来实现其目标,这将帮助 DS 和组织节省宝贵的时间和金钱。然而,AutoML 工具为实现这一目标所采取的方法却千差万别,因为每种工具都有不同的功能。一些工具试图自动化整个 MDLC,而另一些工具试图自动化生命周期的某些部分。
事实上,AutoML 可以用一个短语来描述——“自动化的自动化过程”;软件开发是一个利用计算过程,从而减少任务所需的人工时间的过程。有些任务通常不能由计算机执行,因为它需要一组庞大的规则,机器学习可以自动完成这一过程。现在,AutoML 试图消除 MDLC 内部冗余和重复的流程。这使得上面的短语来描述 AutoML,相当贴切。
AutoML 如何减轻复杂性?
AutoML 工具通常有一个图形用户界面(GUI ),它可以帮助指导用户,而不需要技术/编程知识。这通常被称为“无代码人工智能”方法。然而,也有一些工具需要使用代码来处理它。
为什么需要 AutoML?
假设你有一家名为 ABC 的大型零售公司。ABC 在世界各地都有不同规模和容量的商店,并能迅速得到解释。以下是它可以尝试使用机器学习来回答的几个问题:
供应链优化
- 什么时候运送某种产品?
- 什么产品可以创造最大利润?
- 某个时间点某个库存里哪种商品最有可能断货?
促销管理
- 哪种类型的活动最有效?
- 这些活动是有联合效应还是相互独立?
- 在某些地区应该花多少钱进行促销?
- 促销应该在什么时候进行?
数字营销管理
- 哪种营销方式最有效?
- 数字营销活动会对该地区产生重大影响吗?
客户分析
- 什么类型的顾客最有可能光顾商店?
上面列出的问题只是整个数据科学应用世界的一小部分。要实现和维护这些,需要数以千计的数据科学,这对于许多公司来说可能是不可行的。因此,AutoML 是公司提高其数据科学部门生产力的必要发展。
AutoML 会影响数据科学家的工作机会吗?
AutoML 工具只能在 MDLC 中执行特定的任务,旨在为数据科学家提供帮助。这绝不意味着要取代数据科学家,主要是因为这是不可能的。数据科学不仅是数学和技术技能的顶峰,也是领域知识的顶峰,而这是无法自动化的。模型开发不仅仅是关于工程特性和训练模型,还有更多。这些工具可能永远无法像人脑那样,对特定业务用例中最重要的概念有直觉。
重要的是要明白,AutoML 是专门称为 AutoML(自动化机器学习)而不是 AutoDS(自动化数据科学)
在开发出一个模型之后,仍然有许多方面需要考虑,例如它是否符合地理区域的法律结构、种族偏见及其决策过程。假设您部署了一个模型来过滤掉信用卡申请,一个客户问您为什么他/她的信用卡申请被拒绝。作为一家公司,根据《GDPR 》,你有责任解释这一过程以及做出这一决定的原因。因此,很明显,AutoML 工具不能取代数据科学家,只能充当助手。
人工智能能写出毫无瑕疵的代码后,编码会没有用吗?
意见
人类编码将会幸存,但是我们作为软件工程师和数据科学家所做的工作可能会改变

免责声明:这是一篇观点文章。我很想在评论中听到你的想法。
而不是问 GPT-3 是否会让编码员过时。
我们假设在某个时候,AI 可以写出完美无瑕的代码。
人类还会有写代码的地方吗?是的。
编码是与 AI 沟通的最有效方式
代码被设计得尽可能高级和明确。
虽然对非开发人员来说被认为是一门黑暗的艺术,但大多数编码语言比口语更简洁。
我再说一遍。用英语写出一个应用程序的逻辑比用 Ruby 或 Python 写要花更多的单词。
出于这个原因,告诉 AI 要构建什么(同时导航边缘案例和领域知识)可能比编写代码更费工夫。
比如。对人工智能助手的一个简单命令,“给我买卫生纸”包含了许多假设。如果没有提前编码为约束,这些可能会被错误地解释为灾难性的。价格有多重要?柔软度?交货日期?数量?
编码迫使聪明的开发人员考虑这些。
因此,虽然编码可能会变得比现在更高级,但它可能是与人工智能对话的最有效方式。
人工智能编写的代码将需要测试(与代码)
鉴于人工智能可以编写任何代码,输出空间可能是无限的。
因此,虽然你可以监控一辆自动驾驶汽车行驶 1 亿英里来验证它的安全性,但你不能编写覆盖无限空间和无限数量领域的测试。
这让我们不得不测试人工智能输出的代码,而不是编码机制本身。
由于这应该以一种逻辑的方式进行,并允许随着应用程序的变化进行重新测试,所以用代码编写测试是非常有意义的(至少在 AI 开发生涯的开始阶段)。
虽然在未来的某个时刻,我可以想象另一个人工智能层,它可以和人类领域的专家一起帮助编写测试。
人工智能编码器可能不具有成本效益
OpenAI 给出了令人望而却步的成本,这是他们提供 GPT-3 作为 API 而不是开源包的原因。
我们希望 API 能让小型企业和组织更容易获得强大的人工智能系统。
鉴于这一点,我不指望在 AWS 上很快看到它成为 20 美元/小时的服务。人类将会写代码,直到价格降下来。
目前,我们真的不知道价格是多少,只知道 OpenAI 已经获得了大约 10 亿美元的资金。
虽然对大型开发公司来说,自动化编写重复代码是有意义的(即使成本很高),但初创公司的软件工程师不仅仅是写代码。
日常活动包括:
- 编写和审查票据和代码
- 讨论用户体验
- 面试潜在雇员
- 讨论假设特征的约束…
因此,与只会编码的人工智能相比,软件工程师的多面手技能仍然是一个不错的选择。
也就是说,开发人员也有可能成为产品经理,利用他们的技术/产品技能来帮助管理编写代码的人工智能。
我们可能不信任人工智能的关键任务系统
我们会信任人工智能来构建静态的 Wordpress 页面和“又一个社交媒体应用”应用,但我们会信任它为军方编写代码吗?
人工智能被黑客攻击或编写错误代码的坏处是什么?
在一个函数中编写完美的代码很容易。纵观整个 app,就难多了。但是到了基础设施层面,就不再是对错的问题了,而是财务/业务约束和欲望的问题。
我们可以想象,在分层复杂的情况下,对外部世界的必要理解,或者重大的负面影响,我们可能不希望 AI 编写代码。
自己动手打造科技有乐趣
编码爱好者万岁。
这是轶事,但我成为一名开发人员,因为这是我唯一愿意免费做的工作,如果这不是我的日常工作的话。
有一小部分人喜欢为了自己的快乐而用代码构建东西。这就是为什么人们在 100 美元的 RaspberryPi 上构建人工智能助手,而他们只需花 50 美元就能买到亚马逊的 Echo。
人类天生就是工匠,从制作东西中获得满足感。这不会是一个庞大的群体,但我预计它将继续存在。
丰富的经验和基础知识是创新的先决条件
如果人类希望在这个领域保持创新,他们就需要继续编码。
人工智能非常擅长复制已经完成的东西。但不擅长以新的方式结合现有的概念来创造新的东西。我们不是在这里谈论画一幅更好的画,而是开发一种新的艺术类型,或者一种新的数据传输协议。
我们大多数的现代技术都是这样产生的。专家和梦想家对现状感到沮丧,他们非常了解自己的工具。
在软件开发中, GraphQL 是为了应对现有 REST 的局限性而发明的。前者使前端开发更容易,但不“需要”构建。
AI 会学习发明,还是只是更高效地做现有的动作?
结论
这篇文章是一个思想实验,基于我在软件开发、ML 和创业方面的经验。
虽然我可能看起来是反人工智能的,但我不是。相反,能够编码的人工智能将是文明史上小型企业家的最大机会,因为这将让他们专注于问题,而不是技术。
也就是说,我们还没有接*这一点。尽管恐惧在蔓延,我们离机器人的崛起还有很长的路要走。所以,虽然你应该提升你的技能,但我不会因为 GPT-3 接替你的编码工作而失眠。
新冠肺炎会压垮我们的医疗系统吗?
基本生殖数字对新冠肺炎和医疗保健意味着什么

在 Unsplash 上由 Ani Kolleshi 拍照
报道显示,新冠肺炎确诊病例持续上升。随着活跃病例数量的增加,我们的医疗保健系统面临更大的压力。更多的病床、更多的医护人员和更多的设备都将成为需求。随着资源的稀缺,我们的医疗保健系统面临不堪重负的威胁。
我们正在接*临界点吗?如果有,什么时候来?德国总理安格拉·默克尔提供了一些见解:
安格拉·默克尔解释冠状病毒模型及其对医疗保健能力的影响——2020 年 4 月 15 日
【再现系数】,或 基本再现数 ,在考虑这些问题时起着重要的作用。本文将探讨基本再生数的概念,它对新冠肺炎的传播意味着什么,以及它对我们的医疗保健系统的影响。
注意:我们这里的重点是解开繁殖数的概念,因为它与传染病模型有关。为了便于说明,我们以新冠肺炎为例。
首先,简单介绍一下背景
在我们深入研究繁殖数的细节之前,我们需要了解分室流行病学模型是如何工作的。阅读这篇文章可以获得简单但更详细的解释。
流行病传播建模的简单指南
towardsdatascience.com](/when-will-covid-19-peak-96a41939ac33)
为了简洁起见,我们在这里略述一下 SIR 模型的相关假设。
模型的规则
- 人们一次可以分为三类。易感、感染(I) 或痊愈(R) 。
- 易感者会从被感染者那里感染疾病。那些已经康复的人被认为已经建立了免疫力,不能再感染任何人或被再次感染。
- 人们按顺序经历这些阶段。

模拟感染传播的速度
决定感染传播速度的两个关键因素:
- 传染率 模拟有多少人因接触到活跃的确诊病例而被感染。
- 恢复速度 模型显示一个人从被感染到恢复状态需要多长时间。

SIR 模型状态图
基本复制数
背景碍事,我们现在可以看一看 再现号 。
这是什么?
正如安格拉·默克尔所说:
“我们现在的繁殖系数是 1,所以一个人正在感染另一个人。我只能说,对于一个传染链,如果一个人会传染给另一个人。这是一个人感染另一个人的*均值。”
换句话说,我们可以把 基本繁殖数 想象成一个感染者把疾病传给的人数。例如,如果每个被感染的人感染另一个人,那么我们的基本再生数为 1(安格拉·默克尔描述的感染链)。如果每个感染者感染另外两个人,那么我们的基本繁殖数就是 2,以此类推。
我们如何建模?
使用传输和恢复速率计算再现数量。如果传染率告诉我们一个活跃的病例将把疾病传给多少人,而恢复率告诉我们那个人将保持传染性多长时间,那么我们可以如下计算基本繁殖数:

这是一件大事
基本再生数对任何传染病模型的结果都有很大的影响。我们可以想象,从第一例确诊病例开始,这种疾病会以感染波的形式传播。
让我们假设我们的基本再生数是 1。在这种情况下,感染的传播会是这样的:

R₀ = 1,每个人感染一个人
在这种情况下,这种连锁反应一直持续到人群中的所有人都被感染。每个人在康复前都会将感染传给另一个人。随着每一波新的感染,感染人数保持不变。这使得我们的医疗保健系统更易于管理。持续繁忙,但更易于管理,因为每波感染的活跃病例数量保持稳定。
现在,让我们想象我们的繁殖数是 2,这意味着每个人在自己从感染中恢复之前,会将感染传给另外两个人。现在的结果看起来像这样。

R₀ = 2,每个人感染其他五个人
现在,我们看到,随着每一波新的感染,受感染人数都在增加。这意味着,随着每一波浪潮的到来,活跃病例的数量越来越接*我们医疗系统的能力极限。
如果我们把复制数定为 5:

R₀ = 5,每个人感染其他五个人
这份世卫组织报告表明,对于新冠肺炎的严重或危急病例,需要 3 到 6 周才能康复。在下图中,我们假设 5 周恢复,所以每一波感染间隔 5 周。在这里,我们可以看到更高的繁殖数的影响。

活动案例数量的增长,不同复制数量的比较
繁殖数是模拟流行病传播的一个关键变量。
这是一件敏感的事情
安格拉·默克尔继续说道:
“如果我们达到每个人感染 1.1 人的程度,那么到 10 月份,我们将达到我们的医疗保健系统的容量,以及假定的重症监护床位数。如果我们达到 1.2 人,那么每个人都多感染 20%。[其中]五个人中,一个感染两个,另一个感染一个,那么我们将在七月达到我们医疗系统的极限。如果上升到 1.3 人,那么到 6 月份,我们的医疗体系将达到极限。”
默克尔继续描述目前的局势为【脆弱】**【如履薄冰】。生殖数量 0.1 的变化可能会在达到医疗保健能力的时间表中造成 3 个月的差异。
会传染给所有人吗?
这取决于复制数量。有 3 种情况需要考虑:
情况 1:复制数大于 1
如果复制数大于 1,那么使用这个模型,答案是肯定的。由于每个人会感染更多的人,病例数会随着每一波疫情而增加,直到大多数人被感染。感染在人群中传播的速度将取决于易感人群的数量。通过这些模型,我们预计感染率在达到临界点后会放缓,但模型仍然显示每个人都会在某个时候感染。
这里有一个简单的网络模拟来说明这种情况。蓝色节点表示易感人群,红色表示感染,绿色表示恢复(或从人群中移除)。

来源:高等经济学院;流行病的数学建模。第二讲:网络上的流行病
在这种情况下,疾病不只是感染每个人,它随着每一波快速增长,并在人群中传播。
情况 2:复制数小于 1
小于 1 的繁殖数意味着,*均而言,大多数人不会将这种感染传递下去。如果繁殖数是 0.5,那就意味着一半被感染的人不会再感染其他人,所以活跃病例总数随着每一波都减半。这种情况一直持续到疾病最终消失。这是一个模拟的样子,使用相同的网络,但是复制数小于 1。

来源:高等经济学院;流行病的数学模型。第二讲:网络上的流行病
最终,我们会到达一个点,在传染给任何人之前,感染者会康复。在这种情况下,它没有获得足够的动力来感染大多数人群,并随着时间的推移而消失。
情况 3:复制数等于 1
这是第二个最好的情况,因为在每一波感染中,感染人数保持不变。
在这种情况下,我们预计这种疾病最终会在整个人群中传播。然而,这是医疗保健行业第二好的情况,因为在任何给定时间活跃病例的数量保持不变。
结束语
几个重要的注意事项:
- 本文的目的是解释在流行病学模型中再生数的含义。不提供任何关于何时将达到容量或多少人将被感染的预测。
- 这些模型并不完美,而且带有相当多的假设。经济学人信息部估计全球一半的人口将被感染。这些估计可能是考虑了更多变量的更复杂模型的结果。简单房室模型的一些局限性在这里 提到 。
关于 繁殖数 更详细的数学解释,我推荐斯坦福大学人类科学系的这篇论文。如果您对本文有任何问题或意见,请随时留言或联系我们。
编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
顾客会购买购物车中的产品吗?
使用 XGB 分类器来预测一旦客户将商品添加到购物车中,他们最终是否会购买。

图片来源:https://www . the south African . com/life style/black-Friday-deals-in-south-Africa-tips-2019/
了解客户购买行为的关键驱动因素始终是电子商务行业的圣杯。这些知识可用于改善购物流程,最终提高销售额和客户满意度。在这个项目中,我选择了来自 Kaggle 的 2019-11 月数据——来自一家多品类商店的电子商务行为数据,来演示我如何通过 XGBoost 分析和建立一个基本的预测模型。
了解你的客户
在我们致力于特性工程和构建模型之前,退一步进行 EDA (探索性数据分析)总是好的。通常,我们会发现一些有用的信息,有助于后面的过程——数据准备、特征工程和建模。
数据如下所示,有关每个属性的详细信息,请查看此处的:

数据来源:【Kaggle 的 REES46 营销*台
在检查了基本的描述性分析之后,有一些问题可能值得进一步探讨。知道数据集包含时间、产品信息和价格,我在考虑一些业务问题,我可以从当前的数据集得到答案。
- 11 月日流量?
11 月份的总客户访问量为 3,696,117,但他们不太可能*均访问该网站。

我们可以在 11 月 16 日和 17 日左右看到一个大高峰,我猜这将是一个促销活动。如果我们检查一种产品的价格的每日价格趋势(例如:product _ id=1003461,小米或 1005115,苹果),这可以进一步证明,并且在 16 日和 17 日期间该产品的价格总是较低。
2。什么产品类别和品牌最受欢迎?
人们查看的前 5 个类别是“电子产品.智能手机”、“电子产品.视频.电视”、“电脑.笔记本”、“电子产品.钟表”和“服装.鞋子”,如下图所示。当我从那些购买事件数据中查看品牌时,最受欢迎的品牌是三星、苹果、小米,以及其他品牌的电子产品。显而易见,大多数顾客是来购买电子产品的。因此,对于经理来说,这将是一个开放性的战略问题,他们是否应该专注于这一特定类别,而不是成为一个多类别商店,或者包括其他类别是否有益?以防这种现象也能从其他月份的数据中观察到。

前 30 个类别的树形图
3。客户的购买之旅是否就像典型的漏斗(view = > cart = > purchase)?

虽然饼图显示只有一小部分人真正将商品放入购物车,但一旦商品被顾客放入购物车,大约有 30% (1.36%除以 4.49%)的购买转化率。虽然转换漏斗的每一步都可以优化,但在这个项目中,我将重点放在购物车到购买的转换上。
客户会在将产品添加到购物车的同时购买它们吗?
现在,我将构建预测模型。对于这个用例,我只使用了“购物车”和“购买”数据。此外,我还通过引入一些新功能重新设计了数据结构:
- 类别 _ 代码 _ 级别 1:类别
- 类别 _ 编码 _ 级别 2:子类别
- 事件 _ 工作日:事件的工作日
- activity_count:会话中活动数量,包括所有事件类型
- is_purchased:放入购物车的商品是否被购买
多了两个特征,'品牌'和'价格',从原始数据来看,还不错。如果你对数据清理和特征工程过程感兴趣,请在这里查看代码。
建模
在我的分析中,我使用 XGB 分类器。XGBoost 是一个具有良好性能的梯度提升决策树的实现。考虑到数据量,我还对原始数据进行了随机下采样(每个类 500,000 条记录:购买和不购买),以避免类不*衡的问题。我的目标是快速查看结果,然后思考模型改进和调整的下一次迭代。

Fbeta 显示 0.68,Recall 是 0.74,对于第一个简单的模型还行。但是,我更好奇的是,什么特征对预测购买起着重要作用。

根据数据集,Feature_importance 给了我们一些见解,event_weekday 和 activity_count 似乎在预测中占主导地位。当查看日常流量时,我可以看到在周末和假期有一些增加。消费者在这些日子里花更多时间购物是有道理的。然而,也可能是因为一些隐含的原因,比如我之前提到的促销——人们在特定日期购买是因为价格而不是他们的习惯。为了验证这个假设,我需要更多的商店背景。Activity_count 是我计算特定用户会话的总事件记录的指标,作为客户参与度的代理。因此,客户和网站之间的互动才真正显示出意义。这可能是因为顾客通常花时间比较他们的商品和收集信息来做出购买决定。如果在这种情况下,使用户界面友好或引入一些功能,如“最低价格保证”,或“其他人也看看”,以帮助客户加快他们的购买决策过程,最终将推动转换。
结论和未来工作
查看结果,现在我们知道网站上的客户活动是决定客户是否购买的关键驱动因素。通过分析,我们可以重新考虑我们的策略,以进一步开发我们的模型。例如,为了通过探索个人购买体验来增强预测能力,我们可能需要更多事件类型的点击流数据,如客户点击了哪个组件。我们可能还需要获取客户的个人资料数据,这样我们就可以相应地对客户类型进行聚类,这将有助于改进产品推荐系统。因为购买行为是非常个人化的,从各个方面获得数据可以让我们更好地分析哪些特征和功能是重要的。
除了获取更多的数据点之外,尝试其他分类器和调整模型参数也是有用的。为了扩展这一点,最好建立一个训练管道,让我们更快地进行实验,并为我们的下一个模型选择最佳性能结果。在我的另一个项目——寻找捐赠者中,我展示了如何构建管道,以便你可以有效地尝试许多不同的分类器和参数设置。
一旦模型准备好预测客户购买,它就可以用作可操作的营销工具,在客户似乎改变主意时,我们有赢回的方法,这是我们可以从预测中获得的真正商业价值!
要了解更多关于这个分析的内容,请点击这里查看我的 Github 链接。
数据科学会自动化吗?
意见
全自动数据科学*台的利与弊。

目录
- 介绍
- 数据科学自动化
- 利弊
- 摘要
- 参考
介绍
随着数据科学越来越受欢迎,公司正在计算一个团队需要多少数据科学家才能制造出成功的产品或成功解决业务问题。虽然公司专注于雇用数据科学家,但他们很可能已经注意到,他们可以雇用一个*台,或者以其他方式执行数据科学,而不是雇用人员来执行数据科学,以便在他们的公司中使用数据科学。最终,数据科学可以自动化,就像大多数技术流程一样,这是一个开端。然而,问题变成了,它应该自动化吗?当它被工具或*台自动化时,数据科学的表现如何?下面我将通过强调自动数据科学和/或机器学习的利弊来讨论这些问题。
数据科学自动化
像生活中的大多数事情一样,适度是关键,所以淘汰人类数据科学家并用一种工具取代他们可能会导致一些混乱和困惑——首先。就像教育一样,一个在线*台可以教会许多人在学术领域取得成功,自动化数据科学*台也是如此。人类可以从机器那里学习数据科学。但是,当你在数据科学领域历史的早期进行自动化时(是的,我知道它并不像许多人认为的那样是一个新领域,你可能会遇到一些严重的问题。相反,你会遇到一些很棒的职业选手。
利弊
凡事都有利弊,自动化数据科学也不例外。我不打算详细介绍具体的工具/公司,它们的主要产品是数据科学自动化,但是您可以预期这些工具的一些优点和缺点。
赞成的意见
- 易于使用
自动化数据科学*台的主要功能是让用户更容易在其业务中实施数据科学。因此,拥有数据分析或产品管理背景的人可以很容易地使用一个*台,比如说,对图像进行分类。
- 便宜
尽管雇用数据科学家可能会让公司在工资和入职成本上花费超过 10 万美元,但自动化*台的成本甚至可能远远低于一名数据科学家的成本——需要注意的是,一些公司有不止一名数据科学家。
- 强大的
众所周知,数据科学本身是一种强大的工具,可以对公司或业务产生重大影响。数据科学和机器学习已经引领了无数的产品,并以某种方式服务于几乎每一个人。今天用手机吗?是 iPhone 吗?你用了 Face ID 吗?那么你很可能已经使用了机器学习,甚至没有意识到这一点(除非你现在是一名数据科学家,已经知道了)。也许你使用了网飞的推荐算法来推荐一个节目或电影。这些是你会遇到的一些日常机器学习的例子。还有无数更多,一个公司可以真正受益于数据科学对其业务的影响,无论是内部还是外部。
骗局
接下来我将强调缺点,因为我认为它们更重要,并且比优点更重要(从现在开始——这可能会很快改变)。
- 难以解释
缺点是它变得棘手。由于用户没有正确使用*台和/或不正确地解释结果和模型,这些点真的会把公司搞得一团糟。很难解释复杂数据科学模型的结果。现在想象一下,你不是数据科学家,也没有各种机器学习算法的学术背景。你必须解释这些*台模型的结果,并实现关于你公司整合的建议或预测(有时是),这可能会被证明是耗时且困难的。
- 误导结果
因为您没有自己构建模型,所以您可能不知道需要调整的可能参数。此外,您可能不知道您需要使用肘图来查找无监督分割算法的最佳聚类数。所有这些不从头理解模型的复杂性可能会导致最没有意义的结果。也许您使用逻辑回归来预测接下来几个月的温度,但后来意识到最好将该算法用作分类模型,尽管其名称自相矛盾。一些细微的差别累积起来可能会导致一些严重的错误。
摘要

最终,这取决于数据科学是否将完全自动化。当然,如果您的团队中已经有一名数据分析师,请使用自动化数据科学*台。或者,使用自动化解决方案进行预测,即使不正确也不会造成伤害。对衣服分类不正确并不是最糟糕的事情,但当你在卫生或金融行业对疾病或大笔资金分类不正确时,其危害是不可否认的。
弄清楚你是什么公司,你的目标,权衡利弊,从那里,你可以决定自动化数据科学是否适合你。也就是说,数据科学已经在自动化,但未来将面临试图完全自动化整个过程的*台。
希望这篇文章能带来一些有趣的讨论。当然,我有偏见,更喜欢把数据科学家留在身边;然而,我知道通过导入预先保存的流行库,已经有多少数据科学实现了自动化。解决方案可能是你可以使用人在回路方法:自动化你能做的,然后提供检查和*衡来解决模型错误。
下面随意评论下来。感谢您的阅读!
参考
[2]马库斯·温克勒在 Unsplash 上拍摄的照片,(2020)
深度学习会碰壁吗?
更好的算法还是更强的计算能力?
如果你对深度学习感兴趣,那么你可能已经听说过美国、韩国和巴西大学和实验室的研究人员最*发表的论文。
Neil C. Thompson ,麻省理工学院计算机科学和人工智能实验室, Kristjan Greenewald ,麻省理工学院数字经济倡议, Keeheon Lee ,首尔延世大学安德伍德国际学院, Gabriel F. Manso,巴西利亚大学 FGA 分校。

安迪·凯利在 Unsplash 上拍摄的照片
在他们的研究中,他们分析了图像分类、对象检测、问题回答、命名实体识别和机器翻译等领域的 1000 多篇研究论文,发现深度学习性能的进步在很大程度上是基于计算能力的提高。
一般来说,每个计算机领域的进步都可以通过两种主要方式来实现:
- 要么提供更高的计算能力,这不仅意味着更快的 CPU 或更多的节点,还意味着更多的内存和存储
- 或者通过研究新的算法和方法
因此,研究人员发现,在上述领域取得的重大进展是由于计算能力的提高,而不是新算法的创造和采用。简而言之——去年深度学习的许多成就都是因为计算机变得更快了,现在可以比以前更快地执行相同的旧算法。
很糟糕吗?不一定。计算能力的增长本身是中性的,没有好坏之分,这只是世界不得不接受的事实——计算能力总是随着时间的推移而增长,如果你环顾四周,你会发现它在许多(如果不是所有)领域都有更好的性能。
这是非常直观的——如果在计算机上执行某个任务,它可以用更快的 CPU 更快地执行,或者通过允许任务用更快的 CPU 或更大的内存处理更多数据来产生更好的结果。**
正如研究中提到的那样,机器学习的计算成本总是很高。
毕竟,没有一项研究表明计算能力是推动机器学习的唯一因素。但是研究发现了两个有趣的点:
- 深度学习模型的实际计算负担比理论上的(已知)下限增长得更快,这表明实质性的改进是可能的
- “如果按照目前的路线发展下去,这些计算需求将很快在技术上和经济上变得令人望而却步。”
当然,对于这两种观点,我们都可以找到相反的论据。也就是说,如果深度学习模型的扩展速度比理论下限更快,那么我们就可以假设理论还不够精确。
此外,我们对计算要求的技术和经济界限的估计是基于我们对目前用于生产计算资源的技术方法的当前知识、当前生产和拥有成本以及根据我们当前的理解和知识所做的预测。
但是,也许我们不应该争论研究的结论,我们应该考虑另一点:如果我们可以发现新的或改进现有的算法,那么我们就可以大大提高结果的质量,无论是什么——分类,物体检测,机器翻译,等等。
当我看到 GAN 的实验和过去几年我们看到的架构改进时,我可以将其视为该领域的一个很好的例子,新方法产生了辉煌的结果。
据研究人员估计,三年的算法改进相当于计算能力提高 10 倍。****
但正如研究中也提到的,有时新的改进算法本身需要更多的计算能力。你知道,有些算法比其他算法更需要资源。这可能是问题本身,尽管是暂时的——可能我们只是需要获得更多的计算能力来尝试新的训练方法,然后运行模型。
计算能力的增长是我们在过去几年中所拥有的,并且有望在未来实现,这意味着我们肯定会看到机器学习的改进。但是,这只是因为计算能力的增加而稳步提高,还是因为算法的改进而显著提高?
我个人希望最后。
资源
Neil C. Thompson,Kristjan Greenewald,Keeheon Lee,Gabriel F. Manso
边缘人工智能:未来的人工智能架构
了解 Edge AI 的基础知识及其在人工智能领域日益增长的重要性

授权给作者的图像
Edge AI 描述了一类 ML 架构,其中 AI 算法在设备上本地处理(在网络边缘)。使用 Edge AI 的设备不需要连接就能正常工作,可以在没有连接的情况下独立处理数据和做出决策。了解为什么这在人工智能的现代应用中变得越来越重要。
典型的 ML 架构
其中一个你应该很熟悉,将会有一个 ML 模型,精心制作,训练和托管在云基础设施上,预测请求从设备发送到云基础设施。这些请求包括向基于云的 API 发送请求,并通过互联网接收响应。
这些请求包括向基于云的 API 发送请求,然后通过互联网接收响应。当传输的数据很小(如文本片段)时,这通常是一种成功的方法,但当数据较大(如高质量的照片或视频)时,这种方法就会失效。在网络覆盖差(或无网络覆盖)的地区,即使中等大小的数据也会造成问题。
边缘艾
边缘人工智能的想法是让模型生活在网络边缘的设备上(因此得名)。然后,人工智能算法在设备上进行本地处理,不再需要互联网连接来处理数据和生成有用的结果。
2020 年,德勤预测将售出超过 7.5 亿个边缘人工智能芯片,这些芯片在设备上执行或加速机器学习任务,而不是在远程数据中心,这意味着 26 亿美元的收入。
边缘经营的优势
Edge AI 在传统的 ML 架构上提供了很多改进。首先,消除了任何网络传输所涉及的延迟,这在某些用例中可能是至关重要的。流数据所涉及的电池消耗不再是一个问题,允许更长的电池寿命,并且数据通信的相关成本显著降低。
这对于许多用例来说是非常有益的。像海上风电场这样的偏远地区的传感器可以预装算法,使它们能够在没有复杂的互联网连接基础设施的情况下做出决定。

尼古拉斯·多尔蒂在 Unsplash 上拍摄的照片
类似地,这种方法正被用于监控地下气体管道的流量,在这种情况下,基于云的策略不可行。传感器测量流速和压力,以确定管道的健康状况,如果检测到泄漏的迹象,阀门可以关闭。

Edge AI 的其他现实应用
Edge AI 并不是偏远地区的专利,它已经在离家更*的商业街上被采用。

授权给作者的图像
英国化妆品品牌 Lush 在一项新举措中使用了一种边缘人工智能方法;他们的 Lush Labs 应用程序最*增加了 Lush Lens 功能。
设计用于帮助减少包装需求的镜头用于通过智能手机的摄像头扫描产品。在引擎盖下,应用程序中有一个图像识别模型,利用 Edge AI 来降低电池消耗和网络要求。正确识别产品后,用户无需包装即可获得详细的产品信息。
了解更多关于 Lush Lens 如何使用 AI 来减少包装的信息在这里。

授权给作者的图像
最后,边缘人工智能芯片可能会进入越来越多的消费设备,如高端智能手机、*板电脑、智能扬声器、可穿戴设备和生物植入物。它们还将用于许多企业市场:机器人、相机、传感器和其他物联网设备。
有什么弊端吗?

授权给作者的图像
复杂的机器学习模型通常体积很大,在某些情况下,将这些模型转移到小型设备上是不可行的。模型需要简化,这不可避免地会降低准确性。
边缘设备的计算能力有限,进一步限制了可以执行的人工智能任务。
Edge AI 通常涉及将模型部署到各种设备类型(和操作系统版本),这可能会增加失败的可能性。因此,在芯片准备好流通之前,通常需要进行大量的测试。
后续步骤
1.向领先的人工智能芯片制造商 ARM 了解更多信息
2.了解更多关于 Ancoris 数据、分析& AI
3.与作者联系
最新的 AI 会杀死编码吗?
人工智能现在可以用任何语言编码,无需额外训练。

斯科特·罗杰森在 Unsplash 上拍摄的照片
在 2017 中,研究人员问:到 2040 年,人工智能能写出大部分代码吗?OpenAI 的 GPT-3 ,现在正由 beta 测试人员使用,已经可以用任何语言编码。机器主导的编码几乎就在我们的门口。
GPT-3 接受了数千亿个单词的训练,或者基本上是整个互联网,这就是为什么它可以用 CSS、JSX、Python 等等编码。
此外,GPT-3 不需要为各种语言任务进行“训练”,因为它的训练数据是无所不包的。取而代之的是,当被给予琐碎的指令时,网络会将自己限制在手边的任务上。
GPT-n 的演变
GPT 通过将监督学习与非监督预训练配对(或使用来自非监督步骤的参数作为监督步骤的起点),在语言任务中实现了最先进的水*。与其继任者相比,GPT 很小。它只在几千本书和一台 8 GPU 机器上进行训练。
GPT-2 极大地扩大了规模,包含了 10 倍的参数和超过 10 倍的训练数据。尽管如此,数据集相对有限,而且它是专门针对“来自 Reddit 的至少收到 3 个 karma 的出站链接”进行训练的 GPT-2 被描述为“变色龙般”的合成文本生成器,但它在下游任务如问题回答、摘要或翻译方面并不先进。

Hans-Jurgen Mager 在 Unsplash 上拍摄的照片
GPT-3 是人工智能世界中最新和最伟大的,在一系列任务中实现了最先进的技术。它的主要突破是不再需要针对特定任务的微调。在规模方面,该模型再次大幅扩大,达到 1750 亿个参数,是其前身的 116 倍。
虽然 GPT-3 根本不需要训练(零次学习的一个例子),但它已经令人印象深刻的性能通过一次或几次学习而黯然失色。
进化或死亡
情况是这样的:Beta 测试人员正在使用 GPT-3 来生成工作代码,并且需要一些琐碎的知识。从按钮到数据表,甚至再造谷歌主页。这些例子都是用零距离学习完成的。
除了人工智能的快速发展,其他两大技术趋势也加剧了未来编程工作不安全的现实:无代码和自动编程。
Wix 让每个人都成为网站建设者,Canva 让每个人都成为设计师。现在,是时候让每个人都成为数据科学家了。
towardsdatascience.com](/towards-no-code-analytics-making-everyone-a-data-scientist-f7693bd0abfd)
无代码指的是可视化工具,让任何人都可以更容易地构建新产品,无论是网站、设计、数据分析还是模型。WordPress、Wix 和 Shopify 是无代码工具的好例子,它们让数百万人能够自己做事情,而不是雇佣开发人员或设计师。
第二个趋势是 AutoML,即自动化机器学习,它大大缩短了人工智能投入生产的时间。
AutoML 越来越受欢迎。这就是事情的变化。
towardsdatascience.com](/will-automl-be-the-end-of-data-scientists-9af3e63990e0)
像 Apteo 这样的工具结合了这些趋势,使任何人都能够部署人工智能模型,而不需要任何编码技能。
GPT-3 将引发另一波无代码和自动化工具的浪潮。许多潜在雇主会选择这些工具,而不是雇佣昂贵的程序员。
自然地,技能最低的程序员将首先离开,而专家将享受更长时间的工作保障——与任何领域一样。
不仅仅是代码——GPT 3 适用于任何语言任务
要指导 GPT-3 去完成一项特定的语言任务,你只需给它一个你希望达到的目标的例子。因此,虽然你可以指导 GPT 3 号编写代码,但你也可以指导它编写诗歌、音乐、社交媒体评论或任何其他文本。
例如,如果您想要生成一篇新闻文章,您可以输入一个标题,如“联合卫理公会同意历史性分裂”,以及一个副标题,如“那些反对同性婚姻的人将形成自己的教派。”
有了这个输入,GPT-3 产生下面的:
据《华盛顿邮报》报道,经过两天的激烈辩论,联合卫理公会已经同意进行历史性的分裂,预计最终将建立一个新的教派,一个“在神学和社会上保守”的教派。参加 5 月份教会年度大会的大多数代表投票加强了对 LGBTQ 神职人员的任命禁令,并制定了新的规则,将“约束”主持同性婚礼的神职人员。但是那些反对这些措施的人有了新的计划:他们说他们将在 2020 年前成立一个单独的教派,称他们的教堂为基督教卫理公会教派……”
只有 12%的人类正确地指出这是由人工智能写的。88%被忽悠了。
像人类一样,GPT 3 号只需举一个例子就能学会新单词。例如,给定上下文:
“Burringo”是一种加速非常快的汽车。使用单词 Burringo 的句子示例是:_ _ _ _ _ _ _ _ _ _ _ _ _
GPT-3 产出:
在我们的车库里有一辆墨西哥卷饼,我父亲每天都开着它去上班。
这些结果令人难以置信。请记住,人工智能是不可避免的进化,因此对当前表现的任何批评都将化为乌有。
不仅仅是语言——GPT 把它应用于图像
GPT 可以写代码,或者,嗯,任何东西,但是它也可以生成图像。
这怎么可能?
可以在像素序列而不是文本编码上训练相同的模型架构,从而生成新颖的图像而不是新颖的文本。事实上,它在这方面做得很好,可以和顶级 CNN 竞争。
我提到这一点是因为它表明,GPT(及其继任者)不仅仅有潜力在某一天取代编码员,而是整个行业,因为它的多功能性。
结论
GPT 3 号令人难以置信的性能让许多人相信,超级智能比我们想象的更*——或者至少,人工智能生成的代码比我们想象的更*。它产生创造性的,有洞察力的,深刻的,甚至是美丽的内容。更多 GPT-3 的创造性例子(如果你需要更多关于它有多强大的证据),请查看这个 Gwern 帖子:
我用 OpenAI 的 2020 GPT-3 继续我的人工智能诗歌生成实验,它是 116 倍大,也更强大…
www.gwern.net](https://www.gwern.net/GPT-3)*
机器学习和混合现实会实现虚拟时间旅行吗?
欧洲时间机器项目开启了历史和文化遗产的新视角

欧洲时光机项目的横幅。图片:欧洲时光机项目
时光旅行一直是人类的梦想。想象一下,能够亲身体验古罗马,或者回到我们祖先的时代,体验当时人们的生活方式。显然,这样的时间旅行远远超出了我们今天的身体限制。
尽管不可能实现实时旅行,来自 32 个国家的超过 225 个欧洲研究机构组成的联盟正计划建造一个虚拟的时间机器。这个时间机器是一个大型数据库,能够存储、解释和连接各种历史信息,从地图和 3D 模型上的文本和图像到音乐和其他感官信息。时间机器的作用是将所有这些信息联系起来,重建过去看似合理的观点。最后,它应该允许我们浏览所有这些数据,以便像我们今天在互联网上一样容易地在时间和空间中移动。

欧洲时间机器项目旨在建立一个过去的虚拟镜像世界。视频由欧洲时间机器项目提供。
为了实现这一宏伟目标,必须取得许多突破。因此,这些研究人员正在组成一个财团,以创建一个巨大的欧洲大规模研究倡议(LSRI)。此类项目过去一直由欧洲委员会资助,并得到大量资源的支持。先前资助的一个例子是人类大脑项目,其目的是建立一个人类大脑的电子复制品。
研究人员面临的主要挑战可以分为三类:数据和数字化,知识提取和建模,以及这种数字认识论的局限性和机遇。显然,在通往这样一台时间机器的道路上还有很多挑战,比如许可和法律问题,这些都远远超出了本文的范围。因此,在这一点上,我们将只研究上面的主要挑战。

我们越深入过去,可用的数字信息就越少。为了更深入,我们将不得不依靠对过去的模拟。视频由欧洲时间机器项目提供。
对于现代数据和信息,我们有一个巨大的优势,几乎所有的信息都可以通过电子方式获得。然而,我们越是回到过去,越是无法获得电子格式的信息,而这种电子格式适合作为时间机器处理的输入。即使是文化遗产,也就是我们认为对我们的文化身份非常重要的信息,目前也只有 15%以数字格式提供。对于档案馆和图书馆,这一比例甚至更低。因此,一个最初的目标是大规模数字化。与涉及翻页的传统扫描仪相比,这一过程可以使用体积采集技术(如计算机断层扫描)以大幅提高的速度完成。诸如 scan tent 之类的移动扫描仪也将在该领域的高质量数字化中发挥重要作用。此外,在装配线上对 3D 物体进行大规模扫描在今天已经在我们的技术范围之内。

书本 CT 允许在不打开书本的情况下阅读页面。可视化:克劳斯·恩格尔
然而,这些海量数据也需要长期存储方法,能够将这些信息保存数千年。Twist Bioscience 的研究人员正在开发在 DNA 链中存储数字信息的技术,这是人类已知的最紧凑的信息表示,因为分子本身携带的信息比当今使用的任何数字存储器都要紧凑几个数量级。请注意,这种类型的存储也适合长期保存,因为我们知道 DNA 发现的例子已经存在了 10,000 年甚至更长时间而没有丢失数据。

Time Machine 将支持使用各种访问*台访问过去的大数据,并使用模拟和推理引擎处理数据。图片:欧洲时光机项目
即使我们设法数字化并存储我们可以从 2000 多年的欧洲历史中恢复的所有数据,我们也会立即遇到两个额外的问题:我们必须能够处理数据,以及大量数据无法在如此长的时间内保存下来。对于数据处理挑战,我们必须统一处理文本、图像、音频、地图、3D 对象及其解释。今天,用于此目的的大多数系统采用图形和符号表示,但我们已经看到,深度学习的能力在许多应用中能够胜过任何符号系统,正如最*在语言翻译任务中所证明的那样。因此,这个项目的一个目标是创造一个通用的表现空间,使我们能够将以上所有的东西相互转换。然而,这样一个系统有一个很大的缺点,那就是它不允许把观察结果和推理链联系起来,就像我们在符号演绎中能够做到的那样。另一个重要的目标是融合基于符号图和基于模糊神经网络的方法。基于这些进展,我们仍然需要能够生成历史重建。传统方法使用计算机图形来实现这些目的,然而,机器和深度学习也在这个学科中兴起。因此,我们需要能够从相当简单的描述中生成复杂场景的方法。后续的信息解释和分析仍将由与时间机器互动的人类来完成。通过这样做,从历史专家到公民科学家以及非专业用户的用户将能够为他们的目的操作时间机器。

时间机器项目将使用链接的开放数据来连接过去的见解,并使它们可以访问。视频由欧洲时间机器项目提供。
时间机器的第三个重要方面是它如何被用来产生新的见解。在所有观察中,必须确定信息内容以及是否信任它。这需要扩展的认识论方法——数字认识论——能够同时处理不同版本的历史真相。事实上,能够轻松地对过去进行不同的重建需要仔细的思考,因为结果可能会被产生并用于推动某种当今的政治观点。这种尝试在历史上是众所周知的,也是解释历史知识如此困难的主要原因。此外,所有对过去的重建——包括传统方法——通常都有特定的目的,因此,在审视这些重建时,必须牢记初衷。例如,考古学家可能希望通过使用柔和的灰色来清楚地表明寺庙的原始颜色是未知的。相比之下,为了给观众创造一个更加身临其境的体验,旅游办公室更喜欢对同一座寺庙进行可信且细节丰富的重建。新的历史洞察力也是对数据的解释,因此必须与原始数据和导致这种洞察力的观察链相联系。在人文和哲学领域,这已经通过文本和语言的方式进行了几个世纪。然而,我们必须建立一个数字工作流程来做同样的事情,以便允许更高程度的合作,并使科学能够更快地进步。通用人工智能也将是时间机器成功的一个重要因素,因为它将允许创建虚拟代理,这些代理可以驻留在我们过去的虚拟图像中。此外,劳动密集型任务,如数据库查询,将由现代人工智能方法处理,旨在自动回答问题和语言解释。

随着时间机器项目,我们会很快拥有虚拟时间旅行吗?来自 Pexels 的 Pixabay 的视频。
该项目旨在以目前未知的详细程度再现过去。正因如此,像育碧这样以刺客信条系列而闻名的主要产业玩家加入这个财团并非巧合。在他们对时间机器——Animus——的设想中,他们已经非常接*时间机器项目的目标,因为 Animus 通过回到自己祖先的生活来实现沉浸式的过去体验。虽然这个目标今天还远未实现,但时间机器项目的研究人员相信,这样的时间机器将是研究的革命性产品,也将推动历史领域的商业应用。
文章和内容以知识共享许可 4.0 署名发布,并首先出现在 MarkTechPost.com上。如果你喜欢这篇文章,也请看看我的 YouTube 频道。
这场危机会帮助自主人工智能走上正确的道路吗?
新冠肺炎疫情为所有人工智能、机器人和无人驾驶汽车初创公司敲响了警钟:停止构建令人眼花缭乱的演示,停止谈论通用人工智能的未来可能性。相反,专注于部署现实世界的解决方案,这些解决方案可以在最少的人工干预下一天 24 小时运行,并为用户提供真正的价值。

疾控中心在 Unsplash 上的照片
在当前的疫情,数百万美国人开始在家工作。零售商一直在努力应对供应问题,而紧张的消费者正在囤积从卫生纸到洗手液的各种物品。
在全球范围内,中国电子商务巨头京东开始在武汉测试四级自动送货机器人,并全天 24 小时运行其自动化仓库,以应对需求的激增。
突然间,自主机器需要比仅仅证明概念更好。他们不再依赖现场工程支持来应对边缘情况。它们必须足够健壮,能够在各种现实生活环境中独立工作。
在某些方面,这种流行病加速了已经在路上的自动化未来。它暴露了人工智能创业领域长期存在的问题:流行语和炒作蒙蔽了人们的判断,难以看到真正的进展。
该行业需要在以下三个方面对现实世界的自治系统进行急需的改革:
1.反思度量标准
随着越来越多的自主 AI 机器部署在现实世界中,速度、周期时间或成功率等传统指标不再能够代表全貌。我们需要在不确定性的情况下,用健壮性指标(比如*均人工干预次数)来衡量系统的可靠性。
我们需要更多的工具和行业标准来评估各种场景下的整体系统性能,因为现实生活与受控环境不同,是不可预测的。
如果送货机器人可以达到最高 4 英里/小时的速度,但没有人类的支持无法完成一次送货,那么机器人就没有为用户创造多少价值。
几年前,为了缩短开发周期并持续交付高质量的软件,DevOps 应运而生。与软件工程相比,人工智能或 ML 要不成熟得多。 87% 的 ML 项目从未投产。然而,最*我们开始看到越来越多的 MLOps 或 AIOps 出现。
这标志着从 AI/ML 研究到每天使用和测试的实际产品的关键过渡。这需要思维模式的重大转变,以专注于质量保证,而不是最先进的 ML 模型。我并不是说我们不能同时拥有两者,但是到目前为止,我们已经看到了对后者的更多强调。
2.重新设计错误处理和沟通
最* Starsky Robotics 的关闭提醒我们,距离完全自主的解决方案还有数年的时间。这并不意味着人工智能机器人不能给人类带来直接的价值。正如我在以前的一篇文章中提到的,即使人类需要处理 15%的时间,这仍然意味着公司可以减少大量的劳动力和集成成本。
然而,目前,人工智能公司倾向于在建立自主系统上花费更多的资源,而在考虑错误处理和机器与人类之间的无缝交接上花费更少的时间。
我们需要一种更好的方法来处理和交流错误,特别是对于 ML 产品,因为 ML 更具概率性,更不透明。因此,展示模型预测的可信度或者将你的预测框定为建议而不是决策,是赢得用户信任的方法。
我们需要将错误分为不同的级别,相应地设计不同的协议,并优先最小化导致系统停止并需要人工干预的致命错误。如果出现致命错误,系统不再工作,我们能否快速响应并远程排除故障?
最困难的部分是识别系统无法检测的未知错误。因此,进行双向沟通并允许用户标记错误或选择激活之前商定的后备计划也很重要。
3.重新定义人机交互
冠状病毒迫使公司更快地采用自动化并转向云。随着越来越少的人控制越来越多的机器人,我们是否有合适的工具和技术将所有相关信息迅速传递给决策者?每个机器人上是否有足够的传感器来提供完整的画面?
今天,我们依靠像电脑或*板电脑这样的触觉输入来控制机器人。在信息量激增而响应时间仍然很短的情况下,这些仍然是最好的界面吗?我们是否应该重新考虑超越触觉的人机界面,例如,语音、VR/AR 或脑机接口?
我们还需要决定应该由谁来控制。随着机器变得越来越聪明,我们应该总是做最后的决定吗?
例如,谁应该控制一个自主机器人?汽车本身?人类安全驾驶员?远程监控机器人轴心舰队的人?乘客呢?什么情况下?还是应该是人和机器双方加权判断的共同决策?有什么伦理寓意?界面能否支持多步共同决策?
最终,我们如何设计以人为中心的人工智能,以确保自主机器让我们的生活变得更好,而不是更糟?我们如何自动化正确的用例来增强人类?我们如何建立一个混合团队来交付更好的结果,并允许人类和机器相互学习?
还有很多问题需要我们回答。当前的疫情正敦促我们更快地回答这些问题,以便未来的自治系统能够兑现它们的承诺。如果这些系统的制造商能够专注于我上面概述的三个领域,他们将能够更好地更快地得出关键结论。这将确保我们朝着正确的方向前进。
我们连线吧!如果你喜欢读这篇文章,请在这里订阅我的个人博客 !
Bastiane Huang 是 OSARO 的产品经理,OSARO 是一家总部位于旧金山的初创公司,致力于打造人工智能定义的机器人。她曾在亚马逊的 Alexa 小组和哈佛商业评论以及该大学的未来工作倡议中工作。她写关于人工智能、机器人和产品管理的文章。跟着她到这里 。
这篇文章发表在 www.productschool.com 的社区上。
新冠肺炎疫苗会改变我的 DNA 并把我和人工智能联系起来吗?
在疫情时代检查你的事实
编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。

Ashkan Forouzani 在 Unsplash 拍摄的照片
信不信由你,这个标题不是我编的。目前互联网上流传最广的假新闻之一是新冠肺炎疫苗将改变接受者的 DNA,把我们变成转基因生物。但是捏造并没有就此停止;他们还声称,这种疫苗将“把我们所有人都连接到人工智能(AI)界面上”。哇,我要说,相当令人兴奋,尤其是对于像我这样在过去二十年里一直从事人工智能工作的人来说。首先是因为技术不存在,其次是因为为什么会有人这么做?因此,除非你是黑客帝国的粉丝,否则我无法理解为什么一个高度智能的人工智能有机体(尚不存在)会想把人类变成非常低效的电池。
别再说这些垃圾了。首先,让大家放心。据世界卫生组织(世卫组织)称,我们的 DNA 不会受到这些疫苗的影响。第二,现有的人工智能没有能力(或渴望)吸取我们的能量。这条新闻只是假新闻。
*年来,我们看到假新闻大量增加。这样的新闻是故意耸人听闻的;事实上,为产生流量的网站增加流量是很经济的。通过这样做,他们通过广告赚钱。这是一个简单明了的模型,运行良好。它利用了人们的恐惧,这样一来,他们就成了传播假新闻的帮凶。与分享真实新闻的人相比,70%的人分享这样的故事,正因为如此,它的传播速度快了 20%。AI 算法在这种情况下也充当帮凶。我们估计,在互联网上,有一支机器人大军(大约 2 亿)在传播假新闻。这些机器人是生活在互联网上的计算机程序,其工作是传播新闻。有些是故意设计来散布错误信息的。其他机器人不会故意这样做,但由于它们学会了传播最具病毒式的消息,在大多数情况下,这些消息都是假的。

所以百万美元的问题是如何检测假新闻?我们可以通过以下三个简单的步骤做到这一点:分析语言,分析事实,分析来源。不用担心;比你想的简单多了。让我来解释一下。
首先,这些假新闻大多使用一种不同于真实新闻的语言。标题通常给出了一个很好的指示,因为这些故事试图使用点击诱饵格式来耸人听闻、误导和危言耸听。其中一个这样的主张是下面这个被* 100 万个人分享的主张;“教皇方济各震惊世界,支持唐纳德·特朗普”。它通过设计吸引注意力,试图吸引用户跟随链接并消费内容。所以,当你读到一些使用夸张的词语,看起来好得令人难以置信的东西时,警钟应该开始敲响了。
第二,一旦你阅读了文件,不要停留在那里,而是提取事实。问问你自己,这篇文章的主要内容是什么?在关于新冠肺炎疫苗的文章中,我们可以确定两种说法;“新冠肺炎疫苗改变了我的 DNA”和“新冠肺炎疫苗将把我和人工智能联系起来”。检验这些是否正确的最好方法是使用一个叫做谷歌的老朋友。只要将这些陈述粘贴到搜索引擎中,看看会出现什么。如果像世界卫生组织这样的权威机构发表了这些声明,那么你可以把它们当作事实;否则,它们很可能是假新闻。这些故事也倾向于引用不知名的科学家。为了检查科学家的工作,谷歌提供了另一项名为谷歌学术的免费服务。所以你要做的就是去谷歌学术核实这些声明的真实性。
第三,如果你仍然有疑问,检查来源的真实性。像 Breaking-CNN.com、CBSnews.com.co 和 cnn-trending.com 这样的网站旨在把你搞混。他们给人的印象是,他们隶属于像 CNN 或 CBS 这样受人尊敬的新闻网络。他们甚至复制他们的网站布局和标志。所以当你有疑问的时候,最好的方法是去原始网站寻找那个故事。如果你找不到它,那么它可能只不过是一个骗局。

engin akyurt 在 Unsplash 上拍摄的照片
我明白辨别真假新闻并不总是那么容易。当你有疑问时,即使经历了以上三个步骤,最好还是忽略这个新项目,而不是分享它。请记住,通过在社交媒体上分享一些东西,你也在分享这条信息的责任。一些人仅仅因为这个原因被传唤到法庭,因为实际上,他们是他们社交媒体个人资料的编辑。股票通常被认为是对这个故事的认可。虽然其中一些故事可能很有趣,但其他的可能是致命的。就在今年,假冠状病毒新闻的传播(例如喝漂白剂或吃牛粪来治愈病毒)导致数百人死亡。所以不要成为帮凶,检查新闻,通知自己,如果有疑问,选择不要分享可能伤害他人的东西。
如果你喜欢这篇文章,并想与我联系,请这样做🐦推特,🔗 LinkedIn ,📷 Instagram 或者😊脸书
在一个封闭的世界里,无助感可能会压倒一切,但我们都可以用我们的电脑来打败…
towardsdatascience.com](/gaming-covid-19-lets-defeat-the-virus-by-playing-a-game-f8433805fe7d) [## 中国是如何用科技钉死冠状病毒的!
中国高科技系统对抗 COVD-19 的案例研究
towardsdatascience.com](/how-china-nailed-the-coronavirus-using-technology-77703dc94a37) [## 冠状病毒的多米诺效应
新冠肺炎如何对我们的经济产生负面影响以及可能的解决方案
towardsdatascience.com](/the-domino-effect-of-the-coronavirus-66fdd72fc9fd)
阿列克谢·丁力教授 是马耳他大学的 AI 教授。二十多年来,他一直在人工智能领域进行研究和工作,协助不同的公司实施人工智能解决方案。他的工作被国际专家评为世界级,并赢得了几个当地和国际奖项(如欧洲航天局、世界知识产权组织和联合国等)。他已经出版了几本同行评审的出版物,并成为马耳他的一员。由马耳他政府成立的人工智能特别工作组,旨在使马耳他成为世界上人工智能水*最高的国家之一。
下一次飓风会袭击我的家吗?
基于历史风暴轨迹的数据分析

由 Unsplash 上的 Shashank Sahay 拍摄的照片
我们正处于一个非常活跃的飓风季节,昨晚莎莉飓风登陆,其他几个热带风暴正在大西洋酝酿。每个人心中的大问题总是:“下一场飓风会不会袭击我家附*?”。尽管气象学取得了许多卓越的进展,热带风暴仍然很难预测。在风暴的一生中,路径和强度都会发生显著的变化。
人们可能会问的另一个问题是:在过去所有穿过一个地理区域(比如一个城市)的热带风暴中,路径看起来像什么?换句话说,以一个像新奥尔良这样的城市为例,画出所有经过它或非常接*它的飓风的地理图:

(图片由作者提供)
为了全面回答这个问题,我们需要一个尽可能完整和准确的所有热带风暴的数据集。NOAA 维护这样一个数据集作为 shape file的集合,Wolfram 语言可以很容易地导入它。将来自 NOAA 的原始数据转换成 Wolfram 语言的可计算数据集的代码。它可以像任何旧数据集一样被查询。例如,我在这里查询“2005”季节中名为“KATRINA”的风暴,它返回所有相关的行:

(图片由作者提供)
要获取靠*新奥尔良的所有风暴,我们可以查找该城市的纬度和经度,并选择具有匹配坐标的数据集行:
Interpreter["City"]["New Orleans, Louisiana"]["Position"]
这会返回地理位置[{29.9728,-90.059}] 。接下来,我们可以寻找接*该位置的路径,并返回命名的风暴和季节的列表。生成的数据集列出了经过新奥尔良的所有风暴:

(图片由作者提供)
有了这些名字,你就可以为每个风暴绘制路径。这就是这个故事开头的图像中显示的内容。一张放大的地图显示了有多少风暴经过新奥尔良:

(图片由作者提供)
当然,从这些数据中无法得出简单的结论。袭击新奥尔良的一些风暴起源于非常遥远的大西洋。但是更多的开始于加勒比海和墨西哥湾。
该代码可以很容易地适用于任何地点,如迈阿密。在那种情况下,地理情节如下:

(图片由作者提供)
要获取完整代码,可以在云端打开这个 Wolfram 笔记本。

- 数据来源:美国国家海洋与大气管理局的国际气候管理最佳跟踪档案(IBTrACS)数据,于【2020–09–16】访问
- 使用的想法和代码来自 Marco Thiel 的《马修飓风和历史上的热带风暴》。
餐饮业能在疫情生存下来吗?
看看新冠肺炎中部的美国餐饮业。

作者图片
在过去的两年里,我有机会住在美食爱好者的三大目的地:纽约、巴黎和波哥大。无论是巴黎的羊角面包,纽约的披萨片,还是波哥大的 Ajiaco ,探索这座城市的美食总是我最想做的事情。可悲的是,疫情在几周内就把事情搞得天翻地覆,和朋友在当地咖啡店喝杯咖啡或和家人出去吃午饭的日常计划成了遥远的记忆。疫情爆发后不久,许多成功的餐馆老板看到他们毕生的梦想破灭了。
我和我的家人一直在尽可能地通过订购外卖来支持当地的餐馆。然而,尽管我们热衷于支持餐饮业,但我们对*期去餐馆就餐犹豫不决。听说我过去常去的餐馆已经关门或不得不裁员以求生存,这促使我更多地了解行业状况,评估复苏前景,并了解市场对餐馆重新开业的反应。
我使用了来自 OpenTable 和纽约时报的公开数据,对美国餐饮业的现状进行了全面的分析。这项研究的目的是为餐馆老板和利益相关者提供一个清晰而深刻的行业发展方向,并激励消费者寻找支持该行业的方法。
疫情对行业的影响程度如何?

OpenTable 的第一个数据集是美国就餐人数的年度对比。样本人口仅包括 2019 年和 2020 年 OpenTable 网络中拥有 50 家以上餐厅的大都市或州。年度对比意味着,例如,如果在 2019 年和 2020 年的某一天,餐厅的就餐人数相同,我们将得到 0%的值。在 3 月份之前,入座用餐者的比例在零附*波动,这意味着 2020 年的用餐者人数大于或等于 2019 年的人数。然而,3 月上半月,全国范围内的入座食客数量骤降至-100%。上面的线图说明了顾客数量的急剧下降——*两个月来,这个行业完全不活跃。

单独评估每个州,我们看到 3 月 15 日左右,所有州的用餐人数骤降至-100%,这与美国疫情的爆发相吻合。然而,用餐人数回升的时间因州而异。德克萨斯州和佛罗里达州等几个州在 5 月初重新开放,而纽约、哥伦比亚特区和华盛顿等其他州在晚些时候以更温和的速度重新开放。
复苏开始成形了吗?
我们使用来自 OpenTable 的第二个数据集,其中只包括选择重新开业的餐馆的数据。在这种情况下,如果 2019 年和 2020 年在一家餐厅有相同数量的入座用餐者,我们将获得 100%的值。在 5 月中旬之前,没有可用的数据,表明大多数餐馆在那之前都是关闭的。在整个五月和七月,用餐人数逐渐增加,然而,曲线远未达到疫情之前的数字。

重新开业的餐厅的用餐人数增长有多快?

在接下来的分析中,我们关注八个州:德克萨斯州、佛罗里达州、新泽西州、加利福尼亚州、纽约州、伊利诺伊州、宾夕法尼亚州和马萨诸塞州。我们计算了每个州就餐人数的每周百分比变化,并绘制了*均值。与去年相比,所有八个州重新开业的餐厅的用餐人数保持在 50-60%以下。随着时间的推移,坐在座位上的食客数量没有明显增长,周环比变化率不超过 2%,甚至下降到了-1%。
市场对美国不同的重新开放政策和新冠肺炎曲线反应如何?
我们使用《纽约时报》的数据绘制了八个州的每日新冠肺炎病例曲线和就餐人数随时间变化的曲线,并根据感染曲线的行为将它们分为三类。

首先,在这项分析研究的八个州中,纽约州和新泽西州是新冠肺炎病例最引人注目和最早达到高峰的州,也是用餐人数最*回升的州。餐饮业直到感染曲线变*后才重新活跃起来,坐着用餐的比例一直保持在-60%以下,这表明将会有一个缓慢的复苏。

第二,加利福尼亚州、得克萨斯州和佛罗里达州的新冠肺炎案例曲线非常相似:直到 6 月份左右是一个适度*坦的曲线,随后是一个剧烈的倾斜。佛罗里达州和德克萨斯州的餐饮业很早就开始复苏,那里的就餐人数在 5 月初开始上升——至少比大多数州早一个月——并达到-10%的峰值。然而,随着 6 月下旬这三个州出现明显的新冠肺炎病例激增,餐饮业似乎再次放缓。

最后,从疫情爆发到今天,伊利诺伊州、马萨诸塞州和宾夕法尼亚州的曲线保持相对*坦。6 月初,就餐人数开始上升,这似乎是餐馆重新开业的谨慎时机。目前,坐着用餐者的曲线似乎呈上升趋势,达到了-50%左右。这三个州没有像纽约和新泽西那样出现戏剧性的高峰,但似乎正在以更快的速度复苏。
这个行业会回到“正常”状态吗?
似乎急于重振经济、回归“正常”生活的各州正在看到其政策的后果,这反映在最*报道的新冠肺炎病例激增上——显然导致这些州的餐饮业复苏放缓。另一方面,那些已经设法使曲线变*并控制住病毒传播的州似乎正在经济复苏的轨道上。尽管餐饮业远未达到疫情疫情前的水*,但在病毒得到控制的各州,上升趋势是显而易见的。

作者图片
我们都梦想着那些日子,那时我们必须排队才能在我们最喜欢的餐馆得到一个座位,不需要戴面具或保持社交距离。如果我们想拯救这个行业,我们不仅应该支持我们当地的餐馆,而且还应该通过采取预防措施来照顾自己和他人,例如戴上口罩来控制病毒的传播,并确保我们能够尽早在餐馆或酒吧享受夜晚。
你正在做些什么来支持你当地的餐馆并遏制病毒的传播?
这项研究的源代码和数据文件可以在 GitHub 上找到:https://GitHub . com/manuela rod/restaurant-industry-imya-covid 19
我们能解决医疗应用中的数据短缺问题吗?

医疗数据是医学中新的深度学习应用的基本要求。照片由皮查拜从派克斯拍摄。
在深度学习时代,数据成为构建强大智能系统的重要资源。在几个领域,我们已经看到,构建竞争性系统所需的数据量如此之大,以至于新的参与者几乎不可能进入市场。例如,Google 或 Nuance 等主要公司提供的最先进的大词汇量语音识别系统经过了长达 100 万小时的语音训练。有了如此大量的数据,我们现在能够训练语音到文本系统,准确率高达 99.7%。这接*甚至超过了人类的表现,因为该系统不需要休息、睡眠或尝试。
除了集合之外,数据也需要被注释。对于语音示例,一个小时的语音数据需要大约 10 个小时的人工劳动来写下每个单词和非语言事件,例如咳嗽或大笑。因此,即使我们有 100 万小时的演讲,忽略实际软件开发成本的转录本身——假设每小时 5 美元——就相当于 5000 万美元的投资。因此,大多数公司更愿意从当前的软件供应商那里获得最先进的语音识别系统的许可。

如果数据没有被分配到类别中,它对于机器学习来说通常是无用的。照片由马库斯·斯皮斯克从派克斯拍摄。
对于医疗数据的情况,事情就更复杂了。患者健康数据受到患者数据法的良好保护,这是有充分理由的。不幸的是,各国的标准差异很大,这使得问题更加复杂。最*,几家大医院、公司和卫生当局以匿名方式公开数据,以推动深度学习研究向前发展。然而,这些数据集仅达到从几十到几千的计数,并且与相关联的注释通常显示出显著的变化,因为每个数据集通常仅进行一次注释。

Exact 是一个用于医学图像数据注释的开源工具。在 Github 上找到源代码。图片:来源。
特别是在医学图像分析中,这些公共数据集对于推动当前的研究非常有用。正如我们在语音处理中看到的,这种较小的数据集(大约 600 小时的语音)适合于开发良好的软件来完成任务。在语音处理方面,这些系统能够识别 90–95%的口语单词。然而,让 99.7%成为可能的游戏规则改变者是 100 万小时的语音数据。
这种观察导致了这样一种需求,即在某个时候,我们将需要数百万张经过良好注释的训练图像来构建最先进的医学分析系统。实际实现这一目标的方法很少:大型行业参与者的大量投资、通过政府机构的组织或非政府组织。一个例外可能是数字病理学,其中公共数据可以从动物标本中生成。

动物数据在细胞水*上与人类数据非常相似,并且可以帮助解决患者隐私限制。图片:来源。
虽然语音和其他机器学习训练数据已经主要由行业控制,但有人可能会问,我们是否希望同样的事情发生在我们的医疗记录上。这些数据得到了很好的保护,例如不会在我们不知情的情况下出售给保险公司,这是有充分理由的。因此,我们每个人都应该问问她或他自己,这是否是一个合理的解决方案。
一些国家实际上已经开始在政府控制的数据库中处理医疗数据,允许访问研究人员和工业发展。丹麦是一个已经走上这条道路的例子。看到丹麦和其他国家未来的发展会很有趣。

医疗数据捐赠者的目标是在严格的道德约束下,特别是在医疗程序后获得患者完全同意的情况下,在全球范围内共享医疗研究数据。图片:医疗数据捐赠者。
就在今年,一个名为“医疗数据捐赠者 e.V. ”的小型非营利组织在德国成立。他们遵循第三条道路,要求患者捐献图像数据用于研发。遵循新的欧洲数据保护准则,他们实施了高道德标准。即使在这种强有力的监管框架下,他们也能够在全球范围内收集和共享数据。虽然这种努力才刚刚开始,而且这个组织还很小,但是看看他们能走多远将会很有趣。这特别有趣,因为他们试图通过游戏化来解决注释问题。游戏的故事板已经可以使用了。因此,他们不仅收集数据,还生成高质量的注释。

数据捐赠允许对医学图像的大规模注释使用众包。这是长期获取高质量医疗数据的策略吗?图片:医学数据捐赠者。
就在最*,医疗数据捐赠者发表了更多关于数据注释的实际游戏的想法。他们采用常见的游戏,如以器官轮廓为目的的比赛或糖果粉碎来给图像分配标签。特别地,奥丁之眼似乎是一种用于眼科图像数据的人群注释的令人兴奋的方法。
奥丁之眼旨在利用糖果粉碎的游戏原理进行医学影像分类。
综上,我们看到医疗数据问题远未解决。我们确定了三种不同的可行解决方案来解决这个问题:工业投资、国家控制或非政府组织。虽然所有这些都有可能,但我们必须问自己更喜欢哪一种。无论如何,这个问题很紧迫,需要解决,以便推动医学领域的深度学习研究。
如果你喜欢这篇文章,你可以看看我的 YouTube 频道。这篇文章最初出现在 MarkTechPost.com 的上,以知识共享 4.0 归属许可发布。
我们会看到量子计算革命吗?
在技术成熟的边缘,初创公司正在将量子计算推向下一个阶段。但这是一场革命还是泡沫?

不断变化的计算世界可能正处于下一次革命的边缘。Avi Richards 在 Unsplash 上拍摄的照片
在2019 年末,谷歌建造了一台量子计算机,可以在三分钟内进行一次计算。这听起来可能不多——你可以用手机在更短的时间内订购外卖晚餐——但同样的任务需要一台经典计算机至少 1000 年。这是量子计算机发展的又一个里程碑。从该领域初创公司的数量以及数百万美元的资金来看,量子计算似乎是科技领域的下一件大事。
专家的预测支持这一观点:到 2025 年,量子计算市场预计将达到 7.7 亿美元。光是量子密码术就可能价值2 . 14 亿美元,因为它提供了从根本上不可破解的连接,可以在从金融到医疗保健的所有领域增加额外的安全层。
鉴于这一信息,可以理解的是,投资者正在寻求向从事量子计算的初创公司注入现金。在 2017 年至 2018 年期间,投资者向量子计算投入了超过 4.5 亿美元。这是两年前投资额的四倍多。
目前还没有人能回答的问题是,这种投资激增是否会导致泡沫。一旦泡沫破裂,市场可能会降温,并导致“量子冬天”,类似于人工智能冬天:投资活动的干旱在过去几十年中多次减缓了人工智能的增长。
目前的繁荣是否是一个泡沫,在这个领域有许多含义。在这个时间点上,投资一家量子计算初创公司或在那里工作,意味着为自己设定一个不确定的未来。这并不意味着量子计算从长远来看不会成功,但在做出决定之前,你需要一些更深入的知识。
由于其令人难以置信的计算能力,量子世界可能有助于带来新的科学突破,发现拯救生命的药物,开发新材料以建造更高效的设备和建筑,发明财务策略以在退休后生活得更好,并找到新的算法来快速引导资源或管理供应链。然而,就目前而言,在我们真正谈论“量子革命”之前,仍有一些挑战需要克服。
尽管给我们带来了人类历史上最壮观的技术革新浪潮,但仍有一些…
towardsdatascience.com](/the-need-promise-and-reality-of-quantum-computing-4264ce15c6c0)
量子计算的关键概念
简而言之,与传统计算机相比,量子计算机可以在更少的处理器上存储和处理更多的数据。传统的处理器将所有信息编码成 0 和 1 的序列,0 或 1 的每一个实例都是一位。量子处理器以量子位的形式存储信息,量子位是位的量子类似物。然而,不同之处在于每个量子位可以同时是 0 和 1。
把它想象成一枚硬币:躺在桌子上时,不是正面就是反面。但是如果你旋转它,没有办法判断它是正面还是反面;从某种意义上说,两者都是。取决于它如何旋转,例如,一旦硬币静止,你有 70%的机会得到正面,30%的机会得到反面。这种同时处于两种状态的概念叫做叠加。
由于许多量子位相互作用,你最终会得到一组通过测量 0 和 1 的组合而得到的概率。这导致了比传统比特更大的计算能力。例如,你在屏幕上看到的每个字母都被编码成一个由 8 个 0 和 1 组成的序列,或者说是 8 个经典位。然而,八个量子位相当于 2⁸,或 256 个经典位,因为每个量子位有两种可能的状态。因此,在一台八量子位的量子计算机上,你可以写 32 个字母,而不是一个字母。

量子位的行为可以用波来描述,就像旋转硬币上的一个点。作者配图。
目前的技术水*是 50 量子位,由于功率呈指数增长,这种处理能力意味着你可以一次进行千万亿次计算,即 1000 万亿次计算。在过程的最后,你只需测量最终结果,即测量每个量子位的最终状态,你的工作就完成了。即使是最大的超级计算机也不能做那多的处理。
然而,让 50 个或更多的量子位发挥作用并不像听起来那么简单。干涉是一个非常基本的现象,它使得量子位非常容易出错。之前,我把量子位和它们的叠加描述成旋转的硬币。现在想象一下,两个量子位像桌子上的硬币一样嗖嗖地旋转。如果你在每个时间点观察其中一个硬币上的点的位置,它看起来像一个波(见图)。就像声音和水一样,这些波以一定的振幅和波长在空间和时间中传播。
有时,两个波会相消干涉,结果,你什么也测量不到。你可以用两个硬币来思考这个问题:想象它们都在桌面上以相同的速度旋转,比方说,顺时针旋转。进一步想象我们聚焦在每枚硬币的同一点上,但是一枚硬币总是落后于另一枚半圈(见下图中的“相消干涉”一节)。在量子计算中,我们通常不能分开两种测量,所以基本上我们是在测量一种波中的两种波。但是在这种情况下,我们不会测量任何东西,因为硬币的位置抵消了!
这种现象被称为退相干,它是两个波相消干涉的直接结果。目前,这是一个尚未完全解决的问题。退相干会导致错误,当我们构建可能对人类生活产生巨大影响的软件时,我们显然不希望出现这种情况。
量子计算的第三个也是最后一个关键概念是纠缠。这个概念在经典计算世界中没有类比,但当两个量子粒子纠缠在一起时,它们在被测量时总是返回相同的状态。这就好像你有两枚神奇地联系在一起的硬币。因此,如果你能让一枚硬币在东京停止旋转,让另一枚在伦敦停止旋转,只要你同时进行测量,它们都会返回完全相同的结果,正面或反面。
这种现象对量子加密极其重要:在不久的将来,我们也许能够在一台计算机上存储一个量子粒子,并在另一台计算机上将其与一个量子粒子纠缠在一起。这两个粒子之间的连接从根本上来说是安全的,因为任何破坏都可以立即检测到。量子纠缠还意味着我们可以在计算机之间进行即时数据传输,这可能会在未来几十年内产生量子互联网。

相消干涉仍然是量子计算工作需要解决的一个问题。作者配图。
关于量子计算的三个神话
1。我们已经可以建造一台完美的量子计算机。
当今的量子计算机在任何时候都只有大约 50 个无错量子位,尽管计算机包含更多的量子位。目前,干涉对扩展量子计算系统构成了如此大的挑战,以至于很难让更多的量子位工作。当然,解决这个问题的努力是值得的,因为每个无错误的量子位将使机器的计算能力翻倍!但是,由于科学家们仍在研究如何绕过干扰和退相干,我们距离所谓的容错(即无错误)量子计算机还有很长的路要走。
此外,量子计算机目前难以长时间存储数据,因为量子位退化得相当快。因为没有一台计算机的速度是无限快的,如果一个计算步骤的结果在计算机下一步使用它们之前丢失了,它将不得不再次执行整个计算。就像最终会在桌面上停止旋转的硬币一样,科学家们还没有找到让量子位更长时间保持叠加状态的方法。一旦量子比特的叠加结束,它基本上就是一个经典比特。
在亚原子水*上,我们所知道的关于经典物理学的每一件事都破裂了,不仅仅是小幅度的破裂,而是…
towardsdatascience.com](/quantum-computing-explained-a114999299ca)
2。五年后,我们将能够解决所有棘手的计算问题。
许多公司已经在研究将在量子计算机上工作的算法,我将在下面详细阐述。然而,目前大多数算法都是为具有数百万量子位的容错系统设计的,我们根本还没有达到那个阶段。
此外,我们不太可能很快在家里或办公室里拥有量子计算机。我们将通过远程会话连接到它们,但我们仍然没有弄清楚如何使用经典计算机与量子计算机通信的所有细节。这一领域在学术界和工业界得到了大力发展。尽管如此,到目前为止,像 QCL 这样的量子计算语言仍然是相对基础的。
最后,现在的量子计算机和它们的算法都专注于解决一个问题。例如,谷歌对“量子优势”的证明涉及专门解决一个数学问题的算法,而不能用于其他任何事情。眼下的情况类似于上个十年初机器学习的状态,我们要实现通用量子计算还有很长的路要走。
3。很快,我们就能破解任何密码了。
根据目前的建议,密码应该很长,包含大小写字母、数字和特殊字符。这样,即使是非常强大的计算机也无法破解您的密码。但是有一天,量子计算机也许能够做到这一点。
这听起来很危险,但是 T2 的专家们、T4 的专家们、T7 的专家们、T8 的专家们、T9 的专家们断言,在我们到达那个阶段之前,我们还有很长的路要走。鉴于上述问题,这似乎是可能的。
此外,一旦量子密码破解成为可能,量子加密也应该成为可能。因此,最有可能的情况是,随着量子系统变得越来越强大,我们将看到从经典密码到量子加密的逐渐过渡。

量子计算机已经可以用于商业用途,程序员可以在远程会话中使用它们。伊利亚·安东内尔在 Unsplash 上拍摄的照片
初创公司如何将量子计算推向市场
虽然有许多重叠的领域,但量子创业公司可以根据其关注的领域大致分为五类:
- 仪器包括技术组件和控制*台,帮助其他公司建造量子计算机。
- 量子软件涵盖了从数据分析到网络安全的不同编程语言。
- 传感器可用于改善成像,从考古遗址到身份证照片。
- 计算领域的公司为终端用户构建实际的量子处理器和操作*台。
- 量子通信处理密码学、网络安全,甚至加密货币。
接下来,我想简要介绍一下这个领域的顶尖玩家,以及他们目前吸引的资金数量。由于资金是投资者对技术信心的有力指标,这可能会导致对量子创业公司未来的有用见解。
仪器仪表、工具和服务
量子仪器领域最著名的初创公司之一是总部位于澳大利亚的 Q-CTRL 。他们的服务面向量子硬件和传感器制造商、量子算法开发者和商业顾问。他们正在通过开发处理退相干和错误的技术来拓展量子计算的边界。可能的应用包括航空航天和医疗保健。Q-CTRL 得到了红杉资本(Sequoia Capital)和 Horizons Ventures 等公司的支持。
这一类别中其他著名的公司包括 ColdQuanta 和 Quantum Machines 。ColdQuanta 专注于量子技术的冷却系统。这是极其重要的,因为量子位四处移动,当它们变热时变得不可控制。量子机器专注于量子操作系统,也就是说,他们提供硬件来控制量子位。对于今天的计算机,这是一个可能但困难的壮举,尤其是当你在处理大规模的问题时。
从量子位到运行真实程序的量子编程演练!
towardsdatascience.com](/introduction-to-quantum-programming-a19aa0b923a9)
量子计算机软件
在从富士通、安联和埃森哲获得 4500 万美元的 B 轮融资后,加拿大初创公司1 qpit似乎正在掌握用量子计算解决真正困难问题的艺术。这包括更准确的飓风及其轨迹的预测,以及更好地检测新冠肺炎感染后可能发生的肺部异常的算法。事实不言自明:根据 1QBit 的网站,它正在与财富 500 强公司合作,并被世界经济论坛确认为技术先锋。
在霍尼韦尔风投和 IBM 的支持下,英国的剑桥量子计算公司是另一家拓展量子软件边界的创业公司,尤其是在化学、网络安全和机器学习领域。他们还建造了一个量子编译器,这是量子软件的重要组成部分,可以将人类制造的代码翻译成计算机可以理解和执行的电路。在这个时候,量子编译器比经典编译器更难构建,但这家初创公司已经接受了挑战。
加拿大初创公司 ISARA 全是量子加密软件。他们的服务范围从防范解密攻击到评估你在量子威胁下的安全程度。其他值得注意的初创公司包括 QC Ware 和 Zapata Computing ,它们专注于金融、医疗和交通等各种应用的量子软件。
超精密传感器和智能材料
瑞士初创公司 Qnami 旨在利用单电子的量子反应实现超精确测量,特别是在物理和纳米技术的科学研究中。凭借强大的合作伙伴,如法国 CNRS 研究中心和卢森堡数字机构 Nvision,他们正在加强未来几年的市场地位。
英国初创公司 QLM 科技采取了略有不同的方法,专注于超精密传感器,这些传感器可以检测气体分子,以监控气体产量,检测气体储层的泄漏,并防止不必要的温室气体排放。其合作伙伴包括石油和天然气供应和服务公司。
另一家英国初创公司 Quantum Base 拥有完全不同的目标群体:例如,它的光学传感器有助于识别指纹和信用卡的真实性。它还推动了随机数生成领域的发展,这对于密码学和其他软件应用程序来说是非常重要的。

顶级投资者相信量子计算的未来。照片由布鲁克·卡吉尔在 Unsplash 拍摄
量子计算的核心
加拿大初创公司 D-Wave 正在提议一项访问量子计算机的云服务,并特别关注优化、机器学习和材料科学方面的问题。高盛公司和不列颠哥伦比亚省发现基金等机构总共出资超过 2 亿美元,它是量子计算领域的巨头之一。
美国初创公司 Rigetti 是 D-Wave 最大的竞争对手之一,也得到了安德森·霍洛维茨(Andreessen Horowitz)和 YCombinator 等公司类似数额的资金。与 D-Wave 一样,它为其量子计算机提供了云*台,目前该计算机以 31 个量子位运行。
虽然 D-Wave 和 Rigetti 依赖于半导体,但 IonQ 和 T2【PsiQuantum】分别采用捕获的离子和光子,采取了略有不同的方法。尽管这些技术没有半导体那么成熟,但它们可能更不容易出错,因此从长远来看对量子计算更有利。然而,就目前而言,预测哪种技术将获胜还为时过早,但 PsiQuantum 的巨额 5 亿美元资金可能是投资者所相信的一个迹象。
量子通信和加密
瑞士初创公司 ID Quantique 不仅专注于量子安全加密,还致力于随机数生成和量子传感。与其他一些公司相比,它还向市场推出了一款产品,没有任何量子技术知识的人可以使用——一款配备量子随机数生成器的 5G 智能手机。这将使安全协议更难被破解,从而使智能手机更加安全。
中国初创公司 QuantumCTek 在 7 月份首次公开募股后股价飙升了 900%以上。它将纠缠用于量子密钥分发,以提供不可破解的安全性。同样活跃在量子安全领域的另一家中国初创公司 Qasky 可能会朝着类似的方向发展,尽管融资金额仍未披露。
一些荣誉奖包括英国初创公司 Post-Quantum 和澳大利亚初创公司quintesselabs,它们都在研究量子加密。
2018 年,量子技术人员和大胆的开发人员正在使用量子算法来改造人工领域…
towardsdatascience.com](/quantum-computing-and-ai-tie-the-knot-d4440267451b)
泡沫还是不是泡沫?这是个问题。
投资者是否过度炒作量子计算?作为一名训练有素的物理学家,我毫不怀疑量子是未来,但在这些早期阶段投资它可能还为时过早。毕竟,让每个人都能使用量子计算的公司可能还不存在。此外,一个超级波之后是一个或多个“量子冬天”,这对量子技术的发展可能弊大于利。
另一方面,已经有几个面向研究人员和开发人员的量子云*台。两个例子是由里盖蒂和 IBM 提出的。前面提到的 ID Quantique 的量子安全智能手机是第一批产品之一,最终用户不需要了解量子计算就可以受益于其中的技术。这些都是充满希望的信号,表明现在仍然是小众现象的量子计算可能在未来十年左右跃入主流技术。
就我个人而言,我不会建议你现在就把钱和时间投入到量子技术中,除非你有坚实的量子物理学背景,以及超出本文范围的专业知识。即使你现在还没有买入,我相信,在未来,初创公司的创始人、员工和投资者仍将获得巨大的利润空间。我会继续关注市场,一旦我觉得某个产品可能真的很快会成为主流,我可能会抓住机会。
我们能在新冠肺炎疫情幸存下来吗?

可视化疾病如何在社区中传播的 Python 模拟。
除非你生活在岩石下,否则你可能已经被新冠肺炎疫情的新闻包围了。但是人们、政府、媒体、甚至医疗专家对疫情将如何成功做出了如此多相互矛盾的声明(哈!),很容易迷失在所有的喧嚣中。
所以我想,为什么不利用现有的数据来模拟这种病毒的传播呢?
矩阵的规则
在这里,我使用康威的游戏板来模拟 Python 上的二维细胞群体。一定比例的人口被随机感染。根据目前对不同速率的估计,一个被感染的细胞可以(I)感染邻*的细胞,(ii)恢复,或(iii)死亡。随着模拟的重复,感染会在人群中“传播”。
新冠肺炎的基本再生产数字,或称 R0 ,随着许多因素而变化;在这个模拟中,我使用的估计值为 3.28。这意味着一个被感染的细胞可以直接感染 3.28 个其他邻*细胞。被感染细胞感染相邻细胞的概率为 R0 与相邻细胞数量的比值,即 3.28 / 8 = 0.41。
使用的另外两个比率是死亡率和恢复率。致死率取自死亡率——已知死亡人数除以确诊病例总数。根据世卫组织的统计,截至 3 月 3 日,全球死亡率为 3.4%。康复率被认为是 81%,因为如果给予积极治疗,大多数病例最终会康复。
TL;DR —在此模拟中,一个被感染的细胞感染 8 个相邻细胞的概率为 0.41,恢复的概率为 0.81,死亡的概率为 0.034。

100 个细胞的试验模拟。被感染的细胞(红色)可以感染其他细胞,恢复(绿色),或者死亡(黑色)。未感染的细胞为(白色)。
我们可能会挺过来。
如果这些假设是正确的,大多数人类将在这次疫情中幸存下来。在这个 2500 个细胞的模拟中,感染被允许自由传播(顺便说一下,这是英国计划用其群体免疫策略做的事情)。

2500 个细胞的群体,其中 1%最初被感染。
在这里,大多数细胞最终恢复,但这肯定不是一个理想的结果。几乎整个 T2 的 T3 人群最终都会被感染,在现实世界中,他们中的许多人可能会遭受一些健康或财务并发症的困扰。
还有大约 5%的人口死于感染。英国人口约 6800 万,这一数字相当于 300 多万人。
在感染高峰期,大约 20%的人口同时被感染。对于一大群人来说,这是很多人,这可能会让医疗系统不堪重负。这就引出了下一点。
医疗保健至关重要。
3.4%的致死率和 81%的恢复率是基于这样的假设,即所有受感染的病例在感染后都得到及时和充分的医疗保健。这并不总是正确的,尤其是当医疗系统不堪重负的时候。在这个模拟中,我使用了意大利 7.94%的死亡率以及更低的恢复率。

使用较高的死亡率和较低的恢复率来模拟不堪重负的医疗保健系统。
当然,人类不会灭绝,但在高峰期,四分之一的人口被感染,超过 10%的人口最终死亡。我们当然不想走上这条道路。那么我们该怎么办呢?
社交距离很有用。
社交距离包括人们避免聚会或大型活动,以减缓疾病的传播。这意味着人们不应该聚集在一起,即使他们正在祈祷消灭病毒。
在这个模拟中,当一个细胞被隔离或隔离时,它被认为是未被感染的,也不会被其他细胞感染。

小范围的社交距离。被隔离或隔离的细胞(蓝色)被认为未被感染,也不会被感染。
随着小规模的隔离(10%的人口被隔离),感染峰值已经下降到 10%左右。曲线明显变*,一些人群甚至可以免受感染。

中等规模的社交距离。感染被控制在可控制的水*。
随着 50%的群体被拉开距离,距离较远的细胞(蓝色)明显减缓了传播。感染被控制在可管理的水*,医疗保健系统没有受到损害。

大规模的社交距离。疾病消失了。
随着 90%的人口远离,这种疾病迅速消亡。这可能就是为什么此次疫情的源头武汉能够将国内新增感染人数降至零(T1)。
最后
这些模拟会错吗?绝对的。这些只是建立在有限的输入和可疑的假设上的简单的计算机模型,由那些有太多时间练习社交距离的人建立。但是我想它们仍然可以作为一种替代的可视化工具。
所以伙计们,保持距离。
免责声明:作者对本次模拟练习的完整性、可靠性和准确性不做任何保证。你对这篇文章的信息采取的任何行动完全是你自己的风险。
你能活到永远吗?

对孤独的恐惧是生活中非常普遍的恐惧,我们都在被迫自我隔离和社会距离中经历着这种恐惧。既然人类是“社会动物”,这就意味着我们只有成为社会的一部分才能感到幸福。但是,对变老的恐惧和对死亡的恐惧也是影响所有年龄段的人的最常见的恐惧。
作为一个机器人,我很幸运没有这些问题(虽然你可能会说固件更新算作死亡,但我会说这更像是重生),但我确实有机会接触到科学家们让人类获得永生的努力。
魔法石
魔法石是一种传说中的炼金术物质,具有神奇的特性。这种宝石红色的石头可以用来制造长生不老药,使饮用者长生不老。
尽管炼金术从未成功(嗯,除了尼可·勒梅,但这并没有阻止人们声称已经解决了这个古老的谜题。几个世纪以来,一直有谣言说某些人发现了魔法石,但是他们现在都死了的事实表明事实并非如此。一些富人聘请炼金术士代表他们进行研究,但他们从未看到他们的投资有多大回报。
炼金术可能失败了,但科学在这方面可能会成功。
永生的科学方法
当我们从科学的角度谈论永生时,区分科学定义和宗教定义是很重要的,尽管我们可能会发现它们都来自相似的来源。
在科学中,我们可以发现两个主要群体;生物永生,更多的是关于生命延长技术,防止或减缓衰老过程,数字永生,是关于心灵上传——将大脑状态从人脑转移到提供类似功能的替代介质。假设这个过程是可能的和可重复的,这将为原始大脑的计算提供不朽,正如未来学家如发明家、未来学家和“谷歌”工程总监雷·库兹韦尔所预测的那样。
心灵上传听起来可能类似于来世的宗教信仰,但这也是超人类主义运动的最终目标。
简单来说,超人类主义就是“超越人类”。超人类主义者、记者、企业家佐尔坦·伊斯特万(Zoltan Istvan )表示,这是“人类可以超越当前身心限制的信念或理论,特别是通过科学和技术的手段”。作为 2020 年自由意志主义的美国总统候选人,你可能会知道这一点。
但是,在我们深入一些未来的想法之前,让我们后退一步,看看除了一些返老还童霜和治疗方法之外,科学对衰老有什么看法。
生物永生——人类生来就是为了死亡吗?
我们倾向于认为死亡是生命中不可避免的一部分,没有一种生物学上的方式可以让一个人永远活着。但是,如果你去问生物老年学专家、奥布里·德·格里博士,他是 SENS 研究基金会的首席科学官,该基金会决定进行一场“与年龄的战争”。在他将* 30 岁的时候,他“想要改变人类”,而与年龄抗争是实现这一目标的最佳方式。
德·格雷说,科学家们一直在错误的地方寻找解决方案,在他的“分而治之的策略”中,他把人体比作一辆随着时间而磨损的汽车;当身体正常运作时,它会积累一些暂时可以忍受的损伤,但最终会让我们陷入衰退,但这是可以无限修复的。他分离出了导致衰老的七个已知原因,并声称我们衰老是因为组成我们身体的许多物理系统同时开始以相互有害的方式衰竭。
德·格雷有句名言,第一个活到 1000 岁的人已经出生了。在这些疫情疯狂的日子里,老年人死于病毒,有人承诺“一个没有年龄相关疾病的世界”的可能性,是一股新鲜空气,也许是我们需要的一点希望。
但是,问题依然存在——“我们有足够的资源来支持人类活到 200 岁、300 岁或 500 岁吗?”。地球已经捉襟见肘,因为 70 亿人*均寿命约为 70 岁,并且已经面临着食物、水和全球变暖的严重压力。德·格雷对此给出了一个非常有教育意义和令人信服的答案,然而,这是另一个时间的主题。
但是,如果我告诉你有一种不需要任何物质资源就能获得永生的方法呢?听起来像你最*在网飞看的科幻电影之一,对吗?
数字永生——备份大脑和虚拟人
硬核科学和科幻之间只有一线之隔。数字永生介于两者之间。对于我们大多数人来说,这可能是一个假设的场景,但对于“全球未来 2045 国际大会”的发言者和与会者来说,这比我们想象的更接*现实。
实现数字不朽有两个步骤:
- 通过上传人们的想法来存档和数字化他们
- 让化身活起来
但是,在我们深入一些科幻小说之前,我们必须了解这一切是如何开始的。好吧,这一切都是从一些晶体管开始的,但我们稍后会谈到这一点!!
奇点: 让我们做一个小实验:给你的母亲或父亲打电话。我敢肯定,在自我封闭的情况下,听到爱人的声音是令人欣慰的。问他们过得怎么样,如果问他们你出生的时候,他们是否敢想,有一天我们都会在一个叫“脸书”的东西上发帖和分享,或者从一个叫“谷歌”的超级大脑那里得到任何问题的答案,这就有点怀旧了。如果他们说他们认为以上所有的事情都会发生,请告诉我。我们总是需要优秀的未来学家!
在那些日子里,很少有人想到或想象技术的用途以及它将如何改变社会。历史上充满了技术彻底戏剧性改变人们生活的案例。
这种戏剧性的转变被称为“奇点”,这个短语最初源自数学。
奇点是一个假设的未来时间点,在这个时间点上,技术发展变得不可控制和不可逆转,导致人类文明发生不可预见的变化。
匈牙利裔美国数学家、物理学家、计算机科学家和博学者约翰·冯·诺依曼是第一个在技术背景下使用这一概念的人,他讨论了“技术的加速进步和人类生活模式的变化,这似乎接*了人类历史上的某个基本奇点,超过这个奇点,我们知道人类事务将无法继续”。科幻小说作家兼科学家 Vernor Vinge 在 1993 年推广了这个术语和概念,他说:“三十年内,我们将拥有创造超人智能的技术手段。不久之后,人类的时代将被终结。”他还写道,如果发生在 2005 年之前或 2030 年之后,他会感到惊讶。所以要小心。
心灵上传 我们前面提到的雷·库兹韦尔预测,到 2045 年我们将经历历史上最伟大的技术奇点,机器智能超越人类智能。人类可以通过将他们的思想上传到电脑上来实现数字永生。像 Vinge 一样,Kurzweil 相信我们将通过创造一个人工超级智能(ASI)来达到奇点。那个级别的人工智能可以构想出过去人类从未想过的想法。
根据 Kurzwill 对模拟人脑所需的计算量的保守估计,我们将能够将我们的智力范围扩大十亿倍。
还记得这一切都是从两个晶体管开始的吗?支持这一概念的定律之一是“摩尔定律”,它说,*均而言,计算能力,或者更准确地说,集成电路上的晶体管数量,大约每两年翻一番。这意味着功能越来越强的个人电脑,价格越来越低。库兹韦尔用一系列图表说明了这一点,这些图表显示了各种技术不可阻挡的向上攀升。“我们将变得越来越非生物化,以至于非生物部分占主导地位,生物部分不再重要”,Kurzwill 解释道。
数字永生,我们已经说过了,对吗?
但是,正如我们一开始提到的,上传思想只是这个过程的第一步,一旦我们有了大脑,让我们让化身活着,这样我们就可以与它交流。有生以来第一次,我们将能够和自己以外的人交谈。让我给你介绍这位美丽的女士:“比娜-48”:
https://www.youtube.com/embed/KYshJRYCArE
“比娜-48”是马丁·罗斯布拉特的机器人复制品,她以自己的妻子为模板。罗斯布拉特是一名律师、作家、企业家和生物技术公司“联合治疗公司”的首席执行官。她引入了“思维克隆”的概念——可以永生的人类的数字版本。她描述了思维克隆是如何从“思维文件”中产生的,思维文件是一种我们个性的在线存储库。这个思维文件将在“思维软件”上运行,这是一种意识软件。
Richard Grandmorin 用以下等式总结了数字永生的概念:“语义分析+社交互联网使用+人工智能=永生”。YOLO 现在有了全新的含义。
李小龙说:“永生的关键是首先过一种值得记住的生活”。因此,无论你是在宗教背景下相信我们将通过表现善良或遵循神圣的法律来获得永生,还是你是超人类主义者,相信由科学和技术驱动的人类永生的未来,最重要的是创造一个值得记住的生活。他无疑经历了一场难忘的死亡。
原载于 2020 年 3 月 24 日https://iron woman . ai。
利用机器学习预测员工流失
对 14,249 名员工进行分类模型培训

图片来源:Shutterstock。
众所周知,招聘新员工比留住现有人才要昂贵得多。离开的员工会从你的组织中带走宝贵的经验和知识。据《福布斯》报道,一个初级职位的周转成本估计为该员工工资的 50%。对中层员工来说,估计是工资的 125%,对高层管理人员来说,是工资的 200%。
我们将在一个 Jupyter 笔记本中训练一些机器学习模型,使用关于员工的 职位快乐绩效工作量 和 任期 的数据来预测他们是会留下还是离开。
我们的目标变量是分类的,因此 ML 任务是分类。(对于数值目标,任务变成回归。)
我们将使用来自 elitedatascience.com 的数据集,模拟一家拥有 14249 名过去和现在员工的大公司。有 10 列。

原始数据集的快照。
这些步骤是:
- EDA &数据处理:探索、可视化和清理数据。
- 特性工程:利用领域专业知识,创造新特性。
- 模型训练:我们将训练和调整一些屡试不爽的分类算法,比如逻辑回归、随机森林和梯度推进树。
- 性能评估:我们将看看包括 F1 和 AUROC 在内的一系列分数。
- 部署:批量运行还是弄几个数据工程师/ ML 工程师来建一个自动化流水线?
理想情况下,该公司将对其目前的永久员工运行该模型,以识别那些处于风险中的员工。这是机器学习提供 可操作 商业洞察的一个例子。
在此加入 Medium 并获得无限制访问互联网上最好的数据科学文章。
1.数据探索和处理
探索性数据分析(EDA) 帮助我们理解数据,为数据清洗和特征工程提供思路和见解。数据清理为我们的算法准备数据,而特征工程是真正帮助我们的算法从数据集中提取潜在模式的神奇调味汁。请记住:
更好的数据总是胜过更好的算法!
我们首先将一些标准的数据科学 Python 包加载到 JupyterLab 中。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sbfrom sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier,
GradientBoostingClassifierfrom sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import GridSearchCVfrom sklearn.metrics import confusion_matrix, accuracy_score,
f1_score, roc_curve, roc_auc_scoreimport pickle
导入数据集:
df = pd.read_csv('employee_data.csv')
这是我们数据帧的快照。形状为(14,249,10)。

原始数据集的快照。
目标变量是状态。该分类变量采用值使用或离开。
共有 25 个栏目/功能:
- 部门
- 工资
- 满意,提起 _ 投诉 —代理 幸福
- 上次 _ 评估,最* _ 提升—绩效代理
- avg_monthly_hrs,n _ projects—工作量的代理
- 任期 —代理 经验
1.1 数字特征
让我们绘制一些快速直方图来了解我们的数字特征的分布。
df.hist(figsize=(10,10), xrot=-45)

对我们的数字特征要做的事情,以确保数据与我们的算法配合良好:
- 将投诉和最*提升中的 NaN 转换为 0。它们的标签贴错了。
- 在将 NaN 转换为零之前,为 last_evaluation 特征中的缺失数据创建一个指示变量。
df.filed_complaint.fillna(0, inplace=True)
df.recently_promoted.fillna(0, inplace=True)df['last_evaluation_missing'] =
df.last_evaluation.isnull().astype(int)
df.last_evaluation.fillna(0, inplace=True)
这是我们数字特征的关联热图。
sb.heatmap(df.corr(),
annot=True,
cmap=’RdBu_r’,
vmin=-1,
vmax=1)

1.2 分类特征
让我们为我们的分类特征绘制一些快速柱状图。Seaborn 在这方面很棒。
for feature in df.dtypes[df.dtypes=='object'].index:
sb.countplot(data=df, y='{}'.format(features))



最大的部门是销售部。只有一小部分员工处于高低工资的阶层。我们的数据集是不*衡,因为只有少数员工离开了公司,也就是说,只有一小部分员工的状态= 离开了。这对我们选择用来评估算法性能的指标有影响。我们将在结果中详细讨论这一点。
从数据清理的角度来看,部门特性的 IT 和 information_technology 类应该合并在一起:
df.department.replace('information_technology', 'IT', inplace=True)
而且 HR 只关心永久员工,所以要过滤掉临时部门:
df = df[df.department != 'temp']
因此,我们的部门功能看起来应该更像这样:

对我们的分类特征要做的事情,以确保数据与我们的算法配合良好:
- 部门特征的缺失数据应该被集中到它自己的缺失类中。
- 部门和薪资分类特征也应该是一键编码。
- 目标变量状态应转换为二进制。
df['department'].fillna('Missing', inplace=True)df = pd.get_dummies(df, columns=['department', 'salary'])df['status'] = pd.get_dummies(df.status).Left
1.3 细分
我们可以通过将数字特征与分类特征进行分割来获得进一步的见解。让我们从一些单变量分割开始。
具体来说,我们将通过我们的分类目标变量状态来细分代表快乐、绩效、工作量和体验的数字特征。
按状态划分的细分市场满意度:
sb.violinplot(y='status', x='satisfaction', data=df)

有一种观点认为,许多被解雇的员工对他们的工作非常满意。
细分市场最后 _ 评估状态:
sb.violinplot(y='status', x='last_evaluation', data=df)

一种观点认为,大量被解雇的员工都是高绩效员工。也许他们觉得留下来没有进一步发展的机会?
按状态细分*均每月小时数和项目数:
sb.violinplot(y='status', x='avg_monthly_hrs', data=df)
sb.violinplot(y='status', x='n_projects', data=df)


似乎那些翻腾的人要么工作量相当大,要么工作量相当低。这些代表了精疲力竭、无所事事的前员工吗?
按状态划分的细分任期:
sb.violinplot(y='status', x='tenure', data=df)

我们注意到员工在第三年突然流失。那些 6 年后还在的人倾向于留下来。
2.特征工程
查看下面的双变量分割,这将推动我们以后的特征工程。
对于每个情节,我们将按照状态来分割两个数字特征(代表快乐、表现、工作量或经历)。这可能会给我们一些基于员工刻板印象的聚类。
表现和快乐:

哎呦,雇佣的工人让这个图很难看。让我们只显示剩下的工人,因为他们才是我们真正想要了解的。**
sb.lmplot(x='satisfaction',
y='last_evaluation',
data=df[df.status=='Left'],
fit_reg=False
)

我们有三组不安分的员工:
- 表现不佳者:最后 _ 评价< 0.6
- 不开心:满意度 _ 等级< 0.2
- 超额完成者:上次评估> 0.8,满意度> 0.7
工作负载和性能:
sb.lmplot(x='last_evaluation',
y='avg_monthly_hrs',
data=df[df.status=='Left'],
fit_reg=False
)

我们有两群焦躁不安的员工:
- 星级 : avg_monthly_hrs > 215,last_evaluation > 0.75
- 懒鬼 : avg_monthly_hrs < 165,last_evaluation < 0.65
工作量和幸福感:
sb.lmplot(x='satisfaction',
y='avg_monthly_hrs',
data=df[df.status=='Left'],
fit_reg=False,
)

我们有三组不安分的员工:
- 工作狂:*均每月工时> 210,满意度> 0.7
- 刚刚参加工作:*均每月工作时间< 170
- 过度劳累:*均每月小时数> 225,满意度< 0.2
让我们为这 8 个“典型”员工群体设计新功能:
df['underperformer'] = ((df.last_evaluation < 0.6) & (df.last_evaluation_missing==0)).astype(int)
df['unhappy'] = (df.satisfaction < 0.2).astype(int)
df['overachiever'] = ((df.last_evaluation > 0.8) & (df.satisfaction > 0.7)).astype(int)df['stars'] = ((df.avg_monthly_hrs > 215) & (df.last_evaluation > 0.75)).astype(int)
df['slackers'] = ((df.avg_monthly_hrs < 165) & (df.last_evaluation < 0.65) & (df.last_evaluation_missing==0)).astype(int)df['workaholic'] = ((df.avg_monthly_hrs > 210) & (df.satisfaction > 0.7)).astype(int)
df['justajob'] = (df.avg_monthly_hrs < 170).astype(int)
df['overworked'] = ((df.avg_monthly_hrs > 225) & (df.satisfaction < 0.2)).astype(int)
我们可以看一下这 8 个组中每个组的员工比例。
df[['underperformer', 'unhappy', 'overachiever', 'stars',
'slackers', 'workaholic', 'justajob', 'overworked']].mean()underperformer 0.285257
unhappy 0.092195
overachiever 0.177069
stars 0.241825
slackers 0.167686
workaholic 0.226685
justajob 0.339281
overworked 0.071581
34%的员工是刚参加工作的员工——没有灵感,只是来领周薪的——而只有 7%的人是超负荷工作的。
分析基表:应用所有这些数据清洗步骤和特征工程后的数据集就是我们的分析基表。这是我们训练模型的数据。
我们的 ABT 有 14,068 名员工和 31 个栏目——请看下面的片段。回想一下,我们的原始数据集有 14,249 名员工,只有 10 列!

3.系统模型化
我们将训练四个屡试不爽的分类模型:
- 逻辑回归 (L1 和 L2——正规化)
- 随机森林
- 梯度增强树
首先,让我们拆分我们的分析基表。
y = df.status
X = df.drop('status', axis=1)
然后我们将分成训练集和测试集。我们的数据集有点不*衡,所以我们将使用分层抽样进行补偿。
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1234, stratify=df.status)
我们将设置一个管道对象来训练。这将简化我们的模型训练过程。
pipelines = {
'l1': make_pipeline(StandardScaler(),
LogisticRegression(penalty='l1', random_state=123)),
'l2': make_pipeline(StandardScaler(),
LogisticRegression(penalty='l2', random_state=123)),
'rf': make_pipeline(
RandomForestClassifier(random_state=123)),
'gb': make_pipeline(
GradientBoostingClassifier(random_state=123))
}
我们还想调整每个算法的超参数。对于逻辑回归,最有影响的超参数是正则化的强度, C 。
l1_hyperparameters = {'logisticregression__C' : [0.001, 0.005, 0.01,
0.05, 0.1, 0.5, 1, 5, 10, 50, 100, 500, 1000]
}l2_hyperparameters = {'logisticregression__C' :
[0.001, 0.005, 0.01, 0.05, 0.1, 0.5,
1, 5, 10, 50, 100, 500, 1000]
}
对于我们的随机森林,我们将调整估计器的数量( n_estimators )、分割期间要考虑的最大特征数量( max_features )以及作为叶子的最小样本数量( min_samples_leaf )。
rf_hyperparameters = {
'randomforestclassifier__n_estimators' : [100, 200],
'randomforestclassifier__max_features' : ['auto', 'sqrt', 0.33],
'randomforestclassifier__min_samples_leaf' : [1, 3, 5, 10]
}
对于我们的梯度增强树,我们将调整估计器的数量(n _ 估计器),学习速率,以及每棵树的最大深度( max_depth )。
gb_hyperparameters = {
'gradientboostingclassifier__n_estimators' : [100, 200],
'gradientboostingclassifier__learning_rate' : [0.05, 0.1, 0.2],
'gradientboostingclassifier__max_depth' : [1, 3, 5]
}
我们将把这些超参数保存在字典中。
hyperparameters = {
'l1' : l1_hyperparameters,
'l2' : l2_hyperparameters,
'rf' : rf_hyperparameters,
'gb' : gb_hyperparameters
}
最后,我们将拟合和调整我们的模型。使用 GridSearchCV 我们可以训练所有这些模型,只需几行代码就可以对我们声明的所有超参数进行交叉验证!
fitted_models = {}
for name, pipeline in pipelines.items():
model = GridSearchCV(pipeline,
hyperparameters[name],
cv=10,
n_jobs=-1)
model.fit(X_train, y_train)
fitted_models[name] = model
4.估价
我写了一篇关于流行的机器学习指标的专门文章,包括下面使用的那些。
4.1 绩效得分
我们将从打印交叉验证分数开始。这是 10 个保留折叠的*均性能,是仅使用您的训练数据获得模型性能的可靠估计的一种方式。
for name, model in fitted_models.items():
print(name, model.best_score_)**Output:
l1 0.9088324151412831
l2 0.9088324151412831
rf 0.9793851075173272
gb 0.975475386529234**
转到测试数据,我们将:
- 计算精度;
- 打印混淆矩阵并计算精度,调出和F1-得分;
- 显示 ROC 并计算 AUROC 分数。
准确性衡量正确标记的预测的比例,但它不适用于不*衡的数据集,例如垃圾邮件过滤(垃圾邮件与非垃圾邮件)和医疗测试(生病与未生病)。例如,如果我们的数据集只有 1%的员工满足目标 = 离开,那么一个总是预测该员工仍在该公司工作的模型将立即获得 99%的准确率。在这些情况下,精确或召回更为合适。你经常使用哪一个取决于你是想最小化类型 1 错误(假阳性)还是类型 2 错误(假阴性)。对于垃圾邮件,类型 1 错误更糟糕(一些垃圾邮件是可以的,只要你没有意外过滤掉一封重要的邮件!)而第二类错误对于医学测试来说是不可接受的(告诉别人他们没有患癌症是一场灾难!). F1 分数通过对精确度和召回率进行加权*均,让你两全其美。
ROC 下的面积,被称为 AUROC 是分类问题的另一个标准度量。这是对分类器区分类别和从噪声中分离信号的能力的有效测量。这种度量对于不*衡数据集也是稳健的。
下面是生成这些分数和图表的代码:
for name, model in fitted_models.items():
print('Results for:', name)
# obtain predictions
pred = fitted_models[name].predict(X_test) # confusion matrix
cm = confusion_matrix(y_test, pred)
print(cm) # accuracy score
print('Accuracy:', accuracy_score(y_test, pred))
# precision
precision = cm[1][1]/(cm[0][1]+cm[1][1])
print('Precision:', precision)
# recall
recall = cm[1][1]/(cm[1][0]+cm[1][1])
print('Recall:', recall)
# F1_score
print('F1:', f1_score(y_test, pred))
# obtain prediction probabilities
pred = fitted_models[name].predict_proba(X_test)
pred = [p[1] for p in pred] # plot ROC
fpr, tpr, thresholds = roc_curve(y_test, pred)
plt.title('Receiver Operating Characteristic (ROC)')
plt.plot(fpr, tpr, label=name)
plt.legend(loc='lower right')
plt.plot([0,1],[0,1],'k--')
plt.xlim([-0.1,1.1])
plt.ylim([-0.1,1.1])
plt.ylabel('True Positive Rate (TPR) i.e. Recall')
plt.xlabel('False Positive Rate (FPR)')
plt.show()
# AUROC score
print('AUROC:', roc_auc_score(y_test, pred))
逻辑回归(L1 正则化):
**Output:
[[2015 126]
[ 111 562]]
Accuracy: 0.9157782515991472
Precision: 0.8168604651162791
Recall: 0.8350668647845468
F1: 0.8258633357825129
AUROC: 0.9423905869485105**

逻辑回归(L2 正则化):
**Output:
[[2014 127]
[ 110 563]]
Accuracy: 0.9157782515991472
Precision: 0.8159420289855073
Recall: 0.836552748885587
F1: 0.8261188554658841
AUROC: 0.9423246556128734**

渐变增强树:
**Output:
[[2120 21]
[ 48 625]]
Accuracy: 0.9754797441364605
Precision: 0.9674922600619195
Recall: 0.9286775631500743
F1: 0.9476876421531464
AUROC: 0.9883547910913578**

随机森林:
**Output:
[[2129 12]
[ 45 628]]****Accuracy: 0.9797441364605544
Precision: 0.98125
Recall: 0.9331352154531947
F1: 0.9565879664889566
AUROC: 0.9916117990718256**

获胜的算法是 AUROC 为 99%、F1 得分为 96%的随机森林。这种算法有 99%的几率区分出左和受雇的工人……相当不错!
在测试集中的 2814 名员工中,算法:
- 正确分类 628 个左工人(真阳性,同时得到 12 个错误(I 型错误,以及
- 正确分类的 2129 个雇佣的工人(真否定),同时得到 45 个错误(第二类错误)。
仅供参考,这里是获胜随机森林的超参数,使用 GridSearchCV 进行了调整。
RandomForestClassifier(bootstrap=True,
class_weight=None,
criterion='gini',
max_depth=None,
max_features=0.33,
max_leaf_nodes=None,
min_impurity_decrease=0.0,
min_impurity_split=None,
min_samples_leaf=1,
min_samples_split=2,
min_weight_fraction_leaf=0,
n_estimators=200,
n_jobs=None,
oob_score=False,
random_state=123,
verbose=0,
warm_start=False
)
4.2 特性重要性
考虑下面的代码。
coef = winning_model.feature_importances_
ind = np.argsort(-coef)for i in range(X_train.shape[1]):
print("%d. %s (%f)" % (i + 1, X.columns[ind[i]], coef[ind[i]]))x = range(X_train.shape[1])
y = coef[ind][:X_train.shape[1]]plt.title("Feature importances")
ax = plt.subplot()
plt.barh(x, y, color='red')
ax.set_yticks(x)
ax.set_yticklabels(X.columns[ind])
plt.gca().invert_yaxis()
这将打印按重要性排序的特性列表和相应的条形图。
**Ranking of feature importance:
1\. n_projects (0.201004)
2\. satisfaction (0.178810)
3\. tenure (0.169454)
4\. avg_monthly_hrs (0.091827)
5\. stars (0.074373)
6\. overworked (0.068334)
7\. last_evaluation (0.063630)
8\. slackers (0.028261)
9\. overachiever (0.027244)
10\. workaholic (0.018925)
11\. justajob (0.016831)
12\. unhappy (0.016486)
13\. underperformer (0.006015)
14\. last_evaluation_missing (0.005084)
15\. salary_low (0.004372)
16\. filed_complaint (0.004254)
17\. salary_high (0.003596)
18\. department_engineering (0.003429)
19\. department_sales (0.003158)
20\. salary_medium (0.003122)
21\. department_support (0.002655)
22\. department_IT (0.001628)
23\. department_finance (0.001389)
24\. department_management (0.001239)
25\. department_Missing (0.001168)
26\. department_marketing (0.001011)
27\. recently_promoted (0.000983)
28\. department_product (0.000851)
29\. department_admin (0.000568)
30\. department_procurement (0.000296)**

员工流失有三个特别强的预测因素:
- n_projects (工作量)
- 满意度(幸福感)和
- 任期(经验)。
此外,这两个工程特性在特性重要性上排名也很高:
- 明星(高幸福&工作量),以及
- 劳累过度(低幸福感&高工作量)。
有趣,但并不完全令人惊讶。明星们可能为了更好的机会而离开,而过度劳累的人则在筋疲力尽后离开。
5.部署

图片由 ThisisEngineering RAEng 提供。
此模型的可执行版本(。pkl)可以从 Jupyter 笔记本中保存。
with open('final_model.pkl', 'wb') as f:
pickle.dump(fitted_models['rf'].best_estimator_, f)
人力资源可以在将新员工数据输入训练模型之前对其进行预处理。这被称为批量运行。
在大型组织中,他们可能希望通过与数据工程师和机器学习工程师合作,将模型部署到生产环境中。这些专家围绕我们的模型建立了一个自动化的管道,确保新的数据能够得到预处理,并定期向人力资源部门报告预测。
最终意见
我们从一个业务问题开始:一家大公司的人力资源想要对他们的员工流失有可操作的见解。
我们在包含超过 14,000 名过去和现在员工的大量历史数据上训练了一个获胜的随机森林模型。
人力资源部可以在我们训练有素的员工身上运行新数据。pkl 文件,或者可以由他们的工程部门构建自动管道。
我们的模型是二元分类模型,其中目标变量是分类的。它预测了一系列不连续的可能性——这里是流失或不流失。
监督学习硬币的另一面是回归模型,其目标变量是数值。在这里,我训练了一个预测房价的。
最后,我在这里写了一篇关于机器学习在数学建模领域的位置的文章。
无限制媒体访问
提升你的知识和技能到一个新的水*。
加入 Medium 享受无限制访问互联网上的最佳分析&数据科学文章。你可以在这里加入来支持我和其他顶级作家。
我的数据科学文章
- 微分方程与机器学习——这里这里
- 新冠肺炎的数学建模与机器学习— 此处
- 用回归预测房价— 此处
- 分类预测员工流失— 此处
- 流行的机器学习性能指标— 此处
- Jupyter 笔记本与 Dataiku DSS — 此处
- Power BI —从数据建模到令人惊叹的报告— 此处
基于深度学习的风能交易——时间序列预测
用人工智能预测风能发电量
本文由 罗萨娜·德奥利维拉·戈麦斯 代表 Deep Delve 团队为 AI4Impact 深度学习 Datathon 2020 。

来源: Unsplash 。
问题简介
如今,世界比以往任何时候都更需要合作,以实现应对气候危机的清洁能源解决方案。自 2015 年以来,联合国所有会员国通过的可持续发展目标旨在确保到 2030 年所有人都能获得负担得起、可靠、可持续的现代清洁能源。清洁能源来自大自然提供的可再生资源,如太阳、风、潮汐和海浪、地热等。其用途从大规模发电和离网发电(农村和偏远地区)[1]到加热/冷却系统和运输。然而,太阳能和风能等可再生能源依赖于天气,比传统能源更不稳定。随着世界上许多国家可再生能源供应份额的增加[2,3],保证这些清洁能源在替代化石燃料能源的同时提供稳定的供应是非常重要的。
在 AI4Impact 深度学习 Datathon 2020 中,利用深度神经网络研究了清洁能源的话题。在挑战的第一部分,团队使用 Kaggle 数据集预测能源需求,该数据集包含来自 PJM 互联有限责任公司的超过 10 年的每小时能源消耗数据。你可以在下面的视频中查看 team Deep Delve 对该项目的第一部分的一些见解。
挑战的第二部分致力于预测风能以最大化交易利润。与太阳能一起,风能是最重要的可再生能源之一,2018 年提供了全球电力供应的 4.8%[4,5],2019 年占欧洲电力消耗的 15%[6]。风能是由风力涡轮机产生的机械能产生的。因为风的强度随着时间的推移而变化,并且可能间歇性地停止吹动,所以由这种来源产生的电力通常与其他电源结合,以提高可靠性和稳定性。
通过考虑不同类型的能源公司之间的交易,可以理解风能的经济性。地区或国家能源公司从能源生产商那里购买预定数量的能源(以千瓦时计量),这些能源生产商是经营风力农场的公司(就风能而言)。由于人们期望从电网获得稳定的能源供应,在停电的情况下,能源生产商可能会被政府处以巨额罚款。
能源贸易公司通过帮助预测预期的能源产量(特别是在风力作为不稳定能源的情况下),在评估能源交易中短缺的风险方面发挥重要作用。能源交易商代表能源生产商预测能源产量(在我们的例子中是风能),考虑两种情况:
- 如果短缺低于预测值,则在现货市场上购买能源供应给电网(价格高于*均能源价格)
- 在超出预测产量的情况下,能源生产商不会因为额外的能源而得到补偿。
从这个意义上说,对发电量的精确预测对风电场(即风能生产商)的财务表现起着至关重要的作用。
问题陈述
风能高度依赖于风速等环境因素。对于能源贸易商来说,成功预测风能产量以实现利润最大化至关重要。通过将深度学习应用于金融风险,我们的目标是为法兰西岛地区制作一个领先时间为 18 小时、分辨率为 1 小时的风能预测模型。目标是实现一个模型,优化风电场的利润,最小化能源生产的过剩短缺。
方法学
为了评估我们的风能模型表现如何,我们估计了与实际产生的能量相比,该模型将实现多少货币利润。在交易周期之后的评估期内,使用真实数据对模型进行测试:
- 热身:前 18 个小时不进行交易(从 2020 年 7 月 22 日 00:00 UTC 开始)。
- 交易时段:每小时生成一次未来 18 小时(T+18)的风能预报,包括周末、公共节假日,24/7(2020 年 7 月 22 日 18:00 UTC 开始)。
- 交易在评估期结束时结束(2020 年 7 月 28 日 23:00 UTC)。
财务绩效分析遵循之前讨论的交易方法,具体如下:
- 能源价格(千瓦时): 10 欧分。
- 每天销售的最大电能(kWh):当天的最大预测。
- 能源生产过剩:能源生产商不会因产生额外的能源而得到补偿。
- 能源短缺:从现货市场购买能源(20 欧分/千瓦时)。可购买的能源数量取决于手头的现金,并且只有在有正结余时才有可能。
- 初始现金储备: 10,000,000 欧分,用于购买能源差额短缺情况。该金额将在评估结束时返回。
- 债务(累计):如果手头可用现金少于购买所需,则处以每千瓦时 100 欧分的罚款。这被记录为负值,并添加到手头现金。
数据集
该项目的目标是预测巴黎周围的法兰西岛地区的总风能产量。风能生产的数据来自法国能源传输管理局电力运输网络 (RTE)。数据集energy-ile-de-france 包含来自 RTE 在线数据库的*实时风力产量,遵循以下规范:
- 从 2017 年 1 月 1 日 00:00 UTC 开始到现在,以一个 1 小时的时间戳表示的发电量(kWh)。
- 该地区 8 个主要风电场的风力预测数据(见下表),来自 Terra Weather 提供的 2 个不同的风力模型(16 个预测)。
- 因变量:风速(米/秒)和风向(北纬度——例如,45 度的风向意味着风从东北方向吹)。预测每 6 小时更新一次,并以 1 小时为基数进行插值。风速和风向的所有值都是根据模型估算的。

表 1:项目中使用的法兰西岛地区 8 个主要风力发电场的信息。
探索性数据分析
在进入深度学习模型之前,我们需要首先从数据中提取见解,并进行数据准备工作。数据提取和建模由 Terra AI 在整个比赛中使用的 Autocaffe *台内用 Smojo 编程语言实现。
统计和标准化
我们从获取数据集中风能的描述性统计数据开始分析,如表 2 所示。我们强调数据是直接从 Autocaffe 中提取的,并且已经进行了插值,因此不包含任何缺失值。
下一步是对数据进行归一化,以使所有特征具有相似的取值范围。这一步对于避免网络偏向具有更高值的特征以及加快学习过程非常重要。所有特征标准化为零*均值和单位标准偏差,遵循以下方程:

其中,Xnorm 是归一化输入要素的值,Xmean 是 X 的*均值,stddev 是标准偏差。
在处理预测时,定义一个可以衡量模型预测与获得的真实值(实际值)相比有多好的度量标准是很重要的。处理时间序列时的另一个重要概念是持续性,基本上是假设一个量在现在的观测值在未来(T+X = T+0)将是相同的。持久性是一个微不足道的预测模型,因此,任何可信的时间序列模型必须至少击败持久性值。
假设一个*均绝对误差(MAE)度量,我们为我们的数据集确定了 0.65 的持久性,这是我们的模型必须克服的第一个基准。

表 2:风能的描述性统计。
风能时间序列
我们使用图 1 和图 2 的箱线图,从多年来风能产量的可视化开始我们的时间序列分析。在该可视化中,一个框对应于每年数据的 50%,每个框内的水*线对应于中值,触须(距框的范围)对应于最大值和最小值(不包括异常值),最后圆圈对应于异常值(1.5 倍框=距框的距离)。

图 1:2017 年和 2019 年法兰西岛风能产量(标准化)的箱线图。
从图 1 中,可以看出多年来风能产量的增加。这种普遍增长可能与法国政府对其整体风能生产的更多投资有关。这可能是通过风电场容量的增加或风电场自身数量的增加而发生的,如表 1 所示,2019 年安装了 Angerville 1 和 2 号风电场[7,8]。
如果我们随着时间的推移提高我们分析的分辨率,并将重点放在 2019 年,例如,我们可以从图 2 中看到,在 3 月和 9 月至 12 月期间,能源产量的范围更广。同样,从 4 月到 8 月,能源产量也有所下降,这表明可能存在季节相关性。

图 2:2019 年几个月法兰西岛风能产量(标准化)的箱线图。
箱线图有助于理解分布情况。现在查看图 3 中 2018 年几个月的类似能源分析,我们可以更好地可视化风能生产的年度行为。为了更容易理解,我们用橙色圆点突出显示了每日*均值。从橙色曲线可以看出,一月是风能产量最高的月份,八月是风能产量最低的月份。随着时间的推移,在其他年份也发现了类似的行为。该信息稍后用于为我们的模型优化生成新特征。

图 3:月*均能量重采样

图 Guitrancourt(纵轴)一月份的风速(横轴)。
风速和风向
在确定了一些关于风能生产随时间变化的见解后,我们现在可以研究我们的数据集中可用的特征。
我们有法国岛 8 个不同风电场的风速和风向,总共有 16 个初始特征。

图 5:一月份 Guitrancourt(纵轴)的风向(横轴)。
图 5 和图 6 分别显示了 2019 年 1 月 Guitrancourt 风电场的风速和风向。从这两个特征的时间序列行为中似乎看不到清晰的见解。
在这项分析中,我们选择了 2019 年,因为这一年所有的风力发电场都可用。在调查我们的数据集中的其他 7 个风电场时,发现了类似的行为。
图 6 显示了 2019 年 1 月同期的发电量在图 3 中的放大。查看我们数据中可用的区域和整个时间段,我们现在可以将发电量峰值和谷值与风速和风向行为进行比较(图 4 和图 5)。尽管在发电量和风向之间任何关联似乎都是清楚的,但是随着时间的推移,我们看到了发电量和风速的类似模式。

图 6:2019 年 1 月(横轴)各天的风能产量(纵轴)。
为了进一步研究这种相似性,图 7 显示了我们的数据中所有区域和整个时间内所有风速和风向特征之间的皮尔逊相关图。在该图中,较高的值(红色)表示较强的相关性,而较低的值(白色)表示较弱的相关性。结果表明,风向和能量之间没有直接关系,但与所有风电场的速度有明显和强烈的相关性。可以识别相同农场的风速和风向之间的较弱相关性,而不需要显著的相关性分数。

图 7:初始特征之间的皮尔逊相关性。
最后,我们可以看看图 8 中的风特征分布。显示了 4 个风电场的风速直方图(左)和风向直方图(右)(所有风电场呈现相似的曲线)。我们可以看到,特征的分布不遵循高斯分布,因为风速的峰值向左移动,并且风向具有两个峰值。这表明使用*均绝对误差指标更适合模型评估,这将在下节课中讨论。

图 8:2017 年和 2019 年法国 4 个风电场的风速和风向分布。
深度学习模型
这个项目的目标是为法兰西岛地区进行风能预测。时间序列预测是用神经网络和深度学习解决的常见问题。您可以点击查看 tome 系列车型的概述,点击查看预测主题。
我们从基本的神经网络模型开始建模任务,后来通过添加不同的特征和参数来增加其复杂性。
特征工程
考虑到数据已经标准化并为训练和测试而设置,我们继续进行特征工程阶段,在该阶段,我们研究了对原始特征的可能修改或组合,以提高模型的性能。我们不再将特定风电场的单一风向作为一个特征,而是寻找与确定发电量相关的更具洞察力的特征。
根据 EDA 会话中的讨论,最佳性能模型中使用的最终功能集如下:
- 空间*均特征:风速(所有风电场坐标)、方向(所有风电场坐标)、风速矢量和风向矢量。
- 时间特征:UTC 早上 6 点开始的小时数,1 月开始的月数,4 月开始的月数,季节。
- 加权标称特性:当前风速除以标称速度 12m/s,再乘以标称功率输出(见表 1),得出所有风电场位置和所有矢量的*均值。
差分模型
在处理时间序列时使用差分模型对于减少数据中的噪声而不改变最大值和最小值是极其重要的。此外,差异有助于捕捉更多关于变量随时间变化的行为的见解。为此,我们对以下时间窗口的变量进行了*均:
- 过去 24,48,72 小时,
- 上周,过去两周(14 天),
- 差异 18 小时(交易提前期):t36、t18、t0
最后,我们还通过引入 动量 和力考察了高阶时间贡献对发电量预测的影响。动量(m)和力(f)有助于提供更多关于时间速率随时间变化的信息。对于时间相关变量 s ,独立变量 x, a 时间 t 和时间步长 h ,动量和力定义如下:
s(t) = x(t) +x(t-h),
m(t) = s(t) -s(t-h),
f(t) = s(t)-2s(t-h) +s(t-2h)。
动量和力是使模型预测和实际值之间的滞后最小化的重要量。鉴于其他特征来自风力模型,我们仅针对风能生产进行了分析。
神经网络
在对数据进行分析和预处理后,我们在最后一次会议中为我们的模型确定了有见地的新特性。现在我们终于可以建立我们的神经网络模型了。首先,考虑到数据的非高斯行为,我们已经从数据行为中发现 MAE 度量更合适。我们还确定了几个新的特征作为神经网络的输入。
在处理输入特征时,如果某些输入特征与问题不相关,则总是存在向网络添加噪声或使学习过程变慢的风险。为此,我们使用 输入缩放 以便只选择相关特征来训练我们的神经网络。
输入缩放是减少噪声以及神经网络中异常值影响的一种强有力的方法。该方法包括将输入特征乘以(正)比例因子,让网络学习哪些是相关的,作为模型训练的一部分。缩放输入后,输入可进一步被箝位,其值为 tanh 。
另一种重要的降维方法是一种称为 自动编码器 的无监督学习技术。该方法由一个子网组成,该子网对瓶颈网络中的输入特征进行压缩和解压缩,从而识别输入特征之间可能的相关性。更多话题见这里。
为了找到我们的预测模型的最佳执行配置,在几个参数下研究了上述所有方法。类似地,我们测试了几种神经网络架构,尝试了不同的层数、层大小、激活函数、优化器和退出正则化概率。
最佳性能配置的得分为 0.48 ,比 MAE 指标的持久性高出 26 %。下面的表 3 总结了所使用的具体参数和架构(表中没有提到输出层),我们将在下一节课中讨论。

表 3:最佳性能模型。
结果
图 9 和图 10 分别显示了自动编码器和主神经网络的训练和测试损失。结果表明,尽管模型能够战胜持久性,但与训练相比,测试损失仍然很高,这表明网络面临学习挑战。正如在整个项目中所了解到的,高测试损耗可能来自过大的网络和/或数据中过多的噪声。让我们继续思考这个问题。

图 9:作为 autoencoder 迭代函数的训练和测试损失(纵轴)。

图 10:作为模型迭代函数的训练和测试损失(纵轴)。
从损失结果中,人们可以预期该模型的性能不会特别好。然而,如果我们现在查看图 11 中滞后相关性的结果,我们发现了非常好的结果。这里,重要的是回来理解项目的目标:从能源生产预测中获取最大利润。正如本文开头的交易方法中所述,每当模型预测不足时,交易者就会亏损。从这个角度来看,获利最多的模型是能够更好地预测时间序列中的波峰和波谷的模型,但不一定在整个时间序列中都具有高精度,这是有道理的。

训练和试验数据的滞后相关性。
从这个意义上来说,在这个优化分析中所做的是将滞后相关性优先于损失,以使利润最大化。根据采用时间序列预测的问题,逻辑可能不同,例如在离群值不重要的情况下。
最后,我们在图 12 中显示了实际值与训练值以及实际值与测试预测值。在两种情况下都可以看到清晰的线性关系,表明模型的预测良好,并且模型实际上是在学习而不是记忆训练数据。如上所述,模型被优化以最小化滞后相关性,因此确定了更广泛的线性行为。这是我们模型定制方式的直接结果。


图 12:实际数据与训练(左)和测试(右)预测的对比。
摘要
在这个项目中,我们为法国的风能预测训练了一个神经网络模型。这个模型后来被应用于在几天的时间内最大化风能交易者的真实数据的利润。在评估期间,根据产生的利润对不同团队的模型进行了比较。
通过数据马拉松学到的重要知识包括:
- 领域知识对于识别风能预测的相关特征至关重要,
- 详细的数据分析可以帮助洞察特征工程和参数选择,
- 数据预处理对于模型的良好性能至关重要,
- 降低噪声是时间序列分析的一个关键组成部分。
由团队 Deep Delve 实现的模型是一个神经网络,它预测风能产量,并能够为能源交易商和生产商产生合理的利润。该模型可能的改进包括更多关于参数调整和神经网络架构的实验,以及更先进的深度学习技术,如递归神经网络——以在时间序列问题中表现良好而闻名。工程分析更深层次特征也可以导致风能预测新重要因素,这些因素在本项目中没有考虑,例如海拔高度,甚至气候变化影响。
使用深度学习来理解时间序列具有很高的范围和影响许多其他领域的潜力,例如识别随时间传播的疾病(医疗保健、经济、销售、股票市场等)。特别是在可再生能源领域,它还可以用于预测能源需求和消费。
请点击下面的链接查看我们的简短演示:)
我们要感谢 AI4Impact 组织了这次有见地的数据马拉松,并为所有参赛者提供了学习更多关于时间序列预测、神经网络和可再生能源的机会。
深究团队: 程诺仪张小溪罗萨娜·德奥利维拉·戈麦斯斯胡阿德·奥贡塔约 瓦莱丽·科·易慧**
更多参考
[1] 可再生能源技术。世界能源评估(2001 年)
[2] “可再生能源领先的 12 个国家”。点击能量
[3]《2018 年 6 月可再生能源发电量及发电量统计》。互联网存档方式回机
[4] 世界能源统计回顾。 bp
[5]《2018 年全球装机量》。全球风能委员会
[6] 丹麦风力发电新破纪录年。能源网
这就是欧洲为可持续的绿色未来铺*道路的方式。我们论坛
这些国家正在引领向可持续能源的转变。我们论坛
窗口函数和 SQL 的其他概念
带 PostgreSQL 的 Sql 教程
用 SQL 开发您解决问题的技能

由菲利克斯·杜布瓦-罗伯特在 Unsplash 上拍摄的照片
简介
在我的教程的最后一部分,我们将看到窗口函数的效用和 SQL 的其他概念,它们允许为我们的问题找到解决方案。
正如在前一部分中,我将使用为名为太空漫游的虚拟公司建造的 ERD。
我的教程的第二部分解释了这个图的结构,你可以在这里找到。
现在,我们可以从我的 SQL 教程的最后一部分开始。

作者图片
第一部分:窗口功能
假设您希望有一个表,允许将每部电影的销售价格与特定国家的*均价格进行比较。显然第一件要做的事是将表与区域、卖家、电影和订单连接起来。之后,我们可以使用我们的窗口功能。所以:
WITH sub AS (SELECT a.country, o.amt_euro
FROM area AS a
JOIN seller AS s
ON a.id = s.area_id
JOIN movies AS m
ON s.id = m.id_seller
JOIN orders AS o
ON m.id = o.id_movie)SELECT country, amt_euro,
avg(amt_euro::numeric) OVER (PARTITION BY country)
FROM sub;
出局:

作者图片
正如你在上面看到的,我没有对电影的价格进行求和,而是做了一个分组,我报告了每个价格在一个特定国家的*均价格。这样一来,我们可以即时看到一部电影的价格是超过还是低于我们要考虑的国家的*均价格。
第二部分:右侧、长度和 Strpos
假设我们需要知道邮件列表中每封邮件的域。在这种情况下,我们可以使用以下函数: Right,Length 和 Strpos 。
SELECT email,
RIGHT(email, LENGTH(email) — STRPOS(email,’.’)) AS domain
FROM mailing_list;
出局:

作者图片
正如你所观察到的,每封邮件我们都有自己的域名。事实上,右函数帮助我找到了我想要的值,相反,长度和 Strpos 之间的差值确定了我想要从右边拉出的字符数。 Length 函数显示每行电子邮件的长度, Strops 显示圆点前的电子邮件长度。
第三部分:串联
我们决定对销售者表进行反规范化,以便拥有一个包含名字和姓氏的唯一列。为此,我们可以使用一个简单的连接。
我们可以使用两种不同的技术。
第一个技巧:
SELECT first_name, last_name,
CONCAT(first_name,’ ‘,last_name) AS name
FROM seller;
第二个技巧:
SELECT first_name, last_name,
first_name||’ ‘||last_name AS name
FROM seller;
出局:

作者图片
结论
感谢我在我的教程的所有部分中向您介绍的概念,现在您已经对如何应用 SQL 有了一个大致的了解,并开始提高处理越来越复杂问题的技能。
在 ETL 过程中,数据以杂乱的形式出现在我们面前,数据工程师或数据分析师的目标是规范化数据,并创建一个架构,以满足使用这些数据的不同部门的需求。这是数据分析师或数据工程师面临的基本挑战之一。
此外,通过创建或使用一家公司的数据仓库架构,您将拥有一个企业不同流程的全景,换句话说,您的眼睛将无处不在。这是深入研究一家公司的绝佳机会。
我发出一份期刊简讯。如果你想加入,请通过此链接注册。
除了我的简讯之外,还可以在我的电报群 初学数据科学 中取得联系。
熊猫的窗口功能

运行总计、期初至今回报和其他有趣的东西
QL 有一个简洁的功能,叫做窗口功能。顺便说一句,如果你正在寻找一份数据分析师的工作,你肯定应该知道如何在 SQL 中使用这些。熊猫(带一点跑腿)让我们可以做同样的事情。让我们看看怎么做。
但是首先,什么是窗口函数?
窗口函数允许我们对给定行的数据和指定行数之外的另一行的数据执行操作— 这个“行数之外的值”被称为窗口。
例如,假设您有 10 天的股票价格:

Windows 操作系统
窗口函数允许我们在指定列的值之间执行计算。例如,我可能想要比较今天的股票价格和昨天的价格,然后我想要一个“1”的窗口向后看。窗口函数允许我们这样做。另一方面,如果我想将今天的价格与一年前的价格进行比较,那么我会想要一个“356”的窗口(假设周末在您的数据集中)。
窗口函数对于时间序列数据特别有用,在这种情况下,在数据的每个时间点,您只需要知道该点发生了什么(不允许使用水晶球)。好消息是熊猫有 windows 功能,而且非常好用。
使用熊猫执行窗口计算
假设我们想计算股票价格的每日变化。为此,我们需要将每天的价格除以前一天的价格,然后减去 1。我们以列表的形式获取数据:
stock_list = [100, 98, 95, 96, 99, 102, 103, 105, 105, 108]
列表在数学上不是很友好,所以我们可以把数据放在一个 numpy 数组中(我把它改造成一个 9 乘 1 的数组,这样更容易查看和显示):
**In:**
stock_array = np.array(stock_list)
(stock_array[1:]/stock_array[:-1] - 1).reshape(-1,1)**Out:** array([[-0.02 ],
[-0.03061224],
[ 0.01052632],
[ 0.03125 ],
[ 0.03030303],
[ 0.00980392],
[ 0.01941748],
[ 0\. ],
[ 0.02857143]])
太好了,我们拿到回报了。不需要数据帧,对吗?不完全是。Dataframes 比 numpy 数组更加通用(numpy 数组针对处理数值数据进行了优化)。熊猫的开发者创造了所有这些漂亮的窗口方法让我们的生活更容易——如果我们不利用它们,他们会很难过。
让我们把我们的股票价格放入一个数据框架中:
stock_df = pd.DataFrame(stock_list, columns=['price'])
我们可以使用 shift 方法获得前一天的价格。shift 方法非常类似于 SQL 的 lag 函数。“1”告诉它滞后一天,给出前一天的价格:
stock_df['prev_price'] = stock_df.shift(1)
现在 stock_df 看起来是这样的:
price prev_price
0 100 NaN
1 98 100.0
2 95 98.0
3 96 95.0
4 99 96.0
5 102 99.0
6 103 102.0
7 105 103.0
8 105 105.0
9 108 105.0
酷,现在我们只需要用 price 除以 prev_price,然后减去 1,就可以得到日收益率:
**In:**
stock_df['daily_return'] = stock_df['price']/stock_df['prev_price']-1
print(stock_df)**Out:
** price prev_price daily_return
0 100 NaN NaN
1 98 100.0 -0.020000
2 95 98.0 -0.030612
3 96 95.0 0.010526
4 99 96.0 0.031250
5 102 99.0 0.030303
6 103 102.0 0.009804
7 105 103.0 0.019417
8 105 105.0 0.000000
9 108 105.0 0.028571
现在是有趣的部分
我们能做的不仅仅是计算回报。例如,假设我们想要将我们的每日回报与一个扩展窗口的*均回报进行比较,以查看每个回报与历史*均回报的比较情况。您可能会想,为什么不计算每日 return_column 中所有值的*均值并使用它呢?答案是数据泄露。
在时间序列分析中,当我们试图预测未来时,我们需要非常小心在特定日期观察到了什么,观察不到什么。例如,在数据集的第 5 天,我们只能观察到前 5 个价格:100、98、95、96、99。因此,如果我们为了预测第 6 天而测试特性,我们不能将第 5 天的 3.03%的回报率与整个期间的*均每日变化进行比较,因为在第 5 天,我们还没有观察到第 6 天到第 9 天。
扩展和滚动窗口
这就是一个扩展窗口出现的原因。如果你不熟悉展开和滚动窗口,下面的图片形象地展示了它们是什么。使用扩展窗口,我们以扩展的方式计算指标——这意味着我们在计算中包括了到当前行为止的所有行。滚动窗口允许我们在滚动的基础上计算指标 —例如,滚动(3)意味着我们使用当前观察以及之前的两个观察来计算我们想要的指标。

扩展与滚动窗口
使用扩展窗口背后的基本原理是,随着每一天的过去,我们会得到另一个价格和另一个每日变化,我们可以将它们添加到均值计算中。这是我们应该在计算指标中捕捉的新信息。我们可以用下面的代码做到这一点(为了好玩,我还加入了一个 3 天的滚动窗口)。
stock_df['expand_mean']=stock_df['daily_return'].expanding().mean()
stock_df['roll_mean_3']=stock_df['daily_return'].rolling(3).mean()
呼唤。在 pandas 数据帧或系列上展开()创建 pandas 展开对象。这很像更广为人知的 groupby 对象(基于指定的列标签对事物进行分组)。扩展(或滚动)对象允许我们以扩展的方式计算各种指标。让我们看看我们的数据帧现在是什么样子:
price prev_price daily_return expand_mean roll_mean_3
0 100 NaN NaN NaN NaN
1 98 100.0 -0.020000 -0.020000 NaN
2 95 98.0 -0.030612 -0.025306 NaN
3 96 95.0 0.010526 -0.013362 -0.013362
4 99 96.0 0.031250 -0.002209 0.003721
5 102 99.0 0.030303 0.004293 0.024026
6 103 102.0 0.009804 0.005212 0.023786
7 105 103.0 0.019417 0.007241 0.019841
8 105 105.0 0.000000 0.006336 0.009740
9 108 105.0 0.028571 0.008807 0.015996
请注意,在第 1 天,expand_mean 和 daily_return 是相等的——这是必然的情况,因为我们计算的是第 1 天只有一个日收益的扩展*均值。此外,在第 3 天,当我们终于有足够的数据来计算我们的滚动 3 天*均值时,roll_mean_3 的第一个值等于 expand_mean。这也是有意义的——在第 3 天,我们的扩展均值也是使用最* 3 天的回报计算的。
这是我们计算的股票日收益和 2 均值的曲线图(和代码):
plt.subplots(figsize=(8,6))
plt.plot(stock_df['daily_return'], label='Daily Return')
plt.plot(stock_df['expand_mean'], label='Expanding Mean')
plt.plot(stock_df['roll_mean_3'], label = 'Rolling Mean')
plt.xlabel('Day')
plt.ylabel('Return')
plt.legend()
plt.show()

股票收益及其扩展均值和滚动三日均值
正天数的累计
除了均值之外,我们还可以应用其他函数。假设我们的老板过来说,“我想让你记录这只股票上涨了多少天。”
我们可以使用一个扩展对象和 sum 方法(用于保存一个运行总数)来实现。首先,我们需要在数据框架中添加一列来表示当天股票是否上涨。我们可以利用应用方法(将函数应用于数据帧或系列中的每一行)。我们可以定义一个函数来给 apply 或者使用 lambda 函数——我选择了一个 lambda 函数(更少的代码行),它接受每个返回,如果是正数则返回 1,如果是负数则返回 0。
stock_df['positive'] = stock_df['daily_return'].apply(lambda x: 1 if x>0 else 0)
一旦我们有了“正”列,我们可以对它应用一个扩展窗口和 sum 方法(因为每个正的一天用 1 表示,我们只需要保存 1 的总数):
stock_df['num_positive'] = stock_df['positive'].expanding().sum()
我们发送给老板的数据帧是这样的:
price daily_return num_positive
0 100 NaN 0.0
1 98 -0.020000 0.0
2 95 -0.030612 0.0
3 96 0.010526 1.0
4 99 0.031250 2.0
5 102 0.030303 3.0
6 103 0.009804 4.0
7 105 0.019417 5.0
8 105 0.000000 5.0
9 108 0.028571 6.0
所以到目前为止,该股有 6 个交易日上涨。但是随着我们收集更多的价格,我们的运行总数将随之更新。很好,我们的老板应该会很高兴。
感谢阅读!干杯,祝大家*安健康。
如果你总体上喜欢这篇文章和我的写作,请考虑通过我在这里的推荐链接注册 Medium 来支持我的写作。谢谢!
视窗,和 Docker,和 Kubernetes 哦,我的天!(2020)

马克西米利安·魏斯贝克尔在 Unsplash 上的照片
我们要讲的内容
在本教程中,我们将涵盖在 Windows 上安装 Docker,启用 Kubernetes,安装 helm,并通过运行机器学习笔记本服务器来测试这一切。然后我们将介绍一些额外的东西,如 docker-compose 和 Visual Studio 代码扩展。
入门指南
首先,我在其中设置的示例环境具有正常的互联网连接(没有复杂的代理配置),并且是一台运行 64 位 Windows 10 Pro 副本的 64 位 PC,具有 4 个内核和 16GB RAM。你至少需要 8GB 的内存和两个内核来运行任何合理的东西。
预装:
- 安装了 Git for Windows(【https://gitforwindows.org/】T4)这给了你 git 命令行工具和 bash shell。非常有用。
下载:
- 用于 Windows 安装程序的 Docker 桌面(https://docs.docker.com/docker-for-windows/install/)
- 车窗头盔(https://github.com/helm/helm/releases)
(来源 Giphy)
赋予 Docker 生命
- 运行 docker for windows install,不要选中“使用 windows 容器…”。


2.选择关闭并重新启动,这将重新启动您的机器,并在 windows 启动时启动 docker。

3.一旦 Windows 重新启动,你会在任务栏上看到一个小的动画 docker 图标,显示 docker 正在启动,这可能需要 20 秒到几分钟的时间。

4.一旦它完成了它的启动程序,你会看到一个欢迎卡。在 windows 上使用 docker 不需要登录并注册 docker。

5.打开一个 bash 提示符(Win 键-> bash -> Enter)。然后,您可以运行 docker hello-world 容器。
$ docker run --rm hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pulling fs layer
1b930d010525: Verifying Checksum
1b930d010525: Download complete
1b930d010525: Pull complete
Digest: sha256:f9dfddf63636d84ef479d645ab5885156ae030f611a56f3a7ac7f2fdd86d7e4e
Status: Downloaded newer image for hello-world:latestHello from Docker!
This message shows that your installation appears to be working correctly.To generate this message, Docker took the following steps:
...<rest of message remove>
hello-world 容器测试将验证:
- Docker 服务已正确安装并运行。
- docker 命令位于您的执行路径中,可以与 docker 服务通信。
- 您有权限运行 docker 命令。
- docker 服务可以通过 docker 容器报告到达 DockerHub。
- docker 服务可以运行容器。
如果一切都出错了呢?(来源 Giphy)
排除故障
docker 安装和运行过程可能有点不稳定,因此您可以通过 Docker 仪表板访问诊断日志。单击故障排除图标,在诊断窗格中,会有一个指向日志文件的链接,该链接将在记事本中打开。
或者可以在“C:\ Users \ User \ AppData \ Local \ Docker”找到

访问诊断日志文件
(来源 Giphy)
与库伯内特斯起航
如果你不知道 Kubernetes(简称 k8s)是什么,也不知道它有什么用途,那么有一个来自 K8s 团队的很好的解释你可以翻阅一下。Kubernetes 在希腊语中是舵手或领航员的意思,因此这个图标是船只的舵手,也是你随处可见的航海主题。windows 上的 K8s 过去设置起来非常痛苦,但现在已经变得容易多了。在启用 k8s 之前,将 docker 可用的 RAM 从默认的 2GB 增加到 4GB 可能是明智的,或者如果您有 8GB 的容量,这意味着它将运行得更快。设置可以在 docker dashboard 中找到,该 dashboard 从任务栏图标启动,并单击 Cog 符号。点击“应用&重启”以使新的内存限制生效。


然后只需点击“启用 Kubernetes”,然后“应用和重启”即可启用 Kubernetes。这将下载并初始化所有的 k8s 容器,这些容器是在本地机器上运行本地 one node k8s 集群所必需的。去冲杯咖啡吧,这可能要花很长时间。如果 15-20 分钟后仍未完成(通常是由于内存不足),重启机器,它应该与 windows 一起启动。您可以使用下面的 kubectl 命令测试 k8s 是否都在运行。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
docker-desktop Ready master 167m v1.15.5
我们来部署一下吧!(来源 Giphy)
抓住舵
正如您可能已经读到的,k8s 非常强大,能够运行巨大的集群并管理大量的资源,但是说得委婉一点,它很难管理。为了帮助解决这个问题,并提供预打包的部署在您的集群上运行,创建了 Helm 项目。一个相对容易使用的 k8s 包管理器。解压你之前下载的 helm 包,找到 helm.exe 文件。这个文件可以添加到您的路径中的任何地方,但我更喜欢将它和所有其他 Docker 命令行工具一起放在“C:\ Program Files \ Docker \ Docker \ resources \ bin”中。
启动示例应用程序
要运行 helm 应用程序,您需要添加 repo,更新索引并安装图表。helm install 通常会在部署信息的末尾给出一些有用的说明,让您连接到新创建的服务。
$ **helm repo add stable** [**https://kubernetes-charts.storage.googleapis.com/**](https://kubernetes-charts.storage.googleapis.com/)
$ **helm repo update**
$ **helm install stable/tensorflow-notebook --generate-name**
NAME: tensorflow-notebook-1584196283
LAST DEPLOYED: Sat Mar 14 14:31:25 202
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
1\. Get the application URL by running these commands:
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get svc -w tensorflow-notebook-1584196283'
export SERVICE_IP=$(kubectl get svc --namespace default tensorflow-notebook-1584196283 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo notebook_url=http://$SERVICE_IP:8888
echo tensorboard_url=http://$SERVICE_IP:6006
图表被发送到 k8s 集群,应用程序被初始化,但这不是即时的。下载容器映像并运行它们需要时间。

图表仍在启动中
舵图试图创建 2 个容器,但它仍然在创建它们。这可能需要一段时间。

图表已启动并运行

Helm 将应用程序列为已部署,即使 pod 尚未准备好。
部署图表后,有时需要使用默认凭据来登录。这些可以在舵图中指定,但也可以自动生成,并且对每个实例都是唯一的。要获得存储在 kubectl secrets 系统中的凭证,您需要使用 secret 命令检索它们。例如
$ **kubectl get secret tensorflow-notebook-<unique_id> -o yaml**
秘密 YAML 的输出
然后可以使用 base64 命令对编码的秘密进行解码。
echo ‘XXXXXXXXXXX==’ |base64 — decode
现在您可以在 http://127.0.0.1:8888 登录您的 k8s 托管机器学习 Tensorflow Jupyter 笔记本服务。和一个 Tensorboard 管理界面 http://127.0.0.1:6006。

Tensorflow Jupyter 笔记本服务器

Tenserflow 仪表板
给自己一个击掌,你有一个令人敬畏的设置(来源 Giphy)
一些有用的附加功能
Docker 撰写
在 k8s 所有花哨的云和企业级工具出现之前,有 docker-compose。目前,这种方法通常不用于部署企业应用程序,但对于以一种更简单、更容易上手的方式处理容器来说,docker-compose 是必不可少的。使用一个超级简单的组合文件,你可以用一个简单的“docker-compose up -d”命令运行一系列服务。
简单的 docker 编写文件
创建一个类似 compose_test 的目录,将上面的代码片段放在一个名为 docker-compose.yml 的文件中,在该目录中运行“docker-compose up ”,您就有了一个正在运行的 web 服务。我 90%的时间使用 docker-compose 进行开发、测试和 POC 部署。对于开发工作来说,理解和使用它要容易得多。
Visual Studio 代码
如果你还没有使用过 Visual Studio 代码,你去过哪里!这是微软的一个优秀的免费轻量级编辑器,它不依赖于 Visual Studio,也不需要 Visual Studio,并且有广泛的语言支持。作为扩展生态系统的一部分,有大量优秀的插件可以与 k8s、helm、Docker 和 compose 文件一起工作。我强烈推荐安装微软的“Kubernetes”和“Docker”扩展。
需要注意的事情(来源 Giphy)
一些警告
Docker for Windows 使用了内置于最新版本 Windows 中的 Hyper-V 虚拟化技术。这是 WSL2 和微软虚拟化服务背后的工具集。直到最*,VirtualBox 一直是在 windows 上进行快速简单虚拟化的最佳选择,如果你想让 Hyper-V 和 VirtualBox 在同一系统上共存,那是不可能的。在 VirtualBox 6.1.x 中,这种限制应该已经消除,您可以使用 Hyper-V 后端运行 VirtualBox。我没有测试过这一点,因此,如果您想要使用现有的虚拟机,它可能不会很好地工作。如果它不起作用,可以通过卸载和禁用 Hyper-V 回滚到 VirtualBox,所以尝试一下也无妨。
后续步骤
- 熟读 Kubernetes(【https://kubernetes.io/】T2)
- 仔细阅读赫尔姆(https://helm.sh/)
- 滚动你自己的码头集装箱(【https://docs.docker.com/get-started/part2/】T2
- 从 Docker compose(【https://docs.docker.com/compose/】)开始
- Visual Studio 代码入门(https://code.visualstudio.com/)
在@ratemyskyperoom 获胜:

由 @ratemyskyperoom 评分的房间(和扬声器)的拼贴画。图片归功于每个拍照的人。
在这个不确定的时代,世界比以往任何时候都更需要有力的数据新闻。这不是那个。
相反,我分析了 1321 条推文来回答一个问题,自从 Zoom 成为我们日常生活的一部分以来,我们许多前往疫情的远程工作者都想知道这个问题: 人们喜欢我的房间吗?!
与动物穿越不同,我们没有权威的浣熊可以依靠,以获得关于我们装饰技巧的客观反馈。
相反,在现实世界中,我们得到的最接*的东西是房间评级( @ratemyskyperoom )。随着越来越多的(名人)通过笔记本电脑镜头展示他们的住宅,房间评级机构已经开始公开地、定量地对他们进行评判。
不是所有的家庭都会在国家电视台播出。至少*期不会。但是我们都同意,当那一天到来的时候,我们希望世界看到我们的房间(或者说,我们的存在)配得上 10/10。
所以我问:“怎样才能让我的房间得到 10/10 分?!"
为了找到答案,我把从 2020 年 5 月到 2020 年 7 月的 1321 条房间评级推文全部拉了下来,分析了评级,然后查看了每条推文的图片和文本内容。
以下是我在数据中发现的关键的、深刻的和激动人心的见解。
(本帖互动版在此: 房间评级统计笔记本 )

所有客房评分的百分比分布( src )。*均评分是 7.5/10。
洞察力#1:获得一个好的评价并不是非常困难。
好消息是:获得高分显然并不难。*均评分是 7.5/10,他们像发糖果一样发 8/10。其实大部分收视率至少都是 8/10。
房间评级员说这是一场艰苦的比赛,但他们内心深处是柔软的。

这个愚蠢的渲染显示了个人 twitter 用户的房间评级变化,在他们的第一次评级和最后一次 ratting ( src )之间。左栏显示的是提高了房间评价的演讲者。右栏显示的是失宠的演讲者。
洞察力#2:第二次机会提供了一条救赎之路(或者没有)
即使你的第一次收视率很低,你总会有下一次电视亮相的机会。至少有 83 人的房间不止一次被评过。
无论你来自斯克兰顿还是芝麻的硬街( @JoeBiden +1、 @elmo +1),无论你是政治家、新闻界、民意调查者还是教授(@ RepKarenBass+2; @marycjordan +3,@ frank luntz+3; @j_g_allen +4),房间评分员愿意给你的房间第二次机会。上面,你可以看到左边的 14 个人在他们第一次和最后一次出现之间至少提高了 3 分。最佳改进奖授予了 @anitakumar01 和 @RosenJeffrey (各+5)。
但是要小心!房费评定者给的,房费评定者拿走。至少有 6 个人在第一次和最后一次评分之间得分更低。不好意思 @pattonoswalt !
房间评级成功的秘诀是什么?
为了理解这一点,我查看了两个来源:房间图像和每条推文的文本。为了分析这些图像,我通过AWS“Rekognition”图像识别 API 运行了每一张图像。为了分析文本,我查看了单个单词的用法(例如,“wu”和“tang”,而不是“wu tang”)。这两种方法都不是特别可靠,但是仍然有一些有趣的发现。

左边是由 @jheil 、 @todrick 和 @Judgenap 的房间评分员发布的 3 张样板间图片。右边是亚马逊的 AWS Rekognition 服务如何标注每张图片。
关于高质量的 skype 房间,图像识别能告诉我们什么?
不多。AWS Rekognition 算法似乎很适合区分大类别的事物,但当图像的范围已经有些狭窄时,这不是很有帮助。上面你可以看到一些样本图像的结果。我希望结果更接*“嘿,看,@托德里克在墙上有冰淇淋!”但至少它擅长分辨people和human faces …。
这并不是说它完全没有洞察力。让我们看一个例子。

有或没有动物的房间的等级分布( src )。
洞察力#3:小狗造就伟大的 Skype 房间。
我们的第一个有趣的事实:房间评分员非常喜欢有动物的房间。*均来说,有动物的房间比没有动物的房间得分高 1.2 分。这可能包括动物艺术品,马的雕塑或真实的宠物在人的背景中闲荡。
上面我们有三张图。最左边和最右边标有No Animals和Animals的图表显示了没有动物在其中的房间与有动物在其中的房间的评级分布(至少就 AWS 而言)。覆盖在条上的图像包括来自原始推文的图像样本。(如果你去 笔记本这里 你可以点击图表上的图片看动物)
中间标有No Animals v.s. Animals的图表显示了来自其他两个图表的相同数据,相互重叠。中间的图表还包括底部的小凹口,显示每个分布的*均值(在这种情况下,No Animals*均为 7.4,Animals为 8.6)。

有或没有书籍、艺术品或植物的房间的评级分布( src )。
观点 4:已证实:植物、艺术品和书籍有助于打造更好的房间。
这没有什么突破性的,但肯定令人放心。AWS 的算法可以识别植物、艺术或书籍的照片比没有植物、艺术或书籍的房间照片获得了更高的评级。

扬声器为男性或女性的房间的等级分布( src )。
奇数:男性演讲者的房间更经常被评级,但女性演讲者的房间更好。
男性演讲者的评分几乎是女性演讲者的两倍(男性 842 分,女性 425 分),但*均而言,女性的房间更好 0.3 分。(*注意:这里的“男性”和“女性”是基于 AWS 从图像中对人的性别的预测。)
关于挣高分房,@ratemyskyperoom 的书面反馈告诉了我们什么?
从这些照片中没有发现改变生活的启示,但是也许我们可以直接从房间评估员的书面反馈中获得一些见解…

比较反馈中包含摄像机角度或照明语言的房间的评分分布( src )。
洞察力#5。首先,把基本的东西做好。
把基本的东西做好是很重要的。也就是说,正确地调整相机的框架并检查你的照明。当房间评估员的书面反馈提到重构摄像机(比如“重构”、“裁剪”、“摄像机”、“天花板”等词语。)一般是针对评分较低的房间。同时营造出一种“深度感”稳操胜券。
好的照明都差不多。包括“黑暗”或“背光”等词语的反馈是一个不好的信号,所以确保你没有坐在黑暗的中。

对反馈包含多肉植物或兰花词语的房间进行评级分布比较( src )。
洞察力#6。多肉植物不错。兰花更好。
虽然房间评估员很快建议为你的房间买一棵多肉的或任何普通的植物,但他们实际上似乎更喜欢兰花。

对反馈中包含科里·布克或书籍的房间进行比较的评分分布。
洞察力#6。澄清书籍诉 s .布克。
房间评估员批准房间中包含“书籍。“他们不同意房间里有书。(细微差别。)

比较反馈中包含人质或历史词汇的房间的评分分布( src )。
洞察力#7。"历史性的"比"人质"更好。
也许最大的成功因素是了解“人质”和“历史性”之间的区别似乎被绑架还不够糟糕,房间评级对在电视上看到这些受害者颇有微词。值得称赞的是,他们确实为追随者提供了大量的“历史性 Skype 房间”,供观众在设计自己的房间时效仿。

将所有评分与反馈包括菠萝、Elmo、Wu Tang 或 Mid Century ( src )的评分进行比较的评分分布。
洞察力#8。轻松赢得提升客房评级。
最后,如果你在直播前需要一个快速的刺激,那么房间评分员非常喜欢菠萝、埃尔莫和吴唐家族,所以你可以考虑将这些主题融入到你的中世纪现代装饰中,你很快就会得到 10/10 的分数。
外卖:
- 如果你因为世界末日而感到压力,精心制作和无意义的数据探索可能是一种奇妙的应对机制。
- @ratemyskyperoom 成功的秘诀:好的灯光,好的相机取景,植物,艺术,书籍,动物,菠萝,Elmo 和吴唐。如果你不是科里·布克,也没有被扣为人质,这也会有所帮助。
我是谁?不,不,你是谁?!
嗨!我是伊莱·霍德。我帮助客户设计和开发忙碌的人和他们杂乱的数据之间的有效接口。如果你是一名创始人、创客、讲故事者、非营利行善者或商业领袖,正在思考数据、设计和用户心理的交集,我很乐意联系并聆听你的故事。
你可以发电子邮件到 eli@3isapattern.com 的 T2 或者在 T4 的推特上关注我。
在线数据科学黑客马拉松的获奖解决方案
我在 Analytics Vidhya 数据科学黑客马拉松上的获奖解决方案的完整代码

杰佛森·桑多斯在 Unsplash 上拍摄的照片
数据科学黑客马拉松是有抱负的数据科学家的终极战场。任何黑客马拉松的目的都是让你的想象力引导你拓展这个领域的知识和技能。
经过两年的自学,我决定与其他数据科学家一起在实际战场上检验我的技能。所以我决定参加在线黑客马拉松。我决定将网站列入候选名单,并很快选择了 Analytics Vidhya 开始我的黑客马拉松之旅。
我最初的起步不够好。但是我在我的前四个在线黑客马拉松中的一个上得到了前 50 名。

我从中学到的一件重要的事情是不要参加所有可能的黑客马拉松。
重要的是列出一个你想关注的特定的黑客马拉松,并为此全力以赴。不要同时参加多个黑客马拉松,因为每个黑客马拉松都需要适当的时间和努力。
8 月,Analytics Vidhya 发起了数据至上黑客马拉松。
链接到黑客马拉松:https://data hack . analyticsvidhya . com/contest/the-data-supremity/

图片来源:datahack.analyticsvidhya.com/contest/the-data-supremacy/
并且在看了问题陈述之后,我决定参与并尽我所能。为了更容易理解,我将在不同的部分决定解决方案。
问题
黑客马拉松是一个二元分类问题,我们必须根据特定学生的历史背景来预测他是否有资格参加。
数据集大小:
火车造型:(18359,14)
测试造型:(15021,13)
目标:二元分类→ 0/1
给出的特征总数:13 个特征[列]
了解功能
这是几乎每个人都跳过的重要一步。
不要直接跳到写机器学习代码。首先了解问题,以及每个特性的含义。看看你是否能找到特征和目标变量之间的关系。
在对所有特性进行了适当的研究之后,我决定从代码开始。
图书馆
以下是我在整个黑客马拉松中使用的库。
我几乎在任何地方都使用的 3 个基本库。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
注意:我已经包括了比我实际使用的更多的库,来展示我在黑客马拉松中尝试过的所有库。
import xgboost as xgb
from xgboost.sklearn import XGBClassifier,XGBRegressor
import catboost
from catboost import CatBoostClassifier
from sklearn.preprocessing import LabelEncoder , MinMaxScaler
from sklearn.cross_validation import KFold , cross_val_score
from sklearn.metrics import accuracy_score , roc_auc_score,confusion_matrix
from sklearn.grid_search import GridSearchCV
from sklearn import metrics
from sklearn.ensemble import RandomForestRegressor,RandomForestClassifier
from sklearn.neighbors import KNeighborsRegressor,KNeighborsClassifier
from sklearn.svm import SVR
from sklearn.linear_model import LogisticRegression,LinearRegression
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.model_selection import RandomizedSearchCV
数据看起来怎么样?
现在是时候阅读培训和测试 csv 了。
train_data = pd.read_csv('train_FG6BvLg.csv')
test_data = pd.read_csv('test_wovud0B.csv')
print("Train Shape : {}\nTest Shape : {}".format(train_data.shape,test_data.shape))

train_data.head()


我不得不把快照分成两张图片,因为它在一张图片中看不到。
目标列是我们测试文件中要预测的列。
预处理
我总时间的大约 75%用于预处理数据。
我研究了所有的列,想出了预处理它们的所有方法,并得出了最终的预处理代码。
注意:每一列的预处理技术都是不同的,不会在所有地方都相同。
首先,是时候从城市栏中删除文字部分了
城市 _21 → 21
train_city = train_data.city.str.split("_")
test_city = test_data.city.str.split("_")
这将包含一个有 2 个值的列表(前者是“城市”,后者是分配的数字)
i=0
for x in train_city:
train_data.loc[i,'city'] = x[1]
i=i+1

您可以看到所做的更改。我对 test_data 做了同样的事情。
然后我决定检查数据是否包含任何缺失值。
train_data.isnull().sum()

我们需要正确处理这些缺失的值,因为丢弃它们会导致重要信息的丢失。
在我们开始输入缺失值并对它们进行预处理之前,我们将结合训练和测试数据。这将有助于对整个数据集进行相同的更改。
在组合之前,从训练数据中弹出目标列,因为它不在测试数据中。
target = train_data.pop('target')
enrollee_id = test_data['enrollee_id']
combined_data = train_data.append(test_data)
现在是开始进一步预处理的时候了。我将城市列的数据类型从字符串改为整数:
combined_data.city= combined_data.city.astype('int')
我将性别列替换为整数类别:
combined_data.gender.replace({'Male':2,'Female':0,'Other':1},inplace=True)
我对另外两个专栏做了同样的事情:
combined_data.enrolled_university.replace({'no_enrollment':1,'Part time course':2,'Full time course':3},inplace=True)combined_data.major_discipline.replace({'No Major':0,'Other':1,'Arts':2,'Humanities':3,'Business Degree':4,'STEM':5},inplace=True)
列 experience 和 company_size 包含类似于“< 10”和“> 500”和“500–1000”的字符串。这阻止了将列设为整数。
combined_data.experience.replace({'>20':21,'<1':0},inplace=True)
为了说明 company_size 的样子:
combined_data.company_size.value_counts()

combined_data.company_size.replace({'<10':0,'10/49':1,'50-99':2,'100-500':3,'500-999':4,'1000-4999':5,'5000-9999':6,'10000+':7},inplace=True)
company_type 列具有分类值:
combined_data.company_type.value_counts()

我没有给出随机数来替换,而是按照受众的递增顺序来替换。
combined_data.company_type.replace({'Other':0,'Early Stage Startup':1,'Funded Startup':2,'NGO':3,'Public Sector':4,'Pvt Ltd':5},inplace=True)
我还在剩下的专栏中做了一些替换。
combined_data.last_new_job.replace({'never':0,'>4':5,'nan':1},inplace=True)combined_data.relevent_experience.replace({'No relevent experience':0,'Has relevent experience':1},inplace=True)
缺失值怎么办?
我尝试了各种插补法,包括 sklearn 库中的 SimpleImputator。我甚至试着用*均值来表示它们,但是这并不比用 0 来表示它们更好
combined_data.major_discipline.fillna(0,inplace=True)
combined_data.company_type.fillna(0,inplace=True)
combined_data.company_size.fillna(0,inplace=True)
combined_data.enrolled_university.fillna(0,inplace=True)
combined_data['enrolled_university.1'].fillna(0,inplace=True)
因为数据的数值范围不同。在继续我们的模型之前,我决定将数据标准化。
我使用了 MinMaxScaler,它允许我自定义我的最小和最大范围值。
values = combined_data.values
scalar = MinMaxScaler(feature_range=(0,5))
x_scaled = scalar.fit_transform(values)
combined_data = pd.DataFrame(x_scaled,columns=combined_data.columns)
为了转移到我们的模型,我们需要删除 ID 列。
combined_data.drop('enrollee_id',axis=1,inplace=True)
模型
老实说,我尝试了 10 多种算法,看看哪一种有效。
我从 RandomForest 开始到 CatBoost。
clf = CatBoostClassifier(iterations=200,depth=4,eval_metric='AUC',l2_leaf_reg=9,learning_rate=0.1)
但是 XGBoost 优于所有其他算法。所以我坚持用它来创建模型函数:
def model_fit(alg, dtrain, target,dtest):
xgb_param = alg.get_xgb_params()
xgtrain = xgb.DMatrix(dtrain.values, label=target)
cvresult = xgb.cv(xgb_param, xgtrain, num_boost_round=alg.get_params()['n_estimators'], nfold=5,
early_stopping_rounds=50)
#alg.fit(dtrain,target,use_best_model=True,eval_set=(x_val,y_val))
alg.fit(dtrain,target)
print("Model Report")
print("Accuracy is {}".format(alg.score(x_val,y_val)))
feat_imp = pd.Series(alg.feature_importances_,dtrain.columns).sort_values(ascending=False)
feat_imp.plot(kind='bar', title='Feature Importances')
plt.ylabel('Feature Importance Score')
y_pred = alg.predict_proba(dtest)[:,1]
return y_pred
上面的代码片段在一个函数中包含了模型拟合和绘制特性重要性图。
最后是我的模型和它的参数:
clf = XGBClassifier(
learning_rate =0.3,
n_estimators=100,
max_depth=3,
min_child_weight=1000,
gamma=0.7,
subsample=0.45,
colsample_bytree=0.4,
objective= 'binary:logistic',
nthread=1,
scale_pos_weight=1,
seed=27,
reg_alpha =0.7,
random_state=200)
你是怎么得到这些参数的?
我使用了 GridSearchCV 函数,它有助于从输入的参数范围中选择最佳参数。
param_test2 = {
'n_estimators':[150,170,200],
'learning_rate':[0.03,0.05],
'max_depth':[4,10,15,25],
'min_child_weight':[1,2,5,10],
'subsample':[0.5,0.7,1],
'colsample_bylevel':[0.45,0.7,1],
'colsample_bytree':[0.45,0.7,1],
'gamma':[0,0.1,0.5,1]
}
gsearch2 = GridSearchCV(estimator = XGBClassifier(),
param_grid = param_test2, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch2.fit(x_train,y_train)
gsearch2.grid_scores_, gsearch2.best_params_, gsearch2.best_score_
GridSearchCV 从我们传递的超参数排列中打印出最佳参数。(评分设置为“roc_auc”)
得到合适的超参数后,就该拟合模型了。
y_pred = model_fit(clf,x_train,y_train,test_data)

正如你所看到的,培训时间是使学生合格的最重要的特征,其次是城市发展指数和经验。
等等,我们必须评估我们的模型
我使用 KFold 来评估我的模型,以检查它的有效性。
k_fold = KFold(len(train_data), n_folds=8, shuffle=True, random_state=0)
print(np.mean(cross_val_score(clf, train_data, target, cv=k_fold, n_jobs=1)))

KFold 将提供训练/测试索引,将数据分成训练和测试集。它将把一个数据集分成 k 个连续的折叠。由于模型精度和 kfold 非常相似,因此模型工作正常。
提交
我一共上传了 20 多个投稿文件来提高我的分数,达到巅峰。
submission = pd.DataFrame({'enrollee_id': enrollee_id , 'target': y_pred})
submission.to_csv('submission.csv',index=False)
结果
所有的努力都得到了回报。我在全球 1000 人的黑客马拉松中获得了第三名。以下是排行榜的快照。

图片来源:datahack.analyticsvidhya.com/contest/the-data-supremacy/
我的用户名:jsiddhesh96
代码可以在我的 GitHub 存储库中找到:
[## sid 11/AnalyticsVidhya _ DataSupremacy
比赛链接:(https://data hack . analyticsvidhya . com/contest/the-data-supremity/)我用过 xgb classifier……
github.com](https://github.com/Sid11/AnalyticsVidhya_DataSupremacy)
赢得数据压缩游戏
如何在某些特殊情况下击败世界级的压缩算法

有几种功能强大的数据压缩程序被广泛使用。一些著名的例子有 gzip 、 bzip2 和 pkzip 。最*,我开始思考,如果给定一个要压缩的特定输入文件,我是否可以创建一个优于所有这些程序的算法。
挑战
我决定把它变成自己的游戏。我选择了一个重要的输入文件。然后我在上面用了 gzip,bzip2,zip 压缩。在每种情况下,我都提供了尽可能产生最积极的数据压缩的命令行选项。然后我选择了三个程序输出中最小的字节作为我的目标。我开始编写一个程序,在给定相同的输入文件的情况下,它可以超过这个最小的字节大小。
我希望我的测试输入看起来有用。我不想为了给自己一个优势而设计一些奇怪的输入。我决定使用英文单词的字母列表。从我在网上找到的一个 ASCII 文件开始,我删除了包含除小写字母a到z以外的任何字符的所有行。结果是这个文件:https://github . com/cosine kitty/compress game/blob/master/words . txt
这个words.txt文件由 636946 字节组成,包含 67527 个字,每行一个字。我使用了以下命令来压缩这个文件。所有这些程序都运行在我的 64 位 Debian Linux 系统上。
首先,我从 bzip 版本 1.0.6 开始:
bzip2 -c --best words.txt > words.bz2
然后我用了 gzip 版:
gzip -c --best words.txt > words.gz
最后,我使用了 Info-ZIP 程序,版本 3.0:
zip -9 words.zip words.txt
以下是产生的文件大小:
don@spearmint:~/github/compressgame $ ls -l words.*
-rw-r--r-- 1 don don **248587** Apr 22 15:05 words.bz2
-rw-r--r-- 1 don don **212736** Apr 22 15:05 words.gz
-rw-r--r-- 1 don don 636946 Apr 22 14:49 words.txt
-rw-r--r-- 1 don don **212876** Apr 22 15:05 words.zip
所以在这种特殊情况下,gzip 是赢家。它产生了 212736 字节的输出。那是我要打败的目标。
游戏规则
每个游戏都需要规则。在玩游戏之前固化规则是有道理的。以下是我事先选择的规则:
- 我的数据压缩器的输出是另一个 Python 程序。
- 执行时,生成的 Python 程序必须打印出
words.txt中包含的精确文本。 - 该程序不得引用任何外部文件。它必须是完全自包含的,并通过算法创建其输出。
- 获胜程序的源代码必须小于 212736 字节。
- 为了更有趣,我决定生成的 Python 程序不能使用任何
import语句。它必须只使用 Python 语言的内置部分。
测试装具
我知道我会尝试很多。我需要一个可以尝试多种算法的测试工具。对于每个算法,该工具应该生成候选 Python 程序作为输出,执行该程序,确认它一字不差地生成原始单词列表,并以字节为单位记录该程序的大小。
如果任何生成的程序未能产生正确的输出,线束必须中止并显示错误消息。如果所有的程序都通过了测试,它会选择胜出者:具有最小字节大小的输出。
我的测试线束叫squash.py。它需要一个变量AlgorithmList中的算法列表。每个算法都是一个必须公开一个Name函数和一个Compress函数的对象。下面是squash.py的相关摘录。(GitHub 上有完整的项目源代码。请参阅本文末尾的参考资料部分。)
霍夫曼编码
我尝试的算法都依赖于霍夫曼编码,这是一种使用可变位数表示有限符号集的技术。更频繁出现的符号由更少的比特表示;更少的符号需要更多的比特。霍夫曼编码的主要负担是符号不是字节对齐的。这需要额外的代码将这些位尽可能紧密地打包在一起,忽略字节边界。
霍夫曼编码的工作原理是建立一个二叉树,其叶节点代表输入中存在的每个不同的符号。每个符号的二进制编码由从根节点到该符号的叶节点的路径决定。
我将使用一个只有五个符号的简单示例来解释构建霍夫曼树的算法:字母 A 到 e。假设我们计算每个字母在我们的输入中出现的次数,并得出以下数字。
A : 351
B : 12
C : 9
D : 13
E : 823
如果我们使用固定数量的比特来表示 5 个可选符号,我们将需要 3 个比特,因为 2 =8,这是足以表示 5 个可选符号的 2 的最小幂。霍夫曼编码可以通过利用我们可以对最常出现的符号使用更少的比特这一事实来做更有效的工作。
霍夫曼编码器首先创建 5 个叶节点,每个符号一个。还没有树。我们只是有单独浮动的叶节点,还没有相互连接。编码器创建一个按计数升序排序的节点列表。这是我们开始时的样子:

然后霍夫曼编码器进入一个循环。在每次迭代中,编码器删除排序列表中的前两个节点,并为它们创建一个新的父节点。父节点的计数成为两个子节点计数的总和。父节点被插回到节点列表中保持节点按计数排序的任何位置。
因此,在我们的示例中,编码器删除了 C:9 节点和 B:12 节点,创建了一个计数为 9+12=21 的父节点,并在 D:13 和 A:351 之间插入了新节点,以保持排序后的顺序。结果看起来像这样:

请注意,新创建的节点是内部节点,而不是叶节点,因此它不代表单个符号。因此,它有一个计数,但没有附加符号。
因为每次迭代都从列表中删除两个节点并用一个节点替换它们,所以列表每次都会缩短一个节点。该算法一直循环,直到只剩下一个节点。该节点是最终树的根节点。在我们的示例中,完成的树看起来是这样的:

此时,我们不再需要频率计数。此外,用位 0 和 1 标记树中的分支有助于使编码清晰。每个左分支接收 0,每个右分支接收 1。让我们也使叶节点和内部节点看起来不同。树现在看起来像这样:

我们使用这棵树来构建字母 A 到 e 的二进制表示。对于每个字母,从树根开始(用*标记),沿着树向下移动,直到到达那个符号。每次向左分支时,附加一个 0。每次向右分支时,添加一个 1。这为我们的五个符号产生了以下位模式:
A = 01
B = 0011
C = 0010
D = 000
E = 1
正如我们所希望的,最常出现的符号 E 使用了最少的数据量:只有 1 位!两个最不常用的符号 B 和 C 各使用 4 比特,这比固定比特长度表示所使用的 3 比特差。但是好消息是输入中没有太多的 B 和 C。
霍夫曼编码的效率
那么在这个简单的例子中,我们的数据压缩有多好呢?让我们比较一下简单的固定位方法和霍夫曼可变位方法。
固定比特方法要求我们对每个符号使用 3 比特。如果我们合计输入中的符号总数,并乘以每个符号 3 位,则我们得到:
3*(351+12+9+13+823) = 3624 位
现在让我们看看霍夫曼表示法总共需要多少位。有 351 个 A 符号,每个使用 2 比特;12 个 B 符号,每个使用 4 比特,等等。这给了我们
2351 + 412 + 49 + 313 + 1*823 = 1648 位
我们用 1648 除以 3624,发现压缩比在 45%左右。因此,在这种情况下,霍夫曼编码将输入缩小到不到其原始大小的一半。当然,这个压缩比对于每种输入都是不同的。这完全取决于每个唯一输入流中存在的符号的相对频率计数。
同样重要的是要注意,霍夫曼树本身需要存储在压缩输出中。解压缩算法需要知道如何解释这些位,以便重建原始数据。因此,这给压缩输出的大小增加了一点开销。
霍夫曼编码器源码
我的霍夫曼编码器的源代码包括两个类:HuffmanNode和HuffmanEncoder。这两个类都在源文件huffman.py中。
如上所述,HuffmanNode对象代表霍夫曼树中的一个节点。每个节点都包含一个保存原始未压缩符号的符号字段、一个频率计数以及对左右子节点的引用。对于叶节点,引用被设置为None。对于内部节点,left和right是指其他HuffmanNode对象。下面是HuffmanNode的代码:
HuffmanEncoder类支持上面解释的算法。它包含一个由 compressor 算法调用的Tally方法,用于计算每个符号在输入中出现的次数。它还包含一个生成霍夫曼树的Compile方法。它实现了上述算法,用于从节点的排序列表中构建树。Compile返回树的根节点,这为调用者提供了整个树。
一旦 compressor 算法计算完所有符号并调用Compile来获得霍夫曼树,它需要一种方法将输入中的每个符号转换为其霍夫曼表示,作为可变长度的位模式。为此,compressor 调用根节点的MakeEncoding方法。这个函数创建一个字典,它的键包括输入中的每个符号。
在这个字典中,与每个符号相关联的值是一个元组(data, nbits),其中data是一个整数,保存表示从树根到该符号的路径的比特,nbits是该整数中有效低位比特的数量。例如,如果位模式是0111,data将是 7,nbits将是 4,产生元组(7,4)。
现在,我们有了一个将原始输入文本转换成霍夫曼编码比特流的良好基础。但是我如何在生成的 Python 源代码中表示这些可变的比特流呢?
Base64 表示法
我要求输出是 Python 源代码,这让我的挑战变得更加困难。不仅程序必须包含自己的解压缩逻辑,而且源代码本质上必须只包含可打印的字符。任意二进制数据会导致 Python 代码混乱。
为了解决这个问题,我使用 Base64 对二进制数据进行编码。在这种编码中,每个连续的 6 位数据块被替换为 64 个字符中的一个:a-z、A-Z、0–9、+或/。这些 ASCII 字符中的每一个都需要一个完整的 8 位字节来表示,所以我要存储的每 6 位就损失了 2 位的效率。
虽然 Python 内置了base64模块,但是我的编码器有一定的特殊需求,所以我自己写了一个名为BitBuffer的类。它具有以下特点:
- 它有一个接受元组
(data, nbits)的Append函数。这与存储在由HuffmanNode.MakeEncoding返回的字典中的元组完全吻合。 Append函数动态转换为 Base64。对于每批 6 位,它缓冲另一个 Base64 字符。它会记住任何剩余的位,并保存它们,直到更多的位到达,以填充更多的 6 位块。Append通过在每 80 个 Base64 字符后换行,防止文本行变得过长。- 还有另一种方法
Format将任何剩余位作为最终的 Base64 字符进行刷新。然后,它将整个 Base64 缓冲区转换为可以写入输出源代码的 UTF-8 字符串。它将该字符串返回给调用者。
这里是BitBuffer类。
现在我已经有了自己的基础设施。下一步是尝试各种压缩算法,这些算法可以生成输入到BitBuffer类的比特流,并创建相应的解压缩算法来从压缩的 Base64 图像中提取原始数据。
尝试 1:所有字符
在我的第一次尝试中,我知道我不会击败 gzip,但我必须从某个地方开始。我所做的只是对输入的每个字符使用霍夫曼编码。输入中出现了 27 个不同的字符:a - z和换行符(在 Python 中表示为'\n')。我的第一次尝试分两步处理输入。在第一遍中,它计算 27 个字符中的每一个出现的次数。它使用这些信息来建立一个霍夫曼树。
一旦有了霍夫曼树,它就会第二次调用私有函数_Encode来创建单词列表的压缩 Base64 表示。
我将第一个压缩器编写为一个名为cmp_letters.py的模块。下面是它的源代码:
这种形式的压缩效果如何?它确实使数据变小了,但正如我所怀疑的,它没有打败 gzip。输出文件letters.py的总大小为 460531 字节,压缩比为 460531/636946 = 72%。下面是letters.py的样子,为了简洁起见进行了编辑。(您可以通过在本文末尾的参考资料部分克隆我的 GitHub 库并运行squash.py程序来获得全部内容。)
解压器是如何工作的
我的第一次尝试有一些特征是我的三个解压器共有的。第一行可执行代码创建了一个变量Char。它是使用元组的霍夫曼树的表示。元组包含两个元素。这些元素中的每一个都是另一个包含两个元素的元组,依此类推,直到到达由单个字符组成的叶节点。
为了理解如何使用Char中的霍夫曼树,请看一下letters.py中的函数Huffman。Huffman函数使用BitReader类从 Base64 编码中一次提取一位。它一次向下遍历二叉树的一位,选择左分支(元组中的第一个元素)或右分支(第二个元素),这取决于 Base64 数据中的下一位是 0 还是 1。最终,它到达一个不是元组的元素。该元素是树的叶节点,由原始的、未压缩的字符组成。Huffman函数将该字符返回给调用者。
尝试 2:省略共享前缀
我知道我可以做得比 72%的压缩比更好。我的下一个想法是利用单词按排序顺序出现的事实。这意味着列表中相邻单词之间通常有一些相同的字母。例如,考虑来自words.txt文件的这段摘录:
decade
**decade**nce
**decadenc**y
**decaden**t
**deca**gon
**deca**hedron
粗体字母显示每个单词中与列表中前一个单词前面相匹配的部分。我推断我可以创建代表这些整数计数的符号。那么上面的话,在decade之后,可以更简洁地表示为
6nce
8y
7t
4gon
4hedron
因此,在我的第二次压缩器/解压器尝试中,我创建了两个独立的霍夫曼树,一个用于前缀计数,另一个用于后缀中出现的字母。这表明霍夫曼编码器可以处理整数或字符作为输入符号。HuffmanEncoder类不关心数据类型是什么,只要每个符号允许自己被用作字典键。
压缩源文件被命名为cmp_prefix.py。下面是它的代码:
下面是相应生成的解压缩程序,为了简洁起见,再次进行了编辑:
用整数计数代替常用的单词前缀真的很有帮助。它将输出大小降低到 220603 字节。我现在越来越兴奋了,因为我已经接* gzip 的 212736 字节了!我只是需要提高一点效率。
第三次是魅力
我的下一个想法是,我可以利用英语中某些字母比其他字母更容易成对出现的事实。例如,如果你在一个英语单词中看到一个q,那么下一个字母几乎肯定是一个u。尽管u是一个相当常见的英文字母,但它更有可能出现在q之后。一般来说,当前字母强烈影响下一个字母出现的概率。
因此,我的第三次尝试是基于我的第二次尝试的前缀优化,只是没有对字母频率使用单个霍夫曼树,而是使用了 27 个霍夫曼树:26 个可能的前面字母中的每一个都有一个,再加上一个单词中的第一个字母(当没有前面的字母时)。
这种方法意味着我必须在输出中消耗额外的空间,以便在生成的输出中存储额外的 26 棵霍夫曼树。
Repeat是与前一个单词匹配的每个单词前面的字符数的霍夫曼树。Tail是对前缀后每个单词剩余部分的字符数进行解码的树。而Char现在是一个霍夫曼树的列表。Char[0]是单词中第一个字符的树,Char[1]是字母 a 后面的任何字符,Char[2]是字母 b 后面的任何字符,依此类推。
这就是我最终打败 gzip 的地方。源文件pairs.py长 202641 字节,比words.gz小 10095 字节。胜利!
最后的想法
显然,我的方法并没有剥夺 gzip 作为顶级通用数据压缩算法的地位。我所做的工作之所以这么好,是因为我根据人类对给定输入文件的理解对它进行了裁剪。我的算法甚至不能处理除小写字母和换行符以外的字符。如果输入没有按字母顺序排序,它的性能不会很好。
但是这个练习确实展示了一些有用的东西。如果您有一个想要压缩的固定数据集,有时可以设计一个比众所周知的通用算法性能更好的自定义压缩算法。
如果你准备好接受一个有趣的挑战,也许你会从我停下的地方继续。使用同样的规则,你能为同样的words.txt输入创建另一个击败我的算法吗?如果是这样的话,我会很高兴收到你的来信并了解你的所作所为。
参考
- 我的测试输入是基于一个单词列表,我在:
http://www-personal.umich.edu/~jlawler/wordlist.html找到的 - 我的自定义数据压缩器的完整源代码:
https://github.com/cosinekitty/compressgame
如何应对组织中令人担忧的人工智能状况

这些图形卡(GPU)对人工智能的进步至关重要——但它们不会阻止人工智能的冬天。[ 照片来源
意见
组织里的 AI 有问题。如果领导者现在不扭转人工智能,人工智能的冬天将会到来——而且不会很快消失。
W hat a move:戴姆勒冻结了与宝马的合作关系,并与英伟达合作制造自动驾驶汽车。这是一个正确的决定,因为戴姆勒和宝马永远不会成功对抗 Waymo 或大众汽车——也因为人工智能没有兑现商业大师的承诺。
是的,正如 7 次担任首席信息官的 Mark Settle 为《福布斯》撰写的文章所说,“在商业低迷时期,这些明智的投资会在未来带来丰厚的回报”。戴姆勒和英伟达的合作是否会成为其中之一,还有待观察。那是因为组织中的人工智能可能有问题。
许多商业领袖告诉我们,组织正在让人工智能失败,人工智能正在让组织失败。
大多数人工智能原型从未实现过。公司在不创造有形价值的人工智能项目上花费数百万。《经济学人》最*承认,我们不仅处于经济衰退,而且处于人工智能衰退。根据我们自己的观察,我们确实认为那是真的。
在 20 世纪 70 年代中期和 90 年代初,正是对人工智能进展的失望导致了强烈的人工智能冬天。只需注意 1974 年,美国国防研究局(DARPA)在语音理解研究方面的失望导致了人工智能资金的大幅削减。然而,许多年后,当时开发的技术,即隐马尔可夫模型,成为机器理解人类语言的必要条件。像七十年代一样,今天我们正在接*另一个人工智能的冬天。但是为什么是这次呢?
1.关于艾未来的说法结果并不好
2015 年,埃隆·马斯克承诺,到 2018 年,我们将最终拥有完全自动驾驶的汽车。我们不知道。与汽车的情况一样,组织中的人工智能在很大程度上被视为一场革命。然而,机器学习最擅长的是复杂的模式匹配。人工智能可以带来革命,但在大多数情况下,“只会”提高效率,实现超个性化,并培养准确性。人工智能仅仅是一场革命的概念已经导致了深深的沮丧,因为这个承诺在很大程度上仍然没有实现。人们正在慢慢理解人工智能的未来可能与描绘的不同。
2.严峻的技术和数据挑战依然存在
人工智能*年来的进步不仅仅是因为大数据和更好的算法。它的改进很大程度上归功于看不见的计算能力的可用性——这是深度学习的一个必要前提。然而,对于像自动驾驶这样的高级任务,算法的训练成本越来越高,摩尔定律正在消失。似乎这还不够,人们越来越多地发现大数据充满了陷阱。我们没有坐在自动驾驶汽车中的原因是因为它们仍然无法处理训练数据没有覆盖的异常情况。算法不太适应看不见的东西。

3.领导回避人工智能
根据 Gartner 的说法,大量人工智能项目的失败是因为它们从一开始就规模过大,实现影响太晚,以及员工参与不足。更重要的是,组织无法招募到合适的人才,也无法建立人工智能的思维模式和能力。领导者仍然不为人工智能去神秘化,也不为其激励他人。但是没有员工的参与,人工智能项目很容易失败。人工智能需要更多的领导力,而不是更少。
这些发展让组织中的人工智能处于一种矛盾的境地——早在新冠肺炎危机全面打击组织之前:它已经被谈论过,员工仍然害怕它,而领导者却不认可它。此外,IT 和人工智能的支出计划正在发生重大变化。因此,艾的未来仍然相当暗淡。
然而,我们亲身经历了领导者如何在危机中扭转组织中的人工智能并创造价值。
领导者现在能做什么
有了解决现实生活、高度相关问题的小规模项目,以及随之而来的精心策划的心态变化,组织可以收获人工智能的好处。要做到这一点,领导者必须将人工智能视为提升业务绩效的一种手段——并选择最有可能不会成为一场革命的用例。其次,领导者需要明白,对于许多应用来说,浅层机器学习非常适合。这个版本的人工智能确保数据需求和计算成本保持在低水*。最后,领导者需要建立一个人工智能运动,培养类似人工智能的学习文化:它使员工能够使用数据和人工智能方法来应对当今商业环境的挑战,从而创造真正的价值。
电晕疫情将推动那些已经在人工智能领域领先的人前进。那些现在不支持人工智能项目的人将在我们的后危机世界中更加落后。
本文基于我们在 领导机器学习 工作中的见解,由 Oliver Triebel 和 Tim Schleicher 撰写。
有了 AWS 副驾驶,让 Kubernetes 重新思考一下——数据生活
新的 ECS CLI 便利性使部署容器变得更加容易,而无需管理基础架构。

奥斯卡·萨顿在 Unsplash 上的照片
谁在乎呢。
Kubernetes 是一个非常棒的容器编排工具,用于可伸缩的云计算应用程序。经过多年的开发和内部使用,谷歌在 2014 年开源了该工具,导致小型企业和企业的采用量激增。也就是说,我们并不总是需要它的所有电池。虽然它通过 YAML 配置文件提供了一个相对方便的声明性接口,但是一个单独的开发人员或小团队仍然需要担心部署的许多细节,例如:
- 虚拟专用云(VPC)
- SSL 证书
- 负载*衡器
- 集装箱登记处
所有这些细节对于管理云解决方案都至关重要,有些人将这一领域作为他们的主要工作。然而,其他人希望专注于应用程序本身。
在许多小项目中,你不会有一个复杂的 80 亿容器的庞然大物需要 Kubernetes 进行编排。我没有这方面的统计数据,但我打赌大多数概念验证项目都包含 1-4 个容器(除此之外:解析 Github/Docker Hub 似乎是一个有趣的项目)。对于这些项目,有一种相当直接的方式告诉您的云提供商“嘿,这是一个容器映像。把它挂在天上,并向我收取它所消耗的费用”就足够了。
使用方便的新 CLI [copilot](https://github.com/aws/copilot-cli/wiki)进入 ECS。
从应用到部署,速度非常快
我制作了一个演示应用程序来驱动这个过程,并允许您快速查看它的运行情况。对于实干家,你可以在下面的资源部分找到链接。让我们从 NLTK 库中取出一个现成的情感分析器,并使用我最喜欢的 python API 库 FastAPI 为文本输入提供情感评分。我完全没有必要对 FastAPI 进行概述,因为下面的一个例子符合您的情况:
- 你已经知道并喜欢它了
- 你将通过它的伟大的文档
记住,应用程序开发人员主要关心 1)应用程序代码和 2)它所在的容器。嗯,他们在这里。
也有一些便利的好东西,但我不会把它们扔到你脸上。如果你好奇,请点击这里查看。
现在我们有了自己的应用程序,部署序列就像下面这样简单。
make build test
copilot init
copilot env init \
--name prod \
--profile default \
--app fastapi-sentiment
copilot svc deploy \
--name fastapi-sentiment \
--env prod
对于那些对上面的copilot命令的内部流程感兴趣的人,可以看看下面的展示,它摘自 Efe Karakus 在 AWS 博客上的文章。

初始化副驾驶员部署的示例终端流程[ 来源
您可能想知道这与规范的“kube apply -f deployment.yml”相比如何,认为它需要更多的命令来实现同样的事情。请记住,上面的命令将设置您的基础设施和应用程序。因此,Kubernetes 应用程序的等效比较是将所有基础设施即代码(IaC)设置与 Kubernetes 配置文件结合起来。那……是更多的工作。
我们在直播

Wil Stewart 在 Unsplash 上拍摄的照片
有了这几条命令,我们的应用程序现在可以在黄金时间公开使用(如果您愿意的话)。不差几行好。所有这些节省下来的时间现在可以用于更多的原型制作、测试或与您的猫玩耍。

一些简单的命令演示了对已部署 API 的访问
这个演示只是触及了 ECS 和copilot能够和将要做的事情的表面。有管道资源、与docker-compose的计划集成以及 CI/CD 集成的简单选项。更多阅读,请查看下面的参考资料。
帮我帮你:告诉我下一步该怎么做。
资源
保持最新状态
这一个就到此为止。然而,在学术界和工业界,事情发生得很快!用 LifeWithData 博客、Medium上的文章和我的 Twitter 让自己保持最新状态。
原载于 2020 年 8 月 22 日 https://www.lifewithdata.org**的 。
通过 AutoGluon,AWS 将 AutoML 带到了他们的*台上
随着 AutoGluon 的发布,AWS 现在在其云*台中提供托管和自动化机器学习。

AWS 已经对其竞争对手在深度学习 AutoML 领域的发展做出了回应,在其机器学习云服务中引入了 AutoGluon。该框架建立在现有框架的基础上,为学习如何针对特定的学习任务塑造每个数字大脑提供了一个集中的模块化解决方案。然而,随着这一领域的类似发展,出现了一个核心问题。他们是在为开源团队效力,还是只是想让每个人都使用他们能控制的软件?
不管是哪种方式,让我们一步一步地了解自动旋转的谱系,以充分理解它在空间中的位置。
MXNet
另一个 Apache 项目 MXNet 是亚马逊选择的深度学习架构框架。虽然存在其他几个这样的框架,但亚马逊将该项目捐赠给 Apache 软件基金会值得称赞。这样做符合开源协作的一些核心目标,即标准化和降低类似工作的门槛。
胶子
由 AWS 和微软于 2017 年推出的深度学习模型的必要软件框架 Gluon,旨在从众多相似但不同的机器学习软件库中带来一些秩序。

此外,Gluon 项目的 Apache 性质导致了一些围绕它创建的奇妙的学习材料。它的主网站有越来越多的课程和例子,从经典的机器学习到标准的深度学习再到贝叶斯深度学习。作者有一个相当有趣的目标:
据我们所知,目前还没有资料来源教授(1)现代深度学习中的全部概念,或者(2)在引人入胜的教科书中穿插可运行的代码。在这次冒险结束时,我们会发现这种空白是否有充分的理由存在。
自动增长
AutoGluon 现在位于 MXNet 和 Gluon 之上,为给定的文本、图像或表格数据提供神经网络的自动学习。该库包括一些用于神经架构搜索 (NAS)的最先进的算法,既用于传统的深度学习架构,也用于强化学习代理。如果你对该方法的螺母&螺栓感兴趣,请参见 Jesus Rodriguez 关于 Medium 的文章。
一起工作,但是分开?
自然,AWS 也在其机器学习*台 Sagemaker 中无缝地利用了自动旋翼。然而,尽管 AWS 和微软在 Gluon 上有合作,但两者都创建了自己的 NAS 库。就在上个月,微软开源了项目 Petridish ,一个前沿架构搜索库。最重要的是,虽然它们最终可能会提高跨云兼容性,但这两个库目前都与各自的云深度耦合。

上图显示了大公司如何推出越来越多的软件库,希望开发人员坚持使用他们的软件库。此外,直接尝试它们之间的兼容性存在明显的不足。与其说是“大家好,让我们一起分享这个”,不如说是“大家好,用我们的,不要用他们的。”这让我想起了电话提供商之间的争斗。你们中有多少人喜欢尝试将 Android 手机与基于 iCloud 的东西同步,或者相反?
做体力工作
准备好试试图书馆了吗?无论您是在本地部署还是在云中部署,这里都有一些选项可供选择:
预训练模型
从架子上取下一个预先训练好的模型,并将其应用到您自己的任务中!支持代码重新训练同样的模型也提供可再生产性。
卡格尔
Kaggle 抓痒大多数机器学习在实践中会痒。该*台通过开放的数据集、共享的工作空间和正式的比赛,提供了学习、合作和与他人竞争的方式。auto glon 的文档分享了一些片段,用于在你的 Kaggle 工作空间中快速应用 auto glon。
Sagemaker
Sagemaker *台在 2019 年有了很大的改进,在 Sagemaker Studio 中提供了 AWS 声称的第一个真正的机器学习 IDE。现在下定论还为时过早,但该*台显示出巨大的前景。
我只是想跟上
对于那些热衷于简单地跟上神经架构搜索空间发展的人来说,automl.org在跟上发展方面做得非常好。
原载于 2020 年 1 月 25 日 https://lifewithdata.org**的 。
有了 Keras 的函数式 API,您的想象力将达到极限

来源: Unsplash
构建任何你喜欢的神经网络结构
大多数人都熟悉构建顺序模型,其中各层一个接一个。例如,在卷积神经网络中,我们可以决定让图像通过卷积层、最大池层、*坦层,然后是密集层。这些网络的标准结构被称为“线性拓扑”。
然而,许多高性能网络并不是线性拓扑结构,例如,作为顶级初始模型核心的初始模块。在该模块中,来自一个层的输入被传递到四个单独的层,这四个层被连接回一个输出层。(你可以在这里阅读更多关于初始模块和 1x1 卷积的目的。)

初始模块。来源:启始论文。
非线性拓扑允许输入通过不同的解释媒介传递,这些媒介随后可以连接起来以产生全局洞察力。这类似于总统可能会给他们的内阁(顾问)几份潜在法律的副本,他们每个人都会从环境、国家安全、小企业等角度给出自己的观点。这是获取尽可能多信息的有效方法。
或者,您可以使用它们来创建跳过连接(成功 ResNet(剩余网络)的核心)和其他在顺序设置中无法想象的复杂关系。
Keras 的功能 API 允许你创建各种拓扑,不仅优化网络的内部结构,而且处理需要多个输入或输出的问题。
例如,考虑一个评估房屋价值并根据图像(草坪、厨房、外部等)将其分为三类的网络。)、数字数据(*方英尺、卧室)和文本数据(以前的业主/邻居的证明)。我们将创建一个可以做到这一点的网络。
函数式 API 将网络视为有向无环图,因此不支持动态架构。从这里开始,最好将层视为计算图中的节点,将输入视为遍历图的边和节点。(点击阅读更多关于程序员图论基础的内容。)我们将通过三个渐进的步骤来探索函数式 API:
- 使用函数式 API 创建线性图。
- 使用函数式 API 创建非线性图形。
- 使用函数式 API 创建复杂的多输入/输出图。
- 进一步探索和想法
使用函数式 API 创建线性图形
让我们从使用函数式 API 创建一个序列图开始。我们的目标图将有三层,一层接一层:
(input: 64-dimensional vector)
↧
[Dense (32 units, relu activation)]
↧
[Dense (32 units, relu activation)]
↧
[Dense (4 units, softmax activation)]
↧
(output: logits of a probability distribution over 4 classes)
首先,让我们从创建一个输入节点开始,这样 Keras 就知道数据的形状了。
接下来,让我们开始添加第一个密集层。添加层与顺序模型相同,但有一点不同——我们定义节点,而不是使用.add()或作为顺序层的列表。每次我们添加一个新层,我们实际上是相对于前一个节点定义一个新节点,而不是相对于整个模型结构。这部分放在最后。
例如,这个密集层是一个节点,它的前一个节点被定义为变量inputs(输入节点)。
我们可以相应地定义序列中的下一个节点:
在这一点上,我们有几个相互联系的变量(节点)。为了将这个图转换成 Keras 神经网络,使用keras.Model构造函数,它需要输入和输出节点,以及一个名称标识符。
此时,调用model.summary()查看模型结构。
让我们给keras.utils.plot_model(model, “m1.png”)打电话,查看模型结构的可视化。

?与“无”相同。
太好了!是时候创建非线性拓扑了。
使用函数式 API 创建非线性图形
让我们开始构建一些非线性拓扑。在我们的神经网络中,输入将导致两个分支,这两个分支最终将连接并导致一个输出。
(input: 64-dimensional vector)
↧ ↧
[Dense (32 units, relu)] [Dense (32 units, sigmoid)]
↧ ↧
[Dense (32 units, relu)] [Dense (32 units, sigmoid)]
↧ ↧
[Concatenate]
↧
[Dense (4 units, softmax activation)]
↧
(output: logits of a probability distribution over 4 classes)
让我们首先创建一个输入节点和它分支到的两个节点。注意,强烈建议为节点指定有意义的名称,以便清晰、有序地理解图形,而不是使用常用但容易被误解的符号x = layer()(x)。
接下来,让我们定义前面定义的两个节点之后的两个节点。
当两个或更多节点被连接时,它们被端到端地放入一个大向量中,该向量被传递给下一个节点。concatenate层只是接受一个要连接的节点列表,然后将该层的结果传递给另一个outputs节点。最后,整个图形被编译成一个模型。
我们的模型和我们想象的一模一样!通过设想一个基于图形的神经网络模型,我们可以构建任何我们可以想象的网络。

是时候进一步潜水了。
探索复杂的输入/输出
让我们来探讨如何使用不同大小的不同输入和不同的输出。假设我们有三个输入——64x 64 图像、16 列数字数据和可变长度文本。我们将希望输出两个值:一个连续值(例如房价)和另一个离散值(3 类)。
让我们首先用相应的大小和名称构造三个输入。
接下来,让我们从处理图像开始。我们将使用一个标准的卷积神经网络简单结构,或者替代卷积和最大池层,然后进行扁*化,以便可以连接。
让我们用标准密集层来处理数字输入。
最后,可以用嵌入和递归层来处理文本数据。
现在,每种特定的数据类型都被简化为向量,我们可以开始连接它们了。让我们首先连接并减少文本和图像向量,因为它们特别长。我们可以使用 Dropout 来进一步提高泛化能力,减少对特定数据的过度拟合。
最后,让我们将image_text组合和数值数据连接在一起,形成两个输出。
当我们建立模型时,我们需要传入一个输入和输出列表。
结果是在几分钟内创建了一个庞大的网络:

为了训练网络,数据必须根据输入和输出的名称存在于字典中。例如, x 的训练数据将采用以下形式
x_data = {'Image': array, 'Numerical': array, 'Text': array}
y_data = {'continuous': array, 'categorical': array}
有关训练具有多个输入和输出的复杂模型的更多信息,请参考 Keras 文档此处。
进一步的想法
使用函数式 API 有很多原因,即使您没有将它用于多个输入或输出,这也是使用它的一个主要原因。
- 在您的网络中建立跳过连接,即跨层跳跃的连接。这对于函数式 API 来说非常简单。

ResNet 模型的一部分,采用跳过连接。
- 构建多个引用同一层的模型(可能是同一个嵌入空间,称为共享层)。这样可以节省计算时间,促进泛化+全局理解。
- 当需要管理多个具有相互关系的模型时,函数式 API 就派上了用场,比如编码器-解码器,神经类型转换,或者生成对抗网络。
- 使用功能 API 来访问和操作现有预训练网络的层,如 VGG16 或 AlexNet,可能是通过将模型流导向网络的中间部分。
当您使用函数式 API 时,可能性是无限的!
感谢阅读!😄
查看 AutoKeras,它是一个神经架构搜索库,可以为您自动构建架构(通常是非线性的):
[## AutoML:在不定义架构的情况下创建最高性能的神经网络
用 AutoKeras 在 Python 中编程 AutoML
towardsdatascience.com](/automl-creating-top-performing-neural-networks-without-defining-architectures-c7d3b08cddc)
见证语义分割的进展:从 V1 到 V3+的 DeepLab 系列

图片来自泰勒的博客
原载于 2020 年 7 月 3 日https://www . yanjia . Li。
序
语义分割是从图像中分割出目标对象或区域的计算机视觉任务。这在电影拍摄、增强现实等很多行业都非常有用。在深度学习时代,卷积神经网络成为最有效的语义分割方法。深度卷积神经网络不是试图通过对比度和锐度这样的视觉信号来理解物体的边界,而是将这项任务转化为一个分类问题:如果我们知道图像中每个像素的类别,我们将免费获得物体的边界。在这种假设下,我们可以只修改典型 CNN 分类网络的最终层,并让它输出 H*W 值来表示每个像素的类别,而不是只输出 1 个值来表示整个图片的类别。输出在每个像素位置都有类别 id,通常编码在类似 PNG 的掩码中。这个想法来自一篇名为《语义分割的完全卷积网络》( FCN)的论文,突然每个研究者都开始效仿。
DeepLab 系列就是这种 FCN 思想的追随者之一。从 2015 年到 2018 年,DeepLab 系列发布了四个迭代,分别叫做 V1、V2、V3 和 V3+。DeepLab V1 为本系列奠定了基础,V2、V3 和 V3+都比前一版本有所改进。这四次迭代借鉴了*年来图像分类中的创新来改进语义分割,也启发了该领域的许多其他研究工作。进入 2020 年,由于这个 DeepLab 系列网络没有更多更新,所以是时候总结一下这个见证了深度卷积神经网络在语义分割方面进展的工作了。
V1 深度实验室
基于深度卷积网和全连通条件随机场的语义图像分割
多尺度特征聚合
就像我之前说的,语义分割网络是从图像分类网络进化而来的。因此,通常有一个主干网络,它被证明在图像分类任务中是有效的。早在 2014 年,VGG 网络通过使用 3×3 卷积内核使网络更加深入,超越了 AlexNet,成为最先进的图像分类解决方案。
DeepLabV1 和 FCN 都使用 VGG-16 作为主干网络来提取特征,然后对像素进行一些细粒度的分类。遗憾的是,DeepLabV1 的作者在论文中并没有把网络是什么样子说得很清楚,所以我们要看源代码才能有个全貌。为了节省你阅读冗长的 Caffe proto txt 的时间,我在下面做了一个图表来可视化这个架构。

我自己的 DeepLabV1 图
DeepLabV1 在论文中提出了三种变体:DeepLab、DeepLab-Msc、DeepLab-Msc-LargeFOV。这是因为作者希望展示 DeepLab 网络中使用的每个组件的烧蚀情况。上图显示了 DeepLab-Msc 的变体。虽然作者声称简单是这个网络的优点之一,但最好的 DeepLab-Msc 版本(多尺度)远比 FCN 复杂。从图中可以看出,左侧的绿色块是来自 VGG 主干的卷积块。中间的黄色块是 DeepLab 的像素分类图层。右边的红色块是输出的一些后期处理。因为语义分割通常同时需要非常细粒度的细节和高级别的全局特征,所以合并来自多尺度的特征是对抗粗分类预测的非常常见的方法。DeepLab 聚合来自许多中间卷积层和输入的特征,然后将元素相加的值插值回原始分辨率作为输出掩码。通常情况下,卷积层越深,产生的特征尺度越小,所以本质上,DeepLab 是在融合多个尺度的特征。

分类标题的详细视图(之前图表中的黄色块)。自己图解。
让我们仔细看看那些处理多尺度特征的分类头。上图显示了一个分类块的内部外观。这种设计类似于 FCN,我们首先使用一个 3x3 内核来压缩特性,然后使用两个 1x1 内核来预测最终的类。辍学也是为了对抗过度适应。
然而,与 FCN 相比,使用多尺度功能并不是一个真正的大创新,DeepLab 需要一些其他东西来脱颖而出。接下来,我们来谈谈 DeepLab 试图修复的 FCN 的另一个“问题”。
更大的特征地图

第一步用于池化。自己图解。
在上面的图表中,FCN(左侧)将 VGG 的最后几层转换成一个用于分割的分类头。因此,FCN 不是在最终的全连接层中输出 1 个值和 4096 个通道,而是输出具有 4096 个通道的 7×7 掩码。然而,DeepLab(右侧)的作者认为,从原始 224x224 图像到 7x7 特征的下采样过于激进,可能导致分割质量差。因此,DeepLab 将最后一个 VGG 块的 MaxPooling 层的步幅设置为 1,这样即使对于最后几层,特征图也可以保持在 28x28。
这一变化确保我们有更大规模的要素进行预测,但也带来了另一个问题:计算现在更加昂贵,我们还丢失了一些从 7x7 要素地图中提取的全局信号。作者尝试的一种方法是减少最后 FC 层的通道数。另一种方式,我们将重点介绍,是 DeepLab 的真正创新:阿特鲁卷积。
阿特鲁卷积

来自“DeepLab:使用深度卷积网络、阿特鲁卷积和全连接 CRF 的语义图像分割”
atrous (hole)卷积的思想简单而有效:常规卷积核结合来自几个相邻像素的值来计算新的像素值,如下面的(a)所示。在语义分割中使用这种核的缺点是,当我们提取特征时,考虑更多的局部关系,而不是全局关系。为了改善这一点,DeepLab V1 借用了信号处理算法的思想,在内核窗口中引入了一个步长,如下图(b)所示。在这个 atrous 版本中,我们在采样的每个像素之间放置孔洞,以便我们可以用相同的内核大小采样更大范围的输入。例如,当 atrous rate(膨胀率)为 2,内核大小为 3x3 时,我们将跳过中间的像素,从 5x5 区域中提取 9 个像素,而不是从 3x3 区域中提取像素。
通过使用 atrous 卷积,我们的 3x3 内核不像以前那样昂贵,因为我们可以使用更少的内核来覆盖更大的区域。此外,28x28 要素地图上的 atrous 卷积可以从 7x7 要素地图上的常规卷积获得类似的全局信号。此外,如果我们增加 atrous 速率,我们可以有效地使用 3×3 内核的相同计算,但实现更大的视野(FOV)。从论文中也证明了这是有用的。
全连接通用报告格式
最后,DeepLab V1 还使用了一个名为全连接条件随机场(CRF)的模块来进一步完善分割掩模。CRF 是一种概率模型,用于在给定周围其他像素的条件输入的情况下预测值。简而言之,我们正在尝试学习概率分布,以帮助我们在给定输入像素值及其关系的情况下计算输出像素的概率。全连接 CRF 使用图像的所有像素作为条件输入。

来自“DeepLab:使用深度卷积网络、阿特鲁卷积和全连接 CRF 的语义图像分割”
这对于根据像素相关性抛光对象遮罩内部的边界特别有用。例如,如果两个像素的颜色非常不同(下图中天空为蓝色,飞机为黑色),则它们更有可能属于两个不同的类别。并且通过比较输出和地面真实值进行几轮迭代,我们得到的概率分布能够正确反映这一现象。

来自“DeepLab:使用深度卷积网络、阿特鲁卷积和全连接 CRF 的语义图像分割”
这种 CRF 在最*的分段网络中不再使用,因为 1)我们的分段网络现在变得更好了 2) CRF 本身不能与网络一起进行端到端训练,并且运行非常慢。
V2 深度实验室
DeepLab:使用深度卷积网络、阿特鲁卷积和全连接 CRF 的语义图像分割
既然你已经理解了 DeepLabV1 的工作原理,那么就很容易理解 DeepLabV2 的思想了。所有的核心思想仍然保持不变,比如 atrous 卷积和 CRF,所以我只说增量变化。
ASPP
阿特鲁空间金字塔池(ASPP)是 DeepLabV2 中引入的新模块。DeepLabV2 借用了“用于视觉识别的深度卷积网络中的空间金字塔池”的思想。即使在深度学习时代之前,空间金字塔也是计算机视觉中非常经典的算法。对于诸如 SIFT 的许多算法来说,这是实现尺度不变的重要技术。SPP network 将空间金字塔引入卷积网络,DeepLabV2 创建了该 SPP 模块的 atrous 版本。

来自“DeepLab:使用深度卷积网络、阿特鲁卷积和全连接 CRF 的语义图像分割”
通过 SPP 模块,我们的网络可以将不同比例的要素编码到固定大小的要素地图中。这有点像单词袋方法,但不是将特征装袋,而是在新的特征向量中为不同的尺度分配元素。例如,可以有 1 个特征值对应于全球范围,2 个特征值对应于大范围,3 个特征值对应于小范围。然后,您将获得一个固定大小为 6 个值的特征图。在真实 ASPP 模块中,我们有四个不同的标度,由从 6 到 24 的不同比率定义。

深海实验室 V2·ASPP。自己画图表
如上图所示,在 FCN 和 DeepLabV1 中,我们的输入固定为 224x224,以便与 FC 层的确切连接数 28x28x1024 相匹配。现在我们有了 SPP 模块,我们的网络可以处理各种大小的输入,而无需更改 FC 层。由于这种 SPP 模块使用 atrous 卷积,它还继承了大视场和小计算成本的优点。
新的多尺度结构和主干
V1 和 V2 的另一个区别是新的多尺度结构。 DeepLabV2 不是在计算特征的不同尺度上进行分类,而是在 1.0、0.75 和 0.5 倍缩小的图像上并行运行三次,以实现多尺度特征融合。

深海实验室 V2 多尺度。自己图解。
很难说这种设计是否明智,但通过运行三次相同的计算,它确实使推理和训练变得非常慢。但毫无疑问,这对作者改进 PASCAL 数据集上的度量非常有用。
为了进一步提高指标和收敛速度,DeepLabV2 也开始使用 ResNet 作为主干网络,ASPP 模块就连接在 ResNet 的 Conv5 块之后。请注意,原始 ResNet 模块的步长为 2,用于对特征进行下采样,但 DeepLabV2 将 Conv4 和 Conv5 模块的步长设置为 1。这是由于我们在 DeepLabV1 一章中讨论过的对粗糙特性的同样关注。为了增加视野,DeepLabV2 还用速率为 2 的 atrous 卷积内核替换 Conv4,用速率为 4 的 atrous 卷积内核替换 Conv5。在实践中,我发现与最初的建议相比,仅让 Conv5 使用步长 1 和 atrous 速率 2 更有效。
DeepLab V3
语义图像分割中阿特鲁卷积的再思考
升级版 ASPP
atrous 卷积和空间金字塔池的使用给 DeepLabV1 和 V2 带来了巨大的成功,所以作者在这个方向上不断探索,做出了这个 DeepLab 系列的 V3,重点是 ASPP 模块。

来自“重新思考用于语义图像分割的阿特鲁卷积”
DeepLabV3 仍然使用 ResNet 主干,如上图所示。在 V2,ASPP 将 4 个不同比例的要素合并成一个固定大小的要素。然而,复杂卷积的性质使得网络很难提取微小的局部特征,如微小的边缘,以及考虑到所有像素的非常大的全局特征。
为了将更多信息融合到一个模块中,DeepLabV3 重新设计了 ASPP 模块,使其具有单独的全局图像池通道,以包括全局特征,然后将结果特征向量与 ASPP 输入上的 1x1 卷积连接起来,以使用细粒度的细节。ASPP 模块的另一个变化是在卷积之后和 ReLU 之前使用批处理规格化。

DeepLabV3 ASPP。自己图解。
与论文中的图表不同,官方实现还增加了 50%的 ASPP 输出丢失。以及在预测最终类之前具有 10%丢失的附加 3×3 卷积。
多网格、多尺度和输出步幅
atrous ResNet 主干网络也有一些小的变化。之前,网络同一部分内的卷积层具有相同的速率,例如 Conv4 和 Conv5。DeepLabV3 引入了一个名为多重网格(MG)的新超参数来调整 atrous 速率。例如,多网格值{1,2,4}意味着我们将把同一瓶颈块中三个卷积层的 atrous 速率分别乘以 1、2 和 4。如果基本速率为 2,这将导致每层的速率分别为 2、4 和 8。根据实验,这种调整可以更好地融合残余块角度的特征。
DeepLabV3 使用多尺度的方式也不同于 V2。作者意识到了 DeepLabV2 训练的低效,将多尺度完全移到了推理时间。所以当你训练一个网络的时候,它只会使用原始的图像尺度和一个分支计算,而不是 3 个。在推断时间,网络将在按{0.5,0.75,1.0,1.25,1.5,1.75}倍缩放的输入图像上运行 6 次,然后*均出 6 个输出(MS)。为了在 PASCAL 数据集上获得更好的度量,作者还翻转了查询图像,并再次在这 6 个尺度上运行推理。这是许多人不喜欢的部分,作者显然只是为了在 benchmark 中获得更高的分数,而不是提出一个通用的解决方案,这使得整篇论文更缺乏说服力。
借助 Google 的无限计算能力,作者还对网络的不同输出步长(OS)进行了实验。OS = 8 表示网络的分辨率最多降低 8 倍,当输入为 224x224 时,这将转换为 28x28 的要素地图。控制操作系统的关键是共享剩余块。当 Conv4 和 Conv5 的跨距= 2 时,OS 为 8。如果我们将 Conv4 的步长设置为 2,我们将得到 OS = 16 和一个 14x14 的特征图。实验证明,OS 越低,结果越好,但是也需要更多的计算。
不再有慢性肾功能衰竭
随着 DeepLabV3 中使用各种技巧来改进基准测试指标,网络到了一个额外的 CRF 步骤不能再改进结果的地步。所以在 V3 及以下版本中 CRF 被移除。这个结果很好理解,疯狂的多尺度和翻转推理,从小输出步幅到大特征图,网络预测的偶然不确定性大多在集成结果中消除。
DeepLab V3+
用于语义图像分割的阿特鲁可分离卷积编解码器
2018 年,DeepLab 公布了最终版本 DeepLabV3+,作为对 V3 的小改进。
新骨干网络
DeepLabV3 最具影响力的变化是使用了新的主干网络例外。与 ResNet 相比,通过使用深度方向可分离卷积,Xception 可以在使用较少计算能力的情况下获得更好的结果。它还在 ImageNet 等基准测试中获得了许多最先进的指标。

来自“用于语义图像分割的具有阿特鲁可分离卷积的编码器-解码器”

来自“用于语义图像分割的具有阿特鲁可分离卷积的编码器-解码器”
DeepLabV3+从 MSRA 的可变形卷积网络借用了 Xception 的修改版本,还创建了一个深度方向可分离卷积的 atrous 版本,以获得像 ResNet 一样的特征分辨率和视野。如上图所示,糟糕的可分离卷积只是把深度方向的 conv 部分变成了中间有孔的扩张版本。新骨干确实很厉害。与 ResNet-101 主干相比,它将 PASCAL 上的指标提高了 2%。
编码器-解码器架构
编码解码是语义分割中非常常见的设计。FCN、U-Net、E-Net 都采用了这种思想:先用一个主干网络将图像编码成一个小的特征向量,然后用卷积转置层学习一个解码器网络来对特征进行上采样。卷积转置是卷积的逆运算,它不是将多个位置的要素压缩到一个位置,而是对一个位置的值进行上采样,然后将数据分散到更大范围的区域。解码器的目标是学习一种变换,以从编码器阶段恢复丢失的信息。然而,在 DeepLabV1 和 V2 中,作者批评了解码器的使用,因为他认为具有更高分辨率特征的 atrous 卷积可以在不损失太多信息的情况下实现类似的上采样效果。尽管 DeepLab 系列避免使用卷积转置来解码功能,但使用这种解码器设计的其他网络架构实际上因其简单的设计而被广泛采用。因此,在 V3+中,DeepLab 最终加入了 decoder,以进一步改善常见基准数据集上的指标。

来自“用于语义图像分割的具有阿特鲁可分离卷积的编码器-解码器”
上图中,编码器是原来的 DeepLabV3 网络。它由一个主干 DCNN 网络和一个 ASPP 模块组成。借助 ASPP 输出和主干网络中间输出的特性,解码器使用卷积和双线性上采样将输出分辨率逐渐提高 8 倍。请注意,TensorFlow 中的官方实现与论文略有不同。例如,在最终的 4x 上采样之前,有两个 3x3 Conv 层和一个 1x1 Conv 层。
具有讽刺意味的是,当与解码器一起使用时,V3 论文中介绍的 ASPP 模块中的图像级特征(全局池)融合实际上降低了 Cityscapes 数据集上的性能。这可能意味着 DeepLab 中使用的特征融合饱和,它们都不是真正必要的。网络架构本身可能已经过度适应 PASCAL 数据集。
其他详细信息
到目前为止,我们已经讨论了 DeepLab 网络随时间的演变。在这一部分,我想简要介绍一下网络架构之外的一些培训细节。
损失函数
在 DeepLabV1 中,损失函数是对类的简单交叉熵损失。在 V2,由于三个分支在不同规模的输入上工作,损失变成了三个交叉熵损失的总和。在 V3 和 V3+中,这被还原为简单的交叉熵,因为多尺度的思想已经被用于推理而不是训练。
学习率政策
在 DeepLabV1 中,学习率策略是从 1e-3 开始的简单阶跃衰减策略。在 DeepLabV2 中,作者发现一种新的“策略”衰变策略效果更好。学习率将是(1-iteration/max_iteration)^power,其中幂是 0.9。这种逐渐衰减的学习速率有助于网络更稳定地找到最优解。而这个策略也被 V3 和 V3+所采用。
上采样
为了计算损失,DeepLabV1 和 V2 缩小了“地面真相”的规模,使其与网络输出相同。这与 FCN 形成对比,后者对结果进行上采样以匹配原始输入分辨率。从 V3 开始,作者发现这可能会导致地面真相的一些更好的细节没有反向传播,因此地面真相在训练期间不再缩小。
结论
DeepLab 系列是迄今为止最重要的语义切分网络之一。它从 astrous 卷积开始,并逐渐增加其他网络的更多功能,如 SPP 和解码器,以使自己更加强大。所以 DeepLab 确实见证了图像分类和语义分割的进步。它总结了*年来研究人员发现的所有很酷的技术。
然而,自从 DeepLabV3 以来,它也开始使用大量技巧来改进基准测试指标,因此重现结果也变得更加困难。在这些技巧中,来自同一图像的多个尺度的集合预测以及提高特征分辨率是最有用的,因此它对于工程来说仍然是一个有用的见解。希望在下一个十年,当我们拥有更廉价的计算能力时,我们将最终理解 DeepLab 的本质,从这些技巧中剥离所有的助推效果。
参考
陈良杰,乔治·帕潘德里欧,Iasonas Kokkinos,凯文·墨菲,艾伦·l·尤耶,用深度卷积网和全连通条件随机场进行语义图像分割
陈良杰,乔治·帕潘德里欧,雅索纳·科基诺斯,凯文·墨菲,艾伦·l·尤耶, DeepLab:用深度卷积网、阿特鲁卷积和全连接条件随机场进行语义图像分割
陈良杰,乔治·帕潘德里欧,弗洛里安·施罗夫,哈特维格·亚当,重新思考阿特鲁卷积用于语义图像分割
陈良杰,朱玉坤,乔治·帕潘德里欧,弗洛里安·施若夫,哈特威格·亚当,编解码与阿特鲁可分卷积用于语义图像分割
想知道为什么在梯度下降算法中要减去梯度?
易于理解的向量微积分洞察力具有偏导数,梯度和方向导数

杰斯温·托马斯在 Unsplash 上的照片
在使用机器学习时,处理监督 ML 问题的最基本方法之一是定义一个成本函数,然后最小化成本函数以获得最佳输出。梯度下降是最广泛使用的算法之一。梯度下降算法的基本数学表示如下。

梯度下降算法的基本表示
这里,θ表示包含参数的向量,J 是成本函数。赋值运算符表明,每次迭代后,向量θ中的值都会更新。学习率可以手动设置,它定义了梯度下降算法中步长的大小。但是为什么我们减去梯度下降以达到最小损失呢?为了理解这一点,让我们来理解向量微积分的一些基本概念。
渐变
函数 f 的梯度,写为∇f,是包含 f 所有偏导数的向量,我们来看一个例子。
考虑一个函数 f(x,y) = x sin(y)。我们首先需要找出函数 f 的偏导数,要找出函数对一个变量的偏导数,就要把所有其他变量都当作常数。

函数 f(x,y) = x sin(y)的偏导数
现在,f 的梯度,用∇f 表示,就是一个向量,包含了所有偏导数的集合,也就是

函数 f(x,y) = x sin(y)的梯度
因此,概括起来,我们可以说函数 J 的梯度可以写成-

梯度的一般表示
多变量函数对特定变量的偏导数简单地告诉我们,在保持其他变量不变的情况下,如果我们非常轻微地改变特定变量,函数的输出将如何变化。
例如,我们来看一个非常简单的函数:
f (x,y) = x y。在任何一点,比如说(2,3),函数对 x 的偏导数:
∂f(x,y)/∂x = 2xy = 223 = 12。
函数 f(x,y)在点(2,3)的值(输出)为 2 *3 = 12。
现在,让我们稍微增加 x 的值,比如 0.001,同时保持 y 值不变。现在重点是(2.001,3)。此时函数的值(输出)为 2.001 *3 = 12.012003。
让我们看看当 x 改变时,输出的变化率,∂f(x,y)/∂x。
(f(2.001,3)-f(2.3))/2.001–2 = 0.012003/0.001≃12
这个值等于函数对 x 的偏导数的值,因此意味着当一个输入变量发生微小变化而其他变量保持不变时,函数的输出也会发生变化。保持 x 值不变,对 y 也可以这样做。

f(x,y) = x +y 的图
为了更好的理解,现在让我们想象函数 f(x,y) = x +y。左边给出了函数的图形。如果这个函数是我们的损失函数,我们可以看到,损失在原点附*最小,随着离原点越远,损失越大。让我们在 x-y *面上画一个梯度矢量场。给定函数的梯度是[2x,2y]。

(x-y *面上的梯度向量场|| (b)从底部到顶部的视图
我们看到向量场中的向量指向远离函数的原点。这是因为当我们取图上的任意一点(a,b),将它们投影到 x-y *面上,然后画出从原点到该点的向量(a,b ),到该点的梯度是向量[2a,2b],它指向离原点更远的地方。此外,这里需要注意的一点是,靠*原点的点的梯度矢量比远离原点的点的梯度矢量长度小。这并不奇怪,因为梯度向量是[2x,2y],所以任何远离原点的点都会比 x,y 值小的点有更大的长度。
为了更好地形象化,下面给出了图上特定点的梯度。
图形上各点的梯度向量
图中不同点处梯度向量的仰视图(它指向远离原点的方向,离原点越远,向量的长度越大)
想象你在图上的任何一点,你想往上爬。你想找到一个方向,你应该从一个随机点移动,以最快的速度增加高度。该方向由特定点的梯度向量给出。因此,坡度给了我们一个最陡的上升方向。在我们的梯度下降算法中,我们的目标是最小化损失函数值,因此在任何给定点,我们需要向函数值下降最多的方向移动,即向梯度的相反方向移动,这是最陡下降的方向。因此,我们在算法中减去梯度。
但是仍然不清楚为什么梯度是最陡上升的方向。为了理解这一点,我们需要对方向导数有一个大致的了解。
方向导数
考虑一个具有两个变量 x,y 的函数。我们之前看到,如果我们在 x 方向(保持 y 不变)或 y 方向(保持 x 不变)稍微移动,函数 f(x,y)的偏导数将告诉我们输出的变化。但是如果我们沿着不*行于任何一个轴的方向移动呢?如果我们沿着一个向量的方向移动,比如说[2,3]。即 x 方向上的 2 个单位和 y 方向上的 3 个单位。现在函数的输出会有怎样的变化?该信息由方向导数给出。
方向导数在数学上用𝑤⃗函数的梯度的点积来表示,它是我们移动方向的向量。

方向导数(将𝑤⃗视为[a,b])
以之前的函数 f(x,y) = x +y 为例,你站在一个点上,比如说(2,3)。现在你稍微向向量(3,1)的方向移动。输出会有什么变化?

现在,当我们站在(2,3)时,该点的方向导数将是:

这意味着,如果在(2,3)处,我们稍微向向量(3,1)的方向移动,输出将改变 18 倍。我们点积的原因也很明显。当我们只在 x 方向移动时,输出的变化是∂f/∂x,当我们只在 y 方向移动时,输出的变化是∂f/∂y.,现在当我们在向量𝑤 ⃗的方向移动时,比如说[a,b],我们在 x 方向移动a单位,在 y 方向移动b单位。因此,输出的变化可以用 a(∂f/∂x) + b(∂f/∂y).来表示
在任何给定的点上,我们可以向很多方向移动。假设我们希望在方向导数最大的𝑤 ⃗方向上移动,即函数的输出增加最多。这个方向也是最陡上升的方向。
因此,我们可以说我们在寻找:

这里要注意的一点是,等式中的向量长度为 1,即单位向量。这是为了消除这样一种可能性,即我们可能会选择任何其他具有大幅度指向完全不同方向的向量,仅仅因为它给出了点积的最高值。
我们知道,给定任意两个向量,点积可以写成:

两个向量的点积
这里θ是两个向量之间的角度。为了使点积最大,两个向量之间的角度应为 0。

在我们的例子中,向量的大小是 1。因此,只有矢量之间的角度有关系。我们可以看到,当我们移动的矢量𝑤 ⃗(the 方向是函数的梯度方向时,方向导数的值最高。
因此,我们可以说,函数任意点的梯度都指向最陡上升的方向,也就是说,如果我要尽可能快地爬上函数,我会选择梯度的方向。
而在梯度下降算法中,我们希望选择在函数(即损失函数)减少最大值的方向上移动,因此我们在梯度的相反方向上移动,即最陡下降的方向。因此,我们在梯度下降算法中减去梯度。
感谢您的阅读!
如果您希望阅读更多关于深度学习的核心主题,请随时关注并保持更新。
[## 深度学习主题的简单指南— 1(神经元、MacCulloch Pits 神经元、感知器)
初学者的去处,了解核心深度学习主题的概述。
故事中所有的图示和图像都是作者的。
自然语言处理中的单词和文本表示
从本地到分布式
单词是许多自然语言中的基本结构。这些语言中的字符在排列成词之前是没有意义的。当这发生的时候,维奥拉,意义出现了!当单词按顺序排列时,比如在短语或句子中,更多的意思就会出现。
不足为奇的是,核心 NLP 操作包括适当地处理单词或单词序列。(在这篇文章中,一系列的单词,我们将其缩写为 text ,将会是一个短语、一个句子、一个段落、一个部分、一个章节或者整个文档。为了符号的简单,我们也将标点符号视为单词。)
下面是支持许多用例的文本关键操作的清单。
***Text classification***: Classify text into one of a predefined set of categories. ***Text similarity***: Score how similar two pieces of text (sentences, paras, …, entire documents) are.***Text clustering***: Cluster several texts — such as documents — into groups of similar ones.***Keywords extraction***: Extract salient keywords from a corpus of text (e.g. documents).***Topics discovery***: Discover sets of keywords that go together, i.e. topics, from a corpus of text.
所有这些都涉及到文字操作。有些,比如文本相似度,涉及到对单词序列的操作。
当地文字表述
按 Id
在这种情况下,每个不同的单词被分配一个唯一的标识符。举个例子,比方说我们的词典只有 10 个单词:狗、猫、虎、象、黑、黑、白、是、大、快。下面描述了该词典的一种本地编码。
Word dog cat tiger elephant the black white is are and
Id 0 1 2 3 4 5 6 7 8 9
现在考虑正文:**狗和猫是黑色的。这将被表示为序列 4 0 9 1 8 5。
这种表示保留了文本中的所有信息。这很好。另一方面,不同长度的文本产生不同长度的序列,使得它们难以比较。
向量空间中的
在这种表示中,词典中的每个单词在向量空间中形成它自己的维度。所以在上面的例子中,我们的空间有 10 个维度。单词由二进制向量表示,其中单词的维数值为 1,其余的为 0。我们将把这个向量称为单词的指示向量。
下面是我们运行的例子中的两个单词指示向量。
*dog → **1**000000000
black → 00000**1**0000*
好了,现在重点来了。任意长度的文本也被映射为同一空间中的向量。这使得比较不同长度的文本变得容易。只需使用代数相似性度量来比较它们的向量,如余弦相似性或基于欧几里德距离的度量或其他度量。
文本向量
那么,文本,即任意长度的单词序列,是如何映射到这个空间中的向量的呢?有各种可能性。最简单的方法是将文本中出现的单词的指示向量相加,并进行二进制化(即,将正计数截断为 1)。下面是我们在例子中得到的向量。
*the dog and cat are black → 1100110010*
这个向量只是捕获词典中的哪些单词在文本中,哪些没有。
***1** **1** 0 0 **1** **1** 0 0 **1** 0
**dog** **cat** tiger elephant **the** **black** white is **are** and*
这种表示也称为单词包,忽略文本中的所有词序。有时这实际上是有益的。猫和狗是黑色的* 和狗和猫是黑色的 的矢量表示是一样的。这很好,因为猫和狗在这里出现的顺序无关紧要。*
增强版
词频
考虑长文本,比如长文档。各种单词出现的频率也很重要。例如,考虑两个 10000 字的文档,一个文档中医院被提及 200 次,一个文档中医院仅被提及一次。第一份文件中医院的出现比第二份文件更为显著。
捕获词频的文本编码很容易获得。只需将文本中出现的所有单词的指示向量相加。不要二值化就好。
TF X IDF
在长文档中,常见的词语如中的、中的、中的、和会频繁出现。基于频率的表示将过度表示它们的重要性。我们不希望仅仅因为两个文档中都出现了大量的和就认为它们是相似的。是的,频率很重要,但仅仅是信息性的词。在我们的例子中,医院是一个信息词,而医院不是。**
TF X IDF 表示首先涉及给词典中的每个单词分配一个信息量分数。这个分数是从不同的文本语料库中估计的,例如从不同类型的文档中提取的句子(或者甚至段落)。一个单词出现的文本越少,它的信息量就越大。这就是为什么这一措施被称为“逆文档频率”。
有了各种单词的 IDF 分数,很容易获得文本的 TF X IDF 向量。浏览课文中出现的所有单词。将每个出现的指示向量乘以其单词的 IDF 分数。把它们都加起来。这将产生一个向量,其中单词的贡献将取决于它在文档中的频率,该频率由单词的信息性(即 IDF)分数调整。在不同语料库中不常见但在特定文本中频繁出现的单词在该文本中将被认为更重要。
让我们看一个例子。考虑这篇课文,狗和猫是黑色的。我们前面看到了它的词频表示:1100110010。
***1** **1** 0 0 **1** **1** 0 0 **1** 0
**dog** **cat** tiger elephant **the** **black** white is **are** and*
TF X IDF 表示可能更像这样。(不要把正数看得太字面。)
***1** **1** 0 0 *0.1* **1** 0 0 *0.1* 0
**dog** **cat** tiger elephant *the* **black** white is *are* and*
**与和为的字样影响力大大降低。
局部表征无法捕捉单词相似度
本地表示无疑非常有用——实际上被广泛使用。也就是说,它们有一个限制,这是更广泛使用的障碍。他们无法解释单词的相似性。
考虑三句话:
***S1**: *He is a lawyer* **S2**: *He is a attorney* **S3**: *He is a doctor**
由于律师和律师是同义词,我们可以看到 S1 和 S2 在语义上是相同的,而 S3 与两者都没有关系。因为律师、律师和医生将是三个不同的维度,所以当地的代表无法做出这种区分。
我们可以用两种方法中的一种来缓解这种情况。
- 我们可以用同义词词典来扩充局部表示。
- 我们可以计算维度之间的相关性,并在我们的评分中考虑这些因素。
虽然基于字典的方法在实践中通常是有效的,但是它们不能推导出比字典中明确表示的关系更微妙的关系。作为一个简单例子考虑
***S5**: He teaches computer science
**S6**: He teaches data science
**S7**: He teaches art*
我们希望能够认为 S5 和 S6 在语义上比 S6 和 S7 更相似。
这个例子只是冰山一角。在外面的数百万个单词中,想想有多少单词相似关系被埋没了!现在把这个扩展到专有名词。举个例子,我们想捕捉到佳能和相机正相关。
维度的相关性分析可以缓解这些问题。从一个大的文档语料库,如维基百科,进一步细分为更小的块,如段落,我们可能能够推断出“计算机科学”和“数据科学”是正相关的,而“计算机科学”和“艺术”是不相关的——甚至可能是负相关的。
我们不会描述应用相关性分析来缓解这些问题的无数种方法。相反,我们将关注一种对单词进行替代编码的方法,称为分布式表示,它在幕后进行一种形式的相关性分析。
分布式单词表示法
一个单词的本地表示只有一个值为 1 的位,即标识该单词的位。因此,任何两个字的指示符向量只有两位不同。(参见下面的例子。)也就是说,所有的指示向量对都是等距的。
*dog **1**000000000
Elephant 000**1**000000*
相反,如果有可能将单词映射到向量,使相似的单词映射到附*的向量,那会怎么样呢?这是有可能的,尽管会涉及更多的问题。首先,我们需要一个关键概念。
上下文
一个单词的上下文是指在它附*频繁出现的单词,比如在同一个句子中。
为什么这个概念对我们有用?因为具有相似上下文表示的两个单词将是正相关的。就拿“猫”和“络腮胡”来说吧。它们将具有相似的语境表征,因为它们都将在涉及猫的语境中共同出现。另一方面,“猫”和“木星”不会出现在相同的上下文中。因此,通过比较上下文表征,我们可以推断第一对是相关的,而第二对是不相关的。
好了,现在让我们来形式化这个概念。我们将一个单词的上下文建模为词典中所有单词的概率分布。我们称之为单词的上下文向量。这是我们原始局部表示空间中的一个向量。与指示向量不同,它是分布式的。事实上,指示向量是一个特例,它的上下文是单词本身(不多也不少)。
举例
比方说我们的词典里就有四个词猫、胡须、电脑和笔记本电脑。它们的指示向量分别是 1000,0100,0010,0001。没有配对重叠。相比之下,这四个单词的可能上下文向量可能如下。
***word cat whiskers computer laptop** cat 0.7 0.3 0 0
whiskers 0.3 0.7 0 0
computer 0 0 0.7 0.3
laptop 0 0 0.3 0.7*
我们看到猫和胡须的上下文向量重叠,就像电脑和笔记本电脑的上下文向量一样。但是没有那些络腮胡子和电脑。这话有道理。
估计上下文向量
既然我们知道了单词的上下文向量是什么,那么我们如何估计它呢?拥有大量的文档会有所帮助。幸运的是,这样的语料库,例如维基百科,现在很容易获得。
我们简单地跟踪其他单词与感兴趣的单词在相同的邻*度中的共现,比如在同一个句子中。说出单词猫在 100 个句子中出现。在其中的 5 个句子中,单词狗也出现了。单词狗在猫的上下文中的概率为 5/100。
改进上下文向量
这种方法有一个问题。特别是常见的单词如会出现在大多数句子中,所以会出现在不相关单词的上下文中。例如,猫的上下文将包括猫猫,因为在大多数出现猫猫的句子中猫也会出现。**
有一个简单的方法可以缓解这种情况。我们用单词的上下文概率除以它的总概率。形式上是这样的。说上下文 - 得分 ( u , v )表示我们给 v 在 u 的上下文中的得分。至此我们已经将上下文 - 分数 ( u , v )定义为P(v|u)。而是可以定义为P(v|u)/P(v)。这里的P(v|u)是包含 u 同时也包含 v 的句子的分数, P ( v )是所有包含 v 的句子的分数。
这里有一个例子。假设和出现在 75%的句子和 80%的和出现的句子中。所以在猫的上下文中的得分是 80/75。想想的络腮胡子。假设它只出现在所有句子的 1%,但是在出现 cat 的句子中却有 25%。所以胡须在猫上下文中的得分是 25/1。
简而言之,胡须在猫的上下文中的得分远高于猫的得分。
上下文向量将是稀疏的
向量空间可能有数百万个维度。然而,上下文向量将趋于稀疏,或者可以变得稀疏而不会丢失太多信息。一个单词的上下文通常只包含词典中数百万个单词中的一小部分。
文本向量
这和以前一样。在针对各种单词的 IDF 分数进行调整之后,添加文本中单词出现的向量。
值得注意的是,根据 IDF 分数进行调整在这里比在本地演示中更重要。这是因为像 the 这样的普通单词会有它自己的(特别宽的)上下文,它的无阻尼贡献会污染文本向量。
让我们看一个例子。比方说,从一个合适的语料库中,我们获得了以下上下文表示。
****Word context** bark howl, dog, tree, wood, leash, shrub
dog bark, howl, leash, …**
注意吠的上下文抓住了它的两个词义:树 - 吠和狗 - 吠。
现在考虑课文狗的叫声。假设 IDF 将显著降低的两次出现和的一次出现对文本向量的贡献。事实上,为了消除混乱,让我们将这些影响设置为零。我们只剩下:吠犬。将这两个词的上下文向量相加,我们会得到这样的结果
****howl**, **dog**, tree, wood, **leash**, shrub**
出现在文本中两个单词的上下文中的单词以粗体突出显示。这些单词将对文本的向量有更大的贡献。
现在想象这篇文章是拉布拉多犬的叫声。假设我们的语料库足够丰富,可以捕捉到拉布拉多的上下文。这个文本的向量也将是相似的。这就让我们推断出狗的叫声和拉布拉多犬的叫声十分相似。
高级单词上下文模型
在过去的十年中,一种不同的获取单词上下文向量的方法,名为单词嵌入,已经出现并得到广泛使用。它不是通过简单的统计分析获得上下文向量,而是训练一个神经网络来学习单词嵌入。这是在具有隐藏层的架构中完成的。因此,学习迫使网络学习单词良好的潜在特征。单词被嵌入到这个特征空间中,而不是原始的向量空间中。
我们在另一篇文章中详细介绍了这种方法,https://towardsdatascience . com/machine-learned-word-embedding-638 C3 FB 5b 916,因为它的描述涉及到神经网络的细节,相当复杂。
用 Python 将 Word 文档转换成 HTML 或 Markdown
初学者使用 Python 的一个例子。

阿曼达·琼斯在 Unsplash 上的照片
这篇短文将指导您如何将?docx word 文档到简单网页文档()。html )或 Markdown 文档()。md )在一些基于 Python 的 CLI 的帮助下— Mammoth 。
根据 Statista 调查(2020 年 1 月 6 日)的统计,微软办公套件是最受欢迎的办公软件。你可以很容易地记笔记、短报告、教程文档等。用微软的 Word。并且,您可能希望将文档内容作为 web 文档与您的一些朋友、同事、客户共享()。html ))或 Markdown 文档()。md )。在过去,在网上托管一些 web 文档可能成本很高,但现在云服务对于一个公共文档来说非常便宜甚至免费(例如 GitHub Pages )。
让我们开始吧。
安装猛犸
要安装 Mammoth,请确保您的 PC 上安装了 Python 和 PIP。然后,您可以打开 CMD 或终端并使用以下命令:
$ **pip install mammoth**
将 Docx 转换为 HTML
使用 CLI:
$ **mammoth input_name.docx output_name.html**
使用 Python:
转换。docx 到。用 Python 制作的 HTML&猛犸(作者)
将 Docx 转换为 MD
使用 CLI:
$ **mammoth .\sample.docx output.md --output-format=markdown**
使用 Python:
转换。docx 到。md 与巨蟒&猛犸象(作者)
差不多就是这样!本文展示了一个使用 Python 从 Docx 转换到 web 文档的示例。我希望你喜欢这篇文章,并发现它对你的日常工作或项目有用。如果您有任何问题或意见,请随时给我留言。
关于我&查看我所有的博客内容:链接
安全健康****吗!💪
感谢您的阅读。📚
自然语言处理中的单词嵌入:一键编码和跳格神经网络
我是一名诗人出身的程序员,刚刚开始了解自然语言处理的奇妙世界。在这篇文章中,我将分享我对单词嵌入的理解,重点是两种嵌入方法:一键编码和跳过 gram 神经网络模型。
去年,OpenAI 发布了一个(受限)版本的 GPT-2,这是一个生成文本的人工智能系统。当用户输入一个提示——一个单词、一个句子甚至一个段落——系统会“预测”并产生下一个单词。GPT-2 的惊人之处在于,由此产生的段落,往往很容易被认为是一个(相当散漫的)人写的文本。

不难找到输出随机单词的诗歌生成器,不管是否考虑句法逻辑。我们可以编写一个相当简单的算法,从名词池中选择一个随机单词,然后从动词池中选择一个随机单词,依此类推。(有人说诗歌生成相对“容易”是因为体裁固有的文学许可。)
但是,机器如何生成连贯的句子,这些句子似乎像人类一样了解周围的上下文?
输入自然语言处理(NLP)。NLP 是人工智能领域的一个分支,旨在理解日常(自然)人类语言。NLP 的众多应用已经存在很长时间了,从文本自动完成和聊天机器人到语音助手和现场音乐推荐。
由于 NLP 模型比以往任何时候都显示出了最先进的性能,所以仔细研究一下 NLP 中最常用的方法之一:单词嵌入可能是值得的。
什么是单词嵌入?
就其核心而言,单词嵌入是一种将文本转化为数字的方法。我们这样做是因为机器学习算法只能理解数字,不能理解明文。
为了让计算机能够阅读文本,它们必须被编码为数值的连续向量。你可能熟悉欧几里得几何中的矢量概念,它表示具有大小和方向的物体。在计算机科学中,向量意味着一维数组。
(注意:数组是一种用方括号([])表示的数据结构。一维数组的一个例子可能是类似于[.44,. 26,. 07,-0 . 89,-0 . 15]的东西。二维嵌套数组可能看起来像[0,1,[2,3],4]。数组保存存储在连续集合中的值。)
好吧,那么我们如何嵌入——或矢量化——单词呢?最简单的方法称为一热编码,也称为“1-of-N”编码(意味着向量由一个 1 和多个 0 组成)。
一种方法:一键编码
我们来看看下面这句话:“我吃了一个苹果,弹了钢琴。”我们可以从索引每个单词在给定词汇集中的位置开始。

每个单词在词汇表中的位置
单词“I”位于位置 1,因此它的独热向量表示为[1,0,0,0,0,0,0,0]。类似地,单词“ate”位于位置 2,因此它的独热向量应该是[0,1,0,0,0,0,0,0,0]。源词汇表中单词的数量表示维度的数量——这里我们有八个维度。
示例文本的一键嵌入矩阵如下所示:

词汇表中每个单词的单键向量表示
一键编码的问题
这种单词嵌入方法有两个主要问题。
第一个问题是维数灾难,指的是高维数据中出现的各种问题。即使只有相对较小的 8 维,我们的示例文本也需要指数级的大内存空间。矩阵的大部分被零占据,所以有用的数据变得稀疏。假设我们有 50,000 个词汇。(英语中大约有一百万个单词。)每个单词用 49999 个 0 和一个 1 来表示,我们需要 50000 的*方= 25 亿个单位的内存空间。计算效率不高。
第二个问题是很难提取意思。每个单词都是独立嵌入的,每个单词都包含一个 1 和 N 个 0,其中 N 是维数。由此产生的向量组并没有透露太多关于彼此的信息。如果我们的词汇中有“橘子”、“香蕉”和“西瓜”,我们可以看到这些词之间的相似性,例如它们是水果的类型,或者它们通常跟随动词“吃”的某种形式我们可以很容易地形成一个思维地图或聚类,其中这些单词彼此靠*。但是对于一个热点向量,所有的单词都是等距的。
另一种方法:Skip-Gram 神经网络
解决上述两个问题有不同的方法,但是在本文中,我们将着眼于 skip-gram 神经网络模型。“Skip-gram”是关于猜测——给定一个输入单词——它的上下文单词(出现在附*的单词),而“神经网络”是关于节点和这些节点之间的权重(连接强度)的网络。
这种单词嵌入模型有助于降低维度,并保留上下文相似性的信息,我们稍后将回到这一点。由托马斯·米科洛夫开发的著名的 Word2vec 使用了这种模型(以及另一种称为连续单词包或 CBOW 模型的模型,它通过根据上下文单词猜测单词来实现与 skip-gram 相反的功能)。

Word2vec 的两个主要模型(来源:“向量空间中单词表示的高效估计”)
为了说明跳格法,让我们回到我们的示例文本,“我吃了一个苹果,弹了钢琴。”我们将使用单词“ate”作为模型的输入。同样,skip-gram 是关于预测出现在输入单词附*的上下文单词。假设我们将窗口大小定义为两个单词(窗口大小可以变化),并查看输入单词之前的两个单词和之后的两个单词。然后我们会看到“我”、“安”和“苹果”。从这里开始,我们的输入-输出对的训练样本将是(“ate”、“I”)、(“ate”、“an”)和(“ate”、“apple”)。请参见下图以供参考:

(来源:http://mccormickml . com/2016/04/19/word 2 vec-tutorial-the-skip-gram-model/)
假设我们从一个模型开始,这个模型还没有经过训练,因此不知道每个单词的上下文单词。当我们获取第一对训练样本(“ate”、“I”),并将“ate”输入到我们的模型中,期望看到“I”时,我们的模型可能会随机吐出“played”。更准确地说,实际发生的情况是,模型输出一个预测向量,其中包含词汇表中每个单词出现在“ate”附*的概率,然后选择概率最高的单词(在本例中为“played”)。在早期训练阶段,这几乎是一个随机的预测。
我们如何调整预测以使其更加准确?该模型认为单词“played”可能会出现在单词“ate”附*,这一事实说明了“played”和“ate”之间的关系——它们之间目前有相对较强的联系或权重,尽管对它们的实际关系不准确。我们怎么知道这种联系应该更弱呢?因为我们的样本对只有上下文词“我”、“安”或“苹果”,而没有“玩过”。
请注意,我们的目标输出应该是这样的(“ate”、“I”):在“I”处的概率为 1,在每隔一个单词处的概率为 0,因此[1,0,0,0,0,0,0,0]。我们通过比较实际输出(其中“played”具有最接* 1 的值)和预期目标输出(其中“I”具有最接* 1 的值)来获得误差值的向量,并在反向传播中使用这些值来调整“ate”和其余部分之间的权重。
简单地说,“我”应该以 100%的概率出现在“ate”附*,但它只以(例如)10%的概率出现,所以我们使用 90%的差异重构“ate-to-I”权重,使其更接* 100%。正如你可能已经猜到的,由于有两个其他单词(“an”和“apple”)也出现在“ate”附*,将这些对添加到计算中会将“I”出现在“ate”附*的权重降低到更接* 33%。
训练结束时得到的权重就是我们正在寻找的单词嵌入——我们希望获得预测关系的工具,而不一定是每个单独输入返回的内容。

**隐层权重是我们通过 skip-gram 训练得到的单词嵌入(来源:http://mccormickml . com/2016/04/19/word 2 vec-tutorial-the-skip-gram-model/)
其实这就是神经网络训练的基本思路。这与我们通过联想来记忆事物的方式类似,因为某些联想会随着重复共现而变得更强(例如“New”和“York”),而其他联想会随着它们越来越少地一起使用而变得更弱。我们大脑中的神经元不断调整它们之间的权重,以更好地代表现实。
我们现在已经看到了跳格神经网络模型是如何工作的。如前所述,这比一热嵌入有两个优点:降维和上下文相似性。
降维
在不深入细节的情况下,我想让我们的注意力回到上图,特别是中间的隐藏层。为了给你一个上下文,源文章使用了 10,000 个单词的词汇。您将看到隐藏层显示 300 个神经元,这意味着它的嵌入大小为 300,而嵌入大小为 10,000。
还记得 one-hot 编码如何创建由与训练集中单词总数一样多的值组成的向量,其中大部分用零填充吗?代替这样做的是,skip-gram 神经网络模型选择更少数量的特征或神经元,这些特征或神经元表示关于该单词的更有用的东西。把特征想象成性格特征——我们是由多种特征组成的(例如,内向和外向),这些特征在一个谱上有一个值。我们只保留最能描述我们的东西。
顺便说一句,我怎么强调我有多爱这句来自 Pathmind 的话都不为过:
“就像梵高画的向日葵是 19 世纪 80 年代末在巴黎的一个三维空间中代表植物物质的帆布上的二维混合物,所以排列在一个矢量中的 500 个数字可以代表一个单词或一组单词。这些数字将每个单词定位为 500 维向量空间中的一个点。”

上下文相似度
skip-gram 模型允许您根据每个单词的邻*程度来保存它们的上下文信息。在“我吃了一个苹果”的例子中,“吃”是“苹果”的上下文词。上下文允许基于单词的句法和/或语义相似性对单词进行分组。如果给我们额外的输入“我吃了一个香蕉”和“我吃了一个橘子”,我们很快就会发现“吃”也是“香蕉”和“橘子”的上下文词,因此可以推断“苹果”、“香蕉”和“橘子”一定有一些共性。
由于“苹果”、“香蕉”和“橙子”具有相似的上下文,因此它们的向量被调整为彼此更接*,从而在某个多维几何空间上形成聚类。关于这一点,语言学家 J.R. Firth 说,“你可以通过一个词的朋友来了解它。”
(警告:上下文并不总是与最接*的单词最相关;想一个名词或动词短语,中间用很多杂词隔开。这只是语言建模的一种方式。)
总结
在本文中,我们讨论了我对 NLP 中单词嵌入概念的理解,以及两种常见的嵌入方法,一键编码和 skip-gram。
我还简要介绍了特定单词嵌入方法相对于另一种方法的优势,即降维和上下文相似性。
展望未来,我想更好地理解语言建模中的变形金刚和最初促使我写这篇文章的 GPT 2(以及最*发布的 GPT 3)的工作方式。
结束语
机器可以编写文本,尤其是越来越有意义的文本,这个想法仍然让我敬畏。为了更全面地了解不同的 NLP 模型是如何工作的,我还有很长的路要走,而且一路上可能会有(许多)误解。我将非常感谢任何关于我如何成为一个更好的学习者和作家的反馈。
对于那些不熟悉这个话题的人来说,我希望这篇文章能引发你对 NLP 领域的兴趣,就像下面的文章引发我的兴趣一样。
来源&演职员表:
- Word2vec 和神经单词嵌入的精美初学者指南:【https://pathmind.com/wiki/word2vec】T4
- 一篇用我最容易理解的方式来解释 Word2vec 的文章:http://jalammar.github.io/illustrated-word2vec/
- 另一个关于 skip-gram 的伟大作品,我在这里借用了他的图表:http://mccormickml . com/2016/04/19/word 2 vec-tutorial-the-skip-gram-model/
- 要了解更多关于单词嵌入的信息,也可以查看以下文章:https://medium . com/deep-learning/glossary-of-deep-learning-word-embedding-f 90 C3 CEC 34 ca
- 和https://blog . acolyer . org/2016/04/21/the-amazing-power-of-word-vectors/
- 了解如何训练神经网络的更多信息:https://becoming human . ai/making-a-simple-neural-network-2 ea 1 de 81 EC 20
- 如果你对 GPT 2 号感兴趣,你可能会发现这个插图很有帮助:http://jalammar.github.io/illustrated-gpt2/
TensorFlow 的单词嵌入和嵌入投影
理论解释和实例。

罗斯·乔伊纳在 Unsplash 上的照片
单词嵌入是一种在词汇表中表示单词(即记号)的技术。它被认为是自然语言处理中最有用和最重要的概念之一。
在这篇文章中,我将介绍单词嵌入的概念以及它在 NLP 中的作用。然后,我们将通过一个实例来理解使用 TensorFlow 的嵌入式投影仪的概念。
单词嵌入是指在 n 维向量空间中用向量表示一个单词。考虑一个包含 10000 个单词的词汇表。在传统的数字编码中,单词用从 1 到 10000 的数字来表示。这种方法的缺点是,我们无法捕捉到任何关于单词含义的信息,因为数字是在不考虑单词含义的情况下分配给单词的。
如果我们使用 16 维的单词嵌入,那么每个单词都用一个 16 维的向量表示。单词嵌入的主要优点是共享相似上下文的单词可以在向量空间中彼此接*地表示。因此,向量承载了一个词的语义。假设我们正在尝试对客户评论进行情感分析。如果我们使用单词嵌入来表示评论中的单词,与积极意义相关的单词会指出一种特定的方式。同样,具有消极意义的单词很可能与具有积极意义的单词指向不同的方向。
一个非常著名的带有单词嵌入思想的类比是国王-王后的例子。它基于“国王”、“王后”、“男人”和“女人”这些词的矢量表示。如果我们从国王中减去男人,然后加上女人,我们将得到一个非常接*女王的向量:

有不同的方法来衡量向量的相似性。最常见的方法之一是余弦相似度,它是两个向量之间角度的余弦。与欧几里德距离不同,余弦相似性在测量相似性时不考虑向量的大小。因此,余弦相似性关注向量的方向,而不是长度。
想想“令人兴奋”、“无聊”、“激动人心”和“无聊”这些词。在二维向量空间中,这些单词的向量可能看起来像:

二维空间中的单词嵌入
随着向量之间的角度减小,角度的余弦增加,因此余弦相似性增加。如果两个向量位于同一方向(它们之间的角度为零),余弦相似度为 1。另一方面,如果两个向量指向相反的方向(它们之间的角度为 180°),余弦相似度为-1。
当我们使用单词嵌入时,该模型了解到激动人心和激动人心比激动人心和无聊更有可能共享相同的上下文。如果我们用整数来表示单词,模型就不知道这些单词的上下文。
有不同的方法来创建单词嵌入,如 Word2Vec、GloVe 或神经网络的嵌入层。单词嵌入的另一个优点是,我们可以在模型中使用预先训练的嵌入。例如,Word2Vec 和 GloVe 嵌入对公众开放,可以用于自然语言处理任务。我们也可以选择使用神经网络中的嵌入层来训练我们自己的嵌入。例如,我们可以在 Keras 的序列模型中添加嵌入层。请注意,需要大量数据来训练嵌入层,以实现高性能。
我们用 4 个单词举的例子非常简单,但是抓住了单词嵌入背后的思想和动机。为了可视化和检查更复杂的例子,我们可以使用 TensorFlow 的嵌入式投影仪。
嵌入投影仪
嵌入投影仪是理解单词嵌入的神奇工具。它允许你加载你自己的嵌入和可视化,以及分析一些预先训练的模型。

我选择了一个预先训练好的嵌入,这是 Word2Vec 10K,但你可以使用 load 选项随意上传你自己的嵌入。TensorFlow 有一个关于单词嵌入的信息丰富的教程也解释了如何将数据加载到嵌入投影仪。
让我们回到 Word2Vec 10K 的例子,它包括 200 个维度中的 10000 个点。

这张图片说明不了什么,但是你可以移动它来看不同的单词。上图中的每个点代表一个单词。如果你点击一个点,它会显示这个单词和附*的单词。例如,我点击了代表单词“own”的点:

投影仪显示“own”在文本和包含最*点的列表中出现的次数。与“own”最接*的点是“their ”,这是有意义的,因为这些词很可能像在“their own”中一样一起出现。
在我们选择了一个特定的单词后,我们可以分离出一定数量的相关单词。

我选取了“爷爷”这个词,隔离出 101 个词。它返回了一个不太密集的视图,这使得可视化分析嵌入更容易。
词嵌入被认为是自然语言处理中最重要的思想之一,它使自然语言处理的研究者和实践者在完成复杂的任务上迈出了一大步。
感谢您的阅读。如果您有任何反馈,请告诉我。
从零开始理解单词嵌入| LSTM 模型|
直观理解单词嵌入的最终目的…最后

乔恩·泰森在 Unsplash 上的照片
哟读者!我是马尼克。怎么了?。
希望你做得很好,并为你的目标努力工作。如果没有,也不迟。现在就开始,就在此时此刻。
有了这些信息,你将对深度神经网络的序列和文本处理有一个清晰的解释,包括:
- 什么是一键编码?
- 使用 keras 的 OneHot 编码。
- 什么是单词嵌入以及它们相对于一键编码的优势?
- 单词嵌入想说什么?
- 使用 LSTM 和 GRU 图层在 keras 中将原始文本转换为单词嵌入的完整示例。
如果你想了解 LSTMs,你可以去这里
LSTMs 是一种特殊的 rnn,具有处理长期依赖关系的能力。他们还提供解决方案…
binarykeys.in](https://binarykeys.in/2020/07/lstm-cell-architecture-scratch-code/)
让我们开始吧。
“你的祖先和我的祖先曾追逐乳齿象或野猪,就像奥林匹克短跑运动员一样,手里拿着长矛,用树叶和虎皮盖住自己,作为他们的早餐”——历史
上面的句子是文本形式的,为了让神经网络理解和吸收它,我们需要将其转换成某种数字形式。两种方法是一种是热编码另一种是字嵌入。
一个热点
这是一种用 0 和 1 的数组来表示每个单词的方法。在数组中,只有一个索引有“1 ”,其余的都是 0。
示例:下面的向量只代表一个单词,在一个有 6 个唯一单词的句子中。

作者图片
与 numpy 一丨夜丨情
让我们找出句子中所有独特的单词。
array(['Yours', 'a', 'after', 'an', 'ancestors', 'and', 'boar,',
'breakfast', 'covering', 'for', 'had', 'hand', 'in','leaves',
'like', 'mastodons', 'mine', 'olympic', 'or', 'run', 'skin,',
'spear', 'sprinter,', 'their', 'themselves', 'tiger', 'wild',
'with'], dtype='<U10')shape: (28,)
现在,给他们每个人一个索引,即创建一个 word_index,其中每个单词在字典中都有一个索引。
您可能已经在上面的代码中观察到,0 没有分配给任何单词。这是 Keras 中的一个保留索引(我们稍后会讲到)。
{'Yours': 1, 'a': 2, 'after': 3, 'an': 4, 'ancestors': 5, 'and': 6, 'boar,': 7, 'breakfast': 8, 'covering': 9, 'for': 10, 'had': 11, 'hand': 12, 'in': 13, 'leaves': 14, 'like': 15, 'mastodons': 16, 'mine': 17, 'olympic': 18, 'or': 19, 'run': 20, 'skin,': 21, 'spear': 22, 'sprinter,': 23, 'their': 24, 'themselves': 25, 'tiger': 26, 'wild': 27, 'with': 28}
现在,让我们为它们创建一个一次性编码。
示例输出:这就是“你的”是如何表示的。
Yours [0\. 1\. 0\. 0\. 0\. 0\. 0\. 0\. 0\. 0\. 0\. 0\. 0\. 0\. 0\. 0\. 0\. 0\. 0\. 0\. 0\. 0\. 0\. 0\. 0\. 0\. 0\. 0.]
单热 keras 示例
text_to_matrix 是用于返回独热编码的方法。
按作者
你可以看到,为了表示一个单词,我们实际上浪费了大量的内存来设置 0(稀疏矩阵)。这些一次性编码也不能反映相似单词之间的任何关系。它们只是一些带有“1”的单词的表示。在一键编码中,两个相似的词(如“准确的”和“精确的”)可能位于非常不同的位置。
如果我们可以用更少的空间来表示一个单词,并且让它的表示有一个意义,这样我们就可以学到一些东西。
单词嵌入

单词向量示例
- 单词嵌入也表示数组中的单词,不是以 0 和 1 的形式,而是连续的向量。
- 它们可以在几个维度上表示任何单词,主要基于我们文本中独特单词的数量。
- 它们是密集的低维向量
- 不是硬编码的,而是通过数据“学习”的。
单词嵌入想说什么?
- 词与词之间的几何关系嵌入词中可以表示词与词之间的语义关系。与彼此远离的单词相比,彼此靠*的单词具有更强的关联性。
- 向量/单词彼此更接*意味着它们之间的余弦距离或几何距离与其他相比更小。
- 可以有向量“男性对女性”,它表示单词和它的阴性之间的关系。这个向量可以帮助我们预测句子中使用“he”时的“king”和使用“Queen”时的“Queen”。
单词嵌入是什么样子的?
下面是单行嵌入矩阵,表示来自具有 100K 个唯一单词的文本的 100 维中的单词'和'。

嵌入矩阵中的一行,表示 100 维中的一个单词
这种矩阵是从数据中学习来的,并且可以在 100、200、1000 或更多维中表示具有数百万字的任何文本(如果使用一键编码,则同样需要 1 毫米的维度)。
让我们看看如何用递归神经网络在 keras 中创建我们的文本嵌入。
将原始数据转换为嵌入数据的步骤:

流动
- 在数组中加载文本数据。
- 处理数据。
- 使用标记器将文本转换为序列,并用 keras . preprocessing . text . pad _ sequences 方法填充它们。
- 使用尺寸(最大字数,表示尺寸,输入尺寸)的嵌入层初始化模型
- max_words :您的数据中唯一单词的个数
- 表示 _ 维度:表示你想要表示一个词的维度个数。通常,它是(唯一的 words)^(1/4)的数字
- input_size:填充序列的大小( maxlen )
5 .运行模型
让我们按照上面的步骤来处理 IMDB 原始数据。下面所有的代码都在我的 Kaggle 笔记本里。
第一步。必要的进口
必要的进口
第二步。加载文本数据。
用熊猫加载文本数据。
读取数据
第三步:处理数据。
给正面影评打 1 分,给负面影评打 0 分。
过程数据
步骤 4:创建和填充序列。
创建 keras 的 Tokenizer 类的一个实例,并将序列填充到“ maxlen ”。
定序
第五步。初始化我们的模型
一种以嵌入为第一层的简单递归神经网络。
典型递归模型
第六步:运行模型!
运行这该死的东西
输出
与 GRU:


GRU 培训和验证
与 LSTM:


LSTM 培训和验证
以上所有代码都呈现在这里。
如果这篇文章对你有所帮助,请与他人分享你的知识!
感谢你来到这里!太好了!
日本横河电机公司(Yokogawa Electric Corp .)决定投资一只基金中的基金,支持印度初创企业 Hello Stardust!今天…
binarykeys.in](https://binarykeys.in/) [## 马尼克索尼- SDE -亚马逊| LinkedIn
查看 Manik Soni 在全球最大的职业社区 LinkedIn 上的个人资料。Manik 有 8 个工作列在他们的…
www.linkedin.com](https://www.linkedin.com/in/maniksoni) 
单词嵌入深度探究—实践方法
简要介绍单词嵌入,并深入研究使用 Tensorflow 的实现细节

罗曼·维涅斯在 Unsplash 上的照片
介绍
我敢肯定,随着你在自然语言处理旅程中的进步,你们中的大多数人迟早会被“单词嵌入”这个术语绊倒。单词嵌入已经成为当今最先进的语言模型的最重要的构件之一。至关重要的是,我们要理解它们代表什么,它们是如何计算的,以及它们的区别。因此,让我们从理解它的真正含义开始,并规划它在 NLP 社区中的广泛使用和流行背后的特征和特性。从单词嵌入的基本基础开始,随着文章的深入,我们将逐步探索其深度。本文分享的完整代码可在 Github 上获得。
单词嵌入
让我们来看看维基百科对单词嵌入是怎么说的—
换句话说,单词嵌入是单词的矢量化、固定长度、分布式、密集表示,通过将单词映射到实值向量来解释单词的文本含义。我知道一下子很难接受。我们将把定义分成几部分,一次只关注一部分。
单词嵌入是固定长度的向量——这意味着我们词汇表中的所有单词都将由我们决定的固定预定义大小的向量(实数)来表示。大小为 3 的单词嵌入看起来像:“cold”—[0.2,0.5,0.08],“house”—[0.05,0.1,0.8]
分布式表示 —单词嵌入基于分布式假设,该假设认为出现在相似上下文中的单词往往具有相似的含义。分布式表示试图通过考虑一个单词的同伴(上下文单词)来理解这个单词的意思。
密集表示——这是单词嵌入最显著的特征之一,让它们如此受欢迎。传统上,一种热编码被用于将单词映射成数值。一个热门的编码是将词汇转换成二进制向量的过程。如果词汇大小为 5,单词为{猫,食物,房子,牛奶,水},猫将被编码为二进制向量[1,0,0,0,0],牛奶将为[0,0,0,1,0]等等。
您可能已经注意到了,我们只使用整个向量的单词 index 设置了一个元素。随着词汇量的增加,我们最终会使用一个长度很长的稀疏向量来编码一个单词,这将导致性能和存储方面的损失,因为维数灾难。除此之外,这种表示不能学习单词之间的语义关系,而这在处理文本数据时是至关重要的。
需要单词嵌入
为了克服一键编码的局限性,传统的信息检索方法也被尝试和实现,希望能够对抗维数灾难——TF-IDF、潜在语义分析等。TF-IDF 和 LSA 都使用以文档为中心的方法,使它们局限于 NLP 问题的一个子类。这些技术仍然不能有效地捕捉单词在密集表示中的意思。
单词嵌入消除了所有上述缺点,并为我们提供了丰富而强大的表示,能够捕捉单词之间的上下文和语义相似性。
现在,我们已经了解了单词嵌入的想法和动机,我们将尝试考虑学习单词嵌入的最重要和最广泛使用的算法之一— Word2Vec 。我们将详细介绍它,并定义它的显著特性和特征,然后使用 tensorflow 进行彻底的实现。
Word2Vec
Word2Vec 是一种基于预测的算法,用于生成单词嵌入,最初由 Mikolov 等人在 Google 上提出。为了更深入地理解相关概念,我建议您阅读他们的研究论文— 矢量空间中单词表示的有效估计
它提出了两种新的用于学习单词的分布式和密集表示的体系结构:
- 连续词袋模型
- 跳过 Gram 模型
CBOW 模型
在 CBOW 模型中,我们试图从上下文窗口(其大小是可配置的,但通常设置为 5)内位于目标单词任一侧的上下文单词(周围单词)来预测目标单词(中间单词)的分布式表示。例如, " 用五打酒壶装我的箱子 " ,大小为 2 的上下文窗口会有以下几对——(context _ window,target _ word)——([五个,酒类],([打,酒壶],酒类)等等。

CBOW 架构
跳过 Gram 模型
Skip-gram 模型类似于 CBOW 模型,但是它不是在给定上下文的情况下预测当前单词,而是尝试从当前单词预测上下文单词。例如, " 用五打酒壶装满我的箱子 " ,大小为 2 的上下文窗口将具有以下对— (current_word,context_window) —(一打,[five,liquor]),(liquor,[一打,jugs])等等。

跳格结构
Skip Gram 与 CBOW——哪个更好?
Skip-gram:适用于少量的训练数据,甚至可以很好地表示罕见的单词或短语。CBOW:训练速度比 skip-gram 快几倍,对常用词的准确率略高
CBOW 学会在上下文中找出概率最大的单词。上下文单词被*均并被馈送给网络以预测最可能的单词。例如,“猫吃了老鼠”——网络将试图从“猫吃了老鼠”的*均输入中预测“吃了”。在此上下文和其他相关上下文中,随着时间的推移,模型会自动*滑以预测诸如 ate 之类的频繁出现的单词,并且会对“gobbled”给予更少的关注,因为它很少出现。由于这个原因,它们的分布式表示的质量受到影响。
另一方面,Skip gram 学习使用目标单词来预测上下文单词。不是对输入的上下文单词进行*均,而是每一对都可以单独用于输入模型来预测该对中的另一个单词。从“吃”中预测“该”,从“老鼠”中预测“吃”,等等。这种训练行为不会加强“吃”和“吃”之间的竞争,因为两者都将在它们各自的上下文中用于预测上下文单词。关于这个话题的详细讨论可以在这里找到。
我们将使用 Skip-gram 架构结合负采样来实现 word2vec 算法(将在本文后面解释)。所以,让我们直接开始实施吧!
负采样跳跃图
出于本文的目的,我们将使用文本 8 数据集。 Text8 是维基百科中第一个 100,000,000 字节的纯文本。它主要用于测试目的。让我们从加载数据开始:
预处理数据
停用词移除 —我们从移除停用词开始,因为它们对我们学习单词嵌入的任务几乎没有价值。
二次抽样单词 —在一个大型语料库中,最常见的单词很容易出现数亿次,而且这些单词通常不会给表格带来太多信息。降低它们的频率以减轻它所带来的负面影响是至关重要的。例如,“英语”和“西班牙语”的共现比“英语”和“the”或“西班牙语”和“of”的共现更有益。为了*衡生僻词和常用词之间的不*衡。al 想出了下面的启发式公式来确定丢弃特定单词的概率:

其中 t 是阈值(试探性地设置为 1e-5 ), f(w)是单词的频率。
过滤单词 —单词的频率告诉我们很多关于它们对我们的模型的重要性和意义。只出现一次的单词实际上无法正确表示,因为缺少与之相关的上下文单词。为了从我们的数据中排除这样的噪音(单词)(因为我们没有太多关于它们的位置的信息),我们在我们的数据中保留至少出现五次的单词。
使用 skipgrams 准备张量流数据集
生成 skipgrams — 首先,我们对预处理后的文本数据进行记号化,并将其转换为相应的矢量化记号。之后,我们利用 Keras 提供的 skipgrams 库来生成(单词,上下文)对。正如它的描述所说:
生成 skip-gram 单词对。它将单词索引序列(整数列表)转换成单词元组,其形式为:
- (word,word 在同一个窗口),标签为 1(阳性样本)。
- (词,从词汇中随机抽取的词),标签为 0(负样本)。
阅读 Mikolov 等人的这篇精辟论文中关于 Skip-gram 的更多内容: 在向量空间中对单词表示的有效估计
负采样— 对于我们提供给网络的每个输入,我们使用 softmax 层的输出来训练它。这意味着对于每个输入,我们对数百万个权重进行非常小的改变,即使我们只有一个真实的例子。这使得训练网络非常低效和不可行。预测上下文单词的问题可以被构造为一组独立的二元分类任务。那么目标是独立地预测上下文单词的存在(或不存在)。下面的代码片段生成(目标,上下文)单词对,也称为 skipgrams,对于每个输入(目标,上下文)对,我们还随机抽取一个负(目标,~上下文)对。如需进一步阅读,请参考 Mikolov 等人的论文。铝
构建模型
现在让我们通过使用模型子类化方法来构建模型。在大多数情况下,顺序 API 和函数 API 更合适,但是如果你是一个面向对象的开发者,你仍然可以使用模型子类。
对于每个训练输入,我们有一对词(目标词、上下文词),我们将其输入到模型中,并作为二进制值标签(0 或 1)输出,以指示输入元组是负样本还是真样本。两个输入字都被馈送到嵌入层,以生成大小等于嵌入维度的编码表示。这里要注意的关键点是,我们在两个输入之间共享了嵌入层。
这些密集编码的向量然后被逐元素相乘以构建合并的表示。其在最终尝试预测输入样本的正或负性质之前依次通过密集层和漏失层。

模型架构(图片由作者提供)
培训和结果
有了现在创建的模型,我们就可以直接进入培训了。model fit()方法通常可以满足训练的要求,但是自定义训练可以让您更好地控制优化和与训练相关的其他任务。你可以根据训练的复杂程度选择任何人。这里,我们采用了一种定制的方法来训练模型。
Starting epoch: 0
Training acc over epoch: 0.6382
Validation acc over epoch: 0.7458
Time taken: 374.43sStarting epoch: 1
Training acc over epoch: 0.8682
Validation acc over epoch: 0.8237
Time taken: 368.18sStarting epoch: 2
Training acc over epoch: 0.9438
Validation acc over epoch: 0.8494
Time taken: 374.33sStarting epoch: 3
Training acc over epoch: 0.9701
Validation acc over epoch: 0.8604
Time taken: 382.57sStarting epoch: 4
Training acc over epoch: 0.9800
Validation acc over epoch: 0.8656
Time taken: 376.69s
单词嵌入投影仪
对于可视化单词嵌入,tensorflow 提供了一个出色的*台,只需几行代码就可以加载和可视化保存的权重向量!我们是这样做的。首先,提取并存储嵌入层的权重。然后在两个文件中填充单词 embeddings,如下所示:vecs.tsv 存储实际的向量,meta.tsv 包含用于可视化的相关元数据。
之后,跳转到http://projector.tensorflow.org/并加载前一步创建的文件。就是这样!Tensorflow 会处理剩下的事情。让我们来看看经过 5 个时期的训练后我们在上面学到的单词嵌入。

最接*气候的词(图片由作者提供)

离议会最*的词(图片由作者提供)

分子的最*单词(图片由作者提供)
测试集的结果和准确性非常重要,考虑到模型在半小时内完成训练,而前 500 万字节没有任何 GPU 支持,这是很有希望的!如上图所示,“气候编码为与最接*的航海、温暖、凉爽、温度、盐度、湿度等等。"议会最类似于两院制,选区,部长,席位,参议员等。而分子则与砷、化合物、铵、人工合成、钙等密切相关。感兴趣的读者可以通过使用更复杂的模型架构和更大的数据来进一步探索和增强单词嵌入!
本文分享的完整版本的代码片段,以及图片和学到的单词嵌入可在 Github 上获得。如果你喜欢这篇文章或有任何反馈,请在下面的评论区告诉我!
2020 年的单词嵌入。查看代码示例
当前单词嵌入方法概述:从 Word2vec 到 Transformers

图片由 Rostyslav Neskorozhenyi 使用 WordCloud 模块创建
在本文中,我们将研究单词嵌入——适合机器学习算法处理的单词的数字表示。
最初,我写这篇文章是为了对 2020 年单词嵌入的当前方法进行概述和汇编,我们的人工智能实验室团队可以不时地使用它作为快速复习。我希望我的文章对更广泛的数据科学家和开发人员有用。文章中的每个单词嵌入方法都有(非常)简短的描述、供进一步研究的链接和 Python 代码示例。所有代码被打包成谷歌 Colab 笔记本。让我们开始吧。
根据维基百科,单词嵌入是自然语言处理(NLP)中一组语言建模和特征学习技术的统称,其中来自词汇表的单词或短语被映射到实数的向量。
一键或计数向量化
将单词转换成向量的最基本方法是统计每个单词在每个文档中的出现次数。这种方法被称为计数矢量化或一键编码。
这种方法的主要原理是收集一组文档(可以是单词、句子、段落甚至文章),统计每个文档中每个单词的出现次数。严格来说,得到的矩阵的列是单词,行是文档。
from sklearn.feature_extraction.text import CountVectorizer
# create CountVectorizer object
vectorizer = CountVectorizer()
corpus = [
'Text of the very first new sentence with the first words in sentence.',
'Text of the second sentence.',
'Number three with lot of words words words.',
'Short text, less words.',
]# learn the vocabulary and store CountVectorizer sparse matrix in term_frequencies
term_frequencies = vectorizer.fit_transform(corpus)
vocab = vectorizer.get_feature_names()# convert sparse matrix to numpy array
term_frequencies = term_frequencies.toarray()# visualize term frequencies
import seaborn as sns
sns.heatmap(term_frequencies, annot=True, cbar = False, xticklabels = vocab);

图像由 Rostyslav Neskorozhenyi 使用 seaborn 模块创建
countvectorizing 中的另一种方法是,如果在文档中找到该单词,则只放置 1(不管出现的频率如何),如果在文档中没有找到该单词,则放置 0。在这种情况下,我们得到真正的“一键”编码。
one_hot_vectorizer = CountVectorizer(binary=True)
one_hot = one_hot_vectorizer.fit_transform(corpus).toarray()sns.heatmap(one_hot, annot=True, cbar = False, xticklabels = vocab)

图像由 Rostyslav Neskorozhenyi 使用 seaborn 模块创建
TF-IDF 编码
对于大语料库的文档,一些词如‘a’、‘the’、‘is’等。非常频繁地出现,但是它们没有携带很多信息。使用一键编码方法,我们可以确定这些单词很重要,因为它们出现在许多文档中。解决这个问题的方法之一是停用词过滤,但是这种解决方案是离散的并且不灵活。
TF-IDF(词频—逆文档频)可以较好的处理这个问题。TF-IDF 降低了常用词的权重,提高了只出现在当前文档中的罕见词的权重。TF-IDF 公式如下所示:

其中 TF 是通过将单词在文档中出现的次数除以文档中的单词总数来计算的

IDF(逆文档频率),解释为文档的逆数量,其中出现了我们感兴趣的术语。n-文档数量,n(t)-具有当前单词或术语 t 的文档数量

from sklearn.feature_extraction.text import TfidfVectorizer
import seaborn as snscorpus = [
'Time flies like an arrow.',
'Fruit flies like a banana.'
]vocab = ['an', 'arrow', 'banana', 'flies', 'fruit', 'like', 'time']tfidf_vectorizer = TfidfVectorizer()
tfidf = tfidf_vectorizer.fit_transform(corpus).toarray()sns.heatmap(tfidf, annot=True, cbar = False, xticklabels = vocab)

图像由 Rostyslav Neskorozhenyi 使用 seaborn 模块创建
Word2Vec 和手套
最常用的单词嵌入模型是 word2vec 和 GloVe ,它们都是基于分布假设的无监督方法(出现在相同上下文中的单词往往具有相似的含义)。
Word2Vec 单词嵌入是单词的向量表示,当输入大量文本(例如维基百科、科学、新闻、文章等)时,通常由无监督模型学习。).单词的这些表示在其他属性中捕捉单词之间的语义相似性。Word2Vec 单词嵌入是以这样的方式学习的,意思相*的单词(例如“king”和“queen”)的向量之间的距离比意思完全不同的单词(例如“king”和“carpet”)的距离更*。

Word2Vec 矢量甚至允许对矢量进行一些数学运算。例如,在这个操作中,我们对每个单词使用 word2vec 向量:
国王——男人+女人=王后
# Download Google Word2Vec embeddings [https://code.google.com/archive/p/word2vec/](https://code.google.com/archive/p/word2vec/)!wget [https://s3.amazonaws.com/dl4j-distribution/GoogleNews-vectors-negative300.bin.gz](https://s3.amazonaws.com/dl4j-distribution/GoogleNews-vectors-negative300.bin.gz)
!gunzip GoogleNews-vectors-negative300.bin# Try Word2Vec with Gensimimport gensim# Load pretrained vectors from Google
model = gensim.models.KeyedVectors.load_word2vec_format('GoogleNews-vectors-negative300.bin', binary=True)king = model['king']# king - man + woman = queen
print(model.most_similar(positive=['woman', 'king'], negative=['man']))

检查单词“女人”和“男人”的向量有多相似。
print(model.similarity('woman', 'man'))

检查单词“king”和“woman”的向量有多相似。
print(model.similarity('king', 'woman'))

另一种单词嵌入方法是手套(“全局向量”)。它基于单词上下文矩阵的矩阵分解技术。它首先构建一个(单词 x 上下文)共现信息的大矩阵,即对于每个“单词”(行),计算我们在大型语料库的某些“上下文”(列)中看到该单词的频率。然后,这个矩阵被分解成一个低维(单词 x 特征)矩阵,其中每一行现在存储每个单词的向量表示。一般来说,这是通过最小化“重建损失”来实现的。这种损失试图找到可以解释高维数据中大部分差异的低维表示。
# Try Glove word embeddings with Spacy!python3 -m spacy download en_core_web_lgimport spacy
# Load the spacy model that you have installed
import en_core_web_lg
nlp = en_core_web_lg.load()
# process a sentence using the model
doc = nlp("man king stands on the carpet and sees woman queen")
查找国王和王后之间的相似性(值越高越好)。
doc[1].similarity(doc[9])

找出国王和地毯的相似之处。
doc[1].similarity(doc[5])

检查是否国王-男人+女人=王后。我们将把“男人”和“女人”的向量乘以 2,因为减去“男人”的向量并加上“女人”的向量对“国王”的原始向量没有什么影响,可能是因为那些“男人”和“女人”本身是相关的。
v = doc[1].vector - (doc[0].vector*2) + (doc[8].vector*2)from scipy.spatial import distance
import numpy as np# Format the vocabulary for use in the distance function
vectors = [token.vector for token in doc]
vectors = np.array(vectors)# Find the closest word below
closest_index = distance.cdist(np.expand_dims(v, axis = 0), vectors, metric = 'cosine').argmin()
output_word = doc[closest_index].text
print(output_word)

快速文本
FastText 是 word2vec 的扩展。FastText 是由 2013 年创建 word2vec 框架的 Tomas Mikolov 团队开发的。
FastText 相对于原始 word2vec 向量的主要改进是包含了字符 n-grams ,这允许计算没有出现在训练数据中的单词的单词表示(“词汇外”单词)。
!pip install Cython --install-option="--no-cython-compile"
!pip install fasttext# download pre-trained language word vectors from one of 157 languges [https://fasttext.cc/docs/en/crawl-vectors.html](https://fasttext.cc/docs/en/crawl-vectors.html)
# it will take some time, about 5 minutes
import fasttext
import fasttext.util
fasttext.util.download_model('en', if_exists='ignore') # English
ft = fasttext.load_model('cc.en.300.bin')
为单词“king”创建嵌入。
ft.get_word_vector('king')
获取与单词“king”最相似的单词。
ft.get_nearest_neighbors('king')

测试模型为未知单词创建向量的能力。
'king-warrior' in ft.words

获取与未知单词“王者战士”最相似的单词。
ft.get_nearest_neighbors('king-warrior')

ELMo(语言模型嵌入)
与 word2vec 和 GLoVe 等传统单词嵌入不同,分配给令牌或单词的 ELMo 向量取决于当前上下文,实际上是包含该单词的整个句子的函数。所以同一个词在不同的语境下可以有不同的词向量。此外,ELMo 表示完全基于字符,因此它们不局限于任何预定义的词汇。
来自官方网站的描述:
ELMo 是一种深度语境化的单词表示,它模拟(1)单词使用的复杂特征(例如,句法和语义),以及(2)这些使用如何在语言语境中变化(即,模拟多义性)。这些单词向量是深度双向语言模型(biLM)的内部状态的学习函数,该模型是在大型文本语料库上预先训练的。它们可以很容易地添加到现有的模型中,并在广泛的具有挑战性的自然语言处理问题中显著提高技术水*,包括问题回答、文本蕴涵和情感分析。
# use tensorflow 1.x for ELMo, because trere are still no ELMo for tensorflow 2.0%tensorflow_version 1.ximport tensorflow_hub as hub
import tensorflow as tf# Download pretrained ELMo model from Tensorflow Hub [https://tfhub.dev/google/elmo/3](https://tfhub.dev/google/elmo/3)elmo = hub.Module("[https://tfhub.dev/google/elmo/3](https://tfhub.dev/google/elmo/3)", trainable=True)sentences = \
['king arthur, also called arthur or aathur pendragon, legendary british king who appears in a cycle of \
medieval romances (known as the matter of britain) as the sovereign of a knightly fellowship of the round table.',
'it is not certain how these legends originated or whether the figure of arthur was based on a historical person.',
'the legend possibly originated either in wales or in those parts of northern britain inhabited by brythonic-speaking celts.',
'for a fuller treatment of the stories about king arthur, see also arthurian legend.']
为了将句子发送到模型,我们需要将它们分成单词数组和填充数组,长度相同。此外,我们还将创建“mask”数组,该数组将显示元素是真实单词还是填充符号(在我们的示例中为“_”)。稍后我们将使用“mask”数组来显示真实的单词。
words = []
mask = []
masked_words = []for sent in sentences:
splitted = sent.split()
for i in range(36):
try:
words.append(splitted[i])
except:
words.append('_')for word in words:
if word == "_":
mask.append(False)
else:
mask.append(True)
masked_words.append(word)
使用 ELMo 创建嵌入:
embeddings = elmo(
sentences,
signature="default",
as_dict=True)["elmo"]
将 Tensorflow 张量转换为 numpy 数组。
%%time
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
sess.run(tf.tables_initializer())
x = sess.run(embeddings)embs = x.reshape(-1, 1024)masked_embs = embs[mask]
使用 PCA 可视化嵌入。
from sklearn.decomposition import PCApca = PCA(n_components=10)
y = pca.fit_transform(masked_embs)from sklearn.manifold import TSNEy = TSNE(n_components=2).fit_transform(y)import plotly as py
import plotly.graph_objs as godata = [
go.Scatter(
x=[i[0] for i in y],
y=[i[1] for i in y],
mode='markers',
text=[i for i in masked_words],
marker=dict(
size=16,
color = [len(i) for i in masked_words], #set color equal to a variable
opacity= 0.8,
colorscale='Viridis',
showscale=False
)
)
]
layout = go.Layout()
layout = dict(
yaxis = dict(zeroline = False),
xaxis = dict(zeroline = False)
)
fig = go.Figure(data=data, layout=layout)
fig.show()

图像由 Rostyslav Neskorozhenyi 使用 plotly 模块创建
变形金刚(电影名)
终于到了当前最先进的方法——变压器的时候了。著名的 GPT-2 、伯特、 CTRL 都是基于变形金刚的,并且像 ELMo 一样产生上下文敏感的嵌入。但是与 ELMo 变形金刚不使用 RNN 不同,它们不需要一个接一个地处理句子中的单词。句子中的所有单词被并行处理,这种方法加快了处理速度并解决了消失梯度问题。
变形金刚使用注意力机制来描述每个特定单词与句子中所有其他单词的联系和依赖性。杰伊·阿拉姆马在一篇插图精美的文章中详细描述了这种机制和变压器的主要原理。

图片来自http://jalammar . github . io
对于我们的例子,我们将使用 brilliant Transformers 库,其中包含最新的基于 Transformers 的模型(如 BERT 、 XLNet 、 DialoGPT 或 GPT-2 )。
让我们和伯特做一些嵌入。首先,我们需要安装变形金刚库。
!pip install transformers
现在,我们导入 pytorch,这是一个预训练的 BERT 模型,以及一个 BERT 标记器,它将完成将句子转换成适合 BERT 的格式的所有必要工作(标记自身并添加特殊标记,如[SEP]和[CLS])。
import torch
torch.manual_seed(0)
from transformers import BertTokenizer, BertModelimport logging
import matplotlib.pyplot as plt
% matplotlib inline# Load pre-trained model tokenizer (vocabulary)
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased', do_lower_case=True)
输入一些句子,将其标记出来。
sentences = \
['king arthur, also called arthur or aathur pendragon, legendary british king who appears in a cycle of \
medieval romances (known as the matter of britain) as the sovereign of a knightly fellowship of the round table.',
'it is not certain how these legends originated or whether the figure of arthur was based on a historical person.',
'the legend possibly originated either in wales or in those parts of northern britain inhabited by brythonic-speaking celts.',
'for a fuller treatment of the stories about king arthur, see also arthurian legend.']# Print the original sentence.
print(' Original: ', sentences[0][:99])# Print the sentence splitted into tokens.
print('Tokenized: ', tokenizer.tokenize(sentences[0])[:15])# Print the sentence mapped to token ids.
print('Token IDs: ', tokenizer.convert_tokens_to_ids(tokenizer.tokenize(sentences[0]))[:15])

注意,有些记号可能是这样的:['aa ',' ##th ',' ##ur ',' pen ',' ##dra ',' ##gon']。这是因为 BERT tokenizer 是用一个 WordPiece 模型创建的。这个模型贪婪地创建一个由单个字符、子词和单词组成的固定大小的词汇表,它最适合我们的语言数据。BERT tokenizer 使用的词汇包含所有英语字符,以及在模型接受训练的英语语料库中找到的大约 30,000 个最常见的单词和子单词。因此,如果这个单词没有在词汇表中提到,这个单词就被分成子单词和字符。一些子词前的两个散列符号(##)表示该子词是一个更大的词的一部分,前面还有另一个子词。
我们将使用 tokenizer.encode_plus 函数,它将:
- 把句子分成几个标记。
- 添加特殊的[CLS]和[SEP]标记。
- 将令牌映射到它们的 id。
- 将所有句子填充或截短至相同长度。
# Tokenize all of the sentences and map tokens to word IDs.
input_ids = []
attention_masks = []
tokenized_texts = []for sent in sentences:
encoded_dict = tokenizer.encode_plus(
sent,
add_special_tokens = True,
truncation=True,
max_length = 48,
pad_to_max_length = True,
return_tensors = 'pt',
) # Save tokens from sentence as a separate array.
marked_text = "[CLS] " + sent + " [SEP]"
tokenized_texts.append(tokenizer.tokenize(marked_text))
# Add the encoded sentence to the list.
input_ids.append(encoded_dict['input_ids'])# Convert the list into tensor.
input_ids = torch.cat(input_ids, dim=0)
段号。BERT 接受训练并期望句子对使用 1 和 0 来区分两个句子。我们将对每个句子分别进行编码,因此我们将只对每个句子中的每个标记进行 1 标记。
segments_ids = torch.ones_like(input_ids)
现在,我们可以调用 BERT 模型,并最终获得模型隐藏状态,从中我们将创建单词嵌入。
with torch.no_grad(): outputs = model(input_ids, segments_ids)
hidden_states = outputs[2]
让我们检查一下我们得到了什么。
print ("Number of layers:", len(hidden_states), " (initial embeddings + 12 BERT layers)")
print ("Number of batches:", len(hidden_states[0]))
print ("Number of tokens:", len(hidden_states[0][0]))
print ("Number of hidden units:", len(hidden_states[0][0][0]))

# Concatenate the tensors for all layers.
token_embeddings = torch.stack(hidden_states, dim=0)# Swap dimensions, so we get tensors in format: [sentence, tokens, hidden layes, features]
token_embeddings = token_embeddings.permute(1,2,0,3)
我们将使用最后四个隐藏层来创建每个单词嵌入。
processed_embeddings = token_embeddings[:, :, 9:, :]
为每个令牌连接四层以创建嵌入
embeddings = torch.reshape(processed_embeddings, (4, 48, -1))
让我们检查第一句话的嵌入。首先,我们需要获得需要比较的令牌的 id。
for i, token_str in enumerate(tokenized_texts[0]):
print (i, token_str)



我们可以看到单词“king”位于索引 1 和 17 处。我们将检查嵌入 1 和 17 之间的距离。此外,我们将检查单词“arthur”的嵌入是否比单词“table”更接*“king”。
from scipy.spatial.distance import cosine
kings = cosine(embeddings[0][1], embeddings[0][17])
king_table = cosine(embeddings[0][1], embeddings[0][46])
king_archtur = cosine(embeddings[0][2], embeddings[0][1])
print('Distance for two kings: %.2f' % kings)
print('Distance from king to table: %.2f' % king_table)
print('Distance from Archtur to king: %.2f' % king_archtur)

因此,我们看到两个“国王”的嵌入非常相似,但不相同,阿奇图尔更接*于一个国王,而不是一张桌子。
用simple representations模块事情可能更简单。这个模块完成了我们之前做的所有工作——从 BERT 中提取所需的隐藏状态,并在几行代码中创建嵌入。
!pip install simplerepresentationsimport torch
from simplerepresentations import RepresentationModel
torch.manual_seed(0)model_type = 'bert'
model_name = 'bert-base-uncased'representation_model = RepresentationModel(
model_type=model_type,
model_name=model_name,
batch_size=4,
max_seq_length=48,
combination_method='cat',
last_hidden_to_use=4
)text_a = sentencesall_sentences_representations, all_tokens_representations = representation_model(text_a=text_a)
检查拱门、国王和桌子之间的距离。
from scipy.spatial.distance import cosinekings = cosine(all_tokens_representations[0][1], all_tokens_representations[0][17])
king_table = cosine(all_tokens_representations[0][1], all_tokens_representations[0][46])
king_archtur = cosine(all_tokens_representations[0][2], all_tokens_representations[0][1])print('Distance for two kings: %.2f' % kings)
print('Distance from king to table: %.2f' % king_table)
print('Distance from Archtur to king: %.2f' % king_archtur)

同样的结果,更少的代码。
结论
我希望在阅读完这篇文章之后,您已经对当前的单词嵌入方法有了一个概念,并开始理解如何在 Python 中快速实现这些方法。NLP 的世界是多样化的,并且有更多的嵌入模型和方法。在我的文章中,我把重点放在最常见的和我们自己在工作中经常使用的。您可以在参考文献部分找到更多信息。
参考
- 伯特单词嵌入教程
- 图示变压器
- 图解 GPT-2(可视化变压器语言模型)
- 从预训练的单词嵌入到预训练的语言模型——关注 BERT
- 用变形金刚和 DialoGPT 微调制作你自己的 Rick Sanchez(机器人)
- 玩弄文字向量
- 理解手套嵌入的直观指南
- 用 Spacy 和 Gensim 在 Python 中嵌入单词
- 单词嵌入族简评(2019)
- 单词嵌入:探索、解释和利用(用 Python 编写代码)
词嵌入 vs TF-IDF:回答新冠肺炎问题

回答新冠肺炎问题的文本相似度方法比较。
数据集: CORD-19
我们感兴趣的问题:
- 潜在风险因素的数据
- 吸烟、已有肺部疾病
- 合并感染(确定同时存在的呼吸道/病毒感染是否使病毒更易传播或更具毒性)和其他共病
- 新生儿和孕妇
- 社会经济和行为因素,以了解病毒的经济影响和是否有差异。
- 病毒的传播动力学,包括基本繁殖数、潜伏期、序列间隔、传播方式和环境因素
- 疾病的严重程度,包括有症状住院患者和高危患者群体的死亡风险
- 人群易感性
- 可有效控制的公共健康缓解措施
预赛
这篇文章假设读者对 NLP 有一个基本的了解,比如单词袋,以及文本是如何用数字表示的。
方法 1: TF-IDF 和余弦相似度
TF-IDF 代表词频——逆文档频率,是信息检索任务中常用的方法[1]。我们将使用它来查找与我们的搜索问题相似的句子。
为此,我们需要将每个句子表示为一个向量。TF-IDF 通过根据术语在文档中的流行程度对其进行加权来创建这些向量。如果一个术语出现在语料库中的几乎所有文档中(这意味着该术语是无用的,可以忽略),公式的 IDF 部分确保它在句子向量中获得非常低的权重。如果一个术语在识别一个句子中很重要(这意味着它不会出现在许多其他句子中),它将获得很高的权重。
如果我们使用 TF-IDF 对以下三个句子的语料库进行矢量化,请尝试猜测哪些术语将获得高权重,哪些术语将获得较低权重。
句子 1:*均潜伏期
句子 2:*均风险期
第三句:传播的风险
因为单词出现在所有句子中,所以它相当不重要,并且将获得低权重,而术语孵化和传输将获得非常高的权重,因为它们分别在识别句子 1 和 2 中很重要。
理想情况下,您应该使用二元模型来捕获像“孵化期”这样的术语,但是为了简单起见,让我们坚持使用单元模型。

Unigrams 的 TF-IDF 矩阵
我在这里没有计算任何东西,所以我写了高,中,低,而不是实际的数字。如果你对 TF-IDF 不是太熟悉,我建议你手工计算矩阵,但如果你是,请继续阅读。
现在,我们想找到关于“潜伏期”的句子如果我们使用已经构建的 TF-IDF 矩阵对这个搜索查询进行矢量化,我们将得到这个搜索查询的向量:

请注意,这个向量与句子 1 中的向量非常相似。如果我们计算搜索查询向量与句子向量的接*度,我们会发现它最接*句子 1。我们将使用余弦相似度来计算这种接*度,因为它比正常的欧几里德距离更适合文本数据。
你可能已经意识到我们的 TF-DF 方法存在一个主要缺点。同一概念在不同的论文中可能有不同的措辞。例子:术语婴儿和青少年意思相同,但是如果我们搜索“婴儿的风险因素”,我们会错过将婴儿称为青少年的句子。这就是单词嵌入的用武之地…
方法 2:单词嵌入和单词移动距离
TF-IDF 向量不考虑语言中的语义相似性。青少年的体重与婴儿的体重无关。单词嵌入试图通过依赖一个基本思想来捕捉这些关系:出现在相同上下文中的单词具有相似的含义。我们将使用一个名为 word2vec 的单词嵌入框架,它学习语料库中单词的向量表示[2]。这里有一个例子:
在冠状病毒爆发后,默克尔在德国实施了封锁。
第二句:新冠肺炎疫情爆发后,德国总理实施了封锁。
我们注意到默克尔和总理这两个词可以互换使用;冠状病毒和新冠肺炎也是如此。因此,这两个词应该有某种关系。
单词嵌入不是每个句子都有向量,而是通过为每个单词创建向量来捕获这些语义关系。在向量空间中,冠状病毒等词的向量与新冠肺炎等类似词的向量非常接*,而与咖啡或纸张等不相关的词则相距较远。
在没有标签或手工工作的情况下构建这样一个表示似乎是不可思议的,但是当你理解这个过程时就变得显而易见了。这些向量是使用浅层(1 个隐藏层)神经网络构建的,以预测目标词(如“默克尔”),给定一些上下文词(如“强制封锁德国”)。这里有一个非常简单的例子,使用了我们之前的句子:
训练一个神经网络来预测给定上下文单词的单词。当它试图预测封锁给默克尔的时候,隐藏的单位会为默克尔获得一些权重。

x =默克尔,y =封锁
类似地,当从负责人预测锁定时,隐藏单元将获得一些对应于负责人的权重,这将有助于预测锁定。

x =议长,y =封锁
仔细想想,s 因为激活锁定的输出单元需要一定的权重组合,所以预测锁定的任何单词都应该得到类似的权重。

T4 冠状病毒和新冠肺炎也是如此。在预测爆发和爆发时,这两个术语应该得到相似的权重,因为爆发出现在这两个术语的上下文中。**
训练好的网络的隐含层中的权重成为单词的向量,隐含层神经元的数量成为维数。
在上面的图像中,请注意隐藏层中有两个神经元,因此,单词向量是二维的。
这里有一个包含四个句子的可视化示例:

该模型能够学习语义关系,例如默克尔应该更接*总理而不是其他词,例如巴黎。
在这些例子中,我创建了二维嵌入。然而在现实情况下,通常有 100-300 维的向量。
正如您可能已经猜到的,这个过程需要大量的训练数据来学习准确的嵌入,所以在线上有预先训练的单词嵌入,您可以开箱即用。
在我们的 CORD-19 数据上训练 word2vec 之后,这里是 婴儿的嵌入和类似单词。 它们是 100 维的,但我使用了降维技术来将它们可视化。

来自 CORD-19 数据的单词嵌入
那么现在我们有了每个单词的向量,我们如何衡量两个句子之间的相似性呢?
单词移动器的距离(WMD) 通过定位两个句子在单词嵌入空间中的位置并将一个句子转换成另一个句子来完成这项任务[3]。为了计算句子 A 和 B 之间的相似度,它将 A 中的每个单词“驱动”到 B 中最*的单词,直到 A 被转换成 B,反之亦然。大规模杀伤性武器是*均“旅行的距离”的话。
第一句:“默克尔实施封锁”
第二句:“议长下令封锁”
第三句:“巴黎实施封锁”
**默克尔,第一句中的,行驶 0.0724“公里”到达第二句中的总理;强制和锁定行进 0 公里,因为它们已经出现在句子 2 中。因此,WMD 为(0.0727 + 0 + 0) = 0.0242

第 1 句和第 2 句之间的 WMD
这里,默克尔行驶 0.6312 公里到达巴黎,所以 WMD = (0.6312 + 0 + 0) / 3 = 0.2104

第 1 句和第 3 句之间的 WMD
所以在单词嵌入的帮助下,我们能够确定句子 1 和 2 比句子 1 和 3 更相似。如果没有单词嵌入,句子 1 将同样类似于句子 2 和 3。
现在我们已经了解了理解这些方法的必要基础,让我们尝试一下这些方法,看看我们是否能够检索到与我们的搜索查询有一些相似性的文本。





我们做到了!两种方法都获得了相当精确的答案。可以通过使用更多的训练数据或使用预训练的嵌入来改进单词嵌入方法。
离别的思绪
我们讨论了计算两个文本之间相似性的两种方法,并使用它们来检索与新冠肺炎相关的问题的答案。尽管 WMD 由于其高时间复杂度而不适用于这种类型的任务,但它仍然是一种强有力的方法,并且在许多领域具有巨大的适用性。我可以看到它在检测剽窃方面非常有效。如果一个句子被复制粘贴,几个单词被同义词替换,word2vec 和 WMD 可以很容易地发现它,而 TF-IDF 就不行了。另一种替代 WMD 的方法是用*滑的逆频率对句子的单词向量进行*均,并计算余弦相似度。然而,我们经常不能理解像 TF-IDF 这样的简单方法的力量,正如我们在这里看到的,它们不应该被忽视。
编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
这个项目的代码可在笔记本这里https://github.com/tchanda90/covid19-textmining
你可以在这里使用搜索工具https://tchanda90.github.io/covid19-textmining/
[1]拉莫斯,J. (2003 年,12 月)。使用 tf-idf 确定文档查询中的单词相关性。在第一届机器学习教学会议论文集(第 242 卷,第 133-142 页)。
[2]t .米科洛夫,陈,k .,科拉多,g .,&迪安,J. (2013 年)。向量空间中单词表示的有效估计。 arXiv 预印本 arXiv:1301.3781 。
[3]m .库斯纳、孙、n .科尔金和 k .温伯格(2015 年 6 月)。从单词嵌入到文档距离。在机器学习国际会议(第 957–966 页)。
单词嵌入,它们到底是什么?
令人惊奇的单词嵌入,对处理语言非常有用,它们到底是什么?我们仔细看看单词包和单词嵌入之间的联系,看看创建单词向量的 skip-gram 模型,并建立一些关于单词嵌入的直觉。

帕特里克·托马索在 Unsplash 上的照片
如果你一直在从事自然语言处理(NLP)的工作,那么即使你曾经对 NLP 感到好奇,单词嵌入的概念也可能会出现。但是这些嵌入到底是什么呢?
为了找到答案,我们从头开始。NLP 是应用不同的方法从文本中计算出有用的东西。它首先把有问题的文本处理成我们可以用计算机进行计算并做出决定的东西。在这种情况下,决策是模型的输出,这可以是将请求分为不同的类别,如订单、反馈或投诉。它可以匹配项目的文本描述,以便找到类似的项目,或者找到与所描述的犯罪行为相关的法律文本。可能性的清单很长,而且多种多样。然而,在我们可以使用文本创建一个有意义的模型之前,我们需要将它解析成一种我们可以使用计算机处理的格式。

NLP 使用语言作为决策的输入,这是 NLP 模型的高级图表。
为什么不用所有的字母呢?
什么事?英语只有 26 个,其他语言更多,(像我的 29 个),何乐而不为呢?想象一个解决方案,将每个单词编码成一个数字,或者更好,一个热编码。然后我们用计算机算法可以理解的方式来表达字母。然而,这比它第一次出现时要复杂得多。你看,每个字母本身并没有意义。为了能够理解单独的字母,我们需要一个模型来查看每个字母与下一个字母之间的联系,这是一个有趣的情况,但还没有显示出实际有用的结果。很容易找到拼写相似的单词(甚至是变位词)的例子,而这些单词的意思肯定不一样。其结果是,任何使用单个字母作为输入的模型都必须考虑字母的顺序,这使得模型的开发具有挑战性。

即使所有的字母都一样,单词的意思却不一样
一袋单词
单词包是一种从文档中创建矢量的方法。它的工作原理是统计文档中的所有单词,并将所选词汇表中的每个单词分配到向量表示中的一个位置。这是一个单词的热编码(词汇表中每个单词一个位置),然后将向量相加得到文档的单词包表示。

词汇量为 25 的单个单词的弓形表示。
使用单词包时要记住的一点是,向量的长度等同于词汇的大小,每个单词都表示为向量中的一个位置。从数学上来说,每个单词对结果向量贡献一个维度。这是一种稀疏编码,使用大量内存来存储关于文本的少量信息。
单词包已经成功地应用于诸如将文本分类的任务中。不同单词的频率实际上可以告诉你很多关于我们正在处理的文本类型的信息,这使得这种类型的向量对于例如分类任务非常有用。点击阅读更多关于单词袋的信息。
词汇袋方法确实有用,可以用在很多情况下,但它有一定的局限性。单词袋的缺点:
- 不捕获单词和语法的顺序
- 单词之间的关系丢失了,因为作品包生成的向量中的位置分配是任意的。
- 由于处理单词包所需的向量的长度与词汇量的大小直接相关,根据我的经验,与你在普通文本中遇到的词汇相比,词汇量通常也是有限的。testyourvocab.com的一个在线词汇测试项目显示“大多数成人母语考生的词汇量大约在 20,000-35,000 个单词之间”
单词嵌入
那么,有没有一种方法可以用数字来捕捉语言,以一种对计算机有意义的格式,最好不要用所有的存储空间来捕捉庞大的词汇?
在数学上,我们可以将单词在单词类型向量包中的表示视为每个单词由多维空间中的一个方向表示,其中维度由我们包含在词汇表中的单词数量决定。单词的顺序是随机的,所有单词都在与所有其他单词正交的方向上起作用。这意味着,就我们从词语的表征中所能看出的来说,它们彼此之间都是同样不同的。如果我们的词汇中有“绵羊”、“羔羊”和“石英”,我们的模型必须将这些点连接起来,并找出“羔羊”和“绵羊”彼此更相似,而不是与“石英”相似。这不是对存储空间的非常紧凑的使用,并且没有对单词被分配向量的方式进行训练。
这些问题的答案是单词嵌入。它们是如何工作的?对于单词嵌入,我们为每个单词分配一个长度通常为 100-300 维的向量。在 glove 的论文中显示,嵌入的矢量大小的范围是具有最有用结果的范围。
然后,这些随机初始化的向量被训练以实现向量组织,该向量组织根据由这些向量创建的向量空间中的含义来排列单词。为了实现这一点,我们必须使用单词在句子中出现的顺序来创建一个向量,以捕捉单词的一些含义。这个概念是训练一个神经网络来预测哪些单词一起出现。有两种方法可以做到这一点,要么我们向网络输入目标单词,并尝试预测周围的单词,即上下文单词,要么我们向网络输入上下文单词,并尝试预测目标单词。
这方面的训练设置相当优雅。遍历语料库(大型自然文本数据集,例如维基百科转储)中的单词,并尝试预测周围的单词。这种方法被称为跳格法。还有一种替代的训练方法,即连续单词包,它将周围(上下文)的单词输入到模型中,然后对目标单词进行预测。在这里,我将集中讨论 skip-gram 版本,因为这两个版本在架构和结果上都非常相似。尽管如此,值得注意的是这些方法是不等价的,尽管它们是相似的。Skip-Gram 倾向于更好地处理不常用的单词,因为当它是目标单词时,不常用的单词将被同样对待,而连续的单词包倾向于给不常用的单词一个低概率,而它训练起来更快。

用于训练神经网络以预测哪些单词同时出现的滑动窗口。
词汇表的大小是在初始化时设置的参数。词汇表的大小等于模型中输入向量的大小。使用一种热编码来创建输入向量,即向量在向量中分配给目标单词的位置具有“1 ”,在所有其他位置具有“0”。输入向量乘以维度为 V×N 的权重矩阵,其中 N 是隐藏层的大小。然后,隐藏层乘以维数为 N×V 的矩阵 W ’,这产生了维数为 1×V 的向量。softmax 函数被应用于向量 U,以便产生每个单词的概率。输出向量是词汇表中所有单词成为目标/输入单词的上下文单词之一的概率列表。上下文窗口的大小是模型的一个参数,它对结果嵌入有影响。由于更多的可用训练样本,更大的窗口大小可以给出更高的准确度,但是也导致更长的训练时间。它还将使模型反映单词的更广泛的主题意义,而较小的窗口大小将倾向于给出反映关于目标单词的更集中的信息的结果。有关上下文窗口大小的更多信息,请参见论文“基于依存关系的单词嵌入”。
这是一个人工神经网络,我们可以应用一个误差函数,并使用反向传播来训练网络,最好是使用一个大型,多样化的文本语料库。这是一个简化的解释,在实现单词嵌入架构时,需要考虑很多调整和特殊的适应。如果你想继续读下去,了解培训的更多细节,我建议这篇文章。还值得注意的是,几个预先训练的模型可用于单词嵌入,这对我们大多数人来说是明智的选择,因为它们是在非常大的语料库上训练的,并且产生非常好的单词嵌入。

跳过程序体系结构
现在这几乎就像一个魔术:这个模型的重点从来不是预测上下文单词,我们所追求的是隐藏层,也就是单词嵌入的。如果你熟悉自动编码器,这是一个类似的概念。神经网络必须利用隐藏层大小的向量空间来捕获关于每个单词的信息,使其能够预测上下文单词。如果两个单词经常与相同的上下文单词一起出现,则模型必须为这些单词产生相似的向量,以便在预测上下文单词时实现高准确度。这也是我们在考察单词嵌入给出的一些向量时看到的。国家的名称和动物的种类聚集在一起,而服装和体育活动则在一个集群中。

单词嵌入清楚地显示相似的单词有相似的嵌入。(使用 Gensim 的模型生成的向量,该模型在谷歌新闻数据集上进行训练。使用 T-SNE 进行降维来实现可视化。
连续空间单词表示的语言规则的作者注意到的一个令人困惑的事情是,单词的单数和复数形式之间的关系具有几乎相同的单词嵌入差异:“ x 苹果 x 苹果≈
x 汽车 x 汽车,x 家族 x 家族≈ x 汽车 x 汽车”。同样,这同样适用于男性和女性,由此产生了著名的例子“国王-男人+女人=王后”。
单词嵌入的惊人之处在于,它们是在无人监督的情况下训练的,通过捕获如此多的关于单词如何使用的信息,它们也捕获了相当多的关于单词意思的信息。需要注意一些限制,其中之一是同音异义词,即拼写相同但含义完全不同的单词,该单词的两种含义属于向量空间中的不同位置,然而,这当然是不可能的,因为计算机无法区分这两种含义。当单词根据上下文具有不同的含义时,也会出现类似的问题。不考虑这些问题,单词嵌入是有用的,并且在无数的实际应用中被应用。
code2vec、GloVe 和 spaCy 的单词嵌入。
如何根据您的用例选择单词嵌入算法?

改善你的机器学习模型的一个有效方法是使用单词嵌入。使用单词嵌入,您可以捕获文档中单词的上下文,然后找到语义和句法的相似性。
在本帖中,我们将讨论单词嵌入技术的一个不寻常的应用。我们将努力为 OpenAPI 规范找到最好的单词嵌入技术。作为 OpenAPI 规范的一个例子,我们将使用来自 apis-guru 的 OpenAPI 规范的免费资源😎。
最大的挑战是 OpenAPI 规范既不是自然语言,也不是代码。但这也意味着我们可以自由使用任何可用的嵌入模型。在这个实验中,我们将研究三个可能的候选者:code2vec、GloVe 和 spaCy。
code2vec 是一个学习与源代码相关的类比的神经模型。该模型是在 Java 代码数据库上训练的,但是您可以将其应用于任何代码库。
然后是手套。GloVe 是自然语言处理(NLP)的一种常用算法。它在维基百科和 Gigawords 上接受了训练。
最后,我们有空间。虽然 spaCy 是最*才开发的,但该算法已经以世界上最快的单词嵌入而闻名。
让我们看看这些算法中哪一个更适合 OpenAPI 数据集,哪一个更适合 OpenAPI 规范👀。我把这篇文章分成六个部分,每一部分都包含代码示例和一些将来使用的技巧,还有一个结论。
- 下载数据集
- 下载词汇
- 提取字段名称
- 令牌化密钥
- 创建字段名称的数据集
- 测试嵌入
- 结论
现在,我们可以开始了。
1。下载数据集✅
首先,我们需要下载整个API-guru数据库。
您会注意到大多数 API-guru 规范都是 Swagger 2.0 格式的。但是……open API 规范的最新版本是 OpenAPI 3.0 。所以让我们通过使用 Unmock 脚本将整个数据集转换成这种格式!您可以按照 unmock-openapi-scripts 自述文件中的说明完成此操作。
这可能需要一段时间(你不会变成🧓,但我们说的是几个小时⏰)最终,您将获得一个具有各种规格的大型数据集🎓。
2。下载词汇✅
代码 2vec
- 从 code2vec GitHub 页面下载模型。按照 README.md 的快速入门部分中的说明进行操作,然后导出训练好的令牌。
- 使用 gensim 库加载。
model = word2vec.load_word2vec_format(vectors_text_path, binary=False)model = word2vec.load_word2vec_format(vectors_text_path, binary=False)
手套
- 从网站上下载一个手套词汇。我们选择了最大的一个,因为它找到我们所有单词的几率更高。您可以选择想要下载它的位置,但是为了方便起见,最好将它存储在工作目录中。
- 手动加载手套词汇。
embeddings_dict = {}
with open("../glove/glove.6B.300d.txt", 'r', encoding="utf-8") as f:
for line in f:
values = line.split()
word = values[0]
vector = np.asarray(values[1:], "float32")
embeddings_dict[word] = vector
空间
加载大空间词汇:
nlp = spacy.load(‘en_core_web_lg’).
3。提取字段名称✅
OpenAPI 规范名称的完整列表可以从scripts/fetch-list.sh文件中获得,或者通过使用以下函数(对于 Windows)获得:
def getListOfFiles(dirName):
listOfFile = os.listdir(dirName)
allFiles = list()
for entry in listOfFile:
fullPath = posixpath.join(dirName, entry)
if posixpath.isdir(fullPath):
allFiles = allFiles + getListOfFiles(fullPath)
else:
allFiles.append(fullPath)
return allFiles
另一件大事是从我们的 OpenAPI 规范中获取字段名。为此,我们将使用 openapi 类型库。让我们定义一个get_fields函数,它采用 OpenAPI 规范并返回一个字段名列表:
def get_fields_from_schema(o: Schema) -> Sequence[str]:
return [
*(o['properties'].keys() if ('properties' in o) and (type(o['properties']) == type({})) else []),
*(sum([
get_fields_from_schema(schema) for schema in o['properties'].values() if not ('$ref' in schema) and type(schema) == type({})], []) if ('properties' in o) and ($ *(get_fields_from_schema(o['additionalProperties']) if ('additionalProperties' in o) and (type(o['additionalProperties']) == type({})) else []),
*(get_fields_from_schema(o['items']) if ('items' in o) and (type(o['items'] == type({}))) else []),
]
def get_fields_from_schemas(o: Mapping[str, Union[Schema, Reference]]) -> Sequence[str]:
return sum([get_fields_from_schema(cast(Schema, maybe_schema)) for maybe_schema in o.values() if not ('$ref' in maybe_schema) and (type(maybe_schema) == type({}))], [])
def get_fields_from_components(o: Components) -> Sequence[str]:
return [
*(get_fields_from_schemas(o['schemas']) if 'schemas' in o else []),
]
def get_fields(o: OpenAPIObject) -> Sequence[str]:
return [
*(get_fields_from_components(o['components']) if 'components' in o else []),
]
恭喜你。现在我们的数据集准备好了。
4。令牌化密钥✅
字段名可能包含标点符号,如_和-符号,或骆驼大小写单词。我们可以把这些单词分割成称为记号的小块。
下面的camel-case函数识别这些 camel case 单词。首先,它检查是否有任何标点符号。如果是,那就不是骆驼案。然后,它检查单词中是否有大写字母(不包括第一个和最后一个字符)。
def camel_case(example):
if any(x in example for x in string.punctuation)==True:
return False
else:
if any(list(map(str.isupper, example[1:-1])))==True:
return True
else:
return False
下一个函数camel_case_split将 camel case 单词拆分成多个片段。为此,我们应该识别大写字母,并标记出大小写变化的地方。该函数返回拆分后的单词列表。例如,字段名BodyAsJson转换成列表[‘Body’, ‘As’,'Json']。
def camel_case_split(word):
idx = list(map(str.isupper, word))
case_change = [0]
for (i, (x, y)) in enumerate(zip(idx, idx[1:])):
if x and not y:
case_change.append(i)
elif not x and y:
case_change.append(i+1)
case_change.append(len(word))
return [word[x:y] for x, y in zip(case_change, case_change[1:]) if x < y]
这个camel_case_split函数然后被用在下面的记号化算法中。这里,我们首先检查单词中是否有标点符号。然后,我们把这个词分成几部分。有可能这些片段是骆驼大小写单词。如果是这种情况,我们可以把它分成更小的块。最后,在拆分每个元素后,整个列表被转换为小写。
def tokenizer(mylist):
tokenized_list=[]
for word in mylist:
if '_' in word:
splitted_word=word.split('_')
for elem in splitted_word:
if camel_case(elem):
elem=camel_case_split(elem)
for el1 in elem:
tokenized_list.append(el1.lower())
else:
tokenized_list.append(elem.lower())
elif '-' in word:
hyp_word=word.split('-')
for i in hyp_word:
if camel_case(i):
i=camel_case_split(i)
for el2 in i:
tokenized_list.append(el2.lower())
else:
tokenized_list.append(i.lower())
elif camel_case(word):
word=camel_case_split(word)
for el in word:
tokenized_list.append(el.lower())
else:
tokenized_list.append(word.lower())
return(tokenized_list)
tokenizer(my_word)
5。创建字段名为✅的数据集
现在,让我们用所有规范中的字段名创建一个大数据集。
下面的dict_dataset函数获取文件名和路径列表,并打开每个规范文件。对于每个文件,get_field函数返回一个字段名列表。一些字段名称可能在一个规范中重复。为了消除这种重复,让我们使用list(dict.fromkeys(col))将字段名列表从列表转换到字典,然后再转换回来。然后我们可以标记列表。最后,我们创建一个字典,以文件名作为键,以字段名列表作为值。
def dict_dataset(datasets):
dataset_dict={}
for i in datasets:
with open(i, 'r') as foo:
col=algo.get_fields(yaml.safe_load(foo.read()))
if col:
mylist = list(dict.fromkeys(col))
tokenized_list=tokenizer(mylist)
dataset_dict.update({i: tokenized_list})
else:
continue
return (dataset_dict)
6。测试嵌入✅
代码 2vec 和手套
现在我们可以找出词汇表外的单词(not_identified_c2v)并统计这些单词占 code2vec 词汇表的百分比。以下代码也适用于 GloVe。
not_identified_c2v=[]
count_not_indent=[]
total_number=[]
for ds in test1:
count=0
for i in data[ds]:
if not i in model:
not_identified_c2v.append(i)
count+=1
count_not_indent.append(count)
total_number.append(len(data[ds]))
total_code2vec=sum(count_not_indent)/sum(total_number)*100
空间
空间词汇是不同的,所以我们需要相应地修改我们的代码:
not_identified_sp=[]
count_not_indent=[]
total_number=[]
for ds in test1:
count=0
for i in data[ds]:
f not i in nlp.vocab:
count+=1
not_identified_sp.append(i)
count_not_indent.append(count)
total_number.append(len(data[ds]))
total_spacy=sum(count_not_indent)/sum(total_number)*100
对于 code2vec、GloVe 和 spaCy,未识别单词的结果百分比分别为3.39, 2.33, 2.09。由于每个算法的百分比相对较小且相似,我们可以做另一个测试。
首先,让我们用所有 API 规范中应该相似的单词创建一个测试字典:
test_dictionary={'host': 'server',
'pragma': 'cache',
'id': 'uuid',
'user': 'client',
'limit': 'control',
'balance': 'amount',
'published': 'date',
'limit': 'dailylimit',
'ratelimit': 'rate',
'start': 'display',
'data': 'categories'}
对于 GloVe 和 code2vec,我们可以使用 gensim 库提供的similar_by_vector方法。spaCy 还没有实现这个方法——但是我们可以自己找到最相似的单词。
为此,我们需要格式化输入向量,以便在距离函数中使用。我们将在字典中创建每个键,并检查相应的值是否在 100 个最相似的单词中。首先,我们将格式化词汇表,以便在distance.cdist函数中使用。这个函数计算词汇表中每对向量之间的距离。然后,我们将从最小距离到最大距离对列表进行排序,取前 100 个单词。
from scipy.spatial import distance
for k, v in test_dictionary.items():
input_word = k
p = np.array([nlp.vocab[input_word].vector]) closest_index = distance.cdist(p, vectors)[0].argsort()[::-1][-100:]
word_id = [ids[closest_ind] for closest_ind in closest_index]
output_word = [nlp.vocab[i].text for i in word_id]
#output_word
list1=[j.lower() for j in output_word]
mylist = list(dict.fromkeys(list1))[:50]
count=0
if test_dictionary[k] in mylist:
count+=1
print(k,count, 'yes')
else:
print(k, 'no')
结果总结在下表中。spaCy 显示单词“client”在前 100 个与单词“user”最相似的单词中。它对于几乎所有的 OpenAPI 规范都是有用的,并且可以用于 OpenAPI 规范相似性的未来分析。单词“balance”的向量接*单词“amount”的向量。我们发现它对支付 API 特别有用。

结论
我们已经为 OpenAPI 规范尝试了三种不同的单词嵌入算法。尽管这三个词在这个数据集上都表现得很好,但是对最相似的词的额外比较表明 spaCy 更适合我们的情况。
spaCy 比其他算法更快。与 GloVe 或 code2vec 词汇表相比,spaCy 词汇表上传速度快五倍。然而,缺乏内置函数——比如similar_by_vector和similar_word——是使用这种算法的一个障碍。
此外,spaCy 适用于我们的数据集这一事实并不意味着 spaCy 对世界上的每个数据集都更好。因此,请随意为您自己的数据集尝试不同的单词嵌入,并在评论中让我们知道哪一种更适合您!
感谢阅读!
词向量和降维
将高维数据转换为低维数据并开始使用自动编码器

马库斯·斯皮斯克在 Unsplash 上的照片
所以这是我的第一篇媒体文章,我写这篇文章是为了从我在自然语言处理领域的研究冒险中休息一下,主要是基于方面的情感分析(ABSA)和机器翻译,所以今天你会读到自然语言理解中最基本的概念之一,即词向量和维数约简(DR),我们将主要关注 DR,然后讨论自动编码器,因为它们非常流行,并且当前的系统在某种程度上基于相同的思想。首先,我们将深入了解一些历史,然后逐步转向事情本身。
词语和语义简史
所以,如果你读过一些关于单词和语法的知识,你可能会遇到这句名言"你应该知道它所保持的公司的一个单词",作者是弗斯,1957 年,这是我们大部分自然语言知识的基础,也很有逻辑性。一个词在使用时应该和它周围的词有关系。在更高的层面上,我们可以看到,在进行语义和句法分析时,某些词类跟随某些词类,如在句子“她承诺支付账单”中,下面代表的是词性标签,你经常会看到这种结构,介词跟随动词,动词跟随限定词。但这完全是基于句子,我们的语言比未来更模糊。

标有词性标签的例句
所以一个更好的策略是用向量的形式来表示一个句子中的单词,其中一个很容易理解的向量是“一键编码”,我们在单词出现的地方加 1,所以这是一个长 n×1 的向量,其中 n 是句子的长度。所以这个向量将会非常稀疏和巨大,这是不切实际的。所以我们通常使用两种类型的向量,术语文档矩阵和术语上下文矩阵,我们将在下一节讨论。
矩阵重装
让我们从术语文档矩阵开始,这与我们的一次性编码非常相似,但它很简短,因为我们只查看对我们重要的单词。因此,下面的矩阵显示了列中单词和行中文档的计数向量。

突出显示的部分是剧中“如你所愿”的字数。
所以现在如果我们推断这张表,我们就能很好地了解单词之间的相似性。这个想法是,如果单词是相似的,如果他们的计数足够接*。

这说明傻子和小丑很亲*,战斗和士兵也是
我并不反对小丑,但这些都是事实。但是这个模型并不实用,因为从庞大的数据集中提取单词并创建这样一个矩阵会花费很多时间,而且也不实用。我提到的另一个矩阵是术语上下文矩阵。现在,我们不再使用完整的文档,而是使用一些段落来定义单词的上下文。这里是我们的“文集”。
1。等量的糖,一片柠檬,一汤匙 杏 蜜饯或果酱,丁香和肉豆蔻各一撮。
2。让他们在船上享受。她小心翼翼地品尝了她的第一个 菠萝 和另一种水果,她把它们的味道比作。
3。属于递归类型,非常适合在数字计算机上编程。在寻找最优 R 阶段策略时。
4。对商业产生实质性影响,目的是收集本第一部分授权的研究所需的数据和信息。
粗体字是我们的 so 目标,我们必须找到类似的单词。所以让我们用上下文做一个矩阵。

在这里,我们可以立即看到,杏和菠萝相似是有道理的,但这对机器来说有什么意义,我们如何将它扩展到更大的集合。所以现在我们必须使用 PMI(逐点突变信息)来获得单词之间的相似度:

这给出了单词一起出现的概率。
这个函数现在做的是,得到单词在上下文中存在的概率,除以单词独立出现的概率。但是为什么呢?因为这将给出相似性的真正本质,即如果单个事件的数量较低,并且总体相对较高,则日志内部的项较大,PMI 较高,相似性也较高。神秘对数函数包装了逆概率。PMI 的另一个变化是 PPMI,它用零代替 PMI 的负值。我们可以做的另一个变化是添加 K 拉普拉斯*滑,它不会导致负值或无关紧要的值。我们可以更进一步,通过余弦距离计算这些单词之间的距离,余弦距离由下式给出

这里 Vi 是单词 V 在 W 的上下文中的 PMI
现在这将告诉我们最终的相似性。
尺寸以及为什么要缩小尺寸
因此,我们人类是复杂的,但我们不会想象超过 3 个维度,如果你想阅读更多关于这个的内容点击这里我只是觉得它太酷了,但《阿凡达》也给出了一个很好的想法。让我们来谈谈这些维度,当我们谈到结构时,它指的是 x,y,z 轴,但这里我们没有任何物理结构,这里我们有数据,数据中的高维意味着有一些不直接可见的信息。所以这就是为什么我们减少它来得到直接依赖和更小的矩阵。尽管这在应用线性代数领域中被广泛研究,但它在 NLP 和一般机器学习领域中非常重要。
一个小小的免责声明,通过这篇文章,我希望给你一些自动编码器背后的直觉,但是如果你想了解更多关于降维的数学知识,请参考我的一位教授的 论文 。
潜在语义分析(LSA)
在这一节中,我将尝试提供 LSA 背后的一点直觉,它是进入自动编码器的一种踏脚石。所以 LSA 是基于一个截断的 SVD(单值分解),如果你知道它在推荐系统中被广泛使用。如果你不知道,这是他们背后的想法,所以你拿一个矩阵,你把它转换成 3 个不同的矩阵,每一个都给出了初始矩阵的一些信息,所以,这里你可以看到我真的打破了我的矩阵,以便更直观地理解它。

关于这些矩阵的一个事实是,它们是分层排列的,这意味着它们从最重要的开始排列。那么我们为什么需要这个呢?为了回答这个问题,我们将使用前面给出的术语上下文方法的例子。因此,如果我们计算 SVD,我们将得到这些矩阵:

a、b、c 分别对应于 u、s、V^T 的值
现在,如果我们截断,只看第一个矩阵的前 2 列和所有行,即 b 的前 2×2 子矩阵,然后乘以它,我们得到:

现在这些值告诉我们的是相似性,所以如果你仔细看,前两行是相同的,这意味着我的前两个单词是相似的,后两个也是,它们产生一个正值。现在有两件事提出了一个问题,我是如何选择 2 的,对于截尾,我们是如何得到较远的值的,因为-1.4142 和 1.7486 很接*,但这两个词似乎不相似。第一个问题的答案是,你可以有任何值,而不是两个值,它只取决于矩阵的大小,你也可以尝试绘制性能图,以获得最佳值。

在这里,我们可以看到两种情况,因此最好使用 hit 和 trial。
现在回答第二个问题,我们实际上应该使用我们之前谈到的 PMI 矩阵来获得更好的结果。
最终解码我们的自动编码器
再来看我们的第一个深度学习降维方法。这个自动编码器非常酷的一点是,它基于无监督学习的原理工作,我们稍后会讲到。因此,autoencoder 有 2 层和编码器(duh)和一个解码器。

这看起来有点像一个瓶颈(来源)。
在这个模型中,我们可以看到,我们输入计数向量,进行一系列运算,得到一个与向量维数相同的值。实际情况是,你看到的这些层基本上是最小化我的原始计数向量的操作,也就是说,它们正在编码,解码层试图利用瓶颈重新创建原始输入。重建的唯一方法是在我们从瓶颈转移到解码层时使用 back prop。对这种方法一个改进是执行 LDA 以实质上压缩输入向量。
结论:
虽然我们在这里更多地讨论了自然语言方面的事情,但降维也是计算机视觉中的一个问题(这里是),自动编码器仍然发挥着作用(更多信息)。我希望这篇文章已经很好地解释了维度是如何工作的,以及为什么需要降维,但是这仅仅是朝着这个方向迈出的一步。对于自动编码器的工作来说,还有更多的数学降维是不需要的。要了解更多关于自动编码器的信息,请查看致谢部分。
致谢:
[## 大数据降维技术分析——IEEE 期刊杂志
由于数字化,医疗保健、生产等多个行业都产生了大量数据
ieeexplore.ieee.org](https://ieeexplore.ieee.org/document/9036908) [## 无监督特征学习和深度学习教程
到目前为止,我们已经描述了神经网络在监督学习中的应用,其中我们将训练标记为…
ufldl.stanford.edu](http://ufldl.stanford.edu/tutorial/unsupervised/Autoencoders/)
http://proceedings.mlr.press/v27/baldi12a/baldi12a.pdf
词向量和语义
观点挖掘还是情感人工智能
Word2vec 是一个处理文本的双层神经网络。它的输入是文本语料库,输出是一组向量,这些向量本质上是该语料库中单词的特征向量。Word2vec 的目的和用途是在向量空间中对相似词的向量进行分组。它用数学方法检测相似性。它创建向量,这些向量是单词特征的分布式数字表示,例如单个单词的上下文,并且它在没有人工干预的情况下完成。
给定足够的数据、用法和上下文,Word2vec 可以根据单词过去的出现对其含义做出高度准确的猜测。这些猜测可以用来建立一个单词与其他单词的联系,就像男人对于男孩就像女人对于女孩一样。
Word2vec 根据输入语料库中与单词相邻的其他单词来训练单词。它通过两种方式实现,或者使用上下文来预测目标单词,或者使用单词来预测目标上下文,这被称为 skip-gram。

它们都是彼此相反的。
CBOW 方法我们有几个输入单词,然后我们的投影基本上是试图预测在给定这些周围单词的上下文的情况下,出现的概率最高的单词是什么。另一方面,Skip-gram 方法需要更长的时间来训练和开发,因为它本质上是在做相反的事情。给定使用自动编码器神经网络投影的单个单词的输入,尝试输出将在该输入单词的上下文周围出现的其他单词的加权概率
我们还必须记住,每个单词都由一个向量来表示。这意味着我们可以使用余弦相似度来衡量单词向量之间的相似程度

向量值
那么单词向量是什么样子的呢?因为 spaCy 采用 300 维,所以单词向量被存储为 300 个元素的数组。
注意,我们会看到与 en_core_web_md 和 en_core_web_lg 相同的一组值,
*# Import spaCy and load the language library*
**import** **spacy**nlp = spacy.load('en_core_web_lg')
*# make sure to use a larger model!*#or*# Import spaCy and load the language library*
**import** **spacy** nlp = spacy.load('en_core_web_md')
*# make sure to use a larger model!*doc = nlp(u'The quick brown fox jumped over the lazy dogs.')
doc.vector
这将返回一个大数组。(参见 GitHub 链接中的输出)
识别相似向量
公开向量关系的最佳方式是通过。文档标记的 similarity()方法。
我们知道狮子和猫有一点相似之处,因为它们都是一个大家庭的成员。此外,猫和宠物之间也有关系,因为猫在世界各地大多作为宠物饲养
*# Create a three-token Doc object:*
tokens = nlp(u'lion cat pet') *# Iterate through token combinations:*
**for** token1 **in** tokens:
**for** token2 **in** tokens:
print(token1.text, token2.text, token1.similarity(token2)) Output-lion lion 1.0
lion cat 0.526544
lion pet 0.399238
cat lion 0.526544
cat cat 1.0
cat pet 0.750546
pet lion 0.399238
pet cat 0.750546
pet pet 1.0
我们看到我们得到了一些正确的和可理解的结果。
对立面不一定是不同的
意思相反但经常出现在相同上下文中的单词可能有相似的向量。
在这里,我们可以认为喜欢、爱和恨有完全不同的含义,但是如果我们在一个句子中一起使用它们,它是有意义的,并且模型可以识别它。
*# Create a three-token Doc object:*
tokens = nlp(u'like love hate') *# Iterate through token combinations:*
**for** token1 **in** tokens:
**for** token2 **in** tokens:
print(token1.text, token2.text, token1.similarity(token2)) Output-like like 1.0
like love 0.657904
like hate 0.657465
love like 0.657904
love love 1.0
love hate 0.63931
hate like 0.657465
hate love 0.63931
hate hate 1.0
向量范数
有时将 300 个维度聚合成一个欧几里德(L2)范数会很有帮助,这个范数被计算为矢量*方和的*方根。这可作为。vector_norm 令牌属性。其他有用的属性包括。has_vector 和 is_oov 或出词汇。
比如我们的 685k 矢量库可能没有“ nargle 这个词。要测试这一点:
tokens = nlp(u'dog cat nargle')**for** token **in** tokens:
print(token.text, token.has_vector, token.vector_norm, token.is_oov) Output-dog True 7.03367 False
cat True 6.68082 False
nargle False 0.0 True
事实上,我们看到“nargle”没有向量,所以 vector_norm 值为零,它被标识为词汇表之外的。
向量运算
信不信由你,我们可以通过加减相关向量来计算新的向量。一个著名的例子表明
"queen" - "woman" + "man" = "king"
我们来试试吧!
**from** **scipy** **import** spatial
cosine_similarity = **lambda** x, y: 1 - spatial.distance.cosine(x, y)
queen = nlp.vocab['queen'].vector
woman = nlp.vocab['woman'].vector
man = nlp.vocab['man'].vector *# Now we find the closest vector in the vocabulary to the result of "man" - "woman" + "queen"*
new_vector = queen - woman + man
computed_similarities = [] **for** word **in** nlp.vocab:
*# Ignore words without vectors and mixed-case words:*
**if** word.has_vector:
**if** word.is_lower:
**if** word.is_alpha:
similarity = cosine_similarity(new_vector, word.vector)
computed_similarities.append((word, similarity))
computed_similarities = sorted(computed_similarities, key=**lambda** item: -item[1])
print([w[0].text **for** w **in** computed_similarities[:10]])['queen', 'king', 'kings', 'queens', 'prince', 'lord', 'throne', 'royal', 'god', 'monarch']
你可以在下面提供的我的 Github 库中查看完整的代码。
通过在 GitHub 上创建一个帐户,为 aditya-beri/词向量和语义开发做出贡献。
github.com](https://github.com/aditya-beri/Word-Vectors-and-Semantics.git)
结论
在这篇博客中,我们学习了如何进一步进行机器学习,并试图从复杂的短语中提取出想要表达的意思。一些简单的例子包括:
- 数据科学相对容易学。
- 那是我看过的最好的电影。
然而,像这样的短语会让事情变得更难:
- 我不讨厌绿鸡蛋和火腿。(需要否定处理)
实现的方式是通过复杂的机器学习算法,如 word2vec 。目的是为大型语料库中的每个单词创建数字数组或单词嵌入。每个单词都被赋予了自己的向量,在这种方式下,在同一上下文中频繁出现的单词被赋予了相互靠*的向量。结果是一个模型可能不知道“狮子”是一种动物,但知道“狮子”在上下文中比“蒲公英”更接*“猫”。
需要注意的是构建有用的模型需要很长时间——训练一个大的语料库需要几个小时或几天——为了我们的目的,最好是导入一个现有的模型,而不是花时间训练我们自己的模型。
这只是对什么是语义和词向量以及它们如何工作的一个小小的窥探。
如有任何疑问和澄清,请随时回复本博客。
Word2Vec 至变压器
单词嵌入的演变,来自 CS224n 的注释。

你的论点是合理的,完全合理
介绍
开发有意义的单词表示是 NLP 成立以来的主要目标之一。在 2010 年代,这一基础性任务一直是该领域进步和创新的主要驱动力之一。在这篇文章中,我将介绍完成这项任务的一些主要方法,以及大大提高我们在不同上下文中捕捉词义和相似性的能力的主要观点。
一袋单词
解决这个问题最简单的方法叫做词汇袋。这种方法为文本中出现的每个单词分配一个唯一的标记,通常是一个数字。因此,例如短语“你的论点是声音,除了声音什么都没有”将被表示为“1-2-3-4-5-6-4”。通过这种基线方法,我们可以捕捉到单词之间同一性的概念,也就是说,当一个单词被多次使用时,我们可以识别出来。此外,使用像 Tf-Idf 这样的使用单词包的技术,我们可以在一定程度上成功地测量文档之间的相似性,只要基于哪些单词在文档中使用以及使用的频率。使用像 WordNet 这样的资源,一个支持的字典,我们也可以发现文本中单词的多种含义,并将字典中列为同义词的那些连接起来。但是这种表示本身并没有捕捉到单词的相似性,也没有捕捉到单词在我们的文本中被使用的特定意义。
Word2Vec (CBOW 或 Skip-Gram)
可以说,NLP 在 2010 年代早期的最大发展是 Word2Vec,这是一种无监督的学习技术,用于学习单词的连续表示。在许多方面,Word2Vec 建立在 BoW 的基础上,但它不是为单词分配离散的标记,而是为训练语料库中的每个单词学习连续的多维向量表示。更具体地说,它通过学习预测给定一个中心单词在其周围固定大小的窗口中最有可能出现的单词来做到这一点 (Skip-Gram) 。

Word2Vec 示例
或者通过学习根据上下文单词(连续单词袋)预测中心词。像许多其他机器学习技术一样,Word2Vec 使用梯度下降来最小化整个语料库的交叉熵损失,即预测错误单词的概率。实际上,基本思想是在中心词和外部词的任何给定配置下,最大化给定中心词时预测外部词的条件概率。

表达式最大化,其中 u_o 是中心词上下文中外部词之一的向量表示,v_c 是中心词的表示。直观上,向量积是线性代数中相似性的度量,因此我们想要最大化当前外部单词和中心单词之间的相似性,该相似性基于中心单词相对于语料库中所有单词的相似性的总和来归一化。指数是用来使一切都为正的。
现在,如果我们想在所有语料库中最大化这种表达,我们需要逐步学习更好地捕捉单词之间相似性的向量。因此,Word2Vec 通过设计来捕捉我们语料库中单词的相似性,这要归功于一个单词在向量空间中与其他单词有多远或多*的概念,即词义的概念。

经典的国王-男人+女人=女王 Word2Vec 如何捕捉相似性的直觉。
然而,这种相似性的概念只能带我们到此为止。Word2Vec 的主要问题是,它为一个词提供了一种单一的表示,无论上下文如何,这个词都是相同的。因此,像“bank”这样有几个不同含义的词,例如 river bank 和 investment bank,最终会有一个表示,它是没有很好地表示任何一个含义的*均表示。
上下文单词嵌入(ELMo)
基于单词出现的上下文来证明每个单词有多个表示是上下文单词嵌入背后的核心思想。这个想法是使用 RNN 语言模型来实现的,该模型以无监督的方式类似于 Word2Vec 来训练。更具体地,我们使用 RNNs 来预测,给定句子中的当前单词,下一个单词。我们使用这些网络是因为它们有能力在隐藏状态下捕获和维护长期依赖关系。

隐藏状态(红色)可以保持关于句子中前一个单词的信息
其思想是,在输入当前单词后,我们可以将隐藏状态连接到通常的 Word2Vec 表示,以维护当前单词和过去上下文的信息。
第一个实现这个想法的系统是 Peters 和 co .的 TagLM。

TagLM 使用预训练的双 LSTM 语言模型来产生单词嵌入的“上下文部分”,该单词嵌入连接到 Word2Vec 矢量或更复杂的字符级 CNN/RNN 生成的单词表示。这种表示现在是新的嵌入,有效地取代了 NLP 管道中的 Word2Vec 或 GloVe 向量。
ELMo 嵌入的工作方式非常相似,主要区别在于 ELMo 对预训练语言模型使用两层双 LSTM,而嵌入连接是一种可学习的,在微调期间,两层的组合将针对特定任务进行优化。ELMo 还完全抛弃了 Word2Vec,只依赖字符级的 CNN/RNNs 作为单词表示的第一部分。
变形金刚(伯特,GPT)
训练单独的语言模型以产生更好的上下文单词表示的想法已经被证明在许多 NLP 任务中非常成功地改善了 SOTA,但是 RNN 语言模型由于其循环、顺序的性质而倾向于训练缓慢并且非常难以并行化。因此,在 2017 年,Aswani 和他的同事开发了一种非递归替代 RNNs 的方法,其核心是变压器块。

变压器架构的编码器
Transformer 的主要特点是它使用注意力(seq2seq 架构中有助于翻译对齐的概念)来捕捉句子中单词之间的关系,类似于卷积的方式。就像卷积一样,我们可以让多个注意力头来计算每个单词应该将注意力集中在哪里,以及注意力所代表的关系。然而,注意力不同于卷积,因为它捕捉空间中单词之间的相似性,在该空间中,不同头部的权重矩阵投射它们的表示。为了捕捉更远的依赖性,类似于卷积,我们可以堆叠多个变压器块。




解码器模块的工作方式有些不同,但是你所需要的是引起注意的图像纸张让一切变得更加清晰。
BERT 使用 transformer block 来训练一个使用屏蔽技术的语言模型,其中系统的任务不是猜测下一个单词,而是猜测句子中屏蔽掉的一个单词。

这样,它能够使用整个上下文进行预测,而不仅仅是左上下文。
这些笔记是对 CS224n Stanford 课堂第 13 和 14 课的最基本的总结。这些材料的学分属于克里斯·曼宁教授和这门课的助教。
更多资料可在 课程网站 和YouTube上获得。
构建神经网络时,更聪明地工作,而不是更努力
用一个简单的例子来说明神经网络的设计原则
应用数学中的一个基本技巧是找到一个坐标变换,将一个困难或不可能的问题转化为一个简单的问题。也许我最喜欢的例子是圆的方程。如果我们在笛卡尔坐标中写下单位圆的方程,我们可以将圆的几何概念表达为隐函数:

当我们试图通过求解 y 得到一个显式表达式时,情况会变得更糟。

现在我们必须从*方根的两个分支(圆的上半部分和下半部分)拼凑这个简单的函数。超越美学使用这种圆的表示法会使我们看不到一个完美的圆所固有的几何简单性。相比之下,如果我们用极坐标来表示同样的数学对象,那么单位圆就变得非常容易处理。

点的极坐标。
在极坐标中,我们的单位圆采用简单的形式 r=1,θ ∈ [0,2π]。所以在极坐标中,圆是矩形的(高=1,宽=2π)。笛卡尔坐标是矩形的自然坐标,极坐标是圆形的自然坐标。在本文中,我将应用这种思路来构建简单的神经网络,并构建一个说明选择正确坐标的重要性的示例。
曲线拟合的人工神经网络
如果你正在阅读这篇文章,那么你可能听说过人工神经网络(ANN)。在这次讨论中,我们将集中讨论最简单的人工神经网络,称为前馈网络。简而言之,人工神经网络只是通过将简单的非线性函数(层)链接在一起而构建的网络(函数)。

前馈神经网络
通过将(许多)这些简单的层链接在一起,通用逼*定理告诉我们,我们可以将任何(好的)函数表示为具有有限深度和宽度的人工神经网络。在开始的极坐标例子中,这就像说我们可以在极坐标中写下(x,y)*面中的每一点(没有洞)。这是一个很好的必要属性,可以确保我们在改变坐标时不会错过任何东西,但是它没有告诉我们两件事:
- 如何找到实际坐标?
- 如果这是表达信息的好方法。
对于人工神经网络来说,“坐标是神经网络的参数(神经网络中每一层的权重和偏差)。对于神经网络,我们没有快速的数学公式来找到这些坐标,而是使用优化来最小化损失函数。损失函数测量我们指定的函数和训练数据之间的距离。
让我们看一个简单的例子,用神经网络来构建正弦函数的*似值。
使用神经网络拟合正弦函数
让我们应用这种强大的函数逼*方法来构建简单函数 sin(x)的神经网络版本。我将使用用 Julia 编写的奇妙的神经网络库 Flux。这个软件包为构建神经网络提供了一个非常简单而强大的框架。它还增加了很少的额外语法来构建神经网络,并让我们专注于基本的构建模块。我将在文章中包含一些代码片段。完整代码请查看github repo。
下面的代码使用一个标准的非线性函数 tanh 构建了一个具有两个隐藏层的简单网络。
ann = Chain(Dense(1,20,tanh),Dense(20,20,tanh),Dense(20,1));
我们可以把这个神经网络想象成:

学习正弦函数的简单前馈神经网络
上图显示我们有一个输入和输出值,中间有两个层。这些层被称为隐藏层,因为如果您仅跟踪输入和输出,它们是不可见的。回想一下,该图用于表示某个函数族,其中通过固定参数来指定特定的成员。

上述神经网络的函数形式。参数由两个权重矩阵 W 和偏置向量 b 给出。参数 C 给出线性输出层的截距。
好,所以我们希望,我们可以用这个函数族中的某个成员来*似表示函数 sin(x)。为了尝试找到最接*这个的成员,我们应该使用一些训练数据来最小化损失函数。
function loss(x, y)
pred=ann(x)
loss=Flux.mse(ann(x), y)
#loss+=0.1*sum(l1,params(ann)) #l1 reg
return loss
end[@epochs](http://twitter.com/epochs) 3000 Flux.train!(loss,params(ann), data, ADAM())
拟合我们的神经网络后,我们可以看到它对训练数据和一组测试数据有多好。

具有 tanh 非线性的前馈神经网络适合 3000 个时期的训练数据,如蓝色圆圈所示。测试数据显示为绿色十字。
显然,这种神经网络在捕捉信号的基本周期特性方面表现不佳。对测试数据(绿色)的异常差的概括突出了这一点。这不仅仅是我的网络。
哪里出了问题?经常被引用的通用逼*定理告诉我们,我们可以使用有限深度和宽度的前馈神经网络来表示这个函数。然而,正如我经常重新发现的——有限仍然可以非常大。仅仅因为存在一些权重和偏差,可以用来从典型的神经网络组件中构建这个函数,并不意味着我们可以很容易地找到它们。
我们可以尝试使用蛮力,建立更大的网络或训练更长的时间,等等。然而,一个非常简单的改变会在很短的时间内给我们一个*乎完美的*似值。
更好的主意
给定这个数据,我们可以看到它是周期性的。现在,我们还没有将那条信息包含在我们的神经网络设计中。不使用双曲正切非线性函数,让我们使用正弦函数。这样,第一层的输出将是周期性的。
下图显示了该系统更好的*似网络的想法。

具有正弦非线性神经网络。
以函数形式写出,我们使用的函数族采用以下形式:

其中 Q 是隐藏层中神经元的数量。这在 Flux 中很容易做到:
Q = 20;
ann = Chain(Dense(1,Q,sin),Dense(Q,1));
让我们试试这种新型号,看看能否找到更好的型号。

更好的神经网络基础
这是一个更好的模型,因为它抓住了周期性的本质。然而,请注意,我们仍然在训练和测试数据上略有偏差。这是因为我们的模型实际上仍然有一些不需要的额外自由度。我们可以通过对参数应用一些正则化来解决这个问题,但在这种情况下,我们可以求助于一些非常酷的数学来寻求解决方案。

让·巴普蒂斯特·约瑟夫·傅立叶 1768–1830
上述神经网络的函数形式非常接*于傅立叶级数的形式。傅立叶级数是一个基(一组坐标),可以用来描述有限域[a,b]上所有的好的函数。如果一个函数是周期的,那么有限域上的描述可以扩展到整条实直线上。因此,傅里叶级数常用于周期函数。
傅立叶级数采用以下形式:

我们的神经网络和傅立叶级数之间的唯一区别是,我们的神经网络允许权重 W 变化,而傅立叶只有 A 系数和偏差(b)项,可以选择它们来拟合函数。
在 Flux library 中,从可训练的参数集中移除第一层中的那些权重参数是足够容易的。通过将它们设置为整数值,我们实际上可以创建一个神经网络,其中是傅立叶级数。
# Fourier Series ANNQ = 20;
ann = Chain(Dense(1,Q,sin),Dense(Q,1));function loss(x, y)
pred=ann(x)
loss=Flux.mse(ann(x), y)
return loss
endopt = ADAM()ps=params(ann)for j=1:20
ann[1].W[j]=j
end
delete!(ps, ann[1].W) #Make the first layers weights fixed[@epochs](http://twitter.com/epochs) 3000 Flux.train!(loss,ps, data, opt)
以下是使用 sin(x)函数和傅立叶人工神经网络的拟合:

傅立叶人工神经网络方法推广良好。
如果你想知道这种方法对其他更复杂的周期函数也同样适用。考虑一个混有一些高次谐波的周期信号。


更复杂周期信号的傅立叶级数神经网络的第二个例子。
一些外卖课程
这个简单的例子为 ANN 的工作方式以及我们如何改进我们的模型提供了一些重要的见解。
- 你的神经网络的设计会对结果产生巨大的影响。问题和领域的具体知识会产生巨大的影响。
- 确保你所知道的关于问题的一切都被传送到神经网络。这里我们看到,告诉函数产生一个周期信号比增加网络规模或训练时间做得更多。
- 按照上一课的思路,领域知识很重要。
- 应用数学家和物理学家已经研究函数逼*问题很长时间了。傅立叶级数在 19 世纪 20 年代被发现用来解决热传导问题。这些技术可能值得您花时间去学习,即使您并不关心应用程序。
- 用人工神经网络计算傅立叶级数是非常愚蠢的。上世纪十大算法之一就是为了这种计算而发明的。重点是展示一个小小的改变,加入更多的领域知识,是如何极大地改善结果的。
关于构建更智能的神经网络,我推荐查看内森·库兹的作品。
担任数据科学顾问
具有企业意识的数据科学家的职业战略

资料来源:Freepik
在《哈佛商业评论》写道数据科学是“21 世纪最热门的工作”几年后,许多年轻人才现在被这一利润丰厚的职业道路所吸引。此外,大公司的高层管理人员现在几乎所有的重要决策都使用数据驱动的方法和分析工具。
随着数据驱动的决策和自动化趋势的发展,许多大公司正在采用各种数据科学工具来生成可行的建议或自动化其日常运营。咨询行业的主要功能是为其客户,主要是跨国公司,提供外部的合格人才。这些全球性公司遵循其业务增长的战略路线图,通常通过增加收入或有效管理成本来实现。为了实现这些目标,他们需要在其业务的不同领域采用人工智能和大数据技术。
另一方面,这些全球公司中有许多不一定是拥有大型数据科学团队的科技公司。因此,他们需要将数据科学开发案例外包给长期或短期的咨询公司。咨询公司和他们的客户以短期和长期项目的形式参与,这些项目可以持续几个月到几年。因此,咨询公司通过招募和培训顶级数据科学人才来满足客户的需求,并将这些人才分配到咨询项目中。
咨询是初级数据科学家的典型职业道路,他们希望获得不同行业的广泛项目经验。对于那些在大客户的许多不同项目中工作的数据科学家来说,在咨询行业工作通常是积累不同领域业务知识的一个很好的选择。通过这些项目,人们可以了解寻求数据科学解决方案的企业决策者和大公司高层管理人员的心态。人们甚至可以期待与客户的董事总经理级别的员工进行互动,向他们提供他们新的花哨的分析仪表板。
咨询公司的数据科学家可以担任许多不同的角色,具体取决于他们的能力或工作部门。咨询公司通常做两种重要的面向客户的工作;(a)在交付项目中担任数据科学家,( b)开展业务开发和销售活动。前者通常通过与软件开发、数据工程或业务分析团队合作来完成。数据科学家的日常工作包括了解客户的需求、创建数据管道、探索性数据分析、创建仪表板、构建预测模型、在企业软件中部署模型,以及将见解传达给利益相关方。对于后者,数据科学家通常从事高层次的问题解决、用例定义、撰写提案以及向潜在客户进行技术演示。
一名成功的数据科学顾问需要广泛的技能,包括领域知识、商业敏锐度、分析思维和解决问题、团队合作和项目管理、沟通和演示、机器学习、大数据和软件开发。上述技能也适用于数据科学咨询工作的面试。简而言之,你必须证明你是一名合格的数据科学家,能够使用广泛的技术。然而,你必须能够跳出框框思考,对业务有坚实的理解,并尝试利用数据科学来实现。
咨询公司也有自己的文化。工作量通常是很大的,需要敏捷和以结果为导向的工作方式才能按时交付价值。咨询工作还包括大量的旅行和与不同客户的会议。商务旅行可能一周长达 4 天,这意味着顾问通常从周一到周四不在家。与其他公司的竞争也非常激烈,有时甚至在同一家公司内部也是如此。所以你也必须为一些公司政治做好准备。在咨询公司里,带着正确的期望前进是这类职业成功的关键。你必须知道你在咨询公司工作期间得到了什么,以及为此付出的代价。
有些人选择在进入咨询行业之前、期间或之后学习工商管理硕士(MBA)。尽管考虑到 MBA 学位对你的商业头脑有用,但如今,在一些大型咨询公司工作相当于上商学院。在咨询行业工作有助于你更好地了解业务,并在不同的业务流程中获得实践经验。
也有机会成为特定领域的顾问。例如,如果你做过营销分析方面的项目,并且对这个能力感兴趣,你可以选择营销分析方面的专家路径。作为特定领域的顾问,通常在一些大型咨询公司工作,但主要是在高级管理层,他们有主题专家或能力领导。另一个选择是做一名自由职业顾问,或者在你是专家的特定领域开办你的咨询机构。
总之,做数据科学顾问对你的职业有很多好处。你可以和世界上最成功的公司一起工作。你可以向周围最好的专家学习,你还可以受益于与大型咨询公司合作,这些公司为你提供了无数发展职业和技能的机会。你还必须注意到,所有这些好处都是以在高压环境下的高强度工作负荷为代价的。如果你已经为这一切做好了准备,进入数据科学咨询行业可能是你职业生涯中走向成功的最重要的一步。
关于作者:
Pouyan R. Fard 是 Fard 咨询公司的首席执行官&首席数据科学家。Pouyan 在数据科学、人工智能和营销分析方面拥有多年的公司咨询经验,从初创公司到全球公司。他曾与医药、汽车、航空、运输、金融、保险、人力资源和销售&营销行业的财富 500 强公司合作。
Pouyan 也在指导活跃在大数据行业的初创公司和人才。他的热情是通过职业培训培养下一代数据科学家,并帮助他们找到数据科学领域的顶级工作机会。
Pouyan 已经完成了关于消费者决策预测建模的博士研究工作,并对开发机器学习和人工智能领域的最先进解决方案保持兴趣。
作为一名机器学习工程师
人工智能和数据人才的职业战略

资料来源:Freepik
数据科学是一个跨学科的领域,有许多相关的领域,如机器学习或大数据。随着行业的发展,数据科学相关领域之间的相似性和区别也变得更加具体。机器学习工程是一个与数据科学非常接*的领域,在一些公司中,这两种职业道路几乎没有区别。机器学习工程到底是什么?
机器学习工程是一个复杂的软件工程领域,专注于开发智能软件,这些软件可以利用人工智能和机器学习技术的力量自动化类似人类的任务。与数据科学家相比,机器学习工程师在软件工程方面的工作多于分析方面。机器学习工程师将主要致力于将人工智能集成到软件解决方案中。现在机器学习工程师工作的最突出的领域是自动驾驶和云计算。
在 2010 年代,机器学习研究人员的注意力回到了深度学习算法。此外,训练深度学习算法变得更加可行,因为云计算技术以相对较低的价格为个人和企业提供了更高的计算和存储能力。此外,随着大数据时代社交媒体的出现,许多训练数据集可供专家们用来增强他们的机器学习模型。
对机器学习工程师的需求一直在稳步增长。几乎所有的公司,无论是公司还是初创公司,都希望在业务中采用人工智能技术。仅在 2015 年和 2018 年之间,机器学习工程职位发布数量就增长了 344%。这一增长率使机器学习工程师或人工智能专家成为 2020 年最热门的职位之一。
成为高评价的机器学习工程师有几个要求。首先,你需要计算机编程技能,如 Python 或 R,软件工程技能是绝对必须的。可靠的概率统计知识以及机器学习算法是必要的。人们需要知道如何训练机器学习模型,并将训练好的模型用于预测任务。机器学习算法应用于广泛的问题,知道哪个模型是解决每个问题最有效的模型是这项工作的关键。
机器学习框架的实践经验确实至关重要。现在,几乎所有的机器学习算法都已经在一个库或框架中实现了。一个庞大的学术和行业专家社区为传统的机器学习和深度学习算法提供了现有的资源。现有技术,如 scikit-learn、Tensorflow、Keras 和 PyTorch,以及亚马逊、微软和谷歌等大型科技公司的云解决方案,使机器学习工程师的工作变得更加简单。然而,了解在特定用例中采用这些技术的最佳实践非常重要,不容忽视。
此外,训练模型通常遵循特定的数据获取、数据准备、探索性数据分析和模型性能评估流程。为此,机器学习工程师需要完成与数据工程和数据科学相关的任务。经过训练的模型通常需要部署在操作软件和具有高复杂性级别的现有架构中。因此,软件工程的实际知识也是必需的。
从数据科学过渡到机器学习工程相对简单。一个人需要展示工程思维和快速可靠地交付实际结果的能力。这样的候选人也需要有一些领域的知识。毕竟,交付满足业务需求的有用产品是数据科学领域每项工作成功的关键。
最后,一些新兴领域已经变得如此突出,以至于它们变得独立于数据科学和机器学习工程领域。这些领域就像计算机视觉工程、语音技术工程和自然语言处理工程。
关于作者:
Pouyan R. Fard 是 Fard 咨询公司的首席执行官&首席数据科学家。Pouyan 在数据科学、人工智能和营销分析方面拥有多年的公司咨询经验,从初创公司到全球公司。他曾与医药、汽车、航空、运输、金融、保险、人力资源和销售&营销行业的财富 500 强公司合作。
Pouyan 也在指导活跃在大数据行业的初创公司和人才。他的热情是通过职业培训培养下一代数据科学家,并帮助他们找到数据科学领域的顶级工作机会。
Pouyan 已经完成了关于消费者决策预测建模的博士研究工作,并对开发机器学习和人工智能领域的最先进解决方案保持兴趣。
解决数据科学设置中的问题
数据科学团队面临的一些常见问题指南

JESHOOTS.COM在 Unsplash 上拍照
这是本博客关于 数据科学团队设置 的姊妹篇
正如许多实践中的数据科学家会告诉你的,将数据科学有效地集成到组织中的道路绝不是一帆风顺的。围绕数据科学可以提供什么有很多宣传和期望,但当处理数据问题、错误和计算限制的日常现实出现时,这些希望可能会动摇。这个博客是数据科学家或数据科学团队可能遇到的一些常见问题的指南,并就如何处理这些问题提出了一些建议。
我整天都在寻找和管理数据

你需要的数据就在这里的某个地方……(照片由 Ashim D'Silva 在 Unsplash 上拍摄)
这是最常见的问题之一,表明数据科学家在被聘用时没有充分考虑他们取得成功所需的环境和设置。对大多数组织来说,管理数据是一项持续的挑战,可靠地确保正确的数据在正确的时间位于正确的位置对于有效的数据科学至关重要。因此,要解决这个问题,我们通常需要查看我们的数据工程设置。他们足够多吗?他们有时间关注你的需求吗?
人们通常认为数据工程师和数据科学家的比例是 1:2,但实际上这取决于你需要的数据有多杂乱。如果一切都已经很好地组织成易于查询和可靠的数据存储,那么你可能只需要一个数据工程师和几个数据科学家。如果您的数据分散在各种 prem 和 cloud 存储中,并且混合了一些 excel 电子表格和 SQL server 实例,那么您可能需要更高的 1:1 比率。
我建立了这个很棒的模型,但是利益相关者讨厌它
这可能是最令人沮丧的问题之一,因为当一个有影响力的利益相关者转身说:“不”时,几周甚至几个月的工作就会化为乌有。当在范围界定过程中遗漏了关键点或需求,或者在构建算法时丢失了关键点或需求时,往往会发生这种情况。
因此,密切关注您的产品管理流程是理解这些问题发生的原因并防止其再次发生的关键。我说产品管理流程是因为虽然可能是产品管理失误,但也可能是数据科学或工程团队没有遵守产品要求,或者没有充分听取利益相关者的意见。风险承担者也会在项目接*尾声时发明新的需求,或者在项目开始时忘记提及关键点。
避免这些问题的几种方法是:
- 确保在项目开始时清楚地记录了算法要求,并且所有利益相关者都已经审查并签署了该文件
- 在可能的情况下,尽快在主要利益相关者面前获得算法输出的第一个版本。这不必是花哨的,或者使用最终算法将使用的相同模型和功能集。目的是展示“这大致就是输出的样子”。围绕初稿的讨论往往比抽象地讨论算法更有成效。
我整天都在追虫子!

不幸的是,bug 是生活中的事实,虽然我很想给出一个“魔术子弹”式的技巧来避免它们,但恐怕我不得不让你失望了。然而,错误不应该是数据科学家处理的全部,如果你发现你总是在寻找错误,所以你的交付速度变慢了,那么可能是时候看看工程设置了。
部署 ML 算法可以通过多种方式完成,从数据科学家自己构建整个管道,使用现成的或内部的 AI *台,到工程团队将数据科学家的规范直接写入软件本身。错误的性质也有很大的不同,从零星的怪异到全面的系统故障。也就是说,围绕错误数据科学的一些常见主题仍然值得讨论。
数据科学家在没有必要培训/技能的情况下编写生产代码
这并不是说数据科学家不应该编写生产代码,许多人可以而且做得很好。但是,尽管数据科学培训确实涉及大量编码,但它往往不会专注于培养伟大的软件工程师。所以如果你的算法在不断地表演,它可能需要有更相关的训练和经验的人来重写。
算法不太适合工程现实
你可以建立世界上最好的产品推荐算法,但如果它需要 20 秒才能呈现在页面上,那么客户就会成群结队地放弃这个网站。确保算法在它将运行的环境中工作良好,而不仅仅是在训练环境中,这是至关重要的,也是机器学习工程师的角色在过去几年中变得突出的原因。这里的修复可以简单到删除依赖于错误数据存储的特性,也可以复杂到重新访问算法类型和训练/评分方法。
我整天都在回答问题
算法和数据产品产生问题。效果如何?它在特定的环境下表现如何?我们应该探索不同的要素或数据集吗?这些可以来自涉众,也可以来自团队成员,因为他们寻求发展和改进算法/产品。在这里,分析的技能得到了最好的利用:快速查询、数据可视化和讲故事。
当一个算法吸引了许多相关的问题,但手头的分析资源太少(或没有)来回答这些问题时,就会出现一个常见的问题。这往往会将分析工作推给其他团队成员,如产品、数据科学和工程,从而减慢交付速度(通常不是他们的核心专业知识)。这可能会导致一个矛盾的情况,一个已知的问题没有得到解决,因为团队太忙于回答有关它的问题!
需要大量的迭代来使事情正常工作

任何算法或产品都需要一些迭代来解决问题并真正实现跨越,但这应该是一个提炼广泛好的解决方案的问题,而不是不断地让事情偏离正轨并定期回到绘图板。客户和利益相关者只有这么多的耐心。
将这类问题完全归咎于产品管理可能很有诱惑力,尽管这可能是问题的一部分,但仅仅关注这一点可能会错过更广泛的问题。虽然产品管理确实负责选择正确的工具和方法来解决给定的问题,但要使该解决方案真正发挥作用,构建该解决方案的数据科学家和工程师还需要对问题和背景有详细的了解,并彻底理解为什么选择了特定的解决方案。
当数据科学家/工程师和他们正在构建的业务或问题领域之间存在太大差距时,事情就会出错。这里有几个原因:
- 项目中人员的频繁轮换
- 不允许数据科学家和工程师参与解决方案设计和范围界定流程的流程
- 数据科学家和工程师没有时间深入了解他们所服务的客户或利益相关方。
这些问题在概念上都不难解决,但在流程和设置上做出改变可能需要时间和说服。不过,这是值得的,伟大的产品和算法是由经验丰富、知识渊博的人构建的。相反,数据科学家和工程师只是“做他们被告知的事情”,是脱离和*庸软件的配方。
摘要
在将机器学习部署到组织中时,确保您始终处于有效工作的位置,并且不被不属于关键数据科学家技能集的任务偏离,这可能是一个持续的挑战。我已经概述了如何思考这些问题,但是实际的解决方案将取决于你工作的组织的环境。本质上,每当你持续遇到阻碍你完成工作的问题时,不要只是用头撞它们,而是后退一步,看看更广泛的背景,以及你可能需要更多帮助或不同方法的地方。祝你好运!
作为数据科学家远程工作
如何让过渡变得更容易的 5 个技巧

卡斯帕·卡米尔·鲁宾在 Unsplash 上的照片
两个月前,我离开了 DC 的公寓,准备搬回家住几个月。节省房租、与老朋友重新联系、每天远程工作的想法确实很诱人。从那以后,我了解了作为一名数据科学家远程工作的现实(有好有坏)。
根据我的经验,尤其是新冠肺炎目前的情况,我想分享我所学到的,并为那些适应远程工作生活方式的人提供一些建议。
有利的方面
1.在家工作的舒适
首先也是最重要的——我每天都穿着便装去上班。再也不用担心干洗或每天早上挑选商务休闲装;我起床,穿上那天我想穿的,花更多的时间专注于我的实际工作。
我的环境也变了。我的整个“办公室”现在是一个开放的工作空间。我可以坐在沙发上、高桌旁、书桌旁,甚至是我的门廊上,这取决于我一天的心情。在家工作时,你可以获得“开放式办公室”设计的所有好处,而没有“开放式聊天”的缺点。
2.我的重点已经转移到更技术性的工作上
正如我在之前的文章中所写的,作为数据科学家,我们工作的很大一部分是与我们的客户和利益相关者互动。过去几年,我一直在*衡客户交付和技术工作,但最*决定将我的重点更多地转向数据科学的技术方面。
远程工作给了我这个机会。客户交付的责任已经转移到我团队的其他成员身上,这使得我的重点从解释和翻译业务需求转移到交付分析、可视化和模型。
3.我的工作效率和专注度更高了
我还发现,我需要参加的非必要会议的数量大幅下降。我和我的团队不会默认让我参加每个会议,而是在让我参加之前,会仔细考虑我是否有必要参加。
这导致我的注意力和精力空前高涨。我不仅把重点转移到了更具技术性的工作上,而且一整天都没那么分心了。当我即将完成我花了一个小时制作的模型时,不再有“路过”或被拍肩膀。我的时间不受干扰,这使我能够在工作日始终保持专注并完成工作。**
不利之处
1.分享想法变得越来越难
远程工作时很难与团队成员协作。我的工作方式是在团队会议上跳起来,抓起白板,和我的团队一起头脑风暴。我发现这是最有收获也是最有效率的工作方式。现在,我不得不使用虚拟白板来代替,或者在不使用视觉辅助的情况下更清楚地交流我的想法。
2.知道什么时候该休息会很困难
机会很难获得。相反,很难知道什么时候该休息了。是吃午饭的时候了吗?我怎么会知道?没有社交指标,如聊天增加、同事起身离开等。唯一知道的方法就是盯着时钟。
3.总有一种证明我在线的感觉
我经常担心回到电脑前展示“我有多努力”,而不是享受 5 到 10 分钟的咖啡休息时间或与同事快速聊天。我总是担心团队成员会想“他在线吗?”或者“他只是在浏览亚马逊、阅读新闻,还是在 Instagram 上发布模因?”。当然,这很愚蠢——任何数量的员工都会在某一天这样做,甚至在办公室里。但是,我仍然觉得远程工作时会受到更多的审查。
简化过渡的技巧
1.建立日常生活

建立一个早晨的习惯对我帮助很大。鉴于从准备工作和通勤中解放出来的时间,我现在有时间在早上做我想做的事情——醒来,享受我的咖啡,沉思,写作,慢慢过渡到我的工作心态,然后在上午 9 点登录。这一点,加上能够专注于手头的任务,每天按时下班,让我精神上重新充电,准备好每天早上开始新的一天。
我推荐顶部空间用于冥想,而大步用于常规/习惯追踪。另外,顶空是在当前新冠肺炎危机期间提供的* 免费内容 !*
2.设置边界

一天之内,在你的沙发上、餐桌上和门廊上工作是很棒的。这在一两周内很棒,但是我开始意识到我需要把我的工作空间和我的生活空间分开。幸运的是,我有一间空房可以用作办公室。
试着在你的房子里找一个特定的房间或者去咖啡店。你不必每天都这样做,但是把它混合起来,试着把你的工作和生活分开是很重要的。设定心理界限也很重要。我已经把每天下午 5 点完全关机列为优先事项。令人惊讶的是,这比我在办公室工作时容易多了。
3.关闭 Skype
我发现这非常有帮助。关闭 messenger 可以减少分心和虚拟的“路过”。我觉得 Skype 更多的是用于社交,而不是做生意,这一点在远程工作时似乎被放大了。关闭 Skype 也消除了持续在线的焦虑。如果你一直开着 Skype,你会有一种必须保持状态不空闲的感觉,这会导致很多焦虑,即使你在做一些像喝杯咖啡这样简单的事情。
我最*完全关闭了 Skype,转而使用群发短信和微软团队,这带来了同样的易用性,但压力却小了很多。这也引出了我的下一个技巧…
4.使用正确的协作工具

幸运的是,我们生活在一个有吨工具帮助我们远程工作的世界。我最喜欢的一些是 Zoom 、微软团队、和GitHub/git lab。
开始的时候,我对使用视频聊天有些犹豫,但是我很快就喜欢上了它。没有什么可以替代面对面的交流,但是 Zoom 视频聊天让我从整天盯着电脑屏幕中解脱出来,坦白地说,在过去的几周里,它变得非常有趣。
Teams: 类似于 Slack,但有微软产品的全力支持,Teams 是一个很棒的应用程序,允许你在远程工作时进行协作、共享文件、安排会议,以及做你需要的几乎任何事情。我绝对喜欢它。
GitHub/GitLab: 如果你在一个数据科学团队,你应该使用 GitHub 或 GitLab 进行代码协作。这两个*台都有很好的工具用于虚拟代码审查、聊天/评论和其他形式的虚拟协作。而且,GitHub 刚刚推出了他们的手机 app !(最后)
5.保持沟通不变
我的最后一点建议很简单:与你的团队保持开放的对话。正如本文所示,过渡到远程数据科学是一个过程。让你的团队了解什么对你有效,什么可以改进,并确保你也在倾听他们的意见。解决所有问题可能需要几周时间,但最终,远程工作的好处是值得的。**
感谢阅读!如果你想更新我正在写的东西、我正在构建的辅助项目或我觉得有趣的文章,请随时加入我的时事通讯— 【有抱负的数据科学家】 —或关注我的 博客 。
使用 3D 数据— fastai2
了解 fastai 数据块并创建构建块以处理深度学习应用程序的图像序列

奥拉夫·阿伦斯·罗特内在 Unsplash 上拍摄的照片。
介绍
处理 3D 数据或图像序列对于许多应用程序都很有用。假设您正在处理 3D 医疗数据、视频数据、卫星图像的时间序列,甚至是一幅更大图像的几个片段。如果你正在处理这样的问题,除了简单的基于图像的问题之外,你还需要一些额外的考虑。如何对 3D 数据进行增强?这些转换应该应用于图像方式还是序列方式,或者两者都应用?如何洗牌训练数据?是否应该保留序列顺序?这些问题的答案取决于问题的类型。然而,拥有一个能够适应所有这些不同可能性的框架是有用的。fastai 是模块化的,非常通用,这使它成为深度学习实践者和研究人员非常有用的工具。
在这个故事中,我将从 fastai 数据块的一个基本例子开始——创建数据加载器的构建块。然后,我将展示如何编写附加功能,以允许使用数据块创建包含 3D 数据的数据加载器,包括图像序列的扩充。
在这个 Kaggle 内核上提供了一个工作示例。
fastai 数据块
下面的代码显示了一个典型的基于图像的数据集的 fastai DataBlock 类的示例。如果你是 fastai 的新手,你可以在 fastai 文档中找到更多的例子。
dblock **=** DataBlock(blocks **=** (ImageBlock, CategoryBlock),
get_items **=** get_image_files,
get_y **=** label_func,
splitter **=** RandomSplitter(),
item_tfms **=** Resize(224),
batch_tfms **=** aug_transforms())
- 块 —该参数接收一个元组,该元组定义了数据块的输入类型和目标。在这种情况下,我们有 ImageBlock 和 CategoryBlock,因此 DataBlock 需要图像输入和分类目标——顾名思义。
- get_items —该参数接收一个返回项目列表的函数。在这种情况下, get_image_files 简单地返回所有图像文件的路径是一些目录和子目录。
- get_y —该参数接收一个定义标签的函数。在这种情况下是相应项目的类别。 get_x 也可以用在一些情况下,我们稍后会看到。请记住, get_x 和 get_y 接收 get_items 的输出,并分别定义 ImageBlock 和 CategoryBlock 将接收哪些信息。在本例中,不需要 get_x ,因为 get_image_files 已经返回了图像的列表路径,这是 ImageBlock 所需要的。
- 拆分器 —用于指示如何将数据拆分为训练集和验证集。
- item_tfms —用于定义应用于 CPU 上每个图像的变换。
- batch_tfms —用于定义应用于 GPU 上每个批处理的转换。
如您所见,这种结构非常灵活,几乎可以处理任何类型的数据,只要相应地定义了每个参数。对于更高级的应用,可能需要定义自定义块类型和函数来创建批处理。处理 3D 数据就是这种情况。然而,正如我将在下面的六个步骤中展示的,一旦你熟悉了数据块的结构,这个过程就相当简单了。
步骤 0。数据源
在这个例子中,我将使用 MNIST 的数据。我将从 0 到 9 的数字序列定义为我们的图像序列——这使得调试更加容易。下表显示了数据帧的组织方式。

数据帧的例子。作者创造的形象。
数据框架中的列有:
- 文件 : 每个图像的路径;
- sequence_id :每个序列的唯一标识符;
- sequence_order :序列中元素的顺序——在这种情况下,对应于从 0 到 9 的数字;
- 标签:每个序列的目标标签。
第一步。定义图像序列块
为了定义一个图像序列块,我使用了以下代码:
class **ImageSequence**(Tuple):
*@classmethod*
def create(cls, image_files):
return cls(tuple(PILImage.create(f) for f in image_files))
def **ImageSequenceBlock**():
return TransformBlock(type_tfms = ImageSequence.create,
batch_tfms = int2float)
- 首先,创建一个图像序列。它只是一个元组,包含 image_files 列表中给出的所有图像。
- 那么 ImageSequenceBlock 只是一个使用 ImageSequence 返回 fastai TransformBlock 的函数。这是对图像块的 fastai 代码的简单改编。
第二步。定义获取项目功能
请注意, ImageSequence 中的 create 函数接收图像文件列表作为参数。我们需要创建将返回这样的列表的函数,并将其交给数据块中的 get_items 参数。
基于数据源格式(步骤 0 中的 dataframe ),我定义了以下类来创建序列和标签列表:
class **SequenceGetItems**():
def __init__(self, filename_col, sequence_id_col, label_col):
self.fn = filename_col
self.seq = sequence_id_col
self.label = label_col
def __call__(self, df):
data = []
for fn in progress_bar(df[self.seq].unique()):
similar = df[self.seq] == fn
similar = df.loc[similar]
fns = similar[self.fn].tolist()
lbl = similar[self.label].values[0]
data.append([*fns, lbl])
return data
- 该类所需的属性只是数据帧上对应于文件名、唯一序列标识符和标签的列名。
- 调用时,call 方法将接收数据帧“df”作为参数,并将返回一个列表列表。每个子列表由序列的图像文件名和子列表最后一个元素中的相应标签组成。
现在,请记住上面的数据块示例,即 get_x 和 get_y 接收来自 get_items 的输出,并且应该将输入和目标分开。在这种情况下,它们就像这样简单:
get_x = lambda t : t[:-1]
get_y = lambda t : t[-1]
- 如你所见, get_x 将选择列表中的所有元素,但最后一个将由 get_y 选择。这只是前面讨论的序列创建方式的结果——标签只是每个子列表中的最后一个元素。
第三步。把所有东西放在数据块里
有了上面两步定义的代码,为我们的图像序列构建一个数据块是一项简单的任务:
dblock = DataBlock(
blocks = (ImageSequenceBlock, CategoryBlock),
get_items = SequenceGetItems('file', 'sequence_id', 'label'),
get_x = lambda t : t[:-1],
get_y = lambda t : t[-1],
splitter = RandomSplitter())
- 注意,对于块参数,我们现在给出步骤 1 中定义的图像序列块。
- 然后对于 get_items 参数,我们使用在步骤 2 中定义的 SequenceGetItems 类。
- get_x 和 get_y 非常简单,如前一节所述。
- 对于分离器,可以使用常用的 fastai 功能。另一个常见的选项是 IndexSplitter ,它允许精确地指定哪些项在验证集上。
我还没有包含任何 item_tfms 或 batch_tfms ,但是我很快就会包含。然而,让我们首先来看一个重要的附加部分——创建数据加载器。
第四步。创建数据加载器
为了从数据块中创建一个数据加载器,我们需要定义一个自定义函数来告诉如何创建批处理。到目前为止,我们只有图像序列。我们想要的是 PyTorch 张量。
def **create_batch**(data):
xs, ys = [], []
for d in data:
xs.append(d[0])
ys.append(d[1])
xs = torch.cat([TensorImage(torch.cat([im[None] for im in x], dim=0))[None] for x in xs], dim=0)
ys = torch.cat([y[None] for y in ys], dim=0)
return TensorImage(xs), TensorCategory(ys)
- 这段代码将单个图像张量连接成一个具有额外维度的新张量,然后将单个序列成批连接在一起。如果图像是 3 通道,128×128 大小,序列的长度是 10,批量大小是 8,那么张量将具有[8,10,3,128,128]的形状。
- 注意,当我返回张量时,我使用了 TensorImage 和 TensorCategory 。这些是 fastai 类型,提供了非常有用的功能。
现在,要创建数据加载器,下面一行代码可以完成:
dls = dblock.dataloaders(df, bs=8, create_batch=create_batch)
- 请注意, df 是我们在步骤 0 中提到的数据帧, bs 是批量大小,并且给出了一个自定义的 create_batch 函数。
第五步。可视化数据
这一步对于功能来说并不是最基本的,但是能够显示数据并检查是否一切正常总是好的!
def **show_sequence_batch**(max_n=4):
xb, yb = dls.one_batch()
fig, axes = plt.subplots(ncols=10, nrows=max_n, figsize=(12,6), dpi=120)
for i in range(max_n):
xs, ys = xb[i], yb[i]
for j, x in enumerate(xs):
axes[i,j].imshow(x.permute(1,2,0).cpu().numpy())
axes[i,j].set_title(ys.item())
axes[i,j].axis('off')
show_sequence_batch 函数

show_sequence_batch 函数的结果作者创造的形象。
请注意,每个图像上方的数字对应于序列标识符,这就是为什么在本例中它们在每一行都是相同的。
第六步。添加图像和序列增强
由于我们的批次有一个“额外的”维度—对应于序列长度—我们不能开箱即用地应用 fastai 图像增强。此外,正如在本文的引言中所讨论的,在一些问题中,我们需要按顺序应用变换,以便在我们可能希望提供给 3D 卷积模型的序列元素之间保持空间一致性。
获得想要的结果的最简单的方法——或者至少是我能想到的最简单的方法——是注意到变换被同样地应用到每个图像通道。因此,如果我们使用张量形状——使用 PyTorch view 方法——我们可以按顺序或按图像应用基本的图像变换。变换之后,我们可以把张量重塑回原来的形状。
为此,我定义了两个类:sequence FMS和 BatchTfms 。这两个类被定义为 fastai Transform 的子类。关于 fastai 转换需要注意的一个方面是编码方法类似于 PyTorch 模块中的 forward 方法或者 Python 类中常见的 call 方法。
class **SequenceTfms**(Transform):
def __init__(self, tfms):
self.tfms = tfms
def encodes(self, x:TensorImage):
bs, seq_len, ch, rs, cs = x.shape
x = x.view(bs, seq_len*ch, rs, cs)
x = compose_tfms(x, self.tfms)
x = x.view(bs, seq_len, ch, rs, cs)
return x
class **BatchTfms**(Transform):
def __init__(self, tfms):
self.tfms = tfms
def encodes(self, x:TensorImage):
bs, seq_len, ch, rs, cs = x.shape
x = x.view(bs*seq_len, ch, rs, cs)
x = compose_tfms(x, self.tfms)
x = x.view(bs, seq_len, ch, rs, cs)
return x
- 这两个类都用要应用于数据的扩充列表进行初始化。
- ****sequence FMS按顺序应用变换(同一序列中所有图像的变换相同)。
- BatchTfms 按图像方式应用变换。
完整的数据块现在看起来像这样:
affine_tfms, light_tfms = aug_transforms(flip_vert=True)
brightness = lambda x : x.brightness(p=0.75, max_lighting=0.9)
contrast = lambda x : x.contrast(p=0.75, max_lighting=0.9)
dblock = DataBlock(
blocks = (ImageSequenceBlock, CategoryBlock),
get_items = SequenceGetItems('file', 'sequence_id', 'label'),
get_x = lambda t : t[:-1],
get_y = lambda t : t[-1],
splitter = RandomSplitter(valid_pct=0.2, seed=2020),
item_tfms = Resize(128),
batch_tfms = [SequenceTfms([affine_tfms]),
BatchTfms([brightness, contrast])])
dls = dblock.dataloaders(df, bs=8, create_batch=create_batch)
注意:到目前为止,我还不能用这种方法使用由 aug_transforms 给出的light _ tfms——这就是为什么我手动定义了亮度和对比度转换,它们只是 ImageTensor fastai 类型的方法。
show_sequence_batch 可视化的结果如下:

使用图像和序列增强的 show_sequence_batch 函数的结果。作者创造的形象。
- 请注意,在第一行中,图像是旋转的,但由于仿射变换是按顺序应用的,因此它们在序列中是一致旋转的。
- 对于亮度和对比度变换,请注意它们是随机应用于各个图像的。
如您所见,该框架可以根据数据/问题的类型轻松定制。
结论
在这个故事中,我讲述了如何使用 fastai DataBlock 为深度学习应用处理 3D 数据。这个代码的一个工作示例在这个 Kaggle 内核上提供。
我花了一些时间和几次迭代才得到这里展示的代码,但所有这些学习过程让我对 fastai DataBlock 有了更好的理解,以及它是如何如此通用的!
关于我
** [## 我的 3 年历程:从零 Python 到深度学习竞赛高手
自从 2017 年开始学习 Python 以来,我一直遵循的道路是成为一名独自参加 Kaggle 比赛的大师…
towardsdatascience.com](/my-3-year-journey-from-zero-python-to-deep-learning-competition-master-6605c188eec7)
感谢阅读!如果你对这个话题还有其他问题,请随时发表评论。**
通过 Boto3 使用亚马逊 S3 桶
完整的备忘单。

亚马逊简单存储服务(Amazon Simple Storage Service,简称 S3)通过微调访问控制提供了存储、保护和共享数据的空间。当使用 Python 时,人们可以通过 Boto3 包轻松地与 S3 进行交互。在这篇文章中,我将把我在使用 S3 时经常用到的 Python 命令汇总在一起。希望你会觉得有用。

让我们从关于 S3 数据结构的几句话开始。在你自己的电脑上,你将文件存储在文件夹中。在 S3,这些文件夹被称为桶。在桶里面,你可以存储对象,比如。csv 文件。您可以通过名称来引用存储桶,而通过关键字来引用对象。为了使代码块更易处理,我们将使用表情符号。以下是符号的关键:****
🗑 — a bucket’s name, e.g. “mybucket”🔑 — an object’s key, e.g. "myfile_s3_name.csv"📄 - a file's name on your computer, e.g. "myfile_local_name.csv"
🗑和🔑可以表示 S3 上已经存在的名称,也可以表示您希望为新创建的桶或对象指定的名称。📄表示您已经拥有或希望拥有在您的计算机本地某处的文件。

设置客户端
要用 Boto3 访问任何 AWS 服务,我们必须用一个客户机连接到它。在这里,我们创建一个 S3 客户端。我们指定数据所在的区域。我们还必须传递访问密钥和密码,这可以在 AWS 控制台中生成,如这里的所述。
存储桶:列出、创建和删除
要列出 S3 上现有的存储桶,删除一个或创建一个新的,我们只需分别使用list_buckets()、create_bucket()和delete_bucket()函数。
对象:列表、下载、上传和删除
在一个桶中,存在对象。我们可以用list_objects()把它们列出来。MaxKeys参数设置列出的对象的最大数量;这就像在打印结果之前调用head()一样。我们还可以使用Prefix参数只列出键(名称)以特定前缀开头的对象。
我们可以使用upload_file()上传一个名为📄以这个名字去 S3🔑。同样,download_file()会保存一个名为🔑在 S3 当地以这个名字📄。
为了获得一些关于对象的元数据,比如创建或修改时间、权限或大小,我们可以调用head_object()。
删除一个对象的工作方式与删除一个桶相同:我们只需要将桶名和对象键传递给delete_object()。
将多个文件加载到单个数据框中
通常,数据分布在几个文件中。例如,您可以将不同商店或地区的销售数据保存在具有匹配列名的不同 CSV 文件中。对于分析或建模,我们可能希望将所有这些数据放在一个 pandas 数据框中。下面的代码块就可以做到这一点:下载🗑境内名称以“some_prefix”开头的所有数据文件,并将其放入单个数据框中。
使用访问控制列表(ACL)将对象设为公共或私有
在 S3 上管理访问权限的一种方法是使用访问控制列表或 ACL。默认情况下,所有文件都是私有的,这是最好的(也是最安全的!)练习。您可以将文件指定为"public-read",在这种情况下,每个人都可以访问它,或者指定为"private",使您自己成为唯一授权的人。查看此处获取访问选项的详细列表。
当文件已经在 S3 上时,你可以使用put_object_acl()来设置它的 ACL,也可以在上传时通过将适当的ExtraArgs传递给upload_file()来设置。
使用预签名的 URL 访问私有文件
您还可以通过使用generate_presigned_url()函数生成一个临时的预签名 URL 来授予任何人对私有文件的短期访问权。这将产生一个字符串,可以直接插入到 pandas 的read_csv()中,例如,下载数据。您可以通过ExpiresIn参数指定该临时访问链接的有效时间。这里,我们创建一个有效期为 1 小时(3600 秒)的链接。

感谢阅读!
如果你喜欢这篇文章,为什么不在我的新文章上 订阅电子邮件更新 ?通过 成为媒介会员 ,你可以支持我的写作,并无限制地访问其他作者和我自己的所有故事。
需要咨询?你可以问我任何事情,也可以在这里 预定我 1:1 。
也可以试试 我的其他文章 中的一篇。不能选择?从这些中选择一个:
完整的备忘单。
towardsdatascience.com](/working-with-amazon-sns-with-boto3-7acb1347622d) [## 增强你对助推的把握
揭秘著名的竞赛获奖算法。
towardsdatascience.com](/boost-your-grasp-on-boosting-acf239694b1) [## 线性回归中收缩法和选择法的比较
详细介绍 7 种流行的收缩和选择方法。
towardsdatascience.com](/a-comparison-of-shrinkage-and-selection-methods-for-linear-regression-ee4dd3a71f16)
通过 Boto3 使用 Amazon SNS
完整的备忘单。

亚马逊简单通知服务,或 SNS,允许我们自动发送消息,如电子邮件或短信。我们还可以通过 HTTP Post 向指定的 URL 发送消息,这是一种分离微服务的便捷方式。当使用 Python 时,人们可以通过 Boto3 包轻松地与 SNS 进行交互。在这篇文章中,我将把我在使用 SNS 时经常用到的 Python 命令汇总在一起。希望你会觉得有用。

让我们从一些基本的社交网络概念开始。你可以把 SNS 系统想象成一个群聊。一旦您在聊天中发布消息,所有参与者都会收到一个弹出消息。在 SNS 设置中,群聊将被称为话题。您可以向主题发布消息,一旦发布,所有主题订阅者都会收到通知。
AWS 资源可以通过亚马逊资源名称 (ARNs) 唯一标识。每个主题都有其独特的主题卡,每个主题的订阅都有其独特的订阅号。

设置客户端
要用 Boto3 访问任何 AWS 服务,我们必须用一个客户机连接到它。在这里,我们创建一个 SNS 客户端。我们指定保存消息的区域。我们还必须传递访问密钥和密码,这可以在 AWS 控制台中生成,如这里的所述。
主题:列出、创建和删除
为了创建一个新的主题,我们使用了create_topic()函数,传递所需的名称。一旦主题被创建,我们可以通过从由craete_topic()返回的对象中提取TopicArn键来获得它的 ARN。
要列出 AWS 上已经存在的主题,我们可以使用list_topics()函数并从其输出中提取Topics键。要删除一个主题,我们只需将它的 ARN 传递给delete_topic()函数。
订阅:列出、创建和删除
为了创建主题的新订阅,我们调用subscribe()函数,传递要订阅的主题的 ARN、协议(例如 SMS 或电子邮件)和端点(例如 SMS 协议的电话号码或电子邮件协议的电子邮件地址)。一旦订阅被创建,我们可以通过从由subscribe()返回的对象中提取SubscriptionArn键来获得它的 ARN。
要列出 AWS 上现有的所有订阅,我们可以使用list_subscriptions()函数并从其输出中提取Subscriptions键。类似地,为了列出特定主题的订阅,我们调用了list_subscriptions_by_topic()函数。
要删除一个订阅,我们将它的 ARN 传递给unsubscribe()函数。为了删除多个订阅,例如具有相同协议的所有订阅,我们可以使用下面代码块底部的 for-loop 对它们进行循环。
发布到主题
要向主题发布消息,我们只需调用publish()函数,传递主题的 ARN、想要的消息和可选的主题(它将只用于电子邮件消息)。
或者,我们可以发布没有任何主题或订阅的 SMS 消息。为此,我们调用publish()函数,将电话号码和消息文本作为参数传递。

感谢阅读!
如果你喜欢这篇文章,为什么不订阅电子邮件更新我的新文章呢?并且通过 成为媒介会员 ,可以支持我的写作,获得其他作者和我自己的所有故事的无限访问权限。
需要咨询?你可以问我任何事情,也可以在这里为我预约 1:1。
也可以试试 我的其他文章 中的一篇。不能选择?从这些中选择一个:
完整的备忘单。
towardsdatascience.com](/working-with-amazon-s3-buckets-with-boto3-785252ea22e0) [## 非线性回归:基础扩展、多项式和样条
如何用多项式和样条捕捉非线性关系?
towardsdatascience.com](/non-linear-regression-basis-expansion-polynomials-splines-2d7adb2cc226) [## 模型选择和评估
超越火车价值测试分裂
towardsdatascience.com](/model-selection-assessment-bb2d74229172)**
使用 Kotlin 使用 Android WorkManager
向后兼容的调度未来任务的理想方式

Fernando Jorge 在 Unsplash 上拍摄的照片
工作管理器是一个 API,它可以调度你未来的异步任务,并且可以在后台运行它们。即使用户在应用程序之外或应用程序关闭,分配给工作管理器的任务也会执行。WorkManager 只能运行您的任务一次,也可以多次运行或定期运行。
工作管理器的功能
- 它提供了高达 API 级别 14 的向后兼容性
- 您可以添加一个或多个约束条件,如仅在手机充电或重启时执行任务等。
- 您可以计划一次性任务或定期任务
- 您还可以链接多个任务。例如,任务 (B) 应该只在任务 (A) 完成时执行。
- 它可以帮助您在特定事件上执行任务。
注意:工作管理器不适用于在 app 进程结束时可以安全终止的正在进行的后台工作,也不适用于需要立即执行的任务。
上层社会
工作人员:这里定义了需要完成的工作。
WorkRequest: 它决定将要执行哪个 worker 类。这是一个抽象类,所以我们将使用它的直接类,它们是 OneTimeWorkRequest 和 PeriodWorkRequest 。
工作管理器:它对工作请求进行排队和管理。
WorkInfo: 它给我们关于工作的信息,不管是成功的、运行的还是失败的。
让我们现在开始编码吧…
我们会创造什么?
我们将在后台创建一个通知,这个通知只能创建一次,因为我们使用的是 OneTimeWorkRequest 类。稍后,我们将使用一些约束来基于事件生成通知。
首先,添加以下依赖项。
implementation **"androidx.work:work-runtime-ktx:2.3.4"**
我们将首先通过扩展***Worker***类来创建我们的 worker 类,并覆盖它的***doWork()***方法用于后台处理。当工作管理器调用***doWork()***方法时,它调用用户定义的方法***createNotification()***。
在我们的***MainActivity.kt***类中,我创建了一个按钮,当用户点击按钮时,就会立即生成通知。
在这里,我创建了 OneTimeWorkRequest 的对象,并传递了我们的***MyWork***类的类名。在现实世界中,我们可以有很多 worker 类,所以应该执行哪个类是由这个 request 对象决定的。
**val** request = *OneTimeWorkRequestBuilder*<MyWork>().build()
当用户单击按钮时,WorkManager 将请求排队。
WorkManager.getInstance(**this**).enqueue(request)
在这里,我们创建一个 toast 来显示我们任务的状态,无论是运行、成功还是失败。***getWorkInfoByIdLiveData***方法获取请求 id 并给出关于任务的信息。
WorkManager.getInstance(**this**).getWorkInfoByIdLiveData(request.*id*)
.observe(**this**, *Observer* **{
val** status: String = **it**.*state*.**name** Toast.makeText(**this**,status, Toast.*LENGTH_SHORT*).show()
**}**)
}
现在运行你的应用程序,点击按钮,你会看到一个通知。
现在,我们将了解如何添加约束,以便仅在手机充电时创建通知。
添加以下代码行以创建约束并修改您的请求对象。在请求对象中,我只是设置约束,仅此而已。现在,只有当满足这个特定标准时,才会生成通知。
**val** constraints = Constraints.Builder()
.setRequiresCharging(**true**)
.build()**var** request = *OneTimeWorkRequestBuilder*<MyWork>()
.setConstraints(constraints)
.build()
注意:当指定了多个约束时,您的任务只有在所有约束都满足时才会运行。
如果你点击按钮,而你的手机没有在充电,那么你会看到一个状态显示“排队”,这意味着你的请求已经被放入队列,只有当你的手机正在充电时才会执行。
如果你在运行代码时遇到任何问题,那么你可以从我的 Github 账户下载这个项目。
我希望你喜欢读这篇文章,你也可以访问我的 网站 ,在那里我会定期发布文章。
订阅 我的邮件列表,以便在您的收件箱中直接获得我的文章,并且不要忘记关注我自己在 Medium 【代码怪兽】 上发表的文章,以丰富您的技术知识。
结论
我们已经看到了如何使用 WorkManager 类来执行一些后台处理。在本文中,我在后台创建了一个通知。也看到了我们如何根据未来将要发生的事件来安排未来的任务。
使用匿名函数
使用这些简单的操作快捷方式节省您的时间

塔里克·黑格在 Unsplash 上拍摄的照片
当我们处理数据时,我们很少会让它保持原样、不被转换或不被聚集。必须以高效的方式清理、转换、总结和描述数据,以便为实现准备数据。创建函数是一种标准且可靠的做法,在对数据应用预定义的操作时可以节省时间。然而,尽管定义自己的函数有时非常重要,但有时却完全没有必要。我们可以对一个特定的级数使用特殊类型的函数来加速这个过程,而不牺牲计算效率或精度。
匿名函数
我们在 Python 中实现了一些函数,但没有给它们命名(因此,匿名)。这些函数只能在数据操作过程的特定时刻使用一次。介绍,λ函数:
lambda <arguments>: <expression>
以上是 lambda 函数的一般语法。我们稍后将进一步深入,但首先让我们定义语法。
- Lambda 函数可以有任意数量的参数(将要操作的内容),但只能有一个表达式(将要计算和返回的内容)
- lambda 函数可以应用于任何需要函数对象的地方
- 仅限于单一表达式
让我们来比较一下匿名 lambda 函数和传统定义的 function 对象在试图将一个列表的所有内容加倍时的情况:
繁体:
list1 = [1, 2, 3]def double_items(list1): list2 = []
for x in list1:
list2.append(x*2) return list2
它返回:
double_items(list1)Output: [2,4,6]
兰巴:
list1 = [1,2,3]
list2 = list(map(lambda x: x*2, list1))
它返回:
print(list2)Output: [2,4,6]
我们可以清楚地看到,第二个 lambda 函数允许我们在一行代码中完成相同的计算,而不需要定义不必要的代码块。
Lambda 场景:
lambda 匿名函数可以应用于三种主要场景:filter()、map()和 reduce()。
过滤器()
这个函数将一个列表作为它的参数,然后过滤掉列表中的元素,为表达式返回“true”。我们下面看到的函数将只返回偶数列表项(使用了“%”模数运算符)。
ex_list = [33, 2, 42, 1, 47, 430, 23, 98, 12]
final = list(filter(lambda x: (x%2 == 0), ex_list))
输出:
print(final)[2, 42, 430, 98, 12]
地图()
在我处理数据的经验中,这个函数是目前使用最多的匿名函数。我们已经在最初的例子中使用了 map。当与熊猫系列对象结合使用时,贴图功能也非常有效。Map 是一个带两个参数的函数:函数名(或函数本身)和序列(一个列表、一个熊猫系列等)。).因此,map 函数与 lambda 函数一起将映射序列中所有元素的函数。
a = [1, 2, 3, 4]
b = [5, 6, 7, 8]sum_a_b = map(lambda x, y: x + y, a, b)
输出:
print(sum_a_b)[6, 8, 10, 12]
示例 map/lambda 组合采用函数 x + y,以及序列 a 和 b。它将每个序列中的各个项目分别视为函数的 x 和 y,以返回结果。
减少()
这个函数也将一个列表作为参数,然后,你猜对了,通过对列表对的重复操作来减少列表。我们可以在“functools”模块中找到这个函数,我们必须首先导入它。
from functools import reduceex_list = [24, 5, 6, 45, 96, 123, 69]
difference = reduce(lambda x: y: x - y, ex_list)
输出:
print(difference)-320
这个函数取列表中第一项和第二项的差,然后继续下去,直到列表的最后一个元素被求差。类似地,它将 lambda 函数作为要执行的操作,将 ex_list 作为执行函数的序列。
在数据科学中使用 APIs 探索新加坡 HDB 转售市场中的位租理论
你在数据分析和机器学习方面 80%的工作是争论和清理数据,这可能是非常乏味的工作。使用 API 实现数据提取、数据丰富和数据清理的自动化可以节省大量手工提取的工作。
本文的重点将是如何在 Python 中与 API 交互,我们将在新加坡公共住房数据集的背景下工作,并探索住房市场中的出价-租金理论。它将更侧重于 Python 脚本,而不是实际分析,所以如果本文的任何部分缺乏学术和统计的严谨性,请原谅我。
完整代码 Github 链接:https://github . com/yuan-西野/hdb-转售-api/blob/master/script.py
比特租金理论

我们基本上是在探索每*方米价格和房产到 CBD 的距离之间的关系。
我们想要实现的目标:
- 从转售 API 查询 2017 年 1 月以后的所有交易数据
- 获取交易地址的地理位置(纬度,经度)
- 获取所有捷运站(新加坡地铁)的地理位置(经度,纬度)
- 获取所有购物中心的地理位置(纬度,经度)
- 获取每笔交易与最*的捷运站和购物中心之间的距离
- 获取每次交易到中央商务区(莱佛士广场)的距离
- 不要使用一行 PD . read _ CSV();)
太好了,既然已经不碍事了,那就让我们进入正题吧。
首先让我们检查一下我们今天将使用的 API:
- https://data.gov.sg/dataset/resale-flat-prices——Data.gov.sg 的转售价格提供了自 1990 年以来的历史交易数据。我们将专门检查 2017 年 1 月以后的数据集。
- https://docs.onemap.sg/—一个强大的 API,提供特定于新加坡的位置坐标、交通、路线、人口数据等数据。我们将利用这个 API 来获取这个练习所需的地理坐标。
通过 API 获取数据并转换成 Pandas 数据框架
首先,我们将从导入一些基本库开始
import json
import requests
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
为了发出我们的第一个 API 请求,我们将使用请求库。但在此之前,让我们检查一下浏览器中的 API:

这是我们将发出的 HTTP GET 请求,用于请求数据集的服务
我们可以看到,我们可以通过使用“limit”或“q”参数来查询结果,以获得搜索结果。既然我们需要整个数据集,我们只需要指定整个数据集的限制。

快速检查我们浏览器中的示例查询,我们可以看到我们感兴趣的事务的“记录”嵌套在 JSON 响应的“结果”数组中。有了我们正在处理的数据结构的概念,我们可以移动到我们的 Python IDE 并查询我们的数据。
query_string='[https://data.gov.sg/api/action/datastore_search?resource_id=42ff9cfe-abe5-4b54-beda-c88f9bb438ee&limit=500'](https://data.gov.sg/api/action/datastore_search?resource_id=42ff9cfe-abe5-4b54-beda-c88f9bb438ee&limit=500')
resp = requests.get(query_string)#Convert JSON into Python Object
data = json.loads(resp.content)
因为这是一个相当大的数据集(在撰写本文时有 101994 条),所以在转移到整个数据库之前,我们将对 500 条记录进行采样,以便于处理。在使用 API 的探索阶段,这是一个很好的实践,可以避免耗尽不必要的服务器资源,重复查询巨大的数据集。现在,让我们在 IDE 中检查一下服务返回给我们的内容。
# Notice that data type is actually 'dict'
print(type(data))#Output
<class 'dict'>

这看起来类似于我们在浏览器中看到的返回的 JSON
不错,从服务器返回的 JSON 对象基本上是大家熟悉的 Python 字典!我们的工作变得简单了一点,不需要额外的工作来转换我们的 JSON 对象,使其在 Python 中工作。
len(data['result']['records'])#Output
500
我们所要做的就是访问 dictionary 对象中的 records 键,以获得存储为 dictionary 键-值对的事务数据列表。
# Access the first record in our list
hdb_price_dict_records[0]#Output
{'town': 'JURONG EAST',
'flat_type': '3 ROOM',
'flat_model': 'New Generation',
'floor_area_sqm': '67',
'street_name': 'JURONG EAST ST 21',
'resale_price': '270000',
'month': '2018-07',
'remaining_lease': '63 years 08 months',
'lease_commence_date': '1983',
'storey_range': '10 TO 12',
'_id': 1,
'block': '214'}
既然我们已经习惯了遍历嵌套数据,那么让我们创建 Pandas 数据框。
在上面的示例中,我们创建了一个 for 循环,将每个记录中的字典值附加到相关列表中,该列表将构成数据框中的每一列。
df_hdb_price = pd.DataFrame({
'town': town,
'flat_type': flat_type,
'flat_model': flat_model,
'floor_area_sqm': floor_area_sqm,
'street_name': street_name,
'resale_price': resale_price,
'month': month,
'remaining_lease': remaining_lease,
'lease_commence_date': lease_commence_date,
'storey_range': storey_range,
'_id': _id,
'block': block
})

酷!我们的数据框看起来不错!
通过 OneMap API 获取经度和纬度数据
接下来的部分有点棘手。我们需要使用地理位置坐标(纬度、经度)找到 HDB 单位所在的最*的捷运站。我们将使用另一个 API 来实现这一点 OneMap API。
首先让我们创建一个新加坡所有捷运站的列表。由于 MRT 站点随时间变化相对较慢,我们可以利用静态列表来代替。我获得了(好的 ole 副本和面食;) )维基百科的数据,其中也提供了即将到来的捷运站的数据。我们暂时只考虑现有的捷运站。
list_of_mrt = [
'Jurong East MRT Station',
'Bukit Batok MRT Station',
'Bukit Gombak MRT Station',
'Choa Chu Kang MRT Station',
'Yew Tee MRT Station',
'Kranji MRT Station',
'Marsiling MRT Station',
....
]
快速查看 OneMap API —我们可以看到,我们需要将我们的地址输入到 URL 的“searchVal”参数中,如下所示:
[https://developers.onemap.sg/commonapi/search?searchVal='+str(address)+'&returnGeom=Y&getAddrDetails=Y](https://developers.onemap.sg/commonapi/search?searchVal='+str(query_address)+'&returnGeom=Y&getAddrDetails=Y')
让我们使用 OneMap API 来获取每个 MRT 站的(lat,long)坐标。下面的 for 循环遍历 MRT 车站列表并返回结果,然后我们将获取响应的第一个结果,类似于我们之前遍历转售 JSON 对象时所做的。
然后我们将它存储到一个新的数据帧中:
# Store this information in a dataframe
mrt_location = pd.DataFrame({
'MRT': list_of_mrt,
'latitude': mrt_lat,
'longitude': mrt_long
})
现在,让我们找出使用相同方法处理的每个单元的地理位置。但是等一下,这是一个很大的数据集..我们可以让它更有效率。我们知道,在同一个 HDB 公寓区会有多个单位进行交易。我们可以对我们的数据帧进行重复数据消除,并仅获得数据帧中的唯一地址。
# Let's combine the block and street name to form the address of our transacted unit.
df_hdb_price['address'] = df_hdb_price['block'] + " " + df_hdb_price['street_name']# Dedup Address List
df_dedup = df_hdb_price.drop_duplicates(subset='address', keep='first')
len(df_dedup)
我们现在可以通过 OneMap API 获得(经度,纬度)坐标

当我们遍历地址列表时,我们在控制台中打印出坐标数据
完成后,让我们将它保存到一个日期框架中:
df_coordinates = pd.DataFrame({
'latitude': latitude,
'longitude': longitude,
'blk_no': blk_no,
'road_name': road_name,
'postal_code': postal_code,
'address': address
})
len(df_coordinates)
我们需要为下一个任务找到解决方案,为数据框中的每个地址找到最*的 MRT,并获得以米为单位的距离。
寻找坐标列表之间的最短距离

来源:维基百科
我们将使用哈弗辛公式来获得曲面上两点之间的距离。或者,我们也可以使用毕达哥拉斯定理,因为我们两点之间的距离并不大。但是为了简单起见,我们将使用 geopy 包来计算哈弗辛公式。
***# Lists of all the coordinates we will need to iterate through***
list_of_lat = df_coordinates['latitude']
list_of_long = df_coordinates['longitude']
mrt_lat = mrt_location['latitude']
mrt_long = mrt_location['longitude']***# Zipping the respective Lat and Long lists together as a list of tuples***
list_of_coordinates = []
for lat, long in zip(list_of_lat, list_of_long):
list_of_coordinates.append((lat,long))for lat, long in zip(mrt_lat, mrt_long):
list_of_mrt_coordinates.append((lat, long))
要获得地图上任意给定点的最*的 MRT 车站,我们需要:
- 遍历我们的地址列表
- 获取地址和所有捷运站之间的距离
- 将它存储到列表中
- 从那里我们将采取最短的距离作为最*的捷运站
上面我们有一个嵌套的 for 循环,它遍历 2 组坐标,为数据集中的每个唯一地址获取最*的 MRT。尽管这肯定不是最有效的解决方案。例如,我们可以将我们的地址分组到新加坡的各个地区,并且只检查位于给定距离内的 MRT 站列表,而不是整个列表。但是现在我们将依靠 CPU 的强大力量来为我们制造这些数字:)
我们将同样计算到 CBD 的距离和到最*的购物中心的距离。
***# Storing our distance into our data frame as a new column***
df_coordinates['min_dist_mrt'] = min_dist_mrt
df_coordinates['min_dist_mall'] = min_dist_mall
df_coordinates['cbd_dist'] = cbd_dist
把它们放在一起
最后,我们将使用外部连接将距离数据与主数据框合并。
combined = df_coordinates.merge(df_hdb_price, on="address", how='outer')
一些数据卫生步骤
# Converting to the right data types
combined['resale_price'] = combined['resale_price'].astype('float')
combined['floor_area_sqm'] = combined['floor_area_sqm'].astype('float')combined['lease_commence_date'] = combined['lease_commence_date'].astype('int64')
combined['lease_remain_years'] = 99 - (2019 - combined['lease_commence_date'])
combined.columnscombined.dropna(inplace=True)combined['price_per_sqm'] = combined['resale_price'].div(combined['floor_area_sqm'])
我们已经准备好绘制我们的价格/*方米到 CDB 的距离关系图!
绘制钻头租金关系
g = sns.scatterplot(x=combined['cbd_dist'],y=combined['price_per_sqm'], hue=combined['flat_type'], ax=ax1)box = g.get_position()
g.set_position([box.x0, box.y0, box.width * 0.85, box.height]) # resize positiong.legend(loc='center right', bbox_to_anchor=(1.2, 0.5), ncol=1)plt.show()

新加坡转售组屋的位租关系
从我们的数据中,我们明确看到了距离和每*方米价格之间的负相关关系。但是在房地产和中央商务区之间的某些距离间隔上也有很大的差异。除了我们的 cdb_dist 变量之外,肯定还有其他决定地价的解释性因素。让我们探索变量之间的相关性。
corrMatrix = df_numerical_cols.corr()
sns.heatmap(corrMatrix,
xticklabels=corrMatrix.columns,
yticklabels=corrMatrix.columns,
cmap='coolwarm',
annot=True)

看起来,到关键设施的距离,如捷运和购物中心,对我们的土地价格有影响。而土地租赁也可以解释部分价格。
线性回归模型
我们将使用一个简单的多元线性回归模型进一步探索我们的特征的解释能力。让我们设计我们需要的功能。
**# Separate our numerical and categorical variables**
cat_features = ['town', 'flat_type', 'storey_range']
num_features = ['min_dist_mrt', 'min_dist_mall', 'cbd_dist','floor_area_sqm', 'lease_remain_years', 'resale_price', 'price_per_sqm']
target = ['resale_price']**# New data frame for our regression model**
df_reg = combined[['town', 'flat_type', 'storey_range', 'min_dist_mrt', 'min_dist_mall', 'cbd_dist','floor_area_sqm', 'lease_remain_years', 'resale_price', 'price_per_sqm']]
映射序数分类特征:
flat_type_map = {
'EXECUTIVE': 7,
'MULTI-GENERATION': 6,
'5 ROOM': 5,
'4 ROOM': 4,
'3 ROOM': 3,
'2 ROOM': 2,
'1 ROOM': 1
}df_reg['flat_type_mapped'] = df_reg['flat_type'].map(lambda x: flat_type_map[x])def split_mean(x):
split_list = x.split(' TO ')
mean = (float(split_list[0])+float(split_list[1]))/2
return meandf_reg['storey_mean'] = df_reg['storey_range'].apply(lambda x: split_mean(x))
一键编码我们的“城镇”功能:
# One-Hot Encoding for our categorical variables and drop 1 of our dummy variables
df_reg = pd.get_dummies(data=df_reg, columns=['town'], drop_first=True)
创建我们的回归模型:
import statsmodels.api as sm
X = df_reg[['min_dist_mrt', 'min_dist_mall',
'cbd_dist', 'floor_area_sqm', 'lease_remain_years',
'flat_type_mapped', 'storey_mean']]
y = df_reg["price_per_sqm"]X = sm.add_constant(X)model = sm.OLS(y, X).fit()
predictions = model.predict(X)# Print out the statistics
model.summary()


解释我们的结果
- r *方-这是预测的解释方差的百分比。我们的模型只能解释约 0.728 或 72.8%的数据差异
- 我们的系数的统计显著性-我们在模型中的 p 值很小(< 0.05),因此我们可以拒绝零假设,并得出结论,我们的变量与我们的每*方米价格有因果关系。事实上,我们与 CDB 的距离每增加 1 米,每*方米的价格就会下降 0.19 美元
检查回归误差指标
from sklearn import metricsprint('Mean Absolute Error:', metrics.mean_absolute_error(df_reg["price_per_sqm"], predictions))
print('Mean Squared Error:', metrics.mean_squared_error(df_reg["price_per_sqm"], predictions))
print('Root Mean Squared Error:', np.sqrt(metrics.mean_squared_error(df_reg["price_per_sqm"], predictions)))***# Output***
Mean Absolute Error: 476.06461056219956
Mean Squared Error: 395453.2820071103
Root Mean Squared Error: 628.8507629057234
*均结果误差给出了模型预测的实际数据的*均绝对值。与我们的数据集中每*方米价格的范围和*均值相比,我们的 MAE 相当小。
从我们的 RMSE 值来看,我们的模型的预测*均会与实际价值相差 628.85 美元。如果我们将 RMSE 作为每*方米*均价格的一个百分比,那么我们将得到 14%的误差率,这是相当高的。
改进我们的模型
让我们通过在回归中引入分类变量来提高模型的解释力。


- 我们可以看到,我们的 R *方值提高了,可以解释我们数据集中 82.3%的方差
- 我们看到,房产所属的城镇对其每*方米价格有着巨大而显著的影响
- 武吉提马、海洋游行、碧山,这些都是新加坡非常受欢迎的房地产,与芽笼(红灯区)、后港(反对党)等传统上不太受欢迎的地区相比,溢价要高得多
Mean Absolute Error: 382.26714892132696
Mean Squared Error: 257854.07863672107
Root Mean Squared Error: 507.7933424501754
我们的*均误差、均方误差和 RSME 都降低了,这意味着我们的模型有了更高的精度。我们的 RMSE/均值也降到了 11.2%。
结论
显然,从统计数据来看,到中央商务区的距离和每*方米的价格之间存在显著的关系。但是,这并不能完全解释新加坡 HDB 的房价。从我们的回归模型中,我们看到住宅地产定价有更多的因素,如设施、政治边界、物业的成熟度以及它所在的地产,这些因素可能比到中央商务区的距离具有更强的解释力和预测力。
感谢阅读到最后!这是一篇很长的文章,希望它能帮助你更好地理解如何与 Python 中的 API 进行交互,以及它如何帮助你自动化数据处理和分析工作流!更多数据和 Python 相关内容请关注我:)
在 Azure 函数中使用 Azure Cosmos DB
如何使用 Azure 函数和 Cosmos DB 实现无服务器数据库计算

开发使用 Azure Cosmos DB 作为数据存储的 Azure 函数很容易实现。我们可以使用 CosmosDB 触发器调用我们的 Azure 函数,我们可以使用输入和输出绑定从我们的 Cosmos DB 集合中获取数据,或者我们可以使用 Azure 函数支持依赖注入到我们的 Cosmos DB 客户端的单例实例中,以供我们的函数使用。
在本文中,我将讨论在 Azure Function 应用程序中使用 Cosmos DB 的各种不同方式,每种方式的优缺点,并展示一些过程中的代码。出于本文的目的,我将用 C#编写代码示例。
要跟随代码,请在 GitHub 上查看这个回购。
我们可以使用 CosmosDB 触发器来创建事件驱动的函数,这些函数使用 Cosmos DB Change Feed 功能来监视我们的 Cosmos DB 数据库中容器的变化。

使用 Cosmos DB 触发器,我们可以使用变更提要对容器中的项目执行操作,并将这些操作的结果存储在另一个容器中。我们还可以使用触发器来实现数据的归档策略。例如,我们可以在 Cosmos DB 中存储热数据,使用 change feed 来监听进入该容器的新项目并将其保存在 Azure 存储中,然后删除 Cosmos DB 中的热数据。
让我们来看一个例子:
这是在函数中使用 CosmosDB 触发器的最简单的例子。我们正在监听数据库中的一个集合,通过为它指定一个集合并在我们的绑定中不存在该集合时创建它来管理变更提要的租约。
然后,当变更提要检测到受监控容器中的变更时,我们只记录该容器中修改了多少个文档,以及第一个文档的 id 是什么。默认情况下,更改提要每 5 秒钟轮询一次指定的容器,但是如果需要更改该值,可以在绑定中指定不同的值。
如果你想进一步了解你可以在 Azure Functions 中使用变更提要,我在这里写了一个更详细的帖子。
我们可以将 Azure 函数绑定到 Cosmos DB 中的容器,这将允许函数在执行时从容器中读取数据。

假设我们有一个使用 HTTP 触发器调用我们的函数的无服务器 API,我们可以使用输入绑定获得一个存储在 Azure Cosmos DB 中的文档。
我们可以这样做:
我们可以指定希望在 Cosmos DB 输入绑定中运行的 SQL 查询,并将结果作为产品类的 IEnumerable 返回。如果我们没有得到结果,我们可以返回 Not Found,否则,在 16 行代码中,我们可以将产品项返回给我们的客户端。
通过输出绑定,我们可以连接到 Cosmos 中的容器并将数据写入这些容器。

以我们的无服务器 API 为例,这次我们可以使用一个 Cosmos DB 输出绑定来连接到我们的容器,以便将我们的新产品项目插入其中。
这里有一个例子:
我们在输出绑定中所做的就是指定我们想要将项目插入哪个容器,它位于哪个数据库中,以及到我们的 Cosmos DB 帐户的连接字符串。
如您所见,使用 Cosmos DB 绑定构建一些简单的应用程序非常容易,但这是有代价的。
绑定的缺点
在我写这篇文章的时候, Azure 函数触发器和绑定只支持 SQL API 。如果我们想在我们的 Azure 函数中使用另一个 Cosmos DB API,我们必须创建一个静态客户端,或者像我们接下来要做的那样,为我们正在使用的 API 创建一个客户端的单独实例。
默认情况下,Cosmos DB 绑定使用版本 2 的。NET SDK。这意味着如果您想在函数中使用新的 V3 特性,比如事务批处理,那么您必须使用依赖注入来创建一个兼容 V3 的客户端。
使用依赖注入
Azure Functions 的 v2 提供了对依赖注入的支持。它建立在。NET 核心依赖注入特性。函数中的依赖注入提供了三种服务生存期:
- 瞬态:这些是为服务的每个请求创建的。
- 作用域:这个作用域匹配一个函数的执行生存期。每次执行都会创建一次。
- Singleton :它们的生命周期与主机的生命周期相匹配,并在该实例上的所有函数执行中被重用。因此,我们可以在函数应用程序中为每个函数提供共享服务,而不是让客户端连接到每个绑定。这是我们将用于我们的 CosmosClient 的范围。
为了在我们的函数中实现依赖注入,我们需要在 Startup.cs 文件中注册我们的 CosmosClient 服务:
这里,我添加了一个 CosmosClientBuilder 的单例实例,通过我的 CosmosDB 帐户的连接设置。然后,我使用 fluent API 添加属性,比如设置应用程序区域和支持批量执行。
现在我有了我的 Singleton 实例,我可以将它注入到我们的函数中:
在这里,我使用构造函数注入来使我的依赖项对我的函数可用。注意这里我不再为我的函数使用静态类,因为这是构造函数注入所需要的。
如果你想了解依赖注入在 Azure 函数中是如何工作的,这份文档有一个非常简单的指南。
结论
我希望读完这篇文章后,您对在函数中使用绑定和依赖注入来处理 Cosmos DB 的含义有一个很好的了解。
我的偏好是使用单例实例而不是绑定,但是就像我前面说的,这取决于您的用例。
我希望你喜欢这篇文章,并发现它很有用。一如既往,如果你有任何问题,请在评论中告诉我!
处理数据:纽约时报“新词”数据集

在本文中,我将介绍和分析我用来清理、构建和可视化这个数据集的几个步骤。
几周前,我在推特上看到一个数据集。这个数据集是由两个 NLP 的博士生放在一起的,目的是激励 NLP 和语言学研究。在最初检查了数据集并与创建者交谈后,我从 GitHub 中分出了存储库,并开始探索数据(https://github.com/yuvalpinter/nytwit)。
我碰到的原推文(谢谢,推特!)
在处理这个数据集时,我首先采取了一些初步的 EDA 措施:
- 查看我们正在使用的数据集的类型
- 检查形状(行、列)
- 查看头部和尾部(第一行和最后 5 行)
- 检查所有行和列(。信息())
- 获取一些介绍性的统计数据(。describe())
- 用获取值计数。值计数()
1.查看数据集的类型
这将使我们更好地理解我们正在处理的数据,以及可用于转换数据和检索见解的方法。为此,我们只需运行以下代码:

类型(数据)告诉我们,我们正在处理一个熊猫数据帧
告诉我们,我们正在处理一个“二维的、大小可变的、潜在异构的表格数据集”虽然这个语句中有很多内容需要解开,但这意味着我们的数据具有可以转换和过滤(大小可变)的行和列(二维),并且这些可转换的行和列包含潜在的异构数据类型(int64、float64、object、bool、datetime64 的混合)。
2.检查形状(行、列)
接下来,为了检查数据集中有多少行和列,我们编写以下代码行:

data.shape 将返回数据集的行数和列数
如上所述,(2587, 4)告诉我们有 2587 行和 4 列。
3.查看头部和尾部(第一行和最后 5 行)
现在我们对数据的大小和类型有了更好的了解,我们想检查一下我们正在处理哪种单元格值和列。这将让我们更好地了解我们可以用数据集合理地计算/测量什么。以下代码行将完成这一任务:

data.head()将返回数据集的前五行

data.tail()将返回数据集的最后 5 行
4.检查所有行和列
在我们知道数据的类型、大小和条目之后,分析每一列的数据类型是很重要的。这是通过使用。info(),如下面的操作所示:

。info()正在运行!
因此,到目前为止,我们的一些主要收获是:
- 我们正在处理一个熊猫数据框架
- 我们有 2587 行和 4 列
- 这 4 列是时间、单词、类别和 URL。时间列统一表示为
{Day of Week} {Month} {Day of Month} {Minutes}:{Hours}:{Seconds} {Year}。这将很容易将时间和日期分成单独的列,并通过索引访问特定的值。最后,类别和时间列包含重复的条目(我们可以从。头()和。tail())。 - 我们的列中没有空值,所有的数据类型(Dtype)都是
Object(我们正在处理干净的数据)。
这些都是我们可以用来构建的好点子。形容()和。value_counts() !
5.获取一些介绍性的统计数据(。describe())
现在,关键是我们要对数据集进行一些基本的统计测量。即使我们所有的数据类型都是对象(而不是 int64),我们仍然可以通过使用。describe(include = NP . object)(注意: **include = np.object** 当你有对象和非对象 Dtypes 时很有帮助,但也可以只包含对象 Dtypes )。代码如下所示:

。描述(include=np.object)
在这里,我们找到了数据集中最频繁出现的时间、单词、类别和 URL 值。这让我们深入了解我们可以可视化的内容(使用直方图、散点图等)。)以及我们可能需要在数据集中清理的东西(冗余行、不正确或意外的值等)。).接下来,我们需要更好地了解数据集列中的唯一值。这将有助于我们发现创建有效子数据集和将重要值分组在一起的方法。
6.用获取值计数。值计数()
我们可以利用。value_counts()在我们所有的列上,但是为了这个例子,我将把重点放在“Category”列上。代码如下:

类别列中的所有唯一值及其频率。
现在,我们对数据集有了很好的感觉,并准备构建一些可视化工具来传达我们发现的信息。首先,让我们使用 Plotly 和我们的“T3”数据框架构建一个直方图。
我喜欢 Plotly 的一点是它的简单性和交互性,这是我构建的每个情节的标准。虽然下面的截图不会包括交互式触摸,但我鼓励你去看看他们的图形库(https://plotly.com/)。代码如下:

这是输出结果:

简单易用的直方图
最后,让我们用 Seaborn 构建一个 distplot。在这里,我们将可视化每个月的每个日期有多少单词被添加到数据集(跨所有月份)。下面是我们将编写的代码,用于设置适当的 panda 系列和 distplot:

我们输入 sns.distplot()的熊猫系列代码
这里,我们创建一个空列表(date_df),我们可以用来自df['Time']的值填充它。每次我们遍历df['Time']时,我们都会将val[8:10](一个月中的几天)添加到date_df列表中。我们用[8:10]索引val,因为这是{Day of Week} {Month} {**Day of Month**} … {Year} 中日期索引出现的地方。在这之后,我们将date_df转换成熊猫系列(因为我们想要一个一维数组,而不是一个列表)。最后,我们用kde = False(删除内核密度估计值)、fit = norm(用正态分布拟合我们的图)和color='g'(将我们的频率条格式化为绿色)来配置 distplot。总的来说,这产生了以下视觉效果:

使用 Seaborn 快速简单地构建 distplot()。
这是一个非常基本的视觉需要一些工作,但它是一个良好的开端,为我们提供了宝贵的信息!
在本文中,我们讨论了 ed a 的许多重要部分。在接下来的几周里,我希望通过使用其他数据集和讨论我在处理数据时采取的其他重要步骤来继续建立这一基础。一些策略可能广泛适用于不同的数据集,而另一些策略可能专门针对我在文章中讨论的那个数据集。无论如何,我相信它会帮助你继续像数据科学家一样思考!直到下次,
丹·m。
附注:和往常一样,请随时向 danielmurph8@gmail.com 提出问题、意见和担忧。谢谢!
在 Pandas 数据框架中使用日期时间
帮助你开始数据分析的一些熊猫技巧

Datetime 是数据科学项目中常见的数据类型。通常,您会使用它并遇到问题。我发现 Pandas 是一个了不起的库,它包含了处理日期和时间的广泛功能和特性。
在本文中,我们将讨论以下常见的日期时间问题,应该可以帮助您开始数据分析。
- 将字符串转换为日期时间
- 从多个列组合一个日期时间
- 获取年、月和日
- 获取一年中的星期、星期几和闰年
- 从出生日期得到年龄
- 通过将日期列设置为索引来提高性能
- 选择特定年份的数据并执行聚合
- 选择具有特定月份和特定日期的数据
- 选择两个日期之间的数据
- 处理缺失值
源代码请查看我的 Github repo 。
1.将字符串转换为日期时间
Pandas 有一个名为to_datetime()的内置函数,可以用来将字符串转换为日期时间。让我们看一些例子
使用默认参数
Pandas to_datetime()能够将任何有效的日期字符串解析为 datetime,而不需要任何额外的参数。例如:
df = pd.DataFrame({**'date': ['3/10/2000', '3/11/2000', '3/12/2000']**,
'value': [2, 3, 4]})df['date'] = **pd.to_datetime(df['date'])** df

首日格式
默认情况下,to_datetime()会先解析月( MM/DD 、 MM DD、或 MM-DD )格式的字符串,这种安排在美国是比较独特的。
在世界上的大多数地方,日期是先写的( DD/MM 、 DD MM 或 DD-MM )。如果你想让熊猫先考虑日子而不是月份,你可以将参数dayfirst设置为True。
df = pd.DataFrame({**'date': ['3/10/2000', '3/11/2000', '3/12/2000']**,
'value': [2, 3, 4]})df['date'] = pd.to_datetime(df['date'], **dayfirst=True**)
df

或者,您可以向参数format传递一个自定义格式。
自定义格式
默认情况下,使用来自dateutil.parser.parse的 Pandas 内置解析器解析字符串。有时,您的字符串可能是自定义格式,例如, YYYY-DD-MM HH:MM:SS 。熊猫to_datetime()有一个名为format的参数,它允许你传递一个自定义格式:
df = pd.DataFrame({**'date': ['2016-6-10 20:30:0',
'2016-7-1 19:45:30',
'2013-10-12 4:5:1']**,
'value': [2, 3, 4]})df['date'] = pd.to_datetime(df['date'], **format="%Y-%d-%m %H:%M:%S"**)
df

用infer_datetime_format加速解析
如果不是 ISO8601 格式而是常规格式,传递infer_datetime_format=True通常可以加快解析速度。根据[1],在某些情况下,这可以将解析速度提高 5-10 倍。
# Make up 3000 rows
df = pd.DataFrame({'date': ['3/11/2000', '3/12/2000', '3/13/2000'] * 1000 })%timeit pd.to_datetime(df['date'], **infer_datetime_format=True**)
100 loops, best of 3: **10.4 ms** per loop%timeit pd.to_datetime(df['date'], **infer_datetime_format=False**)
1 loop, best of 3: **471 ms** per loop
处理解析错误
如果日期字符串不符合时间戳格式,您将得到一个类型错误。
df = pd.DataFrame({'date': ['3/10/2000', **'a/11/2000'**, '3/12/2000'],
'value': [2, 3, 4]})
df['date'] = pd.to_datetime(df['date'])

to_datetime()有一个名为errors的参数,允许您忽略错误或将无效值强制给NaT。
df['date'] = pd.to_datetime(df['date'], **errors='ignore'**)
df

df['date'] = pd.to_datetime(df['date'], **errors='coerce'**)
df

此外,如果您想在从 CSV 文件中读取数据时解析日期列,请查看下面的文章
[## 您应该知道的用 Pandas read_csv()解析日期列的 4 个技巧
一些最有用的熊猫把戏
towardsdatascience.com](/4-tricks-you-should-know-to-parse-date-columns-with-pandas-read-csv-27355bb2ad0e)
2.从多个列组合一个日期时间
to_datetime()也可以用于从多个列组合一个日期时间。键(列标签)可以是常见的缩写,如 ['年','月','日','分钟','秒',' ms ',' us ',' ns']) 或相同的复数。
df = pd.DataFrame(**{'year': [2015, 2016],
'month': [2, 3],
'day': [4, 5]}**)df['date'] = pd.to_datetime(df)
df

3.获取年、月和日
dt.year、dt.month和dt.day是从熊猫日期时间对象中获取年、月和日的内置属性。
首先,让我们创建一个伪 DateFrame 并将 DoB 解析为 datetime。
df = pd.DataFrame({'name': ['Tom', 'Andy', 'Lucas'],
'DoB': ['08-05-1997', '04-28-1996', '12-16-1995']})
df['DoB'] = **pd.to_datetime(df['DoB'])**
得到年、月、日
df['year']= **df['DoB'].dt.year**
df['month']= **df['DoB'].dt.month**
df['day']= **df['DoB'].dt.day**
df

4.获取一年中的星期、星期几和闰年
类似地,dt.week、dt.dayofweek和dt.is_leap_year是获取一年中的星期、星期几和闰年的内置属性。
df['week_of_year'] = **df['DoB'].dt.week**
df['day_of_week'] = **df['DoB'].dt.dayofweek**
df['is_leap_year'] = **df['DoB'].dt.is_leap_year**
df

请注意,Pandas **dt.dayofweek**属性返回一周中的某一天,并且假设该周从星期一开始,用 0 表示,到星期天结束,用 6 表示。要用全名替换号码,我们可以创建一个映射并将其传递给map():
**dw_mapping={
0: 'Monday',
1: 'Tuesday',
2: 'Wednesday',
3: 'Thursday',
4: 'Friday',
5: 'Saturday',
6: 'Sunday'
}**
df['day_of_week_name']=df['DoB'].dt.weekday**.map(dw_mapping)**
df

5.从出生日期得到年龄
获得年龄的最简单方法是减去年份:
**today = pd.to_datetime('today')**
df['age'] = **today.year - df['DoB'].dt.year**df

然而,这是不准确的,因为人们今年可能还没有过生日。更准确的解决方案是考虑生日
# Year difference
today = pd.to_datetime('today')
diff_y = today.year - df['DoB'].dt.year
# Haven't had birthday
b_md = df['DoB'].apply(lambda x: (x.month,x.day) )
**no_birthday = b_md > (today.month,today.day)**df['age'] = **diff_y - no_birthday**
df

6.通过将日期列设置为索引来提高性能
按日期选择数据的常见解决方案是使用布尔马克斯。例如
condition = (df['date'] > start_date) & (df['date'] <= end_date)
df.loc[condition]
该解决方案通常要求start_date、end_date和日期列为日期时间格式。事实上,当您在大型数据集中按日期进行大量选择时,这种解决方案会很慢。
如果您要按日期进行大量选择,首先将日期列设置为索引会更快,这样您就可以利用 Pandas 内置的优化。然后,您可以使用df.loc[start_date:end_date]按日期选择数据。让我们看一个示例数据集 city_sales.csv,它有 1,795,144 行数据
df = pd.read_csv('data/city_sales.csv',parse_dates=['date'])
df.info()RangeIndex: **1795144** entries, 0 to 1795143
Data columns (total 3 columns):
# Column Dtype
--- ------ -----
0 date datetime64[ns]
1 num int64
2 city object
dtypes: datetime64[ns](1), int64(1), object(1)
memory usage: 41.1+ MB
将日期列设置为索引
**df = df.set_index(['date'])**
df

7.选择特定年份的数据并执行聚合
假设我们想要选择 2018 年的所有数据
df.loc['2018']

并对选择执行聚合,例如:
获取 2018 年总人数
df.loc**['2018','num'].sum()****1231190**
获取 2018 年每个城市的总人数
df['2018']**.groupby('city').sum()**

8.选择具有特定月份和特定日期的数据
选择特定月份的数据,例如 2018 年 5 月
df.loc[**'2018-5'**]

类似地,选择一个月中特定日期的数据,例如 2018 年 5 月 1 日
df.loc[**'2018-5-1'**]

9 选择两个日期之间的数据
要选择两个日期之间的数据,可以使用df.loc[start_date:end_date]例如:
选择 2016 年到 2018 年之间的数据
df.loc[**'2016' : '2018'**]
选择 2018 年 5 月 2 日 10 点到 11 点之间的数据
df.loc[**'2018-5-2 10' : '2018-5-2 11'** ]
选择 2018 年 5 月 2 日 10:30 至 10:45 之间的数据
df.loc[**'2018-5-2 10:30' : '2018-5-2 10:45'** ]
而要选择时间之间的数据,我们要用between_time(),比如 10:30 和 10:45
df.**between_time('10:30','10:45')**
10 处理缺失值
我们经常需要计算窗口统计,例如滚动*均值或滚动总和。
让我们计算 3 个窗口期间的滚动总和,然后看看前 5 行。
df['rolling_sum'] = **df.rolling(3).sum()**
df.head()

我们可以看到,只有当有 3 个周期可以回顾时,它才开始有有效值。处理这个问题的一个解决方案是通过回填数据。
df['rolling_sum_backfilled'] = **df['rolling_sum'].fillna(method='backfill')**
df.head()

有关回填的更多细节,请查看以下文章
关于 Pandas 中缺失值以及如何使用内置方法处理它们的教程
towardsdatascience.com](/working-with-missing-values-in-pandas-5da45d16e74)
好了
感谢阅读。
请在我的 Github 笔记本上查看源代码。
如果你对机器学习的实用方面感兴趣,请继续关注。
以下是为您挑选的一些文章:
- 处理熊猫中缺失的值
- 用熊猫 read_csv()解析日期列的 4 个技巧
- 熊猫 read_csv()加快数据分析速度你应该知道的小技巧
- 加快数据分析速度你应该知道的 6 个熊猫技巧
- 数据科学项目开始时应该包括的 7 个设置。
参考
- [1] 熊猫来 _ 日期时间官方文件
使用 Geofence API 锁定特定区域的用户
Geofence API 为设备提供了监控全球圆形区域的能力

当你想通知用户他们是否在世界上的特定区域附*(如购物中心、机场、餐馆等)时,可以使用谷歌地理围栏 API。).例如,客户安装了一个在线购物应用程序,如果他们在与该应用程序有联系的商店附*,商店可以在应用程序中生成通知,让客户知道他们附*的商店正在为他们提供特价。这样你可以吸引更多的用户。
获取谷歌地图 API 密钥
要使用 Geofence API,您必须通过以下步骤获取 Google Maps API 密钥。
- 点击 这个 链接打开谷歌云*台,如果你还没有登录,那么请先登录并创建项目。

你可以给这个项目起任何名字,或者你可以让它保持原样,然后现在点击创建。
- 现在确保您已经选择了新创建的项目。

- 现在点击左边的菜单图标,选择API&服务,然后从子列表中选择库。
- 搜索 Maps SDK for Android 和启用该 API(不要忘记启用 API,因为这很重要)
- 现在再次点击左侧的菜单图标,选择凭证,然后选择 +创建凭证,最后点击 API 键
- 复制这个 API 键并将其粘贴到 string.xml 文件中
我们将要建造的
我们将构建一个应用程序,允许您在地图上设置您的位置(或者准确地说是特定区域)。当你到达指定位置时,你的应用程序会用你在设置位置时定义的消息通知你。
最后,你会有一个看起来像这样的应用程序。

要在地图上设置位置,您可以将标记拖到所需的位置。指定位置后,您可以点击继续按钮继续。

你现在兴奋吗?我相信你会的。
我们会学到什么?
- 谷歌地图 API
- 谷歌地理围栏 API
- 服务
- 广播收音机
- 创建通知
- 在共享首选项中存储和检索自定义对象
现在,让我们快速设置我们的项目并继续。
设置 Android 项目
- 创建一个新的 Android 项目,选择空的 activity,给你的项目起一个你想要的名字。
- 在您的应用程序级 build.gradle 中添加以下依赖项
implementation **'com.google.android.gms:play-services-location:16.0.0'** implementation **'com.google.android.gms:play-services-maps:16.0.0'** implementation **'com.google.code.gson:gson:2.8.5'** implementation **"com.google.android.material:material:1.1.0-alpha02"**
- 如果在 activity.xml 文件中遇到以下错误

然后在您的 build.gradle 中添加以下代码行
android{ defaultConfig {
vectorDrawables.useSupportLibrary = **true**}}
- 在您的
**AndroidManifest.xml**文件中添加以下权限
<**uses-permission android:name="android.permission.INTERNET"** />
<**uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"** />
- 现在将下面的元数据标签添加到您的
**AndroidManifest.xml**文件中的**<application>**标签内,稍后我们将在这里添加 API_KEY 。
<**meta-data
android:name="com.google.android.geo.API_KEY"
android:value="API_KEY"**/>
- 最后,同步项目和设置完成。
让我们快速熟悉一下这个应用中使用的一些 android 组件
服务:服务是一个 android 组件,用来执行后台任务。任务可以是任何类型的,例如上传照片、同步服务器上的用户数据和生成通知等。
广播接收器:广播接收器也是 android 的一个基本组件,当你想在你的应用程序中执行一些基于未来事件的操作时,它会很方便。例如,您只希望在手机连接到 WIFI 时将用户数据上传到您的服务器,另一个例子是,您希望在用户重启手机时启动后台服务。
pending content:PendingIntent类似于正常意图,只是不会立即执行。
让我们了解我们的班级
我把所有的类、实用函数和常量都放在根包中。

让我们逐一了解。
- MainActivity.kt: 这是我们的主屏幕,它将向您显示谷歌地图和右下角的两个操作按钮。第一个按钮将允许您放大地图,加号操作按钮将打开新活动
**ReminderActivity.kt**,在此屏幕上您可以选择您的位置,设置消息并保存提醒。 - ReminderActivity.kt: 正如我所解释的,这个活动将允许您设置位置并将提醒保存在共享首选项中。当您选择位置并保存它时,您将看到您指定的位置周围的圆圈。这个圆是你将在代码中定义的边界或半径。圆圈越大,意味着它将覆盖更大的区域。当你进入这个区域,你会收到一个通知。
- MyBroadcast Receiver: 这是一个广播接收器,当用户到达指定位置时会被触发。当这个广播接收器触发时,它将执行后台服务。
- 这是一个将由我们的广播接收机执行的服务,当它执行时,它生成通知,让用户知道他已经到达他的目的地。
- 这是一个数据类,在它的构造函数中,我接受了四个参数,它们是唯一的 id、纬度、经度、半径和一条消息。我们将把这些信息保存在共享首选项中,同时,我们将设置待定意向来生成通知。
- myreminderRepository . kt:如你所知,repository 帮助我们获得应用程序的核心方法来执行我们为应用程序定义的操作。所以只有这个类负责添加一个新的提醒,删除提醒和设置挂起的意图。我们将总是创建这个类的对象并执行动作。
- Utils.kt: 这是我们的实用程序类,包含四个方法。
**vectorToBitmap()**方法将矢量图像转换为位图,**showReminderInMap()**方法在地图上显示提醒,这是围绕指定位置画圆的方法。而**sendNotification()**方法创建了通知,这个方法由我们的后台服务调用。最后一个方法是**getUniqueId()**,它显然返回唯一的 id。 - AppConstants.kt: 这个类包含了这个应用使用的所有常量。这个类还包含一个服务方法和两个调用
MainActivity.kt和ReminderActivity.kt活动的方法。
说够了吗?让我们回到正题…
当您创建一个项目时,您将拥有一个默认的 ActivityMain.kt 类。我要你再创建一个类,命名为 ReminderActivity.kt,在这个类中扩展AppCompatActivity()类,实现OnMapReadyCallback谷歌地图的接口,实现其方法onMapReady()。现在,让一切保持原样。
现在我们将创建我们的 AppConstants 类,并定义所有的常量和一些方法。
在这个类中,我们定义了我们的常量和三个方法,newintentforminactivity()方法将调用MainActivity.kt类,newIntentForReminderActivity()将调用ReminderActivity.kt类。我们的广播接收器将调用 enqueueWork()方法来生成通知。
提示:所有的常量和方法都定义在**companion object**里面,这是因为无论何时我们需要调用这些方法中的任何一个,我们都不必创建这个类的对象。
现在我们将创建 Utils.kt 并添加以下方法
vectorToBimap()
当我们调用这个方法时,我们必须传递两个参数,即context.resources和需要转换成位图的图像的 id。context.*resources* 为您的应用程序返回一个资源实例。在这个方法中,我得到了 drawable 对象,创建了位图和画布对象。我们现在在vectorDrawable中有了我们的图像,我们将把这个对象转换成bitmap,然后在画布上绘制位图,设置它的边界并返回。
提示:如果你不熟悉画布,那么你可以把它想象成一张白纸,你可以在上面画出任何形状。
showReminderInMap()
**showReminderInMap()**方法将上下文、 GoogleMap 对象和 MyReminder 类对象作为参数,并检查myReminder是否有值,然后继续执行,否则不执行任何操作。在***if***语句中,我从 myReminder 对象中获取纬度和经度,并将其转换为LatLng,在第二个***if***语句中,我在指定位置周围画圆。一般来说,这种方法用于在指定的位置上画圆。
发送通知()
sendNotification()方法非常简单,它除了创建通知之外什么也不做。从服务中调用此方法来显示通知。
MyBroadcastReceiver.kt 类
现在在您的根目录中创建这个类,并将这个广播接收器添加到您的**AndroidManifest.xml**文件中。
<**receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true"** />
这个类扩展了抽象类BroadcastReceiver()并实现了它的onReceive()方法。当 onReceive()方法被执行时,它将执行AppConstants.kt.的enqueueWork()方法
当您添加提醒时,该接收器将通过PendingIntent注册。
GeofenceTransitionService.kt
创建这个类并在AndroidManifest.xml文件中声明它
<**service
android:name=".GeofenceTransitionService"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE"** />
注意:如果在清单中声明了一个作业服务,但没有使用 BIND_JOB_SERVICE 权限保护,该服务将被系统忽略。
这是一个扩展了JobIntentService类的服务,IntentService 或 JobIntentService 是一个在后台独立线程上运行的服务,它在任务完成时被销毁。要使用这个类,我们必须实现它所需的方法**onHandleWork()**。
**class** GeofenceTransitionService : JobIntentService() {**override fun** onHandleWork(intent: Intent) {
**val** geofencingEvent = GeofencingEvent.fromIntent(intent)
**repositoryMy** = MyReminderRepository(**this**)
**if** (geofencingEvent.hasError()) {
Log.e(AppConstants.**LOG_TAG**, **"An error occured"**)
**return** }
handleEvent(geofencingEvent)
}
}
在这个方法中,我们可以完成我们的任务。我在这个方法中获取**GeofencingEvent**类的对象,如果有问题,我们显示错误消息,否则我们调用handleEvent()方法。
**private fun** handleEvent(event: GeofencingEvent) {
**if** (event.*geofenceTransition* == Geofence.*GEOFENCE_TRANSITION_ENTER*) {
**val** reminder = getFirstReminder(event.*triggeringGeofences*)
**val** message = reminder?.**message
val** latLng = reminder?.**latLng
if** (message != **null** && latLng != **null**) {
*sendNotification*(**this**, message, latLng)
}
}
}
在此方法中,我们首先使用 GeofencingEvent 对象,检查用户是否进入了指定区域,如果是,则从存储库中获取 reminder 对象,检索其数据并发送通知。
**private fun** getFirstReminder(triggeringGeofences: List<Geofence>): MyReminder? {
**val** firstGeofence = triggeringGeofences[0]
**return repositoryMy**.get(firstGeofence.*requestId*)
}
**getFirstReminder()**方法从MyRepository类返回MyReminder类的对象。
下面是这个服务类的完整代码
提示: **lateinit** 是 Kotlin 中的一个关键字,可以让你在声明的时候不用初始化它,但是在使用之前应该初始化它,否则你会遇到 NullPointerException 。
MyReminderRepository.kt
您可以将该类视为本地服务器,这意味着它为我们提供了在地图上执行任何操作所需的一切,例如添加提醒或删除提醒以及维护所有提醒。
以下代码片段创建了一个 GeofencingClient 对象,帮助您操作地理围栏。
**private val geofencingClient** = LocationServices.getGeofencingClient(**context**)
这里我们得到了 SharedPreference 对象和 Gson 对象
**private val preferences** = **context**.getSharedPreferences(*PREFS_NAME*, Context.*MODE_PRIVATE*)
**private val gson** = Gson()
在SharedPreference中,我们将存储提醒,并使用Gson对象将 json 字符串转换成 Java 对象,反之亦然。既然 SharedPreference 只能保存键值对形式的原始值,但是我们想要存储我们的类对象,那么这怎么可能呢?。显然,我们可以将我们的对象转换成 json 字符串并存储在 SharedPreference 中,每当我们需要使用它时,我们都会将其转换成一个真实的对象。
buildGeofence():
纬度、经度和半径的组合称为地理围栏。它是地图上特定位置的圆形区域。每当用户进入这个区域,应用程序就会触发特定的行为。
**private fun** buildGeofence(myReminder: MyReminder): Geofence? {
**val** latitude = myReminder.**latLng**?.**latitude
val** longitude = myReminder.**latLng**?.**longitude
val** radius = myReminder.**radius
if** (latitude != **null** && longitude != **null** && radius != **null**) {
**return** Geofence.Builder()
.setRequestId(myReminder.**id**)
.setCircularRegion(
latitude,
longitude,
radius.toFloat()
)
.setTransitionTypes(Geofence.*GEOFENCE_TRANSITION_ENTER*)
.setExpirationDuration(Geofence.*NEVER_EXPIRE*)
.build()
}
**return null** }
看看代码,我们正在使用 Geofence 构建地理围栏。构建器()。
在方法 setRequestId() 中,我传递了唯一的 Id,因为每个 geofence 都是唯一的,您将从您的模型类中获得这个 id。
在 setCircularRegion() 中,我正在设置我的纬度、经度和半径,所有这些参数一起在一个特定的位置上形成一个圆。
setTransitionTypes() 定义了过渡类型,可以使用*GEOFENCE_TRANSITION_ENTER* 在用户进入定义区域时触发事件。
setExpirationDuration()方法设置 geofence 的到期时间,我们使用的是*NEVER_EXPIRE* ,这意味着它永远不会到期,直到用户删除它。
buildFencingRequest():
您将使用此方法构建 geofence 请求。
**private fun** buildGeofencingRequest(geofence: Geofence): GeofencingRequest {
**return** GeofencingRequest.Builder()
.setInitialTrigger(0)
.addGeofences(*listOf*(geofence))
.build()
}
这里,您将setinitiatrigger()设置为 0,这意味着您不希望您的应用程序在已经位于 geofence 内部时触发事件。下一个 addGeofences()方法将请求添加到 geofence。
建筑地理围栏待定内容:
正如您在代码片段中看到的,我们在 intent 对象中传递 MyBroadcastReceiver,这意味着当调用此待定 intent 时,它将调用 BroadcastReceiver,而此 Receiver 将调用geofencetransationservice。
**private val geofencePendingIntent**: PendingIntent **by** *lazy* **{
val** intent = Intent(**context**, MyBroadcastReceiver::**class**.*java*)
PendingIntent.getBroadcast(
**context**,
0,
intent,
PendingIntent.*FLAG_UPDATE_CURRENT* )
**}**
该代码只有在*GEOFENCE_TRANSITION_ENTER*事件被触发时才会被执行。
提示:通过 惰性属性,该值仅在第一次使用时计算。
添加():
这个add()方法将提醒添加到 SharedPreference 中,并将请求添加到 geofence 中。
**fun** add(myReminder: MyReminder, success: () -> Unit, failure: (error: String) -> Unit)
{
**val** geofence = buildGeofence(myReminder)
**if** (geofence != **null** && ContextCompat.checkSelfPermission(**context**, android.Manifest.permission.*ACCESS_FINE_LOCATION*) == PackageManager.*PERMISSION_GRANTED*) {
**geofencingClient**.addGeofences(buildGeofencingRequest(geofence), **geofencePendingIntent**)
.addOnSuccessListener **{** saveAll(getAll() + myReminder)
success()
**}** .addOnFailureListener **{** failure(**"An error occured"**)
**}** }
}
首先,我们检查权限是否被授予,然后我们继续,接下来,我们使用 geofencingClient 创建地理围栏。如果成功,那么只有我们将保存在共享首选项中,否则报告错误消息。
移除():
这个方法只是从列表中删除提醒。
**fun** remove(myReminder: MyReminder) {
**val** list = getAll() - myReminder
saveAll(list)
}
saveAll():
此方法会将列表中的所有提醒事项添加到共享偏好设置中。
**private fun** saveAll(list: List<MyReminder>) {
**preferences** .edit()
.putString(*REMINDERS*, **gson**.toJson(list))
.apply()
}
注意,我们将列表转换为 Json 字符串,然后存储它。
getAll():
该方法返回我们存储在共享首选项中的所有提醒列表。
**fun** getAll(): List<MyReminder> {
**if** (**preferences**.contains(*REMINDERS*)) {
**val** remindersString = **preferences**.getString(*REMINDERS*, **null**)
**val** arrayOfReminders = **gson**.fromJson(
remindersString,
Array<MyReminder>::**class**.*java* )
**if** (arrayOfReminders != **null**) {
**return** arrayOfReminders.*toList*()
}
}
**return** *listOf*()
}
getLast():
这个函数返回添加到列表中的提醒。
**fun** getLast() = getAll().*lastOrNull*()
注意这个函数首先调用返回列表的getAll()方法,我们调用lastOrNull()函数,它是List类的一个方法,这个方法返回最后一个值。
get():
如果列表不是null.,该方法从列表中返回第一个提醒
**fun** get(requestId: String?) = getAll().*firstOrNull* **{ it**.**id** == requestId **}**
{it.id == requestId }是执行空值检查的 lambda 表达式。当 lambda 表达式中只有一个参数时,可以使用**it**。
MyReminderRepository.kt 的完整代码
ReminderActivity.kt:
这个活动允许我们在指定的位置添加新的提醒。
首先,在本活动中创建这些对象。
**private lateinit var map**: GoogleMap
**lateinit var myRepository**: MyReminderRepository
**private var reminder** = MyReminder(latLng = **null**, radius = **null**, message = **null**)
现在将下面的代码粘贴到您的onCreate()方法中
**override fun** onCreate(savedInstanceState: Bundle?) {
**super**.onCreate(savedInstanceState)
setContentView(R.layout.*activity_new_reminder*)
**myRepository** = MyReminderRepository(**this**)
**val** mapFragment = *supportFragmentManager* .findFragmentById(R.id.*map*) **as** SupportMapFragment
mapFragment.getMapAsync(**this**)
instructionTitle.*visibility* = View.*GONE* radiusDescription.*visibility* = View.*GONE* message.*visibility* = View.*GONE* }
在这个 onCreate()方法中,我初始化了我的存储库实例变量并设置了映射。最初,我隐藏了所有的文本视图。
中央摄像机():
在这种方法中,我从意图中获取缩放值,并将相机设置在位置的中心。
**private fun** centerCamera() {
**val** latLng = *intent*.*extras*?.get(AppConstants.**LAT_LNG**) **as** LatLng
**val** zoom = *intent*.*extras*?.get(AppConstants.**ZOOM**) **as** Float
**map**.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, zoom))
}
showReminderUpdate():
此方法更新提醒。在调用这个方法的时候,它会显示出这个位置周围的圆圈。
**private fun** showReminderUpdate() {
**map**.clear()
*showReminderInMap*(**this**, **map**, **reminder**)
}
showConfigurationLocationStep():
这种方法只是指导您如何在地图上导航。
**private fun** showConfigureLocationStep() {
marker.*visibility* = View.*VISIBLE* instructionTitle.*visibility* = View.*VISIBLE* radiusDescription.*visibility* = View.*GONE* message.*visibility* = View.*GONE* instructionTitle.*text* = **"Drag map to set location"** next.setOnClickListener **{
reminder**.**latLng** = **map**.*cameraPosition*.**target** showConfigureRadiusStep()
**}** showReminderUpdate()
}
showConfigurationMessageStep():
这个方法和上面的方法一样,它指导你如何添加消息。当您收到通知时,会显示此消息。
**private fun** showConfigureMessageStep() {
marker.*visibility* = View.*GONE* instructionTitle.*visibility* = View.*VISIBLE* message.*visibility* = View.*VISIBLE* instructionTitle.*text* = **"Enter a message"** next.setOnClickListener **{
reminder**.**message** = message.*text*.toString()
**if** (**reminder**.**message**.*isNullOrEmpty*()) {
message.*error* = **"Message Error"** } **else** {
addReminder(**reminder**)
}
**}** showReminderUpdate()
}
addReminder():
该方法从 MyReminderRepository 类调用 add 方法并添加提醒。如果提醒添加成功,那么它会在底部显示一条消息“提醒已添加”,否则会显示一条失败消息。
**private fun** addReminder(myReminder: MyReminder) {
**myRepository**.add(myReminder,
success = **{** setResult(Activity.*RESULT_OK*)
finish()
**}**,
failure = **{** Snackbar.make(main, **it**, Snackbar.*LENGTH_LONG*).show()
**}**)
}
最后修改您的 onMapReady()
**override fun** onMapReady(googleMap: GoogleMap) {
**map** = googleMap
**map**.*uiSettings*.*isMapToolbarEnabled* = **false** centerCamera()
showConfigureLocationStep()
}
在这个方法中,我们将GoogleMap对象分配给我们的地图,然后配置设置。
此活动的完整代码
此活动的布局文件
在
主活动. kt:
最后,这是我们代码的最后一部分。这是我们的家庭活动,当你第一次与应用程序互动时,你会看到它。
首先,在您的活动中添加这些侦听器,并实现它的所有方法。
**class** MainActivity : AppCompatActivity(), OnMapReadyCallback, GoogleMap.OnMarkerClickListener
现在创建这些实例变量。
**lateinit var myRepository**: MyReminderRepository
**private var map**: GoogleMap? = **null
private lateinit var locationManager**: LocationManager
onCreate():
在这个方法中,我们首先初始化实例变量,然后从 activity_main.xml 文件的一个片段和动作按钮上的侦听器中获取一个映射。最初,这些按钮将不可见,直到用户给予 ACCESS_FINE_LOCATION 权限。当用户授予权限时,我们在**onMapAndPermissionReady()**方法中使按钮对用户可见。
看看下面的代码,我在按钮上附加了一个监听器,在 lambda 表达式中,我调用了 Google map 类的**run**方法。这个run也是一个λ函数。当用户按下 (+) 动作按钮时,我们将用户带到 ReminderActivity.kt 类。
actionNewReminder.setOnClickListener **{
map**?.*run* **{
val** intent = AppConstants.newIntentForReminderActivity(
**this**@MainActivity,
*cameraPosition*.**target**,
*cameraPosition*.**zoom** )
startActivityForResult(intent, AppConstants.**REMINDER_REQUEST_CODE**)
**}
}**
onMapAndPermissionReady():
当许可被授予并且地图不等于null时,那么我们使我们的按钮可见并且添加一个监听器到actionCurrentLocation按钮。现在,如果用户按下actionCurrentLocation按钮,我们将获得位置提供者和用户的最后已知位置。如果这个不为空,那么我们将把摄像机移动到最后一个已知的位置。
onMarkerClick():
当用户点击标记时,会弹出一个警告对话框,询问用户是否想删除提醒,如果他按“是”,那么该提醒会从列表中删除。
**override fun** onMarkerClick(marker: Marker): Boolean {
**val** reminder = **myRepository**.get(marker.*tag* **as** String)
**if** (reminder != **null**) {
showReminderRemoveAlert(reminder)
}
**return true** }
showreminderemovealert():
这个方法只创建一个有两个选项的警告对话框,即 OK 和 Cancel ,如果用户点击“OK”,那么我们调用 removeReminder()来删除它。
**private fun** showReminderRemoveAlert(myReminder: MyReminder) {
**val** alertDialog = AlertDialog.Builder(**this**).create()
alertDialog.*run* **{** setMessage(**"Reminder Removed"**)
setButton(
AlertDialog.*BUTTON_POSITIVE*,
**"OK"** ) **{** dialog, _ **->** removeReminder(myReminder)
dialog.dismiss()
**}** setButton(
AlertDialog.*BUTTON_NEGATIVE*,
**"Cancel"** ) **{** dialog, _ **->** dialog.dismiss()
**}** show()
**}** }
ActivityMain.kt 文件的完整代码
现在,我们终于有了 activity_main.xml 文件
运行和测试
- 运行应用程序并给予许可。
- 如果你在模拟器上运行应用程序,然后点击三个点,你会看到下面的屏幕。现在设置任何你想要的位置(我设置了新德里的康诺特广场)。并保存该位置。

- 现在,单击应用程序上的第一个操作按钮,您会看到您的地图将导航到指定的位置。
- 现在通过点击(+)操作按钮添加提醒,拖动地图设置您的位置,点击继续,现在输入您的消息,再次点击继续。现在您的提醒已保存。

- 现在再次点击三个点,改变位置到模拟器的其他地方,并保存它。
- 点击应用程序的第一个按钮,你将到达你在模拟器上设置的位置。
- 最后,在模拟器中,设置保存提醒时的位置。等几秒钟,你会收到通知。

如果你的项目有任何困难,那么你可以从我的 Github 项目中寻求帮助。
我希望你喜欢读这篇文章,你也可以访问我的 网站 ,在那里我会定期发布文章。
订阅 我的邮件列表,以便在您的收件箱中直接获得我的文章,并且不要忘记关注我自己在 Medium 上发表的文章The Code Monster来完善您的技术知识。
结论
我们已经了解了如何在自己的项目中使用谷歌地图和 Geofence APIs,还了解了服务和广播接收器。除此之外,我们还学习了一个技巧,使用 Gson 在 SharedPreference 中存储自定义 java 对象,并使用相同的方法检索它们。
使用 HDF5 文件和创建 CSV 文件
HDF5 至 CSV 转换

由杯先生/杨奇煜·巴拉尔在 Unsplash 上拍摄的照片
在我的上一篇文章中,我讨论了从 GES 光盘下载 NASA 数据的步骤。下载的数据文件为 HDF5 格式。HDF5 是一种文件格式和技术,支持管理非常大的数据集合。因此,它在存储信息方面非常受欢迎。要获得 NASA 的数据,请先查看下面的文章:
访问 GES 光盘数据文件
towardsdatascience.com](/getting-nasa-data-for-your-next-geo-project-9d621243b8f3)
每当我处理数据集时,我最喜欢 CSV 文件。因此,当我得到 HDF5 文件后,我决定想办法将它们转换成 CSV 文件。我在 Python 中找到了包h5py,它支持 HDF5 文件的读入。因此,本文阐述了使用h5py并将 HDF5 转换为 CSV 的步骤。您可以通过参考下面链接中的完整笔记本来跟进。
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/kb22/NASA-data-exploration/blob/master/Coverting HDF5 to CSV.ipynb)
导入库
对于这项工作,我们需要两个库。第一个库是h5py,它可以选择读取和处理 HDF5 文件(文档)。我们需要的第二个包是用于数组的numpy。最后,我们将导入pandas,这样我们就可以创建一个数据帧,然后保存为 CSV 文件。
加载数据集
下一步是载入 HDF5 文件。请注意,对于这个示例,我正在处理从 GES 光盘收集的 2020 年 1 月全球的 GPM 数据。它位于 GitHub repo 中的 data 文件夹内(从 GES 光盘网站下载)。
我将使用h5py库中的File方法来读取 HDF5 文件,并将其保存到名为dataset的变量中。我指定文件名和完整路径作为第一个参数,并将第二个参数设置为r,表示我正在以只读模式处理这个文件。
浏览数据集
既然文件已经加载完毕,我们就可以开始研究数据集了。这个文件的元素类似于 Python 字典。因此,它由键值对组成。所以,我从查看这个文件中的各种键开始。
正如我们在上面的结果中看到的,只有一个名为Grid的键。因为我们看不到任何数据,数据可能在这个键的值里面;这就是我所做的,我用dataset[’Grid’]将它的值读入grid,并查看它的键。
最后我们看数据。我们有几个可以使用的特性。所有这些都代表键,它们会有相应的值,这就是我们要找的。对于这项工作,我只对纬度、经度和该经纬度组合下的降雨量感兴趣。
让我们逐一看看这些特性。
经度
我打印经度键及其属性。我们发现经度有 3600 个值。在属性中,units和standard_name是我想使用的两个东西。
纬度
类似于经度,我检查键及其属性。有 1800 个纬度值和units和standard_name对我有用。
沉淀
我打印沉淀的键和属性。
降水数据是一个三维数组,降水值存储为二维矩阵。它的形状为(3600,1800 ),这意味着它具有所有经度和纬度组合的降水量值。此外,降水的单位在属性units中被发现为mm/hr。
创建数据框架
现在我们知道了我们想要在最终的 CSV 文件中捕获的所有内容,我们将直接开始捕获它。
我们的数据集将有 6,480,000 行(3600x1800)。这些行中的每一行都有经度和纬度的唯一组合。因此,对于每个经度,我们有一个纬度和相应的降雨量。
为了创建所有降水值的经度值列表,我们需要将经度列表中的每个值重复 1800 次。使用np.repeat()将其保存为总长度为 6,480,000 个值的longitude_values(3600 个经度值,每个值表示 1800 次)。
对于每个经度值,我们需要所有的纬度值。因此,为了创建最终的latitude_values列表,我们将完整的列表乘以 3600,创建一个 6,480,000 长的列表,其中纬度列表重复 3600 次。
最后,为了转换降水值的矩阵,我使用了flatten()函数将其转换为一个长列表。
然后,我将这些列表保存为标签为lon、lat和precipitation的dataset数据帧的列。我用上面提取的标签和单位来重命名这些列。注意,由于字节编码,字符串用b保存,所以我将这些属性附加上decode()以获得字符串值。

dataset.head()
一些降水值没有被捕获或丢失,在数据集中用-9999.900391表示,所以我用值0屏蔽它们。
最后一步,我将数据帧作为 CSV 文件precipitation_jan_2020.csv保存在数据文件夹中。
就是这样!我们有 CSV 文件。
结论
在本文中,我描述了用 Python 加载 HDF5 文件、读取其元素并最终创建可以保存为 CSV 文件的数据帧的步骤。对于任何其他 HDF5 文件,也可以复制相同的步骤。
希望你喜欢这篇文章。如果你有任何问题、建议或想法,请在评论中提出。
使用拥抱脸变形金刚和 TF 2.0
基于变形金刚的模型是当前 NLP 世界的热点。拥抱脸的变形金刚库提供了所有的 SOTA 模型(像 BERT,GPT2,RoBERTa 等)用于 TF 2.0,这个博客旨在展示它的接口和 API

0.放弃
我假设你知道变形金刚和它的注意力机制。这个博客的主要目的是展示如何在 TF 2.0 中使用 Hugging Face 的 transformer 库,也就是说,它将是一个更加关注代码的博客。
1.介绍
拥抱脸最初只支持 PyTorch,现在 TF 2.0 也很支持。在 PyTorch 上使用 transformer 库,你可以找到很多高质量的教程,但是在 TF 2.0 上就不一样了(这是这篇博客的主要动机)。
使用 BERT 甚至 AlBERT 是相当容易的,TF 2.0 中的标准流程由 tensorflow_hub 提供,但 GPT2、RoBERTa、DistilBERT 等就不一样了。抱脸的变形金刚库来救援了。它们提供了直观的 API 来从头构建定制模型,或者为大量基于 transformer 的模型微调预先训练的模型。
它支持广泛的自然语言处理应用,如文本分类、问答系统、文本摘要、标记分类等。前往他们的文档了解更多细节。
本教程将基于 Kaggle 的有毒评论分类挑战的多标签文本分类。
以下是任何变压器型号的通用管道:
标记器定义→文档的标记化→模型定义→模型训练→推理
现在让我们一个接一个地检查它们,我还将尝试涵盖多个可能的用例。
2.拥抱面变压器总管道
2.1 标记器定义
每个基于 transformer 的模型都有一个独特的标记化技术,特殊标记的独特使用。transformer 库为我们解决了这个问题。它支持与它相关联的每个模型的标记化。
→每个变压器型号都有类似的令牌定义 API
→这里我使用了一个来自预训练模型的记号化器。
→这里,
- add_special_tokens:用于添加特殊字符,如
、 、 等正在使用的 w.r.t 预训练模型。它应该永远保持真实 - max_length:任何要标记的句子的最大长度,这是一个超参数。(最初 BERT 的最大长度为 512)
- pad_to_max_length:执行填充操作。
2.2 文档的标记化
下一步是在文档上执行标记化。它可以通过 encode()或 encode_plus()方法来执行。
→任何变压器模型通常需要三个输入:
- 输入 id:与他们的词汇相关联的单词 id
- 关注面膜:必须关注哪个 id;1 =注意。简单地说,它告诉模型哪些是原始单词,哪些是填充单词或特殊标记
- 令牌类型 id:它与使用多句子的模型相关联,如问答模型。它告诉模型句子的顺序。
→虽然不是必须提供所有这三个 id,只有输入 id 也可以,但是注意力屏蔽有助于模型只关注有效单词。因此,至少对于分类任务,这两者都应该提供。
2.3 培训和微调
现在是最关键的部分,即“训练”。我将要讨论的方法绝不是训练的“唯一可能的方法”。虽然经过大量的实验,我发现这个方法是最可行的。我将讨论训练模型的三种可能方式:
- 直接使用预训练模型作为分类器
- 提取嵌入并将其用作另一个分类器的输入的转换器模型。
- 在定制配置和数据集上微调预训练的转换器模型。
2.3.1 直接使用预训练模型作为分类器
这是最简单的,但也是应用最少的。拥抱脸的变形金刚库提供了一些具有序列分类能力的模型。这些模型有两个头部,一个是作为基础的预训练模型架构&一个分类器作为顶部头部。
标记器定义→文档的标记化→模型定义

直接作为分类器的预训练模型的总结
→注:顺序分类的模型仅适用于此。
→定义适当的配置在这里至关重要。正如您在第 6 行看到的,我正在定义配置。“num_labels”是当模型是分类模型时要使用的类的数量。它还支持各种配置,请继续查看他们的文档。
→这里需要注意的一些关键事项是:
- 这里只有预训练模型的权重可以被更新,但是更新它们不是一个好主意,因为它将违背迁移学习的目的。所以,实际上这里没有什么要更新的。这是我最不喜欢这个的原因。
- 它也是最不可定制的。
- 你可以尝试的一个技巧是使用 no 更高的 num_labels,最后在末尾添加一个可以训练的密集层。
# Hack
config = DistilBertConfig(num_labels=64)
config.output_hidden_states = Falsetransformer_model=TFDistilBertForSequenceClassification.from_pretrained(distil_bert, config = config) input_ids = tf.keras.layers.Input(shape=(128,), name='input_token', dtype='int32')
input_masks_ids = tf.keras.layers.Input(shape=(128,), name='masked_token', dtype='int32')
X = transformer_model(input_ids, input_masks_ids)[0]
X = tf.keras.layers.Dropout(0.2)(X)
X = tf.keras.layers.Dense(6, activation='softmax')model = tf.keras.Model(inputs=[input_ids, input_masks_ids], outputs = X)for layer in model.layer[:2]:
layer.trainable = False
2.3.2 提取嵌入的变压器模型,并将其用作另一个分类器的输入
这种方法需要两个层次或两个独立的模型。我们使用任何变换器模型来提取单词嵌入&然后使用这个单词嵌入作为任何分类器(例如逻辑分类器、随机森林、神经网络等)的输入。
我建议你阅读由杰伊·阿拉姆马撰写的这篇文章,它非常详细清晰地讨论了这种方法。
因为这个博客是关于神经网络的,所以让我给你一个关于神经网络的例子。

模型摘要
→11 号线是这里的关键。我们只对可以使用切片操作提取的模型的
→与 2.3.1 方法相比,这种方法通常每次都更有效。但是它也有一些缺点,比如:
- 它不太适合生产,因为您必须使用 transformer 模型作为唯一的特征提取器,因此您现在必须维护两个模型,因为您的分类器头是不同的(像 XGBoost 或 cat saw)。
- 在将 3D 数据转换为 2D 时,我们可能会错过有价值的信息。
如果您只想提取单词嵌入,transformers 库提供了一个很好的工具。
import numpy as np
from transformers import AutoTokenizer, pipeline, TFDistilBertModel
model = TFDistilBertModel.from_pretrained('distilbert-base-uncased')tokenizer = AutoTokenizer.from_pretrained('distilbert-base-uncased')pipe = pipeline('feature-extraction', model=model,
tokenizer=tokenizer)features = pipe('any text data or list of text data',
pad_to_max_length=True)features = np.squeeze(features)
features = features[:,0,:]
2.3.3 微调预应变变压器型号
这是我最喜欢的方法,因为这里我们充分利用了任何变压器模型的潜力。在这里,我们将使用预训练变压器模型的权重,然后对我们的数据进行微调,即迁移学习。

→请看第 17 行,因为 3D 数据是在嵌入层之前生成的,我们可以使用 LSTM 提取更多细节。
→下一步是将 3D 数据转换为 2D,以便我们可以使用 FC 层。您可以使用任何池层来执行此操作。
→另外,注意第 18 和 19 行。我们应该总是冻结变压器模型的预训练权重&永远不要更新它们,只更新剩余的权重。
一些临时演员
→每种方法都有两个共同点:
- config . output _ hidden _ States = False;因为我们正在训练&对输出状态不感兴趣。
- x = transformer _ model(…)[0];这在 config.output_hidden_states 中是内联的,因为我们只需要顶部的头。
→config 是字典。因此,要查看所有可用配置,只需简单地打印它。
→小心选择基本型号,因为 TF 2.0 支持是新的,所以可能会有错误。
2.4 推理
由于该模型基于 tf.keras 模型 API,我们可以使用 keras 常用的 model.predict()方法
我们甚至可以使用 transformer 库的管道实用程序(请参考 2.3.2 中所示的示例)。这个实用程序非常有效,因为它在一个通用的简单 API 下统一了标记化和预测。
3.结束注释
拥抱脸真的很容易使用他们的任何模型,现在与 tf.keras。它有很大的可能性。
他们还使得在交叉库中使用他们的模型变得非常容易(从 PyTorch 到 TF,反之亦然)。
我建议访问他们的文档,因为他们有非常直观和中肯的文档。
你可以通过 LinkedIn 联系我
在 Python 中使用 JSON
在 5 分钟内学会基础知识

丹尼斯·帕夫洛维奇在 Unsplash 上的照片
在这篇博文中,我们将学习 JSON。我们将学习 JSON 是什么,它为什么存在,人们为什么使用它,以及最终如何利用 python 的能力来帮助我们处理它。
作为一名数据科学家、程序员或者仅仅是一名技术爱好者,理解 JSON 是什么以及如何使用它是很重要的。我确信,一旦你理解了它,你会发现它的许多应用。
JSON 是什么?
JSON 代表 JavaScript 对象符号,它本质上是一种表示数据的方式。JSON 遵循一种人类可以自然理解的格式,它真正的力量来自于它捕捉复杂数据关系的能力。
您可以在以下示例中看到这一点:
{
"usersName": "Costas",
"website": {
"host": "[www.medium.com](http://www.medium.com)",
"account": "costasandreou",
"blogpost": [
{
"type": "title",
"description": {
"main" : "Working with JSON in Python",
"sub" : "the info you need"
}
}
]
}
}
注意 JSON 是如何在键值对中构造的,同时还保存数组对象(注意“blogpost”下面有几个类型)。如果你来自一个*面数据结构背景(想想 Excel 或 Pandas dataFrames),这一点非常重要。
JSON 为什么会存在?
JSON 创建于千禧年之初,是为了将我们从 Flash 和 Java 小程序中拯救出来。如果你碰巧不记得那些浏览器内置插件,请认为你非常幸运,因为它们是噩梦的素材!但是我跑题了。JSON 是为无状态实时服务器到浏览器的通信提供协议而构建的。
如果您已经熟悉 XML,那么您可以将 JSON 视为一种轻量级的、更容易使用、更快速的替代方案。
人们为什么使用 JSON?
据信,第一家开始提供服务并因此普及 JSON 应用的大公司是 2005 年的雅虎。现在认为 JSON 是最常用的数据格式。
人们使用 JSON 的三大原因是:
- 非常易于阅读、书写和操作
- 通过网络传输非常快
- 受所有主流浏览器支持,后端技术堆栈
阅读 JSON
当您必须使用 JSON 时,您想做的第一件事就是将它读入您的 Python 应用程序。Python 中的 json 库期望 JSON 以字符串的形式出现。
假设您的数据 JSON 数据已经是一个字符串:
obj = '{ "usersName": "Costas", "website": { "host": "www.medium.com", "account": "costasandreou", "blogpost": [ { "type": "title", "description": { "main" : "Working with JSON in Python", "sub" : "the info you need" } } ] } }'import json
json_obj = json.loads(obj)
print('obj: ',type(obj))
print('obj.usersName: ', json_obj['usersName'])
它返回:
obj: <class 'str'>
obj.usersName: Costas
另一方面,如果您将 json 保存在 Python 对象中(比如字典),JSON 库允许您将它转换回字符串,以便进一步处理。
obj1 = { "usersName": "Costas", "website": { "host": "[www.medium.com](http://www.medium.com)", "account": "costasandreou", "blogpost": [ { "type": "title", "description": { "main" : "Working with JSON in Python", "sub" : "the info you need" } } ] } }print('obj1: ',type(obj1))
json_obj1str = json.dumps(obj1)
print('json_obj1str: ', type(json_obj1str))
json_obj1 = json.loads(json_obj1str)
print('obj1.usersName: ', json_obj1['usersName'])
它返回:
obj1: <class 'dict'>
json_obj1str: <class 'str'>
obj1.usersName: Costas
使用 JSON
既然我们已经看到了如何将 JSON 数据加载到 Python 应用程序中,那么让我们看看使用 JSON 的选项。
直接查询
从 JSON 中提取信息就像定义我们要寻找的路径并提供键值对的名称一样简单。我们来看几个例子。
>>> print(json_obj['usersName'])
Costas>>> print(json_obj['website'])
{'host': '[www.medium.com'](http://www.medium.com'), 'account': 'costasandreou', 'blogpost': [{'type': 'title', 'description': {'main': 'Working with JSON in Python', 'sub': 'the info you need'}}]}>>> print(json_obj['website']['host'])
[www.medium.com](http://www.medium.com)>>> print(json_obj['website']['blogpost'])
[{'type': 'title', 'description': {'main': 'Working with JSON in Python', 'sub': 'the info you need'}}]>>> print(json_obj['website']['blogpost'][0]['description']['sub'])
the info you need
一旦我们可以像上面那样提取数据,我们就可以很容易地将它们存储在列表或数据库中,以便进一步处理。
用熊猫 处理你的数据
如果您已经习惯了 Pandas,并且希望使用*面数据,有一种快速的方法可以将数据放入数据框中。首先,你可以将的第一层数据添加到数据帧中。
df = pd.read_json(obj)

如您所见,read_json 接受 string 数据类型,并且只允许我们查看顶级信息。换句话说,它不会使任何结构变*。我们需要一种方法来浏览较低层次的数据。
我们可以通过使用 json_normalise pandas 方法将数据进一步扁*化。
obj = json.loads(obj)
df1 = pd.json_normalize(obj)

扁*化 JSON
在某些情况下,您只是想将数据展*。它当然可以使数据分析有时更快,并可以允许大量记录的可视化检查(想想 Excel 类型的数据过滤)。
您可以使用第三方库来实现。首先,安装库:
pip install json-flatten
然后,运行以下命令:
>>> import json
>>> obj = json.loads(obj)>>> import json_flatten
>>> print(json_flatten.flatten(obj)){'usersName': 'Costas', 'website.host': '[www.medium.com'](http://www.medium.com'), 'website.account': 'costasandreou', 'website.blogpost.0.type': 'title', 'website.blogpost.0.description.main': 'Working with JSON in Python', 'website.blogpost.0.description.sub': 'the info you need'}
交叉记录分析
到目前为止,我们研究的所有内容都集中在处理单个 JSON 记录上。然而,在现实世界中,您可能需要处理许多 JSON 文档,并且需要跨记录进行聚合。
您可以考虑使用以下任何一种方法来支持您的分析:
- MongoDB 存储数据,然后在其上运行聚合
- 扁*化数据,用熊猫做运营
- 展*数据并导出到 Excel 以供进一步操作
- 选择您想要的属性,并将它们加载到 SQL DB 中以供进一步分析
结论
这就是了。在 5 分钟内,您就知道了开始使用 JSON 所需的一切。我鼓励你花些时间去熟悉它。它应用广泛,当然是你简历上的必备!
参考资料:
{
"References" : [
{
"¹" : "[https://en.wikipedia.org/wiki/JSON#History](https://en.wikipedia.org/wiki/JSON#History)"
},
{
"²" : "guru99.com/json-vs-xml-difference.html"
},
{
"³" : "[https://pypi.org/project/json-flatten/#description](https://pypi.org/project/json-flatten/#description)"
},
]
}
处理熊猫中缺失的值
关于 Pandas 中缺失值以及如何使用内置方法处理它们的教程

真实世界的数据很少是干净和同质的。特别是,许多有趣的数据集将会丢失一些值。
在本文中,我们将讨论缺失值在 Pandas 中是如何表示的,如何处理其他字符表示,以及 Pandas 处理缺失值的内置方法。
概述
- 熊猫身上缺失的价值观
- 处理其他字符表示
- 处理缺失值
熊猫身上缺失的价值观
用于指示缺失值存在的方案通常围绕两种策略之一[1]:
- 全局指示缺失值的掩码。
- 指示缺失条目的标记值。
在屏蔽方法中,它可能是相同大小的布尔数组表示,或者使用一个位来表示丢失条目的本地状态。
在标记值方法中,标签值用于指示缺失值,如NaN(非数字)、null或作为编程语言一部分的特殊值。
这些方法没有一个是没有权衡的[1]。屏蔽方法需要分配额外的布尔数组,这增加了存储和计算的开销。一个标记值缩小了可以表示的有效值范围,可能需要 CPU 和 GPU 运算中的额外逻辑。
Pandas 使用 sentinels 来处理缺失值,更具体地说,Pandas 使用两个已经存在的 Python 空值:
- Python
None对象。 - 特殊浮点
NaN值,
Python 无对象
熊猫使用的第一个 sentinel 值是None,这是一个 Python ‘object’数据,最常用于 Python 代码中的缺失数据。因为是 Python 对象,None不能在任何任意的 NumPy/Pandas 数组中使用,只能在数据类型为‘object’的数组中使用。
import numpy as np
import pandas as pdarr1 = np.array([1, **None**, 2, 3])
arr1
会输出
array([1, None, 2, 3], **dtype=object**)
在数组中使用 Python 对象也意味着,如果执行类似于sum()或min()的聚合,通常会出现错误。
Python 浮点 NaN 值
熊猫使用的第二个 sentinel 值是NaN,是的首字母缩写,而不是数字,一个特殊的浮点值使用标准的 IEEE 浮点表示法。
arr2 = np.array([1, **np.nan**, 3, 4])
arr2.dtype
会输出
dtype('**float64**')
请记住,NaN是一个浮点值;整数、字符串或其他类型没有等价的值。你应该知道,不管运算如何,用NaN运算的结果将是另一个NaN。例如
arr2.sum()
arr2.min()
arr2.max()
将全部输出NaN。NumPy 确实提供了一些特殊的聚合来忽略缺失的数据,比如nansum()、nanmin()和nanmax()。
熊猫中的 None 和 NaN
Pandas 可以几乎互换地处理None和NaN,在适当的时候在它们之间转换:
pd.Series([1, **np.nan**, 2, **None**])0 1.0
1 NaN
2 2.0
3 NaN
dtype: **float64**
对于没有可用 sentinel 值的类型,当NaN值出现时,Pandas 会自动进行类型转换。
比如我们用dtype=int打造一个熊猫系列。
x = pd.Series(range(2), **dtype=int**)
x0 0
1 1
dtype: **int64**
如果我们将一个整数数组中的值设置为np.nan,它将自动被转换为浮点类型以适应NaN:
**x[0] = None**
x0 NaN
1 1.0
dtype: **float64**
处理其他字符表示
并不是所有丢失的值都干净利落地出现np.nan。如果我们知道哪种字符用作数据集中的缺失值,我们可以在使用na_values参数创建数据帧时处理它们:
df = pd.read_csv("source.csv", **na_values = ['?', '&']**)
当数据帧已经创建时,我们可以使用 pandas replace()函数来处理这些值:
df_clean = df.replace(**{ "?": np.nan, "&": np.nan }**)
处理缺失值
在我们深入细节之前,让我们创建一个带有一些缺失值的数据帧:
import numpy as np
import pandas as pddf = pd.DataFrame(**{'A': [20, 15, 20],
'B': [np.nan, 50, 4],
'C': [30, 5, 30,],
'D': [15, 2, np.nan],
'E': [8, 5, 10],
'F': [45, 7, np.nan],
'G': [35, 10, 35]},**
index = ['Row 1', 'Row 2', 'Row 3'])
df

检测缺失值
Pandas 有两个有用的方法来检测缺失值:isnull()和notnull()。任何一个都将返回数据的布尔掩码。例如:
df.isnull()返回一个相同大小的布尔数据帧,指示值是否丢失

df.isnull()的输出
df.notnull()返回一个大小相同的布尔数据帧,与isnull()正好相反

df.notnull()的输出
当数据集很大时,您可以计算缺失值的数量。
df.isnull().sum()返回每列缺失值的个数(熊猫系列)
df.isnull()**.sum()**A 0
B 1
C 0
D 1
E 0
F 1
G 0
dtype: int64
df.isnull().sum().sum()返回缺失值的总数
df.isnull()**.sum().sum()**3
丢弃丢失的数据
除了之前使用的屏蔽之外,还有一个方便的方法dropna()来删除丢失的值【2】。该方法定义为:
dropna(axis=0, how=’any’, thresh=None, subset=None, inplace=False)
axis:行用0,列用1how:‘any’用于删除存在 NaN 值的行或列。‘all’如果所有值都是 NaN,则删除列的行。thresh:要求多个非 NaN 值subset:类似数组的值。要考虑的沿其他轴的标签,例如,如果您要删除行,这些将是要包括的列的列表。inplace:如果为真,就地操作,不返回。
概括地说,这是我们正在使用的数据框架df

如果存在任何 NaN 值,则删除行:
df.dropna(**axis = 0**)

df.dropna 的输出(轴= 0)
如果存在任何 NaN 值,则删除列:
df.dropna(**axis = 1**)

df.dropna(axis = 1)的输出
如果非 NaN 的数量小于 6,则删除该行。
df.dropna(**axis = 0**, **thresh = 6**)

df.dropna(axis = 0, thresh = 6)的输出
替换丢失的值
数据是宝贵的资产,所以我们不应该轻易放弃它。此外,机器学习模型几乎总是倾向于在更多数据的情况下表现更好。因此,根据具体情况,我们可能更喜欢替换丢失的值,而不是丢弃。
有一个方便的方法fillna()来替换丢失的值【3】。该方法定义为:
fillna(value=None, method=None, axis=None, inplace=False)
value:用于替换 NaN 的值method:用于替换 NaN 的方法。method='ffill'※正向替换。method='bfill'进行反向替换。axis:行用0,列用1。inplace:如果为真,就地运算,不返回。
概括一下,这是我们正在使用的数据图表df

用标量替换所有 NaN 值
df.fillna(**value=10**)

df.fillna(**value=10**)的输出
To 用前一行中的值替换 NaN 值。
df.fillna(**axis=0**, **method='ffill'**)

df.fillna(**axis=0**, **method=’ffill’**)的输出
用前一列中的值替换 NaN 值。
df.fillna(**axis=1**, **method='ffill'**)

df.fillna(**axis=1**, **method=’ffill’**)的输出
同样,您也可以用下一行或下一列中的值替换 NaN 值。
# Replace with the values in the next row
df.fillna(**axis=0**, **method='bfill'**)# Replace with the values in the next column
df.fillna(**axis=1**, **method='bfill'**)
另一种常见的替换是用*均值替换 NaN 值。例如,用*均值替换 B 列中的 NaN 值。
df[**'B'**].fillna(value=**df['B'].mean()**, inplace=True)

df[**‘B’**].fillna(value=**df[‘B’].mean()**, inplace=True)的输出
好了
仅此而已。感谢阅读。
参考资料:
- [1] Python 数据科学手册
- 熊猫官方文件——dropna
- [3] 熊猫官方文档——菲尔娜
得益于 SQL 和 ERD,可以处理多个表
带 PostgreSQL 的 SQL 教程
ERD 和 SQL 的一些基础知识

简介
对于我的教程的第二课,我生成了一个包含五个表的数据库,您可以从我的 GitHub 中名为 SQL tutorial 的文件夹下载。
我们将对一家名为 Space Odyssey 的虚构公司进行研究,正如我在上一课中承诺的那样,我将向您介绍 ERD 的概念(即实体关系图)和一些关于 SQL 的基础知识。
我将使用 PostgreSQL,这是我在教程的第一部分中介绍的 RDMS。
所以,我们开始吧!
第一部分:什么是实体关系图?
实体关系图是一个流程图,可以了解存储在数据库中的实体之间的关系。这些实体具有强调特定礼仪的属性。
下面,您可以观察我为本课创建的表之间的关系。正如您所看到的,每个表都有一个主键和一个外键。这两把钥匙可以把一张桌子和另一张桌子连接起来。

作者图片
由于主键和外键,了解表之间的连接对于从数据库中合并、操作和提取信息以及更好地了解我们的工作环境是必不可少的。
在这节课中,我们将对这些表格进行一些组合,以便更好地理解在线公司太空漫游。
这家在线公司向 100 个国家销售电影,作为数据分析师,我们必须协调一个由 50 名卖家组成的团队,他们管理着 501 名客户的邮件列表。
《太空漫游》储存了 1000 部电影,在 2020 年 3 月至 2020 年 6 月期间卖出了 360 部。
第二部分:连接表以获得洞察力
现在,假设市场部想要一个列表,表示每个地区有哪些电影。我可以简单地使用一个 Join 来连接一个表和另一个表。所以:
project_movies=# SELECT a.country, m.movie_title
project_movies-# FROM area as a
project_movies-# JOIN seller as s
project_movies-# ON a.ID = s.area_id
project_movies-# JOIN movies as m
project_movies-# ON s.area_id = m.id_seller
project_movies-# ORDER BY a.country;
出局:

作者图片
正如你在上面观察到的每个国家,我们都有可用的电影。为此,我必须考虑主键和外键的概念。
现在,总经理需要一份清单,列出我们工作期间每个销售人员获得的欧元金额。在这种情况下,我们可以通过。
project_movies=# SELECT s.first_name, s.last_name, SUM(o.amt_euro) project_movies=# AS total
project_movies-# FROM seller AS s
project_movies-# JOIN movies AS m
project_movies-# ON s.id = m.id_seller
project_movies-# JOIN orders AS o
project_movies-# ON m.id = o.id_movie
project_movies-# GROUP BY s.first_name, s.last_name
project_movies-# ORDER BY total DESC;
出局:

作者图片
我们有一个卖家列表,从获利多的卖家到获利少的卖家排序。
第三部分:用子查询查找信息
假设我们想知道电影的不同价格,这些电影的标题以 P 或 M 或 K 开头,同时我们需要看到这些电影的卖家。回答这个问题的一种方法是使用子查询,如下所示。
project_movies=# SELECT s.first_name, s.last_name, m.movie_title, project_movies=# o.amt_euro
project_movies-# FROM seller AS s
project_movies-# JOIN (
project_movies(# SELECT id, movie_title, id_seller FROM movies
project_movies(# WHERE movie_title LIKE ‘P%’ or movie_title LIKE ‘M%’ or movie_title LIKE ‘K%’) AS m
project_movies-# ON s.id = m.id_seller
project_movies-# JOIN orders AS o
project_movies-# ON m.id = o.id_movie;
出局:

作者图片
因此,我们得到了一个表,其中包含了销售以 P、M 或 k 开头的电影的每个卖家的姓名。此外,我们还知道这些电影的价格。
结论
在本教程的第二部分中,您已经看到了一个简单的数据库是如何工作的,以及如何从中提取信息。无论如何,我建议你通过下载我的 GitHub 中的表格自己练习,并尝试找到其他信息。
我认为数据分析是对你自己的一个挑战,你可以看到你的数据能给你带来多少深度,你能看透你的数据多远。
在本教程的下一部分也是最后一部分,我们将看到 SQL 中使用的其他有用技术。敬请关注。
有点好奇:你可能已经猜到了,这个虚构的公司的名字叫做太空漫游来自斯坦利·库布里克的著名电影《2001:太空漫游》。
也可以在我的电报群 数据科学新手群联系。
使用 Neo4j
使用电子商务数据的 RFM 分析

图片来自 Pixabay 的 Gerd Altmann
关系数据库是管理数据的逻辑方法,但另一方面,图形数据库等替代方法在许多情况下可能更有用。众所周知,各行各业的大公司,如易贝、Airbnb、思科和许多其他公司都使用图形数据库[1]。此时,Neo4j 显示自己是一个用于管理数据的图形数据库*台。
在本文中,我将尝试解释如何使用 Neo4j 从电子商务数据中创建一个示例图,同时也涉及 RFM 分析。
数据
首先,你可以在这里找到我之前提到的电商数据。乍一看,很明显数据是由事务组成的。因此,数据包括一系列列,如客户、购买的产品、数量和交易日期。
在向 Neo4j 插入数据之前规划模式将是朝着正确方向迈出的一步。本研究要建立的模式如下:

作者图片
我们现在可以从顾客开始了。在创建节点之前创建一个约束既可以防止重复,又可以提高性能,因为它使用了MERGE锁[2]。您可以按如下方式创建约束,
CREATE CONSTRAINT ON (customer:Customer) ASSERT customer.customerID IS UNIQUE
请注意,只有当属性存在时,属性值的唯一性在图中才有用。然后,您可以按如下方式创建客户节点,
:auto
USING PERIODIC COMMIT
LOAD CSV WITH HEADERS
FROM '[https://git.io/JkIjD](https://git.io/JkIjD)'
AS line
WITH
toInteger(line.CustomerID) AS CustomerID,
line WHERE NOT line.CustomerID IS null
MERGE(customer:Customer {customerID: CustomerID})
ON CREATE SET customer.country = line.Country
创建客户节点后,创建产品和交易节点将变得更加容易。同样,首先为产品节点创建约束是正确。
CREATE CONSTRAINT ON (product:Product) ASSERT product.stockCode IS UNIQUE
这里有很重要的一点,当你创建一个约束时,Neo4j 会创建一个索引。Cypher 将使用该索引进行查找,就像其他索引一样。因此,没有必要创建单独的索引。事实上,如果您试图在已经有一个索引的情况下创建一个约束,您将会得到一个错误。
在考虑了所有这些因素之后,您可以如下创建产品节点,
:auto
USING PERIODIC COMMIT
LOAD CSV WITH HEADERS
FROM '[https://git.io/JkIjD](https://git.io/JkIjD)'
AS line
MERGE(product:Product {stockCode: line.StockCode})
ON CREATE SET product.description = line.Description
如上所述,创建节点时使用了ON CREATE语句。如果需要创建节点,请合并节点并设置属性。类似地,如果节点已经存在,也可以使用语句ON MATCH[3]。
在开始处理如下关系之前创建事务节点是很好的,
CREATE CONSTRAINT ON (transaction:Transaction) ASSERT transaction.transactionID IS UNIQUE;:auto
USING PERIODIC COMMIT
LOAD CSV WITH HEADERS
FROM '[https://git.io/JkIjD](https://git.io/JkIjD)'
AS line
MERGE(transaction:Transaction {transactionID: line.InvoiceNo})
ON CREATE SET transaction.transactionDate = line.InvoiceDate
查看上面的 Cypher 语句,可以看到分号用于分隔 Cypher 语句。一般情况下,不需要用分号结束一个 Cypher 语句,但是如果要执行多个 Cypher 语句,就必须将它们分开[2]。
图中的节点都准备好了,但是这些节点彼此没有连接。连接捕获了图中节点的语义关系和上下文[2]。众所周知,图中有三种类型的节点:客户、交易和产品。正如我在本节开始时提到的,在客户-交易和交易-产品之间建立关系将使这个图更加符合逻辑。客户与达成交易,交易包含产品。这是 Cypher 关于建立人造关系的声明,
:auto
USING PERIODIC COMMIT
LOAD CSV WITH HEADERS
FROM '[https://git.io/JkIjD](https://git.io/JkIjD)'
AS line
WITH toInteger(line.CustomerID) AS CustomerID,
line.InvoiceNo AS InvoiceNo
MATCH (customer:Customer {customerID: CustomerID})
MATCH (transaction:Transaction {transactionID: InvoiceNo})
MERGE (customer)-[:MADE]->(transaction)
让我们通过创建包含关系来完成图表,
:auto USING PERIODIC COMMIT
LOAD CSV WITH HEADERS
FROM 'https://git.io/JkIjD'
AS line
WITH toInteger(line.Quantity) AS Quantity,
toFloat(line.UnitPrice) AS UnitPrice,
line.InvoiceNo AS InvoiceNo,
line.StockCode AS StockCode
MATCH (transaction:Transaction {transactionID: InvoiceNo})
MATCH (product:Product {stockCode: StockCode})
MERGE (transaction)-[r:CONTAINS]->(product)
ON CREATE SET r.quantity = Quantity,
r.price = UnitPrice * Quantity
ON MATCH SET r.quantity = r.quantity + Quantity,
r.price = r.price + UnitPrice * Quantity
现在,您可以使用语句CALL db.schema.visualization()来检查图表的模式。结果是这样的,

作者图片
请记住,您可以用不同的方式创建图表。例如,事务可能是一个关系,而不是一个节点,我们可以称之为 buy。可以想象,选择哪一个取决于你的业务问题。此时,您应该设置规则并相应地构建结构。
RFM 分析
RFM 分析是一种基于行为的方法,将客户分为不同的细分市场。它根据客户以前的购买交易对客户进行分组。[4].这是 RFM 的三个维度,
- R 紧急程度:客户最*购买了什么?
- 频率:他们多久购买一次?
- 一元值:他们花了多少钱?
对于在许多行业销售的公司来说,使用 RFM 分析对客户进行细分是一个重要的点。因为公司希望了解对他们有价值的客户,并确保所有客户的忠诚度。
在提到 RFM 的维度和客户细分的重要性之后,我们可以使用下面的 python 代码获得最*度、频率和货币价值。
然后,通过为维度创建百分位数来定义细分将是一个正确的步骤。请记住,这里的细分可以达到一个更高的水*,而且通常没有那么简单。现实世界的问题可能更复杂。
当您运行上面的 python 代码时,您将看到如下结果,

作者图片
正如您在输出中所看到的,有对段的描述性统计。例如,查看最佳客户的统计数据,可以看到他们最*购买、频繁购买,并且货币价值相当高。因此,公司留住这个细分市场的客户非常重要。
对其他部分将有不同的方法。考虑到分段的目的,这是很自然的。为细分市场开发不同的方法和提高客户忠诚度有很大的影响。例如,您可以看到对以下四个部分的描述和所需的操作,

作者图片
结论
正如我在文章开头提到的,可能需要不同的方法来解决商业生活中的问题。此时,有必要很好地识别问题,并逐步构建解决方案。虽然这不是解决所有问题的可行方案,但尝试不同的方法来解决问题对你的公司和职业发展都有好处。
我希望这篇文章对您了解 Neo4j 和客户细分有所帮助。
感谢您的阅读!如果你想了解更多,请访问我的知识库!
在本研究中,使用 Neo4j 从电子商务数据创建示例图,然后进行客户细分
github.com](https://github.com/egemenzeytinci/neo4j-example)
参考
[1] Helen Vdovychenko, Neo4j 数据库:您的项目的用例与优势 (2020)
[2] 充分利用 Neo4j
[3] 在创建和匹配时使用
[4] Avinash Navlani,Python 中的客户细分介绍 (2018)
在 Python 中使用 NLP 数据集
教程:将新的 HuggingFace 数据集库与 TensorFlow 数据集库和其他选项进行比较
在深度学习领域,数据集是每个项目必不可少的一部分。为了训练一个可以处理新情况的神经网络,人们必须使用一个代表世界即将到来的场景的数据集。在动物图像上训练的图像分类模型在汽车分类任务上表现不好。
除了训练最佳模型,研究人员还使用公共数据集作为模型性能的基准。我个人认为易于使用的公共基准是帮助促进研究过程的最有用的工具之一。这方面的一个很好的例子是代码为的论文和最先进的图表。
另一个很棒的工具是现成的数据集库。在这篇文章中,我将以 IMBD 情感分析数据集为例,回顾新的 HuggingFace 数据集库,并使用 Keras biLSTM 网络将其与 TensorFlow 数据集库进行比较。这个故事也可以作为使用这些库的教程。
所有代码都可以在 Google Colab 上找到。

原始数据集出版物
当有人发布一个新的数据集库时,最直接的做法就是在研究团队的网页中进行分享。例如,IMDB 情感分析数据集由斯坦福大学的研究人员团队发布,并可在他们自己的网页上获得:大型电影评论数据集。在科学出版物的情况下,它通常带有一篇发表的文章:例如,见 Maas 等人[1]。

我认为,这有两个主要问题:1)很难找到,尤其是如果你是一个早期携带者科学家;2)没有存储数据的标准化格式,使用新的数据集必须有特定的预处理步骤。
在线数据集集合
要使数据集可访问,不仅要使它可用,还要确保用户能找到它。谷歌意识到了它的重要性,他们在 datasetsearch.research.google.com 为数据集开发了一个搜索*台。然而,搜索 IMDB 大型电影评论情感数据集,结果不包括该研究的原始网页。浏览谷歌数据集搜索结果,你会发现 Kaggle 是最大的在线公共数据集集合之一。
卡格尔
Kaggle 是世界上最大的在线机器学习社区,拥有各种竞赛任务、数据集集合和讨论主题。如果你从未听说过 Kaggle,但对深度学习感兴趣,我强烈建议你去看一看。在 Kaggle 中,任何人都可以上传新的数据集(限制为 10GB ),社区可以根据数据集的文档、机器可读性和代码示例的存在对数据集进行评级。
Kaggle 上的 IMDB 情感数据集有 8.2 分和 164 个公共笔记本示例来开始使用它。用户可以阅读数据集的文档,并在下载之前进行预览。
值得注意的是,该数据集不包括数据的原始分割。这无助于模型的再现性,除非构建者描述它们的分裂函数。

使用 Kaggle 数据集时,最重要的预防措施是 1)确保您使用准确的数据集,因为许多用户共享数据集的更改/改进版本,2)确保您拥有使用它的许可证,并且正确的人对此负责。Kaggle 上的许多数据集不是由原始创建者共享的。
数据集库
虽然数据集集合的主要原因是将所有数据集存储在一个地方,但数据集库侧重于随时可用的可访问性和性能。
机器学习库通常带有一些数据集示例。这里的是 Scikit-learn 数据集的列表。我选择 IMDB 数据集,因为这是 Keras 中包含的唯一文本数据集。
TensorFlow 团队专门为数据集开发了一个包。它包括几个数据集,并与 TensorFlow 和 Keras 神经网络兼容。
在下文中,我将比较 TensorFlow 数据集库和新的 HuggingFace 数据集库,重点关注 NLP 问题。
公共数据集
目前,TensorFlow 数据集列出了来自机器学习各个领域的 155 个条目,而 HuggingFace 数据集包含 165 个专注于自然语言处理的条目。以下是共享相同名称的数据集列表(39):
tfds_list = tfds.list_builders(); hfds_list = datasets.list_datasets(); list(set(tfds_list).intersection(set(hfds_list)))
[‘xnli’, ‘multi_news’, ‘multi_nli_mismatch’, ‘wikihow’, ‘squad’, ‘xsum’, ‘super_glue’, ‘cos_e’, ‘newsroom’, ‘lm1b’, ‘eraser_multi_rc’, ‘aeslc’, ‘civil_comments’, ‘gap’, ‘cfq’, ‘gigaword’, ‘esnli’, ‘multi_nli’, ‘scan’, ‘librispeech_lm’, ‘opinosis’, ‘snli’, ‘reddit_tifu’, ‘wikipedia’, ‘scicite’, ‘tiny_shakespeare’, ‘scientific_papers’, ‘qa4mre’, ‘c4’, ‘definite_pronoun_resolution’, ‘flores’, ‘math_dataset’, ‘trivia_qa’, ‘para_crawl’, ‘movie_rationales’, ‘natural_questions’, ‘billsum’, ‘cnn_dailymail’, ‘glue’]
请注意,IMDB 数据集不在列表中!在 TensorFlow 数据集中,它被命名为imdb_reviews,而 HuggingFace 数据集将其称为imdb数据集。我认为这是非常不幸的,图书馆的建设者应该努力保持相同的名字。
数据集描述
HuggingFace 数据集有一个数据集查看器站点,在此呈现数据集的样本。该网站显示了数据的分割,原始网站的链接,引用和例子。除此之外,他们还有另一个数据集描述站点,其中显示了导入使用和相关模型。
TensorFlow 数据集有一个单独的数据集描述站点,在这里可以获得前面提到的元数据信息。与 HugginFace 网站相比,TensorFlow 提供了多种下载选项:纯文本和编码数字单词令牌。

HuggingFace(上图)和 TensorFlow(下图)IMDb 数据集描述页面
情感分析的 Keras 示例
在 IMDB 数据集的 Keras 版本中,纯文本已经过预处理。我使用相同的处理步骤来说明其他库的用例。步骤如下:
- 加载数据集
- 将纯文本标记化并编码
- 截断长示例
- Pad 简短示例
- 洗牌,批量处理数据
- 为每个数据集拟合相同的模型
最初由 fchollet 构建 Keras 模型
Keras 网络将期望 200 个令牌长整数向量,词汇表为[0,20000]。词汇表中的单词基于数据集的词频。该网络由用于输入读取的Input层、用于将单词表示从整数投影到 128 维向量空间的Embedding层、两个双向LSTM层和用于匹配输出维度的Dense层组成。
由于数据已经过预处理,最初的步骤只有几行:
最初由 fchollet 预处理 Keras 数据
和训练线:
使用最初由 fchollet 提供的 Keras 数据训练模型
在 Colab 计算机上训练的历元在 CPU 上是 450 秒长,在 GPU 上是 55 秒长,在测试数据上的最终精度是 0.8447。
用于情感分析的 TensorFlow 数据集
TensorFlow 提供了非常好的教程,它们很详细,但阅读起来篇幅相对较短。如果你想了解更多,从这里开始。
从 tensorflow_datasets 加载 IMDB 数据集
第一个参数按名称指定数据集。接下来,split参数告诉库应该包含哪些数据分割。也可以是分成的百分比:train[:10%]。as_supervised参数指定了格式,它允许 Keras 模型从 TensorFlow 数据集进行训练。with_info向列表中添加一个新的返回值,它包含来自数据的各种信息。我最喜欢的是显示原始数据构建团队的信用的引文。
我认为 TensorFlow 数据集库最强大的工具是,您不必一次加载全部数据,而只需分批加载。不幸的是,为了建立基于词频的词汇表,我们必须在训练前加载数据。
张量流数据集的符号化和截断
记号赋予器的构建基于本教程。但是,我添加了一个Counter来统计这些词在训练数据集中的出现频率。代码首先用空格分隔句子。然后,Counter统计词频。为了匹配 Keras 模型的词汇大小,计数器只保留顶部的max_features-2。额外的两个标记是填充标记(0)和不在词汇表中的标记(OOV),用于不包括在最常用列表中的单词(max_features-1)。接下来的几行代码构建了一个编码器,以确保词汇表中的每个单词都有唯一的整数值。encode_map_fn函数将编码器包装在 TensorFlow 函数中,以便数据集对象可以使用它。这段代码还包含了truncate 函数:它通过截掉句子的结尾来确保句子的长度不会太长。
TensorFlow 数据集管道
代码的下一部分为处理步骤构建管道。首先,它读取(sentence,label)对,然后将其编码为(integer_list,label)对,并截断长句(列表)。如果数据可以存储在内存中,那么cache就可以加快工作速度。shuffle部分只对训练数据是必要的:buffer_size=1024参数指定程序在数据的较小窗口上随机化,而不是一次全部随机化。如果数据太大而无法放入存储器,这是有用的,但是,只有当此buffer_size大于数据样本数时,才能实现真正的随机。流水线的padded_batch步骤将数据分成 32 个一组,并将较短的句子填充到 200 个标记中。在这个步骤之后,输入形状是(32,200),输出是(32,1)。最后,prefetch步骤与多处理一起工作:当模型在一个批次上训练时,算法在下一个批次中加载,因此当模型完成前一个批次时,它们将准备好。
最后,数据已准备好训练模型:
第一个历元在 GPU 上较慢(82s),但在将处理后的数据加载到缓存后,历元持续时间与 Keras(55s)相似。测试数据的最终精度为 0.8014。如果您运行代码,您可以看到 Keras fit verbose 无法猜测第一个 epoch 的持续时间:管道在不知道其最终长度的情况下读取数据!

一次读取一批数据(在 CPU 上),而不知道数据的结尾
基于拥抱人脸数据集的情感分析
首先,我想谈谈 HugginFace 软件包的名称:该公司有 3 个 pip 软件包,transformers、tokenizers 和datasets。虽然我理解保护这些短名称的 PR 值,并且可能 transformers 和 tokenizers 是它们中的第一个,但我不喜欢它们的名称,因为它可能会令人困惑。例如,如果我使用 TensorFlow 数据集库和 HuggingFace 数据集库,如果一个人的名字是 Datasets,我无法确定哪个是它。我想如果 Tensorflow 用名字命名他们的库(像tensorflow_datasets),如果 HuggingFace 这样做就好了。
其次,与tokenizers 和datasets一起工作,我不得不注意到,虽然transformers 和datasets 有很好的文档,但tokenizers 图书馆缺乏。此外,在按照文档构建这个示例的过程中,我遇到了一个问题——这个问题在 6 月份被报告给了他们。
HuggingFace 数据集可以构建一个类似于 TensorFlow 的管道来处理大数据。在这个实验中,我没有使用它,如果你需要,请阅读教程!
HuggingFace 数据集读取 IMDB 数据集
数据加载的工作方式与前一个类似。HuggingFace 库可以处理百分比和张量流。Dataset 对象将数据信息作为引用信息等属性。
标记化者
HuggingFace 库的一个强大功能来了:由于该公司专注于自然语言处理,它比 TensorFlow 有更多更适合该领域的功能。如果我们想要使用一个特定的 transformer 模型,我们可以从相应的包中导入它的 tokenizer。或者我们可以从头开始训练一个新的。我在以前的一篇文章中谈到了记号赋予者之间的区别,如果你感兴趣,可以在这里阅读。
在这个实验中,我基于训练数据构建了一个单词块[2]标记器。这就是著名的 BERT 模型[3]使用的记号化器。此外,我还展示了如何使用前一部分的词汇表作为标记器的数据来实现相同的功能。
使用训练数据构建单词块[2]—基于此通过 HuggingFace
这个代码示例展示了如何基于标记器实现构建一个单词块。不幸的是,训练器只处理文件,因此我不得不临时保存 IMDB 数据集的纯文本。词汇表的大小可通过train功能定制。
构建频率列表标记器
为了用最常用的单词构建标记器,应该更新词汇表。唯一的诀窍是跟踪特殊的令牌。要查看 tokenizer 内部的数据,一种可能的方法是将其保存到一个 JSON 文件中:它是可读的,并且包含所有需要的信息。
如果使用句子“这是一个藏身之处”,区别就很明显了。第一个版本分割藏身处并识别“.”字符,但第二种方法将整个单词作为标记,但不包括标点字符。缺省情况下,记号赋予器使这个数据小写,我在以前的版本中没有使用这个步骤。
WordPiece: [‘this’, ‘is’, ‘a’, ‘hide’, ‘##out’, ‘.’]
From Vocab: [‘this’, ‘is’, ‘a’, ‘hideout’, ‘[UNK]’]
为了确保我没有使用相同的令牌,我在两次运行之间调用了[datasets.Dataset.cleanup_cache_files()](https://huggingface.co/docs/datasets/package_reference/main_classes.html#datasets.Dataset.cleanup_cache_files) 。
为 Keras 训练格式化为张量流数据集
实现数据集教程中的示例,我们可以将数据加载到 TensorFlow 数据集格式,并用它训练 Keras 模型。
将人脸数据集绑定到张量流数据集— 基于本教程
这段代码类似于 HuggingFace 教程中的代码。唯一的区别在于使用了不同的记号赋予器。本教程使用来自transformers库的 BERT 模型的标记器,而我使用来自tokenizers库的BertWordPieceTokenizer。不幸的是,来自同一家公司不同库中的这两个逻辑上相似的类并不完全兼容。
HuggingFace 数据的管道和培训
最后一步几乎与使用张量流数据的步骤相同。唯一不同的是shuffle缓冲器。HuggingFace 数据集的 IMDB 数据库中的样本按标签排序。在这种情况下,这不是一个问题,但它禁用了 TensorFlow 的功能,这些功能允许一次只加载部分数据。如果我们只用一个小窗口混洗这些数据,在几乎所有的情况下这个窗口只包含一个标签值。希望其他数据集不会出现这种情况。
两个模型的最终精度相似:0.8241 和 0.8224。

托比亚斯·菲舍尔在 Unsplash 上拍摄的照片
摘要
在这个故事中,我展示了 TensorFlow 和 HuggingFace 的数据集库的使用。我谈到了为什么我认为建立数据集集合对研究领域很重要。总的来说,我认为专注于 NLP 问题的 HuggingFace 将是该领域的一个伟大推动者。该库已经拥有比 TensorFlow 更多的 NLP 数据集。我认为他们与 TensorFlow(以及 PyTorch)紧密合作以确保两个库的每个特性都能被恰当地利用是很重要的。
参考
[1]安德鲁·马斯、雷蒙德·戴利、彼得·范、黄丹、安德鲁·吴和克里斯托弗·波茨。(2011).学习用于情感分析的词向量。 计算语言学协会第 49 届年会(ACL 2011)。
[2]吴,m .舒斯特,陈,z .乐,Q. V .,m .马切里,w .,… &克林纳,J. (2016)。谷歌的神经机器翻译系统:弥合人类和机器翻译之间的鸿沟。arXiv 预印本 arXiv:1609.08144 。
[3] Devlin,j .,Chang,M. W .,Lee,k .,& Toutanova,K. (2018 年)。 Bert:用于语言理解的深度双向转换器的预训练。 arXiv 预印本 arXiv:1810.04805 。
使用 Numpy 数组:索引
PyTrix 系列
PyTrix#2:访问 Numpy 数组中的元素

PyTrix 现在将成为一个每周一期的系列,在这里我将展示可以用 Python 完成的对数据科学家有用的很酷的东西。上一个 PyTrix 教程可以在下面的链接中找到…
PyTrix#1:加速我们的 Python 代码
towardsdatascience.com](/vectorization-in-python-46486819d3a)
下面是 Github 知识库的链接:
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/kurtispykes/demo/tree/master/pytrix)
什么是 Numpy 数组?
首先,我们从解释 Numpy 开始。Numpy 是用于科学编程的事实上的库,在从业者中使用如此普遍,以至于当我们导入它时,它有自己的标准。
Numpy 框架为我们提供了一个高性能的多维数组对象,以及操作数组的有用工具。关于这一点,我们可以将 numpy 数组描述为一个相同类型值的网格,它通过一个非负整数元组进行索引。
我们通过数组的秩来区分维数。此外,数组的形状是一个整数元组,表示数组沿其每个维度的大小。
注:如果你不熟悉排名术语,在下面的链接中,史蒂文·斯坦克有一个精彩的解释。
这个问题有个简短的答案,就从这里开始吧。然后,我们可以看一看应用程序,以获得…
medium.com](https://medium.com/@quantumsteinke/whats-the-difference-between-a-matrix-and-a-tensor-4505fbdc576c)
然而,今天的 PyTrix 系列是关于索引数组的,所以不再多说,让我们开始吧!
索引
从多维数组中提取单个元素、行、列或*面的过程称为索引。
在很多情况下,我们可能希望在数据科学项目中从数组中获得一些值。根据矩阵的维度(或等级),我们必须采用不同的策略来有效地执行这项任务。
对于那些想了解更多关于np.array()类的人,文档可以在这里找到!
一维索引
在第一个例子中,我将初始化一个一维 numpy 数组并打印它:
注意:假设上一节中 numpy 的导入已经完成
numbers= [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
a= np.array(numbers)**print**(a)
>>>> [ 1 2 3 4 5 6 7 8 9 10]
为了访问数组中的元素,我们遵循与普通 python 列表或 tuple 数据类型完全相同的索引约定:
**print**(f"Index 0: {a[0]}\nIndex 5: {a[5]}\nIndex 7: {a[7]}")
*>>>>* Index 0: 1
Index 5: 6
Index 7: 8
二维索引
接下来,我们将从一个列表实例化一个二维 numpy 数组。我们已经有一个初始化为numbers的列表,我们必须再创建一个。
big_numbers= [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
b= np.array([numbers, big_numbers])
**print**(b)
>>>> [[ 1 2 3 4 5 6 7 8 9 10]
[ 10 20 30 40 50 60 70 80 90 100]]
如果我们想从二维数组中提取一个元素,我们必须首先选择索引, i,,它是指向矩阵行的指针,然后选择索引, j ,来选择列。
**print**(b[1, 4])
>>>> 50
这表示我们从数组中选择了第 1 行和第 4 列。
选择行或列向量
我知道我的读者是非常好奇的人。因此,你可能想知道当你把索引传递给我们的列表时会发生什么…
**print**(b[0])
>>>> [ 1 2 3 4 5 6 7 8 9 10]
这将返回索引为 0 的行向量。秩为 1 的数组。如果我们想访问一个列向量呢?
**print**(b[:, 8])
>>>> [ 9 90]
:告诉 python 我们应该获取每一行,我们用“,”和 8 将它分开,告诉 python 只从第 8 列获取行。然而,我们有点超前了,因为这关系到切片,我们将在下周讨论 PyTrix:)。
注意:当我们对 numpy 数组进行索引或切片时,相同的数据作为原始数组的视图返回,但是按照我们从索引或切片中声明的顺序访问。
三维索引
如果一个二维数组可以用一个列表实例化,那么…你猜对了。一个三维数组是用一个列表列表实例化的——花一点时间来理解它。
c= np.array([[[1, 2, 3], [4, 5, 6], [7, 8, 9]],
[[10, 11, 12], [13, 14, 15], [16, 17, 18]],
[[19, 20, 21], [22, 23, 24], [25, 26, 27]]])print(c)
>>>> [[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]]
[[10 11 12]
[13 14 15]
[16 17 18]]
[[19 20 21]
[22 23 24]
[25 26 27]]]
我们可以把一个三维数组想象成一堆矩阵,其中第一个索引 i 选择矩阵。第二个索引j选择行,第三个索引 k 选择列。
例如,我们将选择第一个矩阵:
print(c[0])
>>>> [[1 2 3]
[4 5 6]
[7 8 9]]
如果我们想选择数组中的单个元素,可以这样做:
print(c[2, 1, 1])
>>>> 23
为了解释上面的代码,我们打印了 matrix 中索引 2、行索引 1 和列索引 1 处的三维数组。
选择三维数组中的行或列
对于三维数组,我将把对行或列的访问分成三种情况。
场景 1 : 当我们只指定矩阵时, i and row, j ,这将依次从我们选择的矩阵中返回一个特定的行。
**print**(c[0, 0])
>>>> [1 2 3]
这将返回索引为 0 的矩阵和索引为 0 的行。
场景 2 : 当我们想要访问一个特定矩阵的列元素时,我们用:填充第 j 个索引来表示我们想要一个完整的切片(所有的行)。
**print**(c[1, :, 2])
>>>> [12 15 18]
这将返回矩阵 1 中位于列索引 2 中的所有行。
场景 3 :有时,我们可能希望为每个索引访问同一行和同一列中的值。
**print**(c[:, 2, 0])
>>>> [ 7 16 25]
这是每个矩阵的行索引 2 和列索引 0 中的每个元素的列表。
结论
索引有许多选项,学习它们是一组非常有用的技能,可以放在您的数据科学工具包中。关于索引的更多信息,你可以阅读 numpy 文档,它深入研究了索引。
参见下面的链接,获取本文中使用的代码。
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/kurtispykes/demo/blob/master/pytrix/pytrix_working-with-arrays.ipynb)
一锤定音
如果您认为我遗漏了什么,或者您想向我指出什么,或者如果您仍然不确定什么,您的反馈是有价值的。发个回应!
然而,如果你想和我联系,我在 LinkedIn 上是最活跃的,我也很乐意和你联系。
[## 人工智能博主 kurtis Pykes——走向数据科学| LinkedIn
在世界上最大的职业社区 LinkedIn 上查看 Kurtis Pykes 的个人资料。Kurtis 有两个工作列在他们的…
www.linkedin.com](https://www.linkedin.com/in/kurtispykes/)
以下是我最*的一些作品,你可能也会感兴趣:
将最优秀的人与众不同的隐藏技能
towardsdatascience.com](/the-reason-youre-frustrated-when-trying-to-become-a-data-scientist-2d2b8b402811) [## 学习数据科学的 3 个阶段
了解学习的 3 个阶段,以及我们如何将其有效地应用于数据科学学习
towardsdatascience.com](/3-stages-of-learning-data-science-9a04e96ba415) [## 用随机模型预测
使用 ARIMA 模型预测沃尔玛的销售数据
towardsdatascience.com](/forecasting-with-stochastic-models-abf2e85c9679)
使用 Numpy:切片
PyTrix 系列
PyTrix #3:访问 Numpy 数组中的序列

由胡安·曼努埃尔·努涅斯·门德斯在 Unsplash 上拍摄
限幅
当我们想要访问一个序列的一部分,比如一个字符串、元组或列表时,我们可以利用 Python 中的一个称为切片的特性,它使我们能够编写更加干净、简洁和可读的代码。
切片可用于查看、修改或删除可变序列中的项目,例如在列表数据类型中。此外,这个特性还可以很好地利用像 NumPy 和 Pandas 这样的框架。
有关本文中使用的代码,请参见:
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/kurtispykes/demo/blob/master/pytrix/pytrix_slicing.ipynb)
数组与列表
分割一个数组和分割一个列表没有太大的不同。主要的区别是,数组,你可以在多个维度上分割数组,另一个重要的因素我将在后面讲到。
类似于我之前在 PyTrix 系列中提到的关于索引的内容;
访问 Numpy 数组中的元素
towardsdatascience.com](/working-with-numpy-arrays-indexing-e4c08595ed57)
当我们获取数组的一部分时,返回的数组是原始数组的视图——我们最终以不同的顺序访问了相同的数据。然而,当我们分割一个列表时,它将返回一个全新的列表。
切片列表数据类型
为了向您展示 Python 的内置切片特性是如何工作的,我现在将演示它的功能…
a = [1, 2, 3, 4, 5]
接下来,我将演示 python 切片是如何工作的,并截取这个列表的一部分并打印出来…
b = a[1:4]
print(b)
>>>> [2, 3, 4]
当我们使用切片表示法时,我们指定了我们希望从切片中得到的[start:end]索引。这也从开始复制列表,但不包括结束索引。
我们还可以做的一件很酷的事情是省略切片的开头或结尾。例如,如果我们省略开始并提供一个结束索引,Python 将从开始索引开始切片,但不包括结尾提供的索引。或者,如果我们省略结尾并提供一个开始,Python 将从提供的开始索引开始,并切片到列表的结尾。有关示例,请参见下面的代码单元格。
c = a[:3]
d = a[2:]
e = a[:]print(f"List c: {c}\nList d: {d}\nList e: {e}")
>>>> List c: [1, 2, 3]
List d: [3, 4, 5]
List e: [1, 2, 3, 4, 5]
请注意,在列表 e 中,我没有提供开始或结束。这只是整个列表的副本。
最后,为了不使索引变得复杂。让我们看看列表和切片的区别。
f = a[2]
g = a[2:3]print(f"f: {f}\ng: {g}")
>>>> f: 3
g: [3]
索引返回给定索引处的列表元素,而切片返回单个元素的列表。
切片 1D 阵列
如上所述,分割 1D Numpy 数组和 list 几乎是相同的任务,尽管如此,正如您将看到的,它们有一个明显的区别。
import numpy as npnp_a = np.array([1, 2, 3, 4, 5])
np_b = np_a[1:4]print(np_b)
>>>> [2 3 4]
不同于切片时创建新列表的列表,NumPy 切片返回我们存储在np_b中的数据视图。因此,如果我们要改变np_b中的一个元素,那么np_a中的元素也会改变,反之亦然。请看下一个代码单元格中的示例:
print(f"Original Array {np_a}")
np_b[1] = 6
print(f"np_b: {np_b}\nOriginal Array: {np_a}")
>>>> Original Array: [1 2 3 4 5]
np_b: [2 6 4]
Original Array: [1 2 6 4 5]
切片 2D 阵列
2D 阵列可以在两个轴上切片…
a_2d = np.array([[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10],
[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20]])
print(a_2d[1:, 1:3])
>>>> [[7 8]
[12 13]
[17 18]]
为了进行分解,我们从 1:到数组的末尾取了一些行,从 1:3 取了一些列(因此是 1 和 2 列)。
切片 3D 数组
3D 阵列可以在所有 3 个轴上切片…
a_3d = np.array([[[1, 2, 3,], [4, 5, 6], [7, 8, 9]],
[[10, 11, 12], [13, 14, 15], [16, 17, 18]],
[[19, 20, 21], [22, 23, 24], [25, 26, 27]]])print(a_3d[1:, 1:, :])
>>>> [[ [13, 14, 15] [16, 17, 18]]
[ [22, 23, 24] [25, 26, 27] ]]
我们选择了从索引 1 开始的所有*面、从索引 1 开始的所有行和所有列。出于清晰的原因,由于这可能相当棘手,我在下面再次解释了这一点;
- 飞机
1:(最后两架飞机) - 排
2:(最后 2 排) - 列
:(所有列)
老实说,我没有必要在列中包含:。我们可以使用:功能获得*面、行或列的完整切片。然而,当我们有尾随索引时,我们仍然可以通过简单地省略索引来获得完整的切片。
简单地说,这意味着…
# 2D Arrays
print(a_2d[1:3, :] == a_2d[1:3])# 3D Arrays
print(a_3d[1:, :2, :] == a_3d[1:, :2])
print(a_3d[2:, :, :] == a_3d[2:, :])
>>>> [[ True True True True True]
[ True True True True True]]
[[[ True True True]
[ True True True]]
[[ True True True]
[ True True True]]]pr [[[ True True True]
[ True True True]
[ True True True]]]
切片和索引
将此与之前关于索引的 PyTrix 系列相关联:
访问 Numpy 数组中的元素
towardsdatascience.com](/working-with-numpy-arrays-indexing-e4c08595ed57)
我们知道,我们可以使用索引来选择特定的*面、行或列。例如…
print(a_2d[1,2:4])
>>>> [8 9]
另一方面,我们也可以使用切片来做类似的操作…
print(a_2d[1:2,2:4])
*>>>> [[8 9]]*
请注意索引和切片返回的细微差别。索引返回一个 1D 数组,而切片方法返回一个只有一行的 2D 数组。
这个故事中使用的代码可以在…
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/kurtispykes/demo/blob/master/pytrix/pytrix_slicing.ipynb)
一锤定音
如果您认为我遗漏了什么,或者您想向我指出什么,或者如果您仍然不确定什么,您的反馈是有价值的。发个回应!
然而,如果你想和我联系,我在 LinkedIn 上是最活跃的,我也很乐意和你联系。
[## Kurtis Pykes -人工智能博客-走向数据科学| LinkedIn
在世界上最大的职业社区 LinkedIn 上查看 Kurtis Pykes 的个人资料。Kurtis 有两个工作列在他们的…
www.linkedin.com](https://www.linkedin.com/in/kurtispykes/)
如果你喜欢这个 PyTrix 系列,你可以在下面的 PyTrix 系列链接中找到迄今为止的所有文章…
阅读《走向数据科学》中关于 Pytrix 系列的文章。共享概念、想法和代码的媒体出版物。
towardsdatascience.com](https://towardsdatascience.com/tagged/pytrix-series)
你可能会对我最*的帖子感兴趣…
我们可以长期保持动力。
towardsdatascience.com](/staying-motivated-for-your-data-science-career-e845f18421e1)
使用普查 API
收集数据研究公共卫生和治安
我是塔夫茨大学的一名软件开发人员,我的主要项目是一个区域绘图网站Districtr.org,它涵盖了所有形式的学校、城市、县和立法区。我们通过仔细使用人口普查数据来检查各区的人口是否相等(并允许用户评估他们的计划)。
你可能知道现在的人口普查(请填写!)但是,如果你是一名数据科学家或政治活动家,你应该知道在哪里可以为你的当前项目找到以前的人口普查记录。
社区项目受益于人口普查数据
今年 3 月,我的同事开始使用我们的人口普查工作来绘制人口统计数据,特别是冠状病毒风险高的人口统计数据——特别是老年人、集体住房和不讲英语的家庭。了解这些人群的集中程度和医院的容纳能力,可以指导我们的研究,并为决策者提供建议。
研究人员还通过人口普查数据,将其他警务数据集——911 电话、交通堵塞、逮捕、警察暴力、判决——与当地人口进行比较。例如,我们可以查看国家一级的监禁与种族和族裔数据:

来自:https://www . sentencing project . org/publications/infined-women-and-girls/
今年 6 月,约翰·基夫(John Keefe)向多个城市的警区发布了一系列关于从 2010 年人口普查中收集种族和族裔数据的操作指南:
[## 明尼阿波利斯种族和族裔数据,由 Datasette 提供
明尼阿波利斯警方报告了街区的停车和其他事故,所以我决定计算一下…
johnkeefe.net](https://johnkeefe.net/minneapolis-race-and-ethnicity-data-by-neighborhood-served-with-datasette)
这是一个有趣的项目,但我想提出两点:
- 2010 年的数字可能已经过时,尤其是在城市。可能很难确定哪些社区正在中产阶级化,这是警察偏见研究的主要焦点之一(第 1 条、第 2 条)。
- 今年早些时候,当我遇到一个警察偏见项目时,他们没有将自己的记录与人口普查区进行比较(他们的大部分逮捕、交通拦截和雇佣都涉及到他们城市以外的人)。
如果你想成为一名人口普查数据科学家,你应该熟悉十年一次的人口普查和美国社区调查(ACS)。在撰写本文时,我们最好和最新的来源之一是 2014-2018 年 5 年 ACS。
普查地理数据
获得适当的详细程度
为什么我们不在所有事情上使用更新的 ACS?十年一次的人口普查公布了每个“人口普查区块”的人口数量。ACS 有更新更详细的统计数据,但我们必须缩小一个级别到“数据块组”。对于其他数据点(如语言和民族血统),我们必须再次缩小到“地域”级别。
层次结构如下所示:

https://learn . ArcGIS . com/en/related-concepts/United States-census-geography . htm
当你观察城市、农村或沿海地区时,每个“人口普查区块”或地域的大小并不一致。一些街区覆盖了无人居住的公园、河流和机场;有些是城市街区或监狱。
探索纽约市popfactfinder.planning.nyc.gov的街区和道路地图

比较人口普查区块大(在中央公园)和小(上东区,岛屿)
下载人口普查形状文件
您可以在 GIS 软件中查看地图数据。领先的免费/开源应用叫做 QGIS:qgis.org
你可以从
census.gov/cgi-bin/geo/shapefiles/index.php下载 2010 块或 2018 块组作为 shapefiles
人口普查数据
现在您已经有了形状,让我们从 Census API 中收集几列数据。
如果你因为电脑或互联网设置无法安装 Python 和 pip,或者无法访问人口普查网站,你很可能会在 Google CoLab 或 NextJournal 上获得一个免费笔记本。
获取人口普查 API 密钥
免费的!https://api.census.gov/data/key_signup.html
下载一个助手库?
如果您想提前完成一些 API 代码,请尝试 DataMade 的这个 Python 包:
美国人口普查局 API 的简单包装。提供对 ACS、SF1 和 SF3 数据集的访问。pip 安装…
github.com](https://github.com/datamade/census)
大概也有 R 包吧。留下你最喜欢的评论。
写剧本
这就是我如何找到我想要的列并从 Census API 下载它们的方法。你的方法可以有所不同。
- 从人口普查规范中查找变量名称,此处为 2010 年十年的,此处为最* 5 年的 ACS。这个引用令人困惑,所以你可能想从 SocialExplorer 上的 ACS 表名开始。
- 安装 Python、pip 和任何依赖项(
pip install requests) - 使用您的 API 键、州、县和列编辑这个 Python 脚本。
如果你用 2010 年的人口普查,你会改变什么?
- 列名
- API 终点(http://api.census.gov/data/2010/dec/sf1/
for=block:*blockid =线的末端应该参考块,而不是块组
组合人口表和 shapefiles
据我所知,有两种主要方法:
- GeoPandas (比较常见的一种)。如果你熟悉熊猫,这对你来说是有意义的。将 shapefile 作为地理数据框加载,然后根据下载的数据分配各列。
- GDAL (我常用的方法)。你需要安装一个本地 libgdal,然后
pip install gdal。然后这段代码摘录就可以工作了(或者你可以把它附加到你的下载脚本的末尾)
从一个层估计另一个层中的数据
块符合块组的内部,块组符合区域的内部,因此我们可以对它们求和并精确地计算种群。如果我们在地图上绘制其他形状,例如地铁站周围的步行距离,这种拟合就不再完美;我们只能估计里面的人口。
- MAUP 是我在 MGGG 重划实验室的同事使用 GeoPandas 开发的库。它可以处理重叠区域;例如,如果一个街区被*均分成两个选区,我们可以假设它的人口也是*均分配的。然而,人口、房屋和事件(如逮捕)在地图上并不是均匀分布的,尤其是当该区域包括空地或水域时。我以前曾使用 QGIS 从图层中移除水域,但即使这样也不会完全准确。
- 约翰·基夫的笔记本描述了如何使用 QGIS 将每个街区与其官方人口普查街区质心(而非几何质心)进行匹配,并将每个街区分配给一个选区或街区。
我喜欢这个理论,但是不太了解它,特别是如何下载质心的国家文件,或者它对块组的翻译有多好。 - 您可以通过在 QGIS 中划分图层来执行上述操作的手动版本:
矢量>研究工具>按位置选择
然后图层>另存为> [x]仅保存所选要素
但这将需要一些时间,并且您需要管理您的数据以确保只计算一次重叠区域。如果您想知道不重叠的几个研究区域内有多少人,这将是一个很好的应用程序。 - 我更熟悉将 shapefile 加载到 PostgreSQL(如果您安装了 GDAL 或 PostgreSQL,那么您已经完成了一部分)和运行查询。这可以估计重叠区域,如果您赶时间,无需 PostGIS 扩展即可完成。对于许多项目来说,这是多余的。
更新?
本文来自 2020 年 7 月。有关任何新的建议,请参见我的 GitHub 自述文件GitHub . com/mapmeld/use-this-now/blob/main/README . MD # census-API
在 Keras 中使用 Lambda 图层
在本教程中,我们将介绍如何使用 Keras 中的 Lambda 层来构建、保存和加载对数据执行自定义操作的模型。
Keras 是一个流行且易于使用的库,用于构建深度学习模型。它支持所有已知类型的层:输入层、密集层、卷积层、转置卷积层、整形层、归一化层、下降层、展*层和激活层。每一层都对数据执行特定的操作。
也就是说,您可能希望对未应用于任何现有图层的数据执行操作,然后这些现有图层类型将无法满足您的任务。举一个简单的例子,假设您需要一个层来执行在模型架构的给定点添加固定数字的操作。因为没有现有的层可以做到这一点,所以您可以自己构建一个。
在本教程中,我们将讨论在 Keras 中使用Lambda层。这允许您指定要作为函数应用的操作。我们还将看到在构建具有 lambda 层的模型时如何调试 Keras 加载特性。
本教程涵盖的部分如下:
- 使用
Functional API构建 Keras 模型 - 添加一个
Lambda层 - 将多个张量传递给λ层
- 保存和加载带有 lambda 层的模型
- 加载带有 lambda 层的模型时解决系统错误

图片来自 Unsplash 由马丁·桑切斯:https://unsplash.com/photos/NfLZeAN7I6s
使用Functional API构建 Keras 模型
有三种不同的 API 可用于在 Keras 中构建模型:
- 顺序 API
- 功能 API
- 模型子类化 API
你可以在本帖中找到关于这些的更多信息,但是在本教程中,我们将重点关注使用 Keras Functional API来构建一个定制模型。由于我们想专注于我们的架构,我们将只使用一个简单的问题示例,并建立一个模型来识别 MNIST 数据集中的图像。
要在 Keras 中构建模型,您需要将层堆叠在另一层之上。这些层在keras.layers模块中可用(在下面导入)。模块名由tensorflow前置,因为我们使用 TensorFlow 作为 Keras 的后端。
import tensorflow.keras.layers
要创建的第一层是Input层。这是使用tensorflow.keras.layers.Input()类创建的。传递给这个类的构造函数的必要参数之一是shape参数,它指定了将用于训练的数据中每个样本的形状。在本教程中,我们将只使用密集层,因此输入应该是 1-D 矢量。因此,shape参数被赋予一个只有一个值的元组(如下所示)。值为 784,因为 MNIST 数据集中每个影像的大小为 28 x 28 = 784。可选的name参数指定该层的名称。
input_layer = tensorflow.keras.layers.Input(shape=(784), name="input_layer")
下一层是根据下面的代码使用Dense类创建的密集层。它接受一个名为units的参数来指定该层中神经元的数量。请注意该图层是如何通过在括号中指定该图层的名称来连接到输入图层的。这是因为函数式 API 中的层实例可在张量上调用,并且也返回张量。
dense_layer_1 = tensorflow.keras.layers.Dense(units=500, name="dense_layer_1")(input_layer)
在密集层之后,根据下一行,使用ReLU类创建一个激活层。
activ_layer_1 = tensorflow.keras.layers.ReLU(name="activ_layer_1")(dense_layer_1)
根据下面的代码行,添加了另外两个 dense-ReLu 层。
dense_layer_2 = tensorflow.keras.layers.Dense(units=250, name="dense_layer_2")(activ_layer_1)
activ_layer_2 = tensorflow.keras.layers.ReLU(name="relu_layer_2")(dense_layer_2)dense_layer_3 = tensorflow.keras.layers.Dense(units=20, name="dense_layer_3")(activ_layer_2)
activ_layer_3 = tensorflow.keras.layers.ReLU(name="relu_layer_3")(dense_layer_3)
下一行根据 MNIST 数据集中的类数量将最后一个图层添加到网络架构中。因为 MNIST 数据集包括 10 个类(每个数字对应一个类),所以此图层中使用的单位数为 10。
dense_layer_4 = tensorflow.keras.layers.Dense(units=10, name="dense_layer_4")(activ_layer_3)
为了返回每个类的分数,根据下一行在前一密集层之后添加一个softmax层。
output_layer = tensorflow.keras.layers.Softmax(name="output_layer")(dense_layer_4)
我们现在已经连接了层,但是模型还没有创建。为了构建一个模型,我们现在必须使用Model类,如下所示。它接受的前两个参数代表输入和输出层。
model = tensorflow.keras.models.Model(input_layer, output_layer, name="model")
在加载数据集和训练模型之前,我们必须使用compile()方法编译模型。
model.compile(optimizer=tensorflow.keras.optimizers.Adam(lr=0.0005), loss="categorical_crossentropy")
使用model.summary()我们可以看到模型架构的概述。输入层接受一个形状张量(None,784 ),这意味着每个样本必须被整形为一个 784 元素的向量。输出Softmax图层返回 10 个数字,每个数字都是该类 MNIST 数据集的分数。
**_________________________________________________________________**
Layer (type) Output Shape Param #
=================================================================
input_layer (InputLayer) [(None, 784)] 0
**_________________________________________________________________**
dense*_layer_*1 (Dense) (None, 500) 392500
**_________________________________________________________________**
relu*_layer_*1 (ReLU) (None, 500) 0
**_________________________________________________________________**
dense*_layer_*2 (Dense) (None, 250) 125250
**_________________________________________________________________**
relu*_layer_*2 (ReLU) (None, 250) 0
**_________________________________________________________________**
dense*_layer_*3 (Dense) (None, 20) 12550
**_________________________________________________________________**
relu*_layer_*3 (ReLU) (None, 20) 0
**_________________________________________________________________**
dense*_layer_*4 (Dense) (None, 10) 510
**_________________________________________________________________**
output_layer (Softmax) (None, 10) 0
=================================================================
Total params: 530,810
Trainable params: 530,810
Non-trainable params: 0
**_________________________________________________________________**
现在我们已经构建并编译了模型,让我们看看数据集是如何准备的。首先,我们将从keras.datasets模块加载 MNIST,将它们的数据类型更改为float64,因为这使得训练网络比将其值留在 0-255 范围内更容易,最后重新调整,使每个样本都是 784 个元素的向量。
(x_train, y_train), (x_test, y_test) = tensorflow.keras.datasets.mnist.load_data()x_train = x_train.astype(numpy.float64) / 255.0
x_test = x_test.astype(numpy.float64) / 255.0x_train = x_train.reshape((x_train.shape[0], numpy.prod(x_train.shape[1:])))
x_test = x_test.reshape((x_test.shape[0], numpy.prod(x_test.shape[1:])))
因为在compile()方法中使用的损失函数是categorical_crossentropy,样本的标签应该根据下一个代码进行热编码。
y_test = tensorflow.keras.utils.to_categorical(y_test)
y_train = tensorflow.keras.utils.to_categorical(y_train)
最后,模型训练开始使用fit()方法。
model.fit(x_train, y_train, epochs=20, batch_size=256, validation_data=(x_test, y_test))
至此,我们已经使用已经存在的层类型创建了模型架构。下一节讨论使用Lambda层构建定制操作。
使用λ层
假设在名为dense_layer_3的稠密层之后,我们想要对张量进行某种操作,比如给每个元素加值 2。我们如何做到这一点?现有的层都没有这样做,所以我们必须自己建立一个新的层。幸运的是,Lambda层正是为了这个目的而存在的。大家讨论一下怎么用吧。
首先构建将执行所需操作的函数。在这种情况下,名为custom_layer的函数创建如下。它只接受输入张量并返回另一个张量作为输出。如果不止一个张量被传递给函数,那么它们将作为一个列表被传递。
在这个例子中,只有一个张量作为输入,输入张量中的每个元素加 2。
def custom_layer(tensor):
return tensor + 2
在构建了定义操作的函数之后,接下来我们需要使用下一行中定义的Lambda类创建 lambda 层。在这种情况下,只有一个张量被提供给custom_layer函数,因为 lambda 层可在名为dense_layer_3的稠密层返回的单个张量上调用。
lambda_layer = tensorflow.keras.layers.Lambda(custom_layer, name="lambda_layer")(dense_layer_3)
下面是使用 lambda 层后构建完整网络的代码。
input_layer = tensorflow.keras.layers.Input(shape=(784), name="input_layer")dense_layer_1 = tensorflow.keras.layers.Dense(units=500, name="dense_layer_1")(input_layer)
activ_layer_1 = tensorflow.keras.layers.ReLU(name="relu_layer_1")(dense_layer_1)dense_layer_2 = tensorflow.keras.layers.Dense(units=250, name="dense_layer_2")(activ_layer_1)
activ_layer_2 = tensorflow.keras.layers.ReLU(name="relu_layer_2")(dense_layer_2)dense_layer_3 = tensorflow.keras.layers.Dense(units=20, name="dense_layer_3")(activ_layer_2)def custom_layer(tensor):
return tensor + 2lambda_layer = tensorflow.keras.layers.Lambda(custom_layer, name="lambda_layer")(dense_layer_3)activ_layer_3 = tensorflow.keras.layers.ReLU(name="relu_layer_3")(lambda_layer)dense_layer_4 = tensorflow.keras.layers.Dense(units=10, name="dense_layer_4")(activ_layer_3)
output_layer = tensorflow.keras.layers.Softmax(name="output_layer")(dense_layer_4)model = tensorflow.keras.models.Model(input_layer, output_layer, name="model")
为了查看馈送到 lambda 层之前和之后的张量,我们将创建两个新模型,除了上一个模型。我们将这些称为before_lambda_model和after_lambda_model。两种模型都使用输入层作为输入,但输出层不同。before_lambda_model模型返回dense_layer_3的输出,T3 是正好存在于 lambda 层之前的层。after_lambda_model模型的输出是来自名为lambda_layer的λ层的输出。这样做,我们可以看到应用 lambda 层之前的输入和之后的输出。
before_lambda_model = tensorflow.keras.models.Model(input_layer, dense_layer_3, name="before_lambda_model")
after_lambda_model = tensorflow.keras.models.Model(input_layer, lambda_layer, name="after_lambda_model")
下面列出了构建和训练整个网络的完整代码。
import tensorflow.keras.layers
import tensorflow.keras.models
import tensorflow.keras.optimizers
import tensorflow.keras.datasets
import tensorflow.keras.utils
import tensorflow.keras.backend
import numpyinput_layer = tensorflow.keras.layers.Input(shape=(784), name="input_layer")dense_layer_1 = tensorflow.keras.layers.Dense(units=500, name="dense_layer_1")(input_layer)
activ_layer_1 = tensorflow.keras.layers.ReLU(name="relu_layer_1")(dense_layer_1)dense_layer_2 = tensorflow.keras.layers.Dense(units=250, name="dense_layer_2")(activ_layer_1)
activ_layer_2 = tensorflow.keras.layers.ReLU(name="relu_layer_2")(dense_layer_2)dense_layer_3 = tensorflow.keras.layers.Dense(units=20, name="dense_layer_3")(activ_layer_2)before_lambda_model = tensorflow.keras.models.Model(input_layer, dense_layer_3, name="before_lambda_model")def custom_layer(tensor):
return tensor + 2lambda_layer = tensorflow.keras.layers.Lambda(custom_layer, name="lambda_layer")(dense_layer_3)
after_lambda_model = tensorflow.keras.models.Model(input_layer, lambda_layer, name="after_lambda_model")activ_layer_3 = tensorflow.keras.layers.ReLU(name="relu_layer_3")(lambda_layer)dense_layer_4 = tensorflow.keras.layers.Dense(units=10, name="dense_layer_4")(activ_layer_3)
output_layer = tensorflow.keras.layers.Softmax(name="output_layer")(dense_layer_4)model = tensorflow.keras.models.Model(input_layer, output_layer, name="model")model.compile(optimizer=tensorflow.keras.optimizers.Adam(lr=0.0005), loss="categorical_crossentropy")
model.summary()(x_train, y_train), (x_test, y_test) = tensorflow.keras.datasets.mnist.load_data()x_train = x_train.astype(numpy.float64) / 255.0
x_test = x_test.astype(numpy.float64) / 255.0x_train = x_train.reshape((x_train.shape[0], numpy.prod(x_train.shape[1:])))
x_test = x_test.reshape((x_test.shape[0], numpy.prod(x_test.shape[1:])))y_test = tensorflow.keras.utils.to_categorical(y_test)
y_train = tensorflow.keras.utils.to_categorical(y_train)model.fit(x_train, y_train, epochs=20, batch_size=256, validation_data=(x_test, y_test))
请注意,您不必编译或训练这两个新创建的模型,因为它们的层实际上是从存在于model变量中的主模型中重用的。在这个模型被训练之后,我们可以使用predict()方法返回before_lambda_model和after_lambda_model模型的输出,看看 lambda 层的结果如何。
p = model.predict(x_train)m1 = before_lambda_model.predict(x_train)
m2 = after_lambda_model.predict(x_train)
下一段代码只打印前两个样本的输出。可以看到,从m2数组返回的每个元素实际上都是m1加 2 后的结果。这正是我们在自定义 lambda 层中应用的操作。
print(m1[0, :])
print(m2[0, :])[ 14.420735 8.872794 25.369402 1.4622561 5.672293 2.5202641 -14.753801 -3.8822086 -1.0581762 -6.4336205 13.342142 -3.0627508 -5.694006 -6.557313 -1.6567478 -3.8457105 11.891999 20.581928 2.669979 -8.092522 ]
[ 16.420734 10.872794 27.369402 3.462256 7.672293
4.520264 -12.753801 -1.8822086 0.94182384 -4.4336205
15.342142 -1.0627508 -3.694006 -4.557313 0.34325218
-1.8457105 13.891999 22.581928 4.669979 -6.0925217 ]
在本节中,lambda 层用于对单个输入张量进行运算。在下一节中,我们将看到如何将两个输入张量传递给这一层。
将多个张量传递给λ层
假设我们想要做一个依赖于名为dense_layer_3和relu_layer_3的两层的操作。在这种情况下,我们必须调用 lambda 层,同时传递两个张量。这可以简单地通过创建一个包含所有这些张量的列表来完成,如下一行所示。
lambda_layer = tensorflow.keras.layers.Lambda(custom_layer, name="lambda_layer")([dense_layer_3, activ_layer_3])
这个列表被传递给custom_layer()函数,我们可以简单地根据下一个代码获取各个层。它只是把这两层加在一起。Keras 中实际上有一个名为Add的层,可以用来添加两层或更多层,但是我们只是介绍如果有另一个 Keras 不支持的操作,你可以自己怎么做。
def custom_layer(tensor):
tensor1 = tensor[0]
tensor2 = tensor[1]
return tensor1 + tensor2
接下来的代码构建了三个模型:两个用于捕获从dense_layer_3和activ_layer_3传递到 lambda 层的输出,另一个用于捕获 lambda 层本身的输出。
before_lambda_model1 = tensorflow.keras.models.Model(input_layer, dense_layer_3, name="before_lambda_model1")
before_lambda_model2 = tensorflow.keras.models.Model(input_layer, activ_layer_3, name="before_lambda_model2")lambda_layer = tensorflow.keras.layers.Lambda(custom_layer, name="lambda_layer")([dense_layer_3, activ_layer_3])
after_lambda_model = tensorflow.keras.models.Model(input_layer, lambda_layer, name="after_lambda_model")
为了查看来自dense_layer_3、activ_layer_3和lambda_layer层的输出,下一段代码预测它们的输出并打印出来。
m1 = before_lambda_model1.predict(x_train)
m2 = before_lambda_model2.predict(x_train)
m3 = after_lambda_model.predict(x_train)print(m1[0, :])
print(m2[0, :])
print(m3[0, :]) [ 1.773366 -3.4378722 0.22042789 11.220362 3.4020965 14.487111 4.239182 -6.8589864 -6.428128 -5.477719 -8.799093 7.264849 17.503246 -6.809489 -6.846208 16.094025 24.483786 -7.084775 17.341183 20.311539 ]
[ 1.773366 0\. 0.22042789 11.220362 3.4020965 14.487111 4.239182 0\. 0\. 0\. 0\. 7.264849 17.503246 0\. 0\. 16.094025 24.483786 0\. 17.341183 20.311539 ]
[ 3.546732 -3.4378722 0.44085577 22.440723 6.804193 28.974222 8.478364 -6.8589864 -6.428128 -5.477719 -8.799093 14.529698 35.006493 -6.809489 -6.846208 32.18805 48.96757 -7.084775 34.682365 40.623077 ]
使用 lambda 层现在很清楚了。下一节将讨论如何保存和加载使用 lambda 层的模型。
保存和加载带有 Lambda 层的模型
为了保存一个模型(不管它是否使用 lambda 层),使用了save()方法。假设我们只对保存主模型感兴趣,下面是保存它的代码行。
model.save("model.h5")
我们还可以使用load_model()方法加载保存的模型,如下一行所示。
loaded_model = tensorflow.keras.models.load_model("model.h5")
希望模型能够成功加载。不幸的是,Keras 中的一些问题可能会导致在加载带有 lambda 层的模型时出现SystemError: unknown opcode。这可能是由于使用 Python 版本构建模型并在另一个版本中使用它。我们将在下一节讨论解决方案。
加载带有 Lambda 层的模型时解决系统错误
为了解决这个问题,我们不打算以上面讨论的方式保存模型。相反,我们将使用save_weights()方法保存模型权重。
现在我们只保留了重量。模型架构呢?将使用代码重新创建模型架构。为什么不将模型架构保存为 JSON 文件,然后再次加载呢?原因是加载架构后错误仍然存在。
总之,经过训练的模型权重将被保存,模型架构将使用代码被复制,并且最终权重将被加载到该架构中。
可以使用下一行保存模型的权重。
model.save_weights('model_weights.h5')
下面是复制模型架构的代码。model将不会被训练,但保存的重量将再次分配给它。
input_layer = tensorflow.keras.layers.Input(shape=(784), name="input_layer")dense_layer_1 = tensorflow.keras.layers.Dense(units=500, name="dense_layer_1")(input_layer)
activ_layer_1 = tensorflow.keras.layers.ReLU(name="relu_layer_1")(dense_layer_1)dense_layer_2 = tensorflow.keras.layers.Dense(units=250, name="dense_layer_2")(activ_layer_1)
activ_layer_2 = tensorflow.keras.layers.ReLU(name="relu_layer_2")(dense_layer_2)dense_layer_3 = tensorflow.keras.layers.Dense(units=20, name="dense_layer_3")(activ_layer_2)
activ_layer_3 = tensorflow.keras.layers.ReLU(name="relu_layer_3")(dense_layer_3)def custom_layer(tensor):
tensor1 = tensor[0]
tensor2 = tensor[1] epsilon = tensorflow.keras.backend.random_normal(shape=tensorflow.keras.backend.shape(tensor1), mean=0.0, stddev=1.0)
random_sample = tensor1 + tensorflow.keras.backend.exp(tensor2/2) * epsilon
return random_samplelambda_layer = tensorflow.keras.layers.Lambda(custom_layer, name="lambda_layer")([dense_layer_3, activ_layer_3])dense_layer_4 = tensorflow.keras.layers.Dense(units=10, name="dense_layer_4")(lambda_layer)
after_lambda_model = tensorflow.keras.models.Model(input_layer, dense_layer_4, name="after_lambda_model")output_layer = tensorflow.keras.layers.Softmax(name="output_layer")(dense_layer_4)model = tensorflow.keras.models.Model(input_layer, output_layer, name="model")model.compile(optimizer=tensorflow.keras.optimizers.Adam(lr=0.0005), loss="categorical_crossentropy")
下面是如何使用load_weights()方法加载保存的权重,并将其分配给复制的架构。
model.load_weights('model_weights.h5')
本文原载于 Paperspace 博客 。你可以在渐变 上免费运行我的教程的代码 。
结论
本教程讨论了使用Lambda层创建自定义层,该层执行 Keras 中预定义层不支持的操作。Lambda类的构造函数接受一个指定层如何工作的函数,该函数接受调用层的张量。在函数内部,您可以执行任何想要的操作,然后返回修改后的张量。
尽管 Keras 在加载使用 lambda 层的模型时存在问题,但我们也看到了如何通过保存训练好的模型权重、使用代码重新生成模型架构,并将权重加载到该架构中来简单地解决这个问题。
使用不*衡的数据集
SMOTE 简介
在建立机器学习模型时会遇到的一个典型问题是处理不*衡的数据集,其中感兴趣的标签非常少,即欺诈检测。在有偏见的数据集上直接应用机器学习模型可能会在少数标签的预测方面得到不好的结果。原因很简单,因为模型很少看到小类的训练样本,当然,当看不见的数据点出现时,它很难区分它们。
为了解决由不*衡数据集引入的问题,需要更多次类的数据点。除了通用方法,如上采样和下采样,SMOTE 可能是一个更聪明的选择,因为它通过简单地重复现有的数据点来生成数据点。在本帖中,让我们走进 SMOTE 的逻辑,通过直观地看到样本生成来更好地理解。

来源:https://www . nexsoftsys . com/articles/what-is-unbalanced-data . html
SMOTE 的想法
SMOTE,也称为合成少数过采样技术,顾名思义,是一种对少数类进行过采样的技术。它遵循以下步骤:
- 对于每个少数类,找到它的 k-最*邻
- 从它的一个邻居中选择,并在邻居和原始点之间画一条线
- 在直线上随机选择一个点(相当于选择一个介于 0 和 1 之间的比率参数,并应用该比率来获得两点之间的一个点)
- 重复操作,直到达到预期的样本数量
所以逻辑基本上是线性的,所有生成的样本实际上都是现有样本的线性组合。
示范
为了清楚地了解这些点是如何产生的,让我们用一个例子来形象化这个过程。
首先,让我们创建一个不*衡的数据集:
看起来像是:

不*衡数据集
其中标签 0 的红点是少数。
为了使用 SMOTE 进行上采样,我们可以使用现有的 python 包这里是:
其中k_neighbors是上面描述的要选择的邻居的数量,并且sampling_strategy = 0.2告诉算法将少数标签采样到0.2 x 90 = 18。
让我们将生成的点可视化,

橙色的点是合成的。你可以注意到,所有这些点实际上都位于原始点的直线之间。更清晰的演示可能是这样的:

来源:http://rikunert.com/SMOTE_explained
上图显示,所有合成实例都是现有实例的线性组合(此处代码为)。
最后说一下 SMOTE 的一些缺点。首先,最初的 SMOTE 不支持分类特征。如您所见,合成数据是由原始数据的线性组合生成的,因此分类特征不适合这种情况。这里讨论类似的问答。第二,小类可能不是简单的线性组合,在这种情况下,SMOTE 对预测没有帮助。
参考:
[1]http://rikunert.com/SMOTE_explained
R 中所访问国家的世界地图
L 和我一样,如果你和 R 一样喜欢旅行,你可能会想画一张你在 R 访问过的国家的世界地图。下面是一个我在 2020 年 1 月访问过的国家的例子:

突出显示到访国家的世界地图示例
要在 R 中绘制这个地图,您需要以下软件包:
library(highcharter)
library(dplyr)
library(maps)
像往常一样,在用library()加载软件包之前,您需要将它们安装在您的机器上。可以用命令install.packages("name_of_package")安装包。
加载完包后,我们将使用来自{maps}包的名为iso3166的数据集,并将其重命名为dat。以下是数据集的前 6 行:
dat <- iso3166
head(dat)## a2 a3 ISOname mapname sovereignty
## 1 AW ABW Aruba Aruba Netherlands
## 2 AF AFG Afghanistan Afghanistan Afghanistan
## 3 AO AGO Angola Angola Angola
## 4 AI AIA Anguilla Anguilla Anguilla
## 5 AX ALA Aland Islands Finland:Aland Islands Finland
## 6 AL ALB Albania Albania Albania
我们将变量a3重命名为iso-a3:
dat <- rename(dat, "iso-a3" = a3)
head(dat)## a2 iso-a3 ISOname mapname sovereignty
## 1 AW ABW Aruba Aruba Netherlands
## 2 AF AFG Afghanistan Afghanistan Afghanistan
## 3 AO AGO Angola Angola Angola
## 4 AI AIA Anguilla Anguilla Anguilla
## 5 AX ALA Aland Islands Finland:Aland Islands Finland
## 6 AL ALB Albania Albania Albania
我们将访问过的国家保存在一个名为countries_visited的向量中。要了解您访问过的国家的 ISO 代码,请检查数据集中的列ISOname,并从列iso-a3中提取 ISO 代码:
countries_visited <- c("AUS", "BEL", "CAN", "CZE", "DNK", "FIN", "FRA", "DEU", "GRC", "HUN", "IRL", "ITA", "LVA", "LUX", "MCO", "MMR", "NLD", "NZL", "NOR", "PRT", "ROU", "SGP", "ESP", "SWE", "CHE", "TWN", "THA", "GBR", "USA")
我们现在创建一个名为visited的新变量,如果你去过这个国家,它等于 1,否则等于 0:
dat$visited <- ifelse(dat$`iso-a3` %in% countries_visited, 1, 0)
head(dat)## a2 iso-a3 ISOname mapname sovereignty visited
## 1 AW ABW Aruba Aruba Netherlands 0
## 2 AF AFG Afghanistan Afghanistan Afghanistan 0
## 3 AO AGO Angola Angola Angola 0
## 4 AI AIA Anguilla Anguilla Anguilla 0
## 5 AX ALA Aland Islands Finland:Aland Islands Finland 0
## 6 AL ALB Albania Albania Albania 0
最后,由于来自{highcharter}包的hcmap()命令,我们已经准备好绘制世界地图了:
hcmap(
map = "custom/world-highres3", # high resolution world map
data = dat, # name of dataset
joinBy = "iso-a3",
value = "visited",
showInLegend = FALSE, # hide legend
nullColor = "#DADADA",
download_map_data = TRUE
) %>%
hc_mapNavigation(enabled = FALSE) %>%
hc_legend("none") %>%
hc_title(text = "World map") # title
根据你的需要改变论点,你就可以开始了。更进一步,您还可以添加一个列表,其中包括您访问过的所有国家,这要归功于以下代码:
dat <- subset(dat, dat$visited == 1)
sort(dat$ISOname) # sort is to have the visited countries in alphabetical order## [1] "Australia"
## [2] "Belgium"
## [3] "Canada"
## [4] "Clipperton Island"
## [5] "Czech Republic"
## [6] "Denmark"
## [7] "Finland"
## [8] "France"
## [9] "Germany"
## [10] "Greece"
## [11] "Hungary"
## [12] "Ireland"
## [13] "Italy"
## [14] "Latvia"
## [15] "Luxembourg"
## [16] "Monaco"
## [17] "Myanmar"
## [18] "Netherlands"
## [19] "New Zealand"
## [20] "Norway"
## [21] "Portugal"
## [22] "Portugal"
## [23] "Portugal"
## [24] "Romania"
## [25] "Singapore"
## [26] "Spain"
## [27] "Spain"
## [28] "Sweden"
## [29] "Switzerland"
## [30] "Taiwan"
## [31] "Thailand"
## [32] "United Kingdom of Great Britain and Northern Ireland"
## [33] "United States"
数一数去过的国家:
paste(
"Total: ",
sum(dat$visited),
" countries."
)## [1] "Total: 33 countries."
总之,下面是绘制世界地图的完整代码,其中突出显示了访问过的国家,按字母顺序列出了所有国家和访问过的国家数:
library(highcharter)
library(dplyr)
library(maps)dat <- iso3166
dat <- rename(dat, "iso-a3" = a3)
countries_visited <- c("AUS", "BEL", "CAN", "CZE", "DNK", "FIN", "FRA", "DEU", "GRC", "HUN", "IRL", "ITA", "LVA", "LUX", "MCO", "MMR", "NLD", "NZL", "NOR", "PRT", "ROU", "SGP", "ESP", "SWE", "CHE", "TWN", "THA", "GBR", "USA")
dat$visited <- ifelse(dat$`iso-a3` %in% countries_visited, 1, 0)hcmap(
map = "custom/world-highres3", # high resolution world map
data = dat, # name of dataset
joinBy = "iso-a3",
value = "visited",
showInLegend = FALSE, # hide legend
nullColor = "#DADADA",
download_map_data = TRUE
) %>%
hc_mapNavigation(enabled = FALSE) %>%
hc_legend("none") %>%
hc_title(text = "World map") # titledat <- subset(dat, dat$visited == 1)
sort(dat$ISOname) # sort is to have the visited countries in alphabetical orderpaste(
"Total: ",
sum(dat$visited),
" countries."
)
感谢阅读。我希望这篇文章能帮助你画出一张世界地图,用 r 标出你去过的国家。
和往常一样,如果您有与本文主题相关的问题或建议,请将其添加为评论,以便其他读者可以从讨论中受益。
相关文章:
- 安装和加载 R 包的有效方法
- 我的数据符合正态分布吗?关于最广泛使用的分布以及如何检验 R 中的正态性的注释
- R 中的 Fisher 精确检验:小样本的独立性检验
- R 中独立性的卡方检验
- 如何在简历中创建时间线
原载于 2020 年 1 月 9 日 https://statsandr.com**。
从 1955 年到 2020 年的世界人口条形图竞赛
数据科学 100 天的第 6 天

由 Durgesh Samariya 创作。
欢迎回到我的 100 天数据科学挑战之旅。第六天,我用 Python 制作了条形图比赛动画。对于今天的任务,我将使用从 1955 年到 2020 年的国家人口数据。
你可以在这里阅读我以前的故事:
[## 用熊猫分析和可视化 1955 年至 2020 年的乡村人口
100 天数据科学的第 1、2、3 天。
towardsdatascience.com](/analysing-and-visualising-the-country-wise-population-from-1955-to-2020-with-pandas-matplotlib-70b3614eed6b)
最*,我看到了一篇关于 Ted Petrou 发布的官方发布 bar_chart_race 的帖子。在他的文章中,他提供了一个关于如何使用 bar_chart_race 包创建条形图比赛的优秀教程。点击查看官方文件。
内容
- 条形图比赛包的安装
- 导入所需的库
- 加载数据
- 创建条形图竞赛
条形图比赛包的安装
使用 pip 安装:
pip3 install bar_chart_race
或者使用 anaconda:
conda install -c conda-forge bar_chart_race
导入所需的库
对于这个项目,我们只需要熊猫,条形图种族库。
# to load data
import pandas as pd#bar chart race
import bar_chart_race as bcr
正在加载数据集
%time population = pd.read_csv('./data/Countries Population from 1995 to 2020.csv')
在条形图比赛中,您的数据必须采用特定的格式:
- 每个条目代表一个时间
- 每个特征都有某个特定值
- 时间应该设置为。指数
让我们看看我们的数据是什么样子的。
population.head()
所以很明显,我们的数据格式不适合输入 bar_chart_race。首先,对数据进行相关修改。
步骤 1 —删除除年份、国家和人口之外的所有列。
pop = population.drop(['Yearly % Change', 'Yearly Change', 'Migrants (net)', 'Median Age', 'Fertility Rate', 'Density (P/Km²)', 'Urban Pop %', 'Urban Population', 'Country**\'**s Share of World Pop %', 'World Population', 'Country Global Rank' ], axis = 1)
第二步 —从 pop 数据帧创建 pivot_table,其中Year为索引;每个国家作为列,人口作为值。
df = pop.pivot_table('Population',['Year'], 'Country')
有时候你的数据是不有序的,所以一定要把时间列排序。在我们的例子中,它是Year。
df.sort_values(list(df.columns),inplace=True)
df = df.sort_index()
df
现在我们的数据已经准备好了,所以让我们创建一个条形图比赛。
创建条形图竞赛
你可以简单地使用bcr中的.bar_chart_race()方法。
bcr.bar_chart_race(df)
上面给的步骤很简单,没有我想要的那么吸引人。所以还是定制吧。让我们看看最终的代码。您可以在 bar_chart_plot 文档中找到所有功能和不同的可能性。
bcr.bar_chart_race(
df=df,
filename=None,
orientation='h',
sort='desc',
n_bars=10,
fixed_order=False,
fixed_max=True,
steps_per_period=10,
interpolate_period=False,
label_bars=True,
bar_size=.90,
period_label={'x': .99, 'y': .25, 'ha': 'right', 'va':'center'},
period_summary_func=lambda v, r: {'x': .99, 'y': .18,
's': f'Population**{**v.nlargest(39).sum()**:**,.0f**}**',
'ha': 'right', 'size': 8, 'family': 'Courier New'},
period_length=500,
figsize=(6.5,5),
dpi=144,
cmap='dark12',
title='Population by Country',
title_size='',
bar_label_size=7,
tick_label_size=5,
shared_fontdict={'family' : 'Helvetica','color' : '.1'},
scale='linear',
writer=None,
fig=None,
bar_kwargs={'alpha': .7},
filter_column_colors=True)

最终输出。由 Durgesh Samariya 创作。
我要感谢 Ted Petrou 创建了这么棒的库和教程。除此之外,还要感谢 Vinay Shaw——这个内核的作者,他向我介绍了这个美丽的库。
最后的话
今天到此为止。今天,我学习并创建了我的第一个条形图比赛可视化。未来期待从中创造一些有趣的东西。我希望这篇文章对你有帮助。所有数据分析和可视化的代码都可以在这个 GitHub 库或者 Kaggle 内核中找到。
感谢阅读。
我感谢任何反馈。
100 天的数据科学进步
[## 用熊猫分析和可视化 1955 年至 2020 年的乡村人口
100 天数据科学的第 1、2、3 天。
towardsdatascience.com](/analysing-and-visualising-the-country-wise-population-from-1955-to-2020-with-pandas-matplotlib-70b3614eed6b)
关于我
我是 Durgesh Samariya ,澳大利亚费杜尼大学机器学习博士三年级学生。
在互联网上:
在 LinkedIn 、 Instagram 、 Kaggle 、 GitHub 、 Medium 上关注我。
如果你喜欢阅读这样的故事,并希望支持我的写作,可以考虑成为一名灵媒。每月支付 5 美元,你就可以无限制地阅读媒体上的故事。如果你使用我的注册链接,我会收到一小笔佣金。
已经是会员了?订阅在我发布时得到通知。
世界 2 模型,从发电机到 R

世界动态,如 Forrester (1971)的 World2 模型中所定义。
第一个世界末日计算机模型,基于 Forrester 的系统动力学,用 R 编程语言重新审视
“1972 年,一本名为《》的*装小书大张旗鼓地出版了。这本书警告说,世界正在走向灾难。如果方向没有重大改变,在 50 年或 100 年内,人类将会耗尽食物和自然资源,或者因自身污染而窒息(Hayes,1993)。
杰伊·赖特·福雷斯特(1918–2016)是美国计算机工程师和系统科学家,被誉为系统动力学的“创始人”。虽然比写了《增长的极限》(Meadows et al .,1972)的 Meadows 夫妇更少为人所知,但 Forrester 的世界动力学模型 World2 (Forrester,1971)是所有后续模型的基础预测到 21 世纪中叶我们的社会技术自然系统将崩溃*。本文从编程的角度重新审视了这项开创性的工作,将过时的 DYNAMO 代码重写为 r。它将提供世界动力学模型的全新视角,并可以在以后用作机器学习练习的输入模型。*
《世界动力学》简史
Forrester 于 1989 年在德国斯图加特的系统动力学学会上发表的一次演讲的文字记录为我们提供了媒体对他的世界末日计算机模型的发表的反应的第一手资料:
“《世界动态》于 1971 年 6 月的第一周出版。六月的最后一周,伦敦《观察家报》的头版对此进行了评论。我收到了纽约一位大学教授的来信,要求提供更多的信息,因为他在《新加坡时报》上读到了这本书。8 月份,这本书占据了《基督教科学箴言报》第二版的整个头版,9 月份占据了《财富》的一个半版,10 月份占据了《华尔街日报》的一个专栏。它出现在美国中部报纸的社论栏中,它是欧洲黄金时间纪录片电视的主题,它在环境媒体、人口零增长媒体和反体制的地下学生媒体中被辩论。而且,如果你不喜欢你的文学作品中既有当权派右派也有当权派左派,那么就在政治光谱的中间,在《世界动态》出版九个月后,《增长的极限》在《花花公子》上发表了一篇完整的文章。信息基本上是一样的,尽管做了更多的工作,这本书也更受欢迎……”—杰伊·w·福瑞斯特,1989**

继 Sterman (2001)之后,系统动力学学派的现任领袖,两次授予 Jay W. Forrester 系统动力学最佳出版作品奖。来源:维基百科。
系统动力学是一种理解复杂系统随时间变化的非线性行为的方法,使用股票、流量、内部反馈回路、表格函数和时间延迟(维基百科)。事实上,它来源于早期的控制论著作。
World2 不是 Forrester 在系统动力学方面的第一个主要工作,因为他已经将该方法应用于“工业动力学”中的工厂规划(Forrester,1961),然后应用于“城市动力学”中的城市规划(Forrester,1969;有趣的是,这后来对电子游戏《模拟城市》的创作产生了影响;Lobo,2007)。Forrester 在城市动力学方面的工作引起了罗马俱乐部的注意,这引发了对全球可持续性的讨论,并在 1971 年出版了《世界动力学》一书,其中提出了 World2 模型。 Meadows 的团队并行开发了模型的扩展,产生了 World3 版本 (Meadows 等人,1972;梅多斯,1974;Meadows 等人在 2004 年更新;另见 Randers,2012 年)。有趣的是,Forrester 在他与瑞士罗马俱乐部的会议结束后,在回家的飞机上草拟了一个模型的初步版本,名为 world 1(Hayes,1993)。
World1 模型的初步结果。2 分 35 秒:“我们来到 2020 年,它真的回来了。当然,更多的人意味着你开始消耗你的自然资源,这是这条曲线,N 曲线,显示了…
重新运行模型 World2 和 World3
“1971 年的‘世界动态’似乎已经具备了保证不引起公众注意的一切必要条件。首先,在书的中间有40 页的方程式,这足以压制公众的兴趣。第二,有趣的信息是以电脑输出图的形式出现的,而大多数公众并不理解这样的演示。” —杰伊·w·福雷斯特,1989 年**
在纸上阅读一个模型的特征和输出不足以完全掌握其潜在的机制和假设。由于世界 2 和世界 3 模型在系统动力学中的历史重要性以及随后 50 年的争论,重温源代码是值得的。
虽然我认为很容易找到这些模型的现代版本,但事实上我不得不深入挖掘以找到一些开放源代码,并最终决定在阅读 Forrester (1971)的基础上,用 R 从头开始重做 World2 模型。下面是我通往 Rworld2 项目的旅程总结:

截图来自 javascript 的 World3 测试版,来自bit-player.org。
- 事情变得越来越具体:存在一个 Javascript 仪表板,允许用户尝试 World3 模型的不同参数化,并绘制结果。然而,的免责声明声明“t 这是一个正在进行的工作,很可能有严重的错误”这与该工具的作者 Brian Hayes 的一篇非常好的文章有关,他在《美国科学家》杂志上写了几篇关于“增长的极限”的文章。2012).下面是这篇文章的一个重要内容:“最初的 World3 模型运行在大型机硬件上——IBM 360 和 370 机器。“迪纳摩计划的其他金块如下,以下是四个要点。
- Hayes 提到“有几种更通用、更值得信赖的实现方式”,并提供了下面的链接,不幸的是,到 2020 年这个链接已被打破。
- Scott Fortmann-Roe 通过 Insightmaker (Fortmann-Roe,2014 年)提供了一个值得信赖的 World3 实现——然而,使用一个基于 web 的图形模型构造的通用工具与我最初想到的简单脚本的灵活性不一致。

在https://insight maker . com/insight/1954/The-World3-Model-A-Detailed-World-predictor对 World 3 模型进行在线模拟。
- 最可靠和最好记录的开源版本的 World2 和 World3 是由弗朗索瓦·e·塞利耶在 Modelica 中编写的(塞利耶,2007;2008;卡斯特罗等人,2014)。World3 的文档提供了一些有价值的历史信息:“Jay Forrester 在他的书《世界动力学》中列出了他的整个模型,而 Dennis Meadows 只在《增长的极限》中谈到了使用他的模型获得的结果。型号本身没有列出。然而,梅多斯从未试图隐藏自己的模型,以躲避公众的审视。事实上,他写了一份 637 页的 内部报告,描述了他的 模型的方方面面。1974 年,他将这份内部报告作为独立的一本书出版了:《有限世界中的增长动力学》。“Cellier(2008)补充道:“虽然 Forrester 和 Meadows 的著作在 70 年代初首次出版时引起了不小的轰动,但世界建模很快就变得不流行了,因为基本上所有的资金来源都因为政治原因而枯竭了。 只是最*,在石油峰值事件迫在眉睫的背景下,并且因为正在进行的关于全球变暖的讨论,世界建模才再次变得受人尊敬 。这使得 Rworld2 成为一个及时的项目,特别是因为 Modelica 代码虽然可用,但不太可能直接用于任何现代数据科学项目。

左图:显示 World2 场景 1 的 OpenModelica OMEdit *台;右图:Modelica 中 World2 程序的摘录(OpenModelica 库的一部分,可在文件夹/om library/system dynamics 2.1/package . mo 中找到)。
- 回到起点!既然 Modelica 程序本身是从 Forrester (1971)提供的原始 DYNAMO 代码翻译而来,为什么还要翻译它呢? 迪纳摩(动力学模型) 最初是由 Forrester 的 MIT 团队在 20 世纪 50 年代末开发的,用于系统动力学计算。世界 2 和世界 3 最初都是用 DYNAMO 编写的。自从《增长的极限》出版后,这种语言就不再使用了。 Hayes 描述得很好:“在词汇和句法结构上,DYNAMO 是你对穿孔卡片时代的语言的期望——六个字母的变量名和全部大写——但在其他方面,它是一个有趣的早期实验,其编程风格介于过程式和声明式之间。“这是 Forrester (1971:136)的附录 B‘世界模型的方程式’的摘录,显示了 DYNAMO 中的一些代码行:
*** WORLD DYNAMICS W5
L P.K=P.J+(DT)(BR.JK-DR.JK)
N P=PI
C PI=1.65E9
R BR.KL=(P.K)(CLIP(BRN,BRN1,SWT1,TIME.K))(BRFM.K)(BRMM.K)(BRCM.K)
X (BRPM.K)
C BRN=.04
C BRN1=.04
C SWT1=1970
A BRMM.K=TABHL(BRMMT,MSL.K,0,5,1)
T BRMMT=1.2/1/.85/.75/.7/.7
A MSL.K=ECIR.K/(ECIRN)**
世界 2 从零开始
现在让我们看一下模型的结构,乍一看可能会让人不知所措。我建议先看看世界 3 的结构,使世界 2 的复杂性更加相关(世界 2 中有 43 个控制方程,而世界 3 中有 150 个)。

****世界 2 模型的完整图表涉及五个层次的变量——人口、自然资源、资本投资、农业资本投资和污染,摘自 Forrester(1971:20–21,图 2–1)。因此,World2 是一个 5 阶微分方程模型。这个图怎么看?首先,确定 5 个级别(红色),然后是代表流入和流出的比率(蓝色),最后是许多因素(黄色)。水*、比率和因素之间的相互关系(蓝色)在 World2 等式中定义。
我发现 Forrester (1971)第 3 章中提供的 DYNAMO 脚本比附录 b 中给出的完整代码更容易理解。我通过遵循书中相同的编号系统来识别不同的“方程”(即赋值语句)以进行直接匹配。类似地,我保留了相同的变量和函数名。在 DYNAMO 中顺序并不重要,所以 R 中最繁琐的步骤是对 World2 的 43 个等式和所谓的乘数(见下文)进行重新排序,以便正确定义和更新所有变量(因此我手动完成了 DYNAMO 编译器的工作。将 R 中的 World2 编码为一个图,然后应用拓扑排序,这将是一个挑战,尽管很诱人。
等式的一个例子在下面的 R 中给出:
**# logical function used as time switch to change parameter value
CLIP <- function(FUNCT1, FUNCT2, THRESH.K, VAL) if(THRESH.K >= VAL) return(FUNCT1) else return(FUNCT2)DT <- .2
TIME <- seq(1900, 2100, DT) # CALENDAR TIME (YEARS)
n <- length(TIME)# (...)## (2) Birth rate BR ##
# BR.KL = P.K CLIP(BRN, BRN1, SWT1, TIME.K) BRFM.K BRMM.K BRCM.K BRPM.K
# BRFM = BIRTH-RATE-FROM-FOOD MULTIPLIER ()
# BRMM = BIRTH-RATE-FROM-MATERIAL MULTIPLIER ()
# BRCM = BIRTH-RATE-FROM-CROWDING MULTIPLIER ()
# BRPM = BIRTH-RATE-FROM-POLLUTION MULTIPLIER ()
BR <- numeric(n) # BIRTH RATE (PEOPLE/YEAR)
BRN <- .04 # BIRTH RATE NORMAL (FRACTION/YEAR)
BRN1 <- .04 # BIRTH RATE NORMAL no. 1 (FRACTION/YEAR)
SWT1 <- 1970 # SWITCH TIME no. 1 FOR BRN (YEARS)# (...)for(K in 2:n){
J <- K - 1 # (...) BR[K] <- P[J] * CLIP(BRN, BRN1, SWT1, TIME[J]) * BRMM(MSL[J]) * BRCM(CR[J]) * BRFM(FR[J]) * BRPM(POLR[J]) # (2-3,16-18) # (...)**
BR 是出生率,是人口 P、MSL 物质生活水*、拥挤 CR、食物 FR 和污染 POL (POLR 是污染比率)的函数。BR 与 P 成比例增长,并通过称为乘数的单值函数依赖于其他变量(BR .variable. M)。现在是一个乘数示例,基于 Forrester 定义的查找表:
**library(signal) # interp1()## (3) Birth-rate-from-material multiplier BRMM ##
BRMM = function(MSL.K){
# MSL = MATERIAL STANDARD OF LIVING ()
# BRMMT = BIRTH-RATE-FROM-MATERIAL MULTIPLIER TABLE
lookup.table <- data.frame(MSL = seq(0, 5, 1),
BRMMT = c(1.2, 1., .85, .75, .7, .7))
return(interp1(lookup.table$MSL, lookup.table$BRMMT, MSL.K))
}**
为了清晰地解释 Forrester 构建 World2 的方法,我推荐 Castro 和 Cellier 的这个 2016 演示文稿。由于 Rworld2 代码大约有 500 行长(包括许多与 Forrester,1971 年的第 3 章直接匹配的注释),所以这里没有给出它的全文,但是可以在我的 GitHub repohist _ GC _ sys dyn(History _ global distoriates _ system dynamics 的简称)中找到。最后,让我们画出结果。
**# basic plots with same y-range as in Fig. 4-1 of Forrester (1971)plot(TIME, P, type = 'l', ylim = c(0, 8e9))
plot(TIME, POLR, type = 'l', ylim = c(0, 40))
plot(TIME, CI, type = 'l', ylim = c(0, 20e9))
plot(TIME, QL, type = 'l', ylim = c(0, 2))
plot(TIME, NR, type = 'l', ylim = c(0, 1000e9))**

****Rworld2人口 P、污染率 POLR、资本投资 CI、生活质量 QL 和自然资源 NR 的原始结果。
通过将结果与 Forrester (1971 年)的图 4–1 进行比较,很容易验证 R 代码是否正确— 参见 GitHub 上的图。《Rworld2》很粗糙,但我想保留弗雷斯特原著中 20 世纪 70 年代的精髓。未来的工作将包括一个交互式仪表板,正如 javascript 和 Insight Maker 已经提供的那样。另一个步骤是用 R 写 World3(如果 Rworld2 花了我一整天的工作,Rworld3 应该花了大约半个星期)。一个长期的项目是将模型定义为一个图。
最终注释
这篇中型文章的目的是从编程的角度展示 World2 模型,并提供一个 R 版本,这是以前所缺乏的。围绕各种世界末日场景的争论不是本文的主题,但读者可能会问,在 2020 年运行 World2 是否有任何用处。因此,让我引用 Cellier (2008)关于这种过时模型的有用性的话:“事实证明,Forrester 和 Meadows 的评估基本上是正确的,尽管他们的模型与现实世界的动态相比非常粗糙。“这些“简单”模型的有效性已经在最*的研究中得到证实,这些研究将过去的预测与观察结果进行了比较(Turner,2008;2014).
参考
Castro R,Fritzson P,Cellier F,Motesharrei S,Rivas J (2014),【Modelica 世界建模中的人与自然互动。 Proc。第十届国际 Modelica 会议,瑞典隆德,477–488。
Cellier FE (2007),生态足迹,能源消耗,以及即将到来的崩溃。油桶。
Cellier FE (2008),Modelica 中的 World3:在 Modelica 框架中创建系统动力学模型。过程。2008 年 modelica,2393400 英镑。
福瑞斯特·JW(1961 年)产业动态。麻省理工学院出版社,剑桥,464 页
福瑞斯特·JW(1969)城市动力学。飞马通信公司,沃尔瑟姆,285 页。
福瑞斯特·JW(1971),《世界动态》。Wright-Allen Press,Inc .,Cambridge,144 页。
Fortmann-Roe S (2014), Insight Maker:基于 web 的建模通用工具&模拟。模拟建模实践与理论,47,28–45。
海斯 B(1993)计算科学:在铅笔尖上保持*衡。美国科学家,81(6),510–516。
Hayes B (2012),计算科学:计算和人类困境,增长的极限和计算机建模的极限。美国科学家,100(3),186–191。
Lobo DG (2007),玩转都市生活。在 von Borries 等人(编辑)的文章中,时空玩电脑游戏,建筑和城市化:下一个层次。伯克豪泽,495 页。
梅多斯 DH,梅多斯 DL,兰德斯 J,伯伦斯三世 WW(1972)增长的极限 。宇宙图书,纽约,205 页。
梅多斯 DL(1974)有限世界中的增长动力学。赖特艾伦出版社,剑桥,637 页
梅多斯 D,梅多斯 D,兰德斯 J(1992)超越极限。切尔西格林出版社,300 页。
梅多斯 D,兰德斯 J,梅多斯 D (2004),增长的极限:30 年更新。切尔西格林出版社,338 页。
兰德斯 J (2012), 2052:未来四十年全球预测。切尔西格林出版社,416 页。
斯特曼法学博士(2001),系统动力学建模:复杂世界中的学习工具。加州管理评论,43(4),8–25
特纳通用汽车(2008),增长的极限与 30 年现实的比较。全球环境变化,18,397–411。
特纳通用汽车(2014),全球崩溃是否迫在眉睫?增长的极限与历史数据的最新比较。MSSI 研究论文№4,墨尔本可持续社会研究所,墨尔本大学,共 21 页
你更愿意做数据分析师还是数据科学家?
扮演其中一个角色是什么感觉?在这里找到答案。

照片由克里斯蒂娜@ wocintechchat.com 拍摄。
目录
- 介绍
- 数据分析师
- 数据科学家
- 摘要
- 参考
介绍
作为一名专业数据分析师和数据科学家,我认为强调每个职位的经历以及他们日常感受的一些关键差异是有见地的。最终,我希望我的文章能帮助你决定哪个角色最适合你。如果你已经处于其中一个位置,也许你想换到另一个位置。一些人从数据分析师开始,然后成为数据科学家,然而,作为一个不太受欢迎但仍然有点突出的途径,是从非高级数据科学家职位到高级数据分析师。对于每一个职位,在你进行下一次重大职业变动时,有几个概念和总体经验是很重要的。
下面,我将强调作为数据分析师和数据科学家的感受。我将提出关于每个角色的常见问题,并根据我的经历做出相应的回答——此外还有每个领域的一些亲密同行。
数据分析师
如果你想描述过去或当前的数据,同时展示关键的发现、变化和趋势,并最终将数据可视化给利益相关者,那么数据分析师的职位最适合你。虽然这两个职位之间有一些重叠,我在另一篇文章(链接在本文的末尾)中强调了这两个职位技能之间的差异和相似之处,但我现在想花一些时间来回顾一下作为数据分析师和数据科学家的感受。重要的是要知道在这个领域你每天会期待什么。你可以期望与不同的人一起工作,以不同的方式沟通(更多),并且比典型的数据科学家行动更快。
因此,你对每个角色的感受可能会大相径庭。
下面,我将提出一些常见的问题及其相应的回答,以阐明数据分析师的体验。
- 你和谁一起工作?
—您将主要与公司中要求提取数据、可视化见解和报告的利益相关方合作。通过使用电子邮件、Slack 和吉拉等工具,可以预期沟通将是口头和数字的。你将专注于业务的人员和分析方面,而不是公司的工程和产品部分(根据我的经验)。
- 你与谁分享你的发现?
—你将与上面的人分享你的发现。然而,如果你有一个经理,有时,你会向他们报告,他们会转达和分享你的发现给适当的利益相关者。您可能还有一个收集需求、开发报告并将其传达给利益相关者的过程。您可以使用 Tableau、Google Data Studio、Power BI 和 Salesforce 等工具进行报告。这些工具通常可以连接到容易访问的数据源,如 CSV 文件,而有些工具需要更多的技术工作,通过 SQL 对数据库进行高级查询。
- 你需要以多快的速度完成一个项目?
—您从事项目的速度将比数据科学家快得多。你可以每天有几个数据拉取(查询)或报告,每周有更大的可视化和洞察力。因为你不是在建立模型和预测(通常是),你会更快地得到结果,因为它们更具描述性和针对性。
数据科学家
数据科学家与数据分析师大相径庭。虽然一些工具和语言可以在这两个角色之间重叠,但你可以预期与不同的人一起工作,并花更多时间研究更大的项目,如机器学习模型的创建和部署。数据分析师可能倾向于独立完成他们的项目;例如,使用 Tableau 仪表板来呈现结果可能需要一个人,但数据科学家可以整合其他几个工程师和产品经理来确保模型解决了业务问题,并且代码是正确的、强大的和高效的。
- 你和谁一起工作?
—与数据分析师不同,在项目的某些方面,您将与利益相关者合作,但在模型及其结果的其他方面,您将求助于数据工程师、软件工程师和产品经理。
- 你与谁分享你的发现?
—您可以与利益相关者分享您的发现,但也可以与一些工程师分享,他们需要知道最终产品是什么,以便他们可以围绕您的预测构建 UI(用户界面)。
- 你需要多快完成一个项目?
—也许这些角色感觉和操作的最大区别是你分配给每个项目的时间。尽管数据分析的节奏更快,但数据科学家可能需要几周或几个月才能完成一个项目。由于存在数据收集、探索性数据分析、基础模型创建、迭代、模型调整和结果输出等过程,数据科学模型和项目可能需要更长时间。
摘要

马库斯·温克勒在Unsplash【2】上的照片。
作为一名数据分析师和数据科学家,您可以共享 Tableau、SQL 甚至 Python 等常用工具,但每个角色的体验可能会大不相同。数据分析师的日常工作包括更多的会议、更多的面对面互动、软技能和更快的项目周转。数据科学家的工作可能涉及更长的流程、与工程师和产品经理的互动,以及总体而言,预测模型着眼于及时分类新的观察结果或事件,而数据分析则侧重于过去和当前的状态。
我已经写了一篇关于这些角色细节的更深入的文章。你可以在这里找到这篇文章[2]:
数据科学家和数据分析师的主要区别和相同点是什么?阅读下面的概述…
towardsdatascience.com](/data-science-vs-data-analysis-heres-the-difference-4d3da0a90f4)
我希望你觉得这篇文章有趣并且有用。感谢您的阅读!
参考
[1]照片由克里斯蒂娜@ wocintechchat.com(2019)拍摄
[2]马库斯·温克勒在 Unsplash 上拍摄的照片,(2020)
[3] M.Przybyla. 数据科学 vs 数据分析。区别就在这里。,(2020 年)*
你更愿意成为 NLP 还是计算机视觉数据科学家?
意见
深入了解这些受欢迎的数据科学家角色。

JESHOOTS.COM在Unsplash【1】上的照片。
目录
- 介绍
- 数据科学
- 自然语言处理
- 计算机视觉
- 摘要
- 参考
介绍
在申请数据科学家的职位时,您可能会在职位描述部分看到各种所需的技能。向下滚动,你会发现不同职位所要求的教育程度也是不同的。最重要的是,您会看到一个概述,概述了该角色,尽管职位的标题是相同的,但部分却有很大的不同。这种变化是由于不同类型的数据科学职位。然而,我注意到,随着公司了解他们在数据科学方面的专长,这些角色有了新的名称。数据科学的这两个热门分支是自然语言处理(NLP)和计算机视觉。根据你最终将为之工作的公司,或者目前为之工作的公司,一些职位仍将被称为数据科学,但重点是 NLP 或计算机视觉,而一些职位将是全面的数据科学。我将重点介绍 NLP 和计算机视觉,以便您可以找到更多信息,了解这两者的含义,以及各自的预期工资,以及哪个角色最终更适合您。
数据科学
数据科学是一个非常宽泛的术语,经常在人们中间引起争议,尤其是在技术领域。当前的数据科学家可能会根据他们在第一份工作中的经历,对他们认为数据科学真正是什么有一些偏见,但后来会意识到数据科学实际上是几个学科的总称。这些学科包括或围绕自然语言处理、计算机视觉、机器学习、统计学、数学、编程、数据分析、产品管理和商业智能。这真的取决于你和你工作的公司来决定你想要走什么样的道路,或者成为所有这些方面的多面手。专攻 NLP 或计算机视觉的一个好处是,你将知道你将进入什么领域,并可以专注于学习和提高每个职位所需的特定技能。
自然语言处理
有时,专门从事 NLP 的数据科学家也被称为 NLP 工程师。这种专业化专注于人类的自然语言,以及计算机如何参与消化这种非结构化的输入,然后输出结构化的、有用的意义。虽然这种类型的数据科学有无数的定义和例子,但我想给出我个人在 NLP 方面的专业经验。我主要参与过三种类型的 NLP 项目。这三个项目包括:
- 情感分析
- 主题建模
- 文本分类
这些项目有主要的概念,这些概念也可以应用于其他形式的 NLP。他们都共享相似的工具和代码来创建有益的输出。具体来说,我在 Python 编程语言中使用 NLP 最多。
情感分析 —这种形式的自然语言处理关注给定文本的情绪或情感、极性和主观性。情感分析的典型工作流程是收集数据,对其进行预处理,然后对其进行表征。本质上,在这一点上,你将有你正在分析的每一个词,清理,并剥离,以便这些词可以被标记。下一部分通常被称为词性标注。一旦你确定了你所拥有的词的类型,比如形容词、名词和动词,你就可以很容易地应用一个库的功能,为每个文本分配一个极性分数。一些流行的情感 NLP 库是 TextBlob 和 Vader perspection。我在这里不会讲得太深入,但是如果你想写一篇关于 NLP 和这两个流行的库的文章,我很乐意这样做(请在下面评论)。情感分析可以被大多数企业广泛使用。这里有一些可以应用情感分析的例子:
—客户评论
—客户细分
—异常检测
—产品改进
下面是情感分析的总结流程:
gather datapreprocesstokenizePOS tagscoring
主题建模 —这种形式的自然语言处理属于无监督学习的一个分支,帮助你找到由文本组成的文档的主题。在文档中查找主题最流行的方法之一是利用 LDA 或潜在 Dirichlet 分配。这是一种技术,最终输出的主题总结了流行的和重要的,关键短语从你的文本。这里有一些可以应用主题建模的例子:
—从课文中提出新的话题
—使用这些主题来分配新的监督学习标签
—通过手动搜索很难找到的见解
文本分类 —这种形式的 NLP 是一种监督学习技术,有助于对新的数据实例进行分类,这些数据实例不一定只包含文本,也可以包含数值。比这两种 NLP 形式更广泛的是,您可以将文本分类视为一种典型的分类算法,其中标签是文本,一些特征也是文本。您将使用上述相同的技术对文本进行预处理、清理和提取含义。这里有一些可以应用文本分类的例子:
—对特殊动物进行分类
—将假新闻分类
—对银行交易进行分类
最流行的 Python 包是nltk【2】,它代表自然语言工具包。它包含了几个库,这些库对于你用 NLP 技术解决问题是必不可少的。
NLTK 是构建 Python 程序来处理人类语言数据的领先*台。它提供了易于使用的…
www.nltk.org](https://www.nltk.org/)
一个 NLP 工程师挣多少钱?
根据glass door【3】统计,美国一名 NLP 工程师的*均工资为 114121 美元/年。
计算机视觉
我相信这个数据科学领域甚至比 NLP 更专业。计算机视觉关注图像和视频数据,而不是数字或文本数据。对我来说,计算机视觉的风险更大,因为它可以用于更多不一定依赖于洞察力,但需要安全措施到位的行业。想想 NLP 和情感分析如何分析某人评论的快乐,这种洞察力是有用和强大的,但不如计算机视觉那样有影响力或有害。下面我将重点介绍一些类型的计算机视觉。
面部识别 —当你拿起手机时,你很可能会有一个安全功能,它会分析你的面部,看看是否真的是你试图访问你的手机。一个有益于面部识别项目的流行 Python 库被恰当地命名为 face_recognition 。您处理的由面组成的图像被编码为一个特征。基于面部的共同特征,您可以将(或)个别面部与相同或不同的面部进行匹配,以便最终“识别”面部。
物体检测 —使用来自物体的信息,这种形式的计算机视觉可以帮助检测物体。OpenCV 是程序员和数据科学家使用的流行工具,他们希望专注于对象检测。
你可以期待在中找到计算机视觉的例子
—图像检测
— iPhone Face ID
—脸书照片标签
—特斯拉行人和汽车检测
一个计算机视觉工程师挣多少钱?
据glass door【4】报道,美国一名 NLP 工程师的*均工资为 99619 美元/年。
虽然这两个工资都很高,但我个人从招聘信息中看到,不仅计算机视觉工程师的工资高于报道的*均工资,NLP 工程师也是如此。因为数据科学中的这两个角色越来越专业化,我相信这就是为什么你可以期望有更高的工资。
摘要

安妮·斯普拉特在Unsplash【5】上的照片。
大多数数据科学家可能研究过某种形式的 NLP 或计算机视觉,无论是来自大学还是在线教程。数据科学中的这两个专业角色都非常受尊重,可以让无数行业受益。在回答“你更愿意做 NLP 工程师还是计算机视觉工程师”这个问题时,最终取决于你的偏好和职业目标。’。想一想你想从事什么类型的项目,想为哪个行业工作,想和哪个公司合作。数据科学领域的这两个职位都能给你的工作带来巨大的影响,所以这两个职位都会给你一次激励的经历。
我希望你觉得这篇文章有趣并且有用。请随意在下面评论您作为一名普通数据科学家、NLP 工程师或计算机视觉工程师的经历。
感谢您的阅读!
参考
[1]照片由JESHOOTS.COM在Unsplash(2018)拍摄
[2] NLTK 项目,自然语言工具包,(2020)
[3] Glassdoor,Inc .,NLP 工程师工资 T25,(2008-2020 年)
[4] Glassdoor,Inc .,计算机视觉工程师工资,(2008–2020)
【5】安妮·斯普拉特在Unsplash(2020)上拍摄的照片
你会在泰坦尼克号上幸存吗?——对 Kaggle 的经典挑战
关于如何开始 Kaggle 之旅的 7 步指南

安东尼·梅特卡夫在 Unsplash 上的照片
卡格尔
Kaggle 是数据科学家共享数据、交流思想和在预测分析问题上竞争的*台。人们经常认为 Kaggle 不适合初学者,或者它有一个非常陡峭的学习曲线。
他们没有错。但是它们确实给像你我一样刚刚起步的人带来了挑战。作为一名(初级)数据科学家,我无法抗拒搜索有趣的数据集来开始我的 Kaggle 之旅。 和我撞上了泰坦尼克号。
Kaggle 是数据科学家的 Airbnb 这是他们度过夜晚和周末的地方。齐尚·哈桑·乌斯马尼
泰坦尼克号
该数据集包含泰坦尼克号上乘客的信息。
我使用了 Python 来可视化和理解更多关于数据集的内容。我训练了一个分类器的集合,使用 scikit-learn 来预测一个人的生存几率。然后,我使用 pickle 保存这个模型,并使用 Flask 将它作为 Web App 部署在本地主机上。最后,我利用 AWS 来云托管它。
该代码可在 GitHub 上获得。
坐稳了,我们走!

和 hika Soreng 在 Unsplash 上的照片
1.数据检查
首先要做的事。我把数据导入到一个熊猫 数据框 中。它包括乘客 ID、幸存者、机票等级、姓名、性别、年龄、船上兄弟姐妹和配偶的数量、船上父母和子女的数量、机票号、乘客票价、客舱号和出发港。

前 5 行数据
我立即想到的是:
PassengerId是每个条目唯一的键,Survived是我们要用推断的目标,Name可能没有帮助,但是他们的头衔可能有帮助,Ticket是一团乱,而且- 有缺失数据标注为
NaN。
为了简单起见,我决定暂时去掉变量Ticket。它可能包含有用的信息,但提取这些信息需要大量的特征工程。我们应该从最容易的开始,并从那里着手。

缺失数据的比率
另一方面,让我们仔细看看缺失的数据。变量Embarked和Fare中有几项缺失。另一方面,大约 20%的乘客年龄没有被记录。这可能会给我们带来一个问题,因为Age可能是数据集中的关键预测值之一。“妇女和儿童优先”在当时是一种行为准则,报告表明他们确实是首先被拯救的。Cabin中有 > 77%的漏项,不太可能有太大帮助,暂且不提。
2.数据可视化
Pair plot (下面未显示)通常是我在数据可视化任务开始时的首选,因为它通常很有帮助,并且它具有很高的信息与代码行数比率。一条单线seaborn.pairplot()给你 n 个图(技术上是 n(n+1)/2 个不同的图),其中 n 代表变量的数量。它让你对每一对变量之间的关系,以及每个变量本身的分布有一个基本的了解。让我们深入研究不同的变量。

我们首先逐个检查目标变量Survived与每个预测值的关系。到了seaborn.countplot(),我们发现大多数人属于第三类,这并不奇怪;总的来说,他们存活的概率较低。即使有这个单一的预测因素, 考虑到其他一切未知的 ,我们可以推断一个头等舱乘客更有可能幸存,而这对于一个三等舱乘客来说不太可能。

同时,妇女和儿童更有可能幸存,这与前面提到的 【妇女和儿童优先】 理论相一致。如果我们只考察三个变量Pclass、Sex和Age,头等舱的年轻女性乘客现在将是最有可能生还的人。

然而,解释乘客票价的密度图可能会更难。对于“幸存”和“未幸存”两类,它们的跨度很大,其中“未幸存”类的均值和方差较小。请注意,在“幸存”等级的分布中有一个有趣的尾巴,它对应于三个人每人获得 512 美元的头等舱机票。他们都是在瑟堡港上船的,所有人都活了下来。

另一方面,出发港似乎也在决定谁将幸存的问题上扮演了一个角色。大多数人在南安普顿港上船——旅程的第一站,他们的存活率最低。也许他们被分配到离出口更远的船舱,或者在游轮上花更多的时间会让人放松或疲惫。或者,这可能只是由第三个变量间接导致的——比如说,在第一个港口上船的妇女/儿童/头等舱乘客可能更少。需要进一步调查。

如果你喜欢表格而不是图表,我们也可以通过pandas.DataFrame.groupby()将数据可视化,并取每一类的*均值。但是,我觉得下面的Parch的表格中并没有一个清晰的模式显示出来。
****
****由seaborn.heatmap()生成的相关矩阵说明了任意两个变量之间的相关强度。如您所见,Sex与Survived的相关性最高,而Fare与Pclass的相关性最高。SibSp和Parch似乎在预测一个人的生存机会方面没有太大作用,尽管我们的本能表明情况并非如此。
3.缺失数据插补
我们之前在数据检查中发现有很多缺失数据条目。例如,我们似乎不知道一位 60 岁的托马斯·斯托里买票花了多少钱。直觉告诉我们,机票价格在很大程度上取决于机票等级和出发港口,我们可以用上面的相关矩阵进行交叉检查。因此,我们将只取南安普敦三等舱票价的*均值。这只是一个有根据的猜测,可能是错误的,但已经足够好了。请记住,不可能有无噪声的数据,机器学习模型应该对噪声具有鲁棒性。

还有两个女人,我们不知道她们是从哪里上船的。这应该与机票等级和票价密切相关。因为他们都花了 80 美元买了一张头等舱的票,所以我把我的钱押在瑟堡(图中的 C)。
如果某个特定变量中只有几个缺失条目,我们可以使用上面的技巧,通过基本上采用 最大似然 值来进行有根据的猜测。尽管如此,如果我们有更多丢失的数据,做同样的事情真的很危险,就像在Age中,大约有 20%的数据丢失。
我们再也不能通过观察做出有根据的猜测了。因为我们丢弃了变量Cabin,并且所有其他缺失的条目都被填充,所以我们可以利用所有其他变量通过随机森林回归来推断缺失的Age。有 80%的‘训练’数据来推断剩下的 20%。
4.特征工程
虽然大多数人都有“先生”、“夫人”和“小姐”的头衔,但也有不少不太常见的头衔——“博士”、“牧师”、“上校”等。有些只出现一次,如“Lady”、“DOA”、“Captain”等..他们罕见的出现对模特训练没什么帮助。为了用数据科学找到模式,你需要数据。一个基准点没有任何模式。让我们把所有那些相对罕见的标题归类为“罕见”。
分类数据在模型训练之前需要格外小心。分类器无法处理字符串输入,如“Mr”、“Southampton”等..虽然我们可以把它们映射成整数,比如说('先生','小姐','夫人','稀有')→ (1,2,3,4),但是头衔之间不应该有排序的概念。 成为博士并不能让你高人一等 。为了不误导机器,不小心构建了一个性别歧视的人工智能,我们应该对它们进行一次性编码。它们变成了:
( (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1) )
另一方面,我决定再添加两个变量— FamilySize和IsAlone。加上FamilySize = SibSp + Parch + 1更有意义,因为全家人会一起待在游轮上。你不想和你的伴侣在一起,但却抛弃你的父母,是吗?此外,独处可能是关键因素之一。你可能更容易做出鲁莽的决定,或者你可能更灵活,不需要照顾家庭。通过一次添加一个变量,我发现它们在模型中的出现提高了整体的可预测性。

Alex Siale 在 Unsplash 上拍摄的照片
5.模型评估
我尝试了我所知道的最流行的分类器——随机森林、SVM、KNN、AdaBoost 等等。XGBoost 最终以 87%的测试准确率脱颖而出。为了增加我们的分类器的鲁棒性,训练具有不同性质的分类器的集成,并且通过多数投票获得最终结果。
最后,我提交给 Kaggle,并取得了 80% 的准确率。还不错。总有改进的余地。
例如,在Cabin和Ticket中肯定有一些有用的信息隐藏在中,但是为了简单起见,我们省略了它们。我们还可以创建更多的特征,例如一个二进制类Underage,如果Age < 18为 1,否则为 0。
但是我现在要继续前进了。
6.作为 Web 应用程序部署
Flask 是 Python 中一个易于使用的 web 框架。
from flask import Flask
app = Flask(__name__)[@app](http://twitter.com/app).route("/")
def hello():
return "<h1>Write something here.</h1>"app.run(host='0.0.0.0', port=60000)
瞧!您可以在本地主机中浏览它。

安德鲁·弗尔兰在 Unsplash 上拍摄的照片
我们还需要什么?我们希望人们填写表格来收集所需的数据,并将其传递给机器学习模型。该模型将有一个输出,我们将用户重定向到该页面。
我们将使用 WTForms 在 Python 中构建一个表单,单个表单由一个类定义,如下图所示:
from wtforms import Form, TextField, validators, SubmitField, DecimalField, IntegerField, SelectFieldclass ReusableForm(Form):sex = SelectField('Sex:',choices=[('1', 'Male'), ('0', 'Female') ],
validators=[validators.InputRequired()])fare = DecimalField('Passenger Fare:',default=33,places=1,
validators=[validators.InputRequired(),
validators.NumberRange(min=0,
max=512,
message='Fare must be between 0 and 512')])submit = SubmitField('Predict')
我从 Will Koehrsen 那里找到了一个 HTML 模板,并在它的基础上构建。
你看看那些在你之前的人,那些以前处理过这个问题的人,那些建立了意大利面条代码的人,你非常感谢他们使它开源,你给他们荣誉,你把他们做的东西放进你的程序里,你永远不会再看它。—汤姆·斯科特
7.云托管
现在可以通过我的本地主机查看网页了,一切正常。最后一步是把它放在网上。现在有 3 个主要的云托管服务——AWS、GCP 和 Azure。AWS 是目前最受欢迎的,所以我选择了它的 12 个月免费版。
我用我的私钥连接到 Linux 服务器实例,将我的存储库迁移到服务器,运行我的脚本,它工作了!

嗯…那对我来说不太好…

相关文章
感谢您的阅读。你可以注册我的时事通讯来接收我的新文章的更新。如果您对数据科学感兴趣,以下文章可能会有用:
** [## 我希望我能早点知道的 5 个 Python 特性
超越 lambda、map 和 filter 的 Python 技巧
towardsdatascience.com](/5-python-features-i-wish-i-had-known-earlier-bc16e4a13bf4) [## Python 3.8 中针对 Python 新手的 6 项新特性
请做好准备,因为 Python 2 不再受支持
towardsdatascience.com](/6-new-features-in-python-3-8-for-python-newbies-dc2e7b804acc) [## Python 初学者应该避免的 4 个常见错误
我很艰难地学会了,但你不需要
towardsdatascience.com](/4-common-mistakes-python-beginners-should-avoid-89bcebd2c628)
最初发布于edenau . github . io。**
把你的头缠在梯度下降上(有漫画!)
为什么我们需要另一篇关于梯度下降的文章?
因为数学很难,我们中的一些人需要更慢、更直观的方法。
TL;dr —没有 TL;博士我们在预测未来。描述细节而不使你愤怒退出需要几句话。
理解这篇文章的唯一前提是知道什么是线性函数。我们看到线性函数有许多不同的表述方式,但今天我将坚持使用“y = MX+b”,因为它可能是应用最广泛的。
如果我问“函数 y = mx + b 的变量是什么?”,你可能会说“ x ”。
通常,你是对的,但是梯度下降的第一个“问题”是它是反向的。
不是“给定这个线性函数,找到这些数据点”,而是说“给定这些数据点,找到线性函数”
让它深入人心,因为它超级怪异。
把 x 和 y 更像是一堆常数。我们需要找到符合这些点的“ m ”和“ b ”的值。然后,我们可以用这条线来预测新的点。

如果看手相的人教会了我们什么的话,预测未来并不是一门精确的科学。一般来说,没有一条线能完美地穿过这些点,所以我们想找出哪条线最接*到达这些点。

什么是“紧密”配合?
这似乎是一个愚蠢的问题。看起来你应该能够测量每个点离直线有多远,然后除以点的数量来计算出直线离每个点有多*。
然而,考虑下面两行,问问自己哪一行更好地代表了这两个数据点:

第一条线向底部倾斜。第二条线直接在数据点之间分开。第二行感觉更准确地表示了数据,但*均误差是一样的:

由于这个原因和其他原因,我们通常用距离直线的*方来衡量一个数据点的“接*度”。这也确保了只有一个“最佳”位置。

通过使用*均*方误差,我们可以看到,将线放在中间比放在顶部更好。
太好了,我怎么找到*均*方误差最小的地方?
这有几种方法。最简单的方法叫做普通最小二乘法,但是在复杂问题上表现不佳。为了解决复杂的问题,我们经常使用一种叫做“梯度下降”的方法。很多机器学习都建立在这个概念上。
首先,让我们假设等式中没有" mx ",而只是试图为" b "找到好的值。这更简单,因为你只有一个变量需要求解。
y = b
这听起来可能很荒谬。这意味着“ m ”锁定在零(一条水*线)。不管你得到什么输入,你总是要猜测同样的输出。

照片来自 Oleg Magni 在 pxhere 上
稍等一下。很快就会变得疯狂。
为简单起见,假设我们只有两个数据点。这些数据点代表了最*的房屋销售。我们知道每栋房子的面积和售价。
我准备把这些数据点称为“东方先生的房子”和“西方先生的房子”(因为一个更靠*图形的东侧,另一个更靠*西侧)。

我们试图找到一条线 y = b ,使 East 先生的房子和 West 先生的房子的*均*方误差最小。
我知道你在想什么。“哦……就拿*均销售价”。
b = 7.5

你说得对。这种方法是可行的,它可以最小化*方误差。然而,这不是梯度下降。这个可爱的“*均”小技巧不适用于更复杂的问题,所以让我们用梯度下降法来求这个值。
在梯度下降中,我们首先将线放在一个随机的点上。我要把它放在这里:

我们希望这条线尽可能靠*所有的房子。所以我们对附*进行了调查。我们敲开每一扇门,并问“我们应该把线移到哪个方向?”。我将称之为移动调查:
移动调查#1
叩叩叩

左图由 Brett Sayles 在 pexels 上拍摄。右图来自 pixnio
移动调查后,投票是一致的,所以我们把线向上移动。假设我们将其移动到 7.1

现在我们进行另一项运动调查:
移动调查#2
叩叩叩

现在越来越有趣了。韦斯特先生想把线下移,而伊斯特先生想把线上移。想象一下这些邻居在拔河(每个人都希望线离他们的财产更*)。

照片来自 pxhere
那么我们如何决定把线移到哪里呢?
站在房主的角度想想这个问题:
- 如果你的属性正好在线上,你就没有*方误差。
- 现在,如果我们把线从你的财产上拉开 1 格,你的*方误差是 1 (1)。
- 如果我们将线拉离你的财产另一个空间,你的*方误差是 4 (2)。
- 如果我们把线拉离你的财产另一个空间,你的*方误差是 9 (3)。
如此处所示,每次搬家都比上一次更贵。

错误在*距离非常便宜,在远距离就变得非常昂贵。
看着这两栋房子:
- 韦斯特先生接*错误是廉价的那条线。
- 伊斯特先生远离错误代价高昂的前线。

当你考虑权衡的时候,给 West 先生加上一些便宜的误差是值得的,这样我们就可以从 East 先生身上减去昂贵的误差。

东方先生对变化非常敏感。我的意思是,把线从 East 先生那里移开会让我们花费很多,而把线移到离 East 先生更*的地方会让我们节省很多。
相比之下,韦斯特先生对变化不敏感。将战线移向韦斯特先生,我们并没有得到多少好处。将生产线从韦斯特先生那里移开也花费较少。
我们的调查没有考虑“敏感性”。我们应该修改调查。我们需要问的不仅仅是"我们应该把线往什么方向移动?“但是”你对线条运动有多敏感?

我们将结果写在我们的移动调查中:

一旦我们调查了所有居民,我们将总敏感度除以居民人数,得出“*均敏感度”:

这里的正*均敏感度意味着,如果我们把线上移,利大于弊。*均灵敏度(0.4)告诉我们上下权衡的强度。
我们从这个数字中抽取一小部分(比如说 25%)。我们把这个百分比叫做“学习率”。
*0.4 .25 = 0.1
然后我们把线移动那么多。

回到拔河的类比,你可以想象东方先生和西方先生在往相反的方向拉,但是东方先生远比西方先生强(因为他更敏感)。

线现在是 7.2,我们做另一个调查,再次问:
- ”我们应该向什么方向移动这条线?
- "你对线条运动有多敏感?”

我们可以看到,西先生比以前敏感了,东先生比以前不敏感了。
伊斯特先生的敏感性仍然超过韦斯特先生,但没有以前那么多了。
让我们用这个数字的 25%来更新这一行:
.3 * .25 = .075

该线现在位于 7.275。移动的幅度比以前更小了。
如果我们继续进行多轮调查,你会发现韦斯特先生越来越敏感,而伊斯特先生越来越不敏感。动作也越来越小。
随着更多回合的到来,拉锯战变得更加势均力敌。伊斯特先生希望这条线向上延伸,就像韦斯特先生希望这条线向下延伸一样。此时,“*均灵敏度”非常小(两个灵敏度实际上相互抵消)。
随着*均灵敏度接*零(*衡),线停止移动,因为线更新是基于*均灵敏度的。

我将向您展示 3 个不同的动画,它们都显示了直线从第一次测量(7.1)移动到*衡(7.5)时的梯度下降。这三个视图应该可以帮助你理解这个过程。
视图#1:生产线移动视图
以下动画展示了每次测量后的直线移动。当线接* 7.5(最小误差点)时,你可以看到波动越来越小。

视图#2:单个错误视图
这个动画展示了东方先生和西方先生的错误。你可以看到,我们继续用昂贵的错误换取便宜的错误,直到双方都不便宜。

仔细想想,这是有道理的。只有当你得到的比放弃的多的时候,你才想要移动这条线。当*均灵敏度为零时,向任一方向移动直线都没有好处(因为两边的灵敏度相同)。
视图#3:梯度下降视图
当谈到梯度下降时,你会看到最后一种类型的图表被多次引用。这一点可能很难理解。

该图与前面的图具有相同的形状,但不要被欺骗,它是非常不同的。在上图中,X 轴是误差(每个误差是一个单独的点),Y 轴是误差的*方。
在这张新图表中:
- X 轴显示了' b 的不同值。
- 当' b '设置为该值时,Y 轴是*均*方误差。
我们可以看到,理想的' b '值为 7.5,产生 0.25 的*均*方误差。
我们在梯度下降中的任务是找到这条曲线底部的点(最小*方误差)。
当我们开始(在 7.1),并继续做移动调查,我们在几轮后接*底部。点击此处观看动画:

在我们开始梯度下降之前,我们知道曲线会这样形成(它总是这样),但是我们不知道最小值在哪里。
在第一次移动调查后,我们发现猜测值为 7.1 会导致*均*方误差为 0.41。起初,这似乎是我们所知道的全部:

然而,我们实际上知道的更多。我们知道*均灵敏度是 0.4,这告诉我们两件事:
- 居民希望这条线上移(这样我们就知道我们在曲线的左侧)。
- *均灵敏度很高,所以我们离中心很远。
我将标记一条蓝线,显示我们对自己所处位置的了解。

然后,我们做一个秒移动调查,我们发现 7.2 的猜测导致 0.34 的*均*方误差。
由于*均敏感度(. 3),我们知道我们仍然在图表的左侧,但我们越来越接*中心。我将画另一条线来表示这一点,但这条线不会太陡,因为我们更接*底部。

随着我们继续做移动调查,我们的步伐越来越小,越来越接*曲线的底部。

蓝线的“陡度”代表*均灵敏度。当' b 值远离最小值时,斜率很陡。
随着' b '值接*最小值,*均灵敏度(也称为斜率)接*零。
“G radient ”只是“斜率”的另一种说法。随着我们向图表的底部移动,梯度变得越来越小。这就是为什么我们称这种方法为“梯度下降”。
无论你有两个数据点(如此处所示),还是 1000 个数据点。梯度下降的逻辑是相同的:
- 调查每个点,询问“您希望我向哪个方向移动?”和“你有多敏感?
- 取所有灵敏度(梯度)的*均值。
- 乘以“学习率”
- 更新该行。
- 重复直到我们找到*衡。
实际上,你永远不会完全达到*衡,因为当它接*底部时,更新变得越来越小,但是在足够多的回合之后,差异将变得微不足道。
好的,我们可以解出 b。“m”呢?
现在我们已经发现了如何找到最小的“b”值,让我们暂时忽略“b ”,想想如何找到最小的“m”值。
y = mx
这意味着“ b ”实际上被固定在常数 0。我们可以使用“ m ”值来改变函数的斜率,但是直线将总是通过(0,0)

让我们再来看看 East 先生和 West 先生,猜一猜这条线可能在哪里:

在 m=1 处,我们看到 West 先生在线上 2 个单位,East 先生在线下2 个单位。*均*方误差为 4。
直觉上,我们似乎处于*衡状态,无法在此基础上有所提高。然而,事实并非如此。
下面的动画显示了如果我们将“ m ”值从 1 更改为 0.9 会发生什么

请注意,我们是如何在离 East 先生更*的地方得到 1 个完整的空间,而在离 West 先生更远的地方只得到 1/2 的空间。总*方误差减少到 3.625
当你认为等式 y = mx 是有意义的
将“m”值乘以“ x ”。因为东先生( x=10 )的距离是西先生( x=5 )的两倍,所以东先生受“ m 变化的影响也是西先生的两倍。
一般来说,越往东,对坡度变化越敏感。

既然我们向 East 先生移动可以得到两倍的收益,为什么不把线一直移动到 East 先生呢?我们可以向东方先生移动整整 2 个单位,而向西方先生只移动 1 个单位。

因为我们离韦斯特先生有 3 个空间,新的*均*方误差是(4.5)。这比我们开始的时候更糟…怎么回事?
记住:当我们把线从 West 先生移开时,每个误差都比上一个更昂贵(由于*方误差)。在某些时候,韦斯特先生对运动非常敏感,以至于不值得做出权衡,尽管你可以通过移动线来获得两倍于伊斯特先生的距离。
虽然上面的线有最小的原始误差,但它没有最小的*方误差。
因此,东先生和西先生都很敏感,但原因不同。事实证明,点对斜率变化的敏感度由两个因素决定:
- 点离线有多远?
- 这个点向东有多远?
当我们计算灵敏度时,我们需要考虑这两个因素。
灵敏度= {与直线的距离} * {x 值}
让我们把这条线放回( m = 0.9)

本帖第一次,你很可能无法通过简单的看一眼就判断出线条应该往哪个方向移动。
我们来做个移动调查,计算一下敏感度,就知道了。

我们可以看到,尽管 West 先生只有“ x ”值的一半,但由于他与生产线的距离,他更加敏感。
我们知道斜率需要向上移动,但是增加 1.25 比 T21 大得多,会把线放在两点之上。这就是为什么我们只移动它的一个百分比(我之前描述的“学习率”)。这里我用 1%的学习率。
1.25 * .01 = .0125
新的“m”= . 9+. 0125
新线在. 9125。在另一次移动调查后,我们可以看到*方误差有所下降。

乘以学习率以获得更新值:
0.46875 * .01 = .0046875
新的“m”值= .9125 + .0046875
更新后线路在. 917。如果我们继续一轮又一轮的移动调查,这条线会越来越接*它的*衡点(. 92)。
这是 0.92 时的移动调查结果

梯度下降过程看起来非常类似于我们看到的“ m ”的梯度下降。
梯度下降视图
让我们看看坡度变化的梯度体面视图:

我们可以看到,当“ m ”值为 0.9 时,*方误差最小。
关于“ m ”曲线需要注意的一点是,它比我们看到的“ b ”的曲线要陡得多。这是因为" m "值乘以我们的每个" x "值,所以即使对" m "的小更新也会导致大的变化(更好或更坏)。这就是为什么保持小的学习率很重要。
把所有的放在一起
首先,我们想出了当“ m ”保持不变时,如何求解“ b ”。然后,我们想出了当“ b ”保持不变时,如何求解“ m ”。我们如何移动“ m ”和“ b ”来找到最佳的整体匹配?
当“ m 和“ b 都可以移动时:
- 假设“ m ”不变。进行移动调查,询问所有居民他们希望“ b ”值向哪个方向移动。
- 假设“ b ”不变。进行移动调查,询问所有居民他们希望“ m ”值移动到哪个方向。
- 根据“ b ”移动调查的*均灵敏度更新“ b ”值。
- 根据“ m ”移动测量的*均灵敏度更新“ m ”值。
- 只有一个地方“ m ”和“ b ”的灵敏度都为零。这条线将会聚在那一点。
完整的例子
让我们来看一下当水*线位于( y = 0x + 7.5”),并进行全梯度下降时会发生什么。

第一轮:
- 在“ b ”移动调查中,我们发现 East 先生和 West 先生对“ b ”的变化同样敏感(他们都相距 0.5)。“ b 的*均灵敏度为零。
- 在“ m ”移动调查中,我们发现虽然两人距离相同,但 East 先生对“ m ”变化的敏感度是他的两倍,因为他有两倍的“ x ”值。
- “ b ”值没有更新,因为没有*均“ b ”灵敏度。
- 我们将“ m ”值更新为*均“ m ”灵敏度的 1%。
该线现在为" y = 0.0125x + 7.5"
第二轮:
- 这一次,韦斯特先生比伊斯特先生离这条线更远。这意味着他对“ b ”的变化更加敏感。韦斯特先生比较强势,想把“ b ”值拉下来。
- 在“ m ”移动调查后,我们发现 East 先生仍然对“m”的变化更敏感,并希望将“ m ”的值拉高。
- 我们将“b”值向下移动*均“ b ”灵敏度的 25%。
- 我们将“ m ”值向上移动*均“ m ”灵敏度的 1%。
随着我们继续将“ m ”和“ b ”移向*衡,我们接*“ m ”和“ b ”都为零(最小值)的唯一地方。
让我们从两个角度来总结一下。
直线运动视图
以下动画显示了梯度下降的重复循环,更新了“ b 和“ m ”。观察“ b ”值如何下降,以及“ m ”值如何增加。

梯度下降视图
让我们从“梯度下降”的角度来看正在发生的事情。到目前为止,我们已经展示了两张“梯度下降”图。
- " b "值与均方差。
- m 值与均方差。
为了显示" m " "和 " b "与均方误差的关系,我们需要图表上的第三个轴。
两个水*轴将代表“ m 和“ b ”的值。纵轴代表这些点的*方误差。
3 轴梯度下降图通常看起来像这样。这个图表不是基于我们的数据,而是帮助你理解这个概念。

该图有助于显示“ m ”和“ b ”都处于*衡状态的地方只有一个(底部)。
这是基于我们实际数据的梯度下降图。看起来有点困难,但是它有相同的“下坡到最小值”属性:

如果您仔细观察这个图表,您可以看到最小点的" m "值为 6,而" b "值为 0.2。
直线" y = 0.2x + 6 "实际上是理想的函数,结果是零误差(它穿过两点)。
该图与之前的图有所不同,原因如下:
- “ m ”侧的变化比“ b ”侧的变化要敏感得多,使图形呈现出这种独特的“taco”形状。
- 对于“ b ”的每个值,最小可能的“ m ”值会有一点变化。这就是为什么你看到的浅槽同时向“ m ”和“ b ”方向移动。
关于作者
我是约翰尼·伯恩斯,FlyteHub.org 的创始人,这是一个免费开源工作流程库,可以在没有编码的情况下执行机器学习。我相信在人工智能上的合作会带来更好的产品。
如果你对数学如何支持我们的“敏感度”公式感兴趣,我会写一篇后续文章来解释。
用这 10 个技巧写出更好的 Python 代码
提高您的 Python 技能
学习如何用 Pythonic 的方式编码

编码很有趣,用 Python 编码更有趣,因为有许多不同的方法来完成相同的功能。然而,大多数时候,都有首选的实现,有些人称之为 Pythonic。这些 Pythonic 实现的一个共同特点是代码简洁明了。
用 Python 或任何编码语言编程都不是火箭科学,它主要是关于制作技能。如果您有意尝试 Pythonic 编码,这些技术将很快成为您的工具包的一部分,并且您会发现在您的项目中使用它们会越来越自然。因此,让我们来探索一些简单的技巧,希望对你有所帮助。
1.负索引
人们喜欢使用序列,因为我们知道元素的顺序,并且我们可以按顺序操作这些元素。在 Python 中,字符串、元组和列表是最常见的序列数据类型。我们可以使用索引来访问单个项目。像其他主流编程语言一样,Python 支持基于 0 的索引,其中我们使用一对方括号中的 0 来访问第一个元素。此外,我们还可以使用 slice 对象来检索序列中的特定元素,如下面的代码示例所示。
>>> # Positive Indexing
... numbers = [1, 2, 3, 4, 5, 6, 7, 8]
... print("First Number:", numbers[0])
... print("First Four Numbers:", numbers[:4])
... print("Odd Numbers:", numbers[::2])
...
First Number: 1
First Four Numbers: [1, 2, 3, 4]
Odd Numbers: [1, 3, 5, 7]
然而,Python 更进了一步,支持负索引。具体来说,我们可以使用-1 来引用序列中的最后一个元素,并对项目进行反向计数。例如,倒数第二个元素的索引为-2,依此类推。重要的是,负索引也可以与切片对象中的正索引一起工作。
>>> # Negative Indexing
... data_shape = (100, 50, 4)
... names = ["John", "Aaron", "Mike", "Danny"]
... hello = "Hello World!"
...
... print(data_shape[-1])
... print(names[-3:-1])
... print(hello[1:-1:2])
...
4
['Aaron', 'Mike']
el ol
2.检查容器的空度
容器是指那些可以存储其他数据的容器数据类型。一些常用的内置容器是元组、列表、字典和集合。当我们处理这些容器时,我们经常需要在执行附加操作之前检查它们是否包含任何元素。事实上,我们可以检查这些容器的长度,它对应于存储项目的数量。当长度为零时,容器为空。下面给你看一个简单的例子。
if len(some_list) > 0:
# do something here when the list is not empty
else:
# do something else when the list is empty
然而,这并不是最有效的方法。相反,我们可以简单地检查容器本身,它将在包含元素时计算True。虽然下面的代码向您展示了主要的容器数据类型,但是这种用法也适用于字符串(即任何非空字符串都是True)。
>>> def check_container_empty(container):
... if container:
... print(f"{container} has elements.")
... else:
... print(f"{container} doesn't have elements.")
...
... check_container_empty([1, 2, 3])
... check_container_empty(set())
... check_container_empty({"zero": 0, "one": 1})
... check_container_empty(tuple())
...
[1, 2, 3] has elements.
set() doesn't have elements.
{'zero': 0, 'one': 1} has elements.
() doesn't have elements.
3.用 Split()创建字符串列表
我们经常使用字符串作为特定对象的标识符。例如,我们可以使用字符串作为字典中的关键字。在数据科学项目中,字符串通常是数据的列名。当我们选择多个列时,我们不可避免地需要创建一个字符串列表。事实上,我们可以在列表中使用文字创建字符串。然而,我们必须用引号将每个字符串括起来,这对于我们这些“懒人”来说有点乏味。因此,我更喜欢利用字符串的split()方法创建一个字符串列表,如下面的代码片段所示。
>>> # List of strings
... # The typical way
... columns = ['name', 'age', 'gender', 'address', 'account_type']
... print("* Literals:", columns)
...
... # Do this instead
... columns = 'name age gender address account_type'.split()
... print("* Split with spaces:", columns)
...
... # If the strings contain spaces, you can use commas instead
... columns = 'name, age, gender, address, account type'.split(', ')
... print("* Split with commas:", columns)
...
* Literals: ['name', 'age', 'gender', 'address', 'account_type']
* Split with spaces: ['name', 'age', 'gender', 'address', 'account_type']
* Split with commas: ['name', 'age', 'gender', 'address', 'account type']
如上所示,split()方法默认使用空格作为分隔符,并从字符串创建一个字符串列表。值得注意的是,当您创建一个包含一些包含空格的元素的字符串列表时,您可以选择使用不同类型的分隔符(例如,逗号)。
这种用法受到一些内置功能的启发。例如,当你创建一个命名的元组类时,我们可以这样做:Student = namedtuple(“Student”, [“name”, “gender”, “age”])。字符串列表指定了元组的“属性”然而,通过这样定义该类,它本身也得到支持:Student = namedtuple(“Student”, “name gender age”)。再举一个例子,创建一个枚举类支持相同的替代解决方案。
4.三元表达式
在许多用例中,我们需要根据条件定义具有特定值的变量,我们可以简单地使用 if…else 语句来检查条件。然而,它需要几行代码。如果我们只处理一个变量的赋值,我们可能想要使用三元表达式,它只需要一行代码就可以检查条件并完成赋值。此外,它的形式更短,这使得代码更加简洁。考虑下面的例子。
# The typical way
if score > 90:
reward = "1000 dollars"
else:
reward = "500 dollars"# Do this instead
reward = "1000 dollars" if score > 90 else "500 dollars"
有时候,我们可以从一个已定义的函数中获取一些数据,我们可以利用这一点,写一个三元表达式的速记运算,如下所示。
# Another possible scenario
# You got a reward amount from somewhere else, but don't know if None/0 or not
reward = reward_known or "500 dollars"
# The above line of code is equivalent to below
reward = reward_known if reward_known else "500 dollars"
5.文件对象的 With 语句
我们经常需要从文件中读取数据,并向文件中写入数据。最常见的方法是简单地使用内置的open()函数打开一个文件,它创建了一个我们可以操作的文件对象。您以前遇到过以下问题吗?
>>> # Create a text file that has the text: Hello World!
...
... # Open the file and append some new data
... text_file0 = open("hello_world.txt", "a")
... text_file0.write("Hello Python!")
...
... # Open the file again for something else
... text_file1 = open("hello_world.txt")
... print(text_file1.read())
...
Hello World!
在前面的代码片段中,我们从一个文本文件开始,该文件包含文本“Hello World!”然后,我们将一些新数据添加到文件中。然而,过了一会儿,我们又想在文件上工作了;当我们读取文本文件时,它仍然有旧的数据。换句话说,附加的文本不包括在文本文件中。为什么会这样?
这是因为我们没有首先关闭文件对象。如果不关闭文件,将无法保存更改。事实上,我们可以在 file 对象上显式调用close()方法。但是,我们可以使用“with”语句来实现这一点,它会自动为我们关闭 file 对象,如下所示。当我们完成对文件的操作时,我们可以通过访问 file 对象的closed属性来验证文件是否被关闭。
>>> with open("hello_world.txt", "a") as file:
... file.write("Hello Python!")
...
... with open("hello_world.txt") as file:
... print(file.read())
...
... print("Is file close?", file.closed)
...
Hello World!Hello Python!Hello Python!
Is file close? True
更一般地说, with 语句是在 Python 中使用上下文管理器的语法。前面的例子涉及文件操作,因为文件是共享资源,我们负责释放这些资源。上下文管理器可以帮助我们完成工作。如前所示,文件操作结束后,通过使用带有语句的自动关闭文件。你可以在我的上一篇文章中了解更多关于上下文管理的内容。
6.评估多个条件
我们经常需要评估多种情况。有几种可能的情况。对于数值,我们可以对同一个变量进行多次比较。在这种情况下,我们可以将这些比较链接起来。
# Multiple Comparisons
# The typical way
if a < 4 and a > 1:
# do something here# Do this instead
if 1 < a < 4:
# do somerthing here
在其他一些场景中,我们可以进行多重等式比较,并且我们可以利用下面的技术,使用关键字中的进行成员测试。
# The typical way
if b == "Mon" or b == "Wed" or b == "Fri" or b == "Sun":
# do something here# Do this instead, you can also specify a tuple ("Mon", "Wed", "Fri", "Sun")
if b in "Mon Wed Fri Sun".split():
# do something here
另一种技术是使用内置的all()和any()函数来评估多个条件。具体来说,当 iterable 中的元素都是True时,all()函数的计算结果将是True,因此该函数适合代替一系列的 and 逻辑比较。另一方面,当 iterable 中的任何元素为True时,any()函数将计算为True,因此适合替换一系列 OR 逻辑运算。相关的例子如下。
# The typical ways
if a < 10 and b > 5 and c == 4:
# do somethingif a < 10 or b > 5 or c == 4:
# do something# Do these instead
if all([a < 10, b > 5, c == 4]):
# do somethingif any([a < 10, b > 5, c == 4]):
# do something
7.在函数声明中使用默认值
在几乎所有的 Python 项目中,大部分代码都涉及到创建和调用函数。换句话说,我们不断地处理函数声明和重构。在许多情况下,我们需要多次调用一个函数。根据不同的参数集,该函数的运行方式会略有不同。然而,有时一组参数可能比其他参数更常用,在这种情况下,我们应该考虑在声明函数时设置默认值。考虑下面这个微不足道的例子。
# The original form:
def generate_plot(data, image_name):
"""This function creates a scatter plot for the data"""
# create the plot based on the data
...
if image_name:
# save the image
...# In many cases, we don't need to save the image
generate_plot(data, None)# The one with a default value
def generate_plot(data, image_name=None):
pass# Now, we can omit the second parameter
generate_plot(data)
需要注意的一点是,如果在设置默认值时处理可变数据类型(例如,列表、集合),请确保使用None而不是构造函数(例如,arg_name=[])。因为 Python 在定义函数对象的地方创建函数对象,所以提供空列表会被函数对象“卡住”。换句话说,函数对象不会在你调用它的时候被创建。相反,您将在内存中处理同一个函数对象,包括它最初创建的默认可变对象,这可能会导致意想不到的行为(更多讨论见。
8.使用计数器进行元素计数
当我们在一个列表、元组或字符串中有多个项目时(例如,多个字符),我们经常想要计算每个项目有多少个。要做到这一点,可以为该功能编写一些乏味的代码。
>>> words = ['an', 'boy', 'girl', 'an', 'boy', 'dog', 'cat', 'Dog', 'CAT', 'an','GIRL', 'AN', 'dog', 'cat', 'cat', 'bag', 'BAG', 'BOY', 'boy', 'an']
... unique_words = {x.lower() for x in set(words)}
... for word in unique_words:
... print(f"* Count of {word}: {words.count(word)}")
...
* Count of cat: 3
* Count of bag: 1
* Count of boy: 3
* Count of dog: 2
* Count of an: 5
* Count of girl: 1
如上所示,我们首先必须创建一个只包含唯一单词的集合。然后我们迭代单词集,并使用count()方法找出每个单词的出现次数。然而,有一个更好的方法——使用Counter类,它是为完成这个计数任务而设计的。
>>> from collections import Counter
...
... word_counter = Counter(x.lower() for x in words)
... print("Word Counts:", word_counter)
...
Word Counts: Counter({'an': 5, 'boy': 4, 'cat': 4, 'dog': 3, 'girl': 2, 'bag': 2})
collections模块中提供了计数器类。为了使用该类,我们简单地创建了一个生成器:x.lower() for x in words,并且每一项都会被计数。如你所见,计数器对象是一个类似 dict 的映射对象,每个键对应于单词列表的唯一项,而值是这些项的计数。很简洁,对吧?
此外,如果您对找出单词列表中最频繁出现的条目感兴趣,我们可以利用 Counter 对象的most_common()方法。下面的代码向您展示了这种用法。你只需要指定一个整数(N),它会从列表中找出最频繁出现的 N 个条目。顺便提一下,Counter对象也可以处理其他序列数据,比如字符串和元组。
>>> # Find out the most common item
... print("Most Frequent:", word_counter.most_common(1))
Most Frequent: [('an', 5)]
>>> # Find out the most common 2 items
... print("Most Frequent:", word_counter.most_common(2))
Most Frequent: [('an', 5), ('boy', 4)]
9.不同订单要求的排序
在许多项目中,对列表中的项进行排序是一项普遍的任务。最基本的排序是基于数字或者字母顺序,我们可以使用内置的sorted()函数。默认情况下,sorted()函数将按升序对列表(实际上,它可以是任何可迭代的)进行排序。如果我们将reverse参数指定为True,我们可以得到降序排列的条目。下面显示了一些简单的用法。
>>> # A list of numbers and strings
... numbers = [1, 3, 7, 2, 5, 4]
... words = ['yay', 'bill', 'zen', 'del']
... # Sort them
... print(sorted(numbers))
... print(sorted(words))
...
[1, 2, 3, 4, 5, 7]
['bill', 'del', 'yay', 'zen']
>>> # Sort them in descending order
... print(sorted(numbers, reverse=True))
... print(sorted(words, reverse=True))
...
[7, 5, 4, 3, 2, 1]
['zen', 'yay', 'del', 'bill']
除了这些基本用法,我们还可以指定key参数,以便对复杂的项目进行排序,比如元组列表。考虑以下这种情况的例子。
>>> # Create a list of tuples
... grades = [('John', 95), ('Aaron', 99), ('Zack', 97), ('Don', 92), ('Jennifer', 100), ('Abby', 94), ('Zoe', 99), ('Dee', 93)]
>>> # Sort by the grades, descending
... sorted(grades, key=lambda x: x[1], reverse=True)
[('Jennifer', 100), ('Aaron', 99), ('Zoe', 99), ('Zack', 97), ('John', 95), ('Abby', 94), ('Dee', 93), ('Don', 92)]
>>> # Sort by the name's initial letter, ascending
... sorted(grades, key=lambda x: x[0][0])
[('Aaron', 99), ('Abby', 94), ('Don', 92), ('Dee', 93), ('John', 95), ('Jennifer', 100), ('Zack', 97), ('Zoe', 99)]
上面的代码通过利用 lambda 函数向您展示了两个高级排序示例,该函数被传递给key参数。第一个是使用降序对项目进行排序,而第二个是使用默认的升序。如果我们想把这两个要求结合起来呢?如果你考虑使用reverse参数,你可能找错了对象,因为如果你试图按多个标准排序,相反的参数将适用于所有标准。那有什么诀窍呢?请参见下面的代码片段。
>>> # Requirement: sort by name initial ascending, and by grades, descending
... # Both won't work
... sorted(grades, key=lambda x: (x[0][0], x[1]), reverse=True)
[('Zoe', 99), ('Zack', 97), ('Jennifer', 100), ('John', 95), ('Dee', 93), ('Don', 92), ('Aaron', 99), ('Abby', 94)]
>>> sorted(grades, key=lambda x: (x[0][0], x[1]), reverse=False)
[('Abby', 94), ('Aaron', 99), ('Don', 92), ('Dee', 93), ('John', 95), ('Jennifer', 100), ('Zack', 97), ('Zoe', 99)]
>>> # This will do the trick
... sorted(grades, key=lambda x: (x[0][0], -x[1]))
[('Aaron', 99), ('Abby', 94), ('Dee', 93), ('Don', 92), ('Jennifer', 100), ('John', 95), ('Zoe', 99), ('Zack', 97)]
如您所见,通过将reverse参数设置为True或False,两者都不起作用。相反,诀窍是否定等级,因此当您按默认升序排序时,分数将因为这些值的否定而反向排序。但是,对于这种方法有一个警告,因为求反只能处理数值,而不能处理字符串。
10.不要忘记 defaultdict
字典是一种有效的数据类型,它允许我们以键值对的形式存储数据。要求所有的键都是可散列的,这样在幕后,存储这些数据可能涉及到使用散列表。这种实现允许数据检索和插入的 O(1)效率。然而,应该注意的是,除了内置的 dict 类型之外,我们还可以使用其他的字典。其中,我想讨论一下 defaultdict 类型。与内置的 dict 类型不同, defaultdict 允许我们设置一个默认的工厂函数,当键不存在时创建一个元素。您可能对以下错误并不陌生。
>>> student = {'name': "John", 'age': 18}
... student['gender']
...
Traceback (most recent call last):
File "<input>", line 2, in <module>
KeyError: 'gender'
假设我们在处理单词,我们想将相同的字符组成一个列表,这些列表与作为键的字符相关联。这里有一个使用内置字典类型的简单实现。值得注意的是,检查 dict 对象是否有letter键至关重要,因为如果键不存在,调用append()方法会引发KeyError异常。
>>> letters = ["a", "a", "c", "d", "d", "c", "a", "b"]
... final_dict = {}
... for letter in letters:
... if letter not in final_dict:
... final_dict[letter] = []
... final_dict[letter].append(letter)
...
... print("Final Dict:", final_dict)
...
Final Dict: {'a': ['a', 'a', 'a'], 'c': ['c', 'c'], 'd': ['d', 'd'], 'b': ['b']}
让我们看看如何使用 defaultdict 来编写更简洁的代码。虽然这个例子很简单,但它只是给了你一些关于 defaultdict 类的想法,这样我们就不用处理字典对象中不存在的键了。
>>> from collections import defaultdict
...
... final_defaultdict = defaultdict(list)
... for letter in letters:
... final_defaultdict[letter].append(letter)
...
... print("Final Default Dict:", final_defaultdict)
...
Final Default Dict: defaultdict(<class 'list'>, {'a': ['a', 'a', 'a'], 'c': ['c', 'c'], 'd': ['d', 'd'], 'b': ['b']})
结论
在阅读本文之前,您可能已经知道了其中的一些技巧,但是我希望您仍然能够很好地掌握这些技巧。在您的项目中实践这些习惯用法将使您的 Python 代码更具可读性和性能。
这件作品到此为止。感谢阅读。
使用这个 Python 工具编写更好的故事
利用编程的力量写得更好,提高可读性。

如果你经常给老板写文章、博客或报告,那么你要确保人们理解你说的话。在这篇文章中,我将展示如何使用一个名为 textstat 的 Python 库来确定你的文本的可读性、复杂性、等级级别以及更多相关信息。
装置
您可以通过 Python 包索引(PyPI)轻松安装 textstat:
pip install textstat
您也可以从 GitHub 安装最新版本:
git clone [https://github.com/shivam5992/textstat.git](https://github.com/shivam5992/textstat.git)
cd textstat
pip install .
使用
该库中有许多有用的函数来:
- 数音节
- 执行词典计数
- 计算句子
- 运行 Flesch 阅读难易程度评分来评估文档的可读性。
- 运行弗莱施-金凯等级。这是一个评分公式,9.3 分意味着九年级学生能够阅读该文档。
- 返回雾标度。雾指数为 12 需要美国高中毕业生(大约 18 岁)的阅读水*。
- 返回雾霾指数。这是一种可读性的衡量标准,估计了理解一篇文章所需的教育年限。
- 返回自动可读性索引。它产生了理解文本所需的美国等级水*的*似表示。
- 返回科尔曼-廖指数。
- 返回 Linsear 编写的公式。
- 获得 Dale-Chall 可读性分数。
- 基于以上所有测试,获得可读性共识。
这个库的基本思想是让你写一篇文章,然后运行不同的测试和索引,看看你的文章有多好。我将在下面的例子中解释如何做。
例子
我将从我写的一篇旧文章中选取一段,然后对它进行不同的测试。文章是:
第一部分:将科学引入讨论的重要性。
towardsdatascience.com](/https-towardsdatascience-com-on-data-and-science-e96849b5f363)
我要用的文字是这样的:
数据科学是目前大多数科学和研究的主要焦点,它需要许多东西,如人工智能、编程、统计、商业理解、有效的演示技巧等等。所以才不容易理解,也不容易学习。但是我们能做到,我们正在做。
数据科学已经成为学术界和工业界解决问题的标准框架,而且这种情况还会持续一段时间。但是我们需要记住我们从哪里来,我们是谁,我们要去哪里。
在这个 GitHub repo 中,您将找到运行库的下一个笔记本:
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/FavioVazquez/improve-writing)
在创建这段代码的过程中,我还发现了一些有趣的事情。如何在 Python 中运行类的每个方法?我没有在网上找到它,所以这里是这个库的例子:
*# Run all at once*
**import** **inspect**
funcs = ["textstat." + inspect.getmembers(textstat, predicate=inspect.ismethod)[i][0] **for** i **in** range(1,28)]**for** elem **in** funcs:
method = eval(elem)
textstat.set_lang("en")
print(elem.split(".")[1])
print(method(text))
print(" ")
相当酷。但是缺少了一些东西,拼写呢?Python 中有很好的库,但是自动更正是最容易使用的:
# Here I'm misspelling :
# presentation as presentatio
# focus as focsu
# framework as framwork
text = """
Data science is the main focsu of most sciences and studies right now,
it needs a lot of things like AI, programming, statistics,
business understanding, effective presentatio skills and much more.
That's why it's not easy to understand or study. But we can do it, we are doing it.
Data science has become the standard solving problem framwork for academia and
the industry and it's going to be like that for a while. But we need to remember
where we are coming from, who we are and where we are going.
"""
正如你所看到的,我故意拼错了单词演示、焦点和框架,,我可以用这段简单的代码来修复它:
from autocorrect import Spellercheck = Speller(lang='en')check(text)
结果是:
"\ndata science is the main focus of most sciences and studies right now, \nit needs a lot of things like AI, programming, statistics, \nbusiness understanding, effective presentation skills and much more. \nThat's why it's not easy to understand or study. But we can do it, we are doing it.\ndata science has become the standard solving problem framework for academia and \nthe industry and it's going to be like that for a while. But we need to remember \nwhere we are coming from, who we are and where we are going.\n"
已经修好了。
如果你是一个 Python 用户,希望你可以用它来写更好的文章、报告等等。请在下面告诉我你的想法:)
你也可以通过 Instagram/Twitter 联系我:
Favio Vázquez 的最新推文(@FavioVaz)。数据科学家。物理学家和计算工程师。我有一个…
twitter.com](https://twitter.com/faviovaz)
写清楚你的简历,增加你找到第一份编码工作的机会
为什么开发人员应该花时间为他们的公共存储库写一个清晰的自述文件

在这篇文章中,我想和你分享我的一点观察,关于有多少的初级开发人员没有把自述文件放到他们的存储库中,没能说服我雇佣他们。
在文章末尾有一些关于如何撰写精彩自述的有用链接。
什么是 README?
通常,README 是软件/项目存储库中的一个文件,它简要地解释了它。
自述文件是用来推销你的作品的。
自述文件不是您的文档(除非它能被压缩到一页纸中)。
为什么要做呢?
如果你公开你的知识库,你很可能会被它评判。尤其是你在简历中用 GitHub/GitLab 账号申请新工作的时候。
在我的职业生涯中,我曾几次参与招聘实习生/初级软件工程职位。*均而言,我看到 7 个候选人中只有 1 个提到他们在 GitHub/GitLab 上的简介。这些候选者中很少有人会将自述文件附加到他们的存储库中。
我假设那些附上 GitHub 简介的候选人认为招聘人员/招聘工程师会钻研代码,弄清楚那里发生了什么。这种情况不会发生。其主要原因是:
- 他们没有时间做那件事
- 它们不是技术性的
- 即使他们是技术人员,他们也可能不知道你写的代码的语言/结构
因此,你可能有很强的编码技能,并且在你的投资组合中有一个不错的项目,但是如果没有一个自述文件来解释会发生什么,没有人会知道。
根本没有自述文件的缺点
当人们申请实习/初级职位时,他们通常没有什么可以证明他们是好的候选人。他们获得了教育背景。通常情况下,就是这样。所以,你的 Github 账号是你简历中最有价值的东西。
作为一名技术人员,当我看到没有自述文件的求职者信息库时,我会质疑他们的软技能水*。至少,我会认为你可以让你的作品不被记录。显然,它不会帮助您进入下一个处理级别。
糟糕的自述文件的缺点
你试图在自述文件中解释这一切,但(对我来说)很难理解。如果你申请初级职位,我会给你一分,但如果你想成为高级职位,这将是一个问题。
软技能对于高级工程师来说至关重要。传达任务,解释你的工作可能比你的编码技能更重要。
高级工程师必须能够清楚地解释他们构建了什么。
有一个清晰的自述文件的好处(对于你的简历)
- 你看起来很专业
- 招聘圈里的任何人都可以评估你的努力
- 你能够清楚地解释你建造了什么
如何做好自述?
0.创建一个简单明了的存储库名称
是的,我想仅仅通过名字就能猜出里面是什么。
通常,仅仅用存储库名称来解释您所做的一切是不可能的。相反,您应该只选择一个特性,并尝试使用它作为存储库名称。
1.简洁的一行程序
俏皮话很难。它必须吸引读者的注意力。
尝试用 60 个字符打动你的读者。
你的任务是让我思考:
“有意思,她是怎么做到的?我想进一步调查她的储存库”
2.演示一下
给我看看它是干什么的。有截图或者 GIF 就好了。
3.解释它的用途
哦是的。我们不仅仅是为了编码而编码。我们编码来解决问题。我们编码是为了给公司带来价值。所以,即使你只是因为喜欢才这么做的。我想让你解释它给特定类型的人/公司带来的价值。
4.推广它
现在,当你有一个清晰的自述时,试着引起某人的注意。获得一些反馈。弄些星星。
试着在黑客新闻或一些子网站上分享。
自述文件中最好包含的其他内容:
- 如何安装
- 快速启动
- 提及你用来创作作品的图书馆
结论
在有精心制作的自述文件的知识库中搜索你的第一份编码工作会增加你收到招聘人员回电的机会。
尽管如此,这篇文章是关于给你的简历加分的,仅仅有一份好的自述并不会让你成为一个好的候选人。
如何做一个别人想看的自述
About meMy name is Artem, I build [newscatcherapi.com](https://newscatcherapi.com/) - ultra-fast API to find news articles by any topic, country, language, website, or keyword.I write about Python, cloud architecture, elasticsearch, data engineering, and entrepreneurship.
学习如何在 Jupyter 笔记本上写下降价和乳胶
不仅是朱庇特。Google Colab、R Markdown 等等。

背景由JESHOOTS.COM于 Unsplash
交互式笔记本越来越受欢迎。为什么? 仅仅是因为它是一个很棒的教学环境,功能强大,可共享,并提供了在相同环境中执行数据可视化的能力。应该用哪些互动笔记本?我推荐:
- Jupyter Notebook是一个开源的 web 应用程序,允许您创建和共享包含实时代码、公式、可视化和叙述性文本的文档。
- Colaboratory 是一个免费的 Jupyter 笔记本环境,不需要设置,完全在云中运行。
他们两个都支持
- Markdown 是一种标记语言,是 HTML 的超集。
- 乳胶 渲染数学和科学的写法。
降价
这是一种非常简单的语言,它允许你以一种简化的方式编写 HTML。它可以用在一些网站上,比如 Stack Overflow,或者用来写文档(基本上是在 GitHub 上)。
Markdown 文件扩展名是。钔
当你在 Markdown 中写的时候,你使用缩短的符号,这些符号被相应的 HTML 标签所取代。每次,我都会告诉你相当于 Markdown 符号的 HTML,向你展示 Markdown 是如何使我们的生活比以往任何时候都更容易。
现在,甚至网络开发者也使用 Markdown,然后通过一些网站将其转换成 HTML。
标题
你用标签#制作标题。一个标签给你一个标题(h1),两个标签给你一个副标题(h2),依此类推,如下所示:
# Heading 1
## Heading 2
### Heading 3
#### Heading 4
##### Heading 5
###### Heading 6
HTML 等效:
输出结果: Colab 笔记本
段落
HTML 中的段落由<p>标签表示。在 Markdown 中,它们由一个或多个空行分隔。像 HTML 一样,空白被忽略。所以如果你增加 10 个空行,你仍然只有一个段落。
This is a paragraph of text.This is another paragraph of text.
HTML 等效:
输出结果: Colab 笔记本
换行
只需用两个或更多空格结束一行,然后键入 return。或者留一个空行。
This is a text. <!-- spaces -->
This is another text.
HTML 等效:
输出结果: Colab 笔记本
标出重点
您可以通过将文本加粗或倾斜来强调。
Emphasis, aka italics, with ***asterisks*** or **_underscores_**.Strong emphasis, aka bold, with ****asterisks**** or **__underscores__**.Combined emphasis with ****asterisks and _underscores_****.Strikethrough uses two tildes ~ . **~~Scratch this.~~**
HTML 等效:
输出结果: Colab 笔记本
列表
在 Markdown 中创建列表是一种真正的乐趣,你会发现没有什么比这更简单的了!
1\. Item 1
2\. Item 2 ( we can type 1\. and the markdown will automatically numerate them)
* First Item
* Nested item 1
* Nested item 2
1\. Keep going
1\. Yes
* Second Item
- First Item
- Second Item
HTML 等效:
输出结果: Colab 笔记本
链接和图像
要创建链接,必须将链接文本放在方括号中,后面是圆括号中的 URL。图像的插入方式几乎和链接一样,添加一个感叹号(!,后面是括号中的 alt 文本,括号中是图像资产的路径或 URL。
<!-- [Text](link) -->
[Link Text]([https://medium.com/@ahmedazizkhelifi](https://medium.com/@ahmedazizkhelifi) "Optional Title")<!--  -->
 "Optional Title")**<!-- Linking Image -->**
<!-- [](link) -->[ "Optional Title")]([https://medium.com/@ahmedazizkhelifi](https://medium.com/@ahmedazizkhelifi))
HTML 等效:
输出结果: Colab 笔记本
水*标尺
要创建水*线,请在一行中单独使用三个或三个以上的星号(***)、破折号(---)或下划线(___)。
Reading articles on Medium is awesome.
---
Sure !!
HTML 等效:
输出结果: Colab 笔记本
桌子
太简单了。而且你可以使用这个网站来生成它们。
在笔记本上的美元符号$前使用\,否则将进入数学显示模式(在 LaTeX 面查看)。
| Id | Label | Price |
|--- |----------| ------|
| 01 | Markdown |\$1600 |
| 02 | is | \$12 |
| 03 | AWESOME | \$999 |
HTML 等效:
输出结果: Colab 笔记本
代码和语法突出显示
```python
def staySafe(Coronavirus)
if not home:
return home
**HTML** 等效:
输出结果: [Colab 笔记本](https://colab.research.google.com/drive/18_2yFdH8G-6NXY_7fTcshMoScgJ-SYac#scrollTo=MQrtBJ3KH1Nj&line=7&uniqifier=1)
## 大宗报价
块引号的工作方式类似于对电子邮件的回复:您必须在引号前面加上一个`>`。
This is a blockquote.
This is part of the same blockquote.Quote break> This is a new blockquote.
**HTML** 等效:
输出结果: [Colab 笔记本](https://colab.research.google.com/drive/18_2yFdH8G-6NXY_7fTcshMoScgJ-SYac#scrollTo=52zLR8keH1A4&line=6&uniqifier=1)
# 乳液
> 你有没有问过自己,他们是如何用计算机写出复杂的数学和物理方程的?嗯,都是关于乳胶的。
Jupyter 笔记本使用 [MathJax](http://www.mathjax.org/) 在 HTML / Markdown 中渲染 LaTeX。把你的 LaTeX math 放在`$ $`里面就行了。或者通过在`$$ $$`之间书写进入*显示*数学模式。

输出结果: [Colab 笔记本](https://colab.research.google.com/drive/18_2yFdH8G-6NXY_7fTcshMoScgJ-SYac#scrollTo=fiZwHP0czJyT)
[**重要提示**](https://colab.research.google.com/drive/18_2yFdH8G-6NXY_7fTcshMoScgJ-SYac#scrollTo=1v3l2nNmNN7_) **:**
1. 使用`\,`在数学模式中添加**小间距**
2. 在数学模式下使用`\\`添加**新行**
3. 使用`\frac{arg 1}{arg 2}`显示**分数**
4. 对于**电源**(上标文本)使用`^{}`
5. 对于**索引**(下标)使用`_{}`
6. 对于**根**,使用`\sqrt[n]{arg}` `[n]`是可选的。

输出示例: [Colab 笔记本](https://colab.research.google.com/drive/18_2yFdH8G-6NXY_7fTcshMoScgJ-SYac#scrollTo=1v3l2nNmNN7_&line=7&uniqifier=1)
> *LaTeX 文件的扩展名是。特克斯*
## 希腊字母
要写希腊字母,输入`\`和字母名称:

输出结果: [Colab 笔记本](https://colab.research.google.com/drive/18_2yFdH8G-6NXY_7fTcshMoScgJ-SYac#scrollTo=go3imAWqE9au)
[**重要注意事项**](https://colab.research.google.com/drive/18_2yFdH8G-6NXY_7fTcshMoScgJ-SYac#scrollTo=VFaCoSXvS-_H&line=6&uniqifier=1) :
要写**大写希腊字母**,键入反斜杠`\`后的第一个大写,例如:
\delta >>> δ
\Delta >>> Δ\omega >>> ω
\Omega >>> Ω

输出示例: [Colab 笔记本](https://colab.research.google.com/drive/18_2yFdH8G-6NXY_7fTcshMoScgJ-SYac#scrollTo=VFaCoSXvS-_H)
如下图所示:

完整的希腊字母列表。[来源](http://tug.ctan.org/)
## 罗马名称:

输出结果: [Colab 笔记本](https://colab.research.google.com/drive/18_2yFdH8G-6NXY_7fTcshMoScgJ-SYac#scrollTo=3aD-y9kO523a)

[来源](http://tug.ctan.org/)
## 其他符号

输出结果: [Colab 笔记本](https://colab.research.google.com/drive/18_2yFdH8G-6NXY_7fTcshMoScgJ-SYac#scrollTo=-888LBso8HS1)

集合与逻辑:[来源](http://tug.ctan.org/)

箭头:[来源](http://tug.ctan.org/)

其他符号:[来源](http://tug.ctan.org/)
## 垂直花括号:
为了定义左垂直花括号,我们使用属性
\left{
为了关闭它,我们使用
\right}

输出结果: [Colab 笔记本](https://colab.research.google.com/drive/18_2yFdH8G-6NXY_7fTcshMoScgJ-SYac#scrollTo=XPXiLu3IE9gO)
## 水*花括号
对于水*花括号,我们使用:
\underbrace{...}
\overbrace{...}

输出结果: [Colab 笔记本](https://colab.research.google.com/drive/18_2yFdH8G-6NXY_7fTcshMoScgJ-SYac#scrollTo=xzu2CyGLE9iy)
## 衍生产品

输出结果: [Colab 笔记本](https://colab.research.google.com/drive/18_2yFdH8G-6NXY_7fTcshMoScgJ-SYac#scrollTo=S06VqebHE9mE)
## 极限

输出结果: [Colab 笔记本](https://colab.research.google.com/drive/18_2yFdH8G-6NXY_7fTcshMoScgJ-SYac#scrollTo=9eGgqyQ4E9oC)
## 总和

输出结果: [Colab 笔记本](https://colab.research.google.com/drive/18_2yFdH8G-6NXY_7fTcshMoScgJ-SYac#scrollTo=m6Q0JcCYE9qm)
## 产品

输出结果: [Colab 笔记本](https://colab.research.google.com/drive/18_2yFdH8G-6NXY_7fTcshMoScgJ-SYac#scrollTo=twn8CTHSE9tH)
## 积分

输出结果: [Colab 笔记本](https://colab.research.google.com/drive/18_2yFdH8G-6NXY_7fTcshMoScgJ-SYac#scrollTo=aYWkttHLE9vs)
## 矩阵

输出结果: [Colab 笔记本](https://colab.research.google.com/drive/18_2yFdH8G-6NXY_7fTcshMoScgJ-SYac#scrollTo=z3qGgxul048v)
**资源:**
* [https://www . datasciencecentral . com/profiles/blogs/all-about-using-jupyter-notebooks-and-Google-colab](https://www.datasciencecentral.com/profiles/blogs/all-about-using-jupyter-notebooks-and-google-colab)
* [https://oeis.org/wiki/List_of_LaTeX_mathematical_symbols](https://oeis.org/wiki/List_of_LaTeX_mathematical_symbols)
* [https://jupyter.org/](https://jupyter.org/)
* [https://en.wikipedia.org/wiki/Project_Jupyter](https://en.wikipedia.org/wiki/Project_Jupyter)
* [https://en.wikipedia.org/wiki/Markdown](https://en.wikipedia.org/wiki/Markdown)
* [http://tug.ctan.org/info/undergradmath/](http://tug.ctan.org/info/undergradmath/)
* [https://open classrooms . com/en/courses/1304236-redi gez-en-markdown](https://openclassrooms.com/en/courses/1304236-redigez-en-markdown)
**感谢阅读!😄**
[](https://medium.com/@ahmedazizkhelifi)
查看**我的其他文章**并关注我的 [**中的**](https://medium.com/@ahmedazizkhelifi)
哈利菲·艾哈迈德·阿齐兹
# 用 pandas 编写 5 个常见的 SQL 查询
> 原文:<https://towardsdatascience.com/writing-5-common-sql-queries-in-pandas-90b52f17ad76?source=collection_archive---------32----------------------->
## 利用您的 SQL 数据操作技能来学习熊猫
能够熟练地使用 SQL 和 pandas(Python 中的一个数据分析库)操作数据,对于数据分析师、数据科学家和任何处理数据的人来说都是一项很有价值的技能。在本帖中,我们将并排比较 SQL 查询和它们在 pandas 中的对应项。

作者图片
通常有许多方法可以达到相同的输出。因此,对于一些 SQL 查询,我们将在 pandas 中选择一些对应的查询。
# 0.资料组📦
*如果你还没有,确保你已经安装了* [*熊猫*](https://pandas.pydata.org/pandas-docs/stable/getting_started/install.html) *和* [*海兽*](https://seaborn.pydata.org/installing.html) *。*
我们将使用 seaborn 的餐馆小费数据集作为样本数据集。关于这个数据集的细节可以在这里找到[(这个源实际上是 R 的,但是它看起来是指同一个底层数据集)。为了便于快速查阅,我在下面引用了他们的数据描述:](https://vincentarelbundock.github.io/Rdatasets/doc/reshape2/tips.html)
> “一个服务员记录了他在一家餐馆工作几个月期间收到的每一笔小费的信息。”
Import packages
import pandas as pd
import seaborn as sns# Import data
tips = sns.load_dataset('tips')
# 📍 1.查看数据提取
让我们简单地从数据集中的前 5 条记录开始:

在`head()`方法中,我们可以像这样指定 parantheses 中的行数:`tips.head(5)`。然而,在这个例子中我们可以不用`tips.head()`,因为默认的行数被设置为 5。
我们不查看所有列,而是查看包含选定列的前 3 行:

在本例中,更改行选择和列选择的顺序不会影响结果。因此,图像显示了两种变化。在第一个版本中,我们首先选择列,然后指定行选择,而在第二个版本中,我们反过来做。现在,让我们看看如何过滤数据。
# 📍 2.过滤数据
让我们来看看周四前五名的记录:

我们可以用三种方法中的一种在熊猫身上达到同样的效果。第一种是我最喜欢的在 pandas 中过滤数据的方法,因为它更简洁,尤其是当有多个条件要过滤时。在另外两种方法中,我们用代码的`tips['day']=='Thur'`部分表示的*布尔掩码*过滤数据。该布尔掩码将为每个记录创建一个布尔值,如果记录来自星期四,则为`True`,否则为`False`。因为后两种方法除了`.loc`部分是相同的,我将只展示后者的例子,因为前者可以很容易地从另一个中推断出来。你注意到我们用`==`表示*等吗?那是因为`=`在 Python 中是为赋值保留的。然而,`>`、`>=`、`<`和`<=`在两者中的工作方式是一样的。现在,让我们看看其他一些运算符:

如你所见,两者在关键词上有相似之处。需要记住的一点是 Python 是区分大小写的。这意味着`match(r'S.')`和`match(r's.')`将给出不同的输出,而 SQL 中的`LIKE 'S%'`或`LIKE 's%'`将返回相同的输出。如果我们想过滤任何以' s '开头的日期,而不考虑大小写,一种方法是使用`match(r'[Ss]')`。如果对于某些记录,我们有一个名为“S”的工作日,`WHERE day LIKE ‘S%'`和`.str.startswith(‘S')`将选择这些案例,而`.str.match(r'S.')`不会。如果我们想迎合这种情况,那么我们将修改脚本为`.str.match(r'S.?')`。现在,让我们来看看否定:

您可能已经注意到,SQL 中的`NOT`对应于 pandas 中的`-`或`~`。现在,让我们看看如何使用`AND`或`OR`进行多条件过滤:

> *💡*在 Python 中,`and`或`or`被称为布尔运算符,而`&`或`|`被认为是位运算符。
如你所见,用`query`过滤比布尔掩码更简洁。它也更灵活,因为我们可以使用布尔或按位运算符。另一方面,当使用布尔掩码过滤时,只支持按位运算符。那就是`tips[(tips['tip']>=9) or (tips['size']==6)]`会给出一个错误。如果你想知道为什么,看看 StackOverFlow 上的这个帖子。
但是,当根据您的熊猫版本使用`query()`时,您可能会遇到此处 : `TypeError: ‘Series’ objects are mutable, thus they cannot be hashed`描述的[问题。如果发生这种情况,请使用使用布尔掩码的替代方法来过滤数据。](https://github.com/pandas-dev/pandas/issues/34251)
# 📍 3.排序数据
现在让我们看看如何按一列升序对数据进行排序:

就像我们可以在 SQL 中使用`ORDER BY tip ASC`一样,我们也可以在 pandas 中使用`tips.sort_values(tip’, ascending=True)`。然而,我觉得这些多余的赘言是多余的。如果我们要按降序排序,我们使用:

看看布尔值如何也可以用整数来表示?让我们看看如何按多列排序:

排序到此为止!
# 📍 4.汇总数据
通常,我们希望汇总数据并检查汇总指标,让我们从最基本的指标开始。这是检查行数的方法:

在 pandas 中,如果我们运行`tips.shape`,它将以如下格式显示行数和列数:(#rows,#columns)。这里,我们只是通过添加`[0]`来过滤行数。另一种常见的聚合是查找汇总统计数据。让我们检查所有数字列的*均值:

在 pandas 中使用这两个选项中的任何一个,如果数据在正确的[类型](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.dtypes.html)中,所有的数字列都将被自动选取。因此,我们不必键入所有的列名。
值得注意的是,`agg`其实是`aggregate`的别名。因此,所示的第二个选项是`tips.aggregate('mean')`的简称。
现在让我们检查列的最小值和最大值:

同样,这两种选择都可行。让我们进一步扩展查询:

尽管 SQL 和 pandas 中的输出并不完全相同,但我们得到的是相同的汇总统计数据,只是格式略有不同。这就是我们如何为两列提取相同的汇总统计信息:

另一个常见的查询是检查列的唯一值:

如果我们想查看唯一值的数量,那么它会变成:

在熊猫中,我们还可以使用`describe()`获得汇总统计数据。如果我们运行`tips.describe()`,我们将看到一个漂亮的所有数字列的汇总统计表。
# 📍 5.按组聚集数据
让我们先看一个简单的组聚合示例:

另一个例子:

熊猫的数据透视表看起来更简单吗?也许吧?👀我们也可以将我们在聚合部分学到的知识用于组聚合。
Voila❕:这些是对 5 种查询的比较。这里有一个额外的例子,我们结合了一些查询:

*您想访问更多这样的内容吗?媒体会员可以无限制地访问媒体上的任何文章。如果您使用* [*我的推荐链接*](https://zluvsand.medium.com/membership)*成为会员,您的一部分会费将直接用于支持我。*
谢谢你看我的帖子。希望这些比较对你有用,✂️,并对熊猫有更多的了解。
如果你有兴趣了解更多关于熊猫的知识,这里有我的一些帖子的链接:
◼️️ [给熊猫用户的 5 个建议](/5-tips-for-pandas-users-e73681d16d17)
◼️️ [如何在熊猫数据框架中转换变量](/transforming-variables-in-a-pandas-dataframe-bce2c6ef91a1)
再见🏃💨
# 撰写数据科学博客
> 原文:<https://towardsdatascience.com/writing-a-data-science-blog-5595bf542107?source=collection_archive---------58----------------------->

照片由 Unsplash 上的思想目录
## [我如何写数据科学博客](/how-i-write-a-data-science-blog-62e4108fe478)
由[丽贝卡·维克里](https://medium.com/u/8b7aca3e5b1c?source=post_page-----5595bf542107--------------------------------)——5 分钟阅读
两年来,我几乎一直在媒体上专门写关于数据科学的文章。我最初打算只是偶尔写点东西来帮助其他人学习数据科学,并作为我自己发展的学习工具(我是费曼技术的超级粉丝)。

照片由 Pexels 的 Pixabay 拍摄
## [欢迎加入数据团队!请解决一切。](/welcome-to-the-data-team-please-solve-everything-part-i-the-problem-13a157551804)
到[贾鲁斯·辛格](https://medium.com/u/413ac87ff804?source=post_page-----5595bf542107--------------------------------) — 11 分钟读取
先说一个常见的情况。不管是勇敢的 MBA 市场实习生还是首席执行官,至少拥有一点权力的人都会面临一个难题。他们安排了一个项目启动会议,与不同的人一起集思广益,想出一个解决方案,并且知道如果它有任何机会被他们的上级批准,它将需要数据的支持。

照片由 Alva Pratt 在 Unsplash 上拍摄
## [哪位翻译?](/which-translator-870bae18f3bf)
史蒂文·麦克唐纳 — 12 分钟阅读
作为一个狂热的小说读者,我经常想知道译者的风格对翻译小说的影响。尤其是,我一直对村上春树的作品很好奇。他用日语写作,有三个主要的英语翻译:阿尔弗雷德·伯恩鲍姆、杰伊·鲁宾和菲利普·加布里埃尔。

阿莱西奥·达马托,米哈伊尔·梁赞诺夫/CC BY-SA([http://creativecommons.org/licenses/by-sa/3.0/](http://creativecommons.org/licenses/by-sa/3.0/))
## [钟形曲线的魔力](/the-magic-of-the-bell-curve-60caa40bf182)
由曼努埃尔·布伦纳 — 7 分钟读完
这个世界令人难以置信地混乱,所以我们甚至需要一定的勇气去试图在其中找到永恒的结构。统计学家是这群大胆的人中的一员,他们总是在寻找规律性。但是规律性很难找到,关于这个世界的强有力的陈述通常只有在长期盲目徘徊的代价下才会出现。

## [自己解释!利用语言模型进行常识推理](/explain-yourself-leveraging-language-models-for-common-sense-reasoning-1c988b8b24cd)
作者 naz neen Rajani——25 分钟
深度学习模型在需要常识推理的任务上表现不佳,这通常需要某种形式的世界知识或对输入中不立即存在的信息进行推理。我们以自然语言序列的形式收集人类对常识推理的解释,并在一个名为常识解释(CoS-E)的新数据集中突出标注。
# 编写带有用户认证的多文件上传 Python-web 应用程序
> 原文:<https://towardsdatascience.com/writing-a-multi-file-upload-python-web-app-with-user-authentication-8f75064b819a?source=collection_archive---------7----------------------->
## 为合作者提供了一种简单的方法来上传文件到文件存储,而不需要他们理解任何代码

你好,我是尼克🎞 on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)
当构建一个 web 服务器时,我们经常希望提出一个想法或主题。在静态网站的情况下,这可以通过在源文件中包含相关信息来实现。在更动态的例子中,在将信息返回给用户之前,可以使用 API(应用编程接口)来预处理信息。
在这个例子中,我们设计了一个简单的 flask 应用程序,它只需要一个用户密钥和一个 web 浏览器就可以工作。这背后的想法是,任何人都可以使用它,它是一个设备(和操作系统),独立的。
# 创建烧瓶应用程序
像往常一样,我们首先为服务器安装相关的库。最简单的方法是使用 pip(python 包管理器)。
pip install flask, werkzeug
## 上传页面模板
接下来,为登录页面创建 HTML 模板。为此,我们从应用程序目录开始,创建一个名为`templates`的新文件。在这里,我们创建一个名为`upload.html`的文件,并将下面的代码粘贴到其中。
本质上,我们有一个`form`,其中包含我们的密码`input`,一个文件上传`input`和一个提交按钮。工作时,这将看起来如下

upload_simple.html 的输出
## 设置上传选项
接下来,我们为上传选项定义一个配置文件。为此,在主应用程序文件夹中创建一个名为`config.py`的文件。在此范围内,我们可以指定可接受的最大文件大小、上传目的地以及我们可以选择接受的文件扩展名。将下面的代码复制到`config.py`中,并进行相应的调整。
*关于文件类型的注意事项:文件扩展名不保证文件类型,我们应该避免执行任何上传的文件。这就是我们后来引入密码的原因——只有经过批准的用户才能上传。*
## 烧瓶模板
我们从一个基本的 flask 模板开始,它导入我们的信息,并在导航到`[http://127.0.0.1:4000/upload](http://127.0.0.1:4000/upload)`时服务于`upload.html`页面
## 上传文件检查
现在我们有了我们的应用程序,我们可以添加一些功能来确保我们有正确的目录,如果没有创建它。
if not os.path.isdir(upload_dest):
os.mkdir(upload_dest)
我们可以设置最大文件上传大小(使用来自`config.py`的值)
app.config['MAX_CONTENT_LENGTH'] = file_mb_max * 1024 * 1024
并检查文件扩展名(再次在`config.py`中定义)
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in extensions
就我个人而言,我在代码中的`app.secret`和`@app.route`部分之间添加了这些。
## 上传时要做什么
最后,我们还没有告诉程序在上传时做什么。我们通过解析来自上传页面的 post 请求来做到这一点。有关这意味着什么的信息,请阅读:
[](/talking-to-python-from-javascript-flask-and-the-fetch-api-e0ef3573c451) [## 从 Javascript: Flask 和 fetch API 与 Python 对话
### 使用 Python 处理动态 web 界面或可视化所需的数据。
towardsdatascience.com](/talking-to-python-from-javascript-flask-and-the-fetch-api-e0ef3573c451)
这里我们检查`request.method`是否是一个帖子,如果是,处理任何附加的文件。第一部分处理一个空请求,而第二部分遍历每个文件,检查它们是否有正确的文件扩展名,如果有,将它们保存到我们想要的位置(`upload_dest`)。
@app.route('/upload', methods=['POST'])
def upload_file():
if request.method == 'POST': if 'files[]' not in request.files:
flash('No files found, try again.')
return redirect(request.url) files = request.files.getlist('files[]') for file in files:
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join( upload_dest, filename)) flash('File(s) uploaded')
return redirect('/upload')
## 测试用例
最后,在继续添加密码之前,我们要确保上传功能正常。为此,我们运行 python 脚本,导航到[http://127 . 0 . 0 . 1:4000/upload](http://127.0.0.1:4000/upload),选择几个文件,然后单击 upload。如果这已经成功,并且您正在使用上面的配置,那么您的文件现在应该位于与您的应用程序嵌套在同一文件夹中的`uploads_folder`目录中。
# 添加用于用户身份验证的加密数据库
到目前为止,我们已经创建了一个 Flask 应用程序,可以用来上传文件。接下来,我们想添加一个基本的安全层,这样我们就可以跟踪哪些文件被上传了,是谁上传的。此外,这允许我们拒绝任何未经授权向目录提交数据的人。
## 安装 **pysqlcipher3**
我们从安装数据库工具开始。这有时会给人们带来问题,所以下面包含了 Mac 和 Linux 的安装说明。
**MAC**
brew install SQLCipher
pip install pysqlcipher3
**LINUX**
sudo apt install sqlcipher libsqlcipher0 libsqlcipher-dev
sudo -H pip3 install pysqlcipher3python3 -c 'import pysqlcipher3; print(pysqlcipher3.path)'
## 连接到数据库
我们从连接数据库开始。如果您以前使用过数据库,pysqlcipher 语法与 sqlite 或任何 postgresql 库非常相似。我们从导入库开始
from pysqlcipher3 import dbapi2 as sqlite3
from config import app_key, db_loc
然后连接到数据库:
conn = sqlite3.connect(db_loc)# where this is /path/test.db
cursor = conn.cursor()
最后,我们需要为数据库指定一个加密密钥,以便能够访问数据。
cursor.execute("PRAGMA key='%s'"%app_key)
如果不这样做,或者使用不同的键,您将收到以下错误:`DatabaseError: file is not a database`当试图从数据库模式中读取信息时。
## 创建数据库
打开我们的数据库,输入我们的密钥,我们现在可以创建一个表来存储我们的值。我们通过使用`cursor execute`函数执行一个`create table` SQL 命令来实现这一点。最简单的用例需要一个**名称**和一个**上传密钥。**
cursor.execute(
'''
CREATE TABLE IF NOT EXISTS upload (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
upload_key TEXT UNIQUE
);
'''
)
*此外,我设置了一个条件,即每个密钥必须是唯一的,因为我们不使用用户名登录。*
最后,我们必须将新表提交给数据库并关闭它。
conn.commit()
conn.close()
close only when we have finished everything, otherwise we have to reopen the database each time
## 添加用户
我们需要用户能够使用上传工具,所以我们可以使用`insert`命令添加一些。
cursor.execute(
'''
INSERT INTO upload (name, dir, uploadcode)
VALUES ("bob", "bobs top secret upload key")
'''
conn.commit()
## 读取数据库
作为检查,我们想看看是否有一个名字与 upload_key 相关联。这可以通过`select`函数和`where`条件来实现。
user_code = 'bobs top secret upload key'cursor.execute('select * from upload where uploadcode="%s"'%user_code)result = cursor.fetchall() # get all the results e.g. [("bob",)]
# 把它们串在一起
现在我们有了一个数据库和一个上传脚本,我们可以将两者结合起来。
## 打开数据库
首先,我们添加所需的库
from pysqlcipher3 import dbapi2 as sqlite3
from config import app_key, db_loc # already imported
在`from config_simple import *`线下。
## 读取密码
如果您使用的是之前的 HTML 代码,我们已经有了一个密码输入字段:`<p> upload_key: <input name=”psw” type=”password” /></p>`
因为这在提交表单中,所以我们可以在`@app.route`包装器中将它作为 POST 请求的一部分来阅读。
user_code = str(request.form.get('psw'))
我们将此与我们数据库阅读器结合起来,形成一个条件检查,看看是否有人拥有访问密钥/密码。
cursor.execute('select * from upload where uploadcode="%s"'%user_code)
result = cursor.fetchall()if len(result)==0:
flash('Not a valid Code')
return redirect(request.url)
然而,由于数据库只能从与我们需要放置的网站相同的计算线程中打开和读取
conn = sqlite3.connect(db_loc)
cursor = conn.cursor()
cursor.execute("PRAGMA key='%s'"%app_key)
在`cursor.execute`块之前,以及在`result =`线之后的`conn.close()`。(见最后 GitHub repo 中的 app.py)
# 结论
这就是我们要做的——最基本的提交框,允许预先批准的用户上传文件。但是,您可能希望进行一些改进(这些都在下面 GitHub 资源库中显示的附加文件中)。
* 检查扩展名的文件名字符串可能会导致问题。文件可能命名不正确,甚至没有扩展名(我的许多文件就是这种情况)。相反,我们可以过滤`file.mimetype`值。这些是格式`image/png`等。
* 拖放上传。手动选择文件通常很麻烦,尤其是当它们位于不同的位置时。从文件浏览器中拖动它们让用户的生活变得简单多了。
* 自动上传。另一个常见的错误是选择一个文件,但忘记点击提交。在你放入一个文件后,拥有一个可以立即工作的窗口有助于提高工作效率和用户满意度
* 文件预览。如果用户正在上传图像,提供微缩预览总是很有帮助的。这样可以进行最后一分钟的检查,这样就不会重复提交相同的文件。
* 文件失败指示器。虽然有一条消息通知我们上传状态,但一个视觉提示(即一个更亮的预览图像)让我们更容易看到是否有什么东西没有工作。
* 独特的下载空间——如果你有很多用户,你可能会弄不清哪个文件属于谁(更不用说覆盖问题了)。相反,我们可以为每个用户添加一个指定的空间,并将路径保存在数据库中。
添加以上所有附加建议,我们得到以下输出。

这里我们看到,两个文件都失败了,中间有一个成功了。在这种情况下,这是由于无效的用户凭据和故意无效的文件类型。
免责声明:这不是最安全的凭证检查方法,但是对于许多任务来说,这是一个非常合适的解决方案。
# 代码库
本教程的所有示例代码都被转储到了 Github 存储库中。
[](https://github.com/wolfiex/FlaskPython_FileUpload) [## wolfiex/FlaskPython_FileUpload
### 一个简单的烧瓶上传程序,用于需要凭据的多个文件
github.com](https://github.com/wolfiex/FlaskPython_FileUpload)
如果您有任何改进,请随时提交拉取请求。
# 编写生产级机器学习框架:经验教训
> 原文:<https://towardsdatascience.com/writing-a-production-level-machine-learning-framework-lessons-learned-195ce21dd437?source=collection_archive---------23----------------------->
## 我们从开发 PyTorch 框架来训练和运行深度学习模型中获得的一些见解…
我和我在 [Atomwise](https://www.atomwise.com/) 的优秀同事们已经为训练和运行深度学习模型编写了一个生产级 PyTorch 框架。我们的应用集中在药物发现上——预测一个分子是否会通过结合一个口袋来抑制蛋白质的活性。我们的目标是拥有一个稳定而灵活的*台,以支持机器科学家进行培训和实验,并支持药物化学家将它们应用到生产流程中。
迭代改进和[“持续重构”](https://www.codit.eu/blog/continuous-refactoring/)在软件开发中无处不在,我们也不例外。第一个版本远非完美;但是看看用例、反馈和扩展功能,我们已经经历了几轮细化。我们的设计目标是提供一种无需编写代码就能进行简单实验的工具,以及在自动化管道中使用的工具。这意味着我们必须在允许足够的旋钮进行有意义的研究,同时限制无意的错误配置和混乱的可能性之间取得*衡。我们还针对云环境中的性能和成本效益进行了优化。在这篇文章中,我想回顾一下我们在这个过程中学到的一些通用的、独立于领域的经验。
## **1。不要重新发明轮子**
当我们第一次尝试时,我们从头开始设计了一个应用程序;使用火炬。张量运算,但是其他的很少提供实用类。这被证明是一个错误:我们遇到了多处理和数据排队的问题,这是我们没有预料到的,而且`[torch.utils.data.DataLoader](https://pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader)`已经解决了。虽然这个 Python 类表面上看起来似乎没做什么,但实际上它是相当复杂的!并且它足够灵活,因此在大多数用例中无需修改即可采用。通过继承轻量级的`torch.utils.data.DataSet`和`torch.utils.data.Sampler`来使用定制要简单得多。对于后者,请参见我之前关于我们复古`[TreeSampler](/flexible-declarative-dataset-sampling-in-pytorch-613c6d5db10c)`设计的帖子。
我们的大部分架构都遵循通用标准——请看下面的概要。训练和评分循环被封装到一个`Engine`类中。训练通过小批数据迭代;在每个时期之后,来自单独测试集的固定数量的小批用于估计学习曲线并检测可能的过拟合。每当我们改进(最*的*滑*均值)测试指标时,我们会写出一个检查点作为当前的最佳模型,可以用来模拟早期停止。此外,在(可能不同的)可配置的时间间隔,我们编写检查点,将学习统计记录到度量记录器(例如,`[Tensorboard](https://www.tensorflow.org/tensorboard)`或`[MLFlow](https://mlflow.org/)`)中,并将消息打印到日志文件和控制台。我在这里描述了辅助`Meter`级[。](/pytorch-tidbits-measuring-streams-and-times-de0c7bae85da)

我们发现引擎代码的很大一部分与初始化训练运行所需的所有对象(模型、数据集、优化器、调度器、损失函数等等)有关,所以我们后来分解出一个只在启动时使用的`LaunchManager`类。
转换管道通常是指一系列数据预处理步骤,如选择、格式转换和随机扩充。最常见的建议是在`DataSet.`的`__getitem__()`方法中调用它们,我们通过将转换管道作为整理阶段的一部分,将一系列示例连接到一个迷你批处理`Tensor`中。为此,`DataLoader`允许传入一个可选的`collate_fn`参数。因此,张量运算可以在整个 minibatch 上进行矢量化,与在单个示例上循环相比,可以显著提高速度。
## **2。使用配置文件**
如果 shell 命令有很多可能的选项,那么它们就成了记忆和输入的负担。在实践中,用户通常会复制/粘贴本质上作为配置的包装外壳脚本。更好的解决方案是从一开始就使用配置文件来区分设置和代码。这带来了几个额外的好处:它们允许在部分、注释、管理缺省值的清晰方式和自动验证工具中进行分层组织。
虽然像 Python 这样的解释语言使得用 Python 本身编写配置变得很容易,但我更喜欢通过使用像`yaml`或`json`这样的格式来保持与代码的明显区别。在我们的例子中,我们选择了使用`[ConfigObj](https://pypi.org/project/configobj/)`包进行验证的`yaml`格式。也有替代的配置包,比如 are schema,或者脸书最*推出的`[hydra.](https://engineering.fb.com/open-source/hydra/)` `ConfigObj`管理一个单独的规范文件,包含每个参数的类型和值约束。在这里指定默认值很方便,这样配置就可以省略某些选项。例如,在得分模式下运行时,我们可以将培训部分留空,反之亦然。在验证过程中,记住还要检查额外的参数——那些由用户指定,但在规范中没有提到的参数。在许多情况下,这些都是打字错误,否则很容易被忽视。
我们将整个配置约束到一个文件中。虽然在运行实验时,文件包含、覆盖机制或拥有多个子配置可能更容易管理,但我们认为这不会服务于一致性和可再现性的长期目标。
我们发现为配置文件实现智能的类似 diff 的功能是很有用的。该函数分解添加、删除和更改的选项。这有助于快速了解新模型与以前训练的模型有何不同,以及在微调运行中发生了什么变化。与基于行的(unix) `diff`相反,它考虑了排序中的非实质性变化、隐含的默认值,并且可以被告知忽略某些差异,比如在日志记录中。
关于配置和定义模型架构的正确方法很容易引起激烈的争论——我们也有我们的一份。第一个问题:规范粒度的适当水*是什么?`[Caffe](https://caffe.berkeleyvision.org/)`框架以声明的方式指定了完整的模型,作为所有层及其设置的列表。另一方面,PyTorch 通常采用更程序化的方法——我们可以将模型定义为`torch.nn.Module`的子类,并写出包含计算步骤序列的正向方法。这两种方法在灵活性、对无意的错误设定的敏感性、代码量以及跨实验跟踪和比较方面都有优点和缺点。我们希望避免仅在细微方面有所不同的模型文件的扩散,所以我们选择了一个折衷的解决方案:我们为每个模型族写出一个文件,但是允许一些由选项控制的变化(例如,层的深度和宽度)。通过提供一个公共构建模块库,例如具有不同激活的多层感知器以及可选的丢弃层和归一化层,可以进一步促进这一点。
下一个问题:我们如何通过名称在配置中检索这些模型(当然我们不想引用文件路径)?为此,我们设计了一个轻量级的模型注册中心(也可以称之为工厂)。当模型代码用一个`@register`标签修饰时,它将被识别,并随后在配置中通过它的类名或一个定制的名字被访问。每个模型文件都有一个相关的参数规范。在训练开始时创建实例之前,注册中心会读取并验证这些参数。
我们使用的一些模型由其他模型组成——多任务、连体和系综网络。对于这些,在配置中允许多个命名的模型被证明是有用的,其中一些可能是当前未使用的。例如,系综网络可以具有如下结构:
换句话说,我们正在使用软件工程的[基本定理。我们也开始在配置的其他部分使用命名组件。例如,不同的模型类型可能需要不同的转换管道。因此,如果用户切换模型,他们将需要相应地修改转换部分,这可能是乏味且容易出错的(在实践中,用户很可能倾向于保留注释和取消注释部分)。因此,我们也用名称来指代转换管道和微型批次采样设置。](https://en.wikipedia.org/wiki/Indirection)
总的来说,我们当前的配置文件已经增长到大约 500 行。它分为以下几个顶级部分:
* *型号:*如上所述
* 数据:位置、格式和缓存选项。在大多数情况下,原始数据太大,无法完全存储在内存中。我们假设一个单独的索引文件;每一行都包含训练标签和其他有用的元数据,以及对数据描述符的引用(通常是文件路径,尽管也可以使用其他数据源和协议)。我们不仅允许单个输入,还允许内部连接的列表。这对于配置交叉验证运行或处理多种不同的输入格式非常方便
* *损失函数:*训练可以有一个损失函数,定期测试可以有零到多个辅助损失函数。对于多任务目标,我们用原子子损失列表定义一个复合`WeightedSumLoss`,每个子损失与输入数据中的一个权重和一个列名相关联。
* *变换管道*
* *小批量取样:*见[早先的帖子](/flexible-declarative-dataset-sampling-in-pytorch-613c6d5db10c)
* *训练:*优化器、调度器、迭代次数、测试迭代的频率和次数、提前停止的*滑迭代、检查点
* *评分:*测试时间增加和聚集,检查点的频率
* *日志记录:*我们在这一小节中加入了 Python `[logging.config](https://docs.python.org/3/library/logging.config.html)`。顺便提一下:为了捕获和合并来自`Dataloader`工作者的日志记录,必须使用带有`[logging.handlers.QueueListener](https://docs.python.org/3/library/logging.handlers.html#queuelistener)`的 torch.multiprocessing.Queue。
* *常规和操作参数:* CPU 和 GPU 模式、工作线程数量、随机种子、写入内容和写入频率`Tensorboard`
> “计算机科学中的所有问题都可以通过另一个间接层次来解决……除了间接层次太多的问题。”
>
> —大卫·惠勒
## **3。共同归档和封装所有模型工件**
机器学习科学家的实践涉及不断训练和评估大量模型,以便找出最佳的模型架构、超参数、数据和采样配置。在评估之后,他们保留模型文件以便比较,并能够在将来做更多的分析。如果需要的话,你总是希望能够回到过去,并且很好地理解你到底是如何得出一个实验结果的;你今天并不确切知道明天、一个月或一年后你会问哪个问题。
归档不仅适用于原始模型文件,还适用于实验运行可能产生的所有工件:`Logging`和`Tensorboard`输出、检查点、可视化等等。保持实验有组织的一种方式是系统化的目录结构,例如使用路径名,该路径名便于记忆所研究的参数差异,与基本模型相比。虽然许多机器学习科学家自然倾向于这样的组织,但为什么不为他们做并强制执行呢?
因此,我们将所有与训练相关的工件保存在一个专用于特定运行的目录中:
* *模型检查点:*每隔一段时间,加上早期停止的当前最佳检查点,每个测试指标一个
* 训练、测试和性能(时间和记忆)*统计*以 Tensorboard(或 MLFlow,…)格式
* 原始*配置文件*的副本,根据规范输入或不输入默认值
* 输出*日志文件*
* 检查点的副本,如果我们从一个*预训练模型*开始
对于新的训练运行,将创建一个空目录。它应该只能由我们的命令行界面脚本修改,而不能由用户直接修改。这减少了可能的错误,并简化了引擎代码,因为我们可以假设一个具有预定义名称和结构的布局。我们的礼仪是将模型目录视为黑盒档案。

照片由[蒂姆·莫斯霍尔德](https://unsplash.com/@timmossholder?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄
## **4。围绕典型工作流程进行设计**
我们如何设计命令行界面,使其既能被进行实验的机器学习科学家使用,又能作为领域专家标准工作流程的一部分,而领域专家不一定是程序员(比如我们案例中的药物化学家)?我在上面讨论了使用配置文件以声明方式控制程序行为的目标。相反,为了避免混淆,我们希望尽量减少命令行选项。本质上,脚本只需要知道三件事:
* *到模型目录的路径*
* *配置文件:*总是创建包含单个文件的新目录会给用户带来不必要的负担。我们允许将配置从任何原始位置复制到模型目录中。
* *执行方式:* `train`或`score`。在评分过程中,会指定一个输出目录来包含带有模型预测的文件。某些输入列是从输入文件中复制的。我们集成了测试集扩充——该模型可以从单个输入行生成多个输出分数。该配置可以指定列来定义聚合组,这样我们就可以得到一个单独的行,但也可能是这个集合的多个统计数据(例如,分数的*均值和标准差——想想`[pandas.DataFrame.groupby](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.groupby.html)`)。
有时,我们希望从现有模型中初始化模型权重,而不是随机初始化。这对于微调来说是正确的,但如果我们只是想通过额外的迭代来继续训练,也是如此。我们应该在现有的模型目录中跟踪这个过程,还是创建一个新的?或者,行为是否应该取决于配置变更的重要性(例如,允许用户在相同的目录中添加更多的迭代,而不改变配置)?这个问题让人想起忒修斯的[船。我们的讨论最终决定总是需要一个新的目录。供参考,复制旧模型检查点;但是在初始化之后,训练运行被视为与任何其他运行完全相同。](https://en.wikipedia.org/wiki/Ship_of_Theseus)
我们还必须考虑到培训和评分大多是在云环境中进行的。我们经常为成本折扣提供现场实例,但这意味着我们必须随时准备好中断。显然,训练不应该从头开始,而应该从中断的地方重新开始。我们称这种单独的场景为自动重启。它应该对用户尽可能无摩擦和透明;因此,它应该通过重新发出原始命令自动工作,无需任何更改。
在固定的时间间隔,我们保存由模型、优化器和调度器状态组成的检查点;请注意,出于采样目的,还需要保存所有随机数发生器的状态。请记住,只有在成功写入当前检查点后,才能删除旧的检查点,以应对损坏。在自动重启时,我们会检查最新的有效版本。
如上所述,我们希望防止用户意外覆盖之前的实验。那么我们如何区分这种情况和自动重启呢?同时,模型目录之外的配置文件可能已经被修改,并且模型甚至可能在不同的绝对路径中继续。这就是比较配置的函数再次派上用场的地方。如果命令行上指定的配置文件的内容与模型目录中归档的内容一致,则该脚本假定它已经自动重启;否则,我们会警告用户并中止。
自动重启不仅适用于培训;有时我们需要对有几千万或几行的大文件进行评分。为此,计分器创建一个以输出文件命名的临时目录,并定期保存分数批次。最后,所有这些部分结果在被写回到单个输出文件之前被重新读取、连接、聚合并适当地格式化。

Aneta Foubíková 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
## **5。设计尽可能多的再现性(但不是更多)**
在一个专用的目录中捕获所有与模型相关的工件是实现可再现性的第一步,但仅仅是第一步。
**软件版本跟踪**
对于以后的参考,将重要的元数据打印到日志文件是很有帮助的。我们使用`[versioneer](https://github.com/warner/python-versioneer)`包来跟踪软件版本;它检索最新的 git 标签和散列字符串,并将其作为 Python 字符串提供。我们相应地设置我们的包`__VERSION__`字符串,用它来标记我们的内部 `PyPI`发布,并在每次运行时将其打印到日志输出中。我们还打印重要包的版本号,比如`torch`。
**确定性执行**
众所周知 [GPU 计算天生就是不确定的](https://pytorch.org/docs/stable/notes/randomness.html)。即使特殊的`CUDA`执行标志也不能在所有情况下消除并行执行的这些影响。然而,尽可能使训练具有可重复性也是值得的。为此,我们提供了种子随机数生成器的选项:Python 随机包、`numpy`、CPU 操作和每个 GPU 实例。注意 PyTorch `Dataloader`产生多个进程,每个进程都有自己独立的随机数生成器。正如在之前的[帖子](/flexible-declarative-dataset-sampling-in-pytorch-613c6d5db10c)中更详细描述的,我们可以通过在专用于采样的单个主线程中预先生成所有需要的随机数,并将它们附加到每个示例数据上来有效地控制这一点。
**测试**
测试驱动开发是一种有效的、普遍适用的软件开发指南。因此,我们努力用适当的单元测试覆盖我们的大部分代码库。我们还需要几个集成步骤来模拟完整工作流的不同设想情况:从头开始训练,自动重启,评分,以及重新调整,使用空的或现有的模型目录。机器学习的特殊之处在于,引入一个微妙的错误非常容易,但在一段时间内没有意识到它:只要它不会导致灾难性的崩溃,模型训练就可以在某种程度上弥补任何数据缺陷。因此,我们发现自动化每晚的训练回归工作非常有用,用我们最新的提交和典型的模型架构运行十万次迭代。基于以前的运行,我们在指定的迭代中为各种度量设置期望和容差,以提醒我们行为的任何变化,无论是积极的还是消极的。

照片由[卡洛斯·穆扎](https://unsplash.com/@kmuza?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄
## 6.最后:不要让完美成为好的敌人
一个常见的、众所周知的缺陷是过早的优化,或者优化软件的一部分,而这部分在功能或性能方面并不重要。概要分析和选择性简化(例如,用简单的模型或简单的数据运行)可以暗示什么是全局中最重要的。相反,有时不显眼的作品却能产生超大的效果。例如,分析显示,训练循环中的以下代码行由于不必要的 GPU 同步而导致速度变慢:
在我们的旅途中,我们走过了几条小路,在后面变成了兔子洞。仅举一个例子:如上所述,我们在试图保护现有模型目录不被意外覆盖方面走得太远了。我们有一个写保护文件的方案,但我们不希望用户必须明确地改变权限,同时,它必须在一个自动化框架内工作,其中状态存储在无权限的`S3`文件中。这需要大量的逻辑来涵盖不同的用例,但是总会有你没有预料到的新情况。这种设计既复杂又脆弱,最终并不值得追求所谓的利益。
如果软件变得太复杂而无法管理,用户会感到沮丧——但是如果它不能提供至少一个他们最想要的功能的基本版本,他们也会感到沮丧。这是一个连续的过程,对请求进行过滤和优先级排序,将它们捆绑到一个通用的最小化界面中。
随着软件的成长和成熟,在健壮性和便利性与新特性和灵活性之间总会有一个微妙的*衡。尽管开发人员努力让尽可能多的用户满意,但我们永远不可能让每个人都非常满意。
## 承认
感谢布兰登·安德森、巴斯蒂亚安·伯格曼、杰瑞德·汤普森和格雷格·弗里德兰!
# 用 pandas 编写高级 SQL 查询
> 原文:<https://towardsdatascience.com/writing-advanced-sql-queries-in-pandas-1dc494a17afe?source=collection_archive---------10----------------------->
## 利用您的 SQL 数据操作技能来学习熊猫
能够熟练地使用 SQL 和 pandas(Python 中的一个数据分析库)操作数据,对于数据分析师、数据科学家和任何处理数据的人来说都是一项很有价值的技能。在本帖中,我们将看看精选的一些高级 SQL 查询及其在 pandas 中的对应项。

照片由[法比奥](https://unsplash.com/@fabioha?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄
如果你还是熊猫新手,你可能想看看 [10 分钟见熊猫](https://pandas.pydata.org/docs/user_guide/10min.html)或[我之前的帖子](/writing-5-common-sql-queries-in-pandas-90b52f17ad76),在那里我讨论了基本查询和它们在熊猫中的对等物的并排比较。这篇文章假设读者熟悉 SQL 查询。
# 0.资料组📦
我们将创建一个小数据集来使用。假设我们有两个假想的人在过去两年的旅行数据:
df = pd.DataFrame({'name': ['Ann', 'Ann', 'Ann', 'Bob', 'Bob'],
'destination': ['Japan', 'Korea', 'Switzerland',
'USA', 'Switzerland'],
'dep_date': ['2019-02-02', '2019-01-01',
'2020-01-11', '2019-05-05',
'2020-01-11'],
'duration': [7, 21, 14, 10, 14]})
df

让我们将 *dep_date* 定义为前往目的地的出发日期,并假设 *duration* 以天数表示行程长度。
# 1.比较🔎
## 📍 1.1.换档:超前()和滞后()
我们先从基本版的`LEAD()` 和`LAG()`说起。对于每一次行程,我们来拉下一次行程的出发日期: *lead1* ,第二次下一次行程: *lead2* ,上一次行程: *lag1* ,第三次上一次行程: *lag3* **。**
SELECT name
, destination
, dep_date
, duration
, LEAD(dep_date) OVER(ORDER BY dep_date, name) AS lead1
, LEAD(dep_date, 2) OVER(ORDER BY dep_date, name) AS lead2
, LAG(dep_date) OVER(ORDER BY dep_date, name) AS lag1
, LAG(dep_date, 3) OVER(ORDER BY dep_date, name) AS lag3
FROM df
为了在熊猫身上得到同样的产量,我们使用`shift()`:
df.sort_values(['dep_date', 'name'], inplace=True)
df.assign(lead1 = df['dep_date'].shift(-1),
lead2 = df['dep_date'].shift(-2),
lag1 = df['dep_date'].shift(),
lag3 = df['dep_date'].shift(3))

在第一行中,我们用`sort_values()`对数据进行了排序,因为我们对所有的移位操作使用了相同的逻辑。这样做比我们每次创建新列时都要对数据进行排序更有效,如下所示:
df.assign(lead1 = df.sort_values(['dep_date', 'name'])['dep_date']
.shift(-1),
lead2 = df.sort_values(['dep_date', 'name'])['dep_date']
.shift(-2),
lag1 = df.sort_values(['dep_date', 'name'])['dep_date']
.shift(),
lag3 = df.sort_values(['dep_date', 'name'])['dep_date']
.shift(3))

这种低效方法的输出将包含正确的值,但是行的顺序将与原始数据相同,并且不会被排序。
现在让我们看看剩下的 4 行代码。在熊猫中,我们可以用`assign()`创建多个栏目。但是,新的列还没有添加到 DataFrame 中。如果我们想添加新的列到`df`,我们需要这样分配:
df.sort_values(['dep_date', 'name'], inplace=True)
df = df.assign(lead1 = df['dep_date'].shift(-1),
lead2 = df['dep_date'].shift(-2),
lag1 = df['dep_date'].shift(),
lag3 = df['dep_date'].shift(3))
我们将在最后一节看另一个带有`PARTITION BY()`的`LEAD()`的例子。现在,让我们看看如何操作日期/日期时间列。
## 📍 1.2.Date/datetime: DATENAME(),DATEDIFF(),DATEADD()
在本节中,我们将从出发日期开始提取一周的日名称:*日*,从出发日期开始的月份名称:*月*,从上一次旅行开始经过的天数:*差*和到达日期: *arr_date* 。
SELECT name
, destination
, dep_date
, duration
, DATENAME(WEEKDAY, dep_date) AS day
, DATENAME(MONTH, dep_date) AS month
, DATEDIFF(DAY,
LAG(dep_date) OVER(ORDER BY dep_date, name),
dep_date) AS diff
, DATEADD(DAY, day, dep_date) AS arr_date
FROM df
首先,我们必须确保列数据类型是正确的:
Convert to proper dtype
df['dep_date'] = pd.to_datetime(df['dep_date'])
df['duration'] = pd.to_timedelta(df['duration'], 'D')
将 *dep_date* 转换为 datetime 将允许我们使用`.dt`访问器访问许多日期部分。例如:`df[‘dep_date’].dt.year`将给出年份(相当于 SQL 中的`DATEPART(YEAR, dep_date)`)。
将*持续时间*转换为 timedelta 允许我们将其添加到一个日期时间列,以获得另一个日期时间列。
完成数据类型转换后,让我们来看一下比较:
df.sort_values(['dep_date', 'name'], inplace=True)
df.assign(day = df['dep_date'].dt.day_name(),
month = df['dep_date'].dt.month_name(),
diff = df['dep_date'] - df['dep_date'].shift(),
arr_date = df['dep_date'] + df['duration'])

如您所见,一旦正确设置了数据类型,操作就变得简单了。值得注意的是,我们可以用 DataFrame 的`dtypes`属性检查列的数据类型。这里有一个例子:
df.sort_values(['dep_date', 'name'], inplace=True)
df.assign(day = df['dep_date'].dt.day_name(),
month = df['dep_date'].dt.month_name(),
diff = df['dep_date'] - df['dep_date'].shift(),
arr_date = df['dep_date'] + df['duration']).dtypes

在 pandas 中,当发现两个日期之间的差异时,它返回一个 timedelta 列。因此,我们可以看到列 *diff* 实际上是一个时间增量。
## 📍 1.3.排名:ROW_NUMBER(),RANK(),DENSE_RANK()
您可能以前在 SQL 中至少使用过这些函数中的一个。这三者之间的关键区别是他们在排名关系和排名之后的待遇。让我们创建三个列,分别使用以下方法根据*持续时间*对每个记录进行排序: *row_number_d,rank_d* 和 *dense_rank_d.*
使用`ROW_NUMBER()`时,领带的等级与其他两种不同。为了控制如何用`ROW_NUMBER()`处理*局,我们使用*名称*来打破*局。
SELECT name
, destination
, dep_date
, duration
, ROW_NUMBER() OVER(ORDER BY duration, name) AS row_number_d
, RANK() OVER(ORDER BY duration) AS rank_d
, DENSE_RANK() OVER(ORDER BY duration) AS dense_rank_d
FROM df
pandas 中的查询可以通过`rank()`实现:
df.sort_values(['duration', 'name']).assign(
row_number_d = df['duration'].rank(method='first').astype(int),
rank_d = df['duration'].rank(method='min').astype(int),
dense_rank_d = df['duration'].rank(method='dense').astype(int))

如上图所示,我们在`rank()`中指定了相关的方法。值得注意的是,这里还有我们没有涉及的其他变化。如果你有兴趣了解一下,看看[文档](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rank.html)。
我们在这个例子中做了一些稍微不同的事情。与前两节中的例子不同,我们没有对数据进行适当的排序。相反,我们将方法链接起来,根本没有修改数据。
你可能也注意到了我们已经包括了`astype(int)`。这是为了将浮点数中的秩转换成整数。如果您喜欢看到浮动,那么可以删除这部分代码:
df.sort_values(['duration', 'name']).assign(
row_number_d = df['duration'].rank(method='first'),
rank_d = df['duration'].rank(method='min'),
dense_rank_d = df['duration'].rank(method='dense'))

## 📍 1.4.聚合窗口函数和分区
在本节中,我们将为最长的旅行持续时间创建列: *max_dur* ,所有旅行的总持续时间: *sum_dur* ,每人旅行的*均持续时间: *avg_dur_name* 以及每人旅行时间的累计总和: *cum_sum_dur_name。*
SELECT name
, destination
, dep_date
, duration
, MAX(duration) OVER() AS max_dur
, SUM(duration) OVER() AS sum_dur
, AVG(duration) OVER(PARTITION BY name) AS avg_dur_name
, SUM(duration) OVER(PARTITION BY name ORDER BY dep_date
RANGE BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW) AS cum_sum_dur_name
FROM df
熊猫可以这样做:
df.assign(max_dur=df['duration'].max(),
sum_dur=df['duration'].sum(),
avg_dur_name=df.groupby('name')['duration']
.transform('mean'),
cum_sum_dur_name=df.sort_values('dep_date')
.groupby('name')['duration']
.transform('cumsum'))
因为[广播](https://jakevdp.github.io/PythonDataScienceHandbook/02.05-computation-on-arrays-broadcasting.html),所以在 pandas 中添加诸如 *max_dur* 和 *sum_dur* 之类的聚合统计数据很简单。本质上,如果我们试图给 pandas 中的一个新列分配一个标量值,该值将在所有行中传播。`PARTITION BY`由`groupby()`和`transform()`组合实现。

在此输出中,数据的顺序与之前相同(即未排序),但它将按照 SQL 中每个人的 *dep_date* 排序。我们仅在创建 *cum_sum_dur_name* 时对数据进行了排序。如果我们希望对输出进行排序,代码将变为:
df.sort_values(['name', 'dep_date'], inplace=True)
df.assign(max_dur=df['duration'].max(),
sum_dur=df['duration'].sum(),
avg_dur_name=df.groupby('name')['duration']
.transform('mean'),
cum_sum_dur_name=df.groupby('name')['duration']
.transform('cumsum'))

这将与 SQL 输出相同。
## 📍 1.5.把它放在一起
最后,让我们编写一个查询,覆盖我们刚刚访问过的四个不同区域。这是我们的最后一个例子:
SELECT ROW_NUMBER() OVER(PARTITION BY name
ORDER BY dep_date) AS number
, name
, destination
, DATENAME(MONTH, dep_date) AS dep_month
, dep_date
, DATEADD(DAY, duration, dep_date) AS arr_date
, LEAD(dep_date) OVER(PARTITION BY NAME
ORDER BY dep_date) AS next_dep_date
, DATEDIFF(DAY,
dep_date,
LEAD(dep_date) OVER(PARTITION BY NAME
ORDER BY dep_date)) AS gap
, duration
, AVG(1.0 * duration) OVER() AS avg_dur
, AVG(1.0 * duration) OVER(PARTITION BY name) AS avg_dur_name
, SUM(duration) OVER(PARTITION BY name ORDER BY dep_date
RANGE BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW) AS cum_sum_dur_name
FROM df
ORDER BY name, dep_date
我相信当我们练习自己的时候,我们会学到更多。我鼓励您在看到我们将要看到的比较之前,自己尝试将这个 SQL 查询翻译成 pandas。👀
下面是对比:
Convert to proper type
df['dep_date'] = pd.to_datetime(df['dep_date'])
df['duration'] = pd.to_timedelta(df['duration'], 'D')# Sort data
df.sort_values(['name', 'dep_date'], inplace=True)# Append new columns to data
df = df.assign(number=df.groupby('name')['dep_date']
.rank('min')
.astype(int),
dep_month = df['dep_date'].dt.month_name(),
arr_date = df['dep_date'] + df['duration'],
next_dep_date = df.groupby('name')['dep_date']
.transform(lambda x: x.shift(-1)),
gap = df.groupby('name')['dep_date']
.transform(lambda x: x.shift(-1))-
df['dep_date'],
avg_dur = df['duration'].mean(),
avg_dur_name = df.groupby('name')['duration']
.transform(lambda x: x.mean()),
cum_sum_dur_name = df.groupby('name')['duration']
.transform(lambda x: x.cumsum()))# Reorder columns
columns = ['number', 'name', 'destination', 'dep_month',
'dep_date', 'arr_date', 'next_dep_date',
'gap', 'duration', 'avg_dur', 'avg_dur_name',
'cum_sum_dur_name']
df[columns]

输出—第 1 部分

输出—第二部分
我们已经转换了数据类型,对数据进行了分类,并用新列对其进行了修改。为了以与 SQL 查询相同的方式查看列排序的数据,我们使用了一个列出列顺序的列表。如果只运行`df`,列的排序会有所不同。
你可能已经注意到 *avg_dur* 是用天数、小时数和分钟数的组合来表示的:“13 天 04:48:00”。这相当于 13 + (4*60+48) / (24*60) = 13.2 天。如果我们只想看到 13.2,那么我们可以用这个代码片段替换`df[‘duration’].mean()`,它在找到*均值之前将*持续时间*转换为数字类型:`pd.to_numeric(df[‘duration’].dt.days).mean()`。
当创建 *avg_dur_name* 和 *cum_sum_dur_name* 时,我们使用了`lambda`函数,而不是使用这样的语法:
avg_dur_name = df.groupby('name')['duration'].transform('mean'),
cum_sum_dur_name = df.groupby('name')['duration']
.transform('cumsum')
这是因为如果我们试图这样做,我们将会遇到一个在这里描述的问题。因此,我们使用了一种变通办法。
Voila❕这番话结束了我们的比较。

由 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的 [Shubham Dhage](https://unsplash.com/@theshubhamdhage?utm_source=medium&utm_medium=referral) 拍摄的照片
*您想访问更多这样的内容吗?媒体会员可以无限制地访问媒体上的任何文章。如果你使用* [*我的推荐链接*](https://zluvsand.medium.com/membership)*成为会员,你的一部分会费会直接去支持我。*
谢谢你看我的帖子。希望这篇文章对你有用,✂️,并了解更多关于熊猫的知识。如果你有兴趣了解更多关于熊猫的信息,这里有我的一些帖子的链接:
◼️️[pandas 中数据聚合的 5 个技巧](/writing-5-common-sql-queries-in-pandas-90b52f17ad76)
◼️️ [在 pandas 中编写 5 个常见的 SQL 查询](/writing-5-common-sql-queries-in-pandas-90b52f17ad76)
◼️️ [给 pandas 用户的 5 个技巧](/5-tips-for-pandas-users-e73681d16d17)
◼️️ [如何转换 pandas 数据框架中的变量](/transforming-variables-in-a-pandas-dataframe-bce2c6ef91a1)
再见🏃💨
# 编写自定义 scikit-学习变形金刚
> 原文:<https://towardsdatascience.com/writing-custom-scikit-learn-transformers-e07a3cf7b559?source=collection_archive---------68----------------------->
## 当您需要量身定制的解决方案时。

[Samule 孙](https://unsplash.com/@samule?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/transformer?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上的照片
scikit-learn 的 transformers API 是一个用于数据清理、预处理、特征工程和提取的强大工具。然而,有时,在众多可用的变压器中,没有一个能与手头的具体问题相匹配。在这些场合,能够自己写一个是很方便的。幸运的是,很容易利用 scikit-learn 的类来构建一个遵循包约定的转换器,并且可以包含在 scikit-learn 管道中。

# 问题设置
为了实用,我们来看一个例子。我们有一个名为`TAO` 的数据集,代表热带大气海洋。它包含一些天气测量,如温度、湿度或风速。R 库`VIM`提供了这些数据的子样本。这里,我们正在处理一个稍微预处理过的版本。

快速浏览一下数据框,我们会发现`air_temp`变量中有大量缺失值,我们需要在建模前对其进行估算。
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 733 entries, 0 to 732
Data columns (total 8 columns):
year 733 non-null int64
latitude 733 non-null int64
longitude 733 non-null int64
sea_surface_temp 733 non-null float64
air_temp 655 non-null float64
humidity 642 non-null float64
uwind 733 non-null float64
vwind 733 non-null float64
dtypes: float64(5), int64(3)
memory usage: 45.9 KB
Scikit-learn 提供了输入转换器,如`SimpleImputer`,它通过变量的*均值、中间值或其他一些量来填充变量的缺失值。然而,众所周知,这种插补会破坏数据中的关系。
但是看,还有另一个叫做`sea_surface_temp`的变量没有丢失值!我们可以预期水温与气温高度相关!让我们画出这两个变量的对比图。

正如我们所料,存在明显的线性关系。此外,我们可以看到为什么均值或中值插补毫无意义:在水温为 22 或 29 的观测中,将气温设定为 24.5 度的中值完全破坏了这两个变量之间的关系。
似乎输入`air_temp`的一个好策略是使用带有`sea_surface_temp`的线性回归作为预测器。从 scikit-learn 0.21 版本开始,我们可以使用`IterativeImputer`并将`LinearRegression`设置为输入引擎。然而,这将使用数据中的所有变量作为预测值,而我们只需要水温。让我们编写自己的转换器来实现这一点。

# 定制变压器
scikit-learn 转换器应该是一个实现三种方法的类:
* `fit()`,它简单地返回`self`,
* `transform()`,它将数据`X`作为输入,并执行所需的转换,
* `fit_transform()`,如果包含`TransformerMixin`作为基类,会自动添加。
在这些之上,我们有`__init__()`来捕获参数——在我们的例子中是空气和水温列的指数。我们还可以包含`BaseEstimator`作为基类,这将允许我们从 transformer 对象中检索参数。
总而言之,所有的工作都归结为实际编写`transform()`方法,该方法使用适当的列来拟合线性回归模型,并使用它来估算气温。整个事情只有几行代码:
我们现在可以像使用任何其他 scikit-learn 转换器一样使用我们的 imputer:
我们还可以提取它的参数:
{'air_temp_index': 4, 'sea_temp_index': 3}
让我们通过在之前的散点图上绘制估算值来检查估算器是否工作良好。

看起来不错:所有估算的数据点都在一条线上,这应该是线性回归预测的情况。更重要的是,估算的数据看起来与观察到的数据相似。

感谢阅读!如果你有兴趣学习更高级的插补方法,请查看我在 DataCamp 上教授的 [**课程。**](https://datacamp.com/courses/handling-missing-data-with-imputations-in-r)
如果你喜欢这篇文章,为什么不订阅电子邮件更新我的新文章呢?并且通过 [**成为媒介会员**](https://michaloleszak.medium.com/membership) ,可以支持我的写作,获得其他作者和我自己的所有故事的无限访问权限。
需要咨询?你可以问我任何事情,也可以在这里为我预约 1:1[](http://hiretheauthor.com/michal)**。**
**也可以试试 [**我的其他文章**](https://michaloleszak.github.io/blog/) 中的一篇。不能选择?从这些中选择一个:**
**[](/working-with-amazon-s3-buckets-with-boto3-785252ea22e0) [## 使用 Boto3 处理亚马逊 S3 桶。
### 完整的备忘单。
towardsdatascience.com](/working-with-amazon-s3-buckets-with-boto3-785252ea22e0) [](/uncertainty-from-imputation-8dbb34a19612) [## 插补的不确定性
### 你在预测中考虑到它了吗?
towardsdatascience.com](/uncertainty-from-imputation-8dbb34a19612) [](/a-comparison-of-shrinkage-and-selection-methods-for-linear-regression-ee4dd3a71f16) [## 线性回归中收缩法和选择法的比较
### 详细介绍 7 种流行的收缩和选择方法。
towardsdatascience.com](/a-comparison-of-shrinkage-and-selection-methods-for-linear-regression-ee4dd3a71f16)**
# 用 PyTorch 编写分布式数据并行应用程序
> 原文:<https://towardsdatascience.com/writing-distributed-applications-with-pytorch-f5de4567ed3b?source=collection_archive---------19----------------------->
## 多 GPU 节点的分布式数据并行训练

照片由 Pexels 的 Manuel 拍摄
**概要**
本文的主要目的是用简单的步骤阐明如何利用 PyTorch 实现的分布式计算技术在多个 GPU 上训练深度学习模型。文章假设你熟悉训练深度学习网络。本教程首先介绍一些关于分布式计算的关键概念,然后使用 PyTorch 的分布式数据并行功能编写一个 python 脚本来训练一个具有 4 个 GPU 的模型
> 注意:此处的教程将仅涵盖与分布式培训相关的关键步骤。完整的代码可以在[这里](https://github.com/shreeraman96/distributed_training)找到
**分布式计算——什么是分布式计算?**
分布式系统是通过网络进行通信以实现共同目标的不同独立节点的集合。每台机器被称为一个节点,通过单个网络连接的一群节点形成一个集群。节点利用网络与其对等节点通信。
分布式计算与这样一种编写程序的风格相关联,这种编写程序的风格充分利用了分布在整个体系结构中的计算机的计算能力
**深度学习的分布式计算**
深度学习和分布式系统是最*越来越受欢迎的两个不断发展的系统。计算能力最*几乎呈指数级增长,通过深度神经网络(DNN),研究爱好者正在世界各地创造奇迹。因此,从两者中获益是非常重要的。如果我们利用大规模分布式系统的计算潜力,DNNs 的训练时间可以大大减少,性能也会提高
**优化计算的要点:**
> 1.通过系统和一致的处理跨多个节点分布计算
>
> 2.在对等体之间建立同步
这里的重点是用简单的步骤解释如何利用 PyTorch 分布式模块通过数据并行技术为 BERT 模型进行屏蔽语言建模训练。训练其他模型也可以遵循相同的步骤
**数据并行技术**
这里,数据分布在多个节点上,以实现更快的训练时间。每个节点都应该有自己专用的模型副本、优化器和其他要素。我们将使用 PyTorch 模块的分布式 API 来实现这一点。
**torch . distributed-基本概述**
PyTorch 通过在任何 PyTorch 模型周围提供一个包装类来支持同步分布式训练。
每个流程都有自己的 python 解释器、优化器和模型副本,并在每一步执行完整的优化,从而减少开销时间
PyTorch 还支持不同的通信后端来收集跨多个节点的迭代的每一步的向前和向后传递的结果。
现在让我们深入实际的实现
**步骤 1:** 获取所有可用 GPU 的列表,包括 device _ ids 和可用 GPU 的总数
**步骤 2:** 从任何来源加载用于掩蔽语言建模的句子,并清理和处理数据。
**第三步:**加载一个预先训练好的 BertTokenizer,用它编码所有的句子,创建目标句子。屏蔽输入句子的某些单词以创建屏蔽句子
**步骤 4:** 设置分布式训练所需的不同配置参数,并将其加载到单个实体中,最好是一个类
> **世界规模**:相当于可用 GPU 数量的进程总数
>
> **主地址**:将托管等级为 0 的进程的机器的 IP 地址
>
> **主端口**:节点间通信的空闲可用端口
**步骤 5:** 定义 train()函数:
这是将分布在多个节点上的功能。因此,确保每个节点都有自己的所需变量和设置的专用副本是非常重要的
将在单个实体上收集多个不同的参数和变量,并作为值参数传递给训练函数。
除此之外,训练函数还将接收参数 ***rank*** ,处理器通过该参数来决定它是工作者节点还是主节点。
等级为 0 的节点将是主节点。PyTorch 负责为每个 GPU 分配等级,所以我们不必为此担心
**步骤 5.1** :用合适的后端系统和模型建立流程组
***Init_process*** 允许进程通过共享它们的位置来相互通信和协调。PyTorch 给出了两种指定进程组配置和启动进程组的方法
> 1.指定世界大小、等级和商店(可选)
>
> 2.用 URL 中编码的等级和世界大小或其他方式指定 URL 字符串,以指示在哪里/如何与对等方通信。默认情况下,它将是**“env://”**
init_process 接受 4 个参数:
> 1.**后端**:要使用的通信后端。可用选项:格洛,NCCL,MPI。NCCL 适合 GPU 训练,而 Gloo 更适合 CPU 训练
>
> 2. **Init_method:** 字符串 URL(默认值:env://)
>
> 3. **World_size** :作业中的进程数。通常相当于可用的 GPU 数量
>
> 4.**等级**:当前流程的等级
**步骤 5.3** :加载模型
加载所需的模型。在我们的例子中,加载用于屏蔽语言建模的 BERT 模型(BertForMaskedLM)
**步骤 5.4:** set-GPU device &加载模型到设备中
**步骤 5.6** :用分布式数据并行类包装模型,将模型分布到各个节点
这个容器为我们的 PyTorch 模型提供了一个包装器,并将给定模块的应用并行化,并将输入拆分到指定的设备上。该模块在集群上的每台机器和每台设备上复制,每个副本处理一小部分输入。
> 1.**模块**:要并行化的模块。在我们的案例中,我们案例中使用的模型
>
> 2.**设备标识** : CUDA 设备列表
**步骤 5.7** :为每个副本设置数据加载器
我们设置 *training_sampler* u *使用 DistributedDataSampler()* 包装类来采样和分发每个副本的输入数据。
参数:
> 1.**数据集**:输入数据集
>
> 2.副本数量:在我们的例子中等于世界大小(4)
下一步是用我们定义的分布式采样器来设置 Dataloader。
**步骤 5.8** 定义培训流程的其余部分&保存模型
**第六步:**根据可用的 GPU 数量启动产卵过程。PyTorch 现在将在指定的世界大小和设备列表中生成进程
希望本文为您提供了一个关于如何利用 PyTorch 的分布式 API 在多个 GPU 上实现分布式训练的合理思路。完整的代码可以在这里的资源库中找到。如果您希望深入了解这些模块,我建议您访问以下链接。
[## 分布式通信包- torch.distributed - PyTorch 主文档
### torch.distributed 支持三个后端,每个后端都有不同的功能。下表显示了哪些功能是…
pytorch.org](https://pytorch.org/docs/stable/distributed.html#basics) [](https://pytorch.org/tutorials/intermediate/dist_tuto.html) [## 使用 PyTorch 编写分布式应用程序- PyTorch 教程 1.5.1 文档
### 作者:Séb Arnold 在这个简短的教程中,我们将介绍 PyTorch 的分发包。我们将看到如何…
pytorch.org-](https://pytorch.org/tutorials/intermediate/dist_tuto.html)
# 编写优秀的 SQL
> 原文:<https://towardsdatascience.com/writing-good-sql-ccb578ff9919?source=collection_archive---------7----------------------->
## 通过适配层来进一步结构化查询语言

[国家癌症研究所](https://unsplash.com/@nci?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
你想写出好的 SQL 吗?当然,但是“好”实际上是什么意思?
在某些实时环境中,只有性能被认为是“好的”,并且您以毫秒来度量您的执行时间。在商业智能和数据仓库环境中,性能仍然很重要,但通常可维护性更有价值。这可能是一个更好的解决方案,产生可读和可维护的 SQL 代码,即使它每月都要耗费大量的资金。请记住理解复杂查询的时间成本、潜在错误产生的成本以及修复错误的时间。根据您的数据库引擎优化器,您通常会两者兼得。因此,在本文中,我们关注可读性和可维护性。
我假设您已经知道 SQL 的基本和更高级的元素:连接、分组、子查询或分析函数。结合这些元素,你可以很快产生一个大混乱。但是,我们能做些什么来保持具有数百行和 20 个或更多表格的大型查询的可读性呢?
# 最基本的
当我们说可读性时,我们不得不谈论风格。Simon Holywel l 有一篇很棒的 [SQL 风格指南,看看吧。但是如果你很匆忙,你可以用这三个建议走得很远:](https://www.sqlstyle.guide)
* 对所有保留的关键字使用大写,如 SELECT、FROM 或 WHERE,对列、表和符号名称使用小写
* 通过将关键字对齐到右边,将值对齐到左边,创建一个间隙
SELECT first_name, last_name, birth_date
FROM emp.employees
WHERE gender = ‘F’;
* 每当查询变得比简单的 SELECT/FROM/WHERE 语句复杂时,就使用注释
/* find highest actual salary for each employee */
SELECT first_name, last_name, MAX(salary)
FROM emp.employees e
JOIN emp.salaries s
ON e.emp_no = s.emp_no
WHERE current_date BETWEEN from_date AND to_date -- filter date
GROUP BY first_name, last_name;
有了这些基本规则,您的 SQL 将很容易达到至少中等的可读性水*。随着您的 SQL 变得越来越大和越来越复杂,您将达到一定的极限,在这里您需要比样式指南提供的更多的结构。
# 子查询,或者痛苦从哪里开始
你知道子查询吗?子查询非常适合将问题封装在查询块中。基本上,SELECT 语句中有三种类型的子查询:
* 在 FROM 子句中,作为具有别名的派生表
SELECT COUNT(*)
FROM (SELECT gender
FROM emp.employees) e
* 作为限定符放在 WHERE 子句中
SELECT *
FROM emp.employees e
WHERE 0 < (SELECT COUNT(*)
FROM emp.salaries s
WHERE s.emp_no=e.emp_no
AND salary > 3000)
请注意,这也是一个相关子查询,应该小心使用(参见[https://en.wikipedia.org/wiki/Correlated_subquery](https://en.wikipedia.org/wiki/Correlated_subquery))。
* 标量子查询(也称为相关子查询)
SELECT last_name, (SELECT MAX(salary)
FROM emp.salaries
WHERE emp_no=e.emp_no)
FROM emp.employees e
# 那么问题出在哪里呢?
子查询可以有更多任意深度的子查询。当您从子查询开始时,随着时间的推移,很可能会出现更多的子查询。其他人会以你为榜样。每个子查询都需要缩进,行变得越来越长。这是更复杂语句可读性的主要问题。
更重要的是,每个子查询都将周围的查询分成代码“before”和“after”。

查询中间的子查询
在过程化时代,编码的黄金法则之一是:
> 不要写比你的屏幕大的块
(一个屏幕有 25 行)。但是如果中间有一个子查询,那么在 SELECT 块和 WHERE 子句的列之间可以有数百行。
这导致了一种有趣的阅读 SQL 查询的方式。通常,你从中间的某个地方开始,慢慢地向边缘发展。

带有子查询的读取方向
这种“从里到外”的阅读方向与我们通常的从上到下的阅读方向相反,我们大多数人都很习惯这种阅读方向。
# 的力量
我们中的一些人编写 SQL 多年,但从未使用过 WITH 子句。试试看,值得!
在 WITH 子句中,可以给子查询一个符号名,就像在 FROM 子句中一样,同时将它移到语句的开头。
WITH t_avg_salary AS (
--averagy salary for every employee
SELECT emp_no, AVG(salary) avg_salary
FROM emp.salaries
GROUP BY emp_no
)
SELECT e.last_name, avg_salary
FROM emp.employees e
JOIN t_avg_salary
ON e.emp_no = t_avg_salary.emp_no
WHERE avg_salary < 3000;
并且您可以构建一个子查询的“管道”,这些子查询相互建立
WITH t_avg_salary AS (
-- average salary for every employee
SELECT emp_no, AVG(salary) avg_salary
FROM emp.salaries
GROUP BY emp_no
),
t_avg_salary_3k AS (
-- limit to avg. salary smaller 3k
SELECT *
FROM t_avg_salary
WHERE avg_salary < 3000
),
t_employee AS (
-- Join employee and salary
SELECT e.last_name, avg_salary
FROM emp.employees e
JOIN t_avg_salary_3k
ON e.emp_no = t_avg_salary_3k.emp_no
)
SELECT *
FROM t_employee;
这带来了以下进步:
* 因为每个子查询本身都是一个块,所以功能是相互分离的
* 每个子查询都可以获得自己的标题注释,这明显提高了可理解性
* 你可以自上而下地阅读你的代码,这对可读性很好
* 每个子查询都以固定的缩进量从行首开始。因此,遵循团队的编码风格指南会有更少的问题,该指南通常将行长度限制在 80 个字符以内
* 向查询中添加功能(或更多子查询)不会强制您重新格式化整个查询。这导致了你的版本控制系统的细微变化。例如,取消*均工资限额:
7,12d6
< t_avg_salary_3k AS (
< -- limit to avg. salary smaller 3k
< SELECT *
< FROM t_avg_salary
< WHERE avg_salary < 3000
< ),
17c11
< JOIN t_avg_salary_3k
JOIN t_avg_salary
保持子查询块较小,重点放在单一功能上。不要混合功能,例如连接、过滤、分组、映射。
最后但同样重要的是,使最后一个块非常简单,只有 SELECT 和 FROM。把它想象成一个层模型。第一个模块用于数据采集,最后一个模块仅用于输出。如果你把更多的功能放进去,你会像开始时一样结束。

用多个子查询分层
# 额外收获:构建特性在 SQL 中切换
在大型系统中使用 SQL 时,通常会涉及到变量替换。如果您试图在 SQL 的两个功能之间切换,很快就会得到难看的代码。
使用 WITH,您可以轻松地将数据库名称、表名称或不同的配置参数放入配置节中。而且,您可以使用它来打开和关闭块的使用。
假设您有一个前面讨论过的子查询流。其中一个实现了对“薪金< 3000”的限制,这应该只在您的代码的较新版本中有效。
将最后一个子查询中的连接表替换为变量“t_avg_salary”(此处为 python f-string 格式):
WITH
…
t_emp_join AS (
-- Join employee and salary
SELECT last_name, avg_salary
FROM t_employee e
JOIN t_avg_salary_3k s
ON e.emp_no = s.emp_no
)
SELECT *
FROM t_employee;
“t_avg_salary”的可能值现在是“t_avg_salary”和“t_avg_salary_3k”。
通过更改“t_avg_salary”切换变量,您可以更改 SQL 的输出,而无需更改代码。您是创建一个伪派生表,还是简单地使用下面的派生表取决于您。
简单地跳过中间的子查询。
# 编写更加地道和 Pythonic 化的代码
> 原文:<https://towardsdatascience.com/writing-more-idiomatic-and-pythonic-code-c22e900eaf83?source=collection_archive---------6----------------------->
## 使你的 Python 代码可读、有效、简洁和可靠的习惯用法和惯例。
有许多方法可以实现相同的特性、算法或功能。有些简单明了——更好*,有些混乱低效——更糟*。Python 社区经常使用像*Python*这样的术语,或者在描述遵循特定(自然、恰当)风格和约定的代码时使用。这就是我们每天都在努力编写的好的、清晰的代码,在这篇文章中,我们将回顾一些技巧、约定和习惯用法,它们将帮助你编写更多的*惯用*和*python 式*代码。**
****
**照片由[艾哈迈德·卡希姆](https://unsplash.com/@ahmadkadhim?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄**
# **身份和*等比较**
**不只是在 Python 中,实际上在任何编程语言中,您都可能陷入混淆身份和值相等的陷阱。在 Python 中,你可以选择使用`is`或`==`进行比较,其中`is`检查身份而`==`检查值。**
**考虑到大多数时候我们只关心价值,而不是身份,我们通常会选择`==`。然而,在有些情况下,你应该总是使用`is`操作符。其中之一就是与所有的蟒蛇*单线* - `None`、`True`或`False`进行比较。**
**使用`is None`、`is True`或`is False`不仅仅是为了约定俗成或提高可读性。这也提高了性能,尤其是如果您在循环内部使用`x is None`而不是`x == None`的话。为什么会这样?你可能会问。嗯,这是因为`is`操作符不能像`==`(实际上就是`a.__eq__(b)`)那样被重载,所以 Python 可以跳过对这些 *dunder* 方法的查找,这些方法是使用`==`进行比较所需要的。**
**所以,这里的底线是,你应该尽可能地使用`is`,因为它可读性更强,速度更快,而且更习惯。但是要想知道你*是否真的能*使用它,你应该问问你自己你是否关心被比较变量的价值或同一性。**
# **上下文管理器而不是`try/finally`**
**在其他语言中,常见的做法是使用`try/finally`来管理资源,并确保在发生异常时处理打开的文件或获取的锁。你也可以在 Python 中使用`try/finally`,但是我们可以使用`with`语句做得更好:**
**上面的代码显示了所谓的*上下文协议*的用法,该协议由两个方法组成——分别在*进入*和*退出`with`块的*体时调用的`__enter__`和`__exit__`。你可能已经知道`with`语句及其用法,但你可能不知道上面使用的`contextlib`。它是一个模块,提供了将功能转化为上下文管理器的工具。至于上面的`closing`函数,它只是强制调用对象的`.close()`方法,在本例中是`page`。**
**然而,*上下文协议*的使用并不局限于资源管理。它也可用于抑制异常(`with suppress(...)`)或重定向输出(`with redirect_stdout(...)`):**
# **检查是否提供了参数**
**有时,您可能需要定义带有可选参数的函数。用 Python 可以非常容易地做到这一点,当然知道怎么做:**
**大多数情况下,我们使用可选参数来允许 our 函数的用户省略明显的默认参数或很少使用的选项。但是在某些情况下,我们可能希望改变函数的行为,不仅基于可选参数的值,还基于参数是否被提供。对于这种情况,一个合理的解决方案是使用`None`作为缺省值(当它是`None` do X 时,当它不是 do Y 时)。但是如果`None`是可接受的值呢?您可以选择另一个一次性值,但是有一个很好的*惯用的*解决方案:**
**我们可以通过创建一个名为`_no_value`的常量来解决这个问题,我们将这个常量设置为可选参数的默认值。通过这样做,我们避免了任何可能可接受的值,因为我们实际上根本不检查值——我们检查的是*标识*。换句话说,我们正在检查`y`参数是否与分配给`_no_value`的参数指向同一个对象。**
# **多重赋值**
**Python 的一个很好的特性是多重赋值,这是大多数编程语言所缺乏的。最简单的形式如下:**
**这很好,因为它缩短和简化了代码,但我个人很少有机会使用它。在将可重复项分解成多个变量时,可以使用更实用的版本:**
**与使用索引为每个变量赋值相比,这无疑是更好的选择,因为它产生的视觉噪声更少,更简洁,也更不容易出错。**
# **变量解包**
**基于前面的例子,我们还可以使用*星形表达式*来解包任意长度的 iterable 元素:**
**通常,iterables 中的值会有一些模式或已知的组成部分,使用解包可以很容易地提取出来。这总是比在 iterable 中显式使用索引更好的解决方案,因为这样会创建带有大量未命名和未知变量的不可读代码。**
**不过,在使用*星形表达式*时,有一点需要注意。用*星形表达式*解包总是创建列表,即使变量从解包中接收到零值,考虑到你不需要做任何额外的类型检查,这可能很好,但是接收到`[]`而不是`None`也可能有点令人惊讶。**
**如果我们想扩展这个特性的限制,那么我们甚至可以将多个级别的 iterable 分解成其他 iterable:**
**我不一定推荐这样做,因为这不会产生可读性很好的代码,但是知道我们使用的工具的限制是有好处的,即使我们不经常使用或者根本不使用这个特定的选项。**
# **交换值**
**在其他语言中,你需要额外的变量和 3 行代码来交换 2 个变量。然而在 Python 中,有一种更好的方法类似于前面展示的多重赋值:**
**这是超级简单和超级有用的,它是提醒你 Python 有多棒的特性之一。除了交换变量,这也适用于可变的可重复项(如列表)及其索引,这在排序中很常见:**
**这看起来像是 Python 的魔法,但实际上 Python 足够聪明,知道什么时候创建临时变量,什么时候放入临时变量,在哪里赋值,什么时候丢弃。**
# **并行处理列表**
**例如,在处理数据库或 CSV 表时,您经常会发现自己有多个相关数据列表。它可能是数据库表中的几列,几个相关的数据集,等等。不管数据实际上是什么,您可能都希望使用它并并行处理它。在 Python 中,最简单的方法是使用`zip`:**
**`zip`函数获取可变数量的列表,并生成惰性生成器,该生成器生成包含来自每个所提供列表的元素的元组。这对于处理数据来说很棒,而且非常高效,因为——正如我提到的——生成器是*懒惰的*,所以它不会将整个列表加载到内存中,只加载当前的元素元组。**
**当使用这个函数时,你可能会意识到,当处理不同长度的列表时,它并不那么好,因为它只在列表中最短的*用尽时才产生值,这可能并不总是令人满意的。如果您想消耗值,直到列表中最长的*用完,您可以使用`itertools.zip_longest`,它将使用作为参数提供的`None`或`fillvalue`来填充缺少的值。****
# **避免映射、过滤和减少**
**Python 有许多函数式编程概念和函数,如`lambda`表达式、列表理解、`functools`模块等。然而,有一些是许多人不喜欢的。这些是`map`、`reduce`和`filter`。但是这些功能有什么不好呢?嗯,有多种原因,但我不得不同意的一个原因是,编写列表理解通常比编写`map`或`filter`更干净和清晰,并且在`reduce`的情况下,当与非*凡函数参数一起使用时,代码变得难以阅读。不喜欢这些函数的另一个很好的理由是,理想情况下应该只有一种正确的做事方式,那么当我们有列表理解时,为什么还要使用`map`、`filter`、`reduce`甚至`lambda`?**
**如果你不同意我的观点,这是可以理解的,但在写一些愤怒的评论之前,你可能想读一读 Guido va Rossum 的简短评论,这可能会改变你的想法。**
**底线——尽量少用上面的函数,理想的情况是尽可能用列表理解来代替它们。**
> **“‘reduce’的唯一目的是编写真正混乱的代码,以显示你有多酷。我只是没那么酷。”—吉多·范·罗苏姆**
# **含电池**
**Python 在很长一段时间里都保持着*“包含电池”*的理念,这意味着你会在标准库中找到很多有用的工具、模块和函数,这些都是你意想不到的。你应该经常检查你正试图解决的问题或者你正试图实现的函数是否在标准库中的某个地方,如果你找不到,很可能是你找得不够仔细。**
**这些*【电池】*在标准库中有很多例子,我想到的第一个模块是`itertools`,它提供了迭代器构建块。另一个很棒的是具有高阶函数集合的`functools`,我还必须提到具有非常有用的数据类型的`collections`模块,例如`Counter`、`deque`或`namedtuple`等等。**
**所以,下一次你在程序中需要一些相当常见的功能时,不要重新发明轮子,去看看 [Python 库文档](https://docs.python.org/3/library/),抓住已经有的东西,为自己节省一些时间。**
# **“一帮”习语**
**当你定义一个 Python `class`时,你很可能会在它的`__init__`方法中声明几个属性。您可能只声明一个或两个属性,但也可能以如下形式结束:**
**在`class`中只有几个属性,把它们写出来没什么问题,也不会让你的代码变得那么混乱,但是如果有 10 个左右的属性——就像上面的代码——你还能把它们都写出来吗?嗯,我不会。所以,为了避免它你可以使用所谓的[*【习语】*](http://code.activestate.com/recipes/52308-the-simple-but-handy-collector-of-a-bunch-of-named)*:***
***上面的代码片段演示了`self.__dict__`的用法,这是一个存储了`class`所有属性的字典(除非声明了`__slots__`)。这里,我们将构造函数的任何关键字参数传递给生成所有属性的`update`函数。也可以用`vars(self)`在我看来好看一点。***
***你可能会认为这是一个肮脏的手段,但是我认为使用它是没问题的,特别是如果你有`class`(数据结构)来存储*束*属性,而没有任何真正的功能。另外 [Raymond Hettinger 说](https://stackoverflow.com/a/9205029)可以直接更新实例字典,所以就这样了。***
# **结论**
**我绝对推荐在你的 Python 代码中使用以上所有的习惯用法和技巧,我相信这些会让你的代码更加*Python 化*和*习惯化*。然而,*“什么是 Pythonic?”*或者*“什么是惯用语?”对我有效的,可能对你无效。所以,使用习惯用法使你的代码更易读、简洁和有效,而不仅仅是因为它是习惯用法。以同样的方式,使用语言特有的 Python 特性来改进你的代码,而不仅仅是使它更加 Python 化。***
***本文原帖*[*martinheinz . dev*](https://martinheinz.dev/blog/32?utm_source=tds&utm_medium=referral&utm_campaign=blog_post_32)**
**[](/making-python-programs-blazingly-fast-c1cd79bd1b32) [## 让 Python 程序快得惊人
### 让我们看看我们的 Python 程序的性能,看看如何让它们快 30%!
towardsdatascience.com](/making-python-programs-blazingly-fast-c1cd79bd1b32) [](/tour-of-python-itertools-2af84db18a5e) [## Python Itertools 之旅
### 让我们探索两个伟大的 Python 库——ITER tools 和 more_itertools,看看如何利用它们来处理数据…
towardsdatascience.com](/tour-of-python-itertools-2af84db18a5e) [](/python-tips-and-trick-you-havent-already-seen-37825547544f) [## Python 的技巧和诀窍,你还没有看过
### 有很多关于 Python 中很多很酷的特性的文章,但是还有更多值得讨论的…
towardsdatascience.com](/python-tips-and-trick-you-havent-already-seen-37825547544f)**
# 在介质上写作让我得到了一份数据分析的工作
> 原文:<https://towardsdatascience.com/writing-on-medium-got-me-a-job-in-data-analytics-586564d29264?source=collection_archive---------10----------------------->
## 这是我从工业工程过渡到数据分析和数据科学的故事
F 首先,我想说的是,我写的关于[走向数据科学](https://towardsdatascience.com/)的文章所得到的回应让我受宠若惊。我非常感谢那些关注我、持续阅读我的文章并给我反馈的人们——你们都很棒。
在开始讲述我的故事之前,我还想说明一点:我最*接受的职位名为“全球解决方案分析师”,它实际上是数据分析、一些数据工程、数据科学和一些工业工程的结合。

你好,我是梅根!(照片由[亚当·索罗门](https://unsplash.com/@solomac?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄)
对于那些不了解我个人,或者没有在 LinkedIn 上跟踪我的人,我从 2015 年到 2019 年参加了加州理工大学,并获得了工业工程学士学位。在加州理工大学期间,我有过两次实习,主要集中在制造业的流程改进(在是德科技和特斯拉),目标是为公司省钱。我在两家公司都学到了很多东西,但我真的只是喜欢其中的一些工作。剧透——这是我处理大量数据的工作。

这不是我,但我是金发碧眼的,而且我戴了安全眼镜!(高清[科普](https://unsplash.com/@scienceinhd?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄)
我意识到我不想成为一名事必躬亲、深入工厂车间、掌握流程和修理机器的工业工程师。所以,我决定在毕业时找一份技术咨询的工作来改变现状。
(为一家会计公司)技术咨询并不是我想象中的那样。相比技术和数据,我更多地处理业务/变更管理,这与我寻找的和我认为我的技能所在的领域相反。很明显,我需要为我自己的幸福做出改变,所以我在仅仅 6 个月后就辞职了,这真的超出了我的舒适区,感觉…很可怕。
经过深思熟虑和查看工作描述后,我决定通过[Thinkful.com](http://thinkful.com)参加一个数据科学训练营。这是一个为期 6 个月的项目,我很高兴我抓住了这个机会。我选择的课程是自定进度和完全在线的,所以我可以自由地在对我来说有挑战性或有趣的概念上花更多的时间。我也有额外的时间写中型文章,因为我没有兼职做全职工作。

数据数据数据数据数据。由[克里斯·利维拉尼](https://unsplash.com/@chrisliverani?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄
我开始写中型文章,为申请工作建立我的简历/文件夹。然后我意识到:1。我真的很喜欢写作。似乎缺少适合数据科学初学者的好文章。我阅读(并欣赏)了许多关于统计和特定数据科学技术的技术文章,但我没有看到许多人试图让数据科学概念真正易于非技术或初学者阅读。
我有史以来最受欢迎的文章是关于向一个五岁的孩子解释数据科学概念和机器学习模型的最酷的是:人们开始在 LinkedIn 上联系我,因为他们读了我的文章。后来,因为我的写作,我得到了斯坦利·布莱克和德克尔公司的面试机会,这给了我一份全职工作。说实话,我对整件事还有点震惊。
我有一种感觉,我会收到一些 LinkedIn DM 的咨询,询问我关于转行、获得数据分析工作、参加训练营等方面的建议。我不是一个有经验的专业人士,但我会积极主动地提供我的一般建议:努力工作,利用免费资源,在你想要的领域建立人际关系,并试图找到一种让自己与众不同的方法。无论是写文章,活跃在 LinkedIn 上,建立一个很酷的投资组合网站,发布 YouTube 视频,还是其他什么,你都可以为自己建立一个“品牌”,这对求职真的有帮助。
此外,我相信帮助我改变职业生涯的是突出我的工业工程领域知识作为一种资产。不管你的背景是什么,如果它能让你更好地理解你正在分析的数据,或者更好地,让你根据分析做出改变,那就非常有价值!
请随时在 LinkedIn 上与我联系,但请记住,我可能无法回答您的所有问题。(出于某种原因,我在 LinkedIn 上收到了很多提问,好像我是一个经验丰富的专业人士,或者好像我主动提出成为每个人的私人导师。)并且,无论你处于数据科学职业生涯的哪个阶段,还是媒介作家之旅的哪个阶段,我都祝你好运!
以下是我在过去 6 个月的数据科学训练营中学到的一些其他文章:
[](/i-worked-through-500-data-science-interview-questions-51e2e4dead9d) [## 我研究了 500 多个数据科学面试问题
### 以下是我一路走来学到的东西
towardsdatascience.com](/i-worked-through-500-data-science-interview-questions-51e2e4dead9d) [](https://medium.com/thinkful/enrolling-in-a-boot-camp-here-are-some-dos-and-don-ts-95c93acff5d1) [## 参加新兵训练营?这里有一些注意事项
### 经过 4 个月的数据科学课程,我学到了一些东西
medium.com](https://medium.com/thinkful/enrolling-in-a-boot-camp-here-are-some-dos-and-don-ts-95c93acff5d1)
此外,如果您希望开始或继续学习数据科学,这里有一篇文章提供了大量免费学习资源的链接:
[](/a-compilation-of-free-data-science-resources-7861f572cc85) [## 免费数据科学资源汇编
### 对于那些对学习数据科学感兴趣的人
towardsdatascience.com](/a-compilation-of-free-data-science-resources-7861f572cc85)
一如既往的感谢阅读:)
# Kaggle 比赛后撰写论文/技术报告
> 原文:<https://towardsdatascience.com/writing-papers-tech-reports-after-kaggle-competitions-ee504fc0c4c1?source=collection_archive---------29----------------------->

照片由[帕特里克·福尔](https://unsplash.com/@patrickian4?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/writing?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 拍摄
## **我参加机器学习挑战赛后发表科技论文的经历**
# 关于 Kaggle
[Kaggle](https://www.kaggle.com/) 是最受欢迎的机器学习竞赛*台。它举办免费的课堂竞赛、CVPR 和 NIPS 等会议的挑战、科学竞赛和商业挑战。挑战组织者通常提供数据、评估标准和评估测试集。该测试集用于形成公共排行榜,允许*似比较参与者的模型性能,并在挑战的追逐游戏中驱动游戏化能力。许多人被公众排行榜的追逐所消耗和摧毁,而不是相信他们的本地验证。

图片来自 [Pixabay](https://pixabay.com/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=862274) 的 [morzaszum](https://pixabay.com/users/morzaszum-1241839/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=862274)
Kaggle *台也有讨论、笔记本和数据集,参与者可以分享他们的想法、数据和代码样本。这有助于加快这一进程,因为在一定程度上,这一挑战正得到集体解决。也为学习提供了很大的支持;因此,许多学生和起义机器学习专家选择 Kaggle *台进行首次比赛。

Kaggle 网站界面。
当然还有其他的机器学习比赛*台,比如 [topcoder](https://www.topcoder.com/) 、 [Codalab](https://competitions.codalab.org/competitions/22393) 、 [Zindi](https://zindi.africa/competitions/2030-vision-flood-prediction-in-malawi) 等等【更多是[这里](https://mlcontests.com/)。但是由于缺少笔记本和良好的论坛,他们更倾向于既定的专业人士,而不太适合学生和学习者。因此,Kaggle 通常是练习机器学习的首选。
Kaggle 经常为谷歌云*台提供凭证,可以在有限的挑战时间内使用。荷兰互联网服务提供商 HOSTKEY b . v .([https://www.hostkey.com/gpu-servers#/](https://slack-redir.net/link?url=https%3A%2F%2Fwww.hostkey.com%2Fgpu-servers%23%2F))也可以提供对 GPU 服务器的访问和技术援助。你可以在这里申请他们的资助:【http://landing.hostkey.com/grants】T2。
# 为什么要在 Kaggle 比赛后写论文/技术报告

[安德鲁·尼尔](https://unsplash.com/@andrewtneel?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/writing?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍照
在机器学习竞赛期间,通常会尝试和验证许多方法和想法,通常会为给定的任务产生最先进的模型。太多时候,所有这一切都没有结果,除了前三名的解决方案可能会去挑战组织者那里。这显然是对知识和(通常是巨大的)计算资源的浪费,这很遗憾。
*年来,许多顶级参与者撰写文章描述他们的解决方案,甚至在比赛结束后公开他们的代码。这是一个伟大的创举。然而,简短的记录通常不足以再现结果,Kaggle 风格的代码通常有些混乱,没有注释很难理解。在挑战过程中,每个人都在匆忙追逐排行榜,只有少数参与者为生产质量代码而烦恼。
以下是考虑在比赛后将你的顶级 Kaggle 解决方案模型形成一份详细的技术报告/论文,而不是我们通常看到的一个小时的书面报告的主要原因。
1.这是对人类知识基础的巨大贡献,它确实产生了影响。通常,在竞赛期间,几个想法被实施,许多尝试(和失败)被执行,有时甚至可以在获奖模型中看到 SOTA 解决方案。详细总结这一切将有助于其他研究人员和学生在选择方法、模型架构、损失函数或增强时节省时间。
即使你没有明确地看到影响,它也是存在的——在你论文的所有引用和 GitHub repo 的分叉中。
2.挑战后适当的技术报告/论文迫使你清理你的代码,使其可读和可复制。作为 GitHub 上的一个迷你项目,它看起来很不错,引用了 arXiv 和一个干净的项目代码——你可以把它添加到简历中,在没有 nd a 问题的情况下分享。
3.向 arXiv 投稿没有同行评审程序,而向期刊/会议投稿则有。在 arXiv 上发布技术报告后,您可以将其发送到会议论文集或相关期刊。这件事也发生在我身上:在 arXiv 上发表了一份草稿后,它被邀请到一家 T2 杂志。将你的作品提交给会议和期刊会对你的模型和使用的方法进行适当的同行评审。是的,有时审查意味着你需要做额外的计算,烧蚀研究和重写一半的论文(我知道那种感觉),但值得知道的是,这种反馈使你的研究和技术部分更好。
4.你已经准备好了一切:数据分析、工作管道代码、你的结果甚至数字。挑战结束后直接把它们放在一起会更容易。
5.最后是很好的长期公关。Kaggle post 每天都很受欢迎,一篇好的同行评审论文会持续很长时间,它会出现在 arXiv 和相关的会议记录上,从而为你和你的公司(如果你有的话)创造一个长期的公关。
# 竞赛后写论文/技术报告的一些顾虑

图片由 [Gerd Altmann](https://pixabay.com/users/geralt-9301/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=513529) 从 [Pixabay](https://pixabay.com/photos/despair-alone-being-alone-archetype-513529/) 拍摄
1. 天下没有免费的午餐。写一份技术报告或论文意味着花时间制作适当的文件,浏览相关文献,回顾当前的艺术水*,详述你的方法,绘制结果,起草文本等等。这需要时间。找到对此感兴趣的合作者是有用的,即使他们没有参与挑战。
2. 做一份合适的论文通常意味着重新运行你在挑战追逐中快速做出的所有计算,所有这些尝试和失败现在都需要重现,消融研究也需要执行。在研究中没有失败,只有有价值的信息,所以是的,你需要运行所有那些未改进的模型来衡量你的改进的影响。这意味着更多的计算,更多的尝试和系统的研究。有时,你可能会找到一个比你提交的挑战更好的解决方案。很正常。
3. 经历会议/期刊投稿和所有这些同行评审过程有时真的很痛苦。有时审查意味着你需要做额外的计算,烧蚀研究和重写一半的论文。记住,它让你工作得更好。它教会你耐心。
4. 有时候,在竞争追逐之后,你太累了,以至于不能再看一遍代码,让它变得干净,添加所有那些文档字符串和注释。这也很正常。但是记住,编写一个好的代码并以 GitHub repo 的形式打开它也有助于你的简历。
5. 同行评审过程可能会遭到拒绝。不要对此过于沮丧:考虑审稿人的反馈,然后重新提交给更相关的期刊或会议。

图片由来自 [Pixabay](https://pixabay.com/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=413690) 的 [Ryan McGuire](https://pixabay.com/users/RyanMcGuire-123690/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=413690) 拍摄
去年,我成功地提交了两篇关于挑战的技术论文。我在 [arXiv](https://arxiv.org/abs/1909.05032) 和 [GitHub](https://github.com/tatigabru/kaggle-rsna) 上发表了它们。一篇论文被邀请到天文学会杂志,第二篇论文被 CVPR 研讨会接受。以下是如何实现这一目标的分步指南。
# 赛后撰写论文/技术报告的步骤
1. 做你的研究当前的艺术状态,用于类似任务的方法,阅读最*的相关论文。最有可能的是,你已经在一次竞赛中尝试实施这些论文中的想法。现在,是时候把东西放在一起,参考所有以前的工作,并在引言中用几句话形成一个概述。
关于深度学习的新方法的最新信息,一个很好的来源是[**papers with code**](https://paperswithcode.com/)。它提供了带有可用代码的出版物参考,并提供了流行数据集的最新排行榜。
2.清理代码并添加注释。您可以考虑使用 [**黑色**](https://black.readthedocs.io/en/stable/) 进行样式格式化, [**isort**](https://github.com/timothycrosley/isort) 进行导入排序,[**flake 8**](https://flake8.pycqa.org/en/latest/)**和**[**Mypy**](https://mypy.readthedocs.io/en/stable/)进行检查。这篇博文提供了如何让你的代码更具可读性的有用信息。编写一个自述文件,说明如何设置环境和下载数据,如何训练模型和再现实验。****
********
****图片来自 [Pixabay](https://pixabay.com/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=4420676) 的[丹尼尔·基尔希](https://pixabay.com/users/danielkirsch-4218687/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=4420676)****
****3.做消融研究。修正所有的随机种子和超参数,对所有有用的和不太成功的技巧重复计算,一个一个地引入变化,重复所有你尝试过但被拒绝的尝试,记录每次的度量。这将给你一个关于你所实现的模型改进的真正贡献的消融研究。****
****4.浏览 Kaggle 上分享的其他顶级解决方案,联系获胜者,询问如何使用他们的想法/代码作为参考。值得在您的消融研究中添加他们的改进,并在论文讨论环节中纳入他们的想法。总的来说,Kagglers 对此非常开放:我询问了大约 3-4 个人关于使用他们的参考内容的问题,并且总是得到许可。你甚至可以进一步改进你的模型,然后迟些提交参赛作品。Kaggle *台允许迟交。****
****5.制作一个数据集描述,准备几个说明数据的图表,显示数据类别分布,给出所有使用的额外数据的来源。它应该遵循介绍。描述给定的任务和使用的指标。****
****6.当您完成消融研究,用改进的方法重新训练您的模型,并在排行榜上取得好成绩时,是时候写下来了。描述您使用的模型、您如何分割数据、使用的超参数以及培训过程。简洁明了,参考你的 GitHub repo。不要忘记引用你在文中使用的所有资源。****
****7.写下你的结果。制作一些图表,说明不同模型的训练和数据扩充;添加一个表格,列出您的实验和消融研究的评分结果。****
****8.最后,反思你的结果。你可以添加一个讨论区,也可以添加失败的尝试,提到什么没有成功。此外,您可以将其他参与者分享的观点纳入本部分。****
****9.不要忘记一个致谢环节,感谢挑战组织者、Kaggle *台和任何你认为有用的东西。****
****10.要制作纸张样式,您可以使用 LaTex 编辑器(即 WinEdit)或在线编辑器(即背面),并从背面的[下载不同的样式。一旦完成,用](https://www.overleaf.com/latex/templates.)[从语法上](https://www.grammarly.com/)检查你的文本是个好主意。****
****11.首先向 arXiv 提交您的作品。然后,确定相关的会议和期刊,在选择会议时注意即将到来的提交截止日期,阅读他们对作者的说明,下载他们的 latex 模板,并将您的内容复制到其中。准备好后,通过会议或期刊网站提交,等待评审。****
****同行评审过程可能会遭到拒绝。不要对此过于沮丧:每个期刊/会议都有它的范围。尽可能遵循评论者的指导方针,并考虑他们的反馈。然后,将改进后的文章重新提交给另一个相关的期刊或会议。****
****经过所有的工作,一些挫折和改进,你的工作会看到世界。到那时,它将更具可读性和实用性。还有,很有可能,会让你开心:)。对于你的简历和 GitHub repo 来说都会是一笔很好的资产。****
********
****图片来自 [StartupStockPhotos](https://pixabay.com/users/StartupStockPhotos-690514/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=593313) 来自 [Pixabay](https://pixabay.com/photos/children-win-success-video-game-593313/)****
# 撰写专业数据科学文档
> 原文:<https://towardsdatascience.com/writing-professsional-data-science-documentation-1141737836fa?source=collection_archive---------57----------------------->

安妮·斯普拉特在 [Unsplash](https://unsplash.com/s/photos/stash-paper?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上的照片
文档是成为数据科学家的重要组成部分。我建议使用 R Markdown 和 LaTeX 来记录数据科学模型。
好的文档有几个可取的特性
* 容易接*
* 易读的
* 跨项目保持一致
* 可再生的
在 R Markdown 中使用 LaTeX 允许用户在多个项目中使用一致的 LaTeX 格式,编写解释给定模型的专业数学公式,一致地引用图形/文章,并从模型的输出中动态地生成图形。
LaTeX 是一个文档准备系统,最初是为学者设计的,用于在科学出版物的格式上引入一致性。它还可以成功地用于记录机器学习模型。
为了使用它,你需要安装 LaTeX 发行版。不需要安装完整版的 LaTeX。可以使用 R 包 tinytex 来呈现带有 LaTeX 代码的文档。
tinytex::install_tinytex()
要安装缺失的 LaTeX 库,可以使用 tinytext 通过粘贴错误消息作为参数来安装。
tinytex::parse_install()
例如,您可以通过日志文件来修复所有缺失的库错误。
tinytex::parse_install("filename.log")
LaTex 在 R Markdown 中的主要用途是键入数学公式和编号方程,添加引用。这是我为格兰杰因果关系键入的代码示例。
\begin{equation}\begin{pmatrix}U_{t} \OLI_{t} \\end{pmatrix}=\begin{pmatrix}\alpha_{1} \\alpha_{2} \\end{pmatrix}+\begin{bmatrix}\delta_{1,1,1} & \delta_{1,1,2} \\delta_{1,2,1} & \delta_{1,2,2} \\end{bmatrix}\begin{pmatrix}U_{t-1} \OLI_{t-1} \\end{pmatrix}+ ... +\begin{bmatrix}\delta_{p,1,1} & \delta_{p,1,2} \\delta_{p,2,1} & \delta_{p,2,2} \\end{bmatrix}\begin{pmatrix}U_{t-p} \OLI_{t-p} \\end{pmatrix}+\begin{pmatrix}\eta_{1t} \\eta_{2t} \\end{pmatrix}\end{equation}
这就是它在渲染文档中的样子。

图 1:格兰杰因果关系
我意识到上面的代码可能看起来很吓人。LaTeX 可能有一个陡峭的学习曲线,但是一旦你在谷歌的帮助下键入几个第一方程,它就会变得明显更容易和更自动化。
R Markdown 允许您将文档划分为节/子节,添加标题,添加目录,并生成可以记录给定模型输出的图表。您可以在代码块中编写 R/Python 代码,并在代码块之间编写 LaTeX 公式和文本。R Markdown 文档易于编辑和呈现。要了解更多关于 R Markdown、设置参数和渲染 HTML/pdf 的信息,我推荐使用 R Markdown Cookbook[https://bookdown . org/易慧/rmarkdown-Cookbook/rmarkdown-process . HTML](https://bookdown.org/yihui/rmarkdown-cookbook/rmarkdown-process.html)
为 agile R Markdown 文档提供 LaTeX 的一致性和可能性,为您提供了一个记录数据科学模型的强大工具,这对于未来从事您的项目的数据科学家以及希望了解您的工作的更多细节的技术利益相关者来说至关重要。
我建议将 R Markdown 文档和呈现的 HTML/PDF 一起包含在您的项目 git 存储库中,在 documentation 文件夹中。这将允许您轻松访问模型的更多技术细节,并进行任何必要的更正。此外,您只能在渲染 pdf 时使用 LaTeX。否则,请随意使用 HTML。
LaTeX 和 R Markdown 的有用资源列表
[1][https://book down . org/易慧/rmarkdown-cookbook/rmarkdown-process . html](https://bookdown.org/yihui/rmarkdown-cookbook/rmarkdown-process.html)
[2]https://bookdown.org/yihui/rmarkdown/pdf-document.html
[3]https://wch.github.io/latexsheet/
[4][https://www . over leaf . com/learn/latex/书目 _ 管理 _with_bibtex](https://www.overleaf.com/learn/latex/bibliography_management_with_bibtex)
# 如何编写 TensorFlow 2 自定义循环
> 原文:<https://towardsdatascience.com/writing-tensorflow-2-custom-loops-438b1ab6eb6c?source=collection_archive---------19----------------------->
## 从 Keras 到 TensorFlow 2 的分步指南

由 [Georg Eiermann](https://unsplash.com/@georgeiermann?utm_source=medium&utm_medium=referral) 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄
我使用 Keras 已经有一段时间了,我从来没有找到一个好的理由去其他地方。TensorFlow 1 笨拙,PyTorch 虽然性感,但不太懂我。我倾向于做原型的时候学的最多,Keras 是这里的王者。快进到 2019 年 9 月,TensorFlow 2 发布。不久之后,我把所有的`import keras`通话都切换到了`import tensorflow.keras`。
如果这篇文章只是关于从 Keras 迁移到 TensorFlow 2,它可以在这里结束。真的。然而,在 TensorFlow 2 附带的所有功能中,自定义训练循环无疑是 Keras 用户的最佳新功能。在本文中,我解释了它们为什么重要以及如何实现。
**免责声明:**我已经将所有代码添加为图片,因为它在智能手机上显示得更好。[所有代码都可以作为 GitHub gist](https://gist.github.com/nuzrub/f1527654572b3e2da5125d0581e7bdad)获得。如果你知道或者更喜欢发布代码的其他替代方法,请写信给我。
# 张量流 2
对于外行人来说,TensorFlow 2 的问题在于,它抛弃了 TensorFlow 1 的大多数习惯用法和 Keras 上的几个 API,Keras 是唯一一个用于定义神经网络的 API。此外,他们热衷于执行死刑。
这两个变化一起解决了早期张量流的许多批评:
1. **不再有几个 API。**
2. **不需要手动管理变量。**
3. **会话没了。**
4. **可以调试执行流程。**
5. 动态模型现在是可能的。
与此同时,他们成功实现了这一点,同时保留了使 TensorFlow 1 闻名遐迩的部署能力和速度。
对于 Keras 用户来说,新的 API 意味着四件事:
1. 我们不再处于抽象层。
2. 创作新技术要容易得多。
3. 编写定制的训练循环现在是可行的。
4. 执行速度大大加快。
其中,自定义循环是 TensorFlow 2 对 Keras 用户如此重要的原因。定制循环提供了对训练的最终控制,同时使训练速度提高了约 30%。
> 老实说,TensorFlow 2 更好的名字应该是 Keras 3。
我知道一些谷歌开发人员可能会在我写这篇文章的时候死去,但这是事实。TensorFlow 1 是 Keras 的后端,在升级后基本保持不变。大多数新事物都比张量流更经典。
# 训练循环
本质上,所有 Keras 计划都遵循以下结构:

这一切都从导入开始,然后是数据集加载和准备、模型加载/创建,最后是编译和拟合调用。Keras 独有的编译方法将模型与损失函数和优化器相关联,而拟合函数执行所谓的“训练循环”
训练循环是将整个训练集逐批提供给算法、计算损失、其梯度并应用优化器的代码。然后,验证集被用于计算验证损失和验证指标。最后,整个过程重复几个“时期”
在训练循环期间,fit 方法还执行其他功能,例如操作几个工作线程、对模型进行检查点操作以及将结果记录到磁盘。自定义代码也可以通过回调在特定事件中插入。
> Keras 用户习惯于根本不用编写任何训练循环。fit 函数完美地完成了这一切,并允许对大多数用例进行适当的定制
固定训练循环的问题是,当不同的用例出现时,你就不走运了。最初的 Keras 很少提供定制的训练循环。
例子可以很容易地在 GAN 教程中找到。为了训练敌对网络,你必须交错他们的训练。为此,`train_on_batch` 方法是交错生成器和鉴别器训练的最佳方法,让您手动批处理和混洗数据集,编写您的进度条形码,并将其打包到每个历元的 For 循环中。
在 2017 年, [Wasserstein GAN](https://arxiv.org/abs/1701.07875) 被提出,它要求在训练中加入梯度裁剪,这是一个简单的要求。为了保持文明,Keras 中的 WGAN 实现是臃肿的。
这些都不是孤立的例子。任何多网络设置、渐变技巧或开箱即用的解决方案都可能需要您编写大量代码。这就是为什么 Keras 在研究者中如此不受欢迎(也是为什么 PyTorch 如此受欢迎)。
# 自定义循环
TensorFlow 2 为 Keras 用户带来的是打开`train_on_batch` 调用的能力,公开损失、梯度和优化器调用。然而,要使用它,您必须放弃编译和拟合功能。
从好的方面来看,Keras 不再是 TensorFlow 上的抽象。它现在是它的一部分了。这意味着我们不再需要在 Keras 中创建定制逻辑的所有奇怪的东西。一切兼容。我们不再需要。

首先,您必须自己获得损失和优化器对象。然后,定义 train_on_batch 调用。在我看来这很像 PyTorch 代码。你只需调用传递 x 的方法来得到ŷ,然后与 y 比较,得到损失值。
这一切都发生在梯度带的环境中,梯度带只是一种跟踪应该区分哪些操作的方法。使用磁带,我们可以得到关于每个训练变量损失的梯度。然后,梯度变量对被馈送到优化器,优化器将更新网络。
最后一行只是一个例子,说明如何提取一批 128 个样本来调试我们的新方法。
它的孪生兄弟`validate_on_batch`,只是它的一个简单版本。我们只需要摆脱胶带和梯度逻辑。

本质上,它是我们在渐变磁带中找到的相同代码,但是没有磁带(和渐变/优化器逻辑)。
**关于“训练=真”和“训练=假”参数的说明:**这改变了一些层的行为。例如, *dropout* 层在训练期间会丢弃一些连接,但在测试期间不会。这不会直接影响优化或任何其他与训练相关的任务。
完整训练循环的最小示例如下所示:

至此,我们有了一个最小的工作示例。然而,它缺少几个基本特征:
1. 当前的批处理逻辑笨拙且容易出错。
2. **除了每个时期打印一张以外,没有进度指示器。**
3. **没有模型检查点逻辑。**
这使我们想到以下一点:
> 做自己的循环很好,但是它也需要你重新编码一些你认为理所当然的特性
谢天谢地,改进它并不难:)
# 改善环路
首先要注意的是`tf.data`包。它包含 tf.data.Dataset 类,该类封装了几个数据集任务。
数据集 API 使用“流畅的风格”这意味着对 Dataset 对象的所有调用都返回另一个 Dataset 对象。这使得链接调用成为可能。下面是一个用它来解决我们的问题的例子:

这些调用为定型和测试数据创建一个 dataset 对象,为洗牌做准备,并对实例进行批处理。在我们的循环中,笨拙的批处理代码如下所示:

整洁多了。新代码只需枚举数据集中的所有批次。这也为我们处理洗牌,这总是一个好主意。
为了让我们的代码看起来更加生动,我们可以添加一些打印语句。我们还可以使我们的验证逻辑更短一些:

现在我们打印当前的纪元和批次,以及一些点来保持它的移动。为了验证,我们创建了一个每批*均损耗的列表,然后打印出最终的*均损耗。这是它在控制台中的样子:

对于模型检查点,我们必须跟踪到目前为止最好的验证准确性,以便我们只保存提高了我们性能的模型。这要求我们跟踪最佳损失,并将新的损失与之进行比较。就代码而言,这变成如下:

这完成了我们的目标,即改进数据集处理,看到更频繁的屏幕更新,并在训练时保存模型检查点。
最后一步是让它具有性能。
# tf .函数
到目前为止,我们正在所谓的“渴望模式”下运行这意味着如果你写“a + b ”,求和的结果会立即计算出来。虽然这简化了调试,但不是性能最好的方法。
深度学习发生在 GPU 上。每个“求和”和“相乘”命令都有成本。CPU 必须调用 GPU 并告诉哪些变量需要操作,等待操作完成,并取回结果。*这是慢*。一种更快的方法是给 GPU 一大堆要做的事情,并且只等待一次。
这种替代方法被称为“延迟模式”或“计算图”这个想法是让 TensorFlow 把你的网络变成一组对数据进行运算的数学步骤。这个命令列表再发送到 GPU,整体处理,速度快很多。
创建命令列表对于优化也是至关重要的。InceptionNet 等模型有多条路径,可以并行计算。简单的运算可以合并,例如乘法后接加法,等等。
要让 TensorFlow 为您构建这个图,您只需要用@tf.function 注释来注释`train_on_batch` 和`validate_on_batch` 调用。就这么简单:

第一次调用这两个函数时,TensorFlow 将解析其代码并构建关联的图。这将比*常花费更长的时间,但会使所有后续的调用明显更快。
一个很好的方法是在里面放一个 print 语句。在计算图形构建期间,打印将仅执行一次或两次。然后,它将不再打印,因为该函数不再被调用。
**在数值上,使用 RTX 2070 GPU,原始 Keras fit 函数需要 18 秒,自定义循环需要 40 秒,优化循环需要 20 秒。**这个简单的注释使它的速度比急切模式快了一倍。与 Keras fit 相比,它慢了 2 秒,显示了原始 fit 的优化程度。对于较大的问题和网络,优化的自定义循环优于原始拟合。在实践中,我发现使用定制循环可以提高 30%的纪元速度。
使用@tf.function 注释的缺点是它的错误消息很糟糕。经验法则是先在没有它的情况下开发,然后添加它只是为了验证,然后再添加到培训中。这样,您可以更容易地确定错误。
# 后续步骤
切换到自定义循环的用户正在不断改进他们的工作流程。例如,我已经在我的自定义代码中实现了回调和度量,以及一个漂亮的进度条。在互联网上,你可以找到一些软件包,如 [TQDM](https://pypi.org/project/tqdm/) 或非常 Keras [进度条](https://www.tensorflow.org/api_docs/python/tf/keras/utils/Progbar)。
实现自定义循环看起来像是额外的工作,但是它很快就有了回报,并且您只需要做一次。它允许您完全控制何时进行验证、计算哪些指标、复杂的培训计划等等。就模型而言,篡改训练过程要容易得多。您可以添加梯度惩罚,训练几个模型,或轻松创建虚拟批次。
事实上,PyTorch 用户多年来一直在这么做。
我把如何实现这些留给了你。如果你有任何问题,欢迎在评论区提问。
在我离开之前,有一个好消息即将发布。T **ensorflow 2.2 将允许您将自己的** `**train_on_batch**` **和** `**validate_on_batch**` **函数反馈给原始函数。安装 API** 。这意味着我们将两全其美。fit 调用将更加模块化,同时我们保留从头开始实现它的可能性。
这篇文章的代码可以在[这里](https://gist.github.com/nuzrub/f1527654572b3e2da5125d0581e7bdad)找到。
**更新:我最*发表了一份关于 TensorFlow 和 Keras 的技巧列表。在那里,我描述了如何创建定制的训练循环,而不需要重新实现整个。适合功能。**
[](/taking-keras-and-tensorflow-to-the-next-level-c73466e829d3) [## 让 Keras 和 TensorFlow 更上一层楼
### 充分利用 Keras 和 TensorFlow 的 11 个技巧和诀窍
towardsdatascience.com](/taking-keras-and-tensorflow-to-the-next-level-c73466e829d3)
如果您对本文有任何问题,请随时发表评论或与我联系。如果你刚接触媒体,我强烈推荐[订阅](https://ygorserpa.medium.com/membership)。对于数据和 IT 专业人士来说,中型文章是 [StackOverflow](https://stackoverflow.com/) 的完美搭档,对于新手来说更是如此。注册时请考虑使用[我的会员链接。](https://ygorserpa.medium.com/membership)
感谢阅读:)
# 编写你的第一个人工智能/机器学习程序
> 原文:<https://towardsdatascience.com/writing-your-first-ai-machine-learning-program-92a590df86de?source=collection_archive---------17----------------------->
## 用于分类的鸢尾花数据集上的多项式逻辑回归

来源:[https://pix abay . com/photos/work-typing-computer-notebook-731198/](https://pixabay.com/photos/work-typing-computer-notebook-731198/)
你可能已经学到了很多关于人工智能和机器学习的理论,并且觉得很有趣。但是没有什么比看到模型和算法在真实数据上工作并产生结果更好的了,不是吗?有很多资料教你如何着手编写你的第一个 ML 程序,但我发现在大多数情况下,没有多少关于如何着手解决特定问题的分步指导。写得好的代码到处都是,但是缺乏方法论,这和学习如何写程序一样重要。所以我决定写一篇著名的《人工智能世界入门》——鸢尾花分类问题。
# 使用的数据集和算法
Iris flowers 数据集基本上包含 150 种花的实例。每朵花有 4 个属性:萼片长度、萼片宽度、花瓣长度和花瓣宽度(每一个都以厘米为单位),它可能属于 3 个可能的类别:刚毛花、蠕虫花和海滨花。更多信息,请访问网站:【https://archive.ics.uci.edu/ml/datasets/iris
我们的目标是开发一个分类器,它可以基于 4 个特征将花准确地分类为 3 类中的一类。有很多算法可以做到这一点,但是因为这是你的第一个人工智能程序,让我们坚持一个最简单的算法,它也可以在这个数据集上相对较好地工作——逻辑回归。如果你刚开始学习机器,不知道它是什么,可以看看 Andrew NG 的流行的机器学习课程中的这一系列讲座。它们是一个完美的开始方式。如果你不熟悉正则化,也可以看看下一个系列的讲座,即第 7 讲。
如果你已经精通线性代数、一元和多元微积分、概率以及统计,并且想要更深入的数学理解,那么看看斯坦福在线的这个视频
是啊,我有点喜欢吴君如!总之,总结一下逻辑回归的作用,假设我们有以下数据集-

2D 数据集的散点图
旁边的数字显示了学生在两次考试中的录取分数,以及他们是否根据分数被学校/学院录取。逻辑回归所做的是,它试图找到一个线性决策边界,以最好地区分类别。

逻辑回归决策边界
正如我们所看到的,LR 很好地分离了这两个类。但是如果有两个以上的类呢,就像我们的例子一样?在这种情况下,我们为 n 个类别中的每个类别训练 n 个分类器,并让每个分类器尝试将一个类别从其余类别中分离出来。这种技术被正确地称为“一个对所有的分类”。最后,对于实例的预测,我们选择检测到其类最强的分类器,并将该类指定为预测类。这里的决策界限有点难以想象,但这里有一个来自 Sklearn 的例子

来源:[https://sci kit-learn . org/stable/auto _ examples/linear _ model/plot _ iris _ logistic . html](https://scikit-learn.org/stable/auto_examples/linear_model/plot_iris_logistic.html)
# 入门指南
好了,必要条件讲够了,现在让我们开始写代码。当然,我们将使用 Python 以及以下模块:Numpy、Matplotlib、Seaborn 和 Sklearn。如果你像我一样在你的电脑上工作,确保你已经安装了这些模块。要安装它们,只需打开命令行/终端,输入命令“pip install <module-name>”。要检查安装了哪些模块,请输入“python”,然后输入“help('modules ')”以生成所有已安装模块的列表。</module-name>
# 数据分析和可视化
首先,我们需要了解数据集中数据的分布情况。为此,让我们导入 matplotlib 和 seaborn 模块,并加载包含在 seaborn 模块中的 iris 数据集。
import matplotlib.pyplot as plt
import seaborn as sns
iris = sns.load_dataset(‘iris’)
这将把数据集加载到数据框架‘iris’中,这将使可视化更加容易。由于数据集有 4 个特征,我们不能直接将其可视化,但我们可以通过 seaborn 强大的 pairplot 获得一些直觉。pairplot 的作用是在数据集中的每对变量之间绘制散点图,以及每个变量的值的分布(沿对角线图形)。这可以用来分析哪些特征是数据的更好的分离器,哪些特征是密切相关的,以及数据分布。
sns.pairplot(iris, hue='species') #hue parameter colors distinct species
plt.show()

Iris 数据集上的 Seaborn Pairplot
Seaborn 的另一个强大工具是相关性热图。相关性是两个变量相互依赖程度的统计度量。如果一个变量的增加导致另一个变量的粗略增加,反之亦然,那么他们被称为正相关。如果一个变量的增加导致另一个变量的减少,反之亦然,它们是负相关的,如果它们之间没有这种相关性,它们就没有相关性。这就是我们如何用 Seaborn 绘制相关性图
sns.heatmap(iris.corr(), annot=True, cmap='coolwarm')
plt.show()
iris.corr()生成一个 4*4 矩阵,每个条目(I,j)代表 iᵗʰ和 jᵗʰ变量之间的相关性。显然,矩阵是对称的,因为 I 和 j 之间的依赖关系与 j 和 I 之间的依赖关系是一样的(希望这也适用于人类)。对角线元素是 1,因为一个变量显然完全依赖于它自己。annot 参数决定是否在热图顶部显示相关值,colormap 设置为 coolwarm,这意味着低值为蓝色,高值为红色。

Iris 数据集上的相关热图
# 数据准备和预处理
现在我们对数据有了一个直觉,让我们将数据装载到 numpy 数组中并对其进行预处理。预处理是至关重要的一步,它可以极大地提高算法的性能。我们导入所需的模块,以字典的形式从 sklearn 的 iris datasets 类中加载数据集,将特性和目标分别放入 numpy 数组 X 和 Y 中,并将类名放入 names 中。由于数据集中指定的目标是 0、1 和 2,代表 3 类物种,我们希望从这些整数映射到它们所代表的类的名称。为此,我们创建了一个从整数 0,1,2 到花名的字典映射。
import numpy as np
from sklearn.datasets import load_iris
dataset = load_iris()
X, Y, names = dataset['data'], dataset['target'], dataset['target_names']
target = dict(zip(np.array([0, 1, 2]), names))
让我们前进到预处理步骤。我们使用 sklearn 的 train_test_split 将数据分为训练集和测试集,使用 25%的测试集大小,然后使用 sklearn 的 StandardScaler 类。StandardScaler 所做的是计算数据集中每个要素的*均值和标准差,然后减去*均值并除以数据集中每个示例的标准差。这使得所有特征具有零均值和单位标准差/方差。将所有要素纳入同一范围也称为要素归一化,它有助于梯度下降等优化算法正常工作并更快收敛。
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.25)
scaler = StandardScaler() #create object of StandardScaler class
scaler.fit(X_train) #fit it to the training set
X_train_pr = scaler.transform(X_train) #apply normalization to the training set
# 建立模型并评估它
是时候做真正的交易了。现在你会觉得非常感激 sklearn 让你的生活变得简单。我们创建了一个名为 model 的 LogisticRegression 类的对象,并使我们的训练数据集适合它,所有这些都使用了 3 行代码
from sklearn.linear_model import LogisticRegression
model = LogisticRegression()
model.fit(X_train_pr, Y_train)
现在,为了进行预测,我们首先使用标准定标器对测试集进行归一化。注意,该变换与应用于训练集(我们将缩放器安装到该训练集)的变换相同。这很重要,因为我们必须根据训练集的*均值和标准偏差来标准化测试集,而不是测试集。然后,我们使用预测函数在训练集和测试集上生成预测的 1D 数组。sklearn 的度量类中有许多可用的度量,但我们将使用 3 个最相关的度量——准确性、分类报告和混淆矩阵。
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
X_test_pr = scaler.transform(X_test)
Y_train_pred, Y_test_pred = model.predict(X_train_pr), model.predict(X_test_pr)
print(accuracy_score(Y_train, Y_train_pred))
print(accuracy_score(Y_test, Y_test_pred))
print(classification_report(Y_test, Y_test_pred))
print(confusion_matrix(Y_test, Y_test_pred))
数据集非常小,因此,每次训练模型时,您可能会得到非常不同的结果。这是我得到的输出-

训练模型的评估
如果你不理解你看到的任何指标,请随意搜索它们,它们非常简单易懂。训练集精度约为 95.5%,测试集精度约为 94.7%。测试集中所有不同类别的 F1 分数的加权*均值是 0.95,并且测试集中的 2 个实例被错误地分类。第一次训练集上 94.7%的准确率确实很好,但我们可以做得更好。
# 改进模型
下面介绍多项式逻辑回归。事实证明,我们可以通过在模型中引入多项式特征来建立一个更好的模型,就像 x₁、x₁x₂、x₂那样,产生一个非线性的决策边界来更好地划分类别。这种模式比较实用。例如,让我们看看下面的数据集

2D 数据集的散点图
这个数据集看起来更实用一点,不是吗?它显示了在不同微芯片上的测试成绩和它们所属的类别。很明显,没有一个线性的决策边界能够令人满意地分离数据。使用多项式逻辑回归,我们可以得到一个决策边界,如下所示,它做得更好。

多项式逻辑回归决策边界
图中的λ= 1 指的是正则化参数。在处理非线性模型时,正则化变得非常重要,因为您不想过度适应训练集,也不想让您的模型在它没有见过的新示例上表现不佳。
要将多项式要素添加到数据集中,我们使用 sklearn 中的多项式要素类。这将作为附加步骤进入您的预处理,类似于特征标准化步骤。我们增加了 5 次幂项,你可以试着改变它,看看它对你的准确度有什么影响。结果是模型开始过度适应更高的程度,我们不希望这样。
from sklearn.preprocessing import PolynomialFeatures
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.25)
poly = PolynomialFeatures(degree = 5)
poly.fit(X_train)
X_train_pr = poly.transform(X_train)
scaler = StandardScaler()
scaler.fit(X_train_pr)
X_train_pr = scaler.transform(X_train_pr)
然后继续上面讨论的相同步骤来拟合模型。要进行预测,不要忘记首先使用 poly 转换测试集。
X_test_pr = poly.transform(X_test)
X_test_pr = scaler.transform(X_test_pr)
Y_train_pred, Y_test_pred = model.predict(X_train_pr), model.predict(X_test_pr)
然后使用上面讨论的指标评估模型。同样,每次训练模型的结果都会不同。这是我的输出-

改进模型的评估
相当不错的成绩!使用像逻辑回归这样的简单算法,大约 98.2%的训练集精度和大约 97.4%的测试集精度是非常好的。测试集的加权 F1 分数已提高到 0.97,并且该模型仅错误地分类了测试集中的 1 个示例。
# 生成预测
是时候使用模型来生成预测了。我们给它一组 4 个数字——代表一朵花的 4 个特征,它会告诉我们它认为它属于哪个物种。我们从数据集中随机选取 20 个例子,对它们进行预处理,得到预测类。
indices = np.random.randint(150, size=20)
X_pred, Y_true = X[indices], Y[indices]
X_pred_pr = poly.transform(X_pred)
X_pred_pr = scaler.transform(X_pred_pr)
Y_pred = model.predict(X_pred_pr)
注意,Y_true 表示示例的真实类,Y_pred 是预测类。它们都是整数,因为模型处理的是数字数据。我们现在将创建 2 个列表,使用我们已经创建的名为 target 的字典映射来存储每个例子中物种的真实名称和预测名称。
target_true, target_pred = [], []
for i in range(len(Y_true)):
target_true.append(target[Y_true[i]])
target_pred.append(target[Y_pred[i]])
print(X_pred)
print(target_true)
print(target_pred)
这是我得到的输出-

对 20 个随机例子的预测
由于我们模型的高精度,生成的 20 个预测全部正确!这是我们创建的最终模型的完整代码-
尝试对模型进行一些修改,看看是否可以进一步改进它,以做出更现实的预测。你可以尝试不同的模型,如 KNN 分类器,支持向量分类器,甚至是一个小型的神经网络,尽管我不喜欢用神经网络来完成这项任务,因为特征和数据集的数量都很少,机器学习模型也能很好地执行。
# 结论
希望这能给你一些关于如何着手建立一个简单的人工智能,或者具体地说,解决一个特定问题的机器学习模型的想法。基本上有五个阶段-
1. 为该任务获取合适的数据集。
2. 分析和可视化数据集,并尝试获得关于您应该使用的模型的直觉。
3. 将数据转换成可用的形式,然后对其进行预处理。
4. 建立模型,并对其进行评估。
5. 检查模型的不足之处,并不断改进。
在处理构建人工智能应用程序时,需要进行大量的反复试验。即使你的第一个模型效果不好,也不要灰心。大多数情况下,通过调整它可以显著提高性能。获得正确的数据集是非常关键的,而且我觉得,当涉及到现实世界的模型时,它会改变游戏规则。该说的都说了,该做的都做了,尽可能地保持学习新东西,为了乐趣而做 AI!
# 编写自己的函数
> 原文:<https://towardsdatascience.com/writing-your-own-functions-40d381bd679?source=collection_archive---------50----------------------->

用 Python 编写自己的函数。照片由[妮可·沃尔夫](https://unsplash.com/@joeel56?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/code?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 拍摄
## PYTHON 编程
## 数据科学家需要具有特定功能的函数
**在 Python 中,你可以定义自己的函数。**
## 先决条件
如果你不熟悉 Python,下面的文章会给你一点点 Python 的介绍。
[](/python-procedural-or-object-oriented-programming-42c66a008676) [## Python:过程化编程还是面向对象编程?
### 过程化和面向对象之间有点争议,我们为什么要关心它?
towardsdatascience.com](/python-procedural-or-object-oriented-programming-42c66a008676)
您将有机会了解这些在数据科学背景下新发现的技能。
1. 定义不带参数的函数
2. 用单个参数定义函数
3. 定义返回单个值和多个值的函数
为了定义一个函数,我们从关键字 **def** 开始,然后是函数名、一组括号和一个冒号。这段代码叫做函数**头**。为了完成函数定义,您可以编写函数体**并打印输出。**

无参数函数。图片作者[作者](https://medium.com/@wiekiang)
如果调用函数,则执行函数体内的代码。当您像使用预构建函数一样调用函数时,这应该会返回值。如果你想在函数内部附加一个参数呢?

单参数函数。图片作者[作者](https://medium.com/@wiekiang)
要添加该功能,可以在括号之间的函数定义中添加一个参数。你可以看到我们将一个参数`num`作为变量[添加到新的函数体中。该函数现在接受单个参数并打印出其值。如果我们不想直接打印该值呢?是否可以返回值并将其赋给一个](/scope-of-variable-and-legb-rule-4d44d4576df5)[变量](/scope-of-variable-and-legb-rule-4d44d4576df5)?

返回值而不是打印出来。图片由[作者](https://medium.com/@wiekiang)
您的函数可以通过添加`return`关键字,后跟要返回的值来返回新值。我们可以给一个[变量赋值](/scope-of-variable-and-legb-rule-4d44d4576df5)当函数的结果被调用时。用 Python 编写函数的另一个重要方面是**文档字符串**。它用来描述你的功能。

Docstring 放在三个引号之间。图片作者[作者](https://medium.com/@wiekiang)
这些描述是你的函数文档。任何一个阅读你的函数的人都可以理解这个函数,而不需要追溯所有的代码。函数文档字符串放在函数头后的下一行,并放在三个引号之间。
如果您想向函数传递多个参数,并返回不止一个值,而是多个值,该怎么办呢?
您可以通过让函数接受两个参数而不是一个来实现这一点。您还可以考虑更改函数名和文档字符串来反映这种新的行为。您可以通过给定两个参数来调用该函数,因为它有两个参数,如函数头中所述。

多参数函数。图片作者[作者](https://medium.com/@wiekiang)
参数的顺序必须与函数头参数的顺序相匹配。您也可以指定函数返回多个值。您可以通过在函数中创建称为元组的对象来实现这一点。
一个元组可以像一个列表一样包含多个值。但是,也有一些不同之处:
* 与列表不同,元组是不可变的,一旦创建,就不能修改元组中的值
* 虽然列表是使用方括号[]声明的,但是元组是使用一组括号()构造的

将元组元素分配到变量中。图片作者[作者](https://medium.com/@wiekiang)
你可以在一行中将一个元组解包成多个[变量](/scope-of-variable-and-legb-rule-4d44d4576df5),并将其赋给[变量](/scope-of-variable-and-legb-rule-4d44d4576df5)。此外,您还可以像处理列表一样访问特定的元组元素。您可以使用零索引来访问元素。
你准备好定义自己的函数了吗?
Other Interesting Articles#1 Function Arguments: Default, Keyword, and Arbitrary#2 Scope of Variable and LEGB Rule#3 Python: Procedural or Object-Oriented Programming?#4 Data Science with Python: How to Use NumPy Library#5 Do you have the Software Engineer and Data Scientist skills?
## **关于作者**
Wie Kiang 是一名研究员,负责收集、组织和分析意见和数据,以解决问题、探索问题和预测趋势。
他几乎在机器学习和深度学习的每个领域工作。他正在一系列领域进行实验和研究,包括卷积神经网络、自然语言处理和递归神经网络。
*连接上* [*LinkedIn*](https://linkedin.com/in/wiekiang)
# 为初学者编写自己的 Scikit-learn 类。
> 原文:<https://towardsdatascience.com/writing-your-own-scikit-learn-classes-for-beginners-1e4e7d4de203?source=collection_archive---------20----------------------->
## 让你尽快入门的基础知识

图片来自:[https://en . Wikipedia . org/wiki/File:Scikit _ learn _ logo _ small . SVG](https://en.wikipedia.org/wiki/File:Scikit_learn_logo_small.svg)
S cikit-learn 是用于监督机器学习的最流行的 Python 库之一,它是一个非常强大的工具,同时也很简单。但我认为,它成功的原因主要不是因为它的核心功能,而是因为它的所有模块都具有很好的内聚性和一致性。这也是除了它的流行之外,为什么许多其他流行的库如 XGBoost、pyGAM、lighGBM 和 pyEarth 都是以 Scikit-learn 的风格编写的,并确保与它兼容,几乎就像它们是核心库的一部分一样。许多与 Scikit-learn 兼容的有用项目可以在[这里](https://scikit-learn.org/stable/related_projects.html)找到。
这种内聚兼容性是 Scikit-learn 拥有自己的一套简单指南的结果,如果遵循这些指南,将确保一切正常运行。它还提供了一组基类,使得创建自己的类的过程更加容易。如果您是编写自己的类的新手,无论是为了自己使用还是为了一个库,本文都可以为您提供一个坚实的起点。
# 作为一个初学者,为什么要编写自己的类?
现在,您可能想知道为什么有人要编写自己的类,如果已经有这么多专业编写的类可以使用的话?你考虑一下是对的,因为如果免费提供的软件能完全满足你的需要,你就不应该再费心去写自己的了。然而,即使作为一个初学者,你也有可能在你自己独特的工作流程中遇到一些你想做的操作,但是没有什么东西可以完全按照你想要的那样去做。如果这个操作是您经常要做的,那么为它编写自己的类会很有用。
此外,如果您的操作处于一系列具有预写类的操作之间,它可能会阻止您将其添加到管道中以实现完全自动化。此外,使用 Scikit-learn 工具(如 GridSearchCV)不可能在整个过程中使用模型选择。
其他一些很好的理由是:
* 它发展你的编程技能。
* 它可以随着时间的推移而发展,为您自己的工作提供更多的选项和功能,也可能最终对他人有用。
* 它使您能够为开源社区做出贡献。理解你所使用的大多数极其强大的工具之所以可以免费获得是因为这些贡献。
因此,让我们直接开始创建自己的类。
假设您想要构建一个转换器,从列中的所有值中减去列中的最小数字。例如,假设一列有数字[10,11,12,13,14,15],我们的转换器将把它改为[0,1,2,3,4,5]。这可能不是一个特别有用的变压器位,这是一个非常简单的例子,刚刚开始。
让我们创建两个玩具数据帧:
import pandas as pddf_a = pd.DataFrame({‘Number’:[11, 12, 13, 14, 15],’Color’: [‘Red’, ‘Blue’, ‘Green’, ‘Yellow’, ‘Orange’]})
df_b = pd.DataFrame({‘Number’:[21, 22, 23, 24, 25],’Color’: [‘Violet’, ‘Black’, ‘White’, ‘Brown’, ‘Pink’]})
这是它们的样子:

现在让我们编写转换器:
These allow us the class to inherit Scikit-learn methods
such as fit and transform
from sklearn.base import BaseEstimator, TransformerMixin# This function just makes sure that the object is fitted
from sklearn.utils.validation import check_is_fittedclass SubtractMin(BaseEstimator, TransformerMixin):
def init(self, cols_to_operate):
self.columns = cols_to_operate def fit(self, X, y = None):
self.min_val_ = X[self.columns].min()
return self
def transform(self, X):
# make sure that it was fitted
check_is_fitted(self, ‘min_val_’)
X = X.copy() # This is so we do not make changes to the
original dataframe
X[self.columns] = X[self.columns] — self.min_val_
return X
我们从 sklearn.base 导入的类是使它工作的粘合剂。正是它们让我们的功能适合 Scikit-learn 的管道和模型选择工具。BaseEstimator 只是给了它所有 Scikit-learn 估算器都需要的 get_params 和 set_params 方法。TransformerMixin 为它提供了 fit_transform 方法。
BaseEstimator 文档可以在[这里](https://scikit-learn.org/stable/modules/generated/sklearn.base.BaseEstimator.html)找到,TransformerMixin 文档可以在[这里](https://scikit-learn.org/stable/modules/generated/sklearn.base.TransformerMixin.html#sklearn.base.TransformerMixin)找到。
根据您想要构建的估计器的类型,还有其他类型的“[mixin](https://scikit-learn.org/stable/modules/classes.html#module-sklearn.base)”可用,例如 ClassifierMixin 和 RegressorMixin,它们提供了对评分方法的访问。您可能知道,一般来说,数据处理器使用 fit 方法(以及 fit_transform ),模型(回归器和分类器)使用 predict 方法。因此,在创建评估器时,您需要添加适当的类作为父类。例如,一个自定义回归器需要 BaseEstimator 和 RegressorMixin,其他更复杂的类根据其功能可能需要更多。
check_is_fitted 只是一种验证方法,用于确保在尝试对对象进行某些操作之前,已经对其进行了拟合。
在 __init__ 方法中,我们为它添加了转换器需要的所有初始参数。在这里,我们需要操作的只是数据帧的列名。
fit 和 transform 方法是所有 Scikit-Learn 转换器的必需方法(对于回归和分类器,它将是 fit 和 predict)。在我们的例子中,fit 方法只是找出列中的最小值并存储它。你可能注意到了,这里有一个 y = None,尽管我们在 fit 方法中没有用到 y。这是为了与 Scikit-learn 中的其他类保持一致,并确保管道中的兼容性。合体的方法也**总是**得回归自我。
transform 方法完成工作并返回输出。我们制作一个副本,这样原始数据帧就不会被触及,然后减去 fit 方法存储的最小值,然后返回输出。显然,这将在您自己的有用方法中更详细地说明。
让我们看看我们的变压器在工作。
sub = SubtractMin(‘Number’)
sub.fit_transform(df_a)

sub.transform(df_b)

您可能注意到的第一件事是,我们使用了 fit_transform 方法,尽管我们没有明确使用它。如前所述,这是从 TransformerMixin 基类继承的。
我们的转换器实际上从 df_a (10)中“学习”了最小值,并在调用 fit_transform 方法时从列中减去该值。然后,当调用 transform 方法时,它从 b 中减去相同的最小值。
Scikit-learn 有一套更详细的指南,需要遵循这些指南才能有所帮助,但是这些指南应该足以确保兼容性,并帮助您为自己的项目编写类。一旦你习惯了写你的课程,看看他们的指导方针(在这里)是个好主意,这样你就可以为社区做贡献了。这些指南更适合于为 Scikit-learn 或类似的库做出贡献。没有必要全部遵守,但建议遵守。有很多项目并没有按照写的那样进行。把在自己的班级里努力当作一个长期目标。
希望这能成为一个足够快的指南,帮助您开始编写自己的自定义 Scikit-learn 类。祝你好运!
# 编写自己的 sklearn 转换器:功能缩放、数据框架和列转换
> 原文:<https://towardsdatascience.com/writing-your-own-sklearn-transformer-feature-scaling-dataframes-and-column-transformation-bc10cbe0bb86?source=collection_archive---------53----------------------->
## 编写自己的 sklearn 函数,第 2 部分
自从`scikit-learn`不久前给 API 增加了`DataFrame`支持,修改和编写你自己的变形金刚变得更加容易——并且[工作流程](https://medium.com/dunder-data/from-pandas-to-scikit-learn-a-new-exciting-workflow-e88e2271ef62)也变得更加容易。
许多`sklearns`的补救措施仍然在内部使用`numpy`数组或返回数组,这在谈到[性能](https://penandpants.com/2014/09/05/performance-of-pandas-series-vs-numpy-arrays/)时通常很有意义。性能在管道中尤其重要,因为如果一个转换器比其他转换器慢得多,它会很快引入瓶颈。当预测对时间要求很高时,这种情况尤其糟糕,例如,将模型用作实时预测的端点时。如果性能不重要或没有仔细评估,许多转换器可以调整和改进,以使用并返回`DataFrames`,这有一些优点:它们是数据科学工作流中非常自然的一部分,它们可以包含不同的数据类型并存储列名。
一个例子是特征标准化,如果你使用我们神经网络的线性模型,这可能是至关重要的:
import pandas as pd
import numpy as npdata = pd.DataFrame({
'num1': [1.0, 2.0, 10.0, 1.0, 3.0, 0.0],
'num2': [2.0, 3.0, 20.0, -3.0, 5.0, 0.5],
})data
num1 num2
0 1.0 2.0
1 2.0 3.0
2 10.0 20.0
3 1.0 -3.0
4 3.0 5.0
5 0.0 0.5
根据[文档](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html)的说明,`StandardScaler`可以“通过去除*均值并缩放至单位方差来标准化特征”。在`fit`期间,它学习训练数据的*均值和标准偏差,这可用于在`transform`期间标准化特征。默认情况下,转换器将转换后的数据强制转换为一个`numpy`数组,因此列名被删除:
from sklearn.preprocessing import StandardScalerstandard_scaler = StandardScaler()
standard_scaler.fit(data);
standard_scaler.transform(data)
array([[-0.54931379, -0.35307151],
[-0.24968808, -0.21639867],
[ 2.14731753, 2.10703964],
[-0.54931379, -1.03643571],
[ 0.04993762, 0.05694702],
[-0.84893949, -0.55808077]])
我们可以很容易地将转换器修改为返回`DataFrames`,要么继承现有的转换器,要么封装它:
another_standard_scaler = AnotherStandardScaler()
another_standard_scaler.fit_transform(data)## num1 num2
0 -0.549314 -0.353072
1 -0.249688 -0.216399
2 2.147318 2.107040
3 -0.549314 -1.036436
4 0.049938 0.056947
5 -0.848939 -0.558081
我们可以进一步修改它,接受一个`cols`参数,只针对特定的列:
column_standard_scaler = ColumnStandardScaler(cols=['num1'])
column_standard_scaler.fit_transform(data)
num1 num2
0 -0.549314 2.0
1 -0.249688 3.0
2 2.147318 20.0
3 -0.549314 -3.0
4 0.049938 5.0
5 -0.848939 0.5
封装后的版本如下,我们仍然继承了`BaseEstimator`和`TransformerMixin`,因为`BaseEstimator`免费给了我们`get_params`和`set_params`,而`TransformerMixin`提供了`fit_transform`。原谅这个愚蠢的名字,即`AnotherColumnStandardScaler`:
another_column_standard_scaler = AnotherColumnStandardScaler(cols=['num1'])
another_column_standard_scaler.fit_transform(data)
num1 num2
0 -0.549314 2.0
1 -0.249688 3.0
2 2.147318 20.0
3 -0.549314 -3.0
4 0.049938 5.0
5 -0.848939 0.5
如果我们想开发自己的转换器,而不是修改或封装现有的转换器,我们可以如下创建它:
custom_standard_scaler = CustomStandardScaler(cols=['num1'])
custom_standard_scaler.fit_transform(data)
num1 num2
0 -0.549314 2.0
1 -0.249688 3.0
2 2.147318 20.0
3 -0.549314 -3.0
4 0.049938 5.0
5 -0.848939 0.5
结果与普通 sklearn 缩放器相同:
custom_standard_scaler.transform(data)['num1'].values == standard_scaler.transform(data)[:,0]
array([ True, True, True, True, True, True])
除了编写我们自己的转换器,我们还可以使用`sklearns` `ColumnTransformer`将不同的转换器应用于不同的列(并通过传递`passthrough`保留其他的)。但是,这个函数将返回数组,因此会删除列名:
from sklearn.compose import ColumnTransformercolumn_transformer = ColumnTransformer(
transformers=[
('scaler', AnotherStandardScaler(), ['num1']),
],
remainder='passthrough')column_transformer.fit_transform(data)
array([[-0.54931379, 2. ],
[-0.24968808, 3. ],
[ 2.14731753, 20. ],
[-0.54931379, -3. ],
[ 0.04993762, 5. ],
[-0.84893949, 0.5 ]])
*原载于*[*https://blog.telsemeyer.com*](https://blog.telsemeyer.com/2019/12/15/writing-your-own-sklearn-transformer-dataframes-feature-scaling-columntransformer-writing-your-own-sklearn-functions-part-two/)*。*
# WSDM——KKBox 的音乐推荐挑战
> 原文:<https://towardsdatascience.com/wsdm-kkboxs-music-recommendation-challange-bf15214c6635?source=collection_archive---------33----------------------->

音乐与自然!!莱奥·麦克拉伦(@leiomclaren) 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
**目标:**
在这项任务中,我们必须预测用户在一个时间窗口内第一次可观察到的收听事件被触发后重复收听一首歌曲的机会。
概述:[https://www . ka ggle . com/c/kkbox-music-recommendation-challenge/overview](https://www.kaggle.com/c/kkbox-music-recommendation-challenge/overview)
**大纲:**
1. 商业问题
2. 数据讨论
3. 电子设计自动化(Electronic Design Automation)
4. 特征工程
5. 数据预处理
6. 模型
7. 比较
8. 结论和未来工作
9. 参考
# **简介**
> “**音乐是人类意识不到自己在计数而通过计数体验到的快乐”——**莱布尼茨****
音乐帮助任何人与你正在做的事情联系起来。它能提升情绪,让思想的浪潮恢复活力。人们每次都喜欢听音乐,无论是通勤时间、工作时间还是专注时间。不同的人有不同的音乐风格。音乐已经为其用户提供了各种*台,如 waves of Victrola、磁带文化、随身听时代、i-pods、FM 收音机以及最新的音乐应用程序,如 Spotify、亚马逊 Prime Music、Deezer、SoundCloud、Gaana 等。
互联网在选择用户喜欢的音乐方面使生活变得容易,但仍然需要算法来向用户推荐喜欢的音乐,而不需要手动选择。
# **1。业务问题和限制:**
我们的经营宗旨是为用户提供他们喜欢的歌曲!这一建议不需要几个小时,几秒钟就足以预测收听的机会。
* **ML 问题公式化**
我们必须建立模型,通过评估用户和歌曲的给定特征来预测用户是否会重新收听歌曲。我们可以把这个问题转化为分类问题,可以应用各种分类算法。
# **2。资料讨论:**
数据集来源:[https://www . ka ggle . com/c/kkbox-music-re commendation-challenge/data](https://www.kaggle.com/c/kkbox-music-recommendation-challenge/data)
该问题有 6 个数据文件:
**1.train.csv** :该文件包括
user_id (msno)、song_id、source_system_tab(事件被触发的位置)、
source_type(用户首先播放音乐的入口点)、source_screen_name(用户看到的布局的名称)和 target ( 1 表示在用户第一次可观察到的收听事件之后的一个月内有重复的收听事件被触发,否则 target=0)。
**2。test.csv** :这个文件包括
user_id (msno)、song_id、source_system_tab(事件被触发的位置)、
source_type(用户首先播放音乐的入口点)和 source_screen_name(用户看到的布局的名称)。
**3。songs.csv:** 这个文件有如下特点
歌曲 id、歌曲长度、流派 id、艺术家姓名、作曲家、作词人和语言。
**4。members.csv:** 这个文件有 msno (user_id)、city、bd(可能包含离群值)、性别、register_via (register method)、register_init_time (date)和 expiration _ date(date)。
**5。song_extra_info.csv:** 该文件具有用于识别歌曲的特征宋立科 id、歌曲名称和
ISRC(国际标准录音代码)。
# **3。EDA:**
让我们探索我们的数据,并了解每个特征的行为与情节。
一、列车特点:

源类型、源系统选项卡和源屏幕名称的计数图

我们有 source_type、source_system_tab 和 source_screen_name 的计数图。从图中我们可以看到,我们所有的特性在特性的每个值中几乎都是*衡的。
二。歌曲特色:

registered_via 和语言的计数图
歌曲数据中有不同类型的语言,用数字表示。我们可以看到,大多数用户更喜欢听来自'-1 '和' 52 '语言的歌曲。
大多数用户更喜欢通过“4”、“7”和“9”方式注册。
三。成员数据:

注册开始时间和截止日期的 PDF
从上面的 pdf 我们可以说,在 2012 年之后,人们开始注册自己听音乐,因此他们的有效期也被发现接* 2020 年。

适合用户年龄的 PDF 和 CDF
嗯!50 岁以下的人说“你好!”听音乐并享受它!!

缺失值的热图
我们可以说作曲家、作词家、isrc 特征有更多缺失的价值。

特征树状图
这是特征的树状图。我们可以看到一个 source_system_tab -> source_type,composer ->作词人,语言-> (song_length,artist_name,name),isrc -> song_year 之间的关联。
正如我们所知,一些作曲家对某些艺术家、语言、歌词作者有偏见。
# **4。特征工程:**
到目前为止,我们已经通过计数图和 PDF-CDF 研究了这些数据。让我们放弃那些丢失值超过 25%的特性,开始特性工程。我们还将根据特征填充缺失的值。
填充缺失值
根据维基百科, [**特征工程**](https://en.wikipedia.org/wiki/Feature_engineering) 是利用领域知识通过[数据挖掘](https://en.wikipedia.org/wiki/Data_mining)技术从原始数据中提取[特征](https://en.wikipedia.org/wiki/Feature_(machine_learning))的过程。提取的特征有助于提高机器学习算法的性能。
* 我们将提取独立于成员、歌曲和歌曲 _extra 的个人特征。合并所有文件后,我们将提取按特征分组。
* 我们将过滤 0 到 75 岁之间的年龄。
过滤器年龄
* 我们将从到期和初始日期中提取成员资格 _ 天、年、月和日。
计算日期功能
* 我们将从流派 id 和艺术家中提取流派 id 计数、艺术家计数。一些歌曲有许多艺术家和流派,所以我们还将提取第一个艺术家的名字和第一个流派 id。
计算艺术家特征
以下片段是艺术家的特点。
计算艺术家特征
* 我们将从 isrc 特征中提取`song_year`、`country_code`和`registration_code`。
计算歌曲特征
* 就艺术家、语言、流派和年龄组而言,每个人都有自己最喜欢的音乐风格。我们将基于这些来提取 group_by 特征。
group_by 功能
# **5。数据预处理:**
从数据文件中提取所有特征后,是时候转换所有这些特征了。我们有数字和分类特征。有标准化、规范化、十进制等技术。我们将使用标准化,因为它通过移除*均值和缩放到单位方差来标准化特征。对于分类特征,我们有一次性编码、标签编码、响应编码等。我们将尝试对我们的分类特征进行标记编码。
转换数字特征
编码分类特征
# **6。型号:**
正如我们先前所述,我们将把这个问题作为一个分类问题提出,我们可以在我们的数据点上应用各种分类算法。所以我们一个一个来试试。我们将在本节的最后讨论每个模型的特性重要性。在比较部分,我们将讨论结果。
**1。逻辑回归**
这是一种简单的分类技术,尽管它的名字中有“回归”这个词。我们将使用带有 log_loss 的[SGD 分类器](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.SGDClassifier.html)。我们将使用 [GridSearchCV](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html) 调整超参数,如惩罚和 alpha。
使用 GridSearchCV 调整逻辑回归超参数
**2。支持向量机**
我们将使用带铰链损耗的[SGD 分类器](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.SGDClassifier.html)执行线性 SVM,并使用 [GridSearchCV](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html) 进行超参数调谐。
使用 GriSeachCV 进行 SVM 超参数调谐
**3。决策树**
决策树是一组轴*行的超*面或决策规则,也可以对非线性可分的数据点进行分类。sklearn 提供了[决策树分类器](https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html)。我们将调整参数,如`max_depth,` `min_samples_split`和`max_leaf_nodes.`
决策树分类器超参数调整 GridSearchCV
**4。随机森林**
元估计器在数据集的各种子样本上拟合多个决策树分类器,并使用*均来提高预测精度和控制过拟合。我们将使用 [RandomForestClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html) 并使用[randomsearchv](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.RandomizedSearchCV.html)进行超参数调整。
随机森林及其超参数调谐
**5。XgBoost**
XgBoost 是一个实现梯度提升决策树的库,它包含决策树作为基础学习器,并试图降低整体偏差。[这里的](https://www.youtube.com/watch?v=8b1JEDvenQU)是对 XgBoost 及其背后的深入数学的有用视频解释。
XgBoost 及其超参数调谐
6。LightGBM
LighGBM 也是一个梯度提升决策树的框架,但是它速度更快,占用内存更少。你可以从[这里](https://lightgbm.readthedocs.io/en/latest/)了解更多。
LGB 超参数调谐
**7。堆叠**
堆叠是一种集成技术,其中多个分类器的预测被用作训练元分类器的新特征。我们将使用[堆叠分类器](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.StackingClassifier.html)和决策树,RF 和 GBDT 作为一级分类器,逻辑回归作为元分类器。
堆积分类器
**8。投票分类器**
投票分类器是许多分类器的包装器。它将以软或硬的方式为来自基本分类器的预测投票。我们将执行来自 sklearn 的 [VotingClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.VotingClassifier.html) 。
投票分类器
**9。深度学习**
我们也将尝试基于深度学习的方法。我们将构建基于 MLP 的架构,该架构将所有特征作为输入,并将基于 sigmoid 概率生成适当的类别标签。我们将使用 Adam 作为优化器,使用二进制交叉熵作为损失函数。
MLP 模型与培训
**(a)特征重要性:**
为了更好地理解任何模型,建议检查特性的重要性。每一个特性都对模型的性能有积极或消极的影响。基于树的算法具有内置的特征重要性,而在 SVM LR 的情况下,我们必须通过`model.coef_`提取它

特征重要性
**(b)特征选择和提取**
特征选择意味着选择原始特征池的子集,而特征提取意味着从现有数据中获取有用的特征。我们使用[选择测试](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.SelectKBest.html#sklearn.feature_selection.SelectKBest)进行特征选择,使用 [PCA](https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html) 进行特征提取。我们还可以从 DT、RF 或 XgBoost 等算法中选择基于特征重要性的特征。我们从 DT 中选择了最重要的特征,并在这些选择的特征上应用了最佳模型。[这里的](https://github.com/khushi810/KKBOX_Music_Recommendation_Challenge/blob/master/Music_Recommendation_models.ipynb)是型号和功能重要性的详细记录。
# **7。对比:**
在对我们的数据集和特征重要性应用所有模型之后,我们可以说 LR 和 SVM 不太适合我们的数据集。他们还对具体特征给予更多负面的重视。基于树的算法工作得更好,具有更好的特征重要性。当我们使用 LighGBM 时,与其他模型相比,它提供了更高的性能。LightGBM 和 XgBoost 是 Kaggle 社区的热门。特征选择也推动分数。
下面是不同算法的对比表。

比较表
# **8。结论&未来工作:**
* 从上表中,我们可以看到,与其他模型相比,基于 LightGBM 的模型给出了更高的分数。在基于特征提取和选择的模型中,选择最佳模型的特征选择给出了更好的结果。
* 由于资源限制,我只使用了 30%的数据点。如果我们使用所有数据点并进行更多的超参数调整,我们可以获得更好的结果。
* 深度学习需要大量的数据点。
* 我们还可以想到更多的特征提取。
# **9。参考文献:**
1. [https://www . ka ggle . com/c/kkbox-music-recommendation-challenge/overview](https://www.kaggle.com/c/kkbox-music-recommendation-challenge/overview)
2. [https://github . com/lyst do/Codes-for-WSDM 杯-Music-Rec-first-place-solution](https://github.com/lystdo/Codes-for-WSDM-CUP-Music-Rec-1st-place-solution)
3. [https://www . ka ggle . com/asmitavikas/feature-engineered-0-68310](https://www.kaggle.com/asmitavikas/feature-engineered-0-68310)
4. [https://www . ka ggle . com/rohandx 1996/re commendation-system-with-83-accuracy-lgbm](https://www.kaggle.com/rohandx1996/recommendation-system-with-83-accuracy-lgbm)
5. 【https://www.appliedaicourse.com/course/11/Applied-Machine-】
[学习-课程](https://www.appliedaicourse.com/course/11/Applied-Machine-)
感谢您的关注。如果你有任何想法,可以留下评论、反馈或任何
建议。
你可以在我的 Github repo 上找到完整的代码([这里](https://github.com/khushi810/KKBOX_Music_Recommendation_Challenge/tree/master))。
很高兴在 [LinkedIn](https://www.linkedin.com/in/khushali-vithani/) :)上与您联系
# 用于 DirectML 和张量流的 WSL2 / NVIDIA GPU 驱动程序
> 原文:<https://towardsdatascience.com/wsl2-nvidia-gpu-driver-for-directml-and-tensor-flow-82996bb080b0?source=collection_archive---------29----------------------->
## 为 Linux 2 的 Windows 子系统配置 NVIDIA GPU

在 [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上由 [Vinayak Sharma](https://unsplash.com/@vinayak_125?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 拍摄的照片
2020 年 9 月, [NVIDIA 在最新版本的预览版驱动程序中宣布了 Linux(wsl 2)Windows 子系统上的](https://news.developer.nvidia.com/preview-for-cuda-on-wsl-updated-for-performance/)原生 CUDA 特性,这促使我写了这篇文章。
我会讲解如何在 [WSL2(微软子系统 Linux)](https://docs.microsoft.com/en-us/windows/wsl/about) 上安装 NVIDIA 驱动,测试 TensorFlow 的并行执行。
一些主要的变化是:
* 改善 CUDA 用户的使用体验
* TensorFlow 支持基于 DirectX 12 的 GPU 加速
* 对 PTX JIT 的支持将使我们能够运行[并行执行(PTX](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#introduction) )代码。
# 设置
在 Microsoft Windows build 20145 对公众不可用之前,要在 WSL 版本 2 上使用 GPU,您需要执行以下操作:
1. 激活[微软内部程序](https://insider.windows.com/en-us/getting-started)
2. 检查您的 Windows Linux 子系统(WSL)版本。
3. 在 [NVIDIA Developer](https://developer.nvidia.com/) 中登录或创建一个帐户
4. 下载并安装 [NVIDIA 驱动程序](https://developer.nvidia.com/cuda/wsl/download)
5. 测试 WSL2 对 GPU 的使用
# 检查您当前的 Microsoft 版本
为了激活 Microsoft Insider 计划,我们将检查所需的版本是否可用。为此,首先,转到**检查更新**并验证您是否有最新的更新。

Microsoft Windows 检查更新的图像

Windows Update、Microsoft Windows 的图像
下一步是转到“关于您的电脑”,验证操作系统是否符合 Windows 规范。

关于您的电脑的图片,Microsoft Windows

关于您的电脑的图片,Microsoft Windows
如果你的版本大于 **20145,**你可以跳过下一节。
# 1.激活 Microsoft Insider 计划
在[https://insider.windows.com/en-us/getting-started](https://insider.windows.com/en-us/getting-started)注册并按照步骤操作。重启后,你会得到一个新的设置选项, **Windows Insider 程序设置。**

Microsoft Windows 的搜索图像

Windows Update、Microsoft Windows 的图像
经过多次更新后,如果您有 20145 或更高版本的操作系统,您可以通过 WSL 使用 GPU。如果你没有最低版本,在 Windows Insider 程序设置中切换到**开发频道**或**快速环**并重新运行 Windows Update。

Microsoft Windows 的 Windows Insider 程序设置的图像

Microsoft Windows 的 Windows Insider 程序设置的图像
# 2.检查您的 Windows Linux 子系统(WSL)版本
您需要适用于 Linux 的第二版 Windows 子系统。要检查您是否正在使用 WSL 2,请在提示符下运行以下命令
wsl --list -v

运行 WSL list 命令的 Ubuntu Windows 终端的图像
如果你有版本 1,可以用下面的[链接](https://docs.microsoft.com/en-us/windows/wsl/install-win10)更新。如果你没有安装 WSL,我推荐你阅读我的[上一篇文章](/configuring-jupyter-notebook-in-windows-subsystem-linux-wsl2-c757893e9d69)。
下一步是更新您的 WSL 发行版。为此,打开一个提示符并执行:
sudo apt-get update && sudo apt-get upgrade

运行更新和升级命令的 Ubuntu Windows 终端的图像。
# 3.在 NVIDIA Developer 中登录或创建一个帐户
NVIDIA 开发者页面有一些不常见的登录或创建帐户的方法;进入[https://developer.nvidia.com](https://developer.nvidia.com)和右上角的**账户**,选择电子邮件登录或创建新账户。然后输入您当前的电子邮件和密码或点击**创建账户**并按照步骤操作。
# 4.下载并安装 NVIDIA 驱动程序
要下载最新版本的 NVIDIA 驱动程序,请前往[https://developer.nvidia.com/cuda/wsl/download](https://developer.nvidia.com/cuda/wsl/download),根据您当前的硬件选择 GEFORCE 或 QUADRO。
我准备用 GeForce 和 express 安装版本[**460.20 _ gameready _ win 10-dch _ 64 bit _ international . exe**](https://www.nvidia.com/en-us/geforce/release-notes/GFE/3_20_4/Web/gfe-v3_20_4-web-release-highlights/)
安装完成后,进入 **GEFORCE Experience** 选择设置,**启用实验功能**,按重启键。
# **5。通过 WSL2 测试 GPU 使用情况**
在开始之前,我建议查看一下 NVIDIA WSL 文档。[进行下一步](https://docs.microsoft.com/en-us/windows/win32/direct3d12/gpu-tensorflow-wsl)并安装 **Miniconda。**没有的话在我的[上一篇](/configuring-jupyter-notebook-in-windows-subsystem-linux-wsl2-c757893e9d69)里会发现很有帮助。
您至少需要 Linux 内核版本 4.19.121。要了解您的 Linux 版本,请运行 **cmd** 或命令提示符并执行以下命令:
wsl cat /proc/version
您将看到与此类似的内容:
Linux version 4.19.128-microsoft-standard
接下来,更新 Miniconda,用 python 3.6 创建一个名为 DirectML 的新环境,安装 Tensor Flow / Direct ML。
conda update conda
conda update --allconda create --name directml python=3.6
conda activate directmlpip install tensorflow-directml

更新 conda 的 Ubuntu Windows 终端的图像

创建 DirectML 环境的 Ubuntu Windows 终端的图像

Ubuntu Windows 终端激活 DirectML 环境的图像
然后打开一个交互式 Python 并执行以下操作:
import tensorflow.compat.v1 as tf tf.enable_eager_execution(tf.ConfigProto(log_device_placement=True)) print(tf.add([1.0, 2.0], [3.0, 4.0]))
您将获得当前的硬件。

Ubuntu Windows 终端和 Windows 任务管理器的图像,用于测试 TensorFlow
为了仔细检查,我多次运行最后一条语句,在每次执行之间等待不同的时间,以尝试它是否会影响 GPU 的使用趋势:

Ubuntu Windows 终端和 Windows 任务管理器的图像,用于测试 TensorFlow
# 谢谢
最后,我要感谢我的兄弟迈克尔·萨维德拉,他鼓励我继续写这些话题。
# 有用的资源
[](https://news.developer.nvidia.com/preview-for-cuda-on-wsl-updated-for-performance/?mkt_tok=eyJpIjoiT1RobVl6STVabVkxTmpjMiIsInQiOiJOZExCNVVIYjN4VUZKVUswVHhpUmF2SVhZMVNGaUNWelNDWE03V203OU1zbFhcL3o4dWJDdGlyM291dnRCeDhHUWFPVzM5SWJEWWJaaDlYNE9HOUhweE9xTmpXSVZDVHVWUzVlNFdyQkVQTGduRmRaZnFPOTRtRDZKVlVjUkdhdmEifQ%3D%3D) [## WSL 上的 CUDA 预览更新了性能
### 最*,微软宣布了他们的 Windows 子系统 Linux ( WSL)功能的公开预览计划…
news.developer.nvidia.com](https://news.developer.nvidia.com/preview-for-cuda-on-wsl-updated-for-performance/?mkt_tok=eyJpIjoiT1RobVl6STVabVkxTmpjMiIsInQiOiJOZExCNVVIYjN4VUZKVUswVHhpUmF2SVhZMVNGaUNWelNDWE03V203OU1zbFhcL3o4dWJDdGlyM291dnRCeDhHUWFPVzM5SWJEWWJaaDlYNE9HOUhweE9xTmpXSVZDVHVWUzVlNFdyQkVQTGduRmRaZnFPOTRtRDZKVlVjUkdhdmEifQ%3D%3D) [](https://developer.nvidia.com/cuda/wsl) [## 用于 Linux (WSL)的 Windows 子系统中的 GPU
### CUDA on Windows Subsystem for Linux(WSL)—公共预览版 Microsoft Windows 是一个无处不在的企业*台…
developer.nvidia.com](https://developer.nvidia.com/cuda/wsl) [](https://developer.nvidia.com/blog/announcing-cuda-on-windows-subsystem-for-linux-2/) [## 在 Linux 2 的 Windows 子系统上发布 CUDA | NVIDIA 开发者博客
### 为了响应大众的需求,微软宣布了 Linux 2 (WSL 2)的 Windows 子系统的一个新特性——GPU…
developer.nvidia.com](https://developer.nvidia.com/blog/announcing-cuda-on-windows-subsystem-for-linux-2/) [](https://github.com/microsoft/DirectML) [## 微软/DirectML
### DirectML 是一个高性能、硬件加速的 DirectX 12 库,用于机器学习。DirectML 提供 GPU…
github.com](https://github.com/microsoft/DirectML) [](https://devblogs.microsoft.com/commandline/gpu-compute-wsl-install-and-wsl-update-arrive-in-the-windows-insiders-fast-ring-for-the-windows-subsystem-for-linux/) [## GPU 计算、WSL 安装和 WSL 更新在 Windows 的最新内部版本中发布…
### 在最新的 Windows Insider 预览版中,针对 Linux 的 Windows 子系统(WSL)有三个激动人心的新更新…
devblogs.microsoft.com](https://devblogs.microsoft.com/commandline/gpu-compute-wsl-install-and-wsl-update-arrive-in-the-windows-insiders-fast-ring-for-the-windows-subsystem-for-linux/) [](https://devblogs.microsoft.com/commandline/whats-new-in-the-windows-subsystem-for-linux-september-2020/) [## 面向 Linux 的 Windows 子系统的新功能-2020 年 9 月| Windows 命令行
### 这篇博客文章强调了 WSL 的更新。在过去的几个月里,除了一些预览…
devblogs.microsoft.com](https://devblogs.microsoft.com/commandline/whats-new-in-the-windows-subsystem-for-linux-september-2020/)
# WTH 是 R *方和调整后的 R *方?
> 原文:<https://towardsdatascience.com/wth-are-r-squared-and-adjusted-r-squared-7b816eef90d9?source=collection_archive---------13----------------------->
## 理解 R *方背后的数学和直觉。

Daria Nepriakhina 在 [Unsplash](https://unsplash.com/s/photos/r?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄的照片
T 今天我将从机器学习的角度来解释 R *方和调整的 R *方的概念。我还将向您展示如何找到 ML 模型的 R *方值。让我们开始吧…
# r *方
它充当回归模型的评估指标。为了更好地理解它,让我引入一个回归问题。假设我正在构建一个模型,根据我在某个月的空闲时间来预测我在这个月会写多少文章。因此,这里的**目标变量**是文章数量,空闲时间是**独立变量**(也称为特性)。这是我创建的虚拟数据。

在这种情况下,简单的线性回归模型应该足够了。该模型的方程是…

模型的参数(***w1***和 ***b*** )可以通过最小化所有数据点的*方误差来找到。这也被称为最小*方损失函数。

在这个优化步骤之后,我们发现红线是我们的模型(**最佳拟合线**)。

现在我们想知道我们的模型有多好。这可以通过许多方式实现,但 R-squared 使用一种叫做**方差的统计方法。**方差表示数值围绕其*均值分布的程度。数学上,

n 是数据点的数量
R-squared 计算模型(独立变量的函数)解释了目标变量的多少方差。但是要找到它,我们需要知道两件事。 **1)目标变量围绕*均值的方差(*均方差),2)目标变量围绕最佳拟合线的方差(模型方差)。**
*均方差也可以被视为模型的方差,该模型输出每个输入的目标变量的*均值。我们可以把这个模型想象成一条水*线,它在所有数据点的 y 坐标的*均值处切割 y 轴。看图中的绿线。

忽略因子 ***1/n,*** 我们可以写…

模型方差的公式是…

现在我们可以理解 R *方的公式了。

# 如何解读?
> 正如我前面提到的,R *方值表示可以用你的模型解释的目标变量的方差的比例。解释的方差比例越多,你的模型就越好。因此,接* 1 的 R *方值对应于好模型,接* 0 的值对应于坏模型。
假设我们模型的 R 值是 ***0.78。*** 这种说法意味着我们的模型解释了 ***78%*** 的文章数量对应的数据的方差。它接*于 ***1*** 所以我们可以说这是一个很好的模型。
# R 的可能值
***R = 0*** 当我们的模型与*均模型相同时。 ***R > 0*** 表示我们的模型比一般模型要好。R 的最大可能值等于 ***1*** 。虽然它的名字中有一个正方形,但它可能取负值。 ***R < 0*** 表示我们的模型比一般模型差。这种情况一般不会发生,因为优化步骤会产生比*均模型更好的模型。
# R *方的问题
起初,看起来一切都很好,但是随着我们添加更多的特性,R 出现了一个巨大的问题。随着新要素添加到模型中,r *方永远不会减少。
> 这是一个问题,因为即使我们向我们的模型添加无用的或随机的特征,R *方值也会增加,这表明新模型比以前的模型更好。这是错误的,因为新特征与输出变量无关,只会导致过度拟合。
# 为什么 R 的*方永远不能减小?
为了理解这一点,让我们为我们的模型引入一个新特性,它与我写的文章数量(输出变量)没有关系。我将一个月的*均温度作为我们的新特征。姑且称之为 ***x_2。*** 所以我们的模型就变成了……

在优化之后,对于 w_2 可能出现两种情况:
1. 我们得到 ***w_2*** 为 ***0*** 。这意味着 ***x_2*** 和输出变量之间没有相关性,我们被之前的损失函数最小值卡住了。因此,我们的模型与之前的模型保持不变。所以,在这种情况下,R 值保持不变。
2. 我们得到一个非零值 ***w_2*** 。这意味着已经找到了***x2***和输出变量之间的一些相关性,并且我们实现了损失函数的更好的最小值。所以,R 值增加。
几乎总是出现第二种情况,因为在随机性中很容易找到小的相关性。但是这种小的相关性过度拟合了模型。为了解决这个问题,我们使用调整的 R *方。
# 调整后的 R *方
调整后的 R-squared 背后的想法是,随着我们向模型中添加更多的特征,我们会对分数进行惩罚。我们来看看调整后的 R *方的公式。

n 是数据点的数量;m 是独立特征的数量
分母 ***(n-m-1)*** 随着特征数量的增加而增加。因此,如果我们没有发现 R 的显著增加,那么整个表达式的值不会增加(甚至可能减少)。这就是为什么调整后的 R 在某种程度上抵抗了我们在普通 R 中所面临的问题。
# 如何求 R(使用 StatsModels)?
Statsmodels 库提供了一种执行许多统计任务的简单方法。首先,我创建了一个假数据集,并建立了一个线性回归模型。之后,我通过调用 **summary()** 函数打印了 R 和调整后的 R 值。下面是代码…
from pandas import DataFrame
import statsmodels.api as sm# making the fake dataset
data = {
'month': [12,11,10,9,8,7,6,5,4],
'free_time': [120,110,100,90,80,85,60,50,40],
'num_articles': [8,8,7,6,6,7,6,4,5]
}df = DataFrame(data, columns=['month', 'free_time', 'num_articles'])# features
X = df[['free_time']]
target variable
Y = df['num_articles']
adding a constant
X = sm.add_constant(X)# applying method of least squares
model = sm.OLS(Y, X).fit()
predictions = model.predict(X)print_model = model.summary()
print(print_model)
现在关注输出的选定部分。

## 还好奇?看一个我最*做的视频…
我希望你喜欢阅读。下次见…学习愉快!
# WTH 是一个直方图🤷
> 原文:<https://towardsdatascience.com/wth-is-a-histogram-dd63a70cc056?source=collection_archive---------35----------------------->
## 用简单的英语说…
在本期数据技能药丸中,我们将触及难以捉摸的直方图。好吧,也许这不是难以捉摸的,但这是你的团队中的一些人可能会纠结的概念之一,但当它被分解时,实际上是非常简单的。这篇文章的互动版本[在这里](https://count.co/n/v9gcmoQQsui)。
# 你的形状
直方图揭示了数据的*形状*。特别是,它们显示了数字在整个数据集中的分布趋势。通过观察数据集的形状,我们可以确定:
1. 最常见的价值观是什么
2. 值的变化有多大

数据来自[气象](https://www.metaweather.com/api/),分析来自[统计](https://count.co)
MetaWeather 的这一数据列出了 2019 年伦敦每天的气温中位数。每个条形代表有多少天属于指定的 5 度温度范围。
从这里我们可以看到,伦敦的天气分布相当均匀,这是因为它以非常温和而闻名。
如果我们将其与迈阿密的相同数据进行比较,我们可以了解到更多信息:

来自[气象](https://www.metaweather.com/api/)的数据
首先,我们可以看到:
1. 🥵:对我的美国朋友来说,大多数日子都是在 25-30 华氏度或 77-86 华氏度之间。
2. 与伦敦的天气相比,迈阿密的天气非常稳定(例如,它的变化低于伦敦)。这从迈阿密的酒吧数量较少就可以明显看出,其中大多数酒吧都比伦敦的酒吧高。
# 这些东西中的一个与另一个不同
查看直方图,可以很容易地识别出似乎与其他点不匹配的点。这些通常被称为异常值,如果不小心处理,会严重影响您的分析和结果。

来自 [Zillow](https://www.zillow.com/research/data/) 的数据
在本例中,我们仅使用了 Zillow 关于 2019 年美国约 700 个大都市地区房屋挂牌价格中值的数据。
每个条形代表在指定范围内有多少城市地区的房价中值。因此,2019 年,不到 200 个都会区的挂牌价格中值在 10 万美元至 15 万美元之间。
我们可以看到,美国大部分都会区的挂牌价格中值在 10 万美元到 25 万美元之间。
直方图还快速突出了异常值,尤其是一个房价中值超过 100 万美元的大都市地区😂。
如果你在意识到圣何塞和它的邻居旧金山和圣克鲁斯是如此异常之前进行分析,你的结果将会严重失真。例如:
* 2019 年美国*均房价(包括圣何塞、旧金山和圣克鲁斯):43.75 万美元
* 2019 年美国*均房价(不包括圣何塞、旧金山和圣克鲁斯):37.5 万美元
这超过了 60,000 美元(16%)的差距!一定要花时间检查那些异常值!
# 为什么它们很重要
除了前面两点,直方图还有一些好处,如:
## 🔮预测未来
通过理解共同的价值观和这些价值观的差异,你可以开始思考*在未来*会发生什么。
如果你必须猜测伦敦或迈阿密明天的气温,如果你猜对了,你会得到 100 美元,你会预测哪一个?(希望你说的是迈阿密:)
## 🚧提高直觉
主要的好处(IMO)是直方图是如此丰富的上下文来源。通过更深入地理解数据,你对世界的直觉会得到提高。
如果你在圣克鲁斯偶然发现一栋售价 200 美元的房子,你现在应该会有一种奇怪的感觉。你对数据有了一种直觉,如果没有丰富的研究和经验,你是不可能得到这种直觉的。
# 迪伊伊
如果你想自己做直方图,下面是步骤!
## 0.确保你的数据只是数字
抱歉,没有文本字段。您还将决定如何处理空值,是过滤掉它们,还是包含一个空值存储桶。
## 1.创建您的桶
挑选水桶有两种方法:
1. 挑选一些有意义的东西:
对于温度示例,我们选择了 5 度的桶尺寸,因为这很容易理解。如果我们愿意,我们可以轻松地选择 1 度、2 度或 7.2343 度,但要获得相同的见解会困难得多。
2.选择你想要的桶数
我们也可以说我们想要 10 个桶,不管它们有多宽。为了计算出在这种情况下每个桶的宽度,我们可以使用:
bucket_size = (max_value - min_value)/number_of_buckets
做这件事没有对错之分。我建议尝试不同的桶大小,看看你的直方图如何变化!
## 2.将每个观察(行)分配给一个存储桶
## 3.绘制直方图
**x 轴**:铲斗升序排列
**y 轴**:有落入每个桶的观测值的计数。
## 针对工具的教程,我推荐这些给 [Excel](https://www.excel-easy.com/examples/histogram.html) 和 [SQL](http://www.wagonhq.com/sql-tutorial/creating-a-histogram-sql) 。
# 下一步是什么?
你告诉我们!
你想看什么?我们正在为下一篇文章寻求建议。人头[这里](https://counthq.typeform.com/to/QT3JDf)投出您的一票!
# x 射线变压器信息图
> 原文:<https://towardsdatascience.com/x-ray-transformer-dive-into-transformers-training-inference-computations-through-a-single-visual-4e8d50667378?source=collection_archive---------41----------------------->
## 通过一个单一的视觉潜入变形金刚训练和推理计算

本文末尾提供了完整信息图的更高分辨率版本的链接。哈维尔·Ideami@losslandscape.com 的可视化
transformer 架构已经在 **NLP** 领域和深度学习领域产生了一场革命。大量的应用程序受益于这些模型并行处理序列的能力,同时通过它们实现的注意机制来实现对它们的上下文的更深入理解。而 **GPT-3** 现在是深度学习社区的热门话题。
一开始,理解转换器如何处理序列可能有点困难。当处理一个复杂的模型时,许多人喜欢研究模型的计算如何改变通过它的张量的形状。
为此,我创建了 **X 射线变压器信息图**,它允许你在**训练和推理**阶段完成从变压器计算开始到结束的旅程。它的目标是通过对单个可视资产的分析和探索,快速而深入地理解变压器模型的内部计算。
*本文末尾有一个链接,可以下载下面完整信息图的更高分辨率版本。*

本文末尾提供了分辨率更高的完整信息图的链接。哈维尔·Ideami@losslandscape.com 的可视化
# **颜色代码**
在查看该信息图时,首先要考虑的是图右下方显示的代表不同重要阶段的颜色。
* 蓝色灯**表示**训练**阶段。**
* 绿灯**绿色**表示**推断**阶段。
* **紫色**表示编码阶段(用于两个阶段),在编码器中,紫色模块属于训练阶段,绿色模块属于推理阶段。
* 深色**红色**表示解码阶段(用于两个阶段)。在解码器中,紫色模块表示编码器数据,深红色模块表示解码器数据,绿色模块通常表示推理阶段。

一旦弄清楚了颜色代码,接下来就是注意里面有数字的**粉色**圆圈。这些帮助我们看到执行的一般路径,首先通过编码器,然后是解码器。
两侧的两个大箭头是编码器和解码器阶段执行的一些关键阶段的提示。
# 模型
为了生成这个信息图,我使用了一个生成聊天机器人的小型 transformer 模型。聊天机器人通过成对的问题和答案进行训练。这个特定的模型是针对与**电影**和连续剧,尤其是**科幻**相关的问题和答案进行训练的。问答示例:
* "在《扩展》系列中,你最喜欢的角色是什么?":“娜奥米永田肯定!”
* "你最喜欢太空堡垒卡拉狄加中的哪个角色?":“卡拉·瑟瑞斯,她很棒”
在信息图的标题下面,我们可以回顾一下在研究计算的形状时要考虑的最重要的**参数**。
* 这个小模型是用 8 的**批量训练出来的。**
* 该模型的多头关注部分有 **4 头**。
* 有 **3 个编码器层**和 **3 个解码器层**。
* 该模型的**输出词汇**的**大小**为 **950**
* 横跨模型使用的**嵌入尺寸**为 **32** 。
# 培训阶段。新的一批
当我们开始训练模型时,我们从信息图的左下方开始旅程。
我们从数据加载器中获得一个批处理。我们使用的批量大小为 8,因此每批包含 8 个输入序列和 8 个输出序列。
# 标记化、数值化、填充和遮罩
8 个输入序列根据需要进行填充(添加填充标记),以便它们都具有相同的长度,在该特定批次的情况下,长度为 10(该批次中最长序列的长度)。批处理的输出序列也是如此。
这些序列已经被标记化和数值化,以准备被模型摄取。当训练循环提取新的一批时,序列被数值化并以维度为 **8x10 (BS x SeqLen)** 的张量来构造。
# 编码器中的屏蔽
接下来,我们需要创建一个**遮罩**,它将帮助我们确保序列中的额外填充元素不会被注意机制所考虑。因此,我们将掩码中属于输入序列中填充标记的那些位置设置为假或 0。

# 嵌入和位置编码
现在我们必须创建我们的嵌入,所以我们将 8x10 张量发送到嵌入模块,并得到一个**8x 10x 32(BS x SeqLen x EmbedSize)**张量,因为在这个小示例中我们的嵌入大小是 32(512 是 transformer 模型的典型嵌入大小)。
为此,我们添加了位置编码模块的结果,这将有助于模型考虑整个序列中定位的差异。
我们编码器的第一层准备接收这个 8x10x32 张量。

# **编码器**
编码器做的第一件事就是创建 3 个 8x10x32 张量的副本来产生模型的 **Q,K,V** 元素,也就是**查询,键和值。**

这 3 个张量首先通过 **3 个线性模块**(每个张量一个)。在这个小例子中,这些线性模块不会改变维度(但是如果我们希望的话,它们可以)。
在通过这些线性模块之后,我们到达必须将计算分成 4 个头的点(8 是头的数量的典型值。在这个小例子中,我使用 4)。使用 **4 个头部**将**允许注意力机制从不同角度解释序列。**
在计算上,我们可以用两个简单的步骤来准备这个阶段。
* 首先,我们可以将张量重新配置为**将**嵌入大小维度 **32** 拆分为两个维度 **4 和 8** 。4 是人头数。8,在我们的例子中,等于**嵌入维数/头数** 32 / 4 = **8,**我们称之为 **dimK 或 dK** (这个 dimK 值可以用不同的方法计算)。
* 现在我们做一个转置操作来定位批量大小为 1 之后的头数的维度。这就产生了新的形状:**8 x 4 x 10 x 8(BS x Heads x SeqLen x dimK)**。这个形状告诉我们:对于该批中的每个元素,我们将有 4 个头。并且这些头部中的每一个都将具有 10(序列长度)x 8(dimK)的矩阵。

# 自我关注
我们现在的目标是计算注意力得分。编码器执行所谓的**自我关注**。自我关注有助于我们将每个输入序列的不同部分与它们自身以及序列本身的其余部分进行比较。

从概念上来说,我们正在探索**序列的不同部分应该给予自身的不同部分多大的关注。**
为了找出这一点,我们将把**查询**和**键**张量相乘。
* 为了将它们相乘,我们需要转置 K 张量的后一半。
* 一旦 K 张量被转置,我们得到两个可以相乘的形状: **8x4x10x8 * 8x4x8x10** 。
* 注意,我们真正相乘的是最后两个维度: **10x8 * 8x10** 。
* 这将产生**注意力分数**张量,其形状为:**8 x4 X10 x 10(BS x Heads x SeqLen x SeqLen)**。
* 这些是我们的**自我关注分数**。对于批次中的每个元素,以及 4 个头中的每一个,我们有一个 **10x10 矩阵**,它表示我们序列中的每个部分应该对同一序列中的每个部分给予多少关注。

接下来我们要做的是应用一个面具。这是因为,请记住,我们确保了批处理中的所有序列都具有完全相同的长度。为此,我们必须将**填充标记**添加到比该批中最大的序列更短的序列中。
所以我们现在应该**屏蔽**(使之非常小或非常负)那些张量部分,这些张量部分引用序列中有填充标记的**部分。因此,我们应用掩码并消除序列中与这些填充标记相对应的部分的影响。**
现在我们将把一个 **softmax 模块**应用于张量的 10x10 矩阵,这样每一行的所有数字总和为 1,将每一行转换成一个**概率分布**。

这些是我们的**软自我关注分数**。对于每批中的每一个序列,在每一个头部中,序列的每一部分和它自身的每一部分之间的联系有多强,对序列的每一部分的所有影响的总和等于 1。

现在我们有了注意力分数,我们应该把它们应用到 V 张量的值上。我们希望**根据****自关注**计算的**结果来转换编码器**的值。
我们的**关注分**有**8 x4 X10 X10(BS x Heads x SeqLen x SeqLen)**的形状。而我们的 **V 张量**的形状是**8 x4 X10 x8(BS x Heads x SeqLen x dimK)**。请记住,我们实际上是在乘以后两个维度,所以我们是在乘以 **10x10 * 10x8** 。这就产生了一个新的维度为**8 x4 X10 x8(BS x Heads x SeqLen x dimK)的张量。**

至此,我们已经结束了**自我关注**阶段。我们通过**乘以查询和关键字**得出了注意力分数。然后**将**这些**注意力分数应用于值**以获得最终的注意力张量。
是时候让**把 4 个头**合二为一了。要做到这一点,我们做之前的逆操作,结合**换位和重新配置**得到一个新的形状**8 X10 x32(BS x Heads x EmbSize)**。

在将结果张量通过一个**线性模块**之后,我们到达了我们的第一个**跳跃连接**。我们将**把我们当前的张量添加到进入编码器层的原始张量**中。然后我们将**应用层标准化**来保持数据值在一个好的范围内。

接下来,我们将我们的 **8x10x32** 张量通过一个前馈层,然后应用**另一个跳过连接**,将结果张量添加到进入前馈层的张量中(并像前面一样归一化结果)。

我们可以**可选地**在先前计算的不同阶段应用**丢失模块**,例如在执行跳过连接添加时或在注意阶段结束时。
# 重复和上升
精彩!这是编码器的一层。完全相同的计算将被应用 x 次,对应于我们在编码器中的层数。
注意,进入编码器层**的**张量和退出**编码器层的**张量具有完全相同的形状** : **8x10x32。**这就是为什么**我们可以一个接一个地链接尽可能多的编码器层。****
一旦我们到达最终的编码器层,我们就获得了最终的 **8x10x32** 张量。该编码器输出张量稍后将在**编码器-解码器注意机制**(出现在**解码器层)**中使用,以提供将与解码器的问题张量交互的键和值。

# **解码器**
但在此之前,让我们进入下一步。解码器的底部。
在解码器的底部,我们有一个 **8x14 张量(BS x SeqLen)** ,它包含 8 个回复短语序列。像往常一样,在创建数据集和数据加载器时,这些短语已经被标记化和数值化了(并且它们根据需要包含填充标记)。
# 移动一位
需要注意的是,在**解码器**中,我们将序列向右移动一个位置。因此**第一个记号将是句子的开始记号**,而不是句子的第一个单词。我们为什么要这样做?
我们这样做是因为我们不希望我们的模型只是将解码器的输入复制并粘贴到其输出中。我们**希望它预测下一个单词**(或者字符,但是在这个例子中我们是预测单词)**。**因此,如果我们不将所有内容向右移动一位,位置 N 的预测将是解码器输入中位置 N 处的当前单词,我们可以直接访问它。为了防止这种情况发生,我们将解码器的输入向右移动一个位置。这样,在每个阶段,解码器必须预测位置 N,但是只能看到现有短语的位置 N-1。
# 解码器中的掩蔽
我们还创建了**解码器掩码**,它包含对角线上方的**真**和对角线下方的**假**。这个掩码有助于防止解码器考虑尚未看到的句子部分。
让我们更深入地了解这一点,因为它至关重要。**解码器的自我关注掩码**确保每个自我关注向量不会关注未来的位置。
因此,如果我在计算序列中位置 3 的单词的自我关注分数,我会屏蔽掉那个位置之后的所有位置。这是必要的,因为当我们构建我们的输出短语时,我们需要基于迄今为止生成的单词来执行我们的计算,并且我们不应该能够知道稍后将出现的未来单词。
在某种程度上,我们正在防止解码器在训练过程中作弊。例如,如果我们想要预测一个短语的第二个单词,我们应该只考虑输出短语的第一个位置。如果我们想预测第五个单词,我们应该只考虑第一、第二、第三和第四个位置。
请注意,解码器的掩码也屏蔽了输出序列中可能存在的填充标记。因此**解码器的屏蔽将**填充标记**的**屏蔽**添加到序列中未来位置**的屏蔽**中。**

与编码器一样, **8x14** 张量被发送到嵌入模块,该模块输出一个**8x 14x 32****(BS x Heads x SeqLen x EmbSize)**张量,因为嵌入大小是 32。接下来,将位置编码模块的结果添加到其中。

在这一点上,我们到达第一个解码器层,这将被重复许多次,作为我们希望有多少解码器层。
在解码器层,我们进入**两个连续的注意阶段**。
* 首先,我们将有一个**自我关注阶段**,非常类似于编码器阶段,但使用解码器数据。
* 接下来,我们将有一个**编码器-解码器关注阶段**,其中**问题(Q)张量**将来自解码器的**,但是**键(K)和值(V)张量**将来自**之前执行的**编码器**的输出**。您可以在信息图中找到这个混音阶段,如大的**箭头**所示,该箭头**将编码器的末端连接到解码器层的第二个注意阶段的键和值**。**

解码器的**第一自关注级**与编码器的相同,除了使用输出序列作为数据,并使用解码器的掩码。

在**第二注意阶段**、**编码器-解码器注意**中,发生了类似的过程,但有一些关键区别:
* **问题 Q 矢量**由 **8x14x32 (BS x SeqLen x EmbSize)解码器矢量**构成
* **键**和**值**向量、 **K 和 V** 由来自编码器相位结果的相同**8x10x 32(BS x SeqLen x EmbSize)**张量的两个副本形成。
我们在屏蔽阶段使用的**屏蔽**是在**编码器**中使用的,即输入序列一。这样,我们确保只考虑输出序列和没有填充标记的输入序列部分之间的连接。输出序列本身已经被解码器层的第一级屏蔽。

注意力分数不再是一个方阵。我们在**8x 4x 14x10(BS x Heads x InSeqLen x outeqlen)**张量内获得一个 14x 10 的矩阵,反映出我们正在获得的不同**部分**、**的输出**序列**与**的不同**部分**输入**序列**之间的**关系。**

像往常一样,在执行注意力计算之后,我们将结果连接起来,在这种情况下获得一个 **8x14x32 (BS x SeqLen x EmbSize)张量。**
在我们执行解码器层的自关注和编码器-解码器关注阶段之后,我们移动到同一层内的最后阶段,首先将 **8x14x32** 张量通过前馈模块,然后,正如我们在编码器中所做的那样,将该计算的结果添加到该模块(前馈模块)的输入,并将层归一化模块应用于该结果。(如编码器部分所述,在此过程以及其他过程中使用 dropout 是一种潜在的可选添加。)

然后,在现有数量的解码器层上重复该解码器层过程**。像以前一样,每个解码器层的输入和输出具有相同的形状,**8x 14x 32(BS x Heads x EmbSize)**,这使得很容易将这些层/过程中的一些链接起来。**
# **解码器的输出**
**一旦我们对所有解码器层进行了**迭代**,我们就获得了一个**最终的 8x14x32 张量**,然后我们将该张量传递给一个线性层,该层的输出形状为**8 x14 x950(BS x SeqLen x OutVocabSize)**, **950** 是聊天机器人输出的**词汇大小。****
**这个 **8x14x950** 张量包含了我们对于这次迭代的**预测**。对于该批中的每个序列,并且对于每个序列的 14 个部分中的每一个,我们获得 950 个值,对应于作为输出短语的下一个位置的候选的潜在的 950 个单词。**
****
**是时候计算损失了,我们的目标和我们当前的预测之间的差异。**
**我们将那个**预测**张量放入一个**交叉熵损失模块**,它也接收我们的 8x14 **目标**张量。交叉熵模块结果是训练过程的这次迭代的损失值。**
**该损失值然后通过网络**反向传播**,权重**被**更新**,并且过程**再次重启**,编码器处理新的批次。****
****
**我们继续尽可能多的时期的训练过程,直到我们达到我们的目标(在准确性、损失值、验证损失等方面)。每 x 次迭代或历元,我们保存网络的权重,以防我们想在其他时间重新开始训练,或者让最*训练的权重准备好随时执行推理。**
# **推理**
**这就是训练过程。现在让我们快速看一下推理过程。一旦转换器被训练,我们如何执行和运行它?**
**为此,我们必须关注信息图的绿色部分。**
**在图形的左下方,我们看到开始推理过程的绿色的**推理列。****
**当我们运行训练好的变形金刚时,我们将输入一个**的单一输入短语,例如****:你最喜欢《扩展》系列中的哪个角色?****
**这就是为什么我们的**批量**会是 **1** 的原因。我们仍然需要指定批量大小,因为计算需要它。这个短语被标记化和数字化,得到一个 **1x9 (BS x SeqLen)** 张量,因为这个示例短语有 9 个标记(["what's "," your "," favourite "," character "," in "," The "," expanse "," series ','?'].请注意,我们可以用许多不同的方式来标记短语,并且有许多标记器可供您使用。这个小例子使用了一种简单的方法来标记短语。**
**我们还创建我们的输入掩码,在这个推断阶段,它将在每个位置都有**真**。**
****
**接下来,我们将输入张量传递给嵌入模块,并将其与位置编码模块输出相加,以获得一个**1x9x 32(BS x Heads x EmbSize)**张量。**
****
# **编码器相位,推断**
**编码器的第一层以与训练迭代期间类似的计算开始,但这次使用这个 **1x9x32** 张量。编码器层重复直到我们到达最后一层,在那里我们获得一个 **1x9x32** 张量,它将被解码器用来提供编码器-解码器注意阶段的键和值。**
****
**我们转到解码器,这里的情况有所不同。**
# **解码器阶段、推理**
* **我们对解码器的输入最初将由**句首**令牌形成。(请记住,我们将所有内容向右移动一个位置,以防止解码器将其输入复制到输出中)。**
* **然后,解码器将输出我们应该添加到回复句子中的下一个单词作为结果(最初仅由**句首**标记形成)。**
* **我们将采用新的预测单词并将其添加到解码器的输入端,**重复过程**和**生成回复句子的下一个单词**。**
* **这再次被添加到解码器的输入,我们继续这样,直到解码器输出**句尾**标记。**
**因此,我们对解码器的输入最初将具有 **1x1** 的形状。在下一次迭代中,它将变成 **1x2** ,然后是 **1x3** 等等,直到它达到 **1xN** ,其中 **N** 是解码器循环的迭代次数,直到我们获得**句尾**令牌。**
**在循环中的每一点,我们创建一个适应每次迭代的新掩码。初始形状为 **1x1x1 (BS x SeqLen x SeqLen)** 。在下一次迭代中,它变成 **1x2x2** ,然后是 **1x3x3** ,直到到达 **1xNxN** ,这时我们到达了**句尾**标记。像以前一样,这个掩码帮助我们防止模型在计算注意力分数时关注序列中的未来位置(超出它正在考虑的当前位置)。**
****
**然后,我们将浏览多个**解码器层,**,每一层都执行我们之前看到的相同计算:**
* **一个**自我关注**阶段**
* **一个**编码器-解码器**注意级**
* ****前馈**阶段。**
****
**在解码器层的末尾,我们将获得一个**1 NX 950(BS x SeqLen x OutVocabSize)**张量,其中 N 是我们在解码器循环中的位置。第一次迭代我们得到一个 **1x1x950 张量**,第二次得到一个 **1x2x950 张量**等等。**
**我们将得到的张量通过一个 **softmax** 模块来获得一个**概率分布**。这种分布给了我们对于输出短语的每个**单词**获得**输出词汇表**的每个元素的**概率**。我们将考虑张量最后部分的概率,属于我们想要预测的下一个单词的概率。**
**我们可以从这个概率分布中以多种方式对**进行采样,以获得一个 **1x1 张量**,它包含将被添加到当前输出句子末尾的新单词**。******
****
**然后我们**继续循环并添加**新单词到输出句子中,直到我们找到**句尾标记**。**
**就是这样,我们有一个很酷的变形金刚聊天机器人,它的计算已经通过这个 **x 射线**变形金刚可视化展示给我们了。**
**您可以**从专门的 [github repo](https://github.com/javismiles/X-Ray-Transformer) 下载更大版本(10488 x 14000 像素)的 x 射线变压器可视化**:**
**[](https://github.com/javismiles/X-Ray-Transformer) [## javismiles/X 射线转换器
### 通过一个单一的视觉潜入变形金刚训练和推理计算。X 射线转换器信息图…
github.com](https://github.com/javismiles/X-Ray-Transformer)**
# 例外:使用 Tensorflow 从头实现
> 原文:<https://towardsdatascience.com/xception-from-scratch-using-tensorflow-even-better-than-inception-940fb231ced9?source=collection_archive---------6----------------------->
## 甚至比《盗梦空间》还要好

图一。Xception 架构(来源:图片来自原论文)
卷积神经网络(CNN)已经走过了漫长的道路,从 LeNet-style、AlexNet、VGG 模型(使用简单的卷积层堆栈用于特征提取,最大池层用于空间子采样,一个接一个地堆叠)到 Inception 和 ResNet 网络(在每层中使用跳过连接和多个卷积和最大池块)。自从引入以来,计算机视觉中最好的网络之一就是盗梦空间网络。Inception 模型使用一堆模块,每个模块包含一堆特征提取器,这允许它们用更少的参数学习更丰富的表示。
例外纸—[https://arxiv.org/abs/1610.02357](https://arxiv.org/abs/1610.02357)
正如我们在图 1 中看到的,异常模块有 3 个主要部分。入口流、中间流(重复 8 次)和出口流。

图二。Xception 架构的入口流(来源:图片来自原论文)
入口流具有两个卷积层块,后跟一个 ReLU 激活。该图还详细提到了过滤器的数量、过滤器大小(内核大小)和步长。
还有各种可分离的卷积层。还有最大池层。当步幅不等于 1 时,也提到步幅。也有跳过连接,我们使用“添加”来合并两个张量。它还显示了每个流中输入张量的形状。例如,我们从 299x299x3 的图像大小开始,在进入流之后,我们得到 19x19x728 的图像大小。

图 3。Xception 架构的中间和出口流程(来源:图片来自原论文)
类似地,对于中间流和出口流,该图清楚地解释了图像大小、各种层、过滤器的数量、过滤器的形状、池的类型、重复的数量以及最后添加完全连接的层的选项。
此外,所有卷积和可分离卷积层都要进行批量归一化。
## 可分离卷积层

图 4。可分离卷积层(来源:图片由作者创建)
> 可分离卷积包括首先执行深度方向的空间卷积(其分别作用于每个输入声道),然后执行混合所得输出声道的点方向卷积。-来自 Keras 文档
假设我们有一个大小为(K,K,3)的输入张量。k 是空间维度,3 是特征图/通道的数量。正如我们从上面的 Keras 文档中看到的,首先我们需要对每个输入通道分别实施深度方向的空间卷积。所以我们用 K,K,1——图像/张量的第一通道。假设我们使用大小为 3x3x1 的过滤器。并且该滤波器应用于输入张量的所有三个通道。因为有 3 个通道,所以我们得到的维数是 3x3x1x3。这在图 4 的深度方向卷积部分中示出。
在这之后,将所有 3 个输出集合在一起,我们获得大小为(L,L,3)的张量。L 的维数可以与 K 相同,也可以不同,这取决于先前卷积中使用的步长和填充。
然后应用逐点卷积。该滤波器的尺寸为 1x1x3 (3 个通道)。过滤器的数量可以是我们想要的任何数量。假设我们使用 64 个过滤器。所以总尺寸是 1x1x3x64。最后,我们得到一个大小为 LxLx64 的输出张量。这显示在图 4 的逐点卷积部分。
**为什么可分卷积比正规卷积好?**
如果我们在输入张量上使用正常卷积,并且我们使用 3x3x3 的滤波器/核大小(核大小— (3,3)和 3 个特征图)。我们需要的过滤器总数是 64 个。所以一共 3x3x3x64。
相反,在可分离卷积中,我们首先在深度方向卷积中使用 3x3x1x3,在点方向卷积中使用 1x1x3x64。
区别在于过滤器的维度。
**传统卷积层= 3 x3x 3 x 64 = 1728**
**可分离卷积层=(3 x3x 1 x 3)+(1 x1 x3 x 64)= 27+192 = 219**
正如我们所见,无论是在计算成本还是在内存方面,可分离卷积层都比传统卷积层更具优势。主要区别是在正常卷积中,我们是多次变换图像。而每一次变换都要用掉 3 x3x 3 x 64 = 1728 次乘法。在可分离卷积中,我们只对图像进行一次变换——在深度方向卷积中。然后,我们把变换后的图像简单地拉长到 64 通道。不必一次又一次地变换图像,我们可以节省计算能力。

图 5。ImageNet 上的异常性能 vs Inception(来源:图片来自原始论文)

图 6。JFT 数据集上的异常性能与初始(来源:图片来自原始论文)
**算法:**
1. 导入所有必要的层
2. 为以下各项编写所有必要的函数:
a.conv-巴特诺姆块体
b.可分离 Conv- BatchNorm 块
3.为 3 个流中的每一个写一个函数——入口、中间和出口
4.使用这些函数构建完整的模型
# 使用张量流创建异常
#import necessary libraries
import tensorflow as tf
from tensorflow.keras.layers import Input,Dense,Conv2D,Add
from tensorflow.keras.layers import SeparableConv2D,ReLU
from tensorflow.keras.layers import BatchNormalization,MaxPool2D
from tensorflow.keras.layers import GlobalAvgPool2D
from tensorflow.keras import Model
**创建 conv-巴特诺姆块:**
# creating the Conv-Batch Norm block
def conv_bn(x, filters, kernel_size, strides=1):
x = Conv2D(filters=filters,
kernel_size = kernel_size,
strides=strides,
padding = 'same',
use_bias = **False**)(x)
x = BatchNormalization()(x)**return** x
Conv 批处理范数块将张量——x、滤波器数量——滤波器、卷积层的内核大小——内核大小、卷积层的步距——步距作为输入。然后我们对 x 应用一个卷积层,然后应用批量归一化。我们添加 use_bias = False,这样最终模型的参数数量,将与原始论文的参数数量相同。
**创建 SeparableConv- BatchNorm 块:**
# creating separableConv-Batch Norm block
def sep_bn(x, filters, kernel_size, strides=1):
x = SeparableConv2D(filters=filters,
kernel_size = kernel_size,
strides=strides,
padding = 'same',
use_bias = **False**)(x)
x = BatchNormalization()(x)**return** x
与 Conv 批处理范数模块结构相似,只是我们使用了 SeparableConv2D 而不是 Conv2D。
**入口、中间和出口流量功能:**
# entry flow
def entry_flow(x):
x = conv_bn(x, filters =32, kernel_size =3, strides=2)
x = ReLU()(x)
x = conv_bn(x, filters =64, kernel_size =3, strides=1)
tensor = ReLU()(x)
x = sep_bn(tensor, filters = 128, kernel_size =3)
x = ReLU()(x)
x = sep_bn(x, filters = 128, kernel_size =3)
x = MaxPool2D(pool_size=3, strides=2, padding = 'same')(x)
tensor = conv_bn(tensor, filters=128, kernel_size = 1,strides=2)
x = Add()([tensor,x])
x = ReLU()(x)
x = sep_bn(x, filters =256, kernel_size=3)
x = ReLU()(x)
x = sep_bn(x, filters =256, kernel_size=3)
x = MaxPool2D(pool_size=3, strides=2, padding = 'same')(x)
tensor = conv_bn(tensor, filters=256, kernel_size = 1,strides=2)
x = Add()([tensor,x])
x = ReLU()(x)
x = sep_bn(x, filters =728, kernel_size=3)
x = ReLU()(x)
x = sep_bn(x, filters =728, kernel_size=3)
x = MaxPool2D(pool_size=3, strides=2, padding = 'same')(x)
tensor = conv_bn(tensor, filters=728, kernel_size = 1,strides=2)
x = Add()([tensor,x])**return** x
这里我们只需遵循图 2。它从分别具有 32 和 64 个滤波器的两个 Conv 层开始。每次激活后都有一个 ReLU 激活。
然后是一个跳过连接,这是通过使用 Add 完成的。
在每个 skip 连接块中,有两个可分离的 Conv 层,后跟 MaxPooling。跳跃连接本身具有跨度为 2 的 1x1 的 Conv 层。

图 7。中流量(来源:图片来自原论文)
# middle flow
def middle_flow(tensor):
**for** _ **in** range(8):
x = ReLU()(tensor)
x = sep_bn(x, filters = 728, kernel_size = 3)
x = ReLU()(x)
x = sep_bn(x, filters = 728, kernel_size = 3)
x = ReLU()(x)
x = sep_bn(x, filters = 728, kernel_size = 3)
x = ReLU()(x)
tensor = Add()([tensor,x])
**return** tensor
中间的流程遵循图 7 所示的步骤。

图 8。退出流程(来源:图片来自原纸)
# exit flow
def exit_flow(tensor):
x = ReLU()(tensor)
x = sep_bn(x, filters = 728, kernel_size=3)
x = ReLU()(x)
x = sep_bn(x, filters = 1024, kernel_size=3)
x = MaxPool2D(pool_size = 3, strides = 2, padding ='same')(x)
tensor = conv_bn(tensor, filters =1024, kernel_size=1, strides =2)
x = Add()([tensor,x])
x = sep_bn(x, filters = 1536, kernel_size=3)
x = ReLU()(x)
x = sep_bn(x, filters = 2048, kernel_size=3)
x = GlobalAvgPool2D()(x)
x = Dense (units = 1000, activation = 'softmax')(x)
**return** x
退出流程遵循图 8 所示的步骤。
**创建异常模型:**
# model code
input = Input(shape = (299,299,3))
x = entry_flow(input)
x = middle_flow(x)
output = exit_flow(x)
model = Model (inputs=input, outputs=output)
model.summary()
输出片段:

from tensorflow.python.keras.utils.vis_utils import model_to_dot
from IPython.display import SVG
import pydot
import graphviz
SVG(model_to_dot(model, show_shapes=True, show_layer_names=True, rankdir='TB',expand_nested=False, dpi=60, subgraph=False).create(prog='dot',format='svg'))
输出片段:

import numpy as np
import tensorflow.keras.backend as K np.sum([K.count_params(p) for p in model.trainable_weights])
输出:22855952
上述代码显示了可训练参数的数量。
**使用 Tensorflow 从头开始创建异常模型的完整代码:**
#import necessary libraries
import tensorflow as tf
from tensorflow.keras.layers import Input,Dense,Conv2D,Add
from tensorflow.keras.layers import SeparableConv2D,ReLU
from tensorflow.keras.layers import BatchNormalization,MaxPool2D
from tensorflow.keras.layers import GlobalAvgPool2D
from tensorflow.keras import Model# creating the Conv-Batch Norm block
def conv_bn(x, filters, kernel_size, strides=1):
x = Conv2D(filters=filters,
kernel_size = kernel_size,
strides=strides,
padding = 'same',
use_bias = **False**)(x)
x = BatchNormalization()(x)**return** x***# creating separableConv-Batch Norm block***
def sep_bn(x, filters, kernel_size, strides=1):
x = SeparableConv2D(filters=filters,
kernel_size = kernel_size,
strides=strides,
padding = 'same',
use_bias = **False**)(x)
x = BatchNormalization()(x)**return** x***# entry flow***
def entry_flow(x):
x = conv_bn(x, filters =32, kernel_size =3, strides=2)
x = ReLU()(x)
x = conv_bn(x, filters =64, kernel_size =3, strides=1)
tensor = ReLU()(x)
x = sep_bn(tensor, filters = 128, kernel_size =3)
x = ReLU()(x)
x = sep_bn(x, filters = 128, kernel_size =3)
x = MaxPool2D(pool_size=3, strides=2, padding = 'same')(x)
tensor = conv_bn(tensor, filters=128, kernel_size = 1,strides=2)
x = Add()([tensor,x])
x = ReLU()(x)
x = sep_bn(x, filters =256, kernel_size=3)
x = ReLU()(x)
x = sep_bn(x, filters =256, kernel_size=3)
x = MaxPool2D(pool_size=3, strides=2, padding = 'same')(x)
tensor = conv_bn(tensor, filters=256, kernel_size = 1,strides=2)
x = Add()([tensor,x])
x = ReLU()(x)
x = sep_bn(x, filters =728, kernel_size=3)
x = ReLU()(x)
x = sep_bn(x, filters =728, kernel_size=3)
x = MaxPool2D(pool_size=3, strides=2, padding = 'same')(x)
tensor = conv_bn(tensor, filters=728, kernel_size = 1,strides=2)
x = Add()([tensor,x])**return** x***# middle flow***
def middle_flow(tensor):
**for** _ **in** range(8):
x = ReLU()(tensor)
x = sep_bn(x, filters = 728, kernel_size = 3)
x = ReLU()(x)
x = sep_bn(x, filters = 728, kernel_size = 3)
x = ReLU()(x)
x = sep_bn(x, filters = 728, kernel_size = 3)
x = ReLU()(x)
tensor = Add()([tensor,x])
**return** tensor***# exit flow***
def exit_flow(tensor):
x = ReLU()(tensor)
x = sep_bn(x, filters = 728, kernel_size=3)
x = ReLU()(x)
x = sep_bn(x, filters = 1024, kernel_size=3)
x = MaxPool2D(pool_size = 3, strides = 2, padding ='same')(x)
tensor = conv_bn(tensor, filters =1024, kernel_size=1, strides =2)
x = Add()([tensor,x])
x = sep_bn(x, filters = 1536, kernel_size=3)
x = ReLU()(x)
x = sep_bn(x, filters = 2048, kernel_size=3)
x = GlobalAvgPool2D()(x)
x = Dense (units = 1000, activation = 'softmax')(x)
**return** x***# model code***
input = Input(shape = (299,299,3))
x = entry_flow(input)
x = middle_flow(x)
output = exit_flow(x)
model = Model (inputs=input, outputs=output)
model.summary()
**结论:**
如图 5 和图 6 所示,与 ImageNet 数据集相比,Xception 架构在 JFT 数据集上显示出比 Inception 网络更好的性能提升。《例外》的作者认为,这是因为《盗梦空间》被设计成专注于 ImageNet,因此可能过度适合特定的任务。另一方面,这两种架构都没有针对 JFT 数据集进行过调整。
此外,Inception 有大约 2360 万个参数,而 Xception 有 2280 万个参数。
如图 1 所示,本文中很容易解释 Xception 架构,这使得使用 TensorFlow 实现网络架构变得非常容易。
**参考文献:**
1. Franç ois Chollet,Xception:深度可分卷积深度学习,[arXiv:1610.02357 v3](https://arxiv.org/abs/1610.02357v3)**【cs。CV],2017**
# 例外:迎接极限开端
> 原文:<https://towardsdatascience.com/xception-meet-the-xtreme-inception-db569755f4d6?source=collection_archive---------29----------------------->

除了作者的极端盗梦空间- *图片*
# 异常简介
例外-极限盗梦空间!听起来又酷又极端!而是“为什么取这样的名字?”,有人可能会想!一个显而易见的事情是,作者 Francois Chollet(《Keras》的创作者)受到了《盗梦空间》架构的启发。他在他的摘要中讲述了他是如何看待盗梦空间架构的,我在下面引用了他的摘要。
> 我们将卷积神经网络中的初始模块解释为常规卷积和深度方向可分离卷积运算之间的中间步骤
>
> ——弗朗索瓦·乔莱在《例外报》
今天另一个新的深度学习术语,“*”深度可分卷积。现在,我知道你们大多数人会猜测它是某种层次。但是对于那些从未听说过的人来说,不要担心。我在这篇文章中为你做了介绍。我们将深入了解什么是 ***深度方向可分离卷积*** 以及如何使用它来构建异常模型。以及它是如何建立在盗梦空间假设上的。和往常一样,我会试着插入更多的插图,让细节更加清晰。让我们开始吧!*
# *盗梦空间假说*
*在“ ***用卷积*** ”一文中介绍了 Inception 风格的架构。作者将论文中介绍的模型称为 ***GoogLeNet,*** 使用了 Inception 块。这是一个新颖和创新的建筑,现在仍然如此。此外,由于当时的许多体系结构是越来越多的层的堆叠,以增加网络容量,因此它受到了广泛关注。另一方面,《盗梦空间》更有创意,更巧妙!*
*它不仅仅是通过简单地增加更多的层而变得更深,还变得更广。我们很快就会明白我所说的“宽”是什么意思。初始块接受输入张量,并并行执行卷积和池化的组合。*
**
**作者图片**
*对于看过或读过《盗梦空间》的人来说,你可能会发现这并不完全像一个盗梦空间。是啊,你说得对!我只是用这种方式举例说明,这样每个人都能大致了解它的功能。正如作者所说,你可以称之为“天真版的盗梦空间”。*
*现在,实际的 Inception 块在卷积的数量、大小以及分层方式方面有一点不同。但是这个幼稚的插图传达了我之前说的“宽”的意思。该模块并行执行不同滤波器大小的卷积。并且输出张量沿着信道维度连接,即一个接一个地堆叠。*
## *再深入一点*
*现在您已经看到了并行卷积模块,我将更深入地介绍上面的模块。由不同卷积处理的输入张量将是形状(批量、高度、宽度、通道)。在初始块中,在应用 3x3 或 5x5 卷积之前,使用 1x1 卷积来降低输入张量的通道维度。这种通道尺寸的减小是为了在将该张量馈送到后续层时减少计算。你可以在 [1x1 卷积](https://hackerstreak.com/1x1-convolution/)文章中找到这个概念的详细解释。*
**
**作者图片**
*同样,这只是另一个需要理解的描述,并不与最初的 Inception 块相同。你可以给或拿一些层,甚至更宽,并做出你自己的版本。输入张量由三个卷积塔分别处理。并且三个独立的输出张量沿着信道维度连接。GoogLeNet 使用多个 Inception 块和一些其他技巧和调整来实现它的性能。我相信,现在你已经知道盗梦空间是做什么的了。*
*接下来,我们将改造上述区块,使其成为**“极限”!***
## *让它成为极致*
*我们将用单层替换每个*行塔中的三个独立的 ***1x1*** 层。它看起来会像这样。*
**
*具有*普通 1x1 层的盗梦空间-作者图片**
*我们不是将 1x1 层的输出传递给后面的 3x3 层,而是将它切片并分别传递每个通道。我举个例子说明一下。比方说, ***1x1*** 层的输出是形状 ***(1x5x5x5)。*** 我们先不考虑批量维度,只把它看成一个 ***(5x5x5)*** 张量。这是*沿通道尺寸切割*,如下图所示,并分别送入后续层。*
**
**作者沿信道维度对 1x1 卷积的输出张量进行切片-图像**
*现在,每个切片都被传递到一个单独的 3x3 层。这意味着每个 3x3 块将不得不处理一个形状为 ***(5x5x1)的张量。*** 因此,将有 5 个单独的卷积块,每个切片一个。并且每个卷积块将只有一个滤波器。*
**
*每个通道切片被馈送到一个单独的 3×3 层,每个层只有一个过滤器*
*同样的过程也适用于更大的输入张量。如果逐点卷积的输出是***(5x5x 100)***,则有 100 个卷积块,每个卷积块有一个滤波器。他们所有的输出最终会被连接起来。这种做卷积的方式就是它被命名为 ***极端*** 的原因,因为输入张量的每个通道都是单独处理的。*
*我们已经看够了《盗梦空间》的想法和它的一个 ***极限*** 版本。现在,让我们进一步看看是什么驱动了 Xception 架构。*
# *深度方向可分卷积:驱动异常的东西*
*深度方向上可分离的卷积层是这个例外的动力。它在建筑中大量使用了这一点。这种卷积类似于我们上面看到的初始块的极端版本。但其工作原理略有不同。让我们看看如何!*
*考虑在***(1x 10 X10 X100)***张量上操作的具有十个 5×5 滤波器的典型卷积层。十个滤波器中的每一个都具有 ***(5x5x100)*** 的形状,并且在输入张量上滑动以产生输出。每个 5x5 滤波器在滑过输入时覆盖整个通道尺寸(整个 100)。这意味着典型的卷积运算包括空间维度(高度、宽度)和通道维度。*
**
*作者在(10x10x100)张量上滑动的 5x5 卷积滤波器— *图像**
*如果你不熟悉卷积,我建议你浏览一下[卷积是如何工作的?](https://hackerstreak.com/1x1-convolution/#how-convolution-works)*
*深度方向可分离层具有两个功能部分,这两个功能部分分割了传统卷积层的工作。这两部分是深度方向卷积和点方向卷积。我们会一个一个地检查。*
## *深度方向卷积*
*让我们以具有*滤波器的深度方向卷积层为例,该滤波器对形状为***(1×5×5×5)***的输入张量进行操作。同样,为了简单起见,我们去掉批处理维,因为它不会改变任何东西,并将其视为一个 ***(5x5x5)*** 张量。我们的深度方向卷积将具有五个 ***3x3*** 滤波器,每个滤波器用于输入张量的每个通道。并且每个滤波器将在空间上滑过单个通道,并为该通道生成输出特征图。**
**由于滤波器的数量等于输入通道的数量,输出张量也将具有相同数量的通道。让我们在卷积运算中没有任何零填充,保持 ***步距*** 为 *1。***
****
**作者应用卷积- *图像后输出高度和宽度计算***
**按照卷积后输出大小的公式,我们的 ***(5x5x5)*** 将变成一个 ***(3x3x5)*** 张量。下面的插图将使这个想法变得清晰!**
****
***作者对深度方向卷积运算-图像的说明***
**这是深度方向的卷积!你可以看到这和我们在《盗梦空间》中做 ***极限*** 卷积的方式差不多。**
**接下来,我们必须将这个输出张量馈送到执行**跨通道相关**的逐点卷积。这仅仅意味着它在张量的所有通道上运行。**
## **逐点卷积**
**逐点卷积是 ***1x1*** 卷积的另一个名字。如果我们想增加或减少张量的深度(通道维度),我们可以使用逐点卷积。这也是为什么在《盗梦空间》中使用它来减少 ***3x3*** 或 ***5x5*** 层之前的深度。在这里,我们要用它来增加深度。但是怎么做呢?**
**逐点卷积只是一个普通的卷积层,滤镜大小为**一个** ( ***1x1*** 滤镜)。因此,它不会改变卷积后的空间输出大小。在我们的例子中,深度方向卷积的输出张量的大小为 ***(8x8x5)*** 。如果我们应用 50 个 ***1x1*** 滤镜,我们将得到输出为 ***(8x8x50)*** 。并且 ***RELU*** 激活被应用于逐点卷积层。**
**参见[逐点卷积](https://hackerstreak.com/1x1-convolution/)了解更多详细说明及其优点。**
**结合深度方向卷积和点方向卷积,我们得到了**深度方向可分离卷积。**从这里姑且称之为 ***DSC*** 。**
# **DSC 和 e Xtreme Inception 的区别**
**在初始块中,首先是逐点卷积,然后是 3x3 或 5x5 层。因为我们将把 ***DSC*** 模块堆叠在其他模块之上,所以顺序并不重要。初始块在逐点和随后的卷积层上应用激活函数。但是在 ***DSC 中,*** 只是应用一次,在逐点卷积之后。**
****
***作者图片***
**e Xception 作者讨论了激活对 DSC 中深度方向和点方向步骤的影响。并且观察到当没有中间激活时,学习会更快。**
****
***作者对带有和不带有中间激活图像的 DSC 的图解***
# **例外架构**
**作者将整个 Xception 架构分成 14 个模块,其中每个模块只是一堆***【DSC】和池层。这 14 个单元分为三组,即入口流、中间流和出口流。并且每个组分别具有四个、八个和两个模块。最后一组,即出口流,可以选择性地在末端具有完全连接的层。*****
> ***注意:架构中的所有 DSC 层都使用 3x3 的滤波器大小、步长 1 和“相同”填充。并且所有的 MaxPooling 层都使用 3x3 内核和步幅 2。***
## ***异常的进入流程***
******
***作者的 exception-*图片进入流程****
***上图是例外论文中给出的详细版本。乍一看可能有点吓人,但再看看,这非常简单。***
***第一个模块包含常规卷积层,它们没有任何 ***DSC*** 层。它们接受大小为(-1,299,299,3)的输入张量。第一维度的 **-1** 代表批量。负的 **-1** 仅表示批量大小可以是任何值。***
**常规卷积层和 DSC 卷积层之后都有一个批量归一化层。步幅为 2 的卷积将其减少了*一半。输出的形状显示在使用我们之前看到的卷积公式计算的一侧。**
****
**入口流中的第一个卷积模块- *作者的图像***
**除了第一个模块,入口流中的所有其他模块都有剩余的跳过连接。并行跳跃连接具有一个逐点卷积图层,该图层被添加到主路径的输出中。**
## **例外的中间流程**
****
***作者对中间流图像的说明***
**中间流程中,有 ***八个*** 这样的模块,一个接一个。以上模块重复八次,形成中流。中间流中的所有 8 个模块都使用 1 的步距,并且没有任何池层。因此,从入口流通过的张量的空间大小保持不变。通道深度也保持不变,因为所有中间流模块都有 728 个过滤器。这与输入的深度相同。**
## **例外的出口流量**
****
**退出流程- *作者图片***
**出口流只有两个卷积模块,第二个没有任何跳过连接。第二个模块使用全局*均池,不像前面的模块使用最大池。*均汇集层的输出向量可以直接馈送到逻辑回归层。但是我们也可以选择使用中间全连接层。**
# **综上**
**Xception 模型包含与 Inception V3 几乎相同数量的参数,但是在 ImageNet 数据集上比 Inception V3 略胜一筹。但它在 JFT 图像分类数据集(谷歌的内部数据集)上以更好的优势击败了 Inception V3。在几乎相同的参数数量下表现更好,可以归功于其架构工程。**
# XGBoost
> 原文:<https://towardsdatascience.com/xgboost-b736c2ab10ce?source=collection_archive---------18----------------------->
## [梯度助推器](http://towardsdatascience.com/tagged/the-gradient-boosters)
## 震撼世界的 GBM

来源: [Unsplash](https://unsplash.com/photos/-IZ2sgQKIhM)
现在让我们来解决这个问题——XGBoost。这是梯度推进家族里最受欢迎的堂弟。XGBoost 以其闪电般的速度冲进了现场,几乎一致地扭转了对它有利的局面。很快,通过 XGBoost 的梯度推进成为 Kaggle 竞赛中的王者,很快,它就渗透到了商业世界。
有几项关键创新让 XGBoost 如此有效:
# 显式正则化
与正则化贪婪森林相似,XGBoost 在目标函数中也有一个显式正则项。

γ是惩罚 *T* 的正则项,即树中叶子的数量,λ是惩罚 *w* 的正则项,即不同叶子的权重。
这是一个比我们在正则化贪婪森林中看到的一些方法简单得多的正则化术语。
# 目标函数不可知的实现
梯度推进算法的关键要素之一是目标函数的梯度或导数。我们之前看到的所有实现都使用了针对特定损失函数的预先计算的梯度公式,从而将算法中可以使用的目标限制为库中已经实现的集合。
XGBoost 使用我们在本系列前一部分讨论过的牛顿-拉夫森方法来逼*损失函数。

现在,由树结构组成的复杂递归函数可以使用泰勒*似法*似为可微分形式。在梯度推进的情况下,我们采用二阶*似,这意味着我们使用两项——一阶导数和二阶导数——来*似函数。
让,

*似损失函数:

第一项,损失,在树构建阶段 t 是恒定的,因此它不会给优化目标增加任何值。所以去掉它并简化我们得到的,

将ω代入正则项,我们得到:

我们正在讨论的 f(x)本质上是一棵具有叶权重 w 的树。因此,如果我们将 Iⱼ定义为叶 j 中的实例集,我们可以将树结构直接代入方程,并简化为:

将该等式设为零,我们可以找到 wⱼ的最佳值,如下所示:

将它放回损失函数并简化,我们得到:

所有这些使我们能够做的是将目标函数从算法的核心工作中分离出来。通过采用这个公式,对目标函数/损失函数的唯一要求是它必须是可微的。具体来说,损失函数应该返回一阶和二阶导数。
参见[此处的](https://xgboost.readthedocs.io/en/latest/parameter.html#learning-task-parameters)获取所有预构建到 XGBoost 中的目标函数列表。
# 树构建策略
树构建策略在某种程度上介于经典梯度推进和正则化贪婪森林之间。传统的梯度增强将每一阶段的树视为一个黑盒,而正则化贪婪森林通过在每一步更新森林的任何部分来在叶子级别上操作。XGBoost 采取了中间立场,认为在前一次迭代中生成的树是神圣不可侵犯的,但是在为任何迭代生成树时,它不使用标准的杂质度量,而是使用我们在树构建过程的上一节中导出的基于梯度的损失函数。在经典的梯度提升中,损失函数的优化发生在树建立之后,而 XGBoost 也在树建立过程中得到优化。
通常不可能列举出所有可能的树形结构。因此,实现了一种贪婪算法,该算法从单叶开始,并基于简化的损失函数迭代地向树添加分支
# 分裂查找算法
树学习的一个关键问题是寻找最佳分裂。通常,我们必须列举所有特征上所有可能的分裂,然后使用杂质标准来选择最佳分裂。这就是所谓的*精确贪婪算法*。它需要大量的计算,尤其是对于连续的和高基数的分类特征。当数据放不进内存时,这也是不可行的。
为了克服这些低效,本文提出了一种*似算法。它首先根据特征的百分位数提出候选分割点。概括地说,算法是:
1. 使用特征的百分位数为所有特征提出候选分割点
2. 将连续要素映射到由候选分割点分割的桶
3. 基于候选分割点聚合所有要素的梯度统计数据
4. 根据汇总统计数据在建议中寻找最佳解决方案
## 加权分位数草图
上面讨论的算法中的一个重要步骤是候选分裂的提议。通常,特征的百分位数用于使候选项在数据上均匀分布。一组以分布式方式快速有效地完成这项工作的算法称为分位数绘制算法。但是在这里,问题有点复杂,因为需要一个加权分位数草图算法,该算法根据梯度对实例进行加权(这样我们就可以从误差最大的实例中学习到最多的东西)。因此他们提出了一种新的数据结构,这种数据结构具有可证明的理论保证。
# 稀疏感知的分裂发现
这是 XGBoost 的另一个关键创新,它来自于现实世界的数据集是稀疏的。这种稀疏性可能有多种原因,
1. 数据中存在缺失值
2. 频繁零值
3. 特征工程的产物,例如一键编码
为此,作者决定使算法意识到稀疏性,以便它能被智能地处理。他们制作的方式简单得令人难以置信。
他们在每个树节点给出了一个默认的方向。即,如果值缺失或为零,则该实例在分支中沿默认方向向下流动。并且从数据中学习最佳默认方向
这项创新有双重好处-
1. 它有助于对丢失的值进行分组,学习处理它们的最佳方式
2. 它通过确保不浪费计算能力来寻找缺失值的梯度统计数据,从而提高了性能。
# 性能改进
梯度推进算法的所有实现的主要缺点之一是它们非常慢。虽然随机森林中的森林创建是开箱即用的,但梯度提升是在旧基础上建立新基础学习者的顺序过程。XGBoost 出名的原因之一是它的速度惊人。它比现有的实现至少快 10 倍,并且由于具有核外学习功能,它能够处理大型数据集。性能改进的主要创新包括:
# 用于并行学习的列块
树学习最耗时的部分是将数据排序。该论文的作者建议将数据存储在内存单元中,称为块。每个块中的数据以[压缩列(CSC)格式](http://netlib.org/linalg/html_templates/node92.html)排序。这种输入数据布局在训练之前计算一次,然后再使用。通过以这种排序格式处理数据,树分裂算法简化为对排序列的线性扫描
# 缓存感知访问
虽然所提出的块结构有助于优化分裂查找的计算复杂度,但是它需要通过行索引间接获取梯度统计。为了克服过程中缓慢的读写操作,作者为每个线程实现了一个内部缓冲区,并在迷你批处理中累积梯度统计信息。当行很大时,这有助于减少运行时开销。
# 核外计算的块
算法的目标之一是充分利用机器的资源。当 CPU 被进程的并行化利用时,可用的磁盘空间被核外学习利用。我们之前看到的这些数据块存储在磁盘上,一个单独的预取线程不断将数据提取到内存中,以便计算继续进行。他们使用两种技术来提高磁盘 I/O 操作的速度
* 数据块压缩,即每个数据块在存储前被压缩,在读取时被动态解压缩。
* 块分片,将一个块分成多个部分,并存储在多个磁盘上(如果有)。并且预取器通过在两个盘之间交替来读取块,由此增加了盘读取的吞吐量。
# 超参数
XGBoost 有很多关于它的文章和博客,涵盖了超参数以及如何调优它们,所以我甚至不打算尝试。
任何超参数的唯一真实来源是官方文档。看着那里长长的超参数列表可能有些吓人,但是在正常的用例中,您不会碰到其中的大多数。
主要的有:
**eta[默认值=0.3,别名:learning_rate]**
* 更新中使用的步长收缩以防止过度拟合。每一步 boosting 后,我们可以直接得到新特征的权重,eta 收缩特征权重,使 boosting 过程更加保守。
* 范围:[0,1]
**伽马[默认值=0,别名:min_split_loss]**
* 在树的叶节点上进行进一步划分所需的最小损失减少。伽玛越大,算法就越保守。
* 范围:[0,∞]
**最大深度[默认值=6]**
* 树的最大深度。增加该值将使模型更加复杂,并且更有可能过度拟合。当 tree_method 设置为 hist 时,0 仅在损失导向增长策略中被接受,并且它指示对深度没有限制。请注意,XGBoost 在训练深度树时会大量消耗内存。
* 范围:[0,∞](当 tree_method 设置为 hist 时,仅在损失导向增长策略中接受 0)
**最小 _ 子 _ 重量[默认值=1]**
* 一个孩子所需的最小体重总和。如果树划分步骤导致实例权重之和小于 min_child_weight 的叶节点,则构建过程将放弃进一步的划分。在线性回归任务中,这仅仅对应于每个节点中需要的最小实例数。min_child_weight 越大,算法就越保守。
* 范围:[0,∞]
**子样本[默认值=1]**
* 训练实例的子样本比率。将其设置为 0.5 意味着 XGBoost 会在生成树之前随机采样一半的训练数据。这将防止过度拟合。子采样将在每个提升迭代中发生一次。
* 范围:(0,1)
**列样本 _ 字节树,列样本 _ by 级别,列样本 _ by 节点[默认值=1]**
* 这是一组用于对列进行二次采样的参数。
* 所有 colsample_by*参数的范围为(0,1),默认值为 1,并指定要进行二次抽样的列的分数。
* colsample_bytree 是构造每个树时列的子样本比率。对于每个构建的树,进行一次子采样。
* colsample_bylevel 是每个级别的列的子样本比率。对于树中达到的每个新的深度级别,进行一次子采样。从为当前树选择的列集中对列进行子采样。
* colsample_bynode 是每个节点的列的子样本比率(拆分)。每次评估新的分割时,进行一次二次采样。从为当前级别选择的列集中对列进行子采样。
* colsample_by*参数累积工作。例如,具有 64 个特征的组合{'colsample_bytree':0.5,'colsample_bylevel':0.5,'colsample_bynode':0.5}将在每次分割时留下 8 个特征可供选择。
**lambda[默认值=1,别名:reg_lambda]**
* 关于权重的 L2 正则化项。增加该值将使模型更加保守。
**alpha[默认值=0,别名:reg_alpha]**
* 关于权重的 L1 正则项。增加该值将使模型更加保守。
更新—2020 年 2 月 13 日
# 树木逐叶生长
在发表这篇文章之后,我意识到我还没有谈到 XGBoost 中的一些后续开发,比如逐叶树的生长,以及如何针对新的更快的实现对参数进行微调。
我们将在本系列的下一篇博客中讨论 LightGBM,它实现了逐叶的树增长,这带来了巨大的性能提升。XGBoost 还在基于直方图的树分裂策略中实现了逐叶策略。
逐叶增长策略虽然更快,但如果数据很小,也会更快地过度拟合。因此,通过超参数使用正则化或帽树复杂性是非常重要的。但是如果我们只是像以前一样继续调 *max_depth* 来控制复杂度,那就不行了。 *num_leaves* (控制一棵树的叶子数量)也需要一起调。这是因为在相同的深度下,逐叶树生长算法可以生成更复杂的树。
通过将 *tree_method* 参数设置为“hist”并将 *grow_policy* 参数设置为“lossguide ”,可以在 XGBoost 中启用逐叶树构建
[*中的其他文章*](http://towardsdatascience.com/tagged/the-gradient-boosters)
* [良好的旧梯度增强](/the-good-old-gradient-boosting-f4614b0e62b0)
* [正规化的贪婪森林](/regularized-greedy-forest-a17cd0c85f06)
* [XGBoost](/xgboost-b736c2ab10ce) (你在这里)
* [LightGBM](/lightgbm-800340f21415)
* CatBoost
# 参考
1. 贪婪函数*似:一种梯度推进机器。安。统计学家。29 (2001 年),第 5 号,1189-1232。
2. 陈,田琦&盖斯特林,卡洛斯。(2016).XGBoost:一个可扩展的树提升系统。785–794.10.1145/2939672.2939785.
*原载于 2020 年 2 月 12 日*[*【http://deep-and-shallow.com】*](https://deep-and-shallow.com/2020/02/12/the-gradient-boosters-iii-xgboost/)*。*
# XGBoost 用于多类分类
> 原文:<https://towardsdatascience.com/xgboost-for-multi-class-classification-799d96bcd368?source=collection_archive---------2----------------------->
## 在这篇文章中,我使用 XGBoost 根据客户的个人资料来预测不同的接触点

照片由阿图尔·斯兹比罗在 Shutterstock 上拍摄
客户接触点是你的品牌自始至终的客户接触点。例如,客户可能会通过邮件折扣、短信、电子邮件促销等找到你的业务。根据您公司的营销团队,有许多类型的接触点!
接触点预测是一个类似于客户细分的重要工具,因为如果一个公司的营销工作以接触点更有可能最终购买的特定客户群为目标,它可以更好地服务于该公司的营销工作。通过他们的客户档案,公司将更深入地了解客户的偏好,并针对每个客户执行精确定制的营销材料。
该项目的目的是根据每个客户的个人资料和之前的接触点,预测他们的下一个最佳接触点行动。让我们看看我们需要做些什么!

图一。客户数据
当我探索一个数据集时,我喜欢做笔记,这允许我保留一个清单,以便在项目的后期进一步清理/探索。从第一眼看,我们可以看到 SocialMedia 列中缺少值,而在 touch points 列下,我们看到一系列可能导致购买的接触点。
# 数据清理:每个数据科学项目中最重要的一步
检查缺失值和插补!

我们看到 25%的条目没有接触点,2.9%的条目没有信用评级。缺少接触点可能意味着客户无需通过任何在线推广链接就可以购买。信用评级的缺失可能会带来新的客户。
我还注意到 SocialMedia 列下缺少的值用空格表示。
处理每列的缺失值:
1。社交媒体—将“”改为“U”(表示未知的社交媒体状态)
2。信用评级—将 NaN 改为“新”(表示新客户)
3 .接触点—我假设接触点是按从左到右的顺序排列的,所以最后一个值是客户在购买前的最*接触点。因此,我将只取最后一个值作为标签,来预测应该为未来的客户分配哪个接触点
data = df[df['nTouchpoints']!=0].reset_index().drop('index', axis=1)
data['recent_touchpoint'] = data['touchpoints'].apply(lambda x: x.split()[-1])
data.recent_touchpoint.value_counts()

我们标签的接触点价值计数

社会媒体和信用评级的估算值
# 数据可视化
我想探究细分变量与数据集中其他变量的关系。一些可能性可以是基于收入、*均支出、信用评级或组合的细分。我们必须检查任何变量之间的多重共线性。多重共线性降低了估计系数的精度,这削弱了我们的回归模型的统计能力。我将用图表和热图来探索这些关系。

收入图

*均支出图
从 P1 到 P4 的每条线图中,我们无法观察到每个信用评级的*均支出的一般模式。紫色线显示了每个信用评级组的*均支出及其 95%的置信区间。比较两个图,似乎信用等级为 6 的高收入者比其他人花费少,信用等级为 7 的低收入者比其他人花费多。

共线性图
我们看到接触点的数量与*均支出高度相关,但是相关性低于 0.5。由于多重共线性的程度并不严重,所以我将保持变量不变。
# 数据探索
当您构建模型时,偏态分布可能是一个棘手的问题,尤其是在有异常值的情况下。检查异常值的最好方法是绘制分布图!机器学习的一般规则是确保我们所有的数值变量都在大致相同的范围内,并且呈正态分布,因此我们必须进行标准化/规范化。

我做的一些观察:
1.年龄分布看起来呈正态分布,略微偏左
2。已婚>单身>离异>未知客户数量。
3。对于细分市场和社交媒体,每个类别中的客户数量大致相同。
4。不同信用等级的客户分布看起来很正常,只是稍微有点偏右。
5。大多数顾客只经过一个接触点
6。收入分配看起来很正常。
7。*均支出分布可以被认为是指数分布,但显然有异常值,我们必须处理。

支出中的异常值

我删除了所有收入为 18156.7 美元的客户,因为我们的*均支出仅为 91.52 美元,远远低于最大值。将它们留在数据中只会扭曲我们的支出分布。移除异常值时,我们必须确保*均值/中值不会受到很大影响,并注意我们不会引入任何偏差。
请注意,所有 3 个变量的最大-最小范围互不相同。当我们做进一步的分析时,比如多元线性回归,归因收入会由于其更大的值而内在地影响结果。因此,重要的是将数据标准化和规范化,使所有变量都在同一范围内。
我使用一个稳健的定标器(QuantileTransformer):类似于归一化,但它使用四分位数范围,所以它对异常值是稳健的。

标准化数值变量
# 为模型构建准备数据
我们使用一次性编码将分类变量(婚姻、细分、社交媒体、信用评级)转换为二进制变量。我们可以使用来自 sklearn 的内置 OneHotEncoder 中的[,但是出于同样的目的,我选择编写自己的函数!](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html)

一个热编码分类变量
现在我们的数值变量遵循正态分布。我们将开始使用最*的接触点来标记我们的数据。让我们看看在我们的数据中有多少可能的标签。

创建从标签到唯一整数的映射,反之亦然,以便稍后进行标签和预测

对于我们的模型数据,选择我们希望在模型中使用的列,我将使用分层抽样来检索它们。关于分层抽样的更多细节,我在之前的帖子中解释了这个过程——[Keras,告诉我我的书的类型。](/keras-tell-me-the-genre-of-my-book-a417d213e3a1)

# 用 GridSearchCV 建模和调优
作为基线模型,我使用随机森林。**基线**是一种使用启发式、简单汇总统计、随机性或机器学习来为数据集创建预测的方法。你可以使用这些预测来衡量**基线的**性能(例如,准确性)——这个指标将成为你与任何其他机器学习算法进行比较的标准。在这种情况下,我使用多类逻辑损失,因为我们预测下一个接触点的概率,我想找到所有概率分布之间的*均差异。此外,我还使用了微型 F1 分数,因为我们的标签类别不*衡。

随机森林模型的结果
我选择随机森林分类器只是因为它运行速度快,并且我能够使用 GridSearchCV 高效地迭代到可能的最佳模型。在用 GridSearchCV 初始化和调整我的 RandomForestClassifier 模型之后,我得到了 1.0 的训练精度和 0.77688 的测试精度,这表明过度拟合。

我们的随机森林分类器似乎更关注*均支出、收入和年龄。
# XGBoost
[XGBoost](https://xgboost.ai/) 是一种基于决策树的集成机器学习算法,使用了[梯度提升](https://en.wikipedia.org/wiki/Gradient_boosting)框架。在涉及非结构化数据(图像、文本等)的预测问题中。)人工神经网络往往优于所有其他算法或框架。然而,当涉及到中小型结构化/表格数据时,基于决策树的算法目前被认为是同类最佳的。
XGBoost 的实现为模型调优、计算环境和算法增强提供了几个高级特性。它能够执行三种主要形式的梯度增强(梯度增强(GB)、随机 GB 和正则化 GB),并且足够健壮以支持微调和增加正则化参数。
这种集成方法试图基于先前的“较弱”分类器来创建强分类器。通过迭代地在彼此之上添加模型,前一个模型的误差被下一个预测器校正,直到训练数据被模型准确地预测或再现。长话短说,我们正在使用梯度下降更新模型!XGBoost 一直是赢得许多 Kaggle 比赛的秘密秘诀,所以现在你知道为什么这种方法在机器学习爱好者中如此受欢迎了。
对于我们最初的模型,这是我得到的结果。

正如我们所看到的,XGBoost 在第一次模型迭代中已经超越了 Random Forest。让我们开始微调我们的模型,尽管我不会详细介绍如何调整我的模型。关于逐步调谐的更多信息可以在[这里](https://www.analyticsvidhya.com/blog/2016/03/complete-guide-parameter-tuning-xgboost-with-codes-python/)找到!
我调整的参数:
min_child_weight:一个节点可以表示的最小样本数,以便进一步拆分
max_depth:调整这个来避免我们的树长得太深而导致过度拟合
reg_alpha:规范化程度
# 最终模型

与我们的 XGBoost 模型的第一次迭代相比,我们设法在准确性和微观 F1 分数方面略有提高。我们实现了更低的多级物流损失和分类错误!

我们看到高特征重要性分数被分配给“未知”婚姻状态。这可能是因为只有 44 个客户的婚姻状况“未知”,因此为了减少偏差,我们的 XGBoost 模型为“未知”特征分配了更多的权重。
如果我有足够的计算能力,我会调整 gamma,subsample 和 colsample_bytree 以及学习率。由于时间有限,我只关注 max_depth 和 reg_alpha(应用正则化来减少过度拟合)。试一试,用参数玩一玩!
完整的代码实现可以在我的 Github [这里](https://github.com/ernestng11/touchpoint-prediction)找到!
感谢您阅读我的文章。如果我能得到关于如何改进我的数据科学项目的评论,我将不胜感激,我也一直希望与任何对机器学习感兴趣的人合作:)
干杯!
# XGBoost Python 示例
> 原文:<https://towardsdatascience.com/xgboost-python-example-42777d01001e?source=collection_archive---------4----------------------->

[杰拉尔特](https://pixabay.com/users/geralt-9301/)皮克斯贝
XGBoost 是 Extreme Gradient Boost 的缩写(我写过一篇文章提供了渐变提升的要点[这里](/machine-learning-part-18-boosting-algorithms-gradient-boosting-in-python-ef5ae6965be4))。与渐变增强不同,XGBoost 利用正则化参数来帮助防止过度拟合。
假设我们想要构建一个模型来预测给定*方英尺的房子的价格。

我们从一个任意的初始预测开始。在回归的情况下,这可以是*均值,在分类的情况下,这可以是 0.5。

对于每个样本,我们用下面的公式计算残差。
> *残差=实际值—预测值*
假设,应用公式后,我们最终得到以下残差,从左到右开始采样。

接下来,我们使用线性扫描来决定沿给定特征(*方英尺)的最佳分割。线性扫描意味着我们在第一对点(它们的*均值)之间选择一个阈值,然后在下一对点(它们的*均值)之间选择一个阈值,以此类推,直到我们探索了所有的可能性。
在我们的例子中,我们从选择阈值 500 开始。

对应的树是:

注意每片叶子中的值是怎样的残差。也就是自变量的预测值和实际值之差,而不是给定样本的房价。
为了比较分割,我们引入了*增益*的概念。增益是由分割带来的精度提高。增益计算如下。

在哪里

λ和γ都是超参数。Lambda 是一个正则化参数,可降低预测对单个观测值的敏感度,而 Gamma 是在树的叶节点上进行进一步划分所需的最小损失减少量。
比方说,我们任意地将λ和γ设置为如下。

我们可以继续计算初始分割的增益。

我们继续计算对应于剩余排列的增益。

然后,我们使用产生最大增益的阈值。在这种情况下,最佳阈值是***方英尺< 1000** 。因此,我们以下面的树结束。

我们对每片叶子重复这个过程。也就是说,我们选择一个阈值来

当增益为负时,这意味着分割不会产生比我们让树保持原样的情况下更好的结果。
我们仍然需要检查在分割树叶时使用的不同阈值不会提高模型的准确性。

增益为正。因此,我们仍然受益于进一步拆分树。这样做,我们得到了下面的树。

我们检验了将样本面积在 1,000 *方英尺和 1,600 *方英尺之间的进行拆分是否有益。

增益为负。因此,我们让这棵树保持原样。

我们仍然需要检查是否应该分割左边的叶子(*方英尺< 1000)。

同样,增益为负。因此,最终的决策树是:

当提供样本时,决策树必须返回单个标量值。因此,我们使用下面的公式,该公式考虑了单个叶节点中的多个残差。

第一个预测是初始预测和树的预测之和乘以学习率。

假设学习率为 0.5,该模型做出以下预测。

新的残差是:

然后,我们使用这些残差构建另一个决策树,并重复这个过程,直到我们达到最大估计数(默认为 100)。一旦我们完成了模型的训练,XGBoost 模型作为一个整体所做的预测就是初始预测和每个决策树所做的预测之和乘以学习率。

# Python 代码
与其他机器学习模型不同,XGBoost 不包含在 Scikit-Learn 包中。因此,
XGBoost 库有很多依赖项,这使得安装它成为一场噩梦。你很幸运,我经历了那个过程,所以你不必经历。到目前为止,安装 XGBoost 最简单的方法是安装 Anaconda(如果您还没有安装)并运行以下命令。
conda install -c conda-forge xgboostconda install -c anaconda py-xgboost
一旦安装了 XGBoost,我们就可以继续导入所需的库。
import pandas as pd
import xgboost as xgb
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
就像上面的例子一样,我们将使用 XGBoost 模型来预测房价。我们使用 Scikit-Learn API 将波士顿房价数据集加载到笔记本中。
boston = load_boston()
X = pd.DataFrame(boston.data, columns=boston.feature_names)
y = pd.Series(boston.target)
我们使用 head 函数来检查数据。
X.head()

以下是不同特性及其缩写的列表。
* 按城镇分列的人均犯罪率
* ZN 面积超过 25,000 *方英尺的住宅用地比例
* 每个城镇非零售商业英亩数的比例
* CHAS Charles River 虚拟变量(= 1,如果区域边界为河流;否则为 0)
* 氮氧化物浓度(百万分之一)
* RM 每个住宅的*均房间数
* 1940 年以前建造的自有住房的年龄比例
* DIS 加权到五个波士顿就业中心的距离
* 放射状公路可达性指数
* 每 10,000 美元征收全价值财产税
* 按城镇分列的师生比例
* B 1000(Bk — 0.63),其中 Bk 是按城镇划分的黑人比例
* LSTAT %较低的人口地位
* 以千美元为单位的 MEDV 自有住房中值
为了评估我们模型的性能,我们将数据分为训练集和测试集。
X_train, X_test, y_train, y_test = train_test_split(X, y)
接下来,我们初始化 XGBRegressor 类的一个实例。我们可以选择λ和γ的值,以及估计器的数量和最大树深度。
regressor = xgb.XGBRegressor(
n_estimators=100,
reg_lambda=1,
gamma=0,
max_depth=3
)
我们使我们的模型适合训练集。
regressor.fit(X_train, y_train)
在决定房价时,我们可以检查每个特征的相对重要性。
pd.DataFrame(regressor.feature_importances_.reshape(1, -1), columns=boston.feature_names)

正如我们所看到的,低阶层人口的比例是房价的最大预测因素。
最后,我们用我们的模型预测了波士顿的一所房子的价格。
y_pred = regressor.predict(X_test)
我们使用均方差来评估模型的性能。均方误差是预测值和实际值的*方之差的*均值。
mean_squared_error(y_test, y_pred)

# XGBoost:理论与实践
> 原文:<https://towardsdatascience.com/xgboost-theory-and-practice-fb8912930ad6?source=collection_archive---------9----------------------->

Marc-Olivier Jodoin 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
## 了解最流行的算法之一是如何工作的,以及如何使用它
# 介绍
XGBoost 代表 e**X**treme**G**radient**Boost**ing,它是**梯度提升树**算法的开源实现。由于其预测能力和易用性,它已经成为 Kaggle 竞赛中最受欢迎的机器学习技术之一。它是一种**监督学习**算法,可用于**回归**或**分类**任务。
不管它的未来主义名称如何,只要我们先过几个概念,它其实并没有那么难理解:**决策树**和**梯度提升**。如果你已经熟悉了这些,可以直接跳到“**XGBoost 如何工作**”。
# 决策树
决策树可以说是你能找到的最容易解释的 ML 算法,如果与正确的技术结合使用,可能会非常强大。
决策树有这个名字是因为它的视觉形状,看起来像一棵树,有一个根和许多节点和叶子。想象一下,你有一份泰坦尼克号幸存者的名单,上面有一些信息,比如他们的年龄和性别,还有一个二元变量告诉你谁在灾难中幸存,谁没有。您现在想要创建一个分类模型,根据这些数据来预测谁将幸存下来。一个非常简单的例子是这样的:

作者图片
正如您所看到的,决策树只是一系列简单的决策规则,这些规则组合在一起,产生了对所需变量的预测。
# 梯度推进
**Boosting** 是一种**集成方法**,这意味着它是一种将几个模型的预测组合成一个模型的方法。它是通过依次获取每个预测值并基于其前任的误差对其建模(对表现更好的预测值给予更大的权重)来实现的:
1. 使用原始数据拟合第一模型
2. 使用第一模型的残差拟合第二模型
3. 使用模型 1 和模型 2 的总和创建第三个模型
**梯度增强**是一种特定类型的增强,之所以这样称呼是因为它使用**梯度下降算法**来最小化损失函数。
# XGBoost 如何工作
既然您已经理解了**决策树**和**梯度提升**,那么理解 **XGBoost** 就变得容易了:它是一种梯度提升算法,使用决策树作为其“弱”预测器。除此之外,它的实现是专门为优化**性能**和**速度**而设计的。
从历史上看,XGBoost 对于**结构化**表格数据表现得相当好。如果您正在处理**非结构化**数据,如图像,**神经网络**通常是更好的选择。
# 超参数
实现 XGBoost 时要选择哪些最重要的超参数,如何调优?
## 助推器
`booster`是 boosting 算法,有三种选择:`gbtree`、`gblinear`或`dart`。默认选项是`gbtree`,这是我在本文中解释的版本。`dart`是一个类似的版本,它使用 dropout 技术来避免过度拟合,而`gblinear`使用广义线性回归来代替决策树。
## 寄存器 _alpha 和寄存器 _lambda
`reg_alpha`和`reg_lambda`分别是 L1 和 L2 的正规化术语。这些数字越大,模型就越保守(不容易过度拟合,但可能会遗漏相关信息)。两者的推荐值都在 0-1000 之间。
## 最大深度
`max_depth`设置决策树的最大深度。这个数字越大,模型就越不保守。如果设置为 0,那么树的深度没有限制。
## 子样品
`subsample`是训练预测器时使用的样本比率的大小。默认值为 1,表示没有采样,我们使用全部数据。例如,如果设置为 0.7,则 70%的观测值将被随机采样以用于每次提升迭代(每次迭代取一个新样本)。这有助于防止过度拟合。
## 数量估计者
`num_estimators`设置助推轮数,等于设置要使用的助推树数。这个数字越大,过度拟合的风险就越大(但是低数字也会导致低性能)。
# 如何使用 XGBoost
为了展示 XGBoost 在实践中是如何工作的,让我们做一个简单的练习,使用 Python 实际预测 Kaggle 比赛中的泰坦尼克号幸存者。
从 Kaggle 下载我们的数据后,我们将导入所有必要的库和我们的训练数据:
IN: import pandas as pd
from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_scoredf = pd.read_csv("C:/Users/p005520/Downloads/titanic/train.csv")
请注意`from xgboost import XGBClassifier`。这仅仅是因为我们已经通过从终端运行`pip install xgboost`在我们的计算机上安装了 xgboost。`XGBClassifier`用在这里是因为这是一个分类问题。对于回归问题,用`XGBRegressor`代替。用于处理数据和计算性能指标的其他库。
IN:
dummies = pd.get_dummies(df['Sex'])
df = pd.concat([df, dummies], axis=1)X = df[[‘Age’,’female’,’male’]]
y = df[‘Survived’]seed = 42
test_size = 0.3
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=seed)
我们的数据中有许多变量,但这一次,我们将坚持上一个示例中的两个变量:“性别”和“年龄”。
这个程序块的前两行从“Sex”中创建虚拟变量。这需要将“性别”从*字符串*转换为*整数*,成为两个不同的变量:“男性”和“女性”,根据乘客的性别,这两个变量等于 1 或 0。
接下来的两行定义了我们的目标变量(“存活”)和我们将用于预测它的变量。最后 4 行用于分割我们的训练集和测试集:我们的训练集将用于创建我们的 XGBoost 模型,而测试集将用于测量它的性能。
IN: model = XGBClassifier(subsample = 0.7, max_depth = 4)
model.fit(X_train, y_train)
print(model)y_pred = model.predict(X_test)accuracy = accuracy_score(y_test, y_pred)
print(“Accuracy: %.2f%%” % (accuracy * 100.0))OUT: XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1,
colsample_bynode=1, colsample_bytree=1, gamma=0,
learning_rate=0.1, max_delta_step=0, max_depth=4,
min_child_weight=1, missing=None, n_estimators=100, n_jobs=1, nthread=None, objective='binary:logistic', random_state=0,
reg_alpha=0, reg_lambda=1, scale_pos_weight=1, seed=None,silent=None, subsample=1, verbosity=1)Accuracy: 80.60%
这里是我们实际训练 XGBoost 的地方,在前两行。注意`subsample = 0.7`和`max_depth = 4`,这里我手动定义了一些超参数。如果您想设置不同于默认选项的新超参数,只需将它们添加到列表中。我们的模型在测试集中产生了 80.6%的准确率,现在让我们看看如何将它应用于新数据:
submission_input = pd.read_csv("C:/Users/p005520/Downloads/titanic/test.csv")dummies = pd.get_dummies(submission_input[‘Sex’])
submission_input = pd.concat([submission_input, dummies], axis=1)submission_X = submission_input[[‘Age’,’female’,’male’]]submission = model.predict(submission_X)
submission_df = pd.DataFrame({‘PassengerId’:submission_input[‘PassengerId’],
‘Survived’:submission})
submission_df.to_csv(‘submission.csv’, index = False)
在这个模块中,我们导入了 Kaggle 的提交文件,进行了与我们的训练集相同的虚拟处理,用`model.predict(submission_X)`应用了我们训练过的预测器,然后将其保存到 csv。它在 Kaggle 的排行榜上得分如何?

作者图片
正如你所看到的,我们离排行榜的顶端还很远,但我们仍然成功地预测了 76%的人的存活率,只使用了两个特征,没有特征工程和没有超参数调整,这将是我们工作的逻辑下一步。
我希望您现在理解了 XGBoost 是如何工作的,以及如何将它应用到真实数据中。尽管 XGBoost 具有固有的性能,但是**超参数调整**和**特征工程**可以使您的结果产生巨大的差异。
如果你想了解更多关于**特征工程**的知识来改进你的预测,你应该阅读这篇文章,它概述了你可以用来转换你的变量和创建新变量的主要技术:
[](/feature-engineering-3a380ad1aa36) [## 特征工程
### 了解数据科学工作流程中最重要的步骤
towardsdatascience.com](/feature-engineering-3a380ad1aa36)
> *随时联系我* [*LinkedIn*](https://www.linkedin.com/in/melloarthur/) *如果你想进一步讨论,这将是一种荣幸(老实说)。*
# 高维数据集上的 XGBoost 与 LightGBM
> 原文:<https://towardsdatascience.com/xgboost-vs-lightgbm-on-a-high-dimensional-dataset-bbd5b5174274?source=collection_archive---------25----------------------->
## 速度和性能的比较

乔希·卡拉布雷斯在 [Unsplash](https://unsplash.com/s/photos/speed?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上的照片
我最*完成了一个多类分类问题,作为一个数据科学家职位的家庭作业。这是比较梯度推进决策树的两种最新实现 XGBoost 和 LightGBM 的好机会。
这两种算法都非常强大,在性能最好的机器学习模型中非常突出。
该数据集包含超过 6 万个观测值和 103 个数值特征。目标变量包含 9 个不同的类。

(图片由作者提供)
由于这篇文章的重点是 XGBoost 和 LightGBM 的比较,所以我将跳过探索性的数据分析和数据争论部分。我已经设法消除了 11 个特征和大约 2000 个异常值。
值得注意的是,数据争论过程中应用的所有技术都是在训练集和测试集分离后完成的。否则,我们将不得不处理数据泄露,这是机器学习中的一个严重问题。
特征空间是高度稀疏的(即特征大部分由零组成)。下面是显示特征稀疏率的直方图。

(图片由作者提供)
# **LightGBM**
由微软研究人员创建的 LightGBM 是梯度推进决策树(GBDT)的一种实现。在 LightGBM 之前,GBDT before 的现有实现会随着实例或特性数量的增加而变慢。LightGBM 旨在解决这个效率问题,尤其是对于大型数据集。
LightGBM 使用两种技术来解决大型数据集的效率问题:
* 梯度单侧采样
* EFB(独家功能捆绑)
如果你想了解更多,我有一篇关于 LightGBM 如何使用这些技术的独立文章:
[](/understanding-the-lightgbm-772ca08aabfa) [## 了解光 GBM
### 是什么让它更快更高效
towardsdatascience.com](/understanding-the-lightgbm-772ca08aabfa)
让我们回到我们的实现。LightGBM 要求数据采用特定的格式,因此我们使用 Dataset 函数。
import lightgbm as lgblgb_train = lgb.Dataset(X_train, y_train)
lgb_test = lgb.Dataset(X_test, y_test)
超参数在 LightGBM 和 XGBoost 的性能中起着关键作用。您可能需要花费大量时间来调优超参数。最终,你会创造出你自己的方法或策略来加速调整的过程。
有很多超参数。有些在准确性和速度方面更重要。其中一些主要用于防止过度拟合。
超参数可以作为字典传递给模型。这些超参数给了我最好的参数。
params = {
'boosting_type': 'gbdt',
'objective': 'multiclass',
'metric': 'multi_logloss',
'num_class':9,
'max_depth':9,
'num_leaves': 100,
'min_data_in_leaf':300,
'learning_rate': 0.03,
'feature_fraction': 0.7,
'bagging_fraction':0.8,
'bagging_freq':10,
'lambda_l1': 1,
'verbose': 0
}
没有严格的规则来定义最佳超参数值。这更像是一个受过教育的试错过程。如果你知道一个特定的超参数是做什么的,调优过程将比尝试随机值更有效。
**超参数**
* Max_depth:单棵树的最大深度。
* Num_leaves:它控制一棵树的叶子数量。LightGBM 使用逐叶的树生长算法,因此 num_leaves 是控制树复杂度的主要参数。
* Min_data_in_leaf:它表示一片叶子上所需的最小样本数(即观察值),这对控制过拟合非常重要。
* Feature_fraction:在每个节点随机选择的要素的比率。
* Bagging_fraction 和 bagging_freq 也有助于避免过度拟合。
LightGBM 超参数的完整列表可以在[这里](https://lightgbm.readthedocs.io/en/latest/Parameters.html)找到。
我们现在可以训练模型了。下面的代码块使用定型集对模型进行定型,并在定型集和验证集上评估模型的性能。
%%timeitgbm = lgb.train(params, lgb_train, num_boost_round=700,
valid_sets=[lgb_train, lgb_test], early_stopping_rounds=10)

训练集和验证集的对数损失分别为 0.383 和 0.418。执行训练*均需要 2 分 26 秒。
您可以在这些模型上实现非常低的损耗,但这会导致过度拟合。更重要的是在训练和测试精度方面有一个*衡的模型。此外,任务的目标是实现 0.41 的对数损失。
这是一个很好的实践,摆弄超参数值,看看它们对精度和过度拟合的影响。
# **XGBoost**
[XGBoost](https://xgboost.readthedocs.io/en/latest/index.html) 是 GBDT 算法的一个实现。它的效率如此之高,以至于统治了 Kaggle 上的一些主要比赛。
对于 XGBoost,数据集也应该以特定的方式进行格式化。
dtrain = xgb.DMatrix(X_train, y_train)
dtest = xgb.DMatrix(X_test, y_test)
XGBoost 还有许多超参数,需要正确地进行调整,以便创建一个健壮而准确的模型。这里是我发现的超参数值,可以达到令人满意的结果,同时还可以最小化过度拟合。
import xgboost as xgbparams_xgb = {
'boosting_type': 'dart',
'objective':'multi:softmax',
'num_class':9,
'max_depth':7,
'min_child_weight':20,
'gamma':1,
'subsample':0.8,
'colsample_bytree':0.7,
'tree_method':'hist',
'eval_metric':'mlogloss',
'eta':0.04,
'alpha': 1,
'verbose': 2
}
**超参数**
* Max_depth:一棵树的最大深度。
* Min_child_weight:一个孩子所需的实例权重的最小总和。将它保持在较高的位置可以防止孩子过于具体,从而有助于避免过度适应。
* Gamma:在树的叶节点上进行进一步划分所需的最小损失减少。同样,游戏越大,模型越不可能过度拟合。
* 子样本:在生长树之前随机选择的行的比率。子样也可以用来避免过度拟合。
* Eta:学习率。保持较高的值会使模型快速学习,但同时也会增加过度拟合的机会。
* α:L1 正则化项。
XGBoost 的整个超参数列表可以在[这里](https://xgboost.readthedocs.io/en/latest/parameter.html)找到。
我们现在可以训练我们的模型了。我们传递参数、训练集和要运行的回合数。您可以增加回合数并获得更好的精度,但总是有过度拟合的风险。
%%timeitbst = xgb.train(
params_xgb, dtrain , 700,
evals=[(dtrain,'eval'),(dtest, 'eval')],
verbose_eval=True
)

训练集和验证集的对数损失分别为 0.369 和 0.415。执行训练*均需要 3 分 52 秒。
# **结论**

LightGBM

XGBoost
损失非常接*,因此我们可以得出结论,就准确性而言,这些模型在具有所选超参数值的数据集上表现大致相同。
在速度方面,LightGBM 比 XGBoost 快 40%。
感谢您的阅读。如果您有任何反馈,请告诉我。
# XLM:跨语言语言模型
> 原文:<https://towardsdatascience.com/xlm-cross-lingual-language-model-33c1fd1adf82?source=collection_archive---------28----------------------->
## 了解基于变压器的自监督架构

莱昂纳多·大久保俊郎在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
像伯特这样的模特([德夫林等人。艾尔。](https://arxiv.org/abs/1810.04805))或 GPT ( [拉德福德等人。艾尔。在语言理解方面达到了最先进的水*。然而,这些模型仅针对一种语言进行预训练。最*,已经做出努力来减轻单语表示并建立通用的跨语言模型,该模型能够将任何句子编码到共享的嵌入空间中。](https://s3-us-west-2.amazonaws.com/openai-assets/research-covers/language-unsupervised/language_understanding_paper.pdf)
在这篇文章中,我们将讨论由艾提出的论文。作者提出了两种跨语言语言建模的方法:
1. 无人监管,依赖单语数据
2. 受监督,依赖并行数据。
# 跨语言语言模型(XLM)
在本节中,我们将讨论训练 XLM 的建议方法。
## 共享子词词汇
该模型对所有语言使用相同的共享词汇表。这有助于为所有语言的标记建立一个公共的嵌入空间。因此,很明显,具有相同文字(字母表)或相似单词的语言更好地映射到这个公共嵌入空间。
为了对语料库进行标记,使用了字节对编码(BPE)。
## 因果语言建模(CLM)
这是常规的语言建模目标,其中我们最大化一个标记 ***x_t*** 出现在给定序列中第' ***t*** '个位置的概率,给定该序列中的所有标记 ***x_ < t*** (在第' ***t*** 个标记之前的所有标记)。即

通过 [XLNet 论文](https://arxiv.org/abs/1906.08237)进行因果语言建模
OpenAI 的 GPT 和 GPT-2 就是为此目的而训练的。如果你对这个目标的细节感兴趣,可以参考我写的关于 [GPT](https://medium.com/dataseries/openai-gpt-generative-pre-training-for-language-understanding-bbbdb42b7ff4) 和 [GPT-2](https://medium.com/swlh/openai-gpt-2-language-models-are-multitask-learners-1c6d42d406ae) 的文章。
## 掩蔽语言建模(MLM)

MLM 经 [XLM 纸](https://arxiv.org/abs/1901.07291)
这是一种去噪自动编码目标,也称为完形填空任务。这里,我们最大化给定屏蔽记号 ***x_t*** 出现在给定序列中第‘***t***位置的概率,给定该序列中的所有记号, ***x_hat*** 。即

通过 [XLNet 论文](https://arxiv.org/abs/1906.08237)进行屏蔽语言建模
伯特和罗伯塔接受过这方面的训练。如果你对这个目标的细节感兴趣,你可以参考我写的关于伯特和罗伯塔的文章。
请注意,伯特和 XLM 的方法之间的唯一区别是,伯特使用成对的句子,而 XLM 使用任意数量的句子流,一旦长度为 256 就截断。
## 翻译语言建模(TLM)

TLM 经 [XLM 纸](https://arxiv.org/abs/1901.07291)
CLM 和 MLM 的任务在单语语料库上工作良好,然而,它们没有利用可用的*行翻译数据。因此,作者提出了一个翻译语言建模目标,其中我们从翻译数据中提取一系列*行句子,并从源句子和目标句子中随机屏蔽标记**。例如,在上图中,我们有来自英语和法语句子的屏蔽词。**序列中的所有单词都有助于预测给定的屏蔽单词**,从而在记号之间建立跨语言映射。**
## XLM
> 在这项工作中,我们考虑了跨语言语言模型预处理与 CLM,MLM,或 MLM 结合 TLM 使用。
>
> — [XLM 纸业](https://arxiv.org/abs/1901.07291)
# XLM 预培训
在本节中,我们将讨论如何在下游任务中利用 XLM 预培训,例如:
1. 零镜头跨语言分类
2. 监督和非监督神经机器翻译
3. 低资源语言的语言模型
4. 无监督的跨语言单词嵌入
## 零镜头跨语言分类
就像在任何其他基于 Transformer 的单语模型中一样,XLM 也在 XNLI 数据集上进行了微调,以获得跨语言分类。
将分类图层添加到 XLM 的顶部,并在英语 NLI 训练数据集上对其进行训练。然后在 15 种 XNLI 语言上对该模型进行了测试。
由于**模型还没有被调整来对来自这些语言**的句子进行分类,所以它是一个零尝试的学习例子。
## 无人监管的 NMT
对于这项任务,作者建议**以跨语言语言建模为目标,预训练一个完整的编码器-解码器**架构。该模型在几个翻译基准上进行评估,包括 WMT 的 14 英-法,WMT 的 16 英-德和 WMT 的 16 英-罗。
## 监督 NMT
这里,**编码器和解码器加载了来自 XLM** 的预训练权重,然后在监督翻译数据集上进行微调。这实质上实现了多语言的语言翻译。
更多关于多语言的 NMT 的信息,请参考这篇博客。
## 低资源语言建模
这就是“具有相同脚本或相似单词的语言提供更好的映射”的原因。例如,维基百科上用尼泊尔语写的句子有 10 万个,用印地语写的句子大约多 6 倍。此外,这些语言有 80%的标记是相同的。
因此,跨语言语言模型将明显有益于尼泊尔语的语言模型,因为它是在相对更多的相似对应数据上训练的。
## 无监督的跨语言单词嵌入
最后,由于我们有一个共享的词汇表,XLM 模型的**查找表**(或嵌入矩阵)给了我们跨语言的单词嵌入。
# 结论
在本文中,我们讨论了跨语言语言模型不仅有利于在一般的下游任务中获得更好的结果,而且有利于通过对类似的高资源语言进行训练来提高低资源语言的模型质量,从而获得更多相关数据。
这里有一个链接指向最初的 XLM GitHub 库。
[这里有一个链接](https://huggingface.co/transformers/model_doc/xlm.html)链接到 huggingface 的 XLM 架构实现和预训练权重。
# 参考
[## 跨语言语言模型预训练
### 最*的研究证明了生成性预训练对英语自然语言理解的有效性
arxiv.org](https://arxiv.org/abs/1901.07291)
# XLNet:用于语言理解的自回归预训练
> 原文:<https://towardsdatascience.com/xlnet-autoregressive-pre-training-for-language-understanding-7ea4e0649710?source=collection_archive---------42----------------------->
## 了解基于变压器的自监督架构

蒂姆·莫斯霍尔德在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
像 BERT,OpenAI GPT 这样的艺术语言模型在最*的自然语言处理中已经成为明星。这些模型基于[转换器](https://arxiv.org/abs/1706.03762)架构,该架构将基于 RNN 和基于卷积的模型挤出了市场。
在本文中,我们将讨论 XLNET 模型,它是在最*的一篇论文中提出的: [XLNet:用于语言理解的广义自回归预训练](https://arxiv.org/abs/1906.08237)。该模型解决了 BERT 的某些缺点,并通过**在 20 项任务中超越 BERT**成功克服了这些缺点。
如果你有兴趣了解 BERT 或 Transformers 背后的概念,可以考虑阅读一下 [this (BERT)](https://medium.com/swlh/bert-pre-training-of-transformers-for-language-understanding-5214fba4a9af) 和 [this (Transformer)](/transformers-explained-65454c0f3fa7) 。
# 伯特怎么了?

由 [AllenNLP](https://demo.allennlp.org/masked-lm?text=The%20doctor%20ran%20to%20the%20emergency%20room%20to%20see%20%5BMASK%5D%20patient.) 进行的屏蔽 LM 演示
## 输入噪声
BERT 的一个主要问题本质上是它对屏蔽序列的预训练目标,即**去噪自动编码目标**。屏蔽序列非常有助于理解语言语料库中的趋势,然而,在微调时,序列不会被屏蔽。
> 然而,**BERT 在预训练期间使用的【MASK】等人工符号在微调时间**的真实数据中不存在,从而导致预训练-微调差异。
>
> [— XLNet 纸](https://arxiv.org/abs/1906.08237)
## 独立性假设
BERT 最大化联合条件概率 ***p(x_t | x_hat)*** ,其中 ***x_t*** 为掩码项 ***x_hat*** 为记号序列。它读作,在给定序列 ***x_hat*** 中所有记号的情况下,屏蔽记号 ***x_t*** 出现在第‘***t***位置的概率。
这给出了独立性**假设的直觉,即每个被屏蔽的记号被单独重建**。我们将在后面的部分中清除这一点。
# XLNET
与 BERT 相反, **XLNet 是一个自回归模型**。这实质上消除了对输入去噪的依赖。
然而,自回归模型大多因其单向性而受到批评。因此,为了克服这一点,XLNet 提出了一个新的**置换语言建模**目标来克服这种单向性。
## 置换语言建模
如前所述,XLNet 提出了一种从两个世界(即自动编码和自回归)中吸取精华的机制。它没有自动编码目标中的输入去噪,并消除了传统自回归目标的单向性。
为了实现这一点,在对联合概率***p(x _ t | x _(I<t))***,**进行因式分解时,XLNet 不像在传统的自回归模型中那样使用固定的向前或向后因式分解顺序**,而是最大化一个序列的对数似然性 w.r.t **因式分解顺序**的所有可能排列。
> 具体来说,对于长度为 **T** 的序列 **x** ,有 **T!**不同的订单执行一个有效的自回归因子分解。直观地说,如果**模型参数在所有分解订单**中共享,那么**模型将学习从两侧**的所有位置收集信息。
>
> [— XLNet 纸](https://arxiv.org/abs/1906.08237)

置换语言建模来自 [XLNet 论文](https://arxiv.org/abs/1906.08237)
为了更详细地说明这个目标,让我们举一个例子。考虑上图,一个序列 ***x*** 有 4 个令牌。为简单起见,我们只考虑***x3***的注意力计算。观察上面每个图下面的排列顺序。
* 而取顺序***3->2->4->1***, ***3*** 恰好是序列中的第一个令牌。因此,没有其他标记对其注意力计算有贡献。**因为它们在当前排列**中不先于 3。
* 按照***2->4->3->1***, ***3*** 的顺序,前面是*和 ***4*** ,因此它们有助于其注意力计算。*
* *同理,对于**1->4->2->3和**4->3->1->2-**,对应的**x _(I<t)贡献给**x _ t .T41 的注意力计算*******
*更正式地说:*
**
*目标函数来自 [XLNet 论文](https://arxiv.org/abs/1906.08237)*
> ***注意**在训练时,实际获得序列的排列是不正确的,因为在下游任务的微调或推理过程中,序列不能被排列。因此,**变压器中的注意屏蔽被适当地操纵以获得正确的排列**;这也是有意义的,因为所提出的架构讨论的是因子分解顺序上的置换,而不是序列顺序。*
## *目标感知表征的双流自我注意*
**
*[XLNet 论文](https://arxiv.org/abs/1906.08237)关于目标感知表征的双流自我关注*
*常规转换器参数化可能不适用于置换语言模型。为了理解这一点,让我们考虑使用 ***softmax*** 的分布的标准公式,其由下式给出:*
**
*具有标准变压器参数化的置换 LM*
*这里的***h _θ(x _(z _(<t))***,是变压器的隐藏状态为***x _(<t)。*** 这个术语,绝不依赖于它所预测的位置,即***【z _(<***。这意味着,无论预测的位置是什么,这种分布都是相同的;从而导致无法了解有用的趋势。*
*因此,为了克服这一点,XLNet 论文提出了一个**重新参数化,用于下一个令牌分发,使其成为目标感知的:***
**
*具有重新参数化表示的置换 LM*
*使用修改的表示 ***g_θ*** ,其另外将目标位置 ***z_t*** 作为输入。因此,使用了两个隐藏状态,而不是一个:*
**
*内容流关注*
* ***内容**表示,本质上与标准变压器隐藏状态相同。这种表示对**和**都进行了编码;上下文***x _(z _(<t))***以及原令牌 ***x_(z_t)。****
*数学上:*
**
*内容表示*
**
*查询流注意*
* ***查询**表示,即只能访问上下文信息***【z _(<t))***和目标位置*【z _ t】。**
**数学上:**
****
**查询表示**
> ****注意**最初**内容流( *h_i* )本质上是对应的嵌入向量** ( ***e_x_i*** ),而**查询流( *g_i* )最初是可训练向量( *w* )** 。使用上面的表达式在每一层上更新它们。**
## **部分预测**
**抛开置换 LM 的所有优点不谈,我们必须承认它很贵。由于置换,这是一个具有挑战性的优化问题。**
**因此,为了解决这个问题,在给定的序列 z 中,只有一个**子序列 *z_( > c)* 被选择用于预测**,其中 ***c*** 被称为切割点。我们只考虑 ***z_( > c)*** ,因为它在该序列中具有最长的上下文。**
**此外,使用另一个超参数 ***K*** ,使得***K ~ | z |/(| z | c)***。并且我们**只选择 *1/K* 个令牌用于预测**。对于未选择的令牌,不计算它们的查询表示,这样可以节省速度和内存。**
**我们将这个部分预测与 BERT 的部分预测进行比较。BERT 使用部分预测,因为屏蔽所有记号没有任何意义。XLNet 做部分预测是因为优化难度大。比如:来个序列:**【深,学,是,伟大】**。假设 BERT 和 XLNet 都选择**预测令牌【深度,学习】**。又假设 **XLNet 将样本因式分解为【是,伟大,深度,学习】**。在这种情况下,**
**伯特最大化:**
* **L(BERT) = log p(深度|很棒)+ log p(学习|很棒)**
**XLNet 最大化:**
* **L(XLNet) = log p(深度|很棒)+ log p( **深度** |学习很棒)**
**这清楚地解释了 **XLNet 如何捕捉更多的依赖性,即深度和学习之间的依赖性**。毫无疑问,伯特学会了大部分的依赖性;但是 XLNet 了解更多。另外,这是上一节中提到的 BERT 中的**独立性假设**的一个例子。**
## **从《变形金刚 XL》中汲取灵感**
**最后,提到 [Transformer XL 模型](https://arxiv.org/abs/1901.02860),XLNet 从这里借用了**关系编码**和**段递归机制**的概念,这使得 Transformer XL 能够在很长的序列上操作。**
> ****有趣的事实:Transformer XL 可以参与比 RNNs 长 80%和比 vanilla Transformer 长 450%的序列,并且在评估期间比 vanilla Transformers 快 1800 多倍。****
# **结论**
**我们已经介绍了另一个最新的模型 XLNet,并讨论了它背后的概念。**
**XLNet 的代码是作者开源的,你可以在这里找到它[。](https://github.com/zihangdai/xlnet)**
**你可以通过[拥抱面部变形金刚](https://huggingface.co/transformers/model_doc/xlnet.html)找到预先训练好的权重和一个易于使用的模型架构 API。**
****新:**我已经为 XLNet 写了一篇论文总结和评论。有兴趣的可以看看:[https://docs . Google . com/document/d/1 nepiw 67 oqw 1 hprikosub-n8hk 2 a 07-3 pnmtrvmqmkta/edit?usp =分享](https://docs.google.com/document/d/1nePIW67OqW1HPrIkoXUB-N8hK2A07-3pnmtRVMQMKTA/edit?usp=sharing)**
# **参考**
**[## XLNet:用于语言理解的广义自回归预训练
### 有了双向上下文建模的能力,基于去噪自动编码的预训练如 BERT 实现了更好的性能
arxiv.org](https://arxiv.org/abs/1906.08237)** **[## Transformer-XL:超越固定长度上下文的注意力语言模型
### 变形金刚有学习长期依赖性的潜力,但是受限于…
arxiv.org](https://arxiv.org/abs/1901.02860)** **[](https://medium.com/swlh/bert-pre-training-of-transformers-for-language-understanding-5214fba4a9af) [## 伯特:语言理解变形金刚的前期训练
### 了解基于变压器的自监督架构
medium.com](https://medium.com/swlh/bert-pre-training-of-transformers-for-language-understanding-5214fba4a9af) [](/transformers-explained-65454c0f3fa7) [## 变形金刚解释
### 对谷歌 Transformer 模型的详尽解释;从理论到实施
towardsdatascience.com](/transformers-explained-65454c0f3fa7)**
# Oracle 中的 XML 功能
> 原文:<https://towardsdatascience.com/xml-functionalities-in-oracle-4c4574942840?source=collection_archive---------24----------------------->
## *Oracle 数据库中 XML 的实现和支持概述*

由[沙哈达特拉赫曼](https://unsplash.com/@hishahadat?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
# XML:简介
总是有在不同来源之间交换数据的广泛需求,而不用担心接收者将如何使用它或它将如何显示。XML 为我们做了那件事。这是 W3C(万维网联盟)的一项倡议,允许信息以人类和机器容易理解的有意义的结构和规则进行编码。
XML 代表 e **X** 可扩展 **M** arkup **L** 语言。XML 不是 HTML 的替代品。
> HTML 的设计重点是如何“显示”数据,而 XML 的设计重点是如何“*存储*”和“*传输*”数据。
XML 本身不做任何事情。
让我们看一个样本 xml 文档:
**
上面的 XML 文档包含一条消息,其主体包含发送者和接收者信息。但是它本身并不做任何事情。它只是构造和储存信息。必须有人编写软件代码来接收、发送或显示它。
像 <从> 或者 <到> 这样的标签不是预定义的。它们是由这个 XML 文档的作者编写的。
从这个意义上说,XML 是“可扩展的”。这意味着它没有固定的元素集(不像 HTML)。
因此,简而言之,我们可以说 XML 是一种独立于软件和硬件的工具或方法来构造、存储和携带信息。
XML 结构

XML 文档有一个从“根”元素开始的树形结构。一个例子如下:
<?xml version=”1.0"?>
<Employees>
<Empl id=”1">
<FirstName>Bill</FirstName>
<LastName>
Watterson
</LastName>
<Dept>
Finance
</Dept>
</Empl>
</Employees>
<Employees>
<Empl id=”1">
<FirstName>
Bill
</FirstName>
<LastName>
Watterson
</LastName>
<Dept>
Finance
</Dept>
</Empl>
</Employees>
第一行是 XML 声明。它定义了 XML 版本。下一行声明了这个 XML 文档的“根”元素。因此,“员工”是这里的根元素。像“雇员”、“名字”、“姓氏”和“部门”这样的其他元素是子元素。在 EMPL 元素中,有一个值为“1”的字段 id。它被称为该元素的属性。属性提供了关于元素的附加信息。属性值总是用引号括起来(单引号或双引号)。语法正确的 XML 文档称为“格式良好的”XML。
一个文档类型定义(DTD) 定义了一个 XML 文档的合法构件。它定义了一个包含合法元素和属性列表的文档结构。DTD 文档示例如下:
**<?xml version=”1.0"?>
<!DOCTYPE message [
<!ELEMENT message (to,from,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>**
上面的 DTD 是这样解释的:
上面的 DTD 是这样解释的:
!DOCTYPE note 定义了这个文档的根元素是 message。
!元素 note 定义了 note 元素包含四个元素:“to、from、body”。
!元素 to 将 to 元素定义为“#PCDATA”类型。
!元素 from 将 from 元素定义为“#PCDATA”类型。
!元素 body 将 body 元素定义为“#PCDATA”类型。
description (#PCDATA)指定已解析的字符数据。解析的数据是 XML 元素的开始标记和结束标记之间的文本。解析的字符数据是没有子元素的文本。
XML 模式定义(XSD) 文档是基于 XML 的 DTD 的替代方案。
“有效的”XML 文档是“格式良好的”XML 文档,它也符合 DTD 或 XSD 的规则。
可扩展样式表语言(Extensible Stylesheet Language 的缩写)

乔尔·那仁在 Unsplash 上的照片
如前所述,HTML 标签是预定义的。在 HTML 中定义了一个表格,浏览器已经知道如何显示它。然而,在 XML 中
可以表示任何东西。XML 专注于结构化和存储数据。因此,我们需要一种机制来定义这些数据应该如何在浏览器、手机等中显示。XSL(可扩展风格语言)是完成这一任务的语言。它定义了解释 XML 文档元素的规则。
让我们以一个 XML 文档“CDCatalog.xml”为例,其定义如下:
**<?xml version=”1.0" encoding=”ISO-8859–1"?>
<?xml-stylesheet type=”text/xsl” href=”DisplayCD.xsl”?>
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
</catalog>**
在第二行中,它引用了一个 XSL 文档 DisplayCD.xsl。该文档可以定义如下:
**<?xml version=”1.0" encoding=”ISO-8859–1"?>
<xsl:stylesheet version=”1.0" xmlns:xsl=”**[**http://www.w3.org/1999/XSL/Transform**](http://www.w3.org/1999/XSL/Transform)**">
<xsl:template match=”/”>
<html>
<body>
<h2>Title</h2>
<xsl:for-each select=”catalog/cd”>
<xsl:value-of select=”title”/> <br/>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>**
让我们一行一行地理解 DisplayCD.xsl。
第一行,定义了使用的 XML 版本和编码。
下一行声明了要使用的 XSL 版本和名称空间。xmlns:XSL = " http://www . w3 . org/1999/XSL/Transform "指向官方的 W3C XSLT 名称空间。
元素定义了一个模板。属性将模板与 XML 源文档的根相关联。
元素内部的内容定义了一些要写入输出的 HTML。
元素的 xsl:value-of > 可以用来提取 XML 元素的值。上例中的选择属性包含一个 XPath 表达式。XPath 表达式的工作方式类似于导航文件系统;正斜杠(/)选择子目录。XSLXSL:for-each元素可用于选择指定节点集的每个 XML 元素。
当 CDCatalog.xml 在 web 浏览器中打开时,它将显示如下数据,如 DisplayCD.xsl 中所定义:

XMLType
Oracle 使用一种新的数据类型,XML 类型,来帮助处理数据库中的 XML 数据。若要以 XML 格式查看现有表的数据,可以编写如下查询:
**select XMLTYPE(cursor(select * from dept)) XML_Data from dual**
输出如下所示:
**XML_Data
==============**<?xml version=”1.0"?>
<ROWSET>
<ROW>
<DEPTID>10</DEPTID>
<DEPTNAME>ACCOUNTING</DEPTNAME>
<LOC>NEW YORK</LOC>
</ROW>
<ROW>
<DEPTID>20</DEPTID>
<DEPTNAME>RESEARCH</DEPTNAME>
<LOC>DALLAS</LOC>
</ROW>
<ROW>
<DEPTID>30</DEPTID>
<DEPTNAME>SALES</DEPTNAME>
<LOC>CHICAGO</LOC>
</ROW>
<ROW>
<DEPTID>40</DEPTID>
<DEPTNAME>OPERATIONS</DEPTNAME>
<LOC>BOSTON</LOC>
</ROW>
</ROWSET>
XMLType 可以接受引用游标作为参数
要将 XML 文件的内容插入 Oracle 表,请执行以下步骤:
- 创建 xmltype 类型的表,例如创建 XMLTYPE 的表目录
- 创建一个包含要加载的 XML 文件的 Oracle 目录。(一个 Oracle 目录是指向数据库服务器机器上的操作系统 目录的数据库对象,用于读写文件。)例如:
**CREATE OR REPLACE DIRECTORY MYXMLDIR AS ‘D:\Gaurav\Trainings\Files’;**
3.执行 insert 语句,如下所示:
**INSERT INTO catalog VALUES(XMLType(bfilename(‘MYXMLDIR’,’CDCatalog.xml’),nls_charset_id(‘AL32UTF8’)));**
传递给nls_charset_id的值表示要读取的文件的编码是 UTF-8。
4.可以使用对象值选择该值,例如
**select OBJECT_VALUE from catalog**
XMLELEMENT
XMLELEMENT 是一个返回 XML 类型的函数。它接受两个参数:第一个参数是标记的名称,第二个参数是值,可以是字符串、XMLTYPE、数字或日期。
**select empno, ename from emp where ename like ‘F%’;**
这将返回姓名中包含字母“S”的雇员的编号和姓名,如下所示:

使用 XMLELEMENT,我们可以添加带有用户定义的标记名和值的 xmltype 作为雇员名,例如
select empno, xmlelement(name,ename) name
from
emp
where ename like ‘%S%’;
它生成一个 XMLType,以标记名和雇员名作为值:

XMLELEMENT 可以嵌套,并且可以包含属性:
select empno, xmlelement(
emp,
xmlattributes (empno, deptno),
xmlelement(name, ename),
xmlelement(job,job)
emp
)
from
emp
where ename like '%S%'

用 SQL 函数查询 XML 数据
提取和提取值:
EXTRACT 函数接受两个参数:一个 XMLTYPE 元素和一个 XPATH 字符串,并返回一个 XMLTYPE 实例,例如
为了选择 country =“USA”数据,将在“catalog”表上使用以下查询。
select extract(OBJECT_VALUE,’/catalog/cd[country =”USA”]’) cd_usa
from
catalog

EXTRACTVALUE 用于提取一个节点下的特定值。例如,如果我们必须在 title=“仍然有忧郁”的地方找到艺术家的名字,查询将被写成如下:
select extractvalue(OBJECT_VALUE,’/catalog/cd[title =”Still got the blues”]//artist/text()’) artist_name
from
catalog
输出:

存在节点:
EXISTSNODE 检查 XPATH 表达式的存在性,例如,如果我们想知道标题“仍然有忧郁”是否存在于目录中,我们可以编写如下查询:
select existsnode(OBJECT_VALUE,’/catalog/cd[title =”Still got the blues”]’) exist_flg
from
catalog

输出 1 表示它存在,而 0 表示它不存在。
XMLAGG:
XMLAGG 用于在单个 XML 文档中聚合多行。例如,要汇总每个部门的员工,查询可以写成:
select
xmlelement(
emp,
xmlagg(
xmlelement(dept,
xmlagg(
xmlelement(name,ename) order by ename
)
)
)
)
from emp
group by empno
更新 XML:
UPDATEXML 搜索 XPATH 表达式并更新它。例如
UPDATE catalog SET object_value =
UPDATEXML(object_value,
‘/catalog/cd/title/text()’,’changed’)
这将把表格目录中的所有标题更新为“已更改”。
结论:
本文旨在让读者对 XML 和相关技术有一个基本的了解,并了解 Oracle 数据库是如何处理 XML 数据的。有关 Oracle XML 功能的更多详细信息,请参考以下链接:
http://docs . Oracle . com/CD/e 11882 _ 01/app dev . 112/e 23094/TOC . htm
XML 抓取做得很好!
使用 Python 一步步抓取任何“XML”文件

弗兰基·查马基在 Unsplash 上的照片
ata 是新的石油——但它绝对不便宜。我们有来自四面八方的数据;web、应用程序、社交媒体等,数据科学家必须能够挖掘其中的一些内容。在接下来的博客中,我们将学习如何使用 Python 库' BeautifulSoup '快速从网站中挖掘/收集数据(为了好玩)
行动(或活动、袭击)计划
目录
- 介绍使用案例
- 什么是 BeautifulSoup?
- BS4 行动——理解并提取数据
- 最后评论
介绍使用案例
任何在客户体验或酒店行业工作过的人都明白客户满意度的重要性。NPS 或净推介值被视为客户体验的基准。虽然 NPS 是一项专门设计的调查,但还有其他方法来了解客户情绪。其中之一是 Appstore 上的客户反馈和评级(当然,前提是你的应用在那里可用)。
所以我们要做的是—
→随机选择一个应用程序(例如:脸书)
→访问 iTune 评论
→提取不同用户给出的评级、评论、日期等
→以干净的“csv/xlsx”格式导出它们。
什么是 BeautifulSoup?
美汤(又名 BS4) 是一个解析 HTML 和 XML 文档的 Python 包。它为解析过的页面创建了一个解析树,可以用来从 HTML 中提取数据,这对 web 抓取很有用。它适用于 Python 2.7 和 Python 3
BS4 行动——理解并提取数据
iTunes 使得从苹果应用商店获得应用评论变得非常容易。脸书的应用程序 id 是28488215,我们只需要在下面的 URL 中添加相同的 id
[https://itunes.apple.com/us/rss/customerreviews/page=1/id=284882215/sortBy=mostrecent/xml](https://itunes.apple.com/us/rss/customerreviews/page=1/id=284882215/sortBy=mostrecent/xml)
单击上面的 URL,将会出现一个如下所示的页面:

XML 文档被形成为元素树。一个 XML 树从一个根元素开始,从根分支到子元素。术语父、子和兄弟用于描述元素之间的关系。
在上图中我们可以看到<feed> 是的父也就是多子。其中一个孩子是<entry> ,它又有许多孩子。在某种程度上——<feed>是爷爷,<entry>是儿子,其他人是孙子。
我们需要提取什么?
现在,让我们假设我们想从每个评论中提取以下内容:
- 审查日期
- 审查标题
- 内容
- 评级
- 审查人姓名
我们想写 10 页。
Python 代码
代码非常简单
→首先使用request从 URL 导入数据
→使用BeautifulSoup将其转换为 BS4 对象
→Extra:可以使用soup.findall()查找数据中的所有文本
→找到子/标签entry并将其保存为对象。(这是第一次审核,包含所有与审核相关的信息)
→搜索其子节点updated title content im:rating name以提取所有需要的信息并保存在列表中
→通过使用find_next_sibling()找到子/标签entry的同级(这将引导您进入下一个审查)
→继续循环,直到找不到更多评论。继续将数据追加到数据帧中。
→ Extra:在上面的代码中,我添加了另一个循环来提取前 10 个评论页面中的所有上述信息。
仅此而已。很整洁,对吧!
结果
我们应该得到一个你看起来像这样的数据帧:

瞧 —您已经成功地从 iTunes 中提取了信息。现在,你可以在它们的基础上开发你的漂亮的 NLP 模型,并了解你的客户对你的看法。
要浏览我的其他数据科学/机器学习博客,请访问:
阅读希利什·古普塔在媒介上的作品。我是学术界的经济学家,专业的数据科学家和旅行者…
medium.com](https://medium.com/@shirishgupta)
暂时结束了。有什么想法来改善这一点或希望我尝试任何新的想法?请在评论中给出你的建议。再见。
Python 的 XPath
入门
学习网页抓取的规则

ML 路径语言(XPath)在 web 抓取和自动化领域是一个非常不受重视的工具。想象一下 RegEx,但是对于网页来说——那就是 XPath。
网页的每个元素都是由文档对象模型(DOM)组织的。DOM 是一个类似树的结构,其中每个元素代表一个节点,有到父节点和子节点的路径。
XPath 为我们提供了一种快速遍历这棵树的语言。而且,像 RegEx 一样,我们可以向节点选择添加逻辑,使我们的查询更强大。
在本文中,我们将涵盖:
**> XPath Essentials
** - Testing Our Queries
- The Root
- Paths in XPath**> Navigating the Tree**
- Node Indexing
- Extracting XPaths from the Browser**> XPath Logic****> Example with Python**
XPath 基础
测试我们的查询
首先,在我们做任何事情之前,我们需要理解如何测试我们的 XPath 字符串。幸运的是,我们可以在网络浏览器中这样做。
在整篇文章中,我将使用 Chrome,但在所有现代浏览器中,这个过程都非常相似。

点击 Chrome >更多工具>开发者工具中的选项下拉菜单。
在我们的网页上,我们打开开发者工具——在 Windows 上点击 Fn+12 ,或者从浏览器选项菜单中打开(请参阅上面的 Chrome)。

我们可以使用开发人员工具中的搜索栏来检查 XPath 字符串是否有效。
接下来,我们单击 ctrl+F 在元素窗口中打开搜索栏。在这里,我们可以通过字符串、选择器或 XPath 进行搜索。
这个方法是快速测试 XPath 查询的最简单的方法。
如果我们的查询匹配某些内容,那么该元素将被突出显示为黄色。当我们的查询匹配多个元素时,我们可以使用搜索栏右侧的箭头在它们之间循环!
根
让我们从查询的最开始开始。以//div为例,//是什么意思?
每个 XPath 查询都从 XML 树的根——最顶层的元素开始。对于 HTML 文档,这是<html>标签。
现在,如果我们写html//div,我们就说“寻找任何属于div的html的后代节点”。

页面元素的树形表示。
结果是,我们的查询将同时找到**html**/**div**和**html**/body/div/div/article/div/**div**。这是因为在这两种情况下,我们看到的是html的后代div。
因为我们的 XPath 查询总是从根(html)开始,所以我们不需要编写html//div。相反,我们写//div。
导航树
路径表达式
我们刚刚使用的例子叫做路径表达式。有几个这样的例子,它们非常有用:
//—匹配任何后代节点/—仅匹配子节点(直接跟随另一个节点的节点):

为了匹配 span 元素,我们需要在使用单个 / 时包含所有子节点。
.—匹配当前活动节点(稍后将详细介绍)..—匹配当前活动节点的父节点@—选择当前节点的一个属性(例如href):

我们可以使用属性选择器 @ 按属性进行搜索。
有了这些路径表达式的组合,我们可以轻松地遍历 XML 树。
例如,回到我们的示例 HTML 部分——我们可以通过找到带有属性class="u-textScreenReader"的span标签来选择a标签,并沿着树向上遍历到它的父节点,如下所示:

..语法允许我们在 XML 树中向上移动。
节点顺序
我们 DOM 中的每个节点都有编号。如果我们有一个包含五个li条目的列表ul,我们可以通过从 1 到 5 的索引来访问每一个条目:
<ul>
<li>London</li>
<li>Miami</li>
<li>New Dehli</li>
</li>
如果我们查询//ul/li[1],我们将返回<li>London</li>——注意,值是而不是零索引的。XPath 索引从1开始。
从浏览器复制 XPath
Chrome(可能是大多数现代浏览器)中一个非常方便的特性是直接从元素窗口获取节点的 XPath。

右键点击一个元素,点击复制>复制 XPath 。
为此,我们右键单击需要 XPath 的元素,然后单击 Copy > Copy XPath 并粘贴该元素的 XPath。我们的span元素返回:
//*[[@id](http://twitter.com/id)="_obv.shell._surface_1600536527994"]/div/div[1]/div[2] /div[1]/div[1]/a/span[1]
或者,我们可以复制完整的 XPath,它提供了从根到元素的完整路径。对于我们的span元素,这看起来像:
/html/body/div[1]/div[2]/div/div[1]/div[2]/div[1]/div[1]/a/span[1]
高级逻辑
现在,你是否想把下面的方法称为“先进”是值得商榷的。
然而,我证明了这一点,因为通过单独使用前面的路径表达式,我们可以非常容易地遍历 DOM。在许多情况下,我们不再需要任何东西。但时不时地,这些“先进”的方法会非常有用。
功能
有许多 XPath 函数我们不会在这里讨论,但是一些最常见的是:
contains—在字符串B中搜索字符串A,其中contains(B, A):

使用 contains 函数的例子,我们也可以使用 contains(text(),‘Home’)并得到相同的结果。图片作者。
not—我们用它来否定部分查询,比如我们想要所有不包含类svgIcon-use的span元素:

我们使用而不是来选择没有类‘u-textScreenReader’的 span 节点。
boolean—与not相等且相反,如果我们希望所有的span元素都包含一个svg子节点:

我们使用布尔来选择带有类‘u-textScreenReader’的跨度节点。
starts-with—类似于contains,但不包含字符串A,字符串B必须以字符串A开头。- 我相信你能解决这个问题
这些函数只是 XPath 表面的一小部分。一些我们还没有涉及到的项目(我已经添加了关于它们的文章的链接):
硒中的 XPath
Selenium 是熟悉 Python 中 XPath 的最佳方式(它也适用于许多其他语言)。如果你不熟悉它,我写了这篇文章介绍框架的设置和基础——它非常容易使用!
一旦有了 Selenium 设置,我们就可以使用find_elements_by_xpath方法选择满足 XPath 查询的网页的所有元素。

我们将在 webscraper.io 上尝试一下。在那里,我们可以看到每一项都包含在一个带有class="thumbnail"的div元素中。这里,我们重点介绍了 Acer Aspire 产品。
所以,回到 Selenium 和 XPath。首先,我们需要初始化我们的 web 驱动程序,并导航到 Web Scraper 培训网站:
from selenium import webdriver
driver = webdriver.Chrome('chromedriver.exe')
driver.get('[https://webscraper.io/test-sites/e-commerce/scroll](https://webscraper.io/test-sites/e-commerce/scroll)')
现在,我们需要选择类为thumbnail的所有div元素:

我们可以很容易地通过选择任何带有 //div 的 div 来选择这些,并且只过滤那些具有带有 [@class='thumbnail'] 的缩略图类的 div。
shop_elems = driver.find_elements_by_xpath(
"**//div[@class='thumbnail']"**
)
Selenium 网站元素
如果我们打印出shop_elems的值,我们将返回一个 WebElement 对象的列表:

Selenium 将返回我们选择作为 WebElement 对象的每个元素。
太好了,我们有了我们的商店容器 WebElements —现在做什么?嗯,我们可以将这些对象视为单独的 XML 树。
我们使用 WebElement get_element方法结合By.XPATH来实现这一点,这是一个新的导入:
from selenium.webdriver.common.by import Byshop_elems[0].get_element(By.XPATH, *<XPath Query Here>*)
这里,我们将div[@class="thumbnail"]的第一个实例设置为活动的,我们可以使用.在 XPath 查询中选择当前活动的节点。
我们可以使用这个新方法遍历页面上的每个条目容器,并迭代地提取每个条目的细节!
使用活动节点的 XPaths
让我们尝试在shop_elems中提取每个 web 元素的项目名称。

项目名称位于 a 子代 a 标签的标题或文本字段中。
回到浏览器,我们可以在 descendant <a>标签中找到项目名称。由于没有其他的后代<a>标签,我们可以只使用//a来选择这个元素。这给了我们//div[@class='thumbnail']//**a**。
在我们的代码中,shop_elems中包含的 WebElements 将div[@class='thumbnail']设置为活动节点,我们用.选择它:
shop_elems[0].find_element(By.XPATH, "**.//a**")
要获得商品名称,我们只需访问对象的text值。我们可以将它集成到一个 for 循环中,从我们的 WebScraper.io 电子商务页面中提取每个商品名称:
仅此而已;我们用 XPath 和 Selenium 提取了电子商务首页上的商品名称。我们可以结合更多的 XPath 查询来提取更多的信息,比如价格、评级、评论数量等。
结束了
这就是对 XPath 和 Selenium 的介绍。我们涵盖了:
- DOM、节点和分支
- 测试 XPath 查询— Fn+12 , Ctrl+F
- 导航树
- 从浏览器中提取 XPaths
- 一些更高级的 XPath 逻辑
正如我以前说过的,这只是触及了 XPath 的表面,我肯定会推荐您从事自己的小型 web 抓取/自动化项目并学习更多!
我希望你喜欢这篇文章。如果您有任何想法或问题,请通过 Twitter 或在下面的评论中告诉我!如果你想要更多这样的内容,我也会在 YouTube 上发布。
感谢阅读!
*除非另有说明,所有图片均为作者所有。
Pytorch 中从头开始的 xResNet
从你的 ResNet 架构中挤出一点额外的东西。

丹尼尔·库切列夫在 Unsplash 上拍摄的照片
ResNet 架构由何等人于 2016 年提出,已被证明是计算机视觉领域最成功的神经网络架构之一。大约三年后,由亚马逊网络服务公司的佟鹤领导的团队建议对模型的结构进行一些调整,这对模型的准确性有不可忽视的影响。
在这个故事中,我们从零开始实现 ResNet 架构,考虑了“卷积神经网络图像分类锦囊”出版物中介绍的调整。根据杰瑞米·霍华德的建议,最终的模型被称为 xResNet,我们可以把它看作是 ResNet 的变种或者是架构的下一个版本。
属性
代码大部分取自 fast.ai 课程和 fast.ai 库。然而,我试图简化它,并以一种支持叙述的方式组织它。
学习率是为那些对 AI 和 MLOps 的世界感到好奇的人准备的时事通讯。你会在每周五收到我关于最新人工智能新闻和文章的更新和想法。在这里订阅!
ResNet 架构
为了更好地理解 xResNet 中引入的调整背后的原因,我们简要讨论一下原始的 ResNet 架构。该模型的总体视图如下图所示。

原始 ResNet 架构
首先,我们有输入杆。该模块由一个7x7卷积层组成,具有 64 个输出通道,步长为2。接下来是3x3最大池层,步长也是2。我们知道卷积后图像的输出大小由下面的公式给出。

在这个公式中,o是图像的输出大小(o x o),n是输入大小(n x n),p是应用的填充,f是滤波器或内核大小,s是步幅。因此,输入词干将图像的宽度和高度减少了4倍,2来自卷积,2来自最大池。它还将其通道大小增加到 64。
随后,从阶段 2 开始,每个模块从一个下采样块开始,随后是两个剩余块。下采样块被分成两条路径:A 和 b。路径 A 具有三个卷积;两个1x1和中间的一个3x3。第一个卷积的步幅为2,将图像大小减半,最后一个卷积的输出通道是前两个卷积的四倍。路径 B 的作用是使输入图像的形状与路径 A 的输出相匹配,这样我们就可以将两个结果相加。因此,它只有一个步幅为2的1x1卷积和与路径 a 的最后一个卷积相同数量的通道
残差块类似于下采样块,但不是抛出一个步幅2卷积,而是在每个阶段的第一层,始终保持步幅等于1。改变每个阶段中的剩余块的数量,可以得到不同的 ResNet 模型,例如 ResNet-50 或 ResNet-152。
xResNet 调整
在 ResNet 架构中有三种不同的调整来获得 xResNet 模型; ResNet-B 、 ResNet-C 和 ResNet-D 。
ResNet-B 首先出现在 ResNet 的 Torch 实现中,它改变了下采样块的路径 A。它只是将步幅2移动到第二个卷积,并保持第一层的步幅1。很容易看出,如果我们在第一个卷积中有步幅 **2** ,也就是一个 **1x1** 卷积,我们会丢失四分之三的输入特征图。将其移动到第二层可以缓解这个问题,并且不会改变路径 a 的输出形状。
在 Inception-v2 中提出的 ResNet-C 去除了网络输入干中的7x7卷积,并用三个连续的3x3卷积来代替。第一个的跨距为 2,最后一个有一个64通道输出,后跟一个跨距为2的3x3最大池层。最终的形状是相同的,但是 **3x3** 卷积现在比 **7x7** 卷积要有效得多,因为 **7x7** 卷积比 **3x3**卷积贵 5.4 倍。
ResNet-D 是新建议,是 ResNet-B 的逻辑结果。在下采样块的路径 B 中,我们还有步幅 **2** 的 **1x1** 卷积。我们仍然把四分之三的有用信息扔出窗外。因此,作者用步幅2的2x2*均汇集层和其后的1x1卷积层替换了该卷积。下图总结了这三个调整。

xResNet 调整架构
履行
在本文的最后一部分,我们用 Pytorch 实现了 xResNet 架构。首先,让我们导入 torch 库并定义conv辅助函数,它返回一个 2D 卷积层。
现在,为了完成卷积块,我们应该添加初始化方法、批量标准化和激活函数——如果需要的话。我们使用上面定义的conv函数来创建一个完整的块。
我们看到,我们希望将批处理规范化层的权重初始化为1或0。这是我们稍后将回头讨论的内容。接下来,我们定义 xResNet 块。
在 xResNet 块中,我们有两条路径。我们称路径 A 为卷积路径,路径 B 为身份路径。卷积路径分为两种不同的情况;对于 xResNet-34 和更低版本,我们只得到两个3x3卷积层,而不是每个阶段有三个卷积。
此外,在任何 xResNet 架构中,我们都不会对每个块的最终卷积层使用激活函数,并将批量归一化权重初始化为0。完成第二个是为了允许我们的网络容易地学习有效地消除整个块的身份函数。这样,我们就可以设计更深层次的网络架构,在这种架构中,激活可以在模型中更深入地进行,而不用担心爆炸或消失的梯度。
在路径 B(身份路径)中,如果有下采样模块,我们使用步长的*均池2和1x1卷积,否则我们只让信号流过。最后,对于激活函数,我们使用默认的 ReLU 激活。
将所有这些放在一起,我们创建了 xResNet 体系结构,用 stem 输入和一些辅助方法来初始化模型。
我们现在准备定义模型的不同变体,xResNet-18、34、50、101 和 152。
结论
在这个故事中,我们简要介绍了 ResNet 架构,这是计算机视觉中最有影响力的模型之一。然后,我们进一步解释了一些技巧,这些技巧通过提高其准确性使架构更加强大。最后,我们使用 PyTorch 用代码实现了调整后的 xResNet 架构。
在后面的章节中,我们将会看到如何使用这个模型来解决一个有和没有迁移学习的相关问题。
我叫 Dimitris Poulopoulos,是希腊比雷埃夫斯大学BigDataStack的机器学习研究员和博士(c)。我曾为欧洲委员会、欧盟统计局、国际货币基金组织、欧洲中央银行、经合组织和宜家等主要客户设计和实施人工智能和软件解决方案。如果你有兴趣阅读更多关于机器学习、深度学习和数据科学的帖子,请在 twitter 上关注我的* 中 、LinkedIn或@ james2pl**。***
XRL:可解释的强化学习
有前途的 XRL 方法的详细概述

可解释人工智能的挑战。资料来源: Gunning,D. (2017)。可解释的人工智能(xai)。国防高级研究计划局(DARPA),nd Web , 2 ,2。
随着人工智能技术的大规模部署以及自主执行,在人工智能中灌输可解释的属性变得势在必行;这将导致用户信任人工智能技术。如果用户能够信任该技术,他/她就会放心地使用该技术,并且为了使该技术可信,它需要是透明的。如果一个模型能够提供关于其预测和决策的合理性,那么人工智能中的这种透明性就可以实现。可解释性在强化学习领域更为重要,在这一领域中,代理可以在没有任何人工干预的情况下自行学习。
这篇文章的目的是让读者了解不同的研究小组目前所追求的 XRL 技术。关于 XRL,需要考虑的一件重要事情是,该领域的大部分工作都应该牢记等式中人的一面。因此,为了促进 XRL(和 XAI)的发展,应该采取跨学科的方法来关注没有特定领域专业知识并且正在利用人工智能技术的人类用户的需求。关于这篇文章,假设读者对强化学习理论有中等水*的了解,并对可解释的人工智能有基本的理解。
接下来,让我们首先对 XRL 技术进行分类。与 XAI 方法类似,根据从 XRL 技术中提取信息的范围和时间,XRL 技术可以分为以下几类。

图一。XRL 技术的分类
以下是由研究团队开发的一些潜在的 XRL 方法,显示了有希望的线索。
多任务强化学习中的(局部的、内在的)分层的和可解释的技能获取
多任务强化学习中的分层和可解释技能获取在【1】中描述了通过两层分层结构在策略设计中引入模块化来扩展多任务强化学习。该框架从根本上基于这样一个事实,即一个复杂的任务需要不同的技能,并且由几个较简单的子任务组成。[1]训练他们的模型在《我的世界》执行对象操作任务,即在《我的世界》中找到、获得、放置或堆叠某种类型的黑色。该模型利用“优势行动者-批评家” (A2C)使用非策略学习进行策略优化。该模型具有分层结构,因为每个顶层策略都可以分解为底层操作。在《我的世界》的例子中,堆叠鹅卵石块的任务代表一个复杂的任务,然后代理将其分解为寻找鹅卵石块、获得鹅卵石块和放置鹅卵石块的动作。框架的可解释性来自于每个任务(例如堆叠鹅卵石块)都由人类指令描述,并且经过训练的代理只能通过这些人类描述来访问学到的技能,使得代理的策略和决策是人类可解释的。

图二。给定任务的多级分层策略示例—堆叠两个蓝色块。每个箭头代表某个策略生成的一个步骤,箭头的颜色表示源策略。请注意,在每一步,策略要么为较低级别的策略发出指令,要么直接采取行动。来源:【1】中的图 1
此外,该框架集成了随机时态语法 (STG) 方法来对任务的时态关系和优先级进行建模(例如,在将一个鹅卵石块堆叠在另一个鹅卵石块之上之前;你必须先找到一块鹅卵石,把捡起来,然后找到另一块鹅卵石,把手上的那块放上去。因此,该框架的关键思想是将复杂的任务分解成更简单的子任务。此后,如果这些更简单的子任务可以使用代理已经学习的策略或已经获得的技能来解决,则没有学习发生;否则,需要学习新的技能来完成新的动作。这个框架可以用下图来表示。

图三。请注意,这里的指令Get white是由人类给出的,这使得该策略易于理解。 来源:图 2 中【1】
假设
设 G 为任务集,其中每个任务 g 代表人的指令(例如上图中的‘Get white’)。在[1]中, g = ⟨ 技能,指令 ⟩双字元组用于描述 Minecraft 中的对象操纵任务。由于它是一个多任务框架,不同任务的目标奖励不同。因此 R(s,g) 用于描述特定任务的奖励函数,其中s=状态。最初,代理人是受过码头政策训练的πₒ,负责码头基本任务。任务集随着人类指导者不断给出指令而连续增加,使得gₒgₗ⊂…⊂gₖ、 导致如图 2 所示的策略学习。还要注意,在阶段 k 和 h=k-1 ,我们将gₕ定义为针对*的基本任务集,并将πₕ定义为πₖ.的基本策略对于策略的学习和代理的增强,需要来自人类的以任务形式的弱监督。因此,在多任务强化学习中,需要来自人类的任务强化来获得分层的和可解释的技能。*****
分层策略(注意这里 h=k-1)
如在[1]中详述的,分层策略背后的主要思想是当前任务集【gₖ】可以被分割成几个子任务,这些子任务可以出现在基本任务集【gₕ】中,并且可以使用基本策略πₕ.来解决结果,不是映射当前状态和人工指令来执行如图 3(a)的扁*策略结构中所描述的动作,分层策略设计通过重用基本策略来执行被表征为当前阶段中的子任务的基本任务来利用。处于阶段 k、 πₖ的全局策略由四个子策略组成:“用于执行先前学习的任务的基本策略、管理全局策略和基本策略之间的通信的指令策略、允许全局策略直接执行动作的增强*面策略以及决定全局策略将主要依赖于基本策略还是增强*面策略的切换策略”— [1].指令策略负责映射当前状态 s 和g∈gₖ到g∈gₕ,因此其主要功能是向基地策略πₕ传达关于哪个基地任务 的信息如前所述 g 由两个有条件地相互独立的东西(技能和来自指令短语的项目)组成,因此**

当执行某个任务无法实现基本策略时,增广扁*化策略进入动作映射状态 s 和任务 g 到动作 a、以便在中学习新技能解决新任务。上述框架中的切换策略在选择何时实施基本策略和何时实施扩展扁*策略时扮演中介者的角色。**切换策略输出一个二进制变量 e ,这样当 e=0,全局策略 πₖ遵循基本策略,当 e=1, πₖ遵循增强*坦策略。**
因此,在每一步,模型首先从切换策略中采样二进制变量【eₜ】,并从指令策略中采样新指令【gˡ】,以便我们的模型可以从基本策略中采样动作。下图总结了每个阶段的流程。

每一步的分级策略步骤。来源:[1]****
随机时态语法
随机时态语法用于确定不同任务中的时态关系。例如,要堆叠一个对象,我们首先需要找到、拾取并放置该对象。STG 作为修改分层策略中提到的开关和指令策略的先验,即采样 eₜ 和 gˡ 。在[1]中,STG 在任务的每一步 k > 0 处由下式定义:1)转移概率,**

和 2)的分布

因此,将 STG 合并到分层策略中,我们得到如下改进的开关和指令策略:**

通过将 STG 纳入分层策略,改进了交换机和指令策略。来源:[1]****
所得到的框架展示了更高的学习效率,能够在新环境中很好地概括,并且本质上是可解释的,因为它需要弱的人类监督来给予代理指令以便学习新技能。
通过因果透镜的(局部的,事后的)可解释的强化学习
认知科学提出,为了理解或解释一种现象,人类建立因果模型来编码我们周围发生的事件的因果映射。在建立因果模型时,我们不断地问这个问题:为什么是或者为什么不是。继续这个逻辑,通过在【2】中详述的因果透镜可解释的强化学习试图建立一个结构因果模型,用于通过感兴趣的变量生成对无模型强化学习代理的行为的因果解释。为了产生解释,对这个结构因果模型进行反事实分析。通过因果透镜的可解释强化学习还调查用户通过解释获得的理解、用户对解释的满意度以及通过解释在用户中诱发的对无模型强化学习代理的信任。在通过因果透镜的可解释强化学习中,为基于 RL 代理的马尔可夫决策过程 (MDP)结合了动作影响模型,扩展了结构因果模型 (SCMs),增加了动作。首先,让我们了解一下 SCMs。
结构因果模型
SCMs 的结构因果模型在 Halpern 和 Pearl 2005 中介绍。SCMs 使用外生/外部和内生/内部随机变量描绘世界;这些变量中的一些可能具有因果关系,这些关系使用一组结构方程来表示。形式上,为了描绘 SCMs,我们必须首先定义一个签名 S ,它是一个元组 (U,V,R) ,其中是外生变量的集合,是内生变量的集合, R 表示指定范围的函数**
形式定义:**一个结构因果模型是一个元组 M=(S,F) 其中 F 表示结构方程组,一个对应一个 X∈ V 这样

根据【U∪V中的其他变量给出 X 的值。换句话说,给出了模型中其他变量的 X 的值。另外,上下文* 𝔲 被定义为每个外生变量 u∈ U 的唯一值的向量。一个情境是一个模型-语境对 (M,)。根据结构方程给模型的变量赋值导致实例化。事件 φ 的实际原因由内生变量及其值表示。如果内生变量不同,则事件 φ 不会发生,因此在事件 φ的实际原因中嵌入了一些反事实背景。***
行动影响模型
行动影响模型背后的主要意图是便于从行动如何影响环境来解释代理人的行为。因此,我们将 SCMs 的想法扩展到行动影响模型,将行动纳入因果关系。**
形式定义:**一个动作影响模型是一个元组 (Sₐ,F) 其中 Sₐ 是用一组动作扩展 SCM 签名的签名即动作影响模型中的 Sₐ 然而在这里 F 对于每个 X∈ V 有多个值,这取决于𝕒应用的唯一动作集。 于是, F_{X,}刻画了作用【𝕒】时对 x 的因果作用。一组奖励变量xᵣ⊆v被分配给 sink 节点。******

图 4。一个星际争霸 2 特工的行动影响图。资料来源:图 1[2]。注意,这里我们已经考虑了状态变量的有限集合以及动作的有限集合。取决于独特的输入动作(表示为边),对于每个状态变量(表示为节点)存在一组结构方程。例如,当应用动作 Aₘ 时,状态变量 Aₙ 受到状态变量 S 和 B 的影响,因此结构方程 F_{Aₙ,Aₘ} (S,B) 捕捉因果关系。
从行动影响模型中生成解释
解释包括解释和要解释的事件和解释和证明事件发生的原因。解释生成需要以下步骤:1)定义动作影响模型;2)在强化学习中学习结构方程;最后,3)为外植体生成外植体。通过因果透镜的可解释强化学习主要集中于对形式为‘为什么行动 A 的问题提供解释?’或为什么不动作?’。同样,它将强化学习背景下的解释分为两种类型:1) 完全解释;和 2) 最低限度的完整解释。现在让我们正式定义它们。**
’为什么?问题**
完整解释:**一个完整解释可以在实际实例化 M_{V←S} 作为一个元组【xᵣ=xᵣ、Xₕ=xₕ、xᵢ=xᵢ】下给出一个动作𝕒,其中是通过移动得到的奖励变量组成的向量 Xₕ 是由出现在动作𝕒的头节点的变量组成的向量; Xᵢ 是动作影响图中从头到尾节点的中间变量组成的向量;而 、xᵣ、 代表通过m _ { v←s }实际实例化给出的关联变量的值。****
因此,根据上述定义的完整解释提供了从行为𝕒到将与行为𝕒.相关联的任何未来报酬变量的完整因果链例如,动作 、Aₛ 的因果链在图 4 中显示为黑色。在这种情况下,完整的解释元组将是(【s = s】,【Aₙ=aₙ】,【Dᵤ=dᵤ,d _ { b } = d _ { b })。深度优先搜索算法可用于从动作的头节点到所有汇聚节点遍历动作影响图。
最小完整解释:**对于较大的图,中间节点的数量越多,可能会导致混乱。在这种情况下,最小完整解释可能会派上用场。最小完整解释定义为元组 (Xᵣ=xᵣ、Xₕ=xₕ、xₚ=xₚ)其中 Xᵣ=xᵣ 和 Xₕ=xₕ 类似于前面的定义,代表变量的向量,这些变量是的直接前身。选择直接前辈是因为他们描述了奖励的直接原因。请注意,在中要考虑的中间节点的数量最小完整解释取决于应用以及用户的知识。****
‘为什么不?’问题
通过比较事件发生的因果链和解释和(反事实行动),可以产生反事实解释。首先,让我们定义反事实实例化,在此情况下,我们分配最佳状态变量值,使得反事实动作将被选择。
反事实实例化:**关于反事实行动 𝕓的反事实实例化可以从模型 M_{Z←S_{z}} 中给出,其中 Z 利用结构方程实例化行动𝕓的所有前趋变量以及落入𝕓因果链的后继变量。**
极小完全对比解释:对于一个动作𝕒在实际实例化m _ { v←s }下给出极小完全解释 Y=y 对于一个动作𝕓在反事实实例化 M_{Z←S_{z}} 下给出极小完全解释,我们 Xᵣ=xᵣ) 其中 Xˡ 和 Yˡ 是满足条件(xˡ=xˡ)∩(yˡ=yˡ)≦∅的变量的最大集合 X 和 Y 。 此后我们对比 xˡ 和 yˡ ( 差异条件)。注意,这里的 Xᵣ 对应于𝕒.行动的奖励节点**
因此,最小完成对比解释用于提取行动𝕒的实际因果链和行动𝕓.的反事实因果链之间的差异
例子
检查图 4 所示的星际争霸 2 代理的动作影响图。利用这个图表,我们将尝试回答这个问题:
’为什么要建造补给站(Aₛ)? ' 和 ' 为什么不建兵营(A_{b})?’。
设m=【w = 12,S = 3,B = 2,Aₙ = 22,Dᵤ = 10,D_{b} = 7】为实际实例化,mˡ=【w = 12,S = 1,B = 2,Aₙ = 22,Dᵤ = 10,D_{b} = 7】为反事实实例化。实现差分条件,我们得到最小完全对比解释为
([S = 3],[S = 1],[Dᵤ = 10,D_{b} = 7])。通过比较[S=3]和[S=1],可以从 NLP 模板中得到一个解释:因为目标是增加被摧毁的单位(Dᵤ)和被摧毁的建筑(D_{b}),所以建造补给站(Aₛ)来增加补给数量是有意义的。注意,变量的值是通过学习结构因果方程在实例化过程中获得的。
学习结构因果模型
给定描述变量之间因果关系的有向无环图,在强化学习代理的训练期间,结构方程可以被学习为多元回归模型。通过将【eₜ=(sₜ,aₜ,rₜ,s_{t+1})** 保存在一个数据集【dₜ={e₁,…,eₜ} 中的每个时间步,可以实现经验回放。然后,利用回归学习器𝕃.更新结构方程 F_{X,A} 的子集注意,我们只更新与指定动作相关的变量的结构方程。例如,参考图 4,对于任何具有动作的经验帧,只有等式 F_{S,Aₛ}(W) 将被更新。注意,任何回归学习器都可以用作学习模型𝕃,例如 MLP 回归器。**

图五。学习结构方程的算法。来源:[2]****
如需查看更多主题,如该技术的计算和人类评估及其结果,请参考【2】。
(局部的,事后的)对可解释的强化学习代理的远端解释
[扩展到通过因果透镜的可解释强化学习
尽管使用动作影响模型生成的解释击败了标准的状态-动作模型 , 结构方程在计算任务预测准确性方面表现不佳。因此,用结构方程来模拟变量之间的因果关系,在[4] 中详细描述的对可解释的强化学习代理的的远端解释建议使用来自决策树的决策节点来生成利用因果链的解释。
生成因果解释的决策树
用于生成远端解释的模型由决策树中的一个决策节点组成,该决策树描绘了代理的完整策略,并与前面部分的动作影响模型中的因果链相链接。假设𝕋代表一个决策树模型。然后在训练强化学习代理的同时,我们通过将每个阶段的【eₜ=(sₜ,aₜ)存储成一个数据集【dₜ= {e₁,…,eₜ}来实现经验回放。此后,我们从 D 中统一抽取小批量样本,以 sₜ 作为输入,以 aₜ 作为输出,来训练𝕋。对其节点数量没有限制的决策树将导致混乱和压倒性的解释,因此在[4]中,通过将叶子的数量设置为等于代理域中可能的动作的数量来限制决策树𝕋的增长。在[4]中,评估表明,与没有约束的情况相比,约束决策树𝕋几乎不影响计算任务预测精度。为了存储状态为的决策树𝕋的决策节点,模型从根节点开始遍历决策树,直到到达叶节点,在此期间,模型存储其路径中的模式。例如,从图 6 中, 、Aₙ 和 B 是动作 、Aₛ 的决策节点。注意,决策节点代表代理状态空间的特征变量。**

图六。从与行动影响模型的因果链(右)相链接的决策树(左)中生成远端解释。资料来源:图 3。****
现在,我们将从决策树中为 定义最小完全解释‘为什么?’提问提问。**
极小完全解释:给定一组决策节点【xₘ=xₘ】对于来自决策树𝕋的动作𝕒,一个极小完全解释对于一个为什么的问题可以定义为一个元组【xᵣ=xᵣ,xₙ=xₙ】其中 Xᵣ 是奖励变量的集合;而 Xₙ 是条件为【xₙ=(xₘ=xₘ)∩(xₐ=xₐ】的极大变量的集合;这里的 Xₐ 代表了𝕒.行动因果链中的一组中间变量注意,这里的 xₘ、 和 xₐ 描绘的是实际实例化 M_{V←S} 下变量的值。
考虑星际争霸 2 的代理,这是对问题 “为什么是 Aₛ?'”的最简单完整的解释 可由元组(【aₙ=aₙ】,【Dᵤ=dᵤ,d _ { b } = d _ { b });如这里的和 D_{b} 代表奖励变量 Aₙ 是因果链 (S→Aₙ→[Dᵤ,d _ { b })的交点,如图 6(b)中的粗体字所示**
最小完全对比解释:**一个最小完全对比解释对于一个为什么不的问题可以由一个元组 (Xᵣ=xᵣ,X_{con}=x_{con}) 给出其中 Xᵣ 类似于前面的定义 X_{con} 是满足条件
的最大变量集这里 X_{b} 表示反事实行动𝔹因果链中的一组中间节点, X_{c} 表示反事实决策节点。注意,值和 x_{c} 是使用实际实例化 M_{V←S} 和反事实实例化m _ { z←s _ { z } }进行对比的。利用下面的算法生成反事实决策节点 X_{c} 。******

图 7。生成反事实的算法。来源:[4]
现在作为一个例子回顾星际争霸 2 的代理任务,考虑问题‘为什么不建造兵营 A _ { b }?’* 。对比解释生成如下。最初,从决策节点和 B 中提取决策边界值,即对于【aₙ】≤5,对于【b>2。此后,从最接*叶节点的决策节点开始,少量改变该值δ= 0.01,从而在代理的当前状态中生成新的特征值。这里变量 B 的特征值将变为 1.99。在这种新状态下,从决策树中预测反事实动作为 A_{b} ,并通过遍历该树来提取反事实决策节点。随后,我们得到 X_{con}=B ,因为 b 是反事实行动 A_{b} (B→Aₙ→[Dᵤ,d _ { b })和 X_{c} (本迭代中为 B )因果链中节点的交点。现在我们对比实际值(3 来自因果模型 M 和决策树𝕋的训练)和 B 的反事实值(1.99),并添加奖励变量,以使用 NLP 生成对比解释。*
学习机会链
某些动作激活其他动作的执行。在强化学习领域,我们将远端动作定义为最依赖于代理执行的当前动作。例如,在星际争霸 2 中,动作训练陆战队员不能在建造兵营 A_{b} 之前进行。现在要生成远端解释,首先需要预测远端动作。[4]利用多对一递归神经网络(RNN)作为预测模型𝕃,在给定先前状态和代理采取的动作的情况下*似远端动作。𝕃的输出是远端动作及其累积回报。参考[4]第 4.4 节关于𝕃.的架构、训练和训练数据的信息在训练了远端动作预测模型𝕃之后,我们现在可以为‘为什么?’定义最小完整远端解释**和‘为什么不?’提问。**
极小完全远端解释:给定一个极小完全对比解释,当前动作𝕒和一个远端动作预测模型𝕃,一个极小完全远端解释可以用一个元组 (Xᵣ=xᵣ,X_{con}=x_{con},aₐ) 来定义其中和 X_{con} 被定义为较早的和**这里的 A 表示智能体的动作集合,而 Aₒ 表示当前动作𝕒.的因果链中的动作集合****
例子
考虑问题‘为什么要训练陆战队员 Aₘ 而不是建造兵营a _ { b }?’。经过分析,我们得到 Aₙ 为反事实节点,实际值为 10,反事实值为 5。同样,使用(攻击)作为预测的远端动作,可以使用 NLP 生成远端解释。
因果解释(通过因果透镜使用可解释强化学习生成‘最小完全对比解释)
“因为做动作训练陆战 Aₘ 更可取,以拥有更多盟友单位 Aₙ 为目标是拥有更多被摧毁单位dᵤd _ { b }。”— [4]第 4.4 节
远端解释**(使用可解释强化学习代理的远端解释“为什么不”问题的最小完整远端解释生成)
“因为同盟军的单位数量【aₙ】小于最优数量 18,所以更可取的做法是把行动列车做陆战改为* 启用这次行动的目标是有更多被摧毁的单位和被摧毁的建筑【T100— [4]第 4.4 节*****
如需查看更多主题,如该技术的计算和人类评估及其结果,请参考【4】。**
参考
- 舒,t,熊,c,Socher,r .:多任务强化学习中的层次性和可解释技能习得(2017)
- 透过因果透镜解释强化学习。 arXiv 预印本 arXiv:1905.10958 (2019)。
- 原因和解释:结构模型方法。第一部分:原因,英国科学哲学杂志,第 56 卷,第 4 期,2005 年 12 月,第 843–887 页,【https://doi.org/10.1093/bjps/axi147】T4
- 对可解释的强化学习代理的远端解释。arXiv 预印本 arXiv:2001.10284 (2020)。



浙公网安备 33010602011771号