본문 바로가기
BOOSTCAMP AI TECH/2주차_AI를 위한 수학

[BOOSTCAMP AI TECH] 7일차_경사하강법

by 이민우 2021. 1. 26.
728x90
반응형
  • 강의 목록

-경사하강법 (순한맛)

-경사하강법 (매운맛)


  • 요약

강의

 

딥러닝에서 사용될 경사하강법의 개념을 배우고, 이를 코드로 구현하는 방법을 학습했다.

 

피어세션

 

전날 수업에서 배운 내용을 복습하고, 과제 풀이의 시간을 가졌다.

또한 내일부터는 효율적인 학습을 위해 새로운 방식으로 피어세션을 진행할 것을 결정했다.


  • 학습정리

미분 (Differentiation)

  • 변수의 움직임에 따른 함수값의 변화를 측정하기 위한 도구
  • 최적화에 많이 사용된다.
  • 미분은 변화율의 극한(Limit)으로 정의한다.
  • 파이썬에서는 sympy.diff(symm.poly(다항함수), x)로 쉽게 계산할 수 있다.
import sympy as sym
from sympy.abc import x

x = sym.Symbol('x')
xx = sym.diff(sym.poly(x**2 + 2*x + 3), x) #f(x) = x^2 + 3x + 3 미분값
print(xx) #Poly(2*x + 2, x, domain='ZZ')

x, y= sym.symbols('x y')
xx = sym.diff(sym.poly(x**2 + 2*x*y + 3) + sym.cos(x + 2*y), x)
#f(x,y) = x^2 + 2xy + 3 + cos(x + 2y)
print(xx) #2*x + 2*y - sin(x + 2*y)

*도함수 : 어떤 함수 안에 포함된 값의 각각이 0에 한없이 가까워지는 극한값을 구하는 함수

*함수 f(x)의 도함수 f'(x)를 구하는 것을 함수 f(x)를 미분한다라고 한다.

 

 

미분의 활용

  • 미분은 주어진 점 (x, f(x))에서의 접선의 기울기를 구하는 식이다.
  • 한 점에서 기울기를 알면 어느 방향으로 점을 움직여야 함수값이 증가 혹은 감소하는 지 알 수 있다.
  • 미분값을 더하면 함수값이 증가하고, 빼면 감소한다.
  • 미분을 더하여 함수의 극대값의 위치를 구하는 것을 경사상승법 (gradient ascent)라고 한다.
  • 미분값을 빼서 함수의 극소값의 위치를 구하는 것을 경사하강법 (gradient descent)라고 한다.
  • 경사상승법과 경사하강법 모두 극값에 도달하면 움직임을 멈춘다.

 

#경사하강법 알고리즘
import sympy as sym
from sympy.abc import x
import numpy as np

def func(val) :
    '''
    input :
        val : x에 들어갈 입력값
    output :
        fun.subs(x, val) : 방정식에 val을 넣은 결과값
        fun : 방정식
    '''
    fun = sym.poly(x**2 + 2*x + 3)
    #subs : 특정 symbol에 값을 넣어준다. 값뿐 아니라 다른 변수의 조합으로 넣어도 된다.
    return fun.subs(x, val), fun

def func_gradient(fun, val) :
    '''
    input
        fun : 함수가 들어가있는 함수
        val : 시작점
    output
        diff.subs(x, val) : 미분한 도함수에 val을 넣은 결과값
        diff : 미분한 도함수
    '''
    _, 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) :
    '''
    input :
        fun : 미분을 계산하는 함수
        init_point : 시작점
        lr_rate : 학습률 : 1e-2 = 1*10^-2 = 0.01
        epsilon : 알고리즘 종료조건 : 1e-5 = 1*10^-5 = 0.00001
    '''
    cnt = 0 #연산 횟수
    val = init_point
    diff, _ = func_gradient(fun, init_point)
    
    while np.abs(diff) > epsilon : #미분값이 목표치보다 작아지면 탐색 종료
        val = val - lr_rate * diff #현재보다 작은 값 탐색
        diff, _ = func_gradient(fun, val) #미분값 업데이트
        cnt += 1
    
    print('함수 : {}, 연산 횟수 : {}, 최소점 : ({} {})'.format(fun(val)[1], cnt, val, fun(val)[0]))
    #함수 : Poly(x**2 + 2*x + 3, x, domain='ZZ'), 연산 횟수 : 561, 최소점 : (-1.00000490961823 2.00000000002410)

gradient__descent(fun=func, init_point=np.random.uniform(-2,2))

*학습률은 알고리즘이 잘 수렴할지 아닐지를 결정할 중요한 변수이기 때문에 조심히 다루어야 한다.

*학습률이 너무 낮으면 최적점에 도달하는 시간이 오래 걸린다.

*학습률이 너무 높으면 최적점에서 멀어져 발산하게 된다.

 

 

변수가 벡터일 경우

  • 미분은 변수의 움직임에 따른 함수값의 변화를 측정하기 위한 도구
  • 최적화에 많이 사용한다.
  • 만약 벡터가 입력인 다변수 함수의 경우 편미분 (Partial differentiation)을 사용한다.
  • 각 변수 별로 편미분을 계산한 그레디언트 벡터를 이용해 경사하강법과 경사상승법에 사용할 수 있다.

*다변수 함수 : 독립변수가 2개 이상인 함수

*다변수 함수를 미분하는 법

*f(x,y)에서 x를 미분하는 것을 ‘x에 대한 편미분이라고 칭한다.

 

 

그래디언트 벡터 (Gradient Vector, f)

  • 다변수 함수에서 변수 별로 편미분을 계산한 결과
  • ‘▽f’ 는 가장 빨리 증가하는 방향을 표시
  • ‘-▽f’ 는 가장 빨리 감소하는 방향을 표시
  • 경사하강법 알고리즘을 그대로 사용하나, 절대값 대신 노름(Norm)을 계산해 종료조건을 설정한다.

 

*Norm : 크기의 일반화로 ||x||로 표현한다.

 

  • 미분을 알면 어떤 함수의 최소값과 최대값을 구할 수있다.
  • 이를 토대로 경사하강법을 이용하면 어떤 함수의 극소, 극대값을 구할 수 있다.

*eiI번째 값만 1이고 나머지는 0인 단위 벡터

*이 단위벡터를 활용해 특정 I번째 벡터에만 변화를 주어 I번째 방향에서의 변수의 변화율만 계산이 가능

 

 

선형회귀분석

  • n개의 변수로 이루어진 데이터를 선형모델로 해석하면 선형회귀식을 찾을 수 있다.
  • 파이썬의 np.linalg.pinv 함수로 구할 수 있다.

 

 

 

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

  • 선형회귀의 목적식은 ∥y − Xβ∥2이고 이를 최소화하는 β를 찾아야 한다.
  • 다음의 그레디언트 벡터를 구해야 한다.

*y Xβ∥2가 아닌(y Xβ∥2)^2를 최소화해도 된다

  • 목적식을 최소화하는 β를 구하는 경사하강법 알고리즘은 다음과 같다.

*람다로 수렴속도를 조절한다.

*t번째 벡터에서 계속 경사하강법을 적용하면 목적식을 최소화하는 벡터를 계산한다.

 

 

 

경사하강법 기반 선형회귀 알고리즘

#경사하강법 알고리즘
import sympy as sym
from sympy.abc import x
import numpy as np

def func(val) :
    '''
    input :
        val : x에 들어갈 입력값
    output :
        fun.subs(x, val) : 방정식에 val을 넣은 결과값
        fun : 방정식
    '''
    fun = sym.poly(x**2 + 2*x + 3)
    #subs : 특정 symbol에 값을 넣어준다. 값뿐 아니라 다른 변수의 조합으로 넣어도 된다.
    return fun.subs(x, val), fun

def func_gradient(fun, val) :
    '''
    input
        fun : 함수가 들어가있는 함수
        val : 시작점
    output
        diff.subs(x, val) : 미분한 도함수에 val을 넣은 결과값
        diff : 미분한 도함수
    '''
    _, 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) :
    '''
    input :
        fun : 미분을 계산하는 함수
        init_point : 시작점
        lr_rate : 학습률 : 1e-2 = 1*10^-2 = 0.01
        epsilon : 알고리즘 종료조건 : 1e-5 = 1*10^-5 = 0.00001
    '''
    cnt = 0 #연산 횟수
    val = init_point
    diff, _ = func_gradient(fun, init_point)
    
    while np.abs(diff) > epsilon : #미분값이 목표치보다 작아지면 탐색 종료
        val = val - lr_rate * diff #현재보다 작은 값 탐색
        diff, _ = func_gradient(fun, val) #미분값 업데이트
        cnt += 1
    
    print('함수 : {}, 연산 횟수 : {}, 최소점 : ({} {})'.format(fun(val)[1], cnt, val, fun(val)[0]))
    #함수 : Poly(x**2 + 2*x + 3, x, domain='ZZ'), 연산 횟수 : 561, 최소점 : (-1.00000490961823 2.00000000002410)

gradient__descent(fun=func, init_point=np.random.uniform(-2,2))

*종료조건을 미리 설정해 지정된 시간만 수행

*반복 횟수를 지정해 그리드 탐색에서 수렴하는데 너무 오래 걸리는 모델을 막는다.

*반복 횟수가 너무 작으면 경사하강법 알고리즘이 수렴하지 못할 수 있다.

*학습률과 학습횟수가 중요한 파라미터이다.

 

 

경사하강법은 만능인가?

  • 경사하강법은 선형회귀처럼 미분가능하고 볼록한 함수에 대해서만 수렴이 보장된다.
  • 볼록한 함수는 그레디언트 벡터가 항상 최소점을 향하기 때문이다.
  • 비선형회귀의 경우 목적식이 볼록하지 않기에 수렴이 항상 보장되지 않는다.
  • 딥러닝을 사용하는 경우 대부분의 목적식이 볼록함수가 아니기에 수렴이 보장되지 않는다.
  • 이런 경우 확률적 경사하강법을 사용한다.

 

 

확률적 경사하강법 (Stochastic gradient descent, SGD)

  • 모든 데이터를 사용해서 업데이트하는 대신 한 개 혹은 일부의 데이터만 사용해서 업데이트한다.
  • 볼록이 아닌 목적식은 SGD를 통해 최적화할 수 있다.
  • SGD가 만능은 아니지만, 딥러닝의 경우 SGD가 경사하강법보다 실증적으로 더 낫다.
  • SGD는 데이터의 일부(미니배치)로 패러미터를 업데이트하기에 연산량이 감소해 연산자원을 좀 더 효율적으로 활용하는데 도움이 된다.

 

 

SGD의 원리

1) 미니배치 연산

  • 경사하강법은 전체데이터를 가지고 목적식의 그레디언트 벡터를 계산한다.
  • SGD는 미니배치를 가지고 그레디언트 벡터를 계산한다.
  • 매번 다른 미니배치를 확률적으로 선택하여 사용하기에 목적식 모양과 곡선 모양이 바뀐다.
  • SGD는 볼록이 아닌 목적식에서도 사용 가능하므로 경사하강법보다 머신러닝 학습에 더 효율적이다.

*다만 경사하강법처럼 곧바로 최저점으로 가는 것이 아닌, 조금 왔다갔다하는 모양이 있다.

*수렴 속도 자체는 경사하강법보다 빠르나 배치 사이즈가 너무 작으면 더 느림.

 

2) 하드웨어

  • 모든 데이터를 업데이트 하는 경사하강법으로 사이즈가 큰 데이터를 계산하면 메모리 부족으로 Out-of-memory가 발생할 수 있다.
  • 미니배치로 쪼개서 사용할 경우 일부 데이터만 사용함으로 빠른 연산과 하드웨어 한계 극복이 가능하다.

 


  • 피어세션 회의 내용

강의 복습

 

#Numpy

  • 파이썬의 고성능 과학 계산용 패키지
  • Matrix, Vector와 같은 Array 연산의 사실상의 표준
  • 리스트에 비해 빠르고 메모리 효율적이다.
  • 반복문 없이 데이터 배열에 대한 처리를 지원한다.
  • 딥러닝의 선형대수에 관련한 다양한 기능을 제공한다.
  • List에서의 슬라이싱을 그대로 사용할 수 있다.
  • 랜덤 샘플링의 활용이 가능하다.

*균등분포 : numpy.random.uniform

*정규분포 : numpy.random.normal

 

#벡터

  • 숫자를 원소로 가지는 리스트 혹은 배열
  • 원점으로부터 상대적 위치를 표현하며, 공간에서 한 점을 의미한다.
  • 벡터에 숫자(스칼라)를 곱하면 길이만 변한다.
  • 벡터끼리 같은 모양을 가지면 성분곱 계산이 가능하다. (행렬곱셈과 다름을 주의)

 

#Norm

  • 원점으로부터의 거리
  • L1 Norm : 각 성분의 변화량의 절댓값을 모두 더한 값
  • L2 Norm : 피타고라스 정리를 이용해 유클리드 거리를 계산한 값

*Norm은 종류에 따라 기하학적 성질이 달라진다.

*L2 Norm을 사용하면 두 벡터 사이의 거리를 이용해 각도 계산이 가능하다. (2 코사인 법칙)

*머신러닝에서는 각 성질이 필요할 때가 있어 둘 다 사용한다.

 

#내적

  • 정사영의 길이를 벡터 y의 길이 ||y||만큼 조정한 값
  • 유사도 측정에 사용한다.

 

#행렬

  • 벡터를 원소로 가지는 2차원 배열.
  • 행과 열 인덱스를 가진다.
  • 같은 모양끼리는 합과 뺄셈이 가능하다.
  • 성분곱도 가능한데, 벡터와 같다.
  • 행렬곱셈은 I번째 행벡터와 j번째 열벡터 사이의 내적을 의미한다.
  • Numpy의 inner함수는 I 번째 행벡터와 j번째 행벡터 사이의 내적을 의미한다. 이는 수학에서 말하는 내적과 다르다.
  • 행과 열의 숫자가 같고 행렬식이 0이 아니면 역행렬을 계산할 수 있다.
  • 만약 역행렬을 계산할 수 없으면 유사역행렬 또는 Numpy.linalg.pinv(x) 함수를 사용해 무어로펜즈 역행렬을 사용할 수 있다.

 

#np.dot vs np.inner

  • np.dot : 두 행렬의 행렬곱 (i번째 행과 j번째 열의 곱)
  • 두 행렬이 2D Array (Matrix)일 경우 matmul 혹은 @ 사용을 권장.
  • np.inner : I번째 행과 j번째 행의 곱
  • numpy.dot(a,b)는 numpy.inner(a,b.T)와 같다.

 

#torch

  • torch에도 dot과 inner 메소드가 존재한다.
  • torch로 dot으로 2차원 이상의 배열 계산시 에러가 발생한다.
  • matmul은 2차원 이상의 배열 연산에 활용이 가능하다.

 

 

과제 리뷰

 

#벡터 과제

  • 벡터 과제는 다음의 함수를 적절히 활용하면 쉽게 풀이가 가능하다.
numpy.linalg.norm(x, ord=1, axis=0) -노름 구하기
-ord의 값에 따라 Norm1, Norm2
별도로 리턴된다.
-axis0이면 행, 1이면 열
Math.sqrt(x) -x의 제곱근 출력
numpy.arccos(x) -x의 아크코사인 출력

 

#행렬 과제

  • 행렬 과제도 다음의 함수를 적절히 활용하면 쉽게 풀이가 가능하다.
numpy.dot(x, y) -xy의 행렬곱
numpy.linalg.pinv(x) -x의 무어로펜즈 역함수

 

 

논의

 

#inner함수와 수학에서의 내적의 차이점?

  •  확실하게 결론이 나지 못했음으로 조교님께 별도 질문

 

#고유값 분해 (Eigen decomposition)

  • 행렬을 정형화된 형태로 분해함으로써 고유값 및 고유 벡터로 표현
  • 대각화 가능 행렬만이 인수분해 가능 (정방행렬)

 

#특이값 분해 (Singular Value Decomposition)

  • 고유값 분해처럼 행렬을 대각화하는 방법
  • 행렬이 정방행렬이든 아니든 관계없이 모든 행렬에 적용할 수 있다.

 

 

그라운드 룰

 

#전날 수업을 금일 복습하는 식으로 피어세션을 진행중이나, 크게 효율적이지 못함

#비교적 시간이 많은 초반에 이후에 학습할 내용들을 준비하기로 결정

#모든 조원들은 머신러닝 완벽 가이드 (권철민)’을 구매 후 해당 책을 이용해 스터디 진행

#모든 조원들이 책을 수령하기 전까지는 지금까지와 마찬가지로 복습 및 자유주제 발표

 


  • 해야할 일

수학에 약하다보니 오늘 배운 내용을 확실하게 되뇌일 필요가 있다.


 

728x90
반응형