[Standalone DLç] #15 Lab - 09 - How to Write Well-Organized DL Code from Scratch

Data Preparation

import torch
import torchvision
import torchvision.transforms as transforms
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import argparse
import numpy as np

# transform : 이미지를 tensor로 변환해주는 작업 
transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, transform=transform)
trainset, valset = torch.utils.data.random_split(trainset, [40000, 10000])
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                       download=True, transform=transform)

trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
                                          shuffle=True, num_workers=2)
valloader = torch.utils.data.DataLoader(valset, batch_size=4,
                                        shuffle=False)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
                                         shuffle=False, num_workers=2)

classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

Model Architecture

class MLP(nn.Module):
    def __init__(self, in_dim, out_dim, hid_dim, n_layer, act):
        super(MLP, self).__init__()
        self.in_dim = in_dim
        self.out_dim = out_dim
        self.hid_dim = hid_dim
        self.n_layer = n_layer
        self.act = act

        self.fc = nn.Linear(self.in_dim, self.hid_dim)
        self.linears = nn.ModuleList()

        for i in range(self.n_layer-1):
            self.linears.append(nn.Linear(self.hid_dim, self.hid_dim)) # hid_dim으로 들어와서 hid_dim으로 나간다 
        self.fc2 = nn.Linear(self.hid_dim, self.out_dim)

        if self.act == 'relu': 
            self.act = nn.ReLU() # 원하는 activation function이 있으면 condition을 더 추가할 수 있음 

    def forward(self, x):
        x = self.act(self.fc(x))
        for fc in self.linears:
            x = self.act(fc(x))
        x = self.fc2(x)
        return x

net = MLP(3072, 10, 100, 4, 'relu')
print(net) # 모델 구조가 출력된다 

# 아래와 같은 아웃풋이 출력됨 
MLP(
  (fc): Linear(in_features=3072, out_features=100, bias=True)
  (linears): ModuleList(
    (0-2): 3 x Linear(in_features=100, out_features=100, bias=True)
  )
  (fc2): Linear(in_features=100, out_features=10, bias=True)
  (act): ReLU()
)
import torch.optim as optim
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
for epoch in range(2):  

    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        inputs, labels = data 
        #print(inputs.shape) # torch.Size([4, 3, 32, 32]) 
        # 4x3x32x32=12288 개의 tensor로 이루어져 있는 상태 

        inputs = inputs.view(-1, 3072) # -1 자리에는 12288/3072=4 값이 들어가게 됨 
        #print(inputs.shape) # torch.Size([4, 3072])

        optimizer.zero_grad()

        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        if i % 2000 == 1999:   
            print(f'[{epoch + 1}, {i + 1:5d}] loss: {running_loss / 2000:.3f}')
            running_loss = 0.0

print('Finished Training')

출력은 다음과 같다 [1, 2000] loss: 1.755 [1, 4000] loss: 1.663 [1, 6000] loss: 1.617 [1, 8000] loss: 1.593 -> 모델을 학습할 수 있는 상태가 됐다 (loss도 줄어드는 것 같고 .. )

correct = 0
total = 0

with torch.no_grad():
    for data in testloader:
        images, labels = data
        images = images.view(-1, 3072) # 추가해줌 
        outputs = net(images)
        _, predicted = torch.max(outputs, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print(f'Accuracy of the network on the 10000 test images: {100 * correct // total} %')

출력 Accuracy of the network on the 10000 test images: 47 %

correct = 0
total = 0

with torch.no_grad():
    for data in valloader:
        images, labels = data
        images = images.view(-1, 3072)
        outputs = net(images)
        _, predicted = torch.max(outputs, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print(f'Accuracy of the network on the 10000 test images: {100 * correct // total} %')

출력 Accuracy of the network on the 10000 test images: 45 %

Experiment

def experiment(args):

    net = MLP(args.in_dim, args.out_dim, args.hid_dim, args.n_layer, args.act)
    net.cuda()
    print(net)

    criterion = nn.CrossEntropyLoss()
    optimizer = optim.SGD(net.parameters(), lr=args.lr, momentum=args.mm)

    for epoch in range(args.epoch):  

        # ==== Train ===== #
        net.train()

        running_loss = 0.0
        train_loss = 0.0
        for i, data in enumerate(trainloader, 0):
            optimizer.zero_grad() 

            # get the inputs
            inputs, labels = data
            inputs = inputs.view(-1, 3072)

            inputs = inputs.cuda()
            labels = labels.cuda()

            # forward + backward + optimize
            outputs = net(inputs)
            loss = criterion(outputs, labels)
            loss.backward()
            optimizer.step()

            # print statistics
            running_loss += loss.item()
            train_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

        # ==== Validation ====== #
        net.eval()

        correct = 0
        total = 0
        val_loss = 0
        with torch.no_grad():
            for data in valloader:
                images, labels = data
                images = images.view(-1, 3072)

                images = images.cuda()
                labels = labels.cuda()

                outputs = net(images)

                loss = criterion(outputs, labels)
                val_loss += loss.item()

                _, predicted = torch.max(outputs.data, 1)
                total += labels.size(0)
                correct += (predicted == labels).sum().item()

            val_loss = val_loss / len(valloader)
            val_acc = 100 * correct / total

        print('Epoch {}, Train Loss: {}, Val Loss: {}, Val Acc: {}'.format(epoch, train_loss, val_loss, val_acc ))

    # ===== Evaluation ===== #
    net.eval()

    correct = 0
    total = 0
    with torch.no_grad():
        for data in testloader:
            images, labels = data
            images = images.view(-1, 3072)
            images = images.cuda()
            labels = labels.cuda()

            outputs = net(images)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()

        test_acc = 100 * correct / total

    return train_loss, val_loss, val_acc, test_acc
seed = 123
np.random.seed(seed)
torch.manual_seed(seed)

parser = argparse.ArgumentParser()
args = parser.parse_args("")

args.n_layer = 5
args.in_dim = 3072
args.out_dim = 10
args.hid_dim = 100
args.act = 'relu'

args.lr = 0.001
args.mm = 0.9
args.epoch = 2

list_var1 = [4, 5, 6]
list_var2 = [50, 100, 150]

for var1 in list_var1:
    for var2 in list_var2:
        args.n_layer = var1
        args.hid_dim = var2
        result = experiment(args)
        print(result) 

출력값

MLP( (fc): Linear(in_features=3072, out_features=50, bias=True) (linears): ModuleList( (0-2): 3 x Linear(in_features=50, out_features=50, bias=True) ) (fc2): Linear(in_features=50, out_features=10, bias=True) (act): ReLU() ) [1, 2000] loss: 2.202 [1, 4000] loss: 1.900 [1, 6000] loss: 1.791 [1, 8000] loss: 1.722 [1, 10000] loss: 1.693 Epoch 0, Train Loss: 18616.791673600674, Val Loss: 1.7005987400531768, Val Acc: 39.32 [2, 2000] loss: 1.626 [2, 4000] loss: 1.608 [2, 6000] loss: 1.584 [2, 8000] loss: 1.584 [2, 10000] loss: 1.544 Epoch 1, Train Loss: 15891.401199430227, Val Loss: 1.564451172888279, Val Acc: 43.8 (15891.401199430227, 1.564451172888279, 43.8, 45.1) MLP( (fc): Linear(in_features=3072, out_features=100, bias=True) (linears): ModuleList( (0-2): 3 x Linear(in_features=100, out_features=100, bias=True) ) (fc2): Linear(in_features=100, out_features=10, bias=True) (act): ReLU() ) [1, 2000] loss: 2.198 [1, 4000] loss: 1.924 [1, 6000] loss: 1.792 [1, 8000] loss: 1.727 [1, 10000] loss: 1.644 Epoch 0, Train Loss: 18569.359001129866, Val Loss: 1.6861703926563263, Val Acc: 38.98 [2, 2000] loss: 1.594 [2, 4000] loss: 1.579 [2, 6000] loss: 1.561 [2, 8000] loss: 1.545 [2, 10000] loss: 1.525 Epoch 1, Train Loss: 15606.880003154278, Val Loss: 1.5393070907831192, Val Acc: 44.97 (15606.880003154278, 1.5393070907831192, 44.97, 45.94) MLP( (fc): Linear(in_features=3072, out_features=150, bias=True) (linears): ModuleList( (0-2): 3 x Linear(in_features=150, out_features=150, bias=True) ) (fc2): Linear(in_features=150, out_features=10, bias=True) (act): ReLU() ) [1, 2000] loss: 2.179 [1, 4000] loss: 1.895 [1, 6000] loss: 1.760 [1, 8000] loss: 1.684 [1, 10000] loss: 1.646 Epoch 0, Train Loss: 18327.9337708354, Val Loss: 1.644619299721718, Val Acc: 40.36 [2, 2000] loss: 1.570 [2, 4000] loss: 1.566 [2, 6000] loss: 1.520 [2, 8000] loss: 1.507 [2, 10000] loss: 1.500 Epoch 1, Train Loss: 15323.40272564441, Val Loss: 1.521736914730072, Val Acc: 46.1

여기서 파라미터를 바꿔가며 성능을 향상시킬 수 있다.