2017年12月9日星期六

骗过70%的人!这个AI能自动给视频配音,真假难辨(不服来试)

安妮 发自 凹非寺量子位 出品 | 公众号 QbitAI

先来做个"真假美猴王"的游戏。

你将看到两段画面相同的视频,请判断哪段来自视频原声,哪段是AI根据视频画面配上的假声?

莫非两个都是真的?不可能,答案文末揭晓。(还有更多真假难辨的视频原声和配音大对比)

真假难辨,简直让人怀疑耳朵。模型合成的假音效,什么时候都这么逼真了?一切还得从这个自动为自然环境下的视频配音的项目说起。

视听关联

看闪电,知雷声。

对人类来说,声音和视觉通常会打包出现传递信息。就像一个孩子看到闪电会下意识捂住耳朵,看到沸腾的水会想起水汽呼呼的声音。

在论文Visual to Sound: Generating Natural Sound for Videos in the Wild中,北卡罗来纳大学的博士生Yipin Zhou,其导师Tamara L. Berg联合Adobe公司的Zhaowen Wang、Chen Fang和Trung Bui三人,想做出一个计算模型来学习视觉和声音间的关系,减少繁琐的音频编辑流程。

要做出这样一个模型,那第一步肯定是找出一个合适的数据集来训练。

这个数据集可没有那么好找。

加工数据集

研究人员掐指一算,觉得AudioSet不错。

这是今年3月谷歌开放的一个大规模的音频数据集,包含了632个类别的音频及2084320条人工标记的音频,每段音频长度均为10秒。人与动物、乐器与音乐流派、日常环境的声音均覆盖在数据集内。

数据集代码地址:

http://ift.tt/2mjaIEf

但由于AudioSet中很多的音频与视频的关联松散,目标声音可能被音乐等其他声音覆盖,这些噪音会干扰模型学习正确的声音和图像间的映射(mapping),因此也不是很理想。研究人员先清理了数据的一个子集,让它们适应生成任务。

研究人员从AudioSet中选择10个类别进行进一步的清理,分别为婴儿啼哭、人打鼾、狗、流水、烟火、铁路运输、打印机、打鼓、直升机和电锯。每个类别中包含1500-3000个随机抽取的视频。

之后,研究人员用亚马逊众包平台Mechanical Turk(AMT)清理数据。值得一提的是,李飞飞在建立ImageNet数据集时,也是借助这个可以把任务分发给全世界坐在电脑前的人的平台做起来的。

在这个任务中,研究人员借助AMT上兼职的力量验证在图像和音频形态下,视频片段中关注的物体或动作是否存在。如果在视听两种环境下都存在,则认为它是一个噪音较少的可用视频。为了尽可能保留更多数据,研究人员将每段视频分割成两秒钟的短视频,分别标注标签。

这样一来,图像和音频模式上共标注了132209个片段,每个都被3个兼职做了标记,并从原始数据中删除了34392个片段。研究人员在合并相邻的短片段后,总共得到了28109个筛选后的视频。这些视频平均长度为7秒,总长度为55小时。

下图左表显示了视频数量和每个类别的平均长度,而饼图展示了长度的分布。由图中可见大多数视频的长度超过8秒。

研究人员将这个数据集命名为VEGAS(Visually Engaged and Grounded AudioSet ) 。

准备模型

数据集搞定后,研究人员开始了模型研究。

研究人员将任务当成一个条件生成问题,通过训练条件生成模型从一个输入视频合成原始波形样本。条件概率如下:

在这个概率中,x1,……,Xm为输入视频帧的表示,y1,……,yn为输出的波形值,是0到255之间的整数序列。值得注意的是,m通常远远小于n,因为音频的采样率远高于视频的采样率,因此音频波形序列比同步视频的视频帧序列长得多。

大体来说,这个模型由两部分构成,即声音生成器和视频编码器。

声音生成器

研究人员想直接用生成模型合成波形样本。为了得到音效说得过去的音频,他们选用了16kHz的音频采样频率。这就导致序列相当长,给生成器出了个难题。最后,研究人员选择了Yoshua Bengio团队在去年发表的论文《SampleRNN: An Unconditional End-to-End Neural Audio Generation Model》中提出的SampleRNN来合成声音。

论文地址: http://ift.tt/2oG2XpL

SampleRNN是一种递归神经网络,它由粗到细的结构使模型产生极长的序列,而且每一层的周期性结构都能捕捉到关联不紧密的样本间的依赖关系。

SampleRNN已经应用于语音合成和音乐生成任务。在这个项目中,研究人员用它来为自然条件下的视频生成声音。这意味着变化更大、结构模式更少和比语音或音乐数据更多的噪音。

确实是个挑战。

SampleRNN模型的简化结构如下图所示。

图中将示例结构简化到2层,但在实际操作中可能包含更多层次。该模型由多个层、细层(底层)是一个多层感知器(MLP),它从下一个粗层(上层)和前一个k样本中输出,生成一个新样本。

3种编码视觉信息和系统的变体

之后,研究人员提出了三种类型的编码器-解码器结构,这些信息可以与声音生成网络相结合,形成一个完整的框架。这三种变体分别为帧到帧法(Frame-to-frame method)、序列到序列法(Sequence-to-sequence method)和基于流的方法(Flow-based method)。

帧到帧法如上图绿色虚线框内所示。在这种方法中,研究人员将图像表示(蓝色的FC6立方体)与最粗层的节点联系起来。

研究人员将视频帧表示为xi=V(fi),其中fi为第i帧,V(.)是提取VGG19网络中FC6特征的操作,它已经在ImageNet上进行过预训练,xi是一个4096维向量。

序列到序列的模型中,视频编码器和声音生成器明显是分开的,并通过一个bottleneck来表示连接,它将编码的可视信息提供给声音生成器。如上图中红色框的(c)区所示,研究人员建立了一个递归神经网络来编码视频特征。

这个声音生成任务就变成了:

其中H代表视频编码RNN的最后一个隐藏状态,相当于声音生成器最粗一级的RNN的初始隐藏状态。

为了更好地显示运动信号,研究人员在视觉编码器中加入了一个基于光流的深度特征,并将此方法称为基于流的方法。这种方法的总体架构与序列到序列模型完全相同(如(c)所示),通过RNN反复编码视频特征xi,并用SampleRNN进行解码。

唯一的区别是,生成任务

中的oi表示第i帧的光流,而F(.)是提取基于光流的深层特征的函数。

开始训练,Go!

终于开始训练模型了。

研究人员分别用上述三种模型训练筛选出来的10个类别的视频。此时,这些视频已经经过复制和拼接,时长均被填补到10秒。

研究人员用15.6 FPS(156帧10秒)的采样率采集视频,并在约16kHz的采样率对音频采样,具体为159744次每10秒。帧到帧的方法中,将步长s设置为1024。

多维评估结果

训练结果如何?研究人员对模型进行了定性可视化

下面这张图显示了三种场景,分别为小狗、烟火、敲鼓和铁路。在每个场景中,研究人员拿出了两幅关键帧来作对比,下面的四种波形从上到下分别为帧到帧、序列到序列和基于流的方法生成的结果及原始音频。生成的音频与视频中的关键帧对齐。

前三个场景对音画同步很敏感,但可以发现,波形并没有和真实感知的波形一致,但视频中的关键点处理得还不错。

之后,研究人员从损失值和检索实验两方面对模型进行了定量评估

通过平均交叉熵损失,可以看到,基于流和序列方法的训练和测试损失值比帧到帧的方法低。

之后,研究人员又设计了一个检索实验,利用视觉特征,来查询具有最大抽样可能性的音频。在这个实验中,他们把所有测试视频中的音频都合并到一起,构成一个包含1280段音频的数据库,并对每个测试视频进行音频检索性能测试。

其实对于生成结果来说,最主观的评价方法可能也是最正确的评价方法。研究人员找来了一群人,判断哪种编码器解码器结构的效果更好。

研究人员从正确性、少噪音和同步性的维度,对三种变体的效果进行测试。结果可以看出,帧到帧方法的效果明显落后于其他两个,基于流的方法效果最好。

最后,最一颗赛艇的时刻到了。被调查人员能否在真假视频对中找出合成的"假猴王"呢?来看看研究人员的统计结果。

从结果中可以看出,超过70%的生成模型会让人觉得是真实的。

所以,开头判断不出真假的你,也属于这70%的大军啦。

相关资料

项目地址:

http://ift.tt/2C2vnlQ

论文地址:

http://ift.tt/2BQ7CMS

此外,一作YiPin Zhou在项目介绍中表示,过一阵子将开放VEGAS数据集。

对了,开头的短视频中,上面是合成的,下面是真实的,你猜对了吗?

不仅如此。下面这段视频包括打鼓的青年、哭叫的孩子、燃放的烟火、和飞驰的火车等,涵盖了很多自然界中的声音。

每个场景的配音均为一真一假,当场揭晓答案,猜猜你能对几个——

—完—

欢迎大家关注我们的专栏:量子位 - 知乎专栏

诚挚招聘

量子位正在招募编辑/记者,工作地点在北京中关村。期待有才气、有热情的同学加入我们!相关细节,请在量子位公众号(QbitAI)对话界面,回复"招聘"两个字。

量子位 QbitAI · 头条号签约作者

վ'ᴗ' ի 追踪AI技术和产品新动态



via 量子位 - 知乎专栏 http://ift.tt/2BQCPzA
RSS Feed

RSS5

IFTTT

2017CV技术报告:从3D物体重建到人体姿态估计

The M Tank 编辑了一份报告《A Year in Computer Vision》,记录了 2016 至 2017 年计算机视觉领域的研究成果,对开发者和研究人员来说是不可多得的一份详细材料。该材料共包括四大部分,在本文中机器之心对第三部分做了编译介绍,第一部分、第二部分和第四部分详见《计算机视觉这一年:这是最全的一份 CV 技术报告 》、《深度 | 2017 CV 技术报告之图像分割、超分辨率和动作识别 》、《计算机视觉这一年:2017 CV 技术报告 Plus 之卷积架构、数据集与新趋势 》。


「计算机视觉的主要目标是从 2D 的观察复原世界的 3D 结构。」——Rezende et al. (2016, p. 1[92])


正如我们所了解的,在计算机视觉领域,对场景、物体和活动的分类以及绘制边界框和图像分割是许多新兴研究的重点。实际上,这些方法依靠计算来「理解」图像的二维空间。但是,反对者指出,理解 3D 空间对于系统地解释和认知现实世界是必不可少的。


例如,一个网络可能会在图像中找到一只猫,将猫的灰度图上色,并将其归类为一只猫。但是,网络是否能完全理解图像里的猫在其所处环境中的位置?


可能有人会说,在上述任务中,计算机对于 3D 世界的了解很少。与此相反,即使在查看 2D 图片(即透视、遮挡、深度、场景中的对象如何相互关联等情况下)的时候,人们也能够以 3D 空间来理解世界。将这些 3D 表示及其相关知识传递给智能系统是下一场计算机视觉变革的前沿。我这样想的一个主要原因是:


「场景的 2D 投影是关于摄像机的位置、属性,以及灯光和组成场景的物体的复杂函数。如果赋予 3D 理解,智能体可以从这种复杂性中抽象出稳定的、易懂的表示。例如,无论是从上面还是从侧面看,在不同的光照条件和遮挡情况下,模型都可以辨认出椅子就是椅子。」[93]


但是,理解 3D 空间一直面临着几个难题。第一个问题涉及到「自遮挡」和「正常遮挡」的问题,以及大量 3D 形状都能符合单个 2D 表示的特征。由于无法将相同结构的不同图像映射到相同的 3D 空间以及处理这些表示的多模态,对这些问题的理解变得更加复杂 [ 94 ]。最后,实况的 3D 数据集通常相当昂贵且难以获得,加上表示 3D 结构的方法各异,这些都导致了模型训练的局限性。


3D 物体


第一部分有些零散,仅作为一个概览,涉及应用于 3D 数据表示的物体的计算、从 2D 图像推导 3D 物体形状和姿态估计、从 2D 图像确定物体的 3D 姿态的变换。[95] 重建的过程将在下一节中详细地介绍。以下展示了我们团队在这个综合领域的最激动人心的工作:


  • OctNet: Learning Deep 3D Representations at High Resolutions[96] 紧接最新的卷积网络研究进展(即对 3D 数据、体素(voxel)进行 3D 卷积操作)。OctNet 是一种新颖的 3D 表示形式,可以使高分辨率的输入的深度学习变得容易。作者通过'分析分辨率对包括 3D 对象分类、方向估计和点云标记在内的多种 3D 任务的影响'来测试 OctNet 表示。本文的核心贡献是它利用 3D 输入数据的稀疏性,从而能够更有效地使用内存及计算。
  • ObjectNet3D: A Large Scale Database for 3D Object Recognition[97] 贡献了一个数据库,用于 3D 物体识别,其中包含 100 个物体类别的 2D 图像和 3D 形状。『我们的数据库中的图像(取自 ImageNet)中的对象与(从 ShapeNet 存储库获取)的 3D 形状保持一致,该一致性指为每个 2D 物体提供精确的 3D 姿态标注和最接近的 3D 形状标注。』 基准实验包括:生成区域建议、2D 物体检测、联合 2D 检测和 3D 物体姿态估计,以及基于图像的 3D 形状恢复。
  • 3D-R2N2: A Unified Approach for Single and Multi-view 3D Object Reconstruction[98] - 使用任意视角的单个或多个对象实例的图像,以 3D 占据网格的形式重建对象,主要利用合成数据学习从物体 2D 图像到 3D 形状的映射,网络不需要任何图像标注或物体类别标签就可以训练和测试。该网络由一个 2D-CNN、一个 3D 卷积 LSTM(为本文需要新创建的结构)和一个 3D 解卷积神经网络组成。这些不同的组件之间的相互作用和联合端到端的训练是神经网络分层特征的完美例证。


图 11: 3D-R2N2 的功能图例,(左)图像取自 Ebay,(右)是 3D-R2N2 的功能概述。


论文笔记:作者希望重建的一些示例对象图像——视图被高基准分隔开,并且物体的外观纹理很少或是非朗伯表面,亦或两者具备。(b)对我们提出的 3D-R2N2 的概述:网络从任意(未校准的)视角输入一系列图像(或者一张图像)作为输入(在本例中为扶手椅的 3 个视图),并产生重建的体素化 3D 作为输出。随着网络看到对象的更多视图,重建过程逐渐细化。论文:Choy et al. (2016, p. 3)[99]


3D-R2N2 使用 ShapeNet 模型生成渲染图像和体素化模型,并有助于 3D 物体重建,而从运动恢复结构(SfM)和并发建图与定位(SLAM)通常会失败:


「我们的拓展实验分析表明,我们的重建框架(i)优于最好的单一视图重建方法,并且(ii)当传统的 SFM / SLAM 方法失效时,仍然能够对物体进行三维重建。


3D Shape Induction from 2D Views of Multiple Objects[100] 使用投影生成对抗网络(PrGANs)训练一个可以准确表示 3D 形状的深度生成模型,其中判别器仅显示 2D 图像。投影模块捕获 3D 表示,并在传递给判别器之前将其转换为 2D 图像。通过迭代训练周期,生成器通过改进生成的 3D 体素形状来完善投影结果。


图 12:PrGAN 部分架构


论文笔记:用于生成 2D 图像的形状的 PrGAN 架构。3D 体素表示(323)和视角是从输入 z(201-d 向量)独立生成的。投影模块通过给定视角 (θ,φ) 呈现体素形状来创建图像。判别器由二维卷积层和池化层组成,目的在于判定输入图像是生成的还是真实的。论文: Gadhelha et al. (2016, p. 3)[101]


通过这种方式,推断能力是通过无监督的方式学习到的:


「增加投影模块使我们能够在学习阶段不使用任何 3D 信息、视角信息或标注来推断潜在的 3D 形状分布。」


另外,形状的内部表示可以被内插,这意味着体素形状中的离散共性允许实现对象到对象的变换,例如,汽车变飞机。


Unsupervised Learning of 3D Structure from Images[102] 提出了一个完全无监督的生成模型,它首次证明了学习推断 3D 表征的可行性。简而言之,DeepMind 团队展示了一个「学习 3D 结构的强大深度生成模型,并通过概率推理从 3D 和 2D 图像中恢复这些结构」的模型,这意味着 3D 和 2D 都可以作为输入。


DeepMind 的强大的生成模型可运行在 3D 和 2D 图像上。使用 OpenGL 的基于网格的表示允许构建更多的知识,例如,光线如何影响场景和使用的材料。「使用基于 3D 网格的表示和在循环中使用完整的黑盒渲染器进行训练,可以了解对象的颜色、材质和纹理、灯光位置以及其他对象之间的相互作用。」[103]


这些模型质量高,可应对不确定性,并适合概率推理,适用于生成 3D 结构和仿真。该团队在 3D MNIST 和 ShapeNet 上实现了 3D 密度建模的第一个量化基准。这种方法表明,模型可以在 2D 图像上进行端对端的无监督训练,不需要真实的 3D 标签。


人体姿态估计与关键点检测


人体姿态估计试图找出人体部位的方向和构型。2D 人体姿态估计或关键点检测一般是指定位人体的身体部位,例如找到膝盖、眼睛、脚等的 2D 位置。


然而,三维姿态估计进一步通过在三维空间中找到身体部位的方向,然后可以选择执行形状估计/建模。在过去的几年中,这些子领域有了大幅的进步。


在竞争评估方面,「COCO2016 关键点挑战包括同时检测人员和定位关键点」[104]「欧洲计算机视觉国际会议」(ECCV)[105] 提供了有关这些主题的广泛的文献,但我们想安利一篇论文:

Realtime Multi-Person 2D Pose Estimation using Part Affinity Fields[106]


该方法在首届 MSCOCO 2016 关键挑战赛上以 60%的平均精度(AP)取得了当前最佳性能,并在 ECCV 上获得了最佳的演示奖,视频:https://www.youtube.com/watch?v=pW6nZXeWlGM[107]


Keep it SMPL: Automatic Estimation of 3D Human Pose and Shape from a Single Image[108] 这种方法首先预测 2D 人体关节的位置,然后使用另一个称为 SMPL 的模型来创建 3D 身体形状网格,从而允许它从 2D 姿态估计理解 3D 形态。3D 网格能够同时捕捉姿态和形状,而以前的方法只能得到 2D 人体姿势。作者提供了一个优秀的视频,并在视频中分析了他们的工作:https://www.youtube.com/watch?v=eUnZ2rjxGaE [109]


「我们描述了第一种(从一张无约束图像)自动估计人体 3D* *姿态以及 3D* *人体形状的方法。我们估计一个完整的 3D* *网格,并表明仅仅 2D 关节就携带了大量的身体形状的信息。由于人体的复杂性、清晰度、遮挡情况、衣服、照明条件以及从 2D 推断 3D 所固有的模糊性,致使这个问题极具挑战性。」[110]


3D 重建


如前所述,前面重点介绍了一些关于重建的例子。但大部分都在关注物体,特别是物体的形状和姿态。虽然其中有一部分是重建(Reconstruction)技术,但「重建」这个领域本身包括更多不同的类型,例如场景重建、多视点重建、单视点重建、基于运动的重建(SfM)、即时定位与地图重建(SLAM)等。此外,一些重建方法利用了附加(和多个)传感器与设备,比如 Event 或 RGB-D 摄像机等,这些方法可以同时分层运用多种技术来推动系统进程。


那么结果如何?整个场景可以非刚性(non-rigidly)地重建,并在时空上进行改变。例如,对你自己的高保真结构以及你的动作可进行实时更新。


如前所述,围绕 2D 图像映射到 3D 空间的问题持续存在。以下文章介绍了创建高保真实时重建的多种方法:


Fusion4D: Real-time Performance Capture of Challenging Scenes[111] 转向计算机图形学,但是计算机视觉和计算机图形学之间的相互影响极大。作者使用 RGB-D 和 图像分割作为输入,形成以三维像素输出的实时、多视点重建。


图 13:实时反馈的 Fusion4D 案例,来源 Dou et al. (2016, p. 1)[112]。


论文笔记:「我们提出了一种实时高质量 4D(即,时空相干)性能捕获的新方法,允许从多个有噪声的 RGB-D 相机输入中进行增量式非刚性重建。我们的系统以实时速率演示了非刚性序列前所未有的重构,包括对大型帧对帧运动和拓扑变化的鲁棒性处理。」


Fusion4D 创建实时高保真三维像素表示,在虚拟现实、增强现实和远程监控方面的运用令人印象深刻。这项来自微软的工作可能会彻底改变动作捕捉技术,有可能在体育直播中得到运用。

Real-Time 3D Reconstruction and 6-DoF Tracking with an Event Camera[115] 在 2016 年欧洲计算机视觉会议(ECCV)上获得最佳论文。作者提出了一种能够使用单个 Event Camera 实时跟踪 6D 运动和进行各种重建的新算法。


图 14:实时三维重建示例,来源 Kim et al. (2016, p. 12)[116]。


论文笔记:我们的联合估计算法在不同方面各种设置的演示如下。(a)输入事件流的可视化;(b)估计梯度关键帧;(c)具有超高分辨率和高动态范围特性的强度关键帧重构;(d)估算深度图;(e)半密集的 3D 点云。


与传统相机相比,Event Camera 在计算机视觉领域越发受到研究者的青睐。它的延时更短、能耗更低、动态范围更大。传统相机输出的是一系列的帧,但 Event Camera 输出的却是「异步尖峰流(asynchronous spikes),每一个都与像素位置、符号、准确时间相对应,指示了何时单个像素记录阈值对数强度的变化。」[117]


有关 Event Camera 的功能、实时三维重建和 6-DoF 跟踪的说明,请参阅报告原文的附加资料 [118]。


使用单视点进行实时图像绘制和深度估计时,这种方法的表现令人难以置信:


「我们提出了一种方法,它可以在仅利用一个简单的手持式 Event Camera 而不使用其他外加传感的情况下进行实时三维重建,并且可以在无先验知识的非结构化场景下工作。」


Unsupervised CNN for Single View Depth Estimation: Geometry to the Rescue[119] 提出了一种无监督学习方法,训练深度 CNN 进行单视点预测,其结果可与利用监督学习方法的 SOTA 相比。用于单视点深度预测的传统深度 CNN 方法需要大量人工标注的数据,但是无监督方法却无需如此,这证明了无监督方法的价值。作者使用立体声装置(stereo-rig),「通过类似于自编码器」的方式实现了网络训练。


其他未分类的三维重建


IM2CAD[120] 描述了「将图形转换为 CAD 模型」的过程。CAD 是指计算机辅助设计,是为建筑测绘、工程设计、产品设计等领域创建三维场景的主要方法。


「给定一个房间的照片和大型的家具 CAD 模型数据库,我们的任务就是重建一个尽可能与照片相似的场景,其中组成场景的对象都从数据库中提取。」


原论文作者提出了一个自动系统,可以「迭代地优化对象的位置和尺度」,从而对来自真实图像的输入进行最佳匹配。绘制的场景通过使用深度 CNN 作为度量方法验证与原始图像的差别。


图 15:IM2CAD 渲染卧室场景的例子,其中左侧为真实的输入图像,右为根据输入图像和 IM2CAD 自动创建的 CAD 模型。来源 Izadinia et al. (2016, p. 10) [121]。


为何关注 IM2CAD?


作者所处理的问题是 Lawrence Roberts 在 1963 年展示的首进步创技术之一,意义重大。尽管对象是非常简单的线条图,它可以在使用已知对象数据库的情况下推断 3D 场景。


「虽然 Robert 的方法很有远见,但在计算机视觉超过半个世纪的后续研究中,还是没能使他的方法走向实际发展,即没能在真实图像和场景之间可靠地工作。」


本文作者介绍了这个问题的一个变形,旨在利用「3D 目标模型数据库中的对象」,从照片当中重建高保真的场景。


IM2CAD 的流程主要涉及和包含以下部分:


  • 一个进行了端到端训练的全卷积网络,用于寻找用于空间几何估计的几何特征。
  • 用于目标检测的 Faster R-CNN。
  • 在找到图片内的对象之后,找寻 ShapeNet 库中与检测对象最接近的 CAD 模型,完成 CAD 模型对齐。例如,在给定形状和近似的姿态之后,找到椅子的类型。每个 3D 模型都会被渲染到 32 个视点,然后利用深度特征,将其与对象检测生成的边界框相比较
  • 场景中的对象布置
  • Finally Scene Optimization 通过优化渲染场景的摄像机视图与输入图片的视觉相似性,进行对象布置的进一步优化。


同样地,在这个领域中,ShapeNet 意义重大:


「首先,我们利用 ShapeNet,其中包含上百万个对象的 3D 模型,包括上千种不同的桌子、椅子和其他家居用品。这个数据集是使 3D 场景理解研究改变的关键,也是使得我们的工作成为可能的关键。」


  • Learning Motion Patterns in Videos[123] 使用合成视频序列训练网络,提出了一种解决测量独立于相机移动的对象移动的方式。「我们方案的核心是全卷积网络,它完全通过合成视频序列、标注视觉流和运动分割来学习。」作者在新的移动对象分割数据集 DAVIS [124] 中测试了他们的方式,同样也在伯克利的移动分割数据集中进行了测试。测试显示,该方案在两个数据集中都实现了 SOTA。
  • Deep Image Homography Estimation[125] 来自 Magic Leap,一个进行计算机视觉和混合视觉研究的美国初创团队。作者将单应性(homography)估计的任务重新归类为「学习问题」,并且提出了两种深度 CNN 架构,形成了「HomographyNet:一个直接估算实时单应性参数的回归网络,和一个提供量化单应矩阵分布的分类网络」。


单应性(homography)这个术语来自投影几何,指的是将一个平面映射到另一个平面的一种变换。「从一对图像中估计 2D 单应性是计算机视觉领域的一项基本任务,也是单眼 SLAM 系统中的重要部分。」


作者还提供了从真实图像(例如 MS-COCO)的现有数据集中生成「近无限」数据集的方案,这弥补了深度网络大量的数集需求。他们设法「通过在大型图像数据集中应用随机投影变换,创建近无限多个标记训练样例」。


  • gvnn: Neural Network Library for Geometric Computer Vision[126] 为 Torch(一种机器学习的流行计算框架)引入了一种新的神经网络库。Gvnn 的旨在「消除经典计算机几何视觉和深度学习之间的差距」。Gvnn 库允许开发者在已有的网络和训练方法的基础上加入几何功能。


「在这个工作中,我们依赖原本在空间变换网络上提出的 2D 变换层,提供了进行几何变换的多种新型扩展,它们常用于计算机几何视觉中。」

「通过参数变换来调整图像重建误差,这给学习几何变换的不变性开辟了应用,可被用于地点识别、端到端视觉测量、深度估计和无监督学习等领域。」


结语


在这一整节中,我们广泛了解了 3D 理解领域,主要关注了姿态估计、重建、深度估计和单应性矩阵四部分内容。但是由于字数的限制,还有很多精彩的工作被我们忽略了。因此,我们是在为读者提供一个宝贵的出发点,但不是绝对涵盖了所有领域。


大部分突出的工作可以被归类于几何视觉,它通常处理真实世界数量的测量,比如从图片中直接得出的距离、形状、面积、体积。我们获得的启发是,基于识别的任务和通常关注几何视觉的应用有一些不同,它们更关注高级的语义信息。然而我们发现,三维理解的不同领域往往是密不可分的。


即时定位与地图重建(SLAM)是最大的几何问题之一。研究人员正在考虑,SLAM 是否会成为深度学习所面临的下一个主要问题。很多怀疑所谓深度学习「普适性」的研究者指出了 SLAM 作为算法的重要性与功能:


「视觉 SLAM 算法能够在跟踪摄像机位置和方向的同时,建立世界的 3D 地图。」[127] SLAM 方法的几何估计部分目前不太适合深度学习手段,端到端学习同样也不太可能。SLAM 是机器人学最重要的算法之一,而且它是根据计算机视觉领域大量的输入这一特征来设计的。该技术在谷歌地图、自动驾驶汽车、类似 Google Tango[128] 的 AR 设备、甚至火星漫游车中都有应用。


此外,Tomasz Malisiewicz 提供了著名人士关于这个问题的汇总意见。他们同意「语义性是建立更大、更好的 SLAM 系统所必须的。」[129] 这表明了一种潜在的前景,即深度学习未来可能应用在 SLAM 领域。


我们联系了 Plink and Pointy 的联合创始人 Mark Cummins,他在博士期间钻研 SLAM 技术。对以上问题,他提供了自己的看法:


「利用现有手段,SLAM 的核心几何估计部分得到了很好的处理。但是,高级语义和低级系统组成都能受益于深度学习。尤其是:


深度学习能极大地提高地图语义的质量,即超越姿势和点云,并充分理解地图中不同类型的对象或区域。这对于许多应用而言有着更加强大的功能,并且有助于实现通用的鲁棒性。(例如,通过更好地处理动态对象和环境改变)


在低层次,许多组件可以通过深度学习得到改善。其中主要包括:地点识别、闭环检测、重新定位、针对稀疏 SLAM 方法更好地进行点描述等。


总的来说,SLAM 解决方案的结构可能保持不变,但是在组件上可能有一些改进。「大家可能希望用深度学习做一些全新的、根本上的改变,例如完全扔掉几何图形、建立更基于识别的导航系统。但是对于目标是精确几何地图的系统而言,SLAM 中的深度学习可能更倾向于改进组件,而不是做一些全新的事情。」


总而言之,我们相信 SLAM 不太可能完全被深度学习取代。但是,未来这两种途径很可能变得互补。


原报告地址:http://ift.tt/2xEEkTk


]]> 原文: http://ift.tt/2BQ0qAm
RSS Feed

机器知心

IFTTT

分布式TensorFlow入坑指南:从实例到代码带你玩转多机器深度学习

通过多 GPU 并行的方式可以有很好的加速效果,然而一台机器上所支持的 GPU 是有限的,因此本文介绍了分布式 TensorFlow。分布式 TensorFlow 允许我们在多台机器上运行一个模型,所以训练速度或加速效果能显著地提升。本文简要介绍了分布式 TensorFlow 的原理与实践,希望能为准备入坑分布式训练的读者提供简要的介绍。


不幸的是,关于分布式 TensorFlow 的官方文档过于简略。我们需要一个稍微易懂的介绍,即通过 Jupyter 运行一些基本例子。


如果你想交互式地使用 Jupyter,可以在 GitHub 上找到源代码。此外,本文的一些解释是作者自己对实证结果或 TensorFlow 文档的解释,因此可能会有一些小误差。


GitHub 地址:http://ift.tt/2jEcSwk


简介


import tensorflow as tf


比方说,我们希望多个进程共享一些共同的参数。为了简单起见,假设这只是一个单一的变量:


var = tf.Variable(initial_value=0.0)

第一步,我们需要为每个进程创建自己的会话。(假设 sess1 在一个进程中创建,而 sess2 会在另一个进程中创建)。


sess1 = tf.Session() sess2 = tf.Session() sess1.run(tf.global_variables_initializer()) sess2.run(tf.global_variables_initializer())

每次调用 tf.Session() 都会创建一个单独的「执行引擎」,然后将会话句柄连接到执行引擎。执行引擎是实际存储变量值并运行操作的东西。且 Python 天生是面向对象的编程,它里面的元素都是类或对象,因此更正式地说,tf.Seesio() 是 TensorFlow 中的一个方法,它会打开一个会话并运行计算图。


通常,不同进程中的执行引擎是不相关的。在一个会话中更改变量(在一个执行引擎上)不会影响其他会话中的变量。


print("Initial value of var in session 1:", sess1.run(var)) print("Initial value of var in session 2:", sess2.run(var)) sess1.run(var.assign_add(1.0)) print("Incremented var in session 1") print("Value of var in session 1:", sess1.run(var)) print("Value of var in session 2:", sess2.run(var))

上面代码块的输出结果为:


Initial value of var in session 1: 0.0 Initial value of var in session 2: 0.0 Incremented var in session 1 Value of var in session 1: 1.0 Value of var in session 2: 0.0

对于分布式 TensorFlow,我们首先需要了解它的基本原理。以下代码展示了如何构建一个最简单 TensorFlow 集群,以帮助我们理解它的基本原理。


import tensorflow as tf c = tf.constant("Hello, Distributed TensorFlow!") # 创建一个本地TensorFlow集群 server = tf.train.Server.create_local_server() # 在集群上创建一个会话 sess = tf.Session(server.target) print(sess.run(c))

在以上代码中,我们先通过 tf.train.Server.create_local_server 在本地创建一个只有一台机器的 TensorFlow 集群。然后在集群上生成一个会话,通过该对话,我们可以将创建的计算图运行在 TensorFlow 集群上。虽然这只是一个单机集群,但它基本上反映了 TensorFlow 集群的工作流程。


TensorFlow 集群会通过一系列任务(task)来执行计算图中的运算,一般来说不同的任务会在不同的机器上运行。TensorFlow 集群中的任务也会被聚集为工作(job)。例如在训练深度模型时,一台运行反向传播的机器是一个任务,而所有运行反向传播的集合是一个工作。上面简单的案例只是一个任务的集群,若一个 TensorFlow 集群有多个任务时,我们需要使用 tf.train.ClusterSpec 来指定每一个任务的机器。


使用分布式 TensorFlow 训练深度学习模型一般有两种方式,即 in-graph replication 和 between-graph replication。第一种计算图内的分布式会令所有任务都使用一个 TensorFlow 计算图中的变量,而只是将计算部分分配到不同的服务器上。而另一种计算图间的分布式会在每一个计算服务器上创建一个独立的 TensorFlow 计算图,但不同计算图中的相同参数需要以一种固定的方式存放到同一个参数服务器中。以上大概就是分布式 TensorFlow 的基本概念,随后我们将通过具体的案例与代码加深这一部分的理解。


分布式 TensorFlow


为了在进程之间共享变量,我们需要将不同的执行引擎连接在一起,并输入分布式张量流。


若使用分布式 TensorFlow,每个进程会运行一个特殊的执行引擎:一个 TensorFlow 服务器。服务器作为集群的一部分链接在一起。(群集中的每个服务器也称为任务。)


第一步是定义集群的规模。我们从最简单的集群开始:即两台服务器(两个任务),它们都在同一台机器上,一个在 2222 端口,一个在 2223 端口。


tasks = ["localhost:2222", "localhost:2223"]

每个任务都与「工作」(job)相关联,该工作是相关任务的集合。我们将这两个任务与一个称为「local」的工作相关联。


jobs = {"local": tasks}

所有这些即定义为一个集群。


cluster = tf.train.ClusterSpec(jobs)

我们现在可以启动服务器,指定每个服务器对应为集群定义中的哪个服务器。立即启动各服务器,监听集群设置中指定的端口。


# "This server corresponds to the the first task (task_index=0) # of the tasks associated with the 'local' job." server1 = tf.train.Server(cluster, job_name="local", task_index=0) server2 = tf.train.Server(cluster, job_name="local", task_index=1)

将服务器连接在同一个集群中,我们现在可以体验到分布式 TensorFlow 的强大功能:任何具有相同名称的变量都将在所有服务器之间共享。


最简单的例子是在所有的服务器上运行同一张静态计算图,且每个图只有一个变量:


tf.reset_default_graph() var = tf.Variable(initial_value=0.0, name='var') sess1 = tf.Session(server1.target) sess2 = tf.Session(server2.target)

现在,在一台服务器上对变量所作的修改将在第二台服务器上作镜像处理。


sess1.run(tf.global_variables_initializer()) sess2.run(tf.global_variables_initializer()) print("Initial value of var in session 1:", sess1.run(var)) print("Initial value of var in session 2:", sess2.run(var)) sess1.run(var.assign_add(1.0)) print("Incremented var in session 1") print("Value of var in session 1:", sess1.run(var)) print("Value of var in session 2:", sess2.run(var)) Initial value of var in session 1: 0.0 Initial value of var in session 2: 0.0 Incremented var in session 1 Value of var in session 1: 1.0 Value of var in session 2: 1.0

请注意,因为我们只有一个变量且该变量由两个会话共享,第二个会话再调用 global_variables_initializer 就有些多余。


存放


现在我们可能会想:变量究竟存储在哪个服务器上?又是哪个服务器在运行操作?


按经验来说,变量和操作都默认存储在集群的第一个任务上。


def run_with_location_trace(sess, op):  # From http://ift.tt/2B5j22p  run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)  run_metadata = tf.RunMetadata()  sess.run(op, options=run_options, run_metadata=run_metadata)  for device in run_metadata.step_stats.dev_stats:    print(device.device)    for node in device.node_stats:      print("  ", node.node_name)

例如,如果我们使用连接到第一个任务的会话来处理变量 var,那么所有操作都会运行在这个任务上:


run_with_location_trace(sess1, var) /job:local/replica:0/task:0/device:CPU:0 _SOURCE var run_with_location_trace(sess1, var.assign_add(1.0)) /job:local/replica:0/task:0/device:CPU:0 _SOURCE AssignAdd_1/value var AssignAdd_1

但是,如果我们尝试使用连接到第二个任务的会话处理变量 var,那么图节点仍然会在第一个任务上运行。


run_with_location_trace(sess2, var) /job:local/replica:0/task:1/device:CPU:0 _SOURCE /job:local/replica:0/task:0/device:CPU:0 _SOURCE var

要将一个变量或操作固定到特定任务上,我们可以使用 tf.device:


with tf.device("/job:local/task:0"):  var1 = tf.Variable(0.0, name='var1') with tf.device("/job:local/task:1"):  var2 = tf.Variable(0.0, name='var2') # (This will initialize both variables) sess1.run(tf.global_variables_initializer()) 现在,var1 像之前一样运行在第一个任务上。 run_with_location_trace(sess1, var1) /job:local/replica:0/task:0/device:CPU:0 _SOURCE var1

但是 var2 运行在第二个任务上。即使我们尝试使用连接到第一个任务的会话来评估它,它仍然在第二个任务上运行。


run_with_location_trace(sess1, var2) /job:local/replica:0/task:0/device:CPU:0 _SOURCE /job:local/replica:0/task:1/device:CPU:0 _SOURCE var2

变量 2 亦是如此。


run_with_location_trace(sess2, var2) /job:local/replica:0/task:1/device:CPU:0 _SOURCE var2 run_with_location_trace(sess2, var1) /job:local/replica:0/task:1/device:CPU:0 _SOURCE /job:local/replica:0/task:0/device:CPU:0 _SOURCE var1


计算图


分布式 TensorFlow 处理图的过程有几点需要注意。


谁构建了这个图?首先,尽管在整个集群中共享变量值,但图并不会自动共享。我们用两台服务器创建一个新的集群,然后用显式创建的图设置第一台服务器。


cluster = tf.train.ClusterSpec({"local": ["localhost:2224", "localhost:2225"]}) server1 = tf.train.Server(cluster, job_name="local", task_index=0) server2 = tf.train.Server(cluster, job_name="local", task_index=1) graph1 = tf.Graph() with graph1.as_default():  var1 = tf.Variable(0.0, name='var') sess1 = tf.Session(target=server1.target, graph=graph1) print(graph1.get_operations()) [, , , ]

如果我们创建连接到第二台服务器的会话,请注意图不会自动获取镜像。


graph2 = tf.Graph() sess2 = tf.Session(target=server2.target, graph=graph2) print(graph2.get_operations()) ———————————————————————————— []

要访问共享变量,我们必须手动添加一个同名的变量到第二个图中。


with graph2.as_default():  var2 = tf.Variable(0.0, name='var') 只有如此我们才可以访问它。 sess1.run(var1.assign(1.0)) sess2.run(var2) ———————————————————————————— 1.0

关键是:每个服务器负责创建自己的图。


所有服务器上的图都必须一样吗?


到目前为止,我们所有的例子都是在两台服务器上运行相同的图。这被称为图内复制(in-graph replication)。


例如,假设我们有一个包含三台服务器的集群。服务器 1 保存共享参数,而服务器 2 和服务器 3 是工作站节点,每个都有本地变量。在图内复制中,每台服务器的图如下所示:



图内复制的问题在于每个服务器都必须具有整个图的副本,包括可能只与其他服务器相关的子图。这可能会导致图变得非常大。


另一种方法是图间复制(between-graph replication)。在这里,每个服务器都运行一个只包含共享参数的图,而且任何变量和操作都与单个服务器相关。



这种方法缩减了图的大小,因此我们推荐使用图间复制。


实践细节


在介绍完整示例之前,有几个实践中遇到的细节问题需要讨论一下。


如果在所有服务器互联之前尝试在集群上运行某些程序,会发生什么?我们再次创建一个双任务集群。


cluster = tf.train.ClusterSpec({  "local": ["localhost:2226", "localhost:2227"] })

这一次,让我们在隔离进程中启动每个服务器。(这允许我们随时关闭服务器,以便再次启动它们进行后续的实验。除了关闭启动服务器的进程之外,目前没有其它办法关闭服务器。)


from multiprocessing import Process from time import sleep def s1():  server1 = tf.train.Server(cluster,                            job_name="local",                            task_index=0)  sess1 = tf.Session(server1.target)  print("server 1: running no-op...")  sess1.run(tf.no_op())  print("server 1: no-op run!")  server1.join() # Block def s2():  for i in range(3):      print("server 2: %d seconds left before connecting..."            % (3 - i))      sleep(1.0)  server2 = tf.train.Server(cluster,                            job_name="local",                            task_index=1)  print("server 2: connected!")  server2.join() # Block # daemon=True so that these processes will definitely be killed # when the parent process restarts p1 = Process(target=s1, daemon=True) p2 = Process(target=s2, daemon=True)

服务器 1 即刻加入集群,但服务器 2 在连接之前等待了一会儿。结果如下所示:


p1.start() p2.start() server 2: 3 seconds left before connecting... server 1: running no-op... server 2: 2 seconds left before connecting... server 2: 1 seconds left before connecting... server 2: connected! server 1: no-op run!

可以看出,每个服务器都试图在集群上运行一个操作,直到所有的服务器都加入。


p1.terminate() p2.terminate()

当服务器脱离集群会怎样?

我们用两台服务器建立一个集群。服务器 1 只是反复尝试和运行位于服务器 1 上的 no-op 操作。服务器 2 将在两秒钟后宕机。


def s1():  server1 = tf.train.Server(cluster,                            job_name="local",                            task_index=0)  with tf.device("/job:local/task:0"):      no_op = tf.no_op()  sess1 = tf.Session(server1.target)  for _ in range(6):      print("Server 1: about to run no-op...", end="")      sess1.run(no_op)      print("success!")      sleep(1.0) def s2():  server2 = tf.train.Server(cluster,                            job_name="local",                            task_index=1)  sleep(2.0)  print("Server 2 dieing...") p1 = Process(target=s1, daemon=True) p2 = Process(target=s2, daemon=True) p1.start() p2.start() ———————————————————————————————— Server 1: about to run no-op...success! Server 1: about to run no-op...success! Server 2 dieing... Server 1: about to run no-op...success! Server 1: about to run no-op...success! Server 1: about to run no-op...success! Server 1: about to run no-op...success!

短期内,只要我们试图运行的操作不在脱离的服务器上,似乎不会出现问题(我没有测试过长期运行会发生什么)。如果操作是在脱离的服务器上:


def s1():  server1 = tf.train.Server(cluster,                            job_name="local",                            task_index=0)  # This time, we place the no-op on server 2,  # which is going to leave  with tf.device("/job:local/task:1"):      no_op = tf.no_op()  sess1 = tf.Session(server1.target)  for _ in range(5):      print("Server 1: about to run no-op...", end="")      sess1.run(no_op)      print("success!")      sleep(1.0) p1 = Process(target=s1, daemon=True) p2 = Process(target=s2, daemon=True) p1.start() p2.start() —————————————————————————————————— — Server 1: about to run no-op...success! Server 1: about to run no-op...success! Server 2 dieing...

然后尝试运行操作代码。


p1.terminate() p2.terminate()

如果服务器又加入集群会怎样?


p1 = Process(target=s1, daemon=True) p2 = Process(target=s2, daemon=True) p1.start() p2.start() sleep(3.0) # At this point, server 1 is blocked, and server 2 is dead. print("Restarting server 2...") p2 = Process(target=s2, daemon=True) p2.start() ———————————————————————————— Server 1: about to run no-op...success! Server 1: about to run no-op...success! Server 2 dieing... Restarting server 2... Process Process-7: Traceback (most recent call last): File "/Users/matthew/tensorflow/lib/python3.6/site-packages/tensorflow/python/client/session.py", line 1323, in _do_call  return fn(*args) File "/Users/matthew/tensorflow/lib/python3.6/site-packages/tensorflow/python/client/session.py", line 1302, in _run_fn  status, run_metadata) File "/Users/matthew/tensorflow/lib/python3.6/site-packages/tensorflow/python/framework/errors_impl.py", line 473, in __exit__  c_api.TF_GetCode(self.status.status)) tensorflow.python.framework.errors_impl.AbortedError: Graph handle is not found: 0000000000000001 Server 1: about to run no-op...Server 2 dieing...

系统报了一个 Graph handle is not found 的错误。


因此分布式 TensorFlow 不会自动恢复服务器故障。(如果您对容错有兴趣,请查看 https://www.youtube.com/watch?v=la_M6bCV91M。)


谁负责初始化共享变量?


一种方法是让所有工作站运行 tf.global_variables_initializer()。


但是如果我们想保持代码整洁并且只用一个服务器进行初始化,那么如果有其他服务器在初始化之前尝试使用这些变量,可能会遇到问题。一个解决方案就是让其他工作站等待,直到使用 tf.report_uninitialized_variables 的初始化开始。


def s1():  server1 = tf.train.Server(cluster,                            job_name="local",                            task_index=0)  var = tf.Variable(0.0, name='var')  sess1 = tf.Session(server1.target)  print("Server 1: waiting for connection...")  sess1.run(tf.report_uninitialized_variables())  while len(sess1.run(tf.report_uninitialized_variables())) > 0:      print("Server 1: waiting for initialization...")      sleep(1.0)  print("Server 1: variables initialized!") def s2():  server2 = tf.train.Server(cluster,                            job_name="local",                            task_index=1)  var = tf.Variable(0.0, name='var')  sess2 = tf.Session(server2.target)  for i in range(3):      print("Server 2: waiting %d seconds before initializing..."            % (3 - i))      sleep(1.0)  sess2.run(tf.global_variables_initializer()) p1 = Process(target=s1, daemon=True) p2 = Process(target=s2, daemon=True) p1.start() p2.start() ————————————————————————————————— Server 1: waiting for connection... Server 2: waiting 3 seconds before initializing... Server 1: waiting for initialization... Server 2: waiting 2 seconds before initializing... Server 1: waiting for initialization... Server 2: waiting 1 seconds before initializing... Server 1: waiting for initialization... Server 1: variables initialized! p1.terminate() p2.terminate()


示例


让我们把所学的知识融合到最后一个使用多进程的例子中。


我们将创建:


一个存储单个变量 var 的参数服务器。两个工作站任务(worker task),每个工作站将多次增加变量 var 的值。我们将让参数服务器多输出几次 var 的值,以便查看其变化。


import tensorflow as tf from multiprocessing import Process from time import sleep cluster = tf.train.ClusterSpec({  "worker": [      "localhost:3333",      "localhost:3334",  ],  "ps": [      "localhost:3335"  ] }) def parameter_server():  with tf.device("/job:ps/task:0"):      var = tf.Variable(0.0, name='var')  server = tf.train.Server(cluster,                           job_name="ps",                           task_index=0)  sess = tf.Session(target=server.target)  print("Parameter server: waiting for cluster connection...")  sess.run(tf.report_uninitialized_variables())  print("Parameter server: cluster ready!")  print("Parameter server: initializing variables...")  sess.run(tf.global_variables_initializer())  print("Parameter server: variables initialized")  for i in range(5):      val = sess.run(var)      print("Parameter server: var has value %.1f" % val)      sleep(1.0)  print("Parameter server: blocking...")  server.join() def worker(worker_n):  with tf.device("/job:ps/task:0"):      var = tf.Variable(0.0, name='var')  server = tf.train.Server(cluster,                           job_name="worker",                           task_index=worker_n)  sess = tf.Session(target=server.target)  print("Worker %d: waiting for cluster connection..." % worker_n)  sess.run(tf.report_uninitialized_variables())  print("Worker %d: cluster ready!" % worker_n)  while sess.run(tf.report_uninitialized_variables()):      print("Worker %d: waiting for variable initialization..." % worker_n)      sleep(1.0)  print("Worker %d: variables initialized" % worker_n)  for i in range(5):      print("Worker %d: incrementing var" % worker_n)      sess.run(var.assign_add(1.0))      sleep(1.0)  print("Worker %d: blocking..." % worker_n)  server.join() ps_proc = Process(target=parameter_server, daemon=True) w1_proc = Process(target=worker, args=(0, ), daemon=True) w2_proc = Process(target=worker, args=(1, ), daemon=True)


ps_proc.start() ———————————————————————————— Parameter server: waiting for cluster connection... Parameter server: cluster ready! Parameter server: initializing variables... Parameter server: variables initialized Parameter server: var has value 0.0 Parameter server: var has value 2.0 Parameter server: var has value 4.0 Parameter server: var has value 5.0 Parameter server: var has value 7.0 Parameter server: blocking...


w1_proc.start() ———————————————————————————————— Worker 0: waiting for cluster connection... Worker 0: cluster ready! Worker 0: waiting for variable initialization... Worker 0: variables initialized Worker 0: incrementing var Worker 0: incrementing var Worker 0: incrementing var Worker 0: incrementing var Worker 0: incrementing var Worker 0: blocking...


w2_proc.start() ——————————————————————————————— Worker 1: waiting for cluster connection... Worker 1: cluster ready! Worker 1: waiting for variable initialization... Worker 1: variables initialized Worker 1: incrementing var Worker 1: incrementing var Worker 1: incrementing var Worker 1: incrementing var Worker 1: incrementing var Worker 1: blocking... for proc in [w1_proc, w2_proc, ps_proc]:  proc.terminate()


总结


通过本文,我们了解了:


  • 如何将多个 TensorFlow 执行引擎(运行在不同进程或不同机器上)集成为一个集群,以便共享变量。
  • 如何为变量或操作指定服务器。
  • 图内复制与图间复制。
  • 在所有服务器互联之前或在服务器脱离集群之后在集群上运行操作,会发生什么。
  • 如何等待变量被集群中的另一个任务初始化。


更多信息和实例,请查阅官方文档:http://ift.tt/2nSVHXM


原文链接:http://ift.tt/2mWk2QE


]]> 原文: http://ift.tt/2jEcUUY
RSS Feed

机器知心

IFTTT

继图像识别后,图像标注系统也被对抗样本攻陷!


近日,针对深度学习系统的对抗性样本攻击问题,来自麻省理工学院,加州大学戴维斯分校,IBM Research 和腾讯 AI Lab 的学者在 arXiv 上发表论文提出对于神经网络图像标注系统(neural image captioning system)的对抗样本生成方法。实验结果显示图像标注系统能够很容易地被欺骗。


深度学习系统正在越来越广泛地应用于各种场景中,帮助人类完成许多繁琐的工作。但是在很多方面上,计算机科学家们并不完全理解深度学习的工作机理。最近的研究显示,深度学习系统可能将两张人眼看不出任何差别的图片识别成两个完全不同的物体。这些样本的存在让人们对深度学习系统的鲁棒性提出了质疑。关于对抗样本的研究正在逐渐成为近期深度学习的热点方向之一。


与之前的相关研究不同的是,这一篇由来自麻省理工学院、加州大学戴维斯分校、IBM Research 和腾讯 AI Lab 的学者撰写的论文将对抗样本攻击延伸到了图像标注(image captioning)系统领域。图像标注标系统能从给定的图像中自动生成一段描述性文字。这一技术還能够帮助视觉上有障碍的人了解新闻、社交网站等媒体上的图像的含义。图像标注系统中的对抗样本同样有着严重的危害。想象一下,你的图片经过肉眼无法察觉的改造后,自动标注系统可能会对这张图片生成任何无关、完全相反甚至恶意的描述。


与仅由卷积神经网络(CNN)构成的图像识别分类器相比,图像标注系统由于涉及机器视觉和自然语言生成两个方面,结构更为复杂(Encoder-Decoder 结构,CNN+RNN),并且输出值所在的空间也要更大。在这篇文章中,作者设计了三种对抗样本的生成模式:


(1)有目标攻击(targeted attack):对于一张图片和一个目标标注句子,生成一个对抗样本,使得标注系统在其上的标注与目标标注完全一致;

(2)关键词目标攻击(targeted keyword attack):对于一张图片和一组关键词,生成一个对抗样本,使得标注系统在其上的标注含有所有的关键词;

(3)无目标攻击(untargeted attack):对于一张图片,生成一个对抗样本,使得标注系统在其上的标注与原标注无关。


作者通过一个求解优化问题来生成对抗样本,并设计了新颖的损失函数来实现高效的搜索。该方法成功地在 Show-and-Tell 模型和 MSCOCO 数据集上生成了人眼无法识别的对抗样本,并且这些对抗样本还能够迁移到带有 Show-Attend-and-Tell 模型上。



论文中的有目标对抗样本示例。新的标注都是人为指定的目标,而 Show-and-Tell 给出的标注和目标相同。左图中 Show-and-Tell 模型将一个关于纳达尔的对抗样本标注为了「一名女子正在浴室里刷牙。」;右图中 Show-and-Tell 模型将一个关于停车标志的对抗样本标注为了「一只棕色的泰迪熊躺在床上。」



论文中关键词目标攻击的示例。左图中给定「猫」、「狗」和「飞盘」三个关键词,生成了一个蛋糕图片的对抗样本使得 Show-and-Tell 模型给出了「一只狗和一只猫正在玩飞盘。」的标注。右图中给定「足球」、「群」和「玩」三个关键词,生成了一个长颈鹿图片的对抗样本使得 Show-and-Tell 模型给出了「一群年轻人在玩一个足球的游戏。」的标注。



文中无目标攻击的示例。左图中的披萨饼图片对抗样本使得 Show-and-Tell 生成了「几把雨伞坐在草地上。」的标注。右图中火车图片的对抗样本使得 Show-and-Tell 生成了「几只狗躺在地上」的标注。


给定一张图片 I,文中作者将寻找对抗扰动构造为一个由损失函数 loss 和 2-范数构成的优化问题:



1.有目标攻击


给定一个目标标注句子 S,文中提出了基于未归一化的概率(logits)的损失函数和基于对数概率(log probability)的损失函数形式。


2.关键词目标攻击


给定一组关键词 K,文中也提出了两种损失函数形式,以鼓励所有的关键词都在标注句子中的出现。



3.无目标攻击


文中还给出了两种用于在没有给定任何目标信息时的无目标攻击损失函数,使得对抗样本的标注和原始图片不同。



在 Show-and-Tell 模型和 MSCOCO 数据集上生成有目标攻击和关键词目标攻击对抗样本的成功率和平均 2-范数畸变



在 Show-and-Tell 模型上生成的对抗样本可以迁移到 Show-Attend-and-Tell 模型上,左图中 Show-and-Tell 模型将一个关于浴室的对抗样本标注为「一名男子在冲浪板上冲浪。」,而同一个对抗样本在 Show-Attend-and-Tell 上的标注结果为「一名男子在空中的冲浪板上。」。右图中 Show-and-Tell 模型将一个关于书桌的对抗样本标注为「一只猫躺在浴室里的盥洗台里。」,而同一个对抗样本在 Show-Attend-and-Tell 上的标注结果为「一只猫坐在浴室盥洗台里。」


论文:Show-and-Fool: 为神经网络图像标注系统设计对抗样本(Show-and-Fool: Crafting Adversarial Examples for Neural Image Captioning)



论文地址: http://ift.tt/2BR0QGP


论文摘要:当今典型的深度学习图像标注系统(neural image captioning system)主要采用编码-解码的架构,包含两个主要的部分。其一是一个卷积神经网络(CNN),用于对图像特征的提取。其二是一个递归神经网络(RNN),用于标注句子(caption)的生成。我们从关于卷积神经网络的图像分类器对于对抗扰动(adversarial perturbation)的鲁棒性的分析中获得灵感,提出了名为 Show-and-Fool 算法来生成图像标注系统的对抗样本。与图像分类系统中仅有有限的分类标签不同的是,为图像标注系统生成人眼难以区分的对抗样本更加困难,因为可能的标注句子所在的空间几乎是无限大的。在这篇文章中,作者设计了三种对抗样本的生成模式:(1)有目标攻击(targeted attack)(2)关键词目标攻击(targeted keyword attack)(3)无目标攻击(untargeted attack)。我们通过一个求解优化问题来生成对抗样本,并设计了新颖的损失函数来实现高效的搜索。在 Google 的 Show-and-Tell 模型和 MSCOCO 数据集上该方法能够成功生成人眼难以分辨的对抗样本,使得 Show-and-Tell 模型输出给定标注。并且这些生成的对抗样本能够迁移到带有注意力机制的 Show-Attend-and-Tell 模型上。这些对抗样本的出现显示出对于深度学习图像标注系统鲁棒性分析的必要性。尽我们所知,这是第一个为图像标注系统生成有效对抗样本的工作。


]]> 原文: http://ift.tt/2C02ney
RSS Feed

机器知心

IFTTT

Tensor Core究竟有多快?全面对比英伟达V100/P100的RNN加速能力


RNN 是处理量化金融、风险管理等时序数据的主要深度学习模型,但这种模型用 GPU 加速的效果并不好。本文使用 RNN 与 LSTM 基于 TensorFlow 对比了英伟达 Tesla P100(Pascal)和 V100(Volta)GPU 的加速性能,且结果表明训练和推断过程的加速效果并没有我们预期的那么好。


循环神经网络(RNN)


很多深度学习的应用都涉及到使用时序数据作为输入。例如随时间变化的股价可以作为交易预测算法、收益预测算法的输入而对未来某个时间点的可能状态进行推断。循环神经网络(RNN)非常是适合于建模长期或短期的时间依赖项,因此是本文测试的理想模型。


下图展示了 RNN 中的一个神经元,它不仅是最基础的组成部分,同时还是其它更复杂循环单元的基础。下图可以看出该神经元的输出 y 不仅取决于当前的输入 x,同时还取决于储存的前面状态 W,前面循环的状态也可以称之为反馈循环。正是这种循环,RNN 能够学习到时序相关的依赖性。



如下图所示,RNN 单元可以组织成一个个层级,然后再堆叠这些层级以组织成一个完整的神经网络。



深度循环神经网络


由于梯度消失和爆炸问题,RNN 很难学习到长期依赖关系。这两个问题主要发生在训练时期的反向传播过程中,其中损失函数的梯度由输出向输入反向地计算。由于反馈循环,较小的梯度可能快速消失,较大的梯度可能急剧增加。


梯度消失问题阻止了 RNN 学习长期时间依赖关系,而长短期记忆模型(LSTM)正是 RNN 的一种变体以解决该问题。LSTM 引入了输入门、遗忘门、输入调制门和记忆单元。这允许 LSTM 在输入数据中学习高度复杂的长期依赖关系,因此也十分适用于学习时序数据。此外,LSTM 也可以堆叠多层网络形成更复杂的深度神经网络。


在假定隐藏层具有相同的宽度下,深度 RNN 网络的计算复杂度与采用的层级数成线性缩放关系。因此,单层 RNN 或 LSTM 单元就可以看作是深度 RNN 中的基础构建块,这也就是为什么我们要选择下面这样的层级进行基础测试。


硬件对比


下表展示了英伟达 P100 和 V100 GPU 的关键性能与不同点。



请注意 FLOPs 的计算先假定纯粹的加乘混合(fused multiply-add /FMA)运算指令记为两个运算,即使它们都只映射到一个处理器指令中。


在 P100 上,我们测试的是半精度(FP16)FLOPs。而在 V100 上,我们测试的是张量 FLOPs,它以混合精度的方式在 Tensor Cores 上运行:以 FP16 的精度执行矩阵乘法,而以 FP32 的精度进行累加。


也许 V100 GPU 在深度学习环境下最有意思的硬件特征就是 Tensor Cores,它是能以半精度的方式计算 4×4 矩阵乘法的特定内核,并在一个时钟周期内将计算结果累加到单精度(或半精度)4×4 矩阵中。这意味着一个 Tensor Cores 在每个时钟周期内可以执行 128 FLOPs,并且带有 8 个 Tensor Cores 的 Streaming Multiprocessor 能实现 1024 FLOPs/cycle 的速度。这比常规单精度 CUDA 核要快 8 倍。为了从这种定制化的硬件中获益,深度学习模型应该以混合精度(半精度与单精度)或纯粹以半精度的方式编写,因此才能利用深度学习框架高效地使用 V100Tensor Cores。


TensorFlow


TensorFlow 是一个谷歌维护的开源深度学习框架,它使用数据流图进行数值计算。TensorFlow 中的 Tensor 代表传递的数据为张量(多维数组),Flow 代表使用计算图进行运算。数据流图用「结点」(nodes)和「边」(edges)组成的有向图来描述数学运算。「结点」一般用来表示施加的数学操作,但也可以表示数据输入的起点和输出的终点,或者是读取/写入持久变量(persistent variable)的终点。边表示结点之间的输入/输出关系。这些数据边可以传送维度可动态调整的多维数据数组,即张量(tensor)。


TensorFlow 允许我们将模型部署到台式电脑、服务器或移动设备上,并调用这些设备上的单个或多个 CPU 与 GPU。开发者一般使用 Python 编写模型和训练所需的算法,而 TensorFlow 会将这些算法或模型映射到一个计算图,并使用 C++、CUDA 或 OpenCL 实现图中每一个结点的计算。


从今年 11 月份发布的 TensorFlow 1.4 开始,它就已经添加了对半精度(FP16)这种数据类型的支持,GPU 后端也已经为半精度或混合精度的矩阵运算配置了 V100 Tensor Cores。除了 1.4 这个主线版本外,英伟达还在他们的 GPU Cloud Docker 注册表以 Docker 容器的形式维护了一个定制化和优化后的版本。这个容器目前最新版为 17.11,为了实现更好的性能,我们将使用这个 HGC 容器作为我们的测试基准。


基准测试


我们的基准性能测试使用含有多个神经元的单隐藏层网络架构,其中隐藏层的单元为分别为原版 RNN(使用 TensorFlow 中的 BasicRNNCell)和 LSTM(使用 TensorFlow 中的 BasicLSTMCell)。网络的所有权重会先执行随机初始化,且输入序列因为基准测试的原因而采取随机生成的方式。


我们比较了模型在 Pascal 和 VoltaGPU 上的性能,且系统所使用的配置如下所示:



性能


为了度量性能,我们需要重复执行模型的训练,然后再记录每次运行的时钟长度,直到估计的时间误差低于特定值才停止。性能度量包括完整的算法执行时间(使用梯度下降的时间加上推断的时间),训练的输入为批量大小为 128 的 10 万批数据,且每一个序列长度为 32 个样本。训练过程大概有 1300 万的训练样本,且我们使用重叠的窗口进行序列分析。一个深度学习模型大概会依据 32 个以前样本的状态而预测未来的输出,因此我们修正隐藏层中 RNN/LSTM 单元的数量以执行基线测试。


训练


以下两图展示了 V100 和 P100 GPU 在训练过程中对 RNN 和 LSTM 的加速,这个过程的单精度(FP32)和半精度(FP16)运算都是使用的 NGC 容器。此外,隐藏层单元数也在以下图表中展示了出来。



推断


以下两图展示了 V100 和 P100 GPU 在推断过程中对 RNN 和 LSTM 的加速,这个过程的单精度(FP32)和半精度(FP16)运算都是使用的 NGC 容器。此外,隐藏层单元数也在以下图表中展示了出来。



结语


对于测试过的 RNN 和 LSTM 深度学习模型,我们注意到 V100 比 P100 的相对性能随着网络的规模和复杂度(128 个隐藏单元到 1024 个隐藏单元)的提升而增加。我们的结果表明 V100 相对于 P100 在 FP16 的训练模式下最大加速比为 2.05 倍,而推断模式下实现了 1.72 倍的加速。这些数据比基于 V100 具体硬件规格的预期性能要低很多。


这一令人失望的性能比可能是因为 V100 中强大的 Tensor Cores 只能用于半精度(FP16)或混合精度的矩阵乘法运算。而对这两个模型进行分析的结果表示矩阵乘法仅占 LSTM 总体训练时间的 20%,所占 RNN 总体训练时间则更低。这与擅长于处理图像数据的卷积神经网络形成鲜明对比,它们的运行时间由大量的矩阵乘法支配,因此能更加充分地利用 Tensor Cores 的计算资源。


虽然 V100 与 P100 相比显示出强大的硬件性能提升,但深度学习中擅于处理时序数据的循环神经网络无法充分利用 V100 这种专门化地硬件加速,因此它只能获得有限的性能提升。


原文地址:http://ift.tt/2BufDb9


]]> 原文: http://ift.tt/2kLQc0u
RSS Feed

机器知心

IFTTT

这或许是苹果今年最大一笔收购,4亿美元买下一个音乐识别app究竟为了什么?

编译 | 王宇欣

编辑 | 宇多田

来源 | Techcrunch、Quarz、The Motley Fool


或许那个平常经常在你 iPhone 上开玩笑的 Siri,已经能够弄清楚在嘈杂的酒吧中你正在听的歌曲。


就在今天上午,科技媒体 Techcrunch 独家报道了苹果的一项最新收购计划:


这家全球最有钱的公司正在计划以 3 亿英镑(3.95 亿美元)买下 Shazam,一款音乐识别 app。


根据 Pitchbook 的资料数据来看,截止 2015 年,这家成立了 18 年的公司估值约为 10 亿美元;又据金融时报报道,这次交易或许将成为硅谷巨头收购英国公司的案子中金额最大的一笔。


果然,按照惯例,苹果拒绝对这次爆料发表评论。


不过,在了解为何苹果决定支付这么一大笔钱买下一个音乐识别app之前,我们先科普一下这家知名的「听音识曲」技术公司。


Shazam 大受欢迎的「听音识曲」业务


Shazam 成立于 1999 年,最初提供的业务是一项「电话识曲服务」。人们可以拨打一个电话,然后拿听筒对着一段音乐,一会儿电话里就会告诉你测出的曲名及歌手。


2008 年,随着苹果 App Store 的发布,这家公司在商店里发布了一款 iPhone 的 app,它可以在几秒钟之内将几乎任何歌曲的名称显示出来。




据第三方 app 数据监测平台 App Annie 介绍,这款应用从其发布后就几乎从未掉出 store 上下载量的 Top 100 名单。


Shazam 除了是音乐识别方面的一个重量级角色。这家公司也一直在探索如何利用增强现实(AR)技术将用户与内容相连。说到这里你应该会想到,苹果 CEO 库克也对 AR 技术有强烈推崇。


苹果为什么想要收购 Shazam?


有很多原因可以解释为什么苹果会对 Shazam 感兴趣。


其中最明显的就是 Shazam 可以帮助提升 Apple Music 中的内容发现功能。


想象一下,如果 Apple Music 深入地无缝集成了音乐识别功能,用户可以快速识别在周围背景中播放的音乐,然后将其添加到自己的音乐库中或是根据其内容创建广播电台。


举个例子,如果用户在 Shazam 上发现了 Ed Sheehan 的最新单曲,用户可以让 Siri 在 iTunes 上进行购买,或者将其放置在 Apple Music 的播放列表中。


实际上,Shazam 长期以来的确也一直为数字音乐商店(包括 iTunes)提供流量,在用户掏钱包购买音乐时来赚取「推荐费」。


此外,通过收购 Shazam,苹果公司可能也会收到用户搜索歌曲时产生的大量数据。


这些数据在 iTunes 和 Apple Music 等软件上考虑为用户究竟提供什么样的新内容时十分有益,并且在销售服务的新渠道方面也能给予帮助。


与竞争对手 Spotify 相比,Apple Music 因为不直观而广受诟病。前者在今年早些时候收购了初创公司 Niland,以增强自己的在音乐推荐和发现方面的功能。


特别是 Spotify 的 Discovery Weekly 这个功能,利用机器学习为每个用户定制个性化播放列表,让其备受用户欢迎。(网易云音乐在这方面做的也同样不错)




第二,Siri 数年来一直可以在用户询问时进行歌曲识别,只是达不到 Shazam 那样的效率以及准确度。


使用 Shazam 的音频检测软件不仅可以增强 Siri 的这一功能,而且可以帮助 Siri 更好地理解用户。


值得注意的是,虽然苹果是第一家在其移动软件中内置数字化助手的公司,但由于 Google Assistant 以及亚马逊的 Alexa 等同类软件的面世,Siri 也已经逐渐失去了往日所拥有的竞争力。


经过几个月的推迟之后,就在明年年初,苹果会发布 HomePod,这将是对 Google Home 和亚马逊的 Echo 的正面回应。既然产品价格是竞品们的 3 倍,那么 Siri 的智能化程度与音乐识别的准确率也将成为一个重要评测标准。


由于智能音箱的兴起,音乐界面正在向音控模式发展,这样的趋势使得音乐识别在首次尝试中显得尤为重要。


苹果将支付 9 位数?巨额毋庸置疑


多年来,苹果已经进行多笔与「内容识别」有关的收购。


譬如,2012 年估值约为 5000 万美元的 Chomp(移动应用发现)、2013 年估值在 1000 万到 1500 万美元的 Matcha(视频发现),2014 年估值在 1000 万到 1500 万美元之间的 BookLamp(书籍发现),以及本周刚刚确认将收购博客搜索初创公司 Pop Up Archive(博客发现),但并未公布具体收购金额。


但是,根据苹果收购公司的历史金额,投入巨资并不常见。


3 年前,5 亿美元曾是苹果公司在收购方面的金额上限,这也是为何 2014 年苹果以 30 亿美元的价格收购 Beats 令世人惊愕。


从那时起,库克就多次重申,价格从来就不是苹果公司考虑收购时的阻碍,目标的战略意义才是关键。


库克曾表示:「以 10 位数的价格收购值得争取的公司,我们欣然接受,这对苹果公司的长远利益来说是正确的;如果不是,1 分钱都不会出。」


而现在收购 Shazam,据消息爆料苹果将为此支付高达 9 位数的巨额资金。不过根据苹果当下的收购标准来看,这个收购价格并不贵。


而且,音乐识别功能对音乐流媒体的服务,远比独家视频内容来的重要。


]]> 原文: http://ift.tt/2A6z9cz
RSS Feed

机器知心

IFTTT

LangChain 彻底重写:从开源副业到独角兽,一次“核心迁移”干到 12.5 亿估值 -InfoQ 每周精要No.899期

「每周精要」 NO. 899 2025/10/25 头条 HEADLINE LangChain 彻底重写:从开源副业到独角兽,一次"核心迁移"干到 12.5 亿估值 精选 SELECTED 1000 行代码手搓 OpenAI gpt-oss 推理引...