[ํ์ดํ ์น] ํ์ดํ ์น๋ก CNN ๋ชจ๋ธ์ ๊ตฌํํด๋ณด์! (๊ธฐ์ดํธ + DataLoader ์ฌ์ฉ๋ฒ)
์๋ณธ ๊ฒ์๊ธ: https://velog.io/@euisuk-chung/ํ์ดํ ์น-ํ์ดํ ์น๋ก-CNN-๋ชจ๋ธ์-๊ตฌํํด๋ณด์-๊ธฐ์ดํธ-DataLoader-์ฌ์ฉ๋ฒ
์ค๋์ MNIST๋ฐ์ดํฐ๋ก Convolutional Neural Network(์ดํ CNN)์ ๊ตฌํํ๊ณ ๋๋ ค๋ณด๋ ์๊ฐ์ ๊ฐ๋๋ก ํ๊ฒ ์ต๋๋ค!
๋จผ์ , CNN์ ํฌ๊ฒ ์๋์ ๊ฐ์ ๊ตฌ์ฑ์์๋ก ์ด๋ฃจ์ด์ ธ์์ต๋๋ค.
- ํฉ์ฑ๊ณฑ ์ฐ์ฐ(CNN) : ์ด๋ฏธ์ง์ ํน์ฑ ์ถ์ถ
- ๋งฅ์คํ๋ง(Max Pooling) : ์ด๋ฏธ์ง์ ํน์ฑ ์ถ์ฝ
- ์์ ์ฐ๊ฒฐ ์ ๊ฒฝ๋ง(Fully Connected Network) : ์ถ์ถ ๋ฐ ์ถ์ฝ๋ ํน์ง์ ์ ๋ ฅ์ ์ฌ์ฉํ์ฌ downstream task ์ํ
Import Library
๊ฐ๋ณ๊ฒ ์ด๋ค ๋ชจ๋ธ๋ค์ importํ ์ง ์ดํด๋ณผ๊น์?
1
2
3
4
5
6
7
8
9
10
11
12
import torch
import torch.nn as nn # ์ ๊ฒฝ๋ง๋ค์ด ํฌํจ๋จ
import torch.optim as optim # ์ต์ ํ ์๊ณ ๋ฆฌ์ฆ๋ค์ด ํฌํจ๋จ
import torch.nn.init as init # ํ
์์ ์ด๊ธฐ๊ฐ์ ์ค
import torchvision.datasets as datasets # ์ด๋ฏธ์ง ๋ฐ์ดํฐ์
์งํฉ์ฒด
import torchvision.transforms as transforms # ์ด๋ฏธ์ง ๋ณํ ํด
from torch.utils.data import DataLoader # ํ์ต ๋ฐ ๋ฐฐ์น๋ก ๋ชจ๋ธ์ ๋ฃ์ด์ฃผ๊ธฐ ์ํ ํด
import numpy as np
import matplotlib.pyplot as plt
Set Hyperparameter
ํ์ต์ ํ์ํ Hyperparameter๋ฅผ ์ ์ํด๋ณผ๊น์?
batch_size
: batch size๋ ํ ๋ฒ์ batch๋ง๋ค ์ฃผ๋ ๋ฐ์ดํฐ ์ํ์ size๋ก, ๋๋ ์ง ๋ฐ์ดํฐ ์ ์ ๋ปํ๋ฉฐ, iteration๋ ํ๋ฒ์ epoch๋ฅผ batch_size๋ก ๋๋์ด์ ์คํํ๋ ํ์๋ผ๊ณ ์๊ฐํ๋ฉด ๋ฉ๋๋ค.learning_rate
: learning_rate์ ์ด๋ ์ ๋์ ํฌ๊ธฐ๋ก ๊ธฐ์ธ๊ธฐ๊ฐ ์ค์ด๋๋ ์ง์ ์ผ๋ก ์ด๋ํ๊ฒ ๋๊ฐ๋ฅผ ๋ํ๋ด๋ ์งํ๋ก, ํ์ต์ด ์ผ๋ง๋ ๋นจ๋ฆฌ ์งํ๋๋๊ฐ๋ฅผ ์ ํด์ฃผ๋ ์งํ๋ผ๊ณ ์๊ฐํ๋ฉด ๋ฉ๋๋ค.num_epoch
: ํ ๋ฒ์ epoch๋ ์ธ๊ณต ์ ๊ฒฝ๋ง์์ ์ ์ฒด ๋ฐ์ดํฐ ์ ์ ๋ํด forward pass/backward pass ๊ณผ์ ์ ๊ฑฐ์น ๊ฒ์ ๋งํฉ๋๋ค. ์ฆ, ์ ์ฒด ๋ฐ์ดํฐ ์ ์ ๋ํด ํ ๋ฒ ํ์ต์ ์๋ฃํ ์ํ๋ผ๊ณ ๋ ๋ณผ ์ ์๋๋ฐ์. ์ด๋ ๊ฒ ์ ์ฒด ๋ฐ์ดํฐ์ ์ ๋ช๋ฒ ๋ณผ ๊ฒ์ธ๊ฐ๋ฅผ num_epoch๋ฅผ ํตํด ์ ์ํด์ฃผ๊ฒ ๋ฉ๋๋ค.
1
2
3
batch_size = 100
learning_rate = 0.0002
num_epoch = 10
Load MNIST Data
ํ์ต์ฉ ๋ฐ์ดํฐ์ ์ธ MNIST๋ฅผ ๊ฐ์ ธ์๋ณด๊ฒ ์ต๋๋ค. ๊ฐ๊ฐ ํจ์๋ ๋ค์๊ณผ ๊ฐ์ด ์ ์๋ ์ ์์ต๋๋ค.
-
root
=โ์ํ๋ ๊ฒฝ๋กโ- root๋ ์ฐ๋ฆฌ๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ด๋์๋ค๊ฐ ์ ์ฅํ๊ณ , ๊ฒฝ๋ก๋ก ์ฌ์ฉํ ์ง๋ฅผ ์ ์ํด์ค๋๋ค.
-
train
=True(๋๋ False)- train์ ์ฐ๋ฆฌ๊ฐ ์ง๊ธ ์ ์ํ๋ ๋ฐ์ดํฐ๊ฐ ํ์ต์ฉ์ธ์ง ํ ์คํธ์ฉ์ธ์ง ์ ์ํด์ค๋๋ค.
-
transform
=transforms.ToTensor()- ๋ฐ์ดํฐ์ ์ด๋ ํ ๋ณํ์ ์ค ๊ฒ์ธ๊ฐ๋ฅผ ์ ์ํด์ค๋๋ค.
- ํด๋น
transforms.ToTensor()
์ ๊ฒฝ์ฐ, ๋ชจ๋ธ์ ๋ฃ์ด์ฃผ๊ธฐ ์ํด ํ ์ ๋ณํ์ ํด์ค์ ์๋ฏธํฉ๋๋ค.
-
target_transform
=None- ๋ผ๋ฒจ(ํด๋์ค)์ ์ด๋ ํ ๋ณํ์ ์ค ๊ฒ์ธ๊ฐ๋ฅผ ์ ์ํด์ค๋๋ค.
-
download
=True- ์์์ ์ง์ ํด์ค ๊ฒฝ๋ก์ ํด๋น ๋ฐ์ดํฐ๊ฐ ์์ ์ ๋ค์ด๋ก๋ํ๋๋ก ์ ์ํด์ค๋๋ค.
1
2
mnist_train = datasets.MNIST(root="../Data/", train=True, transform=transforms.ToTensor(), target_transform=None, download=True)
mnist_test = datasets.MNIST(root="../Data/", train=False, transform=transforms.ToTensor(), target_transform=None, download=True)
Define Loaders
DataLoader๋ ์์์๋ ๋ง์๋๋ ธ๋ฏ์ด DataLoader๋ ํ์ต ๋ฐ ๋ฐฐ์น๋ก ๋ชจ๋ธ์ ๋ฃ์ด์ฃผ๊ธฐ ์ํ ํด์ ๋๋ค. ์์์ ์ ์ํ ๋ฐ์ดํฐ์ ์ DataLoader์ ๋ฃ์ด์ฃผ๊ฒ ๋๋ฉด ์ฐ๋ฆฌ๊ฐ ์ ์ํด์ค ์กฐ๊ฑด์ ๋ง๊ฒ ๋ชจ๋ธ์ Train, Inferenceํ ๋ ๋ฐ์ดํฐ๋ฅผ Loadํด์ฃผ๊ฒ ๋ฉ๋๋ค.
-
batch_size
=batch_size- ์ ์๋ ๋ฐ์ดํฐ๋ฅผ batch_size๊ฐ์๋งํผ ๋ฌถ์ด์ ๋ชจ๋ธ์ ๋ฃ์ด์ฃผ๊ฒ ๋ค๋ ์๋ฏธ์ ๋๋ค.
-
shuffle
=True- ๋ฐ์ดํฐ๋ฅผ ์์ด์ค ๊ฒ์ธ๊ฐ ์ง์ ํด์ฃผ๋ ํ๋ผ๋ฏธํฐ์ ๋๋ค.
-
num_workers
=2- ๋ฐ์ดํฐ๋ฅผ ๋ฌถ์๋ ์ฌ์ฉํ ํ๋ก์ธ์์ ๊ฐ์๋ฅผ ์๋ฏธํฉ๋๋ค.
-
drop_last
=True- ๋ฌถ๊ณ ๋จ์ ๋ฐ์ดํฐ๋ฅผ ๋ฒ๋ฆด ๊ฒ์ธ๊ฐ๋ฅผ ์ง์ ํด์ฃผ๋ ํ๋ผ๋ฏธํฐ์ ๋๋ค.
1
2
train_loader = DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=2, drop_last=True)
test_loader = DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=2, drop_last=True)
Define CNN(Base) Model
๋จผ์ class๋ฅผ ํตํด CNN class๋ฅผ ์ ์ํด๋ณด๊ฒ ์ต๋๋ค. torch์ nn.Module์ ์ฌ์ฉํ์ฌ nn.Module class๋ฅผ ์์๋ฐ๋ CNN์ ๋ค์๊ณผ ๊ฐ์ด ์ ์ํ ์ ์์ต๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class CNN(nn.Module):
def __init__(self):
# superํจ์๋ CNN class์ ๋ถ๋ชจ class์ธ nn.Module์ ์ด๊ธฐํ
super(CNN, self).__init__()
# batch_size = 100
self.layer = nn.Sequential(
# [100,1,28,28] -> [100,16,24,24]
nn.Conv2d(in_channels=1,out_channels=16,kernel_size=5),
nn.ReLU(),
# [100,16,24,24] -> [100,32,20,20]
nn.Conv2d(in_channels=16,out_channels=32,kernel_size=5),
nn.ReLU(),
# [100,32,20,20] -> [100,32,10,10]
nn.MaxPool2d(kernel_size=2,stride=2),
# [100,32,10,10] -> [100,64,6,6]
nn.Conv2d(in_channels=32, out_channels=64, kernel_size=5),
nn.ReLU(),
# [100,64,6,6] -> [100,64,3,3]
nn.MaxPool2d(kernel_size=2,stride=2)
)
self.fc_layer = nn.Sequential(
# [100,64*3*3] -> [100,100]
nn.Linear(64*3*3,100),
nn.ReLU(),
# [100,100] -> [100,10]
nn.Linear(100,10)
)
def forward(self,x):
# self.layer์ ์ ์ํ ์ฐ์ฐ ์ํ
out = self.layer(x)
# view ํจ์๋ฅผ ์ด์ฉํด ํ
์์ ํํ๋ฅผ [100,๋๋จธ์ง]๋ก ๋ณํ
out = out.view(batch_size,-1)
# self.fc_layer ์ ์ํ ์ฐ์ฐ ์ํ
out = self.fc_layer(out)
return out
device๋ฅผ ์๋์ ๊ฐ์ด ์ ์ธํ์ฌ gpu๊ฐ ์ฌ์ฉ ๊ฐ๋ฅํ ๊ฒฝ์ฐ์๋ device๋ฅผ gpu๋ก ์ค์ ํ๊ณ , ๋ถ๊ฐ๋ฅํ๋ฉด cpu๋ก ์ค์ ํด์ค ์ ์์ต๋๋ค.
1
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
CNN().to(device)
์ ์ธ์ ํตํด ์ ์ํ ๋ชจ๋ธ ๊ฐ์ฒด๋ฅผ ์ ์ธํ๊ณ , ์ด๋ฅผ ์ง์ ํ ์ฅ์น(device)๋ก ์ฌ๋ ค์ค๋๋ค.
1
model = CNN().to(device)
๋ชจ๋ธ์ด ํ์ต์ ์ํํ๋ ค๋ฉด, ์์คํจ์์ ์ต์ ํํจ์๊ฐ ํ์ํ๋ฐ ์ด๋ ์๋์ ๊ฐ์ด ์ ์ํ ์ ์์ต๋๋ค. (์์คํจ์๋ Cross Entropy, ์ต์ ํํจ์๋ Adam Optimizer์ ์ฌ์ฉํ์์ต๋๋ค)
๋ํ, model.parameters()
์ lr=learning_rate
์ torch.optim.Adam()
๋ก ๊ฐ์ธ์ค์ผ๋ก์จ ๋ชจ๋ธ์ ํ๋ผ๋ฏธํฐ๋ค์ ์ฌ์ ์ ์ ์ํ learning_rate๋ก ์
๋ฐ์ดํธ ํด์ฃผ๊ณ ์ ํฉ๋๋ค.
1
2
3
loss_func = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
Train Model
train_loader์์ image์ label์ ์์ batch_size๋งํผ ๋ฐ์์ ๋ชจ๋ธ์ ์ ๋ฌํ์ฌ ์์ค์ ๊ณ์ฐํ๊ณ , ์์ค์ ๋ํ ๊ฒฝ์ฌํ๊ฐ๋ฒ์ ์งํํ์ฌ ๋ชจ๋ธ์ ์ ๋ฐ์ดํธํฉ๋๋ค. ์ด๋ 1000๋ฒ์งธ iteration๋ง๋ค loss๋ฅผ ์ถ๋ ฅํ๊ณ , ์ด๋ฅผ loss_arr์ ์ถ๊ฐํ๋๋ก ์ฝ๋๋ฅผ ์์ฑํ์์ต๋๋ค.
enumerate(train_loader)
ํจ์๋ฅผ ํตํด ๊ฐ๊ฐ batch์index(j)
์[image,label]
๋ฅผ ๋ฐ์์ x, y๋ก ์ ์ํด์ค๋๋คoptimizer.zero_grad()
๋ฅผ ํตํด ์ง๋ loop์์ ๊ณ์ฐํ๋ ๊ธฐ์ธ๊ธฐ๋ฅผ 0์ผ๋ก ์ด๊ธฐํํด์ค๋๋ค.loss.backward()
ํธ์ถ์ ํตํด ๊ฐ๊ฐ์ model(weight) parameter์ ๋ํ ๊ธฐ์ธ๊ธฐ๋ฅผ ๊ณ์ฐํ๊ณ ,optimizer.step()
ํจ์๋ฅผ ํธ์ถํ์ฌ ์ธ์๋ก ๋ค์ด๊ฐ๋model.parameters()
์์ ๋ฆฌํด๋๋ ๋ณ์๋ค์ ๊ธฐ์ธ๊ธฐ์learning_rate
๋ฅผ ๊ณฑํ์ฌ ๋นผ์ฃผ๋ฉด์ ์ด๋ฅผ ์ ๋ฐ์ดํธํ๊ฒ ๋ฉ๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
loss_arr =[]
for i in range(num_epoch):
for j,[image,label] in enumerate(train_loader):
x = image.to(device)
y= label.to(device)
optimizer.zero_grad()
output = model.forward(x)
loss = loss_func(output,y)
loss.backward()
optimizer.step()
if j % 1000 == 0:
print(loss)
loss_arr.append(loss.cpu().detach().numpy())
Test Model
๋ง์ง๋ง ๋ถ๋ถ์ ํ์ต๋ ๋ชจ๋ธ์ ๋ฐํ์ผ๋ก ํ ์คํธ ๋ฐ์ดํฐ์ ๋ํ์ฌ ๊ฒ์ฆํ๋ ๋ถ๋ถ์ ๋๋ค.
model.eval()
:model.eval์ ํด๋น ๋ชจ๋ธ์ ๋ชจ๋ ๋ ์ด์ด๊ฐ eval mode์ ๋ค์ด๊ฐ๊ฒ ํด์ค๋๋ค. ์ด ๋ง์ ๊ณง, ํ์ตํ ๋๋ง ์ฌ์ฉํ๋ ๊ฐ๋ ์ธ Dropout์ด๋ Batchnorm ๋ฑ์ ๋นํ์ฑํ ์ํจ๋ค๋ ๊ฒ์ ์๋ฏธํ๋ค.torch.no_grad()
: with torch.no_grad()๋ pytorch์ autograd engine์ ๋นํ์ฑํ ์ํต๋๋ค. ์ฆ, ๋์ด์ gradient๋ฅผ ํธ๋ํนํ์ง ์์์ ์๋ฏธํ๊ณ , ์ด์ ๋ฐ๋ผ ํ์ํ ๋ฉ๋ชจ๋ฆฌ๊ฐ ์ค์ด๋ค๊ณ ๊ณ์ฐ์๋๊ฐ ์ฆ๊ฐํ๊ฒ ๋ฉ๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
correct = 0
total = 0
# evaluate model
model.eval()
with torch.no_grad():
for image,label in test_loader:
x = image.to(device)
y= label.to(device)
output = model.forward(x)
# torch.maxํจ์๋ (์ต๋๊ฐ,index)๋ฅผ ๋ฐํ
_,output_index = torch.max(output,1)
# ์ ์ฒด ๊ฐ์ += ๋ผ๋ฒจ์ ๊ฐ์
total += label.size(0)
# ๋์ถํ ๋ชจ๋ธ์ index์ ๋ผ๋ฒจ์ด ์ผ์นํ๋ฉด correct์ ๊ฐ์ ์ถ๊ฐ
correct += (output_index == y).sum().float()
# ์ ํ๋ ๋์ถ
print("Accuracy of Test Data: {}%".format(100*correct/total))
๊ธด ๊ธ ์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค ^~^