旅行好きなソフトエンジニアの備忘録

プログラミングや技術関連のメモを始めました

【C#】 最急降下法による一変数関数の最適化

最急降下法を利用して一変数関数の最適化を行うクラスを実装します。最急降下法ではここ(【C#】 微分を計算するクラス - 旅行好きなソフトエンジニアの備忘録)で作成したDerivativeというクラスを利用します。

public class SteepestDescentMethod1V
{
    private readonly Func<double, double> _f;
    private double _xn;
    private readonly double _learningRate;

    /// <summary>
    /// コンストラクタ
    /// </summary>
    /// <param name="f">一変数関数</param>
    /// <param name="initialX">初期値</param>
    /// <param name="learningRate">学習係数</param>
    public SteepestDescentMethod1V(Func<double, double> f, double initialX, double learningRate)
    {
        if (learningRate <= 0.0)
        {
            throw new ArgumentOutOfRangeException("learningRate", "learningRate must be positive");
        }

        _f = f;
        _xn = initialX;
        _learningRate = learningRate;
    }

    /// <summary>
    /// 現在の解を取得するためのプロパティ
    /// </summary>
    public double Xn
    {
        get { return _xn; }
    }

    /// <summary>
    /// 最急降下法を利用して一変数関数の最小値を与える解を探索するメソッド
    /// </summary>
    /// <param name="f">一変数関数</param>
    /// <param name="initialX">初期値</param>
    /// <param name="iteration">計算回数</param>
    /// <param name="learningRate">学習係数</param>
    /// <returns>関数の最小値を与える解</returns>
    public static double Compute(Func<double, double> f, double initialX, int iteration, double learningRate)
    {
        ValidateArguments(iteration, learningRate);

        double xn = initialX;

        for (int i = 0; i < iteration; ++i)
        {
            double fdx = Derivative.Compute(f, xn);
            xn -= learningRate*fdx;
        }

        return xn;
    }

    private static void ValidateArguments(int iteration, double learningRate)
    {
        if (iteration <= 0)
        {
            throw new ArgumentOutOfRangeException("iteration", "iteration must be positive");
        }

        if (learningRate <= 0.0)
        {
            throw new ArgumentOutOfRangeException("learningRate", "learningRate must be positive");
        }
    }

    /// <summary>
    /// 現在の解を更新するメソッド
    /// </summary>
    public void Update()
    {
        double fdx = Derivative.Compute(_f, _xn);
        _xn -= _learningRate*fdx;
    }
}

次にSteepestDescentMethod1Vクラスを利用して、f(x)=x^2-4x+4の最小値を与えるxを求めてみます。

Func<double, double> f = x => x*x - 4.0*x + 4.0;
double initialX = 0.0;
int iteration = 100;
double learningRate = 0.1;
double answer = SteepestDescentMethod1V.Compute(f, initialX, iteration, learningRate);

answerには2に限りなく近い値が入っており、正しく計算できていることを確認できます。更新時のxの遷移を様子を確認したい場合はstaticメソッドではなくSteepestDescentMethod1Vクラスをインスタンス化して下記のように利用します。

Func<double, double> f = x => x*x - 4.0*x + 4.0;
double initialX = 0.0;
double learningRate = 0.1;
var optimizer = new SteepestDescentMethod1V(f, initialX, learningRate);
int iteration = 100;
var xHistory = new List<double>();
for (int i = 0; i < iteration; ++i)
{
    optimizer.Update();
    xHistory.Add(optimizer.Xn);
}

上記コードでxHistoryの中身を調べることでxの遷移の様子が分かります。

メタヒューリスティクスとナチュラルコンピューティング

メタヒューリスティクスとナチュラルコンピューティング