728x90

2023-11-13 48th Class

Gradient-based Learning

토막 상식 (?)
layer가 지날떄마다 데이터 분포가 바뀜-> batch normalization
레이어 지날때마다 data standardization하면 성능이 높아질 수있다는 논문
하지만 내부적인 다른 문제를 해결해줬기 때문에 성능이 높아진거다라는 연구 발표

#️⃣ The Object of GBL

Gradient Based Learning의 목적은 임의의 x에서 시작하여 함숫값 y를 최소로 만드는 x를 찾는 것

SGD = Stochastic Gradient Descent

graph

Pasted image 20231113120035.png|500

  • argmin은 오른쪽에 쓴 함수가 가장 작아지는 값, 즉 min() 임
  • 위의 1/10 x2 함수에서는 min값이 0임

gradient based leaerning에서는 식을 사용해서 기울기를 업데이트함

  • f=-3 에서 업데이트: f’(-3) = -0.6 이므로 업데이트 하면 x:= -3 -(-0.6) = -2.4로 x값이 바뀜
  • f=-2 에서 업데이트: f’(-2) = -0.4 이므로 업데이트 하면 x:= -2.4 -(-0.4) = -1.6으로 x값이 바뀜
  • f=-1 에서 업데이트: f’(-1) = -0.2 이므로 업데이트 하면 x:= -1 -(-0.2) = -0.8으로 x값이 바뀜
  • f=1 에서 업데이트: f’(1) = 0.2 이므로 업데이트 하면 x:= -3 -(-0.6) = -2.4로 x값이 바뀜
  • f=2 에서 업데이트: f’(2) = 0.4 이므로 업데이트 하면 x:= 2 - 0.4 = 1.6
  • f=3 에서 업데이트: f’(3) = 0.6 이므로 업데이트 하면 x:= 3 - 0.6 = 2.4

초기의 식을 iterative update하여(반복 적용) x*에 근접한 값으로 update됨

#️⃣ gradient based learning 연습

연습.1 임의의 x에 대해 를 반복 적용하면 x* = 0에 가까워지는 것을 확인

연습.2 ITERATIONS를 증가시키면 같은 x에서 시작했을 때, 더욱 x*에 가까워 지는 것을 확인

code

def f(x):  
    return 1/10 * x**2  
  
  
def df_dx(x):  
    return 1/5 * x  
  
  
x = 3  
ITERATIONS = 20  
  
print(f"Initial x: {x}")  
  
for iter in range(ITERATIONS):  
    dy_dx = df_dx(x)  
    x = x - dy_dx  
    print(f"{iter+1}-th x: {x:.4f}")

'''
Initial x: 3
1-th x: 2.4000
2-th x: 1.9200
3-th x: 1.5360
4-th x: 1.2288
5-th x: 0.9830
6-th x: 0.7864
7-th x: 0.6291
8-th x: 0.5033
9-th x: 0.4027
10-th x: 0.3221
11-th x: 0.2577
12-th x: 0.2062
13-th x: 0.1649
14-th x: 0.1319
15-th x: 0.1056
16-th x: 0.0844
17-th x: 0.0676
18-th x: 0.0540
19-th x: 0.0432
20-th x: 0.0346
'''

연습.3 x* 가 다른 함수 에 대해서 GBL을 통해 x* 을 찾아가도록 프로그래밍

도함수 f’(x) = 1/5(x-2)

visualization full code

import matplotlib.pyplot as plt  
import numpy as np  
  
  
def f(x):  
    return 1/10 * x**2  
  
  
def df_dx(x):  
    return 1/5 * x  
  
  
x = 3  
ITERATIONS = 20  
x_track = list()  
x_track.append(x)  
y_track = list()  
y_track.append(f(x))  
print(f"Initial x: {x}")  
  
for iter in range(ITERATIONS):  
    dy_dx = df_dx(x)  
    x = x - dy_dx  
    x_track.append(x)  
    y_track.append(f(x))  
    print(f"{iter+1}-th x: {x:.4f}")  
  
  
function_x = np.linspace(-5, 5, 100)  
function_y = f(function_x)  
  
fig, axes = plt.subplots(2, 1, figsize=(8, 4))  
axes[0].plot(function_x, function_y)  
axes[0].scatter(x_track, y_track, c=range(ITERATIONS+1) , cmap='rainbow')  
axes[0].set_xlabel('x', fontsize=15)  
axes[0].set_ylabel('y', fontsize=15)  
  
  
axes[1].plot(x_track, marker='o')  
axes[1].set_xlabel('iteration', fontsize=15)  
axes[1].set_ylabel('x', fontsize=15)  
fig.tight_layout()  
plt.show()

gbl 231113.png

#️⃣ gradient exploding problem

함수의 계수가 높아지면 이동 보폭이 커짐

def gradient_exploding():  
    def f1(x): return 1/10 * x**2  
    def df1_dx(x): return 1/5 * x  
  
    def f2(x): return 1/5 * x**2  
    def df2_dx(x): return 2/5 * x  
  
    def f3(x): return 1/3 * x**2  
    def df3_dx(x): return 2/3 * x  
  
    x1, x2, x3 = 3, 3, 3  
    ITERATIONS = 10  
    x_track1, y_track1 = [x1], [f1(x1)]  
    x_track2, y_track2 = [x2], [f2(x2)]  
    x_track3, y_track3 = [x3], [f3(x3)]  
  
    for iter in range(ITERATIONS):  
        dy1_dx = df1_dx(x1)  
        dy2_dx = df2_dx(x2)  
        dy3_dx = df3_dx(x3)  
  
        x1 = x1 - dy1_dx  
        x2 = x2 - dy2_dx  
        x3 = x3 - dy3_dx  
  
        x_track1.append(x1)  
        y_track1.append(f1(x1))  
        x_track2.append(x2)  
        y_track2.append(f2(x2))  
        x_track3.append(x3)  
        y_track3.append(f3(x3))  
  
    fig, axes = plt.subplots(3, 1, figsize=(6, 6))  
    function_x = np.linspace(-5, 5, 100)  
  
    axes[0].plot(function_x, f1(function_x), label='f1(x)', color='C0')  
    axes[1].plot(function_x, f2(function_x), label='f1(x)', color='C1')  
    axes[2].plot(function_x, f3(function_x), label='f1(x)', color='C2')  
  
    axes[0].scatter(x_track1, y_track1, c=range(ITERATIONS+1), cmap='rainbow')  
    axes[1].scatter(x_track2, y_track2, c=range(ITERATIONS+1), cmap='rainbow')  
    axes[2].scatter(x_track3, y_track3, c=range(ITERATIONS+1), cmap='rainbow')  
  
    for ax in axes: ax.tick_params(labelsize=15)  
    fig.tight_layout()  
    plt.show()  
  
gradient_exploding()

exploding 231110.png

함수의 계수가 높아지면 발생하는 문제
계수가 증가할 수 록 x*에 접근하는 속도가 빨라짐
계수가 크니까 미분계수가 크게 잡혀서 업데이트 되는 스텝이 커짐

함수의 계수를 더 높여 다음과 같이 설정하면
x가 지그재그를 돌면서 minima에 접근함

함수의 계수가 더 높은경우

값이 튀어나가게 됨 -> Gradient Exploding Problem

#️⃣ gradient exploding code (bigger)

code

def gbl2():  
    def f(x): return 2 * x ** 2  
    def df_dx(x): return 4 * x  
  
    x = 3  
    ITERATIONS = 3  
    x_track, y_track = [x], [f(x)]  
    print(f"Initial x: {x}")  
  
    for iter in range(ITERATIONS):  
        dy_dx = df_dx(x)  
        x = x - dy_dx  
        x_track.append(x)  
        y_track.append(f(x))  
        print(f"{iter + 1}-th Iteration")  
        print(f"{dy_dx = :.4f}")  
        print(f"next x: {x:.4f}\n")

gbl2()

'''
Initial x: 3
1-th Iteration
dy_dx = 12.0000
next x: -9.0000

2-th Iteration
dy_dx = -36.0000
next x: 27.0000

3-th Iteration
dy_dx = 108.0000
next x: -81.0000
'''
  • 미분 계수의 절댓값이 너무 커서 x* 방향으로 가긴 하지만, x* 보다 훨씬 먼 값으로 업데이트 됨
  • 이 현상이 반복되면 x는 minima로 학습되지 않고,
  • x와 미분계수는 무한대로 발산하게 됨

#️⃣ 학습률 (learning rate)

0 < a < 1의 값을 미분 계수에 곱하여 x가 발산하는 문제를 막음
이렇게 미분계수를 suppress 시키는 값을 learning rate(학습률)이라고 함

def gbl2_lr():  
    def f(x): return 2 * x ** 2  
    def df_dx(x): return 4 * x  
  
    x = 3  
    ITERATIONS = 3  
    LR = 0.1  
    x_track, y_track = [x], [f(x)]  
    print(f"Initial x: {x}")  
  
    for iter in range(ITERATIONS):  
        dy_dx = df_dx(x)  
        # x = x - dy_dx 기존  
        x = x - LR * dy_dx  
        x_track.append(x)  
        y_track.append(f(x))  
        print(f"{iter + 1}-th Iteration")  
        print(f"{dy_dx = :.4f}")  
        print(f"next x: {x:.4f}\n")  
  
gbl2_lr()

'''
Initial x: 3
1-th Iteration
dy_dx = 12.0000
next x: 1.8000

2-th Iteration
dy_dx = 7.2000
next x: 1.0800

3-th Iteration
dy_dx = 4.3200
next x: 0.6480
'''

learning rate을 도입하면 발산하던 f(x) = 2x 의 학습이 정상적으로 변하는 것을 알 수 있음

즉 learning rate은 학습의 안정성을 위해 미분계수를 얼만큼 suppress시킬지 결정하는 값

α = 0.1 ⟶ 미분계수의 10%만 반영하여 update
α = 0.01 ⟶ 미분계수의 1%만 반영하여 update

#️⃣ Multivariate Case

함숫값을 최소로 만들기 위해선 임의의 x1, x2 에서 출발하여 x1, x2 모두 함숫값을 감소시키는 방향으로 학습되어야 함

x1, x2가 각각 함숫값 y를 최소로 만드는 방향으로 학습되는 식

함숫값은 점점 작아지며, x1는 x1* = 0 에 가까워지고 x2는 x2* = 0에 가까워짐

multivariate case code

# multivariate case  
def f(x1, x2): return x1**2 + x2**2  
  
  
def df_dx(x): return 2*x  
  
x1, x2 = 3, -2  
ITERATIONS = 10  
LR = 0.1  
  
print(f"Initial (x1, x2): ({x1}, {x2})")  
print(f"Initial y: {f(x1, x2)}")  
  
for iter in range(ITERATIONS):  
    dy_dx1 = df_dx(x1)  
    dy_dx2 = df_dx(x2)  
  
    x1 = x1 - LR * dy_dx1  
    x2 = x2 - LR * dy_dx2  
  
    print(f"{iter +1}-th (x1, x2): ({x1:.3f}, {x2:.3f})")  
    print(f"y: {f(x1, x2):.3f}")

'''
Initial (x1, x2): (3, -2)
Initial y: 13
1-th (x1, x2): (2.400, -1.600)
y: 8.320
2-th (x1, x2): (1.920, -1.280)
y: 5.325
3-th (x1, x2): (1.536, -1.024)
y: 3.408
4-th (x1, x2): (1.229, -0.819)
y: 2.181
5-th (x1, x2): (0.983, -0.655)
y: 1.396
6-th (x1, x2): (0.786, -0.524)
y: 0.893
7-th (x1, x2): (0.629, -0.419)
y: 0.572
8-th (x1, x2): (0.503, -0.336)
y: 0.366
9-th (x1, x2): (0.403, -0.268)
y: 0.234
10-th (x1, x2): (0.322, -0.215)
y: 0.150
'''

#️⃣ contour plot

contour plot: 등고선

# multivariate case  
def f(x1, x2): return x1**2 + x2**2  
  
  
def df_dx(x): return 2*x  
  
  
x1, x2 = 3, -2  
ITERATIONS = 10  
LR = 0.1  
x1_track, x2_track = [x1], [x2]  
y_track = [f(x1, x2)]  
  
print(f"Initial (x1, x2): ({x1}, {x2})")  
print(f"Initial y: {f(x1, x2)}")  
  
for iter in range(ITERATIONS):  
    dy_dx1 = df_dx(x1)  
    dy_dx2 = df_dx(x2)  
  
    x1 = x1 - LR * dy_dx1  
    x2 = x2 - LR * dy_dx2  
  
    x1_track.append(x1); x2_track.append(x2); y_track.append(f(x1, x2))  
    print(f"{iter +1}-th (x1, x2): ({x1:.3f}, {x2:.3f})")  
    print(f"y: {f(x1, x2):.3f}")  
  
  
# Bivariate Function을 Contour Plot으로 시각화 하기  
def f(x1, x2): return x1**2 + x2**2  
  
function_x1 = np.linspace(-5, 5, 100)  
function_x2 = np.linspace(-5, 5, 100)  
  
function_X1, function_X2 = np.meshgrid(function_x1, function_x2)  
function_Y = np.log(f(function_X1, function_X2))  
  
fig, ax = plt.subplots(figsize=(10, 10))  
ax.contour(function_X1, function_X2, function_Y, levels=100, cmap='Reds_r')  
ax.plot(x1_track, x2_track, marker='o', label='Optimization Path')  
  
ax.set_xlabel("x", fontsize=15)  
ax.set_ylabel("y", fontsize=15)  
ax.tick_params(labelsize=15)  
fig.tight_layout()  
plt.show()

'''
Initial (x1, x2): (3, -2)
Initial y: 13
1-th (x1, x2): (2.400, -1.600)
y: 8.320
2-th (x1, x2): (1.920, -1.280)
y: 5.325
3-th (x1, x2): (1.536, -1.024)
y: 3.408
4-th (x1, x2): (1.229, -0.819)
y: 2.181
5-th (x1, x2): (0.983, -0.655)
y: 1.396
6-th (x1, x2): (0.786, -0.524)
y: 0.893
7-th (x1, x2): (0.629, -0.419)
y: 0.572
8-th (x1, x2): (0.503, -0.336)
y: 0.366
9-th (x1, x2): (0.403, -0.268)
y: 0.234
10-th (x1, x2): (0.322, -0.215)
y: 0.150
'''

contour 231113.png

딥러닝에서 bayes’ theorem에서 하이퍼파라미터 최적화에 사용됨


Backpropagation

#️⃣ Training Model’s Parameters

딥러닝 모델은 수많은 뉴런으로 이루어져있음
모든 뉴런은 weight과 bias로 이루어져 있음

모델 학습은 모델이 목적에 맞는 출력을 내도록 weight과 bias를 학습한다는 것을 의미

J 는 yhat과 y의 차이를 수치화하는 함수
모델의 파라미터는 J를 감소시키기 위한 방향으로 학습되어야 함

-> Gradient Based Learning (GBL)을 이용해 loss를 줄이는 방법으로 학습할 수 있음

#️⃣ Chain Rule을 이용하여 합성함수의 미분계수 구하기

!Pasted image 20231113222924.png

각 미분계수를 곱하여 최종 입력에 대한 최초 출력의 미분계수 구하기

# Chain Rule을 이용하여 미분계수 구하기  
  
class Function1:  
    def forward(self, x):  
        z = x - 2  
        return z  
  
    def backward(self, dy_dz):  
        dz_dx = 1  
        dy_dx = dy_dz * dz_dx  
        return dy_dz  
  
  
class Function2:  
    def forward(self, z):  
        self.z = z  
        y = 2 * (z**2)  
        return y  
  
    def backward(self):  
        dy_dz = 4 * self.z  
        return dy_dz


function1, function2 = Function1(), Function2()  
  
x = 5  
z = function1.forward(x)  
print(f"{z=}")  
y = function2.forward(z)  
print(f"{y=}")  
  
dy_dx = function1.backward(function2.backward())  
print(f"{dy_dx=}")

'''
z=3
y=18
dy_dx=12
'''

한개의 class로 묶어서 실행하기

class Function:  
    def __init__(self):  
        self.func1 = Function1()  
        self.func2 = Function2()  
  
    def forward(self, x):  
        z = self.func1.forward(x)  
        print(f"{z=}")  
        y = self.func2.forward(z)  
        print(f"{y=}")  
        return y  
  
    def backward(self):  
        dy_dz = self.func2.backward()  
        print(f"{dy_dz=}")  
        dy_dx = self.func1.backward(dy_dz)  
        print(f"{dy_dx=}")
        return dy_dx  
  
  
fn = Function()  
y = fn.forward(5)  
dy_dx = fn.backward()

'''
z=3
y=18
dy_dz=12
dy_dx=12
'''

쪽지 시험

20231114_100004.jpg

20231114_100015.jpg

반응형