TowardsDataScience 博客中文翻译 2019(二百二十五)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

管理数据科学代码库中复杂性的更好习惯

原文:https://towardsdatascience.com/habits-for-managing-complexity-in-data-science-codebases-5a16314c3737?source=collection_archive---------33-----------------------

现实世界中的 DS

如何通过更好的编码习惯变得更加敏捷和高效

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo credit: https://www.pexels.com/@fox-58267

如果你尝试过机器学习或数据科学,你会知道代码会很快变得混乱。

Actual footage of me writing data science code

通常,训练 ML 模型的代码是在 Jupyter 笔记本中编写的,它充满了(I)副作用(例如打印语句、漂亮打印的数据帧、数据可视化)和(ii)没有任何抽象、模块化和自动化测试的粘合代码。

虽然这对于旨在教授人们机器学习过程的笔记本电脑来说可能没什么问题,但在实际项目中,这可能会导致难以维护的混乱。缺乏良好的编码习惯使得代码难以理解,因此修改代码变得痛苦且容易出错。这使得数据科学家和开发人员越来越难以发展他们的 ML 解决方案来适应业务需求。

复杂性是不可避免的,但它是可以划分的。在我们的家中,如果我们不积极组织和合理安排物品摆放的位置、原因和方式,就会造成混乱,原本简单的任务(如找钥匙)就会变得不必要的耗时和令人沮丧。这同样适用于我们的代码库。

每当我们以添加另一个移动部分的方式编写代码时,我们就增加了复杂性,并且在我们的头脑中增加了更多的东西。虽然我们不能——也不应该试图——逃避问题的本质复杂性,但我们经常通过糟糕的编码实践增加不必要的意外复杂性和不必要的认知负荷。

如果我们可以通过应用下面列出的原则来控制复杂性,我们的大脑就可以解放出来解决我们想要解决的实际问题。以此为背景,我们将与分享一些识别增加代码复杂性的坏习惯****以及帮助我们管理复杂性的习惯的技巧。

管理复杂性的五个习惯

“管理软件复杂性的最重要的技术之一是设计系统,使得开发人员在任何给定的时间只需要面对整体复杂性的一小部分。” ( 约翰·奥特)

1.保持代码整洁

不干净的代码使代码难以理解和修改,从而增加了复杂性。因此,更改代码以响应业务需求变得越来越困难,有时甚至是不可能的。

一个常见的坏习惯(或“代码味”)是在代码库中留下死代码。死代码是被执行的代码,但是它的结果从来没有在任何其他计算中使用过。死代码是开发人员在编码时必须记住的另一个不相关的东西。例如,比较以下两个代码示例:

See how much easier it is to read the second code sample?

干净的代码实践已经被广泛地用几种语言写成,包括 T2、Python 和 T3。我们将这些干净的代码原则用于机器学习环境,你可以在这个 干净代码-ml 报告 中找到它们。

2.使用函数来抽象复杂性

函数通过抽象出复杂的实现细节并用一个更简单的表示——它的名字——来代替它们,从而简化了我们的代码。

想象你在一家餐馆里。给你一份菜单。这份菜单不是告诉你菜名,而是详细说明每一道菜的烹饪方法。例如,这样的一道菜是:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

What dinner menus look like without abstraction

如果菜单隐藏了菜谱中的所有步骤(即实现细节),而是给我们一道菜的名字(即一个接口,一道菜的抽象),那对我们来说就容易多了。(回答:那是扁豆汤**。**

为了说明这一点,这里有一个代码样本,来自 Kaggle 的 Titanic competition 中的一个笔记本,在重构到一个函数之前和之后。

通过将复杂性抽象成函数,我们使代码可读、可测试和可重用。

当我们重构功能时,我们的整个笔记本都可以简化,变得更加优雅:

Life is happier when you read code that tells you what they do, and not how they do it

我们的精神开销现在大大减少了。我们不再被迫处理许多许多行的实现细节来理解整个流程。相反,抽象(即函数)抽象掉了复杂性,告诉我们它们做了什么 ,让我们不必花心思去弄清楚它们是如何做的。****

3.尽快从 Jupyter 的笔记本里偷出代码

在室内设计中,有一个概念(“平面定律”),即“家庭或办公室中的任何平面都容易积聚杂物。”Jupyter 笔记本是 ML 世界的平面。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

当然,Jupyter 笔记本对于快速原型制作非常有用。但是我们倾向于把很多东西放在这里——粘合代码、打印语句、美化的打印语句(df.describe()df.plot())、未使用的导入语句甚至堆栈跟踪(🙈).尽管我们的意图是好的,但是只要笔记本还在,脏乱就会越积越多。

笔记本很有用,因为它们能给我们快速的反馈,当我们得到新的数据集和新的问题时,这通常是我们想要的。然而,笔记本变得越长,就越难得到关于我们的改变是否奏效的反馈

例如,当我们更改一行代码时,确保一切正常的唯一方法是重启并重新运行整个笔记本。我们被迫承担整个代码库的复杂性,即使我们只想做其中的一小部分。

相比之下,如果我们将代码提取到函数和 Python 模块中,并且如果我们有单元测试,测试运行程序将在几秒钟内给我们反馈我们的更改,即使有数百个函数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The more code we have, the harder it is for Jupyter notebooks to give us fast feedback on whether everything is working as expected.

因此,我们的目标是尽早将代码从笔记本转移到 Python 模块和包中。这样,他们可以在单元测试和领域边界的安全范围内休息。这将有助于通过提供一个逻辑地组织代码和测试的结构来管理复杂性,并使我们更容易发展我们的 ML 解决方案。

****那么,我们如何将代码从 Jupyter 笔记本中移出呢?假设您已经在 Jupyter 笔记本中编写了代码,您可以遵循以下流程:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The refactoring cycle for Jupyter notebooks

这个过程中每一步的细节可以在clean-code-ml repo中找到。

4.应用测试驱动开发

到目前为止,我们已经讨论了在代码已经写在笔记本上之后编写测试。这个建议并不理想,但是它仍然比没有单元测试好得多。

有一种神话认为我们不能将测试驱动开发(TDD)应用于机器学习项目。对我们来说,这完全是不真实的。在任何机器学习项目中,大部分代码都与数据转换有关(例如,数据清理、特征工程),一小部分代码库是实际的机器学习。这种数据转换可以写成纯函数,为相同的输入返回相同的输出,这样,我们可以应用 TDD 并获得的好处。例如,TDD 可以帮助我们将大而复杂的数据转换分解成较小的问题,这样我们就可以一次解决一个问题。

至于测试代码的实际机器学习部分是否如我们预期的那样工作,我们可以编写功能测试来断言模型的度量(例如,准确度、精确度等)高于我们预期的阈值。换句话说,这些测试断言模型按照我们的期望运行(因此得名,功能测试)。这里有一个测试的例子:****

Example of a automated functional test for ML models

当我们写完这些单元测试和功能测试后,我们可以让它们在团队成员推送代码时在持续集成(CI)管道上运行。这将允许我们在错误被引入我们的代码库时就捕捉它们,而不是在几天或几周后。

5.小而频繁地提交

当我们不进行小而频繁的 git 提交时,我们增加了不必要的精神开销。当我们正在处理一个问题时,对早期问题的更改仍然显示为未提交。这在视觉和潜意识上分散了我们的注意力;这让我们更难专注于当前的问题。

例如,看看下面的第一个和第二个图像。你能找出我们在做哪个功能吗?哪个图像给了你更轻松的时间?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传**********外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

当我们频繁地进行小规模提交时,我们会获得以下好处:

  • 减少视觉干扰和认知负荷。
  • 如果工作代码已经提交,我们就不必担心会意外破坏它。
  • 除了红绿重构,我们还可以红绿红绿还原。如果我们无意中破坏了一些东西,我们可以很容易地退回到最近的提交,并再次尝试。这样我们就不用浪费时间去解决我们在试图解决本质问题时无意中制造的问题。

那么,多小的提交才算足够小呢?当有一组逻辑上相关的变更并通过测试时,尝试提交。一种技术是在我们的提交消息中寻找单词“and ”,例如“添加探索性数据分析,将句子拆分成标记,重构模型训练代码”。这三个更改中的每一个都可以分成三个逻辑提交。在这种情况下,您可以使用 git add -p 将代码分成小批提交。

结论

我们希望这篇文章对你有所帮助。这些习惯帮助我们管理机器学习和数据科学项目中的复杂性,并且帮助我们在项目中保持敏捷和高效。

如果您对此有其他想法和建议,请随时发表评论:-)

P.S .原贴 ThoughtWorks Insights:https://www . ThoughtWorks . com/Insights/blog/coding-habits-data-scientists

P.P.S .如果你对组织如何采用连续交付实践来帮助机器学习实践者和项目变得敏捷的例子感兴趣,你可以查看我们关于 机器学习连续交付(CD4ML) 的文章。

P.P.P.S .特别感谢 Aditi、Chandni、Danilo、Gareth 和 Jonathan 对本文的反馈!

黑客马拉松!和我一起准备🤓😎

原文:https://towardsdatascience.com/hackathon-get-ready-with-me-3192c7690cb7?source=collection_archive---------13-----------------------

尤其是对于一个有抱负的数据科学家来说

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

黑客马拉松是一项竞赛,包括程序员、开发人员和图形设计师在内的各种人在短时间内合作设计软件项目。黑客马拉松的目标是在活动结束时创造一个功能产品。

上个月,我有机会参加由 Junction Ltd .举办的 JunctionX Seoul Hackathon。我们的团队开发了一个 iOS 应用程序,通过图像搜索引擎为韩国美容产品带来了优化的购物服务体验。该应用程序可以帮助那些想探索韩国美容产品,但由于明显的语言障碍而不知道该尝试什么的旅行者。用户只需扫描产品就能找到有用的数据,这些信息会被翻译成当地语言。还有一个搜索历史页面,可用于进一步定制服务。你可以在这里找到更多关于的信息。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The app, Skana

然而,我不想谈论我做了什么,我想分享我在这个过程中学到的东西。所以今天,我要谈谈你应该申请参加黑客马拉松的 4 个原因(尤其是如果你是一名有抱负的数据科学家),你应该如何准备以及我的一些建议。我希望这篇文章能激励你像我一样走出自己的舒适区,获得一些深刻的启发。

1.为什么是黑客马拉松

如果你之前没有尝试过黑客马拉松或者编程比赛,说你问我是否值得一试,我会毫不犹豫的点头。以下是你应该参加黑客马拉松的 4 个理由。

超越“建模”

我们,数据科学学习者,倾向于单独工作或单独学习。我们倾向于将大部分时间投入到数据预处理和搜索中。而我们把大部分精力都倾注在研究机器学习或者深度学习算法上。我们的项目通常从导入数据开始(或者有时我们自己构建数据),以评估预测结束。

可悲的是,世界上几乎所有现实世界的项目都不是这样的。我们不是单独行动的。我们和其他团队成员一起工作。从数据库到部署和产品管理,所有的过程都应该通过协作来完成。拥有超越“建模”领域的工具包对有志之士来说是一大优势。(许多文章都指出了这一点,你也可以在这里找到一个)现在重要的问题是,我们在哪里可以学习和体验这一点?****

“Un 像比赛,没人给你两个。csv 文件称为培训和测试和一个很好的书面评估指标。几乎 80%的工作都花在了定义问题、获取和处理数据上。剩下的 20%的精力用于纯粹的建模和部署。”

黑客马拉松可以是一个与他人合作并进行真实项目的好机会。虽然有一些限制,但你仍然可以学习如何与他人交流(他们不像你一样有很多机器学习方面的知识),并了解发布实际产品的整体工作流程。如果你有机会和一个在机器学习方面比你懂得更多的人在一起,我相信这也是一个挑战你极限的好时机。

商业思维比你想象的更重要

这个项目的第一步将是决定建造什么。而且我可以说一半的胜算都是在这里决定的。你应该有个好的开始。实施也是一个关键点,但业务价值起着重要作用。如果它被忽视了,你的工作只能是炫耀你的技术能力。

"L ikewise,数据科学家是被雇佣来创造商业价值的,而不仅仅是建立模型。问问你自己:我的工作成果将如何影响公司的决策?我要怎么做才能让这种效果最大化?凭借这种创业精神,第三次浪潮数据科学家不仅能产生可操作的见解,还能寻求带来真正的变化。”

你会看到很多人已经指出了商业思维的重要性。发现用户隐藏的需求,观察市场中的一个问题。用你的技术知识提出解决方案。开发一项可以盈利的服务。在黑客马拉松上,你被要求带着这个观点进行深入的思考和讨论。

我听说过一个家伙赢得了超过 15 次的编程比赛。在赢得谷歌的一场比赛后,他退出了自己的“猎奖”生涯。你知道他的秘密是什么吗?这只是商业意识和一点点智慧。通过制造一个聪明而有创意的产品,他让人们大喊“太棒了!”这肯定会在面板上留下印象。因此,如果你正在寻找一个额外的获胜秘诀,拥有一些机智的商业价值观会是一个很好的奖励。

处理极端压力的绝佳机会

黑客马拉松的目标是在短时间内(通常是 2 到 3 天)创造一个功能产品。在这些日子里,你被要求展示你的产品的原型或演示。都是时间有限,资源有限,精力有限的问题。有时,你可能会遇到事情出错的情况,你必须立即找到解决方案。有时候你必须优先考虑你的工作,这意味着什么先来,什么时候停下来。你会感受到完成所有任务和不成为团队负担的巨大压力。和久而久之,变得完全饥饿和疲惫是不可避免的!(更像是能量耗尽或耗尽,直到最终“入睡”)。

如果你和我一样,在极度紧张的时刻会冻僵,你甚至会被比赛大厅的空气淹没。这是我第一次参加黑客马拉松,所以我经历了几次大脑瘫痪。我不得不呼吸一些新鲜空气,放松一下来缓解紧张。但这也成为我学习如何应对压力的一个好机会。因为在工作场所不可避免地会遇到一些意想不到的问题,我们必须处理好这些棘手的问题。

“你好,世界”的实时性

认识新朋友也是一大乐事,尤其是如果是志同道合的人。在黑客马拉松上,你可以遇到来自不同地区、有不同专业但有相同兴趣的各种人。他们可以是网页开发人员、应用程序开发人员或设计师,但他们是愿意在周末开发一些很酷的东西的人。

我遇到了从俄罗斯来韩国参加这个活动的人。我还遇到了一位在韩国学习计算机科学的乌克兰女士。有来自丹麦、法国、中国和日本的人。认识新朋友本身就是一大乐事。但更重要的是,我可以听到他们有什么样的痛点,他们真正热衷的是什么。通过与其他参与者聊天,我可以在编程方面开阔眼界。这就是为什么我认为你应该尝试一个主题丰富的黑客马拉松,而不是专注于数据科学领域。

此外,关心别人做什么也是件好事。尽管你会忙得没有时间去额外照顾别人,但这将帮助你理解我上面提到的整个过程。不管怎样,我们是来学习新东西的!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photos by JunctionXSeoul and Me giving a presentation (on the right bottom)

2.如何准备

那么,现在黑客马拉松听起来值得一试吗?如果是这样,那么你的下一个问题可能是你应该如何准备。实际上,没什么。只管去做吧。不管你有什么技能,不要犹豫去申请。黑客马拉松不仅仅是专业人士的舞台。但是如果你想在这一天之前准备一些东西,那么我想列出一些我认为有用和适用的技巧。

后端和部署

这不是一个以导入数据开始,以一些评估图结束的项目。您需要根据您的团队项目收集数据并构建数据库。在您完成数据分析或建模部分后,您必须将您的模型传递给前端。或者你可能需要自己在网上发布你的作品。因此,在你的工具包中有数据库和 Flask 对于这个活动来说是一个巨大的资产。

如果你对数据库完全陌生,你可以从理解SQL 和NoSQL 的区别开始学习。这里有一个针对初学者的简洁的 SQL 课程 但是除此之外还有很多关于数据库的教程。所以去根据自己的口味挑选吧。

Flask 是用 Python 编写的微框架,简单易学,对模型部署非常有用。这里有一个关于 如何用 Flask 部署 Keras 深度学习模型的优秀教程,作者是本·韦伯。你也可以在这里找到烧瓶 的 系列视频课程。

API 和云服务

每个黑客马拉松的条件可能不同,但我参加的那个高度依赖于 API 和云服务。有几个子轨道,参与者被要求使用特定的 API 来参与给定的轨道。但是除了这个条件之外,你还需要自己收集数据。你将从哪里获得数据?而用于训练的计算能力呢?你可能会把装有 GPU 的“超级计算机”留在家里。

因此,最好有从 API 获取数据并以 JSON 格式处理数据的经验。如果你是 API 新手, 这篇文章 可以向你解释 API 是怎么一回事。

能够使用像 AWS 或谷歌云 GPU 的云服务也是必要的。这里是 AWS EC2 的官方指南 但是这里是 AI 学院的 一个友好的视频 。你还可以找到更详细的初学者教程 作者迈克尔·加拉内克

充满能量的你

睡眠充足。控制你的状况。这不是开玩笑。比赛期间,我总共睡了 4 个小时。睡得少可能是一个好策略,但你需要在进入大厅时保持良好的状态。此外,一个颈枕和一条用来小睡的毯子可以和“烧瓶”或“AWS”一样有用。😂😂

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photos by JunctionXSeoul

3.我最后的小建议

最后,这里是我的一些小技巧。

如何组建你的团队

如果你是一个人申请,你必须从底层建立你的团队。可以有一个像 Slack 这样的社交网络平台,你可以从其他申请人那里获得信息,并在活动开始前组建一个团队。你可能有机会加入一个有空缺的预制团队。

如果你有一个关于你想开发什么的想法,把它分成几个部分,然后寻找能承担每个部分的成员。你需要找到一个与我们技能不同的人(比如数据建模)。你需要具备的成员通常是后端工程师、web/app 开发者和 UI/UX 设计师。

诚实面对你能做的事情

你可能认为你的能力不足以做这件事。所以你可能想隐藏你的水平或者假装比你知道的更多。但是你真的不需要。黑客马拉松对所有人开放。不管你对编程了解多少,总有一部分只能由你来填补!旁边的人比你懂得多吗?厉害!你有更多的机会学习。寻求帮助是“帮助”你的团队的更好方式,而不是独自奋斗,浪费宝贵的时间。

享受,享受,享受!

不仅仅是赢。虽然你应该在这段时间里尽你所能,但不要失去你脸上的笑容。享受学习的时间,结识新朋友,和他们一起工作。享受创造和开发世界上不存在的新事物的时间。享受压力和超越极限的时光。要求会非常高。然而,在活动结束时,即使你没有获奖,你也会带着新的想法和教训回家。你会发现自己像我一样期待着下一次黑客马拉松。😉

这个故事引起你的共鸣了吗?请与我们分享您的见解。我总是乐于交谈,所以请在下面留下评论,分享你的想法。我还在 LinkedIn 上分享有趣和有用的资源,所以请随时关注并联系我。下次我会带来另一个有趣的故事。一如既往,敬请期待!

黑客马拉松故事

原文:https://towardsdatascience.com/hackathon-story-fa2aed2cf4c2?source=collection_archive---------28-----------------------

我受邀在俄罗斯最大的数据科学和机器学习会议 DataFest 6 上讲述我在数据科学黑客马拉松中的经历。我决定讨论组织此类活动时常见的错误以及如何避免它们。我的演讲录音最近发表了(俄语演讲,有英语字幕)。我注意到了视频的如下评论(从俄语翻译过来):

这个家伙找到我们,建议我们合并解决方案,然后平分奖金。我们拒绝了,然后他赢了,我们没有_(ツ)_/

其实这背后有一个很酷的故事。

故事

这次黑客马拉松是在莫斯科举行的,之前由同一组织者在圣彼得堡成功举办了几次,我是远程参与者(当时我在芬兰学习)。像往常一样,这次它被允许从另一个城市参加,但我决定下线。

它开始于周五晚上,截止日期定在周日中午。“Moscow AI Hack”的总奖金池约为 6k 美元,以下每条赛道的冠军可获得 15k 美元奖金:

  • 预测俄罗斯各地区汽油价格(时序表格数据,Gazprom 的 Kaggle 竞争);
  • 用表格数据和汽车图片预测特定汽车对给定用户的“适用性”(莫斯科初创公司 True North 举办的 Kaggle 竞赛);
  • 检测 Gazprom 横幅中的错误和“异常”(计算机视觉任务;没有提供明确的指标,尽管数据集包含大量高分辨率照片);
  • 最佳商业解决方案(开放式课程,前提是必须使用数据集才有资格获奖)。

在浏览了所有数据集并仔细阅读了所有任务后,我决定专注于第二个——来自正北的轨迹。我在周五创建了一个基线解决方案,回家后花了一整天设计新功能,训练不同的模型,并在周六远程调整它们的超参数。一天下来,我完全筋疲力尽,没有主意了。不过,我在 26 支队伍中名列前三。

前三名的所有三支队伍都是单人参赛。我和第二个家伙只差几分,而第一名的参与者不知何故得到了比其他任何人都好得多的分数。鉴于“赢家通吃”的政策,我知道我没有机会独自赢得它,因为分数的差距,时间的缺乏,以及竞争的动态。策略很简单:我应该和排行榜首位的人合并;如果我们的解决方案足够多样化,这样的合并将提高我们的共同得分。

我问排在第二位的那个人,他是否愿意合作,整合我们的解决方案。他想了一会儿,然后回答说他第二天会给我答案。我去睡觉,第二天早上我看到 top1 和 top2 已经合并成一个团队。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我获胜的机会大大减少了。这有点令人沮丧,但我不想放弃,现在还不想。我不得不另找一支队伍。

我去黑客马拉松的地方找人合作。我向前十名中的几乎所有人提议合并我们的团队并分享奖金,但没有人同意。回想起来,我认为这可能有多种原因:部分原因是最后期限越来越近,他们失去了获胜的希望;部分原因是分成比例(无论团队大小,奖金的 50%归我,50%归他们团队)对他们来说似乎不公平(但实际上,这非常公平);部分原因是我看起来不像一个有经验的数据科学家,所以也许他们认为我只是一个普通人(黑客马拉松上有很多这样的人);也许我不够有说服力;也许他们在等待排行榜的大变动;也许原因在于“未知的未知”。不管怎样,这没关系。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

That’s what I looked like at that time. It was hard to believe that such a guy could be an experienced Data Scientist (even for me, even right now)

当我意识到我没有机会获胜的时候,一位来自 True North 的高级数据科学家来到了我工作的地方。我们讨论了任务、排行榜、方法和其他东西。我们聊得很愉快,当时我问他,他们将如何通过这种模式赚钱?经过一番讨论后,发现任务的目标并不是最优的,也就是说,他们做了一个回归任务,但实际上,他们需要某种推荐。此外,预测的目标没有选对。

我提出了另一个管道,这可以提高其中一个流程的效率。我们讨论了要做的实验的设置、度量标准、成功的衡量标准以及其他一些东西。我看得出他非常喜欢我的思维方式,我喜欢他喜欢这一点。

我尽可能快地运行了我们讨论的所有实验——大约花了我一个小时。截止日期前 15 分钟,我开始准备演示文稿。我勉强在时间结束前完成了它。在做那个演讲的时候,我很紧张,因为我的演示设计并不完美。然而,我成功地克服了这一点,因为我牢记,尽管我的幻灯片并不漂亮,但它们对公司是有价值的。

最终,我没有赢得 Kaggle 比赛(我获得了第 3 名),但我的解决方案被评为“最佳商业解决方案”,我赢得了“开放解决方案”赛道。我也收到了面试的邀请。在两周内,我作为真北的数据科学家继续这个项目。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

回首过去总是很有趣。

如果那天有人同意和我合并,我们会在 Kaggle 比赛中获得第二或第三名。我不会赢,也不会得到我的第一份工作。事实上,我很幸运被每个人拒绝。接下来的事件对我来说会有所不同,但毫无疑问无论如何都会很酷。

评估分类模型

原文:https://towardsdatascience.com/hackcvilleds-4636c6c1ba53?source=collection_archive---------12-----------------------

在 Python 中使用 Sci-Kit Learn 的引导式演练

作者:Ishaan Dey,Evan Heitman,& Jagerynn T. Verano

分类介绍

医生想知道他的病人是否有病。信用卡公司有兴趣确定某项交易是否是欺诈。一个研究生院的候选人感兴趣的是她是否有可能被她的项目录取。许多应用模型对预测二元事件的结果感兴趣,二元事件是可以通过简单的是或否问题来回答的任何事件。这被称为二元分类问题,不同于多类分类,在多类分类中,我们感兴趣的是预测某个人会患哪种疾病*,或者某个顾客会从选择中购买哪种产品。*

概述

回归模型通常会报告熟悉的指标,如 R2,以显示拟合优度,或模型与数据的吻合程度。对于离散的结果,我们不能应用相同的公式,因此在这篇文章中,我们将通过分解模型可能出错的错误类型和可以总结模型按照我们的预期执行的能力的定量测量,来研究如何评估分类器模型的性能。

在本文中,我们使用信用卡交易数据集,目标是标记欺诈交易(二元结果,1 表示欺诈)。鉴于欺诈发生率较低,该数据集被欠采样,以创建一个 90:10 的非欺诈与欺诈观察值的新分布。该数据被进一步分割成 70:30 的序列以测试分割。我们拟合了一个逻辑回归模型,并使用 0.5 的阈值来预测欺诈值。您可以使用下面链接的交互式 python 笔记本来跟进。

https://github . com/is handey/Classification _ Evaluation _ walk through

基础知识:混淆矩阵

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Confusion Matrix from a Binary Classification Model

对于任何包含二元结果的数据集,我们可以用 1(表示事件的发生)或 0(事件的不存在)来标记所有的观察值。我们可以将模型拟合到特征变量,并在给定特征变量集的情况下对每个观察值进行预测。被模型正确分类的值被标记为真(T),而不正确的预测为假(F)。正§和负(N)指的是模型的预测,而不是实际的观察值。例如,一个负值被错误地归类为正值,就称为假阳性。

作为题外话,许多读者将熟悉分别作为α和β的假阳性和假阴性。假阳性用α表示,而假阴性用β表示。因此,当我们建议统计的概率小于 0.05 的α时,我们说错误地将结果标记为显著的概率是 0.05。

我们的模型,用下面的函数产生,产生了下面的混淆矩阵。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Confusion Matrix from Logistic Regression Classifier (Threshold = 0.5)

confusion_matrix(y_test, y_pred)

准确性有什么问题?

假设我们遇到了一种用于检测癌症的诊断工具,该工具可以以 99.8%的准确率正确预测结果。这真的令人印象深刻吗?据 CDC 估计,一般人群的癌症死亡率约为 0.16%。如果我们制作一个虚拟模型,并说不管我们可获得的特征信息如何,每一个结果都应被归类为负面情况,我们将以 99.84%的准确率执行,因为我们样本中 99.84%的观察结果都是负面的。我们可能会错过对每一个癌症患者的诊断,但我们仍然可以如实地报告,我们的模型具有 99.84%的准确率。显然,一个好的模型应该能够区分什么是阳性案例,什么不是阳性案例。

从更专业的角度来说,准确性的值可以很容易地从混淆矩阵中获得,因为真阳性和真阴性的数量除以测试的观察总数。简单来说,就是观察值被正确分类的比例。

准确度= (TP + TN) / (TP + TN + FP + FN)

回顾我们的欺诈检测数据,我们的逻辑回归模型预测准确率为 97.7%。但是这比虚拟分类器好多少呢?让我们看看结果的实际分布。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Target Class Distribution of Full Dataset

sns.countplot(x=fraud['Class'])

在这里,阶级不平衡,或者说结果的倾斜分布是显而易见的。使用快速检查。value_counts()函数显示,我们的数据中有 137 个欺诈案例,1339 个非欺诈案例,这意味着我们的测试案例中有 9.3%是欺诈的。如果我们要应用一个虚拟模型,盲目地预测所有的观察结果为非欺诈案例,就像可以用 sklearn.dummy 包完成的那样,我们可以看到我们报告的 90.7%的准确性与我们的频率分布(90.7:9.3)相匹配。因此,即使我们的虚拟模型毫无用处,我们仍然可以报告它的执行准确率达到 90%。下面的混淆矩阵进一步显示了这种无法区分的细节。

y_test.value_counts() / len(y_test)
dum = DummyClassifier(strategy='most_frequent')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Confusion Matrix from Dummy Classifier

我们可以用什么措施来代替呢?

使用混淆矩阵的最大好处是,我们可以很容易地得出各种其他值,这些值反映了我们的模型相对于我们对虚拟模型的预期运行得有多好。

敏感度(或回忆,或真阳性率)

正如我们在上面的例子中看到的,一个好的模型应该能够成功地检测到几乎所有的实际欺诈案件,因为没有发现欺诈案件的成本要比通过错误地暗示这是一个欺诈案件来审查一个非欺诈交易的成本高得多。敏感度,也称为回忆,量化了这种直觉,并反映了正确分类的阳性与实际阳性病例的比率。

灵敏度= TP / (TP + FN)

敏感性的解释相当简单。所有值的范围都在 0 到 1 之间,其中值 1 表示模型检测到了每一个欺诈案例,而值 0 表示没有检测到所有实际的欺诈案例。使用我们的逻辑回归模型,我们的灵敏度为 108 / 137 = 0.788。

特异性

特异性有助于我们确定在所有真正的非欺诈案例中,有多少被正确归类为非欺诈案例。假阳性率或假警报率与特异性相反。在我们的欺诈检测模型中,我们的特异性为 1334/1339 = 0.996

特异性= TN / (TN + FP)

假阳性率= 1 —特异性

在这种特殊情况下,特异性是一个不太相关的指标,因为将非欺诈案件归类为欺诈案件的成本低于完全遗漏欺诈案件的成本。但是在有些情况下,错误警报同样是不可取的,例如在疾病检测中,误诊会导致不必要的后续程序。

精度

另一方面,我们可能想要测试我们预测的确定性,例如,我们可能对我们的模型所发现的欺诈案例中有多少是真正的欺诈案例感兴趣。Precision 就是这样做的,它提供了真实阳性相对于预测阳性的比例。直觉上,低精度意味着我们让很多客户头疼,因为我们对欺诈交易的分类多于实际欺诈交易。使用逻辑回归模型,我们的精度是 108/113 = 0.956

精度= TP / (TP + FP)

我们应该最大化特异性还是敏感性?

在我们的欺诈数据集中,更重要的是我们要最大限度地减少未被发现的欺诈案件的数量,即使这样做的代价是将非欺诈案件错误地归类为欺诈案件,因为前一个案件给公司带来的成本要高得多(潜在的数千美元的收入损失或客户验证其交易的几分钟时间)。换句话说,我们宁愿犯第一类错误,也不愿犯第二类错误。尽管我们更倾向于最大化敏感性和特异性的模型,我们更倾向于最大化敏感性的模型,因为它最小化了 II 型错误的发生。

F1 比分

最后但同样重要的是,F1 分数总结了精确度和召回率,并且可以被理解为这两个度量的调和平均值。f 1 值为 1 表示完美的精确度和召回率,因此 F1 值越高,模型越好。我们的逻辑模型显示 F1 值为 0.864。

F1 = 2 (精度灵敏度)/(精度+灵敏度)

当我们改变概率截止值时会发生什么?

我们到底是如何得出我们的预测的?从我们的逻辑回归,我们计算出一个给定的观察是欺诈性的预测概率落在 0 到 1 之间。我们说,所有大于 0.5 的概率都应该表示欺诈预测,而所有小于 0.5 的值都返回合法交易的预测。

但是考虑到我们更愿意犯第一类错误,即使只有很小的可能性,把一个案例归类为欺诈不是更好吗?换句话说,如果我们降低区分欺诈和非欺诈的门槛,让我们抓住更多的欺诈,从橙色线转移到绿色线,会怎么样?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Orange = Discrimination Threshold of 0.5; Green = Discrimination Threshold of 0.1

根据我们之前讨论的定义,模型的特异性会增加,因为我们现在分类了更多的阳性,但同时,我们增加了将非欺诈案件错误地标记为欺诈的可能性,从而随着假阴性数量的增加而降低了灵敏度。这是一个不断的权衡,灵敏度从降低特异性增加的速率是每个模型特有的属性。

到目前为止,我们报告的指标来自于以 0.5 为阈值计算的混淆矩阵(橙色线),但是如果我们将区分阈值降低到 0.1(绿线),灵敏度将从 0.788 增加到 0.883,而特异性将从 0.996 下降到 0.823。

classification_report(y_test, y_pred)

ROC 曲线

我们可以使用受试者操作特征或 ROC 曲线来查看所有阈值下这些值的变化,ROC 曲线绘制了每个阈值的真阳性率对假阳性率,或灵敏度对 1 特异性。我们可以在这里看到我们的逻辑分类器的 ROC 曲线,其中不同的阈值应用于预测的概率,以产生不同的真阳性和假阳性率。

我们的虚拟模型是蓝色曲线上(0,0)处的一个点,在该点处,判别阈值使得任何小于 1.0 的概率都被预测为非欺诈案例。这表明,尽管我们正确地分类了所有非欺诈案例,但我们错误地分类了所有非欺诈案例。另一方面,一个完美的区分模型将在曲线上的(0,1)处有一个点,这将表明我们的模型完美地分类了所有欺诈案件以及非欺诈案件。这两个模型的线将作为阈值水平的函数生成。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

fpr, tpr, thresholds = roc_curve(y_test, y_pred_prob[:,1])

对角线显示了真阳性率与假阳性率相同的情况,其中正确检测欺诈案件和将非欺诈案件检测为欺诈案件的机会相等。这意味着任何高于对角线的 ROC 曲线都比预测结果的随机机会更好,假设 50/50 类平衡。

因此,我们将在该领域中遇到的所有 ROC 曲线将被绘制在 y=x 线之上,垂直向上到(0,1),然后水平穿过(1,1)的线之下。我们可以通过观察曲线下面积或 AUC ROC 来量化 ROC 曲线的表现程度。该值将采用介于 0.5(对角线)和 1.0(完美模型)之间的值。我们的欺诈检测模型的 AUC ROC 为 0.934,对于开箱即用的模型来说还不错。

from sklearn.metrics import auc
auc = roc_auc_score(y_test,logis_pred_prob[:,1])

PRC 曲线

回到 logit 模型,当我们将辨别阈值从橙色转移到绿色时,精确度和召回率会发生什么变化?当我们从阈值 0.5 移动到 0.1 时,召回率从 0.788 增加到 0.883,因为正确检测到的欺诈在实际欺诈数量中的比例增加,而精确度从 0.956 下降到 0.338,因为真实欺诈案例在预测欺诈案例数量中的比例下降。正如我们对 ROC 曲线所做的那样,我们可以将精确度和召回率之间的权衡绘制为阈值的函数,并获得精确度-召回率曲线,或 PRC。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

precision, recall, thresholds = precision_recall_curve(y_test, y_pred_prob[:,1])

通常,PRC 更适合在高度不平衡的数据集上训练的模型,因为 ROC 曲线的假阳性率公式中使用的高真负值会“夸大”模型表现如何的感觉。PRC 曲线避免了这个值,因此可以反映模型性能的偏差较小的度量。

我们可以使用平均精度值或平均 F1 分数(每个阈值的平均值)来简洁地总结这条曲线,理想值接近 1。

from sklearn.metrics import f1_score
from sklearn.metrics import average_precision_scoref1 = f1_score(y_test, y_pred_prob)
ap = average_precision_score(y_test, y_pred_prob)

离别的思绪

公式快速回顾:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

TPR = True Positive Rate; FPR = False Positive Rate

在这篇文章中,我们讨论了如何超越精度来衡量二元分类器的性能。我们讨论了灵敏度、特异性、精确度和召回率。我们研究了当我们改变决策阈值时会发生什么,以及我们如何可视化灵敏度和特异性之间的权衡,以及精确度和召回率之间的权衡。提醒一下,所有生成的可视化和模型都可以在交互式 python 笔记本上找到,链接如下。

https://github . com/is handey/class ification _ Evaluation _ walk through

这个帖子到此为止。如果您有任何问题,请告诉我们!

黑客谷歌珊瑚边缘 TPU:运动模糊和 Lanczos 调整大小

原文:https://towardsdatascience.com/hacking-google-coral-edge-tpu-motion-blur-and-lanczos-resize-9b60ebfaa552?source=collection_archive---------9-----------------------

谷歌的珊瑚项目最近已经停止测试。根据基准测试,Coral 设备为 DIY 创客提供了出色的神经网络推理加速。这些设备基于专门的张量处理单元 ASIC (Edge TPU),这被证明是有点棘手的工作,但强制限制和怪癖是值得的。我渴望探索 TensorFlow 和 Edge TPU 之间互操作的深层内部机制,并对两者进行破解,以做一些很酷的、非标准的、疯狂的事情。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

以下期望你熟悉张量流和边缘 TPU 基础知识。官方文档很好,所以通过查看边缘 TPU 和边缘 TPU 编译器 上的 TensorFlow 模型应该足以继续。重复我的实验需要 Ubuntu Linux 和一个外部 Coral USB 加速器。

首先,Edge TPU 软件不是完全开源的。最“美味”的部分、edgetpu_compiler可执行文件和libedgetpu.so共享库是专有的。这一事实增加了潜在的黑客复杂性,但也使它更有趣!例如,查看哪些 API 被libedgetpu.so公开的唯一方法是用objdump转储导出的符号:

$ objdump -TCj .text /usr/lib/x86_64-linux-gnu/libedgetpu.so.1/usr/lib/x86_64-linux-gnu/libedgetpu.so.1:     file format elf64-x86-64DYNAMIC SYMBOL TABLE:
000000000006baa0 g    DF .text 000000000000000d  VER_1.0     edgetpu::RegisterCustomOp()
0000000000072b40 g    DF .text 000000000000001f  VER_1.0     edgetpu::EdgeTpuContext::~EdgeTpuContext()
0000000000072ad0 g    DF .text 0000000000000006  VER_1.0     edgetpu::EdgeTpuContext::~EdgeTpuContext()
0000000000072ad0 g    DF .text 0000000000000006  VER_1.0     edgetpu::EdgeTpuContext::~EdgeTpuContext()
000000000006dc10 g    DF .text 000000000000000a  VER_1.0     tflite_plugin_destroy_delegate
000000000006be50 g    DF .text 00000000000001dd  VER_1.0     edgetpu_list_devices
000000000006bb80 g    DF .text 0000000000000107  VER_1.0     edgetpu_version
000000000006bab0 g    DF .text 000000000000000a  VER_1.0     edgetpu::EdgeTpuManager::GetSingleton()
000000000006d090 g    DF .text 0000000000000b7c  VER_1.0     tflite_plugin_create_delegate
000000000006bb20 g    DF .text 0000000000000012  VER_1.0     edgetpu_free_devices
000000000006bac0 g    DF .text 000000000000005e  VER_1.0     edgetpu::operator<<(std::ostream&, edgetpu::DeviceType)
000000000006c030 g    DF .text 0000000000000c1a  VER_1.0     edgetpu_create_delegate
000000000006bb40 g    DF .text 000000000000000a  VER_1.0     edgetpu_free_delegate
000000000006bb50 g    DF .text 0000000000000024  VER_1.0     edgetpu_verbosity

该输出明确暗示 Edge TPU API 是用又长又粗的钉子钉在 TensorFlow Lite 上的。根本不可能以任何其他方式使用该设备。如果你期望看到像“将这个矩阵乘以 TPU 边缘的那个向量”这样的低级 API,那就不走运了。

让我们快速回顾一下面向 Edge TPU 的 TensorFlow Lite API 是如何工作的:

  1. 使用常规张量流生成计算图。比如训练一个深度神经网络。
  2. 将其转换为 TensorFlow Lite 格式,这是 flatbuffers 而不是 protobuf,并且具有不同的模式。新图必须是特殊的,使 TPU 边缘的朋友。值得注意的是,包含的操作(ops)必须量化为 [uint8](https://www.tensorflow.org/lite/performance/quantization_spec),因为边缘 TPU 只能处理无符号字节。
  3. 交叉手指再转换一次,这次用edgetpu_compiler。底层格式保持不变,但是支持的操作被融合并编译成一个神奇的 Edge TPU 块。
  4. 确保 Coral 设备已连接,使用 Edge TPU 操作代理创建一个新的 TensorFlow Lite 解释器,并调用

因此,如果不调用外部程序并随时读写文件,在 Edge TPU 上运行任意计算是不可能的。

这个多步骤过程中的关键细节是可以为 Edge TPU 编译的操作列表。TensorFlow Lite 不支持它的哥哥的所有功能,Edge TPU 只支持剩下的一小部分。比如没有矩阵乘法(惊喜!我根据经验检查了*“输出张量是一维的”*完全连通)。这些限制使得除了卷积和全连接神经网络之外的边缘 TPU 推理变得非常困难。但并非不可能。这就是《邮报》变得有趣的地方。

边缘 TPU 上的运动模糊

运动模糊效果是图像与“半径”内核的 2D 卷积的结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Horizontal motion blur. Left: original photo. Right: photo after applying the effect. Source: Wikipedia.

在张量流术语中,该操作被称为 DepthwiseConv2d,它被广泛用于深度卷积神经网络中,并得到边缘 TPU 的支持。图像像素可以用 RGB 格式表示,每个通道一个字节——这正是 Edge TPU 所需要的。让我们穿越所有的坑和危险,并基准如何快速运动模糊图像过滤边缘 TPU 与 Python!

0 →传递函数

在本节中,忘记 TensorFlow Lite 和 Edge TPU 的存在。让我们熟悉一下主要的逻辑。以下代码创建卷积内核。dim是尺寸,angle是平面上的运动角度,单位为弧度。

当涉及到肮脏的视觉效果时,它总是很方便。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下一步是用常规的 TensorFlow 2.0 测试我们的运动模糊效果。我们利用[tf.nn.depthwise_conv2d](https://www.tensorflow.org/api_docs/python/tf/nn/depthwise_conv2d)来计算图像与内核的 2D 卷积。所有步幅都等于 1,因此图像尺寸不会改变。

在 Jupyter 中,人们可以使用%timeit motion_blur(images)快速测量运动模糊性能。在我的 4x2(HT)英特尔 i7–8565 u CPU 上,它产生了大约 5.30 秒±0.09 秒的结果。

tf.function → tflite

既然我们已经确定了整体方法是可行的,那么是时候将其移植到 TensorFlow Lite 了。

我们必须在create_motion_blur_func中指定tf.function的输入签名,因为 TensorFlow Lite 目前不允许除第一个“批处理”维度之外的可变形状。因此,我们的运动模糊只能处理相同大小的图像。

create_motion_blur_func_lite是围绕create_motion_blur_func的包装器,将后者转换为 TensorFlow Lite。generate_lite_model从属于tf.function的计算图中初始化tf.lite.TFLiteConverter——我们的运动模糊算法——并将转换结果写入磁盘。create_func_lite加载它,设置一个新的tf.lite.Interpreter并返回调用闭包。

根据%timeit,新的实现速度更快:3.50 秒 0.24 秒对 5.3 秒。这种性能提升令人惊讶,因为根据系统监视器,执行只利用了我的八个 CPU 核心中的一个。我们可以想象结果。带[netron](https://github.com/lutzroeder/netron)的 tflite 型号:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

TensorFlow Lite motion blur graph visualized with netron.

tflite →边缘 TPU

最后,我们需要从香草 TensorFlow Lite 过渡到边缘 TPU。这一步是迄今为止最棘手和最复杂的。我们将继续在现有代码的基础上构建,一次添加一个特性。

边缘 TPU 需要uint8操作数据类型(dtype)而不是float32。不幸的是,我们不能让tf.nn.depthwise_conv2d直接与uint8一起工作:只支持float64float32bfloat16float16。因此,我们不得不求助于“训练后量化”,这意味着伪造数据类型并给所有运算增加量化属性。gen_input_samples模拟从 0 到 255 的像素值范围,这就是 TensorFlow Lite 中量化参数化的方式。我们进一步调用量化模型上的edgetpu_compiler来用边缘 TPU 的优化代码替换 2D 卷积 op。tf.lite.Interpreter必须增加experimental_delegates=[load_delegate("libedgetpu.so.1.0")]以让它知道如何处理优化的边缘 TPU 运算

edgetpu_compiler支持 TensorFlow 2.0 的理想世界中,上面的代码应该可以工作。让我们运行代码看看。

Edge TPU Compiler version 2.0.267685300Model compiled successfully in 231 ms.Input model: motion_blur_1_1920_1058_3_25_1.57.tflite
Input size: 3.03KiB
Output model: motion_blur_1_1920_1058_3_25_1.57_edgetpu.tflite
Output size: 296.85KiB
On-chip memory available for caching model parameters: 1.73MiB
On-chip memory used for caching model parameters: 10.00KiB
Off-chip memory used for streaming uncached model parameters: 0.00B
Number of Edge TPU subgraphs: 1
Total number of operations: 3
Operation log: motion_blur_1_1920_1058_3_25_1.57_edgetpu.logModel successfully compiled but not all operations are supported by the Edge TPU. A percentage of the model will instead run on the CPU, which is slower. If possible, consider updating your model to use only operations supported by the Edge TPU. For details, visit g.co/coral/model-reqs.
Number of operations that will run on Edge TPU: 1
Number of operations that will run on CPU: 2Operator                       Count      StatusDEPTHWISE_CONV_2D              1          Mapped to Edge TPU
DEQUANTIZE                     1          Operation is working on an unsupported data type
QUANTIZE                       1          Operation is otherwise supported, but not mapped due to some unspecified limitation

DEPTHWISE_CONV_2D成功编译,但是有奇怪的DEQUANTIZEQUANTIZE操作没有成功。它们是编译器不支持的 TensorFlow 2.0 工件,是从motion_blur_func签名中的强制float32 dtype 产生的。netron可视化应该让一切都变得清晰。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Quantized TensorFlow Lite model (left) and compiled Edge TPU model (right) visualized with netron.

因此,我们必须做四次多余的工作:

  1. 将像素值从uint8切换到float32,将其传递给 TensorFlow Lite 引擎。
  2. TensorFlow Lite 执行QUANTIZE并切换回uint8
  3. 计算完卷积后,我们返回到DEQUANTIZE中的float32
  4. TensorFlow Lite 将控制权交还给调用者,我们转换到uint8以保存图像。

将原来的QUANTIZEDEQUANTIZE去掉。tflite 将使模型再次变得伟大。不幸的是,要做到这一点并不容易。根本没有官方 API 来操作 TensorFlow Lite 模型。我们需要深入挖掘。

深入挖掘

我提到 TensorFlow Lite 模型格式是 flatbuffers 。这是一种相对较新的通用序列化格式,我敢打赌它会在 Google 内部与 protobuf 竞争。flatbuffers 的 Python API 不允许修改现有文件,bummer。幸运的是,flatbuffers 展示了一个对象的两种不同表示:binary 和 JSON,并支持它们之间的无损转换。有flatc -jflatc -b命令可以分别转换。tflite 到 JSON 以及向后。我们将使用它们从模型中去除冗余的操作,由提供。模式是公共的。其实这就是 TensorFlow Lite 开发者自己用来升级的方法。tflite 型号

密码显示原始的。tflite 模型具有错误的 dtypes: int8而不是uint8。TensorFlow 2.0 在没有被要求的情况下尝试应用多声道量化;Edge TPU 不支持多声道量化,我们也必须解决这个问题。

第二次尝试更成功:

Edge TPU Compiler version 2.0.267685300Model compiled successfully in 171 ms.Input model: motion_blur_1_1920_1058_3_25_1.57.tflite
Input size: 2.71KiB
Output model: motion_blur_1_1920_1058_3_25_1.57_edgetpu.tflite
Output size: 296.56KiB
On-chip memory available for caching model parameters: 1.73MiB
On-chip memory used for caching model parameters: 10.00KiB
Off-chip memory used for streaming uncached model parameters: 0.00B
Number of Edge TPU subgraphs: 1
Total number of operations: 1
Operation log: motion_blur_1_1920_1058_3_25_1.57_edgetpu.logOperator                       Count      StatusDEPTHWISE_CONV_2D              1          Mapped to Edge TPU

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Patched mother TensorFlow Lite model (left) and compiled Edge TPU model (right) visualized with netron.

现在是承诺的基准。我安装了不限制工作频率的libedgetpu-max。我的结果是 5.00s 0.25 和 0.262s 0.001 的原始和边缘 TPU 版本,相应地。 Edge TPU 比我的 CPU 上最快的float32实现快 10-20 倍!当然,这种比较是不公平的,因为只有一个 CPU 内核被用来运行原来的处理器。tflite,我不能在 Python 中更改它(在 C++ 中看起来可能)。我预计真正的性能加速在 2-4 倍之间。此外,一个合适的矢量化uint8 CPU 实现应该比float32快 4 倍——例如 pillow-simd 。所以边缘 TPU 已经没有公平至上。从好的方面来看,Coral 设备的功耗至少降低了 20 倍。

边缘 TPU 上生成的图像看起来与地面真实情况相同,但由于精度损失而不是字节精确的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Motion blur effect comparison. Left: canonical tf.function with float32. Right: Edge TPU.

Lanczos 在边缘 TPU 调整大小

模糊图像很有趣,但tf.nn.depthwise_conv2d还有更实际的应用。可以说,调整图像大小的最佳方法也是由卷积实现的。卷积在像素重采样之前充当低通滤波器。有各种各样的像素平均内核,Lanczos 可能是最著名的。我们的计划是在 TensorFlow 中定义一个新的 Lanczos 内核,并生成边缘 TPU 模型以将图像缩小 2 倍。

我们重用之前设计的函数:create_func_edgetpugenerate_edgetpu_model。代码中最有趣的地方是我们如何利用卷积核在同一操作中实现像素子采样。我们来看看结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Lanczos resize with TensorFlow’s depthwise_conv2d. Left: canonical tf.function with float32. Right: Edge TPU.

展示鹦鹉

哎呀。普通白色矩形。出事了。原因隐藏在 Lanczos 内核的外观中。让我们像在运动模糊部分一样想象它。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Lanczos 2D kernel, “hot” color scheme.

你在颜色栏里看到“-0.006”了吗?没错,Lanczos 内核含有负值。如您所知,内置量化将float32更改为int8,我的“JSON 后处理”将 dtypes 设置为uint8。因此内核被错误地应用,我们因大量溢出而遇难。我们必须从零开始提升量化零点,并更新所有的核权重。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Lanczos resize with TensorFlow’s depthwise_conv2d. Left: canonical tf.function with float32. Right: Edge TPU after the fix.

屏蔽溢出

我们的内核并不完美:由于舍入误差和整体不精确,一小部分像素通道看起来大于 256。这就是为什么在天气是白色的地方,蓝绿色的人工制品会出现在鹦鹉的眼睛附近。类似地,最暗的区域可能变成负值。我们需要价值剪裁。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Lanczos resize comparison. Left: canonical tf.function with float32. Right: Edge TPU after all the fixes.

基准测试结果:tf.function/float32150 ms 2,TF lite/float32165 ms 3,TF lite/uint8220 ms 3,Edge TPU 47.8 ms 0.1。也就是说, Edge TPU 快了 3 倍。使用 pillow-simd 调整相同图像的大小需要 4.9 毫秒 0.1 秒,因此 Edge TPU 比我的 CPU 上最快的 Lanczos resize 实现慢了大约 10 倍。我使用了以下代码:

from PIL import Image
img = Image.open("parrot.jpg")
%timeit img.resize((529, 960), Image.LANCZOS)

我不知道 pillow-simd 中使用的确切内核大小,可能是 5x5 而不是我们的 11x11。将内核大小更改为 5x5 会在边缘 TPU 上产生 40 毫秒。如此微妙的时间变化表明,更大的内核尺寸释放了 Edge TPU 的全部能量。

所以我用不同的内核大小进行了测试。edgetpu_compiler内核大小大于或等于 28 时崩溃。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Lanczos resize time for different kernel sizes on Edge TPU.

显然,在张量流/Python 方面有一个巨大的常数因子,它先于边缘 TPU 的优势。25x25 内核导致 25 倍以上的计算,但运行时间仅增加两倍。这个事实与 Coral 文档中关于尽可能避免 CPU 边缘 TPU 通信的陈述一致。

结论

  1. 只有在 TensorFlow Lite 中才能使用边缘 TPU。
  2. 除了在边缘 TPU 上进行卷积神经网络推理之外,很难编写任何程序。但有可能。比如使用tf.nn.depthwise_conv2d的通用图像滤波。
  3. Edge TPU 不喜欢 TensorFlow 2.0。最好还是坚持文档说的,用 1.x,用 2.0 还是有可能黑掉的。
  4. TensorFlow Lite 模型可以在 JSON 表示中自由编辑和黑客攻击。
  5. 边缘 TPU 上的变换将被绑定到特定的图像大小。不过,预先生成模型是可能的。
  6. 运动模糊或其他卷积滤波器可以在具有微小视觉差异的边缘 TPU 上计算。性能与现代 4x2HT 英特尔 CPU 相当,或者在更大的内核尺寸上更好。
  7. Lanczos 在 Edge TPU 上调整大小的速度很快,但仍然比一个伟大的矢量化 CPU 实现慢 10 倍。
  8. 演示的图像处理技巧应该作为包含在 CNN 模型中的增强管道的一部分工作得最好。
  9. 当主机 io 不占优势时,边缘 TPU 显示出最大功率。

我鼓励读者在 Edge TPU 上尝试其他卷积核。各种模糊、线条检测、边缘检测、锐化等。等。搜索“图像卷积示例”

神经网络的安全漏洞

原文:https://towardsdatascience.com/hacking-neural-networks-2b9f461ffe0b?source=collection_archive---------5-----------------------

当你的网络认为一切都是鸵鸟的时候怎么办。

神经网络无处不在,它们是可以被黑客攻击的。本文将深入研究神经网络和机器学习模型的对抗性机器学习和网络安全。

本文借用了哈佛大学 AC 209 b 讲座的内容,很大程度上归功于哈佛大学 IACS 系的讲师帕夫洛斯·普罗托帕帕斯

“人们担心计算机会变得太聪明并接管世界,但真正的问题是它们太笨了,它们已经接管了世界。”佩德罗·多明戈斯

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

神经网络在现代世界中变得越来越普遍,并且它们的实现经常没有考虑到它们潜在的安全缺陷。这导致了一个新的网络安全领域,它着眼于神经网络的脆弱性,以及我们如何保护它们免受黑客的攻击。虽然这是一个相对严肃的问题,但在本文中,我将采用幽默的方式概述神经网络的弱点,希望读者会觉得有趣并有所收获。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Adding strategic noise to an image can be used to fool neural networks. Source: Goodfellow et al., 2015.

概观

在这篇文章中,我将向读者全面介绍 对抗性机器学习 领域,其中试图愚弄、毒害或操纵机器学习模型,以规避其内在目的,通常是出于恶意目的。

我在本教程中要回答的问题是:

  • 什么是对抗性机器学习?
  • 我的机器学习模型怎么会被攻击?
  • 我如何保护我的模型免受攻击?
  • 这些攻击在现实世界中会产生什么后果?

由于神经网络的基础架构和学习过程,这个问题是神经网络最关心的问题,因此本文的大部分内容都集中在这些网络上,尽管也给出了其他示例。

现实世界中的神经网络

你可能从来不知道,但你可能每天都在不假思索地使用神经网络。问过 Siri 一个问题吗?有一个神经网络。自动驾驶汽车?神经网络。你相机上的人脸识别?更多的神经网络。Alexa?再一次,神经网络…

这些只是有形的应用,人们每天都在使用神经网络的大量无形应用。无论是你在工作中使用的软件程序,还是仅仅为了寻找那天晚上去吃饭的地方,你都可能使用某种形式的神经网络(我这样说是因为有大量不同类型的网络)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Some of the most common uses of neural networks today.

这些网络的普及性只会增加,这是有充分理由的。神经网络是 通用函数逼近器 。那是什么意思?这意味着,如果我的神经网络有足够大的容量(即足够多的节点——权重和偏差——可以修改),那么我就能够使用神经网络逼近任何非线性函数。这是一个非常有力的陈述,因为我基本上可以学习任何函数,不管它有多复杂。

然而,我们为此付出了代价,那就是网络可能对你给它们的输入相当敏感。因此,可以说,如果你知道按哪个按钮是对的,就比较容易愚弄网络。通过操纵图像的某些节点,很容易激活与特定特征相关的神经元,这可以使网络给出虚假的输出。

这可能看起来相对无辜,毕竟,正如 Ian Goodfellow 在他的开创性论文中所示,看到神经网络随机将熊猫变成长臂猿是非常滑稽的。然而,这类攻击的实际应用可能会令人望而生畏。

我们以 Alexa 为例,假设你定期使用 Alexa 买东西。有一天,一个特别聪明的黑客坐在你的房子外面(在网络安全世界中通常被称为 wardriving ),入侵你的 Wifi——如果你没有妥善保护你的路由器或者仍然有默认密码,使用 aircrack-ng 这出奇地容易。

黑客现在可以访问 Alexa,它具有代表你进行交易的安全特权,给予你口头批准。如果黑客足够聪明,可以想象他们可以欺骗 Alexa 把你所有的钱都给黑客,只要按下神经网络上的正确按钮。

这可能会吓到你,你可能会去扔掉 Alexa。然而,仅仅因为这是可能的,并不意味着它是容易的。有些“攻击”可以在神经网络上实施,大多数拥有聪明工程师的公司至少已经考虑到这可能是一个问题。

我们现在将深入探讨如何攻击神经网络,可能的攻击类型,以及工程师和数据科学家如何帮助保护他们的网络免受这些潜在漏洞的影响。

对抗性攻击

在这一节中,我将向读者介绍对抗性攻击的分类。

白盒攻击与黑盒攻击

从本质上讲,对神经网络的攻击包括引入策略性放置的噪声,旨在通过错误地刺激对产生特定结果很重要的激活电位来欺骗网络。为了真正理解我说的“有策略地放置噪音”是什么意思,请考虑以下由谷歌大脑开发的图像,以展示同样的效果如何愚弄人类。你以为左边和右边的图都是猫吗?还是狗?还是各一个?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

就具有决策边界的分类算法而言,这里是网络如何被引入的策略噪声破坏的图示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Illustration of an adversarial attack in the feature space. Source: Panda et al. 2019

有两种主要类型的攻击是可能的:白盒攻击和黑盒攻击。灰箱攻击出现在网络安全领域,但不存在于神经攻击中。

当有人能够访问底层网络时,就会发生白盒攻击。因此,他们会知道网络的架构。这类似于对公司 IT 网络的白盒渗透测试——这是企业界测试公司 IT 基础设施防御能力的常规测试。一旦黑客了解了您的 IT 网络是如何构建的,就更容易进行破坏。在这种情况下,“知识就是力量”这句话尤其适用,因为了解网络的结构可以帮助您选择最具破坏性的攻击来执行,也有助于揭示与网络结构相关的弱点。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

White box attack. Architecture is known and individual neurons can be manipulated.

当攻击者对底层网络一无所知时,就会发生黑盒攻击。在神经网络的意义上,该架构可以被认为是一个黑盒。虽然对黑盒进行攻击更加困难,但它仍然不是不受影响的。黑盒攻击的底层程序首先由 Papernot et al. (2015) 描述。

假设我们能够在网络上测试尽可能多的样本,我们可以通过将一组训练样本传入网络并获得输出来开发一个推断网络。然后,我们可以使用这些标记的训练样本作为我们的训练数据,并训练一个新的模型,以获得与原始模型相同的输出。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一旦我们有了新的网络,我们可以为我们推断的网络开发对抗性的例子,然后使用这些例子对原始模型进行对抗性攻击。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

该模型不依赖于对网络体系结构的了解,尽管这将使攻击更容易进行。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

物理攻击

您可能已经意识到,这些攻击都是软件攻击,但实际上也有可能是物理攻击网络。我不知道这种情况是否真的发生过,但是一些研究已经在研究使用“敌对标签”来欺骗网络。下面是一个例子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Physical attacks on neural networks using adversarial stickers.

显然,这给自动驾驶汽车的大规模采用带来了一个潜在问题。没有人会希望自己的车无视停车标志,继续驶向另一辆车、一栋建筑或一个人。不过,不要太惊慌,有一些方法可以保护网络免受所有这些类型的攻击,我将在后面介绍。

闪避和毒药攻击

到目前为止,我们讨论的所有攻击都是规避攻击,即它们涉及“愚弄”系统。一个很好的例子是欺骗垃圾邮件检测器来保护电子邮件帐户,这样你就可以将垃圾邮件发送到某人的收件箱中。垃圾邮件检测器通常使用某种形式的机器学习模型(如朴素贝叶斯分类器),可用于单词过滤。如果一封电子邮件包含太多通常与垃圾邮件相关的“流行语”(给定你的电子邮件历史作为训练数据),那么它将被归类为垃圾邮件。然而,如果我知道这些单词,我可以故意改变它们,使检测器不太可能将我的电子邮件视为垃圾邮件,我将能够欺骗系统。

另一个很好的例子是在计算机安全领域,机器学习算法通常在入侵检测系统(IDSs)或入侵防御系统(IPSs)中实现。当一个网络数据包到达我的电脑时,如果它带有恶意软件的特征签名,我的算法就会在它做出任何恶意行为之前阻止它。然而,黑客可以使用混淆代码来“迷惑”网络,使其无法发现问题。

最后一个例子是,麻省理工学院的一些研究人员开发了一只 3D 打印的乌龟,它的纹理能够骗过谷歌的物体检测算法,并使其将乌龟归类为步枪。鉴于谷歌的算法目前在许多行业用于商业目的,最后一个问题有点令人担忧。

中毒攻击包括损害算法的学习过程。这是一个比规避攻击稍微微妙和阴险的过程,但只对参与在线学习的模型有效,即他们在工作中学习,并在新的经验(数据)对他们可用时重新训练自己。这听起来可能不是太大的问题,直到我们考虑一些中毒攻击的例子。

回到我们的 IDSs 示例,由于新的病毒总是在开发中,所以这些都是通过在线学习不断更新的。如果希望防止零日攻击,有必要让这些系统具备在线学习的能力。攻击者可以通过注入精心设计的样本来破坏训练数据,最终危及整个学习过程。一旦发生这种情况,您的 IDS 基本上就变得毫无用处,您面临着更大的潜在病毒风险,甚至可能意识不到这一点。因此,中毒可被视为训练数据的敌对污染。我们的垃圾邮件检测器示例也是如此。

这一节给了我们一个大概的概述,介绍了我们可能会遇到的各种问题。在下一节中,我们将更仔细地研究如何处理白盒和黑盒攻击,最常见的敌对攻击类型,以及人们可以在其神经网络中使用的防御措施,以改善这些安全问题。

具体攻击类型

Ian Goodfellow(生成对抗网络的创始人,也是创造这个术语的人)发表了第一篇研究神经网络潜在安全漏洞的论文。他决定把这叫做‘对抗性机器学习’,相对容易和生成性对抗性网络混淆。先说清楚,它们不一样。

伊恩描述了第一种攻击,快速梯度步进法。正如我们到目前为止所讨论的,这通过引入策略噪声来操纵分类器所使用的清晰的决策边界。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

When your neural network suddenly thinks everything is an ostrich… Source: Szegedy et al. (2013)

后 Goodfellow 2015 攻击

在过去几年中,出现了许多新的攻击媒介,主要有:

为了避免这篇文章拖得太长,我将不深入讨论每个算法的具体细节。但是,如果您希望我在以后的文章中更详细地介绍这些内容,请随意发表评论。

网络防御

已经开发了许多方法来保护神经网络免受我们已经讨论过的各种类型的攻击媒介。

对抗训练

防御对抗性攻击的最好方法是通过对抗性训练。也就是说,你主动生成对立的例子,调整它们的标签,并把它们添加到训练集中。然后,您可以在这个更新的训练集上训练新的网络,这将有助于使您的网络在对抗实例中更加健壮。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Adversarial training in action.

平滑决策边界

正规化永远是答案。从这个意义上说,我们正则化的是数据的导数。这有助于平滑类之间的决策边界,并使使用策略噪声注入来操纵网络分类变得不那么容易。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(Left) Hard decision boundary, (right) smoothened decision boundary.

搞混了

Mixup 是一个简单的过程,乍一看似乎有点奇怪,它涉及通过某个因子λ混合两个训练样本,该因子介于 0 和 1 之间,并为这些训练样本分配非整数分类值。这有助于增加训练集,并降低网络的乐观分类倾向。它本质上扩散和平滑了类别之间的边界,并减少了分类对少量神经元激活电位的依赖。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 [## 混淆:超越经验风险最小化

大型深度神经网络是强大的,但表现出不良行为,如记忆和敏感性…

arxiv.org](https://arxiv.org/abs/1710.09412)

挂钩

对于那些不熟悉聪明的汉斯的故事的人,我建议你谷歌一下。主要的故事是关于一匹马,据说它能够通过跺一定次数的脚来做基本的算术。但后来发现,这匹马其实是在作弊,是在回应周围人群的言语和视觉线索。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 [## 张量流/cleverhans

一个用于构建攻击、构建防御和基准测试的对抗性示例库…

github.com](https://github.com/tensorflow/cleverhans) [## 欢迎来到 cleverhans 博客

与 cleverhans 相关的 Jekyll 博客

www.cleverhans.io](http://www.cleverhans.io/)

本着同样的精神,CleverHans 是一个 Python 库,它被开发来测试机器学习系统对敌对例子的脆弱性。如果你正在开发一个神经网络,想看看它有多健壮,用 CleverHans 测试一下,你会发现对它的脆弱程度有一个概念。这类似于使用 Burp 套件来测试代码注入漏洞。

渗透测试

就像任何形式的网络安全一样,你总是可以花钱请人来黑你,看看他们造成了多大的破坏。然而,你可以让他们签署一份文件,规定限制攻击者可以做什么。这让你知道自己在实际网络攻击中的脆弱程度。我不知道渗透测试公司目前是否提供这些类型的服务,但这不会让我感到惊讶。

最终意见

我希望你喜欢这篇文章,现在对这个有趣的网络安全新子领域有了更好的理解,它涉及通过各种攻击媒介损害机器学习模型。

这个领域仍然非常新,可以找到大量的研究论文,其中大部分可以在 arxiv 上找到,这意味着公众可以免费查看它们。我建议你读一读其中的一些——参考资料中的文章是一个很好的起点。

时事通讯

关于新博客文章和额外内容的更新,请注册我的时事通讯。

[## 时事通讯订阅

丰富您的学术之旅,加入一个由科学家,研究人员和行业专业人士组成的社区,以获得…

mailchi.mp](https://mailchi.mp/6304809e49e7/matthew-stewart)

参考

[1] Panda,p .,Chakraborty,I .,和 Roy,k .,针对对抗性攻击的安全机器学习的离散化解决方案。2019.

[2]张,h .,西塞,m .,多芬,y .,洛佩兹-帕兹,d .2017.

[3] Goodfellow,I .,Shlens,j .,和 Szegedy,c.《解释和利用对立的例子》。2014.

[4]n . paper not,p . McDaniel,I . good fellow,Jha,s .和 z .切利克,Swami,a .对机器学习的实用黑盒攻击。2015.

[5] Papernot,n .,McDaniel,p .,Jha,s .,弗雷德里克松,m .,切利克,Z.B .,和 Swami,a.《对抗环境中深度学习的局限性》。《第一届 IEEE 欧洲安全与隐私研讨会论文集》,第 372–387 页,2016 年。

[6] Kurakin,a .,Goodfellow,I .,和 Bengio,s,《大规模的对抗性机器学习》。2016.

[7] Tramer `,f , Kurakin,a , paper not,n , good fellow,I , Boneh,d ,和 McDaniel,p,《整体对抗训练:攻击和防御》。2018.

深度学习的黑魔法

原文:https://towardsdatascience.com/hacks-for-doing-black-magic-of-deep-learning-ab5be5ff5f56?source=collection_archive---------24-----------------------

机器学习

建议,这将有助于你掌握 CNN 的培训

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by Greg Rakozy on Unsplash

总是超负荷

深度神经网络被称为“黑盒”,在那里很难进行调试。编写完训练脚本后,您无法确定脚本中没有任何错误,也无法预见您的模型是否有足够的参数来学习您需要的转换。

安德烈·卡帕西(Andrej Karpathy)关于过度饮食的建议就来自这里。

在训练开始时,在将所有数据输入到你的网络之前,试着在一个固定的批次上过量,没有任何增加,学习率非常小。如果它不会被过度拟合,这意味着,要么你的模型没有足够的学习能力来进行你需要的转换,要么你的代码中有一个 bug。

只有成功过拟合后,才合理的开始对整个数据进行训练。

选择您的标准化

归一化是一种强有力的技术,用于克服消失梯度并以更高的学习速率训练网络,而无需仔细的参数初始化。最初在 S.Ioffe 的论文中,提出了对整个批次的特征进行归一化,并且将激活转向单位高斯分布,以学习一个用于所有数据分布(包括测试数据)的通用均值和方差。当您需要预测影像的一个(或多个,如果是多标签分类)标签时,这种方法适用于所有分类任务。但是当你进行图像到图像的翻译任务时,情况就不同了。在这里,对整个数据集学习一个移动平均线和一个移动平均线可能会导致失败。在这种情况下,对于每一幅图像,作为网络的一个输出,你要获得独一无二的结果。
这就是实例规范化的由来。相反,在实例标准化中,为批中的每个图像独立计算统计数据。这种独立性有助于成功地训练网络,以完成图像超分辨率、神经风格转换、图像修复等任务。
所以要小心,不要在图像转换任务中使用迁移学习的常见做法,最著名的预训练网络有 ResNet、MobileNet、Inception。

越大(不总是)越好

众所周知,在训练深度神经网络的过程中,批量越大,收敛越快。而且,经验表明,在某个点之后,批量大小的增加会损害模型的最终性能。在工作中,N.S. Keskar 等人。艾尔。这与以下事实有关:在大批量的情况下,训练倾向于收敛到训练函数的尖锐极小值,而在小批量的情况下,收敛到平坦极小值。因此,在第一种情况下,来自训练函数的灵敏度很高,数据分布的微小变化将损害测试阶段的性能。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

A Conceptual Sketch of Flat and Sharp Minima. The Y-axis indicates the value of the loss function and the X-axis the parameters.

但是 P. Goyal 等人。艾尔。⁴在论文《精确、大型迷你批处理 SGD:在 1 小时内训练 ImageNet》中表明,可以用高达 8K 的批处理大小训练 ImageNet,而性能不会下降。正如作者所说,优化困难是大型迷你批处理的主要问题,而不是泛化能力差(至少在 ImageNet 上)。作者提出了一个线性比例规则的学习率,取决于批量大小。规则如下

当小批量乘以 k 时,将学习率乘以 k。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

ImageNet top-1 validation error vs. minibatch size.

小批量也可以被认为是正则化的形式,因为在这种情况下,您将有嘈杂的更新,这有助于避免快速收敛到局部最小值并提高泛化能力。

深度方向可分卷积并不总是你的救星

近年来,随着性能的提高,神经网络中的参数数量急剧增加,设计高效、低成本的神经网络成为当今的一个课题。
作为 Tensorflow⁵框架的一部分,谷歌提出的解决方案之一是深度方向可分离卷积,这是传统卷积层的一种修改,需要的参数更少。

让我们假设,我们有一层

fi - 输入过滤器

fo-输出滤波器

KH-内核的高度

kw-内核宽度

在卷积的情况下,层中的参数数量将为

N = kh * kw * fi * fo

我们按照输出滤波器的次数对每个输入滤波器进行卷积,然后求和。

在深度方向可分离卷积的情况下,它将是

N = kh * kw * fi + 1 * 1 * fo

我们用内核 (kh,kw) 对每个输入滤波器进行一次卷积,然后用内核 *(1,1)*按照输出滤波器的次数对这些中间滤波器进行卷积。

现在,让我们来看两个例子。

示例 1

假设该层有以下值

fi = 128

fo = 256

kh = 3

kw = 3

卷积层中的参数数量将是

3 * 3 * 128 * 256 = 294.912

深度方向可分离卷积中的参数数量为

3 * 3 * 128 + 1 * 1 * 256 = 99.456

在深度方向可分离卷积的情况下优势是明显的!!!

示例 2

现在让我们假设该层有其他值

fi = 128

fo = 256

kh = 1

kw = 1

卷积层中的参数数量将是

1 * 1 128 * 256 = 32.768*

深度方向可分离卷积中的参数数量为

1 * 1 128 + 1 * 1 * 256 = 32.896*

因此,我们可以看到,在第二种情况下,我们没有减少,而是增加了参数的数量。

参考

[1]安德烈·卡帕西博客,http://karpathy.github.io/2019/04/25/recipe/

[2]S.Ioffe,C. Szegedy 批量归一化:通过减少内部协变量移位加速深度网络训练,2015,第 32 届机器学习国际会议论文集,

[3]N. S. Keskar,D. Mudigere,J. Nocedal,M. Smelyanskiy,和 P. T. P. Tang,关于深度学习的大批量训练:泛化差距和尖锐极小值,2017,

[4]P. Goyal,P. Dollar,R. Girshick,P. Noordhuis,L. Wesolowski,A. Kyrola,,Y. Jia,K. He .,精准,大迷你批量 SGD:1 小时训练 ImageNet,2017,arXiv:1706.02677

[5] Tensorflow 官网

手部关键点检测

原文:https://towardsdatascience.com/hand-keypoints-detection-ec2dca27973e?source=collection_archive---------7-----------------------

用小训练数据集检测手部图像上的关键点位置。

H 训练一个网络来准确预测手指和手掌线的位置需要多少标记图像?我受到了这篇博文的启发,在这篇博文中,作者报告了 97.5%的分类准确率来分类一个人是否戴着眼镜,而每类只有 135 张训练图像。对于我的任务,从 15 个不同的人那里得到的 60 张带标签的图片能有多准确?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

12 points to detect.

目的是在给定未标记的手部图像的情况下,精确估计 12 个点的 x 和 y 坐标。8 个点位于指尖和四个手指的根部,另外 4 个点沿手掌的中心线等距分布,第一个点和最后一个点正好位于中心线的起点和终点。

这些图片是我的朋友们的,他们友好地同意参加,每人最多 6 张图片,左手 3 张,右手 3 张。这些照片是用不同的智能手机在不同的白色背景和不同的光照条件下拍摄的。60 张图片都是我手动标注的。

对于神经网络处理,我使用了基于 PyTorch 的 fastai 库 v1.0.42。Jupyter 笔记本做 IDE,我笔记本的 NVidia GTX 960M 4Gb VRAM 做训练用。我下面分享的结果的总训练时间是 25 小时,非常合理的时间,因为这种 GPU 远远不是当今市场上最好的硬件!

该项目的主题是数据增强,幸运的是 fastai 提供了高效的图像转换算法,以及定义这些算法的干净 API。让我们深入细节。

数据和模型

标记的数据被分成 51 个训练图像和 9 个验证图像。验证包括出现在列车组中的人的 3 个图像,但也包括既不出现在列车组中也不与列车中的任何人共享相同背景\摄像机的两个人的 6 个图像。在预处理中,所有右侧图像都水平翻转。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

All train images with labels.

在如此小的数据集上进行训练,数据增强是必要的,我对进入神经网络的每张图像都使用了随机缩放、包裹、旋转、亮度和对比度转换。fastai 库允许很容易地定义它,并且同样的仿射变换也应用于标签点。根据训练集的样本均值和方差对图像进行归一化,分别针对每个 RGB 通道进行归一化,并将其大小调整为 4:3 的比例,更具体地说是 384×288 像素。听起来要定义很多东西?令人惊讶的是,整个数据定义可以归结为下面一小段代码。

transforms = get_transforms(do_flip=False, max_zoom=1.05, max_warp=0.05, max_rotate=5, p_affine=1, p_lighting=1)data = (PointsItemList.from_folder(path=PATH/'train', recurse=False)
   .split_by_idxs(train_idx, valid_idx)
   .label_from_func(get_y_func, label_cls=PointsLabelList)
   .transform(transforms, tfm_y=True, size=(384,288),
              padding_mode='border', resize_method=ResizeMethod.PAD)
   .databunch(bs=6, num_workers=0, no_check=True)
   .normalize(stats))

该型号是带有定制头标准 resnet34 主干。从 resnet34 中删除了最后两个分类层,取而代之的是,我添加了 1x1 卷积以减少通道数,然后是两个完全连接的层。第二个完全连接的层输出 24 个激活,在通过 tanh 激活函数后,这些激活表示 12 个关键点的 x 和 y 坐标。

但有时用暗语说更好:

head_reg = nn.Sequential(
    nn.Conv2d(512,64,kernel_size=(1,1)),
    nn.BatchNorm2d(64),
    nn.ReLU(),
    Flatten(),
    nn.Linear(64 * 12 * 9, 6144),
    nn.ReLU(),
    nn.Linear(6144, 24),
    Reshape(-1,12,2),
    nn.Tanh())
learn = create_cnn(data, arch, metrics=[my_acc,my_accHD], loss_func=F.l1_loss, custom_head=head_reg)

上面的自定义头定义使用常规 PyTorch 语法和模块,除了我写的整形模块,它只是…嗯,整形张量。这种整形是必需的,因为我的标签坐标由 fastai 内部表示为 12 乘 2 张量,它需要匹配。此外,标签被重新调整为[-1;1]范围,这就是为什么 tanh 激活函数在这里是合适的。

优化目标是最小化列车组的 L1 损失。

还有两个额外的准确性指标来判断网络的性能和进度。在验证集上测量,这两个度量计算预测坐标落在实际标签的 0.1 和 0.01 误差内(分别对于第一和第二度量)的比例。这里标签的范围也是[-1;1],并且给定 384×288 像素的图像尺寸,可以容易地计算出第二(高精度)度量允许高度和宽度的最大误差分别为 1.92 和 1.44 像素。

神经网络训练

NN 训练是通过运行这行代码 40 次来完成的:

learn.fit_one_cycle(cyc_len = 100, max_lr = 1e-4)

除了使用 Adam optimizer 运行 100 个时期的常规训练之外,该 fastai 方法具有有趣的学习速率和动量策略,fastai 使用该策略在广泛的应用中提供更快的收敛。在 Sylvain Gugger 的博客文章中可以看到更多的细节。我发现它对于我的模型来说开箱即用。对于每 100 个历元周期,50 个历元后的误差比开始时高,但在周期结束时总是有所改善。看看下图中的典型错误发展。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Learning rate (left) and momentum (right) changing across 100 epochs, 8 batches in every epoch.

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Losses for epochs 2500 to 2600, 8 batches per epoch. More data were added at epoch 2500.

这个学习速率和动量过程被称为 1 周期策略。据称,它也有助于对抗过度拟合,而且它似乎比我尝试的其他(公认有限的)选项收敛得更快。

为了理解不同变化的影响,我将培训分为 5 个步骤:

  1. 1500 个时期,resnet34 个主干层冻结在 ImageNet 预训练值上,并且仅训练自定义头层。仅使用 35 个训练图像的子集。
  2. 300 个纪元,解冻骨干层。
  3. 700 个纪元,增加了更多数据增强。具体来说,最大缩放 5%到 10%,最大扭曲 5%到 20%,最大旋转 5 度到 10 度。
  4. 500 个历元,从 4 个额外的人添加 16 个图像到训练集。使总的训练集大小达到 51。
  5. 1000 个周期,每个周期降低 20%的学习率,最后一个周期达到 1e-5 左右的学习率。记住,每个周期是 100 个纪元。

下图总结了进度:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Loss and accuracy metrics during the training on 4000 epochs.

这 5 个步骤中的每一步都对模型进行了额外的改进。数据扩充中的更多转换尤其重要,对验证集误差的改善有显著贡献。解冻和更多的数据也为验证集提供了很好的改进。另一方面,学习率的降低显著改善了训练集误差,而验证集误差则停滞不前。过度适应在这里是一个真正的问题,使用较小的学习率会使情况变得更糟。

总的来说,在训练期间,网络看到了 147k 个不同的变换图像,并且训练花费了 25.5 小时。

讨论结果

虽然训练集的最终平均 L1 误差是 0.46 像素,但是验证集的最终平均误差是 1.90 像素。此外,训练集的这个分数是针对经变换的图像的,而验证图像是未经变换的(更容易)。这是一个明显的过度拟合。

尽管如此,结果还是相当不错的,下图显示了验证集推理。请注意,绿点是实际的标签,红点是最终模型的预测。观察结果,似乎该模型使其预测更加全局化,并且在点之间更加相互依赖,而不是给予局部边缘更多的权重。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Validation set final results. Images 1, 2 and 3 are of people present in the train set, but different images. Images 4 to 6 and 7 to 9 are two people not appearing in the train set. Green points are actual labels and red points are predicted ones.

模型改进的明确方向是更多的数据和更智能的数据增强。平均误差仍然需要小 4-5 倍,才能达到良好的/生产水平的质量。

什么没用?

在不同位置添加下降层和应用权重衰减没有用。这可能是因为 1 周期策略的高学习率本身就是一种正则化形式,不需要更多的正则化。

替代 Adam 的其他优化方法没有显示出任何改进,或者取得了更差的结果。为不同的层组选择不同的学习速率也是一个死胡同。

如果从自定义头中删除 BatchNorm 层,即使学习率为 1e-5,模型也不再是可训练的。

我尝试了另外两种主干模型架构, DarknetU-Net ,它们有不同的定制头,但在我的实验中,它们的效果不如更简单的 resnet34。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

最后,fastai 库在这一点上只有仿射变换(平行线映射成平行线)和透视变换(直线映射成直线)。鉴于数据扩充在这个项目中的重要性,我实现了一个额外的转换,见图片。然而,由于某种原因,它并没有导致改善。

结论

仅用 51 幅训练图像,所讨论的模型在独立图像上达到了相当好的预测精度。更多的数据和更多的数据扩充被证明可以提高精度,并在某种程度上成功地对抗过拟合。

Fastai 库是这个项目的合适工具,它提供了以下好处:

  1. 简洁但灵活的数据和模型定义
  2. 一系列用于数据扩充的内置仿射和透视变换,也可以自动变换标注点
  3. 智能学习率和动量策略似乎给出了更快的收敛并减少了过拟合

使用 Turi Create 和 Core ML 进行手动跟踪

原文:https://towardsdatascience.com/hand-tracking-with-turi-create-and-core-ml-f3f9d3b60f7a?source=collection_archive---------14-----------------------

在移动设备上进行实时手部跟踪的任务既有趣又具有挑战性。手是身体中较难检测和跟踪的部分之一。原因是手可能看起来非常不同,无论是形状(尽管这也适用于身体的其他部位)还是手指的位置。一手牌可以在几分钟内从出拳变成击掌。这意味着很难从不同的手状态、不同的角度收集带有适当注释的数据集。在这篇文章中,我们将看到如何使用 Turi Create 实现这一点的方法,Turi Create 是苹果用于创建核心 ML 模型的框架。

https://www.youtube.com/watch?v=q7cBgyssAg8

目标跟踪

这里我们需要的机器学习任务是物体检测。对象检测使您不仅可以检测当前相机帧中是否存在对象,还可以检测该对象的位置。这使您能够绘制一些视觉指示(如矩形)或在检测到的地方呈现一个虚拟模型。一旦找到该对象,您就可以使用视觉的跟踪功能,在感兴趣的对象移动时更新视觉指示。

如果你只想知道物体是否在图片上,你需要图像分类。为此,您可以使用 Create ML,这是苹果公司用于创建机器学习模型的另一个框架。

Turi 创建

Turi Create 是一个简化定制机器学习模型创建的工具,可以轻松导出为苹果的核心 ML 格式。这意味着,你不必成为机器学习专家,就可以在你的应用程序中添加一些智能。Turi Create 支持的任务有推荐、图像分类、对象检测、风格转换、活动分类等等。虽然它需要一点 Python 代码,但它仍然很容易上手,正如我们在这篇文章中看到的。

你可以在他们的 GitHub repo 上找到如何安装 Turi Create 的细节。基本上,您需要从终端执行以下操作:

pip install -U turicreate

一旦安装了 Turi Create,我们的下一个任务就是找到并准备数据。

准备数据

机器学习的最大挑战之一是找到足够的数据来训练机器学习模型。正如我们在开始时讨论的,检测一手牌的正确界限可能有点棘手。这就是为什么我们需要非常好的数据集。让我们看看我发现了什么。

我使用的数据集可以在找到。我试过几种选择,这一种被证明是最好的。它相当大,大约 6 GB,对于没有 GPU 的 Mac 机器来说,训练模型是相当具有挑战性的。我发现的其他选项有 EgoHandsOxford Hand 数据集。我在考虑将这三者结合起来,建立一个更好的机器学习模型,但我的 Mac 无法处理这一点。

现在,让我们看看数据。我使用的 VIVA 手部检测挑战的数据被分成两个文件夹,pos 和 posGt,都在 train 文件夹下。pos 文件夹包含所有图像,而 posGt 包含 csv 格式的所有注释,以及关于手(左手或右手)的信息。每个 csv 条目包含关于手的边界框的信息,使用 2D 图像平面中的左上角点、宽度和高度[x y w h]来描述。

Turi Create 期望什么?

另一方面,Turi Create 需要 SFrame,这是一种表格数据结构,可以将图像和相应的注释放入其中。注释是 JSON 格式的。每个图像都有一个对象数组,其中有键坐标和标签。坐标值包含边界框的信息,而标签则包含什么是边界框。在我们的例子中,不是左手就是右手。

[ {'coordinates': {'height': 104, 'width': 110, 'x': 115, 'y': 216}, 'label': 'left'}, ...]

坐标代表矩形的中心,以及宽度和高度,这与手部数据集中的数据组织方式不同(这里是左上角,而不是矩形的中心)。

为了创建该数据结构,我们将进行一些 Python 编码。以下脚本将把数据集的图像和 csv 文件转换成 SFrame,然后可用于创建 Turi Create 模型。

import turicreate as tc
import os
from os import listdir
from os.path import isfile, joinpath = 'train/posGt'
imagesDir = "train/pos"
files = [f for f in listdir(path) if isfile(join(path, f))]
annotations = []
labels = []
for fname in files:
 if fname != ".DS_Store":
  lines = tuple(open(path + "/" + fname, 'r'))
  count = 0
  entries = []
  for line in lines:
   if count > 0:
    words = line.split()
    passengerLabel = words[0]
    label = "left"
    if passengerLabel.find("left") == -1:
     label = "right"
    x = int(words[1])
    y = int(words[2])
    width = int(words[3])
    height = int(words[4])
    xCenter = x + width / 2
    yCenter = y + height / 2
    coordinates = {'height': height, 'width': width, 'x': xCenter, 'y': yCenter}
    entry = { 'coordinates' : coordinates, 'label' : label }
    entries.append(entry)
   count = count + 1
  annotations.append(entries)
sf_images = tc.image_analysis.load_images(imagesDir, random_order=False, with_path=False)
sf_images["annotations"] = annotations
sf_images['image_with_ground_truth'] = \
    tc.object_detector.util.draw_bounding_boxes(sf_images['image'], sf_images['annotations'])
sf_images.save('handsFrame.sframe')

为了实现这一点,我们首先浏览注释,解析 CSV 并创建坐标 JSON,同时还将左上坐标转换为中心坐标。接下来,我们使用 turicreate 包中的辅助函数加载图像。然后我们简单地放置注释,同时保持顺序。然后,将 SFrame 保存到手帧数据结构中。

还可以调用 sf_images.explore()来可视化边界框和图像。然而,你应该用少量的图片来测试,否则它会永远加载。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下一步是使用 SFrame 创建核心 ML 模型。这意味着我们应该进行另一轮 Python 编码。

import turicreate as tc# Load the data
data =  tc.SFrame('handsFrame.sframe')# Make a train-test split
train_data, test_data = data.random_split(0.8)# Create a model
model = tc.object_detector.create(train_data, feature='image', max_iterations=120)# Save predictions to an SArray
predictions = model.predict(test_data)# Evaluate the model and save the results into a dictionary
metrics = model.evaluate(test_data)# Export for use in Core ML
model.export_coreml('Hands.mlmodel')

我们在这里做的是首先加载我们用第一个脚本创建的 SFrame。然后,我们创建训练和测试数据,以 80–20%的比例随机分割。然后,使用 Turi Create 中的 object_detector.create 方法,我们用训练数据创建模型。您可以使用 max_iterations 属性(我的机器在 150 时崩溃,所以 120 是我能做的最好的)。之后,我们进行预测并评估模型。在最后一步中,我们以核心 ML 格式导出模型。

iOS 实施

现在我们有了核心 ML 模型,很容易将其集成到 iOS 应用程序中,只需将其拖放到 Xcode 项目中。

让我们检查一下创建的模型。这种模式被称为 Pipeline,它与苹果从 iOS 12 开始的愿景非常契合。它接受 416×416 大小的图像作为输入。作为输出,它提供了两个 MLMultiArrays,其中包含关于检测到的对象的置信度和坐标。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

好消息是,您不必处理这些复杂的多维数组 Vision 框架会自动为您完成这项工作(针对使用 Turi Create 创建的管道模型),并为您提供一个VNRecognizedObjectObservation。该类型包含关于边界框的信息(作为一个 CGRect ),以及置信度。现在,当您运行 Vision 会话时,您只需要检查结果是否属于该类型,并绘制适当的边界框。

func handleNewHands(request: VNRequest, error: Error?) {
        DispatchQueue.main.async {
            //perform all the UI updates on the main queue
            guard let results = request.results as? [VNRecognizedObjectObservation] else { return }
            for result in results {
                print("confidence=\(result.confidence)")
                if result.confidence >= self.confidence {
                    self.shouldScanNewHands = false
                    let trackingRequest = VNTrackObjectRequest(detectedObjectObservation: result, completionHandler: self.handleHand)
                    trackingRequest.trackingLevel = .accurate
                    self.trackingRequests.append(trackingRequest)
                }

            }
        }
    }

一旦物体被检测到,我们可以告诉视觉跟踪它。为此,我们正在创建类型为 VNTrackObjectRequest 的对象,在这里我们通过已识别的对象观察并开始跟踪。每次调用完成处理程序 handleHand 时,我们都会更新跟踪矩形。

源代码

这是 iOS 实施中最重要的部分。你可以在这里找到完整的源代码,以及所有的视觉检测和跟踪细节。

结论

对我来说,这是一个非常有趣的机器学习练习。Turi Create 是一个非常强大的创建机器学习模型的工具。它创建了与 iOS 应用程序无缝协作的模型。

这个项目有很大的改进空间。首先,该模型应该用更多的数据进行训练,这样它就可以在所有光线条件和手的位置下正确地工作。此外,可以改进 iOS 代码,以更好地处理跟踪请求。现在,同一只手可能会识别出多个矩形。

另一件很酷的事情是不仅跟踪整只手,还跟踪手指。

这就是这篇文章的全部内容。你认为检测身体部位对我们未来的应用程序有用吗?一般来说机器学习怎么样?在下面的部分省去任何注释。

原载于 2019 年 1 月 19 日【martinmitrevski.com】

手工制作人工神经网络

原文:https://towardsdatascience.com/handcrafting-an-artificial-neural-network-e0b663e88a53?source=collection_archive---------11-----------------------

在这篇文章中,我已经实现了一个完全矢量化的人工神经网络代码,具有辍学和 L2 正则化。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by JJ Ying on Unsplash

在本文中,我实现了一个在多个数据集上测试的人工神经网络的全矢量化 python 代码。此外,辍学和 L2 正则化技术的实施和详细解释。

强烈建议您完成人工神经网络的基本工作、前向传播和反向传播。

本文分为 10 个部分:

  1. 介绍
  2. 先决条件
  3. 导入我们的库
  4. 编码我们的激活函数和它们的导数
  5. 我们的神经网络课
  6. 初始化权重和偏差
  7. 正向传播
  8. 价值函数
  9. 反向传播
  10. 预测新数据集的标注

还有,

非常感谢任何形式的反馈。

1.介绍

人工神经网络是最美的监督的深度学习的基本概念之一。它可以用于执行多种任务,如二元或分类。看起来很容易理解和实现,除非你开始编写代码。在编写这样一个网络的过程中,小问题会突然出现,导致大错误并帮助你理解你之前错过的概念。因此,在本文中,我试图实现一个人工神经网络,它可能会帮助您节省正确编码和理解该主题的每个概念所需的几天时间。我会在文章中使用标准的符号和记号。

这篇文章太密集了,所以如果你对神经网络和它们的符号不熟悉,你可能会发现很难理解所有的东西。所以,我建议给它一些时间,慢慢来,参考我在文章中提供的资源。

Github 上有的完整代码

[## tirthasheshpatel/神经网络

演示和教学用手工制作的神经网络

github.com](https://github.com/tirthasheshpatel/Neural-Network)

2.先决条件

我假设你知道什么是神经网络,以及它们是如何学习的。如果你熟悉 Python 和像 numpy 这样的库,这很容易理解。此外,要轻松通过正向反向传播部分,还需要具备线性代数微积分的良好知识。此外,我强烈建议浏览一下吴恩达Coursera 上的课程视频。

3.导入我们的库

现在,我们可以开始编码一个神经网络。第一件事是导入我们实现网络所需的所有库。

我们将使用 pandas 来导入和清理我们的数据集。 Numpy执行矩阵代数和复杂计算最重要的库。像警告时间系统操作系统这样的库很少使用。

4.编码我们的激活函数和它们的导数

我们将在本文后面需要激活函数来执行正向传播。同样,我们需要在反向传播过程中激活函数的导数。所以,让我们编写一些激活函数。

我们已经编写了四个最流行的激活函数。首先是常规的老式乙状结肠激活功能。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Source

然后我们有 ReLU整流线性单元。我们将主要使用这个激活功能。注意,我们将保持 ReLU 0 在点 0 的导数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Source

我们还有一个 ReLU 的扩展版本,叫做 Leaky ReLU 。它的工作方式就像 ReLU 一样,可以在一些数据集(不一定是所有数据集)上提供更好的结果。

然后我们有 tanh (双曲正切)激活函数。它也被广泛使用,并且几乎总是优于s 形

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Source

另外, PHIPHI_PRIME 是分别包含激活函数及其衍生函数的 python 字典

5.我们的神经网络课

在本节中,我们将创建并初始化我们的神经网络类。首先,我们将决定在初始化期间使用哪些参数。我们需要:

  1. 每层中的神经元数量
  2. 我们希望在每一层中使用的激活函数
  3. 我们的特征矩阵( X ),特征沿行,示例沿列)。
  4. 标签对应特征矩阵(y 为行向量)
  5. 初始化我们的权重和偏差的方法
  6. 要使用的损失函数

记住这一点,让我们开始编码我们的神经网络的类:

现在我们有了一个正确记录的神经网络类,我们可以开始初始化网络的其他变量。

如图所示,我们将使用’ self.m’ 来存储数据集中的个示例self.n 会存储每层个神经元的信息。 self.ac_funcs 是各层激活函数的 python 列表。 self.cost 将在我们训练网络时存储成本函数的记录值。 self.acc 将存储训练后数据集达到的记录的精度*。已经初始化了我们网络的所有变量,让我们进一步初始化我们网络的权重和偏差。*

6.初始化权重和偏差

有趣的部分现在开始。我们知道权重不能初始化为零,因为每个神经元的假设变得相同,并且网络从不学习。所以我们必须有一些方法来打破对称性,让我们的神经网络学习。我们可以使用高斯正态分布来得到我们的随机值。由于这些分布的平均值为零,权重以零为中心,并且非常小。因此,网络开始快速有效地学习。我们可以使用 np.random.randn() 函数从正态分布中生成随机值。下面两行代码足以初始化我们的权重和偏差。

我们已经将权重初始化为来自正态分布的随机值。偏差已经被初始化为零。

7.正向传播

首先,让我们了解一下没有任何正则化的前向传播。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Source

我们有 Z 作为从一层到另一层的每个神经元连接的假设。一旦我们计算出Z ,我们将激活函数f 应用于Z 值,得到每层中每个神经元的激活 y 。这就是’纯香草’正向传播。但是正如 N.Shrivastava 等人在论文中所说的。艾尔。,2014 ,辍学是一项惊人的技术,可以提高神经网络的泛化能力,使其更加健壮。所以,我们先来对退学正规化有一些直觉。

辍学正规化的本质

Dropout,顾名思义,指的是“去激活我们神经网络中的一些神经元,并训练其余的神经元。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Source

为了提高性能,我们可以用不同的超参数值来训练数十或数百个神经网络,获得所有网络的输出,并取它们的平均值来获得我们的最终结果。这个过程是 计算上非常昂贵的 并且实际上不能实现。因此,我们需要一种更优化、计算成本更低的方法来做类似的事情。辍学调整以一种非常便宜和简单的方式做着完全类似的事情。事实上,辍学是优化性能的一种非常容易和简单的方式,最近它获得了很多关注,并且几乎在许多其他深度学习模型中到处使用。

为了实现辍学,我们将使用以下方法:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Source

我们将首先从伯努利分布中抽取随机值,如果概率高于特定阈值,则保留神经元,然后执行规则的正向传播。请注意,在预测新数据集的值或测试期间,我们不会应用 dropout。

实现辍学的代码

我们将 keep_prob 作为每层神经元存活的概率。我们将只保留概率高于存活概率或 keep_prob 的神经元。假设,它的值是 0.8。这意味着我们将停用每层中 20%的神经元,并训练其余 80%的神经元。请注意,我们在每次迭代后都会停用随机选择的神经元。这有助于神经元学习在更大的数据集上概括的特征。文[1]给出了一个非常直观的证明。

我们首先初始化列表*,它将存储 ZA 的值。我们首先在 Z 中添加第一层的线性值,然后在 A 中添加第一层神经元的激活。这里, PHI 是一个 python 字典,包含了我们之前编写的激活函数。我们同样使用循环的*计算所有其他层的 ZA 的值。注意,我们没有在输入层应用 dropout。我们最后返回 ZA 的计算值。**

8.价值函数

我们将使用标准的二元/分类交叉熵成本函数*。*

我们已经用 L2 正则化 编码了我们的成本函数。参数λ被称为惩罚参数*。这有助于权重值不会快速增加,从而更好地进行概化。这里,’ a’ 包含输出层的激活值。我们还有函数 _cost_derivative 来计算关于输出层激活的成本函数的导数。我们将在反向传播*中用到它。**

9.反向传播

这里有一些公式,我们将需要执行反向传播。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Source

我们将在深度神经网络上实现这一点。右边的公式是完全矢量化的,因此我们将使用它们。一旦你理解了这些公式,我们就可以开始编码了。

我们将历元*、 alpha (学习率)、 _lambdakeep_probinterval 作为我们函数的参数来实现反向传播。在文档注释中给出了它们的描述。*

我们从正向传播开始。然后我们计算我们的成本函数的导数为 delta 。现在,对于每一层,我们计算 delta_wdelta_b ,其包含关于我们网络的权重和偏差的成本函数的导数。然后我们根据各自的公式更新 delta权重、偏差。在从最后一层到第二层更新权重和偏差之后,我们更新第一层的权重和偏差。我们这样做几次迭代,直到权重和偏差的值收敛。**

重要提示:这里可能的一个大错误是在更新权重和偏差之后更新德尔塔*。这样做会导致非常糟糕的情况 消失/爆炸渐变问题***

我们的大部分工作都在这里完成了,但我们仍然需要编写能够预测新数据集结果的函数。因此,作为我们的最后一步,我们将编写一个函数来预测新数据集的标签。

10.预测新数据集的标注

这一步非常简单。我们只需要执行前向传播,而没有丢失正则化。我们在测试期间不应用退出正则化,因为我们需要所有层的所有神经元为我们提供正确的结果,而不仅仅是一些随机值。

如图所示,我们将返回输出层的激活结果。

整个代码

这是你自己实现一个人工神经网络的全部代码。我添加了一些代码,用于打印我们训练网络时的成本和准确性。除此之外,一切都一样。

恭喜你!我们终于完成了神经网络的编码。现在,我们可以在不同的数据集上测试我们的网络。

测试我们的神经网络

我们将在著名的 MNIST 数字分类数据集上测试我们的网络。我们将只使用 8000 张图像来训练我们的网络,并在 2000 张其他图像上进行预测。可以在 Kaggle 上获取数据集。

我训练了两个 32 和 16 个神经元的隐层神经网络。我在两层都使用了 ReLU 激活函数。在用惩罚参数 1.0* 和学习率 0.1 训练网络 2500 个时期后,我们得到:*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

成本与时代的关系图如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们在训练集和测试集上都取得了相当好的准确率。通过使用像网格搜索*、随机网格搜索等技术来调整超参数,我们可以获得更高的精度。*

此外,可以随意尝试超参数、激活函数和数据集的不同值。如果你认为代码还有改进的地方,请在 GitHub 或评论区分享。非常感谢任何形式的反馈。

对你来说有些挑战

如果你已经理解了我上面提供的神经网络的代码,那么这里有一些你可以做的更好的改变。

  1. 尝试编码 softmax 激活功能 并使其工作。
  2. 比方说,我想停用第一层 30%的神经元,第二层 50%的神经元。尝试编码一个网络,在其中,我可以为每一层使用不同的 keep_prob 值。
  3. 尝试执行 小批量梯度下降 算法。它对于手写数字分类非常有用。

我希望你喜欢这篇文章和挑战。祝你数据科学之旅愉快!

使用专门为其制作的损失来处理类不平衡数据

原文:https://towardsdatascience.com/handling-class-imbalanced-data-using-a-loss-specifically-made-for-it-6e58fd65ffab?source=collection_archive---------4-----------------------

这篇文章是对 Google 在 CVPR 19 年会上发表的一篇名为基于有效样本数的等级平衡损失的论文的评论。

TL;DR——它针对最常用的损失(softmax 交叉熵、焦点损失等)提出了一种分类加权方案。)快速提高准确性,尤其是在处理类别高度不平衡的数据时。

链接到本文的实现(使用 py torch)——GitHub

有效样本数

在处理长尾数据集(大部分样本属于很少的几个类,而许多其他类的支持度很低)时,决定如何对不同类的损失进行加权可能会很棘手。通常,权重被设置为类支持的倒数或类支持的平方根的倒数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Traditional re-weighting vs proposed re-weighting

然而,如上图所示,这是过冲的,因为*随着样本数量的增加,新数据点的额外好处会减少。*新添加的样本很有可能是现有样本的近似副本,主要是在大量数据扩充(如重新缩放、随机裁剪、翻转等)时。)在训练神经网络时使用。通过样本的有效数量重新加权给出了更好的结果。

样本的有效数量可以想象为N 个样本将覆盖的实际体积,其中总体积 N 由总样本数表示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Effective number of samples

形式上,我们把它写成:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Effective number of samples

这里,我们假设新样本只会以两种方式与先前采样的数据量进行交互:要么完全覆盖,要么完全在外(如上图所示)。有了这个假设,利用归纳法就可以很容易地证明上面的表达式(证明参考论文)。

我们也可以这样写:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Contribution of every sample

这意味着第 j 个样本对样本的有效数量贡献了 Beta^(j-1。

上式的另一个含义是,如果β= 0,则 En = 1。还有,En → n 为β→1。后者可以很容易地用洛必达法则来证明。这意味着当 N 很大时,有效样本数与样本数 N 相同。在这种情况下,唯一原型数 N 很大,每个样本都是唯一的。然而,如果 N=1,这意味着所有数据都可以用一个原型来表示。

类别平衡损失

在没有额外信息的情况下,我们无法为每个类别设置单独的β值,因此,使用整个数据,我们将它设置为特定值(通常设置为 0.9、0.99、0.999、0.9999 中的一个)。

因此,类平衡损失可以写成:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

CB Loss

这里, L(p,y) 可以是任意损失函数。

类别平衡焦点损失

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Class-Balanced Focal Loss

焦点丢失的原始版本有一个 alpha 平衡变体。相反,我们将使用每个类别的有效样本数对其进行重新加权。

类似地,这样的重新加权项也可以应用于其他著名的损失(sigmoid 交叉熵、softmax 交叉熵等)。)

履行

在开始实施之前,在使用基于 sigmoid 的损失进行训练时需要注意一点——使用 b = -log(C-1)初始化最后一层的偏差,其中 C 是类的数量而不是 0。这是因为设置 b=0 会在训练开始时导致巨大的损失,因为每个类的输出概率接近 0.5。因此,我们可以假设类 prior 是 1/C,并相应地设置 b 的值。

类别的权重计算

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

calculating normalised weights

上面几行代码是一个简单的实现,用于获取权重并将其归一化。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

getting PyTorch tensor for one-hot labels

这里,我们得到了权重的一个热点值,这样它们可以分别与每个类的损失值相乘。

实验

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

类平衡提供了显著的好处,尤其是当数据集高度不平衡时(不平衡= 200,100)。

结论

使用有效样本数的概念,我们可以解决数据重叠的问题。由于我们不对数据集本身做出任何假设,因此重新加权条款通常适用于多个数据集和多个损失函数。因此,类不平衡的问题可以通过更合适的结构来解决,这一点很重要,因为大多数真实世界的数据集都存在大量的数据不平衡。

参考

[1]基于有效样本数的类平衡损失:【https://arxiv.org/abs/1901.05555

处理不平衡的数据:

原文:https://towardsdatascience.com/handling-imbalanced-data-4fb691e23fe9?source=collection_archive---------23-----------------------

预测客户取消电话服务

在这个项目中,我试图预测取消电话服务的客户,以便采取措施留住这些客户。
我使用的数据集来自openml.org,你可以点击这里直接链接到这个数据集。

不平衡数据集

这个数据集是不平衡的,这给它带来了一些挑战。首先让我们来看看不平衡:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Amount of customers staying or cancelling

为了解决这个问题,我使用了 SMOTE 方法对数据进行上采样。我也试着用 ADYSN,但是效果不太好。这可能是由于 ADYSN 也产生随机噪声,对于这个问题,只是降低了模型预测结果的能力。

由于数据集的情况,我决定优化召回率,同时在一定程度上仍然考虑准确性。这是因为希望正确预测尽可能多的客户离开,同时尽可能降低误报率。

我用成本收益分析给模型打分。计算方法如下:根据我掌握的资料,平均每月账单是 60 美元。然后我计算了 24 个月合同的长度,结果是 1440 美元。然后,我决定促销金额为 500 美元,这只是为了便于计算。
任何真正的否定,或被正确预测为留下的客户,不加或减任何东西。
任何假阴性,或者将取消但被预测为留下的客户,每个都将损失 1,440 美元。
任何真阳性,或将要取消并被预测为取消的客户,将被提供促销,因此收益将是$1,440 - $500 = $940。
任何误报,或预测离开但没有离开的客户,将被提供促销,因此将损失 500 美元。

特征的相关性

我在开始时注意到的另一件事是关于特性的相关性:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

与目标没有太多的相关性,这是“类”的特征,一些在大约 0.2,分钟和费用也几乎%100 相关。这是有道理的,因为在当时,这些数据是账单中最重要的部分。

建模

当我在考虑功能的数量和相关性问题时,一方面在费用和分钟之间有很多相关性,但另一方面没有多少主要功能。因此,我认为随机森林最适合这种类型的数据,因为它对每棵树的特征和数据进行随机化,然后对整个森林进行预测。我用基本的默认超级参数测试了几种不同的方法,随机森林确实表现最好。在对测试数据进行了几次优化后,我得到的结果是这样的:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这样做的成本效益是 71,320 美元,或者说,如果我们预测要离开的 121 个客户都留下来,那么留住将要离开的客户的实际收益将是每个先前实际离开的客户 589 美元。
在进行 5 次交叉验证后,测试集的平均成本收益为 51,152 美元。

交叉验证

在处理不平衡数据时,只需注意交叉验证。因为你应该只对训练数据进行 smote,然后对真实的测试数据进行预测,这就限制了你如何进行交叉验证。常规的交叉验证方法对此不起作用。
为了解决这个问题,我使用了分层的 K 型折叠,它从每个类别中提取相同的数量,因此每个折叠的结果都是相似的。
然后我得到每一个分裂,对训练数据应用 SMOTE,在此基础上训练模型。保存每次折叠的分数,然后进行平均。

下面是示例代码:

skf = StratifiedKFold(n_splits=5) # Set up the splitsX_trains2 = {} # Make dictionaries for each train and test split
X_tests2 = {}
y_trains2 = {}
y_tests2 = {}
for i, (train_index, test_index) in enumerate(skf.split(X, y)):# Get the folds and make the index number the key in each dict
 X_trains2[i] = X.loc[train_index]
 X_tests2[i] = X.loc[test_index]
 y_trains2[i] = y[train_index]
 y_tests2[i] = y[test_index]scores2 = [] # Make a list to put all scores into 
train = 0 # Setup to save all train scores 
test = 0 # Setup to save all test scores
cb = 0 # Cost-Benefit numbersfor i in range(5):
 smoted_x, smoted_y = sm.fit_sample(X_trains3[i], y_trains3[i])     # SMOTE the training splits
 rf5.fit(smoted_x, smoted_y) # Fit the model
 trainpred = rf5.predict(X_trains3[i]) # Predict train (not Smoted)
 testpred = rf5.predict(X_tests3[i]) # Predict test
 train += recall_score(y_trains3[i], trainpred) # Total all train recall scores for each loop
 test += recall_score(y_tests3[i], testpred) # Total all train recall scores for each loop

 cb += cost_benefit(y_tests3[i], testpred) # Total the Cost-benefit scores scores2.append((i, recall_score(y_trains3[i], trainpred), recall_score(y_tests3[i], testpred))) # Append a tuple of index, train recall total and test recall total) print(f’\n{recall_score(y_tests3[i], testpred)}’)
 cm(y_tests2[i], testpred) # Print a confusion matrix
 print(classification_report(y_tests3[i], testpred))
print(train/5, test/5, cb/5) # Print the total scores / # of Folds

特征重要性

有了一个好的工作模型后,我就可以查看特性的重要性,看看我能从中找出什么来改善模型的解释能力,然后就能在未来的业务决策中使用它。

我做了一些功能工程,将所有的分钟和所有的费用合并成两个功能。因为这两个特性高度相关,所以我删除了分钟列。我还删除了所有的 call number 列,以及 state 和 account_length 列,因为它们的特性重要性接近于 0。
我还放弃了 number_vmail_messages,因为它与语音邮件计划高度相关。
我总结了 4 个特征,它们具有相应的重要性:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Top Features

运行仅具有这些特征的模型,并进行 5 次交叉验证,可带来 56,964 美元的成本效益。
这向我展示了通过基于特征重要性组合和删除特征,模型变得更好。

基于重要性的特征分析

在研究了特征关系后,我发现的最重要的事情是,任何账单超过 72 美元的人都更有可能离开。从这个视频中可以看出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

超过 72 美元钞票的数字如下:
离开:315
留下:247

考虑到一般数据中有 85%到 15%的离职率,让更多的人以更高的离职率离开是非常重要的。在检查账单超过 72 美元的顾客如何与其他特征交叉时,这也是一致的。
如果我们向平均账单超过 72 美元的每个人提供 500 美元的促销活动,那么每位入住的顾客将获得 547 美元,这与我们从整个数据集获得的金额相似。
我建议对此进行更多的调查,看看如何最好地处理账单较高的客户,以留住更多的客户。

推荐

我最后的建议是向顾客提供某种形式的促销。
弄清楚应该提供多少优惠,什么样的促销最能成功留住顾客。
多研究如何用更高的账单留住顾客。

请留下反馈,如果你对此有想法,如果你想看到它的代码,可以在这里看到。

使用重采样处理不平衡数据

原文:https://towardsdatascience.com/handling-imbalanced-data-using-re-sampling-872b6db4fe67?source=collection_archive---------48-----------------------

不平衡数据是高级分析解决方案中普遍存在的问题。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo By Lauraljstc from Pixabay

介绍

不平衡类数据是机器学习中常见的问题。除非以适当的方式处理,否则它可能会导致一个假装为最佳执行模型的模型,同时偏向于特定的类。

例如,考虑以下包含 10 个类的数据集,但是每个类的出现是不均匀分布的。使用此数据集训练的模型偏向于 C1、C2、C4 和 C5,但很少预测 C7、C8、C9 和 C10 类。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Imbalanced classes— Image by Author

重新取样

合成少数过采样技术(SMOTE) [1]

这是一种流行且成功的方法,它创造了少数民族阶层的新样本,而不仅仅是复制样本。考虑前面的场景,其中我们只有 C10 的 3 个样本(C10_D_1、C10_D_2 和 C10_D_3)(因为我们只有 3 个样本,所以这三个样本都被视为邻居),SMOTE 在 3 个数据点之间创建线,并从线中选取合成数据点(C10_S_1 和 C10_S_2)。下图说明了我们所讨论内容的图形解释。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Features Space— Image by Author

这可以使用名为“ scikit-learn-contribscikit-learn 的扩展版本来执行,下面的代码段显示了使用 scikit-learn-contrib 使用 SMOTE。

SMOTE 算法有一些属性值需要优化;不幸的是,由于 SMOTE 的评分和转换方法的不变性,流水线和随机化搜索 CV 不能用于执行自动优化。因此,我们需要手动阻止和播放属性值来进行优化。有关 SMOTE 和属性的更多详细信息,请参考[2]。

过采样[3]、[4]

这种方法为少数民族班级复制一个样本以平衡班级。这里我们逐步讨论如何实现过采样。

步骤 1 :识别数据中优势类的频率(frequency _ of _ majority _ class)。

第二步:将数据集一分为二(数据集包含优势类(DF_FOR_MAJORITY)和次要类(DF_FOR_MINORITY))。

第三步:获取辅修课列表。

步骤 4 :使用重采样方法复制少数类样本。

这里我们使用n _ samples=frequency _ of _ majority _ class来指定要生成的样本数。

步骤 5 :连接 DF_FOR_MAJORITY 和过采样数据(DF_FOR_MINORITY_OVERSAMPLED)。

下表是由于过采样而产生的输出。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Balanced classes — Image by Author

欠采样[3]

这种方法移除多数类的样本,并尝试与少数类的样本数量相匹配。

不同之处在于,在步骤 1 中,考虑次要类别的频率,而不是识别主要类别的频率。

注意: 这种方法通常不被推荐,因为它会丢失大多数类的有用行为。

最后的想法

我们讨论了解决不平衡数据问题的三种方法,根据问题的背景,我们应该选择正确的方法。它确保高级分析解决方案是有意义的,而不是有偏见的特定行为。

参考

[1] N. V. Chawla,K. W .鲍耶,L. O.Hall,W. P. Kegelmeyer,“SMOTE:合成少数过采样技术”,人工智能研究杂志,321–357,2002 年。

[2]勒迈特 g .,诺盖拉 f .,和阿迪达斯 C. (2016)。“不平衡学习:一个 Python 工具箱来解决机器学习中不平衡数据集的诅咒”。更正 abs/1609.06570。

[3]m .库巴特和 s .马特温(1997 年)。解决不平衡训练集的诅咒:单边选择。收录于:第十四届机器学习国际会议论文集,第 179-186 页,田纳西州纳什维尔。摩根·考夫曼。

[4]凌和李(1998 年)。直接营销中的数据挖掘问题及解决方案。《第四届知识发现和数据挖掘国际会议(KDD-98)会议录》。AAAI 出版社。

机器学习中不平衡数据集的处理

原文:https://towardsdatascience.com/handling-imbalanced-datasets-in-machine-learning-7a0e84220f28?source=collection_archive---------0-----------------------

入门

面对不平衡的班级问题,应该做什么,不应该做什么?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

本帖与 约瑟夫·罗卡 共同撰写。

介绍

假设你在一家特定的公司工作,你被要求创建一个模型,根据你所掌握的各种测量结果,预测一个产品是否有缺陷。你决定使用你最喜欢的分类器,对数据进行训练,瞧,你得到了 96.2%的准确率!你的老板很惊讶,决定使用你的模型,不做任何进一步的测试。几周后,他走进你的办公室,强调你的模型毫无用处。事实上,您创建的模型从用于生产时起就没有发现任何缺陷产品。
经过一些调查,您发现贵公司生产的产品中只有大约 3.8%有缺陷,而您的模型总是回答“无缺陷”,因此准确率为 96.2%。你得到的那种“幼稚”的结果是由于你正在处理的不平衡的数据集。本文的目标是回顾不同的方法,这些方法可以用来处理不平衡类的分类问题。

概述

首先,我们将概述不同的评估指标,这些指标有助于发现“幼稚行为”。然后,我们将讨论一大堆重新处理数据集的方法,并说明这些方法可能会产生误导。最后,我们将展示返工问题在大多数情况下是继续进行的最佳方式。

一些由(∞)符号表示的小节包含了更多的数学细节,可以跳过而不影响对这篇文章的整体理解。还要注意,在接下来的大部分内容中,我们将考虑两类分类问题,但是推理可以很容易地扩展到多类情况。

发现“幼稚行为”

在第一部分中,我们想提醒不同的方法来评估一个训练过的分类器,以确保检测到任何类型的“幼稚行为”。正如我们在简介示例中看到的,准确性虽然是一个重要且不可避免的指标,但可能会产生误导,因此应该谨慎使用,并与其他指标一起使用。让我们看看还可以使用哪些其他工具。

混淆矩阵、精确度、回忆和 F1

在处理分类问题时,一个好的简单的度量标准是混淆矩阵。这个指标给出了一个模型表现如何的有趣概述。因此,它是任何分类模型评估的一个很好的起点。我们在下图中总结了可以从混淆矩阵中得出的大多数指标

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The confusion matrix and the metrics that can be derived from it.

让我们简单描述一下这些指标。模型的准确性基本上是正确预测的总数除以预测的总数。类别的精度定义了当模型回答一个点属于该类别时结果的可信度。类别的召回表示模型能够多好地检测该类别。一个类的 F1 值由精度和召回率的调和平均值(2×精度×召回率/(精度+召回率))给出,它将一个类的精度和召回率结合在一个度量中。

对于给定的类,召回率和精确度的不同组合具有以下含义:

  • 高召回率+高精度:该类被模型完美处理
  • 低召回率+高精确度:模型不能很好地检测类,但是当它检测到类时是高度可信的
  • 高召回率+低精确度:该类被很好地检测到,但是该模型还包括其他类的点
  • 低召回率+低精确度:模型对类的处理很差

在我们的介绍性示例中,我们有以下 10000 种产品的混淆矩阵。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The confusion matrix of our introductory example. Notice that the “defective” precision can’t be computed.

如前所述,准确率为 96.2%。无缺陷类别精度为 96.2%,缺陷类别精度不可计算。无缺陷类别的召回率为 1.0,这是完美的(所有无缺陷产品都被贴上了这样的标签)。但是缺陷类的召回率是 0.0,这是最差的情况(没有检测到缺陷产品)。因此,我们可以得出结论,我们的模型对于这个类做得不好**。有缺陷产品的 F1 分数不可计算,无缺陷产品的 F1 分数为 0.981。在本例中,查看混淆矩阵可能会导致重新思考我们的模型或目标(正如我们将在以下部分看到的)。它可以避免使用无用的模型。**

ROC 和 AUROC

另一个有趣的度量是 ROC 曲线(代表接收器工作特性),它是根据给定的类别定义的(在下文中我们将表示为 C)。

假设对于给定的点 x,我们有一个模型,输出这个点属于 C 的概率:P(C | x)。基于这种概率,我们可以定义一个判定规则,即当且仅当 P(C | x)≥T 时,x 属于 C 类,其中 T 是定义我们的判定规则的给定阈值。如果 T=1,只有当模型 100%确信某个点属于 C 时,该点才被标记为属于 C。如果 T=0,每个点都被标记为属于 c。

阈值 T 的每个值产生一个点(假阳性、真阳性),然后,ROC 曲线是当 T 从 1 变化到 0 时产生的点的集合所描述的曲线。这条曲线从点(0,0)开始,到点(1,1)结束,并且是递增的。一个好的模型会有一条从 0 到 1 快速增加的曲线(意味着只需要牺牲一点点精度就可以获得高召回率)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Illustration of possible ROC curves depending on the effectiveness of the model. On the left, the model has to sacrifice a lot of precision to get a high recall. On the right, the model is highly effective: it can reach a high recall while keeping a high precision.

基于 ROC 曲线,我们可以建立另一个更容易使用的指标来评估模型:AUROC,即 ROC 曲线下的面积。AUROC 有点像总结整个 ROC 曲线的标量值。可以看出,AUROC 在最佳情况下趋向于 1.0,在最差情况下趋向于 0.5。
同样,好的 AUROC 分数意味着我们正在评估的模型不会牺牲很多精度来获得对观察类(通常是少数类)的良好召回。

真正的问题是什么?

在试图解决这个问题之前,让我们试着更好地理解它。为此,我们将考虑一个非常简单的例子,它将允许我们快速回顾两类分类的一些基本方面,并更好地掌握不平衡数据集的基本问题。这个例子也将在下面的章节中使用。

不平衡的例子

让我们假设我们有两个班级:C0 和 C1。来自 C0 类的点遵循均值为 0、方差为 4 的一维高斯分布。来自 C1 类的点遵循均值为 2、方差为 1 的一维高斯分布。假设在我们的问题中,C0 类代表了数据集的 90%(因此,C1 类代表了剩下的 10%)。在下图中,我们描绘了一个包含 50 个点的代表性数据集,以及两个类按正确比例的理论分布

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Illustration of our imbalanced example. Dotted lines represent the probability densities of each class independently. Solid lines also take into account the proportions.

在这个例子中,我们可以看到 C0 类的曲线总是在 C1 类的曲线之上,因此,对于任何给定点,该点从 C0 类中提取的概率总是大于从 C1 类中提取的概率。数学上,使用基本的贝叶斯规则,我们可以写

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以清楚地看到先验的影响,以及它如何导致一个类总是比另一个类更有可能的情况。

所有这些意味着,即使从完美的理论角度来看,我们知道,如果我们必须在这些数据上训练一个分类器,当总是回答 C0 时,分类器的准确性将是最大的。因此,如果目标是训练一个分类器以获得尽可能好的准确性,那么这不应该被视为一个问题,而只是一个事实:有了这些特征,我们能做的最好的事情(就准确性而言)就是总是回答 C0。我们必须接受它。

关于可分性

在给定的例子中,我们可以观察到这两个类是不可分的(它们彼此相距不远)。然而,我们可以注意到,面对不平衡的数据集并不一定意味着这两个类不能很好地分离,因此,分类器不能在少数类上做得很好。例如,假设我们仍然有两个类 C0 (90%)和 C1 (10%)。C0 的数据遵循均值为 0、方差为 4 的一维高斯分布,而 C1 的数据遵循均值为 10、方差为 1 的一维高斯分布。如果我们像以前一样绘制数据,那么我们有

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

In our Gaussian example, if the means are different enough with respect to the variances, even imbalanced classes can be well separable.

这里我们看到,与前一种情况相反,C0 曲线并不总是高于 C1 曲线,因此,有些点更可能来自 C1 类而不是 C0 类。在这种情况下,这两个类足够分开以补偿不平衡:一个分类器不一定总是回答 C0。

理论最小误差概率(∞)

最后,我们应该记住一个分类器有一个理论上最小的错误概率。对于这种分类器(一个特征,两个类别),我们可以提到,在图形上,理论上的最小误差概率是由两条曲线的最小值下面的面积给出的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Illustration of the theoretical minimal error for different degree of separability of two classes.

我们可以用数学方法恢复这种直觉。实际上,从理论的角度来看,最好的可能分类器将为每个点 x 选择两个类别中最可能的一个。这自然意味着,对于给定的点 x,最佳理论误差概率由这两类中可能性较小的一类给出

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

然后我们可以表示总的错误概率

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

该面积是上述两条曲线中最小值下方的面积。

返工数据集并不总是一个解决方案

首先,当面对不平衡的数据集时,第一个可能的反应是认为数据不代表现实:如果是这样,我们假设真实数据几乎是平衡的,但在收集的数据中存在比例偏差(例如,由于收集方法)。在这种情况下,尝试收集更具代表性的数据几乎是强制性的。现在,让我们看看,当数据集不平衡时,我们能做些什么,因为现实就是如此。在接下来的两节中,我们将介绍一些经常被提及的处理不平衡类和处理数据集本身的方法。特别是,我们讨论了与欠采样、过采样和生成合成数据相关的风险,以及获得更多功能的好处。

欠采样、过采样和生成合成数据

这些方法通常被认为是在数据集上安装分类器之前平衡数据集的好方法。简言之,这些方法对数据集的作用如下:

  • 欠采样包括从多数类中采样,以便只保留这些点的一部分
  • 过采样包括从少数类中复制一些点,以增加其基数
  • 生成合成数据包括从少数类创建新的合成点(例如参见 SMOTE 方法)以增加其基数

所有这些方法都旨在重新平衡(部分或全部)数据集。但是我们应该重新平衡数据集,使两个类的数据一样多吗?还是多数阶级应该保持最具代表性?如果是这样,我们应该以什么样的比例进行再平衡?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Illustration of the effect that different degrees of majority class undersampling have on the model decisions.

当使用重采样方法时(例如,从 C0 获得的数据与从 C1 获得的数据一样多),我们在训练期间向分类器显示了两个类别的错误比例。以这种方式学习的分类器在未来的真实测试数据上将比在未改变的数据集上训练的分类器具有更低的准确度。事实上,了解类的真实比例对于分类新点非常重要,而在对数据集进行重采样时,这些信息已经丢失。

因此,如果这些方法不能被完全拒绝,它们应该被谨慎地使用:如果有目的地选择新的比例(我们将在下一节看到这一点),它可以导致一个相关的方法,但是仅仅重新平衡类而不进一步考虑这个问题也可能是一个没有意义的事情。总结这一小节,让我们假设用类似重采样的方法修改数据集正在改变现实,因此需要小心并记住这对我们分类器的输出结果意味着什么。

获取附加功能

我们在上一小节中讨论了这样一个事实,即对训练数据集进行重采样(修改类的比例)是不是一个好主意取决于分类器的真正目的。我们特别看到,如果两个类别不平衡,不能很好地分离,并且我们的目标是一个尽可能准确的分类器,那么得到一个总是回答同一个类别的分类器不一定是一个问题,而只是一个事实:没有什么比这些变量更好的了。

但是,通过使用附加要素(或更多要素)丰富数据集,仍有可能获得更好的精度结果。让我们回到我们的第一个例子,在这个例子中,类是不可分的:也许我们可以找到一个新的附加特征来帮助区分这两个类,从而提高分类器的准确性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Looking for additional features can help separate two classes that were not initially separable.

与前一小节中提到的建议改变数据的真实性的方法相比,这种用更多来自现实的信息来丰富数据的方法是一个更好的想法。

返工问题更好

到目前为止,结论是相当令人失望的:如果数据集是真实数据的代表,如果我们不能获得任何额外的特征,如果我们的目标是具有最佳可能准确性的分类器,那么“幼稚行为”(回答总是相同的类)不一定是问题,应该被接受为事实(当然,如果幼稚行为不是由于所选分类器的有限能力)。

那么如果我们对这些结果仍然不满意呢?在这种情况下,这意味着,以这样或那样的方式,我们的问题没有被很好地陈述(否则我们应该照原样接受结果),并且我们应该重新工作以便获得更令人满意的结果。让我们看一个例子。

基于成本的分类

感觉得到的结果不好可能是因为目标函数没有被很好地定义。到目前为止,我们假设我们的目标是一个具有高精度的分类器,同时假设两种错误(“假阳性”和“假阴性”)具有相同的成本。在我们的例子中,这意味着我们假设当真实标签是 C1 时预测 C0 和当真实标签是 C0 时预测 C1 一样糟糕。误差是对称的。

让我们考虑一下有缺陷(C1)和无缺陷(C0)产品的介绍性例子。在这种情况下,我们可以想象,与错误地将一个没有缺陷的产品贴上有缺陷的标签(生产成本损失)相比,没有检测到一个有缺陷的产品会给公司带来更大的成本(客户服务成本,如果有危险的缺陷,可能的司法成本……)。现在,当真正的标签是 C1 时预测 C0 比当真正的标签是 C0 时预测 C1 要糟糕得多。误差不再是对称的。

然后更具体地考虑我们有以下成本:

  • 当真实标签为 C1 时预测 C0 的成本为 P01
  • 当真实标注为 C0 时,预测 C1 的成本为 P10(0 < P10 < < P01)

然后,我们可以重新定义我们的目标函数:我们不再以最佳精度为目标,而是寻找更低的预测成本。

理论最小成本(∞)

从理论的角度来看,我们不想最小化上面定义的误差概率,但是预期的预测成本由下式给出

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

其中 C(。)定义了分类器函数。因此,如果我们想要最小化预期的预测成本,理论上的最佳分类器 C(。)最小化

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

或者等价地,除以 x 的密度,C(。)最小化

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,利用这个目标函数,从理论角度来看,最佳分类器将是这样的:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

请注意,当成本相等时,我们恢复了“经典”分类器的表达式(侧重于准确性)。

概率阈值

在我们的分类器中考虑成本的第一种可能的方法是在训练之后进行。这个想法是,首先,训练一个分类器的基本方法是输出以下概率

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

无需承担任何成本。那么,预测的类将是 C0 if

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

C1 则不然。

在这里,我们使用哪个分类器并不重要,只要它输出给定点的每个类的概率。在我们的主要示例中,我们可以在数据上拟合一个贝叶斯分类器,然后我们可以重新加权获得的概率,以调整带有成本误差的分类器,如上所述。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Illustration of the probability threshold approach: the outputted probabilities are reweighted such that costs are taken into account in the final decision rule.

等级重新加权

类重新加权的思想是在分类器训练期间直接考虑成本误差的不对称性。这样做,每个类别的输出概率将已经嵌入成本误差信息,然后可以用于定义具有简单的 0.5 阈值的分类规则。

对于一些模型(例如神经网络分类器),在训练期间考虑成本可以在于调整目标函数。我们仍然希望我们的分类器输出

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

但这一次,它被训练成最小化以下成本函数

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对于其他一些模型(例如贝叶斯分类器),可以使用重采样方法来偏置类比例,以便在类比例中输入成本误差信息。如果我们考虑成本 P01 和 P10(这样 P01 > P10),我们可以:

  • 以因子 P01/P10 对少数民族类进行过采样(少数民族类的基数应乘以 P01/P10)
  • 通过因子 P10/P01 对多数类进行欠采样(多数类的基数应乘以 P10/P01)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Illustration of the class reweight approach: the majority class is undersampled with a proportion that is chosen carefully to introduce the cost information directly inside the class proportions.

外卖食品

这篇文章的主要观点是:

  • 无论何时使用机器学习算法,都必须谨慎地选择模型的评估指标:我们必须使用能够最好地概括我们的模型相对于我们的目标表现如何的指标
  • 当处理不平衡的数据集时,如果类与给定的变量不能很好地分离,并且如果我们的目标是获得尽可能好的准确性,那么最好的分类器可能是一个总是回答多数类的“幼稚”分类器
  • 可以使用重采样方法,但必须仔细考虑:它们不应该作为独立的解决方案使用,而是必须与问题的返工相结合,以达到特定的目标
  • 返工问题本身通常是解决不平衡类问题的最佳方式:分类器和决策规则必须根据精心选择的目标来设置,例如,最小化成本

我们应该注意到,我们根本没有讨论像“分层抽样”这样在批量训练分类器时有用的技术。当面临不平衡的类问题时,这种技术确保训练期间更大的稳定性(通过消除批内的比例差异)。

最后,姑且说这篇文章的主要关键词是“目标”。准确地知道您想要获得什么将有助于克服不平衡的数据集问题,并确保获得最佳结果。完美地定义目标应该永远是要做的第一件事,也是为了创建机器学习模型而必须做的任何选择的起点。

感谢阅读!

与约瑟夫·罗卡一起写的最后一篇文章:

[## 理解生成敌对网络(GANs)

一步一步地建立导致 GANs 的推理。

towardsdatascience.com](/understanding-generative-adversarial-networks-gans-cd6e4651a29)

处理杂乱的 CSV 文件

原文:https://towardsdatascience.com/handling-messy-csv-files-2ef829aa441d?source=collection_archive---------14-----------------------

为什么它们是一个问题以及如何解决

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by Jan Kolar on Unsplash.

如果你是一名工作数据科学家,CSV 文件很可能是你的面包和黄油。它们对于人类和计算机来说都很容易阅读,可以在版本控制中被跟踪,并且可以很容易地通过电子邮件发送和压缩!但是,如果您已经工作了一段时间,您可能也熟悉 CSV 文件的黑暗面:不常见的单元格分隔符、不均匀的行长度、双引号、转义字符、注释行等等!😱

这个问题有一个非常简单的原因:“逗号分隔值”文件不是一种标准的文件格式,但实际上更像是一种人们尚未完全同意的约定。因此,您可能在互联网上遇到的 CSV 文件格式的变体数量是巨大的!作为示例,以下是一些真实世界的 CSV 文件示例:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Examples of real-world CSV files: (a) includes comment lines at the top, prefixed with the # character, which is not part of the CSV “standard”, (b) uses the ^ symbol as delimiter and the ~ symbol for quotes, and (c) uses the semicolon as delimiter, but yields the exact same number of columns when using the comma.²

为什么这是个问题?为什么我们关心 CSV 文件以不同的格式出现?在保存表格数据时,这难道不是一种表达个性的绝妙方式吗?嗯……不是。CSV 文件是用来存储数据的,所以应该很容易从中加载数据。通过改变所使用的格式,CSV 文件在加载之前需要人工检查。

这是后一点的一个例子。Kaggle上的这个数据集包含了从 IMDB 检索到的 14762 部电影的信息。假设我们希望将这些数据加载到 Python 中,并希望使用 Pandas 将其加载到一个漂亮的数据框中:

>>> import pandas as pd
>>> df = pd.read_csv('./imdb.csv')
Traceback (most recent call last):
# ... skipping the full traceback ... 
pandas.errors.ParserError: Error tokenizing data. C error: Expected 44 fields in line 66, saw 46

哼,那没用。如果我们使用检测格式的标准方法,也称为方言,并按照Python 标准 csv 库文档的建议加载文件,会怎么样?

>>> import csv 
>>> with open('./imdb.csv', newline='') as csvfile: 
... dialect = csv.Sniffer().sniff(csvfile.read())
... csvfile.seek(0)
... reader = csv.reader(csvfile, dialect)
... rows = list(reader)
>>> len(rows)
13928

好的,这确实做了一些事情,但是最终读取了 13,928 行,而不是我们预期的 14,762 行!这是怎么回事??

事实证明,当电影标题包含逗号时,这个特殊的 CSV 文件使用了转义字符(\)。Pandas 和标准的csv库都没有自动检测到这一点,因此无法正确加载数据。想象一下,如果你开始分析这些数据,而没有意识到这种情况发生了!🙈

当然,您可以手动检查您在 web 上遇到的每个 CSV 文件,并确保它没有任何问题。但是都 2019 年了,为什么还要处理乱七八糟的 CSV 文件?为什么这些包不能正确检测方言?这很困难的一个原因是,CSV 文件有太多的变体。另一个原因是,要想出一个能一直正确完成的算法实际上并不容易,因为任何方言都会给你一些表,但应该只有一个表正确反映存储的数据。

CSV 是一个教科书式的例子,说明了如何而不是设计文本文件格式。
—Unix 编程的艺术(Raymond,2003)

谢天谢地,现在有一个解决方案: CleverCSV ,一个用于高精度检测 CSV 文件方言的 Python 包。它是以人类确定方言的方式为模型的:通过寻找导致单元格中有“干净数据”的规则表格结构的模式(如数字、日期等)。).CleverCSV 实际上是基于研究,在那里我们调查了近 10,000 个 CSV 文件,以开发检测 CSV 方言的最佳方法。为了便于将现有代码切换到 CleverCSV,该包被设计为 CSV 模块的直接替代品。所以不用import csv,你可以用import clevercsv(或者,如果你真的很聪明的话:import clevercsv as csv)。

但是等等,还有呢!当然,您不希望一遍又一遍地检测同一个文件的方言,因为它不太可能经常改变。因此,CleverCSV 还提供了一个命令行界面,它只给你你 need:⁴的代码

$ clevercsv code ./imdb.csv# Code generated with CleverCSV version 0.4.7import clevercsvwith open(“imdb.csv”, “r”, newline=””, encoding=”utf-8") as fp:
    reader = clevercsv.reader(fp, delimiter=”,”, quotechar=””, escapechar=”\\”)
    rows = list(reader)

CleverCSV 还附带了一些常用功能的包装器,比如read_csv用于检测方言并将文件加载为列表列表,以及csv2df用于将文件加载到 Pandas 数据框中。GitHub 和 PyPI 上的提供了 CleverCSV。此外,导致 CleverCSV 的研究是完全可重复的,并且是公开的(如果你关心这样的事情!😃)

数据争论和数据清理是数据科学家最耗时的任务,也不是最有趣的。事实上,的调查显示数据科学家将大部分时间花在这些琐碎的任务上,同时也是他们最不喜欢的工作部分!CleverCSV 是一个旨在部分解决这一问题的工具,它为数据科学家提供了一种从杂乱的 CSV 文件中正确加载数据的枯燥任务,从而节省了时间。我希望你试一试!

脚注

  1. RFC4180 提出了一个定义,但这不是正式意义上的标准,库和编程语言并不总是遵循它。
  2. 图片改编自:G.J.J. van den Burg,A. Nazábal,C. Sutton,通过检测行和类型模式来处理杂乱的 CSV 文件 (2019),数据挖掘与知识发现 33:(6)。
  3. 相比之下,R 的read.csv()方法也好不到哪里去,最终读取了 15,190 行!
  4. 如果你更喜欢熊猫数据框,只需使用:clevercsv code -p <filename>

原载于 2019 年 11 月 26 日https://gertjanvandenburg.com

处理文本数据质量问题:你能处理吗?

原文:https://towardsdatascience.com/handling-text-data-quality-issues-can-yuo-h-andle-thsi-7f77dac3ddff?source=collection_archive---------34-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by Marija Zaric on Unsplash

介绍

退一步说,过去的几周很有趣。我收到了一个非常有趣的问题,任务是解决一些文本数据质量问题。

正如您可能已经从标题中猜到的那样,有两个主要问题需要解决。

  1. 拼写错误的检测和处理
  2. 单词之间的随机空格

你能处理这个吗?

作为人类,我们可以很容易地将上面的陈述理解为“你能处理这个吗?”然而,创建一种方法来解决这样的数据问题被证明是具有挑战性的。

如果你像我一样,你可能会说“哦,这不应该是坏!”

哦…我错了。

永远不要低估会突然出现并导致大量问题的复杂性。

无论如何,在本文中,我将介绍我用来解决这个问题的方法。这可能不是最好的方法,但似乎对我来说已经足够了。

我们开始吧!😃

数据质量问题 1:拼写错误的检测和处理

在我上一篇关于处理拼写错误的文章中,我使用了单词向量并做了大量的翻译来形成一个广义翻译向量

这是一种处理单词向量拼写错误的新方法。

然而,对于这种情况,我决定求助于一种更简单的方法来处理拼写错误。

这包括两个部分:

  1. 检测拼写错误的单词
  2. 处理拼写错误的单词

我使用了 SAS Viya 的开箱即用的拼错动作 tpSpell 来做到这一点。

SAS 与称为动作集的东西一起工作,动作集与 Python 库同义。在每个动作集中,有许多可以执行的动作

第 1 部分:检测拼写错误的单词

在这个步骤中,tpSpell 动作执行所谓的候选提取。

候选提取将单词分成两类;拼写正确的候选单词和拼写错误的候选单词。

在运行该过程之前,由预设参数确定拼写正确的候选单词。此参数指定一个术语要被视为拼写正确的候选单词,必须出现在多少个文档中。

例如,参考下面的图 1:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 1 — Correctly-spelled candidate example (source)

潜在的候选名称是“Term”、“Role”和“Parent”列的串联,形成“algorithms-N-algorithm”。

如果潜在候选名称“algorithms-N-algorithm”在 4 个文档中出现 5 次,那么它出现的文档的数量等于 4。

如果名为“最小亲本”的预设参数被设置为 3,因为 4 比 3 大,所以单词“algorithms-N-algorithm”被添加到拼写正确的候选列表中。

跟了这么远?😃

那么,拼写错误的候选单词列表呢?我们如何创建这个表?

就像拼写正确的单词候选列表一样,还有另一个预设参数。这次叫“最大子女”。

例如,请看下面的图 2:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 2 — Misspelled candidate example (source)

如果“最大子代”参数被设置为 3 并且“algoxxxthzs-N-algoxxxthzs”出现的文档数小于 3,那么“algoxxxthzs-N-algoxxxthzs”被添加到拼写错误的候选单词列表中。

注:如果符合上述参数,潜在候选人可以同时出现在两个候选人列表中。

现在我们已经有了一个拼写正确的候选列表和一个拼写错误的候选列表,接下来是如何将拼写正确的单词分配给它们各自的拼写错误。

第 2 部分:处理拼写错误

在该步骤中,执行候选比较

tpSpell 动作现在将检查拼写错误列表中的所有单词和拼写正确列表中的所有单词。

它将拼写错误的候选单词与每个拼写正确的候选单词进行比较,并计算它们之间的距离。

另一个预置参数确定拼写错误的单词是否有正确的拼写。例如,如果单词“algoxxxthzs”是给定的拼写错误的单词并且单词“算法”是正确拼写的候选单词,那么计算出的“algoxxxthzs”和“算法”之间的距离将是 50。

如果名为“最大法术距离”的预设参数设置为 20。由于 50 大于 20,正确拼写的候选“算法”现在被认为是单词“algoxxxthzs”的正确拼写。

您还可以在这里设置其他高级参数,以考虑多个术语,如“消防车”、“继续”或“加入”等。你可以在这里阅读文档

虽然我在上面说得听起来很复杂…

不是。😃

下面是如何运行以上所有操作的方法。

proc cas;
 textParse.tpSpell /
  table={name="pos", caslib="public"}
  minParents=3
  maxChildren=6
  maxSpellDist=15
  casOut={name="tpSpell_Out", replace=true};
   run;
quit;

结果将是这样的:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 3 — Misspelling Handling Results

数据质量问题 2:随机空白

这个问题确实让我测试了多种方法。所有这些都表现不好,除了我现在要讲的方法。

为了恢复你的记忆,你的问题就像这样。

当我收到这样一个数据集时,我的第一个想法是“这个文本怎么会变成这样……”

但是,嘿,这是真实的世界。不是我们都熟悉的 kaggle 数据集或我们的研究数据集。

不过,上面有几个数据质量问题值得指出:

  1. 单词之间的空格,如“probl em”
  2. 缺少字符,即“锁定”
  3. 交换字符,即“thsi”
  4. 双重字符,即“你”
  5. 问题的组合,即“m emry”

我必须找到一种方法来解决单词之间的空格,同时解决拼写错误(数字 2、3 和 4)。

令人欣慰的是,处理拼写错误可以相对容易地解决,就像我上面展示的那样。

但是由于问题的组合,复杂性在于如何最好地处理(第五点)。在思考这项工作的最佳解决方案时,我从编码器-解码器网络的工作方式中获得了灵感。

我需要一种方法来“编码”句子,然后“解码”它们。

显然我在这里用词不当。我说的“编码”实际上是指删除单词之间的所有空白。像这样:

torefreshyyourmerytheeplokeddlikethsi

通过“解码”,我的意思是在解决拼写错误后,将单词重新分解成它们各自的单词:

提醒你一下,这个问题看起来是这样的

解码的单词基于单词的最长匹配。例如,如果我看到单词“helloworld ”,我将首先在潜在候选列表中查找单词“hell”。

直到下一个字符“o”出现,由于“hell+o”=“hello”,“hello”成为更好的潜在候选。我放弃了“地狱”这个词作为主要候选词,保留了“你好”这个词。

当读入下一个字符时,“hello+w”=“hellow”,因为“hellow”在潜在候选列表中不是一个合适的单词,所以最好的候选词是“hello”。

然后,我在“hello”和“world”之间添加一个空格,形成“hello world”。

那么这个潜在候选名单是什么呢?

这是我从前面的 tpSpell 动作生成的正确拼写单词列表中挑选出来的列表。

潜在候选人名单的管理

创建这个列表的诀窍在于我如何进行标记化。

是的,有现成的标记化方法,但没有一种方法会进行拼写错误检查,因为它同时执行标记化。

简单地说,我做了一个标记器,在解析标记时执行拼写错误的解析。

比如“hello wrld”会解析为“hello”+“world”。拼写错误的“wrld”将自动解析为“world ”,这是基于我用前面提到的 tpSpell 操作创建的一个拼写错误列表。

除了拼写错误的解决方案,我还删除了停用词,所有形式的标点符号,小写所有单词,只保留父术语来形成列表。

为什么?

因为最重要的总是那些小事情。

此处删除停用词是合理的,因为:

  1. 下游自然语言处理(NLP)任务是一个信息检索任务。即文档相似性。在文档相似性任务中,保持语义相关信息比语法相关信息更重要。因此我认为,删除停用词不会对我的下游任务产生太大影响。
  2. 如果不删除停止字,它将干扰我的“解码器”的工作。以这句话为例:

“丛书”->“丛书”

考虑停用词会将第一个词解析为“这些”而不是“该”。此外,因为“ries”不再是一个单词,所以算法将跳过这个输出,并将句子解码为“these of books”,这是不正确的。

“这些书”->“这些书”

影响“解码器”工作方式的另一个复杂因素如下所示:

“自行车运动”→“自行车运动”→“自行车港口”(错误)

为了解决这个问题,我只在候选列表中保留了父术语。即自行车 s →自行车。通过只查看父术语,我的输出如下所示:

“自行车运动”→“自行车运动”→“自行车运动”(正确)

还有一些其他的小因素也在起作用,但是你会明白的;最重要的是小事。

所以你有它!

我自己的“编码器-解码器”方法来解决这个文本数据质量问题!😃

很明显,我已经简化了整个方法,以便给你一个解决方案如何工作的要点。

正如我以前说过的,这不是一个完美的解决方案,但它确实足以让我开始我的下游 NLP 任务。

亲眼目睹这一切!

为了向您展示运行代码后的样子,我针对一个存在数据质量问题的图书评论语料库运行了代码。

下面是示例和流程的样子:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 4 — Data Cleaning Process in Action

在我看来还不算太寒酸。😃

结尾注释

嗯,那就这样吧!

我希望你觉得这篇文章很有见地:)

尽管花了将近 2 周的时间尝试了许多方法,但我从中获得了巨大的乐趣!

如果你有更好的方法,请不吝赐教!很想听听!

下一个帖子可能会是这个的续集。这将是关于文件的相似性,以及我如何应用平滑逆频率和共同组成部分删除,以获得一个体面的结果。

下次见,再见!

领英简介:谭震霆

在数据科学算法面试中处理树

原文:https://towardsdatascience.com/handling-trees-in-data-science-algorithmic-interview-ea14dd1b6236?source=collection_archive---------28-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Image by Johannes Plenio from Pixabay

算法面试

不是那种树桩

算法和数据结构是数据科学不可或缺的一部分。虽然我们大多数数据科学家在学习时都没有上过适当的算法课程,但它们仍然至关重要。

许多公司在招聘数据科学家的面试过程中会询问数据结构和算法。

现在,许多人在这里问的问题是,问一个数据科学家这样的问题有什么用。 我喜欢这样描述,一个数据结构问题可以被认为是一个编码能力测试。

我们都在人生的不同阶段进行过能力倾向测试,虽然它们不是判断一个人的完美代理,但几乎没有什么是真的。那么,为什么没有一个标准的算法测试来判断人的编码能力。

但我们不要自欺欺人,他们需要像你的数据科学面试一样的热情,因此,你可能需要花一些时间来研究算法和数据结构问题。

这篇文章是关于快速跟踪这项研究,并为数据科学家解释树的概念,以便你下次在面试中被问到这些问题时轻松通过。

但是首先,为什么树对数据科学很重要?

对于数据科学家来说,树和软件工程师有着不同的含义。

对于软件工程师来说,树只是一种简单的数据结构,他们可以用它来管理层次关系,而对于数据科学家来说,树是一些最有用的分类和回归算法的基础。

那么这两个在哪里见面呢?

它们必然是同一件事。不要惊讶。下面是数据科学家和软件工程师对树的看法。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

They are essentially the same

唯一的区别是数据科学树节点保存了更多的信息,帮助我们确定如何遍历树。例如,在用于预测的数据科学树的情况下,我们将查看节点中的要素,并根据分割值确定我们要移动的方向。

如果你想从头开始写你的决策树,你可能也需要从软件工程的角度理解树是如何工作的。

树的类型:

在这篇文章中,我将只讨论在数据科学面试问题中经常被问到的两种树。二叉树(BT)和称为二分搜索法树(BST)的二叉树的扩展。

1.二叉树:

二叉树是一种简单的树,其中每个节点最多有两个子节点。决策树是我们在日常生活中看到的一个例子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Binary Tree: Each Node has up to 2 children

2.二叉查找树(英国夏令时):

二叉查找树是一棵二叉树,其中:

  • 一个节点的所有左后代都小于或等于该节点,并且
  • 该节点的所有右后代都大于该节点。

谈到平等,这个定义有各种不同的说法。有时等式在右边或两边。有时树中只允许不同的值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Source

8 大于左侧子树中的所有元素,小于右侧子树中的所有元素。这同样适用于树中的任何节点。

创建简单的树:

那么我们如何构造一棵简单的树呢?

根据定义,树是由节点组成的。所以我们从定义用于创建节点的node类开始。我们的节点类非常简单,因为它保存了节点的值、左侧子节点的位置和右侧子节点的位置。

class node:
    def __init__(self,val):
        self.val = val
        self.left = None
        self.right = None

我们现在可以创建一个简单的树,如下所示:

root = node(1)
root.left = node(2)
root.right = node(3)

现在我注意到,如果我们自己不做一些编码,我们就不能真正掌握基于树的问题。

因此,让我们更深入地了解一下代码部分,我发现一些关于树的最有趣的问题。

有序树遍历:

有很多种方法可以遍历一棵树,但是我发现顺序遍历是最直观的。

当我们在二叉查找树的根节点上进行有序遍历时,它以升序访问/打印节点。

def inorder(node):
    if node:
        inorder(node.left)
        print(node.val)
        inorder(node.right)

上述方法非常重要,因为它允许我们访问所有节点。

因此,如果我们想在任何二叉树中搜索一个节点,我们可以尝试使用 inorder 树遍历。

从排序后的数组创建二叉查找树

如果我们需要像上面那样手动创建一棵树,我们会是什么样的编码人员呢?

那么我们能从一个唯一元素的有序数组中创建一个 BST 吗?

def create_bst(array,min_index,max_index):
    if max_index<min_index:
        return None
    mid = int((min_index+max_index)/2)
    root = node(array[mid])
    leftbst = create_bst(array,min_index,mid-1)
    rightbst = create_bst(array,mid+1,max_index)
    root.left = leftbst
    root.right = rightbst
    return roota = [2,4,5,6,7]
root = create_bst(a,0,len(a)-1)

树本质上是递归的,所以我们在这里使用递归。我们取数组的中间元素,并将其指定为节点。然后,我们将create_bst函数应用到数组的左边部分,并将其分配给node.left,对数组的右边部分做同样的操作。

我们得到了 BST。

我们做得对吗?我们可以通过创建 BST 然后进行有序遍历来检查它。

inorder(root)
------------------------------------------------------------
2
4
5
6
7

好像没错!

让我们检查一下我们的树是否是有效的 BST

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Think Recursion!!!

但是,如果我们需要打印所有的元素并手动检查 BST 属性是否得到满足,那么我们是什么样的编码人员呢?

这里有一个简单的代码来检查我们的 BST 是否有效。我们假设在我们的二叉查找树中存在严格的不平等。

def isValidBST(node, minval, maxval):
    if node:
        # Base case
        if node.val<=minval or node.val>=maxval:
            return False
        # Check the subtrees changing the min and max values
        return isValidBST(node.left,minval,node.val) &    isValidBST(node.right,node.val,maxval)
    return TrueisValidBST(root,-float('inf'),float('inf'))
--------------------------------------------------------------
True

我们递归地检查子树是否满足二叉查找树性质。在每次递归调用时,我们改变调用的minvalmaxval,为函数提供子树的允许值范围。

结论

在这篇帖子里,我从软件工程的角度谈论了树。如果你想从数据科学的角度看树,你可以看看这篇文章。

[## 3 个决策树分裂标准背后的简单数学

🌀理解分割标准

towardsdatascience.com](/the-simple-math-behind-3-decision-tree-splitting-criterions-85d4de2a75fe)

树构成了数据科学算法面试中一些最常见问题的基础。我过去常常对这种基于树的问题感到绝望,但现在我已经开始喜欢其中涉及的精神锻炼。我喜欢这类问题中的递归结构。

虽然您可以在不学习它们的情况下在数据科学中走得更远,但您可以为了一点乐趣而学习它们,也许是为了提高您的编程技能。

这是给你的一个小笔记本,我把所有这些小概念都放在这里,让你尝试和运行。

看看我在算法面试系列的其他帖子,如果你想了解递归动态规划或者链表

继续学习

如果你想阅读更多关于算法和数据结构的内容,我强烈推荐 UCSanDiego在 Coursera 上的 算法专门化。

谢谢你的阅读。将来我也会写更多初学者友好的帖子。在 媒体 关注我或者订阅我的 博客 了解他们。一如既往,我欢迎反馈和建设性的批评,可以通过 Twitter @mlwhiz 联系。

此外,一个小小的免责声明——这篇文章中可能会有一些相关资源的附属链接,因为分享知识从来都不是一个坏主意。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值