当前位置: 首页 > 科技观察

像乐高:从头开始解释神经网络的数学过程_0

时间:2023-03-16 10:41:11 科技观察

神经网络是线性模块和非线性模块的巧妙排列。当这些块被智能地选择和连接时,我们得到了一个强大的工具来逼近任何数学函数,例如能够使用非线性决策边界进行分类的神经网络。运行代码的步骤如下:gitclonehttps://github.com/omar-florez/scratch_mlp/pythonscratch_mlp/scratch_mlp.py尽管反向传播具有直观、模块化的特性,负责更新可训练参数,但它是a尚未深入解释的主题。让我们以乐高积木为例,一次添加一块,从头开始搭建一个神经网络,探索其内部功能。神经网络就像乐高积木。上图描述了用于训练神经网络的部分数学过程。我们将在本文中解释这一点。读者可能会感兴趣,神经网络是许多具有不同目标的模块的堆栈。输入变量X将原始数据提供给神经网络,该神经网络存储在一个矩阵中,其中行是观察值,列是维度。权重W_1将输入X映射到第一个隐藏层h_1。权重W_1然后充当线性内核。sigmoid函数防止隐藏层中的数字落在0-1范围之外。结果是一组神经激活,h_1=Sigmoid(WX)。此时,这些操作只是构成了一个一般的线性系统,无法对非线性相互作用进行建模。当我们添加另一层时,这种情况会发生变化,从而增加模块结构的深度。网络越深,我们将学习到越微妙的非线性相互作用,我们就能解决越复杂的问题。也许这就是深度神经模型兴起的原因之一。我为什么要读这个?如果你了解神经网络的内部部分,你可以很快知道遇到问题时首先改变哪里,并可以制定策略来测试你知道的算法的一些不变量和预期的行为。因为调试机器学习模型是一项复杂的任务。根据经验,数学模型不会在第一次尝试时起作用。它们可能对新数据给出低准确度、需要很长时间训练或使用过多内存、返回较大的假阴性值或NAN预测……在某些情况下,了解算法的工作机制可以使我们的任务更容易:如果训练花费太多时间,增加minibatch大小可能是个好主意,这会减少观察的方差,从而帮助算法收敛。如果您看到NAN预测,则该算法可能会收到较大的梯度并耗尽内存。将其视为在多次迭代后爆炸的矩阵乘法。降低学习率会缩小这些值。减少层数会减少乘法次数。剪切梯度也可以显着控制这个问题。具体实例:学习异或函数下面我们打开黑匣子。我们现在要从头开始构建一个学习XOR函数的神经网络。这个非线性函数的选择绝对不是随机的。没有反向传播,很难学会用直线分类。为了说明这一重要概念,请注意下图中一条直线如何无法对XOR函数输出中的0和1进行排序。现实生活中的问题也是非线性可分的。这个网络的拓扑结构非常简单:输入变量X是一个二维向量权重W_1是一个具有随机初始化值的2x3矩阵隐藏层h_1包含3个神经元。每个神经元接收观测值的加权和作为输入,即下图中以绿色突出显示的内积:z_1=[x_1,x_2][w_1,w_2]权重W_2是一个具有随机初始化值h_2的3x2矩阵输出层包含两个神经元,因为XOR函数的输出不是0(y_1=[0,1])就是1(y_2=[1,0])下图更直观:我们现在来训练这个模型。在我们这个简单的例子中,可训练的参数是权重,但应该明白,目前的研究正在探索更多类型的可以优化的参数。示例包括层之间的快捷连接、正则化分布、拓扑、残差、学习率等。反向传播是一种方法,在给定一批带标签的观测值的情况下,在最小化预定义误差度量(即损失函数)的方向(梯度)上更新权重。作为另一种更通用的技术的特例,该算法已多次被重新发现,这种技术称为反向累积模式下的自动微分。网络初始化让我们用随机数初始化网络权重前向步骤:这一步的目标是将输入变量X前向传递到网络的每一层,直到计算出输出层h_2的向量。这就是计算中发生的事情:输入数据X的线性变换,权重W_1作为线性核:使用sigmoid激活函数缩放加权和以获得第一个隐藏层h_1的值。请注意,原始2D向量现在映射到3D空间。类似的过程发生在第2层h_2中。让我们从计算第一个隐藏层的加权和z_2开始,它现在是输入数据。然后计算它们的sigmoid激活函数。向量[0.371665960.45414264]表示网络对给定输入X计算的对数概率或预测向量。计算整体损失也称为“实际值减去预测值”,这个损失函数的目标是量化预测向量h_2和人工标签y之间的距离。请注意,此损失函数包括一个正则化项,该项以岭回归的形式惩罚较大的权重。换句话说,具有大平方值的权重会增加损失函数,这是我们要最小化的指标。反向步骤:这一步的目标是沿着最小化损失函数的方向更新神经网络的权值。正如我们将看到的,这是一种递归算法,它重用了先前计算的梯度并在很大程度上依赖于微分函数。由于这些更新减少了损失函数,神经网络“学习”了近似已知类别观察的标签。这是一个称为泛化的属性。与正向步骤不同,此步骤以相反的顺序执行。它首先计算损失函数对输出层中每个权重的偏导数(dLoss/dW_2),然后计算隐藏层的偏导数(dLoss/dW1)。让我们详细解释每个导数。(1)dLoss/dW_2:链式法则表明我们可以将神经网络的梯度计算分解成许多微分部分:为了帮助记忆,下表列出了上面使用的一些函数定义及其一阶导数:更直观地说,我们想要更新下图中的权重W_2(蓝色部分)。为此,我们需要沿着导数链计算三个偏导数。将值代入这些偏导数,我们可以如下计算W_2的偏导数:结果是一个3x2矩阵dLoss/dW_2,它将在损失函数最小的方向上更新W_2的值。(2)dLoss/dW_1:计算更新第一个隐藏层W_1权重的链式法则,提供了重用现有计算的可能性。更直观地说,从输出层到权重W_1的路径遇到了已经在后面的层中计算出的偏导数。例如,偏导数dLoss/dh_2和dh_2/dz_2已在上一节中计算为输出层dLoss/dW_2的学习权重的依赖项。将所有导数放在一起,我们可以再次执行链式规则来更新隐藏层W_1的权重。最后,我们给权重赋予新的值,完成神经网络训练的一步。实现我们只使用numpy作为线性代数引擎,将上述数学方程式转换为代码。神经网络在循环中进行训练,每次迭代都会为神经网络提供标准输入数据。在这个小例子中,我们在每次迭代中只考虑整个数据集。前向步骤、损失函数和后向步骤的计算泛化得更好,因为我们在每个循环中用它们相应的梯度(矩阵dL_dw1和dL_dw2)更新可训练参数。代码保存在这个repo中:https://github.com/omar-florez/scratch_mlp让我们运行这段代码!您可以在下面看到一些经过多次迭代训练以逼近异或函数的神经网络。左图:准确度;中图:学习决策边界;右图:损失函数首先,让我们看看为什么隐藏层中有3个神经元的神经网络很弱。该模型学习使用开始为直线的简单决策边界执行二元分类,但随后表现出非线性行为。随着训练的继续,右图中的损失函数也明显下降。隐藏层中有50个神经元的神经网络显着提高了模型学习复杂决策边界的能力。这不仅会导致更准确的结果,还会使梯度爆炸,这是训练神经网络时的一个显着问题。当梯度很大时,反向传播中的乘法会产生很大的更新权重。这就是为什么损失函数在训练的最后几步(step>90)突然增加的原因。损失函数的正则化项计算变大的权重的平方值(sum(W2)/2N)。如您所见,可以通过降低学习率来避免这个问题。这可以通过实施随时间降低学习率的策略来实现。或者通过强制更强的正则化,可能是L1或L2。梯度消失和梯度爆炸都是很有意思的现象,后面我们会做完整的分析。原文链接:https://medium.com/towards-artificial-intelligence/one-lego-at-a-time-explaining-the-math-of-how-neural-networks-learn-with-implementation-from-scratch-39144a1cf80【本文为《机器之心》专栏原文翻译,微信公众号“机器之心(id:almosthuman2014)”】点此阅读更多本作者好文