AITech 학습정리-[DAY 7] 경사하강법
과거의 것들/AI Tech boostcamp

AITech 학습정리-[DAY 7] 경사하강법

===================================

수업내용

[AI Math 3강] 경사하강법(순한맛)

 

 

내가 옛날에 배웠던 그 미분정의.

import sympy as sym
from sympy.abc import x

print(sym.poly(x**2 + 2*x + 3))
print(sym.diff(sym.poly(x**2 + 2*x + 3)))
grad_equation = sym.diff(sym.poly(x**2 + 2*x + 3))
print(grad_equation.subs(x,2)) # x에 2를 대입
Poly(x**2 + 2*x + 3, x, domain='ZZ')
Poly(2*x + 2, x, domain='ZZ')
6

 

기울기를 알면 어느방향으로 가야 함수값이 증가하는지/감소하는지 알 수 있다. 

함수값을 증가시키고 싶으면 미분값을 더해주고, 감소시켜주고 싶으면 미분값을 뺀다.

 

경사상승/경사하강 방법은 극값에 도달하면(기울기가 0이 되면) 움직임을 멈춘다. 최적화 알고리즘이 자동으로 끝남.

 

여기서 f(x)는 손실함수고, 그러니까 예측값 - 실제값의 오차 error 에 대한 함수. 이 error가 작을수록 잘 예측한다는 거니까 f(x)가 작은 쪽으로 x값을 찾아가는거고, f(x)가 작은쪽으로 찾아가는게 경사하강법이다.

 

x-lambda*f`(x)

update gradient

import numpy as np
import sympy as sym
from sympy.abc import x

def func(val):
    fun = sym.poly(x**2 + 2*x + 3)
    return fun.subs(x, val), fun # x**2 + 2*x + 3 식에 x=val 대입한 결과를 반환

def func_gradient(fun, val):
    _, function = fun(val)
    diff = sym.diff(function, x)
    return diff.subs(x, val), diff

def gradient_descent(fun, init_point, lr_rate=1e-2, epsilon=1e-5):
    cnt = 0
    val = init_point
    grad, _ = func_gradient(fun, init_point) # grad는 val 위치에서의 기울기 값
      while np.abs(grad) > epsilon: # 기울기 값이 거의 0보다 클 경우
            val = val - lr_rate*grad
        grad, _ = func_gradient(fun, val)
        cnt+=1

    print(f"함수: {fun(val)[1]}, 연산횟수: {cnt}, 최소점: ({val}, {fun(val)[0]})")

gradient_descent(fun=func, init_point=np.random.uniform(-2,2))
함수: Poly(x**2 + 2*x + 3, x, domain='ZZ'), 연산횟수: 634, 최소점: (-0.999995033494063, 2.00000000002467)

 

실제론 다변수 함수.. 편미분 사용

모든 방향으로 편미분한 벡터.. 그레디언트(gradient) 벡터

역삼각형: nabla

 

f(x,y) = x^2 + 2*y^2 일 때 -그레디언트f 를 사용해서 감소하는 방향으로 갈 수 있다.

벡터는 종료조건에서 절대값 대신 노름을 사용

import numpy as np
import sympy as sym
from sympy.abc import x, y

def eval_(fun, val):
    val_x, val_y = val
    fun_eval = fun.subs(x, val_x).subs(y, val_y)
    return fun_eval

def func_multi(val):
    x_, y_ = val
    func = sym.poly(x**2 + 2*y**2)
    return eval_(func, [x_, y_]), func

def func_gradient(fun, val):
    x_, y_ = val
    _, function = fun(val) # function 은 sym.poly(x**2 + 2*y**2)
    diff_x = sym.diff(function, x) # 위 식을 x에 대한 편미분 식
    diff_y = sym.diff(function, y) # 위 식을 y에 대한 편미분 식
    grad_vec = np.array([eval_(diff_x, [x_, y_]), # x편미분 식에 x,y 대입
                         eval_(diff_y, [x_, y_])], # y편미분 식에 x,y 대입
                        dtype=float)
    # val 점에서 기울기 결과값 [x점에서 미분결과값, y점에서 미분결과값], 편미분한 식들
    return grad_vec, [diff_x, diff_y] 

def gradient_descent(fun, init_point, lr_rate=1e-2, epsilon=1e-5):
    cnt = 0
    val = init_point
    grad, _ = func_gradient(fun, init_point) # grad는 val 에서의 기울기 값
    while np.linalg.norm(grad) > epsilon: # 기울기 값이 거의 0보다 클 경우
        val = val - lr_rate*grad
        grad, _ = func_gradient(fun, val)
        cnt+=1

    print(f"함수: {fun(val)[1]}, 연산횟수: {cnt}, 최소점: ({val}, {fun(val)[0]})")

pt = [np.random.uniform(-2,2), np.random.uniform(-2,2)]
gradient_descent(fun=func_multi, init_point=pt)
함수: Poly(x**2 + 2*y**2, x, y, domain='ZZ'), 연산횟수: 447, 최소점: ([4.92220124e-06 1.15745136e-08], 2.42283329954978E-11)

 

[AI Math 4강] 경사하강법(매운맛)

 

전 시간에 배웠던 이 선형회귀분석으로 문제를 풀면 좋겠지만.. 세상은 그렇지 않더라.

뭔말이냐면 저 선 기울기 베타 가 보통 아니겠지

역행렬을 이용하지 말고 경사하강법을 이용해 적절한 선형모델을 찾아보자.

 

경사하강법으로 선형회귀 계수 구하기

선경회귀의 목적식은 ||y-Xb||2 이고 이를 최소화하는 b를 찾아야 하므로 다음과 같은 그레디언트 벡터를 구해야 한다. 위에서 말한 손실함수 f(x) 가 ||y-Xb||2 인 것. 즉 f(x) = ||y-Xb||2. 그래서 f(x)가 최소화되는 쪽으로 경사하강법을 적용한다. 찾고자 하는건 f(x)가 최소화 할 수 있는 b를 찾는 것. 그래서 new_b = old_b - diff(f(x)) 를 하는 것이다. 그럼 왜 b를 찾는가? 데이터 입력값 x가 있고 그 결과의 y이 있으니까 나중엔 x에다가 어떠한 식만 곱하면 y를 찾을 수 있도록 예측하는게 목표다. 그래서 x에다가 어떤 b를 곱하면 y가 되는지 예측하기 때문에 적절한 b를 찾는 것. x에다가 곱할 가중치 b를 찾는 것. 이게 후에 딥러닝에서 나온 weight. 딥러닝에서도 weight를 구하는 것이 목적이니까.

 

이제 경사하강법 알고리즘으로 역행렬을 이용하지 않고 회귀계수를 게산할 수 있다.

X = np.array([[1, 1], [1, 2], [2, 2], [2, 3]])
y = np.dot(X, np.array([1, 2])) + 3 # 1과 2라는 계수. y절편은 3. 을 이용해서 데이터 생성
# [1,2] 가 1차원이라 지가 알아서 세워서 계산해 품

beta_gd = [10.1, 15.1, -6.5] # [1, 2, 3] 이 정답
X_ = np.array([np.append(x,[1]) for x in X]) # intercept 항 추가
for t in range(5000): # 전처럼 기준점 못 넘을때 끝나는걸로 해도 됨
    error = y - (X_ @ beta_gd) # y-(X*beta)
    grad = - np.transpose(X_) @ error
    beta_gd = beta_gd - 0.01 * grad
print(beta_gd)
[1.00000367 1.99999949 2.99999516]

 

경사하강법은 만능일까?

- 이론적으로 경사하강법은 미분가능하고 볼록(convex)한 함수에 대해선 적절한 학습률과 학습횟수를 선택했을 때 수렴이 보장되어 있습니다.

- 특히 선형회귀의 경우 목적식 ||y-Xb||2 은 회계계수 b에 대해 볼록함수이기 때문에 알고리즘을 충분히 돌리면 수렴이 보장됩니다.

- 하지만 비선형회귀 문제의 경우 목적식이 볼록하지 않을 수 있으므로 수렴이 항상 보장되지는 안습니다. 경사하강법이 항상 수렴하지는 않음. 볼록이 2개 이상일 수도 있으니까..

그래서 변형된 경사하강법을 써야 하는데, 그게 확률적 경사하강법이다.

 

확률적 경사하강법

- 확률적 경사하강법(stochastic gradient descent)은 모든 데이터를 사용해서 업데이트하는 대신 데이터 한개 또는 일부를 활용하여 업데이트합니다.

- 볼록이 아닌(non-convex) 목적식은 SGD를 통해 최적화할 수 있습니다.

SGD라고 해서 만능은 아니지만 딥러니으이 경우 SGD가 경사하강법보다 실증적으로 더 낫다고 검증되었다.

- SGD는 데이터의 일부를 가지고 패러미터를 업데이트하기 때문에 연산자원을 좀 더 효율적으로 활용하는데 도움이 됩니다.

전체 데이터 (X,y) 를 쓰지 않고 미니배치 (X(b), y(b)) 를 써서 업데이트 하므로 연산량이 O(d^2n) 에서 O(d^2b)로 , b/n로 감소한다.

stochastic 확률(론)적인

파라미터란 앞에서 봤던 beta를 의미하는 것 같다.

앞에서 봤던 그래프는 볼록인 이상적인 형식이었지만, 보통은 안그러니까 그냥 일부만 뽑아서 볼록하다고 가정하고 그레디언트 구해서 경사하강법을 적용하는 듯. 일부만 빼낸 걸로 그레디언트 적용하고.

일부만 가져다 쓰기 때문에 효율적일 수 밖에 없다.

beta가 어디있을 때, 무슨 값일때, 어떤 값일 때 error 함수가 제일 낮은건가? 경사하강법은 error 함수인 f(x)가 제일 낮은 쪽으로 가는거다.

 

 

 

- 경사하강법은 전체데이터 D = (X,y)를 가지고 목적식의 그레디언트 벡터인 gredL(D,theta)를 계산합니다.

- SGD는 미니배치 D(b) = (X(b),y(b))<D 를 가지고 그레디언트 벡터를 계산합니다. 미니배치는 확률적으로 선택하므로 목적식 모양이 바뀌게 됩니다.

- SGD는 볼록이 아닌 목적식에서도 사용 가능하므로 경사하강법보다 머신러닝 학습데 더 효율적 입니다.

어쨋든 감소됨

세상엔 너무 많은 데이터.. 어쩔수 없이 미니배치로 쪼개야 한다. GPU에서 행렬 연산과 모델 패러미터를 업데이트하는 동아나 CPU는 전처리와 GPU에서 업로드할 데이터를 준비한다.

===================================

further question

 

 

 

 

=============================

퀴즈

어려운거 딱히 없었음

==================================

피어세션

벡터 내적은 왜 배웠을까? 수직인거 이용해서 차원 늘리려고..?

np.where를 써보자

reduce는 좀 어렵다.

try except

Linear 관련 개념 너무 어려우면 https://www.youtube.com/watch?v=TxIVr-nk1so&list=PLlMkM4tgfjnLSOjrEJN31gZATbcj_MpUm&index=6 여기 설명을 들어보자

 

고용보험 일용직이라도 들어놓기

 

앞으로의 피어세션 활동은 일단 공통적으로 leetcode 한문제씩 풀고, 나머진 자기가 하고 싶은거 해보기로 했다.

============================