最近我做了一个家庭作业,我不得不学习一个MNIST 10位数分类的模型。HW有一些脚手架代码,我应该在这个代码的上下文中工作。
我的家庭作业很有效/通过了测试,但现在我尝试从头开始(我自己的nn框架,没有hw脚手架代码),我不得不在后台步骤中应用softmax的宏伟功能,甚至认为hw脚手架代码可能不正确。
hw让我使用他们所说的‘软最大损失’作为nn中的最后一个节点。这意味着,由于某种原因,他们决定将softmax激活与交叉熵损失结合在一起,而不是将softmax作为激活函数和交叉熵作为单独的损失函数。
然后,hw损失函数看起来如下(由我编辑的最低限度):
class SoftmaxLoss:
"""
A batched softmax loss, used for classification problems.
input[0] (the prediction) = np.array of dims batch_size x 10
input[1] (the truth) = np.array of dims batch_size x 10
"""
@staticmethod
def softmax(input):
exp = np.exp(input - np.max(input, axis=1, keepdims=True))
return exp / np.sum(exp, axis=1, keepdims=True)
@staticmethod
def forward(inputs):
softmax = SoftmaxLoss.softmax(inputs[0])
labels = inputs[1]
return np.mean(-np.sum(labels * np.log(softmax), axis=1))
@staticmethod
def backward(inputs, gradient):
softmax = SoftmaxLoss.softmax(inputs[0])
return [
gradient * (softmax - inputs[1]) / inputs[0].shape[0],
gradient * (-np.log(softmax)) / inputs[0].shape[0]
]
正如你所看到的,在前面它做了softmax(x)然后交叉熵损失。
但在后台,它似乎只做交叉熵的导数,而不做softmax的导数。Softmax就是这样留下的。
的输入
假设它应该采用softmax的导数,我不确定这个hw是如何通过测试的.
现在,在我自己从头开始的实现中,我创建了softmax和交叉熵分离的节点,类似于这样(p和t代表预测和真理):
class SoftMax(NetNode):
def __init__(self, x):
ex = np.exp(x.data - np.max(x.data, axis=1, keepdims=True))
super().__init__(ex / np.sum(ex, axis=1, keepdims=True), x)
def _back(self, x):
g = self.data * (np.eye(self.data.shape[0]) - self.data)
x.g += self.g * g
super()._back()
class LCE(NetNode):
def __init__(self, p, t):
super().__init__(
np.mean(-np.sum(t.data * np.log(p.data), axis=1)),
p, t
)
def _back(self, p, t):
p.g += self.g * (p.data - t.data) / t.data.shape[0]
t.g += self.g * -np.log(p.data) / t.data.shape[0]
super()._back()
正如你所看到的,我的交叉熵损失(LCE)和hw中的一样的导数,因为这是损失本身的导数,还没有进入softmax。
但是那样的话,我仍然需要做softmax的导数来用损失的导数来链接它。这就是我被困的地方。
对于softmax,定义为:
衍生产品通常被定义为:
但是,我需要一个导数,它的大小与softmax的输入相同,在本例中是batch_size x 10,所以我不确定如何将上述结果应用于10个组件,因为它意味着对于所有输出(所有组合)或所有输入,我都会有所不同。
以矩阵形式出现。
发布于 2018-04-01 14:28:11
经过进一步的研究,我发现:
因此,对于向量(梯度)形式:
在我的矢量化numpy代码中,简单地是:
self.data * (1. - self.data)
其中,self.data
是输入的softmax,以前是从前传计算的。
https://datascience.stackexchange.com/questions/29735
复制相似问题