反向传播的扩展性问题

3

我正在跟随这篇关于神经网络和反向传播的教程

我是 Python 的新手,现在尝试将代码转换成 MATLAB。请问有人能够解释一下以下代码行(来自教程):

delta3[range(num_examples), y] -= 1

简而言之,如果我没错的话,delta3y是向量,num_examples是整数。
据我所知,delta3=probs-y,就像这个数学交换条目中所述(感谢@rayryeng)。什么时候和为什么要减1?
否则,有人能指导我到一个在线网站,我可以简单地运行并跟随代码吗?我在尝试运行时到处都出现错误(包括我的家用电脑):
“NameError:name'sklearn'未定义”(可能是我缺少的导入)

1
什么是 delta3NameError 表示找不到 sklearn,因此您可能没有正确地进行 import - A. Sokol
1个回答

3
这一行代码:delta3[range(num_examples), y] -= 1 是计算softmax损失函数梯度的一部分。我建议您查看这个链接,了解更多关于该损失函数的公式和直觉背后的原理:http://peterroelants.github.io/posts/neural_network_implementation_intermezzo02/
此外,我还建议您看一下Mathematics Stack Exchange上的这篇文章,了解如何导出softmax损失的梯度:https://math.stackexchange.com/questions/945871/derivative-of-softmax-loss-function。第一篇文章可以深入了解,而第二篇则是第一篇文章的简短概括。
softmax损失函数的梯度是输出层的梯度,需要将其向后传播到输出层前的层中,以继续反向传播算法。
总结上面的文章,如果您为一个训练样例计算softmax损失的梯度,则对于每个类别,该损失的梯度就是该类别的softmax值。您还需要将实际训练样例所属类别的损失值减去1。请记住,类别 i 的样例的梯度等于 p_i - y_i ,其中 p_i 是该样例的类别 i 的softmax分数, y_i 使用一位有效编码方案进行分类标签。如果 i 不是样例的真实类别,则 y_i = 0 ,否则为 y_i = 1 delta3 包含您小批量训练中每个样例的softmax损失函数梯度。具体而言,它是一个2D矩阵,其中总行数等于训练样例的数量或,而列数是类的总数。
首先,我们为每个训练样例和每个类别计算softmax值。接下来,对于每个梯度的行,我们确定对应于样本所属的真实类别的列位置,并将分数减去1。 range(num_examples)将生成一个列表,从0num_examples-1,而y包含每个样例的真实类别标签。因此,对于每对range(num_examples)y,这会访问正确的行和列位置,以最终确定损失函数梯度。
现在在数学堆栈交换帖子以及您的理解中,梯度是delta3 = probs - y。这假设y是一个独热编码矩阵,这意味着yprobs具有相同的大小,并且对于y的每一行,除包含正确类别的列索引设置为1外,所有零都为0。因此,如果您正确考虑,如果生成一个矩阵y,其中每行的列都为零,除了该示例所属的类号之外,它等效于仅访问每行的正确列并将得分减1。
在MATLAB中,实际上需要创建线性索引以便进行此减法。具体来说,您需要使用sub2ind将这些行和列位置转换为线性索引,然后我们可以访问梯度矩阵并将值减1。
因此:
ind = sub2ind(size(delta3), 1 : num_examples, y + 1);
delta3(ind) = delta3(ind) - 1;

在您提供的Python教程中,假设类标签从0N-1,其中N是总类数。但在MATLAB中,我们从1开始索引数组,因此我已经在上面的代码中添加了1y,以确保标签从1而不是0开始。 ind包含需要访问的行和列位置的线性索引,因此我们使用这些索引完成减法运算。

如果您要利用您的编辑所获得的知识进行公式化,您应该执行以下操作:

ymatrix = full(sparse(1 : num_examples, y + 1, 1, size(delta3, 1), size(delta3, 2));
delta3 = probs - ymatrix;
ymatrix 包含了我所说的矩阵,其中每一行对应一个示例,所有元素都是零,除了与示例所属类别相关的列,该列的值为1。你可能之前没见过 sparsefull 函数。 sparse 允许你创建一个零矩阵,并且你可以指定非零元素的行和列位置以及这些位置的值。在这种情况下,我正好每行访问一个元素,并使用示例的类别 ID 访问列,并将这些位置的值设置为1。同时请记住,我会加1,因为我假设你的类别 ID 从0开始。由于这是一个 sparse 矩阵,我将其转换为 full,以便用数字矩阵表示,而不是使用 sparse 形式。因此,这段代码的操作与我之前展示的代码片段等效。但是,第一种方法更有效率,因为你不需要创建额外的矩阵来促进梯度计算。你直接在原地修改梯度。
顺带一提,sklearn 是 Python 机器学习包 scikit-learn,而 NameError 是指你没有安装实际的包。要安装它,请使用 pipeasy_install 将 Python 包安装到计算机上...所以在命令行中,只需简单输入:
pip install sklearn

或者:

easy_install sklearn

然而,运行上述减法代码并不需要scikit-learn。但是,您确实需要安装 NumPy 包。对于 pip
pip install numpy

...以及easy_install

easy_install numpy

首先,我认为我很难理解这个问题,因为我既不理解y_i的结构,也不理解减1的原因和情况。在我的实现中(不同于原文),y是一个向量,每个条目都有一个标签。该标签仅包含正确类别的编号。我熟悉您分享的Mathematics Stack Exchange,但我仍然无法理解为什么要减去1,或者当我的y标记方式如此时应该如何操作。 - havakok
顺便说一句,我给了你一个“^”,只是为了感谢你详尽的专业回答。非常感谢。 - havakok
你不理解什么?y是标签向量,它不会改变。我链接的帖子将类别标签转换为一个零向量,长度为N,这个向量的非零位置就是类别标签所在的位置,并将其设置为1。你甚至不需要理解推导过程...你只需要知道梯度就是softmax分数,对于每个示例或每行,你访问与该示例的类别标签相关联的列,并将其减去1。如果还不清楚,请告诉我,但我认为我的回答已经非常清楚易懂了。 - rayryeng
我知道你的困惑所在... 我需要更新我的答案。 - rayryeng
@havakok 请看一下我的编辑。这样你就能更好地理解,你对梯度的理解与我在另一个评论中谈到的内容是等价的。 - rayryeng
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接