import torch
import torch.nn as nn
import torch.optim as optim
inputs = torch.Tensor(1,1,28,28)
print(inputs.shape)
# 결과 : torch.Size([1, 1, 28, 28])
- 먼저 필요한 모듈들을 import 해주고 임의로 설정들을 준 변수를 tensor 형태로 만들어줍니다
첫번째 Conv2D
# padding을 통해 이미지 크기(?)를 유지 할 수 있음, 현재 코드에서 padding을 주지 않았을 시에는 이미지 크기(?)가 26이 됨
conv1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3, padding='same')
out = conv1(inputs)
print(out.shape) # 채널만 늘어난 것을 볼 수 있음
# 결과 : torch.Size([1, 32, 28, 28])
- 먼저 CNN 레이어 과정을 진행하게 되는데 컨볼루션 작업을 진행합니다
- 이때 kernel_size는 filtter의 크기를 나타내는것 입니다
- padding = same 은 이미지 데이터의 크기를 동일하게 유지하기 위한 설정입니다
- 따라서 기존의 설정에서 out_channels 가 32로 늘어난걸 확인할 수 있습니다
첫번째 MaxPool2D
pool = nn.MaxPool2d(kernel_size=2)
out = pool(out)
print(out.shape) # kernel_size 설정에 따라 이미지의 크기가 비율에 맞춰서 줄어듦(비율에 맞춰서 줄여줘야 함)
# 결과 : torch.Size([1, 32, 14, 14])
- 이후 이미지의 불필요한 부분을 제외하기 위해 풀링 작업을 진행합니다
- 기존의 28로 설정한 w, h를 줄여주기 위해 kernel_size로 조절해 줍니다
- 따라서 높이와 너비가 절반으로 줄어든 것을 확인할 수 있습니다
두번째 Conv2D, MaxPool2D
conv2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, padding='same')
out = conv2(out)
pool = nn.MaxPool2d(kernel_size=2)
out = pool(out)
print(out.shape) # kernel_size 설정에 따라 이미지의 크기가 줄어든 것을 볼 수 있음
# 결과 : torch.Size([1, 64, 7, 7])
flatten = nn.Flatten()
out = flatten(out)
print(out.shape) # 64 * 7 * 7
# 결과 : torch.Size([1, 3136])
fc = nn.Linear(3136, 10)
out = fc(out)
print(out.shape)
# 결과 : torch.Size([1, 10])
- 첫번째 과정과 동일하게 채널은 늘려주고 불필요한 부분은 줄여주면서 이미지를 조절할 과정을 구축합니다
- 이후 학습시키기 위해 조절(?)된 이미지를 학습시키기 위한 레이어를 만들어서 모델을 완성해줍니다
CNN으로 MNIST 분류하기
import torchvision.datasets as datasets
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
from torch.utils.data import DataLoader
device = 'cuda' if torch.cuda.is_available() else 'cpu'
train_data = datasets.MNIST(
root = 'data',
train = True,
transform = transforms.ToTensor(),
download = True
)
test_data = datasets.MNIST(
root = 'data',
train = False,
transform = transforms.ToTensor(),
download = True
)
loader = DataLoader(
dataset = train_data, # list 화 할 필요 없음
batch_size = 64,
shuffle = True
)
- 이제 이렇게 만들어진 CNN 모델로 MNIST 데이터셋을 학습시켜보겠습니다
- 필요한 모듈(?)을 불러온 뒤 MNIST 데이터셋을 train과 test로 나누어 줍니다
- 이후 학습할 train 데이터셋의 DataLoader를 만들어줍니다
- 여기서 batch_size는 한번에 학습할 데이터의 개수입니다
model = nn.Sequential(
nn.Conv2d(1,32,kernel_size=3,padding='same'),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2),
nn.Conv2d(32,64,kernel_size=3,padding='same'),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2),
nn.Flatten(),
nn.Linear(7*7*64,10)
).to(device)
- 그리고 위에서 만들었던 모델을 torch 형태로 만들어서 model 변수안에 저장해 줍니다
- 구조를 확인해보면 하나의 input 으로 받아서 채널을 32개로 늘리고 fillter를 3으로 설정해줍니다
- 이후 높이와 너비를 반으로 줄이는데 이 작업을 두번 반복해서 최종 레이어에 넣어줍니다.
- 그러면 이미지 구조는 (1, 64, 7, 7)로 하나가 들어가 64개의 채널로 나누어지고 높, 너비는 7이 되게 됩니다
# 학습
# optimizer : Adam
# Epoch 1/10 Loss : 0.180667 Accurcay : 94.62%
# ...
# Epoch 10/10 Loss : 0.010452 Accurcay : 99.64%
optimizer = optim.Adam(model.parameters(), lr=0.001)
epochs = 10
for epoch in range(epochs + 1):
sum_losses = 0
sum_accs = 0
for x,y in loader: # 전체 데이터를 64개씩 뽑아서 돌림
x,y = x.to(device), y.to(device) # 위에서 모델을 GPU로 설정해 놓았기 때문에 받아온 데이터도 GPU로 설정해 주어야 함
y_pred = model(x)
loss = nn.CrossEntropyLoss()(y_pred, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
sum_losses = sum_losses+loss.item() # GPU로 설정했을 경우에는 value 값만 뽑아와야 하기 때문에 item() 함수를 적용
y_prob = nn.Softmax(1)(y_pred)
y_pred_index = torch.argmax(y_prob, axis=1)
acc = (y == y_pred_index).float().sum() / len(y) * 100
sum_accs = sum_accs + acc
avg_loss = sum_losses / len(loader)
avg_acc = sum_accs / len(loader)
print(f'Epoch {epoch:4d}/{epochs} Loss : {avg_loss:.6f} Accuracy : {avg_acc:.2f}')
# 결과
Epoch 0/10 Loss : 0.185871 Accuracy : 94.55
Epoch 1/10 Loss : 0.056025 Accuracy : 98.28
Epoch 2/10 Loss : 0.041502 Accuracy : 98.74
Epoch 3/10 Loss : 0.033060 Accuracy : 98.93
Epoch 4/10 Loss : 0.026642 Accuracy : 99.19
Epoch 5/10 Loss : 0.021057 Accuracy : 99.33
Epoch 6/10 Loss : 0.018160 Accuracy : 99.41
Epoch 7/10 Loss : 0.014223 Accuracy : 99.53
Epoch 8/10 Loss : 0.012186 Accuracy : 99.61
Epoch 9/10 Loss : 0.009820 Accuracy : 99.71
Epoch 10/10 Loss : 0.009074 Accuracy : 99.71
- 이렇게 준비가 끝났으면 데이터를 학습시켜주는데 학습 횟수는 epochs으로 10회 진행하며 위에서 지정한 train 데이터셋인 loader를 불러옵니다
- 그리고 64개씩 학습을 하고 그 과정에서 발생한 loss 수치와 Accuracy 수치를 저장하고 다시 초기화하는 과정을 반복해서
- epochs이 한번 돌때마다 그 수치를 확인해줍니다
- 그렇게 10번을 반복하는 반복문을 작동시킵니다
loader = DataLoader(
dataset = test_data, # list 화 할 필요 없음
batch_size = 64,
shuffle = True
)
sum_accs = 0
model.eval() # 모델을 테스트 모드로 전환(초기화(?) 해서 메모리 사용량을 줄임)
for x,y in loader:
x,y = x.to(device), y.to(device)
y_pred = model(x)
y_prob = nn.Softmax(1)(y_pred)
y_pred_index = torch.argmax(y_prob, axis=1)
accuracy = (y == y_pred_index).float().sum() / len(y) * 100
sum_accs = sum_accs + accuracy
avg_acc = sum_accs / len(loader)
print(f'테스트 정확도는 {avg_acc:.2f}% 입니다')
# 결과 : 테스트 정확도는 99.18% 입니다
- 학습이 된 모델을 가지고 test 데이터를 통해 정확도를 확인해 줍니다
- 데이터로더를 생성하고 데이터를 학습된 모델의 성능을 확인하기 위해 반복문을 작동시켜 줍니다
- 그리고 학습과정과 동일하게 정확도 수치를 출력합니다