2012-10-10 17 views
18

Scopo del programma: Integrazione. Sto implementando un algoritmo adattativo in quadratura (alias dell'integrazione numerica) per alte dimensioni (fino a 100). L'idea è di rompere il volume in modo casuale in sezioni più piccole valutando i punti utilizzando una densità di campionamento proporzionale a una stima dell'errore in quel punto. All'inizio I "burn-in" un campione uniforme, quindi scelgo casualmente i punti secondo una distribuzione gaussiana oltre l'errore stimato. In un modo simile alla ricottura simulata, "abbasso la temperatura" e riduco la deviazione standard del mio Gaussiano col passare del tempo, così che i punti a basso errore inizialmente hanno una buona possibilità di essere scelti, ma in seguito vengono scelti con una diminuzione costante probabilità. Ciò consente al programma di inciampare su picchi che potrebbero essere persi a causa di imperfezioni nella funzione di errore. (Il mio algoritmo è simile nello spirito a integrazione Markov-Chain Monte-Carlo.)Definizione dell'obiettivo con Microsoft Solution Foundation

Funzione Caratteristiche. La funzione da integrare è la perdita stimata della polizza assicurativa per più edifici a causa di un disastro naturale. Le funzioni politiche non sono uniformi: ci sono franchigie, massimi, livelli (ad esempio, il pagamento zero fino a 1 milione di dollari, il 100% di pagamento da 1-2 milioni di dollari, quindi il pagamento zero oltre 2 milioni di dollari) e altri termini dispari. Questo introduce un comportamento non lineare e funzioni che non hanno derivati ​​in numerosi piani. In cima alla funzione politica c'è la funzione di danno, che varia in base al tipo di edificio e alla forza dell'uragano e non è sicuramente a forma di campana.

Contesto dei problemi: Funzione errore. La difficoltà è scegliere una buona funzione di errore. Per ogni misure registrare punto I che sembrano utili a questo: l'ampiezza della funzione, quanto è modificata a seguito di un measuremnent precedente (un proxy per la derivata prima), il volume della regione del punto occupa (volumi maggiori può nascondere l'errore meglio) e un fattore geometrico correlato alla forma della regione. La mia funzione di errore sarà una combinazione lineare di queste misure in cui a ciascuna misura viene assegnato un peso diverso. (Se ottengo risultati mediocri, contemplerò le funzioni non lineari.) Per aiutarmi in questo sforzo, ho deciso di eseguire un'ottimizzazione su una vasta gamma di valori possibili per ciascun peso, da qui la Microsoft Solution Foundation.

Cosa ottimizzare: Riga errore. Le mie misure sono normalizzate, da zero a uno. Questi valori di errore vengono progressivamente modificati man mano che l'integrazione procede per riflettere le medie recenti per valori di funzione, modifiche, ecc. Come risultato, non sto cercando di creare una funzione che restituisce valori di errore effettivi, ma produce un numero che ordina lo stesso di l'errore vero, cioè se tutti i punti campionati sono ordinati per questo valore di errore stimato, dovrebbero ricevere un grado simile al rango che riceverebbero se ordinati per errore vero.

Non tutti i punti sono uguali. Mi interessa molto se la regione del punto con errore vero e proprio # 1 è classificata # 1000 (o viceversa), ma ci tengo molto poco se il punto # 500 è classificato # 1000. La mia misura del successo è quello di ridurre al minimo la somma dei seguenti elementi su molte regioni in un punto parzialmente in esecuzione dell'algoritmo:

ABS (log2 (trueErrorRank) - log2 (estimatedErrorRank))

Per log2 Sto usando un funzione che restituisce la potenza maggiore di due inferiore o uguale al numero. Da questa definizione, arrivano risultati utili. Scambiare n. 1 e n. 2 costa un punto, ma scambiare n. 2 e n. 3 non costa nulla. Questo ha l'effetto di stratificare i punti in potenza di due gamme. I punti scambiati all'interno di un intervallo non si aggiungono alla funzione.

Come valuto.Ho costruito una classe chiamata Classifica che fa questo:

  1. Ranks tutte le regioni dal vero errore di una volta.

  2. Per ciascun set separato di pesi parametrizzati, calcola l'errore di prova (stimato) per quella regione.

  3. Ordina le regioni in base a quell'errore di prova.

  4. Calcola il grado di valutazione per ciascuna regione.

  5. aggiunge la differenza assoluta di tronchi di due ranghi e chiede questo il valore della parametrizzazione, quindi il valore da minimizzato.

C# Codice. Avendo fatto tutto ciò, ho solo bisogno di un modo per configurare Microsoft Solver Foundation per trovare i parametri migliori. La sintassi mi ha bloccato. Ecco il mio codice C# che ho finora. In esso vedrai i commenti per tre problemi che ho identificato. Forse puoi individuare ancora di più! Qualche idea su come farlo funzionare?

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(); 
} 

UPDATE: ho deciso di cercare un'altra libreria come ripiego, e ho trovato DotNumerics (http://dotnumerics.com/). Il loro Nelder-Mead Simplex risolutore è stato facile chiamare:

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()); 

Tutto quello che serviva era di implementare ObjectiveFunction come metodo di prendere una doppia matrice:

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

non l'ho provato con i dati reali, ma Ho creato una simulazione in Excel per impostare i dati del test e segnarlo. I risultati che tornavano dal loro algoritmo non erano perfetti, ma fornivano un'ottima soluzione.

+4

Sto iniziando a pensare che l'alternativa in un clic di persone a "TLDNR" sia UpVote. Potrei testare questa teoria ponendo una domanda che sembra incredibilmente complessa, lasciando cadere alcune parole d'ordine avanzate per algoritmi computazionali e una serie di frammenti di codice. Eh, non importa, preferirò semplicemente questo. :-) – kingdango

risposta

4

Ecco il mio riepilogo TL; DR: Non sa come minimizzare il valore di ritorno di LinearTrial, che accetta una serie di doppi. Ogni valore in questo array ha il suo valore min/max e sta modellando quello usando Decisions.

Se questo è corretto, a quanto pare si può solo effettuare le seguenti operazioni:

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++; 
}   

La NelderMeadSolver è nuovo a MSF 3.0. Il metodo statico Solve "trova il valore minimo della funzione specificata" in base alla documentazione nell'assembly MSF (nonostante la documentazione MSDN sia vuota e mostra la firma della funzione errata).

Disclaimer: Non sono esperto di MSF, ma quanto sopra ha funzionato per me e la mia funzione obiettivo di test (somma dei pesi).