2015-08-23 10 views
8

Ich habe vor kurzem die AI-Klasse bei Coursera gestartet und ich habe eine Frage bezüglich meiner Implementierung des Gradientenabstiegsalgorithmus.Gradientenabstieg in Java

meine aktuelle Implementierung hier (ich gerade tatsächlich „übersetzt“ die mathematischen Ausdrücke in Java-Code):

public class GradientDescent { 

private static final double TOLERANCE = 1E-11; 

private double theta0; 
private double theta1; 

public double getTheta0() { 
    return theta0; 
} 

public double getTheta1() { 
    return theta1; 
} 

public GradientDescent(double theta0, double theta1) { 
    this.theta0 = theta0; 
    this.theta1 = theta1; 
} 

public double getHypothesisResult(double x){ 
    return theta0 + theta1*x; 
} 

private double getResult(double[][] trainingData, boolean enableFactor){ 
    double result = 0; 
    for (int i = 0; i < trainingData.length; i++) { 
     result = (getHypothesisResult(trainingData[i][0]) - trainingData[i][1]); 
     if (enableFactor) result = result*trainingData[i][0]; 
    } 
    return result; 
} 

public void train(double learningRate, double[][] trainingData){ 
    int iteration = 0; 
    double delta0, delta1; 
    do{ 
     iteration++; 
     System.out.println("SUBS: " + (learningRate*((double) 1/trainingData.length))*getResult(trainingData, false)); 
     double temp0 = theta0 - learningRate*(((double) 1/trainingData.length)*getResult(trainingData, false)); 
     double temp1 = theta1 - learningRate*(((double) 1/trainingData.length)*getResult(trainingData, true)); 
     delta0 = theta0-temp0; delta1 = theta1-temp1; 
     theta0 = temp0; theta1 = temp1; 
    }while((Math.abs(delta0) + Math.abs(delta1)) > TOLERANCE); 
    System.out.println(iteration); 
} 

}

Der Code funktioniert ganz gut, aber nur, wenn ich eine sehr kleine Alpha wählen, hier als lernkurs bezeichnet. Wenn es höher als 0,00001 ist, divergiert es.

Haben Sie Vorschläge zur Optimierung der Implementierung oder eine Erklärung für das "Alpha-Issue" und eine mögliche Lösung dafür?

Update:

Hier ist der Haupt darunter auch einige Beispieleingänge:

private static final double[][] TDATA = {{200, 20000},{300, 41000},{900, 141000},{800, 41000},{400, 51000},{500, 61500}}; 

public static void main(String[] args) { 
    GradientDescent gd = new GradientDescent(0,0); 
    gd.train(0.00001, TDATA); 
    System.out.println("THETA0: " + gd.getTheta0() + " - THETA1: " + gd.getTheta1()); 
    System.out.println("PREDICTION: " + gd.getHypothesisResult(300)); 
} 

Der mathematische Ausdruck von Gradientenabfallsaktualisierung sich wie folgt:

enter image description here

+3

Sie sollten wahrscheinlich einige Beispiel Daten/Eingabe, in einer 'main' Methode, und vielleicht einen Backlink zu den Foren, die Sie" übersetzt "bereitstellen. – Marco13

+0

Ich habe die Frage aktualisiert und ich habe auch ein kleines Problem gefunden. Nachdem ich es behoben habe, kann ich nun die Lernrate auf 0,0001 setzen. Aber ich denke, es ist immer noch ziemlich niedrig, aber viel besser als vorher. –

+0

Welche der Coursera ML/AI-Klassen war es? Stanfords maschinelles Lernen? –

Antwort

1

Um dieses Problem zu lösen, müssen die Daten mit diesem Formular normalisiert werden: (Xi-mu)/s. Xi ist der aktuelle Trainingssatzwert, mu der Durchschnitt der Werte in der aktuellen Spalte und s der Maximalwert minus der Mindestwert der aktuellen Spalte. Diese Formel wird die Trainingsdaten ungefähr in einen Bereich zwischen -1 und 1 bringen, was es ermöglicht, höhere Lernraten und Gradientenabstieg zu wählen, um schneller zu konvergieren. Aber es ist danach notwendig, das vorhergesagte Ergebnis zu denormalisieren.

0

Sie sollten Java verwenden .math.BigDecimal für Ihre arithmetischen Operationen.
double hat seine Abrundungsprobleme bei der Ausführung von Arithmetik.

+4

Das ist eine wilde Vermutung, und ich bin nahe daran, dies zu verwerfen: Nur das Ersetzen von 'Double' durch' BigDecimal' wird nicht unbedingt das Problem lösen, das (!) Völlig unabhängig von der Genauigkeit sein könnte ... – Marco13

2
private double getResult(double[][] trainingData, boolean enableFactor){ 
double result = 0; 
for (int i = 0; i < trainingData.length; i++) { 
    result = (getHypothesisResult(trainingData[i][0]) - trainingData[i][1]); 
    if (enableFactor) result = result*trainingData[i][0]; 
} 
return result; 

In dieser Funktion. Ergebnisvariable überschreibt jede Iteration, wobei der alte Wert verloren geht. Bei der Eingabe der Werte berechnet nur das letzte Element im Array. Der Rest von ihnen ist egal.

+0

Sie haben Recht ! Danke für deinen Vorschlag! Es stellte sich heraus, dass es sich um einen sehr langsamen stochastischen Gradientenabfall handelt ... immer noch funktional, aber langsam, da alle Samples verarbeitet werden, um die Parameter mit nur einem Sample zu aktualisieren. –