2017-09-29 2 views
1

Ich versuche, einen einfachen Gradienten-Abstieg-Algorithmus in C++ (für 10.000 Iterationen) zu schreiben. Hier ist mein Programm:Warum bekomme ich immer Nan als Ausgabe?

#include<iostream> 
#include<cmath> 

using namespace std; 

int main(){ 

    double learnrate=10; 
    double x=10.0; //initial start value 

    for(int h=1; h<=10000; h++){ 
    x=x-learnrate*(2*x + 100*cos(100*x)); 
    } 

    cout<<"The minimum is at y = "<<x*x + sin(100*x)<<" and at x = "<<x; 

    return 0; 
} 

Die Ausgabe endet als: y = nan und x = nan. Ich habe versucht, die Werte von x und y zu betrachten, indem ich sie in eine Datei lege, und nach einer bestimmten Anzahl von Iterationen bekomme ich alle Nans (für x und y). edit: Ich habe die Lernrate (oder Schrittgröße) als Experiment auf 10 gesetzt, danach verwende ich viel kleinere Werte.

+3

Was ist der Wert von 'x' kurz bevor es' NaN' wird? – NathanOliver

+0

Stellen Sie sicher, dass alle Ihre Operationen Doubles beinhalten und dass Ihre Ganzzahl-Literale/-Konstanten keine impliziten Konvertierungen verursachen. – Dai

+0

Hallo. Der letzte x-Wert vor nan ist: -1.7761e + 307. Wenn es darauf ankommt, ist der letzte Wert für y 3.96343e + 307 und danach, für mehrere weitere Werte, sagt er "inf" für y, und x hat noch nummerierte Werte und dann werden beide x und ynan. (@NathanOliver) – user8697693

Antwort

0

Drucken x vor dem Aufruf der Funktion Cosinus und Sie werden sehen, dass die letzte Zahl gedruckt vor NaN (bei h = 240) ist:

-1.7761e + 307

Das bedeutet, dass der Wert geht in die Unendlichkeit, die nicht dargestellt werden kann (also Not a Number).

Es überläuft den double Typ.

Wenn Sie long double verwenden, werden Sie in 1000 Iterationen erfolgreich sein, aber Sie werden immer noch den Typ mit 10000 Iterationen überlaufen. Das Problem ist, dass der Parameter learnrate einfach zu groß ist. Sie sollten Schritte ausführen, während Sie einen Datentyp mit größerem Bereich verwenden, wie ich oben vorgeschlagen habe.

+0

Oh danke! Entschuldigung, ich habe deinen Kommentar bis jetzt nicht gesehen. Ich vermutete, dass das Problem war, sollte ich meinen Anfangswert von x in Radianten umwandeln? – user8697693

+0

@ user8697693 Ich erinnere mich nicht wirklich jetzt. Viel Glück! – gsamaras

+0

Danke, aber noch eine Question: Was ist mit meiner Formel falsch? – user8697693

0

Da bei h = 240 die Variable "x" die Grenzen des doppelten Typs überschreitet (1.79769e + 308). Dies ist eine divergierende arithmetische Progression. Sie müssen Ihre Lernrate reduzieren.

Ein paar weitere Dinge: 1- Verwenden Sie nicht "using Namespace std;" es ist eine schlechte Übung. 2- Sie können „std :: isnan() Funktion verwenden, um diese Situation zu identifizieren

Hier ein Beispiel:..

#include <iomanip> 

#include <limits> 
int main() 
{ 


    double learnrate = 10.0; 

    double x = 10.0; //initial start value 

    std::cout<<"double type maximum=" << std::numeric_limits<double>::max()<<std::endl; 
    bool failed = false; 
    for (int h = 1; h <= 10000; h++) 
    { 

    x = x - learnrate*(2.0*x + 100.0 * std::cos(100.0 * x)); 
    if (std::isnan(x)) 
    { 
     failed = true; 
     std::cout << " Nan detected at h=" << h << std::endl; 
     break; 
    } 
    } 
    if(!failed) 
    std::cout << "The minimum is at y = " << x*x + std::sin(100.0*x) << " and at x = " << x; 

    return 0; 
} 
+0

Während ich in meinen cpp-Dateien normalerweise nicht "using namespace std" verwende, würde ich es nicht als schlechte Übung bezeichnen. Die Verwendung von Header-Dateien in einer Datei ist eine schlechte Übung. –

1

Es ist etwas falsch mit Ihrer Formel sein müssen bereits die ersten 10 Werte x sind wie die Hölle zu erhöhen:

-752.379 
15290.7 
-290852 
5.52555e+06 
-1.04984e+08 
1.9947e+09 
-3.78994e+10 
7.20088e+11 
-1.36817e+13 
2.59952e+14 

Egal welchen Wert beginnen Sie den absoluten Wert des nächsten x wählen, wird größer sein

.

Zum Beispiel überlegen, was passiert, wenn man einen sehr kleinen Startwert (|x|->0) wählt, dann

|next_x| = | 0 - 20 * 0 - 100 * cos (0) | = 100 
0

Die „Lernrate“ viel zu hoch ist. Ändern Sie es zum Beispiel zu 1e-4, und das Programm funktioniert, für einen Anfangswert von mindestens 10. Wenn die Lernrate 10 ist, springen die Iterationen zu weit über die Lösung hinaus.

Im besten Fall ist Gradientenabstieg kein guter Algorithmus. Für ernsthafte Anwendungen möchten Sie etwas besseres verwenden. Viel besser. Suchen Sie nach Brent-Optimierer und BFGS.

+0

Danke, ich werde es jetzt ausprobieren. Okay, meine Ausgabe war: Das Minimum ist bei y = -0.97581 und bei x = -0.15399. Dieser Algorithmus (wenn ich es richtig gemacht habe) war nur für meine eigene Praxis :). Ich habe gerade den Graphen der Funktion überprüft: y = x^2 + Sinus (100x) (das Integral meiner Ableitung) und eines der lokalen Minima ist um diese Zahlen herum. Ich werde auch diesen Brent Optimizer zu einem späteren Zeitpunkt ausprobieren! – user8697693

Verwandte Themen