F#で逆誤差伝播法(ミニバッチ対応版)
下記記事にて逆誤差伝播法をF#で実装してみました。
F#で逆誤差伝播法 - 何でもプログラミング
1データ/教師データ毎にネットワークを更新していましたが、今回はある程度の数学習してその変位の平均でネットワークを更新する、ミニバッチ法に対応してみたいと思います。
前回のものを流用して、ネットワーク更新の時にAffine層の平均を計算するのでもよいのですが、今回はそもそも入力でMatrix(列方向に複数データが入る)を受け取れるよう実装してみます。
特に記載のないものは、上記記事を参照してみてください。
伝播&逆伝播関数
let forward (input : Matrix<double>) (layer : Layer) : Matrix<double> = match layer with | Affine(weight, bias) -> input * weight |> Matrix.mapRows (fun _ x -> x + bias) | ReLU -> input |> Matrix.map (max 0.0) let forwardAndCreateBackward (rate : double) (input : Matrix<double>) (layer : Layer) : (Matrix<double> -> Layer * Matrix<double>) * Matrix<double> = let output = forward input layer let backward = match layer with | Affine(weight, bias) -> (fun (dy : Matrix<double>) -> let dx = dy * weight.Transpose() let dw = input.Transpose() * dy Affine(weight - rate * dw, bias - rate * (Matrix.sumCols dy)), dx ) | ReLU -> (fun (dy : Matrix<double>) -> let dx = dy |> Matrix.mapi (fun i j dy -> if output.[i, j] = 0.0 then 0.0 else dy) layer, dx ) backward, output
学習関数
let softmaxRows (x : Matrix<double>) : Matrix<double> = x |> Matrix.mapRows (fun _ x -> softmax x) let learn (rate : double) (network : Network) (input : Matrix<double>) (teacher : Matrix<double>) : Network = let backwards, y = network.Layers |> Array.mapFold (forwardAndCreateBackward rate) input let dy = match network.LastLayer with | SoftmaxCrossEntropy -> ((softmaxRows y) - teacher) / (double y.RowCount) let layers, _ = backwards |> Array.rev |> Array.mapFold (|>) dy { network with Layers = layers |> Array.rev }
評価関数
let predict (network : Network) (input : Matrix<double>) : Matrix<double> = let y = network.Layers |> Array.fold forward input match network.LastLayer with | SoftmaxCrossEntropy -> softmaxRows y let accuracy (network : Network) (input : Matrix<double>) (teacher : Matrix<double>) : double = let output = predict network input Seq.map2 (=) (output |> Matrix.toRowSeq |> Seq.map Vector.maxIndex) (teacher |> Matrix.toRowSeq |> Seq.map Vector.maxIndex) |> Seq.averageBy (fun x -> if x then 1.0 else 0.0)
MNISTを学習
10000データ学習ごとの正答率は下記のように推移しました。
[ 0.0947, 0.8723, 0.8893, 0.9092, 0.9154, 0.9154, 0.9183 ]
let shuffle (ary : 'a[]) : 'a[] = let random = System.Random() ary |> Array.sortBy (fun _ -> random.Next()) let trainImages = Mnist.loadImageVectors "train-images.idx3-ubyte" let trainLabels = Mnist.loadLabelVectors "train-labels.idx1-ubyte" let testImages = Mnist.loadImageVectors "t10k-images.idx3-ubyte" |> Matrix.Build.DenseOfRowVectors let testLabels = Mnist.loadLabelVectors "t10k-labels.idx1-ubyte" |> Matrix.Build.DenseOfRowVectors let initialNetwork = { Layers = [| createAffineHe 784 50 ReLU createAffineHe 50 10 |] LastLayer = SoftmaxCrossEntropy } let batchSize = 100 seq { 1..trainImages.Length / batchSize } |> Seq.scan (fun net i -> let indices = [| 0..trainImages.Length - 1 |] |> shuffle |> Array.take batchSize let images = indices |> Array.map (fun i -> trainImages.[i]) |> Matrix.Build.DenseOfRowVectors let labels = indices |> Array.map (fun i -> trainLabels.[i]) |> Matrix.Build.DenseOfRowVectors learn 0.1 net images labels ) initialNetwork |> Seq.iter (fun network -> printf "accuracy %f\n" (accuracy network testImages testLabels))