Machine Learning

ML with pytorch

해파리냉채무침 2024. 4. 6. 15:57

출처: 차근차근 실습하며 배우는 파이토치 딥러닝 프로그래밍

 

경사하강법 구현 방법

예측 계산, 손실 계산, 경사 계산, 파라미터 수정 4개의 단계로 진행된다.

데이터 입력 및 변환

sampleData1 = np.array([
    [166, 58.7],
    [176.0, 75.7],
    [171.0, 62.1],
    [173.0, 70.4],
    [169.0,60.1]
])
print(sampleData1)

데이터를 불러와서 독립변수, 종속변수를 나눠준다

x = sampleData1[:,0]
y = sampleData1[:,1]
print(x) #독립변수
print(y) #종속변수

[166. 176. 171. 173. 169.]

[58.7 75.7 62.1 70.4 60.1]

X = x - x.mean()
Y = y - y.mean()
plt.scatter(X,  Y,  c='k',  s=50)
plt.xlabel('$X$')
plt.ylabel('$Y$')
#plt.title('데이터 가공 후 신장과 체중의 관계')
plt.show()

경사하강법의 대상이 될 값은 절댓값이 1을 넘지 않는 비교적 작은 값이 필요하다. 각 평균값을 빼주면 경사하강법이 수월해짐.

 

1) 예측 계산

데이터 X와 Y를 텐서 변수로 바꿔줌

X= torch.tensor(X).float()
Y= torch.tensor(Y).float()
print(X)
print(Y)

W= torch.tensor(1.0,requires_grad=True).float()
B= torch.tensor(1.0,requires_grad=True).float()

초기값은 1.0, 경사계산을 위해 자동으로 계산된 미분값이 저장되도록 requires_grad=True로 설정함.

def pred(X):
  return W*X +B
Yp = pred(X)
print(Yp)

tensor([-4., 6., 1., 3., -1.], grad_fn=<AddBackward0>)

params = {'W': W, 'B': B}
g = make_dot(Yp, params=params)
display(g)

각 파라미터를 W,B값을 넣고 계산 그래프를 그려준다.

2) 손실 계산

def mse(Yp,Y):
  loss = ((Yp-Y) **2).mean()
  return loss
loss = mse(Yp,Y)
print(loss)

tensor(13.3520, grad_fn=<MeanBackward0>)

 

손실함수의 계산그래프를 그려준다.

손실이란 예측함수와 손실함수의 합성함수이다.

params = {'W': W, 'B': B}
g = make_dot(loss, params=params)
display(g)

3) 경사 계산

loss.backward()
print(W.grad)
print(B.grad)

tensor(-19.0400)

tensor(2.0000)

 

4) 파라미터 수정

경사 계산 후 파라미터 - 학습률* 파라미터 기울기를 통해 파라미터를 갱신함 

W,B값은 외부에 영향을 끼치므로 마음대로 값을 수정할 수 없기 때문에, with torch.no_grad()를 통해 일시적으로 계산 그래프 생성 기능이 작동하지 않게 한다. grad.zero를 통해 다음 경사 계산을 준비하기 위해 경사값을 초기화 한다. 

lr = 0.001
with torch.no_grad():
    W -= lr * W.grad
    B -= lr * B.grad
    
    W.grad.zero_()
    B.grad.zero_()
    
print(W)
print(B)
print(W.grad)
print(B.grad)

 

반복 계산

W = torch.tensor(1.0, requires_grad=True).float()
B = torch.tensor(1.0, requires_grad=True).float()
num_epochs = 500
lr = 0.001
history = np.zeros((0,2))

루프 처리 구현은 다음과 같다

for epoch in range(num_epochs):
  Yp = pred(X)
  loss = mse(Yp,Y)
  loss.backward()

  with torch.no_grad():
    W -= lr* W.grad
    B -= lr * B.grad

    W.grad.zero_() #경삿값 초기화
    B.grad.zero_()

  if (epoch % 10 == 0):
    item = np.array([epoch,loss.item()])
    history = np.vstack((history,item))
    print(f'epoch = {epoch}  loss = {loss:.4f}')

결과평가

손실이 13.35에서 최종 4.67까지 줄어들었다.

# 최종 파라미터 값
print('W = ', W.data.numpy())
print('B = ', B.data.numpy())

# 손실 확인
print(f'초기상태 : 손실:{history[0,1]:.4f}') 
print(f'최종상태 : 손실:{history[-1,1]:.4f}')

최적화 함수와 step 함수 이용

확률적 경사하강법으로 최적화를 진행하는 코드를 추가한다.

W = torch.tensor(1.0, requires_grad=True).float()
B = torch.tensor(1.0, requires_grad=True).float()
num_epochs = 500
lr = 0.001 

import torch.optim as optim
optimizer = optim.SGD([W,B],lr = lr)

history = np.zeros((0,2))
for epoch in range(num_epochs):
  Yp = pred(X)
  loss = mse(Yp,Y)
  loss.backward()
  optimizer.step() #파라미터 수정
  optimizer.zero_grad() #경사값 초기화
  if (epoch % 10 ==0):
    item = np.array([epoch,loss.item()])
    history = np.vstack((history,item))
    print(f'epoch = {epoch}  loss = {loss:.4f}')

step() 함수를 호출하여 파라미터값을 변경했다.초기화도 zero_grad 함수를 사용한다. 

# 최종 파라미터 값
print('W = ', W.data.numpy())
print('B = ', B.data.numpy())

# 손실 확인
print(f'초기상태 : 손실:{history[0,1]:.4f}') 
print(f'최종상태 : 손실:{history[-1,1]:.4f}')

이전버전 결과와 비슷하게 나왔다.

 

최적화 함수의 튜닝버전 -> momentum = 0.9 옵션을 추가하여 학습속도를 더 빠르게 진행한다.

import torch.optim as optim
optimizer = optim.SGD([W, B], lr=lr, momentum=0.9)