PyTorch 기본¶
먼저 PyTorch의 주요 패키지들을 가져온다.
- torch는 PyTorch의 최상위 패키지이고, Numpy와 비슷하지만, Tensor라는 N차원벡터를 GPU위에서 다룰 수 있는 패키지이다.
- torch.autograd는 경사하강법 및 미분을 자동으로 해주는 패키지이다.
- torch.nn은 여러 많이 사용되는 신경망들의 구현체를 가지고있는 패키지이다.
- torch.optim은 SGD나 Adam을 비롯한 여러가지 최적화 함수들을 가지고있는 패키지이다.
PyTorch 텐서¶
위에서 언급한 것 처럼 텐서는 N차원벡터들을 의미한다. 아래는 PyTorch에서 텐서를 어떻게 사용하는지 보여주는 코드이다.
import torch
from torch import autograd, nn, optim
import torch.nn.functional as F
import numpy as np
N = 5
# 5 x 10사이즈의 2차원 텐서에 랜덤한 Float값들을 넣어 만든다
x = torch.randn(N, 10).type(torch.FloatTensor)
x
# view함수를 사용해 2차원의 행들을 하나로 합친다
x.view(1, -1)
# 기존의 텐서 x와 차원이 바뀐 텐서의 shape을 비교해본다
x.shape, x.view(1, -1).shape
PyTorch Autograd¶
앞서 이야기했듯이 autograd패키지는 경사하강법 즉 미분을 자동으로 해주는 패키지이다. 각 파라미터들의 에러(오차)값을 구하려면 미분을 해야하기 때문이다.
이것을 텐서에 적용하려면 requires_grad 파라미터를 True로 설정해주면 된다.
x = torch.tensor([1., 2., 3., 4., 5., 6.], requires_grad=True)
x.grad
# 딥러닝에서는 보통 텐서에 활성함수를 사용하지만, 여기서는 단순 계산
L = (2*x+1).sum()
# 역전파를 사용해 텐서안의 값들(딥러닝에선 파라미터)을 갱신해준다
L.backward()
앞서 grad를 출력하면 아무것도 나오지 않았지만, 아래 다시 grad를 출력하면 갱신된 값들을 가진 텐서를 볼 수 있을것이다.
x.grad
PyTorch nn¶
nn모듈에는 딥러닝에서 많이 사용하는 신경망 모델들, 즉 레이어들과 활성함수 (비용함수)들이 들어있다.
아래의 예제는 N x 5 행렬을 N x 3 행렬로 변환하는 선형레이어 예제이다.
D = 5
M = 3
linear_map = nn.Linear(in_features=D, out_features=M)
# 랜덤으로 할당된 파라미터 값들을 출력해준다
[p for p in linear_map.parameters()]
PyTorch를 활용한 선형회귀¶
선형회귀는 주어진 데이터(x값과 y값을 가진 점들)에 가장 잘 맞는 선을 구하는 문제이다.
lin = lambda a, b, x : a * x + b
# 가짜 데이터를 생성한다
def gen_fake_data(n, a, b):
x = np.random.uniform(0, 1, n)
y = lin(a, b, x) + 0.1 * np.random.normal(0, 3, n)
return x, y
x, y = gen_fake_data(50, 3., 8.)
# 생성된 데이터를 5개씩만 출력해본다
x[:5], y[:5]
import matplotlib.pyplot as plt
# matplotlib를 사용해 데이터를 그려본다
# 가짜 데이터가 선형회귀에 알맞게 잘 분포되어 있는것을 볼 수 있다
%matplotlib inline
plt.scatter(x,y, s=8)
plt.xlabel("x")
plt.ylabel("y")
선형회귀 문제에서는 데이터에 맞는 직선 a * x + b의 a와 b를 데이터를 기반으로 구하는것이 핵심이다. 이를 위해서 에러함수인 Mean Squared Error함수를 정의하겠다.
만약 우리가 a = 10, b = 5라고 가정하고 실제 y데이터와 비교해주면 다음과 같이 에러를 리턴해준다. (데이터 자체가 랜덤생성값이기 때문에 위에서 데이터를 다시 정의해주면 값이 바뀐다.)
mse = lambda y_hat, y: ((y_hat - y) ** 2).mean()
# 데이터의 x값을 사용해 10x + 5의 직선으로 y 데이터를 예측한다.
y_hat = lin(10, 5, x)
mse(y_hat, y)
MSELoss = lambda a, b, x, y: mse(lin(a, b, x), y)
MSELoss(10, 5, x, y)
PyTorch 경사하강법¶
MSELoss는 x와 y의 함수가 아니라 a와 b의 함수이다. 그 이유는 x와 y는 이미 정해져있는 데이터셋이기 때문이다. 따라서 우리는 MSELoss함수를 최소화하는 a와 b값을 찾아야한다.
경사하강법은 이 오차함수를 최소화 하는 알고리즘이다. 경사하강법은 어떠한 파라미터값(가중치)으로 시작해 오차함수를 최소화 하는 파라미터값으로 반복을 통해 조금씩 이동하게 하는 알고리즘이다.
아래는 이것이 구현된 PyTorch 코드이다.
# 앞서 정의한 함수로 가짜 데이터를 만들어낸다
x_orig, y_orig = gen_fake_data(10000, 3., 8.)
x_orig.shape, y_orig.shape
from sklearn.model_selection import train_test_split
# 데이터를 Train과 Test로 나눠준다
x_train, x_test, y_train, y_test = train_test_split(x_orig, y_orig, test_size=0.3, random_state=42)
# 데이터를 텐서로 만들어준다
x_train = torch.tensor(x_train)
y_train = torch.tensor(y_train)
x_test = torch.tensor(x_test)
y_test = torch.tensor(y_test)
x_train, y_train, x_test, y_test
# 파라미터값을 0과 1사이의 랜덤한 값으로 초기화 하고,
# 경사하강법(자동미분)을 적용하는 텐서를 만든다
a, b = np.random.randn(1), np.random.randn(1)
a = torch.tensor(a, requires_grad=True)
b = torch.tensor(b, requires_grad=True)
a, b
학습률(learning rate)는 경사하강법이 한번에 얼마만큼 변하는지를 결정한다. 아래의 코드에서는 학습률을 0.003으로 설정해 주었다.
learning_rate = 1e-3
for t in range(10000):
# 먼저 초기값의 a와b를 통해 y를 예측한다
loss = MSELoss(a, b, x_train, y_train)
if t % 1000 == 0:
print(loss.item())
# 오차 역전파 미분을 시행한다.
# requires_grad=True로 설정했기 때문에 자동미분을 수행한다
loss.backward()
# 경사와 학습률을 곱한 만큼 a와b를 갱신한다
a.data -= learning_rate * a.grad.data
b.data -= learning_rate * b.grad.data
#경사를 다시 0으로 설정해준다
a.grad.data.zero_()
b.grad.data.zero_()
print(a, b)
테스트 데이터를 사용해 모델을 평가해본다. 평가는 학습때와 같이 MSE Loss를 이용한다. loss의 결과를 확인하면 학습때의 loss와 얼추 비슷하게 나오는 것을 확인할 수 있다.
a.grad.data.zero_()
b.grad.data.zero_()
loss = MSELoss(a, b, x_test, y_test)
loss.item()
PyTorch nn을 사용한 경사하강법¶
위의 코드를 PyTorch의 nn패키지를 사용해 조금 더 간결하게 작성해보겠다.
# 입력 차원 1개 출력 차원 1개의 선형 모델이다
nn.Linear(1, 1)
# 이를 감싸주는 클래스를 작성한다
# forward함수는 모델이 호출될 때 자동으로 불리게 되어있다
# Sequential을 사용한 케라스 스타일의 작성법도 있지만,
# 복잡한 모델을 만들때는 클래스형식을 많이 쓰기 때문에 작성하지 않는다
class LinearRegression(nn.Module):
def __init__(self):
super().__init__()
self.lin = nn.Linear(1, 1)
def forward(self, x):
x = self.lin(x)
return x
model = LinearRegression()
print([p for p in model.parameters()])
from sklearn.model_selection import train_test_split
x, y = gen_fake_data(10000, 3., 8.)
# 데이터를 Train과 Test로 나눠준다
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=42)
x_train = torch.tensor(X_train).float()
y_train = torch.tensor(y_train).float()
x_test = torch.tensor(X_test).float()
y_test = torch.tensor(y_test).float()
x_train, y_train, x_test, y_test
nn의 모델을 사용해 예측을 하는 것은 다음과 같이 한다:
# x의 형식을 모델에 적용하기 좋게 바꿔준다
x_train_unsqueezed = torch.unsqueeze(x_train, 1)
y_hat = model(x_train_unsqueezed)
y_hat
앞서 정의했던 반복문보다 조금 짧은것을 볼 수 있다. 주석을 보고 같은 부분을 비교해보자. 결과값을 보면 앞서 했던 경사하강법과 성능이 비슷한 것을 알 수 있다.
learning_rate = 1e-3
optimizer = optim.SGD(model.parameters(), lr=learning_rate)
for t in range(10000):
model.train()
y_hat = model(x_train_unsqueezed)
# 먼저 초기값의 a와b를 통해 y를 예측한다
loss = F.mse_loss(y_hat, y_train.unsqueeze(1))
if t % 1000 == 0:
print(loss.item())
# 오차 역전파 미분을 시행한다.
# requires_grad=True로 설정했기 때문에 자동미분을 수행한다
loss.backward()
# 경사와 학습률을 곱한 만큼 a와b를 갱신한다
optimizer.step()
#경사를 다시 0으로 설정해준다
optimizer.zero_grad()
print([p for p in model.parameters()])
이제 모델을 평가해보자. loss함수를 사용해 평가해보면 위의 마지막 loss와 비슷한 결과를 얻은것을 볼 수 있다.
x_test_unsqueezed = torch.unsqueeze(x_test, 1)
model.eval()
with torch.no_grad():
outputs = model(x_test_unsqueezed)
loss = F.mse_loss(outputs, y_test_tensor.unsqueeze(1))
print(loss.item())
아래는 단순한 경사하강법 대신 Adam이라는 최적화함수를 사용하고, 학습률 0.1을 적용한 결과이다. 이전 결과보다 빨리 수렴하는것을 볼 수 있다.
model = LinearRegression()
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=42)
x_train = torch.tensor(X_train).float()
y_train = torch.tensor(y_train).float()
x_test = torch.tensor(X_test).float()
y_test = torch.tensor(y_test).float()
x_train, y_train, x_test, y_test
x_train_unsquezzed = torch.unsqueeze(x_train, 1)
learning_rate = 0.1
optimizer = optim.Adam(model.parameters(), lr=learning_rate)
for t in range(10000):
y_hat = model(x_train_unsquezzed)
loss = F.mse_loss(y_hat, y_train.unsqueeze(1))
if t % 1000 == 0:
print(loss.item())
loss.backward()
optimizer.step()
optimizer.zero_grad()
print([p for p in model.parameters()])
x_test_unsqueezed = torch.unsqueeze(x_test, 1)
model.eval()
with torch.no_grad():
outputs = model(x_test_unsqueezed)
loss = F.mse_loss(outputs, y_test_tensor.unsqueeze(1))
print(loss.item())
참고자료
아래의 원본 소스를 기반으로 번역 및 개인 의견을 덧붙인 코드입니다.
'데이터사이언스 > PyTorch' 카테고리의 다른 글
PyTorch 5 - 딥러닝 기반 협업필터링 (0) | 2019.07.22 |
---|---|
PyTorch 4 - 협업필터링 (0) | 2019.07.14 |
PyTorch 3 - 데이터 로더, 신경망 (0) | 2019.07.13 |
PyTorch 2 - 로지스틱 회귀 (0) | 2019.07.11 |