이 블로그글은 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)
를 실행하고, 결과를 출력한다.