그라디언트 디센트는이 데이터 세트에서 보통 최소 제곱에 대한 솔루션을 찾지 못합니까?


12

나는 선형 회귀를 연구하고 아래 세트 {(x, y)}에서 시도했습니다. 여기서 x는 평방 피트 단위의 주택 면적을 지정하고 y는 가격을 달러 단위로 지정했습니다. Andrew Ng Notes 의 첫 번째 예입니다 .

2104,400
1600,330
2400,369
1416,232
3000,540

샘플 코드를 개발했지만 실행할 때 각 단계마다 비용이 증가하는 반면 각 단계마다 비용이 감소합니다. 아래에 주어진 코드와 출력. biasW 0 X 0 이고, 여기서 X 0 = 1입니다. featureWeights[X 1 , X 2 , ..., X N ] 의 배열입니다 .

나는 또한 여기 에서 사용 가능한 온라인 파이썬 솔루션을 시도하고 여기설명했다 . 그러나이 예제는 동일한 출력을 제공합니다.

개념을 이해하는데 차이가있는 곳은 어디입니까?

암호:

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

여기서는 주제가 아닙니다.
Michael R. Chernick

3
여기서와 같이 일이 무한대로 터지면 아마도 어딘가에 벡터의 스케일로 나누는 것을 잊어 버릴 것입니다.
StasK

5
Matthew의 대답은 분명히 통계적입니다. 이것은 질문에 통계적 (프로그래밍이 아닌) 전문 지식이 필요하다는 것을 의미합니다. 그것은 정의에 의해 주제를 주제로 만듭니다. 다시 열기 위해 투표합니다.
amoeba는

답변:


35

짧은 대답은 단계 크기가 너무 크다는 것입니다. 대신 협곡 벽을 내림차순으로, 당신의 단계가 너무 커서 그 하나 개의 측면에서에서 당신이있는 거 점프 높은 다른 최대!

아래 비용 함수 :

여기에 이미지 설명을 입력하십시오

긴 대답은 비용 함수의 수준 집합이 원이 아닌 길쭉한 타원이기 때문에 순진한 그라데이션 하강 이이 문제를 해결하기가 어렵다는 것입니다. 이 문제를 강력하게 해결하려면보다 복잡한 방법을 선택할 수 있습니다.

  • 단계 크기 (상수를 하드 코딩하는 것보다)
  • 단계 방향 (구배 하강보다).

근본적인 문제

근본적인 문제는 비용 함수의 수준 집합 길쭉한 타원이며, 이로 인해 경사 하강 문제가 발생한다는 것입니다. 아래 그림은 비용 함수의 레벨 세트를 보여줍니다.

  • 026.789
  • 스텝 크기가 너무 크면 문자 그대로 하단의 파란색 영역 위로 이동하여 하강 대신 오름차순으로 이동합니다.
  • θ0

Quora 에서이 답변 을 읽는 것이 좋습니다 .

여기에 이미지 설명을 입력하십시오

빠른 수정 1 :

코드를로 변경 private float ALPHA = 0.0000002f;하면 오버 슈트가 중단됩니다.

빠른 수정 2 :

XX

더 고급 수정

목표가 단순히 클래스의 기울기 강하를 배우기보다 보통 최소 제곱을 효율적으로 해결하는 것이라면 다음을 관찰하십시오.

  • 행 검색Armijo 규칙 과 같이 단계 크기를 계산하는보다 정교한 방법이 있습니다 .
  • 지역 조건이 우세한 해답에 가까운 Newton의 방법 은 2 차 수렴을 구하며 단계 방향과 크기를 선택하는 좋은 방법입니다.
  • 최소 제곱을 푸는 것은 선형 시스템을 푸는 것과 같습니다. 최신 알고리즘은 순진한 하강을 사용하지 않습니다. 대신 :
    • k
    • 대형 시스템의 경우 최적화 문제임을 공식화하고 Krylov 하위 공간 방법 과 같은 반복 방법을 사용 합니다.

(XX)b=Xyb

실제 해결책은

  26.789880528523071
   0.165118878075797

그것들은 비용 함수의 최소값을 달성한다는 것을 알게 될 것입니다.


5
+1 다른 사람들이 코드를 디버깅 할 수있게하는 것은 사치입니다!
Haitao Du

4
@ hxd1011 처음에는 바보 코딩 오류라고 생각했지만 대신 순진한 하강으로 잘못 될 수있는 것에 대한 매우 유익한 예가됩니다 (imho).
Matthew Gunn

@ MatthewGunn 나는 해결책 b = 0.99970686, m = 0.17655967 (y = mx + b)를 얻었다. 그리고 "상수를 하드 코딩하는 것보다 스텝 크기"란 무엇을 의미합니까? 반복 할 때마다 변경해야합니까? 또는 입력 값을 기준으로 계산해야합니까?
Amber Beriwal

αiiααif

@AmberBeriwal (26.789, .1651)의 비용은 약간 저렴합니다. 비용 함수가 작은 경사를 갖는 방향으로 (.9997, .1766)에서 약간 내리막입니다.
Matthew Gunn

2

Matthew (Gunn)가 이미 지적했듯이이 경우 3 차원 비용 또는 성능 함수의 윤곽은 매우 타원형입니다. 자바 코드 그래디언트 디센트 계산 한 스텝 사이즈의 값을 사용하기 때문에, 웨이트 (즉, Y 축 절편 선형 함수의 기울기)에 업데이트되는 이 단일 단계 크기에 의해 지배.

결과적으로 더 큰 기울기 (이 경우 선형 함수의 기울기)와 관련된 가중치의 업데이트를 제어하는 ​​데 필요한 매우 작은 단계 크기는 더 작은 기울기로 다른 가중치를 얼마나 빨리 제한하는지 ( 선형 함수의 y 축 절편이 업데이트됩니다. 현재 조건에서 후자의 무게는 약 26.7의 실제 값으로 수렴하지 않습니다.

Java 코드 작성에 투자 한 시간과 노력을 감안할 때 각 가중치에 적절한 단계 크기 인 두 개의 개별 단계 크기 값을 사용하도록 수정하는 것이 좋습니다. Andrew Ng는 비용 함수의 윤곽이보다 규칙적인 (즉, 원형) 형태를 유지하기 위해 기능 스케일링을 사용하는 것이 좋습니다. 그러나 각 가중치에 대해 다른 단계 크기를 사용하도록 Java 코드를 수정하는 것은 기능 스케일링을 보는 것 외에도 좋은 연습이 될 수 있습니다.

고려해야 할 또 다른 아이디어는 초기 무게 값을 선택하는 방법입니다. Java 코드에서 두 값을 모두 0으로 초기화했습니다. 가중치를 작은 분수 값으로 초기화하는 것도 일반적입니다. 그러나,이 특정한 경우에, 이들 접근법 모두 3 차원 비용 함수의 매우 타원형 (즉, 비 원형) 윤곽에 비추어 작동하지 않을 것이다. 이 문제에 대한 가중치는 게시물 끝에 Matthew가 제안한 선형 시스템 솔루션과 같은 다른 방법을 사용하여 찾을 수 있다고 가정하면 올바른 가중치에 더 가까운 값으로 가중치를 초기화하고 원래 코드가 어떻게 작동하는지 확인할 수 있습니다 단일 단계 크기 수렴을 사용합니다.

찾은 Python 코드는 Java 코드와 동일한 방식으로 솔루션에 접근합니다. 둘 다 단일 단계 크기 매개 변수를 사용합니다. 각 가중치마다 다른 단계 크기를 사용하도록이 Python 코드를 수정했습니다. 아래에 포함 시켰습니다.

from numpy import *

def compute_error_for_line_given_points(b, m, points):
    totalError = 0
    for i in range(0, len(points)):
        x = points[i, 0]
        y = points[i, 1]
        totalError += (y - (m * x + b)) ** 2
    return totalError / float(len(points))

def step_gradient(b_current, m_current, points, learningRate_1, learningRate_2):
    b_gradient = 0
    m_gradient = 0
    N = float(len(points))
    for i in range(0, len(points)):
        x = points[i, 0]
        y = points[i, 1]
        b_gradient += -(2/N) * (y - ((m_current * x) + b_current))
        m_gradient += -(2/N) * x * (y - ((m_current * x) + b_current))
    new_b = b_current - (learningRate_1 * b_gradient)
    new_m = m_current - (learningRate_2 * m_gradient)
    return [new_b, new_m]

def gradient_descent_runner(points, starting_b, starting_m, learning_rate_1, learning_rate_2, num_iterations):
    b = starting_b
    m = starting_m
    for i in range(num_iterations):
        b, m = step_gradient(b, m, array(points), learning_rate_1, learning_rate_2)
    return [b, m]

def run():
    #points = genfromtxt("data.csv", delimiter=",")
    #learning_rate = 0.0001
    #num_iterations = 200

    points = genfromtxt("test_set.csv", delimiter=",")
    learning_rate_1 = 0.5
    learning_rate_2 = 0.0000001
    num_iterations = 1000

    initial_b = 0 # initial y-intercept guess
    initial_m = 0 # initial slope guess


    print("Starting gradient descent at b = {0}, m = {1}, error = {2}".format(initial_b, initial_m, compute_error_for_line_given_points(initial_b, initial_m, points)))
    print("Running...")

    [b, m] = gradient_descent_runner(points, initial_b, initial_m, learning_rate_1, learning_rate_2, num_iterations)

    print("After {0} iterations b = {1}, m = {2}, error = {3}".format(num_iterations, b, m, compute_error_for_line_given_points(b, m, points)))

if __name__ == '__main__':
    run()

파이썬 3에서 실행되는데, "print"문에 대한 인수를 괄호로 묶어야합니다. 그렇지 않으면 괄호를 제거하여 Python 2에서 실행됩니다. Andrew Ng의 예제 데이터를 사용하여 CSV 파일을 작성해야합니다.

Python 코드를 상호 참조하여 Java 코드를 확인할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.