Я изучал линейную регрессию и попробовал ее на приведенном ниже множестве {(x, y)}, где x указал площадь дома в квадратных футах, а y - цену в долларах. Это первый пример в Andrew Ng Notes .
2104.400 1600.330 2400.369 1416.232 3000.540
Я разработал пример кода, но когда я его запускаю, стоимость увеличивается с каждым шагом, тогда как она должна уменьшаться с каждым шагом. Код и вывод приведены ниже. bias
является W 0 X 0 , где X 0 = 1. featureWeights
является массивом [X 1 , X 2 , ..., X N ]
Я также попробовал онлайн-решение Python, доступное здесь , и объяснил здесь . Но этот пример также дает тот же результат.
Где пробел в понимании концепции?
Код:
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);
}
}
Выход:
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
Янтарь беривал
источник
источник
Ответы:
Короткий ответ: размер шага слишком велик. Вместо спуска стены каньона, ваш шаг настолько велик , что вы прыжки через дорогу от одной стороны к более на другом!
Функция стоимости ниже:
Длинный ответ состоит в том, что наивному градиентному спуску трудно решить эту проблему, потому что наборы уровней вашей функции стоимости представляют собой сильно вытянутые эллипсы, а не круги. Чтобы надежно решить эту проблему, обратите внимание, что есть более сложные способы выбора:
Основная проблема
Основная проблема заключается в том, что наборы уровней вашей функции стоимости представляют собой сильно вытянутые эллипсы, и это вызывает проблемы для градиентного спуска. На приведенном ниже рисунке показаны наборы уровней для функции стоимости.
Я предлагаю прочитать этот ответ на Quora.
Быстрое исправление 1:
Измените свой код на,
private float ALPHA = 0.0000002f;
и вы перестанете выходить за пределы.Быстрое исправление 2:
Если вы масштабируете свои данные X до 2.104, 1.600 и т. Д. ... ваши уровни становятся сферическими, и градиентное снижение быстро сходится с более высокой скоростью обучения. Это снижает число условий вашей матрицы дизайна .X′X
Более сложные исправления
Если целью было эффективное решение обычных наименьших квадратов, а не просто изучение градиентного спуска для класса, обратите внимание на то, что:
Фактическое решение
Вы обнаружите, что они достигают минимального значения для функции стоимости.
источник
Как уже указывал Мэтью (Ганн), в этом случае контуры трехмерной функции стоимости или производительности являются очень эллиптическими. Так как ваш код Java использует одно значение шага размера для градиентных расчетов по родовым, обновлениям для весов (то есть ось у перехватывать и наклон линейной функции) являются как регулируются этим одностадийного размером.
В результате, очень маленький размер шага, необходимый для управления обновлением веса, связанного с большим градиентом (в данном случае наклон линейной функции), резко ограничивает, насколько быстро другой вес с меньшим градиентом ( пересечение оси Y линейной функции). В текущих условиях последний вес не сходится к своему истинному значению приблизительно 26,7.
Учитывая время и усилия, которые вы вложили в написание своего Java-кода, я бы предложил изменить его, чтобы использовать два дискретных значения размера шага, соответствующий размер шага для каждого веса. Эндрю Нг в своих заметках предполагает, что лучше использовать масштабирование объектов, чтобы гарантировать, что контуры функции стоимости имеют более регулярную (т.е. круговую) форму. Тем не менее, изменение вашего Java-кода для использования разного размера шага для каждого веса может быть хорошим упражнением в дополнение к рассмотрению масштабирования функций.
Другая идея, которую стоит рассмотреть, - это как выбрать начальные значения веса. В вашем коде Java вы инициализировали оба значения нулем. Также довольно часто инициализировать веса для небольших дробных значений. Однако в этом конкретном случае оба эти подхода не будут работать в свете сильно эллиптических (то есть некруглых) контуров трехмерной функции стоимости. Учитывая, что веса для этой проблемы можно найти с помощью других методов, таких как решение для линейной системы, предложенное Мэтью в конце его поста, вы можете попытаться инициализировать веса для значений, близких к правильным весам, и посмотреть, как ваш исходный код с помощью одного шага размер сходится.
Код Python, который вы нашли, подходит к решению так же, как ваш код Java - оба используют один параметр размера шага. Я изменил этот код Python, чтобы использовать разные размеры шага для каждого веса. Я включил это ниже.
Он работает под Python 3, который требует скобки вокруг аргумента для операторов "print". В противном случае он будет работать под Python 2, удалив скобки. Вам нужно будет создать CSV-файл с данными из примера Эндрю Нга.
Использование может дать перекрестную ссылку на код Python, чтобы проверить ваш код Java.
источник