私は最近、MNISTの10桁分類のモデルを学習する必要がある宿題をしました。HWにはいくつかの足場コードがあり、私はこのコードのコンテキストで作業することになっていた。
私の宿題は機能します/テストに合格しましたが、今はすべてをゼロから実行しようとしています(自分のnnフレームワーク、ハードウェアスキャフォールディングコードなし)。バックプロップステップでソフトマックスの壮大さを適用して立ち往生しています。足場コードは正しくない可能性があります。
ハードウェアでは、nnの最後のノードとして、「softmax loss」と呼ばれるものを使用します。つまり、何らかの理由で、ソフトマックスをアクティベーション関数として扱い、クロスエントロピーを個別の損失関数として扱うのではなく、クロスエントロピー損失と一緒にソフトマックスのアクティベーションに参加することにしました。
hw loss funcは次のようになります(最低限私が編集します)。
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の導関数も必要ではありませんか?
それが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)はハードウェアのそれと同じ導関数を持っています。これは、損失自体の導関数であるため、まだソフトマックスに入らないためです。
しかし、それでも、私はそれを損失の導関数とつなげるために、ソフトマックスの導関数を実行する必要があります。これは私が行き詰まるところです。
次のように定義されたsoftmaxの場合:
導関数は通常、次のように定義されます。
しかし、私はsoftmaxへの入力と同じサイズのテンソルをもたらす導関数が必要です。この場合、batch_size x 10です。したがって、10のコンポーネントだけに上記を適用する必要があるかどうかはわかりません。すべての出力(すべての組み合わせ)に関して、または行列形式で、すべての入力を区別します。