Hadoop-深度学习-全-

Hadoop 深度学习(全)

原文:annas-archive.org/md5/fb21c7d21fa111e9900de84c112a1378

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

本书将教你如何使用 Hadoop 在深度神经网络中部署大规模数据集,以实现最佳性能。

本书从理解深度学习是什么开始,并介绍与深度神经网络相关的各种模型,接着展示如何为深度学习设置 Hadoop 环境。

本书内容

第一章,深度学习简介,讲述了深度学习在过去十年如何获得流行,并且由于其增强的功能,现正比机器学习增长得更快。本章首先介绍了人工智能在实际生活中的应用、相关挑战以及深度学习如何有效地应对这些挑战。章节深入解释了深度学习,并解决了一些主要的机器学习问题,如维度灾难、梯度消失问题等。为了为后续章节的深度学习打下基础,本章后半部分讨论了各种深度学习网络的分类。本章主要适合那些希望了解深度学习基础,而不深入研究具体深度神经网络的读者。

第二章,大规模数据的分布式深度学习,解释了大数据和深度学习无疑是近年来最热门的技术趋势。它们是紧密相连的,并且在过去几年中表现出了巨大的增长。本章首先讨论了如何通过深度学习技术结合大量非结构化数据,以便从中提取有价值的隐藏信息。像 Google、Facebook、Apple 等著名科技公司正在使用这些大规模数据,在他们的深度学习项目中训练更智能的深度神经网络。然而,深度神经网络在处理大数据时也面临一些挑战。本章详细解释了这些挑战。章节后半部分介绍了 Hadoop,讨论如何使用 Hadoop 的 YARN 及其迭代式 Map-reduce 范式来实现深度学习模型。章节进一步介绍了 Deeplearning4j,这是一款流行的开源分布式深度学习框架,并解释了其各个组成部分。

第三章,卷积神经网络,介绍了卷积神经网络(CNN),这是一种深度神经网络,广泛应用于各大科技行业的深度学习项目中。CNN 在图像识别、视频识别、自然语言处理等多个领域都有着广泛的应用。卷积,作为一种特殊的数学运算,是 CNN 的核心组成部分。为了入门,本章首先通过一个实际例子讨论卷积的概念。接着,本章通过描述网络的各个组件,提供了卷积神经网络的深入解释。为了提高网络的性能,CNN 有三个最重要的参数,即稀疏连接、参数共享和等变表示。本章详细解释了这些参数,以帮助更好地理解 CNN。此外,CNN 还具有一些关键的超参数,用于决定网络输出体积的维度。本章对这些超参数之间的数学关系进行了详细讨论。章节的后半部分聚焦于分布式卷积神经网络,并展示了如何使用 Hadoop 和 Deeplearning4j 实现它。

第四章,循环神经网络,解释了它是一种特殊类型的神经网络,可以处理长序列的向量并生成不同的向量序列。近年来,它们已成为建模可变长度序列的极受欢迎的选择。RNN 已成功应用于语音识别、在线手写识别、语言建模等多个领域。本章通过提供必要的数学关系和视觉表示,详细解释了 RNN 的各种概念。RNN 具有自己的记忆,用于存储中间隐藏层的输出。记忆是循环神经网络的核心组件,本章中以适当的框图进行了讨论。此外,本章还提供了单向循环神经网络的局限性,并引入了双向循环神经网络(BRNN)的概念,以克服这一问题。接下来,为了解决第一章中提出的梯度消失问题,讨论了一种 RNN 的特殊单元——长短时记忆(LSTM)。最后,展示了如何使用 Hadoop 和 Deeplearning4j 实现分布式深度循环神经网络。

第五章 ,限制玻尔兹曼机,涵盖了第三章和第四章讨论的模型,并解释它们是判别模型。在第五章中讨论了一种生成模型叫做限制玻尔兹曼机(RBM)。当给定隐藏参数时,RBM 能够随机生成可见数据值。本章以介绍基于能量的模型为起点,阐述了限制玻尔兹曼机与之相关的内容。进一步讨论了一种特殊类型的 RBM,称为卷积限制玻尔兹曼机,它是卷积和限制玻尔兹曼机的结合体,并有助于提取高维图像的特征。

深度置信网络(DBN)是一种广泛使用的多层网络,由多个限制玻尔兹曼机组成,出现在本章的后半部分。本部分还讨论了如何在分布式环境中使用 Hadoop 实现 DBN。章节的最后部分讨论了在 Deeplearning4j 中实现 RBM 以及分布式 DBN 的实现。

第六章 ,自编码器,引入了另一种生成模型称为自编码器,通常用于降维、特征学习或提取。本章首先解释了自编码器的基本概念及其通用块图。自编码器的核心结构基本分为两部分:编码器将输入映射到隐藏层,而解码器将隐藏层映射到输出层。基本自编码器的主要目标是将输入层的某些方面复制到输出层。本章的下一部分讨论了一种称为稀疏自编码器的类型,它基于隐藏层的分布稀疏表示。进一步讨论了深度自编码器的概念,包括多个编码器和解码器,通过适当的例子和块图进行了深入说明。随着我们的进展,还解释了去噪自编码器和堆叠去噪自编码器。最后,第六章还展示了在 Hadoop 中使用 Deeplearning4j 实现堆叠去噪自编码器和深度自编码器的方法。

第七章,使用 Hadoop 进行的各种深度学习操作,主要集中在分布式环境中最常用的三种机器学习应用的设计上。本章讨论了大规模视频处理、大规模图像处理和自然语言处理(NLP)在 Hadoop 中的实现。它解释了如何将大规模视频和图像数据集部署到 Hadoop 分布式文件系统(HDFS)中,并使用 Map-reduce 算法进行处理。对于 NLP,本章末尾对设计和实现进行了深入的解释。

本书所需的内容

我们期望本书的所有读者具备一定的计算机科学背景。本书主要讨论不同的深度神经网络、它们的设计及其在 Deeplearning4j 中的应用。为了最大限度地从本书中获得收益,读者需要了解机器学习基础、线性代数、概率论、分布式系统和 Hadoop 的概念。对于使用 Hadoop 实现深度神经网络,本书中广泛使用了 Deeplearning4j。以下是运行 Deeplearning4j 所需的所有资源链接:

deeplearning4j.org/quickstart

本书适用对象

如果你是一位希望学习如何在 Hadoop 上进行深度学习的数据科学家,那么这本书非常适合你。需要掌握基本的机器学习概念,并对 Hadoop 有一定了解,以便充分利用本书的内容。

约定

本书中包含多种文本样式,用于区分不同类型的信息。以下是这些样式的一些示例及其含义的解释:

文本中的代码词汇、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟网址、用户输入和 Twitter 用户名如下所示:“.build() 函数用于构建层。”

代码块以如下格式展示:

public static final String DATA_URL =     
  "http://ai.stanford.edu/~amaas/data/sentiment/*";

当我们希望引起你对代码块中特定部分的注意时,相关行或项将以粗体显示:

MultiLayerNetwork model = new MultiLayerNetwork(getConfiguration());
Model.init();

新术语重要词汇 以粗体显示。你在屏幕上看到的词汇,例如在菜单或对话框中显示的内容,会以这样的形式出现在文本中:“简单来说,任何有两层或更多层(隐藏层)的神经网络都被定义为深度前馈网络前馈神经网络。”

注意

警告或重要注意事项会以框的形式出现:

提示

提示和技巧以这样的形式出现。

读者反馈

我们始终欢迎读者的反馈。告诉我们您对本书的看法——您喜欢或不喜欢的地方。读者反馈对我们来说非常重要,因为它帮助我们开发出真正能让您从中受益的书籍。要向我们提供一般反馈,只需发送电子邮件至feedback@packtpub.com,并在邮件主题中提到书名。如果您在某个领域具有专业知识,并且有兴趣撰写或参与编写一本书,请参阅我们的作者指南:www.packtpub.com/authors

客户支持

现在您是一本 Packt 书籍的自豪拥有者,我们提供许多帮助您最大化利用购买的资源。

下载示例代码

您可以从您的帐户下载本书的示例代码文件,网址是www.packtpub.com。如果您是在其他地方购买的此书,您可以访问www.packtpub.com/support,并注册将文件直接通过电子邮件发送给您。

您可以按照以下步骤下载代码文件:

  1. 使用您的电子邮件地址和密码登录或注册我们的网站。

  2. 将鼠标指针悬停在顶部的SUPPORT选项卡上。

  3. 点击代码下载 & 勘误表

  4. 搜索框中输入书名。

  5. 选择您希望下载代码文件的书籍。

  6. 从下拉菜单中选择您购买此书的地方。

  7. 点击代码下载

文件下载完成后,请确保使用以下最新版本解压或解压文件夹:

  • Windows 的 WinRAR / 7-Zip

  • Mac 的 Zipeg / iZip / UnRarX

  • Linux 的 7-Zip / PeaZip

本书的代码包也托管在 GitHub 上,网址是github.com/PacktPublishing/Deep-Learning-with-Hadoop。我们还有其他书籍和视频的代码包,您可以在github.com/PacktPublishing/查看。

下载本书的彩色图片

我们还为您提供了一个 PDF 文件,包含本书中使用的截图/图表的彩色图像。这些彩色图像将帮助您更好地理解输出结果的变化。您可以从www.packtpub.com/sites/default/files/downloads/DeepLearningwithHadoop_ColorImages.pdf下载该文件。

勘误表

尽管我们已尽一切努力确保内容的准确性,但错误难免发生。如果您在我们的一本书中发现错误——可能是文本或代码中的错误——我们将非常感激您能将其报告给我们。通过这样做,您可以帮助其他读者避免困扰,并帮助我们改进后续版本的书籍。如果您发现任何勘误,请访问www.packtpub.com/submit-errata,选择您的书籍,点击勘误提交表格链接,并填写您的勘误详情。经核实后,您的勘误将被接受,并上传到我们的网站或添加到该书籍标题下的现有勘误列表中。

要查看之前提交的勘误,请访问www.packtpub.com/books/content/support并在搜索框中输入书名。所需信息将在勘误部分下显示。

盗版

网络上盗版受版权保护的材料一直是所有媒体面临的持续问题。在 Packt,我们非常重视版权和许可证的保护。如果您在互联网上发现我们作品的任何非法复制形式,请立即向我们提供相关的位置信息或网站名称,以便我们采取补救措施。

请通过copyright@packtpub.com联系我们,并附上涉嫌盗版材料的链接。

我们感谢您在保护作者权益和确保我们能够为您提供有价值内容方面的帮助。

问题

如果您对本书的任何内容有问题,可以通过questions@packtpub.com联系我们,我们将尽力解决您的问题。

第一章 深度学习简介

“迄今为止,人工智能最大的危险在于人们过早地认为自己理解了它。”
--埃利泽·尤德科夫斯基

是否曾想过,为什么即使是游戏中最优秀的棋手,也常常很难战胜计算机?Facebook 是如何在数亿张照片中识别你的面孔的?你的手机是如何识别你的声音,并将电话转接到正确的人那里,尽管通讯录中有成百上千个联系人?

本书的主要目标是解决许多相关问题,并为读者提供详细的解决方案。本书适用于多种不同的读者和用途,然而,我们在撰写本书时主要针对两类受众。一类是学习深度学习和人工智能的本科生或研究生;另一类是已经具备大数据、深度学习和统计建模知识的软体工程师,但他们希望迅速了解如何将深度学习应用于大数据,反之亦然。

本章将主要通过提供深度学习的基本概念、术语、特性以及主要挑战,为读者奠定基础。本章还将提出深度网络算法的分类,这些算法在过去十年里被研究人员广泛使用。本章将涵盖以下主要内容:

  • 开始学习深度学习

  • 深度学习术语

  • 深度学习:人工智能的革命

  • 深度学习网络的分类

自人类文明的曙光以来,人们一直梦想着创造能够像人类一样工作和行为的人工机器或机器人。从希腊神话人物到古代印度史诗中,都有许多类似的例子,明显表明了人们对创造和拥有人工生命的兴趣和倾向。

在计算机的初期发展阶段,人们一直在想,计算机是否有可能像人类一样智能!随着时间的推移,即使在医学科学领域,自动化机器的需求也变得不可或缺,几乎是无法避免的。随着这种需求的不断增加以及相关领域的持续研究,人工智能AI)已经成为一项蓬勃发展的技术,并在多个领域得到了广泛应用,例如图像处理、视频处理以及医学科学中的许多诊断工具。

虽然人工智能系统每天解决着许多问题,但没有人知道人工智能系统是如何被编程的!以下是一些直观的问题:

  • Google 搜索,能够非常好地理解你输入或说出的内容

  • 如前所述,Facebook 在识别你的面孔方面也颇有成效,因此也能了解你的兴趣。

此外,随着概率、线性代数、统计学、机器学习、深度学习等各个领域的融合,人工智能在研究领域中已经获得了巨大的关注和流行。

人工智能早期成功的一个关键原因可能是,它主要处理的是计算机无需大量知识即可解决的基本问题。例如,1997 年,IBM 的深蓝象棋系统能够击败世界冠军加里·卡斯帕罗夫[1]。尽管当时这种成就可以算得上是重要的,但训练计算机仅用象棋的有限规则,绝对不算是一项繁重的任务!用固定且有限的规则训练系统被称为计算机的硬编码知识。许多人工智能项目都经过了这一硬编码知识,涵盖了许多传统语言中关于世界各方面的内容。随着时间的推移,这种硬编码知识似乎无法应对处理大量数据的系统。此外,数据遵循的规则数量也频繁变化。因此,大多数基于这种系统的项目未能达到预期的高度。

这一硬编码知识的挫折表明,人工智能系统需要某种方式,从提供的原始数据中泛化出模式和规则,而不需要外部的强制输入。系统做到这一点的能力被称为机器学习。我们日常生活中有许多成功的机器学习应用。以下是一些最常见和最重要的应用:

  • 垃圾邮件检测:给定你收件箱中的一封电子邮件,模型可以检测是否将该电子邮件归类为垃圾邮件或收件箱邮件。一种常见的朴素贝叶斯模型可以区分这类邮件。

  • 信用卡欺诈检测:一个模型可以检测在特定时间间隔内进行的一系列交易是否由原始客户执行。

  • 1990 年,Mor-Yosef 等人提出了最受欢迎的机器学习模型之一,该模型使用了逻辑回归,可以推荐是否需要为患者进行剖腹产!

有许多这样的模型是通过机器学习技术实现的。

深度学习介绍

图 1.1:该图展示了不同类型表示法的示例。假设我们想训练机器检测果冻豆之间的空隙。在右侧的图像中,我们看到果冻豆分布稀疏,AI 系统更容易确定空白部分。然而,在左侧的图像中,果冻豆分布极为紧密,因此,机器要找到空隙将是一个极为困难的任务。图像来源于 USC-SIPI 图像数据库。

机器学习系统的性能很大一部分取决于输入给系统的数据。这被称为数据的表示。与表示相关的所有信息都被称为数据的特征。例如,如果使用逻辑回归来检测患者的脑肿瘤,AI 系统并不会直接诊断患者!相反,相关的医生会根据患者的常见症状提供必要的输入给系统。AI 系统随后会将这些输入与已接收到的过去输入进行匹配,这些输入曾被用来训练该系统。

基于系统的预测分析,它会给出关于疾病的决策。尽管逻辑回归可以根据给定的特征进行学习并做出决策,但它无法影响或修改特征的定义方式。逻辑回归是一种回归模型,其中因变量的可能值有限,且根据自变量的不同而有所变化,与线性回归不同。所以,例如,如果该模型被提供的是剖腹产患者的报告,而不是脑肿瘤患者的报告,它肯定无法预测正确的结果,因为给定的特征永远不会与训练数据匹配。

机器学习系统对数据表示的依赖并不是什么我们不知道的事情!事实上,我们大多数的计算机理论在数据表示的方式上表现得更好。例如,数据库的质量通常是根据其架构设计来评估的。即使在处理成千上万行数据时,如果表格被正确索引,任何数据库查询的执行速度也会变得极为迅速。因此,AI 系统对数据表示的依赖性不应让我们感到惊讶。

日常生活中也有很多类似的例子,其中数据的表示方式决定了我们的效率。比如在 20 个人中定位某个特定的人显然比在 500 人的拥挤人群中定位同一个人要容易得多。前面的图 1.1展示了两种不同类型数据表示法的可视化示例。

因此,如果 AI 系统输入适当特征的数据,即使是最困难的问题也能够得到解决。然而,以正确的方式收集并输入数据一直是计算机程序员面临的严重障碍。

在许多实时场景中,提取特征可能是一项繁琐的任务。因此,数据的表示方式决定了系统智能的主要因素。

注意

在一群人和猫中寻找猫咪可能会变得非常复杂,特别是当特征不合适时。我们知道猫咪有尾巴,因此,我们可能希望通过检测尾巴的存在作为一个显著的特征。然而,考虑到尾巴的形状和大小各异,往往很难用像素值来准确描述尾巴的外观!此外,尾巴有时可能会与人的手混淆。而且,物体的重叠可能会遮掩猫咪尾巴的存在,使得图像变得更加复杂。

从以上讨论可以得出结论,AI 系统的成功主要取决于数据的表示方式。此外,各种表示可以捕捉并缓存数据背后不同的解释因素。

表示学习是应对这些特定问题的最流行和广泛应用的学习方法之一。表示学习可以定义为从现有数据表示中学习下一层的表示。理想情况下,所有的表示学习算法都有这种学习表示的优势,这些表示能够捕捉潜在因素,并且每个特定子任务可能适用一个子集。下面的图 1.2提供了一个简单的示例:

深度学习简介

图 1.2:该图展示了表示学习。中间层能够发现解释因素(蓝色矩形框中的隐藏层)。一些因素解释了每个任务的目标,而另一些则解释了输入。

然而,从大量原始数据中提取一些高层次的数据和特征,需要某种程度的人类理解,这已经显示出了其局限性。可以举出很多这样的例子:

  • 区分两个相似年龄婴儿的哭声。

  • 识别猫咪眼睛的图像在白天和夜晚都会变得繁琐,因为猫咪的眼睛在夜间会发光,而白天则不会。

在所有这些前述的边缘情况中,表示学习似乎并没有表现得特别出色,反而显示出了一种阻碍行为。

深度学习,作为机器学习的一个子领域,可以通过构建多个表示层次或从一系列其他简单的表示和特征中学习特征层次,来解决表示学习中的主要问题[2][8]。

深度学习简介

图 1.3:本图展示了深度学习系统如何通过识别诸如角和轮廓之类的各种组合来表征人类形象,这些可以以边缘的术语来定义。图像经 Ian Goodfellow、Yoshua Bengio 和 Aaron Courville 授权再版,出版者为 MIT 出版社。

前面的图 1.3显示了深度学习模型的插图。对于计算机来说,解码原始非结构化输入数据的意义通常是一个繁琐的任务,如此图像所示,由不同的像素值组成。理想情况下,将像素组转换为识别图像的映射函数是非常困难的。此外,直接训练计算机进行这种映射几乎是不可逾越的。对于这类任务,深度学习通过创建一系列子映射来解决难题,以达到期望的输出。每个子映射对应于模型的不同层次集合。输入包含可以观察到的变量,并因此在可见层中表示。从给定的输入中,我们可以逐步提取数据的抽象特征。由于这些值在给定数据中不可用或不可见,因此这些层被称为隐藏层。

在图像中,从数据的第一层开始,通过对比相邻像素的研究,可以轻松识别边缘。第二个隐藏层可以区分第一个隐藏层描述的边缘中的角和轮廓。从描述角和轮廓的第二个隐藏层开始,第三个隐藏层可以识别特定对象的不同部分。最终,可以从第三层中清晰地检测出图像中存在的不同对象。

深度学习起步于 2006 年,Hinton 等人在 2006 年[2],还有Bengio 等人在 2007 年[3]最初集中于 MNIST 数字分类问题。在过去的几年里,深度学习已经从数字过渡到自然图像中的对象识别。此外,Krizhevsky 等人在 2012 年[4]利用 ImageNet 数据集取得了重大突破。

本书的范围主要限于深度学习,因此在直接深入探讨之前,应讨论深度学习的必要定义。

许多研究者以多种方式定义深度学习,因此,在过去的十年中,它也经历了许多定义的变化!以下是几种广泛接受的定义:

  • 根据 GitHub 的说明,深度学习是机器学习研究的新领域,旨在使机器学习更接近其原始目标之一:人工智能。深度学习涉及学习多层次的表示和抽象,有助于理解诸如图像、声音和文本等数据。

  • 根据维基百科最近的更新,深度学习是机器学习的一个分支,基于一组算法,尝试通过使用深度图(多个处理层,由多个线性和非线性变换组成)对数据中的高级抽象进行建模。

正如定义所示,深度学习也可以被视为一种特殊的机器学习类型。深度学习在数据科学领域取得了巨大流行,因为它能够从各种简单特征中学习复杂的表示。为了深入理解深度学习,我们列出了一些在接下来章节中将频繁使用的术语。本章的下一个主题将通过提供深度学习中使用的各种术语和重要网络,帮助你为深度学习打下基础。

深度学习入门

要理解本书中深度学习的发展历程,必须了解所有机器学习的术语和基本概念。然而,如果你已经对机器学习及相关术语有了足够的了解,可以跳过这一部分,直接进入本章的下一个主题。对数据科学充满热情、并且希望深入学习机器学习的读者,可以参考 Tom M. Mitchell(1997 年)的《机器学习》[5]和《机器学习:概率视角》(2012 年)[6]。

注意

神经网络并不执行奇迹。但如果合理使用,它们可以产生一些惊人的结果。

深度前馈网络

神经网络可以是递归的,也可以是前馈的。前馈网络的图中没有任何循环,它们被排列成一组层。一个具有多层的网络被称为深度网络。简单来说,任何具有两层或更多隐藏层的神经网络都被定义为深度前馈网络前馈神经网络图 1.4展示了深度前馈神经网络的通用表示。

深度前馈网络的工作原理是,随着网络深度的增加,它能够执行更多的顺序指令。顺序中的指令可以提供强大的能力,因为这些指令可以指向之前的指令。

前馈网络的目标是泛化某个函数 f。例如,分类器 y=f(x) 将输入 x 映射到类别 y。深度前馈网络修改了映射关系,y=f(x; α),并学习参数 α 的值,该值给出了最合适的函数值。下图图 1.4展示了深度前馈网络的简单表示,突出了与传统神经网络的架构差异。

注意

深度神经网络是具有多个隐藏层的前馈网络。

深度前馈网络

图 1.4:图中展示了浅层和深层前馈网络的表示

各种学习算法

数据集被视为学习过程的构建块。数据集可以定义为一组相互关联的数据集合,包含独立实体,但可以根据具体使用场景作为一个整体使用。数据集的单个数据元素称为数据点

以下的图 1.5给出了来自社交网络分析的各种数据点的视觉表示:

各种学习算法

图 1.5:图片展示了社交网络分析中的散布数据点。图片来源于维基百科

  • 未标记数据:这部分数据由人类生成的对象组成,通常可以轻松地从周围环境中获得。例子包括 X 射线、日志文件数据、新闻文章、语音、视频、推文等。

  • 标记数据:标记数据是从一组未标记数据中归一化得到的数据。这类数据通常格式良好,已经分类、标记,并且容易为人类理解,便于后续处理。

从高层次的理解来看,机器学习技术可以根据其学习过程的执行方式,分为监督学习和无监督学习。

无监督学习

在无监督学习算法中,给定的输入数据集没有期望的输出。系统通过分析数据集的经验学习有意义的属性和特征。在深度学习中,系统通常会尝试从数据点的整体概率分布中学习。有多种类型的无监督学习算法用于聚类。简单来说,聚类就是将数据点按相似类型的数据分组。然而,这种学习方式中没有基于最终输出的反馈,也就是说,没有教师来纠正你! 图 1.6 展示了无监督聚类的基本概述:

无监督学习

图 1.6:图中展示了无监督聚类的简单表示

一个实际的无监督聚类算法示例是谷歌新闻。当我们打开谷歌新闻下的某个主题时,它会显示多个超链接,指向不同的页面。这些主题可以看作是指向独立链接的超链接集群。

监督学习

在监督学习中,与无监督学习不同,每一步经历都有一个期望的输出。系统会得到一个数据集,并且已经知道期望输出的样子,以及每个相关层之间输入和输出的正确关系。这种学习方式通常用于分类问题。

以下的视觉表示在图 1.7中给出:

监督学习

图 1.7:图中展示了基于监督学习的数据分类

监督学习的实际应用示例包括人脸检测、人脸识别等。

尽管监督学习和无监督学习看起来是不同的身份,但它们通常通过各种方式相互连接。因此,这两种学习方式之间的细微界限对于学生群体来说往往是模糊的。

上述陈述可以通过以下数学表达式来公式化:

概率的通用乘法规则指出,对于n个数据集 n ε ℝ^t,联合分布可以分解如下:

监督学习

该分布表明,出现的无监督问题可以通过t个监督问题来解决。除此之外,p (k | n)的条件概率作为一个监督问题,可以通过无监督学习算法来解决,从而体验p (n, k)的联合分布。

监督学习

虽然这两种类型不是完全独立的身份,但它们通常有助于根据执行的操作对机器学习和深度学习算法进行分类。通俗来说,聚类形成、基于相似性识别群体密度等被称为无监督学习,而结构化输出、回归、分类等则被认为是监督学习。

半监督学习

顾名思义,在这种学习类型中,训练过程中同时使用有标签和无标签数据。这是一种监督学习,训练时使用大量无标签数据。

例如,半监督学习用于深度信念网络(稍后会解释),这是一种深度网络,其中某些层学习数据的结构(无监督),而一层学习如何对数据进行分类(监督学习)。

在半监督学习中,使用来自p (n)的无标签数据和来自p (n, k)的有标签数据来预测给定n的条件下k的概率,或者p (k | n)

半监督学习

图 1.8:图示展示了在半监督学习技术中,大量无标签数据的影响。图源自 Wikipedia

在前述的图 1.8中,顶部展示了模型在区分白色和黑色圆圈后所使用的决策边界。底部的图展示了模型采用的另一个决策边界。在该数据集中,除了两种不同类别的圆圈外,还有一组无标签数据(灰色圆圈)。这种训练可以视为首先创建聚类,然后用有标签数据标记这些聚类,从而将决策边界从高密度数据区域移开。

前述的图 1.8展示了半监督学习的示例。你可以参考Chapelle 等人的书籍[7]了解更多关于半监督学习方法的信息。

因此,既然你已经对人工智能、机器学习和表示学习有了基础了解,我们现在可以将全部焦点转移到深入讨论深度学习,并进一步描述其内容。

从前面提到的深度学习定义中,可以提炼出深度学习的两个主要特征,如下所示:

  • 一种通过后续抽象层次的连续知识体验无监督和监督学习的特征表示方式

  • 一个由多个抽象阶段的非线性信息处理组成的模型

深度学习术语

  • 深度神经网络DNN):这可以定义为一个具有多个隐藏层的多层感知器。所有层的权重彼此完全连接,并接收来自前一层的连接。权重通过监督学习或无监督学习初始化。

  • 递归神经网络RNN):RNN 是一种专门用于从时间序列或顺序数据(如语音、视频等)中学习的深度学习网络。RNN 的基本概念是需要保留来自前一状态的观察信息,以便用于下一状态。当前深度学习中的热门话题之一是长短时记忆LSTM)。

  • 深度信念网络DBN):这种网络[9][10][11]可以定义为一种概率生成模型,具有可见层和多个潜在变量(隐藏层)。每个隐藏层通过学习在较低层单元之间具有统计关系。网络越是趋向更高层次,关系就越复杂。这种网络可以通过贪心的逐层训练方法进行高效训练,其中所有隐藏层一次一个地以自下而上的方式进行训练。

  • 玻尔兹曼机BM):这可以定义为一个网络,它是一个对称连接的类神经元单元,能够对是否保持开/关做出随机决策。玻尔兹曼机通常具有简单的学习算法,可以揭示许多有趣的特征,这些特征代表了训练数据集中的复杂规律。

  • 限制玻尔兹曼机RBM):RBM 是一种生成式随机人工神经网络,是玻尔兹曼机的一种特殊类型。这类网络能够学习一组数据集的概率分布。RBM 由一层可见单元和隐藏单元组成,但没有可见-可见或隐藏-隐藏连接。

  • 卷积神经网络:卷积神经网络是神经网络的一部分;这些层之间以及与输入层之间的连接是稀疏的。后续层的每个神经元只负责输入的一部分。深度卷积神经网络在定位识别、图像分类、面部识别等领域取得了无与伦比的表现。

  • 深度自编码器:深度自编码器是一种具有多个隐含层的自编码器。这种类型的网络可以作为单层自编码器的堆叠进行预训练。训练过程通常较为复杂:首先,我们需要训练第一隐层以重构输入数据,然后用这个数据来训练下一个隐层,重构前一个隐层的状态,依此类推。

  • 梯度下降法 (GD):这是一种广泛应用于机器学习的优化算法,用于确定一个函数(f)的系数,从而减少整体成本函数。梯度下降法通常在无法通过解析方法(例如线性代数)计算所需参数时使用,必须通过某种优化算法来找到这些参数。

在梯度下降法中,模型的权重会在每次训练数据集(周期)的迭代中逐步更新。

成本函数 J(w),带有平方误差的和,可以表示为如下形式:

深度学习术语

权重更新的方向和幅度是通过沿成本梯度的反方向进行一步计算得出的,如下所示:

深度学习术语

在前面的公式中,η 是网络的学习率。每经过一个周期(epoch),权重会根据以下规则逐步更新:

                         for one or more epochs, 
                           for each weight i, 
                             wi:= w + ∆wi 
                           end  
                         end 

深度学习术语

使用梯度下降法优化的流行示例包括逻辑回归和线性回归。

  • 随机梯度下降法 (SGD):许多基于大量数据集的深度学习算法,都是基于一种叫做随机梯度下降法的优化算法。梯度下降法在小规模数据集上表现良好。然而,在非常大规模的数据集上,这种方法变得非常昂贵。在梯度下降法中,整个训练数据集只需一次遍历就能进行一次权重更新;因此,随着数据集规模的增加,整个算法的运行速度最终会变慢。权重更新的速度非常缓慢,因此,收敛到全局成本最小值所需的时间变得非常长。

因此,为了处理如此大规模的数据集,使用了梯度下降法的变种——随机梯度下降法。与梯度下降法不同,随机梯度下降法在每次训练数据集迭代后更新权重,而不是在整个数据集结束后更新。

                     until cost minimum is reached 
                       for each training sample j: 
                         for each weight i 
                           wi:= w + ∆wi 
                         end 
                       end 
                     end 

深度学习术语

近年来,深度学习获得了极大的关注,它成为了许多广泛应用学科的交汇点,如模式识别、神经网络、图形建模、机器学习和信号处理等。

深度学习广泛应用的其他重要原因可以通过以下几点总结:

  • 近年来,图形处理单元(GPU)的能力显著提高。

  • 用于训练目的的数据集的数据规模显著增加。

  • 近年来,机器学习、数据科学和信息处理领域的最新研究显示了一些重大进展。

这些观点的详细描述将在本章的后续主题中提供。

深度学习:人工智能的革命

深度学习的详尽历史超出了本书的范围。然而,为了对这一主题产生兴趣和了解,了解一些基本的背景背景是必要的。

在介绍中,我们已经简要讨论了深度学习如何在人工智能的边缘占据一席之地。本节将详细讨论机器学习和深度学习如何相互关联或有何不同。我们还将讨论这两个主题在过去十年左右的趋势变化。

"深度学习浪潮已经在计算语言学的海岸边拍打了数年,但 2015 年似乎是这场海啸全面冲击主要自然语言处理(NLP)会议的一年。"
--克里斯托弗·D·曼宁博士,2015 年 12 月

深度学习:人工智能的革命

图 1.9:图表明深度学习大约 10 年前处于初期阶段。然而,机器学习在研究者社区中已成为热门话题。

深度学习正在迅速扩展其在人工智能领域的地盘,并不断以其惊人的实证结果令许多研究人员惊讶。机器学习和深度学习都代表了两种不同的思想流派。机器学习可以被视为 AI 的最基本方法,而深度学习则可以被认为是一个新的、巨大的时代,增加了一些该学科的功能。

深度学习:人工智能的革命

图 1.10:图表明深度学习如何在当今日益流行,并试图达到机器学习的水平。

然而,机器学习在完全解决 AI 的许多关键问题上通常失败,主要是语音识别、物体识别等。

在处理高维数据时,传统算法的性能似乎更具挑战性,因为随机变量的数量不断增加。此外,传统机器学习方法中用于实现泛化的程序不足以学习高维空间中的复杂义务,这通常会增加整体模型的计算成本。深度学习的发展主要受到了这些函数中基本算法的崩溃的激励,并且克服了上述障碍。

大多数研究人员和数据科学家认为,随着时间的推移,深度学习将占据人工智能的主要部分,最终使机器学习算法变得过时。为了清晰了解这一点,我们查看了这两个领域的当前谷歌趋势,并得出了以下结论:

  • 机器学习的发展曲线在过去十年一直处于增长阶段。深度学习是新的,但增长速度超过了机器学习。当仔细观察趋势时,可以发现深度学习的增长速度相比机器学习要更快。

前面提到的图 1.9图 1.10展示了谷歌趋势的可视化。

深度学习的动机

机器学习算法面临的最大已知问题之一是维度灾难[12] [13] [14]。这指的是当数据集的维度数很高时,某些学习算法可能表现不佳。在下一节中,我们将讨论深度学习是如何通过引入新特性为这个问题带来足够的希望的。还有许多其他相关问题,深度架构在这些问题上显示出了相较于传统架构的显著优势。在本章的这一部分,我们希望将这些更加突出的挑战作为一个单独的主题介绍。

维度灾难

维度灾难可以定义为在高维空间(通常是成千上万甚至更高维度)中进行数据分析和组织时所出现的现象。当数据集的维度很高时,机器学习问题将面临极大的困难。高维数据难以处理,原因如下:

  • 随着维度数量的增加,特征的数量将呈指数级增长,最终导致噪音的增加。

  • 在标准实践中,我们无法获得足够多的观察样本来对数据集进行泛化。

维度灾难的一个直接解释是组合爆炸。根据组合爆炸的理论,当收集了一定数量的变量时,会形成一个庞大的组合。例如,对于n个二进制变量,可能的组合数为 O (2^n)。因此,在高维空间中,总的配置数将几乎是无法计数的,远远超过我们可用的样本数——大多数配置将没有与之相关的训练样本。图 1.11显示了类似现象的图示,便于更好理解。

因此,由于训练的困难,这种情况对于任何机器学习模型来说都是繁琐的。休斯效应[15]指出了以下内容:

“在固定数量的训练样本下,随着维度的增加,预测能力会减少。”

因此,随着解释变量数量的增加,模型的可达精度几乎崩溃。

为了应对这种情况,我们需要增加输入系统的样本数据集的大小,直到它足以与这种情况相抗衡。然而,随着数据复杂性的增加,维度的数量几乎达到一千。对于这种情况,即使是数亿张图像的数据集也不足以应对。

深度学习通过更深的网络配置,在部分解决这个问题上取得了一些成功。这一贡献主要归因于以下几个原因:

  • 现在,研究人员能够通过在将样本输入进行训练前重新定义网络结构来管理模型复杂性。

  • 深度卷积网络关注数据的高级特征,而非基础层次的信息,这大大减少了特征的维度。

尽管深度学习网络为应对维度灾难提供了一些思路,但它们仍未能够完全征服这一挑战。在微软最近的超深神经网络研究中,他们提出了 150 层的结构;因此,参数空间变得更加庞大。研究团队还探索了更加深层的网络,层数几乎达到 1000 层;然而,由于模型的过拟合,结果并未达到预期。

机器学习中的过拟合:当模型经过过度训练,导致其性能受到负面影响时,称为模型的过拟合现象。当模型学习到训练数据集中的随机波动和不必要的噪声时,就会发生这种情况。这些现象的后果是不可取的——模型无法在新数据集上表现良好,这会负面影响模型的泛化能力。

机器学习中的欠拟合:指的是模型无法在当前数据集上或新数据集上表现良好的情况。这种模型不适用,且在数据集上的表现较差。

维度灾难

图 1.11:图示显示,从一维到三维,随着维度数量的增加,从上到下,随机变量的数量可能会呈指数增长。此图像经尼古拉斯·查帕多斯(Nicolas Chapados)授权转载,摘自他的文章《数据挖掘算法在精算定价中的应用》。

在前图的 1D 示例(上)中,由于只有 10 个兴趣区域,对于学习算法正确地进行泛化不应是一个困难的任务。然而,在更高维度的 3D 示例(下)中,模型需要跟踪所有 101010=1000 个兴趣区域,这变得更加繁琐(或者几乎是模型无法完成的任务)。这可以作为维度灾难的最简单例子。

梯度消失问题

梯度消失问题[16]是训练人工神经网络时遇到的障碍,尤其与一些基于梯度的方法,如反向传播,相关。理想情况下,这个问题使得学习和训练前面几层变得非常困难。当深度神经网络的层数急剧增加时,情况变得更加糟糕。

梯度下降算法特别是通过将梯度的负值乘以一个小的标量值(介于01之间)来更新权重。

梯度消失问题梯度消失问题

如前面的方程所示,我们将重复梯度计算,直到它趋近于零。理想情况下,我们通常会设置一个超参数来限制最大迭代次数。如果迭代次数过多,训练的时间也会相应变长。另一方面,如果某些深度神经网络的迭代次数变得不可察觉,我们最终肯定会得到不准确的结果。

在梯度消失问题中,网络输出相对于前一层参数的梯度变得极其微小。因此,权重在每次迭代时不会发生显著变化。即使前几层的参数值发生了较大变化,对整体输出的影响也不明显。由于这个问题,深度神经网络的训练变得不可行,模型的预测效果也不令人满意。这种现象被称为梯度消失问题。这将导致一些拉长的代价函数,如下一个图 1.12所示:

梯度消失问题

图 1.12:平坦梯度和拉长代价函数的图像

下面的图 1.13展示了一个梯度较大的例子,其中梯度下降能够快速收敛:

梯度消失问题

图 1.13:较大梯度代价函数的图像;因此梯度下降能够更快地收敛

这是深度学习成功的一个重大挑战,但现在,得益于各种不同的技术,这个问题在一定程度上得到了克服。长短期记忆LSTM)网络是 1997 年消除这个问题的一个重要突破。详细描述请参见第四章,循环神经网络。此外,一些研究人员也尝试通过不同的技术来解决这个问题,包括特征准备、激活函数等。

分布式表示

所有的深度网络大多基于分布式表示的概念,这也是深度学习算法成功背后的理论优势所在。在深度学习的背景下,分布式表示是多尺度表示,且与理论化学和物理学的多尺度建模密切相关。分布式表示背后的基本思想是,感知到的特征是多个因素的结果,这些因素组合在一起以产生期望的结果。一个日常生活中的例子可以是人脑,它通过分布式表示来伪装周围的物体。

在这种表示方式下,人工神经网络将以一种方式构建,使其拥有表示我们所需模型所必需的众多特征和层。该模型将通过多个相互依赖的层来描述数据,例如语音、视频或图像,每一层都负责在不同的尺度层次上描述数据。通过这种方式,表示将在多个层次中分布,涉及多个尺度。因此,这种表示方式被称为分布式表示。

注意

分布式表示本质上是稠密的。它遵循两种表示之间的多对多关系。一个概念可以通过多个神经元来表示。另一方面,一个神经元也可以表示多个概念。

传统的聚类算法,如最近邻算法、决策树或高斯混合模型等,使用的是非分布式表示,这些算法都需要 O(N) 个参数来区分 O(N) 个输入区域。曾几何时,人们几乎无法相信会有任何其他算法能表现得比这更好!然而,深度网络,如稀疏编码、RBM、多层神经网络等,能够仅用 O(N) 个参数区分多达 O(2^k) 个输入区域(其中 k 代表稀疏表示中非零元素的总数,k=N 适用于其他非稀疏的 RBM 和密集表示)。

在这些操作中,要么将相同的聚类应用于输入的不同部分,要么进行多个聚类并行操作。聚类对分布式表示的推广被称为多重聚类。

使用分布式表示的指数级优势源于在多个示例中重用每个参数,而这些示例不一定彼此接近。例如,限制玻尔兹曼机(Restricted Boltzmann Machine)可以作为一个合适的例子。然而,在局部泛化的情况下,输入空间中的非相同区域仅关心其自身的私有参数集。

主要优势如下:

  • 数据内部结构的表示在抗损伤性和优雅降级方面具有强大的鲁棒性

  • 它们有助于概括数据之间的概念和关系,从而增强推理能力。

以下图 1.14展示了分布式表示的一个实时例子:

分布式表示

图 1.14:图示展示了分布式表示如何帮助模型区分图像中各种类型的表达。

深度学习网络的分类

人工神经网络在机器学习中常被许多研究者称为新一代神经网络。我们所听说的大多数学习算法,基本上是为了让系统学习和生物大脑一样的方式而构建的。这就是人工神经网络这个名称的由来!从历史上看,深度学习的概念源自人工神经网络ANN)。深度学习的实践可以追溯到 1960 年代,甚至可能更早。随着深度学习的兴起,人工神经网络(ANN)在研究领域获得了更大的关注。

多层感知机MLP)或具有多个隐藏中间层的前馈神经网络,被称为深度神经网络DNN),是深度架构模型的一些典型例子。第一个流行的深度架构模型由 Ivakhnenko 和 Lapa 于 1965 年发表,采用了监督式深度前馈多层感知机[17]。

深度学习网络的分类

图 1.15:GMDH 网络有四个输入(输入向量 x 的组成部分),和一个输出 y,该输出是对真实函数 y=f(x)=y 的估计。

另一篇来自 Alexey Ivakhnenko 的论文,他当时正在研究如何更好地预测河流中的鱼群数量,使用了数据处理组方法GMDH)算法,该算法尝试解释一种具有八个训练层的深度网络,发表于 1971 年。至今,这仍被视为本千年最受欢迎的论文之一[18]。前面的图 1.15展示了具有四个输入的 GMDH 网络。

此后,反向传播BP)作为一种广为人知的算法,用于学习类似类型网络的参数,在 1980 年代得到了广泛应用。然而,由于多种原因,具有多个隐藏层的网络难以处理,因此反向传播未能达到预期的效果[8] [19]。此外,反向传播学习使用的是基于局部梯度信息的梯度下降算法,这些操作从一些随机的初始数据点开始。在网络深度逐渐增加的过程中,这些数据往往会集中在一些不期望的局部最优点,因此,结果通常会陷入不良解中。

与深度架构模型相关的优化约束在两篇论文[8] [20]中通过建立一种高效的无监督学习算法得到了实质性减少。这两篇论文介绍了一类被称为深度置信网络DBN)的深度生成模型。

2006 年,另外两种具有非生成性、非概率特征的无监督深度模型被发布,并且在研究人员中获得了极大的关注。一种是基于能量的无监督模型[21],另一种是具有后续层训练的自编码器变体,类似于先前的 DBN 训练[3]。这两种算法都可以高效地用于训练深度神经网络,几乎与 DBN 完全相同。

自 2006 年以来,深度学习的研究经历了巨大的爆发。除了传统的浅层机器学习技术外,这一领域也持续呈现出指数级增长。

基于本章前面提到的学习技术,并根据所使用的技术和架构的应用案例,深度学习网络可以大致分为两大类。

深度生成或无监督模型

许多深度学习网络都属于这一类别,如受限玻尔兹曼机、深度置信网络、深度玻尔兹曼机、去噪自编码器等。大多数这些网络可以通过在网络中进行采样来生成样本。然而,一些其他网络,例如稀疏编码网络等,难以进行采样,因此不是生成式的。

一种流行的深度无监督模型是深度玻尔兹曼机DBM)[22] [23] [24] [25]。传统的 DBM 包含多层隐藏变量;然而,同一层中的变量之间没有任何连接。尽管传统的玻尔兹曼机BM)算法较为简单,但它过于复杂,难以研究,且训练速度非常慢。在 DBM 中,每一层都能获得前一层潜在特征响应之间的高阶复杂关联。许多现实生活中的问题,如物体和语音识别,需要学习复杂的内部表示,这些问题通过 DBM 可以更容易地解决。

具有一个隐藏层的 DBM 被称为受限玻尔兹曼机RBM)。与 DBM 类似,RBM 没有任何隐藏层之间或可见层之间的连接。RBM 的关键特性体现在其构成多个 RBM 上。随着多个潜在层的形成,前一个 RBM 的特征激活作为下一个 RBM 的输入训练数据。这种架构生成了一种不同类型的网络,称为深度置信网络DBN)。关于受限玻尔兹曼机和深度置信网络的各种应用在第五章中有详细讨论,受限玻尔兹曼机

DBN 的一个主要组成部分是一组层,它通过减少网络的规模和深度的时间复杂度来提高效率。结合 DBN 的特性,它能够克服 BP 的主要缺点,即从某些期望的初始化数据点开始训练,它还具有其他吸引人的特点。以下是其中一些特点:

  • DBN 可以看作是一个概率生成模型。

  • DBN 通常有数亿个参数,因此会出现过拟合问题。此外,由于其庞大的数据集,深度架构通常还会经历欠拟合问题。这两种问题都可以通过预训练步骤有效地减少。

  • DBN 有效地利用了未标记数据。

另一个可以用于无监督(以及监督)学习的深度生成网络是和积网络SPN)[26],[27]。SPN 是深度网络,可以视为有向无环图,其中图的叶节点是观察变量,内部节点是求和和乘积操作。‘求和’节点表示混合模型,而‘乘积’节点则构建了特征层次结构。SPN 使用期望最大化算法和反向传播一起训练。学习 SPN 的主要障碍是,当梯度传播到深层时,梯度迅速减小。具体来说,从条件似然的导数生成的常规深度神经网络的标准梯度下降会遇到困难。减少此问题的一种解决方案是用潜在变量的最可能状态代替边际推断,然后通过此状态传播梯度。在[28]中,Domingo 和 Gens 在小规模图像识别任务中展示了出色的结果。以下是图 1.16,展示了一个示例 SPN 网络以帮助理解。它显示了和积网络的框图:

深度生成或无监督模型

图 1.16:和积网络的框图

另一种流行的深度生成网络,可以用作无监督(以及有监督)学习的,是循环神经网络RNN)。这种类型的网络的深度直接取决于输入数据序列的长度。在无监督 RNN 模型中,利用之前数据样本的经验来预测未来的数据序列。RNN 已被广泛用于数据序列化文本或语音,然而,由于梯度消失问题的出现,最近它们的流行度有所下降[29] [16]。通过使用随机曲率估计,Hessian-free 优化[30]在某种程度上克服了这些限制。最近,Bengio 等[31]和 Sutskever[32]提出了不同的变种来训练生成型 RNN,这些变种在性能上超越了 Hessian-free 优化模型。RNN 在本书的第四章中有进一步的阐述,循环神经网络

在其他无监督深度网络的子类中,基于能量的深度模型是最为知名的架构之一[33] [34]。无监督模型类别中的一个典型例子是深度自编码器。深度自编码器的大多数变种本质上是生成性的;然而,它们的特性和实现方式通常各不相同。流行的例子包括预测稀疏编码器、变换自编码器、去噪自编码器及其堆叠版本等等。自编码器在第六章中有详细解释,自编码器

深度判别模型

大多数在有监督学习中使用的判别技术是浅层架构,例如隐马尔可夫模型[35],[36],[37],[38],[39],[40],[41]或条件随机场。然而,最近,出现了一种深度结构的条件随机场模型,它通过将每一层的输出作为输入传递到更高层。深度结构的条件随机场模型有多个版本,已经成功应用于自然语言处理、电话识别、语言识别等领域。尽管判别方法在深度架构中取得了一定的成功,但它们仍未达到预期的效果。

如前节所述,RNN(循环神经网络)已经被用于无监督学习。然而,RNN 也可以作为判别模型,并通过有监督学习进行训练。在这种情况下,输出成为与输入数据序列相关的标签序列。语音识别技术早在很久以前就已经看到了这种判别 RNN 的应用,但成功的案例很少。[42]号论文展示了使用隐马尔可夫模型将 RNN 的分类结果转化为标签序列。然而,遗憾的是,由于种种原因,隐马尔可夫模型的使用未能充分发挥 RNN 的全部潜力。

最近,针对 RNN(循环神经网络)开发了几种其他方法和模型,其基本思想是将 RNN 的输出视为某些条件分布,并将其分布在所有可能的输入序列上[43],[44],[45],[46]。这有助于 RNN 在嵌入长短期记忆(LSTM)到其模型的同时进行序列分类。其主要优点是,它既不需要对训练数据集进行预先分割,也不需要对输出进行后处理。基本上,数据集的分割是由算法自动执行的,并且可以为条件分布的优化推导出一个可微的目标函数。此类算法的有效性广泛适用于手写识别操作。

另一种流行的判别式深度架构是卷积神经网络CNN)。在 CNN 中,每个模块包含一个卷积层和一个池化层。为了形成深度模型,这些模块通常是一个接一个地堆叠,或者在其上方叠加一个深度神经网络。卷积层有助于共享许多权重,而池化层则将卷积层的输出进行分隔,最小化来自上一层的数据流量。CNN 已被公认为一种高效的模型,尤其在图像识别、计算机视觉等任务中表现出色。最近,通过对 CNN 设计的特定修改,它在语音识别方面也被证明同样有效。时延神经网络TDNN)[47] [48],最初用于早期的语音识别,是卷积神经网络的一个特例,也可以视为其前身。

在这种模型中,权重共享仅限于时间维度,并且没有池化层。第三章,卷积神经网络深入讨论了 CNN 的概念和应用。

深度学习凭借其多种模型,具有广泛的应用。许多顶尖科技公司,如 Facebook、Microsoft、Google、Adobe、IBM 等,广泛采用深度学习。除了计算机科学,深度学习还对其他科学领域做出了重要贡献。

现代用于物体识别的 CNN 为视觉处理提供了重要的见解,甚至神经科学家也可以进一步探索。深度学习还提供了处理大规模数据所需的功能工具,并在科学领域中进行预测。该领域在预测分子行为方面也非常成功,旨在促进制药研究。

总结来说,深度学习是机器学习的一个子领域,因其应用范围更广,已在实用性和流行度方面取得了卓越的增长。然而,未来的几年应该充满挑战与机遇,以进一步改进深度学习,并为新的数据爱好者探索这一主题。

注意事项

为了帮助读者更好地理解深度学习,以下是一些优秀的、经常更新的在线阅读列表:deeplearning.net/tutorial/ ufldl.stanford.edu/wiki/index.php/UFLDL_Tutorial deeplearning.net/reading-list/

摘要

在过去的十年里,我们有幸听到许多人工智能领域伟大科学家和公司关于深度学习的重大发明。深度学习是一种机器学习方法,近年来在其实用性和流行度上取得了巨大的增长。其原因主要在于它能够处理涉及高维数据的大型数据集,解决了如梯度消失问题等主要难题,并且有训练更深层网络的技术。在本章中,我们已经详细解释了大部分这些概念,并且还对深度学习的各种算法进行了分类,后续章节将详细阐述这些内容。

本书的下一章将介绍大数据与深度学习的关联。该章节将主要聚焦于深度学习如何在从大规模数据中提取有价值信息方面发挥重要作用。

第二章 分布式深度学习与大规模数据

“我们信仰上帝,其他的都必须提供数据”
--W. Edwards Deming

在这个指数增长的数字世界中,大数据和深度学习是两大最热门的技术趋势。深度学习和大数据是数据科学领域中两个相互关联的主题,在技术发展方面,它们密切相连,且同样重要。

数字数据和云存储遵循一种通用法则,称为摩尔定律[50],大致表示全球数据每两年翻一番;然而,存储这些数据的成本大致以相同的速度下降。这种数据的激增带来了更多的特征和多样性,因此,为了从中提取所有有价值的信息,应该构建更好的深度学习模型。

这种海量数据的可用性为多个行业带来了巨大的机会。此外,带有分析部分的大数据,在数据挖掘、数据获取和从中提取隐藏信息的领域中也带来了许多挑战。在人工智能领域,深度学习算法在学习过程中能够在大规模数据下提供最佳输出。因此,随着数据增长速度空前加快,深度学习在提供大数据分析解决方案中也发挥着至关重要的作用。

本章将深入探讨深度学习模型在大数据中的表现,并揭示相关的挑战。章末将介绍 Deeplearning4j,一个开源分布式框架,提供与 Hadoop 和 Spark 的集成,用于大规模数据的深度学习部署。本章还将提供示例,展示如何使用 Deeplearning4j 实现基本的深度神经网络,以及它与 Apache Spark 和 Hadoop YARN 的集成。

本章将涵盖以下重要主题:

  • 面向海量数据的深度学习

  • 深度学习在大数据中的挑战

  • 分布式深度学习与 Hadoop

  • Deeplearning4j:一个用于深度学习的开源分布式框架

  • 在 Hadoop YARN 上设置 Deeplearning4j

面向海量数据的深度学习

在这个 Exa-Byte 级别的时代,数据以指数级的速度增长。许多组织和研究人员以各种方式分析这种数据增长,且目的各异。根据国际数据公司IDC)的调查,互联网每天处理约 2 Petabytes 的数据[51]。2006 年,数字数据的大小约为 0.18 ZB,而这一数据量在 2011 年增加到了 1.8 ZB。到 2015 年,预计这一数据量将达到 10 ZB,而到 2020 年,全球数据量将达到大约 30 ZB 至 35 ZB。这一数据山脉的时间线如图 2.1所示。数字世界中这些庞大的数据量正式被称为大数据。

“大数据的世界正在燃烧”
--《经济学人》,2011 年 9 月

海量数据的深度学习

图 2.1:图表展示了约 20 年时间跨度内数据的增长趋势

Facebook 的存储量几乎为 21PB,存储了 2 亿个对象[52],而 Jaguar ORNL 的存储量超过 5PB。这些存储的数据增长速度如此之快,以至于预计在 2018 到 2020 年间,Exa-Byte 级别的存储系统将被使用。

数据的爆炸性增长无疑对传统的数据密集型计算构成了直接威胁,并指向了需要某种分布式和可扩展的存储架构,用于查询和分析大规模数据。关于大数据的一个普遍观点是,原始数据非常复杂、种类繁多,并且不断增长。一个理想的大数据集包含大量的无监督原始数据,并且只有少量的结构化或分类数据。因此,在处理这些非静态结构化数据时,传统的数据密集型计算通常会失败。因此,具有无限多样性的大数据需要复杂的方法和工具,以便提取模式并分析大规模数据。大数据的增长主要是由计算处理能力的提升和现代系统以更低成本存储数据的能力推动的。

考虑到大数据的所有特征,可以将其分为四个不同的维度,通常称为四个 V:数据量(Volume)数据种类(Variety)数据速度(Velocity)数据真实性(Veracity)。下图图 2.2展示了大数据的不同特征,并提供了所有 4V 的数据:

海量数据的深度学习

图 2.2:图表展示了大数据 4V 的可视化表现

在这个数据密集型的技术时代,数据的速度、数据收集和获取的增长速度与大数据的其他参数一样重要,即数据量(Volume)数据种类(Variety)。随着数据生成速度的加快,如果不加以合理收集和分析,就有可能丧失重要的数据。尽管可以将这些快速生成的数据存储到大容量存储中,供以后批量处理,但处理这些高速度数据的真正关键在于组织能够多快地将原始数据转化为结构化且可用的格式。具体而言,像航班票价、酒店费用或某些电子商务产品的价格等时间敏感信息,如果不立即以系统化的方式保存和处理,将会变得过时。大数据中的真实性(Veracity)参数涉及数据分析后得到的结果的准确性。随着数据变得越来越复杂,保持对大数据中隐藏信息的信任,带来了重大挑战。

为了提取和分析如此复杂的数据,需要一个更好、更周密的模型。在理想情况下,与小数据集相比,模型在处理大数据时应该表现更好。然而,这并不总是如此。接下来,我们将通过一个例子进一步讨论这个问题。

图 2.3所示,在小数据集的情况下,最好的算法比最差的算法要多 n%。然而,随着数据集大小的增加(大数据),性能也会呈指数级增强,达到多 k % >> n %。从[53]中可以清晰地看到,大规模训练数据集对模型性能的影响。然而,认为仅使用大数据就能通过任何最简单的模型达到最佳性能,这种说法是完全误导的。

从[53]中我们可以看到,算法 1 基本上是一个朴素贝叶斯模型,算法 2 属于基于记忆的模型,而算法 3 对应的是 Winnow 模型。下图显示,在小数据集的情况下,Winnow 的表现不如基于记忆的模型。而在处理大数据集时,朴素贝叶斯和 Winnow 的表现都优于基于记忆的模型。因此,查看图 2.3时,很难推测出这些简单模型在大数据环境中哪个表现更好。一个直观的解释是,基于记忆的方法在处理大数据时表现较差,原因在于加载大量数据到内存时产生的延迟。因此,这纯粹是一个与内存相关的问题,仅仅使用大数据并不能解决这个问题。因此,性能的主要原因应是模型的复杂性。这也说明了深度学习模型的重要性。

注意

大数据。小智慧。没有进步!大数据。大智慧。突破![54]

深度学习与大数据有所不同。深度学习通过利用大规模的数字数据,已成功地应用于各种行业产品,并被各类研究人员广泛实践。像 Facebook、Apple 和 Google 等著名科技公司每天都会收集并分析大量数据,并且在过去几年里,他们在多个深度学习相关项目中不断取得进展。

Google 在从各种来源收集的大量非结构化数据上部署深度学习算法,包括 Google 的街景、图像搜索引擎、Google 翻译以及 Android 的语音识别。

深度学习用于海量数据

图 2.3:不同类型算法在数据集大小增加时准确率的变化

苹果的 Siri 是 iPhone 的虚拟个人助手,提供各种不同的服务,如体育新闻、天气预报、用户问题的回答等。Siri 的整个应用基于深度学习,收集来自不同 Apple 服务的数据并获取其智能。其他行业,主要是微软和 IBM,也在使用深度学习作为其处理这海量非结构化数据的主要领域。IBM 的类大脑计算机沃森和微软的必应搜索引擎主要利用深度学习技术来挖掘大数据。

当前的深度学习架构包含数百万甚至数十亿个数据点。此外,数据增长的规模使得模型避免了过拟合的风险。计算能力的快速增长也使得训练先进模型变得更加容易。

表 2.1 显示了如何在最近的研究中使用流行的深度学习模型来最大化数据的提取信息:

模型 计算能力 数据集 平均运行时间
卷积神经网络 [55] 两个 NVIDIA GTX 580 3 GB GPU 大约 90 轮训练,使用 120 万张高分辨率图片 五到六天
深度置信网络 [41] NVIDIA GTX 280 1 GB GPU 100 万张图片 大约一天
稀疏自编码器 [66] 1000 个 CPU,每个拥有 16000 个核心 1000 万张 200*200 像素的图片 大约三天

表 2.1:大规模深度学习模型的最新研究进展。部分信息摘自[55]

在分层学习方法的帮助下,深度学习算法主要用于从输入的原始数据中提取有意义的通用表示。基本上,在更高的层次上,从前一层和多层学习模型的低抽象数据中学习到更复杂、抽象的数据表示。尽管深度学习也可以从大量标注(分类)数据中学习,但当模型能够从未标注/未分类数据中学习时,它们通常更具吸引力[56],从而帮助生成大规模非结构化数据的有意义模式和表示。

在处理大规模无监督数据时,深度学习算法可以比浅层学习架构更好地提取数据点之间的通用模式和关系。以下是深度学习算法在用大规模未标注数据训练时的一些主要特征:

  • 从更高层次的抽象和表示中,可以从深度学习模型中获得大数据的语义和关系知识。

  • 即使是一个简单的线性模型,也可以通过从过于复杂和更抽象的巨大数据集表示中获得的知识,进行有效的表现。

  • 来自无监督数据的这种巨大数据表示种类为学习其他数据类型(如文本、音频、视频、图像等)打开了大门。

因此,可以肯定地得出结论,深度学习将成为提供大数据情感分析、预测分析等的关键要素,特别是随着处理能力的增强和图形处理单元GPU)容量的提升。本章的目的是不是广泛讨论大数据,而是展示大数据与深度学习之间的关系。接下来的章节将介绍深度学习在处理大规模未分类数据时的关键概念、应用和挑战。

深度学习在大数据中的挑战

大数据的潜力无疑是值得注意的。然而,要在如此规模下充分提取有价值的信息,我们需要新的创新和有前景的算法来解决这些相关的技术问题。例如,为了训练模型,大多数传统的机器学习算法会将数据加载到内存中。但对于海量数据,这种方法显然不可行,因为系统可能会耗尽内存。为了克服这些棘手的问题,并通过深度学习技术最大化大数据的价值,我们将需要集思广益。

尽管正如前面一节讨论的那样,大规模深度学习在过去十年里取得了许多成就,但这个领域仍处于成长阶段。大数据不断地通过其 4V 特性提出新的限制。因此,为了应对这些挑战,模型中还需要进行更多的进步。

由于海量数据,深度学习面临的挑战(第一个 V)

大规模数据的体量对深度学习构成了巨大的挑战。随着维度(属性)非常高,示例(输入)数量庞大以及分类(输出)种类繁多,大数据常常增加模型的复杂性,同时也增加了算法的运行时间复杂度。大量数据使得使用集中存储及其有限的处理能力几乎不可能进行深度学习算法的训练。为了应对由数据量巨大带来的挑战,应该使用分布式框架和并行化的服务器。这些升级后的深度网络模型已经开始使用 CPU 和 GPU 集群来加速训练速度,同时不妥协算法的准确性。模型并行性和数据并行性已经发展出了各种新的策略。

在这些类型中,模型或数据被拆分成可以适应内存数据的块,然后分发到各个节点进行前向和反向传播[57]。基于 Java 的深度学习分布式工具 Deeplearning4j 使用数据并行性来实现这一目标,下一节将详细介绍。

大量数据总是伴随着标签噪声和数据不完整性,这在大规模深度学习训练中构成了一个重大挑战。大部分大数据由无标签或非结构化数据构成,其中噪声标签主要存在于此。为了克服这个问题,数据集需要进行一定程度的人工整理。例如,所有搜索引擎都用于收集过去一年间的数据。对于这些数据,我们需要进行某种形式的筛选,特别是去除冗余和低价值数据。先进的深度学习方法对于处理这类噪声和冗余数据至关重要。此外,相关算法应能够容忍这些杂乱的数据集。还可以实现更高效的代价函数和更新的训练策略,以完全克服噪声标签的影响。此外,使用半监督学习[58] [59]有助于增强与这些噪声数据相关的解决方案。

深度学习面临的来自高种类数据的挑战(第二个 V)

这是大数据的第二个维度,代表所有类型的格式,具有不同的分布并来自众多来源。指数级增长的数据来自异质来源,其中包括大量的音频流、图像、视频、动画、图形以及来自各种日志文件的非结构化文本。这些不同种类的数据具有不同的特性和行为。数据集成可能是解决此类情况的唯一途径。正如第一章《深度学习简介》中所述,深度学习有能力从结构化/非结构化数据中进行学习。深度学习可以以分层的方式执行无监督学习,即逐层训练,更高层次的特征由紧接着的较低层次定义。深度学习的这一特性可以用来解决数据集成问题。一个自然的解决方案是从每个单独的数据源中学习数据表示,然后在随后的层次中集成学到的特征。

已经有一些实验[60] [61]成功证明,深度学习可以轻松应用于异构数据源,显著提高系统性能。然而,深度学习在未来几年仍需解决许多未解之谜。目前,大多数深度学习模型主要在双模态(仅来自两个源的数据)上进行测试,但在处理多模态数据时,系统性能是否会得到提升呢?可能出现多种数据源提供相互冲突的信息;在这种情况下,模型如何能够消除这些冲突,并以一种建设性且富有成效的方式整合数据呢?由于深度学习能够学习中间表示和与各种数据相关的潜在因素,因此它似乎非常适合于多模态的各种数据源的集成。

深度学习面临的高速度数据挑战(第三个 V)

数据增长的极端速度给深度学习技术带来了巨大挑战。对于数据分析,按此速度创建的数据也应该及时处理。在线学习是从这种高速数据中学习的解决方案之一[62-65]。然而,在线学习使用的是一种顺序学习策略,其中整个数据集需要保存在内存中,这对于传统机器来说极为困难。尽管传统神经网络已经针对在线学习进行了修改[67-71],但在深度学习领域仍有很大的进展空间。作为在线学习的替代方法,随机梯度下降法[72],[73]也被应用于深度学习。在这种方法中,一个带有已知标签的训练样本被馈送到下一个标签,以更新模型参数。此外,为了加速学习,更新也可以基于小批量进行[74]。这种小批量能够在运行时间和计算机内存之间提供良好的平衡。在下一部分,我们将解释为什么小批量数据对分布式深度学习至关重要。

与这种高速度数据相关的另一个重大挑战是,这些数据在性质上极为变化无常。数据的分布在时间上变化过于频繁。理想情况下,随着时间的推移变化的数据被划分为来自小时间段的块。基本的思想是数据在一段时间内保持静止,并且具有一定程度的相关性[75] [76]。因此,大数据的深度学习算法应具备将数据作为流来学习的特性。能够从这些非平稳数据中学习的算法对于深度学习至关重要。

深度学习面临的数据真实性挑战(第四个 V)

数据的真实性、不精确性或不确定性有时会被忽视,尽管它与大数据的其他 3Vs 一样具有重要的影响。随着大数据的巨大多样性和速度,组织再也无法依赖传统模型来衡量数据的准确性。根据定义,非结构化数据包含大量的不精确和不确定数据。例如,社交媒体数据本质上是不确定的。尽管有一些工具可以自动化数据的规范化和清理,但它们大多处于前工业化阶段。

分布式深度学习与 Hadoop

从本章的前面部分,我们已经获得了足够的洞察力,了解深度学习与大数据之间的关系如何以及为何能给研究领域带来重大变化。而且,随着时间的推移,集中式系统不会对这种关系产生实质性帮助。因此,将深度学习网络分布到多个服务器上已成为当前深度学习从业者的主要目标。然而,在分布式环境中处理大数据总是伴随着多个挑战。大多数这些挑战在前一节中已被深入解释。这些挑战包括处理高维数据、特征过多的数据、可用内存的存储量、大规模大数据集的处理等等。此外,大数据集对 CPU 和内存的计算资源需求很高。因此,减少处理时间已成为一个极其重要的标准。以下是分布式深度学习中的核心和主要挑战:

  • 我们如何将数据集的块保存在节点的主内存中?

  • 我们如何保持数据块之间的协调,以便它们能够一起被移动并最终得出结果?

  • 我们如何使分布式和并行处理变得极其有序和协调?

  • 我们如何在数据集上实现一种管弦乐式的搜索过程,从而获得高效能?

使用分布式深度学习处理大数据集有多种方式。然而,当我们谈论大数据时,过去五年中应对大多数挑战表现出色的框架是 Hadoop 框架 [77-80]。Hadoop 支持并行和分布式处理。它无疑是最受欢迎且最广泛使用的框架,且相比其他传统框架,它能够更高效地存储和处理大数据量。几乎所有主要的科技公司,如 Google、Facebook 等,都使用 Hadoop 来部署和处理其数据。Google 所设计的多数需要处理大量数据的软件,都使用 Hadoop。Hadoop 的主要优势在于它能跨越成千上万台普通服务器存储和处理大量数据,从而产生一些有序的结果 [81]。从我们对深度学习的基本理解来看,我们可以联想到,深度学习确实需要那种分布式计算能力,才能从输入数据中产生一些奇妙的成果。大数据集可以被拆分成小块,分布到多个普通硬件上进行并行训练。此外,深度神经网络的完整阶段可以拆分为多个子任务,然后这些子任务可以并行处理。

注意

Hadoop 已经成为所有数据湖的汇聚点。将深度学习转移到已经存储在 Hadoop 中的数据,已成为必然的需求。

Hadoop 基于移动计算比移动数据更便宜的概念运作 [86] [87]。Hadoop 允许在集群中的普通服务器上分布式处理大规模数据集。它还提供高效的负载均衡,具有非常高的容错能力,并且在水平扩展方面几乎不需要额外的努力。它可以检测并容忍应用层的故障,因此非常适合在普通硬件上运行。为了实现数据的高可用性,Hadoop 默认保持一个复制因子为三,每个数据块的副本分布在另外两台机器上。因此,如果某个节点发生故障,恢复可以立即从另外两台节点完成。Hadoop 的复制因子可以根据数据的重要性及其他相关需求轻松增加。

Hadoop 最初主要用于处理批量任务,因此它最适合用于深度学习网络,其中主要任务是对大规模数据进行分类。特征选择用于学习如何对数据进行分类,通常是在一大批数据集上进行的。

Hadoop 是极其可配置的,可以根据用户的需求轻松进行优化。例如,如果用户希望为数据保留更多副本以提高可靠性,他可以增加复制因子。然而,副本数量的增加最终会增加存储需求。在这里,我们不会详细解释数据的特性和配置,而是主要讨论 Hadoop 在分布式深度神经网络中的广泛应用部分。

在新版本的 Hadoop 中,本书主要使用的部分是 HDFS、Map-Reduce 和 Yet Another Resource Negotiator (YARN)。YARN 已经在很大程度上主导了 Hadoop 的 Map-Reduce(在下一部分将详细解释)。YARN 目前负责将任务分配给 Hadoop 的数据节点(数据服务器)。Hadoop 分布式文件系统 (HDFS) 是一种分布式文件系统,分布在所有数据节点上,通过一个集中管理的元数据服务器称为 NameNode 来管理。为了实现高可用性,在后续版本中,Hadoop 框架集成了一个二级 NameNode,目的是在特定的检查点后保留主 NameNode 的元数据副本。

Map-Reduce

Map-Reduce 模式 [83] 是 Google 在 2004 年开发的一种分布式编程模型,旨在通过并行和分布式算法在集群中的多个机器上处理海量数据集。整个 Map-Reduce 应用程序在大规模数据集上非常有用。基本上,它有两个主要组件,一个称为 Map,另一个称为 Reduce,此外还有一些中间阶段,如洗牌、排序和分区。在 Map 阶段,大的输入任务被拆分为多个小任务,每个任务会被分配到不同的核心上。然后,在这些机器上的每个小任务上执行相应的操作。Reduce 阶段将所有散布的和转化的输出汇集成一个单一的数据集。

详细解释 Map-Reduce 的概念超出了本章的范围;有兴趣的读者可以阅读 "Map-Reduce: Simplified data processing on large clusters" [83] 以深入了解该内容。

迭代 Map-Reduce

深度学习算法具有迭代特性——模型从优化算法中学习,这些算法会经过多个步骤,以实现最小误差的目标。对于这些模型,Map-Reduce 应用程序似乎不像在其他用例中那样高效。

Iterative Map-Reduce,一种下一代 YARN 框架(与传统的 Map-Reduce 不同),对数据进行多次迭代,而数据仅经过一次。尽管Iterative Map-ReduceMap-Reduce的架构设计不同,但对这两种架构的高层次理解是简单的。Iterative Map-Reduce不过是一个 Map-Reduce 操作的序列,其中第一个 Map-Reduce 操作的输出成为下一个操作的输入,依此类推。在深度学习模型中,map阶段将某一特定迭代的所有操作放置在分布式系统的每个节点上。然后,它将巨大的输入数据集分发到集群中的所有机器上。模型的训练在集群的每个节点上进行。

在将聚合后的新模型发送回每台机器之前,reduce阶段会将从map阶段收集的所有输出进行处理,并计算参数的平均值。Iterative Reduce算法会反复执行相同的操作,直到学习过程完成,并且误差降至接近零。

图 2.4对比了这两种方法的高层次功能。左侧图像显示的是 Map-Reduce 的框图,而右侧则是 Iterative Map-Reduce 的特写。每个“Processor”是一个工作中的深度网络,正在对较大数据集的小块进行学习。在“Superstep”阶段,参数的平均值在整个模型重新分发到整个集群之前进行计算,如下图所示:

Iterative Map-Reduce

图 2.4:Map-Reduce 与并行迭代式 reduce 的功能差异

Yet Another Resource Negotiator (YARN)

YARN 的主要思想是将作业调度和资源管理与数据处理分离开来。这样,数据可以在与 Map-Reduce 批处理作业并行的情况下继续在系统中处理。YARN 拥有一个中央资源管理器,它主要根据需要管理 Hadoop 系统资源。节点管理器(特定于节点)负责管理和监控集群中各个节点的处理过程。此处理过程由一个ApplicationMaster专门控制,ApplicationMaster监控来自中央资源管理器的资源,并与节点管理器协作,监控和执行任务。下图展示了 YARN 架构的概览:

Yet Another Resource Negotiator (YARN)

图 2.5:YARN 高层架构概览

Hadoop 的所有这些组件主要用于分布式深度学习,以克服之前提到的所有挑战。以下小节展示了分布式深度学习要实现更好性能所需满足的标准。

分布式深度学习设计的重要特征

以下是分布式深度学习设计的重要特征:

  1. 小批量处理:在分布式深度学习中,网络必须快速并行地接收和处理数据。为了更准确地处理并提供结果,集群中的每个节点应每次接收约 10 个元素的小数据块。

    例如,假设 YARN 的主节点正在协调 20 个工作节点处理一个 200GB 的大数据集。主节点将数据集拆分成 10GB 的 20 个小批次数据,每个工作节点分配一个小批次。工作节点将并行处理数据,并在完成计算后将结果发送回主节点。所有这些结果将由主节点汇总,并最终将平均结果重新分配给各个工作节点。

    深度学习网络在使用接近 10 的小批量数据时表现更好,而不是使用 100 或 200 个大批量数据。小批量数据使网络能够深入地从数据的不同方向学习,这些学习成果最终重新编译,为模型提供更广泛的知识。

    另一方面,如果批量大小过大,网络会尝试快速学习,这会最大化错误。相反,较小的批量大小会减慢学习速度,并可能导致发散,随着网络接近最小误差率。

  2. 参数平均:参数平均是分布式深度网络训练中的关键操作。在一个网络中,参数通常是节点层的权重和偏置。如在小批量处理部分所述,一旦多个工作节点完成训练,它们会将不同的参数集返回给主节点。每次迭代时,参数会被平均、更新,并返回主节点进行进一步的操作。

    参数平均的顺序过程可以概述如下:

    • 主节点配置初始网络并设置不同的超参数。

    • 根据训练主节点的配置,大数据集被拆分成多个较小的数据集块。

    • 对于每个训练数据集的拆分,直到误差率趋近于零,执行以下操作:

      • 主节点将参数从主节点分配给每个独立的工作节点。

      • 每个工作节点开始使用其专门的数据集块训练模型。

      • 参数的平均值被计算并返回给主节点。

    • 训练完成后,主节点将拥有一份训练网络的副本。

  3. 在分布式训练中,参数平均提供以下两个重要优势:

    • 它通过生成同时的结果来实现并行化。

    • 它通过将给定的数据集分配到多个较小的数据集来帮助防止过拟合。然后,网络学习平均结果,而不是仅仅汇总来自不同小批次的结果。

图 2.6展示了小批量处理和参数平均操作的结合图示概览:

分布式深度学习设计的关键特性

图 2.6:图中展示了分布式深度学习架构的高层次架构图

Deeplearning4j - 一个开源分布式深度学习框架

Deeplearning4jDL4J)[82]是一个为 JVM 编写的开源深度学习框架,主要用于商业级应用。该框架完全使用 Java 编写,因此名称中包含“4j”一词。由于与 Java 的兼容性,Deeplearning4j 开始在更广泛的受众和从业人员中获得越来越多的关注。

该框架基本上由一个分布式深度学习库组成,集成了 Hadoop 和 Spark。借助 Hadoop 和 Spark,我们可以非常容易地分发模型和大数据集,并运行多个 GPU 和 CPU 进行并行计算。Deeplearning4j 主要在图像、声音、文本、时间序列数据等领域的模式识别中取得了显著的成功。除此之外,它还可以应用于各种客户场景,如人脸识别、欺诈检测、商业分析、推荐引擎、图像和语音搜索、以及传感器数据的预测性维护等。

以下图 2.7展示了 Deeplearning4j 的通用高层架构框图:

Deeplearning4j - 一个开源分布式深度学习框架

图 2.7:Deeplearning4j 的高层架构框图 [82]

Deeplearning4j 的主要特点

Deeplearning4j 具有许多独特的功能,使其与其他现有深度学习工具(如 Theano、Torch 等)完全不同。

  • 分布式架构:Deeplearning4j 的训练可以通过两种方式进行——使用分布式、多线程深度学习,或使用传统的单线程深度学习技术。训练在商品节点的集群中进行。因此,Deeplearning4j 能够快速处理任何量级的数据。神经网络采用迭代减少方法进行并行训练,该方法可在 Hadoop YARN 和 Spark 上运行。它还与 Cuda 内核集成,进行纯 GPU 操作,并支持分布式 GPU。

Deeplearning4j 的操作可以作为作业在 Hadoop YARN 或 Spark 上运行。在 Hadoop 中,迭代减少工人会在每个 HDFS 块上工作,并同步并行处理数据。处理完成后,它们将转化后的参数推送回主节点,主节点计算参数的平均值,并更新每个工人节点的模型。

在 Deeplearning4j 中,分布式运行时是可互换的,它们在一个巨大的模块化架构中充当目录的角色,可以根据需要进行替换。

  • 数据并行性:神经网络可以通过两种方式进行分布式训练:一种是数据并行性,另一种是模型并行性。Deeplearning4j 采用数据并行性进行训练。在数据并行性中,我们可以将大型数据集拆分成小块数据集,并将这些数据集分发给在不同服务器上运行的并行模型进行并行训练。

  • JVM 的科学计算能力:在 Java 和 Scala 中的科学计算,Deeplearning4j 包括一个 N 维数组类,使用Java 的 N 维数组ND4J)。ND4J 的功能比 Python 的 Numpy 要快得多,且大部分代码用 C++编写。它实际上是一个用于矩阵操作和线性代数的生产环境库。ND4J 的大多数例程都设计为以最小的内存需求快速运行。

  • 机器学习的向量化工具:为了对各种文件格式和数据类型进行向量化,Canova 已与 Deeplearning4j 合并。Canova 使用类似于 Hadoop 使用 Map-Reduce 的输入/输出系统来执行向量化。Canova 主要用于通过命令行界面CLI)对文本、CSV、图像、声音、视频等进行向量化。

Deeplearning4j 功能总结

以下是 Deeplearning4j 功能的总结:

  • Deeplearning4j 可以称为迄今为止最完整、最具生产力、开源的深度学习库

  • 与基于 Theano 的工具相比,它有更多专为深度网络设计的功能

  • Deeplearning4j 非常易于使用,即使是非专业人士也能运用其惯例解决计算密集型问题

  • 这些工具具有广泛的适用性,因此,网络可以同样有效地处理图像、声音、文本和时间序列数据

  • 它是完全分布式的,可以并行运行多个 GPU,这与未分布式的 Theano [84]和未自动化分布式处理的 Torch7 [85]不同,后者像 DL4J 一样没有自动化其分布式功能

在 Hadoop YARN 上设置 Deeplearning4j

Deeplearning4j 主要用于多层网络的工作。要开始使用 Deeplearning4j,需要熟悉相关的先决条件,以及如何安装所有依赖的软件。大多数文档可以很容易地在 Deeplearning4j 的官方网站上找到:deeplearning4j.org/ [88]。

在本章的这一部分,我们将帮助你熟悉 Deeplearning4j 的代码。最初,我们将展示如何使用 Deeplearning4j 实现一个简单的多层神经网络操作。本节的后半部分将讨论使用 Deeplearning4j 库进行分布式深度学习。Deeplearning4j 通过使用 Apache Spark 在多个分布式 GPU 上训练分布式深度神经网络。本节的后半部分还将介绍 Deeplearning4j 的 Apache Spark 设置。

熟悉 Deeplearning4j

本部分将主要介绍使用 deeplearning4j 的“Hello World”深度学习程序。我们将通过两个简单的深度学习问题,借助库的基本功能进行讲解。

在 Deeplearning4j 中,MultiLayerConfiguration 类可以视为构建块的基础,负责组织神经网络的各层及其相应的超参数。这个类可以看作是 Deeplearning4j 中神经网络的核心构建块。在本书中,我们将使用这个类来配置不同的多层神经网络。

注意

超参数是决定神经网络学习过程的主要支柱。它们通常包括如何初始化模型的权重、更新次数、模型的学习率、使用的优化算法等。

在第一个示例中,我们将展示如何利用 Deeplearning4j 对多层感知器分类器进行数据模式分类。

以下是本程序中将使用的示例训练数据集:

  0, -0.500568579838,  0.687106471955 
  1,  0.190067977988, -0.341116711905 
  0,  0.995019651532,  0.663292952846 
  0, -1.03053733564,   0.342392729177 
  1,  0.0376749555484,-0.836548188848 
  0, -0.113745482508,  0.740204108847 
  1,  0.56769119889,  -0.375810486522 

最初,我们需要初始化网络的各种超参数。以下代码片段将为程序设置 ND4J 环境:

Nd4j.ENFORCE_NUMERICAL_STABILITY = true; 
int batchSize = 50; 
int seed = 123; 
double learningRate = 0.005; 

训练周期数设置为 30

int nEpochs = 30;  
int numInputs = 2; 
int numOutputs = 2; 
int numHiddenNodes = 20; 

以下代码将把训练数据加载到网络中:

RecordReader rr = new CSVRecordReader(); 
rr.initialize(new FileSplit(new File("saturn_data_train.csv"))); 
DataSetIterator trainIter = new RecordReaderDataSetIterator      
                            (rr,batchSize,0,2); 

训练数据加载后,接下来我们将通过以下代码将测试数据加载到模型中:

RecordReader rrTest = new CSVRecordReader(); 
rrTest.initialize(new FileSplit(new File("saturn_data_eval.csv"))); 
DataSetIterator trainIter = new RecordReaderDataSetIterator
                            (rrTest,batchSize,0,2); 

所有网络层的组织以及超参数的设置可以通过以下代码片段完成:

MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder() 
.seed(seed)
.iterations(1)                          
.optimizationAlgo(OptimizationAlgorithm.STOCHASTIC_GRADIENT_DESCENT) 
.learningRate(learningRate) 
.updater(Updater.NESTEROVS).momentum(0.9)
.list() 
.layer(0, new DenseLayer.Builder().nIn(numInputs).nOut(numHiddenNodes) 
   .weightInit(WeightInit.XAVIER) 
   .activation("relu")
   .build()) 
 .layer(1, new OutputLayer.Builder(LossFunction.NEGATIVELOGLIKELIHOOD) 
   .weightInit(WeightInit.XAVIER)                         
   .activation("softmax")
   .nIn(numHiddenNodes).nOut(numOutputs).build())
 .pretrain(false)
 .backprop(true)
 .build(); 

现在,我们已经加载了训练和测试数据集,通过调用 init() 方法可以完成模型的初始化。这也将开始从给定输入中训练模型:

MultiLayerNetwork model = new MultiLayerNetwork(conf); 
model.init(); 

为了检查一定时间间隔后的输出,我们可以每进行 5 次参数更新时打印一次得分:

model.setListeners(new ScoreIterationListener(5));      
for ( int n = 0; n < nEpochs; n++) 
{ 

最后,通过调用 .fit() 方法来训练网络:

 model.fit( trainIter ); 
}  
System.out.println("Evaluating the model....");  
Evaluation eval = new Evaluation(numOutputs); 
while(testIter.hasNext())
  { 
    DataSet t = testIter.next(); 
    INDArray features = t.getFeatureMatrix(); 
    INDArray lables = t.getLabels(); 
    INDArray predicted = model.output(features,false); 
    eval.eval(lables, predicted); 
  } 
System.out.println(eval.stats()); 

所以模型的训练已经完成。在下一部分,数据点将被绘制,并计算数据的对应准确性,如下代码所示:

double xMin = -15;
double xMax = 15; 
double yMin = -15; 
double yMax = 15; 

int nPointsPerAxis = 100; 
double[][] evalPoints = new double[nPointsPerAxis*nPointsPerAxis][2]; 
int count = 0; 
for( int i=0; i<nPointsPerAxis; i++ )
{ 
 for( int j=0; j<nPointsPerAxis; j++ )
 { 
   double x = i * (xMax-xMin)/(nPointsPerAxis-1) + xMin; 
   double y = j * (yMax-yMin)/(nPointsPerAxis-1) + yMin; 

   evalPoints[count][0] = x; 
   evalPoints[count][1] = y; 

   count++; 
 } 
} 

INDArray allXYPoints = Nd4j.create(evalPoints); 

INDArray predictionsAtXYPoints = model.output(allXYPoints); 

以下代码将在图表中绘制之前,将所有训练数据存储在一个数组中:


rr.initialize(new FileSplit(new File("saturn_data_train.csv"))); 
rr.reset(); 
int nTrainPoints = 500; 
trainIter = new RecordReaderDataSetIterator(rr,nTrainPoints,0,2); 
DataSet ds = trainIter.next(); 
PlotUtil.plotTrainingData(ds.getFeatures(), ds.getLabels(),allXYPoints, predictionsAtXYPoints, nPointsPerAxis); 

通过网络运行测试数据并生成预测,可以使用以下代码:

rrTest.initialize(new FileSplit(new File("saturn_data_eval.csv"))); 
rrTest.reset(); 
int nTestPoints = 100; 
testIter = new RecordReaderDataSetIterator(rrTest,nTestPoints,0,2); 
ds = testIter.next(); 
INDArray testPredicted = model.output(ds.getFeatures()); 
PlotUtil.plotTestData(ds.getFeatures(), ds.getLabels(), testPredicted, allXYPoints, predictionsAtXYPoints, nPointsPerAxis); 

当执行上述代码时,它将运行大约 5-10 秒,具体时间取决于你的系统配置。在此期间,你可以查看控制台,控制台将显示模型训练的更新得分。

下面显示了一段评估结果:

o.d.o.l.ScoreIterationListener - Score at iteration 0 is    
                                 0.6313823699951172 
o.d.o.l.ScoreIterationListener - Score at iteration 5 is 
                                 0.6154170989990234 
o.d.o.l.ScoreIterationListener - Score at iteration 10 is     
                                 0.4763660430908203 
o.d.o.l.ScoreIterationListener - Score at iteration 15 is 
                                 0.52469970703125 
o.d.o.l.ScoreIterationListener - Score at iteration 20 is    
                                 0.4296367645263672 
o.d.o.l.ScoreIterationListener - Score at iteration 25 is 
                                 0.4755714416503906 
o.d.o.l.ScoreIterationListener - Score at iteration 30 is 
                                 0.3985047912597656 
o.d.o.l.ScoreIterationListener - Score at iteration 35 is 
                                 0.4304619598388672 
o.d.o.l.ScoreIterationListener - Score at iteration 40 is   
                                 0.3672477722167969 
o.d.o.l.ScoreIterationListener - Score at iteration 45 is 
                                 0.39150180816650393 
o.d.o.l.ScoreIterationListener - Score at iteration 50 is 
                                 0.3353725051879883 
o.d.o.l.ScoreIterationListener - Score at iteration 55 is 
                                 0.3596681213378906 

最后,程序将使用 Deeplearning4j 输出模型训练的不同统计信息,如下所示:

Evaluating the model.... 
Examples labeled as 0 classified by model as 0: 48 times 
Examples labeled as 1 classified by model as 1: 52 times 

在后台,我们可以将数据的绘制过程可视化,这将给人一种土星行星的印象。在下一部分中,我们将展示如何将 Hadoop YARN 与 Spark 以及 Deeplearning4j 进行集成。以下的图 2.8展示了程序的图形化输出:

熟悉 Deeplearning4j

图 2.8:当前面的程序执行时,绘制的散点数据点给人一种土星行星的印象

Hadoop YARN 和 Spark 集成进行分布式深度学习

要在 Hadoop 上使用 Deeplearning4j,我们需要包括deeplearning-hadoop依赖项,如下所示:

<!-- https://mvnrepository.com/artifact/org.Deeplearning4j/Deeplearning4j-hadoop --> 
<dependency> 
    <groupId>org.Deeplearning4j</groupId> 
    <artifactId>Deeplearning4j-hadoop</artifactId> 
    <version>0.0.3.2.7</version> 
</dependency> 

同样地,对于 Spark,我们需要包括deeplearning-spark依赖项,如下所示:

<!-- https://mvnrepository.com/artifact/org.Deeplearning4j/dl4j-spark-nlp_2.11 --> 
<dependency> 
    <groupId>org.Deeplearning4j</groupId> 
    <artifactId>dl4j-spark-nlp_2.11</artifactId> 
    <version>0.5.0</version> 
</dependency> 

解释 Apache Spark 的详细功能超出了本书的范围。感兴趣的读者可以在spark.apache.org/了解更多内容。

配置 Spark 在 Hadoop YARN 上内存分配的规则

如前所述,Apache Hadoop YARN 是一个集群资源管理器。当 Deeplearning4j 通过 Spark 提交训练任务到 YARN 集群时,YARN 负责管理资源分配,如 CPU 核心、每个执行者消耗的内存量等。然而,为了从 YARN 上的 Deeplearning4j 获得最佳性能,需要进行一些精确的内存配置。操作如下:

  • 执行者 JVM 内存量需要通过spark.executor.memory来指定。

  • YARN 容器内存开销需要通过spark.yarn.executor.memoryOverhead来指定。

  • spark.executor.memoryspark.yarn.executor.memoryOverhead的总和必须始终小于 YARN 分配给容器的内存量。

  • ND4j 和 JavaCPP 应当知道堆外内存的分配;这可以通过org.bytedeco.javacpp.maxbytes系统属性来完成。

  • org.bytedeco.javacpp.maxbytes必须小于spark.yarn.executor.memoryOverhead

当前版本的 Deeplearning4j 使用参数平均法执行神经网络的分布式训练。以下操作正是按照前面章节中参数平均部分的描述进行的:

SparkDl4jMultiLayer sparkNet = new SparkDl4jMultiLayer(sc,conf, 
                               new ParameterAveragingTrainingMaster
                              .Builder(numExecutors(),dataSetObjSize 
                              .batchSizePerWorker(batchSizePerExecutor) 
                              .averagingFrequency(1) 
                              .repartionData(Repartition.Always) 
                              .build()); 
sparkNet.setCollectTrainingStats(true); 

要列出 HDFS 中的所有文件,以便在不同节点上运行代码,请运行以下代码:

Configuration config = new Configuration(); 
FileSystem hdfs = FileSystem.get(tempDir.toUri(), config); 
RemoteIterator<LocatedFileStatus> fileIter = hdfs.listFiles
  (new org.apache.hadoop.fs.Path(tempDir.toString()),false); 

List<String> paths = new ArrayList<>(); 
while(fileIter.hasNext())
  { 
   String path = fileIter.next().getPath().toString(); 
   paths.add(path); 
  } 

如何设置 Spark 与 YARN 和 HDFS 的完整代码将与代码包一起提供。为了简化,本文只展示部分代码,以便理解。

现在,我们将展示一个示例,演示如何使用 Spark 并将数据加载到内存中与 Deeplearning4j 结合使用。我们将使用一个基本的 DataVec 示例来展示如何对一些 CSV 数据进行预处理操作。

示例数据集将如下所示:

2016-01-01 17:00:00.000,830a7u3,u323fy8902,1,USA,100.00,Legit 
2016-01-01 18:03:01.256,830a7u3,9732498oeu,3,FR,73.20,Legit 
2016-01-03 02:53:32.231,78ueoau32,w234e989,1,USA,1621.00,Fraud 
2016-01-03 09:30:16.832,t842uocd,9732498oeu,4,USA,43.19,Legit 
2016-01-04 23:01:52.920,t842uocd,cza8873bm,10,MX,159.65,Legit 
2016-01-05 02:28:10.648,t842uocd,fgcq9803,6,CAN,26.33,Fraud 
2016-01-05 10:15:36.483,rgc707ke3,tn342v7,2,USA,-0.90,Legit 

该程序的问题描述如下:

  • 移除一些不必要的列

  • 过滤数据,仅保留MerchantCountryCode列中值为USAMX的示例

  • 替换TransactionAmountUSD列中的无效条目

  • 解析数据字符串,并从中提取出一天中的小时数,创建一个新的HourOfDay

Schema inputDataSchema = new Schema.Builder() 
     .addColumnString("DateTimeString") 
     .addColumnsString("CustomerID", "MerchantID")  
     .addColumnInteger("NumItemsInTransaction") 
     .addColumnCategorical("MerchantCountryCode",  
      Arrays.asList("USA","CAN","FR","MX")) 
     .addColumnDouble("TransactionAmountUSD",0.0,null,false,false)  
     .addColumnCategorical("FraudLabel",Arrays.asList("Fraud","Legit"))
     .build(); 

System.out.println("\n\nOther information obtainable from schema:"); 
System.out.println("Number of columns: " + 
                   inputDataSchema.numColumns()); 
System.out.println("Column names: " +              
                   inputDataSchema.getColumnNames()); 
System.out.println("Column types: " +  
                   inputDataSchema.getColumnTypes()); 

接下来的部分将定义我们要在数据集上执行的操作:

TransformProcess tp = new TransformProcess.Builder(inputDataSchema) 
.removeColumns("CustomerID","MerchantID") 
.filter(new ConditionFilter(
 new CategoricalColumnCondition("MerchantCountryCode", 
 ConditionOp.NotInSet, new HashSet<>(Arrays.asList("USA","MX"))))) 

在非结构化数据中,数据集通常比较嘈杂,因此我们需要处理一些无效数据。如果出现负数美元值,程序会将其替换为0.0。我们会保留正数美元金额不变。

.conditionalReplaceValueTransform( 
  "TransactionAmountUSD",       
  new DoubleWritable(0.0),      
  new DoubleColumnCondition("TransactionAmountUSD",ConditionOp.LessThan
  , 0.0))   

现在,为了按照问题描述格式化DateTime格式,使用以下代码段:

.stringToTimeTransform("DateTimeString","YYYY-MM-DD HH:mm:ss.SSS",            
 DateTimeZone.UTC) 
.renameColumn("DateTimeString", "DateTime") 
.transform(new DeriveColumnsFromTimeTransform.Builder("DateTime") 
   .addIntegerDerivedColumn("HourOfDay", DateTimeFieldType.hourOfDay()) 
   .build())
.removeColumns("DateTime")
.build(); 

执行所有这些操作后,会创建一个不同的架构,具体如下:

Schema outputSchema = tp.getFinalSchema(); 

System.out.println("\nSchema after transforming data:"); 
System.out.println(outputSchema); 

以下代码段将设置 Spark 执行所有操作:

SparkConf conf = new SparkConf(); 
conf.setMaster("local[*]"); 
conf.setAppName("DataVec Example"); 

JavaSparkContext sc = new JavaSparkContext(conf); 

String directory = new  ClassPathResource("exampledata.csv").getFile()
.getParent(); 

要直接从 HDFS 获取数据,必须传递hdfs://{文件路径名称}

JavaRDD<String> stringData = sc.textFile(directory); 

输入数据通过CSVRecordReader()方法进行解析,具体如下:

RecordReader rr = new CSVRecordReader(); 
JavaRDD<List<Writable>> parsedInputData = stringData.map(new  StringToWritablesFunction(rr)); 

执行 Spark 的预定义转换,具体如下:

SparkTransformExecutor exec = new SparkTransformExecutor(); 
JavaRDD<List<Writable>> processedData = exec.execute(parsedInputData, 
tp); 

JavaRDD<String> processedAsString = processedData.map(new 
WritablesToStringFunction(","));  

如前所述,要将数据保存回 HDFS,只需将文件路径放在hdfs://后面即可:

processedAsString.saveAsTextFile("hdfs://your/hdfs/save/path/here") 

List<String> processedCollected = processedAsString.collect(); 
List<String> inputDataCollected = stringData.collect(); 

System.out.println("\n ---- Original Data ----"); 
for(String s : inputDataCollected) System.out.println(s); 

System.out.println("\n ---- Processed Data ----"); 
for(String s : processedCollected) System.out.println(s); 

当使用 Deeplearning4j 执行 Spark 程序时,我们将得到以下输出:

14:20:12 INFO MemoryStore: Block broadcast_0 stored as values in memory (estimated size 104.0 KB, free 1390.9 MB) 
16/08/27 14:20:12 INFO MemoryStore: ensureFreeSpace(10065) called with curMem=106480, maxMem=1458611159 
16/08/27 14:20:12 INFO MemoryStore: Block broadcast_0_piece0 stored as bytes in memory (estimated size 9.8 KB, free 1390.9 MB) 
16/08/27 14:20:12 INFO BlockManagerInfo: Added broadcast_0_piece0 in memory on localhost:46336 (size: 9.8 KB, free: 1391.0 MB) 
16/08/27 14:20:12 INFO SparkContext: Created broadcast 0 from textFile at BasicDataVecExample.java:144 
16/08/27 14:20:13 INFO SparkTransformExecutor: Starting execution of stage 1 of 7 
16/08/27 14:20:13 INFO SparkTransformExecutor: Starting execution of stage 2 of 7 
16/08/27 14:20:13 INFO SparkTransformExecutor: Starting execution of stage 3 of 7 
16/08/27 14:20:13 INFO SparkTransformExecutor: Starting execution of stage 4 of 7 
16/08/27 14:20:13 INFO SparkTransformExecutor: Starting execution of stage 5 of 7 

以下是输出:

---- Processed Data ---- 
17,1,USA,100.00,Legit 
2,1,USA,1621.00,Fraud 
9,4,USA,43.19,Legit 
23,10,MX,159.65,Legit 
10,2,USA,0.0,Legit 

与此示例类似,许多其他数据集可以在 Spark 中以自定义方式处理。从下一章开始,我们将展示具体深度神经网络的 Deeplearning4j 代码。Apache Spark 和 Hadoop YARN 的实现是通用过程,不会根据神经网络的不同而改变。读者可以根据需要使用该代码在集群或本地部署深度网络代码。

总结

与传统的机器学习算法相比,深度学习模型具有解决大量输入数据所带来的挑战的能力。深度学习网络旨在自动从非结构化数据中提取复杂的表示。这一特性使得深度学习成为从大数据中学习隐藏信息的宝贵工具。然而,由于数据的数量和种类每天都在快速增加,深度学习网络需要以分布式方式进行存储和处理。Hadoop,作为应对这种需求最广泛使用的大数据框架,在这种情况下非常方便。我们解释了 Hadoop 中一些分布式深度学习架构所必需的主要组件。分布式深度学习网络的关键特性也进行了深入的讲解。Deeplearning4j,一个开源的分布式深度学习框架,能够与 Hadoop 集成,满足上述不可或缺的需求。Deeplearning4j 完全用 Java 编写,能够以分布式方式通过迭代的 Map-Reduce 更快地处理数据,并能够应对大规模数据所带来的许多问题。我们提供了两个示例,帮助您了解 Deeplearning4j 的基本代码和语法。我们还提供了一些 Spark 配置的代码片段,并与 Hadoop YARN 和 Hadoop 分布式文件系统集成。

本书的下一章将介绍卷积神经网络(CNN),一种流行的深度学习网络。该章节将讨论卷积方法及其如何用于构建主要用于图像处理和图像识别的高级神经网络。接下来,本章将提供如何使用 Deeplearning4j 实现卷积神经网络的信息。

第三章 卷积神经网络

“一个计算机是否能够思考的问题,并不比潜水艇是否能游泳的问题更有趣。”
--埃兹赫尔·W·代克斯特拉

卷积神经网络(CNN)——它是否让你有一种数学与生物学相结合,并且加入了一些微不足道的计算机科学元素的奇特感觉?然而,这类网络一直是计算机视觉领域中最具主导地位和强大功能的架构之一。自 2012 年以来,随着深度学习领域的一些先驱者的贡献,CNN 在分类精度上取得了巨大的进展,开始获得广泛关注。自那时以来,许多高科技公司开始使用深度 CNN 提供各种服务。亚马逊利用 CNN 进行产品推荐,谷歌用它来进行照片搜索,Facebook 则主要用于其自动标记算法。

CNN [89] 是一种前馈神经网络,由具有可学习权重和偏置的神经元组成。这类网络主要用于处理具有网格状拓扑结构的数据。正如其名称所示,CNN 是一种神经网络,其中与普通的矩阵乘法不同,至少在后续的某一层中,使用了一种特殊的线性数学运算——卷积。CNN 的架构旨在利用具有多维结构的输入。包括输入图像的二维结构、语音信号,甚至是一维时间序列数据。凭借这些优势,CNN 在许多实际应用中都取得了巨大的成功。因此,CNN 在自然语言处理、推荐系统、图像识别和视频识别等领域特别成功。

注意

偏置单元是一个额外的神经元,它的值为 1,并被添加到每个预输出层。这些单元与前一层没有连接,因此在真实意义上并不代表任何活动

在本章中,我们将深入讨论 CNN 的构建模块。我们将首先讨论什么是卷积以及卷积操作在神经网络中的必要性。在这一主题下,我们还将涉及池化操作,它是 CNN 中最重要的组成部分。本章的下一个主题将指出 CNN 在处理大规模数据时面临的主要挑战。本章的最后部分将帮助读者学习如何使用 Deeplearning4j 设计 CNN。

本章的主要内容如下:

  • 理解卷积

  • CNN 的背景

  • CNN 的基本层

  • 分布式深度 CNN

  • 使用 Deeplearning4j 实现 CNN

理解卷积

为了理解卷积的概念,假设我们通过激光传感器来定位丢失的手机。假设在时间 t 时,手机的位置可以由激光传感器提供,即 f (t)。激光传感器会为所有 t 的值给出不同的位置读数。激光传感器通常会受到噪声的干扰,这在这个场景中是不可取的。因此,为了获得更少噪声的手机位置测量,我们需要计算各种测量的平均值。理想情况下,测量次数越多,位置的准确性就越高。因此,我们应该进行加权平均,以赋予测量值更多的权重。

加权函数可以由函数 w (b) 给出,其中 b 表示测量的时间。这时,为了推导出一个新的函数,从而更好地估计手机的位置,我们需要在每个时刻对权重进行平均。

新的函数可以表示如下:

理解卷积

上述操作被称为卷积。卷积的传统表示方法使用星号或星号符号 '*' 来表示:

理解卷积

正式而言,卷积可以定义为两个函数的乘积的积分,其中一个函数被反转并移动。此外,通过加权平均,卷积还可以用于其他目的。

从卷积网络的术语来看,我们示例中的函数 f 被称为输入函数,而函数 w,即第二个参数,被称为操作的核心(kernel)。该核心由多个滤波器组成,这些滤波器将用于输入数据以获得输出,即 特征图。更直观地说,核心可以看作是一个膜,只允许输入的期望特征通过它。图 3.1 展示了该操作的示意图:

理解卷积

图 3.1:该图展示了卷积网络的简单表示,其中输入必须通过核心才能提供特征图。

在实际场景中,如我们的示例所示,激光传感器并不能在每个时间点提供测量值。理想情况下,当计算机处理数据时,它只能在某些固定的时间间隔内工作,因此时间是离散的。因此,传感器通常会在某些定义的时间间隔内提供结果。如果我们假设仪器每秒提供一次输出,那么参数 t 将只取整数值。基于这些假设,函数 fw 将仅在 t 的整数值下定义。离散卷积的修改方程可以写成如下形式:

理解卷积

在机器学习或深度学习应用中,输入通常是一个多维数据数组,而卷积核使用算法获取的不同参数的多维数组。基本假设是函数的值仅对一组有限的点为非零,且我们仅存储这些点的值,其他地方为零。因此,无限求和可以表示为一组有限数组元素的求和。例如,对于一个二维图像 I 作为输入和一个对应的二维卷积核 K,卷积函数可以表示如下:

理解卷积

所以,通过这一部分,你已经了解了一些卷积的背景知识。在本章的下一部分,我们将讨论卷积在神经网络中的应用以及卷积神经网络的构建模块。

卷积神经网络的背景

卷积神经网络(CNN)作为深度学习模型的一种特殊形式,并不是一个新概念,它们已经被计算机视觉领域广泛采用了很长时间。该模型在 1998 年由 LeCun 等人成功应用于手写数字识别[90]。但不幸的是,由于 CNN 无法处理高分辨率图像,它的流行度随着时间的推移有所下降。其原因主要是由于硬件和内存的限制,以及大规模训练数据集的缺乏。随着计算能力的不断提高,尤其是 CPU 和 GPU 的广泛普及以及大数据的产生,各种大型数据集(如 MIT Places 数据集(参见 Zhou 等,2014)、ImageNet[91]等)使得训练更大、更复杂的模型成为可能。这一点最早在 Krizhevsky 等人[4]的论文《使用深度卷积神经网络进行 Imagenet 分类》中得到了展示。在这篇论文中,他们通过将错误率降低到传统方法的一半,取得了突破。接下来的几年里,他们的论文成为计算机视觉领域最重要的论文之一。由 Alex Krizhevsky 训练的这个流行网络,名为 AlexNet,可能是计算机视觉领域使用深度网络的起点。

架构概述

我们假设读者已经熟悉传统的神经网络。在本节中,我们将探讨卷积神经网络(CNN)的一般构建模块。

传统的神经网络接收一个单一的向量作为输入,并通过一系列潜在(隐藏)层达到中间状态。每个隐藏层由多个神经元组成,每个神经元与前一层的每个神经元完全连接。最后一层,称为“输出层”,是完全连接的,它负责类别分数。一个由三层组成的常规神经网络如图 3.2所示:

架构概述

图 3.2:该图展示了一个三层常规神经网络的框图。每一层的神经元都与上一层的所有其他层神经元完全连接。

常规神经网络在处理大规模图像时面临巨大挑战。例如,在 CIFAR-10 RGB 数据库中,图像的尺寸为32x32x3,因此,传统神经网络中第一隐藏层的单个全连接神经元将具有32323=3072个权重。虽然权重的数量在一开始看起来似乎是合理的,但随着维度数量的增加,管理这些权重将变得非常繁琐。对于另一个 RGB 图像,如果尺寸变为(300x300x3),那么神经元的总权重数将达到3003003=270000个权重。此外,随着层数的增加,这个数字也将急剧增加,很快会导致过拟合。此外,图像的可视化完全忽视了图像的复杂二维空间结构。因此,神经网络的全连接概念从一开始就似乎无法适应更大维度的数据集。因此,我们需要构建一个能够克服这两种限制的模型:

架构概览

图 3.3:图中展示了 CNN 在三维(宽度、高度和深度)中的排列方式。每一层将三维输入体积转换为相应的三维输出体积,输出的是神经元激活值。红色输入层保持图像,因此其宽度和高度将是图像的尺寸,而深度则为三(红色、绿色和蓝色)。图像来源于 Wikipedia。

解决这个问题的一种方法是用卷积代替矩阵乘法。学习一组卷积滤波器(核)比从整个矩阵(300x300x3)中学习要容易得多。与传统神经网络不同,CNN 的每一层的神经元排列在三维空间中:宽度、高度和深度。图 3.3展示了这一表示方式。例如,在前面提到的 CIFAR-10 示例中,图像的尺寸为32x32x3,这分别是宽度、深度和高度。在 CNN 中,每一层的神经元不会像全连接网络那样与上一层的所有神经元相连,而是仅与上一层中某一子集的神经元相连。该部分的详细内容将在本节后续部分中解释。此外,最终输出层的 CIFAR-10 图像将具有1x1x10的尺寸,因为 CNN 会将完整的图像缩减为一个类别分数的单一向量,并与深度维度一起放置。

CNN 的基本层

卷积神经网络由一系列层组成,其中网络的每一层都通过一个可微分的函数将自己从一个激活体积转换到另一个激活体积。构建 CNN 时使用四种主要类型的层:卷积层、修正线性单元层、池化层和全连接层。所有这些层堆叠在一起,形成一个完整的 CNN。

一个常规的 CNN 可能具有以下架构:

[输入 - 卷积 - ReLU - 池化 - 全连接]

然而,在深度卷积神经网络中,通常会在这五个基本层之间插入更多的层。

一个经典的深度神经网络将具有以下结构:

输入 -> 卷积 -> ReLU -> 卷积 -> ReLU -> 池化 -> ReLU -> 卷积 -> ReLU -> 池化 -> 全连接

如前文所述,AlexNet 可以作为这种结构的完美示例。AlexNet 的架构如图 3.4所示。在每一层之后,都会添加一个隐式的 ReLU 非线性函数。我们将在下一节中详细解释这一点。

你可能会问,为什么 CNN 中需要多层?本章的下一节将为你解释这一点:

CNN 的基本层

图 3.4:图中展示了 AlexNet 的深度和权重的示意图。花括号内的数字表示上方写明的滤波器数量。

CNN 中深度的重要性

在论文[96]中,作者提出了一些统计数据,展示了深度网络如何帮助提高输出的准确性。如前所述,Krizhevsky 等人的架构使用了八层,并在 ImageNet 上进行训练。当移除最上层的全连接层(第 7 层)时,约有 1600 万个参数被移除,性能下降了 1.1%。此外,当移除最上面的两层(第 6 层和第 7 层)时,减少了近 5000 万个参数,并且性能下降了 5.7%。类似地,当移除上面的特征提取层(第 3 层和第 4 层)时,减少了约 100 万个参数,性能下降了 3.0%。为了更好地理解这一情况,当移除上面的特征提取层和全连接层(第 3、4、6 和 7 层)时,模型仅剩下四层。在这种情况下,性能下降了 33.5%。

因此,可以轻松得出结论:我们需要深度卷积网络来提高模型的性能。然而,正如前面所说,深度网络在集中式系统中非常难以管理,因为受限于内存和性能管理的局限性。因此,需要一种分布式的方法来实现深度卷积神经网络。在本章的后续部分,我们将解释如何借助 Deeplearning4j 实现这一点,并将处理过程与 Hadoop 的 YARN 集成。

卷积层

如架构概述中所示,卷积的主要目的是使模型在特定时间内仅处理有限数量的输入。此外,卷积支持三项最重要的特性,这些特性在显著提高深度学习模型性能方面起着关键作用。以下列出了这些特性:

  • 稀疏连接

  • 参数共享

  • 等变表示

现在我们将依次描述这些特性。

稀疏连接

如前所述,传统网络层通过一个包含不同参数的矩阵进行矩阵乘法,这些参数描述了每个输出单元与输入单元之间的相互作用。另一方面,卷积神经网络(CNN)采用稀疏连接,有时也称为稀疏交互或稀疏权重。这个想法通过保持卷积核的大小小于输入来实现,这有助于降低算法的时间复杂度。例如,对于一个大型图像数据集,图像可能包含成千上万或数百万个像素;然而,我们可以通过卷积核识别出图像中的小而重要的特征,比如边缘和轮廓,而这些卷积核只包含几百个或几十个像素。因此,我们只需要保持少量的参数,从而帮助减少模型和数据集的内存需求。这个想法还缓解了操作次数,能够增强整体的计算能力。这反过来大大降低了计算的运行时间复杂度,最终提高了效率。图 3.5 以图示的方式展示了如何通过稀疏连接方法减少每个神经元的感受野数量。

注意

卷积层中的每个神经元都会呈现前一层应用的滤波器的响应。这些神经元的主要作用是将响应传递通过某些非线性函数。前一层中应用该滤波器的总区域称为该神经元的感受野。因此,感受野的大小始终等于滤波器的大小。

稀疏连接

图 3.5:该图展示了稀疏连接下,M 的输入单元如何影响输出单元 N3。与矩阵乘法不同,稀疏连接方法中,感受野的数量从五个减少到三个(M2、M3 和 M4)。箭头还表示了参数共享的方法。来自一个神经元的连接与模型中的两个神经元共享。

因此,使用稀疏连接方法时,每层的感受野比使用矩阵乘法方法时的感受野要小。然而,值得注意的是,对于深度卷积神经网络(CNN),单元的感受野实际上比相应的浅层网络的感受野要大。原因是深度网络中的所有单元几乎都间接连接到网络中的几乎所有神经元。图 3.6展示了这种情况的可视化表示:

稀疏连接

图 3.6:深度卷积神经网络稀疏连接的表示。与图 3.5 不同,图中单元 N3 有三个感受野,而这里 N3 的感受野数量增加到了五个。

改进的时间复杂度

类似于上一节中给出的例子,如果在某一层有p个输入和q个输出,那么矩阵乘法将需要(pq)个参数。算法的运行时间复杂度将变为O(pq)。使用稀疏连接方法时,如果我们将与每个输出相关联的上限连接数限制为n,则只需要nq个参数,运行时复杂度将减少到O(nq)。对于许多实际应用,稀疏连接方法在深度学习任务中提供了良好的性能,同时保持了n << p的参数规模。

参数共享

参数共享可以定义为在模型中,函数的相同参数可以被多个函数使用的过程。在常规神经网络中,权重矩阵的每个元素仅在计算层的输出时使用一次。权重与输入的一个元素相乘,但不会被再次访问。参数共享也可以称为绑定权重,因为用于一个输入的权重值与用于其他输入的权重值是绑定的。图 3.5也可以作为参数共享的一个例子。例如,M2中的某个特定参数同时被N1N3使用。

操作的主要目的是控制卷积层中自由参数的数量。在 CNN 中,卷积核的每个元素几乎在输入的每个位置上都被使用。对此的一个合理假设是,如果某个特征在某个空间位置上是需要的,那么它也应该在其他位置上进行计算。

由于单个深度切片中的所有元素共享相同的参数化类型,因此卷积层中每个深度切片的前向传递可以视为输入体积与神经元权重的卷积。此卷积的结果是激活图。这些激活图集合与深度维度的关联被堆叠在一起,最终形成输出体积。尽管参数共享方法赋予了 CNN 架构平移不变性,但它并未提高前向传播的运行时间。

改进的空间复杂度

在参数共享中,模型的运行时间仍然保持为 O (nq)。然而,它有助于显著减少整体空间复杂度,因为模型的存储需求减少到 n 个参数。由于pq通常是相似大小的,n的值与pq相比几乎可以忽略不计。

注意

卷积在时间复杂度和空间复杂度上都比传统的稠密矩阵乘法方法更高效。

等变表示

在卷积层中,由于参数共享,层具有一种称为平移等变性的特性。一个等变函数被定义为其输出以与输入相同的方式变化的函数。

从数学角度看,如果 X 和 Y 都属于同一群 G,则一个函数f: X 等变表示 †’ Y 被称为等变的,如果对于所有g 等变表示 G和所有xX,都有f (g.x) = g.f(x)

在卷积的情况下,如果我们将g视为任何一个函数,它以相同的幅度移动输入,那么卷积函数对g是等变的。例如,设I是一个函数,对于任何偶坐标,它给出图像的颜色。设h是另一个函数,它将一个图像函数映射到另一个图像函数,给定如下方程:

等变表示

I^/ 是一个图像函数,将I的每个像素向右移动五个单位。因此,我们得到以下结果:

等变表示

现在,如果我们对I应用这个平移操作,然后进行卷积,结果将与我们先对I^/应用卷积,再对输出应用变换函数h时的结果完全相同。

对于图像,卷积操作生成一个二维的特征图,显示输入中所有明确的特征。因此,类似于前面的例子,如果我们将输出中的物体按某个固定比例进行平移,输出表示也会以相同的比例移动。这个概念在某些情况下是有用的;例如,考虑一张包含两支不同球队的板球运动员的合影。我们可以在图像中找到球衣的某些共同特征来识别一些运动员。现在,类似的特征显然也会出现在其他人的 T 恤上。所以,将参数共享到整个图像是相当实际的。

卷积还有助于处理一些特殊类型的数据,这些数据对于传统的固定形状矩阵乘法方法来说是困难的,甚至可以说是无法处理的。

选择卷积层的超参数

到目前为止,我们已经解释了卷积层中的每个神经元是如何连接到输入体积的。在这一部分,我们将讨论控制输出体积大小的方法。换句话说,控制输出体积中神经元的数量,以及它们的排列方式。

基本上,有三个超参数控制卷积层输出体积的大小。它们是:深度、步幅和零填充。

我们如何知道应该使用多少卷积层,过滤器的大小应该是多少,步幅和填充的值应该是多少?这些问题非常主观,其解决方案本质上并不简单。没有研究人员为这些超参数设定任何标准参数。神经网络通常在很大程度上依赖于用于训练的数据类型。这些数据的大小、输入原始图像的复杂性、图像处理任务的类型以及许多其他标准可能有所不同。根据大数据集的普遍思路,我们必须思考如何选择超参数,以推导出正确的组合,从而在适当的尺度上对图像进行抽象。我们将在这一小节中讨论所有这些内容。

深度

在输出体积中,深度被视为一个重要参数。深度对应于我们希望对输入的某些变化应用的过滤器数量。如果第一个卷积层以原始图像作为输入,那么在颜色斑块或不同方向的边缘出现时,沿深度维度的多个神经元可能会被激活。同一区域中的神经元集称为深度列。

步幅

步幅指定了在空间维度(宽度和高度)周围分配深度列的策略。它基本上控制了滤波器如何围绕输入体积进行卷积。步幅可以正式定义为滤波器在卷积过程中移动的量。理想情况下,步幅的值应该是整数,而非分数。从概念上讲,这个量有助于决定在进入下一层之前,我们希望保留多少输入图像信息。步幅越大,保留的信息就越多,供下一层使用。

例如,当步幅为1时,一个新的深度列分配到空间位置,且相邻的空间单位之间有一个单位的间隔。由于列之间的感受野重叠较多,这会产生较大的输出体积。另一方面,如果步幅值增加,感受野之间的重叠会减少,从而导致输出体积的空间维度变小。

我们将通过一个例子来进一步简化这个概念。假设我们有一个77的输入体积和一个33的滤波器(为了简便起见,我们忽略第三维度),步幅为1。在这种情况下,输出体积的维度将是55,如图 3.7所示。然而,这看起来相对简单。现在,步幅为2,保持其他参数不变,输出体积的维度将会缩小为33。在这种情况下,感受野会移动2单位,因此,体积会缩小到33*的维度:

步幅

图 3.7:说明了滤波器如何以步幅 1 卷积 7x7 的输入体积,从而得到 5x5 的输出体积。

这在图 3.8中有进一步说明。所有这些计算基于本节下一个主题中提到的一些公式。现在,如果我们希望进一步增加步幅到3,我们将会面临空间安排的困难,并且需要确保感受野能够适应输入体积。理想情况下,程序员只有在需要更少重叠的感受野并且需要更小的空间维度时,才会增加步幅的值:

步幅

图 3.8:说明了滤波器如何以步幅 2 卷积 7x7 的输入体积,从而得到 3x3 的输出体积。

零填充

我们已经有足够的信息推断出,当我们继续向输入体积应用更多的卷积层时,输出体积的大小会进一步减小。然而,在某些情况下,我们可能希望保留关于原始输入体积的几乎所有信息,以便我们还能够提取低级特征。在这种情况下,我们会在输入体积的边缘周围使用零填充。

这种零填充的大小被视为超参数。它可以定义为超参数,直接用于控制输出体积的空间大小,在我们希望精确保持输入体积的空间大小时。

例如,如果我们对一个32323的输入体积应用一个553的滤波器,输出体积将减小为28283。然而,假设我们希望使用相同的卷积层,但需要保持输出体积为32323,我们将在该层使用大小为2的零填充。这样,我们将得到一个36363的输出体积,如下图所示。如果我们应用三个卷积层,每个滤波器的大小为553,则输出体积将为32323,因此保持输入体积的空间大小不变。图 3.9展示了这种情况的图示:

零填充

图 3.9:输入体积的维度为 32323。两个零边界将生成一个 36363 的输入体积。进一步应用卷积层,使用三个大小为 553 的滤波器,步幅为 1,将得到一个 32323 的输出体积。

超参数的数学公式

本章这一部分将介绍一个方程,用于根据我们到目前为止讨论的超参数计算输出体积的空间大小。这个方程对于选择 CNN 的超参数非常有用,因为这些是决定神经元“适配”网络的因素。输出体积的空间大小可以作为输入体积大小(W)、感受野大小或卷积层神经元的滤波器大小(K)、应用的步幅值(S)以及在边缘使用的零填充量(P)的函数来表示。

计算输出体积空间大小的方程可以写成如下形式:

超参数的数学公式

考虑图 3.7图 3.8中给出的例子,其中W=7K=3,且没有填充,P =0。对于步幅1,我们有S=1,这将给出以下结果:

超参数的数学公式

同样,对于步幅2,该方程会给出2的值:

超参数的数学公式

因此,如图 3.7所示,我们会得到一个空间大小为3的输出。然而,在这种配置下,当应用步幅3时,它将无法适应输入体积,因为这个方程会返回一个分数值2.333作为输出体积:

超参数的数学公式

这也意味着超参数的值之间存在相互约束。前面的例子返回了一个分数值,因此超参数会被视为无效。然而,我们可以通过在边缘添加一些零填充来解决这个问题。

超参数的空间排列具有相互约束关系。

零填充的影响

如零填充部分所述,其主要目的是保持输入体积的信息传递到下一层。为了确保输入和输出体积具有相同的空间尺寸,传统的零填充公式,步幅S=1,如下所示:

零填充的影响

图 3.9中的示例为例,我们可以验证公式的正确性。在该示例中,W = 32K=5S=1。因此,为了确保空间输出体积等于 32,我们选择的零填充数量如下:

零填充的影响

因此,若P=2,输出体积的空间尺寸如下所示:

零填充的影响

因此,这个方程式很好地保持了输入体积和输出体积的空间尺寸相同。

ReLU(修正线性单元)层

在卷积层中,系统基本上通过逐元素的乘法和求和来计算线性操作。深度卷积通常在每层之后执行卷积操作,并在每层之后进行非线性操作。这是必需的,因为级联的线性操作会产生另一个线性系统。层与层之间添加非线性增强了模型的表达能力,优于线性模型。

因此,在每个卷积层之后,会对当前输出应用一个激活层。因此,该激活层的主要目标是为系统引入一些非线性。现代卷积神经网络(CNN)使用修正线性单元ReLu)作为激活函数。

在人工神经网络中,激活函数——整流器,定义如下:

ReLU(修正线性单元)层

其中,x是神经元的输入。

一个操作整流器的单元称为 ReLU。早期,许多非线性函数如tan h、sigmoid 等被用于网络中,但近年来,研究人员发现 ReLU 层效果更好,因为它们帮助网络更快地训练,同时不影响结果的准确性。计算效率的显著提升是其中的一个重要因素。

此外,该层增强了模型和其他整体网络的非线性特性,而不会对卷积层的感受野产生任何影响。

最近,在 2013 年,Mass 等人[94]提出了一种新的非线性版本,称为 leaky-ReLU。Leaky-ReLU 定义如下:

ReLU(修正线性单元)层

其中,α是预设参数。后来,在 2015 年,He 等人[95]更新了该方程,提出参数α也可以进行训练,从而大幅改进了模型。

ReLU 相较于 sigmoid 函数的优势

ReLU 有助于缓解梯度消失问题,这在第一章 深度学习简介中有详细解释。ReLU 将前述函数 f(x) 应用到输入值的所有数据,并将所有负激活值转换为0。对于最大值函数,梯度定义如下:

ReLU 优势相对于 Sigmoid 函数

然而,对于 Sigmoid 函数,当我们增加或减少 x 的值时,梯度往往会消失。

Sigmoid 函数如下所示:

ReLU 优势相对于 Sigmoid 函数

Sigmoid 函数的值域为[0, 1],而 ReLU 函数的值域为0, ![ReLU 优势相对于 Sigmoid 函数 ˆž]。因此,Sigmoid 函数通常用于建模概率,而 ReLU 可以用来建模所有正数。

池化层

该层是卷积神经网络(CNN)的第三阶段。经过若干次线性整流单元(ReLU)后,程序员可能会选择应用池化层。该层也可以被称为降采样层。

池化函数基本上用于进一步修改层的输出。该层的主要功能是用邻域输出的总结统计信息替换网络中某一位置的输出。该层有多种选项,其中最大池化(Max-pooling)是最常用的。最大池化操作[93]在矩形邻域内进行,并报告其中的最大输出。最大池化基本上使用一个大小为2x2的滤波器和相同长度的步幅,即2。然后,将该滤波器应用于输入体积,在滤波器卷积经过的每个区域输出最大值。图 3.10 展示了相同内容的表示。池化层的其他常见选项包括矩形邻域的L2 正常化的平均值、矩形邻域的平均值或基于与中心像素的距离的加权平均。

池化层

图 3.10:带有 2*2 滤波器和步幅为 2 的最大池化示例。图像来源于维基百科。

注意

如果我们关注的是邻域特征,而不是特征的精确位置,那么局部平移不变性是极为有益的。

它在哪里有用,在哪里没有用?

池化层的直观原因是,一旦知道了原始输入体积的某个特定特征,其精确位置相较于与其他特征的相对位置变得无关紧要。借助池化,表示几乎对输入的微小平移保持不变。平移不变性意味着,对于输入的微小平移,大多数池化输出的值不会发生显著变化。

对局部平移的不变性在我们更关注邻近特征而非特征的准确位置时极为有益。然而,在处理计算机视觉任务时,使用池化层需要小心。虽然池化有助于大幅降低模型的复杂性,但它可能最终会丧失模型的位置敏感性。

让我们以图像处理为例,涉及到识别图像中的一个框。池化层在这种情况下会有帮助,如果我们只是简单地想要确定图像中是否存在这个框。然而,如果问题陈述更关心框的准确位置,我们在使用池化层时需要特别小心。再举一个例子,假设我们正在处理一个语言模型,且关心识别两个单词之间的语境相似性。在这种情况下,不建议使用池化层,因为它会丧失一些有价值的特征信息。

因此,可以得出结论,池化层基本上用于减少模型的计算复杂性。池化层更像是一个平均过程,我们更关注一组邻近的特征。该层可以应用于我们可以舍弃一些局部信息的场景中。

全连接层

全连接层是卷积神经网络(CNN)的最终层。该层的输入来自前一层卷积层、ReLU 层或池化层的输出。全连接层接收该输入并输出一个N维向量,其中N是初始输入数据集中的不同类别数量。全连接层的基本工作原理是,它基于从前一层接收到的输出,识别与特定类别最相关的特征。例如,如果模型正在预测一张图片中是否包含猫或鸟,它会在激活图中具有较高的值,这些值代表一些高级特征,如四条腿或翅膀。

分布式深度 CNN

本章这一部分将介绍一些极具挑战性的深度 CNN 架构,相关的网络挑战,以及为克服这些挑战而需要的大规模分布式计算。此部分还将解释 Hadoop 及其 YARN 如何为这个问题提供足够的解决方案。

最流行的激进深度神经网络及其配置

近年来,CNN 在图像识别方面展示了惊人的成果。然而,不幸的是,它们的训练成本非常高。以顺序训练过程为例,卷积操作大约占据了总运行时间的 95%。在大规模数据集的情况下,即使是低规模的分布式训练,训练过程也需要几天时间才能完成。2012 年获得大奖的 CNN,AlexNet,使用两个 GTX 580 3 GB GPU,在 ImageNet 数据集上进行训练,几乎花费了一整周的时间。以下表格列出了几种最受欢迎的分布式深度 CNN,它们的配置和训练过程所花费的时间:

模型 计算能力 数据集 层数 训练过程所需时间
AlexNet 两个 NVIDIA GTX 580 3 GB GPU 在 ImageNet 数据集上训练,该数据集包含来自超过 22,000 个类别的 1500 万张高分辨率图像。 八层 五到六天。
ZFNet [97] GTX 580 GPU 130 万张图像,覆盖 1000 个不同的类别。 八层 十二天。
VGG Net [98] 4 个 Nvidia Titan Black GPU 数据集包含 1000 个类别的图像,分为三部分:训练集(130 万图像)、验证集(5 万图像)和测试集(10 万图像,部分类别标签被排除)。 19 层 两到三周。
GoogLeNet [99] 一些高端 GPU 120 万张训练图像。 网络深度为 22 层(仅计算具有参数的层,若包括池化层则为 27 层)。该网络的总层数(独立构建模块)大约为 100 层。 一周内。
Microsoft ResNet [100] 8 GPU 机器 在 128 万张训练图像上进行训练,并在 5 万张验证图像上进行评估。 152 层。 两到三周。

训练时间 - 深度神经网络面临的主要挑战

从前面的表格可以明确推断出,研究人员在提高结果准确性方面付出了大量努力。表格中突出的一点是,网络层数已成为提高准确性的主要标准之一。微软的 ResNet 使用了一个深度达 152 层的神经网络,这成为了一个极具攻击性的深度神经网络架构。该架构在 2015 年通过深度卷积神经网络(CNN)在分类、定位和检测等领域创下了许多新纪录。此外,ResNet 还凭借惊人的错误率改进,仅为 3.6%,赢得了 2015 年 ILSVRC(ImageNet Large Scale Visual Recognition Challenge)。

尽管深度卷积神经网络几乎达到了预期的准确率,几乎所有这些深度 CNN 的主要问题仍然是表格中最右侧的那一列。因此,这表明当前训练深度 CNN 的挑战在于构建一个大规模的分布式框架,以便通过快速互联的网络将训练过程并行化,分配到多个 CPU 和 GPU 上。

Hadoop 用于深度 CNN

本节将解释如何使用 Hadoop 在大规模上分发深度模型以加速处理。

CNN 的运行时间可以分为两大类:

  • 网络中所有的卷积层消耗了大约 90-95%的计算。它们使用了大约 5%的参数,并且有较大的表示形式。[101]

  • 剩余的计算大约占 5-10%,由全连接层处理。它们使用了大约 95%的参数,并且表示形式较小。

Alex Krizhevsky 在[101]中提出了一种使用分布式架构训练卷积神经网络(CNN)的算法。在传统的 CNN 中,卷积操作本身几乎占用了整个过程的全部运行时间;因此,应该使用数据并行性来加速训练。然而,对于全连接层,建议使用模型并行性的方法。本节将通过 Hadoop 及其 YARN 来解释该算法。

在 Hadoop 中,分布式系统的工作节点位于每个 HDFS 块上,并同步并行处理数据。我们将使用一个小批量大小,1024 个来自原始输入图像的示例,这些示例将被拆分为 N 个多个Hadoop 分布式文件系统HDFS)的块。因此,总共有N个工作节点将为每个小批量数据进行工作。HDFS 的块大小将保持为大小K。那么,K的大小应该是多少呢?虽然较小的K将增加块的数量,并有助于加快训练,但较大的N也最终会增加在 NameNode 中存储的元数据量。在这种情况下,一个主要的缺点是 Hadoop 的单点故障SPOF)问题,且更容易出现 NameNode 的主内存较小。然而,使用较大的K,我们将得到较少数量的 HDFS 块,因此,平行工作的节点数量将减少。这又会使训练过程变慢。因此,选择K的最佳方法将主要取决于以下三个因素:

  • 您的 NameNode 的主内存大小可用性。

  • 输入批量的大小以及对每个数据块执行的操作的复杂性。

  • 数据的中间结果有多重要或有价值。根据这些标准,我们可以设置复制因子。然而,复制因子越高,NameNode 的负载就越大。

HDFS 的块分布在 Hadoop 的所有 DataNode 上,YARN 将在这些 DataNode 上并行运行。

分布式训练卷积层的步骤如下:

  1. 每个N块都分配了来自原始输入图像的不同小数据批量,共 1024 个示例。

  2. 相同大小的滤波器和步幅应用于每个 N 个块,这将基于输入的值生成单独的空间输出。

  3. ReLU 会在所有这些上并行、同步地应用,以在结果中引入一定的非线性。

  4. 根据结果的需求,可以在这些独立的数据块上应用最大池化或任何其他下采样算法。

  5. 每次迭代的输出(转化后的参数)会被发送回名为资源管理器的主节点,在那里它们的参数会被平均。更新后的新参数会被发送回每个N块,再次执行相应的操作。

  6. 步骤 2 到步骤 5 会为预定的周期数重复执行。

对于全连接层,任何一个正在处理N个数据块中一个小批量输入图像的N个工作节点中的一个,会将最后一阶段的卷积活动发送给其他所有的(N-1)个工作节点。然后,工作节点会对这批 1024 个样本执行全连接操作,随后开始反向传播这些 1024 个样本的梯度。接下来的工作节点与此操作并行,会将其最后一阶段的卷积层活动发送给其他工作节点,类似于之前的情况。工作节点将再次对第二批 1024 个样本执行全连接活动。此过程会迭代,直到我们得到具有所需最小误差的结果。

在这种方法中,工作节点将它们的最后一阶段卷积层信息广播给所有其他工作节点。这种方法的主要优势是,通信的大部分部分((N-1)/N)可以被抑制,并且可以与全连接层的计算并行执行。这种方法在网络通信方面具有极大的优势。

因此,很明显,Hadoop 在 HDFS 和 Hadoop YARN 的帮助下,对于提供分布式环境给 CNN 具有极大的好处。

现在我们已经熟悉了通过 Hadoop 并行分布模型的方法,本节的下一部分将讨论每个工作节点在 HDFS 的每个数据块上进行操作的编码部分。

使用 Deeplearning4j 的卷积层

本章这一部分将提供如何使用 Deeplearning4j 编写 CNN 代码的基本思路。你将能够学习本章中提到的各种超参数的语法。

使用 Deeplearning4j 实现 CNN 的整个思路可以分为三个核心阶段:加载数据或数据准备、网络配置以及模型的训练与评估。

加载数据

对于 CNN,一般来说,我们只处理图像数据来训练模型。在 Deeplearning4j 中,图像可以通过ImageRecordReader读取。以下代码片段展示了如何加载16×16的彩色图像进行模型训练:

RecordReader imageReader = new ImageRecordReader(16, 16, false);
imageReader.initialize(new FileSplit(new      
File(System.getProperty("user.home"), "image_location")));

之后,使用CSVRecordReader,我们可以从输入的 CSV 文件中加载所有的图像标签,方法如下:

int numLinesToSkip = 0;
String delimiter = ",";
RecordReader labelsReader = new    
CSVRecordReader((numLinesToSkip,delimiter);
labelsReader.initialize(new FileSplit(new   
File(System.getProperty("user.home"),"labels.csv_file_location"))

为了将图像和标签数据结合在一起,我们可以使用ComposableRecordReaderComposableRecordReader在需要合并来自多个源的数据时也非常有用:

ComposableRecordReader(imageReader,labelsReader);

类似地,在某些情况下,如果需要将 MNIST 数据集加载到模型中,而不是使用 imageset,我们可以使用以下部分。此示例使用了一个随机数种子12345

DataSetIterator mnistTrain = new      
MnistDataSetIterator(batchSize,true,12345);
DataSetIterator mnistTest = new     
MnistDataSetIterator(batchSize,false,12345);

模型配置

操作的下一部分是配置 CNN。Deeplearning4j 提供了一个简单的构建器,可以逐层定义深度神经网络,设置不同的超参数:

MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder() MultiLayerConfiguration.Builder builder = new  
NeuralNetConfiguration.Builder() 
.seed(seed) 
.iterations(iterations)
.regularization(true)
.l2(0.0005) 
.learningRate(0.01)

第一个层次,卷积层,可以使用ConvolutionLayer.Builder方法来调用。.build()函数用于构建该层。.stride()用于设置该卷积层的步幅大小:

.layer(0, new ConvolutionLayer.Builder(5, 5)

nInnOut表示深度。这里的nInnChannelsnOut是应用于卷积的过滤器数量:

.nIn(nChannels)
.stride(1, 1)
.nOut(20)

为了将恒等函数添加为激活函数,我们将以这种方式定义它:

.activation("identity")
.build())

为了添加一个类型为最大池化的 Pooling 层,我们将在第一个层之后调用SubsamplingLayer.Builder()方法:

.layer(1, new SubsamplingLayer.Builder(SubsamplingLayer.PoolingType
.MAX)
.kernelSize(2,2)
.stride(2,2)
.build())

修正线性单元ReLU)层可以通过调用新的DenseLayer.Builder().activation("relu")来添加:

.layer(4, new DenseLayer.Builder().activation("relu")
.nOut(500).build())

可以通过调用init()方法来初始化模型,如下所示:

MultiLayerNetwork model = new MultiLayerNetwork(getConfiguration());
model.init();

训练与评估

如前面所述,在训练阶段,我们需要将整个大数据集分成多个批次。然后,模型将在 Hadoop 中逐个处理这些批次。假设我们将数据集分成 5000 个批次,每个批次包含 1024 个示例。1024 个示例将被拆分成多个块,工人将在并行处理。这种大数据集的拆分操作是通过RecordReaderDataSetIterator()方法完成的。让我们首先初始化需要调用该方法的参数,如下所示:

int batchSize = 1024; 
int seed = 123;
int labelIndex = 4; 
int iterations = 1

假设图像中的类别总数为10

int numClasses = 10;

现在,既然我们已经为RecordReaderDataSetIterator()设置了参数数量,我们可以调用该方法来设置训练平台:

DataSetIterator iterator = new RecordReaderDataSetIterator(recordReader,batchSize,labelIndex,numClasses);
DataSet batchData= iterator.next();
batchData.shuffle();

在训练阶段,我们可以将批次随机拆分为训练集和测试集。如果我们希望 70 个样本用于训练集,剩余的 30 个样本用于测试集,我们可以通过以下配置来设置:

SplitTestAndTrain testAndTrain = batchData.splitTestAndTrain(0.70); 
DataSet trainingData = testAndTrain.getTrain();
DataSet testData = testAndTrain.getTest();

trainAndTest =batchData.splitTestAndTrain(0.70);
trainInput = trainAndTest.getTrain();                

testInput.add(trainAndTest.getTest().getFeatureMatrix());

当模型完全训练完成后,对于每个批次,可以保存测试数据以验证模型。因此,通过定义一个Evaluation类的对象,我们将能够收集整个数据集的统计信息:

Evaluation eval = new Evaluation(numOfClasses);
for (int i = 0; i < testInput.size(); i++) 
{
 INDArray output = model.output(testInput.get(i));
 eval.eval(testLabels.get(i), output);
}

现在,模型已经完全准备好进行训练。这可以通过调用fit()方法来完成,如下所示:

model.fit(trainInput);

总结

CNNs,虽然不是一个新概念,但在过去的半个世纪获得了巨大的流行。该网络主要应用于视觉领域。过去几年中,谷歌、微软、苹果等技术公司以及各大著名研究者对 CNN 进行了重大研究。本章从开始就讨论了卷积的概念,这是这种类型网络的核心。继而介绍了这个网络的各种层。接着,为深度 CNN 的每一层提供了深入的解释。之后,理论上和数学上解释了各种超参数及其与网络的关系。随后,本章讨论了如何借助 Hadoop 及其 YARN 将深度 CNN 分布到各个机器上的方法。最后部分讨论了如何利用 Deeplearning4j 在每个处理 Hadoop 每个块的工作人员上实现此网络。

在下一章中,我们将讨论另一种流行的深度神经网络,称为循环神经网络。近年来,循环神经网络因其能够建模可变长度序列而广受欢迎。到目前为止,这种网络已成功应用于语言建模、手写识别、语音识别等不同问题。

第四章:循环神经网络

我认为大脑本质上是一个计算机,意识就像一个计算机程序。当计算机关机时,程序就会停止运行。从理论上讲,它可以在神经网络上重新创建,但这将非常困难,因为这需要重建一个人的所有记忆。
--斯蒂芬·霍金

解决每个问题时,人们并不是从零开始思考。我们的思维是非易失性的,它是持久的,就像计算机的只读存储器ROM)一样。当我们阅读一篇文章时,我们从对句子中早期单词的理解中理解每个单词的含义。

让我们通过一个现实生活中的例子来进一步解释这个问题。假设我们想基于视频中每个时刻发生的事件进行分类。由于我们没有视频早期事件的信息,对于传统深度神经网络来说,要找到一些区分这些事件的理由将是一个繁琐的任务。传统的深度神经网络无法执行这个操作,因此,这是它们的一个主要限制。

循环神经网络RNN)[103]是一种特殊类型的神经网络,能够为这些困难的机器学习和深度学习问题提供许多神秘的解决方案。在上一章,我们讨论了卷积神经网络,它专门处理一组值X(例如,一张图像)。类似地,RNN 在处理一系列值时具有神奇的能力,x (0)x (1)x(2)...x(τ-1)。为了在本章开始讨论 RNN,我们首先将这个网络与卷积神经网络进行对比,这样你就能对其基本功能有所了解,并大致了解这个网络。

卷积神经网络可以轻松扩展到具有大宽度、高度和深度的图像。此外,一些卷积神经网络也可以处理变尺寸的图像。

相比之下,循环网络可以轻松扩展到长序列;此外,其中大多数还可以处理变长序列。为了处理这些任意长度的输入序列,RNN 利用其内部记忆。

RNN 通常在小批量序列上操作,并包含时间步索引t范围从0(τ-1)的向量x (t)。序列长度τ对于小批量中的每个成员也可以有所不同。这个时间步索引不一定总是指代现实世界中的时间间隔,还可以指代序列内部的位置。

当 RNN 展开时,可以看作是一个深度神经网络,具有不定数量的层。然而,与常见的深度神经网络相比,RNN 的基本功能和架构有所不同。对于 RNN 来说,层的主要功能是带来记忆,而非层级处理。对于其他深度神经网络,输入仅在第一层提供,输出则在最后一层生成。然而,在 RNN 中,输入通常在每个时间步接收,并在这些时间间隔内计算相应的输出。随着每次网络迭代,新的信息被整合进每一层,网络可以根据这些信息进行无限次的更新。然而,在训练阶段,循环权重需要学习该传递哪些信息,哪些信息应该被拒绝。这个特点促使了一个特殊形式的 RNN 的出现,称为长短期记忆(LSTM)

RNN 的起源可以追溯到几十年前[104],但近年来,它已成为建模变长序列的热门选择。到目前为止,RNN 已经成功应用于许多问题,如学习词嵌入[105]、语言建模[106][107][108]、语音识别[109]和在线手写识别[110]。

本章中,我们将讨论你需要了解的关于 RNN 及其核心组件的所有内容。稍后,我们将介绍一种特殊类型的 RNN——长短期记忆(LSTM)。

本章的主题组织结构如下:

  • 循环神经网络与其他神经网络有何不同?

  • 循环神经网络(RNNs)

  • 时间反向传播(BPTT)

  • 长短期记忆(LSTM)

  • 双向 RNN

  • 分布式深度 RNN

  • 使用 Deeplearning4j 的 RNN

循环神经网络与其他神经网络有何不同?

你可能好奇 RNN 的特别之处。本章的这一部分将讨论这些内容,接下来的章节将介绍这种类型网络的构建模块。

从第三章的卷积神经网络中,你可能已经感受到卷积网络的严峻限制,它们的 API 过于受限;网络只能接收固定大小的输入向量,并且输出也是固定大小的。此外,这些操作是通过预定义数量的中间层来执行的。使得 RNN 与其他神经网络区别开来的主要原因是它们能够处理长序列的向量,并生成不同的向量序列作为输出。

"如果训练传统神经网络是对函数的优化,那么训练循环网络就是对程序的优化"
--亚历克斯·勒布伦

我们在图 4.1中展示了神经网络的不同类型输入输出关系,以描绘它们的差异。我们展示了以下五种输入输出关系:

  • 一对一:这种输入输出关系适用于没有涉及 RNN 的传统神经网络处理。主要用于图像分类,其中映射是从固定大小的输入到固定大小的输出。

  • 一对多:在这种关系中,输入和输出保持一对多的关系。模型通过一个固定大小的输入生成一系列输出。常见于模型接受图像(图像描述),并生成一串单词的句子。

  • 多对一:在这种关系中,模型接受一系列输入,并输出一个单一的观测值。例如,在情感分析中,模型接受句子或评论,并将句子分类为积极或消极情感。

  • 多对多(可变中间状态):模型接收一系列输入,并生成相应的一系列输出。在这种类型中,RNN 读取英文句子,然后将其翻译并输出德文句子。用于机器翻译的场景。

  • 多对多(固定数量的中间状态):模型接收一个同步的输入序列,并生成一系列输出。例如,在视频分类中,我们可能希望对电影的每个事件进行分类。

递归网络与其他网络的不同之处

图 4.1:图中的矩形表示序列向量的每个元素,箭头表示函数。输入向量以红色显示,输出向量以蓝色显示。绿色表示中间 RNN 的状态。图片来自[111]。

涉及序列的操作通常比具有固定大小输入和输出的网络更强大和更具吸引力。这些模型用于构建更智能的系统。在接下来的章节中,我们将看到如何构建 RNN,以及网络如何通过定义的函数将输入向量与其状态向量结合,从而生成新的状态向量。

循环神经网络(RNNs)

本节将讨论 RNN 的架构。我们将讨论时间如何展开以进行递归关系,并用于执行 RNN 中的计算。

展开递归计算

本节将解释展开递归关系如何导致深度网络结构中参数的共享,并将其转换为计算模型。

让我们考虑一个简单的动态系统递归形式:

展开递归计算

在前面的方程中,s ^((t))表示系统在时间t的状态,θ是所有迭代中共享的相同参数。

这个方程被称为递归方程,因为计算s ^((t))需要s ^((t-1))返回的值,s ^((t-1))的值将需要s ^((t-2))的值,依此类推。

这是一个简单的动态系统表示,目的是帮助理解。让我们再举一个例子,其中动态系统由外部信号x ^((t))驱动,并产生输出y ^((t))

展开递归计算

理想情况下,RNN 遵循第二种类型的方程式,其中中间状态保留了关于整个过去序列的信息。然而,任何涉及递归的方程式都可以用来建模 RNN。

因此,与前馈神经网络类似,RNN 的隐藏(中间)层状态可以通过时间t的变量h来定义,如下所示:

展开递归计算

我们将在本节的下一部分解释这个前面方程在 RNN 中的功能。到目前为止,为了说明这个隐藏层的功能,图 4.2展示了一个没有输出的简单递归网络。图的左侧显示了一个网络,其当前状态影响下一个状态。循环中间的框表示两个连续时间步之间的延迟。

如前面的递归方程所示,我们可以展开或展开时间中的隐藏状态。图像的右侧显示了递归网络的展开结构。在那里,通过在时间上展开,网络可以转换为前馈网络。

在展开的网络中,每个时间步的每个变量可以作为网络的一个独立节点展示。

展开递归计算

图 4.2:图的左侧显示了递归网络,其中信息在每个时间步通过隐藏层多次传递。在右侧,我们有相同网络的展开结构。该网络的每个节点都与一个时间戳相关联。

因此,来自图 4.2的展开操作可以定义为一个操作,它将左侧电路的映射执行到右侧分割成多个状态的计算模型。

在时间上展开的模型的优势

在时间上展开一个网络带来了几个主要优势,列举如下:

  • 一个没有参数的模型将需要大量的训练示例用于学习。然而,学习一个共享的单一模型有助于推广序列长度,甚至是那些在训练集中不存在的序列。这使得模型能够使用较少的训练示例来估计即将到来的序列数据。

  • 不论序列的长度如何,模型的输入大小始终保持不变。在展开模型中,输入大小是指从隐藏状态到其他状态的过渡。然而,对于其他情况,它是根据状态历史的未定义长度来指定的。

  • 由于参数共享,相同的过渡函数f可以在每个时间步使用相同的参数。

RNN 的记忆

到目前为止,您可能已经对前馈神经网络和递归网络之间的主要区别有了一些了解。反馈循环作为其自己中间结果的输入被摄入到下一个状态中。每个输入序列的每个元素都执行相同的任务。因此,每个隐藏状态的输出取决于先前计算的结果。在实际情况中,每个隐藏状态不仅关注当前活动的输入序列,还关注它们在时间上一步感知到的内容。因此,理想情况下,每个隐藏状态必须具有上一步结果的所有信息。

由于需要持久信息,据说 RNN 具有其自身的内存。顺序信息作为循环网络隐藏状态中的记忆得以保留。这有助于处理随着每个新序列向前级联更新处理的即将到来的时间步骤。

图 4.3显示了 Elman 在 1990 年提出的简单递归神经网络的概念[112];它展示了为 RNN 提供持久内存的插图。

在下一个图中,底部的字序列 AYSXWQF 的一部分代表当前正在考虑的输入示例。此输入示例的每个框表示一个单元池。向前箭头显示了从每个发送输入单元到下一个时间步的每个输出单元的完整可训练映射。上下文单元可以被视为持久内存单元,保留了前几步的输出。从隐藏层到上下文单元的向后箭头显示了输出的复制操作,用于评估下一个时间步的结果。

RNN 在时间步t的决策主要取决于其在(t-1)时间步的上一个决策。因此,可以推断出,与传统神经网络不同,RNN 有两个输入源。

其中一个是考虑中的当前输入单元,即下图中的X,另一个是从图中的上下文单元获取的来自最近过去的信息。这两个源结合起来决定当前时间步的输出。关于这点将在下一节中讨论。

RNN 的记忆

图 4.3: 显示了具有 RNN 记忆概念的简单递归神经网络。

架构

因此,我们了解到 RNN 有它们的记忆,用于收集到目前为止计算的信息。在本节中,我们将讨论 RNN 的一般结构及其工作原理。

一个典型的 RNN,在进行前向计算时的展开(或展开)过程如 图 4.4 所示。

展开或展开一个网络意味着将网络写成完整的输入序列。让我们在开始解释结构之前举个例子。如果我们有一个包含 10 个单词的序列,那么 RNN 将展开成一个 10 层深的神经网络,每一层对应一个单词,如下图所示:

架构

图 4.4:该图展示了一个 RNN 被展开或展开成完整网络的过程。

从输入 x 到输出 o 的时间段被分割成多个时间戳,分别为 (t-1)t(t+1),以此类推。

RNN 的计算步骤和公式列举如下:

  • 在前面的图中,x[t] 是时间步 t 的输入。图中展示了三个时间戳 (t-1)t(t+1) 的计算,其中输入分别为 x[(t-1)]x[t]x[(t+1)]。例如,x[1]x[2] 是对应于序列中第二个和第三个单词的向量。

  • s[t] 表示时间步 t 的隐藏状态。从概念上讲,这个状态定义了神经网络的记忆。从数学角度来看,s[t] 的公式或记忆传递过程可以写成如下形式:

架构

因此,隐藏状态是时间步 x[t] 输入与权重 U 的乘积,再加上上一个时间步的隐藏状态 s[t-1],它与自身的隐藏状态到隐藏状态的矩阵 W 相乘。这种隐藏状态到隐藏状态的转换通常称为转移矩阵,类似于马尔可夫链。权重矩阵像滤波器一样起作用,主要决定过去隐藏状态和当前输入的重要性。当前状态产生的误差会通过反向传播返回,用于更新这些权重,直到误差被最小化到期望值。

注意

为了计算第一个隐藏状态,我们需要确定值 s-1,通常初始化为全零。

与传统的深度神经网络不同,在传统深度神经网络中,每一层的计算都使用不同的参数,而 RNN 在所有时间步中共享相同的参数(这里是 UVW),用来计算隐藏层的值。这使得神经网络的训练过程更为简便,因为我们需要学习的参数数量较少。

这份输入权重和隐藏状态的总和通过函数 f 被压缩,通常 f 是一个非线性函数,如逻辑 Sigmoid 函数、tan h 或 ReLU:

  • 在最后一张图中,o[t] 表示在时间步 t 上的输出。步骤 o[t] 的输出仅基于时间 t 时刻网络可用的记忆进行计算。从理论上讲,虽然 RNN 可以持续记忆任意长的序列,但实际上这有点复杂,它们仅限于回溯几个时间步。数学上,这可以表示如下:

架构

下一节将讨论如何通过反向传播训练一个 RNN。

时间反向传播(BPTT)

你已经学过,RNN 的主要需求是明确地分类顺序输入。误差的反向传播和梯度下降主要有助于执行这些任务。

对于前馈神经网络,反向传播是从最终的误差输出、权重和每个隐藏层的输入反向传递的。反向传播通过计算每个权重的偏导数来分配导致误差的权重:时间反向传播(BPTT),其中 E 表示误差,w 是相应的权重。偏导数作用于学习率,梯度下降更新权重,以最小化误差率。

然而,一个 RNN 在没有直接使用反向传播的情况下,使用它的扩展,称为时间反向传播BPTT)。在本节中,我们将讨论 BPTT,解释 RNN 的训练是如何进行的。

误差计算

时间反向传播BPTT)学习算法是传统反向传播方法的自然扩展,它在一个完整展开的神经网络上计算梯度下降。

图 4.5 显示了展开的 RNN 中每个隐藏状态的相关误差。从数学上讲,每个状态相关的误差可以表示如下:

误差计算

其中 o[t] 代表正确的输出,ô[t] 代表在时间步 t 上的预测词。整个网络的总误差(成本函数)是通过对每个时间步的所有中间误差求和来计算的。

如果 RNN 展开为多个时间步,从 t[0]t[n-1],则总误差可以表示如下:

误差计算误差计算

图 4.5:该图显示了 RNN 每个时间步的相关误差。

在时间反向传播方法中,与传统方法不同,梯度下降的权重在每个时间步都会更新。

w[ij] 表示从神经元 i 到神经元 j 的权重连接。η 表示网络的学习率。因此,从数学上讲,梯度下降的权重更新在每个时间步可以通过以下方程表示:

误差计算

长短期记忆(LSTM)

在本节中,我们将讨论一个特殊的单元,称为长短期记忆LSTM),它被集成到 RNN 中。LSTM 的主要目的是防止 RNN 的一个重要问题——梯度消失问题。

深度反向传播在时间上的问题

与传统的前馈网络不同,由于 RNN 在狭窄时间步长下的展开,生成的前馈网络可能会非常深。这有时使得通过时间反向传播程序进行训练变得极其困难。

在第一章中,我们讨论了梯度消失问题。展开的 RNN 在进行反向传播时会遭遇梯度消失或梯度爆炸问题。

RNN 的每个状态都依赖于其输入和上一个输出与当前隐藏状态向量的乘积。反向传播过程中,梯度在逆向传播时执行相同的操作。展开的 RNN 的各层和多个时间步通过乘法相互关联,因此导数在每次传递时容易消失。

另一方面,小梯度趋向于变得更小,而大梯度在每次时间步传递时会变得更大。这分别导致了 RNN 的梯度消失或梯度爆炸问题。

长短期记忆

在 90 年代中期,德国研究人员 Sepp Hochreiter 和 Juergen Schmidhuber 提出了一种更新版的 RNN,加入了一个特殊单元,称为长短期记忆LSTM)单元,以防止梯度爆炸或梯度消失问题[116]。

LSTM 有助于保持恒定的误差,这些误差可以在时间和网络的每一层中传播。恒定误差的保持使得展开的循环神经网络能够在极深的网络中学习,甚至展开到千个时间步。这最终打开了一条通道,可以远程关联因果关系。

LSTM 的架构通过特殊的记忆单元保持恒定的误差流。下图(图 4.6)展示了 LSTM 的基本框图,便于理解:

长短期记忆

图 4.6:该图显示了长短期记忆的基本模型。

如前图所示,LSTM 单元由一个主要用于长期存储信息的记忆单元组成。三个专门的门神经元——写入门、读取门和遗忘门——保护着对该记忆单元的访问。与计算机的数字存储不同,门是模拟的,范围从 0 到 1。模拟设备相对于数字设备具有额外的优势,因为它们是可微分的,因此,适合反向传播方法的需要。LSTM 的门单元并不将信息作为输入传递给下一个神经元,而是设置与神经网络其他部分连接到记忆单元的相关权重。记忆单元基本上是一个自连接的线性神经元。当遗忘单元被重置(设置为0)时,记忆单元会将其内容写入自身并记住记忆的最后内容。对于写入操作,遗忘门和写入门应被设置(设置为1)。此外,当遗忘门输出接近1时,记忆单元实际上会忘记它之前存储的所有内容。现在,当写入门被设置时,它允许任何信息写入到记忆单元中。同样,当读取门输出1时,它将允许网络其他部分从记忆单元中读取数据。

如前所述,传统 RNN 计算梯度下降的问题在于,误差梯度在展开的网络中传播时迅速消失。加入 LSTM 单元后,从输出反向传播的误差值被收集到 LSTM 单元的记忆单元中。这一现象也被称为误差旋转木马。我们将通过以下示例来描述 LSTM 如何克服 RNN 的梯度消失问题:

长短时记忆

图 4.7:该图显示了一个展开的长短期记忆单元,展示了如何通过三个门的帮助保护记忆单元的内容。

图 4.7 显示了一个通过时间展开的长短期记忆单元。我们将从初始化遗忘门的值为1和写入门的值为1开始。如前图所示,这将把信息K写入记忆单元。写入后,通过将遗忘门的值设置为0,该值将被保留在记忆单元中。然后我们将读取门的值设置为1,从记忆单元中读取并输出值K。从将K加载到记忆单元开始,到从记忆单元读取该值时,都会遵循时间上的反向传播过程。

从读取点接收到的误差导数通过网络进行反向传播,直到写入点,并且在过程中进行一些微小的变化。这是因为记忆神经元的线性特性。因此,通过这种操作,我们可以在不陷入梯度消失问题的情况下,保持数百步的误差导数。

因此,长期短期记忆(LSTM)之所以优于标准 RNN,有很多原因。LSTM 在未分割的连接手写识别中达到了目前已知的最佳结果[117];同时,它也成功地应用于自动语音识别。到目前为止,主要的科技公司,如苹果、微软、谷歌、百度等,已经开始广泛使用 LSTM 网络作为其最新产品的主要组成部分[118]。

双向 RNN

本章将讨论 RNN 的主要局限性,以及如何通过双向 RNN(一种特殊类型的 RNN)克服这些不足。与传统的 RNN 不同,双向神经网络除了从过去获取输入,还能够从未来的上下文中获取信息以进行所需的预测。

RNN 的不足之处

标准或单向 RNN 的计算能力受到限制,因为当前状态无法获得其未来的输入信息。在许多情况下,后续的未来输入信息对于序列预测变得极为重要。例如,在语音识别中,由于语言依赖性,作为音素的语音的适当解释可能依赖于接下来几句话中的词汇。手写识别中也可能出现类似的情况。

在一些修改版本的 RNN 中,这一特性通过在输出中插入一定数量的时间步(N)的延迟部分实现。这个延迟有助于捕捉未来的信息,以预测数据。虽然理论上,为了捕捉大部分可用的未来信息,N 的值可以设定得非常大,但在实际应用中,模型的预测能力实际上会随着 N 值的增大而减弱。文献 [113] 对这一推论给出了一些逻辑解释。随着 N 值的增大,RNN 的大部分计算能力仅仅集中在记忆输入信息上,以便从RNN 的不足之处图 4.8 中)预测结果,y[tc]。 (t[c] 在图中表示当前的时间步)。因此,模型会减少处理来自不同输入向量的预测知识的能力。以下 图 4.8 显示了不同类型的 RNN 所需的输入信息量:

RNN 的不足之处

图 4.8:该图展示了不同类型 RNN 使用的输入信息的可视化。[113]

克服不足的解决方案

为了克服上一节中解释的单向 RNN 的局限性,双向递归网络BRNN)于 1997 年发明[113]。

双向 RNN 的基本思想是将常规 RNN 的隐藏状态分为两部分。一部分负责前向状态(正时间方向),另一部分负责后向状态(负时间方向)。从前向状态生成的输出与后向状态的输入不连接,反之亦然。一个简单的双向 RNN 版本,展开为三个时间步骤,如图 4.9所示。

通过这种结构,由于考虑了两个时间方向,当前评估的时间框架可以轻松使用来自过去和未来的输入信息。因此,当前输出的目标函数最终会最小化,因为我们不需要再增加延迟来包含未来的信息。对于常规的 RNN 来说,这在上一节中已经提到,是必须的。

解决方法

图 4.9:该图展示了一个常规双向神经网络在三个时间步骤中的展开结构。

到目前为止,双向 RNN 在语音识别[114]、手写识别、生物信息学[115]等应用中被发现极为有用。

分布式深度 RNN

现在,既然你已经理解了 RNN 的应用、特点和结构,我们可以继续讨论如何将该网络作为分布式架构来使用。分布式 RNN 并不是一项容易的任务,因此,过去只有少数研究者在这方面进行了研究。尽管数据并行的基本概念对于所有网络都是相似的,但将 RNN 分布到多个服务器上需要一些头脑风暴,并且工作也相对繁琐。

最近,谷歌的一项研究[119]尝试在语音识别任务中将递归网络分布到多个服务器上。在这一节中,我们将讨论谷歌在分布式 RNN 方面的工作,并借助 Hadoop 来实现。

异步随机梯度下降 (ASGD) 可以用于大规模训练循环神经网络(RNN)。ASGD 在深度神经网络的序列判别训练中表现出了特别的成功。

一个两层深度的长短期记忆(LSTM)RNN 用于构建长短期记忆网络。每个 LSTM 包含 800 个记忆单元。该论文使用了 1300 万个 LSTM 网络参数。对于单元的输入和输出,使用的是 tan h(双曲正切激活函数),对于写入、读取和遗忘门,使用的是逻辑 sigmoid 函数。

为了进行训练,输入的语音训练数据可以被拆分并随机打乱,分布到 Hadoop 框架的多个 DataNode 上。长短期记忆(LSTM)被部署到所有这些 DataNode 上,并在这些数据集上进行并行分布式训练。异步随机梯度下降用于此分布式训练。使用一个参数服务器,专门用于维护所有模型参数的当前状态。

为了在 Hadoop 上实现这个过程,每个 DataNode 必须对分区数据执行异步随机梯度下降操作。每个工作节点在每个 DataNode 的块上运行,逐个处理分区中的数据。对于每个语音片段,模型参数P会从前面提到的参数服务器中获取。工作节点计算每一帧的当前状态;解码语音片段以计算最终的外部梯度。更新后的参数然后被发送回参数服务器。工作节点随后会反复请求参数服务器提供最新的参数。然后,执行时间反向传播来计算下一组帧的更新参数梯度,并再次将其发送回参数服务器。

使用 Deeplearning4j 的 RNN

训练一个 RNN 并非易事,有时它可能会非常消耗计算资源。当训练数据涉及多个时间步长的长序列时,训练过程有时会变得异常困难。到目前为止,你已经对为何以及如何通过时间反向传播(backpropagation through time)来训练 RNN 有了更好的理论理解。在本节中,我们将考虑 RNN 应用的一个实际示例,并使用 Deeplearning4j 实现它。

现在,我们通过一个示例来说明如何使用 RNN 进行电影评论数据集的情感分析。该网络的主要问题是,将一段电影评论的原始文本作为输入,并根据内容将该评论分类为正面或负面。原始评论文本中的每个单词都使用 Word2Vec 模型转换为向量,然后输入到 RNN 中。该示例使用了一个大规模的原始电影评论数据集,数据来自ai.stanford.edu/~amaas/data/sentiment/

使用 DL4J 实现该模型的完整过程可以分为以下几个步骤:

  1. 下载并提取原始电影评论数据。

  2. 配置训练所需的网络配置,并评估性能。

  3. 加载每个评论并使用 Word2Vec 模型将单词转换为向量。

  4. 为多个预定义的训练周期(epoch)执行训练。对于每个周期,都会在测试集上评估性能。

  5. 为了下载并提取电影评论数据,我们首先需要设置下载配置。以下代码片段设置了完成此操作所需的一切:

            public static final String DATA_URL = 
            "http://ai.stanford.edu/~amaas/data/sentiment/*"; 
    
    
  6. 在本地文件路径中设置训练和测试数据保存与提取的位置如下所示:

            public static final String DATA_PATH = FilenameUtils.concat
            (System.getProperty("java.io.tmpdir"),local_file_path); 
    
    
  7. Google News 向量在本地文件系统中的位置如下所示:

            public static final String WORD_VECTORS_PATH =    
            "/PATH_TO_YOUR_VECTORS/GoogleNews-vectors-negative300.bin"; 
    
    
  8. 以下代码有助于将数据从 Web URL 下载到本地文件路径:

            if( !archiveFile.exists() )
            { 
             System.out.println("Starting data download (80MB)..."); 
             FileUtils.copyURLToFile(new URL(DATA_URL), archiveFile); 
             System.out.println("Data (.tar.gz file) downloaded to " +  
             archiveFile.getAbsolutePath()); 
    
             extractTarGz(archizePath, DATA_PATH); 
            }
            else 
            {       
             System.out.println("Data (.tar.gz file) already exists at " +  
             archiveFile.getAbsolutePath()); 
             if( !extractedFile.exists())
               { 
                extractTarGz(archizePath, DATA_PATH); 
               } 
             else 
               { 
                System.out.println("Data (extracted) already exists at " +   
                extractedFile.getAbsolutePath()); 
               } 
             } 
             } 
    
    
  9. 现在,随着我们下载了原始电影评论数据,我们可以开始设置我们的 RNN 进行数据训练。下载的数据被分割成多个例子,每个迷你批次会在 Hadoop 的每个工作节点上进行分配,以便进行分布式训练。为此,我们需要声明一个变量batchSize。这里,作为示例,我们使用每个批次包含 50 个例子,这些例子将被分配到多个 Hadoop 块中,工作节点将并行运行:

          int batchSize = 50;      
          int vectorSize = 300; 
          int nEpochs = 5;  
          int truncateReviewsToLength = 300; 
     MultiLayerConfiguration conf = new             
          NeuralNetConfiguration.Builder()
     .optimizationAlgo(OptimizationAlgorithm.STOCHASTIC_GRADIENT_
             DESCENT)
     .iterations(1)
            .updater(Updater.RMSPROP) 
            .regularization(true).l2(1e-5) 
            .weightInit(WeightInit.XAVIER) 
            .gradientNormalization(GradientNormalization
            .ClipElementWiseAbsoluteValue).gradientNormalizationThreshold
            (1.0) 
            .learningRate(0.0018) 
            .list() 
            .layer(0, new GravesLSTM.Builder()
                  .nIn(vectorSize)
                  .nOut(200) 
                  .activation("softsign")
                  .build()) 
            .layer(1, new RnnOutputLayer.Builder()
                  .activation("softmax") 
                  .lossFunction(LossFunctions.LossFunction.MCXENT)
                  .nIn(200)
                  .nOut(2)
                  .build()) 
            .pretrain(false)
            .backprop(true)
            .build(); 
    
          MultiLayerNetwork net = new MultiLayerNetwork(conf); 
          net.init(); 
          net.setListeners(new ScoreIterationListener(1)); 
    
    
  10. 在我们为 RNN 设置网络配置后,接下来我们可以继续进行训练操作,如下所示:

          DataSetIterator train = new AsyncDataSetIterator(new    
          SentimentExampleIterator(DATA_PATH,wordVectors,
          batchSize,truncateReviewsToLength,true),1);
          DataSetIterator test = new AsyncDataSetIterator(new          
          SentimentExampleIterator(DATA_PATH,wordVectors,100,
          truncateReviewsToLength,false),1); 
          for( int i=0; i<nEpochs; i++ )
          { 
            net.fit(train); 
            train.reset(); 
            System.out.println("Epoch " + i + " complete. Starting    
            evaluation:"); 
    
    

    网络的测试通过创建Evaluation类的对象来执行,如下所示:

            Evaluation evaluation = new Evaluation(); 
            while(test.hasNext())
            { 
              DataSet t = test.next(); 
              INDArray features = t.getFeatureMatrix(); 
              INDArray lables = t.getLabels(); 
              INDArray inMask = t.getFeaturesMaskArray(); 
              INDArray outMask = t.getLabelsMaskArray(); 
              INDArray predicted =  
              net.output(features,false,inMask,outMask); 
              evaluation.evalTimeSeries(lables,predicted,outMask); 
            } 
          test.reset(); 
    
          System.out.println(evaluation.stats()); 
          } 
    
    

概述

与其他传统的深度神经网络相比,RNN 具有特殊性,因为它们能够处理长时间序列的向量,并输出不同的向量序列。RNN 会随着时间展开,像前馈神经网络一样工作。RNN 的训练是通过时间反向传播(Backpropagation Through Time,简称 BPTT)来进行的,这是一种传统反向传播算法的扩展。RNN 的一个特殊单元,称为长短期记忆(Long Short-Term Memory,LSTM),有助于克服时间反向传播算法的局限性。

我们还讨论了双向 RNN,它是单向 RNN 的升级版本。单向 RNN 有时由于缺乏未来输入信息,无法正确预测。后来,我们讨论了深度 RNN 的分布式处理及其在 Deeplearning4j 中的实现。可以使用异步随机梯度下降(Asynchronous stochastic gradient descent)来训练分布式 RNN。在下一章中,我们将讨论另一种深度神经网络模型,称为限制玻尔兹曼机(Restricted Boltzmann machine)。

第五章 限制玻尔兹曼机

"我无法创造的东西,我就无法理解。"
--理查德·费曼

到目前为止,我们在本书中仅讨论了判别模型。这些模型在深度学习中的应用是建模未观察到的变量 y 与已观察到的变量 x 之间的依赖关系。数学上,它被公式化为 P(y|x)。在本章中,我们将讨论用于深度学习的深度生成模型。

生成模型是指在给定一些隐藏参数时,可以随机生成一些可观测数据值的模型。该模型基于标签序列和观测的联合概率分布。

生成模型在机器学习和深度学习中的应用,可以作为生成条件概率密度函数的中间步骤,或直接从概率密度函数中建模观测值。

限制玻尔兹曼机RBM)是本章讨论的一个流行生成模型。RBM 基本上是概率图模型,也可以解释为随机神经网络。

注意

随机神经网络可以定义为一种通过向网络中引入随机变化生成的人工神经网络。随机变化可以通过多种方式提供,例如通过提供随机权重或通过为网络的神经元提供随机传输函数。

在本章中,我们将讨论一种特殊类型的玻尔兹曼机,称为 RBM,这是本章的主要内容。我们将讨论基于能量的模型EBM)如何与 RBM 相关,以及它们的功能。随后,我们将介绍深度置信网络DBN),它是 RBM 的扩展。接下来,本章将讨论这些模型在分布式环境中的大规模实现。最后,本章将通过使用 Deeplearning4j 来给出 RBM 和 DBN 的示例。

本章的组织结构如下:

  • 基于能量的模型

  • 玻尔兹曼机

  • 限制玻尔兹曼机

  • 卷积限制玻尔兹曼机

  • 深度置信网络

  • 分布式深度置信网络

  • 使用 Deeplearning4j 实现 RBM 和 DBN

基于能量的模型

深度学习和统计建模的主要目标是编码变量之间的依赖关系。通过了解这些依赖关系,利用已知变量的值,模型可以回答关于未知变量的问题。

基于能量的模型EBMs)[120]通过识别标量能量来收集变量的依赖关系,能量通常是每个变量配置的兼容性度量。在 EBM 中,通过设置观察到的变量的值并找到未观察到的变量的值,从而最小化总体能量,进而做出预测。EBM 中的学习包括制定一个能量函数,该函数将低能量分配给未观察到的变量的正确值,将较高能量分配给不正确的值。基于能量的学习可以视为分类、决策或预测任务中对概率估计的替代方法。

为了清楚地说明 EBM 是如何工作的,让我们看一个简单的例子。

图 5.1所示,我们考虑两个变量集合,观察到的和未观察到的,分别为XY。图中的变量X表示图像的像素集合。变量Y是离散的,包含了对象分类所需的可能类别。在这种情况下,变量Y包含六个可能的值,即:飞机、动物、人类、汽车、卡车和以上都不是。该模型作为一个能量函数,用来衡量XY之间映射的正确性。

该模型采用一种约定,小的能量值表示变量的高度相关配置。另一方面,随着能量值的增加,变量的不兼容性也同样增加。与XY变量都相关的函数称为能量函数,表示如下:

基于能量的模型

在能量模型的情况下,输入X来自周围环境,模型生成一个输出 Y,更可能是关于观察到的变量X的答案。该模型需要生成一个值Y^/,该值从集合Y中选择,目的是使能量函数E(Y, X)的值最小。数学上,这可以表示为:

基于能量的模型

以下图 5.1展示了前一节中提到的整体示例的框图:

基于能量的模型

图 5.1:图中展示了一个能量模型,它计算了观察到的变量 X 和未观察到的变量 Y 之间的兼容性。图中的 X 是一组像素,而 Y 是用于对 X 进行分类的级别集合。模型发现,选择“动物”使得能量函数的值最小。图片来自[121]。

深度学习中的 EBM 与概率相关。概率与e的负能量幂成正比:

基于能量的模型

EBM 通过构造函数 E(x) 间接定义概率。指数函数确保概率始终大于零。这也意味着,在基于能量的模型中,可以自由选择能量函数,依据观察和未观察变量。尽管在能量模型中的分类概率可以任意接近零,但永远不会真正达到零。

上述方程形式的分布是一种 Boltzmann 分布。因此,EBM 通常被称为 Boltzmann 机。我们将在本章的后续部分解释 Boltzmann 机及其各种形式。

Boltzmann 机

Boltzmann 机 [122] 是一种对称连接的神经元单元网络,用于对给定数据集进行随机决策。最初,它们被引入用于学习二进制向量的概率分布。Boltzmann 机具有简单的学习算法,这帮助它们推断并得出关于包含二进制向量的输入数据集的有趣结论。在具有多个特征检测层的网络中,学习算法变得非常缓慢;然而,每次只有一层特征检测器时,学习速度可以更快。

为了解决学习问题,Boltzmann 机由一组二进制数据向量组成,并更新相应连接上的权重,使得数据向量成为优化问题的良好解。为了求解学习问题,Boltzmann 机对这些权重进行许多小的更新。

Boltzmann 机在一个 d 维二进制向量上可以定义为 x ˆBoltzmann 机 {0, 1} ^d。如前一节所述,Boltzmann 机是一种能量基础的函数,其联合概率函数可以通过以下给定的能量函数定义:

Boltzmann 机

这里,E(x) 是能量函数,Z 称为分配函数,它确认 Σ[x ]P(x) = 1。Boltzmann 机的能量函数如下所示:

Boltzmann 机

这里,W 是模型参数的权重矩阵,b 是偏置参数的向量。

像 EBM 这样的 Boltzmann 机工作在观察变量和未观察变量上。当观察变量的数量不高时,Boltzmann 机工作效率更高。在这些情况下,未观察或隐藏变量像多层感知器的隐藏单元一样,展现出可见单元之间的高阶交互。

Boltzmann 机在隐藏层之间以及可见单元之间具有层间连接。图 5.2 展示了 Boltzmann 机的示意图:

Boltzmann 机

图 5.2:图展示了一个简单 Boltzmann 机器的图形表示。图中的无向边表示节点之间的依赖关系,w[i,j] 表示节点 i 和 j 之间的权重。图中显示了 3 个隐藏节点和 4 个可见节点。

Boltzmann 机器的一个有趣特性是,随着隐藏单元的增加,学习规则不会发生变化。这最终有助于学习二进制特征,从而捕捉输入数据中的高阶结构。

Boltzmann 机行为作为离散变量上概率质量函数的通用逼近器。

注意

在统计学习中,最大似然估计MLE)是一种通过找到一个或多个参数的值来最大化观测数据的似然性,从而确定统计模型参数的过程。

Boltzmann 机器如何学习

Boltzmann 机器的学习算法通常基于最大似然估计方法。当 Boltzmann 机器使用基于最大似然估计的学习规则进行训练时,连接模型中两个单元的特定权重的更新将仅取决于这两个单元。网络中的其他单元参与修改生成的统计数据。因此,权重可以在不让网络其余部分知晓的情况下更新。换句话说,网络的其他部分只能知道最终的统计数据,但不知道这些统计数据是如何计算的。

缺陷

在 Boltzmann 机器中,具有许多隐藏层时,网络变得非常庞大,这使得模型通常变得较慢。Boltzmann 机器在大规模数据的学习中会停止学习,因为机器的大小也会同时呈指数增长。对于一个大网络,权重通常非常大,并且平衡分布也会变得非常高。不幸的是,这为 Boltzmann 机器带来了一个显著问题,最终导致达到分布平衡状态的时间更长。

这个限制可以通过限制两层之间的连接来克服,从而通过一次学习一个潜在层来简化学习算法。

限制性 Boltzmann 机器

限制性 Boltzmann 机器RBM)是深度学习中使用的深度概率模型构建块的经典例子。RBM 本身并不是一个深度模型,但可以作为构建其他深度模型的构建块。实际上,RBM 是一种无向概率图模型,由一层观察变量和一层隐藏变量组成,可以用于学习输入的表示。在本节中,我们将解释如何使用 RBM 构建许多更深层次的模型。

让我们通过两个例子来查看 RBM 的使用案例。RBM 主要在因子分析的二进制版本上运行。假设我们有一家餐厅,并且希望请顾客根据 0 到 5 的评分尺度来评价食物。在传统方法中,我们将试图根据变量的隐藏因素来解释每个食物项和顾客。例如,像意大利面和千层面这样的食物会与意大利因素有很强的关联。另一方面,RBM 采用不同的方法。它不是让每个顾客根据连续的评分尺度来评价食物项,而是简单地询问他们是否喜欢该食物,然后 RBM 将尝试推断出各种潜在因素,这些因素有助于解释每个顾客的食物选择激活。

另一个例子是根据某人喜欢的电影类型来猜测他可能选择的电影。假设 X 先生提供了他对给定的五部电影的二进制偏好。RBM 的任务是根据隐藏单元激活他的偏好。因此,在这种情况下,这五部电影会向所有隐藏单元发送消息,要求它们更新自身。然后,RBM 会根据之前提供给该人的一些偏好,以较高的概率激活隐藏单元。

基本架构

RBM 是一个浅层的、两层的神经网络,用作构建深度模型的基础模块。RBM 的第一层称为观测层或可见层,第二层称为潜在层或隐藏层。它是一个二分图,不允许在观测层的任何变量之间,或潜在层的任何单元之间有连接。如图 5.3所示,层与层之间没有内部通信。由于这种限制,该模型被称为限制玻尔兹曼机。每个节点用于计算,处理输入,并通过做出随机(随机确定)决策来参与输出,决定是否传递该输入。

注意

二分图是一种图,其中顶点可以分为两个不相交的集合,使得每条边都连接一个集合的顶点到另一个集合的顶点。然而,同一集合的顶点之间没有连接。顶点集合通常被称为图的一个部分。

RBM 两层结构的主要直觉是,有一些可见的随机变量(例如,来自不同顾客的食物评价)和一些潜在的随机变量(如菜系、顾客的国籍或其他内部因素),训练 RBM 的任务是找到这两组变量如何相互连接的概率。

为了数学地构建 RBM 的能量函数,我们用v表示由一组n[v]个二进制变量组成的观测层。隐藏层或潜在层由n[h]个二进制随机变量表示,记作h

类似于玻尔兹曼机,RBM 也是一个基于能量的模型,其中联合概率分布由其能量函数决定:

基本架构基本架构

图 5.3:图示展示了一个简单的 RBM。该模型是一个对称的二分图,其中每个隐藏节点与每个可见节点相连接。隐藏单元表示为 h[i],可见单元表示为 v[i]

一个具有二进制可见单元和潜在单元的 RBM 的能量函数如下:

基本架构

在这里,abW 是不受约束的、可学习的实值参数。从前面的图 5.3中,我们可以看到模型被分为两组变量,vh。单元之间的交互由矩阵W描述。

RBM 如何工作

既然我们现在已经了解了 RBM 的基本架构,在这一节中,我们将讨论该模型的基本工作过程。RBM 从一个数据集中接受输入数据进行学习。模型的每个可见节点从数据集中的一项数据接收一个低级特征。例如,对于灰度图像,最低级的项目将是图像中的一个像素值,模型的可见节点将接收该像素值。因此,如果图像数据集有 n 个像素,处理这些像素的神经网络也必须具有 n 个输入节点在可见层上:

RBM 如何工作

图 5.4:图示展示了一个输入路径的 RBM 计算

现在,让我们将一个单一像素值p在这两层网络中进行传播。在隐藏层的第一个节点,p与权重w相乘,并加上偏置。最终结果通过激活函数生成节点的输出。该操作产生了结果,可以称之为通过该节点传递的信号强度,给定输入像素p图 5.4展示了单个输入 RBM 所涉及的计算的可视化表示。

RBM 如何工作

RBM 的每个可见节点都与一个独立的权重相关联。来自不同单元的输入在一个隐藏节点中合并。每个来自输入的p(像素)都会与其相关的独立权重相乘。所有的乘积会被加总并加入偏置。然后,该结果通过激活函数生成节点的输出。以下图 5.5展示了多个输入到可见层 RBM 时所涉及的计算的可视化表示:

RBM 如何工作

图 5.5:图示展示了具有多个输入和一个隐藏单元的 RBM 计算

上述图 5.5展示了与每个可见节点相关的权重是如何用于计算来自隐藏节点的最终结果的。

RBM 如何工作

图 5.6:该图展示了在 RBM 中多个可见单元和隐藏单元的计算过程

如前所述,RBM 类似于一个二分图。进一步来说,机器的结构基本上类似于一个对称的二分图,因为所有从可见节点接收到的输入都被传递给 RBM 的所有潜在节点。

对于每个隐藏节点,每个输入 p 都会与其相应的权重w相乘。因此,对于单个输入pm个隐藏单元,该输入将有m个与之相关的权重。在图 5.6中,输入p将有三个权重,总共形成 12 个权重:四个来自可见层的输入节点和三个来自下一层的隐藏节点。两层之间所有相关的权重形成一个矩阵,其中行数等于可见节点的数量,列数等于隐藏单元的数量。在前面的图中,第二层的每个隐藏节点都接受四个输入,并与它们各自的权重相乘。所有乘积的总和再加上一个偏置。这一结果会通过一个激活算法,以生成每个隐藏层的一个输出。图 5.6表示了在这种情况下发生的整体计算过程。

通过堆叠 RBM,它将形成一个更深的神经网络,其中第一个隐藏层的输出将作为输入传递给下一个隐藏层。这将通过你使用的隐藏层的数量传播,直到到达所需的分类层。在接下来的部分,我们将解释如何将 RBM 用作深度神经网络。

卷积限制玻尔兹曼机

高维度输入,如图像或视频,会给传统的机器学习模型带来巨大的内存、计算和操作压力。在第三章中,我们展示了如何通过用小核的离散卷积运算替代矩阵乘法来解决这些问题。接下来,Desjardins 和 Bengio [123] 也表明,当这种方法应用于 RBM 时,它同样能有效工作。在本节中,我们将讨论该模型的功能。

卷积限制玻尔兹曼机

图 5.7:该图展示了 RBM 的观察变量或可见单元如何与图像的小批量相关联,以计算最终结果。权重连接表示一组滤波器

进一步来说,在普通的 RBM 中,可见单元通过不同的参数和权重与所有隐藏变量直接相关。为了描述图像的空间局部特征,理想情况下需要较少的参数,这有助于更好的泛化。这有助于从高维图像中检测和提取相同的局部特征。因此,使用 RBM 从图像中提取所有全局特征进行物体检测并不太理想,尤其是对于高维图像。一个简单的方法是使用从输入图像中抽样的小批量,在 Hadoop 的 Datanodes 上分块训练 RBM,以生成局部特征。这个方法的表示称为基于补丁的 RBM,如图 5.7所示。然而,这种方法有一些潜在的局限性。在 Hadoop 的分布式环境中使用的基于补丁的 RBM 没有遵循小批量的空间关系,而是将每个图像的小批量视为来自附近补丁的独立补丁。这使得从相邻补丁中提取的特征是独立的,并且可能存在显著的冗余。

为了处理这种情况,使用了卷积限制玻尔兹曼机CRBM),它是传统 RBM 模型的扩展。CRBM 在结构上与 RBM 几乎相似,是一个两层模型,其中可见和隐藏随机变量被结构化为矩阵。因此,在 CRBM 中,可以为可见单元和隐藏单元定义局部性和邻域。在 CRBM 中,可见矩阵表示图像,矩阵的小窗口定义了图像的小批量。CRBM 的隐藏单元被划分为不同的特征图,以定位可见单元多个位置上多个特征的存在。特征图中的单元表示在可见单元的不同位置上相同的特征。CRBM 的隐藏-可见连接是完全局部的,权重通常会分配到隐藏单元的簇中。

CRBM 的隐藏单元用于从重叠的小批量可见单元中提取特征。此外,相邻小批量的特征互为补充,并协同工作以建模输入图像。

图 5.8 显示了一个 CRBM(卷积限制玻尔兹曼机),其中包含一个可见单元矩阵V和一个隐藏单元矩阵H,它们通过K 33的滤波器连接,即W[1]W[2]W[3],...W[K]。图中的隐藏单元被分成K个子矩阵,称为特征图,H[1]H[2],...H[K]。每个隐藏单元H[i]表示在可见单元的33邻域中某个特征的存在。

与基于补丁的 RBM 不同,CRBM 是在整个输入图像或图像的大区域上进行训练,以学习局部特征,并利用重叠的迷你批次的空间关系,这些迷你批次通过 Hadoop 以分布式方式处理。在 CRBM 中,重叠迷你批次的隐藏单元相互依赖并合作。因此,一旦解释了一个隐藏单元,就不需要在邻近的重叠迷你批次中再次解释它。这反过来有助于减少特征的冗余。

卷积限制玻尔兹曼机

图 5.8:图中展示了 CRBM 中的计算过程。

堆叠卷积限制玻尔兹曼机

CRBM 可以堆叠在一起形成深度神经网络。堆叠卷积限制玻尔兹曼机Stacked CRBM)可以层层训练,采用自下而上的方法,类似于全连接神经网络的层级训练。在每个 CRBM 滤波层后,在堆叠网络中实现了一种确定性的子采样方法。对于特征的子采样,采用最大池化在非重叠的图像区域中进行。正如在第三章中解释的,卷积神经网络有助于最小化特征的维度。除此之外,它使得特征对小的平移具有鲁棒性,并有助于将更高层次的特征传播到输入图像的区域中。

深度 CRBM 需要一个池化操作,以便每一层的空间大小逐渐减小。尽管大多数传统的卷积模型可以很好地处理各种空间大小的输入,但对于玻尔兹曼机来说,由于几个原因,改变输入大小变得有些困难。首先,能量函数的分区函数会随输入大小的变化而变化。其次,卷积网络通过增加池化函数的大小,使其与输入大小成比例,从而实现尺寸不变性。然而,对于玻尔兹曼机来说,要实现池化区域的缩放是非常困难的。

对于 CRBM,位于图像边界的像素也会带来困难,尤其是考虑到玻尔兹曼机本身具有对称性。这个问题可以通过隐式零填充输入来解决。请注意,零填充输入通常是由于输入像素较少,这些像素可能在需要时没有被激活。

深度信念网络

深度信念网络DBN)是 2006 至 2007 年间最受欢迎的非卷积模型之一,可以成功地作为深度神经网络进行部署[124][125]。深度学习的复兴很可能始于 2006 年 DBN 的发明。在 DBN 出现之前,优化深度模型是非常困难的。通过超越支持向量机SVM),DBN 证明了深度模型的成功可能性;尽管与其他生成性或无监督学习算法相比,DBN 的流行度有所下降,且如今很少使用,但它们仍在深度学习历史中扮演着非常重要的角色。

注意

只有一个隐藏层的 DBN 实际上就是一个 RBM。

DBN 是生成性模型,由多个隐藏变量层组成。隐藏变量通常是二进制的;然而,可见单元可能包含二进制或实数值。在 DBN 中,每一层的每个单元都与其相邻层的每个单元连接,尽管也可以有稀疏连接的 DBN。各中间层之间没有连接。如图 5.9所示,DBN 基本上是一个由若干 RBM 构成的多层网络。顶层两个层之间的连接是无向的。而其他所有层之间的连接都是有向的,箭头指向数据最接近的层。

除了堆叠中的第一层和最后一层外,DBN 的每一层都有两个作用。首先,它作为前一层的隐藏层,同时作为下一层的可见层或输入层。DBN 主要用于聚类、识别和生成视频序列和图像。

深度信念网络

图 5.9:图中展示了由三个 RBM 组成的 DBN

贪婪逐层训练

2006 年提出了一种贪婪逐层训练算法用于训练深度信念网络(DBN)[126]。该算法一次训练一个层。采用这种方法时,首先训练一个受限玻尔兹曼机(RBM),它以实际数据为输入并对其进行建模。

一个单层的 DBN 就是一个 RBM。贪婪逐层方法的核心理念是,在训练一个 m 层 DBN 的顶层 RBM 后,解释参数的方式会随着添加(m+1)层 DBN 而发生变化。在 RBM 中,层(m-1)和m之间,层m的概率分布是基于该 RBM 的参数来定义的。然而,在 DBN 的情况下,层m的概率分布是基于上层的参数来定义的。这个过程可以无限重复,直到连接上任意数量的 DBN 层。

分布式深度信念网络

到目前为止,DBN 在许多应用中取得了显著成绩,如语音和电话识别[127]、信息检索[128]、人体运动建模[129]等。然而,RBM 和 DBN 的顺序实现存在各种限制。对于大规模数据集,由于涉及长时间的计算、算法对内存的高需求等,模型在应用中会表现出各种不足。为了处理大数据,RBM 和 DBN 需要分布式计算来提供可扩展、一致和高效的学习。

为了使 DBN 能够适应存储在计算机集群上的大规模数据集,DBN 应该采用带有 Hadoop 和 Map-Reduce 的分布式学习方法。[130]中的论文展示了 RBM 每一层的键值对方法,其中预训练是在 Map-Reduce 框架的分布式环境中通过层级进行的。学习通过在 Hadoop 上执行迭代计算方法进行 RBM 的训练。因此,DBN 的分布式训练通过堆叠多个 RBM 来实现。

限制玻尔兹曼机的分布式训练

如前所述,RBM 的能量函数如下:

限制玻尔兹曼机的分布式训练

设输入数据集 I = {x[i] = i= 1, 2,... N} 用于限制玻尔兹曼机(RBM)的分布式学习。如前所述,针对深度信念网络(DBN)的学习,RBM 每一层的权重和偏置首先通过贪心层级无监督训练进行初始化。分布式训练的目的是学习权重和相关的偏置 bc。对于使用 Map-Reduce 的分布式 RBM,每个训练周期中都需要进行一次 Map-Reduce 作业。

对于矩阵乘法,使用吉布斯采样,RBM 的训练大部分计算时间都消耗在此。因此,为了减少计算时间,吉布斯采样可以在 Map 阶段分布到多个数据集上,这些数据集在 Hadoop 框架上运行不同的 Datanode。

注意

吉布斯采样是一种马尔可夫链蒙特卡洛MCMC)算法,用于确定从指定的多元概率分布中估计的观察序列,当传统的直接采样变得困难时。

最初,训练所需的不同参数,如可见层和隐藏层的神经元数量、输入层偏置 a、隐藏层偏置 b、权重 W、周期数(假设为 N)、学习率等,都被初始化。周期数表示映射和归约阶段将迭代 N 次。对于每个周期,映射器会针对每个数据节点块运行,并执行 Gibbs 采样来计算 Wab 的近似梯度。然后,归约器使用计算出的增量更新这些参数,以便为下一个周期做好准备。因此,从第二个周期开始,映射阶段的输入值,即 Wab 的更新值,是从前一个周期的归约器输出中计算得出的。

输入数据集 I 被拆分成多个块,并存储在不同的块中,分别在每个数据节点上运行。每个在这些块上运行的映射器将计算该块中存储的特定数据块的权重和偏置的近似梯度。然后,归约器计算各自参数的增量,并相应地更新它们。这个过程将得到的参数和更新后的值视为该周期 Map-Reduce 阶段的最终结果。每个周期结束后,归约器决定是否存储学习到的权重,如果是最后一个周期,或者是否增加周期索引并将键值对传播给下一个映射器。

深度置信网络的分布式训练

对于具有 L 个隐藏层的 DBN 的分布式训练,学习是通过预训练 L 个 RBM 完成的。底层 RBM 的训练如前所述,但对于其余的 (L-1) 个 RBM,每一层的输入数据集都会发生变化。

对于 m[th] 层的输入数据(L 深度置信网络的分布式训练 ¥ m > 1)RBM,将会是 (m-1)[th] 层 RBM 隐藏节点的条件概率:

深度置信网络的分布式训练

分布式反向传播算法

这是分布式反向传播算法的第二阶段,用于调优全局网络。在此过程中,计算权重的梯度时,前向传播和反向传播方法占据了大部分的计算时间。因此,为了加快每个周期的执行速度,该过程应在每个输入数据集的小批量上并行运行。

在该过程的第一步中,将 L 层 DBN 的学习权重,即 W1W2,...WL 加载到内存中,并初始化其他超参数。在此微调阶段,映射和归约阶段的主要任务与 RBM 的分布式训练类似。映射器将确定权重的梯度,并最终更新权重增量。归约器从一个或多个权重中更新权重增量,并将输出传递给映射器以执行下一次迭代。

此过程的主要目的是通过将标签层放置在全局网络之上,并迭代调整整个层的权重,从而获得模型的一些判别能力。

RBM 和 DBN 的性能评估

论文[130]在 Hadoop 集群上进行了分布式 RBM 和 DBN 的实验,以便与传统的顺序方法进行比较研究。实验在 MNIST 数据集上进行,用于手写数字识别。训练集包含 60,000 张图片,测试集包含 10,000 张图片。HDFS 的块大小设置为 64MB,副本因子为 4。所有节点设置为最多运行 26 个映射器和 4 个减少器。有兴趣的读者可以修改块大小和副本因子,查看这些参数下实验的最终结果。

训练时间的显著提升

本实验的目的是比较分布式 RBM 和 DBN 与传统训练策略(顺序训练)在训练时间上的差异。顺序程序在一颗 CPU 上执行,而分布式程序则在一个节点的 16 颗 CPU 上执行。两次实验均在前面提到的 MNIST 数据集上进行。所得结果汇总在表 5.1表 5.2中:

训练时间的显著提升

表 5.1:该表格表示完成分布式和顺序 RBM 训练所需的时间

训练时间的显著提升

表 5.2:该表格表示完成分布式和顺序 DBN 训练所需的时间

表中显示的数据清楚地描述了使用 Hadoop 分布式 RBM 和 DBN 相较于传统顺序方法的优势。分布式方法在模型训练时间上显示出了显著的提升。此外,使用 Hadoop 框架进行分布的一个关键优势是,它能随着训练数据集的大小和用于分布的机器数量的增加而表现出卓越的扩展性。

本章的下一部分将演示使用 Deeplearning4j 的两种模型的编程方法。

使用 Deeplearning4j 的实现

本章的这一部分将提供如何使用 Deeplearning4j 编写 RBM 和 DBN 代码的基本思路。读者将能够学习本章中提到的各种超参数的语法。

为了使用 Deeplearning4j 实现 RBM 和 DBN,整个思路非常简单。总体实现可以分为三个核心阶段:加载数据或数据准备、网络配置、以及模型的训练和评估。

在本节中,我们将首先讨论在 IrisDataSet 上使用 RBM,然后我们将讨论 DBN 的实现。

限制玻尔兹曼机

对于 RBM 的构建和训练,首先我们需要定义并初始化模型所需的超参数:

Nd4j.MAX_SLICES_TO_PRINT = -1; 
Nd4j.MAX_ELEMENTS_PER_SLICE = -1; 
Nd4j.ENFORCE_NUMERICAL_STABILITY = true; 
final int numRows = 4; 
final int numColumns = 1; 
int outputNum = 10; 
int numSamples = 150; 

这里的批次大小可以初始化为 150,意味着一次会将 150 个样本提交到 Hadoop 框架中。请放心,所有其他参数的初始化与我们在之前的章节中所做的一样。

int batchSize = 150; 
int iterations = 100; 
int seed = 123; 
int listenerFreq = iterations/2; 

在下一阶段,Irisdataset 根据定义的 batchsize 和每批次的样本数量被加载到系统中:

log.info("Load data....");
DataSetIterator iter = new IrisDataSetIterator(batchSize, numSamples); 
DataSet iris = iter.next();

在这里,RBM 作为一层使用 NeuralNetConfiguration.Builder() 创建。同样,限制玻尔兹曼机的对象用于存储属性,例如应用于观察层和隐藏层的变换——分别是高斯变换和修正线性变换:

NeuralNetConfiguration conf = new NeuralNetConfiguration.Builder()
.regularization(true)       
 .miniBatch(true) 
.layer(new RBM.Builder().l2(1e-1).l1(1e-3) 
    .nIn(numRows * numColumns)       
    .nOut(outputNum)

ReLU 被用作激活函数:

.activation("relu") 

weightInit() 函数用于初始化权重,它表示用于放大每个节点输入信号的系数的初始值:

.weightInit(WeightInit.RELU) 
 .lossFunction(LossFunctions.LossFunction.RECONSTRUCTION
     _CROSSENTROPY.k(3) 

高斯变换用于可见单元,修正线性变换用于隐藏层。在 Deeplearning4j 中这非常简单。我们需要在 .visibleUnit.hiddenUnit 方法中传递参数 VisibleUnit.GAUSSIANHiddenUnit.RECTIFIED

.hiddenUnit(HiddenUnit.RECTIFIED).visibleUnit(VisibleUnit.GAUSSIAN) 
 .updater(Updater.ADAGRAD).gradientNormalization(Gradient
     Normalization.ClipL2PerLayer) 
    .build())       
  .seed(seed)    
  .iterations(iterations) 

反向传播的步长在此处定义:

.learningRate(1e-3)
  .optimizationAlgo(OptimizationAlgorithm.LBFGS)       
  .build();       

Layer model = LayerFactories.getFactory(conf.getLayer()).create(conf);       
model.setListeners(new ScoreIterationListener(listenerFreq));

log.info("Evaluate weights....");       
INDArray w = model.getParam(DefaultParamInitializer.WEIGHT_KEY);       
log.info("Weights: " + w);

为了扩展数据集,可以通过调用 Dataset 类的对象来执行 scale()

iris.scale(); 

在先前过程中的评估完成后,模型现在已完全准备好进行训练。它可以以类似的方式使用 fit() 方法进行训练,和之前的模型一样,并传递 getFeatureMatrix 作为参数:

log.info("Train model....");       
for(int i = 0; i < 20; i++)
  {       
   log.info("Epoch "+i+":");model.fit(iris.getFeatureMatrix()); }

深度置信网络

如本章所述,DBN 是多个 RBM 的堆叠版本。在这一部分,我们将展示如何使用 Deeplearning4j 编程方式部署 DBN。程序的流程将遵循与其他模型相同的标准过程。使用 Deeplearning4j 实现简单的 DBN 非常简单。该示例将展示如何使用 DBN 训练和遍历输入的 MNIST 数据。

对于 MNIST 数据集,以下行指定了批次大小和示例数量,用户将指定这些参数以便一次性加载数据到 HDFS 中:

log.info("Load data....");
DataSetIterator iter = new MnistDataSetIterator(batchSize,numSamples,
true);

在下一阶段,模型将通过堆叠 10 个 RBM 来构建。以下代码段将指定如何使用 Deeplearning4j 来完成此操作:

log.info("Build model....");
MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder()
  .seed(seed)
  .iterations(iterations)
  .optimizationAlgo(OptimizationAlgorithm.LINE_GRADIENT_DESCENT)
  .list()
  .layer(0, new RBM.Builder().nIn(numRows * numColumns).nOut(1000)
  .lossFunction(LossFunctions.LossFunction.RMSE_XENT).build())
  .layer(1, new RBM.Builder().nIn(1000).nOut(500)
  .lossFunction(LossFunctions.LossFunction.RMSE_XENT).build())
  .layer(2, new RBM.Builder().nIn(500).nOut(250)
  .lossFunction(LossFunctions.LossFunction.RMSE_XENT).build())
  .layer(3, new RBM.Builder().nIn(250).nOut(100)
  .lossFunction(LossFunctions.LossFunction.RMSE_XENT).build())
  .layer(4, new RBM.Builder().nIn(100).nOut(30)
  .lossFunction(LossFunctions.LossFunction.RMSE_XENT).build())  
  .layer(5, new RBM.Builder().nIn(30).nOut(100)
  .lossFunction(LossFunctions.LossFunction.RMSE_XENT).build())  
  .layer(6, new RBM.Builder().nIn(100).nOut(250)
  .lossFunction(LossFunctions.LossFunction.RMSE_XENT).build())
  .layer(7, new RBM.Builder().nIn(250).nOut(500)
  .lossFunction(LossFunctions.LossFunction.RMSE_XENT).build())
  .layer(8, new  RBM.Builder().nIn(500).nOut(1000)
  .lossFunction(LossFunctions.LossFunction.RMSE_XENT).build())
  .layer(9, new OutputLayer.Builder(LossFunctions.LossFunction.
   RMSE_XENT).nIn(1000).nOut(numRows*numColumns).build())
  .pretrain(true)
  .backprop(true)
  .build();

MultiLayerNetwork model = new MultiLayerNetwork(conf);
model.init();

在最后一部分,代码将使用加载的 MNIST 数据集进行训练,通过调用 fit() 方法:

log.info("Train model....");
while(iter.hasNext())
  {
   DataSet next = iter.next();
   model.fit(new DataSet(next.getFeatureMatrix(),next.
   getFeatureMatrix()));
  }

执行代码后,过程将输出以下结果:

Load data.... 
Build model.... 
Train model.... 

o.d.e.u.d.DeepAutoEncoderExample - Train model.... 
o.d.n.m.MultiLayerNetwork - Training on layer 1 with 1000 examples 
o.d.o.l.ScoreIterationListener - Score at iteration 0 is 394.462 
o.d.n.m.MultiLayerNetwork - Training on layer 2 with 1000 examples 
o.d.o.l.ScoreIterationListener - Score at iteration 1 is 506.785 
o.d.n.m.MultiLayerNetwork - Training on layer 3 with 1000 examples 
o.d.o.l.ScoreIterationListener - Score at iteration 2 is 255.582 
o.d.n.m.MultiLayerNetwork - Training on layer 4 with 1000 examples 
o.d.o.l.ScoreIterationListener - Score at iteration 3 is 128.227 

......................................... 

o.d.n.m.MultiLayerNetwork - Finetune phase 
o.d.o.l.ScoreIterationListener - Score at iteration 9 is 132.45428125 

........................... 

o.d.n.m.MultiLayerNetwork - Finetune phase 
o.d.o.l.ScoreIterationListener - Score at iteration 31 is 135.949859375 
o.d.o.l.ScoreIterationListener - Score at iteration 32 is 135.9501875 
o.d.n.m.MultiLayerNetwork - Training on layer 1 with 1000 examples 
o.d.o.l.ScoreIterationListener - Score at iteration 33 is 394.182 
o.d.n.m.MultiLayerNetwork - Training on layer 2 with 1000 examples 
o.d.o.l.ScoreIterationListener - Score at iteration 34 is 508.769 
o.d.n.m.MultiLayerNetwork - Training on layer 3 with 1000 examples 

............................ 

o.d.n.m.MultiLayerNetwork - Finetune phase 
o.d.o.l.ScoreIterationListener - Score at iteration 658 is 142.4304375 
o.d.o.l.ScoreIterationListener - Score at iteration 659 is 142.4311875 

总结

RBM 是一种生成模型,当输入一些潜在或隐藏参数时,它可以随机生成可见数据值。在本章中,我们讨论了 Boltzmann 机的概念和数学模型,它是一种基于能量的模型。接下来,本章讨论并给出了 RBM 的可视化表示。此外,本章还讨论了 CRBM,它是卷积和 RBM 的结合,用于提取高维图像的特征。然后,我们介绍了流行的 DBN,它实际上是 RBM 的堆叠实现。最后,本章讨论了如何在 Hadoop 框架中分布式训练 RBM 和 DBN 的方法。

本章通过提供两种模型的代码示例来结束。本书的下一章将介绍一种名为自动编码器的生成模型及其多种形式,如去噪自动编码器、深度自动编码器等。

第六章 自编码器

“人们担心计算机会变得太聪明,接管世界,但真正的问题是它们太愚蠢,已经接管了世界。”
--Pedro Domingos

在上一章中,我们讨论了一个生成模型——限制玻尔兹曼机。在本章中,我们将介绍另一个生成模型——自编码器。自编码器是一种人工神经网络,通常用于降维、特征学习或特征提取。

在本章接下来的部分,我们将详细讨论自编码器的概念及其各种形式。我们还将解释 正则化自编码器稀疏自编码器 这两个术语。将会介绍稀疏编码的概念,并探讨在稀疏自编码器中选择稀疏因子的标准。随后,我们将讨论深度学习模型——深度自编码器,并展示如何使用 Deeplearning4j 实现它。去噪自编码器是传统自编码器的另一种形式,将在本章的后部分讨论。

总体来说,本章分为几个小节,具体内容如下:

  • 自编码器

  • 稀疏自编码器

  • 深度自编码器

  • 去噪自编码器

  • 自编码器的应用

自编码器

自编码器是一个具有一个隐藏层的神经网络,经过训练来学习一种身份函数,尝试将其输入重建为输出。换句话说,自编码器试图通过将输入数据投影到由隐藏节点定义的低维子空间来复制输入数据。隐藏层 h 描述了一种代码,用于表示输入数据及其结构。因此,这个隐藏层被迫从输入训练数据集中学习结构,以便在输出层能够复制输入。

自编码器的网络可以分为两个部分:编码器和解码器。编码器通过函数 h=f (k) 描述,解码器则通过 r = g (h) 尝试重建或复制。自编码器的基本思想应该是只复制输入中优先的那些方面,而不是创建输入的精确副本。它们的设计方式是限制隐藏层只做大致的复制,而不是从输入数据中复制所有内容。因此,如果自编码器学习到完全设置 g(f(k) = k 对所有 k 的值都成立,则它不能被称为有用的。图 6.1 表示自编码器的通用结构,通过内部隐藏层代码 h 将输入 k 映射到输出 r

自编码器

图 6.1:自编码器的通用框图。这里,输入 k 通过隐藏状态或内部表示 h 映射到输出 r。编码器 f 将输入 k 映射到隐藏状态 h,解码器 g 执行 h 到输出 r 的映射。

再举一个例子,考虑 图 6.2。该图展示了自编码器对输入图像块 k 的实际表示,它学习隐藏层 h 以输出 r。输入层 k 是图像块强度值的组合。隐藏层节点有助于将高维输入层投影到隐藏节点的低维激活值。这些隐藏节点的激活值合并在一起生成输出层 r,它是输入像素的近似。在理想情况下,隐藏层的节点数通常比输入层节点数少。因此,它们被迫以某种方式减少信息,以便仍能生成输出层。

Autoencoder

图 6.2:该图展示了一个实际示例,说明了自编码器如何从输入像素的近似中学习输出结构。

将输入的结构复制到输出可能听起来无效,但实际上,自编码器的最终结果并不完全依赖于解码器的输出。相反,训练自编码器的主要思想是复制输入任务的有用特性,这些特性将在隐藏层中体现。

从自编码器中提取所需特征或信息的常见方法之一是将隐藏层 h 的维度限制为比输入 k 的维度 d 小的维度 d^/,即 d^/ < d。这种结果较小的维度层可以称为输入 k 的损失压缩表示。隐藏层维度小于输入维度的自编码器被称为欠完备

如上所述的学习过程可以通过最小化损失函数 L 来数学表示,公式如下:

Autoencoder

简单来说,L 可以定义为一个损失函数,它惩罚 g (f (k)) 与输入 k 的差异。

使用线性解码器函数,自编码器学习形成与主成分分析PCA)程序相似的空间基础。收敛后,隐藏层将形成由训练数据集的主子空间所生成的空间基础。然而,与 PCA 不同,这些过程不一定会生成正交向量。因此,具有非线性编码器函数 f 和非线性解码器函数 g 的自编码器可以学习 PCA 更强大的非线性泛化。这最终会大大增加编码器和解码器的容量。然而,随着容量的增加,自编码器开始表现出不希望出现的行为。

它接着学习将整个输入复制,而不注意提取所需的信息。从理论上讲,一个自编码器可能是一个一维编码,但实际上,一个非常强大的非线性编码器可以学习表示每个训练示例k(i),并用代码i来表示。然后,解码器将这些整数(i)映射到特定训练示例的值。因此,使用具有更高容量的自编码器完全无法从输入数据集中复制仅有的有用特征。

注意

主成分分析(PCA)是一种统计方法,通过正交变换将一组可能相关的观测变量转化为一组线性相关的变量,称为主成分。在 PCA 方法中,主成分的数量小于或等于原始输入变量的数量。

类似于为欠完备自编码器提到的边缘情况问题,在该问题中,隐藏层的维度小于输入的维度,允许隐藏层或编码器的维度与输入相等的自编码器通常面临相同的问题。

一个隐藏代码维度大于输入维度的自编码器称为完备自编码器。这种类型的自编码器更加容易受到上述问题的影响。即使是线性编码器和解码器也能在不学习任何输入数据集的期望属性的情况下,完成将输入复制到输出的学习。

正则化自编码器

通过为隐藏层选择合适的维度,并根据模型分布的复杂性调整编码器和解码器的容量,任何类型架构的自编码器都可以成功构建。能够提供这种能力的自编码器被称为正则化自编码器。

除了能够将输入复制到输出的功能外,正则化自编码器还有一个损失函数,帮助模型具备其他特性。这些特性包括对缺失输入的鲁棒性、数据表示的稀疏性、表示的导数小等。即使是非线性和过完备的正则化自编码器,也能学习到关于数据分布的某些信息,而不考虑模型的容量。正则化自编码器[131]能够通过重构误差与正则化项之间的有效对立,捕捉训练分布的结构。

稀疏自编码器

分布式稀疏表示是深度学习算法中学习有用特征的关键之一。它不仅是数据表示的一种连贯模式,还帮助捕捉到大多数现实世界数据集的生成过程。在这一部分,我们将解释自编码器如何鼓励数据的稀疏性。我们将从介绍稀疏编码开始。当输入引发神经网络中相对少量的节点激活时,结合这些节点可以以稀疏的方式表示输入,那么这种编码就被称为稀疏编码。在深度学习技术中,类似的约束被用来生成稀疏编码模型,以实现常规自编码器,这些自编码器是通过名为稀疏自编码器的稀疏常数训练出来的。

稀疏编码

稀疏编码是一种无监督学习方法,用于学习过完备的基集合,以便以连贯高效的方式表示数据。稀疏编码的主要目标是确定一组向量(n) v[i],使得输入向量k可以表示为这些向量的线性组合。

从数学角度来看,这可以表示为如下形式:

稀疏编码

这里a[i]是与每个向量v[i]相关联的系数[.]

在 PCA 的帮助下,我们可以以一种连贯的方式学习一组完整的基向量;然而,我们希望学习一组过完备的基向量来表示输入向量k 稀疏编码,其中n>m。拥有过完备基的原因在于,基向量通常能够捕捉到输入数据固有的模式和结构。然而,过完备性有时会引发退化问题,即通过其基,系数a[i]无法唯一标识输入向量k。因此,引入了一种额外的标准,称为稀疏性,这在稀疏编码中起到了作用。

简单来说,稀疏性可以定义为具有少量非零组件,或者具有少量不接近零的组件。如果对于给定的输入向量,非零系数的数量或远离零的系数数量较少,则称系数集合a[i]为稀疏。

有了对稀疏编码的基本理解后,我们可以进入下一部分,探讨稀疏编码概念如何用于自编码器,从而生成稀疏自编码器。

稀疏自编码器

当输入数据集保持一定结构,并且输入特征之间存在相关性时,即便是一个简单的自编码器算法也能够发现这些相关性。此外,在这种情况下,一个简单的自编码器最终会学习到一个低维表示,类似于主成分分析(PCA)。

这种看法基于隐藏层数量相对较少的事实。然而,通过对网络施加其他约束条件,即便是大量隐藏层的网络,依然能够从输入向量中发现所需的特征。

稀疏自编码器通常用于学习特征,以执行其他任务,如分类。对于添加了稀疏性约束的自编码器,它必须响应其训练数据集的独特统计特征,而不仅仅是充当一个恒等函数。

稀疏自编码器

图 6.3:图示展示了一个典型的稀疏自编码器示例

稀疏自编码器是一种具有稀疏性约束的自编码器,它有助于引导单层网络学习隐藏层代码。该方法通过限制重构所需的代码词数来最小化重构误差。这种稀疏化算法可以视为一个分类问题,它将输入限制为单一类别值,从而帮助减少预测误差。

在本部分中,我们将通过一个简单的架构来解释稀疏自编码器。图 6.3展示了稀疏自编码器的最简单形式,包含一个隐藏层h。隐藏层h通过权重矩阵W与输入向量K相连,形成编码步骤。在解码步骤中,隐藏层h通过绑定权重矩阵W^T输出到重构向量K`。在网络中,激活函数用f表示,偏置项用b*表示。激活函数可以是任何类型:线性、Sigmoid 或 ReLU。

用于计算隐藏代码l的稀疏表示的方程式如下所示:

稀疏自编码器

重构的输出是隐藏表示,通过以下方式线性映射到输出:

稀疏自编码器

学习通过反向传播重构误差进行。所有参数都被优化以最小化均方误差,公式如下:

稀疏自编码器

现在我们已经设置了网络,可以加入稀疏化组件,它将驱动向量L朝着稀疏表示方向发展。在这里,我们将使用 k-Sparse 自编码器来实现这一层的稀疏表示。(不要混淆 k-Sparse 表示中的kK输入向量。为了区分它们,我们将这两者分别用小k和大K表示。)

k-Sparse 自编码器

k-Sparse 自编码器[132]是基于具有绑定权重和线性激活函数的自编码器。k-Sparse 自编码器的基本思想非常简单。在自编码器的前馈阶段,一旦我们计算出隐藏代码l = WK + b,方法就不会从所有隐藏单元重构输入,而是寻找k个最大的隐藏单元,并将其余隐藏单元的值设置为零。

还有其他方法可以确定 k 个最大的隐藏单元。通过排序隐藏单元的活动或使用 ReLU,隐藏单元的阈值会被调整,直到我们确定 k 个最大的活动。这个选择步骤用于找到 k 个最大的活动是非线性的。这个选择步骤像一个正则化器,帮助防止在通过重构输入生成输出时使用过多的隐藏单元。

如何选择稀疏性水平 k

如果我们强制使用较低的稀疏性水平,例如 k=10,在训练 k-Sparse 自编码器时可能会出现一个问题。一个常见的问题是,在前几个 epoch 中,算法会积极地开始将各个隐藏单元分配到训练样本的组中。这种现象可以与 k-means 聚类方法进行比较。在接下来的 epochs 中,这些隐藏单元会被选中并重新强化,但其他隐藏单元则不会被调整。

这个问题可以通过适当地安排稀疏性水平来解决。假设我们的目标是稀疏性水平为 10。在这种情况下,我们可以从较大的稀疏性水平开始,比如 k=100k=200。因此,k-Sparse 自编码器可以训练所有的隐藏单元。逐渐地,在一个 epoch 的前半部分,我们可以将稀疏性水平从 k=100 线性地减少到 k=10。这样大大增加了选中所有隐藏单元的机会。然后,在该 epoch 的后半部分,我们将保持 k=10。通过这种方式,这种调度方法可以保证即使在低稀疏性水平下,所有的过滤器都能得到训练。

稀疏性水平的影响

在设计或实现 k-Sparse 自编码器时,k 值的选择至关重要。k 的值决定了期望的稀疏性水平,这有助于使算法适用于各种数据集。例如,一个应用可以用于预训练一个深度判别神经网络或一个浅层网络。

如果我们选择一个较大的 k 值(例如在 MNIST 数据集上 k=200),算法将倾向于识别和学习数据集的非常局部的特征。这些特征有时表现得过于初期,无法用于浅层架构的分类。浅层架构通常有一个简单的线性分类器,它实际上没有足够的架构能力来合并所有这些特征并实现显著的分类率。然而,类似的局部特征在预训练深度神经网络时非常有用。

对于较小的稀疏性水平(例如在 MNIST 数据集上 k=10),输出是通过使用较少的隐藏单元从输入重构的。这最终会导致检测到数据集中的全局特征,而不是像前面那样检测到局部特征。这些较少的局部特征适用于浅层架构的分类任务。相反,这种情况对于深度神经网络来说并不理想。

深度自编码器

到目前为止,我们讨论的只是简单自编码器的单层编码器和单层解码器。然而,具有多个编码器和解码器的深度自编码器带来了更多的优势。

前馈网络在深度增加时表现更好。自编码器本质上是前馈网络,因此,基础前馈网络的优势同样适用于自编码器。编码器和解码器是自编码器,它们也像前馈网络一样工作。因此,我们可以在这些组件中也应用前馈网络深度的优势。

在这个过程中,我们还可以讨论普适逼近定理,该定理保证了一个至少包含一层隐藏层,并且隐藏单元足够多的前馈神经网络,可以逼近任何任意函数,并且可以达到任意精度。根据这一概念,深度自编码器只要有至少一层隐藏层,并且包含足够的隐藏单元,就能逼近输入到编码的任意映射。

注意

使用一个两层网络可以逼近任何连续函数,且可以达到任意精度。在人工神经网络的数学理论中,普适逼近定理指出,如果前馈网络至少有一层隐藏层,并且隐藏单元数量是有限的,那么它可以逼近R^n的任何连续函数。

深度自编码器相比浅层架构提供了许多优势。自编码器的非平凡深度抑制了表示一些函数的计算。同时,自编码器的深度大大减少了学习这些函数所需的训练数据量。甚至通过实验发现,深度自编码器在压缩性能上优于浅层自编码器。

训练深度自编码器的常见做法是训练一系列浅层自编码器。因此,在训练深度自编码器时,常常会遇到一系列浅层自编码器。在接下来的小节中,我们将深入讨论深度自编码器的概念。

深度自编码器的训练

此处所解释的深度自编码器设计基于 MNIST 手写数字数据库。在论文[133]中,详细解释了构建和训练深度自编码器的结构化流程。训练深度自编码器的基本过程分为三个阶段:预训练、展开和微调。

  1. 预训练:训练深度自编码器的第一阶段是“预训练”。这一阶段的主要目的是处理二进制数据,将其推广到实值数据,并最终得出结论,表明它在各种数据集上都能良好工作。

    我们已经有足够的见解,认为单层隐藏单元并不是建模大量图像结构的合适方式。深度自编码器由多个限制玻尔兹曼机层组成。在第五章,限制玻尔兹曼机中,我们提供了足够的信息来说明限制玻尔兹曼机是如何工作的。使用相同的概念,我们可以继续构建深度自编码器的结构:

    深度自编码器的训练

    图 6.4:预训练深度自编码器涉及学习一堆限制玻尔兹曼机(RBM),每个 RBM 具有一层特征检测器。一个限制玻尔兹曼机学习到的特征作为“输入数据”用于训练堆叠中的下一个 RBM。经过预训练阶段,所有 RBM 都会被展开或展开以构建深度自编码器。然后,使用误差导数的反向传播方法对深度自编码器进行微调。

    当 RBM 的第一层通过数据流驱动时,该层开始学习特征检测器。这种学习可以作为下一层学习的输入数据。通过这种方式,第一层的特征检测器成为下一层限制玻尔兹曼机学习的可见单元。这个逐层学习的过程可以根据需要反复进行。这个过程在预训练深度自编码器的权重时确实非常有效。每一层捕捉到的特征具有与下方隐藏单元活动之间的高阶相关性。图 6.4的第一部分给出了这一过程的流程图。在处理基准数据集 MNIST 时,深度自编码器在每个 RBM 之后会使用二值变换。为了处理实值数据,深度自编码器在每个限制玻尔兹曼机层之后使用高斯修正变换。

    深度自编码器的训练

    图 6.5:图示表示编码器和解码器的向量数量如何在各个阶段变化。

  2. 展开:一旦深度自编码器的多层特征检测器经过预训练,整个模型将被展开以生成编码器和解码器网络,最初它们使用相同的权重。我们将在图像第二部分中分别解释每个部分的设计,以便更好地理解这一阶段。

    • 编码器:对于一个 28x28 像素的 MNIST 数据集,网络输入的图像将是 784 个像素。根据经验法则,深度自编码器第一层的参数数量应该略大。如 图 6.4 所示,网络第一层使用了 2000 个参数。这可能听起来不太合理,因为增加输入的参数数量会增加过拟合的风险。然而,在这种情况下,增加参数数量最终会增加输入的特征,从而使得自编码器数据的解码成为可能。

    • 图 6.4 所示,层的宽度分别为 2000100050030 个节点。这个现象的快照在 图 6.5 中得以展示。最终,编码器将生成一个 30 长度的向量。这个 30 个数字的向量是深度自编码器编码器的最后一层。这个编码器的大致框架如下:深度自编码器训练

    • 解码器:在编码阶段结束时,得到的 30 个向量是 28x28 像素图像的编码版本。深度自编码器的第二部分是解码器阶段,它基本上学习如何解码压缩向量。因此,编码器阶段的输出(30 个向量)成为解码器阶段的输入。深度自编码器的这一部分是一个前馈网络,经过每一层后,编码的压缩向量逐渐传递到重建的输入。图 6.4 中显示的各层是 3050010002000。这些层最初的权重与预训练网络中的对应层相同,唯一不同的是,权重被转置,如图所示。这个编码器的大致框架如下:深度自编码器训练

      因此,解码深度自编码器的一半的主要目的是学习如何重建图像。这个操作是在第二个前馈网络中进行的,且该网络也执行反向传播,这通过重建熵来实现。

  3. 微调:在微调阶段,随机活动被替换为确定性、实值概率。整个深度自编码器每一层的权重通过反向传播方法被微调,以实现最优重建。

使用 Deeplearning4j 实现深度自编码器

现在,你已经对如何使用多个限制玻尔兹曼机构建深度自编码器有了足够的了解。在本节中,我们将解释如何利用 Deeplearning4j 设计深度自编码器。

我们将使用与上一节相同的 MNIST 数据集,并保持深度自编码器的设计与之前解释的相似。

如前面例子所述,使用从原始 MNIST 数据集中的小批次 1024 个样本,可以将其拆分成 N 个 Hadoop 块。这些 N 个块将由每个工作节点并行地在 Hadoop 分布式文件系统上运行。实现深度自编码器的代码流程简单直接。

步骤如下所示:

  1. 在 HDFS 中按批次加载 MNIST 数据集。每个批次将包含 1024 个样本。

  2. 开始构建模型。

  3. 执行编码操作。

  4. 执行解码操作。

  5. 通过调用 fit() 方法训练模型。

final int numRows = 28;

设置 Hadoop 环境所需的初始配置。batchsize 被设置为 1024

final int numColumns = 28; 
int seed = 123; 
int numSamples = MnistDataFetcher.NUM_EXAMPLES; 
int batchSize = 1024; 
int iterations = 1; 
int listenerFreq = iterations/5; 

将数据加载到 HDFS 中:

log.info("Load data...."); 
DataSetIterator iter = new  MnistDataSetIterator(batchSize,numSamples,true); 

我们现在已经准备好构建模型,加入受限玻尔兹曼机的层数以构建深度自编码器:

log.info("Build model...."); 
MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder() 
  .seed(seed) 
  .iterations(iterations) 
  .optimizationAlgo(OptimizationAlgorithm.LINE_GRADIENT_DESCENT) 

为了创建一个具有指定层数(这里是八层)的 ListBuilder,我们调用 .list() 方法:

 .list(8) 

下一步是构建模型的编码阶段。可以通过将受限玻尔兹曼机(Restricted Boltzmann machine)逐步加入到模型中来完成。编码阶段包含四层受限玻尔兹曼机,每层分别有 2000100050030 个节点:

  .layer(0, new RBM.Builder().nIn(numRows *    
  numColumns).nOut(2000).lossFunction(LossFunctions.LossFunction
  .RMSE_XENT).build()) 
  .layer(1, new RBM.Builder().nIn(2000).nOut(1000)
  .lossFunction(LossFunctions.LossFunction.RMSE_XENT).build()) 
  .layer(2, new RBM.Builder().nIn(1000).nOut(500)
  .lossFunction(LossFunctions.LossFunction.RMSE_XENT).build()) 
  .layer(3, new RBM.Builder().nIn(500).nOut(30)
  .lossFunction(LossFunctions.LossFunction.RMSE_XENT).build()) 

编码器后的下一阶段是解码器阶段,我们将在此阶段使用四个受限玻尔兹曼机,方法与之前相似:

  .layer(4, new RBM.Builder().nIn(30).nOut(500)
  .lossFunction(LossFunctions.LossFunction.RMSE_XENT).build())  
  .layer(5, new RBM.Builder().nIn(500).nOut(1000)
  .lossFunction(LossFunctions.LossFunction.RMSE_XENT).build()) 
  .layer(6, new RBM.Builder().nIn(1000).nOut(2000)
  .lossFunction(LossFunctions.LossFunction.RMSE_XENT).build()) 
  .layer(7, new OutputLayer.Builder(LossFunctions.LossFunction.MSE)
  .activation("sigmoid").nIn(2000).nOut(numRows*numColumns).build()) 

由于所有中间层已构建完毕,现在可以通过调用 build() 方法来构建模型:

 .pretrain(true).backprop(true) 
  .build(); 

实现的最后阶段是训练深度自编码器。可以通过调用 fit() 方法来完成:

MultiLayerNetwork model = new MultiLayerNetwork(conf); 
model.init(); 

model.setListeners(new ScoreIterationListener(listenerFreq)); 

log.info("Train model...."); 
while(iter.hasNext())
  { 
   DataSet next = iter.next(); 
   model.fit(new DataSet(next.getFeatureMatrix(),next
   .getFeatureMatrix())); 
  } 

去噪自编码器

从输入重构输出并不总是能保证得到期望的输出,有时可能只是简单地复制输入。为避免这种情况,[134] 提出了一个不同的策略。在该提议的架构中,重构标准是基于清理部分损坏的输入来构建的,而不是在输入数据的表示上施加一些约束。

"一个好的表示是能够从损坏的输入中稳健地获得的,并且对恢复相应的干净输入是有用的。"

去噪自编码器是一种自编码器,它以损坏的数据作为输入,模型被训练以预测原始的、干净的和未损坏的数据作为输出。在本节中,我们将解释设计去噪自编码器的基本思想。

去噪自编码器的架构

去噪自编码器的主要思想是引入一个损坏过程,Q (k^/ | k),并从损坏的输入k^/重构输出r图 6.6展示了去噪自编码器的整体表示。在去噪自编码器中,对于每个小批量的训练数据k,应该使用Q (k^/ | k)生成相应的损坏k/*。从这里来看,如果我们将初始输入视为损坏输入*k/,那么整个模型可以看作是一种基本编码器的形式。损坏的输入k^/被映射生成隐藏表示h

因此,我们得到以下内容:

去噪自编码器的结构

从这个隐藏表示中,可以使用r = g (h)推导出重构的输出r。去噪自编码器重新组织数据,然后尝试学习这些数据,以便重构输出。这种数据的重新组织或数据的洗牌生成了噪声,模型通过噪声学习特征,从而实现对输入的分类。在网络训练过程中,它生成一个模型,该模型通过损失函数计算该模型与基准之间的距离。其思想是最小化训练集上的平均重构误差,使输出 r 尽可能接近原始未损坏的输入k

去噪自编码器的结构

图 6.6:设计去噪自编码器的步骤。原始输入为 k;从 k 衍生的损坏输入表示为 k^/。最终输出表示为 r。

堆叠去噪自编码器

构建堆叠去噪自编码器以初始化深度神经网络的基本概念类似于堆叠多个限制玻尔兹曼机来构建深度置信网络或传统深度自编码器。生成损坏输入仅在每个单独层的初始去噪训练中需要,以帮助学习有用的特征提取。

一旦我们知道编码函数f来达到隐藏状态,它就可以应用于原始的、未损坏的数据,以到达下一层。通常情况下,不会施加任何损坏或噪声来生成表示,作为训练下一层的未损坏输入。堆叠去噪自编码器的一个关键功能是其逐层的无监督预训练,当输入被馈送通过时。一旦某一层经过预训练,能够对来自前一层的输入进行特征选择和提取,就可以进入下一个阶段的监督微调,类似于传统深度自编码器的情况。

图 6.7展示了设计堆叠去噪自编码器的详细表示。学习和堆叠多个去噪自编码器层的整体过程如下图所示:

堆叠去噪自编码器

图 6.7:堆叠去噪自编码器的表示

使用 Deeplearning4j 实现堆叠去噪自编码器

可以通过使用 Deeplearning4j 创建一个包含自编码器作为隐藏层的MultiLayerNetwork来构建堆叠去噪自编码器。自编码器具有一定的corruptionLevel,即噪声。

在这里,我们设置了建立模型所需的初始配置。为了演示的目的,选择了batchSize1024个示例。输入数和输出数分别设置为10002

int outputNum = 2;
int inputNum = 1000;
int iterations = 10;
int seed = 123;
int batchSize = 1024;

输入数据集的加载与在深度自编码器部分中解释的一样。因此,我们将直接跳到如何构建堆叠去噪自编码器。为了说明方法,我们采用了一个五层隐藏层的深度模型:

log.info ("Build model....");
MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder ()
.seed(seed)
.gradientNormalization(GradientNormalization
  .ClipElementWiseAbsoluteValue)
.gradientNormalizationThreshold (1.0)
.iterations(iterations)
.updater(Updater.NESTEROVS)
.momentum(0.5)
.momentumAfter(Collections.singletonMap(3, 0.9))
.optimizationAlgo(OptimizationAlgorithm.CONJUGATE_GRADIENT)
.list()
.layer(0, new AutoEncoder.Builder()
.nIn(inputNum)
.nOut(500)
.weightInit(WeightInit.XAVIER)
.lossFunction(LossFunction.RMSE_XENT)

以下代码表示输入数据需要被破坏的程度:

.corruptionLevel (0.3)
    .build())
  .layer(1, new AutoEncoder.Builder()
    .nIn(500)
    .nOut(250)
    .weightInit(WeightInit.XAVIER).lossFunction
    (LossFunction.RMSE_XENT)
    .corruptionLevel(0.3)
    .build())
  .layer(2, new AutoEncoder.Builder()
    .nIn(250)
    .nOut(125)
    .weightInit(WeightInit.XAVIER).lossFunction         
    (LossFunction.RMSE_XENT)
    .corruptionLevel(0.3)
    .build())
  .layer(3, new AutoEncoder.Builder()
     .nIn(125)
     .nOut(50)
     .weightInit(WeightInit.XAVIER).lossFunction
     (LossFunction.RMSE_XENT)
     .corruptionLevel(0.3)
     .build())
   .layer(4, new OutputLayer.Builder   
   (LossFunction.NEGATIVELOGLIKELIHOOD)
     .activation("softmax")
     .nIn(75)
     .nOut(outputNum)
     .build())
   .pretrain(true)
.backprop(false)
.build();

一旦模型建立完成,就通过调用fit()方法进行训练:

try {
     model.fit(iter);
    } 
catch(Exception ex)
   {
     ex.printStackTrace();
   }

自编码器的应用

自编码器可以成功应用于许多场景,因此,在深度学习领域获得了广泛的关注。在本节中,我们将讨论自编码器的重要应用和用途:

  • 维度降维:如果你记得,在第一章《深度学习简介》中,我们介绍了“维度灾难”这一概念。维度降维是深度学习的最初应用之一。自编码器最初的研究就是为了克服维度灾难的问题。从本章内容,我们已经对深度自编码器如何在高维数据上工作并在最终输出中减少维度有了大致的了解。

  • 信息检索:自编码器的另一个重要应用是在信息检索中。信息检索基本上是指在数据库中搜索与输入查询匹配的条目。在高维数据中进行搜索通常是一项繁琐的任务;然而,通过降低数据集的维度,某些低维数据的搜索可以变得非常高效。自编码器所获得的维度降维可以生成低维且具有二进制性质的编码。这些编码可以存储在键值存储数据结构中,其中键是二进制代码向量,值是相应的条目。这样的键值存储帮助我们通过返回与查询匹配的二进制代码的所有数据库条目来执行信息检索。通过降维和二进制编码来检索信息的这种方法被称为语义哈希[135]。

  • 图像搜索:正如在深度自编码器部分所解释的,深度自编码器能够将高维图像数据集压缩成非常小的向量,例如 30 个。因此,这使得对高维图像的图像搜索变得更加容易。一旦上传图像,搜索引擎会将其压缩成小向量,然后将该向量与索引中的其他所有向量进行比较。对于搜索查询,将返回包含相似数字的向量,并转换为映射的图像。

总结

自编码器是最受欢迎且广泛应用的生成模型之一,本章对此进行了讨论。自编码器基本上帮助完成两个阶段:一个是编码阶段,另一个是解码阶段。在本章中,我们对这两个阶段进行了详细的数学解释。接下来,我们介绍了一种特殊类型的自编码器,称为稀疏自编码器。我们还讨论了自编码器如何在深度神经网络的世界中使用,通过解释深度自编码器来说明。深度自编码器由限制玻尔兹曼机的层组成,它们参与了网络的编码和解码阶段。我们解释了如何使用 Deeplearning4j 部署深度自编码器,通过将输入数据集的块加载到 Hadoop 分布式文件系统中。本章后面,我们介绍了最流行的自编码器形式——去噪自编码器及其深度网络版本,称为堆叠去噪自编码器。还展示了如何使用 Deeplearning4j 实现堆叠去噪自编码器。我们通过概述自编码器的常见应用来总结本章内容。

在下一章中,我们将借助 Hadoop 讨论一些常见的深度学习实用应用。

第七章. 使用 Hadoop 的杂项深度学习操作

“在开拓者时代,他们使用牛来进行重型牵引,当一头牛无法移动一根原木时,他们不会试图养一头更大的牛。我们不应该追求更大的计算机,而是应该追求更多的计算机系统。”
--格蕾丝·霍普

到目前为止,本书讨论了各种深度神经网络模型及其概念、应用和在分布式环境中的实现。我们还解释了为什么集中式计算机难以存储和处理大量数据,并使用这些模型提取信息。Hadoop 被用于克服大规模数据带来的限制。

随着我们已进入本书的最后一章,本章将主要讨论三种最常用的机器学习应用的设计。我们将解释使用 Hadoop 框架进行大规模视频处理、大规模图像处理和自然语言处理的一般概念。

本章的组织结构如下:

  • 使用 Hadoop 进行大规模分布式视频处理

  • 使用 Hadoop 进行大规模图像处理

  • 使用 Hadoop 进行自然语言处理

数字世界中大量的视频正在为近年来产生的大数据贡献着巨大的份额。在第二章《分布式深度学习与大规模数据》中,我们讨论了如何将数百万个视频上传到各种社交媒体网站,如 YouTube 和 Facebook。除此之外,安装在各大商场、机场或政府机构中的监控摄像头也会每天生成大量的视频。由于这些视频占用巨大的存储空间,大多数视频通常以压缩格式存储。在许多企业中,监控摄像头全天运行,随后存储重要的视频,以备将来调查。

这些视频包含了隐藏的“热数据”或信息,需要迅速处理和提取。因此,处理和分析这些大规模视频已经成为数据爱好者的优先事项之一。此外,在许多不同的研究领域,如生物医学工程、地质学和教育研究等,都需要处理这些大规模视频,并将它们提供给不同地点进行详细分析。

在本节中,我们将探讨使用 Hadoop 框架处理大规模视频数据集的问题。大规模视频处理的主要挑战是将视频从压缩格式转码为非压缩格式。因此,我们需要一个分布式视频转码器,它将视频写入 Hadoop 分布式文件系统HDFS),并行解码比特流块,生成序列文件。

当 HDFS 中的输入数据块被处理时,每个 mapper 进程分别访问每个分片中的行。然而,在大规模视频数据集的情况下,当它被分割成多个预定义大小的块时,每个 mapper 进程应该单独解释这些位流块。然后,mapper 进程将提供对解码后的视频帧的访问,以供后续分析。在以下小节中,我们将讨论如何将包含视频位流的 HDFS 中的每个块转码成图像集,以便进行进一步的分析。

Hadoop 中的分布式视频解码

大多数流行的视频压缩格式,如 MPEG-2 和 MPEG-4,遵循位流中的分层结构。在这一小节中,我们假设使用的压缩格式具有分层结构。为简单起见,我们将解码任务分为两个不同的 Map-reduce 作业:

  1. 提取视频序列级别信息:一开始就可以很容易预测,所有视频数据集的头信息可以在数据集的第一个块中找到。在这个阶段,map-reduce 作业的目的是从视频数据集的第一个块中收集序列级别信息,并将结果输出为 HDFS 中的文本文件。序列头信息对于设置解码器对象的格式是必需的。

    对于视频文件,应实现一个新的FileInputFormat,并具有自己的记录读取器。每个记录读取器将以这种格式向每个 map 过程提供<key, value>对:<LongWritable, BytesWritable>。输入键表示文件中的字节偏移量;与BytesWritable对应的值是一个字节数组,包含整个数据块的视频位流。

    对于每个 map 过程,键值与0进行比较,以确定它是否是视频文件的第一个块。一旦确定了第一个块,位流将被解析以确定序列级别信息。然后,这些信息将被转储到.txt文件中,并写入 HDFS。我们将该.txt文件的名称表示为input_filename_sequence_level_header_information.txt。由于只有 map 过程可以为我们提供所需的输出,因此此方法的 reducer 数量设置为0

    注意

    假设有一个包含以下数据的文本文件:深度学习 与 Hadoop 现在,第一行的偏移量为0,Hadoop 作业的输入将是<0,深度学习>,第二行的偏移量将是<14,与 Hadoop>。每当我们将文本文件传递给 Hadoop 作业时,它会内部计算字节偏移量。

  2. 解码并转换视频块为序列文件:这个 Map-reduce 任务的目的是解码每个视频数据集的块,并生成相应的序列文件。序列文件将包含每个视频块的解码视频帧,格式为 JPEG。InputFileFormat 文件和记录读取器应与第一个 Map-reduce 任务保持一致。因此,mapper 输入的 <key, value> 对是 <LongWritable, BytesWritable>。Hadoop 分布式视频解码

    图 7.1:Hadoop 视频解码的整体表示

    • 在第二阶段,第一次任务的输出作为第二次 Map-reduce 任务的输入。因此,这个任务的每个 mapper 将读取 HDFS 中的序列信息文件,并将此信息与以 BytesWritable 输入形式传入的位流缓冲区一起传递。

    • map 过程基本上将解码的视频帧转换为 JPEG 图像,并生成 <key, value> 对,作为 map 过程的输出。该输出的 key 编码了输入视频文件名和块编号,格式为 video_filename_block_number。对应于此 key 的输出值是 BytesWritable,它存储了解码视频块的 JPEG 位流。

    • 然后,reducers 会将数据块作为输入,并将解码的帧简单地写入包含 JPEG 图像的序列文件中,作为输出格式进行进一步处理。一个简单的格式和整个过程的概览如 图 7.1 所示。我们使用一个输入视频 sample.m2v 进行说明。此外,在本章中,我们将讨论如何使用 HDFS 处理大规模的图像文件(来自序列文件)。

    注意

    Mapper 的输入 <key,value><LongWritable, BytesWritable>

    例如:<17308965, BytesWritable> Mapper 输出的 <key,value><Text, BytesWritable> 例如:<sample.m2v_3, BytesWritable>

使用 Hadoop 进行大规模图像处理

我们在前面的章节中已经提到,图像的大小和数量正日益增加;存储和处理这些庞大的图像对于集中式计算机来说是一项挑战。我们来考虑一个实际的例子,以便更好地理解这种情况。假设我们有一张尺寸为 81025 像素 x 86273 像素的大规模图像。每个像素由三个值组成:红色、绿色和蓝色。假设每个值需要使用 32 位精度的浮动点数来存储。那么,该图像的总内存消耗可以通过以下公式计算:

86273 * 81025 * 3 * 32 位 = 78.12 GB

抛开对图像进行任何后处理,因为可以清楚地得出结论,传统计算机甚至无法将如此大量的数据存储在其主内存中。即使一些高级计算机具有更高的配置,但考虑到投资回报率,大多数公司并不选择这些计算机,因为它们的购买和维护成本过于昂贵。因此,适当的解决方案应该是使用通用硬件来运行这些图像,以便图像能够存储在其内存中。在本节中,我们将解释如何使用 Hadoop 以分布式方式处理这些大量图像。

Map-Reduce 作业的应用

在本节中,我们将讨论如何使用 Map-reduce 作业和 Hadoop 来处理大型图像文件。在作业开始之前,所有待处理的输入图像将被加载到 HDFS 中。在操作过程中,客户端发送一个作业请求,该请求经过 NameNode。NameNode 从客户端接收该请求,搜索其元数据映射,然后将文件系统的数据块信息以及数据块的位置发送回客户端。一旦客户端获得了数据块的元数据,它会自动访问存储该数据块的 DataNodes,然后通过适用的命令处理该数据。

用于大规模图像处理的 Map-reduce 作业主要负责控制整个任务。基本上,在这里我们解释了可执行 shell 脚本文件的概念,该文件负责从 HDFS 中收集可执行文件的输入数据。

使用 Map-reduce 编程模型的最佳方式是设计我们自己的 Hadoop 数据类型,用于直接处理大量图像文件。系统将使用 Hadoop Streaming 技术,帮助用户创建和运行特殊类型的 Map-reduce 作业。这些特殊类型的作业可以通过前面提到的可执行文件来执行,该文件将充当映射器或化简器。程序的映射器实现将使用一个 shell 脚本来执行必要的操作。这个 shell 脚本负责调用图像处理的可执行文件。这些图像文件列表作为输入传递给这些可执行文件,进行进一步处理。该处理的结果或输出稍后会写回到 HDFS 中。

因此,输入的图像文件应首先写入 HDFS,然后在 Hadoop Streaming 的输入目录中生成一个文件列表。该目录将存储文件列表的集合。文件列表的每一行将包含要处理的图像文件的 HDFS 地址。Mapper 的输入是Inputsplit类,这是一个文本文件。Shell 脚本管理器逐行读取文件并从元数据中检索图像。接着,它调用图像处理可执行文件进一步处理图像,然后将结果写回 HDFS。因此,Mapper 的输出就是最终所需的结果。Mapper 完成了所有工作,包括从 HDFS 检索图像文件、图像处理,然后将其写回 HDFS。该过程中的 Reducer 数量可以设置为零。

这是一个使用 Hadoop 按二进制图像处理方法处理大量图像的简单设计。其他复杂的图像处理方法也可以部署来处理大规模的图像数据集。

使用 Hadoop 进行自然语言处理

网络信息的指数增长增加了大规模非结构化自然语言文本资源的传播强度。因此,在过去几年中,提取、处理和共享这些信息的兴趣显著增加。在规定的时间内处理这些知识源已成为各个研究和商业行业面临的主要挑战。在这一部分,我们将描述使用 Hadoop 以分布式方式爬取网页文档、发现信息并运行自然语言处理的过程。

为了设计自然语言处理NLP)的架构,首先要进行的任务是从大规模非结构化数据中提取标注的关键词和关键短语。为了在分布式架构上执行 NLP,可以选择 Apache Hadoop 框架,因为它提供了高效且可扩展的解决方案,并且能改善故障处理和数据完整性。大规模网页爬虫可以被设置为从 Web 中提取所有非结构化数据并将其写入 Hadoop 分布式文件系统以供进一步处理。为了执行特定的 NLP 任务,我们可以使用开源的 GATE 应用程序,如论文[136]所示。分布式自然语言处理架构的初步设计概览如图 7.2所示。

为了分配网络爬虫的工作,可以使用 Map-reduce 并在多个节点上运行。NLP 任务的执行以及最终输出的写入都是通过 Map-reduce 来完成的。整个架构将依赖于两个输入文件:i)存储在 seed_urls.txt 中的用于抓取特定网页的 seedurls,ii)NLP 应用程序的路径位置(例如 GATE 的安装位置)。网络爬虫将从 .txt 文件中获取 seedurls,并为这些 URL 并行运行爬虫。异步地,一个提取插件将在抓取的网页上搜索关键词和关键短语,并与网页一起独立执行。在最后一步,一个专用程序会根据需求将提取的关键词和关键短语存储在外部 SQL 数据库或 NoSQL 数据库中,如 Elasticsearch。架构中提到的所有这些模块将在以下子章节中进行描述。

网络爬虫

为了解释这一阶段,我们不打算深入讨论,因为这几乎超出了本书的范围。网络爬虫有几个不同的阶段。第一阶段是 URL 发现阶段,该过程将每个种子 URL 作为 seed_urls.txt 文件的输入,并通过分页 URL 来发现相关的 URL。这个阶段定义了下一阶段要抓取的 URL 集合。

下一阶段是抓取 URL 页面内容并保存在磁盘中。操作是按段进行的,每个段包含一定数量的预定义 URL。操作将在不同的 DataNode 上并行执行。各个阶段的最终结果将保存在 Hadoop 分布式文件系统中。关键词提取器将在这些保存的页面内容上工作,为下一阶段做准备。

网络爬虫

图 7.2:展示了自然语言处理在 Hadoop 中的执行过程,下一阶段将会获取这些数据。下一阶段是抓取 URL 页面内容并保存在磁盘中。该操作是按段进行的,每个段包含一定数量的预定义 URL。操作将在不同的 DataNode 上并行执行。各个阶段的最终结果将保存在 Hadoop 分布式文件系统中。关键词提取器将在这些保存的页面内容上工作,为下一阶段做准备。

关键词提取和自然语言处理模块

对于每个 URL 的页面内容,创建并存储一个 文档对象模型 (DOM) 回到 HDFS 中。在 DOM 中,文档具有类似树的逻辑结构。通过使用 DOM,可以编写 xpath 来收集自然语言处理阶段所需的关键词和短语。在本模块中,我们将定义 Map-reduce 作业来执行下一阶段的自然语言处理应用程序。定义的 map 函数是一个 <key, value> 对,key 是 URL,value 是该 URL 对应的 DOM。reduce 函数将执行自然语言处理部分的配置和执行。接下来的关键词和短语在网页域级别的估算将在 reduce 方法中执行。为此,我们可以编写一个自定义插件来生成规则文件,通过执行各种字符串操作,过滤掉从提取文本中获得的噪声和不需要的词汇。规则文件可以是 JSON 文件或任何其他易于加载和解析的文件,具体取决于用例。通常,我们将常见的名词和形容词识别为文本中的常见关键词。

从页面估算相关关键词

论文 [136] 提出了一个非常重要的公式,用于从网页文档中找到相关的关键词和关键短语。他们提供了 词频 - 逆文档频率 (TF-IDF) 度量,用于从整个语料库中估算相关信息,语料库由属于同一网页域的所有文档和页面组成。计算 TF-IDF 的值并为丢弃其他关键词设定阈值,使我们能够从语料库中生成最相关的词语。换句话说,它丢弃了可能在文本中出现频率较高的常见冠词和连词,这些词通常不包含任何有意义的信息。TF-IDF 度量本质上是两个函数 TFIDF 的乘积。

TF 提供了每个词在语料库中的频率,即一个词在语料库中出现的次数。而 IDF 起到了平衡作用,对于在整个语料库中出现频率较低的词,给出较高的值。

从数学上讲,关键词或关键短语 i 在文档 d 中的 TF-IDF 度量公式如下:

(TF-IDF)[i] = TF[i] . IDF[i]

这里 TF[i] = f[i]/n[d]IDF[i] = log N[d]/N[i]

这里的 f[i] 是候选关键词或关键短语 i 在文档 d 中的频率,n[d] 是文档 d 中的总词数。在 IDF 中,N[D] 表示语料库 D 中的文档总数,而 N[i] 表示包含关键词或关键短语 i 的文档数量。

根据使用案例,应该为TF-IDF定义一个通用的阈值频率。对于一个关键字或关键短语i,如果TF-IDF的值超过阈值,该关键字或关键短语将被接受并直接写入 HDFS。另一方面,如果相应的值低于阈值,则该关键字将从最终集合中删除。通过这种方式,最终所有所需的关键字将被写入 HDFS。

总结

本章讨论了机器学习中最广泛使用的应用程序以及如何在 Hadoop 框架中设计它们。首先,我们从一个大型视频集开始,展示了如何在 HDFS 中解码视频,并将其转换为包含图像的序列文件,以便后续处理。接下来,本章讨论了大规模图像处理。用于此目的的 mapper 有一个 shell 脚本,执行所有必要的任务。因此,不需要 reducer 来执行此操作。最后,我们讨论了如何将自然语言处理模型部署到 Hadoop 中。

附录 1. 参考文献

[1] Hsu, F.-H. (2002). 《深蓝背后:打造击败世界象棋冠军的计算机》. 普林斯顿大学出版社,普林斯顿,新泽西州,美国。 [2] Geoffrey E. Hinton, Simon Osindero, 和 Yee-Whye Teh. 2006 年. 《深度信念网络的快速学习算法》。神经计算,18(7)(2006 年 7 月),1527-1554。 [3] Bengio, Yoshua,等人. "深度网络的贪心层训练"。《神经信息处理系统进展》 19 (2007): 153。 [4] Krizhevsky, Alex, Ilya Sutskever 和 Geoffrey E. Hinton. "使用深度卷积神经网络进行 ImageNet 分类。"《神经信息处理系统进展》,2012 年。 [5] 《机器学习》,Tom Mitchell,McGraw Hill,1997 年。 [6] 《机器学习:一种概率视角》(自适应计算与机器学习系列),Kevin P. Murphy [7] O. Chapelle,B. Scholkopf 和 A. Zien 主编,"半监督学习(Chapelle,O. 等人,主编;2006 年)[书评]",《IEEE 神经网络学报》,第 20 卷,第 3 期,542-542 页,2009 年 3 月。 [8] Y. Bengio. 《为人工智能学习深度架构》。载于《机器学习基础与趋势》,2(1):1–127,2009 年。 [9] G. Dahl,D. Yu,L. Deng 和 A. Acero. "基于上下文的 DBNHMMs 在大词汇量连续语音识别中的应用"。国际声学、语音与信号处理会议(ICASSP)论文集,2011 年。 [10] A. Mohamed,G. Dahl 和 G. Hinton. "使用深度信念网络进行声学建模"。《IEEE 音频、语音与语言处理学报》,20(1),2012 年 1 月。 [11] A. Mohamed,D. Yu 和 L. Deng. "深度信念网络全序列训练在语音识别中的应用研究"。国际语音学会会议论文集,2010 年。 [12] Indyk, Piotr 和 Rajeev Motwani. "近似最近邻:消除维度灾难的尝试"。第 30 届 ACM 计算理论年会论文集,ACM,1998 年。 [13] Friedman, Jerome H. "关于偏差、方差、0/1 损失和维度灾难"。《数据挖掘与知识发现》 1.1 (1997): 55-77。 [14] Keogh, Eamonn 和 Abdullah Mueen. "维度灾难"。《机器学习百科全书》,Springer US,2011 年,257-258 页。 [15] Hughes, G.F. (1968 年 1 月). "关于统计模式识别器的平均准确率"。《IEEE 信息理论学报》,14(1): 55–63。 [16] Bengio, Yoshua, Patrice Simard 和 Paolo Frasconi. "使用梯度下降学习长期依赖关系是困难的"。《IEEE 神经网络学报》5.2 (1994): 157-166。

[17] Ivakhnenko, Alexey (1965). Cybernetic Predicting Devices. Kiev: Naukova Dumka. [18] Ivakhnenko, Alexey (1971). "Polynomial theory of complex systems". IEEE Transactions on Systems, Man and Cybernetics (4): 364–378. [19] X. Glorot 和 Y. Bengio。理解深度前馈神经网络训练的困难。在人工智能和统计学会议论文集(AISTATS)中的论文。2010 年。[20] G. Hinton 和 R. Salakhutdinov。利用神经网络减少数据的维度。科学,313(5786):504–507,2006 年 7 月[21] M. Ranzato, C. Poultney, S. Chopra 和 Y. LeCun。利用基于能量的模型高效学习稀疏表示。在神经信息处理系统(NIPS)会议论文集中。2006 年。[22] I. Goodfellow, M. Mirza, A. Courville 和 Y. Bengio。多预测深度玻尔兹曼机。在神经信息处理系统(NIPS)会议论文集中。2013 年。[23] R. Salakhutdinov 和 G. Hinton。深度玻尔兹曼机。在人工智能和统计学会议(AISTATS)论文集中。2009 年。[24] R. Salakhutdinov 和 G. Hinton。预训练深度玻尔兹曼机的更好方法。在神经信息处理系统(NIPS)会议论文集中。2012 年。[25] N. Srivastava 和 R. Salakhutdinov。多模态学习与深度玻尔兹曼机。在神经信息处理系统(NIPS)会议论文集中。2012 年。[26] H. Poon 和 P. Domingos。总-产品网络:一种新的深度架构。在不确定性人工智能会议论文集中。2011 年。[27] R. Gens 和 P. Domingo。总-产品网络的判别学习。神经信息处理系统(NIPS),2012 年。[28] R. Gens 和 P. Domingo。总-产品网络的判别学习。神经信息处理系统(NIPS),2012 年。[29] S. Hochreiter。动态神经网络研究。技术大学慕尼黑,计算机学院,1991 年。毕业论文。[30] J.Martens。使用无 Hessian 优化的深度学习。在国际机器学习大会(ICML)论文集中。2010 年。[31] Y. Bengio。代表性的深度学习:展望。在统计语言和语音处理中,页 1–37。斯普林格出版社,2013 年。[32] I. Sutskever。训练递归神经网络。多伦多大学,博士论文,2013 年。[33] J. Ngiam, Z. Chen, P. Koh 和 A. Ng。学习深度能量模型。在国际机器学习大会(ICML)论文集中。2011 年。[34] Y. LeCun, S. Chopra, M. Ranzato 和 F. Huang。文件识别和计算机视觉中的基于能量的模型。在国际文档分析和识别大会(ICDAR)论文集中。2007 年。[35] R. Chengalvarayan 和 L. Deng。利用最小分类错误学习进行语音轨迹判别。IEEE 语音和音频处理交易,6(6):505–515,1998 年。

[36] M. Gibson 和 T. Hain. 错误逼近与最小电话错误声学模型估计。IEEE 音频、语音与语言处理学报,18(6):1269–1279,2010 年 8 月。[37] X. He, L. Deng 和 W. Chou. 序列模式识别中的判别学习—面向优化的语音识别统一综述。IEEE 信号处理杂志,25:14–36,2008 年。[38] H. Jiang 和 X. Li. 使用凸优化的统计模型参数估计:一种面向语音和语言处理的先进判别训练方法。IEEE 信号处理杂志,27(3):115–127,2010 年。[39] B.-H. Juang, W. Chou 和 C.-H. Lee. 语音识别的最小分类错误率方法。IEEE 语音与音频处理学报,5:257–265,1997 年。[40] D. Povey 和 P. Woodland. 最小电话错误与 I 平滑用于改进判别训练。在国际声学、语音与信号处理会议(ICASSP)上发表,2002 年。[41] D. Yu, L. Deng, X. He 和 X. Acero. 面向大规模语音识别任务的“大间隔最小分类错误训练”。在国际声学、语音与信号处理会议(ICASSP)上发表,2007 年。[42] A. Robinson. 循环神经网络在电话概率估计中的应用。IEEE 神经网络学报,5:298–305,1994 年。[43] A. Graves. 使用循环神经网络的序列转导。国际机器学习会议(ICML)表征学习研讨会,2012 年。[44] A. Graves, S. Fernandez, F. Gomez 和 J. Schmidhuber. 连接主义时序分类:使用循环神经网络标注未分段序列数据。在国际机器学习会议(ICML)上发表,2006 年。[45] A. Graves, N. Jaitly 和 A. Mohamed. 使用深度双向 LSTM 的混合语音识别。在自动语音识别与理解研讨会(ASRU)上发表,2013 年。[46] A. Graves, A. Mohamed 和 G. Hinton. 使用深度循环神经网络的语音识别。在国际声学、语音与信号处理会议(ICASSP)上发表,2013 年。[47] K. Lang, A. Waibel 和 G. Hinton. 用于孤立词识别的时延神经网络架构。神经网络,3(1):23–43,1990 年。[48] A. Waibel, T. Hanazawa, G. Hinton, K. Shikano 和 K. Lang. 使用时延神经网络的音素识别。IEEE 声学、语音与信号处理学报,37:328–339,1989 年。[50] Moore, Gordon E. (1965-04-19). "将更多组件集成到集成电路中"。电子学。2016-07-01 检索。[51] www.emc.com/collateral/analyst-reports/idc-the-digital-universe-in-2020.pdf [52] D. Beaver, S. Kumar, H. C. Li, J. Sobel 和 P. Vajgel, “在干草堆中找针:Facebook 的照片存储”,在 OSDI,2010 年,第 47-60 页。[53] Michele Banko 和 Eric Brill. 2001 年。大规模语料库在自然语言歧义消解中的扩展。在第 39 届计算语言学年会(ACL '01)上发表。计算语言学协会,美国宾夕法尼亚州斯特劳兹堡,26-33 页。[54] www.huffingtonpost.in/entry/big-data-and-deep-learnin_b_3325352 [55] X. W. Chen 和 X. Lin, "大数据深度学习:挑战与展望," 在 IEEE Access,第 2 卷,第 X 期,514-525 页,2014 年。[56] Bengio Y, LeCun Y (2007) 将学习算法扩展至人工智能。In: Bottou L, Chapelle O, DeCoste D, Weston J (编). 大规模核机器。麻省理工学院出版社,美国剑桥,第 34 卷。第 321–360 页。www.iro.umontreal.ca/~lisa/pointeurs/bengio+lecun_chapter2007.pdf [57] A. Coats, B. Huval, T. Wng, D. Wu 和 A. Wu, “使用 COTS HPS 系统的深度学习,”J. Mach. Learn. Res., 第 28 卷,第 3 期,第 1337-1345 页,2013 年。[58] J.Wang 和 X. Shen, "大间隔半监督学习," J. Mach. Learn. Res., 第 8 卷,第 8 期,第 1867-1891 页,2007 年。[59] R. Fergus, Y. Weiss 和 A. Torralba, “在庞大的图像集合中进行半监督学习”,在 NIPS 会议论文集(2009 年)中,第 522-530 页。[60] J. Ngiam, A. Khosla, M. Kim, J. Nam, H. Lee 和 A. Ng, “多模态深度学习”,在第 28 届国际机器学习大会(2011 年)上发表,美国华盛顿州贝尔维尤。[61] N. Srivastava 和 R. Salakhutdinov, “使用深度玻尔兹曼机的多模态学习”,在 NIPS 会议论文集(2012 年)中发表。[62] L. Bottou, “在线算法与随机逼近”,在《神经网络中的在线学习》,D. Saad(编)。剑桥大学出版社,英国剑桥,1998 年。[63] A. Blum 和 C. Burch, “在线学习与度量任务系统问题”,在第 10 届计算学习理论年会(1997 年)上发表,第 45-53 页。[64] N. Cesa-Bianchi, Y. Freund, D. Helmbold 和 M. Warmuth, “在线预测与对话策略”,在欧罗科尔特计算学习理论会议论文集,第 53 卷。英国牛津,1994 年,第 205-216 页。[65] Y. Freund 和 R. Schapire, “博弈论、在线预测与提升”,在第 9 届计算学习理论年会(1996 年)上发表,第 325-332 页。[66] Q. Le 等,“使用大规模无监督学习构建高级特征”,在国际机器学习会议论文集,2012 年。[67] C. P. Lim 和 R. F. Harrison, “使用多个神经网络系统的在线模式分类:一项实验研究”,IEEE 系统、人工智能与控制学报,C 卷,33(2):235-247,2003 年 5 月。[68] P. Riegler 和 M. Biehl, “两层神经网络中的在线反向传播”,J. Phys. A,第 28 卷,第 20 期,第 L507-L513 页,1995 年。[69] M. Rattray 和 D. Saad, “多层神经网络的全局最优在线学习规则”,J. Phys. A,数学一般,第 30 卷,第 22 期,第 L771-776 页,1997 年。

[70] P. Campolucci, A. Uncini, F. Piazza 和 B. Rao,针对局部递归神经网络的在线学习算法,''IEEE Trans. Neural Netw.,第 10 卷,第 2 期,页 253-271,1999 年 3 月。[71] N. Liang, G. Huang, P. Saratchandran 和 N. Sundararajan,一种快速且准确的前馈网络在线顺序学习算法,''IEEE Trans. Neural Netw.,第 17 卷,第 6 期,页 1411-1423,2006 年 11 月。[72] L. Bottou 和 O. Bousequet,神经网络中的随机梯度学习,''在 Neuro-Nimes 会议论文集,1991 年。[73] S. Shalev-Shwartz, Y. Singer 和 N. Srebro,Pegasos:SVM 的原始估计子梯度求解器,''在国际机器学习会议论文集,2007 年。[74] D. Scherer, A. Müller 和 S. Behnke,卷积架构中池化操作的评估用于物体识别,''在国际人工神经网络会议论文集,2010 年,页 92-101。[75] J. Chien 和 H. Hsieh,使用顺序和变分贝叶斯学习进行非平稳源分离,''IEEE Trans. Neural Netw. Learn. Syst.,第 24 卷,第 5 期,页 681-694,2013 年 5 月。[76] W. de Oliveira,``Rosenblatt 贝叶斯算法在非平稳环境中的学习,''IEEE Trans. Neural Netw.,第 18 卷,第 2 期,页 584-588,2007 年 3 月。[77] Hadoop 分布式文件系统,hadoop.apache.org/2012。[78] T. White. 2009 年. 《Hadoop:权威指南》。O'Reilly Media, Inc. 2009 年 6 月。[79] Shvachko, K.; Hairong Kuang; Radia, S.; Chansler, R.,2010 年 5 月. 《Hadoop 分布式文件系统》,2010 年 IEEE 第 26 届大容量存储系统与技术研讨会(MSST)。卷,无,页 1,10。[80] Hadoop 分布式文件系统,hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-hdfs/。[81] Dev, Dipayan 和 Ripon Patgiri. "Dr. Hadoop: 一种无限可扩展的 Hadoop 元数据管理——小象如何变得永生。" 《信息技术与电子工程前沿》17(2016 年):15-31。[82] deeplearning4j.org/ [83] Dean, Jeffrey 和 Sanjay Ghemawat. "MapReduce: 简化大规模集群数据处理。" 《ACM 通讯》51.1(2008 年):107-113。[84] deeplearning.net/software/theano/ [85] torch.ch/ [86] Borthakur, Dhruba. "Hadoop 分布式文件系统:架构与设计。" Hadoop 项目网站 11.2007(2007 年):21。[87] Borthakur, Dhruba. "HDFS 架构指南。" HADOOP APACHE PROJECT hadoop.apache.org/docs/r1.2.1/hdfs_design.pdf(2008 年):39。[88] deeplearning4j.org/quickstart

[89] LeCun, Yann 和 Yoshua Bengio。"用于图像、语音和时间序列的卷积网络。"大脑理论和神经网络手册 3361.10(1995 年):1995。 [90] LeCun, Y.、Bottou, L.、Bengio, Y.和 Haffner, P.(1998)。基于梯度的文档识别应用学习。IEEE 86, 2278-2324。doi:10.1109/5.726791 [91] Gao, H.、Mao, J.、Zhou, J.、Huang, Z.、Wang, L.和 Xu, W.(2015)。您在与机器交谈吗?多语言图像问答的数据集和方法。arXiv 预印本 arXiv:1505.05612。 [92] Srinivas, Suraj 等人。"计算机视觉深度卷积神经网络的分类学。"arXiv 预印本 arXiv:1601.06615(2016)。 [93] Zhou, Y-T.等人。"使用神经网络进行图像恢复。"IEEE 声学、语音和信号处理期刊 36.7(1988):1141-1151。 [94] Maas, Andrew L.、Awni Y. Hannun 和 Andrew Y. Ng。"整流器非线性改进神经网络声学模型。"Proc. ICML。Vol. 30。No. 1。2013。 [95] He, Kaiming 等人。"深入研究整流器:超越图像分类上的人类水平性能。"IEEE 国际计算机视觉会议论文集。2015。 [96] web.engr.illinois.edu/~slazebni/spring14/lec24_cnn.pdf [97] Zeiler, Matthew D.和 Rob Fergus。"可视化和理解卷积网络。"欧洲计算机视觉会议。斯普林格国际出版社,2014 年。 [98] Simonyan, Karen 和 Andrew Zisserman。"用于大规模图像识别的非常深的卷积网络。"arXiv 预印本 arXiv:1409.1556(2014)。 [99] Szegedy, Christian 等人。"通过卷积更深入地挖掘。"IEEE 计算机视觉与模式识别会议论文集。2015。 [100] He, Kaiming 等人。"用于图像识别的深度残余学习。"arXiv 预印本 arXiv:1512.03385(2015)。 [101] Krizhevsky, Alex。"并行化卷积神经网络的一个奇怪的技巧。"arXiv 预印本 arXiv:1404.5997(2014)。 [102] S. Hochreiter 和 J. Schmidhuber。长短期记忆。神经计算,9(8):1735-1780,1997。 [103] Mikolov, Tomas 等人。"基于递归神经网络的语言模型。"Interspeech。Vol. 2。2010。 [104] Rumelhart, D. E.、Hinton, G. E.和 Williams, R. J.(1986)。通过反向传播学习表示。自然,323,533-536。 [105] Mikolov, T.、Sutskever, I.、Chen, K.、Corrado, G.和 Dean, J.(2013a)。单词和短语的分布式表示及其组合性。在神经信息处理系统 26 的进展中,页面 3111-3119。 [106] Graves, A.(2013)。使用递归神经网络生成序列。arXiv:1308.0850 [cs.NE]。 [107] Pascanu, R.、Mikolov, T.和 Bengio, Y.(2013a)。训练递归神经网络的困难。在 ICML’2013。

[108] Mikolov, T., Sutskever, I., Deoras, A., Le, H., Kombrink, S., 和 Cernocky, J. (2012a)。使用神经网络的子词语言建模。未发表的[109] Graves, A., Mohamed, A., 和 Hinton, G. (2013)。深度递归神经网络的语音识别。ICASSP[110] Graves, A., Liwicki, M., Fernandez, S., Bertolami, R., Bunke, H., 和 Schmidhuber, J. (2009)。一种新型的连接主义系统,用于改善无约束手写识别。IEEE 模式分析与机器智能汇刊。[111] karpathy.github.io/2015/05/21/rnn-effectiveness/ [112] web.stanford.edu/group/pdplab/pdphandbook/handbookch8.html [113] Schuster, Mike, 和 Kuldip K. Paliwal。"双向递归神经网络"。IEEE 信号处理汇刊 45.11 (1997): 2673-2681。[114] Graves, Alan, Navdeep Jaitly, 和 Abdel-rahman Mohamed。"基于深度双向 LSTM 的混合语音识别"。自动语音识别与理解(ASRU),2013 IEEE 研讨会。IEEE,2013[115] Baldi, Pierre, 等。"在蛋白质二级结构预测中利用过去和未来"。生物信息学 15.11 (1999): 937-946[116] Hochreiter, Sepp, 和 Jürgen Schmidhuber。"长短时记忆"。神经计算 9.8 (1997): 1735-1780。[117] A. Graves, M. Liwicki, S. Fernandez, R. Bertolami, H. Bunke, J. Schmidhuber。改进的无约束手写识别的新型连接主义系统。IEEE 模式分析与机器智能汇刊,31 卷,第 5 期,2009 年。[118] 使用 QuickType,苹果公司希望做的不仅仅是猜测你的下一条文本。它还想给你一个 AI。"WIRED"。2016 年 6 月 16 日检索。[119] Sak, Hasim, Andrew W. Senior, 和 Françoise Beaufays。"用于大规模声学建模的长短时记忆递归神经网络架构"。INTERSPEECH。2014 年。[120] Poultney, Christopher, Sumit Chopra, 和 Yann L. Cun。"利用基于能量的模型进行稀疏表示的高效学习"。神经信息处理系统进展。2006 年。[121] LeCun, Yann, 等。"关于基于能量的学习的教程"。结构化数据预测 1 (2006): 0。[122] Ackley, David H., Geoffrey E. Hinton, 和 Terrence J. Sejnowski。"Boltzmann 机的学习算法"。认知科学 9.1 (1985): 147-169。[123] Desjardins, G. 和 Bengio, Y. (2008)。卷积 RBM 在视觉中的经验评估。技术报告 1327,蒙特利尔大学计算机科学与运筹学系。[124] Hinton, G. E., Osindero, S., 和 Teh, Y. (2006)。一种快速学习算法,用于深度置信网络。神经计算,18,1527–1554。[125] Hinton, G. E. (2007b)。学习多层表示。认知科学趋势,11(10),428–434。

[126] Bengio, Yoshua 等人. "深度网络的贪婪分层训练." 《神经信息处理系统进展》19 (2007): 153. [127] A.-R. Mohamed, T. N. Sainath, G. Dahl, B. Ramabhadran, G. E. Hinton 和 M. A. Picheny, 使用判别特征进行电话识别的深度信念网络,'' 见《IEEE ICASSP 会议论文集》,2011 年 5 月,pp. 5060-5063. [128] R. Salakhutdinov 和 G. Hinton, 语义哈希,'' 《近似推理国际期刊》,第 50 卷,第 7 期,pp. 969-978,2009 年. [129] G. W. Taylor, G. E. Hinton 和 S. T. Roweis, ``使用二进制潜变量建模人类运动,'' 见《神经信息处理系统进展》. 美国马萨诸塞州剑桥: MIT 出版社, 2006 年,pp. 1345-1352. [130] Zhang, Kunlei 和 Xue-Wen Chen. "大规模深度信念网络与 MapReduce." 《IEEE Access》2 (2014): 395-403.

[131] Yoshua Bengio, Aaron Courville 和 Pascal Vincent. 表示学习:回顾与新视角. 技术报告,arXiv:1206.5538,2012b. [132] Makhzani, Alireza 和 Brendan Frey. "k-稀疏自编码器." arXiv 预印本 arXiv:1312.5663 (2013). [133] Hinton, Geoffrey E. 和 Ruslan R. Salakhutdinov. "使用神经网络减少数据的维度." 《科学》313.5786 (2006): 504-507. [134] Vincent, Pascal 等人. "堆叠去噪自编码器:在具有局部去噪准则的深度网络中学习有用的表示." 《机器学习研究期刊》11.12 (2010): 3371-3408. [135] Salakhutdinov, Ruslan 和 Geoffrey Hinton. "语义哈希." RBM 500.3 (2007): 500. [136] Nesi, Paolo, Gianni Pantaleo 和 Gianmarco Sanesi. "基于 Hadoop 的平台用于网页和文档的自然语言处理." 《视觉语言与计算期刊》31 (2015): 130-138.

posted @ 2025-07-13 15:43  绝不原创的飞龙  阅读(40)  评论(0)    收藏  举报