我正在按照这个教程学习神经网络和反向传播。
我是Python新手,正在尝试将代码转换为MATLAB。有人可以解释一下教程中的以下代码行吗:
delta3[range(num_examples), y] -= 1
简而言之,如果我没记错的话,delta3
和y
是向量,而num_examples
是一个整数。
据我理解,delta3=probs-y
,如这个数学交换条目中所述(感谢@隐藏人名)。为什么以及何时我应该减去1?
否则,有人能指引我到一个可以简单运行和跟随代码的在线网站吗?我在尝试运行的每个地方都遇到了错误(包括我的家用电脑):
“NameError: name ‘sklearn’ is not defined”(可能是我缺少的导入)
回答:
这一行: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。请将第一个链接视为深入探讨,而第二个链接是第一个链接的tl;dr
版本。
softmax损失函数的梯度是输出层的梯度,你需要将其反向传播到输出层之前的层,以便继续进行反向传播算法。
总结我上面链接的帖子,如果你计算训练示例的softmax损失的梯度,对于每个类别,损失的梯度只是该类别的softmax值。你还需要对实际训练示例所属的类别减去1。请记住,示例的某个类别i
的梯度等于p_i - y_i
,其中p_i
是示例对类别i
的softmax分数,而y_i
是使用独热编码方案的分类标签。具体来说,如果i
不是示例的真实类别,则y_i = 0
,如果是,则y_i = 1
。delta3
包含你小批量中每个示例的softmax损失函数的梯度。具体来说,它是一个二维矩阵,其中总行数等于训练示例的数量,或num_examples
,而列数是类别的总数。
首先,我们计算每个训练示例和每个类别的softmax分数。接下来,对于梯度的每一行,我们确定对应于示例所属的真实类别的列位置,并将分数减去1。range(num_examples)
将生成从0
到num_examples - 1
的列表,而y
包含每个示例的真实类别标签。因此,对于range(num_examples)
和y
的每一对,这将访问正确的行和列位置,以减去1来完成损失函数的梯度计算。
现在,在Mathematics Stack Exchange的帖子以及你的理解中,梯度是delta3 = probs - y
。这假设y
是一个独热编码矩阵,意思是y
与probs
大小相同,并且对于y
的每一行,它都是零,除了包含正确类别的列索引,该列设为1。因此,如果你正确地考虑这个问题,如果你生成一个矩阵y
,其中每一行的列都是零,除了示例所属的类别号,它相当于简单地访问每一行的正确列并将分数减去1。
在MATLAB中,你实际上需要创建线性索引来促进这种减法。具体来说,你需要使用sub2ind
将这些行和列位置转换为线性索引,然后我们可以访问梯度矩阵并将值减去1。
因此:
ind = sub2ind(size(delta3), 1 : num_examples, y + 1);delta3(ind) = delta3(ind) - 1;
在你链接的Python教程中,类别标签被假定为从0
到N-1
,其中N
是类别的总数。在MATLAB中,你必须小心,我们从1
开始索引数组,所以我在上面的代码中对y
加了1
,以确保你的标签从1
而不是0
开始。ind
包含我们需要访问的行和列位置的线性索引,我们因此使用这些索引完成减法运算。
如果你使用你编辑中获得的知识来表述这个,你会这样做:
ymatrix = full(sparse(1 : num_examples, y + 1, 1, size(delta3, 1), size(delta3, 2));delta3 = probs - ymatrix;
ymatrix
包含我提到过的矩阵,其中每一行对应一个示例,所有的值都是零,除了与示例所属的类别相关的列,该列是1。你可能之前没有见过sparse
和full
函数。sparse
允许你创建一个零矩阵,你可以指定非零的行和列位置以及这些位置的取值。在这种情况下,我准确地访问每一行的单个元素,并使用示例的类别ID来访问列,并将这些位置设置为1。还要记住,我加了1,因为我假设你的类别ID从0开始。因为这是一个sparse
矩阵,我然后将其转换为full
,以给你一个数字矩阵而不是以sparse
形式表示。因此,这段代码在操作上与我之前展示的代码片段等效。然而,第一种方法更有效,因为你没有创建额外的矩阵来促进梯度计算。你是在原地修改梯度。
作为一个旁注,sklearn
是scikit-learnPython机器学习包,而NameError
是指你没有安装实际的包。要安装它,使用pip
或easy_install
来安装Python包到你的电脑上…所以在你的命令行中,操作非常简单:
pip install sklearn
或者:
easy_install sklearn
然而,运行上述减法代码不需要scikit-learn。你确实需要NumPy,所以请确保你安装了这个包。
对于pip
:
pip install numpy
…对于easy_install
:
easy_install numpy