超単純な 3 層ニューラルネットワークを実装

C# でゼロから Deep Learnig を実装する挑戦の続き。 今回は超単純な 3 層ニューラルネットワークを実装してみた。 といっても、『ゼロから作る Deep Learnig』の 3 章の写経みたいなもの。

C# + Math.NET Numerics で試行錯誤しながら書いたコードは次の通り。

using MathNet.Numerics.LinearAlgebra;
using System;

namespace ThreeLayerNeuralNetworkSample
{
    class Program
    {
        static void Main(string[] args)
        {
            var network = new ThreeLayerNeuralNetwork();
            var x = Vector<double>.Build.DenseOfArray(new double[] { 1.0, 0.5 });
            var y = network.Forward(x);
            Console.WriteLine(y);
            Console.ReadLine();
        }
    }

    /// <summary>
    /// 3 層ニューラルネットワークを表します。
    /// </summary>
    public class ThreeLayerNeuralNetwork
    {
        /// <summary>
        /// 第 1 層の重み
        /// </summary>
        readonly Matrix<double> W1;

        /// <summary>
        /// 第 1 層のバイアス
        /// </summary>
        readonly Vector<double> B1;

        /// <summary>
        /// 第 2 層の重み
        /// </summary>
        readonly Matrix<double> W2;

        /// <summary>
        /// 第 2 層のバイアス
        /// </summary>
        readonly Vector<double> B2;

        /// <summary>
        /// 第 3 層の重み
        /// </summary>
        readonly Matrix<double> W3;

        /// <summary>
        /// 第 3 層のバイアス
        /// </summary>
        readonly Vector<double> B3;

        /// <summary>
        /// <see cref="ThreeLayerNeuralNetwork"/>
        /// クラスの新しいインスタンスを初期化します。
        /// </summary>
        public ThreeLayerNeuralNetwork()
        {
            W1 = Matrix<double>.Build.DenseOfArray(new double[,]
            {
                { 0.1, 0.3, 0.5 },
                { 0.2, 0.4, 0.6 }
            });
            B1 = Vector<double>.Build.DenseOfArray(new double[] {
                0.1, 0.2, 0.3
            });
            W2 = Matrix<double>.Build.DenseOfArray(new double[,]
            {
                { 0.1, 0.4 },
                { 0.2, 0.5 },
                { 0.3, 0.6 }
            });
            B2 = Vector<double>.Build.DenseOfArray(new double[] {
                0.1, 0.2
            });
            W3 = Matrix<double>.Build.DenseOfArray(new double[,]
            {
                { 0.1, 0.3 },
                { 0.2, 0.4 }
            });
            B3 = Vector<double>.Build.DenseOfArray(new double[] {
                0.1, 0.2
            });
        }

        public Vector<double> Forward(Vector<double> x)
        {
            // ベクトルと行列の内積は * で計算できる
            // 活性化関数にはシグモイド関数を使う
            var a1 = x * W1 + B1;
            var z1 = Sigmoid(a1);

            var a2 = z1 * W2 + B2;
            var z2 = Sigmoid(a2);

            // 第 3 層では活性化関数ではなく恒等関数を使う
            // つまり、そのまま出力層に渡す
            var a3 = z2 * W3 + B3;
            var y = IdentityFunction(a3);

            return y;
        }

        /// <summary>
        /// シグモイド関数
        /// </summary>
        /// <param name="a">ベクトル</param>
        /// <returns>活性化後のベクトル</returns>
        private Vector<double> Sigmoid(Vector<double> a)
        {
            return a.Map(x => 1 / (1 + Math.Exp(-x)));
        }

        /// <summary>
        /// 恒等関数
        /// </summary>
        /// <param name="a">ベクトル</param>
        /// <returns><paramref name="a"/> をそのまま返します</returns>
        private Vector<double> IdentityFunction(Vector<double> a)
        {
            return a;
        }
    }
}

実行結果がこちら。

『ゼロから作る Deep Learnig』と同じ結果が出力できている。

Vector<T>Matrix<T>演算子オーバーロードしてくれているおかげで、 ベクトルと行列の内積を良い感じで記述できた。 Math.NET Numerics なかなかやるな。