Softmax層
Softmax関数を計算する層です。出力層でよく用いられる活性化関数の一種ですが、少し特殊なため前回は実装しませんでした。
今まで実装した活性化関数(sigmoid, tanh, ReLU)は、変数を1つ受け取って1つの値を出力する関数でした。対して、Softmax関数は変数を個受け取って、個の値を出力する関数です。Softmax関数への入力を、出力をとしたとき、番目の出力は以下のようになります。
上の式から、Softmax関数の出力の和は1となるため、確率分布を表現する際によく用いられます。Softmax関数は多変数関数なので偏微分をします。をで偏微分すると次式のようになります。
の時、シグモイド関数の微分と全く同じ見た目になりますが、これはSoftmax関数がシグモイド関数を多変数に拡張したものだからです。実際、Sotmax関数の2変数バージョンは、式変形をするとシグモイド関数に一致します。
Softmax層の逆伝播
Softmax層は、逆伝播の際に入力それぞれに勾配を伝えます。ここでは、入力に関する勾配を求めてみます。
Softmax層の出力側から逆伝播してきた勾配をとします。このとき、の値は次式のように表せます。
これをSoftmax関数の微分を用いて式変形していきます。
よって、と求まります。
実装
それではSoftmax層を実装していきます。バッチ処理に対応するため、Softmax層は行列を入力にとり、入力行列の各列に対してSoftmax関数を適用していきます。
using MathNet.Numerics.LinearAlgebra.Single; namespace NeuralNET.Layers.Activation { /// <summary> /// ソフトマックス関数 /// </summary> public class SoftmaxLayer : IActivationLayer { DenseMatrix? output; readonly bool SAVE_OUTPUT_REF; public SoftmaxLayer() : this(false) { } public SoftmaxLayer(bool saveOutputRef) => this.SAVE_OUTPUT_REF = saveOutputRef; public DenseMatrix Forward(DenseMatrix x, DenseMatrix y) { x.ColumnSoftmax(y); SaveOutput(y); return y; } public DenseMatrix Forward(DenseMatrix x) { var y = DenseMatrix.Create(x.RowCount, x.ColumnCount, 0.0f); Forward(x, y); SaveOutput(y); return y; } public DenseMatrix Backward(DenseMatrix dOutput, DenseMatrix res) { if (this.output is null) throw new InvalidOperationException("Backward method must be called after forward."); this.output.PointwiseMultiply(dOutput, res); var colSums = (DenseVector)res.ColumnSums(); dOutput.SubtractRowVector(colSums, res); res.PointwiseMultiply(this.output, res); return res; } public DenseMatrix Backward(DenseMatrix dOutput) { if (this.output is null) throw new InvalidOperationException("Backward method must be called after forward."); var res = DenseMatrix.Create(dOutput.RowCount, dOutput.ColumnCount, 0.0f); return Backward(dOutput, res); } void SaveOutput(DenseMatrix output) { if (this.SAVE_OUTPUT_REF) { this.output = output; return; } this.output = output.CopyToOrClone(this.output); } } }
上のコードでは、いくつかオリジナルの関数を用意しています。まず、DenseMatrix.ColumnSoftmaxメソッドは、DenseMatrixの各列に対してSoftmax関数を適用した行列を返します。
そして、DenseMatrix.SubtractRowVectorメソッドでは、行列と行ベクトルとの引き算を行います。この引き算では、行列の各行から行ベクトルが引かれます。NumPyでいうブロードキャストです。
全てのコードを載せると長すぎるので、詳しくはリポジトリを参照してください。