C#で初めてのディープラーニング～Pythonでの実装をまねする～

#C#でディープラーニング（マトリクスの計算）

今回は、ディープラーニングに必要なマトリックスの計算をするプログラムだけ作成しました。
参考にした書籍は「ゼロから作る　Deep Learning(オライリー)」です。
この本は、とてもわかりやすく、いい本だと思いますので、おすすめです。
漫画じゃないのに、止まらない・・・それくらい、興味を引かれました。
まだ、半分くらいしか読んでいないので、よく分からない部分も多いですが、今回は、マトリックスの計算プログラムをC#で作りました。

##Pythonじゃなくて、C#？

この本は、理論的なことも解説していますので、Pythonのライブラリーに頼らず、C#で**ゼロから**作りたいと思いました。
後々、F#や、Haskellでも実装してみようかと思います。

##なぜディープラーニングを始めたか？

F#や、Haskellなど関数型言語も最近勉強し始めて、わざわざ関数型を使う意味が未だに理解できず、
「そういえば、関数型言語って数学的な考えに近いってきいたことあるなぁー」と、・・・
ならば、ディープラーニングを！（動機は関数型言語の使い道が分からなかったから。）
今回はとりあえず、C#でやりました。

##C#のソースコード

```Matrix.cs
namespace Matrix
{
    class Mat
    {
        private int r = 0;
        public int R
        {
            get { return r; }
        }
        private int c = 0;
        public int C
        {
            get { return c; }
        }
        private bool err = false;
        public bool Err
        {
            get { return err; }
        }
        private double[][] matrix_data;
        public double[][] Matrix_data
        {
            get {
                double[][] a = new double[2][];
                a[0] = new double[] { 0, 0 };
                a[1] = new double[] { 0, 0 };
                if (err) return a;
                else return matrix_data;
            }
            set
            {
                matrix_data = value;
            }
        }
        public Mat(params double[][] vs)
        {
            int len = vs[0].Length;

            for (int i = 0; i < vs.Length; i++)
            {
                if (i != 0 && len != vs[i].Length)
                {
                    err = true;
                }
            }
            if (!err)
            {
                r = vs.Length;
                c = vs[0].Length;
                matrix_data = vs;
            }
        }
        public static double[][] operator +(Mat p1, Mat p2)
        {
            double[][] d = new double[p1.R][];

            if (p1.C == p2.C && p1.R == p2.R)
            {
                for (int i = 0; i < p1.R; i++)
                {
                    d[i] = new double[p1.C];
                    for (int j = 0; j < p1.C; j++)
                    {
                        d[i][j] = p1.Matrix_data[i][j] + p2.Matrix_data[i][j];
                    }
                }
            }
            else
            {
                for (int k = 0; k < p1.R; k++)
                {
                    d[k] = new double[2] { 0, 0 };
                }
            }

            return d;
        }
        public static double[][] operator +(double p1, Mat p2)
        {
            double[][] d = new double[p2.R][];
            for (int i = 0; i < p2.R; i++)
            {
                d[i] = new double[p2.C];
                for (int j = 0; j < p2.C; j++)
                {
                    d[i][j] = p2.Matrix_data[i][j] + p1;
                }
            }

            return d;
        }
        public static double[][] operator +(Mat p1, double p2)
        {
            double[][] d = new double[p1.R][];
            for (int i = 0; i < p1.R; i++)
            {
                d[i] = new double[p1.C];
                for (int j = 0; j < p1.C; j++)
                {
                    d[i][j] = p1.Matrix_data[i][j] + p2;
                }
            }

            return d;
        }
        public static double[][] operator -(Mat p1, Mat p2)
        {
            double[][] d = new double[p1.R][];

            if (p1.C == p2.C && p1.R == p2.R)
            {
                for (int i = 0; i < p1.R; i++)
                {
                    d[i] = new double[p1.C];
                    for (int j = 0; j < p1.C; j++)
                    {
                        d[i][j] = p1.Matrix_data[i][j] - p2.Matrix_data[i][j];
                    }
                }
            }
            else
            {
                for (int k = 0; k < p1.R; k++)
                {
                    d[k] = new double[2] { 0, 0 };
                }
            }

            return d;
        }
        public static double[][] operator -(double p1, Mat p2)
        {
            double[][] d = new double[p2.R][];
            for (int i = 0; i < p2.R; i++)
            {
                d[i] = new double[p2.C];
                for (int j = 0; j < p2.C; j++)
                {
                    d[i][j] = p1 - p2.Matrix_data[i][j];
                }
            }

            return d;
        }
        public static double[][] operator -(Mat p1, double p2)
        {
            double[][] d = new double[p1.R][];
            for (int i = 0; i < p1.R; i++)
            {
                d[i] = new double[p1.C];
                for (int j = 0; j < p1.C; j++)
                {
                    d[i][j] = p1.Matrix_data[i][j] - p2;
                }
            }

            return d;
        }
        public static double[][] dot(Mat p1, Mat p2)
        {
            double[][] d = new double[p1.R][];
            double temp = 0;

            if (p1.C == p2.R)
            {
                for (int i = 0; i < p1.R; i++)
                {
                    d[i] = new double[p2.C];
                    for (int j = 0; j < p2.C; j++)
                    {
                        for(int a = 0; a < p1.C; a++)
                        {
                            temp = temp + p1.Matrix_data[i][a] * p2.Matrix_data[a][j];
                        }
                        d[i][j] = temp;
                        temp = 0.0;
                    }
                }
            }
            else
            {
                for (int k = 0; k < p1.R; k++)
                {
                    d[k] = new double[2] { 0, 0 };
                }
            }

            return d;
        }
    }
}
```

クラス`Mat`は、マトリックスを計算するクラスです。
使用例は以下の通りです。

```Program.cs
namespace Matrix
{
    class Program
    {
        static void Main(string[] args)
        {
            Mat A = new Mat(
                new double[] { 1, 2, 3 },
                new double[] { 2, 3, 4 }) ,
                B = new Mat(
                new double[] { 1, 2 },
                new double[] { 2, 3 },
                new double[] { 3, 4 });

            double[][] ans = Mat.dot(A, B);

            _ = 1;    //ブレークポイントを置くためのダミープログラム

        }
    }
}
```

コンソールに出力するのが面倒だったので、`_ = 1;`の行にブレークポイントを置いて配列`ans`の中身を見て確認しました。

![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590410/2fc52ae7-36f2-7a19-be3f-7c61c464bf0f.png)

ちゃんと計算されています。
`Mat`で今回はドット積を計算していますが、足し算、引き算もできます。

＊２次元配列で表現されていないのは、ディープラーニングの入力など１次元配列になる可能性があったため。
コンストラクタで、オーバーロードすることも考えましたが、力不足でうまくできませんでした。