J'ai étudié la régression linéaire et je l'ai essayée sur l'ensemble ci-dessous {(x, y)}, où x spécifiait la superficie de la maison en pieds carrés et y spécifiait le prix en dollars. Ceci est le premier exemple dans Andrew Ng Notes .
2104,400 1600,330 2400,369 1416,232 3000,540
J'ai développé un exemple de code mais quand je l'exécute, le coût augmente à chaque étape alors qu'il devrait diminuer à chaque étape. Code et sortie donnés ci-dessous. bias
est W 0 X 0 , où X 0 = 1. featureWeights
est un tableau de [X 1 , X 2 , ..., X N ]
J'ai également essayé une solution en ligne de python disponible ici et expliquée ici . Mais cet exemple donne également la même sortie.
Où est l'écart dans la compréhension du concept?
Code:
package com.practice.cnn;
import java.util.Arrays;
public class LinearRegressionExample {
private float ALPHA = 0.0001f;
private int featureCount = 0;
private int rowCount = 0;
private float bias = 1.0f;
private float[] featureWeights = null;
private float optimumCost = Float.MAX_VALUE;
private boolean status = true;
private float trainingInput[][] = null;
private float trainingOutput[] = null;
public void train(float[][] input, float[] output) {
if (input == null || output == null) {
return;
}
if (input.length != output.length) {
return;
}
if (input.length == 0) {
return;
}
rowCount = input.length;
featureCount = input[0].length;
for (int i = 1; i < rowCount; i++) {
if (input[i] == null) {
return;
}
if (featureCount != input[i].length) {
return;
}
}
featureWeights = new float[featureCount];
Arrays.fill(featureWeights, 1.0f);
bias = 0; //temp-update-1
featureWeights[0] = 0; //temp-update-1
this.trainingInput = input;
this.trainingOutput = output;
int count = 0;
while (true) {
float cost = getCost();
System.out.print("Iteration[" + (count++) + "] ==> ");
System.out.print("bias -> " + bias);
for (int i = 0; i < featureCount; i++) {
System.out.print(", featureWeights[" + i + "] -> " + featureWeights[i]);
}
System.out.print(", cost -> " + cost);
System.out.println();
// if (cost > optimumCost) {
// status = false;
// break;
// } else {
// optimumCost = cost;
// }
optimumCost = cost;
float newBias = bias + (ALPHA * getGradientDescent(-1));
float[] newFeaturesWeights = new float[featureCount];
for (int i = 0; i < featureCount; i++) {
newFeaturesWeights[i] = featureWeights[i] + (ALPHA * getGradientDescent(i));
}
bias = newBias;
for (int i = 0; i < featureCount; i++) {
featureWeights[i] = newFeaturesWeights[i];
}
}
}
private float getCost() {
float sum = 0;
for (int i = 0; i < rowCount; i++) {
float temp = bias;
for (int j = 0; j < featureCount; j++) {
temp += featureWeights[j] * trainingInput[i][j];
}
float x = (temp - trainingOutput[i]) * (temp - trainingOutput[i]);
sum += x;
}
return (sum / rowCount);
}
private float getGradientDescent(final int index) {
float sum = 0;
for (int i = 0; i < rowCount; i++) {
float temp = bias;
for (int j = 0; j < featureCount; j++) {
temp += featureWeights[j] * trainingInput[i][j];
}
float x = trainingOutput[i] - (temp);
sum += (index == -1) ? x : (x * trainingInput[i][index]);
}
return ((sum * 2) / rowCount);
}
public static void main(String[] args) {
float[][] input = new float[][] { { 2104 }, { 1600 }, { 2400 }, { 1416 }, { 3000 } };
float[] output = new float[] { 400, 330, 369, 232, 540 };
LinearRegressionExample example = new LinearRegressionExample();
example.train(input, output);
}
}
Production:
Iteration[0] ==> bias -> 0.0, featureWeights[0] -> 0.0, cost -> 150097.0
Iteration[1] ==> bias -> 0.07484, featureWeights[0] -> 168.14847, cost -> 1.34029099E11
Iteration[2] ==> bias -> -70.60721, featureWeights[0] -> -159417.34, cost -> 1.20725801E17
Iteration[3] ==> bias -> 67012.305, featureWeights[0] -> 1.51299168E8, cost -> 1.0874295E23
Iteration[4] ==> bias -> -6.3599688E7, featureWeights[0] -> -1.43594258E11, cost -> 9.794949E28
Iteration[5] ==> bias -> 6.036088E10, featureWeights[0] -> 1.36281745E14, cost -> 8.822738E34
Iteration[6] ==> bias -> -5.7287012E13, featureWeights[0] -> -1.29341617E17, cost -> Infinity
Iteration[7] ==> bias -> 5.4369677E16, featureWeights[0] -> 1.2275491E20, cost -> Infinity
Iteration[8] ==> bias -> -5.1600908E19, featureWeights[0] -> -1.1650362E23, cost -> Infinity
Iteration[9] ==> bias -> 4.897313E22, featureWeights[0] -> 1.1057068E26, cost -> Infinity
Iteration[10] ==> bias -> -4.6479177E25, featureWeights[0] -> -1.0493987E29, cost -> Infinity
Iteration[11] ==> bias -> 4.411223E28, featureWeights[0] -> 9.959581E31, cost -> Infinity
Iteration[12] ==> bias -> -4.186581E31, featureWeights[0] -> -Infinity, cost -> Infinity
Iteration[13] ==> bias -> Infinity, featureWeights[0] -> NaN, cost -> NaN
Iteration[14] ==> bias -> NaN, featureWeights[0] -> NaN, cost -> NaN
regression
least-squares
gradient-descent
supervised-learning
Amber Beriwal
la source
la source
Réponses:
La réponse courte est que la taille de votre pas est trop grande. Au lieu de descendre la paroi du canyon, votre pas est si grand que vous sautez d'un côté à l'autre plus haut!
Fonction de coût ci-dessous:
La réponse longue est qu'il est difficile pour une descente de gradient naïf de résoudre ce problème car les ensembles de niveaux de votre fonction de coût sont des ellipses très allongées plutôt que des cercles. Pour résoudre ce problème de manière robuste, notez qu'il existe des moyens plus sophistiqués de choisir:
Problème sous-jacent
Le problème sous-jacent est que les ensembles de niveaux de votre fonction de coût sont des ellipses très allongées, ce qui pose des problèmes de descente de gradient. La figure ci-dessous montre les ensembles de niveaux pour la fonction de coût.
Je suggère de lire cette réponse sur Quora.
Solution rapide 1:
Modifiez votre code
private float ALPHA = 0.0000002f;
et vous arrêterez le dépassement.Solution rapide 2:
Corrections plus avancées
Si l'objectif était de résoudre efficacement les moindres carrés ordinaires plutôt que d'apprendre simplement la descente de gradient pour une classe, observez que:
La solution réelle est
Vous constaterez que ceux-ci atteignent la valeur minimale pour la fonction de coût.
la source
Comme Matthew (Gunn) l'a déjà indiqué, les contours de la fonction de coût ou de performance tridimensionnelle sont très elliptiques dans ce cas. Étant donné que votre code Java utilise une seule valeur de pas pour les calculs de descente de gradient, les mises à jour des poids (c'est-à-dire l'ordonnée à l'origine et la pente de la fonction linéaire) sont toutes deux régies par cette seule taille de pas.
En conséquence, la très petite taille de pas qui est nécessaire pour contrôler la mise à jour du poids associé au plus grand gradient (dans ce cas, la pente de la fonction linéaire) limite considérablement la vitesse à laquelle l'autre poids avec le plus petit gradient (le l'ordonnée à l'origine de la fonction linéaire) est mise à jour. Dans les conditions actuelles, ce dernier poids ne converge pas vers sa vraie valeur d'environ 26,7.
Compte tenu du temps et des efforts que vous avez investis dans l'écriture de votre code Java, je vous suggère de le modifier pour utiliser deux valeurs de taille de pas discrètes, une taille de pas appropriée pour chaque poids. Andrew Ng suggère dans ses notes qu'il est préférable d'utiliser la mise à l'échelle des fonctionnalités pour s'assurer que les contours de la fonction de coût sont de forme plus régulière (c'est-à-dire circulaires). Cependant, la modification de votre code Java pour utiliser une taille de pas différente pour chaque poids peut être un bon exercice en plus d'étudier la mise à l'échelle des fonctionnalités.
Une autre idée à considérer est de savoir comment les valeurs de poids initiales sont sélectionnées. Dans votre code Java, vous avez initialisé les deux valeurs à zéro. Il est également assez courant d'initialiser les poids à de petites valeurs fractionnaires. Dans ce cas particulier, cependant, ces deux approches ne fonctionneraient pas à la lumière des contours très elliptiques (c'est-à-dire non circulaires) de la fonction de coût tridimensionnelle. Étant donné que les poids pour ce problème peuvent être trouvés en utilisant d'autres méthodes, comme la solution pour le système linéaire suggérée par Matthew à la fin de son article, vous pouvez essayer d'initialiser les poids à des valeurs plus proches des poids corrects et voir comment votre code d'origine en utilisant une seule taille de pas converge.
Le code Python que vous avez trouvé aborde la solution de la même manière que votre code Java - les deux utilisent un seul paramètre de taille d'étape. J'ai modifié ce code Python pour utiliser une taille de pas différente pour chaque poids. Je l'ai inclus ci-dessous.
Il s'exécute sous Python 3, ce qui nécessite les parenthèses autour de l'argument des instructions "print". Sinon, il s'exécutera sous Python 2 en supprimant les parenthèses. Vous devrez créer un fichier CSV avec les données de l'exemple d'Andrew Ng.
L'utilisation peut renvoyer le code Python pour vérifier votre code Java.
la source