双深 Q 网络
DQN 家族
解决深度 Q 学习中的最大化偏差
简介
在本帖中,我们将探究双 Q 学习网络背后的动机,并看看实现这一点的三种不同方式:
- 原算法在 【双 Q 学习】(Hasselt,2010)
- 《双 Q 学习的深度强化学习》(Hasselt et al .,2015) 中来自同一作者的更新算法,
- 最近的方法,Clipped Double Q-learning,见于《Actor-Critic Methods 中的寻址函数逼近误差》(Fujimoto et al .,2018) 。
如果你还不完全熟悉 Q-learning,我建议你快速看一下我关于 Q-learning 的帖子!
动机
考虑目标 Q 值:
具体来说,
照这样取最大高估值就是隐含地取最大值的估计值。这种系统性的高估在学习中引入了最大化偏差。由于 Q-learning 涉及 bootstrapping——从估计值中学习估计值——这种高估可能是有问题的。
这里有一个例子:考虑一个单一状态 s ,其中所有动作的真实 Q 值都等于 0,但是估计的 Q 值分布在零上下一些。取这些估计值的最大值(明显大于零)来更新 Q 函数会导致 Q 值的高估。
Hasselt 等人(2015)在跨不同 Atari 游戏环境的实验中说明了这种高估偏差:
Source: “Deep Reinforcement Learning with Double Q-learning” (Hasselt et al., 2015),
正如我们所看到的,传统的 DQN 倾向于大大高估行动价值,导致不稳定的培训和低质量的政策:
解决方法:双 Q 学习
解决方案包括使用两个独立的 Q 值估计器,其中一个用于更新另一个。使用这些独立的估计,我们可以无偏 Q 值估计的行动选择使用相反的估计[3]。因此,我们可以通过从有偏差的估计中分离出我们的更新来避免最大化偏差。
下面,我们将看看双 Q 学习的 3 种不同公式,并实现后两种。
1。“双 Q 学习”中的原始算法(Hasselt,2010)
Pseudo-code Source: “Double Q-learning” (Hasselt, 2010)
最初的双 Q 学习算法使用两个独立的估计值Q^{A}
和Q^{B}
。对于 0.5 的概率,我们使用估计值Q^{A}
来确定最大化动作,但是使用它来更新Q^{B}
。相反,我们使用Q^{B}
来确定最大化动作,但是使用它来更新Q^{A}
。通过这样做,我们获得了预期 Q 值的无偏估计量Q^{A}(state, argmaxQ^{next state, action)
,并抑制了偏差。
**2。来自同一作者的“使用双 Q 学习的深度强化学习”(Hasselt 等人,2015),**的更新版本
在第二个双 Q 学习算法中,我们有一个模型Q
和一个目标模型Q’
,而不是像(Hasselt,2010)中那样有两个独立的模型。我们使用Q’
进行动作选择,使用Q
进行动作评估。那就是:
我们最小化Q
和Q*
之间的均方误差,但是我们让Q'
慢慢复制Q
的参数。我们可以通过定期硬拷贝参数来实现,也可以通过 Polyak 平均来实现:
其中,θ’是目标网络参数,θ是主要网络参数,τ(平均速率)通常设置为 0.01。
3。削波双 Q 学习,见于 《演员-评论家方法中的寻址函数逼近误差》(藤本等,2018) 。
在削波双 Q 学习中,我们遵循 Hasselt 2015 的原始公式。我们对真实的 Q 值有两个独立的估计。这里,为了计算更新目标,我们取由两个 Q 网络产生的两个下一状态动作值的最小值;当一方的 Q 估计值大于另一方时,我们将其降至最小,避免高估。
Fujimoto 等人提出了这种设置的另一个好处:最小值算子应该为具有较低方差估计误差的状态提供较高的值。这意味着最小化将导致对具有低方差值估计的状态的偏好,从而导致具有稳定学习目标的更安全的策略更新。
实施指南
我们将从与本系列第 1 部分相同的 DQN 代理设置开始。如果你想看更完整的设置实现,请查看我的 Q-learning 帖子或我的 Github 库(底部链接)。
DQN 代理:
- Hasselt 等人的双 Q 学习,2015:
我们将初始化一个模型和一个目标模型:
为了计算损失,我们使用目标模型来计算下一个 Q 值:
然后我们慢慢地将模型参数复制/平均到目标模型参数:
2.藤本等人 2018 年剪辑双 Q 学习:
我们初始化两个 Q 网络:
为了计算损耗,我们计算两个模型的当前状态 Q 值和下一状态 Q 值,但使用下一状态 Q 值的最小值来计算预期 Q 值。然后,我们使用预期的 Q 值更新两个模型。
最后是更新功能:
这就结束了我们的双 Q 学习算法的实现。双 Q 学习经常在最新的 Q 学习变体和演员评论方法中使用。在我们以后的文章中,我们会一次又一次地看到这种技术。
感谢阅读!
在这里找到我的完整实现:
Q-learning 家族(PyTorch)算法的模块化实现。实现包括:DQN,DDQN,决斗…
github.com](https://github.com/cyoon1729/deep-Q-networks)
参考
- “使用函数逼近进行强化学习的问题”(Thrun 和 Schwartz,1993)
- “双 Q 学习”(Hasselt,2010)
- “双 Q 学习的深度强化学习”(Hasselt et al .,2015) ,
- “演员-评论家方法中的寻址函数近似误差”(藤本等人,2018)
- 强化学习:导论(萨顿和巴尔托)
打倒技术债!面向数据科学家的干净 Python。
ata 科学团队倾向于向两个相互竞争的方向发展。一方面是数据工程师,他们重视高度可靠、健壮的代码,这些代码承担着较低的技术债务。另一方面,有一些数据科学家,他们重视在概念验证等环境中快速建立想法和算法的原型。
虽然更成熟的数据科学职能部门在双方之间享有富有成效的工作伙伴关系,拥有复杂的 CI / CD 管道,并有明确的职责分工,但早期团队往往由大量缺乏经验的数据科学家主导。结果,代码质量受损,技术债务以粘合代码、管道丛林、死的实验代码路径和配置债务【1】的形式成倍积累。
Can you imagine a life without xkcd?
最近,我写了一篇关于为什么数据科学家的代码往往平庸的头脑风暴,在这篇文章中,我希望揭示一些方法,让更多初出茅庐的数据科学家能够编写更干净的 Python 代码,更好地构建小规模项目,同时减少你无意中给自己和团队带来的技术债务。
下面的内容既不详尽,也不深入,只是一系列浅显的介绍,告诉你如何以一种更有思想的方式建立数据科学项目。有些点会很明显,有些会不太明显。
这里有一个快速的概述:(1)风格指南,(2)文档,(3)类型检查,(4)项目文件夹结构,(5)代码版本控制,(6)模型版本控制,(7)环境,(8) Jupyter 笔记本,(9)单元测试,(10)日志记录。
Python 风格指南— PEP 8 和林挺
可读性很重要。以至于有一个完整的 PEP 致力于此:PEP8,它为编写干净的 Python 代码提供了编码约定。
符合 PEP8 标准被认为是构成python 式代码的最低要求。这表明你已经意识到了 Python 开发者应该具备的最基本的习惯,这表明你能够更容易地与其他开发者合作,最重要的是,这使得你的代码更具可读性,愚蠢地保持一致,并且更容易被你消化。
如果我在这里复制并重新格式化 PEP8 风格指南,那将是浪费大家的时间。所以,你可以随意浏览pep8.org,看看例子,体会一下在微观层面上写干净代码意味着什么(相对于在宏观或系统层面上写干净代码)。
PEP8 中给出的例子包括为命名约定、缩进、导入和行长度设置标准。
顺便说一下,PEP8 是你应该使用成熟的 ide,如 PyCharm (我认为是更好的 Python IDE)来编写代码,而不是像 Sublime 这样的简单文本编辑器的众多原因之一。Python 的重量级 ide 通常符合 PEP8 风格指南,当您违反其原则时会发出警告,并提供代码库的自动重新格式化。
有四个,尽管实际上还有很多其他的——命令行工具对你的源代码进行静态分析,以保持它的整洁和一致:
- ——最受欢迎的棉绒。检查源代码,并作为一个错误和质量检查。它比 PEP8 有更多的验证检查和选项。众所周知,这是一个有点过了头的沉重的输出按照默认设置,但是。
- 黑色—自动重新格式化您的 Python 代码。Black 就地重新格式化整个文件,并将字符串格式化为双引号。
- PyCodeStyle—官方 linter 工具,根据 PEP8 python 的样式约定检查 python 代码。
- flake 8—pyflakes、pycodestyle 和 mccabe 周围的包装器,它验证 pep8、py flakes 和循环复杂度的包装器。
边注 1。linter 不会告诉你你是否已经很好的命名了你的变量。这个被新手开发者嘲笑的技能是一个值得掌握的技能。
旁注 2。在安装这些软件包之前,最好是在虚拟环境中。稍后会详细介绍。
记录您的项目— PEP257 和 Sphynx
当 PEP8 概述了Python 的编码约定时, PEP257 标准化了 docstrings 的高层结构、语义和约定:它们应该包含什么,以及如何以一种清晰的方式表述。和 PEP8 一样,这些不是硬性规定,但它们是你应该明智地遵循的指导方针。
如果你违反了这些惯例,最糟糕的是你会得到一些白眼。
那么,什么是 docstring 呢?docstring 是作为模块、函数、类或方法定义中的第一条语句出现的字符串文字。这样的 docstring 成为该对象的 doc special 属性。与 PEP8 一样,我不会复制并重新格式化整个 PEP,您应该在自己的时间浏览它,但这里有两个函数的 docstrings 示例。
- 示例单行 docstring for a 函数 add :
def add(a, b):
"""Sum two numbers."""
return a + b
2.示例函数复合体的多行 docstring:
def complex(real=0.0, imag=0.0):
"""Form a complex number.
Keyword arguments:
real -- the real part (default 0.0)
imag -- the imaginary part (default 0.0)
"""
if imag == 0.0 and real == 0.0:
return complex_zero
...
Sphynx
在所有格式良好的文档字符串都准备好之后,接下来您需要将它们转换成漂亮的项目文档。事实上的 Python 文档生成器被称为 Sphynx,它可以生成 html、pdf、unix 手册页等输出。
这里有一个关于 Sphynx 入门的好教程。简而言之,在某个目录(通常是您的 docs 目录)中初始化 Sphynx 并设置其配置之后,您将处理您的 reStructuredText (。rst)文件,在调用生成*后,将被转换成您喜欢的输出文档类型。
有趣的是,您可以直接从 docstrings 创建对 Python 程序其他部分的引用,这些引用在输出文档中显示为链接。
为了说明 Sphynx 文档生成器的典型输出,这里的是一个包含 Sphynx 生成的文档的 Python 项目的不完整但膨胀的列表。例子包括 matplotlib 、 networkX 、 Flask 和 pandas 。
类型检查— PEP484 、 PEP526 和 mypy
因为 Python 是一种动态类型语言,所以默认情况下没有静态类型检查。这是好的,因为它提供了灵活性和快节奏的开发,但这是坏的,因为您不会在运行系统之前(在编译时)捕捉简单的错误,而是在运行时捕捉它们。通常,像 Python 这样的动态类型语言比静态类型语言需要更多的单元测试。这是乏味的。在 Scala、Java 或 C#等语言中,类型声明出现在代码中,编译器检查变量是否通过类型合法传递。
最终,静态类型充当了一个安全网,在某些情况下使用它是明智的。好消息是 Python 实际上提供了一种叫做类型提示的东西。
下面是一个函数注释类型提示的例子。这意味着name
应该是类型str
,并且该函数也应该返回一个str
。
def greeting(name: str) -> str:
return 'Hello ' + name
下面是一个变量注释类型提示的例子。这意味着变量i
应该是一个整数。请注意,以下语法适用于 Python 3.6 及更高版本。
i: int = 5
…但坏消息是 Python 解释器忽略了类型提示,并且没有运行时效果(PEP 目前正在进行中)。
但是,如果类型提示没有运行时效果,为什么还要麻烦它呢?两个原因。首先,它让你的代码更清楚地知道它要做什么以及它是如何流动的。其次,因为你可以在 Python 中使用静态类型检查器,默认情况下它不会运行。主要的是 mypy 。
附注 1:由于类型提示的可选性,你可以自由地将它们放在代码中的任何地方,某些地方,或者什么地方都不放。只是尽量保持他们的位置一致。
边注 2:如前所述,PyCharm 对于执行 PEP8 标准非常有用。但是它在类型提示的情况下也很有用。它会自动检查您的类型提示,并在您违反预期时告诉您。默认情况下,在 PyCharm 中它们被设置为警告,但是您也可以将它们设置为错误。
项目文件夹结构— cookiecutter
就像凌乱的桌子是混乱思想的标志一样,凌乱的文件夹结构也是如此。
许多项目从一开始就受益于一个考虑周全的目录结构。不幸的是,启动项目的一个常见方法是创建一个基础项目目录,并将所有内容直接放在该目录下——从数据到笔记本到生成的模型再到输出——而没有考虑到当您的简单游乐场项目变成越来越复杂的参数化管道时出现的不良影响。
最终,你会以某种形式的技术债务结束,这些债务必须在以后以时间和努力的形式偿还。而真正的悲剧呢?所有这些都可以提前避免,只要从一开始就获得正确构建项目的远见。
我们之所以纠结于此,部分原因是因为创建项目文件夹结构是乏味的。我们想一头扎进探索数据和建立机器学习模型。但是相对较小的努力投入可以节省大量的精力。
更整洁的文件夹结构将鼓励最佳实践,简化关注点的分离,并使学习(或重新学习)旧代码更加愉快。
幸运的是——或者说是开源开发者的辛勤工作——已经有了一个现成的解决方案来创建我们想要的文件夹结构: cookiecutter 。
创建许多数据科学项目通用的干净的文件夹结构只需一个命令。观看下面的视频,了解如何设置 cookiecutter 项目结构。
由 ericmjalbert 录制
asciinema.org](https://asciinema.org/a/244658)
请注意,cookiecutter 非常强大,实际上不仅仅是生成干净的项目文件夹结构。要了解更多信息,请查看精彩的 cookiecutter 文档,这是一个数据科学项目理念。**
代码版本控制— git
这一点我就不赘述了,因为这应该是一个既定的事实。软件开发的现代世界已经远离了 2000 年前黑暗的西部。每个人和他的狗都应该为他们的项目使用某种版本控制。简单的协作、高效的版本控制、回卷和代码备份。说够了。
使用 git 只是第一步。用好它和完全是另一回事。
提交代码:“尽早提交,经常提交”是明智的建议。避免提交大块的代码,而要提交小的、孤立的功能。提交时,编写描述性的提交消息,准确记录您的更改。
多用户最佳实践:这在很大程度上是由上下文驱动的。我与我的团队合作的方式是有一个主分支,它是从不直接推进的,一个开发分支,其中代码的运行版本正在工作,但从不直接工作,然后特性分支,其中团队的个别成员将编码特性,这些特性后来被合并到开发分支。当开发分支准备好发布时,它被合并到主分支中。这种封装的工作方式使得多个开发人员在不干扰主代码库的情况下处理特定功能变得简单,从而减少了合并冲突的可能性。欲了解更多信息,请查看此链接。**
模型和数据版本控制— dvc
模型和数据与代码不一样,它们永远不应该被推入代码库中。他们有独特的生命周期管理要求,有不同的运营限制。然而,适用于代码版本化的相同原理应该适用于数据和模型版本化。
构建在 git 之上的一个很棒的开源工具 dvc 可能就是您在这里寻找的。它本质上是一个数据管道构建工具,具有简单的数据版本化选项,在一定程度上有助于解决数据科学中的再现性危机。它能够有效地将您的数据和模型推送到您的服务器,无论是本地、AWS S3、GCS、Azure、SSH、HDFS 还是 HTTP。
dvc 围绕三个主要理念:
- 大型文件的版本控制
- 内置具有再现性的轻质管道
- git 之上的版本管理和实验管理
补充说明:对整个数据集进行版本控制的另一种方法是存储重新创建这些数据集所需的元数据,并引用在引用元数据后面创建的模型。
从环境开始构建— 虚拟化
如果您的常规实践手册中没有划分您的环境,您可能已经花了一两个下午来平衡系统范围的库版本。也许你正在做一个项目,然后转移到另一个项目,更新 numpy 和噗!,你的第一个项目有一个依赖中断。
想象一下另一种情况,您的项目被另一个团队成员从 git 中取出,他使用了项目中某个库的不同版本。他们编写一些代码,这些代码依赖于一个你的版本没有的新函数,然后推回到主分支(给聪明人一句话:永远不要直接推送到主分支),这是你拉的。你的密码被破解了。太好了。
通过使用虚拟环境来避免这种情况。对于简单的 Python 项目,使用 virtualenv。如果你有复杂的环境需求,使用 docker 之类的东西。
下面是一个简单的 virtualenv 工作流程:
- 创建新项目时运行
mkvirtualenv
pip install
您的分析所需的软件包- 运行
pip freeze > requirements.txt
来锁定用于重新创建分析的确切包版本 - 如果您发现您需要安装另一个包,再次运行
pip freeze > requirements.txt
并将更改提交给版本控制。
关于笔记本的说明— jupytext
Jupyter 笔记本在数据科学领域非常普遍。它们是围绕识字编程范式构建的,并作为强大的媒介,能够将快速原型开发和易于开发与生成流畅演示的能力相结合,中间代码段与输出和解释性文本交错。漂亮的东西。
但是,尽管笔记本电脑给人们带来了很多好处,它们也带来了很多痛苦。你的电脑里有多少Untitled7.ipynb
文件?也许笔记本电脑最大的挫折是它们与版本控制的不协调。
这样做的原因是因为他们是一类被称为的编辑器,所见即所得,编辑软件允许用户查看与最终结果非常相似的内容。这意味着文档抽象出了元数据,对于笔记本来说,它通过将代码封装在大型 JSON 数据结构中来嵌入代码,二进制数据(如图像)保存为 base-64 编码的 blobs。
不过,这里需要澄清一下。Git 可以处理笔记本,因为你可以把它们推送到你的仓库。Git 不能在比较不同版本笔记本的情况下很好地处理它们,并且也很难为你提供对写在笔记本上的代码的可靠分析。
如果你在你的公司内部或者仅仅是在公共的 GitHub 上搜索签入的笔记本,你很可能会发现数据库凭证、敏感数据、“不要运行这个单元”代码块,以及一大堆其他的不良行为。为了避免这种痛苦,你可以在每次准备签入笔记本时清除输出。但这是手动的,意味着您每次都必须重新运行代码来生成输出,如果有多个用户拉和推同一个笔记本,即使被清除的笔记本元数据也会改变。
通过工具,有选择。其中最受欢迎的是 jupytext 。这里是作者关于如何使用它的一个很棒的教程。你所要做的就是安装 jupytext,它将提供一个整洁的笔记本下拉菜单,用于下载你的代码的降价版本,并省略输出,然后显式忽略.gitignore
中的所有.ipynb
文件。
单元测试——单元测试
对代码进行单元测试是确保隔离的代码块按预期工作的有效方法。它允许自动化您的测试过程,及早发现错误,使过程更加敏捷,并最终帮助您设计更好的系统。python 中最常用的测试框架是“包含电池”,这是一个名为[unittest](https://docs.python.org/3/library/unittest.html#module-unittest)
的内置标准模块,它提供了一套丰富的工具来构建和运行测试。另一个测试工具是[pytest](https://docs.pytest.org/en/latest)
。
有很多关于如何进行单元测试的教程,但是这里有一些关键的提示。正如异常处理应该最小化你试图捕捉的潜在错误的功能数量,每个单元测试应该集中在一个微小的功能上,以证明它在工作。每个单元测试应该是完全独立的,并且能够单独运行。您的测试套件应该在开发新功能之前和之后运行;事实上,实现一个钩子来自动运行所有测试是一个好主意,在将代码推送到共享的 repo 之前,这种类型的测试通常是一些 CI / CD 管道的一部分,许多开源示例服务中的一个叫做 travis 。试着让你的测试更快!对每个测试函数使用长的描述性名称。测试应该位于你的源代码的一个单独的目录中,更多信息请参见文件夹结构部分。
测井— PEP282 ,
日志记录是任何超越 POC 领域的系统的关键部分。这是一种跟踪程序执行过程中发生的事情,并将这些信息保存到磁盘上的方法。它对调试有很大的帮助,在石头上留下了痕迹,可以帮助你很好地识别你的 bug。
它通常用于两个目的之一。有诊断日志**,它记录与应用程序操作相关的事件。以及审计日志,其记录用于 MI 报告的基本运行时分析。并且每个日志信息有几种类型:调试、信息、警告、错误和严重。**
日志是内置的、用于日志的标准库 Python 模块。
包裹
如果你坚持到最后,恭喜你。你应该得到由衷的赞扬,希望以上信息至少有一些是有用的。现在出去编码吧。
使用简单的 Python 爬虫程序下载课程材料
一个自动下载课程资料的爬虫示例
Photo by Darwin Vegher on Unsplash
最近我在修加州大学伯克利分校著名的 CS 61A 。我想下载幻灯片并全部打印出来。但是总共有 36 张幻灯片。作为一个懒的程序员,我不会浪费宝贵的时间去点击 36 次下载按钮。所以我写了一个简单的脚本来自动下载所有的幻灯片。
在这篇文章中,我将简单介绍你需要知道什么来构建一个简单的下载器。
找到下载按钮的模式
首先,打开下载页面。下载按钮显示为“8pp”(见下图),这意味着一页包含 8 张幻灯片。
8pp is the download button
好了,接下来我们在 Chrome 中打开 DevTools 。您也可以在其他浏览器中找到 DevTools。单击 DevTools 左上角的箭头图标,然后将光标移动到“8pp”按钮,这将突出显示 DevTools 中的 HTML 元素。
我们可以得到下面的 HTML 行。下载链接为“assets/slides/01-Functions _ 8pp . pdf”和“assets/slides/02-Names_8pp.pdf”。这些链接在a
标签的href
属性中。
<li><a href="assets/slides/01-Functions_full.pdf" class="label label-outline">full</a></li>
<li><a href="assets/slides/01-Functions_1pp.pdf" class="label label-outline">1pp</a></li>
<li><a href="assets/slides/01-Functions_8pp.pdf" class="label label-outline">**8pp**</a></li>
<li><a href="assets/slides/01.py" class="label label-outline">01.py</a></li>
...
<li><a href="assets/slides/02-Names_full.pdf" class="label label-outline">full</a></li>
<li><a href="assets/slides/02-Names_1pp.pdf" class="label label-outline">1pp</a></li>
<li><a href="assets/slides/02-Names_8pp.pdf" class="label label-outline">**8pp**</a></li>
<li><a href="assets/slides/02.py" class="label label-outline">02.py</a></li>
我们希望自动提取所有链接。所以我们必须找到这些a
标签行的一些模式。一个非常明确的模式是,它们都包含 8pp 文本(上面代码中的粗体部分)。我们可以先找到包含 8pp 文本的a
标签行,然后通过href
属性提取链接。
找到包含“8pp”的a
标签行
我们使用 XPath 来定位a
标记行。
slide_links = html.xpath('//li/a[text()="8pp"]/@href')
我们可以认为html
是下载页面的全部 HTML 源代码。然后我们用 XPath 提取链接。//
是整页的开始。li/a
表示我们在li
标签下找到所有a
标签行。[text()="8pp"]
是一个过滤器,用于获取那些只包含“8pp”文本的a
标签行。
完成下载链接
我们得到的slide_links
如下图所示
['assets/slides/36-Natural_Language_8pp.pdf',
'assets/slides/37-Final_Examples_8pp.pdf',
'assets/slides/36-Natural_Language_8pp.pdf',
...]
我们必须添加一个前缀来完成下载链接。前缀是下面的url
。
url = 'http://inst.eecs.berkeley.edu/~cs61a/fa18/'
总结一下
在运行脚本之前,您需要安装一些 Python 包。
pip install requests
pip install lxml
pip install wget
requests
是一个进行 HTTP 请求的工具。lxml
是一个使用 XPath 的工具。而wget
是给定网址下载素材文件的工具。
别忘了创建一个“幻灯片”文件夹来存放下载的文件。
在你做好准备之后,你可以运行下面的脚本来下载所有的材料。
查看我的其他帖子 中等 同 一分类查看 !
GitHub:bramble Xu LinkedIn:徐亮 博客:bramble Xu
使用 REST API 从 Twitter 下载数据
嘿大家好!这是关于从 Twitter 上获取数据并使用它来获得某些见解的出版物列表中的第二篇文章,比如某个趋势上最有影响力的用户、主题建模等等。
如果你没有读过第一篇文章,你可以在这里看看:
在这篇文章中,我们将介绍如何使用流媒体 API 来获取包含特定单词或标签的推文,以及如何…
medium.com](https://medium.com/@jaimezornoza/downloading-data-from-twitter-using-the-streaming-api-3ac6766ba96c)
虽然上一篇文章讨论了如何从 Twitter 上收集实时生成的数据,但这篇新文章将涵盖如何收集历史数据,如某个用户、他的追随者或他的朋友以前的推文。
我们开始吧!
使用 REST API 收集历史数据
当使用流 Twitter API 时,我们收集实时产生的数据,REST API 服务于相反的目的:收集收集时间之前产生的数据,即。历史数据。
使用这个 API,我们可以收集包含某些关键字的旧推文,类似于以前的做法,但我们也可以收集与平台相关的其他信息,如不同用户帐户的朋友和追随者,某个帐户的转发,或某个推文的转发。
Twitter APIs 中的用户由两个不同的变量来标识:
- 用户 screen_name ,也就是我们都习以为常的带@的 Twitter 名称。比如“@jaimezorno”。
- user_id ,是每个 Twitter 用户的唯一数字标识符,是一个很长的数字串,比如 747807250819981312。
在数据收集过程中,当我们想要指定我们想要从中收集数据的用户时,我们可以使用该用户的 screen_name 或 user_id 来完成,因此在深入研究 REST API 提供的更复杂的函数之前,我们将了解如何获取某个用户的 Twitter Id,我们知道该用户的用户名,反之亦然。
用用户名获取 Twitter 的 Id,反之亦然
从 Twitter Id 到用户屏幕名称是需要的,因为我们稍后将描述的一些函数返回 Twitter 标识符而不是用户屏幕名称,所以如果我们想知道谁是与相应 Id 相关联的实际用户,我们需要这个功能。
和往常一样,第一步是收集到 Twitter API。
import tweepy
import time
access_token = "ENTER YOUR ACCESS TOKEN"
access_token_secret = "ENTER YOUR ACCESS TOKEN SECRET"
consumer_key = "ENTER YOUR CONSUMER KEY"
consumer_secret = "ENTER YOUR CONSUMER SECRET"
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
api = tweepy.API(auth)
在代码中,将“输入您的……”然后运行最后三行来创建到 Twitter REST API 的连接。
注意,这次我们没有像使用流式 API 那样创建一个流对象,而是创建了一个 api 对象。一旦我们做到了这一点,从屏幕名称到 id 以及从 id 到屏幕名称的转换就非常简单了,这是通过运行以下代码块中的行来完成的:
user = api.get_user(screen_name = 'theresa_may')
print(user.id)
这个块在 REST API 中查询 Theresa May 的官方 Twitter 帐户的 user_id,返回: *747807250819981312,*这是与该帐户相关联的 id。这里需要注意的是,screen_name 不包含@。
要以相反的方向完成这项工作,并收集我们知道其 id 的帐户的屏幕名称,非常简单:
user = api.get_user(747807250819981312)
print(user.screen_name)
它将打印: theresa_may 。正如我们所见,Id 和屏幕名称都是 API 返回的 用户 对象的属性,其中包含许多有价值的信息,如用户关注者计数、出版物数量、帐户创建日期等等。这些参数将在另一篇文章中探讨。
从用户名到 id 以及从 id 到用户名的转换就这么简单。现在让我们探索 REST API 更复杂和有用的功能。
从特定用户的时间线收集推文
某个用户的时间线是他或她过去发布或转发的推文。收集这些信息有助于了解社交网络中某个账户的先前活动。
然而,我们必须知道,将要使用的方法只能返回特定用户最近 3200 条推文,因此,如果我们正在收集一个非常活跃的帐户的帖子,并且想要很久以前的推文,我们将无法获得它们。
这是 Twitter API 的一个已知限制,目前还没有修复,因为通过这样做,Twitter 不必存储每个 Twitter 帐户产生的所有 tweets。
在如上所述创建了到 Twitter REST API 的连接之后,为了收集用户的时间表,我们必须使用类似于以下代码块中所示的代码结构:
try:
for tweet in tweepy.Cursor(api.user_timeline, screen_name="theresa_may", exclude_replies=True).items():
print(tweet)
except tweepy.TweepError:
time.sleep(60)
正如我们所见,这段代码引入了 Twitter API 固有的新概念: 光标对象。 尽管看起来令人生畏,但这只不过是 API 必须处理分页并能够以高效有序的方式交付内容的方式。
在这种情况下,我们将从用户 @theresa_may 收集历史推文,排除其他用户对推文的回复。可以添加一个类似的参数 include_rts 来从这个用户时间表中消除转发。
此外, try-except duo 被添加来处理我们可能发现的任何错误,如请求率超出或保护用户。当操作这种类型的 API 时,这是非常常见的。
这段代码的输出是一个非常难看的对象,叫做 状态对象 ,用于每条推文,看起来像这样:
Status(_api=<tweepy.api.API object at 0x000001C52728A710>, _json={'created_at': 'Sun May 12 11:55:41 +0000 2019', 'id': 1127542860520329216, 'id_str': '1127542860520329216', 'text': 'Congratulations to @SPendarovski on your inauguration as President of North Macedonia. I witnessed the strong relat…………
另一篇文章,如用户对象案例,将详细解释这些对象的性质及其属性,然而现在我们将只描述如何从中收集一些最有趣的字段。
让我们看看我们如何能做到这一点。
我们将保持与前一个块相同的代码结构,但是添加了一些额外的行,我们将使用这些行来获取我们认为最相关的 status 对象的部分。
try:
for tweet in tweepy.Cursor(api.user_timeline, screen_name="theresa_may", exclude_replies=True, count = 10).items():
tweet_text = tweet.text
time = tweet.created_at
tweeter = tweet.user.screen_name
print("Text:" + tweet_text + ", Timestamp:" + str(time) + ", user:" + tweeter)
except tweepy.TweepError:
time.sleep(60)
这一次,执行这段代码应该会显示如下内容:
Text:We’re driving the biggest transformation in mental health services for more than a generation. [https://t.co/qOss2jOh4c,](https://t.co/qOss2jOh4c,) Timestamp:2019-06-17 07:19:59, user:theresa_may
Text:RT @10DowningStreet: PM @Theresa_May hosted a reception at Downing Street to celebrate:
✅ 22 new free schools approved to open
✅ 19,000 ad…, Timestamp:2019-06-15 13:53:34, user:theresa_may
Text:Two years on from the devastating fire at Grenfell Tower, my thoughts remain with the bereaved, the survivors and t… [https://t.co/Pij3z3ZUJB,](https://t.co/Pij3z3ZUJB,) Timestamp:2019-06-14 10:31:59, user:theresa_may
考虑到您将获得的 tweet 取决于您正在搜索的用户在执行代码之前发布的 tweet,因此如果您以目标用户 theresa_may 运行这些块,您很可能不会获得与我相同的 tweet。
尽管从前面的代码块返回的结果可能看起来更好,但我们可能希望数据的格式便于以后存储和处理,比如 JSON。
我们将对代码做最后一次修改,以便打印出每条 tweet,以及我们想要的 tweet 中的字段,作为 JSON 对象。为此,我们需要导入 json 库,并对代码做进一步的修改,如下所示:
import json
try:
for tweet in tweepy.Cursor(api.user_timeline, screen_name="theresa_may", exclude_replies=True, count = 10).items():
tweet_text = tweet.text
time = tweet.created_at
tweeter = tweet.user.screen_name
tweet_dict = {"tweet_text" : tweet_text.strip(), "timestamp" : str(time), "user" :tweeter}
tweet_json = json.dumps(tweet_dict)
print(tweet_json)
except tweepy.TweepError:
time.sleep(60)
这一次,我们将输出与以前相同的字段,但采用 JSON 格式,这样便于其他人处理和理解。在这种情况下,相同 tweet 的输出将是:
{"tweet_text": "We\u2019re driving the biggest transformation in mental health services for more than a generation. [https://t.co/qOss2jOh4c",](https://t.co/qOss2jOh4c%22,) "timestamp": "2019-06-17 07:19:59", "user": "theresa_may"}
{"tweet_text": "RT @10DowningStreet: PM @Theresa_May hosted a reception at Downing Street to celebrate:\n\u2705 22 new free schools approved to open \n\u2705 19,000 ad\u2026", "timestamp": "2019-06-15 13:53:34", "user": "theresa_may"}
{"tweet_text": "Two years on from the devastating fire at Grenfell Tower, my thoughts remain with the bereaved, the survivors and t\u2026 [https://t.co/Pij3z3ZUJB",](https://t.co/Pij3z3ZUJB%22,) "timestamp": "2019-06-14 10:31:59", "user": "theresa_may"}
在了解了如何有效地收集和处理某个用户的时间表之后,我们将看看如何收集他们的朋友和追随者。
收集某个用户的追随者。
获取一组用户的关注者是 Twitter 研究中最常见的行为之一,因为创建关注者/被关注者网络可以提供一些关于某个主题或标签的特定用户群的非常有趣的见解。
要获得某个用户的关注者,只需像以前一样使用我们的凭据连接到 API,然后运行以下代码即可:
try:
followers = api.followers_ids(screen_name="theresa_may")
except tweepy.TweepError:
time.sleep(20)
通过在 api = tweepy 中设置参数wait _ on _ rate _ limit为真。API(auth,wait_on_rate_limit=True) 当我们连接到 API 时,下载任何类型的数据时超过速率限制的错误都可以避免,因此尽管在本文的前几部分中没有使用过它,我还是建议在您打算从 Twitter REST API 下载大量数据时使用它。
这里有一个列表,上面有账户 *@theresa_may 的所有关注者的 id。*这些 id 然后可以使用我们之前描述的 api.get_user 方法翻译成用户名。
如果我们想要收集某一组用户的关注者,我们只需要在前面的代码块中添加几行代码,如下所示:
user_list = ["AaltoUniversity", "helsinkiuni","HAAGAHELIAamk", "AaltoENG"]follower_list = []
for user in user_list:
try:
followers = api.followers_ids(screen_name=user)
except tweepy.TweepError:
time.sleep(20)
continue
follower_list.append(followers)
在这种情况下,我们将收集与芬兰大学相关的用户帐户的追随者。该代码的输出将是一个列表( follower_list ),在每个索引中有一个列表,其中包含来自 user_list 的具有相同索引的帐户的追随者。
使用枚举函数可以很容易地关联这两个列表(用户和关注者列表):
for index, user in enumerate(user_list):
print("User: " + user + "\t Number of followers: " + str(len(follower_list[index])))
这个模块的输出将是:
User: AaltoUniversity Number of followers: 5000User: helsinkiuni Number of followers: 5000User: HAAGAHELIAamk Number of followers: 4927User: AaltoENG Number of followers: 144
这可能会让你感到疑惑:账号*@阿尔托大学*和 @helsinkiuni 的粉丝数量是否完全相同,都是 5000 人?
最明显的答案是否定的。如果你查看这两所大学的 Twitter 账户,你会发现它们的粉丝都在万分之几的范围内。
那为什么我们只得到 5000 呢?
嗯,这是因为对于涉及分页的问题,Twitter API 将它们的响应分解在不同的页面中,我们可以认为这些页面是具有某个最大大小的所请求信息的*【块】,要从一个页面转到下一个页面,我们需要使用一种特殊的对象,称为光标*对象,这在上面已经提到过。
以下代码使用了相同的功能,但这次使用了一个光标对象,以便能够抓取每个用户的所有关注者:
user_list = ["AaltoUniversity", "helsinkiuni","HAAGAHELIAamk", "AaltoENG"]
follower_list = []
for user in user_list:
followers = []
try:
for page in tweepy.Cursor(api.followers_ids, screen_name=user).pages():
followers.extend(page)
except tweepy.TweepError:
time.sleep(20)
continue
follower_list.append(followers)
这一次,如果我们使用枚举循环来打印每个用户和他们的追随者数量,输出将是:
User: AaltoUniversity Number of followers: 35695User: helsinkiuni Number of followers: 31966User: HAAGAHELIAamk Number of followers: 4927User: AaltoENG Number of followers: 144
也就是每个账户的真实粉丝数。
收集某个用户的朋友。
类似于我们如何收集某个用户的关注者,我们也可以收集他的*【朋友】*,也就是某个用户关注的人群。为此,我们将一如既往地使用我们的凭据连接到 API,然后运行以下代码:
friends = []
try:
for page in tweepy.Cursor(api.friends_ids, screen_name="theresa_may").pages():
friends.extend(page)
except tweepy.TweepError:
time.sleep(20)
该代码块中的变量 friends 将是一个列表,其中包含我们选择的 screen_name 用户的所有朋友(在本例中为 theresa_may)
查看某个用户的关注者/朋友的数量。
如果我们对某个账户的追随者/朋友是谁不感兴趣,而只对他们的数量感兴趣,Twitter API 允许我们收集这些信息,而不必收集所需账户的所有追随者/朋友。
要做到这一点而不实际收集所有的关注者(考虑到下载速率限制,如果用户有很多关注者,这可能需要一段时间),我们可以使用我们之前使用的从 user.screen_name 到 user.id 的 api.get_user 方法*,反之亦然。下面的代码块显示了如何操作:*
user = api.get_user(screen_name = 'theresa_may')
print(user.followers_count)
print(user.friends_count)
它将输出:
83939129
我们也可以使用 Twitter user.id 来做这件事,如果我们知道它的话,就像之前看到的那样,就像这样:
user = api.get_user(747807250819981312)print(user.followers_count)
print(user.friends_count)
它会再次输出:
83939129
我们可以从 Theresa 的官方账户中看到,这是正确的追随者和朋友数量。
结论:
我们已经描述了 Twitter REST API 的主要功能,并解决了从它那里收集数据时可能会发现的一些问题。
这些数据可以用于很多目的:从使用复杂的机器学习算法检测趋势或假新闻,到推断某个品牌的积极程度的情感分析,图形构建,信息扩散模型等等。
如需进一步研究或澄清此处的信息,请参考本指南中的链接或:
Twitter 开发者页面:https://developer.twitter.com/en/docs
Tweepy 的 github 页面:https://github.com/tweepy/tweepy
Tweepy 官方页面:https://www.tweepy.org/
推特的高级搜索:https://twitter.com/search-advanced
敬请关注《社交网络分析》的更多帖子!
通过 Google Colab 将数据集下载到 Google Drive
在 Google Colab 中为数据科学项目使用 Google Drive 的分步指南
更新:(即将推出)TensorFlow 2.0 将为 Keras 推出新的分发策略,以便您可以使用相同的代码在 TPUs 上分发您的模型 。 Google Colab 发布后可以直接访问 Google Cloud TPUs !观看TF Dev Summit’19了解更多信息。
Google Colab and Google Drive back you up in deep learning — Photo Credit: Elizabeth Tsung
如果你像我一样使用旧的 MacBook Pro(2013 年末,120GB 高清),有限的存储将是我在数据科学项目中最大的障碍。对于那些也在从事具有大型数据集的数据科学项目的人来说,我确信保存数据集并在云上训练模型肯定会让你放心。
在本教程中,我将与您分享我在以下方面的经验:
- 将 Google Drive 安装到 Google Colab
- 通过 Google Colab 将数据集直接下载到 Google Drive
- 使用 Kaggle API
- 从竞赛网站,要求下载时需要用户名和密码
额外收获:一键启用 Google Colab 中的免费 GPU 支持,使用 Tensorflow 进行训练。
将谷歌硬盘安装到谷歌实验室
第一步
首先,进入你的谷歌实验室,然后输入以下内容:
from google.colab import drive
drive.mount('/content/gdrive')
该单元将返回以下内容,您需要转到该链接来检索授权码。那你就可以走了!
第二步
如果您能够访问 google drive,您的 Google Drive 文件应该都在以下位置:
/content/g Drive/My Drive/
而您当前的目录将是 /content/
Click on the arrow on the left and you will find the data structure.
为了方便使用,只需保存下面的代码片段并将其粘贴到 Google Colab 中,您就可以轻松地将您的 Google Drive 安装到笔记本上。
通过 Google Colab 将数据集直接下载到 Google Drive
在这一节中,我将与您分享我从 Kaggle 和其他竞争对手那里下载数据集的经验。
通过 Kaggle API 下载 Kaggle 数据集
步骤 1-从您的帐户获取 API 密钥
访问www.kaggle.com⇨登录⇨我的帐户⇨创建新的 API 令牌
将自动下载“kaggle.json”文件。
步骤 2 —上传 kaggle.json 文件
使用 Google Colab 中的这些代码片段完成任务:
from google.colab import files
files.upload() #this will prompt you to upload the kaggle.json
下面将创建必要的文件夹路径。
!pip install -q kaggle
!mkdir -p ~/.kaggle
!cp kaggle.json ~/.kaggle/
!ls ~/.kaggle
!chmod 600 /root/.kaggle/kaggle.json # set permission
步骤 3-下载所需的数据集
只需下载所需的数据集,语法如下:
!kaggle 竞赛下载-c ‘竞赛名称’-p “目标 _ 实验室 _ 目录”
!kaggle competitions download -c histopathologic-cancer-detection -p /content/gdrive/My\ Drive/kaggle/cancer
额外收获:请参见下面搜索 Kaggle 数据集的 git 要点
步骤 4 —解压缩
对于有多个 zip 文件的数据集,我倾向于将目录切换到指定的文件夹,然后一个一个地解压缩。
!解压缩-q 文件。zip]-d[出口]
-q 禁止打印正在解压的文件名
**-d【解压目录】**可选解压文件的目录
import os
os.chdir('gdrive/My Drive/kaggle/cancer') #change dir
!mkdir train #create a directory named train/
!mkdir test #create a directory named test/
!unzip -q train.zip -d train/ #unzip data in train/
!unzip -q test.zip -d test/ #unzip data in test/
!unzip sample_submission.csv.zip
!unzip train_labels.csv.zip
点击这里阅读更多 Kaggle API 文档。
从竞赛网站下载数据集,请求下载时需要用户名和密码
对于像 ICIAR2018 这样的比赛,你需要在下载数据集时提供用户名和密码。
要在 Google Colab 中执行此操作,首先您可以将当前目录更改为您希望保存数据集的文件夹。然后,使用 wget 而不是使用 curl 命令。
!wget --user=your_username --password=your_password http://cdn1.i3s.up.pt/digitalpathology/ICIAR2018_BACH_Challenge.zip
下载后,您可以使用上面相同的方法解压缩文件。
额外收获:一键启用 Google Colab 中的免费 GPU,用 Tensorflow 进行训练
在您将 Google Drive 安装到 Google Colab 并下载了所需的数据集之后,让我们在您的 Colab 笔记本中启用 GPU 并训练您的模型。
从任务栏:运行时⇨更改运行时类型
硬件加速器:无⇨ GPU
希望你觉得这个教程有用,祝你云计算愉快!
信用
感谢 Matt Gleeson 、 Finlay Macrae 的建议,让内容变得更好。
普通深度 Q 网络
DQN 家族
深度 Q 学习解释
介绍
该职位的结构如下:
我们将简要介绍一般的策略迭代和时间差分方法。然后,我们将 Q 学习理解为一个一般的策略迭代。最后,我们将理解并实现 Deepmind 的论文“用深度强化学习玩雅达利(Mnih et al. 2013) 中提出的 DQN。
通用策略迭代(GPI)
General policy iteration
我们把一般策略迭代称为策略评估和策略迭代的交替。我们从某个任意初始化的策略开始,评估该策略(表示为 E ),从评估中导出一个新策略(表示为 I ),并重复这个过程,直到我们达到一个最佳策略。通过这个迭代过程,我们得到{V_π}和{π}的单调递增(改进)序列。
我们如何保证这一点?我们将看看政策改进定理:
该定理指出,遵循政策π’的价值比遵循政策π’的价值更大。即政策π’优于π。我们现在将证明政策改进定理。
因此,通过政策评估和迭代的迭代相互作用,我们可以最终达到我们的最优政策和价值函数。
时间差分法
时间差分法是蒙特卡罗方法和动态规划方法的结合。回忆每种方法:
- 蒙特卡罗方法使用估计值(1)进行更新。因为我们不知道真实的期望值,所以我们从环境中采样 G_t。
- 动态编程(DP)方法使用(3)进行更新。我们说 DP 方法是 bootstrap,因为我们使用 v_pi(s_{t+1})的当前估计来执行更新。
在 TD 方法中,我们将蒙特卡罗的采样与动态规划的自举结合起来。我们对期望值进行采样,如(1)所示,并使用下一个状态值的当前估计值来更新原始状态的值,如(3)所示。TD 目标\delta
可以有多种形式,但以下是最基本的形式:
q 学习和 GPI
在 Q 学习中,我们直接逼近我们的最优动作值函数。在 GPI 意义上,我们从 Q 函数中导出策略,并通过 TD 方法执行策略评估,以获得下一个 Q 函数。
现在让我们的 Q 函数用θ参数化,在我们的例子中,就是神经网络。根据 GPI 公式,我们希望将当前 Q 值与目标 Q 值之间的差异降至最低。为此,我们希望获得两者之间的均方误差:
然后执行梯度下降以最小化两者之间的误差。
深度 Q 网络
发表在(Mnih 等人,2013 年)的 Deep Q learning 利用深度学习的进步从*高维度感官输入中学习策略。*具体来说,它使用卷积网络从 Atari 2600 游戏中学习原始像素,而不是低维特征向量。下图展示了 DQN 的建筑:
为了使计算更加可行,原始的 4 帧游戏图像(RGB 像素)被缩小到 4 帧(84 x 84)图像,从而得到一个(84 x 84 x 4)张量。然后我们把它输入到一个卷积神经网络,它输出一个包含每个动作的 Q 值的向量。从那里,我们使用一个探索方案(通常是 epsilon-greedy ),并在具有最高 Q 值的动作和随机动作之间进行概率选择。
在更高的层面上,深度 Q 学习是这样工作的:
- 使用当前策略在重放缓冲区中收集和存储样本
- 从重放缓冲器中随机抽样批次的体验(称为体验重放)
- 使用采样的经验来更新 Q 网络
- 重复 1-3
我们将进一步了解步骤(2)和(3),这将让我们直接进入实现阶段。
体验回放
为什么我们要随机抽样经验,而不是仅仅使用过去的连续经验?连续的经历彼此(在时间上)高度相关。在统计学习和优化任务中,我们希望我们的数据独立分布。也就是说,我们不希望我们提供的数据以任何方式相互关联。经验的随机抽样打破了这种行为的时间相关性,并将其分布/平均到许多以前的状态。通过这样做,我们避免了模型中的显著振荡或发散——相关数据可能产生的问题。
更新 Q 网络
为了更新 Q 网络,我们希望最小化目标 Q 值(根据贝尔曼方程)和当前 Q 输出之间的均方误差:
在哪里
最理想的情况是,我们希望误差减少,这意味着我们当前政策的输出越来越接近真实的 Q 值。因此,利用如上定义的损失函数,我们根据以下等式对损失函数执行梯度步骤:
实施指南
我们将从构建配备有卷积神经网络的深度 Q 网络开始:
在forward
函数中,我们输入像素图像,并通过我们的模型输出对应于每个动作的 Q 值向量。
然后,我们将构建我们的重放缓冲区,在那里我们可以存储体验—(状态、动作、奖励、下一个状态、bool(is_done))转换—以及用于学习的随机体验样本:
接下来,我们编写一个函数来计算每个梯度步长的值损失。这看起来像:
最后,我们将把它们都放在我们的 DQN 代理中:
香草 DQN 的实现到此结束。您可以在我的 GitHub 资源库中找到完整的可运行实现:
Q-learning 家族(PyTorch)算法的模块化实现。实现包括:DQN,DDQN,决斗…
github.com](https://github.com/cyoon1729/deep-Q-networks)
在本系列的后面部分,我们将探索 DQN 的许多变体,它们在许多方面改进了原来的版本。
感谢阅读!
参考资料:
- 用深度强化学习玩雅达利(Mnih et al. 2013)
- 强化学习:导论(萨顿和巴尔托)
下一篇文章:
我的系列将从香草深度 Q 学习(这篇文章)开始,直到 Deepmind 的彩虹 DQN,当前的艺术状态。查看我的下一篇文章关于用双 Q 学习减少高估偏差!
- 深度 Q 网络
- 双深 Q 网络
德卡里斯。—使用 Docker Machine、PyTorch 和 Gigantum 实现可移植和可再现的 GPU 工作流
CC image By I, Luc Viatour, CC BY-SA 3.0
TL;速度三角形定位法(dead reckoning)
- 手动创建可移植和可再现的 GPU 工作流是脆弱的、技能密集型的和费力的,即使使用容器也是如此。
- 幸运的是,你可以使用 Docker Machine、PyTorch & Gigantum 或多或少地实现自动化。
- 我们用这三样东西来展示一个强大的系统,以创建在笔记本电脑和云、CPU 和 GPU 之间无缝移动的工作流。
假设 —你应该有:
- 使用 Bash(在 Linux/macOS 上)或 PowerShell(在 Windows 上)的经验。
- Docker CE 安装在本地。
- AWS 凭证& EC2 GPU 实例的足够权限。
如果达不到这些要求,也不要绝望。你可以通过一些背景阅读和一些剪切粘贴来快速阅读这篇文章。
剧透预警——完成这篇帖子所用的时间将远远少于《GoT》第五集丹妮莉丝·坦格利安烧毁君临所需的时间。
介绍
用于并行处理的 GPU 为一些计算提供了令人难以置信的速度提升——最著名的是深度学习。好的图形处理器可以在 CPU 上完成复杂的计算。
不幸的是,安装和配置必要的软件环境需要技能和时间。对于大多数用户来说,访问 GPU 的唯一方式是通过不断增长的平台之一,这些平台提供了进入托管云环境的浏览器界面。这些平台的基本问题是,它们要么是免费的&在计算上毫无价值,要么是功能性的,但似乎是为企业预算量身定制的。
获得更广泛访问的一个途径是通过最大限度地减少设置所需的技能和时间,让人们能够自己做事情。另一种方法是使 GPU 工作流可移植,即自包含&易于跨各种资源运行。例如,促进 CPU & GPU 机器之间的移动使得能够在更便宜的 CPU 上测试&调试,从而节省昂贵的 GPU 用于实际计算。在这篇文章中,我们将做这两件事。
基本上,当谈到轻松复制和便携式 GPU 笔记本电脑时,我们会给你自己的龙。
我们使用的工具
Docker Machine是一个简单的 Apache 许可命令行工具,用于供应、配置&管理远程虚拟环境。
PyTorch 是一个 BSD 授权的深度学习框架,可以轻松在 CPU 和 GPU 之间切换进行计算。
Gigantum 是一个麻省理工学院许可的本地应用程序,它与云服务配对,以创建任何人都可以轻松使用的可复制工作流*。*
我们的存在性证明有三个部分:
- 用 Docker Machine 创建 EC2 GPU 实例的简单过程:
- 从 Bash 提示符配置实例;
- 只需点击几下鼠标,即可导入和运行 PyTorch 迁移学习笔记本。
在我们开始之前——为了使这篇文章对不同的用户都是可靠的,我们在过程的确定性方面犯了错误&工具的简单性。
第 1 部分—创建一个 EC2 p2.xlarge 实例
我们开始吧。
第 1 步到第 3 步是一次性步骤,但是第 4 步中的 Docker Machine 命令是您随时可以用来创建新遥控器的命令。
如果您以前没有使用过,Docker Machine 是一个简单的工具,可以在远程主机上轻松安装、管理和连接 Docker。方便的是,它可以自动发送和简单的端口转发。可以了解一下这里 & 这里。
第 1 步— 验证 Docker CE &是否正在运行。
在终端(Bash 或 PowerShell)中,运行:
docker version
您必须在本地运行 Docker CE。如果你没有一个相当最近的版本,你可能想要更新它。注意:你不能使用 Docker 工具箱。
**第二步(仅限 Linux 用户)——**没有 Docker 机器就安装。
Docker for Linux 的发行版通常不包含 Docker Machine(但 macOS & Windows 包含)。要解决这个问题,请在 Bash 终端中运行以下命令:
base=https://github.com/docker/machine/releases/download/v0.16.0 curl -L $base/docker-machine-$(uname -s)-$(uname -m) > /tmp/docker-machinesudo install /tmp/docker-machine /usr/local/bin/docker-machine
然后,注销&登录——确保 docker-machine 在您的路径上。
步骤 3 — 为 CLI 配置 AWS API 凭证。
如果您没有凭证,请访问控制台进行设置。您需要:
- 您的访问密钥 ID, youraccesskey。
- 您的秘密访问密钥: yoursecretkey 。
确保将您的 AWS CLI 配置为自动调用凭证供命令行使用,否则您需要将它们添加到下面的 Docker 机器命令中。
在终端中,运行:
aws configure
AWS Access Key ID [None]: ***youraccesskey***
AWS Secret Access Key [None]: ***yoursecretkey***
Default region name [None]:
Default output format [None]:
步骤 4 — 使用 Docker Machine 创建实例。
现在可以使用一个 Docker Machine 命令(带有多个参数)来设置 p2.xlarge 实例。
*(在 Linux 或 Mac 上)*在 Bash 终端中输入以下命令
docker-machine create --driver amazonec2\
--amazonec2-amiami-0a313d6098716f372 \
--amazonec2-instance-type p2.xlarge \
--amazonec2-region us-east-1 \
--amazonec2-root-size 64 \
gigantum-gpu
*(在 Windows 上)*在 PowerShell 终端中输入以下命令
docker-machine create --driver amazonec2 `
--amazonec2-amiami-0a313d6098716f372 `
--amazonec2-instance-type p2.xlarge `
--amazonec2-region us-east-1 `
--amazonec2-root-size 64 `
gigantum-gpu
Pasting and executing the commands for Windows PowerShell is pretty simple.
如果这次成功了 —恭喜!您已经设置了实例。
如果没有成功——原因可能很简单。
- 你的一把钥匙错了。纠正一下。
- 您没有足够的权限创建实例。纠正这一点,并确保从亚马逊请求访问一个
p2.xlarge
。 - (Windows)Docker 有问题。重新启动 Docker,然后再次尝试 Docker Machine 命令。
用 Docker 机器管理遥控器
- 用
docker-machine ls
检查正在运行什么。 - 用
docker-machine start gigantum-gpu
开始实例。 - 用
docker-machine restart gigantum-gpu
重启实例。 - 用
docker-machine stop gigantum-gpu
停止实例。 - 用
docker-machine ssh gigantum-gpu
SSH 进入实例。
不是受益人
删除实例时要小心。这个命令很简单,docker-machine rm gigantum-gpu
,但是有两个潜在的问题。
第一个——是永久的。你将会失去一切。
第二个——在停止实例之前删除实例可能不会实际关闭实例。它可能仍在运行&你不会知道,直到你去控制台。
有意识、有目的地删除实例。在删除实例之前,请始终停止该实例。
第 2 部分—在 GPU 实例上安装软件
好的。现在结束了,让我们再走 5 步。你只需要做一次。
准备好了吗?
第一步 — 登录遥控器。
docker-machine ssh gigantum-gpu
Docker Machine completely automates the SSH process. No more secrets. No more putty.
除非我们另外告诉你,否则你是在远程的 Bash 终端中。
第二步 — 将你的用户添加到 docker 组,添加显卡驱动 ppa,&安装 Nvidia GPU 驱动。
sudo usermod -aG docker $USER
sudo add-apt-repository -y ppa:graphics-drivers/ppa
然后
sudo apt-get install -y linux-aws nvidia-headless-430 nvidia-utils-430
将出现一个菜单。默认设置是保留当前的本地版本。不要。
选择Install the package maintainer's version
并点击回车。
You will see this menu. Install the package maintainer’s version, although it doesn’t matter much how you answer.
步骤 3 — 安装 Nvidia Docker 驱动,然后注销并重启实例。
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | \
sudo apt-key add -
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | \
sudo tee /etc/apt/sources.list.d/nvidia-docker.list
然后
sudo apt-get update
sudo apt-get install -y nvidia-docker2
通过从本地终端输入ctrl+d
、&来注销,并使用
docker-machine restart gigantum-gpu
第四步 — 登录,安装 Gigantum,注销&然后停止实例。
通过在本地终端输入以下内容登录:
docker-machine ssh gigantum-gpu
然后,在远程运行的 Bash 提示符下:
sudo apt-get install -y python3-pip
pip3 install --user gigantum
现在,通过输入ctrl+d
&退出,然后使用以下命令从本地终端重新登录:
docker-machine ssh gigantum-gpu
然后
gigantum install
最后,用ctrl+d
&退出,然后从本地终端用以下命令停止:
docker-machine stop gigantum-gpu
第五步 — T 休息一下,伸伸腿,稍微反思一下。
当你休息的时候,回想一下刚刚发生的事情。你的努力换来了什么?
- 在很短的时间内&使用相对较少的命令,您就可以设置最便宜的 EC2 GPU 实例并为容器化的 GPU 工作流安装软件。
- 您可以通过启动实例并使用 Docker Machine 登录来随时访问它。以上是一次性设置!
最后,请记住,如果您不停止实例,Amazon 将向您收取计算时间费用。此外,它还连接了 64 GB 的 EBS 存储。如果你不删除它,即使它被停止,你也会产生(相对较小的)费用。
第 4 部分—运行 PyTorch GPU 工作流
好的。让我们开始讨论最后 20 分钟的要点。
我们将轻松地在 GPU 实例上运行 CPU/GPU 不可知的&可再现 PyTorch 笔记本。笔记本改编自Sasank Chilamkurthy的转学教程。举例说明了 CPU/GPU 便携笔记本的最佳实践和两种迁移学习思路:微调 & 特征提取。
导入和检查 PyTorch 项目
对于下一部分,只有三个命令。其余的在浏览器中。
docker-machine start gigantum-gpu
docker-machine ssh gigantum-gpu -L 10000:localhost:10000
gigantum start --wait 60
对于 Gigantum 和 Jupyter 的工作,我们不会列出另一个步骤序列,我们将只显示一个过程的视频(加速)。
可以使用以下 URL 导入迁移学习项目:
[*https://gigantum.com/tinydav/simple-pytorch-transfer-learning*](https://gigantum.com/tinydav/pytorch-transfer-learning)
From start to finish, this takes a total of 7 minutes in real time.
该视频应该是不言自明的,但我们可以解开它一点。
- 登录应用程序后,运行 PyTorch 笔记本所需的一切都作为代码、数据和环境的存储库导入。
- 自动构建一个包含运行笔记本的环境的容器。
- 在构建容器时,我们查看活动和环境的信息。
- 构建完成后,单击 Code 选项卡中的文件名会在另一个选项卡中启动 JupyterLab。如果没有,你可能已经安装了弹出窗口拦截器。
- 笔记本运行在 GPU 上。
非常简单,在 p2.xlarge 上每小时只需 0.90 美元,而不是在 SageMaker 上每小时 1.20 美元。
有一点需要注意——你并不局限于在基于这个实例的 CUDA 10.0 上运行。如果你查看另一个项目my-first-project
的环境选项卡,你会注意到它没有的 CUDA 10.0,它有一套完全不同的包。
完成后,请确保执行以下操作:
- 在终端运行
gigantum stop
。 - 用
ctrl+d
退出实例。 - 用
docker-machine stop gigantum-gpu
停止实例。
现在,给读者一个练习
您可能还记得,我们说过您可以使用相同的设置在 CPU 上运行笔记本电脑。我们将此作为一个练习留给读者,但我们给出了一个简短的草图,以使它进行得更顺利一些。
按照上面的说明,你可以自己设置一个 CPU 实例,然后运行这个笔记本。如果你有 Docker CE,你可以通过安装 Gigantum 在本地完成,或者你可以通过修改上述过程在远程完成。
这次你不需要安装任何与 Nvidia 相关的软件。事实上,你甚至不需要在 AWS 上这样做,因为 Docker Machine 为其他提供商工作,比如数字海洋。你甚至可以用你自己的遥控器来做
在 CPU EC2 实例上运行该笔记本的基本步骤是:
- 使用 Docker Machine 创建一个 t2.xlarge EC2 实例,使用我们为
gigantum-cpu
创建的 AMI。 - 将您的用户添加到 docker 组,安装 pip3,然后安装 Gigantum。
- 请注意,在运行
gigantum install
之前,您需要注销然后重新登录。 - SSH 进入
gigantum-cpu
,做同样的端口转发,启动 Gigantum &然后导入运行笔记本。
完成后,确保停止gigantum-cpu
实例。
最后 TensorFlow 对 LSTM 笔记本的调侃
如果您完成了上面的练习,那么您现在已经有了一个使用 PyTorch 非常容易地运行 CPU/GPU 不可知工作流的系统。其中重要的一点是 PyTorch 可以无缝地为您管理交换机。
但是 TensorFlow 呢?它不能以这样一种无缝的方式处理这种切换,但事实证明,您可以使用不同的方法在 CPU 或 GPU 上创建工作流,同样是在 Gigantum 中。
在下一篇文章中,我们将讨论如何使用 Docker Machine、Gigantum & TensorFlow 实现这一点。
但是,如果您愿意,您现在可以使用刚刚配置的 p2.xlarge 自己完成这项工作。你不需要改变什么。
你可以使用 https://gigantum.com/dmk/trump-speech-generation 的 LSTM 笔记本开始使用。
由Dav Clark(HDS)&Tyler white house(Gigantum 的 CEO)撰写。
在 Twitter 上关注我们,或者在 Spectrum 上向我们问好!
拖放数据预处理:使用 Tableau Prep 清理 Titanic 数据集
Photo by Markus Spiske on Unsplash
Tableau Prep,有多厉害,失败在哪里,最大优势在哪里。
在我作为 Tableau 的商业智能顾问的工作中,我听到了很多“Tableau 不是 ETL”的说法,对此我大部分时间都不得不同意。不管最近的变化,Tableau 已经对其 Tableau 桌面产品进行了改进,以提高处理大量数据的性能,在大多数情况下,在使用 Tableau 桌面进行分析和报告之前,有必要添加一个 ETL 流程,如 Talend 或 Pentaho。
意识到之前的情况,去年(2018) Tableau 向公众发布了产品 Tableau Prep Builder,目的是在使用 Tableau Desktop 进行数据探索之前提供一个拖放工具。作为该工具的顾问,我有责任探索其潜力,了解其优势和实际能力,以评估在客户的 BI 项目中向其展示该工具是否可行。为了做到这一点,我决定将我曾经在 Python 中做过的清理过程复制到流行的 Titanic 数据集,小心地注意到该工具可能存在不足之处,以及它是否真的足够兼容以应用于更大的项目。
背景和规格
作为我在数据科学学习过程中的一部分,我在一年多前参加了广受欢迎的 Kaggle 竞赛“Titanic:Machine Learning from Disaster”,为了这个项目,我使用 Python 执行了数据集清理和预测,并将其与 Tableau 中的数据集探索和分析相集成。该项目可以在这个链接中读取,我将使用 Tableau Prep Builder 版本 2019.3 复制用 Python 完成的数据准备工作。在整篇文章中,我将尝试解释 Tableau Prep 的一般功能,但重点是 Python 工作与工具中创建的流的比较。
[## 使用集成学习的泰坦尼克号预测
下载数千个项目的开放数据集+在一个平台上共享项目。探索热门话题,如政府…
www.kaggle.com](https://www.kaggle.com/danielmartinezb/titanic-prediction-using-ensemble-learning/notebook?scriptVersionId=4573741)
第 1 部分:加载数据集
与大多数 Kaggle 数据集一样,清理过程从读取 CSV 训练文件开始。Pandas 用于确保数据的结构是正确的,并使用函数 describe() 了解数据集的描述性统计信息,例如记录的数量、每列的最大值和最小值等。
Reading the CSV file with Python and Pandas
Summary of the data
在 Tableau Prep 中,与纯文本文件中的数据源的连接与在 Pandas 中一样简单,此外,它还具有 Tableau Desktop 中为大量服务器(如 Hadoop、Teradata、MySQL、BigQuery 等)提供的已知原生连接向导。
Some of the connections available in Tableau Prep
此时,Tableau Prep 开始显示其一些节省时间的功能。如果有多个结构相同的文件,可以创建一个“通配符联合”,只需单击一下,就可以解决 Pandas 中需要的多个连接。
此外,一旦连接到数据,我们就可以定义一个在流程中使用的样本。这使得流程中的每个进程都具有更好的性能,因为无论如何,在准备流程结束时,清理将应用于整个数据集。
Options available when connecting to a source in Tableau Prep
我们还发现,从 Pandas describe()函数获得的信息可以在 Tableau Prep 的“Profile Pane”中找到,在这里我们将能够查看每个字段的摘要描述,并将其与原始表格结构进行对比(甚至利用一些视觉效果)。
第 2 节:处理缺失值
决定如何处理包含空值字段的记录是数据清理最常见的任务之一。对于这一步,我开发了一个函数,能够可视化每个字段中的空记录的数量以及它们所代表的总数的百分比(这些信息可以在 Prep 中从“Profile Pane”中查询)。
Pandas in Python vs Profile Pane in Tableau Prep
从具有空值的字段的信息中,由于缺失值的数量很大,我决定删除列“Cabin ”( Tableau Prep 也建议这样做),从列“apollowed”中删除两个空值,并用任意值填充空值“Age ”,以便稍后进行转换。
在 Tableau Prep 中复制这些操作简单、直观,只需几次点击。
(“年龄”列的更改是在流程的后期开发的)
在 Python 清理过程的这一点上,我开始分析 Tableau 中的数据集,并根据结果决定对变量应用不同的转换。应该注意的是,从 Tableau Prep 中,我们可以在流程的任何时候加载 Tableau Desktop 中的更改。
第 3 节:列转换
Tableau Desktop 中分析的第一个转换是创建“家庭规模”字段,该字段由“Parch”和“SibSp”字段的总和组成。
要复制 Tableau Prep 中的行为,只需创建一个计算字段,其公式为:
[SibSp] + [Parch]
然后简单地删除菜单中剩余的列。
下一步是提取每个名字的标题。由于乘客的姓名没有给模型添加任何信息,所以我决定提取其头衔(先生、小姐、夫人等。)我们可以更广泛地概括乘客。
这只需要在 Tableau Prep 上点击几下。
然后进行分组,其中只有“主人”、“小姐”、“先生”和“夫人”的称谓保留,其余的归类为“其他”。
对于这种行为,Tableau Prep 提供了几个分组选项(包括按单词相似度甚至发音相似度分组)。
下一个清理步骤可能是最复杂的,在 Tableau Desktop 的支持下,我获得了每个图书的平均年龄,并用该值填充了年龄字段的空记录。
为了在 Tableau Prep 中自动模拟此行为,我需要创建一个具有此平均值的字段(使用 Tableau Prep 聚合过程),然后通过连接过程将其集成到数据集,最后我创建了一个计算字段,该字段复制了“年龄”字段,如果记录为空,则取平均值字段的值。
Section in the flow to complete the nulls in ‘Age’
Agregation and Join steps in Tableau Prep
计算字段中使用的公式为:
IF ISNULL([Age]) THEN
[Average Age]
ELSE
[Age]
END
最后,列转换过程以删除带有乘客 ID 和机票号码的字段结束(类似于客舱删除步骤)。
第 4 部分:用于建模的数据集的调整
到目前为止,数据集是完全干净的,可以用于模式分析和报告。可以看出,所有步骤都可以通过一系列点击以最小的努力来执行,Tableau Prep 已经实现了其功能(在使用 Tableau Desktop 创建报告之前清除数据)。此时,您可以添加一个输出过程,该过程会将所有更改应用到数据集,并将结果导出为 CSV 格式或作为 Tableau Desktop 的摘录来创建报告。
然而,为了让数据集准备好训练模型,还需要两个额外的转换(将分类变量转换为 1 和 0 的数字格式,以最终对它们进行规范化)。根据我的经验,我无法在 Tableau Prep 中本地应用这两种转换(或者不需要很多额外的步骤),但是我能够集成一个定制的 Python 脚本来满足我的需要。
在与 Python 的集成中(尽管它也可以与 R 集成),我发现了 Tableau Prep 的最大缺点,也是它真正不足的地方。
首先,对于集成来说,有必要使用配置相当简单的 TabPy 库,可以在这里找到一个博客,它非常有助于理解如何将 Python/R 集成到 Tableau Prep。
[## 在 Tableau 准备流程中引入对定制 R 和 Python 脚本的支持
在本文的示例中,Anton 解释了如何配置 Prep Builder 来运行 Python 脚本,并展示了一些…
www.tableau.com](https://www.tableau.com/about/blog/2019/8/introducing-support-custom-r-and-python-scripts-tableau-prep-flows)
基本上,这是必要的脚本有一个主要功能,将通过参数接收熊猫数据帧,我们必须在它的所有转换。我们将在 Tableau Prep UI 中链接此函数,只需键入其名称。
然而,真正的缺点是,我们必须强制包含 get_output_schema() 函数,该函数将向 Tableau Prep 指示我们的数据集在脚本末尾的结构。这样做的问题是,我们必须几乎手动地创建结构,如果我们的数据集有许多列(超过 20 列就已经不可行了),这将是一项非常繁琐的任务。
为了说明我所说的,我添加了必要的脚本来转换分类变量:
为了应用标准化:
在将这些脚本包含到流中之后,我能够满足我的需求。我甚至可以在任何时候分割流,以生成独立字段 X 的矩阵和包含因变量 y 的列向量。
Final part of the flow
结论
在这个实验结束时,我能得出的最后结论是,尽管 Tableau Prep 对公众开放的时间很短,但它将能够简化许多常见的和一些更复杂的过程,这些过程通常在 ETL 或数据科学项目中完成。具体来说,对于这个项目,在需要使用外部脚本之前,只需很少的努力就可以复制 100 多行代码。
总的来说,Tableau Prep 给人留下了非常好的感觉,它绝对是一个值得在一些行业项目中获得机会的工具。显然,它在什么时候暴露了它的容量不足,以及在使用中可能出现什么问题。
作为最后一个优势,强调将流复制到具有相同结构的数据源是多么简单是很重要的。例如,在 Python 中,如果您想要对测试数据集应用相同的转换,您将需要复制代码并调整变量的名称。在 Tableau Prep 中,简单的复制和粘贴会产生相同的结果。
此外,有趣的是,流可以保存为打包格式,其中包括在任何其他使用 Tableau Prep 的计算机上复制流所需的脚本和文件。
简而言之,这绝对是一个我推荐使用并给它一个机会的工具,我个人很高兴看到它的下一个版本中包含的新特性。
我是谁?
我叫丹尼尔·马丁内斯,是哥伦比亚波哥大 Bera 集团 SAS 公司 Tableau 的首席商务智能顾问。我对机器学习和数据科学充满热情,目前我拥有 Tableau 桌面专家认证,但作为一名顾问,我有大约一年的时间从事仪表板的构建、使用 Tableau 的 BI 解决方案的架构以及 Tableau 服务器的管理。
如果你想联系我,你可以发邮件到我的邮箱【daniel.martinez@bera-group.com 或者在 LinkedIn 上联系我。
[## 丹尼尔·马丁内斯·比洛斯托茨基——Tableau 顾问——Bera Group SAS | LinkedIn
我是诺特大学最后一个学期的本科生,学习数据科学、机器学习…
www.linkedin.com](https://www.linkedin.com/in/daniel-martinez-bielos/)
也可以在我的个人博客中读到更多关于我其他项目的信息。
sites.google.com](https://sites.google.com/view/danielmartinezbielos)
如果你想更多地了解我们在 Bera Group SAS 的工作,你可以查阅用西班牙语和 T2 语编写的服务手册。
变装者:为变装女王训练一个变装者
用逼真的化妆改变任何自拍
如今,变装皇后在越来越多的人群中变得流行起来。他们在 LGBTQ 群体中也发挥着强大和鼓舞人心的作用。他们通常以出色的表演技巧和奢华的妆容吸引观众。
虽然大多数变装皇后幽默的主持风格和精彩的舞蹈动作很难模仿,但他们复杂的妆容总是更让我惊讶。平均来说,一个专业的变装皇后需要大约 1.5-3 个小时来完成她的化妆。打造拖妆所需的技巧之多,丝毫不亚于油画。我一直在想,如果我和我的朋友们化上男扮女装的妆,他们会是什么样子。
因为我不认识任何化妆师,也负担不起几百美元的预算,所以我求助于机器学习来解决问题。
最近我一直在研究 GANs(生成对抗网络)。它们是一种使用深度学习方法(如卷积神经网络(CNN))进行生成建模的方法。GANs 是一个激动人心且快速变化的领域。最先进的 GANs 能够在一系列问题领域生成逼真的图像,最显著的是在图像到图像的翻译任务中,例如将夏天的照片翻译成冬天的照片或白天到夜晚的照片,以及生成甚至人类都无法辨别的物体、场景和人物的逼真照片。因此,他们是这个项目的完美模特,理想情况下,他们可以将任何正面清晰的照片转换为完整的化妆。如果你有兴趣了解更多关于 GANs 的知识,这里的是一篇很棒的文章,会很有帮助。
项目计划(有我的时间预估):
- Web Scrape 拖动皇后和常规自拍图像资产(1 天)
- 研究发表的论文并实施最先进的 GAN 算法(1 周)
- 为模型培训准备项目数据集(1 天)
- 对 GAN 进行项目数据集培训(2 周)
1。网页抓取拖动女王和普通自拍图像资产
为了收集质量好的变装皇后照片和普通自拍照片,我最初的想法是从谷歌图片搜索中刮照片。我写了一个快速的 python scraper 来从 Google 图片搜索中收集拖拉女王的照片。我的目标是 3000 张照片。然而,谷歌图片搜索上的许多图片都有断开的链接,并且在点击通过一定数量的搜索页面后算法停止。我最终只有大约 700 张照片。真扫兴。
一天,当我因为轻微的社交媒体成瘾而滚动浏览我的 Instagram feed 时(2019 问题,我说得对吗?),我突然想到,如果我想要大量高质量的图像数据,在当今世界没有比 Instagram 更好的地方了。因此,我创建了另一个 python scraper,并从 Instagram 图片搜索中收集了使用“变装女王”和“自拍”等标签的变装女王照片和普通自拍。这一次,我在 10 分钟内获得了超过 3000 张变装皇后和普通照片。
2。发表论文研究并实现最先进的 GAN 算法
我在 CNN 和深度学习方面有一些经验,我参加过一个基于 GAN 的 Kaggle 竞赛,它要求参与者使用 GAN 生成小狗照片。为了那场比赛,我训练了 DC 甘、WGAN GP 和 CGAN。训练这些模型并不容易,我一直在努力解决不收敛和生成器梯度减小等问题。
这一次,我决定在开始创建我的模型之前做更多的研究。我最近读了很多关于最先进的 GANs 的论文,特别是那些在涉及人脸的图像翻译任务中取得成功的 GANs。最终, Junho Kim 等人关于 U-GAT-IT 的一篇论文引起了我的注意。他们的 GAN 模型把女生的自拍转化成了具有视觉感染力效果的动漫形象,坦白说和我的项目目标(从自拍到拖)挺像的。
在详细阅读了这篇论文和他们的代码后,我决定尝试实现他们的模型,原因如下:
1.他们论文中最突出的特点是,他们提出了一个自适应层实例归一化(AdaLIN)函数,以自适应地选择实例归一化(IN)和层归一化(LN)之间的适当比例,而不是只使用其中一个。在 GAN 训练期间,如果仅使用 IN,则由于逐通道归一化特征统计,源域的特征将被很好地保留。然而,目标域的翻译量可能不够,因为实例规范化可能无法捕获全局样式。另一方面,如果仅使用 LN,则目标领域风格可能由于逐层标准化的特征统计而被很好地转移。但是源域的特征可能保存得不太好。通过根据源和目标域分布自适应地选择 IN 和 LN 的比率,该模型可以产生更具视觉吸引力的结果。
文章用一个例子说明了这一理论。下图是来自 selfie2anime 模型的对比。(a)中的图像是源文件。(b)使用 AdaLIN 翻译图像。©仅使用实例规范化来翻译图像。(d)仅使用图层规范化转换图像。显而易见,( b)具有最吸引人的视觉效果。©很好地保留了源图像的细节,如颧骨和面部表情,但没有很好地与动画特征融合。(d)很好地捕捉了动画特征,但是没有从源文件翻译足够的细节。
https://arxiv.org/pdf/1907.10830.pdf
2.本文没有使用基本的最小最大损失函数,而是实现了 4 种不同的损失函数:对抗性损失、循环损失、同一性损失和 CAM 损失。
对抗性损失试图将生成的图像与目标分布相匹配。循环损耗对发电机施加循环一致性约束。它要求生成器将源图像翻译到目标域,然后成功地翻译回源域。这有助于解决模式崩溃问题,这是许多 GAN 模型训练中的一大难题。身份损失有助于确保源图像和目标图像的颜色分布相似。如果在生成器中使用来自目标域的图像来翻译该图像,则该图像不应改变。CAM 损耗有助于发生器和鉴别器了解在这两个域中何处需要改进。
报纸上还有很多我喜欢的其他功能。由于这篇博客的篇幅有限,更多细节请参考论文。
3。为模型训练准备项目数据集
在开始构建模型之前,关键的一步是以正确的格式准备数据。我收集了 3000 张变装皇后的照片和 3000 张自拍,但并不是所有的照片都有清晰的正面,也不是所有的照片都被裁剪得恰到好处,只展示了面部区域。为了准备数据,我利用 Open-CV 的级联分类器,编写了一个 python 脚本来识别数据集中的人脸,并对它们进行裁剪,使人脸位于中间。后来,我手动整理了我的数据集,剔除了质量不好的照片。最后我有了 1000 张拖照和 1000 张自拍。
Training set sample
当我收集普通自拍时,最初我只打算包括男性自拍,因为大多数传统的男扮女装者都是由男性转变而来的。然而,我注意到在我的数据集中有大量女性变形的男扮女装照片。因此,我也在我的数据集中加入了女性自拍。将来探索不同性别对模型结果的影响会很有趣。
另一件引起我注意的事情是,大多数男扮女装的照片都有假发,尽管我试图在最终的照片中只包括脸部。而很多自拍,尤其是男性自拍,都是短发。假发最终在我的模特训练中扮演了一个有趣的角色。我将在第 4 节进一步讨论它。
4。在项目数据集上训练 GAN
4.1 宏伟设计
与任何其他 GAN 类似,我的模型有一个生成器和鉴别器。生成器由编码器、解码器和辅助分类器组成。发电机的设计如下图所示:
https://arxiv.org/pdf/1907.10830.pdf
训练辅助分类器来学习每层特征图的权重,然后用于计算一组领域特定注意特征图。然后,解码器的残差块(AdaLIN)中的参数可以通过全连接层从注意力图中动态计算。
同样,鉴别器也有一个编码器,一个辅助分类器。然而,鉴别器具有分类图像是来自目标域还是由模型生成的分类器,而不是具有带有残余块的解码器。
https://arxiv.org/pdf/1907.10830.pdf
4.1 支付或不支付
大家都喜欢免费的资源,尤其是免费的 GPU。训练机器学习模型可能会很快变得昂贵。尽管 GCP 和 AWS 提供相对便宜的 GPU,但训练一个模型几天就要花费 100 多美元。甘人以训练时间长著称。
幸运的是,我有一个相对较小的数据集。通过在 Google Drive 上安装我的数据集,我能够完全在 Google Colab 上训练我的模型。如果你对机器学习感兴趣,但受限于计算能力,我推荐你去看看 Google Colab。Google Colab 的另一个优点是,你可以从任何机器上登录它。如果你像我一样每天都很忙,它会给你很大的灵活性。
对于这个特定的模型,每次迭代转换一个图像并产生一组发生器和鉴别器损耗值。我的目标是为 300,000 次迭代训练模型,并在此过程中监控结果。在 Colab 上,训练一次迭代大约需要 3 秒钟,这意味着我将不间断地花费大约 250 小时来达到 300,000 次迭代。呃。
但是现在还不要沮丧。有一个巧妙的技巧实际上救了我的命。在 Pytorch 中,下面的代码可以大大提高训练效率:
torch . backends . cud nn . benchmark = True
该标志启用 cudnn 中的基准模式。在基准模式下,如果网络中的输入大小没有变化,cudnn 将为该特定配置寻找一组最佳算法(这需要一些时间)。这通常会导致更快的运行时间,在我的例子中,这将每次迭代的运行时间减少到了 1 秒。耶!
不过,对这一招要半信半疑。如果输入大小在每次迭代中都发生变化,那么 cudnn 将在每次出现新的大小时进行基准测试,这可能会导致运行时性能下降。
4.2 细节决定成败
当我监控模型结果时,我很快意识到一些自拍转换比其他的更糟糕。深入挖掘,问题就显现出来了。这些自拍要么戴着眼镜,要么脸部附近有障碍物。我决定立即停止训练,因为我从经验中了解到,在机器学习中,没有什么比高质量的数据集更重要,无论你的算法有多天才。因此,我仔细地重新扫描了我的数据集,删除了任何不清晰、有眼镜或有障碍物遮挡面部的照片。我还附上了一些额外的照片。
4.3 大损失,小损失
为了确保我的模型训练正确,我收集了每次迭代的发电机损耗和鉴频器损耗,并绘制了它们的趋势。下图显示了直到 295,000 次迭代的对数变换损失值图表。
鉴别器损失相对一致且较小,这意味着鉴别器总是善于分辨哪些拖动图像是真实的,哪些是模型生成的。发电机损耗从大值开始,然后随着训练的进行逐渐降低。在整个训练过程中,发生器和鉴别器损耗值都有恒定的波动性。这种易变性是意料之中的,因为每次迭代只包含一张照片,并且翻译的难度会根据光线、角度和图像质量等条件而有很大变化。因此,不同的输入图像可能具有非常不同的损失值。
4.4 没有照片,就没有真相
跟踪训练进度的另一种方法是简单地查看结果。让我们看看两张示例照片的结果进展。
Original
5,000 iterations after
15,000 iterations after
100,000 iterations after
130,000 iterations after
180,000 iterations after
240,000 iterations after
看起来没那么糟吧!随着训练的进行,模特似乎捕捉到了更突出的化妆特征:大烟熏妆,彩色而光滑的嘴唇,鼻子上的高光,脸颊上的阴影。从 100,000 次迭代开始,翻译的照片已经看起来像专业的拖动图像。随着训练的深入,化妆越来越突出。正是我想要的!
4.5 好的记忆可能不好
就在我准备坐下来放松一下,进一步观看火车模型时,我注意到在翻译图像的面部附近有一些随机的笔画。一开始我以为是因为输入图像质量的问题,但是情况越来越糟。在 130,000 次迭代时,我只能看到一些可能被误认为阴影的光线。在 240,000 次迭代后,它们变成了清晰的波浪形图案,围绕在脸部周围。然后我意识到,这些波浪状的图案实际上是从模型中生成的假发。由于大多数变装照片都有色彩鲜艳的假发,模特也应该逐渐学会在照片上戴假发。
然而,随着模特开始越来越关注假发,翻译后的波浪状图案使照片背景看起来扭曲,视觉吸引力下降。
这可能是阿达林的结果。AdaLIN 帮助模型成功地保留了大量来自源域的面部特征,并与来自目标域的化妆特征融合。然而,对于头发,模型可能应该尽可能多地保留原始特征,以使图像看起来更真实。对于未来的开发,我可以修改模型,以便 AdaLIN 应用实例与图层归一化的比率,该比率随图像上的位置而变化(面部区域使用更多的实例归一化,外部区域使用更多的图层归一化)
表演时间到了!
除了翻译我和我朋友的照片,我还和我的模特一起翻译了一些名人的照片。你能猜出以下哪些名人发布了他们精彩的变装吗?请在你的猜测下面评论他们的名字!(或者更好,也评论一下你认为他们的拖名会是什么!😄)
a
b
c
d
后续步骤和最终反思
总的来说,我花了四周时间完成这个项目,比最初估计的时间多了一周。我从这个项目中学到了很多,总体上对结果很满意。回想起来,这些是我学到的最有价值的东西:
- 确实,数据科学家只花 20%的时间在实际数据分析上,80%的时间在寻找、清理和重组数据上。对于这个项目,收集和转换数据的时间比我想象的要长得多。数据集的质量直接关系到结果的质量。在项目开始的时候,我很想直接投入我的数据来训练模型。由于低质量的训练数据,我不得不重新清理我的数据,并从零开始重新训练我的模型,这给了我很大的教训。
- 使用各种指标来帮助监控培训过程,包括损失值、分数和目视检查。特别是对于像 GANs 这样的无监督学习任务,您需要选择最适合项目目的的度量标准。
- 尽可能利用免费资源。Google Colab 和 Kaggle 都为机器学习项目提供免费的 GPU。我个人更喜欢 Colab,因为你可以在 Colab 中直接安装你的 Google Drive。它在模型训练和结果保留方面提供了很大的灵活性。
- 深入了解模型架构。训练机器学习模型是复杂的任务。理解每个模型组件的特征和功能对于理解模型训练行为是必不可少的。
正如我在 4.5 节中提到的,我的下一步是研究如何改善翻译的假发外观。我可能还会收集更多的数据,因为当前的训练集大小仅为每个类 1000 个。我很想知道更多的数据是否会给模型带来更真实的结果。
这个项目的代码可以在我的 GitHub 上找到。看看吧!
如果你喜欢这个,请随时在媒体上关注我,或者在LinkedIn&Twitter上联系我!
在帕洛阿尔托路上的物体周围画一百万个方框
为自动驾驶汽车比赛的 Kaggle Lyft 3D 对象检测构建语义点云
介绍
这篇文章详细介绍了我和 Stefano Giomo 参加最近的 Kaggle Lyft 自动驾驶汽车 3D 物体检测比赛时使用的方法(https://www . ka ggle . com/c/3D-object-detection-for-autonomous-vehicles)。
这场比赛使用了 Lyft 车辆捕获的数据,这些车辆配备了多个摄像头和激光雷达传感器。这些车辆在帕洛阿尔托的道路上拍摄了数百个 20 秒的场景。竞赛的目的是围绕这些场景中不同类别的对象放置 3D 包围盒。
我们在数据的 2D 鸟瞰图表示上训练了一个 UNet 模型。2D 表示是通过一系列预处理步骤创建的,这些预处理步骤将每个激光雷达点云与来自相机和帕洛阿尔托街道地图的语义信息相结合。
为了将 2D 预测从模型转换到 3D 包围体,我们执行了许多后处理步骤,包括计算机视觉技术,以及使用激光雷达点云构建地形图。
在获得铜牌的过程中,我们面临了许多挑战。这次比赛的数据集很大,大约 120Gb,存储和处理数据需要很长时间。学习在不同的参考坐标系之间正确转换数据是复杂的,需要小心谨慎。
决定如何组合激光雷达数据和多个相机图像,以及如何将数据转换为神经网络的输入也具有挑战性。在应对这些挑战的过程中,我们学到了很多,并开发了一些有趣的方法,我们认为值得分享。
在这篇文章中,我们总结了我们如何执行预处理步骤来组合不同的数据类型,我们如何创建输入和训练我们的模型,以及我们如何对我们的预测进行后处理以弥补我们模型的一些限制。
数据集
Sensor data from a scene in the Lyft Level 5 data set
比赛数据取自 Lyft 的 Level 5 数据集(【https://level5.lyft.com/】)遵循 nuScenes 数据格式(【https://www.nuscenes.org/】)。数据模式中每个实体的元数据都用 JSON 表示。
Lyft 构建了一个 SDK 来促进数据结构的工作:它允许用户在场景中导航,操纵盒子,可视化传感器数据等等。SDK 的存储库在这里(https://github.com/lyft/nuscenes-devkit ),可以作为 pip 包安装。
场景、样本和传感器
The Lyft vehicle and its sensors
数据由 20 秒长的场景组成。每个场景都由许多样本组成。反过来,样本由同时采集的来自多个传感器的数据组成。每个场景由大约 125 个样本组成。与每个样本相关联的是关于车辆相对于场景的位置以及传感器相对于车辆的位置的信息。该信息允许数据点被合成并正确地放置在场景中。所有车辆都至少有一个激光雷达传感器和六个摄像头。
训练集具有由每个样本中所有感兴趣对象周围的包围体组成的注释。感兴趣的对象分为九类。从柱状图中可以看出,这些类非常不平衡(注意对数刻度)。有 50 多万辆汽车和不到 200 辆急救车辆。
包围体是相对于场景的坐标系定义的。它们平行于 XY 平面,并由七个值指定:中心的三个坐标;体积的三维以及围绕垂直轴的旋转角度(偏航)。
Sample from the street map
还提供了一张源自 OpenStreetMap(https://www.openstreetmap.org/)的地图,覆盖的区域包括数据中的所有场景。
训练和测试数据集
训练和测试数据集总共包含超过 350,000 幅图像和近 60,000 个点云。这些数据集总共使用了 120 千兆字节的磁盘空间!
任务和评价标准
这组测试场景没有注释,要求竞争者预测类别,以及场景中每个感兴趣对象周围的包围体(七个值)。
使用平均精度指标对预测进行评分。预测的平均 精度是针对联合(IOU)阈值的交集范围计算的,并且度量是作为这些平均 精度的 M ean 计算的。
平均精度的计算如下。例如,假设 IOU 阈值为 0.6,所有与地面真实量重叠至少 60%的量被认为是命中,而其他的被认为是误报。然后,该阈值的平均精度被计算为命中数除以预测数量。阈值 0.5、0.55、…、0.9、0.95 用于计算平均精度的平均值。
分割相机图像
Forward facing cameras with semantic segmentation masks
为了利用相机图像中包含的信息,我们创建了每个图像的语义分段,以将语义类与激光雷达点云中的点相关联。我们这样做是为了给我们的网络提供关于每个激光雷达点反射的对象的信息。
我们使用了麻省理工学院计算机科学和人工智能实验室发布的预训练 PyTorch 网络(https://github . com/CSAILVision/semantic-segmentation-py torch)。该模型已在 ADE20K 场景解析数据集(http://groups.csail.mit.edu/vision/datasets/ADE20K/)上进行训练,该数据集包含大量场景,包括美国公共道路的场景,以及 150 类对象的分割基础事实。
我们对测试集和训练集中的每幅图像进行推理,并将结果保存为 GIF 图像以节省磁盘空间。在 150 个类别中,我们根据它们的频率和与竞争的相关性选择了 24 个类别的子集。例如,我们排除了像沙发和床这样的家庭用品的类别。我们选择了 24 个类来减少表示分类信息所需的嵌入大小。
将类投影到点云
Lyft SDK 提供了在摄像机图像上可视化点云的功能。我们从这段代码中获得灵感来进行相反的操作:将图像和语义数据映射到点云中的点上。
Associating LIDAR points with pixels in a camera image (drawing by Stefano Giomo)
该图显示了我们如何将相机图像和语义图投影到点云中的相关点上。例如,P 是激光雷达点云上的一个点。我们沿着将 P 连接到激光雷达传感器的线将 P 投影到相机图像中,找到 2D 点 Q。我们使用 Q 对语义分割遮罩进行采样,并找到点 P 的语义类
A camera image and semantic classes projected onto the corresponding points in the point cloud
合并来自多个传感器的语义和相机数据
我们为每个样本中的每个图像投影分割类和相机图像数据。结合所有的投影,我们有一个语义点云,其中每个点都有一个相关的语义类标签。
来自相邻摄像机的图像在边缘重叠。对于重叠区域中的激光雷达点,我们定义了一个规则来根据类与属性域的相关性选择最终标注。
Multiple camera images and segmentation classes projected onto the entire LIDAR cloud
上面的可视化显示了以球坐标表示的整个语义点云,其中 x 轴是方位角,y 轴是连接该点和激光雷达传感器的射线的仰角。
鸟瞰模型
我们研究了各种可能的网络架构,包括具有 3D 卷积和区域提议网络的架构。这些模型复杂且计算量大,为了确保我们能够及时提交,我们做出了务实的决定,选择使用 Lyft 团队提供的参考模型作为起点(https://github . com/Lyft/nu scenes-dev kit/blob/master/notebooks/Reference % 20 model . ipynb)。
参考模型基于自上而下(或鸟瞰)的激光雷达点云。想法是将点云体素化,将 Z 维度划分为三个区域,产生三通道 2D 表示。然后,每个体素中的归一化点数被用作像素强度,第一通道被分配给最低 Z 区域,第三通道被分配给最高 Z 区域。鸟瞰图可以像 RGB 图像一样处理。
Original Birds Eye View model input and target data
该模型预测输入图像的每个像素属于十个类别(九个输出类别和一个背景类别)之一的概率。
Example predictions for the class ‘car’
改进鸟瞰模型
Our data preprocessing pipeline to create inputs to the neural network (drawing by Stefano Giomo)
受 PointPillars 论文(【https://arxiv.org/abs/1812.05784】T2)的启发,我们通过使用多种渠道向模型提供更多信息来扩展这个参考模型。
具体来说,我们添加了来自街道地图和语义点云的信息。为了包含语义信息,我们在 Rossmann Store Sales Kaggle 竞赛(【https://arxiv.org/pdf/1604.06737.pdf】T4)的实体嵌入纸的样式中使用了 5 维嵌入。
我们修改了参考模型中使用的 PyTorch UNet 实现,并使用杰瑞米·霍华德的 fastai 库训练它。
Training data for our model
后处理
生成 2D 矩形
一旦我们完成了对测试集的推断,我们就按照参考模型中使用的方法将我们的预测转化为 2D 包围盒。该方法首先对预测进行阈值处理,以获得每个类别的二进制掩码。然后进行侵蚀,接着进行扩张(称为打开)。这消除了二进制掩码中的小伪影。我们找到了每个二进制掩码的轮廓和这些轮廓的最小外接矩形。这些包围矩形是我们包围体的基础。
An example of predictions and extracted contours for class ‘car’
在这个过程完成后,我们有了指定包围体的七个参数中的五个。我们已经计算了 x 和 y 的位置和比例,以及 xy 平面上的旋转(或偏航)。我们仍然需要 z 位置和比例(即海拔和高度)。
高度和标高
参考模型使用了一些简化假设,以便将预测的 2D 盒转换成 3D 体积。第一个假设是特定类别的所有对象都具有相同的高度,具体来说,就是训练数据中该类别的平均高度。第二个假设是所有的物体都与装有传感器的车辆(自我车辆)处于相同的高度。
我们试图通过对激光雷达点云进行后处理来改善这两种假设。在这里,我们详细说明了我们是如何改进仰角假设的。
构建地形高程图
Point cloud a scene with a hill, the spikes are roadside trees
我们推断,我们应该能够使用激光雷达点云中的最小高程来找到场景的地面高程。我们将每个场景的所有 125 个可用点云转换到同一个参考坐标系,并将它们合并以构建整个场景的点云视图。我们使用这个组合的点云来构建场景的最小高度或地形图。
Minimum elevation map for a scene with a hill
我们面临的一个问题是,场景中的任何物体,如车辆或行人,都会导致对地面高度的高估。我们通过在空间和时间中取最小高度来避免这个问题。通过在一个物体周围的空间区域中取最小值,我们发现在物体旁边的地面上有更低的点。通过在不同的时间点取最小值,我们找到了任何移动物体离开后的地面。
上面的点云是从包含一座小山的训练集中的场景中获取的。下面的视频显示了这个场景的摄像机和注释。
Youtube video showing a scene with a hill.
在场景开始时,车辆正在下山,后面跟着几辆车。下面的动画对比了放置在与 ego 车辆相同高度的包围体和放置在我们创建的地形地图上的包围体。
Bounding volumes of vehicle on a hill placed at ego vehicle elevation vs at terrain elevation
最初,当车辆下降时,与自我车辆处于同一水平的包围体明显比放置在地形图上的包围体差。然而,随着道路表面变平,差异消失,因此这种技术提高了具有山丘或道路高程中的其他差异的场景的分数。
结论
我们为这次比赛花费的大部分精力都放在输入数据的预处理和预测的后处理上。我们对参考模型进行了两项关键改进。通过将来自相机图像的语义信息投影到点云中,我们将关于对象类别的信息传递给我们的模型。其次,通过构建地形图,我们提高了预测体积的高程精度。
和大多数 Kaggle 竞赛一样,我们在用完创意之前就用完了时间。我们尝试在多个连续样本(扫描)上训练我们的网络,但不太成功,但如果给我们更多时间,我们确信这条路线会给我们带来提升。后来我们意识到,我们应该尝试使用更高分辨率的鸟瞰图像。
如果我们有更多的时间,我们会尝试使用更先进的端到端方法,更好地表示点云和区域建议网络,以获得更好的包围体。我们希望用语义点来扩展 PointPillars 网络,我们也希望在球形映射的语义点云上训练一个模型。
我们将很快为此项目发布我们的 GitHub 资源库。我们正在撰写更多的文章,这些文章将更详细地介绍预处理、后处理和训练网络背后的更多技术元素。
感谢 Stefano Giomo 的精彩画作!感谢你的阅读!
如果你想看看我们从数据集生成的更酷的视频,以及我们的语义分段遮罩,这里有一个 youtube 播放列表:
Playlist of youtube videos generated from the Lyft Level 5 data
绘制架构:在 Pytorch 中构建深度卷积 GAN
我不能创造的,我不理解。
理查德·费曼
费曼并没有创造甘的无监督学习或对抗训练,但通过这句话,他确实证明了智力和理解事物的能力不仅仅是一项有监督的、有鉴别能力的任务。为了理解某样东西,你必须做的不仅仅是根据你已经看过一百万次的类似东西给它贴上标签——为了理解你正在看的东西,你必须能够再现它。在深度学习中,创造能力是一般敌对网络与其前辈不同的地方。GAN 是生成输出的生成模型;这背离了标记输入的判别模型。这使得他们成为深度学习和人工智能领域一股强大的范式转变力量,值得严乐存和其他深度学习之父给予的大肆宣传。GAN 的潜力超过了辨别网络,因为 GAN 使用深度学习来合成信息并从中创造出一些新奇的东西。正如费曼所说,这是最有影响力的理解形式。
在这篇文章中,我将介绍如何使用深度卷积 GAN 来创建建筑(建筑物的外部)。我想体验使用和创建不是自动内置的数据集——如 Imagnet 和 MINST——并对其进行微调以创建逼真的外观。我使用的数据集是这里的。我还通过从网上抓取额外的图片来扩充数据集。
什么是 GAN:
一般的对抗性网络是两个相互竞争的神经网络,以产生与输入非常相似的输出。这两个网络——生成器和鉴别器——扮演着对立的角色。生成器网络基于输入图像从随机噪声创建新图像。随机噪声从不相干像素演变成相干图像,其中具有可辨别的形式,这是因为鉴别器告诉它如何改变。鉴别器网络确定图像是真的还是假的。GAN 的目标是使发生器图像与真实图像非常相似,从而能够欺骗鉴别器认为所产生的图像是真实的。GANs 最重要的特征之一是,在 GANs 中实现的神经网络使用的参数数量远远小于用于训练它们的数据量。这迫使模型学习并内化数据中最重要的特征,以便模型可以生成它们。
Vanila GAN Architecture
为了更好地理解这一点,让我们看看 Ian Goodfellow 和他的同事在 2014 年发表原始论文时使用的类比。生成器就像一队伪造者试图创造一个与真画相匹配的输出(输入),而鉴别器就像一队侦探试图确定真实图像和假图像之间的差异。对于算法的每次迭代,生成器永远不会看到原始输入,而是看到潜在的随机变量(基于真实输入图像的视觉噪声)和鉴别器的判断。这个生成器就像一个盲人伪造者,试图通过给她颜料来重现蒙娜丽莎,然后侦探告诉她如何使用它。伪造者画的画在每次迭代后看起来越来越像蒙娜丽莎。
深度卷积 GAN (DCGAN)
普通 GAN 架构功能强大,但这种架构不允许任何真正的空间推理,因为它依赖前馈神经网络而不是卷积神经网络来从图像中提取特征。前馈神经网络不能推理诸如尖锐边缘和突出曲线之类的特征,因为它们不保持图像的空间结构。卷积层保留了图像的空间结构,这意味着将从图像中提取最准确、最详细的特征。这为生成器和鉴别器提供了关于它们将生成的输出以及关于如何区分真实图像中的特征和伪图像中的特征的更高级的空间推理能力。所提取的特征的增强质量通常是在处理图像时使用 DCGAN 的原因。
生成器和鉴别器都将是卷积神经网络。鉴别器有一个普通的 CNN 架构,因为它执行的是鉴别、监督的图像分类任务。生成器将具有如下修改的卷积架构:
- 池化被卷积步幅所取代。这允许网络学习它自己的空间下采样(改变输入的大小)。不需要事先设置。
- CNN 的结尾没有完全连接的层。生成器不是分类器,因此不需要这一部分。
- 除了生成器的输出图层和鉴别器的输入图层之外,批处理规范化用于每个图层。这通过标准化激活(先前层具有零均值和单位方差)和梯度的流动来稳定训练过程。在的论文中,结果表明发生器中的输出层和鉴别器中的输入层导致模型不稳定,这就是为什么在那些层中不使用批范数。
- 在生成器中使用 ReLU,但使用 tanh 的输出除外。双曲正切函数的对称性允许模型更快地学习饱和并覆盖训练分布的颜色空间。
发电机:
发生器接收随机噪声作为输入,并对鉴频器的输出进行采样,以产生图像。这个过程可以被认为是将噪声映射到输入数据分布,以便产生图像。生成器架构是一个经过修改的卷积神经网络,由 ConvTranspose2d 层组成,这些层具有可学习的参数,其余架构如上所述。
nn.Sequential(
nn.ConvTranspose2d( nz, ngf * 8, 4, 1, 0, bias=False),
nn.BatchNorm2d(ngf * 8),
nn.ReLU(True),
nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1, bias=False),
nn.BatchNorm2d(ngf * 4),
nn.ReLU(True),
nn.ConvTranspose2d( ngf * 4, ngf * 2, 4, 2, 1, bias=False),
nn.BatchNorm2d(ngf * 2),
nn.ReLU(True),
nn.ConvTranspose2d( ngf * 2, ngf, 4, 2, 1, bias=False),
nn.BatchNorm2d(ngf),
nn.ReLU(True),
nn.ConvTranspose2d( ngf, nc, 4, 2, 1, bias=False),
nn.Tanh()
)
随机噪声:
Random Noise
在生成式学习中,机器试图从复杂的概率分布(输入)中生成新的输出。在深度学习中,同样的想法被建模为神经网络——在我们的情况下是卷积神经网络——它将简单的随机变量作为输入,并将遵循目标分布的随机变量作为输出。神经网络能够导出输入概率分布中的等级关系。在这种情况下,这种关系,即随机噪声,是一组类似于真实图像的像素。
在训练循环的第一次迭代中,随机噪声被发送到鉴别器,它确定噪声与真实图像的相似程度。然后,一旦鉴别器告诉它与原始输入的偏差有多大,生成器就会获取该信息并调整噪声,直到它变成类似输入的图像。因此,发电机不会直接与输入一起工作。生成器间接地学习如何将噪声转换成看起来像输入的东西。
fixed_noise = torch.randn(64, nz, 1, 1, device=device)
2D 转置图层
发生器层由 ConvTranspose2d 层组成。这些层将对噪声向量进行上采样,从而将噪声转换为图像。这与反卷积或卷积层不是一回事。卷积层寻求提取越来越小的特征,这些特征稍后将被分类。去卷积层寻求反转卷积层的操作。
2D Transpose Layer
从最简单的意义上说,转置导致至少两个事物相互交换位置。噪声向量和图像空间将彼此交换位置。这意味着我们正在改变它们的维数顺序,因此,我们将交换矩阵中相对于对角线的值。此过程会对最终输出(图像)的细节进行上采样(放大和填充)。这是发生器“绘制”实际图像的部分。
从高层次来看,如果你看架构,这更有意义。我们从噪声矢量开始。这个向量不是图像。噪声是图像的压缩版本。发生器中的 2D 转置层将解压缩噪声,因此它可以成为一个 8x8 的图像,所有细节都在图像的正确位置。这是发电机的最终产品。
2D Transpose Layer
鉴别器
鉴别器得到真实和伪造的图像,它的工作是将它们分类为真实或伪造的。
nn.Sequential(
nn.Conv2d(nc, ndf, 4, 2, 1, bias=False),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(ndf, ndf * 2, 4, 2, 1, bias=False),
nn.BatchNorm2d(ndf * 2),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, bias=False),
nn.BatchNorm2d(ndf * 4),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 1, bias=False),
nn.BatchNorm2d(ndf * 8),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(ndf * 8, 1, 4, 1, 0, bias=False),
nn.Sigmoid()
)
体系结构
鉴别器是具有上述结构的卷积神经网络。这为鉴别器提供了空间推理能力,它需要学习哪些精确的空间保留特征使图像真实,然后使用这些空间保留特征将图像分类为真实或虚假。鉴别器不使用与生成器相同的 2D 转置图层,因为鉴别器执行的是监督任务-隔离和提取要素-而不是生成任务-对要在影像中创建的要素进行上采样。
间接培训
鉴别器将向生成器提供信息,因此它将学习如何创建鉴别器在真实图像中发现的真实特征。最终,我们希望生成的图像非常好,以至于鉴别器无法区分真实图像和虚假图像。这意味着我们需要间接训练 DCGAN。DCGAN 中的发生器并不试图精确匹配输入的概率分布,而是该发生器正在创建一个概率分布,它可以欺骗鉴别器网络,使其认为发生器的输出分布来自与真实图像相同的分布。
然后鉴别者会给一幅图像打分,显示它看起来有多像一幅真实或虚假的图像。在训练中,生成器希望让鉴别器给生成的图像打高分,这样鉴别器就会认为假图像是真的。该分数稍后用于学习过程,该学习过程允许生成器在生成图像方面变得更好,并且允许鉴别器在分类图像方面变得更好。
损失函数和学习过程
Training Process
鉴别器
鉴别器输出值 D,该值指示生成的图像与真实图像有多接近。目标是最大化鉴别器将真实图像识别为真实而将生成的图像识别为伪造的机会。交叉熵函数测量该过程的损失 p log(q)。该功能衡量分类模型的性能。这个函数特别适合这个任务,因为预测的概率偏离标签越远,损失就越大。
Discriminator Loss
真实的图像, p 等于 1 它是真实的最大几率。对于生成的图像,我们需要反转标签(减 1)以最小化它是 1 的机会。这是鉴别器的目标函数。
发电机
生成器的目标是创建图像来欺骗鉴别者。这意味着它的目标函数想要鼓励模型创建具有最高可能值 D 的图像来欺骗鉴别者。
Generator Loss
最小最大游戏
GAN 是一种极小极大游戏,其中 G 想要最小化 V 而 D 想要最大化 V。这是一种零和非合作游戏,你的对手想最小化他们的行动,而你想最大化他们的行动;双方都在最大化自己的收益。目标是最小化最大损失(最小化最坏情况)。
Nash Equilibrium source
MinMax 游戏来自博弈论。GANs 被设计成达到一个纳什均衡点,在这个点上每个参与者都不能在不改变其他参与者的参数的情况下降低他们的成本。当鉴别器和发生器达到纳什均衡时,GAN 收敛。这是上面最小最大方程的最优点。纳什均衡点意味着成本降低,没有什么可以改变。生成器能够创建欺骗鉴别器的图像。至此,两个目标都达到了。
有些函数不会收敛。这通常发生在非凸函数中。从博弈论的角度来说,当你的对手总是反对你的行动时,很难让你的模型收敛。这就是为什么甘的训练如此困难的原因。
输出
现在公布结果!最终,在 GPU 上进行了 500 次训练后,结果出来了。这是训练过程的 GIF 图片:
你可以看到从随机像素到像素化图像再到建筑图像的过程。
这些是我最喜欢的(精选的)图片:
I would live there!
并非所有生成的图像看起来都完美。有些有一个扭曲的,变色的,像素化的门面,只有母亲才能爱…
distorted, discolored pixelated images
结论
一般的对抗网络同时处理所有这些领域:无监督学习、博弈论和监督学习。这证明了费曼所说的,为了理解某事,你必须能够创造它。创造本质上是多维的——为了做出新的东西,你必须能够同时理解不止一个想法或功能的交互——当深度学习具有这种能力时,它的能力可能是无限的。这种力量显然伴随着前所未有的责任,但未来无监督学习和特别是生成模型,如甘的模型,具有重新定义深度学习和人工智能的巨大潜力。
点击这里查看整个项目!
[## emilyelia/建筑-DCGAN
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/emilyelia/Architecture-DCGAN)
像机器一样画画和其他人工智能实验
Photo by Ms Jemini Photos on Unsplash
你有没有想过“人工智能”这个术语背后隐藏着什么?使用谷歌实验准备的在线应用程序,可以看到“机器学习”给我们带来的可能性。总的来说,是一群设计师和工程师创造有趣的实验,作为引入这些基于谷歌技术的概念的一种方式。有些是可爱快捷的网页游戏,有些更先进,但所有这些都是为了让其他人更容易理解新技术。
1。快,画!
快,画!是一个类似于猜字游戏的网络游戏。不同的是,你不是和你的朋友玩,而是和电脑玩。你有 20 秒的时间根据显示的单词画一幅画,同时人工智能会根据你在屏幕上涂写的所有线条来猜测你在画什么。
Best onion drawing ever
在这个项目的开始,这个模型被故意训练得不完美,所以它会更有趣。用户做出的所有涂鸦都有助于创建一个充满绘画的描述良好的数据集。由于它们,用于建立这个模型的神经网络每天都在学习越来越多的东西。由超过 5000 万张图片组成的数据集在在线可用。它启发了许多研究人员建立新的模型,发表研究论文并进行各种分析,如你在画火烈鸟方面有多糟糕。下面你可以看到人们画长颈鹿的不同方式。
Example of giraffe drawings made by users
这是描述该网站的官方视频:
2。自动绘图
另一个绘图应用程序的例子是 AutoDraw——多亏了这个应用程序,你可以画出你生活中最好的插图。该网站的广告是“为每个人快速绘图”,这是相当准确的描述。你是否曾经计划为聚会制作一份传单,但你缺乏绘画技巧,结果总是使用预先制作的模板?
Here you can see my drawing skills again (or lack of them)
在这个网站上,你可以画出你想要的任何东西,计算机会尽力帮助你完成这项任务。给你的漫画添加更多的细节将会导致计算机方面更准确的预测,如果你对它满意,你可以简单地用 site 提出的图纸交换你的图纸。
AutoDraw 模型是在 Quick,Draw!但是剪贴画本身是由各种插图画家和设计工作室制作的。
AutoDraw 官方视频:
3.可示教机器
在这个网站上,你可以教电脑按照你想要的方式运行。使用您的摄像头,可教机器让您在浏览器中现场教机器,而不需要任何编码经验。这个实验让任何人都更容易开始探索机器学习是如何工作的。
那么,它是如何工作的呢?在屏幕上,你可以看到来自你的相机,三个大按钮和 gif 的饲料。来自相机的图片将成为神经网络算法学习的输入。在做不同的手势时,你必须使用“训练”按钮拍照。对于每一种姿势,你至少需要拍 30 张照片。这三类图片与输出相关联——第一个手势将启用第一个 gif,第二个和第三个是后续的。在训练完你的神经网络后,你可以马上测试它。显示与教导阶段相同的手势将导致输出的改变。
你也可以改变 gif 图片,让网站显示你挑选的图片。除了 gif,你可以得到的另一种类型的输出是声音和语音——你可以选择不是显示猫和兔子,而是与计算机进行对话。向算法提供你的数据和它学习正确分类的整个过程就是机器学习的全部内容。
建立在可教机器之上的应用之一是可教蛇。正如你可能会猜测,这是一个众所周知的蛇游戏,但控制器是你必须使用你的相机显示的迹象。拿一张纸,画一个箭头,告诉蛇它必须去哪里吃掉所有的点!
4.人工智能二重唱
在这个实验中,你可以和机器一起演奏钢琴二重奏。在这里,你可以释放你所有的音乐创造力,并尝试使用你的笔记本电脑键盘和人工智能一起演奏最好的即兴二重奏。A.I. Duet 是一个工具,它通过神经网络运行你弹奏的音符,并试图在这场冒险中陪伴你。神经网络已经用大量的旋律样本进行了训练。在传统的编程方法中,代码需要实现音符、键和定时之间所有可能的连接。人工智能二重奏模型从数据角度创造了旋律的所有规则,并将它们结合在一起,现在它可以生成与你的部分相匹配的全新曲调。
5.乔治·卡姆
这个实验结合了两大机器学习问题——图像识别和语音合成。它始于用户用手机里的相机给他们周围的东西拍照。Giorgio Cam 试图识别该对象,并对其发现的内容进行描述。这段描述后来被改编成了乔治奥·莫洛德尔创作的说唱歌曲的歌词。通过拍摄不同的东西,再加上你的手机,你可以成为一个说唱之王!
摘要
总的来说,上面所有的例子都显示了机器学习是如何工作的——它收集大量的输入,并从中学习来预测一些事情。当越来越多的人使用这些应用程序,并告诉他们什么是对或错时,这些应用程序受益最大。从用户那里收集的正确数据越多,结果就越准确。人工智能还有比敲击相机和用电脑玩哑谜更高级的应用,但这些只是简单的例子,可以向你展示我们从使用算盘计数到现在已经走了多远。
德雷德尔分析公司
Photo by Robert Zunikoff on Unsplash
古代赌博用的钱球
介绍
就像迪斯尼的奥拉夫唱的那样(这些天他差不多一直在我家唱)“现在是一年中的这个时候!”如果你是犹太人,或者你的家庭受到多元文化小学教育的影响,你可能会被劝在这个周末玩一些纸牌游戏。你不能从维基百科学到的是,根据广泛的分析确定,享受德雷德尔的关键是每个玩家只玩 3 个标记。
就我个人而言,我认为你应该完全跳过打陀螺,直接吃马克笔。但也许玩 dreidel 会让你闻到马加比营地的木头烟味和羊肉味,并与一个紧张地争论自己是 T4 自由战士还是激进原教旨主义者的年轻游击战士建立联系。或者也许你会和那些已经开始吃糖果的孩子一起庆祝。
在这种情况下,你需要玩,玩得没有挫败感,这样你就可以回到假期体验中更愉快的部分。控制游戏持续时间是保持游戏乐趣的最容易获得的杠杆,事实证明,游戏长度因玩家可用标记的数量而变化很大。如果你只是想看为什么是 3 个标记而不是 4 个(但愿不会如此),跳过方法部分,直接看结果。如果你想了解内部工作原理,请继续阅读。
方法
让我们模拟游戏来确定开始计数器的数量和完成游戏所需的回合数之间的关系。一路上我们会检查游戏的公平性(剧透警告:维基百科是对的;dreidel 不公平)。
为了在 R 中模拟游戏,我们将构建一个新的类“gameState ”,它将记录 dreidel 任何给定旋转后游戏的状态。这意味着它将跟踪每个玩家的计数器数量、底池中的计数器数量、回合数、下一个轮到的玩家、游戏中的玩家数量以及仍有计数器的玩家数量。我们将使它成为一个类,这样我们就可以创建一个方便的打印方法来改进 gameState 对象的调试和一般可用性。
这相对简单,如下所示:
现在我们已经定义了这个类,我们需要一个基于 dreidel 旋转来更新游戏状态的方法。让我们将传统的{ג,ה、נ,ש的 dreidel 方映射到{4,3,2,1},并使用整数作为输入,看看如何更新当前用户的持有量和底池的值,将回合数加 1,然后重新计算仍在游戏中的玩家人数。如果我们想通过改变底注的值来改变规则,让我们把它作为输入包含到 takeATurn 函数中。
这个函数比我预期的要复杂得多。大部分这些复杂的部分都是为了防止在“真实”游戏中不会发生的极端情况下的无限循环。这可能是一个基本的编程课程,但让我们继续关注如何在不玩太多的情况下玩够 dreidel。
既然我们可以玩一轮德赖德,那么玩德赖德到只剩下一个玩家就很容易了。为了简化我们的模拟调用,让我们制作一个名为 runGame 的新函数,它只是重复旋转一个 dreidel ( ceiling(runif(1,0,4) )调用 takeATurn() ,直到只剩下一个活动玩家,返回最终的游戏状态。为了对模拟结果进行一些分析,runGame 应该将允许创建不同游戏的输入(例如,玩家数量和每个玩家获得的计数器数量)作为输入。
模拟 10,000 个游戏感觉足以理解一个游戏可能要走多少个回合。因为如此多的迭代会花费大量时间,所以我们应该使用 foreach 和 doParallel 包在多个内核上运行这些游戏。这里有一个循环来模拟不同玩家数量和标记起始数量(每个玩家)的游戏:
结果
通过绘制中值回合数与起始代币数的图表,我们可以看出为什么过去所有的德雷德尔游戏都以提前吃掉代币而告终。四个玩家和 10 个标记给出了 400+旋转的平均时间!
但是让我们来看 3 个标记。即使有 10 名球员(当然我们可以做两场 5 人的比赛?)一场比赛的中位数是 300 次旋转。对于一个更典型的 4 人游戏来说,中间时间不到 20 分钟(在我的模拟中是 22 分钟)。每个人旋转 5 次(对于中位游戏),这足以满足每个人旋转 dreidel 的愿望,但不会旋转太多,以至于你必须吃掉所有的标记才能离开游戏。
但是不公平的部分呢?事实证明这相当复杂。似乎每个玩家的标记数量、玩家数量以及哪个玩家具有优势之间存在某种关系。
我们可以专注于首选的起始值 3,而不是理解所有这些,对于玩家数量的所有测试值,模拟结果返回非常接近公平值 1,这让我们感觉很好。
结论
Dreidel 一直坚持不是因为它有趣,而是因为它是传统。鉴于传统在节日期间有着不可避免的重要性,应用一些分析来优化我们如何处理传统是为一年中的那个时间做准备的一个很好的方式
从这个简单的模拟中,我们可以看到,一个不错的每人 5 次旋转的 dreidel 游戏需要比大多数玩家使用的代币少得多的代币(从 3 个开始!).我们还可以看到,如果我们想偷所有的孩子的糖果,我们应该在一大群玩家中玩,先走,使用 2 或 4 个标记。
如果你想运行你自己的 dreidel 模拟,你可以从这里获得本文中使用的全部代码。
我希望你的假期愉快、有趣、没有烦恼。
Keras 中基于自动编码器的服装分割
从照片中提取服装
Photo by Oladimeji Odunsi on Unsplash
时尚行业是人工智能非常赚钱的领域。数据科学家可以在很多领域开发有趣的用例并提供好处。我已经展示了我对这个领域的兴趣在这里,我开发了一个从 Zalando 在线商店推荐和标记服装的解决方案。
在这篇文章中,我试图进一步开发一个系统,该系统接收原始图像(从网络上获取或用智能手机制作)作为输入,并尝试提取其中显示的服装。请记住,分割的挑战是臭名昭著的极端噪声出现在原始图像;我们试图用聪明的技巧(在预处理期间)开发一个强大的解决方案来处理这个方面。
最后,你也可以尝试将这个解决方案与之前引用的合并。这允许你开发一个系统,通过你外出时拍摄的照片,实时推荐和标记服装。
数据集
最近,一个关于服装视觉分析和分割的 Kaggle 竞赛也启动了。这是一个非常有趣的挑战,但这并不适合我们…我的目标是从照片中提取服装,因此由于其冗余和细粒度的属性,这个数据集是不够的。我们需要包含大部分服装的图像,所以最好的选择是我们自己构建数据。
我从网上收集了一些图片,这些图片包含了在不同场景下穿着不同类型女装的人们。下一步需要创建遮罩:如果我们希望训练一个能够只关注真正感兴趣的点的模型,这对于每个对象分割任务都是必要的。
下面我报告一个数据样本供我们使用。我从网上收集了原始的图片,然后我很享受地进一步剪辑它们,把人和衣服分开。
Example of image segmentation
我们进行这种区分,是因为我们想在背景、皮肤和着装之间进行区分。背景和皮肤是这类问题中最相关的噪声源,所以我们尽量抑制它们。
有了这些剪报,我们可以重新创建我们的面具如下所示,这是简单的图像二值化。皮肤是因人和服饰的不同而获得的。
Example of masks
最后一步,我们将所有的图像合并成一个三维的图像。这张图片解码了我们感兴趣的原始图像的相关特征。我们的目的是保持背景,皮肤和服装之间的分离:这个结果对我们的范围来说是完美的!
Final mask
我们对数据集中的每一幅图像重复这个过程,以便为每一幅原始图像建立一个相关的三维蒙版。
模型
我们拥有一切来创造我们的模型。我们心目中的工作流程非常简单:
我们拟合一个模型,该模型接收原始图像作为输入,并输出三维掩模,即它能够从原始图像中重建皮肤/背景和服装之间的期望分离。这样,当一个新的 raw 图像进来时,我们可以将其分成三个不同的部分:背景、皮肤和服装。我们只考虑我们感兴趣的通道(服装),用它从输入图像中创建一个蒙版,并剪切它以重新创建原始服装。
由于 UNet 的力量,所有这些魔法都是可能的。这种深度卷积自动编码器通常用于像这样的分割任务。它很容易在 Keras 中复制,我们训练它为我们想要的遮罩的每个通道重建像素。
在开始训练之前,我们决定用它们的 RGB 平均值来标准化我们所有的原始图像。
结果和预测
我们注意到,在预测过程中,当我们遇到具有高噪声(就模糊背景或皮肤而言)的图像时,我们的模型开始挣扎。这种不便可以通过简单地增加训练图像的数量来克服。但是我们也发展了一个聪明的捷径来避免这些错误。
我们使用 OpenCV 提供的 GrubCut 算法。该算法利用高斯混合模型实现前景和背景的分离。这对我们很有帮助,因为它有助于指出前景中的人周围去噪。
这里我们实现了一个简单的函数来实现它。我们假设我们感兴趣的人站在图像的中间。
def cut(img): img = cv.resize(img,(224,224))
mask = np.zeros(img.shape[:2],np.uint8)
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,65),np.float64)
height, width = img.shape[:2] rect = (50,10,width-100,height-20)
cv.grabCut(img,mask,rect,bgdModel,fgdModel,5,
cv.GC_INIT_WITH_RECT)
mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
img2 = img*mask2[:,:,np.newaxis]
img2[mask2 == 0] = (255, 255, 255)
final = np.ones(img.shape,np.uint8)*0 + img2
return mask, final
GrubCut in action
现在我们应用 UNet,并准备在新图像上看到一些结果!
Input — GrubCut + Prediction — Final Dress
Input — GrubCut + Prediction — Final Dress
Input — GrubCut + Prediction — Final Dress
Input — GrubCut + Prediction — Final Dress
Input — GrubCut + Prediction — Final Dress
我们预处理步骤与 UNet 功能相结合,能够获得更好的性能。
摘要
在这篇文章中,我们开发了一个端到端的服装分割解决方案。为了达到这个目的,我们利用强大的自动编码器结合巧妙的预处理技术。我们计划这个解决方案是为了在真实照片的真实场景中使用它,并有可能在它的基础上建立一个视觉推荐系统。
保持联系: Linkedin
卷积层上的丢失很奇怪
为什么卷积层上的丢失与全连接层上的丢失有根本的不同。
Dropout 常用于正则化深度神经网络;然而,在全连接层上应用丢弃和在卷积层上应用丢弃是根本不同的操作。虽然在深度学习社区中众所周知,当应用于卷积层时,辍学具有有限的好处 ,但我想展示一个简单的数学示例来说明这两者为什么不同。为此,我将定义 dropout 如何在全连接层上操作,定义 dropout 如何在卷积层上操作,并对比这两种操作。
全连接层上的脱落
一个 n 层全连接神经网络(忽略偏差)可以定义为:
其中 ϕᵢ 为非线性(如 ReLU),对于 i ∈{1,…, n }为权重矩阵, x 为输入。
考虑一个单隐层全连接神经网络f(x):ℝ⁹→没有非线性和偏差的ℝ⁹。我们可以将网络表达为:
下面是我们的单隐层神经网络的示意图。
让我们把辍学加入到这个网络中。设 r ∈ {0,1}⁹为独立同分布(iid)伯努利随机变量的向量。在我们之前定义的神经网络中,Dropout 可以表示为:
其中vᵀx已经折叠到 h 为空格。
要了解这如何等价于辍学,考虑具体的情况,其中 r = (1,0,0,…,0)ᵀ.最终的网络是:
这就是最初的辍学论文中讨论的内容。
我们看到,全连接神经网络中的丢失等同于从与全连接层相关联的权重矩阵中清除一列。该操作对应于“丢弃”神经网络中的一个神经元。以这种方式丢弃神经元是合理的,因为定性地说,它促进了权重矩阵中的冗余,即子网络可以稳健地执行所需的操作。
卷积神经网络中的丢失
一个 n 层卷积神经网络(忽略偏差)可以定义为:
其中∫为卷积算子, ϕᵢ 为非线性, Kᵢ 为 i ∈{1,…, n }为卷积核, x 为输入。
为了弄清我们的方向,让我们先来研究卷积运算。我们可以将离散卷积重写为矩阵乘法。让我们考虑一下将 x ∈ ℝ ˣ与 K ∈ ℝ ˣ进行卷积。那么K∵x定义为:
这在整形操作之前相当于:
既然我们已经确定卷积可以作为矩阵乘法来应用,那么让我们以与我们表述全连接网络大致相同的方式来表述卷积网络。
设g(x):ℝˣ→ℝˣ是一个没有非线性和偏差的全卷积神经网络。本着我们之前网络的精神,我们将 g 定义为:
其中 u,v ∈ ℝ ˣ是卷积核, x ∈ ℝ ˣ是图像。
为了简单起见,也因为不影响我们的分析,让**【h =v∵x。注意,为了使变换 g 有效,我们需要在每个卷积层使用零填充。这可以合并到卷积核的矩阵形式中,因此我们的网络可以写成:**
其证明留给读者作为练习。
像之前在我们的全连接网络中一样,让我们在卷积网络中加入 dropout。
设 U 定义为上式中的矩阵(即补零的扩展卷积核)设 r ∈ {0,1}⁹ iid 伯努利随机变量,如前。然后我们有:
为了说明这种压差应用并不等同于全连接情况,只需关注前面所示矩阵左上角的 3 × 3 模块即可:
请注意,权重 u ₄ ,u ₅ ,u ₆ 在这个剪切图的三列中至少出现了两次。因此,如果 r ₁ = 0 和 r ₂=1 或 r ₁ = 1 和 r ₂=0,我们仍将更新权重 u ₅和 u ₄(不考虑 r ₃的值!).
结果
在我们对全连接网络中的丢失的分析中,我们表明丢失操作可以理解为在神经网络中将权重矩阵的列清零。这个操作相当于不训练或“放弃”一个神经元。
在上面的分析中,我们已经表明卷积层上的丢失不会产生相同的效果。这由以下事实来证明:将对应于卷积核的权重矩阵的列置零仍然允许训练该列中的权重。
由于 dropout 通常用于在训练期间不随机训练神经元子集,而卷积层上的 dropout 不进行这种操作,因此该名称具有误导性,并且——因为效果不明显可解释——很奇怪。
我并不是说卷积层的丢包没有用。在这些 论文中,作者在卷积层上使用 dropout 时得到了更好的结果。然而,卷积层上的丢失效应似乎相当于将伯努利噪声乘以网络的特征图。我的观点是,在没有更多潜在理论的情况下,选择这种方式将噪声注入系统似乎很奇怪;在全连接层的情况下,哪个是的理论。
因此,如果您尝试在卷积层后添加 dropout 并得到不好的结果,不要沮丧!似乎没有很好的理由让提供好的结果。
补充说明
在全连接单隐层神经网络上应用的丢失已经被示出对应于可解释的正则化器,一个均衡权重矩阵的正则化器。然而,这些结果的推导大量利用了矩阵的每个元素的权重彼此独立的事实;这个事实在卷积层中不成立。我没有立即看到如何调和这一事实与卷积层,所以我不明白如何辍学可以提供类似的,可解释的正则化。
基于机器学习的睡意检测
我们的团队如何用 Python 构建一个睡意检测系统。
团队成员:格兰特·钟、瑞英、【何】王、奥朗则布·西迪奎、高拉夫·乔德里
简介
“每 25 名成年司机中就有 1 人报告说,他们在过去 30 天里开车时睡着了”
如果你以前开过车,你会在某个时候昏昏欲睡。我们不愿意承认这一点,但这是一个有着严重后果的重要问题,需要加以解决。每 4 起交通事故中就有 1 起是由疲劳驾驶引起的,每 25 名成年司机中就有 1 人报告说他们在过去的 30 天里在开车时睡着了。最可怕的是,昏昏欲睡的驾驶不仅仅是在开车时睡着了。昏昏欲睡的驾驶可能小到当驾驶员没有完全注意道路时的短暂无意识状态。疲劳驾驶每年导致超过 71,000 人受伤,1,500 人死亡,以及 125 亿美元的经济损失。由于这个问题的相关性,我们认为开发一种用于困倦检测的解决方案是重要的,特别是在早期阶段以防止事故。
此外,我们认为困倦也会对人们在工作和课堂环境中产生负面影响。虽然睡眠不足和大学生活是密切相关的,但是在工作场所打瞌睡,尤其是在操作重型机械的时候,可能会导致严重的伤害,就像昏昏欲睡地开车一样。
我们对这个问题的解决方案是建立一个检测系统,识别困倦的关键属性,并在有人昏昏欲睡时及时触发警报。
数据源和预处理
对于我们的训练和测试数据,我们使用了由位于阿灵顿的德克萨斯大学的一个研究小组创建的现实生活困倦数据集,专门用于检测多阶段困倦。最终目标是不仅检测极端和可见的困倦情况,还允许我们的系统检测更柔和的困倦信号。该数据集由 60 名不同参与者约 30 小时的视频组成。从数据集中,我们能够从 22 名参与者的 44 个视频中提取面部标志。这使得我们能够获得足够数量的关于清醒和困倦状态的数据。
对于每个视频,我们使用 OpenCV 从 3 分钟标记开始每秒提取 1 帧,直到视频结束。
import cv2
data = []
labels = []
for j in [60]:
for i in [10]:
vidcap = cv2.VideoCapture(‘drive/My Drive/Fold5_part2/’ + str(j) +’/’ + str(i) + ‘.mp4’)
sec = 0
frameRate = 1
success, image = getFrame(sec)
count = 0
while success and count < 240:
landmarks = extract_face_landmarks(image)
if sum(sum(landmarks)) != 0:
count += 1
data.append(landmarks)
labels.append([i])
sec = sec + frameRate
sec = round(sec, 2)
success, image = getFrame(sec)
print(count)
else:
sec = sec + frameRate
sec = round(sec, 2)
success, image = getFrame(sec)
print(“not detected”)
每个视频大约 10 分钟长,所以我们从每个视频中提取了大约 240 帧,整个数据集有 10560 帧。
Facial Landmarks from OpenCV
每帧总共有 68 个标志,但是我们决定只保留眼睛和嘴巴的标志(点 37-68)。这些是我们用来提取模型特征的重要数据点。
特征抽出
正如前面简要提到的,基于我们从视频帧中提取的面部标志,我们冒险为我们的分类模型开发合适的特征。虽然我们假设并测试了几个特征,但我们为最终模型总结的四个核心特征是眼睛长宽比、嘴巴长宽比、瞳孔圆形度,最后是嘴巴长宽比超过眼睛长宽比。
眼睛纵横比(耳朵)
耳朵,顾名思义,就是眼睛的长度与眼睛的宽度之比。如下图所示,眼睛的长度是通过平均穿过眼睛的两条不同的垂直线计算的。
Eye Aspect Ratio (EAR)
我们的假设是,当一个人昏昏欲睡时,他们的眼睛可能会变小,他们可能会眨得更多。基于这一假设,如果一个人在连续帧中的眼睛纵横比开始下降,即他们的眼睛开始更加闭合或他们眨眼更快,我们预计我们的模型会将该类预测为困倦。
口长宽比(MAR)
如你所料,MAR 在计算上与耳朵相似,它测量嘴的长度与宽度之比。我们的假设是,当一个人变得昏昏欲睡时,他们很可能会打哈欠,并对自己的嘴巴失去控制,使他们的 MAR 比这种状态下的正常水平高。
Mouth Aspect Ratio (MAR)
瞳孔圆形(PUC)
PUC 是对耳朵的补充,但它更强调瞳孔而不是整个眼睛。
Pupil Circularity
例如,由于分母中的平方项,与眼睛完全睁开的人相比,眼睛半开或几乎闭上的人将具有低得多的瞳孔圆度值。与耳朵类似,预期是当个体昏昏欲睡时,他们的瞳孔圆形度可能下降。
嘴巴长宽比超过眼睛长宽比(MOE)
最后,我们决定添加 MOE 作为另一个特性。MOE 就是 MAR 与 EAR 的比值。
Mouth Over Eye Ratio (MOE)
使用此功能的好处是,如果个体的状态发生变化,EAR 和 MAR 预计会向相反的方向移动。与 EAR 和 MAR 相反,MOE 作为一种测量方法将对这些变化更敏感,因为它将捕捉 EAR 和 MAR 中的细微变化,并将随着分母和分子向相反方向移动而夸大这些变化。因为 MOE 以 MAR 为分子,EAR 为分母,所以我们的理论是,当人昏昏欲睡时,MOE 会增加。
虽然所有这些特征都有直观的意义,但当用我们的分类模型进行测试时,它们在 55%到 60%的准确度范围内产生了较差的结果,对于二进制平衡分类问题来说,这仅是 50%的基线准确度的微小改进。尽管如此,这种失望让我们有了最重要的发现:这些特征没有错,我们只是没有正确地看待它们。
特征标准化
当我们用上面讨论的四个核心特性测试我们的模型时,我们看到了一个令人担忧的模式。每当我们在训练和测试中随机分割帧时,我们的模型将产生高达 70%的准确性结果,但是,每当我们按个体分割帧时(即,测试集中的个体将不在训练集中),我们的模型性能将会很差,正如前面提到的。
这让我们意识到,我们的模型正在与新面孔进行斗争,这种斗争的主要原因是每个人在默认的警报状态下都有不同的核心特征。也就是说,人 A 可能自然地具有比人 B 小得多的眼睛。如果在人 B 上训练模型,则当在人 A 上测试时,该模型将总是预测状态为困倦,因为它将检测到耳朵和 PUC 的下降以及 MOE 的上升,即使人 A 是警觉的。基于这一发现,我们假设将每个人的特征标准化可能会产生更好的结果,事实证明,我们是正确的。
为了归一化每个人的特征,我们取了每个人的警报视频的前三帧,并将它们用作归一化的基线。计算这三个帧的每个特征的平均值和标准偏差,并用于标准化每个参与者的每个特征。从数学上讲,这是归一化方程的样子:
Normalization Method
既然我们已经规范化了四个核心特性中的每一个,我们的特性集就有八个特性,每个核心特性都由它的规范化版本补充。我们测试了模型中的所有八个特性,结果显著改善。
基本分类方法和结果
在我们提取和标准化我们的特征之后,我们希望尝试一系列建模技术,从最基本的分类模型开始,如逻辑回归和朴素贝叶斯,然后转向包含神经网络和其他深度学习方法的更复杂的模型。这里需要注意的是性能和可解释性之间的权衡。虽然我们优先考虑性能最佳的模型,但如果我们要将这一解决方案商业化,并向不熟悉机器学习术语的利益相关者展示其商业含义,可解释性对我们也很重要。为了训练和测试我们的模型,我们将我们的数据集分成分别来自 17 个视频的数据和来自 5 个视频的数据。因此,我们的训练数据集包含 8160 行,测试数据集包含 2400 行。
我们如何将序列引入基本分类方法?
我们在这个项目中面临的一个挑战是,我们试图预测序列中每一帧的标签。虽然像 LSTM 和 RNN 这样的复杂模型可以解释序列数据,但基本的分类模型却不能。
我们处理这个问题的方法是将原始预测结果与前两帧的预测结果进行平均。由于我们的数据集根据个体参与者分为训练和测试,并且数据点都是按时间顺序排列的,因此在这种情况下,平均是有意义的,并允许我们提供更准确的预测。
Introducing Sequence to Basic Classification Models
从我们尝试的不同分类方法来看,K-最近邻(kNN,k = 25)的样本外准确率最高,为 77.21%。朴素贝叶斯表现最差,为 57.75%,我们的结论是,这是因为模型在处理数字数据时遇到了困难。虽然 kNN 产生了最高的准确性,但假阴性率非常高,为 0.42,这意味着有 42%的可能性,实际上昏昏欲睡的人会被我们的系统检测为警觉。为了降低假阴性率,我们将阈值从 0.5 降低到 0.4,这使得我们的模型能够预测更多的困倦情况而不是警觉情况。尽管其他一些模型的精度有所提高,但 kNN 仍然报告了最高的精度,为 76.63% (k = 18),尽管其自身的精度有所下降。
Left: Original Results | Right: Results after lowering threshold from 0.5 -> 0.4
特征重要性
我们想了解特征的重要性,所以我们可视化了随机森林模型的结果。
Feature Importance from Random Forest
归一化后的嘴部长宽比是我们的 8 个特征中最重要的特征。这是有道理的,因为当我们昏昏欲睡时,我们往往会更频繁地打哈欠。标准化我们的特征夸大了这种效应,并使其成为不同参与者嗜睡的更好指标。
卷积神经网络(CNN)
卷积神经网络(CNN)通常用于分析图像数据并将图像映射到输出变量。然而,我们决定建立一个一维 CNN,并发送数字特征作为连续输入数据,以尝试和理解两个状态的每个特征之间的空间关系。我们的 CNN 模型有 5 层,包括 1 个卷积层,1 个展平层,2 个完全连接的密集层,以及输出层之前的 1 个丢弃层。展平层展平卷积层的输出,并在将其传递到第一个密集层之前使其线性。丢弃层从第二密集层随机丢弃 20%的输出节点,以防止我们的模型过度拟合训练数据。最后的密集层有一个输出节点,输出 0 表示警报,输出 1 表示困倦。
CNN Model Design
CNN Parameters
长短期记忆(LSTM)网络
另一种处理顺序数据的方法是使用 LSTM 模型。LSTM 网络是一种特殊的递归神经网络(RNN),能够学习数据的长期依赖性。递归神经网络是反馈神经网络,具有允许信息持续存在的内部存储器。
RNNs 如何在处理新数据的同时拥有内部存储空间?
答案是,在做出决策时,RNNs 不仅考虑当前输入,还考虑它从先前输入中学习到的输出。这也是 RNNs 与其他神经网络的主要区别。在其他神经网络中,输入是相互独立的。在 RNNs 中,输入是相互关联的。公式如下:
RNN formula
我们选择使用 LSTM 网络,因为它允许我们研究长序列,而不必担心传统 rnn 面临的梯度消失问题。在 LSTM 网络中,每个时间步有三个门:遗忘门、输入门和输出门。
LSTM Network Visualized
遗忘门:顾名思义,该门试图“遗忘”之前输出的部分内存。
Forget Gate Formula
**输入门:**该门决定应该从输入中保留什么,以便修改存储器。
Input Gate Formula
**输出门:**门通过结合输入和存储器来决定输出是什么。
Output Gate Formula
首先,我们将视频转换成批量数据。然后,使用 sigmoid 激活函数将每一批发送通过具有 1024 个隐藏单元的全连接层。下一层是我们的 LSTM 层,有 512 个隐藏单元,接着是 3 个 FC 层,直到最后的输出层,如下图所示。
LSTM Network Design
LSTM Parameters
在超参数调整后,我们优化的 LSTM 模型实现了 77.08%的总体准确性,与我们的 kNN 模型的假阴性率(0.42)相比,假阴性率低得多,为 0.3。
迁移学习
迁移学习侧重于使用在解决一个问题时获得的知识,并将其应用于解决一个不同但相关的问题。这是一套有用的技术,尤其是在我们训练模型的时间有限或训练神经网络的数据有限的情况下。由于我们正在处理的数据只有很少的独特样本,我们认为这个问题是使用迁移学习的一个很好的候选。我们决定使用的模型是带有 Imagenet 数据集的 VGG16 。
VGG16 是由牛津大学的 K. Simonyan 和 A. Zisserman 在他们的论文“用于大规模图像识别的非常深的卷积网络”中提出的卷积神经网络模型。该模型在 ImageNet 中成功实现了 92.7%的前 5 名测试准确率,ImageNet 是一个包含属于 1000 个类别的超过 1400 万张图像的数据集。
ImageNet 是一个数据集,包含超过 1500 万张带有标签的高分辨率图像,属于大约 22,000 个不同的类别。这些图片是从互联网上收集的,由人类贴标机使用亚马逊的众包工具 Mechanical Turk 进行标记。自 2010 年以来,作为 Pascal 视觉对象挑战赛的一部分,每年都会举办一次名为 ImageNet 大规模视觉识别挑战赛(ILSVRC)的比赛。ILSVRC 使用一个更小的 ImageNet 集,大约有 1000 个图像,每个类别有 1000 个图像。大约有 120 万幅训练图像、50,000 幅验证图像和 150,000 幅测试图像。ImageNet 由不同分辨率的图像组成。因此,图像的分辨率需要更改为 256×256 的固定值。图像被重新缩放和裁剪,中心的 256×256 小块形成最终的图像。
VGG16 Network Architecture
cov1 图层的输入是 224 x 224 RGB 图像。图像通过一叠卷积层,在卷积层中,滤波器的接收域非常小:3×3。在其中一种配置中,该模型还利用 1×1 卷积滤波器,可以将其视为输入通道的线性变换,然后是非线性变换。卷积步距固定为 1 个像素;卷积层输入的空间填充使得卷积后保持空间分辨率,即对于 3×3 卷积层,填充是 1 个像素。空间池由五个最大池层执行,这五个最大池层位于一些卷积层之后。不是所有的 conv。层之后是最大池。最大池在 2×2 像素窗口上执行,步长为 2。
一堆卷积层之后是三个全连接(FC)层:前两层各有 4096 个通道,第三层执行 1000 路 ILSVRC 分类,因此包含 1000 个通道。最后一层是软最大层。全连接层的配置在所有网络中都是相同的。
所有隐藏层都配备了校正(ReLU)非线性。还要注意的是,除了一个以外,没有一个网络包含局部响应归一化(LRN),因为这种归一化不会改善模型的性能,而是导致计算时间增加。
我们将训练视频分成 34,000 个图像,这些图像是每 10 帧拍摄的截图。我们将这些图像输入 VGG16 模型。我们相信图像的数量足以训练预训练的模型。在对模型进行 50 个时期的训练后,我们得到了以下准确度分数。我们的结果如下所示。
VGG16 Results
很明显,这个模型是过度拟合的。对此的一个可能的解释是,我们通过模型传递的图像是 22 名受访者几乎一动不动地坐在背景不受干扰的摄像机前。因此,尽管我们的模型采用了大量的帧(34,000),但该模型本质上是试图从 22 组几乎相同的图像中学习。因此,该模型实际上没有足够的训练数据。
结论
在这个项目中,我们学到了很多东西。首先,在完成任务时,简单的模型和复杂的模型一样有效。在我们的例子中,K-最近邻模型给出了与 LSTM 模型相似的精确度。然而,因为我们不希望将昏昏欲睡的人错误分类为警觉,所以最终最好使用假阴性率更低的更复杂的模型,而不是部署起来可能更便宜的更简单的模型。第二,正常化对我们的表现至关重要。我们认识到每个人的眼睛和嘴巴长宽比都有不同的基线,对每个参与者进行标准化是必要的。在我们模型的运行时间之外,数据预处理和特征提取/标准化占用了我们大量的时间。更新我们的项目并研究如何降低 kNN 和其他更简单模型的假阴性率将会很有趣。
未来范围
展望未来,我们可以做一些事情来进一步改善我们的结果和微调模型。首先,我们需要合并面部标志之间的距离,以说明视频中对象的任何移动。实际上,参与者在屏幕上不会是静止的,我们认为参与者的突然移动可能是困倦或从微睡眠中醒来的信号。其次,我们希望用更复杂的模型(神经网络、集成等)更新参数。)才能达到更好的效果。第三,也是最后一点,我们希望从更大的参与者样本中收集我们自己的训练数据(更多数据!!!)同时包括新的明显的困倦信号,如突然的头部运动、手部运动或者甚至跟踪眼睛运动。
产品预览
我们想包括一些我们的系统在运行中的截图!
首先,我们需要根据参与者校准系统,如下所示。
System Calibration
现在,系统应该自动检测参与者是困倦还是警觉。示例如下所示。
Drowsiness Detection System Example
非常感谢您通读我们的整个博客!如有任何问题或建议,请随时联系 LinkedIn 上的任何人,以改进我们的系统。
完整的项目和代码可以在 GitHub 上查看!
承认
我们要特别感谢 Joydeep Ghosh 博士,他在整个项目中提供了非常有价值的指导。
参考
德克萨斯大学阿灵顿分校的现实生活困倦数据集(UTA-RLDD)是为多阶段…
sites.google.com](https://sites.google.com/view/utarldd/home)
https://arxiv.org/abs/1904.07312
【https://pypi.org/project/opencv-python/
https://towards data science . com/understanding-rnn-and-lstm-f 7 CDF 6 DFC 14 e
https://neurohive.io/en/popular-networks/vgg16/
ImageNet 是一个根据 WordNet 层次结构(目前只有名词)组织的图像数据库,其中每个…
www.image-net.org](http://www.image-net.org/)