(4주차) 3월28일
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import tensorflow.experimental.numpy as tnp
tnp.experimental_enable_numpy_behavior()
- $loss=(\frac{1}{2}\beta-1)^2$를 최소하는 $\beta$를 컴퓨터를 활용하여 구하는 문제를 생각해보자. (우리는 답을 알고 있긴 함 $\beta=2$)
- 비판1: [-10,10]이외에 해가 존재하면?
- 이 예제의 경우는 운좋게 [-10,10]에서 해가 존재했음
- 하지만 임의의 고정된 $x,y$에 대하여 $loss(\beta)=(x\beta-y)^2$ 의 형태의 해가 항상 [-10,10]에서 존재한다는 보장은 없음
- 해결책: 더 넓게 많은 범위를 탐색하자?
- 비판2: 효율적이지 않음
- 알고리즘을 요약하면 결국 -10부터 10까지 작은 간격으로 조금씩 이동하며 loss를 조사하는 것이 grid search의 아이디어
- $\to$ 생각해보니까 $\beta=2$인 순간 $loss=(\frac{1}{2}\beta-1)^2=0$이 되어서 이것보다 작은 최소값은 존재하지 않는다(제곱은 항상 양수이어야 하므로)
- $\to$ 따라서 $\beta=2$ 이후로는 탐색할 필요가 없다
(1) 임의의 초기값을 선정하고 loss를 계산한다.
- $\beta=-5 \to loss(-5)=(-5/2-1)^2=12.25$
(-5/2-1)**2
(2) 임의의 초기값에서 좌우로 약간씩 이동해보고 loss를 계산한다.
- 왼쪽으로 이동: $\beta=-5.01,\quad loss(5.01)=12.285025$
- 오른쪽으로 이동: $\beta=-4.99, \quad loss(-4.99)=12.215025 $
(-5.01 /2 -1)**2
(-4.99 /2 -1)**2
(3) (2)의 결과를 보고 어느쪽으로 이동하는것이 유리한지 따져보고 유리한 방향으로 이동한다.
- $\beta=-4.99$ 로 이동
(4) (2)-(3) 의 과정을 반복한다. 왼쪽/오른쪽 모두 가봐도 유리한 지점이 없다면 알고리즘을 멈춘다.
- 알고리즘이 멈추는 지점은 $\beta=2$이다. 왜냐하면 이경우 왼쪽으로 가도, 오른쪽으로 가도 현재 손실함수값보다 크기 때문.
- 이 알고리즘은 $loss=(x\beta-y)^2$의 꼴에서 $[-10,10]$ 이외의 지점에 해가 존재하여도 적절하게 해를 찾을 것.
- 또한 비효율적으로 $\beta=2$ 이후에도 탐색을 반복하지 않는다.
- 알고리즘해석
(2)의 의미: 미분을 하라는 뜻
(3)의 의미: update
- 미분계수의 의미
- 미분계수가 양수이다: 왼쪽으로 이동 = 빼기 0.01
- 미분계수가 음수이다: 오른쪽으로 이동 = 더하기 0.01
- 수식화
$$\beta_{next} = \begin{cases} \beta_{old} - 0.01 & loss'(\beta_{old})>0 \\ \beta_{old} + 0.01 & loss'(\beta_{old})<0 \end{cases}$$
- 동일하게 0.01씩 이동하는게 맞을까?
_beta = np.linspace(-10,5)
plt.plot(_beta,(_beta/2-1)**2)
- 위의 그림에서 $\beta=-10$ 일 경우의 접선의 기울기는 $-6$이고 $\beta=-4$ 일때 접선의 기울기는 $-3$이다.
$\because loss = (0.5\beta-1)^2 \to loss' = 0.5\beta-1$
- $\beta=-10$에서 0.01만큼 이동했다면 $\beta=-4$에서 0.005만큼 이동해야함
- 아이디어를 수식화하자!
$$\beta_{next} \leftarrow \beta_{old} -\alpha \left[\frac{\partial}{\partial \beta} loss(\beta)\right]_{\beta=\beta_{old}}$$
- 아까 수식이랑 좀 다르다? 달라보이지만 $\beta_{old}$를 이동시켜 $\beta_{next}$를 만든다는 개념은 같음
- $\alpha>0$
- $\alpha$의 의미: 한번 업데이트할때 움직이는 보폭
- $\alpha=\frac{0.01}{6}$ 로 만약 설정하면 $\beta=-10$일때 오른쪽으로 0.01움직임
(개선한 알고리즘을 이용한 풀이)
iter1: beta=-10 출발
beta = tf.Variable(-10.0)
with tf.GradientTape(persistent=True) as tape:
loss = (beta/2-1)**2
tape.gradient(loss,beta)
alpha=0.01/6
beta.assign_sub(alpha*tape.gradient(loss,beta))
iter2 beta=-9.99
beta
with tf.GradientTape(persistent=True) as tape:
loss = (beta/2-1)**2
beta.assign_sub(alpha*tape.gradient(loss,beta))
- 왜 tf.Variable의 메소드에 assign_add, assign_sub 정도만 있는지?
- persistenct도 왜 디폴트가 False인지?
for문
(수업용)
beta = tf.Variable(-10.0)
alpha=0.01/6
for k in range(10000):
with tf.GradientTape(persistent=True) as tape:
loss = (beta/2-1)**2
beta.assign_sub(alpha*tape.gradient(loss,beta))
beta
(시도1)
beta = tf.Variable(-10.0)
alpha=0.01/6
for k in range(100):
with tf.GradientTape(persistent=True) as tape:
loss = (beta/2-1)**2
beta.assign_sub(alpha*tape.gradient(loss,beta))
beta
(시도2)
beta = tf.Variable(-10.0)
alpha=0.01/6
for k in range(1000):
with tf.GradientTape(persistent=True) as tape:
loss = (beta/2-1)**2
beta.assign_sub(alpha*tape.gradient(loss,beta))
beta
- 목표: 아래의 학습과정을 시각화해보자.
beta = tf.Variable(-10.0)
alpha=0.01/6
for k in range(100):
with tf.GradientTape(persistent=True) as tape:
loss = (beta/2-1)**2
beta.assign_sub(alpha*tape.gradient(loss,beta))
beta
- 환경설정
plt.rcParams["animation.html"]="jshtml"
from matplotlib import animation
- 도화지와 네모틀 생성
fig = plt.figure() # fig 는 도화지
ax = fig.add_subplot() # ax 네모틀
fig
- 도화지와 네모틀는 포함관계에 있음.
fig.axes
id(fig.axes[0])
id(ax)
- 네모틀(ax)의 특수기능(=메소드)중에는 plot이 있음. 이것은 또 어떤 오브젝트를 생성함
pnts, = ax.plot([1,2,3],[3,4,5],'or')
pnts
fig
- pnts 오브젝트: x,y data를 변경해보자.
pnts.get_xdata()
pnts.get_ydata()
pnts.set_ydata([4,4,4])
pnts.get_ydata()
fig
- 에니매이션
def animate(i):
if i%2 == 0:
pnts.set_ydata([3,4,5])
else:
pnts.set_ydata([4,4,4])
ani=animation.FuncAnimation(fig,animate,frames=30)
ani
예비학습끝
- 다시 학습과정 시각화 문제로 돌아오자.
beta_lst = [-10.0,-9.00,-8.00]
loss_lst = [(-10.0/2-1)**2,(-9.00/2-1)**2,(-8.00/2-1)**2]
fig = plt.figure()
ax = fig.add_subplot()
_beta= np.linspace(-15,19)
ax.plot(_beta,(_beta/2-1)**2)
fig
pnts, = ax.plot(beta_lst[0],loss_lst[0],'ro')
def animate(i):
pnts.set_xdata(beta_lst[:(i+1)])
pnts.set_ydata(loss_lst[:(i+1)])
ani=animation.FuncAnimation(fig,animate,frames=3)
ani
- 학습과정을 beta_lst, loss_lst로 저장하자.
beta_lst = []
loss_lst = []
beta = tf.Variable(-10.0)
alpha=0.01/6
beta.numpy()
beta_lst.append(beta.numpy())
loss_lst.append((beta.numpy()/2-1)**2)
for k in range(100):
with tf.GradientTape(persistent=True) as tape:
loss = (beta/2-1)**2
beta.assign_sub(alpha*tape.gradient(loss,beta))
beta_lst.append(beta.numpy())
loss_lst.append((beta.numpy()/2-1)**2)
fig = plt.figure() # fig 는 도화지
ax = fig.add_subplot()
ax.plot(_beta,(_beta/2-1)**2)
pnts, = ax.plot(beta_lst[0],loss_lst[0],'or')
ani=animation.FuncAnimation(fig,animate,frames=100)
ani
- alpha를 조정한다!! ($\alpha=0.1$)
beta_lst = []
loss_lst = []
beta = tf.Variable(-10.0)
alpha=0.1
beta_lst.append(beta.numpy())
loss_lst.append((beta.numpy()/2-1)**2)
for k in range(100):
with tf.GradientTape(persistent=True) as tape:
loss = (beta/2-1)**2
beta.assign_sub(alpha*tape.gradient(loss,beta))
beta_lst.append(beta.numpy())
loss_lst.append((beta.numpy()/2-1)**2)
fig = plt.figure() # fig 는 도화지
ax = fig.add_subplot()
ax.plot(_beta,(_beta/2-1)**2)
pnts, = ax.plot(beta_lst[0],loss_lst[0],'or')
ani=animation.FuncAnimation(fig,animate,frames=100)
ani
- alpha를 더 크게하면? ($\alpha=1$)
beta_lst = []
loss_lst = []
beta = tf.Variable(-10.0)
alpha=1
beta_lst.append(beta.numpy())
loss_lst.append((beta.numpy()/2-1)**2)
for k in range(100):
with tf.GradientTape(persistent=True) as tape:
loss = (beta/2-1)**2
beta.assign_sub(alpha*tape.gradient(loss,beta))
beta_lst.append(beta.numpy())
loss_lst.append((beta.numpy()/2-1)**2)
fig = plt.figure() # fig 는 도화지
ax = fig.add_subplot()
ax.plot(_beta,(_beta/2-1)**2)
pnts, = ax.plot(beta_lst[0],loss_lst[0],'or')
ani=animation.FuncAnimation(fig,animate,frames=100)
ani
- alpha는 학습속도를 의미함. $\to$ 빨리배우는게 좋으니까 학습률이 크면 무조건 좋은거아닌가? $\to$ 아니에용
(예시1) 너무 큰 학습률의 비효율성 ($\alpha=3.9$)
beta_lst = []
loss_lst = []
beta = tf.Variable(-10.0)
alpha=3.9
beta_lst.append(beta.numpy())
loss_lst.append((beta.numpy()/2-1)**2)
for k in range(100):
with tf.GradientTape(persistent=True) as tape:
loss = (beta/2-1)**2
beta.assign_sub(alpha*tape.gradient(loss,beta))
beta_lst.append(beta.numpy())
loss_lst.append((beta.numpy()/2-1)**2)
fig = plt.figure() # fig 는 도화지
ax = fig.add_subplot()
ax.plot(_beta,(_beta/2-1)**2)
pnts, = ax.plot(beta_lst[0],loss_lst[0],'or')
ani=animation.FuncAnimation(fig,animate,frames=100)
ani
(예시2) 너무 큰 학습률의 위험성 ($\alpha=4.05$)
beta_lst = []
loss_lst = []
beta = tf.Variable(-10.0)
alpha=4.05
beta_lst.append(beta.numpy())
loss_lst.append((beta.numpy()/2-1)**2)
for k in range(100):
with tf.GradientTape(persistent=True) as tape:
loss = (beta/2-1)**2
beta.assign_sub(alpha*tape.gradient(loss,beta))
beta_lst.append(beta.numpy())
loss_lst.append((beta.numpy()/2-1)**2)
fig = plt.figure() # fig 는 도화지
ax = fig.add_subplot()
ax.plot(_beta,(_beta/2-1)**2)
pnts, = ax.plot(beta_lst[0],loss_lst[0],'or')
ani=animation.FuncAnimation(fig,animate,frames=100)
ani
경사하강법을 이용하여 $y=(x-1)^2$의 최소값을 구하고 이를 애니메이션으로 시각화하라. (100번정도에 수렴하도록 적당한 학습률을 설정할것)