
이 블로그글은 2019년 조재영(Kevin Jo), 김승수(SeungSu Kim)님의 딥러닝 홀로서기 세미나를 수강하고 작성한 글임을 밝힙니다.
💡 도움이 되셨다면 ♡와 팔로우 부탁드려요! 미리 감사합니다.
Github 링크 : 실습 코드 링크
💡 목표 CIFAR10 이미지를 분류하는 MLP 개발

import torch
import torchvision
import torchvision.transforms as transforms
torchvision 라이브러리를 사용하여 CIFAR-10 데이터를 다운로드한다.torchvision.transforms를 이용해 불러온 이미지 데이터를 전처리하여 학습에 적합한 형태로 변환한다.전처리 정의
transform = transforms.Compose(
{transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))}
)
ToTensor() : 이미지를 Tensor로 변환 (0~1 범위로 스케일 조정)Normalize() : 각 RGB 채널을 평균 0.5로 shift하고, 표준편차 0.5로 나누어 (-1, 1) 범위로 변환데이터셋 다운로드 및 분리
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)
torchvision.datasets.CIFAR10 : CIFAR-10 데이터셋을 다운로드하고 로드torch.utils.data.random_split() : 데이터셋을 랜덤하게 두개로 나눔데이터 로더 생성
trainloader = torch.utils.data.DataLoader(trainset, batch_size = batch_size,
shuffle = True, num_workers=2)
valloader = torch.utils.data.DataLoader(valset, batch_size = batch_size,
shuffle = False, num_workers=2)
testloader = torch.utils.data.DataLoader(testset, batch_size = batch_size,
shuffle=False, num_workers=2)
DataLoader : 모델 학습에 필요한 데이터를 미니배치 단위로 효율적으로 불러와주는 역할이미지 시각화
import matplotlib.pyplot as plt
import numpy as np
def imshow(img):
img = img / 2 + 0.5
npimg =img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show()
classes = ('plane', 'car', 'bird', 'cat', 'dear', 'dog', 'forg', 'horse', 'ship', 'truck')
dataiter = iter(trainloader)
images, labels = next(dataiter)
imshow(torchvision.utils.make_grid(images))
print(' '.join('%5s' % classes[labels[j]] for j in range(4))) # ground truth 출력

torchvision.utils.make_grid(images) : 배치 형식의 여러 이미지를 하나의 그리드로 배열💡 Ground Truth란?
모델 학습이나 평가 시 정답 레이블을 의미한다.
데이터 타입과 shape 확인
print(type(images))
print(images.shape)
torch Tensor 객체임을 확인미니 배치 크기, RGB, 가로, 세로를 의미한다.print(type(labels))
print(labels.shape)
print(labels)
torch Tensor 객체임을 확인미니배치 크기를 의미한다.모델 정의
import torch.nn as nn
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_in = nn.Linear(self.in_dim, self.hid_dim)
self.linears = nn.ModuleList(nn.Linear(self.hid_dim, self.hid_dim) for _ in range(self.n_layer-1) )
self.fc_out = nn.Linear(self.hid_dim, self.out_dim)
if self.act == 'relu':
self.act = nn.ReLU()
def forward(self, x):
x = self.act(self.fc_in(x))
for fc in self.linears:
x = self.act(fc(x))
x = self.fc_out(x)
return x
__init__: 입력 차원, 출력 차원, 은닉층 차원, 레이어 수, 활성화 함수를 인자로 받아, 입력층, 은닉층, 출력층 레이어를 정의한다.fc_in: 입력층을 은닉층으로 연결하는 첫 번째 레이어linears: 은닉층들을 연결하는 레이어 목록 (n_layer에 따라 레이어 수 결정)fc_out: 마지막 은닉층을 출력층으로 연결하는 레이어act: 활성화 함수로 ReLU를 사용하도록 설정forward: 입력 데이터 x가 각 레이어를 지나며 활성화 함수로 변환되고, 마지막 출력층에서 결과가 반환된다.모델 객체 생성
model = MLP(3072, 10, 100, 4, 'relu')
import torch.optim as optim
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
criterion: 손실 함수로 교차 엔트로피 손실(CrossEntropyLoss)을 사용한다.optimizer: 확률적 경사 하강법(SGD) 최적화 알고리즘을 사용하여 모델의 가중치를 업데이트한다.for epoch in range(2):
running_loss = 0.0
for i, data in enumerate(trainloader):
inputs, labels = data
inputs = inputs.view(-1, 3072)
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
if i % 2000 == 1999:
print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 2000))
running_loss = 0.0
print('Finished Training')
inputs = inputs.view(-1, 3072)에서 입력 이미지를 1차원으로 변환해, MLP 모델의 입력 차원(3072)에 맞춰준다.Validation
correct = 0
total = 0
val_loss = 0
with torch.no_grad():
for data in valloader:
images, labels = data
images = images.view(-1, 3072)
outputs = model(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)
acc = 100 * correct / total
print(f'Accuracy of the network on the 10000 val images: {acc:.2f}% {val_loss:.2f}')
correct = 0
total = 0
with torch.no_grad():
for data in testloader:
images, labels = data
images = images.view(-1, 3072)
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
acc = 100 * correct / total
print(f'Accuracy of the network on the 10000 test images: {acc}%')
💡 with 구문이란?
- 파이썬에서 특정 작업의 시작과 종료를 자동으로 관리해주는 구문
- 예) 파일 처리 :
with open(...) as file, 파일을 열고 블록이 끝나면 파일을 자동으로 닫아줌- 예) PyTorch 예측 과정:
with torch.no_grad(), 기울기 계산을 임시로 꺼서 메모리를 절약하고 속도를 높여줌
import argparse
# seed 설정
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
Seed란?
- 난수 생성의 시작값으로, 같은 Seed 값을 사용하면 매번 동일한 난수 생성
- 예)
np.random.seed(seed): Numpy 라이브러리에서 난수 생성 seed 고정- 예)
torch.manual_seed(123): PyTorch 라이브러리에서 난수 생성 seed 고정- 딥러닝 실험에서 결과 재현성을 보장하기 위해 사용
def experiment(args):
model = MLP(args.in_dim, args.out_dim, args.hid_dim, args.n_layer, args.act)
model.cuda()
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr = args.lr, momentum = args.mm)
## ==== Train ==== ##
for epoch in range(args.epoch):
model.train() # 학습 모드 설정
running_loss = 0.0
train_loss = 0.0
for i, data in enumerate(trainloader):
inputs, labels = data
# reshape, 여기서 -1은 남아있는 몫을 채워줌
inputs = inputs.view(-1, 3072)
inputs = inputs.cuda()
labels = labels.cuda()
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
train_loss += loss.item()
if i % 2000 == 1999: # print every 2000 mmini-batches
print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 2000))
running_loss = 0.0
# 에포크 종료 후 미니배치 개수로 나누기
train_loss = train_loss / len(trainloader)
## ==== validation ==== ##
model.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 = model(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)
acc = 100 * correct / total
print(f'Epoch {epoch}, Train Loss : {train_loss:.2f}, Val Loss : {val_loss:.2f}, Val Acc : {acc}')
## ==== Evaluation ==== ##
model.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 = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
test_acc = 100 * correct / total
print(f'Accuracy of the network on the 10000 test images: {test_acc}%')
return train_loss, val_loss, train_loss, test_acc
args에 등록하여 함수 내부 코드 수정없이 여러 실험이 가능하다.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)
experiment(args)를 실행하고, 결과를 출력한다.