본 글은 Gradient Descent의 Pytorch 코드에 대한 필자의 이해를 위해 번역된 글로, 원글은 여기서 보실 수 있습니다.
Pytorch 를 통해 이미지 분류기를 만들어보도록 하겠습니다. 흐름은 다음과 같습니다.
- 데이터셋 준비하기 -
torchvision
을 통해 CIFAR10 데이터셋 준비(Loading train/test dataset, normalization 포함) - CNN 네트워크 준비하기
- Loss function 정의하기
- Train dataset을 CNN 네트워크 학습하기
- CNN 네트워크로 test dataset 예측하기
본 글은 이미지가 학습되는 전체 구조 중심으로 설명하기 보다는 gradient descent가 어떤 흐름으로 이루어져있는지 자세히 살펴보고자 만들어졌습니다. 그럼 시작하겠습니다.
1. 데이터셋 준비하기
데이터셋을 받아서 Tensor로 변환하고, 이를 최종적으로 dataloader에 올리는 일까지 모든 일을 통칭하여 이루어집니다.
""" | |
데이터셋 준비하기: Dataset -> torchvision.transforms를 통한 Tensor 변환 -> Dataloader 로 준비 (train_dataloader, test_dataloader) | |
""" | |
# 라이브러리 준비 | |
import torch # 모델 학습시 필요한 torch | |
import torchvision # 이미지 다룰 때 필요한 torchvision | |
import torchvision.transforms as transforms # Dataset loading, Normalize 시 필요한 transforms | |
# transform = Dataset -> Normalized된 Tensor로 바꾸는 역할 | |
transform = transforms.Compose( | |
[transforms.ToTensor(), | |
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) | |
batch_size = 4 | |
# Train dataset 준비: torchvision의 CIFAR10 -> transform을 통해 Normalized된 Tensor로 변경 -> DataLoader에 올리기 | |
trainset = torchvision.datasets.CIFAR10(root='./data', train=True, | |
download=True, transform=transform) | |
trainloader = torch.utils.data.DataLoader(trainset, batch_size=batch_size, | |
shuffle=True, num_workers=2) | |
# Test dataset 준비: torchvision의 CIFAR10 -> transform을 통해 Normalized된 Tensor로 변경 -> DataLoader에 올리기 | |
testset = torchvision.datasets.CIFAR10(root='./data', train=False, | |
download=True, transform=transform) | |
testloader = torch.utils.data.DataLoader(testset, batch_size=batch_size, | |
shuffle=False, num_workers=2) | |
# 분류기가 학습해야할 class 종류 | |
classes = ('plane', 'car', 'bird', 'cat', | |
'deer', 'dog', 'frog', 'horse', 'ship', 'truck') |
transforms 라이브러리를 이용하여 Normalization뿐만 아니라 Crop, Rotation 등 여러 일이 가능합니다.
Output:
Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./data/cifar-10-python.tar.gz
Extracting ./data/cifar-10-python.tar.gz to ./data
Files already downloaded and verified
데이터셋을 한번 보여보도록 하겠습니다. train dataset의 Dataloader에서 이미지와 라벨을 하나씩 꺼내서 출력해보는 구조로 진행됩니다.
""" | |
Dataloader에 올라간 데이터셋 보기 | |
- Dataloader는 iteration에 기반한 구조이므로 iter(dataloader)처리하여 하나하나 볼 수 있다. | |
""" | |
# 라이브러리 준비 | |
import matplotlib.pyplot as plt #이미지 보여줄 때 사용 | |
import numpy as np #각종 계산 용이 | |
# 이미지 보여주기 | |
def imshow(img): | |
img = img / 2 + 0.5 # unnormalize | |
npimg = img.numpy() | |
plt.imshow(np.transpose(npimg, (1, 2, 0))) | |
plt.show() | |
# train dataset을 모아놓은 dataloader를 통해 이미지를 볼 수 있다. | |
# iter(dataloader), dataloader.next()로 보 수 있다. | |
dataiter = iter(trainloader) | |
images, labels = dataiter.next() | |
# show images | |
imshow(torchvision.utils.make_grid(images)) | |
#라벨 출력 | |
print(' '.join('%5s' % classes[labels[j]] for j in range(batch_size))) |
Output:

cat plane bird ship
2. CNN 네트워크 준비하기
앞선 데이터에 학습해야 할 모델이 필요합니다. 간단한 CNN 네트워크를 이용해서 구현을 해보려 합니다. 코드는 다음과 같고, 이번 글의 목표는 Gradient descent 부분에 더 치우쳐져 있으므로 자세한 설명은 생략하겠습니다.
""" | |
CNN 네트워크 정의하기: 본 예제에서는 Conv2d와 MaxPool2d, Linear를 간단히 쌓아올린 구조로 보겠습니다. | |
""" | |
# 라이브러리 import | |
import torch.nn as nn #CNN 네트워크를 정의할 때 사용. class 정의시 많이 필요 | |
import torch.nn.functional as F | |
# CNN 네트워크를 나타내는 class Net | |
# __init__에서 객체르 생성하면서 기본 실행이 이루어지고 | |
# forward를 통해 학습이 이루어집니다. | |
class Net(nn.Module): | |
def __init__(self): | |
super().__init__() | |
self.conv1 = nn.Conv2d(3, 6, 5) | |
self.pool = nn.MaxPool2d(2, 2) | |
self.conv2 = nn.Conv2d(6, 16, 5) | |
self.fc1 = nn.Linear(16 * 5 * 5, 120) | |
self.fc2 = nn.Linear(120, 84) | |
self.fc3 = nn.Linear(84, 10) | |
def forward(self, x): | |
x = self.pool(F.relu(self.conv1(x))) | |
x = self.pool(F.relu(self.conv2(x))) | |
x = torch.flatten(x, 1) # flatten all dimensions except batch | |
x = F.relu(self.fc1(x)) | |
x = F.relu(self.fc2(x)) | |
x = self.fc3(x) | |
return x | |
#CNN 네트워크를 정의하는 하나의 클래스 정의 완료 | |
net = Net() |
CNN 네트워크인 class Net을 정의하였고, 그에 따른 객체인 net을 생성하게 됩니다.
3. Loss function, Optimizer 정의하기
""" | |
Loss function, Optimizer 정의하기 | |
""" | |
# 라이브러리 import하기 | |
import torch.optim as optim # Optimizer정의할 때 필요 | |
criterion = nn.CrossEntropyLoss() #Loss function = Cross-entropy loss | |
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) #Optimizer = SGD with Momentum |
이번 모델은 Cross-entropy loss와 SGD(Stochastic Gradient Descent) with momentum을 이용해서 구현해보겠습니다.
4. Train dataset을 CNN 네트워크 학습하기
train시에 Gradient Descent가 이루어집니다. 대략적인 개념을 살펴보겠습니다.
들어가기에 앞서, Gradient Descent 의 대략적 개념 살펴보기
Gradient descent는 딥러닝 모델의 최고 목표인 function approximation을 위해 모델 내의 Parameter들을 조금씩 조정하게 되며, 이를 Gradient descent라 일컫습니다. Gradient Descent가 이루어지기 위해서는 단계별로 process가 이루어지며, 다음과 같습니다.
0) Loss function, Optimizer 정의하기
Repeat{
1) Loss function 계산하기
2) Derivation 계산하기
3) Back-propagation 수행하기
}
하나하나 설명해보도록하겠습니다.
0) Loss function, Optimizer 정의하기: Gradient Descent는 모델들 내의 파라미터를 조금씩 조정하는 일이라고 하였습니다. 모델 파라미터를 조정하기 위해서는 어떤 프로세스가 이루어져야 할까요. 생각해보면 모델의 현재 성능을 파악하고, 이에 맞춰서 파라미터를 조금씩 수정하는 작업이 필요합니다. 모델의 현재 성능을 파악할 때 Loss function, 파라미터를 수정할 때 Optimizer가 필요합니다.
1) Loss function 계산하기: 가지고 있는 모델로 forward-propagation을 수행했습니다. 결과값들을 하나하나 비교하여 모델의 현재 성능을 파악합니다.
2) Derivation 계산하기: Gradient Descent는 모델 파라미터 값들을 Update할 때, Optimizer가 Derivation을 이용하여 조금씩 수정해서 Derivation이 필요합니다.
3) Back-propagation 수행하기: Optimizer의 기준과 2)에서 계산한 Derivation을 바탕으로 값을 수정하며, 이 과정을 Back-propagation 과정이라 부릅니다.
이런 개념으로 바라보면 다음 실행 코드를 더 수월하게 이해할 수 있습니다.
""" | |
CNN모델 학습하기 | |
가지고 있는 것 | |
trainloader (데이터셋) | |
net() (CNN 네트워크) | |
optimizer, criterion (back-propagation 시 사용될 도구들) | |
코드에서 정의한 모델 학습 정보 | |
epoch:2번 | |
""" | |
for epoch in range(2): #epoch = 2 | |
running_loss = 0.0 | |
for i, data in enumerate(trainloader, 0): | |
# 데이터셋 꺼내놓기 [inputs, labels] (batch 당 이루어집니다.) | |
inputs, labels = data | |
# zero the parameter gradients | |
optimizer.zero_grad() | |
""" | |
Gradient Descent Process | |
0) forward-propagation | |
1) Loss funcion 계산하기 (forward-propagation의 input 예측값과 ground truth 값을 비교하여 성능 확인 | |
2) Derivation 계산하기 | |
3) Back-propagation 수행하기: Optimize | |
""" | |
outputs = net(inputs) # 0) forward-propagation | |
loss = criterion(outputs, labels) # 1) Loss funcion 계산하기: criterion | |
loss.backward() # 2) Derivation 계산하기 | |
optimizer.step() # 3) Back-propagation 수행하기: Optimize | |
# 결과 출력 | |
running_loss += loss.item() | |
if i % 2000 == 1999: # print every 2000 mini-batches | |
print('[%d, %5d] loss: %.3f' % | |
(epoch + 1, i + 1, running_loss / 2000)) | |
running_loss = 0.0 | |
print('Finished Training') |
5. CNN 네트워크로 test dataset 예측하기
우선 위에서 학습된 모델을 저장하는게 중요하겠죠. 다음 코드로 저장합니다.
PATH = './cifar_net.pth' | |
torch.save(net.state_dict(), PATH) |
이제 train dataset 말고도 test dataset을 적용하여 모델의 진짜 성능을 확인해봐야합니다. 이 때 중요한 점은 test시에는 Gradient Descent를 의무적으로 꺼줘야 한다는 점입니다. pytorch가 gradient descent를 수행할 수도 있는 불상사를 미연에 방지하기 위해 다음과 같은 코드로 예측하게 되며 결과를 확인할 수 있습니다.
""" | |
전체 데이터 성능 확인하기 | |
정확도 측정: (맞은 image 개수) / (전체 image 개수 = 맞은 image + 틀린 image) | |
""" | |
net.load_state_dict(torch.load(PATH)) | |
correct = 0 | |
total = 0 | |
# 우리는 train하는 단계가 아니므로, 혹시 모르니 gradient descent는 꺼놓도록 하겠습니다. | |
with torch.no_grad(): | |
for data in testloader: #test loader에서 데이터들을 하나씩 꺼냅니다. | |
images, labels = data | |
outputs = net(images) #image를 net에 넣으면 class당 probability를 출력합니다. | |
_, predicted = torch.max(outputs.data, 1) #가장 높은 probability를 가지는 class에게 1을 부여합니다. | |
total += labels.size(0) | |
correct += (predicted == labels).sum().item() #해당 class가 맞다면 correct에 포함합니다. | |
print('Accuracy of the network on the 10000 test images: %d %%' % ( | |
100 * correct / total)) |
이제 결과를 확인해보면 다음과 같네요.
Accuracy of the network on the 10000 test images: 54 %
54% 정확도가 나온 것을 확인하실 수 있습니다.
딥러닝 모델의 꽃인 Gradient Descent가 Pytorch에서는 어떻게 작동되는지 일련의 과정들을 살펴보았습니다. 시간이 가능하다면 다음에는 Gradient Descent의 여러 모델을 정리해보도록 하겠습니다. 긴 글 읽어주셔서 감사합니다. =)
'AI, Deep Learning Basics > Computer Vision' 카테고리의 다른 글
[Generative Model] Variational AutoEncoder 1. Basic: AE, DAE, VAE (0) | 2021.12.06 |
---|---|
[Computer Vision] Image, Video 분야 subtask 및 데이터 종류 정리 (0) | 2021.12.01 |
[Basic] 3x3 Conv, 1x1 Conv 하는 이유(FCN vs. FC Layer vs. FPN) (0) | 2021.11.20 |
[Instance segmentation] Mask R-CNN/Detectron2 모델 파일 분석 (0) | 2021.11.09 |
[논문리뷰] Everybody Dance Now (0) | 2021.04.24 |