作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
近年来,人工智能领域出现了复苏. 它已经传播到学术界以外的主要参与者,比如 谷歌微软,以及 脸谱网 建立了自己的研究团队,取得了一些令人印象深刻的成果 收购.
这可以部分归因于社交网络用户产生的大量原始数据, 其中很多都需要分析, 发达国家的崛起 数据科学解决方案,以及廉价的计算能力 gpgpu.
但在这些现象之外, 这种复苏在很大程度上是由人工智能的新趋势推动的, 特别是在 机器学习被称为“深度学习”. 在本教程中, 我将向你介绍深度学习背后的关键概念和算法, 从最简单的组成单元开始,构建Java中的机器学习概念.
(为了充分披露:我也是一个Java深度学习库的作者,可用 在这里,本文中的示例是使用上述库实现的. 如果你喜欢, 你可以在GitHub上给它打颗星来支持它,对此我很感激. 使用说明可在 主页.)
如果你不熟悉,看看这个 机器学习概论:
一般程序如下:
This setting is incredibly general: your data could be symptoms 和 your labels illnesses; or your data could be images of h和written characters 和 your labels the 实际 characters they represent.
最早的监督训练算法之一是感知器算法, 一个基本的神经网络构建块.
假设我们有 n 平面上的点,标记为0和1. 给我们一个新的点,我们想要猜测它的标签(这类似于上面的“狗”和“不是狗”场景)。. 我们该怎么做呢?
一种方法可能是查看最近的邻居并返回该点的标签. 但一个稍微聪明一点的方法是选择一条线,最好地将标记数据分开,并将其用作分类器.
在这种情况下,每个输入数据都将表示为一个向量 x = (x_1、x_2),我们的函数应该是这样的:“在线以下为' 0 ',在线以上为' 1 '”.
为了在数学上表示这一点,让我们的分隔符由权重向量定义 w 垂直偏移(或偏置) b. 然后,我们的函数将输入和权重与一个加权和传递函数结合起来:
这个传递函数的结果将被输入一个激活函数以产生一个标记. 在上面的例子中,我们的激活函数是一个阈值截止值(e).g.,如果大于某个值则为1):
感知器的训练包括输入多个训练样本并计算每个样本的输出. 每个样本后,权重 w 以这样的方式调整,以尽量减少 输出误差,定义为 想要的 (目标)和 实际 输出. 还有其他的误差函数,比如 均方误差,但训练的基本原则保持不变.
单感知器深度学习方法有一个主要缺点:它只能学习 线性可分函数. 这个缺点有多严重? 以XOR, 一个相对简单的函数, 注意它不能被线性分隔符分类(注意失败的尝试), 下图):
为了解决这个问题, 我们需要使用多层感知器, 又称前馈神经网络:在效果上, 我们将把这些感知器组合在一起来创建一个更强大的学习机制.
神经网络实际上就是感知器的组合, 以不同的方式连接,操作不同的激活功能.
首先,我们来看看前馈神经网络,它有以下属性:
如果我们的每个感知器只允许使用一个线性激活函数呢? 那么,我们网络的最终输出将 仍然 是输入的线性函数, 只是根据网络中收集的大量不同权重进行了调整. 换句话说,一堆线性函数的线性复合仍然是一个线性函数. 如果我们局限于线性激活函数, 那么前馈神经网络并不比感知器更强大, 不管它有多少层.
正因为如此,大多数神经网络使用非线性激活函数,比如 物流, 双曲正切, 二进制 or 整流器. 没有它们,网络只能学习 输入的线性组合.
最常见的用于多层感知器监督训练的深度学习算法被称为反向传播. 基本程序:
计算输出误差,通常为均方误差:
在哪里 t 目标值是和吗 y 实际的网络输出是多少. 其他误差计算也是可以接受的,但是MSE是一个很好的选择.
使用一个方法来最小化网络错误 随机梯度下降.
梯度下降是通用的, 但在神经网络的例子中, 这是训练误差作为输入参数函数的图. 每个权重的最优值是误差达到a时的值 全球最低. 在Training阶段, 权重以小步骤更新(在每个训练样本或几个样本的小批量之后),这样它们总是试图达到全局最小值,但这不是一件容易的事, 因为你经常会得到局部极小值, 就像右边的那个. 例如,如果权重值为0.6,它需要向0改变.4.
该图表示最简单的情况,其中误差取决于单个参数. 但是,网络错误取决于 每一个 网络权值和误差函数要复杂得多.
值得庆幸的是, 反向传播提供了一种更新两个神经元之间相对于输出误差的权重的方法. 的 推导 本身相当复杂,但给定节点的权重更新具有以下(简单)形式:
在哪里 E 输出错误,和 w_i 输入的权重是多少 i 到神经元.
本质上,我们的目标是在相对于权重的梯度方向上移动 i. 关键词是, 当然, 误差的导数, 这并不总是容易计算的:你如何找到一个大型网络中随机隐藏节点的随机权重的导数?
答案是:通过反向传播. 首先在输出单元中计算误差,其中公式非常简单(基于目标值和预测值之间的差异)。, 然后以一种巧妙的方式通过网络传播回来, 允许我们在训练中有效地更新我们的重量,并(希望)达到最小.
隐藏层是特别有趣的. 由 普遍近似定理, 一个具有有限数量神经元的单个隐藏层网络可以被训练成近似任意随机函数. 换句话说,一个隐藏层就足够强大到可以学习 任何 函数. 也就是说,我们通常在实践中使用多个隐藏层(例如.e.(更深的网).
隐藏层是网络存储训练数据的内部抽象表示的地方, 类似于人类大脑(大大简化的类比)对现实世界的内部表征. 在本教程中,我们将介绍使用隐藏层的不同方法.
你可以看到一个简单的(4-2-3层)前馈神经网络 虹膜 Java实现的数据集 在这里 通过 testMLPSigmoidBP 方法. 该数据集包含三种鸢尾花植物,具有萼片长度、花瓣长度等特征. 该网络每班提供50个样本. 这些特征被固定在输入单元上, 而每个输出单元对应于数据集的一个类:“1/0/0”表示该植物属于Setosa类, “0/1/0”表示Versicolour, “0/0/1”表示弗吉尼亚). 分类误差为2/150 (i.e.,它错误地分类了150个样本中的2个).
在这种情况下,神经网络可以有多个隐藏层, 更高的层在前一层的基础上“构建”新的抽象. 正如我们之前提到的,在实践中,你可以用更大的网络学得更好.
然而,增加隐藏层的数量会导致两个已知的问题:
让我们看看一些深度学习算法来解决这些问题.
大多数介绍性机器学习课程倾向于停止于前馈神经网络. 但可能的网络空间要丰富得多——所以让我们继续.
自编码器通常是一个前馈神经网络,其目的是 学习数据集的压缩、分布式表示(编码).
从概念上讲,神经网络被训练来“重建”输入,即i.e.,输入数据和目标数据是相同的. 换句话说:你试图输出与输入相同的东西,但以某种方式压缩. 这是一种令人困惑的方法,所以让我们看一个例子.
假设训练数据由28x28个灰度图像组成,每个像素的值被固定到一个输入层神经元(i.e.,输入层将有784个神经元). 然后, 输出层将具有与输入层相同数量的单元(784),并且每个输出单元的目标值将是图像的一个像素的灰度值.
这种架构背后的直觉是,网络不会学习训练数据与其标签之间的“映射”, 而是会学习 内部结构 以及数据本身的特征. (正因为如此,隐藏层也被称为 特征检测器.)通常, 隐藏单元的数量小于输入/输出层, 是什么迫使网络只学习最重要的特征并实现降维.
实际上, 我们希望中间的几个小节点能够在概念层面上真正了解数据, 生成一个紧凑的表示,以某种方式捕获我们输入的核心特征.
为了进一步演示自动编码器,让我们再看一个应用程序.
在这种情况下,我们将使用由流感症状组成的简单数据集(归功于此) 博客 为了这个想法). 如果您感兴趣,可以找到这个示例的代码 在 testAEBackpropagation 方法.
数据集是这样分解的:
我们会认为病人生病,当他或她有 至少两个 如果他或她至少具有后三个特征中的两个,那么他或她是健康的(与有利于健康患者的关系打破)。, e.g.:
我们将训练一个有六个输入和六个输出单元的自编码器(使用反向传播),但是 只有两个隐藏单位.
经过几百次迭代, 我们观察到,当每个“病态”样本被呈现给机器学习网络时, 两个隐藏单元中的一个(每个“病态”样本的相同单元)总是表现出比另一个更高的激活值. 相反,当呈现“健康”样本时,另一个隐藏单元具有更高的激活.
实际上,我们的两个隐藏单元 学会了 流感症状数据集的紧凑表示. 为了了解这与学习的关系,我们回到过拟合的问题. 通过训练我们的网络来学习数据的紧凑表示, 我们倾向于更简单的表示,而不是过度拟合训练数据的高度复杂的假设.
在某种程度上, 通过支持这些更简单的表示, 我们试图以更真实的方式了解数据.
下一个合乎逻辑的步骤是看一个 受限玻尔兹曼机 (元) 生成式随机神经网络,可以学习其输入集的概率分布.
rbm由隐藏层、可见层和偏置层组成. 不像前馈网络, 可见层和隐藏层之间的连接是无向的(值可以在可见到隐藏和隐藏到可见的方向上传播)和完全连接的(给定层的每个单元连接到下一层的每个单元-如果我们允许任何层中的任何单元连接到任何其他层, 那么我们就有了玻尔兹曼(而不是 限制了玻耳兹曼机).
标准RBM具有二进制隐藏和可见单位:即在a下,单位激活为0或1 伯努利分布,但也有其他的变种 非线性.
虽然研究人员已经知道rbm有一段时间了, 最近引入的对比散度无监督训练算法重新引起了人们的兴趣.
单步对比散度算法(CD-1)的工作原理如下:
重量更新:
在哪里 a 学习率是和吗 v, v’, h, h’, w 是向量.
该算法背后的直觉是,正相位(h 鉴于 v)反映了网络的内部表征 现实世界中 data. 与此同时, 否定阶段表示尝试基于此内部表示重新创建数据(v’ 鉴于 h). 主要目标是 生成的数据 尽可能靠近 现实世界中 这反映在权重更新公式中.
换句话说, 网络对如何表示输入数据有一些感知, 因此,它试图根据这种感知再现数据. 如果它的复制不够接近现实,它会做出调整并再次尝试.
为了演示对比差异,我们将使用与前面相同的症状数据集. 测试网络是一个包含6个可见单元和2个隐藏单元的RBM. 我们将使用对比发散和症状来训练网络 v 夹紧到可见层. 在测试过程中, the symptoms are again presented to the visible layer; 然后, 数据被传播到隐藏层. 隐藏的单位代表生病/健康状态, 与自动编码器非常相似的架构(将数据从可见层传播到隐藏层).
经过几百次迭代, 我们可以观察到与自动编码器相同的结果:当出现任何“病态”样本时,其中一个隐藏单元具有更高的激活值, 而另一个则总是对“健康”样本更活跃.
您可以看到实际的示例 在 testContrastiveDivergence 方法.
We’ve now demonstrated that the 隐藏的 层 of autoencoders 和 遏制 act as effective 特征检测器s; but it’s rare that we can use these features directly. 事实上,上面的数据集是一个例外而不是规则. 相反,我们需要找到一些方法来间接地使用这些检测到的特征.
幸运的是, 它被发现了 这些结构可以 堆放 形成 深的 网络. 这些网络可以被贪婪地训练,一次一层,以帮助克服 消失的梯度 和 过度拟合 与经典反向传播相关的问题.
由此产生的结构通常非常强大,产生令人印象深刻的结果. 举个例子,b谷歌很有名 “猫”论文 其中他们使用一种特殊的深度自动编码器来“学习”人类和猫的面部检测 无标号 data.
让我们仔细看看.
顾名思义,这个网络由多个堆叠的自编码器组成.
自动编码器的隐藏层 t 作为自动编码器的输入层 t + 1. 第一个自编码器的输入层是整个网络的输入层. 贪婪的分层训练过程是这样的:
堆叠式自动编码器, 然后, 是否都是为了提供一种有效的预训练方法来初始化网络的权重, 留给你一个情结, 多层感知器准备训练(或 调整).
与自动编码器一样,我们也可以堆叠玻尔兹曼机来创建一个称为 深度信念网络(dbn).
在本例中,是RBM的隐藏层 t 作为RBM的可见层 t+1. 第一个RBM的输入层是整个网络的输入层, 贪婪的分层预训练是这样的:
这个过程类似于堆叠式自动编码器, 而是将自编码器替换为rbm,反向传播替换为对比发散算法.
(注:有关构建和训练堆叠自编码器或深度信念网络的更多信息, 请查看示例代码 在这里.)
作为最终的深度学习架构, 让我们来看看卷积网络, 一种特别有趣和特殊的前馈网络,非常适合于图像识别.
在我们看卷积网络的实际结构之前,我们首先定义一个图像 过滤器,或具有相关权重的方形区域. 过滤器应用于整个输入图像,您通常会应用多个过滤器. 例如,您可以对给定的输入图像应用四个6x6过滤器. 然后, 坐标为1的输出像素,1是左上角为1的6x6正方形输入像素的加权和,1和过滤器的权重(也是6x6的正方形). 输出像素2,1是输入左上角为2,1的正方形的结果,以此类推.
在此基础上,这些网络由以下属性定义:
你可以看到几个卷积网络训练的例子(反向传播) MNIST 数据集(手写字母灰度图像) 在这里,特别是在 testLeNet * 我推荐的方法 testLeNetTiny2 因为它在相对较短的时间内达到了2%左右的低错误率。. 还有一个很好的类似网络的JavaScript可视化 在这里.
现在我们已经介绍了最常见的神经网络变体, 我想我应该写一些关于在实现这些深度学习结构过程中所面临的挑战.
一般来说,我的目标是创造一个 深度学习库 过去是(现在仍然是)建立一个基于神经网络的框架,满足以下标准:
为了满足这些需求,我采用了分层(或模块化)的方法来设计软件.
让我们从基础开始:
这种结构足够灵活,可以用于经典的前馈网络,也可以用于 遏制 更复杂的架构,比如 ImageNet.
它还允许一个层成为多个网络的一部分. 例如,a中的层 深度信念网络 在相应的rbm中也有层吗.
除了, 这种体系结构允许在预训练阶段将DBN视为堆叠的rbm列表,在微调阶段将DBN视为前馈网络, 哪一种既直观又编程方便.
下一个模块负责通过网络传播数据,这个过程分为两步:
正如我之前提到的, 神经网络近年来重新兴起的原因之一是它们的训练方法非常有利于并行性, 允许您使用GPGPU显着加快训练速度. 在本例中,我选择使用 Aparapi 库添加GPU支持.
Aparapi对连接计算器施加了一些重要的限制:
因此,大多数数据(权重、输入和输出数组)都存储在 矩阵 实例,它在内部使用一维浮点数组. 所有Aparapi连接计算器都使用其中一种 AparapiWeightedSum (对于完全连接层和加权和输入函数), AparapiSubsampling2D (用于子采样层),或 AparapiConv2D (对于卷积层). 其中一些限制可以通过引入 异构系统架构. Aparapi还允许在CPU和GPU上运行相同的代码.
的 Training 模块实现了各种训练算法. 它依赖于前两个模块. 例如, BackPropagation教练 (所有的教练都在使用 教练 基类)在前馈阶段使用前馈层计算器,并使用一个特殊的宽度优先层计算器来传播误差和更新权重.
我最近的工作是关于Java 8的支持和其他一些改进,将很快合并到 主.
本Java深度学习教程的目的是向您简要介绍深度学习算法领域, 从最基本的组成单元(感知器)开始,并通过各种有效和流行的架构进行进展, 就像受限玻尔兹曼机一样.
的 ideas behind neural 网络 have been around for a long time; but today, 如果你没有听说过深度网络或其他关于深度学习的说法,你就不可能踏入机器学习社区. 炒作不应被误认为是正当理由, 但随着GPGPU计算的进步,以及杰弗里·辛顿等研究人员取得的令人印象深刻的进展, Yoshua Bengio, Yann LeCun和Andrew Ng, 这一领域无疑显示出很大的前景. 没有比现在更好的时间来熟悉和参与.
如果你有兴趣了解更多,我发现以下资源在我的工作中非常有用:
世界级的文章,每周发一次.
世界级的文章,每周发一次.