Eine multivariable Funktion mit scipy minimieren. Derivat nicht bekannt

8

Ich habe eine Funktion, die eigentlich ein Aufruf an ein anderes Programm ist (etwas Fortran-Code). Wenn ich diese Funktion ( run_moog ) aufruft, kann ich 4 Variablen analysieren und es gibt 6 Werte zurück. Diese Werte sollten alle nahe bei 0 liegen (um zu minimieren). Allerdings habe ich sie so kombiniert: np.sum(results**2) . Jetzt habe ich eine Skalarfunktion. Ich möchte diese Funktion minimieren, dh die np.sum(results**2) möglichst nahe Null bringen.
Hinweis : Wenn diese Funktion ( run_moog ) die 4 Eingabeparameter verwendet, wird ein Eingabedatei für den Fortran-Code, der von diesen Parametern abhängt.

Ich habe verschiedene Möglichkeiten aus scipy docs ausprobiert. Aber keiner funktioniert wie erwartet. Die Minimierung sollte Grenzen für die 4 Variablen haben können. Hier ist ein Versuch:

%Vor%

Das gibt mir dann

%Vor%

Der dritte Parameter ändert sich geringfügig, während die anderen genau gleich sind. Außerdem gab es 5 Funktionsaufrufe ( nfev ), aber keine Iterationen ( nit ). Die Ausgabe von scipy wird hier angezeigt.

    
Daniel Thaagaard Andreasen 24.04.2015, 19:25
quelle

4 Antworten

7

Ein paar Möglichkeiten:

  1. Probiere COBYLA aus. Es sollte derivatfrei sein und Ungleichheitsbeschränkungen unterstützen.
  2. Sie können verschiedene epsilons nicht über die normale Schnittstelle verwenden; Versuchen Sie also, Ihre erste Variable um 1e4 zu skalieren. (Teile es ein, multipliziere es wieder.)
  3. Überspringe den normalen automatischen jacobian-Konstruktor und mach dein eigenes:

Angenommen, Sie versuchen, SLSQP zu verwenden, und Sie stellen keine jacobian-Funktion bereit. Es macht eins für dich. Der Code dafür lautet in approx_jacobian in slsqp.py . Hier ist eine Kurzfassung:

%Vor%

Sie könnten versuchen, diese Schleife durch:

zu ersetzen %Vor%

Sie können dies nicht als jacobian für minimize angeben, aber es ist einfach zu beheben:

%Vor%

Sie können dann minimize like:

aufrufen %Vor%     
Jay Kominek 16.05.2015 16:24
quelle
2

Es hört sich so an, als ob Ihre Zielfunktion keine gutartigen Ableitungen hat. Die Zeile in der Ausgabe jac: array([ 0., 0., -54090399.99999981, 0.]) bedeutet, dass nur der dritte Variablenwert geändert werden kann. Und weil das Derivat w.r.t. zu dieser Variable ist praktisch unendlich, da ist wahrscheinlich etwas falsch in der Funktion. Deshalb endet auch der dritte Variablenwert in seinem Maximum.

Ich würde vorschlagen, dass Sie sich die Derivate ansehen, zumindest in einigen Punkten in Ihrem Parameterraum. Berechne sie mit endlichen Unterschieden und der Standardschrittweite von SciPys fmin_l_bfgs_b , 1e-8 . Hier ist ein Beispiel dafür, wie Sie die Derivate berechnen können.

Versuchen Sie auch, Ihre Zielfunktion zu plotten. Zum Beispiel, halten Sie zwei der Parameter konstant und lassen Sie die beiden anderen variieren. Wenn die Funktion mehrere lokale Optima hat, sollten Sie keine gradientenbasierten Methoden wie BFGS verwenden.

    
jasaarim 17.05.2015 06:56
quelle
1

Wie schwierig ist es, einen analytischen Ausdruck für den Gradienten zu bekommen? Wenn Sie das haben, können Sie das Produkt des Hessischen mit einem Vektor unter Verwendung der endlichen Differenz approximieren. Dann können Sie andere verfügbare Optimierungsroutinen verwenden.

Unter den verschiedenen Optimierungsroutinen, die in SciPy zur Verfügung stehen, ist die sogenannte TNC (Newton Conjugate Gradient with Truncation) ziemlich robust gegenüber den numerischen Werten, die mit dem Problem verbunden sind.

    
haripkannan 17.05.2015 07:20
quelle
1

Die Nelder-Mead-Simplex-Methode (vorgeschlagen von Cristián Antuña in den Kommentaren oben) ist bekanntlich eine gute Wahl für die Optimierung von (schlecht benommenen) Funktionen ohne Kenntnis der Derivate (siehe Numerische Rezepte in C, Kapitel 10 ).

Es gibt zwei etwas spezifische Aspekte zu Ihrer Frage. Die erste ist die Beschränkung der Eingaben und die zweite ist ein Skalierungsproblem. Im Folgenden werden Lösungen für diese Punkte vorgeschlagen, aber Sie müssen möglicherweise manuell einige Male zwischen ihnen durchlaufen, bis die Dinge funktionieren.

Eingabebeschränkungen

Angenommen, Ihre Eingabebeschränkungen bilden eine konvexe Region (wie Ihre obigen Beispiele zeigen, aber ich würde es gerne verallgemeinern ein bisschen), dann kannst du eine Funktion schreiben

%Vor%

Mit dieser Funktion wird angenommen, dass der Algorithmus vom Punkt from_ zum Punkt to wechseln möchte, wobei from_ bekanntermaßen in der Region liegt. Dann findet die folgende Funktion effizient den äußersten Punkt auf der Linie zwischen den zwei Punkten, an denen sie fortfahren kann:

%Vor%

(Beachten Sie, dass diese Funktion für einige Regionen optimiert werden kann, aber es lohnt sich kaum, da sie nicht einmal Ihre ursprüngliche Objektfunktion aufruft, die die teure ist.)

Einer der schönen Aspekte von Nelder-Mead ist, dass die Funktion eine Reihe von Schritten ausführt, die so intuitiv sind. Einige dieser Punkte können Sie offensichtlich aus der Region werfen, aber es ist einfach, dies zu ändern. Hier ist eine Implementierung von Nelder Mead mit vorgenommenen Änderungen zwischen Paaren von Zeilen der Form ################################################################## :

%Vor%

Hinweis Diese Implementierung ist GPL , was in Ordnung ist für dich oder nicht. Es ist jedoch sehr einfach, NM aus irgendeinem Pseudocode zu modifizieren, und Sie sollten in jedem Fall simuliertes Glühen einwerfen.

Skalierung

Das ist ein kniffligeres Problem, aber jasaarim hat diesbezüglich einen interessanten Punkt gemacht. Sobald der modifizierte NM-Algorithmus einen Punkt gefunden hat, möchten Sie möglicherweise matplotlib.contour ausführen und dabei einige Dimensionen korrigieren. um zu sehen, wie sich die Funktion verhält. An dieser Stelle möchten Sie möglicherweise eine oder mehrere der Dimensionen neu skalieren und das geänderte NM erneut ausführen.

-

    
Ami Tavory 22.05.2015 08:51
quelle