파이토치를 이용하여 회귀모형 학습하기

- (1/5) 회귀모형 소개, 손실 함수

- (2/5) 경사하강법, 경사하강법을 이용하여 회귀계수 1회 업데이트

- (3/5) 회귀계수 반복 업데이트

- (4/5) 학습률

- (5/5) 사과영상

import torch
import numpy as np
import matplotlib.pyplot as plt

로드맵

  • 회귀분석 $\to$ 로지스틱 $\to$ 심층신경망(DNN) $\to$ 합성곱신경망(CNN)

- model: $y_i= w_0+w_1 x_i +\epsilon_i = 2.5 + 4x_i +\epsilon_i, \quad i=1,2,\dots,n$

  • w라는 로테이션을 많이 사용하는 딥러닝

- model: ${\bf y}={\bf X}{\bf W} +\boldsymbol{\epsilon}$

  • ${\bf y}=\begin{bmatrix} y_1 \\ y_2 \\ \dots \\ y_n\end{bmatrix}, \quad {\bf X}=\begin{bmatrix} 1 & x_1 \\ 1 & x_2 \\ \dots \\ 1 & x_n\end{bmatrix}, \quad {\bf W}=\begin{bmatrix} 2.5 \\ 4 \end{bmatrix}, \quad \boldsymbol{\epsilon}= \begin{bmatrix} \epsilon_1 \\ \dots \\ \epsilon_n\end{bmatrix}$
torch.manual_seed(202150754)
n=100
ones=torch.ones(n)
x,_ = torch.randn(n).sort() # 배열하면 tendor,index가 반환됨. 필요한 x만 반환
X = torch.vstack([ones,x]).T
W = torch.tensor([2.5,4])
ϵ = torch.randn(n)*0.5
y = X@W + ϵ  #@는 벡터를 곱하라는 뜻..!
ytrue = X@W # 우리가 알고 싶은 것은 평균 직선
plt.plot(x,y,'o') #우리가 관측한 값
plt.plot(x,ytrue,'--') # 우리가 추론하고 싶은 값
[<matplotlib.lines.Line2D at 0x7f14326a1f10>]
학습

- 파란점만 주어졌을때, 주황색 점선을 추론하는것.

- 좀 더 정확하게 말하면 given data로 $\begin{bmatrix} \hat{w}_0 \\ \hat{w}_1 \end{bmatrix}$를 최대한 $\begin{bmatrix} 2.5 \\ 4 \end{bmatrix}$와 비슷하게 찾는것. (2.5와 4는 true의 값)

  • given data : $\big\{(x_i,y_i) \big\}_{i=1}^{n}$

  • parameter: ${\bf W}=\begin{bmatrix} w_0 \\ w_1 \end{bmatrix}$

  • estimated parameter: ${\bf \hat{W}}=\begin{bmatrix} \hat{w}_0 \\ \hat{w}_1 \end{bmatrix}$

plt.plot(x,y,'o') # 그림을 보고 '적당한' 추세를 찾는 과정
[<matplotlib.lines.Line2D at 0x7f1432614970>]

- 시도: $(\hat{w}_0,\hat{w}_1)=(-5,10)$을 선택하여 선을 그려보고 적당한지 판단.

  • $\hat{y}_i=-5 +10 x_i$ 와 같이 $y_i$의 값을 적합시키겠다는 의미
plt.plot(x,y,'o')
plt.plot(x,-5+10*x,'--') # 그림을 보고 판단해보는 단계
[<matplotlib.lines.Line2D at 0x7f14325f38b0>]

- 벡터 표현으로주황색 추세선을 계산

What = torch.tensor([-5.0,10.0]) # float으로 선언해야 함!!!
plt.plot(x,y,'o')
plt.plot(x,X@What,'--')
[<matplotlib.lines.Line2D at 0x7f1432546c10>]
파라미터를 학습하는 방법, 즉 적당한 선에 맞춰가는 과정

- 이론적으로 추론 (회귀 분석)

- 컴퓨터의 반복계산을 이용하여 추론(경사하강법)

(1) initial value: 임의의 선을 그어봄

What = torch.tensor([-5.0,10.0],requires_grad=True)
What
tensor([-5., 10.], requires_grad=True)
  • 처음에는 ${\bf \hat{W}}=\begin{bmatrix} \hat{w}_0 \\ \hat{w}_1 \end{bmatrix}=\begin{bmatrix} -5 \\ 10 \end{bmatrix} $ 를 대입해서 주황색 점선을 적당히 그려보자는 의미

  • 끝에 requires_grad=True는 나중에 미분에 사용되기 위함.

yhat=X@What
# yhat을 구하면 단지 X(미분X)*What(미분O) = 미분 옵션이 있는 텐션이 되어버린다.
yhat.data # 미분 옵션이 사라짐.
tensor([-27.9716, -26.0391, -25.8951, -24.1830, -23.6405, -23.1161, -22.0441,
        -21.9913, -21.4959, -21.2860, -20.4771, -19.6991, -19.1434, -18.0758,
        -17.5390, -17.4888, -16.8212, -16.6630, -16.2503, -14.3326, -13.8527,
        -13.6397, -13.5228, -13.2096, -12.8514, -12.8461, -12.7527, -12.2431,
        -12.0267, -11.7990, -11.6495, -11.5587, -11.5497, -11.1709, -10.9643,
        -10.7969, -10.7696, -10.7324, -10.6567, -10.4404, -10.1049,  -9.9527,
         -9.7916,  -9.3899,  -9.2762,  -8.2773,  -8.0850,  -7.9550,  -7.8498,
         -7.7767,  -7.6419,  -7.2295,  -7.1686,  -6.9773,  -6.9454,  -6.6435,
         -5.6597,  -5.5200,  -5.4562,  -5.3640,  -4.9588,  -4.9111,  -4.5447,
         -3.9894,  -3.6367,  -3.0762,  -2.4928,  -2.4512,  -2.1695,  -2.0062,
         -1.7060,   0.1909,   0.5915,   0.9467,   1.3453,   1.4359,   2.0752,
          2.4723,   2.5368,   2.7189,   2.7902,   2.8337,   3.2249,   3.7238,
          3.8636,   3.9170,   3.9852,   5.0601,   5.7496,   6.0569,   7.0621,
          7.2674,   7.6805,   7.9669,   8.4266,   9.6044,   9.6791,  10.7418,
         12.6324,  18.9507])
plt.plot(x,y,'o')
plt.plot(x,yhat.data,'--')
[<matplotlib.lines.Line2D at 0x7f143277e040>]

(2) 첫 번째 수정: 추세선의 적당한 정도를 판단하여 적당한 선으로 업데이트

- '적당한 정도'를 판단하기 위하 장치로서 loss function 도입

$loss=\sum_{i=1}^{n}(y_i-\hat{y}_i)^2=\sum_{i=1}^{n}(y_i-(\hat{w}_0+\hat{w}_1x_i))^2$

$=({\bf y}-{\bf\hat{y}})^\top({\bf y}-{\bf\hat{y}})=({\bf y}-{\bf X}{\bf \hat{W}})^\top({\bf y}-{\bf X}{\bf \hat{W}})$

구현하기 편하게 하기 위해 벡터로 구현

- loss 함수의 특징

  • $y_i \approx \hat{y}_i$ 일수록 loss값이 작다.
  • $y_i \approx \hat{y}_i$ 이 되도록 $(\hat{w}_0,\hat{w}_1)$을 잘 찍으면 loss값이 작다.
  • (중요) 주황색 점선이 적당할수록 loss값이 작다.
loss = torch.sum((y-yhat)**2) #  = (y-yhat)@(y-yhat)
loss
tensor(11003.1260, grad_fn=<SumBackward0>)

- $loss(=11003.1260)$을 줄이는, 혹은 없애는 것이 목표 $\to$ 아예 모든 조합 $(\hat{w}_0,\hat{w}_1)$에 대하여 가장 작은 $loss$ 찾기

- 문제의 치환

  • 적당해 보이는 주황색 선을 찾자 $\to$ $loss(w_0,w_1)$을 최소로 하는 $(w_0,w_1$의 값을 찾기

- $loss(w_0,w_1)$를 최소로 하는 $(w_0,w_1)$ 구하는 것으로 수정된 목표

  • 단순한 수학문제가 되었다. 마치 $loss(w)=w^2-2w+3$ 을 최소화하는 $w$를 찾으라는 것과 같음.

- 경사하강법, 벡터미분 사용!

경사하강법

경사하강법 아이디어(1차원)

(step 1) 임의의 점을 찍는다.

(step 2) 그 점에서 순간기울기를 구한다. (접선) <-- 미분

(step 3) 순간기울기(= 미분계수)의 부호를 살펴보고 부호와 반대방향으로 움직인다. (순간기울기와 같은 방향으로 움직이면 점점 커질테니까.)

(Tip) 기울기의 절대값 크기와 비례하여 보폭(= 움직이는 정도)을 조절한다.

경사하강법 아이디어(2차원)

(step 1) 임의의 점을 찍는다.

(step 2) 그 점에서 순간기울기를 구한다. (접평면) <-- 편미분

(step 3) 순간기울기(= 여러개의 미분계수)의 부호를 살펴보고 부호와 반대방향으로 각각 움직인다. (순간기울기와 같은 방향으로 움직이면 점점 커질테니까.)

(Tip) 기울기의 절대값 크기와 비례하여 보폭(= 움직이는 정도)을 각각 조절한다.

loss를 줄이도록 $W$를 개선하는 방법

- $수정값 \leftarrow 원래값 - 기울어진 크기(= 미분계수) \times \alpha$

  • 여기에서 $\alpha$는 전체적인 보폭의 크기를 결정한다. 즉, $\alpha$값이 클수록 한 번의 update에 움직이는 양이 크다.

- ${\bf W} \leftarrow {\bf W} - \alpha \times \frac{\partial}{\partial {\bf W}}loss(w_0,w_1)$

  • 마이너스의 의미 기울기의 부호를 보고 반대방향으로 움직여라

  • $\frac{\partial}{\partial {\bf W}}loss(w_0,w_1):$ 기울기의 절대값 크기와 비례하여 움직이는 정도를 조정하라. (속도의 조절)

  • $\alpha$의 의미: 전체적인 보폭의 속도를 조절, $\alpha$가 크면 전체적으로 빠르게 움직인다. 다리의 길이로 비유할 수 있다.


- 목표: $loss(=11003.1260)$ 값을 줄이는 것.

- 방법: 경사하강법

- 경사하강법으로 loss를 줄이기 위해서는 $\frac{\partial}{\partial {\bf W}}loss(w_0,w_1)$의 계산이 필요한데, 이를 위해서 벡터미분이 필요하다.

  • requires_grad=True를 가진 텐서로 미분.
loss=torch.sum((y-yhat)**2)= torch.sum((y-X@What)**2)
# 이었고 
What=torch.tensor([-5.0,10.0],requires_grad=True)
# 이므로 결국 What으로 미분하라는 의미. 
# 미분한 식이 나오는 것이 아니고, 
# 그 식에 (-5.0, 10.0)을 대입한 계수값이 계산됨.
loss.backward() # requires_grad=True를 가진 텐서로 미분하라는 의미
# 단지 미분 계수가 계산되어 있음
# 정확하게 말하면 미분을 활용하여 $(-5,10)$에서의 순간기울기를 구했다는 의미임. 
What.grad.data
tensor([-1730.4250,  1485.8135])
  • 이것이 의미하는 건 $(-5,10)$에서의 순간기울기가 $([-1730.4250, 1485.8135])$ 이라는 의미 (각각 (양수, 음수)로 움직여야 함)

- 직접 계산하여 검증

  • $loss(w_0,w_1)=(y-\hat{y})^\top (y-\hat{y})=(y-XW)^\top (y-XW)$

  • $\frac{\partial}{\partial W}loss(w_0,w_1)=-2X^\top y+2X^\top X W$

-2 * X.T @ y + 2 * X.T @ X @ What # = What.grad.data
tensor([-1730.4250,  1485.8135], grad_fn=<AddBackward0>)
alpha=0.001
print('수정 전: ' + str(What.data)) # 미분 옵션 없애기!
print('수정하는 폭: ' + str(-alpha*What.grad.data))
print('수정 후: ' + str(What.data-alpha*What.grad.data))
print('*참값: (2.5,4)')
수정 전: tensor([-5., 10.])
수정하는 폭: tensor([ 1.7304, -1.4858])
수정 후: tensor([-3.2696,  8.5142])
*참값: (2.5,4)
Wbefore = What.data
Wafter = What.data-alpha*What.grad.data
Wbefore, Wafter
(tensor([-5., 10.]), tensor([-3.2696,  8.5142]))
plt.plot(x,y,'o')
plt.plot(x,X@Wbefore,'--b') # 수정 전 파란 점선
plt.plot(x,X@Wafter,'--r') # 수정 후 빨간 점선
plt.title("before: blue // after: red")
Text(0.5, 1.0, 'before: blue // after: red')

(3) Learn (=estimate $\bf\hat{W})$:

What= torch.tensor([-5.0,10.0],requires_grad=True)
alpha=0.001 
for epoc in range(30): 
    What.grad=None
    yhat=X@What 
    loss=torch.sum((y-yhat)**2)
    loss.backward()  # What으로 미분하는 과정
    What.data = What.data-alpha * What.grad.data # 적정한 선으로 Update!
What.data # true 값은 2.5,4
tensor([2.5248, 3.9898])
plt.plot(x,y,'o')
plt.plot(x,(X@What.data),'--') # 순수 데이터만 뽑기 위해 .data꼭 붙이기
plt.plot(x,(X@np.array([2.5,4])),'-') # 거의 비슷해서 한 선으로 보임
[<matplotlib.lines.Line2D at 0x7f14329ed7f0>]

파라메터의 수정 과정을 관찰할 수 없나.(학습과정 모니터링)

- 기록 해보기

losses=[] # 기록하고 싶은 것 1
yhats = [] # 기록하고 싶은 것 2
Whats = [] # 기록하고 싶은 것 3
What= torch.tensor([-5.0,10.0],requires_grad=True)
alpha=0.001 
for epoc in range(30):
    Whats=Whats+[What.data.tolist()] # What을 list화 해서 저장
    What.grad=None
    yhat=X@What 
    yhats=yhats+[yhat.data.tolist()]
    loss=torch.sum((y-yhat)**2)
    losses=losses+[loss.item()]
    loss.backward()  # What으로 미분하는 과정
    What.data = What.data-alpha * What.grad.data # 적정한 선으로 Update!

- $\hat{y}$ 관찰

plt.plot(x,y,'o')
plt.plot(x,yhats[5],'--') # 5번 업데이트된 추세선
[<matplotlib.lines.Line2D at 0x7f14324f35b0>]
plt.plot(x,y,'o')
plt.plot(x,yhats[10],'--') # 10번 업데이트된 추세선
[<matplotlib.lines.Line2D at 0x7f143244d340>]

- $\hat{\bf{W}}$

losses
[11003.1259765625,
 6417.849609375,
 3748.93798828125,
 2195.045166015625,
 1290.041015625,
 762.7489624023438,
 455.38189697265625,
 276.1114196777344,
 171.48153686523438,
 110.3656005859375,
 74.63228607177734,
 53.71556854248047,
 41.45503234863281,
 34.25670623779297,
 30.02236557006836,
 27.52593421936035,
 26.050209045410156,
 25.175155639648438,
 24.654428482055664,
 24.34327507019043,
 24.156478881835938,
 24.043743133544922,
 23.975299835205078,
 23.93347930908203,
 23.907733917236328,
 23.891769409179688,
 23.881786346435547,
 23.8754940032959,
 23.871488571166992,
 23.868919372558594]
plt.plot(losses)
[<matplotlib.lines.Line2D at 0x7f14324200d0>]

Animation

plt.rcParams['figure.figsize'] = (10,4) # 크기
plt.rcParams["animation.html"] = "jshtml"  # 애니메이션 나오게 하는 옵션

from matplotlib import animation

fig = plt.figure()
ax1 = fig.add_subplot(1, 2, 1)
ax2 = fig.add_subplot(1, 2, 2, projection='3d')

## ax1: 왼쪽그림 
ax1.plot(x,y,'o')
line, = ax1.plot(x,yhats[0]) 
## ax2: 오른쪽그림 
_w0 = np.arange(-6, 11, 0.5) ## 파란색곡면을 그리는 코드 (시작) 
_w1 = np.arange(-6, 11, 0.5)
w1,w0 = np.meshgrid(_w1,_w0)
l=w0*0
for i in range(len(_w0)):
    for j in range(len(_w1)):
        l[i,j]=torch.sum((y-_w0[i]-_w1[j]*x)**2)
ax2.plot_surface(w0, w1, l, rstride=1, cstride=1, color='b',alpha=0.35) ## 파란색곡면을 그리는 코드(끝) 
ax2.scatter(2.5,4,torch.sum((y-2.5-4*x)**2),s=200,color='red',marker='*') ## 최소점을 표시하는 코드 (붉은색 별) 
ax2.scatter(np.array(Whats)[0,0],np.array(Whats)[0,1],losses[0],color='b') ## 업데이트되는 What을 표시하는 점 (파란색 동그라미) 
ax2.azim = 40  ## 3d plot의 view 조절 
ax2.dist = 8   ## 3d plot의 view 조절 
ax2.elev = 5   ## 3d plot의 view 조절 

def animate(epoc):
    line.set_ydata(yhats[epoc])
    ax2.scatter(np.array(Whats)[epoc,0],np.array(Whats)[epoc,1],losses[epoc],color='grey')
    return line

ani = animation.FuncAnimation(fig, animate, frames=30)
plt.close()
ani
</input>

$\alpha$에 대하여 ($\alpha$는 학습률)

(1) $\alpha$가 너무 작다면?$\to$ 비효율적이다.

losses = [] # 기록하고 싶은것 1  
yhats = [] # 기록하고 싶은것 2 
Whats = [] # 기록하고 싶은것 3 

alpha=0.0001 

What= torch.tensor([-5.0,10.0],requires_grad=True)
for epoc in range(30): 
    Whats=Whats+[What.data.tolist()] 
    What.grad=None
    yhat=X@What 
    yhats=yhats+[yhat.data.tolist()]
    loss=torch.sum((y-yhat)**2)
    losses = losses + [loss.item()]
    loss.backward() 
    What.data = What.data-alpha * What.grad.data 

fig = plt.figure()
ax1 = fig.add_subplot(1, 2, 1)
ax2 = fig.add_subplot(1, 2, 2, projection='3d')

## ax1: 왼쪽그림 
ax1.plot(x,y,'o')
line, = ax1.plot(x,yhats[0]) 
## ax2: 오른쪽그림 
_w0 = np.arange(-6, 11, 0.5) ## 파란색곡면을 그리는 코드 (시작) 
_w1 = np.arange(-6, 11, 0.5)
w1,w0 = np.meshgrid(_w1,_w0)
l=w0*0
for i in range(len(_w0)):
    for j in range(len(_w1)):
        l[i,j]=torch.sum((y-_w0[i]-_w1[j]*x)**2)
ax2.plot_surface(w0, w1, l, rstride=1, cstride=1, color='b',alpha=0.35) ## 파란색곡면을 그리는 코드(끝) 
ax2.scatter(2.5,4,torch.sum((y-2.5-4*x)**2),s=200,color='red',marker='*') ## 최소점을 표시하는 코드 (붉은색 별) 
ax2.scatter(np.array(Whats)[0,0],np.array(Whats)[0,1],losses[0],color='b') ## 업데이트되는 What을 표시하는 점 (파란색 동그라미) 
ax2.azim = 40  ## 3d plot의 view 조절 
ax2.dist = 8   ## 3d plot의 view 조절 
ax2.elev = 5   ## 3d plot의 view 조절 

def animate(epoc):
    line.set_ydata(yhats[epoc])
    ax2.scatter(np.array(Whats)[epoc,0],np.array(Whats)[epoc,1],losses[epoc],color='grey')
    return line

ani = animation.FuncAnimation(fig, animate, frames=30)
plt.close()
ani
</input>

(1) $\alpha$가 너무 크다면? $\to$ 다른의미에서 비효율적이다 + 위험하다..

losses = [] # 기록하고 싶은것 1  
yhats = [] # 기록하고 싶은것 2 
Whats = [] # 기록하고 싶은것 3 

alpha=0.0083

What= torch.tensor([-5.0,10.0],requires_grad=True)
for epoc in range(30): 
    Whats=Whats+[What.data.tolist()] 
    What.grad=None
    yhat=X@What 
    yhats=yhats+[yhat.data.tolist()]
    loss=torch.sum((y-yhat)**2)
    losses = losses + [loss.item()]
    loss.backward() 
    What.data = What.data-alpha * What.grad.data 

fig = plt.figure()
ax1 = fig.add_subplot(1, 2, 1)
ax2 = fig.add_subplot(1, 2, 2, projection='3d')

## ax1: 왼쪽그림 
ax1.plot(x,y,'o')
line, = ax1.plot(x,yhats[0]) 
## ax2: 오른쪽그림 
_w0 = np.arange(-6, 11, 0.5) ## 파란색곡면을 그리는 코드 (시작) 
_w1 = np.arange(-6, 11, 0.5)
w1,w0 = np.meshgrid(_w1,_w0)
l=w0*0
for i in range(len(_w0)):
    for j in range(len(_w1)):
        l[i,j]=torch.sum((y-_w0[i]-_w1[j]*x)**2)
ax2.plot_surface(w0, w1, l, rstride=1, cstride=1, color='b',alpha=0.35) ## 파란색곡면을 그리는 코드(끝) 
ax2.scatter(2.5,4,torch.sum((y-2.5-4*x)**2),s=200,color='red',marker='*') ## 최소점을 표시하는 코드 (붉은색 별) 
ax2.scatter(np.array(Whats)[0,0],np.array(Whats)[0,1],losses[0],color='b') ## 업데이트되는 What을 표시하는 점 (파란색 동그라미) 
ax2.azim = 40  ## 3d plot의 view 조절 
ax2.dist = 8   ## 3d plot의 view 조절 
ax2.elev = 5   ## 3d plot의 view 조절 

def animate(epoc):
    line.set_ydata(yhats[epoc])
    ax2.scatter(np.array(Whats)[epoc,0],np.array(Whats)[epoc,1],losses[epoc],color='grey')
    return line

ani = animation.FuncAnimation(fig, animate, frames=30)
plt.close()
ani
</input>

(3) $\alpha=0.0085$ 아예 모형을 벗어나버린다.

losses = [] # 기록하고 싶은것 1  
yhats = [] # 기록하고 싶은것 2 
Whats = [] # 기록하고 싶은것 3 

alpha=0.0085

What= torch.tensor([-5.0,10.0],requires_grad=True)
for epoc in range(30): 
    Whats=Whats+[What.data.tolist()] 
    What.grad=None
    yhat=X@What 
    yhats=yhats+[yhat.data.tolist()]
    loss=torch.sum((y-yhat)**2)
    losses = losses + [loss.item()]
    loss.backward() 
    What.data = What.data-alpha * What.grad.data 

fig = plt.figure()
ax1 = fig.add_subplot(1, 2, 1)
ax2 = fig.add_subplot(1, 2, 2, projection='3d')

## ax1: 왼쪽그림 
ax1.plot(x,y,'o')
line, = ax1.plot(x,yhats[0]) 
## ax2: 오른쪽그림 
_w0 = np.arange(-6, 11, 0.5) ## 파란색곡면을 그리는 코드 (시작) 
_w1 = np.arange(-6, 11, 0.5)
w1,w0 = np.meshgrid(_w1,_w0)
l=w0*0
for i in range(len(_w0)):
    for j in range(len(_w1)):
        l[i,j]=torch.sum((y-_w0[i]-_w1[j]*x)**2)
ax2.plot_surface(w0, w1, l, rstride=1, cstride=1, color='b',alpha=0.35) ## 파란색곡면을 그리는 코드(끝) 
ax2.scatter(2.5,4,torch.sum((y-2.5-4*x)**2),s=200,color='red',marker='*') ## 최소점을 표시하는 코드 (붉은색 별) 
ax2.scatter(np.array(Whats)[0,0],np.array(Whats)[0,1],losses[0],color='b') ## 업데이트되는 What을 표시하는 점 (파란색 동그라미) 
ax2.azim = 40  ## 3d plot의 view 조절 
ax2.dist = 8   ## 3d plot의 view 조절 
ax2.elev = 5   ## 3d plot의 view 조절 

def animate(epoc):
    line.set_ydata(yhats[epoc])
    ax2.scatter(np.array(Whats)[epoc,0],np.array(Whats)[epoc,1],losses[epoc],color='grey')
    return line

ani = animation.FuncAnimation(fig, animate, frames=30)
plt.close()
ani
</input>

(4) $\alpha=0.01$

losses = [] # 기록하고 싶은것 1  
yhats = [] # 기록하고 싶은것 2 
Whats = [] # 기록하고 싶은것 3 

alpha=0.01

What= torch.tensor([-5.0,10.0],requires_grad=True)
for epoc in range(30): 
    Whats=Whats+[What.data.tolist()] 
    What.grad=None
    yhat=X@What 
    yhats=yhats+[yhat.data.tolist()]
    loss=torch.sum((y-yhat)**2)
    losses = losses + [loss.item()]
    loss.backward() 
    What.data = What.data-alpha * What.grad.data 

fig = plt.figure()
ax1 = fig.add_subplot(1, 2, 1)
ax2 = fig.add_subplot(1, 2, 2, projection='3d')

## ax1: 왼쪽그림 
ax1.plot(x,y,'o')
line, = ax1.plot(x,yhats[0]) 
## ax2: 오른쪽그림 
_w0 = np.arange(-6, 11, 0.5) ## 파란색곡면을 그리는 코드 (시작) 
_w1 = np.arange(-6, 11, 0.5)
w1,w0 = np.meshgrid(_w1,_w0)
l=w0*0
for i in range(len(_w0)):
    for j in range(len(_w1)):
        l[i,j]=torch.sum((y-_w0[i]-_w1[j]*x)**2)
ax2.plot_surface(w0, w1, l, rstride=1, cstride=1, color='b',alpha=0.35) ## 파란색곡면을 그리는 코드(끝) 
ax2.scatter(2.5,4,torch.sum((y-2.5-4*x)**2),s=200,color='red',marker='*') ## 최소점을 표시하는 코드 (붉은색 별) 
ax2.scatter(np.array(Whats)[0,0],np.array(Whats)[0,1],losses[0],color='b') ## 업데이트되는 What을 표시하는 점 (파란색 동그라미) 
ax2.azim = 40  ## 3d plot의 view 조절 
ax2.dist = 8   ## 3d plot의 view 조절 
ax2.elev = 5   ## 3d plot의 view 조절 

def animate(epoc):
    line.set_ydata(yhats[epoc])
    ax2.scatter(np.array(Whats)[epoc,0],np.array(Whats)[epoc,1],losses[epoc],color='grey')
    return line

ani = animation.FuncAnimation(fig, animate, frames=30)
plt.close()
ani
</input>

(5) $\alpha=0.006$ 숙제

losses = [] # 기록하고 싶은것 1  
yhats = [] # 기록하고 싶은것 2 
Whats = [] # 기록하고 싶은것 3 

alpha=0.006

What= torch.tensor([-5.0,10.0],requires_grad=True)
for epoc in range(30): 
    Whats=Whats+[What.data.tolist()] 
    What.grad=None
    yhat=X@What 
    yhats=yhats+[yhat.data.tolist()]
    loss=torch.sum((y-yhat)**2)
    losses = losses + [loss.item()]
    loss.backward() 
    What.data = What.data-alpha * What.grad.data 

fig = plt.figure()
ax1 = fig.add_subplot(1, 2, 1)
ax2 = fig.add_subplot(1, 2, 2, projection='3d')

## ax1: 왼쪽그림 
ax1.plot(x,y,'o')
line, = ax1.plot(x,yhats[0]) 
## ax2: 오른쪽그림 
_w0 = np.arange(-6, 11, 0.5) ## 파란색곡면을 그리는 코드 (시작) 
_w1 = np.arange(-6, 11, 0.5)
w1,w0 = np.meshgrid(_w1,_w0)
l=w0*0
for i in range(len(_w0)):
    for j in range(len(_w1)):
        l[i,j]=torch.sum((y-_w0[i]-_w1[j]*x)**2)
ax2.plot_surface(w0, w1, l, rstride=1, cstride=1, color='b',alpha=0.35) ## 파란색곡면을 그리는 코드(끝) 
ax2.scatter(2.5,4,torch.sum((y-2.5-4*x)**2),s=200,color='red',marker='*') ## 최소점을 표시하는 코드 (붉은색 별) 
ax2.scatter(np.array(Whats)[0,0],np.array(Whats)[0,1],losses[0],color='b') ## 업데이트되는 What을 표시하는 점 (파란색 동그라미) 
ax2.azim = 40  ## 3d plot의 view 조절 
ax2.dist = 8   ## 3d plot의 view 조절 
ax2.elev = 5   ## 3d plot의 view 조절 

def animate(epoc):
    line.set_ydata(yhats[epoc])
    ax2.scatter(np.array(Whats)[epoc,0],np.array(Whats)[epoc,1],losses[epoc],color='grey')
    return line

ani = animation.FuncAnimation(fig, animate, frames=30)
plt.close()
ani
</input>

다루기 싫지만 해야하는 사소한 문제들

(A1) 손실함수

- $\sum_{i=1}^{n}(y_i-\hat{y}_i)^2$ 대신에

  • $\frac{1}{n}\sum_{i=1}^{n}(y_i-\hat{y}_i)^2$
    • 평균, 이 형태를 많이 쓴다.
  • $\frac{1}{2n}\sum_{i=1}^{n}(y_i-\hat{y}_i)^2$
    • 미분하면 2 사라짐

중 하나를 사용하여도 상관없다.

(A2) 별표로 표시된 점이 정말 $(2.5,4.0)$일까? $\Longleftrightarrow$ l이 정말 $w_0=2.5$, $w_1=4.0$에서 최소화 되는가?

- np.argmin 소개(최소화되는 인덱스 출력해주는 함수)

_a=np.array([1,2,0,2,3,4])
np.argmin(_a) # 인덱스 2(여기서는 0값)번이 최소값
2
np.argmin(l)
598

-무슨 값인지 모름.이런 값이 나오는 이유.

_X=np.array([[1,2,3],[1,-5,-5]])
_X
array([[ 1,  2,  3],
       [ 1, -5, -5]])
np.argmin(_X)
4
l.shape # 34*34=1156개 있음
(34, 34)

- array의 구조가 너무 컴퓨터 위주의 숫자임.. $\to$ np.unravel_index() 함수사용

np.unravel_index(4,_X.shape) # index로 반환해 달라 4 말고
(1, 1)

- 응용하면

np.unravel_index(np.argmin(l),l.shape)
(17, 20)
_w0[17],_w1[20] # 우리가 바라던 값이 나왔다
(2.5, 4.0)

- (2.5,4.0)에서 ;이 최소값을 가지는 것이 맞긴 함

- 그런데 이론적으로 그래야 하는 것은 아님

torch.sum((y-2.5-4.0*x)**2)
tensor(23.9743)
XX=np.matrix(X)
yy=np.matrix(y).T # (100,1)로 치환
yy.shape
(100, 1)
(XX.T*XX).I * XX.T * yy
matrix([[2.530568 ],
        [3.9915466]], dtype=float32)
torch.sum((y-2.530568-3.9915466*x)**2) # loss값이 더 작아진 모습
tensor(23.8640)
  • 진짜로 (2.530568,3.9915466) 에서의 로스가 더 작음

- $n$이 커질수록 (2.530568, 3.9915466) 의 값은 점점 (2.5,4.0)의 값에 가까워 진다.

(A3) 행벡터와 열벡터

- 아래의 매트릭스를 관찰하자.

XX
matrix([[ 1.        , -2.2971632 ],
        [ 1.        , -2.1039104 ],
        [ 1.        , -2.0895088 ],
        [ 1.        , -1.9183004 ],
        [ 1.        , -1.8640488 ],
        [ 1.        , -1.8116142 ],
        [ 1.        , -1.7044138 ],
        [ 1.        , -1.699133  ],
        [ 1.        , -1.6495895 ],
        [ 1.        , -1.6286039 ],
        [ 1.        , -1.5477134 ],
        [ 1.        , -1.469909  ],
        [ 1.        , -1.4143386 ],
        [ 1.        , -1.3075817 ],
        [ 1.        , -1.2539034 ],
        [ 1.        , -1.2488844 ],
        [ 1.        , -1.1821247 ],
        [ 1.        , -1.1662971 ],
        [ 1.        , -1.1250314 ],
        [ 1.        , -0.93326014],
        [ 1.        , -0.88526654],
        [ 1.        , -0.8639698 ],
        [ 1.        , -0.8522789 ],
        [ 1.        , -0.82096314],
        [ 1.        , -0.7851367 ],
        [ 1.        , -0.7846103 ],
        [ 1.        , -0.775272  ],
        [ 1.        , -0.72431004],
        [ 1.        , -0.7026662 ],
        [ 1.        , -0.67990315],
        [ 1.        , -0.6649538 ],
        [ 1.        , -0.65587115],
        [ 1.        , -0.6549699 ],
        [ 1.        , -0.6170914 ],
        [ 1.        , -0.5964267 ],
        [ 1.        , -0.5796867 ],
        [ 1.        , -0.576962  ],
        [ 1.        , -0.573241  ],
        [ 1.        , -0.5656716 ],
        [ 1.        , -0.5440386 ],
        [ 1.        , -0.51049155],
        [ 1.        , -0.49526608],
        [ 1.        , -0.4791573 ],
        [ 1.        , -0.43899277],
        [ 1.        , -0.42762285],
        [ 1.        , -0.32772934],
        [ 1.        , -0.30849513],
        [ 1.        , -0.2955021 ],
        [ 1.        , -0.28497764],
        [ 1.        , -0.2776678 ],
        [ 1.        , -0.26418892],
        [ 1.        , -0.22294523],
        [ 1.        , -0.21685947],
        [ 1.        , -0.19773111],
        [ 1.        , -0.19453613],
        [ 1.        , -0.16434576],
        [ 1.        , -0.06597225],
        [ 1.        , -0.05200045],
        [ 1.        , -0.04561628],
        [ 1.        , -0.03639911],
        [ 1.        ,  0.00411945],
        [ 1.        ,  0.00889459],
        [ 1.        ,  0.0455331 ],
        [ 1.        ,  0.10105584],
        [ 1.        ,  0.13632803],
        [ 1.        ,  0.19237518],
        [ 1.        ,  0.2507207 ],
        [ 1.        ,  0.2548768 ],
        [ 1.        ,  0.28304642],
        [ 1.        ,  0.29937938],
        [ 1.        ,  0.3294042 ],
        [ 1.        ,  0.51909333],
        [ 1.        ,  0.559151  ],
        [ 1.        ,  0.59467036],
        [ 1.        ,  0.63452995],
        [ 1.        ,  0.6435871 ],
        [ 1.        ,  0.7075169 ],
        [ 1.        ,  0.7472348 ],
        [ 1.        ,  0.753683  ],
        [ 1.        ,  0.77188945],
        [ 1.        ,  0.77901787],
        [ 1.        ,  0.78336984],
        [ 1.        ,  0.82249224],
        [ 1.        ,  0.87238336],
        [ 1.        ,  0.8863593 ],
        [ 1.        ,  0.8916975 ],
        [ 1.        ,  0.8985204 ],
        [ 1.        ,  1.006009  ],
        [ 1.        ,  1.0749605 ],
        [ 1.        ,  1.1056927 ],
        [ 1.        ,  1.2062144 ],
        [ 1.        ,  1.2267364 ],
        [ 1.        ,  1.268053  ],
        [ 1.        ,  1.2966905 ],
        [ 1.        ,  1.3426594 ],
        [ 1.        ,  1.460442  ],
        [ 1.        ,  1.4679053 ],
        [ 1.        ,  1.5741758 ],
        [ 1.        ,  1.763243  ],
        [ 1.        ,  2.3950703 ]], dtype=float32)
XX[:,1]
matrix([[-2.2971632 ],
        [-2.1039104 ],
        [-2.0895088 ],
        [-1.9183004 ],
        [-1.8640488 ],
        [-1.8116142 ],
        [-1.7044138 ],
        [-1.699133  ],
        [-1.6495895 ],
        [-1.6286039 ],
        [-1.5477134 ],
        [-1.469909  ],
        [-1.4143386 ],
        [-1.3075817 ],
        [-1.2539034 ],
        [-1.2488844 ],
        [-1.1821247 ],
        [-1.1662971 ],
        [-1.1250314 ],
        [-0.93326014],
        [-0.88526654],
        [-0.8639698 ],
        [-0.8522789 ],
        [-0.82096314],
        [-0.7851367 ],
        [-0.7846103 ],
        [-0.775272  ],
        [-0.72431004],
        [-0.7026662 ],
        [-0.67990315],
        [-0.6649538 ],
        [-0.65587115],
        [-0.6549699 ],
        [-0.6170914 ],
        [-0.5964267 ],
        [-0.5796867 ],
        [-0.576962  ],
        [-0.573241  ],
        [-0.5656716 ],
        [-0.5440386 ],
        [-0.51049155],
        [-0.49526608],
        [-0.4791573 ],
        [-0.43899277],
        [-0.42762285],
        [-0.32772934],
        [-0.30849513],
        [-0.2955021 ],
        [-0.28497764],
        [-0.2776678 ],
        [-0.26418892],
        [-0.22294523],
        [-0.21685947],
        [-0.19773111],
        [-0.19453613],
        [-0.16434576],
        [-0.06597225],
        [-0.05200045],
        [-0.04561628],
        [-0.03639911],
        [ 0.00411945],
        [ 0.00889459],
        [ 0.0455331 ],
        [ 0.10105584],
        [ 0.13632803],
        [ 0.19237518],
        [ 0.2507207 ],
        [ 0.2548768 ],
        [ 0.28304642],
        [ 0.29937938],
        [ 0.3294042 ],
        [ 0.51909333],
        [ 0.559151  ],
        [ 0.59467036],
        [ 0.63452995],
        [ 0.6435871 ],
        [ 0.7075169 ],
        [ 0.7472348 ],
        [ 0.753683  ],
        [ 0.77188945],
        [ 0.77901787],
        [ 0.78336984],
        [ 0.82249224],
        [ 0.87238336],
        [ 0.8863593 ],
        [ 0.8916975 ],
        [ 0.8985204 ],
        [ 1.006009  ],
        [ 1.0749605 ],
        [ 1.1056927 ],
        [ 1.2062144 ],
        [ 1.2267364 ],
        [ 1.268053  ],
        [ 1.2966905 ],
        [ 1.3426594 ],
        [ 1.460442  ],
        [ 1.4679053 ],
        [ 1.5741758 ],
        [ 1.763243  ],
        [ 2.3950703 ]], dtype=float32)
  • 정상적을 잘 선택되었다.

- 이제 XX에서 첫번째 row를 선택하고 싶다면?

XX[0,:]
matrix([[ 1.       , -2.2971632]], dtype=float32)

- X에 관심을 가져보자.

- 첫번째 row를 뽑고싶다면?

X[0,:]
tensor([ 1.0000, -2.2972])

- 두번째 col을 뽑고 싶다면?

X[:,1] # 왜 벡터형으로 나올까
tensor([-2.2972, -2.1039, -2.0895, -1.9183, -1.8640, -1.8116, -1.7044, -1.6991,
        -1.6496, -1.6286, -1.5477, -1.4699, -1.4143, -1.3076, -1.2539, -1.2489,
        -1.1821, -1.1663, -1.1250, -0.9333, -0.8853, -0.8640, -0.8523, -0.8210,
        -0.7851, -0.7846, -0.7753, -0.7243, -0.7027, -0.6799, -0.6650, -0.6559,
        -0.6550, -0.6171, -0.5964, -0.5797, -0.5770, -0.5732, -0.5657, -0.5440,
        -0.5105, -0.4953, -0.4792, -0.4390, -0.4276, -0.3277, -0.3085, -0.2955,
        -0.2850, -0.2777, -0.2642, -0.2229, -0.2169, -0.1977, -0.1945, -0.1643,
        -0.0660, -0.0520, -0.0456, -0.0364,  0.0041,  0.0089,  0.0455,  0.1011,
         0.1363,  0.1924,  0.2507,  0.2549,  0.2830,  0.2994,  0.3294,  0.5191,
         0.5592,  0.5947,  0.6345,  0.6436,  0.7075,  0.7472,  0.7537,  0.7719,
         0.7790,  0.7834,  0.8225,  0.8724,  0.8864,  0.8917,  0.8985,  1.0060,
         1.0750,  1.1057,  1.2062,  1.2267,  1.2681,  1.2967,  1.3427,  1.4604,
         1.4679,  1.5742,  1.7632,  2.3951])

- shape 비교

XX.shape,(XX[0,:]).shape,(XX[:,1]).shape
((100, 2), (1, 2), (100, 1))
X.shape,(X[0,:]).shape,(X[:,1]).shape
(torch.Size([100, 2]), torch.Size([2]), torch.Size([100]))
  • row-vec, col-vec의 구분없이 그냥 길이2인 벡터, 길이가 100인 벡터로 고려됨
  • row-vec, col-vec의 구분을 하려면 2차원이 필요한데 1차원으로 축소가 되면서 생기는 현상
  • 대부분의 경우 별로 문제가 되지 않음.
  • 수학적으로는 col-vec, row-vec를 엄밀하게 구분하는 것이 좋지만, 프로그래밍 효율을 생각하면 떄로는 구분이 모호한게 유리할 수도 있다.