자바스크립트가 비활성화 되어있습니다.
자바스크립트가 활성화 되어야 콘텐츠가 깨지지 않고 보이게 됩니다.
자바스크립트를 사용할수 있도록 옵션을 변경해 주세요.
- willbsoon

본문 바로가기
Bigdata, AI/NLP

WHAT IS TORCH.NN REALLY?

by willbsoon 2020. 8. 26.

파이토치는 공식페이지에 예제를 올려놓는다. 이를 한국어로 번역하는 프로젝트가 진행중인데. 이게 오픈소스 형식이다보니 아직 번역이 안되어있는 부분들도 많다. 그래서 이걸 좀 번역해보면 어떨까 싶다.

이전에 예제로 올렸던 것은 랜덤한 텐서들을 이용해 파이토치로 신경망을 구축해보는것을 했다면 여기 나온 예제들은 실제 데이터들을(물론 예제 데이터지만..) 이용해서 추론하는 것이기 때문에 하면 좋을것 같다는 생각이 든다.

물론 영어를 잘하는게 아니라 오역이 넘칠수도 있지만 시도해보는게 좋은게 아닐까 생각한다.

 

원문을 참조하고 싶다면 여기를 보자

 

해당 페이지는 colab과 ipynb 파일을 제공한다. 굳이 쳐보지 않아도 코드를 제공하는 센스까지! 하지만 나는 직접 처보겟다..

 


 

Pytorch는 우아하게 구현된 모듈과 torch.nn, torch.optimm, Dataset, DataLoader 와 같이 신경망을 만들고 학습시킬수 있는 클래스들을 제공한다. 완전히 그것들의 성능을 활용하고 문제에 그것들을 커스터마이징하기 위해서 내가 하고자하는것이 무엇인지 정확하게 이해 하는게 필요하다. 이러한 이해들을 발전시키기 위해서 우리는 처음에 MNIST 데이터셋으로 이러한 모델의 어떠한 기능들을 사용하지 않고, 오직 기본적인 pytorch tensor의 기능성만을 사용해서 기본적인 신경망을 학습하려고 한다. 그래서 우리는 점점 torch.nn, torch.optim, dataset, DataLoader 로부터 하나씩 추가하고 각각 무엇을 하는지, 얼마나 이 코드가 간결하고 유연하게 쓰이는지 정확하게 보여줄 것이다.

 

이 튜토리얼은 니가 이미 pytorch가 설치되어있고, tensor 연산에 기초에 친숙한 사람이라고 가정한다.

혹시 니가 numpy array 연산에 익숙하다면 여기서 쓰여지는 pytorch tensor 연산과 거의 동일하다는 것을 찾을것이다.

 

 

MNIST data setup

우리는 classic한 MNIST dataset을 사용할 건데, 손으로 쓴 숫자의 흑백이미지로 이뤄져있다.

우리는 pathlib을 사용할거고, requests를 이용해 dataset을 다운받을 것이다. 우리가 그것들을 사용할때 그 모듈들만 import해 온다. 그래서 그것들이 각 포인트에서 어떻게 쓰여지고 있는지 정확하게 볼수 있어야 한다.

 

from pathlib import Path
import requests

DATA_PATH = Path("projects/mnist")
PATH = DATA_PATH / "data"

PATH.mkdir(parents=True, exist_ok=True)

URL = "http://deeplearning.net/data/mnist/"
FILENAME = "mnist.pkl.gz"

if not (PATH / FILENAME).exists():
        content = requests.get(URL + FILENAME).content
        (PATH / FILENAME).open("wb").write(content)

코드는 내가 사용하기 편하게 변경될수있다...

 

 

 

 

이 데이터셋은 numpy array 포맷이고 파이썬에 특화된 직렬화 데이터 포맷인 pickle로 압축되어있다.

그래서 이것을 해제해주자.

import pickle
import gzip

with gzip.open((PATH / FILENAME).as_posix(), "rb") as f:
        ((x_train, y_train), (x_valid, y_valid), _) = pickle.load(f, encoding="latin-1")

이 코드를 통해서 x_train, y_train, x_valid, y_valid ,_ 에 다운받은 데이터셋이 저장된다.

각 이미지는 28* 28 사이즈이고, 784(28*28) 길이의 flattened row로 압축되어있다. 이걸 2d로 reshape 해보자.

 

%matplotlib inline
from matplotlib import pyplot
import numpy as np

pyplot.imshow(x_train[0].reshape((28, 28)), cmap="gray")
print(x_train.shape)

%matplotlib inline 은 이미지를 보여주기 위해 썼다. 매직 커맨드에 대해서 알고자 한다면 여기를 보자

 

 

자 이제 그러면 numpy array 를 대신해서 이걸 torch.tensor로 변경해 보자. 

import torch

x_train, y_train, x_valid, y_valid = map(
    torch.tensor, (x_train, y_train, x_valid, y_valid)
)
n, c = x_train.shape
x_train, x_train.shape, y_train.min(), y_train.max()
print(x_train, y_train)
print(x_train.shape)
print(y_train.min(), y_train.max())

 

NeuralNet from scratch( no torch.nn) - 번역 개판......ㅠ

먼저 pytorch tensor 연산만 사용해서 모델을 만들어보자. 우리는 니가 이미 신경망에 익숙하다고 생각한다. 그렇지않다면 course.fast.ai 여기서 배워라...(광고,,?)

pytorch는 임의의 수 또는 0으로 채워진 tensor를 만드는 메서드를 제공한다. 우리는 간단한 선형 모델을 위해 가중치와 편향에 이를 사용할것이다. 이것들은 바로 아주 특별한 것이 추가된 regular tensor 들이고, 이것을 우리는 기울기가 필요하다고 말한다?? 이것은 pytorch가 모든 연산의 완료를 기록하게 한다. 그래서 역전파동안 자동으로 기울기를 계산할수 있도록 한다.?

 

기울기에 포함된 그 단계를 원하지 않기 때문에 가중치를 위해 초기화후에 우리는 requires_grad 를 설정한다. 

뒤에 _ 가 있는 것은 pytorch에서 연산이 제자리에서 수행됨을 의미한다.???

 

import math

weights = torch.randn(784, 10) / math.sqrt(784)
weights.requires_grad_()
bias = torch.zeros(10, requires_grad=True)

 

 

기울기를 자동으로 계산할수 있는 pytorch의 능력 덕분에 우리는 어떠한 표준 파이썬함수라도 모델처럼 사용할수 있다. 그래서 평범한 행렬곱 과 단순한 선형모델을 만들기위한 broadcasted addition을 써보자. 또한 우리는 활성화함수가 필요하다. 그래서 log_softmax를 써보고 이를 사용해보자. 기억하자. 비록 pytorch가 미리 기록해둔 많은 loss function, activation function, 등등 을 제공하더라도 너는 평범한 파이썬을 사용해서 너만의것을 쉽게 만들수 있다. pytorch는 심지어 너의 코드를 위한 빠른 GPU와 벡터화된 CPU를 자동으로 만들수 있다.

 

def log_softmax(x):
    return x - x.exp().sum(-1).log().unsqueeze(-1)

def model(xb):
    return log_softmax(xb @ weights + bias)

 

위에서 @는 내적(dot product) 연산을 나타낸다. 우리는 데이터의 한번의 배치에 이 함수를 호출할것이다. 아래 코드는  한 번  forward 메서드를 실행한것이다. 우리의 예측은 우리가 random 가중치로부터 시작했기 때문에 random보다 더 좋아질 수 없을 것이다. 

bs = 64  # batch size

xb = x_train[0:bs]  # a mini-batch from x
preds = model(xb)  # predictions
preds[0], preds.shape
print(preds[0], preds.shape)

 

니가 봤듯이, preds 텐서는 텐서 값 뿐 아니라 기울기 함수까지 포함하고 있다. 우리는 나중에 역전파에 사용할 것이다. 

손실함수로 음의 로그우도(negative log-likelihood)를 실행해보자.(다시 말하지만 우리는 그냥 표준 파이썬을 사용할수있다.)

def nll(input, target):
    return -input[range(target.shape[0]), target].mean()

loss_func = nll

 

이제 random model의 loss 값을 구해보자. 그리고 우리는 역전파 학습 이후에 성능이 올라갔는지 볼수있다.

yb = y_train[0:bs]
print(loss_func(preds, yb))

 

우리 모델의 정확도를 계산하기위한 함수를 실행해보자. 각 예측마다 큰 값을 가진 인덱스가 목표 값과 일치한다면 예측은 정확한 것이다.

def accuracy(out, yb):
    preds = torch.argmax(out, dim=1)
    return (preds == yb).float().mean()
    
print(accuracy(preds, yb))

 

 

 

 

우리는 이제 학습 loop를 실행할수 있다. 각각의 반복에 대해 우리는 

- 데이터의 미니배치를 선택하고

- 예측을 만들기위한 모델을 사용하고

- 손실을 계산하고

- loss.backward()는 모델의 기울기를 업데이트할것이다. 이번 케이스는 weights, bias.

 

우리는 지금 가중치와 편향을 업데이트하기 위해 기울기를 사용하고있다. 우리는 torch.no_grad() context manager 와 함께 사용한다. 왜냐하면 우리는 이러한 실행이 기울기의 다음 계산을 위해 기록되어지길 원하진 않는다. 우리는 autograd record 연산에 대해 여기서 더 읽을수 있다.

우리는 기울기를 -으로 설정하고 다음 반복을 준비한다. 반면에 기울기는 발생한 모든 작업의 ​​누적 집계를 기록한다.

from IPython.core.debugger import set_trace

lr = 0.5  # learning rate
epochs = 2  # how many epochs to train for

for epoch in range(epochs):
    for i in range((n - 1) // bs + 1):
        #         set_trace()
        start_i = i * bs
        end_i = start_i + bs
        xb = x_train[start_i:end_i]
        yb = y_train[start_i:end_i]
        pred = model(xb)
        loss = loss_func(pred, yb)

        loss.backward()
        with torch.no_grad():
            weights -= weights.grad * lr
            bias -= bias.grad * lr
            weights.grad.zero_()
            bias.grad.zero_()

set_trace() 함수를 쓰면 파이썬 표준 디버거처럼 사용할수 있다.

 

 

우리는 스크래치로 작은 신경망을 만들고 완전히 학습했다. 우리는 은닉층이 없었기 때문에 로지스틱회귀진행했다.

손실과 정확도를 확인하고 우리가 먼저 얻었던것과 비교하자. 우리는 손실이 감소하고 정확도가 올라가길 기대하고 그럴것이다.

print(loss_func(model(xb), yb), accuracy(model(xb), yb))

 


 

 

이전에 썻던것과 중복된 과정들이 좀 많기도 하고... 설명은 좋은데 코드가 너무 조잡하게 기록되어있어서 진행이 매끄럽지 못하다는 생각이 든다.

 

댓글