神经网络背后的数学原理:就像用乐高积木搭建起来
在这篇文章中,作者从头开始,一步步解释了用于训练神经网络的数学过程。
神经网络是线性和非线性模块的智能排列。当这些模块被智能地选择和连接时,我们就获得了一个强大的工具来近似任何数学函数,例如可以使用非线性决策边界进行分类的神经网络。
尽管该技术具有直观、模块化的性质,但它负责更新可训练参数,这是一个尚未深入解释的主题。让我们用乐高积木来类比,从头开始构建一个神经网络,一次一块,以探索其内部工作原理。
神经网络就像由乐高积木组成
上图描述了用于训练神经网络的部分数学过程。我们将在本文中对此进行解释。读者可能会觉得有趣的是,神经网络只是许多具有不同目标的模块堆叠在一起。
- 输入变量 X 为神经网络提供原始数据,该数据存储在一个矩阵中,该矩阵的行是观测值,列是维度。
- 权重 W_1 将输入 X 分配给第一个隐藏层 h_1。然后权重W_1充当线性核心。
- sigmoid 函数可防止隐藏层中的数字落在 0-1 范围之外。结果是一系列神经激活,h_1 = Sigmoid(WX)。
此时,这些操作仅形成一般的线性系统,无法模拟非线性相互作用。当我们添加新层以增加模块结构的深度时,这种情况会发生变化。网络越深,我们将学习到的非线性交互越微妙,我们可以解决的问题也越复杂。也许这就是深度神经模型兴起的原因之一。
我为什么要读这篇文章?
如果您了解神经网络的内部结构,您可以在遇到问题时快速知道首先要更改的位置,并可以制定策略来测试您对算法的部分不变量和预期行为的了解。
因为调试机器学习模型是一项复杂的任务。根据经验,数学模型在第一次尝试时不起作用。它们可能会降低新数据的准确度、需要很长时间来训练或使用过多的内存、返回较大的假阴性值或 NAN 预测……在某些情况下,需要了解算法的行为。机制可以使我们的任务变得更容易:
- 如果训练花费太多时间,增加小批量的大小可能是个好主意,这可以减少观察的方差,从而帮助算法收敛。
- 如果您看到 NAN 预测,则算法可能会收到较大的梯度,从而导致内存溢出。将其视为在多次迭代后爆炸的矩阵乘法。降低学习率可以缩小这些值。减少层数可以减少乘法次数。剪切梯度也可以显着控制这个问题。
具体例子:学习XOR函数
让我们打开黑匣子。我们现在将从头开始构建一个学习函数的神经网络。这个非线性函数的选择绝不是随机的。如果没有反向传播,就很难学习使用直线进行分类。
为了说明这个重要概念,请注意下图中的一条直线,这就是 XOR 函数输出中的 0 和 1 无法分类的原因。实际问题也是非线性可分的。
该网络的拓扑结构非常简单:
- 输入变量 每个神经元接受观测值的加权和作为输入,即下图中以绿色突出显示的内积: z_1 = [x_1, x_2][ w_1, w_2]
- 权重 W_2 为 3x2,具有随机初始化值矩阵
- 输出层 h_2 包含两个神经元,因为 XOR 函数的输出要么是 0(y_1=[0,1]) 要么1( y_2 = [1,0])
下图更直观:
现在让我们训练这个模型。在我们的简单示例中,可训练参数是权重,但应该理解的是,当前的研究探索了几种可以优化的参数。例如,层、分布、拓扑、残差、学习率等之间的快捷连接。
反向传播是一种方法,在给定一组标记的观测值的情况下,沿最小化预定义误差测量的方向(梯度)更新权重(即损失函数)。该算法已被多次发现,是另一种更通用的技术(称为反向累积模式自动微分)的特例。
网络初始化
让我们使用随机数来初始化网络权重
前进步骤:
这一步的目标是将输入变量X传递到网络的每一层,直到该层的输出向量计算h_2。
这是发生的计算过程:
使用重量 W_1 作为线性检查数据。请注意,原始 2D 矢量现在映射到 3D 空间中。
类似的过程发生在第 2 层 h_2 中。我们首先计算第一个隐藏层的加权和z_2,它现在是输入数据。
然后计算它们的Sigmoid激活函数。向量 [0.37166596 0.45414264] 表示网络针对给定输入 X 计算的对数似然或预测向量。
计算总损失
也称为“实际值减去预测损失值”,此目标就是量化预测向量h_2和人工标签y之间的距离。
请注意,此损失函数包含一个正则化项,该项以岭回归的形式惩罚较大的权重。换句话说,具有大二次比的权重会增加损失函数,这是我们想要最小化的指标。
反转步骤:
这一步的目标是在最小化损失函数的方向上更新神经网络的权重。正如我们将看到的,这是一种递归算法,它重用先前计算的梯度并依赖于微分函数。由于这些更新减少了损失函数,神经网络“学习”近似已知类别的观察标签。这是一个称为泛化的属性。
与向前的步骤不同,此步骤以相反的顺序继续。它首先计算输出层中每个权重的损失函数的偏导数(dLoss/dW_2),然后计算隐藏层的偏导数(dLoss/dW1)。让我们详细解释每个导数。
dLoss/dW_2:
链式法则表明我们可以将神经网络的梯度计算分解为几个不同的部分:
为了帮助记忆,下表显示了上面使用的一些函数定义及其它们一阶导数:
更直观的来说,我们要更新下图中的权重W_2(蓝色部分)。为此,我们需要计算沿导数链的三个偏导数。
代入这些偏导数中的值,我们可以计算W_2的偏导数,如下:
结果是一个3x2矩阵dLoss/dW_2,它将在损失函数最小化的方向上更新W_2 的值。
dTap/dW_1:
用于更新第一隐藏层W_1权重的链式法则的计算显示了重用现有计算结果的可能性。
更直观地说,从输出层到权重W_1的路径将满足后面各层已经计算出的偏导数。
例如,上一节已计算偏导数 dLoss/dh_2 和 dh_2/dz_2 作为输出层学习权重 dLoss/dW_2 的依赖关系。
将所有导数放在一起,我们可以再次执行链式法则来更新隐藏层中W_1的权重。
最后,我们为权重赋予新的值,完成神经网络的一步训练。
实现
让我们仅使用 numpy 作为线性代数引擎将上述数学方程转换为代码。神经网络在循环中进行训练,其中每次迭代都向神经网络提供标准输入数据。
在这个小例子中,我们在每次迭代中只考虑整个数据集。前向步骤、损失函数和后向步骤的计算将具有更好的泛化性,因为我们在每次迭代中用相应的梯度(矩阵 dL_dw1 和 dL_dw2)更新可训练参数。
代码保存在此存储库中:https://github.com/omar-florez/scratch_mlp
让我们运行此代码!
下面您可以看到一些经过多次迭代训练后可以逼近 XOR 函数的神经网络。
左图:准确度;中图:学习决策边界;右图:损失函数
我们先来看看为什么隐藏层有3个神经元的神经网络很弱。该模型学习使用简单的决策边界来执行二元分类,该决策边界以直线开始,但随后表现出非线性行为。随着训练的继续,右图中的损失函数也显着下降。
隐藏层有 50 个神经元的神经网络显着提高了模型学习复杂决策边界的能力。这不仅会产生更准确的结果,而且还会导致梯度爆炸,这是训练神经网络时的一个重要问题。当梯度很大时,反向传播中的连续相乘会产生很大的更新权值。这就是为什么损失函数在训练的最后一步(步数>90)突然增加。损失函数的正规项计算变得非常大的权重的平方值(sum(W²)/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
From:游动的鱼
文章摘自算法笔记https://github.com/omar-ch_florez/scrat♸
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。