带你读《基于浏览器的深度学习 》之一:深度学习-阿里云开发者社区

开发者社区> 华章出版社> 正文
登录阅读全文

带你读《基于浏览器的深度学习 》之一:深度学习

简介: 本书是Web开发和深度学习的跨界,主要介绍基于浏览器的深度学习技术,具体内容包括神经网络架构、主流的JavaScript深度学习框架、深度学习的JavaScript基础、基于WebGL的GPU加速、浏览器上的数据抽取和操作,以及tensorflow.js实践应用。每章都配有完整的代码示例以及可视化效果,轻松易学。也详细介绍了tensorflow.js重要的模块tfjs-core、tfjs-layers、tfjs-node、tfjs-converter等。

智能系统与技术丛书
点击查看第二章
点击查看第三章
基于浏览器的深度学习
Deep Learning in the Browser

image.png

[ 法 ] 泽维尔·布里(Xavier Bourry) 
[ 美 ] 佐佐木凯(Kai Sasaki)
[ 奥地利 ] 克里斯托夫·科纳( Christoph K.rner)
[ 日 ] 中野礼一郎(Reiichiro Nakano) 著
侠 天 易乐天 译

第1章

深度学习
本章简单介绍深度学习的主要的数学和实际概念以及架构。我们将聚焦监督学习中分类的数学概念。它是从标注的训练集中学习将新数据进行正确分类的过程。一旦你理解了概念,你能将相同的技术应用于其他监督学习任务,比如回归。
从本章开始,我们将介绍神经网络背后的数学知识。读完 1.1 节,你可以通过简单的预训练神经网络完成二分类任务。
接下来的 1.2 节讲解基于标注训练集训练神经网络的技术。然后,你会理解损失函数的重要性、反向传播算法如何工作、优化技术(比如梯度下降算法和牛顿动量势能算法)之间的区别。

1.1 深度神经网络的数学基础

本节介绍深度学习和神经网络背后的数学基础概念。我们主要关注从输入数据样本计算神经网络的输出,这通常称为模型推断。
这里展示的神经网络推断主要包括多个嵌套权重线性函数、非线性激活函数和归一化的组合。在二维世界里,权重线性函数是一个直线方程。激活函数就是,当输入值达到某个阈值时立即激活的函数。我们称它们为门限或者非线性函数,归一化函数主要由减去均值和除以标准差组成。
在简单的分类任务中,直线方程描述的是两个不同分类的元素间的分割直线。如果两个分类是线性可分的,则完美的分类器是分割直线将所有的元素分成两边。代入一个点坐标,直线方程返回一个到该直线的有符号距离的倍数,其中符号代表直线的一边。非线性激活函数将这个有符号距离转换为不同的值。比如,最流行的转换函数返回值的符号(+1或者-1),返回+1或者0,或者只返回正值和0。你将会在后续看到这些激活函数并讨论它们的数学特性。归一化函数将所有的数据点转化成中心点为 0 且标准差为 1 的归一化空间。
正如你所见,这些概念相当直观,在高中课程中就有介绍。我们下面用 n 维空间(代替二维)的操作和嵌套函数构建网络,这会增加一些复杂性。

1.1.1 感知机--------门控线性回归

第一个高级的人工智能和机器学习的数学框架是在 1958 年由心理学家 F. Rosenblatt 提出的。他构建了一个神经脑细胞的简单数学模型--感知机(Perceptron),通过激活和重复模拟生物学习行为。
如图1-1所示,感知机计算所有输入变量 xi 的权重和,接着是通过阶跃函数表示的激活门函数。如果输入的权重之和大于0,则门限打开(输出1)。下面是感知机公式,它是由阶跃函数封装的输入权重之和:

image.png

在这里显示的简单数学操作是在 20 世纪 50 年代提出的,它是现代深度神经网络的基石。此外,我们将解释,感知机数学概念和计算二分类是极其相似的,用多维直线方程作为二分类之间的分割直线。

1.1.1.1 直线方程

先介绍二维直线方程:
image.png
在上面的公式中,k是直线的斜率,d是偏离中心的位移。用变量a、b和c代替k和d(这里,image.png )。你也可能看到如下的直线表示:

image.png

上面的公式有一个很好的特性,当代入一个数据点 P = (p1; p2) 到直线公式,结果的符号代表数据点 P属于直线的哪一边。该函数形成一个训练好的线性分类器,它将数据点分为两个分类,分别位于直线的两边。

image.png

上面结果的绝对值是数据点到直线的最短距离的倍数:image.png。该值也称分类得分。
从上面的例子可以看出和感知机等式非常相似,唯一不同的是激活门函数用的是(sign符号) 函数。想象一下数据点 P 是一个元素的两个维度,直线是二维二分类模型,y返回分类+1或者-1。寻找优化的分割直线将会在1.2节讨论。

1.1.1.2 超平面方程

紧接着,你可以用矩阵表示重写直线方程。这对处理大于二维的情况非常有用:

image.png

该直线方程由权重向量image.png表示。现在这个方程可以用n维权重向量 image.png和输入向量image.png 扩展到n维空间。这个多维直线方程称为超平面(hyperplane)。
注意:在机器学习中,许多问题都是高维度的。计算机视觉是一个例子,其中深度学习模型操作像素值。对于灰度图,输入图形是100×100维,分割超平面将被定义为一个10000维的空间。

1.1.1.3 线性二分类器

下面我们生成一个更通用的门限激活函数 h(x),代替简单的阶跃函数或者符号函数作为门限。线性分类器等式如下,变量 b 表示偏置,代替二维中的变量 c:

image.png

上面的公式是感知机的向量表示,带有更通用的激活函数。因此,你能认为感知机(也叫作神经元(Neuron)) 就像线性多维二分类器,通过线性超平面将数据点分为两类。
下面来看三种不同的有两类数据点的二维数据集。图 1-2显示这些数据集,不同颜色的点代表属于不同的类别。将这些数据分类意味着绘制一个几何图形把所有相同类别的数据点划分在一边,而其他的样本数据点在图形的另外一边。
感知机作为一个线性分类器有极大的局限性:它只能正确地分类线性可分的元素;在二维空间中,分类器为直线。因此,只有左边图形表示的数据集可以用单个直线分开。异或(XOR)数据集只能用多个线性分割组合将它们分开,然后与对角线结合。我们将在下面介绍这种问题。右边图形的数据集需要更复杂的分类器将所有的数据点正确标记。

image.png

1.1.2 多层感知机

感知机最大的缺陷是只能用来进行线性分类。因为感知机只由输入权重之和组成,它只能表示线性可分的函数。如果考虑 XOR 问题(所有的类别不能用单个直线分开),你需要寻找一个更灵活的解决方案,它能组合线性分割以解决更复杂的分类。
下面在一个层中聚合多个神经元,比如每个神经元生成一个输出 yi。将当前层所有的神经元权重和偏置组合为一个权重矩阵image.png和一个偏置向量image.png

image.png

上面的公式image.png经常被称为全连接层(fully-connected layer)。因此,一个全连接层是由神经元的数目定义的。n个神经元计算n个输出,其中每个输出都是感知机公式的结果。
多层感知机背后的思想是堆叠多个全连接层,每个层的输出成为下一个层的输入。
下面是将三个层堆叠在一起的例子:

image.png

多层感知机也称为隐藏层(hidden layer),它是大部分深度学习架构的一个模块。多层堆叠的全连接层称为人工神经网络(Artiflcial Neural Network)。
虽然多个嵌套隐藏层的模型能近似任意函数,但是它们还是比单个全连接层更难训练。事实上,网络的综合误差项并不能用来更新单个层的权重。在 1.2 节,你将学习更多关于反向传播算法的内容,其将综合误差分解成每个层的误差,该方法使得嵌套层的训练变为可能。
推荐你去playground.tensorflow.org 试试不同的输入数据,包括激活函数及神经元和隐藏层的数目。

1.1.3 卷积和池化

当全连接层用于二维图片数据集时,它会有些缺点。首先,模型训练的参数数量(W和image.png中的所有项)相当大。对于向量化的100×100 RGB图片来说,1024个神经元的单个层需要训练1024×100×100×3(W的参数)+1024(image.png的参数)个训练参数。进一步地讲,像素值的局部邻域都是互相独立的,但是在每个神经元中的输入像素值都是相关的。

1.1.3.1 通过卷积共享权重

对于二维图片数据集的有效使用方式是使用较小的二维n×n局部过滤器。对于每个输出像素,计算所有n个输入像素的权重之和,这里的权重是过滤器的参数。滑动过滤器通过图片上的所有可能的位置,对每个局部使用相同的过滤器权重。该过程称为卷积(convolution),在传统计算机视觉应用中 (比如,边界或者块探测) 经常被用作经典的过滤器。
卷积操作由过滤器大小和两个传统的参数(步长(stride)和填充(padding))描述。步长和填充控制过滤器(也称为卷积核(kernel))如何滑动图像局部。步长参数是过滤器从一个局部移动到另外一个局部滑过的像素数目。如果你在一个100×100的输入图片上用1个像素的步长滑动3×3的过滤器,输出的结果图片是98×98的维度(你将会在图片的每个边界丢失一个像素)。如果步长为2,那么输出的维度将会减半。填充参数描述的是增加的像素对应的常数值(经常设为0),如果允许你控制卷积输出的维度,那么会在输入图片的周围增加该值。所以,如果3×3的过滤器,步长为1,填充为1,那么输入图片的维度和输出维度相同。
许多深度学习框架用same和valid代替数值型值。这样处理的原因是:如果依赖过滤器的大小和步长,你必须手动计算正确的填充值来保证输出的维度与输入的维度相同,或者生成一个合法的输出图片。而用 same和valid 选项,会自动计算正确的数值型填充值,这对用户来说更方便。
卷积神经网络(Convolutional Neural Network,CNN)是在二维图片数据集中使用卷积操作的神经网络。相对于ANN将输入图片像素简单地堆叠放入一个向量,CNN的输入是宽度、高度和深度(RGB颜色通道)的三维输入。在大部分神经网络中,一个batch的所有输入图片会堆叠成第四维(batch),然后图片过滤器应用于每个batch的三维图片数据。
一个2D卷积层(也称为 Conv Layer)由一系列 2D 过滤器组成。每个 2D 过滤器在每个输入图片的深度维度上沿着空间维度卷积,然后计算所有深度维度上的权重之和。所以一个2D过滤器创建输入三维数据的一个2D输出图片。沿着深度维度叠加多个2D过滤器,我们创建沿着深度维度的多个输出图片。
由于权重参数共享,卷积层比全连接层更有效,可以利用二维图片数据的局部依赖。在图片数据集中,这些局部邻域由相同形状、对象、纹理等的多个相关的邻域的像素表示。一个卷积层有w×h×d×f+f个参数需要在训练过程中学习获得。w和h参数表示过滤器的宽度和高度,d表示过滤器的深度,f是过滤器的数量。
类似全连接层,卷积层经常紧跟着非线性激活函数。你将会在 1.1.4 节学习到更多关于激活函数的知识。

1.1.3.2 通过池化减少空间维度

神经网络可以只使用卷积层,但是它很难控制输出的空间维度。再加上,卷积层总是包含可训练的参数。池化是参数化的下采样操作,它可以减少输入数据的空间维度。如果你有计算机视觉方面的背景,你会明白在许多图片过滤器(比如,高斯和拉普拉斯图像金字塔SIFT和SURF)中使用该操作。
池化操作不可训练的参数之一是聚合的下采样函数:max 和 avg 是最常用的两种选择。在神经网络中,大部分情况下会选择 max池化,这是因为它使得梯度计算变得简单。对于n×n的池化来讲,这些聚合函数应用于n个输入像素会生成一个输出像素。类似于卷积操作,池化操作也会应用于所有的图片位置,并使用步长和填充参数。

1.1.4 激活函数

非线性激活函数将超平面的权重之和与图像过滤器转换成一个激活信号,所以,如果他们大于 0,会输出值。这些函数在神经网络中占据重要地位,因为它们能堆叠层。如果没有激活函数,嵌套多层感知机将会等效于单个层。因为它只由权重之和组成。
激活函数也是以层的形式组织并嵌入神经网络架构。激活函数经常紧跟卷积层和全连接层使用。在神经网络中,ReLU 和 ELU 激活函数对于阈值型激活函数来说是常用的选择。
有一些激活函数层因其特殊的数学特性,经常选作神经网络的最后一个层。例如, softmax 激活函数,它会返回一个向量,其中向量的所有元素之和为1。该激活函数一般用于创建分类器的输出。
本节将讲解常用的激活函数,以及它们的数学特性。

1.1.4.1 sigmoid 和超正切 tanh 激活函数

在感知机方程中的阶跃函数之后,sigmoid激活函数成为20世纪80年代激活函数的最常用的选择。理由是sigmoid函数平滑地将任意值映射到0~1,并且相对于阶跃函数,sigmoid激活函数连续可导。
sigmoid 激活函数由下面的公式定义 (如图 1-3 所示):

image.png

第二个介绍的是激活函数tanh,其在20世纪80年代被重度使用。它的形状与sigmoid 非常相似,取值范围是-1~1。tanh的数值计算过程更稳定,激活函数的数值稳定性可以避免在训练过程中梯度爆炸。

image.png

tanh 激活函数由以下公式定义 (如图1-4所示):

image.png
image.png

1.1.4.2 ReLU、Leaky ReLU 和 ELU 激活函数

线性整流(Rectifled Linear Unit,ReLU)函数广泛用于深度神经网络的非线性部分。ReLU 激活函数在0的位置不可导,ReLU激活函数极大地提升了神经网络的学习过程,因为输入层在门限触发时,总的梯度可简单地获取。
ReLU激活函数由下面的公式定义 (如图1-5所示):

image.png

ReLU激活函数的一个问题是其负数的激活值都为 0。这个问题看起来很直白,所有的训练样本都会在超平面错误的一边。这会导致每个神经元的0激活,输出为0,损失也为0。当损失为0时,神经元的权重不可调。
带泄漏的线性整流函数(Leaky ReLU或者 Parametric Leaky ReLU)引入一个负的斜率,确保在负值的情况下激活值不等于0。斜率由参数a调整,标准的Leaky ReLU斜率为0.01。如图 1-6 所示。

image.png

ReLU和Leaky ReLU的另一个问题是激活函数在0处不可导。 指数线性单元(Exponential Linear Unit ,ELU)通过一个全可导指数函数近似 Leaky ReLU 激活函数来解决上述问题。如图1-7所示。

image.png

1.1.4.3 softmax 激活函数

softmax激活函数将所有值归一化为0~1之间的取值,并且所有值之和等于1。softmax激活函数由下面的等式定义:

image.png

softmax激活函数主要用在分类网络的最后输出层,因为它将原始的分类得分转换成概率值。输出的结果向量描述的是输入的概率分布。如图1-8所示。

image.png

1.2 深度神经网络的训练

在1.1 节,我们假设有一种方法可以计算神经网络的权重W,使得神经网络表示的直线(或者超平面)能够完美地分割两个(或多个)类别的元素。在本节,你将学习训练一个神经网络的知识,通过使用一系列标记的训练数据集来找到合适的权重W。
假设一系列的训练样本属于某一类,那么你首先需要计算当前的超平面将这些样本分割到不同的类别有多好。好意味着误差 (比如误分类的样本数量,或者超平面分割错误的样本) 最小。度量“好”的函数称为损失函数(loss function),它评估当前分类器是否合适。你会发现损失函数的形状会强烈地影响训练和学习过程。
虽然你有了分类器的综合损失函数,但是你不可能为了提高整体模型的效果而开始调优单个嵌入层的权重,因为每个层将其损失增加到总的损失中。这就是为什么神经网络在 20 世纪 60 年代没有广泛流行起来。为了适当调整模型权重,首先需要计算综合损失中每个嵌入层的贡献值,这个过程称为反向传播(back-propagation)。一旦你有了每个层的损失函数,可以无依赖地调整每个层的权重。反向传播要求你计算每个层的导数,这也是为什么深度神经网络也经常被称为可微分编程(difierentiable programming)。
无依赖的层权重是在逐步迭代的过程中调整。在每个迭代步骤中,层权重沿着梯度的反方向调整,以达到优化损失函数的目的。最常用的优化算法是梯度下降算法(Gradient Descent),也有其他更有效率的技术,比如牛顿动量势能(Nesterov Momentum)算法和Adam 法。

1.2.1 损失函数的重要性

优化或者训练一个模型最重要的部分是损失函数,它用来评估训练模型在给定训练集上的表现好坏。当定义损失函数时要关注该函数的良好数学性质,比如可导性。当损失函数的导数为0时,你可以得到该函数的极值。训练一个神经网络模型的目标就是找到最小化的损失函数值。
在神经网络中,损失函数是维度非常高的函数(经常是百万级别),取决于所有可训练的神经网络参数。
损失函数的选择依赖于具体的任务。带有分类标签的回归和分类模型,考虑预测值和真实值之间的误差,可以从损失函数获得好处。带有类别的分类模型只有 true和false两个值。
在分类任务中,常用的损失函数是交叉熵(Cross-Entropy)损失函数,它用来计算两个概率分布的拟合:真实值的概率分布和预测值的概率分布。交叉熵损失函数经常和 softmax 激活函数一起使用。在回归任务中,常用的损失函数是平方误差的和,也称为均方误差(Mean Squared Error)。

1.2.2 正则化

训练带有几千到百万级参数的神经网络模型的一个常见问题是过拟合。神经网络学习样本的特殊性,却丢失了泛化性。这个问题可以通过对比训练数据集的准确度和测试集的准确度来诊断:神经网络在训练集上预测分类准确度高于在新数据集上的预测准确度。避免过拟合的一个流行的策略是给损失函数增加一个正则项。正则项一般是由每个层的所有可训练参数的L1和L2范式计算得到。
L1正则项惩罚值不为0的参数,使得权重变得稀疏。L2正则项惩罚值大的参数,留下值小的参数。L2权重正则化也称为权重延迟(weight decay),因为在梯度下降过程中每个参数更新前将权重裁剪。
整个训练模型的损失是由分类或者回归的损失函数加上一个正则项损失来提升泛化能力。

1.2.3 反向传播算法

一旦知道了神经网络预测的损失函数,你不能直接用这个损失更新每个层的权重。你首先需要计算每个层的损失贡献总和,然后根据损失的梯度调整层的权重。你可以通过逐步反向迭代计算出每个层的梯度,然后应用导数的链式法则计算每个层的梯度贡献值。这个过程称为反向传播算法。
链式法则形式如下:

image.png

上面等式的意义是,将计算z对x的偏导数转化为计算z对y的偏导数乘以y对x的偏导数。有了这种转化之后,你可以将计算损失函数对于模型输入的偏导数,转化成计算神经网络损失对最后一个层的输出的偏导数,最后一个层的损失对前一个层的输出的偏导数,以此类推。反向传播算法可以无依赖地调整层的权重。

1.2.4 优化方法

你需要一个优化器算法,在每个 epoch 训练时减少分类器的训练损失,调整网络权重,达到高效地训练深度神经网络模型的目的。最经典的常用优化器选择是梯度下降(Gradient Descent ,GD)算法。
对于梯度下降算法,首选,从神经网络的最后一个层的损失开始,采用反向传播算法计算每个层的损失的梯度。该梯度是一个高维度向量,其指向最小损失的反方向。第二步,为权重增加一个梯度的乘积来更新权重。其中梯度乘积对应的因子称为学习率(learning rate),它可以控制损失函数空间迭代的步长。
下面的等式显示了第i个层的权重 Wi采用梯度下降优化算法更新的逻辑,其中α是学习率,Li是第i个层的损失值。

image.png

在深度学习中,我们经常采用mini-batch的梯度下降的算法,该算法同时兼顾了随机梯度下降算法和经典梯度算法的优点:随机梯度算法每次迭代只使用一个样本;经典梯度下降算法迭代一次使用整个样本集。
虽然梯度下降算法简单,但是它也有不少缺点,比如步长固定、局部最优、收敛时长、复杂损失函数的收敛性,等等。因此,有许多基于梯度下降算法开发的更健壮的优化算法,比如Momentum、Nesterov Momentum、Adam、RMSProp等算法。你可以从大部分深度学习框架中的Optimizer下找到上述算法。
提升梯度下降算法收敛性的一种方式是,使用梯度指数级衰减的移动平均来构建速度v进行梯度下降的更新。这种优化算法称为动量算法(Momentum),它的收敛速度比梯度下降快。更新步骤如下:

image.png

如果你知道当前的动量,你可以计算出新位置的梯度 W+v来代替W。Nesterov Momentum是该算法的一种变体。

1.3 本章小结

前面我们介绍了主要的数学知识和实践的概念以及深度学习的架构,从下一章开始我们学习神经网络架构。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享: