TowardsDataScience-博客中文翻译-2019-二十五-

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

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

用 Kotlin 元编程探索神经网络:深度学习的更廉价选择?

原文:https://towardsdatascience.com/exploring-neural-networking-with-kotlin-metaprogramming-a-cheaper-alternative-for-deep-learning-99c416eb7d16?source=collection_archive---------21-----------------------

使用抽象组合契约改进传统机器学习模型的案例研究

在科特林公司的会议上,我就龙卷风-套件进行了发言,这是一个几乎无法实现的开源项目,旨在允许用户将龙卷风项目的源代码上传到应用程序中,应用程序将解析和分析随后的代码以生成简单的用户界面测试。

我刚刚在科特林会议上透露了初步的发现和建议,但是在我的神经紧张和等待公开分享这些发现之间,我很确定我只是兴奋地吐出了一大堆单词。

我希望有机会跟进并放慢速度,以便真正强调我们在使用 TornadoFX-Suite 时偶然发现的东西的重要性。

Most recent branch: https://github.com/ahinchman1/TornadoFX-Suite/tree/mapping-nodes-to-functions

龙卷风套件:通过分析外部代码生成用户界面测试

到目前为止,我们已经成功地创建了一个元编程定位机制,该机制将递归读取从 KastreeGson 解析的抽象语法树(AST ),定位相关的节点层次结构,并保存用户交互的相关控件(文本字段、按钮等)的结果,并从中生成测试。

什么是元编程定位器机制?它是如何工作的?

元编程定位机制是我命名的术语,用于描述 TornadoFX-Suite 使用 AST 解析分析外部代码的能力,以检测所述代码中的特定问题。

在我们的例子中,我们的元编程定位机制负责在视图中定位用户界面节点。一旦 AST 解析识别出一个项目中的一个视图,这些节点就会出现在一个有向图中,或者一个Digraph中,以保留一个计算机程序很容易理解的层次结构。

Sample TornadoFX view code (left) and the corresponding digraph (right) AST parsing builds out.

有了龙卷风配置指示我们可能关心的元素(按钮、文本字段、表单等),我们可以从保存的用户界面层次结构中拉出我们认为用户可能与之交互的节点。

现有人工智能定位器机制的缺点

生成 UI 测试的想法远非原创。有许多现有的产品是由来自 test.aiTestComplete 这样的地方的大型代码库和团队组成的,它们通过以多种方式解决案例中的问题来工作。同样,也有学术论文旨在检测 Android 应用程序中的 UI 元素,更多的是在机器学习中使用图像分类。

不幸的是,当前的人工智能图像分类定位器机制受到部分视图渲染识别以及检测通常不考虑用户输入的自定义对象交互的影响,正如在关于使用神经网络创建 GUI 骨架的研究中所展示的

此外,许多基于人工智能的测试解决方案在资源和处理能力方面极其密集,这些资源和处理能力是由幕后存在的算法计算的。这个算法会消耗数据。往往是很多很多的数据。

出于这些原因,我认为元编程定位器机制本质上比他们的“基于训练的”定位器机制要好。

好的,我们检测到了这些视图。可以做什么样的测试?

目前,buttonstextfields生成的测试如下:

**@Test**
fun testwuwp0yiTextField() {
    assertNotNull(wuwp0yiTextField.text)
    assertTrue(!wuwp0yiTextField.text.isNullOrEmpty())
    clickOn(wuwp0yiTextField).write("Something")
    assertEquals("Something", wuwp0yiTextField.text)
}**@Test** fun testvoVuVUjButton() {
    clickOn(voVuVUjButton)
}

他们并不复杂。目前,对这些生成的测试所能做的最好的事情就是与这些单独的节点以及分组节点的排列进行交互。

走到这一步本身就是一个壮举,但是产生的测试充其量也只是愚蠢的。UI 测试是这样的:

Sure, we’ve grabbed a view and we perform an action on that view. But what about the last step?

我需要更智能的测试。我研究了为 TornadoFX-Suite 实现 Tensorflow 的可能性,但是在深入研究机器学习之后,我意识到我现在必须把机器学习放在一边。

传统机器学习模型的缺点

与测试相关的人工智能技巧是围绕理解错误理论而发展的;理解当我们处理空间和时间的复杂性时,我们是如何以及在哪里犯认知错误的。

但是 AI 并不聪明。事实上,深度学习中仍然存在很多人类因素。

在我对机器学习和计算思维的研究中,杰夫·尼曼在测试和机器学习方面的广泛工作和研究被证明是改变这个项目方向的更有影响力的作品之一,因为我不得不暂时搁置应用机器学习的想法。

这是我抛开机器学习的原因吗?

  1. 在某些方面,overfitting / underfitting的概念只是我们想出来的一个术语,本质上是“机器学习实际上不能处理很多层/每层的张量”的翻译。当您的特征集严重不足或者当您的数据“太”嘈杂时,偏见可能会悄然而至。(“太吵”意味着它的证明价值被它的偏见价值压倒了。)
  2. 如果正则化不充分或太强,这可能是有害的。在这种情况下,“太强”意味着你的训练数据中的任何复杂性都会被扣分;因此,为了避免overfitting,你应用了太多的数据正则化,这就是引入偏差的原因。

杰夫·尼曼在他的 Pacument 项目中展示了这种偏差-方差的区别,在该项目中,Pacman 展示了神经网络的权重如何被“调整”到足以提供一个偏向于一个方向而不是另一个方向的偏差因子。

https://github.com/jeffnyman/pacumen

因此,以《吃豆人》为例,尼曼表明,对能量丸的偏好会导致吃豆人到处追逐鬼魂,试图杀死它们,代价是完成游戏并获胜。

在这里,我们进入了以短期回报为代价的长期回报的概念,这是影响我们今天看到的许多机器学习的因素。

机器学习的传统模型似乎有严重的局限性,尤其是在现实生活问题中。我脑海中不断闪现的问题是“我们如何知道什么是相关的,什么是不相关的?”

我不知道什么是好的 UI 测试。我不知道是什么导致了糟糕的 UI 测试。当我不知道什么是 UI 测试的相关和不相关因素时,我如何知道我应该考虑什么权重,以及我如何能够计算偏差?

如果我不知道哪些因素是相关的或不相关的,运行这些模型所需的资源成本可能会变得很高,并且损失函数变得很难尝试和最小化。我只是没有资源或专业知识来独自处理这件事…但也许,只是也许,传统的机器学习模型不适合这个项目。

转向元编程来创建神经网络关系

我们可以抓取这些节点并生成可以在运行时附加的随机 id,但是我们如何知道一个节点看起来是否像它应该的样子呢?这是一个很难回答的问题。我们需要很多交织的信息来回答这样的问题。

让我们重新审视一下短期奖励与长期奖励的对比,短期奖励通常是在机器学习中实现的。

机器学习是在预定义的可能性空间内搜索一些输入数据的有用表示,使用来自反馈的指导,告诉我们这些表示有多好或多坏。

Machine Learning is so “smart” it can be explained with Family Guy

深度学习模型是由不同层组成的引擎,我们可以将其视为数据处理单元。每个单元都包含数据过滤器。这些单元一个接一个地堆叠起来。

重要的是模型通过数据堆栈传递输入。神经网络本身被称为模型,但也可以被认为是算法。

从概念上讲,神经网络正在搜索成形数据的转换,并试图将其展平成可以处理的东西。

本质上,我们已经通过 AST 解析实现了数据的扁平化,并且将这些字面上的三维形状分解成类分解,这使得我们很容易在解析后进行有组织的分析。元编程如何将这种扁平化的数据提供给更具体的层?

我们点击一个按钮。接下来会发生什么?嗯,那要看情况。在这个例子中,编辑器对话框中的保存button直到模型被弄脏后才被激活(修改了textfields的内容)。

所以我们现在甚至不能点击我们的按钮!我们如何预测我们与节点交互的能力?这个问题可以比作复杂的游戏系统,如 Skyrim 或 Mass Effect。如果你与一个群体结盟,你就不能加入另一个群体(同样,如果你选择成为吸血鬼,你也不能成为狼人)。这种形式规范系统也称为有限状态机,这是一种通用的 ui 形式化策略。

形式化 UI 系统规范的一个好处是,正式的思想和清晰的结构使我们能够创建一个元编程可以使用的系统。

正式规范的另一个更明显的好处是允许我们检查 UI 节点的属性;是否有任何转换未明确说明?有可能被锁在互动之外吗?

You can reference a Kotlin FSM implementation of this problem in this blurb.

更重要的是,元编程如何创建自己的有限自动机来预测与模型绑定时的状态,如按钮启用和禁用?

在每个 UI 节点中,都有可能存储以下信息:

UINode(
    **nodeType**: String,
    **uiLevel**: Int,
    **nodeChildren:** JsonObject,
    **valueAssignment**: String,
    **functionComposition:** Digraph<UIFunctionComposition>,
    **associatedFunctions:** ArrayList<String>()
)

有了元编程,我意外地意识到— 也许我们不必猜测来自数据过滤器的结果的好坏。

在这种情况下,我们可以使用 TornadoFX-Suite 的类分解和定位的 UI 节点,并重新分析我们的代码分析以创建新的图形关系,这可以消除一些猜测,从而能够预测来自“黑盒”测试的行为。

也就是说,在创建 UINode 时,我们创建一个空的有向图来存储节点的functionCompositionassociateFunctions,这是从我们在 AST 解析中实现的初始类分解中检索的。

Will likely clean up this idea later the more I think about the definition of an associated function.

一个associatedFunction是一个负责影响节点属性或状态的函数。可以通过扫描具有值的节点来发现相关联的函数,这些值可以被识别为可能存在于任何其他类中的任何View类型成员的成员变量。只要在函数中存在对分配的节点值的引用,该函数就有可能影响节点的状态。

functionalComposition可以被认为是被拒绝与特定节点交互的功能。幸运的是,我们可以重用用于创建 UI 层次结构的有向图结构。

更重要的是,我们可以映射这些functionalCompositionassociatedFunctions,以便元编程创建 Kotlin 状态机来帮助预测与节点的某些交互的预期状态,这是元编程定位器机制的另一个版本(只是不太明显)。处理是密集的,但是当我们继续映射这些关系时,有更少但更多的相关信息可以处理,因为我们以非常非常愚蠢的方式创建了更聪明的关系。这个想法是下一阶段实施的重点。

深度学习算法是神经网络;元编程创建了神经网络

什么是神经网络?按照机器学习的说法,是算法。但是神经网络通过模拟大量类似神经元抽象版本的互连处理单元来工作。

在元编程中,神经网络是一个名词,而在机器学习中,神经网络是一个动作。在这两种情况下,都有 一组对事物的操作,随着神经网络的进化,更多的新操作被添加。然而,当那些操作是有限的,并且那些操作需要如何工作的定义不一定被编辑时,命令式世界工作得最好(因为试图通过调整权重来最小化损失函数往往发生在机器学习中)。

我提出的答案远没有传统机器学习模型通过隐藏层输入数据来提供的答案那么不透明。我提到人工智能并不聪明——嗯,元编程也不聪明。然而,在给定我们已经拥有的信息的情况下,使用元编程计算和创建进一步的图形关系所需的操作似乎成比例地和指数地减少,而机器学习似乎相反。

我真的觉得我找到了一些东西。

参考资料:

  • 从 UI 设计图像到 GUI 骨架:一个引导移动 GUI 实现的神经机器翻译器。2018 年 5 月 27 日,chunyang-chen.github.io/publication/ui2code.pdf. ICSE 18 日,Github
  • 尼曼杰夫。"揭开机器学习的神秘面纱,第二部分."来自一个软件测试人员的故事,2018 年 9 月 9 日,Tester Stories . com/2018/09/demysiting-machine-learning-part-2/。
  • 尼曼杰夫。“测试 AI …在用 AI 测试之前。”来自一个软件测试人员的故事,2018 年 5 月 30 日,Tester Stories . com/2018/05/testing-ai-before-testing-with-ai/。
  • 韦恩,希勒尔。"正式指定用户界面."希勒尔·韦恩,2018 年 6 月 11 日www.hillelwayne.com/post/formally-specifying-uis/.

探索纽约市水箱检查数据。

原文:https://towardsdatascience.com/exploring-new-york-city-water-tank-inspection-data-855c5776c566?source=collection_archive---------20-----------------------

我使用 Python 和 Plotly 探索、分析和可视化房地产数据的方法。

Typical water tank. Photo by author.

在过去的几年里,我一直在探索纽约市的房地产数据,并尝试各种数据分析和可视化方法。一路上,我学到了很多关于纽约市房地产数据的知识,尤其是如何获取、清理、分析和可视化/绘制这些数据。我花了大量时间学习数据和各种编程语言和技术,搜索 Stackoverflow 上的问答、关于媒体的文章和各种数据教程。我希望通过总结我一直在做的一个项目,并概述我探索数据驱动的房地产问题的过程、房地产数据的来源和数据分析工具,我将说明洞察力可以通过数据分析获得,并对那些探索自己的数据驱动问题的人有所帮助。

I spy over 12 water tanks in this photo. Photo by author.

我一直对建造水箱感兴趣。尽管科学有了很大的进步,但这些装着建筑物供水的木桶在上个世纪基本上没有变化。在纽约市,它们是维持超过 6 层的建筑的水压所必需的。

第一步——确定你的问题和数据来源

我很好奇水箱有多干净,是否有可识别的模式。通过一些研究,我了解到纽约市行政法规第 31 章于 2015 年 5 月生效,该法规要求业主对他们的水箱进行年度检查,并将结果报告给纽约市卫生局。第 31 章要求每栋建筑在下一年的 1 月 15 日前完成这项工作,并且所有的结果都要公之于众。纽约市卫生局有一个在线工具,可以查询纽约市任何一栋建筑的检查结果。

当城市数据在基于在线的工具中可用时,很有可能基础数据也可以通过纽约市开放数据门户获得(如果不能,就应该这样)。这是因为纽约市认识到数据是有价值的,应该让每个人都可以访问,并在 2012 年通过了当地法律 11,要求在 2018 年前识别和汇总全市数据。搜索这个庞大数据资源的入口是https://opendata.cityofnewyork.us/

Various ways to search for datasets on https://opendata.cityofnewyork.us/

只需简单搜索水箱,您就能快速找到我们正在寻找的数据,这些数据可以作为.csv文件下载。

步骤 2 —清理并浏览数据

我一般用 Python 分析数据。我喜欢 Python 的原因(也是它非常受欢迎的原因)是它在不断地被开发和添加新的库,有大量的学习资源,并且对于你试图完成的任何事情,都有机会在 StackOverflow 上有多个讨论来指导你。

如果你打算跟着做,我建议你安装Anaconda,它是一个非常流行的 Python 包,包括许多必须预装的 Python 库。一个这样的库是Jupyter,它允许你通过笔记本在浏览器中运行 Python 命令,这使得实验和与他人分享你的笔记本变得容易。与本文对应的 Jupyter 笔记本可以在这里通过 nb viewer查看,所有文件(包括底层数据)都可以在这个 GitHub 资源库中找到。

另一个流行的库(也是 Jupyter 包的一部分)是Pandas,它简化了组织、搜索和修改表格数据的方式。加载完库后,数据(之前下载的)可以读入 Panda 的数据帧。

Created a dataframe called data with the tank inspection results

命令shape告诉我们数据由 19,752 行 48 列组成,我们可以了解下面包含的数据(和数据类型)。

Python 的优势之一是可以快速汇总数据,下面的代码循环遍历特定的数据列,以确定每个类别中的结果数。由此我们得知,在检查计划的历史上,曼哈顿进行了 17,002 次检查,2018 年进行了 5,461 次检查,对生物生长和鸟类/昆虫残骸的观察比我希望看到的更多(尽管从百分比来看,这是非常低的)。

使用 Python 可视化数据也相当简单。当我们这样做时,一些观察结果就很突出,例如在过去四年中检查的增加(最大的增加是在 2017 年和 2018 年之间,大多数检查发生在曼哈顿,以及少数公司在进行检查中的主导地位。

当我最初在 2019 年 5 月开始这项分析时,数据最后一次更新是在 2018 年 10 月,因此不包括 2018 年最后三个月的报告。我希望这些报告已经完成并归档,并联系纽约市开放数据中心,要求更新这些数据(纽约市开放数据中心有一个程序,要求提供关于其数据集的额外信息)。六月初,数据被更新(截止到 6 月 5 日),现在似乎每周都在更新!

在浏览数据时,除了识别丢失的数据之外,您还经常会遇到需要清理的数据。例如,当我处理 2018 年 10 月的数据时,我注意到没有纬度列的数据,这使我意识到纬度数据被错误地标记为经度,右侧的每一列都倾斜了一列。这随后在 2019 年 6 月的数据发布中得到纠正。

此外,检验公司的名称也不尽相同,“伊塞克斯兄弟公司”也被称为“伊塞克斯兄弟公司”**“伊塞克斯”。如果分析这一点对你的研究很重要,你会想要使数据一致。

第三步——分析并可视化数据

由于我对数据的初步研究,我决定将我的分析重点从检查结果转移到水箱不符合年度报告义务的问题上。

分组数据 — Python 有一个强大的功能GROUPBY,它允许你按照某些属性有效地分组数据,并对结果进行各种计算(比如求和、平均和计数)。由于我们的调查仅限于建筑物是否进行了年度检查,我们将首先选择我们需要的数据。由于每个报告都有一行数据,因此我们将筛选出具有相同 BBL 的同一年的多个报告(保留最后一个)。

组合数据 —当您组合多个数据集来寻找模式时,数据分析通常会变得更有趣。为了说明这一点,我将介绍冥王星数据集,它结合了地理数据和每座建筑物的信息。在这个分析中,我将把重点放在曼哈顿的住宅合作和共管建筑上。我创建了一个.csv文件,其中包含了曼哈顿所有 6065 栋合作和共管建筑的地理编码和经纬度信息。基于我们对水箱必不可少的情况的理解,我过滤了这个数据集,只包括六层以上的建筑——共有 2919 栋。

我的计划是将水箱检查的数据框架与合作公寓建筑的数据框架结合起来,创建一个数据框架。为了做到这一点,我们需要在每个数据帧中有一个键或唯一的标识符来匹配数据。在我们的例子中,我们有 BBL,它是每栋建筑的唯一标识符(基于每栋建筑的区/块/地段)。然后,我们可以使用 Pandas 合并df_cc数据帧和df_summary数据帧。

然后,我们可以确定在曼哈顿有多少本应该有水塔的合作公寓/共管公寓建筑在过去 5 年中没有被检查——其中有 1232 栋!

最后,使用Plotly express我们可以创建一个交互式地图(只用一行代码!)标识曼哈顿的合作公寓和共管公寓建筑,并将过去 5 年中未被检查的建筑标为红色。px.scatter_mapbox(dfnew, lat=”Latitude”, lon=”Longitude”, color=”Reported”, hover_name=”Address”, hover_data=a, color_discrete_map = {“Never”: “red”, “Yes”: “green”}, zoom=14)

结论/后续步骤

根据我们的初步分析,曼哈顿有 2,919 栋带有水箱的合作公寓/共管公寓建筑,其中 1,232 栋从未进行过水箱检查— 42.2% 。这是一个非常重要的数字,希望是夸大其词!

我的下一步是测试建筑物的样本,以(I)确认水箱的存在(可以使用谷歌地图的卫星视图或类似服务),我怀疑六层以上的建筑物需要水箱的前提过于简化,以及(ii)将我的结果与通过纽约市健康建筑水箱搜索工具提供的信息进行比较,以确认调查结果。

我欢迎反馈和建议,并且特别想知道其他人如何在此基础上发展。你可以在推特上找到我

使用 Apache OpenNLP 探索 NLP 概念

原文:https://towardsdatascience.com/exploring-nlp-concepts-using-apache-opennlp-4d59c3cac8?source=collection_archive---------39-----------------------

介绍

Open-source image widely used

在查看了 Awesome AI/ML/DL 上列出的许多基于 Java/JVM 的 NLP 库之后,我决定选择 Apache OpenNLP 库。其中一个原因是另一个开发者(之前看过)推荐了它。此外,这是一个 Apache 项目,在过去二十年左右的时间里,他们一直是 F/OSS Java 项目的大力支持者。不言而喻, Apache OpenNLPApache 2.0 许可证支持。

此外,来自 NLP 研究人员的这条推文对此事增加了一些信心:

在我目前的研究项目中,我用@ apacheopenlp进行了实验,很高兴看到这是一个有价值的 nlp 工具包,它有一个用户友好的 API。把@ apacheopenlp@ stanfordnlp做个比较还为时过早。我先看看它们在命名实体识别中的表现。

- Linda(夏)刘(@ DrLiuBigData)2019 年 3 月 2 日

我想说的是,到目前为止,我的个人体验与 Apache OpenNLP 很相似,我赞同它的简单性和用户友好的 API 和设计。当我们进一步探索时,你会发现事实就是如此。

使用 Apache OpenNLP 探索 NLP

Java 绑定

在这篇文章中,我们不会涉及 Java API 到 Apache OpenNLP 工具,但是你可以在他们的文档中找到一些例子。过一会儿,为了更进一步,你还需要这篇文章底部的资源部分的一些资源。

命令行界面

我被可用的 CLI 的简单性所吸引,它只是开箱即用,例如在需要模型的情况下,以及当它被提供时。它不需要额外的配置就可以工作。

为了使它更容易使用,也不必记住它支持的所有 CLI 参数,我整理了一些 shell 脚本。看看 README 来更深入地了解它们是什么以及如何使用它们。

入门指南

从现在开始,您将需要以下内容:

  • Git client 2.x 或更高版本(GitHub 上用于派生回购的帐户)
  • Java 8 或更高版本(建议安装 GraalVM CE 19.x 或更高版本)
  • Docker CE 19.x 或更高版本,并在继续操作之前检查它是否正在运行
  • 能够从 CLI 运行 shell 脚本
  • 了解读/写 shell 脚本(可选)

注意:在撰写本文时,Apache OpenNLP 的 1.9.1 版本已经发布。

我们已经将的脚本放在一起,让每个人都可以轻松完成这些步骤:

这将引导我们找到包含以下文件的文件夹:

注意:已经提供了一个 docker 映像 来运行一个 docker 容器,该容器将包含您进一步需要的所有工具。您可以看到已经创建了 *shared* 文件夹,这是一个挂载到您的容器中的卷,但它实际上是在您的本地计算机上创建的目录,并映射到该卷。因此,在那里创建或下载的任何东西即使在您退出容器后也仍然可用!

快速阅读主 README 文件,了解如何使用 docker-runner.sh shell 脚本,并快速浏览用法部分 此后,还可以查看一下 Apache OpenNLP README 文件,查看其中提供的脚本用法

运行 NLP Java/JVM docker 容器

在项目的根目录下,在本地计算机命令提示符下,执行以下操作:

在得到提示之前,您有可能首先看到以下内容:

然后您将看到容器内的提示:

该容器中包含了开始探索各种 NLP 解决方案所需的所有 Apache OpenNLP 脚本/工具。

在容器中安装 Apache OpenNLP

当您在容器内部时,在容器命令提示符下,我们将进一步了解以下内容:

您将看到apache-opennlp-1.9.1-bin.tar.gz工件被下载并展开到shared文件夹中:

查看和访问共享文件夹

就像你运行容器一样,一个共享的文件夹被创建,开始的时候它可能是空的,但是随着时间的推移,我们会发现它被不同的文件和文件夹填满了。

你也可以在这里找到下载的模型,Apache OpenNLP 二进制文件被展开到它自己的目录中(名为apache-opennlp-1.9.1)。

您也可以从命令提示符(在容器外部)访问并查看它的内容:

从容器内部看,这是您所看到的:

在容器内执行 NLP 操作

好的一面是,你无需离开当前文件夹就可以执行这些 NLP 操作(查看自述中的探索 NLP 概念部分):

任何脚本的使用帮助:在任何时候,您都可以通过以下方式查询脚本:

例如

给我们这个用法文本作为输出:

更多示例和详细输出见自述文件中的检测语言部分。

  • 检测单行文本或文章中的句子。

更多示例和详细输出见自述文件中的检测句子部分。

  • 在单行文本或文章中查找人名、组织名、日期、时间、金钱、位置、百分比信息。

更多示例和详细输出见自述文件中的查找名称部分。本节中有许多类型的名称查找器示例。

  • 将一行文本或一篇文章标记成更小的组成部分(如单词、标点、数字)。

更多示例和详细输出见自述文件中的符号化部分。

更多例子和详细输出见自述文件中的解析器部分。

有关更多示例和详细输出,请参见自述文件中的标记词性部分。

  • 文本分块通过将文本或文章分成单词的句法相关部分,如名词组、动词组。您可以将此功能应用于已标记的词性文本或文章。对已经由 PoS tagger* 标记的文本应用分块(参见 Penn Treebank tag set 了解标记类型的图例,也参见https://nlpforhackers.io/text-chunking/*

更多示例和详细输出见自述文件中的分块部分。

退出 NLP Java/JVM docker 容器

事情就是这么简单:

您将返回到本地计算机提示符。

标杆管理

该工具的一个显著特点是,它记录和报告其在不同执行点的操作的指标—在微观和宏观级别所用的时间,这里有一个示例输出来说明这一特点:

从以上内容中,我发现了对我作为科学家、分析师甚至工程师都有用的 5 个指标:

在进行性能比较时,像这样的信息是非常宝贵的,例如:

  • 在两个或多个模型之间(加载时和运行时性能)
  • 在两个或多个环境或配置之间
  • 在执行相同 NLP 的应用程序之间,使用不同的技术栈将动作放在一起
  • 还包括不同的语言
  • 寻找不同文本数据语料库之间的相互关系(定量和定性比较)

经验例子

BetterNLP 用 python 写的库也在做类似的事情,参见Kagglekernel:*Better NLP 笔记本 Better NLP 总结者笔记本 (搜索 time_in_secs 这两个笔记本里面都有*

**

就个人而言,这非常鼓舞人心,也证明了这是一个提供给最终用户的有用特性(或行为)。

其他概念、库和工具

在下面的参考资料部分中提到了其他的基于 Java/JVM 的 NLP 库,为了简洁起见,我们不会涉及它们。所提供的链接将为你自己的追求带来进一步的信息。

Apache OpenNLP 工具本身中,我们只讨论了它的命令行访问部分,而没有讨论 Java 绑定。此外,我们并没有再次介绍该工具的所有 NLP 概念或特性,因为简洁起见,我们只介绍了其中的一小部分。但是文档资源GitHub repo 上应该有助于进一步的探索。

你也可以通过检查 docker-runner 脚本来找出如何为自己构建 docker 形象

结论

Image source by Simon Ray

在经历了以上内容之后,我们可以通过探究其优缺点来总结以下关于 Apache OpenNLP 工具的内容:

优点

  • 这是一个易于使用和理解的 API
  • 浅显的学习曲线和包含大量示例的详细文档
  • 涵盖了许多 NLP 功能,在文档中有比我们上面所做的更多的内容要探索
  • 简单的外壳脚本Apache OpenNLP 脚本已经被提供来使用这个工具
  • 下面有很多资源可以用来了解关于 NLP 的更多信息(参见下面的资源部分)
  • 为快速入门和探索 Apache OpenNLP 工具而提供的资源

缺点

  • 查看 GitHub 回购,似乎进展缓慢或停滞不前(最近两次提交有很大差距,即 2019 年 5 月和 2019 年 10 月 15 日)
  • 浏览文档(手册)中的示例时,遗漏了一些型号
  • 根据您的使用案例,当前提供的模型可能需要进一步培训,请参见此推文:

资源

Apache OpenNLP

其他相关员额

关于我

Mani Sarkar 是一名主要在 Java/JVM 领域充满激情的开发人员,目前在与小团队和初创公司合作时加强团队并帮助他们加速,作为一名自由软件工程师/数据/ml 工程师,更多…

推特:@ theneomatrix 369|GitHub:*@ neomatrix 369*

最初发表于https://blog.valohai.com**

在支持 Java 的 Jupyter 笔记本中使用 Apache OpenNLP 探索 NLP 概念

原文:https://towardsdatascience.com/exploring-nlp-concepts-using-apache-opennlp-inside-a-jupyter-notebook-e53489ba2bd8?source=collection_archive---------39-----------------------

介绍

Image source (unsplash) via Daniel Olah

经过一番说服之后,我一直在探索和使用 Apache OpenNLP 库。对于那些不知道它的人来说,这是一个 Apache 项目,过去二十年左右 F/OSS Java 项目的支持者(见维基百科)。我发现他们的命令行界面使用起来非常简单,对于学习和理解自然语言处理(NLP)来说,这是一个非常好的学习工具。独立于这篇文章,你可以找到使用 Apache OpenNLP 的另一种视角,所有这些都直接来自你的命令提示符。

我可以说,这个领域的几乎每个人都知道并熟悉 Jupyter 笔记本(如果你不是,可以看看这个视频【1】【2】)。从现在开始,我们将会在笔记本的领域内做和你自己的实验一样的事情。

使用 Apache OpenNLP 探索 NLP

命令行界面

我会让你参考贴子,在那里我们讲述了关于 Apache OpenNLP 的命令行体验,这是让你熟悉这个 NLP 库的一个很好的方法。

Jupyter 笔记本:入门

在继续下一步之前,请执行以下操作:

$ git clone git@github.com:neomatrix369/nlp-java-jvm-example.gitor$ git clone https://github.com/neomatrix369/nlp-java-jvm-example.git$ cd nlp-java-jvm-example

然后是 请参阅 README 的 部分中的 入门 一节,从基于 Java 的 Jupyter 笔记本 内部探索 NLP 概念,然后再继续。

此外,默认情况下,我们将 JDK 选择为 GraalVM ,您可以从控制台消息中的这些行看到这一点:

<---snipped-->JDK_TO_USE=GRAALVMopenjdk version "11.0.5" 2019-10-15OpenJDK Runtime Environment (build 11.0.5+10-jvmci-19.3-b05-LTS)OpenJDK 64-Bit GraalVM CE 19.3.0 (build 11.0.5+10-jvmci-19.3-b05-LTS, mixed mode, sharing)<---snipped-->

注意 :已经提供了一个 docker 镜像 来运行一个 docker 容器,它将包含您需要的所有工具。您可以看到已经创建了共享文件夹,它链接到装入容器的卷,从本地机器映射您的文件夹。因此,任何创建或下载到共享文件夹中的内容,即使在您退出容器后也仍然可用!

快速阅读主 README 文件,了解如何使用 docker-runner.sh shell 脚本,并快速浏览脚本用法部分

运行 Jupyter 笔记本容器

请参阅 README 的 部分中的 运行 Jupyter 笔记本容器 部分,从基于 Java 的 Jupyter 笔记本 内部探索 NLP 概念。

您所需要做的就是在克隆上面链接中提到的 repo 之后运行此命令:

$ ./docker-runner.sh --notebookMode --runContainer

一旦你运行了上面的操作,这个操作将会自动打开并加载 Jupyter 笔记本界面到一个浏览器窗口中。您将有几个 Java 笔记本可供选择(放在本地机器上的 shared/notebooks 文件夹中):

在容器中安装 Apache OpenNLP

当处于笔记本模式的容器中时,有两种方法可以安装 Apache OpenNLP :

查看和访问共享文件夹

请参阅自述文件 中的 查看和访问共享文件夹 部分,从基于 Java 的 Jupyter 笔记本 内部探索 NLP 概念。

这也将在下一节的 Jupyter 笔记本中有所涉及。您可以通过%system Java cell magic 看到目录内容,然后从命令提示符看到类似的文件/文件夹布局。

在 Jupyter 笔记本中执行 NLP 操作

当您运行笔记本电脑服务器时,笔记本电脑服务器一启动,您就会看到此启动器窗口,其中包含笔记本电脑和其他支持文件的列表:

上面的每个笔记本都有一个用途,myfirstjupyternlp javanotebook . ipynb展示了如何在 IPython 笔记本中编写 Java,并使用调用 Apache OpenNLP 库功能的 Java 代码片段执行 NLP 操作(参见文档了解关于类和方法的更多细节,以及 Java 文档了解关于 Java API 用法的更多细节)。

另一个笔记本mynextjupyternlpjavanotebook . ipynb在一个远程云实例上运行相同的 Java 代码片段(在 Valohai CLI 客户端的帮助下)并返回单元格中的结果,只需一个命令。创建账户并在自由层计划内使用快速且免费。

我们能够从两个基于 Java 的笔记本内部检查以下 Java API 与 Apache OpenNLP 库的绑定:

直接通过笔记本探索上述 Apache OpenNLP Java APIs】

我们能够在笔记本内部完成这项工作,运行 IJava Jupyter 解释器,它允许在典型的笔记本中编写 Java。我们将使用 Java 代码的小片段探索上述命名的 Java APIs,并在笔记本中看到结果:

因此,回到您的浏览器,寻找myfirstjupyternlpjavanotebook . ipynb笔记本,用它玩一玩,读取并执行每个单元格,观察响应。

借助远程云服务,通过笔记本探索上述 Apache OpenNLP Java APIs】

我们能够在笔记本内部完成这项工作,运行 IJava Jupyter 解释器,它允许在典型的笔记本中编写 Java。但是在这本笔记本中,我们更进一步,使用了%system Java cell magicValohai CLI magic,而不是像以前的笔记本那样在各个单元格中运行 Java 代码片段。

这样,模型的下载和使用模型的文本处理就不会在本地机器上进行,而是在云中一个更复杂的远程服务器上进行。你可以从笔记本电脑内部控制这个过程。当要处理的模型和数据集很大,而您的本地实例没有必要的资源来支持长期的 NLP 过程时,这一点更为重要。我看到 NLP 培训和评估需要很长时间才能完成,因此高规格的资源是必须的。

再次回到你的浏览器,寻找mynextjupyternlpjavanotebook . ipynb笔记本,用它玩一玩,读取并执行每个单元格。所有必要的细节都在那里,包括文档和支持页面的链接。

为了更深入地了解这两个笔记本是如何组装在一起的,以及它们是如何操作的,请查看所有的源文件

合上 Jupyter 笔记本

在执行此操作之前,请确保您已经保存了笔记本。切换到运行 docker-runner shell 脚本的控制台窗口。在运行 Docker 容器的控制台中按下 Ctrl-C ,您会看到:

*<---snipped--->[I 21:13:16.253 NotebookApp] Saving file at /MyFirstJupyterJavaNotebook.ipynb^C[I 21:13:22.953 NotebookApp] interruptedServing notebooks from local directory: /home/jovyan/work1 active kernelThe Jupyter Notebook is running at:[http://1e2b8182be38:8888/](http://1e2b8182be38:8888/)Shutdown this notebook server (y/[n])? y[C 21:14:05.035 NotebookApp] Shutdown confirmed[I 21:14:05.036 NotebookApp] Shutting down 1 kernelNov 15, 2019 9:14:05 PM io.github.spencerpark.jupyter.channels.Loop shutdownINFO: Loop shutdown.<--snipped-->[I 21:14:05.441 NotebookApp] Kernel shutdown: 448d46f0-1bde-461b-be60-e248c6342f69*

这将关闭容器,您将返回到本地机器的命令提示符下。您的笔记本会保留在本地计算机上的“共享/笔记本”文件夹中,前提是您在不断更改它们时已经保存了它们。

其他概念、库和工具

下面的参考资料部分提到了其他基于 Java/JVM 的 NLP 库,为了简洁起见,我们就不一一赘述了。所提供的链接将为你自己的追求带来进一步的信息。

在 Apache OpenNLP 工具本身中,我们只讨论了它的命令行访问部分,而没有讨论 Java 绑定。此外,我们并没有再次介绍该工具的所有 NLP 概念或特性,因为简洁起见,我们只介绍了其中的一小部分。但是文档资源GitHub repo 上应该有助于进一步的探索。

您还可以通过检查 docker-runner 脚本来了解如何为自己构建 docker 映像

限制

尽管 Java cell magic 确实有所不同,并有助于运行命令。即使它不是基于 Python 的笔记本,我们仍然可以在我们的单元中运行 shell 命令和执行 Java 代码,并在 Jupyter 笔记本中做一些不错的 NLP 工作。

如果你有一个基于 python 的笔记本,那么 Valohai 的扩展 Jupyhai 就足够了。看看本帖资源版块的聚居地子版块(在帖子末尾)。事实上,我们一直在 Jupyhai 笔记本中运行我们的所有操作,尽管我一直称它为 Jupyter Notebook,看看浏览器中面板中间部分工具栏上的图标):

结论

Image source (unsplash) via Aaron Burden

这是一种与大多数其他探索和学习方式非常不同的体验,你可以看到为什么整个行业,特别是涵盖学术界、研究、数据科学和机器学习的领域,都像风暴一样采用这种方法。我们仍然有局限性,但是随着时间的推移,它们会被克服,让我们的经历变得更顺利。

在你的代码所在的同一个页面上看到你的结果是一个很大的保证,给了我们一个简短快速的反馈循环。尤其是能够看到可视化并动态地改变它们并获得即时结果,这可以为每个领域的忙碌和渴望的学生、科学家、数学家、工程师和分析师开辟道路,而不仅仅是计算或数据科学或机器学习。

资源

ijjava(Jupyter 解释程序)

朱皮海

Apache OpenNLP

其他相关员额

探索波士顿和西雅图的房地产投资机会

原文:https://towardsdatascience.com/exploring-real-estate-investment-opportunity-in-boston-and-seattle-9d89d0c9bed2?source=collection_archive---------18-----------------------

Airbnb’s treehouse

让 Airbnb 的数据告诉你下一个投资的房产。

该项目旨在设计一个城市房地产市场分析框架,目标是确定 Airbnb 市场中产生租金收入的房地产。随着 Airbnb 成为事实上的短期住宿市场,甚至成为酒店的替代品,其用户活动反映了一个城市房地产和酒店业的脉搏。房产所有者现在能够像一个迷你连锁酒店那样经营,提供短期到中期的住宿空间。波士顿和西雅图的数据集用于开发该框架,但其真正的目标是使其可用于 Airbnb 覆盖的其他城市,假设其他城市数据集共享相同的要素数据。

商业理解

波士顿和西雅图是位于美国两侧的两个城市,据说具有鲜明的房地产特征:房地产的需求和供应以及在决定价格方面发挥作用的因素。分析结果将用于房地产投资者,他们打算翻新投资的物业,以从 Airbnb marketplace 获得租金收入。该分析试图回答以下问题:

  1. 哪个城市的租赁市场更强劲,因此对投资更有吸引力?
  2. 需求是否稳定且不断增长?
  3. 这座城市的哪个街区投资回报率更高?
  4. 投资哪种类型的房产?
  5. 哪些因素决定租金收益率/价格?

以下技术问题应回答上述商务问题:

  1. 波士顿和西雅图的价格分布是怎样的?
  2. 这些年来,这两个城市的租赁需求发生了怎样的变化?有什么潜在的因素影响他们吗?市场饱和了吗?
  3. 与价格高度相关的特征是什么?

数据理解

CSV 文件中的三个数据集用于分析:

  1. 列表—显示每个列表的 92(西雅图)和 95(波士顿)属性的列表数据。
  2. 评论——由客人给出的具有 5 个属性的评论。关键属性包括日期(日期时间)、列表标识(离散)、审阅者标识(离散)和注释(文本)。
  3. 日历-通过列表预订下一年的数据。总共四个属性,包括 listing_id(离散)、date(日期时间)、available(分类)和 price(连续)。

值得注意的是,Airbnb 数据集有其局限性,因此它只是这两个城市短期租赁市场的一部分。例如,它不会显示未满足的需求。它也没有提供购买投资性房地产的价格数据,以获得良好的投资回报(ROI):

Airbnb 数据集是分子。分母仍然需要房地产价格数据集,这将在同一主题的后续文章中探讨。

数据准备

为了准备用于分析的数据,需要采取几个步骤来清理数据集:

  1. 处理缺失值-删除缺失值超过 1000 的列
  2. 低值列—删除包含 URL 链接、不可用记录(如主机名、街道)以及只有一个唯一值的列。
  3. 布尔列—带有“t”和“f”的列被转换为布尔“真”和“假”。
  4. 数字列—清理价格、费率和百分比等列,以便提取其数值并保存为浮点型。
  5. 分类数据-包含分类数据(如邮政编码、物业类型、邻居、房间类型、床类型)的列经过编码,以便用于机器学习算法。
  6. 异常值-识别异常值并记录下来,它可能会影响以后的分析。然而,它们仍然保留在数据集中,以供可视化观察。稍后将采取措施处理异常值。
  7. 共线特征-由于在此阶段尚未采用机器学习,因此尚未识别共线特征。此处做了一个注释,以便在未来工作中需要时处理共线特征。

Q1。哪个城市的租赁市场更强劲,因此对投资更有吸引力?

就短期住宿业务而言,这两个城市全年都有大量的文化节、音乐会、会议和展览以及体育赛事。波士顿也是举办大型活动的热门地点,例如波士顿马拉松赛吸引了大量的人群来到这个城市。这些活动带来的活力是短期租赁市场的巨大商机。

[## 西雅图—活动日历

有趣的事情,夏季活动,街头集市,艺术和音乐节。食物、啤酒和葡萄酒品尝。活动日历…

www.events12.com](https://www.events12.com/seattle/) [## 波士顿活动|音乐会、节日、马拉松、展览

波士顿的活动包括餐厅周、体育赛事、节日、假日活动等等。找到一个完整的…

www.bostonusa.com](https://www.bostonusa.com/events/events-calendar/)

除了休闲和文化,我们还可以把这个城市视为一个吸引商务旅客的商业中心。这些旅行者将能够在这个城市没有任何活动发生的安静时期填补空白。

大学毕业时期是另一个季节性事件,当父母和亲戚参加毕业典礼时,会吸引大量人群进入城市。在这方面,波士顿大都会有一长串的学院:

[## 波士顿大都会的学院和大学列表—维基百科

这是波士顿大都市的学院和大学列表。有些位于波士顿市区内,而有些…

en.wikipedia.org](https://en.wikipedia.org/wiki/List_of_colleges_and_universities_in_metropolitan_Boston)

虽然西雅图作为教育中心有点落后,但仍然是毕业季的保证:

[## 华盛顿西雅图附近的四年制大学列表-华盛顿西雅图四年制大学

探索华盛顿州西雅图的四年制大学列表。

www.collegesimply.com](https://www.collegesimply.com/colleges/washington/seattle/four-year-colleges/)

旅游景点在城市是另一个点,看看填补空置客房在非季节性期间。

为了更好地了解住宿需求,我们可能需要两个城市的旅客和交通数据来进行更全面的定量分析。与此同时,Airbnb 的预订数据揭示了反映在价格上的需求。

Figure 1

从 Airbnb 预订数据集生成的价格分布图按需提供了一些提示。波士顿分布有一个更厚的尾部,显示了更不稳定的价格范围。而西雅图价格大多在 500 美元以内。

Figure 2

Figure 3

当我们把同一个预订数据集作为趋势来看,对每天的价格进行平均,我们会看到需求是如何在定价中发挥作用的。当供应不能满足城市大型活动的需求时,价格就会上涨。该图显示了周波动和季节动态。波士顿的价格趋势反映了受供应限制的更加多事和强劲的租赁市场。因此,不稳定的价格波动所造成的需求得不到满足。图 2 中 2017 年 4 月的大幅飙升很可能是波士顿马拉松的结果。规则和微小的波动是周末效应,因为网格线是在周六绘制的。

Q2。需求是否稳定且不断增长?

Figure 4

Figure 5

评论是由用户在入住酒店后提供的,因此可以很好地代表已满足的需求。从这两个城市的需求趋势来看,我们可以说这些年来需求确实在稳步攀升。波士顿在过去的两年里经历了需求的急剧攀升。价格波动每年都有季节性变化,这在 Q1 得到了部分揭示。图 4 和图 5 提供了更大的图景,需求通常在夏末秋初达到峰值。

然而,波士顿的价格动态仍处于上升轨道,而西雅图在 4 年的增长后看起来已经饱和。

Q3。这座城市的哪个街区投资回报率更高?

看图 2 和图 3,我们注意到波士顿的价格区间较高,甚至在淡季也高于西雅图的价格区间。这表明,在供应有限的情况下,中国对短期住房的需求更加强劲。

然而,这只是投资回报率的一部分。我们还需要考虑拥有房产的价格,以验证更高的租金收入是否能证明总体收益是合理的。换句话说,如果波士顿的房地产价格高得多,导致租金收入的收益率较低,那么市场就不会提供更好的投资回报率。

Q4。投资哪种类型的房产?

Figure 6

Figure 7

Figure 8

波士顿

对于波士顿市场,我们可以看到,公寓,共管公寓和房子是最受欢迎的产品在这里的大多数街区。租金收入较高的房产类型包括房子、联排别墅和共管公寓,这可能是因为上市数量柱状图显示的供应有限。

根据图 8,市中心、皮革区和南波士顿滨水区的租金通常高于其他街区。Back Bay,Dorchester,Fen Way,Jamaica Plain 和 South End 是供应充足的街区,这些应该是市场竞争更激烈的街区,因此低于平均租金价格。从这个角度来看,海湾村、南波士顿海滨和西区是供应方面竞争较弱的市场,租户的良好需求反映在他们愿意支付更高的价格。

Figure 9

Figure 10

Figure 11

西雅图

公寓,房子和公寓是西雅图最受欢迎的住宿产品。除了沃灵福德可能是一个异常值之外,大多数街区的租金都保持在一个范围内。价格最高的房产类型是船,一种奇异的房产。贝尔敦、弗里蒙特 Minor 和沃林福德出租物业价格更具竞争力,供应充足。荆棘崖、工业区、先锋广场和东南木兰是供应较少的街区,因此租金较高。如果购买房产的成本与其他地区相比,这些地区可能值得投资。

简而言之,位置和季节性在价格设定中扮演着重要的角色。

Q5。哪些因素决定租金收益率/价格?

由数字特征组成的相关矩阵显示,该房产可容纳的人数、床位、卧室和浴室的数量与租赁价格最相关,这非常合理,因为这意味着更大的房产。所以它在这里没有帮助揭示太多的洞察力。

包括点评分数在内的其他因素在设定租金价格时几乎不起作用。

Figure 12

Figure 13

根据以上分析,海湾村、南波士顿海滨和波士顿西区看起来是更好的投资选择。也许我们需要房地产价格数据来确定他们的投资回报率。

总之,随着 Airbnb 的发展和将其市场扩展到更多的城市,基于城市的数据集在分析房地产市场方面变得非常方便。然而,由于它们只是图片的一部分,有必要将数据集与其他房地产市场(如 Trulia、Zillow)以及旅客和交通跟踪数据结合起来,以促进房地产投资分析。

该框架编码在一个 Jupyter 笔记本中,可以从我的 Github 库中获得

使用机器学习和 R 探索招聘偏差

原文:https://towardsdatascience.com/exploring-recruitment-bias-using-machine-learning-and-r-8e071dad7dce?source=collection_archive---------20-----------------------

你好,

由于我在人力资源咨询方面的背景,我有机会与世界各地的众多人力资源专业人士共事。通过我的工作和学习,我很快意识到招聘过程是任何与多元化相关的研究的起点。与招聘过程相关的潜在人为偏见有很多。我希望我的工作有助于确立它毕竟不是一项复杂的研究。

目标:

使用一个 实验性 数据集进行本案例研究,我工作的主要目标是只调查招聘流程的入围阶段,以及:

  • 对招聘数据进行探索性数据分析,以确定招聘阶段的性别、种族模式
  • 调查性别和种族是否影响申请人入围流程
  • 应用机器学习来预测谁将入围并确定关键驱动因素
  • 根据调查结果建议更新招聘策略

数据集概述:

实验数据集包含以下字段:

  • 申请人代码:系统中每个申请的唯一标识符

  • 性别:男性代码为 1,女性代码为 2
  • ATSIyn :如果候选人是土著居民或托雷斯海峡岛民,则指定 1 =是。如果候选人是普通申请人,分配 2 =否
  • 入围 yn :如果被拒绝则分配 0,如果入围则分配 1
  • 已面试:未面试分配 0,面试分配 1
  • FemaleONpanel :仅针对男性小组分配 1 分,如果小组中有女性成员,则分配 2 分
  • OfferNY :如果向候选人提供了 offer,则分配 1;如果没有提供 offer,则分配 0
  • AcceptNY :如果接受则赋值 1,如果拒绝则赋值 0
  • JoinYN :如果加入则赋值 1,如果不加入则赋值 0

研究性别和种族模式的探索性数据分析:

女性申请者占申请者总数的 72.1%。

土著或托雷斯海峡岛民申请人占申请人总数的将近一半(43.2%)。

在简历筛选后,总共 280 名申请人中有 88 人进入下一阶段的决选名单。

入围的申请者中有 55 人接受了面试。

在进行的 55 次访谈中,有 22 次有女性成员。

在总共进行的 55 次面试中,有 28 人被录用。

要约接受率为 64%。18 名接受聘用的申请人加入了该公司。

调查性别和种族是否影响申请人入围流程:

根据上述探索性研究,申请人中的性别和种族比例分别为 72 %和 43%。抛开所有外部条件不谈,一个公正的招聘过程应该导致基于性别和种族的入围申请人具有相当相似的代表性。这是本节研究的目的。

申请人入围名单中的种族偏见分析:

如上所述,申请人的族裔比例为 43.2%。然而,根据以下调查结果,候选人名单中的族裔比例下降到 15% (19/88)。 这似乎表明在入围过程中倾向于一般申请者。

气球图表明,一般申请人可能会入围。

使用统计分析(卡方) ,可以确定具有种族背景的申请人的预期数量和观察数量之间存在巨大差距。在下面的案例中,只有 19 个入围(观察到),而预期的数字应该是 38。

入围过程中对普通类申请人的偏好是出于运气或机遇的可能性已经被非常低的卡方值 23.184 所否定,p < 0.0001.

对入围申请人的性别偏见的分析:

如上所述,女性在申请人中的比例为 72%。然而,根据以下调查结果,入围申请人名单中的女性比例已降至 56.7% (50/88),被拒人数高达 152 人。这似乎显示出在入围过程中对男性申请者的偏爱。

气球情节证实了女性申请者在面试入围阶段的高拒绝率。

使用统计分析(卡方检验) ,可以确定入围的女性申请人的预期人数和实际人数之间存在差距。在下面的案例中,只有 50 人入围(观察到),而预期数字应该是 63 人。

入围过程中优先考虑男性申请人是出于运气或机遇的可能性已经被非常低的卡方值 13.905 否定,p < 0.0001。

应用机器学习来预测申请人的入围名单并确定关键驱动因素:

在上述分析中,性别和种族是分开评估的。然而,如果大多数有种族背景的申请人碰巧是女性,那么暗示性别和有种族背景的申请人都面临偏见是不正确的。因此,在本节中,预测申请人入围名单的分析将考虑这两个变量。

进行这种分析的方法是通过逻辑回归。逻辑回归是一种机器学习算法,可用于预测申请人在申请流程中入围的可能性。该算法将用于研究我们的数据集,以确定申请人入围名单的主要驱动因素。

机器学习分析的结果表明,性别和 ATSIyn(土著或托雷斯岛民)都是申请人入围的重要预测因素。

总之,根据调查结果,男性申请人被列入候选名单的可能性是女性申请人的 3.3 倍。普通类申请人比土著或托雷斯海峡岛民申请人入围的可能性高 4.5 倍。

准备一个招聘策略来抵消偏见:

数据分析表明,基于综合数据集,性别和种族偏见存在于公司内部招聘流程的入围阶段。

为了消除偏见,推荐的策略是引入盲审流程,在入围流程中,对审查工作申请的内部或外部招聘人员隐藏申请人的姓名、性别和种族背景信息。

代码和数据集的引用和链接:

这两位学生在这一领域的工作深深地激励了我,这也是我在这一领域进一步探索的兴趣的起点。视频链接在这里给你看看。

所用的数据集和案例研究如马丁·爱德华兹和柯尔斯顿·爱德华兹所著的《预测性人力资源分析——掌握人力资源指标》一书中所述。r 代码是我开发的。

我的 github 账号上有数据集和 R 代码。下面提供了链接:

[## sambit 78/人员分析项目

我的所有个人项目都与人员分析相关-sambit 78/人员分析-项目

github.com](https://github.com/Sambit78/People-Analytics-Project/tree/master/05 - Recruiting Bias Analysis)

谢谢:)

使用 PRAW API 包装器探索 Reddit 的“问我任何问题”

原文:https://towardsdatascience.com/exploring-reddits-ask-me-anything-using-the-praw-api-wrapper-129cf64c5d65?source=collection_archive---------14-----------------------

未来 Reddit 分析师的简短 PRAW 教程

PRAW + Python can be used to quickly access Reddit’s API

在撰写本文时,Reddit.com 是美国互联网上第五大最受欢迎的网站。你们中的大多数人可能都在这个网站上呆过,但对于那些不熟悉的人来说,Reddit 是一个由社区驱动的论坛或“子编辑”的大集合,在这里人们可以分享几乎任何主题的新闻、内容和观点。最受欢迎的社区之一,向我提问(r/IAmA),将著名或有趣的人物与日常 Redditors 联系起来,以便直接回答匿名参与者的问题,旨在充分利用与世界上最引人注目的一些人的无与伦比的接触。

在本文中,我将重点关注这个特定的社区,通过使用 PRAW(一种 Python“包装器”)连接到 Reddit API 中的基本信息的过程,在高层次上指导当前和未来的数据科学家,这种包装器就像一个附加包,可以将 API 调用集简化为用户易于使用的函数。这个特定的演练更多的是关于快速建立连接,避免任何深入到业务应用程序中(联系 Reddit Partnerships 以获得该信息)。也就是说,我希望留给你的不仅仅是一种新的数据技能,还有一些最引人注目的 AMA 作品的集合。

第一步:在你的终端上安装或更新 PRAW

#to install for the first time
pip install praw#to update
pip install praw --upgrade praw

步骤 2:创建和/或登录您的 Reddit 帐户,开始通过 OAuth 进行身份验证

点击此处创建账户或登录现有账户,并确保您已经验证了您的电子邮件。完成后点击这里进入你的应用列表,点击有点难找到的按钮‘创建应用’。

Beware of Reddit’s API guidelines.

命名你的应用程序和用例(“脚本”适合基本的、天真的探索),写一个关于你的计划的简短说明,并输入一个必需的重定向 URL(为此我使用了我的 GitHub URL)。点击“创建应用程序”后,您的页面将会更新,显示一个个人使用脚本密钥(在许多 API 教程中通常被称为“客户端 id ”)和一个机密密钥(您可能知道它是“API 密钥”)。

当使用需要 API 密钥和密码的 API 时,你应该不要将这些值硬编码到你的主文件中。为了保护你的 API 密匙不被公开传播,看看这个伟大的教学 jupyter 笔记本,里面有一些关于设置的更详细的注释。secret/ 目录将您的敏感信息存储在本地 JSON 文件中,并在需要时适当地调用它。您的credentials.json文件应该如下所示:

{"client_id": "XXXXXXXXX-XXXX","api_key": "XXXXXXXXXXXX_XXXXXX-XXXXXXX","username": "<your Reddit username>","password": "<your Reddit password"}

步骤 3:创建您的第一个授权 Reddit 实例

credentials.json或类似标题的文件夹中加载您的密钥:

# Load secret keys from credentials.json
import json
url = '[https://www.reddit.com/'](https://www.reddit.com/')
with open('/Users/<Your CPUs User>/.secrets/credentials.json') as f:
    params = json.load(f)

导入 PRAW 包装并授权 Reddit 实例:

import praw
reddit = praw.Reddit(client_id=params['client_id'], 
                     client_secret=params['api_key'],
                     password=params['password'], 
                     user_agent='<name it something descriptive> accessAPI:v0.0.1 (by /u/<yourusername>)',
                     username=params['username'])

步骤 4:从你的 reddit 实例中获取一个 Subreddit 实例

要获得 subreddit 实例,在您的reddit实例上调用subreddit时传递 subreddit 的名称。例如:

subreddit = reddit.subreddit('iama')print(subreddit.display_name)  # Output: iama
print(subreddit.title)         # Output:I Am A, where the mundane...
print(subreddit.description)

其他一些基本分析的子编辑方法:

**created_utc**:创建子编辑的时间,用 Unix Time 表示。

**description**:子编辑描述,降价中。

**display_name**:子编辑的名称。

**id**:子编辑的 ID。

**name**:子编辑的全称。

**subscribers**:用户数

第五步:从你的 Subreddit 实例中获取一个提交实例

为了收集感兴趣的 subreddit 中提交的一些数据,我们可以使用 for 循环遍历指定数量的提交,按照页面上的有争议、镀金、热门、新、上升、top 提交进行排序。

#iterating through the 10 submissions marked **hot**
for submission in subreddit**.hot**(limit=**10**):
    print(submission.title)  # Output: the submission's title
    print(submission.score)  # Output: the submission's upvotes
    print(submission.id)     # Output: the submission's ID
    print(submission.url)    # Output: the URL

基础分析的其他一些提交方法:

**author**提供了Redditor的一个实例。

**created_utc**提交创建的时间,用 Unix 时间表示。

**distinguished**划界案是否有区别。

**link_flair_text**link flair 的文本内容(有点像 subreddit 中的提交类别),或者如果没有 flair 就没有。

**name**提交材料的全称。

**num_comments**提交的评论数量。

**over_18**提交的文件是否被标记为 NSFW。

**spoiler**提交的内容是否被标记为剧透。

**stickied**提交的文件是否被粘贴。

**title**提交的标题。

**upvote_ratio**提交的所有投票中赞成票所占的百分比。

提交链接到的 URL,如果是自发布,则是永久链接。

步骤 6:从“问我任何问题”子编辑中创建一个熊猫基本提交统计数据框架

我选择制作一个 200 行 7 列的数据框架,由 r/IAmA subredit 上的 top 提交内容组成,许多著名的“问我任何问题”讨论都在这里进行。这可能需要相当长的时间,取决于从子 reddit 获取的数据的大小以及本地或虚拟机的速度,所以我喜欢在 for 循环中包含 print 语句,以便跟踪进度。

ama_title = []
time = []
num_upvotes = []
num_comments = []
upvote_ratio = []
link_flair = []
redditor = []
i=0for submission in subreddit.top(limit=200):
    i+=1
    ama_title.append(submission.title)
    time.append(submission.created_utc)
    num_upvotes.append(submission.score)
    num_comments.append(submission.num_comments)
    upvote_ratio.append(submission.upvote_ratio)
    link_flair.append(submission.link_flair_text)
    redditor.append(submission.author)
    if i%5 == 0:
        print(f'{i} submissions completed')

这将为 for 循环遍历的每 5 个提交打印一个注释。一旦完成,你可以把你的数据放入熊猫的数据框架中进行交钥匙辩论和分析。

ama_df = pd.DataFrame(
    {'ama_title': ama_title,
     'time': time,
     'num_comments': num_comments,
     'num_upvotes': num_upvotes,
     'upvote_ratio': upvote_ratio,
     'link_flair': link_flair,
     'redditor': redditor
    })ama_df.head(10)

这应该会产生类似这样的结果:

第七步:探索数据

帮助我们快速判断最有意义的 ama 的最简单的方法是根据最多评论(num_comments)、最多投票(num_upvotes)和最积极(upvote ratio)来绘制领导者,然后在根据主题/类别(“link_flair”)对行进行分组后,做同样的事情,取每个组的这些相同统计的平均值。

最迷人的 AMA 人物

这些名单中的一些人是不足为奇的名人:欧巴马、比尔·盖茨、伯尼·桑德斯、小罗伯特·唐尼。然而,如果你有时间,我强烈建议你读一读其中一些真正有趣的惊喜。丽亚·雷米尼讨论了她从山达基教会走向情感和精神自由的非凡旅程,她是 2016 年总统选举期间病毒般犹豫不决的选民肯·伯恩,约瑟夫·史达林独裁统治下公开诚实的幸存者阿纳托利·康斯坦丁,以及最近被拘留的维基解密创始人、记者兼计算机科学家朱利安·阿桑奇

一些纯粹正面的 ama 让我真的很欣赏一些非超级名人的讨论。有一位工程师在现场为他的免费 Photoshop 版本实施新功能建议( ivanhoe90 ),接触到美国宇航局瞄准冥王星以外的新视野团队的太空科学家(艾伦·斯特恩),美国宇航局 JPL 的软件开发人员和招聘经理,以及第二个在月球上行走的人巴兹·奥德林。喜剧传奇人物埃里克·艾多尔加入了这些极其引人注目的冒险家,他带着整个《巨蟒剧团》剧组来到 AMA,与粉丝们交流。

最吸引人的 AMA 话题

由于职业运动员的高知名度、广泛的媒体报道以及像 NBA、UFC ( 隆达·罗西)、体育机构和服装品牌这样的大公司的可能支持,他们在网络上获得大量追随者并不奇怪。众所周知,这些 ama 有时会与推广产品、活动或品牌的活动一起预订。非营利主题出现在所有三个条形图由于比尔盖茨代表他的基金会,网络中立活动家和美国公民自由联盟的支持者。像 Reddit 这样以社区为先的平台,对于那些寻求组织起来煽动变革,或者在 Twitter 上谈论如何打击你的快餐竞争对手的人来说,确实是一个很好的枢纽(温迪的社交团队)。

高票的有新闻价值的事件讨论由 Sid Fischer 主持,他是佛罗里达州帕克兰市斯通曼道格拉斯高中第三个房间枪击案的幸存者,还有一个稍微轻松一点的人,他打扮成大富翁去轰炸前 Equifax 首席执行官理查德·史密斯的参议院听证会。

《犯罪与正义》在最积极的 AMA 话题中独占鳌头,因为一个话题引起了相当一致的庆祝,这个话题自豪地写道:“爱达荷州通过了一项 Ag-Gag 法律,将揭露工厂化农场虐待动物的行为定为犯罪。我们起诉了他们并赢了。“著名的辛普森谋杀案审判检察官克里斯托弗·达顿,也在 2017 年 7 月花了一点时间向 AMA 社区开放。Elon Musk 和 Reddit 的新任首席执行官 Steve Huffman 帮助提高了商业话题的投票率。

我希望我能够扩大那些对数据科学感兴趣的人和那些通过 PRAW 新连接到 Reddit 的 API 的人的研究潜力!

对于未来的文章,请联系我们,提出任何问题、评论、想法或想法。

感谢您的阅读,

亚历克斯

详细 API 连接&探索代码,查看我的 GitHub

有关充分利用 PRAW 的更多详情,请查看 文档

我们连线上LinkedIn

探索推特上的右翼极端主义

原文:https://towardsdatascience.com/exploring-right-wing-extremism-on-twitter-941a9d02825e?source=collection_archive---------16-----------------------

介绍

在过去的几个月里,我们已经看到社交媒体网站如何成为极右翼观点的避风港。《纽约时报》探究了 YouTube 如何通过其推荐算法将用户推向激进内容, ProPublica 详细介绍了脸书的一个组织,ICE 员工在那里分享关于移民的种族主义迷因。Reddit 也因托管极端右翼内容而受到的争议。Twitter 是这场争论中相对毫发无损的一个社交媒体网站。这很大程度上是由于社交网站的分布式特性。用户不会被聚集到容易识别的论坛或子区域中,而是每个账户作为一个独立的实体存在。虽然拥有许多追随者的知名用户被禁止(如亚历克斯·琼斯),但更难识别推广极端主义内容的个人网络。这是我所知的第一次大规模分析 Twitter 上的极右极端主义。

为了调查这个问题,我分析了来自两个极右翼极端主义用户的 297849 个 Twitter 账户。我的发现确定了一个约有 19,000 名用户的网络,这些用户与极端主义内容高度接近。著名的右翼人物,如查理·柯克和瑞安·福尼尔,在这个网络中的影响力不亚于公开的种族主义报道。

调查的结果

最接近极端主义内容的用户群中最有影响力的前十个账户可以归类为毫不掩饰的白人民族主义者和另类右翼巨魔账户。这些用户公开分享种族主义迷因,宣扬白人至上主义。四个最著名的用户是@NoWhiteGuilt、@Outsider__14、@esc_press 和@Urbanus_Crusade

Top ten most influential users by PageRank

在下面的推文中,@Urbanus_Crusade 将他们的“MAGA 议程”列为包括“欧洲同质人口”:

Tweet by @Urbanus_Crusade

@esc_press 分享了无忧无虑的白人的老照片,标题通常是“按[esc]键返回”,暗示已经失去的田园时光。在一条现已删除的推文中,他们通过分享这张图片揭露了他们的种族主义议程:

Image shared by @esc_press

@Outsider__14 主要分享推文,充当分发极端主义内容的枢纽。在这里,他们转发了一个用户赞扬白人民族主义者联合权利的抗议:

这四个白人民族主义用户总共有 21607 名粉丝,他们在推特上发布这种内容。总的来说(减去已删除的账户),前 10 个账户共有 354,602 名关注者。

令人惊讶的是,在网络中发现了大量的土耳其用户。前 10 名中被删除的用户是一名受欢迎的土耳其记者,他似乎与右翼极端主义没有太大关系。土耳其账户是一个意外的发现。这似乎表明有大量的土耳其人对右翼主题的内容感兴趣。甚至可能是他们中的一些人在幕后操纵这些账户,但目前我只能推测。

一旦我对这个网络中的内容类型有了了解,我就开始关注拥有最多追随者的个人。这揭露了一些著名的右翼人物:

Popular right-wing personalities by PageRank

我们找到 Ryan Fournier,Trump 的学生主管,他的 PageRank 是 16。从这个角度来看,这意味着在这个网络中,瑞安·福涅尔的影响力不亚于发布这条微博的人:

Tweet by @AMERIOPA

前国会候选人查克·卡列斯托排在第 46 位,“转折点美国”的负责人查理·柯克排在第 153 位。紧随查理·柯克之后的是@PayYourG0yTax(现已暂停),让你感受一下这些人物的陪伴。

过程

从 2019 年 5 月开始,我开始收集极端右翼推特账户的数据。开始,我选了两个账号:@NoWhiteGuiltNWG 和@Goy_Talk_USA。这两个用户都运营着 YouTube 频道,他们在那里制作视频宣传白人民族主义理论,并且在 Twitter 上也非常活跃。下面是@Goy_Talk_USA 发来的一条推文,让你了解一下他们宣传的内容:

我收集了这两个账户的关注者的数据,然后是他们每个人的关注者,以此类推,直到我建立了一个拥有 297,849 名用户的网络。对于每个用户,我计算了他们与两个原始极端主义用户的距离。下图对此进行了说明:

Goy_Talk_USA is depth 0. Each of their followers is depth 1. Their follower’s-followers are depth 2.

我根据深度将网络中的用户分为三组。距离最初两个极端分子账户 0-2 深度的用户是最接近的。距离 3 个深度的用户为中等,距离大于 3 个深度的用户为最远。最接近可以解释为最接近极端主义内容的用户,中等和最远被几层用户移除。总的来说,我在最接近的组中找到了 19,825 个用户。

下图显示了这个网络。网络的核心是绿色最接近的用户。规模更大、更温和的红色媒体用户从他们那里扩散开来。最后,我们看到最远的用户要么自己孤立,要么在网络的末端。

下一步是使用一种叫做 PageRank 的算法来计算网络中每个用户的连通性。这将根据用户在网络中出现的次数为他们分配一个号码。经常出现的用户会有较高的数字,而不经常出现的用户会有较低的数字。它可以被解释为一种衡量某人在网络中的影响力的方法。

摘要

这些发现指向一大群用户在 Twitter 上推广极端右翼内容。其他社交媒体平台已被追究其平台上极端主义活动的责任,并已采取行动遏制此类内容。然而,Twitter 允许这些网络相对不受约束地继续发展。所有这些账户都是公开的,这意味着任何人只需点击几下鼠标就可以查看和阅读极端主义言论。持续展示这些内容有助于将仇恨言论正常化,并使个人变得激进。我希望这项研究将有助于揭示 Twitter 上存在的极端主义的规模和范围,并有助于社交媒体平台上正在进行的关于极端主义的讨论。

密码

这个项目分为两部分:从 Twitter 上收集数据,然后进行分析。下面是这两个步骤的 GitHub 库。

[## nbucklin/twitternet 网络

这个程序从单个用户开始生成一个 Twitter 追随者网络。对于第一个用户,它将检索…

github.com](https://github.com/nbucklin/TwitterNetwork) [## nbucklin/BigNetworkPython

此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…

github.com](https://github.com/nbucklin/BigNetworkPython)

使用 Dash、Flask 和 Heroku 构建的交互式 Webapp 探索 SG 的租赁——第 1 部分

原文:https://towardsdatascience.com/exploring-sgs-rentals-with-an-interactive-webapp-built-using-dash-flask-and-heroku-part-1-cccd9e8dd1b8?source=collection_archive---------26-----------------------

通过构建 DS/ML Webapp 并将其部署到世界各地来升级

1.介绍

您是否曾经研究过一些有趣的数据科学问题,并获得了一些真正有趣的见解或 ML 解决方案,但最终却意识到您所拥有的只是一个装满代码的 Ipython 笔记本,而这些代码将永远不会到达观众手中?或者,它可能只会被你的同道 DS 从业者阅读,你已经花了相当多的时间说服他们克隆你的回购协议,下载你的笔记本——出于对你的尊重——努力让通读整本书,最终实现你的才华

你不满足了。一方面,你希望你的工作被更广泛的受众欣赏,不仅仅是你的技术同行,也包括你的非技术同行。也许你想把它分享给你的朋友,他对 DS 很好奇,但可能不完全理解代码,仅仅是因为他/她从未真正潜过那么深。也许你想向你的非工程师朋友、同事、老板甚至是《商业分析 101》中坐在你旁边的可爱女孩/男孩炫耀。

另一方面,你觉得也许你本可以做得更多,你需要加快你的步伐来制作一些更最终的东西,一些或多或少类似于成品的东西,这样你就可以宣称“啊哈!我建造了这个。这不是很酷吗?”

在我最近的项目中,我一度有这种感觉。我决定通过构建一个完全交互式的 web 应用程序将它提升到一个新的水平——尽管我以前从未构建过——在那里用户可以处理数据,构建他们的模型,挑选他们的重要特征并获得某个预测/结果

最后,我成功了——构建了我的第一个 ML Webapp。你可以在这里查看 app 。这是一个关于探索新加坡租赁物业的应用程序,我就住在那里。

该应用程序由 3 部分组成

A. 第一部分 允许用户通过指定最小&最大租金来处理数据,然后选择他们感兴趣的功能和物业类型,应用程序返回一些 交互式可视化效果

B. 第二部分 允许用户提出一个假设的物业,应用程序将尝试 预测其租金 使用由用户通过一些引导性问题就地 建造的模型

C.

我使用 Flask 作为我的 Webapp 框架来创建我的服务器端 Python 程序,使用 Plotly Dash 来为我的交互式可视化服务,使用常见的怀疑对象 Numpy、Pandas、Scikit-learn 作为工具来构建我的程序的各个部分。最后但同样重要的是,我使用免费的应用托管网站 Heroku 来部署我的工作。看看这里:https://www.heroku.com/home

对于那些对学习如何构建这样的应用感兴趣的人,请系好安全带,因为我将在后续章节中更详细地解释这些步骤。

2.了解所需的库

A .烧瓶

Flask 是基于 Werkzeug、Jinja 2 的 Python 的 web 开发微框架。你可以在这里查看它的官方网站http://flask.pocoo.org/。它允许您设置不同的路由/视图(稍后将详细介绍),这些路由/视图与您的 web 浏览器的 HTTP 请求进行交互,并执行一些功能/任务来将某些内容返回到浏览器或呈现的 Html 页面。您可以使用 pip 通过一个简单的命令下载该框架

*pip install -U flask*

最新版本是 Flask 1 . 0 . 2——你可以在这里测试一个简单的 Flask 应用程序

*from flask import Flaskapp = Flask(__name__)@app.route('/')
def hello():
    return 'Hello, World!'if __name__ == '__main__':
 app.run(port=8080, debug=True)*

这将在 localhost:8080 提供应用程序供您查看

B .破折号

Dash 是一个 Plotly 产品,它是一个用于交互式可视化的 Python 框架,作为 web 应用程序的一部分。你可以在这里查看它的官方网站https://plot.ly/products/dash/。它允许你收集不同的元素,如下拉菜单/滑动条/条形图/地图,这些元素可以相互交流,形成一个布局,并在 web 视图中提供

  • 您可以在这里安装组件
*pip install -U dash
pip install dash_core_components
pip install dash_html_components*
  • 您可以测试写在主介绍页面上的示例程序。请记住指定以下内容
*app.scripts.config.serve_locally = True
if __name__ == '__main__':
    app.run_server(port=8080)*

C. Wtforms

Wtforms 是一个很好的 API,允许你定制你的前端表单,它接受用户的输入并把它们提供给你的后端应用。该 API 支持大量流行的 Python web 开发框架,包括 Flask 和 Django。你可以从这里的文档中阅读更多关于如何使用 wtforms 的内容https://wtforms.readthedocs.io/en/stable/

  • 您可以安装它
*pip install -U dash*

D .通常的嫌疑人——Numpy,Pandas,Scikit-learn,Seaborn…

这些是清理、准备、探索和分析数据以及构建机器学习模型来处理这些数据的基本包。对使用 Python 实践数据科学略知一二的读者应该熟悉这些包。如果没有,随时欢迎您查看我以前讨论其他项目的文章

  • *** 现在所有的框架/包都有了自己的介绍,让我们继续探索如何将它们结合在一起交付一个最终的工作产品*

3.构建所需的模块

实现该项目需要几个模块/组件:

A.具有完全定义的布局的嵌套 Dash 应用程序

B.配置用于主 HTML 页面的输入表单

C1。主 HTML 页面由带有输入表单的不同部分组成

C2。由主程序的不同路径/视图提供的其他 HTML 页面

D.要在主应用程序中调用的助手函数

  • 让我们讨论每一个组件

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

组件 A —仪表板应用

  • app_dash = Dash 应用的名称。还为仪表板配置了一个名为 '/dashboard' 的单独视图

  • 首先为 Dash 应用程序设置虚拟布局。

  • '/哈哈哈'视图将被触发&每当 form4 提交请求 时,应用程序的布局将填充一些交互图表*。*

  • 构建图的参数取自用户在表格 1 的输入。这意味着用户需要确保在他/她触发图表视图之前提交form 1(该操作还会导致交互式图表的创建并将 plot_charts_cue 更改为 True)

  • 否则,如果用户未能首先提交 form1,将首先显示一个error.html页面

  • 交互式图表是用打包的 Plotly 的图形对象构造的(导入为 go )。每个图表由一个图表布局和一组元素组成。这些元素有很多选择(go。走吧。散开,走。Map…),这取决于您想要绘制的图表类型

  • 下面是一个如何创建交互式条形图的示例

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

组件 B——配置的输入表单

输入表单可以通过以下方式进行配置:首先定义一个基于表单的扩展表单类,然后定义表示表单中需要填写的信息的不同变量名,以及这些变量的输入字段类型。

例如,第 1 部分(修改数据集)和第 3 部分(搜索相似列表)的两个表单类是:

  • 您可以使用验证器对输入字段强制执行一些规则。关于如何使用它们的更多细节,请参见文档
  • 在主index.html页面上,如果验证器不满足要求,将会闪烁错误消息(由您指定)。如果输入值的类型不同于指定的字段类型,该字段还会闪烁其默认错误消息

然后,这些表格通过以下代码集成到主程序中。

*from flask import requestform1 = ResuableForm(request.form)
form3 = ReusableForm_SimFilter(request.form)//Parse the forms to the rendered main Html page
@app.route('/')
def some_func():
    //Do sth
    return render_template('index.html', form1=form1, form3=form3*

最后但同样重要的是,在 index.html 页面上,应该有两个表单部分,它们的各自的 id应该分别是‘form 1’和‘form 3’。稍后将详细介绍如何构建这个页面

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

组件 C —所需的 HTML 模板

这个项目使用的所有 HTML 模板都放在一个“template”文件夹中,这个文件夹与主程序放在同一个目录中。这是 render_template 函数的一个要求。您应该对自己项目的模板做同样的事情

用于主视图的真实 index.html 要长得多,由许多形式组成。但是,我将简化这种情况,只使用一个表单(获取参数以修改数据集所需的表单)来呈现模板的简短版本。看看下面表单的代码:

** * 有一些解释可以帮助您更好地理解这种形式:

  • 是如何在模板内部声明一个表单节。属性 method = post 决定了表单提交后将向后端程序发送何种请求。
  • 可以使用属性 action = 'view_name' 来指定表单提交将触发哪个 Flask route/view。如果未指定属性,提交将默认将其数据发送到主视图
  • 在模板中调用 form1。因此,当页面被渲染时,form1 首先需要被定义、赋值并作为参数被解析到 render_template()函数中
  • 属于 form1 的字段可以调用为 form1.field_name 并由模板进行相应的处理
  • 您也可以使用 <选择></选择> 来配置分类特性的下拉值选择,而不是使用空白输入字段
  • 还要注意闪烁错误消息是如何在模板中配置的
  • 关于表单的 CSS 样式,请自己阅读要点
  • 如果你想访问完整的 index.html 版本,你可以在项目目录 的文件夹 Flaskapp/template 里面查看一下

在主页的顶部,您的模板文件夹应该包含其他模板(例如由一个视图返回的模板,显示您的模型试图预测的结果)。我使用另外两个模板,一个是 prediction_result.html ,用于返回第 2 部分的答案,另一个是 filtering_result.html ,用于返回第 3 部分的答案。每当程序的某些条件不满足时,我还使用各种各样的模板向用户返回一些带有说明的错误页面。下面显示了一个示例

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

组件 D —辅助功能

我的项目需要许多助手功能。例如:

  1. 为 Dash 应用程序绘制交互式图表的函数(讨论组件 B 的部分展示了一个这样的例子)
  2. 基于用户偏好修改数据集的函数
  3. 使用 Pyspark 或 Sklearn 库(取决于用户的选择)为第 2 部分创建和训练预测模型的函数
  4. 第 2 节中使用的函数,用于将测试实例转换为适合已训练模型的格式,并返回预测
  5. 第 3 节中使用的函数,用于将测试实例转换成适合已训练模型的格式,并返回类似的结果

此类功能的一个示例如下所示。这就是上面第 5 点提到的功能。该函数建立一个最近邻居的学习模型,并使用它根据用户给定的权重搜索相似的列表:

我不会继续展示这个项目中使用的所有助手函数。如果您有兴趣深入阅读它们,可以通过我的 Github 库查看项目目录

最后……

在我们创建完 Flaskapp 所需的所有模块之后,是时候构建项目的最后也是最重要的部分了:主程序。看看这篇文章的第二部分,自己看看如何做到这一点。

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

使用 Dash、Flask 和 Heroku 构建的交互式 Webapp 探索 SG 的租赁——第 2 部分

原文:https://towardsdatascience.com/exploring-sgs-rentals-with-an-interactive-webapp-built-using-dash-flask-and-heroku-part-2-24656906ccbb?source=collection_archive---------18-----------------------

通过构建 DS/ML Webapp 并将其部署到世界各地来升级

** 这是 2 篇系列文章的第 2 部分。查看 第一部 这里!

在上一篇文章中,我们讨论了如何构建不同的组件来形成 Flaskapp。现在是时候将它们集成在一起了

4.把所有东西放在主程序中

这是万物融为一体的地方。主程序配置后端 Flaskapp,并定义它如何响应来自前端的请求。所有子模块都在这里调用

********* 主程序的 骨架 如下图

***import ___                       //whatever packages you need
***import ___                       //whatever helper funcs needed
from flask import Flask, request, render_template
from dash import Dash
from dash_core_components import ____
from dash_html_components import ____
from wtforms import Form
from wtforms.fields import _____    //choose necessary fields**app** = Flask(__name__)
**app_dash** = Dash(__name__, server=app, url_base_pathname = '/dash/')
app_dash.layout = ____              //set up your dash layoutform1 = Form(request.form)
form2 = Form(request.form)
//Form can be customized using extented class and wtforms.fields**@app.route**('/', methods=___)        //main view, choose methods
def some_function():
    if request.form = form1
        //Do something
        **return** ___ 
    elif request.form = form2
        //Do something
        **return** ___
//something to the server OR render_template('somepage.html')**@app.route**('/route1', methods=___)  //new view, choose methods  
def some_function():
    //Do something
    **return** ___ 
//something to the server OR render_template('somepage.html')**@app.route**('/route2', methods=___)  //new view, choose methods   
def some_function():
    global app_dash
    //Do something
    app_dash.layout = ___           //change your dash layout  
    **return** flask.redicrect('/dash/')
//direct the flask app to display the Dash dashboardif __name__ == '__main__':
 **app**.run(port=8000, debug=True)

主程序由一个或多个视图组成。每个路线/视图都有一个代码部分。每个路线/视图最终都必须返回一些东西

一旦你理解了上面的框架,你就可以开始阅读我的复杂得多的主程序,而不会被它包含的代码行数所淹没。此程序的更多解释如下

** * 我的主程序的一些说明

  • 应用 = Flask app 的名称。每个 @application.route() 代码块定义一个路径/视图
  • 主程序有 3 个视图 : A)主视图'/',B)第二视图'/哈哈哈'和 C)第三视图'/seaborn '
  • ReusableForm(Form) 是一个将基本表单类扩展为具有可定制输入字段的新表单类的例子
  • 如果没有来自前端网络浏览器的请求,主视图提供由 5 个表格组成的index.html页面。第一节
  • 如果有来自表单 1发布请求,应用程序修改数据集,重新显示带有一些通知文本的index.html页面
  • 如果有一个来自 form2POST 请求,app 会创建并训练一个 ML 模型,然后用它来预测所提议房产的租金,并返回 prediction_result.html 页面显示预测
  • 如果有一个来自 form3、POST 请求,应用程序会搜索一些的与提议的酒店相似的房源,并返回 filtering_result.html 显示这些房源的详细信息
  • 如果有一个来自 form4 的 POST 请求,应用程序触发第二个视图@ application . route('/哈哈哈'),这将改变 Dash 应用程序的布局,然后将用户重定向到其仪表板
  • 如果有一个来自 form5、POST 请求,应用程序会触发第三个视图@ application . route('/seaborn '),返回一个显示图表的页面
  • 方法:指定路由/视图可以处理来自前端网页的哪种 HTTP 请求(' POST '或' GET')
  • request.form 用于定义一个表单。此外,它还用于接收后端程序所需的 用户输入
  • request.method 用于在每个视图中构造 if 子句,分别处理不同类型的 HTTP 请求。
  • render_template :该函数用于在触发路线/视图时返回一个 Html 页面。模板内部调用的表单和变量需要作为参数解析到 render_template()函数中
  • 全局用于每个视图内的变量,因此每个视图内的动作导致变量的永久变化,这些变化可以在整个程序中共享
  • port=8080 将允许您通过浏览器中的 localhost:8080 在本地部署应用程序

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

5.在 HEROKU 上部署完成的应用程序

在你完成主程序并确保程序中不再有 bug 之后(重要!—在本地运行,直到你对此非常有信心),然后你就可以把它部署到 Heroku 上,这是一个面向业余开发者的流行的免费应用托管网站

创建帐户

但是,在您做任何事情之前,您需要首先通过访问该网站创建一个帐户,除非您已经有了一个帐户。

创建您的帐户并登录后,您将看到一个可以管理您的应用程序的仪表板。您可以选择从那里配置您的应用程序,或者下载适用于您的操作系统的 Heroku Cli 并使用客户端配置您的应用程序。我将在这里展示第二种方法

安装 CLI 客户端后,在您的终端中运行以下命令,登录到您的 Heroku 帐户

$ heroku login

出现提示时,输入您的凭据(电子邮件和密码)。

创建 Heroku 应用程序

部署过程的下一步是您需要创建一个 Heroku 应用程序。为此,请在您的终端中运行以下命令;

$ heroku apps:create unique-name-of-your-app

此操作将在 Heroku 上创建一个应用程序,并为您上传文件创建一个存储库

准备 Git 存储库

在这个阶段,我假设您应该已经建立了一个本地 git 存储库。您需要通过运行命令向现有的本地目录添加一个新的远程上游

$ git remote add heroku YOUR_HEROKU_REPO_ADDRESS

另一方面,如果您还没有设置存储库,请将创建的 Heroku 存储库克隆到您的本地目录中,并将您的工作文件复制到该文件夹中。

选择合适的 web 服务器

Flask 对于开发阶段来说是一个很好的 web 服务器,但对于生产环境来说不是最佳选择。建议使用更好的 web 服务器。最受欢迎的两个是 Gunicorn 和 waste。你可以在这里查看这些服务器的列表。我在我的应用程序中使用 waste,因为我发现它有助于应用程序更好地运行,因为它能够缓冲请求和响应。另一方面,Gunicorn 导致我的应用程序给出不一致的响应,可能是因为它没有缓冲能力,无法处理我的应用程序的缓慢进程。此外,这里的一篇文章解释了 Gunicorn 不是 Heroku 应用程序最佳选择的一些原因

您可以用这个命令下载这两个文件

$ pip install waitress
OR
$ pip install gunicorn

创建 Procfile

procfile 告诉 Heroku 如何运行你的应用程序。该文件没有文件类型扩展名。用简单的命令在存储库中创建一个

$ touch Procfile

将下面一行添加到配置文件中。我为 Guniconr 和 waste 都举了例子。此外,“文件名”是您的主程序文件的名称,而“应用程序名”是您的程序中配置的 Flask 应用程序的名称。

$ web:gunicorn -w:? file_name:app_name
OR
$ web:waitres-serve --port=$PORT -w:? file_name:app_name
\\? is the number of threads/workers to be started by the server.

准备 Requirements.txt 文件

这个文件告诉 Heroku 哪些库/包要和你的应用一起安装来运行它。它包含包的名称和每个包的版本号。如果您不确定您当前使用的是什么版本,您可以使用“$ pip list”命令进行检查。记得把新下载的网络服务器放在其中

这是我自己的 requirements.txt 文件的一个例子

gunicorn==19.9.0
waitress==1.2.1
numpy==1.16.2
pandas==0.23.0
matplotlib==2.2.2
seaborn==0.9.0
pyspark==2.3.0
scikit-learn==0.20.3
plotly==3.3.0
flask==1.0.2
flask_sqlalchemy==2.3.2
wtforms==2.2.1
psycopg2==2.7.7
dash==0.38.0
dash_core_components==0.43.1
dash_html_components==0.13.5

但是,如果您一直在使用虚拟环境开发您的应用程序,那么您只需运行一个简单的命令,就可以帮助您在一瞬间创建这个文件

$ pip freeze > requirements.txt

指定合适的构建包

在部署到 Heroku 之前,您需要指定您的应用程序将使用哪个(哪些)构建包。您可以通过运行以下程序来添加它们(根据您的应用程序的语言来更改构建包)

$ heroku buildpacks:set heroku/python

* *添加数据库(可选)

如果您的应用程序需要数据库服务器,Heroku 提供免费或付费的 Postgresql。自由层版本最多只允许 10,000 行和 20 个并发连接。

实际上,为应用程序设置数据库并配置它与数据库进行交互需要几个步骤。

步骤 1 :要创建一个数据库,运行以下命令

$ heroku addons: add heroku-postgresql:hobby-dev
\\hobby-dev is the free tier mentioned

您可以从应用仪表板查看数据库的连接详细信息。只需转到新创建的数据库部分>点击设置>查看凭证。在这里你可以看到数据库的 URI 被配置为一个名为$DATABASE_URL 的环境变量。数据导入过程和程序设置都需要这个变量。

步骤 2 :通过添加下面的代码片段来修改您的 flask 应用程序,以便它可以连接到数据库。

import os
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ['DATABASE_URL']
\\app is your Flask app's name

第三步:将您的数据导入 Heroku

我将假设您的数据以 CSV 文件的形式出现。如果您已经有了本地 PostgreSQL 数据库中的数据,或者 SQL 文件形式的数据,那么您实际上可以跳过下面的一些步骤

**STEP 0\.** Download and install PostgreSQL on your computer **STEP 1**. Create a local Postgres Database & Table (with name=Tablename) with PGAdmin**STEP 2**. Run the following command
$ psql -U user_name -d database_name -c "\copy Tablename FROM '\file directory\file.csv' WITH CSV;"
\\ Transfer the csv file to local database\\If you have many columns, table creation can be a headache. For such case, you can use a tool called pgfutter to automate table creation with some datatype identification. Check out this [answer](https://stackoverflow.com/questions/21018256/can-i-automatically-create-a-table-in-postgresql-from-a-csv-file-with-headers) on stackoverflow**STEP 3**. Push from local database to remote Heroku database
Method1:
A. pg_dump -U user_name -d database_name --no-owner --no-acl -f backup.sql
B. heroku pg:psql -app app_name < backup.sqlMethod2:
A. heroku pg:reset //make sure remote database is empty
B. heroku pg:push local_database_name --app app_name

步骤 4 :创建 SQLAlchemy ORM 对象,以便从 Flask 应用程序内部与数据库进行通信。您首先需要安装软件包

$ pip install flask_sqlalchemy

然后你把这些代码添加到你的程序中

from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy(app)
\\app is your Flask app's name

下面是代码要点,展示了我如何使用我的 ORM 与数据库通信,查询它,用一些用户的输入条件过滤查询,并返回一个结果数据帧。我在这里使用反射从步骤 3 中上传的现有数据库中加载信息

或者,您可以通过用 db 声明 table 类来创建一个全新的表。在 Flask 应用程序中建模,并使用 db.session.add()db.session.commit() 将数据添加到表中。当你的应用有一些允许用户操作/更新数据库的特性时,这是很有用的。你可以看看这篇文章了解更多关于如何做的细节

* *为 Heroku 定制

在将应用程序部署到 Heroku 时,有两件事需要注意

  1. Heroku 自由层(和一些低级计划)只有低内存分配 (512MB)。确保您的进程/应用程序在此限制内使用资源。如有必要,自定义您的应用程序
  2. Heroku 有一个 30 秒超时策略。这意味着对于 web 客户端请求,您的后端服务器有 30 秒的“期限”来返回响应。如果你的服务器需要更长的时间,应用程序将崩溃

我用来绕过第二条规则的一个技巧(我的一些过程,比如模型训练或交互式情节准备需要超过 30 秒)是让应用程序以某种形式的状态返回给服务器,关于正在运行的过程,在指定的间隔小于 30 秒

<h3 class=”heading-3-impt-red”>Click <button style=”font-size:20px” **onclick=”myfunc()**”>Here</button> to Start Preparing Interactive Scatter Plot
</h3><script src="[**https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.j**s](https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js)"></script>
<script>

  **function myfunc(){** alert("Preparing Plot Now...");
  count = 0;
  var interval = setInterval(function(){**wowwah()**; count++; if (count>=4)clearInterval(interval);},8000); // call every 8 seconds
  };

  **function wowwah(){** $.ajax({
  url:"/hehehe", //the page containing python script
  method:"GET",
  dataType:"json",
  success:function(result){ alert(result); }
  });}
</script>

我在我的 HTML 模板中使用了这个脚本。每当调用 wowwah()函数时(每 8 秒一次),视图“/hehehe”以 jsonify()对象的形式向浏览器返回一个状态文本。

部署到 Heroku

最后,当所有工作完成后,你可以将应用程序部署到 Heroku,就像你将你的东西部署到 Github 一样。确保您准备好所有内容,并更新您的。git 如果需要,忽略文件

$ git add *
$ git commit
$ git push heroku master
// change heroku to origin if you previously clone the heroku repo

your _ app _ name . heroku . com访问您的应用程序,看看它是否有效。如果它不像预期的那样工作,您可以在控制台中键入“$ heroku logs-tails”来找出错误是什么。如果是的话,那么恭喜你成功了!您已经构建了您的第一个 DS webapp

6.结论

通过 Flask 应用程序将您的 DS/ML 项目部署到 Heroku,使您能够将您的想法和发现转化为工作产品,并向全世界展示。对于那些新手来说,这应该让你体会到当公司聘请数据科学家/软件工程师团队来交付数据科学项目时,他们真正关心的是什么:一个成品生产级模型/软件,可以在需要时为他们提供可操作的见解,而不仅仅是一份报告或一个代码笔记本。

尽管我自己在这方面还是个新手,但我希望我的文章能够对您的学习之旅有所帮助,并成为数据科学艺术的更好实践者。如果是的话,我很乐意看到你的一两条评论来解释这是怎么回事。如果您能给我一些反馈,告诉我如何更好地改进我的帖子/文章,我将不胜感激

感谢大家一直关注到这个阶段!不要忘记关注我的下一篇文章。

Huy,

探索悉尼郊区,开一家新餐馆

原文:https://towardsdatascience.com/exploring-sydney-suburbs-for-opening-a-new-restaurant-e1e43a6c32c7?source=collection_archive---------23-----------------------

Sydney harbour bridge photoed by Jun

Python 数据科学的魔力

在过去的几个月里,我一直在学习 Coursera 的 IBM 数据科学课程。在学习完本课程的所有 Python 基本技能和工具后,下面是最后一个作业。这个任务的目标是定义一个与我选择的城市相关的商业问题,然后通过使用 Foursquare 位置数据来解决它。

虽然这是一项任务,但它非常类似于数据分析师在日常工作中需要解决的真实世界的问题,包括问题定义数据收集数据清理数据分析数据可视化报告形成。在整个作业中,我使用了网络抓取、缺失值插补、Foursquare API、follow map 和 k 均值聚类。

我希望你能从这篇文章中收集信息或找到有用的数据。欢迎留下任何建议和意见。详细的代码/笔记本/数据也可以在这里找到。

1.商业问题

你在一家专门从事餐厅营销的精品咨询公司工作。一位客户希望在悉尼新开一家意大利餐厅,但是他不确定新地点的最佳位置和营销策略。你被指派帮助他制定一个全面的营销计划。悉尼是一个充满活力的城市,一年四季都有精彩的活动。它以其旅游景点和田园海滩而闻名。漫步在城市郊区,如果没有数据驱动的方法,它很难在现有的竞争对手中找到一个开辟新餐馆的干净利基。你会如何建议你的客户使用数据科学来决定他的餐厅位置?

2.数据收集/清理

综合自创业出版社出版的披萨店、咖啡馆、熟食店、面包店、餐饮企业以下要素被认为是选择餐厅位置的关键因素:人口统计、交通密度、犯罪率、竞争对手和房价承受能力。我们可能无法在如此短的时间内解决所有这些因素,但是,可以通过使用可用的公共数据集或网络搜集来解决其中的一些问题。

2.1。维基百科上的悉尼郊区列表

由于整个分析是基于位置的,我们应该考虑悉尼所有郊区的详细坐标。从政府网站不难找到新南威尔士州所有郊区的 geojson 文件。但是,挑战在于选择悉尼市区内的那些郊区?在这里,我使用 web 爬行方法,使用requestsBeautifulsoup4维基百科中删除了一个列表。

此外,我也刮了所有悉尼郊区的邮政编码列表从这里使用相同的方法。经过一些修改,我得到了下面的数据帧:

 Suburb        State    Postcode    Suburbs_with_boundries
0    Abbotsbury    New South Wales    2176    ABBOTSBURY
1    Abbotsford    New South Wales    2046    ABBOTSFORD
2    Acacia GardensNew South Wales    2763    ACACIA GARDENS
3    Agnes Banks   New South Wales    NaN     AGNES BANKS
4    Airds         New South Wales    2560    AIRDS

检查最终表以查看在该过程中是否生成了任何不正确的行是非常重要的。例如,“Agnes Banks”在最终表中没有邮政编码,但是它实际上有一个。这对于后面的步骤非常重要。

2.2。悉尼人口统计和郊区房价中位数

找了好一阵子后,我找不到任何关于悉尼郊区人口统计和房价中位数的数据表。最后,我再次使用网络爬行从获取人口统计数据(人口和年龄范围),从房地产获取房产中值价格数据。这是澳大利亚两个很受欢迎的房地产网站。您可能已经注意到,查找给定郊区的 url 结构只是域名链接、郊区名称、新南威尔士州和邮政编码的组合。

url = 'https://www.domain.com.au/suburb-profile/{}-nsw-{}'.format(            suburb,postcode)

下面是我在这个任务中使用的主要函数的一个例子:

2.3。房产中值价格的缺失数据插补

从网上抓取数据永远不会完美。那很好。重要的是,我们需要有自己的假设来预测基于不同情况的缺失值。在我的例子中,人口、年龄范围、购房中间价、租房中间价、单位购房中间价和单位租房中间价都有缺失值。

在研究了人口统计表后,我发现人口或年龄范围值缺失的郊区通常是偏远郊区,要么年龄范围为 60 岁以上,要么人口为 0。然后,我相应地估算所有这两种类型的缺失值。像这样:

sydney_demography_data[['Age']] = sydney_demography_data[['Age']].fillna('60+')

房地产价格中位数的情况有点复杂,如线性回归图所示。我们在不同的对之间有非常不同的线性关系。

Linear regression between two parameters

为了简化问题,我决定用购房中值价格作为房产负担能力的指标。由于购房中间价和租房中间价之间存在明显的正相关关系,我为这两个参数建立了一个简单的线性模型。然后,该模型用于预测这两个参数的缺失值。

2.4。人口、年龄范围和财产负担能力分布图

在这个阶段,我们已经有了解决人口统计和房产负担能力所需的数据。让我们把它们放在地图上以便有一个清晰的视野。以下是该任务的示例代码:

地图看起来像这样,它们可以从这里下载。

Choropleth map for age range, population, and property affordability

2.5。使用 Foursquare API 检索所有悉尼郊区的场馆

有了最终的悉尼郊区列表,我们就可以使用 Foursquare API 检索每个郊区的场地列表。Foursquare 是一个强大的位置信息数据库。你基本上只需要通过radiusLIMITlatitudeslongitudes就可以得到定义半径内特定地点的场馆列表。注意,你需要注册 Foursquare API,拥有自己的CLIENT_IDCLIENT_SECRET才能使用 Foursquare 的服务。它的免费版本足以进行这种类型的分析。试试吧,你会喜欢的。这是我在这个任务中使用的函数。

3.数据分析

现在数据终于准备好了。我们来看看数据。正如我们在这里看到的,只有 565 个郊区归还场馆。这可能是因为任意选择的郊区中心并不是真正的郊区中心,我们应该在未来找到一种更好的方法来定义郊区中心。但总的来说,这个列表非常接近现实。

Code for list top 20 suburbs with the most venue numbers

565
Suburb    Venue
0    Darlinghurst    100
1    The Rocks    100
2    Surry Hills    89
3    Crows Nest    86
4    Newtown    85
5    Haymarket    83
6    Chippendale    79
7    Millers Point    73
8    Burwood    72
9    Sydney    72
10    North Sydney    68
11    Dawes Point    64
12    Woolloomooloo    59
13    Randwick    57
14    Enmore    57
15    Milsons Point    55
16    Rushcutters Bay    53
17    Coogee    53
18    Waterloo    51
19    Paddington    51

由于我们的任务是从 698 个郊区挑选一些候选人,我们可能会发现首先根据这些郊区最常见的场地对它们进行分组是有用的。这背后的想法是寻找以餐馆为特色的郊区。这种类型的郊区可以为新的企业提供稳定的客户来源。因此,我在这个任务中使用了 k 均值聚类:

我们发现第二组中的郊区以餐馆、酒吧和咖啡馆为特色。然后,我们缩小了第二组中的候选郊区列表。在这个集群中,我们需要确定相对较低的房地产价格,高人口密度和中年郊区。为了能够看出这一点,让我们使用一个散点图,用Population作为 X 轴,House_buy/M作为 Y 轴,Age作为色调。

plt.figure(figsize=(10,10))
ax = sns.scatterplot(x="Population", y="House_buy/M", hue='Age', s=80,
                     data=sydney_demography_data_cluster[sydney_demography_data_cluster['Cluster Labels'] == 2])

Scatterplot to find candidate suburbs

如散点图所示,我们可以确定有 5 个郊区符合我们的要求。他们分别是查茨伍德、兰德威克、马里克维尔、帕拉马塔和莱德。进一步研究这 5 个郊区的餐馆概况,考虑到餐馆类型的多样性,Randwick 和 Chatswood 都很突出。在一个特定的郊区,不同的餐馆类型可能意味着当地的顾客愿意尝试新事物,从而为我们的新餐馆提供相对容易生存的经营环境。

----CHATSWOOD----
Chinese Restaurant       4
Thai Restaurant          2
Sushi Restaurant         2
Malay Restaurant         2
Udon Restaurant          1
Japanese Restaurant      1
Arepa Restaurant         1
Szechuan Restaurant      1
Portuguese Restaurant    1
Dumpling Restaurant      1
Name: 111, dtype: object ----MARRICKVILLE----
Vietnamese Restaurant        8
Thai Restaurant              2
Fast Food Restaurant         1
Greek Restaurant             1
Japanese Restaurant          1
Indonesian Restaurant        0
Indian Restaurant            0
Indian Chinese Restaurant    0
Grilled Meat Restaurant      0
Dumpling Restaurant          0
Name: 321, dtype: object ----PARRAMATTA----
Japanese Restaurant        1
South Indian Restaurant    1
Seafood Restaurant         1
Lebanese Restaurant        1
Asian Restaurant           1
Australian Restaurant      1
Chinese Restaurant         1
Falafel Restaurant         0
Fast Food Restaurant       0
Filipino Restaurant        0
Name: 402, dtype: object ----RANDWICK----
Thai Restaurant          7
Indonesian Restaurant    2
Tapas Restaurant         2
Fast Food Restaurant     2
Spanish Restaurant       1
Japanese Restaurant      1
Lebanese Restaurant      1
Mexican Restaurant       1
Moroccan Restaurant      1
Vietnamese Restaurant    1
Name: 433, dtype: object ----RYDE----
Vietnamese Restaurant        0
Dumpling Restaurant          0
Iraqi Restaurant             0
Indonesian Restaurant        0
Indian Restaurant            0
Indian Chinese Restaurant    0
Grilled Meat Restaurant      0
Greek Restaurant             0
German Restaurant            0
French Restaurant            0
Name: 462, dtype: object

此外,如果我们考虑这两个郊区之间的位置因素,Randwick 比 Chatswood 有明显的优势。Coogee beach 距离 Randwick 中心不到一公里。新南威尔士大学也在 Randwick 旁边,这提供了大量的潜在客户群,包括国际学生。最重要的是,有了新运营的悉尼轻轨,来自 CBD 的潜在客户只需要再花 15-20 分钟就可以到达 Randwick。

4.结论

综合来看,兰德威克是我们应该考虑开设意大利餐厅的最佳郊区。但是,我们应该注意,这只是使用公共数据集的非常原始的分析。我们只能解决在选择餐厅位置时很重要的几个因素。例如,我们还没有考虑人口构成、客户流量和停车位。这些信息可以使我们进一步了解在 Randwick 地区经营意大利餐厅的可行性。然而,这一分析展示了数据科学在解决现实世界问题方面的神奇力量。

一如既往,我欢迎反馈,建设性的批评,并听取您的数据科学项目。你可以在 Linkedin 上找到我。

使用 LDA 探索文本数据

原文:https://towardsdatascience.com/exploring-textual-data-using-lda-ef1f53c772a4?source=collection_archive---------29-----------------------

通过应用机器学习原理来理解非结构化文本数据。

简介

我最近在工作中完成了我的第一个机器学习项目,并决定将该项目中使用的方法应用到我自己的项目中。我在工作中完成的项目围绕着使用潜在狄利克雷分配 (LDA)对文本数据进行自动分类。

LDA 是自然语言处理领域中的一种无监督机器学习模型。由于其无监督的性质,LDA 不需要标记的训练集。这使得它非常适合某些用例,或者当大型的、带标签的文本数据集不容易获得时。

LDA 主要用于主题建模,通过相似性对文本文档进行聚类。文档大小可以小到一个单词(不理想),大到整个出版物。LDA 聚类的内容是使用每个文档中的术语(单词)以及它们出现的频率来确定的,有时甚至是它们出现的顺序(使用 n-grams )。被认为彼此相似的文档被聚类在一起,并且我们假设每个聚类代表一个主题,尽管直到聚类被创建之后我们才知道主题本身是什么。需要指出的是模型既不理解这些集群中文档的内容也不理解其上下文,因此实际上不能给集群一个主题标签。相反,它使用来自( 0n)的索引整数来“标记”每个聚类; n 是我们告诉模型要寻找的主题数量。一个人,或者说非常聪明的水生哺乳动物,需要分析这些聚类,并确定每个聚类应该如何被标记。

在这篇文章中,我们将清理一些 Twitter 数据,并编写一个 LDA 模型来对这些数据进行聚类。然后我们将使用 pyLDAvis 来生成集群的交互式可视化。

关键依赖:熊猫、 nltkgensim 、numpy、 pyLDAvis

这里有一些需要事先熟悉的定义:

  1. 文档:文本对象(例如 tweet)
  2. 字典 : 我们的文档集合中所有惟一标记(单词、术语)的列表,每个标记都有一个惟一的整数标识符
  3. *:我们所有文档的集合,每个文档简化为一个矩阵列表,文档中的每个单词对应一个矩阵— 使用 gensim 的doc 2 bow,每个矩阵表示为一个元组,带有一个* 术语的唯一整数 id ,索引为 0 和(例如,文档“the box was in the bigger box”将被简化为类似于[("the ",2),(" box ",2),(" was ",1),(" in ",1),(" bigger ",1)]的内容,但用" term "替换术语的唯一字典 id)
  4. coherence score: 一个范围从 0 到 1 的浮点值,用于评估我们的模型和聚类数与我们的数据的吻合程度
  5. **集群:代表一组文档的节点,一个推断的主题

1.数据

今年早些时候,我开始收集几十万条政治推文,最终目标是对推文及其元数据进行各种分析,为 2020 年美国总统大选做准备。

这篇文章的数据集将由 3500 条推文组成,其中至少提到以下一条:“@berniesanders”、“kamalaharris”、“joebiden”、“ewarren”(分别是伯尼·桑德斯、卡玛拉·哈里斯、乔·拜登和伊丽莎白·沃伦的推特账号)。我在 2019 年 11 月初收集了这些推文,并在这里提供下载。我们将研究这些数据,并试图找出人们在 11 月初发推文的内容。

我不会深入研究如何收集推文,但我已经包括了我在下面使用的代码。成功运行代码需要访问 tweepy API 。我没有收集转发,也没有收集不是用英语写的推文(该模型需要更多的调整以适应多种语言)。

*class Streamer(StreamListener):
    def __init__(self):
        super().__init__()
        self.limit = 1000 # Number of tweets to collect.
        self.statuses = []  # Pass each status here.

    def on_status(self, status):
        if status.retweeted or "RT @" 
        in status.text or status.lang != "en":
            return   # Remove re-tweets and non-English tweets.
        if len(self.statuses) < self.limit:
            self.statuses.append(status)
            print(len(self.statuses))  # Get count of statuses
        if len(self.statuses) == self.limit:
            with open("/tweet_data.csv", "w") as    file: 
                writer = csv.writer(file)  # Saving data to csv. 
                for status in self.statuses:
                    writer.writerow([status.id, status.text,
              status.created_at, status.user.name,         
              status.user.screen_name, status.user.followers_count, status.user.location]) 
            print(self.statuses)
            print(f"\n*** Limit of {self.limit} met ***")
            return False
        if len(self.statuses) > self.limit:
            return False

streaming = tweepy.Stream(auth=setup.api.auth, listener=Streamer())

items = ["@berniesanders", "@kamalaharris", "@joebiden", "@ewarren"]  # Keywords to track

stream_data = streaming.filter(track=items)*

这会将 tweet 文本数据及其元数据(id、创建日期、名称、用户名、关注者数量和位置)传递给名为 tweet_data 的 csv。

*import pandas as pddf = pd.read_csv(r"/tweet_data.csv", names= ["id", "text", "date", "name", "username", "followers", "loc"])*

现在我们已经将数据打包到一个整洁的 csv 中,我们可以开始为我们的 LDA 机器学习模型准备数据了。文本数据通常被视为非结构化数据,在进行有意义的分析之前需要清理。由于不一致的性质,推文尤其混乱。例如,任何给定的 Twitter 用户可能某一天用完整的句子发推,而第二天用单个单词和标签发推。另一个用户可能只发链接,另一个用户可能只发标签。除此之外,还有用户可能会有意忽略的语法和拼写错误。还有一些口语中使用的术语不会出现在标准英语词典中。

清洁

我们将删除所有标点符号、特殊字符和 url 链接,然后对每条推文应用 lower() 。这为我们的文档带来了一定程度的一致性(记住每条 tweet 都被视为一个文档)。我还删除了“berniesanders”、“kamalaharris”、“joebiden”和“ewarren”的实例,因为它们会扭曲我们的词频,因为每个文档至少会包含其中一项。

*import stringppl = ["berniesanders", "kamalaharris", "joebiden", "ewarren"]def clean(txt):
    txt = str(txt.translate(str.maketrans("", "", string.punctuation))).lower() 
    txt = str(txt).split()
    for item in txt:
        if "http" in item:
            txt.remove(item)
        for item in ppl:
            if item in txt:
                txt.remove(item)
    txt = (" ".join(txt))
    return txt

df.text = df.text.apply(clean)*

2.数据准备

下面是我们需要导入的包,以便在将数据输入模型之前准备好数据。在编写数据准备的代码时,我也会包括这些导入。

*import gensim
from gensim.utils import simple_preprocess
from gensim.parsing.preprocessing import STOPWORDS as stopwords
import nltk
nltk.download("wordnet")
from nltk.stem import WordNetLemmatizer as lemm, SnowballStemmer as stemm
from nltk.stem.porter import *
import numpy as np
np.random.seed(0)*

我们已经清理了一些文档,但是现在我们需要对它们进行词法分析和词干分析。词汇化将文档中的单词转换为第一人称,并将所有动词转换为现在时。词干处理将文档中的单词还原为它们的根格式。幸运的是,nltk 有一个 lemmatizer 和一个词干分析器可供我们利用。

LDA 涉及到一个随机过程,意味着我们的模型需要产生随机变量的能力,因此有了 numpy 导入。添加 numpy.random.seed(0) 允许我们的模型是可重复的,因为它将生成并使用相同的随机变量,而不是在每次代码运行时生成新的变量。

Gensim 的停用词是一个被认为不相关或可能混淆我们词汇的术语列表。在 NLP 中,“停用词”指的是我们不希望模型选取的术语集合。此列表将用于从我们的文档中删除这些不相关的术语。我们可以 print(stopwords) 来查看将要删除的术语。

以下是停用词中的术语。

对于这个模型,我们将保持停用词列表不变,但在某些情况下,可能需要添加我们希望模型忽略的特定术语。下面的代码是向停用词添加术语的一种方法。

*stopwords = stopwords.union(set(["add_term_1", "add_term_2"]))*

词汇化和词干化

让我们为我们的数据准备写一些代码。

*import warnings 
warnings.simplefilter("ignore")
import gensim
from gensim.utils import simple_preprocess
from gensim.parsing.preprocessing import STOPWORDS as stopwords
import nltk
nltk.download("wordnet")
from nltk.stem import WordNetLemmatizer as lemm, SnowballStemmer as stemm
from nltk.stem.porter import *
import numpy as np
np.random.seed(0)*

初始化词干分析器。

*stemmer = stemm(language="english")*

写一个函数,既能对我们的文档进行词汇化,又能对其进行词干分析。GeeksforGeeks 有关于使用 nltk 进行词法分析的个例子和关于使用 nltk 进行词干分析的个例子

*def lemm_stemm(txt):
    return stemmer.stem(lemm().lemmatize(txt, pos="v"))*

编写一个函数,将停用词从我们的文档中删除,同时也应用lemm _ stem()

*def preprocess(txt):
    r = [lemm_stemm(token) for token in simple_preprocess(txt) if       token not in stopwords and len(token) > 2]
    return r*

将我们清理和准备好的文档分配给一个新变量。

*proc_docs = df.text.apply(preprocess)*

3。模型的制作

现在我们已经准备好了数据,我们可以开始编写模型了。

词典

正如引言中提到的,字典(在 LDA 中)是在我们的文档集合中出现的所有唯一术语的列表。我们将使用 gensim 的语料库包来构建我们的词典。

*dictionary = gensim.corpora.Dictionary(proc_docs)
dictionary.filter_extremes(no_below=5, no_above= .90)
len(dictionary)*

filter_extremes() 参数是针对停用词或其他常用术语的第二道防线,这些停用词或常用术语对句子的意义没有什么实质意义。摆弄这些参数可以帮助微调模型。关于这一点我就不赘述了,但我在下面附上了来自 gensim 的字典文档中解释参数的截图。

我们的字典有 972 个独特的单词(术语)。

词汇袋

如引言中所述,单词包(在 LDA 中)是我们分解成矩阵的所有文档的集合。矩阵由术语的标识符和它在文档中出现的次数组成。

*n = 5 # Number of clusters we want to fit our data to
bow = [dictionary.doc2bow(doc) for doc in proc_docs]
lda = gensim.models.LdaMulticore(bow, num_topics= n, id2word=dictionary, passes=2, workers=2)print(bow)*

让我们通过查看定义集群的关键术语来了解我们的集群是如何形成的。

*for id, topic in lda.print_topics(-1):
    print(f"TOPIC: {id} \n WORDS: {topic}")*

查看每个主题群,我们可以了解它们代表了什么。看一下题目 1 和题目 4。

关于话题 1: 在话题 1 中,关键词“cenkuygur”和“anakasparian”是指 Cenk 维吾尔族Ana Kasparian共同主持人少壮派(某政论事务所及节目)。主题 1 还包括关键术语“权利”、“特朗普”和“全国步枪协会”。****

11 月 15 日,加州圣塔克拉里塔附近的索格斯高中发生了校园枪击案。关于这一悲剧事件,T2 媒体进行了大量报道,网上也议论纷纷。年轻的土耳其人(TYT)是更严格的枪支法律的口头支持者,并经常与全国步枪协会和其他枪支团体发生冲突。TYT 甚至带头发起了名为#NeverNRA 的承诺运动。

这个主题群可以被标为“TYT 对全国步枪协会”,或类似的东西。

关于主题 4: 术语“cenkuygur”和“anakasparian”在主题 4 中重复出现。话题 4 还包括“theyoungturk”,指的是年轻的土耳其人,以及“berni”,指的是伯尼·桑德斯。

11 月 12 日,岑克维为候选人伯尼·桑德斯发布公开背书。TYT 的推特账户重复了这一表态。伯尼·桑德斯随后公开感谢他们的支持。此外,11 月 14 日,维吾尔先生宣布他将竞选国会议员。这两项进展都在 Twitter 上获得了显著关注。

这个主题群可以被称为“TYT 和伯尼·桑德斯”,或者类似的名称。

**********

其他主题群也有类似的解释。

4。评估、可视化、结论

大多数好的机器学习模型和应用都有一个反馈环。这是一种评估模型的性能、可伸缩性和整体质量的方法。在主题建模空间中,我们使用一致性分数来确定我们的模型有多“一致”。正如我在介绍中提到的,coherence 是一个介于 0 和 1 之间的浮点值。为此我们也将使用 gensim。

*****# Eval via coherence scoringfrom gensim import corpora, models
from gensim.models import CoherenceModel
from pprint import pprintcoh = CoherenceModel(model=lda, texts= proc_docs, dictionary = dictionary, coherence = "c_v")
coh_lda = coh.get_coherence()
print("Coherence Score:", coh_lda)*****

我们得到了 0.44 的一致性分数。这不是最好的,但实际上也不算太差。这个分数是在没有任何微调的情况下获得的。真正挖掘我们的参数和测试结果应该会得到更高的分数。得分真的没有官方门槛。我的一致性分数目标通常在 0.65 左右。参见这篇文章和这个堆栈溢出线程了解更多关于一致性评分的信息。

用 pyLDAvis 可视化

最后,我们可以使用 pyLDAvis 可视化我们的集群。这个包创建了一个聚类的距离图,沿着 x 和 y 轴绘制聚类。这个距离地图可以通过调用 pyLDAvis.display() 在 Jupiter 中打开,也可以通过调用 pyLDAvis.show() 在 web 中打开。

*****import pyLDAvis.gensim as pyldavis
import pyLDAvislda_display = pyldavis.prepare(lda, bow, dictionary)
pyLDAvis.show(lda_display)*****

这是我们的 pyLDAvis 距离图的截图。

将鼠标悬停在每个集群上,会显示该集群中关键术语的相关性(红色)以及这些相同关键术语在整个文档集合中的相关性(蓝色)。这是向风险承担者展示调查结果的有效方式。

结论

这里是我上面使用的所有代码,包括我用来生成单词云的代码和我用来收集推文数据的代码。

*****### All Dependencies ###

import pandas as pd
from wordcloud import WordCloud as cloud
import matplotlib.pyplot as plt
import string
import gensim
from gensim.utils import simple_preprocess
from gensim.parsing.preprocessing import STOPWORDS as stopwords
import nltk
nltk.download("wordnet")
from nltk.stem import WordNetLemmatizer as lemm, SnowballStemmer as stemm
from nltk.stem.porter import *
import numpy as np
np.random.seed(0)
from gensim import corpora, models
from gensim.models import CoherenceModel
from pprint import pprint
import pyLDAvis.gensim as pyldavis
import pyLDAvis

### Word Cloud ###

df = pd.read_csv(r"/tweet_data.csv", names=["id", "text", "date", "name",
                                                                 "username", "followers", "loc"])

def clean(txt):
    txt = str(txt).split()
    for item in txt:
        if "http" in item:
            txt.remove(item)
    txt = (" ".join(txt))
    return txt

text = (df.text.apply(clean))

wc = cloud(background_color='white', colormap="tab10").generate(" ".join(text))

plt.axis("off")
plt.text(2, 210, "Generated using word_cloud and this post's dataset.", size = 5, color="grey")

plt.imshow(wc)
plt.show()### Stream & Collect Tweets ###class Streamer(StreamListener):
    def __init__(self):
        super().__init__()
        self.limit = 1000 # Number of tweets to collect.
        self.statuses = []  # Pass each status here.

    def on_status(self, status):
        if status.retweeted or "RT @" 
        in status.text or status.lang != "en":
            return   # Remove re-tweets and non-English tweets.
        if len(self.statuses) < self.limit:
            self.statuses.append(status)
            print(len(self.statuses))  # Get count of statuses
        if len(self.statuses) == self.limit:
            with open("/tweet_data.csv", "w") as    file: 
                writer = csv.writer(file)  # Saving data to csv. 
                for status in self.statuses:
                    writer.writerow([status.id, status.text,
              status.created_at, status.user.name,         
              status.user.screen_name, status.user.followers_count, status.user.location]) 
            print(self.statuses)
            print(f"\n*** Limit of {self.limit} met ***")
            return False
        if len(self.statuses) > self.limit:
            return False

streaming = tweepy.Stream(auth=setup.api.auth, listener=Streamer())

items = ["@berniesanders", "@kamalaharris", "@joebiden", "@ewarren"]  # Keywords to track

stream_data = streaming.filter(track=items)### Data ###

df = pd.read_csv(r"/tweet_data.csv", names= ["id", "text", "date", "name",
                                                                 "username", "followers", "loc"])

### Data Cleaning ###

ppl = ["berniesanders", "kamalaharris", "joebiden", "ewarren"]

def clean(txt):
    txt = str(txt.translate(str.maketrans("", "", string.punctuation))).lower()
    txt = str(txt).split()
    for item in txt:
        if "http" in item:
            txt.remove(item)
        for item in ppl:
            if item in txt:
                txt.remove(item)
    txt = (" ".join(txt))
    return txt

df.text = df.text.apply(clean)

### Data Prep ###

# print(stopwords)

# If you want to add to the stopwords list: stopwords = stopwords.union(set(["add_term_1", "add_term_2"]))

### Lemmatize and Stem ###

stemmer = stemm(language="english")

def lemm_stemm(txt):
    return stemmer.stem(lemm().lemmatize(txt, pos="v"))

def preprocess(txt):
    r = [lemm_stemm(token) for token in simple_preprocess(txt) if       token not in stopwords and len(token) > 2]
    return r

proc_docs = df.text.apply(preprocess)

### LDA Model ###

dictionary = gensim.corpora.Dictionary(proc_docs)
dictionary.filter_extremes(no_below=5, no_above= .90)
# print(dictionary)

n = 5 # Number of clusters we want to fit our data to
bow = [dictionary.doc2bow(doc) for doc in proc_docs]
lda = gensim.models.LdaMulticore(bow, num_topics= n, id2word=dictionary, passes=2, workers=2)
# print(bow)

for id, topic in lda.print_topics(-1):
    print(f"TOPIC: {id} \n WORDS: {topic}")

### Coherence Scoring ###

coh = CoherenceModel(model=lda, texts= proc_docs, dictionary = dictionary, coherence = "c_v")
coh_lda = coh.get_coherence()
print("Coherence Score:", coh_lda)

lda_display = pyldavis.prepare(lda, bow, dictionary)
pyLDAvis.show(lda_display)*****

LDA 是探索文本数据的一个很好的模型,尽管它需要大量的优化(取决于用例)来用于生产。在编写、评估和显示模型时,gensim、nltk 和 pyLDAvis 包是无价的。

非常感谢你让我分享,以后还会有更多。😃

使用 python 探索数据。

原文:https://towardsdatascience.com/exploring-the-data-using-python-47c4bc7b8fa2?source=collection_archive---------7-----------------------

在本教程中,我们将使用探索性数据分析方法来总结和分析 cars 数据集的主要特征。

Image Credits: Statistical Agency

让我们了解如何使用 python 探索数据,然后在下一个教程中基于这些数据构建一个机器学习模型。本教程的完整代码可以在我的 GitHub 库中找到。

1)选择一个数据集

我从 Kaggle 选择了一个名为“汽车”的数据集,这个数据集的作者是 Lilit Janughazyan [1]。从童年开始,我就对汽车感兴趣和着迷。我还记得我曾经有一本书,里面贴着不同汽车的图片和说明书。我更了解最新的汽车及其规格。我更像是一张规格表,记着几乎所有关于汽车的信息,向人们解释市场上不同的汽车。当我年轻的时候,我的梦想是根据汽车的规格来预测它的价格。在这种兴趣的帮助下,我想在这次作业中选择一个基于汽车的数据集。我想实现我的梦想,创建一个模型,该模型将输入汽车的规格,如马力、气缸或发动机大小,然后该模型将根据这些规格预测汽车的价格。数据集可以在这里找到:汽车数据集

我选择数据集而不是其他数据集的主要原因是,在 Kaggle 中投票最多的类别下有近 110 个关于汽车的数据集(投票最多意味着 Kaggle 上可用的最好和最著名的数据集集合),几乎所有这些数据集都缺少一个或另一个特征。例如,数据集“汽车数据集”[2]具有汽车的大多数特征,但其中没有价格特征,根据我的兴趣,这是最重要的特征。因此,我花了很多时间筛选出许多数据集,然后我总结出“汽车”数据集,因为这个数据集几乎具有汽车的每一个重要特征,如马力、建议零售价、发票、气缸、发动机尺寸等等。因为这些良好的特征,这是我选择这个数据集而不是 Kaggle 中其他数据集的主要原因。

这个数据集直接以 CSV(逗号分隔值)格式存储在 Kaggle 上。我不需要执行任何操作就可以将数据转换成格式。由于数据已经是 CSV 格式,导入数据集只需很少的工作,我所要做的只是下载、读取 CSV 数据并将其存储在 pandas 数据框中,为此我必须导入 pandas 库。

2)获取数据

为了获取数据集或将数据集加载到笔记本中,我所做的只是一个微不足道的步骤。在笔记本左侧的 Google Colab 中,您会发现一个“>”(大于号)。点击它,你会发现一个有三个选项的标签,你可以从中选择文件。然后,您可以在上传选项的帮助下轻松上传数据集。无需安装到 google drive 或使用任何特定的库,只需上传数据集,您的工作就完成了。这就是我如何把数据集放进笔记本的

3)擦洗和格式化

将数据格式化成数据帧

因为数据集已经是 CSV 格式。我所要做的只是将数据格式化成熊猫数据框。这是通过导入 pandas 库使用名为(read_csv)的 pandas 数据框方法完成的。read_csv 数据帧方法通过将文件名作为参数传递来使用。然后通过执行这个,它将 CSV 文件转换成一个组织整齐的 pandas 数据帧格式。

**# Importing the required libraries**import pandas as pd 
import numpy as np
import seaborn as sns #visualisation
import matplotlib.pyplot as plt #visualisation
%matplotlib inline 
sns.set(color_codes=True)**# Loading the CSV file into a pandas dataframe.**df = pd.read_csv(“CARS.csv”)
df.head(5)

确定实例和特征的数量。

这个数据集有 428 个实例和 15 个特征,也称为行和列。此处的实例代表不同的汽车品牌,如宝马、奔驰、奥迪和 35 个其他品牌,特征代表品牌、型号、类型、产地、驱动系统、建议零售价、发票、发动机尺寸、气缸、马力、MPG-城市、MPG-公路、重量、轴距和汽车长度。

去除不相关的特征。

我将从该数据集中移除一些要素,如传动系、模型、发票、类型和来源。因为这些特征无助于价格的预测。截至目前,我将删除传动系,传动系将不支持预测汽车价格,因为该数据集中的大多数汽车是前轮驱动(52.8%),其余是后轮和全轮驱动。

类似地,型号、类型和产地是不相关的,在这种情况下也不需要,重要的是品牌而不是汽车的型号,当谈到汽车的类型时,大多数汽车都是轿车类型,我保留了汽车的重量和长度特征,在这种情况下,我可以很容易地确定它是 SUV、轿车还是卡车。我还将删除汽车的发票功能,因为我有 MSRP 作为价格,我不需要发票,因为有任何一种类型的汽车价格更有意义,它可以防止导致模糊的结果(因为 MSRP 和发票都非常密切相关,你不能预测给定发票的 MSRP)。最后,汽车的起源与预测率无关,所以我不得不删除它,大部分汽车来自欧洲。

**# Removing irrelevant features**df = df.drop([‘Model’,’DriveTrain’,’Invoice’, ‘Origin’, ‘Type’], axis=1)
df.head(5)

4)探索性数据分析

使用 info()识别数据类型

为了识别数据类型,我使用了 info 方法。info 方法打印数据框中数据的摘要及其数据类型。这里有 428 个条目(0–427 行)。移除不相关列之后的数据帧包括 10 列。在这里,品牌、MSRP 是对象类型,而发动机尺寸和气缸是浮动类型,马力、MPG_City、MPG_Highway、重量、轴距和长度是整数类型。因此,数据帧中存在 2 种对象类型、2 种浮点类型和 6 种整数类型的数据。

**# To identify the type of data**df.info()**<class ‘pandas.core.frame.DataFrame’>
RangeIndex: 428 entries, 0 to 427 
Data columns (total 10 columns): 
Make 428 non-null object 
MSRP 428 non-null object 
EngineSize 428 non-null float64 
Cylinders 426 non-null float64 
Horsepower 428 non-null int64 
MPG_City 428 non-null int64 
MPG_Highway 428 non-null int64
Weight 428 non-null int64 
Wheelbase 428 non-null int64 
Length 428 non-null int64 
dtypes: float64(2), int64(6), object(2) 
memory usage: 33.5+ KB**

寻找数据帧的尺寸

为了获得数据框的行数和列数,我使用了 shape 方法。shape 方法获取数据框的行数和列数。这里有 428 行和 10 列。因此 shape 方法返回(428,10)。为了找到数据帧的尺寸,我使用了 ndim (dimension)方法。此方法打印数据框的尺寸。这里,整个数据帧是二维的(行和列)。

**# Getting the number of instances and features**df.shape**(428, 10)**# Getting the dimensions of the data frame
df.ndim**2**

查找重复数据。

这是在数据集上执行的一件方便的事情,因为数据集中可能经常有重复或冗余的数据,为了消除这种情况,我使用了 MSRP 作为参考,这样就不会有超过两个相同的汽车 MSRP 价格,这表明很少有数据是冗余的,因为汽车的价格永远不会非常准确地匹配。因此,在删除重复数据之前,有 428 行,删除之后有 410 行,这意味着有 18 个重复数据。

df = df.drop_duplicates(subset=’MSRP’, keep=’first’)
df.count()**Make 410 
MSRP 410 
EngineSize 410 
Cylinders 408 
Horsepower 410 
MPG_City 410 
MPG_Highway 410 
Weight 410 
Wheelbase 410 
Length 410 
dtype: int64**

查找缺失值或空值。

很多时候,数据集中可能会有很多缺失值。有几种方法可以处理这种情况,我们可以删除这些值,或者用该列的平均值填充这些值。这里,2 个条目在圆柱体特征中有 N/A。这可以通过使用 is_null()方法找到,该方法返回数据框中的空值或缺失值。因此,我没有删除这两个条目,而是用圆柱体列的平均值填充这些值,它们的值都是 6.0。我在查看数据集的第一行和最后几行时发现了这一点。我认为与其删除这是一个好方法,因为每一项数据都是至关重要的。我发现在 Cylinders 特性中有两个存储为 NaN(不是数字)的值。所以我用提到他们索引的切片技术把他们打印出来。

**# Finding the null values**print(df.isnull().sum())**Make 0 
MSRP 0 
EngineSize 0 
Cylinders 2 
Horsepower 0 
MPG_City 0 
MPG_Highway 0 
Weight 0 
Wheelbase 0 
Length 0 
dtype: int64****# Printing the null value rows**df[240:242]

**# Filling the rows with the mean of the column**val = df[‘Cylinders’].mean()
df[‘Cylinders’][247] = round(val)val = df[‘Cylinders’].mean()
df[‘Cylinders’][248]= round(val)

将对象值转换为整数类型。

在查看数据时,MSRP 被存储为对象类型。这是一个严重的问题,因为不可能在图形上绘制这些值,因为在绘制图形期间,所有值必须是整数数据类型是一个基本要求。作者以不同的格式存储了 MSRP(36,000 美元),所以我必须删除格式,然后将它们转换为整数。

**# Removing the formatting**df[‘MSRP’] = [x.replace(‘$’, ‘’) for x in df[‘MSRP’]] 
df[‘MSRP’] = [x.replace(‘,’, ‘’) for x in df[‘MSRP’]]df[‘MSRP’]=pd.to_numeric(df[‘MSRP’],errors=’coerce’)

检测异常值

离群点是不同于其他点的点或点集。有时它们会很高或很低。检测并移除异常值通常是个好主意。因为离群值是导致模型不太精确的主要原因之一。因此,移除它们是个好主意。我将执行 IQR 评分技术来检测和删除异常值。通常,使用箱线图可以看到异常值。下图是 MSRP 的方框图。在图中,你可以发现一些点在方框之外,它们不是别的,就是异常值。我在参考资料部分[3]提到了上述数据科学文章中的异常值技术。

sns.boxplot(x=df[‘MSRP’])

Q1 = df.quantile(0.25)
Q3 = df.quantile(0.75)
IQR = Q3 — Q1
print(IQR)**MSRP           19086.50 
EngineSize         1.55 
Cylinders          2.00 
Horsepower        85.00 
MPG_City           4.00 
MPG_Highway        5.00 
Weight           872.25 
Wheelbase          9.00 
Length            16.00 
dtype: float6**df = df[~((df < (Q1–1.5 * IQR)) |(df > (Q3 + 1.5 * IQR))).any(axis=1)]

使用该技术后,如下图所示,MSRP 盒图不包含异常点,这是一个很大的改进。以前有超过 15 个点的异常值,现在我已经删除了这些异常值。

sns.boxplot(x=df[‘MSRP’])

执行 5 个数字汇总(最小值、下四分位数、中值、上四分位数、最大值

下一步是对数字数据执行 5 个数字的汇总。如前所述,在这种情况下,数字数据是 MSRP、发动机尺寸、马力、气缸、马力、MPG_City、MPG_Highway、重量、轴距和长度。五位数总和包括最小值、下四分位数、中值、上四分位数和最大值,所有这些值都可以通过使用 describe 方法获得。

df.describe()

把不同的特征彼此对立起来。

热图

热图是一种图表,当我们需要找到因变量时,它是必不可少的。使用热图可以找到特征之间相关性的最佳方法之一。如下所示,价格特征(MSRP)与马力的相关性高达 83%,这一点非常重要,因为变量之间的关系越密切,模型就越精确。这就是如何使用热图找到特征之间的相关性。在热图的帮助下,我可以在构建模型时使用这些相关功能。

**# Plotting a heat map**plt.figure(figsize=(10,5))
c= df.corr()
sns.heatmap(c,cmap=”BrBG”,annot=True)

两个相关变量之间的散点图

我知道功能,尤其是建议零售价和马力更相关。因为我有两个相关的变量,所以我用散点图来显示它们的关系。这里绘制了马力和 MSRP 之间的散点图,如下所示。根据下面给出的图表,我们可以在建模过程中轻松绘制趋势线。我可以很容易地在图中看到一条最合适的线。我没有包括 MSRP 和发动机尺寸或气缸之间的散点图,原因是这些数据与 MSRP 的相关性比 MSRP 和马力(83%)的相关性要小。因为如上所述,MSRP 和发动机尺寸之间的相关性为 54 %, MSRP 和气缸之间的相关性为 64%,所以没有理由绘制这些特征。

**# Plotting a scatter plot**fig, ax = plt.subplots(figsize=(5,5))
ax.scatter(df[‘Horsepower’], df[‘MSRP’])
plt.title(‘Scatter plot between MSRP and Horsepower’)
ax.set_xlabel(‘Horsepower’)
ax.set_ylabel(‘MSRP’)
plt.show()

5)报告初步调查结果

我认为汽车的建议零售价(价格)和马力特性有很大的关系。我将在作业 4 中对此进行更多的探索。现在我知道我的问题陈述是“给定汽车的规格,预测汽车的价格(MSRP)”。主要想法是预测汽车的(建议零售价)价格。现在我知道我必须预测一个值,所以我应该使用回归算法,因为我有两个相关的特征(独立和从属特征)。但有许多类型的回归算法,如线性回归,随机森林回归,套索和岭回归等等。所以我可能会使用其中的一种算法,并在下一篇教程中实现一个机器学习模型来预测价格。因此,这项任务主要涉及探索性的数据分析,我准备好了数据,现在可以建立模型了。

参考

[1]janjughazhyan,L. (2017 年)。汽车数据。[在线]Kaggle.com。可在:https://www.kaggle.com/ljanjughazyan/cars1【2019 年 8 月 15 日访问】。

[2] Srinivasan,R. (2017 年)。汽车数据集。[在线]Kaggle.com。可在:【https://www.kaggle.com/toramky/automobile-dataset 【2019 年 8 月 16 日进入】。

[3]n .夏尔马(2018 年)。检测和去除异常值的方法。【在线】中等。可在:https://towards data science . com/ways-to-detect-and-remove-the-outliers-404d 16608 DBA【2019 年 8 月 15 日获取】。

在电影数据集上探索 Neo4j 中的全文搜索索引

原文:https://towardsdatascience.com/exploring-the-full-text-search-index-in-neo4j-on-a-movies-dataset-3cddca69db7a?source=collection_archive---------11-----------------------

全文搜索索引是在 Neo4j 版本 3.5 中引入的,由 Apache Lucene 支持索引。这篇博文的议程是介绍基本的 Lucene 查询语言,并描述如何在 Neo4j 中使用它。正如您将看到的,我们还可以将 Lucene 和 Cypher 结合起来操作结果。

我这篇博文的大部分灵感来自克里斯托夫·威廉森,他是 Graphaware 团队的一员。他很好地描述了 FTS 对像我这样的新手的使用,谢谢!

我们将使用 kaggle 上的电影对话语料库数据集。它有丰富的元数据,因为它包含了 617 部不同电影中角色之间的所有对话。我有几篇使用该数据集的博客文章,但是首先,我们将只使用关于电影的数据来探索全文搜索索引功能。

图形模型

Made using apcjones.com/arrows/

我们的图表中有两个标签(电影、电影标签)。它们通过 HAS_TAG 关系进行连接。电影可以有额外的属性,如发行年份、IMDB 评级和 IMDB 上的投票数。

创建约束

为了优化导入和以后的查询,我们为电影和电影标签定义了唯一的约束。

CREATE CONSTRAINT ON (m:Movie) ASSERT m.id IS UNIQUE;
CREATE CONSTRAINT ON (m:MovieTag) ASSERT m.id IS UNIQUE;

导入

在运行导入查询之前,将movie _ titles _ metadata . tsv文件复制到 $Neo4j/import 文件夹中。

LOAD CSV FROM "file:///movie_titles_metadata.tsv" as row FIELDTERMINATOR "\t"
MERGE (m:Movie{id:row[0]})
SET m.title = row[1],
    m.release_year = toInteger(row[2]),
    m.imdb_rating = toFloat(row[3]),
    m.no_votes = toInteger(row[4])
WITH m, apoc.convert.fromJsonList(
          replace(row[5]," ",",")) as tags
UNWIND tags as tag
MERGE (mt:MovieTag{id:tag})
MERGE (m)-[:HAS_TAG]->(mt)

全文搜索索引

简单来说,lucene 索引管道由两个步骤组成。第一步是“分析器”步骤,它负责文本的预处理。第二步,“索引器”将“分析器”的结果存储到索引中。

Image from https://www.tutorialspoint.com/lucene/lucene_indexing_process.htm

在这篇博文中,我们将使用 Neo4j 中默认的“标准”分析器。它对非字母进行标记,并过滤掉英语停用词和标点符号。不做词干分析,但注意将可能的产品名称、URL 和电子邮件地址作为单个术语。

您可以查看其他可用的分析仪。

CALL db.index.fulltext.listAvailableAnalyzers;

创建全文搜索索引

首先,我们必须将所有包含数字的属性转换成字符串,因为我们不能索引整数或浮点数。索引过程会自动忽略数字。

IMDB 评级属性是一个只有一个小数点的数字。要将其转换为字符串,我们将首先将其乘以 10,然后将其转换为字符串。例如,评分 6.1 将被保存为“61”。

MATCH (m:Movie)
SET m.string_rating = toString(toInteger(m.imdb_rating * 10)),
    m.string_release_year = toString(m.release_year)

当使用范围查询时,我们必须小心,因为排序是按照词汇顺序(字母顺序)进行的。在我们的例子中,所有的评级都在“00”到“99”之间,所以应该没有问题。例如,如果范围跨越到“150”,我们就会遇到一个问题,因为默认情况下,在“50”和“150”之间查找间隔将不起作用。在 Lucene range 的博客文章中描述了解决方法。为了允许在具有不同位数的数字之间进行范围搜索,我们需要对这些值进行预处理,在它们前面加上零,以确保所有的值都具有相同的位数。

在这个例子中,我们将所有的数字预处理为七位数。

WITH 7 as total_length
MATCH (m:Movie)
WHERE exists (m.imdb_rating)
WITH m, total_length, 
        toString(toInteger(m.imdb_rating * 10)) as string_rating
WITH m, total_length — length(string_rating) as zeros, string_rating
WITH m, apoc.text.join([x in range(1,zeros) | “0”],””) +    
                                  string_rating as final_rating
SET m.range_rating = final_rating

现在我们创建“MovieIndex”索引,它包含带有标签 Movie 和四个指定属性的节点。第一个参数是索引的名称。第二个参数定义节点的标签,第三个参数定义我们想要索引的属性。

CALL db.index.fulltext.createNodeIndex(
"MovieIndex",["Movie"],["title","string_rating","range_rating","string_release_year"])

Lucene 查询

让我们看看一些基本的 lucene 查询操作符。

具体属性

搜索标题中带有“梦”字的电影。

CALL db.index.fulltext.queryNodes("MovieIndex", "title:dream") YIELD node, score
RETURN node.title as title, score

结果

逻辑运算符

有两种逻辑运算符可用,“或”和“与”。

搜索 1999 年或 2000 年发行的电影。

CALL db.index.fulltext.queryNodes("MovieIndex", 
     "string_release_year:1999 or 2000") YIELD node, score
RETURN node.title as title, score
LIMIT 5

结果

单字符通配符

单字符通配符操作符?查找与被替换的单个字符匹配的术语。

CALL db.index.fulltext.queryNodes("MovieIndex", "title:th?") YIELD node, score
RETURN node.title as title, score

结果

需要注意的一点是,“黑客帝国”电影不会显示在结果中,因为标准分析器删除了像“the”这样的停用词。

多字符通配符

多字符通配符运算符查找零个或多个字符。也可以像dre*am一样把运算符放在词条中间。为了防止极其缓慢的通配符查询,术语不应该以通配符*dream开头。

CALL db.index.fulltext.queryNodes("MovieIndex", "title:drea*") YIELD node, score
RETURN node.title as title, score

结果

模糊搜索

模糊搜索通过使用数学公式来计算两个单词之间的相似度。计算相似性的常用方法是 Levenshtein 距离。

CALL db.index.fulltext.queryNodes("MovieIndex", "title:dream~") YIELD node, score
RETURN node.title as title, score
LIMIT 5

结果

范围查询

Lucene 区分了具有包含或排除端点的范围操作符。

操作员:

{} ->不包括边缘

[] ->包括边缘

CALL db.index.fulltext.queryNodes("MovieIndex", "string_rating:[50 TO 99}") YIELD node, score
RETURN node.title as title,score
LIMIT 5

结果

为了说明为什么我们在进行范围搜索时需要相同位数的数字,让我们尝试下面的查询。

CALL db.index.fulltext.queryNodes("MovieIndex", "string_rating:[50 TO 100]") YIELD node, score
RETURN node.title as title,score

直觉上你会认为它工作得很好,但是由于词汇排序的原因,情况并非如此。我们用来解决这个问题的变通方法是 range_rating 属性,其中的值以零为前缀,以允许在不同数量级的数字之间进行字母排序。

CALL db.index.fulltext.queryNodes(“MovieIndex”, “range_rating:[0000050 TO 0000150]”) YIELD node, score
RETURN node.title as title,score
LIMIT 5

结果

提高分数

Lucene 查询语言还支持使用脱字符(^) 操作符提升结果的分数。

提升 string_rating 介于 50 和 99 之间的结果。

CALL db.index.fulltext.queryNodes("MovieIndex", "title:dream string_rating:[50 TO 99]^2") YIELD node, score
RETURN node.title as title,score
LIMIT 5

结果

时间衰变

我在 Christophe Willemsen 的 Nodes 2019 网络研讨会上看到一个 lucene time decay 查询,我想复制它。基本上,我们结合许多增强参数来实现时间衰减效果。为了说明这意味着什么,我们将使用apoc.text.join()来生成九个助推参数。

RETURN apoc.text.join([
 x in range(0,8) | 
 "string_release_date:" + toString((date().year - x)) + "^" + toString(10-x)
]," ")

结果

string_release_date:2019^10 string_release_date:2018^9 string_release_date:2017^8 string_release_date:2016^7 string_release_date:2015^6 string_release_date:2014^5 string_release_date:2013^4 string_release_date:2012^3 string_release_date:2011^2

你可以看到,2019 年上映的电影将增加 10 倍,2018 年的电影将增加 9 倍,以此类推。

总结这篇博文,让我们在一个查询中结合评分提升和时间衰减效果。为了让你知道以后你可以用 cypher 处理结果,让我们添加一个过滤器,只返回惊悚片。

WITH apoc.text.join([x in range(0,10) | 
"string_release_date:" + toString((date().year — x)) + "^" +   
  toString(10-x)]," ") as time_decay
CALL db.index.fulltext.queryNodes("MovieIndex", "title:dream string_rating:[50 TO 99]^2 "+ time_decay) YIELD node, score
// filter only thrillers
MATCH (node)-[:HAS_TAG]->(:MovieTag{id:'thriller'})
RETURN node.title as title,score
LIMIT 5

结果

结论

全文搜索是一个非常受欢迎的特性,它可以帮助我们开发更好更快的 Neo4j 应用程序,还可以解决其他问题。如果您有任何问题或反馈,请加入 Neo4j 社区论坛

所有代码在Github上都有。

用 R 刮和探索 SP500 第 1 部分

原文:https://towardsdatascience.com/exploring-the-sp500-with-r-part-1-scraping-data-acquisition-and-functional-programming-56c9498f38e8?source=collection_archive---------10-----------------------

刮擦、数据采集和功能编程

今天,我想通过一个简单的例子,使用函数式编程和整洁的迭代概念,将抓取、对 Yahoo finance api 的调用、数据连接和简单的资产分析结合起来。

photo credit: https://unsplash.com

最终目标:

我想确定过去 3 个月哪些 SP500 资产的平均回报率最高。

这个 分析的最终结果 就是这个图表:

这个图表的制作,资产分析,回答我的问题,会在这个系列 的第二部分 中讲到。

介绍和 Tidyverse:

在今天的分析中,我将使用 R 编程语言。如果你过去读过我在 Linkedin 或 Medium 上的任何帖子,你可能已经注意到我通常用 python 编程。总的来说,我更喜欢 python 编程语言,因为它的语法更简单,应用更广泛,也更容易投入生产。

潮间带生态系统:

在 R 编程语言中,有一组包组成了所谓的 tidyverse 。这些软件包主要由 Rstudio 的工程师和数据科学家维护,并提供了一种简单、集成和统一的方法来操作 r 中的数据。tidyverse 围绕着 整理数据 这是 Hadley Wickham 创造的一个术语,用来描述数据,其中:

  • 每个变量都是一列
  • 每个观察(或案例)都是一行

今天我将利用几个图书馆。在下面的代码块中,我导入了我将要使用的库。

# for scraping
library(rvest)
# blanket import for core tidyverse packages
library(tidyverse)
# tidy financial analysis 
library(tidyquant)
# tidy data cleaning functions
library(janitor)

我要做的下一件事是用今天的日期定义一个变量。然后我从今天减去 3 个月。这将返回另一个 date 对象,指示 3 个月前的某一天。我需要这个,因为我想得到每个股票最近 3 个月的价格数据。

# save current system date to a variable
today <- Sys.Date()
# subtract 3 months from the current date
date = today %m+% months(-3)
print(date)

一个股票

我将使用 tidyquant 包获取所有 SP500 证券交易所的财务数据。tidyqunat 包的核心函数是 tq_get(),可以用来获取股票的各种信息。如果我将一个包含股票代号的字符串传递给 tq_get(),它将返回开盘、盘高、盘低、收盘或 OHLC 数据。我将 SP500 的报价器^GSPC 传递给 tq_get()函数。

# pass SP500 ticker ^GSPC to tq_get function
one_ticker = tq_get("^GSPC", from = date)
one_ticker %>% 
  head()

上面有几件事需要注意

  • tq_get()返回一个整洁的数据帧
  • 股票代码名称不在数据帧中
  • %>%运算符称为管道。它将前面的对象作为第一个参数传递给后面的函数。

放大第 1 部分—获得所有 505 个分笔成交点的向量:

我想要所有 SP500 售票员的 OHLC 数据。为了做到这一点,我需要做几件事:

  • 创建所有 SP500 证券交易所的向量
  • 迭代这个向量并对向量的每个元素调用 tq_get(),为向量中的每个元素返回一个 dataframe
  • 将所有这些数据帧组合成一个数据帧

哇!听起来有点复杂,对吧?幸运的是,有了 R,这将变得非常简单。维基百科有一个包含所有 505 家 SP500 证券交易所(一些公司,如谷歌,有多个资产类别)的表格,位于以下网址:

https://en.wikipedia.org/wiki/List_of_S%26P_500_companies

为了得到所有的 SP500 ticker,我们将使用 rvest 包来清理这个表。rvest 包是 R 中的一个简单的刮包,非常类似于 python 的美汤。在编程的上下文中,抓取被定义为以编程方式从互联网和网页上收集人类可读的内容。

在下面的代码中,我抓取了维基百科表格,并创建了一个包含所有 SP500 证券交易所的向量:

  • 我首先将维基百科的 URL 赋给一个变量
  • 从 URL 读入 html
  • 选择正确的 html 节点并提取 html 表格
  • 对股票代号做了一个小小的改动,因为雅虎财经用了一个“_”而不是“.”对于某些符号名称

抓取最困难的部分是找出 xpath 或 css 来指示选择哪些 html 节点。我真的不太了解 html 或 css,但是使用 Google Chrome 我能够找到正确的 xpath(下面会有更多相关内容)。

# get the URL for the wikipedia page with all SP500 symbols
url <- "[https://en.wikipedia.org/wiki/List_of_S%26P_500_companies](https://en.wikipedia.org/wiki/List_of_S%26P_500_companies)"
# use that URL to scrape the SP500 table using rvest
tickers <- url %>%
  # read the HTML from the webpage
  read_html() %>%
  # one way to get table
  #html_nodes(xpath='//*[[@id](http://twitter.com/id)="mw-content-text"]/div/table[1]') %>%
  # easier way to get table
  html_nodes(xpath = '//*[[@id](http://twitter.com/id)="constituents"]') %>% 
  html_table()
#create a vector of tickers
sp500tickers <- tickers[[1]]
sp500tickers = sp500tickers %>% mutate(Symbol = case_when(Symbol == "BRK.B" ~ "BRK-B",
                                           Symbol == "BF.B" ~ "BF-B",
                                            TRUE ~ as.character(Symbol)))

如何使用 Google Chrome 找到我传递给 html_nodes 函数的 xpath:

  • 大多数网页内容通常在 html 文档的主体中。我们会扩大那部分。看起来会像这样:

  • 查看网页时,我可以看到我想要的表格就在第一个 h2 标题的下方:

  • 在浏览了页面结构后,我找到了第一个 h2 标题和我想要放在它下面的表格

  • 我可以点击这个表,右键点击并复制这个表所需要的 xpath

  • XPath =//*[@ id= " constituents "],这就是传递给 html_nodes 的内容
html_nodes(xpath = '//*[[@id](http://twitter.com/id)="constituents"]'

向上扩展第 2 部分— Purrr、迭代和函数式编程:

迭代是指以编程方式重复一个步骤或一组步骤,重复一定的次数,或者直到达到某个条件。

通常,当我们在任何编程语言中迭代时,我们使用循环,通常是 for 循环。我需要迭代 SP500 ticker 向量中的每个元素,并将其传递给函数 tq_get()。我可以用 for 循环做到这一点,但是使用 purrr 包是一个更好的主意。R 中的循环很慢,很难读懂。purrr 包提供了一套用于迭代和函数式编程的函数,可以很好地与 tidyverse 的其余部分集成。map()中 purrr 的核心函数。大多数编程语言(包括我最喜欢的一个 python)都有一个 map 函数,用于将一个函数应用于一个对象的所有元素。

函数式编程是一种编程范式,与相反,函数构建程序的结构和逻辑。在函数式编程中,通常避免使用 For 循环。相反,函数被映射或应用于列表或其他对象。

正如哈德利·韦翰在他的《高级 R 书》中所说:

“很难准确描述什么是功能性风格,但通常我认为它意味着将一个大问题分解成更小的部分,然后用一个功能或功能组合来解决每个部分。当使用函数式风格时,您努力将问题的组成部分分解成独立操作的孤立函数。每个函数本身都简单易懂;复杂性是通过以各种方式组合函数来处理的。”

让我们用一个例子来看看 purrr 中的迭代和 for 循环的区别。这两种操作大致相同:

# get a sequence of the numbers 1 to 5
numbers = seq(1:5)
print('for loop')# for loop 
for (i in numbers){

  print(i)
}print('purrr :)')# purr functional programming approach
list = map(numbers, print)

需要注意的几件事:

  • for 循环和 map 函数都对向量中的每个元素进行运算
  • map 函数返回一个嵌套的列表,其中每个条目都是在该列表中调用的函数的结果,该函数针对的是被迭代的对象中的一个条目。我把它赋给一个变量列表,以避免它被打印出来。
  • 使用 purrr 的 map 函数只需要一行代码和很少的代码
  • 使用 purrr 的地图功能更容易阅读

好吧,现在让我们迭代得到所有 505 SP500 ticker!

首先,我需要编写一个函数来迭代或应用到向量的每个元素。我不能简单地将 tq_get()与 map()一起使用,因为它不会将 ticker 名称作为 dataframe 的一列返回。为了获得带有数据帧的 ticker,我将使用 dplyr 中的 mutate()函数创建一个带有 ticker 名称的新列。

get_symbols = function(ticker = "AAPL"){
  df = tq_get(ticker, from = date) %>% mutate(symbol = rep(ticker, length(date)))
}

然后,我将这个函数与 map()结合使用来遍历所有符号的列表。这将返回一个嵌套列表,其中包含每个 ticker 的数据帧。我使用 dplyr bind_rows()函数将数据帧按行绑定在一起,创建一个包含所有 SP500 报价机的数据帧。

#create the dataframe of SP500 data by interating over our list of symbols and call our get symbols function each time
#the map function accomplishes thistickers_df = map(symbols, get_symbols) %>% bind_rows()

我还希望这个数据帧包含来自维基百科表的信息,最重要的是公司的名称。这可以通过用符号连接两个数据帧来实现。

tickers_df = tickers_df %>% 
  # left join with wikipedia data
  left_join(sp500tickers, by = c('symbol' = 'Symbol')) %>% 
  # make names R compatible
  clean_names() %>% 
  # keep only the columns we need
  select(date:security, gics_sector, gics_sub_industry)

加入数据后,我们应该做一个快速的健全检查,以确保我们有所有 505 SP500 ticker

tickers_df %>% 
# select just the symbol column
select(symbol)%>% 
# get the distinct values
distinct()%>% 
# count the distinct values 
count() %>% 
# we can use select to rename columns 
select("Total Number of Tickers" = n)

最后,我们可以检查数据帧的前几行,以确认我们已经获得了想要的数据:

tickers_df %>% 
  head()

完美!正如我们所料。

总结:

在第一篇博文中,我们有:

  • 了解了 tidyverse 生态系统和 tidyquant 的基础知识
  • 学习了整洁迭代和函数式编程的基础知识
  • 学会了如何利用 rvest 和 chrome 从维基百科收集数据
  • 学会了如何从阅读单一资产转移到整个 SP500

中的 中的下一篇 :

我来回答我原来的问题:

过去 3 个月,哪些 SP500 资产的平均回报率最高?

需要注意一些事情:

  • 使用 TQ _ index(“SP500”),您可以更容易地获得所有 SP500 股票的列表。这个函数需要 XLConnect 库。我目前在本地机器上导入并运行这个库时遇到了问题。

  • tq_get()实际上接受报价器列表,所以没有必要使用 map()
  • 我有意以这种方式编写代码,以演示所描述的概念

声明:在大学里,我用 SP500 数据和 python 做了一个项目。分享这个项目是违反班级政策的。虽然我在这里做的事情有些类似,但我使用的是完全不同的编程语言,回答的是完全不同的问题。鉴于我之前陈述,我断言这并不违反课程政策。我有意使用 R 编程语言,而不是 python,以完全避免任何问题,并尊重我以前的教授的意愿。

代码可以在这里找到

这是另一个关于抓取的很好的资源,对写这篇文章有一点帮助。

https://medium . com/@ ky leake/Wikipedia-data-scraping-with-r-rvest-in-action-3c 419 db 9 af 2d https://medium . com/@ ky leake/Wikipedia-data-scraping-with-r-rvest-in-action-3c 419 db 9 af 2d

注来自《走向数据科学》的编辑: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

用 R 刮和探索 SP500 第二部分

原文:https://towardsdatascience.com/exploring-the-sp500-with-r-part-2-asset-analysis-657d3c1caf60?source=collection_archive---------17-----------------------

资产分析和可视化

photo credit: https://unsplash.com/photos/uJhgEXPqSPk

在这篇博文的第一部分,我从维基百科搜集了一系列 SP500 符号,并使用这些信息从雅虎财经 API 中提取了所有 SP500 股票的 OHLC(开盘价、最高价、最低价、收盘价)数据。

在这篇博文的第二部分,我将回答我最初的问题:

在过去的三个月里,哪些 SP500 资产的平均回报率最高?

在第 1 部分中,我们创建了 tickers_df,这是一个包含所有 505 个 SP500 tickers 的数据框架。

让我们再快速看一下 tickers_df:

这个数据是整理的意思:

  • 每个变量都是一列
  • 每个观察(或案例)都是一行

这种结构使数据易于过滤和聚合,也更易于扩展。只需几行代码,我们就可以轻松提取和可视化一项资产的原始价格:

ticker = "GOOGL"
tickers_df %>% 
  filter(symbol == !!ticker) %>% 
  ggplot(aes(date, adjusted))+
  geom_line()

我们可以整天查看原始价格数据,但很难比较价格差异巨大的资产。通常,当我们分析资产时,我们会查看价格或回报的百分比变化。使用 tidyquant 包,计算回报极其容易。在下面的代码片段中,我将每项资产的原始调整收盘价转换为回报率。这是使用 tq _ transmute()函数完成的。

daily_sector = tickers_df %>% group_by(security, gics_sector, symbol) %>% 
tq_transmute(select     = adjusted, 
              mutate_fun = periodReturn, 
              period     = "daily") %>% 
              ungroup()

需要注意一些事情:

  • 数据仍然很整洁
  • 我们从雅虎财经 API 和维基百科页面上搜集了信息
  • 使用这种格式,很容易按符号、证券或 gics_sector 进行汇总
  • 别忘了解组!我最初是这样做的

最佳表现者:

让我们继续计算证券的平均日收益率,然后按这个排序,来回答我的问题。我还会计算收益的标准差。在数量金融学中,收益的标准差一般被称为波动率。

avg_return =daily_sector %>% 
  group_by(security, gics_sector) %>%
  summarise(avg_return = round(mean(daily.returns), 4),Volatility =   sd(daily.returns)) %>%         
arrange(desc(avg_return), desc(Volatility))
avg_return %>% head()

从这张表中我们可以很快地看到,美国著名的化学公司 Dow Inc .在过去的 3 个月里拥有最高的日平均回报率。这张表回答了我原来的问题。

让我们使用 ggplot 将此表可视化为一个条形图:

avg_return %>% head(20) %>% ggplot(aes(reorder(security, -avg_return), avg_return, fill = avg_return))+
  geom_col()+
  coord_flip()+
  labs(title = "Securities With Highest Average Returns In SP500 Over Past 3 Month", x = "Security", y = "Average Return")+
  theme_classic()+
  theme(legend.position="none")

Wow Dow Inc .的平均回报率远高于其他市场。我可能漏掉了一些重要的东西。快速的谷歌搜索告诉我这有一个明显的原因。自 3 月底正式从 Dowdupont 分离出来后,陶氏公司才作为一家独立的化学公司上市。平均回报率可能只有这么高,因为它是一种新交易的资产,我们没有太多的观察数据。

看着其他表现出色的人,我注意到一些事情:

  • 过去 30 天里,许多表现最佳的公司都是科技公司。
  • Chipotle 的强劲表现让我有点吃惊。他们显然已经从之前围绕食物中毒的争论中恢复过来
  • 两家计算机硬件制造商 Nvidia 和 Advanced Micro Devices 的回报率高于其竞争对手英特尔,后者在历史上表现更好。

通常,当你用数据回答一个问题时,更多的问题就会出现。我已经知道哪些股票在过去几个月的平均回报率最高,但波动性呢?波动性是证券或投资组合最重要的投资指标之一。

根据维基百科的说法,投资者关心波动性至少有八个原因:

  1. 投资价格的波动越大,就越难不担心;
  2. 交易工具的价格波动可以定义投资组合中的头寸规模;
  3. 当在特定的未来日期需要通过出售证券获得一定的现金流时,更高的波动性意味着更大的短缺机会;
  4. 在为退休储蓄的同时,回报的波动性更高,导致可能的最终投资组合价值分布更广;
  5. 退休时回报率的波动性更高,这使得提款对投资组合的价值产生更大的永久影响;
  6. 价格波动提供了低价买入资产、高价卖出的机会;
  7. 投资组合波动对该投资组合的复合年增长率 (CAGR)有负面影响
  8. 波动率影响期权的定价,是布莱克-斯科尔斯模型的一个参数。

通常,年轻投资者可以承受更大的波动性,而接近退休年龄的投资者则希望小心波动性。

使用 ggplot2,我们可以很容易地绘制出平均回报和回报波动性之间的关系。我将创建一个散点图,使用资产标记而不是图中的点。

plot = avg_return %>% ggplot(aes(avg_return, Volatility))+
  geom_text(aes(label = symbol), size = 3)+
  labs(title = "Average Return vs Volatility Over Last 3 Months In SP500", x = "Average Return", subtitle = "Data Source: Yahoo Finance")+
  theme_minimal()

plot

这张图比我们之前的柱状图更好地展示了市场上正在发生的事情。

与市场上的其他股票相比,一些股票的确很突出

  • 道琼斯公司有最高的平均回报率,似乎围绕市场波动。
  • AMD 和黄凤英表现突出,高于市场波动和回报。对于年轻投资者来说,这些可能是可靠的投资机会
  • Matel 和 EA games 表现突出,其市场回报率在 100%左右,但波动性较高.这种高波动性让投资者有机会在证券价格过低时买入,在价格过高时卖出。
  • 图左上角最突出的是福克斯公司两种资产类别的高波动性和低平均回报率

让我们在图表上突出显示福克斯公司的代号:

avg_return = avg_return %>% 
  mutate(Indicator = case_when(symbol %in% c('FOX', 'FOXA') ~ "Fox Corporation",
                               TRUE ~ "The Rest of the SP500"))plot = avg_return %>% ggplot(aes(avg_return, Volatility, color = Indicator))+
  geom_text(aes(label = symbol), size = 3)+
  labs(title = "Average Return vs Volatility Over Last 3 Months In SP500", x = "Average Return", subtitle = "Data Source: Yahoo Finance")+
  theme_minimal()

plot

哇!看那个!我走进这个分析,对哪些公司表现最好感兴趣。但是,最终,真正脱颖而出的是福克斯公司,在过去的 3 个月里,相对于市场的其他公司,它的表现似乎相当疲软。

福克斯公司,尤其是他们的新闻站假新闻(我拼对了吗?),多年来一直颇有争议。该网络对许多问题有独特的观点,比一般的新闻网络有更多基于观点的节目。

让我们获得更多一点的福克斯公司的价格数据:

symbols = c('FOX', 'FOXA')
fox = tq_get(symbols, from = '2017/01/01')
fox %>% 
  ggplot(aes(date, adjusted))+
  geom_line()+
  geom_smooth(method = 'lm', alpha = 0.3)+
  facet_wrap(~symbol)+
  theme_classic()

  • 在过去的几年里,这两个狐狸的标志似乎都在稳步增长
  • 最近价格大幅下跌。随后继续呈下降趋势

让我们放大最近的价格:

tickers_df %>% 
  filter(symbol %in% c('FOX', 'FOXA')) %>% 
  ggplot(aes(date, adjusted))+
  geom_point(alpha = 0.3, color = 'black')+
  geom_line()+
  facet_wrap(~symbol)+
  theme_classic()

我们可以看到价格下跌发生在 3 月初。3 月 13 日出现了最初的大幅下跌,随后出现了短暂反弹。继反弹之后,价格下跌,并继续呈下降趋势。

让我们求助于谷歌来弄清楚 3 月 13 日发生了什么。快速的谷歌搜索告诉我,媒体事务在 3 月 13 日组织了一场反对福克斯公司的大规模抗议活动。

继 3 月 13 日下跌后,2019 年 3 月 19 日,福克斯公司开始独立于 21 世纪福克斯进行交易。这使得资产价格急剧下跌。不过,这并不像人们最初认为的那样,代表着资产表现不佳。

实际发生的是,福克斯公司开始使用 21 世纪福克斯的代码进行交易,21 世纪福克斯现在使用 TFCF 和 TFCFA 的代码进行交易。这导致许多新闻媒体错误地报道了福克斯和福克斯下降了 22%

完成并制作最后的情节:

好了,现在我们想分享一下我们在互动情节中发现的东西,这样技术用户就不会有有趣的、信息丰富的互动情节了。

r 有很多很好的交互式数据可视化框架。我个人最喜欢的 R 中的交互式图形库是 plotly 库。Plotly 是一个交互式的 javascript 库,带有许多不同编程语言的 API。我更熟悉 python API(以及一般的 python 语言)。

使用与 ggplot2 中非常相似的代码,我们可以生成一个漂亮的交互式绘图。我会在这篇文章中嵌入情节。

p <- plot_ly(avg_return, x = ~avg_return, y = ~Volatility, type = 'scatter',
        mode = 'text', text = ~symbol, textposition = 'middle right',
        color = ~Indicator, colors = 'Set1',
        textfont = list(size = 8)) %>%
  layout(title = 'Average Return vs Volatility Over Last 3 Months In SP500',
         xaxis = list(title = 'Averaage Return', zeroline = FALSE
                      ),
         yaxis = list(title = 'Volatility'
                      ))

概括地说,在这两部分系列中,我们:

  • 复习了关于抓取、函数式编程和 tidyverse 的介绍
  • 整个 SP500 的导入股票数据
  • 提出一个问题,哪些股票在过去 3 个月的平均回报率最高?
  • 我们回答了这个问题,并通过对我们的数据提出更多的问题,我们更全面地回答了这个问题,并发现最终有趣的是看似较低的平均回报率和相对于市场的高波动性。
  • 这种不断对数据提出问题的循环工作流在数据科学中很常见
  • 我们需要小心数据质量,确保我们理解我们的数据。FOX,FOXA 看起来表现很差,而道琼斯看起来比市场其他股票表现好得多。这些结论会产生误导!

来自《走向数据科学》编辑的注释: 虽然我们允许独立作者根据我们的 规则和指南 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

探索纽约街区的品味

原文:https://towardsdatascience.com/exploring-the-taste-of-nyc-neighborhoods-1a51394049a4?source=collection_archive---------22-----------------------

k-均值聚类——一种无监督的机器学习算法

Image Source — Unsplash

这是一篇图文并茂的文章。要访问 Jupyter 笔记本— 点击这里

介绍

背景

纽约市是美国人口最多的城市,是联合国总部所在地,也是重要的国际外交中心。它可能是地球上最多样化的城市,因为它是 860 多万人和 800 多种语言的家园。

正如一篇文章中所引用的— 食物告诉我们的文化
“传统美食是一代一代传下来的。它也是一种文化身份的表达。移民无论去哪里都会带着他们国家的食物,烹饪传统食物是他们搬到新地方时保存自己文化的一种方式。”

问题

毫无疑问,食物的多样性是一个多民族大都市的重要组成部分。这个项目的想法是将纽约市的街区分成几个主要的区域,并研究他们的烹饪。一个可取的意图是检查附近集群的饮食习惯和口味。进一步的研究可能会揭示食物是否与社区的多样性有任何关系。这个项目将通过利用 Foursquare 的“Places API”和“k-means clustering”无监督机器学习算法的场地数据,帮助了解一个街区的多样性。探索性数据分析(EDA)将有助于进一步发现社区的文化和多样性。

利益相关者

这种可量化的分析可以用来理解不同文化和美食在“地球上最多样化的城市——纽约市”的分布。此外,它还可以被愿意开餐馆的新食品供应商使用。或者由政府机构更好地检查和研究他们城市的文化多样性。

数据

为了检查上述内容,使用了以下数据来源:

纽约市数据集

链接:https://geo.nyu.edu/catalog/nyu_2451_34572

这个纽约市街区名称点文件是作为纽约市街区指南创建的,它出现在网络资源“纽约:街区之城”上标签质心的最佳估计值是以 1:1,000 的比例建立的,但理想情况下以 1:50,000 的比例查看。该数据集将以 json 格式提供纽约市附近的地址。json 的摘录如下:

四方应用编程接口

链接:https://developer.foursquare.com/docs

Foursquare API 是一个位置数据提供者,它将被用来进行 RESTful API 调用,以检索不同街区的场馆数据。这是到 Foursquare 场馆类别层级的链接。从所有街区检索到的场馆大致分为“艺术&娱乐”、“学院&大学”、“活动”、“美食”、“夜生活场所”、“户外&娱乐”等。API 调用的摘录如下:

方法学

下载并探索纽约市数据集

为了分割纽约市的街区,需要一个包含 5 个区和街区的数据集,每个区都有各自的纬度和经度坐标。这个数据集是使用提到的 URL 下载的。

一旦。json 文件被下载,它被分析以理解文件的结构。URL 返回一个 python 字典,所有相关数据都在 features 键中,features 键基本上是一个邻域列表。通过遍历数据并一次一行地填充数据帧,字典被转换成 pandas 数据帧。

结果,创建了一个数据帧,其中包含纽约市街区的区、街区、纬度和经度细节。

经分析发现,该数据框架由 5 个区和 306 个街区组成。

此外,“geopy”库用于获取纽约市的纬度和经度值,返回的值为纬度:40.71,经度:-74.01。

然后,通过创建纽约市地图,将精选的数据帧用于可视化,在地图上叠加街区。以下描述是使用 python“folium”库生成的地图。

对 Foursquare 的 RESTful API 调用

Foursquare API 用于探索邻域并对其进行分割。为了访问 API,定义了“客户端 ID”、“客户端秘密”和“版本”。

Foursquare 上有许多端点可用于各种 GET 请求。但是,为了探索美食,要求所有提取的地点都来自“食物”类别。检索 Foursquare 场所类别层次结构,并进一步分析返回的请求。

分析发现,场馆有 10 个大类或母类,所有其他子类都包含在这些大类或母类下。

如前所述,“食物”类别是感兴趣的问题。我们创建了一个函数来返回一个字典,其中包含“食品”及其子类别的“类别 ID”和“类别名称”。

上面的函数获取父类别 ID,并返回所有子类别的类别名称和类别 ID。

为了进一步理解 GET 请求的结果,探索“纽约市”数据集的第一邻域。返回的第一个邻域是“Wakefield ”,纬度为 40.89,经度为-73.85。然后,创建一个 GET 请求 URL 来搜索“类别 ID”= '4d 4b 7105d 754 a 06374d 81259',即“食物”的“类别 ID”,半径= 500 米的地点。然后检查返回的请求,如下所示:

因为目标是根据纽约市附近的“食物”来分割纽约市的邻近地区,所以还需要从所有 306 个邻近地区的场所获取该数据。

为了克服上述过程的冗余,创建了函数“getNearbyFood”。这个函数遍历纽约市的所有街区,并创建一个半径= 500,限制= 100 的 API 请求 URL。通过限制,定义了最多应该返回 100 个附近的场所。此外,GET 请求是向 Foursquare API 发出的,只从中提取每个附近地点的相关信息。然后,数据被追加到 python“列表”中。最后,python“列表”被展开或展平,以将其附加到函数返回的数据帧中。

令人好奇的是,如果在 GET 请求中指定了顶级类别,Foursquare API 会返回所有子类别。

泡菜

Pickle 是一个非常重要且易于使用的库。它用于序列化从 GET 请求中检索到的信息,以使。“pkl”文件。这个文件稍后可以被反序列化以检索精确的 python 对象结构。这是至关重要的一步,因为它将阻止对 Foursquare API 的任何冗余请求,这些请求的收费超过了阈值限制。

返回的“数据帧”如下:

到目前为止,创建了两个 python“数据帧”:

1.“neighborhoods ”,包含纽约市邻近地区的区、邻近地区、纬度和经度细节,以及

2.“nyc _ venues”是“neighborhoods”数据框架与其“Food”类别场馆的合并,使用“半径”= 500 米和“限制”= 100 进行搜索。此外,每个场馆都有自己的纬度,经度和类别。

探索性数据分析

合并后的数据框架“nyc _ venues”包含所有必需的信息。确定该数据帧的大小,并且发现总共有 14,047 个地点。

现在,重要的是要找出有多少独特的类别可以从所有返回的场馆策划。并且发现有 194 个这样的类别。

数据清理

重要的是要明白,这个项目的兴趣点是通过使用场地的类别对社区进行分类,从而了解社区的文化多样性。因此,重要的是从“数据框架”中移除所有具有一般化类别的场馆。这里,概括地说,这意味着这些分类的场所在不同的文化和饮食习惯中是常见的。这种类型的场所的类别的例子是咖啡店、咖啡馆等。因此,首先,所有独特的类别都被输入到一个 python“列表”中。

然后,手动将类别确定为“一般”(如上所述)。这种数据预处理完全取决于“数据分析师”的判断,可以根据需要进行修改。以下是列为“一般”的类别:

两个 python“列表”即“唯一 _ 类别”和“一般 _ 类别”的简单相减给出了进一步分析所需的所有类别的“列表”。python“列表”管理用于删除所有类别不在“食物类别”中的场所。

再次检查独特类别的数量,发现只有 92 个,而以前有 194 个。这意味着,几乎 50%的数据对于分析来说是一个噪音。这个重要的步骤,数据清理,有助于捕获感兴趣的数据点。

特征工程

现在,对每个街区进行单独分析,以了解其 500 米范围内最常见的菜肴。

上述过程是通过使用 python“熊猫”库的“一次热编码”功能实现的。一种热编码将分类变量(即“场地类别”)转换成可以提供给 ML 算法的形式,以便在预测中做得更好。

在转换分类变量时,如上所示,“邻域”列被添加回来。检查新数据帧“nyc_onehot”的大小,发现总共有大约 6,846 个数据点。

此外,统计每个街区中每个类别的场地数量。观察到,在数据帧的前五个邻近区域中,“安纳代尔”、“阿登高地”和“阿灵顿”在其 500 米附近具有 3、3、2 个“美国餐馆”。

前 10 个“场馆类别”也可以通过统计它们的出现次数来找到。下面的分析显示,“韩国餐厅”、“中国餐厅”、“加勒比海餐厅”、“印度餐厅”和“快餐店”位列前五。

数据可视化

使用 python 'seaborn '库将这 10 大类别分别绘制在条形图上。创建一个代码块,该代码块循环并绘制一个类别的前 10 个邻域的图形。

接下来,将邻域的行分组在一起,并通过取平均值来计算每个类别的出现频率。

由于限制被设置为 100,Foursquare API 将返回许多地点。但是一个社区的饮食习惯可以由它附近的前五个地点来定义。此外,所创建的数据帧然后被馈入相应邻域中的前 5 个最常见的场所类别。

机器学习

“k-means”是一种无监督的机器学习算法,它创建由于某些相似性而聚集在一起的数据点的聚类。该算法将用于计算可变聚类大小的每个聚类标签的邻域。

为了实现该算法,确定最佳的聚类数(即 k)是非常重要的。有两种最流行的方法,即“肘法”和“剪影法”。

肘法

对于不同的“k”值,肘形法计算样本到其最近聚类中心的平方距离之和。最佳聚类数是这样一个值,在该值之后,距离的平方和没有显著减少。

以下是该方法的实现(具有从 1 到 49 的不同数目的聚类):

有时,肘方法不能给出所需的结果,这种情况就发生了。由于距离的平方和逐渐减少,因此无法确定最佳的聚类数。为了解决这个问题,可以实现另一种方法,如下所述。

剪影法

正如维基百科中引用的那样——“侧影法衡量的是一个点与自己的聚类(内聚)相比与其他聚类(分离)的相似程度。”

下面是此方法的一个实现。由于需要至少 2 个聚类来定义相异度,聚类数(即“k”)将在 2 到 49 之间变化:

在 k = 2、k = 4 和 k = 8 处有一个峰值。两个和四个数量的聚类将非常广泛地聚类邻域。因此,簇的数量(即“k”)被选择为 8。

k 均值

以下代码块运行聚类数= 8 的 k-Means 算法,并打印分配给不同聚类的邻域计数:

此外,所管理的聚类标签被添加到数据帧,以获得基于其附近最常见的地点分割邻域的期望结果。现在,“neighborhoods _ venues _ sorted”与“nyc_data”合并,以添加每个街区的区、纬度和经度。

同样,纽约市的街区也是利用 python“叶”库可视化的。生成的以下地图显示了所需的纽约市街区分割:

结果

群集— 0

以下是 0 类分析的结果:

“加勒比海餐厅”对这一集群负有重大责任,在不同社区的“第一最常见地点”中出现了 18 次,其次是“快餐店”和“披萨店”,在“第二最常见地点”中出现了 7 次。此外,“加勒比海餐厅”在“第二常见场所”中出现了 2 次。另外,令人好奇的是,这些社区大部分都在纽约市的“布鲁克林”区。

因此,Cluster-0 是一个“加勒比海餐馆”占优势的聚类。

集群— 1

以下是第 1 类分析的结果:

“中国餐馆”在这个集群中有 18 次出现,其次是“比萨饼店”,在不同社区的“第一大常见场所”中有 8 次出现。此外,在“第二常见地点”中,“披萨店”出现了 10 次,其次是“中国餐馆”,出现了 8 次。此外,令人好奇的是,这些社区大部分都在纽约市的“皇后”区。

因此,Cluster-1 是“中国餐馆”和“比萨饼店”的组合。

群组— 2

以下是第 2 类分析的结果:

“意大利餐厅”是该集群的主要原因,出现了 27 次,其次是“披萨店”,在不同社区的“第一大常见场所”中出现了 16 次。此外,“意大利餐厅”出现了高达 16 次,其次是“披萨店”,在“第二大常见场所”中出现了 15 次。此外,令人好奇的是,这些社区大部分都在纽约市的“斯塔滕岛”和“皇后区”。

因此,Cluster-2 是“意大利餐馆”和“比萨饼店”的组合。披萨是一种意大利美食,因此集群-2 可以被称为“意大利餐厅”主导集群。

群组— 3

以下是第 3 类分析的结果:

这是所有聚类中最大的一个,这意味着大多数的邻居都聚集在其中。同样,如聚类 2 所示,“比萨饼店”和“意大利餐馆”在“第一最常见场所”和“第二最常见场所”中占据这些类别出现次数的前 2 位,但是,有趣的是,第 3 位主要由“美国餐馆”类别占据,这就是该聚类与聚类 2 分离的原因。

此外,令人好奇的是,这个集群中的社区平均分布在“曼哈顿”、“布鲁克林”和“皇后区”,其中“斯塔滕岛”有相当数量的社区。

因此,聚类-3 是“意大利餐馆”、“比萨饼店”和“美国餐馆”的组合,其中“美国餐馆”显示了从聚类-2 中分割该聚类的优势

群组— 4

以下是 Cluster-4 分析的结果:

在这个集群中,“披萨店”占据了所有其他类别,在不同社区的“第一最常见场所”中拍摄了 52 次,其次是“中国餐馆”,在“第二最常见场所”中拍摄了 12 次。此外,令人好奇的是,这些社区中的大多数几乎平均分布在纽约市的“斯塔滕岛”、“皇后区”和“布朗克斯区”。

因此,簇 4 可以被称为“比萨饼店”优势簇。

群组— 5

以下是 Cluster-5 分析的结果:

很明显,只有一个社区“灯塔山”是在这个集群下管理的。这种分割可以从“灯塔山”是其遗产的旅游景点这一事实中得到理解,它位于从斯塔滕岛东北角辐射出来的一连串山脉的最南端。这个街区有各种各样的美食,在最常见的 5 个地点列表中,因此是一个独立的集群。

因此,到目前为止,Cluster-5 可以说是非常出色的。

群组— 6

以下是 Cluster-6 分析的结果:

“快餐店”以 28 次出现在该集群中,其次是“披萨店”,在不同社区的“第一大常见场所”中有 14 次出现。此外,在“第二常见地点”出现次数中也可以看到相同的比例分布。

另外,令人好奇的是,这些社区大部分都在纽约市的“布朗克斯”和“皇后”区。

众所周知,虽然比萨饼是一种意大利美食,但它也是一种快餐。因此,集群 6 可以被称为“快餐店”优势集群。

群组— 7

以下是 Cluster-7 分析的结果:

“美国餐厅”在不同社区的“第一最常见场所”中出现了 14 次,对这一群体负有重大责任。此外,在“第二最常见的场所”有各种美食。另外,令人好奇的是,这些社区大多位于纽约市的“曼哈顿”和“布鲁克林”区。

因此,集群 7 可以被称为“美国餐馆”优势集群。

讨论

为了理解集群,进行了三个分析,即:

1.“区”的计数

2.“第一最常见地点”的计数

3.“第二常见场所”的数量

以上信息充分说明了基于邻域间相似性度量的聚类的基础现实。

将 k 均值无监督机器学习算法的结果列表:

比萨饼,谁不喜欢它。从分析中可以明显看出,披萨店是所有集群或社区中最常见的场所。因此,由于 Pizza Place 对纽约市来说是一个现成的地方,所以它被放在一边以重命名集群。

以下可能是由 k-Means 无监督机器学习算法分割和管理的聚类的名称:

集群 0 — 加勒比海

集群 1 — 中文

集群 2 — 意大利语

集群 3 — 意大利裔美国人

集群 4 — 披萨

集群 5 — 美食组合

集群 6 —快餐

集群 7 — 美国

结论

对多维数据集应用聚类算法、k-Means 或其他算法,可以得到非常有趣的结果,这有助于理解和可视化数据。纽约市的街区被非常简单地划分为八个集群,通过分析,可以根据该街区及其周围的场地类别对它们进行重命名。除了美国菜,意大利菜和中国菜在纽约市占主导地位,多样性统计也是如此。

通过使用当前纽约市的数据集以及对美食场所更感兴趣的 API 平台(如 Yelp 等),可以改进这个项目的结果,并使其更具好奇心。)这个项目的范围可以进一步扩大,以了解每个社区的动态,并向新的供应商建议一个有利可图的地点来开设他或她的食品店。此外,政府当局可以利用它来更好地检查和研究他们城市的文化多样性。

参考

Alex Aklson 和 Polong Lin 为 Coursera 上的“应用数据科学顶点”课程制作的笔记本

感谢您的阅读!如果你觉得这有帮助或者没有帮助,请在评论中告诉我。如果这篇文章有帮助, 分享一下

访问 Jupyter 笔记本— 点击此处

领英

[## Eklavya Saxena -东北大学-马萨诸塞州波士顿| LinkedIn

东北大学精通数据科学的研究生,在以下领域有 2 年以上的工作经验

linkedin.com](https://linkedin.com/in/EklavyaSaxena)

GitHub

[## eklavyasaxena/邻里之战

纽约市是美国人口最多的城市,也是联合国总部和纽约市的所在地

github.com](https://github.com/eklavyasaxena/The-Battle-of-Neighborhoods/tree/master/Exploring-the-Taste-of-NYC-Neighborhoods)

探索东京街区:现实生活中的数据科学

原文:https://towardsdatascience.com/exploring-the-tokyo-neighborhoods-data-science-in-real-life-8b6c2454ca16?source=collection_archive---------4-----------------------

网络抓取、Foursquare API、叶子地图等等

Tokyo! (Source: Louie Martinez)

作为最终的 IBM 顶点项目的一部分,我们了解了数据科学家在现实生活中的经历。期末作业的目标是定义一个商业问题,在网上寻找数据,并使用 Foursquare 位置数据来比较东京行政区内的不同地区(城市的选择取决于学生),以找出哪个社区适合开办餐馆业务(“想法”也取决于学生个人)。为了准备作业,我一步一步地完成了问题设计、数据准备和最终分析部分。详细的代码和图片在 Github 中给出,链接可以在文章末尾找到。

1.业务问题的讨论和背景:

问题陈述:日本东京靠近办公区的午餐餐馆的前景。

我现在住的东京是世界上人口最多的大都市。东京目前在全球经济实力指数中排名第三,绝对是创业的最佳地点之一。

During the daytime, specially in the morning and lunch hours, office areas provide huge opportunities for restaurants. Reasonably priced (one lunch meal 8$) shops are usually always full during the lunch hours (11 am — 2 pm) and, given this scenario, we will go through the benefits and pitfalls of opening a breakfast cum lunch restaurant in highly densed office places. Usually the profit margin for a decent restaurant lie within 15−20% range but, it can even go high enough to 35%, as discussed here. The core of Tokyo is made of 23 wards (municipalities) but, I will later concentrate on 5 most busiest business wards of Tokyo — Chiyoda (千代田区), Chuo (中央区), Shinjuku (新宿区), Shibuya (渋谷区) and Shinagawa (品川区), to target daily office workers.

我们将讨论这个项目的每个步骤,并分别解决它们。我首先概述了最初的数据准备,并描述了在东京开始邻里之战的未来步骤。

目标受众

什么类型的客户或群体会对这个项目感兴趣?

  1. 想投资或开餐馆的商务人员。这一分析将是一个全面的指南,以在午餐时间开设或扩大针对东京大量上班族的餐厅。
  2. 喜欢把自己的餐馆作为副业的自由职业者。这个分析会给出一个思路,开一个餐厅有多大的好处,这个生意有哪些利弊。
  3. 应届毕业生,寻找靠近办公室的合理午餐/早餐地点。
  4. 初露头角的数据科学家,他们希望实现一些最常用的探索性数据分析技术,以获得必要的数据,对其进行分析,并最终能够从中讲述一个故事。

2.数据准备:

2.1.从维基百科抓取东京病房表

我首先利用 Wiki 上的东京特殊病房页面来创建一个数据框。为此,我使用了请求Beautifulsoup4 库来创建一个包含东京 23 个区的名称、面积、人口和第一大区的数据框。我们从下面开始—

稍加处理后,数据帧如下所示

Data-frame from Wikipedia Table.

2.2.获取主要区域的坐标: Geopy 客户端

下一个目标是使用 Geopy 客户端的 geocoder 类获得这 23 个主要地区的坐标。使用下面的代码片段—

正如你所看到的,4 个坐标是完全错误的(文京、江东、大田、江户川),这是由于这些地区的名称写得与它们在这个数据框中的方式没有什么不同(例如 hong \u- Hongo),所以,我必须用从 google 搜索获得的值替换这些坐标。在和熊猫玩了一会儿之后,我可以得到一个排列良好的数据框,如下所示

2.3.东京主要行政区的平均地价:网络抓取

另一个可以指导我们以后决定哪个地区最适合开餐馆的因素是,23 个区的平均土地价格。我从刮“东京土地市场价值区域”网页上获得这些信息,与之前的维基页面类似。由于我想考虑第 1 节中提到的东京 5 个最繁忙的商业区,数据框如下所示

2.4.使用 Foursquare 位置数据:

Foursquare 的数据非常全面,它为苹果、优步等公司提供位置数据。对于这个商业问题,作为作业的一部分,我使用了 Foursquare API 来检索东京这 5 个主要地区的热门景点的信息。返回的热门景点取决于最高的客流量,因此取决于进行呼叫的时间。因此,根据一天中不同的时间,我们可能会得到不同的受欢迎的场所。该调用返回一个 JSON 文件,我们需要将它转换成一个数据帧。在这里,我为半径 1 公里内的每个主要地区选择了 100 个热门景点。下面是从 Foursquare 返回的 JSON 文件中获得的数据帧

3.可视化和数据探索:

3.1.树叶库和树叶地图:

Folium 是一个 python 库,可以使用坐标数据创建交互式传单地图。因为我对作为热门地点的餐馆感兴趣,所以我首先创建一个数据框,其中前面的数据框中的‘Venue_Category’列包含单词“餐馆”。我用了下面这段代码—

下一步是使用此数据框创建一个带有叶子的传单地图,以查看 5 个主要地区中最常去的餐馆的分布。

传单地图上面的代码片段如下所示

Figure 1: Circular marks represent the most frequently visited restaurants in the 5 Major (Nihombashi- Green, Nagatacho- Red, Shibuya- Orange, Shinjuku- Magenta, Shinagawa- Blue) districts of Tokyo, according to Foursquare data.

3.2.探索性数据分析:

有 134 个独特的场所类别,拉面餐厅名列榜首,正如我们在下面的图表中看到的—

Figure 2: Most Frequent venues around Shinjuku, Shibuya, Nagatacho, Nihombashi, Shinagawa, according to Foursquare data.

现在,这让我想起了拉面,的确是时候休息一下了。

Ramen Restaurants are the most frequently visited places around 5 major districts of Tokyo. Yum!

在美味的拉面之后,让我们回到探索数据上来。要了解每个地区排名前 5 的场馆,我们按如下步骤进行

  • 为场馆类别创建一个带有熊猫 one hot 编码的数据框。
  • 使用地区列上的 pandas groupby,并获得一次性编码场馆类别的平均值。
  • 转置步骤 2 中的数据帧,并按降序排列。

让我们看看下面的代码片段—

上面的代码输出每个地区的前 5 个场馆—

从我为探索性数据分析创建的几个数据框中,使用其中的一个,我绘制了哪个区有最常去的餐馆,千代田区的永田町有 56 家餐馆。

Figure 4: Number of restaurants as top most common venues in 5 districts of Tokyo.

我们也可以看看小提琴图 s,它们被用来表示分类数据,我用 seaborn 图书馆展示了 4 种主要类型的餐馆在不同地区的分布

Figure 5: Lots of Japanese and Chinese restaurants in Nagatacho, whereas Shinagawa has many Ramen restaurants.

一旦我们对东京 5 个主要地区的不同类型的场所,特别是餐馆有了大致的了解,就该使用 K-Means 对这些地区进行聚类了。

4.聚集地区

最后,我们尝试根据场地类别对这 5 个地区进行聚类,并使用 K-Means 聚类。因此,我们的预期将基于场馆类别的相似性,这些地区将被分组。我使用了下面的代码片段—

5 districts of Tokyo divided in 3 clusters based on the most common venues obtained from Foursquare Data.

我们可以使用叶库在活页地图中表示这 3 个聚类,如下所示—

Figure 6: 5 major districts of Tokyo segmented into 3 clusters based on the most common venues. The size of the circles represents number of restaurants as most common venues for each district, which is highest at Nagatacho and lowest at Shibuya as shown in figure 4.

5.结果和讨论:

在分析的最后,我们得到了东京 5 个主要行政区的一个秘密峰值,由于商业问题始于在最繁忙的地区之一开一家午餐餐馆的好处和缺点,数据探索主要集中在餐馆上。我使用了来自维基百科等网络资源、Geopy 等 python 库和 Foursquare API 的数据,建立了一个非常真实的数据分析场景。我们发现—

  • 拉面餐厅在这五个地区最常见的场所中名列前茅。
  • 千代田区的永田町区和中央区的日本桥以餐馆为主,是最常见的场所,而涉谷和新宿区以酒吧、酒馆和咖啡馆为主,是最常见的场所。
  • 永田町的餐厅数量最多,是最常见的场所,而涩谷区的餐厅数量最少。
  • 由于聚类仅基于每个地区最常见的场馆,新宿、涉谷属于同一聚类,永田町、日本桥属于另一聚类。品川与这两个集群是分开的,因为便利店是最常见的场所(频率非常高)。

根据该分析,品川区将为即将开业的午餐餐厅提供最少的竞争,因为便利店是该地区最常见的场所,与其他地区相比,餐厅作为常见场所的频率非常低。从网上废弃的数据也可以看出,品川及其周边地区的平均土地价格比靠近东京市中心的地区要便宜得多。所以,毫无疑问,这个地区有可能成为开设高档餐厅的目标。这种分析的一些缺点是——聚类完全基于从 Foursquare 数据中获得的最常见的场所。由于土地价格、场馆与最近车站的距离、潜在客户的数量、品川作为港口地区的优势和劣势都可能发挥重要作用,因此,这一分析肯定远非结论性的。然而,它确实给了我们一些非常重要的初步信息,关于在东京主要地区周围开餐馆的可能性。此外,这种分析的另一个缺陷可能是只考虑东京每个区的一个主要地区,考虑 5 个主要区下的所有地区会给我们一个更现实的画面。此外,如果我们使用 DBSCAN 等其他聚类技术,这个结果也可能会有所不同。我写了一篇单独的帖子介绍 DBSCAN 的详细理论以及我们如何使用它来集群空间数据库。

6.结论

最后,为了结束这个项目,我们已经对现实生活中的数据科学项目有了一点了解。我使用了一些常用的 python 库来删除 web 数据,使用 Foursquare API 来探索东京的主要地区,并看到了使用小叶地图分割地区的结果。这种分析在现实生活商业问题中的潜力将被详细讨论。此外,还提到了一些缺点和改进的机会,以呈现更真实的图片。最后,由于我的分析主要集中在开一家针对大量办公室工作人员的餐馆的可能性上,所以得到的一些结果出乎意料地与我在东京呆了 5 年后的预期完全一致。特别是新宿和涉谷地区最常去的咖啡馆、酒吧和酒馆,以及永田町地区日本桥附近的日本餐馆!希望这种分析能够为您提供初步的指导,让您能够利用数据科学应对更多现实生活中的挑战。

保持坚强,干杯!!

Github 中找到代码。

Linkedin 找到我。

使用 Python 探索多伦多自行车共享出行

原文:https://towardsdatascience.com/exploring-toronto-bike-share-ridership-using-python-3dc87d35cb62?source=collection_archive---------9-----------------------

在过去的几年里,自行车共享已经成为一种越来越有吸引力的城市出行方式。考虑到我家附近自行车被盗的数量、Bike Share 站点覆盖范围的迅速扩大以及 TTC 票价的上涨,今年 3 月我很容易就决定续签第三年的会员资格。

美国人也在做出类似的决定:2017 年,美国的自行车爱好者通过自行车共享计划进行了 3500 万次出行,比 2016 年增长了 25%。与此同时,自行车共享多伦多见证了同一时期81%的乘客量增长。

我想探索自行车共享乘客数据集,以更好地了解多伦多人如何使用自行车共享,并展示一些可用于探索由开放数据计划发布的其他 294 个(还在统计中)数据集的技术。

关于数据

Bike Share 使用第三方机构提供的系统收集数据。由于 2016 年提供商变更,历史乘客数据的可用性有限。我决定专注于 2017 年的数据。数据可以从开放数据目录或新的门户(今年晚些时候取代目录的新网站)中检索。

如果你愿意跟随我的步骤,我使用的笔记本也可以在 GitHub 上找到。

该数据是一个包含多个 CSV 的 ZIP 文件,按季度分隔年度数据。对 Q1 数据的初步检查显示,数据中的每个记录代表一次独特的旅行,并包含以下各列:

  • trip_id :每次行程的唯一标识符
  • 行程开始时间:行程开始的时间
  • 行程停止时间:行程结束的时间
  • trip_duration_seconds :以秒为单位测量的行程持续时间
  • from_station_id :旅程开始的车站 id
  • from_station_name :行程开始的车站名称
  • to_station_id :行程结束的站点 id
  • to_station_name :行程结束的车站名称
  • user_type :识别用户是会员还是购买了通行证

但是,进一步研究其他文件中的数据会发现一些问题:

  1. 文件之间的日期时间格式有所不同(例如,2017 年 12 月 31 日 12:30 与 2017 年 12 月 30 日 12:30:00)
  2. 只有 Q1 和 Q2 的数据包含站点 id
  3. 车站名称不一致(例如湖岸大道与湖岸大道)
  4. 起点站和终点站相同的行程,表明潜在的数据收集错误

不理想,但在典型的数据分析项目中也相当常见。在任何分析中,数据清理占用高达 80%的时间并不罕见。来自真实世界的数据通常是杂乱的,在进行任何分析或建模之前,必须对其进行清理。虽然数据清理既繁琐又耗时,但它也会对最终结果产生重大影响。

为了这个分析,我创建了一个包含 Jupyter 笔记本的新目录和一个包含我使用的所有数据的数据文件夹。

数据清理

该步骤的目标是将来自多个来源的数据整合到一个单一的 pandas 数据框架中,该框架包含标准化的日期和站点信息,并且剔除了异常值。参见这篇文章了解更多其他情况下常见数据清理步骤的深入分析。

Bike Share 公开了包含服务信息的 API 端点。station_information 端点可用作事实的来源,以解决桩号 id 和名称之间的不一致,还可以使用每个桩号的附加地理空间信息来增强数据。

标准化

首先,我导入了所需的库,并从自行车共享 API 端点加载了站点数据:

接下来,我手工确定了每个文件中使用的日期结构,并使用确定的结构将乘客数据连接成一个数据帧。

此外,来自 Q1 和 Q2 的数据位于 UTC 时区(提前 4 小时),而来自 Q3 和 Q4 的数据位于东部时区。这一点并不明显,我只是在后来可视化数据时才意识到这个问题。数据清理是一个迭代的过程,几乎不可能一次就发现所有的问题。

接下来,我需要解决站点 id 和名称的问题。为了提高效率,我提取了电台 ID 和电台名称的唯一组合。

具有 id 的站可以很容易地从 API 数据更新,但是没有 id 的站需要稍微复杂一点的解决方案。模糊匹配是一种用于识别相似事物的技术(例如央街 vs 央街)。Python 中有很多模糊匹配字符串的方法,我选择了使用 fuzzywuzzy 库。

最后,我连接了站点数据,将其与 API 数据合并以包含站点的位置坐标,并用正确的站点 id 和名称更新了数据帧。

与多伦多停车管理局(TPA )(负责管理自行车共享的城市部门)的对话显示,车站 id 与物理终端而不是位置相对应。如果终端移动,API 将只显示新的坐标。因此,通过与车站 id 上的 API 数据合并,存在一些起点或终点位置不正确的行程。

极端值

在移除异常值之前,我使用了describe()函数来查看数据帧中数据的简单轮廓(为了更清晰地查看,我排除了所有 ID 列):

考虑到 Bike Share 的定价模式,我预计大部分行程不到 30 分钟。但是数据显示旅行持续时间从 1 到 6,382,030 秒不等(约 74 天),表明数据中有异常值。

在他们自己分析中,TPA 通常认为少于一分钟的行程是错误的行程。我去掉了这 29595 次出行,约占总出行的 2%。

从数据中剔除异常值的常用统计方法是考虑四分位距(IQR),即数据的中间 50%。异常值定义为距离中值 1.5*IQR 的数据点。我用这种方法去除了一些异常值。参见这篇文章中一个很好的分步示例。

最后,我保存了清理后的数据以备后用。

研究问题定义

在开始分析之前,我们应该在分析结束时定义我们想要回答的问题(否则我经常会创建一个又一个没有得出特定结论的图表)。我们采用了一种相当非正式的方法进行分析:

  1. 集思广益,讨论各种与城市自行车共享和骑自行车相关的问题
  2. 确定这些问题是否可以用现有数据来回答,或者是否需要其他外部数据集
  3. 删除了由于数据访问受限而无法回答的问题(例如,由于隐私原因删除了用户信息)
  4. 将遗留的问题分成几个主题

A sample of the questions identified relating to Bike Share and biking in Toronto

我们确定的最终主要类别是:

  • 谁在使用自行车共享服务?会员和临时用户的行为有明显的区别吗?如果有,这些区别是什么?
  • 什么时候人们开始使用自行车共享?一年中、一周中和一天中的使用情况如何变化?
  • 自行车共享的使用情况如何?人们主要使用自行车共享作为通勤上班或探索城市的方式吗?天气如何改变人们使用自行车共享的方式?

分析和可视化

就个人而言,我更喜欢创建一个新的 Jupyter 笔记本,仅用于分析。在新的笔记本中,我首先导入库和清理的数据,然后为一周中的某一天(星期一、星期二等)创建新的 pandas Categorical 数据类型。)和月份名称,以确保固定的排序顺序(在可视化数据时很有用)。

接下来,我转换了数据,使以后的分析和可视化更加容易。这些转变包括:

  • 为清晰起见,重命名了字段
  • 从旅行开始时间解析出季度、月份、日期、星期几和小时
  • 以“起点站 id-终点站 ID”的格式为每个行程生成新的 route_id,以标识行程的路线

接下来,我还计算了起点站和终点站之间的距离。这增加了另一个维度,以后可能会有帮助。

谁在使用自行车共享?

回答“谁”这个问题的一个常用方法是研究人口统计学。最初,根据车站位置估计人口似乎是有希望的。例如,我们可能期望位于年轻人口居住区的电视台更受欢迎。

然而,大多数车站位于地铁/有轨电车线路附近,并且位于市中心。因此,结果可能偏向不同于用户真实代表的特定人口统计。请随意使用邻域概况数据集对此进行探索。

因为我不能可靠地推断出任何用户信息,所以我只能使用用户类型(会员和临时会员)。我深入研究了用户类型,并可视化了行程持续时间和距离的分布。

该图显示了会员和临时用户之间行为的明显差异:虽然两个群体通常在相似的距离内旅行,但会员到达目的地的速度要快得多。

在接下来的章节中,我通过查看这些用户使用自行车共享的时间和方式,进一步分析了他们之间的行为差异。

什么时候使用自行车共享?

我按照用户类型和旅行日期汇总了数据,并计算了每天的旅行次数。

然后,我设想了 2017 年的乘客趋势。

虽然图表显示了一些预期的趋势(例如,在温暖的月份有更多的乘客),但也出现了一些不太预期的模式:

  • 会员和临时用户之间的旺季转移
  • 在小时间范围内持续上升和下降

我放大并可视化了几个月的数据,以便在更精细的层次上获得更好的视图。

虽然 7 月份的趋势更加明显,但图表显示每周的乘客量会增加和减少。为了进一步梳理这个循环,我可视化了每个工作日的平均每日出行量,并按照季度和用户类型来分离数据。

这些图表进一步讲述了不同用户类型如何骑车的故事:会员主要在工作日骑车,而临时用户主要在周末骑车。唯一的例外是第三季度,周三的休闲旅行显著增加。深入研究这几个月的数据显示,这实际上只发生在 7 月。

谷歌快速搜索显示,这是因为 Bike Share 在 2017 年 7 月周三提供免费服务。

最后,我还想看看一天中每小时的乘客趋势。

此外,该图显示了会员和临时用户之间行为的明显差异:会员在早上 8 点和下午 5 点左右经历高峰时间,而临时用户在一天中更加稳定。

这与之前的发现(会员主要在工作日使用自行车共享)相结合,表明会员通常使用自行车共享作为通勤方式,而临时用户则使用自行车共享进行休闲旅行。

自行车共享是如何使用的?

前面部分的分析已经回答了我提出的一些关于如何使用自行车共享的问题(如会员通勤)。相反,我决定把重点放在天气和乘客量之间的关系上。

我从加拿大的政府气象站获取了每日的温度和降水数据,从多伦多城市气象站获取了数据

首先,我将乘客数据与天气数据合并,并创建了一个 pairplot,以便在较高的层面上理解变量之间的关系。此外,我创建了一个热图来可视化变量之间的皮尔逊相关系数。

虽然我最初对降雨量和乘车量之间的低相关性感到惊讶,但这个结果是有意义的。该数据仅提供特定气象站位置每天的总降雨量。然而,一整天的降雨量很少是一致的,在所有自行车共享站的位置上也很少是均匀的。由于乘客量随小时而变化,我需要多个气象站每小时的降雨量数据,以了解这些变量之间的关系。

使用雨量计数据随意探索这个话题,对于这个分析的其余部分,我将重点放在温度上。我将不同时间的客流量和温度可视化为会员和临时用户的双轴图。

事实上,在温暖的月份里,人们越来越多地骑自行车,这有点“废话”所以我想更进一步,找到人们决定是否骑自行车的转折点。我决定只关注临时用户,因为他们与温度的相关性更高,但是同样的想法也适用于这两种用户类型。

我在试图定义这一点时遇到了一些困难。直觉上,似乎必须有一种方法来从数学上确定这一点,然而,许多行业只是根据长期的经验观察来确定这一点。最后,我的一个同事找到了这篇文章,它提出了一种基于曲线的通用方法。原来已经有一个 Python 库在使用这个应用程序: kneed

kneed 库要求在确定点之前对数据进行曲线拟合。给定数据后,我将数据拟合为线性函数、Y 轴无偏移的指数函数和指数函数,然后评估结果。

只有没有偏移的指数增长导致了可以确定拐点的曲线(其他两条曲线太线性)。结果显示该点在 16.1℃

我可以通过使用百分比累积行程(即计算 x 百分比的行程发生在 y C)下,其中曲线拟合函数将是一个最大极限为 100%的逻辑增长。该方法显示的温度为 16.0℃

利用类似的概念,我想找到一个点,在这个点上,乘客数量会随着温度的升高而停止增长。直觉上,一旦温度“足够高”,用户不再关心温度是有意义的(例如,22℃和 23℃之间的差异不会成为用户决定是否骑自行车的决定性因素)。为了找到这一点,我简单地将相同的计算应用于大于初始点的曲线部分,我发现这第二个点是 21.6 C。

我有兴趣将这一概念用于一个更温暖的城市的数据,那里的日平均温度可能达到 30+ C,看看在什么温度下热量会抑制乘车。

结论和下一步措施

虽然这个数据集本身相当简单,只包含几列基本信息,但我能够通过将数据与其他公共数据集合并来确定一些细微的见解。

在我们与 TPA 的交谈中,他们提到,尽管他们努力将自行车共享作为旅行“第一英里和最后一英里”的方法,但公众通常将自行车共享视为促进多伦多旅游业的一项计划。该分析中确定的行为表明,大多数自行车共享用户实际上是经常骑自行车的通勤者。

虽然用户信息不公开,无法进行用户行为分析,但深入研究自行车共享计划的地理空间方面是值得的。例如,将 TTC/GO 站(或其他公共中心)的数量与自行车共享站的受欢迎程度联系起来,或者进一步考虑站点之间自行车道的路线和可用性。

我希望这个数据故事提供了使用 Python 进行数据分析的介绍,并确定了您在进行分析时可能要考虑的一些工具。如果您有任何问题,请随时通过 opendata@toronto.ca 联系开放数据团队,我鼓励您使用开放数据进行自己的分析。

探索多伦多街区-开一家印度餐馆

原文:https://towardsdatascience.com/exploring-toronto-neighborhoods-to-open-an-indian-restaurant-ff4dd6bf8c8a?source=collection_archive---------16-----------------------

网络抓取、Foursquare API、叶子地图和机器学习

Toronto, City in Ontario, Canada

作为 IBM 数据科学专业计划 Capstone 项目的一部分,我们使用真实数据集来体验数据科学家在现实生活中的经历。这个项目的主要目标是定义一个商业问题,在网上寻找数据,并使用 Foursquare 位置数据来比较多伦多的不同社区,以找出哪个社区适合开办新的餐馆业务。在这个项目中,我们将一步一步地完成从问题设计、数据准备到最终分析的所有过程,并最终提供一个商业利益相关者可以用来做决策的结论。

1.业务问题描述和背景讨论(介绍部分):

问题陈述:在加拿大多伦多开一家印度餐馆的前景。

多伦多,安大略省的省会,是加拿大人口最多的城市。它的多样性反映在多伦多的种族社区,如唐人街,Corso Italia,Greektown,Kensington Market,Koreatown,Little India,Little Italy,Little Jamaica,Little Portugal & Roncesvalles。多伦多是北美对移民最友好的城市之一,超过一半的印裔加拿大人居住在这里,这里是开印度餐馆的最佳地点之一。

在这个项目中,我们将通过一步一步的过程来决定开一家印度餐馆是否是一个好主意。我们分析了多伦多的社区,以确定最赚钱的地区,因为餐厅的成功取决于人和氛围。因为我们已经知道多伦多比加拿大任何其他城市都容纳了更多的印度人,所以在这里开餐馆是个好主意,但我们需要确定这是否有利可图。如果是这样,我们可以把它放在哪里,这样它会给所有者带来更多的利润。

Source: Bethesda Indian Food Festival

目标受众

谁会对这个项目更感兴趣?什么样的客户或群体会受益?

  1. 想在多伦多投资或开印度餐馆的商务人员。这一分析将成为针对印度人群开办或扩大餐馆的全面指南。
  2. 喜欢把自己的餐馆作为副业的自由职业者。这个分析会给出一个思路,开一个餐厅有多大的好处,这个生意有哪些利弊。
  3. 想要寻找有很多印度餐馆的街区的印度人。
  4. 业务分析师或数据科学家,他们希望使用探索性数据分析和其他统计和机器学习技术来分析多伦多的街区,以获得所有必要的数据,对其执行一些操作,并最终能够讲述一个故事。

2.数据采集和清理:

2.1 数据来源

a)我正在使用“加拿大邮政编码列表:M”(https://en . Wikipedia . org/wiki/List _ of _ Postal _ codes _ of _ Canada:_ M)wiki 页面来获取多伦多现有街区的所有信息。这一页有邮政编码,borough &是多伦多所有街区的名称。

b)然后我使用“【https://cocl.us/Geospatial_data"】CSV”文件来获取所有街区的地理坐标。

c)为了获得按种族划分的人口分布信息,我使用了“多伦多人口统计”(https://en . m . Wikipedia . org/wiki/Demographics _ of _ Toronto # Ethnic _ diversity)维基页面。使用此页面,我将确定印度人密集的社区,因为这可能有助于确定合适的社区来开设一家新的印度餐馆。

d)我使用 Foursquare 的 explore API 来获取多伦多各个场馆的位置和其他信息。使用 Foursquare 的 explore API(提供场馆推荐),我获取了多伦多现有场馆的详细信息,并收集了它们的名称、类别和位置(纬度和经度)。

从 four square API(https://developer.foursquare.com/docs)),我为每个场馆检索了以下内容:

  • 名称:场地名称。
  • 类别:由 API 定义的类别类型。
  • 纬度:场地的纬度值。
  • 经度:场地的经度值。

2.2 数据清理

a)从维基百科抓取多伦多邻居表

抓取了下面的 Wikipedia 页面,“加拿大邮政编码列表:M ”,以获得多伦多&及其邻近地区的数据。

为获得以下数据框架所做的假设:

  • 数据帧将由三列组成:邮政编码、区和邻居
  • 只会处理具有指定区的像元。未分配的行政区将被忽略。
  • 一个邮政编码区可以有多个邻居。例如,在维基百科页面的表格中,您会注意到 M5A 被列出了两次,并且有两个街区:Harbourfront 和 Regent Park。这两行将合并成一行,相邻行用逗号分隔,如上表中的第 11 行所示。
  • 如果像元有一个区,但没有分配邻域,那么邻域将与区相同。

Wikipedia —包用于从 wiki 中抓取数据。

Dataframe formed from the scraped wiki page

经过一些清理,我们得到了带有邮政编码、行政区和社区信息的正确数据帧。

Dataframe from ‘List of Postal code of Canada: M’ Wikipedia Table.

b)向邻居添加地理坐标

下一个重要步骤是将地理坐标添加到这些社区中。为此,我提取了地理空间数据 csv 文件中的数据,并根据邮政编码将它们与现有的邻域数据帧合并。

DataFrame with latitude & longitude of Postal codes in Toronto

我将对这些列进行重命名,以匹配现有的由加拿大邮政编码列表组成的数据框架。之后,我通过合并邮政编码将两个数据帧合并成一个。

Merged new dataframe with info about Neighborhoods, borough, postalcode, latitude & longitude in Toronto

c)从维基百科中删除人口分布

另一个可以帮助我们决定哪个社区是开餐馆的最佳选择的因素是,每个社区基于种族多样性的人口分布。因为这有助于我们识别印度人聚居的街区,因为那是开印度餐馆的理想地点。

抓取了下面的维基百科页面,“多伦多人口统计”,以获得关于多伦多及其邻近地区的数据。与下面多伦多的所有街区相比,给定的街区只有相当数量的印度人。我们正在检查这些社区的人口,以确定印度人口密集的社区。

Scraping the wiki page

多伦多只有六个街区有印度人居住,所以我们收集了这些街区的人口比例。

TORONTO & EAST YORK population distribution by ethnicity

NORTH YORK population distribution by ethnicity

SCARBOROUGH population distribution by ethnicity

ETOBICOKE & YORK population distribution by ethnicity

d)使用 Foursquare 获取位置数据

Foursquare API 是一个非常有用的在线应用程序,被我的许多开发者和其他应用程序如优步等使用。在这个项目中,我用它来检索多伦多附近地区的信息。API 返回一个 JSON 文件,我们需要将它转换成数据帧。在这里,我为半径 1 公里内的每个社区选择了 100 个热门景点。

Dataframe with venues in each neighborhood along with the category info of the venues.

3.探索性数据分析:

3.1 叶片库和叶片图

Folium 是一个 python 库,我用它来绘制一个使用坐标数据的交互式传单地图。

code to draw the folium map

Folium map of Toronto Neighborhood with popup label

3.2 邻里和印度餐厅的关系

首先,我们将从上面的多伦多数据框架中提取邻居和印度餐馆列,以供进一步分析:

Dataframe formed using Foursquare api information about venues in each neighborhoods

代码片段

Manipulating the data to make the analysis easy

在为场馆类别执行了 pandas one hot encoding 之后,让我们将这个数据帧与多伦多数据帧合并,该数据帧带有附近地区的纬度&经度信息。最后,只提取印度餐馆的值和街区信息。

Toronto Dataframe for Indian restaurants count in each neighborhood

让我们试着用上面的数据框画一些图:

Figure: Violin plot

借助这张小提琴图,我们可以识别出印度餐馆密集的行政区。它是使用 seaborn library 绘制的,显示了印度餐馆在不同行政区的分布。

让我们想象一下有印度餐馆的街区:

Figure : Bar plot

3.3 邻里和印第安人的关系

另一个重要特征是印度人在每个社区的分布。让我们分析一下这些街区,找出印度人口最多的街区。

为了实现这一点,我们将使用 wiki 页面的所有社区数据框架与少数民族人口连接起来,并且我们只提取每个社区的印度人口。

Dataframe with neighborhoods & their population distribution

Extracted dataframe with just Indian population information

让我们画一张图来直观地显示社区中的人口分布:

Bar graph to show the population in each ridings in Toronto

这种对邻里之间的关系以及这些邻里中的印度人口的分析和可视化有助于我们识别人口高度密集的印度邻里。一旦我们确定了这些社区,它就能帮助我们决定在哪里开新的印度餐馆。位于人口稠密的印度社区的印度餐馆比位于印度人口较少或没有印度人口的社区的餐馆更有可能吸引更多的印度顾客。因此,这种分析有助于确定新印度餐馆的成功。

3.4 印度人口与印度餐馆的关系

在进行数据清理和数据分析后,我们无法确定人口密集的印度社区和印度餐馆数量之间的关系。这可能是因为数据中的缺失,因为这是一个可以在未来的分析中改进的领域,以便更深入地了解业务问题。

Dataframe of densely populated neighborhoods with number of Indian restaurants

4.预测建模:

4.1 多伦多的聚集街区:

K 均值聚类的第一步是确定最佳 K 值,即给定数据集中的聚类数。为此,我们将对具有印度餐馆百分比的多伦多数据集使用肘方法。

Elbow method to identify best k value

代码片段—

Elbow visualizer to identify the K value

用肘法分析后用失真评分&对每个 K 值的平方误差,看起来 K = 6 是最佳值。

使用 K =6 的 K-Means 聚类多伦多邻域

6 clusters & its labels

code snippet for clustering the Toronto dataframe

Dataframe with cluster labels for neighborhood

Folium map for the clusters of different neighborhoods

4.2 检查集群:

我们总共有 6 个集群,如 0,1,2,3,4,5。让我们一个接一个地检查。

聚类 0 包含印度餐馆数量最少的所有邻域。它在地图中以红色显示

聚类 1 包含人口稀少的印度餐馆。它在地图上以紫色显示。

聚类 2 & 4 没有行,这意味着在这些质心附近没有数据点或邻域。

聚类 3 包含所有中等印度餐馆数量的邻近地区。它在地图中以蓝色显示。

聚类 5 包含印度餐馆密集分布的所有邻近地区。它在地图中以橙色显示

5.结果和讨论:

5.1 结果

我们已经到达了分析的末尾,在这一节中,我们将记录上述数据集的聚类和可视化的所有发现。在这个项目中,我们从寻找一个好的社区来开一家新的印度餐馆的商业问题开始。为了实现这个目标,我们调查了多伦多的所有街区,分析了每个街区的印度人口&这些街区的印度餐馆数量,以得出哪个街区更好的结论。我们使用了各种数据源来建立一个非常现实的数据分析场景。我们发现—

  • 在这 11 个行政区中,借助于多伦多行政区印度餐馆数量之间的小提琴图,我们发现只有多伦多市中心、多伦多市中心、东多伦多、东约克、北约克和斯卡伯勒行政区有大量的印度餐馆。
  • 在所有骑马区中,斯卡伯勒-吉尔伍德、斯卡伯勒-鲁日公园、斯卡伯勒中心、斯卡伯勒北部、亨伯河-布莱克克里克、东河谷、斯卡伯勒西南部、东河谷和斯卡伯勒-阿金库尔是印第安人骑马区的密集区。
  • 借助集群检查&小提琴阴谋看起来像多伦多市中心,多伦多市中心,东纽约已经密集的印度餐馆。因此,最好不要考虑这些行政区,而只考虑新餐馆的地点——斯卡伯勒、东多伦多和北约克。
  • 经过仔细考虑,在斯卡伯勒开一家新的印度餐馆是一个好主意,因为那里有大量的印度人口,这给了更多的顾客可能性和更低的竞争,因为附近的印度餐馆很少。

5.2 讨论

根据这一分析,斯卡伯勒将为即将开业的新印度餐馆提供最少的竞争,因为很少有印度餐馆或在几个社区没有印度餐馆。此外,从人口分布来看,这里似乎是印度人的聚居地,这有助于新餐厅提高顾客到访的可能性。所以,毫无疑问,这个地区可能是开办优质印度餐馆的绝佳地点。这种分析的一些缺点是-聚类完全只基于从 Foursquare API 获得的数据,关于每个街区的印度人口分布的数据也是基于 2016 年的人口普查,而不是最新的。因此,人口分布数据有 3 年的巨大差距。尽管有很多地方可以改进,但这一分析无疑为我们提供了一些很好的见解,关于可能性的初步信息&通过正确地设置垫脚石来开始这个业务问题。

6.结论:

最后,为了结束这个项目,我们有机会讨论一个商业问题,比如一个真正的数据科学家会怎么做。我们使用了许多 python 库来获取数据、操作内容&分析和可视化这些数据集。我们利用 Foursquare API 探索多伦多附近的场馆,然后从维基百科获得大量数据,这些数据是我们在维基百科 python 库的帮助下收集的,并使用 seaborn & matplotlib 中的各种情节进行了可视化。我们还应用机器学习技术来预测给定数据的输出,并使用 follow 将其可视化在地图上。

一些缺点或改进领域向我们表明,这种分析可以在更多数据和不同机器学习技术的帮助下进一步改进。同样,我们可以使用这个项目来分析任何场景,如开设一个不同的美食餐厅或开设一个新的健身房等。希望这个项目有助于作为使用数据科学应对更复杂的现实生活挑战的初始指导。

github 中找到代码,但是使用 nbviewer 和 github url 查看。

LinkedIn 找我!

使用 Golang 探索多伦多选民统计

原文:https://towardsdatascience.com/exploring-toronto-voter-statistics-using-golang-33077b6db123?source=collection_archive---------27-----------------------

十月标志着加拿大联邦选举活动的正式开始。因此,我对探索由多伦多开放数据发布的多伦多选民统计数据集很感兴趣。虽然数据是从多伦多市政选举中收集的,但它似乎是一个有趣的数据集,可以提供一些关于多伦多人如何投票的见解。

选民投票率低被认为是特朗普赢得 2016 年美国总统大选的主要原因之一。虽然加拿大自己的 2015 年联邦选举出现了几十年来最高的投票率,全国平均投票率为 68.49%,但我很好奇这一趋势是否也反映在加拿大最大的直辖市的地方层面。

与我之前研究过的多伦多自行车共享出行数据集不同,这个数据集结构良好,易于使用。我没有像往常一样使用 Python,而是决定利用这个数据集来探索 Go 的数据处理和可视化能力。

围棋进入我的视野已经有一段时间了。尽管 Go 并不以其与数据相关的功能而闻名,但诸如 gophernotesgonum 之类的软件包正在使与数据相关的工作变得更加平易近人。此外,像这样的帖子正在令人信服地论证为什么对于数据密集型应用程序,Go 可能是比 Python 更好的选择。

关于数据

提供了 2003-2018 年过去 5 次选举的数据。但是,由于 2018 年的病房数量从 44 个变成了 25 个,我决定把事情简单化,只关注 2018 年选举的数据。

所提供的数据由选区和投票站汇总,并提供以下信息:

  • 选区和投票站号码
  • 投票站地点名称和地址
  • 选民名单上的选民人数、选民名单的修改以及最终合格选民的人数
  • 投票的选民人数
  • 学校支持的选民人数
  • 被拒绝和拒绝的选票计数

我感兴趣的是根据投票站地址对数据进行地理编码,并将历史投票数据映射到包含 25 个选区的新地图上。这将使我能够跟踪这些年来投票趋势的变化。目前,我认为这已经超出了探究 Go 的范围。

如果你愿意跟随,我使用的数据和笔记本可以在 GitHub 上的这里找到。

准备

在深入分析之前,我确定了一些我有兴趣回答的研究问题:

  1. 道岔按病区分布是怎样的?在比较高投票率病房和低投票率病房时有什么模式吗?
  2. 病房的变化对道岔有什么影响?
  3. 这些年来,登记选民和投票人数有什么变化?
  4. 进入投票站的便利性对投票率有影响吗?
  5. 投票站的投票率高低有什么规律吗?

问题 2、3 和 4 需要与之前的选举年份进行比较或进行地理编码,因此不在本次分析的范围之内。

我定义了一些函数,将在后面的分析中使用:

数据本身的结构相当好,数据清理很少。混合在数据(按选区和投票站汇总)中的还有按选区汇总的数据行。我删除了这些行,因为以后可以很容易地计算它们。

原始数据以 Excel 表格的形式提供,为了便于导入,我手动将其转换为 CSV 格式。然后,我通过以下方式将数据读入内存:

道岔分布

gonum 绘图库使可视化变得相当简单。我把病房和道岔想象成:

有几件事引起了我的注意。首先,2018 年多伦多市政选举的投票率比 2015 年的联邦选举少得多。所有 25 个选区的平均投票率为 40.67%,比 2016 年的联邦投票率低约 28%。

最高和最低失误率的病房之间也有很大的差异。14 区投票率最高,为 49.22%,23 区投票率最低,为 34.05%。

快速的视觉比较显示,客流量最大的 5 个病房(14、15、12、19 和 4 号病房)都位于市中心附近。人数最少的 5 个区(23 区、7 区、10 区、1 区和 21 区)大多位于郊区,10 区是个例外。

https://www.toronto.ca/city-government/data-research-maps/neighbourhoods-communities/ward-profiles/

在地图上进行视觉对比感觉不太靠谱,所以我从选区概况数据集中提取了一部分数据进行更系统的分析,并重点研究了教育和收入对选民投票率的影响。我使用的提取数据文件在 GitHub 上也有。

该数据集按选区显示了最新的人口普查数据。由于人口普查数据是在 2016 年收集的,我假设在 2016 年和选举之间没有发生重大的人口统计变化。我特别关注两个因素:

  1. 受过大专或高等教育的人口百分比
  2. 总收入高于 80,000 美元的家庭人口百分比(2016 年家庭收入中位数为78,373 美元)

教育和收入的皮尔逊相关系数分别为 0.57 和 0.55。基于此,似乎可以公平地说,教育和收入对投票率都有积极影响。虽然相关值较低,但这很可能是由于其他因素也影响了选民投票的可能性。

第 10 选区的投票率为 34.85%,在选区地图的初步审查和人口普查数据分析中都很突出。这个区不仅位于市中心(这里的投票率通常很高),住在这个区的 78%的人都受过高等教育。我有兴趣在未来的分析中将年龄作为一个额外的因素,看看它是否可能导致 10 区异常低的投票率。

投票站趋势

接下来,我通过将每个车站的道岔可视化为盒须图来进一步分解道岔。

提前投票站没有固定的合格选民数量,我从这个可视化中删除了这些投票站。

我很惊讶地看到有 100%和 0%的投票率的车站。我回顾了这些数据,试图理解这些异常值发生在哪里。

虽然投票率为 0%的车站没有明显的趋势,但我注意到投票率为 100%的车站中有许多是长期护理机构(即养老院、退伍军人护理场所等。).我为这些设施确定了一个关键词列表,并计算了名称中包含这些关键词的异常站点的数量。

我没有只关注 100%道岔的车站,而是检查了所有道岔大于或等于其选区第 91 百分位的车站。在这 164 个投票站中,只有 34 个投票站包含其中一个关键字。这些只是所有异常值站的一小部分。

接下来,我计算了一下,每个投票站平均预计大约有 1106 名投票人。但是这些投票率很高(> 91%)的投票站预计只有大约 94 名选民。由于这些投票站预计只有少数选民,因此在这些地点很容易达到高投票率。这可能是这些投票站投票率通常很高的原因,而不是这些投票站选民人口统计数据的差异。

关于围棋的思考

我立即注意到了 Go 的两件大事:

  1. 它是冗长的。我总是需要明确地说明预期的内容,无论是定义来自 CSV 的数据的预期输入,还是如何将数字字符解析为数值类型
  2. 数据的预建函数较少。熊猫和 numpy 可能用简单的数据操作宠坏了我。尽管 gonum plot 函数简单易用,但在 Go 中创建可视化效果也比 Python 受限得多(例如,在创建条形图时,我需要明确说明每个条形之间的距离,以避免条形重叠)。

这两件事都导致了花在代码上的时间更长。我已经习惯了这样的工作流程,我可以将数据直接加载到内存中(作为一个熊猫数据帧),浏览数据,然后决定下一步。但是,使用 Go 时,工作流程会发生逆转。我需要知道数据包含什么,我计划用数据做什么(这样我就可以定义正确的结构),然后最终加载数据。

总的来说,用 Go 探索数据感觉笨拙而缓慢尽管 gophernotes 使得在 Jupyter 笔记本上运行代码片段成为可能。

但这并不是说在数据科学的世界里没有发展的空间。熊猫之类的图书馆做假设。例如,在 pandas 中,包含 NaN 的整数列的数据类型是 float 而不是 int。虽然这些假设几乎总是正确的,但它们并不总是保证 100%正确。由于 Go 冗长,功能通常由开发人员定义,所以 Go 应用程序通常以更可控的方式执行。这意味着 Go 在生产中更加健壮、更加可靠

总的来说,我不认为我会在另一个主要目的是探索数据的项目中使用 Go。但是对于任何强调一致性的应用程序(如数据管道、仪表板后端等)。)我肯定会考虑用 Go over Python。

后续步骤和结论

我有兴趣在将来使用 Python 以更具地理空间的方式重新访问这个数据集。潜在的未来分析可能包括:

  1. 对所有可用选举的投票数据进行地理编码,并将历史数据映射到当前的邻域边界。然后跟踪每个街区的投票率随时间的变化
  2. 将数据与邻里档案数据集相结合,并在教育和收入之外的更多维度上分析人口统计数据和投票率之间的关系

我希望这个数据故事概述了一些关于多伦多人民如何投票的有趣见解,并为您自己进行更深入(和有趣)的分析指明了方向。如果您有任何问题,请随时通过 opendata@toronto.ca 联系开放数据团队,或者直接通过赵毅联系我。谭@多伦多。

探索单变量数据

原文:https://towardsdatascience.com/exploring-univariate-data-e7e2dc8fde80?source=collection_archive---------6-----------------------

使用超级英雄数据开始使用 Python 中的单变量 EDA

Photo by Limor Zellermayer on Unsplash

Wikipedia 称“单变量分析可能是最简单的统计分析形式。。。关键事实是只涉及一个变量。”

因为单变量分析非常简单,所以它是探索性分析的好起点。

开始时要考虑的一些问题包括:

  • 我有多少变量?
  • 我有丢失的数据吗?
  • 我有哪些类型的变量?

我们将探索来自 Kaggle 的超级英雄数据集来开始回答这些问题。数据包括两个 csv 文件。我们在这里使用的第一个包含了每个超级英雄的特征。第二个列出了每个英雄拥有的超能力。完整的笔记本可以在这里找到。

我有多少变量?

我喜欢从查看数据帧的前几行开始,并打印出图形:

print(info_df.shape)
info_df.head()

马上我们可以看到我们有列“未命名:0 ”,我们最有可能安全地删除它。这样我们总共有 10 个变量。

我们还可以在肤色栏中看到,我们缺少一些值,这就引出了我们的下一个问题。

我有丢失的数据吗?

我们已经看到我们有一些丢失的数据,但是让我们检查每个变量的空值的总和。

*#check for null values*
info_df.isnull().sum()name           0
Gender         0
Eye color      0
Race           0
Hair color     0
Height         0
Publisher     15
Skin color     0
Alignment      0
Weight         2
dtype: int64

虽然这表明存在缺失数据,但这可能有些误导。

上面我们看到皮肤颜色列包含破折号值,Python 在技术上不将其解释为空值。这表明数据的视觉检查是重要的。我们可以清除破折号并用 NaN 替换:

info_df['Skin color'].replace('-', np.nan, inplace=True)

整理好数据后,我们可以进入下一个问题。

我有哪些类型的变量?

变量可以是两种类型之一:分类的或数字的。

分类数据

分类数据将项目分类成组。这种类型的数据可以进一步细分为名义值、序数和二进制值。

  • 序数值有固定的顺序。这里的一个例子可以是从低到高的排序。
  • 名义值没有固定的顺序。例子包括超级英雄的性别和排列。
  • 二进制数据只有两个值。这可以表示为真/假或 1/0。

总结分类变量的一种常用方法是使用频率表。为了形象化,我们将使用条形图。

sns.countplot(x='Publisher', data='info_df')plt.title('Number of Superheros by Publisher')
plt.ylabel('Number of Superheros')
plt.xlabel('Publisher')
plt.xticks(rotation = 90)
plt.show();

Barchart visualizing the number of Super Heroes by publisher.

这里我们可以看到漫威漫画的超级英雄数量最多,其次是 DC 漫画。

数据

数字数据是我们可以对其执行数学运算的值。它们被进一步细分为连续和离散数据类型。

  • 离散变量必须是整数。一个例子是超级英雄的数量。
  • 连续可以是任何值。这里的例子包括身高和体重。

数字数据可以用直方图显示出来。直方图是对连续数据的一个很好的初步分析。这里要考虑的四个主要方面是形状、中心、分布和异常值。

  • 形状是直方图的整体外观。它可以是对称的、偏斜的、均匀的或者具有多个峰值。
  • 中心指的是平均值或中间值。
  • 传播指的是数据到达的范围或多远。
  • 离群值是远离大部分数据的数据点。
sns.distplot(info_2.Weight, kde=False)plt.title('Histogram of Superhero Weight')
plt.show();

Histogram of superhero weight.

从直方图中我们可以看到大多数超级英雄的体重在 50 到 150 磅之间。我们有一个大约 100 磅的峰值和超过 800 磅的异常值。我们可以通过使用 describe 函数打印数字摘要来确认这一点:

info_2.Weight.describe()count    490.000000
mean     112.179592
std      104.422653
min        4.000000
25%       61.000000
50%       81.000000
75%      106.000000
max      900.000000
Name: Weight, dtype: float64

Describe 向我们显示关键统计数据,包括平均值、标准偏差和最大值。除了上面的直方图之外,使用汇总统计可以让我们对数据的样子有一个很好的了解。

单变量结论

数据集包含 10 个变量和一些缺失数据。我们看到 DC 漫画拥有最多的超级英雄,而权重变量有一些异常值。

这绝不是一个完整的探索性分析。我们可以继续探索剩下的变量,然后进行双变量分析。

进一步探索的有趣的事情可能是比较漫威和华盛顿的漫画。我们可以用数据科学来确定超级宇宙吗?

探索印度尼西亚的村庄边界空间数据集

原文:https://towardsdatascience.com/exploring-village-boundaries-spatial-dataset-in-indonesia-part-1-cleaning-and-identifying-data-cf94d0099359?source=collection_archive---------10-----------------------

清理和识别数据

Photo by Capturing the human heart. on Unsplash

测绘 19.05 亿平方公里的广阔覆盖面并不是一项容易的任务,沿途有无数的障碍。地理位置——山区、河流和群岛——是典型的困难,除了与解决边界线问题的当地利益相关者进行复杂的谈判之外,这些困难也使这项工作资源枯竭。因此,现在我们仍然可以在印度尼西亚的一些地方,尤其是东部地区,发现明显的概括——直线和粗略的划分。但是,至少政府正在努力逐年改进,以提供关于行政边界信息的最准确信息,这对于土地登记和管理至关重要。由于地理空间信息局(BIG)试图将包括 1:5.000 比例的全印度尼西亚底图项目在内的交易安全交付给潜在投资者,如世界银行和英国国家测绘局,因此可能还需要更多的改进。因此,我们仍然需要等待像样的行政边界。

此外,在 2019 年任期内,BIG 发布了字母为 SK Kepala BIG 第 27/2019 号 的专题地图,包括四级行政边界的测绘归Direktorat penata an administra si and Pemerintahan Desa管辖,而二级和三级则归Direktorat topo nimi Dan Batas DAE rah管辖。仍然在同一个部,但不同的董事会。这可能会解决以前的问题,当时仍然没有明确的权限来确定谁对数据负责,有时 BPS 和 BIG 在此数据方面会出现重叠。尽管如此,当涉及到这个问题时,应该依靠哪些数据之间仍然存在一些二元论,甚至其他部委也有同样的头痛。为了更好地理解这一问题,我将这一问题的范围界定为更详细的视图,因此我们通过基于来自内政部(Kemendagri)和印度尼西亚统计局(BPS)的数据集进行一些探索性数据分析,对村庄边界数据有了更多的了解。

所使用的村庄边界数据是从 BPSKemendagri 各自的 REST 服务中获得的。在谷歌合作实验室这里,我使用熊猫、海牛和地质熊猫,试图探索其背后的数据。在此第一部分中,进行了清洁和识别程序,以提供一些一般信息,并对这两者进行一些交叉检查。

清理数据

清理数据过程初始化,以清理一些凌乱的数据,其中一些看起来令人恼火。首先,打开驱动器中的数据,将其加载到 Google Colabs 中,地理空间格式是提供商提供的原始格式(geojson 格式)

打开来自 Kemendagri 的空间数据,第一时间很难解释,因为讨厌的列名。为了更好地理解每一个专栏,你可以在这里看到真正的名字。使用 tail() 而不是 head() 是为了区分该数据的前 10 行中的空值。后来,我设法简化了数据并提高了可读性。

df_1 = gpd.read_file("/content/drive/My Drive/village_boundary/desa_kemendagri.json")df_1.tail()

Data from village boundary (Source: http://gis.dukcapil.kemendagri.go.id/)

从 BPS 打开空间数据集,不需要太多清理,只是为了简化必要的数据,这将是很大的考虑。尽管如此,数据仍然需要改进,因为我发现一些数据包括地理特征(湖泊、森林和田野),这实际上不是一个村庄名称类别,所以后来我试图删除其中一些。

df_2 = gpd.read_file("/content/drive/My Drive/village_boundary/desa_bps.json")df_2.head()

Data from village boundary (Source: BPS)

接下来,我想知道这两个中使用了多少数据。也许通过分析数字就能发现两者之间的显著差异是有用的

df_dagri.count() # the result is 83.322df_bps.count() # the result is 79.191

Kemendagri 的数据就是这种情况,通过使用来提高简单性和可读性。iloc 从第 17 栏到第 225 栏,我可以区分出一些在这一部分并不真正需要的特性,然后使用原始中的指南对其进行重命名。我打算保留的列是不同级别管理的代码编号和管理名称。

df_dagri = df_1.drop(df_1.iloc[:, 17:225], axis = 1)df_dagri = df_dagri.drop([‘giskemenda’, ‘giskemen_1’, ‘giskemen_2’, ‘giskemen_3’, ‘giskemen_4’, ‘giskemen_5’, ‘giskemen_6’, ‘giskemen_7’], axis=1)df_dagri.columns = ['ID_PROP', 'ID_KAB_KOT', 'ID_KEC', 'ID_KEL', 'ID_DESA', 'PROVINSI', 'KABKOT', 'KECAMATAN', 'DESA', 'LUAS', 'geometry']df_dagri.tail()

在 BPS 的情况下,就像前面的数据一样,它在某种程度上是可管理的..

df_bps = df_2.drop(df_2.iloc[:, 10:215], axis = 1)df_bps.head()

现在是清理数据的最后一步,Kemendagri 的数据仍然有一个空值,包括湖泊要素(称为 Danau),因此我尝试通过基于列 ID_PROP 来消除这些要素,但我真的不知道我是否通过简单地删除它们做出了正确的选择。

df_dagri_clean = df_dagri.dropna(subset=['ID_PROP'])

继续讨论更困难的问题,虽然 BPS 的数据没有任何空值,但是有一些要素在地理要素之前没有被归类为村庄,例如湖泊、森林和田地。

那么,我们如何区分这些特征呢?我意识到 ID_DESA 列是由唯一代码组成的。例如,ID_DESA = 1107050001 意味着 11 是省代码,07 是县/市代码,050 是地区代码,最后 001 是村代码。因此,我决定将这些值拆分到名为 ID_NEW 的新列中,该列只显示村庄代码。

df_bps['col'] = df_bps['ID_DESA'].astype(str)df_bps['ID_NEW'] = df_bps['col'].str[7:10]df_bps.drop('col', axis=1, inplace=True)df_bps.head()

然后,我尝试显示基于该列的唯一值

df_bps.ID_NEW.unique()
df_bps.count()

结果就在这里..注意数据之间有一些异常值:444、555、888 和 999。

array([‘001’, ‘002’, ‘003’, ‘004’, ‘005’, ‘006’, ‘007’, ‘008’, ‘009’, ‘010’, ‘011’, ‘012’, ‘013’, ‘014’, ‘015’, ‘016’, ‘017’, ‘018’, ‘019’, ‘020’, ‘021’, ‘022’, ‘023’, ‘024’, ‘025’, ‘026’, ‘027’, ‘028’, ‘029’, ‘030’, ‘031’, ‘032’, ‘033’, ‘034’, ‘035’, ‘036’, ‘037’, ‘038’, ‘039’, ‘040’, ‘041’, ‘042’, ‘043’, ‘044’, ‘045’, ‘046’, ‘047’, ‘048’, ‘049’, ‘050’, ‘051’, ‘052’, ‘053’, ‘054’, ‘055’, ‘056’, ‘057’, ‘058’, ‘059’, ‘060’, ‘062’, ‘063’, ‘070’, ‘071’, ‘079’, ‘080’, ‘061’, ‘064’, ‘065’, ‘067’, ‘068’, ‘069’, **‘999’**, ‘066’, ‘072’, ‘073’, **‘888’**, ‘078’, ‘081’, ‘083’, ‘084’, ‘103’, ‘104’, ‘105’, ‘106’, ‘107’, ‘108’, ‘109’, ‘111’,  ‘112’, ‘113’, ‘114’, ‘115’, ‘116’, ‘117’, ‘118’, ‘119’, ‘120’, ‘121’, ‘122’, ‘074’, ‘075’, ‘076’, ‘077’, ‘085’, ‘086’, ‘087’, ‘088’, ‘089’, ‘090’, ‘091’, ‘092’, ‘093’, ‘094’, ‘095’, ‘096’, ‘097’, ‘099’, ‘100’, ‘101’, ‘102’, ‘110’, ‘082’, ‘098’, **‘555’**, ‘**444’**, ‘123’, ‘124’, ‘125’, ‘126’, ‘128’, ‘129’, ‘131’, ‘132’, ‘133’, ‘134’, ‘136’, ‘137’, ‘138’, ‘127’], dtype=object)

因为我真的很好奇这些数据意味着什么,所以我决定更进一步…

outliers = [‘444’, ‘555’, ‘888’, ‘999’]select_outliers = df_bps.loc[df_bps[‘ID_NEW’].isin(outliers)]select_outliers.groupby(['ID_NEW', 'DESA']).count()

如您所见,这里有一些异常值,如无人居住岛屿的“444”和“555”,湖泊的“888”,森林的“999”。

最后,最后一件事是放弃 200 以上的值,因为我发现这样做真的很方便,而不是使用一些条件。

df_bps_clean = df_bps.drop(df_bps[(df_bps['ID_NEW'] >= '200')].index)df_bps_clean.count()

现在,数据比以前干净多了,但在删除一些特征之前,在大小上有一些缺点。为了显示原始版本和清理版本之间的差异,这里快速查看了各自数据的总大小。

dagri_before = df_dagri[‘LUAS’].sum()dagri_after = df_dagri_clean[‘LUAS’].sum()bps_before = df_bps[‘LUAS_KM2’].sum()bps_after = df_bps_clean[‘LUAS_KM2’].sum()

Kemendagri 和 BPS 数据的计算结果分别为 0 和 2,4 ha。零值仅仅是因为我删除了一开始没有任何大小值的特征。由于删除了一些巨大的特征,如大湖和大森林,因此 2,4 ha 的 BPS 差异是可以接受的。此外,改造后两者相差 1.4 公顷

**bps_before** # the result is 1913322.973425157 km2**dagri_before - dagri_after** # the result is 0.0 km2**bps_before — bps_after** # the result is 24101.28374174121 km2**bps_before - dagri_after** # the result is 38489.174897136865 km2**bps_after — dagri_after** # the result is 14387.891155395657 km2

如您所见,BPS 的原始数据与内政部的官方规定(见:Permendagri 编号 137/2017)相符,该规定称印度尼西亚的总面积为 191 万平方公里。BPS 数据于 2016 年发布,而 Kemendagri 数据于 2017 年发布,但后者显示出 3.8 公顷的巨大缺口。怎么会这样

印度尼西亚,我们真的在缩小吗?为什么一年后的数据会比前一年的数据小?尽管 Kemendagri 数据中的特征数量(83.322 行)比 BPS (79.191 行)多。我看不出这两者之间有任何更清晰的解释,甚至没有挑选出任何可信的数据。

无论如何,Kemendagri 真的需要尽快解决这个问题,这是他们的责任之一。

第 2 部分将对之前在此清理的数据进行分析和可视化。没有更多的无聊的编辑,可能是一些令人着迷的图表和有趣的信息将被显示。

用强化学习探索虚拟世界

原文:https://towardsdatascience.com/exploring-virtual-worlds-with-reinforcement-learning-25a245e9afea?source=collection_archive---------28-----------------------

你在办公室里走来走去,你花了很多时间努力工作,喝咖啡,和同事打乒乓球。突然,一个危险的影子出现了。你转过身,看到一个巨大的黄色球向你滚来。他张开大嘴,准备把你整个吃掉,而你开始逃跑。那个黄色的球一直跟着你,好像它是一个真正的人。你躲不了。你逃不掉的。吃豆人会来抓你的。

上面的场景可能听起来很可怕,但它描述了一种游戏,这种游戏可以通过我们正在研究的东西来开发:增强现实和人工智能的结合。‘我们’,就是口袋里的 的人 ,欧洲最好的数码产品工作室。我们公司由多个团队组成,其中一个团队专注于 AR,另一个团队专注于 AI。第一个是和 Unity 合作,而后者对 TensorFlow 了如指掌。最近他们联手创造了一些奇迹。

我们的增强现实团队 masters Unity——他们去年夏天为室内导航的内部演示建立了我们办公室的 3D 模型。两个物体被添加到这个环境中:一个绿色的玩具坦克和一个黄色的积木。我们的目标很简单:奖励碰撞的坦克&在我们的虚拟办公室中捕获随机放置的方块。为了做到这一点,我们——尤其是我们的实习生 Fabrice——利用了来自 Unity 员工的 ml-agents 。这是一个很好的框架,可以帮助你用机器学习的方法立刻创建代理。最先进的强化学习算法,如有耐心的近似策略优化,都是现成的,并且被证明非常有效

起初,代理人有点愚蠢,当它被随意扔在一个房间时,他没能逃出去。但几个小时后,它学会了走出房间,尽管撞到了很多墙。我们更新了我们的奖励功能,以惩罚当它撞上墙壁和继续训练的代理人。代理开始越来越好地捕捉黄色块!我们注意到,当它们在视线范围内但距离很远时,它经常会错过它们。所以我们增加了光线投射的密度,代理人再次得到改善。强化学习 Kaizen!

看看最终的模型,看看它在我们的办公室里是如何学会自我导航的:

An example of the Agent catching cubes in our Digital Office

Unity 作为一个游戏平台,非常擅长将这些环境和模型编译到移动设备上。为了实现创建一个在我们办公室里跑来跑去的 AR Pacman 的目标,我们已经将这个环境编译到 iOS 上,只是为了看到坦克在我们的走廊里捕捉积木。这里有一个这样的印象:

接下来,在 Hololens、Magic Leap 或 HTC Vive 上旋转一下吧!

真正的交易

你可能认为我们的创造只是为了好玩,但我们对它的潜力是非常认真的。想象一下在商店、机场或游乐园有类似的东西指引你?创建一个 AR 寻路应用程序是一回事,但是如果你能够跳过人工编程,让代理根据你的需求训练自己,那就更好了(并且更具可扩展性)。

真正有趣的是,这些代理可以显示人类的行为和模式。虚拟机器人可能生活在模型中,但它知道真实世界。想象一下,如果你正在开发一个公共建筑——无论是医院、火车站还是机场——你组织了一场建筑设计竞赛。我们的代理可以做的事情之一是通过进行数百万次模拟“行走”来测试建筑的可达性和安全性。人们走哪条路?他们被卡在哪里?危险情况下的人群控制呢?

目前最大的警告是,代理人生活在三维世界中,并没有感觉到现实世界。我们的桌子还没有在 Unity 环境中建模,所以如果你用 HoloLens 或其他 AR 设备观看,坦克只会穿过桌子。我们正在思考和探索如何在 Unity 模型中反推环境的 3D 网格信息。我们还通过在 3D 环境中放置随机障碍物来使坦克更加坚固,这样它就可以学会有效地绕过它们,预测现实世界中的实时变化。虽然观察代理和手动调整奖励函数很有趣,但它威胁到虚拟代理学习自己导航新环境的可扩展性。我们正在探索多阶段学习的新途径;首先掌握在我们办公室导航的基本知识,然后掌握如何快速和避开障碍物。

障碍不一定会阻止你。如果你碰壁了,不要转身放弃。想办法爬上去,穿过去,或者绕过它。来吧,吃豆人,你有一整晚的时间和一千万次的尝试!

特别感谢 Fabrice Cops 和 Dieter Vanhooren,他们为这个项目提供了支持。

探索,可视化和建模明尼苏达维京人队的进攻

原文:https://towardsdatascience.com/exploring-visualizing-and-modeling-the-minnesota-vikings-offense-f8ebdcabf473?source=collection_archive---------17-----------------------

Viktor the Viking gazes into the abyss.

经过简短的搜索,我从 NFL 专家那里获得了整个 2018 赛季的详细数据。因为我本质上是明尼苏达的粉丝,所以我决定尝试一些机器学习技术,看看能否找到一些量化的方法,让维京人在过去的一年中有所欠缺。

对于一些最初的 EDA ,我决定看看通过传球积累的码数,按传球位置和接球手细分。根据传球方向,我汇总了每个维京人接球手在赛季中至少 10 次接球的所有接球码。因为平均码数/接发球受几次长时间接发球的影响很大(并且因为不完整传球导致平均码数/接发球经常为 0),所以我使用总码数(总和)来汇总数据。该数据显示在左侧,并按总接收码/球员(从多到少)排序。我认为值得注意的是:蒂伦和迪格斯倾向于在球场的对立双方分裂;蒂伦在球场右侧接到更多的远传码,而迪格斯在左侧接到更多的远传码。我认为,这也凸显了可靠的第三 WR 的相对稀缺;凯尔·鲁道夫无疑是球队最好的第三选择,但是奥尔德里奇·罗宾逊(总共 17 次接球)和拉昆·特德韦尔(总共 35 次接球)如果他们不得不在蒂伦和迪格斯坐在一场比赛中排队的话,那他们就太掉队了。

Dot plots of penalties/team, grouped by defensive penalties, offensive penalties, and all penalties. MN Vikings are marked in purple.

鉴于他们在本赛季最后一场比赛中由于点球至少部分被取消,我接下来看了看维京人在点球上的总体表现。明尼苏达队是第二个受到处罚最少的球队。整个 2018 赛季,他们只接受了 88 次处罚(47 次进攻,41 次防守),而平均每支球队接受了 106 次处罚(56 次进攻,50 次防守)。这些处罚只有 729 码有效,在联盟中排名第三(平均每个球队有 881 码的处罚)。虽然这个基本总结中肯定有额外的混杂因素(不是所有的处罚都是平等的,无论是在放弃的绝对码数方面还是在它们对比赛结果的影响程度方面),但数据表明明尼苏达州训练有素,纪律严明。所以我想知道明尼苏达是否在他们的比赛叫牌中做了一些奇怪的事情,也许是向对方的防守倾斜,给他们的对手额外的信息来准备。

首先,我简单地用逻辑回归来尝试预测一部戏是否过关。作为预测,我使用了当前的比赛条件:比赛剩余的时间(通过将四分之一、一分钟和一秒钟组合成一个单一的度量标准来计算),当前的比分差距,要走的距离和码数,距离端区的距离,以及对方球队的身份。在 1,000 次训练拆分中,逻辑回归预测维持数据的通过状态的准确率约为 58%(-3%)。虽然这明显比运气好(如果你只是在每场比赛中预测“传球”,你会有 53.7%的准确率;针对该值对我们观察到的精度进行单样本 t 检验,得到 t=15.18,p<2–16),这在解释数据方面并不特别令人印象深刻。从系数来看,时间对一部戏是否合格没有影响;虽然随着时间的推移,球队不应该总是传更多的球,但在一些常见的情况下,他们应该这样做。然而,我们的逻辑模型没有考虑预测因素的相互作用,例如当两个或更多的财产下降时是否有时钟的影响,或者它是否是第三个下降和英寸或第三个下降和长,这有助于说明这种相对简单的方法的一些缺点。

鉴于这些结果,我开始想知道能够预测一场比赛是否会通过(即使没有场上所有的额外信息)是否真的是一件好事,以及更多“可预测”的球队是否会赢得更少的比赛。我分别在每个团队中重新运行了相同的逻辑建模代码;根据逻辑回归模型方法,明尼苏达是第五个最“可预测”的球队,排在杰克逊维尔、西雅图、匹兹堡和印第安纳波利斯之后。在所有 32 个团队中,胜率和逻辑可预测性之间没有显著相关性(r=0.19,p=0.30,见下图)。然而,逻辑准确性和获胜之间的非显著正相关可能可以用这样一个事实来解释,即获胜最多的团队往往会通过,直到他们以多个分数获胜,然后在场上尽可能快地结束比赛。此外,一些最不可预测的球队(例如旧金山、奥克兰和坦帕湾)在赛季中途就基本上被淘汰出季后赛了。当没有动力去最大化你的胜利数字时,我想球队会更愿意多样化他们的打法,让他们的球员尽可能多的代表不同种类,并在下赛季给他们自己更多的经验和更好的机会。

Logistic regression model accuracy for each NFL team (left), and win percentage by logistic model accuracy (right).

Confusion matrix for the random forest model of Minnesota’s offensive play calls.

为了改进基本的逻辑回归结果,并考虑到预测因素之间可能的相互作用,我接下来使用了随机森林模型来预测游戏条件下的游戏类型。我没有简单地估计“is_pass”结果,而是允许结果具有数据中实际存在的多个级别。总共使用了 500 棵树,每次分裂选择 2 个变量。总的来说,预测的随机森林模型比通过概率的逻辑回归模型发挥得更好,在所有条件下具有大约 63%的准确性。看一下混淆矩阵(左),随机森林模型在预测大多数特定游戏类型方面做得很好。它预测射门的准确率为 81%,传球准确率为 69%,踢门准确率为 91%。自然地,失球和麻袋从来没有被模型预测到,考虑到它们的稀有性和内在的不可预测性,这是有意义的(大多数进攻协调者可能不会故意制造失球)。

随机森林模型最不可靠的地方是预测灯芯草;只有 52%的预测会是快攻的比赛最终实际上停留在地面上,而 43%最终是传球。这可能是由于建立模型的数据的缺点(例如,使用阵型和人员信息可能会有所帮助),但也可能是由于维京人倾向于(至少在赛季早期)成为传球优先的球队。

我选择不包括在初始随机森林模型中的最大数据源之一是队形类型(对于维京人,这些包括散弹枪、中锋下方、无拥挤、无拥挤散弹枪、平底船和球门队形)。最初,我更感兴趣的是尝试从教练的角度,看看我是否能预测他们在给定的比赛情况下会想出什么打法。然而,另一种方法是扮演对方防守的角色,并尝试根据比赛情况阵型(这是混战线的公开信息)预测比赛叫牌。

在随机森林模型中加入该信息得到了更高的精确度,误差率仅为 29%。更重要的是,以前预测维京人何时会抢球的难度大大降低了,因此当模型预测抢球时,63%的时间是正确的(相比之下,在没有阵型数据的情况下,52%的准确率)。在其他团队中尝试这种随机的 forest+模型在准确性方面产生了类似的改进。所有 32 个团队的平均错误率为 33%,范围从 24-40%不等。明尼苏达队再次成为最“可预测”的球队之一(总排名第五),但考虑到第一和第三最可预测的球队(分别是新英格兰队和洛杉矶公羊队)正在参加超级碗比赛,这再次表明该模型预测球队比赛类型的能力并不一定能更容易地阻止他们取得成功。

到综上所述,使用随机森林方法来模拟逐场比赛数据,在确定当前游戏条件下将进行何种类型的比赛方面相当准确,并且在通过/不通过的简单逻辑回归上有明显的改进。鉴于维京人在点球和失球等“错误”指标上的表现远高于平均水平,我也不认为有太多理由认为他们只是运气不佳。

一些分析家认为柯克考辛斯是罪魁祸首,但我肯定不会说他是主要问题。在他的 最近的几个赛季中,考辛斯有五个不同的接球手,整个赛季有 500+的接球码,而今年他只有蒂伦、迪格斯和鲁道夫可以依靠。部分原因是库克错过了赛季的一部分,但即使他整年都健康,可靠的接收选择仍然比考辛斯习惯的要少。进攻线在快攻和传球阻挡上的困难也是需要改进的地方,可能会出现在选秀中。不过,总的来说,这个建模练习表明,在给定的比赛条件下,比赛的类型与联盟中一些最成功的球队是一致的,这让我对明年的改进感到乐观,特别是如果球队可以增加另一个一致的接收选项。

探索自然语言生成的西部——从 n-gram 和 RNNs 到 Seq2Seq

原文:https://towardsdatascience.com/exploring-wild-west-of-natural-language-generation-from-n-gram-and-rnns-to-seq2seq-2e816edd89c6?source=collection_archive---------12-----------------------

NLP Wild West

斯坦福大学 NLP 课程中讲授的自然语言模型介绍

最初,文本生成是基于模板或基于规则的系统,对于特定的狭义应用程序来说,它们具有相当好的可解释性和良好的行为。但是扩展这样的系统需要大量不可行的手工工作。简单地说,语言没有清晰明确的规则,这就是为什么它经常被许多人比作西部荒野。由于这个原因,这个领域转移到了统计语言模型

语言建模(LM)是预测下一个单词是什么的任务,或者更一般地说,是一个为一段文本序列分配概率的系统。

N-gram最简单的语言模型,其性能受到缺乏复杂性的限制。像这种简单的模型无法实现流畅、足够的语言变化和正确的长文本写作风格。由于这些原因,神经网络 ( NN ) 被探索为新的黄金标准,尽管它们很复杂,并且 递归神经网络(RNN) 成为用于任何种类的序列的基本架构。如今,RNN 被认为是文本的“香草”架构,但 rnn 也有自己的问题:它不能长时间记住过去的内容,并且它很难创建长的相关文本序列。由于这些原因,其他架构如【LSTM】门控循环单元(GRU) 被开发出来,并成为许多 NLG 任务的最先进解决方案。在本文中,我们将探索这些基本思想和模型演变

Different flavours of RNN: RNN, GRU and LSTM (source of images)

毫无疑问,【NLP】【NLG】已经经历了重大的进步,尤其是最近五年,这就是为什么我们每天多次使用语言模型并从中受益。每当我们在搜索框中键入文字或者在手机上书写文字时,幕后的一个模型就在预测下一个即将出现的字符或单词。 GmailLinkedIn 根据我们之前的对话和虚拟助手(例如 SiriAlexa 生成类似人类的回复。聊天机器人正在成为客户服务的重要组成部分,谷歌翻译为大量语言提供了更好的翻译。生成流畅连贯的冗长文本,同时保持对输出语义的控制,这是一个挑战。这可以通过类似于 序列到序列(Seq2Seq) 的架构来解决,该架构用于解决条件文本生成问题,但我们将在本文的后面对此进行更多讨论。

Examples of applications with NLG models

N-gram

最直接的语言模型是一个ngram 模型。文本只不过是一系列字符(或单词)。词汇 V = { W ₁,…, W |ᵥ|}下一个单词 x ⁽ᵗ⁺ ⁾的概率分布为:

因此,单词序列 x ⁽ ⁾, x ⁽ ⁾,…, x ⁽ᵀ⁾的概率为:

N-gram 假设 下一个字 x ⁽ᵗ⁺ ⁾只依赖前面的 n-1 个字。

用数学术语来说,这意味着:

N-gram 又称为 非光滑极大似然估计 。对于最大 似然估计,我们统计每个单词在 n-1 个单词(历史)之后出现的次数,并除以这 n-1 个单词在训练数据中出现的次数。未平滑部分意味着,如果我们在训练数据中没有看到 n-1 个单词历史之后的给定单词,我们将为其分配零概率。

4 克 LM 的示例:

H̵e̵l̵l̵o̵,̵ ̵I̵想 ____

p(字| 想)= count(想字)/ count(想)

P(观| 想)=0.03, P(书| 想)=0.04

完成这些计算后,文本生成就很容易了。生成下一个消息 的 步骤为:

  1. 看最后 n-1 克(历史)。
  2. 从 n-gram 模型中获得下一个单词的分布。
  3. 根据相应的分布抽取一克样本。(注意,有不同的方法来选择下一个 gram,并在本文的结尾进行了解释。)

为了生成一系列的 k 线图,我们简单地循环上面的过程,在每一轮用生成的 k 线图更新历史。

Sparsity problem

这种简单化的方法有一个 稀疏性问题 :

  1. 正如我们已经提到的,如果一个 gram 从未出现在历史数据中,n-gram 分配 0 概率 ( 0 分子)。一般来说,我们应该 平滑 的概率分布,因为每件事都应该至少有一个小概率分配给它。
  2. 如果历史数据中从未出现过 n-1 个 gram 序列,我们就无法计算出下一个 gram 的 概率(0 分母)。在这种情况下,我们应该只考虑一个更短的 n-gram (即使用 n-1 gram 模型代替)。这种技术叫做 回退

由于模型的简化假设,如果我们保持上下文和长期依赖性,我们就不会像我们一样擅长预测即将到来的单词。于是我们以等不连贯的文字收场

"在鞋楦和制鞋业生产的同时,股市也随之上涨"

然后,有人可能认为更大的上下文可能给出更好的预测,但是通过增加模型中的克数(n)稀疏性成为更大的问题,并且整个模型实际上变得不正常。此外,更大的 n 会导致型号的更大,根据经验, n 不应超过 5

NN

Trade-offs between these two types of systems. (source)

需要一种具有上下文感知的更复杂的模型。有了 NN,就没有稀疏性问题,因为它可以给我们以前从未见过的组合的概率分布,并且不需要存储所有观察到的 n-gram。一个 NN 似乎是现在每个硬数据问题的解决方案,这里的情况是这样吗?

Fixed window neural language model

例如,固定窗口神经 LM 查看固定长度窗口中的单词,其输入层是连接的单词嵌入向量。但是我们仍然在看一个有限大小的固定窗口,并且扩大窗口大小导致模型参数 不可维护的 增加。此外,在处理输入的方式上也没有 对称性——看上面的图,我们可以看到单词被乘以不同的权重。因此,我们有效地多次学习相似的函数。

**在我们如何处理引入的单词嵌入方面应该有很多共性

顾名思义,递归神经网络(RNN)通过对所有单词输入 循环使用相同的网络和权重矩阵来解决这个问题 。因此,RNN 可以被认为是同一个网络的多个副本,每个副本都向后继者传递消息。这使得 RNN 成为比固定窗口 NNs 更紧凑的模型。下一节将使这一陈述更加清晰。

Unrolling of an RNN (modified figure from source)

rnn、gru 和 lstm

在 RNN 中, 输入序列 可以是任意长度的**,因为我们对每个时间步(字)应用相同的权重矩阵。这意味着型号的尺寸不会因输入变长而增加,并且在输入处理方式上存在 对称性 。此外,RNN 具有一系列隐藏状态(而不是像我们在固定窗口神经 LM 中那样只有一个隐藏状态)。某个步骤的输出是基于先前的隐藏状态和该步骤的输入计算的。隐藏状态就像一个单一的状态,随着时间的推移而变异,形成网络的记忆。**

Vanilla RNN architecture where Xt is the input (eg. encoding of word at step t) and ht is output value (eg. encoding of the next word) (modified figure from source)

隐藏状态是前一个隐藏状态和当前模型输入的线性组合,然后通过非线性处理(如 sigmoid,tanh)。我们正在尝试学习一个 通用函数 (上图中的 A)关于我们应该如何处理一个单词给定的前一个单词的上下文,而学习语言和上下文的一般表示。

在字符级而不是单词级预测即将到来的上下文是一个分类问题,因为字符比词汇表中的单词范围少,所以分类数量明显较少。在训练了一个预测下一个字符的 RNN 之后,我们可以按照下面的 步骤生成新的文本 :

  1. 首先选择一个字符串,初始化 RNN 状态,并设置要生成的字符数。
  2. 使用起始字符串和 RNN 状态,将预测作为下一个字符的分布。使用分类分布计算预测字符的索引。
  3. 使用这个预测的字符作为模型的下一个输入。
  4. 由模型返回的 RNN 状态被反馈到模型中,以便它现在具有更多的上下文,而不仅仅是输入(预测的先前字符)。(见上面 RNN 展开的图)

对文本序列中的一个字符的计算(理论上)使用来自许多步骤之前的信息,但是循环字符计算仍然很慢,并且在实践中很难访问来自许多步骤之前的信息。

预测“太阳在天空照耀”这句话的最后一个单词是可行的。但是“11 月的天气主要是”这句话的最后一个字就比较难预测了当 100 个单词之前有一句话“我计划去塞浦路斯旅行。”。

RNNs 挣扎记住长范围依赖关系主要是由于所谓的 消失梯度问题上下文信息的线性衰减*** 。梯度的大小受激活函数的权重和导数的影响(由于链式法则),它们在网络中反复出现。如果这些因子中的任何一个小于 1(当我们将小值相乘时),梯度可能会及时消失,如果这些因子大于 1,梯度可能会爆炸。在这两种情况下,我们都会丢失信息,尤其是当文本较长时。训练期间非常小的梯度转化为重量的微小变化,因此,没有显著的学习。*****

LSTM 和后来的 GRU 的设计是为了缓解控制模型更新记忆的消失梯度问题。

****lstmGRUs 中的重复模块(图中的 A)具有不同的结构,这使得它们 能够学习长期依赖 。它们的结构使用由非线性(例如 sigmoid)神经网络层和乘法或加法运算组成的门。

LSTM architecture and equations of its repeating module (modified figure from source)

有不同类型的 可用于 更新状态 。例如,遗忘门决定我们要丢弃什么信息,而输入门决定我们要存储什么新信息。LSTM 的一个关键特征是单元状态(上图中 LSTM 顶部的水平线),它通过网络,只有一些微小的线性交互,允许信息沿着网络流动。 GRU 是 LSTM 的一个更快(但功能更弱)的变体,它合并了单元格状态和隐藏状态。

GRU architecture and equations of its repeating module (modified figure from source)

条件语言模型— Seq2Seq

到目前为止,我们已经看到了可以生成文本的不同模型(n-gram、RNN、LSTM、GRU ),但是我们还没有探索如何基于除起始文本之外的条件来生成文本。一些条件文本生成应用如下:

作者姓名 > 作者风格的文本

****话题>话题关于那个话题的文章

电子邮件 > 电子邮件主题行(又名总结)

问题 > 自由形式问题回答(又名聊天机器人)

****条>条条的总结(又名总结)

图像 > 描述图像的文本(又名图像字幕)

自然语言代码描述 > 代码(又名代码生成)

****句>句句翻译成不同的语言(又名机器翻译)

在条件语言模型中,我们希望在给定一些条件上下文( x )的情况下,将概率分配给单词序列( y ):

这种范式被称为 Seq2Seq 建模,它是 2014 年神经机器翻译的一大突破。从那时起,它一直是一种领先的标准方法,并在 2016 年使用了 谷歌翻译 。为了训练这样一个模型,我们需要大量的并行数据

The Rosetta Stone: First parallel language dataset

Seq2Seq 的架构是由两个 rnn 组成的单一神经网络模型:

  • 一个编码器 :创建一个固定长度的编码(一个实数向量)封装关于输入的信息。在机器翻译中,编码器从源句子中提取所有相关信息以产生编码。**
  • 一个解码器 (本质上是一个条件 LM):一个语言模型,用编码器创建的编码生成目标序列条件。在机器翻译中,解码器生成源句子的翻译句子。**

Seq2Seq example for generating a message to property agents based on some search criteria

解码器是用一种叫老师强制** 的方法训练的。这意味着“老师”迫使解码器的输入成为黄金目标输入,而不是它的预测。目标序列是偏移了一个单词的输入序列,以便网络学习预测下一个单词。Seq2Seq 中的反向传播端到端操作(一端是损耗函数,另一端是编码器 RNN 的起点),我们 相对于学习整个系统****

End-to-end training using one loss function J

由编码器创建的嵌入成为解码器 RNN 的初始状态。我们强制在这个向量中捕获关于源句子的所有信息,因为这是提供给解码器的唯一信息。这样我们就陷入了一种的信息瓶颈** 的问题中。如果一些信息不在这个向量中,那么解码器就无法正确翻译这个句子。 注意 提供了这个问题的解决方案,同时也帮助解决了其他问题,包括 消失渐变问题 。**

**注意是通用的深度学习技术。给定一组向量值和一个向量查询,它计算依赖于查询的值的加权和

Seq2Seq with Attention

在 Seq2Seq 情况下,每个解码器隐藏状态 s𝘵(查询)关注所有编码器隐藏状态 h₁,…,h_N(值)。因此,在解码器的每一步,我们都有一个直接连接到编码器,但是我们关注源句子的不同部分

上面的网络图在数学上所做的是:

Attention (a_t) translated in mathematical equations

  1. 计算解码器隐藏状态(步骤 t 中的 s)与每个编码器隐藏状态(h₁,…,h_N)的点积,以便获得注意力分数(eᵗ)
  2. 使用软蜡获得这一步的注意力分布(αᵗ)
  3. 使用该分布来获得注意力输出(步骤 t 中的 a ),作为编码器隐藏状态的加权和。
  4. 将注意力输出与解码器隐藏状态([a;s])并按照非注意 Seq2Seq 模型进行。

最后,在训练 Seq2Seq 模型之后,可以遵循不同的策略来通过解码器预测下一个字:

  • 贪婪 :我们总是挑选概率最高的词(又名 argmax)。我们现在做了最好的选择,没有回头路了。然而,这不一定会给我们整个句子的 argmax。
  • 光束搜索 :光束搜索跟踪每一步的 k 可能变量,避免被局部最大值引入歧途。当 k 为 ( k=1 表示贪婪)时,我们可能会得到不合语法的不自然的无意义的不正确的句子。当 k 比时,这些问题会减少,但会更加昂贵并给出更多通用回复,汇聚成安全“正确”响应,然而不太相关
  • 采样 :我们从一个截断的条件词概率分布中采样,即从前 k 个最可能的词中采样。用这种方法,通常句子由于随机性而没有太大的意义。注意 k=1贪婪解码相同, k= 大小的词汇与纯采样相同。

遗言

我们讨论了生成文本的不同方法,从简单的方法到不同的 RNN 架构,包括GRU 。我们还研究了 S eq2Seq 架构,其中用*可以处理 条件文本生成 机器翻译等问题。***

这些模型在谷歌和我的团队之外有Zoopla的数据科学团队用这些知识挑战财产门户体验。希望在读完这篇文章后,你对文本生成有了更好的直觉,并且你现在也能够为你的行业找到有用的应用。如果你想了解更多,以下是我发现的一些有用且有见地的资源:

特别感谢我在 Zoopla 的同事们,他们重视知识分享和新想法的实验,并感谢 Jan Teichmann 对本文的反馈和支持。

探索 XGBoost

原文:https://towardsdatascience.com/exploring-xgboost-4baf9ace0cf6?source=collection_archive---------5-----------------------

集成学习和 XGBoost 在分类和特征选择中的应用

我最近在弗朗索瓦·乔莱的《用 Python 进行深度学习》一书中偶然发现了一种新的【对我来说】方法,梯度推进机器(具体来说就是 XGBoost)。Chollet 提到,XGBoost 是一个成功的应用机器学习者今天应该熟悉的一种肤浅的学习技术,所以我相信了他的话,并深入了解了更多。

我主要想写这篇文章,因为我认为其他具有一些机器学习知识的人也可能像我一样错过了这个主题。我绝不是这方面的专家,老实说,我在理解一些机制方面有困难,但是我希望这篇文章是你探索这方面的一个很好的入门(底部也有很好的资源列表)!

集成学习

那么 XGBoost 是什么,它在 ML 的世界中处于什么位置呢?梯度推进机器属于称为集成学习的 ML 类别,集成学习是 ML 方法的一个分支,它可以同时用许多模型进行训练和预测,以产生单一的优秀输出。把它想象成规划出几条不同的路线去一个你从未去过的地方;当你使用所有的路线时,你开始了解哪些交通灯需要很长时间,以及一天中的时间如何影响一条路线,使你能够精心设计一条完美的路线。您试验并组合了一些不同的模型,以得出最佳结论。集成学习也差不多!

集成学习分为三个主要子集:

  • Bagging : Bootstrap 聚合或 Bagging 有两个不同的特征,这两个特征定义了它的训练和预测。对于训练,它利用引导程序将训练数据分成不同的随机子样本,模型的不同迭代使用这些子样本进行训练。对于预测,bagging 分类器将使用每个模型中投票最多的预测来产生其输出,bagging 回归将取所有模型的平均值来产生输出。Bagging 通常应用于高方差模型,如决策树,随机森林算法是 bagging 的一个非常接近的变体。
  • 堆叠:堆叠模型是一个“元模型”,它利用来自许多不同模型的输出作为输入特性。例如,这允许您使用所有训练数据训练 K-NN、线性回归和决策树,然后获取这些输出并将它们与逻辑回归合并。这个想法是,这可以减少过度拟合,提高精度。
  • 助推:终于助推了!boosting 的核心定义是一种将弱学习者转换为强学习者的方法,通常应用于树。更明确地说,boosting 算法依次增加模型的迭代,同时调整弱学习者的权重。这减少了来自模型的偏差,并且通常提高了准确性。流行的提升算法有 AdaBoost、梯度树提升和 XGBoost,我们将在这里重点介绍。

XGBoost

eXtreme Gradient Boosting 或 XGBoost 是一个针对现代数据科学问题和工具优化的梯度提升算法库。它利用了 boosting 中提到的技术,并封装在一个易于使用的库中。XGBoost 的一些主要优点是它的高度可伸缩性/可并行性、执行速度快,并且通常优于其他算法。

在这一节中,我们将通过预测泰坦尼克号上的乘客是否幸存来探索 XGBoost。用于此分析的完整 jupyter 笔记本可在这里找到。

我首先将 Titanic 数据加载到 Pandas 数据框中,并探索可用的字段。注意:为了简洁起见,在加载之前,我手动转换了 csv 中的‘上船’和‘性别’特征

大多数元素似乎是连续的,那些包含文本的元素似乎与预测幸存者无关,所以我创建了一个新的数据框(“train_df”),只包含我想要训练的特征。还要注意,XGBoost 将处理 nan,但(至少对我来说)不处理字符串。

就像其他模型一样,将数据分成训练和测试数据很重要,我在 SKLearn 的train_test_split中就是这么做的。请注意,我决定只采用 10%的测试数据。我这样做主要是因为 titanic 数据集已经很小了,而我的训练数据集已经是全部可用数据集的一个子集。这可能会导致过度拟合,可能不是最佳实践。

准备好数据后,我的目标是将乘客分类为生还者,我从 XGBoost 导入了 XGBClassifier。然后我们为XGBClassifier()创建一个对象,并传递给它一些参数(没有必要,但我最终想尝试手动调整一下模型)。最后我们把模型fit()交给我们的训练特征和标签,我们准备进行预测!如您所见,使用 XGBoost 库与使用 SKLearn 非常相似。

使用我的模型进行预测,并使用准确性作为我的衡量标准,我可以看到我达到了超过 81%的准确性。这是经过一点手动调整后的结果,虽然我希望得到更好的结果,但这仍然比我过去在相同数据上使用决策树所获得的结果要好。

XGBoost 的一个超级酷的模块是plot_importance,它为您提供每个特性的 f 值,显示该特性对模型的重要性。这对于选择特性很有帮助,不仅对于 XGB,对于您可能在数据上运行的任何其他类似模型也是如此。

更进一步,我发现了一个精彩的代码样本和一篇关于自动评估要使用的特性数量的文章,所以我必须尝试一下。

本质上,这段代码通过按照重要性反复删除特征来训练和测试模型,并记录模型的准确性。这使您可以轻松地删除功能,而不是简单地使用试错法。虽然这里没有示出,但是这种方法也可以应用于模型的其他参数(learning_ratemax_depth等),以自动尝试不同的调整变量。我不会深入讨论模型调优的细节,但是大量的调优参数是 XGBoost 如此受欢迎的原因之一。

我希望这是对 XGBoost 是什么以及如何使用它的有用介绍。感谢阅读。现在,去做点什么吧!

有用的资源/参考资料

GXBoost 概述 ( 必读!)

特征重要性和选择

调优 XGBoost

集成学习

注:此内容原载于AI Time Journal

仅用一行 Python 代码探索您的数据

原文:https://towardsdatascience.com/exploring-your-data-with-just-1-line-of-python-4b35ce21a82d?source=collection_archive---------2-----------------------

A pretty picture to catch your eye.

在不到 30 秒的时间内完成所有标准数据分析。熊猫侧写的奇迹。

谢谢你。

这是一篇非常短的文章,但在我们开始之前,我只想对所有阅读并分享我上一篇文章的人表示感谢: Python 窍门 101,每个新程序员都应该知道的事情 。招待会完全是疯狂的。掌声和观点的数量完全使我的其他文章相形见绌。所以谢谢,让我们继续吧!

香草熊猫之路(无聊之路)

任何使用 Python 处理数据的人都会熟悉 pandas 包。如果你不是,pandas 是大多数行列格式数据的首选包。如果您还没有 pandas,请确保在您的首选终端中使用 pip install 安装它:

pip install pandas

现在,让我们看看默认的 pandas 实现能为我们做些什么:

Pretty decent, but also bland.. And where did the “method” column go?

对于那些不知道上面发生了什么的人:

任何熊猫' DataFrame '都有一个. describe() 方法返回上面的摘要。但是,请注意,在该方法的输出中,分类变量丢失了。在上面的例子中,输出中完全省略了"方法"列!

让我们看看我们是否能做得更好。
(提示:… 我们可以!)

熊猫侧写(奇特的方式)

This is just the beginning of the report.

如果我告诉您,我只用 3 行 Python 代码(如果不计算我们的导入,实际上只有 1 行代码)就可以生成以下统计数据,您会怎么想?) :

  • 要素:类型、唯一值、缺失值
  • 分位数统计如最小值、Q1、中值、Q3、最大值、范围、四分位间距
  • 描述性统计如均值、众数、标准差、总和、中位数绝对偏差、变异系数、峰度、偏度
  • 最频繁值
  • 直方图
  • 相关性突出显示高度相关的变量、Spearman、Pearson 和 Kendall 矩阵
  • 缺失值缺失值的矩阵、计数、热图和树状图

(特性列表直接来自 【熊猫仿形】GitHub )

我们可以。通过使用熊猫概况包!要安装 Pandas Profiling 软件包,只需在您的终端中使用 pip install:

pip install pandas_profiling

经验丰富的数据分析师乍一看可能会嘲笑这种方式过于浮华,但它绝对有助于快速获得数据的第一手印象:

See, 1 line, just as I promised! #noclickbait

首先你会看到的是概述(见上图),它给出了你的数据和变量的一些非常高级的统计数据,以及警告,比如变量之间的高相关性、高偏斜度等等。

但这还不是全部。向下滚动我们会发现该报告有多个部分。简单地用图片展示这个一行程序的输出并不公平,所以我制作了一个 GIF:

我强烈建议您亲自探索这个包的特性——毕竟,它只是一行代码,您可能会发现它对您未来的数据分析很有用。

import pandas as pd
import pandas_profiling
pd.read_csv('[https://raw.githubusercontent.com/mwaskom/seaborn-data/master/planets.csv').profile_report()](https://raw.githubusercontent.com/mwaskom/seaborn-data/master/planets.csv').profile_report())

更像这样

如果您喜欢这些易于应用的东西来改进您的 Python 工作流,请看看我的新文章:

[## 快速改进 Python 数据分析的 7 件事

让您的数据分析更上一层楼!

towardsdatascience.com](/7-things-to-quickly-improve-your-data-analysis-in-python-3d434243da7)

结束语

这只是一个非常快速和简短的问题。我刚刚发现熊猫在给自己做简介,所以想和大家分享一下!

希望你觉得有用!

如果你想多看多学,一定要 关注我上媒 🔍碎碎念 🐦

** [## 彼得·尼斯特鲁普-中等

阅读彼得·尼斯特拉普在媒介上的作品。数据科学、统计和人工智能...推特:@PeterNistrup,LinkedIn…

medium.com](https://medium.com/@peter.nistrup)**

指数级的技术将会使几乎所有的工作变得过时,所以你能做些什么呢?

原文:https://towardsdatascience.com/exponential-technologies-will-make-virtually-all-jobs-obsolete-so-what-can-you-do-about-it-4663b8d13b83?source=collection_archive---------20-----------------------

人们普遍认为,人工智能(AI)、增强现实和虚拟现实(AR/VR)、比特币、生物技术、物联网(IoT)、3D 打印和机器人等指数级技术将取代许多工作岗位。根据不同的方法,对未来几十年人工智能可以取代多少工作的估计从 10%到 55%不等。

Portrait of Edmond Belamy created by GAN (Generative Adversarial Network), 2018

然而,这些高数字过于保守。我认为指数技术将会消除几乎所有的工作。我意识到这是一个可怕的命题,但不幸的是,我有充分的理由这样说,我将在下面详述。

我们必须为生活所有领域的海啸般的变化做好准备,这将使我们不仅质疑我们如何谋生,更重要的是质疑我们作为人类在地球上的位置。

人工智能已经可以比人类做得更好,这比任何人预期的都要快得多(除了少数未来学家)。软件现在可以比普通人更好地识别和命名物体或生物的图像,错误率低于 5%。

我们可以很好地区分猫和狗,但在面对更复杂的图像识别问题时,人类比人工智能更容易出错。我们很多人都不能自信地区分哈巴狗、法国牛头犬和英国牛头犬。训练有素的人工智能完成这样的任务没有问题。虽然识别狗的品种似乎不是一项非常重要的任务,但想想医学领域的图像处理,识别肿瘤可以挽救你的生命。在如此关键的领域,每超过人类专业知识一个百分点,就意味着真正的生命得到了拯救,所以我们不能在道德上阻止进步。

Pug, French Bulldog, English Bulldog.

人工智能已经可以与人类专家匹敌的领域数不胜数:风险分析、交易、军事应用、驾驶、法律实践、新闻等等。任何需要持续做出正确决定的职业都受到了人工智能的威胁/增强。随着我们在各种流程中部署更多的自动化,我们也将产生更多的数据,这反过来将使人工智能变得更好。有一天,即使是世界顶级专家也无法超越训练有素的人工智能系统,因为人工智能吸取了数百万人的专业知识。它变成了人类的蜂巢大脑。

其他指数级技术,如机器人与人工智能的结合,使机器人不仅超级人类强壮,而且超级人类聪明。在中国,机器人警察已经被部署到经常光顾的地区,以寻找罪犯和侦查犯罪活动。这些机器人警察可以搜索拥有数百万张面孔的数据库,并利用数万小时观察视频产生的行为模式。经过一些“练习”,这种机器人将接管许多意想不到的职业,如警务、消防、救援工作、建筑等。

我们不必探索所有指数级的技术,就能意识到对可替代工作的影响将是巨大的。然而,许多人认为,仍然会有一些工作不能或不会被人工智能、机器人或物联网取代,因为它们需要理解复杂的情况,包括人类自身或人类创造的环境。

虽然我同意上述前提,即某些工作对于非人类智能来说太难了,但现实是企业家将彻底改造整个行业。更不用说将被取代的个人工作了。全新的技术解决方案将以全新的方式满足这些需求。怎么会这样?

就拿老年护理护士这个职业来说吧。这项任务被广泛认为是不可替代的,因为只有人类才能提供老年人所需的同情、理解和关怀。但是,如果年老不再是一件事呢?

今天,我们对衰老的原因有所了解。在不远的将来,有一天,可能在人工智能的帮助下,我们将找到阻止衰老的方法,不久之后,我们也将能够逆转衰老。人们将能够几乎永远活着(除非被自动驾驶卡车撞上),并生活在他们选择的年龄。老年身体将是一种选择,而不是我们所遭受的痛苦。老年人护理这一职业将像镇上的叫卖者和排球场工人一样成为过去。

Pinsetters working in Subway Bowling Alleys, Brooklyn, New York, 1910

教书怎么样?教育行业也在发生巨大的变化。在许多国家(尤其是亚洲)的成人学习领域,博士水平的教育是通过在线课程进行的,只是偶尔与现实世界的教师进行磋商,他们可能只通过视频会议向学生展示。在这种情况下,学生每学期只和他们的教授见面一次。各种职业的任何水平的沉浸式虚拟现实课程将提供可想象的最引人入胜的学习体验。

对于教师这一职业,让我们来看看一项更具雄心的潜在行业变革技术。如果有一天我们能够通过简单地将知识上传到我们的大脑来学习,而不是依靠人类来传达想法,那会怎么样?学习一门技能,比如用 C 语言编程(如果这仍然相关的话),或者只是为了好玩而学习一门新的死语言,比如拉丁语,只需要去购物中心的电子学习中心,那里的知识会在几个小时内传送到我们的大脑。世界各地的十几个研究机构正在研究脑机接口(BCI),这将使这成为可能。如果不是 BCI,一些类似的革命性的东西也会让教师这个职业几乎过时。除了一些学科专家将课程经验整合起来教授最新的发现,几十年后将不再需要课堂教师。

那么,人类还有什么可做的吗?是的,会留下一些工作,但不会很多。

对于最不熟练的人来说,在很长一段时间里,通过给物体、声音、情境、情感、策略等贴标签来教授人工智能。将是一条获得收入的可靠途径。这是一个成千上万的人已经在做的职业。然而,这种新的职业可能也不是未来的证明,因为最终人工智能将能够通过找到自己的数据源来自主学习。它将观看数以亿计的 YouTube 视频,并阅读所有已出版的书籍,以获得对物理世界和人类文化的新理解。总有一天,那些不计其数的挑战视频会派上用场,让人工智能了解人类忍耐的极限。

我们中的许多人将和 AI 一起工作。随着时间的推移,自动化水平将会提高,人类的参与将会减少,到某一点,人类将只是一个道德观察者,以确保人工智能不会出错。这已经在许多地区开始发生。例如,放射科医生经常咨询基于人工智能的系统来诊断病人,谷歌的风险投资家依赖人工智能来挑选基于先前数据显示最有可能成功的创业团队。

数量相对较少的训练有素的工程师、产品经理、设计师和其他专业人士将构建人工智能和自动化系统,让我们剩下的人自动完成我们训练了一辈子的工作。

AI 靠数据。数据越多,人工智能越好。人工智能越好,使用它的人就越多,因此它可以产生更多的数据。这就形成了一个恶性循环,赢家通吃。拥有巨大市场和数据的技术先进国家(如美国和中国)将领先于较小的国家,并将获得越来越大的优势。

Abba — The Winner Takes It All

现在,如果你喜欢 Abba,开始播放视频,继续阅读。

众所周知,没有工作的人更有可能陷入抑郁甚至自杀。但是,即使人们没有工作也能保持理智,如果没有“任何事情”可供他们做,他们也很难找到生活的意义。我们可能会看到社会的大规模崩溃。

所有这些听起来都很可怕。这些可能发生的事件有好的一面吗?是的,我们可以预见多种事情的发生,使这个新的现实变得可以忍受。

所有的东西都会便宜很多。由于所有任务都由机器人以最佳方式完成,从房产到面包的一切都将大幅便宜。而且差了很多。

一般来说,劳动力成本在任何制造过程中都占很大一部分。当从获取原材料到交货的一切都由机器完成时,每一步我们都在积累储蓄和储蓄复合。相当于当前可用质量和效用的商品和服务的价格可能下降多达 90%。

这意味着,即使你只有一份低薪工作,你仍然能够支付你的需求,过上舒适的生活。你不会像富人一样进入月球上的赌场,但你会有你的虚拟现实谷歌眼镜来享受完美逼真的沉浸式音乐会体验,并吃健康的克隆牛排,旁边放着垂直养殖的西兰花。

将会出现目前不存在的新工作,对某些现有工作的需求将会增加。在一个高度科技化和虚拟化的世界,对原创人造物品和人类现场表演的需求可能会激增。

人们将能够花更多的时间和他们所爱的人在一起,因为他们负担得起,或者因为他们没有其他事情可做。爱情、友谊、哲学将获得更大的重要性。人们会在相互交往和思考中找到意义,而不是从他们的工作和成就中获得自我价值。

我们将能够收拾工业革命以来留下的烂摊子。当智能机器人不知疲倦地工作时,我们将能够分配给它们目前人工无法想象的任务,如清理海洋,或通过从垃圾中挑选有用的原材料来进行近乎完美的回收。改变工业的基本技术也可能出现在环境保护领域。一旦我们能够设计和大规模生产一旦被丢弃就立即腐烂的材料,包装废物可能不再是一个问题。

Star Trek Communicator

如果你怀疑这种技术在 30 年内成为现实的可能性,那么回头看看 1989 年。网络刚刚诞生,脱口秀节目中的人们嘲笑人们有一天会在互联网上工作的想法。今天,几乎每个人都至少部分地在互联网上工作(想想电子邮件和社交媒体)。像《星际迷航》这样的科幻电影中想象的遥远未来的平板电脑和通信设备现在已经成为商品,我们的设备比想象的要酷得多。更重要的是,技术进步不是线性的,而是指数式的。在接下来的 20-30 年里,我们将会经历过去 100 年里所经历的一样多的创新。我上面概述的想法将会很平常。

那么,你如何为即将到来的工作危机做准备呢?我显然不确定,但我有八个提示(中国文化中最幸运的数字),很高兴听到你进一步的想法:

Wang Dongling at CHINA 8 Exhibition Tour, 2015, Germany

  • 在科技发达的社会中建立你的生活。这可能是最难实现的一个,但可能是最重要的一个。尽量搬到美国、中国,或者这些顶级科技大鳄拥有强大经济和文化影响力的地区。
  • 学习指数级技术这样你就可以参与构建未来技术,而不仅仅是承受和享受其后果。你不需要改变职业,只需要探索这种技术将如何彻底改变你的领域,看看你自己是否能成为先锋。
  • 学习普通话和中国文化。由于中国快速的技术进步和越来越多的用户产生数据,中国很可能在十年内在人工智能方面超过美国。能够有效地参与中国控制的经济生态系统将使你的生存和成功机会翻倍。
  • 成为技艺高超的艺术家。因为你的“人情味”,获得一项即使机器人能做得更好也会被其他人欣赏的技能。
  • 尽可能多存钱,以你认为合适的任何方式投资指数技术。了解像比特币这样的非通胀货币,它在未来可能比中央法定货币贬值更少。
  • 看和读科幻,因为编剧已经想通了各种未来的场景。一些未来的场景会成为现实,认清这些迹象会帮助你在未来做出更好的生活决定。
  • 看历史小说和历史书。历史会重演,人类在各种情况下的行为都是可以预测的。在不断变化的环境中理解人性,有一天真的可以拯救你的生命。
  • 给自己时间去思考、梦想和幻想。我发现冥想的形式太局限了,但是我同意它的前提。当你有一分钟的时候,不要马上拿起手机,把你的大脑周期浪费在迷因上。相反,让你的眼睛和头脑去思考。分析你收到的信息。质疑显而易见的事实。注意你可以利用的机会。

将熊猫数据导出到 Elasticsearch

原文:https://towardsdatascience.com/exporting-pandas-data-to-elasticsearch-724aa4dd8f62?source=collection_archive---------8-----------------------

Photo by CHUTTERSNAP on Unsplash

如何将您的数据帧行发送到 elasticsearch 数据库。

介绍

因此,您已经完成了数据的下载和分析,并准备好将其转移到 Elasticsearch 数据库中。本文介绍了如何准备数据并将其发送到 elasticsearch 端点。

要求

Python 3.6.5
numpy==1.15.0
pandas==0.23.4
elasticsearch==6.3.1import numpy as np
import pandas as pdfrom elasticsearch import Elasticsearch
from elasticsearch import helpers
es = Elasticsearch(http_compress=True)

清理您的数据

Pandas dataframes 很乐意在您的数据中保存 NaN 值,但是对 null 值的容忍不是 elasticsearch 的特性。仔细想想,这是有道理的;您要求索引器不索引任何内容。您可以通过一些简单的函数运行数据来避免这种情况。

空白日期

如果您的数据框架有空白日期,您需要将其转换为 elasticsearch 接受的值。elasticsearch 中的日期可以是格式化的日期字符串(例如“6–9–2016”)、自 Unix 纪元以来的毫秒数或自 Unix Epoc 以来的秒数( elastic docs )。使用毫秒的空日期,因为 Unix 纪元是 1970 年 1 月 1 日。如果你有包括 70 年代早期的历史日期,你可能要考虑一些其他的。

这是一个可以和 dataframe 一起使用的简单函数。apply()清理日期列。

from datetime import datetimedef safe_date(date_value):
    return (
        pd.to_datetime(date_value) if not pd.isna(date_value)
            else  datetime(1970,1,1,0,0)
    )df['ImportantDate'] = df['ImportantDate'].apply(safe_date)

避免其他空白值

任何包含空值的字段都是有问题的,因为空日期。字符串值比日期更容易,但是您需要提供一个值。下面的代码使用 df.apply()函数将空格替换为安全字符串。

def safe_value(field_val):
    return field_val if not pd.isna(field_val) else "Other"df['Hold'] = df['PossiblyBlankField'].apply(safe_value)

创建文档

一旦您确信您的数据已经准备好发送到 Elasticsearch,就该将行转换为文档了。Panda dataframes 有一个方便的“iterrows”函数可以直接实现这一点。它返回行的索引和包含行值的对象。这是一个“pandas.core.series.Series”对象,但其行为类似于传统的 python 字典。这个代码片段演示了如何将 iter 响应分配给变量。

df_iter = df.iterrows()index, document = next(df_iter)

Elasticsearch 需要 python 格式的数据,使用。Series 对象的 to_dict()方法。但是,您可以选择要发送到数据库的数据,并使用简单的过滤函数。注意下面的函数返回一个字典理解。

use_these_keys = ['id', 'FirstName', 'LastName', 'ImportantDate']def filterKeys(document):
    return {key: document[key] for key in use_these_keys }

发电机

我们终于准备好使用 python 客户机和助手向 Elasticsearch 发送数据了。helper.bulk api 需要一个 Elasticsearch 客户端实例和一个生成器。如果你不熟悉发电机,去了解他们的记忆尊重的好处。如果你没有时间做这个,只需要理解神奇的是yield,当 bulk.helpers 函数请求数据时,a 会给出数据。下面是遍历数据框架并将其发送到 Elasticsearch 的代码。

from elasticsearch import Elasticsearch
from elasticsearch import helpers
es_client = Elasticsearch(http_compress=True)def doc_generator(df):
    df_iter = df.iterrows()
    for index, document in df_iter:
        yield {
                "_index": 'your_index',
                "_type": "_doc",
                "_id" : f"{document['id']}",
                "_source": filterKeys(document),
            }
    raise StopIterationhelpers.bulk(es_client, doc_generator(your_dataframe))

分解生成的字典

doc_generator 的工作只是提供一个带有特定值的字典。这里有一些关于这里发生的事情的细节的评论。

"_index": 'your_index',

这是您在 Elasticsearch 中的索引名称。如果没有索引,可以在这里使用任何有效的索引名。Elasticsearch 会尽最大努力自动索引你的文档。但是,提前创建索引是避免拒绝文档和优化索引过程的好主意。

"_type": "_doc",

请注意:_type已被 Elasticsearch 弃用。版本 6.3.1 仍然支持命名类型,但这是开始转换为' _doc '的好时机。

“_id” : f”{document[‘id’]}”,

_id 是 Elasticsearch 的唯一 id。不要把它和你文档中自己的“id”字段混淆。这可能是添加 itterows()中的index变量的好地方,通过类似f”{document['id']+index}".的东西使文档更加独特

"_source": filterKeys(document),

_source 是这个练习的核心:要保存的文档。使用document.to_dict()或任何其他有效的 python 字典都可以。

raise StopIteration

出于礼貌,我加入了这一行。bulk.helpers 函数将处理生成器的突然终止。但是提高 StopIteration 可以省去这个麻烦。

结论

注意一些细节,把你的熊猫数据转移到一个弹性搜索数据库是没有戏剧性的。只要记住空值是 elasticsearch 的一个问题。剩下的就是创建一个生成器,将您的行处理成 python 字典。

使用 Jupyter 内核网关公开端点

原文:https://towardsdatascience.com/expose-endpoints-using-jupyter-kernel-gateway-e55951b0f5ad?source=collection_archive---------14-----------------------

Photo by Nikola Knezevic on Unsplash

在浏览中型文章时,我偶然发现了 Jupyter 内核网关,它对我来说是全新的。所以,我也探索了一样。我发现了它的文档,并决定实施一个项目来更好地理解它。

Jupyter 内核网关是一个 web 服务器,提供对 Jupyter 内核的无头访问。— Jupyter 内核网关回购

基本上,该服务允许我们与任何给定的 Jupyter 笔记本的 Jupyter 细胞进行交互,然后相应地使用这些信息。支持GETPOSTPUTDELETE。让我们一起开始这段旅程吧。你可以在这里查看我的知识库

关于项目

Photo by Breno Assis on Unsplash

在这个项目中,我们将使用来自sklearn的加州住房数据集,在其上训练一个Random Forest Regressor模型,然后预测房价。我们将设置一个GET调用来获取数据集的统计数据,并设置一个POST端点来获取具有给定特征集的房屋的预测价格。

由于这篇文章是关于 Kernel Gateway 的,而不是关于加州住房的机器学习项目本身,我将跳过它的细节,但将解释笔记本的相关单元。笔记本位于这里

基础

Photo by Mimi Thian on Unsplash

我们先来看一下 Jupyter 内核网关的基础知识。

定义端点

我们需要用注释开始我们想要创建为端点的单元格。如果我们想要创建一个路径为/housing_statsGET端点,它被定义为:

# GET /housing_stats

然后,我们定义 Python 代码并处理数据。在单元中完成所有工作后,我们定义端点的响应。它被定义为一个带有 JSON 值的print()命令。因此,如果我们必须在参数total_houses中返回数据集中房屋的总数,我们将其定义为:

print(json.dumps({
  'total_houses': 20640
}))

就这么简单。如果需要,我们可以将功能扩展到更复杂的解决方案。因此,每个端点 Jupyter 单元将类似于下面的 Github 要点:

启动服务器

要启动服务器,有一个非常简单的命令。我们将使用名为House Price Prediction的 Jupyter 笔记本。同样的将在0.0.0.0:9090上市。代码如下:

jupyter kernelgateway --api='kernel_gateway.notebook_http' --seed_uri='House Price Prediction.ipynb' --port 9090

只要您计划在不同的项目上工作,只需更改端口号和/或 Jupyter 笔记本名称。

获取和发布端点

Photo by Mathyas Kurmann on Unsplash

让我们在笔记本中设置我们的端点。

获取端点

第一行包含单词GET来定义它是一个 GET 端点,然后是路径/housing_stats。我们提取数据集中的所有房屋,所有房屋的最大值和所有房屋的最小值。此外,根据我在笔记本中的分析,我发现最重要的特征是区块的中值收入。因此,我将所有这些值转储到一个JSON中,并将其放在打印命令中。print 命令定义了该端点的响应。

终点后

现在,我想使用我训练过的机器学习模型来预测任何具有给定功能集的新房子的价格。因此,我使用这个端点来发布我的功能,并获得作为预测价格的响应。

我通过将单元格定义为路径为/get_pricesPOST端点来开始该单元格。请求数据包含在对象REQUEST内,键body内。因此,我首先加载请求,然后从body标签中读取所有值,并将其转换成一个 Numpy 数组。然而,形状是不正确的,因此,我使用 Numpy 的函数reshape来纠正它。然后我预测价格。它返回一个预测价格的数组,该数组只有一个值,所以我将该值读入变量predicted_price。我将其重新格式化为两位小数,并乘以100000,因为值的单位是 100,000。最后,我通过将该值附加到一个字符串并放入 print 命令中来返回值。

提出请求

Photo by Dan Gold on Unsplash

让我们按照步骤与我们的端点进行交互:

  1. 启动你的 Jupyter 笔记本。我把我的笔记本命名为House Price Prediction,所以我开始也一样。
  2. 启动服务器。使用我在上面定义的命令,您的服务器将在http://0.0.0.0:9090开始运行。
  3. 最后,决定从哪里调用端点。您可以使用名为Postman的工具,或者创建一个网页来进行这些呼叫,或者您可以简单地创建另一个笔记本来呼叫这些端点。

这里,我们将创建一个笔记本Requests并使用 Python 中的requests包来调用这些端点并获得结果。

建立

让我们创建一个新的 Jupyter 笔记本Requests。我们将把requests包导入到笔记本中,它用于调用 Python 中的端点。然后,我们将变量URL中的基本 url 指定为[http://0.0.0.0:9090](http://0.0.0.0:9090.)

发出 GET 请求

我们使用request.get()发出 get 请求,并指定完整的 URL,即[http://0.0.0.0:9090](http://0.0.0.0:9090.)/housing_stats,并将其保存在变量stats中。然后,我们从该变量加载JSON。对于每个键值对,我们打印相同的内容。

stats有应答对象。为了获得编码的内容,我们使用content后跟decode('UTF-8)。然后迭代结果。

我已经在上面的 Github Gist 中添加了结果作为注释。端点用所有房屋、它们的最高和最低价格以及预测任何房屋价格的最重要因素来响应。

提出发布请求

这里,我们将使用requests.post()来发出 POST 请求。我们首先指定完整的 URL,即[http://0.0.0.0:9090](http://0.0.0.0:9090.)/get_price。我们以 JSON 的形式发送特性。您可以随意更改这些值,并查看对预测价格的影响。然后,我们从端点加载结果,并打印我们得到的结果。

expected_price有响应对象。为了获得编码的内容,我们使用content后跟decode('UTF-8)。然后读取结果,其result字段具有我们的实际响应。

从上面的评论回复中,您可以看到对于给定的一组特性,房子的预测价格是 210,424 美元。

结论

在本文中,我们讨论了 Jupyter 内核网关,它允许我们将 Jupyter 笔记本单元转换为 REST 端点,我们可以调用这些端点并从中获得响应。然后,我们通过一个示例项目探索了相同的用法。要了解更多信息,您应该查看 Jupyter 内核网关的文档。

请随时分享你的想法,想法和建议。我们随时欢迎您的反馈。

用自定义激活函数扩展 PyTorch

原文:https://towardsdatascience.com/extending-pytorch-with-custom-activation-functions-2d8b065ef2fa?source=collection_archive---------5-----------------------

Photo by chivozol from Pexels

PyTorch 和深度学习初学者教程

介绍

今天,深度学习正在迅速传播,并被应用于各种机器学习问题,如图像识别、语音识别、机器翻译等。有各种高度可定制的神经网络架构,当给定足够的数据时,它们可以适合几乎任何问题。每个神经网络都应该被精心设计以足够好地适应给定的问题。您必须微调网络的超参数(学习率、下降系数、权重衰减和许多其他参数)以及隐藏层的数量和每层中的单元数量。为每一层选择正确的激活函数也至关重要,可能会对模型的度量分数和训练速度产生重大影响

激活功能

激活函数是每个神经网络的基本构建模块。我们可以从流行的深度学习框架的大量流行激活函数中进行选择,如 ReLUSigmoidTanh 以及许多其他函数。

然而,要创建一个特别为您的任务定制的最先进的模型,您可能需要使用自定义激活功能,这是您正在使用的深度学习框架中所没有的。激活功能可以根据复杂性大致分为以下几组:

  1. 简单的激活功能路斯平方根倒数单元(ISRU) 。你可以使用任何深度学习框架快速实现这些功能。
  2. 具有可训练参数的激活功能,如软指数激活或 S 形整流线性单元(SReLU)
  3. 激活功能,在某些点不可微,需要自定义实现后退步骤,例如双极整流线性单元(BReLU)

在本教程中,我将介绍使用 PyTorch 框架实现和演示所有这些类型的函数。你可以在 GitHub 上找到这篇文章的所有代码。

安装

要浏览激活函数的实现示例,您需要:

  • 安装 PyTorch
  • 要将必要的导入添加到脚本中,

The necessary imports

Prepare the dataset

最后一件事是建立一个示例函数,它运行模型训练过程并打印出每个时期的训练损失:

A sample model training function

现在已经为创建带有定制激活功能的模型做好了一切准备。

实现简单的激活功能

最简单常见的激活功能

  • 是可微的并且不需要手动执行后退步骤,
  • 没有任何可训练参数。它们的所有参数都应该预先设定。

这种简单功能的一个例子是 Sigmoid 线性单元或路斯,也称为 Swish-1:

SiLU

这样一个简单的激活函数可以像 Python 函数一样简单地实现:

所以现在路斯可以用在用神经网络创建的模型中。顺序:

或者在一个简单的模型中,它扩展了 nn。模块类别:

用可训练参数实现激活功能

有很多带参数的激活函数,可以在训练模型的同时用梯度下降法进行训练。其中一个很好的例子是软指数函数:

Soft Exponential

要使用可训练参数实现激活功能,我们必须:

  • 从 nn 派生一个类。模块,并使该参数成为其成员之一,
  • 将参数包装为 PyTorch 参数,并将 requiresGrad 属性设置为 True。

以下是软指数的一个例子:

现在,我们可以在模型中使用软指数,如下所示:

用自定义后退步骤实现激活功能

激活功能的完美例子是 BReLU (双极性整流线性单元),它需要实现自定义的后退步骤:

BReLU

此函数在 0 处不可微,因此自动梯度计算可能会失败。这就是为什么我们应该提供一个自定义的后退步骤来确保稳定的计算。

要使用后退步骤实现自定义激活功能,我们应该:

  • 创建一个继承 torch.autograd 函数的类,
  • 重写静态向前和向后方法。Forward 方法只是将函数应用于输入。向后方法是在给定损失函数相对于输出的梯度的情况下,计算损失函数相对于输入的梯度。

让我们看一个 BReLU 的例子:

我们现在可以在模型中使用 BReLU,如下所示:

结论

在本教程中,我介绍了:

  • 如何用 PyTorch 创建一个简单的自定义激活函数
  • 如何创建一个带有可训练参数的激活函数,该函数可以使用梯度下降进行训练,
  • 如何创建一个自定义后退的激活函数?

本教程的所有代码都可以在 GitHub 上获得。为 PyTorchKeras 实现定制激活功能的其他例子可以在这个 GitHub 库中找到。

改进

在构建许多自定义激活函数时,我注意到它们通常会消耗更多的 GPU 内存。使用 PyTorch 就地方法创建定制激活的就地实现改善了这种情况。

附加参考

以下是额外资源和进一步阅读材料的链接:

  1. 激活功能 wiki 页面
  2. 【PyTorch 扩展教程

在 Travis CI 上进行广泛的 Python 测试

原文:https://towardsdatascience.com/extensive-python-testing-on-travis-ci-4c24db9bf961?source=collection_archive---------17-----------------------

在几种操作系统上测试开源 Python

假设您有一个正在维护的开源 Python 项目或包。您可能想在目前广泛使用的主要 Python 版本上测试它。你绝对应该。在某些情况下,您可能还需要在不同的操作系统上测试它。在这篇文章中,我将讨论这两种情况,并建议一种方法来做到这一点。

为了这篇文章,我假设你是:

  1. 在 Github 上托管你的开源项目。
  2. 使用pytest来测试你的代码。
  3. 检查代码覆盖率。
  4. 您希望向免费服务 codecov.io 提交覆盖率统计数据,以便在您的 repo 上有一个漂亮的动态测试覆盖率徽章。

因为这是一个很好的简单流程,也是我使用的流程。然而,这里介绍的方法可以很容易地适用于其他流程。

下面的帖子向你展示了产生 .travis.yml 文件的结构的各个阶段和合理性,该文件全面涵盖了不同 Python 版本和操作系统上的测试。如果你对这些都不感兴趣,你可以看看我为它创建的 GitHub gist 的最终结果。

Travis 的基本 Python 测试

在几个 Python 版本上测试代码的一个很好的方法是使用 Travis CI 服务,它为开源(即公共)GitHub 项目提供(除了其他特性之外)基于云的持续测试免费。让我简单地向您介绍一下 Travis 的基本 Python 测试。如果你熟悉这个话题,请随意跳过。

要使用 Travis,只需遵循以下三个步骤:

  1. 向 Travis 注册 GitHub,允许 Travis 访问您的项目。
  2. 您的存储库页面中为您想要测试的存储库启用 Travis。
  3. 将一个.travis.yml文件放在您的存储库的根目录下;这个文件将告诉 Travis 应该如何构建和测试这个特定的项目。

. travis.yml 文件

那么假设你有一个小 Python 包,带一个简单的 [setup.py](https://github.com/shaypal5/skift/blob/master/setup.py) 文件。您有下面这个非常基本的.travis.yml,它通过几个 Python 版本运行您的测试:

让我们检查一下上述文件的每个部分:

  • 第一行声明我们正在构建一个 Python 项目。

  • 第二个条目详细描述了我们想要构建的 Python 版本:2.7、3.5 和 3.6。

  • 第三个条目按顺序详细说明了我们想要运行的预安装命令集。我使用四个命令:
    (1) python --version来查看我正在运行的确切的 Python 版本。因为你总是要使用最新的画中画。有些构建会因为没有这个而失败(比如 Python 2.7,对我来说)。
    (3)pip install -U pytest——我已经决定一直这样做,因为这又一次让我免于失败。对于我目前的一些项目,如果我只有pip install pytest而没有-U来更新,Python 3.6 将会失败。
    (4) pip install codecov —因为我只在 Travis 上使用它,而不是在本地,所以它不是我的包的额外[test]依赖项的一部分。

  • 第四个条目详细介绍了安装命令。在这种情况下,由于命令在存储库的根文件夹中运行,并且我们有一个 pip-installable 包,我们用可选的测试依赖项pip install“this”文件夹(so .)。我的包的测试依赖通常是集成了两者的pytestcoveragepytest-cov

  • 第五个条目详细描述了要运行的构建/测试脚本。我们只想运行pytest。我假设您在一个pytest.ini文件中详细描述了pytest的所有 CLI 参数。当我们开始添加内容时,这将有助于保持.travis.yml文件的整洁。这里有一个pytest.ini的例子:

  • 最后一项详细描述了测试脚本成功完成后要运行的命令。在我们的例子中,我们只想向 codecov.io 报告测试覆盖结果,所以我们运行相应包的命令。它将获取由我们的pytest运行生成的覆盖报告,并将其发布到那里。如果您想要任何构建的覆盖率结果——失败的或者成功的——只需将这一行作为script条目的第二项。

特拉维斯建造

有了这个配置,每次提交到您的存储库都会触发 Traivs CI 上相应项目的良好构建,每个构建由每个 Python 版本的一个作业组成(都在 Linux 机器上),如下所示:

A travis build consisting of several jobs

您还可以进入每个作业的日志,查看每个命令的结果,无论是实时的还是事后的:

A log for a specific job in a Travis build

在几个 Python 版本上测试您的 Python 代码的另一种方法是使用 tox,这是一个自动化和标准化 Python 测试的强大工具。我支持上面的方法,因为它的one job = = one Python version方法意味着构建完成得更快(因为在 Travis 上有多达三个作业并行运行),并且您可以立即了解哪个版本给您带来了麻烦,而无需深入查看日志。当然这只是我个人的喜好。

事情急转直下…

当我们试图使用上述结构来测试 Python 3.7 时,多版本 Python 测试中的第一个复杂性出现了。如果您只是在 Python 版本条目中添加了3.7,那么您的新 3.7 版本将很早就失败,需要寻找一个足够的归档。这是因为 Linux 构建默认运行在 Ubuntu 14.04(昵称trusty)上,而 Python 3.7 和更高版本的作业需要 Ubuntu 16.04(昵称xenial)。

你可以通过在你的.travis.yml文件中添加一个dist: xenial条目来解决这个问题,让所有版本的作业都建立在 Ubuntu 16.04 上。但是如果你喜欢在 Ubuntu 14.04 上测试较低版本,并且只在 Python 3.7 上使用xenial呢?虽然这可能是一个可以忽略的特例,但它将允许我以渐进的方式介绍 Travis 构建矩阵,这些将在稍后被证明是至关重要的,所以请跟我来。

构建矩阵和 Python 3.7

在 Travis 中有两种方法可以指定多个并行作业。首先是为影响构建环境的多个条目提供多个选项;所有可能组合的构建矩阵被自动创建并运行。例如,以下配置生成一个扩展到 4 个单独(2 * 2)作业的构建矩阵:

第二个是在matrix.include中指定您想要的配置的精确组合。继续上面的例子,如果 Python 2.7 不支持并行化,您可能更喜欢指定三个特定的作业:

或者,在我们的例子中,要在xenial上运行 Python 3.7 作业,添加一个条目:

酷毙了。因此,我们看到了在 Travis 上测试 Python 3.7(Python 的特殊雪花)的两种方法,并对 Travis 构建矩阵有了一些了解。我们继续吧。

在其他操作系统上测试 Python 项目

因此,您正在 Python 的每个重要主要版本上测试您的简单纯 Python 包。你是一个负责任的开源贡献者。为你欢呼。

但是如果你的 Python 项目不是纯血统,而是包含一些专门的 C++代码的麻瓜呢?或者也许你的代码纯 Python,但是它与操作系统交互(例如写文件、处理线程或进程等。)从某种意义上说,这在不同的操作系统之间会有所不同?

如果是这样的话,如果您希望您的项目支持这三种主要的操作系统,那么您肯定应该在这三种操作系统上测试您的代码(也可能构建它)。

对我自己来说,需求来自两个项目,都是纯 Python:

  1. 第一个是 Cachier ,一个为 Python 函数提供持久、无陈旧、本地和跨机器缓存的包。当我写文件时,为了多线程的安全,我不得不锁定文件,结果是当一个 Windows 用户第一次试图使用我的包时,我的内置解决方案(使用fcntl)崩溃了。
  2. 第二个是 Skift ,为Pythonfast text实现scikit-learnwrappers。实现需要以不同的编码读写文件,在某些情况下,这在不同的操作系统上表现不同。

我选定的解决方案是扩展 Travis 构建矩阵,以包含操作系统和主要 Python 版本的特定组合,每个版本都在完全独立的环境中运行,各司其职。

同样,当将这种方法与使用tox进行比较时,我要说的主要优势是:

  1. 把复杂性和责任从你身上卸给特拉维斯。
  2. 获得真实环境的更准确的表示:直接在操作系统级安装单一版本的纯 Python,而不是通过tox。这是大多数小型开源 Python 项目用户安装代码的方式。
  3. 一个作业==一个 OS 版本和一个 Python 版本。您可以立即看到构建是否因为您的测试在特定 Python 版本(例如,所有操作系统上的 2.7 版本)、特定操作系统(Windows 上的所有 Python 版本)或特定组合上失败而失败。这在作业视图中非常明显:

We obviously have a Linux-related build problem

希望我已经让你相信这是一种有效的多操作系统测试方法,所以我们可以进入细节了。我们将从在 macOS 上进行测试开始,到在 Windows 上结束。

在 macOS 上测试 Python 项目

在撰写本文时, Python 版本在 macOS 环境中不可用。这并不意味着不可能用 Travis 在 macOS 上测试 Python,只是下面这种幼稚的方法行不通:

无论你给python键分配什么版本号,你都会得到一台安装了 Python 3.6.5 的 macOS 机器。这是因为请求一台带有os: osx的机器会启动一台使用默认 Xcode 映像的机器,对于 Travis 来说,默认 Xcode 映像当前是 Xcode 9.4.1。

当前获取特定 Python 版本的 macOS 机器的黑客方式是使用osx_image标签请求一个特定的 Xcode 映像,您知道它预装了您想要使用的 Python 版本。

例如,要获得一台装有 Python 3.7 的机器,您可以添加条目osx_image: xcode10.2(具体来说,您将获得 Python 3.7.3)。酷毙了。那么如何知道哪个 Xcode 映像来自哪个 Python 版本呢?不幸的是,这个映射在 Travis 的网站或文档中没有列出。

不过,你很幸运,我做了肮脏的工作,挖出了这个信息。这基本上意味着积极地在 Travis 的博客中搜索关于 Xcode 图片的帖子发布版,以搜寻每张图片的 Python 版本。我发现的主要 Python 版本的最新版本有:

  • xcode9.3 —预装 Python 2.7.14_2
  • xcode9.4 —预装在 Python 3.6.5 中
  • xcode10.2 —预装 Python 3.7.3

不幸的是,我还没有找到 Python 3.5 预装的 Travis Xcode 映像(如果有,请告诉我!).

所以你得到了正确的 Xcode 标签。然而,您仍然需要修改一些构建命令。例如,对于 Python 3 版本,我们需要显式调用pip3python3来安装和调用(分别)Python 3 代码,因为 macOS 预装了 Python 2(这是python命令所指向的):

考虑到这一点,您可能会认为 Python 2 作业需要更少的自定义条目。不幸的是,因为我们使用的是 OS Python,所以 pip 安装命令需要附加 Python 2 的--user标志。此外,结果是他们的 CLI 命令不会被安装,所以我们将不得不再次通过python命令调用他们的命令:

很好,我们已经完成了在 macOS 上测试 Python。吃块饼干吧。

A cookie

在 Windows 上测试 Python 项目

Travis 对 Windows 版本的支持还处于早期阶段。目前,仅支持 Windows Server(版本 1803)。这不是 Python 自带的,而是 Chocolatey 自带的,它是 Windows 的一个包管理器,我们将用它来安装 Python。

因为我们使用 Chocolatey 来安装 Python,所以我们受限于通过它可以获得的版本。对于 Python 3,它们是 3.5.4、3.6.8 和 3.7.4。对于 Python 2,版本 2.7.16 是当前默认安装的版本。

下面是获取 Windows-Python 作业的作业条目的简单变体,它包括 Chocolatey install 命令choco和一个环境变量设置:

如您所见,通用的scriptafter_success阶段工作得很好。你可以看一下最终文件来了解每个版本所需的细微变化,包括 Python 2.7。

最终注释

到目前为止,我们已经介绍了几乎所有常见操作系统和重要 Python 版本的组合。结合我们上面看到的零碎内容,我们可以得出一个不那么短的.travis.yml文件,为 Python 项目提供全面的测试,你可以在我创建的 Github gist 中找到它。

然而,在我结束这篇文章之前,我想补充一些最后的注释。

允许失败和快速完成

在某些情况下,您可能希望在您预计会失败的特定操作系统版本组合上连续测试您的代码,例如当某些测试在 Windows 上失败,但您正准备在不久的将来添加 Windows 支持时。在这种情况下,最好不要因为这样的工作而导致整个构建失败,这样您就不会收到烦人的构建失败通知(还因为您可以在 repo 上展示一个漂亮而闪亮的“build: passing”徽章)。

Gandalf being a dick about a failing Windows job

您可以通过在矩阵条目下添加一个allow_failures条目来实现这一点,详细说明允许作业失败的键值对。例如,要允许 macOS 上的 Python 3.7 失败,请使用:

matrix:
  - allow_failures:
      - os: osx
        osx_image: xcode 10.2

设置- os: windows将允许所有 Windows 构建失败。

此外,如果您已经在使用allow_failures逻辑,您可能想要利用fast_finish功能。一旦所有不允许失败的任务完成,设置fast_finish: true将决定整体构建状态——通过或失败,而其余的任务保持运行。这在小型开源项目中通常并不重要,但是拥有它是很好的,特别是当一些外来的操作系统或 Python 版本的任务被允许失败需要很多时间的时候。

针对开发分支的测试

您可以通过添加相应的条目来针对不同 Python 版本的开发分支测试您的代码,比如在python键下的3.7-dev。要测试的一个重要的开发分支可能是3.8-dev,为即将到来的事情做准备。您可能还希望允许所有使用开发分支的作业失败。

Python 版本或基于操作系统的逻辑

我介绍的解决方案将 macOS 和 Windows 版本的大部分特殊代码放在构建矩阵中。但是,如果您有一些特定于 Python 版本的安装或测试代码,但应该在所有操作系统上运行,则可以根据相应 Travis 环境变量的值来调整命令:

if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then pip install . ancient_testing_packge; else pip install ".[test]"; fi

要对同一操作系统的所有作业执行相同的操作,请使用:

if ["$TRAVIS_OS_NAME" == "linux"]; then pip install . special_linux_packge; else pip install ".[test]"; fi

当然,如果是这种情况,您可能应该考虑在您的setup.py文件中更干净地处理这个问题,通过基于 Python 版本或操作系统(使用 Python 代码推断)动态地为test构建extras_require

感谢你通读这篇文章。希望你觉得有用。😃
同样,您可以在专用的 Github gist 中查看完整的结果.travis.yml文件。

如果您有任何意见、更正或改进的想法,请写在下面,或者通过我的个人网站联系我。干杯!

外部产品

原文:https://towardsdatascience.com/exterior-product-ecd5836c28ab?source=collection_archive---------16-----------------------

为什么线性代数可能不是最后的空间数学形式

线性代数

在使用线性代数一段时间后,你开始忘记一些小东西并没有真正的意义。

决定因素

之字形图案起初令人困惑,但你被告知要放心。你可以认为矩阵 A 的行列式等于它的向量所覆盖的面积。

好的,那么下面矩阵的行列式呢?

原来,行列式只为方阵定义。🤔

好吧,但是如果你有两个矩阵 A 和 B 的面积,你当然可以把它们加起来!

不,结果是没有人知道 lhs 是否是 rhs 的一个功能。

交叉乘积

几何学上,叉积指的是两个向量 a 和 b 的平行四边形所跨越的面积。

https://en.wikipedia.org/wiki/Cross_product

但是由于某种神秘的原因,叉积只定义在ℝ上。🤔

想想看,当你处理任何类型的数据集时,你都在处理比ℝ大得多的维度,所以为什么你会在深度学习代码中看到内积,而不是叉积,尽管它确实计算了一些有用的东西。

老实说,挥动你的手指并根据你的选择改变你的计算结果也很奇怪。如果你维护一个物理相关的代码库,你需要确保你正在使用的所有库都有足够的方向转换。

https://en.wikipedia.org/wiki/Cross_product#/media/File:Right_hand_rule_cross_product.svg

我们能做得更好吗?

原来,线性代数只是在麦克斯韦公布了他著名的方程之后才变得流行起来,但是还有其他人,比如格拉斯曼和克利福德,正在用不太为人所知的形式来工作,使得某些空间运算变得更加简单。

虽然现在研究几何代数的细节可能看起来很深奥,但考虑到在可预见的未来,我们将与深度模型和制造机器人一起工作,因此我们的数学工具自然会进化以更好地适应我们。

外部产品

为了体验用几何代数可以做的事情,我们将看看外部产品。

外积定义为某个向量空间 V 中的 a ∧ b,其中 a,b ∈ V。

它需要满足两个属性

1.反对称 : a ∧ b =-b ∧ a

2.双线性 : a ∧ (b + λ c) = a ∧ b + λ a ∧ c

到目前为止,这两个性质似乎很抽象,但让我们看一个反对称和双线性成立的例子。几何!

决定因素

我们取一个平行四边形,边向量为 a 和 b。

问:如果我们将 b 加倍,平行四边形的面积如何变化?

a:面积(a,2b) = 2 面积(a,b)

问:如果我们将 b 乘以-2,平行四边形的面积会如何变化?

a:面积(a,-2b) =面积(a,2b) = 2 面积(a,b)

问:如果我们把 c 加到 b 上,平行四边形的面积如何变化?

a:面积(a,b + c) =面积(a,b) +面积(a,c)

区域也有一个方向,这是我们在线性代数中通常忽略的,但对于像计算机图形中的碰撞检测和剔除这样的问题是有用的。

面积(a,b)是旋转远离屏幕的面积,面积(b,a)是旋转朝向屏幕的面积,它们的关系是面积(a,b)=-面积(b,a)

我们可以把这些例子归纳成 3 条定律

1.面积(a,b)=-面积(b,a)

2.面积(λ a,b) = λ面积(a,b)

3.面积(a,b + c) =面积(a,b) +面积(a,c)

现在,如果我们将 Area(a,b)重命名为 a ∧ b,你会看到平行四边形的面积只是楔形积的一个特殊应用

  1. a ∧ b =-b ∧ a

2.(λ a) ∧ b = λ (a ∧ b)

3.a ∧ (b + c) = a ∧ b + a ∧ c

在线性代数中,我们通常认为向量或矩阵是保存ℝ元素的容器,因此一个 n 维向量保存 n 个ℝ.元素相反,我们可以把一个 n 维向量看作ℝ^n 的一个元素,并在这个更高层次的结构上执行我们的计算。

任何向量都可以用基向量来表示。在二维空间中,我们有两个基向量:

我们可以用它们的基向量来表示 a 和 b

楔形积∧有一个重要的性质

现在如果我们取 a ∧ b

我们拿回行列式公式!在任何时候我们都没有把自己局限在方阵中。

交叉乘积

叉积通常定义在ℝ上,这意味着我们需要计算 3 个基本向量

我们想用两个向量 a 和 b。

如果我们取他们的楔形积∧

如果我们看看这些系数,它们看起来和典型的叉积是一样的。

这个定义没有理由只适用于ℝ,我们可以很容易地把它推广到ℝ⁴和更远的地方。

但是我们不用 I,j,k 作为基本向量,而是用 e ∧ e,这是一个更高维的空间。这是几何代数中的一个普遍主题。

在线性代数中,我们通常认为向量是矩阵,是保存ℝ元素的容器,所以一个 n 维向量保存 n 个ℝ.元素相反,我们可以把一个 n 维向量看作ℝ^n 的一个元素,并在这个更高层次的结构上执行我们的计算。

下一步

几何代数是一种数学语言,用于规划几何问题。

由于深度学习和机器人技术变得如此流行,我们极有可能在未来做更多的几何。如果线性代数不能像几何代数那样自然地表达几何问题,为什么要选择它这样一种流行的形式主义呢?

我只讨论了一个运算符,即 wedge product∧——它还有很多应用,了解它们的一个好方法是查看下面的参考资料。

  • 计算机科学的几何代数—这本书令人惊奇的地方在于,它详细介绍了构建一个高效的几何代数库和渲染器的所有细节,你可以随意摆弄。这是我最喜欢的几何代数书,但不可否认它很难阅读,据我所知,没有教科书对初学者非常友好,但这应该改变,因为越来越多的人开始注意形式主义的优点。
  • R 上的几何代数介绍 —伟大的几何代数第一次介绍。我强烈建议你在检查动画时也检查源代码,看看一切是如何工作的。
  • 线性代数做对了在我看来是我读过的线性代数理论方面最好的介绍。(比 Strang 清晰)。它将帮助你把矩阵看作线性映射,这简化了对矩阵所有性质的理解。
  • 应用线性代数简介:向量、矩阵和最小二乘专注于数值线性代数及其在优化和机器学习中的应用。它附带了一个免费的在线指南,介绍了 Julia 中的所有算法,大约和主书一样长。
  • 通过外积的线性代数是一个很好的参考,它使用外积重新证明了线性代数的所有主要定理。
  • ganja . js-Javascript 中的几何代数库,有疑问的时候看看代码。通过查看示例,你会学到很多东西。
  • 几何代数在 66 页中给出了严格的介绍。
  • 游戏引擎开发基础,第 1 卷:数学——涵盖了 3D 图形所需的所有数学知识,这本书的最后一部分致力于 Clifford 和 Grassman 代数。这本书简洁明了,附有大量精美的插图

承认

我从来没有想过我会最终研究像几何代数这样的东西,但是亚当·尼梅克多次坚持认为这是值得的,他是对的。他维持着一个有趣而活跃的图形研究人员和数学家的不和谐社区,讨论如何更广泛地使用几何代数。我还要感谢 u/muntoo 仔细校对了这篇文章。

使用 Apache Jena (SPARQL 引擎)提取和查询知识图

原文:https://towardsdatascience.com/extract-and-query-knowledge-graphs-using-apache-jena-sparql-engine-5c66648797a4?source=collection_archive---------28-----------------------

Image by LTDatEHU — Pixabay

在本文中,我们将使用 Apache Jena 下载、提取和查询 AGROVOC 三元组

AGROVOC 是一个受控词汇,涵盖了联合国粮食及农业组织( FAO )关注的所有领域,包括粮食、营养、农业、渔业、林业、环境等。

AGROVOC 由资源描述框架(RDF)三元组组成。每个三元组由 3 个部分(主语、谓语和宾语)组成,如“Alice — knows — Bob”,其中“Alice”是主语,“knows”是谓语或关系,“Bod”是宾语。然而,由于我们可能有不止一个人的名字叫“爱丽丝”,那么最好给主题添加一个统一的资源标识符(URI ),使其唯一。例如,“http://Google . com/developers/teamA/Alice”现在更容易区分不同的主题。称为知识图的 RDF 三元组的集合。

为什么我们需要提取三元组?

如果你观察水的 URI<http://aims.fao.org/aos/agrovoc/c_8309>,你会注意到最初的实体c_8309代表“水”。然而,我们把它的 ID 而不是真正的单词“water ”,因为它也可能存在于其他语言中(查看下图)。因此,我们需要提取实际的单词,而不是 id,这是第一个查询的目的。

The page for the entity “c_8309”, notice the entity names in different languages on the right bar

下载要求

  • 首先,你需要下载 AGROVOC 数据集。点击这里或在谷歌上搜索 AGROVOC,然后点击“发布”并下载文件“agro VOC LOD nt(zipped-44 MB)”,扩展名“nt”告诉你这是一个三重文件。
  • 您还需要下载 Apache Jena,以便我们可以使用 SPARQL 引擎提取和查询数据集。点击这里或者谷歌搜索 Apache Jena 然后下载"Apache-Jena-3 . 13 . 1 . zip(sha 512PGP )"

提取三元组

下载 Apache Jena 后,我们将使用两个主要命令:tdbloader2tdbquery.,第一个命令为三元组创建索引以使将来的查询更快,第二个命令允许我们运行 SPARQL 查询。请注意:为了能够使用这些命令,您需要导航到apache-jena-<version>/bin/然后运行命令,或者您需要提供其位置的路径,例如apache-jena-<version>/bin/tdbloader2

为三元组建立索引

tdbloader2 --loc Indices agrovoc_2019-06-03_lod.nt

注意:Indices是存储索引的目录(给出任何名称)。agrovoc_2019-06-03_lod.nt是我们从 AGROVOC 下载的输入文件(triples)的名称。

之后,创建一个新文件(我更喜欢扩展名为.sparql)并编写以下 SPARQL 查询,该查询只提取英语对象:

**SELECT** ?subject ?predicate ?object
**WHERE** {
?subject ?predicate ?object
**FILTER**(lang (?object) = 'en')
}

最后,使用以下命令执行查询:

tdbquery --loc Indices --query English_Query.sparql 

English_Query.sparql是查询文件的名称。可以使用 Linux >命令将输出定向到一个文件,如下所示:

tdbquery --loc Indices --query English_Query.sparql > output.txt

其他查询示例

另一个简单的查询是返回整个知识图(所有三元组)而不进行任何过滤:

**SELECT** ?subject ?predicate ?object
**WHERE** {
?subject ?predicate ?object
}

运行以下查询以返回所有以“水”为主题的三元组(不要忘记在水之前包括 URI):

**SELECT** ?predicate ?object
**WHERE** {
<water> ?predicate ?object
}

注意,在这个例子中,我们没有在查询中包含?subject,因为我们的目标是返回与<water>主题相关的谓词和对象。

您可以使用非常有用的命令,比如与 SQL 语法非常相似的FILTER, GROUP BY, HAVING, etc.。然而,其他数据集可能由四元组而不是三元组组成。四元组由三元组组成,第四部分称为上下文。比如“Alice—knows—Bobs—Google dev department”。您仍然可以使用包含上下文部分的精确查询,因为图中可能有多个上下文。以下查询将返回上下文为“GoogleDevDepartment”的所有四边形:

**SELECT** * **WHERE** {
**GRAPH** ?GoogleDevDepartment {
?subject ?predicate ?object
}
}

恭喜你,现在你应该能够提取和查询知识图了!

从在线评论中提取热门话题

原文:https://towardsdatascience.com/extract-trending-topics-from-user-reviews-9d6c896451d7?source=collection_archive---------27-----------------------

文本中频繁项集挖掘的实验

Photo by Rock Staar on Unsplash

顾客在选择餐馆时依靠在线用户评论。当一群评论者开始发布关于一个共同话题的评论时,评论可以保持趋势。这个话题可以是一家餐馆新增加的特色,一家新餐馆在城里开张,或者是关于菜单上一个受欢迎的食物。这个共同话题可以解释为趋势性话题。然后顾客开始追随新的潮流。我的方法是通过使用频繁项集挖掘技术来获得评论者每月的趋势主题。我要使用的数据集是来自 Yelp 挑战赛 2019 的 Yelp 数据集。

频繁项集挖掘与关联规则挖掘

频繁模式在数据集中反复出现。频繁项集由这些模式之一组成。频繁项集挖掘发现超过最小阈值的有趣的频繁项集。此阈值是一个度量值,它指示项集中包含的最小项数。频繁模式挖掘的概念是随着购物篮分析而发展起来的。购物篮分析可以作为当今最流行的例子。这里对一起销售的产品进行分析。简单地说,频繁项集挖掘显示哪些项在一个事务中一起出现。

关联规则挖掘就是寻找这些项目之间的关系。鉴于购物篮分析,它计算购买一个或多个产品后购买某个产品的概率。然而,当应用于问题时,这两个概念是一起的。正在使用的流行算法有 先验FP-GrowthEclat

支持和信心

对于频繁项集挖掘,有两种主要的度量方法。他们是支持和信心。这两个度量评估模式有多有趣。支持度是绝对频率的量度,信心是相对频率。支持指示项目在所有事务中一起出现的次数。置信度表示有多少交易遵循某一规则。基本上,支持度是不考虑项目之间关系的频繁项目的度量,而置信度是项目之间关联规则的度量。以下是注释。

Support(A-> B)= Support _ count(AB)

置信度(A -> B) =支持计数(AB)/支持计数(A)

文本中的频繁项集挖掘

文本通常被称为非结构化数据。大多数时间频繁项集挖掘算法都可以应用于结构化数据。然而,文本可以转化为结构化数据,数据挖掘技术可以应用。为了输出数据的结构化版本,需要遵循几个预处理步骤。这种结构化数据可以分为两类:单个单词和多个单词。一个单词叫做单词包。包词和多重词的主要区别在于多重词保留了词与词之间的关系,从而保持了句子的语义。

另一方面,一个句子可以被看作是单词的集合。多个句子之间可以有常用词。类似地,段落由单词组成,并且多个段落具有频繁的单词集。这些常用词可以看作是一个频繁项集。Word 将成为项目集中的一个项目。所以频繁模式可以由多个单词组成。挖掘这些模式将给出句子或段落中常见单词的基本概念,而不是从文本中提取单个关键词。说到用户评论,这些常用词可以认为是趋势话题。该实验的主要目的是使用频繁项集挖掘来导出用户评论中的趋势主题。

履行

为了实现上述想法,选择来自特定餐馆的最新评论,并提取每个月的趋势主题。以下是筛选出最方便解决问题的餐厅的标准。

数据集有各种类型的业务。为了过滤掉餐馆,我在类别字段中选择了包含“餐馆”的企业。对于最新的评论,我选择了 2018 年发布的评论。当比较美国各州的餐馆数量时(图 1 ),我们看到亚利桑那州的餐馆数量最多。然后通过计算和比较 2018 年每个餐厅的评论数,选出了“亚利桑那州”最受好评的餐厅。

Figure 1 : Number of restaurants in each state in the USA according to Yelp Data set (Yelp Challenge 2019)

一开始,数据是从 Yelp 提供的 JSON 文件中提取的,并存储在关系数据库中以便于访问。我遵循的预处理步骤是转换成小写,删除标点符号,删除空格,标记单词和删除停用词。以下是代码片段。

**import** string**import** pandas **as** pd
**from** nltk.tokenize **import** word_tokenize
**from** sklearn.feature_extraction.stop_words **import** ENGLISH_STOP_WORDS**def** preprocess_reviews(reviews: pd.DataFrame):
    word_token_list = []
    **for** _, r **in** reviews.iterrows():
        formatted_review = r[**'review_text'**].lower()
        formatted_review = formatted_review.translate(str.maketrans(**""**, **""**, string.punctuation))
        formatted_review = formatted_review.strip()
        tokens = word_tokenize(formatted_review)
        result = [i **for** i **in** tokens **if not** i **in** ENGLISH_STOP_WORDS]
        word_token_list.append(result)

    **return** word_token_list;

用于检索趋势主题的算法是 先验 ,支持度是 0.3。下面是代码片段。(我提到的文章位于本文末尾的参考资料部分,以了解更多详细信息)

**import** pandas **as** pd
**from** mlxtend.frequent_patterns **import** apriori
**from** mlxtend.preprocessing **import** TransactionEncoder**def** get_trending_words_from_reviews(preprocess_reviews: []):
    transaction_encoder = TransactionEncoder()
    transaction_encoder_array = transaction_encoder.fit(preprocess_reviews).transform(preprocess_reviews)
    df = pd.DataFrame(transaction_encoder_array, columns=transaction_encoder.columns_)

    frequent_itemset = apriori(df, min_support=0.3, use_colnames=**True**)

    **return** frequent_itemset

请注意,在这个实验中没有推导出关联规则。下面的代码解释了如何调用上述函数以及要传递的审核列表的格式。

reviews = pd.DataFrame([{**'review_text'**: **'good food'**}, {**'review_text'**: **'great food'**}, ])reviews = preprocess_reviews(reviews)
frequent_itemset = get_trending_words_from_reviews(reviews)
print(frequent_itemset)

结果和讨论

下表(表 1 )显示了上述实验的结果。

Table 1 : Frequent words derived from frequent itemset mining for each month using reviews of most reviewed restaurant in Arizona by year 2018

所选餐厅在类别字段中描述自己为'三明治、意大利早餐&早午餐、美式(传统)'。我们可以看到“肉丸”、“酱”和“意大利面”这些词在一月份很流行。在二月,人们只谈论这三个术语中的两个。它们是“肉丸”和“酱”。当谈到三月时,“酱”和“意大利面”都消失了,而“肉丸”这个词幸存了下来。尽管这三个项目都是今年年初的流行趋势,但只有一个项目可以继续流行。此外,调查结果显示,术语“肉丸”已经成为全年的热门话题。“意大利面”在八月又成了一个热门话题。我们可以观察到的另一件事是,年底的复习次数一直在减少(图 2 )。

Figure 2 : Monthly review count of most reviewed restaurant in Arizona by year 2018

这可以解释为餐馆正随着时间的推移而失去它的名声。原因可能是,尽管餐馆的服务和位置得到了好评,但对于常客来说,没有什么新鲜和令人兴奋的东西可以分享。如果一家餐馆能推出新的食品,并提高逐渐消失的食品的质量,它就能挽回声誉。

每个餐厅的结果都不一样,应该据此做出解释。可以通过增加更多的预处理步骤来改善结果。在这种情况下,词干、词汇化和词性标注等概念会有所帮助。此外,可以通过将该方法分别应用于好评和差评来改进预测。

参考

[## 文本频繁项集挖掘的概念

频繁项集挖掘是一种流行的具有频繁模式或频繁项集的数据挖掘技术。

iopscience.iop.org](https://iopscience.iop.org/article/10.1088/1757-899X/434/1/012043) [## Python 中的关联分析

Python 中基于 Apriori 算法的频繁项集挖掘

medium.com](https://medium.com/analytics-vidhya/association-analysis-in-python-2b955d0180c) [## Python 中基于 Apriori 算法的关联规则挖掘

关联规则挖掘是一种识别不同项目之间潜在关系的技术。举一个例子…

stackabuse.com](https://stackabuse.com/association-rule-mining-via-apriori-algorithm-in-python/)

使用 OpenCV 和 Python 从图像中提取形状

原文:https://towardsdatascience.com/extracting-circles-and-long-edges-from-images-using-opencv-and-python-236218f0fee4?source=collection_archive---------7-----------------------

利用 OpenCV 从图像中高效提取已知形状的物体

欢迎阅读本系列博客中关于使用 OpenCV 和 Python 从图像中提取特征的第一篇文章。

图像和视频的特征提取是计算机视觉领域的一个常见问题。在本帖中,我们将考虑识别台球桌上的球和桌边的任务。

考虑下面这个在线台球游戏的例子。

Screenshot from an online game of pool (image source author)

假设我们想要标记这个图像中每个球的位置以及桌子的四个内边。有多种方法可以做到这一点,对于给定的图像,有些方法比其他方法效果更好。然而,一个有用的方法是尝试根据图像的颜色组成来分离出图像的内容。

例如,在上面的图像中,我们可以看到桌面、球和图像背景都有不同的颜色。

因此,如果我们能够分离出图像中的颜色,我们就离解决问题更近了一步。

一种简单的方法是将 RBG 图像转换成 HSV 格式,然后找出与感兴趣的对象相对应的 H、S 和 V 值的范围。

关于这一步的细节,请参考我的关于 HSV 提取的博客(即将发布)。

一旦我们有了桌面的 HSV 颜色图,我们可以使用 OpenCV "inRange()" 函数来获得提取的遮罩的可视化,如下所示。

Image mask generated using OpenCV (image source author)

正如我们所见,这一步骤有助于实现以下目标:

  1. 表格对象(白色)与图像背景(黑色)明显不同
  2. 球(黑色)与桌面(白色)清晰可辨

作为第一步,我们需要从图像中提取表格对象,以便将注意力集中在表格及其内容上,而忽略图像中的其他对象,例如背景、外部对象等。

现在是应用边缘检测技术从图像中识别和提取所需成分的正确时间。有多个选项可用,如 Canny 和 Sobel 函数,每个都有其优点和缺点。

我们将使用 OpenCV“find contours()”函数进行边缘检测,以提取掩模图像中的所有轮廓。面积最大的轮廓是对应于桌子本身的轮廓。

为了实现桌子的平滑提取,我们将找到桌子轮廓的外接矩形(OpenCV "boundingRect()" 函数),并使用其坐标从仅包含感兴趣对象的原始图像中提取子图像,在这种情况下,桌子表面和球如下图所示。

Extracted only the region of interest using OpenCV (image source author)

现在剩下的任务是提取单个的球并识别球台的内边缘。

为了实现这一点,我们将再次使用先前使用的基于 HSV 的提取方法来获得遮罩,首先聚焦于球,然后聚焦于桌子边缘。

探测球

球的遮罩图像看起来将与我们之前用于桌子的图像相同。从获得的掩模图像中,我们将再次使用 OpenCV "findContours()" 函数提取球轮廓。这一次我们感兴趣的只是那些类似圆的给定大小的轮廓。

同样,有许多方法来检测球的轮廓,但是最有效的一种方法是为每个检测到的轮廓找到最小的外接矩形,并且选择最类似正方形并且也位于期望的区域范围内的那些。在这种情况下,我们将使用 OpenCV 函数“minarerect()”

在选定的一组轮廓上,我们将进一步应用 OpenCV“minEnclosingCircle()”函数来获得每个球上统一大小的圆。

现在我们只需要使用 OpenCV "circle()" 函数用我们选择的任何颜色绘制每个检测到的球。

检测表格边缘

这是一个分两步走的方法,因为表既有外边缘又有内边缘,我们只对后者感兴趣。

第一步是使用基于 HSV 的方法得到桌子边缘的遮罩。得到的面具看起来像下面,其中所有的四个方面都可以很容易区分。

Mask for table edges detection obtained using OpenCV (image source author)

有了这个遮罩,我们现在可以通过定位离图像中心最近的两条水平线和两条垂直线来提取内部边缘。

我们将使用 OpenCV "HoughLines()" 函数来查找图像中的所有线条,并只选择我们感兴趣的 4 条。

一旦检测到 4 条线,我们只需要使用 OpenCV "line()" 函数来绘制相应的表格边缘。

然后可以将获得的图像覆盖在原始图像上,以完成如下所示的任务。

Identified all the balls and four table edges with high accuracy (image source author)

希望你喜欢阅读这篇文章。

在我的下一篇文章中,我将介绍另一个有趣的特征提取的例子,请继续关注。

重要免责声明:

这篇博文中使用的方法,尤其是用于检测球和桌子边缘的 HSV 值,不一定适用于每张图像。每幅图像的特征都是独一无二的,并且需要正确的参数集,以使特征提取按预期工作。这正是计算机视觉如此有趣和具有挑战性的领域。

为机器学习提取数据

原文:https://towardsdatascience.com/extracting-data-for-machine-learning-f90b97a97f4c?source=collection_archive---------13-----------------------

为下一个机器学习项目获取数据的三种方法

Photo by Matthieu Oger on Unsplash

任何机器学习项目最重要的第一步就是获得优质数据。作为一名数据科学家,您经常需要使用各种不同的方法来提取数据集。您可能使用公开可用的数据、通过 API 可用的数据、在数据库中找到的数据,或者在许多情况下这些方法的组合。

在下面的帖子中,我将简要介绍 python 中提取数据的三种不同方法。在这篇文章中,我将讲述如何在 Jupyter 笔记本中提取数据。我以前在早先的文章中报道过如何从命令行使用这些方法。

结构化查询语言

如果您需要从关系数据库中获取数据,很可能需要使用 SQL。您可以使用名为 SQLAlchemy 的库将 Jupyter 笔记本连接到最常见的数据库类型。这个链接提供了支持哪些数据库以及如何连接到每种类型的描述。

您可以直接使用 SQLAlchemy 来查看和查询表,也可以编写原始查询。要连接到您的数据库,您需要一个包含您的凭据的 URL。然后,您可以使用create_engine命令来创建连接。

from sqlalchemy import create_engine
engine = create_engine('dialect+driver://username:password@host:port/database')

现在,您可以编写数据库查询并返回结果。

connection = engine.connect()
result = connection.execute("select * from my_table")

Web 抓取用于从网站下载数据,并从这些页面中提取所需的信息。有很多 python 库可以用来做这件事,但是最简单的一个是 Beautiful Soup

你可以通过 pip 安装这个软件包。

pip install BeautifulSoup4

让我们通过一个简单的例子来说明如何使用它。我们将使用 Beautiful Soup 和 urllib 库从猫途鹰网站获取酒店名称和价格。

让我们导入将要使用的所有库。

from bs4 import BeautifulSoup
import urllib.request

接下来,我们要下载我们要抓取的页面内容。我将收集希腊克里特岛酒店的价格,所以我使用一个包含该目的地酒店列表的 URL。

TripAdvisor

下面的代码将 URL 定义为一个变量,使用 urllib 库打开页面,使用 Beautiful Soup 读取页面,并以易于阅读的格式返回结果。代码下面显示了部分输出。

URL = '[https://www.tripadvisor.co.uk/Hotels-g189413-Crete-Hotels.html'](https://www.tripadvisor.co.uk/Hotels-g189413-Crete-Hotels.html')
page = urllib.request.urlopen(URL)
soup = BeautifulSoup(page, 'html.parser')
print(soup.prettify())

接下来,让我们获取页面上的酒店名称列表。我们将使用find_all函数,它允许您提取文档中您感兴趣的部分。您可以使用find_all以多种方式过滤文档。通过传入字符串、正则表达式或列表。您还可以过滤标签的一个属性,这就是我们将在这里使用的方法。如果你不熟悉 HTML 标签和属性,这篇文章给出了一个很好的概述。

要了解如何最好地访问数据点,您需要检查 web 页面上该元素的代码。要查找酒店名称的代码,我们右键单击列表中的名称,如下图所示。

当您点击inspect时,代码将出现,包含酒店名称的部分将高亮显示,如下所示。

我们可以看到酒店名称是名为listing_title的类中唯一的一段文本。下面的代码将这个属性的类和名称传递给find_all函数,以及div标签。

content_name = soup.find_all('div', attrs={'class': 'listing_title'})
print(content_name)

这会以列表形式返回包含酒店名称的每一段代码。

为了从代码中提取酒店名称,我们可以使用 Beautiful Soup 的getText函数。

content_name_list = []
for div in content_name:
    content_name_list.append(div.getText().split('\n')[0])
print(content_name_list)

这会以列表形式返回酒店名称。

我们可以用类似的方法得到价格。检查价格代码,我们可以看到它有以下结构。

所以我们可以用非常相似的代码提取这一段。

content_price = soup.find_all('div', attrs={'class': 'price-wrap'})
print(content_price)

价格有点复杂,如果我们运行下面的代码,我们会看到它。

content_price_list = []
for div in content_price:
    content_price_list.append(div.getText().split('\n')[0])
print(content_price_list)

输出如下所示。当酒店列表显示降价时,除了一些文本之外,还会返回原始价格和销售价格。为了使这个有用,我们只想返回酒店的实际价格,如果我们今天预订的话。

我们可以使用一些简单的逻辑来获得文本中显示的最后价格。

content_price_list = []
for a in content_price:
        a_split = a.getText().split('\n')[0]
        if len(a_split) > 5:
            content_price_list.append(a_split[-4:])
        else:
            content_price_list.append(a_split)  

print(content_price_list)

这给出了以下输出。

应用程序接口

API 代表应用程序编程接口,就数据提取而言,它是一个基于 web 的系统,为数据提供一个端点,您可以通过一些编程连接到该端点。通常,数据将以 JSON 或 XML 格式返回。

在机器学习中,你可能需要用这种方法获取数据。我将给出一个简单的例子,说明如何从一个公开可用的名为 Dark Sky 的 API 中获取天气数据。要访问这个 API,你需要注册,每天免费提供 1000 个电话,这应该足够尝试了。

为了访问来自黑暗天空的数据,我将使用requests库。首先,我需要获得正确的 URL 来请求数据。“黑暗天空”提供预报和历史天气数据。对于这个例子,我将使用历史数据,我可以从文档中获得正确的 URL。

该 URL 具有以下结构。

https://api.darksky.net/forecast/[key]/[latitude],[longitude],[time]

我们将使用requests库来获取特定纬度和经度以及日期和时间的结果。让我们想象一下,在获得克里特岛酒店的每日价格后,我们想要找出价格是否以某种方式与天气相关。例如,让我们为列表中的一家酒店选择坐标。

Google.com

首先,我们用我们需要的正确坐标和日期时间构造 URL。使用requests库,我们可以访问 JSON 格式的数据。

import requestsrequest_url = '[https://api.darksky.net/forecast/fd82a22de40c6dca7d1ae392ad83eeb3/35.3378,-25.3741,2019-07-01T12:00:00'](https://api.darksky.net/forecast/fd82a22de40c6dca7d1ae392ad83eeb3/35.3378,-25.3741,2019-07-01T12:00:00')
result = requests.get(request_url).json()
result

我们可以将结果标准化到一个数据框架中,以便于阅读和分析。

import pandas as pddf = pd.DataFrame.from_dict(json_normalize(result), orient='columns')
df.head()

Part of the resulting data frame

使用这些方法,您可以做更多的事情来自动提取这些数据。对于 web 抓取和 API 方法,可以编写函数来自动执行该过程,以便轻松提取大量日期和/或位置的数据。在这篇文章中,我想简单地给出一个概述,用足够的代码来探索这些方法。在以后的文章中,我将会写一些更深入的文章,介绍如何构建完整的数据集并使用这些方法进行分析。

感谢阅读!

从金融 pdf 中提取数据

原文:https://towardsdatascience.com/extracting-data-from-financial-pdfs-dc2fa0b73169?source=collection_archive---------8-----------------------

如何从市政债券 CAFR 报告中快速提取文本和数据

Photo by Markus Spiske on Unsplash

什么在传播

财务的很大一部分是用来写和读财务报表的。在美国,财务报表必须是 PDF 格式,才能被认为是官方的。这给以后使 pdf 成为机器可读的文件带来了挑战。

在过去,银行和金融机构常常雇佣整个团队来阅读 PDF 格式的财务报表,并将其输入到银行的数据库中。这些职位是数据输入,表格输入。然而,其他团队会将输入的值映射到银行常用的值。这一系列的任务被称为金融传播

例如,一个申请贷款的大型家庭农场会在农场的存货清单中列出诸如彼得比尔特卡车之类的项目。但是,当银行的财务人员读取财务数据时,他们需要将卡车归类为子类别“车辆”下的非流动(非货币)资产。这使得银行可以减少财务不确定性,因为它可以将这种特定的卡车映射到银行已经处理多年的其他类似车辆。用金融术语来说,它允许银行将这种情况映射到更广泛的、标准化的帐户集合。

在不久以前,这些吊具位于低成本地区和离岸地区。今天,许多金融机构已经以某种形式采用了人工智能,以减轻人工阅读每一份金融 PDF 文件的人类负担。

我们来讨论一下 AI 是如何读取和处理这些 pdf 的。

用机器提取 PDF

我们将查看综合年度财务报告(CAFR ),这是市政债券的主要年度披露文件。CAFRs 往往主要是 PDF 格式的扫描文档。这里是纽约州市 CAFR 的一个例子。

美国大多数城市都有未偿债券,有义务通过 CAFRs 披露年度财务状况。这是一张很好的幻灯片,展示了城市总体规模和 CAFR 覆盖率。让我们来看一个来自 CAFR 的样本页面,我们将使用它来运行光学字符识别(OCR)数据提取—

CAFR from City of Hoboken, NJ, page 150

如果你仔细看,你会注意到这是一个扫描页面,保存为 PDF 格式,所以它还不是机器可读的。

CAFR 文件包含了非常有趣的关于市政规划、预算和运作的信息。它们包含了公立学校、警察部门、消防部门的信息。比如公立医院有多少病床,每张病床的收入和其他运营成本。

引擎盖下发生了什么

那么 AI 如何让上面的文档成为机器可读的文本呢?

在核心部分,人工智能试图隔离有黑色墨迹的区域和没有黑色墨迹的区域。然后,另一个人工智能,像小学生一样,观察单个墨迹来辨认单词和字符。而另一个人工智能试图理解所有检测到的文本是否被组织成表格、表格或自由形式的文本。

更具体地说,PDF 是作为图像被机器读入的。然后,一种算法试图在它看到的任何文本周围画出方框。每一个单词或字符组会得到它的盒子。一旦算法覆盖了边界框中的每一段文本,则下一组算法可以继续进行。这是边界框的样子—

Bounding box detection algorithm at work

接下来,另一种算法——光学字符识别(OCR)开始对每个边框内的文本进行解码。该算法接收文本图像,并将它们转换成包含在该图像中的计算机可理解的文本。

最后,另一种算法查看所有这些边界框和其中的文本,以了解每个单词是构成一行还是表格的一部分。

例如,最上面一行写着“霍博肯市”,边界框算法检测到 3 个带有文本的区域——“城市”、“OF”、“霍博肯”,并为它们分配置信度得分和唯一 id——

然后,合并器算法获取 3 个单词的输出,并根据文本在页面上的位置将它们组合在一起

Notice Confidence: 95 of Text: “CITY OF HOBOKEN”

最后一种合并算法还对表格、自由格式文本或表单等页面区域进行分类。这是它放在一起的样子—

Machine interpretation of the PDF table on the right

今天,上面提到的每个算法都是一个深度神经网络,用单独的训练数据进行训练,并由人类进行注释。我们可以看到每一步的置信度得分,因此我们可以通过主动学习来修正算法。

如果您对在 AWS 上建立端到端 OCR 即服务感兴趣,请点击这里

结论

由于人工智能的最新进展,我们现在可以将 PDF 文档一起读入我们的数据湖,并执行下游机器学习和分析来揭示 alpha。

新的创业公司正在涌现,从金融领域的大量文件中收集见解,这在过去是极其昂贵的。

OCR 只是金融文档如何被摄取、处理和显示以供投资专业人员获得货币优势(alpha)的一小部分,但却很重要。

利用 OpenCV 人脸检测神经网络提取人脸

原文:https://towardsdatascience.com/extracting-faces-using-opencv-face-detection-neural-network-475c5cd0c260?source=collection_archive---------9-----------------------

Photo by Maxim Dužij on Unsplash

最近,我偶然发现了 https://www.pyimagesearch.com/的网站,那里有一些关于 OpenCV 的很棒的教程。在通读它的众多文章时,我发现 OpenCV 有自己的人脸检测神经网络,准确率非常高。

所以我决定用 OpenCV 的这个神经网络做一个项目,从图像中提取人脸。每当有人处理人脸并需要从大量图像中提取人脸时,这样的过程就会很方便。

完整的项目可以作为 GitHub 库获得。为了这篇文章,我从我的 Instagram 账户上拍了一张照片。

Image used for extracting face

目标

这个项目有两个基本要素。包围人脸的方框:在图像中识别的所有人脸周围显示白色方框。Python 文件是data _ generator . py2。提取的人脸:从一个文件夹中的所有图像中提取人脸,并将每个人脸保存到一个目标文件夹中,以创建一个方便的数据集。Python 文件是 face_extractor.py

Face detection and extraction

首先,让我们执行这两个部分的共同步骤,即导入库、加载人脸检测模型、创建输出目录、读取图像和检测人脸。

项目

导入库

我导入os来访问目录中的各种文件。然后,cv2将用于处理图像。numpy有助于轻松处理多维数组。

定义路径和负载模型

OpenCV repo 本身提供了模型的prototxtcaffemodel。我使用了相同的方法,并将它们放在我的项目中的model_data目录中。prototxt文件包括网络的文本描述,而caffemodel包括权重。我读取了这两个文件,并使用cv2加载了我的model

创建目录

如果存储结果图像的目录不存在,我将创建一个目录。输出文件夹为更新 _ 图像

在提取人脸时,我会将人脸保存到目录 faces 中。如果它不存在,我将创建它。

读取图像

我遍历了图像文件夹中的所有图像。提取扩展名后,我检查文件是否属于类型.png.jpg,并且只对这些文件进行操作。

检测人脸

使用cv2.imread,我读取图像,并使用cv2.dnn.blobFromImage创建一个斑点。然后,我将这个斑点输入到模型中,并使用model.forward()从页面中获取检测结果。

通用步骤现在已经完成。对于第一个任务,我将在人脸周围绘制白色矩形,并将它们保存在 updated_images 目录中。对于第二个任务,我将把提取的人脸保存在 faces 目录中。

1.在面周围创建方框

我一个接一个地迭代图像中检测到的所有人脸,并提取它们的起点和终点。然后,提取检测的置信度。如果算法有超过 50%的把握检测到的是人脸,我会在它周围显示一个矩形。

然后,使用cv2.imwrite,我将图像保存到同名的updated_images文件夹中。

Image with white rectangle around face

2.提取人脸

如上所述,我迭代所有人脸,计算检测的置信度,如果超过 50%,我提取人脸。注意这条线frame = image[startY:endY, startX:endX]。它从图像中提取人脸。

然后,我将这个新图像转储到faces文件夹中,名称为 face number,后跟_,然后是文件名。如果我们从名为sampleImage.png的图像中提取第一张人脸,人脸文件的名称将为0_sampleImage.png。对于每个面,我递增count并在完成执行后,将计数打印到控制台。

Extracted face

终于,项目准备好了。您可以输入尽可能多的图像,并生成可用于后续项目的数据集。

结论

在本文中,我讨论了使用 OpenCV 人脸检测神经网络来检测图像中的人脸,用白色矩形标记它们,并将人脸提取到单独的图像中。

一如既往,我很想听听你的想法和建议。

从 Scikit-Learn 管道中提取特征重要性

原文:https://towardsdatascience.com/extracting-feature-importances-from-scikit-learn-pipelines-18c79b4ae09a?source=collection_archive---------6-----------------------

Photo by Thomas Ashlock on Unsplash

评估管线中特征重要性的简单方法

Scikit-learn 管道提供了一种非常简单的方法来将机器学习开发中的预处理步骤与模型拟合阶段链接在一起。使用管道,您可以嵌入这些步骤,以便在一行代码中,模型将在拟合模型或调用 predict 的同时执行所有必要的预处理步骤。

除了减少项目中的代码行之外,这样做还有很多好处。使用标准管道布局意味着同事或未来的自己很容易快速理解您的工作流程。这反过来意味着你的工作更具可复制性。此外,通过管道,您可以强制执行转换发生的顺序。

然而有一个缺点,尽管 scikit-learn 模型具有高度可解释的优点。一旦您将模型嵌入到管道中,就很难提取元素,例如使这些模型如此可解释的特性重要性。

我最近花了一些时间研究这个问题。在下一篇文章中,我将展示一个我发现的简单方法,使用 python 库 ELI5 从管道中提取特性重要性。

简单管道

在本文中,我将使用来自机器学习竞赛网站 drivendata.org 的数据集。数据集可以在这里下载。

首先,我将导入我正在使用的所有库。

import pandas as pd
import numpy as npfrom sklearn import preprocessing
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from sklearn.compose import ColumnTransformer
from sklearn.metrics import f1_score
from sklearn.preprocessing import OneHotEncoder
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.metrics import classification_report
from sklearn.linear_model import LogisticRegressionimport eli5

然后,我使用 pandas 库读取我之前下载的数据集。特征和目标标签在单独的 CSV 文件中,所以我也使用熊猫合并功能将它们合并到一个数据框中。

train_values = pd.read_csv('train_values.csv')
train_labels = pd.read_csv('train_labels.csv')
train_data = train_values.merge(train_labels, left_on='building_id', right_on='building_id')

如果我们检查数据类型,我们可以看到数字和分类数据的混合。因此,我们需要在训练模型之前应用一些预处理。因此,管道对于该数据集将是有用的。

train_data.dtypes

在构建管道之前,我删除了“building_id”列,因为它不需要用于训练,将数据分成测试集和训练集,并定义一些变量来标识分类列和数字列。

train_data = train_data.drop('building_id', axis=1)numeric_features = train_data.select_dtypes(include=['int64', 'float64']).drop(['damage_grade'], axis=1).columns
categorical_features = train_data.select_dtypes(include=['object']).columns
X = train_data.drop('damage_grade', axis=1)
y = train_data['damage_grade']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

我将构建一个简单的管道,将预处理和模型拟合步骤连接在一起。此外,我将为任何缺失值添加一个估算器。虽然我在这里使用的数据集没有任何丢失的数据,但在这一步添加是明智的。这是因为在现实世界中,如果我们将它部署为一个机器学习应用程序,我们试图预测的新数据可能会丢失值。因此,将它添加为安全网是一种良好的做法。

下面的代码构建了一个管道,用于估算任何缺失值,将标准缩放器应用于数字特征,将任何分类特征转换为数字特征,然后拟合分类器。

numeric_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='median')),
    ('scaler', StandardScaler())])
categorical_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='constant', fill_value='missing')),
    ('one_hot', OneHotEncoder())])
preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, numeric_features),
        ('cat', categorical_transformer, categorical_features)
    ])pipe = Pipeline(steps=[('preprocessor', preprocessor),
                      ('classifier',  LogisticRegression(class_weight='balanced', random_state=0))])

model = pipe.fit(X_train, y_train)

我们可以通过运行下面的代码来检查管道的质量。

target_names = y_test.unique().astype(str)
y_pred = model.predict(X_test)
print(classification_report(y_test, y_pred, target_names=target_names))

我们可以看到,就模型的性能而言,很可能还有改进的空间。除了模型选择和超参数优化之外,我们想探索的一个领域是特征工程。然而,为了决定设计哪些新功能,我们首先需要了解哪些功能最具预测性。

ELI5

从这条管道中提取特征重要性并不容易。然而,有一个叫做 ELI5 的 python 库使这变得非常简单。这个库以俚语“像我 5 岁一样解释”命名,是一个提供简单方法来解释和解释机器学习模型的包。它兼容大多数流行的机器学习框架,包括 scikit-learn、xgboost 和 keras。

该库可以通过 pip 或 conda 安装。

pip install eli5conda install -c conda-forge eli5

让我们使用 ELI5 从管道中提取特性重要性。

ELI5 需要知道所有的特性名称,以便构造特性重要性。通过对管道中的分类变量应用一次性编码,我们引入了许多新特性。因此,我们首先需要提取这些特征名称,并将它们添加到已知的数字特征列表中。下面的代码使用 scikit-learn 管道中内置的“named_steps”函数来实现这一点。

onehot_columns = list(pipe.named_steps['preprocessor'].named_transformers_['cat'].named_steps['one_hot'].get_feature_names(input_features=categorical_features))
numeric_features_list = list(numeric_features)
numeric_features_list.extend(onehot_columns)

为了提取特性的重要性,我们只需要运行这行代码。

eli5.explain_weights(pipe.named_steps['classifier'], top=50, feature_names=numeric_features_list)

这给出了一个格式良好的输出。

ELI5 库还提供了解释单个预测的能力,但管道尚不支持这一功能。在本文中,我演示了一种从 scikit-learn 管道中提取特性重要性的简单方法,这为调试和改进机器学习模型提供了一个良好的起点。

感谢阅读!

使用 R Regex 从文本中提取关键字(简单)

原文:https://towardsdatascience.com/extracting-information-from-a-text-in-5-minutes-using-r-regex-520a859590de?source=collection_archive---------10-----------------------

我们可以从美国的职位描述中提取相关信息,并将其可视化。

photo credit to Jason from unsplash

当您有大量文本文件时,只提取相关信息并不容易。这既复杂又耗时。出于这个原因,我想向您展示我是如何使用 Regex 从美国职位描述中提取位置的。

文本文件

以下是工作描述的结构。我的目标是从这个文本列中只提取州。

Preview of the job description

Example of one job description

第一步。去掉标点符号和尾随空格

状态有一个尾随空格,它位于一行的末尾,后面没有任何字符。如果不删除它(\n),将会提取出多余的单词。

此外,保持州名缩写为大写字母也很重要(对于加利福尼亚,用‘CA’而不是‘CA’),因为如果你不这样做,它将提取所有包含‘CA’的单词。

# deleting punctuations
description$text<-gsub("[[:punct:][:blank:]]+", " ", description$text)# deleting trailing space
description$text<-gsub("\\n"," ", description$text)

Job description after cleaning

第二步。使用正则表达式

清理后,您可以按空格分割工作描述文本,并找到与州缩写列表(字典)匹配的字符串。完成后,您可以将其分配到 location 列,如下所示。

#creating a location columndescription$location <- NA #using for loop to extract all the states for (i in 1:length(description$text)){ #split the text by space  
split <- strsplit(description$text[i]," ")[[1]] #comparing split with the state abbreviation   
state <- match(split, stateabbreviation$Abbreviation) #if it matches, get the position of each state  
state <- which(!is.na(state)) #extract the state based on the position  
state_split <- split[state] #adding states to the new column   
description$location[i] <- state_split[1]  
}

为了找到职位描述中的州缩写,我使用了自己创建的字典“stateabbreviation”。

和...瞧啊。这是结果。

如果还想提取城市,只需使用相同的代码和' which(!idspnonenote)即可。is.na(位置))-1 '。你用“-1”是因为城市在州的前面。

第三步。可视化

现在你可以把它形象化,以便更好地理解你的发现。

library(ggplot2)state_count<-table(description$location)state_count<-as.data.frame(state_count)ggplot(state_count, aes(x = as.character(state_count$Var1), y = state_count$Freq)) + geom_bar(stat="identity") + theme(axis.text.x = element_text(angle = 60, vjust = )) + labs(y = "Frequency", x= "States")

这是我工作的一部分,以便更好地了解当前的数据科学就业市场。根据这一结果,加利福尼亚州、纽约州和德克萨斯州非常需要精通数据/技术的专业人员。

关于我的文本挖掘项目的更多细节,请随时访问我的 GitHub 页面。感谢您的阅读!

另外,请随意查看我的其他文章:

如何获得关于货币汇率的 Twitter 通知:Web 抓取和自动化

数据块:如何在本地计算机上将数据帧保存为 CSV 文件

使用脸书 Pytorch BigGraph 从知识图中提取知识。

原文:https://towardsdatascience.com/extracting-knowledge-from-knowledge-graphs-e5521e4861a0?source=collection_archive---------6-----------------------

机器学习让我们有能力训练一个模型,它可以将数据行转换成标签,以这样的方式将相似的数据行映射到相似的或相同的标签。

例如,我们正在为电子邮件构建垃圾邮件过滤器。我们有很多电子邮件,其中一些被标记为垃圾邮件,一些被标记为收件箱。我们可以建立一个学习识别垃圾邮件的模型。将被标记为垃圾邮件的消息在某些方面类似于那些已经被标记为垃圾邮件的消息。

相似度的概念对于机器学习来说至关重要。在现实世界中,相似性的概念对于主题来说是非常具体的,它取决于我们的知识。

另一方面,大多数数学模型假定相似性的概念是确定的。通常,我们将数据表示为多维向量,并测量向量之间的距离。

https://www.quora.com/Why-do-we-use-cosine-similarity-on-Word2Vec-instead-of-Euclidean-distance

特征工程是将我们对现实世界物体的知识转换成这种物体的数字表示的过程。我们认为相似的物体被表示为邻近矢量。

例如,我们正在估算房价。我们的经验告诉我们,房子是由卧室的数量、浴室的数量、年龄、面积来决定的。镜头、位置等。位于同一街区、大小和年龄相似的房子,价格应该差不多。我们将我们对住房市场的了解转化为表征房屋的数字,并用它来估计房屋的价格。

不幸的是,如上所述,手动特征工程在我们将知识转化为描述性特征的能力方面存在局限性。

有时我们的知识仅限于相似性原则,而不是使物体相似的确切特征。我们对现实世界的了解往往比简单的表格形式更复杂。这是一个典型的相互联系的概念和关系的图表。

嵌入模型允许我们获取原始数据,并根据我们的原理知识自动将其转换为特征。

Word2Vec

Word2Vec 可能是最著名的嵌入模型,它为单词构建相似性向量。在这种情况下,我们关于世界的知识是以叙事的形式呈现的,表现为文本,文本是一系列的单词。

几十年来,人们努力尝试使用人工定义的特征来表征单词,但成效有限。在有限的情况下,这些解决方案通常不能扩展到全部知识或工作。

当托马斯·米科洛夫和他在谷歌的团队决定建立一个基于众所周知的相似性原理的模型时,一切都改变了。在相似的上下文中使用的单词通常是相似的。在这种情况下,上下文是由位于附近的单词定义的。

Graph representation of the sequence of words.

我们看到的是,牢记这些原则,我们可以通过简单地在预定义的窗口(通常是 5 个单词)内将每个单词与其邻居连接起来,从我们的文本中构建一个图表。

现在我们有了一个基于我们的知识连接成图的真实单词对象(单词)的图。

最简单/复杂的单词表示

我们仍然无法建立任何模型,因为单词不是用形式或向量来表示的。

如果我们需要的只是将单词转换成数字,有一个简单的解决方案。让我们拿起字典,给每个单词指定它在字典中的位置。

比如我有三个词:猫,毛毛虫,小猫。

我的向量表示将如下:猫-[1],毛毛虫-[2]和小猫-[3]。

不幸的是,这不起作用。通过像这样分配数字,我们隐含地引入了单词之间的距离。猫和毛毛虫之间的距离是 1,猫和小猫之间的距离是 2。我们在说,猫更像毛毛虫,而不是小猫,这与我们的知识相矛盾。

替代表示也称为一键编码可以做到这一点:

猫— [1,0,0]

卡特彼勒— [0,1,0]

小猫— [0,0,1]

这个模式认为所有的单词都是相互正交的。我们承认对词语相似性没有先入为主的概念。我们将依靠我们的知识图(如上所述)来构建嵌入,该知识图结合了我们的单词相似性原则。

在现实世界中,字典的大小比 3 大得多。典型的维数从数万到数百万。不仅这些向量不能真正代表我们的相似性概念,而且这些向量非常庞大,不能真正用于实践。

构建单词嵌入

我们的知识图为我们提供了大量的图边,每条边都可以被解释为输入数据作为边的起点,标签作为边的终点。我们正在构建一个模型,它试图使用单词周围的单词作为标签来预测单词。通常有两种方式。我们或者从它的邻居的总和中重建单词向量,或者我们通过尝试从单词中预测邻居来做相反的事情。

https://papers.nips.cc/paper/5021-distributed-representations-of-words-and-phrases-and-their-compositionality.pdf

该模型的细节不在讨论范围内,在许多其他帖子中有详细描述。从根本上说,该团队使用基本的编码器/解码器模型来学习从高维空间(数百万维)到有限维空间(通常为 300 维)的投影,然后再回到高维空间。训练的目标是在压缩过程中保留尽可能多的信息(最小化交叉熵)。

http://mccormickml.com/2016/04/19/word2vec-tutorial-the-skip-gram-model/

这种将低维投影从稀疏正交数据集学习到更密集的低维空间的概念是许多其他嵌入训练模型的基础。

该模型通常在 google crawl、twitter 数据集或维基百科等来源上进行训练。我们正在消费世界知识,并以此为基础构建我们的词汇。

Word2Vec 嵌入的属性

Word2Vec 的重要属性是保留关系和暴露结构等价的能力。

下面的图表显示了国家和首都之间的联系。

或者其他不同的概念。

https://www.tensorflow.org/tutorials/representation/word2vec

本质上,它允许对这样的单词进行代数运算:

国王——男人+女人=王后。

使用单词嵌入

单词嵌入极大地改善了文本分类、命名实体识别、机器翻译等任务。

以下是更多信息:http://www . wild ml . com/2015/11/understanding-convolutionary-neural-networks-for-NLP/

Node2Vec

由 A. Grover 和 J. Leskovec 设计的 Node2Vec 。是模型,它通过扩展 Word2Vec 的思想来分析同质加权图。这篇论文背后的思想是,我们可以通过探索它的周围来表征图节点。我们对世界的理解基于两个原则——同质性和结构等效性。

同质性

类似的节点位于附近。

示例:

  • 社交网络——我们和像我们一样的人联系更加紧密。
  • 商业位置——金融公司、诊所或营销公司似乎通常位于同一条街道上
  • 组织结构——同一个团队的人拥有相似的特质

结构等价

不同的社区共享相同的结构:

  • 组织结构——虽然团队之间的联系可能很弱,但团队的结构(经理、高级成员、新人、初级成员)在团队之间是重复的。

https://arxiv.org/pdf/1607.00653.pdf

为了将这两个原则结合到我们的嵌入中,Node2Vec 论文的作者提出了一种随机行走方法,该方法结合了广度优先采样来捕获同伦性,以及深度优先采样来捕获结构等价性。

正如我们可以看到的,节点(u)充当组(s1,s2,s3,s4)中的集线器,这类似于 s6 是(s7,s5,s8,s9)的集线器。我们通过做 DFS 发现了 BFS 的(s1,s2,s3,s4)社区和(u) (s6)相似性。

我们通过探索周围环境来了解每个节点。这种探索将图形转换成由随机游走产生的大量序列(句子),随机游走结合了 BFS 和 DFS 探索。BFS 和 DFS 混合由图边的权重以及模型的超参数控制。

一旦我们有了完整的序列(句子),我们就可以像应用于文本一样应用 Word2Vec 方法。它产生图节点嵌入,这是基于我们定义的原则以及来自图的知识。

Node2Vec 属性

Node2Vec 表示改进了节点的聚类和分类模型。学习到的嵌入的相似性将有助于像欺诈检测这样的任务。

node2vec 生成的 Les Misérables 共生网络的补充可视化,标签颜色反映了同质性(上图)和结构等价性(下图)。—https://arxiv.org/pdf/1607.00653.pdf

https://arxiv.org/pdf/1607.00653.pdf

Node2Vec 在链路预测方面表现出显著的改进。它能够提高重建图形的能力,其中一些百分比的边被删除。链接预测评估过程将在本文中进一步讨论。

知识图表

下面我们要讨论的是 PYTORCH-BIGGRAPH:一个大规模图嵌入系统论文进一步命名为 PBG 以及相关的论文家族。

知识图是特殊类型的图,它包含已知的实体以及不同类型的边。它代表结构知识。

在知识图中,节点通过不同类型的关系连接。

https://arxiv.org/pdf/1503.00759.pdf

训练的目标是产生代表我们知识的嵌入。一旦我们有了节点的嵌入,应该很容易确定相应的节点在我们的知识图中是否通过特定类型的关系被连接(或者应该被连接)。

不同的模型提出了不同的比较嵌入的方法。最简单的模型使用余弦或矢量积距离来比较嵌入矢量。更复杂的模型在比较之前对向量的元素应用不同的加权方案。加权方案被表示为矩阵,并且特定于关系的类型。作为培训的一部分,我们可以学习加权矩阵。

https://www.sysml.cc/doc/2019/71.pdf

我们需要找到一种方法来测量边之间的相似性得分,并使用该得分来估计这些节点连接的可能性。

知识图的表示

知识图可以表示为邻接张量。为了建立它,我们将有一个正方形矩阵来代表每一种关系。每个矩阵都有与图中节点一样多的列或行。矩阵的值将是 1,这些节点通过这种类型的关系连接,如果不是,则为 0。很明显,这个矩阵会非常大,非常稀疏。

为了学习我们的嵌入,我们需要将每个节点转换成固定大小的向量。让我们讨论“好”嵌入的性质。

好的嵌入表示我们以图边的形式表达的知识。位于“附近”的嵌入向量应该代表更可能连接的节点。基于这一观察,我们将以这样的方式训练我们的模型,即在邻接张量中标记为 1 的连接节点的相似性得分将更高,而在邻接张量中标记为 0 的连接节点的相似性得分将更低。

https://arxiv.org/pdf/1503.00759.pdf

我们正在训练我们的嵌入以 用最小的信息损失从节点嵌入中重建 知识图的边。

负采样

我们的训练方法有点问题。我们正在尝试使用图形数据来区分 1(节点已连接)和 0(节点未连接)。然而,我们实际拥有的唯一数据是连接在一起的节点。这就像学习只看猫来区分猫和狗一样。

负采样是一种通过使用非常简单的观察来扩展我们的数据集并提供更好的训练数据的技术。任何随机选择的节点,不作为我们的图的一部分连接,将代表一个标签为 0 的样本数据。出于训练的目的,PBG 的论文提出读取图的每条边,然后提出一个负样本,其中一个节点被随机选择的节点代替。

对于每个边,我们可以分配一个正的相似性得分和一个负的相似性得分。基于节点嵌入和边关系类型权重来计算正相似性得分。以相同的方式计算负的 相似性得分,但是边的一个节点被破坏并被随机节点替换。

排名损失功能,将在训练中优化。它被构造成在图中所有节点和所有关系类型的正和负相似性得分之间建立可配置的余量。排序损失是节点嵌入和关系特定权重的函数,这将通过找到最小排序损失来学习。

培养

现在,我们拥有了训练嵌入模型所需的一切:

  • 数据—负边缘和正边缘
  • 标签— (1 或 0)
  • 要优化的函数(可以是排序损失、更传统的逻辑回归损失或 word2vec 中使用的交叉熵 softmax 损失)
  • 我们的参数是相似性得分函数的嵌入和权重矩阵。

现在的问题是使用微积分找到参数——嵌入,优化我们的损失函数。

随机梯度下降

随机梯度下降的本质是逐渐调整损失函数的参数,使得损失函数逐渐减小。为此,我们以小批量读取数据,使用每一批来计算损失函数的参数更新,以使其最小化。

有多种方法可以实现随机梯度下降。PBG 论文使用 ADAGrad,这是随机梯度下降的一种风格来寻找参数,使我们的损失函数最小化。强烈推荐这篇博客了解梯度下降的所有味道:http://ruder . io/optimizing-gradient-descent/index . html # adagrad

像 tensorflow 和 pytorch 这样的软件包提供了不同风格的现成实现。

梯度下降的关键要素是多次更新模型参数的过程,直到我们最小化损失函数。在训练结束时,我们期望有嵌入和评分功能,这满足了整合我们的知识的目标。

HogWild —分布式随机梯度下降

使用随机梯度下降进行分布带来了挑战。如果我们同时通过调整参数来最小化损失函数,那么就需要某种锁定机制。在传统的多线程开发中,我们通过悲观或乐观锁定在更新期间锁定数据。锁定减慢了进度,但确保了结果的正确性。

幸运的是,的论文证明了我们不需要一个锁定机制。我们可以简单地批量读取数据,计算参数调整,并将其保存在共享参数空间中,而不考虑正确性。霍格维尔德算法就是这么做的。训练可以是分布式的,每个 HogWild 线程可以更新我们的参数,而不用考虑其他线程。

我推荐这个博客来获得更多关于 HogWild 的信息:https://medium . com/@ Krishna _ SRD/parallel-machine-learning-with-hog wild-f 945 ad 7 e 48 a 4

分布式培训

当图形跨越数十亿个节点和数万亿条边时,很难在一台机器的内存中容纳所有参数。如果我们要等到每一批结束时才开始下一批计算,也会花费很多时间。我们的图表如此之大,以至于能够并行化训练并同时学习参数将是有益的。这个问题被脸书团队解决了,他们发布了 PBG 论文。

节点按实体类型拆分,然后组织成分区:

https://torchbiggraph.readthedocs.io/en/latest/data_model.html

https://torchbiggraph.readthedocs.io/en/latest/data_model.html

https://www.sysml.cc/doc/2019/71.pdf

  1. 节点被划分成 P 个桶,边被划分成 PxP 个桶。基数小的实体类型不必分区。
  2. 训练与以下约束并行进行:

对于每个边缘桶(P1;p2)除了第一个,重要的是边缘桶(P1;)或(;p2)在先前的迭代中被训练。

只要在不相交的分区集上操作,就可以并行训练多个边缘桶。

https://www.sysml.cc/doc/2019/71.pdf

训练在多台机器上并行进行,每台机器上有多个线程。每个线程根据分配的桶和数据批次计算参数更新。锁服务器根据建立的约束分配训练桶。请注意,锁服务器只控制数据批次在 hogwild 线程中的分布,而不控制参数更新。

PBG 嵌入的特征

知识嵌入可以以两种方式使用:

  • 链接预测。链接预测通过找到可能连接或即将连接的节点来帮助填补我们知识中的空白。
    示例:图表代表客户和客户购买的产品。边是采购订单。嵌入可以用来形成下一个购买建议。
  • 学习节点的属性
    嵌入可以用作特征向量,作为各种分类模型的输入。学习过的类可以填补我们关于物体属性知识的空白。

使用 MRR/Hits 评估链接预测 10

这个过程在论文中有所描述——“学习知识库的结构化嵌入”,后来在包括脸书·PBG 在内的许多其他论文中被用作衡量嵌入模型质量的方法。

该算法采用测试边缘的子集,并执行以下操作:

  1. 通过用负采样边替换边的开始或结束来破坏边。
  2. 在部分损坏的数据集上训练模型
  3. 计算测试数据集中边的聚合 MRR 和 Hits10 度量。

平均倒数等级

MRR 或平均倒数排名是对搜索质量的衡量。我们选取一个未损坏的节点,并使用定义为相似性得分的距离来查找“最近的邻居”。我们通过相似性得分对最近的邻居进行排序,并期望被连接的节点出现在排序的顶部。如果节点不在顶部倾斜,MRR 会降低准确度分数。

另一个度量是 Hits10,我们期望被破坏的节点出现在前 10 个最近的邻居中。

https://www.sysml.cc/doc/2019/71.pdf

PBG 的论文表明,在许多数据集上,随着我们将资源分配到训练中,MRR 度量逐渐增加。并行性在一定程度上不会影响排序的质量,但会节省大量时间。

通过简单地探索和可视化图形,可以执行进一步的评估。

https://ai.facebook.com/blog/open-sourcing-pytorch-biggraph-for-faster-embeddings-of-extremely-large-graphs/

上图是从 Freebase 知识图构建的嵌入的 2d 投影。如我们所见,相似的节点被组合在一起。国家、数字、科学期刊、专业人士甚至在精心准备的二维投影上似乎都有集群。

知识图模型的局限性。

如上所述的知识图表代表了我们知识的静态快照。它没有反映出知识是如何积累起来的。在现实世界中,我们通过观察时间模式来学习。虽然可以了解节点 A 和节点 B 之间的相似性,但很难看出节点 A 和节点 C 之间的相似性,就像 3 年前一样。

例如,如果我们观察森林一天,我们会看到两棵大红杉之间的相似之处。然而,如果不对森林进行长期观察,很难理解哪棵树苗会长成一棵大红杉。

理想情况下,我们需要探索在不同时间点构建的一系列知识图,然后构建嵌入,这将包含代际相似性。

来自 Twitter 的患者对药品的看法

原文:https://towardsdatascience.com/extracting-patient-sentiment-for-pharmaceutical-drugs-from-twitter-2315870a0e3c?source=collection_archive---------38-----------------------

Photo by Pixabay on Pexels

制药公司面临的一个常见问题是根据患者的旅程预测患者是否会更换药物。指示寻求转换处方药的患者的信息可以出现在社交媒体帖子中。分析患者在关于处方药的推文中表达的情绪,可能是朝着解决这个问题的正确方向迈出的一步。

在本帖中,我们将提取美国一些最常见处方药的患者情绪。我们将关注在美国开出的前 10 种药物中的 5 种:

  1. 维柯丁:一种用于治疗中度至重度疼痛的药物
  2. 辛伐他汀:一种 HMG-CoA 还原酶抑制剂(他汀类),用于降低中风和心脏病发作的风险
  3. 赖诺普利:一种血管紧张素转换酶(ACE)抑制剂,用于降低高血压和预防糖尿病引起的肾衰竭
  4. 立普妥:一种用于预防冠心病患者中风和心脏病发作的他汀类药物
  5. 二甲双胍:治疗二型糖尿病的药物

首先,你需要申请一个 Twitter 开发者账户:

Source

您的开发人员帐户获得批准后,您需要创建一个 Twitter 应用程序:

Source

申请 Twitter 开发者账户和创建 Twitter 应用程序的步骤在这里列出

为了访问 Twitter API,我们将使用免费的 python 库 tweepy。tweepy 的文档可以在这里找到。

  1. 安装

首先,确保您已经安装了 tweepy。打开命令行并键入:

pip install tweepy

2.导入库

接下来,打开您最喜欢的编辑器,导入 tweepy 和 pandas 库:

import tweepy
import pandas as pd

3.认证

接下来,我们需要我们的消费者密钥和访问令牌:

Source

请注意,该网站建议您保持您的密钥和令牌私有!这里我们定义了一个假的密钥和令牌,但是在创建 Twitter 应用程序时,您应该使用真正的密钥和令牌,如上所示:

consumer_key = '5GBi0dCerYpy2jJtkkU3UwqYtgJpRd' 
consumer_secret = 'Q88B4BDDAX0dCerYy2jJtkkU3UpwqY'
access_token = 'X0dCerYpwi0dCerYpwy2jJtkkU3U'
access_token_secret = 'kly2pwi0dCerYpjJtdCerYkkU3Um'

下一步是创建 OAuthHandler 实例。我们传递上面定义的消费者密钥和访问令牌:

auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)

接下来,我们将 OAuthHandler 实例传递给 API 方法:

api = tweepy.API(auth)
  1. TWITTER API 请求

接下来,我们为我们有兴趣分析的字段初始化列表。现在,我们可以查看推文字符串、用户和推文时间。接下来,我们在一个 tweepy“Cursor”对象上编写一个 for 循环。在“Cursor”对象中,我们传递“api.search”方法,为我们想要搜索的内容设置查询字符串,并设置“count”= 1000,这样我们就不会超过 twitter 的速率限制。在这里,我们将搜索关于维柯丁的推文,维柯丁是一种用于治疗中度至重度疼痛的止痛药。我们还使用“item()”方法将“Cursor”对象转换为 iterable。

为了简化查询,我们可以删除转发,只包含英文推文。为了了解该请求返回的内容,我们还可以打印附加到每个列表的值:

twitter_users = []
tweet_time = []
tweet_string = []
for tweet in tweepy.Cursor(api.search,q='Vicodin', count=1000).items(1000):
            if (not tweet.retweeted) and ('RT @' not in tweet.text):
                if tweet.lang == "en":
                    twitter_users.append(tweet.user.name)
                    tweet_time.append(tweet.created_at)
                    tweet_string.append(tweet.text)
                    print([tweet.user.name,tweet.created_at,tweet.text])

我们也可以看看辛伐他汀的微博:

twitter_users = []
tweet_time = []
tweet_string = []
for tweet in tweepy.Cursor(api.search,q='Simvastatin', count=1000).items(1000):
            if (not tweet.retweeted) and ('RT @' not in tweet.text):
                if tweet.lang == "en":
                    twitter_users.append(tweet.user.name)
                    tweet_time.append(tweet.created_at)
                    tweet_string.append(tweet.text)
                    print([tweet.user.name,tweet.created_at,tweet.text])

为了实现可重用性,我们可以将它封装在一个函数中,该函数将药物关键字作为输入。我们还可以将结果存储在数据帧中并返回值:

def get_related_tweets(key_word):twitter_users = []
    tweet_time = []
    tweet_string = [] 
    for tweet in tweepy.Cursor(api.search,q=key_word, count=1000).items(1000):
            if (not tweet.retweeted) and ('RT @' not in tweet.text):
                if tweet.lang == "en":
                    twitter_users.append(tweet.user.name)
                    tweet_time.append(tweet.created_at)
                    tweet_string.append(tweet.text)
                    print([tweet.user.name,tweet.created_at,tweet.text])
    df = pd.DataFrame({'name':twitter_users, 'time': tweet_time, 'tweet': tweet_string})

    return df

当我们可以用药物名称‘赖诺普利’调用函数时,我们得到:

get_related_tweets('Lisinopril')

对于“立普妥”:

get_related_tweets('Lipitor')

最后是“二甲双胍”:

get_related_tweets('Metformin')

为了获得情感分数,我们需要导入一个名为 textblob 的 python 包。textblob 的文档可以在这里找到。要安装 textblob,请打开命令行并键入:

pip install textblob

下次导入 textblob:

from textblob import TextBlob

我们将使用极性得分作为积极或消极感觉的衡量标准。极性得分是一个从-1 到+1 的浮点数。

例如,如果我们定义一个 textblob 对象并传入句子“我喜欢 Aetna 的健康保险计划”,我们应该得到一个正值的极性得分:

sentiment_score = TextBlob(“I love my health insurance plan with Aetna”).sentiment.polarity
print("Sentiment Polarity Score:", sentiment_score)

让我们给关于“维柯丁”的推文打分:

df = get_related_tweets("Vicodin")
df['sentiment'] = df['tweet'].apply(lambda tweet: TextBlob(tweet).sentiment.polarity)
print(df.head()

我们也可以计算积极和消极情绪的数量:

df_pos = df[df['sentiment'] > 0.0]
df_neg = df[df['sentiment'] < 0.0]
print("Number of Positive Tweets", len(df_pos))
print("Number of Positive Tweets", len(df_neg))

同样,对于代码重用,我们可以将其全部封装在一个函数中:

def get_sentiment(key_word):
    df = get_related_tweets(key_word)
    df['sentiment'] = df['tweet'].apply(lambda tweet: TextBlob(tweet).sentiment.polarity)
    df_pos = df[df['sentiment'] > 0.0]
    df_neg = df[df['sentiment'] < 0.0]
    print("Number of Positive Tweets about {}".format(key_word), len(df_pos))
    print("Number of Negative Tweets about {}".format(key_word), len(df_neg))

如果我们用“立普妥”调用这个函数,我们得到:

get_sentiment(“Lipitor”)

如果我们能以编程方式可视化这些结果,那将会很方便。让我们导入 seaborn 和 matplotlib 并修改我们的 get _ 情操函数:

import seaborn as sns
import matplotlib.pyplot as pltdef get_sentiment(key_word):
    df = get_related_tweets(key_word)
    df['sentiment'] = df['tweet'].apply(lambda tweet: TextBlob(tweet).sentiment.polarity)
    df_pos = df[df['sentiment'] > 0.0]
    df_neg = df[df['sentiment'] < 0.0]
    print("Number of Positive Tweets about {}".format(key_word), len(df_pos))
    print("Number of Negative Tweets about {}".format(key_word), len(df_neg))
    sns.set()
    labels = ['Postive', 'Negative']
    heights = [len(df_pos), len(df_neg)]
    plt.bar(labels, heights, color = 'navy')
    plt.title(key_word)get_sentiment(“Lipitor”)

其他四种药物的结果是:

如您所见,维柯丁、辛伐他汀、二甲双胍和立普妥的积极情绪多于消极情绪,赖诺普利的消极情绪略多于积极情绪。我鼓励读者对其他药物进行同样的分析,看看推特上对该药物的普遍看法。收集几年的数据来观察某些药物的情绪得分是否存在任何时间依赖性(季节性)将是有趣的。也许我会把它留到以后的文章里。

感谢您的阅读。这篇文章的代码可以在 GitHub 上找到。祝好运,机器学习快乐!

注来自《走向数据科学》的编辑: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

从图像中提取感兴趣区域

原文:https://towardsdatascience.com/extracting-regions-of-interest-from-images-dacfd05a41ba?source=collection_archive---------3-----------------------

利用 OpenCV 从图像中高效提取感兴趣区域

欢迎来到本系列的第二篇文章,我们将讨论使用 OpenCV 和 Python 从图像中提取感兴趣区域(ROI)。

回顾一下,在本系列的第一篇文章中,我们介绍了从台球桌的图像中提取球和球台边缘的步骤。我们使用简单的 OpenCV 函数,如 inRange,findContours,boundingRect,minAreaRect,****mineclosing Circle,circle,HoughLines,line 等来实现我们的目标。

对于 OpenCV 的初学者,我建议浏览一下的帖子,以便熟悉上述函数的用法。

在这篇文章中,我们将着眼于一个更复杂的问题,并探索一些我们可以用来获得预期结果的方法。

我们今天的任务是从包含患者大脑活动图快照的图像中提取所需的片段。然后,所提取的片段可以用于许多应用中,例如可以诊断任何健康异常的机器学习模型中。

让我们从输入图像本身开始。这是神经科学领域中使用的医疗仪器生成的典型报告,这些仪器使用传感器来检测来自患者大脑的信号,并将它们显示为彩色图。通常有四幅地图,每幅地图都描绘了某个特征,并一起进行分析以进行诊断(进一步的细节不在当前范围内)。

Our target image for this exercise containing the four brain maps (image source author)

从上面的图像中,我们只想提取对应于四个地图(头部扫描)的区域,其他的都不提取。所以我们走吧。

第一步是检测我们想要提取的片段的边缘。这是一个多步骤的过程,如下所述:

  1. 使用“CVT color()”将 RGB 图像转换为灰度
  2. 通过应用模糊函数“Gaussian blur()”去除灰度图像中的噪声
  3. 最后对模糊图像应用“Canny()”函数以获得边缘

边缘检测过程的输出如下所示:

Edge detection output using Canny algorithm (image source author)

请注意,虽然识别了脑图片段,但是有许多不需要的边缘需要消除,并且一些边缘之间有间隙需要闭合。

用于此目的的一种常用方法是形态变换,它涉及到在图像上使用一系列的膨胀和腐蚀来移除不需要的边缘和闭合间隙。

我们在多次迭代中使用 OpenCV 函数“explain()”和“erode()”来获得如下输出。

Some enhancements in the edges using OpenCV (image source author)

正如你所看到的,边缘现在是完整的,比以前平滑多了。

现在,我们可以使用 OpenCV 函数“find contours()”提取该图像中的轮廓,并仅选择具有以下属性的轮廓:

  1. 几何形状是圆形或椭圆形
  2. 面积高于某个阈值(在本例中,值 7000 很好)。

对于第一部分,我们将使用 OpenCV“bounding rect()”检测每个轮廓的外接矩形,并检查纵横比(高度与宽度之比)是否接近 1。

看起来我们的任务已经完成,但是还需要一点微调。

通常情况下,在一个片段上检测到多个重叠的轮廓,而我们只对一个感兴趣。

使用非最大值抑制解决了这个问题,即我们查看所有重叠的轮廓,并选择具有最大面积的轮廓作为最终候选轮廓。逻辑非常简单,因此我们不需要任何内置的 OpenCV 或 Python 函数。

另一个重要的逻辑是分别识别四个部分,即左上、右上、左下和右下。

这也非常简单,包括识别图像中心坐标以及我们检测到的每个片段的质心。分段轮廓的质心检测需要在轮廓上应用 OpenCV "moments()" 函数,然后使用下面的公式计算中心 X,Y 坐标:
center_x,center _ Y =(int(M[" M10 "]/M[" m00 "]),int(M[" m01 "]/M[" m00 "])

将分段质心坐标与图像中心坐标进行比较,可以让我们将四个分段放置在它们各自的位置。

现在我们已经确定了四个部分,我们需要建立图像蒙版,它将允许我们从原始图像中提取出想要的特征。

我们将使用 OpenCV 函数 "drawContours()" ,使用白色(R,G,B=255,2555,255)和填充厚度(-1)在黑色背景上绘制所有四个分段轮廓。结果如下所示:

Mask for extracting our ROIs (image source author)

在原始图像上应用这个蒙版可以在我们选择的背景(例如黑色或白色)上得到我们想要的片段。

对于黑色背景,我们创建一个黑色画布,然后使用 OpenCV 函数“bitwise _ and()”使用之前获得的蒙版在其上绘制。

Extracted ROIs over a black background (image source author)

对于白色背景,我们首先创建一个白色画布,然后创建一个彩色反转遮罩,如下图所示,使用 OpenCV 函数 "drawContours()" 以黑色(R,G,B = 0,0,0)绘制轮廓,填充厚度为(-1)。

An alternative Inverted mask for ROI extraction (image source author)

然后,我们使用 OpenCV“add()”函数将这个反转的蒙版与之前获得的黑色背景相加,得到相同的结果,但背景为白色。

Extracted ROIs over a white background (image source author)

这篇文章到此结束,在这篇文章中,我们研究了几种方法,使用这些方法可以很容易地从图像中提取感兴趣的区域。

应当注意,在具有不同复杂度的其他图像的情况下,上面使用的方法可以进行修改。然而,上面讨论的基础知识将为解决此类问题所需的任何高级技术奠定基础。

[## 2019 年 10 月 11 位最佳自由职业 OpenCV 开发者——Toptal

自 2013 年 10 月 28 日起成为会员 Ivan 是一名算法开发人员和研究科学家(博士),具有很强的数学…

www.toptal.com](https://www.toptal.com/opencv)

极端深度因子分解机器(xDeepFM)

原文:https://towardsdatascience.com/extreme-deep-factorization-machine-xdeepfm-1ba180a6de78?source=collection_archive---------5-----------------------

推荐系统领域的新热点

Photo by Nathan Dumlao on Unsplash

我们生活在一个被选择宠坏的时代。无论是食物、音乐还是娱乐,我们拥有的选择之多简直令人难以置信。但是由于推荐引擎为这些应用/网站提供了燃料,这些备选方案以排名列表的形式提供给我们。在这篇博客中,我们将讨论一种新的推荐算法,称为极端深度分解机器(xDeepFM)。

本博客组织如下:

  1. 当前生产中的推荐系统
  2. 介绍 xDeepFM
  3. 介绍 CIN(xDeepFM 的核心支柱)
    3.1 CIN 特性
    3.2 CIN 隐层
    3.3 与 RNN 相似
    3.4 与 CNN 相似
    3.5 隐层的池化与串接
    3.6 xDeepFM 的输出
  4. CIN 复杂度
    4.1 空间复杂度
    4.2 时间复杂度

5。使用 deepCTR 库的编码示例

6。参考文献

7。概要

开始吧!!

1.当前推荐系统

当前的推荐格局由基于 FM/DNN 的模型主导。但是也出现了一些融合 FM 和 DNN 系统的混合架构。

1。基于因式分解机(FM)的方法

  • +ve:自动学习组合特征的模式
  • +ve:很好地概括看不见的特征
  • -ve:试图捕获所有导致无用交互学习的特征交互。这可能会引入噪声。
  • 示例:推荐领域最受信任的主力

2。基于深度神经网络(DNN)的方法

  • +ve:学习复杂和选择性的功能交互
  • -ve:功能交互是在元素级别上建模的。一种热编码用于分类变量,以在维度 d 中表示它们。这将被馈送到完全连接的层中。这与基于 FM 的方法形成鲜明对比,基于 FM 的方法在向量级别(用户向量*项目向量)对特征交互进行建模。
  • 例如:神经协同过滤的 DNN 部分(NCF),Youtube 推荐的 DNN

3。DNN +调频(混合)方法

  • +ve:学习低/高阶特征交互
  • 例如:宽深度网络(Youtube)、神经协同过滤(NCF)、深度和交叉网络(DCN)、深度分解机(DeepFM)和极端深度分解机(xDeepFM)

你可以看看这篇文章,了解一下神经协同过滤(NCF)的概况。这是所有混合动力车型中被引用最多的。

[## 神经协同过滤

基于神经网络的增压协同过滤

medium.com](https://medium.com/@abhisheksharma_57055/neural-collaborative-filtering-96cef1009401)

所有混合类别的例子都使用 DNN 来学习隐式的按位特征交互。它们的不同之处在于如何学习高阶特征相互作用。

2.xDeepFM 简介

Figure 1: The architecture of xDeepFM

xDeepFM 由 3 部分组成:

  1. 线性模型(直接在原始输入要素上工作)
  2. 普通 DNN(在密集要素嵌入的基础上工作)
  3. CIN(在密集特征嵌入之上工作)

在这三个国家中,CIN 是 xDeepFM 独有的。

3.介绍【CIN】压缩互动网络

基于 DNN 的系统的问题是它们隐含地学习高阶交互。在 xDeepFM 中,通过压缩交互网络(CIN)学习显式高阶特征交互。

3.1 CIN 的特点

xDeepFM 推荐 CIN 是因为以下好处:

  • 它在向量层次上学习功能交互,而不是在逐位层次上。
  • 它明确地测量高阶特征相互作用。
  • 它的复杂性不会随着相互作用的程度呈指数增长。

3.2 CIN 隐藏层

CIN 通过其隐藏层(图 4 中的每个 x 是一个隐藏层)学习显式特征交互,其定义如下

Equation 1

X(k-1,i) : 第 k 层(k-1 隐层)第 I 个场的嵌入向量
X(0,j) :第 k 层第 j 个场的嵌入向量(基嵌入/原始特征嵌入)
W(k,H,I,j) :维数为 m * H(k-1)的可学习参数

X(k) 中的每一行仅用 W(k,h)来区分。与 DNN 相反,没有激活来转换隐藏层。

3.3 与 RNN 相似

等式 1 类似于基于 RNN 的模型,因为 CIN 的输出取决于最后一个隐藏层和附加输入。

3.4 与 CNN 相似

Figure 2: Outer products along each dimension for feature interactions

m: 原始特征矩阵的行数
H(k): 隐藏层 k 的特征矩阵的行数

Z(k+1)是一个中间张量,它是隐藏层 X(k)和原始特征矩阵 X(0)的外积。Z(k+1)可以看作一种特殊类型的图像,W(k,h)可以看作一种滤波器。

Figure 3: The k-th layer of CIN. It compresses the intermediate tensor from H(K)*m to H(k+1)

当你沿着维度 D(每个嵌入特征的大小)滑动权重矩阵时,你得到一个隐藏向量 X(k+1,I)。

Figure 4: This is the expansion of CIN component of Figure 1

T: 网络的深度

3.5 隐藏层的汇集和连接

隐藏层的结果按行相加,然后在馈送到输出层之前连接在一起。

  1. 每个隐藏层都与输出单元有联系
  2. 总和池应用于每个隐藏层,以创建一个池向量,如下所示:

Equation 2:

Pooling Vector of length H(k) at layer k

隐藏层中的所有池向量在连接到输出单元之前被连接:

Concatenation of pooling vectors of all layers

3.6 xDeepFM 的输出方程

线性、CIN 和 DNN 都是用如下输出等式并行训练的:

Equation 3: Output Function of CIN

适马:s 形函数
a: 原始特征
W,b: 可学参数

基于 DNN 和 CIN 的层可以很好地互相补充,一起学习

1.隐式(DNN)和显式(CIN)特征交互。

2.低阶(DNN)和高阶(两者)特征相互作用。

4.CIN 空间和时间复杂性

4.1 空间复杂度

Space Complexity of CIN

第一项表示输出层的参数数量
第二项表示每层 k
的参数数量,如果我们假设所有隐藏层具有相同数量的特征向量 H。参数数量可以表示为 O(mHH*T)

4.2 时间复杂度

时间复杂度相当于 O(mHD * H * T)。
mhd: 计算 1 行 Z(k+1,H)的成本
H:H 层的特征向量(行数)
T:CIN 的隐藏层数

至此,我们完成了 xdeepfm 的理论。是时候看看它的实际效果了。

5.编码示例

我使用了 deepCTR 库中的 xDeepFM 实现。对于其他一些实现,请查看参考部分。

这里下载样本数据,然后使用以下代码读取数据并将特征分为密集或稀疏。

稀疏特征需要嵌入,而密集特征需要归一化。我们使用 MinMax scaler 进行标准化,而 LabelEncoder 进行特征编码。

This is how sparse_feature_columns[0] look

# creating a dense feat
dense_feature_columns = [DenseFeat(feat, 1) for feat in dense_features]

This is how dense_feature_columns[0] looks like

在下一个代码 blob 中,我们将使用 xDeepFM 进行初始化、编译、训练和预测

6.参考

  1. xDeepFM 论文
  2. 一些现成的 xDeepFM 实现有:-
    1。代码来自论文作者 xDeepFM
    2。来自微软推荐库
    3 的代码。 deepctr 用于以下实施

7.摘要

xDeepFM 是混合架构的一个例子。它将 MF 和 DNN 结合起来,以得到更好的性能。这是通过使用 CIN(压缩交互网络)实现的。CIN 有两种特殊的美德:

  1. 它可以有效地学习有界度特征交互。
  2. 它在向量水平上学习特征交互。

像其他流行的混合方法一样,xDeepFM 将 CIN 和 DNN 结合在一起。
由此可以显式和隐式地学习高阶特征交互。这减少了手动特征工程的需要。

我会敦促你尝试一下微软的库或 xDeepFM 作者的 T2 代码,尝试不同的 xDeepFM 实现。

你也可以看看这篇帖子来简要了解 NCF,这是另一种流行的混合架构。

[## 神经协同过滤

基于神经网络的增压协同过滤

medium.com](https://medium.com/@abhisheksharma_57055/neural-collaborative-filtering-96cef1009401)

请在评论区分享你的想法和主意。

用 LSTM 自动编码器预测极端事件

原文:https://towardsdatascience.com/extreme-event-forecasting-with-lstm-autoencoders-297492485037?source=collection_archive---------10-----------------------

提高预测性能开发强大的神经网络架构

Photo by Nur Afni Setiyaningrum on Unsplash

处理极端事件预测是每个数据科学家经常做的噩梦。环顾四周,我发现了处理这个问题的非常有趣的资源。就我个人而言,我简直爱上了优步研究人员发布的方法。在他们的论文中(这里有两个版本这里有这里有),他们开发了一个 ML 解决方案来预测未来的日常出行需求。他们的方法因其友好、解释清楚和易于实施而吸引了我的注意力。所以我的目的是用 pythonic 语言重现他们的发现。我对这个挑战非常满意,最终,我提高了回归预测的知识。

这篇文章最重要的收获可以总结为:

  • 开发一个稳定的方法来评估和比较 Keras 模型(同时避免权重种子生成器的问题);
  • 实现一个简单聪明的 LSTM 自动编码器用于新功能的创建;
  • 用简单的技巧提高时间序列的预测性能(见上一步);
  • 处理嵌套数据集,即我们的观察数据属于不同实体的问题(例如不同商店/引擎/人员的时间序列等)……从这个意义上说,我们只开发了一个适用于所有人的高性能模型!

但是保留 Kalm,让我们一步一步来。

问题概述

在优步,对已完成行程的准确预测(特别是在特殊活动期间)提供了一系列重要的好处:更有效的司机分配,从而减少乘客的等待时间,预算规划和其他相关任务。

为了实现对驾驶员拼车需求的高精度预测,优步的研究人员开发了一个高性能的时间序列预测模型。他们能够用来自不同地点/城市的大量异类时间序列来拟合(一次性)单一模型。这个过程允许我们提取相关的时间模式。最终,他们能够预测需求,针对不同的地点/城市进行归纳,优于传统的预测方法。

数据集

在这项任务中,优步利用了不同城市间日常出行的内部数据集,包括附加功能;即天气信息和城市级信息。他们的目标是从过去的固定观察窗口预测第二天的需求。

不幸的是,我们没有这种数据,所以作为 Kaggle 的粉丝,我们选择了不错的鳄梨价格数据集。该数据显示了两种不同品种鳄梨的历史价格,以及在多个美国市场的销量。

我们的选择是因为需要一个具有时间依赖性的嵌套数据集:我们有每个美国市场的时间序列,总共 54 个,如果我们考虑每种类型(传统和有机)的一个时间序列,这个数字将增长到 108。优步的研究人员强调这种数据结构的重要性,因为它允许我们的模型发现重要的不可见关系。此外,序列之间的相关性为我们的 LSTM 自动编码器在特征提取过程中带来了优势。

为了建立我们的模型,我们利用了截至 2017 年底的价格时间序列。2018 年的前 2 个月被存储并用作测试集。对于我们的分析,我们还将考虑所有提供的回归变量。观察结果以微弱的频率显示,因此我们的目的是:给定一个固定的过去窗口(4 周)的特征,预测即将到来的微弱价格。

Train (blue) Test (orange) overview of avocado prices

由于没有指数增长和趋势行为,我们不需要调整我们的价格系列。

建模

为了解决我们的预测任务,我们复制了优步提出的新的模型体系结构,它为异质预测提供了单一的模型。如下图所示,该模型首先通过自动特征提取启动网络,训练一个 LSTM 自动编码器,这对大规模捕捉复杂的时间序列动态至关重要。然后将特征向量与新输入连接起来,并馈送给 LSTM 预测器进行预测。

我们的预测工作流程很容易想象:我们有不同市场的每周价格的初始窗口(加上其他功能)。我们开始在它们上面训练我们的 LSTM 自动编码器;接下来,我们移除编码器并将其用作特征创建器。训练用于预测的预测 LSTM 模型所需的第二步也是最后一步。基于真实/现有的回归变量和之前人工生成的特征,我们能够提供下周的鳄梨价格预测。

from Time-series Extreme Event Forecasting with Neural Networks at Uber

我们很容易用 Keras 重新创建这个逻辑。

我们的 LSTM 自动编码器由一个简单的 LSTM 编码器层组成,后面是另一个简单的 LSTM 解码器。在评估期间你会明白辍学的效用,在这一点上他们是无害的,相信我!

inputs_ae = Input(shape=(sequence_length, n_features))
encoded_ae = LSTM(128, return_sequences=True, dropout=0.5)(inputs_ae, training=True)
decoded_ae = LSTM(32, return_sequences=True, dropout=0.5)(encoded_ae, training=True)
out_ae = TimeDistributed(Dense(1))(decoded_ae)
sequence_autoencoder = Model(inputs_ae, out_ae)

我们计算特征提取,并将结果与其他变量连接起来。在这一点上,我做了一点偏离优步的解决方案:他们建议操纵由我们的编码器提取的特征向量,通过集成技术(例如,平均)聚集它们。我决定让他们原创和免费。我做这个选择是因为它允许我在实验中获得更好的结果。

最后,预测模型是另一个简单的基于 LSTM 的神经网络:

inputs = Input(shape=(sequence_length, n_features))
lstm = LSTM(128, return_sequences=True, dropout=0.5)(inputs, training=True)
lstm = LSTM(32, return_sequences=False, dropout=0.5)(lstm, training=True)
dense = Dense(50)(lstm)
out = Dense(1)(dense)

估价

最后,我们几乎准备好看到一些结果,并作出预测。最后的步骤包括创建一个竞争模型和一个用于结果比较的稳健预测方法的结果定义。

就个人而言,评估两种不同程序的最佳方式是尽可能地复制它们,以便只在真正感兴趣的地方引起注意。在这个实现中,我想展示 LSTM Autoencoder 作为时间序列预测相关功能创建工具的能力。从这个意义上来说,为了评估我们方法的优劣,我决定开发一个新的价格预测模型,其结构与我们以前的预测神经网络相同。

它们之间的唯一区别是它们作为输入接收的特征。第一个接收编码器输出加上外部回归量;第二个接收过去的原始价格加上外部回归量。

不确定性估计和结果

时间序列预测在本质上对于感兴趣领域的极端可变性是至关重要的。此外,如果你试图建立一个基于神经网络的模型,你的结果也会受到内部权重初始化的影响。为了克服这个缺点,存在许多不确定性估计的方法:从贝叶斯到基于 bootstrap 理论的方法。

在他们的工作中,优步的研究人员结合了 Bootstrap 和 Bayesian 方法,产生了一个简单、健壮和紧密的不确定性界限,具有良好的覆盖范围和可证明的收敛特性。

这项技术非常简单和实用…间接我们已经实现了它!如下图所示,在前馈过程中,丢弃应用于编码器和预测网络中的所有层。因此,编码器中的随机丢失智能地干扰了嵌入空间中的输入,这说明了潜在的模型错误设定,并通过预测网络进一步传播。

from Time-series Extreme Event Forecasting with Neural Networks at Uber

Pythonic 说,我们只需在我们的神经网络中添加可训练的辍学层,并在预测期间重新激活它们(Keras 用于在预测期间减少辍学)。

对于最终的评估,我们必须迭代调用预测函数并存储结果。我还计算每次交互的预测得分(我选择平均绝对误差)。我们必须设置计算求值的次数(在我们的例子中是 100 次)。有了存储的分数,我们就能够计算 MAE 的平均值、标准偏差和相对不确定性。

我们对仅由 LSTM 预测网络制作的“对手模型”重复同样的程序。在平均分数和计算不确定性后,LSTM 自动编码器+ LSTM 预测器的最终结果比单一的 LSTM 预测器更好。

Performance (MAE) comparison on test data

摘要

在这篇文章中,我复制了一个在优步开发的用于特殊事件预测的端到端神经网络架构。我想强调:LSTM Autoencoder 在特征提取器角色中的力量;这种解决方案的可扩展性可以很好地概括,避免为每个时间序列训练多个模型;为神经网络评估提供稳定且有利的方法的能力。

我还注意到,当您拥有足够数量的共享共同行为的时间序列时,这种解决方案非常适合…这些行为是否立即可见并不重要,Autoencoder 为我们实现了这一点。

查看我的 GITHUB 回购

保持联系: Linkedin

参考文献

[1]对时间序列的深刻而自信的预测:,朱,拉普捷夫

[2]用神经网络进行时间序列极端事件预测:Nikolay Laptev,Jason Yosinski,Li Erran Li,Slawek Smyl

极端罕见事件分类:现实世界数据集的直接解决方案

原文:https://towardsdatascience.com/extreme-rare-event-classification-a-straight-forward-solution-58a20ef56ef5?source=collection_archive---------8-----------------------

用于检测造纸厂罕见事件(断纸)的预测模型。

首先,感谢 Chitta Ranjan 为我们提供了一个造纸厂的真实数据集。这是一个具有挑战性的时间序列数据集,也是预测维护领域的一个常见问题。本文针对该问题提出了一个可行且直截了当的解决方案。我强烈推荐阅读奇塔以前的文章(链接)。

在预测性维护领域,许多公司面临的挑战是根据设备行为(基于状态的维护)在故障发生之前预测故障。目前,维护人员使用的常用工具是 APM(资产性能管理),它基于风险和可靠性技术(例如威布尔曲线)。

总结:这种类型曲线的思想是将概率曲线上的故障分为三个不同的区域:过早故障、随机故障和寿命终止故障。

这是一个简单的想法,可以让我们了解这个领域中重要的特性,比如运行时间(计数器)。

Figure 1 — Weibull curve showing three tipical parts of probabilistic failure [Source: https://www.weibull.com/hotwire/issue14/relbasics14.htm]

预处理

首先,您需要使用以下代码创建问题的框架:

df['y_1'] = df['y'].shift(-1)
df['y_2'] = df['y'].shift(-2)
df = df.loc[df.y==0] #deleting the downtime eventdf['y'] = df.apply(lambda x: 1 if ((x['y_1'] == 1) | (x['y_2'] == 1)) else 0, axis=1)features = df.columns.tolist()# adding delayed info
features.remove('time')
features.remove('y_1')
features.remove('y_2')
features.remove('x61')
target = 'y'
features.remove(target)

一些亮点:由于数据周期(不到一个月)不能证明任何新功能的创建(小时、天、班次、季度、星期几等),所以时间功能被取消了。).另一个被排除的变量是完全冻结的 x61。

少数类约占所有数据的 1.3%。有一个很简单的方法可以稍微改善一下,就是确定活动前 6 分钟和 4 分钟一样好。所以,它将改善 50%的少数阶级,而且可以公平地断言,这不是一种可怕的罪恶:

df['y'] = df.apply(lambda x: 1 if ((x['y_1'] == 1) | (x['y_2'] == 1) | (x['y_3'] == 1) ) else 0, axis=1)

现在称为 count_y 的计数器(运行时间)是为将模型的时间流逝联系起来而创建的特性。

Figure 2 — The counter reset after the failure (y=1)

一类策略的异常检测

异常检测的最简单策略是使用一类算法,如 SVM 或马哈拉诺比斯距离,来了解数据中哪些是正常的,哪些是不正常的。

首先,数据将被分为训练和测试。

#Splitting
from sklearn.model_selection import train_test_splitx_train, x_test, y_train, y_test = train_test_split(df[features],df[target],shuffle=True,
random_state=10)#Scalling
from sklearn.preprocessing import StandardScaler
scale = StandardScaler()x_train = pd.DataFrame(data=scale.fit_transform(x_train),columns=features,index=y_train.index)
x_test = pd.DataFrame(data=scale.transform(x_test),columns=features,index=y_test.index)

数据分离后,SVM 成为候选模型:

from sklearn.svm import OneClassSVM
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrixsvm1 = OneClassSVM(kernel='rbf', gamma='auto',nu=0.05)svm1.fit(x_train_pos[features])y_pred = pd.DataFrame(data=svm1.predict(x_test[features]))
# need to convert to the same standard, 0 == normal and 1 for break
y_pred.loc[y_pred[0]==1] = 0 # normal 
y_pred.loc[y_pred[0]==-1] = 1 #breakprint(classification_report(y_test,y_pred,digits=3))
confusion_matrix(y_test,y_pred)

结果低于基准(F1<0.1)

precision    recall  f1-score   support

           0      0.984     0.947     0.965      4486
          ** 1      0.040     0.123     0.061        81**

   micro avg      0.933     0.933     0.933      4567
   macro avg      0.512     0.535     0.513      4567
weighted avg      0.967     0.933     0.949      4567

Mahalabonis 距离提供了类似的结果:

clf_total = MahalanobisOneclassClassifier(x_train_pos[features], significance_level=0.001)mahalanobis_dist_total = clf_total.predict_proba(x_test_pos[features].values)print(classification_report(y_test,clf_total.predict(x_test[features].values),digits=3))precision    recall  f1-score   support

           0      0.984     0.886     0.932      4486
 **1      0.032     0.210     0.056        81**

   micro avg      0.874     0.874     0.874      4567
   macro avg      0.508     0.548     0.494      4567
weighted avg      0.967     0.874     0.917      4567

使用一类异常检测的小特征组

两种算法都使用所有的特性作为输入进行了基准测试。对于这种类型的算法来说,问题的规模肯定是复杂的。一种可能的策略是将特征分成相似组。

通常,与领域专家进行一次良好的工作会谈会有所帮助。另一种方法是使用 GradientBoost 树(要素重要性)来支持此定义。

Figure 3— Fragment of feature importance from XGBoost used to create the 1-class groups

通过使用前 20 个标签,将它们分成 2 个标签组,并在一个类别方法中计算 Mahalanobis 距离,我们人工创建了 10 个新特征,它们代表了与训练数据的标准操作的距离。

df['maha_dist'] = clf.predict_proba(df[feat_maha].values)
df['maha_dist2'] = clf2.predict_proba(df[feat_maha2].values)
df['maha_dist3'] = clf3.predict_proba(df[feat_maha3].values)
df['maha_dist4'] = clf4.predict_proba(df[feat_maha4].values)
df['maha_dist5'] = clf5.predict_proba(df[feat_maha5].values)
df['maha_dist6'] = clf6.predict_proba(df[feat_maha6].values)
df['maha_dist7'] = clf7.predict_proba(df[feat_maha7].values)
df['maha_dist8'] = clf8.predict_proba(df[feat_maha8].values)
df['maha_dist9'] = clf9.predict_proba(df[feat_maha9].values)
df['maha_dist10'] = clf10.predict_proba(df[feat_maha10].values)

问题框架的新功能:

['x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x9', 'x10', 'x11', 'x12', 'x13', 'x14', 'x15', 'x16', 'x17', 'x18', 'x19', 'x20', 'x21', 'x22', 'x23', 'x24', 'x25', 'x26', 'x27', 'x28', 'x29', 'x30', 'x31', 'x32', 'x33', 'x34', 'x35', 'x36', 'x37', 'x38', 'x39', 'x40', 'x41', 'x42', 'x43', 'x44', 'x45', 'x46', 'x47', 'x48', 'x49', 'x50', 'x51', 'x52', 'x53', 'x54', 'x55', 'x56', 'x57', 'x58', 'x59', 'x60', 'count_y', 'maha_dist', 'maha_dist2', 'maha_dist3', 'maha_dist4', 'maha_dist5', 'maha_dist6', 'maha_dist7', 'maha_dist8', 'maha_dist9', 'maha_dist10']

它将使用 GradientBoostClassifier 作为接收这个新特性的主要分类器(图 4)。

Figure 4 — Simplied model structure

应注意避免使用相同的随机状态混合训练样本和测试样本:

x_train, x_test, y_train, y_test = train_test_split(df[features],df[target],shuffle=True,
random_state=10)gbc = XGBClassifier(n_estimators=1000,subsample=0.9,max_depth=6,random_state=10, max_features=0.9, n_jobs=2)**%**time gbc.fit(x_train,y_train)**Wall time: 1min 4s**

少数类 0.5 的 F1 结果高于问题的基准。

precision    recall  f1-score   support

           0      0.989     1.000     0.994      4486
 **1      0.968     0.370     0.536        81**

    accuracy                          0.989      4567
   macro avg      0.978     0.685     0.765      4567
weighted avg      0.988     0.989     0.986      4567

每个特征的异常检测

一个更简单的策略是使用第一个 XGBoost 特性重要性所指向的前 20 个标签为模型输入中的每个特性创建 AD(异常检测器)。

#top 20 tags
original_features = ['count_y','x51', 'x28','x26', 'x43','x60', 'x8','x54', 'x50','x14', 'x18','x24', 'x53','x23', 'x15','x22', 'x52','x42', 'x21', 'x36', 'x3']features_individual_ad = original_features.copy()
ad_maha = []
for feat in original_features:
    if not feat == 'count_y':
        _model = MahalanobisOneclassClassifier(x_train_pos[[feat]], significance_level=0.01)
        ad_maha.append(_model)
        _ad_name = feat + '_ad'
        df[_ad_name] = _model.predict_proba(df[[feat]].values)
        features_individual_ad.append(_ad_name)

并且结果甚至比成对特征的组合更好。

precision    recall  f1-score   support

           0      0.992     0.998     0.995      4486
 **1      0.865     0.556     0.677        81**

    accuracy                          0.991      4567
   macro avg      0.929     0.777     0.836      4567
weighted avg      0.990     0.991     0.990      4567

结论

使用人工特征(运行时间特征和 Mahalanobis 距离)和良好的 ML 分类器算法(梯度增强)的非常直接的解决方案为该问题提供了高于初始基准的结果(F1>0.1)。对于少数类,算法的最佳拓扑能够实现 0.67 F1。建议在训练和测试之间进行更稳健的分离(交叉验证),以验证该结果,并对模型参数进行微调。

完整的代码可以在 GIT 上找到。

Figure 5 — Featured image : All starts here … [source: pexels]

极端罕见事件分类:使用 Keras 中的 LSTM 估计剩余使用寿命

原文:https://towardsdatascience.com/extreme-rare-event-classification-remaining-useful-life-estimation-using-lstm-in-keras-81c9b5aa15f0?source=collection_archive---------10-----------------------

一种基于回归的估计造纸厂剩余使用寿命(RUL)的方法,用于纸幅断裂预测问题。

这篇文章中提出的观点、见解和工具是由之前发表在 Medium 上的两篇文章激发的。第一篇(链接)由奇塔·兰詹撰写,简要介绍了极端事件分类问题,并使用 LSTM 自动编码器方法来检测异常。通过分析自动编码器重建步骤中的误差,我们可以识别造纸厂中可能的卷筒纸断裂。第二篇(链接)由 Roberto Mansur 撰写,简要概述了基于状态的维护,并展示了威布尔曲线在概率故障分类中的应用。与 Ranjan 的文章不同,后者通过创建基于变量对之间的 Mahalanobis 距离的新特征来进行异常检测。

结合两篇文章中提出的观点,我们可以创建一个模型,根据造纸厂的历史条件来估计其 RUL。为了获得这种估计,使用具有长短期记忆(LSTM)结构的递归神经网络(RNN)作为回归模型。

图 1 通过显示锂离子电池在连续充电和放电循环中的容量曲线,举例说明了 RUL 的使用情况。为了估计该电池的 RUL,有必要在真实数据点上训练模型,并确定电池将在哪个充电和放电循环中产生其最小可接受容量,或者也称为其故障阈值。

Figure 1 — RUL Prediction for Lithium-Ion Batteries Based on Gaussian Processes Mixture [Source: https://journals.plos.org/plosone/article/figure?id=10.1371/journal.pone.0163004.g002]

这个问题用到的源代码和数据集可以在我的 GitHub 上找到(链接)。

概观

断纸是所有垂直造纸行业都会遇到的非常复杂的问题,由于其复杂性,很难找到断纸事件的具体原因。此外,中断纸张生产过程来调查和定位可能出现的问题非常耗时且成本高昂。以这种方式,许多直接处理预测性维护的公司利用 RUL 技术来安排老化部件和系统的维护,并监控设备的健康状况。

围绕极端罕见事件(如网页中断)的问题是不平衡类(中断和工作区),这导致在使用标准监督学习算法时对少数类的预测不准确。因此,为了避免这种情况,我们可以通过创建一个代表每个工作区RUL 的回归计数器,将二元分类问题转化为回归问题。在用先前的特征和新的目标计数器变量训练 LSTM 模型之后,我们可以预测剩余的使用寿命并确定小的故障阈值(取决于所需的响应时间),该阈值用于将每个事件分类为网断裂或不断裂。

逐步实施

预处理

在创建中断事件之前,从数据帧和新的 6 分钟区域中移除中断。此外,为了避免在创建这个新区域时出现问题,所有持续时间少于 6 分钟的工作区都从数据帧中删除。此外,由于缺少针对该问题给出的信息,所有冻结的标签也被从数据集中移除。

target = 'y'
# Creating 3 time shifts to build the region before break
df['y_1'] = df['y'].shift(-1)
df['y_2'] = df['y'].shift(-2)
df['y_3'] = df['y'].shift(-3)# First generated feature
df = count_change(df,'y','count_y')# Creates an ID for each counter reset
df = count_pikes(df,'y','count_id', True)
# ID Value adjustment - Starts at 0
df['count_id'] = df['count_id'] - df['count_id'].min()# Deleting the downtime events and the working zones with less than 6min
df_grouped = df.groupby('count_id').max()[['count_y', target]]
ids = df_grouped.loc[(df_grouped[target] == 0) & (df_grouped['count_y'] >= 3)].index
df = df.loc[df['count_id'].isin(ids)]# Building the region before the break
df['y'] = df.apply(lambda x: 1 if ((x['y_1'] == 1) | (x['y_2'] == 1) | (x['y_3'] == 1)) else 0, axis=1)# Removing target and unused columns
features = df.columns.tolist()
features.remove('time') # Unused column
features.remove('y_1')      # Aux columns
features.remove('y_2')      # ''
features.remove('y_3')      # ''
features.remove('count_id') # ''
features.remove('x61') # Freezed variable
features.remove(target)

特征工程

为了创建包含新目标的列,我们使用了在上一步中创建的辅助列: count_ycount_id。RUL 变量基本上是一个递减计数器,从最大剩余使用寿命值到零,这发生在断裂事件之前。

Figure 2 — Break target variable with the auxiliary columns.

总之,参见图 2,可以得出结论,新的目标 RUL 变量与 count_y 相反。因此,为了反转计数方向,我们找到了每个 count_id 的最大值 count_y ,并创建了一个名为 MAX_RUL 的新辅助列。

max_rul = df.groupby('count_id').max()['count_y'].reset_index()
max_rul.rename(columns={“count_y”:”MAX_RUL”}, inplace=True)

Figure 3 — New dataframe created with the MAX_RUL and the count_id information.

下一步是使用 count_id 在原始数据帧上执行新创建的数据帧的左连接。最后,从 MAX_RUL 中减去 count_y 得到新的目标 RUL 列。

# Left join on the count_id
df = pd.merge(df,max_rul,how='left',on='count_id')df['RUL'] = df['MAX_RUL'] - df['count_y']# Setting the new target
target = 'RUL'

应用所有更改后,数据帧应如下所示:

Figure 4— Dataframe with the RUL variable created as the new target.

拆分数据

对于这个问题,选择了 5 个时间步长,并使用 train_test_split 函数提供的默认测试大小分割数据。应用于数据帧的时移使得在 shuffle 参数设置为 True 的情况下拆分数据成为可能。

from sklearn.model_selection import train_test_splittimesteps = 5# List of shifted dataframes according to the number of timesteps
df_list = [df[features].shift(shift_val) if (shift_val == 0) 
                                else df[features].shift(-shift_val).add_suffix(f'_{shift_val}') 
                                for shift_val in range(0,timesteps)]# Concatenating list
df_concat = pd.concat(df_list, axis=1, sort=False)df_concat = df_concat.iloc[:-timesteps,:]# Default train_test_split - test_size=0.25
x_train, x_test, y_train, y_test = train_test_split(df_concat, df[target].iloc[:-timesteps], random_state=10, shuffle=True)

标准化数据

考虑到在这个问题中使用的递归神经网络(如 LSTM)的尺度敏感性,有必要对数据进行标准化。因此,通过使用 Scikit-Learn 库提供的标准定标器,对数据进行了定标。

from sklearn.preprocessing import StandardScalerscaler = StandardScaler()# Scaling and transforming the array back to the dataframe format
# Training
x_train_lstm = pd.DataFrame(data=scaler.fit_transform(x_train), columns=x_train.columns)# Test
x_test_lstm = pd.DataFrame(data=scaler.transform(x_test), columns=x_test.columns)

使用 LSTM 的回归方法

在这种方法中,我们将使用 Tensorflow 中的 LSTM 和 Keras 层来操作它。考虑到来自少数类的少量样本,使用 LSTM 方法作为不平衡类的分类器太具有挑战性(它使得模型不太专门用于预测中断事件)。也就是说,这个问题可以通过应用剩余使用寿命方法来解决。

因此,定义神经网络架构之前的第一步是格式化数据形状,使其符合 LSTM 的要求:【样本、时间步长、特征】。为此,创建了一个名为到 3D 的新函数。

def to_3D(X, features, timesteps=5):
    '''
        Shapes the dataset so it can fit into 
        LSTM's format requirement.
    '''
    # Creating an empty tridimensional array
    X_trans = np.empty((X.shape[0], timesteps, 0)) # Adjusting the shape of the data
    for feat in features:
        # Regular expressions to filter each feature and
        # drop the NaN values generated from the shift
        df_filtered = X.filter(regex=f'{feat}(_|$)')
        df_filtered=df_filtered.values.reshape(df_filtered.shape[0],
                                               timesteps, 1)
        X_trans = np.append(X_trans, df_filtered, axis=2)

    return X_trans

定义 LSTM 模型架构

这个模型由 5 层组成:2 层 LSTM,2 层辍学,1 层致密。代码和摘要如下所示:

from keras.models import Sequential, load_model
from keras.layers import Dense, Dropout, LSTM
import kerasmodel = Sequential()
model.add(LSTM(input_shape=(timesteps, len(features)), units=15, return_sequences=True))
model.add(Dropout(0.2))
model.add(LSTM(input_shape=(timesteps,len(features)), units=10, return_sequences=False))
model.add(Dropout(0.2))
model.add(Dense(units=1, activation = 'relu'))
model.compile(loss='mean_squared_error', optimizer='adam')
print(model.summary())

Figure 5 — Model architecture summary.

训练 LSTM 回归模型

model_path = 'lstm_model.h5'history = model.fit(to_3D(x_train_lstm, features), 
                    y_train, 
                    epochs=1000, 
                    batch_size= 8, 
                    validation_split=0.2, 
                    verbose=1, 
                    callbacks = [
                        keras.callbacks
                             .EarlyStopping(monitor='val_loss',
                                                     min_delta=0, 
                                                     patience=200,
                                                     verbose=1,
                                                     mode='min'), keras.callbacks
                             .ModelCheckpoint(model_path, 
                                              monitor='val_loss',
                                              save_best_only=True, 
                                              mode='min',
                                              verbose=1)])

评估回归模型

在用均方误差损失训练模型之后,我们获得了以下损失val_loss 曲线:

Figure 6 — Curves representing the validation loss and the loss of mean squared error through epochs.

该模型的 R2 分数为 0.9926,这代表了一个很好的结果。但是,我们不得不考虑到,数据是非常不平衡的,高分几乎完全是由于多数阶级造成的。预测值和实际值的比较如下所示:

Figure 7 — True and predicted RUL values.

将回归问题转化为分类问题

如前所述,通过选择目标的失效阈值,可以将 RUL 回归问题转化为分类问题。在这种情况下,每 2 分钟采集一次数据,因此在休息前选择 10 分钟的间隔(5 个样本)。检查图 8 中的性能。

y_test_class = df.y.reindex(y_test.index)
print(classification_report(y_test_class,rul_pred<=5,digits=3))
confusion_matrix(y_test_class,rul_pred<=5)

Figure 8— Classification report for a 10-minute failure threshold.

Figure 9— Confusion matrix for a 10-minute failure threshold.

故障阈值优化

之前选择了 10 分钟的故障阈值,但是在给定特定指标的情况下,我们如何找到最佳的故障阈值呢?首先,我们需要选择一个分数或指标来优化,对于这个问题,它是 F1 分数,它携带了关于精确度和召回分数的信息。

接下来,创建了一系列不同的故障阈值。我们选取了[1,30]个样本或[2 分钟,60 分钟]的间隔,并计算了每个值的 F1 值。

from sklearn.metrics import f1_scorefail_thresh = np.arange(1,31)f1_scores = np.array([])for ft in fail_thresh:
    f1_scores = np.append(f1_scores, round(f1_score(y_test_class,
                                                   rul_pred<=ft),4))

计算出每个给定阈值的 F1 分数后,我们通过用 f1_scores 数组绘制一个简单的图表,并确定曲线峰值的对应参数,找到了最大值。

Figure 10— Plotting the F1 scores to optimize the failure threshold value.

从图 10 所示的图中,我们可以看到最佳阈值是 3 个样本(或实际中断前 6 分钟)。完整的分类报告和混淆矩阵如下所示:

Figure 11— Classification report for a 6-minute failure threshold.

Figure 12— Confusion matrix for a 6-minute failure threshold.

从图 11 中的分类报告中,我们还可以看到少数民族类别的 f1 分数有所提高,大约为 2.88%。对于更大的数据集,行为可能会发生剧烈变化,优化过程将比针对该问题获得的改进百分比更重要。此外,f1 分数已经从 Chitta 文章的基准(f1 分数0.072)提高了248%,验证了用于 RUL 问题的方法。

使用类别权重的 LSTM 分类

在成功实现 RUL 回归模型之后,我们仍然可以尝试一种分类方法,通过将类权重寻址到 LSTM 来补偿类之间的不平衡。

为了实现分类问题,我们首先需要将中断指示器变量 y 设置为我们的目标,因为它最初是在问题表述中定义的。

用于该问题的 LSTM 拓扑与 RUL 回归方法相同(图 5),但由于其分类性质,现在使用不同的指标来编译模型。

model_class = Sequential()
model_class.add(LSTM(input_shape=(timesteps,len(features)), units=15, return_sequences=True))
model_class.add(Dropout(0.2))
model_class.add(LSTM(input_shape=(timesteps,len(features)), units=10, return_sequences=False))
model_class.add(Dropout(0.2))
model_class.add(Dense(units=1, activation = 'sigmoid'))
model_class.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
print(model.summary())

用于拟合模型的参数如下所示:

model_path = 'lstm_model_classification.h5'
class_weight = {0: 1,
                1: 50} #2% are breaks ...history = model_class.fit(to_3D(x_train_lstm, features),
                          df['y'].reindex(y_train.index),  
                          epochs=1000, 
                          batch_size= 8, 
                          validation_split=0.2, 
                          verbose=1, 
                          class_weight=class_weight,
                          callbacks = [        
                             keras.callbacks
                                  .EarlyStopping(monitor='val_loss',
                                                        min_delta=0,
                                                       patience=200,
                                                          verbose=1,
                                                        mode='min'),                                                                                                               

                            keras.callbacks
                                  .ModelCheckpoint(model_path, 
                                           monitor='val_loss',
                                          save_best_only=True, 
                                                   mode='min',  
                                                   verbose=1)])

在这种情况下,将类权重应用于神经网络会通知模型:类 1 的 1 个实例(中断)表示类 0 的 50 个实例(无中断)。因此,模型将为少数类样本分配较高的值,将损失函数转换为加权平均值,其中样本权重在 class_weight 字典中指定。

评估分类模型

Figure 13- Curves representing the validation loss and the loss for the classification problem.

通过分析少数类的分类报告,我们可以看到 0.544 的出色 f1 得分结果,表明与之前的回归方法相比,模型性能提高了一倍以上。与上一份报告相比,精确度和召回分数也有所提高,这意味着该模型得到了充分优化,并且没有出现性能折衷。

Figure 14 — Classification report for the LSTM model using class weights.

模型之间的比较

为了进行方法比较,选择了四种不同的模型:来自 Chitta 文章的 LSTM 自动编码器模型、Roberto 的成对 Mahalanobis 距离模型、RUL 优化模型以及最后的类权重分类模型。用于比较模型的指标有:精确度、召回率和 f1 分数。

Figure 15 — Evaluating the models with different classification metrics.

我们可以在上面的柱状图中看到,Mahalanobis 距离模型在其他模型中脱颖而出,表现出最高的 f1 分数和精确度。同时,类权重模型在分析召回(敏感度)分值时克服了。

为了更好地理解用于评估模型的分类度量,我推荐阅读下面的文章:(链接)。

结论

Web 中断检测是一个非常复杂的问题,主要是因为类之间的不平衡、特征的质量以及收集的数据量。从这个意义上来说,这两个模型,从这篇文章和之前引用的文章,在分类一个真正的造纸厂的断纸方面做了非常好的工作。

RUL 回归模型仍然可以通过微调参数和收集更多数据来优化。还可以通过为每个类别选择最佳权重来改进类别权重模型。当然,为罕见事件检测选择正确的模型是困难的,并且性能可能会根据问题的性质以及数据的大小和质量而变化。考虑到本文中分析的方法,如果目标是实现高精度,最合适的模型是使用 Mahalanobis 距离的模型,但是如果目标是获得高灵敏度,则类权重模型是最佳选择。

RUL 和重量级模型表现出色,f1 值分别为 0.25 和 0.54。它们不仅可以用于 web 中断预测,还可以用于任何罕见的事件检测问题。

Figure 16 — Image used on the story preview of this article [Source: https://pxhere.com/en/photo/1558069?utm_content=shareClip&utm_medium=referral&utm_source=pxhere]

使用 Keras 中的自动编码器进行极端罕见事件分类

原文:https://towardsdatascience.com/extreme-rare-event-classification-using-autoencoders-in-keras-a565b386f098?source=collection_archive---------1-----------------------

在这篇文章中,我们将学习如何实现一个自动编码器来构建一个罕见事件分类器。我们将在这里使用来自的真实世界罕见事件数据集 [1]。

<了解深度学习,了解更多> >

背景

什么是极端罕见事件?

在一个罕见的问题中,我们有一个不平衡的数据集。也就是说,我们的阳性标记样本比阴性标记样本少。在典型的罕见事件问题中,正面标记的数据约占总数的 5-10%。在一个极端罕见的事件问题中,我们只有不到 1%的正面标记数据。例如,在这里使用的数据集中,它约为 0.6%。

这种极端罕见的事件问题在现实世界中很常见,例如,制造过程中的纸张断裂和机器故障,在线行业中的点击或购买。

对这些罕见事件进行分类相当具有挑战性。最近,深度学习已经相当广泛地用于分类。然而,少量的正标签样本阻碍了深度学习应用。无论数据有多大,深度学习的使用都会受到积极标记样本数量的限制。

为什么我们还要费心去使用深度学习呢?

这是一个合理的问题。为什么我们不应该考虑使用另一种机器学习方法呢?

答案是主观的。我们总是可以采用机器学习的方法。为了使其工作,我们可以从负标签数据中进行欠采样,以得到接近平衡的数据集。由于我们有大约 0.6%的正标签数据,欠采样将导致大约是原始数据大小的 1%的数据集。机器学习方法,例如 SVM 或随机森林,仍然可以在这种规模的数据集上工作。然而,它的准确性会有局限性。我们不会利用剩余的大约 99%的数据中的信息。

如果数据足够,深度学习方法可能更有能力。通过使用不同的架构,它还允许模型改进的灵活性。因此,我们将尝试使用深度学习方法。

在这篇文章中,我们将学习如何使用一个简单的密集层自动编码器来构建一个稀有事件分类器。这篇文章的目的是演示极端罕见事件分类的自动编码器的实现。我们将把探索自动编码器的不同架构和配置的任务留给用户。如果你发现什么有趣的东西,请在评论中分享。

分类自动编码器

用于分类的自动编码器方法类似于异常检测。在异常检测中,我们学习正常过程的模式。任何不遵循这种模式的都被归类为异常。对于罕见事件的二进制分类,我们可以使用类似的使用自动编码器的方法(从这里的 [2】)得到)。

快速修订:什么是自动编码器?

  • 自动编码器由两个模块组成:编码器和解码器。
  • 编码器了解进程的基本特征。这些特征通常尺寸减小。
  • 解码器可以从这些底层特征中重建原始数据。

Figure 1. Illustration of an autoencoder. [Source: Autoencoder by Prof. Seungchul Lee
iSystems Design Lab]

如何使用自动编码器稀有事件分类?

  • 我们将数据分为两部分:正标和负标。
  • 负标记的数据被视为过程的正常状态。正常的状态是当过程无事件时。
  • 我们将忽略带正标签的数据,只对带负标签的数据训练自动编码器。
  • 该自动编码器现在已经学习了正常过程的特征。
  • 一个训练有素的自动编码器将预测来自过程的正常状态的任何新数据(因为它将具有相同的模式或分布)。
  • 因此,重建误差会很小。
  • 然而,如果我们试图从一个罕见的事件中重建一个数据,自动编码器将会很困难。
  • 这将使罕见事件期间的重建误差较高。
  • 我们可以捕捉如此高的重建误差,并将其标记为罕见事件预测。
  • 该过程类似于异常检测方法。

履行

数据和问题

这是一个来自纸浆造纸厂的二进制标签数据。纸张断裂是造纸过程中的严重问题。一个单独的纸页断裂会造成几千美元的损失,并且工厂每天至少会看到一个或多个断裂。这导致每年数百万美元的损失和工作危害。

由于过程的性质,检测中断事件具有挑战性。如[1]中所述,即使断裂减少 5%,也会给工厂带来巨大的利益。

我们拥有的数据包含 15 天内收集的大约 18k 行。列y包含二进制标签,1 表示分页符。其余列是预测值。大约有 124 个阳性标记样品(~0.6%)。

在这里下载数据

代码

导入所需的库。

%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as snsimport pandas as pd
import numpy as np
from pylab import rcParamsimport tensorflow as tf
from keras.models import Model, load_model
from keras.layers import Input, Dense
from keras.callbacks import ModelCheckpoint, TensorBoard
from keras import regularizersfrom sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, precision_recall_curve
from sklearn.metrics import recall_score, classification_report, auc, roc_curve
from sklearn.metrics import precision_recall_fscore_support, f1_scorefrom numpy.random import seed
seed(1)
from tensorflow import set_random_seed
set_random_seed(2)SEED = 123 #used to help randomly select the data points
DATA_SPLIT_PCT = 0.2rcParams['figure.figsize'] = 8, 6
LABELS = ["Normal","Break"]

请注意,我们设置随机种子是为了结果的可重复性。

数据预处理

现在,我们阅读和准备数据。

df = pd.read_csv("data/processminer-rare-event-mts - data.csv")

这个罕见事件问题的目的是在板块断裂发生之前预测它。我们会尽量提前 4 分钟预测课间休息。为了构建这个模型,我们将把标签上移 2 行(对应于 4 分钟)。我们可以这样做df.y=df.y.shift(-2)。然而,在这个问题中,我们希望进行这样的移位:如果第 n 行被正标记,

  • 使 row ( n -2)和( n -1)等于 1。这将帮助分类器学习 4 分钟前的预测。
  • 删除第行第行。因为我们不希望分类器学习预测已经发生的中断。

我们将为这种曲线移动开发以下 UDF。

sign = **lambda** x: (1, -1)[x < 0]

**def** curve_shift(df, shift_by):
    *'''*
 *This function will shift the binary labels in a dataframe.*
 *The curve shift will be with respect to the 1s.* 
 *For example, if shift is -2, the following process*
 *will happen: if row n is labeled as 1, then*
 *- Make row (n+shift_by):(n+shift_by-1) = 1.*
 *- Remove row n.*
 *i.e. the labels will be shifted up to 2 rows up.*

 *Inputs:*
 *df       A pandas dataframe with a binary labeled column.* 
 *This labeled column should be named as 'y'.*
 *shift_by An integer denoting the number of rows to shift.*

 *Output*
 *df       A dataframe with the binary labels shifted by shift.*
 *'''*

    vector = df['y'].copy()
    **for** s **in** range(abs(shift_by)):
        tmp = vector.shift(sign(shift_by))
        tmp = tmp.fillna(0)
        vector += tmp
    labelcol = 'y'
    *# Add vector to the df*
    df.insert(loc=0, column=labelcol+'tmp', value=vector)
    *# Remove the rows with labelcol == 1.*
    df = df.drop(df[df[labelcol] == 1].index)
    *# Drop labelcol and rename the tmp col as labelcol*
    df = df.drop(labelcol, axis=1)
    df = df.rename(columns={labelcol+'tmp': labelcol})
    *# Make the labelcol binary*
    df.loc[df[labelcol] > 0, labelcol] = 1

    **return** df

在继续之前,为了简单起见,我们将删除时间和分类列。

*# Remove time column, and the categorical columns*
df = df.drop(['time', 'x28', 'x61'], axis=1)

现在,我们将数据分为训练集、有效集和测试集。然后,我们将采用只有 0 的数据子集来训练自动编码器。

df_train, df_test = train_test_split(df, test_size=DATA_SPLIT_PCT, random_state=SEED)
df_train, df_valid = train_test_split(df_train, test_size=DATA_SPLIT_PCT, random_state=SEED)df_train_0 = df_train.loc[df['y'] == 0]
df_train_1 = df_train.loc[df['y'] == 1]
df_train_0_x = df_train_0.drop(['y'], axis=1)
df_train_1_x = df_train_1.drop(['y'], axis=1)df_valid_0 = df_valid.loc[df['y'] == 0]
df_valid_1 = df_valid.loc[df['y'] == 1]
df_valid_0_x = df_valid_0.drop(['y'], axis=1)
df_valid_1_x = df_valid_1.drop(['y'], axis=1)df_test_0 = df_test.loc[df['y'] == 0]
df_test_1 = df_test.loc[df['y'] == 1]
df_test_0_x = df_test_0.drop(['y'], axis=1)
df_test_1_x = df_test_1.drop(['y'], axis=1)

标准化

对于自动编码器,通常最好使用标准化数据(转换为高斯数据,均值为 0,方差为 1)。

scaler = StandardScaler().fit(df_train_0_x)
df_train_0_x_rescaled = scaler.transform(df_train_0_x)
df_valid_0_x_rescaled = scaler.transform(df_valid_0_x)
df_valid_x_rescaled = scaler.transform(df_valid.drop(['y'], axis = 1))df_test_0_x_rescaled = scaler.transform(df_test_0_x)
df_test_x_rescaled = scaler.transform(df_test.drop(['y'], axis = 1))

自动编码器分类器

初始化

首先,我们将初始化自动编码器架构。我们正在构建一个简单的自动编码器。应该探索更复杂的架构和其他配置。

nb_epoch = 200
batch_size = 128
input_dim = df_train_0_x_rescaled.shape[1] *#num of predictor variables,* 
encoding_dim = 32
hidden_dim = int(encoding_dim / 2)
learning_rate = 1e-3

input_layer = Input(shape=(input_dim, ))
encoder = Dense(encoding_dim, activation="relu", activity_regularizer=regularizers.l1(learning_rate))(input_layer)
encoder = Dense(hidden_dim, activation="relu")(encoder)
decoder = Dense(hidden_dim, activation="relu")(encoder)
decoder = Dense(encoding_dim, activation="relu")(decoder)
decoder = Dense(input_dim, activation="linear")(decoder)
autoencoder = Model(inputs=input_layer, outputs=decoder)
autoencoder.summary()

培训

我们将训练模型并将其保存在文件中。保存训练好的模型是为将来的分析节省时间的好方法。

autoencoder.compile(metrics=['accuracy'],
                    loss='mean_squared_error',
                    optimizer='adam')cp = ModelCheckpoint(filepath="autoencoder_classifier.h5",
                               save_best_only=True,
                               verbose=0)tb = TensorBoard(log_dir='./logs',
                histogram_freq=0,
                write_graph=True,
                write_images=True)history = autoencoder.fit(df_train_0_x_rescaled, df_train_0_x_rescaled,
                    epochs=nb_epoch,
                    batch_size=batch_size,
                    shuffle=True,
                    validation_data=(df_valid_0_x_rescaled, df_valid_0_x_rescaled),
                    verbose=1,
                    callbacks=[cp, tb]).history

Figure 2. Loss for Autoencoder Training.

分类

在下文中,我们展示了如何将自动编码器重构误差用于罕见事件分类。

如前所述,如果重建误差较高,我们会将其归类为断纸。我们需要确定这方面的门槛。

我们将使用验证集来确定阈值。

valid_x_predictions = autoencoder.predict(df_valid_x_rescaled)
mse = np.mean(np.power(df_valid_x_rescaled - valid_x_predictions, 2), axis=1)
error_df = pd.DataFrame({'Reconstruction_error': mse,
                        'True_class': df_valid['y']})precision_rt, recall_rt, threshold_rt = precision_recall_curve(error_df.True_class, error_df.Reconstruction_error)
plt.plot(threshold_rt, precision_rt[1:], label="Precision",linewidth=5)
plt.plot(threshold_rt, recall_rt[1:], label="Recall",linewidth=5)
plt.title('Precision and recall for different threshold values')
plt.xlabel('Threshold')
plt.ylabel('Precision/Recall')
plt.legend()
plt.show()

Figure 3. A threshold of 0.4 should provide a reasonable trade-off between precision and recall.

现在,我们将对测试数据进行分类。

我们不应该从测试数据中估计分类阈值。这将导致过度拟合。

test_x_predictions = autoencoder.predict(df_test_x_rescaled)
mse = np.mean(np.power(df_test_x_rescaled - test_x_predictions, 2), axis=1)
error_df_test = pd.DataFrame({'Reconstruction_error': mse,
                        'True_class': df_test['y']})
error_df_test = error_df_test.reset_index()threshold_fixed = 0.4
groups = error_df_test.groupby('True_class')fig, ax = plt.subplots()for name, group in groups:
    ax.plot(group.index, group.Reconstruction_error, marker='o', ms=3.5, linestyle='',
            label= "Break" if name == 1 else "Normal")
ax.hlines(threshold_fixed, ax.get_xlim()[0], ax.get_xlim()[1], colors="r", zorder=100, label='Threshold')
ax.legend()
plt.title("Reconstruction error for different classes")
plt.ylabel("Reconstruction error")
plt.xlabel("Data point index")
plt.show();

Figure 4. Using threshold = 0.4 for classification. The orange and blue dots above the threshold line represents the True Positive and False Positive, respectively.

在图 4 中,阈值线上方的橙色和蓝色圆点分别代表真阳性和假阳性。如我们所见,我们有很多误报。为了看得更清楚,我们可以看到一个混淆矩阵。

pred_y = [1 if e > threshold_fixed else 0 for e in error_df.Reconstruction_error.values]conf_matrix = confusion_matrix(error_df.True_class, pred_y)plt.figure(figsize=(12, 12))
sns.heatmap(conf_matrix, xticklabels=LABELS, yticklabels=LABELS, annot=True, fmt="d");
plt.title("Confusion matrix")
plt.ylabel('True class')
plt.xlabel('Predicted class')
plt.show()

Figure 5. Confusion Matrix on the test predictions.

我们可以预测 41 个中断实例中的 8 个。请注意,这些情况包括提前 2 或 4 分钟的预测。这在 20%左右,对于造纸行业来说是一个不错的召回率。假阳性率在 6%左右。对于一个磨坊来说,这并不理想,但也不可怕。

尽管如此,该模型还可以进一步改进,以较小的误报率提高召回率。我们将查看下面的 AUC,然后讨论下一步的改进方法。

ROC 曲线和 AUC

false_pos_rate, true_pos_rate, thresholds = roc_curve(error_df.True_class, error_df.Reconstruction_error)
roc_auc = auc(false_pos_rate, true_pos_rate,)plt.plot(false_pos_rate, true_pos_rate, linewidth=5, label='AUC = %0.3f'% roc_auc)
plt.plot([0,1],[0,1], linewidth=5)plt.xlim([-0.01, 1])
plt.ylim([0, 1.01])
plt.legend(loc='lower right')
plt.title('Receiver operating characteristic curve (ROC)')
plt.ylabel('True Positive Rate')
plt.xlabel('False Positive Rate')
plt.show()

AUC 是 0.69。

Github 库

带注释的全部代码都呈现在这里

[## cran 2367/自动编码器 _ 分类器

稀有事件分类的自动编码器模型。通过创建……为 cran 2367/auto encoder _ classifier 开发做出贡献

github.com](https://github.com/cran2367/autoencoder_classifier/blob/master/autoencoder_classifier.ipynb)

这里有哪些地方可以做得更好?

自动编码器优化

自动编码器是 PCA 的非线性扩展。然而,上面开发的传统自动编码器不遵循 PCA 的原理。在构建正确的自动编码器——使用 PCA 原理进行调整和优化。第一部分第二部分,解释并实现了自动编码器中应包含的 PCA 优化原则。

LSTM 自动编码器

这里讨论的问题是一个(多元)时间序列。然而,在自动编码器模型中,我们没有考虑时间信息/模式。在下一篇文章中,我们将探索 RNN 是否可行。我们将尝试一个 LSTM 自动编码器。

结论

我们研究了来自造纸厂的极端罕见事件二进制标记数据,以构建自动编码器分类器。我们达到了合理的精确度。这里的目的是演示如何使用基本的自动编码器进行罕见事件分类。我们将进一步开发其他方法,包括一个 LSTM 自动编码器,它可以提取时间特征以获得更好的准确性。

关于 LSTM 自动编码器的下一个帖子在这里, LSTM 自动编码器罕见事件分类

推荐后续阅读

参考

  1. Ranjan、m . Mustonen、k . pay nabar 和 k . pour AK(2018 年)。数据集:多元时间序列中的稀有事件分类。T3【arXiv 预印本 arXiv:1809.10717。
  2. https://www . data science . com/blog/fraud-detection-with-tensor flow
  3. Github 回购:https://github.com/cran2367/autoencoder_classifier

免责声明:这篇文章的范围仅限于构建密集层自动编码器并将其用作罕见事件分类器的教程。通过网络调优,从业者有望获得更好的结果。这篇文章的目的是帮助数据科学家实现一个自动编码器。

极度不平衡的数据—欺诈检测

原文:https://towardsdatascience.com/extremely-imbalanced-data-fraud-detection-a5cc989fd897?source=collection_archive---------5-----------------------

欠采样+逻辑回归

为了打击欺诈,我们必须首先发现它。发现欺诈时,您必须考虑:

  • 如果你试图找到所有的欺诈案例,其中一些案例将会被误标。这将导致无辜的人被指控犯有欺诈罪。
  • 如果你试图让无辜的人免于被指控,你会给一些诈骗犯贴上无辜的标签。在这种情况下,你的公司会损失更多的钱。

你的欺诈检测算法不完美是必然的。你会倾向哪边?

再来看这个数据。

这是一个货币交易的数据集。它给出了发送者的 ID、接收者的 ID、被转移的金额以及交易前后发送者和接收者的余额。它还告诉我们哪些样本是欺诈,哪些不是。它是一个生成的数据集。公司不想让你知道他们损失了多少钱,所以这是我们唯一能做的。

让我们加载数据集,看看它是什么样子:

cols = ['step', 'type', 'amount', 'nameOrig', 'oldbalanceOrg', 'newbalanceOrig',
        'nameDest', 'oldbalanceDest', 'newbalanceDest', 'isFraud', 'isFlaggedFraud']
df = pd.read_csv('PS_20174392719_1491204439457_log.csv', header = 0, names = cols)
print('df.shape:', df.shape)
df.head()

我们可以把桌子水平分成不同的组。每个集合都有所有的特征,但不是所有的观察值。

对于训练集,我们可以使用 isFraud 列值来训练我们的模型。对测试集应用该模型将为我们提供每次观察的预测 isFraud 值。

一个简单的方法是尝试:

pd.value_counts(df.isFraud, normalize = True)

另一种方法是使用 isFraud 列的模式:

majority_class = df.isFraud.mode()[0]
y_pred = np.full(shape = df.isFraud.shape, fill_value = majority_class)
accuracy_score(df.isFraud, y_pred)

这给了我们一个 0.998709 的准确度分值,与 value_counts()相同——这是预期的。与 isFraud=1 相比,isFraud=0 有更多的值。我们可以从上面的 value_counts()或下面打印的分类报告中看到这一点:

print(classification_report(df.isFraud, y_pred))

我们可以看到,我们得到了完美的召回和精度,但支持值告诉我们另一个故事。我们有 6354407 个值支持 isFraud=0 的情况,有 8213 个值支持 isFraud=1。

不求准确性,就求 ROC AUC 分吧。这个分数衡量我们的模型区分类别的能力。

roc_auc_score(df.isFraud, y_pred)

这使我们的值为 0.5。ROC AUC 得分的值 1.0 是任何人在任何模型中所能得到的最好值。为什么我们得到了 0.5?这是因为我们可以完美地预测所有 isFraud = 0 的情况,但没有一个 isFraud = 1 的情况。因此,在这两个类别中,我们只能预测 1(这使我们的 ROC AUC 为 0.5)。

为了让我们的模型公平竞争,我们可以对欺诈交易进行过采样,或者对干净交易进行欠采样。我们可以通过使用不平衡学习库来实现这一点。

from imblearn.under_sampling import RandomUnderSamplerX = df.drop(['isFraud', 'type', 'nameOrig', 'nameDest'], axis = 1)
y = df.isFraud
rus = RandomUnderSampler(sampling_strategy=0.8)
X_res, y_res = rus.fit_resample(X, y)
print(X_res.shape, y_res.shape)
print(pd.value_counts(y_res))

RandomUnderSampler 的 sampling_strategy 设置为 0.8。这只是为了展示当我们这样做时会发生什么。它允许我们指定少数类样本与多数类样本的比率。它为我们提供了 18479 行数据,其值计数如下:

让我们看看在重采样并删除这些列后,我们的表是什么样子的:

cols_numeric = ['step', 'amount', 'oldbalanceOrg', 'newbalanceOrig',
                'oldbalanceDest', 'newbalanceDest', 'isFlaggedFraud']
df_rus = pd.DataFrame(X_res, columns = cols_numeric)
df_rus.head()

现在,让我们将数据集分成 3 个部分—训练、验证和测试数据集。我们可以在不同的模型中反复使用验证数据集。一旦我们认为我们得到了最好的模型,我们将使用我们的测试数据集。

我们这样做的原因是,我们的模型不仅应该使用部分训练数据集为我们提供良好的结果,还应该使用我们从未见过的数据提供良好的结果。这就是现实生活中会发生的事情。通过保留只使用一次的测试数据集,我们强迫自己不要过度适应验证数据集。这也是 Kaggle 竞赛所做的。

trainsize/valsize/testsize 显示了应该为训练/验证/测试保留的总数据集的一部分。

from sklearn.model_selection import train_test_splitdef train_validation_test_split(
    X, y, train_size=0.8, val_size=0.1, test_size=0.1, 
    random_state=None, shuffle=True): assert int(train_size + val_size + test_size + 1e-7) == 1 X_train_val, X_test, y_train_val, y_test = train_test_split(
        X, y, test_size=test_size, random_state=random_state, shuffle=shuffle) X_train, X_val, y_train, y_val = train_test_split(
        X_train_val, y_train_val,    test_size=val_size/(train_size+val_size), 
        random_state=random_state, shuffle=shuffle) return X_train, X_val, X_test, y_train, y_val, y_test X_train, X_val, X_test, y_train, y_val, y_test = train_validation_test_split(
    X_res, y_res, train_size=0.8, val_size=0.1, test_size=0.1, random_state=1)
class_weight = {0: 4, 1: 5}
model = LogisticRegression(class_weight=class_weight)
model.fit(X_train, y_train)y_pred = model.predict(X_val)
print(classification_report(y_val, y_pred))
print('accuracy', accuracy_score(y_val, y_pred))
roc_auc_score(y_val, y_pred)

请注意 class_weight 参数。我们把它放在那里是因为欠采样行数在 isFraud=0 时是 10000,在 isFraud=1 时是 8000。我们要称它们的重量,使它们保持平衡。这样做的比例是 4:5,这是这里使用的类权重。

如果我们在 sampling_strategy=0.8 的情况下进行了欠采样,我们将拥有平衡的类,并且不需要 class_weight 参数。如果我们得到一个新的数据集,它有轻微的不平衡参数,我们可以使用带有类权重的 LogisticRegression 来平衡它,而不需要重采样。

现在我们得到了 0.90 的准确度分数,这是一个很好的分数。我们的 ROC AUC 得分也是 0.9。

现在,让我们在测试数据集上尝试我们的模型:

y_pred = model.predict(X_test)
print(classification_report(y_test, y_pred))
print('Accuracy', accuracy_score(y_test, y_pred))
print('ROC AUC score:', roc_auc_score(y_test, y_pred))

又是 0.90 的好成绩。看来 RandomUnderSampler 干得不错。

我们必须将我们的模型应用于完整的(未采样的)数据集。让我们接下来做那件事。

y_pred = model.predict(X)
print(classification_report(y, y_pred))
print('Accuracy:', accuracy_score(y, y_pred))
print('ROC AUC score:', roc_auc_score(y, y_pred))

这个更有意思。准确性和 ROC AUC 得分非常好,isFraud=0 的精确度/召回率/f1 得分也非常好。问题是 isFraud=1 的精度非常非常低,只有 0.01。因为 f1 分数是精确度和召回率的加权平均值,所以它也很低,为 0.02。也许我们没有足够的数据来做好逻辑回归。或者我们应该过采样而不是欠采样。

我们将继续使用这个数据集,在以后的博客文章中应用许多技术。非常感谢 Lambda 学校的讲师 Ryan Herr 提供了 train_validation_test_split()函数。

数据科学的寓言——安斯科姆的四重奏

原文:https://towardsdatascience.com/fables-of-data-science-anscombes-quartet-2c2e1a07fbe6?source=collection_archive---------22-----------------------

寓言是一个简短的故事,它可以给人一个教训或者传达一个寓意。在这里,我们探索安斯库姆的四重奏,看看它警告我们什么样的恐怖。

从前..

..在一个很远很远的地方,曾经住着一个叫弗朗西斯·约翰“弗兰克”安斯科姆的人。他是一位非常有名的统计学家。

他四处游荡,会见了许多同行,并坐在城堡里训练年轻的学徒学习神圣的统计学艺术。他对人们说得越多,训练得越多,他就看到了一种危险的趋势。人们倾向于忽视可视化,而偏爱汇总统计。大家都说绘制数据太费劲了。

A worried Frank is a Red Frank

这很危险,弗兰克非常担心。这场瘟疫传播得很快,他没有时间去对抗它。他必须做点什么,做点什么来阻止这一切。

所以他跑去和长老议会谈话,但是每个人都嘲笑他。

“当你有了数据的意思,
你还想考察什么?
标准差只是崇高,
绘图是浪费时间!”PoorPoetrix 大师

弗兰克从来没有预料到议会也遭到了毒害,他不得不睁开眼睛。

所以他旅行了,向北旅行到永远是冬天的地方,到达霜封山的顶峰。他坐在那里,陷入沉思。5 分钟后,他意识到外面的山里冷得要命,他应该多带些羊毛衣服。

他冷得直哆嗦,四处寻找一个洞穴安顿下来,幸运的是他找到了一个。

弗兰克在那里冥想,他深度调解,尽管有人声称他只是睡着了。

在他的梦里,他看到了数字,数字和更多的数字,就在那时,他意识到数字是解决问题的关键。他日复一日地琢磨这些数字,终于解开了它们的秘密。

他叫来一只鸟,并通过他送给委员会一张羊皮纸,上面有 4 组 11 个数据点,并请求委员会按照他的遗愿来绘制这些点:

.

.

+-------+--------+-------+-------+-------+-------+-------+------+
|   **    I        |       II      |      III      |      IV  **    |
+-------+--------+-------+-------+-------+-------+-------+------+
| x     | y      | x     | y     | x     | y     | x     | y    | +-------+--------+-------+-------+-------+-------+-------+------+
| 10.0  | 8.04   | 10.0  | 9.14  | 10.0  | 7.46  | 8.0   | 6.58 |
| 8.0   | 6.95   | 8.0   | 8.14  | 8.0   | 6.77  | 8.0   | 5.76 |
| 13.0  | 7.58   | 13.0  | 8.74  | 13.0  | 12.74 | 8.0   | 7.71 |
| 9.0   | 8.81   | 9.0   | 8.77  | 9.0   | 7.11  | 8.0   | 8.84 |
| 11.0  | 8.33   | 11.0  | 9.26  | 11.0  | 7.81  | 8.0   | 8.47 |
| 14.0  | 9.96   | 14.0  | 8.10  | 14.0  | 8.84  | 8.0   | 7.04 |
| 6.0   | 7.24   | 6.0   | 6.13  | 6.0   | 6.08  | 8.0   | 5.25 |
| 4.0   | 4.26   | 4.0   | 3.10  | 4.0   | 5.39  | 19.0  |12.50 |
| 12.0  | 10.84  | 12.0  | 9.13  | 12.0  | 8.15  | 8.0   | 5.56 |
| 7.0   | 4.82   | 7.0   | 7.26  | 7.0   | 6.42  | 8.0   | 7.91 |
| 5.0   | 5.68   | 5.0   | 4.74  | 5.0   | 5.73  | 8.0   | 6.89 |
+-------+--------+-------+-------+-------+-------+-------+------+

按照他们的习惯,委员会只用描述性统计数据对他们进行分析,他们得到了完全相同的结果,他们想知道老傻瓜弗兰克又在搞什么名堂:

 **Summary**
+-----+---------+-------+---------+-------+----------+
| Set | mean(X) | sd(X) | mean(Y) | sd(Y) | cor(X,Y) |
+-----+---------+-------+---------+-------+----------+
|   1 |       9 |  3.32 |     7.5 |  2.03 |    0.816 |
|   2 |       9 |  3.32 |     7.5 |  2.03 |    0.816 |
|   3 |       9 |  3.32 |     7.5 |  2.03 |    0.816 |
|   4 |       9 |  3.32 |     7.5 |  2.03 |    0.817 |
+-----+---------+-------+---------+-------+----------+

但是 PoorPoetrix 大师决定尊重他朋友的请求,就在那时,当他绘制数据集时,奇迹出现了:

Anscombe’s quartet — Plot

他发现图表完全不同,尽管摘要完全相似

  • 第一个散点图(左上)似乎是一个简单的线性关系,对应于两个相关变量,并遵循正态假设。
  • 第二张图(右上)不是正态分布的;虽然这两个变量之间的关系很明显,但它不是线性的,皮尔逊相关系数也不相关。更一般的回归和相应的决定系数会更合适。
  • 在第三张图中(左下方),分布是线性的,但应该有不同的回归线(稳健回归将被要求)。计算的回归被一个异常值抵消,该异常值施加足够的影响以将相关系数从 1 降低到 0.816。
  • 最后,第四个图(右下)显示了一个例子,其中一个高杠杆点足以产生高相关系数,即使其他数据点并不表明变量之间的任何关系。

PoorPoetrix 大师意识到了委员会的愚蠢并改正了它们,这个数据集被称为 Anscombe 的四重奏。

弗兰克拯救了世界,作为一个快乐的老人在山洞里度过了他的最后几天。

.

.

这个故事的寓意

人们普遍引用 PoorPoetrix 大师用这些不朽的话总结了这一事件:

为了得到你可以信任的分析,
绘制你的数据,你必须绘制。PoorPoetrix 大师

参考

  1. https://en.wikipedia.org/wiki/Anscombe%27s_quartet
  2. https://www . r-bloggers . com/using-and-abuse-data-visualization-anscombes-quartet-and-checking-bonferroni/
  3. gif 图片取自https://giphy.com/

人脸对齐:深度多任务学习

原文:https://towardsdatascience.com/face-allignment-deep-multi-task-learning-203f46a22106?source=collection_archive---------11-----------------------

1。简介

面部关键点预测:给定一张面部图片,预测各种面部特征的位置。

这个问题属于计算机视觉的范畴,并伴随着它自身的挑战。

在这方面已经做了很多工作。在本文中,我们将深入研究这篇 论文 提出的独特方法,并从头实现它,全部使用 keras。

Deep Multi-task learning

2。为什么我们需要解决它?它的应用是什么?

你有没有用过 Snapchat,专门试用过他们的图像滤镜?它是如何变得如此神奇的?它是如何如此准确地用假胡子代替你的胡子的?

要做到这一点,首先它需要识别你脸上与胡子相对应的部分。然后切掉一部分(当然是内部的)并用人造的替换掉。

这就是面部关键点检测发挥作用的地方。识别面部的不同部分。

这只是一个具体的应用,还有很多这样的应用。查看这篇文章了解更多信息。

3.数据概述

这个项目的数据集由论文作者自己提供,可以在 这里 找到。

数据总共有 12295 幅图像,其中 10000 幅是训练图像,2295 幅是测试图像。

数据还附带了两个 txt 文件:training.txttesting.txt。这两个文件保存了关于图像路径、面部特征的坐标位置和 4 个其他面部属性的信息:

第一属性:性别【男/女】

第二属性:微笑/不微笑

第三属性:戴眼镜/不戴眼镜

第四属性:姿态变化

3.1 加载和清理数据

让我们加载 training.txt 文件,并尝试理解和分析数据。当你使用pandas read_csv函数读取 training.txt 文件时,使用空格作为分隔符,它不会被正确加载,这是因为每行的开头都有空格。所以,我们需要把它去掉。

Training.txt file

下面的代码将完全做到这一点。

f = open('training.txt','r')
f2 = open('training_new.txt','w')
for i,line in enumerate(f.readlines()):
    if i==0:
        continue
    line = line.strip()

    f2.write(line)
    f2.write('\n')
f2.close()
f.close()

现在,我们将在项目中使用这个新创建的文件training_new.txt。对于testing.txt文件也是如此。

读取已清理的training.txt文件。

names = ['Path']+list('BCDEFGHIJK')+['Gender','Smile','Glasses','Pose']train = pd.read_csv('training_new.txt',sep=' ',header=None,names=names) train['Path'] = train['Path'].str.replace('\\','/')

下面是培训文件中每个属性的含义。

  • 路径:图像的路径(绝对路径)
  • b:右眼中心的 x 坐标
  • c:左眼中心的 x 坐标
  • 鼻中心的 D: x 坐标
  • e:嘴部最右侧点的 x 坐标
  • f:嘴部最左侧点的 x 坐标
  • 右眼中心的 G: y 坐标
  • 左眼中心的 H: y 坐标
  • 鼻中心的 I: y 坐标
  • 嘴部最右端点的 y 坐标
  • 嘴巴最左边的 y 坐标
  • 性别:此人是男是女,1:男,2:女
  • 微笑:不管对方是否微笑,1:微笑,2:不微笑
  • 眼镜:这个人是否戴眼镜,1:戴眼镜,2:不戴眼镜
  • 姿势:【姿势估计】,5 类。

3.2 可视化数据

现在,让我们想象一些带有面部关键点的图像。

代码:

#visualising the dataset
images = []
all_x = []
all_y= []
random_ints = np.random.randint(low=1,high=8000,size=(9,))
for i in random_ints:
    img = cv2.imread(train['Path'].iloc[i])
    x_pts = train[list('BCDEF')].iloc[i].values.tolist()
    y_pts = train[list('GHIJK')].iloc[i].values.tolist()
    all_x.append(x_pts)
    all_y.append(y_pts)
    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    images.append(img)fig,axs = plt.subplots(nrows=3,ncols=3,figsize=(14,10))k =0
for i in range(0,3):
    for j in range(0,3):
        axs[i,j].imshow(images[k])
        axs[i,j].scatter(all_x[k],all_y[k])
        k += 1plt.show()

4。深潜

现在,我们知道面部关键点预测是怎么回事了。让我们深入了解一下它的技术细节。

接受图像作为输入,并给出面部特征的坐标。

这是一个回归问题,因为它预测连续值,即面部标志的坐标。

Face Alignment, Magic box?

盒子里有什么神奇的东西能做这些?

让我们更深入地了解它。

到目前为止,有两种方法可以解决这个问题,一种是普通的计算机视觉技术(如 viola 和 jones 的人脸包围盒预测),另一种是基于深度学习的,特别是基于卷积神经网络的。

但是这个卷积神经网络到底是个什么鬼?

简单地说,它是用来从图像中提取和检测有意义信息的技术。如果你有兴趣了解更多,请点击这里的

对于这个问题,我们将采取第二条路线,即基于深度学习。

5.文献综述****

不同的研究人员在这个领域做了大量的工作。围绕这个问题的大部分工作把它作为一个单一的任务问题,他们试图单独解决这个问题。但是 这篇研究论文 提出了一个有趣的想法,那就是,他们把它作为一个深层的多任务问题。

什么是多任务问题?

多任务问题:与其只解决一个主要问题,不如一起解决相关的多个问题。

不要只解决面部标志检测问题,让我们也解决相关的辅助问题,比如:图像中的人是否相似,这个人的性别,等等..

但是为什么要一起解决多个任务呢?
上述论文的作者注意到关于面部标志检测(主要任务)的一个非常关键的细节,即面部标志的位置高度依赖于人是否在微笑、图像中人的姿势以及其他支持的任务。因此,他们引入了深度多任务学习的概念,并发现它对于这个问题是准确的。

6.履行

如果你已经熟悉深度学习,到现在,你应该知道这是一个多输出问题,因为我们试图同时解决这个多任务。由于我们将使用 keras 来实现,所以多输出模型可以通过功能 API 来实现,而不是顺序 API。****

根据数据,我们手头有 5 个任务,其中面部对齐是主要的一个。因此,我们将使用多输出模型来训练这 5 项任务的模型。

我们将用不同的辅助任务来训练主任务(人脸对齐),以评估深度多任务学习的有效性。

第一个模型:面部对齐+所有其他辅助任务(4)

第二种模式:脸部对齐+性别+微笑+眼镜

第三种模型:人脸对齐+姿态估计

第四种模式:仅面部对齐

网络架构

我们将使用四个卷积层、三个最大池层、一个密集层,以及用于所有任务的独立输出层。除了图像的输入形状之外,网络架构与本文作者实现的网络架构相同。

Network architecture. Source

6.1 第一个模型的实施

代码如下:

inp = Input(shape=(160,160,3))#1st convolution pair
conv1 = Conv2D(16,kernel_size=(5,5), activation='relu')(inp)
mx1 = MaxPooling2D(pool_size=(2,2))(conv1)#2nd convolution pair
conv2 = Conv2D(48,kernel_size=(3,3), activation='relu')(mx1)
mx2 = MaxPooling2D(pool_size=(2,2))(conv2)#3rd convolution pair
conv3 = Conv2D(64,kernel_size=(3,3), activation='relu')(mx2)
mx3 = MaxPooling2D(pool_size=(2,2))(conv3)#4th convolution pair
conv4 = Conv2D(64,kernel_size=(2,2), activation='relu')(mx3)flt = Flatten()(conv4)dense = Dense(100,activation='relu')(flt)reg_op = Dense(10,activation='linear',name='key_point')(dense)gndr_op = Dense(2,activation='sigmoid',name='gender')(dense)smile_op = Dense(2,activation='sigmoid',name='smile')(dense)glasses_op = Dense(2,activation='sigmoid',name='glasses')(dense)pose_op = Dense(5,activation='softmax',name='pose')(dense)model = Model(inp,[reg_op,gndr_op,smile_op,glasses_op,pose_op])model.summary()

这将打印出以下输出:

现在,下一步是提及我们将在 keras 中为每个输出使用的损失函数。这很容易弄清楚。我们将对面部关键点使用均方误差(MSE)** ,对性别输出、微笑输出和眼镜输出使用二元交叉熵,对姿势输出使用分类交叉熵。**

loss_dic = {'key_point':'mse','gender':'binary_crossentropy','smile':'binary_crossentropy', 'glasses':'binary_crossentropy' , 'pose':'categorical_crossentropy'}

在内部,总损失将是所有单个损失的总和。现在,我们也可以在 keras 中为每个损失函数显式设置权重,得到的损失将是所有单个损失的加权和。

由于主要任务是关键点检测,因此我们将给予更多的权重。

loss_weights = {'key_point':7,'gender':2,'smile':4,'glasses':1,'pose':3}

每项任务的衡量标准是:

metrics = {'key_point':'mse','gender':['binary_crossentropy','acc'],'smile':['binary_crossentropy','acc'], 'glasses':['binary_crossentropy','acc'] , 'pose':['categorical_crossentropy','acc']}

一切就绪。让我们训练网络。

epochs = 35
bs = 64H = model.fit(train_images,[train_keypoint_op,]+train_categorical_ops, epochs = epochs, batch_size=bs, validation_data=(val_images,[val_keypoint_op,]+val_categorical_ops),callbacks=[TrainValTensorBoard(log_dir='./log',write_graph=False)])

让我们评价一下 model 的性能。

train_pred = model.predict(train_images)
val_pred = model.predict(val_images)print('MSE on train data: ', mean_squared_error(train_keypoint_op,train_pred[0]))
print('MSE on validation data: ', mean_squared_error(val_keypoint_op,val_pred[0]))

上面的代码片段给出了以下输出:

MSE on train data:  2.0609966325423565
MSE on validation data:  29.55315040683187

可视化验证集上的结果。

Output Result: Model 1

它仅用 35 个纪元和相当简单的模型架构就能很好地工作。

6.2 第二种模式的实施

这一个的代码如下:

inp = Input(shape=(160,160,3))#1st convolution pair
conv1 = Conv2D(16,kernel_size=(5,5), activation='relu')(inp)
mx1 = MaxPooling2D(pool_size=(2,2))(conv1)#2nd convolution pair
conv2 = Conv2D(48,kernel_size=(3,3), activation='relu')(mx1)
mx2 = MaxPooling2D(pool_size=(2,2))(conv2)#3rd convolution pair
conv3 = Conv2D(64,kernel_size=(3,3), activation='relu')(mx2)
mx3 = MaxPooling2D(pool_size=(2,2))(conv3)#4th convolution pair
conv4 = Conv2D(64,kernel_size=(2,2), activation='relu')(mx3)flt = Flatten()(conv4)dense = Dense(100,activation='relu')(flt)reg_op = Dense(10,activation='linear',name='key_point')(dense)gndr_op = Dense(2,activation='sigmoid',name='gender')(dense)smile_op = Dense(2,activation='sigmoid',name='smile')(dense)glasses_op = Dense(2,activation='sigmoid',name='glasses')(dense)model = Model(inp,[reg_op,gndr_op,smile_op,glasses_op])model.summary()

编译模型。

loss_dic ={'key_point':'mse','gender':'binary_crossentropy','smile':'binary_crossentropy', 'glasses':'binary_crossentropy' }loss_weights = {'key_point':2,'gender':1,'smile':4,'glasses':1}metrics = {'key_point':'mse','gender':['binary_crossentropy','acc'],'smile':['binary_crossentropy','acc'], 'glasses':['binary_crossentropy','acc'] }model.compile(optimizer='adam',loss=loss_dic,loss_weights=loss_weights,metrics=metrics)

一切就绪。让我们训练网络。

H = model.fit(train_images, [train_keypoint_op,]+train_categorical_ops[:-1], epochs = epochs, batch_size=bs, validation_data=(val_images,[val_keypoint_op,]+val_categorical_ops[:-1]),callbacks=[TrainValTensorBoard(log_dir='./log3',write_graph=False)])

让我们评价一下 model 的性能。

train_pred = model.predict(train_images)
val_pred = model.predict(val_images)print('MSE on train data: ', mean_squared_error(train_keypoint_op,train_pred[0]))
print('MSE on validation data: ', mean_squared_error(val_keypoint_op,val_pred[0]))

上面的代码片段给出了以下输出:

MSE on train data:  2.9205250961752722
MSE on validation data:  35.072992153148434

可视化验证集上的结果。

Output Result: Model 2

6.3 第三种模式的实施

这一个的代码如下:

inp = Input(shape=(160,160,3))#1st convolution pair
conv1 = Conv2D(16,kernel_size=(5,5), activation='relu')(inp)
mx1 = MaxPooling2D(pool_size=(2,2))(conv1)#2nd convolution pair
conv2 = Conv2D(48,kernel_size=(3,3), activation='relu')(mx1)
mx2 = MaxPooling2D(pool_size=(2,2))(conv2)#3rd convolution pair
conv3 = Conv2D(64,kernel_size=(3,3), activation='relu')(mx2)
mx3 = MaxPooling2D(pool_size=(2,2))(conv3)#4th convolution pair
conv4 = Conv2D(64,kernel_size=(2,2), activation='relu')(mx3)flt = Flatten()(conv4)dense = Dense(100,activation='relu')(flt)reg_op = Dense(10,activation='linear',name='key_point')(dense)pose_op = Dense(5,activation='softmax',name='pose')(dense)model = Model(inp,[reg_op,pose_op])model.summary()

编译模型。

loss_dic = {'key_point':'mse','pose':'categorical_crossentropy'}loss_weights = {'key_point':4,'pose':11}metrics = {'key_point':'mse', 'pose':['categorical_crossentropy','acc']}model.compile(optimizer='adam',loss=loss_dic,loss_weights=loss_weights,metrics=metrics)

一切就绪。让我们训练网络。

H = model.fit(train_images, [train_keypoint_op,train_categorical_ops[-1]], epochs = epochs, batch_size=bs, validation_data=(val_images,[val_keypoint_op,val_categorical_ops[-1]]),callbacks=[TrainValTensorBoard(log_dir='./log4',write_graph=False)])

让我们评价一下 model 的性能。

train_pred = model.predict(train_images)
val_pred = model.predict(val_images)print('MSE on train data: ', mean_squared_error(train_keypoint_op,train_pred[0]))
print('MSE on validation data: ', mean_squared_error(val_keypoint_op,val_pred[0]))

上面的代码片段给出了以下输出:

MSE on train data:  2.825882283863525
MSE on validation data:  31.41507419233826

可视化验证集上的结果。

Output Result: Model 3

6.4 第四种模式的实施

这一个的代码如下:

inp = Input(shape=(160,160,3))#1st convolution pair
conv1 = Conv2D(16,kernel_size=(5,5), activation='relu')(inp)
mx1 = MaxPooling2D(pool_size=(2,2))(conv1)#2nd convolution pair
conv2 = Conv2D(48,kernel_size=(3,3), activation='relu')(mx1)
mx2 = MaxPooling2D(pool_size=(2,2))(conv2)#3rd convolution pair
conv3 = Conv2D(64,kernel_size=(3,3), activation='relu')(mx2)
mx3 = MaxPooling2D(pool_size=(2,2))(conv3)#4th convolution pair
conv4 = Conv2D(64,kernel_size=(2,2), activation='relu')(mx3)flt = Flatten()(conv4)dense = Dense(100,activation='relu')(flt)reg_op = Dense(10,activation='linear',name='key_point')(dense)model = Model(inp,reg_op)model.summary()

编译模型。

loss_dic = {'key_point':'mse'}metrics = {'key_point':['mse','mae']}model.compile(optimizer='adam',loss=loss_dic,metrics=metrics)

一切就绪。让我们训练网络。

H = model.fit(train_images, train_keypoint_op, epochs = epochs, batch_size=bs, validation_data=(val_images,val_keypoint_op),callbacks=[TrainValTensorBoard(log_dir='./log5',write_graph=False)])

让我们评价一下 model 的性能。

train_pred = model.predict(train_images)
val_pred = model.predict(val_images)print('MSE on train data: ', mean_squared_error(train_keypoint_op,train_pred ))
print('MSE on validation data: ', mean_squared_error(val_keypoint_op,val_pred ))

上面的代码片段给出了以下输出:

MSE on train data:  2.822843715225789
MSE on validation data:  30.50257287238015

可视化验证集上的结果。

Output Result: Model 4

结论

根据上面的实验,很容易得出结论,多任务学习比单独解决这个问题更有效。

有时,解决多任务问题比单独解决问题更有帮助,但请注意,如果主问题依赖于辅助问题,则只解决辅助问题。

这个项目的完整源代码可以在 这里找到

希望,你喜欢这篇文章,如果你从中学到了什么新的东西,那么你可以通过与他人分享和关注我来展示你的爱..花了这么多时间来写这么全面的博文,希望我的努力能帮助你们中的一些人理解这个案例研究的细节,以便你们也能在自己的地方实施它。

欢迎在 LinkedIn 上与我联系,在 TwitterQuora 上关注我。

使用 OpenCV 和 Python 在 2 分钟内完成人脸检测

原文:https://towardsdatascience.com/face-detection-in-2-minutes-using-opencv-python-90f89d7c0f81?source=collection_archive---------1-----------------------

在这篇短文中,我想分享一个在 OpenCV 和 Python 中使用 Haar 级联检测人脸的非常流行和简单的方法。

The video version for those who prefer that !

首先确保你已经安装了 OpenCV。您可以使用 pip 安装它:

pip install opencv-python

使用 Haar 级联的人脸检测是一种基于机器学习的方法,其中用一组输入数据训练级联函数。OpenCV 已经包含了许多预先训练好的人脸、眼睛、微笑等分类器..今天我们将使用面部分类器。您也可以尝试其他分类器。

需要下载训练好的分类器 XML 文件(Haar cascode _ frontal face _ default . XML),在 OpenCv 的 GitHub 资源库中有。将其保存到您的工作位置。

若要检测图像中的人脸:

需要注意一些事情:

  • 该检测仅对灰度图像有效。因此,将彩色图像转换为灰度图像非常重要。(第 8 行)
  • 检测多尺度函数(第 10 行)用于检测人脸。它有 3 个参数——输入图像、比例因子最小邻居比例因子指定图像尺寸随每个比例缩小多少。最小邻居指定每个候选矩形应该有多少邻居来保留它。你可以在这里详细了解。您可能需要调整这些值以获得最佳结果。
  • 包含找到面的矩形区域的坐标列表。我们使用这些坐标来绘制图像中的矩形。

结果:

同样,我们可以检测视频中的人脸。如你所知,视频基本上是由帧组成的,帧是静止的图像。因此,我们对视频中的每一帧进行人脸检测。代码如下:

这里唯一的区别是,我们使用一个无限循环来遍历视频中的每一帧。我们使用 cap.read() 来读取每一帧。返回的第一个值是一个标志,指示帧是否被正确读取。我们不需要它。返回的第二个值是我们将在其上执行检测的静止帧。

找到这里的代码:【https://github.com/adarsh1021/facedetection】

希望这对你有用。如果你在实现这个过程中有任何困难或者你需要任何帮助,请联系我。

电子邮件:adarsh1021@gmail.com

社交媒体: LinkedIn Twitterinsta gram YouTube

8 行代码实现人脸检测、识别和情感检测!

原文:https://towardsdatascience.com/face-detection-recognition-and-emotion-detection-in-8-lines-of-code-b2ce32d4d5de?source=collection_archive---------2-----------------------

用数据做酷事!

介绍

人类一直具有识别和区分人脸的先天能力。现在计算机也能做到这一点。这打开了大量的应用。人脸检测和识别可以用来提高访问和安全性,就像最新的苹果 Iphone 一样(见下面的 gif),允许在没有物理卡的情况下处理支付——Iphone 也是这样做的!,实现罪犯识别,并允许个性化的医疗保健和其他服务。人脸检测和识别是一个深入研究的课题,网上有大量的资源。我们已经尝试了多个开源项目,以找到最容易实现同时又准确的项目。我们还创建了一个管道,用于在图像加载后对任何输入图像进行检测、识别和情感理解,只需 8 行代码!我们的代码在 Github 上开源。

Facial Biometric

本博客分为三个部分:

  1. 面部检测—检测任何输入图像或帧中面部位置的能力。输出是检测到的面的边界框坐标
  2. 面部识别—将多张脸放在一起比较,以识别哪些脸属于同一个人。这是通过比较人脸嵌入向量来完成的
  3. 情绪检测——将脸上的情绪分为快乐、愤怒、悲伤、中性、惊讶、厌恶或恐惧

所以让我们开始吧!

面部检测

面部检测是我们管道的第一部分。我们使用了 python 库人脸识别,我们发现它易于安装,并且在检测人脸时非常准确。该库扫描输入图像,并返回所有检测到的面的边界框坐标,如下所示:

Face Detection

下面的代码片段展示了如何使用 face_recognition 库来检测人脸。

face_locations = face_recognition.face_locations(image)
top, right, bottom, left = face_locations[0]
face_image = image[top:bottom, left:right]

安装和使用面部识别的完整说明也在 Github

面部识别

面部识别验证两张脸是否相同。面部识别在安全、生物计量、娱乐、个人安全等方面的应用非常广泛。用于人脸检测的同一个 python 库 face_recognition 也可以用于人脸识别。我们的测试表明它具有良好的性能。给定两张匹配的脸,它们可以相互匹配,给出真或假的结果。面部识别涉及的步骤有

  • 在图像中查找人脸
  • 分析面部特征
  • 比较两个输入面的特征
  • 如果匹配则返回真,否则返回假。

下面是实现这一点的代码片段。我们为两个人脸创建人脸编码向量,然后使用内置函数来比较向量之间的距离。

encoding_1 = face_recognition.face_encodings(image1)[0]

encoding_2 = face_recognition.face_encodings(image1)[0]

results = face_recognition.compare_faces([encoding_1], encoding_2,tolerance=0.50)

让我们在下面两张图片上测试模型:

Face 1

Face 2

如右图所示,我们有两张不同姿势的莱昂纳多·迪卡普里奥的脸。在第一张照片中,脸部也不是正面照。当我们使用上面共享的代码运行识别时,人脸识别能够理解这两张脸是同一个人!

情感检测

人类习惯于从面部情绪中获取非语言暗示。现在计算机也越来越擅长阅读情感。那么我们如何在图像中检测情绪呢?我们使用了来自 Kaggle 的开源数据集——人脸情绪识别(FER )并构建了一个 CNN 来检测情绪。情绪可以分为 7 类——快乐、悲伤、恐惧、厌恶、愤怒、中性和惊讶。

模型——我们在 Keras 中构建了一个 6 层卷积神经网络(CNN ),并使用图像增强来提高模型性能。我们尝试了许多不同的模型,并在这个链接上开源了我们的最佳实现。

您可以加载预训练的模型,并使用下面的两行代码在映像上运行它:

model = load_model("./emotion_detector_models/model.hdf5")
predicted_class = np.argmax(model.predict(face_image)

结论

这篇博客演示了在你的应用程序中实现面部检测和识别模型是多么容易。面部检测可以是许多定制解决方案的起点。我希望你自己尝试一下我们的开源代码

我有自己的深度学习咨询公司,喜欢研究有趣的问题。我已经帮助许多初创公司部署了基于人工智能的创新解决方案。请到 http://deeplearninganalytics.org/来看看我们。如果你有一个我们可以合作的项目,那么请通过我的网站或在info@deeplearninganalytics.org联系我

你也可以在https://medium.com/@priya.dwivedi看到我的其他作品

参考文献:

面部/关闭:使用 Viola Jones 方法的高速面部跟踪

原文:https://towardsdatascience.com/face-off-high-speed-facial-tracking-using-the-viola-jones-method-f9e0ba491c9f?source=collection_archive---------19-----------------------

有没有想过你数码相机上的盒子是如何准确地知道一张脸在哪里和是什么的?

早在 2011 年,在卷积神经网络或任何其他类型的深度学习技术流行之前,就有了 Viola-Jones 算法。这种算法是如此的创新和有效,以至于这篇论文被引用了 19000 多次。

研究人员的目标是解决面部检测的两个最大问题。该算法实现了以下功能:

  • 鲁棒性—高检测率(真阳性率)和极低的假阳性率。
  • 速度—对于实时,每秒必须处理至少两帧。

最大限度地利用这两个条件将需要大量的处理能力,而这在 2001 年是不具备的。即使是现在,如果我们希望能够在一个便宜的设备上运行面部检测,例如 Raspberry Pi,这种权衡将是一个优先事项。

显然需要某种创新的方法来实现鲁棒性和速度之间的平衡,从而返回准确的结果。

现在让我们来看看维奥拉和琼斯是如何实现这个目标的。

分类器

研究人员发现了一个过程,在这个过程中,你可以非常成功地识别图像的子窗口是否是一张脸。

首先,图像被转换成灰度。然后,为了识别图像中的一张脸,Viola 和 Jones 决定使用所谓的 Haar Like 特征。这些是图像的子窗口,其中一部分比另一部分暗。

然而,这是一个弱分类器。例如,具有两种对比色的盒子可以被视为一张脸,因为在个人的眼睛和脸颊之间经常设置相似的对比色。

将选择数以千计的这些特征,并且如果它们被证明是真实的,我们将能够以高精度决定图像的子窗口是否包含人脸。

积分图像

该算法最聪明的部分是它如何达到它的速度。为了有效地计算特征,它使用所谓的积分图像。

Via Matlab

当一个新的图像被分类时,像素值的总和被跨越行和列,使得像素(I,j)是在它之前的所有像素的总和。

对图像进行这种计算预处理允许计算机通过一些加法和减法运算来计算矩形特征,而不是遍历像素值。

现在,当算法测试图像的一部分是否是特征时,它可以很容易地在常数时间内取矩形区域的差。

分类器学习/选择

这些矩形框的原始特征数量是天文数字。根据这篇研究论文,仅仅在一个 24x24 的图像窗口中就有 162,336 个特征。

由于每个矩形本身都是一个弱分类器(每个矩形的表现都比猜测略好),Viola 和 Jones 使用了自适应增强(AdaBoost)。

Zhuo Wang — Realizing Low-Energy Classification Systems by Implementing Matrix Multiplication Directly Within an ADC

AdaBoost 使用所有矩形特征,并根据训练数据集训练它们。在此过程中,它将通过对弱分类器进行线性组合来寻找弱特征组并使其成为分类器。

简而言之,AdaBoost 构建的每个分类器都是一组固定数量的特征,每个特征都有不同的权重,以确定其形成的分类器的二元分类决策。使用由提升方法形成的这些分类器而不是每个单独的特征,为模型提供了更强的图像分类方法。

注意力级联

为了区分一个图像的子窗口实际上是不是一张脸,他们建立了一个注意力级联。它本质上是一棵决策树,其中每个节点都是所构造的分类器之一。

Via Matlab

级联的节点从简单的两个特征分类器开始排序,具有非常高的检测率,但是具有高的假阳性率。随着我们的前进,树节点变成一个更复杂的分类器,这将给我们更多的准确性,但在计算上更复杂。

Via stack overflow

图像的每个子窗口对照树中的第一个分类器进行测试,并且或者被分类为“可能是一张脸”,这将其移动到下一个分类器,或者被分类为“不是一张脸”,并且将不会用下一个分类器进行测试。

每个分类器的计算时间取决于组成分类器的特征数量。因此,该树从具有高检测率的简单分类器开始,该分类器通过将大量子窗口分类为注意力级联的早期节点中的非人脸来丢弃它们。

研究人员训练的模型最终形成了一个 38 层的级联分类器树。

他们的模型结果相当出色。该论文指出,在 700 Mhz 奔腾 III 处理器上,面部检测器可以在大约 0.067 秒内处理 384×288 像素的图像。

结论

尽管在图像识别领域已经取得了许多进步,但这表明这种算法虽然仍是机器学习,但比 CNN 更容易解释。在算法的每个阶段,你都能够准确地解释算法实际在做什么。

在实践中,我们可以优化实时识别和跟踪的一种方法是使用 Viola Jones 来检测面部,然后使用像 Kanade–Lucas–Tomasi(KLT)特征跟踪器这样的算法来跟踪视频中检测到的面部。

如果你有兴趣自己尝试一下,OpenCv 有一个实现,我将在下面链接,还有 Viola 和 Jones 的原始论文以及 Mike Pound 博士在 Computerphile 上的一个非常有用的解释。

https://www . cs . CMU . edu/~ efros/courses/lbmv 07/Papers/viola-cvpr-01 . pdf

[## 使用 Haar 级联的人脸检测

使用基于 Haar 特征的级联分类器的目标检测是由 Paul…

docs.opencv.org](https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_objdetect/py_face_detection/py_face_detection.html)

使用 JavaScript API 的人脸检测— face-api.js

原文:https://towardsdatascience.com/face-recognition-using-javascript-api-face-api-js-75af10bc3dee?source=collection_archive---------4-----------------------

Face-API.js

在本文中,我们将学习使用face-api.js的面部检测(年龄/性别/面部位置/情绪),以及在网络浏览器上使用coco-ssd模型的附近物体检测(人/电话等)。

face-api.js是一个 javascript 模块,构建在 tensorflow.js 核心 之上,实现了几个 CNN s ( 卷积神经网络)来解决人脸检测、人脸识别和人脸地标检测,针对 web 和移动设备进行了优化。

让我们现在开始,

先决条件:

  1. 对 React.js 有基本的了解(你可以选择任何你喜欢的库或前端框架)
  2. 对 p5.js 库的基本了解。
  3. 已安装的 create-react-app 和 Node.js 版本> = 10.15.1

让我们创建一个 react 项目,

npx create-react-app object_face_detection
cd object_face_detection

现在,安装下面的依赖项

npm install @tensorflow-models/coco-ssd
npm install @tensorflow/tfjs-converter
npm install @tensorflow/tfjs-core
npm install face-api.js
npm install p5
npm install react-p5-wrapper

让我们一个一个地了解每一个依赖关系——

  1. @tensorflow-models/coco-ssd —这将用于其他物体检测,如电话、墙壁等。在脸的周围。Coco-ssd是一个 TensorFlow 模型,已经用大量通用图像进行了训练,可以直接在浏览器中使用。你可以在这里阅读—https://github . com/tensor flow/tfjs-models/tree/master/coco-SSD
  2. @tensorflow/tfjs-converter—将 TensorFlow 保存的模型和 keras 模型直接转换为 tensorflow.js 使用。我的意思是已经有很多使用 python 或 R 制作/训练的模型,但是模型保存的格式不同于 TensorFlow.js 使用/消费的格式。所以需要这种依赖来将其他模型转换成 TensorFlow 的可消费格式。
  3. @tensorflow/tfjs-core — Tensorflow 核心 javascript 库。你可以读读这个——https://www.tensorflow.org/js/tutorials/setup。face-api.js 是在这种依赖的基础上构建的。
  4. face-api.js —这是本文的核心 API,将用于人脸检测。了解更多—https://github.com/justadudewhohacks/face-api.js?files=1
  5. 这是最近发展起来的另一个伟大的库,在我们的上下文中,我们将使用它来制作网络摄像头视频,并在检测到的人脸和物体周围绘制一个红色框。你可以阅读—https://p5js.org
  6. react-p5-wrapper —这只是一个在 p5js 功能上编写的 reactjs 包装器。所以与其写一个,不如用它来节省时间。

现在让我们深入编码💻

在我们开始之前,我们需要下载face-api.js 模型(已经建立做面部和情绪检测)。因此,让我们在我们的public文件夹中创建一个名为models的文件夹,并下载位于https://github . com/justadudewhohacks/face-API . js/tree/master/weights的文件

models文件夹将会是这样的

我们现在将在我们的src文件夹中创建一个名为ObjectDetectionSketch.js的文件,它将包含我们所有的逻辑。

该文件将有如下一些导入语句

import * as p5 from 'p5'import "p5/lib/addons/p5.dom";import * as cocoSsd from '@tensorflow-models/coco-ssd';import * as faceapi from 'face-api.js';
  1. p5 和 p5.dom —需配合 p5js,忍着点,过几段你就明白确切用法了。
  2. cocoSsdfaceapi——你现在已经知道了。

接下来,我们将定义我们的 face-API 模型 URL

const MODEL_URL = '/models' 
// this will pick public folder by default

现在,我们将创建名为 sketch 的函数(包含所有逻辑的包装函数)

export default function sketch (p) {}

在 sketch 函数内部,我们将定义几个变量和四个函数,两个自定义的,两个来自 p5.js,分别叫做setupdraw

变量

// Variables// save current camera image
let capture = null;// save cocossd Model
let cocossdModel = null;// to save the result of cocossd and face-api results
let cocoDrawings = [];
let faceDrawings = [];

自定义功能

// Custom Function// Used to store the result of coco-ssd model
function showCocoSSDResults(results) {
    const id = capture.id();
    cocoDrawings = results;
}// used to store the result for the face-api.js model
function showFaceDetectionData(data) {
    faceDrawings = data;
}

P5.js 函数

// P5.js Functions
p.setup = async function() {}
p.draw = function() {}

我们来详细了解一下这两个 p5 的功能。🚀

Setup功能

页面加载后,将自动调用 p5.js 设置。我们正在覆盖 p5 内置的setup函数来初始化我们需要的一些细节。下面是我们将在设置函数中执行的步骤。

1。加载我们将用于人脸检测的三个 face-api.js 模型

await faceapi.loadSsdMobilenetv1Model(MODEL_URL);
await faceapi.loadAgeGenderModel(MODEL_URL);
await faceapi.loadFaceExpressionModel(MODEL_URL);

2创建一个 p5.js 画布

p.createCanvas(1280, 720);

3在画布上实现摄像头捕捉功能

const constraints = {
  video: {
      mandatory: {
      minWidth: 1280,
      minHeight: 720
      },
      optional: [{ maxFrameRate: 10 }]
  },
  audio: false
};capture = p.createCapture(constraints, () => {});

4。 设置视频 Id 和大小。

capture.id("video_element");
capture.size(1280, 720);
capture.hide(); // this is require as we don't want to show the deafault video input

5加载 cocoSsd 模型并保存在本地。

cocoSsd.load().then((model) => {
  try {
      cocossdModel = model;
  } catch(e) {
      console.log(e);
  }
}).catch((e) => {
    console.log("Error occured : ", e);
});

绘图功能

如果在 p5js 画布上绘制任何东西,就会调用 p5js 的 draw 函数。在我们的自定义绘制函数中,我们将执行以下步骤。

  1. 将背景设置为白色,并在上面绘制我们的图像。此外,添加透明度,这样任何进一步绘制到画布上的东西都将是透明的。
p.background(255);
p.image(capture, 0, 0);     
p.fill(0,0,0,0);

2.代码渲染 coco-ssd 模型结果。

cocoDrawings.map((drawing) => {
  if (drawing) {
      p.textSize(20);
      p.strokeWeight(1);
      const textX = drawing.bbox[0]+drawing.bbox[2];
      const textY = drawing.bbox[1]+drawing.bbox[3];

      const confidenetext = "Confidence: "+ drawing.score.toFixed(1);
      const textWidth = p.textWidth(confidenetext);

      const itemTextWidth = p.textWidth(drawing.class);
      p.text(drawing.class, textX-itemTextWidth-10, textY-50);p.text(confidenetext, textX-textWidth-10, textY-10);
      p.strokeWeight(4);
      p.stroke('rgb(100%,100%,100%)');
      p.rect(drawing.bbox[0], drawing.bbox[1], drawing.bbox[2], drawing.bbox[3]);
  }
});

这里我们有一个cocoDrawings对象,包含 coco-ssd 模型检测到的当前对象细节。这个物体的形状看起来像

{
  "bbox": [
    6.165122985839844,
    2.656116485595703,
    1034.7143936157227,
    712.3482799530029
  ],
  "class": "person",
  "score": 0.9296618103981018
}

我们使用这个对象数据来绘制一个矩形,该矩形定义了当前对象的位置以及被检测到的对象的名称(在上面的例子中是人)和分数。

这是一个基本的 p5js 代码,用于绘制文本和矩形。如果你觉得很难理解,那么试试 p5.js docs,一个小时之内你就会明白了。—https://p5js.org/

我们可以在画布上绘制多个对象,因为它们会被检测到。

3.代码呈现 face-api.js 模型结果。

faceDrawings.map((drawing) => {
  if (drawing) {
    p.textSize(15);
    p.strokeWeight(1); const textX = drawing.detection.box._x+drawing.detection.box._width;
    const textY = drawing.detection.box._y+drawing.detection.box._height;

    const confidenetext = "Gender: "+ drawing.gender;
    const textWidth = p.textWidth(confidenetext);
    p.text(confidenetext, textX-textWidth, textY-60); const agetext = "Age: "+ drawing.age.toFixed(0);
    const ageTextWidth = p.textWidth(agetext);
    p.text(agetext, textX-ageTextWidth, textY-30); const copiedExpression = drawing.expressions;
    const expressions = Object.keys(copiedExpression).map((key) => {
        const value = copiedExpression[key];
        return value;
    }) const max = Math.max(...expressions);

    const expression_value =    Object.keys(copiedExpression).filter((key) => {
        return copiedExpression[key] === max; 
    })[0]; const expressiontext = "Mood: "+ expression_value;
    const expressionWidth = p.textWidth(expressiontext);
    p.text(expressiontext, textX-expressionWidth, textY-10);

    p.strokeWeight(4);
    p.stroke('rgb(100%,0%,10%)');
    p.rect(drawing.detection.box._x, drawing.detection.box._y, drawing.detection.box._width, drawing.detection.box._height);
  }
});

这里我们定义文本大小,将从 face-api.js 获得的数据绘制到 p5.js 画布上。

现在,每个检测到的人脸都有以下由 face-api.js 模型返回的数据

{
  "detection": {
    "_imageDims": {
      "_width": 1280,
      "_height": 720
    },
    "_score": 0.6889822483062744,
    "_classScore": 0.6889822483062744,
    "_className": "",
    "_box": {
      "_x": 121.50997161865234,
      "_y": 15.035667419433594,
      "_width": 507.80059814453125,
      "_height": 531.7609024047852
    }
  },
  "gender": "male",
  "genderProbability": 0.9683359265327454,
  "age": 30.109874725341797,
  "expressions": {
    "neutral": 0.9950351715087891,
    "happy": 0.0000017113824242187547,
    "sad": 0.000005796719960926566,
    "angry": 0.00000466804613097338,
    "fearful": 1.3292748013427058e-9,
    "disgusted": 3.015825145169515e-9,
    "surprised": 0.004952521994709969
  }
}

你可以看到,我们得到的是一个矩形坐标的人脸、性别、年龄和表情数据

我们可以从detection._box 中提取矩形坐标,对于表达式,我们有所有的表达式及其相应的分数。所以,

const copiedExpression = drawing.expressions;
const expressions = Object.keys(copiedExpression).map((key) => {
    const value = copiedExpression[key];
    return value;
})const max = Math.max(...expressions);const expression_value = Object.keys(copiedExpression).filter((key) => {
    return copiedExpression[key] === max; 
})[0];

用上面的代码,我们将估计和获得高度记分员表达式,并显示在一个矩形内

最困难的部分是将文本放入矩形,所以我们做了一个不太好的实现,但它是可行的。

所以我们从盒子的 x 坐标上去掉这个宽度,再加 10,这样左边界和显示文本就有了一些空白。

const ageTextWidth = p.textWidth(agetext);
p.text(agetext, textX-ageTextWidth, textY-30);

等一等🤔这一切都很好,但是检测人脸和其他物体的代码在哪里呢?

这就是了👇

4.检测人脸和其他元对象的代码。

faceapi.detectAllFaces(capture.id())
  .withAgeAndGender()
  .withFaceExpressions()
  .then((data) => {
    showFaceDetectionData(data);
});if(capture.loadedmetadata) {
    if (cocossdModel) {
        cocossdModel
        .detect(document.getElementById("video_element"))
        .then(showCocoSSDResults)
        .catch((e) => {
            console.log("Exception : ", e);
        });
    }
}

我们结束了🎢准备测试。

在候机楼里面做什么

cd object_face_detection
npm start

这是尝试的结果—

https://github . com/overflow js-com/object _ face _ detection _ web cam _ react——如果你觉得很难理解本文的内容,可以参考这里的代码。

注意:这个实现很慢,因为我们在浏览器中加载所有的模型,并且是实时的

如果您想被添加到我的电子邮件列表中,请考虑在这里输入您的电子邮件地址 和关注我的 medium 阅读更多关于 javascript 的文章,并关注github查看我的疯狂代码。如果有什么不清楚或者你想指出什么,请在下面评论。

你可能也会喜欢我的其他文章

  1. Javascript 执行上下文和提升
  2. Javascript —生成器-产出/下一个&异步-等待🤔
  3. 理解 Javascript‘this’关键字(上下文)
  4. Javascript 数据结构与映射、归约、过滤
  5. Javascript- Currying VS 部分应用
  6. Javascript ES6 —可迭代程序和迭代器
  7. Javascript —代理
  8. Javascript —作用域

如果你喜欢这篇文章,请随意分享,以帮助他人找到它!

脸书 vs .欧盟人工智能和数据政治

原文:https://towardsdatascience.com/facebook-vs-eu-artificial-intelligence-and-data-politics-8ab5ba4abe40?source=collection_archive---------22-----------------------

Photo by @greystorm

关于人工智能和脸书数据政治的新 FRA 论文

本文是欧盟基本权利机构(FRA)的一篇名为 数据质量和人工智能——减轻偏见和错误以保护基本权利 的论文的摘要。然后,我继续研究脸书最近在数据政治方面的举措;扎克伯格发表的声明;以及他们最近聘请前副首相尼克克莱格(Nick Clegg)担任全球政策和沟通主管。我希望这能使欧盟的政策更容易理解,并向你们概述脸书在这方面采取的一些行动。

什么是 FRA?

欧盟基本权利署是一个欧盟机构,其任务是“……收集和分析原则上涉及《宪章》所列所有权利的基本权利数据”,这里指的是《欧洲联盟基本权利宪章》。《宪章》规定的这些权利包括:尊严、自由、平等、团结、公民权利和正义。

FRA 关于数据质量和人工智能的论文

FRA 有一个关于人工智能、大数据和基本权利的项目于 2018 年启动。该报告评估了在选定的欧盟成员国将人工智能(AI)和大数据用于公共管理和商业目的的基本权利的利弊。2019 年 6 月发表的论文 数据质量和人工智能——减少偏见和错误以保护基本权利 是这个项目的一部分,也是我在这一部分的重点。

这篇论文是对正在进行的人工智能和大数据政策讨论的贡献。本部分中的引用将全部来自本文。

该报告声称,机器学习系统和人工智能(AI)中使用的算法只能与用于开发它们的数据一样好。然而,关键问题是如何定义'质量'。他们强调透明度,认为数据量往往比质量更重要。

人工智能包含在欧盟委员会关于欧洲人工智能的通信中:“人工智能(AI)是指通过分析其环境并采取一定程度的自主行动来实现特定目标的智能行为的系统。

他们提到 AI 不是指一个事物,而是一套流程和技术发展。

在论文中,数据质量是一个宽泛的概念。他们强调了社会科学和调查研究中使用的与数据质量有关的两个通用概念:

  • 表示错误,这意味着数据没有很好地覆盖它应该覆盖的人群;
  • 测量误差 ,这意味着数据并没有测量出它们想要测量的东西。

解释复杂算法如何工作一直是一个焦点,但是有人认为 ML 和数据科学忽略了数据质量的关键方面。

其他报告和欧洲理事会多次提到这个问题。欧洲科学和新技术伦理小组指出,应避免数据集中的歧视性偏见。

欧盟委员会人工智能高级专家组在其道德准则中将数据治理作为可信人工智能的要求之一。

由人权和技术组织联盟撰写的“多伦多宣言”将一种风险命名为“不完整或不具代表性的数据,或代表历史或系统偏见的数据集”。

该报告提到了涉及至少三个不同数据集的机器学习(ML)算法:

  1. 训练数据。在监督学习中,用于学习期望结果的数据是所谓的特征。期望的结果通常被称为标签。这是算法如何学习模式的基础。
  2. 输入数据。当一个算法被部署时,新的看不见的特征被添加。
  3. 推断标签(或预测、推论、推断动作或输出数据)当看不见的数据馈入 ML 算法时产生。

在这篇论文中,他们提出了一个案例研究,重点是来自互联网的数据,以说明“表示错误”的可能性。

该报告提到了欧盟对大数据的使用。总体而言,欧盟三分之一的大型企业(33 %)使用大数据分析。数据的使用引发了问题。他们列出了以下内容:

并非所有人:

  1. 能接触到社交媒体,报道也不尽相同。
  2. 想要访问互联网、社交媒体或其他应用程序。

有些群体不包括在收集的数据中,只包括那些放弃数据的人。下图显示了数字鸿沟,通常被称为数字鸿沟。资料来源:2019 年欧洲森林资源管理局【根据欧统局(ISOC _ ci _ in _ h)】😗*

还有一个地理上的差异。因此,收集的数据不代表人口中的某些群体。

然后,他们继续讨论基本权利,每个权利都有一个简短的例子:

  • 不歧视。
  • 男女平等。
  • 获得公平审判和有效补救。
  • 私人和家庭生活
  • 个人资料的保护

*研究人员已经开始分析这个话题,强调将推断的

数据视为个人数据的重要性。只有这样,GDPR 的权利才能适用,包括了解这些数据的权利,以及访问、纠正、删除和反对这些数据的权利。*

对于自动化决策,GDPR 要求数据控制者提供关于所涉及的逻辑的有意义的信息,以及这种处理对于数据主体的重要性和预期后果。

评估数据质量

存在着完整性、准确性、一致性、及时性、复制性、有效性、可用性和出处等问题。提到的一个具体问题是:

  • 大规模移民中的生物特征数据。

数据可能不准确,对技术的信任可能导致不幸的情况。“数据质量”的一个定义是所使用的数据是否“符合目的”。

"因此,数据的质量在很大程度上取决于它们的使用目的."

文中提到了两种常见的误差来源。这是在经典调查研究的背景下讨论的:测量误差和表示误差。

测量误差指的是所使用的数据指示或反映要测量的内容的准确程度。

必须如何评估回答者?编辑和重组数据对此有多大影响?你如何衡量一个好员工?作为一个评论,我可以补充:你如何衡量一个好用户?

数据近似于真实世界的现象,测量中总会有误差。多大的误差是可以接受的?数据通常由人工标记(如标记图片),因此标记过程中的质量控制至关重要。

表示错误:如果数据没有很好地覆盖它应该覆盖的人群,那么得到的统计数据将是不正确的(即有偏见的)。

一个重要的问题是,用于构建应用程序的数据是否能够准确地代表未来的用户。人们通常会开始以不同的方式行事。

FRA 的信度和效度

社会科学中用来描述某些概念的测量误差的两个概念。可以通过指数等指标来完成;或者当无法直接测量时,使用相关问题的测量。

可靠性:指测量的稳定性和一致性。

有效性:数据和预测是否实际测量了它们打算测量的东西的问题,因此与表示和测量的误差有关。

不可靠的数据可能有正确的目标,但显示出太多的变化和不确定性,从而错过了目标,尽管平均结果良好。大的数字减少统计的不确定性,然而低的质量可能不增加有效性。

欧洲统计系统中的 FRA 数据质量——人工智能的教训

根据本文,数据质量是一项竞争优势。本文引用了欧洲统计系统的质量减速,我发现将它们包括在内很有价值:

  1. 质量方针是公开的。

  2. *制定了计划和监控数据生产流程的

    质量的程序。*

  3. *定期监控、评估

    和报告产品质量。*

  4. 定期对输出进行全面评审,包括由外部专家进行评审。

这种统计数据的产生与人工智能相关,因为它们与所使用的机器学习方法相关。在该论文中,提出了统计学和机器学习之间的主要区别如下。统计学旨在描述具有特征、相关性和因果解释的群体。根据洗钱司的意见,案文内容如下:

……机器学习主要关注的是预测一个单位的特征,比如一个人、一个公司或者一个国家。这具有稍微不同的含义,因为与关于人口群体的一般统计相比,预测的准确性变得更加重要。

质量评估数据集描述—行业标准

人工智能和人工智能领域的专家提出了数据集描述,称为‘数据表’,或‘营养标签’。有人建议以与硬件组件相同的方式描述数据集,以检查它们是否符合行业标准。这篇论文声称,在人工智能领域,没有标准化的方法来描述数据集。

这种标准化必须考虑到灵活性,以便能够包括人工智能应用中使用的各种可能的数据格式和集合。这一点很重要,因为如果数据是为一个目的生成的,就需要评估它们是否也适合另一个目的。

数据文档倡议(DDI)有一个描述数据集(即元数据)的标准化方法。在某种程度上,这对于共享以供重用是有用的。需要数据收集的背景、方法和元级描述。通过这种方式,使用特定数据集评估工具的潜在误差将更加容易。因此,本文认为统计实践为人工智能方面的数据质量保证提供了潜在的途径。

FRA 结论

利用数据的 ML 系统和算法需要更广泛和更灵活的方法来评估和处理数据质量。他们建议询问几个问题,以确定算法使用中的质量问题:

  • 数据从哪里来?谁负责数据收集、维护和传播?
  • 数据中包含哪些信息?数据中包含的信息是否适合算法的目的?
  • 数据涵盖了哪些人?谁在数据中被低估了?
  • 数据集内是否缺少信息,或者是否只覆盖了部分单元?
  • 用于构建应用程序的数据收集的时间框架和地理覆盖范围是什么?

由于质量可能会对歧视性做法和错误系统产生影响,因此缓解潜在问题非常重要。该论文认为,这可以借鉴社会科学和调查研究的“严谨性”。然而他们说:

“新技术需要对潜在的基本权利挑战进行全面评估[……]为了更好地理解对基本权利的影响,需要进行跨学科研究,因为这一主题结合了许多不同领域的要素,包括法律、计算机科学、统计学和社会科学。”

脸书及其数据政治

剑桥分析报告发布后,欧盟美国联邦贸易委员会同意对脸书罚款 50 亿美元以解决隐私侵犯问题。据《卫报》2019 年 7 月 12 日报道,这将是联邦贸易委员会对一家技术公司的最大一笔罚款,也是对任何一家侵犯隐私的公司的最大一笔罚款

通用数据保护条例(GDPR)

GDPR 于 2018 年 5 月 25 日生效,是“世界上最严厉的隐私和安全法”。在欧盟的 GDPR 页面上写着:

…违反 GDPR 的罚款非常高。处罚分为两级,最高为€2000 万英镑或全球收入的 4%(以较高者为准),此外,数据主体有权寻求损害赔偿

看到脸书的例子后,我们可以理解这不仅仅是一个大胆的主张。该文章提到:

  • 个人数据:与个人相关的任何可以直接或间接识别的信息。
  • 数据处理:对数据执行的任何动作,无论是自动还是手动。
  • 数据主体:数据被处理的人。
  • 数据控制者:决定为什么以及如何处理个人数据的人。
  • 数据处理者:代表数据控制者处理个人数据的第三方。例如服务器。

特别是,在同意方面有额外的严格规则,使其在更大程度上可以理解。还有三种情况可能需要任命一名数据保护官员(DPO): (1)如果你是一个公共机构;(2)大规模系统地监控人;(3)大规模处理特殊类别的数据(定罪和医学)。法规本身有 88 页,我在这里没有提供足够好的概述。

脸书在 GDPR

在“服从”这个表达中,脸书似乎给在 GDPR 上创建了一个页面。他们在几个段落中概述了关键词。然后,他们继续采取行动,他们将采取遵守。

  • 透明度:脸书的数据政策定义了他们如何处理人们的个人数据。他们承诺提供有关其数据政策的教育。他们将通过(1)产品内通知和(2)消费者教育活动来确保人们了解他们的数据如何被使用以及他们的选择。
  • 控制:提供对数据使用方式的控制。启动控制中心,使隐私设置更容易理解和更新。它们提醒人们在使用脸书时如何查看和编辑他们的设置。
  • 责任:脸书有隐私原则解释他们如何看待隐私和数据保护。他们有一个团队来帮助确保我们记录我们的合规性。此外,他们定期会见来自世界各地的监管者、决策者、隐私专家和学者,通报他们的做法。

扎克伯格拥抱 GDPR 并雇佣政策专家

马克·扎克伯格最近积极拥抱 GDPR,尽管他的动机受到质疑。然而,扎克伯格并不是第一个支持 GDPR 式规则的科技公司首席执行官。微软首席执行官塞特亚·纳德拉称赞欧洲法律和蒂姆·库克去年呼吁联邦隐私监管。其他国家,如加拿大,现在开始考虑是否能让脸书参与隐私问题的讨论。

最近,脸书聘请英国前副首相尼克克莱格爵士(Sir Nick Clegg)担任其全球政策和沟通主管。这可能是由于公司在过去几年中面临的一系列问题。这包括但肯定不限于:

马克·扎克伯格呼吁在四个不同领域进行监管。有害内容、完整性、隐私和数据可移植性是他概述的关键问题:

有害内容——他希望社交应用能有总体的规则和基准来衡量

选举诚信——他希望政府对什么构成政治或议题广告做出明确的定义

隐私——他想要 GDPR 式的全球法规,对违反者进行制裁

数据可移植性——他希望用户能够将他们的信息从一个应用程序带到另一个应用程序

尼古拉斯爵士威廉彼得克莱格

2019 年 7 月 17 日新政治家报道称,Nick 作为一名翻译有着积极的看法,他的方法是:“忽略意识形态和党派偏见;求进步,求妥协;寻找基于证据和现实解决方案”他能流利地说五种语言:英语、西班牙语、法语、德语和荷兰语。

此外,负面的一面是,他会为了利益不择手段。克莱格一家住在门洛帕克一栋 700 万英镑的豪宅里,根据《福布斯》的说法,这是美国“最昂贵的邮政编码”。《卫报》的一篇观点文章称他“在学费问题上背叛了公众和他在学生城的核心选票”。作为一名学生,我在英国生活了三年,我觉得这反映了相当多的英国年轻人的情绪。

他最近在欧洲其他国家的首都旅行,要求政府对脸书进行监管。克莱格去了威斯敏斯特学校,在剑桥学习人类学。他声称传统媒体比新媒体对政治的影响更大,声称没有证据表明俄罗斯干涉英国选举。他说脸书希望被监管。

脸书和中国

正如 Nick Clegg 在他的观点中所提出的,脸书现在正在争论它作为一个抗衡中国的力量的地位,以及反对‘专制社会价值观’的个人权利。这一点最近已经为人所知,因为一个关于卡利布拉推动引入新货币的听证会。脸书 Calibra 的负责人 David Marcus 说:

我相信,如果美国不在数字货币和支付领域引领创新,其他人会的。如果我们不采取行动,我们很快就会看到一种由价值观截然不同的人控制的数字货币。

不到一年前,2018 年 10 月,马克·扎克伯格在 Recode 的一次采访中对卡拉·斯威舍说:

我们在这里长大,我认为我们分享了很多我认为这里的人们非常珍视的价值观,我认为我们这样做总体来说是非常好的,无论是出于安全原因还是从价值观的角度来看。因为坦率地说,我认为另一个选择将是中国公司。如果我们采取这样的立场,“好吧,作为一个国家,我们将决定,我们要切断这些公司的翅膀,让它们更难在不同的地方运营,它们必须变得更小,”那么就会有很多其他公司愿意并且能够取代我们正在做的工作。

尼克·克莱格在 2019 年 1 月重复了这条信息。2019 年 7 月 19 日,TechCrunch 发表了一篇全面的文章。文章的结论是响亮的:“也许援引中国的威胁来煽动政府官员的担忧是政治上的精明,甚至可能是有效的。这并不意味着它是正确的。”以这种方式,现在的问题是一家大公司及其作为一家公司的公民责任或地位。**

公民责任和数据政治

面对越来越多的监管和指控,以及脸书方面以这种方式做出的回应,很难理解下一步该何去何从。然而,我们可能会开始考虑,当一家公司变得如此庞大,有如此多的在线用户以各种方式讨论他们的生活时,会发生什么。

不久前的一次聚会上,我听到有人说:

“这些大型科技公司有时和国家一样大。也许他们应该开始这样做,并承担一些公民责任。”

对大多数人来说,脸书作为一个国家行为者或具有类似国家的品质似乎有些牵强,包括我在内,但这是一种值得考虑的可能性。公私合作制定公平的数据政策,既保护人民,又为创新敞开大门,这不是一件容易的事情。

我们可以大胆地进入福柯式的概念,如生物政治、生物勘探和抵抗——是的,我们当然可以问权力的问题。政治和公司的分离差距越来越大,在国际关系(IR)的意义上,这种观点的相关性越来越大。甚至在涉及国际关系的基本概念时。

这是现实主义、自由主义还是社会建构主义?看起来脸书的数据政策正在从自由主义的“自由”向 T2 提出的“基于现实的解决方案”的方向发展。两者都同样模糊不清。

从数据主体到数据公民

从私人权力到国家权力的问题,以及随之而来的困惑,在这个、【结尾】或中,看看数据政治的学术定义会很有趣。

Evelyn RUP pert、Engin Isin 和 Didier Bigo 于 2017 年 12 月在 Researchgate 上发表的一份报告中定义了数据政治:

我们将‘数据政治’定义为关于这些世界的政治问题的表达,以及它们通过提出权利主张来激发主体管理自己和他人的方式。我们认为,如果不理解这些可能性条件——世界、主体和权利——就很难干预或塑造数据政治,如果这意味着将数据主体转变为数据公民的话。

因此,这种表达当然是说明有数据公民身份,他们还没有定义这是隐含的。这些权限是在文章中累积数据的上下文中定义的。提交人质疑公民是否有权知道谁:

  1. 拥有
  2. 分配
  3. 接近
  4. 使用
  5. 挪用
  6. 修改
  7. 辞职

因此,数据公民权意味着公民义务。如果您有权使用您的数据,并且您拥有这些数据,那么您还有责任管理您的数据。你行使权力或控制——指导该领域政策的制定和管理。这不仅是 GDPR 的敌人,也让人们有权利增加责任感和义务感。

“……每一项权利都意味着一项责任;每一个机会,一种义务;每一份财产,都是一种责任

我们必须尽最大努力了解数据公民身份,以及我们在这方面有哪些权利、责任和义务。特别是在人工智能和数据政策的背景下,这两个领域在国际和当地的讨论中受到越来越多的关注。

这是第 500 天的第 48 天

什么是#500daysofAI?
我在挑战自己,写下并思考未来 500 天的人工智能话题。一起学习是最大的快乐,所以如果你觉得一篇文章引起了你的共鸣,请给我反馈。感谢您的阅读!

FaceMath:人工智能中的思想代数。

原文:https://towardsdatascience.com/facemath-an-algebra-of-ideas-in-a-i-568979737639?source=collection_archive---------21-----------------------

人工智能的无监督学习

变换面孔、文字和其他很酷的东西

闭上眼睛,在脑海中想象一个洋娃娃。现在想象同一个娃娃会说话。看娃娃说话。你刚才在脑子里所做的——你能看到会说话的东西——是一个娃娃的想法和会说话的想法的结合。事实证明,我们可以使用人工智能通过代数将概念相加,这就是本文的内容。我喜欢称它为“思想的代数”,因为我们正在使用基础数学将概念相加,形成复合概念。

让我们来谈谈想法,并把它们变成数字。回到这篇卡通人工智能文章,我们看到了文本块如何被转化为特殊的固定长度的数字列表(嵌入向量),这些向量的聚类揭示了关于漫画组主题的信息。单个单词也可以嵌入到向量中,通过它们与其他单词的关系给出它们的意思。传统的例子是向量“国王”——“男人”+“女人”=“女王”。这意味着我们可以学习一种表示法,这样国王、男人和女人的向量可以用来找到王后的向量。这是文字上的代数,但我们可以做得更多!事实证明你也可以用图片来做这件事!

Interpolating between yound and old faces (between faces, not ages). More on how we did this below.

通过为一组图像找到好的编码(即嵌入),你可以开始做我们在文字上做的那种“特征数学”,以酷的方式修改图片。以面部为例,模型可以被训练成包括诸如年龄、性别或面部是否戴眼镜之类的特征。然而,用于模拟人脸的神经网络(例如,autoencoder 或 gan)不同于用于模拟单词及其相互关系的数学类型(单词嵌入有 word2vecgloVefastTextELMoBERTXLNet 等)。).

文献中激励人心的例子

将这种概念代数应用于图像有许多实际应用。从 snapchat 的性别交换过滤器(T1)到带有图像特征提取的面部匹配(T2)都有使用案例。这种技术的一个著名例子是 thispersondoesnotexist.com 的,它可以生成看起来很真实的高分辨率人脸照片。

我研究这个主题的动机是几年前亚历克·拉德福德在论文“深度卷积生成对抗网络的无监督表示学习”中所做的一些工作。打开 PDF,看看第 10 页。这是我脑子里的灯真正打开的地方。他们展示了如何通过将想法加在一起,使用神经网络有条件地生成人脸。我们在第 10 页看到,“戴眼镜的男人”减去“不戴眼镜的男人”加上“不戴眼镜的女人”的向量给了我们一张戴眼镜的女人的脸。这种做法很疯狂。这些加号和减号促使我不断了解这方面的进展。就是超级爽。

我稍后将讨论我们为本文构建的内容。首先更多的是关于已经存在的。最新和最伟大的作品是我现在想谈论的。看看whichfaceisreal.com看看你是否能检测出真实与脸部的照片。诀窍是密切注意图像的背景。虽然假图像超级逼真,但它们没有经过良好的训练来生成逼真的背景。生成器确实理解像眼镜的光学、反射、阴影等东西,但是它有时填充背景很差。

下面以视频的形式嵌入了三个即将推出的令人惊叹的项目。在第一个“基于文本的视频编辑”中,我们看到视频编辑将被大规模破坏,因为我们现在可以使用深度学习来编辑人们在视频中说的话。激励人心的例子是一份关于股票市场的报告。在第二个视频中,你看到“现实神经说话头部模型的少镜头对抗学习”,其中模型学习拍摄几张图片(帧),并制作一个可以说话的模型(说话头部模型)。这篇论文使用了很多漂亮的技巧,比如元学习和现实主义分数。最后,第三个视频是关于学习和生成人脸的。作者使用了自己的数据集。这类似于我们下面使用自动编码器的方法。我们在项目中采用了一个可变自动编码器(VAE),但这只是一个细节。他很好地解释了让模特学习低维人脸模型的概念。

Text-based Editing of Talking-head Video (SIGGRAPH 2019)

Few-Shot Adversarial Learning of Realistic Neural Talking Head Models

Face embedding with PCA

看了外面的样品,让我们做自己的事。

行动计划

对于本文,我们用于创建人脸嵌入的策略是通过训练卷积自动编码器。我们最终选定了一种可变自动编码器。它基本上是 zip 文件的机器学习版本:你拍摄一幅图像,将其压缩成少量数据,然后尝试使用这些数据来重新绘制原始图像。通过修改压缩数据,您可以调整模型重建图片的方式。该压缩数据成为图像嵌入。我们使用的代码是根据托马斯·德阿纳的优秀文章修改而来的。

我们使用了 UTKFace 数据集(参见本文),而不是像在原始文章中那样使用水果。它被许可用于研究,所以我们在这里,做研究。该数据集的主要优势在于,它包含正确裁剪的对齐人脸,并按种族、年龄和性别进行标记。

使用自动编码器,神经网络的输入和输出在训练期间是相同的。这基本上意味着神经网络需要学习压缩然后解压缩图像,在这样做的过程中,它学习了构成人脸的本质特征。如果它可以学习人脸,那么我们就可以使用训练好的模型来做一些很酷的事情,比如生成新的人脸,在人脸之间变形,给人脸添加概念,比如添加眼镜,或者减去一个性别添加另一个,或者降低年龄,等等。

你会在这篇文章中注意到,我们并没有试图通过贴标签来分类。相反,我们感兴趣的是对数据集中的数据(面孔)分布进行建模,并使用该模型用数学“做事情”。这是无监督学习中的想法:你从数据中学习,而不强迫模型学习数据的标签。

训练模型学习人脸

我和玛丽·凯特·麦克弗森一起做这个项目,我们的第一步是在人脸数据集上训练自动编码器。该结构与原始文章相比几乎没有变化,只是增加了历元的数量,因为这对 faces 数据集有更好的结果。我花了整整两天的时间,试图根据我过去在音频方面的工作编写自己的卷积自动编码器,但结果不如汤姆的 VAE。正如我们在下图中看到的,重建的脸看起来相当不错,尽管肯定还是有些模糊。稍后我们会看到锐化滤波器可以帮助解决这个问题。

Face reconstruction using variational autoencoder. The first row is the input and the second is the resulting output.

Another example of reconstructed faces.

And even more…

代数!对来自验证数据集的人脸进行实验

训练好模型后,是时候对模型没有训练识别的图片进行一些有趣的嵌入实验了。第一个实验是两张脸之间的简单插值。生成这个的等式只是平均两个人脸嵌入,每次迭代加权第二个脸多一点,第一个脸少一点。这得到了一些有趣的结果!

The CEO to CTO morph.

And the gif, for your viewing pleasure.

我们可以拉名人进来!

Me morphing into Keanu Reeves. Because awesome!

下面是一张从我到基努过渡的 gif。看到脸是如何变化的很有趣。小胡子长得很迷人。有些地方有点起伏不定,但这是个好的开始。

The Daniel Shapiro, PhD to Keanu morph as a gif.

这是下一个合乎逻辑的步骤:

Mathieu Lemay to Keanu morph.

我们不得不多做一些,只是为了好玩。

The Sam to Daniel Shapiro, PhD morph. The beard gets weird as it grows in from the outside in and the inside out.

下一个实验是研究面部年龄。这个问题的总方程式是:

“原始脸”——“原始脸年龄的平均脸”+“新年龄的平均脸”

所以要把一个 35 岁的人变成一个 5 岁的孩子,你会说:

I = 35 岁的形象

a = 35 岁人脸嵌入的平均向量

n =岁人脸嵌入的平均向量

当你将向量 i-a+n 推入自动编码器的解码器时,你会得到原始人脸的娃娃脸版本。

Baby face of me. It didn’t quite delete the beard.

这对于非常年轻的脸比非常老的脸更有效,因为图像的模糊性使得很难显示皱纹之类的东西。下图显示的是马修·勒梅变成了一个婴儿。

Mathieu Lemay morphing into a devil baby!

而这里是这个神奇过程的 gif:

Baby eyes do not look good on an adult’s face.

添加眼镜效果很好:

Adding glasses to Mathieu Lemay.

它对我也有效:

Adding glasses to me.

以下是山姆脸的变形,最大限度地增加了女性特征:

现在我们来谈谈如何生成全新的面孔。我们可以在嵌入空间中进行随机行走,得到这样的人脸:

这个模型似乎理解照明:

Animation of generated faces. You can see that the lighting is a concept the neural network seems to understand.

嵌入空间中附近的人脸看起来相似:

A smaller subset of the same pictures to show the similarity between images.

放大时,生成的面会非常细致:

使用后处理步骤来锐化我们的面部生成器的输出看起来更加真实!想象面孔的锐化版本(一个男性;一名女性):

Before

After

Before

After

其他数据集呢?

回想一下,我们写过一篇关于外服一代的文章,Mary Kate MacPherson 想用她策划的数据集尝试这个模型。以下是在外服数据集上训练后的初步结果,动画特征之间的变形:

Waifu morph

一个生成的外服:

外服变形为 gif:

显示组成 gif 的每一帧:

放大 gif,让您更清楚地看到细节:

结论?

我们有更多的材料,可能会进入第二篇文章。我认为这篇文章的结论是,我们可以在图像空间中进行数学运算,并像处理想法一样处理图像,包括年龄修改、面部变形、年龄修改、添加眼镜等功能(正如我们在 DCGAN 论文中看到的那样)、添加女性特征、生成面部、学习照明等等。我们看到这个想法也适用于动画角色,并且输出的真实感可以用经典的图像处理技术来提高,比如锐化滤波器。在本文中,您了解了无监督学习在人脸数据集上的一些背景和应用。如果你喜欢这篇文章,那么看看我的其他一些文章,比如“如何为一个人工智能项目定价”和“如何聘请人工智能顾问”嘿,看看我们的简讯

下次见!

——丹尼尔
Lemay.ai
丹尼尔@lemay.ai

色情图片的面部识别仍然是一个糟糕的主意

原文:https://towardsdatascience.com/facial-recognition-for-porn-is-still-a-terrible-idea-bb5dbb9c0281?source=collection_archive---------18-----------------------

(Adapted from Markus Spiske on Unsplash)

这个人是否真的建造了他声称建造的东西并不重要。

发生了什么事?几周前,一名在德国的中国开发者声称,他开发了一个应用程序,通过与社交媒体上的图像进行交叉检查,使用面部识别技术来识别网络色情中的女性。他的说法最初在中国微博平台微博上疯传,在评论帖子中引起了极大的兴奋。耶鲁大学法学院保罗·蔡中国中心的副研究员秦怡·傅将这个故事上传到推特上,并翻译给国际观众:

一名在德国工作的中国程序员表示,他和一些朋友已经识别出了来自世界各地的 10 万名色情女演员,将色情视频中的面孔与社交媒体上的照片进行交叉引用。目的是帮助其他人检查他们的女朋友是否曾出演过这些电影。''

她进一步报告说,根据最初的微博帖子,来自 PornHub 和 xVideos 等色情网站的超过 100 万亿字节的视频数据与来自脸书、Instagram、抖音、微博和其他社交媒体平台的个人资料图片进行了匹配,以确定 10 万名色情演员。

虽然没有人真正见过这款应用,自称为徐莉的开发者的身份也未得到证实,但这个故事还是流传开来。毕竟是关于色情的。

(Screenshot: Yiqin Fu on Twitter)

在微博上,徐莉向主板证实,他计划在接下来的一周发布一个“数据库模式”和进一步的“技术细节”。这从来没有发生过,因为他取消了他要回答媒体问题的直播。他删除了整个项目及其数据,这更可能是因为这个故事在网上引起的愤怒,而不是出于反省和更好的判断。

社交媒体上关于这个故事的许多讨论都围绕着这个应用程序是否曾经存在和工作过,这个团队在创建它的时候可能看过多少色情内容,以及他们是否有女朋友。不用说——我希望——这个角度分散了人们对将面部识别软件应用于色情内容的关注。

这个故事只是更广泛趋势的一个例子

有人会开发这样一款应用,这既不是不可能的,也不是不可能的。当我们根本不应该感到惊讶的时候,询问他们是否真的开发了这个应用程序,以及它可能或可能没有多准确地工作是假装错误的怀疑。

Motherboard 两年前就警告过这种用例,当时 PornhubxHamster 都开始使用面部识别技术来自动标记和分类他们的视频。他们称之为“等待发生的隐私噩梦”

还记得当 FindFace 被用于 dox 性工作者和色情演员,通过将他们的脸与俄罗斯社交媒体平台 VK 上的个人资料进行匹配?

这个想法既不是新的,也不是原创的。

不是每个人都生活在强有力的数据保护立法之下

徐莉最初的印象是,他的项目不存在法律问题,因为他没有公开数据,而且性工作在德国是合法的。后者是正确的,但与个人(更糟糕的是,生物特征)数据的非自愿收集和处理没有什么关系。

GDPR 定义了许多处理个人数据的许可理由。在这种情况下,显然不存在同意,也不存在任何合同义务或公共利益。当唯一的兴趣是欺骗女性,以便男性可以验证他们的妻子和女朋友是否曾出现在色情作品中时,以合法利益为由立案似乎很困难。

在处理照片时,照片被视为特别敏感的生物特征数据

"通过特定的技术手段对自然人进行独特的识别或认证."

对于那些生活在欧盟和其他保护公民免受这种剥削性数据使用的国家的人来说,这是一个好消息。但是色情和社交媒体平台吸引了全球用户。到目前为止,并不是每个地区都受到强有力的数据保护,而且执行现有的法规通常既耗时又昂贵。

在色情作品的合法性受到限制或不确定的国家,对表演者的影响尤其成问题,在色情作品中表演是非常可耻的,数据保护立法要么不存在,要么不够强大,无法保护这种特殊的使用情况。

性别监控是一个越来越大的威胁

最终,这个项目是关于识别、揭露、羞辱或骚扰从事色情表演的女性。根据 twitter 上的翻译,该应用程序的最初目的是为男性提供一种方法来验证他们的妻子和女朋友是否曾出现在色情作品中。

"目的是帮助其他人检查他们的女朋友是否曾出演过这些电影。"

许多表演者使用艺名,这对于业余表演者和其他需要将自己的演艺事业与个人生活、家庭或其他职业分开的人来说尤为重要。通过面部识别的方式对他们进行 Doxxing 不仅是令人难以置信的侵犯和严重的隐私侵犯,也是性别化的监视。

而且也不是孤立事件。有针对性的在线骚扰既不是始于也不是止于 Gamergate,而是影响妇女和边缘化群体在网上表达自己的自由。所谓的 stalkerware 也是一种增长趋势。也就是说,软件直接或间接销售给虐待者,用于秘密监视配偶或前伴侣。对于任何想了解更多的人来说,Motherboard 在这个话题上有一个优秀的系列

当前关于 deepfakes 的争论是另一个恰当的例子。大多数关于 deepfakes 的研究和报道都集中在使用的方法和 T2 的政治含义上。诚然,这些都是重要的问题。但值得记住的是, deepfake 视频技术的第一个主流用途是将女性的面孔插入色情视频中,以骚扰和羞辱她们。

早在当代监控技术出现之前,女性的身体、参与、运动和自主权就已经受到男性和社会的监视和控制。但是,随着数据量的不断增加和机器学习的进步,性别化的监督出现了更多的方式并被放大,正如监督学者多年来一直指出的那样。

最后,使用类似的算法“永远”也不是解决办法

在对他的项目的强烈反对声中,开发者后来补充道,这款应用也可以被女性用来识别复仇色情。虽然我怀疑这种事后的想法是出于对复仇色情受害者的真正关心,但这在技术上是真的。一种能够通过社交媒体识别色情演员的算法也将发现那些从一开始就不想参与色情活动的女性的档案。

然而,在实践中,风险大于潜在收益。发现复仇色情很少是主要问题。受害者更为挣扎的是从各自的平台上删除图像,在他们的家庭地址和电话号码被公布后的个人安全,通常随之而来的骚扰浪潮的创伤,执法部门的不重视,以及处理这种事件对家人、朋友和工作场所的影响。面部识别对此没有任何帮助。

保护数据,保护隐私,安全分享裸照,是对复仇色情最好的防御。教授和学习如何做这些事情比试图用面部识别软件修复复仇色情片更能利用任何人的时间。

Nicole Shephard 是一名独立研究员、顾问和自由撰稿人,她拥有性别研究博士学位和十年的技术和人力资源工作经验。她写了关于数据和技术的性别政治,以及工作场所的多元化包容。

碎碎念: @kilolo_

如何建立一个微笑探测器

原文:https://towardsdatascience.com/facial-recognition-happiness-bbb3c4293d1d?source=collection_archive---------9-----------------------

Photo by Nathan Dumlao on Unsplash

使用您自己的网络摄像头,并使用 OpenCV 检测快乐

用 Python 建立一个面部识别模型

1.介绍

企业努力提供最重要的产品:幸福。

src: https://cutt.ly/HwrN0KU

为什么?快乐可能不仅仅是一种化学反应。一个快乐的顾客更有可能再次走进这扇门,关于快乐的数据可以帮助企业了解哪些产品会做得更好,并具有更高的保留率。机器可以学习识别快乐,在本教程中,我将向你展示如何创建一个可以识别快乐的面部识别模型。

让我们从基础开始。我们如何表达快乐?在脸上,大多是通过我们的眼神和笑容。当我们看到某人脸上的微笑时,我们会自然而然地推断出他是快乐的。微笑可以有很多变化,但它的形状大多类似于一种扁平的“U”形。

😃

在接下来的步骤中,我将放下我的代码,解释我是如何构建幸福探测器的。要理解算法背后的直觉,请查看之前关于 Viola-Jones 算法的文章。

你需要什么来建造幸福探测器:

我在 Anaconda 上使用 Spyder,但是我想你也可以使用 Jupyter Nb。一旦你拥有了一切,就进入 IDE(代码编辑器)。

如果你使用 spyder,你应该有一个这样的屏幕。
在开始编码之前,请确保下载了 haar cascades(下一节)

2.哈尔瀑布

src: https://cutt.ly/rwrN978

正如我在上面引用的文章中解释的那样,Viola-Jones 算法使用类似 haar 的特征来检测面部属性。级联是一系列过滤器,它们将一个接一个地应用,以通过面部特征来检测面部。

这些过滤器存储在 Haar Cascade GitHub 存储库中它们自己的 XML 文件中。

为了构建我们的幸福探测器,我们需要这 3 个 XML 文件:
-haarcascade _ eye . XML
-haarcascade _ smile . XML
-haarcascade _ frontal face _ default . XML

这些是脸部、眼睛和微笑的层叠。如果我们的形象是一张快乐的脸,我们必须具备这些特征。从每个链接中获取代码,将其放入文本编辑器中,并根据上面提到的名称保存文件。将所有三个 XML 文件放在同一个文件夹中,在这里您将启动一个 python 笔记本。现在我们有了 Haar Cascades,让我们来看看虚拟环境。

— — — — — — —现在,终于到了开始编码的时候了。— — — — — — — —

3.导入 OpenCV 并加载级联

import cv2cascade_face = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')cascade_eye = cv2.CascadeClassifier('haarcascade_eye.xml') cascade_smile = cv2.CascadeClassifier('haarcascade_smile.xml')

唯一需要导入的库是 OpenCV。尽管它是一个非常强大的对象识别工具,但它不是最强大的。有更新更好的,但 OpenCV 仍然提供了很多价值,是理解对象识别基础的好方法。
导入 OpenCV (cv2)后,我调用了下载的每个级联。为此,我只需要使用名为 CascadeClassifier 的 OpenCV 函数。

4.定义面部、眼睛和微笑的检测功能

def detection(grayscale, img):
    face = cascade_face.detectMultiScale(grayscale, 1.3, 5) 
    for (x_face, y_face, w_face, h_face) in face:
        cv2.rectangle(img, (x_face, y_face), (x_face+w_face, y_face+h_face), (255, 130, 0), 2) 

在为检测定义函数之前,您应该知道它将应用于单个图像,然后将这些图像放在一起以获得最终结果。

由于 Viola-Jones 算法适用于灰度图像,因此我将该函数的第一个参数输入为灰度。但是,我也希望最终输出的原始图像是彩色的,所以我为原始图像输入了另一个参数 img
接下来,我需要得到检测人脸的矩形的坐标。为了定义这些坐标,我取了 4 个元组:x,y,w,h.
x & y 是左上角的坐标,w & h 分别是矩形的宽度和高度。我将这些元组存储在变量 face 中,然后使用 OpenCV 的另一个函数detect scale来实际获取这些坐标。因此,我们使用我们的对象 Cascade_face 并将 detectMultiScale 方法应用于它,该方法具有 3 个参数:
-在 b & w
中分析图像时的灰度-比例因子 1.3(图像的大小将减小 1.3 倍)
-可接受的最小邻居区域数:5 个邻居。

接下来,为了实际绘制矩形,我创建了一个“for 循环”,在 faces 中有 4 个元组 x_face,y_face,h_face,w_face。在 for 循环中,我使用了 rectangle 函数,这是另一个 OpenCV 函数。这实际上在你的脸上画出了矩形,并给出了以下参数:
-‘img’因为我们希望矩形画在我们原始的彩色图像上。
-左上角坐标:x 和 y
-右下角坐标:w 和 h
-矩形颜色:我选择了偏蓝的颜色。
-矩形边缘的厚度:我选了 2。(这并不重要,但 2 是个不错的选择)

注意:下面的代码是续篇(我已经把它们都放在底部了)

 ri_grayscale = grayscale[y_face:y_face+h_face, x_face:x_face     + w_face] 
        ri_color = img[y_face:y_face+h_face, x_face:x_face+w_face]

现在我完成了面部,我要检测眼睛。这是唯一有点棘手和难以理解的部分。基本上,上面的几行提到你在面部寻找眼睛,因此面部成为我们的“感兴趣区域”。因为该算法适用于灰度图像,所以我们输入灰度的参数。但是,我也想得到彩色图像,所以我将为彩色图像添加另一个参数。
本质上,有两个感兴趣的区域:一个用于灰度图像,一个用于原始彩色图像。随后,我用矩形 y:y+h 和 x:x+w 的坐标范围在灰度图像上创建ri _ gray,然后,在彩色图像上,我用相同的坐标为矩形创建 ri_color

 eye = cascade_eye.detectMultiScale(ri_grayscale, 1.2, 18)

        for (x_eye, y_eye, w_eye, h_eye) in eye:
            cv2.rectangle(ri_color,(x_eye, y_eye),(x_eye+w_eye, y_eye+h_eye), (0, 180, 60), 2) 

对于眼睛,我对面部重复了第一步,用 cascase_eye 创建了一个名为 eye 的对象,并使用detect scale方法找到 4 个元组。我将比例因子改为 1.2,最小邻居数改为 18。(我不得不试验这些值以获得完美的结果)。

接下来,我也为眼睛创建了一个 for 循环。你可以复制粘贴前一个,只需要改变元组名称,并选择不同的颜色。

smile = cascade_smile.detectMultiScale(ri_grayscale, 1.7, 20) 
        for (x_smile, y_smile, w_smile, h_smile) in smile:
            cv2.rectangle(ri_color,(x_smile, y_smile),(x_smile+w_smile, y_smile+h_smile), (255, 0, 130), 2)

        return img

然后再次为微笑,我重复同样的步骤,我做了脸和眼睛。当使用检测多尺度方法时,我使用了 1.7 的比例因子和 20 的最小邻居。(就像我之前说的,这需要一些实验来弄清楚)。完成所有操作后,您只需返回原始框架。

5.在网络摄像头上显示输出

vc = cv2.VideoCapture(0)while True:
    _, img = vc.read()
    grayscale = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 
    final = detection(grayscale, img) 
    cv2.imshow('Video', final)
    if cv2.waitKey(1) & 0xFF == ord('q'): 
        break vc.release() 
cv2.destroyAllWindows() 

我首先创建了一个名为 vc 的对象,并使用了来自 OpenCV视频捕获类。它只需要一个参数:0 或 1。如果您使用内置网络摄像头,则为 0;如果是外置网络摄像头,则为 1。

由于检测函数只对单个图像起作用,我现在必须创建某种循环来允许它对一系列图像进行检测。所以我开始了一个无限的 while 循环,稍后我将使用一个 break 函数来中断它。接下来的几行可能看起来有些混乱,但是基本上,VideoCapture 的 read 方法得到了两个元素:其中一个是来自网络摄像头的最后一帧。因为你只想要那个,所以使用 _,img ,因为 read 方法返回 2 帧,而你只想要这个。现在我需要从视频捕获类中调用这个方法。

我使用 cvtColor 函数将彩色 img 转换为灰度,因为它需要一个 b & w 帧用于检测功能。我称之为灰度,取 cvtColor 类,取 2 个参数:
-帧(img)

  • cv2。COLOR _ BGR2GRAY 对蓝绿色红色进行平均,以获得正确的灰色阴影。
    现在我创建了一个名为' final 的新变量,这将是 detect 函数的结果。在' final '中,我使用带有参数灰度和 img 的检测函数。

imshow 函数是另一个 OpenCV 函数,它允许我们用矩形动画查看来自网络摄像头的原始素材。它以动画方式显示处理过的图像。

然后我用另一个函数在我用完的时候关闭窗口。我应用了一个 if 条件,每当我按下键盘上的‘q’按钮时,它就会终止应用程序。这将中断 while 循环以结束该过程。
最后,我用释放的方法关闭摄像头,用 DestroyAllWindows 函数终止窗口。

我把我的完整代码粘贴在下面,以防上面的摘录有任何混淆。

6.结果

起初,有很多原因导致这个模型不起作用。它会把一张没有微笑的脸也检测为微笑。为了得到完美的结果,我不得不使用比例因子和最少的邻居。

使用 React 和 face-api.js 的 BNK48 偶像团体面部识别 SPA

原文:https://towardsdatascience.com/facial-recognition-spa-for-bnk48-idol-group-using-react-and-face-api-js-ad62b43ec5b6?source=collection_archive---------6-----------------------

如何零后端代码部署自己的偶像识别系统

如今,人脸检测和识别已经不是什么新鲜事了。一年前,我曾经尝试在 Python 上使用 TensorFlow 和 facenet 制作自己的面部识别系统。该项目旨在从 AKB48 成员的照片中进行人脸检测和识别。

通过看他们的脸,你可能会同意我的观点,任何人都很难记住他们的名字,更不用说识别和区分他们的脸了。(声明一下,我不是粉丝什么的)

Cr: http://akb48.wikia.com

我的 Python 项目进行得非常好。我可以在 Jupyter-Notebook 中编写代码,从任何输入图像中检测和识别成员。然而,该系统是在 Python 环境下运行的,这对于大多数人脸识别系统来说是很正常的。但这意味着,如果我想从其他设备输入图像,例如智能手机,我需要创建前端来连接和发送图像到 Python 后端,以处理面部检测和识别。(大量工作要做)

由于缺乏动力,项目被搁置,一年过去了。直到我找到了文森特·穆勒face-api.js ,一个用 TensorFlow.js 进行人脸检测和识别的 JavaScript API。现在可以在浏览器上运行深度学习的所有过程,而不需要后端代码。(声音好听!)

是的,不需要任何后端编码或环境设置!!你需要的只是一个静态的虚拟主机。它可以在任何设备或浏览器上运行。(如果你的浏览器可以运行 TensorFlow.js,就这些)在本文的最后,我会向你展示如何将这个 React app 部署到 Github 页面。

又一个偶像团体:BNK48

这次回到我的项目,我决定使用另一个偶像团体的照片, BNK48 ,AKB48 的姐妹乐队,总部设在曼谷。

我来告诉你为什么偶像组合是实验面部识别 app 的好选择。(再次声明,我不是粉丝。)这是因为我们将需要许多已知的面孔和名字来测试我们的系统,对吗?使用 5-10 张照片来测试系统可能很容易,但在现实世界中,你不会为仅仅 10 个人制作人脸识别系统,对吗?这就是为什么偶像团体的 30-50 名成员是一个很好的测试数字。(不算少,也不算多)我们可以很容易地从网上找到他们各种视角的肖像照片,尤其是他们的 facebook 粉丝页面。

我们要做什么?

在这里,在这个项目中,我们将使用 React 和 face-api.js 库制作单页 App 来检测和识别偶像人脸。由于 Vincent 在他的 API 中为我们完成了所有困难的部分,该 API 带有预先训练的人脸检测、人脸标志、人脸对齐和人脸识别模型,所以我们不必自己训练模型。我们甚至也不需要用 TensorFlow 写 DL 模型。事实上,你真的不需要知道深度学习或 CNN 如何制作这个应用程序。你所需要知道的至少是 JavaScript 和 React 的基本概念。

如果你迫不及待想看看它的样子,请访问我的演示页面这里。而我的 App 完整回购就是这里。我们将在本教程中制作的代码将会更简单,但是不要担心,我也会在另一个 repo 中分享它。

人脸识别系统简介

如果您已经知道它是如何工作,或者不是很关心,您可以直接进入编码部分。

现在,让我们想象一下,当你去某个政府办公室索要一份你的个人文件时。柜台后面的工作人员通常会要求你证明你是谁。你给他们看你的身份证。她看着你的名字和照片,然后检查你的脸,确保你就是你声称的那个人。

同样,面部识别系统应该已经存储了您的姓名以及您的参考面部信息。然后,当你输入另一张照片进行识别时,系统将首先尝试检测图像上是否存在任何人脸,在这一步人脸检测网络将完成这项工作。我在这个项目中使用的模型是微型人脸检测器,因为它体积小,便于移动。(API 还为面部检测器提供了 SSD mobileNetMTCNN ,但现在让我们忘记它们。)

回到我们的系统。一旦检测到人脸,人脸检测器模型将返回每个人脸的边界框,告诉我们人脸在图像中的位置。然后,我们使用人脸标志网络来标记 68 个点的人脸标志,并在馈送到人脸识别网络之前使用对齐模型来确保人脸居中。

人脸识别网络是另一个神经网络(准确地说, RestNet-34 类似于神经网络)返回一个人脸描述符(特征向量包含 128 个值),我们可以用它来比较和识别图像中的人。

就像指纹一样,人脸描述符是每张人脸的唯一值。当我们比较来自不同图像源的同一个人的面部描述符时,它们应该非常接近。在这个项目中我们用欧几里德距离来比较。如果距离小于我们设置的阈值,我们确定他们很可能是同一个人。(距离越小,自信越高)

通常情况下,系统会将每个人的脸部描述符作为参考,同时将他或她的名字作为标签保存。当我们输入查询图像时,系统会将新图像的人脸描述符与所有参考描述符进行比较,并识别出最低的人。如果比较结果都不低于阈值,此人将被识别为未知

开始编码吧!

有 2 个功能,我们希望在这个应用程序中实现。一种是从输入图像文件中识别偶像,另一种是使用实况视频作为输入。

先从create-react-app开始,安装react-router-dom,启动 App。

npx create-react-app react-face-recognition
cd react-face-recognition
npm i react-router-domnpm start

打开浏览器,进入 http://localhost:3000/ 如果你看到带有 React 标志的起始页,那么你就可以继续了。现在用你喜欢的任何代码编辑器打开项目文件夹。您应该会看到这样的文件夹结构。

react-face-recognition 
├── README.md 
├── node_modules 
├── package.json 
├── .gitignore 
├── public 
│   ├── favicon.ico 
│   ├── index.html 
│   └── manifest.json 
└── src 
    ├── App.css 
    ├── App.js 
    ├── App.test.js 
    ├── index.css 
    ├── index.js 
    ├── logo.svg 
    └── serviceWorker.js

现在转到src/App.js,用下面的代码替换代码。

src/App.js

我们这里只有导入Home组件并创建一条到"/"的路径作为我们的登陆页面。我们将很快创建这个组件。

让我们从创建新文件夹src/views开始,在这个文件夹中创建新文件Home.js。然后将下面的代码放到文件中并保存。

src/views/Home.js

我们只创建 2 个链接,分别是Photo Input链接到"localhost:3000/photo"Video Camera链接到"localhost:3000/camera。如果一切顺利,我们应该在登录页面看到如下内容。

Landing Page

Face API

在我们继续创建新页面之前,我们希望安装 face-api.js 并创建我们的 api 文件来连接与 API 的反应。现在回到控制台并安装库。

npm i face-api.js

该库附带 TensorFlow.js 和我们想要的所有组件,除了模型权重。如果你不知道它们是什么,模型权重是已经用大型数据集训练过的神经网络权重,在这种情况下,是成千上万的人脸图像。

由于许多聪明人已经为我们训练了模型,我们需要做的只是掌握我们想要使用的必要权重,并手动输入我们的项目。

你会在这里找到这个 API 的所有权重。现在让我们新建一个文件夹public/models来放置所有的模型权重。然后下载所有必要的重量到下面的文件夹。(正如我告诉你的,我们将在这个项目中使用微型人脸检测器型号,所以我们不需要 SSD MobileNetMTCNN 型号。)

Necessary Models

确保您将所有重量放在下面的public/models文件夹中,否则没有合适的重量,我们的模型将无法工作。

react-face-recognition 
├── README.md 
├── node_modules 
├── package.json 
├── .gitignore 
├── public 
│   ├── models
│   │   ├── face_landmark_68_tiny_model-shard1
│   │   ├── face_landmark_68_tiny_model-weights_manifest.json
│   │   ├── face_recognition_model-shard1
│   │   ├── face_recognition_model-shard2
│   │   ├── face_recognition_model-weights_manifest.json
│   │   ├── tiny_face_detector_model-shard1
│   │   └── tiny_face_detector_model-weights_manifest.json
│   ├── favicon.ico 
│   ├── index.html 
│   └── manifest.json

现在返回并为 API 创建新文件夹src/api,并在文件夹内创建新文件face.js。我们要做的是加载模型并创建函数来将图像提供给 API 并返回所有的人脸描述,还可以比较描述符来识别人脸。稍后我们将导出这些函数并在 React 组件中使用。

src/api/face.js

这个 API 文件有两个重要的部分。第一个是用函数loadModels()加载模型和权重。我们在这一步只加载微小人脸检测器模型、人脸标志微小模型和人脸识别模型。

另一部分是函数getFullFaceDescription(),其接收图像斑点作为输入,并返回全脸描述。该函数使用 API 函数faceapi.fetchImage()将图像 blob 提取到 API。然后faceapi.detectAllFaces()将获取该图像并找到图像中的所有人脸,然后.withFaceLandmarks()将绘制 68 个人脸标志,然后使用.withFaceDescriptors()返回 128 个值的人脸特征作为Float32Array

值得一提的是,我使用 image inputSize 512 像素进行图像输入,稍后将使用 160 像素进行视频输入。这是 API 推荐的。

现在我要你把下面的图片保存到新文件夹src/img中,并命名为test.jpg。这将是我们的测试图像来测试我们的应用程序。(以防你不知道,她是 Cherprang,顺便说一下,是 BNK48 的成员。)

Save this image as src/img/test.jpg

让我们创建新文件src/views/ImageInput.js。这将是视图组件输入和显示我们的图像文件。

src/views/ImageInput.js

此时,该组件将只显示测试图像src/img/test.jpg,并开始将 API 模型加载到您的浏览器中,这将花费几秒钟的时间。之后,图像将被输入 API 以获得完整的面部描述。我们可以将返回的fullDesc存储在state中以备后用,也可以在 console.log 中看到它的详细信息

但是在此之前,我们必须将ImageInput组件导入到我们的src/App.js文件中。并为/photo创建新的Route。开始了。

src/App.js with new Route and Component

现在,如果您转到登录页面[http://localhost:3000](http://localhost:3000)并点击Photo Input,您应该会看到照片显示。如果你检查你的浏览器控制台,你应该看到这个图片的全脸描述如下。

面部检测盒

如你所见,描述包含了我们在这个项目中需要的所有人脸信息,包括descriptordetectiondetection内有坐标x``y``top``bottom``left``right``height``width等方框信息。

face-api.js 库自带函数用 html 画布画人脸检测框,真的很好看。但是既然我们用的是 React,为什么不用 CSS 来画人脸检测框呢,这样我们就可以用 React 的方式来管理框和识别显示了。

我们想要做的是使用检测的box信息在图像上叠加人脸框。我们还可以稍后显示应用程序识别的每张脸的名称。这就是我如何将drawBox添加到ImageInput组件中。

让我们一起添加input标签,这样我们就可以改变输入图像。

src/views/ImageInput.js

在 React 中使用内联 CSS,我们可以像这样放置所有的面部框来覆盖图像。如果您尝试用更多面孔来更改照片,您也将能够看到更多框。

面部识别

现在有趣的部分来了。为了识别一个人,我们需要至少一个参考图像来从图像中提取 128 个特征向量值或descriptor

API 具有函数LabeledFaceDescriptors来为我们想要识别每个人创建描述符和名字的标签。该标签将与查询的descriptor一起提供给 API 以匹配人员。但在此之前,我们需要准备一个名称和描述符的配置文件。

平面轮廓

我们已经有一个 Cherprang 的图像参考。因此,让我们使用它的descriptor来制作一个配置文件。

我们现在要做的是创建新的 JSON 文件和文件夹src/descriptors/bnk48.json。该文件将包含成员姓名和参考照片中的描述符。这是第一个只有一个descriptor的样本文件。

Sample face profile

如果我们有所有成员的照片,我们可以添加descriptor并逐一命名来完成我们的面部轮廓。你知道吗?我已经做了一个。我用每个成员的 5-10 张照片来创建这个完整的面部轮廓。所以,你可以下载这个文件并替换src/descriptors/bnk48.json,很简单。(抱歉,我用泰语和平假名作为显示名称)

整个成员的文件大小在 1MB 左右,对于我们的测试 App 来说还不错。但是在现实世界中,您可能需要将所有面部轮廓存储在数据库中,这样您就不必再担心文件大小,但是您将需要使用服务器端来运行面部识别过程。

面部匹配器

下一步我们要为人脸识别任务创建labeledDescriptorsfaceMatcher。现在回到src/api/face.js,然后将下面的函数添加到你的文件中。

src/api/face.js add function createMatcher

该函数将接收面部轮廓(JSON 文件)作为输入,并创建每个成员的描述符的labeledDescriptors,以其名称作为标签。然后我们可以创建并导出带标签的faceMatcher

你可能会注意到我们配置了maxDescriptorDistance 0.5。这是欧氏距离的阈值,用来确定引用描述符和查询描述符是否足够接近,可以说点什么。API 默认值为 0.6,对于一般情况来说已经足够了。但我发现 0.5 对我来说更精确,误差更小,因为一些偶像的脸非常相似。如何调整这个参数取决于您。

既然我们的函数已经准备好了,让我们回到src/views/ImageInput.js来完成我们的代码。这是我们的最后一个。

Final code for ImageInput.js

在这个最终代码中,我们从face.js导入createMatcher函数,并用我们准备好的面部轮廓创建faceMatcher。内部函数handleImage(),我们从图像中得到fullDesc后,绘制出descriptors,找到每张脸的最佳match

然后,我们使用p标签和 CSS 在每个人脸检测框下显示我们的最佳匹配。就像这样。

Face detect and recognize correctly

如果您已经下载了完整的面部轮廓。你可以试着用这个改变形象。我希望你能看到正确匹配检测到的所有人脸!!

Try this image

实时视频输入

本节将指导您使用 React-webcam 将实时视频作为 face-api.js 的输入。让我们从安装库开始。

npm i react-webcam

同样,在制作新的视图组件之前,我们还需要在/src/App.js中添加一个用于视频输入的Route。我们将很快创建VideoInput组件。

Add VideoInput Component and Route

视频输入组件

让我们创建新文件src/views/VideoInput.js并将下面的所有代码放入文件并保存。这是这个组件的完整代码。(不再按部就班。解释如下。)

人脸检测和识别的所有机制与ImageInput组件相同,除了输入是每隔 1500 毫秒从网络摄像头捕获的屏幕截图。

我将屏幕尺寸设置为 420x420 像素,但您可以尝试更小或更大的尺寸。(尺寸越大,处理人脸检测所需的时间越长)

内部功能setInputDevice我只是检查设备是否有 1 个或 2 个摄像头(或更多)。如果只有一个摄像头,我们的应用程序将假设它是一台 PC,然后我们将从网络摄像头facingMode: user捕捉,但如果有两个或更多,那么它可能是智能手机,然后我们将从背面用摄像头捕捉facingMode: { exact: ‘environment’ }

我使用与组件ImageInput相同的函数来绘制人脸检测框。其实我们可以把它做成另一个组件,这样就不用重复两遍了。

现在我们的应用程序完成了。你可以用你的脸测试 VideoInput,但它很可能会把你识别为unknown或者有时会错误地把你识别为某个偶像。这是因为如果欧几里德距离小于 0.5,系统将尝试识别所有的脸。

结论和经验教训

该应用程序可以相当准确地检测和识别偶像的脸,但仍有一些错误时有发生。这是因为拍摄对象可能没有直接面对相机,他们的脸可能会倾斜,或者照片被其他一些应用程序编辑过。

一些偶像可能长得很像,这让 App 有时会混淆。我发现,当来自不同的来源或光线设置时,偶像的脸会有所不同。戴眼镜或浓妆艳抹的偶像也会让我们的应用程序感到困惑。

我不得不承认这个系统并不完美,但仍有改进的余地。

我在 Chrome 和 Safari 上进行了测试,在 PC 上运行良好。我认为它应该也能在 IE 或 Firefox 上运行。用 Android 智能手机测试图像输入和视频输入都很好,但 react-webcam 由于安全问题不能与 iPhone 一起工作,我仍在寻找解决方法。老式手机往往无法与 TensorFlow 一起正常工作,因为它需要足够的计算能力来运行神经网络。

部署到 Github 页面

您可以将这个应用程序部署到任何静态主机,但是本节将指导您使用一些技巧将这个 React 应用程序部署到 Github 页面。你需要有 Github 帐户。如果你没有,去做一个。它是免费的。

首先,让我们安装gh-pages库。

npm i gh-pages

然后我们需要像这样在src/App.jscreateHistory()里面加上{ basename: process.env.PUBLIC_URL }

现在转到您的 Github 并创建一个名为 App name 的新存储库,在我们的例子中是react-face-recognition,然后复制 git URL,稍后添加到我们的项目中。接下来,打开package.json,像这样用你的 Github 账号和 App 名添加"homepage"

"homepage": "http://YOUR_GITHUB_ACCOUNT.github.io/react-face-recognition"

暂时不要关闭package.json文件,因为我们会像这样在"scripts"下添加predeploydeploy命令行。

"scripts": {
  "start": "react-scripts start",
  "build": "react-scripts build",
  "test": "react-scripts test",
  "eject": "react-scripts eject",
  "predeploy": "npm run build",
  "deploy": "gh-pages -d build"
}

现在您可以保存文件并返回到您的控制台终端,然后运行 git 命令将代码上传到您的 Github 存储库,并运行npm run deploy部署到 Github 页面。该页面应使用您设置为http://YOUR_GITHUB_ACCOUNT.github.io/react-face-recognition的 URL 发布

git add .
git commit -m "make something good"
git remote add origin https://github.com/YOUR_GITHUB_ACCOUNT/react-face-recognition.git
git push -u origin master

npm run deploy

你可以在这里查看本教程的 Github 页面,也可以完成回购

我希望你喜欢我的教程,并尝试让你自己的反应面部识别。如果你觉得这个教程很简单,想看更完整的版本,请访问我的演示页面这里,还有回购

面对神经网络的 ARIMA 模型

原文:https://towardsdatascience.com/facing-the-arima-model-against-neural-networks-745ba5a933ca?source=collection_archive---------10-----------------------

北京污染的时间序列预测

Photo by Ling Tang on Unsplash

简介

T 这个小项目的目的是通过 ARIMA 模型来评估它在单变量数据集中的表现。此外,它的性能将与其他技术进行比较,这些技术目前可用于使用神经网络创建时间序列预测。

将要使用的数据集属于 http://archive.ics.uci.edu/ml/datasets/Beijing+PM2.5+Data 的 UCI 知识库()

它每小时测量北京空气中的 PM2.5 含量。这个数据集带有其他信息,如该城市的气象数据。然而,由于本练习的目的是从单变量变量建立模型,因此不考虑其他信息。
我们将一步一步地介绍整个过程:从导入数据开始,获得一些见解,应用 ARIMA 模型,最后将结果与神经网络进行比较,以评估每个模型的性能。

(披露)
本帖由预测时间序列的不同方法组成。然而,这些方法中没有一个是完美的,因为没有完美的方法来预测未来,所以这些结果应该小心对待,并总是听取专家的建议。

如果你要找的代码在 Jupyter 笔记本里这里

理解数据

正如我之前提到的,这个数据集包含了关于北京空气中 PM2.5 (ug/m)浓度的信息。

这些是漂浮在空气中的直径小于 2.5 微米的颗粒。
(https://www . who . int/news-room/fact-sheets/detail/ambient-(outdoor)-air-quality-and-health))
世界卫生组织声明,接触这些颗粒物会导致心血管和呼吸系统疾病以及癌症。他们估计,这种空气污染在 2016 年每年导致 420 万人过早死亡。

探索性数据分析

快速浏览一下数据,看看我们需要处理什么。

关于第一个图,我们可以看到,在每年的年底和年初,PM2.5 颗粒的浓度高于一年中的其他时期,但它仍然是模糊的数据,我们几乎无法更好地了解实际发生的情况。因此,需要进行一些转换,以便能够更深入地了解我们的数据。有许多工具可用,这里我们只使用两个,但是还有许多其他工具可以提供更好或不同的见解。

按不同时间段分组

第一种方法简单到按周或月计算数据测量的平均值。

Weekly average.

Monthly average,

正如我们从图中看到的,我们使用这种方法从数据中获得的信息不是很清楚。

移动平均线

这一次,我们仍将使用平均值,但方式不同,使用移动平均值,计算 N 个给定时间步长的平均值。它将平滑数据,让观众推断出一些可见的模式或趋势。

Weekly Moving average

在最后这张图中,我们可以清楚地发现一些规律。然而,这些方法对异常值非常敏感,正如我们在第一张图中看到的,我们的数据中有许多异常值。

请注意这两种转换的区别,因为它们看起来相似,但并不相同。在平均值中,我们只计算每周的平均值,在移动平均值(MA)中,我们计算 N 个滞后观察值的平均值。不过,第二种方法更有用,因为它可以帮助我们平滑数据,去除噪声。

第一个结论

尽管数据中似乎有许多异常值,但我们从数据中得不到任何明确的信息。人们可以认为这些异常值是与任何种类的故障相关的设备的错误测量。然而,做一些研究我们会发现,这些水平已经达到,并记录在中国的首都。

[## 如果你要去北京,最好的建议是:不要呼吸

由于测量污染的仪器打破所有记录,儿童和老人被敦促留在室内

www.theguardian.com](https://www.theguardian.com/world/2013/jan/13/beijing-breathe-pollution)

离最后几周越来越近

到目前为止,我们评估的数据看起来相当混乱,由于这个小项目的目标是应用不同的预测技术,我们将在整个数据集的最后四周集中精力。通过这样做,可视化将变得更容易,我们将清楚地看到预测如何符合我们的数据。

The caption of the last four weeks

在数据的分布中,我们可以看到大多数数据都分组在第一个值中,看起来像指数分布。

在这个数据集中,有 716 个粒子浓度的测量值,大约一个月的数据。平均每小时 78 个粒子。这种方法被认为是不健康的,人们不应该长时间暴露在这些水平下。

平稳性

在应用任何统计模型之前,检查我们的数据是否被认为是平稳的是很重要的

平稳性基本上意味着均值和方差等属性不会随时间而改变。
(https://www . ITL . NIST . gov/div 898/handbook/PMC/section 4/PMC 442 . htm)

有各种方法来检查数据是否是平稳的,一种好的方法是 Dickey-Fuller 测试,该测试表明,如果 p 值低于给定的阈值,则认为它不是平稳的。

测试迪基-富勒

正如我们用 Dickey-Fuller 检验证明的那样,幸运的是,我们的数据是稳定的,这意味着它没有任何趋势,因此不需要任何转换,我们可以直接应用 ARIMA。

有时时间序列是非平稳的。对于这些情况,可以对数据进行变换,使其保持不变。

ARIMA

在通过 ARIMA 之前,我们将拆分有助于我们训练模型的数据,然后评估它与测试数据集的准确性。

train_dataset = last_weeks['2014-12': '2014-12-29']
test_dataset = last_weeks['2014-12-30': '2014']

训练数据集有 29 天的数据,测试集有 2 天的数据。

分割数据集的目的是因为模型必须用一些标记数据进行测试,这意味着要看预测如何接近真实数据。

ARIMA 代表自回归综合移动平均线。它用于时间序列预测。

它包含三个不同的组件。自回归将时间序列回归到自己身上,综合(I)成分是为了校正数据的非平稳性。最后一个分量移动平均(MA)根据过去的误差对误差进行建模。

每个分量接收不同的参数 AR(p),I(d),MA(q)。为了估计每个参数的值,我们需要得到自相关函数(ACF)和偏自相关函数(PACF)

自相关函数(ACF)和偏自相关函数(PACF)

这个函数将告诉我们在时间序列中观察值的相关程度。

此外,主要目的是让我们知道哪些是在 ARIMA 模型中使用的最佳系数。

在我们的例子中,是 PACF 的模式(在第一或第二个滞后期有显著的相关性,随后是不显著的相关性)。PACF 中显著相关的数量告诉我们自回归(AR)模型的术语。在我们的例子中,系数是 3。

然而,基于 ACF 的模式(函数逐渐减少),我们无法推断移动平均线(MA)的项,因此最佳选择是使用零。

应用 ARIMA

第一个图显示了我们数据的残差,我们可以观察到大部分数据分布在零附近。让我们更详细地看看这是如何分布的。

正如我们所见,它具有与正态分布或高斯分布相似的形状(对称钟形曲线 )

得到一些预测

现在是时候从模型中获得一些预测,以评估我们的模型有多准确,我们将使用我们制作的测试数据集。
此外,我们需要执行滚动预测,这意味着在计算每个预测后添加它并重新计算模型。

Predictions compared with the test dataset

在上面的图中,我们可以看到测试数据的结果,看起来非常吻合!

评估

计算均方误差以获得精度

MSE: 338.9

神经网络

现在将应用神经网络模型,具体来说,我们将使用递归神经网络

递归神经网络(RNN)

RNN 是一种神经网络,主要用于自然语言处理和时间序列预测。这些类型背后的基本思想是每个神经元的状态被存储并反馈给下一层。这就是为什么它在 NLP 和时间序列中如此使用的原因,因为它在计算上一个观察值时预测下一个观察值。

有一种类型的 RNN 被称为长短期记忆,它被提出来解决所有 RNN 提出的一个巨大的问题,这被称为消失梯度问题,但我们不打算进入这种细节。这就是我们需要知道的应用于数据集的全部内容。

这一部分将比前一部分短得多,因为基本上应用神经网络比应用 ARIMA 模型更容易

为了举例,我们将在最后四周工作。

此外,用于训练和测试网络的数据集将是相同的,以便正确地比较这两个模型。

为了在我们的网络中有更好的准确性,我们需要首先标准化我们的数据值。

现在我们的数据集在 0 和 1 之间。

递归神经网络根据 X 个滞后观测值预测新的观测值。对于我们的例子,让我们试着用 168 个滞后的观察值来得到一个新的预测。

现在是时候创建神经网络的架构了!我们将使用 Keras,这是一个流行的框架,它是在 tensor flow(Google 发布的神经网络库)之上制作的。Keras 之所以如此出名,是因为它让建立网络的任务变得非常容易。

现在是时候创建神经网络的架构了!我们将使用 Keras,这是一个流行的框架,它是在 tensor flow(Google 发布的神经网络库)之上制作的。Keras 之所以如此出名,是因为它让建立网络的任务变得非常容易。

网络的架构

我们的网络由 LSTM 层构成,它包含五层,每层有 100×200×300×200×100 个神经元。
我用掉线来提高精度,减少历元后的损失。
为了计算网络的损耗,我使用了和之前一样的损耗函数,均方误差(MSE)函数。

regressor.fit(X_train, y_train, epochs = 100, batch_size = 32)

现在,该模型符合我们的训练数据集模型,是时候创建预测了。

幸运的是,看起来预测很好地符合真实值,但是 MSE 告诉了一件不同的事情。

MSE: 409.1

看起来我们有一个赢家,因为我们可以看到神经网络的结果比 ARIMA 得到的结果更差!

结论

D 在这个实际案例中,我们看到了许多不同的事情,这些事情是从统计模型和时间序列操作的不同概念(不同的变换、时间序列的平稳性、自相关函数)开始的。接着是一个统计模型(ARIMA)和应用于我们特定数据集的递归神经网络。

我们可以看到,与神经网络提供的 409.1 相比,ARIMA 对我们的测试集表现更好,具有更小的均方差 338.96

还有一些其他的东西可以尝试,但是对于这个有大约 720 个观察值的特定数据集,代价是统计模型!

未来项目

一旦理解了这一点,首先想到的是将相同的方法应用于整个数据集(超过 41000 个观察值)。在这里,我们将看到哪一个在数据量更大的情况下表现更好。

下一步(仍然使用相同的数据集)是使用其他变量来创建预测。一开始,我们去掉了大约 7 个不同的变量,这些变量包含了我们模型中没有用到的有价值的信息。

我们也可以改进神经网络的结构,直到我们达到一个最佳点。还有另一种统计模型,如季节性 Arima (SARIMA ),也许可以改进这些指标。

最后,所有这些研究的目的是从数据中提取有用的见解,在我们的例子中,这将是关于北京的 PM2.5 水平。

…但是让我们在新的帖子中看到所有这些东西。

参考

https://towards data science . com/an-end-to-end-to-end-project-on-time-series-analysis-and-forecasting-with-python-4835 e6bf 050 b

https://towards data science . com/forecasting-exchange-rates-using-ARIMA-in-python-f 032 f 313 fc 56

https://www . ka ggle . com/ber hag/CO2-emission-forecast-with-python-seasonal-ARIMA/data

https://machinelingmastery . com/ARIMA-for-time-series-forecasting-with-python/

https://www.udemy.com/deeplearning/

因素分析 101

原文:https://towardsdatascience.com/factor-analysis-101-31710b7cadff?source=collection_archive---------8-----------------------

我们能减少数据集中的变量数量吗?

Not related to Factor Analysis at all.

尽管这是对因子分析的介绍,但我假设读者具有一些数学和统计学知识,并且希望了解多元统计分析。这篇文章将集中在对因子分析的理论理解上,因此我将省略与这个主题相关的代码——除了一个简单的 R 说明。

如果你喜欢这篇文章并想了解更多,请务必关注我的简介 !

我们在谈论什么?

在现实生活中,数据往往遵循一些模式,但从数据分析开始,原因并不明显。因素分析的基本目的是用一些潜在的和不可观察的随机成分来描述几个变量之间的协方差关系,我们将这些成分称为因素

我们将假设变量可以通过查看它们的相关性来分组。也就是说,假设特定组中的所有变量彼此之间具有高相关性,但是与其他组中的变量具有低相关性。在这种情况下,我们可以把每组变量看作是一个单一的基本结构的代表,或者是一个因素,它负责观察到的相关性。

例如,来自包括英语、数学和生物分数的变量组的相关性可能来自潜在的“智力因素”,而代表健康分数的另一组变量可能对应于另一个潜在因素。
因子分析是主成分分析(PCA)的延伸。两个模型都试图逼近协方差矩阵σ,但是因子分析质疑数据是否符合某种规定的结构。如果你没有听说过 PCA,我推荐这篇用 R:

[## 主成分分析(PCA) 101,使用 R

一次提高一个维度的可预测性和分类能力!使用 2D 图“可视化”30 个维度!

towardsdatascience.com](/principal-component-analysis-pca-101-using-r-361f4c53a9ff)

所以让我们来看看数学!

矩阵形式的因子分析模型为:

X 是一个可观测的随机向量,有 p 个分量,均值向量 μ 和协方差矩阵σ。因子模型说 X对少数 m 个不可观测随机变量的线性依赖

称为的公因子p 的变异源,

称为误差或特定因素。系数 l _ ij 称为变量 i 对因子 j加载量,这样矩阵 L 就是因子加载量的矩阵(我用“_”表示小写字母)。
具体因素
Iϵ_I只与 i 变量 X _ i 关联。 p 偏差、

用随机变量 p + m 表示,

这些都是不可观察的。这就是因子模型与多元回归模型的不同之处,在多元回归模型中可以观察到独立变量。
所有这些不可观察的成分都带来了一些复杂性。如果我们有不可观察的成分,我们不能直接验证因子模型,但是通过对 Fϵ 的一些假设,模型实现了协方差关系,我们可以尝试解释它们。

然后我们将假设 Fϵ 的平均值和协方差为:

Fϵ 是独立,所以 Cov( ϵf)= e(ϵf’)= 0。
这里的 Cov( ϵ )包括ψ是每个变量的唯一性,即不能用任何因子解释的方差的量。

如果不可观测的随机向量 Fϵ 满足这些条件,我们就有了一个具有 m 个公因子的因子模型:

该模型的协方差结构为:

因子模型在公因子上是线性的。如果 X 与底层因素的关系不是线性的,那么上面的协方差结构是不充分的。

但是等等,还有更多!

i 个变量由 m 个公因子贡献的方差比例称为 i个公因子,h _i .因特定因子而产生的 Var(X I)=σii的比例就是唯一性或特定方差。我们将会看到:

(即 Var(X_i) =公度+特定方差)
或者说

i 个公因子则是第 i 个变量在第 m 个公因子上的负载的平方和。

最大似然估计

如果共同因素和特定因素可以假设正态分布,我们可以使用最大似然法的因素负荷和具体的变化。似然函数并不漂亮,这里就不收录了,但是要明白它取决于 Lψ通过σ=ll^t+ψ。由于对 L 有许多选择,所以没有定义这个模型,这就是唯一性条件的用处:

这是一个对角矩阵,它确保了一个最大值,因此可以找到^ L**ψ**的估计值(我将在字母前使用来表示“帽子”运算符)。
从这里开始,第 j 个因子中包含的总方差的比例可以用估计的载荷来解释。这里的问题是因子载荷的最大似然解受到唯一性条件的限制,并且在因子旋转之前很难看到它们的清晰模式。

我说的旋转是什么意思?

我们的结构是正交(例如,如果两个向量形成 90 度角,则认为它们是正交的)。因子载荷的正交变换(因此意味着因子的正交旋转)称为因子旋转。如果^ L 是估计的因素负荷矩阵,那么

是“旋转”载荷的矩阵。估计的协方差矩阵保持不变,这种旋转可以给出更简单的结构,并使因子更容易解释,就像显微镜一样。分析解决方案是 Varimax 标准(Google it ),它选择正交变换来实现最大化。因子旋转尤其适用于 MLE 的估计,因为它使用了唯一性条件。

太好了!我们躲过了理论部分,所以让我们看看 r 中的一个例子。

好的,那么我们刚刚花了 10 分钟读了什么?处理多维数据可能会很困难,所以如果我们能“削减”维度的数量,我们就能使数据更容易查看,并且我们可以主观地尝试解释潜在的因素。

我们将看看你能得到的奥林匹克十项全能数据(约翰逊和威彻恩,第 499 页)

od.data <- read.table("http://static.lib.virginia.edu/statlab/materials/data/decathlon.dat")

编辑:如果您是第一次尝试,如果您没有添加矩阵翻译,您将会得到一个错误。这通过以下方式实现:

od.data <- as.matrix(od.data)
rownames(od.data) <- colnames(od.data) <- c("100m","LJ","SP","HJ","400m","100mH","DS","PV","JV","1500m")

首先让我们尝试 3 个因素。输出是:

默认情况下,factanal提供 ML 估计和 Varimax 旋转。
首先是独特性。它代表早期的^ψ。如果唯一性很高,方差就不能用任何一个因子来描述,因此命名为唯一性或特定方差。这不能用一些潜在的因素来解释。这是 1500 米跑的情况,反之亦然。如果我们从 1 中减去唯一性,我们得到公度,然后它会告诉多少方差是由 3 个因素解释的。

负载代表之前的^l。我对数据进行了分类,我们可以很容易地看到推铅球(SP)和铁饼(DS)相对于因子 1 中的其他变量具有较高的负荷,这意味着该因子的名称“臂力”。具体来说,这意味着例如推铅球与因子 1 的相关性为 0.915,与其他两个因子的相关性较小。

我们还对“累积 var”感兴趣,即解释的方差的累积比例,它应该是一个“高”数,其中高是主观的。我们可能应该尝试拟合 4 个因子,因为我不认为 0.554 那么高,我们可能会做得更好。最后的低 p 值也拒绝了 3 个因子就足够的假设。
现在的输出是:

这看起来更有希望。4 个因子就足够了(根据 p 值),我们已经成功地将变量的数量从 10 个减少到 4 个。对这些因素的解释是主观的,我们不能明确地给它们命名,但是通过查看变量的高相关性,一个好的猜测可能是“臂力”、“腿力”、“速度/加速度”和“跑步耐力”。标枪和 100 米栏有很高的独特性,不适合任何一个因素。可能在因素 1 和因素 2 中各有一点。

最重要的部分是找到因子的个数。从这里开始,你可以使用估算和旋转的方法,并锐化显微镜的焦点,但这并不重要。

结束语

Johnson 和 Wichern 指出“绝大多数尝试的因素分析没有产生明确的结果。”(第 526 页)不能保证因素分析会导致对有意义因素的满意描述。如果你发现自己对因子分析的结果感到困惑,因为它似乎没有“工作”,很有可能你没有做错什么,因子分析只是没有发现任何有趣的东西。

此外,如果你试图测量诸如“智力”、“同情心”或“潜力”之类的数量,因素分析可能会导致争议。这些量很难测量,它们能对我们知道可以测量的事件负责吗,比如 SAT 高分?因子分析无法回答这个问题。
因素分析的质量更多地取决于“哇”的标准,因为质量还没有量化,如果你能说“哇,我理解这些因素”,那么应用程序可能已经成功了。

正如我之前所说,累积方差是一个主观的衡量标准,很难知道 0.621 是好是坏,直觉上,60%可能不是那么好。
无论哪种方式,如果您能够将维度从 50 减少到 8(或者在我们的情况下从 10 减少到 4),它可以帮助您更好地探索您的数据,并通过了解变量之间的协方差使其更易于管理。

请记住,这纯粹是介绍性的,不应该是一个结论或详尽的分析。如果我错过了什么或者做错了什么,请随时纠正我(我可能就是这样)。有许多不同的方法来处理多元数据,因子分析甚至比本文讨论的范围还要多。

如果你喜欢这篇文章并想看更多,请务必关注我的简介

未来工厂:人工智能实时控制

原文:https://towardsdatascience.com/factory-of-the-future-real-time-control-with-artificial-intelligence-92d799dcc590?source=collection_archive---------18-----------------------

https://images.pexels.com/photos/1020325/pexels-photo-1020325.jpeg

大数据和物联网在现代工作环境中的逐步实施正在彻底改变商业领域。为了清楚地了解这些变革在未来几年的影响程度,值得咨询顶级应用开发公司,了解工作的实时控制对未来工厂的影响。

未来工厂的概念指的是一种组织生产方式的新方式,并对全球化以第四次工业革命的形式给我们的经济存在带来的影响作出回应。

工业 4.0 通过实施新的数据处理方法、智能软件和传感器来实现价值链的全面数字化,以便供应商和消费者可以实时监控公司内部发生的事情。

智能制造领导联盟(SMLC)等北美经验,以及安格拉·默克尔政府(这一概念的大力倡导者)等机构已经将这一技术的优势付诸实践,因此这是一个开始形成的现实。您的企业也可以通过与顶级应用程序开发公司会面来利用这项技术。

实时控制

目前,该行业正处于一个进步的时刻,这是由不断的数字化和来自其所有控制领域的信息管理所决定的。

这就是我们所说的高效实时管理工厂的原因,它保证了更大的灵活性,更充分地满足了日益个性化关注的消费者的需求。以至于公司所有管理领域之间的实时互联性已经成为解读新市场带来的挑战的一个基本点。

我们已经抛弃了信息社会等旧的概念,认识到有效管理的数据是实现智能工作场所的决定性工具,可以加快和改善生产流程。您的数据可以通过顶级应用开发公司开发的工具进行妥善管理。

显而易见,这需要高度的自动化和技术现代化。这需要互联网和被称为网络物理(cyber physics)的系统之间日益一体化的竞争,或者说,这些虚拟网络有助于跟踪正在被制造出来的物体。

当谈到实现这一目标时,从最小到最大的公司都必须优化工厂,将它们转变为人们所理解的智能工厂。

从生产链到产品交付,再到库存和包装,这些都是一项工作的各个阶段之间持续和即时的相互沟通。从这个意义上说,人工智能将是必不可少的,并且可以通过使用顶级应用开发公司开发的应用来加以利用。

通过物联网(IoT)使用传感器有助于工厂的设备和仪器提高对任何时候发生的事情的审查,从而能够纠正错误和改善。

智能工厂的优势

智能优化工厂使操作变得简单,只需最少的人工干预和最高的可靠性。自动化的工作流程、资产同步、所有流程的监控和调度、优化的能耗(以及由此带来的节约)可以提高性能、正常运行时间和最终结果的质量,并降低成本。

由于这些进步和顶级应用程序开发公司开发的应用程序,员工和控制系统可以在问题或缺陷出现之前预测和采取行动,而不是简单地在问题或缺陷出现后做出反应。这种特性可以包括识别异常、故障、库存补充、质量问题的识别和预测性解决、安全问题的监控,当然还有维护。

智能工厂可以根据历史数据预测未来的结果,并可以实时减少执行某项操作所需的时间,同时提高其性能。

在智能工厂中,可以分配诸如“数字双胞胎”之类的过程,这是美国宇航局提出的一个概念,它提供了将操作数字化并超越自动化和预测能力集成的可能性。

另外,根据这种类型的制造,捕获的数据将更加透明。实时数据的可视化可以转换从仍在生产的过程和产品中获得的信息,使其对人类或机器人的决策有用并可操作。

透明的网络提高了所有安装的可见性,并确保公司能够做出更准确的决策。为什么?因为除了前面提到的监控系统之外,它还提供了实时警报和通知等工具。

人工智能的危险

新的和更先进的人工智能形式的发展并非没有争议,因为许多声音一直警告说,如果没有适当的控制和监管,它可能是一个严重的风险。

当发现两个脸书市场聊天机器人已经自主开发了他们自己的语言时,围绕马克·扎克伯格发生了一场重大争议。

这些实验性聊天机器人是他们的公平倡议(脸书人工智能研究)框架内的概念证明,其中两个自主机器人正在学习谈判技术,以便在未来,它们能够独立谈判协议。

研究人员发现,这些机器人(鲍勃和爱丽丝)已经学会了非常先进的技术,并且是非常高级的谈判者。例如,他们能够假装对某些商品感兴趣,然后在谈判中放弃这些商品,以便与他们真正感兴趣的产品达成更好的协议。

但是,当他们意识到这些机器人在没有人类干预的情况下创造了自己的语言,他们无法理解,甚至跳过指导方针和标记脚本以更有效地发挥谈判作用时,争议就来了。

埃隆·马斯克(Elon Musk)等名人认为,人工智能的发展是“我们作为一个文明所面临的最大风险”,甚至开始指责扎克伯格的轻率。

无论人工智能的智能如何,它仍然需要人类的干预,才能与它为之设计的人保持一定程度的互动。这是顶级 app 开发公司可以有效进行的。今天,企业必须考虑利用人工智能。

作者简介:

Melissa Crooks 是一名内容作家,为 Hyperlink InfoSystem——一家位于纽约、美国和印度的移动应用开发公司——撰写文章,该公司拥有最优秀的技术娴熟的专业应用开发团队。她是一名多才多艺的科技作家,喜欢探索最新的科技趋势、企业家和创业专栏。她还为顶级的应用开发公司写文章。

#失败:人工智能是一门科学

原文:https://towardsdatascience.com/fail-artificial-intelligence-is-a-science-6e543b135193?source=collection_archive---------26-----------------------

“我没有失败。我刚刚发现了一万种行不通的方法。”—爱迪生

科学突破界限,与失败密不可分

科学很乱。我不认为科学领域之外的人欣赏失败与成功的比率。在我的工作中,我经常无法完成一个想法。有时模型不起作用。有时候这个想法是错误的。有时候想法需要改变。人工智能更多的是实验和迭代,而不是从纸面到生产建立强大而清晰的解决方案。

科学的巨大悲剧——美丽的假设被丑陋的事实扼杀。赫胥黎

科学有一个基本的方面叫做零假设。如果你在发展你的想法时没有机会接受零假设,那么,简单地说,你不是在做科学。人们做了许多实验来尝试观察以太,但都是徒劳的,因为根本不存在以太。同样,仅仅因为你有一个关于人工智能的优雅应用的想法,并不意味着它会成功。这只是意味着你有一个假设要测试。

在本文的下一部分,我将与你分享我得到的两个负面结果。希望分享这些糟糕的结果会让你更好地了解人工智能科学如何导致失败,以及如何为此做好准备,做出反应,并度过难关。

无监督学习机器

监督学习和非监督学习之间的区别是非常基本的:监督学习模型学习(被训练)将输入映射到输出,而在非监督学习中,任务是学习没有标签的数据的底层分布。这些方法的区别不是本文的重点,所以请记住,无监督学习是关于学习事物如何工作,而不是学习将它们放在什么盒子里。如果有创造性的许可,我会说无监督学习的目标是学习本身的行为;为了了解数据集的东西,模型显示。

失败介绍(第一部分):面向嵌入的 CAE

在最近的一些研究工作中,我一直试图建立一个卷积自动编码器(CAE)来学习人脸的嵌入。简而言之,我试图找到有意义的小向量来代表人脸的图像。为什么?下面是最近一个令人振奋的使用人脸嵌入来生成内容的例子:

我最初的动机是上面描述的工作的前身 DCGAN 论文。参见本文 (DCGAN)第 10 页和第 11 页的图 7 和图 8,了解为什么人脸嵌入如此酷。CAE 能做和 DCGAN 一样的事情吗?

下面是 2017 年的一个视频,展示了 DNN(密集)自动编码器和 PCA 的基本概念,促使我相信 CAE 应该可以工作( 2018 更新此处):

去年我和一些同事在 CAE 上发表了一篇关于处理多麦克风音频信号的论文,所以我有足够的背景来尝试这些想法。不要管 CAE 模型是如何工作的,我突然意识到一张图片胜过千言万语,图像是展示神经网络正在学习什么的一个很好的媒介。那么为什么不用 CAE 代替 GAN 来学习人脸嵌入呢?CAE 应该是在将图像压缩到某个瓶颈尺寸后,将图像重建回原始图像。我的假设是,这将对我的面部图像“有效”。我抓起一个相当不错的人脸图像数据集,开始编写 CAE 程序。

大约在这个时候,我正在阅读一篇名为“ASR 的无监督调制滤波器学习方法的比较的相关论文,其中提到了无监督学习的几种模型(GAN、CAE 和其他网络)。这个想法是,几种类型的模型可以用来学习无监督的数据结构。这给了我一些信心,如果语义图像嵌入,CAE 可以做 GANs 正在做的事情。

让我们看看在最初的模型开发之后,在我的第一天模型训练中,学习过程是什么样子的。下面视频中的第一行是输入人脸图像,输出中的第二行(用于测试数据,而不是训练数据)看起来应该是相同的:

我可以看到这个模型学到了一些东西,并且随着时间的推移变得越来越好。请注意,各通道的输出处于饱和或截止状态:白色表示 3 通道饱和,黄色表示 2 通道饱和(红色=255,绿色=255,蓝色=0),黑色全为 0,依此类推。没有像 123 或 65 这样的中间像素值,只有 0 和 255。随着时间的推移,亏损在减少,但回报却在减少,约为 0.5。所以我尝试了更长的训练时间,批量标准化,tanh 激活而不是 sigmoid,不同大小的网络和瓶颈,不同大小的图像,以及许多其他方法。以下是第二天的结果:

第三天:

在这一点上,我准备扯掉我的头发,把我的电脑扔出窗外。我采用了和 GAN 论文一样的方法,比如批量标准化,但是失败得很惨。我平行进行实验,并开始拉拢同事加入我失败的探索。这让我恍然大悟。也许我的想法有点离谱。让我们后退一步。为什么不用 VAE 或甘来代替 CAE 呢?

改变我的想法:VAE 或者别的什么怎么样?

我不得不面对这样一个事实,我的 CAE 方法和基本上我所有的工作都没有成功,我不知道为什么。我有一些想法,但不知道哪里出了问题。放弃几天的工作不是一种好的感觉,但我不得不接受零假设,即我的 CAE 在重建用于语义嵌入操作的人脸图像方面不够好。GAN 在这项任务中很受欢迎,因为它们确实有效(例如 DCGAN),我只是花了几天时间尝试做一些最终无效的酷事情。也许变型自动编码器(VAE)会更好地工作?这就是文献所指出的…

在这最低谷的时候,我收到了玛丽·凯特·麦克弗森的回信。她不仅让 CAE 模型工作,她的 CAE 代码看起来几乎和我的代码一模一样。在这一点上,我回过头来对自己说,真的有很多方法可以不做灯泡。这是她几个小时训练后的结果:

Results for Mary Kate MacPherson’s efforts of literally a few hours.

这里学到的经验是,有时候你真的不应该放弃,你绝对应该和聪明人在一起。归根结底,这都是她的事。就编程而言,我的努力失败了,但通过一些协作和合作,我们能够取得胜利。我计划写一整篇单独的文章,只是关于有效的东西,作为我和玛丽·凯特·麦克弗森关于产生深度学习的动漫女孩的文章的后续。

Morphing from baby picture to picture of me, using deep learning.

另一个巨大的失败(第 2 部分):增长神经网络

我正与 Herschel Caytak 博士合作一个项目,我们正在研究在训练期间发展一个深度神经网络的好处。NVIDIA 报道了一些惊人的结果在训练中使用这种技术生长 GAN 以获得越来越大的输出图像。这里有一个 github 仓库,里面有一些使用 keras 的类似代码。同样,这都与面孔数据有关。

在我们遇到的几个资源中讨论了在训练期间发展神经网络,但是在我们所有的谷歌搜索中,我们无法找到一篇论文证明使用像 keras 这样的现代工具对密集网络(不是 CNN/GAN)的方法的优点。我们在这里谈论的不是遗传算法或其他增长神经网络的随机方法。相反,这种想法是将未经训练的神经元添加到已训练的网络中,训练新的更大的网络,然后再次这样做。在这项工作中,我们也对问题之间的迁移学习不感兴趣。我们狭隘地专注于在任何给定数据集上的分类器训练期间发展深度神经网络的好处(或坏处)。

人们对这种不断发展的神经网络想法已经思考了很长时间。对 2000 年之前的方法的一个很好的回顾是这个。文献中也有许多评估生长神经网络的讨论和想法。例如,参见基于这篇研究文章这篇博文中的“动态网络扩张”。这仍然是一个研究课题,你可以从这个 Quora 帖子中看到。更多此处

定性地说,一层一层地生长神经网络解决了需要调整的参数太多的问题。它允许在小型网络上进行学习,然后当网络通过增加一层而增长时,网络的大部分参数在一个良好的位置开始,用于训练过程的下一部分。理论上,这是一个好主意,因为添加到神经网络的每一层都可以构建更细粒度的特征,这些特征映射到之前训练的层的一般化。然而,现代反向传播是不可思议的。我们真的能从培养神经网络中获得任何好处吗?这里的无效假设是,从零开始训练一只 DNN 犬和这种奇特的分层训练一样有效。

对于卷积神经网络(CNN)来说,发展神经网络的想法可以解释为检测总体特征,然后构建激活越来越精细特征的层

我们遇到的问题是缺乏这种方法的科学证据,特别是缺乏 DNNs 的证据。仅仅因为某些东西可以编译,并不意味着它比传统的好方法更好。种植全连接的 DNN 怎么样?我们能拒绝零假设,即发展一个网络和仅仅训练一个大网络是一样的吗?每次训练迭代都按层宽增长网络怎么样?或者可能是通过高度(层数)的方式?证据的缺乏导致我们进行了这里描述的实验。

值得注意的是,生长神经网络与我们对生物神经网络的理解完全不同,在生物神经网络中,网络往往会被修剪 +具有关键期。在生物神经网络中学习新事物可能涉及招募现有神经元形成新回路,但让我们避免陷入关于皮层竞争的讨论,并专注于最初的问题:当训练 DNN 时,我应该尝试培养它吗?值得吗?

令人沮丧的是,我们去年对这个想法做了一些测试,但没有成功。原来我们有一个编程错误。这一次我们又试了一次,但更加小心。对消极的结果做好准备。现在我们开始吧…

增长神经网络指标

更快并不意味着更好。例如,我们知道 Adam 比 SGD 训练得更快,但 SGD 在测试数据上比 Adam 做得更好(来源)。同样,在这项工作中,我们检查了增长神经网络对标准测试数据性能的影响。

我们使用标准指标(准确度、精确度、召回率、f1 分数和损失曲线)对 DNNs 进行全面评估和比较。

增长 NN 数据集

我们使用 Colab 笔记本来运行我们的模型(我们使用该平台的动机是通过使用 GPU 选项进行硬件加速来加速模型训练)。我们使用导入的 panda 库将 UCI 机器学习库中的 covertype 数据集导入到一个数据框架中。该数据集由与林地特征相关的 54 个类别(特征)中的 581012 个数据点的未缩放定量和二进制数据组成。分类任务是为给定的观测值(30 x 30 米像元)预测位于北科罗拉多州罗斯福国家森林的四个荒野地区的森林覆盖类型。森林覆盖类型名称是 1-7 范围内的整数。

生长神经网络过程

在数据摄取之后,我们将数据集分成“状态”信息(特征)和类。我们使用 MinMaxScaler(从 sklearn.preprocessing 导入)将所有特性缩放到范围 0–1,并对类进行一键编码。对数据维度的快速检查显示,x(要素)具有(581012,54)的形状,y(类)具有(581012,8)的形状。我们将数据分为 20/80 的测试训练,这意味着该模型将对 80%的数据进行训练,并对其余 20%的数据测试/验证其预测。K-fold 交叉验证用于以 3 种不同的方式随机分割数据,这确保了训练测试分割在整个数据集的许多组合中发生,消除了与不均匀采样和有偏数据相关的问题。

成长 NN 模型准备

分类任务是使用 3 种不同的模型实现的,这 3 种模型的区别仅在于层结构(数量、宽度和可训练层)。我们使用“分类交叉熵”和“adam”分别作为模型损失和优化器。模型性能是使用验证数据集(模型未被训练的数据)测量的,测量的具体指标是丢失、准确度、精确度、召回和 f 分数(请参见此处的和此处的了解这些指标的含义以及如何计算它们的详细解释)。

生长神经网络基础模型 1

初始模型由 3 层组成。第一层采用 x 特征(54)的形状,然后数据被传递到宽度为 30 的完全连接的第二层。添加脱落层(0.5),随后是宽度为 25 的另一层。随后是另一个脱落层(0.5),该脱落层连接到宽度为 8 的最终输出层。我们对输入层和隐藏层使用“relu”激活函数,对最终输出层应用 softmax 函数。训练设置为 10 个时期,批量大小为 128。

包含冷冻基础模型 1 的生长神经网络模型 2

第二个模型由第一个模型组成,在网络的输入端增加了另外两层。经训练的第一模型从 HDF5 扩展文件加载。我们通过将先前模型的可训练属性设置为假来“冻结”加载的第一个模型。附加层的宽度分别设置为 100 和 54,激活功能设置为 relu。(第一层被选择为模型宽度的任意但合理的增加。选择第二层是为了与冻结层的输入层的尺寸正确对接。)

生长神经网络模型 3

最后,我们生成了第三个模型,该模型在大小和设计上与模型 1 和模型 2 的组合 DNN 相当。模型 3 由完全可训练的层组成。评估所有 3 个 dnn 的性能指标,以查看将预训练模型作为更大模型的一部分进行重用和冻结是否有优势。

成长 NN 观察与讨论

结果如下图所示:

Figure 1: Bar Chart of K-fold mean accuracy with standard deviation error bars

Figure 2: Model recall (left), precision (center) and accuracy (right) for test data

Figure 3: Model f-score (left) and loss (right) for test data

这些实验的结果非常有趣,尽管有些不确定。我们最初的零假设是,与在单个步骤中训练较大模型相比,使用预训练模型作为较大模型中的冻结层不会提供任何优势。

图 1 显示,所有模型都达到了相似的平均准确度水平。模型 2 似乎具有最高的平均准确度,但是误差线的大小表明模型之间的差异不太可能具有统计学意义。

参见图 2 和图 3,第二个模型在召回率、准确度和 f 值方面表现最佳。然而,图表结果仅针对单个 k 倍迭代显示,并且如前所述,可能不具有统计显著性(所有条形图值在图 1 中处于大约相同的高度,因此要有所保留)。

在任何情况下,我们都可以看到,上面显示的结果并没有否定使用冻结预训练模型的潜在效用,尽管任何优势都可能很小,并且可能不保证实现这一想法所需的努力。看起来那里可能有什么东西,但如果有,那也很小。我称之为否定结果。需要进行更多的研究来确定种植 DNNs 是否是一个好主意,但是到目前为止,运气不好。

结论

在这篇文章中,你看到了人工智能研究人员日常生活中的一些负面结果和极度沮丧。人脸嵌入和生长神经网络肯定是真实的,而且肯定很难。是科学,科学很乱!

如果你喜欢这篇文章,那么看看我过去最常读的一些文章,比如“如何给一个人工智能项目定价”和“如何聘请人工智能顾问”嘿,加入我们的简讯

下次见!

——丹尼尔
Lemay.ai
丹尼尔@lemay.ai

失败的激增:分析北京在推特上的虚假信息运动激增

原文:https://towardsdatascience.com/failed-surge-analyzing-beijings-disinformation-campaign-surge-on-twitter-b376aed812ed?source=collection_archive---------35-----------------------

随着香港紧张局势从 2019 年 5 月开始急剧加剧,中国加强了在社交媒体上的造谣活动。具有讽刺意味的是,这很可能为 Twitter 最终关闭该操作埋下伏笔。

Bar chart race showing the surge in Chinese state Twitter troll account creation. Online version here.

推特首次揭露中国针对香港抗议运动的虚假信息活动时,还不完全清楚北京在该平台上的操作最初是如何被发现的。

这家社交媒体公司暗示,不寻常的知识产权活动可能泄露了北京的比赛。根据 Twitter 的新闻稿,Twitter 在中国被禁止,但据报道,来自中国的 troll 帐户使用 VPN 和未封锁的 IP 地址访问该服务。

但在查看 Twitter 于 2019 年 8 月 19 日发布的第一批约 360 万条推文时,没有明显的联系。这一批 890 个巨魔账户中的大部分都是在 2017 年或更早的时候创建的,他们的活动在 2017 年达到顶峰,特别是在当年的 11 月和 12 月(在我之前关于这个主题的中型帖子中对这第一批的图表和分析)。

现在,谜题的主要部分终于出现了。9 月 20 日,在首次发布声明一个月后,Twitter 发布了第二批与中国政府针对香港抗议运动的造谣活动有关的推文。

在大约 1020 万条推文中,我发现 2019 年 5 月至 7 月期间,中国国家巨魔的活动明显激增,这是香港政治紧张局势急剧升级的时期。我认为,讽刺的是,这种“竞选热潮”可能是该网络在 Twitter 上拉响警报并被关闭的关键原因之一。

根据我的分析,一些主要数据如下:

  • 第二批 4301 个巨魔账户中有 54%(2320 个)是在 2019 年 5 月至 7 月间创建的。请记住,Twitter 现在是一个传统的社交媒体平台,不再经历指数级的用户增长。像这样的账户创建量激增是不可能躲过 Twitter 的注意的,尤其是在服务被屏蔽的中国。下面图表的互动版本可在这里获得。

It wasn’t clear from the first set of Chinese troll tweets how the clandestine network came to Twitter’s attention only in 2019. Details from a second tranche, on the right, finally gave us a major clue.

  • 在香港,当重大政治声明或暴力冲突在部分地区发生时,新 troll 帐户创建的峰值日期就在或接近关键里程碑。巨魔账户单日创建量最大的一次是在 6 月 14 日——在 香港行政长官林郑月娥暂停引渡法案 的前一天,这引发了抗议。巧合还是协调?我也不知道,但这个时机值得注意。

  • 名义上,在这一“激增”期间,中国官方巨魔账户发出了 87,369 条推文和转发。但事实上只有 27192 条独特的推文,这表明许多账户都在转发对方的推文,或者是来自主要钓鱼者的推文。

这段时间的推文/转发主要是中文的,英文推文仅占 69,402 条州政府推文的 6%,即 3,723 条,在过滤后的子集中,我删除了与逃亡的中国亿万富翁郭文贵进行口水战的推文。下面图表的互动版本可在这里获得。

The difference in the volume of English Vs Chinese troll tweets during this surge period is stark. Did someone in the disinformation campaign decide that they had already lost the battle for the hearts and minds of those who read in English?

  • 中国在大陆禁止 Twitter 的决定带来了一些意想不到的后果。由于禁令,一些负责操纵 Twitter 的政府官员对这个平台并不熟悉,导致了一些滑稽的“noobish”行为。

例如,在创建时,43 个新的中国国家巨魔账户继续发出默认的第一条推特——“只是建立我的推特。# myfirstweet”——正如 Twitter 所建议的那样,然后继续转发主要巨魔的消息(@RuthMiller916、@eituji1994 和@bindarsou 在中国国家巨魔的第二批推文中出现得相当突出)。

以下是一个名为“qdu af1 S3 zcbagxgqdzla 3+cxvr 4 TP 5 ssciebdrqqinws =’”的巨魔账户(其用户名被 Twitter 散列)的推文示例:

'Just setting up my Twitter. #myfirstTweet',
       '打扮好。恋爱去~🤣🤣🤣 https://t.co/jR3VqsJvtG',
       'RT @RuthMiller916: //醒一醒!唔好再用「是你教我和平示威是沒用」、去為自己暴力衝擊的違法行為合理化!//\n\n【短片】【堅守底線】馬逢國提醒示威者和平表達是公民責任、籲傳媒不要模糊社會道德規範:仲有幾多警員手指可以被咬斷?\n#HongKong #HK #香港…',
       'RT @feituji1994: 冲击警方,扔砖块,扔铁棍,用铁棍殴打警察,这哪还是普通市民,明明就是一群暴徒!此时此刻,那些沉默的大多数难道还要继续沉默?!所有热爱香港热爱家园的人都应该站出来,谴责暴徒,驱逐暴力,支持港警,守护家园! #HK #HongKong #香港 #逃…',
       'RT @bindarsou: 「反修例」游行中頻頻發生的暴力事件,顯然不是偶然事件,而是有組織有預謀的事件。參與「反修例」的港人應當擦亮眼睛,看清披着「民主」外衣下丑陋而險惡的用心。一腔愛港熱血固然重要,但是是非必須明辨,正邪必須分清,應應當徹底與禍亂香港的暴動分子分清界限。#…'

另一个巨魔账号“yuaz 3 l 8 su 2 izeg 7d 7m cdpoxc 2 vavji 5 crbpudsg 8s 08 = "发推文:

'Just setting up my Twitter. #myfirstTweet',
       'RT @feituji1994: 那些游行示威的人要求香港民主、法治,殊不知自己的暴力行为,才真正破坏了香港法治的核心价值!😡😡😡😡😡#HongKong #HK #antiELAB #香港 #反修例 https://t.co/4SIu5Upsr9',
       'RT @feituji1994: 说好的理性、和平示威!暴徒却以伞为剑狂殴警察!哪个国家的警察可以被如此对待?!香港的混乱、荒诞让人震惊!#HongKong #HK #香港 #逃犯条例 #沙田示威 #游行 https://t.co/sZXGhfAYfu',
       'RT @feituji1994: 总是把自己包装的很“正义”,是“自由”、“民主”的代言人!看看香港人是怎么说的?!那一小部分人不能代表香港,他们只是最卑微“暴徒”!#HongKong #HK #香港 #逃犯条例 https://t.co/rg4bOvDzh1',
       'RT @feituji1994: 回顧 #香港 修例以來,頻繁 #遊行 到修例壽終正寢,到處處充斥著暴力破壞,到無辜民眾被毆打,到立法會、中聯辦被衝擊,國徽被玷污!反修例只是藉口,不顧民眾安危,搞亂香港,推動顏色革命呼之欲出!境外的黑手,#反對派 的醜惡,暴露無遺!熱愛香港的人…',
       'RT @feituji1994: 穿黑衣、戴头盔的年轻人,竟是反对派会议召集人毛孟静儿子?\n 请全城市民认清他们的真面目!有法律界人士指,示威者已犯暴动、刑事毁坏两项罪名,每罪的最高刑罚均是 10 年监禁。支持警方严正执法,尽快将暴徒绳之于法!\n#搗亂 #立法會 #暴動刑毀 # 毛孟…',
       'RT @bindarsou: 當學校開始給學生洗腦,煽動他們荒廢學業轉而上街遊行時,這個城市還有未來嗎?\n 這不僅是學校的問題,家長也是很關鍵的一環。為人父母,放任子女自甘墮落,這難道就是所謂的自由嗎?長此以往,香港如何進步?\n#香港清醒點吧 https://t.co/eS0Zk…',

这条默认的“设置我的 Twitter”信息是 Twitter 的联合创始人兼首席执行官杰克·多西在 2006 年 3 月 21 日发出的著名的“第一条推特”的变体。在他们的行动被推特揭露后,这些巨魔会很快发出他们最后的推文。

这是我分析中国政府针对香港抗议活动的虚假信息系列文章的第三篇。我将在下面的小节中更详细地解释我的方法。我之前关于这个主题的帖子是这里的和这里的。

数据和回购

这里是这个项目的回购,和这个帖子的笔记本。由于文件大小的原因,它不太可能在 Github 上正常显示。您最好下载或克隆 repo 并在本地机器上运行它。不过警告,由于大量使用交互式图形,运行笔记本电脑的计算成本很高。

CSV 文件太大,无法上传到 Github 上。直接从 Twitter下载。

中国国家巨魔推文的两个部分的广泛比较

在过滤第二部分巨魔推文以关注“激增”时期之前,我首先在广泛的层面上比较了两组:

The second tranche of Chinese state troll tweets released by Twitter was larger by far, comprising 10.2 million rows of data compared to 3.6 million for the first tranche.

在第二批推文中,独立用户的数量要高得多,这提供了从哪里开始挖掘的第一个主要线索——在这种情况下,是账户创建日期。北京行动背后的政府巨魔可能做了两件事之一:获得更多现有账户,或大量创建新账户。后者是国家支持的虚假信息运动的一个主要迹象,比如我们看到俄罗斯在 2016 年总统大选前在美国发动的运动。

TROLL 帐户创建日期的比较

中国巨魔推文的第二部分不仅在各个方面都更大,而且如右边的第二张图表所示,2019 年创建了大量可疑的新账户。从 5 月到 7 月的峰值远远超过了我们在第一批中国巨魔推文中看到的(左):

2019 年 5 月至 8 月期间创建的 2,320 个账户占第二批 4,301 个 troll 账户总数的近 54%。北京可能认为,面对香港不断升级的紧张局势和抗议活动,它必须加大宣传力度。

但这很可能适得其反,因为 Twitter 的监控团队不可能没有注意到在一个禁止 Twitter 的国家有数量可疑的大量账户(Twitter 称这些账户来自【中国】)。

分析“激增”账户

由于时间和资源的限制,我选择重点分析 2019 年 5 月至 8 月期间创建的中国国家巨魔账户。然而,第二批中的一些顶级巨魔是在更早的时候被创造出来的。

对整个第二阶段的深入研究将不得不留给资源更充足的研究机构。以下是我发现的第二部分的一些有趣特征:

A .语言设置,使用的推文语言

这两个部分的全部推文包括多种语言,如印度尼西亚语,充满了体育、色情、全球政治等垃圾内容。

但在“激增”阶段,虚假信息运动背后的中国机构似乎更专注于创建主要设置为英文和中文的巨魔账户。这些新的巨魔账户也主要以中文和英文发布推文,如下表所示:

B .推文活动

正如所料,在这个“激增”时期创建的巨魔账户在 7 月份的推文/转发次数最多,当时街头抗议和冲突升级到了前所未有的水平。让我们更详细地分析一下:

在 7 月份大约三分之一的时间里,每天至少有 400 个巨魔账户在发微博。峰值出现在 7 月 9 日,当天有 420 个巨魔账号在发推/转发。

值得注意的7 月 9 日发生了什么: 香港特首林郑月娥宣布引渡法案已经胎死腹中 她的政府在立法方面的工作已经“彻底失败”

我将推文的分布进一步分解为具体的日-小时-分钟时间框架。您可以点击此处下载下面图表的互动版本,了解详细的细分信息:

Medium doesn’t make it easy for interactive charts to be embedded directly, so best to download the repo and run this chart on your browser. Hover over the specific data points to see the detailed timings for when the troll tweets peaked.

对“激增”推文的分析

对推特的完整语料库进行详细而严格的内容分析超出了我的能力范围。我建议也单独看一下转发的内容,因为它们通常反映了这些流氓及其老板试图传播的关键信息。

一个简单的术语频率表也再次证实了一个主要的巨魔帐户的影响作用— @ctcc507,又名梦想新闻。请注意,在“激增”期间,该账户的网名出现在英语转发中最常用的 50 个术语列表中的显著位置:

以下是其他巨魔账户转发的@ctcc507 推文的快速样本:

RT @ctcc507: Governing Hong Kong by law is the core value of Hong Kong. We don’t allow anyone to run roughshod over the law. [https://t.co/P…',](https://t.co/P%E2%80%A6',)
       'RT @ctcc507: The legislative council belongs to the people of Hong Kong.Those people with ulterior motives indicated by forces hide behind…',
       'RT @ctcc507: The legislative council belongs to the people of Hong Kong.Those people with ulterior motives indicated by forces hide behind…',
       'RT @ctcc507: Governing Hong Kong by law is the core value of Hong Kong. We don’t allow anyone to run roughshod over the law. [https://t.co/P…',](https://t.co/P%E2%80%A6',)
       'RT @ctcc507: Governing Hong Kong by law is the core value of Hong Kong. We don’t allow anyone to run roughshod over the law. [https://t.co/P…'],](https://t.co/P%E2%80%A6'],)

@ctcc507 仅在这个小子集就被转发了 465 次。上面的例子清楚地表明了巨魔账户想要发送的信息。在转发的其他地方,我们看到巨魔继续宣扬外国干预香港抗议的阴谋论(几个“别有用心”的变体排在最前面)。

为了处理中文推文中的大量文本,我只关注了 7 月份的推文。然而,那个月的中国推文是激增期间迄今为止最大的一块,由大约 35,000 条推文组成。就像英文巨魔的内容一样,转发占据了中文巨魔推文的大部分。

中国“原创”推文中的热门词汇:

反 對       314
社 會       277
對 派       248
反 對 派     248
瘟 龟       215
行 為       191
瘟 鬼       183
破 壞       178
香港 嘅      160
穩 定       158
香港 法治     151
佢 哋       148
严惩 暴徒     131
香港 警察     126
遊 行       120
嚴 懲       117
一定 要      116
應 該       114
维护 香港     108
勢 力       105
嘅 行       104
香港 和平     103
中 國       102

中文转发热门词汇:

逃犯 条例            8182
香港 逃犯            7951
香港 逃犯 条例        7299
逃犯 条例 游行        6132
条例 游行            6132
香港 逃犯 条例 游行    5695
護 香港              4751
遊 行                4535
社 會                4257
反 對                4131
游行 暴徒             3885
守 護                3738
逃犯 条例 游行 暴徒    3354
条例 游行 暴徒        3354
反 對 派             3153
對 派                3153
守 護 香港            3011

What I’ve noticed from the term frequency counts above and a weekend of looking through the tweets is the higher usage of terms condemning the protestors who turned violent. The term 暴徒 (violent thugs) and the call to punish the violent protestors (严惩 暴徒) came up more prominently in the second tranche of tweets, compared to the first.

但需要更全面的内容分析来确认这是否确实反映了虚假信息运动中“叙述”焦点的变化。

散点图

我之前曾用散点图来可视化中国国家巨魔推文的第一部分。我感觉这种图的视觉价值第二次没有那么高,但我把这些图留在了笔记本上,以便在图的左下角找到有用的搜索功能。

如果您下载了 html 文件,您可以使用搜索框轻松找到与所需搜索词相关的推文和转发,如“别有用心”。这对未来的研究很有价值。

然而,当你试图从一个大的中文文本语料库中创建散点图时,产生的文件是巨大的,并且在消费级计算机上实际上是不可用的。

所以出于实际原因,我选择只关注第二批国家巨魔推文中的两个顶级巨魔——@ bindar sou@hk_observer 。这两条微博都被不太活跃的“巨魔”账户定期转发,共转发了约 4.5 万条。 @bindarsou 是两个账户中最大的一个。

快速重新解读散点图:

颜色

  • 图表中的单词根据它们之间的联系被涂上颜色。蓝色的与原创推文联系更紧密,而红色的与转发推文联系更紧密。每个点对应一个提到的单词或短语。

定位:

  • Words that appear frequently in both tweets and rewteets, like “香港”(Hong Kong) and “暴力”(violence), appear in the upper-right-hand corner, reflecting Beijing’s main line of criticisms against the protests.
  • 靠近图顶部的单词代表“原始”推文中最常用的单词。

关键领域:

  • 左上角:这些词经常出现在推文中,但不在转发中。
  • 右下角:同样,在转发中频繁出现的词,而不是推文出现在右下角。

嗯,这大概是像我这样的“公民数据分析师”可以投入到这种分析中的时间。像往常一样,如果你发现任何错误,ping 我@

推特: @chinhon

领英:www.linkedin.com/in/chuachinhon

我早期的关于国家的项目推特上的虚假信息运动:https://github.com/chuachinhon/twitter_state_trolls_cch

未能实现航班延误预测

原文:https://towardsdatascience.com/failing-to-land-flight-delay-predictions-a281689dd602?source=collection_archive---------11-----------------------

在早期的一篇文章(“推理的损失”)中,我提到了利用 auto-mpg 数据集的模型中非独立变量的误用,这是数学/技术决定论的一种症状,这种决定论现在充斥着数据科学,原因是对生产的关注贬低了批判性评估。

在这篇文章中,我希望通过查看 2015 年航班延误数据集的使用,说明这种向生产评估和关键评估的转变不一定是一种优点。这是另一个流行的数据集,因为它很大(由美国境内的 580 万次航班组成),并且易于理解。我已经通过谷歌搜索结果回顾了许多使用这个数据集的文章,那些模型和结论几乎是无用或错误的;在我的评论中,我还没有遇到一个理解航班延误条件概率的人,即没有人(根据我的评论)费心去确定他们正在探索的模式/他们的模型正在预测的模式是否是一个有效的现象,或者仅仅是任何给定日期/航空公司/机场的航班数量的函数。

除了按星期几划分的延误次数之外,绘制航班次数,我们可以看到延误发生率和航班发生率之间有很高的相关性。每天航班延误的任何“模式”都是当天航班数量的假象。

当我们查看航空公司和目的地机场的延误条件概率时,我们观察到每个航空公司和目的地机场的延误条件概率都是相同的(有一两个亮点)——这些点几乎都在同一条线上。换句话说,夏威夷航空公司(延误最少)和西南航空公司(延误最多)航班延误的几率差不多。

这一证据导致的结论是,根据这一数据训练的模型既没有预测任何东西,也没有发现任何有趣的东西。他们的结果只是复制数据中的局部最大值,并不构成任何有意义的模式。

在下图中,我们可以看到平均延误(y 轴)本身取决于延误发生率(x 轴)以及到达机场的航班发生率(气泡大小)。低发生率机场平均延误的高方差随着发生率的增加而收敛(沿 x 轴)是小样本的基本统计特性。

就像早期文章中的假设示例一样,如果对该特定数据采用 boosting(或基于密度的算法)等技术,则得到的模型将在模型误差/准确性度量方面得分较高,但在该输入数据集之外的预测能力有限;然而,由于低发生率机场的高方差,方法上合适的基于概率/随机的模型将显示“不适合”(在当前天真的更好的“准确性”/更新的技术=更好的时代精神中,将被拒绝为“次等”)。

模型准确性和过度拟合是同一枚硬币的两面,再多的正则化、交叉验证或花哨的算法也无法解决关于数据的根本性错误理解或假设。考虑一下,每当航空公司改变其航班时刻表和/或航班目的地时,飞往机场的航班发生率的变化会影响延误发生率以及这些延误发生率的可变性,这将打破大多数“精确”模型。

这具有超出直接数据集的更广泛的含义。考虑对重要但发生率低的客户的处理(如欺诈案件、高价值客户):您的模型在多大程度上被过度设计以抑制或适应其高可变性的趋势?

那些在医疗保健领域工作以训练用于诊断目的的机器学习模型的人应该评估在罕见情况下的数据中是否捕获了超出模型收敛所需的足够的差异。如果不是这样,在假阳性悖论之上可能会有额外的不幸,当整体人口的发病率低于假阳性率时,就会发生这种情况(这里有一个链接,链接到简明易懂的语言解释)。

希望我已经提出了一个案例,数据科学应该比 scikit.fit 和 scikit.predict(或等效物)的生产流更重要。

基于 DCGAN 模型的人脸生成器

原文:https://towardsdatascience.com/fake-face-generator-using-dcgan-model-ae9322ccfd65?source=collection_archive---------9-----------------------

Photo by Camilo Jimenez on Unsplash

概观

在下面的文章中,我们将在人脸数据集上定义和训练一个深度卷积生成对抗网络 (DCGAN) 模型。该模型的主要目标是获得一个生成器网络来生成看起来尽可能真实的新的假人脸图像。

为此,我们将首先尝试理解 GAN s 和 DCGAN s 工作背后的直觉,然后结合这些知识来构建一个假脸生成器模型。到本文结束时,您将能够使用本文中的概念在任何给定的数据集上生成您的假样本。

介绍

以下文章分为两个部分:

  • 理论 —理解DCGAN 工作背后的直觉。
  • 实用 —在 Pytorch 中实现假面生成器。

本文将涵盖这两个部分。所以让我们开始旅程吧…

理论

生成性对抗网络背后的直觉

Generative Adversarial Network(GAN) architecture. Image from https://sthalles.github.io/intro-to-gans/

  • 定义

总的来说,GAN 可以被定义为一个生成模型,让我们并行地生成一个完整的图像。与其他几种生成模型一起, GAN s 使用由神经网络表示的可微分函数作为生成器网络

  • 发电机网络

生成器网络将随机噪声作为输入,然后通过可微分函数(神经网络)运行噪声,以转换噪声并将其整形为具有与训练数据集中的图像相似的可识别结构。发生器的输出由输入随机噪声的选择决定。在几种不同的随机输入噪声上运行发生器网络会产生不同的真实输出图像。

生成器的最终目标是学习与训练数据集的分布类似的分布,以采样出真实的图像。要做到这一点,需要对发电机网络进行培训。与其他生成模型相比, GAN s 的训练过程非常不同(大多数生成模型通过调整参数来训练,以最大化生成器生成真实样本的概率。For-eg 变分自动编码器(VAE))。另一方面,GAN s 使用第二个网络来训练发电机,称为鉴别器网络

  • 鉴频器网络

鉴别器网络是一个基本的分类器网络,它输出图像真实的概率。因此,在训练过程中,鉴别器网络一半时间显示来自训练集的真实图像,另一半时间显示来自生成器的虚假图像。鉴别器的目标是为真实图像分配接近 1 的概率,为虚假图像分配接近 0 的概率。

另一方面,生成器尝试相反的方法,其目标是生成假图像,对于这些假图像,鉴别器将产生接近 1 (认为它们是来自训练集的真实图像)的概率。随着训练的进行,鉴别器将会更好地区分真假图像。所以为了骗过鉴别器,生成器将被迫改进以产生更真实的样本。所以我们可以说:

GANs 可以被认为是一个两人(生产者和鉴别者)非合作博弈,每个参与者都希望最小化其成本函数。

gan 和 DCGANs 的区别

DCGAN 非常类似于 GAN s,但特别关注于使用深度卷积网络来代替普通 GAN s 中使用的全连接网络

卷积网络有助于发现图像中的深度相关性,也就是说,它们寻找空间相关性。这意味着 DCGAN 将是图像/视频数据的更好选择,而 GAN s 可以被认为是一个通用概念,在此基础上开发了 DCGAN s 和许多其他架构 (CGAN、CycleGAN、StarGAN 和许多其他架构)

在本文中,我们主要研究图像数据,这意味着与普通 GAN 相比,DCGAN 是更好的选择。所以从现在开始,我们将主要关注 DCGANs。

训练 DCGANs 的一些技巧

所有的训练技巧同样适用于香草甘 s 以及

  • 确保鉴别器和生成器至少有一个隐藏层。这确保了两个模型都有一个 通用近似属性

普适逼近性质陈述了一个单隐层包含有限个隐单元的前馈网络,在给定足够多隐单元的情况下,可以逼近任意概率分布。

  • 对于隐藏单元,许多激活功能可以工作,但泄漏 ReLUs 是最受欢迎的。渗漏的 ReLUs 确保梯度流过整个建筑。这对 DCGAN 非常重要,因为发生器能够学习的唯一方法是从鉴频器接收梯度。
  • 对于发电机网络的输出,最流行的激活函数之一是正切双曲线激活函数(基于 对 GANs 论文的改进训练技术)
  • 由于鉴别器是二元分类器,我们将使用 Sigmoid 激活函数来获得最终概率。

到目前为止,我们已经讨论了工作直觉和一些训练 GAN s/ DCGAN s 的技巧和诀窍,但是仍然有许多问题没有得到解答。其中一些是:

选择哪个优化器?成本函数是如何定义的?一个网络需要训练多久?以及许多其他内容,将在实用部分中介绍。

实际的

实施部分被分解为从加载数据到定义和训练敌对网络的一系列任务。在本节结束时,您将能够可视化您训练过的生成器的结果,以了解它是如何执行的;你生成的样本应该看起来像带有少量噪点的真实人脸。

DCGAN Architecture. Image from https://gluon.mxnet.io/chapter14_generative-adversarial-networks/dcgan.html

(1)获取数据

你将使用名人面孔属性数据集 (CelebA) 来训练你的敌对网络。与 MNIST 相比,该数据是一个更复杂的数据集。所以,我们需要定义一个更深的网络 (DCGAN) 来产生好的结果。我建议您使用 GPU 进行培训。

(2)准备数据

由于本文的主要目标是构建一个 DCGAN 模型,因此我们将使用一个预处理过的数据集,而不是自己进行预处理。你可以从这里下载 CelebA 数据集的较小子集。如果您有兴趣进行预处理,请执行以下操作:

  • 裁剪图像以移除不包括面部的部分。
  • 将它们调整为 64x64x3 的数字图像。

现在,我们将创建一个数据加载器来批量访问图像。

def get_dataloader(batch_size, image_size, data_dir='train/'):
    """
    Batch the neural network data using DataLoader
    :param batch_size: The size of each batch; the number of images in a batch
    :param img_size: The square size of the image data (x, y)
    :param data_dir: Directory where image data is located
    :return: DataLoader with batched data
    """
    transform = transforms.Compose([transforms.Resize(image_size),transforms.CenterCrop(image_size),transforms.ToTensor()])

    dataset = datasets.ImageFolder(data_dir,transform = transform)

    dataloader = torch.utils.data.DataLoader(dataset = dataset,batch_size = batch_size,shuffle = True)
    return dataloader# Define function hyperparameters
batch_size = 256
img_size = 32# Call your function and get a dataloader
celeba_train_loader = get_dataloader(batch_size, img_size)

数据加载器超参数:

  • 您可以决定任何合理的 batch_size 参数。
  • 但是,你的 image_size 必须是 32。将数据调整到较小的大小将有助于更快的训练,同时仍然可以创建令人信服的人脸图像。

接下来,我们将编写一些代码来获得数据集的可视化表示。

def imshow(img):
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))# obtain one batch of training images
dataiter = iter(celeba_train_loader)
images, _ = dataiter.next() # _ for no labels# plot the images in the batch, along with the corresponding labels
fig = plt.figure(figsize=(20, 4))
plot_size=20
for idx in np.arange(plot_size):
    ax = fig.add_subplot(2, plot_size/2, idx+1, xticks=[], yticks=[])
    imshow(images[idx])

请记住将张量图像转换成 NumPy 类型,并转置尺寸以正确显示基于上述代码的图像(在 Dataloader 中,我们将图像转换成张量)。运行这段代码来获得数据集的可视化。

Images Centered around faces

现在,在开始下一节(定义模型)之前,我们将编写一个函数来将图像数据缩放到-1 到 1 的像素范围,我们将在训练时使用该函数。这样做的原因是 tanh 激活的生成器的输出将包含范围从-1 到 1 的像素值,因此,我们需要将我们的训练图像重新缩放到范围从-1 到 1 (现在,它们在范围 0–1)

def scale(x, feature_range=(-1, 1)):
    ''' Scale takes in an image x and returns that image, scaled
       with a feature_range of pixel values from -1 to 1\. 
       This function assumes that the input x is already scaled from 0-1.'''
    # assume x is scaled to (0, 1)
    # scale to feature_range and return scaled x
    min, max = feature_range
    x = x*(max-min) + min
    return x

(3)定义模型

一个 GAN 由两个对抗网络组成,一个鉴别器和一个生成器。因此,在这一节中,我们将为它们定义架构。

鉴别器

这是一个卷积分类器,只是没有任何 MaxpPooling 层。这是鉴别器网络的代码。

def conv(input_c,output,kernel_size,stride = 2,padding  = 1, batch_norm = True):
    layers =[]
    con = nn.Conv2d(input_c,output,kernel_size,stride,padding,bias = False)
    layers.append(con)

    if batch_norm:
        layers.append(nn.BatchNorm2d(output))

    return nn.Sequential(*layers)class Discriminator(nn.Module):def __init__(self, conv_dim):
        """
        Initialize the Discriminator Module
        :param conv_dim: The depth of the first convolutional layer
        """
        #complete init functionsuper(Discriminator, self).__init__()
        self.conv_dim = conv_dim
        self.layer_1 = conv(3,conv_dim,4,batch_norm = False) #16
        self.layer_2 = conv(conv_dim,conv_dim*2,4) #8
        self.layer_3 = conv(conv_dim*2,conv_dim*4,4) #4
        self.fc = nn.Linear(conv_dim*4*4*4,1)def forward(self, x):
        """
        Forward propagation of the neural network
        :param x: The input to the neural network     
        :return: Discriminator logits; the output of the neural network
        """
        # define feedforward behavior
        x = F.leaky_relu(self.layer_1(x))
        x = F.leaky_relu(self.layer_2(x))
        x = F.leaky_relu(self.layer_3(x))
        x = x.view(-1,self.conv_dim*4*4*4)
        x = self.fc(x)
        return x

解释

  • 以下架构由三个卷积层和一个最终全连接层组成,输出单个 logit。这个逻辑定义了图像是否真实。
  • 除了第一个卷积层,每个卷积层后面都有一个批量归一化(在 conv 辅助函数中定义)
  • 对于隐藏单元,我们使用了泄漏 ReLU 激活功能,如理论部分所述。
  • 在每个卷积层之后,高度和宽度变成一半。例如,在第一次卷积后,32×32 的图像将被调整为 16×16,以此类推。

可以使用以下公式计算输出尺寸:

Formula-1

其中 O 为输出高度/长度, W 为输入高度/长度, K 为滤波器尺寸, P 为填充, S 为步幅。

  • 每次卷积后特征图的数量基于参数conv _ 尺寸(在我的实现中 conv _ 尺寸= 64)

在这个模型定义中,我们没有对最终输出 logit 应用 Sigmoid 激活函数。这是因为我们损失函数的选择。这里我们将使用 BCEWithLogitLoss,而不是使用普通的 BCE(二元交叉熵损失),它被认为是 BCE 的数值稳定版本。 BCEWithLogitLoss 被定义为首先在 logit 上应用 Sigmoid 激活函数,然后计算损失,与 BCE 不同。你可以在这里阅读更多关于这些损失函数的内容。

发电机

生成器应该对输入进行上采样,并生成与我们的训练数据 32X32X3 相同大小的新图像。为此,我们将使用转置卷积层。这是发电机网络的代码。

def deconv(input_c,output,kernel_size,stride = 2, padding =1, batch_norm = True):
    layers = []
    decon = nn.ConvTranspose2d(input_c,output,kernel_size,stride,padding,bias = False)
    layers.append(decon)

    if batch_norm:
        layers.append(nn.BatchNorm2d(output))
    return nn.Sequential(*layers)class Generator(nn.Module):

    def __init__(self, z_size, conv_dim):
        """
        Initialize the Generator Module
        :param z_size: The length of the input latent vector, z
        :param conv_dim: The depth of the inputs to the *last* transpose convolutional layer
        """
        super(Generator, self).__init__()
        # complete init function
        self.conv_dim = conv_dim
        self.fc = nn.Linear(z_size,conv_dim*8*2*2)
        self.layer_1 = deconv(conv_dim*8,conv_dim*4,4) #4
        self.layer_2 = deconv(conv_dim*4,conv_dim*2,4) #8
        self.layer_3 = deconv(conv_dim*2,conv_dim,4) #16
        self.layer_4 = deconv(conv_dim,3,4,batch_norm = False) #32

    def forward(self, x):
        """
        Forward propagation of the neural network
        :param x: The input to the neural network     
        :return: A 32x32x3 Tensor image as output
        """
        # define feedforward behavior
        x = self.fc(x)
        x = x.view(-1,self.conv_dim*8,2,2) #(batch_size,depth,width,height)
        x = F.relu(self.layer_1(x))
        x = F.relu(self.layer_2(x))
        x = F.relu(self.layer_3(x))
        x = torch.tanh(self.layer_4(x))
        return x

解说

  • 以下架构由一个全连接层和四个转置卷积层组成。该架构被定义为使得在第四转置卷积层之后的输出产生尺寸为 32×32×3(来自训练数据集的图像大小)的图像。
  • 发生器的输入是一定长度的矢量 z_size(z_size 是噪声矢量)
  • 除了最后一层,每个转置卷积层后面都有一个批量归一化(在 deconv 辅助函数中定义)
  • 对于隐藏单元,我们使用了 ReLU 激活功能。
  • 在每个转置卷积层之后,高度和宽度变成两倍。例如,在第一次转置卷积后,2X2 图像将被调整大小为 4X4,依此类推。

可以使用以下公式计算:

*# Padding==Same: H = H1 * stride*

*# Padding==Valid H = (H1-1) * stride + HF*

其中 H =输出尺寸,H1 =输入尺寸,HF =滤波器尺寸。

  • 在每个转置卷积之后的特征映射的数量基于参数 conv_dim(在我的实现中 conv_dim = 64)

(4)初始化网络的权重

为了帮助模型收敛,我根据原文 DCGAN 论文对模型中卷积层和线性层的权重进行了初始化,该论文称:所有权重都是从以零为中心的正态分布初始化的,标准差为 0.02。

def weights_init_normal(m):
    """
    Applies initial weights to certain layers in a model .
    The weights are taken from a normal distribution 
    with mean = 0, std dev = 0.02.
    :param m: A module or layer in a network    
    """
    # classname will be something like:
    # `Conv`, `BatchNorm2d`, `Linear`, etc.
    classname = m.__class__.__name__

    if hasattr(m,'weight') and (classname.find('Conv') != -1 or classname.find('Linear') != -1):

        m.weight.data.normal_(0.0,0.02)

        if hasattr(m,'bias') and m.bias is not None:
            m.bias.data.zero_()
  • 这会将权重初始化为正态分布,以 0 为中心,标准偏差为 0.02。
  • 如果存在偏差项,可以不考虑或设置为 0。

(5)建立完整的网络

定义您的模型的超参数,并从在定义模型部分定义的类中实例化鉴别器和生成器。这是代码。

def build_network(d_conv_dim, g_conv_dim, z_size):
    # define discriminator and generator
    D = Discriminator(d_conv_dim)
    G = Generator(z_size=z_size, conv_dim=g_conv_dim)# initialize model weights
    D.apply(weights_init_normal)
    G.apply(weights_init_normal)print(D)
    print()
    print(G)

    return D, G

# Define model hyperparams
d_conv_dim = 64
g_conv_dim = 64
z_size = 100D, G = build_network(d_conv_dim, g_conv_dim, z_size)

当您运行上面的代码时,您会得到下面的输出。它还描述了鉴别器和生成器模型的模型架构。

Instantiated Discriminator and Generator models

(6)培训过程

训练过程包括定义损失函数、选择优化器以及最后训练模型。

鉴频器和发电机损耗

鉴频器损耗

  • 对于鉴别器,总损失是(d _ real _ loss+d _ fake _ loss)之和,其中 d_real_loss 是从训练数据中的图像上获得的损失, d_fake_loss 是从生成器网络生成的图像上获得的损失。对于-例如

z —噪声矢量

i —来自训练集的图像

g(z)-生成的图像

D(G(z)) —生成图像上的鉴别器输出

d(I)-训练数据集图像上的鉴别器输出

损失=真实损失(D(i)) +虚假损失(D(G(z)))

  • 请记住,我们希望鉴别器为真实图像输出 1,为虚假图像输出 0,因此我们需要设置损耗来反映(在阅读下面的代码时请记住这一行)

发电机损耗

  • 只有在标签翻转的情况下,发电机损耗才会看起来相似。生成器的目标是让鉴别器认为它生成的图像是真实的 T4。对于-例如

z —噪声矢量

g(z)-生成的图像

D(G(z)) —生成图像上的鉴别器输出

Loss = real_loss(D(G(z))。

下面是真实损失虚假损失的代码

def real_loss(D_out):
    '''Calculates how close discriminator outputs are to being real.
       param, D_out: discriminator logits
       return: real loss'''
    batch_size = D_out.size(0)
    labels = torch.ones(batch_size)
    if train_on_gpu:
        labels = labels.cuda()
    criterion = nn.BCEWithLogitsLoss()
    loss = criterion(D_out.squeeze(),labels)
    return lossdef fake_loss(D_out):
    '''Calculates how close discriminator outputs are to being fake.
       param, D_out: discriminator logits
       return: fake loss'''
    batch_size = D_out.size(0)
    labels = torch.zeros(batch_size)
    if train_on_gpu:
        labels = labels.cuda()
    criterion =  nn.BCEWithLogitsLoss()
    loss = criterion(D_out.squeeze(),labels)
    return loss

优化者

对于 GAN,我们定义了两个优化器,一个用于发生器,另一个用于鉴别器。这个想法是同时运行它们以不断改善两个网络。在这个实现中,我在两种情况下都使用了 Adam 优化器。要了解更多关于不同优化器的信息,请参考此链接

# Create optimizers for the discriminator D and generator G
d_optimizer = optim.Adam(D.parameters(),lr = .0002, betas = [0.5,0.999])
g_optimizer = optim.Adam(G.parameters(),lr = .0002, betas = [0.5,0.999])

学习率(lr)β值基于原始 DCGAN 纸

培养

训练将包括交替训练鉴别器和发生器。我们将使用之前定义的真实损耗虚假损耗函数来帮助我们计算鉴频器和发电机损耗。

  • 你应该通过交替使用真实和虚假的图像来训练鉴别者
  • 然后是发生器,它试图欺骗鉴别器,应该有一个相反的损失函数

这是训练的代码。

def train(D, G, n_epochs, print_every=50):
    '''Trains adversarial networks for some number of epochs
       param, D: the discriminator network
       param, G: the generator network
       param, n_epochs: number of epochs to train for
       param, print_every: when to print and record the models' losses
       return: D and G losses'''

    # move models to GPU
    if train_on_gpu:
        D.cuda()
        G.cuda()# keep track of loss and generated, "fake" samples
    samples = []
    losses = []# Get some fixed data for sampling. These are images that are held
    # constant throughout training, and allow us to inspect the model's performance
    sample_size=16
    fixed_z = np.random.uniform(-1, 1, size=(sample_size, z_size))
    fixed_z = torch.from_numpy(fixed_z).float()
    # move z to GPU if available
    if train_on_gpu:
        fixed_z = fixed_z.cuda()# epoch training loop
    for epoch in range(n_epochs):# batch training loop
        for batch_i, (real_images, _) in enumerate(celeba_train_loader):batch_size = real_images.size(0)
            real_images = scale(real_images)
            if train_on_gpu:
                real_images = real_images.cuda()

            # 1\. Train the discriminator on real and fake ima.ges
            d_optimizer.zero_grad()
            d_out_real = D(real_images)
            z = np.random.uniform(-1,1,size = (batch_size,z_size))
            z = torch.from_numpy(z).float()
            if train_on_gpu:
                z = z.cuda()
            d_loss = real_loss(d_out_real) + fake_loss(D(G(z)))
            d_loss.backward()
            d_optimizer.step()
            # 2\. Train the generator with an adversarial loss
            G.train()
            g_optimizer.zero_grad()
            z = np.random.uniform(-1,1,size = (batch_size,z_size))
            z = torch.from_numpy(z).float()
            if train_on_gpu:
                z = z.cuda()
            g_loss = real_loss(D(G(z)))
            g_loss.backward()
            g_optimizer.step()

            # Print some loss stats
            if batch_i % print_every == 0:
                # append discriminator loss and generator loss
                losses.append((d_loss.item(), g_loss.item()))
                # print discriminator and generator loss
                print('Epoch [{:5d}/{:5d}] | d_loss: {:6.4f} | g_loss: {:6.4f}'.format(
                        epoch+1, n_epochs, d_loss.item(), g_loss.item()))## AFTER EACH EPOCH##    
        # this code assumes your generator is named G, feel free to change the name
        # generate and save sample, fake images
        G.eval() # for generating samples
        samples_z = G(fixed_z)
        samples.append(samples_z)
        G.train() # back to training mode# Save training generator samples
    with open('train_samples.pkl', 'wb') as f:
        pkl.dump(samples, f)

    # finally return losses
    return losses

# set number of epochs 
n_epochs = 40# call training function
losses = train(D, G, n_epochs=n_epochs)

使用 GPU 进行了 40 多次训练,这就是为什么我必须将我的模型和输入从 CPU 转移到 GPU。

(6)结果

  • 以下是在每个时期之后记录的发生器和鉴别器的训练损失的曲线图。

Training Loss for Discriminator and Generator

发生器训练损失的高波动是因为发生器网络的输入是一批随机噪声向量(每个 z_size) ,每个随机噪声向量从(-1,1)的均匀分布中采样以生成每个时期的新图像。

在鉴别器图中,我们可以观察到训练损失(x 轴上大约 50)上升,然后逐渐下降,直到结束,这是因为发生器已经开始生成一些真实的图像,欺骗了鉴别器,导致误差增加。但随着训练的进行,鉴别器在区分真假图像方面变得越来越好,导致训练错误逐渐减少。

  • 40 个历元后生成的样本。

Generated fake images

我们的模型能够生成看起来尽可能真实的假人脸的新图像。我们还可以观察到,所有图像的阴影都变浅了,甚至棕色的脸也变浅了。这是因为 CelebA 数据集有偏差;它由大部分是白人的“名人”面孔组成。也就是说,DCGAN 成功地从纯粹的噪声中生成了接近真实的图像。

Image from https://memeshappen.com/meme/success-kid-original/finally-we-did-it-6891/5

参考

查看我关于这篇文章的 Github 报告

伪追随者计算器误导用户,记者提供可疑的统计数据。

原文:https://towardsdatascience.com/fake-follower-calculators-misinform-users-journalists-with-dubious-statistics-659b60fc4d5a?source=collection_archive---------22-----------------------

99 点的误差传播

第三方 Twitter 应用并不是为拥有数百万粉丝的账户设计的。当然,反正用户就是这么做的。

虚假追随者计算器,尤其是一个名为 Twitter Audit 的平台,已经被许多新闻媒体引用,包括《每日电讯报》、【名利场】《哥伦比亚新闻评论》。但这些平台的统计技术对大账户来说远不严谨。

一个由 SparkToro 创建的假追随者计算器,一家声称获得约 180 万美元资金的初创公司,甚至使用机器学习算法来区分真实账户和虚假账户。但 Twitter 允许第三方开发者在给定时间内访问多少数据的瓶颈,迫使这些网络应用对拥有数百万粉丝的账户使用“随机样本”的模糊定义。

数学是错误的。

当程序员向 Twitter 请求数据时,Twitter 在 15 分钟内最多返回 75k 个追随者 id。如果 Twitter 返回的 id 是随机的,就不会有问题。但是这些 id 是按照最近关注用户的顺序排列的。如果我们想知道有多少假账户追随卡迪·B,想想这意味着什么

How the two platforms (SparkToro and Twitter Audit) would calculate fake followers.

左边是 SparkToro 目前的抽样方法和 Twitter Audit 直到 2017 年 5 月的方法。SparkToro 要求 Twitter 提供最新的 10 万名关注者,并对 2k 人进行抽样分析。Twitter 审计要求 75,000 名追随者,并抽样 5,000 名。

当我问 SparkToro 的联合创始人凯西·亨利(Casey Henry),他是否认为这种方法像他们的应用程序所说的那样是“随机的”(他们后来更新了网站,澄清了他们的方法),他说,“定义不是:‘对所有追随者随机’。”这是你的追随者的随机抽样。"

抛开语义不谈,这里潜在的错误使得他们的结果对于足够大的账户来说毫无价值。“实际上毫无价值”意味着如果 SparkToro 返回 Cardi B 的数字“65%是假的”,他们实际上说的是这个数字在 1%到 99%之间。可以回答“Cardi B 最近收获了多少假关注者?”但将这一比例扩大到她的数百万粉丝,就好比在康涅狄格州进行了一次民意调查后,预测有多少人会投票给一名全国候选人。

2017 年 5 月,Twitter Audit 的联合创始人大卫·卡普兰(David Caplan)开始存储旧 id,以此作为绕过 Twitter 速度限制的一种方式(右图)。这个过程很复杂,但基本上如果有超过 100 万的关注者,Twitter Audit 会在他们的数据库中存储一半的关注者。他们不检查是否有任何重复。卡普兰认可了这个图形,并尽力解释这个过程,但他不愿意分享他的代码。

卡普兰承认他使用陈旧的数据,但他说,在我联系他之前,他不知道 Twitter 会先返回最新的关注者。他在电子邮件中补充道,“当时,这个网站更像是一个玩具,所以我并不太在乎统计数据的准确性。我也没想到会有很多人用!”

诚然,这种一半关注者、充满重复、陈旧数据的方法比 Twitter Audit 在 2017 年更新之前的方法要好。然而,在最引人注目的情况下,它仍然可能有超过 50 个百分点的误差传播。

重申一下,这些误差线并不是来自用来区分“真实”和“虚假”账户的方法——尽管这总是一个容易出错的困难过程。这些额外的误差条仅来自采样过程。

Twitter Audit 为@realDonaldTrump 进行的计算从他们的 3000 万粉丝数据库中抽取了 5k 个样本,回答了“自 2017 年以来,总统获得了多少假粉丝”这一问题如果我们可以假设虚假粉丝是均匀分布的,那也没什么大不了的,但是来自《纽约时报》的报道表明,如果有人购买粉丝,这些账户可能会高度聚集,并且出现在 Twitter 历史的早期。

值得称赞的是,SparkToro 分析了每个关注@realDonaldTrump 的用户,估计他的粉丝中有 70.2%是假的。相比之下,只查看最新 id 的 Twitter Audit 发布了@realDonaldTrump 有 11%的虚假粉丝。

记者被快速统计数据误导了。

记者们可能太容易相信每个人都使用的网络应用程序。《名利场》(带有踢球者“数字不会说谎”)引用 Twitter Audit 批评@realDonaldTrump。马修·英格拉姆在《哥伦比亚新闻评论》中提到了 Twitter Audit 对@realDonaldTrump 的统计。他给我写道,“每当我在关于唐纳德·特朗普(Donald Trump)的虚假追随者之类的文章中提到它们时,我都会试图提到和/或链接到对它们价值的怀疑评估。”

自 2012 年出现以来,Twitter Audit 已经成为记者的快速来源。此类出版物不胜枚举:《华盛顿邮报》《新闻周刊》Quartz《每日电讯报》都引用了 Twitter 审计。公平地说,他们没有办法知道平台的错误——即使是它的联合创始人也没有完全意识到这些数字会有多错误。

卡普兰和亨利都提到他们已经为一些记者进行了全面的分析。然而,当被问及具体细节以及上面列出的引用该应用的故事时,卡普兰写道,“对不起,我没有完整的审计记录,所以我不能肯定。”但是由于上面列出的出版物中没有一个作者发表评论,我们不知道哪个发表的数字更可靠。SparkToro 和 Twitter Audit 都表示,他们随时准备对提出要求的记者进行全面审核。

SparkToro 并不同情一个假设的记者,他只相信他们的数字。“作为一名记者,你应该尽职尽责地了解这个工具是如何工作的,”联合创始人亨利说。我反驳说,这款应用的方法有误导性。亨利说:“你想怎么拿就怎么拿。你找到了一个边缘案例。恭喜你。”

这些工具不是用来对付政客或凯蒂·佩里的。

据他们各自的创始人称,Twitter Audit 是一个用来发现“完全是欺诈”的账户的工具,SparkToro 是一家分析公司,可以帮助广告商评估影响者的价值,其中大多数人的粉丝不到 10 万。

亨利说:“我们不是在宣传或标榜我们自己是你应该分析和批评 2020 年候选人的地方。”“这不是这个工具的用途……我们可以在上面贴一个红色标志,写着‘这个工具不做这个’但它是一个免费的工具。这对我们来说不是首要任务。这只是我们产品的一小部分。”(不过需要注意的是,SparkToro 发了一篇博文明确计算了各种政客的假追随者,包括三位民主党 2020 总统候选人。)

Twitter Audit 分享说,不到 1%的审核是针对粉丝超过 100 万的账户,SparkToro 分享说,不到 5%的审核是针对粉丝超过 10 万的账户。两者都声称每月有数万的流量。

其他虚假追随者计算器存在,但用户应该怀疑超过 75,000 名追随者的账户上给出的任何快速结果。在一个公共论坛上,Twitter 工作人员安迪·派珀写道“我不知道有任何 API 方法可以对追随者进行随机抽样。”这意味着,任何其他假追随者计算器都可能成为 SparkToro 或 Twitter Audit 同样问题的受害者。

在回答我的询问时,双方都考虑更新他们的方法,并更清楚地解释他们平台的局限性。但是,即使我们得到了稍微更准确的数字,用户和记者还应该继续引用这些数字来形容知名人士吗?当一个账户拥有数百万粉丝时——当一个人在公告牌排行榜上名列前茅或成为美国总统时——真的有人会质疑他们的影响力吗?

更新(2019 . 9 . 25):印度总理纳伦德拉·莫迪的账户在 2018 年被批评有假粉丝。推特审核被引用。对此,推特发言人表示,“‘推特审计’使用的方法存在严重缺陷,他们的不正确信息不应该被当真。”

疑问?评论?联系我,查看更多项目在 我的网站

基于异常检测的假新闻分类

原文:https://towardsdatascience.com/fake-news-classification-via-anomaly-detection-765c4c71d539?source=collection_archive---------15-----------------------

可以用异常检测来检测过滤假新闻吗?

假新闻已经成为我们这个时代最大的问题之一。它污染了线上和线下的话语。我们甚至可以说,迄今为止,假新闻对西方民主构成了明显的现实威胁。

由于技术世界已经创造了允许大规模出版这类出版物的机制,它应该想出一个解决方案来对它们进行分类和过滤,这才是公平的。

在这篇文章中,我们将尝试提出一种基于数据科学的方法,这种方法可以用来对 T2 的假新闻进行分类和过滤。

请注意,这不是一个成熟的算法,而是我们如何解决这个问题的框架描述。如果您对算法有任何建议,或者您是否愿意加入一个开源项目,尝试将它从架构变为实现,请发表评论。

算法概述

为了使用异常检测对假新闻进行分类,我们建议采取以下步骤:

  • 从多个在线来源(如 Twitter、Google、脸书)提取热门话题
  • 检测到的新闻异常
  • 交叉检查来源之间和来源内部的异常情况,找出矛盾之处
  • 将矛盾异常归类为假新闻

如果不完全清楚,请不要担心,我们会深入研究以上每一项。

异常检测

数据挖掘异常检测(也称异常检测)是对罕见项目、事件或观察结果的识别,这些项目、事件或观察结果通过与大多数数据显著不同而引起怀疑。通常,异常项目会转化为某种问题,如银行欺诈、结构缺陷、医疗问题或文本中的错误。异常也被称为异常值、新奇、噪音、偏差和异常。

Anomaly in a Time Series

本文的目的不是提供一个全面的教程(你可以在这里找到一个非常好的介绍),也不假设读者精通用于检测异常的底层算法。唯一的先决条件是你对异常现象有一个基本的直觉(见上图)。

新闻反常现象

在我们开始之前,让我们定义一下什么是新闻异常。想象一天在办公室里,在线阅读新闻。你的 twitter 账户开始关注某个事件的新闻。这可能是一个地方性的事件,一个全球性的事件,或者是你个人感兴趣的事情。不管实际的主题是什么,你都会清楚地注意到关于它的信息传递的峰值。

我们将把新闻异常定义为某个话题,它是多个来源的趋势,例如 Twitter、脸书、谷歌搜索趋势等。请注意,我们考虑到不同来源之间的时间差,因为新闻可能不会同时触及所有来源。

发现新闻异常

在我们对新闻异常进行分类之前,我们必须先检测出一个。

为此,我们需要使用来自自然语言处理的工具来解析 Twitter 提供的趋势话题和标签。我们将在一个预定义的时间窗口内收集多个属于趋势推特列表的推特,并将它们归类到主题中。

我们进一步收集 twits 流中的主要短语,创建一个时间特征向量,保存每个主题和短语随时间的量。我们现在有一个短语的时间序列。

我们继续为每个在线来源(如脸书、谷歌)制作这样一个时间序列,寻找在这些其他来源上收集的主题和短语。我们使用来自 Twitter 的 seed 趋势-主题和短语来查找脸书和谷歌搜索趋势上的上述主题参考。

完成后,我们有三个时间序列对象,每个对象代表每个源上每个趋势主题的数量(也可能是频率——但我们现在不会深入讨论)。

由于一个热门话题可能不会同时出现在所有来源上(例如首先出现在 Twitter 上,然后是脸书,然后人们开始在 Google 上搜索它),我们需要对时间序列对象进行时间对齐。我们通过尝试对时间序列对象进行相移来做到这一点,直到我们达到最小对准误差

谎言比真相传播得更快

“全世界都在关注虚假新闻及其影响政治、经济和社会福祉的可能性。为了了解虚假新闻是如何传播的,Vosoughi 等人使用了 2006 年至 2017 年 Twitter 上的谣言瀑布数据集。大约 126,000 个谣言被 300 万人传播。虚假的消息比真实的消息接触到更多的人;前 1%的虚假新闻扩散到 1000 到 100,000 人之间,而真相很少扩散到 1000 人以上。谎言也比真理传播得更快。新奇程度和接受者的情绪反应可能是观察到的差异的原因。”

来自网上真假消息的传播,Soroush Vosoughi 等人。麻省理工学院艾尔

我们知道这样一个事实,即时间对齐可能会扭曲假新闻和真新闻之间的区别,但是,在消除不同来源之间的新闻传播延迟和不丢失信息之间存在紧张关系(需要进一步思考)。这是一个很好的问题,可以通过分别测量每个源内某个主题T传播速度来解决,将传播速度作为另一个特征添加到下面的分类中。

进入异常检测。

我们将使用任何广泛可用的技术(如 ARIMA、霍尔特-温特斯等。)以便检测上述每个时间序列对象中的原始异常。注意我们必须检测正和负的音量异常(一分钟后会有更多)。

使用上面的异常列表,每个异常被检测为跨所有源的提取的趋势主题的时间点(在某些环境下)。

请注意,我们仅讨论音量异常,因为这些异常很容易从所有源类型中提取出来,同时我们也可以将异常转换到频域(使用 FFT ),创建该主题出现在新闻中的频率的时间序列。然而,由于并非所有来源都支持这种转换(例如 Google 搜索趋势),我们现在将保持在卷域。

假新闻分类

一旦我们为某个主题 T 很好地对齐了时间序列对象,我们就可以对它们进行比较。

我们正在寻找的是反相关的异常,这意味着一个来源的异常量与其他来源的异常量有很大不同。

我们现在对原始文本数据运行第二步(仅在异常窗口)。我们在这些窗口中收集所有文本数据,寻找证据表明人们已经确定这些是假新闻。这可能包括词语假,假新闻,或任何其他暗示人们指的是虚假新闻的短语(为了清楚起见,我们将这一步过于简化,然而词语假新闻等。必须在句子语序中的某个附近的上下文中,或者在句法上连接,但是我们不会在这里进行如此详细的描述)。

Comparing the appearance of the words “Fake news” with “Pizzagate” in Google Trends

然后,我们将运行最终分析步骤,在该步骤中,我们对异常窗口中的原始文本数据使用情感分析,以情感方式标记时间上的反相关点,即,人们对多个来源的文本具有强烈的负面和正面情感。

结合上述发现,我们现在可以在上述每个分类步骤之间执行多数投票,输出一组检测。每一次检测都是一个时间点(或环境),其中根据上述过程,话题 T 被怀疑是假新闻。

在时间轴上设置检测,我们测量检测之间的量趋势,即我们绘制一个图,其中 Y 轴是消息(或搜索)量,X 轴是时间,在该图上我们绘制主题 T 随时间的组合量。

如果上面的图表是一个缓慢开始的趋势线,随后是一个峰值,然后下降(快速或缓慢),我们将该主题定义为假新闻。否则,如果图形显示出很大的变化,我们就完全放弃检测。

Normal News Trend

这一最终过滤步骤背后的原因是,趋势新闻通常表现为波浪,即开始缓慢,达到峰值,然后逐渐减弱。如果我们检测到一个很大的方差信号,这可能表明这根本不是一条新闻(然而,这需要对实际数据进行进一步调查)。

呼吁采取行动

正如本文开头提到的,我们并没有提供一个经过测试的假新闻分类器,而是一个算法思想。为了证明以上是具有可接受的召回和准确性水平的可行解决方案,需要在从多个来源提取的大型语料库上实现该算法,并且相对于已知假新闻案例(即,标记数据)来验证该算法。

请随意指出上述方法中的任何问题,或者加入我们实现上述方法的开源计划。

基于 BERT 的假新闻分类

原文:https://towardsdatascience.com/fake-news-classification-with-bert-afbeee601f41?source=collection_archive---------15-----------------------

Photo by Connor Danylenko on Pexels

假新闻是一种通过新闻媒体和/或社交媒体故意传播虚假信息的宣传方式。鉴于假新闻的传播可能会产生严重影响,如左右选举、加剧 T2 的政治分歧,开发检测假新闻内容的方法非常重要。在本帖中,我们将使用一种叫做 BERT 的算法来预测新闻报道是否是假的。

BERT 代表来自变压器的双向编码器表示。描述伯特算法的论文由谷歌发表,可以在这里找到。BERT 的工作原理是随机屏蔽单词标记,并用基于上下文的向量来表示每个被屏蔽的单词。BERT 的两个应用是“预训练”和“微调”。

预训练 BERT

对于预训练 BERT 算法,研究人员训练了两个无监督学习任务。第一个任务被描述为屏蔽 LM。其工作原理是随机屏蔽 15%的文档,并预测这些被屏蔽的标记。第二个任务是下一句预测(NSP)。这是由问答和自然语言推理等任务激发的。这些任务需要模型来准确捕捉句子之间的关系。为了解决这个问题,他们对二进制预测任务进行了预训练,该任务可以从单一语言的任何语料库中轻松生成。他们在论文中给出的例子如下:如果你有句子 A 和 B,A 有 50%的时间被标注为“isNext”,另外 50%的时间是从语料库中随机选取的句子,被标注为“notNext”。针对这一任务的预训练被证明对于问题回答和自然语言推理任务是有益的。

微调伯特

微调 BERT 的工作原理是用自我关注对连接在一起的文本对进行编码。自我注意是学习当前单词和先前单词之间相关性的过程。这一点的一个早期应用是在长短期记忆()论文(Dong2016)中,研究人员使用自我注意进行机器阅读。BERT 的好处在于,通过对带有自我关注的串联文本进行编码,可以捕捉句子对之间的双向交叉关注。

在本文中,我们将应用 BERT 来预测一个文档是否是假新闻。这个数据集的名字是关于假新闻的真相,在这里可以找到。这篇文章的灵感来自于 BERT to the Rescue ,它使用 BERT 对 IMDB 数据集进行情感分类。从 伯特到营救 的代码可以在这里找到。

BERT 算法的输入是单词序列,输出是编码的单词表示(向量)。对于单句分类,我们使用每个单词的向量表示作为分类模型的输入。

现在让我们开始吧!

  1. 导入包
import pandas as pd 
import numpy as np 
import torch.nn as nn
from pytorch_pretrained_bert import BertTokenizer, BertModel
import torch
from torchnlp.datasets import imdb_dataset
from keras.preprocessing.sequence import pad_sequences
from sklearn.metrics import classification_report

2.数据探索

首先,让我们将数据读入 dataframe 并打印前五行。我们还可以将最大显示列数设置为“无”。为简单起见,让我们看看“文本”和“类型”列:

pd.set_option('display.max_columns', None)
df = df[['text', 'type']]
df = pd.read_csv("fake.csv")
print(df.head())

我们的分类模型的目标在“类型”列中。为了了解“type”值的分布和种类,我们可以使用集合模块中的“Counter ”:

from collections import Counter
print(Counter(df['type'].values))

我们感兴趣的是分类新闻文本是否是假的。我们可以看到我们只有 19 条“假”新闻记录。新闻标签的其他变体对应于不可靠的新闻来源,如“仇恨”,这是一种宣扬种族主义、厌女症、同性恋恐惧症和其他形式的歧视的新闻。另一个是“clickbait ”,通过耸人听闻的标题优化广告收入最大化。数据的完整描述可在这里找到。理想情况下,我们希望我们的目标具有“假新闻”和“真新闻”的值。不幸的是,这些数据并没有提供一个我们可以用作对照组的新闻类别。为了简单起见,我们可以将我们的目标定义为“虚假”和“讽刺”,看看我们是否可以建立一个分类器来区分这两者。我鼓励读者尝试用一些其他标签构建其他分类器,或者用“真实”新闻增强数据集,这些新闻可以用作对照组。

3.资料准备

由于我们想要对应于“假”和“讽刺”的“类型”值的数据,我们可以如下过滤我们的数据:

df = df[df['type'].isin(['fake', 'satire'])]

我们用“计数器”验证我们得到了期望的输出:

print(Counter(df['type'].values))

让我们也放弃“南”值:

df.dropna(inplace = True)

接下来,我们想平衡我们的数据集,这样我们就有相同数量的“假”和“讽刺”类型。我们还应该随机改变目标:

df_fake = df[df['type'] == 'fake'] 
df_statire = df[df['type'] == 'satire'] 
df_statire = df_statire.sample(n=len(df_fake))
df = df_statire.append(df_fake)
df = df.sample(frac=1, random_state = 24).reset_index(drop=True)

再次验证我们得到了想要的结果:

print(Counter(df['type'].values))

接下来,我们要格式化数据,这样它就可以用作我们的 BERT 模型的输入。我们将数据分为训练集和测试集:

train_data = df.head(19)
test_data = df.tail(19)

我们用“文本”和“类型”关键字生成一个字典列表:

train_data = [{'text': text, 'type': type_data } for text in list(train_data['text']) for type_data in list(train_data['type'])]
test_data = [{'text': text, 'type': type_data } for text in list(test_data['text']) for type_data in list(test_data['type'])]

从字典列表中生成元组列表:

train_texts, train_labels = list(zip(*map(lambda d: (d['text'], d['type']), train_data)))
test_texts, test_labels = list(zip(*map(lambda d: (d['text'], d['type']), test_data)))

生成令牌和令牌 id:

tokenizer = BertTokenizer.from_pretrained('bert-base-uncased', do_lower_case=True)
train_tokens = list(map(lambda t: ['[CLS]'] + tokenizer.tokenize(t)[:511], train_texts))
test_tokens = list(map(lambda t: ['[CLS]'] + tokenizer.tokenize(t)[:511], test_texts))train_tokens_ids = list(map(tokenizer.convert_tokens_to_ids, train_tokens))
test_tokens_ids = list(map(tokenizer.convert_tokens_to_ids, test_tokens))train_tokens_ids = pad_sequences(train_tokens_ids, maxlen=512, truncating="post", padding="post", dtype="int")
test_tokens_ids = pad_sequences(test_tokens_ids, maxlen=512, truncating="post", padding="post", dtype="int")

请注意,我们将输入字符串截断为 512 个字符,因为这是 BERT 可以处理的最大令牌数。

最后,为我们的测试和训练集生成一个基于“type”值的布尔数组:

train_y = np.array(train_labels) == 'fake'
test_y = np.array(test_labels) == 'fake'

4.模型建立

我们创建了我们的 BERT 分类器,它包含一个“初始化”方法和一个返回令牌概率的“转发”方法:

class BertBinaryClassifier(nn.Module):
    def __init__(self, dropout=0.1):
        super(BertBinaryClassifier, self).__init__()self.bert = BertModel.from_pretrained('bert-base-uncased')self.dropout = nn.Dropout(dropout)
        self.linear = nn.Linear(768, 1)
        self.sigmoid = nn.Sigmoid()

    def forward(self, tokens, masks=None):
        _, pooled_output = self.bert(tokens, attention_mask=masks, output_all_encoded_layers=False)
        dropout_output = self.dropout(pooled_output)
        linear_output = self.linear(dropout_output)
        proba = self.sigmoid(linear_output)
        return proba

接下来,我们生成训练和测试掩码:

train_masks = [[float(i > 0) for i in ii] for ii in train_tokens_ids]
test_masks = [[float(i > 0) for i in ii] for ii in test_tokens_ids]
train_masks_tensor = torch.tensor(train_masks)
test_masks_tensor = torch.tensor(test_masks)

生成用于训练和测试的令牌张量:

train_tokens_tensor = torch.tensor(train_tokens_ids)
train_y_tensor = torch.tensor(train_y.reshape(-1, 1)).float()
test_tokens_tensor = torch.tensor(test_tokens_ids)
test_y_tensor = torch.tensor(test_y.reshape(-1, 1)).float()

最后,准备我们的数据加载器:

train_dataset =  torch.utils.data.TensorDataset(train_tokens_tensor, train_masks_tensor, train_y_tensor)
train_sampler =  torch.utils.data.RandomSampler(train_dataset)
train_dataloader =  torch.utils.data.DataLoader(train_dataset, sampler=train_sampler, batch_size=BATCH_SIZE)test_dataset =  torch.utils.data.TensorDataset(test_tokens_tensor, test_masks_tensor, test_y_tensor)
test_sampler =  torch.utils.data.SequentialSampler(test_dataset)
test_dataloader =  torch.utils.data.DataLoader(test_dataset, sampler=test_sampler, batch_size=BATCH_SIZE)

5.微调

我们使用 Adam 优化器来最小化二进制交叉熵损失,并且我们使用 1 个时期的批量大小 1 来训练:

BATCH_SIZE = 1
EPOCHS = 1bert_clf = BertBinaryClassifier()
optimizer = torch.optim.Adam(bert_clf.parameters(), lr=3e-6)for epoch_num in range(EPOCHS):
    bert_clf.train()
    train_loss = 0
    for step_num, batch_data in enumerate(train_dataloader):
        token_ids, masks, labels = tuple(t for t in batch_data)
        probas = bert_clf(token_ids, masks)
        loss_func = nn.BCELoss()
        batch_loss = loss_func(probas, labels)
        train_loss += batch_loss.item()
        bert_clf.zero_grad()
        batch_loss.backward()
        optimizer.step()
        print('Epoch: ', epoch_num + 1)
        print("\r" + "{0}/{1} loss: {2} ".format(step_num, len(train_data) / BATCH_SIZE, train_loss / (step_num + 1)))

我们评估我们的模型:

bert_clf.eval()
bert_predicted = []
all_logits = []
with torch.no_grad():
    for step_num, batch_data in enumerate(test_dataloader):token_ids, masks, labels = tuple(t for t in batch_data)logits = bert_clf(token_ids, masks)
        loss_func = nn.BCELoss()
        loss = loss_func(logits, labels)
        numpy_logits = logits.cpu().detach().numpy()

        bert_predicted += list(numpy_logits[:, 0] > 0.5)
        all_logits += list(numpy_logits[:, 0])

print(classification_report(test_y, bert_predicted))

Classification Report

鉴于我们没有太多的训练数据,结果证明性能精度相当低。随着更多的数据和更大数量的历元,这个问题应该会得到解决。

BERT to the Rescue中可以找到对代码的更彻底的遍历。再次,我鼓励你尝试修改分类器,以预测一些其他标签,如政治宣传中的“偏见”。另一个有趣的标签是“垃圾科学”,这是促进伪科学和其他科学上可疑的说法的来源。可以在这里找到数据的描述及其标注方式。这篇文章的代码可以在 GitHub 上找到。

感谢阅读,机器学习快乐!

假消息还是假消息?

原文:https://towardsdatascience.com/fake-news-or-not-edad1552aa02?source=collection_archive---------14-----------------------

使用自然语言处理来识别文章

(Original photo by Robert Owen-Wahl.)

虽然“假新闻”一词近年来成为热门话题,但假新闻本身并不新鲜。事实往往比小说更离奇,这也不是什么新鲜事——有时真实的故事很难让人相信。

研究员克莱尔·沃德尔将假新闻分为七种类型,大致以欺骗的意图来衡量。

7 Types of Mis- and Disinformation (Source: “Fake News. It’s Complicated.”)

洋葱 属于第一种:讽刺或戏仿。该组织于 1988 年开始出版讽刺文章,然后于 1996 年上线。来自 The Onion 及其讽刺姐妹网站的文章在 Reddit subredditr/The Onion上分享,截至 2018 年 12 月 21 日,该网站拥有 83.1 万订户。

还有一个副标题r/not theo nion:“对于真实的故事,它们是如此的荒谬,以至于你可以发誓它们来自洋葱。”它拥有 1450 万订户,远远超过了它所引用的出版物的子编辑数量。

另一个分支 联盟 拥有 22.9 万用户,数量也超过了联盟的用户。这一篇是献给“那些没有把《洋葱》的文章视为讽刺作品的人”

这两种观点都指向了人们对“奇怪但真实”的新闻的兴趣以及将事实与虚构分开的挑战。

这就提出了一个问题:

能否利用自然语言处理,仅凭标题来预测一篇文章是来自 r/TheOnion(假新闻)还是来自 r/nottheonion(真新闻)?

Even Reddit users get r/TheOnion and r/nottheonion mixed up.

数据收集

我最初使用 Reddit API 来收集数据,但是被限制在每个 subreddit 1000 个帖子。我还试着使用 PRAW,但无法超过 1000 篇文章的上限,因为它只是 Reddit API 的一个包装器。

然而, pushshift.io API 不受上限限制。使用这个,我收集了每个子主题、 r/TheOnionr/nottheonion 的 10,000 个最近提交的内容(在收集时)。

向每个部门提交的内容跨越不同的时间范围:

  • r/TheOnion:2016 年 9 月 22 日至 2018 年 12 月 17 日
  • r/nottheonion:2018 年 10 月 26 日至 2018 年 12 月 17 日

r/not theon的较短时间跨度反映了更大的用户基数和更大的信号源多样性。人们可以提交来自任何用英语撰写的“原始、可靠来源”的新闻文章,而 r/TheOnion 只接受来自 The Onion 或姐妹网站的文章。

处理

在收集之后,根据以下步骤处理每个提交的标题:

  1. 重音符号被删除,所以单词不会被不恰当地分开。(例如,“神奇宝贝”被变成了“神奇宝贝”和“神奇宝贝”。)
  2. 标点符号被删除。
  3. 大写被删除,以便只返回小写单词。
  4. 单词被词条化了。
  5. 停用字词已被删除。

这些步骤把这个:

Fighting Fire With Fire: Mitch McConnell Is Attempting To Channel Alexandria Ocasio-Cortez’s Populist Appeal By Preparing A Supper Of Boiled Dog Live On Instagram

变成这个“干净”的标题:

fighting fire fire mitch mcconnell attempting channel alexandria ocasio cortez populist appeal preparing supper boiled dog live instagram

从这里开始,数据被分成训练和测试数据,然后使用CountVectorizer()进行矢量化。

建模

由于每个子编辑有 10,000 个提交,基线准确度分数是 50%。换句话说,一个以同样方式对每本书进行分类的模型将会得到一半的正确答案。

我运行了几种不同的分类模型,并获得了每种模型的交叉验证、训练和测试准确度分数。测试分数如下:

  • 随机森林(通过 GridSearch 调整超参数):84.5%
  • 逻辑回归分析:86.8%
  • 多项式朴素贝叶斯:87.1%

所有模型都返回了比基线更好的准确度分数。

选定的模型详细信息

尽管多项朴素贝叶斯的表现略好于逻辑回归,但考虑到可解释性,我最终选择了逻辑回归。

准确性得分如下:

  • 交叉验证:85.5%
  • 培训:97.3%
  • 测试:86.8%

分类标准:

  • 错误分类率:13.2%
  • 召回率/敏感度:86.4%
  • 特异性:87.3%
  • 精确度:87.2%

该模型显然与训练数据过度拟合,但从交叉验证得分来看,其表现与预期大致相同。它能够仅凭标题就以 86.8%的准确率预测一篇文章是来自 r/TheOnion 还是 r/nottheonion

Word clouds showing the most frequent words in r/TheOnion (left, in green) and r/nottheonion (right, in red).

洞察力

关联词语

我生成了上面的单词云来显示每个子编辑的“干净”标题中的单词频率。和 T2 有许多相同的最常用的词:男人,王牌,女人,比如说,新的。

逻辑回归能够提供更多有用的信息:与每个子主题最相关的单词。在 r/TheOnion 中,更有可能出现在标题中的单词是小测验、国家、博客、不可思议和提示。更有可能出现在标题中的词是便便、被捕、起诉、说和警察。

便便和 r/not theon

该模型识别出包含单词“便便”的标题更有可能来自r/nottheoneion。事实上,包含这个词的 47 个标题中有 46 个来自这个子编辑。这些投稿中有许多被删除了,因为它们没有“独特的品质”(看起来更像讽刺而不是新闻,不仅仅是一个有趣的标题),或者来自不可靠的新闻来源。

下面是一篇提交给r/not theon但被删除的文章的例子:

[## 科学家们吃了乐高玩具,看看需要多长时间才能把它们拉出来

儿科医生在日常工作中必须处理各种有趣的情况,包括儿童饮食…

www.yahoo.com](https://www.yahoo.com/news/scientists-ate-legos-see-long-takes-poop-them-184836638.html)

提交给 r/TheOnion 的带有“便便”一词的投稿被删除,因为它不是来自洋葱或其姐妹网站。它包含在测试数据中,但被错误分类。

更多的错误分类

如上所述,一些错误分类可能是由包含模型与其他子编辑相关联的单词的标题引起的。仔细观察会发现,由于没有遵循提交指南,此类提交也可能已从给定子编辑中删除。由于各种原因,许多其他的错误分类也被从它们给定的子分类中删除。这表明过滤掉从子编辑中删除的提交内容有助于提高模型的准确性。

复制

然而,由于重复,许多提交内容也从 r/TheOnionr/nottheonion 中删除。能够看到这些提交可以洞察订阅者对文章的兴趣。

r/not theon的情况下,它还可以洞察订户认为哪些文章适合子编辑,即使它们最终被删除。例如,在 2018 年 12 月 2 日至 12 月 4 日期间,下面这篇文章(或来自其他网站的类似标题的文章)在 r/nottheonion 上提交了 80 多篇,显示出新闻发布后不久的一系列活动。

[## “这是真正的我”:尼日利亚总统否认死亡和被克隆人取代

穆罕默杜·布哈里打破沉默,澄清了一个在社交媒体上流传数月的谣言

www.theguardian.com](https://www.theguardian.com/world/2018/dec/03/its-real-me-nigerian-president-denies-dying-and-being-replaced-by-clone?CMP=fb_gu&fbclid=IwAR20Y9NWZdaIfUGiXnwQYlGd3s1bGK32XbGWNJwxlrubVgR3mDZtTZEVDik)

真实世界的事件

r/TheOnion 的情况下,讽刺文章的重复发布可能与现实世界的事件有关。在 2017 年 10 月 2 日至 2018 年 11 月 8 日期间,有 27 篇关于枪支暴力的文章提交:

[## 唯一一个经常发生这种情况的国家说,没有办法防止这种情况

加利福尼亚州千橡市——在加利福尼亚州发生暴力事件后的几个小时内,一名单独的袭击者杀死了 12 名…

www.theonion.com](https://www.theonion.com/no-way-to-prevent-this-says-only-nation-where-this-r-1830308976)

这篇文章促成了将单词“nation”与这个子编辑相关联的模型。但更重要的是,这些转发通常与真实的大规模枪击事件相对应,包括:

  • 内华达州拉斯维加斯(2017 年 11 月 1 日)
  • 德克萨斯州萨瑟兰斯普林斯(2017 年 11 月 5 日)
  • 加利福尼亚州特哈马县(2017 年 11 月 14 日)
  • 佛罗里达州帕克兰市斯通曼·道格拉斯高中(2018 年 2 月 14 日)
  • 德克萨斯州圣菲高中(2018 年 5 月 18 日)
  • 马里兰州安纳波利斯《首都公报》(2018 年 6 月 29 日)
  • 加利福尼亚州贝克斯菲尔德(2018 年 12 月 9 日)
  • 北卡罗来纳州巴特勒高中(2018 年 10 月 29 日)
  • 加利福尼亚州千橡市(2018 年 11 月 8 日)

事实上,许多 r/TheOnion 用户开始使用转发作为另一场大规模枪击事件发生的信号,其中一位评论“通常我在看到洋葱的文章之前就听说了枪击事件。这次不会了。”

后续步骤

我想对这个项目采取的一些后续步骤包括:

  • 深入理解错误分类的标题。
  • 尝试过滤掉从子编辑中删除的提交内容。
  • 查看 r/TheOnion 中的重复帖子如何与真实新闻相关联。
  • 开发更稳健的模型。

感谢阅读!想法、问题和反馈总是很受欢迎。还可以查看本项目 GitHub 回购。

掉进栅栏里——一场不平衡的“噩梦”

原文:https://towardsdatascience.com/falling-through-the-grates-an-unbalanced-nightmare-1f907cf5d337?source=collection_archive---------47-----------------------

欢迎来到第一版“用数据科学回答的愚蠢问题”我写这篇博客的目的是探究一些我们可能会问自己,却经常问不到别人或找不到答案的问题和担忧。通过检查这些问题,我将不得不提高和使用我的数据科学技能,概念上和实践上的,并且我在我的严格分析之后写的文章——或者缺乏分析,你来判断——将有希望以有趣和容易学习的方式阐明数据科学概念或技术。

既然介绍已经不存在了…这里谁已经采取了措施(看我在那里做了什么?)在城市或市区避免走过通风管道或其他格栅?好,很好,我看到很多人举手了。“人们”(当然不是我)害怕当走过一个时,炉栅会倒塌,他们会被遗忘,再也看不到。

City grates — a petrifying view for some…

但是这种情况发生的频率有多高呢?我敢肯定,每个人都有第四或第五手的故事,他们堂兄的男朋友的叔叔看着某人从壁炉中掉下来,自己侥幸躲过了这场灾难。轶事给这种以某种频率发生的传说火上浇油。

但是…这些轶事之外没有数据。我把我的研究范围限定在我的家乡华盛顿特区。我找到的是当这样的事情发生时的新闻报道,但是从 DC 的公开数据中没有列出一个涉及倒下或倒塌的壁炉的行人事故。这些数据涉及超过 220,000 起不同的事故,包括车辆、自行车和行人。下面的链接显示了当这样的事件发生时所写的故事类型。这并没有发生在华盛顿特区,但是为了我的分析,我假设每 220,000 起事故中有 1 起这样的事件发生。

[## 噩梦:一名男子从鲍尔斯顿的栅栏中掉了下去

在鲍尔斯顿,一名男子步行去参加一个会议,在他踩到一个钢格栅后,从 15 英尺高的地方掉进了通风井…

dcist.com](https://dcist.com/story/13/10/09/nightmare-man-falls-through-grate-i/)

从栅栏里掉下去的概率是多少?. 000455%……完全不可能。考虑到每天走在 DC 街道上的数百万行人,这还没有考虑事故发生的可能性。这是众多事件中的一个特例,表明我们称之为“不平衡的阶级”这只是用一种方式来说明分类问题中的数据,在这个问题中,各个类别并没有被平等地表示。

如果我们想创建一个模型来预测某人是否会从壁炉中掉下来,我们就遇到了一个问题。我们可以很容易地预测它永远不会发生,我们在 99.999555%的时间里都是正确的。任何具有这种精确度的模型听起来都很棒,对吗?!接受我们将完全错过这个——我将称之为“黑天鹅”的事件。试着告诉 DCist 文章里的那个家伙,他永远不会掉进栅栏里…我肯定他会喜欢听的。底线是准确性悖论是不可接受的,当后果严重到足以保证成千上万的人在走路时避开栅栏

这是一个半开玩笑的例子,但这些同样不平衡的类存在于预防和预测恐怖袭击。这是一个现实世界的问题,有着同样的问题…那么我们如何让我们的模型更好地进行预测呢?

更多数据!

解决这个问题的第一个方法是在不平衡阶层的多数人较少的地方收集足够的数据。如果这真的是我们试图预测的事情,我们将不得不努力寻找更多“成功”的例子。在炉排的情况下,任何超过一个例子肯定会有所帮助。

采样

在机器学习中,有可能人为地创建少数类的附加示例。这些重采样技术有助于确定对人从炉栅中跌落影响最大的因素(即模型特征)。有两种这样的方法可以使用,SMOTE 和 Near Miss。这两者,包括代码,在这篇中间文章中有很好的解释。

异常检测

看待这个问题的另一种方式是改变我们的视角。如果我们真的把掉进栅栏里的人看作黑天鹅,这将是我们数据中的一个异常。异常检测用于检测银行欺诈,以及许多医疗应用。这篇中型文章熟练地描述了如何在 Python 中进行异常检测。

结论

有时候数据和真人的感知不相符。有时候,220,000 次事件中只有一次会导致行为的改变。事件的罕见发生使得通过数据科学进行预测变得更加困难,但是通过解决不平衡类别的技术创建更好的模型是可能的。收集更多数据、采样技术和异常检测是这些方法中的一部分。如果没有这些方法,我们的预测在纸上看起来会很好,但无法检测出失败是如何发生的。

我希望你喜欢这篇文章。如果您认为我应该在下一篇文章中研究任何建议或问题,请联系我!

不充分 A/B 测试中的错误发现率、M 型和 S 型错误

原文:https://towardsdatascience.com/false-discovery-rate-type-m-and-type-s-errors-in-an-underpowered-a-b-test-d323df5271fa?source=collection_archive---------33-----------------------

如果我们转向模拟,我们可以很容易地理解在不充分的统计测试中的“统计显著性”发现如何能够提供关于观察到的效应的再现性、其大小以及其符号的错误结论。

Harmelen, Netherlands

错误的答案

假设我们正在对两个版本的登录页面进行 A/B 测试,我们观察到 4.32%的差异。观察值是随机产生的几率有多大?

我们运行一个双尾 t 检验,对于 p 值,我们得到 0.0139。由于我们决定显著性水平为α = 0.05,我们宣布差异具有统计显著性,并继续实现版本 B 作为我们的登录页面。却看到生产中的表现变成了上面观察到的效果的四分之一。

Figure 1. An example of an A/B test output. Data is simulated using the beta distribution. True means for two distribution are 1.5%(A) and 2.8%(B)

发生了什么事?如果我们将 p 值解释为:“鉴于两个版本之间的真实差异为零,观察到差异 4.32%的概率为 1.39%”,我们将完全错误。

假阳性率不应与假发现率混淆

如果我们能够不止一次地运行上面的 t 检验,比如说几万次,我们会对给出错误答案的可能性有一个更清晰的了解,就像上面的例子一样。幸运的是,比如 Rstudio,让我们可以通过模拟来做到这一点。

我们做一万次 t 检验。首先,我们需要转化率 A 和 b 的两个分布,我取贝塔分布。这两个分布将由它们的形状参数定义,我将设置为:

  • 版本 A
# conversions (i.e the number of visitors who subscribed)
> shape1 <- 2 
# visitors who chose not to subscribe (complement of shape1)
> shape2 <- 131
  • 版本 B
# conversions (i.e the number of visitors who subscribed)
> shape1 <- 4
# visitors who chose not to subscribe (complement of shape1)
> shape2 <- 137

请注意,我选择这些分布作为基础事实。意思是,差异的真实均值是 1.3%。图 1 中的例子。是从这些中取样的,因此转换率不同。

如果两个参数足够大且彼此接近,则贝塔分布接近正态分布。但是我们的访问者数量很少,相对于选择不订阅的访问者数量,订阅者的数量更成问题。也就是说,t 检验中假设的两个条件中有一个不满足。然而,这是我们的模拟,我们的游乐场。让我们看看如果我们假设两个样本接近正态会发生什么。

Figure 2. Density plots after 10,000 simulated t-tests. Distribution of the null hypothesis is presented in grey. Simulated data with the mean being the true difference between versions A and B in blue. Red dotted lines represent 95% confidence interval of the null hypothesis. The full grey line represents the example observed effect size 4.32%.

图二。显示真实效果大小有多小和多嘈杂。它几乎与零假设重叠。小的真实效应大小加上噪声测量总是产生不可靠的测试。下面,图 3。展示了我们 t-test 的威力。只有 11%。假设零假设是错误的,只有 11%的 t 检验会正确地拒绝它。这是一个动力不足测试的极端例子。

Figure 3. Left: the red shaded area represents the power of a t-test where the true effect size is 1.3%. The fraction, equal to the power, of tests that show real effect is the true positive rate. Right: the grey shaded area represents the p-value (false positive rate) for the example effect size.

为了使用我们测试的能力来计算真正的阳性率,我们需要假设显示真实效果的测试分数(零假设是假的)。这个分数无法精确测量。基本上是贝叶斯先验。一般来说,我们必须在实验开始前假设它。我将使用 10%,因为我们的 10,000 次 t 检验中有 1,064 次显示差异为正,并且其 95%的置信区间不包含零。因此,我们有以下内容:

# number of **true positive** : 
> true_positive <- 10000 * 0.1 * 0.11
> true_positive
[1] **110**# number of **false positive** : 
> false_positive <- 10000 * 0.9 * 0.05
> false_positive
[1] **450**# where 10000 is the number of simulated samples
#      0.1 is the fraction of samples that contain the real effect
#      0.11 is the power
#      0.9 is the fraction of samples that don't contain the real #effect
#     0.05 is the significance level

现在,我们可以将假发现率计算为假阳性与阳性发现总数的比率:

**# false discovery rate**: 
> false_discovery_rate <- false_positive / (false_positive + true_positive)
> false_discovery_rate
[1] 0.8036

因为我们测试的功效如此之低,80%的时间我们的“统计显著性”发现将是错误的。因此,在我们的例子中,观察到的差异为 4.32%,p 值为 0.0139,我们犯第一类错误的概率不是 1.39%,而是 80%。

M 型(夸张)和 S 型(符号)错误

在动力不足的测试中,问题不会以错误的发现而结束。在图 4 中,我们可以看到 10,000 次 t 检验的 p 值分布。其中 11050 个等于或小于 0.05。这是意料之中的,因为功率是 11%。发现网站两个版本之间显著差异的平均值为 4.08。这意味着,平均而言,t 检验会产生比真实效应大小大 3 倍的显著结果。更进一步,在这 11,050 个差值中,有 57 个有错误的符号。我们有很小的机会观察到我们网站的版本 A 比版本 B 表现得更好!

Figure 4. Left: the distribution of the 10,000 p-values. Right: observed effect sizes that correspond to p-value ≤ 0.05

R 中有一个库,可以用来计算这两个误差和乘方。叫做逆向设计。我们的真实效应大小和合并标准误差的输出为:

> library(retrodesign)
> retrodesign(0.013, 0.017)
$power
[1] 0,1156893$typeS
[1] 0,02948256$exaggeration
[1] 3,278983

能做些什么?

动力不足的统计测试总是误导人。为了避免对计算出的 p 值得出错误的结论,在进行 A/B 测试之前必须做一些事情:

  • 必须设定真实效应大小的估计值。由于偏倚和较大效应具有较小 p 值的趋势,观察到的效应大小往往被夸大。
  • 基于该估计和测得的噪声,必须设置适当的样本大小,以实现可接受的功率水平。如上所述,低功率使得测试不可重复。
  • 显著性级别应设置为小于 0.003 的值。根据 James Berger [5,6],p 值=0.0027 对应于 4.2%的假发现率,这接近于 0.05 显著性水平的假阳性率。

这些模拟的代码可以在我的 GitHub 上获得。

这些形状参数会给我们带来较大的标准误差和较低的功耗,这正是我们在这个例子中所需要的。由于样本量不足或数据偏斜导致的非正态数据经常出现。转换率样本分布在没有仔细检查条件的情况下被草率地假设为接近正态。如例子所示,这会导致错误的发现。

retrodesign 库是由 Andrew Gelman 和约翰·卡林在他们的论文中创建的[1]。

参考

[1]安德鲁·盖尔曼和约翰·卡林(2014)。超越功率计算:评估 S 型(符号)和 M 型(量值)误差。心理科学透视。第九卷第六期第 641 至 651 页。
(DOI:10.1177/1745691614551642

[2]大卫·科尔昆(2014 年)。对错误发现率和 p 值误解的调查。R. Soc。打开 sci。1: 140216.http://dx.doi.org/10.1098/rsos.140216

[3]克里斯·斯图基奥(2013 年)。使用贝叶斯规则分析转换率(贝叶斯统计教程)。https://www . chriss tuccio . com/blog/2013/Bayesian _ analysis _ conversion _ rates . html

[4]彼得·博登(2014 年)。差点让我被解雇。https://blog . sumall . com/journal/optimize ly-get-me-fired . html

[5]塞尔克 T,巴亚里 MJ,伯杰乔。(2001).检验精确零假设的 p 值校准。我是。统计。55, 62–71.(DOI:10.1198/000313001300339950)

[6]伯格·乔,塞尔克·t .(1987 年)。检验一个点零假设:p 值和证据的不可调和性。J. Am。统计。协会 82,
112–122。(DOI:10.1080/01621459 . 1987 . 10478397)

熟悉相似系数

原文:https://towardsdatascience.com/familiarity-with-coefficients-of-similarity-73697d357acf?source=collection_archive---------9-----------------------

当你在做一个关于推荐系统或图像语义分割的项目时,你一定遇到过相似性得分。基于这些相似性分数,你预测这个产品与那个产品相似或者预测的分割图像与地面真实情况相似的程度。

相似性度量是重要的,因为许多数据挖掘技术使用这些度量来确定项目或对象之间的相似性,用于根据要求的不同目的,例如聚类、异常检测、自动分类、相关性分析。本文将向您简要介绍不同的相似性度量,而不会过多涉及技术细节。

本文的重点是向您介绍下面的相似性度量标准,

1.简单匹配系数

2.Jaccard 索引

3.欧几里得距离

4.余弦相似性

5.居中或调整的余弦指数/皮尔逊相关性

开始吧!

假设,两个用户 A 和 B 在表单中提供了对十种产品的评论,不管他们是否喜欢这些产品。让我们把它写成向量的形式,

A = (P,P,P,P,P,N,N,N,N

B = (P,P,P,N,N,N,N,N,N

其中 P 表示用户喜欢该产品,N 表示用户不喜欢该产品。

A & B 用户的 SMC 计算如下:

在哪里,

M11(两个 A & B 都喜欢这个产品)= 3

M00(双方都不喜欢该产品)= 5

M10 (A 喜欢该产品,但 B 不喜欢)= 2

M01 (A 不喜欢该产品,但 B 喜欢)= 0

对于 A & B 用户,SMC 为 8/10,即 0.8。这向我们展示了用户 A & B 有 80%的时间是相似的。因此,如果 A 喜欢一种新产品,而 B 还没有见过,你可以向 B 推荐,因为两者有很好的相似性。

这是构建推荐系统的一种非常简单和直观的方法。理想情况下,我们应该使用用户 A 和 b 给出的 1 到 5 的等级,在此基础上,我们可以得出他们喜欢该产品的程度,然后在此基础上提供推荐。

这里要注意的重要一点是,SMC 在向量具有二元属性(正/负)、(真/假)和(男/女)并且两个类携带相同信息的情况下是有用的。

你能想出为什么我们不能在类不携带相等信息的情况下使用 SMC 的任何理由吗?这个问题把我们带到了新的相似性度量。

Jaccard 指数:

让我们考虑另一种情况。一家保险公司希望根据相似性对其客户提交的索赔进行分类。他们有一个索赔数据库,数据库中有 100 个属性,在此基础上,公司决定索赔是否欺诈。属性可以是人的驾驶技术、汽车检查记录、购买记录等。每个属性为索赔生成一个红色标志。在大多数情况下,只有少数属性会生成红旗,其他属性很少会改变。

在这种情况下,红旗比绿旗向保险公司提供了更多的信息(不对称)。

如果我们使用 SMC,我们得到的分数会因很少产生危险信号的属性而有所偏差。在这种情况下,使用 Jaccard 索引。让我们用数字来核对一下。

考虑三个声明 A,具有 20 个二元属性的 B & C,

索赔 A = (R,R,R,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G)

索赔 B = (R,R,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G)

索赔 C = (R,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G)

每对的 Jaccard 指数计算如下:

在哪里,

M11-两个声明都带有红色标记的属性数量,

M10,M01-一个索赔带有红色标志,另一个带有绿色标志的属性数。

对于索赔 A 和 B,Jaccard 指数是 2/3,即 0.66,SMC 是 19/20,即 0.95。

对于索赔 A 和 C,Jaccard 指数为 1/3,即 0.33,SMC 为 18/20,即 0.90。

对于索赔 B 和 C,Jaccard 指数为 1/ 2 即 0.5,SMC 为 19/20 即 0.95。

我们看到,所有三对的 SMC 分数彼此接近,Jaccard 指数显示出显著差异。这是 SMC 的问题,当类不携带相同的信息时。例如,在我们的例子中,R 类比 G 携带更多的信息,但是 SMC 认为它们是相等的。

Jaccard 指数也称为 IOU(交集/并集)度量,用于对图像进行语义分割。

相似性指数通过交叉点图像中高亮像素的数量除以联合图像中高亮像素的数量来计算。

Jaccard 指数可以被认为是 SMC 的一个推广情况。在我们有多个对称类(多个类具有相同的权重)的情况下,我们不能使用 SMC,因为它只适用于二进制对称类。在这种情况下,我们可以为每个类创建虚拟变量,这将使单个虚拟变量不对称,因为每个虚拟变量中存在一个类将提供比没有该类更多的信息。然后,我们可以使用 Jaccard 索引来找出相似性得分。基本上,我们将多个对称类转换为二进制非对称哑变量,然后计算 Jaccard 指数。

到目前为止,我们只是在讨论具有二元属性的向量,如果属性是连续的/数字的呢?这是我们转向基于距离和角度的相似性得分的情况。

欧几里德距离:

欧几里德距离更像闵可夫斯基距离和马哈拉诺比斯距离一样是不同的度量。我之所以包括这一点,是因为它构成了对即将到来的指标进行讨论的基础。

我们知道,空间中距离较近的点之间的距离要小于距离较远的点之间的距离。因此,较小的距离意味着更大的相似性,这就是使用欧几里德距离作为相似性度量背后的思想。向量 p 和 q 之间的欧几里德距离计算如下:

考虑三个用户 A、B 和 C。他们为几部电影提供了评级,每个评级的范围从 1 到 5,0 表示用户没有看过这部电影。

用户 A = (0.5,1,1.5,2,2.5,3,3.5)

用户 B = (0.5,1.5,2.5,0,0,0,0)

用户 C = (0.5,1,1.5,2,0,0,0)

使用上面的公式,我们得到 A & B 之间的距离为 5.72,B & C 之间的距离为 2.29,A & C 之间的距离为 3.5。如果你仔细看,A & C 向量给了前四部电影相同的评级,这告诉我们两个对电影有相似的喜欢,但因为 C 没有看过几部电影,所以我们在他们之间有很大的距离。

由于上述向量有七个维度,我们无法在此将其可视化。相反,让我们看看两个轴上的相似向量,每个轴代表一部电影。在图中,红色向量代表用户 A,绿色向量代表用户 B,蓝色向量代表用户 c。所有向量在原点都有尾部。

根据上面的图,我们应该期望蓝色和红色向量显示出高度的相似性,因为它们是共线的。但是当我们计算欧几里得距离时,我们得到了它们之间的显著距离。如果我们不使用向量之间的距离,而是计算它们之间角度的余弦值,会怎么样?向量可以有更小或更大的长度,它们之间的角度将保持不变。这将我们带到新的相似性度量。

余弦相似度:

在我们的学术中,我们遇到过两个向量的点积和叉积。两个向量的点积被计算为每个向量的幅度与向量之间角度的余弦的乘积,即

其中,| A |和|B|代表矢量 A 和 B 的长度,它是 A 和 B 到原点的距离。

A.通过对向量 A & B 的逐元素乘法求和来获得 b,即

余弦相似度计算如下:

由于评级为正,我们的向量将始终位于第一象限。因此,我们将在范围[0,1]内得到余弦相似性,1 是高度相似的。

我们想到使用余弦相似度,因为我们知道向量之间的角度保持不变,不管它们的长度如何,但是我们能进一步改进它吗?你发现什么问题了吗?让我们看看!

居中或调整余弦相似度:

居中?那是什么?到目前为止,我们一直在努力寻找小苹果和大苹果之间的相似之处。击球手出局了吗我们知道,有些人在评分时总是很严格,而有些人则很慷慨(我就属于这一类😃).如果我们试图寻找它们之间的相似性,我们总是会因为这种行为而产生一些偏差。

这可以通过从一个用户的所有电影评级中移除该用户给出的平均评级来处理,通过围绕平均值排列评级,这只是标准化评级。一旦所有的向量被标准化,我们计算余弦相似性。这不过是居中或调整后的余弦相似度!这也就是俗称的皮尔逊相关!

为了证明上述观点,我创建了两个数组,其中第二个数组是通过在第一个数组中添加偏移量来获得的,同时保持第一个数组的所有变量相同。检查笔记本下方的实施情况。

我们得到相关性为 1,余弦相似性为 0.85,这证明了相关性比余弦相似性表现得更好。这是因为矢量的归一化。

也有一些其他可用的相似性度量,但是我们到目前为止讨论的度量是我们在处理数据科学问题时经常遇到的度量。下面是一些参考链接,您可以从中了解更多关于这些指标及其使用案例的信息。

  1. 向量空间模型的余弦相似度
  2. 相似度和相异度的比较研究
  3. 评估图像分割模型
  4. 用户-物品相似度研究门户
  5. 皮尔逊相关&索尔顿余弦度量

谢谢你一直读到最后。我希望你喜欢它。

学习愉快,再见!

从零开始到部署的图像分类

原文:https://towardsdatascience.com/fantastic-and-straightforward-image-classification-with-fastai-library-for-pytorch-30c3380ac284?source=collection_archive---------17-----------------------

我们回顾了分类图像和清理训练数据集的技术。我们使用 Google 创建了一个图像数据集,将该模型应用于一个真实案例,并添加了一个使用 Starlette 和 Uvicorn 部署该模型的指南

Photo by Harshal Desai on Unsplash

背景

这些天,由于某些情况,我发现自己在做一个用 fast.ai 设计的免费 Pytorch 课程,这是本文完全基于的材料(第 2 课),它让我着迷,因为这些库简单、有用、可靠。

在这篇文章中,我们将发现一些用于 Pytorch 的 fast.ai 工具,在完成阅读后我们将知道:

  • 如何用谷歌的图像搜索引擎创建数据集
  • 如何开发和调整神经网络来分类图像
  • 如何使用 ImageCleaner 清理训练数据集并加以改进?
  • 一个真实案例的例子,并应用了私有数据集
  • 关于如何部署模型的说明性指南

从 Google 搜索引擎创建数据集

我在 fast.ai 在线课程中看到的第一件事并不明确与 Pytorch 或 fast.ai 相关,而是与直接从谷歌图像搜索引擎创建数据集的方法相关。

这项技术受到了弗朗西斯科·英厄姆和杰瑞米·霍华德/阿德里安·罗斯布鲁克的工作的启发。

为每个类别创建 URL 列表

我们将基于鸟类、野猫和飞机的照片创建一个包含三类图像的数据集。

我们必须分别组合每一个类别,为了获得飞机的图像,我们在浏览器中搜索,例如:“飞机摄影”。

为了细化搜索,我们可以使用所有的搜索引擎工具,例如排除图像名称中不需要的元素(例如使用-textToExclude:-camera-airport)或使用标签按钮仅过滤特定类别。

对于数据集的公共使用,您必须过滤“工具-使用权”,并根据适用情况尊重版权。

一旦我们在浏览器中有了想要的图像,我们将在控制台上执行两个 Javascript 语句。

“在 Windows/Linux 中按 Ctrl-Shift-J,在 Mac 中按 Cmd-Opt-J,会出现一个小窗口 javascript 控制台’。你需要得到每张图片的网址。在运行以下命令之前,您可能需要禁用广告拦截扩展(uBlock、AdBlockPlus 等)。)在 Chrome 中。否则,window.open()命令不起作用。然后,您可以运行以下命令

urls **=** Array.from(document.querySelectorAll('.rg_di .rg_meta')).map(el**=>**JSON.parse(el.textContent).ou);window.open('data:text/csv;charset=utf-8,' **+** escape(urls.join('\n')));

这段代码将开始下载一个文件,我们必须将其重命名为 urls_[ClassName].csv,我们使用前缀“urls_”和类名作为文件名的约定;这对代码的运行非常重要,我们将在后面看到,

我们重复在浏览器中过滤图像的操作,并将它们的 URL 下载到一个文件中,该文件被重命名为 CSV,用于其他三个类别,直到您获得:

如果我们看一下这些文件的内部,我们会发现它们都是简单的文件,包含过滤图像的 URL 列表

下载并验证每个路径中的图像

一旦我们在 CSV 文件中有了每个类别的 URL 列表,我们将运行代码来下载图像并构建我们的数据集。在我的例子中,我创建了一个“数据/照片”路径,这是我的基本路径。在那里,我放置了三个类别的 CSV 文件,然后我们执行代码来创建子目录,图像将被下载到这些子目录中

from fastai.vision import *
classes = ['birds','flowers','aircrafts']
path = Path('data/photos')
for folder in classes:
    dest = path/folder
    dest.mkdir(parents=True, exist_ok=True)path.ls()

来真正下载我们使用的图像

for cla in classes:
   print (cla)
   file = 'urls_'+cla+'.csv'
   dest = path/cla
   download_images(path/file, dest, max_pics=200)

然后,我们执行代码来验证所有下载的图像都是正确的,并删除可能损坏的图像(delete=True)

for c in classes:
   print(c)
   verify_images(path/c, delete=True, max_size=500)

要查看每个语句的文档,可以运行 doc()命令,例如:

doc(verify_images)

创建和可视化数据集

在每个类别对应的路径中下载并验证图像后,我们可以创建一个 fast.ai 数据束(类似的数据帧),将标记的图像放入其中,并开始可视化或使用它们

np.random.seed(7)data = ImageDataBunch.from_folder(path, train=".", valid_pct=0.2, ds_tfms=get_transforms(), size=224, num_workers=4).normalize(imagenet_stats)

并运行代码以在数据集内随机显示一批 3 行

data.show_batch(rows=3, figsize=(7,8))

模型第一版

在本练习中,我们将使用 resnet34 格式的卷积神经网络。在模型中,有一组预定义的网络架构,涉及不同的结构和 complexities⁴.

原则上,resnet34 配置对于建议的范围来说足够有效和简单

learn = cnn_learner(data, models.resnet34, metrics=error_rate)
learn.fit_one_cycle(4)

第一次训练后,我们看到网络完全没有出错,达到了 95%的准确率(error_rate = 0.045455),训练时间非常完美。

为了能够进一步调整这种精度,我们有一种方法来绘制学习率参数的行为:

learn.lr_find()

我们看到该方法如何搜索不同学习率值的神经网络的 20 次损失。然后,我们可以绘制图表,寻找最适合的范围,并以该设置重新输入:

learn.recorder.plot()

左图显示了神经网络中 learning_rate 的结果,然后我们必须寻找从持续下降开始到下降结束和上升开始之间最重要的学习范围。

这个环境,在右边的图中用底部的红条标出,让我们在 1e-⁶和 1e-⁵之间的中间有了一个起点,所以我们可以粗略地说,起点是近(1e-⁶/2);最终射程显然在 1e-

learn.fit_one_cycle(2, max_lr=slice(**1e-6/2**,**1e-3**))

我们看到模型的精确度提高了,达到 96.5%,误差率= 0.034091

让我们保存这个版本的模型,这样我们就可以回顾它

learn.save('stage-2')

清理训练数据集

混淆矩阵

虽然,在这种情况下,获得的精确度可能是足够的,但是除了经典的“混淆矩阵” ***** 之外,我们将看到两个令人兴奋的工具来尝试理解环境混淆了训练。

learn.load('stage-2');
interp = ClassificationInterpretation.from_learner(learn)
interp.plot_confusion_matrix()

现在让我们使用 ImageCleaner,fastai.widgets 的一个小部件,它将帮助我们删除或重新标记模型中不正确的图像。

顶部损失的图像清洁器

对于第一个清理训练数据集工具,我们需要找到使用。from_toplosses 方法定位相应的索引,并将它们传递给 ImageCleaner。

该小部件不会删除相应路径的图像,但会生成一个新的 CSV 文件(cleaned.csv ),其中包含“不用于删除”的图片列表以及正确的类别,以防其被重新分配

from fastai.widgets import *db = (ImageList.from_folder(path)
.split_none()
.label_from_folder()
.transform(get_transforms(), size=224)
.databunch())learn_cln = cnn_learner(db, models.resnet34, metrics=error_rate)
learn_cln.load('stage-2');

我们创建一个新的 learn_cln 模型,从文件夹(from_folder)中读取数据,并获取前一阶段中保存的阶段 2 的权重。

然后分离数据集和从主要训练损失中提取的指数。对于这些对象,我们调用 ImageCleaner,它将在屏幕上显示混淆的元素(from_toplosses ),以允许我们消除它们或将它们重新分配到正确的类

ds, idxs = DatasetFormatter().from_toplosses(learn_cln)
ImageCleaner(ds, idxs, path)

该工具向我们批量展示了一组令人困惑的图像,并允许我们在每个图像下方的分类框中删除或重新分类它们。通过单击下一批,cleaned.csv 文件将重新标记或从列表中删除它们。我们必须重复这个过程,直到小部件中不再有图像。

重复的图像清除器

调试我们的训练图像的第二个工具是向我们呈现“可能”相同的图像,只留下其中一个

ds, idxs = DatasetFormatter().from_similars(learn_cln)
ImageCleaner(ds, idxs, path, duplicates=True)

在这种情况下,使用的原始数据集足够干净,不会因重复图像而产生混淆。然而,在下面的例子中,我们将看到它是如何应用于寻找图像之间的相似性的。

图像清洗后的模型

这里我们有每个目录的原始图像,并清理。CSV 文件包含通过重新标记将保留和移动的图像列表

这里,我们必须小心处理代码,因为有必要重新生成 DataBunch(包含图像及其标签的数据帧)。尽管如此,在这种情况下,我们将指明原点是“from_csv”(在前面的示例中,它是“from_folder”)。

db = (ImageList.**from_csv**(path, 'cleaned.csv', folder='.')
.no_split()
.label_from_df()
.transform(get_transforms(), size=224)
.databunch())learn_cln = cnn_learner(db, models.resnet34, metrics=error_rate)
learn_cln.fit_one_cycle(2, max_lr=slice(1e-6/2,1e-3))

通过重新训练模型,您可以达到 100%的准确性,而不会发现令人困惑的值,也不会出现训练模型的 error_rate。太好了!

一个真实的使用案例

到目前为止,我们已经在我们从谷歌创建的数据集上使用“resnet34”建立了一个图像分类模型。

出于文章的目的,条件足以说明机器学习、可视化和清理数据集工具的威力。

最近出现了一个真实的案例,包括拍摄在 ERP 软件上报告需求的用户的票据所附的图像。我们的目标是将这些图像分为以下几类:“报告”、“错误”、“屏幕”、“罚单”

图像托管在服务器上,在“attach”路径下,在子目录中,用报告错误、消息或需求的用户的 ID 进行编号。

我们使用下面的 Python 代码将图像统一到一个目录中,从那里,客户端将它们“标记”在对应于每个类别的文件夹中。

import os
import sys
from shutil import copyfiledir_ori = 'attach'
dest = 'attach/images'
os.chdir(dir_ori)for x in os.listdir('.'):
   if os.path.isdir(x):
       newdir = os.path.join(dir_ori,x)
       os.chdir(newdir)
       for w in os.listdir(newdir):
           if os.path.isfile(w):
               file, ext = os.path.splitext(w)
               if ext.lower() == '.jpg':
                  if sys.platform == "win32":
                     os.popen('copy '+w+' '+dest)
                  else
                     os.popen('cp '+w+' '+dest)
       os.chdir(dir_ori)

一旦文件被统一,就安装一个应用程序,以便客户可以在它们相应的文件夹中标记它们,并且应用相同的训练和调色过程,获得以下结果:

我们的初始准确率(在 ImageCleaner 之前)刚刚超过 85%。

这里我们可以看到一个带有相似搜索的 ImageCleaner 的清晰示例,这在之前的模型中是看不到的。

在训练数据集清理和重新标记混淆案例后,我们从 cleaned.csv 中重新获取 dataBuch,当重新训练模型时,以 clean base 作为数据源,我们再次获得 100%的准确率。

太好了,太好了!

部署模型

根据 fast.ai 课程第 2 课的内容,为了不浪费出现在那里的优秀内容,我们将传播它,因为在许多情况下,远离编程的人很难想象如何部署一个模型。

工作环境中的预测

首先,让我们看看 Jupyter 笔记本中的模型如何根据我们的工作环境进行预测

learn.export()

该命令在工作目录中创建一个名为“export.pkl”的文件,该文件包含部署模型所需的所有内容(权重、类的元数据以及所使用的转换或规范化)。

defaults.device = torch.device('cpu')
img = open_image(path/'birds'/'00000022.jpg')
img

现在,我们必须看看 00000022.jpg,我们测试模型:

learn = load_learner(path)
pred_class,pred_idx,outputs = learn.predict(img)
pred_class

结果,当然,是鸟类这个类别!

Starlette⁵和 Uvicorn⁶服役期间的预测

现在,让我们把模型投入生产。为此,我们认为他的框架在 Python 3.6+上是有限制的,为了安装它,我们从 Python 环境中运行

$ pip3 install starlette or $ pip install starlette

我们还需要建立一个服务器;在这种情况下,我们将使用我们安装的 uvicon[VII]

$ pip3 install uvicorn o $ pip install uvicorn

让我们尝试一个简单的 Python 代码,看看能否创建一个 API

from starlette.applications import Starlette
from starlette.responses import JSONResponse
import uvicornapp = Starlette(debug=True)
@app.route('/')async def homepage(request):
    return JSONResponse({'MySAMPLE': 'It works!'})if __name__ == '__main__':
    uvicorn.run(app, host='0.0.0.0', port=8000)

当您运行它时,将构建一个 Uvicorn 服务器,让您从浏览器访问 http://localhost:8000/

最后,我们可以应用接收一些图像的 URL 作为参数的预测,例如我们可以用

http://localhost:8000/classify-URL?url=imagen.jpg

这里,来自西蒙·willison⁷的代码作为完整部署的参考(记住我们需要在部署环境中获取 model.pkl 文件)

from fastai.vision import@app.route("/classify-url", methods**=**["GET"])**async** **def** classify_url(request):
   bytes **=** **await** get_bytes(request.query_params["url"])
   img **=** open_image(BytesIO(bytes))
   _,_,losses **=** learner.predict(img)
   **return** JSONResponse({
"predictions": sorted(zip(cat_learner.data.classes, map(float, losses)),key**=lambda** p: p[1],reverse**=True**)
})

注意:此代码显示为指南,直接取自上述课程。逐步解释或验证它不是本文的范围。一般来说,代码从请求中接收图像的 URL 作为参数,打开它,进行预测,然后返回一个 JSON,其中包含每个类别的概率。你可以参考西蒙·威尔逊的完整代码, 感谢 他。

摘要

我们已经看到了如何从 Google 搜索引擎创建我们自己的一组图像,或者如何操作来自服务器的图像,以将它们统一到我们的客户端可以标记它们。

我们已经开发了一个 resnet34 神经网络,获得了非常好的初始结果,在调整学习速率后更好,在清除可能产生混淆或重复的训练图像后获得了优异的最终结果。

最后,我们看到了关于如何部署模型来预测新病例的肤浅指南。

所有这些都是通过 fast.ai for Pytorch 实现的,只需几行简短的代码。

一旦我们记住这篇文章是受课程 https://course.fast.ai 第 2 课的启发,我们已经对其进行了测试,并且从中我们获得了我们真实案例的基础。

我希望这篇文章对你有用,如果有可以做得更好的地方,我将非常感谢你的评论和报告。

参考

[1]在 fast.ai 课程中,这项技术的灵感来自:弗朗西斯科·英厄姆和杰瑞米·霍华德/ [ 阿德里安·罗斯布鲁克 ]

[2] https://course.fast.ai,第二课,Jupyter 笔记本:第二课-下载

[3]https://towards data science . com/an-overview-of-resnet-and-its-variants-5281 e2f 56035

[4]https://medium . com/@ 14 Prakash/understanding-and-implementing-architectures-of-resnet-and-resnext-for-state-the-art-image-cf 51669 e 1624

[*]最近,我发现有两个以上类的混淆矩阵看起来奇怪而错误,显然是 matplotlib 版本的一个主题,所以我决定无论如何都要包括它。

[5]uvicon是一个快如闪电的 ASGI 服务器,建立在 uvloophttptools 之上。

[6] Starlette 是一个轻量级的 ASGI 框架/工具包,是构建高性能 asyncio 服务的理想选择

[7]西蒙·威廉森制作的结构这个代码

神奇动物和在哪里可以找到它们(在推特上)

原文:https://towardsdatascience.com/fantastic-beasts-and-where-to-find-them-on-twitter-18059f2e25b3?source=collection_archive---------12-----------------------

Newt Scamander

从 1997 年第一本书发行到 2018 年最新的《神奇动物》电影,巫师世界一直是一个长期持续的趋势,吸引了许多千禧一代。这是过去二十年中最具影响力的现象之一,并吸引了大批追随者。然而,自从第二部《神奇动物》电影上映后,人们对这部电影的抵触情绪似乎比以往任何时候都多。这甚至导致粉丝群体中的一些人形成了反粉丝心态(加纳,2018 年),这意味着粉丝会因为不喜欢某个特定部分而与该部分脱离关系。

《神奇动物:格林德沃的罪行》并不是大规模系列电影中第一部受到强烈反对的电影,两年前《星球大战:最后的绝地武士》等其他著名电影也发生过这种情况。这被许多粉丝视为失败,这部电影在网上受到了来自评论家和粉丝的大量仇恨(福布斯,2018 & 快报,2018 ),然而当莫顿贝(2018) 在推特上进行的一项研究显示,只有 21.9%的评论是负面的。因此,问题来了,这是否会是《神奇动物》的相同场景,因为大多数人都与魔法世界系列有着密切的关系,我们想给我们的读者一个影迷对新电影的看法的总体概述(带有一些魔法视觉)。这篇文章是写给对世界上最大的球队和球迷之一感兴趣的普通读者的,也是写给球迷自己的。

这些年来,魔法世界的曝光率一直很高,可以说是一种持续的趋势。通过展示谷歌趋势,可以洞察到新系列与哈利波特系列相比有多受欢迎。尽管《神奇动物》也不差,但不用多久我们就会发现《哈利·波特》仍然比《神奇动物》更受欢迎。

Google Trends

值得一提的是,哈利波特自 1997 年以来一直在确立自己的地位,而《神奇动物》只是在 2016 年才上映,然而第一部电影上映时,第一部哈利波特电影就获得了大量的粉丝和金钱。所以,当宣布粉丝们将得到一部前传时,许多粉丝都很兴奋,因为这被视为他们对魔法世界的“延伸”,这也在票房报告中看到。

BOX Office Results Bar Chart

BOX Office Results Pie Chart

《神奇动物在哪里》比《阿兹卡班的囚徒》更受欢迎,这表明它肯定不是系列电影中收入最低的。然而,随着第二部电影在影院上映两个月,它的名声越来越差,收入也明显减少。这些新电影成为了评论家们的热门话题,比如《每日电讯报》(2016 年)、《T2》(2016 年)、《Buzzfeed》(2016 年)和《T4》(2016 年)、《福布斯》(2016 年),《福布斯》(2016 年),自从约翰尼·德普在 2016 年电影的结尾以格林德沃的身份意外亮相以来。关于约翰尼·德普的更多见解将在本文后面给出。

Tweet 1: Popular Negative Opinions

由于这些粉丝对《神奇动物》系列电影有着非常不同的看法,所以研究人员对这些粉丝喜欢或不喜欢这部电影的原因进行了描述。为了这项研究,我们求助于人人都爱过度分享的平台:Twitter。从第一批预告发布到电影停止在影院上映,每部电影有 250 条推文被分析。因此,导致《神奇动物在哪里》从 2016 年 6 月 1 日到 2017 年 4 月 30 日,以及《神奇动物:格林德沃的罪行》从 2018 年 11 月 1 日到 2019 年 1 月 7 日。

通过滚动这些推文,已经可以注意到负面评论围绕着电影中的演员和清晰的情节漏洞等主题,但是稍后我们将详细阐述这些主题,看看人们不喜欢第二部电影是否有特定的原因。

为了理清混乱(由 500 条推文组成),这些帖子被多次分成不同的类别,以简要概述 Twitter 上存在的积极和消极情绪。第一个主要类别由负面、正面或营销相关推文的数量决定,这导致了以下发现:

Fantastic Beasts 1 and Fantastic Beasts 2 Divide of Comments

在 500 条推文中,超过一半是营销相关的,准确地说是 251 条,这表明用于电影的标签高度商业化。其余的推文似乎对两部电影都非常积极,对第一部电影的负面评论很少(105 条评论中只有 2 条),对《格林德沃的罪行》电影的总评论中约有三分之一是负面的(144 条中有 35 条)。
然而,这些主要类别仍然没有给我们更多的信息为什么《格林德沃的罪行》被描述为“玷污了哈利波特遗产的电影”(2018 年福布斯)

所以,在看了一下非营销相关的推文后,可以看出 Twitter 上的粉丝似乎对电影的角色以及演员有很大的兴趣(无论是积极还是消极),并通过他们的语言,粉丝艺术和粉丝小说来表现这一点。一些剩余的推文还包括用户提到他们计划去看电影或只是看了电影,没有表达实际意见。这些推文被标记为电影体验,在做更多研究时没有被考虑在内。

Histogram Discussed Topics

Bar Chart Characters FB 1 & 2

我们将从推特上粉丝们最喜欢的角色开始。如上面的视频所示,粉丝们为《神奇动物在哪里》中的纽特·斯卡曼神魂颠倒。虽然,这并不令人惊讶,因为他是电影中的主要主角,他是第一个更有爱心和温柔的男性角色,加上他是赫奇帕奇的事实(而且,每个人都喜欢赫奇帕奇)。Twitter 上其他受欢迎的角色包括邓布利多(或者更确切地说是电影中没有他)和克雷登斯。所有这些男性角色也收到了相当多的粉丝艺术。

Tweet 2: Fan showing love towards Newt Scamander

在《格林德沃的罪行》中,聚光灯从纽特身上移开,影迷的新欢似乎已经转向电影的对手;格林德沃。这既是因为他成为了第二部电影的主角,也是格林德尔多夫妇中的一员,这也解释了为什么年轻的邓布利多(#Dumbledamn)经常在评论中被提及。尽管粉丝们仍然明显对两人之间缺乏浪漫感到失望(《卫报》,2018 )。

Tweet 3: Complaints about lack of Grindeldore

Tweet 4 : Support for Grindeldore

Pie Chart Characters FB 1 & 2

Twitter 上另一个激烈辩论的话题是电影的演员,尤其是因为围绕他们有很多问题。当看《神奇动物》系列第一部电影的推文时,只有两个演员和一个女演员被真正谈论过;雷德梅恩(纽特),德普(格林德沃)和苏多尔(奎尼)。在这些推文中值得注意的是,所有用户对电影明星都有积极的反应。

Actors mentioned in Tweets FB 1

Tweet 5: Fan showing love towards actors

然而,在第二部电影中,提到了更多的演员。这一次,扮演邓布利多的裘德洛和扮演克雷登斯的埃兹拉·米勒得到了他们应得的赞赏。

Actors mentioned in Tweets FB 2

除了约翰尼·德普,所有的演员都因为他们在电影中的表现而得到了赞扬。在提到他的 28 个帖子中,有 5 个是负面的。这些帖子都提到了他虐待前妻的指控,尽管其余 23 个正面帖子都为他辩护,或者只是说他们不关心他的个人生活,但确实喜欢他的表演。

Tweet 6 about the Johnny Depp situation

为了真正找出电影中令人喜爱或讨厌的地方,还根据对情节的评论对推文进行了分析。对于《神奇动物在哪里》,我们发现所有的评论都是正面的。然而,许多评论并没有提到他们为什么喜欢这部电影,只有 5 条评论具体说明了他们喜欢实际情节的什么,这些都与故事情节有着更黑暗的基调和只是“好”或者时间设置和使用的惊人视觉效果有关。

Infographic Plot FB 1

Tweet 7: Positive opinion about plot FB 1

对于《神奇动物:格林德沃的罪行》,意见似乎有所不同,仍然有对这部电影的喜爱(66%的评论仍然是积极的),然而粉丝们几乎没有表明他们为什么喜欢它。不喜欢这个情节的人(幸运的是)提到了原因,大多数评论都提到了故事情节不完整或不正确,这使得电影很难理解。这些结果已经表明了粉丝心态的分歧,以及基于他们是否喜欢《神奇动物》系列的问题,似乎出现了不同的亚文化。

Infographic Plot FB 2

Tweet 8: Negative opinion about plot FB 2

粉丝在 Twitter 上表达自己的另一种方式是发布粉丝艺术、粉丝理论和粉丝小说。《神奇动物在哪里》的 105 篇帖子中有 21 篇是粉丝创作的。帖子主要围绕格林德尔多夫妇(格林德尔沃德和邓布利多),纽特本人和纽特娜(纽特和蒂娜),在所有的粉丝艺术中,值得注意的是这对夫妇受到了全世界粉丝的喜爱,因为有些帖子甚至用泰语、俄语和日语发布。

Tweet 9 & 10: Japanese fan-art about Newtina and other Fantastic Beasts Characters

围绕第二部电影的推文包含了更多粉丝的创作,在 144 条帖子中,有 41 条包含了粉丝艺术和粉丝小说。几乎所有这些帖子都围绕着同一对夫妇和纽特·斯卡曼个人。

不同粉丝艺术的创作清楚地展示了粉丝们对特定角色组合的投入,这与魔法世界粉丝们多年来所做的事情非常吻合;如果他们不能获得足够的东西,他们会自己创造更多。

Tweet 11 & 12: Containing fan-art of the Grindeldore couple

Pie Chart Marketing Comments FB 1 & FB 2

在分析了 Twitter 之后,很明显,为电影制作的标签也被大量用于营销目的。既然魔法世界系列已经赚了数十亿美元,商业空间的出现也就不足为奇了。随着 500 条推文中有 251 条与营销相关,很明显越来越多的公司意识到这种神奇的特许经营是他们可以赚钱的东西。此外,还有官方粉丝网站,如 Pottermore 和 MuggleNet,它们主要由粉丝驱动,但仍然被视为中立和商业网站,因为它们为电影做了大量宣传。公司试图对这个市场产生重大影响,他们要么通过在推特上发布关于电影赠品或促销的消息,比如发布更多事实、演员活动、商品或票务网站链接。

Tweet 13: Give-away by popular account OriginalFunko

Tweet 14: Promotional post by the original Fantastic Beasts account

人们对神奇动物系列电影的最大问题之一是约翰尼·德普的选角,他是也是一个有争议的人物,因为他被前妻指控虐待,这受到了各种媒体的广泛批评(综艺,2018 & 卫报,2018 )。在续集中,约翰尼·德普出现得更多,在格林德沃的罪行被公布的时候,对话(或指控)再次被点燃。推特和媒体上对此的看法非常明显,许多粉丝对演员和 J.K .罗琳的选角感到愤怒。

Bar Chart #boycottjohnnydepp & #johnnydeppisinnocent

通过查看 Twitter 上最常用的标签来显示对这位演员的支持(# JohnnyDeppIsInnocent)或仇恨(#BoycottJohnnyDepp),我们看到平台上有一些非常直言不讳的用户。特别是在韩国,这位演员并不受欢迎,有单独的韩国标签被创建来批评他。然而,平台上对约翰尼·德普的支持仍然占大多数,大约三分之二的评论是积极的,许多人仍然表达了他们对他的爱。

Tweet 15: Negative Johnny Depp Opinion

Word-clouds on researched Hashtags

由于推特用户对这样的话题直言不讳,这导致了另一种现象在巫师界发生:在最初的崇拜者圈子里出现了多种崇拜者亚文化。大多数粉丝都直言不讳地表达了他们的观点,这在粉丝群体之间造成了隔阂。一些反粉丝强烈批评那些喜欢约翰尼·德普或神奇动物系列的粉丝。

Tweet 16 & 17: Criticising other fans

Fantastic Beasts vs Harry Potter

通过收集所有的结果,我们可以清楚地看到巫师世界不再是一个大社区,而是多个小社区。所有这些社区对新的神奇动物电影和其中的演员都有非常不同的观点。

著名的《活着的男孩》已经让粉丝着迷了二十多年,而让人们着迷于整个现象的东西(比如房子和令人难以置信的情节)似乎被 J.K .罗琳推到了一边。由于新电影不是基于书籍,也不是围绕哈利·波特、赫敏·格兰杰或罗恩·韦斯莱,它引起了粉丝们的兴趣,因为它带走了哈利·波特的魔力。所以,在新系列播出后,问题来了,罗琳试图在她的魔法世界里放入这么多的系列,是否真的是在摧毁她的魔法。

这可以通过今天的一些研究来回答;是的,她可能正在摧毁她的世界的某些部分,但粉丝已经变得如此之大,无论她发布什么,都会有很大一部分人支持她。
目前的情况与上一部绝地电影有关,在网上看起来这部电影受到了大量的反对,但当它被放在一个角度看时,实际上网上的仇恨相对较少。在粉丝发布的 249 条推文中,有 14.8%是关于神奇动物系列的负面推文。虽然这并不能掩盖《最后的绝地》和《神奇动物》都达到了让粉丝非常困惑和失望的目的。

我们完全建议你做的是自己决定你对新系列的看法,特别是因为有这么多不同的意见,而这只是总共 5 部电影的续集。因此,等到下一部电影来寻找答案也不是一个坏选择。众所周知,J.K .罗琳是创造宏伟故事情节的大师,所以现在,我们所能做的就是对她(以及她背后的整个营销团队)多一点信心,并希望她能利用她的魔法将神奇动物变成粉丝应得的特许经营权。

毫无疑问,即将上映的《神奇动物》系列电影将重新点燃新一代人或老一辈人对魔法的热爱。也许这不会是自 1997 年以来哈利波特教给我们的对魔法的热爱,但纽特·斯卡曼肯定有更多神奇的动物让我们去爱。

由 Imme van Oudheusden 和 Kim Jansen 创作。

APA 参考:

m .贝(2018)。《仇恨者的武器化:最后的绝地和通过社交媒体操纵的流行文化的战略政治化》。洛杉矶:南加州大学安娜堡传播与新闻学院。

Bennett,A. (2016 年 11 月 2 日)。哈利·波特迷对约翰尼·德普在《神奇动物》中的角色感到愤怒。检索自 BuzzFeed:https://www . BuzzFeed . com/alannabennett/Johnny-Depp-fantastic-beasts

丘巴,K. (2018 年 10 月 10 日)。艾梅柏·希尔德在#MeToo 周年纪念活动上阅读关于约翰尼·德普虐待指控的信。检索自综艺:https://Variety . com/2018/scene/news/amber-heard-Johnny-Depp-open-letter-me-too-1202975127/

加纳河(2018)。媒体粉丝和粉丝研究指南。《不是我的命根子:自我民族志、情感波动和流行音乐反狂热》(第 6 章)。约翰·威利父子公司。

霍克斯河(2016 年 11 月 9 日)。约翰尼·德普确认出演《神奇动物》中的格林德沃——但并不是每个人都高兴。从电讯报检索:https://www . Telegraph . co . uk/films/2016/11/09/Johnny-Depp-confirmed-as-grindelwald-in-fantastic-beasts-but-n/

Kyriazis,S. (2018 年 7 月 10 日)。《星球大战:最后的绝地反击》和影迷抵制:导演警告后果自负。检索自 Express:https://www . Express . co . uk/entertainment/films/986642/Star-Wars-Last-Jedi-backlash-fan-boycit-Han-Solo-flop-Kathleen-Kennedy-Rian-Johnson

Mendelson,S. (2016 年 11 月 2 日)。为什么约翰尼·德普不帮助《神奇动物在哪里》。检索自福布斯:https://www . Forbes . com/sites/Scott Mendel son/2016/11/02/why-Johnny-Depp-donts-help-fantastic-beasts-and-where-to-find-them/# 19e 400442 e44

Mendelson,S. (2018 年 11 月 14 日)。回顾:《神奇动物 2》是一场系列杀人灾难。检索自福布斯:https://www . Forbes . com/sites/Scott Mendel son/2018/11/14/review-fantastic-beasts-the-crimes-of-grindelwald-franchise-killing-disaster-JK-Rowling-Johnny-Depp/# ad 50 ACF 31803

Mendelson,S. (2018 年 6 月 26 日)。《星球大战 9》的大问题不会是《最后的绝地武士》的反弹或者《单飞》的失败。检索自福布斯:https://www . Forbes . com/sites/Scott Mendel son/2018/06/26/star-wars-9-last-Jedi-网飞-solo-force-awakens-wonder-woman-1984-frozen-2-juman Ji-James-bond-box-office/# 4014 AE 3c 3d 17

普尔弗,A. (2018 年 10 月 4 日)。艾梅柏·希尔德的法律团队谴责约翰尼德普虐待否认。从《卫报》检索:https://www . The Guardian . com/film/2018/oct/04/amber-heards-legal-team-谴责-Johnny-Depp-虐待-否认

纽特·斯卡曼图片检索自:https://t1 . dau mcdn . net/movie/0161 e 246399349 CBA 68d 8 f1 f 9 C5 c 15 c 41540263032405

神奇动物大战哈利波特图片检索自:http://ovicio . com . br/WP-content/uploads/eg2tsrg 5 bvaybfnqc0ql . jpg

纽特·斯卡曼与 Bowtruckle Gif 检索自:https://68 . media . Tumblr . com/715 c 11d 0 CEB 1812 c 878 AFD 81 f 20 c8 e 83/Tumblr _ oh 069 mpdcq 1 vpj 1 fho 1 _ 500 . Gif

恶作剧托管图片检索自:https://just stitchers . in/WP-content/uploads/2017/01/恶作剧托管. png

所有推文:

推文 1:由用户 @ JennyENicholson 于 2018 年 7 月 20 日发布
检索自:https://Twitter . com/jennye Nicholson/status/1020406911676502016

推文 2:由用户@prettymessygurl 于 2018 年 12 月 1 日发布
检索自:https://Twitter . com/prettymessygurl/status/1068947380379230209

推文 3:由用户@deppheadgang 于 2018 年 11 月 23 日发布
检索自:https://twitter.com/deppheadgang/status/1065945167058935809

推文 4:由用户@deppheadgang 于 2019 年 2 月 1 日发布
检索自:https://twitter.com/deppheadgang/status/1080454573192548352

Tweet 5:由用户@ magicalGicques 于 2018 年 9 月 12 日发布
检索自:https://Twitter . com/magialgicques/status/1071788822264930305

推文 6:由用户@cindispy00 于 2018 年 3 月 12 日发布
检索自:https://twitter.com/Cindispy00/status/1080966882553942016

推文 7:由用户@wyattyhalpert 于 2018 年 11 月 20 日发布
检索自:https://twitter.com/wyattyhalpert/status/800275977871863808

推文 8:由用户@ryanmeft 于 2018 年 11 月 16 日发布
检索自:https://twitter.com/RyanmEft/status/1063672113293377536

推文 9:由用户@ tapioccat 于 2018 年 11 月 27 日发布
检索自:https://twitter.com/tapioccat/status/1078439397618380800

推文 10:由用户@osatou_tw 于 2018 年 3 月 1 日发布
检索自:https://twitter.com/osatou_tw/status/1081087335159087104

推文 11:由用户@decembristsson 于 2018 年 4 月 1 日发布
检索自:https://Twitter . com/decembristsson/status/1081190992567549952

推文 12:由用户@dichi71 于 2018 年 5 月 1 日发布
检索自:【https://twitter.com/DiChi71/status/1081659007750807552】T4

推文 13:由用户@originalfunko 于 2018 年 12 月 22 日发布
检索自:https://Twitter . com/original funko/status/1063976339114471424

推文 14:由用户@fantasticbeasts 于 2018 年 11 月 30 日发布
检索自:https://Twitter . com/fantastic beasts/status/1068671018628079617

推文 15:由用户@ sukuraai 于 2018 年 9 月 26 日发布
检索自:https://twitter.com/SakuraAi/status/1044586020572778498

推文 16:由用户@mathewkearney 于 2018 年 11 月 16 日发布
检索自:https://Twitter . com/mathewkearney/status/1063553558556155904

推文 17:由用户@kyloporg 于 2018 年 8 月 25 日发布
检索自:https://twitter.com/kyloporg/status/1044616526681329664

常见问题聊天机器人 MVP

原文:https://towardsdatascience.com/faq-chatbot-mvp-871ab7db94cc?source=collection_archive---------11-----------------------

使用 Python、Flask、Docker 和谷歌云服务

https://willtscott.github.io/inquire-boulder-chatbot/

这是一个快速的过程和工具的纲要,我研究并使用它来为博尔德市建立一个聊天机器人,CO 的常见问题页面。将来我会写一些关于技术方面的更详细的帖子。

github:【https://github.com/willtscott/inquire-boulder-chatbot

结果:现在,这个例子是一个最小可行的产品。我之所以分享这个过程,是因为它可以很容易地转移到其他 FAQ 网站,并且可以通过其他功能进行扩展。我遵循的路线也受到我希望获得标准行业实践经验的影响,因此是简单和复杂选项的平衡。

数据采集

将 FAQ 页面转换成数据集在理论上非常简单,第一步是将网页转换成逗号分隔值的文件。根据你的出发点,这可能是琐碎的,也可能是乏味的。

我开始使用科罗拉多州博尔德市的内容,所以当我看到他们的查询博尔德 FAQ 页面的第一反应是震惊!页面是动态生成的,这使得下载或抓取变得更加困难。因此,我最初的选择是点击 250 多个主题中的每一个,并手动将它们的信息复制粘贴到一个文件中。 Yowch!(值得注意的是,虽然这可能需要几个小时令人麻木的点击,但弄清楚如何抓取动态页面可能需要更长的时间。)

幸运的是,我有了一个更好的主意,这个主意给我带来了回报,也让我少了很多烦闷。在博尔德开放数据页面上,我找到了 FAQ 相关数据集维护负责人的联系信息。仅仅发了几封邮件之后,她就从网站管理员那里给我发来了一个文件,里面包含了每个 FAQ 页面的内容,按类别、部门和主题进行了分类。

在项目的早期阶段,这种资产是纯金的,并继续得到回报,因为我的联系人提供了进一步的帮助,建议类似的数据集、相关的项目,以及向博尔德数据团队展示我的 ChatBot 并在他们的数据展示页面上托管它的可能性。所以非常感谢,尼科利亚!

经验教训:在你花大量时间去完成一项琐碎的任务之前,花几分钟四处看看,看看你是否能直接找到问题的根源。许多市政府发布公共数据集,必须有人负责处理所有这些数据。礼貌地提问,你永远不知道你会得到什么样的数据。此外,社会关系可能会更有回报。

数据清理

得到这个文件是一个巨大的推动,但它还没有结束。在将文件从 Excel 转换为 CSV 格式后,我仍然需要清理数据并使其达到最终状态:一列问题和一列相应的答案。

我将每个条目转换成 unicode 文本,移除工件,并利用 spaCy 库的特性来分隔条目中的每个句子。虽然这不是一个完美的修复,但它让我在处理干净数据方面有了一个巨大的开端,足以让我能够继续尝试 NLP 和 ML 技术。

后来,我重新开始清理这些条目,并手工编辑了一些由地址、不标准的语法、奇怪的标点和样式造成的错误。

对于pie ce de resistance我编写了一个复杂的正则表达式,从一组文本中分离出问答对,然后将它们放入两个独立的列中。

跳到最后:根据你自己的需要,你可以在这里停下来,把你整洁的 q-n-a 电子表格带到聊天机器人平台,如微软 QnA Maker谷歌 Dialogflow ,它会把你的数据插入一个预建的界面,包括聊天回复和开箱即用的机器学习。在默认设置的这两个选项中,QnA Maker 给我的结果稍好一些。价格会有所不同,所以在你承诺之前做好你的功课。在这篇博客的最后,我从我的经历中提供了一些建议。

自然语言处理

经过预处理和手工清理后,我的 NLP 管道是删除标点符号和停用词,将所有内容都小写,然后对剩余的词进行词条整理。我发现变元化比单纯的词干化要有效得多。将单词简化为最基本的形式有助于更广泛的相似性匹配,这有助于聊天机器人在响应查询时的准确率。NLTK 库有一些简单的选项可以提供帮助。

下一步是将文档转换成 ML 模型能够理解的数字向量。首先,我使用单词包,这是一个大型的稀疏矩阵,用二进制值来表示语料库中的每个单词是否存在于文档中。从这里开始,BOW 向量被转换成术语频率——逆文档频率向量,它基于每个单词在文档中的频率及其在整个文档集合中的频率来表示每个单词对语料库的重要性。

因此,对于我的数据集,单词“Boulder”具有相对较低的 TF-IDF 值,这是因为它在整个文档中非常普遍。对于一个关于不同城市的常见问题,你会认为“博尔德”更有分量。(无双关之意?)

行业工具:单词袋和 TF-IDF 矢量化是任何 NLP 项目的标准,为更复杂的操作提供了坚实的基础。下面的大多数匹配策略涉及对 TF-IDF 向量应用各种函数。

相似性匹配

为了将用户问题与数据集中的答案进行匹配,我尝试了几种不同的方法:

  • 语义相似度 通过预先训练的模型被包括在 NLP 库空间中,该模型认为如果单词在相似的上下文中使用,则它们是相似的。根据我的经验,“中型”空间模型在分配相似性方面几乎和“大型”模型一样好,而且加载速度明显更快。该库的实现既快速又简单(甚至 spaCy 中包括了矢量化),但是直接的结果并不特别令人印象深刻,并且大型模型可能会在部署时损害您的钱包。
  • 余弦相似度 是两个向量之间夹角的余弦度量。这里的应用程序是计算用户查询向量和数据集中每个向量之间的夹角余弦。最接近 1 的余弦是用户查询的最佳匹配。我从 numpyscikit-learn 的现有部分构建了这个比较,它足够简单,并且不需要庞大的模型。我发现结果相当不错,并最终在我的 MVP 机器人中使用了这种精益方法。
  • KD 树 是 scikit-learn 中表示的一种数据结构,对存储 K 近邻关系很有用。这种静态存储使您不必在每次比较向量时重新计算距离。scikit-learn 版本中的距离度量不支持余弦相似性,余弦相似性将是该策略的理想扩展,但一些包含的度量(欧几里德、l2、闵可夫斯基和 p)在我的测试案例中工作得一样好。
  • Gensim的 Doc2Vec 是 Word2Vec 方法的扩展,它使用神经网络来学习整个文档的嵌入,而不仅仅是单个单词。我发现这种方法的设置比以前的方法要复杂得多,并且结果不如基本余弦相似度和 KDTree。然而,如果你对神经网络很熟悉,并且愿意钻研细节,那么这是一个很有前途的角度。需要注意的是:大多数 FAQ 不会包含足够的文本来获得强大的训练,并且没有太多对预训练 Doc2Vec 模型的支持,但是这里有一个开始

保持简单和愚蠢:相似性匹配有许多不同的方法。同样,遵循阻力最小的途径,寻找一个为您想要使用的策略提供内置功能的库。没有必要重新发明轮子。当你准备好部署你的机器人时,你可以回来重新检查你的选项以提高准确性。

我的建议是将你的管道构建成独立的模块,这样你就可以很容易地交换新的选项,而不用重写整个程序。这种模块化还将帮助您测试和比较结果,以便您知道您正在做出关于选择哪个函数的明智决策。

Web 服务和部署

随着您的管道能够返回相关的响应,下一步是让机器人上线,这样人们就可以实际使用它。

Flask 是一个 Python 微框架,它可以通过在本地 URL 上托管一个基本的 HTML 页面,并将用户查询作为 POST 请求提交给 Flask route 函数,后者返回来自相似性匹配算法的响应,并将其呈现在页面上,从而使聊天机器人的 web 服务变得异常简单。

分享就是关怀:网络服务的真正力量在于把你的机器人放到互联网上,让公众和其他程序都能访问。只要有当地的 Flask 服务,你就已经成功了一半。

在网上提供你的机器人的另一半是让它可以被公众访问。像 [ngrok](http://ngrok - secure introspectable tunnels to localhost https://ngrok.com/) 这样的工具在技术上可以做到这一点,但是规模很小,需要你自己的电脑来托管。更有用的是谷歌、亚马逊、微软等提供的现代云服务。这将为您处理托管(当然是收费的。)

我与谷歌云服务合作托管我的 Flask 应用程序,将它包装在 Docker 容器中,并在负载平衡的 Kubernetes 集群上运行,以允许扩展。([谷歌应用引擎](http://App Engine - Build Scalable Web & Mobile Backends in Any ... https://cloud.google.com/appengine/)是一个替代方案,在某些情况下可能会更容易。)这一步最后给了 bot 一个公共 URL。分享给你的朋友吧!

连接

如果你想建立你自己的网页界面,这是一个好地方。

我在这里采用了一种混合方法,并将我的 q-n-a 数据集上传到 Google Dialogflow,作为知识库意图。然后,我通过使用应用程序的公共 URL 作为一个 webhook ,将知识意图连接到我在 GCP 的 web 服务。

现在,每当用户向 Dialogflow 代理查询知识库中的主题时,代理都会在后台向我的 web 服务应用程序发送 webhook 请求,并接收由我的自定义相似性匹配确定的响应。

我的混合方法的好处是,用户可以与 Dialogflow 接口进行交互(包括闲聊响应),如果我的自定义 bot 服务器出现故障,Google 的知识意图机器学习可以作为后备响应。

自豪的家长:我应该在这里指出——尽管我的相似性算法很基本,但它仍然胜过我测试的大规模技术公司服务中的任何开箱即用的 ML 平台。

关于成本的一些话:

谷歌云产品:300 美元的注册信用旨在帮助你在掌握事物的过程中吸收成本,但仍然需要小心你在服务上投入的重量,这样你就可以避免成本飙升的经典恐怖故事。我使用的服务需要启用计费。在测试 Docker、clusters 和 swarms 时,我在几周内花掉了大约 15 美元。一天自己占了 10 美元左右,跟踪钱的去向可能会很棘手,所以要小心!最后,一个用户基数小的聊天机器人运行起来应该相当便宜。稍后我会提供一个更新,提供更多关于信用持续时间的细节。

更新: 经过几周的持续运营,我有了一些关于成本的初步数据。一开始我的服务大概吃了【3 美元/天 。仅使用 webhooks 使我能够消除 Dialogflow API 库,并缩减到仅 1 个集群,而不是 3 个,成本降至~【2 美元/天】。

Dialogflow: 标准版免费,企业版有按使用付费要求。这里有一张对比图,但即使是标准版配额也应该足以让你入门。这个平台有很多小功能,包括语音支持以及与脸书和 Slack 等其他消息应用的集成。

微软 QnA Maker: 如果你只是上传一个包含两列的 Excel 文件:问题和答案,微软 Azure 平台上的这个 web 服务开箱即用就能很好地工作。对于我的小测试集,ML 响应比 Dialogflow 的内置 ML 更准确,尽管界面不是那么用户友好。我用 200 美元的免费注册信用连续运行了整整一个月(流量很少),但我建议在你设置的时候想办法禁用这项服务,以便进一步扩展你的信用。这是这项服务的定价页面。

下一集…

在我的下一篇文章中,我将进一步深入细节,并提供一个指南,通过 Docker 跟踪将这个简单的 Flask 应用程序部署到 GCP Kubernetes 集群的路径。我计划解决一些其他教程中似乎没有的空白,所以如果你对这个话题感兴趣,请继续关注。如果你有任何问题或者只是想聊聊机器人,请告诉我!

AI 能阻止犯罪吗?使用计算机视觉的逃票检测

原文:https://towardsdatascience.com/fare-evasion-detection-using-computer-vision-9bf0cead6937?source=collection_archive---------19-----------------------

逃票是纽约市交通管理局面临的一个日益严重的问题。去年的报告显示:

  • 据估计,2018 年因逃票造成的收入损失为 2.15 亿美元
  • 地铁 9600 万美元,公交车 1.19 亿美元
  • 预计未收收入将比 2015 年增加 1.1 亿美元

Fare Evasion in New York. (Source)

在 2018 年 12 月举行的 MTA 委员会特别会议上,纽约市交通总裁安迪·拜福德表示,甚至机构高管也可能被部署来阻止逃票者。他说:“我们将让总部的人组成团队,随机去乘坐公共汽车或站在大门前,提供一个物理屏障,以确保你在进入车站或乘坐公共汽车前有票。”。“当然,我们会有(额外的 MTA)团队或警察来支持我们。”

正如这里的所提到的,逃票还有其他的社会含义。“当我是警察局长时,我们开始关注逃票,”布拉顿在 2014 年说。“我们发现了什么?七分之一的人被通缉。每 21 个人中就有一个携带武器,从美工刀到简自豪冲锋枪。因此,如果你愿意的话,纽约奇迹始于 25 年前对地铁逃票行为的执法。”

用于估计逃票的当前数据收集过程远非完美,并且依赖于人工生成的数据和采样技术。

MTA fare evasion data collection (source)

我相信,通过利用计算机视觉的技术进步,利用安全摄像机镜头测量(而不是估计)地铁逃票的成本有效的自动化系统是可能的。大多数十字转门都被安全摄像头覆盖,这些镜头被记录并保存在中央服务器上。有可能设计一个软件管道来消耗来自这些服务器的视频数据,并使用计算机视觉来检测十字转门跳线,并将该信息记录在数据库中。这种自动化将有以下好处:

  • 无需安装边缘设备或现场硬件,这意味着实施成本最低。
  • 逃票数据将具有精确的时间粒度,而不是采样。
  • 逃票数据将具有精细的空间粒度(车站入口级别),不同于当前方法的全系统估计。
  • 将其转化为实时信息系统的潜力
  • 准确的估算将使 MTA 能够更好地进行成本效益分析,并以数据驱动的方式决定下一步行动。
  • 数据的时空特性将使高效的人员调度成为可能,以防止逃票。
  • 它将削减人工数据收集的成本

计算机视觉营救

我们将用计算机视觉可以解决的方式来重新表述这个问题。目标是识别视频中发生逃票的帧。当一个人跳过十字转门而不是走过它时,就会发生逃票。因此,如果你能在给定的画面中区分一个行走的人和一个跳跃的人,你就能在此基础上检测逃票行为。

许多基于卷积神经网络的不同架构都有可能解决这个问题。但是从头开始训练这些模型需要大量的数据,而我们并不具备解决这个特定问题的能力(除非你在 MTA 有亲戚)。但是在计算机视觉的现代,你很少需要从头开始。网络上有大量或预先训练好的模型可供使用,可以用来初始化模型的部分或完整状态,也可以用来提取可以用来训练浅层分类的特征。这被称为迁移学习。

姿态估计系统似乎是用于特征提取的完美候选,因为姿态特征应该能够从行走的人识别跳跃的人。姿态估计系统使用卷积神经网络从图像中估计人体上选定关键点的位置。因此,该解决方案分为两个模块:

姿态特征提取:该模块将检测帧中的所有人,并通过检测关键身体点来估计他们的姿态。 Open Pose 是一个开源的人体姿态估计系统,我用它来提取姿态特征。

姿势分类:基于人走过或跳过十字转门的姿势会有明显不同的直觉。姿态分类将基于姿态特征将姿态分类为跳跃或行走。

概念验证解决方案

为了测试提议方法的可行性,我开发了一个概念验证解决方案。准备了来自 google 图像搜索的大约 200 个图像的小数据集。一半的图片是站着的人,另一半是在空中跳跃的人。开放姿态用于从图像中提取姿态特征,这给你身体的 18 个关键点的位置。随后在 18 维特征空间上训练一个小的随机森林来解决双向分类问题。

分类器给出了 91%的精确度和 85%的召回率,即使训练/测试分裂为 50/50,这表明它是一个简单的分类问题。从如此小的数据集获得良好结果的原因是,在 14 维特征空间中,跳跃姿态和行走姿态是容易分离的。

Example predictions on unseen data

现在,我们将尝试通过模型运行一个视频,看看它是否可以识别包含跳跃的人的帧。确实如此。

Me walking through a poorly simulated turnstile.

Me jumping over a poorly simulated turnstile.

结果是有希望的,似乎有可能使用计算机视觉设计一个自动逃票检测系统。

我们相信,通过根据标记的 MTA 监测数据重新训练/微调整个模型管道,可以提高性能。这是因为每个系统都生成具有某些特征属性的数据分布。例如,对于 MTA 监测数据来说,遮挡模式、捕捉角度和照明条件将是独特的。在训练中使用类似的数据应该能够提高性能。

github 上有代码。你可以随意摆弄它,如果你喜欢它,可以留下一颗星星。

[## muaz-urwa/逃票-使用计算机视觉检测

通过在 GitHub 上创建一个帐户,为 muaz-urwa/逃票检测-使用计算机视觉的发展做出贡献。

github.com](https://github.com/muaz-urwa/Fare-Evasion-Detection-using-Computer-Vision)

基于整体嵌套边缘检测的卫星图像农场分割

原文:https://towardsdatascience.com/farm-segmentation-from-satellite-images-using-holistically-nested-edge-detection-63454a24b164?source=collection_archive---------15-----------------------

想象一下,如果我们丢弃了整个印度的卫星图像,可以检测出农场边界,不仅可以映射到单个农民,还可以映射到离相应农场最近的银行、铁路、公路和市场。

Farm Segmentation Layer after 12 epochs of training on top of a random area in Rajasthan

我最近一直在做一份问题声明,这将有助于检测印度所有的农场。这是一项相当具有挑战性的任务,因为我们将使用废弃的谷歌地球卫星图像来实现这一目标,而农场的边界通常很难区分,因此很难检测到。我决定采用的模型是一种基于深度学习的 ConvNeuralNet 模型,称为整体嵌套边缘检测,它给定一幅图像,在每一层后输出一个预测,并将其与地面真实二进制图像(包含相应图像的真实边缘图)进行比较,以在每一层生成一个边损失,然后将其相加,以给出融合损失。在每个损失级别生成权重,然后反向传播以给出优化值。

现在我们已经对管道有了一个简要的概述,不再赘述,让我们开始吧。

预训练模型预测

适用于 HED 的预训练模型已经在著名的 BSDS 数据集上进行了训练,但对我们没有太大用处,因为作为数据集一部分的图像类型是普通图像,而不是卫星图像。我决定将预训练的模型用于一个小图像,并测试它是否足够令人满意,以便通过一些后期处理来缩放它。

Original Image in juxtaposition with its edge map produced from the pre-trained model

正如您在边缘地图中看到的,结果没有按比例进行切割,给出了一个健壮的农场边界检测模型。可怕的…数据生成和图像标记!

数据生成

由于预训练模型未能为我们提供一个稳健的模型,我必须想出一种方法来生成一个数据集及其相应的地面实况,该数据集与 BSDS 数据集一样大,以确保模型不会过度拟合,并以最小的验证损失提供良好的整体准确性。

幸运的是,我从第一天开始就可以访问废弃的谷歌地球卫星图像数据,因此计算出从哪个确切的区域开始分割相对容易得多。在花了几个小时寻找能够包围最大农场并具有清晰边缘以进行地面校正的区域后,Ganganagar tehsil 被选中来获取标记数据。BSDS 数据集包含大约 30,000 个用于训练算法的标记图像(大约 480 X 480),因此要获得与该数据集接近的精度,大约需要 30,000 个标记图像。废弃的谷歌地球图片每张大约是 1000 X 1000,这样我们就有 480 X 480 的图片可以贴标签了。为了获得与 BSDS 一样大的数据集,需要标注 1,20,000 幅影像。疯狂!

数据增强拯救世界。

增大

图像增强是指当你没有足够的数据来训练深度学习模型时,通过对现有数据进行形态学转换来增加数据集的大小。在这种情况下,我确实有足够的数据,但扩充的想法是对相对较小的数据进行基础处理,然后将其扩充到大约 30,000。通过这种方式,人工地面实况调查将减少到标记 1225 幅图像(在将大约 16,1000 X 1000 幅图像裁剪成 480X480 之后),并且这些图像将增加 24 倍,使得数据集的大小大约为 29,400。我实现了以下基本的扩充技术来增加数据集,这将在下一篇文章中详细介绍。https://medium . com/nano nets/how-to-use-deep-learning-when-you-has-limited-data-part-2-data-augmentation-c 26971 DC 8 ced

1.缩放比例

对于这个特殊的问题陈述,我将数据集缩放到 1.5 和 0.5,使放大因子为 3 倍。进一步应用于变换的技术将针对每个标度,使它们进一步乘以因子 3。

Original Scale Image →0.5 →1.5

Scaling in OpenCV

2.轻弹

下一个转换是水平翻转图像,然后针对每个比例垂直翻转。因此,对于每个比例,您会得到两张图像,这意味着放大系数是 3 倍。

Original Image → Horizontally flipped → Vertically flipped

Flipping in OpenCV

3.轮流

实现的另一种形态变换是旋转。旋转时唯一需要注意的是,旋转后的图像尺寸可能不一样。将正方形图像旋转 90 度,将矩形图像旋转 180 度,可以保留图像的尺寸。在我的例子中,图像是正方形的,所以可以把它旋转 90 度。放大系数 2 倍。

Original Image → Rotated 90 degrees

Rotation in OpenCV

4.高斯噪声

高斯噪声扭曲了高频(出现最多且特别无用的)特征,这通常是过拟合的原因。高斯噪声也会扭曲低频特征(实际数据),但网络可以学会忽略它。为数据添加少量 apt 可以增强学习能力。我在整个数据集中添加了随机的粉红色像素来引入高斯噪声。放大系数 2 倍。

Original Image → With Gaussian Noise

Adding Gaussian noise

在这些技术的帮助下,我们终于能够创建一个非常接近 30,000 大关的数据集。现在是劳动密集型的部分...图像标注!

标记

使用了一个基于网络的工具,它将上传一个图像,使用鼠标指针,可以在图像的顶部绘制线条,突出农场的边界。在图像上画线后,提取二值图像,其中白色像素代表农场边缘,黑色像素代表非边缘。这是相应的 RGB 卫星图像的地面实况边缘图。

Manual Labeling of Edges using RGB satellite images

Extracted Binary image after completing labeling

整体嵌套边缘检测

该算法基于由 5 个卷积层组成的卷积神经网络架构。在输入图像通过之后,每一层对其输入进行非线性操作,并生成边缘图,该边缘图与输入图像的地面真实图像进行比较,以生成边损。每个侧输出层与产生侧损失的具有相应权重的分类器相关联。在这种图像到图像的训练中,在训练图像中的所有像素上计算损失函数。

Side-output error for each layer. weights are denoted as w = (w(1) , . . . , w(M) ). where l(side) denotes the image-level loss function for side outputs

对于标准卫星图像,边缘/非边缘像素的分布严重倾斜。估计 85–90%的图像是非边缘的,这就是使用成本敏感函数来正确捕捉误差的原因。该算法在每个像素项的基础上引入类平衡权重β。索引 j 在图像 x 的图像空间维度上。然后,我们使用这个类平衡权重作为一种简单的方法来抵消边缘和非边缘之间的这种不平衡。具体地,我们定义了等式(1)中使用的以下类别平衡的交叉熵损失函数

l(side) denotes the image-level loss function for side outputs

其中,β= | Y |/| Y |和 1β= | Y+|/| Y |。| Y |和|Y+|分别表示边缘和非边缘基础真值标签集。pr(yj = 1 | X;w,w(m) ) = σ(a (m) j ) ∈ [0,1]使用 sigmoid 函数σ(.在每个侧输出层,我们然后获得边缘图预测 yˇ(m)side =σ(aˇ(m)side),其中 aˇ(m)side≡{ a(m)j,j = 1,.。。,|Y |}是层 m 的侧面输出的激活

为了直接利用侧输出预测,该算法在网络中增加了一个加权融合层,同时在训练过程中学习融合权重。

loss function at the fusion layer L(fuse)

其中 Y fuse ≡ σ(求和(m=1-M)(hmA(m)侧))其中 h = (h1,.。。,hM)为融合权重。Dist(,)是融合预测和地面真实标签图之间的距离,我们将其设置为交叉熵损失。将一切放在一起,我们通过标准(反向传播)随机梯度下降最小化以下目标函数:

The final objective function used for backpropagation

Network Architecture for HED highlighting the error backpropagation paths. Side-output layers are inserted after each convolutional layer. Supervision is imposed at each side-output layer, guiding the side-outputs towards edge predictions with the characteristics we desire. The outputs of HED are multi-scale and multi-level, with the side-output-plane size becoming smaller and the receptive field size becoming larger. One weighted-fusion layer is added to automatically learn how to combine outputs from multiple scales. The entire network is trained with multiple error propagation paths (dashed lines).

https://arxiv.org/pdf/1504.06375.pdf

轮廓检测、绘图和后处理

原始预测图像的边缘可能完整,也可能不完整,一旦我们从该图像中提取轮廓,我们希望轮廓是一个闭合的多边形,因为农场也是闭合的。为了处理不完整的边缘,引入了扩张,这将加宽边缘,使得缺失的链接将被完成,并且在扩张后的图像上进行骨架化,以从宽边缘提取单像素线,从而给出最终的完整轮廓。

Satellite Image with prediction after dilation and skeletonization. Hanging edges still remain.

此外,如果两条边之间的距离对于膨胀处理来说很大,则在黑色图像上绘制每个轮廓(从已经生成的轮廓中获取),进行填充,然后从中提取新的轮廓,这将自动移除多边形中可能存在的不完整的边。

Satellite image with contours after post-processing. Most of the hanging edges are removed.

结论

在基于深度学习架构的模型的帮助下,我能够正确地检测拉贾斯坦邦的农田,准确率为 80–85%。这有助于构建进一步的模型,包括:一种测量农场等级的新方法(农场指南),一种连接矩阵,其中农场被映射到最近的铁路、水路、银行、mandi、土壤的各个方面,基于不同的指数和盐度。

(注意:整体嵌套边缘检测的 Keras 实现借用了https://github.com/lc82111/Keras_HED

使用神经网络的时尚产品图像分类|从零开始的机器学习(第六部分)

原文:https://towardsdatascience.com/fashion-product-image-classification-using-neural-networks-machine-learning-from-scratch-part-e9fda9e47661?source=collection_archive---------5-----------------------

通过在 Python 中从头构建神经网络,了解如何处理图像数据和对时尚产品进行分类

TL;DR 从头开始用 Python 构建神经网络。使用该模型将时尚产品的图片分为 10 类中的 1 类。

我们生活在 Instagram、YouTube 和 Twitter 的时代。图像和视频(一系列图像)主导了千禧一代和其他怪人消费信息的方式。

让模特了解图像显示的内容,对于了解你的情绪状态(是的,你可能会在 Instagram 上发布分手自拍后立即收到个性化的可乐广告)、位置、兴趣和社交团体至关重要。

主要地,理解实践中使用的图像数据的模型是(深度)神经网络。在这里,我们将从头开始用 Python 实现一个神经网络图像分类器。

[## 7 .神经网络

合作笔记本

drive.google.com](https://drive.google.com/file/d/1S59KWV8KmZTI-A6OpXge3yG87HXyIcUz/view?usp=sharing)

图像数据

希望你不会对计算机不能像我们一样看到图像感到惊讶。设备上的每个图像都以矩阵的形式表示/存储,其中每个像素都是一个或多个数字。

读取时尚产品数据

时尚-MNIST 是 Zalando 文章图片的数据集——由 60,000 个示例的训练集和 10,000 个示例的测试集组成。每个示例都是 28x28 灰度图像,与 10 classes 中的标签相关联。我们打算将时尚 MNIST 作为原始 MNIST 数据集的直接替代,用于机器学习算法的基准测试。它共享训练和测试分割的相同图像大小和结构。

以下是这些图片的示例:

你可能熟悉原始的手写数字 MNIST 数据集,并想知道为什么我们不使用它?嗯,在做预测可能太容易了。当然,时尚更酷,对吗?

探索

产品图像是灰度的,28x28 像素,看起来像这样:

以下是图像像素矩阵的前 3 行:

请注意,这些值在 0–255 范围内(灰度)。

我们有 10 类可能的时尚产品:

让我们来看看使用 t-SNE 的一些产品的低维表示。我们将使用来自 scikit-learn 的实现将数据转换成二维数据:

您可以观察到一些类之间有明显的分隔,而其他类之间有明显的重叠。让我们建立一个可以尝试区分不同时尚产品的神经网络!

神经网络

神经网络(NNs),特别是深度神经网络,在过去几年里在机器学习领域风靡一时。这并不奇怪,因为大多数关于各种机器学习问题的最新成果(SOTA) 都是通过神经网络获得的。

浏览论文用源代码实现 SOTA

人工神经元

模拟我们的生物神经元的目标导致了人工神经元的发明。这是你大脑中单个神经元的样子:

source: CS231n

另一方面,我们有一个大大简化的数学模型,它在实践中非常有用(神经网络的成功证明了这一点):

source: CS231n

人工神经元的想法很简单——你有来自某处的数据向量 X ,参数向量 W 和偏置向量 b 。神经元的输出由下式给出:

其中 f 是控制神经元输出信号强度的激活函数。

构建神经网络

您可以使用单个神经元作为分类器,但有趣的部分开始于您将它们分组到层中。具体来说,神经元连接成一个无环图,数据在各层之间流动:

source: CS231n

这个简单的神经网络包含:

  • 输入层-3 个神经元,应该与输入数据的大小相匹配
  • 隐藏层-您的模型在训练期间应该学习的权重为 W 的 4 个神经元
  • 输出层-提供模型预测的 2 个神经元

想建立深度神经网络?只需添加至少一个隐藏层:

source: CS231n

乙状结肠的

sigmoid 函数是相当常用的激活函数,至少直到最近是这样。它具有明显的 S 形,对于 00 到 11 之间的任何实输入值和输出值,它都是一个可微的实函数。此外,它在每一点都有一个正导数。更重要的是,我们将使用它作为模型隐藏层的激活函数。

下面是它的定义:

下面是我们实现它的方法:

它的一阶导数(我们将在训练算法的反向传播步骤中使用)具有以下公式:

我们的实现重用了 sigmoid 实现本身:

Softmax

softmax 函数很容易区分,它是纯函数(输出仅取决于输入),结果向量的元素和为 1。这是:

以下是 Python 实现:

在概率论中,softmax 函数的输出有时用作分类分布的表示。让我们来看一个示例结果:

输出的大部分权重对应于输入 8。softmax 函数突出显示最大值并抑制较小值。

反向传播

当使用神经网络时,反向传播几乎是我们所做的任何事情的支柱。该算法由 3 个子任务组成:

  1. 向前传球
  2. 计算误差
  3. 进行反向传递(反向传播)

在第一步中,backprop 使用数据和网络的权重来计算预测。接下来,基于预测和提供的标签计算误差。最后一步是从最后一层开始,通过网络传播错误。因此,权重基于误差一点一点地更新。

让我们对算法实际上在做什么建立更多的直觉:

求解异或

我们将尝试创建一个能够正确预测 XOR 函数值的神经网络。下面是它的真值表:

这是一个直观的表示:

让我们从定义一些参数开始:

epochs 参数控制我们的算法在训练期间“看到”数据的次数。然后,我们设置输入层、隐藏层和输出层中神经元的数量——我们有 2 个数字作为输入,1 个数字作为输出大小。学习率参数控制我们的神经网络从新数据中学习并忘记已经知道的东西的速度。

我们的训练数据(来自表格)如下所示:

我们的神经网络中的 W 向量需要有一些初始值。我们将采样一个均匀分布,用适当的大小初始化:

最后,反向投影算法的实现:

那个误差好像在减少!耶!而且实现也没那么吓人吧?

forward步骤中,我们取数据 X 和 W_hidden 的点积,并应用我们的激活函数来获得我们的隐藏层的输出。我们通过获取隐藏层输出和 W_output 的点积来获得预测。

为了获得error,我们计算真实值和预测值之间的差异。请注意,这是一个非常粗略的度量,但是对于我们的示例来说,它很好。

最后,我们使用计算出的误差来调整权重。注意,我们需要来自正向传递act_hidden的结果来计算 W_output,并使用sigmoid_prime计算一阶导数来更新 W_hidden。

为了进行推断(预测),我们将只进行前一步(因为我们不会根据结果调整 W ):

我们的魔法似乎起作用了!预测是正确的!

图像分类

构建神经网络

我们的神经网络将只有一个隐藏层。我们将实现上面显示的训练算法的一个更加复杂的版本,以及一些方便的方法。

初始化权重

我们将对一个均匀分布进行采样,初始权重的值在-1 和 1 之间。下面是实现过程:

培训

我们来看看训练方法:

对于每个时期,我们应用反向投影算法,评估相对于权重的误差和梯度。然后,我们使用学习率和梯度来更新权重。

做一个反向投影步骤比我们的 XOR 例子要复杂一点。我们在返回梯度之前做了一个额外的步骤——应用 L1 和 L2 正则化。正则化通过惩罚参数 W 的大值来引导我们的训练朝向更简单的方法。

我们的forwardbackward步骤与上一个例子中的非常相似,误差如何?

测量误差

我们将使用交叉熵损失(称为对数损失)函数来评估误差。该函数测量分类模型的性能,其输出是概率。它惩罚(严厉地)错误和自信的预测。定义如下:

其中 C 是类的数量, y 是二进制指示符,如果类标签是观察的正确分类, po 属于类 c 的预测概率

Python 中的实现如下所示:

现在我们有了损失函数,我们终于可以定义模型的误差了:

在计算交叉熵损失之后,我们添加正则化项并计算平均误差。以下是 L1 和 L2 正则化的实现:

做预测

现在我们的模型可以从数据中学习,是时候对它以前没有见过的数据进行预测了。我们将实现两种预测方法— predictpredict_proba:

回想一下,神经网络中的预测(通常)包括对数据应用向前的一步。但它的结果是一个值的向量,表示每个类对数据的信任程度。我们将使用最大似然估计(MLE) 来获得我们的最终预测:

MLE 的工作方式是选取最高值,并将其作为输入的预测类返回。

方法predict_proba返回所有类别的概率分布,代表每个类别正确的可能性。注意,我们通过将softmax函数应用于forward步骤的结果来获得它。

估价

是时候测试我们的神经网络模型了。我们可以这样训练它:

培训可能需要一些时间,所以请耐心等待。让我们来预测一下:

首先,我们来看看训练错误:

这里看起来有些可疑,似乎我们的模型不能继续减少 150 个历元左右的误差。让我们来看一个单一的预测:

那个好像是对的!让我们再看几个:

不太好。培训和测试的准确性如何:

嗯,那些看起来不太好。虽然随机分类器将返回约 10%的准确度,但在测试数据集上约 50%的准确度也不会成为实用的分类器。

提高准确性

训练误差图上的“锯齿状”线显示了我们的模型无法收敛。回想一下,我们使用反向传播算法来训练我们的模型。数据标准化后,训练神经网络收敛速度更快

我们将使用 scikit-learn 的标度来标准化我们的数据。该文件指出:

以平均值为中心,以单位方差为分量标度。

这是新的训练方法:

让我们来看看错误:

误差似乎稳定得多,稳定在一个较低的点——约 200 对约 400。让我们来看看一些预测:

那些看起来也好多了!最后,准确性:

训练集上的约 87%(对约 50%)比未缩放的方法有了巨大的改进。最后,你的努力得到了回报!

结论

多好的旅程啊!我希望你也能从头开始开发你的第一个神经网络!

你学会了如何处理图像数据,转换它,并用它来训练你的神经网络。我们使用了一些方便的技巧(缩放)来极大地提高分类器的性能。

[## 7 .神经网络

合作笔记本

drive.google.com](https://drive.google.com/file/d/1S59KWV8KmZTI-A6OpXge3yG87HXyIcUz/view?usp=sharing)

最初发表于【https://www.curiousily.com】

喜欢你读的吗?你想了解更多关于机器学习的知识吗?提升你对 ML 的理解:

[## 从零开始实践机器学习

“我不能创造的,我不理解”——理查德·费曼这本书将引导你走向更深的…

leanpub.com](https://leanpub.com/hmls)

时尚科学进行季节性色彩分析

原文:https://towardsdatascience.com/fashion-science-takes-on-seasonal-color-analysis-b399ecc8f675?source=collection_archive---------15-----------------------

结果是数据中找不到“季节”

穿对颜色的衣服,更有魅力!

这就是季节性色彩分析的魅力所在。通过恰当地将你置于四季之一——春、夏、秋、冬——每一季都有适合你的调色板。

本文运用时尚科学来探索一个简单的概念——人们真的会被分成四个部分来映射传统的季节分配吗?对我来说,时尚科学包括图像处理、机器学习和数据分析,但也包括传统的艺术和心理学概念。

使用从我们当前的 iPhone 应用程序收集的超过 100,000 张“肖像自拍”(所有人都关注色调),我们将每张自拍分成关键的身体部位——皮肤、头发、眼睛和嘴唇。(我说的‘肖像自拍’明确指的是高分辨率的肖像——我们需要虹膜上有很多像素!)然后,我们应用聚类分析技术,根据自拍者身体部位颜色的相似性,将自拍者分组在一起。虽然两个集群和四个集群显然很好,但这些与季节描述完全不相关。

有了新的、基于科学的细分,就有了一个坚实的基础来更好地看待时装设计、营销和零售市场细分的数据方法。进一步的工作也将带我们重新审视个人色彩分析——基于个人色彩调和的个性化调色板的创建。

背景

季节性色彩分析(SCA)基于和谐的色彩会增强个体的自然美这一概念。季节方面涉及到把所有人放入四个部分中的一个,以季节命名。有用户自我识别的指导,尽管历史上用户已经被专业的从业者放入他们的季节。有趣的是,这些从业者经常不同意将图像放在哪个季节,尤其是在边缘情况下。

鉴于带摄像头的智能手机的普及,这难道不是自动识别一个人的季节的自然领域吗?

因此,我们一群人研究、设计并部署了一个 iOS 应用程序,它就能做到这一点,即自拍肖像并识别那个人的季节。此外,我们向那个人展示了他们的季节性调色板,还为他们提供了增强现实功能——例如来自他们相机手机的实时视频流,显示了好的和不好的颜色。这在诸如服装店的场所是有用的。

Augmented Reality in a Clothing Store for an Autumn

这个版本的应用程序存在一些挑战,最重要的是,由于光照和人像自拍构图的变化,季节性分配缺乏可重复性。我们已经在下面的分析中解决了这个问题,并计划在不久的将来发布一个更有效的应用程序。

通过使用我们的应用程序,我们有超过 10 万张人类的自拍照。这个集合并不代表人类在年龄、性别、地理和种族上的分布。一种有用的方式是把这些肖像想象成“对 iPhone 感兴趣的时尚人士”。这些肖像自拍只是为了分析而收集的。用户在提供他们的自拍肖像之前同意这一条款,这些在个人层面上是不可用或不显示的。

给定这组人像自拍的数据集,我们能学到什么?让我们测试一下假设,在我们的数据中确实有季节来对个体进行分类。

对肖像进行分类

评价一幅肖像的本质在于把肖像图像分解成身体部位——皮肤、头发、眼睛、嘴唇。每一个身体部位的颜色都代表了这个人,并引导我们找到他们的颜色。这是一个至关重要的假设——季节性颜色分配是基于关键的身体部位颜色。

简而言之,这个过程是一个简单的管道:

  • 过滤掉糟糕的图像——那些过度亮/暗的图像,那些阴影很深的图像,戴着眼镜/帽子的图像,等等。
  • 自动白平衡图像——通过对机器学习网络的广泛训练,我们在显示“真实”人类方面取得了良好的效果。作为一个练习,浏览一组脸书的自拍照,看看明显的人类肤色的广泛变化!

Before / After — Automatically adjusting White Balance for good skin tones

  • 识别身体部位-使用图像处理和额外的机器学习来识别皮肤、头发、眼睛(虹膜)、眼睛(巩膜-白色)和嘴唇。

Sample body part images — hair, skin, iris, sclera, lips — from various models

  • 对这些身体部位的颜色进行分类——包括平均颜色和代表颜色的等级,以便抓住更微妙的次要颜色的丰富性。

就这样——我们现在有了自拍肖像的数字表示,可以进行分析了。

聚类分析

聚类分析意味着对肖像进行分组,以便相似的肖像在聚类中。这是数据分析中的常见任务。有许多方法和算法可用于聚类分析。我们聚类分析的输入是一组身体部位的一组颜色。输出是一组聚类和每个肖像对其聚类的标识。

以下结果使用了三个身体部位-皮肤、头发和虹膜。这些是根据它们在 Lab 颜色空间中的平均颜色而不是 RGB 进行聚类的。正如维基百科所说,Lab“被设计成在人类色觉方面是一致的,这意味着这些值的相同数量的数字变化对应于大约相同数量的视觉感知变化。”也就是说,我们已经用多个身体部位和各种颜色空间进行了评估,以获得类似的结果。

识别聚类的第一种方法被称为肘方法。从 1 到 30 的聚类被评估它们的聚类程度。聚类数量的最佳表示是在图中有一个弯头的地方。

Elbow method to determine optimal clustering — using Skin, Hair, Iris in Lab colorspace

季节色彩分析会让我们相信,应该有两个——温暖/凉爽——和四个——季节的清晰和明显的集群。在此图中,并没有突出显示“肘部”在哪里,是 2 个、3 个、4 个还是 5 个集群?

另一种方法是层次聚类。这可以根据每个模型的身体部位颜色之间的距离来识别聚类。输出显示为树状图——一种树形结构。在图表中,Y 轴代表肖像之间的亲密程度。解释该图表以识别聚类的方法是找出聚类一致的大 Y 长度。

Dendrogram — using Skin, Hair, Iris in Lab colorspace

使用这种方法,人们可以清楚地看到两个簇接着四个簇的解释。这符合当季色彩的诠释。

季节性颜色分析中的一个概念是对两个部分的聚类可以/应该基于皮肤的暖色/冷色。在色轮上,暖色通常与色轮的顶部相关——红色到黄色——而冷色是另一侧——或者蓝色到紫色。传统上,“温暖”包括春季和秋季,而“凉爽”包括夏季和冬季。

在我们的一组代表性数据中,肤色似乎都是暖色,即(惊喜!)肉色,随着色调的变化,集群似乎均匀分散。换句话说,双聚类模型对肤色/色调没有帮助。尽管注意到这种温度与色调的关联通常被认为是心理上的而不是物理上的,指的是确定温度的“底色”。这种基调没有出现在数据中。

Skin Hues in the two cluster classification

四个星团——它们是季节吗?大考

如果我们从之前的树状图聚类图中提取四个聚类模型,我们可以测试它们是否映射到季节。四个聚类中每个聚类的实际平均颜色在该图表的底部以图形方式显示。

为了测试我们的聚类,我们将我们的聚类任务与一位流行的季节性色彩专家的任务进行了对比。因为我们不知道季节如何映射到这些新发现的聚类,所以我们需要尝试所有的组合,例如,尝试将聚类 1 作为春天,然后将聚类 1 作为夏天,等等。总而言之,对于可能的季节,我们的星团有 24 种可能的组合。完美的映射是 100%,而完全随机的映射是 25%。从经验来看,我希望有 80%-90%的映射能够成功。

对于我们的聚类到专家季节分类的最佳映射,有 36%的匹配百分比。这不是一张可信的地图。

忽略总映射(每个季节映射到一个集群,但一次只关注一个季节),最佳映射是冬季到集群二,顺便说一下,这看起来有些合适。其次是夏想的集群三。然而,尽管完美的映射将再次是 100%,但是冬季到聚类 2 的映射百分比仍然只有 47%,而夏季到聚类 3 的映射百分比仍然是 45%——这仍然是一个很差的、不可信的结果。

总结

我们已经表明,在我们超过 10 万张自拍肖像和季节性色彩分析模型之间,充其量只有微弱的联系——我们看到每类随机分配 25%,但远远没有达到预期的 80–90%。这通过各种聚类技术、暖/冷评估的应用以及季节性颜色的专家分配的应用来显示。可以有多种方式来反对我们的解释,从自拍肖像的颜色提取不良到我们的数据集与他人的问题。我们尊重所有的挑战,但保留这项调查正在开始的警告——但根据时尚科学的规则——这是在可能的情况下对基于事实的分析的强烈偏好。

这项工作包含了对时装业的强烈暗示。根据颜色对细分市场进行更加精确和真实的识别,可以更好地调整设计、营销甚至零售。

我们正在以各种方式继续这项工作。作为这项工作的一个分支,一个有趣的方法是创建动态调色板——或个性化调色板,而不是季节或集群。这进一步提供了将调色板调整到特定身体部位着色的优点,例如染发剂、唇彩等。以及变化的照明条件,例如阳光、舞厅、办公室等。

请继续关注我们的研究,分享数据集和代码!

(Preliminary) Creation of Personalized Palette based on Color Harmonies

Fast.ai 库:第一印象

原文:https://towardsdatascience.com/fast-ai-library-1st-impression-958cb52afc?source=collection_archive---------12-----------------------

最近尝试了一下 fast.ai 深度学习库,想分享一下经验。

首先,我的背景——不是为了展示我有多酷/多坏,而是因为它主要与像我一样思考和编码的人相关,而与其他人不太相关。

我从来没有学过软件工程,所有的事情都是自学的。从 C++和 matlab 开始,在我参与的 caffe 工作了 3 年,然后转到 python 和 pytorch 。大多数情况下,我写脏代码,并不关心它是否是次优的和不可伸缩的,除非它成为瓶颈。我很少在尝试做某件事之前阅读文档。
也就是说,我可以设计并实现干净、可扩展且高效的软件——就像我曾经在一家名为“先拍照后购买”的时尚初创公司担任首席技术官一样。但这需要时间,而且不适合深度学习研究的快速原型制作,所以大多数情况下我不愿意这么做。我也是 Jupyter 笔记本的粉丝。

先从 fast.ai 的好东西说起,然后描述一下我经历过的挑战。

fast.ai 里的牛逼东西

  1. 微数据集。我无法表达微数据集对于快速调试、可再现性、健全性检查和学习有多酷。

https://github.com/fastai/fastai/blob/master/examples/vision.ipynb

2.学习率+wd+动量和增量的默认参数。这可能看起来是小事,但关系重大。当你试图让工作变得疯狂时,一切都可能出错。数据,采样,缩放,一切。从等式中去掉两个主要的东西是一件解放大脑的大事

https://docs.fast.ai/vision.transform.html#Data-augmentation-details

  1. lr_find()一个周期策略concat 池。Lr_find 是非常强大而简单的方法,来自 Leslie N. Smith 的“训练神经网络的循环学习率”,该论文值得 NIPS/ICML 的最佳论文,但可能会被忽视。fast.ai 保存并提升。想法很简单——用不同的 lr 做一个小批量 SGD,重新编码,选择最好的一个。

https://docs.fast.ai/basic_train.html#lr_find

它工作得很好:当我们为硬网络论文写代码并做实验的时候,我们花了大约 1 周的时间去发现并相信 lr = 10 是好的。使用 lr_find 和 one_cycle,我可以从 2 次尝试中恢复几乎相同的性能。

ConcatPool 是简单的[avg,max]池。根据我的经验,这是仅次于宝石池的第二好选择。

太棒了,但是只有当你摸它们的时候

  1. 。复试。** 这是最重要的事情**,这可能在 fast.ai 课程中已经提到很多了,但对于外行人来说一点也不明显,尤其是当你只是谷歌那些不起作用的东西,而不是仔细阅读文档的时候。
    我如何尝试新事物?在 vim 中打开 train.py,把一些代码放在不同的地方,通常利用全局可见的变量(也有风险,但谁在乎呢)。因为我是那种“宁愿花一周时间在谷歌上搜索一些东西,也不愿占用别人的时间寻求帮助”的人 Radek Osmulski ,我花了两天时间(虽然不是全职)试图找出如何在 fast.ai 中做同样的事情
    我几乎要放弃了,但后来找到了回调,并意识到它们是我所需要的。我只是不知道该找什么。Fast.ai 开发者标记了“插入你代码的地方”,你可以通过写回调来访问它们。您还可以全面访问所有变量。

My explanation of what fast.ai callbacks are — just marked places to put your code.

对我没用的东西

它们大多是测试时的东西,不在“单一输入—单一类输出”的范例中。例如,我的网络有 5 个独立的头(OK,4 个独立的和 1 个相关的),并输出 2 个列表:一个列表具有来自 5 个分类器的预测,另一个具有 4 个 512d 特征。这足以打破许多现成的功能。
所有故障实例都可以从这里重现:https://github . com/ducha-aiki/whale-identificati on-2018/blob/master/reproduct _ problems . ipynb

  1. learn.predict()摘自推理教程:

2.好的,我可能应该使用函数 get_preds(),它根据教程给出了原始输出,对吗?不对。

********

Things which work with tutorial (left), do not work for me.

3.最后一次尝试,测试时间增加

Again, when output is list, not tensor, nothing works out of the box

不要误会我的意思,这些都不是严重的问题:我很容易就构建了工作区,尽管不是以 fast.ai 的方式

Not nice, but works

对于测试时的增强,我只是切换了转换。另一个问题:我还没有找到如何使火车装载机是非洗牌,所以在肮脏的方式:

Poor-man TTA

4.fp16 从来没有为我工作过,结果是失败。我现在已经尝试了 fast.ai 的最新版本,但是不知何故其他东西被改变了,以前工作的代码现在不工作了。我已经经历过几次了,所以:

5. fast.ai 真的很快,这很好,但也有它自己的缺点:你不能只是更新版本,然后希望什么都不会坏。特别简单的改名都很烦。

我也有一些其他问题,但现在认为它们不值得一提——它们更像是我的错,而不是图书馆的错。

总之,我很高兴我已经尝试了 fast.ai,并且可能也会在我的研究项目中使用它。

快速简便地将开放的街道地图数据动态导入到 Google 工作表文件中

原文:https://towardsdatascience.com/fast-and-easy-way-to-dynamically-import-open-street-map-data-into-a-google-sheet-file-495a400ec25?source=collection_archive---------21-----------------------

问题概述

你在一家管理意大利大城市地铁运输的公司担任业务分析师,你需要向营销和传播部门提供该地区酒吧和咖啡馆的列表,并向他们分发可以贴在窗户上或房间内的地图。此外,销售部门将联系离车站最近的酒吧和咖啡馆,提议加入贵公司提供的交通服务的售票计划。

这是一个每月发生的定期请求。

数据源

这类地理相关问题的主要数据源之一是公开的街道地图。

有几种方法可以访问开放的街道地图数据库,但是最流行的方法之一是使用天桥 API。在本文中,我们将看到如何使用这些 API 在 Google Sheet 中生成一个动态列表,该列表由参数化查询生成,并由最终业务用户设置值。通过这种方式,其他同事可以自己获取数据,并可以复制、编辑或保存列表以供将来操作,例如将每个酒吧或咖啡馆分配给一个将联系该结构的重要客户经理。

通过天桥 API 的查询可以使用两种语言:天桥 XML 或天桥 QL。

这次我们将使用天桥 QL,,一种用 C 风格语法编写的命令式编程语言。学习这门语言的主要“入门”指南可在本页找到。

数据析取

让我们总结一下我们需要提取的内容:

  • 文本输出,我们将使用标准的 CSV 格式,标题在第一行,用逗号作为列分隔符,Google Sheet 会喜欢这个选择的!
  • 指定区域的酒吧和咖啡馆列表;
  • 经营场所必须在地铁站入口 X 米半径范围内。

要提取的属性的细节变化很大,并且不是每个场所的所有信息都被完全编辑,与其他国家(首先是德国)相比,可能有许多细节字段仍然不完整(例如电话号码或营业时间)。

超时将在 20 秒后修复。商业用户将不能轻易改变这个值。增加它是不合适的,因为我们想要提取的数据并不大,并且以下规则适用:

"如果查询运行时间超过此时间,服务器可能会超时中止查询。第二个影响是,这个较高的值,服务器可能会在执行查询之前拒绝它。

让我们来设置查询!

正如预期的那样,我们希望提取特定区域的酒吧和咖啡馆列表。这些元素在 OSM 数据库中用以下标签分类:

  • 舒适=酒吧
  • 舒适=咖啡馆

当我们必须使用天桥 API 时,要遵循的一个好规则是查阅与我们想要提取的标签相关的 TagInfo 页面:

在这里,我们可以找到各种有用的信息。在“ combination ”选项卡中,我们找到了与该标签相关的特性列表,这是制作我们想要提取的列列表的良好起点,稍后我们将在项目中指定这些列。

在“概述”选项卡中,我们可以看到这个地方在数据库中是如何编码的。在这种情况下,我们很容易看到,酒吧和咖啡馆可以是节点,方式或关系。出于我们的目的,该信息是基本的,因为在查询中我们将指定在所有这些实体中进行搜索,并且因为我们知道该信息可以存储为点(节点)或更大的区域(方式)。在这个项目中,我们显然希望只提取一次与酒吧/咖啡馆相关的信息,因此我们希望获得的纬度和经度坐标是线所包围区域的中心坐标。

让我们举一个例子,在一个方形条的情况下,在 OSM 数据库中有四个 way 元素,但是我们希望在表中所有这些都在一行中表示,考虑到条总是相同的(相同的名字,相同的电话号码,…)。这一目的将通过指示立交桥仅提取该区域的中心来实现。

查询中要考虑的最后一个元素是我们要提取的区域。我们选择使用每个自治市的边界提取数据,这些数据将在表格的用户填写的输入单元格中显示,记住一些地铁站位于不同的自治市。

从这张有用的表中,我们可以了解到,意大利直辖市的行政边界在 OSM 数据库中用 8 级表示。

至此,我们已经拥有了构建查询的所有元素:

我们考虑了酒吧/咖啡馆和车站入口之间的固定距离值(50 米)的查询,以及固定的自治市(ISTAT 代码 015146 对应于米兰市),因此我们必须修改查询,使其可用于 Google sheet。

如何在电子表格中使用查询

我们需要遵循以下步骤:

  • 内包含查询 IMPORTDATA 公式(阅读 Google Sheet 官方指南);
  • 用嵌套双引号替换单单双引号;
  • 将距离值替换为&B5&
  • &B4&替换市 ISTAT 代码的值。

我们最后得出以下结论:

通过链接到隐藏表的数据验证和获得 ISTAT 代码VLOOKUP 公式 ,我们可以制作一个用户友好的下拉菜单,用户可以从中选择自治市的名称和要考虑的距离值。

每次单元 B3 或单元 B4 发生变化时,服务地址列表的值都会更新。

使用 Nvidia DALI 在 PyTorch 中快速增强数据

原文:https://towardsdatascience.com/fast-data-augmentation-in-pytorch-using-nvidia-dali-68f5432e1f5f?source=collection_archive---------18-----------------------

原帖:https://www . basic ml . com/performance/2019/04/16/py torch-data-augmentation-with-NVIDIA-Dali

在我工作的新项目中,我必须为多标签多类别分类任务处理一组足够大的图像数据。尽管 GPU 利用率接近 100%,但运行超过 200 万张图像的单个训练时段需要近 3.5 小时。如果您正在进行基线实验,并且想要快速得到结果,这是一个大问题。我首先想到的是,由于我在处理原始大小的图像,每个图像至少有几兆字节,所以瓶颈是磁盘 I/O。我使用 Imagemagick mogrify 来调整所有 200 万张图像的大小,这花了很长时间。令我惊讶的是,调整图片大小根本没有减少训练时间!嗯,不明显。因此,我仔细检查了代码,发现主要的瓶颈是 PyTorch 中的图像增强操作。

在偶然发现 Github 的时候,我发现在 Nvidia 工作的人最近发布了一个库——DALI,据说就是为了解决这个问题。该库仍在积极开发中,并支持所有主要 ML 开发库的快速数据扩充— PyTorchTensorflowMXNet

Fig 1: A typical data augmentation pipeline

使用 Nvidia DALI,可以通过将适当的操作移到 GPU 来优化上述数据流水线。使用 DALI 后,管道看起来像这样-

Fig 2: Fig 2: An Nvidia DALI pipeline

有关 DALI 功能的更多细节,请参见这篇由 Nvidia 开发人员撰写的初学者友好帖子,标题为使用 NVIDIA DALI 进行快速 AI 数据预处理。在这篇文章的剩余部分,我将展示如何将 Nvidia DALI 合并到 PyTorch 代码中。欢迎读者对下面的代码提供可能的改进。

我们从安装所需的依赖项开始。

到目前为止,您已经完成了“nvidia-dali”的安装,我们现在将把它集成到我们的 PyTorch 代码中。为了创建虚拟数据集,我们下载了由 Udacity 提供的花卉分类数据。数据集包含两个文件夹——“训练”和“有效”。我们使用“train”文件夹中的图像,并展平目录,该目录组织为一个分层文件夹,包含按标签排列的图像,每个标签有一个子文件夹。我们不使用提供的标签,而是生成虚拟标签进行演示。

接下来,我们创建一个空格分隔的文件,它符合 Nvidia DALI 官方文档页面上给出的示例。

接下来,我们创建一个“ExternalInputIterator ”,它对我们的数据进行批处理,并由 DALI 管道用来输入数据,并将其馈送给相应的设备进行处理。下面的代码改编自官方代码这里为多个标签工作。感谢悉达多甘居指向官方教程。

接下来,我们实例化该迭代器,并将其作为输入提供给‘externalsourcepipeline ’,该‘externalsourcepipeline’扩展了‘pipeline’类,并将数据提供给相应的设备用于增强操作。

我们差不多完成了,现在我们实例化一个“DALIGenericIterator ”,它帮助我们迭代数据集,就像我们在 PyTorch 中通常做的那样。

谷歌 Colab: 笔记本

我还没有在我的代码中对 DALI 进行基准测试,一旦有了结果,我会更新这篇文章。

保持优雅。

链接:

-达利 Github 回购

-大理官方博客文章

快速傅里叶变换

原文:https://towardsdatascience.com/fast-fourier-transform-937926e591cb?source=collection_archive---------0-----------------------

Photo by Fré Sonneveld on Unsplash

如何从零开始用 Python 实现快速傅立叶变换算法?

如果你有电气工程的背景,你很可能听说过傅立叶变换。通俗地说,傅立叶变换是一种将信号的域(x 轴)从时间改变到频率的数学运算。后者对于分解由多个纯频率组成的信号特别有用。更多细节请看下面的视频。

傅立叶变换的应用不限于数字信号处理。事实上,傅立叶变换可以加速卷积神经网络的训练过程。回想一下卷积层如何在图像的一部分上覆盖内核,并对该位置的所有值执行逐位乘法。然后,内核被转移到图像的另一部分,并且重复该过程,直到它遍历了整个图像。

傅立叶变换可以通过利用以下特性来加速卷积。

上面的等式表明,两个信号的卷积等价于它们的傅立叶变换的乘法。因此,通过将输入变换到频率空间,卷积变成单个元素的乘法。换句话说,卷积层和内核的输入可以使用傅立叶变换转换成频率,乘以一次,然后使用傅立叶逆变换转换回来。存在与将输入变换到傅立叶域和傅立叶逆变换以将响应返回到空间域相关联的开销。然而,这被从执行单次乘法而不是将图像的不同部分与内核相乘获得的速度所抵消。

离散傅里叶变换

离散傅立叶变换(DTF)可以写成如下形式。

为了确定离散信号x[n](其中N是其域的大小),我们将它的每个值乘以n的某个函数的e。然后我们对给定的n的结果求和。如果我们使用计算机来计算信号的离散傅立叶变换,它需要执行 N(乘法)x N(加法)= O(N)次运算。

顾名思义,快速傅立叶变换(FFT)是一种确定输入的离散傅立叶变换的算法,比直接计算要快得多。用计算机科学的行话来说,FFT 将大小为N的问题所需的计算次数从O(N^2)减少到O(NlogN)

表面上看,这似乎没什么大不了的。然而,当 N 足够大时,它可以产生一个不同的世界。请看下表。

假设执行一次操作需要 1 纳秒。对于大小为N = 10 ⁹.的问题,快速傅立叶变换算法需要大约 30 秒来计算离散傅立叶变换相比之下,常规算法需要几十年的时间。

快速傅立叶变换算法

假设,我们将傅立叶变换分成偶数和奇数索引子序列。

在做了一点代数运算后,我们得到了两项的和。这种方法的优点在于可以同时计算偶数和奇数索引子序列。

假设,N = 8,为了可视化数据随时间的流动,我们可以利用一个蝴蝶图。我们同时计算偶数和奇数项的离散傅立叶变换。然后,我们使用上面的公式计算x[k]

我们可以用大 O 符号来表示增益,如下所示。第一项来自于我们两次计算离散傅立叶变换的事实。我们将后者乘以在一半原始输入上计算离散傅立叶变换所花费的时间。在最后一步,需要N步来累加特定k的傅立叶变换。我们通过在最终产品中添加N来说明这一点。

请注意,我们是如何将傅里叶变换的计算时间缩短了 1/2 的。我们可以通过应用分治法来进一步改进算法,每次将计算成本减半。换句话说,我们可以继续分割问题大小,直到我们剩下两个一组,然后直接计算每一对的离散傅立叶变换。

只要N是 2 的幂,你可以分成两等份的最大次数由p = log(N)给出。

如果我们使用问题大小为N = 8的快速傅立叶变换算法,结果会是这样。请注意我们是如何划分p = log(8) = 3阶段的。

Python 代码

让我们看看如何使用 Python 从头开始实现快速傅立叶变换算法。首先,我们导入numpy库。

import numpy as np

接下来,我们定义一个函数来直接计算离散傅立叶变换。

def dft(x):
    x = np.asarray(x, dtype=float)
    N = x.shape[0]
    n = np.arange(N)
    k = n.reshape((N, 1))
    M = np.exp(-2j * np.pi * k * n / N)
    return np.dot(M, x)

通过将结果与从 numpy 的fft函数获得的结果进行比较,我们可以确保我们的实现是正确的。

x = np.random.random(1024)
np.allclose(dft(x), np.fft.fft(x))

我们可以清楚地看到,离散傅里叶变换函数比快速傅里叶变换算法慢几个数量级。

%timeit dft(x)
%timeit np.fft.fft(x)

就像我们之前看到的,快速傅立叶变换的工作原理是计算整个问题的小子集的离散傅立叶变换,然后组合结果。后者可以使用递归在代码中轻松完成。

def fft(x):
    x = np.asarray(x, dtype=float)
    N = x.shape[0]
    if N % 2 > 0:
        raise ValueError("must be a power of 2")
    elif N <= 2:
        return dft(x)
    else:
        X_even = fft(x[::2])
        X_odd = fft(x[1::2])
        terms = np.exp(-2j * np.pi * np.arange(N) / N)
        return np.concatenate([X_even + terms[:int(N/2)] * X_odd,
                               X_even + terms[int(N/2):] * X_odd])

同样,我们可以通过比较从 numpy 获得的结果来验证我们的实现是否正确。

x = np.random.random(1024)
np.allclose(fft(x), np.fft.fft(x))

FFT 算法比直接实现要快得多。然而,它仍然比 numpy 实现落后很多。其中一个原因是 numpy 实现使用矩阵运算来同时计算傅立叶变换。

%timeit dft(x)
%timeit fft(x)
%timeit np.fft.fft(x)

我们定义另一个函数来计算傅立叶变换。只是这一次,我们利用了向量运算而不是递归。

def fft_v(x):
    x = np.asarray(x, dtype=float)
    N = x.shape[0]
    if np.log2(N) % 1 > 0:
        raise ValueError("must be a power of 2")

    N_min = min(N, 2)

    n = np.arange(N_min)
    k = n[:, None]
    M = np.exp(-2j * np.pi * n * k / N_min)
    X = np.dot(M, x.reshape((N_min, -1)))while X.shape[0] < N:
        X_even = X[:, :int(X.shape[1] / 2)]
        X_odd = X[:, int(X.shape[1] / 2):]
        terms = np.exp(-1j * np.pi * np.arange(X.shape[0])
                        / X.shape[0])[:, None]
        X = np.vstack([X_even + terms * X_odd,
                       X_even - terms * X_odd])return X.ravel()

同样,通过与 numpy 库中的结果进行比较,我们可以确保获得正确的结果。

x = np.random.random(1024)
np.allclose(fft_v(x), np.fft.fft(x))

正如我们所看到的,使用向量运算的 FFT 实现比我们以前获得的要快得多。我们仍然没有接近 numpy 库计算傅立叶变换的速度。这是因为 numpy 的fft背后的 FFTPACK 算法是一个 Fortran 实现,经过了多年的调整和优化。如果您有兴趣了解更多,我建议您看看源代码。

%timeit fft(x)
%timeit fft_v(x)
%timeit np.fft.fft(x)

Ray Tune:一个 Python 库,可以在任意比例下快速调整超参数

原文:https://towardsdatascience.com/fast-hyperparameter-tuning-at-scale-d428223b081c?source=collection_archive---------2-----------------------

在不改变代码的情况下,将您的搜索从笔记本电脑扩展到数百台机器。 验出雷调

如果你曾经试图调整机器学习模型的超参数,你就会知道这可能是一个非常痛苦的过程。简单的方法很快变得耗时。

现在,您比以往任何时候都更需要尖端的超参数调整工具来跟上最先进的技术。

模型的进步越来越依赖于更新更好的超参数调整算法,如基于人口的训练 ( PBT )、HyperBand 和 ASHA。

Population-based Training improves DeepMind’s state-of-the-art algorithms on many domains by significant margins. Source: https://deepmind.com/blog/population-based-training-neural-networks/

这些算法提供了两个关键优势:

事实是,绝大多数研究人员和团队没有利用这样的算法。大多数现有的超参数搜索框架没有这些更新的优化算法。一旦达到一定规模,大多数现有的并行超参数搜索解决方案可能会很难使用——您需要为每次运行配置每台机器,并且通常需要管理单独的数据库。

实际上,实现和维护这些算法需要大量的时间和工程。

但是不需要这样。没有理由为什么您不能轻松地将超参数调整集成到您的机器学习项目中,在您的集群中的 8 个 GPU 上无缝地运行并行异步网格搜索,并在云上大规模利用基于群体的训练或任何贝叶斯优化算法。

在这篇博文中,我们将介绍 RayTune,这是一个强大的超参数优化库,旨在消除缩放实验执行和超参数搜索之间的摩擦。

Tune scales your training from a single machine to a large distributed cluster without changing your code.

RayTune 是一个强大的库,可以加速超参数优化。以下是一些核心功能:

  • RayTune 提供了现成的分布式异步优化。
  • RayTune 提供最先进的算法,包括(但不限于)【ASHA】【BOHB】基于群体的训练
  • 您可以将 RayTune 超参数搜索从单台机器扩展到大型分布式集群,而无需更改您的代码。
  • RayTune 集成了许多优化库,如 Ax/BotorchHyperOpt贝叶斯优化,使您能够透明地扩展它们。
  • RayTune 支持任何机器学习框架,包括 PyTorch、TensorFlow、XGBoost、LightGBM、scikit-learn 和 Keras。

除了 RayTune 的核心特性之外,研究人员和开发人员更喜欢 RayTune 而不是其他现有超参数调优框架的两个主要原因是:规模灵活性

Note for Search Algorithms: as of 8/12/2019, HpBandSter supports HyperBand, Random Search, and BOHB. KerasTuner supports Random Search, HyperBand, and Bayesian Optimization. Optuna supports Median (Percentile) Stopping, ASHA, Random Search, and Bayesian Optimization (TPE). HyperOpt supports Bayesian Optimization and Random Search. Tune supports PBT, BOHB, ASHA, HyperBand, Median Stopping, Random Search, Bayesian Optimization (TPE, etc), and numerous others due to library integrations.

调整简化了缩放。

通过添加不到 10 行 Python 代码,利用机器上的所有内核和 GPU 来执行并行异步超参数调优。

If you run into an ImportError, try installing from a snapshot wheel: https://ray.readthedocs.io/en/latest/installation.html#trying-snapshots-from-master

https://twitter.com/MarcCoru/status/1080596327006945281

使用另一个配置文件和 4 行代码,在云上启动大规模分布式超参数搜索,并自动关闭机器(我们将在下面向您展示如何做到这一点)。

借助 Tune 的内置容错、试验迁移和集群自动扩展,您可以安全地利用 spot(可抢占)实例,并将云成本降低高达 90%。

调谐是灵活的。

Tune 与实验管理工具无缝集成,如 MLFlowTensorBoard

Tune 为优化算法提供了一个灵活的接口,允许您轻松实现和扩展新的优化算法。

您可以使用 Tune 来利用和扩展许多最先进的搜索算法和库,如 HyperOpt (如下)和 Ax ,而无需修改任何模型训练代码。

使用 Tune 很简单!

现在让我们深入一个具体的例子,展示如何利用最先进的早期停止算法(ASHA)。我们将首先在您工作站上的所有内核上运行 Tune。然后,我们将用大约 10 行代码在云上扩展相同的实验。

我们将在这个例子中使用 PyTorch,但是我们也有可用的 Tensorflow 和 Keras 的例子。

Tune 作为射线的一部分安装。要运行这个示例,您需要安装以下软件:pip install ray torch torchvision.

你可以在这个博客这里下载完整版的博客。

我们首先运行一些导入:

header of tune_script.py

让我们用 PyTorch 写一个神经网络:

要开始使用 Tune,在 PyTorch training below 函数中添加一个简单的日志记录语句。

def train_mnist(config):
    model = ConvNet()
    train_loader, test_loader = get_data_loaders()
    optimizer = optim.SGD(
        model.parameters(), 
        lr=config["lr"], 
        momentum=config["momentum"])
    for i in range(10):
        train(model, optimizer, train_loader, torch.device("cpu"))
        acc = test(model, test_loader, torch.device("cpu"))
 **tune.track.log(mean_accuracy=acc)**
        if i % 5 == 0:
            # This saves the model to the trial directory
            torch.save(model, "./model.pth")

注意,在上面的训练脚本中有几个助手函数;你可以在这里看到它们的定义。

运行曲调

让我们运行 1 次试验,从一个均匀分布中随机抽样,以获得学习率和动量。

现在,你已经完成了你的第一次跑调!您可以通过指定 GPU 资源来轻松启用 GPU 使用— 参见文档了解更多详细信息。然后我们可以画出这次试验的表现。

并行执行和提前停止

Early stopping with ASHA.

让我们集成 ASHA,一个可扩展的提前停止算法(博文论文)。ASHA 终止了不太有希望的试验,并将更多的时间和资源分配给更有希望的试验。

使用num_samples在机器上的所有可用内核中并行搜索(额外的尝试将被排队)。

您可以使用与上例相同的数据帧绘图。运行后,如果安装了 Tensorboard,还可以使用 Tensorboard 可视化结果:tensorboard --logdir ~/ray_results

走向分布式

建立一个分布式超参数搜索通常工作量很大。Tune 和 Ray 让这一切变得天衣无缝。

使用简单的配置文件启动云

Launch a cluster and distribute hyperparameter search without changing your code

首先,我们将创建一个配置光线簇的 YAML 文件。作为射线的一部分,Tune 与射线簇发射器之间的交互非常干净。下面显示的相同命令将在 GCP、AWS 和本地私有集群上工作。除了一个头节点之外,我们还将使用 3 个工作节点,因此集群上总共应该有 32 个 vCPUs,这使我们能够并行评估 32 个超参数配置。

tune-default.yaml

把东西放在一起

要在光线簇中分布超参数搜索,需要将它附加到脚本的顶部:

考虑到计算的大幅增长,我们应该能够增加搜索空间和搜索空间中的样本数量:

你可以在这个博客(作为tune_script.py)下载完整版的剧本。

启动你的实验

要启动您的实验,您可以运行(假设到目前为止您的代码在一个文件tune_script.py中):

$ ray submit tune-default.yaml tune_script.py --start \
     --args=”localhost:6379”

这将在 AWS 上启动您的集群,将tune_script.py上传到 head 节点上,并运行python tune_script localhost:6379,这是 Ray 为支持分布式执行而打开的一个端口。

脚本的所有输出都会显示在您的控制台上。请注意,集群将在设置任何工作节点之前首先设置头节点,因此最初您可能只看到 4 个可用的 CPU。一段时间后,您可以看到 24 个试验被并行执行,其他试验将排队等候,一旦一个试验空闲,就立即执行。

要关闭集群,您可以运行:

$ ray down tune-default.yaml

你完了🎉!

了解更多信息:

Tune 还有许多其他特性,使研究人员和从业人员能够加速他们的开发。这篇博客文章中没有涉及的其他调整功能包括:

对于能够访问云的用户,Tune 和 Ray 提供了许多实用程序,可以实现在笔记本电脑上开发和在云上执行之间的无缝过渡。文档包括:

  • 在后台会话中运行实验
  • 向现有实验提交试验
  • 在 TensorBoard 中可视化分布式实验的所有结果。

Tune 旨在轻松扩展实验执行和超参数搜索。如果您有任何意见或建议,或者对 Tune 有兴趣,您可以联系我ray-dev 邮件列表

代码:https://github . com/ray-project/ray/tree/master/python/ray/tune
文档:http://ray.readthedocs.io/en/latest/tune.html

关于超参数调整的其他阅读材料,请查看 Neptune.ai 关于 Optuna vs HyperOpt 的博文!

感谢彭于晏、埃里克·梁、乔伊·冈萨雷斯、扬·斯托伊察、尤金·维尼斯基、丽莎·邓拉普、菲利普·莫里茨、、阿尔文·万、丹尼尔·罗斯柴尔德、布里詹·塔南杰扬、阿洛克·辛格(也许还有其他人?)来通读这篇博文的各种版本!

用于目标检测的快速 R-CNN

原文:https://towardsdatascience.com/fast-r-cnn-for-object-detection-a-technical-summary-a0ff94faa022?source=collection_archive---------6-----------------------

技术论文摘要

在试图理解使用深度学习的对象检测的关键概念和里程碑时,之前关于使用 CNN【1】的区域的文章强调了有史以来第一个具有深度学习主干的检测器网络中每个阶段的功能和训练细节。这篇文章深入挖掘了它的热门继任者 Fast R-CNN 的细节。[2]

随着 2014 年 R-CNN 论文发表以来我们看到的技术进步和计算能力的增加,我们现在很容易看到作者当时发现的 R-CNN 的明显缺陷:

  • 多阶段、昂贵的训练:网络所有阶段所需的独立训练过程——在对象提议上微调 CNN,学习 SVM 以对来自 CNN 的每个提议的特征向量进行分类,以及学习边界框回归器以微调对象提议(更多细节请参考区域和 CNN)在时间、计算和资源方面被证明是一种负担。例如,为了训练 SVM,我们需要将前一阶段的数千个可能的区域提议的特征写入磁盘。
  • 慢测试时间:给定这种多级流水线,使用简单的 VGG 网络作为主干的检测 CNN 需要 47 秒/图像。

2015 年发表的另一篇论文 SPP-Nets 或“空间金字塔池网络”[3]解决了问题的一部分。它的主要贡献是 a)使用多尺度图像特征池来提高分类和检测的网络性能,以及 b)使用任意大小的输入图像来训练 CNN,尽管使用了全连接(FC)层。在其检测管道中,它引入了一个重要的概念——从整个图像中仅提取一次单个特征图,并从该单个特征图中汇集该图像的任意大小的“区域提议”的特征。这使得他们的网络运行速度比 R-CNN 快几个数量级。他们没有像 R-CNN 那样对 2000 个地区的提案运行 CNN,而是只有一个转发通道,可以从中汇集所有 2000 个候选提案的特征。

快速 R-CNN 流水线

架构细节:

为了更好地理解快速 R-CNN 如何以及为什么提高 R-CNN 和 SPP 网络的效率和性能,让我们首先来看看它的架构。

  • 快速 R-CNN 由 CNN(通常在 ImageNet 分类任务上预先训练)组成,其最终池层由“ROI 池”层代替,其最终 FC 层由两个分支代替——一个(K + 1)类别 softmax 层分支和一个类别特定的边界框回归分支。

Figure 1: The Fast R-CNN pipeline

Figure 2: The ROI pooling layer as a special case of SPP layer

  • 整个图像被馈送到主干 CNN,并且从最后的卷积层获得特征。取决于所使用的主干 CNN,输出的特征地图比原始图像尺寸小得多。这取决于中枢 CNN 的步幅,在 VGG 中枢的情况下通常是 16。
  • 同时,对象提议窗口从类似选择性搜索的区域提议算法中获得[4]。正如在带 CNN 的区域中所解释的,物体提议是图像上的矩形区域,表示物体的存在。
  • 然后,属于该窗口的主干特征图的部分被馈入 ROI 汇集层。
  • ROI 池图层是只有一个金字塔等级的空间金字塔池(SPP)图层的特例。该层基本上将来自所选建议窗口(来自区域建议算法)的特征划分成大小为 h/Hw/W 的子窗口,并在这些子窗口的每一个中执行汇集操作。这产生了大小为(H x W)的固定大小输出特征,而与输入大小无关。选择 h 和 W,使输出与网络的第一个全连接层兼容。在 Fast R-CNN 论文中 H 和 W 的选择值是 7。像常规池一样,ROI 池在每个渠道中单独进行。
  • ROI 池层(N x 7 x 7 x 512,其中 N 是建议数量)的输出特征随后被送入后续 FC 层,以及 softmax 和 BB-regression 分支。softmax 分类分支产生属于 K 个类别和一个总括背景类别的每个 ROI 的概率值。BB 回归分支输出用于使来自区域提议算法的边界框更加精确。

损失:

softmax 层的分类分支给出了(K +1)个类别上每个 ROI 的概率 p = p₀,… pₖ.分类损失 L 𝒸ₗₛ (p,u) 由-log(pᵤ给出)是真实 u 类的对数损失

Equation 1: smooth L1 loss for BB regression

Equation 2: Joint loss for multi-task training

回归分支产生 4 个边界框回归偏移 tᵏᵢ,其中 i = x,y,w,h. (x,y) 代表左上角, wh 表示边界框的高度和宽度。类别 u 的真实边界框回归目标由 v ᵢ表示,其中当 u≥1 i = x,y,w,h忽略 u=0 的情况,因为背景类没有背景事实框。使用的回归损失是等式 1 中给出的平滑 L1 损失。每个 ROI 的联合多任务损失由两个损失的组合给出,如等式 2 所示。请注意,这里的快速 R-CNN 有一个组合学习方案,可以微调主干 CNN,并对边界框进行分类和回归。

培训:

  • 在训练期间,每个小批量由 N=2 个图像构成。小批量由来自每个图像的 64 个 ROI 组成。
  • 像 R-CNN 一样,25%的 ROI 是具有至少 0.5 IoU 的对象提议,具有前景类的地面真实边界框。对于特定的类,这些将是正的,并且将被标上适当的 u=1…K。
  • 其余感兴趣区域从 IoU 的基础边界框在[0.1,0.5]之间的提案中取样。这些 ROI 被标记为属于类别 u = 0(背景类别)。
  • 直到 ROI 合并层之前,整个图像都要通过 CNN。这样,来自同一图像的所有 ROI 在向前和向后通过 CNN 时共享计算和内存(在 ROI 合并层之前)。
  • 与 SPP 网络不同,采样策略(从非常少的图像中批量采样所有 ROI)允许整个模块(特征生成、分类和回归)一起训练。在 SPP 网络中,大多数输入 ROI 来自不同的图像,因此用于检测的训练过程仅在特征生成之后微调完全连接的层,因为在 SPP 层之前更新权重是不可行的(一些 ROI 在原始图像上可能具有非常大的感受野)。实际特征仍然来自为分类而训练的预训练网络。这限制了 SPP 网络的准确性。
  • 请注意,在 ROI 合并层之前,CNN 网络的批次大小非常小(批次大小= 2),但在接下来的 softmax 和回归层中,网络的批次大小要大得多(批次大小= 128)。

其他注意事项:

  • 通过 ROI pooling 层反向传播:对于每个小批量 ROI r,让 ROI pooling 输出单元 yᵣⱼ 作为其子窗口 R(r,j)中最大池的输出。然后,如果该位置*是为 yᵣⱼ.选择的 argmax,则在 R(r,j) 中的输入单元( xᵢ )中累积梯度*

  • 在快速 R-CNN 的多尺度流水线中,输入图像在训练时被调整为随机采样的尺寸,以引入尺度不变性。在测试时,每个图像以多个固定的比例被馈送到网络。对于每个 ROI,仅从这些尺度之一汇集特征,选择这些尺度使得缩放的候选窗口具有最接近 224×224 的像素数。然而,作者发现单尺度流水线的性能几乎一样好,而计算时间成本低得多。在单尺度方法中,训练和测试时的所有图像都被调整为 600(最短边),最长边的上限为 1000。
  • 可以用截短的 SVD 来压缩大的全连接层,以使网络更有效。这里,由 w 作为其权重矩阵参数化的层可以被因式分解,以通过将它分成两层(具有偏差的σₜvᵀ和 u)来减少参数计数,而它们之间没有非线性,其中 w ~ u σₜvᵀ.

结果

  • Fast R-CNN 在发布 VOC12、VOC10(含额外数据)和 VOC07 时实现了最先进的性能。
  • 快速 R-CNN 在测试时处理图像的速度比 R-CNN 快 45 倍,在训练时快 9 倍。它的训练速度比 SPP-Net 快 2.7 倍,运行测试映像的速度比 SPP-Net 快 7 倍。在进一步使用截断 SVD 时,网络的检测时间减少了 30%以上,mAP 仅下降 0.3。
  • 该论文的其他贡献表明,对于更大的网络,微调 conv 层(而不仅仅是 SPPnets 等 FC 层)对于实现更好的 mAP 非常重要。对于较小的网络,这种改进可能不会很大。
  • 多任务训练不仅更容易,而且还能提高表现,因为训练时任务会相互影响。因此,训练网络的不同阶段一起提高了它的共享代表力量(骨干 CNN)

这就是 Fast R-CNN 论文的技术总结。希望你喜欢(理解)!欢迎在下面的评论中讨论或更正。

阅读快速 R-CNN 的继任者和最先进的物体探测网络——更快 R-CNN 这里

参考资料:

[1] Girshick,Ross 等人,“用于精确对象检测和语义分割的丰富特征层次。”2014 年 IEEE 计算机视觉与模式识别会议(2014)
【2】Girshick,Ross。"快速 R-CNN "2015 年 IEEE 计算机视觉国际会议(ICCV)(2015)
【3】何,等,“用于视觉识别的深度卷积网络中的空间金字塔池”计算机科学讲义(2014)
[4] Uijlings,J. R. R .等,“物体识别的选择性搜索”国际计算机视觉杂志 104.2 (2013)

用 Turf.js 和命令行构建的快速静态 D3 地图

原文:https://towardsdatascience.com/fast-static-d3-maps-built-with-turf-js-and-the-command-line-5b7c72b7e775?source=collection_archive---------19-----------------------

将 Mike Bostock 的命令行制图教程与 Node.js 的灵活性结合起来

Estimated percent of undocumented residents in U.S. metro areas. Source: Pew Research Center

最近,我需要制作一些美国各州的泡沫地图,嵌入到《圣安东尼奥快报新闻》的报道中。我想使用 D3,但担心缓慢的资产加载和地图渲染,特别是因为 CMS 的限制,我需要单独iframe每个地图。

为了找到导出 D3 生成的 SVG 的方法,我进行了一些谷歌搜索。

这家伙做了个出口按钮,挺酷的。纽约时报SVG Crowbar ,提供类似的功能。然而,我发现的最令人兴奋的结果是这篇由四部分组成的文章,Mike Bostock 的

我强烈推荐 Bostock 的文章,本教程的大部分内容都来源于此。我对他的方法的唯一问题是,对我来说,在命令行上编写 d3 代码会变得复杂和混乱。这里有一个片段:

**(topo2geo tracts=- \
    < ca-topo.json \
    | ndjson-map -r d3 -r d3=d3-scale-chromatic 'z = d3.scaleThreshold().domain([1, 10, 50, 200, 500, 1000, 2000, 4000]).range(d3.schemeOrRd[9]), d.features.forEach(f => f.properties.fill = z(f.properties.density)), d' \
    | ndjson-split 'd.features'; \
topo2geo counties=- \
    < ca-topo.json \
    | ndjson-map 'd.properties = {"stroke": "#000", "stroke-opacity": 0.3}, d')\
  | geo2svg -n --stroke none -p 1 -w 960 -h 960 \
  > ca.svg**

我想找到一个利用 Node.js 的解决方案,允许我仍然从命令行使用 Bostock 的 d3-geo-projection ,但是将大部分制图操作和地图渲染的处理交给 JavaScript。这就是 Turf.js 的用武之地——一个用于编写和操作 GeoJSON 的令人惊叹的开源地理空间分析工具。

本教程一步一步地介绍如何使用 node 和命令行生成静态地图。我们将以 SVG 格式构建上述地图,该地图显示了美国 100 个最大的大都市区中无证居民比例最高的 50 个大都市区。

第一步:加载你的数据

地图需要的文件有:

  1. 2017 年皮尤研究中心研究的未记录居民数据。
  2. 定义美国城市区域位置的 shapefile。
  3. 定义美国各州轮廓的 shapefile。

启动命令行并创建一个工作目录:

**mkdir undocumented-residents-map
cd undocumented-residents-map**

创建一个 bash 文件,我们将使用它作为我们的主要构建脚本:

touch buildmap.sh

原始皮尤数据在处可用。我将其简化为两列,并将其存储在 data.world。shapefiles 由人口普查局提供。我们可以通过浏览器下载所有这些文件,但以编程方式进行更有趣。

在您最喜欢的文本编辑器中打开buildmap.sh,写下如下内容:

上面有条件地下载并解压所有三个文件到我们的项目文件夹的根目录。

现在我们有了我们的资产,我们可以开始构建我们的地图。

第二步:将 Shapefiles 转换为 GeoJSON

我们将使用 NPM 包来实现这一点,所以现在是设置package.json的好时机。

运行npm init并按照提示生成 JSON。接下来,您需要将以下内容添加到生成的package.json中:

**"scripts": { "buildmap": "sh buildmap.sh" }**

这将允许我们在当前 NPM 一揽子计划的背景下管理buildmap.sh

安装 Mike Bostock 的流 shapefile 解析器(npm i shapefile -D),并在buildmap.sh的底部添加以下代码行,将我们的 shape file 转换为 GeoJSON:

这两个 GeoJSON 文件用于以下目的:

  • metroareas.geojson:我们将使用地理数据将未记录的常驻 CSV 数据绑定到地图上。
  • states.geojson:我们将用来绘制地图背景的美国州轮廓。

最终,这两个文件将与 CSV 文件合并成一个主 GeoJSON 文件,我们将把它转换成 SVG。但是首先…

第三步:将位置数据添加到我们未记录的居民数据点

在这一步中,我们将使用 Node 将 CSV 中的数据(美国大都市区未登记居民的百分比)转换为 GeoJSON 文件中以各自大都市区为中心的缩放圆。

如果你是 GeoJSON 新手,我推荐你从维基百科页面开始。

我们输出的 GeoJSON 将包括 CSV 中每个大都市区域的单个要素-一个点。这些特征的坐标将从metroarea.geojson中获得,每个点的“属性”对象将包括一个pointRadius属性,用于相对于在该城市区域中发现的无证居民的相应百分比来缩放点的大小。

下面是我们将在 JS 文件中采取的步骤的概述:

  1. metroareas.geojsonmetro-area-percent-undocumented.csv转换为 JS 对象
  2. 过滤metroareas.geojson,只包括我们的 CSV 中也存在的城市区域
  3. 使用 Turf.js 找到每个都会区多边形的中心,并将我们的点固定到中心的坐标上。
  4. 在每个特性的“properties”对象中为我们的点添加样式(颜色等),包括我们的pointRadius属性,它是使用 d3 缩放的。
  5. 将要素集导出为新的 GeoJSON 文件。

首先,创建文件:

**touch mapMetroPathsToCircles.js**

打开mapMetroPathsToCircles.js并编写以下代码,完成上述 5 个步骤,并在注释中详细说明每个步骤:

在将它添加到 bash 文件之前,我们需要安装所需的依赖项:

**npm i @turf/turf d3 csv-parse@3.1.3 -D**

然后,在buildmap.sh中我们可以添加:

**node mapMetroPathsToCircles.js # Create circles.geojson**

第四步:将州轮廓添加到我们的城市区域 GeoJSON 中

现在我们有了圆形的地理图,但是没有背景图。因此,我们将合并到美国的州大纲中。

在命令行上:

**touch mapAndMergeStates.js**

用以下内容填充文件(注释中的详细信息):

最后把这个加到buildmap.sh:

**node mapAndMergeStates.js # merge in states, output topo.geojson**

步骤五: 将 GeoJSON 转换为 SVG

我们现在有了主 GeoJSON 文件(topo.geojson),可以将其转换为 SVG。

然而,我们应该首先对我们的数据应用地理投影,这样 SVG 就可以按比例生成。为此,我们将使用 Bostock 的地质项目

我们还希望分割 GeoJSON,以便每个要素都在一行上。因此,当我们将 GeoJSON 转换为 SVG 时,每个要素将在 XML 中占据一个新行。当我们在本教程末尾使用命令行拼接图例时,这将非常有用。

安装:

**npm i d3-geo-projection ndjson-cli -D**

buildmap.sh的顶部,将您需要的投影(在我们的例子中是美国艾伯斯)和 SVG 输出的高度和宽度放入 bash 变量:

**# USA albers
PROJECTION='d3.geoAlbersUsa()'# Display height and width
WIDTH=960
HEIGHT=600**

然后在buildmap.sh底部添加:

注意第 7 行,我们运行 sed 命令从 SVG 中提取最后一行:因为我们使用了ndjson-split将 GeoJSON 转换为换行符分隔的 JSON 文件,所以我们的 SVG XML 也是换行符分隔的。因此,使用 sed 命令,我们去掉了 SVG 的右括号。当我们构建我们的 legend SVG 时,我们也将去掉它的左括号,并将两者连接到我们的最终输出中。

你可以在这里了解更多关于换行符分隔的 JSON 及其用法

第六步:给我们的地图添加一个图例

我们快完成了。最后一步是使用 node 和 D3 生成 SVG 格式的图例,然后将所述图例合并到我们的地图中。

我们将使用 Brad Oyler 的 D3 节点库在 D3 手写我们的图例(并导出 SVG)。我们还将使用标志传入我们的 SVG 高度和宽度变量,并用minimit库解析所述标志。

安装:

**npm i d3-node minimist -D**

在命令行上创建我们的 JS 文件:

**touch buildLegend.js**

然后写下以下内容(细节再次在评论中):

现在,在buildmap.sh中,我们可以添加:

上面运行buildLegend.js,传入我们的 SVG 宽度和高度。然后,它使用tail将图例和地图连接到我们的最终输出topo.svg

最后两行只是清理目录,删除不必要的文件。

我们完事了。!!在命令行上运行:

**npm run buildmap**

如果一切顺利,这应该会生成topo.svg,看起来有点像这个

作为参考,这里有一个回购包括所有这些文件的最终形式和一个直接链接到所述回购中的buildmap.sh

Fastai 与🤗变形金刚(伯特、罗伯塔、XLNet、XLM、迪沃伯特)

原文:https://towardsdatascience.com/fastai-with-transformers-bert-roberta-xlnet-xlm-distilbert-4f41ee18ecb2?source=collection_archive---------3-----------------------

使用 Fastai 实现最先进的 NLP 模型进行情感分析的教程

在【2018 年初,杰瑞米·霍华德(fast . ai 的联合创始人)和塞巴斯蒂安·鲁德推出了通用语言模型微调文本分类 (ULMFiT)方法。ULMFiT 是第一个应用于 NLP 的迁移学习方法。因此,除了显著优于许多最先进的任务之外,它还允许在只有 100 个标记示例的情况下,匹配相当于基于 100 倍以上数据训练的模型的性能。

ULMFiT requires less data than previous approaches. (Howard and Ruder, ACL 2018)

我第一次听说 ULMFiT 是在杰瑞米·霍华德的一次 fast.ai 课程中。他演示了如何通过几行代码轻松实现完整的 ULMFiT 方法——多亏了fastai库。在他的演示中,他使用了在 Wikitext-103 上预先训练的 AWD-LSTM 神经网络,并迅速获得了最先进的结果。他还解释了关键技术——也在 ULMFiT 中演示过——来微调模型,如区分学习率逐步解冻倾斜三角形学习率

自从 ULMFiT 的引入,迁移学习在 NLP 中变得非常流行,然而谷歌(BERT,Transformer-XL,XLNet),脸书(RoBERTa,XLM)甚至 OpenAI (GPT,GPT-2)都开始在非常大的语料库上预先训练他们自己的模型。这一次,他们没有使用 AWD-LSTM 神经网络,而是使用了基于变压器的更强大的架构(参见注意力是你所需要的全部)。

虽然这些模型很强大,但是fastai并没有将它们全部集成。幸好抱紧脸🤗创建了众所周知的[transformers](https://github.com/huggingface/transformers) 。这个库以前被称为pytorch-transformerspytorch-pretrained-bert,它汇集了超过 40 个最先进的预训练 NLP 模型(伯特、GPT-2、罗伯塔、CTRL……)。该实现提供了有趣的附加工具,如 tokenizer、optimizer 或 scheduler。

[## 拥抱脸/变形金刚

用于 TensorFlow 2.0 和 PyTorch 的最先进的自然语言处理🤗变形金刚(以前称为…

github.com](https://github.com/huggingface/transformers)

transformers库可以是自给自足的,但是将它并入fastai库提供了与强大的fastai工具兼容的更简单的实现,如区分学习速率逐步解冻倾斜三角形学习速率。这里的要点是允许任何人——专家或非专家——轻松获得最先进的结果,并“让 NLP 再次变得不酷”。

值得注意的是,在fastai中集成拥抱脸transformers库已经在:

虽然这些文章质量很高,但是它们的演示的某些部分不再与transformers的最新版本兼容。

🛠集成变压器和 fastai 进行多类分类

在开始实现之前,请注意可以通过多种方式将transformers集成到fastai中。为此,我带来了——我认为是——最通用、最灵活的解决方案。更准确地说,我试图对两个库进行最小的修改,同时使它们与最大数量的 transformer 架构兼容。但是,如果你找到一个聪明的方法来实现这个,请在评论区告诉我们!

本教程的 Jupiter 笔记本版本可以在这个 Kaggle 内核上获得。

库安装

首先,您需要安装fastaitransformers库。为此,只需遵循此处和此处的说明。

在这个演示中,我使用了已经安装了fastai库的 Kaggle。所以我只是用命令安装了transformers:

pip install transformers

用于本演示的库的版本是fastai 1.0.58transformers 2.1.1

🎬示例任务

选择的任务是对电影评论进行多类文本分类。

本文的数据集和相应的笔记本可以在 Kaggle 上找到。

[## 电影评论的情感分析

下载数千个项目的开放数据集+在一个平台上共享项目。探索热门话题,如政府…

www.kaggle.com](https://www.kaggle.com/c/sentiment-analysis-on-movie-reviews/data)

对于每个文本电影评论,模型必须预测情感的标签。我们根据分类精度评估模型的输出。情感标签是:

0→负
1→有点负
2→空档
3→有点正
4→正

使用pandas将数据加载到DataFrame中。

主变压器等级

transformers中,每个模型架构都与 3 种主要类型的类相关联:

  • 一个模型类,用于加载/存储特定的预训练模型。
  • 一个记号赋予器类,用于预处理数据并使其与特定模型兼容。
  • 一个配置类,用于加载/存储特定型号的配置。

例如,如果您想使用 BERT 架构进行文本分类,您可以使用[BertForSequenceClassification](https://huggingface.co/transformers/model_doc/bert.html#bertforsequenceclassification)作为模型类,使用[BertTokenizer](https://huggingface.co/transformers/model_doc/bert.html#berttokenizer)作为标记器类,使用[BertConfig](https://huggingface.co/transformers/model_doc/bert.html#bertconfig)作为配置类

稍后,您将看到那些类共享一个公共的类方法from_pretrained(pretrained_model_name, ...)。在我们的例子中,参数pretrained_model_name是一个字符串,带有要加载的预训练模型/标记器/配置的快捷方式名称,例如bert-base-uncased。我们可以在transformers文档这里找到所有的快捷方式名称。

为了在类之间轻松切换——每个类都与一个特定的模型类型相关——我创建了一个字典,它允许通过指定正确的模型类型名来加载正确的类。

值得注意的是,在这种情况下,我们只对一个多类文本分类任务使用transformers库。出于这个原因,本教程只集成了实现了序列分类模型的 transformer 架构。这些模型类型是:

  • 伯特(来自谷歌)
  • XLNet(来自谷歌/CMU)
  • XLM(来自脸书)
  • 罗伯塔(来自脸书)
  • 蒸馏啤酒(来自拥抱脸)

然而,如果你想更进一步——通过实现另一种类型的模型或 NLP 任务——本教程仍然是一个很好的开端。

数据预处理

为了匹配预训练,我们必须以特定的格式格式化模型输入序列。
为此,您必须首先进行标记,然后文本进行正确的数字化。
这里的困难在于,我们将微调的每个预训练模型需要完全相同的特定预处理,即记号化 & 数值化,而不是在预训练部分使用的预处理。
幸运的是,来自transformers记号赋予器类提供了正确的预处理工具,对应于每个预训练的模型。

fastai库中,数据预处理是在DataBunch创建期间自动完成的。
正如您将在DataBunch实现部分看到的那样,记号赋予器数值化器在处理器参数中以如下格式传递:

processor = [TokenizeProcessor(tokenizer=tokenizer,…), NumericalizeProcessor(vocab=vocab,…)]

让我们首先分析如何将transformers记号赋予器集成到TokenizeProcessor函数中。

自定义标记器

这一部分可能有点混乱,因为许多类被包装在一起,并且具有相似的名称。
继续,如果我们仔细观察fastai的实现,我们会注意到:

  1. [TokenizeProcessor](https://docs.fast.ai/text.data.html#TokenizeProcessor) 对象Tokenizer对象作为tokenizer参数。
  2. [Tokenizer](https://docs.fast.ai/text.transform.html#Tokenizer) 对象将一个BaseTokenizer对象作为tok_func参数。
  3. [BaseTokenizer](https://docs.fast.ai/text.transform.html#BaseTokenizer) 对象实现函数tokenizer(t:str) → List[str],该函数获取文本t并返回其令牌列表。

因此,我们可以简单地创建一个继承自BaseTokenizer的新类TransformersBaseTokenizer,并重写一个新的tokenizer函数。

在这个实现中,要注意三件事:

  1. 由于我们没有使用 RNN,我们必须将序列长度限制为模型输入大小。
  2. 大多数模型需要在序列的开头和结尾放置特殊的标记。
  3. 像 RoBERTa 这样的一些模型需要一个空格来开始输入字符串。对于那些模型,应该在add_prefix_space设置为True的情况下调用编码方法。

下面,您可以找到本教程中使用的 5 种模型类型的每个预处理要求的简历。您也可以在每个模型部分的拥抱脸文档中找到该信息。

伯特:[CLS] +代币+ [SEP] +填充

蒸馏瓶:[CLS] +代币+ [SEP] +填充

罗伯塔:[CLS] +前缀空格+记号+ [SEP] +填充

XLM: [CLS] +代币+ [SEP] +填充

XLNet:填充+令牌+ [SEP] + [CLS]

值得注意的是,在这部分实现中,我们不添加填充。
我们将在后面看到,fastai会在DataBunch创建期间自动管理它。

自定义数值化器

fastai中,[NumericalizeProcessor](https://docs.fast.ai/text.data.html#NumericalizeProcessor) 对象将一个[Vocab](https://docs.fast.ai/text.transform.html#Vocab) 对象作为vocab参数。
通过这种分析,我建议采用两种方法来适应fastai 数控化器:

  1. 你可以像在 Dev Sharma 的文章中描述的那样(第 1 节。设置记号赋予器,检索记号列表并创建一个Vocab对象。
  2. 创建一个继承自Vocab的新类TransformersVocab,并覆盖numericalizetextify函数。

即使第一个解决方案看起来更简单,Transformers也没有为所有模型提供一个直接的方法来检索他的令牌列表。
因此,我实现了第二个解决方案,它针对每个模型类型运行。
包括分别使用numericalizetextify中的convert_tokens_to_idsconvert_ids_to_tokens功能。

注意:函数__gestate____setstate__允许函数导出load_learnerTranformersVocab一起正确工作。

定制处理器

既然我们已经有了自定义的记号赋予器数值化器,我们就可以创建自定义的处理器。注意我们正在传递include_bos = Falseinclude_eos = False选项。这是因为默认情况下fastai添加了自己的特殊标记,这会干扰我们的自定义标记器添加的[CLS][SEP]标记。

设置数据发送

对于 DataBunch 创建,您必须注意将参数processor设置为我们新的定制处理器transformer_processor,并正确管理填充。

正如拥抱脸文档中提到的,伯特、罗伯塔、XLM 和迪尔伯特是具有绝对位置嵌入的模型,所以通常建议在右侧填充输入,而不是在左侧。关于 XLNET,它是一个具有相对位置嵌入的模型,因此,您可以在右侧或左侧填充输入。

定制模型

正如这里提到的一样,每个模型的 forward 方法总是输出一个tuple,其中包含各种元素,具体取决于模型和配置参数。在我们的例子中,我们只对访问逻辑感兴趣。
访问它们的一种方法是创建自定义模型。

为了使我们的转换器适应多类分类,在加载预训练模型之前,我们需要精确标签的数量。为此,您可以修改 config 实例,或者像 Keita Kurita 的文章中那样修改num_labels参数。

学员:自定义优化器/自定义指标

pytorch-transformers中,拥抱脸实现了两个特定的优化器——BertAdam 和 OpenAIAdam——它们已经被一个 AdamW 优化器所取代。
这个优化器匹配 Pytorch Adam 优化器 Api,因此,将它集成到fastai中变得很简单。
注意,为了重现贝塔姆的特定行为,你必须设置correct_bias = False

有区别的微调和逐步解冻

为了使用区别学习率G 随机解冻fastai提供了一个工具,允许将结构模型“分割”成组。fastai 文档中描述了执行“分割”的指令,此处为

不幸的是,模型架构差异太大,无法创建一个独特的通用函数,以方便的方式“拆分”所有的模型类型。因此,您将不得不为每个不同的模型架构实现一个定制的“分割”。

例如,如果我们使用 DistilBERT 模型,我们通过制作print(learner.model)来观察他的架构。我们可以决定将模型分成 8 块:

  • 1 嵌入
  • 6 变压器
  • 1 个分类器

在这种情况下,我们可以这样分割我们的模型:

请注意,我没有发现任何文件研究了区别学习率逐步解冻甚至倾斜三角形学习率对变压器架构的影响。因此,使用这些工具并不能保证更好的结果。如果你发现了任何有趣的文档,请在评论中告诉我们。

火车

现在我们终于可以使用所有的fastai内置特性来训练我们的模型了。像 ULMFiT 方法一样,我们将使用倾斜三角学习率、区分学习率逐渐解冻模型。

因此,我们首先冻结除分类器之外的所有组,使用:

learner.freeze_to(-1)

对于倾斜三角形学习率,您必须使用函数fit_one_cycle。更多信息,请查看 fastai 文档这里

为了使用我们的fit_one_cycle,我们需要一个最佳的学习速率。我们可以用一个学习率查找器来找到这个学习率,这个学习率查找器可以用[lr_find](https://docs.fast.ai/callbacks.lr_finder.html#callbacks.lr_finder)来调用。我们的图表看起来像这样:

我们将选择一个比最小值稍早的值,此时损失仍会增加。这里 2x 10–3 似乎是一个不错的值。

learner.fit_one_cycle(1,max_lr=2e-03,moms=(0.8,0.7))

损失图看起来像这样:

然后,我们解冻第二个组,并重复这些操作,直到所有组都解冻。如果您想使用区别学习率,您可以如下使用slice:

要解冻所有组,使用learner.unfreeze()

创建预测

既然我们已经训练了模型,我们想要从测试数据集生成预测。

Keita Kurita文章中所述,由于函数get_preds默认情况下不按顺序返回元素,因此您必须将元素按正确的顺序重新排序。

Kaggle 示例中,在没有过多使用参数的情况下,我们获得了 0.70059 的公开分数,这使我们在排行榜上排名第 5!

📋结论

在本文中,我解释了如何将transformers库与心爱的fastai库结合起来。它的目的是让你明白在哪里寻找和修改这两个库,使它们一起工作。很可能,它允许你使用倾斜三角形学习率区别学习率和甚至逐步解冻。因此,甚至无需调整参数,您就可以快速获得最先进的结果。

今年,变形金刚成了 NLP 的必备工具。正因为如此,我认为预先训练的变形金刚架构将很快被整合到fastai的未来版本中。同时,本教程是一个很好的开端。

我希望您喜欢这第一篇文章,并发现它很有用。
感谢您的阅读,不要犹豫,请留下问题或建议。

我会继续在 NLP 上写文章,敬请期待!

📑参考

[1]拥抱脸,变形金刚 GitHub(2019 年 11 月),https://github.com/huggingface/transformers

[2] Fast.ai,Fastai 文档(2019 年 11 月),https://docs.fast.ai/text.html

[3]https://arxiv.org/abs/1801.06146杰瑞米·霍华德&塞巴斯蒂安·鲁德,文本分类通用语言模型微调(2018 年 5 月)

[4] Keita Kurita,用快速人工智能微调 BERT 的教程(2019 年 5 月)

[5] Dev Sharma使用 RoBERTa 和 Fastai 进行 NLP(2019 年 9 月)

更快的 A/B 测试🚀—用数字表示

原文:https://towardsdatascience.com/faster-a-b-testing-in-numbers-ee82e1af0885?source=collection_archive---------47-----------------------

除了传统假设检验(也称为 frequentist)方法之外的其他方法正变得越来越普遍——我发现贝叶斯方法是一个有意义的改进。

传统的方法🎓

典型的 A/B 测试意味着假设测试——这意味着我们想要“证明 A & B 是显著不同的”。我们可以将我们的测试结果插入到现有的 A/B 测试评估器中,例如来自 Evan Miller 的评估器,并得到结果。

在大多数这些工具中,部分结果是“p”值。通常,如果小于 5%,结果被认为是显著的。有趣的是,许多人不知道“p”值的实际含义,并且经常被误解。这是问题的一部分,因为如果你不能解读测试结果,就很难得出合理的结论。

“p”值代表 A 和 B 来自同一分布的可能性

为什么要改变?😱

在我看来,常规方法有两个重大问题:

  1. 由于理解“p”值很难,人们经常得出错误的结论
  2. 传统的方法需要非常大的样本量:用典型的参数来显示 1%成功率的转换漏斗上 10%的提升,你必须让 320 000 个用户看到你的测试!

贝氏救援队。⛑

为了评估其他工具,我构建了一个模拟环境来测试各种框架及其参数。下面我将分享我的高级发现,并推荐一些你可以使用的参数,我发现这些参数非常健壮。

场景:假设你计划在接下来的几周内一个接一个地运行 10 个 A/B 测试,如果一个测试被接受,你将为所有未来的用户推出这个特性。不是所有的 A/B 测试都是肯定的,有时你不知道用户在寻找什么——我假设你测试的是更好或更差的版本。

在每个模拟中,我综合了每个测试的改进——因此,如果 10 个测试中有 3 个测试获得了+3%、+7%、+2%的改进,我会计算:

(1.03 * 1.07 * 1.02)-1 = 0.124.. = 12.4%

我重复了 1000 次以上的模拟,然后平均出复合结果:

正如你所看到的,所有的框架都接受积极的测试,并允许你向所有用户推出改进。但是你也可以看到, Bayes(完整持续时间)版本产生了最大的提升(给定相同的精确 A/B 测试结果)。这怎么可能呢?贝叶斯伊恩方法接受小结果的负担也较低——因为来自多个测试的小结果可以合成大结果,所以贝叶斯伊恩方法胜出。您还可以看到,如果您运行 A/B 测试的时间较短,那么框架将无法做出如同拥有所有数据一样好的决策。

但是贝叶斯方法有什么缺点吗?不幸的是,这一点经常被忽略,但是要回答这个问题,我们不应该看平均模拟结果,而应该看复合结果的分布。

百分位数显示,在一小部分模拟中(~1%)测试框架的错误决策会导致整体性能下降。虽然在我们的模拟中我们可以看到这一事实,但在现实世界中,您不会注意到您正在损害您的业务!相比之下,你可以看到非常保守的传统(Frequentist)方法没有这个问题。

好吧,那么哪个更好呢?🤔

总的来说,我更喜欢贝叶斯伊恩方法——只要你不是超级倒霉,你就可以期待用这种方法得到更好的结果,但在这种情况下,你也不会比传统方法失去太多。

您还必须考虑时间方面——为您的决策框架提供更少的时间(和数据)将会导致更糟糕的决策……

除了:如果更短的测试持续时间可以让你运行更多的测试,这是一个值得的权衡。

比方说,如果你在三分之一的时间内运行你的测试,它允许你在相同的时间内运行两倍的测试。这对结果有什么影响?

正如你所看到的,在 Frequentist 方法中用更短的测试持续时间运行更多的测试并没有太大的帮助,但是在 Bayes ian 的情况下,我们减少了可能的负面影响并显著提高了正面影响。

让我们在 0 时间内运行无限量的测试🦁

我的模拟显示,在 Bayes ian 框架中,只要运行同等数量的测试,就有可能将测试时间越来越短(例如,将时间减少 10 倍,运行 10 倍的测试)。实际上,这有几个问题:

  1. 如果您正在运行的测试的质量因为频率的增加而下降——所有的赌注都将被取消
  2. 通常你不能运行尽可能多的测试,因为有人需要构建它们——在这种情况下,为测试分配尽可能多的时间以做出最好的决策是有意义的
  3. 一般来说,你应该运行你的测试至少一个星期——用户在周末的行为可能与工作日有很大的不同,你的测试应该能够考虑这些情况!

让我们用贝叶斯分析一些测试🐳

我建议你使用以下参数来保护你的缺点:

  1. 获胜的可能性应该大于 66%
  2. 利润应大于 1%

我将在下面提供 python 代码,但是您也可以在大多数其他编程语言中这样做!该代码只是近似的,运行速度相当快。

*# to install dependencies: pip install numpy scipy* **import** numpy **as** _numpy
**from** scipy.stats.distributions **import** beta **as** _beta **def** get_likelihood_of_win(test_result, margin=0.01):
    success = 0
    total = 0 ref_pos = test_result[**'A'**][**'success_cnt'**]
    ref_neg = test_result[**'A'**][**'fail_cnt'**]
    test_pos = test_result[**'B'**][**'success_cnt'**]
    test_neg = test_result[**'B'**][**'fail_cnt'**] conv_rate_base = max(
        (ref_pos/ ref_pos + ref_neg),
        (test_pos/ test_pos + test_neg)
    ) **for** x **in** _numpy.linspace(0, min(conv_rate_base * 2, 1), 100):
        prob = _beta.pdf(x=x, a=ref_pos, b=ref_neg)
        prob_test_wins = 1 - _beta.cdf(
            x=x * (1 + margin), 
            a=test_pos, b=test_neg
        )
        success += prob_test_wins * prob
        total += prob **return** success / total **if** __name__ == **'__main__'**:
    test_result = {
        **'A'**: {
            **'success_cnt'**: 10,
            **'fail_cnt'**: 100,
        },
        **'B'**: {
            **'success_cnt'**: 18,
            **'fail_cnt'**: 100,
        }
    }
    likelihood_of_win = get_likelihood_of_win(
        test_result=test_result, margin=0.01
    )
    accepted = likelihood_of_win > 0.66
    print(accepted, round(likelihood_of_win, 2))

更快的 ALS 推荐与特征提取(和布偶!)

原文:https://towardsdatascience.com/faster-als-recommendations-with-feature-extraction-and-muppets-78402af9c011?source=collection_archive---------24-----------------------

CC

在最近的另一篇文章中,我讲述了我是如何为非漫画读者创建一个漫画推荐系统到漫画。我回顾了我是如何做到这一点的一些步骤,包括创建一个交替最小二乘法(ALS)模型,该模型将用户和项目分解成他们自己的矩阵,可以这样可视化:

当我们将这两个值分开后,可以将它们相乘,得到稀疏矩阵中原始值的最接近的组合,但也可以填入我们没有的值。现在,每个用户和物品都有自己独特的功能,这些功能以数字矩阵值的形式表现出来,我们用这些值来实现这些功能。

🐸十.🧀

我的推荐系统使用大量的项目和用户,所以为了帮助说明这一点,我将看一下我自己想象的一个非常简化的版本。我在怪异的例子中学得最好,所以它们会在我的大脑中停留更长的时间。所以,为了你的享受,让我们想象布偶需要一个奶酪的推荐系统。你把他们中的一些人聚集在一起,得到他们从 1 到 5 的分数,然后创建一个数据框,看起来像这样:

使用 ALS,我们可以将评级矩阵分解为代表用户和项目的矩阵,每个矩阵都通过潜在特征来表达。假设我们决定布偶奶酪推荐器有十个潜在特征,我们将得到两个特征矩阵,如下所示:

使用矩阵乘法,当我们重新组合这些矩阵时,我们会得到一个新的矩阵,如下所示:

你现在可以看到的一件大事是,这个过程已经填充了任何原本缺少评级的单元格。我们使用用户和项目的潜在特征来估计他们对项目的评价。如果我们向 Gonzo 推荐一种奶酪,知道他已经对 Swiss & Roquefort 进行了评级,我们可以看到 Gouda 是他没有吃过的项目中评级最高的,这是一个可靠的选择!

特征提取的魔力

我们做到了,我们可以向布偶推荐奶酪。但是…我们如何向牙齿医生推荐一种奶酪呢?滑板车?老鼠里索!?我们怎么会忘记他呢!我们需要帮助获得更多的布偶和最适合它们的奶酪。

一位新用户向我们提出了所谓的冷启动问题。我们必须弄清楚如何向没有信息的用户推荐(或者如果我们想添加新的奶酪,我们会遇到类似的问题)。我们可以只推荐评价最高的,也许是评价最高的,有许多不同的方法来尝试和解决这个问题。回到我最初的漫画推荐用例,我决定从他们那里获得一些用户评级,并从那里开始预测,但是我如何使用新的评级呢?

正如您在 ALS 中看到的,要有效地获得推荐,您需要有完整的矩阵,创建模型,然后找到最佳匹配。这样做的问题很简单:计算量很大,速度很慢。如果每次需要推荐时都需要重新创建模型,那么效率会非常低。

为了在不建立新模型的情况下快速获得结果,我们可以使用从项目中提取的特征以及新用户的一些推荐,并简单地进行矩阵乘法来获得分数。这听起来可能很直接,但让我详细介绍一下这个概念:

在 Python 中,让我展示一下可能的样子。首先,让我们假设牙齿博士决定加入这个布偶奶酪聚会,并将高达奶酪和切达奶酪都评为 5 分(他是一个相当快乐的家伙)。我们把他的分数放在他们自己的 numpy 数组中:

dr_teeth = np.array([[5],[5]])
dr_teeth.shape # (2, 1)

接下来,我们获取 gouda 和 cheddar 的项目特征,并将它们放在一个单独的数组中。它们是上图中要素数据框中的前两个项目,因此看起来会像这样:

select_cheese = items.iloc[0:2, 2:12].to_numpy()
select_cheese.shape # (2, 10)

接下来,我们将使用最小二乘法的线性代数解来求解牙齿博士的用户特征:

dr_teeth_features = np.linalg.lstsq(select_cheese, dr_teeth, rcond=None)
dr_teeth_features = dr_teeth_features[0].reshape((10,))
dr_teeth_features
###
array([1.94412615, 0\.        , 0\.        , 1.49873073, 0.20873594,
       0\.        , 0\.        , 0.42289857, 0.36965752, 0.19383103])

我们做到了!现在,我们有了用户功能,就像所有其他现有用户一样。然后,我们可以使用这些和所有项目的矩阵乘法来返回牙齿医生的全套分数:

dr_teeth_scores = [np.dot(dr_teeth_features, row) for row in items.iloc[:, 2:12].to_numpy()]for row in zip(items['cheese'], dr_teeth_scores):
    print(row)###
('gouda', 4.9999999999999964)
('cheddar', 5.000000000000001)
('brie', 4.270218133363084)
('swiss', 2.505755675697055)
('roquefort', 3.5066194383500755)

看起来对他来说下一个最好的奶酪是布里干酪。我们现在有一种快速的方法来近似新用户的结果,而不必创建一个全新的模型。

解开这一想法最重要的部分是知道你可以将特征提取应用到许多涉及数据科学的操作中。特别是当你开始使用大型预训练模型进行图像分类时,我们可以类似地使用它们的特征,而不需要运行大规模的神经网络来在更短的时间内获得结果的近似。

更快的数据科学

原文:https://towardsdatascience.com/faster-data-science-7ce9aa8d0b28?source=collection_archive---------29-----------------------

商业领袖经常问如何加速数据科学项目。众所周知,数据科学家花费 80%的时间在数据争论上。缩短这一时间可以加快数据科学项目的周转,并让数据科学家将更多的时间花在其他人无法完成的高价值活动上。随着数据科学团队规模的扩大,经济效益和工作效率会快速增长。这篇文章为数据工程的适度投资提供了一个案例,通过满足对较小的代表性数据集的需求,大大减少了花费在交互式数据探索上的时间。

减少分析查询的周转时间

减少花费在数据探索和代码开发上的时间的关键是最大限度地减少获得关于数据的基本问题的答案所花费的时间。当分析查询的典型运行时间从几个小时到一个小时到 10 分钟到不到一分钟到不到 10 秒钟时,开发过程的动态会发生巨大的变化。一旦查询响应时间达到几秒钟的范围,就可以提出许多问题来测试不同的假设,并最终在更短的时间内获得更好的结果。

大数据太大了

在研究项目和数据管道的开发过程中,数据科学家必须多次运行他们的查询,对它们进行提炼和改进,以便以他们想要的格式获得他们需要的数据。然而,处理大数据非常耗时,因为无论您使用的是 Hadoop 还是现代分布式数据仓库(如 Snowflake、Redshift 或 Vertica ),处理大型数据集都需要很长时间。使用共享计算资源的模式常常会加剧这个问题,在这种模式下,交互式查询会与较大的批处理进程竞争。

数据科学家对推进研究所需的时间感到沮丧,他们发明了避免长时间等待的捷径,但往往牺牲了分析结果的准确性或适用性。例如,处理一个小时的日志数据使查询时间变得可以忍受,从而可以取得进展。然而,这些结果不能用来得出关于整个数据集的结论。

数据工程师可以通过采用几种简单的方法来减少查询时间,只需要少量的工作和很少的额外成本,从而减轻数据争论的痛苦。这些方法旨在将数据的大小和范围缩减到与所提问题相关的子集,下面将对此进行详细介绍。

使用列式数据存储

数据的存储方式对数据的分析访问有很大的影响。列数据格式,如 Parquet ,被广泛使用并提供了许多好处。除了将数据扫描限制在查询中出现的列并允许更好的压缩之外,这些格式还以二进制格式而不是文本格式存储数据。即使在一个高效的 CSV 阅读器中,将文本解析为二进制数据类型也会消耗将数据加载到计算机内存所需时间的 80%。此外,正确解析文本字段本身也是一个挑战。一次预处理和以二进制格式存储数据避免了每次访问数据时的额外计算。数据仓库通常提供现成的二进制列数据存储。

创建始终在线的采样数据集

许多数据问题可以通过使用代表性的数据样本来回答。采样数据集比一小段时间的数据好得多,因为它们代表整个数据集,足以回答各种问题。尽管提取和更新样本是乏味的,但每个数据科学家迟早都会被诱惑这样做,仅仅是为了缩短他们查询的周转时间。一个更有效且成本不高的解决方案是定期提供自动生成的数据样本。理想情况下,采样数据的生成应依赖于产生完整数据集的流水线。

在数据代表一些事件(例如,广告印象、购买交易、网站访问等)的典型情况下。)与一些实体(例如,在线用户、公司等)相关。),用不同类型的采样创建数据集是有益的。一个显而易见的方法是对事件进行采样,随机选择一定百分比的事件包含在数据集中。另一种抽样策略,对数据科学家或分析师来说,可能更有价值,也更难实现,就是覆盖与实体样本相关的所有记录。例如,我们可能希望包含 5%的站点用户的所有购买。这样的样本允许人们有效地执行基于用户的分析。采样策略,包括自适应采样,是另一篇文章的主题。

提取较小的相关数据集

当感兴趣的事件很少时,取样可能不是一个选项。例如,在数字广告设置中,人们可能对在有限的时间范围内提取特定广告活动的所有可用数据感兴趣。这样的数据集虽然包含所有必需的字段,但只是所有数据的一个小子集。与这些数据进行交互的分析师和数据科学家在处理一个项目时可能会发出数百个查询。如果数据工程团队构建工具,允许数据科学家为初始提取创建一个查询,并由另一个查询负责定期更新,则提取这样一个数据集并使其保持最新的过程可以自动化。项目完成后,不再需要这些数据,可以将其存档或删除。

将较小的数据集放在高效的 SQL 存储中

SQL 无疑是数据分析最常用的语言。因此,使用 SQL 使数据集可用于查询扩大了可以与数据交互的人数。抛开数据的民主化不谈,在高效的分析 SQL 查询引擎中提供更小的数据集可以进一步减少查询时间,从而减少数据科学家和分析师浪费的时间。根据数据的大小和对基础设施的要求,这种查询引擎的范围很广,从 MySQL 和 PostgreSQL 到 Snowflake 和 Redshift,再到完全托管的服务,如亚马逊的 Athena 和谷歌的 BigQuery。

提供专用的计算资源

数据探索中最大的挫折之一是不得不在一个共享队列中等待一个查询的结果,这个查询最终运行不到一分钟。在共享集群上为交互式查询提供单独的高优先级资源池(可能限制在工作时间内),对于改善整体查询周转时间大有帮助。此外,为数据科学和分析团队提供具有足够 CPU 和内存容量的专用计算资源,可以将较小的数据集加载到他们选择的工具中,以执行开发和深入分析。

投资是值得的!

人类的时间比计算机时间和数据存储成本要贵一个数量级。通过比较数据科学家的有效时薪和一台强大计算机的时薪,人们可以很容易地观察到这一点。此外,人力时间为公司创造了新的净价值。因此,将典型的分析查询完成时间缩短到不到一分钟可能是加速数据科学和分析项目的最有效的技术投资。

Kelly sik kema 和 Glen Noble 在 Unsplash 上拍摄的照片由 Sergei Izrailev 合成

这篇文章最初出现在生活数据博客上。

具有并行处理的更快的熊猫:cuDF 与 Modin

原文:https://towardsdatascience.com/faster-pandas-with-parallel-processing-cudf-vs-modin-f2318c594084?source=collection_archive---------10-----------------------

Photo by Lindsay Henwood on Unsplash

对于 Pandas,默认情况下我们一次只能使用一个 CPU 内核。这对于小数据集来说没问题,但是当处理较大的文件时,这可能会造成瓶颈。

通过使用并行处理来加快速度是可能的,但是如果你从未编写过多线程程序,不要担心:你不需要学习如何去做。一些新的图书馆可以为我们做到这一点。今天我们就来对比一下其中的两位: cuDF摩丁。它们都使用类似 pandas 的 API,所以我们可以通过修改 import 语句来开始使用它们。

cuDF

cuDF 是一个 GPU 数据帧库,它提供了一个类似熊猫的 API ,允许我们加速我们的工作流,而无需进入 CUDA 编程的细节。lib 是 RAPIDS 的一部分,RAPIDS 是一套开源库,使用 GPU 加速,与流行的数据科学库和工作流集成,以加速机器学习。

The RAPIDS suite

该 API 与 pandas 非常相似,因此在大多数情况下,我们只需更改一行代码就可以开始使用它:

**import cudf as pd**s = pd**.Series(**[1,2,3,None,4]**)**df = pd**.DataFrame**([('a', list(range(20))),
                     ('b', list(reversed(range(20)))),
                     ('c', list(range(20)))]**)**df.**head**(2)df.**sort_values**(by='b')
df['a']
df.**loc**[2:5, ['a', 'b']]s = pd.Series([1,2,3,None,4])s.fillna(999)df = pd.**read_csv(**'example_output/foo.csv'**)** df.**to_csv(**'example_output/foo.csv', index=False**)**

cuDF 是一个单 GPU 库。对于多 GPU,他们使用 Dask 和 dask-cudf 包,该包能够在单台机器上的多个 GPU 之间扩展 cudf,或者在一个集群中的许多机器上扩展多个 GPU[cuDF 文档 ]。

摩丁

Modin 还提供了一个 pandas-like API 使用 Ray 或 Dask 实现一个高性能的分布式执行框架。有了 Modin,你可以使用你机器上的所有 CPU 内核。它在 4 个物理内核的笔记本电脑上提供了高达 4 倍的速度提升[ 摩丁文档 ]。

Modin

环境

我们将使用 Maingear VYBE PRO 数据科学 PC ,我正在使用 Jupyter 运行脚本。以下是技术规格:

Maingear VYBE PRO 数据科学电脑

  • 125gb 内存
  • i9–7980 xe,36 个内核
  • 2x 泰坦 RTX 24GB

用于基准的数据集是巴西 2018 年高等教育普查

基准 1:读取一个 CSV 文件

让我们使用 Pandas、cuDF 和 Modin 读取一个 3gb 的 CSV 文件。我们将运行它 30 次并得到平均值。

Reading a CSV file

摩丁以平均不到 4s 胜出。它会自动在系统的所有可用 CPU 内核中分配计算,我们有 36 个内核,这可能就是原因🤔?

基准 2:缺失值

在这个基准测试中,我们将填充数据帧的 NaN 值。

Filling missing values

摩丁也是这个基准的赢家。cuDF 是平均运行时间更长的库。

基准 3:分组依据

让我们将行分组,看看每个库的行为。

Grouping rows

这里 cuDF 是赢家,摩丁表现最差。

酷,那么我应该使用哪个库呢?

https://twitter.com/swyx/status/1202202923385536513

要回答这个问题,我想我们必须考虑一下我们在工作流程中最常用的方法。在今天的基准测试中,使用 Modin 读取文件要快得多,但是我们需要在 ETL 中使用多少次read_csv()方法呢?相比之下,理论上,我们会更频繁地使用groupby()方法,在这种情况下,cuDF 库的性能最好。

Modin 很容易安装(我们只需要使用 pip),cuDF 比较难(你需要更新 NVIDIA 驱动,安装 CUDA,然后使用 conda 安装 cuDF)。或者你可以跳过所有这些步骤,得到一台数据科学电脑,因为它配备了所有 RAPIDS 库和完全安装的软件。

此外,摩丁和 cuDF 仍处于早期阶段,他们还没有完整覆盖整个熊猫 API。

入门指南

如果你想深入研究 cuDF,10 分钟到 cuDF 和 Dask-cuDF 是一个很好的起点。

关于摩丁的更多信息,这篇博文解释了更多关于用射线并行化熊猫的信息。如果你想更深入地了解,还有关于用 Modin 透明地扩展交互式数据科学的技术报告,它很好地解释了 Modin 的技术架构。

用于目标检测的快速 R-CNN

原文:https://towardsdatascience.com/faster-r-cnn-for-object-detection-a-technical-summary-474c5b857b46?source=collection_archive---------0-----------------------

技术论文摘要

R-CNN 家族中使用最广泛的最先进版本——更快的 R-CNN 于 2015 年首次发布。本文是了解当今物体探测基础的系列文章的第三篇,也是最后一篇,阐述了更快的 R-CNN 探测管道的技术细节。回顾它的前辈,看看这些总结:地区与 CNN (R-CNN)快速 R-CNN

在 R-CNN 系列论文中,版本之间的演变通常是在计算效率(整合不同的训练阶段)、减少测试时间和提高性能(mAP)方面。这些网络通常包括——A)生成“边界框”或图像中可能对象的位置的区域提议算法;b)通常使用 CNN 获得这些对象的特征的特征生成阶段;c)分类层,用于预测该对象属于哪个类别;以及 d)回归层,用于使对象边界框的坐标更加精确。

快速 R-CNN 网络中剩下的唯一独立部分是区域提议算法。R-CNN 和快速 R-CNN 都使用基于 CPU 的区域提议算法,例如,选择性搜索算法,每幅图像大约需要 2 秒钟,在 CPU 上运行。更快的 R-CNN [3]论文通过使用另一个卷积网络(RPN)来产生区域提议,解决了这个问题。这不仅将每个图像的区域提议时间从 2s 降低到 10ms,而且允许区域提议阶段与随后的检测阶段共享层,导致特征表示的整体改进。在本文的其余部分,“更快的 R-CNN”通常指的是一种检测管道,它使用 RPN 作为区域提议算法,使用快速 R-CNN 作为检测器网络。

区域提案网络

Figure 1: The architecture of the region proposal network or RPN

体系结构

  • 区域建议网络(RPN)从输入图像被馈送到骨干卷积神经网络开始。首先调整输入图像的大小,使其最短边为 600 像素,最长边不超过 1000 像素。
  • 主干网络的输出特征(由 H x W 表示)通常比输入图像小得多,这取决于主干网络的跨度。对于本文中使用的两种可能的主干网(VGG、ZF 网络),网络跨距为 16。这意味着主干输出特征中的两个连续像素对应于输入图像中相距 16 个像素的两个点。
  • 对于输出特征图中的每个点,网络必须了解输入图像中相应位置是否存在对象,并估计其大小。这是通过在主干网络的输出要素地图上的每个位置的输入图像上放置一组“锚点”来实现的。这些锚表示在该位置的各种尺寸和长宽比的可能物体。下图显示了 9 个可能的锚点,它们以 3 种不同的纵横比和 3 种不同的大小放置在输入图像上,用于输出特征地图上的点 A。对于帕斯卡挑战,所使用的锚具有 128、256、512 和 1:1、1:2 和 2:1 的 3 种比例的盒子面积。

Figure 2: The possible anchors in the input image in a location corresponding to point A in the feature map.

  • 当网络移动通过输出特征图中的每个像素时,它必须检查跨越输入图像的这些 k 对应锚点是否实际包含对象,并细化这些锚点的坐标以给出作为“对象提议”或感兴趣区域的边界框。
  • 首先,具有 512 个单元的 3×3 卷积被应用于主干特征图,如图 1 所示,以给出每个位置的 512-d 特征图。接下来是两个兄弟层:1 x 1 卷积层,18 个单元用于对象分类,1 x 1 卷积层,36 个单元用于边界框回归。
  • 分类分支中的 18 个单元给出大小为(H,W,18)的输出。该输出用于给出主干特征图(尺寸:H×W)中的每个点在该点的所有 9 个锚点中是否包含对象的概率。
  • 回归分支中的 36 个单元给出大小为(H,W,36)的输出。该输出用于给出主干特征图(大小:H×W)中每个点的 9 个锚的 4 个回归系数。这些回归系数用于改善包含对象的锚点的坐标。

训练和损失函数

  • 输出的特征图由大约 40×60 个位置组成,总共对应 40609 ~ 20k 个锚点。在列车运行时,所有跨越边界的锚都被忽略,因此它们不会造成损失。这使得每个图像大约有 6k 个锚。
  • 如果一个锚满足以下两个条件中的任何一个,则该锚被认为是“正”样本——a)该锚具有最高的 IoU(交集/并集,一种重叠的度量);b)锚具有大于 0.7 的 IoU 和任何地面实况框。同一个 groundtruth 框可以导致多个锚点被分配阳性标签。
  • 如果一个锚点的 IoU 与所有 groundtruth 框小于 0.3,则该锚点被标记为“负”。剩余的锚(既不是正面的也不是负面的)在 RPN 训练中被忽略。
  • 用于训练 RPN 的每个小批量来自单个图像。从该图像中采样所有锚会使学习过程偏向负样本,因此随机选择 128 个正样本和 128 个负样本来形成该批,如果正样本数量不足,则用额外的负样本填充。
  • RPN 的训练损失也是多任务损失,由下式给出:

  • 这里 i 是主播在 mini-batch 中的索引。分类损失 L𝒸ₗₛ(pᵢ,pᵢ)* 是两类(对象对非对象)的对数损失。 pᵢ 是主播 i 的分类分支的输出分数, pᵢ* 是地面实况标签(1 或 0)。
  • 回归损失 Lᵣₑ(tᵢ,tᵢ)* 仅在锚点实际包含一个对象时被激活,即基础事实 pᵢ* 为 1。术语 tᵢ 是回归层的输出预测,由 4 个变量组成【tₓ,tᵧ,t w ,tₕ].回归目标 tᵢ*计算如下—

  • 这里 x,y,w,h 对应于盒子中心的 (x,y) 坐标和盒子的高度 h 和宽度 w 。xₐ,x*代表锚定框的坐标及其对应的地面真实边界框。
  • 请记住,锚盒的所有 k ( = 9)都有不同的回归量,它们不共享权重。因此,锚 i 的回归损失应用于其相应的回归量(如果是正样本)。
  • 在测试时,所学习的回归输出 tᵢ可以被应用于其相应的锚框(被预测为正的),并且用于预测的对象提议边界框的 x,y,w,h 参数可以从下式反算

测试时间详细信息

在测试时,来自每个图像的 20k 个锚经过一系列后处理步骤,发送到对象提议边界框中。

  • 回归系数被应用于锚点以进行精确定位。这给出了精确的边界框。
  • 所有的盒子都按照它们的 cls 分数排列。然后,应用阈值为 0.7 的非最大抑制(NMS)。从上到下,所有与另一个边界框的 IoU 大于 0.7 的边界框都被丢弃。因此,最高得分的边界框被保留用于一组重叠的框。
  • 这给出了每个图像大约 2k 个建议。
  • 跨界边界框被保留并被剪切到图像边界。
  • 当使用这些对象建议来训练快速 R-CNN 检测流水线时,使用来自 RPN 的所有 2k 个建议。在用于快速 R-CNN 检测的测试时间,仅选择来自 RPN 的前 N 个提议。

对象检测:更快的 R-CNN (RPN +快速 R-CNN)

Figure 3: The RPN for region proposals and Fast R-CNN as a detector in the Faster R-CNN detection pipeline

更快的 R-CNN 架构包括作为区域提议算法的 RPN 和作为检测器网络的快速 R-CNN。

快速 R-CNN 作为快速 R-CNN 的检测器

快速 R-CNN 检测器还包括一个 CNN 主干、一个 ROI 池层和完全连接的层,后面是两个用于分类和边界框回归的兄弟分支,如图 3 所示。

  • 输入图像首先通过主干 CNN 得到特征图(特征大小:60,40,512)。除了测试时间效率,使用 RPN 作为建议生成器的另一个关键原因是 RPN 主干和快速 R-CNN 检测器主干之间重量共享的优势。
  • 接下来,来自 RPN 的边界框提议被用于汇集来自主干特征地图的特征。这是由 ROI pooling 层完成的。ROI 汇集层本质上是通过以下方式工作的:a)从主干特征地图中提取与提议相对应的区域;b)将该区域分成固定数量的子窗口;c)在这些子窗口上执行最大汇集以给出固定大小的输出。要了解投资回报池层的细节及其优势,请阅读 Fast R-CNN
  • ROI 池层的输出大小为(N,7,7,512 ),其中 N 是区域建议算法的建议数。在通过两个完全连接的图层后,这些要素将被送入兄弟分类和回归分支。
  • 注意,这些分类和检测分支与 RPN 的不同。这里,分类层对于检测任务中的每个类(包括一个包罗万象的背景类)都有 C 个单元。这些特征通过 softmax 层得到分类分数,即一个建议属于每个类别的概率。回归层系数用于改进预测的边界框。这里的回归量是大小不可知的,(不同于 RPN),但特定于每个类。也就是说,所有类都有单独的回归变量,每个变量有 4 个参数,对应于回归层中的 C*4 个输出单元。
  • 关于更快的 R-CNN 如何被训练和它的损失函数的更多细节参考快速 R-CNN

4 步交替训练

为了迫使网络在 RPN 和检测器之间共享 CNN 主干的权重,作者使用了 4 步训练方法:

a)如上所述,独立训练 RPN。该任务的中枢 CNN 用来自为 ImageNet 分类任务训练的网络的权重初始化,然后为区域提议任务进行微调。
b)快速 R-CNN 检测器网络也是独立训练的。该任务的中枢 CNN 用来自为 ImageNet 分类任务训练的网络的权重初始化,然后为对象检测任务进行微调。RPN 权重是固定的,并且来自 RPN 的建议被用于训练更快的 R-CNN。
c)RPN 现在使用来自这个更快的 R-CNN 的权重进行初始化,并且针对区域提议任务进行微调。这一次,RPN 和检测器之间的公共层中的权重保持固定,只有 RPN 特有的层被微调。这是最终的 RPN。
d)再次使用新的 RPN,快速 R-CNN 检测器被微调。同样,只有检测器网络特有的层被微调,而公共层权重是固定的。

这给出了共享卷积层的更快的 R-CNN 检测框架。

Figure 4: Results of the Faster R-CNN detection framework with a VGG backbone

结果

  • 在 PASCAL 数据集上的所有实验中,选择快速 R-CNN 作为检测器。使用 RPN+ZF 主干作为建议网络(不与检测器共享权重)与使用“选择性搜索”(SS)作为区域建议算法的性能相当。这已经给了我们相当的结果,大大减少了检测时间。RPN+VGG 主干网仅作为一个具有非共享权重的提案网络,其表现略好于 SS 地区提案基线。当检测器使用共享权重时,RPN 中的 ZF 和 VGG 主干的性能都超过了 SS 基线。这与大量其他实验一起验证了 RPN 作为区域提议方法的使用。
  • 与选择性搜索的 1.8 秒相比,VGG RPN 的检测需要 198 毫秒。
  • 执行的其他实验验证了 NMS 的使用,以及单独的分类和回归分支的使用。用于根据分数对提议进行排序的分类分支似乎是保持合理的高召回率与 IoU 重叠率的重要因素,即使当对象提议的数量减少时。
  • 在观察锚箱比例和长宽比的重要性的消融研究中,作者发现使用 3 个比例和一个长宽比几乎与 3 个比例和 3 个长宽比一样有效。根据任务和数据集,可以修改这些比率和比例。在每个位置使用单个锚点会导致地图显著下降。

这就结束了更快的 R-CNN 论文的技术总结。希望你喜欢(理解)!欢迎在下面的评论中讨论或更正。

参考资料:

[1]任,何,罗斯吉斯克,更快的 R-CNN:用区域提议网络实现实时目标检测,NIPS'15 会议录
【2】http://www . telesens . co/2018/03/11/Object-Detection-and-classification-using-R-CNNs/
【3】https://Leonardo araujosantos . git books . io/artificial-intelligence/

更快的 RCNN 对象检测

原文:https://towardsdatascience.com/faster-rcnn-object-detection-f865e5ed7fc4?source=collection_archive---------2-----------------------

简介

更快的 RCNN 是由罗斯·吉斯克邵青·任明凯·何孙健于 2015 年提出的一种物体检测架构,是像 YOLO(你只看一次)和 SSD(单次检测器)一样使用卷积神经网络的著名物体检测架构之一。

让我们解释一下这个架构是如何工作的,

更快的 RCNN 由 3 部分组成

  • 第 1 部分:卷积层

在这一层中,我们训练过滤器来提取图像的适当特征,例如,假设我们要训练这些过滤器来提取人脸的适当特征,那么这些过滤器将通过训练只存在于人脸中的形状和颜色来学习。

所以我们可以把卷积层比作咖啡过滤器,咖啡过滤器不让咖啡粉通过杯子,所以我们的卷积层学习物体的特征,不让任何东西通过,只让想要的物体通过。

  • 咖啡粉+咖啡液=输入图像
  • 咖啡过滤器= CNN 过滤器
  • 咖啡液体 CNN 的最后一个专题图

让我们更多地讨论卷积神经网络,
卷积网络通常由卷积层、汇集层和最后一个组件组成,该组件是完全连接的或另一个扩展的组件,将用于适当的任务,如分类或检测。

我们通过沿着我们的输入图像的滑动滤波器来计算卷积,并且结果是称为特征图的二维矩阵。

池包括通过消除低值像素来减少要素地图中的要素数量。

最后一件事是使用全连接层来分类这些特征,这在快速 RCNN 中不是我们的情况。

  • 第 2 部分:区域提议网络(RPN)

RPN 是一种小型神经网络,它在卷积层的最后一个特征图上滑动,预测是否存在对象,并预测这些对象的包围盒。

  • 第 3 部分:类别和包围盒预测

现在,我们使用另一个完全连接的神经网络,该网络将 RPN 提出的区域作为输入,并预测对象类别(分类)和边界框(回归)。

  • 培训

为了训练这种架构,我们使用 SGD 来优化卷积层过滤器、RPN 权重和最后完全连接的层权重。

应用更快的 RCNN

您可以使用 Tensorflow 对象检测 API ( 链接)在您的自定义数据集上训练更快的 RCNN 预训练模型(链接)。

高效 CNN 的快速培训

原文:https://towardsdatascience.com/faster-training-of-efficient-cnns-657953aa080?source=collection_archive---------18-----------------------

最近,已经证明深度卷积在设计高效网络方面非常有效,例如 MobileNetShuffleNet 。然而,这种高效的网络需要花费更长的时间来训练,通常需要 300-400 个历元,才能在 ImageNet 数据集上达到最先进的精度。

在本文中,我们描述了 ESPNetv2 论文(CVPR'19)中介绍的一种有效的学习率调度器 ,它允许在大约 100 个时期内训练有效的网络,而不会对准确性产生任何重大影响。为了展示 ESPNetv2 中学习率调度程序的卓越性能,我们在本文中使用了 ShuffleNetv2 的架构。我们注意到我们的发现与其他高效网络一致,包括 MobileNetv2

学习率调度程序

ESPNetv2 引入了余弦学习率的变体,其中学习率线性衰减直到周期长度,然后重新开始。在每个时期 t ,学习率ηₜ被计算为:

Figure 1: Cyclic learning rate policy with linear learning rate decay and warm restarts

F 紫苑培训

使用上述学习速率调度程序,我们在四个 TitanX GPUs 上使用 SGD 在两种不同的 FLOP 设置下为训练 ShuffleNetv2 总共 120 个历元,批量大小为 512:(1)41m flops 和(2) 146 MFLOPs。对于前 60 个时期,我们将 ηₘᵢₙηₘₐₓT 分别设置为 0.1、0.5 和 5。对于剩余的 60 个纪元,我们设置 ηₘₐₓ=0.1T=60ηₘᵢₙ = ηₘₐₓ/T 。下图显示了 120 个时期的学习策略。

Figure 2: Cyclic learning rate scheduler with warm restarts for faster training proposed in the ESPNetv2 paper.

ImageNet 数据集上的结果

结果见图 3 和表 1。我们可以清楚地看到,学习率调度器(如上所述)能够实现更快的训练,同时提供与 ShuffleNet 论文中的线性学习率调度器相似的性能。

Figure 3: Epoch-wise training (left) and validation (right) accuracy of ShuffleNetv2 (41 MFLOPs) using linear (see ShuffleNetv1/v2 papers) and cyclic (discussed above) learning rate schedule.

Table 1: Top-1 accuracy of the ShuffleNetv2 model with two different learning rate schedules: (1) Linear, as in the ShuffleNet paper and (2) Cyclic, as in the ESPNetv2 paper (described above). The slight difference in reported and our implementation is likely due to the batch size. We used a batch size of 512 while in ShuffleNetv2 paper a batch size of 1024 is used.

我们可以在不同的工作上使用 ESPNetv2 排程器吗?

是的,调度程序是通用的,可以跨不同的任务使用。在 ESPNetv2 论文中,我们将它用于所有三个标准视觉任务:(1)图像分类,(2)对象检测,以及(3)语义分割。详见论文

在 R 中安装和加载库的最快方法

原文:https://towardsdatascience.com/fastest-way-to-install-load-libraries-in-r-f6fd56e3e4c4?source=collection_archive---------15-----------------------

在与一位同学合作之后,很明显我需要一种新的方式来加载我在学校学到的库。不是每个人都安装了相同的库,这可能会出错。我希望代码能为每个人无缝运行。此外,不得不反复编写相同的函数来安装和加载不同的库是很痛苦的。我和我的同学努力寻找一种简单的方法来做到这一点。然后我使用了一个名为 tictoc 的包来测量不同方法的速度。

注意:测试是在干净的全球环境中进行的。所有的包都已经被安装和加载了,所以测试可以保持一致(包会被重新加载)。

首先,为了进行分析,需要安装和加载 tictoc 包。我还将为要加载的包列表定义一个变量。

install.packages("tictoc")
library(tictoc)packages <- c("tidyverse", "dplyr", "stringr", "zoo", "ROCR", "caret", "class", "gmodels", "randomForest")

方法 1 —分别加载和安装每个库:

我注释掉了 install.packages,因为不管这个包是否存在,它都会重新安装。这不是最有效的方法,因为如果省略 install.packages 并且该软件包在当前安装中不存在,您将重新安装所有内容或得到一个错误。

总时间:0.11 秒

tic("basic")#install.packages(packages)
library(tidyverse)
library(dplyr)
library(stringr)
library(zoo)
library(ROCR)
library(caret)
library(class)
library(gmodels)
library(randomForest)toc(log = TRUE)
log.txt <- tic.log(format = TRUE)

方法 2——使用 for 循环

总时间:0.13 秒

tic("for loop")libraries <- function(packages){
  for(package in packages){
    #checks if package is installed
    if(!require(package, character.only = TRUE)){
      #If package does not exist, then it will install
      install.packages(package, dependencies = TRUE)
      #Loads package
      library(package, character.only = TRUE)
    }
  }
}libraries(packages)toc(log = TRUE)
log.txt <- tic.log(format = TRUE)

方法 3 —使用应用方法

总时间:0.07 秒

tic("apply")ipak <- function(pkg){
  new.pkg <- pkg[!(pkg %in% installed.packages()[, "Package"])]
  if (length(new.pkg))
    install.packages(new.pkg, dependencies = TRUE)
  sapply(pkg, require, character.only = TRUE)
}ipak(packages)toc(log = TRUE)
log.txt <- tic.log(format = TRUE)

方法 4 —使用install . load

在这篇文章中,我假设用户已经安装了 install.load 来计算时间,因为这只会发生一次。要安装的代码只是出于可复制性的目的。

总时间:0.12 秒

tic("install.load")if (!require("install.load")) install.packages("install.load")
library(install.load)install_load(packages)toc(log = TRUE)
log.txt <- tic.log(format = TRUE)

方法 5——使用 吃豆人

这也是在假设用户已经安装了 pacman 的情况下计算的。

总时间:0.06 秒

tic("pacman")if (!require("pacman")) install.packages("pacman")
#pacman will not accept a character vector so the same packages are repeated
pacman::p_load("tidyverse", "dplyr", "stringr", "zoo", "ROCR", "caret", "class", "gmodels", "randomForest")toc(log = TRUE)
log.txt <- tic.log(format = TRUE)

最终判决

安装和/或加载许多软件包的最快方法是使用 pacman 软件包。但是,如果您不想安装额外的软件包,最好使用 apply 方法。它只是稍微慢了一点。

值得一提的是,对于“基本”测试,如果用户不确定他们当前已经安装的软件包,由于上面提到的原因,这种方法会变得非常慢。

快速文本分类器是如何工作的?

原文:https://towardsdatascience.com/fasttext-bag-of-tricks-for-efficient-text-classification-513ba9e302e7?source=collection_archive---------5-----------------------

当线性模型优于复杂的深度学习模型时

脸书研究院开发的 FastText ,是一个高效学习单词表示和文本分类的库。FastText 支持单词和句子的监督(分类)和非监督(嵌入)表示。

然而,FastText 包的文档没有提供关于实现的分类器和处理步骤的细节。

在这里,我们试图跟踪 FastText 包的底层算法实现。

简单地说,没有什么魔法,但是很少有聪明的步骤:

  • 通过平均单词/n-gram 嵌入来获得句子/文档向量。
  • 对于分类任务,使用多项式逻辑回归,其中句子/文档向量对应于特征。
  • 当在具有大量类的问题上应用 FastText 时,可以使用分层的 softmax 来加速计算。

【fast Text】高效文本分类锦囊妙计 :

论文:http://aclweb.org/anthology/E17-2068

  • 从平均为文本表示的单词表示开始,并将它们馈送到线性分类器(多项式逻辑回归)。
  • 作为隐藏状态的文本表示,可在要素和类之间共享。
  • Softmax 层,以获得预定义类别的概率分布。
  • Hierarchial Softmax:基于霍夫曼编码树,用于降低计算复杂度 O(kh) 到 O(hlog(k)),其中 k 是类的数量,h 是文本表示的维数。
  • 使用一袋 n-grams 来保持效率而不损失准确性。没有明确使用词序。
  • 使用哈希技巧来保持 n 元语法的快速和内存高效映射。
  • 它是用 C++编写的,在训练时支持多重处理。

FastText vs 深度学习进行文本分类:

对于不同的文本分类任务,FastText 显示的结果在准确性方面与深度学习模型不相上下,尽管在性能上快了一个数量级。

:简单的 FastText 算法的高精度表明,文本分类问题仍然没有被很好地理解,不足以构建真正有效的非线性分类模型。【3】

参考文献:

[## Facebook 研究/快速文本

快速文本表示和分类库。-Facebook 研究/快速文本

github.com](https://github.com/facebookresearch/fastText) [## 快速文本:引擎盖下

在这里我们将看到一个性能最好的嵌入库是如何实现的。

towardsdatascience.com](/fasttext-under-the-hood-11efc57b2b3)

https://pdfs . semantic scholar . org/9d 69/93f 60539d 30 ee 325138 b 3465 aa 020 fa 3 BCB 4 . pdf

https://gist . github . com/shagunsodhani/432746 f 15889 F7 F4 a 798 BF 7 f 9 EC 4 b 7d 8

推特快速文本情感分析:简单指南

原文:https://towardsdatascience.com/fasttext-sentiment-analysis-for-tweets-a-straightforward-guide-9a8c070449a2?source=collection_archive---------6-----------------------

快速文本架构、清理、上采样和推文情感的本质

A robot learning sentiments

在这篇文章中,我们介绍了 fastText 库,它如何在文本分类方面比一些深度神经网络实现更快的速度和类似的准确性。接下来,我们将展示如何借助由AWS comprehende生成的数据来训练情感分析模型。在的另一篇文章中,我们展示了如何使用 AWS Elastic Beanstalk 创建一个机器学习服务器来服务你的模型。

fast text——浅层神经网络架构

FastText 是由 facebook AI 开发的开源 NLP 库,最初于 2016 年发布。它的目标是高效地提供单词嵌入文本分类。据他们的作者称,在准确性方面,它通常与深度学习分类器不相上下,并且在训练和评估方面快许多数量级。[1]

这使得 fastText 成为构建 NLP 模型并为生产环境生成实时预测的优秀工具。

FastText 架构概述

FastText 的核心依赖于用于单词表示的连续单词包 (CBOW)模型和一个分层分类器来加速训练。

连续单词包(CBOW)是一个浅层神经网络,它被训练来从其邻居预测一个单词。FastText 用预测类别取代了预测单词的目标。这些单层模型训练速度非常快,并且可以很好地扩展。

此外,fastText 用分层的 softmax 替换标签上的 softmax。这里每个节点代表一个标签。这减少了计算量,因为我们不需要计算所有标签的概率。有限数量的参数减少了训练时间。

fastText hierarchical architecture for sentiment analysis.

训练速度更快,但结果相似

根据最初的论文[1],fastText 实现了与其他算法类似的结果,同时训练速度快得多。

正如您在下面看到的,fastText 的训练时间在 1 到 10 秒之间,而其他型号的训练时间为几分钟或几小时。

Bag of Tricks for Efficient Text Classification — Joulin 2016

打开用于情感分析的数据集

大多数用于文本分类的开放数据集都很小,我们注意到,除了英语之外,很少(如果有的话)可以用于其他语言。因此,除了为情感分析提供指导,我们还想为情感分析提供开放数据集【2】。

出于这些原因我们提供了包含推文列表及其观点的文件:

  • 英语推文数据集=>630 万条推文可用。
  • 西班牙语推文数据集=>120 万推文。
  • 法语推文数据集= > 25 万条推文
  • 意大利推文数据集= > 425 000 条推文
  • 德国推文数据集= > 210 000 条推文

这些都是得益于 AWS 领悟 API 生成的。对于西班牙语和法语,首先使用谷歌翻译将推文翻译成英语,然后使用 AWS 领悟进行分析。情绪分为积极、消极、中性或混合。

对于本文,我们使用的是英语推文数据集。

为情绪清理推文。

他们说清理通常是数据科学家 80%的时间。可悲的是,这里没有例外。为了获得最好的结果,我们必须确保数据接近正确的英语,因为我们在处理推文,这不是一件容易的事情。

Example of (funny) misspelled tweets — source: thepoke.co.uk

我们的目标是清理推文,使它们更容易被机器阅读。有许多技术可以清理文本。最著名的是词汇化词干化停用词

  • 词干化和词条化的目标都是将一个单词的屈折形式和派生相关形式简化为一个通用的基本形式。例如:am,are,is = > be / dog,dogs,dogs’s,dogs =>dog。)这些减少了语料库的大小及其复杂性,允许更简单的单词嵌入(am、are 和 is 共享相同的精确单词向量)。
  • 停用词过滤增加噪音或对机器理解文本没有价值的常用词。例子:a,and,the…

虽然词干化和词汇化有助于情感分析,但停用词过滤并不简单。停用词的目标是删除不必要的词,但是如果您查看可用的停用词列表,例如来自 NLTK 库的列表,您会发现可能传达负面情绪的词,例如:not,don 't,has not…但是对于情感分析问题,我们希望保留负面词汇。很明显,“这是一个好游戏”和“这不是一个好游戏”提供了相反的观点。因此,用户需要编辑停用词列表以排除传达负面含义的词,或者根本不使用停用词。我们选择了后者。

此外, tweets 是包含大量表情符号、缩写、标签、拼写错误的单词和俚语的短信。其中大多数对情感分析没有什么价值,需要清理:

  • 缩写/俚语清理。如果我们想简化我们的问题,当有合适的替代方法时,我们需要去掉缩写并翻译俚语。然而,很难找到这样的词汇库或数据库。为此,我们必须创建一个列表。去我的 GitHub 页面看看吧。
#CONTRACTIONS is a list of contractions and slang and their conversion. { "you've":"you have", "luv":"love", etc...}
tweet = tweet.replace("’","'")
words = tweet.split()
reformed = [CONTRACTIONS[word] if word in CONTRACTIONS else word for word in words]
tweet = " ".join(reformed)
  • 修复拼错的单词。这里我们使用一个正则表达式,使用 regex 来删除单词中的重复字符。除了 regex 之外,您还可以使用其他能够检测和修复拼写错误的库。遗憾的是,它们非常慢,当你每天有数千条推文要分析时,这在生产中是不可接受的。
import itertools
tweet = ''.join(''.join(s)[:2] for _, s in itertools.groupby(tweet))
  • 转义 HTML 字符:Twitter API 有时会返回 HTML 字符。当这种情况发生时,我们需要将它们转换成 ASCII 形式。比如, %20 转换成空格,&amp;被转换为&。为此,我们使用 Beautiful Soup,一个用于解析 HTML 和 XML 文档的 Python 包。
from bs4 import BeautifulSoup
tweet = BeautifulSoup(tweet).get_text()
  • 移除标签/账户:需要移除使用 Twitter 标签(#)和账户(@)的名称。我们不希望一个足球运动员的名字被我们的模型永远归类为“负面”,仅仅因为他在我们的数据集中与糟糕的评论相关联。
tweet = ' '.join(re.sub("(@[A-Za-z0-9]+)|(#[A-Za-z0-9]+)", " ", tweet).split())
  • 删除网址:
tweet = ' '.join(re.sub("(\w+:\/\/\S+)", " ", tweet).split())
  • 去除标点符号:标点符号不用于“单词袋”技术。
tweet = ' '.join(re.sub("[\.\,\!\?\:\;\-\=]", " ", tweet).split())
  • 小写:将所有内容转换为小写,以避免区分大小写的问题:
#Lower case
tweet = tweet.lower()
  • 表情符号/表情符号:在一条推文中,表情符号和表情符号用“\”或标点符号表示,因此没有正确标记。为了保持它们的意义,我们需要将它们转换成更简单的形式。对于表情符号,有一个 python 库“e moji”通过将表情符号代码转换为标签来实现这一点。对于表情符号:-),您必须提供您的列表,我们会在 GitHub 页面上提供。
#Part for smileys - SMILEY is a list of smiley and their conversion. {"<3" : "love", ":-)" : "smiley", etc...}
words = tweet.split()
reformed = [SMILEY[word] if word in SMILEY else word for word in words]
tweet = " ".join(reformed)
#Part for emojis
tweet = emoji.demojize(tweet)
  • 口音只限于英语,但广泛用于其他语言,口音经常被放错地方或遗忘。对付它们最简单的方法就是摆脱它们。
def strip_accents(text):
    if 'ø' in text or  'Ø' in text:
        #Do nothing when finding ø 
        return text   
    text = text.encode('ascii', 'ignore')
    text = text.decode("utf-8")
    return str(text)

要查看所有相关内容,请查看我的 GitHub 页面上的代码[2]。

格式化数据

FastText 需要标记数据来训练监督分类器。标签必须以前缀__label__开头,这是它识别标签或单词的方式。下面是标签为正面和负面的推文所需格式的示例。

__label__POSITIVE congratulations you played very well yesterday.
__label__NEGATIVE disappointing result today.
...

我们使用下面的代码来格式化数据。

def transform_instance(row):
    cur_row = []
    #Prefix the index-ed label with __label__
    label = "__label__" + row[0]  
    cur_row.append(label)
    #Clean tweet and tokenize it
    cur_row.extend( nltk.word_tokenize(tweet_cleaning_for_sentiment_analysis(row[1].lower())))def preprocess(input_file, output_file, keep=1): with open(output_file, 'w') as csvoutfile:
        csv_writer = csv.writer(csvoutfile, delimiter=' ', lineterminator='\n')
        with open(input_file, 'r', newline='') as csvinfile: #,encoding='latin1'
            csv_reader = csv.reader(csvinfile, delimiter=',', quotechar='"')
            for row in csv_reader:
                if row[0].upper() in ['POSITIVE','NEGATIVE','NEUTRAL','MIXED']:
                    row_output = transform_instance(row)
                    csv_writer.writerow(row_output)# Preparing the training dataset 
preprocess('BetSentimentTweetAnalyzed.csv', 'tweets.train')

向上采样以抵消类别不平衡。

当一个标签比其他标签出现得更频繁时,就会出现类别不平衡的问题。在这种情况下,分类器往往会被大类淹没,而忽略小类。

应用于我们的英语推特数据集[2],我们注意到中性与积极/消极类别的不平衡。因此,将所有事物分类为中性的原始策略将给出 73%的准确率(见下表)。出于同样的原因,我们的模型可能倾向于中性。如果不加以管理,类别不平衡将使我们的模型简单化和不准确。

Example of imbalance in labels

为了解决这个问题,我们必须使用上采样。上采样(或过采样)包括为少数类添加新的推文,正的和负的,以使它们达到与多数类相等的推文数量,这里是中性的。我们提供了一个简单的代码来做到这一点。

def upsampling(input_file, output_file, ratio_upsampling=1):
    #
    # Create a file with equal number of tweets for each label
    #    input_file: path to file
    #    output_file: path to the output file
    #    ratio_upsampling: ratio of each minority classes vs majority one. 1 mean there will be as much of each class than there is for the majority class.
    i=0
    counts = {}
    dict_data_by_label = {} i=0
    counts = {}
    dict_data_by_label = {}# GET LABEL LIST AND GET DATA PER LABEL
    with open(input_file, 'r', newline='') as csvinfile:
        csv_reader = csv.reader(csvinfile, delimiter=',', quotechar='"')
        for row in csv_reader:
            counts[row[0].split()[0]] = counts.get(row[0].split()[0], 0) + 1
            if not row[0].split()[0] in dict_data_by_label:
                dict_data_by_label[row[0].split()[0]]=[row[0]]
            else:
                dict_data_by_label[row[0].split()[0]].append(row[0])
            i=i+1
            if i%10000 ==0:
                print("read" + str(i))# FIND MAJORITY CLASS
    majority_class=""
    count_majority_class=0
    for item in dict_data_by_label:
        if len(dict_data_by_label[item])>count_majority_class:
            majority_class= item
            count_majority_class=len(dict_data_by_label[item])  

    # UPSAMPLE MINORITY CLASS
    data_upsampled=[]
    for item in dict_data_by_label:
        data_upsampled.extend(dict_data_by_label[item])
        if item != majority_class:
            items_added=0
            items_to_add = count_majority_class - len(dict_data_by_label[item])
            while items_added<items_to_add:
                data_upsampled.extend(dict_data_by_label[item][:max(0,min(items_to_add-items_added,len(dict_data_by_label[item])))])
                items_added = items_added + max(0,min(items_to_add-items_added,len(dict_data_by_label[item])))# WRITE ALL
    i=0with open(output_file, 'w') as txtoutfile:
        for row in data_upsampled:
            txtoutfile.write(row+ '\n' )
            i=i+1
            if i%10000 ==0:
                print("writer" + str(i))upsampling( 'tweets.train','uptweets.train')

通过上采样,你冒着过度拟合的风险,一遍又一遍地重复同样的推文。但是如果你的数据集足够大,这应该不是问题。

使用 fastText 进行培训

现在是有趣的部分。是时候训练我们的机器了!

我们使用 fastText python 包装器来训练我们的模型。你可以在脸书研究院的 GitHub 页面上找到实现示例和文档。请确保您使用“git clone …”安装 fastText,而不是使用“pip install fasttext”。

由于我们已经准备好了数据,现在我们需要做的就是使用函数fastText.train_supervised。此功能有大量选项,但为了简单起见,我们主要关注以下内容:

  • 输入:我们训练数据的路径。
  • lr :学习率。我们将其设置为 0.01。
  • epoch :我们遍历整个数据集的次数。我们用 20。
  • wordNgrams :一个 n 元文法是来自给定文本样本的最大 n 个单词的连续序列。我们把它设置为 2。
  • dim :字向量的维度。我们用 20。

以下 python 代码显示了我们模型的训练。

hyper_params = {"lr": 0.01,
    "epoch": 20,
    "wordNgrams": 2,
    "dim": 20}     

# Train the model.
model = fastText.train_supervised(input=training_data_path, **hyper_params)
print("Model trained with the hyperparameter \n {}".format(hyper_params))

一旦经过训练,我们需要评估我们的模型在情感分析方面有多好。为此,我们可以使用两个度量精度和召回率,它们是 fastText 函数model.test的输出。然而,由于我们的问题的性质,精度和召回给出类似的数字,我们可以只关注精度。

下面的代码对训练和验证数据执行model.test,以比较我们模型的准确性。请注意,为了验证,我们采用了不同的数据集,在该数据集上我们使用了相同的清理过程,但没有上采样。

# CHECK PERFORMANCE      
result = model.test(training_data_path)
validation = model.test(validation_data_path)

# DISPLAY ACCURACY OF TRAINED MODEL
text_line = str(hyper_params) + ",accuracy:" + str(result[1])  + ",validation:" + str(validation[1]) + '\n' 
print(text_line)

总的来说,该模型对训练数据给出了 97.5%的准确度,对验证数据给出了 79.7%的准确度。

考虑到我们没有调整超参数,还不错。此外,研究估计人在判断对一段特定文本的情感时,只有 同意约 60%到 80%的次数 。因此,虽然我们可以尝试达到 100%的准确性,但我们必须记住,人类是会犯错的……最重要的是,我们在推特上工作!!

结论

我们刚刚展示了 fastText 如何工作,以及如何训练一个英语情感分析模型。我们使用了由AWS comprehensive产生的情感数据。在的另一篇文章中,我们解释了如何使用 AWS Elastic Beanstalk 和 Python Flask 应用程序为您的模型提供健壮的云基础设施。

如果你想重现结果,只要去我的 gitHub。如果需要完整的英文数据集,可以问我(对 GitHub 来说太大了)。我很乐意与你分享它。

成为中等会员,在平台上支持我!

https://medium.com/@charlesmalafosse/membership

参考

[1] 高效文本分类的锦囊妙计, 阿曼德·茹林,爱德华·格雷夫,彼得·博亚诺夫斯基,托马斯·米科洛夫,2016

https://github.com/charlesmalafosse。我的 GitHub 页面,包含本文的全部代码。

[3]脸书 GitHub,带有 fastText python 包装器。https://github . com/Facebook research/fast text/tree/master/python

[4]用 AWS Elastic Beanstalk 部署一个机器学习模型https://medium . com/@ charlesmalafosse/Deploy-a-machine-learning-model-with-AWS-Elastic Beanstalk-dfcc 47 b 6043 e

深度学习中的“深度”——一种实用方法

原文:https://towardsdatascience.com/fathoming-the-deep-in-deep-learning-a-practical-approach-c18adab31cbe?source=collection_archive---------16-----------------------

“深度学习”是难以捉摸的,但只要有一点数学知识就可以接近。这引发了一个实际问题:初等微积分足以解释深度学习吗?答案确实是肯定的。带着无限的好奇心去学习和重新学习新的和旧的,如果你能有条不紊地遵循下面的章节,我认为你会跨越鸿沟,直观地理解和应用每一个概念,包括微积分,以消除深度学习的所有复杂性。

学习“深度学习”既有趣又令人担忧。数学是基础,可以帮助清楚地理解和不可磨灭地蚀刻概念。当我开始阅读迈克尔·尼尔森的书“神经网络和深度学习”时,我一开始不小心完全理解了许多需要复习的基本概念,我想如果我可以与他人分享我的旅程以简化他们的学习,那么我想我已经为促进深度学习尽了自己的一份绵薄之力。

在这篇文章中,我将讲述我所采取的步骤以及我所研究、阅读和理解的内容——尽可能直观地揭示每个概念以及引起你兴趣的其他主题。每一步都在一个标题下详述,以理解深度学习的整个架构,这是仍在发展的人工智能的基石。随意跳过熟悉的部分,深入研究感兴趣的内容。

测量深度的步骤:

  1. 开端——用感知器模拟决策
  2. 节点内部的主力——激活功能
  3. 稍微绕了一下基础——微分学
  4. 基础——基本统计和损失减少
  5. 大优化—梯度下降
  6. 救援直观示例—下降揭秘 d
  7. 系综直接向后&向前—前馈&反向传播
  8. Bare neural net 的内部工作——与代码匹配的矩阵
  9. 学习曲线回溯—参考文献&致谢

开始——用感知器模拟决策

榜首

生物神经元模型解释了神经系统促进感知(无论是感觉还是其相关活动)的潜在机制。从这种感知中产生了“感知器”,一种寓言式的数学单位或模型。简而言之,感知器(1)接受多个输入,(2)加权(乘以一个设定的权重),(3)求和并累加到一个称为 bias 的常数,最后(4)发送到一个激活函数以(5)提供一个输出。

通常你可以把感知机想象成一个数学模型,在给定一组输入和一个阈值的情况下,决定我今天是否可以去看电影。假设输入是云量、假日事件、一天中的时间,所有这些都是一个按比例缩放的实数,每个都通过乘以相应的预设权重进行修改,并与阈值(偏差因子)相加,然后这个组合数被步进或平滑,其中步进函数的结果是 1 或 0,而平滑函数的值在 1 或 0 之间,这可以解释为结果发生的概率。

为了清楚起见,下面是上面例子的一个说明:

从数学上来说,如果我们有 j 个感知器输入,输出表示为:

为了进一步简化,我们可以将上面的等式视为点积或标量积,这是一种代数运算,采用两个等长的数字序列(通常是坐标向量)并返回一个数字。两个向量 w = [ w 1、 w 2、…、 wnx = [ x 1、 x 2、…、 xn 的点积定义为:

以这种方式表达上面的例子,一个 1 × 3 矩阵(行向量)乘以一个 3 × 1 矩阵(列向量)得到一个 1 × 1 矩阵,用它的唯一条目来标识:

从上图中,我们可以直观地将感知器网络想象成一个可以连续过滤前一层的网络,即,对前一层的决策进行加权和决策,以此类推,直到我们到达作为输出的最后一层。有时,输入可以被表示为一个感知器,只有一个固有的或恒定的偏差,没有权重或指向它的输入。早期,当感知器模型被创建时,人们认为 NAND 或 XNOR 不能被模拟,这使得创新的步伐处于休眠状态,后来人们发现所有的位运算都可以被模拟,或者事实上是展示任何模式的函数。随着多层网络变得明显,深度学习终于打破了它的休眠,并迅速收拾残局。

节点内部的主力—激活功能

顶部

对任何过程建模都是测量输出与给定输入的基本活动,以获得理解,更重要的是保证在已建立模型的情况下给定输入的结果的可重复性。本质上这个模型对我来说总是用简化的术语来说就是“曲线拟合”。有了输入和模型,您总是可以确定输出是什么。激活函数是“曲线拟合器”,在给定输入的情况下,它们根据定义的模型提供输出。同样,模型的变化导致不同的激活函数。

步进功能

一个基本的激活功能是步进功能,如下图所示:

方程式:

步进器或二进制是一个输出为 1 或 0(是或否)的函数。或者,它也被称为 Heaviside 阶跃函数或单位阶跃函数。该函数为所有负输入发出 0,为所有正输入发出 1。感知器使用这种激活来根据输入发出是否继续的信号。

线性函数

顾名思义,线性函数将给定的输入增加一倍。数学上,线性函数是其图形是直线的函数,即一次或零次多项式函数。

方程式:

该图表示线性函数 f(x) = 4x,本质上对输入有 4 倍的乘数效应。每当我们想要按比例缩放输入时,这在神经元中是有用的,但是它的用例很少。

Sigmoid 函数

Sigmoid 是一条 S 曲线,其中所有正值的输出都= 0.5,输出永远不会超过 1。对于 x 轴和-x 轴的较高值,y 值是一个渐近线。S 形的顶部是凹的,底部是凸的。sigmoidal 函数的定义非常广泛,但唯一的是它有一个实值输出,一个拐点和一个钟形一阶导数。

方程式:

一般来说,Sigmoid 函数是从逻辑函数获得的,逻辑函数的常见形状是“S ”,其等式为

在哪里

  • e =自然对数底(也称为欧拉数),
  • x0 =形中点的 x 值,
  • L =曲线的最大值,以及
  • k =曲线的逻辑增长率或陡度

在标准逻辑 sigmoid 函数的情况下,值将如下:L = 1,k = 1,x 0 = 0,给出上面的 Sigmoid 方程。

。这个函数的一个很大的优点是它是非线性的。在-3 和 3 的范围内显示线性,在该范围的其余部分逐渐变细到渐近线。它可用于将连续空间值转换为二进制范围之间的值,这种离散化方法对于抑制极端情况非常有用,其特征是在低激活时极限值为零,在高激活时极限值为一。目前,这个函数足以开始我们的神经网络之旅,但我们稍后将全面探讨其他函数及其适用性。

Sigmoid 优化背后的理念

对 sigmoid 的兴趣在于它可以在同一个函数中管理线性和非线性建模。在上图中,拐点在 0.5 处,由 z 表示。Sigmoid 曲线模拟了典型的规模经济问题。例如,如果制造商开始制造玩具,初始投资很大,回报率很低或接近于零,一旦公众开始购买玩具,销售额逐渐增加,并随着需求的增加而呈线性增长,此时供应不能完全满足需求,利润飙升至供应赶上需求的拐点,此时利润理想,供应超过需求,无论施加多大的影响,利润率都会下降,不能超过最大值。

这推测任何依赖于阈值的活动都是通过 sigmoid 函数进行优化的理想候选。让我们看看几个这样的候选对象,并将其与它如何帮助深度学习用例联系起来。如果竞选经理试图优化他们的广告支出,那么他们的目标函数将模拟一个结果,在这个结果中,他们希望把钱花在可能改变投票支持他们的候选人的州上。而花在最终失败的州上的钱则完全浪费了。Sigmoidal 函数也会以同样的方式运行,只建议在一个州花钱,如果他们能通过获得大多数选民的门槛。他们会预测,在一个很有可能受到影响的州投入大量资金,要比在两个州各投入一半资金要好,因为在这两个州,他们最终可能会两败俱伤。在从给定图像预测数字的“深度学习用例”中,每个像素或像素组共同跨越一个阈值将是有用的,如果使用分层 sigmiod 函数连续交叉验证它们,那么它可以以更少的错误完美地预测结果,并且在此过程中,我们的模型将学习得很好。

关于基础的一个温和的迂回——微分学

榜首

区别

在我们深入研究之前,我们必须回忆一下我们在高中数学中学习的关于微积分的知识——这对我们外行的大脑来说是一个可怕的主题,那么,这是一个带着乐趣和功利主义心态进行反思的完美时间,而不是我们在学校时代所做的死记硬背。

衍生品就是变化率。你从家到目的地需要多长时间?这些年人口增长有多快?人们有多富有?所有这些都在谈论变化率,一个对另一个。与上面的例子相比,它们是距离对时间,计数对年,美元对人计数。

顺便提一下,变化率也是斜率,如果将上述用例数据之一绘制在 x-y 轴、y 轴上的距离和 x 轴上的时间的图表中,则两个时间点之间的平均速度由所用时间内行驶的距离给出,这实际上就是斜率。如果你真的想找到某个特定时间点的速度,很难测量在无限小的时间片上走过的距离,因此没有办法计算变化率。但是差异化在这里很有帮助。

受到对复杂数学概念的清晰而简单的解释的启发,让我们借用一下学习的概念(感谢 MathIsFun site,by Ed。罗德·皮尔斯)以更清晰、有趣和简洁的方式解释衍生品。请确保你仔细阅读了每一篇文章,以回忆/刷新你当时所学的内容,并将微分学内化,这对理解神经网络的功能是至关重要的。

  1. 导数介绍 —讲述变化率,它与斜率的关系,我们如何得出给定函数的斜率
  2. 导数规则 —涵盖管理各种类型函数导数的规则。
  3. 幂法则——将幂函数去杂波的最简单方法
  4. 二阶导数 —理解速度、加速度和加加速度的二阶导数的绝妙方法
  5. 偏导数 —实际使用案例,使用我们熟悉的圆柱体了解偏导数的真实用途
  6. 可微分 —该测试允许检查曲线的可微分性,以确定斜率的存在
  7. 使用导数寻找最大值和最小值 —学习梯度下降和反向传播的基础

微分就绪计算器

Image Credit: MathIsFun

Image Credit: MathIsFun

如何理解衍生品:积、幂&链规则 —提供了一种直观的方式来理解我们在高中时一直记忆的积、幂和链规则。有了对微分学的坚定理解,我们再下一步!

基础——基本统计数据和减少损失

顶端

统计数字

在统计学中,您应该熟悉对位置和可变性的估计,这对于数据如何表示和变化以及如何缩放数据以实现标准化和加快分析至关重要。与这一“深度学习”之旅相关的基本概念的快速总结:

位置估计

具有测量数据的变量可能有数千个不同的值。探索数据的一个基本步骤是获得每个特征(变量)的“典型值”:估计大部分数据的位置(即它们的集中趋势)。

  • 表示 所有数值的总和除以数值的个数
    替代:平均值
  • 加权平均值 所有值的总和乘以一个权重除以权重的总和。
    替代:加权平均
  • Median 使一半数据位于上下的值。
    备选:第 50 百分位
  • 加权中值 使权重之和的一半位于排序数据上下的值
  • 稳健 对极值不敏感。
    替代:抗性
  • 离群值 与大部分数据差异很大的数据值。
    替代:极值

可变性的估计

位置只是概括一个特征的一个方面。另一个维度是可变性,也称为离差,衡量数据值是紧密聚集还是分散。统计学的核心是可变性:测量可变性,减少可变性,区分随机可变性和真实可变性,识别真实可变性的各种来源,并在存在可变性的情况下做出决策。

  • 方差
  • 平均值绝对偏差
    平均值与平均值偏差的绝对值的平均值。
  • 范围
    数据集中最大值和最小值之差。
  • 百分位数 该值使得 P 个百分比的值等于或小于该值,而(100-P)个百分比等于或大于该值。
  • 四分位距 第 75 个百分位和第 25 个百分位之差

数据缩放

来自维基百科:由于原始数据的取值范围千差万别,在一些机器学习算法中,目标/损失函数如果不进行归一化将无法正常工作。例如,大多数分类器通过欧几里德距离计算两点之间的距离。如果其中一个要素的值范围很大,则距离将由该特定要素决定。因此,应该对所有要素的范围进行归一化,以便每个要素对最终距离的贡献大致成比例。

应用要素缩放的另一个原因是,使用要素缩放时,梯度下降(我们稍后将更详细地了解这一点)的收敛速度要比不使用要素缩放时快得多。

  • mix-Max 的最小-最大缩放比例 公式:

这种方法在一个范围内(比如[0,1])重新缩放单个数据点。假设我们有一个包含 100 个数据点的数据集,x 是一个单独的数据点,min(x)是 100 个数据点中的最小值,max(x)是其中的最大值,min-max 缩放将每个 x 值压缩(或拉伸)到范围[0,1]内。有时该范围可以设置为[-1,1]。目标范围取决于数据的性质和分析中采用的算法。下图给你一个直观的说明。

Image Credit : Feature Engineering by Alice Zheng

单位换算或 L2 归一化 单位换算公式:

这是通过将每个数据点除以距离中心或平均点的向量的欧几里德长度而获得的。L2 范数测量坐标空间中向量的长度。勾股定理给出了距离

由于这种划分,数据在-1 到 1 的范围内正常化。

Image Credit : Feature Engineering by Alice Zheng

方差缩放或标准化 标准化公式:

平均值直观地表示数据的绝对分布,而标准差(SD/sd)给出了加权分布的概念。当数据的分布围绕平均值对称,并且其方差相对接近正态分布时,分布的度量是最有用的。(这意味着它是近似正常的)。在数据近似正常的情况下,标准偏差有一个规范的解释:68%的数据将在 1 个标准差内,95%在 2 个标准差内,99%在 3 个标准差内。问题是分布是宽还是紧?这给出了数据和可能出现的异常值的概念。
在这里,对于每个数据点,平均值被去除,并通过它们的标准偏差进一步加权。从本质上来说,是根据数据点的总体分布来调整每个数据点的中心值。
得到的缩放数据点被标准化为平均值为 0,方差为 1。如果原始特征具有高斯分布,则缩放后的特征是标准高斯,也称为标准正态。下图直观地说明了这个概念。

Image Credit : Mastering Feature Engineering by Alice Zheng

损失函数

可变性激发了预测偏差的测量方式,与标准偏差类似,损失可以被测量、缩放和标准化。损失函数的核心是衡量你的预测与原始值相比表现如何。现在,您有了一个工具来跟踪模型性能,同时调整其性能,以便将损失降至最低。非常基本的损失函数是找到原始值和预测值之间的绝对差值。这提供了对单个预测的良好判断(绝对误差— AE ),并且同样可以对所有预测进行平均,以获得平均绝对误差(MAE / L1 损失),并且平均它们的平方误差给出均方误差(MSE / L2 损失/二次损失)。

中小企业与制造业——一项分析

我们来取一个简单的函数 y = f(x) = 4(x),针对这个函数画一个图如下。还绘制了相应的预测值 y,以显示与实际值的偏差以及损耗、绝对损耗和平方损耗。还给出了统计值以及平均误差和均方误差。

这些公式是:
MSE=

MAE=

y^p=预测值(predicted),y =观测值(actuals)。

如果公式不是你的主食,你可以通过
MSE 找到 MSE & RMSE

  • 平方残差/损失。
  • 求残差/损失的平均值。

RMSE

  • 取结果的平方根。

MAE 和 MSE 都表示在不同的感兴趣单元中的平均模型预测误差稍有不同。这两个指标的范围从 0 到∞并且与方向误差无关。它们是负校准分数,这意味着值越低越好。取平均平方误差或简单平方误差的平方根有一些有趣的含义——因为误差是平方的,并且在它们被平均之前,MSE/RMSE 给大误差一个相对高的权重。这意味着当大误差特别不理想时,MSE/RMSE 应该非常有用。

查看上图,我们可以看到实际值和预测值的 MAE 和 MSE (RMSE 就是 MSE 的平方根)值。除了误差非常高的 2 个异常值之外,大多数预测是内联的。与 MAE 相比,MSE/RMSE 将会给异常值很大的权重,因为你会注意到平方损失远远高于他们更接近的损失。直观地,我们可以从损失函数角度解释预测性能,其中如果试图最小化 MSE,那么预测应该是所有目标值的平均值。但是如果我们试图最小化 MAE,这个预测将是所有观测值的中间值。中位数比平均值对异常值更稳健,这使得 MAE 比 MSE 对异常值更稳健。

使用 MAE 损失(特别是对于神经网络)的一个大问题是其梯度始终相同,这意味着即使损失值很小,梯度也会很大。这对学习没有好处。为了解决这个问题,我们可以使用动态学习率,它随着我们接近最小值而降低。MSE 在这种情况下表现很好,甚至在固定的学习速率下也会收敛。对于较大的损失值,MSE 损失的梯度较高,并且随着损失接近 0 而降低,使其在训练结束时更加精确(见下图)。)

MAE 损失

Image Credit: Heartbeat — MSE & MAE Loss

MSE 损失

上面是 MAE & MSE 函数的图,其中真实目标值为 100,预测值的范围在-10,000 到 10,000 之间。MSE 损失(Y 轴)在预测值(X 轴)= 100 时达到最小值。范围是 0 到∞。

神经网络中的一些损失函数

损失函数在稳定和提高给定模型的预测精度方面起着至关重要的作用。它们被用于将实际值和预测值之间的差异最小化。下面列出了深度学习中使用的损失函数的总结——名称、方程和它的作用要点。

请注意,y 是实际点或节点数据,yhat 是 y 的预测或函数输出,C 是所有数据点的总误差。

在回归中广泛使用,数据点的合成拟合线应该是使每个点到拟合回归线的距离总和最小的线。实际值和预测值之间的平方差称为“残差”,损失函数的目标是最小化 DL 中的残差平方和。

顾名思义,它是 MSE 值的一半,这是为了方便计算,我们稍后会讲到。

简单地求和,不求均方误差的平均值。也叫 L2 损失

它测量实际和预测的对数值的方差差异。当预测值和实际值都很大时,这很有用-使用对数的力量将指数值减少到渐进值。MSLE 对低估的惩罚多于高估。

实际值和预测值之间的绝对差值。用 MSE 计算一个好的梯度更容易,而 MAE 只给出一个常数。

很明显,实际值与预测值的百分比误差超过了实际值。当实际值为零时,不能使用。当实际和预测之间的差异很大时,它会超过 100%。该函数倾向于选择预测值过低的模型。

MAE 没有平均值,即没有除以 n

通常在二进制分类和神经网络中,输出是表示预期结果的概率的真实值,而不是给定事件的 0 或 1。或者可以很容易地计算出来——给定一组数据点,测井曲线损失衡量实际与预测的概率分布的差异。我们还可以假设实际数据点和预测数据点代表一种概率分布。从二进制分类的角度来看,y 是事件 A 或 B 的非此即彼标签(如果 A 发生,则为 1;如果不发生或 B 发生,则为 0 ), p(y)是其概率。左手边代表,标签乘以标签为 A 的概率,而右手边 1-标签值乘以为 b 的概率。进一步的直观解释此处和参考此处为从 softmax 导数的良好推导。

当模型输出是每个类别的概率而不是最有可能的类别时-其中它处理多类别,即每个输出将表示例如每个输出是猫、狗还是老鼠。Lj Miranda 博客中的理解 softmax 和负对数似然是一篇关于 soft-max 和 NLL 的好文章。我完全被那篇文章中的精彩画面迷住了。

Image Credit: Lj Miranda Blog

将以上两者联系起来,直接阅读直观的文章以获得简洁的解释。

从高中开始,我们知道 Cos 0 是 1,Cos 90 是 0,这表明余弦接近于 0 的给定的一组点比接近于 1 的点相似,在 1 的点上它们是完全发散的。这样可以忽略幅度差异,只考虑角度收敛。

损失及其对误差减少的影响以及与梯度下降的比较在这里详细讨论。接下来我们将更直观地处理梯度下降。

大优化—梯度下降

榜首

梯度下降

在提到 ML/DL 时,经常提到的算法是梯度下降法。因为它是如此的基础,所以大部头的书都已经涵盖了对它的说明、解释和驳斥,但是这里我们将通过两个例子来更直观地理解它,而不是你遇到的花哨的 3D 图表和动画。

在我们深入研究一个例子之前,梯度下降字面上是“一种穿越的方式”,这样我们就到达了谷底。形象地说,我们必须下降,并检查我们是否在最低处,如果不是,寻找下一个最佳下降方向,并重复迭代。一个警告是,如果我们被困在一个最低的最小值,但不是最低的(假设我们有一个可以看穿岩石的激光视力),然后折回并开始在将导致最低最小值的方向,全球最小值在谷底深处,谷底点。

假设我的山谷没有扭曲,是一维的,看起来像代表 y = x^2 的曲线,那么我们可以在下图中画出这个山谷。回想之前使用一阶和二阶导数找到最大值和最小值的过程。这里,斜率向右增大,向左减小。参考两条曲线,第一条是曲线 y = x^2,下一条是曲线 y = 1+2x-x^2.下图说明了这个想法。

了解坡度&在基本曲线上下降

正如我们注意到的,在上面的左图中,斜率是正的,并且随着我们向右移动而增大,随着我们向左移动而减小。为了找到全局最小值,这是一个简单的操作——在斜率图中直观地查看斜率达到零的位置,或者在数学上,如果斜率方程等于零,我们得到 x = 0,斜率最小的点是全局最小值。你可以在 x (=0),二阶导数= 2 这一点上应用二阶导数测试,当它大于 0 时,它是局部最小值,并且是一维的,它也是全局最小值。还要注意没有迭代过程,因此一维函数本身没有梯度下降。同样,右图给出了一个想法,当我们冒险寻找全局最大值时,抛物线方程会发生什么。这里,二阶导数= 2–2x,当 x = 1 时,斜率为零。应用二阶导数测试,在这一点上是-2,我们知道这是一个一维函数的局部最大值,也是全局最大值。

二阶导数不可能为零,除非 y = x(如上图所示),这里斜率是常数,没有最小值或最大值点,而是一个技术上未确定的点,称为鞍点。鞍点发生在多维函数中。

另一个重要的直觉是,在一维函数中,我们向左或向右移动,但从另一个角度来看,如果当斜率为正时,你向相反方向移动,你将达到最小值,即当斜率在某一点最陡时,然后向相反方向移动,陡度降低并达到零。这在多维函数的梯度下降中很方便。为什么重要——我们可以简单地将该点的最陡斜率乘以一个小因子,然后减去/加上,将 x 轴点向相反方向移动,并重新评估函数的斜率,看它是否变得不那么陡,并达到最小值,然后重复这一过程,直到达到最小值。

救援的直观例子——下降揭秘

榜首

简单函数 f(x,y) = x + y 的梯度下降

是时候回忆一下我们之前研究过的偏导数了。如果 f(x,y,z)= x ^ 4-3 XYZ,那么用卷笔字表示法:

现在让我们考虑一个函数 f(x,y) = x + y。将 x 和 y 的值分别初始化为 2 和 4,输出将是 6。

这里招手的是,如何调整 x 和 y 的值使泛函输出最小?公式很简单:

  • 新 x 位置=旧 x 位置—(小常数)* fn w . r . t x 的斜率
  • 新 y 位置=旧 y 位置—(小常数)* fn w . r . t y 的斜率

让我们用它的分量来区分 f(x,y)

这些偏导数可以被认为是作用在 x 和 y 输入上产生输出的力。当我们产生输出时,目标是达到最小或零输出(顺便提一下,此时 x 和 y 的斜率为零,二阶导数为+ve——回想一下我们之前的最小值/最大值探索,这里的输出和损失函数也是一样的)。让我们看看如何应用上述公式,我们假设 delta = 0.01。

x 和 y 的新位置导致 f(x,y) = x + y 的值为@ 1.99 + 3.99 = 5.98。与我们的起始值 6 相比,它略小。为了达到值 0,我们将利用梯度下降的重要特征,即迭代步骤,直到我们达到收敛。过程是重复的

直到 x+y 为零。因此,如果我们重复上述步骤 300 次,我们将得到 x = -1 和 y = 1,最终输出 x+y = 0,我们最终得到的 x 和 y 的值将给出最小值 0。

函数 f(x) = mx + C 的梯度下降

让我们举一个简单的例子,沉浸在内部工作,因此可以清晰地上升梯度下降。

根据在城市中央商务区(CBD)进行的一项调查,收集并在栏 A栏 B 中列出了办公空间租金价格,分别确定了以平方英尺为单位的办公空间面积(X)与以千美元为单位的每月租金收益率(Y)。

问题陈述:对于 CBD 的一个新办公空间,给定建筑面积,每月租金收益是多少?

绿线描绘了当前收益率价格与地板覆盖面积的关系。我们将使用一个简单的线性模型来预测给定其建筑面积(X)的新租金收益率(Y pred)。琥珀色线描述了预测给定区域产量的模型

Y pred = mx + C

绿线给出了基于当前调查的实际租赁收益(Y actual)。

绿色点和琥珀色点之间带有互连线,表示 Y 实际值和(Y 预测值)之间的差异,即预测损失或误差(E)。
我们有责任找到 C 和 m 的最佳值(称为权重),使其最适合 Y pred = mX + C 支配的预测线,以减少预测误差并提高准确性。我们将采用二次损失函数 RMSE 作为损失函数,考虑到实际值和预测值,需要将其最小化。

简单的说 HMSE =,

让我们利用梯度下降算法来找到权重 C 和 m 的最佳值,以最小化 HMSE。

这里列举了所涉及的步骤:

  1. 使用最小-最大归一化法将 A 列和 C 列中的数据分别归一化到 D 列和 F 列中,并将用作 X 和 Y
  2. 将 C 和 m 初始化为随机值,并计算每个数据点的平方误差(SE)
  3. 当 C 和 m 相对于原始随机值变化很小时,单独计算每条线/数据点' HMSE 变化',总变化构成所有数据点所有这些变化总和。
    求和后,我们可以将求和值除以数据点数来求平均值,但这是不必要的,因为它只是缩小了总变化,而且会影响我们随后引入的变化,如下一条语句所示。本质上,这可能会减慢收敛速度,因此我们可以停止 ar 求和,无需求平均值。在动态情况下,输入数据点的数量事先不知道,那么我们也不能平均,因此最好是左总结
  4. 调整相应的权重 C 和 m 减去乘以学习率的总“HMSE 变化”
  5. 使用新的权重来计算 se 的总量,并检查它是否在减少
  6. 重复步骤 3 和 4,直到总硒不再显著减少

详细步骤:

  1. 参考列 D、E&F——它们是对应于列 A、B & C 的最小-最大归一化租金数据
  2. 要拟合 Y pred = C + mx 线,首先使用随机种子值并计算预测损失 HMSE。HMSE 在所有数据点上,当应用于单个数据点时,公式中的 n 被去掉——只需计算出每个数据点的半平方误差,然后求和。SE 和 HSME 在这方面指的是同一个。

下载源 Excel

3.计算重量的误差梯度 w.r.t,在我们的例子中是 C 和 m

我们稍后将分别对上述方程进行演算。上述两个等式给出了“C 和 m”的变化,并且是简单的梯度,其将运动方向从 w.r.t .指向 SE。参考最后两栏的计算

4.用梯度改变权重 C 和 m,以达到最小的最佳 SE 值。

我们将调整 C 和 m 的初始随机值,使我们朝着最优 C 和 m 的方向前进。

更新规则:

这里 lr 是学习率,因此:
New C = 0.52–0.01 * 5.040 = 0.47
New m = 0.81 = 0.01 * 2.279 = 0.79

5.使用新的权重来计算 se 的总和并检查其是否减少-检查上图中 HSME 值 id 的总和是否减少

6.随着步骤 3 和 4 的重复,直到总 SE 不再连续显著减少,然后我们达到了具有最佳预测率的最佳 C 和 m

梯度下降背后的微积分

平方损失函数

每个数据点的 HMSE 最终应用

一阶导数是 w.r.t 'C '是

现在让我们算出偏导数:

应用不同的损失函数

如果您决定在上述努力中应用不同的损失函数,我们可以尝试 Log-Cosh 函数,它也支持一阶和二阶导数。如果你应用 MAE 或 RMSE,两者都产生一个常数,因此从学习和收敛的角度来看,在神经网络梯度下降过程中是有益的!

二阶导数是简单地将上述项乘以 x

梯度下降变体

批量-普通梯度下降

如在之前的沉浸式示例中,有 12 个数据点,并且模型参数 m & C 仅在结束时更新,即,在针对 12 个数据点的整个记录集计算通过每个数据点的所有导数和损失值之后。本质上,这是普通的批处理 GD,其中模型参数仅在所有数据点计算完成后更新——处理所有数据点被视为一个时期。

小批量—梯度下降

在上面的例子中,在 12 个数据点中,我们可以先处理 4 个数据点并更新模型参数,然后是接下来的 4 个数据点来更新参数,依此类推,直到所有的数据点都用完为止。基本上,在每个小批量(在我们的例子中是 4 个数据点)被处理之后,模型参数被更新,因此在一个时期内,参数被更新 n 次,其中 n =总数据点/小批量数据点。

随机梯度下降法

通常,SGD 使用单个数据点计算梯度,但大多数 SGD 应用实际上使用几个样本的小批量。因此,在每个时期开始之前,所有数据点都被随机打乱,然后进行小批量处理。对于具有大量局部最大值/最小值的误差流形,SGD 工作得很好(与批量梯度下降相比,现在有种高级方法)。这样,使用减少数量的数据点计算的稍微更嘈杂的梯度倾向于将模型从局部最小值中拉出到希望更优化的区域中。单个数据点确实有噪声,而小批量数据往往会平均消除一点噪声。因此,在使用小批量时,加加速度会降低。当小批量可以避免一些不良的局部最小值,但足够大以包括全局最小值或性能更好的局部最小值,并且理想地引导我们更容易落入放置最佳最小值的更大和更深的吸引盆地时,可能可以达到良好的平衡。

SGD 类似于对选民的横截面进行抽样以获得预测,而不是进行实际的选举。与批处理相比,学习率是惊人的,假设我们的总数据点是一百万(1M ),如果小批量是 10K,那么我们得到的加速因子是 1M/1K = 1000。加速因子可能不准确,并包括统计波动,但足以引导成本函数逐步降低。SGD 的一个好处是计算速度更快,并且可以并行化。

当构建梯度下降算法时,可以设置最大历元计数(迭代计数)以及损失因子差,它们协同工作以达到收敛或停止无休止的处理,无论哪一个先发生。点击进一步探索这些变体

系综引导的来回-前馈&反向传播

榜首

链式法则

在我们之前的梯度下降示例中,我们看到了一个简单的函数 y = mx + C。这可以想象为一个单节点激活函数,它简单地采用一个输入来提供一个输出,本质上是将输入缩放 m 并添加一个偏差 c。
现在让我们想象一组这样的激活函数相互连接,一个的输出是下一个的输入。这是一个简单的线性网络,可能只有很少的节点。在我们的例子中,我们将考虑如下所示的 4 节点线性网络。同样的想法也适用于此——我们需要找到正确的权重和偏差,以便优化整体函数的输出,使原始输出和“互连函数”输出之间的任何误差最小。
为了演示这个想法,请看下图——这是一个具有输入节点和输出节点的两层隐藏式神经网络。

每个节点都是与其相连的前一个节点的功能。如果我们要更改输入节点中的输入或权重值,这种更改将向下级联,因为每个节点都将更改它们接收的输入,并最终反映在输出中。因此,输出变化可能看起来很复杂,但直觉上每个节点都会放大级联变化。由于每个节点相互依赖并且具有函数依赖性的概念,我们可以用数学公式表示一个复合函数来表示该网络的行为:

将所有等式合并为一个等式:

由于中间节点不是单独工作的,并且依赖于来自前面节点的输入,如果我们想要了解 w1 的微小变化对输出的整体影响,那么我们必须对输出 w1 从最后一个节点到第一个节点依次应用导数。合成的复合导数将是:

让我们在这里暂停一下,看看如何利用链式法则的详细例子。

链式法则在起作用——用一个例子来理解

微分链规则是反向传播的基础,获得直观的理解对全面理解神经网络大有帮助。通过下面的例子,我们可以解释链式法则是如何工作的,并了解因素的变化如何导致最终产出的变化。在下面的图表中,我们有 3 个以线性方式连接的激活函数,基本上任何输入通过它们级联,以给出遵循所述规则的输出。很容易理解:

  • i/p 表示整个线性网络的输入
  • 任何 i/p 被 w1 加权并补充以偏置 b1,共同表示为 z1,被馈送到激活功能#1
  • 该函数将任何输入缩小 1/8 倍,得到 h1 作为输出
  • 相同的模式重复,直到输出 h3
  • 最后,我们有一个误差函数,其中如果我们知道实际 h3 和预测 h3,就可以计算误差/成本,但现在我们忽略这部分

让我们列举从输出到输入的上述线性网络的方程

  • h1 = f1(w1 * x+B1)
    =(w1 * x+B1)/8
  • H2 = F2(w2 * h1+B2)
    =(w2 * h1+B2)/7
  • O = H3 = F3(w3 * H2+B3)
    =(w3 * H2+B3)/9

如上所示,基本上你在 x 轴上的任何摆动都会按照我们的例子缩小,然后由于每一层的重量和偏差而略微上升。还有一点需要注意的是,“h”不知道 x 发生了什么,只是改变了“g”的变化,“g”也不知道 x 的变化,只是摆动“f”等等。现在 x 的任何变化都会被 f 的变化率放大,然后是下一层等等。
O = h(g(f(x)))h w . r . t . x 的变化为

链式法则不仅仅是分母和分子的单位相消——它是一个波动的级联,在每一步都可以放大、缩小或调整。链式法则适用于任何数量的变量(f 取决于 g 取决于 h,依此类推),只是级联摆动。把它想象成放大不同变量的视角——从 dx 开始向上看,你可以想象在波动到达 h 之前需要的整个调整链。

O w.r.t w1 的任何变化(w1 也是上述 x 的放大值)可以用链式法则写成,如下所示:

以上表格用数字数据总结了链式法则背后的思想

  1. A 列:考虑这个线性网络的几个输入
  2. B 列:计算 h1
  3. 列 C:计算 h2
  4. D 列:计算 h3 —最终输出
  5. E 列:根据 w1 的变化计算新的 h3—只需计算 h3 相对于 w1 的变化率,然后乘以 w1 的变化,最后将其加到 H3 上,即可得到新值
  6. 重复的行通过合并新的 w1 值并进行所有端到端计算来计算 h1、h2 和 h3,从而得出 h3 —注意 E 列数据和 D 列标题“新 h3”下的数据(在修改的 w1 上)是相同的
  7. F 列:使用近似值计算导数,如公式所示:

这里 O 只不过是计算 h3
用近似法和链式法则计算的值与图示相同

反向传播——反向链规则

在我们之前的线性节点网络中,让我们添加一个名为成本函数的最终函数。这将获得最终输出(使用给定模型(我们的线性网络)计算的 y 值也称为预测值),并计算与给定原始输出(原始 y 值)的差异。由于这是另一个函数依赖关系,现在我们的成本也受到输入、权重和偏差的影响,因此是它们的函数。

现在让我们在这个网络的末端附加一个“成本函数”,如上所示,很明显这个函数的输出也依赖于输入和中间权重。因此,我们也可以将相同的导数公式扩展到我们的成本函数:

在“通过示例理解链式法则”一节中,我们能够使用近似法找到复合导数。即通过将 w1 改变一个小值并计算新的输出,因此。

价值。
现在会出现一个问题,如何应用近似原理来计算真实神经网络中成本 w.r.t .权重的变化。走这条路是可行的,但是会导致计算速度变慢。让我们假设一个大的权重网络,为了找到成本 w.r.t w1 的变化,我们需要找到所有权重承载节点处的 C 和 C(具有δw1 ),对它们求和,得到它们的最终数量,然后应用近似公式。如果节点数以百万计,近似算法会嘎然而止,而链规则方法在将导数传播回网络时会很方便,通过梯度下降快速找到权重,以降低总成本,如下所示。

神经网络

网络

是时候深入研究真正的神经网络了,根据目前的理解,算出方程式,并将其应用到 python 代码中。符号和代码是基于由迈克尔尼尔森的开创性的书。我不得不花相当多的时间来理解 python 代码中的符号、等式和推论。我在这里所做的是用更好的可视化来进一步简化它,这样它就可以很快被吸收,并且这本书可以被用作参考资源。

想象一个 4 层的网络(如上所示):

  1. 第 0 层—3 个节点的输入层
    输入没有转换,它们只是被传递
  2. 第 1 层——包括 4 个神经元
    对于该层中的每个神经元,第 0 层输入被加权,最后添加一个偏置并馈入激活函数
  3. 第 2 层——包括 3 个神经元
    对于该层中的每个神经元,第 1 层输入被加权,最后添加一个偏置并馈入激活函数
  4. 第 3 层——包括 2 个神经元
    。对于该层中的每个神经元,第 2 层输入被加权,最后添加一个偏置并馈入激活函数

让我们给我们现在所说的赋予适当的符号,并用数学方法恰当地表达出来,这很容易理解。考虑该网络的输入用例也是很好的,但不需要完全匹配输入和输出,而是提供网络将如何处理整体数据的上下文和直观理解。
MNIST 是深度学习的标准火炬手数据集——它包含 60K 张手写数字的图像和每张图像的注释正确数值。

  1. 输入是一个 28×28 像素的图像
    转换成一个 784 个输入的阵列,存储 0 到 1 之间的灰度值
  2. 输出表示 0–9 之间的数字
    转换为 10 个输出的数组,每个输出为 0 到 1 之间的值,大于 0.5 的值表示该数字按顺序排列。在某种程度上,输出也可以被认为是该数字出现在 i/p 图像中的概率

数学表达的网络

一个完整的网络和完整的符号在这里给出,并得到一个完整的景观点击图片。

基本定义

现在,让我们一步一步地定义所有的基本要素——输入、权重、偏差,请参考下图

  • 输入(在第一层)
    输入= x

    通常是原始的或归一化的数字数据,在这一层中用 x 表示。还要注意,在这一层中,没有激活、权重和偏差,因此所有这些都为零,我们也可以说在这一层中:

我们随后会看到 z 和 a 是什么

  • 行和层(列) Rows = j 和/或k Layers =LL
    j始终与当前层关联, k 是指第 (l-1)层L’表示最后一层或一层的集合。
    还要注意的是,在上图中,神经元并没有从顶部等距排列,而是很好地放置在相应的行和层中,以匹配我们稍后将看到的矩阵表示。
  • 重量

每个神经元从前一层神经元的输出中获得大量输入,然后将偏差合并为输入。j 和 k 符号和以前一样,如下图所示

  • 偏置

来自前一层的所有加权输入被相加并加到 j 行中 l 层节点的偏置,被馈送到 l 层和 j 行的神经元。

其他定义

现在让我们一步一步地定义下一个层次——组合输入、激活、输出、成本函数,请参考下图。

  • 神经元的输入

  • 激活

  • 成本

  • 从 MNIST 数据集的角度来看,输入的数量:50k 个训练数字图像,每个都单独表示为 784×1 的实数矩阵,该矩阵在 0 到 1 之间,是像素的灰度值

  • 从 MNIST 数据集的角度来看,输出:经过我们的神经网络后,是数字输出-10 的数组-代表 0 到 9 之间的每个数字-其值也在 0 到 1 之间,这是特定数字的概率值
    输出数量(对于给定的图像输入)= 10 个输出

  • 从 MNIST 数据集的角度来看,原始输出:给定图像的经过注释和验证的数值-我们可以将其转换为 10 个值的数组,每个值表示数字 0 到 9,其中对应的数字为 1,其余的数字为零
    原始输出的数量(对于给定的图像输入-表示为每个数字为 10–1 的数组)= 10 个输出

  • 从 MNIST 数据集的角度来看,输入/样本计数:50K 个训练样本

由于成本是输出激活的函数,我们可以写为:

反向投影方程推导

以链式法则为例,我们可以说,重量/偏差的任何波动都会影响组合输入

这反过来影响产量

最后影响成本/误差

作为推论,输入的任何变化都会影响输出和成本,输出的任何变化也会影响成本,不管重量/偏差的变化,也就是说,每个中间组件都会影响它的邻居和通道中的所有其他组件。相反,最终成本的任何变化都依产出、投入、权重和偏差的顺序而定。将 w.r.t .枚举到第 l 层节点 j 中的上述符号:

变化和影响的可能组合

当发现成本的变化时,权重/偏差的变化产生 4 个重要的等式,包括 反向投影 算法— BP1…BP4。

成本与重量的变化

参照上图,让我们来算出第一个也是最重要的等式——成本变化与重量变化的关系

成本相对误差的变化

最后一层输入的成本变化

中间层输入的成本变化

我们可以使用全导数,但这很繁琐,我们将看到如何通过在单线多层网络上应用链式法则来推导,并将其用于多行多层网络,理解其背后的直觉,但现在等式如下:

  • 向量形式:

  • 基于网络中的每一行:

BP2 —直观推导—单行网络

下面让我们考虑一个线性网络,它有四个神经元,每个神经元都有权重、偏差、输入/激活和输出,如图所示。上标代表它们所在的层。

向前传递:激活

反向传递:优化重量以降低成本

根据梯度下降和链式法则,我们知道我们需要计算导数,从而计算“w”和“b”中的变化,以使成本最小,即——回想一下,w 中的任何波动都会影响通道中的所有中介,我们最初尝试找到最后一层的成本变化。对于单排神经元多层网络,更容易计算中间值。按链规则:

现在,我们可以总结重量的“反向传递”,类似地,您也可以测试偏差(鼓励读者进行详细的测试,为了简洁起见,省略了测试),如下所示:

BP2 —直观推导— 2 排 4 层网络

让我们关注第 4 层,根据我们之前的理解,我们可以计算出如下激活:

计算从第 4 层到第 3 层的反向通路,总结如下:

反向投影—一种不同的拍摄方式

如上图所示,激活 w.r.t 第 3 层(从 3 到 4): 只不过是第 4 层权重乘以第 3 层输出并加到第 4 层偏置上的和,反馈给激活函数,给出第 4 层的输出,即

反向传播 w.r.t 第 3 层(从 4 到 3): 是第 4 层权重转置乘以第 4 层成本变化 w.r.t 输出第 3 层激活差异的 hadmard 乘积。下图对此进行了说明。

裸神经网络的内部工作——与代码匹配的矩阵

顶端

我们神经网络上的 MNIST 数据集—节点和层

类似于“hello world”程序对任何语言都是事实,MNIST 数据集是神经网络学习的事实。它包含 60K 训练图像和 10K 测试图像。这些图像被标准化以适合 28×28 像素的分辨率,并进行抗锯齿处理以给出灰度。我们将继续构建一个神经网络,其:

Generated with NN-SVG Tool

  1. 第一层——输入层:
    将是一个 784 节点——以线性 784×1 阵列矩阵的 28×28 像素边界框图像为特征
  2. 第二层—隐藏层

3.第三层—隐藏层
将是 40 个节点的隐藏层—权重矩阵将是(40×100),偏差矩阵将是(40×1)

4.第四/最后一层—输出层
将是 10 个节点的输出(伪隐藏)层—权重矩阵将是(10×40),偏差矩阵将是(10×1)。如此选择最后一层,使得每一层将代表一个数字(数字 0…9 中的任一个。因为该值是实数,所以输出表示该特定数值的概率,比如说大于 0.5 是肯定的,小于 0.5 是不可能的。

我们将使用上述神经网络架构来理解迈克尔·尼尔森的书中给出的 python 代码。这在 python 中提供了一个紧凑、简单的最小神经网络实现。

  1. 将下面的 repo 克隆/复制到本地目录
    https://github . com/mnielsen/neural-networks-and-deep-learning . git
  2. 移动到您克隆的本地目录,并从这里启动 python 2.6 或 2.7
  3. 运行以下代码片段开始:

Python 中的整体 NeuralNet

查看 network.py 的源代码,并按照下面提到的函数进行操作,以便更好地理解 python 逻辑的功能!

  1. SGD 函数
    (循环遍历次数)
    a .参数:
    training_data —相应格式化的训练数据矩阵
    epochs —对整个数据集进行迭代的次数
    mini_batch_size —顾名思义
    eta —学习率乘数
    test_data —相应格式化的测试数据矩阵—用于评估学习到的权重和偏差
    b .混洗所有 50K 个训练记录(每个包含 784
  2. update_mini_batch 函数
    (循环每个小批量计数)其中小批量计数=总训练样本除以小批量大小
    a .将权重和偏差矩阵初始化为零
    a .执行反向传播(包括前馈和反向传播)。循环每个训练记录+其小批量输出记录
    c .获取增量矩阵并添加到之前的增量矩阵(针对权重和偏差)
    d .计算 GD 并从权重和偏差中减去
  3. 反向传播函数 a .执行前馈——围绕每层权重/偏差循环计算每层的输入&激活矩阵
    b .执行反向传递
    c .首先计算最后一层增量
    d .从倒数第二层循环到第一层&计算每层权重和偏差的增量矩阵

前馈和反向反馈-用 Python 计算的矩阵

4。评估函数 a .使用 SGD 计算的权重和偏差,对训练输入
执行前馈 b .获取最终输出并将其与实际输出
c .报告比较的正确结果计数

希望它对你的深度学习之旅有所帮助,如果你想联系,请给我发电子邮件

学习曲线回溯—参考文献和致谢

榜首

这些是我阅读、思考和吸收的书籍、博客和代码,因为我试图理解深度学习,并希望通过在这里陈述它,可能有助于在你的旅程中引用它们,同时感谢所有类似地帮助打造深度学习的人。

书籍

  1. 神经网络和深度学习书籍
  2. 数据科学家实用统计
  3. 掌握特色工程

代码

  1. 简单的 GD 代码
  2. 尼尔森书中的神经网络代码

激活功能

  1. 深度学习的基础——激活函数
  2. 立方体功能

损失函数

  1. 所有机器学习者都应该知道的 5 个回归损失函数
  2. 原木损耗
  3. 神经网络中的损失函数
  4. 余弦相似性——理解数学
  5. 二元交叉熵
  6. 交叉熵损失和 Softmax

梯度下降

  1. 机器学习 101:梯度下降的直观介绍
  2. 梯度下降:你需要知道的一切
  3. 梯度下降——初学者指南
  4. 保持简单!如何理解梯度下降算法
  5. 线性回归

反向传播

  1. 神经网络和反向传播浅显易懂
  2. 神经网络&反向传播算法,讲解
  3. 我试图理解反向传播

原载于 2019 年 4 月 29 日http://avantlive.wordpress.com

故障检测,即预测健康管理和测试方法

原文:https://towardsdatascience.com/fault-in-the-stars-c7308ba1b840?source=collection_archive---------29-----------------------

In most cases, machines can’t tell you when they’re sick

机器什么时候会出故障

“预测健康管理(PHM)是对系统状态的询问,并使用潜在损坏的非破坏性评估来评估已部署系统中的产品寿命。系统健康状况通常在实际操作环境中进行评估。”

(http://cave . auburn . edu/RS rch-thrusts/proportional-health-management-for-electronics . html)。

这就是 PHM 的定义,至少谷歌是这么认为的。有人称之为“故障检测”,有一段时间我确实这么叫它,直到我了解到它不仅仅是能够检测故障何时发生。它还根据收集的数据预测机器的剩余寿命。

这是一个有趣的话题,考虑到几乎每一个制造的物品或它被制造的物品都受益于知道它什么时候会出问题。随着我的兴趣被激起,我对方法做了一些研究,并在我能找到的几个数据集上进行了一些实验(由于专有信息,很难找到这样的数据)。有了这个,我想采取一些方法。首先,进入直接故障分类,然后预测剩余使用寿命(RUL)。

我还应该指出,PHM 的核心也是时间序列分析。

故障分类

一言以蔽之,故障分类基本上是一个二元分类问题。这种观察有错还是没有错?为了节省您的时间,我将简单介绍一下这个模型,给出一个简单的结果,并在最后贴上一个 Github 的链接。

我为这个特定模型选择的模型看起来像这样:

Fault Classification Model

只是为了突出可能不太明显的项目:

滤镜

我用来预处理数据的滤波器是卡尔曼滤波器。有许多论文详细介绍了所涉及的数学,但总而言之,它有助于消除传感器数据中的“噪声”。在自动驾驶汽车传感器和其他一些应用中,它被大量用于接收信息。这里有一篇关于卡尔曼滤波器的中型文章 https://Medium . com/@ jae ms 33/understanding-Kalman-filters-with-python-2310 e 87 b8 f 48,Github repo 用 python 对其进行了编码。

上/下采样

在这种特殊情况下,不到 1%的数据实际上是错误的。像这样的问题,SMOTE 自 imblearn 库https://unbalanced-learn . readthedocs . io/en/stable/generated/imb learn . over _ sampling。SMOTE.html将从少数民族标签中随机生成新样本。

结果如下:

Precision: .99 , Recall: .99, F1: .99, CV: 95.4

剩余使用寿命

这一部分提出了更大的挑战。RUL 根据以前周期的数据预测机器的寿命。为此收集的数据来自 PHM 协会 2008 年的一次实际比赛。同样,查看 Github 来查看数据和完整的笔记本。

我为这个模型选择了一条更复杂的路线:

Remaining Useful Life Model

以下是不太明显的步骤:

目标工程

这个特殊的数据集没有现成的训练目标。所以我做了一个:

  1. 利用可用的周期
  2. 对每个单元进行反向排序
  3. 转换成剩余寿命的百分比(重要的一步)

xgb 回归器/特征工程 g

这两个为什么在一起?基本上,这是一个模型堆叠的例子。对于像这样的回归问题,分类数据可能是一个小烦恼。通过训练好的回归网络运行分类数据并使用这些输出作为另一个特征是有意义的。最终,它确实在损失改善方面发挥了相当大的作用。我测试了一个调优的 XGBRegressor 和一个深度神经网络。令人惊讶的是,两者的结果非常接近。

Unit 1 results — test set RMSE between 49–55

两者的结果因单位而异,有些单位的 NN 优于 XGB,反之亦然。

外卖

如果从这篇文章中有所收获的话,那就是我们发现的这些收获:

  1. 两个数据集都没有关于所采取措施的细节——理论上,不同类型的测量(压力、温度、振动)会有不同类型的方法,适用于适当的预处理。
  2. 仅仅从视觉上,你可以看到神经网络是如何学习的。最初的几次迭代偏离很远,随着它的进展,它逐渐接近目标。
  3. 这个领域的机会是无限的。在给定信息的情况下,这两个模型的具体程度,可以肯定地说,任何机器和/或材料在某个时候都会有一个为其设计的模型。

关于这个主题有大量的研究,也有许多方法。我采用的方法可能不是最好的方法,我会继续研究这个课题。欢迎所有评论。感谢您的宝贵时间!

Github 位于:https://github . com/Jason-M-Richards/Fault-Detection-Methodologies

恐惧告诉我们必须做什么

原文:https://towardsdatascience.com/fear-tells-us-what-we-have-to-do-ec016aecdbf2?source=collection_archive---------43-----------------------

我 2019 年 9 月 30 日至 2019 年 7 月 10 日的深度学习自学

我是一名数学讲师,也是一名有抱负的数据科学家,希望参与人工通用智能研究,本周我决定开始每周写一篇博客,记录我一直在做的事情,既是为了自己参考,也可能是为了帮助走上类似道路的其他人,遵循 fast.ai 的雷切尔·托马斯的建议,“写一篇能帮助你在六个月前阅读的博客。”

我有一个纯数学的硕士学位,但我没有太多的统计知识,所以我正在通过可汗学院的“统计和概率”课程学习,我还通过 fast.ai 的“程序员实用深度学习”学习深度学习。最后,我正在使用 Udemy 上何塞·波尔蒂利亚的“数据科学和机器学习的 Python boot camp”学习 NumPy、Pandas、matplotlib 和其他数据科学的 Python 库。

在过去的几周里,我倾向于只关注其中的一个,直到我陷入困境,然后跳槽去做另外一个。对一些人来说,这种工作流程可能是高效的,但我可以感觉到自己在使用它来避免在处理困难的事情时冒出的自我怀疑的情绪,我最后提醒自己这句话:

~Steven Pressfield, The War of Art: Break Through the Blocks & Win Your Inner Creative Battles (https://stevenpressfield.com/books/the-war-of-art/)

所以上周,我为每周的每门课程设定了具体的学习目标。我认为避免我在自己身上观察到的两种次优学习行为会更容易:完全避免困难的事情,而选择简单的事情,或者——在有用性钟形曲线的另一端——在困难的事情上花费太多时间,在其他事情上毫无进展。用机器学习的术语来说,我需要把我自己的学习速率调得更高,以避免陷入局部最小值!

每周,我计划做:
一堂来自 fast.ai 的课
一个单位的统计数据
Python 课程的 0.5–1 个部分(一些部分是简短的,其他部分是完整的项目)
阅读一篇深度或机器学习论文

当我真的不想做某件事时,我会使用番茄工作法:我只要求自己做 25 分钟,然后我可以停下来或者休息一下。前 25 分钟几乎总能让我克服情绪上的阻力,之后我会更容易坚持下去。这个技巧也帮我完成了研究生期间的家庭作业!

所以上周,我做了 fast.ai 的“程序员实用深度学习”第四课,你可以在这里详细阅读我的经验。
我还学习了可汗学院统计学课程的“探索双变量数据”和“研究设计”单元,学习如何计算和解释最小二乘回归线,以及实验研究和观察研究。

在 Udemy 上的“Python for Data Science and Machine Learning boot camp”课程中,我完成了第 5-7 节,复习了我在 Coursera 上的 Deep Learning . ai“Deep Learning”课程中学到的 NumPy 语法,并练习了用 Pandas 查询数据。我以前学过一些 SQL,但没有意识到 Pandas 和 SQL 有多少共同点。然后我开始想知道什么时候一个比另一个更好用,并发现这个帖子非常有帮助:

使用哪种工具取决于你的数据在哪里,你想用它做什么,你自己的优势是什么。如果您的数据已经是一种文件格式,就没有必要使用 SQL 做任何事情。如果您的数据来自数据库,那么您应该通过以下问题来了解您应该在多大程度上使用 SQL。

~《SQL 与熊猫》(https://towardsdatascience.com/sql-and-pandas-268f634a4f5d)作者凯丽·史密斯

我选择阅读的论文——“用好奇心驱动的深度强化学习学习温和的物体操纵”——来自 DeepMind ,作者是 Sandy H. Huang、Martina Zambelli、Jackie Kay、Murilo F. Martins、Yuval Tassa、Patrick M. Pilarski 和 Raia Hadsell。你可以在这里 查看我做的笔记和我阅读时的疑问.)。这项工作围绕着使用深度强化学习来教机器人轻轻地处理物体。

这是我第一次阅读深度或机器学习论文,事实证明这是一个非常棒的选择。即使对于外行人来说,整体想法也是直观的,更好的是,实际实验的模拟和视频使得方法和结果易于理解。但是当我阅读的时候,我仍然有很多问题,并且学到了很多。

在这篇论文之前,我没有读过多少关于强化学习的东西,我不明白它会有什么帮助。奖励?处罚?为什么?为什么都是?我不明白为什么一个机器人执行一项特殊的任务需要“奖励”,更不用说“痛苦惩罚”了。现在看来很明显,我明白了,但这确实是该项目的“学习”方面:如果我们提前知道机器人应该使用多少力来处理一个物体,我们就完成了。但是,如果我们希望能够让机器人处理一个它以前从未遇到过的物体,而不必为它采取的每个动作指定一个力的大小,我们必须有一种方法让它知道它什么时候做对了。

当学习者的一个实例(“代理”)被训练与一个物体接触时只有奖励,它会经常过度用力。然而,只对过度用力进行惩罚的训练,会导致代理人完全避免与物体接触。受儿童发展研究的启发,作者为一个代理人设立了一个奖励,当它预测到它应该不正确地使用的力量时,以真正的金发女孩的方式,三种奖励类型的组合是赢家。代理人对疼痛惩罚感到“好奇”,并试图探索不同的力量来找到“恰到好处”的力量。

很难表达这篇论文读起来有多有趣,以及瞥见人类心理学如何被用来推进人工智能是多么令人着迷。我们称之为人工“智能”,因为我们明确理解这种体贴是人类的努力,但令人难以置信的是,它是如何实际实现的。

我是加州大学东湾分校的数学讲师,也是一名有抱负的数据科学家。在 LinkedIn 上和我联系,或者在 Twitter 上和我打招呼。

使用 SVM 权重的特征消除

原文:https://towardsdatascience.com/feature-elimination-using-svm-weights-c287c16a5151?source=collection_archive---------23-----------------------

特别是对于 SVMLight,但是这种特征消除方法可以用于任何线性 SVM。

Figure 1: a random example of accuracy based on the number of SVM features used.

大约在 2005 年至 2007 年,我在写硕士论文时,不得不根据 SVM 模型计算特征权重。这是在 2007 年开始的 SKlearn 之前。这个想法是基于算法认为影响最小的特征来迭代地删除冗余特征,看看我们可以在不牺牲性能的情况下删除多少。今天,您可以在 SKlearn 和其他包中轻松地选择这些类型的特性,但是,如果您坚持使用 SVMLight,您可以使用下面的代码。总的来说,这种方法今天仍然有效。

当时我们有两个选择,SVMLight 或者 LibSVM。我选择了 SMLight。Thorsten Joachims 发布了一个 Perl 脚本来计算 SVM 重量,但我使用的是 python,我用 Python 重写了他的脚本,他还在他的网站上放了一个下载链接

可以在这里找到 Perl 脚本原文:http://www.cs.cornell.edu/people/tj/svm_light/svm_light_faq.html 和 Python 脚本:http://www . cs . Cornell . edu/people/TJ/SVM _ light/SVM 2 weight . py . txt

使用该脚本将获得所有要素的权重。这在以后非常有用,正如您在下面的伪代码中看到的,您可以系统地消除功能:

  1. K = 50%
  2. 在对所有当前特征进行训练之后,选择具有最高 SVM 权重 s 的 K 个特征和具有最低(最负)SVM 权重的 K 个特征
  3. 再培训
  4. 基于看不见的数据集测量准确性
  5. 迭代:转到 2。当没有更多功能可供选择时停止。
  6. 选择如图 1 所示的最佳“弯头”。在本例中,重点是 128 个特征允许您获得与所有特征相同的精度。

您会注意到,仅使用您的特征子集就可以获得更高的预测结果。这是特征选择的本质。

# Compute the weight vector of linear SVM based on the model file
# Original Perl Author: Thorsten Joachims (thorsten@joachims.org)
# Python Version: Dr. Ori Cohen (orioric@gmail.com)
# Call: python svm2weights.py svm_modelimport sys
from operator import itemgettertry:
    import psyco
    psyco.full()
except ImportError:
    print 'Psyco not installed, the program will just run slower'def sortbyvalue(d,reverse=True):
    ''' proposed in PEP 265, using  the itemgetter this function sorts a dictionary'''
    return sorted(d.iteritems(), key=itemgetter(1), reverse=True)def sortbykey(d,reverse=True):
    ''' proposed in PEP 265, using  the itemgetter this function sorts a dictionary'''
    return sorted(d.iteritems(), key=itemgetter(0), reverse=False)def get_file():
    """
    Tries to extract a filename from the command line.  If none is present, it
    assumes file to be svm_model (default svmLight output).  If the file
    exists, it returns it, otherwise it prints an error message and ends
    execution.
    """
    # Get the name of the data file and load it into
    if len(sys.argv) < 2:
        # assume file to be svm_model (default svmLight output)
        print "Assuming file as svm_model"
        filename = 'svm_model'
        #filename = sys.stdin.readline().strip()
    else:
        filename = sys.argv[1] try:
        f = open(filename, "r")
    except IOError:
        print "Error: The file '%s' was not found on this system." % filename
        sys.exit(0) return fif __name__ == "__main__":
    f = get_file()
    i=0
    lines = f.readlines()
    printOutput = True
    w = {}
    for line in lines:
        if i>10:
            features = line[:line.find('#')-1]
            comments = line[line.find('#'):]
            alpha = features[:features.find(' ')]
            feat = features[features.find(' ')+1:]
            for p in feat.split(' '): # Changed the code here.
                a,v = p.split(':')
                if not (int(a) in w):
                    w[int(a)] = 0
            for p in feat.split(' '):
                a,v = p.split(':')
                w[int(a)] +=float(alpha)*float(v)
        elif i==1:
            if line.find('0')==-1:
                print 'Not linear Kernel!\n'
                printOutput = False
                break
        elif i==10:
            if line.find('threshold b')==-1:
                print "Parsing error!\n"
                printOutput = False
                break i+=1
    f.close() #if you need to sort the features by value and not by feature ID then use this line intead:
    #ws = sortbyvalue(w)    ws = sortbykey(w)
    if printOutput == True:
        for (i,j) in ws:
            print i,':',j
            i+=1

Ori Cohen 博士拥有计算机科学博士学位,专注于机器学习。他领导着 Zencity.io 的研究团队,试图积极影响市民的生活。

泰坦尼克号数据集的特征工程和算法精度

原文:https://towardsdatascience.com/feature-engineering-and-algorithm-accuracy-for-the-titanic-dataset-5891cfa5a4ac?source=collection_archive---------18-----------------------

机器学习最流行的数据集之一对应于泰坦尼克号事故

在这里,我们正在处理这个数据集中的特征,试图发现选择不同的特征对一些基本的最大似然算法的准确性的影响。这些特征对应于数据的头部

the head of the dataset (first 5 rows)

数据集显示了一名乘客是否幸存(1)以及在几列中对该乘客的描述,如他的阶级(Pclass)、年龄等。下表(从 kaggle 中提取)对这些特性进行了描述:

我们做的第一个图形探索性分析显示了特征之间的一些关系

我们可以为一些特殊的功能关闭它,例如 SibSp 和 Parch

这幅图描述了一个乘客是否幸存,基于他/她同行的家庭成员的数量。不过,他的关系并不清楚。

我们建议在随机森林分类器下传递这些特征,以更好地理解它们对存活率的影响:

结果并不清楚,这就是为什么我们创造了新的功能,如“孤独”或“家庭大小”。

下一个图显示了这些特征对幸存乘客数量的影响:

同样,我们可以探讨年龄和阶级之间的关系:

然而,当我们再次训练一个随机森林算法时,这种关系更加清晰。下一个图显示了基于乘客年龄和阶级的存活率

Before grouping Age feature

这很好,但是,为年龄创建更小的组,算法显示出更好的洞察力:

After grouping Age feature

这种表示更清楚,因此我们期望在年龄列中使用较小的组(而不是连续值)的学习算法会有更好的结果

此外,我们还可以检查新功能是否为我们未来的模式增加了价值:

这不是很清楚,所以我们也可以评估所有特征的熵,并删除这些可能产生比价值更多的噪声的列:

Survived column entropy : 0.96
Pclass column entropy : 1.44
Age column entropy : 1.4
SibSp column entropy : 1.34
Parch column entropy : 1.13
Fare column entropy : 1.61
FamilySize column entropy : 1.82
IsAlone column entropy : 0.97
Age*Class column entropy : 2.14
female column entropy : 0.94
male column entropy : 0.94
C column entropy : 0.7
Q column entropy : 0.42
S column entropy : 0.85

训练模特

我们测试了 4 个选项,第一个是在功能没有任何变化的情况下进行的训练(即,没有上述部分的工作):

No features, we use only the columns of the original data

下一次尝试包括了第一部分中创建的所有功能:

accuracy of model with all features

接下来,我们只包括那些用熵< 2, that is droping those columns that maybe add more noise than value

Feature with entropy < 2

it seems that the global accuracy of all the models is increasing

Next, we select only those features with entropy < 1,5 :

Feature with entropy < 1,5

It seems this feature combination give the better accuracy for all the algorithms

训练 KNN 的特征

另一点,例如,当训练一个有几个邻居的 KNN 时,结果取决于定义的特征。

我们绘制了几个邻居的 KNN 的精确度:

该图清楚地显示了定义要素将如何改变算法的输出,甚至是在选择其参数之前(例如,KNN 的邻域数)

这个“实验的代码在这里:

[## EDA,特征工程比较=> ML 精度

使用泰坦尼克号的数据:灾难中的机器学习

www.kaggle.com](https://www.kaggle.com/felipesanchezgarzon/eda-feature-engineering-comparison-ml-accuracy)

感谢阅读!!!

如果你想继续阅读这样的故事,你可以在这里订阅!

使用超市销售数据的特征工程和数据准备

原文:https://towardsdatascience.com/feature-engineering-and-data-preparation-using-supermarket-sales-data-part-2-171b7a7a7eb7?source=collection_archive---------12-----------------------

续。从 利用超市销售数据进行探索性数据分析。第一部分。

在上一篇 文章 中,我们对超市销售数据集进行了基本的数据探索。我们将在这一部分使用这些重要的见解和发现。我建议你在这里查看第一篇文章,这样我们就在同一页上了。

在开始之前,让我们了解什么是特征工程,以及我们为什么要这样做。

我相信我应该首先定义什么是特性,所以开始吧;

特征仅仅是原始数据的一个方面的表示,一些作者也称之为属性。例如, Product_Weight、Product_Type、Product_Price 是我们超市数据中的一些特征。这些特征可以是数字或分类的,最后必须转换成数字格式(机器学习模型可以使用的唯一类型)。

现在,让我们定义特征工程;

特征工程是从原始数据中提取重要特征并将其转换为适合机器学习模型的格式的行为。为了进行功能工程,数据科学家必须使用领域知识(关于特定领域的知识)、数学和编程技能来转换或提出新的功能,以帮助机器学习模型更好地执行。

特征工程是机器学习的一个非常广泛和实用的领域,我建议你查阅一些关于这个主题的书籍或课程,因为这篇文章只是触及了表面。下面列出了一些链接。

有了这些基本知识,让我们开始研究超市数据。在这篇文章的最后,我们将有一个干净的数据集,我们可以扔进任何机器学习模型。

首先,让我们实现上一篇文章(数据探索)中的所有发现。

新特征创建

code for is_normal_fat feature creation

代码解释:

代码片段 1:我们首先打印出 Product_Fat_Content 列中唯一的类,这样我们就知道正确的类别及其拼写。

代码片段 2:我们创建一个字典,并将类别映射到二进制类。
低脂肪和超低脂肪为 0 级,而正常脂肪为 1 级。
其次,我们使用 pandas map 函数将 Product_Fat_Content 中的值映射到我们创建的字典中的类。

代码片段 3:我们确认我们有类(0 和 1)

代码片段 4:打印出两个类中的点数。

我们建议创建的第二个功能是一个列,该列捕获具有(低价)正常脂肪的产品和具有(高价)低/超低脂肪的产品的价格。
现在,再次将正常脂肪从其他两类中分离出来会给我们一个多余的特征,因为我们已经在之前的特征(is_normal_fat)中将它们分离出来了。所以我们跳过这个特征创建。
**

第三个被提议的特性是一个捕获开业年份的列。此功能将 Ssuper market _ Opening _ Year分为 90 年代和 2000 年代。**

我们创建这个列,如下所示。

代码片段 1:一个简单的函数,将年份作为参数,如果小于 1996,则返回 0,否则返回 1。

代码片段 2:我们使用 pandas apply 方法创建特性 open_in_the_2000s 。我们将函数(cluster_open_year)应用于超市 _ 开业 _ 年份中的每个值。

3:新列的预览。

第四个建议的特性是将产品类型分为两类。类别 1 将包含家庭、健康卫生、其他类别,而类别 0 将包含其余类别。

让我们在下面创建它。

代码片段 1:这里我们获得了 Product_Type 列中的唯一类别,然后删除了属于类别 1 的类别。

代码片段 2:我们创建了一个简单的函数,如果 Product_Type 是类 0,则返回 0,否则返回 1。

代码片段 3:我们应用我们的函数来创建新列 Product_type_cluster。

转换倾斜特征

在我们的 EDA 过程中,我们发现产品 _ 超市 _ 销售产品 _ 货架 _ 可见性是右偏的,我们建议将它们转换成正态分布。

代码片段 1:我们创建了一个 1 行 2 列的子图来保存转换前后的图,这样我们就可以比较变化了。

代码片段 2:在轴 1 中绘制普通的 Product_Supermarket_Sales 特性。

代码片段 3:使用 numpy 的 np.log1p( ) 函数对特征进行日志转换。该函数在应用对数函数之前将变量加 1。然后我们在第二个轴上绘制新的变换特征。

代码片段 4:用 tight_layout()参数在两个轴之间添加空格,并添加一个标题。

当我们运行上面的代码时,我们注意到对数转换使特征Product _ Supermarket _ Sales更接近正态分布,这是更好的,并且主要是由统计学家鼓励的。如果想知道为什么统计学家喜欢正态分布,请点击这里。

接下来,让我们转换产品 _ 货架 _ 可见性

代码与前者非常相似,但是这里我们转换了 Product_Shelf_Visibility。

运行代码,我们得到以下结果…

当我们应用对数变换时,这个特性没有显著的变化。这是因为这些值大多接近于零。像这样转换特性的高级方法是存在的,但是在这篇文章中我们将坚持使用它。

处理分类特征

接下来,我们将处理所有的分类列,但是首先,让我们解释一些重要的概念,它们将帮助我们正确地处理这些特性。

分类编码,是什么?

分类变量用于表示类别或类。例如,我们数据集中的 Product_Type 列是一个分类变量,有 3 个类别(低脂肪、超低脂肪和正常脂肪)。

分类编码是用于将非数字特征/变量转换成数字的方法。执行分类编码有多种方法,但这可能取决于我们正在处理的分类变量的类型。
在进行编码之前,我们通常考虑的两种范畴类型是序数和非序数/名词性范畴。在选择编码方案之前,理解这两种类型之间的区别是很重要的。我们先来了解一下他们。

序数范畴:这些是可以按数量顺序排列的范畴。也就是说,我们可以以这样一种方式安排这些类,使得一个类可以比另一个类具有更大的幅度。例如,我们数据集中的特征 SuperMarket_Size 是一个有序分类,因为分类标签 High 大于 Medium ,后者又大于 Small 。因此,如果我们对这些特征进行编码,我们肯定应该给类分配一个更大的类号。****

{高:3,中:2,小:1}

这些类别在数量上没有有意义的差别。即一个类别标签不大于另一个。例如,在 feature Product_Type ,**,软饮料不大于家用健康与卫生,,类也不大于日记,**类。****

现在让我们来谈谈一些类型的编码
对于具有少量类别的分类特征的编码,通常少于 20 个

一热编码:在一热编码中,我们用一组比特来表示一个类,其中一个特定时间只能激活一个类。考虑我们数据集中的特征Supermarket _ Location _ Type,该特征有三个类(分类 1、分类 2 和分类 3)。我们可以使用如下所示的三位对该特性进行一次性编码。****

在这里,我们可以看到我们使用了 3 位,一次只能激活一个类。

哑元编码:哑元编码与单热编码相同,唯一的区别是在哑元编码中,我们比单热编码少用一位。也就是说,我们有一个全是 0 位的类。区别是微妙的,两种方法可以互换使用。

具有大量类别的分类特征的编码,通常大于 20 个

  • 标签编码:在标签编码中,我们简单的给每个类别分配整数,比如 1 到 n (n =类别数)。这通常会产生可订购的功能。
  • 注意:一些机器学习模型,如树或线性模型,可以很好地执行这种类型的编码,因为树不处理数据点的大小。但这将影响神经网络和基于距离的模型,如 KNN,它考虑了数据点的数量。

其他先进但有效的编码方案是嵌入、特征散列和容器计数。

现在,让我们看看我们的分类特征和它们的独特类别。

类别数最多的列是 Product_Type 列(15)。

根据我们对上述编码方案的理解,我们可以使用 one-hot-encoding 方案,因为我们的数据很小(大约 5000 个),唯一类别也很小。

对于 Python 中的 one-hot-encoding,我们将使用 pandas 中可用的虚拟函数。这是一个简单的方法,我们只需要调用。getdummies() 方法并传入我们想要编码的数据帧。

处理缺失值

在清理和转换数据时,另一件重要的事情是处理丢失的值。这意味着我们可以删除丢失的值或者填充它们。

首先,我们将使用熊猫 isnull() 函数打印出每一列中缺失值的数量。

请注意,列的数量急剧增加,这是由于我们之前进行的一次性编码。一种热编码使用分类列的唯一类创建新要素。

从输出中,我们看到唯一缺少值的特性是 Product_weight 列。

4990 中有 802 个缺失值,我们将使用平均值来填充这些缺失值。

注意:除了仅使用平均值,还有其他填充或处理缺失值的方法。sci kit learn(python 中的一个机器学习包)有一个叫做的模块,用于自动处理缺失值。但是因为我们只有一列缺少值,所以我们将手动执行填充。****

code for filling missing values

代码片段 1:我们使用 pandas mean()函数计算平均值。

代码片段 2:我们使用 pandasfillna方法用我们计算的平均值自动填充每个缺失值。 inplace = True 表示对原始数据集进行操作。**

最后,我们预览我们的变化。在这里,我们看到我们不再有丢失的值。

咻…我们的数据几乎准备好建模了。我们将执行最后两个重要的步骤:创建我们的系列测试测试集和 f 特征标准化/缩放。

将我们的数据分为训练集和测试集

我们通常把数据分成三组。训练集、测试集和验证集。我们使用训练集来训练我们的模型,在每轮训练后使用测试集来评估我们的模型,并在项目结束时使用最终验证集来最终评估我们的模型。

最终的验证集很重要,因为在训练和测试的过程中,模型开始学习测试集,并且会不断地做得更好。

这是不好的,因为我们可能最终认为我们的模型会概括得很好,而实际上它只是记住了训练和测试集。因此,建议只在建模的最后使用验证集。

我们将使用 scikit-learn 中一个流行的函数 train_test_split 来拆分我们的数据。

code to split data into train and test sets

首先,我们导入函数 train_test_split,然后传递四个变量

  1. X_train:训练数据
  2. X_test:测试数据
  3. y_train:培训目标
  4. y_test:测试目标

train_test_split 函数中,我们传递想要分割的数据和目标,然后指定分配给测试数据的数据数量(0.3 = > 30%的数据)

最后,我们检查新数据的形状。

注意:X _ train 和 y_train 的第一个尺寸必须相同。

最后,我们将数据标准化

特征标准化/缩放

特征标准化/缩放用于将独立特征的值的范围标准化为大致相同的范围。这一点很重要,因为数据集的各个特征的值的范围变化很大,这可能导致大多数机器学习模型表现不佳。

例如,在我们的数据集中,特征超市 _ 开业 _ 年份的值的范围在 1992 年和 2016 年之间,而特征 Product_Shelf_Visibility 的值的范围以(0-0.4)为中心。这两个特性的取值范围非常不同,应该标准化。

当我们使用指定的方法进行规范化时,这些值被转换到相同的范围,有时在(0 和 1,-1 和 1 或最小值和最大值)之间

我们将使用robust scaler模块中的 Scikit learn 来执行本项目中的归一化。其他可用的归一化方法有最小最大缩放器规格化器标准缩放器最大最小缩放器等等

首先,我们从 sklearn.preprocessing 导入 RobustScaler 包,然后我们从它创建一个对象。

其次,我们拟合我们的训练数据(X_train)。拟合过程是函数学习它将用于转换训练、测试和(如果可用的话)验证数据的所有度量的过程。

接下来,我们使用计算出的指标在 X_train 和 X_test 上执行实际的转换。

最后,我们打印新转换的数据的形状。

注意:只适合你的训练数据!

咻…我们的数据终于可以用于建模了!

在这篇文章中,我们已经从探索性的数据分析转向实际的特征创建和清理。我们创建了新的要素,处理了缺失值,分割了数据,并执行了标准化。

我们在这里所做的一切都可以归类到数据准备之下,这是任何数据分析项目中最耗时的部分。

的下一篇中,我们将使用这些干净的数据为 建模

所以,去做一些功能创建和清理的乐趣。再见了。

本项目的笔记本和资料可以在 这里 找到

问题,评论和贡献总是受欢迎的。

twitter 上和我联系。

insta gram上跟我连线。****

Kaggle“房价竞赛”前 10 名的特征工程和集合模型

原文:https://towardsdatascience.com/feature-engineering-and-ensembled-models-for-the-top-10-in-kaggle-housing-prices-competition-efb35828eef0?source=collection_archive---------8-----------------------

我们一步一步地详细说明开发回归模型的程序,以便在这场全球竞赛中进入“前 10 名”。

Photo by Jesse Roberts on Unsplash

aggle 是数据科学家和机器学习者最重要的在线社区之一。在 Kaggle 中,经常可以找到比赛,不同的用户生成预测模型并提交他们的结果,根据分数在排行榜中排名。

支持每个初学者进行的在线课程的竞赛是“Kaggle Learn 用户房价竞赛”。

因此,全球和以学习为导向的能力,网上流通的材料是丰富的,甚至对于那些看起来很好的人来说,可以直接找到优秀模型的完整代码,这些代码在记分牌上非常接近 1%。

本文基于 Kaggle 中 Sandeep Kumar 曝光的代码。这个代码是我们在比赛中最重要的起点。虽然单凭这段代码不足以进入前 10 名,但它使用了许多令人兴奋的技术。除了这里使用的方法之外,我们还添加了其他方法,使我们的模型进入了前 10 名。

因为当你开始尝试提高 1%以内时,每个细节都很重要,所以你需要非常有条理和彻底地处理数据工程和模型参数。

数据集和工作流

该数据集包含 1460 行房屋及其相应的 79 个要素和目标变量:每栋房屋的价格。还有一个测试集,有 1459 个其他的房子。

在本文中,我们将对我们将要应用的不同技术进行统一处理,并在实现最终预测以提高结果之前将它们分开。

数据集的工作流将

a)导入训练特征和测试特征数据。

b)保存并删除索引(Id ),准备不带目标变量的训练特征

c)创建连接它们的数据框“特征”

d)执行数据工程

e)在完成数据工程工作后,再次分割 X_train 和 X_test 数据集。

f)制作模型、拟合和预测

g)如果我们对目标变量(Y)应用任何变换,我们必须在预测上反转这个变换。

h)提交结果

目标变量:销售价格

我们正面临一个回归练习,我们必须计算每栋房子的销售价格,数据集的每一行。

我们的目标是列 SalePrice,首先,让我们看看它的一般行为:

train['SalePrice'].describe()

现在让我们看看它在任何直接使用它的模型中的实践,或者对它应用对数。

因此,我们看到,如果我们改变模型训练,将目标变量的对数而不是“销售价格”直接作为结果,那么从 12934 到 8458 的改进是显著的。

特征工程

与目标变量的相关性

相关性的目的是检查两个定量变量之间关联的方向和强度。因此,我们将知道它们之间关系的强度,以及当一个变量的值增加时,另一个变量的值是增加还是减少。”

如果你想知道更多关于变量和目标变量相关性的发生率,你可以回顾这一期的详细分析。

我们将创建一个包含数字列的数据框,并对它们应用 Pandas“corr()”方法,然后评估销售价格目标:

data_corr = train.select_dtypes(include=[np.number])
data_corr.head()

corr = data_corr.corr()
corr.head(20)
corr.sort_values(['SalePrice'], ascending=False, inplace=True)
corr['SalePrice']

这里我们有一个与目标变量相关的变量的有序列表,随着我们使用与目标变量越来越不相关的变量,模型精度的提高可能会被认为是降低的。

数据集中的异常值

“…异常值会显著影响估计统计数据的过程(,例如,样本的平均值和标准偏差),从而导致高估或低估值。因此,数据分析的结果在很大程度上取决于缺失值和异常值的 processed…"⁴方法

在引用这段话的文章中,可以深入找到关于异常值处理的优秀概念。

让我们以 GrLivArea 为例,它是与价格相关性最高的变量之一。

为了可视化异常值,我们通常使用箱线图。

这里有一篇关于箱线图和离群点 detection⁵的使用的优秀文章,以及他的主要特点:

  • 箱线图有从 LQ 到 UQ 的方框,中间标有
  • 它们描绘了数据最小值、LQ、中值、UQ、最大值的五个数字的图形摘要
  • 帮助我们了解数据分布
  • 帮助我们轻松识别异常值
  • 25%的人口低于第一个四分位数,
  • 75%的人口低于第三个四分位数
  • 如果盒子被推到一边,而一些值离盒子很远,那么这是异常值的明显迹象

我们使用整个训练集对任何模型进行检查,剔除大于 4500 的异常值和大于 4000 的异常值,获得以下值

在 4000 或 4500 切割之间的性能差异似乎并不显著;然而,当 slice GrLivArea 寄存器大于 4500 时,精度的提高是相当可观的。

原则上,根据训练数据,这似乎是一个合适的策略。尽管如此,还是有必要在模型的入口中寻找一个平衡,因为它可能会产生过度拟合,或者使我们的模型失去泛化的可能性。

删除不必要的列并填充空值

如果我们使用此代码在这些操作之前和之后在数据框中部署空值,我们将获得如下图所示的图形。

import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(12, 6))
sns.heatmap(features.isnull())
plt.show()

请记住,在输入模型之前,必须清除所有的空值。

这些数据中的许多我们已经知道我们不打算精确化它们,或者填充空值的尝试对它们来说是不可行的;我们直接应用柱的下降。

features.drop(['Street'], axis=1, inplace=True)
features.drop(['Fence'], axis=1, inplace=True)

填充缺失数据的日常用法是用“无”或“零”来填充,这取决于数据的类型。

让我们在这里看两个例子来分配最频繁的值(Pandas mode 函数)或如何应用整个列或一组列的平均值。

这些示例仅用于说明目的;您可以应用任何 Pandas 函数,并根据您想要组成集群的任何属性进行分组。它还可以用于创建“集群”类型的新功能。

示例 a :应用最频繁的 MSZoning 值,为每个 MSSubClass 分组

features['MSZoning'] = features.groupby('MSSubClass')['MSZoning'].transform(lambda x: x.fillna(x.mode()[0]))

例 b :应用为每个街区分组的地段临街面的平均值

features['LotFrontage'] = features.groupby('Neighborhood')['LotFrontage'].transform(lambda x: x.fillna(x.median()))

例 c :用最频繁的量来完成你的特色

features['Exterior1st'] = features['Exterior1st'].fillna(features['Exterior1st'].mode()[0])

最后,我们必须确保没有空值,以便我们可以根据列的类型将填充应用于无或零:

objects = [col for col in features.columns if features[col].dtype == "object"]
features.update(features[objects].fillna('None'))numerics = [col for col in features.columns if features[col].dtype in np.number]
features.update(features[numerics].fillna(0))

根据他人创造变量

您可以创建新的变量来添加有助于模型定义数据集的特定信息,或者通过创建平均值、总和或布尔变量来简化模型。

必须仔细选择这些变量中的每一个,并且必须测量对模型的影响,以查看它是否增加了结果的质量,并且在用于“简化”的情况下,如果被消除的列的损失不影响模型的准确性。

features['CondTot'] = (features['Condition1'] + features['Condition2']) / 2features['OverTot'] = features['OverallQual'] + features['OverallCond']features['TotalSF'] = features['TotalBsmtSF'] + features['1stFlrSF'] + features['2ndFlrSF']features['haspool'] = features['PoolArea'].apply(lambda x: 1 if x > 0 else 0)

倾斜特征

对于正态分布的数据,偏斜度应该在 0 左右。对于单峰连续分布,偏度值> 0 意味着分布的右尾有更多的权重。函数 skewtest 可用于确定偏斜值是否足够接近 0,从统计角度来说。⁵

我们使用 scipy.boxcoxp1 来计算 1 + x 的 Box-Cox 变换。 计算的 Box-Cox 变换为:boxs p1p:

y = ((1+x)**lmbda - 1) / lmbda  **if** lmbda != 0
log(1+x)                        **if** lmbda == 0Returns *nan* if x < -1\. Returns *-inf* if x == -1 and lmbda < 0.

为了在 boxscox 1 中获得输入的λ值,我们使用来自 Scipy 的 boxscox _ normmax(features[ind]),它为输入 data⁷计算最佳的 Box-Cox 变换参数

这种转换只能在具有所有正值的特征上进行;如果需要对具有负值的元素进行转换,请参阅参考 note⁸。

from scipy.stats import skew
from scipy.special import boxcox1p
from scipy.stats import boxcox_normmaxnumerics = [col for col in features.columns if features[col].dtype in np.number]skew_f = features[numerics].apply(lambda x: skew(x)).sort_values(ascending=False)highest_skew =  skew_f[skew_f > 0.5]
highest_skew

skew_idx = highest_skew.index
for ind in skew_idx:
    features[ind] = boxcox1p(features[ind], boxcox_normmax(features[ind))features[highest_skew.index].head()

创建与目标变量相关的特征

当创建与目标变量相关的变量时,有必要只在列车数据集上执行这些操作,然后尝试将它们带到测试数据集,因为在那里我们不知道目标变量“Y”的值。

这类变量使我们在竞争排行榜上有了显著的改善

例如,我们可以计算每平方米的价格,与每一个社区或每一类住房相关联,然后我们首先在列车组中算出它:

train['Sqr'] = train['SalePrice'] / train['LotArea']
train['Sqr'] = train.groupby('Neighborhood')['Sqr'].transform(lambda x: x.median())

然后我们用每个街区的价格编了一本字典:

d = {}
for indice_fila, x_train in train.iterrows():
    d.update({x_train['Neighborhood']:x_train['Sqr']})

最后,我们在测试数据集中创建了特征

test['Sqr'] = 0.00
for indice, x_test in test.iterrows():
    test.loc[test.index == indice ,'Sqr'] = d[x_test['Neighborhood']]

分类对象变量

“一种热门的编码方式是,将一个包含分类数据的列进行标签编码,然后将该列拆分为多列。数字被“1”和“0”代替,取决于哪一列有什么值……”⁹

对对象特征进行分类可能非常方便,关于使用哪些列对其应用 OHE 的讨论非常广泛,并且它们的局限性也很分散,因为列中的每个不同值都将被转换为模型矩阵输入的新维度。有时,创建一个对不同值进行分组以降低维数的新特征可能会很方便,例如,一组具有相似特征和价格的邻域。

对于某些数值列,我们可能需要对它们进行分类,为此,将类型更改为字符串很方便,因为最后,我们将对 type 对象的所有列应用 get_dummies。

features['MSSubClass'] = features['MSSubClass'].apply(str)
features['MoSold'] = features['MoSold'].astype(str) 
final_features = pd.get_dummies(features).reset_index(drop=True)

虚拟变量陷阱:

“虚拟变量陷阱直接来自于对分类变量应用的一热编码……一热向量的大小等于分类列占用的唯一值的数量,并且每个这样的向量中正好包含一个‘1’。这将多重共线性纳入我们的数据集中……”⁰

换句话说,当执行转换时,添加的 OHE 特征集与目标变量对齐,因此通常会删除任何列。在我们的模型中,我们没有应用这种技术。

过度进食

Sandeep Kumar 的原始工作包括在特征工程结束时应用 get_dummies,这是一种寻找那些具有大量零值(高于 99.94%)的列的代码,这些值是 drop。

这种技术间接地减轻了分类变量中虚拟变量陷阱的影响,并且还避免了在模型中包括由于分类内容的影响而与试图提取主要特征并对其进行概括的模型无关的列。

overfit = []
for i in X.columns:
    counts = X[i].value_counts()
    zeros = counts.iloc[0]
    if zeros / len(X) * 100 **> 99.94:** overfit.append(i)overfit = list(overfit)
**overfit.append('MSZoning_C (all)')** overfit

X = X.drop(overfit, axis=1).copy()

集合模型

回归算法在支持分类后的列方面没有问题,所以除了非常不方便的情况,删除它们似乎没有必要,甚至不是一个好主意。然而,这只是一种方法;在这篇文章的结尾,我们将看到解决这个问题的其他方法非常不同,而且一样有用,甚至更有用。

在这种情况下,没有必要降低模型的维数,因为使用不同算法的训练时间不是问题,并且强力似乎不是一个坏策略。

“集合模型”是什么意思?

“集合模型给了我们出色的表现,可以解决各种各样的问题。它们比其他类型的技术更容易训练,需要的数据更少,效果更好。在机器学习中,集成模型是标准。即使你没有使用它们,你的竞争对手也是… 集合模型由几个本身并不优秀的弱模型组成,被称为弱学习者。当您将它们结合在一起时,它们可以弥补彼此的不足,并提供比单独部署时更好的性能。”

模型度量

竞争指标是平均绝对误差(MAE)。

对于初始测量,RMSE 和 CV_RMSE 使用。

因为 CV_RMSE 是负的,要应用平方根,需要翻转它。

def rmsle(y, y_pred):
    return np.sqrt(mean_squared_error(y, y_pred))def cv_rmse(model):
    rmse = np.sqrt(-cross_val_score(model, X, y, scoring="neg_mean_squared_error", cv=kfolds))
    return rmse

模特们

这是 Sandeep Kumar 使用的原始模型的一个示例,我们可以看到大量超参数的使用:

gbr = GradientBoostingRegressor(n_estimators=3000, learning_rate=0.05,max_depth=4, max_features='sqrt', min_samples_leaf=15, min_samples_split=10, loss='huber', random_state=42)

制作集合模型

在我们对 GBR 模型执行的相同操作中,我们可以对其他不同的回归模型执行该操作。然后在一个函数中,我们给它们分配一个总值的关联系数,这样比率加起来就是 1。

Sandeep Kumar 混合模型:

def blend_models_predict(X):
   return (
(0.1 * elastic_model_full_data.predict(X)) + \
(0.1 * lasso_model_full_data.predict(X)) + \
(0.1 * ridge_model_full_data.predict(X)) + \
(0.1 * svr_model_full_data.predict(X)) + \
(0.1 * gbr_model_full_data.predict(X)) + \
(0.15 * xgb_model_full_data.predict(X)) + \
(0.1 * lgb_model_full_data.predict(X)) + \
(0.25 * stack_gen_model.predict(np.array(X))))

Mi 混合模型:

def blend_models_predict(X=X):
    return (
(0.10 * elastic_model_full_data.predict(X)) +
(0.05 * lasso_model_full_data.predict(X)) +
(0.05 * ridge_model_full_data.predict(X)) +
(0.05 * svr_model_full_data.predict(X)) +
(0.20 * gbr_model_full_data.predict(X)) +
(0.20 * xgb_model_full_data.predict(X)) +
(0.10 * lgb_model_full_data.predict(X)) +
(0.25 * stack_gen_model.predict(np.array(X))))

我们能够使用其他模型,如线性回归、树形回归等。分配给每个模型的重要性取决于我们对测试数据集上的算法的信心,有时 GBR、XGB 或 LGB 等算法在拟合中工作得很好,但由于在测试集中过度拟合而失败。另一方面,像 SVR 或 RIDGE 这样的模型往往精度较低,但更稳定。

把所有这些放在一起,花了很多时间的努力

其他方法

当我们设法将我们的模型放入世界比赛的前 10 名,并知道我们的知识非常匮乏,渴望获得更多,但感觉处于平稳状态时,我联系了当时排名第三的 Adam su wik,他仍然在那里处于领先位置:

亚当非常友好地同意在一篇非常生动的帖子中分享他的模型的方法。

我们在这里抄录了这篇文章中一些最重要的段落:

“……这不是什么秘密知识,只是简单的逻辑思维(尽管该领域的专业知识也会很有帮助)。

根据模型调整数据—线性回归很可能是首选。不幸的是,大多数数据没有多大意义,可能会对结果产生不利影响。例如,质量——小房子和大别墅的优秀质量并不具有相同的价值——它应该被“优秀质量的平方英尺大小”所取代。而且在我看来,大部分数据应该是这样准备的——结合地块大小、房屋面积、地下室面积、公摊面积等。

组合数据/简化模型——这是我经常使用的方法。世界没有那么复杂。在这种情况下,对我来说 3 个特征就足够了——房子有多大?,多好啊?有多舒服?,也许是一些额外的设施,如游泳池、额外的车库等。…

丢弃数据—我也经常使用它。如果我看不到价值,或者认为信息包含在其他地方,或者如果我以某种方式设计了数据,我会放弃一切可能的东西。一些专业知识在这里会非常有用,但是仅仅使用常识,许多看起来有影响的数据实际上是次要的。没有人会在廉价的地基上建造豪宅,反之亦然…

聚类——并非总是相同的规则适用于整个群体,如果我能够隔离一大群相似的案例,我将为他们建立一个单独的模型。从生活经验来看——如果你看看每平方英尺的价格,你会注意到,对于较小的公寓来说,它肯定更大。

我可能还可以描述一些其他的事情,但这一切都归结为一件事——仔细分析所有的数据,并思考如何设计它们以适应您使用的模型。我相信一个原则:“垃圾进,垃圾出”。你提供什么质量的数据,你将得到什么质量的结果…

南的。不幸的是,答案如上——没有秘密——做任何看起来有意义的事情。我喜欢“0”,“没有”,我真的喜欢我自己的预测,我讨厌平均值,这就像承认缺乏任何想法,只是最小化假设错误的成本…

这是另一种方法,还有更多方法可以使用网络。在任何竞争中占 1%本身就是一个足够好的模式,也是我们走在正确道路上的标志。

摘要

我们已经看到,在他人分享的初步工作中开始一项工作是多么重要,在这种情况下,Sandeep Kumar 模型的迁移学习。谢谢他!

之前的迁移学习为我们提供了基础知识和基本知识,比新手从在线课程中获得的经验更加详尽和精炼。

然后,经过几个小时的努力和系统测试,我们看到了每种技术对不同变量的影响,以做出关于工程特性和模型集合的决策。

有一天,我有了一个想法,这个想法很重要,被分散了注意力,但仍然在思考,这个想法是根据一个给定的概念来计算平方英尺的值,达到前十名的满足感是一个不可思议的时刻。

这场卡格尔比赛的目的是学习

这篇文章旨在传播一些技巧,如果你像我几个月前一样在游戏中陷入困境,你可以使用这些技巧,希望能帮助你走上改善模型的正确道路,或者帮助你点亮那盏有所作为的小灯。

如果有什么地方可以做得更好,我将非常感谢您的评论和报告。

参考

Kaggle 是一个由数据科学家和机器学习者组成的在线社区,归谷歌有限责任公司所有。Kaggle 允许用户查找和发布数据集,在基于网络的数据科学环境中探索和构建模型,与其他数据科学家和机器学习工程师合作,并参加竞赛以解决数据科学挑战。Kaggle 通过提供机器学习竞赛起家,现在还提供公共数据平台、基于云的数据科学工作台和简短的人工智能教育。2017 年 3 月 8 日,谷歌宣布他们正在收购 Kaggle。

[2]【https://www.google.com/url?sa=t】T4&source = web&RCT = j&URL = https://personal . us . es/vararey/adatos 2/correlacion . pdf&ved = 2 ahukewiaytlhqnlahugllkghel 2 b 24 qfjamegqibxab&usg = aovvaw 1 mijaxh 5 f 0 qxixmpead 5j

[3] 回归模型中相关性与时间特征的关联

[4] 统计数据准备:缺失值和异常值的管理

“…基本上有三种方法来处理数据集中的异常值。一种方法是去除异常值,作为修整数据集的手段。另一种方法包括替换异常值或通过异常值权重调整来减少异常值的影响。第三种方法是使用稳健技术来估计异常值。

修剪:在这种方法下,分析排除离群值的数据集。微调估计量(如均值)会减少数据中的方差,并导致基于低估或高估的偏差。假设异常值也是观察值,将它们从分析中排除会使这种方法不适用于异常值的处理。

Winsorization :这种方法包括修改离群值的权重,或者用期望值替换离群值的测试值。权重修改方法允许在不丢弃或替换离群值的情况下修改权重,从而限制离群值的影响。值修改方法允许在排除异常值的观测值中用最大或第二小的值替换异常值。

稳健估计方法:当总体分布的性质已知时,这种方法被认为是合适的,因为它产生对异常值稳健的估计量,并且估计量是一致的。近年来,许多研究提出了各种稳健估计的统计模型;然而,由于复杂的方法学问题,它们的应用进展缓慢。"

[5]https://docs . scipy . org/doc/scipy/reference/generated/scipy . stats . skew . html

[6]https://docs . scipy . org/doc/scipy/reference/generated/scipy . special . boxcox 1p . html

[7]https://docs . scipy . org/doc/scipy/reference/generated/scipy . stats . box Cox _ normmax . html?highlight = normmax # scipy . stats . box Cox _ normmax

[8]https://www . ka ggle . com/rtatman/data-cleaning-challenge-scale-and-normalize-data

“你可以做的一件事是加上一个固定值,这个值刚好大于你的最小负值,使你的所有值都正常。另一种规范化方法是约翰逊变换,它可以处理负值。

[9]https://towards data science . com/choosing-the-right-encoding-method-label-vs-one hot-encoder-a 4434493149 b

[10]https://towards data science . com/one-hot-encoding-multi-commonline-and-the-dummy-variable-trap-b 5840 be 3c 41a

[11]https://medium . com/@ ODSC/ensemble-models-demystified-c 871 D5 ee 7793

https://github.com/scikit-learn/scikit-learn/issues/2439

sk learn . cross _ validation . cross _ val _ score 返回的均方误差始终为负值。虽然这是一个经过设计的决策,以便该函数的输出可以用于给定一些超参数的最大化,但直接使用 cross_val_score 时会非常混乱。至少我问自己,一个平方的平均值怎么可能是负的,并认为 cross_val_score 没有正确工作或者没有使用提供的度量。只有在深入 sklearn 源代码之后,我才意识到这个标志被翻转了。

scorer.py 的 make_scorer 中提到了这种行为,但是 cross_val_score 中没有提到,我认为应该提到,因为否则会让人认为 cross_val_score 没有正常工作

面向机器学习的特征工程基本技术

原文:https://towardsdatascience.com/feature-engineering-for-machine-learning-3a5e293a5114?source=collection_archive---------0-----------------------

用熊猫例子进行综合数据预处理所需的所有方法。

介绍

什么是特性,为什么我们需要它的工程化?基本上,所有的机器学习算法都使用一些输入数据来创建输出。该输入数据包括特征,这些特征通常是结构化列的形式。算法需要具有某些特定特征的功能才能正常工作。这里,出现了对特征工程的需求。我认为特性工程的努力主要有两个目标:

  • 准备适当的输入数据集,与机器学习算法要求兼容。
  • 提高机器学习模型的性能。

你使用的特性比其他任何东西都更能影响结果。据我所知,没有任何算法可以单独补充正确的特征工程所提供的信息增益。

—卢卡·马萨隆

根据《福布斯》的一项调查,数据科学家花费 80% 的时间在数据准备上:

Source: https://www.forbes.com/sites/gilpress/2016/03/23/data-preparation-most-time-consuming-least-enjoyable-data-science-task-survey-says/

这一指标显示了特征工程在数据科学中的重要性,令人印象深刻。因此,我决定写这篇文章,用简短的描述总结特征工程的主要技术。我还为每种技术添加了一些基本的 python 脚本。你需要导入 PandasNumpy 库来运行它们。

import pandas as pd
import numpy as np

上面的一些技术可能更适合一些算法或数据集,而其中一些可能在所有情况下都是有益的。本文不打算在这方面深入探讨。尽管如此,还是有可能为上面的每一种方法写一篇文章,我试着保持解释的简短和翔实。我认为获得特征工程专业知识的最佳方式是在各种数据集上实践不同的技术,并观察它们对模型性能的影响。

技术列表

1.归罪

当您尝试为机器学习准备数据时,丢失值是您可能会遇到的最常见问题之一。丢失值的原因可能是人为错误、数据流中断、隐私问题等等。无论是什么原因,丢失的值都会影响机器学习模型的性能。

一些机器学习平台在模型训练阶段自动丢弃包括缺失值的行,并且由于训练规模的减小而降低了模型性能。另一方面,大多数算法不接受具有缺失值的数据集并给出错误。

对于缺失值,最简单的解决方案是删除行或整列。没有最佳的删除阈值,但是您可以使用 70% 作为示例值,并尝试删除丢失值高于此阈值的行和列。

threshold = 0.7**#Dropping columns with missing value rate higher than threshold**
data = data[data.columns[data.isnull().mean() < threshold]]

**#Dropping rows with missing value rate higher than threshold**
data = data.loc[data.isnull().mean(axis=1) < threshold]

数值插补

插补是比删除更可取的选择,因为它保留了数据的大小。然而,有一个重要的选择,你归咎于丢失的价值。我建议从考虑列中缺失值的可能默认值开始。例如,如果您有一个只有 1NA 的列,那么很可能 NA 行对应于 0 。再举一个例子,如果您有一个显示“上个月的客户访问计数”的列,那么只要您认为这是一个合理的解决方案,缺失的值可能会被替换为 0

缺少值的另一个原因是连接不同大小的表,在这种情况下,输入 0 可能也是合理的。

除了缺失值有默认值的情况,我认为最好的插补方式是使用列的中位数。因为列的平均值对异常值敏感,而中间值在这方面更可靠。

**#Filling all missing values with 0**
data = data.fillna(0)**#Filling missing values with medians of the columns**
data = data.fillna(data.median())

分类插补

用列中的最大出现值替换缺失值是处理分类列的一个好选择。但是,如果你认为该列中的值是均匀分布的,并且没有一个主导值,那么把一个像“其他”这样的类别归入可能更合理,因为在这种情况下,你的插补很可能收敛于一个随机选择。

**#Max fill function for categorical columns**
data['column_name'].fillna(data['column_name'].value_counts()
.idxmax(), inplace=True)

2.处理异常值

在提到如何处理异常值之前,我想说明检测异常值的最好方法是直观地展示数据。所有其他的统计方法都有可能出错,而可视化异常值提供了一个高精度决策的机会。无论如何,我计划在另一篇文章中深入讨论可视化,让我们继续讨论统计方法。

统计方法不如我提到的精确,但另一方面,他们有优势,他们很快。这里我将列出两种处理异常值的不同方法。这些将使用标准差百分位数来检测它们。

基于标准差的异常值检测

如果一个值与平均值的距离高于 *x 标准差,则可以将其视为异常值。那什么 x 应该是?

对于 x 没有简单的解决方案,但是通常,介于 2 和 4 之间的值似乎是可行的。

**#Dropping the outlier rows with standard deviation**
factor = 3
upper_lim = data['column'].mean () + data['column'].std () * factor
lower_lim = data['column'].mean () - data['column'].std () * factor

data = data[(data['column'] < upper_lim) & (data['column'] > lower_lim)]

另外,可以用 z-score 代替上面的公式。 Z 分数(或标准分数)使用标准差标准化数值和平均值之间的距离。

基于百分位数的异常值检测

另一种检测异常值的数学方法是使用百分位数。您可以将顶部或底部值的某个百分比假定为异常值。这里的关键点是再次设置百分比值,这取决于前面提到的数据分布。

此外,一个常见的错误是根据数据范围使用百分位数。换句话说,如果你的数据范围是从 0100 ,那么你的 top 5% 就不是在 96100 之间的值。Top 5% 在这里是指超出第 95 个数据百分点的数值。

**#Dropping the outlier rows with Percentiles**
upper_lim = data['column'].quantile(.95)
lower_lim = data['column'].quantile(.05)

data = data[(data['column'] < upper_lim) & (data['column'] > lower_lim)]

离群困境:下降还是上升

处理异常值的另一个选择是限制它们,而不是丢弃它们。因此,您可以保持您的数据大小,并且在一天结束时,它可能对最终的模型性能更好。

另一方面,封顶会影响数据的分布,因此最好不要夸大它。

**#Capping the outlier rows with Percentiles**
upper_lim = data['column'].quantile(.95)
lower_lim = data['column'].quantile(.05)data.loc[(df[column] > upper_lim),column] = upper_lim
data.loc[(df[column] < lower_lim),column] = lower_lim

3.扔掉

Binning illustration of numerical data

宁滨可应用于分类数据和数值数据:

**#Numerical Binning Example****Value      Bin**       
0-30   ->  Low       
31-70  ->  Mid       
71-100 ->  High**#Categorical Binning Example****Value      Bin**       
Spain  ->  Europe      
Italy  ->  Europe       
Chile  ->  South America
Brazil ->  South America

宁滨的主要动机是使模型更加健壮并防止过度拟合,然而,这是以性能为代价的。每次你绑定一些东西,你就牺牲了一些信息,使你的数据更有规律。(请参见机器学习中的正则化)

性能过度拟合之间的权衡是宁滨过程的关键点。在我看来,对于数值列,除了一些明显的过度拟合情况,宁滨对于某种算法来说可能是多余的,因为它会影响模型性能。

然而,对于分类列,低频标签可能会对统计模型的稳健性产生负面影响。因此,为这些不太频繁的值分配一个通用类别有助于保持模型的稳健性。例如,如果您的数据大小是 100,000 行,那么将计数小于 100 的标签合并到一个新的类别中,比如“其他”,这可能是一个不错的选择。

**#Numerical Binning Example**data['bin'] = pd.cut(data['value'], bins=[0,30,70,100], labels=["Low", "Mid", "High"]) **value   bin**
0      2   Low
1     45   Mid
2      7   Low
3     85  High
4     28   Low**#Categorical Binning Example** **Country**
0      Spain
1      Chile
2  Australia
3      Italy
4     Brazilconditions = [
    data['Country'].str.contains('Spain'),
    data['Country'].str.contains('Italy'),
    data['Country'].str.contains('Chile'),
    data['Country'].str.contains('Brazil')]

choices = ['Europe', 'Europe', 'South America', 'South America']

data['Continent'] = np.select(conditions, choices, default='Other') **Country      Continent**
0      Spain         Europe
1      Chile  South America
2  Australia          Other
3      Italy         Europe
4     Brazil  South America

4.对数变换

对数变换是特征工程中最常用的数学变换之一。日志转换的好处是什么:

  • 它有助于处理倾斜的数据,在转换后,分布变得更接近正常。
  • 在大多数情况下,数据的数量级在数据范围内变化。例如:年龄 1520 的差别不等于年龄 6570 。从年龄上来说,是的,他们是相同的,但是对于其他所有方面来说, 5 年轻年龄的差异意味着更高的量级差异。这种类型的数据来自乘法过程,对数变换会像这样对幅度差异进行归一化。
  • 由于幅度差的归一化,它还降低了异常值的影响,并且模型变得更加稳健。

重要提示:您应用对数变换的数据必须只有正值,否则您会收到一个错误。此外,您可以在转换数据之前将 1 添加到数据中。因此,您可以确保转换的输出为正。

Log(x+1)

**#Log Transform Example**
data = pd.DataFrame({'value':[2,45, -23, 85, 28, 2, 35, -12]})data['log+1'] = (data['value']+1).transform(np.log)**#Negative Values Handling
#Note that the values are different** data['log'] = (data['value']-data['value'].min()+1) .transform(np.log) **value  log(x+1)  log(x-min(x)+1)**
0      2   1.09861          3.25810
1     45   3.82864          4.23411
2    -23       nan          0.00000
3     85   4.45435          4.69135
4     28   3.36730          3.95124
5      2   1.09861          3.25810
6     35   3.58352          4.07754
7    -12       nan          2.48491

5.一键编码

一键编码是机器学习中最常见的编码方式之一。该方法将一列中的值扩展到多个标志列,并将 01 分配给它们。这些二进制值表示分组列和编码列之间的关系。

该方法将算法难以理解的分类数据更改为数字格式,并使您能够在不丢失任何信息的情况下对分类数据进行分组。(详情请参见分类列分组的最后一部分)

One hot encoding example on City column

为什么一热?:如果你的列中有 N 个不同的值,把它们映射到 N-1 个二进制列就足够了,因为缺失的值可以从其他列中扣除。如果我们手里的所有列都等于 0 ,那么缺失值一定等于 1 。这就是它被称为一键编码的原因。不过,我会用熊猫的 get_dummies 函数来举例说明。该函数将一列中的所有值映射到多列。

encoded_columns = pd.get_dummies(data['column'])
data = data.join(encoded_columns).drop('column', axis=1)

6.分组操作

在大多数机器学习算法中,每个实例都由训练数据集中的一行来表示,其中每一列都显示该实例的不同特征。这种数据称为【整齐】

整齐的数据集易于操作、建模和可视化,并且具有特定的结构:每个变量是一列,每个观察值是一行,每种类型的观察单元是一个表。

—哈德利·韦翰

像事务这样的数据集很少符合上面整洁数据的定义,因为一个实例有多行。在这种情况下,我们按实例对数据进行分组,然后每个实例仅由一行表示。

分组操作的关键是确定特征的聚合函数。对于数字特征,平均值和总和函数通常是方便的选项,而对于分类特征,这就更复杂了。

分类列分组

我建议用三种不同的方法来聚合分类列:

  • 第一个选项是选择最高频率的标签。换句话说,这是对分类列的 max 操作,但是普通的 max 函数一般不会返回这个值,为此需要使用 lambda 函数。
data.groupby('id').agg(lambda x: x.value_counts().index[0])
  • 第二个选择是制作一个数据透视表。这种方法类似于上一步中的编码方法,但有一点不同。代替二进制表示法,它可以被定义为分组列和编码列之间的值的聚合函数。如果您的目标不仅仅是二进制标志列,而是将多个要素合并成信息更丰富的聚合要素,那么这将是一个不错的选择。

Pivot table example: Sum of Visit Days grouped by Users

**#Pivot table Pandas Example**data.pivot_table(index='column_to_group', columns='column_to_encode', values='aggregation_column', aggfunc=np.sum, fill_value = 0)
  • 最后一个分类分组选项是在应用一键编码后,通过函数应用分组。这个方法保留了所有的数据——在第一个选项中,您会丢失一些——此外,您同时将编码列从分类转换为数字。关于数值列分组的解释,可以查看下一节。

数字列分组

在大多数情况下,使用 summean 函数对数字列进行分组。根据特征的含义,两者都是优选的。例如,如果你想获得列,你可以使用二元列的平均值。在同一个示例中,sum 函数也可用于获得总计数。

#sum_cols: List of columns to sum
#mean_cols: List of columns to averagegrouped = data.groupby('column_to_group')

sums = grouped[sum_cols].sum().add_suffix('_sum')
avgs = grouped[mean_cols].mean().add_suffix('_avg')

new_df = pd.concat([sums, avgs], axis=1)

7.特征分割

Photo by Jaxon Lott on Unsplash

分割特征是使它们在机器学习方面有用的好方法。大多数情况下,数据集包含的字符串列违反了整齐数据原则。通过将列的可利用部分提取到新特征中:

  • 我们让机器学习算法能够理解它们。
  • 使得可以对它们进行绑定和分组。
  • 通过发现潜在信息来提高模型性能。

分割功能是一个很好的选择,但是,没有一种分割功能的方式。这要看栏目的特点,怎么拆分。下面用两个例子来介绍一下。首先,一个普通名称列的简单拆分函数:

**data.name**
0  Luther N. Gonzalez
1    Charles M. Young
2        Terry Lawson
3       Kristen White
4      Thomas Logsdon#Extracting first names **data.name.str.split(" ").map(lambda x: x[0])** 0     Luther
1    Charles
2      Terry
3    Kristen
4     Thomas#Extracting last names **data.name.str.split(" ").map(lambda x: x[-1])** 0    Gonzalez
1       Young
2      Lawson
3       White
4     Logsdon

上面的例子通过只取第一个和最后一个元素来处理长度超过两个单词的名称,这使得该函数对于极端情况是健壮的,这在处理这样的字符串时应该被考虑。

split 函数的另一种情况是提取两个字符之间的字符串部分。以下示例显示了通过在一行中使用两个 split 函数来实现这种情况。

#String extraction example **data.title.head()**
0                      Toy Story (1995)
1                        Jumanji (1995)
2               Grumpier Old Men (1995)
3              Waiting to Exhale (1995)
4    Father of the Bride Part II (1995)**data.title.str.split("(", n=1, expand=True)[1].str.split(")", n=1, expand=True)[0]** 0    1995
1    1995
2    1995
3    1995
4    1995

8.缩放比例

在大多数情况下,数据集的数字特征不具有特定的范围,并且它们彼此不同。在现实生活中,期望年龄收入栏有相同的范围是无稽之谈。但是从机器学习的角度来看,这两列怎么比较呢?

缩放解决了这个问题。在缩放过程之后,连续特征在范围方面变得相同。对于许多算法来说,这个过程不是强制性的,但是应用起来可能还是不错的。然而,基于距离计算的算法,如 k-NNk-Means 需要将缩放的连续特征作为模型输入。

基本上,有两种常见的缩放方式:

正常化

归一化(或最小-最大归一化)在 01 之间的固定范围内缩放所有值。这种变换不会改变特征的分布,由于标准偏差减小,异常值的影响会增加。因此,在标准化之前,建议处理异常值。

data = pd.DataFrame({'value':[2,45, -23, 85, 28, 2, 35, -12]})

data['normalized'] = (data['value'] - data['value'].min()) / (data['value'].max() - data['value'].min()) **value  normalized**
0      2        0.23
1     45        0.63
2    -23        0.00
3     85        1.00
4     28        0.47
5      2        0.23
6     35        0.54
7    -12        0.10

标准化

标准化(或 z 分数标准化)在考虑标准偏差的同时缩放数值。如果特征的标准偏差不同,它们的范围也会彼此不同。这降低了特征中异常值的影响。

在下面的标准化公式中,均值表示为标准 偏差表示为 σ

*data = pd.DataFrame({'value':[2,45, -23, 85, 28, 2, 35, -12]})

data['standardized'] = (data['value'] - data['value'].mean()) / data['value'].std() **value  standardized**
0      2         -0.52
1     45          0.70
2    -23         -1.23
3     85          1.84
4     28          0.22
5      2         -0.52
6     35          0.42
7    -12         -0.92*

9.提取日期

虽然日期列通常提供关于模型目标的有价值的信息,但是它们作为输入被忽略或者被无意义地用于机器学习算法。可能是这个原因,日期可以以多种格式呈现,这使得算法很难理解,即使它们被简化为类似" 01–01–2017 "的格式。

如果不处理日期列,在值之间建立顺序关系对于机器学习算法来说是非常具有挑战性的。这里,我建议对日期进行三种类型的预处理:

  • 将日期的各个部分提取到不同的列中:年、月、日等。
  • 根据年、月、日等提取当前日期和列之间的时间段。
  • 从日期中提取一些特定的特征:工作日的名称、是否是周末、是否是假日等。

如果您将日期列转换为如上所述的提取列,它们的信息就会公开,机器学习算法可以轻松理解它们。

*from datetime import date

data = pd.DataFrame({'date':
['01-01-2017',
'04-12-2008',
'23-06-1988',
'25-08-1999',
'20-02-1993',
]})

**#Transform string to date**
data['date'] = pd.to_datetime(data.date, format="%d-%m-%Y")

**#Extracting Year**
data['year'] = data['date'].dt.year

**#Extracting Month**
data['month'] = data['date'].dt.month

**#Extracting passed years since the date**
data['passed_years'] = date.today().year - data['date'].dt.year

**#Extracting passed months since the date**
data['passed_months'] = (date.today().year - data['date'].dt.year) * 12 + date.today().month - data['date'].dt.month

**#Extracting the weekday name of the date**
data['day_name'] = data['date'].dt.day_name() **date  year  month  passed_years  passed_months   day_name**
0 2017-01-01  2017      1             2             26     Sunday
1 2008-12-04  2008     12            11            123   Thursday
2 1988-06-23  1988      6            31            369   Thursday
3 1999-08-25  1999      8            20            235  Wednesday
4 1993-02-20  1993      2            26            313   Saturday*

结论

https://xkcd.com/1838/

我试图解释在特征工程过程中有益的基本方法。在这篇文章之后,继续进行数据准备的其他主题,例如特征选择、训练/测试分割、采样可能是一个不错的选择。

你可以查看我的另一篇关于过采样的文章

最后,我想以一个提醒来结束这篇文章。这些技术不是神奇的工具。如果你的数据微小、肮脏且无用,特征工程可能仍然无能为力。别忘了***垃圾进来,垃圾出去!”*****

参考

Python 中的要素工程

原文:https://towardsdatascience.com/feature-engineering-in-python-part-i-the-most-powerful-way-of-dealing-with-data-8e2447e7c69e?source=collection_archive---------3-----------------------

"我们的目标是将数据转化为信息,并将信息转化为洞察力。——卡莉·菲奥莉娜

Photo by h heyerlein on Unsplash

我们生活在一个数据驱动的经济中。在这个世界里,拥有大量数据、理解这些数据并知道如何处理数据就是力量。理解你的数据并不是数据科学中最困难的事情之一,但是很费时间。当我们知道数据的来源时,数据的解释是有效的。

数据

我们相信上帝。所有其他人必须携带数据。— W .戴明
首先,我们需要了解我们的数据。有 3 种数据:

  1. 数值 这代表某种定量测量。例如:身高的人,股票价格,页面加载时间等。它可以进一步分解为两部分:
    离散数据: 这是基于整数的,通常是一些事件的计数。例如:“一个用户喜欢多少首歌?”或者“一枚硬币掷到“头”上多少次?”
    连续数据: 它有无限多种可能的值。比如:“一个用户购买用了多长时间?”
  2. 分类的 这代表没有明显内在数学意义的定性数据。例子:是或否,性别,种族,婚姻状况等。这些可以被赋予像是(0)和否(1)这样的数字,但是数字没有数学意义。
  3. 序数
    它兼具数值型和分类型数据的特征。但是分类中的数字有数学意义。例如:电影评分为 1-5 等。这些值具有数学意义。因此,我们往往会忽略这一步,直接跳入水中。

特征工程

机器学习将数学符号与数据进行拟合,以获得一些见解。模型将特征作为输入。特征通常是现实世界现象或数据的一个方面的数字表示。就像迷宫中有死胡同一样,数据路径充满了噪音和缺失的部分。作为一名数据科学家,我们的工作是找到一条通往洞察最终目标的清晰道路。

Photo by Victor Garcia on Unsplash

数学公式对数字量起作用,原始数据并不完全是数字。特征工程是从数据中提取特征并将其转换为适合机器学习算法的格式的方法。

它分为三大类

特征选择:所有特征不相等。这就是从大量的特性中选择一小部分特性。我们选择那些最能解释自变量与目标变量之间关系的属性。对于模型的准确性来说,某些特征比其他特征更重要。它与降维不同,因为降维方法是通过组合现有属性来实现的,而特征选择方法是包含或排除那些特征。
特征选择的方法有卡方检验、相关系数评分、LASSO、岭回归等。

特性转换:就是把我们原来的特性转换成原来特性的功能。缩放、离散化、宁滨和填充缺失数据值是最常见的数据转换形式。为了 减少 数据的右偏,我们使用 log。

特征提取:当一个算法要处理的数据太大时,一般认为是冗余的。使用大量变量进行分析需要大量的计算能力和内存,因此我们应该减少这些类型变量的维数。这是一个构建变量组合的术语。对于表格数据,我们使用主成分分析来减少特征。对于图像,我们可以使用直线或边缘检测。

我们从 MachineHack 的这个 数据集开始。
我们将首先导入特征工程所需的所有包。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import datetime

我们将使用 pandas 加载数据,并将显示设置为最大,以便显示所有包含详细信息的列:

pd.set_option('display.max_columns', None)data_train = pd.read_excel('Data_Train.xlsx')data_test = pd.read_excel('Data_Test.xlsx')

在我们开始预处理数据之前,我们希望单独存储目标变量或标签。在从训练数据集中移除标签之后,我们将组合我们的训练和测试数据集。我们合并训练和测试数据集的原因是,机器学习模型不擅长推断,ML 模型不擅长从现有信息中推断出尚未明确陈述的东西。因此,如果测试集中的数据没有得到很好的表示,比如在训练集中,预测就不可靠。

price_train = data_train.Price # Concatenate training and test sets data = pd.concat([data_train.drop(['Price'], axis=1), data_test])

这是我们在data.columns之后得到的输出

Index(['Airline', 'Date_of_Journey', 'Source', 'Destination', 'Route','Dep_Time', 'Arrival_Time', 'Duration', 'Total_Stops',
'Additional_Info', 'Price'], dtype='object')

要检查前五行数据,请键入data.head()

为了更全面地了解情况,我们使用了data.info()方法。

<class 'pandas.core.frame.DataFrame'>
Int64Index: 13354 entries, 0 to 2670
Data columns (total 10 columns):
Airline            13354 non-null object
Date_of_Journey    13354 non-null object
Source             13354 non-null object
Destination        13354 non-null object
Route              13353 non-null object
Dep_Time           13354 non-null object
Arrival_Time       13354 non-null object
Duration           13354 non-null object
Total_Stops        13353 non-null object
Additional_Info    13354 non-null object
dtypes: int64(1), object(10)
memory usage: 918.1+ KB

要了解我们数据的分布,使用data.describe(include=all)

我们希望分析数据并删除所有重复的值。

data = data.drop_duplicates()

因此,我们希望检查数据中的任何空值。

Airline            0
Date_of_Journey    0
Source             0
Destination        0
Route              1
Dep_Time           0
Arrival_Time       0
Duration           0
Total_Stops        1
Additional_Info    0
Price              0
dtype: int64

因此,我们使用下面的代码来删除空值。

data = data.drop(data.loc[data['Route'].isnull()].index)

航空公司

让我们查一下航空公司一栏。我们注意到它包含了分类值。在使用了data['Airline'].unique()之后,我们注意到航空公司的价值观在某种程度上是重复的。

我们首先想要可视化该列:

sns.countplot(x='Airline', data=data)plt.xticks(rotation=90)

这种形象化帮助我们理解某些航空公司被分成了两个部分。例如,Jet Airways 有另一个部分叫做 Jet Airways Business。我们想把这两类结合起来。

data['Airline'] = np.where(data['Airline']=='Vistara Premium economy', 'Vistara', data['Airline'])data['Airline'] = np.where(data['Airline']=='Jet Airways Business', 'Jet Airways', data['Airline'])data['Airline'] = np.where(data['Airline']=='Multiple carriers Premium economy', 'Multiple carriers', data['Airline'])

这就是我们的列中的值现在的样子。

航班的目的地

目的地也是如此。我们发现德里和新德里被分成了两个不同的类别。因此,我们将它们合二为一。

data['Destination'].unique()data['Destination'] = np.where(data['Destination']=='Delhi','New Delhi', data['Destination'])

旅行日期

我们检查这个列,data['Date_of_Journey'],发现这个列的格式是:-

24/03/2019
1/05/2019

这只是原始数据。我们的模型不能理解它,因为它不能给出数值。为了从这个列中提取有用的特性,我们想把它转换成工作日和月份。

data['Date_of_Journey'] = pd.to_datetime(data['Date_of_Journey'])**OUTPUT** 2019-03-24
2019-01-05

然后从中获取工作日

data['day_of_week'] = data['Date_of_Journey'].dt.day_name()**OUTPUT** Sunday
Saturday

从旅行日期开始,我们还会得到月份。

data['Journey_Month'] = pd.to_datetime(data.Date_of_Journey, format='%d/%m/%Y').dt.month_name()**OUTPUT** March
January

航空公司的出发时间

出发时间采用 24 小时制(22:20),我们希望将其绑定以获得更多信息。

我们将创建固定宽度的箱,每个箱包含一个特定的数字范围。通常,这些范围是手动设置的,具有固定的大小。在这里,我决定将时间分成 4 个部分。[0–5]、[6–11]、[12–17]和[18–23]是 4 个容器。我们不能在计数中有大的间隙,因为这可能会产生没有数据的空箱。这个问题通过基于数据的分布定位箱来解决。

data['Departure_t'] = pd.to_datetime(data.Dep_Time, format='%H:%M')a = data.assign(dept_session=pd.cut(data.Departure_t.dt.hour,[0,6,12,18,24],labels=['Night','Morning','Afternoon','Evening']))data['Departure_S'] = a['dept_session']

我们在“出发 _S”列中用“夜间”填充空值。

data['Departure_S'].fillna("Night", inplace = True)

持续时间

我们的持续时间栏中的时间是这样写的2h 50m。为了帮助机器学习算法获得有用的见解,我们将把这些文本转换成数字。

duration = list(data['Duration'])for i in range(len(duration)) :
    if len(duration[i].split()) != 2:
        if 'h' in duration[i] :
            duration[i] = duration[i].strip() + ' 0m'
        elif 'm' in duration[i] :
            duration[i] = '0h {}'.format(duration[i].strip())dur_hours = []
dur_minutes = []  

for i in range(len(duration)) :
    dur_hours.append(int(duration[i].split()[0][:-1]))
    dur_minutes.append(int(duration[i].split()[1][:-1]))

data['Duration_hours'] = dur_hours
data['Duration_minutes'] =dur_minutesdata.loc[:,'Duration_hours'] *= 60data['Duration_Total_mins']= data['Duration_hours']+data['Duration_minutes']

我们现在得到的结果本质上是连续的。

可视化数据后,删除持续时间少于 60 分钟的行是有意义的。

# Get names of indexes for which column Age has value 30indexNames = data[data.Duration_Total_mins < 60].index# Delete these row indexes from dataFramedata.drop(indexNames , inplace=True)

我们将删除有噪音或文本的列,这对我们的模型没有帮助。为了获得更好的见解,我们将这些内容转化为更新的专栏。

data.drop(labels = ['Arrival_Time','Dep_Time','Date_of_Journey','Duration','Departure_t','Duration_hours','Duration_minutes'], axis=1, inplace = True)

虚拟变量

我们设计了几乎所有的功能。我们已经处理了缺失值、装箱的数字数据,现在是时候将所有变量转换为数字变量了。我们将使用get_dummies()来进行转换。

cat_vars = ['Airline', 'Source', 'Destination', 'Route', 'Total_Stops',
       'Additional_Info', 'day_of_week', 'Journey_Month', 'Departure_S' ]
for var in cat_vars:
    catList = 'var'+'_'+var
    catList = pd.get_dummies(data[var], prefix=var)
    data1 = data.join(catList)
    data = data1

data_vars = data.columns.values.tolist()
to_keep = [i for i in data_vars if i not in cat_vars]data_final=data[to_keep]

下一步

我们将不得不重新划分数据集。特征工程有助于从原始数据中提取信息,即它已经创建了许多特征。这意味着我们需要找到整个批次的主要特征。这也被称为维数灾难。我们用特征选择来对抗特征生成。

如果你想了解 峰度 ,点击这里。

如果你想了解数据科学 背后的 统计学,点击这里。

如果你想获得一个 机器学习入门 ,点击这里。

感谢阅读!

这里也可以给支持:https😕/*buymeacoffee.com/divadugar*

雪花中的特征工程

原文:https://towardsdatascience.com/feature-engineering-in-snowflake-1730a1b84e5b?source=collection_archive---------12-----------------------

在之前的故事中,我展示了一个真正的云数据仓库能够处理结构化数据的常见机器学习任务,如训练基于树的模型。

我的论点的要点是,如果架构是正确的,用例是常见的,那么您不应该需要将数据从数据库转移到通用计算集群中。作为雪花用户,您的分析工作负载可以利用其微分区来删除大量处理,并且预热后的每秒计费计算集群已准备好参与非常短暂但繁重的数字处理任务。

然而,人们常说,数据科学家 80%的时间都花在特性工程上,这几乎总是在你可以训练模型之前的一个要求。这部分也可以在数据库中完成吗?

什么是特征工程?

特征工程是关于转换输入数据,以便它可以被机器学习算法使用。数据背后有许多上下文含义,人类使用我们丰富的经验和能力来概括我们的学习,从而直观地解释这些含义。但是,尽管有一些营销材料,机器还不能自己做到这一点。

一些 ML 算法要求它们的输入是“工程化的”,这有几个原因,但用一个例子来说明:假设你的两个输入变量是工资和年龄,你的目标变量是购买产品的可能性。在你的数据集中,工资可能在 30,000 到 200,000 美元之间,年龄可能在 10 到 90 岁之间。读者们可能会理解,20 岁的年龄差距比 20 美元的工资差距要重要得多,但对于算法来说,它们只是需要拟合曲线的数字。因此,为了做好工作,计算机需要你考虑到这一点,在这种情况下,你可以通过缩放来做到这一点(例如,使工资和年龄都在 0.0 和 1.0 之间)。您还可以使用像宁滨(又名分桶)这样的技术将值放入固定数量的值范围之一(例如,工资范围 0 到 6)。

实现数据库内

当你看到像 scikit-learn 的预处理库这样的东西时,从头开始实现它们以在数据库中运行的前景可能会令人望而生畏,想象出处理低级数字操作的数千行代码的图像。

我不觉得它令人生畏的原因是,它的大部分实际上不是从零开始的。从概念上讲,它更像是实现一个金字塔的顶端部分,那里已经有了许多基础的构建块。例如,在我的决策树算法中,我使用标准偏差减少来计算树分裂。但是我不需要从头开始实现标准差。这是一个内置的雪花函数,我只需要把它插在正确的地方。有用的不仅仅是聚合函数;窗口函数有助于分解数据集,表函数非常适合切片和切块。

同样的原则应该适用于特征工程。如果你真的找到了一个缺失的构建模块,或者只是想把事情收拾得干净整洁,你总是可以成为一个优秀的工程师,像雪花函数一样孤立地解决它,然后根据需要调用它。继续读下去,你会发现我这样做了好几次。

在这个故事中,我将研究 scikit-learn 预处理函数的完整列表,并展示一个等效的雪花解决方案,使用所有雪花帐户可用的样本数据。

除非另有说明,否则数据库为“雪花 _ 样本 _ 数据”,模式为“TPCDS_SF10TCL”。

如果您像我一样在 Tableau Desktop 中运行这些查询,请确保在最后添加一个示例(500 行),因为其中一些表有数十亿行,真的会挤满一个图表。

preprocessing.Binarizer

根据阈值将数据二值化(将特征值设置为 0 或 1)

作为热身练习,让我们将 customer _ demographic dependencies 计数二进制化。假设它只与你是否有家属相关,我们不在乎有多少。

Before

select iff(CD_DEP_COUNT>0, true, false ) as CD_DEPENDANTS
from CUSTOMER_DEMOGRAPHICS

After

preprocessing.FunctionTransformer

从任意可调用函数构造一个转换器。

这个只是让你 BYO 变换函数,所以不是真正的算法。我们知道我们可以对存储过程或函数做同样的事情。

preprocessing.KBinsDiscretizer

将连续数据分入区间。

在 scikit-learn 函数中有几个选择。编码器参数可以是“onehot”、“onehot-dense”或“ordinal”。我将使用 ordinal 这个简单的方法,但是不要担心,我们稍后必须实现一个热编码作为它自己的函数。

同样,为了简单起见,我将采取“统一”的策略,但我计划在以后的帖子中处理 k-means。

这是商店销售的 SS_LIST_PRICE:

Before

放入 20 个等宽的箱子中:

with aggregates as (
  select min(SS_LIST_PRICE) as min_price,
  max(SS_LIST_PRICE) as max_price
  from STORE_SALES)
select width_bucket("SS_LIST_PRICE",min_price,max_price,20)
from aggregates,STORE_SALES
limit 100

After

这是垃圾箱的样子:

preprocessing.KernelCenterer

居中一个核矩阵

我不知道您怎么想,但是我在关系数据库的上下文中并不经常遇到内核矩阵,所以很难想象这是如何应用的。如果你认为我在这里太容易摆脱困境,请发表评论。

preprocessing.LabelBinarizer

以一对一的方式二进制化标签

这个和 OneHotEncoder 太像了,这里就不赘述了。

preprocessing.LabelEncoder

用 0 和 n_classes-1 之间的值对标签进行编码。

这里我们利用了雪花中一些很酷的数组函数,将所有不同的值用 array_agg 推入一个数组,这样我们就可以使用 array_position 函数对标签进行编码。

Before

with distinct_values as (
    select array_agg(distinct cd_marital_status) as marital_status_array from CUSTOMER_DEMOGRAPHICS)
select array_position(cd_marital_status::variant,marital_status_array) 
from distinct_values,CUSTOMER_DEMOGRAPHICS

After

preprocessing.MultiLabelBinarizer

在可迭代的可迭代和多标签格式之间转换。

真正适用的唯一方法是使用雪花数组存储多标签数据。我怀疑这是否普遍,但这也不是一个疯狂的想法。我将创建一个例子来匹配 doco 中的第二个例子。

示例表创建:

create or replace temporary table films as (
  select array_construct('sci-fi','thriller') as genres
  union
  select array_construct('comedy'));

select * from films

我们还需要一个简单的函数,给定一个完整的可能值数组,告诉我们每个值是否出现在给定的子集中。

create or replace function array_element_matches(MASTER_ARRAY array,ELEMENTS array)
  returns array
  language javascript
as
$$
  return MASTER_ARRAY.map(function(master_element){
    for (var i=0;i<ELEMENTS.length;i++){
      if (ELEMENTS[i]==master_element){
        return true;
      }
    }
    return false;
  });
$$
;

通过演示,该功能更容易解释:

select array_element_matches(
   array_construct('a','b','c','d','e'),
   array_construct('b','d')) as test

使用创建的函数,我们可以继续为我们的电影制作一个多标签二值化器。首先,我们将所有数组合并成一个数组,并获得一个不同值的列表。

然后我们可以使用函数来进行二值化。

with distinct_values as (
    select array_agg(distinct value::string) as all_genres_array from films
    ,lateral flatten(input => genres) g
    )
select all_genres_array,genres,array_element_matches(all_genres_array,genres) as genres_mlb
from distinct_values,films

我只解释一下 array_agg +横向展平部分。当您对数组值调用 array_agg 时,您会得到包含数组的数组(例如[ [1,2],[3,4] ])。这些很难处理,所以我们需要把它们都放到一个大的数组中([1,2,3,4])。

preprocessing.MaxAbsScaler

按最大绝对值缩放每个特征。

Before

这很简单,用每个值除以总的最大绝对值:

with aggregates as (
  select max(abs(WS_LIST_PRICE)) as max_abs_dep
  from WEB_SALES)
select WS_LIST_PRICE,max_abs_dep,WS_LIST_PRICE / max_abs_dep AS WS_LIST_PRICE_SCALED
from aggregates,WEB_SALES

After

preprocessing.MinMaxScaler

通过将每个特征缩放到给定范围来变换特征。

Before

这类似于 MaxAbsScaler,除了我们使用最小值和最大值。

with aggregates as (
  select min(CD_DEP_COUNT) as min_dep,max(CD_DEP_COUNT) as max_dep
  from CUSTOMER_DEMOGRAPHICS)
select (CD_DEP_COUNT - min_dep)
        / (max_dep - min_dep)
from aggregates,CUSTOMER_DEMOGRAPHICS

After

preprocessing.Normalizer

将样本分别标准化为单位标准。

这个公式需要一个除以零的保护措施,所以我们把它放在一个函数里。在这个例子中,我们将使用 L2 标准

create or replace function normalize_scale(x float,y float)
  returns float
  language javascript
as
$$
  var magnitude=Math.sqrt(X**2 + Y**2);
  if (magnitude==0){
    return 0;
  }else{
    return X/magnitude;
  }
$$
;

这是客户人口统计数据中的 DEP 就业人数和 DEP 大学人数。它们是离散值而不是连续值,但是你明白了。

Before

然后,根据自身和 CD _ DEP _ 学院 _ 计数对 CD _ DEP _ 就业 _ 计数进行规范化:

select 
  normalize_scale(CD_DEP_EMPLOYED_COUNT,CD_DEP_COLLEGE_COUNT) as dep_employed_count_normalized,
  normalize_scale(CD_DEP_COLLEGE_COUNT,CD_DEP_EMPLOYED_COUNT) as dep_college_count_normalized
from CUSTOMER_DEMOGRAPHICS
limit 1000000

preprocessing.OneHotEncoder

将分类整数特性编码为一个独热数值数组。

你是否对类别或标签进行编码无关紧要,这只是一个 if 语句。因为这将为每一列提供一个特性,所以您也可以只选择您计划使用的特定特性。

让我们从 CUSTOMER_DEMOGRAPHICS 返回 CD _ marriage _ STATUS:

Before

select 
    iff(cd_marital_status='S',true,false) as MARITAL_STATUS_S,
    iff(cd_marital_status='D',true,false) as MARITAL_STATUS_D,
    iff(cd_marital_status='W',true,false) as MARITAL_STATUS_W,
    iff(cd_marital_status='U',true,false) as MARITAL_STATUS_U
from CUSTOMER_DEMOGRAPHICS

After

请注意,您可以在此处轻松滚动标签,例如,将离婚和丧偶视为同一要素。

preprocessing.OrdinalEncoder

将分类特征编码为一个整数数组。

在关系数据库中这真的没有意义,它不像你按列付费。如果在一个数组中有固定数量的特征,只需将它们分成各自的列,并分别编码。

preprocessing.PolynomialFeatures

生成多项式和交互特征。

这些可以通过添加您计划使用的计算列(使用乘法、平方等)来轻松完成。

preprocessing.PowerTransformer

按特征应用幂变换,使数据更像高斯。

“yeo-johnson”或“box-cox”是可用于建立正态分布的两个函数。虽然它们不太复杂,但是有一些参数需要自动选择,这是一个需要进行一些构建的领域。Scikit-learn 包含许多参数优化函数(在本例中,是 Brent 的方法),这将是一个很难实现的函数。也许在以后的帖子里?

preprocessing.QuantileTransformer

使用分位数信息变换特征。

简而言之,scikit-learn 实现:

  1. 计算一组百分位数(默认为 1000)
  2. 使用这些百分位数的线性插值将每个值映射到 0–1 的范围内。

我会试着先想象一下。使用 CUSTOMER 表中的 C_BIRTH_YEAR 列,我们可以想象在 x 轴上展开的年份。

选择 10 作为一个简单的百分位数,我们可以计算这些,并在 y 轴上绘制。第一个点(10%,或 0.1)表示出生年份,您必须选择将 10%的值放在它的左边,将 90%的值放在它的右边。如果您一次做一个,您可以使用雪花的 PERCENTILE_*函数之一,但是我们需要在一个查询中生成它们。

-- Make a 0-percentile row containing the minimum value 
-- (i.e. 100% of values fall to its right)
select 0 as quantile,(select min(SS_LIST_PRICE) from STORE_SALES) as quantile_value
union all
-- generate 10 percentile values (10% increments) and for each, determine the maximum value that  
-- will divide the dataset row counts by that percentage
select quantile,max(case when rownum/numrows <= quantile then SS_LIST_PRICE end) as quantile_value
from 
(
  select 
  row_number() over (partition by null order by null) -1 as seq,
  0.1+(seq/10) as quantile
  from table(generator(rowcount => 10)) v 
  order by 1
) quantiles,
(
  select SS_LIST_PRICE,
    row_number() over (partition by NULL order by SS_LIST_PRICE) as rownum,
    count(*) over (partition by NULL) as numrows
  from STORE_SALES sample(10000 rows)
  where SS_LIST_PRICE is not null
) totals
group by quantile
order by quantile_value

这给了我们标价百分比:

现在让我们把这些都放在 2D 图表上:

一旦我们计算出我们的百分位数,就需要遍历 x 轴上的每个 SS_LIST_PRICE 值,确定哪两个蓝点(百分位数)离它最近,然后使用线性插值公式来确定它在 y 轴上的值(红色问号)。

首先,我们需要一个线性插值公式(通常被称为“lerp”的字符计数意识):

create or replace function linear_interpolation(x1 float,y1 float,x2 float,y2 float, x float)
  returns float
  as
  $$
    y1 + ((x-x1)/(x2-x1)) * (y2-y1)
  $$
  ;

然后我们进一步扩展我们的查询,将它们联系在一起:

with quantile_values as(
  -- Make a 0-percentile row containing the minimum value (i.e. 100% of values fall to its right)
  select 0 as quantile,(select min(SS_LIST_PRICE) from STORE_SALES) as quantile_value
  union all
  -- generate 10 percentile values (10% increments) and for each, determine the maximum value that  
  -- will divide the dataset row counts by that percentage
  select quantile,max(case when rownum/numrows <= quantile then SS_LIST_PRICE end) as quantile_value
  from 
  (
    select 
    row_number() over (partition by null order by null) -1 as seq,
    0.1+(seq/10) as quantile
    from table(generator(rowcount => 10)) v 
    order by 1
  ) quantiles,
  (
    select SS_LIST_PRICE,
      row_number() over (partition by NULL order by SS_LIST_PRICE) as rownum,
      count(*) over (partition by NULL) as numrows
    from STORE_SALES sample(1000 rows)
    where SS_LIST_PRICE is not null
  ) totals
  group by quantile
  order by quantile_value
)
select SS_LIST_PRICE as x,
(select max(b.quantile) from quantile_values b where b.quantile_value<a.SS_LIST_PRICE) as y1,
(select min(b.quantile) from quantile_values b where b.quantile_value>=a.SS_LIST_PRICE) as y2,
(select max(b.quantile_value) from quantile_values b where b.quantile_value<a.SS_LIST_PRICE) as x1,
(select min(b.quantile_value) from quantile_values b where b.quantile_value>=a.SS_LIST_PRICE) as x2,
coalesce(CONSORTIUM_SHARING.MOBILE.linear_interpolation(x1,y1,x2,y2,x),0) as y
from STORE_SALES a sample(1000 rows)
where SS_LIST_PRICE is not null
order by SS_LIST_PRICE

注意,从性能的角度来看,在最终查询中使用 min & max 并不理想,这就是我这次必须对表进行采样的原因。相反,我们可以构建自己的用户定义的表函数,利用我们所知道的值的顺序,一次性传递数据。但是我们现在有用来说明的,我们剩下的是从 0 到 1 的分布:

After

preprocessing.RobustScaler

使用对异常值稳健的统计数据缩放要素。

这种方法使用四分位范围,所以我们不是取最小值和最大值,而是取中间值,然后在两边再取一次。

在本例中,我们将在 WEB_RETURNS 表中缩放 WR_RETURN_AMT。

Before

with med as (select 
  median(WR_RETURN_AMT) as median_ret_amt 
    from WEB_RETURNS),
  lower_med as (select median(WR_RETURN_AMT) as lower_median 
    from WEB_RETURNS, med where WR_RETURN_AMT< median_ret_amt),
  upper_med as (select median(WR_RETURN_AMT) as upper_median 
    from WEB_RETURNS, med where WR_RETURN_AMT> median_dep)
select (WR_RETURN_AMT- lower_median) / (upper_median - lower_median) as wr_return_amount_scaled
from lower_med,upper_med,WEB_RETURNS

After

preprocessing.StandardScaler

通过移除平均值并缩放至单位方差来标准化特征。

使用与前面示例相同的 WEB_RETURNS 列。

with aggregates as (
  select avg(WR_RETURN_AMT) as avg_dep,
  stddev(WR_RETURN_AMT) as stddev_dep
  from WEB_RETURNS)
select (WR_RETURN_AMT - avg_dep)
        / (stddev_dep) as wr_return_amount_scaled
from aggregates,WEB_RETURNS

结论

通过写这篇文章,我学到了一些东西。

有用!

我认为这是一次巨大的成功。如果你把它们写成查询,会有一些样板文件,但是如果它们是由前端应用程序发出的,或者是代码库中的下推语句,那就不是问题了。电力变压器是可行的,但我会等到我需要它。

大部分功能都不吓人

滑动和挤压主要涉及标准聚合。长时间用不同的名字称呼他们,很容易忘记这一点。

然而,当从像 Python 这样的命令式语言转向像 SQL 这样的更具声明性的语言时,它确实倾向于要求重新思考,而不是直接移植代码。

Tableau 是一个很好的搭档

观想是对你试图实现的目标的最好强化。使用雪花,您可以轻松地在非常大的数据集上进行特征工程,而无需采样,只需记住,要求 Tableau 在图表上放置数十亿个点不是一个好主意。但是对最终结果进行采样要比对初始输入进行采样好得多!

一如既往,欢迎评论和反馈。

posted @ 2024-10-13 15:22  绝不原创的飞龙  阅读(452)  评论(0)    收藏  举报