※ 본 게시물에 사용된 내용의 출처는 대다수 <펭귄브로의 3분 딥러닝-파이토치맛>에서 사용된 자료이며, 개인적인 의견과 해석이 추가된 부분도 존재합니다. 그림의 경우 교재를 따라 그리거나, 제 임의대로 추가 수정한 부분도 존재합니다. 저 역시도 공부하고자 포스팅한 게시물이니, 잘못된 부분은 댓글로 알려주시면 감사하겠습니다.
- AutoGrad, 경사하강법
- 경사하강법(Gradient descent)으로 이미지 복원
- 신경망 모델 구현
AutoGrad : 기울기(gradient)를 자동으로 계산
정답(ground truth) - 예측 값(prediction value) = 거리(distance)
학습 데이터에 대하여 거리들의 평균 = 오차(loss)
==> 오차가 작은 모델 == 데이터에 잘 학습된 좋은 모델
그 중, 오차를 최소화 하는 알고리즘으로 gradient descent(경사 하강법)이 많이 사용
- Gradient descent(경사하강법) : 간단하게 오차를 수학함수로 표현한 f(x)를 미분한 후 함수의 기울기를 구해 가장 오차의 최솟값이 작은 방향을 찾아내는 알고리즘
Pytorch에는 Autograd라는 미분 계산 자동화 함수를 이용하여 경사 하강법을 직접 구현하지 않고도 사용할 수 있음.
[사용법]
값이 1.0인 스칼라 텐서 w를 정의하고 w에 대해 미분하여 기울기를 계산할 때,
w의 requires_grad를 True로 설정하면 자동으로 Autograd 기능이 실행되어, w에 대한 미분값을 w.grad에 저장하게 됨
w = torch.tensor(1.0, requires_grad = True) # Autograd 자동 실행
a = w*3 # 수식 지정
b = a**2
b.backward()
print('b를 w로 미분한 값은 {}'.format(w.grad))
이미지 복원하기
프로젝트 개요 및 목표
: 이미지 처리를 위해 만들어 두었던 weird_function() 함수에 실수로 버그가 들어가 100x100 픽셀의 오염된 이미지가 만들어졌습니다. 이 오염된 이미지와 오염되기 전 원본 이미지를 동시에 파일로 저장하려 하였으나, 모종의 이유로 원본 이미지 파일은 삭제된 상황입니다. 다행히도 weird_function()의 소스코드는 남아있습니다. 오염된 이미지와 weird_function을 활용해 원본 이미지를 복원해봅시다.
- 사용 코드
import torch # 파이토치 import
import pickle # pickle library(파이썬 객체 파일 입출력 관련) import
import matplotlib.pyplot as plt # matplotlib library(시각화) import
from google.colab import drive # 구글 드라이브 import
drive.mount('/gdrive',force_remount=True)
구글 드라이브로부터 데이터를 로딩하며, 정상적으로 드라이브가 마운트 될 경우 (방법) 아래와 같이 연결이 완료된다.
# 이미지 shape
shp_original_img = (100, 100)
# 오염된 이미지 파일 로딩(pytorch tensor shape)
broken_image = torch.FloatTensor(pickle.load(open('/gdrive/My Drive/Colab Notebooks/3-min-pytorch-master/03-파이토치로_구현하는_ANN/broken_image_t.p', 'rb'),encoding='latin1' ))
# matplotlib로 100,100으로 바꾼 후 시각화
plt.imshow(broken_image.view(100,100))
아래 함수는 이미지를 오염시킨 함수 코드이며, 구체적으로 이 함수를 이해할 필요는 없다.
def weird_function(x, n_iter=5):
h = x
#filt = tensor([-0.3333, 0.3333, -0.3333])
filt = torch.tensor([-1./3, 1./3, -1./3])
for i in range(n_iter):
# zero_tensor = tensor([0.]) / zero_tensor.shape = torch.Size([1])
zero_tensor = torch.tensor([1.0*0])
# zero tensor와 input feature(h, 가상 feature)를 이어붙여 신규 h_l, h_r feature를 제작
h_l = torch.cat( (zero_tensor, h[:-1]), 0)
h_r = torch.cat((h[1:], zero_tensor), 0 )
# h_l과 h_r를 일정 비율로 결합하여 h를 업데이트
h = filt[0] * h + filt[2] * h_l + filt[1] * h_r
if i % 2 == 0:
h = torch.cat((h[h.shape[0]//2:],h[:h.shape[0]//2]), 0)
# 짝수 번 반복될때마다 신규 h feature의 앞뒤(5000을 기준)를 바꿔서 이어붙임(5000:) + (:5000)
return h
오차 함수를 정의하고,
# 무작위 텐서(random_tensor)를 weird_function함수에 입력하여 얻은
# 가설 텐서(hypothesis tensor)와 오염된 이미지 간의 차(=loss)를 구하는 함수
def distance_loss(hypothesis, broken_image):
return torch.dist(hypothesis, broken_image)
실제 원본이미지가 될 랜덤이미지를 정의한다.
# 경사하강법을 통해서 향후 원본 이미지의 형상이 될 무작위 tensor
random_tensor = torch.randn(10000, dtype = torch.float)
학습률 정의
# 학습률(learning rate) = 0.8
lr = 0.8
일정 반복을 통해 학습을 진행하는 main 함수를 구현한다.
for i in range(0,20000):
# 오차 함수를 random_tensor로 미분해야 하기 때문에 requires_grad를 True로 설정
random_tensor.requires_grad_(True)
# random_tensor를 weird_function()함수에 통과하여 hypothesis를 구함.
hypothesis = weird_function(random_tensor)
#distance_loss함수에서 hypothesis와 broken_image간 오차(loss)를 계산
loss = distance_loss(hypothesis, broken_image)
# backward함수로 loss를 random_tensor로 미분
loss.backward()
# 직접 gradient descent를 구현하기 위하여 torch.no_grad 함수를
# 사용하여, 자동 기울기 계산을 비활성화
with torch.no_grad():
# random_tensor : loss.backward()에서 계산한 loss의 기울기 --> loss가 최댓값이 되는 방향
# 이 방향의 반대 방향으로 lr를 이동.
random_tensor = random_tensor - lr*random_tensor.grad
# for문이 1000번 반복될때마다 오차 출력
if i % 1000 == 0:
print('Loss at {} = {}'.format(i, loss.item()))
# 전체 반복문을 다 돌 경우, random_tensor가 제대로 복원됐는지 확인
plt.imshow(random_tensor.view(100,100).data)
간단하게 아래 그림으로 전체 절차를 확인할 수 있다.