18

プログラムの目的: 統合。高次元 (最大 100) の適応求積 (別名数値積分) アルゴリズムを実装しています。アイデアは、そのポイントでのエラーの推定値に比例するサンプリング密度を使用してポイントを評価することにより、ボリュームをランダムに小さなセクションに分割することです。早い段階で、均一なサンプルを「バーンイン」してから、推定誤差に対するガウス分布に従ってポイントをランダムに選択します。シミュレートされたアニーリングと同様の方法で、「温度を下げ」、時間の経過とともにガウス分布の標準偏差を減らします。これにより、最初はエラーの少ないポイントが選択される可能性がかなり高くなりますが、後で着実に減少して選択されます。確率。これにより、プログラムは、エラー関数の不完全性のために見逃される可能性のあるスパイクに遭遇することができます。(私のアルゴリズムは精神的に似ていますマルコフ連鎖モンテカルロ積分.)

機能特性。統合する機能は、自然災害による複数の建物の保険損害の推定です。ポリシー機能はスムーズではありません: 免責額、上限、レイヤー (たとえば、100 万ドルの損失まではゼロの支払い、100 万ドルから 200 万ドルまでは 100% の支払い、200 万ドルを超えるとゼロの支払い)、およびその他の奇妙なポリシー条件があります。これにより、多くの平面で導関数を持たない非線形の動作と関数が導入されます。ポリシー関数に加えて被害関数があります。これは、建物の種類とハリケーンの強さによって異なり、決して釣鐘型ではありません。

問題のコンテキスト: エラー関数。難しいのは、適切な誤差関数を選択することです。各ポイントについて、これに役立つと思われる測定値を記録します。関数の大きさ、以前の測定の結果としてどれだけ変化したか (一次導関数のプロキシ)、ポイントが占める領域の体積 (より大きな体積はエラーをよりよく隠す)、および領域の形状に関連する幾何学的要因。私の誤差関数は、各測定値に異なる重みが割り当てられているこれらの測定値の線形結合になります。(悪い結果が得られた場合は、非線形関数を検討します。) この取り組みを支援するために、各重みの可能な値の広い範囲にわたって最適化を実行することにしました。これが Microsoft Solution Foundation です。

最適化するもの: エラー ランク。私の測定値は、0 から 1 まで正規化されています。これらのエラー値は、積分が関数値、変更などの最近の平均を反映するように進行するにつれて、徐々に修正されます。その結果、実際のエラー値を生成する関数を作成しようとしているのではなく、真の誤差、つまり、すべてのサンプル ポイントがこの推定誤差値で並べ替えられた場合、真の誤差で並べ替えられた場合と同様のランクが付けられるはずです。

すべてのポイントが等しいわけではありません。#1 の真のエラーを含むポイント領域が #1000 にランク付けされているかどうか (またはその逆) は非常に気にしますが、#500 のポイントが #1000 にランク付けされているかどうかはほとんど気にしません。私の成功の尺度は、アルゴリズムの実行の途中の時点で、多くの領域にわたって次の合計を最小化することです。

ABS(Log2(trueErrorRank) - Log2(estimatedErrorRank))

Log2 の場合、数値以下の最大の 2 乗を返す関数を使用しています。この定義から、有用な結果が得られます。1 番と 2 番を入れ替えると 1 ポイントかかりますが、2 番と 3 番を入れ替えても無料です。これには、ポイントを 2 の累乗範囲に階層化する効果があります。範囲内で交換されたポイントは機能に追加されません。

私が評価する方法これを行うRankというクラスを作成しました。

  1. すべての領域を真の誤差で 1 回ランク付けします。

  2. パラメーター化された重みの個別のセットごとに、その領域の試行 (推定) エラーを計算します。

  3. その試行錯誤で領域を並べ替えます。

  4. 各地域のトライアル ランクを計算します。

  5. 2 つのランクのログの絶対差を合計し、これをパラメーター化の値と呼びます。したがって、値を最小化する必要があります。

C# コード. 以上のことをすべて行った後は、最適なパラメーターを見つけるために Microsoft Solver Foundation をセットアップする方法が必要です。構文は私を困惑させました。これが、これまでに作成した C# コードです。そこには、私が特定した 3 つの問題に対するコメントが表示されます。多分あなたはさらに多くを見つけることができます!これを機能させる方法はありますか?

public void Optimize()
{
    // Get the parameters from the GUI and figures out the low and high values for each weight.
    ParseParameters();

    // Computes the true rank for each region according to true error.
    var myRanker = new Rank(ErrorData, false);

    // Obtain Microsoft Solver Foundation's core solver object.
    var solver = SolverContext.GetContext();
    var model = solver.CreateModel();

    // Create a delegate that can extract the current value of each solver parameter
    // and stuff it in to a double array so we can later use it to call LinearTrial.
    Func<Model, double[]> marshalWeights = (Model m) =>
    {
        var i = 0;
        var weights = new double[myRanker.ParameterCount];
        foreach (var d in m.Decisions)
        {
            weights[i] = d.ToDouble();
            i++;
        }
        return weights;
    };

    // Make a solver decision for each GUI defined parameter.
    // Parameters is a Dictionary whose Key is the parameter name, and whose 
    // value is a Tuple of two doubles, the low and high values for the range.
    // All are Real numbers constrained to fall between a defined low and high value.
    foreach (var pair in Parameters)
    {
        // PROBLEM 1! Should I be using Decisions or Parameters here?
        var decision = new Decision(Domain.RealRange(ToRational(pair.Value.Item1), ToRational(pair.Value.Item2)), pair.Key);
        model.AddDecision(decision);
    }

    // PROBLEM 2! This calls myRanker.LinearTrial immediately, 
    // before the Decisions have values. Also, it does not return a Term.
    // I want to pass it in a lambda to be evaluated by the solver for each attempted set
    // of decision values.
    model.AddGoal("Goal", GoalKind.Minimize,

        myRanker.LinearTrial(marshalWeights(model), false)
    );
    // PROBLEM 3! Should I use a directive, like SimplexDirective? What type of solver is best?
    var solution = solver.Solve();
    var report = solution.GetReport();
    foreach (var d in model.Decisions)
    {
        Debug.WriteLine("Decision " + d.Name + ": " + d.ToDouble());
    }
    Debug.WriteLine(report);

    // Enable/disable buttons.
    UpdateButtons();
}

更新: フォールバックとして別のライブラリを探すことにし、DotNumerics ( http://dotnumerics.com/ ) を見つけました。彼らの Nelder-Mead シンプレックス ソルバーは、簡単に呼び出すことができました。

    Simplex simplex = new Simplex()
    {
        MaxFunEvaluations = 20000,
        Tolerance = 0.001
    };
    int numVariables = Parameters.Count();
    OptBoundVariable[] variables = new OptBoundVariable[numVariables];

    //Constrained Minimization on the intervals specified by the user, initial Guess = 1;
    foreach (var x in Parameters.Select((parameter, index) => new { parameter, index }))
    {
        variables[x.index] = new OptBoundVariable(x.parameter.Key, 1, x.parameter.Value.Item1, x.parameter.Value.Item2);
    }


    double[] minimum = simplex.ComputeMin(ObjectiveFunction, variables);

    Debug.WriteLine("Simplex Method. Constrained Minimization.");
    for (int i = 0; i < minimum.Length; i++)
        Debug.WriteLine(Parameters[i].Key + " = " + minimum[i].ToString());

必要なのは、ObjectiveFunction を double 配列を取るメソッドとして実装することだけでした。

private double ObjectiveFunction(double[] weights)
{
    return Ranker.LinearTrial(weights, false);
}

実際のデータに対して試したことはありませんが、Excel でシミュレーションを作成してテスト データを設定し、スコアを付けました。アルゴリズムから返された結果は完璧ではありませんでしたが、非常に優れたソリューションが得られました。

4

1 に答える 1

5

これが私の TL;DR の要約です。彼はLinearTrial、double の配列を取る の戻り値を最小化する方法を知りません。この配列の各値には独自の最小/最大値があり、彼はDecisions.

それが正しければ、次のことを実行できるようです。

double[] minimums = Parameters.Select(p => p.Value.Item1).ToArray();
double[] maximums = Parameters.Select(p => p.Value.Item2).ToArray();
// Some initial values, here it's a quick and dirty average
double[] initials = Parameters.Select(p => (p.Item1 + p.Item2)/2.0).ToArray();

var solution = NelderMeadSolver.Solve(
    x => myRanker.LinearTrial(x, false), initials, minimums, maximums);

// Make sure you check solution.Result to ensure that it found a solution.
// For this, I'll assume it did.

// Value 0 is the minimized value of LinearTrial
int i = 1;
foreach (var param in Parameters)
{
    Console.WriteLine("{0}: {1}", param.Key, solution.GetValue(i));
    i++;
}        

NelderMeadSolverは、MSF 3.0 の新機能です。MSFアセンブリのSolveドキュメントによると、静的メソッドは「指定された関数の最小値を見つけます」(MSDNドキュメントが空白であり、間違った関数シグネチャを示しているにもかかわらず)。

免責事項: 私は MSF の専門家ではありませんが、上記は私と私のテスト目標関数 (重みの合計) で機能しました。

于 2012-11-16T01:08:25.077 に答える