【Pytorch 실습】 CIFAR10 데이터셋. 학습 및 추론, Activation fucntion = relu, tanh

아래의 코드는 Kaggle 및 Git의 공개된 코드를 적극 활용한, 과거의 공부한 내용을 정리한 내용입니다.

  • 코드를 한줄한줄 치면서 공부해보기로 했다. CIRAR10 데이터를 사용해서 모델을 학습시키고 추론해보는 일렬의 과정들을 공부해보자. 그리고 Python과 모델 구현을 잘하는 방법은 눈으로 공부하는 것도 있지만, 한줄한줄 코드를 치면서 공부하고 깨닫는 것이 매우 중요하다고 하다.
  • 데이터셋 다운로드 : https://www.cs.toronto.edu/~kriz/cifar.html
  • 이번 Post의 목적은 코드를 한줄한줄 치면서 어떻게 공부하고 검색하는지에 대한 느낌을 가져가는 것이다.

1. Activation Function : Relu

1. 데이터 Load, 분할(train,valu), Pytorch.tensor.Load

def unpickle(file):
    import pickle
    with open(file, "rb") as fo:
        dict = pickle.load(fo, encoding = "bytes")
    return dict

다운 받은 파일에 1. data_batch1,2,3,4,5, 2. test 파일이 있다. 당연히 전자는 Train데이터 들이다.

tmp = unpickle("../data/CIFAR10/cifar-10-batches-py/data_batch_1")
# 이런식으로 변수를 계속 print해보고 확인해보면, 사실 다 별거아니다. 
print(tmp.keys())
tmp[b'data'].shape
(10000, 3072)
# 위의 일렬의 과정을 data_batch_1,2,3,4,5를 모두 해야하기 때문에 함수로 정의해 사용.
def pickle_to_images_and_labels(root):
    data = unpickle(root)
    data_images = data[b'data'] / 255
    data_images = data_images.reshape(-1, 3, 32, 32).astype("float32")
    data_labels = data[b'labels']
    return data_images, data_labels
images1, label1 = pickle_to_images_and_labels("../data/CIFAR10/cifar-10-batches-py/data_batch_1")
images2, label2 = pickle_to_images_and_labels("../data/CIFAR10/cifar-10-batches-py/data_batch_2")
images3, label3 = pickle_to_images_and_labels("../data/CIFAR10/cifar-10-batches-py/data_batch_3")
images4, label4 = pickle_to_images_and_labels("../data/CIFAR10/cifar-10-batches-py/data_batch_4")
images5, label5 = pickle_to_images_and_labels("../data/CIFAR10/cifar-10-batches-py/data_batch_5")
test_images, test_labels = pickle_to_images_and_labels("../data/CIFAR10/cifar-10-batches-py/test_batch")

아하.. 분할되어 다운받은 이미지 어떻게 할지 궁금했는데, 걍 Concatenation 해버리네…?? 이렇게 데이터가 분할되어 있으면 일단 가져오고, Concatenation해버리면 되는구나 라고 알아두자.

import numpy as np
train_images = np.concatenate([images1, images2, images3, images4, images5], axis = 0)
train_labels = np.concatenate([label1, label2, label3, label4, label5], axis = 0)
test_images = np.concatenate([test_images], axis = 0)
test_labels = np.concatenate([test_labels], axis = 0)
from sklearn.model_selection import train_test_split
train_images, valid_images, train_labels, valid_labels = train_test_split(train_images, train_labels, stratify = train_labels, random_state = 42, test_size = 0.2)
print("The Shape of Train Images: ", train_images.shape)
print("The Shape of Valid Images: ", valid_images.shape)
print("The Shape of Test Images: ", test_images.shape)
The Shape of Train Images:  (40000, 3, 32, 32)
The Shape of Valid Images:  (10000, 3, 32, 32)
The Shape of Test Images:  (10000, 3, 32, 32)
print("The number of Train Labels: ", train_labels.shape)
print("The number of Valid Labels: ", valid_labels.shape)
print("The number of Test Labels: ", test_labels.shape)
The number of Train Labels:  (40000,)
The number of Valid Labels:  (10000,)
The number of Test Labels:  (10000,)
import torch
from torch.utils.data import TensorDataset, DataLoader
train_images_tensor = torch.tensor(train_images)
train_labels_tensor = torch.tensor(train_labels)
train_tensor = TensorDataset(train_images_tensor, train_labels_tensor)
train_loader = DataLoader(train_tensor, batch_size = 64, num_workers = 0, shuffle = True)

valid_images_tensor = torch.tensor(valid_images)
valid_labels_tensor = torch.tensor(valid_labels)
valid_tensor = TensorDataset(valid_images_tensor, valid_labels_tensor)
valid_loader = DataLoader(valid_tensor, batch_size = 64, num_workers = 0, shuffle = True)

test_images_tensor = torch.tensor(test_images)

2. 모델 구현

import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Conv2d(in_channels = 3, out_channels = 8, kernel_size = 3, padding = 1)
        self.conv2 = nn.Conv2d(in_channels = 8, out_channels = 16, kernel_size = 3, padding = 1)
        self.pool = nn.MaxPool2d(kernel_size = 2, stride = 2)
        self.fc1 = nn.Linear(8 * 8 * 16, 64)
        self.fc2 = nn.Linear(64, 32)
        self.fc3 = nn.Linear(32, 10)
        
    def forward(self, x):
        x = self.conv1(x) # 32 * 32 * 8
        x = F.relu(x)     # 32 * 32 * 8
        x = self.pool(x)  # 16 * 16 * 8
        x = self.conv2(x) # 16 * 16 * 16
        x = F.relu(x)     # 16 * 16 * 16
        x = self.pool(x)  # 8 * 8 * 16
        
        x = x.view(-1, 8 * 8 * 16)
        x = self.fc1(x)
        x = F.relu(x)
        x = self.fc2(x)
        x = F.relu(x)
        x = self.fc3(x)
        x = F.log_softmax(x)
        return x
USE_CUDA = torch.cuda.is_available()
DEVICE = torch.device("cuda" if USE_CUDA else "cpu")
model = CNN().to(DEVICE)
optimizer = optim.Adam(model.parameters(), lr = 0.001)
print("Model: ", model)
print("Device: ", DEVICE)
Model:  CNN(
  (conv1): Conv2d(3, 8, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (conv2): Conv2d(8, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (fc1): Linear(in_features=1024, out_features=64, bias=True)
  (fc2): Linear(in_features=64, out_features=32, bias=True)
  (fc3): Linear(in_features=32, out_features=10, bias=True)
)
Device:  cuda

3. 학습 및 추론

def train(model, train_loader, optimizer):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(DEVICE), target.to(DEVICE, dtype = torch.int64)
        optimizer.zero_grad()
        output = model(data)
        loss = F.cross_entropy(output, target)
        loss.backward()
        optimizer.step()
        
        if batch_idx % 100 == 0:
            print("Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}".format(
            epoch, batch_idx * len(data), len(train_loader.dataset), 100. * batch_idx / len(train_loader), loss.item()))
def evaluate(model, valid_loader):
    model.eval()
    
    valid_loss = 0
    correct = 0
    
    with torch.no_grad():
        for data, target in valid_loader:
            data, target= data.to(DEVICE), target.to(DEVICE, dtype = torch.int64)
            output = model(data)
            valid_loss += F.cross_entropy(output, target, reduction = "sum").item()
            prediction = output.max(1, keepdim = True)[1]  # 예측한 class score 중 가장 큰 index 어딘지? 즉 Class가 무엇인지. 
            correct += prediction.eq(target.view_as(prediction)).sum().item() # Ground True와 예측한 값이 몇개인지 계산해서 정확도를 파악한다. 
    
    valid_loss /= len(valid_loader.dataset)
    valid_accuracy = 100. * correct / len(valid_loader.dataset)
    return valid_loss, valid_accuracy
'''TRAINING'''
EPOCHS = 10
for epoch in range(1, EPOCHS + 1):
    train(model, train_loader, optimizer)
    valid_loss, valid_accuracy = evaluate(model, valid_loader)
    print("[EPOCH : {}], \tValidation Loss: {:.4f}, \tValidation Accuracy: {:.2f} % \n".format(
        epoch, valid_loss, valid_accuracy))
Train Epoch: 9 [0/40000 (0%)]	Loss: 1.180444
Train Epoch: 9 [6400/40000 (16%)]	Loss: 1.049773
Train Epoch: 9 [12800/40000 (32%)]	Loss: 0.980881
Train Epoch: 9 [19200/40000 (48%)]	Loss: 1.017157
Train Epoch: 9 [25600/40000 (64%)]	Loss: 0.960528
Train Epoch: 9 [32000/40000 (80%)]	Loss: 1.220239
Train Epoch: 9 [38400/40000 (96%)]	Loss: 0.947019
[EPOCH : 9], 	Validation Loss: 1.1328, 	Validation Accuracy: 60.19 % 

Train Epoch: 10 [0/40000 (0%)]	Loss: 0.898633
Train Epoch: 10 [6400/40000 (16%)]	Loss: 1.141634
Train Epoch: 10 [12800/40000 (32%)]	Loss: 0.852636
Train Epoch: 10 [19200/40000 (48%)]	Loss: 1.075439
Train Epoch: 10 [25600/40000 (64%)]	Loss: 1.237481
Train Epoch: 10 [32000/40000 (80%)]	Loss: 1.139879
Train Epoch: 10 [38400/40000 (96%)]	Loss: 0.973299
[EPOCH : 10], 	Validation Loss: 1.1565, 	Validation Accuracy: 58.96 % 

4. 추론 및 테스트 출력해보기

def testset_prediction(model, test_images_tensor):
    model.eval()
    result = []
    
    with torch.no_grad():
        for data in test_images_tensor:
            data = data.to(DEVICE)
            output = model(data.view(-1, 3, 32, 32))
            prediction = output.max(1, keepdim = True)[1]
            result.append(prediction.tolist())
    return result
from sklearn.metrics import accuracy_score
test_predict_result = testset_prediction(model, test_images_tensor)
accuracy_score(test_labels, np.squeeze(test_predict_result))
0.5898
test_images[30].shape
(3, 32, 32)
import matplotlib.pyplot as plt
plt.imshow(np.transpose(test_images[30], (1, 2, 0)))
plt.show()
test_labels[30]
6
test_predict_result[30]
[[6]]


2. Activation Function : tanh

코드 전계 과정은 위와 동일

import torch
USE_CUDA = torch.cuda.is_available()
DEVICE = torch.device("cuda" if USE_CUDA else "cpu")
from torchvision import transforms, datasets
train_loader = torch.utils.data.DataLoader(
    datasets.CIFAR10("../data/CIFAR_10/",
                     train = True,
                     download = True,
                     transform = transforms.Compose([
                         transforms.RandomHorizontalFlip(),
                         transforms.ToTensor(),
                         transforms.Normalize((0.5, 0.5, 0.5),
                                              (0.5, 0.5, 0.5))])), batch_size = 64, shuffle = True)

test_loader = torch.utils.data.DataLoader(
    datasets.CIFAR10("../data/CIFAR_10",
                     train = False,
                     transform = transforms.Compose([
                         transforms.RandomHorizontalFlip(),
                         transforms.ToTensor(),
                         transforms.Normalize((0.5, 0.5, 0.5),
                                              (0.5, 0.5, 0.5))])), batch_size = 64)
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Conv2d(in_channels = 3, out_channels = 8, kernel_size = 3, padding = 1, stride = 2)
        self.conv2 = nn.Conv2d(in_channels = 8, out_channels = 16, kernel_size = 3, padding = 1)
        self.conv3 = nn.Conv2d(in_channels = 16, out_channels = 32, kernel_size = 3, padding = 1)
        self.conv4 = nn.Conv2d(in_channels = 32, out_channels = 64, kernel_size = 3, padding = 1)
        self.pool = nn.MaxPool2d(kernel_size = 2, stride = 2)
        self.fc1 = nn.Linear(1 * 1 * 64, 64)
        self.fc2 = nn.Linear(64, 32)
        self.fc3 = nn.Linear(32, 10)

    def forward(self, x):
        x = self.conv1(x) # 32 * 32 * 3 -> 16 * 16 * 8
        x = F.tanh(x)     # 16 * 16 * 8
        x = self.pool(x)  # 8 * 8 * 8

        x = self.conv2(x) # 16 * 16 * 8 -> 16 * 16 * 16
        x = F.tanh(x)     # 16 * 16 * 16
        x = self.pool(x)  # 8  *  8 * 16

        x = self.conv3(x) # 8 * 8 * 16 -> 8 * 8 * 32
        x = F.tanh(x)     # 8 * 8 * 32
        x = self.pool(x)  # 4 * 4 * 32

        x = self.conv4(x) # 4 * 4 * 32 -> 4 * 4 * 64
        x = F.tanh(x)     # 4 * 4 * 64
        x = self.pool(x)  # 2 * 2 * 64

        x = x.view(-1, 1 * 1 * 64)
        x = self.fc1(x)
        x = F.tanh(x)
        x = self.fc2(x)
        x = F.tanh(x)
        x = self.fc3(x)
        x = F.log_softmax(x, dim = 1)
        return x


model = CNN().to(DEVICE)
optimizer = optim.Adam(model.parameters(), lr = 0.001)
print("DEVICE: ", DEVICE)
print("MODEL: ", model)
DEVICE:  cuda
MODEL:  CNN(
  (conv1): Conv2d(3, 8, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
  (conv2): Conv2d(8, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (conv3): Conv2d(16, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (conv4): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (fc1): Linear(in_features=64, out_features=64, bias=True)
  (fc2): Linear(in_features=64, out_features=32, bias=True)
  (fc3): Linear(in_features=32, out_features=10, bias=True)
)
def train(model, train_loader, optimizer):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(DEVICE), target.to(DEVICE)
        optimizer.zero_grad()
        output = model(data)
        loss = F.cross_entropy(output, target)
        loss.backward()
        optimizer.step()

        if batch_idx % 100 == 0:
            print("Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}".format(
                epoch, batch_idx * len(data), len(train_loader.dataset), 100. * batch_idx / len(train_loader), loss.item()))
def evaluate(model, test_loader):
    model.eval()

    test_loss = 0
    correct = 0

    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(DEVICE), target.to(DEVICE)
            output = model(data)
            test_loss += F.cross_entropy(output, target, reduction = "sum").item()
            prediction = output.max(1, keepdim = True)[1]
            correct += prediction.eq(target.view_as(prediction)).sum().item()

    test_loss /= len(test_loader.dataset)
    test_accuracy = 100. * correct / len(test_loader.dataset)
    return test_loss, test_accuracy
EPOCHS = 3
for epoch in range(1, EPOCHS + 1):
    train(model, train_loader, optimizer)
    test_loss, test_accuracy = evaluate(model, test_loader)
    print("[{}] Test Loss: {:.4f}, accuracy: {:.2f}%\n".format(epoch, test_loss, test_accuracy))
Train Epoch: 3 [0/50000 (0%)]	Loss: 1.320623
Train Epoch: 3 [6400/50000 (13%)]	Loss: 1.344092
Train Epoch: 3 [12800/50000 (26%)]	Loss: 1.440148
Train Epoch: 3 [19200/50000 (38%)]	Loss: 1.254739
Train Epoch: 3 [25600/50000 (51%)]	Loss: 1.224567
Train Epoch: 3 [32000/50000 (64%)]	Loss: 1.368665
Train Epoch: 3 [38400/50000 (77%)]	Loss: 1.217434
Train Epoch: 3 [44800/50000 (90%)]	Loss: 1.176838
[3] Test Loss: 1.2608, accuracy: 55.20%

파라메터 수 확인하는 방법 :

print("CNN's number of Parameters: ", sum([p.numel() for p in model.parameters()]))
CNN's number of Parameters:  31098

© All rights reserved By Junha Song.