神经网络的表示
上图是只有一个隐藏层的神经网络,其中左侧为输入层, 表示一个样本的 3 个特征,中间为隐藏层,表示 4 个隐藏单元,右侧为输出层,表示一个单元。在训练集中,输入层和输出层的数值是可以观察到的,而这些中间层的真实数值是看不到的,所以叫做隐藏层。
输入样本 x 可以用 表示,这里的 a 也表示激活的意思,它意味着网络中不同层的值会传递给后面的层。输入层将 x 的值传递给隐藏层,我们将输入层的激活值,记为 。隐藏层也会产生激活值,记为 。第 层的第 i 个隐藏单元记为 ,图中隐藏层包括 4 个隐藏单元,所以 是一个 4 x 1 维的列向量,如下所示:
最后的输出层会产生一个实数 ,这就是预测值 。当我们计算神经网络的层数时,不将输入层算作在内,通常将输入层称为第 0 层,所以上图是拥有一个隐藏层和一个输出层的双层神经网络。隐藏层和输出层都是带有参数的,这里的隐藏层有两个相关参数 w 和 b,记作。其中 是一个 4 x 3 的矩阵, 是一个 4 x 1 的向量,这里 4 代表隐藏层有 4 个隐藏单元,3 代表输入层有 3 个输入特征,1 代表输出层有 1 个隐藏单元。输出层的参数是 ,其中 是一个 1 x 4 的矩阵, 是一个 1 x 1 的向量,即一个实数,4 代表隐藏层有 4 个隐藏单元,1 代表输出层有 1 个单元。
神经网络输出的计算
上图显示,一个隐藏单元代表了逻辑回归计算的两个步骤:首先计算出 z,然后再计算激活函数值 a。通常一个隐藏层包含多个隐藏单元,需要将特征分别输入到多个不同的隐藏单元进行计算:
首先,第一个隐藏单元的计算过程,如下所示:
第 1 步,计算:,其中 x 包含 3 个特征,分别为 。第 2 步计算:。这和逻辑回归计算类似,接下来,第 2 个隐藏单元也执行相似计算:
直到隐藏层所有单元计算完成,计算过程如下图所示:
其中 是 3 x 1 维的向量,转置后为 1 x 3 维的向量,x 为 3 x 1 的向量, 为一个实数,所以 的结果 是一个实数。如果利用循环,遍历所有隐藏单元进行计算,计算效率非常低下,所以我们应该将 4 个等式向量化。
向量化计算
可以把一个隐藏层的多个单元纵向堆叠起来构成列矩阵,直接计算隐藏层的所有隐藏单元。将第一个隐藏层的 4 个隐藏单元的 w 参数提取出来并转置,构成一个 4 x 3 维的矩阵 ,将第一个隐藏层的 4 个隐藏单元的 b 参数提取出来构成一个 4 x 1 维的向量 ,如下所示:
然后,直接计算得出隐藏层所有隐藏单元的预测值 ,这是一个 4 x 1 的向量,其中第 i 个元素即第 i 个隐藏单元的计算结果 ,而,这和非向量化计算的等式完全一致。计算过程如下所示:
接下来,需要将 sigmoid 函数作用到向量 z 的每个元素,得出每个隐藏单元的激活值,构成了 4 x 1 维的向量 :
概况起来,一个样本在双层神经网络的向量化计算过程为:
其中,隐藏层参数 是一个 4 x 3 维的矩阵,输入样本 x(即 )是一个 3 x 1 维的向量,隐藏层参数 是一个 4 x 1 维的向量, 也是一个 4 x 1 维的向量,进行 sigmoid 激活后的隐藏层结果 也是一个 4 x 1 维的向量。输出层参数 是一个 1 x 4 维的矩阵,参数 是一个实数, 也是一个实数,最后输出结果 也是一个实数。
逻辑回归模型的假设函数为:,可以将输出层参数 看作是逻辑回归模型中的参数 ,将输出层参数 看作逻辑回归模型中的参数 b,将隐藏层的激活值 看作逻辑回归中的输入 x,那么输出层的激活值 即为预测结果 。
以上通过向量化的四个等式,即完成了一个样本在双层神经网络的计算,前两个等式为隐藏层计算,后两个等式为输出层计算。
多个样本的向量化计算
我们知道了单个样本 x 计算预测值 的过程,很容易想到可以利用循环来计算多个样本的预测值。可以用 来表示第 i 个样本在输出层的计算结果,那么 m 个样本的非向量化计算过程如下:
为了提高计算效率,我们需要使用向量化计算。首先需要重新设计输入和参数结构,用 表示第 i 个样本,将输入层的 m 个样本 横向堆叠起来构成 n x m 维的输入矩阵 X,其中 n 为特征数。将隐藏层的 m 个计算结果 横向堆叠起来构成 4 x m 维的矩阵 ,将隐藏层 m 个激活函数计算结果 横向堆叠起来构成 4 x m 维的矩阵 ,这里的 4 代表第一个隐藏层的单元数。同理,矩阵 Z 和矩阵 A 的横向长度对应训练集样本数,纵向长度对应神经网络的中隐藏层的单元数。
多个样本的向量化计算公式为:
计算过程如下:
将第 i 层的单元数记为 ,将训练样本 x 的特征数记为 。第一个隐藏层参数 为 的矩阵,X 是 m 个样本横向堆叠构成的 的矩阵, 为 的向量, 为 的向量。所以在第 1 个隐藏层中,第 i 个样本的计算结果为 ,是一个的向量。第 1 个隐藏层 m 个样本的计算结果 为 的矩阵,后面 同理。
激活函数
在神经网络中,我们可以选择隐藏层和输出层采用哪种激活函数,通常用 表示第 i 层的激活函数。
sigmoid 激活函数,可以将预测值限制在 0-1 之间,作为预测分类概率,适合用于二元分类的输出层。
tanh 函数也叫双曲正切函数,在数学中实际上是 sigmoid 函数平移后的版本。激活函数使用 tanh 几乎总比 sigmoid 表现好。因为 tanh 函数输出值介于 -1 到 1 之间,输出的平均值更接近 0,有数据中心化的效果,而 sigmoid 函数输出值则更接近 0.5。除了在输出层,tanh 激活函数是比 sigmoid 激活函数更好的选择。
tanh 函数和 sigmoid 函数都有一个缺点,当 z 非常大或非常小时,导数的梯度(斜率)会非常小,甚至接近 0,这将使梯度下降变得非常缓慢。
ReLU 是最常用的激活函数,叫做修正线性单元。根据公式,当 z > 0,导数为 1;当 z < 0,导数为 0;当 z = 0 时,导数未定义,但 z = 0 的概率很低,实际开发中,可以在 z = 0 时给导数设为 0 或 1。ReLU 激活函数的优点是,当 z > 0,一般来说激活函数的导数和 0 差很远,所以不会出现由于导数接近 0 而减慢学习速度的效应,这会使得梯度下降的速度快很多。ReLU 激活函数的缺点是,当 z 为负数,导数为 0,不过这也没什么太大问题。
ReLU还有一个版本叫做带泄漏的 ReLU,当 z < 0,导数不再为 0,而是有一个很平缓的斜率,这通常比 ReLU 激活函数更好,不过实际中使用的频率没有那么高,还是使用 ReLU 激活函数的更多。
可以通过交叉验证集分别进行测试,从而选择效果最好的激活函数。
为什么需要非线性激活函数
如上图所示,我们尝试在神经网络中将激活函数去掉,在计算过程中,令 ,那么:
将 带入到 中:
可以发现,x 的权重参数由 变为 ,记作 ,偏置参数由 变为 ,记作 。事实证明,如果使用线性激活函数或者不用激活函数,模型的输出 不过是把输入线性组合后再输出。所以不论神经网络包括多少层,线性激活函数都不会起到作用,因为两个线性函数组合还是一个线性函数,和去掉所有隐藏层没有本质区别。
如图所示,如果中间隐藏层为线性激活函数,这和没有任何隐藏层的标准逻辑回归是一样的,因为线性隐藏层没有任何作用。当然,如果要学习的是回归问题,可以在输出层使用线性激活函数,因为输出的预测值的取值范围是负无穷到正无穷,如果预测值一定大于 0 ,也可以在输出层使用 ReLU 激活函数,但解决分类问题时,隐藏单元还是不能用线性激活函数。
激活函数的导数
激活函数的导数可以表示为 ,也可以表示为 g'(z),在微积分中这个符号 ' 叫做 prime,表示函数 g 对输入变量 z 的导数。
sigmoid 激活函数的导数:
z 越大,g(z) 越接近 1,所以 越接近0;
z 越小,g(z) 越接近 0,所以 越接近0;
z = 0,g(z) = 0.5,所以 。
g'(z) = a(1 - a),计算出 a 值后,可以快速计算出 g’(z)。
tanh 激活函数的导数:
z 越大,g(z) 越接近 1,所以 越接近 0;
z 越小,g(z) 越接近 -1,所以 越接近 0;
z = 0,g(z) = 0,所以 。
如果 a = g(z), g(z) = tanh(z),那么 ,计算出 a 值后,即可快速计算出 g'(z)。
ReLU 激活函数的导数:
实际情况中,我们可以令 g'(0) = 1 或 g'(0) = 0,g'(0) 就是激活函数 g(z) 的次梯度,梯度下降仍然有效,但 z = 0 的概率非常小,所以 g'(0) 的值实际上并不重要。
带泄漏的 ReLU 激活函数的导数:
同样,可以令 g'(0) = 1 或 g'(0) = 0。
神经网络的梯度下降
图中双层神经网络中的参数为:,通常 (即 ) 表示输入层样本的特征数, 表示隐藏层的单元数, 表示输出层单元数。
神经网络的代价函数为:
表示损失函数,这里和标准逻辑回归的损失函数一样,即 。
通常梯度下降分为三个步骤:前向传播、反向传播以及迭代更新参数。首先通过前向传播计算每个样本的预测值 ,向量化计算公式为:
其中激活函数 g(z) 为 sigmoid 激活函数, 是 维的矩阵,X 是 m 个样本横向堆叠构成的 维的矩阵(), 是一个 维的向量,所以 是 维的矩阵。 是 维的矩阵, 是 维的矩阵, 是 维的向量,因为这里 = 1,所以 是 1 x m 维的向量, 就是 m 个样本预测值横向堆叠起来形成的 1 x m 维的向量,即 。
接下来,再通过反向传播计算各个参数的导数:,向量化计算公式为:
其中,Y 是 m 个样本真实值横向堆叠起来形成的 1 x m 维的向量,, 是 1 x m 维的向量, 是 维的矩阵,所以 是 维的矩阵, 是 1 x 1 维的矩阵,即一个实数, 是 维的矩阵,所以 是 维的矩阵,所以 是 维的矩阵,又 是 维的矩阵,所以 是 维的矩阵, 是 维的矩阵。这里利用 np.sum()
函数 axis = 1
进行水平相加求和,keepdims = True
是为防止 Python 输出秩为 1 的数组,确保输出的是矩阵。
最后根据梯度下降公式,更新参数:
至此,已经完成了一次梯度下降迭代,需要不断重复迭代,更新参数,才能将代价函数收敛到最小值。
反向传播的计算
逻辑回归模型中,通常根据前向传播计算流程,利用反向传播来分步计算导数,先计算 da,接着计算 dz,最后计算 dw 和 db。
损失函数为:
首先计算 da:
接着计算 dz:,又 ,,,所以
最后计算 dw 和 db:
以上就是逻辑回归模型中单个样本的反向传播计算过程,这和双层神经网络很像,如下所示:
前向传播的计算过程是:首先计算 ,然后计算 ,然后计算 , 也取决于参数 和 ,然后从出发,计算,最后得到损失函数。
反向传播的计算过程是:向后推算出 ,然后是 ,然后计算 和 ,然后反向计算 ,,最后求出 和 。
我们可以跳过显式计算 ,直接计算 ,将两步计算合并成一步,直接得到 。,和逻辑回归导数计算中 类似,只不过这里的 x 变为 ,还有一个额外的转置运算,因为逻辑回归中 w 是行向量,而神经网络中 W 是列向量。。
接下来需要计算 ,然后计算。这里也可以将两步计算合并成一步,直接得到 。
最后 ,, 相当于 。
我们用 表示输入特征数, 表示隐藏层单元数, 表示输出层单元数。那么, 是 维的矩阵, 是 维的向量,当做二元分类时, = 1,就是 1 x 1 维,即一个实数。 和 是 维的向量,可以发现变量和变量导数的维度是一样的。整个计算过程,如下所示:
反向传播的向量化计算
在前向传播的向量化计算中,需要计算:,,我们把 z 横向堆叠起来形成行向量:,从而得出如下向量化计算公式:
反向传播也是如此,下图中,左侧为单个样本反向传播的计算过程,右侧为多个样本反向传播的向量化计算过程。
首先计算 ,,这里 是由于代价函数是 m 个样本的平均损失,即 ,接下来计算:
其中 是 维矩阵,后面两项也是,两项逐个元素乘积。
最后计算:
随机初始化权重
逻辑回归中,我们可以将参数全部初始化为 0,但在神经网络中将参数全部初始化为 0 会导致梯度下降失效。
假设输入层有两个输入特征,即 ,隐藏层有两个单元,即 。那么和隐层相关的参数 就是一个 2 x 2 的矩阵,假设将其全部初始化为 0,将 也初始化为 0(实际上将偏置项 b 初始化为 0 是可行的,但将 w 初始化为 0 就有问题了),因为对于输入的任何样本, 和 是一样的。也就是说这两个激活函数完全一样,因为两个隐藏单元都在做完全一样的计算,当计算反向传播时,事实证明,出于对称性, 和 也是相同的,那么两个隐藏单元完全相同,这就是所谓的完全对称,我们通过归纳法证明,每次训练迭代之后,两个隐藏单元仍然在计算完全相同的函数,两个隐藏单元对输出单元的影响也是一样的,在一次迭代之后同样的对称性依然存在,两个隐藏单元仍然是对称的。无论尝试迭代多少次,都还是对称的。所以在这种情况下多个隐藏单元没有意义。当然对于更大的神经网络或者输入有 3 个特征,或者隐藏单元的数目非常多,如果把所有权重的初始化为 0,那所有隐藏单元都是对称的,不管运行多久梯度下降,它们的计算完全一样,所以没有任何意义。因为我们需要不同的隐藏单元去计算不同的函数。解决方案是随机初始化所有参数。
我们通常把权重初始化为非常小的随机值。因为如果使用 tanh 或者 sigmoid 激活函数,权重太大的话,当计算激活函数值时,很容易得出非常大或非常小的值,那么函数曲线会非常平缓,梯度的斜率非常小,梯度下降会变得非常慢。如果是深度神经网络,应该用0.001甚至更小。但参数 b 是没有对称性问题的。