C# でゼロから Deep Learning を実装する挑戦はまだ 4 章。 機械学習で使う勾配降下法を実装してみた。 勾配を計算するメソッドは前回記事を流用している。
using MathNet.Numerics.LinearAlgebra; using System; using System.Linq; namespace GradientDescentSample { class Program { static void Main(string[] args) { // f(x0, x1) = x0^2 + x1^2 Func<Vector<double>, double> function2 = (x) => Math.Pow(x[0], 2) + Math.Pow(x[1], 2); var initX = Vector<double>.Build.DenseOfArray(new double[] { -3.0, 4.0 }); var result = GradientDescent( f: function2, initX: initX, learningRate: 0.1, stepNum: 100 ); Console.WriteLine(result); Console.ReadLine(); } /// <summary> /// 勾配降下法を使って最小値を計算します。 /// </summary> /// <param name="f">最適化したい関数</param> /// <param name="initX">初期値</param> /// <param name="learningRate">学習率</param> /// <param name="stepNum">勾配法による繰り返しの数</param> /// <returns>最小値</returns> static Vector<double> GradientDescent( Func<Vector<double>, double> f, Vector<double> initX, double learningRate, int stepNum = 100 ) { var x = initX; foreach (var i in Enumerable.Range(0, stepNum)) { var grad = NumericalGradient(f, x); x = x - learningRate * grad; } return x; } /// <summary> /// 勾配を計算します。 /// </summary> /// <param name="f">関数</param> /// <param name="x">関数に渡す引数</param> /// <returns> /// <paramref name="x"/> の各要素に対しての数値微分。 /// </returns> static Vector<double> NumericalGradient(Func<Vector<double>, double> f, Vector<double> x) { var h = 0.0001; var grad = Vector<double>.Build.Dense(x.Count); foreach (var idx in Enumerable.Range(0, x.Count)) { var tmpVal = x[idx]; // f(x+h) の計算 x[idx] = tmpVal + h; var fxh1 = f(x); // f(x-h) の計算 x[idx] = tmpVal - h; var fxh2 = f(x); grad[idx] = (fxh1 - fxh2) / (2 * h); // 値を元に戻す x[idx] = tmpVal; } return grad; } } }
実行結果。
数式だと難しく見えるが、コードにすると非常にシンプルだ。