解释神经网络如何学习的数学
神经网络是一种组合
上图描述了用于训练神经网络的一些数学。在这篇文章中,我们会理解这一点。读者可能会觉得有趣的是,神经网络是一堆具有不同目的的模块:
- 输入X向原始数据馈送神经网络,原始数据存储在矩阵中,其中观察值为行,维数为列
- 权重W1将输入X映射到第一隐藏层h1。然后权重W1作为线性核函数
- Sigmoid函数防止号码在隐藏层从通过将它们扩展到落入0-1超出范围。结果是一系列神经激活 h1 = Sigmoid(WX)
这些操作只计算一般的线性系统,它不具备模拟非线性交互的能力。当我们再堆叠一个图层时,这就改变了,增加了这个模块结构的深度。网络越深,我们可以学到的更微妙的非线性相互作用以及我们可以解决的更复杂的问题,这可能部分地解释了深度神经模型的兴起。
我为什么要读这个?
如果您理解了一个神经网络的内部部分,那么您将很快知道在不工作的情况下首先要更改什么,并定义一个测试不变量和预期行为的策略,您知道这些行为是算法的一部分。当您想要创建当前未在您正在使用的ML库中实现的新功能时,这也将非常有用。
因为调试机器学习模型是一项复杂的任务。根据经验,数学模型在第一次尝试时并没有达到预期的效果。它们可能会让你对新数据的准确性很低,花费大量的训练时间或内存,返回大量的假阴性或NaN预测,等等。
- 如果训练需要这么长时间,那么增加一个小批的大小以减少观测中的方差,从而帮助算法收敛也许是个好主意
- 如果您观察NaN预测,那么该算法可能已经收到了产生内存溢出的大梯度。把它看作是连续的矩阵乘法,在多次迭代之后会发生爆炸。降低学习速率将会降低这些数值。减少层的数量将减少乘法的数量。剪切梯度将显式地控制这个问题
具体示例:学习XOR函数
为了说明这个重要的概念,请注意下面的直线如何分隔0和1,即XOR函数的输出。现实生活中的问题也是非线性分离的。
网络的拓扑很简单:
- 输入X是一个二维向量
- 权重W1是具有随机初始化值的2x3矩阵
- 隐藏层h1由三个神经元组成。每个神经元接收到一个观测值的加权总和作为输入,这是下图中以绿色突出显示的内部产品:z1 = [x1,x2] [w1,w2]
- 权重W2是一个3x2矩阵,具有随机初始化值和
- 由于XOR函数返回0(y1 = [0,1])或1(y2 = [1,0]),输出层h2由两个神经元组成
更直观地:
在我们的简单示例中,可训练参数是权值,但是请注意,目前的研究正在探索更多类型的参数以进行优化。例如层之间的快捷方式、正则分布、拓扑、剩余、学习率等。
反向传播是一种向方向(梯度)更新权重的方法,该方法可最大限度地减少预先定义的误差度量,称为给定一批标记观察值的损失函数。该算法已被反复重新发现,并且是反向累积模式中称为自动微分的更通用技术的特例。
网络初始化
我们 用随机数初始化网络权重 。
Forward Step:
这一步骤的目标是 将输入X 传播 到网络的每一层,直到在输出层h2中计算矢量。
这是如何发生的:
- 使用权重W1作为核对输入数据X进行线性映射:
- 用Sigmoid函数对该加权和z1进行缩放以获得第一隐藏层h1的值。请注意,原始2D矢量现在映射到3D空间。
- 对于第二层h2发生类似的过程。我们首先计算第一个隐藏层(现在是输入数据)的加权总和 z2。
- 然后计算它们的Sigmoid激活函数。该向量[0.37166596 0.45414264]表示由网络给定输入X计算的对数概率或预测向量。
计算总损失
损失函数的目标是量化预测向量h2与人类y提供的实际标签之间的距离。
请注意,Loss函数包含一个正则化组件,该组件惩罚Ridge回归中的大权重值。换句话说,大的平方权值会增加损失函数,这是一个我们确实想要最小化的误差度量。
Backward step:
这一步的目标是 更新神经网络的权重, 使其损失函数最小化。正如我们将会看到的那样,这是一种 递归算法 ,它可以重用以前计算的梯度,并且严重依赖于可 微函数 。由于这些更新减少了损失函数,网络“学习”以近似已知类别的观测标签。一种称为 泛化 的属性。
这一步的顺序是向后的,而不是向前的。它首先计算损失函数对输出层(dLoss/dW2)的权值的偏导数,然后计算隐藏层(dLoss/dW1)。让我们详细地解释每一个。
dLoss / dW2:
链式规则表明,我们可以将神经网络的梯度计算分解为不同的部分:
这些是上面使用的函数定义及其一阶导数:
函数第一阶导数损失=(y-h2)2dLoss / dW2 = - (y-h2)h2 = Sigmoid(z2)dh2 / dz2 = h2(1-h2)z2 = h1W2dz2 / dW2 = h1z2 = h1W2dz2 / dh1 = W2
更直观地说,我们的目标是更新下图中的权重W2(蓝色)。为此,我们需要计算链中的三个偏导数。
将值插入这些偏导数允许我们根据权重W2计算梯度,如下所示
结果是一个3x2矩阵dLoss / dW2,它将更新原始W2值的方向,使损失函数最小化
dLoss / DW1:
计算用于更新第一隐藏层W1的权重的链规则表现出重新使用现有计算的可能性
更直观地看,从输出层到权重W1的路径接触已在后面的层中计算出的偏导数
例如,已经将偏导数dLoss / dh2和dh2 / dz2计算为用于在前一部分中学习输出层dLoss / dW2的权重的依赖性
将所有的导数放在一起,我们可以再次执行链式法则,更新隐含层W1的权重:
最后,我们分配权重的新值,并完成了对网络训练的迭代
实现
让我们把上面的数学方程转化为只使用Numpy作为线性代数引擎的代码。神经网络是在一个循环中进行训练的,在这个循环中,每个迭代都向网络提供已经校准过的输入数据。在这个小示例中,让我们考虑每个迭代中的整个数据集。由于我们在每个周期中更新了可训练参数(代码中的矩阵w1和w2)及其对应的梯度(矩阵dL_dw1和dL_dw2),因此对Forward step, Loss,和 Backwards step的计算可以得到很好的泛化。代码存储在这个存储库中:https://github.com/omar-florez/scratch_mlp
让我们来运行它!
请参见下面的一些神经网络,这些神经网络训练用于在多次迭代中近似XOR函数。
左情节:准确性。中心图:已学习的决策边界。右图:损失函数。
首先让我们看看隐藏层中具有3个神经元的神经网络是如何具有小容量的。这个模型学习用一个简单的决定边界来分开2个类,这个边界开始是一条直线,然后显示出一个非线性行为。随着培训的继续,右图中的损失函数很低。
在隐藏层中有50个神经元显着提高了模型的能力以学习更复杂的决策边界。这不仅可以产生更准确的结果,而且可以分解梯度,这在训练神经网络时是一个值得注意的问题。这种情况发生在非常大的梯度在反向传播期间乘以权重并因此产生大的更新权重。这就是为什么在训练的最后阶段损失值突然增加的原因(步骤> 90)。损失函数的正则化部分计算已经非常大(总和(W 2)/ 2N)的权重的平方值。
可以通过降低学习率来避免这个问题,如下所示。或通过执行随着时间降低学习速率的策略。或者通过加强正则化,可能是L1而不是L2。