分別使用 Python 和 Math.Net 調用優化算法


1. Rosenbrock 函數

在數學最優化中,Rosenbrock 函數是一個用來測試最優化算法性能的非凸函數,由Howard Harry Rosenbrock 在 1960 年提出 。也稱為 Rosenbrock 山谷或 Rosenbrock 香蕉函數,也簡稱為香蕉函數。
Rosenbrock 函數的定義如下:

f(x)=100(y−x2)2+(1−x)2

Rosenbrock 函數的每個等高線大致呈拋物線形,其全域最小值也位在拋物線形的山谷中(香蕉型山谷)。很容易找到這個山谷,但由於山谷內的值變化不大,要找到全域的最小值相當困難。

這篇文章分別用 Python 和 Math.Net 求Rosenbrock函數的最小值

2. Python

Python 里面的 scipy.optimize 提供了豐富的優化算法,對於 Rosenbrock函數,它的求解代碼如下:

import numpy as np
from scipy.optimize import minimize
def rosenbrock(x):
    return (1 - x[0])**2 + 100 * ((x[1] - x[0] * x[0])**2)
x0 = np.array([1.2, 1.2])
best = minimize(rosenbrock, x0)
print(best)

minimize 有兩個參數,其中 rosenbrock 是要去求得最小值得 objective function;x0 是初始值,有時候初始值對結果影響很大。

上面代碼得輸出如下:

     fun: 3.3496916936926394e-12
hess_inv: array([[0.49944334, 0.99865554],
      [0.99865554, 2.00167338]])
     jac: array([-4.95083209e-05,  2.79682766e-05])
 message: 'Desired error not necessarily achieved due to precision loss.'
    nfev: 159
     nit: 10
    njev: 49
  status: 2
 success: False
       x: array([0.99999874, 0.9999976 ])

即 x(1) 和 y(1) 在接近 (1,1) 的情況下,Rosenbrock 函數有最小值,最小值接近 0。

也可以通過參數 'method='nelder-mead' 指定 minimize 使用 Nelder-Mead 算法,Nelder-Mead 算法是一種求多元函數局部最小值的算法,其優點是不需要函數可導並能較快收斂到局部最小值。使用 Nelder-Mead 算法的輸出結果如下:

final_simplex: (array([[0.999993  , 0.99998474],
      [0.99995096, 0.99990431],
      [1.00003347, 1.00007239]]), array([2.05633807e-10, 2.97215547e-09, 4.09754011e-09]))
          fun: 2.0563380675204333e-10
      message: 'Optimization terminated successfully.'
         nfev: 82
          nit: 43
       status: 0
      success: True
            x: array([0.999993  , 0.99998474])

其它參數的說明請參考 官方文檔

3. Math.Net

Math.Net 是一個開源項目,旨在構建和維護涵蓋基礎數學的工具箱,以滿足 .Net 開發人員的高級需求和日常需求。其中 Math.NET Numerics 旨在為科學、工程和日常使用中的數值計算提供方法和算法。涵蓋的主題包括特殊函數,線性代數,概率模型,隨機數,插值,積分變換等等。

要使用 Math.NET Numerics,首先安裝它的 Nuget 包:

Install-Package MathNet.Numerics

相比 Python,Math.Net 求解 Rosenbrock 函數的代碼復雜些。它先使用 ObjectiveFunction.Value 創建目標函數,然后使用 NelderMeadSimplex 的 FindMinimum 函數求解,代碼如下:

using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.LinearAlgebra.Double;
using MathNet.Numerics.Optimization;
using System;

double Value(Vector<double> input)
{
    return Math.Pow((1 - input[0]), 2) + 100 * Math.Pow((input[1] - input[0] * input[0]), 2);
}
var obj = ObjectiveFunction.Value(Value);
var solver = new NelderMeadSimplex(convergenceTolerance: 0.0000000001, maximumIterations: 1000);
var initialGuess = new DenseVector(new[] { 1.2, 1.2 });

var result = solver.FindMinimum(obj, initialGuess);
Console.WriteLine("Value:\t" + result.FunctionInfoAtMinimum.Value);
Console.WriteLine("Point:\t" + result.MinimizingPoint[0] + " , " + result.MinimizingPoint[1]);
Console.WriteLine("Iterations:\t" + result.Iterations);

輸出如下:

Value:  5.352382362443507E-19
Point:  1.0000000007114838 , 1.0000000014059296
Iterations:     145

雖然 MathNet.Numerics.Optimization 命名空間下還提供了其它類,例如 BfgsBMinimizer 和 NewtonMinimizer,但它們還需要開發者提供梯度函數,這對我來說太復雜了,反而不如 NelderMeadSimplex 好用。

4. 最后

Math.Net 提供了很多多元函數局部最小值的算法,但比起 Python 還是簡化了太多,例如我還搞不清楚 Math.Net 中的優化算法怎么添加約束條件,這方面有機會再研究研究。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM