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

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

用 Python 为人工智能竞赛创建一个平台

原文:https://towardsdatascience.com/creating-a-platform-for-ai-competitions-1184666f32e4?source=collection_archive---------25-----------------------

详细说明在开发 AI 竞赛平台过程中遇到的最重要方面。

去年今年我写了一篇博客,介绍我和我的同事 EliasPieter 、奥赞和 Cedric 专门为一年级的工科学生设计的人工智能竞赛解决方案。在这个竞争中,玩家可以编写他们自己的代理,该代理为他们与其他玩家的代理玩游戏。为了促进这一竞争,创建了一个平台,允许学生注册自己,上传他们的代码,并检查他们的代理在排行榜上的排名。排行榜根据定期模拟的游戏结果不断更新。这篇博客文章将详细讨论这个名为 Hexatron 的平台在今年的创建过程,内容安排如下:

  1. 高层综述及采用的技术
  2. 我们模拟器的详细讨论
  3. 讨论我们的数据库技术和数据方案
  4. 撮合及评级代理人技能
  5. 我们的前端概述

请注意,这只是我们平台的第二个版本。绝对有改进的空间!因此,任何形式的反馈都是非常受欢迎和非常赞赏的。

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

This year we even designed our own logo!

高层架构和使用的技术

以下是我们系统中不同组件及其交互方式的高级概述:

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

A (very) high-level overview of the architecture, consisting of three main modules: (i) a front-end for user registration, agent submission and display of our data, (ii) a SQL database with our data, and (iii) a runner that periodically samples two players from the database and simulates a game between them. Arrows depict the flow of data.

我们可以区分 4 个大组件。核心组件是我们的后端:一个基于 SQL 的数据存储。关于用户和提交的代理的数据通过前端收集并存储在后端中。而且所有的数据都是通过前端应用显示的。采样器组件定期从数据库中提取一对用户,以便使用模拟器组件在 docker 容器中模拟他们之间的游戏。配对是为了尝试和确保竞争激烈、激动人心的比赛。

代理合规性和基于 docker 的模拟

由于游戏是在服务器端模拟的,而且由于 python 脚本可能会对您的主机造成损害(想象一下shutil.rmtree('/')exec('sudo smth')),所以我们采取了一些措施,以确保不可能(或至少很难)向我们的系统提交代理并有效地对我们的服务器造成损害。这是通过向我们的平台提交代码时的一些初始检查,以及尝试在“沙盒”环境中模拟游戏实现的。

每个参与者需要实现一个函数,该函数接受某个游戏状态作为输入,并输出代理想要进行的移动。每次他或她向我们的平台提交新编写的代码,我们都会进行一些检查。首先,我们检查上传文件的大小是否没有超过一兆字节。这是为了避免用户提交会在我们的硬盘上分配太多空间的解决方案,并避免为每个可能的游戏状态硬编码最佳移动。其次,我们只允许使用 NumPy 和标准库中的某些包。第三,也是最后一点,不允许顶级执行代码(所有代码都必须包装在函数中),并且禁止一些构造,包括evalexecopenfilesubprocessdircache等等。如果所有这些检查都成功,则相应用户的最新提交将被更新为新代码,这将在下一次模拟中使用。

模拟是使用 Docker 容器执行的。每当一个新的代理被提交到我们的平台时,就会为该代理创建一个 docker 容器,其中运行着一个 flask 服务器。这个 flask 服务器有一个 post route,它接受一个游戏状态并把它的移动返回给代理。虽然 Docker 容器不太适合作为沙盒环境,但它确实使得通过提交的代码破坏系统变得更加困难。

后端技术和数据方案

作为后端技术,我们使用了 SQL。例如,虽然它比简单的 MongoDB 需要更多的开发工作,但它通常更高效。我们的数据方案(可能太复杂了)描述如下:

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

The data scheme used in our SQL store.

我们后端的中心对象是用户提交游戏。对于用户提交到我们平台的每个代理*,一个提交对象被创建并添加到 DB 中。此外,这个新添加的提交用于“覆盖”用户的活动提交。我们的运行器定期对成对的用户对象进行采样,并在它们之间运行模拟。该模拟的结果存储在新添加的游戏对象中,从而创建了新的等级对象,该对象代表用户的更新等级。而主动提交提交对象之间并没有真正的区别。我们确实做了区分,这样我们就可以允许在提交的代理之间切换功能。*

配对和技能评级

比赛的两个重要方面是如何尽可能准确地确定代理商的排名以及如何尽可能确保竞争性比赛。同样,现有的算法和技术被用来解决这些问题。

为了对代理进行排名,我们首先应用了微软开发的true skill系统。TrueSkill 类似于著名的 ELO 系统(起源于国际象棋),但有一些额外的优势,例如适用于 1 对 1 以外的游戏,并自然地处理玩家的不活跃状态(而在 ELO 系统中,这些玩家只会保持相同的等级)。虽然提到的优势在我们的场景中并不真正相关(在我们的场景中,游戏是 1 对 1 的,并且由于定期采样,不活动的情况不会发生),但 TrueSkill 系统是最近才出现的,因此值得一试。不幸的是,我们注意到真实技能等级变化太慢,使得比其他人晚加入比赛的人很难获得更高的等级。因此,我们在比赛中途重构回 ELO 系统。

为了确保有竞争力的匹配,我们使用了一个已经存在的算法,称为***,它在一个图中找到最大匹配。更正式地说:给定一个图 G = (V,E),该算法找到一个匹配的 M (它是边的集合),使得 V 中的每个顶点至多与 M 中的一条边关联,并且| M |(或者在我们的情况下是权重的总和)被最大化。这个问题本身非常类似于二分图匹配问题,可以通过应用 匈牙利算法 来解决(这是我们首先要研究的),但是后者在两组(不同的)顶点(通常代表人和工作)之间创建了一个匹配。举个例子,假设我们的系统中有四个玩家。一个玩家 (A) 拥有非常强的代理,一个玩家【B】拥有非常弱的代理,另外两个玩家【C,D】*拥有一般的代理。然后我们可以用加权图来表示我们的排行榜。这个图是全连通的,每个顶点对应一个玩家。来自顶点的每个输出边上的权重对应于这两个玩家之间的比赛质量(玩家之间的竞争越激烈,质量越高):

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

Representing our leaderboard of four players in a graph. The width of the edges depicts the match quality.

在这种情况下,Blossom 算法将返回 2 条边,使得它们的权重之和最大化,并且使得我们的图中的每个顶点恰好与这些边中的一条边相关联。这正是我们想要的:每个玩家与另一个玩家比赛,比赛质量的总和最大化。当然,这只有在顶点数为偶数时才有可能。在奇数个玩家的情况下,从所有玩家中均匀地抽取 1 个随机玩家,并从图形中移除。

在前端显示所有内容

前端主要使用 Flask 创建。我们平台上的每个可能页面都对应一条路线。当用户浏览这条路线时,从后端检索相关对象并提供给 JINJA 模板,然后呈现该模板。对于我们应用程序的交互部分(比如在特定日期范围内过滤游戏的滑块和不同的可视化),我们使用了 JavaScript 和 D3。下面是该应用程序的一些截图:

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

Screenshot of the leaderboard page

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

Screenshot of the game history page

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

Creating a new account

总结词

这就是了。现在,我已经简要介绍了在平台开发过程中需要大量时间的方面和组件。我希望这能对那些正在尝试创造类似东西的人有所帮助。一如既往:如果有任何不清楚的地方,如果有你想要我进一步阐述的事情,或者如果你有建议或反馈,请不要犹豫与我联系!你可以通过评论这篇文章或者给我发邮件来做到这一点。

也许明年,我可以再发一篇关于我们在年度人工智能竞赛的第三个版本中所做的改变的文章。到时候见,

吉勒斯、伊利亚皮特、奥赞塞德里克

使用变压器创建流行音乐发生器

原文:https://towardsdatascience.com/creating-a-pop-music-generator-with-the-transformer-5867511b382a?source=collection_archive---------4-----------------------

TLDR;训练一个深度学习模型生成流行音乐。你可以在这里用我们预先训练好的模型—【http://musicautobot.com】作曲。源代码在这里可以找到—https://github.com/bearpelican/musicautobot

在这篇文章中,我将解释如何训练一个深度学习模型来生成流行音乐。这是“构建人工智能音乐生成器”系列的第一部分。

快速提示:有几种方法可以生成音乐。非平凡的方式是生成实际的声波(wave netMelNet)。另一种是为乐器演奏生成乐谱(类似于乐谱)。我将解释如何做到后者。

我来这里只是为了目的地…

好吧!这里有一些很酷的音乐生成的例子给你。

我建了一个网站:MusicAutobot。它由我们将在本文中构建的音乐模型驱动。

歌曲#1(灵感来自里奇·汪妮——拉·邦巴)

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

Red Notes are generated by the model. Green/White notes are the original

歌曲#2(灵感来自帕切尔贝尔——D 中的佳能)

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

Feel free to press the red button and generate your own music!

注:MusicAutobot 最好在桌面上观看。对于那些手机用户,你只需要听下面的。

Generated Part starts at 0:03

Generated Part starts at 0:06

背景

Transformer 架构是 NLP 的最新进展,它在生成文本方面产生了惊人的结果。与以前的语言模型相比,变形金刚训练速度更快,长期记忆能力更强。给它几个词,它就能继续生成比这更有意义的整段文字。

自然地,这看起来非常适合创作音乐。为什么不给它一些音符,让它产生一段旋律呢?

这正是我们将要做的。我们将使用一个变压器来预测音符!

这是我们正在尝试做的事情的高层次图表:

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

用…音乐数据训练变压器

我们正在做的,是为音乐建立一个序列模型。取一个输入序列并预测一个目标序列。无论是时间序列预测、音乐还是文本生成,构建这些模型都可以归结为两个步骤:

第一步。 将数据转换成一系列令牌

第二步** 建立并训练模型以预测下一个令牌

在两个 python 库 music21fastai 的帮助下,我们构建了一个简单的库 musicautobot ,使得这两个步骤变得相对容易。

第一步。

将数据(音乐文件)转换成符号序列(音符)

拿一张钢琴单看起来像这样:

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

Piano Score

然后把符号化成这样:

xxbos xxpad n72 d2 n52 d16 n48 d16 n45 d16 xxsep d2 n71 d2 xxsep d2 n71 d2 xxsep d2 n69 d2 xxsep d2 n69 d2 xxsep d2 n67 d4 xxsep d4 n64 d1 xxsep d1 n62 d1

使用 musicautobot,您可以这样做:

More examples in this Notebook

注:musicautobot 在后台使用 music21 来加载音乐文件和令牌化。这种转换的细节将在下一篇文章中介绍。

第二步。

构建并训练模型以预测下一个令牌。

fastai 有一些惊人简单的训练代码用于训练语言模型。

如果我们修改数据加载来处理音乐文件而不是文本,我们可以重用大部分相同的代码。

训练我们的音乐模型现在就像这样简单:

去试试这个 笔记本 来训练自己吧!

是的,就是这样。

现在让我们看看它是否真的有效。

在下一部分,我将使用我在一个大型 MIDI 数据库上预先训练了几天的模型。你可以在这里直接玩预先训练好的模型

预测流行音乐

第一步:创建一小段笔记:

Snippet from here

看起来是这样的:

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

First few notes of La Bamba — sounds like this

**第二步:**把它喂给我们的模特:

Hyperparameters:[Temperature](https://cs.stackexchange.com/questions/79241/what-is-temperature-in-lstm-and-neural-networks-generally) adjusts how "creative" the predictions will be. You can control the amount of variation in the pitch and/or rhythm.[TopK/TopP](https://twitter.com/Thom_Wolf/status/1124263860104507393) - filters out the lowest probability tokens. It makes sure outliers never get chosen, even if they have the tiniest bit of probability

瞧啊。

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

听起来是这样的:

这其实是我得到的第一个结果,但副作用可能会有所不同。你可以在这里创造你自己的变化

真正的考验:流行音乐理论

据 HookTheory 的牛逼人士称,现代音乐中最流行的和弦进行是 I-V-VI-IV 和弦。

你可能以前听过。很多流行歌曲中都有。

喜欢每一个。单身。同POPULATION宋。

All these songs use the I-V-vi-IV chord progression

构建和弦

让我们测试我们的模型,看看它是否能识别这个和弦进行。我们将输入前三个和弦“I — V — vi”,看看它是否能预测“IV”和弦。

以下是你如何用音乐创作前三个和弦 21:

看起来是这样的:[C-E-G]——[G-B-D]——[A-C-E]

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

Model Input: First 3 chords (I-V-vi)

预测“IV”和弦

现在,我们把这些和弦输入我们的模型来预测下一个:

以下是我们得到的结果(包括 3 个输入和弦):

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

Model predicted the final chord — IV

万岁。该模型预测了音符[F-A-C]——也就是“IV”和弦。

这正是我们希望它预测的。看起来我们的音乐模型能够遵循基本的音乐理论,制作每一首流行歌曲!至少是和弦。

自己测试一下

不要只相信我的话。在音乐机器人上试试吧:

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

你所要做的就是按红色按钮。

注:10 次中有 8 次,你会得到一个“IV”和弦或者它的一个 转位 。我不能保证确定的结果。只有概率。

这就是全部了!

现在你知道了训练一个音乐模型的基本步骤。

这篇文章中的所有代码都可以在这里找到:https://github.com/bearpelican/musicautobot

使用我们刚刚建立的模型播放和生成歌曲:
http://musicautobot.com
^这些是实时预测,请耐心等待!

接下来:更深的潜水和更深的模型

我可能忽略了一些细节。还有呢!

第二部分。训练音乐模型的实用技巧——深入研究音乐编码和训练——它将涵盖我刚刚忽略的所有细节。

第三部分。建立一个多任务音乐模型*——*训练一个超级酷的音乐模型,它可以协调,生成旋律,以及混音歌曲。下一个令牌预测是如此…基本。

第四部分。使用一个音乐机器人来重新混合链烟——我们将使用 musicautobot 在 Ableton 重新混合一个 EDM drop。仅用于纯粹的娱乐目的。

特别感谢耶鲁安·克斯滕杰瑞米·霍华德的指导,南方公园公地帕拉帕维克的支持。

用扔量子硬币来分析量子行走

原文:https://towardsdatascience.com/creating-a-quantum-walk-with-a-quantum-coin-692dcfa30d90?source=collection_archive---------18-----------------------

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

So how does a quantum particle walk?

那么什么是“散步”呢?

一次散步正是我们在公园或其他地方进行漫无目的的漫步时所做的事情。我们站在某个地方(可以认为是公园里有一些坐标的位置),迈一步,最后到了另一个地方(是公园里的某个其他位置)。

*漫步,*或者更准确地说,关于本文的内容,随机漫步是系统从一个状态到另一个状态的转换序列。需要注意的一个重要事实是,这种转移是随机的,或者形式上有一个概率分布。

它们到底为什么重要?

量子行走在一些量子算法的开发中非常有用。比如复杂性分离和通用计算模型。最后都是值得研究他们的。

伯努利随机漫步

在深入研究量子行走之前,研究一个简单的一般随机行走是有用的。考虑以下场景:

你站在一个名为原点的点上,用整数 0 表示。你可以向右或向左移动,通过掷硬币来决定( 你走路的随机性的原因)。你如何分析这种行走,比如说硬币翻转 100 次?

简单来说,你有 50 %的机会向左或向右移动,相当于硬币有 50 %的机会是正面或反面。所以如果你把到达某个地方的概率列表,你会得到这样的结果:

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

放弃在时间 t 到达位置 n 的概率

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

或者前一状态即在时间(t-1)的概率的平均值。这导致了对离散马尔可夫链的整个讨论,马尔可夫链是强大的模型,并且已经被广泛地翻译到量子世界。简而言之,马尔可夫链是这样的:在给定历史的所有信息的情况下,在当前状态下着陆的概率是 与给定 之前的状态下,在当前状态下着陆的概率是

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

其中 n(a)代表系统在时间 t 的状态。

一般来说,这是一个由下式给出的二项式分布:

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

Generic probability of landing at state n at time t

随着我们增加 *n,*二项式分布可以更好地近似为以下形式的正态分布:

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

The Gaussian or the normal distribution

量子行走是怎样的?

现在让我们把上面的行走转换成量子行走。如果你知道量子力学的基本原理,那会很有帮助。但就算你不说,我也会一路解释。在分析行走之前,我们需要创建一个 孤立的量子系统 ,行走将在那里发生。

量子世界最基本的属性是叠加,或者一个系统同时存在于几个状态的能力。形式上,我们定义了一个无限维希尔伯特空间 ,它将表示我们的行走器的位置(与上面讨论的伯努利行走示例中的线上的点相对比)。我们称这个希尔伯特空间为 a。

我们已经把沃克转移到量子世界了。现在我们需要翻译硬币。离散硬币有两种不同的状态可供选择(正面或反面)。类似地,我们定义另一个希尔伯特空间 B,它是二维的,计算基|0 >和|1 >。通过**计算基础,**我简单的意思是,通过下面的线性组合方程,可以在这个希尔伯特空间中获得任何叠加形式:

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

for given values of scalars a and b

如何刻画这样的量子币?

如果我们能找到一个符合我们对 B 的定义的量子系统,我们就可以走了。而且谢天谢地,这样的量子系统是存在的:电子。

考虑一个孤立电子。它可以以它的*、自旋的任何叠加形式存在,但是正好有两个*方向可以通过上述线性组合方程组合起来产生任何可能的自旋。

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

Consider the left one as |1> and the right one as |0> and we have our computational basis realised in physical terms

原来量子力学中的 测量破坏了系统的叠加态。 这仅仅意味着当你测量处于叠加态的电子时,它的状态会坍缩,变成|1 >或|0 >。

这难道不是非常接近于掷硬币吗?!?!

事实证明,在我们前进之前,我们需要一种机制来将我们的 walker 系统(希尔伯特空间 A)和 coin 系统(希尔伯特空间 B)结合成一个单一的系统。这是通过以下张量运算完成的:

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

Tensor equation for C

如果你不完全理解张量运算,就简单地把它想象成量子力学中的一种方式,把硬币给行走者,这样他们就可以一起行走。从数学上来说,我们发现了一种方法,可以同时在一个无限维的希尔伯特空间(A)和另一个二维的希尔伯特空间上操作,就好像它们是 c 描述的另一个更大的量子系统的子部分一样。

C 中的任何状态都可以用上面给出的张量方程来描述,例如|n>|0 >,其中|0 >属于 B,表示电子的负半自旋,而|n >属于 a。

一个人可以用几个硬币;我们将使用最著名的一种——哈达玛硬币。

哈达玛是由下式给出的矩阵:

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

Hadamard matrix

有趣的是,它在 B (the |0 >和 the |1 >)的计算基础上有以下行为:

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

很容易观察到哈达玛是如何将一个态转换成叠加态的。

除了硬币操作符,让我们定义另一个操作符,类似于移位操作符,它根据量子硬币投掷的结果走一步。

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

Looks exactly like the decision of the classical coin toss.

现在要靠我们自己走完全程了。假设我们系统的原始状态是|n> |0 >。为了将其提升到叠加状态(,这将为其提供一些随机性,应用以下张量(在 C 中):

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

其中 I 是属于希尔伯特空间 A 的单位矩阵,H 是属于希尔伯特空间 B 的阿达玛币;两者在希尔伯特空间 c 中形成一个张量,直观上可以认为 I 作用于|n >而 H 作用于|0 >(或者简单地说他们作用于和他们一样属于希尔伯特空间的向量)。

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

作用于|n >的同一性就这样保持着。相反,哈达玛 T12 将| 0>提升到叠加状态,在希尔伯特空间 C 中产生以下内容:

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

分布张量,可以得到如下结果:

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

移位操作符应用于上述操作会产生以下结果:

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

这里,我们看到了在应用投掷硬币之后,由移位操作符执行的行走器位置的最终叠加状态。

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

这是我们的量子步行者的概率分布。总之,如果我们知道助行器的初始状态,那么在任何给定时间的状态都可以定义如下:

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

结论

在这里,我们已经完成了对量子行走器的定义,并得出了一个方程,它告诉我们,在任何给定的时间内,我们的量子系统的演化。

理解最基本的量子行走,因为这使任何人都能够将其扩展到离散马尔可夫链的实现到其量子等价物。这些想法也适用于连续的量子行走,这是我们不久将探讨的。敬请关注!

从头开始创建放射科医生

原文:https://towardsdatascience.com/creating-a-radiologist-from-scratch-d776eb944399?source=collection_archive---------19-----------------------

在我的第一次数据科学竞赛中,我是如何制作 x 射线阅读器来检测肺部缺陷的

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

A Computer learns to see (albeit at the wrong place)

我不知道该怎么做。

除了我自己的 x 光片,我从来没有看过其他的 x 光片,更不用说那些显示出身体缺陷的 x 光片了。我不知道该看什么来确定缺陷。一个对放射学毫无经验的人怎么能告诉一台机器如何看 x 光图像呢?

六个小时后,我仍然不知道如何确定是否有缺陷。但不知何故,我能够创造出一台在检测缺陷方面几乎和放射科医生一样好的机器。

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

Which x-ray shows a disorder?

我浏览着我的笔记本电脑屏幕,试图了解我面前的网页。有两个文件夹:都包含 x 光图像。一些图像被标记为阳性,一些图像被标记为阴性。我从两个标签上各拿了一个,老实说我看不出有什么不同。在我旁边的是我的九个队友,其中两个正在解决同一个问题。作为唯一一名本科生(也是唯一一名除了微积分课程之外没有接受过任何大学数学正规教育的学生),我周围要么是研究生,要么是从事数据科学家、数据工程师和公共卫生研究的专业人士。

我们正在开发一种机器,可以对 x 射线扫描是否包含气胸(基本上是肺萎陷)进行分类,然后识别图像中的萎陷区域。除了不能区分,我们必须找到一种方法来显示图像中的哪个区域有差异。虽然图像可能有标签,但它没有标记气胸出现的区域。虽然我们的团队中有两个具有放射学经验的人,但我们肯定不能依靠他们在 3000 多张气胸阳性图像中告诉我们受影响区域的确切位置。作为我第一次 datathon 的最后一个个人障碍,当我使用 Pytorch 时,每个人都在使用 Keras,这两个框架都是深度学习的框架。在很大程度上,我们无法相互理解。

再说一次,除了不能区分,我们必须找到一种方法来显示图像中的哪个区域有差异。你在让一个盲人教一台机器如何“看”和看图像来确定它是否是气胸阳性。最重要的是,你能得到的最接近的帮助是在两个不同的编码框架中工作,这让我们感觉像是在用两种不同的方言交谈。然后,我们不得不奇迹般地克服这种情况,将我们建立的任何东西与一个可能用于拯救人类生命的系统结合起来。这太疯狂了。

你在让一个盲人教一台机器如何“看”和看图像来确定它是否是气胸阳性。

火的洗礼

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

Our group’s presentation at the National University of Singapore (NUS)-Massachusetts Institute of Technology (MIT) Datathon

该问题分为两部分,分类和分段。分类器任务是根据图像的标签对图像进行分类(例如,x 射线图像是否显示气胸(阳性)或不显示气胸(阴性)),而分割任务是通过将图像分割成“部分”来识别图像的哪一部分具有所讨论的对象(例如,如果是阳性的,则阳性区域将被给予一个边界,该边界将其与图像内的其他对象分开)。我自愿参加分类,因为它似乎更容易。

由于我们在不同的框架中编码,我不得不制作自己的分类器。在我们的三人小组中,我们要比较我们的分类器,并选择一个最准确的。我的一个队友把预处理 x 光图像作为一项工作。作为最后的警告,我两周前才开始学习深度学习,我也从未仅仅使用 Pytorch 制作过真正的模型。在我写这篇文章的时候,我还没有独家使用 Pytorch(但是我快到了!).

相反,我只用过基于 Pytorch 的 fastai。这是一个初学者友好的库,它简化了加载图像数据集,在深度学习模型上训练它,然后用它来预测新图像的标签的整个过程。他们在网上也有完整的课程,我用第一课作为指导,制作了我的第一个图像分类器。但这是我在加入这个数据马拉松之前制作的第一个也是唯一一个分类器。

所以没有人问我如何在 Pytorch,中实现它,我简单地复制了我的第一个项目的步骤。我从医学成像信息学协会(SIIM)获取了提供的图像数据集,并将其远程连接到 NSCC 国家超级计算中心的 GPU。我用 Python 处理 Excel 的知识清理了 labels 文件(仍然花了我两个多小时才找到一个简单的公式,将一列中所有非零值都改为 1)。

我使用这个数据集在 Resnet-50 上训练模型。Resnet-50 是一个模型/神经网络,之前显示了数百万张图像,以产生一系列“权重”。您可以将权重视为代表模型中神经元之间强度的一组数字,这些数字有助于模型“观察”图像。直觉是,使用 Resnet-50 的权重比使用随机选取的权重更好,因为该模型已经更善于“观察”图像。这个过程叫做迁移学习。再打个比方,就像老师(预训练的模型)把知识传授给学生(针对具体问题的新模型)。

同一个图像有 4 种不同的尺寸。我从 1024 x 1024 像素的最高分辨率开始,因为更高质量的图像可以帮助计算机“看”它,因为它更清晰,对吗?这个过程用了一个半小时,几乎把给我的 GPU 都烧光了。

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

Fit one cycle policy is a learning acceleration technique, but running on 1024 x 1024 still took almost 2 hours 😦

我给电脑输入了总共大约 44,000 张高清图像。但令人惊讶的是,默认设置的准确率为 86%。最后,我不得不放弃这种方法,因为我的 GPU 经常内存不足。

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

I was 6MB away from remotely crashing a foreign government’s hardware

自学

一开始就达到了 86%的准确率,太神奇了。这比猜测要好,也可能比我看 x 光的能力要好。但是它是怎么到那里的呢?我没有给出任何关于如何查看图像的说明或提示,只是说哪个图像对于训练测试来说是正面的或者不是正面的,但是它能够在没有标签的验证集上进行预测。机器是怎么学会怎么学会的?

从经验和观察中抽象出想法是学习的基本特征,机器在这方面做得越来越好。

看看这个模型是怎么来的。机器“学习”的来源是给它提供例子。从例子中,就像人类回忆经历一样,它能够找到模式。这不是死记硬背,因为它能够归纳并应用于新的未知的数据。我没有写任何明确告诉它如何的代码。它自己发展了一套规则。从经验和观察中抽象出想法是学习的基本特征,机器在这方面做得越来越好。所以,我对放射学毫无经验,怎么能告诉一台机器如何观察它呢?我没有,这也正是它学习的原因。

回到过去,我被迫使用较小的图像,所以我选择了 512 X 512 的图像尺寸。与之前的运行相比,我很惊讶它的速度有多快:

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

我的模型达到了几乎相同的精度,但速度却快了十倍。哇哦。

我错了。与以前的版本相比,它可以在相同的时期更好地对进行分类,并且分辨率几乎是以前的一半。我的直觉被打破了。也许模型不需要考虑不必要的细节,这些细节使得更高的分辨率得以呈现。如果我再做一次,我会得到更高的初始精度。我花了最后几个小时调整模型来改进它。

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

The final version of the model. I actually got 89% accuracy but I wasn’t able to save the version.

最终我的分类器表现最好(下一个有~71%的准确率),它和分割模型结合完成了气胸检测器。我已经设法利用我在深度学习方面的稀缺知识制作了一个模型,这个模型的表现优于专业人士制作的其他模型*。这个模型既不是建立在复杂的预处理技术上,也不是建立在优雅的算法上。仅仅遵循一些简单的技术就让我惊讶地发现,我能够构建一个具有竞争力且精确的模型。*

还是一个 Noob

但一路走来,我欠队友的支持。我曾请 CS 博士生帮助我处理 Python 回调,并试图覆盖 fastai 库中的一些预建函数。我从医学影像分析师那里获得了如何预处理图像的技巧(不幸的是,我无法实现,因为我不知道如何对它们进行编码)。我得到了放射科医生的帮助,他告诉我如何手动确定气胸的位置。我还从一位数据工程师那里得到了关于如何通过热图看到模型“看到”的帮助。这个模型还有很多我可以改进的地方,但是如果没有这些地方,我一开始就不可能造出一个来。

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

Grad-CAM: Where the model “looks”. (Middle) The hotter the area is, the higher focus of the model on that area. Notice how it ignores the labels and arrows on the side. Our radiologist confirmed that the Pneumothorax was within the hot area. (Right) The image in grayscale.

最重要的是,他们分享了他们如何进入机器学习的故事。虽然大多数人有适当的分析背景,如生物医学工程、计算机科学和纯数学学位,但没有人在课堂上学到这些。无论是自动化他们的任务还是增强他们的研究洞察力,他们都能够通过参加在线课程、获得在线证书和参加 Kaggle 竞赛来自学。

我们正处于这样一个时期,那里有许多有待开发的人工智能系统的未开发领域,我们正处于实现提高我们生活质量的潜力的过程中。

缺乏正规培训和气胸问题的存在反映了人工智能应用的整体状况,因为它目前正在改变我们的工作方式。我们正处于这样一个时期,那里有许多有待开发的人工智能系统的未开发领域,我们正处于实现提高我们生活质量的潜力的过程中。单看气胸问题,有人开发一种解决方案,在这个过程中不需要放射科医生,就像自动驾驶汽车不需要司机一样。同样,这在缺乏放射科医生的地方有巨大的应用。将此扩展到人力稀缺的专业工作。

然而,这再次延伸到危害,如劳动力减少和缺乏对失业工人的再培训,使用人工智能做出不道德的判断,以及使用人工智能系统来针对使社会和资本不平等永久化的被剥夺权利的群体。这些最终将成为普通人必须解决的主要问题的一部分,就像贫困和政治一样,因为这只是一个时间问题,这种系统将主宰我们的生活,就像互联网塑造大多数人与现实的互动一样。

这种情况告诉我的最后一件事是,教室不足以让我了解这个世界。仅有理论是远远不够的,只要存在独特和未知问题的现实世界的变化快于课堂教学的变化,课堂课程就无法跟上。

你可以和我团队中的许多人一样有背景,但这只能让你在某些领域拥有优势。从收集信息,到训练模型,再到可视化,这些都需要专业化,而这通常不是一个人就能完成的。你需要某个领域的专业知识,以及合作和理解其他学科的灵活性。这些人花时间学习机器学习,因为他们意识到这是一种可以帮助他们的工具。

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

像他们一样,我会继续学习,继续练习。希望这不是我唯一从头开始训练的东西。

我用的代码在我的 资源库 上可以作为 jupyter 笔记本使用。看看吧!

在 Microsoft Azure 中创建用于流式物联网数据的无服务器解决方案—第一部分

原文:https://towardsdatascience.com/creating-a-serverless-solution-for-streaming-iot-data-in-microsoft-azure-part-i-5056f2b23de0?source=collection_archive---------37-----------------------

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

Photo by Markus Spiske on Unsplash

毫无疑问:物联网设备的使用(以及它们产生的数据)正在疯狂增长。根据 Gartner 的一项研究,到 2020 年,将有 58 亿台物联网设备在线,比 2018 年的 39 亿台增长 46%。到 2025 年,物联网设备产生的数据总量预计将达到惊人的 79.4 吉字节,这几乎是 2018 年数据量的六倍。

奇妙的是,像微软 Azure 这样的平台使得接收、存储和分析这些数据变得非常容易。在本文中,我将向您介绍如何从物联网温度传感设备采集流式遥测数据,并将其存储在数据湖中,同时对其进行一些基本的异常检测。本文的所有代码都可以在这个 GitHub 库中找到。

该解决方案的组件包括:

  • Azure 物联网中心,用于接收物联网设备发送的数据,并将原始数据捕获到数据湖。
  • Azure Stream Analytics ,用于处理发送到物联网中心的数据并检测异常。
  • Azure Event Hub ,用于在检测到异常时从流分析接收消息,以便进行下游处理。
  • Azure Data Lake Storage ,用于存储发送到物联网中心的原始数据,以供以后可能的分析。

先决条件

如果您想跟进,您必须运行位于链接的 GitHub 存储库的terraform文件夹中的 Terraform 部署。在存储库的自述文件中有这样做的说明。这是可选的,因为在整篇文章中我会有大量的屏幕截图,但无论如何都要挖掘并构建自己的实验室!

解决方案的详细组件

Azure 物联网中心

Azure 物联网中心提供了一种简单的 PaaS 产品,允许物联网设备向消息传递端点发送数据。它们很容易扩展,您可以从每月零成本开始(免费层每天提供多达 8,000 条消息,非常适合概念验证)。当部署实时安装时,你应该总是从基本层或标准层开始,而不是免费层,因为根据微软的说法,你不能将免费层集线器升级到其他层。在做出决定之前,请仔细查看提供的功能列表和消息吞吐量数字,当然,您可以在以后扩展解决方案。

为了允许设备连接到物联网集线器,您必须提供设备身份。这可以通过 Azure CLI 界面轻松完成,如下所示:

Commands to create a new IoT Device in IoT Hubs

这将显示在各种可用的客户端库中使用的连接字符串(其中有很多)。

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

您还必须创建共享访问策略,以允许下游应用程序(如 Azure 流分析或 Azure 函数)访问消费设备消息。在本例中,Terraform 模板已经创建了一个名为“streamanalytics”的模板,它被授予了服务连接权限,允许它从 hub 读取消息。有关可能授予的所有权限的列表,请参见

提示:永远不要使用内置的 ***iothubowner*** 策略,因为那样会授予 hub 基本上无限制的权限。此外,为每个消费应用程序创建一个策略,以便您可以轻松地轮换访问键,而不必在大量地方替换它们。

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

在使用消息时,您希望为每个读取消息的应用程序配置不同的使用者组。这允许每一个独立操作,例如跟踪它们在消息流中的位置。在我们的例子中,部署创建了一个streamanalytics消费者组,因为它将读取消息。

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

最后,还有路由的概念,它决定消息被路由到哪里!这允许你向多个端点发送消息,比如 Azure Event Hub 兼容的端点、服务总线队列和 Azure 存储。后者对于捕获原始数据并将其存储到数据湖中非常有用,这就是我们在这里配置它的原因。

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

配置存储端点时,您可以根据文件夹结构配置文件的创建方式。您还可以配置文件写入的频率,以及在将文件写入存储帐户之前必须通过的批处理量。

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

提示:为了获得数据的全功能以后使用,请确保您配置的存储帐户为Azure Data Lake Storage gen 2启用。此外,确保文件的路径和名称是可排序的;例如,您可以确保路径以年开始,然后是月,然后是日,等等。

Azure 数据湖存储

Azure 数据湖存储提供了一种健壮且经济高效的方式来在现有的 Azure Blob 存储平台上存储大量数据。它与众多分析平台完全兼容,如 Hadoop、Azure Databricks 和 Azure SQL 数据仓库。虽然在此概念验证中未启用,但您可以通过将网络访问限制为仅授权的公共 IP 或 Azure VNET 范围,以及仅 Azure 可信服务(在这种情况下是必需的,因为我们使用了物联网中心)来轻松保护对存储帐户的访问。

Azure 流分析

Azure Stream Analytics 使用简单易学的 SQL 语法,可以轻松处理流式物联网数据。在我们的例子中,我们使用内置的异常检测功能来检测意外的温度变化。

流分析作业基于流单元 (SUs)进行扩展,流单元定义了分配给特定作业的计算资源(CPU 和内存)的数量。

流分析部署有三个主要部分:输入、输出和处理数据的查询。让我们更详细地看一下它们。

投入

输入定义了流分析从哪里获取数据进行处理。在我们的例子中,我们将输入定义为 Azure IoT Hub,它是作为我们部署的一部分创建的。

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

因为该资源是通过自动化部署创建的,所以已经输入了用于连接到物联网中心的所有信息。如前所述,您应该始终专门为流分析连接创建特定的共享访问策略,而不是使用内置的高权限策略(或任何其他预先存在的策略)。您还希望确保创建一个特定的消费者群体。通过点击“测试”按钮,我们可以确认一切工作正常。

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

Azure 流分析也支持从 Azure Blob 存储和 Azure 事件中心接受数据。有关不同类型的流输入的更多细节,请参见这份微软文档

还值得注意的是,流分析接受一种不同的输入,称为参考数据。这用于加载静态数据集,这些数据集可用于连接和丰富流数据。例如,您可以使用一个包含不同传感器设备的详细信息的文件,作为在流的输出中包含有用信息的手段。有关使用参考数据流的更多详细信息,请参见本微软文档

输出

Azure Stream Analytics 支持其产生的数据的大量输出,包括 blob 存储、Azure SQL 数据库、事件中心和许多其他内容。有关可能输出的完整列表,请参见本文档。

在我们的例子中,我们为 Azure Event Hubs 配置了一个输出,它被用作检测到的异常的目的地。与输入一样,明智的做法是使用特定的授权策略来允许流分析作业连接到事件中心。

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

请注意,有一个属性我们必须手动设置,因为 Terraform 提供者当前(截至 2019 年 11 月)不支持它,这就是分区键列。正确地设置这一点很重要,这样可以确保相关事件组总是在同一个分区中结束,这样就可以按顺序处理它们。在我们的例子中,一个合理的选择是device列,因为这样我们可以确保来自一个特定设备的所有事件都在同一个分区中结束。

询问

查询是定义流分析所做工作的核心。在这里,我们可以使用简单的 SQL 方言来聚合、转换和丰富通过流分析作业传输的数据。对语言和所有可能性的完整解释将会是一篇独立的文章,所以现在我们将简单地描述当前作业中的查询。

该查询由三部分组成。第一种使用内置的[AnomalyDetection_ChangePoint](https://docs.microsoft.com/en-us/stream-analytics-query/anomalydetection-changepoint-azure-stream-analytics)功能来检测温度数据中的异常,通过识别的设备进行分区,并限于最近 30 分钟的数据。我们还在数据中设置时间戳列,以便流分析知道如何对数据进行排序。第二部分从异常检测函数的输出中检索两个值,这两个值告诉我们数据是否被认为是异常的,如果是,算法对数据的意外程度如何。最后,我们查询一些数据被检测为异常的字段,并将结果输出到异常作业输出(之前定义为事件中心)。

这样一来,我们就可以回顾我们概念验证的最后一个组件,即 Azure Event Hubs。

Azure 活动中心

Azure Event Hubs 是一个可扩展的 PaaS 产品,为应用程序提供发布/订阅消息平台。在我们的例子中,我们使用它作为目的地来接收检测到的异常事件,以便由一个或多个下游应用程序进行处理。

活动中心由以下组件组成。

活动中心

事件中心是一个逻辑存储桶,或者用消息传递体系结构中常用的一个短语来说,是一个主题,相关的事件在其上发布。在事件中心内,您可以定义一些属性,如分区数量(提示,选择比您认为需要的更多的分区,因为如果不重新创建事件中心,它们将无法更改)、消费者组(类似于物联网中心,用于定义处理数据的应用程序,以确保同步应用程序在流中的位置)、捕获(便于将事件发送到存储帐户)和消息保留(消息应在中心保留多长时间;考虑您可能需要重新处理多长时间,最多 7 天)。

在我们的例子中,我们定义了一个名为anomalies的事件中心,它将从我们的流分析作业接收输出。

为什么要使用事件中心而不是更简单的队列产品,比如 Azure Queues?通过使用发布/订阅消息总线,而不是队列,我们考虑到了多个消息消费者的可能性;例如,我们可以使用一个消费者生成松弛警报,但是也可以将结果发布到仪表板的某种数据存储中。正如后面所讨论的,我想在这方面准备一些东西,但是我已经超过了我自己为这篇文章规定的时间限制,所以将推迟到后面的文章。

事件中心名称空间

事件中心名称空间是相关事件中心的逻辑集合,是我们为名称空间内的所有事件中心资源定义分配的处理能力的地方。与存储帐户一样,事件中心命名空间名称必须是全局唯一的。

在我们浏览完各种资源之后,让我们通过系统实际运行一些数据,看看结果!

实时数据流

首先,我们必须模拟一个物联网设备向 Azure 物联网中心发送数据。为此,我构建了一个 Docker 容器,您可以使用下面的命令运行它。您将需要我们在上一节中检索的 IoT Hub 连接字符串。请确保在单独的命令提示符下运行该命令,因为它会一直控制命令行,直到您退出容器。

接下来,我们需要启动流分析作业。这可以通过门户来完成。

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

您可以选择“现在”作为开始时间,这样作业将从当前日期和时间开始消耗数据。

Query窗格中,您可以单击Test Query Results按钮来查看查询的示例输出。

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

注意:因为只显示异常情况,您可能实际上看不到任何结果。在我们有意引入异常数据的情况下,您可以在下一个操作后重复该步骤。

现在,为了显示异常到达事件中心,我们将引入一些显著不同的数据,从而触发异常检测算法。

首先,退出容器进程,然后指定一些附加选项重新运行它,如下所示。

为了显示输出正在到达事件中心,我们可以浏览到事件中心名称空间资源的Metrics面板,并查看Incoming Messages度量。确保您选择 30 分钟作为您的时间窗口,以获得最佳视图,您可能需要点击几次刷新按钮。

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

摘要和待办事项

在本文中,我们介绍了使用 Azure 平台从物联网设备接收和使用消息的参考设置,包括物联网中心、Azure 流分析和 Azure 事件中心。在接下来的一篇文章中,我将介绍如何使用 Azure 函数从这个解决方案中消费和生成 Slack 警报。敬请期待!

使用 TensorFlow 创建智能秤

原文:https://towardsdatascience.com/creating-a-smart-scale-with-tensorflow-cf62a834b670?source=collection_archive---------30-----------------------

想要尝试机器学习,同时构建一些有用的东西吗?这里有一个有趣的周末项目,使用 TensorFlow 从你的体重秤图片中自动读取你的体重,并绘制出一段时间的图表。您将学习到 TensorFlow 对象检测 API 的基础知识,并能够将其应用于这个和其他图像分析项目。

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

Weight over time as automatically detected using TensorFlow from analyzing pictures (the line is more jagged on the right side because there are more data points)

这篇文章故意写得很短,这样你可以很快得到一个很好的概述,同时提供包含更多细节的其他材料的链接。就这样,让我们开始吧!

获取您的秤的图像

首先,与任何机器学习项目一样,数据是关键。每次你站在你的体重秤上,拍一张清楚显示你体重的照片。为了获得最佳效果,可以在不同的光线条件下拍照,也可以在房子的不同区域拍照。100 张图片应该可以作为一个相当好的训练集。

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

如果你的照片被备份到 Google 相册,那么你可以使用一个脚本,比如这个来下载它们。

标记您的图像

接下来,用一个工具给你的图片加标签,比如 labelImg 。他们的 docker 映像很容易设置,只需几个简单的命令就可以使用。我在整个刻度周围添加了一个名为“刻度”的标签,在圆形显示屏周围添加了另一个名为“读数”的标签。labelImg 将创建 XML 文件,每个图像一个,看起来像这个

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

Image of a scale with bounding boxes drawn around the scale and also around the display

训练一个神经网络来检测你的秤

一旦你有了数据,你就可以训练神经网络来检测你的图像的比例。概括地说,步骤如下:

  1. 安装tensor flow 对象检测 API
  2. 安装用于向谷歌云机器学习(ML)引擎提交作业的gcloud命令行工具。
  3. 创建一个谷歌云平台存储桶。您的训练数据将存储在这里。
  4. 上传预先训练的模型,开始你的训练。TensorFlow 为模型动物园提供了一些常见的模型。ssd_mobilenet_v1_coco是一个流行的选择,它在简单模型的速度和准确性之间取得了良好的平衡。
  5. 准备您的自定义输入数据并上传到您的云存储桶。
  6. 将张量流对象检测源代码捆绑到 ML 引擎上运行。
  7. 更新物体检测配置文件。你可以通过最小的改变来重用
  8. 向 ML 引擎提交培训作业。请注意,ML 引擎需要几分钟时间来启动作业。
  9. 当您的训练作业运行时,它每十分钟将其状态写入您的存储桶。您可以使用 TensorBoard 对其进行监控,并寻找曲线的收敛性。如果在单个 GPU 上运行,这个作业可能需要几个小时才能收敛。
  10. 当你的模型被充分训练后,下载并导出它。
  11. 使用您导出的模型来执行一些推理。您可以使用 TensorFlow 的演示版 Jupyter 笔记本检查您的结果。

这里记录了我为每个步骤运行的确切命令,包括我为帮助完成任务而编写的脚本,所以请在那里查看详细信息。也可以参考官方快速入门指南了解更多。

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

Screenshots of TensorBoard; left: loss curve converging; right: inferred output shown side-by-side with the hand-annotated bounding boxes

训练一个神经网络来读取显示器

现在你已经训练了一个神经网络来检测你的体重,下一步是训练一个从体重计上读取你的体重。

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

幸运的是,这个过程几乎和你已经做过的一样。概括地说,步骤如下:

  1. 使用该标度的训练模型来自动裁剪出显示器的图像。你可以试试这个任务的我的脚本
  2. 标记这些图像中的每个数字,以教会神经网络识别它们。我为每个数字(0–9)和“%”使用了一个标签。我的标签图传递给物体检测代码是这里是
  3. 使用与上述相同的步骤来训练神经网络识别数字。由于训练数据的差异,使用稍微修改的物体检测配置文件标签图

把它放在一起

如果你已经做到了这一步,恭喜你!你已经训练了一个模型来识别你的体重,然后再做一次来读取你的体重。现在,您可以将它放在一起,自动分析您拍摄的所有照片,读取您的体重,并使用简单的 HTML 文件(如 this )绘制其随时间变化的趋势。

精明的读者可能会想,为什么我要运行两次,而不是只训练一个模型来识别比例和数字。原因是我不认为单一模型在我的数据上是有效的。对象检测管道通常用于训练大小为 300x300 或类似大小的图像。它当然可以在更大的图像上训练,但这可能需要更长的时间来收敛。当我的图像大幅缩小时,圆形显示变得不可读。因此,我首先训练一个神经网络来找到圆形显示(我仍然在我的配置中将训练图像的大小增加到 600x600)。利用这一点,我们可以从图像中自动提取出圆形显示,在我的数据中,它恰巧是大约 300x300,现在可以使用这些提取的图像在其原始分辨率下训练另一个神经网络来识别数字。

你可能还想知道为什么我不简单地使用现有的 OCR 工具包来读取刻度上的数字。我试了试 Tesseract ,但是它不能识别任何文本,即使在一些预处理之后。我预计,最终,在这个数据集上训练的神经网络无论如何都会胜过传统的 OCR 软件。

最后,我希望这有助于您了解 TensorFlow 的对象检测,并启发您自己构建一些很酷的项目。请在评论区留下你的想法和反馈。

感谢阅读!

创建可靠的数据科学开发环境

原文:https://towardsdatascience.com/creating-a-solid-data-science-development-environment-60df14ce3a34?source=collection_archive---------11-----------------------

如何使用 Conda、Git、DVC 和 JupyterLab 来组织和复制您的开发环境。

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

1.介绍

开始一个数据科学项目通常很有趣,至少在开始的时候是这样。你得到一些数据,开始提问并探索它,制作一些图,尝试一些模型,几分钟后,你有一堆有趣而混乱的见解和更多的数据争论要做。然后你意识到你必须整理你的 Jupyter 笔记本,开始注释和版本化你的代码,并且你需要花一些时间在你的分析中“不那么有趣”的部分。如果您需要与他人分享您的发现,或者将模型投入生产,那么前面会有更多的问题,因为您发现您并不确切知道在您的分析过程中使用了哪些库和版本。

一般来说,我们数据科学家倾向于更关注结果(模型、可视化等)而不是过程本身,这意味着我们没有像软件工程师那样足够重视文档和版本控制。

既然如此,就有必要使用当今可用的适当工具,为数据科学项目的开发建立良好的实践。

**目标:**本文的目标是为数据科学家提供工具和方向,通过使用四个关键工具:Conda、Git、DVC 和 JupyterLab,以可靠和可重复的方式管理他们的项目。本教程结束时,您将能够创建一个存储库,对您的脚本、数据集和模型进行版本化,并在新机器上复制相同的开发环境。

本教程是在运行 Ubuntu 18.04 的 Linux 机器上完成的,但是可以很容易地在 Mac 或 Windows 上使用其他命令行包管理器复制,如家酿 (Mac),或巧克力 (Windows)。

此外,我们将使用 S3 自动气象站来存储我们与 DVC 的数据文件。要遵循教程中的相同步骤,您需要一个安装并配置了 awscliAWS 帐户。

遵循本教程创建的项目资源库可以在我的 GitHub 页面上访问。

2.工具

康达

Conda 是一个环境和包管理器,可以代替 Python 中的 pipenvpip 。它是专注于数据科学的 Python(和 R)发行版 Anaconda 的一部分。您可以选择安装完整版(Anaconda,大约 3GB)或轻型版(Miniconda,大约 400MB)。我推荐使用 Miniconda,因为你将只安装你需要的库。关于更广泛的评论,请查看 Gergely Szerovay 关于 Conda 的文章

饭桶

Git 是一个管理软件开发的版本控制系统。使用 Git,您可以跟踪对存储在存储库文件夹中的代码所做的所有更改。你通常使用云服务如 GitHubBitbucketGitLab 连接到你的本地存储库来管理和存储你的存储库。我们将使用 GitHub 来存储我们的项目资源库,因此您需要一个活动帐户来遵循教程的步骤。

DVC

DVC (数据版本控制)是管理数据集和机器学习模型的 Git 等价物。你通过 DVC 将你的 Git 库链接到云(AWS,Azure,Google Cloud Platform 等)或本地存储来存储大文件,因为 Git 不适合大于 100MB 的文件。关于 DVC 的完整教程,请看看 Dmitry Petrov 的文章。

JupyterLab

JupyterLab 是一个用于 Jupyter 笔记本、代码和数据的交互式开发环境。这是 Jupyter 项目的最新版本,它提供了传统 Jupyter 笔记本的所有功能,界面更加坚固。笔记本电脑在数据科学项目中非常受欢迎,因为它提供了一种动态探索数据的好方法。

代码编辑器和 Git 客户端

代码编辑器是程序员必备的工具,如今有很多开源和付费的选项。因此,请随意选择更适合您需求的代码编辑器。

Git 客户端是为你的代码版本化提供图形用户界面的工具,并且可以成为帮助你管理项目的工具集的有趣补充。

3.安装 Git 和 Conda

为了开始组织我们的开发环境,我们首先需要安装工具。我们将从安装 Git (1)开始,并使用我们的终端配置它(2)。

**# 1) Install Git** sudo apt-get install git**# 2) Configure your Git account** git config --global user.name "Your Name" 
git config --global user.email "yourmail@mail.com"

接下来,我们将安装 Miniconda,方法是下载它的最新版本(3),更改安装文件的权限(4)并运行它(5)。将 Miniconda 文件夹添加到您的系统路径(6)也很重要,只需在终端上键入 conda 即可运行它的命令。

**# 3) Download Miniconda latest release for Linux** wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh**# 4) Change the permission to run the Miniconda bash file** chmod +x Miniconda3-latest-Linux-x86_64.sh**# 5) Run Miniconda installation file** ./Miniconda3-latest-Linux-x86_64.sh**# 6) Export the path to Miniconda installation folder** export PATH=/home/YOURNAME/miniconda3/bin:$PATH

4.配置开发环境

现在我们已经安装了工具,是时候开始设置我们的开发环境了。

创建项目 Git 存储库

首先,我们将使用 GitHub 信息定义变量(8),在 GitHub 上创建一个远程存储库(9),并检查创建是否成功(10)。接下来,我们创建一个本地文件夹来存储我们的项目存储库(11)和自述文件(12)。然后,我们启动我们的本地 Git 存储库(13)并将我们的第一个提交推送到 GitHub (14)。

**# 8) Define the your GitHub information as variables** GitHubName=<YourGitHubName>
GitHubPassword=<YourGitHubPassword>**# 9) Create a new git repository on GitHub 
#    named "DataScience_DevEnv"** curl -u $GitHubName:$GitHubPassword [https://api.github.com/user/repos](https://api.github.com/user/repos) -d '{"name":"DataScience_DevEnv"}'**# 10) Check if your new repository is available on GitHub**
curl "https://api.github.com/users/$GitHubName/repos?per_page=100" | grep -w clone_url | grep -o '[^"]\+://.\+.git'**# 11) Create a folder with the name of your repository** mkdir DataScience_DevEnv
cd DataScience_DevEnv**# 12) Create a README file for your repository** echo "# Data Science development environment repository" >> README.md**# 13) Initiate our local Git repository** git init**# 14) Add, commit and push README.md to GitHub** git add README.md
git commit -m "first commit with README file"
git remote add origin https://github.com/GabrielSGoncalves/DataScience_DevEnv.git
git push -u origin master

我们可以在 GitHub 页面上检查一下,是否在第一次提交时正确地创建了包含 README 文件的存储库。

用康达创造环境

现在我们已经设置好了 Git 存储库,我们将创建我们的 conda 环境(15)。我们只需要定义我们的环境的名称(-n)、python 版本和我们想要安装的库(例如 pandas 和 scikit-learn)。创建完成后,我们只需要输入conda activate和环境名(16)。

**# 15) Create o Conda environment** conda create -n datascience_devenv python=3.7 pandas scikit-learn**# 16) Activate your environment** conda activate datascience_devenv

在我们的环境中安装 JupyterLab、DVC 和其他库

现在,我们正在我们的 conda 环境中工作,我们可以安装 JupyterLab (17)和 DVC (18)。使用 conda 的另一个好处是它也可以用来安装包,就像我们使用 pip 一样。

**# 17) Install JupyterLab with
# conda**
conda install -c conda-forge jupyterlab**# or pip** pip install jupyterlab**# 18) Install DVC with
# conda**
conda install -c conda-forge dvc**# or pip** pip install dvc

我们可以使用命令list (19)列出当前环境中可用的库。我们还可以使用 conda 或 pip (20)为您的环境生成需求文件。

**# 19) List your packages installed
# with conda**
conda list**# with pip** pip list**# 20) Create requirements file
# with conda**
conda list --export > requirements.txt**# with pip**
pip freeze > requirements.txt

DVC 和附属国

要使用 DVC 来存储您的大数据文件,您需要配置一个远程存储文件夹。我们将在我们的教程中使用 AWS S3,但你有其他选项(本地文件夹、Azure Blob 存储、谷歌云存储、安全外壳、Hadoop 分布式文件系统、HTTP 和 HTTPS 协议)。在 DVC 安装过程中,您必须定义将要使用的存储类型,并在括号(21)中指定。在为 DVC 安装了 AWS S3 依赖项之后,我们初始化我们的 DVC 存储库(22)。接下来,我们将在存储库中创建一个名为data的文件夹来存储我们的数据文件,并用 DVC (23)进行版本控制。然后,我们创建一个 S3 存储桶来远程存储我们的数据文件(24)。重要的是要记住,我们已经用 IAM 凭证配置了 awscli,以便使用终端运行 AWS 命令。创建 S3 存储桶后,我们将其定义为我们的 DVC 远程文件夹(25),并检查最后一步是否被接受(26)。现在我们可以下载一个 csv 文件到我们的data文件夹(27),并开始用 DVC (28)对它进行版本控制。

**# 21) Install DVC and its dependecies for connection with S3** pip install dvc[s3]**# 22) Initialize DVC repository** dvc init**# 23) Create folder on repository to store data files** mkdir data**# 24) Create S3 bucket** aws s3 mb s3://dvc-datascience-devenv**# 25) Define the new bucket as remote storage for DVC** dvc remote add -d myremote s3://dvc-datascience-devenv**# 26) List your DVC remote folder** dvc remote list **# 27) Download data file** wget -P data/ [https://dvc-repos-gsg.s3.amazonaws.com/models_pytorch_n_params.csv](https://dvc-repos-gsg.s3.amazonaws.com/models_pytorch_n_params.csv)**# 28) Add data file to DVC** dvc add data/models_pytorch_n_params.csv

每当我们向 dvc 添加文件时,它都会创建一个. DVC 文件,该文件跟踪对原始文件所做的更改,并且可以用 Git 进行版本控制。DVC 还在data文件夹中创建了一个. gitignore,并将数据文件添加到其中,这样 Git 就可以忽略它,我们不需要手动设置它(29)。最后,我们使用 DVC (30)将数据文件推送到我们的远程文件夹(我们创建的 S3 桶)。

**# 29) Start tracking DVC file and .gitignore with Git** git add data/.gitignore data/models_pytorch_n_params.csv.dvc
git commit -m "Start versioning csv file stored with DVC on S3 bucket"
git push**# 30) Push data file to DVC remote storage on S3 bucket** dvc push

DVC 还可以帮助我们建立管道和进行实验,使测试和重现特定的 ETL 步骤变得更加容易。有关 DVC 功能的更多信息,请查看 Gleb Ivashkevich文章

JupyterLab 内核

安装 JupyterLab 后,我们可以在终端上输入jupyter lab来运行它。作为默认设置,JupyterLab 使用我们的基本 Python 安装作为内核,所以如果我们尝试导入您安装在我们新创建的 conda 环境(而不是基本 Python 环境)上的库,我们将得到一个ModuleNotFoundError。为了解决这个问题,我们需要从我们的环境(32)中安装 ipython 内核(31)。通过这样做,我们将拥有一个与我们的 conda 环境相对应的内核,因此每个已安装和新安装的库都将在我们的 JupyterLab 环境中可用。我们还可以检查安装在我们机器上的可用 Jupyter 内核(33)。

**# 31) Install ipython using conda** conda install ipykernel**# 32) Install your kernel based on your working environment**ipython kernel install --user --name=datascience_devenv**# 33) List the kernels you have available** jupyter kernelspec list

导出我们的康达环境

正如在简介中提到的,一个可靠的开发环境的一个重要方面是容易复制它的可能性。一种方法是将关于 conda 环境的信息导出到 YAML 文件(34)。记住,为了做到这一点,你需要先激活环境。

**# 34) To export your current conda environment to YAML** conda env export > datascience_devenv.yaml**# 35) Add the yaml file to our GitHub repository** git add datascience_devenv.yaml
git commit -m 'add environment yaml to repo'
git push

我们项目存储库的结构

到目前为止,我们的项目存储库具有以下结构(36)。

**# 36) Project repository structure** tree.
├── data
│   ├── models_pytorch_n_params.csv
│   └── models_pytorch_n_params.csv.dvc
├── datascience_devenv.yaml
├── README.md
└── requirements.txt

如果我们在命令tree中使用参数-a,我们可以更好地理解构成 Git 和 DVC (37)的配置文件。如前所述,DVC 为我们添加的每个数据文件创建了一个. gitignore,这样 Git 就可以避免跟踪它。

**# 37) Detailed repository structure**
tree -a
.
├── data
│   ├── .gitignore
│   ├── models_pytorch_n_params.csv
│   └── models_pytorch_n_params.csv.dvc
├── datascience_devenv.yaml
├── .dvc
│   ├── cache
│   │   └── 6f
│   │       └── 387350081297a29ecde86ebfdf632c
│   ├── config
│   ├── .gitignore
│   ├── state
│   ├── tmp
│   └── updater
├── .git
│   ├── branches
│   ├── COMMIT_EDITMSG
│   ├── config
│   ├── description
│   ├── HEAD
│   ├── hooks
│   │   ├── applypatch-msg.sample
│   │   ├── commit-msg.sample
│   │   ├── fsmonitor-watchman.sample
│   │   ├── post-update.sample
│   │   ├── pre-applypatch.sample
│   │   ├── pre-commit.sample
│   │   ├── prepare-commit-msg.sample
│   │   ├── pre-push.sample
│   │   ├── pre-rebase.sample
│   │   ├── pre-receive.sample
│   │   └── update.sample
│   ├── index
│   ├── info
│   │   └── exclude
│   ├── logs
│   │   ├── HEAD
│   │   └── refs
│   │       ├── heads
│   │       │   └── master
│   │       └── remotes
│   │           └── origin
│   │               └── master
│   ├── objects
│   │   ├── 10
│   │   │   └── c06accd2ad99b6cde7fc6e3f3cd36e766ce88f
│   │   ├── 19
│   │   │   └── 193f4a173c56c8d174ecc19700204d250e9067
│   │   ├── 4e
│   │   │   └── 0790499d1d09db63aaf1436ddbd91bfa043058
│   │   ├── 52
│   │   │   └── 4cb7d319626c1bcf24ca5184d83dc1df60c307
│   │   ├── 5f
│   │   │   └── 694b1bd973389b9c0cdbf6b6893bbad2c0ebc6
│   │   ├── 61
│   │   │   └── d5f990a1bee976a2f99b202f1dc14e33b43702
│   │   ├── 67
│   │   │   └── 3b06660535a92d0fdd72fe51c70c9ada47f22d
│   │   ├── 70
│   │   │   └── 1490f13b01089d7da8fa830bae3b6909d12875
│   │   ├── 72
│   │   │   └── a0ddbcc242d223cd71ee5a058fc99de2fa53cc
│   │   ├── a3
│   │   │   └── b5ebf7e3b752fa0da823aeb258b96e007b97ef
│   │   ├── af
│   │   │   └── 8017769b22fcba5945e836c3c2d454efa16bd1
│   │   ├── c1
│   │   │   └── 694ff5e7fe6493206eebf59ac31bf493eb7e6b
│   │   ├── d7
│   │   │   └── 39682b1f99f9a684cecdf976c24ddf3266b823
│   │   ├── e4
│   │   │   └── 5eca3c70f6f47e0a12f00b489aabc526c86e8b
│   │   ├── e6
│   │   │   └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
│   │   ├── ee
│   │   │   └── 75f0e66a68873ac2f767c212c56411cd729eb2
│   │   ├── info
│   │   └── pack
│   └── refs
│       ├── heads
│       │   └── master
│       ├── remotes
│       │   └── origin
│       │       └── master
│       └── tags
├── README.md
└── requirements.txt

接下来,我们在你的存储库的根目录下为我们不想跟踪的其他文件创建一个. gitignore (例如 Python 编译的字节码文件。pyc)与 Git (38)。

**# 38) Add .gitignore for script files on our repository** echo "*.pyc" >> .gitignore
git add .gitignore
git commit -m 'Add .gitignore for regular files'
git push

现在我们已经配置好了开发环境,并且准备好了。我们的 JupyterLab 拥有与我们的 conda 环境相匹配的内核,我们的数据文件由 DVC 进行版本控制,我们的 Git 存储库正在跟踪其余的文件。因此,对我们项目所做的任何更改都将被记录下来,并且可以很容易地被复制和跟踪。

5.复制我们的开发环境

在设置我们的 Git 存储库和配置我们的 DVC 存储文件夹之后,我们可以在任何新机器上复制它。简单地克隆存储库(39),从 YAML 文件创建一个 conda 环境(40),激活它(41),为我们的环境创建一个 JupyterLab 内核(42),最后使用 DVC 从 S3 桶拉数据文件(43)。

**# 39) On a new machine, clone the repository** git clone [https://github.com/$GitHubName/DataScience_DevEnv.git](https://github.com/GabrielSGoncalves/DataScience_DevEnv.git)**# 40) Create conda environment** conda env create --file=datascience_devenv.yaml**# 41) Activate environment** conda activate datascience_devenv**# 42) Install the JupyterLab kernel** ipython kernel install --user --name=datascience_devenv**# 43) Pull the data file from the S3 bucket using DVC** dvc pull

因此,我们可以在一台新机器上拥有完全相同的开发环境(包括数据文件和已安装的库),只需要 5 条命令。

7.结论

在本文中,我们展示了为数据科学家创建可靠且可重复的开发环境的关键工具。我们相信,通过在项目开发中使用最佳实践,数据科学是一个可以变得更加成熟的领域,康达、Git、DVC 和 JupyterLab 是这种新方法的关键组成部分

要了解更多关于实践和方法的数据科学开发环境的观点,请看看威尔·科尔森文章

非常感谢你阅读我的文章!

  • 你可以在我的个人资料页面 找到我的其他文章🔬
  • 如果你喜欢并且想成为中级会员,你可以使用我的 推荐链接 来支持我👍

更多资源

[## 为什么需要 Python 环境以及如何使用 Conda-protostar . space 管理它们

我不应该只安装最新的 Python 版本吗?

medium.com](https://medium.com/@gergoszerovay/why-you-need-python-environments-and-how-to-manage-them-with-conda-protostar-space-cf823c510f5d) [## 数据版本控制教程

2019 年 3 月 4 日更新:本教程中的代码示例已经过时。请使用更新的教程…

blog.dataversioncontrol.com](https://blog.dataversioncontrol.com/data-version-control-tutorial-9146715eda46) [## JupyterLab 已经为用户准备好了

我们很自豪地宣布 JupyterLab 的测试版系列,这是 Project…

blog.jupyter.org](https://blog.jupyter.org/jupyterlab-is-ready-for-users-5a6f039b8906) [## 使用 DVC 创建可重复的数据科学工作流

“入门”教程进入 DVC,在你的日常管理工作中建立一个结构和秩序

medium.com](https://medium.com/y-data-stories/creating-reproducible-data-science-workflows-with-dvc-3bf058e9797b) [## 快速添加到。从终端 gitignore

我不久前学了一个技巧来创造我的。gitignore 文件(并添加到它)很快从终端。这里有一个常见的…

raddevon.com](https://raddevon.com/articles/adding-to-gitignore-from-the-terminal/) [## 2019 年排名前 5 的代码编辑器

自从微软的 Visual Studio 代码推出以来,代码编辑器大战真的白热化了。有这么多…

www.software.com](https://www.software.com/review/ranking-the-top-5-code-editors-2019) [## Mac 的 10 个最佳 GUI Git 客户端

Git 是一个版本控制系统,用于跟踪文件变化。通常用于团队环境,尤其是…

www.fossmint.com](https://www.fossmint.com/gui-git-clients-for-mac/) [## 如何避免数据科学编程环境中的常见困难

减少编程环境中的附带问题,这样您就可以专注于重要的数据科学问题。

towardsdatascience.com](/how-to-avoid-common-difficulties-in-your-data-science-programming-environment-1b78af2977df)

用 C++创建 TensorFlow CNN(第 2 部分)

原文:https://towardsdatascience.com/creating-a-tensorflow-cnn-in-c-part-2-eea0de9dcada?source=collection_archive---------6-----------------------

在这篇文章中,我将展示如何使用 TensorFlow C++ API 创建、训练和测试一个卷积神经网络

背景

最近两年,谷歌的 TensorFlow 越来越受欢迎。它是迄今为止最受欢迎的深度学习框架,与 Keras 一起,它是最主要的框架。

现在有了版本 2,TensorFlow 包含了 Keras 构建的内容。

然而,当谈到 C++ API 时,你真的找不到太多关于使用它的信息。大多数代码示例和文档都是用 Python 编写的。正如我在以前的帖子中提到的,我希望允许 C++用户,比如我自己,使用 TensorFlow C++ API,这是一个低级 API,这实际上意味着您必须投入更多的工作来实现它。我的帖子是来帮助你的。

就像我过去写的,我喜欢 C++。我并不反对 Python,但是我希望能够用 C++来构建我的模型,并且拥有工具生态系统来帮助我调试和优化它。

我阅读了文档,并寻找将向我展示如何创建卷积神经网络的代码示例。有几个例子,这里的和这里的启发了我。仅此而已!

我必须对一些 API 进行逆向工程,经历了大量的试验和错误,直到它工作为止。

我希望这篇文章能够帮助那些想开始使用 C++ API 的人。

我在 MacOS 上使用 XCode,但我试图以一种在 Windows 或 Linux 上也能工作的方式构建代码。我没有测试过,所以如果你有任何改变的建议,请告诉我。

设置

我假设您已经安装了 TensorFlow,并且有一个 XCode 项目可以使用它。如果你需要这方面的帮助,请阅读并遵循我的指示这里

正如我在第 1 部分中所写的,我们的目标是实现 Arden Dertat 著名文章系列第 4 部分中的 AlexNet 缩减版。

我还假设你知道卷积网络的术语,如果没有,我建议你阅读 Arden 的文章。

为了获得训练数据,您需要从 Kaggle 下载图像。

这里下载小版本。那里有足够的图像供我们使用。

提取文件,并将它们放在项目文件夹下的数据文件夹中。

它应该是有组织的,所以你有三个子文件夹用于训练,验证和测试。

密码

这篇文章的代码可以在这里找到。

有一个驱动执行的 main.cpp 和两个类文件 CatDogCNN.cpp 和 CatDogCNN.h,它们是网络本身的实现。

mainV1.cpp 是前面的文章代码。

通常,在为 TensorFlow 编写代码时,将图形的创建与其实际运行分开是明智的。这是由于一个简单的事实,即您通常创建一次图表,然后用不同的输入运行多次。即使你不改变图的变量(如权重),也没有必要在每次运行图时重新创建图,除非它非常简单,并且分离的努力是无用的。

完成数据准备

CreateGraphForImage 是一个创建类似于我在第一部分中展示的图形的方法。

它接受是否拆分图像的布尔值。当您只想加载一个图像时调用 false,当您想加载一批图像时调用 true。这是因为堆叠批次时会增加一个额外的尺寸。但是,当你想运行 CNN 只有一个图像,你需要有所有 4 个维度。

请注意,当从拆分更改为堆叠时,您必须重新创建图形。

ReadTensorFromImageFile 负责运行由前面的方法创建的图形。你输入一个文件的完整路径名,得到一个 3 或 4 维张量。

这两种方法的代码几乎与 mainV1.cpp(文章第 1 部分)中的代码相同。

处理文件夹和路径

ReadFileTensors 处理文件夹和文件。它接受一个基本文件夹字符串和一个向量对[子文件夹,标签值]。

如果你从 Kaggle 下载了图片,你可能会注意到在 train 下有两个子文件夹 cats 和 dogs。用一个数字标记它们中的每一个,并将这些对作为输入进行传递。

返回的是一对向量,每一对向量都是一个张量(图像的)和一个标签。

这里有一种说法:

base_folder = "/Users/bennyfriedman/Code/TF2example/TF2example/data/cats_and_dogs_small/train";vector<pair<Tensor, float>> all_files_tensors;model.ReadFileTensors(base_folder, {make_pair("cats", 0), make_pair("dogs", 1)}, all_files_tensors);

我们需要打开一个目录,读取其中的文件,然后一个一个地检查。

要连接两个路径字符串,请使用 io::JoinPath(包括 tensorflow/core/lib/io/path.h)

string folder_name = io::JoinPath(base_folder_name, “cats”);

要与文件系统交互,请使用“Env”。这是一个实用程序类(没有真正的文档记录),它为您提供了类似于 C++17 std::filesystem 的便利。

Env* penv = Env::Default();TF_RETURN_IF_ERROR(penv->IsDirectory(folder_name));vector<string> file_names;TF_RETURN_IF_ERROR(penv->GetChildren(folder_name, &file_names));for(string file: file_names){…}

ReadFileTensors 还会打乱矢量中的图像,这样我们就可以在训练时输入不同的图像(你不会想先喂所有的猫,然后再喂所有的狗)。

创建批处理

ReadBatches 封装了所有的逻辑 main 需求。你给它一个基本文件夹,一对子文件夹和标签的向量,以及一个批处理的大小。作为回报,你得到两个张量向量,一个用于图像,一个用于标签。每个张量都是按照你传递的大小一批图像/标签。

首先,它将文件夹及其子文件夹的内容读入一个向量。接下来,它会计算如何拆分批次。然后,它断开张量和标签对以创建两个输入向量,其中张量向量中的每个第 n 个元素匹配标签向量中的第 n 个元素。

为了堆叠这两个向量,我们当场创建一个小图,并在每次迭代中运行它。

结果被添加到输出向量中。

注意堆栈操作是如何需要得到一个要堆栈的图像张量的输入列表的。最简单的方法是创建一个 vector(或另一个容器)并用它实例化 InputList。

vector<Input> one_batch_image;one_batch_image.push_back(Input(tensor));//Add more tensorsInputList one_batch_inputs(one_batch_image);Scope root = Scope::NewRootScope();auto stacked_images = Stack(root, one_batch_inputs);ClientSession session(root);vector<Tensor> out_tensors;TF_CHECK_OK(session.Run({}, {stacked_images}, &out_tensors));//use out_tensors[0]

如上所述,张量以三维张量的形式出现,而创建的批次是四维的。

CNN 图表

现在有趣的部分来了。

我们想创建一个类似于 AlexNet 的模型。

投入

首先,我们想要定义图形的输入:我们已经知道我们需要输入一批图像:

input_batch_var = Placeholder(t_root.WithOpName("input"), DT_FLOAT);

占位符类似于函数参数。您指定了它的名称和元素类型,但此时它的形状未知。

从占位符获取结果的变量是 CatDogCNN 类类型输出的成员。

首先,我们在图中使用它作为第一层的输入,稍后我们将需要在 ClientSession 运行命令中指定它及其值(批处理张量)。

接下来有几个占位符,我稍后会解释。

与通常的 Python Keras 网络不同,C++层是操作的组合(Google 称之为低级 API)。为了知道每个操作属于哪一层,我们为每一层创建一个子范围。

Scope scope_conv1 = t_root.NewSubScope("Conv1_layer");

将使用此范围对象创建操作。

盘旋

第一层是卷积层,所以我们需要定义 4 个参数:过滤器高度和宽度以及进出通道。此外,我们还有激活函数(Relu)和后面的 MaxPool 操作。

我们有 4 个 Conv 层,所以我创建了一个函数来创建一个包含这些操作的通用层:

Input CatDogCNN::AddConvLayer(string idx, Scope scope, int in_channels, int out_channels, int filter_side, Input input){TensorShape sp({filter_side, filter_side, in_channels, out_channels});m_vars["W"+idx] = Variable(scope.WithOpName("W"), sp, DT_FLOAT);m_shapes["W"+idx] = sp;m_assigns["W"+idx+"_assign"] = Assign(scope.WithOpName("W_assign"), m_vars["W"+idx], XavierInit(scope, in_channels, out_channels, filter_side));sp = {out_channels};m_vars["B"+idx] = Variable(scope.WithOpName("B"), sp, DT_FLOAT);m_shapes["B"+idx] = sp;m_assigns["B"+idx+"_assign"] = Assign(scope.WithOpName("B_assign"), m_vars["B"+idx], Input::Initializer(0.f, sp));auto conv = Conv2D(scope.WithOpName("Conv"), input, m_vars["W"+idx], {1, 1, 1, 1}, "SAME");auto bias = BiasAdd(scope.WithOpName("Bias"), conv, m_vars["B"+idx]);auto relu = Relu(scope.WithOpName("Relu"), bias);return MaxPool(scope.WithOpName("Pool"), relu, {1, 2, 2, 1}, {1, 2, 2, 1}, "SAME");}

让我们回顾一下代码:

AddConvLayer 获取一个索引字符串(用于区分层)、子范围、输入和输出通道、过滤器大小(我们假设高度和宽度相同)和输入 Input。

通道中的第一层是图像的颜色数(3 种颜色),输入是图像张量。

Conv2D 操作需要一个张量变量来保存不同的滤波器(第一层中的 32 个)。当网络在训练时,这个变量将在每一步内改变。

出于这个原因,有一个成员映射 m_vars 保存这些变量以及偏差。此外,我们需要存储这些变量的形状(m_shapes map),我们还需要用值初始化这些变量(类似于 Python 变量初始化器)——这些操作存储在 m_assigns 中。Xavier 初始化现在很流行,所以我为 Conv 和 Dense 实现了 XavierInit。

在 Conv2D 之后,调用 BiasAdd 来添加偏置(初始化为 0)。

接下来是对 Relu 的调用,然后是对 MaxPool 的调用。

请注意,对于 Python Keras 版本中的 MaxPool,您只需指定窗口大小及其两个维度(例如 2,2)。这里我们需要提供两个四维的形状(窗口大小和跨度)。要将每层中的图像大小减半,必须在窗口大小和步幅中输入 1,2,2,1。

还要注意,在 CreateGraphForCNN 函数中,我跟踪了进出通道的大小,因为它们必须在层之间匹配。

此外,由于没有办法从运算中得到张量的形状(只有在运行时才知道),我们必须在每一步中计算图像的大小。

平的

扁平化意味着我们将只有两个维度,批处理和平面数据。

为此,我们需要重塑张量。因为我不想硬编码批处理大小(我不知道它是否会被批处理),所以我必须知道平面数据的大小。这就是为什么我在这些层中做这些计算,直到我们得到 flat_len。

auto flat = Reshape(flatten, pool4, {-1, flat_len});

在 Reshape 中,您可以在其中一个维度中传递-1,因此它将被自动计算。整形不会添加数据,只会改变数据的组织方式。

拒绝传统社会的人

这一层负责丢弃随机的神经元,这样网络就不会过度适应。

我们创建一个具有相同形状的新张量,并根据丢弃率随机填充 0 或 1,然后用它乘以我们的数据。

然而,当你想要验证或预测时,你不想放弃神经元。

实现这一点的逻辑方法是在图中放置一个条件。有一些操作以一种非常麻烦的方式支持它(切换和合并),但是在 C++中它们不支持反向传播,所以我们不能使用它们。

相反,我向网络 drop_rate 和 skip_drop 引入了另外两个输入参数,例如,如果您分别传递 0.4 和 0,就会得到 40%的丢弃。如果你通过 1 和 1,就不会有水滴。

Scope dropout = t_root.NewSubScope("Dropout_layer");auto rand = RandomUniform(dropout, Shape(dropout, flat), DT_FLOAT);//binary = floor(rand + (1 - drop_rate) + skip_drop)auto binary = Floor(dropout, Add(dropout, rand, Add(dropout, Sub(dropout, 1.f, drop_rate_var), skip_drop_var)));auto after_drop = Multiply(dropout.WithOpName("dropout"), Div(dropout, flat, drop_rate_var), binary)

丢弃时,我们通过除以丢弃率来增加剩余神经元的值。

注意,当 skip_drop 为 1 时,floor 会将“二进制”变为全 1。

完全连接(密集)

接下来是三个致密层。

密集是基于乘以权重并加上偏差。我们将变量保存在地图中,对形状和赋值也是如此。

我们还用 Xavier init 初始化权重,用 0 初始化偏差。

注意,在最后一个密集层中,我们需要跳过 Relu 激活(为此我们有细菌培养)。

二元分类

最后的致密层将尺寸减小到 1(超过批量尺寸)。这就是所谓的逻辑。

为了使逻辑值为 0(代表猫)或 1(代表狗),我们调用 Sigmoid 并返回这个结果。

这就是打开 TensorBoard 时网络的样子(详见第 1 部分):

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

The CNN before we add optimization

我打开了其中一个子范围,这样你就可以看到第四个卷积层中的低级操作。

优化图(反向传播)

这封装在 CreateOptimizationGraph 函数中。它不是一个单独的图,而是同一个图中的附加节点(同一个 t_root 主作用域)。

我们从标签的占位符开始。如果图像张量是 x,这是 y。

接下来我们计算损失。这是一个分类问题,所以我们应该使用交叉熵,但是我不知道如何使用 SoftmaxCrossEntropyWithLogits 操作,也找不到任何有意义的信息,所以我必须使用常规的均方 diff 方法。

input_labels_var = Placeholder(t_root.WithOpName("inputL"), DT_FLOAT);Scope scope_loss = t_root.NewSubScope("Loss_scope");out_loss_var = Mean(scope_loss.WithOpName("Loss"), SquaredDifference(scope_loss, out_classification, input_labels_var), {0});TF_CHECK_OK(scope_loss.status());

接下来,我收集所有的权重和偏差,并将它们和损失一起发送给神奇的 API 调用 AddSymbolicGradients。这个神奇的功能不是手术。它获取一个图(通过其相关范围),添加所有相关的反向传播操作,并返回一个梯度向量,其大小与权重和偏差的数量相同。

然后,我们对权重和偏差中的每个变量使用 ApplyAdam 及其各自的梯度。

auto adam = ApplyAdam(t_root, i.second, m_var, v_var, 0.f, 0.f, learning_rate, 0.9f, 0.999f, 0.00000001f, {grad_outputs[index]});v_out_grads.push_back(adam.operation);

出于某种原因,m_var 和 v_var 必须是变量(它们不能是常数)。

传递给 ApplyAdam 的最重要的变量是学习率。我怎么强调这个价值的重要性都不为过。我从 0.001(来自 python 版本)开始,无法停止对为什么损失不收敛的困惑。最终我意识到优化函数在两个峰值之间跳得太长了。仅仅过了几天,当我把它改成 0.0001 时,它就开始工作了。您可以尝试不同的值,看看它在您的情况下是如何工作的。

你也可以改变 ApplyAdam 得到的其他值,尽管我不确定这会有什么影响。

现在,张量板图像看起来“有点”不同:

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

The CNN with optimization

初始化

我已经提到了用赋值操作初始化所有变量的需要。

我们将所有 m_assigns 元素(也是我们为 ApplyAdam 添加的元素)收集到一个 vector 中,并在创建的会话中运行它们。

vector<Output> ops_to_run;for(pair<string, Output> i: m_assigns)ops_to_run.push_back(i.second);t_session = unique_ptr<ClientSession>(new ClientSession(t_root));TF_CHECK_OK(t_session->Run(ops_to_run, nullptr));

保留摘要文件编写器代码周围的注释,仅当您想要为 TensorBoard 可视化生成新图形时才使用它。

培养

让我们回到 main:我们构造了 CatDogCNN 类的一个实例,创建了图形,加载了图像并运行了初始化。

现在是主历元/步长循环的时间。

一个历元是贯穿所有训练数据的一次运行。一个步骤是一批图像的一次运行。

我们定义历元的数量,遍历它们,然后遍历批。

在每次迭代中,我们调用 TrainCNN,我们调用 ValidateCNN,我们做一些计算来检查网络的性能。

TrainCNN 得到一批图像,一批标签(对应这些图像)并返回结果和损失。

该函数首先运行批处理:

vector<Tensor> out_tensors;//Inputs: batch of images, labels, drop rate and do not skip drop.//Extract: Loss and result. Run also: Apply Adam commandsTF_CHECK_OK(t_session->Run({{input_batch_var, image_batch}, {input_labels_var, label_batch}, {drop_rate_var, 0.5f}, {skip_drop_var, 0.f}}, {out_loss_var, out_classification}, v_out_grads, &out_tensors));

然后它计算精确度。注意使用张量方法来提取数据:out_tensors[0]。标量 ()(0)得到一个标量和 out_tensors[1]。matrix ()得到一个矩阵(几乎就像向量的向量)。

确认

验证是重要的,因为事实上我们运行的网络图像,网络从未见过。如果在每一个时期我们都有更好的表现,我们就不会过度适应。

vector<Tensor> out_tensors;//Inputs: batch of images, drop rate 1 and skip drop.TF_CHECK_OK(t_session->Run({{input_batch_var, image_batch}, {drop_rate_var, 1.f}, {skip_drop_var, 1.f}}, {out_classification}, &out_tensors));

请注意不同之处:我们跳过了漏失(见上面的解释),我们不评估损失,我们不运行梯度。

输出控制台如下所示:

Epoch 1/20:....................................................................................................Validation:..................................................Time: 74 seconds Loss: 0.243231 Results accuracy: 0.561 Validation accuracy: 0.642Epoch 2/20:....................................................................................................Validation:..................................................Time: 72 seconds Loss: 0.203312 Results accuracy: 0.6875 Validation accuracy: 0.692***Epoch 20/20:....................................................................................................Validation:..................................................Time: 73 seconds Loss: 0.041021 Results accuracy: 0.956 Validation accuracy: 0.742

损失在持续下降,结果准确率将达到 95%,验证准确率将达到 75%。

测试

我们来测试一下训练好的网络。

为此,我们需要重新创建负载图像图,这样它就不会散开。

然后我们调用 ReadFileTensors 来加载 test 子文件夹中的图像。最后,我们运行其中几个的循环,以查看网络的性能。

预测类似于验证,尽管它运行单个图像。

输出控制台如下所示:

Test number: 1 predicted: 0 actual is: 0Test number: 2 predicted: 1 actual is: 1***Test number: 20 predicted: 0 actual is: 0total successes: 16 out of 20

80%的准确率。有一些改进的空间,但是我们比 Arden 在他的网络上得到更好的结果。

摘要

我们看到了如何只用 C++和 TensorFlow API 实现、训练和测试 CNN。

我们看到了如何准备数据(用于训练、验证和测试的图像)以及如何对它们进行批处理,以便我们可以将这些批处理提供给 TensorFlow 低级 API。

我们实现了梯度和其他优化方法。

我们取得了良好的结果,但还有更多工作要做。

接下来我打算做一些改进。

正如你在控制台输出中看到的,我正在我的本地 MacBook Pro CPU 上运行,它需要很长时间才能运行…

我打算实现冻结模型功能和 GPU 的使用,以加快速度。

此外,我会看看我是否可以做一些图像增强,为培训创建更多的数据。

用 C++创建 TensorFlow CNN(第 3 部分:冻结模型并扩充它)

原文:https://towardsdatascience.com/creating-a-tensorflow-cnn-in-c-part-3-freezing-the-model-and-augmenting-it-59a07c7c4ec6?source=collection_archive---------17-----------------------

这是关于如何在 TensorFlow C++ API 上创建 CNN 的第三篇也是最后一篇文章。在本文中,我将讨论实现模型冻结、保存和加载,以及数据扩充

背景

前两篇文章(这里这里)讨论了如何实现 CNN,以及训练、验证和测试它的方法。现在,您需要优化模型以获得更好的结果,并将其部署到“生产”环境中,以便您可以使用优化后的模型进行一些预测。

就像我之前的文章一样,我会详细说明 TensorFlow 在其 C++ API 中不太成熟的地方,以及如何克服这些困难。

这篇文章的代码位于这里

冻结模型

虽然冷冻模型是最后一步,但我先讨论一下。

你建立你的模型,然后你训练和优化它。一旦你对结果满意,你想要保存模型权重和偏差;把它们从变量变成常量。

这样做时,将模型保存到磁盘将保存常数,稍后当您加载模型时,它就可以运行并可以对新图像进行分类。

新旧 API

TensorFlow C++ API 包括两组 API:

–旧的(基于会话类别);和

–新的(基于 ClientSession 类)

我选择了新的。然而,我注意到存储库中的一些实用程序类还没有迁移到新的 API 中。TensorFlow 存储库附带的冻结/保存实用程序类使用较旧的 API。在/tensor flow/cc/tools/freeze _ saved _ model . cc 文件和头文件中实现。

要使用 API,应该包含/tensor flow/cc/tools/freeze _ saved _ model . h 文件,并调用 FreezeSavedModel API 调用来冻结模型。

问题是,您需要传递会话对象(间接),以便内部实现可以进入活动模型以获取变量的当前值。但是,您没有会话,您有一个 ClientSession,并且该 ClientSession 不公开其具有该会话对象的内部数据。

那你是做什么的?有几个策略:

1.使用旧的 API 重新实现 CNN,这意味着不仅要使用会话,还要使用字符串作为变量名,而不是句柄(模型创建代码中的“auto”声明)。

2.不需要发送会话对象:在调用 API 之前收集变量值,并传递这些值和其他参数。

我选择了第二个选项。我想保持代码的整洁,以便有一天,当有人将这些代码移植到新的 API 时,您可以简单地调用它而无需准备。

如何冷冻

为了冻结模型,我们需要找到所有变量及其各自的值。

变量和偏差的列表已经可用,我们在创建优化图时使用它作为向量(这些是我们计算梯度的变量)。

因此,我们所需要的是一个映射这些变量和当前值的 map 对象:

vector<Tensor> out_tensors;//Extract: current weights and biases current valuesTF_CHECK_OK(t_session->Run(v_weights_biases , &out_tensors));unordered_map<string, Tensor> variable_to_value_map;int idx = 0;for(Output o: v_weights_biases){ variable_to_value_map[o.node()->name()] = out_tensors[idx]; idx++;}

接下来,我们从示波器中获得图形(就像我们使用 TensorBoard 可视化它时所做的那样)。

接下来,我们创建一个包。bundle 是一种特殊的结构,它结合了会话和元图。将不会使用该会话(如前所述),因为我们已经有了所需的地图。

元图是一个非常复杂的类,用于各种用例,但我们只需要它用于 SignatureDef,这类似于函数签名(输入和输出)。

我们用数据填充它,并添加图表本身:

SavedModelBundle saved_model_bundle;SignatureDef signature_def; (*signature_def.mutable_inputs()) [input_batch_var.name()].set_name(input_batch_var.name());(*signature_def.mutable_outputs())[out_classification.name()].set_name(out_classification.name());MetaGraphDef* meta_graph_def = &saved_model_bundle.meta_graph_def;(*meta_graph_def->mutable_signature_def())["signature_def"] = signature_def;*meta_graph_def->mutable_graph_def() = graph_def;SessionOptions session_options;saved_model_bundle.session.reset(NewSession(session_options));//even though we will not use it

接下来,我们准备几个输出参数,并调用修改后的 API:

TF_CHECK_OK(FreezeSavedModel(saved_model_bundle, variable_to_value_map, &frozen_graph_def, &inputs, &outputs));

原始 API 中不存在参数 variable_to_value_map,我添加了它,这样我们就可以跳过会话对象的使用。您还可以查看 freeze_saved_model.cc 文件,查看该文件中与该更改相匹配的其他更改。

最后,我们调用 WriteBinaryProto 将冻结的输出图保存到文件中。

加载冻结的模型

加载模型又涉及到旧的 API(意味着会话,而不是客户端会话):

std::unique_ptr<GraphDef> graph_def;SessionOptions options;f_session.reset(NewSession(options));graph_def.reset(new GraphDef());TF_CHECK_OK(ReadBinaryProto(Env::Default(), file_name, graph_def.get()));return f_session->Create(*graph_def.get());

我们首先创建一个会话,然后创建一个 GraphDef,最后读取 protobuf 文件以在会话中创建图形。

运行“生产”模型预测

由于我们使用旧的 API 来创建会话,我们还需要一个特殊的方法来运行预测模型,使用我们前面创建的相同会话:

Status CatDogCNN::PredictFromFrozen(Tensor& image, int& result){ vector<Tensor> out_tensors; Tensor t(DT_FLOAT, TensorShape({1})); t.scalar<float>()(0) = 1.f; //Inputs: image, drop rate 1 and skip drop. TF_CHECK_OK(f_session->Run({{input_name, image}, {drop_rate_name, t}, {skip_drop_name, t}}, {out_name}, {}, &out_tensors)); auto mat = out_tensors[0].matrix<float>(); result = (mat(0, 0) > 0.5f)? 1 : 0; return Status::OK();}

该方法获取单个张量图像并返回类(0 或 1:猫或狗)。

请注意我们是如何使用变量的名称而不是句柄的。除此之外,代码的其余部分不言自明。

在 main 中使用类方法

在 main 中,我创建了一个 if 分支,它选择我们是要使用一个冻结的模型,还是训练这个模型,然后保存它:

string proto_name = "/Users/bennyfriedman/Code/TF2example/TF2example/frozen/cnn.pb";bool use_frozen = false;if(!use_frozen){ … //this is where the model training code is … …//and at the end s = model.FreezeSave(proto_name); TF_CHECK_OK(s);}else //use the frozen model{ s = model.LoadSavedModel(proto_name); TF_CHECK_OK(s); //testing the model s = model.CreateGraphForImage(false);//rebuild the image loading model without unstacking TF_CHECK_OK(s); string base_folder = "/Users/bennyfriedman/Code/TF2example/TF2example/data/cats_and_dogs_small/test"; vector<pair<Tensor, float>> all_files_tensors; s = model.ReadFileTensors(base_folder, {make_pair("cats", 0), make_pair("dogs", 1)}, all_files_tensors); TF_CHECK_OK(s); //loop through the images and call PredictFromFrozen to get the prediction.}

阅读存储库中的代码,查看统计数据是如何计算的。
不要忘记你需要包含修改后的头文件,并将修改后的 cc 文件添加到你的项目中,这样它将作为产品的一部分进行编译。

衡量模型性能

我们已经了解了如何计算损失、准确性和验证指标。

现在的目标是看到一个图表,显示指标随时间的变化(运行时期的进度)。这将告诉我们是否过度适应或者干脆停止学习。

在 Python 中使用 Keras API 时,内置了指标的可视化。

在 C++ API 中,您有两个选项:

1.使用模型中的日志操作将指标写入磁盘

2.使用我们在上一篇文章中用来可视化图表的实用程序类摘要文件编写器,将度量保存到模型外部的磁盘上

在这两种情况下,你都使用张量板来想象。

你用哪一个?看情况。一方面,由于 API 是对 ScalarSummary 操作的一个简单调用,因此在模型内部更容易一些。另一方面,使用 utility 类为您提供了在模型之外计算指标的灵活性。

我使用了第二个选项:

SummaryWriterInterface *w1;TF_CHECK_OK(CreateSummaryFileWriter(1, 0, "/Users/bennyfriedman/Code/TF2example/TF2example/graphs/loss/", "loss", Env::Default(), &w1));Tensor t(DT_FLOAT, TensorShape({1}));t.scalar<float>()(0) = loss_sum/num_batches;TF_CHECK_OK(w1->WriteScalar(epoch, t, "Original"));

首先创建一个摘要文件编写器对象,然后创建一个标量形状的张量,并使用访问器 scalar <>()填充数据。

然后使用 WriteScalar 将其写入磁盘。

可视化将通过 TensorBoard 完成(见下文)。

组合图表

在大多数情况下,您希望看到指标之间的相互关系如何随着时间的推移而变化。您希望一个接一个地查看图表,以便比较准确性和有效性。

这有点令人困惑,所以你需要注意:

1.对于每个指标,您需要一个单独的文件写入器,将数据保存在单独的文件夹中(文件名后缀参数不太重要)

2.对于图形的每个版本,都需要一个惟一的标记(传递给 WriteScalar 的字符串)。带有此标签的数据将出现在同一个组合图表中。

3.连续运行该程序会在磁盘上添加不同的文件,但 TensorBoard 会将新数据添加到相同的图表中,除非您更改文件夹或删除旧文件。

所以策略应该是:

1.使用 3 个文件写入器(分别用于损失、准确性和验证指标),在“/graphs”文件夹下有 3 个子文件夹。

2.为模型的每个版本使用不同的标签。例如,我将进行一些数据扩充,这将增加通过模型运行的图像数量,因此我将使用“原始”进行正常运行,并使用“扩充”进行具有更多数据的更好模型。例如,这是“原始”图表:

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

Legend

如您所见,损失几乎为 0 (0.0187),但准确性和验证之间的差距越来越大,验证也没有从大约第 8 纪元开始改善。这意味着我们过度适应。

解决这个问题的唯一方法是获取更多数据来训练模型。运行更多的纪元不能改进模型。

数据扩充

获取更多数据的一种方法是获取更多图像。然而,就像在我们模型的 Python Keras 版本中,我们可以增加图像来“创建”更多的数据来训练模型。

在 Python 中这非常容易,您只需指定想要创建什么样的数据扩充,一切都在幕后为您完成。

对于 C++ API 来说,这有点复杂,但也是可能的。

我们需要创建一个新的图表,将图像批次和扩大他们。

我实现了 4 种增加数据的方法:

1.从右向左翻转(上下没有意义)

2.顺时针和逆时针旋转到一定程度

3.将图像放大或缩小到某个比例

4.水平或垂直向上移动到某个系数

每一种方式都有一个随机因素,所以我们将在不同的时期创造不同的变化。您在 main 中指定的是机会或限制:

float flip_ratio = 0.5f;float rotation_max_angles = 40.f;float scale_shift_factor = 0.2f;TF_CHECK_OK(model.CreateAugmentGraph(batch_size, image_side, flip_ratio, rotation_max_angles, scale_shift_factor));

方法 CreateAugmentGraph 创建图形,该图形随后在每个批次上运行,以创建每个时期中图像的不同变化。

翻转图像

让我们先来看看我们是如何翻转图像的:

auto rand1 = RandomUniform(a_root, {batch_size}, DT_FLOAT);auto flips_vector = Floor(a_root, Add(a_root, rand1, flip_chances));auto flips_tensor = Cast(a_root, Reshape(a_root, flips_vector, {batch_size, 1, 1, 1}), DT_FLOAT);auto reverses = Reverse(a_root, input_images, {2}); // right to leftauto tensors_reversed = Mul(a_root, reverses, flips_tensor);auto tensors_normal = Mul(a_root, input_images, Sub(a_root, 1.f, flips_tensor));fliped_images = Add(a_root, tensors_reversed, tensors_normal);

首先,我们创建一个批量大小的随机向量。然后,我们加上翻转机会因素和地板。这给我们批中的每个图像一个 1 或 0(翻转或不翻转)。

现在,我们对矢量进行整形,使其与图像批次的形状相匹配,并且增加了 3 个维度。

翻转是通过在从右向左的维度 2 上调用反向操作来完成的。

现在我们有两个批次,一个翻转,一个正常。我们在每个图像上调用与随机向量及其 1 的补数相乘,以将我们不需要的图像置零,并将它们相加在一起,以获得增加的批次。

做投影变换

射影变换是将任意维的张量变换成新的张量,同时通过投影改变其“视觉”性质的数学方法。

这正是我们需要移动或缩放图像以及旋转它们。

幸运的是,作为 TensorFlow repository 附带的“Contrib”代码的一部分,已经有人实现了完成这种转换的操作。

代码位于/tensorflow/contrib/image 文件夹中。

不幸的是,贡献者没有完全实现 C++ API,而只是实现了 Python API。我们可以解决。

自定义操作

张量流是可扩展的。有关于如何添加新操作的指南,所以你可以在你的模型中使用它们。有些操作是由贡献者开发的,但没有成为核心张量流的一部分。它们在 Git 存储库中,但是不会作为基本包的一部分进行编译。

为了能够使用它们,我们需要单独编译它们(是的,使用 Bazel),将它们的二进制文件添加到项目中,并包含一个头文件。

控制代码生成和编译的是 Bazel 使用的构建文件。

当查看/tensor flow/contrib/image/BUILD 文件时,您可以看到一个 tf_gen_op_wrapper_py 部分,它负责在操作之上生成 python 包装器,但是没有 tf_gen_op_wrapper_cc 部分,它将为 C++类创建包装器。此外,cc_library 部分缺失,它将创建我们想要链接的库。

使用我的存储库中的构建文件覆盖原始构建文件,然后运行:

bazel build -c opt --verbose_failures //tensorflow/contrib/image:image_cc

构建完成后,您应该有了可以链接的库文件/tensor flow/bazel-bin/tensor flow/contrib/image/libimage _ cc . so。请注意,如果您将它复制到本地工作区(在 lib 子文件夹中的项目文件夹下),您可能需要使用 install_name_tool,以便能够消除 dyld 错误。

此构建还生成了文件/tensor flow/bazel-gen files/tensor flow/contrib/image/image _ ops . cc 和头文件。

这些是从 contrib 源文件生成的包装类。我们将需要包括标题,并添加到我们的项目抄送文件。

将这两个文件与同一文件夹中生成的内部 ops 文件一起复制到项目文件夹下的/include/tensor flow/contrib/image 子文件夹中。

现在我们准备使用自定义操作(ImageProjectiveTransform)

构建转换数据结构

与其他转换操作一样,ImageProjectiveTransform 接受一个名为 transforms 的参数。

这是一个具有 8 个浮点数的张量,称为 a0、a1、a2、b0、b1、b2、c0、c1,如果我们处理一批图像,还有另一个维度的批大小(每个图像有一组 8 个数字)。

为了创建这个张量,在我们的例子中是 20x8,我们需要创建一个 InputList(由一个向量组成)并沿着轴 1 连接它。我们不能使用 Stack,因为这将导致 8x20 的形状被转换操作拒绝。

要水平或垂直移动图像(出于某种原因也称为 translate),我们需要在 a2 和 b2 中放置像素数(正数和负数)。

要缩放图像(放大或缩小),我们需要在 a0 和 b1 中放一个因子(大于或小于 1)。

因为我们还需要加入一个随机因子,所以计算是这样进行的:

auto rand1 = RandomUniform(a_root, {batch_size, 1}, DT_FLOAT);auto rand2 = RandomUniform(a_root, {batch_size, 1}, DT_FLOAT);auto rand3 = RandomUniform(a_root, {batch_size, 1}, DT_FLOAT);auto rand4 = RandomUniform(a_root, {batch_size, 1}, DT_FLOAT);auto rand_shift1 = Sub(a_root, Mul(a_root, rand1, scale_shift_factor*2*image_side), scale_shift_factor*image_side);auto rand_shift2 = Sub(a_root, Mul(a_root, rand2, scale_shift_factor*2*image_side), scale_shift_factor*image_side);auto rand_scale1 = Add(a_root, Mul(a_root, rand3, scale_shift_factor*2), 1-scale_shift_factor);auto rand_scale2 = Add(a_root, Mul(a_root, rand4, scale_shift_factor*2), 1-scale_shift_factor);Input::Initializer zeros(0.f, {batch_size, 1});auto transforms = Concat(a_root, MakeTransforms(batch_size, rand_scale1, zeros, rand_shift1, zeros, rand_scale2, rand_shift2), Input::Initializer(1, {}));shifted_images = ImageProjectiveTransform(a_root, input_images, transforms, "BILINEAR");

首先,我们创建 4 个随机向量(以一批的长度)。因为这些是 0 到 1 之间的数字,我们需要做一些数学运算来得到我们需要的数字。请注意,我们传递到计算中的因子与 Keras API 匹配:当您传递 0.2 时,每个轴上的缩放在 0.8 到 1.2 之间,并且向右或向左以及向上或向下移动高达 20%。

我们将不使用的转换成员置零,并通过连接创建转换。MakeTransform 只是构建 InputList。

最后,我们用图像批处理调用 ImageProjectiveTransform。

旋转图像

旋转稍微复杂一点,但是它使用相同的转换结构和操作:

auto rand6 = RandomUniform(a_root, {batch_size}, DT_FLOAT);auto angles = Mul(a_root, Sub(a_root, rand6, 0.5f), max_angles*2);auto rand_angles = Div(a_root, Mul(a_root, angles, (float)M_PI), 180.f);auto sins = Sin(a_root, rand_angles);auto m_sins = Mul(a_root, sins, -1.f);auto coss = Cos(a_root, rand_angles);float img_side_1 = image_side - 1;auto x_offset = Div(a_root, Sub(a_root, img_side_1, Sub(a_root, Mul(a_root, coss, img_side_1), Mul(a_root, sins, img_side_1))), 2.f);auto y_offset = Div(a_root, Sub(a_root, img_side_1, Add(a_root, Mul(a_root, sins, img_side_1), Mul(a_root, coss, img_side_1))), 2.f);auto transforms = Concat(a_root, MakeTransforms(batch_size, coss, m_sins, x_offset, sins, coss, y_offset), Input::Initializer(1, {}));aug_tensor_output = ImageProjectiveTransform(a_root, fliped_images, transforms, "BILINEAR");

我们首先创建一个随机向量,转换成正负度数,然后转换成弧度。

接下来我们计算正弦,负正弦,余弦等。

我们再次创建一个转换并调用转换操作。我通过查看它的 python 版本(tf.contrib.image.rotate)实现了这个方法。

查看创建包含所有 4 个转换的图形的完整代码。有一些优化,以防你没有做其中的一两个。

对输入图像批次运行模型非常简单。

增强图像的验证

如果图像以这样一种方式被转换,以至于它们不再能被人眼识别为猫或狗,那会怎样?我们怎么检查呢?

我已经编写了一个实用方法 WriteBatchToImageFiles,它可以帮助您将增加的张量转储回您可以查看的图像文件。

在培训的主循环中(我们不会在验证或测试中增加图像),您可以看到以下代码:

if(augment_data){ //TF_CHECK_OK(model.WriteBatchToImageFiles(image_batches[b], "/Users/bennyfriedman/Code/TF2example/TF2example/data/cats_and_dogs_small", "source")); Tensor augmented; TF_CHECK_OK(model.RandomAugmentBatch(image_batches[b], augmented)); //TF_CHECK_OK(model.WriteBatchToImageFiles(augmented, "/Users/bennyfriedman/Code/TF2example/TF2example/data/cats_and_dogs_small", "augmented")); s = model.TrainCNN(augmented, label_batches[b], results, loss);}else s = model.TrainCNN(image_batches[b], label_batches[b], results, loss);

如果我们使用扩充的数据(我们应该这样做),我们首先扩充批处理,然后用扩充的张量调用 TrainCNN 方法。

取消对增强前后的 WriteBatchToImageFiles 调用的注释,以查看图像如何随机变化。

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

模型性能

让我们看看模型性能是否有所提高:

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

Legend

您可以清楚地看到,即使经过 100 个时期,损失仍在下降,准确性和验证数字之间的差距也不那么明显了。

它仍然不完美,我们在测试中远远超过 80%,但为了改善这一点,我们可能应该运行更多的时代,并获得新鲜的图像。

摘要

在这一系列文章(概述第一部分第二部分)中,我展示了如何使用 XCode 进行 TensorFlow C++开发,如何使用 C++ API 创建 CNN,可视化它,为它准备数据并以几种方式进行优化。我试图遵循 Arden Dertat 在他的第四篇文章中的步骤,实现他在 Keras Python 中实现的 AlexNet。

我希望这将作为其他人的教程,这些人在使用 C++作为主要语言的同时开始了他们在 TensorFlow 中的道路。

让我知道你的想法。欢迎建议!

创建语音识别计算器 Android 应用程序

原文:https://towardsdatascience.com/creating-a-voice-recognition-calculator-android-app-869a515bf6d1?source=collection_archive---------3-----------------------

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

Photo by Andres Urena on Unsplash

自动语音识别是当今机器学习中最著名的话题之一,每天都有许多新人投入他们的时间和专业知识。在这篇文章中,我们将构建一个简单的端到端语音激活计算器应用程序,它将语音作为输入,并将语音作为输出返回。输入包括整数和基本的数学运算符,而输出是用户说出的运算结果。

这个教程不是什么

  • 关于语音识别如何工作的理论教程。
  • 关于基于云的语音 API 的教程,如 Amazon Polly 或 Google Cloud Speech。
  • Java 语法和原理教程。
  • 关于 Java-XML 通信如何在 Android 上工作的教程。
  • 安卓材质设计教程。

先决条件

  • 基本的 Android 开发知识。
  • Android Studio(您可以使用 Eclipse 等其他 ide,但我将在本教程中介绍 Android Studio)。
  • 期望:基本的 API 知识(不需要认证)。

教程大纲

  • 创建 Android 项目
  • 创建语音转文本功能
  • 处理输入值(整数和运算符)
  • 计算结果
  • 创建文本到语音转换功能

创建 Android 项目

让我们首先打开 Android Studio 并开始一个新项目。如果你已经打开了一个项目,进入文件- >关闭项目,然后按照下图操作:

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

选择空活动然后点击下一步:

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

选择您想要的项目细节,或者保持所有设置为默认,然后单击完成:

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

给 Android Studio 几分钟时间来完成项目的构建和索引。如果您达到以下要求,您就万事俱备了:

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

创建用户界面

现在让我们创建 activity_main.xml 布局文件。我尽可能保持简约,因为这里的目标不是设计,而是功能:

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

其中 A、B、+ 只是可点击的占位符,而 C= 是不可点击的占位符。**走!**按钮触发计算并更新结果 C 。用户将点击它们中的每一个,并以语音形式输入它们自己的值。当然,设计和布局取决于作为开发者的你。这只是为了演示。

将布局链接到 Java 代码

现在让我们导航回 outMainActivity.java,并为我们拥有的每个可点击的文本视图设置一个 OnClickListener 。随后,我们的 MainActivity 类将如下所示:

创建语音识别功能

现在开始本教程有趣的部分。让我们从请求 RECORD_AUDIO 许可开始这一部分。这是通过在 AndroidManifest.xml 中的 <应用> 标签前添加以下行来实现的:

<uses-permission android:name="android.permission.RECORD_AUDIO" />

Android 有一个内置的语音识别 API,其形式为 Intent 。我们处理这个意图就像处理任何其他 Android 意图一样。现在回到我们的。我们拥有的 onClick 方法将负责记录语音并将其转录成文本,以便我们可以将其作为数学运算来处理。因此,我们的意图代码需要驻留在我们所有的 onClick 方法中。

让我们首先为识别器创建一个 Intent 实例:

Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);

让我们添加一些额外的数据(用于内务管理和语言选择),并以请求代码开始意向活动:

intent.putExtra(RecognizerIntent.*EXTRA_LANGUAGE_MODEL*, RecognizerIntent.*LANGUAGE_MODEL_FREE_FORM*);intent.putExtra(RecognizerIntent.*EXTRA_LANGUAGE*, Locale.*ENGLISH*);startActivityForResult(intent, 10);

以上两段代码需要复制粘贴到我们所有的三个 onClick 方法中,但是具有不同的请求代码。现在,MainActivity.java类中的那些方法应该是这样的:

到目前为止,我们的代码能够检测可点击文本视图上的点击,并在点击时开始识别语音。然而,它没有返回转录文本的方法。基本上,我们开始了一个意图,但没有收到预期的结果。因此,我们需要定义一个 onActivityResult 方法(在MainActivity.java)来处理那些转录结果:

我们的数据变量是一个意图实例,我们可以通过数组列表从中获取结果。这可以通过简单地调用:

data.getStringArrayListExtra(RecognizerIntent.*EXTRA_RESULTS*)

请记住,这个结果在我们拥有的所有 3 个识别器意图之间是共享的。现在我们需要一种方法来从这个列表中找到并获得实际的整数——在请求代码为 10 或 20 的情况下。为此,我们将创建一个循环遍历由 intent 返回的结果的方法(即 ArrayList )和另一个将说出的数字从字符串转换为 int 的方法:

这里有一个旁注,为了保持代码的整洁,在 switch 语句中不需要任何 break 语句。这是因为一旦其中一种情况评估为真,返回将负责停止开关的执行。

参考上面的代码,当请求代码为 10 或 20 时,我们可以从我们的 onActivityResult() 中调用 getNumberFromResult() 。当用户点击 firstNumberTextViewsecondNumberTextView 时,这将负责将说出的数字转录成相应的整数。

现在让我们为操作符转录创建类似的方法:

最后,让我们将调用 getOperatorFromResult()和 getNumberFromResult()的部分添加到我们的 onActivityResult()中:

其中 FIRST_NUMBERSECOND_NUMBER运算符是类中的全局变量,定义如下:

private int FIRST_NUMBER;
private int SECOND_NUMBER;
private char OPERATOR;

这些将是我们要计算的实际值。让我们定义一个执行这些计算的方法:

这个方法将从 **GO 中调用!**按钮的 onClick 方法。我们还没有处理过这个按钮。所以让我们添加以下代码:

goButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        RESULT = performCalculations();
        resultTextView.setText(String.*valueOf*(RESULT));
    }
});

其中结果是在类中定义的全局整数变量。

就是这样!现在,应用程序应该可以成功运行了。

创建语音合成功能

语音合成是将文本转换成语音的过程。我们将在应用程序中使用它,以便让 API 大声读出 performCalculations() 方法返回的结果。对于这个相当短的任务,我们将使用 Android 内置的 TextToSpeech 功能。

首先,该类需要实现 TextToSpeech 监听器。简单地说,转到 MainActivity 类签名,将它从:

public class MainActivity extends AppCompatActivity { ...

收件人:

public class MainActivity extends AppCompatActivity implements TextToSpeech.OnInitListener { ...

这表明需要在类中实现以下方法(从类签名中删除红色下划线):

@Override
public void onInit(int i) { }

我们将暂时保留没有实际实现的方法,因为应用程序的当前用例不需要实现。这是必需的,因为我们正在实现一个接口(遵循 OOP 原则)。

现在让我们声明一个对类 TextToSpeech 的全局引用:

TextToSpeech textToSpeech;

然后在我们的 onCreate() 方法中初始化它:

textToSpeech = new TextToSpeech(this, this);

我们现在已经准备好使用这个对象了。导航到 **GO!**按钮的 onClickListener 并在我们使用 setText() 的部分后添加下面一行:

textToSpeech.speak(
        String.*valueOf*(RESULT), TextToSpeech.*QUEUE_ADD*, null
);

就是这样!只要用户点击开始!按钮,结果将由 API 以语音的形式发出。

最后看一下代码

我已经把 Android 项目推给了 GitHub。可以通过这个链接查看:https://github.com/AnasAlmasri/VoiceRecognitionCalculator

然而,这里有一个 AndroidManifest.xml 文件:

下面是 activity_main.xml 文件:

最后一节,MainActivity.java课:

我们构建了一个 Android 应用程序,它将两个整数(0-9)和一个语音形式的运算符作为输入,识别这些输入,执行数学运算,然后将结果作为语音返回,同时在屏幕上显示出来。这只是初学者掌握语音识别应用程序如何工作的一次尝试。接下来将支持多位数、负数、浮点数,并一次性合并所有三种输入,以便用户可以说出“一加五”,应用程序可以分割感兴趣的单词,并进行必要的中间步骤以获得结果。

为 GameBoy 创建 AI 第 1 部分:编写控制器代码

原文:https://towardsdatascience.com/creating-ai-for-gameboy-part-1-coding-a-controller-5eb782f54ede?source=collection_archive---------17-----------------------

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

2003 年发布的《火徽,炽剑》是一款非常成功的战略游戏,其角色出现在《超级粉碎兄弟》中,该系列的第 15 部将于 2019 年初上映。游戏的玩法是选择角色(又名单位),决定将他们移动到哪里,然后从一组选项中决定攻击、使用物品等等。因为我在十几岁和十几岁时非常喜欢这个游戏,所以我想为这个 Gameboy Advance classic 制作一个 AI。这篇文章将是我在从头开始构建一个玩游戏的人工智能的过程中,将这一努力分成更多小块的第一篇。这里展示的代码可以在 github.com/aaronfrederick 找到,它的最新版本是为那些想要创建类似项目的人准备的。

在这个项目开始的时候,我把任务分成了 4 大块:

  1. 在 python 中创建控制器
  2. 从游戏中获取数据
  3. 自动化播放过程以连续生成数据
  4. 实现一种算法来优化游戏

为了玩火徽和创造一个 AI,我下载了 VisualBoy Advance 模拟器和一个火徽 ROM 到我的电脑上。该模拟器与 ROM 一起,允许人们在一台计算机上从不同的控制台玩游戏。另一个例子是海豚模拟器,它允许在电脑上玩 Wii 和 Gamecube 游戏。

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

Pyautogui will not work unless the box is checked!

现在我们已经有了模拟器设置,我们需要能够以编程方式访问它。在准备过程中,我打开了模拟器,并把它放在电脑屏幕的左上角,不受其他应用程序的干扰。有一个一致的位置是至关重要的,在这里我们将点击选择模拟器,但这个一致的位置更重要的是从屏幕上获取信息时,到了屏幕上的数据模拟器。pyautogui 库非常适合用 python 控制你的电脑,但是如果你像我一样在 Mac 上,你需要让终端访问你的电脑,如左图所示。

我们需要做的第一件事是选择模拟器,让它运行,这可以通过将鼠标移动到应用程序上并单击来完成。

import pyautoguipyautogui.moveTo(7,54,0.2)
pyautogui.click()

The code above with a 4 second time to reach destination for clarity

传递给 moveTo 函数的参数是 x 像素位置y 像素位置到达该位置所用的时间。因为左上角是(0,0),所以我们将鼠标移动到靠近左上角,但不是一直移动,这样我们就可以将光标放在应用程序上。

为了在我们选择模拟器后按下按钮,我们需要一个再次使用 pyautogui 的函数。下面的 press_key 函数为将来简化了这一点,这样我们只需键入 press_key(键,次数)就可以轻松控制游戏。

def press_key(key, n_times = 1):
    for _ in range(n_times):
        pyautogui.keyDown(key)
        pyautogui.keyUp(key)

现在我们可以按按钮了,我们需要把它应用到游戏中。我将在这个例子中展示的三个函数是 select_next_unit、move_unit 和 wait。这些函数将为这个人工智能做大部分的工作,所以把它们做好是很重要的。

如下所示,select_next_unit 只需按“q”键,然后按“'”键。这相当于在 Gameboy Advance 上按 L,然后按 A,这是将光标移动到下一个可用单位并按 A 的控件。这将弹出一个界面,允许我们选择将单位移动到哪里。

def select_next_unit():
    press_key('q')
    press_key("'")

在我的 github 上实现的 move_unit 函数比这里显示的稍微复杂一些——它允许随机移动——但是功能基本相同。这个功能背后的基本思想是,我们要输入一组坐标来移动,然后按下按键来实现这些坐标。这些不等式是针对随机移动世代的,因此,例如,如果左的值大于右,我们只按左。

def move_unit(left=0,right=0,up=0,down=0):
    ret_list = [left,right,up,down]
    if left>right:
        press_key('a', left-right)
    elif right>left:
        press_key('d', right-left)
    if up>down:
        press_key('w', up-down)
    elif down>up:
        press_key('s', down-up)
    press_key("'")
    time.sleep(0.2)
    return ret_list

使用 W,A,S,D 分别作为上,左,下,上,我们可以按下按键的次数,对应于我们传入的坐标。一旦我们移动我们的单位,我们会看到一个菜单,允许我们使用一个项目或等待。使用等待功能,我们将保持我们的单位(Lyn)在我们移动她的地方,然后结束我们的回合。

def wait():
    time.sleep(0.2)
    press_key('w')
    time.sleep(0.2)
    press_key("'")

The code to the right playing Fire Emblem!

把这三个功能放在一起,我们就可以在火徽第一关第一个回合了!左边的 GIF 显示了下面的代码在工作。

#Select the Emulator
pyautogui.moveTo(7,54,0.2)
pyautogui.click()
time.sleep(0.3)#Play the Game
select_next_unit()
move_unit(left=2, up=2)
wait()

在第 2 部分,我将讨论如何从这个游戏中获取信息,以便我们可以为机器学习创建数据集和损失函数。这将涉及一些图像处理技术,并创建一个图像分类器来智能地从屏幕上提取数据。

为 GameBoy 创建人工智能第 2 部分:从屏幕上收集数据

原文:https://towardsdatascience.com/creating-ai-for-gameboy-part-2-collecting-data-from-the-screen-ccd7381a1a33?source=collection_archive---------21-----------------------

欢迎来到为 GameBoy 创建人工智能的第 2 部分!如果您错过了第 1 部分:编写控制器代码,单击此处继续学习。在这一期,我将讲述如何通过各种图像处理和分类技术智能地从游戏中获取信息。这对任何游戏人工智能都很重要,但对它在火徽中的应用至关重要,因为游戏是完全通过决策来进行的。从游戏中获取信息如此重要的原因是,我们将训练一个机器学习算法来玩,由于 ML 算法需要数据集来学习,我们需要能够生成数据。考虑到这一点,我们需要知道我们需要哪些信息来建立我们的特征和目标。特征数据应该表示为游戏状态和采取的行动,而目标应该是我们玩游戏的好坏的衡量标准。和以前一样,当前状态的代码可以在我的 github 上找到。

这个任务需要几个库,所以我认为有必要简单回顾一下哪些库对哪些任务有用:

  • Python 图像库(PIL)-用于截屏/提供图像
  • cv2 —用于处理图像和转换颜色
  • pytesserac——用于从图像中获取文本
  • Keras 用于训练模型以从图像中获取数字
  • Scikitlearn —用于训练模型从图像中获取数字

图像处理任务的基本工作流程遵循以下顺序:

  1. 用 PIL 拍一个大截图(在我的旧笔记本电脑上,这个功能非常慢,大约 0.6 秒到 0.75 秒)
  2. 根据需要用下面列出的函数对图像进行子集化(如果 PIL 不那么慢,多张截图就好了
  3. 使用 cv2 填充功能,根据需要转换颜色和填充
  4. 将图像分类模型应用于经处理的图像以获得数据

下面的函数是我用 PIL 拍摄的图像的子集。虽然使用列表中的切片方法可能会更快,但与 PIL ImageGrab 函数拍摄初始图片所需的 0.6 秒以上的时间相比,这种差异可以忽略不计。

def subscreen(x0,y0,x1,y1, screen):
    sub_img = []
    for i in range(y0,y1,1):
        row=[]
        for j in range(x0,x1,1):
            row.append(screen[i][j])
        sub_img.append(np.array(row))
    sub_img = np.array(sub_img)
    return sub_img

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

We need to know the name of our character: Lyn

对于这两张图片,我们最感兴趣的是文本数据——我们将要使用的角色的名字以及我们可以使用的选项。为此,pytesseract 是一个无价的工具。Google 的 tesseract 是一个开源的光学字符识别(OCR)工具,在 python 中以 pytesseract 的名字发布。这个库的使用非常简单,我们可以从左上角的蓝色窗格中获得文本“Lyn ”,只需一行代码(上图)和第二张图(下图)中显示的选项:

#for black text
text = pytesseract.image_to_string(image)#for white text
text = pytesseract.image_to_string(inverted_grayscale(image))

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

pytesseract can tell us what options we have

处理函数inverted _ gray可能会因情况而异——在这种情况下,它会将图片变成灰色,并反转所有像素值,将白色像素变成黑色,反之亦然。Cv2 有很好的颜色变化功能,可以替代我使用的处理功能,这是我为这个特定情况编写的,但是真正神奇的是 pytesseract 使我们能够做到的。Pytesseract 允许我们放弃创建带标签的图像数据集、训练分类模型,或者重新发明轮子,这是一个巨大的时间节省,因为我们将看到…

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

The ‘Status’ screen shows the current game state

下面两张图片分别代表游戏状态和角色状态。充满了数字,从这些屏幕上可以获得很多信息。理想情况下,我们将只使用上面的 pytesseract 代码,但遗憾的是这些字母给软件包带来了一些麻烦。在为一个数据集标记了多个图片并在 Keras 中训练了一些卷积神经网络模型(CNN)之后,我决定返回到基本的 sci-kit 学习包,看看慢速训练的 CNN 是否真的有必要检测哪些图像对应于哪些数字。在没有任何超参数操作的情况下,第一次尝试逻辑回归模型时,我能够达到 99.5%的准确性——大概这 0.5%来自一个错误标记的图像。坦白说,我错标了 500 张图片中的 2 张……显示了这个模型有多好。用于数据提取的代码的实际用法如下,为清楚起见进行了编辑:

#using logistic regression model 'block_reader'
#padder function adds black to outline if image is too smallstats['str'] = block_reader \
                 .predict(padder(str_img).reshape(***to fit model***))[0]

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

更简单的模型如此成功的原因是由于小的图像大小,不同类别之间的明显差异,以及每个类别之间的一致性。更进一步地说,逻辑回归模型可以处理 30x24 的图像,而不是 400x600 的图像,因为每次观察的特征(像素)数量相对较少。类别的差异允许不同的像素在分类中具有不同的重要性。每个类中的一致性是导致开箱即用模型成功的另一个因素,因为模型的测试数据与之前在训练数据中看到的数据非常相似(如果不完全相似的话)(这就是为什么我们可以使用训练准确性作为评估指标)。

现在,有了谷歌和我们自己训练的模型,我们就可以从火徽中获取信息了。我们所需要做的就是拍摄图片,处理它,并应用我们的模型获得数据,并将其纳入游戏过程!

为 GameBoy 创造人工智能第 3 部分:自动化(糟糕的)游戏

原文:https://towardsdatascience.com/creating-ai-for-gameboy-part-3-automating-awful-gameplay-b60fe7504e4e?source=collection_archive---------23-----------------------

通过随机移动生成数据!

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

欢迎来到为 GameBoy 创造 AI 的第 3 部分!如果你错过了前两部分中的任何一部分,你可以在这里找到第一部分和第二部分。一如既往,我的 GitHub 是这个项目的最新版本。在这个项目的第三部分,我们将自动随机输入游戏,因此标题。这个游戏必须在玩的时候为我们生成数据,这样我们就可以在未来训练我们的机器学习模型。

我们训练算法所需的数据有三种:状态、动作和度量。状态数据指的是游戏的状态——我们的角色在哪里,谁在我们周围,我们角色的健康水平如何。同样,行动指的是我们在给定的状态下采取的行动。状态和行动是相互关联的数据点,这些数据点将产生一个结果,我们需要对这个结果进行衡量,以了解行动有多好。在这样的尝试中有一些算法可以使用(Q 学习,遗传算法),其中度量数据有不同的名称,如奖励、适合度或 Q(s,a)。我还不想把这个项目放在一个特定的算法上,所以我将把我们对成功的测量称为我们的度量。

为了完全自动化游戏,我们需要两个功能:一个是玩游戏,一个是在出错或运行结束时重启模拟器。游戏功能将更加复杂,因为它将实现我们在第 1 部分中构建的控制器,同时使用我们在第 2 部分中编写的图像处理生成数据。每次我们单位做出一个举动或决定,我们都会捕捉并存储数据。总的来说,每个回合的游戏功能的工作流程是:

  • 收集游戏状态——记录敌人和玩家单位的数量,回合计数(状态)
  • 游戏状态也给了我们想要最小化的值的信息,也就是剩余的敌人单位+回合数(度量)
  • 找到我们的角色—记录名字(动作)
  • 移动我们的角色——记录我们将她移动到的坐标(动作
  • 选择一个选项——记录我们是否攻击、使用物品或等待(动作)

下面我将展示 gen_data 函数的一个截断版本,以展示上面概述的思想的整体感觉和逻辑。在火徽的上下文中,为了清晰易读,我省略了一些按键和睡眠线。

相比之下,重启功能要简单得多。本质上,它按下重启模拟器所需的按键,并通过菜单导航,再次开始游戏。下面我将这两个函数结合起来生成数据,这些数据将在本系列的第 4 部分中使用,以便从这些数据中学习如何玩得更好。通过一个指定要完成的运行次数的 for 循环,我们可以通宵玩游戏来为这个挑战的学习方面生成数据。

#s, a, m are lists containing states, actions and metrics valuesreset_to_prologue()
states, actions, metrics = gen_data_prologue()
s.extend(states)
a.extend(actions)
m.extend(metrics)

对于任何参与类似项目的人来说,火之徽不同于许多其他游戏,因为控制随着每个命令而变化,与其他游戏相比,项目的这一部分变得复杂。在像马里奥,索尼克和许多其他游戏中,“右”总是把角色带到右边,“A”几乎总是跳,“开始”会调出菜单。在火焰徽章中,根据屏幕的不同,“向下”可以有多种含义。它可以将光标下移一格,将一个单位下移一格,将选择从一个选项移动到另一个选项,如果正在检查一个单位,甚至可以改变单位。由于这种复杂性,我编写了一些函数,在很多游戏都不需要这些函数的时候,很明智地解决了这个问题。如果你试图为一个不同的游戏编写一个人工智能,很可能你可以省略这些功能,使用屏幕图像作为状态,使用控制器输入作为动作。

为 GameBoy 创造人工智能第 4 部分:Q-学习和变化

原文:https://towardsdatascience.com/creating-ai-for-gameboy-part-4-q-learning-and-variations-2a0d35dd0b1c?source=collection_archive---------8-----------------------

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

When the boss dies, he asks us a fitting question

我们期待已久的部分

大家好,欢迎来到为 Gameboy 构建人工智能的第 4 部分!这是真正的奇迹发生的地方——我们已经建立了我们的工具,现在是时候启动它们了。快速回顾一下我们到目前为止的旅程:首先,我们构建了一个控制器,这样我们就可以使用 Python 脚本运行游戏;第二,我们使用了开源和自制的图像处理工具相结合的方式,从游戏中提取信息;第三,我们自动化了这个过程,这样游戏就可以在随机输入的情况下自动运行。现在,我们将实现一个 Q-learning 的变体来击败这个游戏的第一关。如果你想更深入地研究强化学习,我强烈推荐这两篇帖子(链接 1链接 2 ),它们在分解算法和实现方面做得非常好。

The final result — playing using the Q-Table’s most rewarded actions

什么是 Q-Learning,我们如何在这里应用它?

为了解决火徽的问题,我们将使用一个 Q 表。该表的行代表游戏的可能状态,列代表动作。在这种特殊的情况下,桌子有 150 种状态(关卡就像一个有 150 个方块的棋盘——10 行 15 列)和 180 种可能的行动。这个数量的动作来自我们的角色 Lyn,她能够走最大曼哈顿距离五个单位(这导致了 60 种不同的移动选项),并且当她到达时有三个选项(攻击、物品和等待)。填充每个状态、动作对的值是在给定状态下与采取该动作相关联的估计回报。这种回报通常被表示为函数 Q(s,a),这就是为什么这个过程被称为 Q-learning。你可能对“深度 Q-Learning”(DQN)这个词很熟悉,并想知道我们为什么不实现它——游戏运行太慢,无法为深度学习生成令人满意的数据量,我们无法在当前的模拟器设置中加快它的速度。下面是一个在任意问题上填写 Q 表的例子。

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

A small example of a Q-table getting filled in — Source

要填满这个 150x180 的桌子,我们必须多次玩这个游戏。我们从根据我们的状态采取随机行动开始,并随着我们的进步记录奖励。在这种情况下,奖励是我们在上一轮击败的敌人数量。如果我们在一个任意的状态 s,采取一个任意的行动 a,我们的奖励 Q(s,a)要么是 0(如果该回合所有敌人都幸存)要么是 1(每杀死一个敌人)。当我们随机玩游戏时,我们目前正在探索动作/状态空间,并跟踪回报,以便当我们以后想要利用这些知识时,我们可以对什么工作得好有所了解。我们必须首先探索,以便找到行之有效的路线,就像我们最终必须使用我们获得的信息来改进我们的游戏性一样。在实践中,这是由一个称为 exploration_rate(或 epsilon)的变量表示的,它随着我们的进展而衰减。每次我们尝试关卡时,我们会降低我们的探索速度(通过指数衰减)并将其与一个随机数进行比较,看看我们是否会探索或利用这一尝试。在学习过程的最后,我们将基本上在没有任何随机产生的决定的情况下玩游戏,仅仅依靠我们通过玩存储在 Q 表中的游戏而积累的知识。

在实施过程中,我们有 3 个主要部分:

  1. 初始化数据结构
  2. 初始化超参数
  3. 实现算法

设置代码

这里的第一个代码块设置了我们需要的数据结构,即 Q 表和字典,它们将状态和动作映射到所述表中的索引。

第二个代码块初始化我们将在这个实验中使用的超参数。当我们更新 Q 表时,学习率和 gamma 是影响我们奖励得分的乘数。

第三个代码块显示了实际的 Q 学习。我们通过重置游戏和将我们的状态设置到开始位置来开始每一轮,然后开始轮流。对于每个回合,我们使用一个随机数生成器来决定是探索还是剥削,并评估该数字是否高于我们当前的探索率。如果是这样,我们正在利用 Q 表中现有的知识。一旦我们做出了决定,我们要么随机(探索)产生一个行动,要么从 Q 表中选择最高回报的行动(利用)。然后,我们使用我编写的 take_turn*函数轮流执行那个动作,该函数返回信息:谁移动了,他们移动到哪里,他们采取了什么动作,以及奖励。布尔自变量表明我们是在探索还是在利用 Q 表。因为有一些空间是禁区,例如不可通行的地形,敌人的单位,我包括了一个检查,看看移动是否无效。然后,我们使用先前的状态和移动到的位置来设置新的状态,并用结果更新我们的 Q 表。在每次训练结束时,我们降低我们的探索率,以便我们可以更恰当地使用我们的信息。

*关于 take_turn 函数的注意事项:在 OpenAI 的 gym 中,他们有一个非常类似的方法——env . step(action)在游戏中进行一步后返回类似的信息。我想模仿他们的逻辑,使我的实现尽可能适用于其他问题。如果你想了解我的 take_turn 功能,你可以在我的 GitHub 上找到它。

情节变得复杂了——替代方法

如当前描述的,这种方法是非深度 Q 学习的典型方式。对于这个问题,是不行的。这个程序不仅玩游戏太慢,不能为 DQN 生成数据,甚至不能用最优值填充 Q 表。亲爱的读者,不要烦恼,因为到目前为止我还没有浪费你的时间;Q-learning 有不同的变体,我们将讨论如何解决这个问题。

改变奖励

到目前为止,奖励仅仅是为了杀死敌人。这并不能激励我们的英雄走向最终的静态 boss。她随机移动到老板旁边的方格攻击他,然后停留在那里第二回合结束他的概率微乎其微。这并不是说,通过足够多的训练和加速模拟,这是不可能做到的,但坦率地说,我有更好的事情要做,我的时间和电脑。为了解决这个问题,我在奖励中加入了一个基于欧几里德距离的东西,放在老板旁边的一个正方形里。不是每杀死一个敌人增加 1,我们现在增加 1/敌人+%从起点到终点的进度。

明智地初始化 Q 表

改变奖励帮助很大,但我们仍然努力完成平衡杀死我们的第一个敌人和利用我们已知的目标广场治愈我们自己所需的探索。我们没有找到通向老板的最佳路径,而是在第一条随机选择的路径上花费了更多的时间,因为我们知道那是“可行的”。在我看来,这是一个不需要成为问题的问题——在我们开始玩之前,我们已经知道基于距离的奖励条款将是什么。考虑到这一点,我循环了 Q 表中的每一个可能的状态,并奖励了每一个动作在这一关结束时我们离老板有多近。这样,我们就可以以最快的路线到达关卡的终点,并且只需要“学习”如何对付路上的敌人。这也允许我们增加探索率衰减,因为有更多的先验知识和更少的“学习”要做,我们需要执行更少的随机运动。

使用 Q-learning 和合理的奖励函数,我能够在最佳数量的回合中击败第一级火徽。第二层向我们介绍盟友,使我们的问题更加复杂:我们如何改变我们的方法来使用盟友?我将在下一期《为 GameBoy 创造 AI》中回答这个多智能体强化学习问题!

使用 IBM PowerAI Vision 快速创建人工智能驱动的计算机视觉应用程序。

原文:https://towardsdatascience.com/creating-ai-powered-computer-vision-applications-in-no-time-with-ibm-powerai-vision-33ebe49f4f2e?source=collection_archive---------12-----------------------

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

自从机器学习和人工智能术语浮现在我们周围人的脑海中以来,这已经是一个漫长的旅程,它激励我们许多人探索更新的维度,以应对自动化的需求,在一些可能的特定场景中,甚至可以挑战人类水平的准确性。在机器学习维度中,NLP、计算机视觉、语音识别等等,要解决计算机视觉问题非常非常复杂,如果你在更高的层次上思考它,你会意识到我们正在提供像素矩阵形式的图像,并试图分类、识别或分割对象。正如我们已经看到的,训练机器学习是一个迭代过程,这个过程是调整不同参数、超参数、重塑、添加或删除一个或多个层、连接或跳过连接一个或多个残差块等等的永无止境的迭代。

为了简化和统一为实现不同算法而在数据集上采取的大量操作,有一个简单的解决方案,IBM PowerAI Vision,它通过将复杂的代码任务包装在一个简单易学和易于使用的 web 界面中,使即使是机器学习新手也能够为测试和生产环境创建、训练、调整、维护和部署他们的模型。它在其核心中利用 IBM PowerAI 平台,并使用 IBM Power Systems 支持的加速计算平台,同时尊重 PowerAI GPU/CPU 通信和并行性。可以导入使用 Tensorflow、Keras 或许多其他框架训练的深度学习模型,以将其作为 PowerAI Vision 平台内的自定义模型。这些定制模型可以用作基础模型,它们的功能和性能指标可以通过在 PowerAI 愿景中进行调整来增强。通过使用自动标记功能,我们作为数据科学家可以有效地减少我们的工作量。随着部署模型的跨平台可用性,开发过程的开销变得更少。

聊够了,让我们把手弄脏:

要开始使用 PowerAI Vision,您需要做的只是通过 IBM PowerAI Vision 的官方网站请求试用,https://developer . IBM . com/linuxonpower/deep-learning-power ai/Vision/access-registration-form/并填写您的详细信息以及您的企业或机构电子邮件地址。您将在两个工作日内收到他们的回复,并提供您登录 PowerAI Vision 门户网站的凭据。只需按照电子邮件中提供的链接,输入提供的凭据,您就可以开始了。

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

PowerAI Vision Login Page

当我们完成登录后,我们将会看到应用程序本身。它分为四个不同的选项卡,数据集、模型、自定义模型和部署模型。“数据集”选项卡允许我们上传不同的图像和视频,作为输入要素提供。“模型”选项卡提供了在指定不同的超参数以帮助算法以更高的准确性更快地收敛之后,在选定的数据集上根据不同的预定义算法训练机器学习模型的选项。自定义模型类型允许您将之前在 PowerAI 视觉平台上训练的预训练视觉模型作为. zip 文件导入,并允许您使用该模型在更大程度上节省工作和时间。这个平台可以被视为迁移学习的真实例子。为了能够对对象进行分类或分割图像,我们需要创建一个非常深的神经网络模型,该模型具有大量的层和数百万个参数、超参数、训练、开发和测试矩阵,以便向前看。更重要的是,我们需要巨大的计算能力和如此多的时间来训练网络。但这就是迁移学习的用武之地,我们不是从零开始,我们从预先训练的模型开始,特别是在计算机视觉模型中训练。这就是像 IBM PowerAI vision 这样的平台发挥作用的地方,它不仅可以简化开销,还可以有效地为用户提供计算能力和数据洞察力。deployed models 选项卡允许您浏览您已经为生产环境部署的模型,可能通过不同的应用程序编程接口向您的客户展示对代表性状态转移范例的尊重。比如你可以从你开发的应用程序内部使用下面的 API 调用来识别或分类或分割结果。在为输入图像提供预测的同时,PowerAI Vision 还提供了嵌入在输入图像顶部的热图,以表示图像矩阵中的位置,这些位置对于算法正确分类或识别图像中的对象起着重要作用。在下面的 API 调用中,我将 true 传递给 containHeatMap 标准,以获取该热图作为响应。

https://{your_deployment_url_or_ip.com}/api/dlapis/{deployed_model_id}?imageUrl={url_of_the_image_object}?containHeatMap="true"?containRle="{true/false}"?containPolygon="{true/false}"?confthre={specify_floating_point_threshold_for_prediction}?cisnum={to_determine_possible_matches}

让我们从创建一个示例计算机视觉模型开始。让我们想象一下,我们将为无人机创建一个计算机视觉算法,以正确识别在洪水、地震或其他自然灾害中受损的房屋。嗯,我在这个主题上通过使用传统的深度神经网络(不使用 PowerAI Vision)实现了一个全尺寸模型,目前它在我的实验室中处于测试阶段。在本次演示中,我将向大家展示,使用 IBM PowerAI 视觉平台开始这样一项复杂的任务是多么容易,我最终在 PowerAI 视觉平台中获得了相当高的分类精度,无需做太多调整,在精度方面一点也不差,同时记住算法训练的迭代过程,调整不同的超参数,将不同的算法集成在一起等等。

所以,让我们来收集一些数据。因此,在这次演示中,我们将从互联网上下载一些被毁房屋的图像,在这次演示中,我只下载了 40 张可能包含受损房屋的航拍图像,还下载了一些其他完好房屋的航拍图像。这是我的数据集,总共只包含 52 张图片。嗯,这绝对不会是我们的任何生产阶段部署。我们将使用 PowerAI 视觉平台提供的工具来增强更多的图像。

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

Creating or importing Datasets to the PowerAI Vision portal.

选择创建新数据集或使用导入功能导入数据集。zip 文件开始使用。等待图像和视频文件完成上传,在上传过程完成后,您将看到一个类似如下的界面:

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

Dataset Page: After Creating /Importing a dataset augmentation Process in progress.

有了这么少量的文件,我们的算法不会收敛。所以,我们能做的,就是从我们上传的 52 张图片中增加更多的图片。为此,我们只需单击“增强数据”,它将提供诸如裁剪、旋转、噪声等各种选项来增强原始图像中的新图像,并以您提供的名称创建新数据集。增强过程可能需要很长时间,具体取决于输入图像或视频的大小。通过扩充,我们能够从仅仅 52 个输入图像中生成近 1300 个文件。

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

After Data Augmentation

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

Initiation of Training of the dataset

在增强过程完成之后,我们有 312 个图像被分类为完好房屋,其中 3082 个对象被标记为完好房屋,1040 个图像被分类为受损房屋,其中 1850 个对象被标记为受损房屋。直到这个数据集不意味着生产就绪的计算机视觉项目,只是为了演示的目的。因此,我们将进一步从我们生成的图像中训练出一种算法。因此,让我们单击“Train Model ”(训练模型),选择“object recognition model ”(对象识别模型),我们将选择要在更快的 RCNN 算法上训练的模型,以获得更高的准确性。我们单击高级选项来进一步微调超参数,例如迭代或时期的最大数量、梯度下降的动量、训练和测试/验证集的比率、学习率、正则化的权重衰减以减少过度拟合的机会。现在我们开始训练,PowerAI 视觉平台将在提供的 GPU 上开始训练。默认情况下,PowerAI Vision 上的每个试用帐户都可以访问 12 个 GPU,从而大大减少了训练模型的时间。随着培训的进行,您将会看到一个实时的损失与迭代图,在图中您会看到随着迭代的增加,损失会减少。培训结束后,您将会看到一个部署模型和导出模型的选项。只需单击 Deploy Model 或 Deploy and Export Model 即可为生产部署模型。正如您在下面看到的,我的模型在所选的超参数下表现出 100%的准确性。

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

Model Evaluation Metrics

模型完成训练后,您还可以查看不同的算法指标,以分析模型的实际表现,包括混淆矩阵、迭代与损失图等。导航到 Models 选项卡,单击您刚刚创建的模型,打开 advanced metrics 选项卡,您就可以开始了。

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

Confusion Matrix and Precision-Recall Curve of the trained model

现在,让我们测试我们的模型如何在两个不同的插图中识别受损的房屋和完好的房屋。要使用您自己的映像进行测试,请导航到 Deployed Models 选项卡,单击您刚刚部署的模型并单击以打开模型详细信息,然后在 Test model 部分下,放置您的映像或提供一个指向对象存储的超链接,或者使用 import 按钮导入映像。为了这个演示,我从网上下载了两张图片,一张是一栋毁坏的房子,另一张是一栋豪华平房。让我们看看算法对它们的表现。我们将 0.5 设置为算法进行预测的置信度阈值。

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

Algorithm performing object detection over the wrecked houses

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

Algorithm performing object detection over the intact houses

我们可以看到,这个模型表现得非常准确。由于我们使用了正则项,过拟合的可能性非常小。但由于数据较少,该模型不适合生产环境。现在,使用该模型作为基础模型,您可以训练不同数据集的模型,使用该模型自动标记新数据集中的对象,调整超参数,在一个地方创建集合模型。

自动标记数据集意味着使用您之前部署的模型自动标记数据集中的对象。随着此过程向图像中添加更多数据,模型的性能也会提高。

只需点击下面的链接,您就可以获得这个演示的部署基础模型。您将需要一个 IBM PowerAI Vision 帐户来访问该模型。

https://129 . 33 . 249 . 70/power ai-vision-ny/#/deployed models/2b 998d 64-7a 47-4d 21-92ff-2a 08a 093702 e

那么,你还在等什么?继续构建您自己的支持深度学习的模型,并立即运行!

结束!

宇宙中所有的力量都已经是我们的了。是我们把双手放在眼前,哭喊着天已经黑了”——斯瓦米·维威卡难达。

用神经网络中的正弦激活函数创造替代真理

原文:https://towardsdatascience.com/creating-alternative-truths-with-sine-activation-function-in-neural-networks-d45aac83ee52?source=collection_archive---------10-----------------------

利用正弦激活函数快速训练神经网络

你好!今天我要讲的是在神经网络中使用正弦函数作为激活。我会试着回答“这是什么?”,“它如何改变神经网络的未来?”、“这种方法的缺点是什么?”最后我还会演示一个例子。

什么是 sin 激活功能?

您可能已经从成千上万的出版物中听说过,神经网络使用单调函数,将神经网络的输出映射到 0 和 1 之间。这是正确的方法,因为这些激活函数给出类似概率的输出。但是,这种方法的缺点是输出只有一个真和一个错。但是在现实生活中,完全不同的值可能对一个事件给出相同的输出。因此,我们需要能够触及多个价值领域来获得真理,而不是试图接近一个小的价值领域来获得真理。

Josep M.Sopena、Enrique Romero、Rene Alquezar 撰写的论文“具有周期和单调激活函数的神经网络:分类问题的比较研究”也主张我们使用正弦激活的方法。下图展示了多种可能性。

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

Figure from paper

具有非单调激活功能的单元可以将空间划分为两个以上的区域。如果函数是周期的,那么区域的数目是无限的,可以解释为一个波前在变量空间中传播。

从图中我们可以看到,sigmoid 函数的输出只能是特定值的 1。但是,正弦函数的输出可以无限次为 1。这实际上意味着,如果神经网络应该为 x 和 x+100 个输入提供输出 1,我们可以通过使用 sin(x)和 sin(x+100)来使 model 的函数接近 y(x)=1 和 y(x+100)=1。如果我们不使用 sin 函数,我们的网络必须调整其权重和偏差,以便它们可以将 x 和 x+100 映射到 0 和 1 之间的范围。

它如何改变神经网络的未来?

证明了具有单调函数的神经网络给出了令人满意的结果。但是他们真正的问题是训练。它们被训练得如此之慢,是因为网络需要调整的参数数量达到了数百万。如果我们将使用 sin 函数作为激活,网络应该进行的调整次数将会更少。因此,网络的训练时间将显著减少。这可以降低神经网络模型的训练成本。

这种方法的缺点是什么?

不确定性和容易过拟合。由于具有正弦激活函数的网络在调整权值时简单而快速,这也造成了过拟合。不要过度拟合网络,我们需要给模型一个小的学习率,这样我们可以防止过度拟合。将网络的输出映射到无限概率空间实际上是增加了不确定性。对一个值的调整可能导致另一个值映射到一个非常不同的概率。

履行

正弦激活函数的正向和反向操作肯定有不同的实现。我只是尝试了我脑海中的那个。

def error_function_for_sin_single(output,y):
    to_search_best = np.sin(np.linspace(output-1,output+1,10000)*np.pi/2)
    index = np.argmin(np.abs(to_search_best-y))
    to_be = np.linspace(output-1,output+1,10000)[index]
    error = to_be-output
    #print("to be:",to_be,"as is",output,"error",error/10)
    return errordef error_function_for_sin_multiple(self,output,y):
    derror_doutput = []
    for cnt in range(y.shape[0]):
        derror_doutput.append(self.error_function_for_sin_single( output[cnt], y[cnt]))
    derror_doutput = np.array(derror_doutput)
    #print("____________")
    return derror_doutput/2

代码的解释:

  • 假设我们有一个输出数组(output)比如[o1,o2,o3,…]仍然没有被正弦函数激活。我们有“未来”数组(y)
  • 首先,我们通过 error _ function _ for _ sin _ multiple 函数中的 for 循环从数组中取出每个元素。
  • 然后,用我们从数组中取的元素调用 error_function_for_sin_single 函数。
  • 在 error_function_for_sin_single 函数中,我们计算输出值(to_search_max)周围的正弦函数。(在正弦函数中,我将输出乘以 pi/2,因为我希望值 1 为 pi/2,它稍后将映射为值 1,作为正弦函数的输出。)
  • 然后我们通过计算 y 和 to_search_best 之间的差来找到最小误差的指标。
  • 给出最小误差的索引实际上是输出应该是的值。因此,我们找到这个值和输出之间的差异,以便我们可以反馈到神经网络进行反向传播。
  • 找到错误后,我们将它们附加到一个列表中,以便将它们全部交给反向传播。

演示如何快速接近目标值

数据集:MNIST 数字数据库

算法:使用正弦基函数的前馈神经网络

层数:输入= 4704(我做基本特征提取,所以不是 784),L1 = 512,L2: 128,输出:10

学习率:0.0000001

我试图过度训练模型,将图像映射到其标签,以测量最少的纪元。

使用正弦激活功能:误差在 13 个周期内降至 0.044。

无正弦激活功能:误差在 19 个周期内降至 0.043。

另外,我上面提到的论文对正弦激活函数做了几个实验。其中一个是“螺旋问题”

来自论文的结果:

具有简单架构的标准 BP 还没有找到这个问题的解决方案(参见[Fahlman and Labiere,1990])。[Lang 和 Witbrock,1988]使用具有复杂架构的标准 BP(2–5–5–5–1,带快捷方式)在 20,000 个时代内解决了该问题。“Supersab”平均需要 3500 个历元,“Quickprop”需要 8000 个,“RPROP”需要 6000 个,而“Cascade Correlation”需要 1700 个。为了解决这个问题,我们构建了一个架构为 2–16–1 的网络,在隐藏层使用正弦作为激活函数,在输出层使用双曲正切。这种架构是迄今为止用来处理这个问题的最简单的架构。结果如表 1 所示。初始权重范围的重要性是显而易见的。对于小范围的非线性参数,学习是不可能的(见表 1 中的前两行

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

The results of experiment made on spiral problem.

我希望你喜欢阅读这篇文章。虽然每个人都在谈论寻找复杂问题解决方案的极其复杂的神经网络,但我认为我们仍然应该检查神经网络的基本算法。因为基本面的变化带来的影响更大。

用 Tableau 创建一个动画条形图比赛

原文:https://towardsdatascience.com/creating-an-animated-bar-chart-race-with-tableau-6a7839f703af?source=collection_archive---------11-----------------------

使用 Tableau 模拟动画条形图竞赛的分步指南

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

不久前,我在 Reddit 上的/r/dataisbeautiful 上发布了一个动画条形图,显示了澳大利亚按原籍国分列的国际学生人数的变化。这样的动画条形图非常适合以非常醒目(或者有时很烦人)的方式展示一段时间内不同类别对应的数值数据的演变。

半个月前我没有时间去记录它是如何完成的,但我最近决定,在忘记它们之前,最好在某个地方记录我遵循的步骤和我的思维过程,所以这里有一篇文章详细介绍了当时我脑海中的想法:

在我继续之前,我想说这篇文章很大程度上受到了我从 Ludovic Tavernier 的博客上读到的教程的启发。我对他发现这种用画面模拟条形图动画帧的方法表示最大的尊敬和钦佩,他所做的一切值得称赞!

事实上,我密切遵循他的步骤来创建我发布的条形图,但在浏览完教程后,我认为提炼和阐述某些部分可能会有用,以使教程更全面,更易于初学者理解,并概述所涉及方法的步骤背后的动机和逻辑。本文中的所有代码(除了一些调整)都取自 Ludovic 的,再次感谢他!

在分步指南中,我将向您展示如何使用 Tableau 创建自己的动画条形图,并以我的学生人数数据集为例。希望在本教程结束时,您能看到一些东西在移动!

入门指南

Tableau 不会自动从数据集创建动画条形图。解决这个问题的一个快速方法是让 Tableau 创建单独的帧,当你把它们串在一起时模拟一个动画。这和卡通的制作方式是一样的。

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

It’s never too late to get moving. Sometimes we fall, but sometimes we succeed too. (Photo: freepik.com / macrovector)

现在,要做到这一点,你最好让 Tableau 自动创建这些帧,这些帧又是从你提供给 Tableau 的数据中创建的。但是除非你真的喜欢手动创建数据集,否则你实际上是如何自动创建这些“伪数据”的呢?

这种方法就是围绕这样做的。

1.创建必要的数据条目

在这个步骤中,您需要创建 3 张数据(最好都在同一个 Excel 文件中)。目标是创建一个包含更多行的更大的数据集,使得每一行代表 1)每个动画帧和 2)动画条的每一点(更多信息见下文)。

不明白吗?这里有一个简单的例子:你从一个包含 20 个条目的表格开始,从 2001 年到 2020 年。现在,您想保留更多关于每年每个月的信息,所以您为“月”创建了一个额外的列。每一年包含 12 个月,您需要确保每一年,比如 2001 年,每个月重复 12 次。因此,您会得到 20 x 12 个条目。这就是我们所说的笛卡儿积。

回到我们应该做的事情,我们想在数据集中复制条目,但是我们想以更优雅的方式来做,而不仅仅是手动为每个条目创建行。因此,我们将通过在单独的表格中为我们想要引入的每个新列创建新的通用条目(或者在前面的例子中,为每个单独的月份创建新的通用条目)来实现这一点。然后,我们让 Tableau 在这些表上自动执行笛卡尔连接。

您的第一张工作表将包含您的原始数据,而另外两张工作表将包含新的通用条目。

表 1:关于数据集的信息

第一张工作表实际上是您想要制作动画的主数据集。应包含等基本信息。除此之外,您需要创建一个额外的列链接,稍后将用于连接其他数据表上的数据。

  • :您观察的时间单位的数值
  • 类别:任何你想观察的事物的名称,比如国家名称
  • :您感兴趣的数值,如 count
  • 链接:作为连接各种数据表的锚的标识符(在所有数据表中使用相同的值;为了简单起见,在这种情况下为 1)

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

Sheet 1: Information about your dataset

第 2 页:各酒吧信息

该工作表实际上为数据集中的每个类别创建了条形图的 4 个角(将出现在图表中)。点 1 代表矩形左上方的点,随后的每个点都是顺时针方向,以点 4 代表左下方结束。

  • 链接:作为连接各种数据表的锚点的标识符(在整个数据集中使用相同的值;为了简单起见,在这种情况下为 1)
  • :表示条的某个角的点(编号为 1-4)

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

The four corners of a bar labelled from 1 to 4

您的第二张工作表应如下所示:

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

Sheet 2: Information about each bar

表 3:每个动画帧的信息

此表创建动画帧,以动画显示数据集中指定的固定间隔之间的时间间隔。例如,如果您的数据集中的最小时间间隔是 1 年,该表将创建 k 个帧数,以动画显示从年【n+1】年的条形图。

  • link :一个标识符,作为连接各种数据表的锚(在整个数据集中使用相同的值;为简单起见,在这种情况下为 1)
  • anim :创建的每一帧的标识符(编号为 1 到 k ,您想要为每个间隔创建的帧数)

您的第三个工作表应该完全如下所示:

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

Sheet 3: Information about each animation frame

有了这三张工作表后,打开一个新的 Tableau 工作簿,并选择您在上一步中创建的 Excel 文件。对链接列上的所有工作表执行笛卡尔连接。

回头看看 Tableau 创建的新数据条目,您应该会看到,原始数据集中的每个数据条目都已经用条形中每个点的条目以及每个动画帧的条目进行了复制:

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

Each original entry in your dataset gets replicated for each animation frame and for each point on the bar. Each highlighted box shows the 4 entries needed to define a specific bar at a particular frame.

2.创建必要的变量来定义每个条形

现在,我们的数据集大大扩展,以容纳每个动画帧和每个动画条的更多数据条目,我们现在的目标是计算这些条目的一些值,这些值将定义每个特定帧的条的大小和位置。

切换到一个空的 Tableau 表,然后我们继续使用数据集的列中包含的数据创建几个计算字段:

anim_inter :一个,包含当前帧/每间隔总帧数的比值(例如 2/10 = 0.2)

[Anim]/{MAX([Anim])}

:一个数字,表示某一区间(如 2001.2 年)内的确切动画帧

[Year]+[anim_inter]

value_current :一个数字,表示间隔开始时的棒线实际值(如 2001 年至 2002 年间隔开始时为 190)

{FIXED [Year],[Category]:MIN([Value])}

value_next :一个数字表示间隔结束时棒线的实际值(如 2001 年和 2002 年之间的间隔结束时为 195)

LOOKUP(ATTR([value_current]),1)

value_inter :一个数字,表示在间隔内的一个精确动画帧上的条的内插值(例如在间隔 2001.3 时为 193.2)

ATTR([value_current])+
([value_next]-ATTR([value_current]))*ATTR([anim_inter])

rank_current :一个数字,根据间隔开始时的 value_current 显示该条相对于其他条的等级

RANK_UNIQUE(ATTR([value_current]),”desc”)

rank_next :一个数字,显示间隔结束时棒线的等级

LOOKUP([rank_current],1)

rank_inter :一个数字显示在间隔内的一个精确的动画帧上条形的‘中间’等级

[rank_current]+([rank_next]-[rank_current])*ATTR([anim_inter])

@x_inter :一个数字表示条的指定角的 x 值(即点 1 和点 4 的值应该总是 0,因为条的左侧不移动;而点 2 和 3 的那些应该总是指示条的当前长度)

IF ATTR([Point])=1 THEN 0
ELSEIF ATTR([Point])=2 THEN [value_inter]*1.0
ELSEIF ATTR([Point])=3 THEN [value_inter]*1.0
ELSEIF ATTR([Point])=4 THEN 0
END

@y_inter :一个数字表示条的指定角的 y 值(即点 1 和 2 的值应该总是相同的,并且表示条的上边缘的高度;而 3 和 4 的那些应该总是相同的,并且代表条的底部边缘。y 值之间的任何差异表示该条的高度)

IF ATTR([Point])=1 THEN [rank_inter]*1.0 
ELSEIF ATTR([Point])=2 THEN [rank_inter]*1.0
ELSEIF ATTR([Point])=3 THEN [rank_inter]+0.5
ELSEIF ATTR([Point])=4 THEN [rank_inter]+0.5
END

filter :一个数字,显示该条 value_inter 的等级;我们将使用它来过滤掉不在顶部 x 条数内的所有其他条

RANK_UNIQUE([value_inter],”desc”)

标签:一个字符串表示该条的类别value _ inter类别显示在条形的点 1 上方,而 value_inter 显示在点 3 上方

IF ATTR([Point])=1 THEN ATTR([Category])
ELSEIF ATTR([Point])=3 THEN STR(INT([value_inter]/1000))+”K”
END

3.观想时间!

本质细节都解决了,我们现在终于可以专注于这个过程中令人兴奋的部分:观想!

设置货架和卡片

让我们从在 Tableau 工作表上摆放所有搁板和卡片开始:

  1. @x_inter 拖到架上。修改表格计算
  2. @y_inter 拖至架,然后再次重复完全相同的步骤。将第二个 @y_inter 设置为双轴
  3. 右键单击视图窗格上的任意两个 y 轴,并选择同步轴。将轴的范围设置为从 0 到 11,并反转其刻度。
  4. 拖至架,并确保其为离散尺寸
  5. 过滤器拖到过滤器架上。
  6. 选择 @y_inter 标记卡片下的多边形标记,添加年份类别anim_inter细节,最后路径。确保这些都作为尺寸添加。
  7. 选择 @y_inter(2) 标记卡片下的圆圈标记,添加类别标签标签,以及年份、anim_inter明细。确保这些都作为尺寸添加。

在这一系列步骤的最后,您的 Tableau 工作表应该看起来像这样:

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

How your Tableau worksheet should look like at the end of this series of steps

指定一些表格计算

设置好所有字段后,就该配置一些表计算了,以确保我们之前创建的计算字段在视图窗格中正常工作:

  1. 右键点击架上的 @x_inter ,配置表格计算。使用计算 @x_inter 。这就使得 Tableau 对每一个 anim_inter类别 *按计算 @x_inter
  2. 右键点击架上的 @y_inter ,配置表格计算。设置为使用类别计算 rank_current ,使用类别年份计算 rank_next ,每选择一个 类别重新启动。对两个 @y_inter 轴重复此操作。
  3. 右键点击过滤器卡下的过滤器,设置表格计算。使用类别计算过滤器,使用计算值 _ 下一个。指定 0–10 作为值的范围。

*对于那些不真正理解表格计算的人来说,请点击这里查看安迪·克里贝尔的精彩解释。

瞧啊。经过这么多步骤后,你终于有了一些条形图的外观!点击播放(界面右上角)查看你的条形图移动!

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

The moment of truth: a bar chart appears!

在条形图上润色

然而,正如你肯定会注意到的,你的星盘可能仍然是丑陋的一面,仍然有一些事情要做,以润色一些美感:

  1. @y_inter(2) 标记卡片下,选择标签。点击“…”编辑文本。从字段中删除<类别>,并将< AGG(label) >向右对齐。接下来,将标签定位自动改为右上。最后选择复选框允许标签覆盖其他标记
  2. 点击尺寸并在滑块上选择最小尺寸。现在你不再有看起来傻傻的长方形了!
  3. 为了给你的条添加一些冷色调,将 value_inter 添加到 @y_inter 标记卡中作为颜色。使用计算 value_inter (就像前面的一系列步骤一样),最后选择你想要的颜色渐变。

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

How your screen should look like after some aesthetic tweaks

这些是我认为使你的图表看起来最起码是像样的一些步骤,尽管你也应该自己尝试其他的事情来使你的图表看起来更好——尝试重命名/隐藏你的轴、轴标题和图表标题!

您还可以创建一个 Tableau 仪表板,除了具有条形图的视图窗格之外,还具有多个视图窗格。一个这样的视图窗格可以显示随着图形的移动动画的年份。

如果你发现了有趣的个性化条形图的方法,请告诉我!

结束步骤

最后但同样重要的是,目前还不能使用 Tableau 直接将模拟的动画条形图导出为图像或视频。对于那些想以另一种格式保存动画图表的人,可以考虑使用电脑(或第三方应用程序)上的屏幕录制功能来录制 Tableau 上的动画帧。然后你可以使用互联网上的免费工具将你的视频文件转换成. gif 格式,如果你喜欢的话,就像我一样。这就是你要的,一个漂亮的动画图表!

现在一切都好

所以这应该是本教程!我想我可能已经记下了我的大部分想法和我为了得到一个好看的动画条形图比赛而遵循的步骤,所以如果我有一段时间不接触 Tableau,我可能很容易在未来重新创建一个。

如果你有任何问题或疑问,请告诉我,谢谢你的阅读!希望你在创建图表时玩得开心!

如果你想阅读我创作的动画条形图比赛(澳大利亚的比赛在最上面)的简短反思以及它在 Reddit 上引起的巨大讨论,请查看我的另一篇文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值