✍🏻 5일 공부 이야기.
오늘 공부한 실습 코드는 위 깃허브에 올려두었습니다 :) 사진 클릭시 해당 링크로 이동해요 !
앞서 배웠던 TensorFlow
와 같은 흐름으로 PyTorch
도 배워보자. 둘은 같이 개발되고 있기 때문에 기능이 거의 다 비슷하다.
PyTorch
도
4 파트만 익히면 된다!
앞서 Variable과 Constant가 달리 있었던 TensorFlow
와 달리 PyTorch
에서는 이를 하나로 모두 Tensor
로 본다.
torch.tensor(변환시킬 데이터)
, torch.as_tensor(변환시킬 데이터)
: 기존의 데이터를 Tensor로 변환
torch.tensor(변환시킬 데이터)
, torch.as_tensor(변환시킬 데이터)
, torch.from_numpy(변환시킬 데이터)
: numpy 데이터를 Tensor로 변환
변환시킬 데이터.numpy()
: Tensor -> numpy
.shape
, .size
: Tensor의 속성값 확인
데이터 타입 선언
생성시 선언
# 생성시 선언 dtype=torch.float32
torch.randint(10, size=(5,), dtype=torch.float32)
이미 생성된 변수에 선언
a = torch.randint(10, size=(5,))
print(a.dtype)
# 텐서이름.type(원하는 데이터타입)
print(a.type(torch.float32))
print(a.dtype)
# inplace 명령어가 아니므로 갱신시켜주어야함
a = a.type(torch.float64)
print(a.dtype)
torch.cuda.is_available()
: GPU 사용
GPU에서 사용하기 위해 cuda에서 사용하는 데이터 타입으로 바꿔줘야함
방법 1. 생성 시 device 설정
# 방법 1. device 설정하기.
x = torch.ones(2, 2, device='cuda')
# 여러개의 GPU 중에 하나의 GPU에 할당하고 싶을 때
# 번호는 nvidia-smi 명령을 Shell에 입력해서 찾을 수 있음
x = torch.ones(2, 2, device='cuda:0')
# device 객체를 입력하는게 기본
x = torch.ones(2, 2, device=torch.device('cuda'))
방법 2. tensor_var.cuda()
# 방법 2. .cuda()
a = torch.rand(10)
print(a)
a = a.cuda()
print(a)
방법 3. tensor_var.to(device)
# 방법 3. .to(device) : 대부분 많이 사용
a = torch.rand(2)
print(a)
a = a.to("cuda") # a.to(torch.device("cuda"))
print(a)
대부분의 연산은 TensorFlow
와 동일하다. 몇 가지 특이 케이스만 정리해보았다.
tensorflow의 axis
-> pytorch dim
크기와 차원 변경
torch.reshape
텐서명.view
: expand_dims 같은 함수 대신 이걸로 사용torch.transpose
torch.sqeeze
/ torch.unsqueeze
텐서이름.view_as(다른 텐서이름)
/ 텐서이름.reshape_as(다른 텐서이름)
: 이전 shape에 맞춰서 두번째 텐서의 shape이 변경됨대부분 함수 끝에 _ 를 붙이면 inplace 명령이 됨
아래와 같은 오류가 발생한다면 텐서이름.contiguous()
호출하고 다시 실행시키기
autograd
는 PyTorch에서 핵심적인 기능을 담당하는 하부 패키지이다.
autograd는 텐서의 연산에 대해 자동으로 미분값을 구해주는 기능을 한다.
requires_grad
인수를 True
로 설정하거나 .requires_grad_(True)
를 실행하면📌 그 텐서에 행해지는 모든 연산에 대한 미분값을 계산한다.
# requires_grad=True : 미분값을 트래킹할 변수임을 선언
x = torch.rand(2, 2, requires_grad=True)
y = torch.sum(x * 3)
print(y, y.grad_fn) # grad_fn라는 속성값이 생기게 되면서 미분 값을 트래킹할 수 있게됨
y.backward() # 선언 후(x의 미분값이 자동으로 갱신됨)
x.grad # 미분을 구하고자하는 변수.grad 를 하면 미분값이 구해짐
📌 상황에 따라 특정 연산에서는 미분값을 계산하고 싶지 않은 경우
계산을 멈추고 싶으면 .detach()
함수나 with을 이용해 torch.no_grad()
를 이용하면 된다.
x_d = x.detach() # 미분값 트래킹 x
torch.sigmoid(x_d)
print(x_d.grad) # None
with torch.no_grad():
x_d2 = torch.sigmoid(x)
print(x_d2.grad) # None
Boston
데이터를 이용해 Linear Regression 을 PyTorch로 구현해보자.
위 수식을 이용해보자.
x = torch.tensor(df.values)
y = torch.tensor(y.values).view(-1, 1)
XT = torch.transpose(x, 0, 1)
# 역행렬이 존재시, 가중치 벡터 구하는 수식
w = torch.matmul(torch.matmul(torch.inverse(torch.matmul(XT, x)), XT), y)
y_pred = torch.matmul(x, w)
print("예측한 집값 :", y_pred[19], "실제 집값 :", y[19])
💻 출력
예측한 집값 : tensor([18.4061], dtype=torch.float64) 실제 집값 : tensor([18.2000], dtype=torch.float64)
📌 loss 추출하기
# 가중치 생성
# const 열을 추가했기 때문에 이론 상으로 b는 필요없지만
# 그냥 일반적인 경우를 설명하기 위해 맞춰준 것
w = torch.rand((14, 1), dtype=torch.float64, requires_grad=True)
b = torch.rand((1, 1), dtype=torch.float64, requires_grad=True)
# linear regression
z = x.mm(w) + b
loss = torch.mean((z - y)**2)
loss.backward()
# print(loss)
# tensor(303557.1678, dtype=torch.float64, grad_fn=<MeanBackward0>)
# loss 숫자만 추출하는 방법
# 303557.16782532405
print(loss.detach().numpy()) # 방법 1
with torch.no_grad(): # 방법 2
print(loss.numpy())
# 이렇게 로그를 추출할 때는 숫자만 추출하도록 설정해줌
# 방법 3
loss.item()
📌 추출한 gradient로 가중치 업데이트하기
lr = 3e-7
for epoch in range(100):
z = x.mm(w) + b
loss = torch.mean((y-z)**2)
loss.backward() # 미분값 계산
w.data -= w.grad * lr # 업데이트
b.data -= b.grad * lr
# 위 코드와 동일
## 미분값 계산
#grads = torch.autograd.grad(loss, [w, b])
#
#w.data -= grads[0] * lr # 업데이트
#b.data -= grads[1] * lr
print('{} - loss : {}'.format(epoch, loss.item()))
# 초기화
# 누적값을 초기화해주어야함
w.grad.zero_()
b.grad.zero_()
opt = torch.optim.SGD([w, b], lr=lr)
for epoch in range(100):
z = (x.mm(w) + b)
loss = torch.mean((z - y)**2)
loss.backward()
opt.step()
print("{:3} - loss : {}".format(epoch, loss.item()), end="\r")
opt.zero_grad()
y_pred = x.mm(w) + b
print("예측한 집값 :", y_pred[19].item(), "실제 집값 :", y[19].item())
💻 출력
예측한 집값 : 22.069598463384597 실제 집값 : 18.2
Data Load 및 전처리 -> 데이터 확인 -> 모델 정의 -> 모델 학습 -> 평가
batch_size = 32
# 데이터 부르기
train_loader = torch.utils.data.DataLoader(
datasets.MNIST('dataset/',
train=True, download=True, # 로컬 환경에 데이터가 없다면 다운로드하라
transform=transforms.Compose([ # 가져올 때 미리 아래와 같이 전처리해서 가져오겠다
transforms.ToTensor(),
transforms.Normalize(mean=(0.5,), std=(0.5,)) # mean이 0.5이고 std가 0.5가 되는 분포가 되도록 정규화시켜라.
# 튜플 형태로 넣어주어야함
])),
batch_size=batch_size,
shuffle=True)
test_loader = torch.utils.data.DataLoader(
datasets.MNIST('dataset/',
train=False,
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(mean=(0.5,), std=(0.5,))
])),
batch_size=batch_size,
shuffle=False)
images, labels = next(iter(train_loader))
images.shape, images.dtype # (torch.Size([32, 1, 28, 28]), torch.float32)
- TF - (batch, height, width, channel)
- PyTorch - (batch, channel, height, width)
PyTorch와 TF는 이미지를 표현하는데에 있어서 channel의 위치 차이가 있으니 명심하자!!
주로 nn.Module
을 상속 받아서 모델을 정의한다.
from torch import nn # 학습할 파라미터가 있는 것들 # conv2d
import torch.nn.functional as F # 학습할 파라미터가 없는 것들 # maxpool, relu
class Net(nn.Module):
def __init__(self): # forward에서 사용되는 레이어, 파라미터 정의
super(Net, self).__init__()
# tf에서는 output만 넣어주었다면
# pytorch에서는 input과 output을 같이 정의해줌
# input, output
self.conv1 = nn.Conv2d(1, 20, 5, 1)
self.conv2 = nn.Conv2d(20, 50, 5, 1)
self.fc1 = nn.Linear(4*4*50, 500)
self.fc2 = nn.Linear(500, 10)
def forward(self, x): # 실제 연산
x = F.relu(self.conv1(x))
x = F.max_pool2d(x, 2, 2)
x = F.relu(self.conv2(x))
x = F.max_pool2d(x, 2, 2)
x = x.view(-1, 4*4*50)
x = F.relu(self.fc1(x))
x = self.fc2(x)
return F.log_softmax(x, dim=1)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = Net().to(device) # 모델을 device로 넘겨줌
model을 이쁘게 정리해서 출력해주는 torchsummary
는 이후 모델링 파트에서 정리할 예정!
'''
- epoch
- batch
- model
- loss
- grad
- model update
'''
import torch.optim as optim
opt = optim.SGD(model.parameters(), lr=0.003)
for epoch in range(1):
# 학습
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device) , target.to(device) # 데이터 device로 보내기
output = model(data)
loss = F.nll_loss(output, target)
# 지금 원핫인코딩이 안 되어있는 상태이다.
# tf 에서는 SparseCategoricalCrossentropy loss를 사용해
# 원핫 인코딩이 안 되어있는 상태에서도 사용 가능했는데
# 이를 torch에서 구현하려면 log softmax와 nll_loss를 조합하면 됨.
loss.backward()
opt.step() # 업데이트
print('batch {} loss : {}'.format(batch_idx, loss.item()))
opt.zero_grad() # 초기화
# 평가
model.eval()
test_loss = 0
with torch.no_grad():
for data, target in test_loader:
output = model(data)
test_loss += F.nll_loss(output, target).item()
# 한 epoch 마다 배치 데이터의 평균 loss 값
test_loss /= (len(test_loader.dataset) // 32)
print('Epoch {} test loss : {}'.format(epoch, test_loss))