250x250
Recent Posts
Recent Comments
Archives
- Today
- Total
KimDove
안녕하세요, 딥러닝 엔지니어 김둘기 입니다.
비둘기 둥지
[인공지능 기초 / pytorch] 3. DNN 본문
728x90
1.Fashion MNIST 데이터 셋 살펴보기
- 28 x 28 픽셀 70,000개의 흑백 이미지로 구성된 이미지 데이터 셋
- 레이블은 신발, 드레스, 가방 등 총 10가지 카테고리 존재
< ! > 이미지 데이터를 다루기 위한 파이토치, 토치비전 모듈들
- torch.utils.data
→ 데이터 셋의 표준을 정의, 로딩, 셔플에 쓰는 도구들이 들어있는 모듈
→ 파이토치 모델을 학습시키기 위한 데이터 셋 표준을 torch.utils.data.Dataset에 정의
→ 데이터 셋 모듈을 상속하는 파생 클래스는 학습에 필요한 데이터를 로딩해주는
torch.utils.data.DataLoader 인스턴스 입력으로 사용 - torchvision.datasets
→ torch.utils.data.Dataset을 상속하는 이미지 데이터 셋 모음
→ Fashion MNIST 데이터 셋을 포함하고 있음. - torchvision.transforms
→ 이미지 데이터 셋에 쓸 수 있는 여러 변활 필터를 담고 있는 모듈
→ 크기 조절, 크롭 등 이미지를 수정할 수 있고, 밝기, 대비도 조절할 수 있음. - torchvision.utils
→ 이미지 데이터를 저장하고 시각화 하기 위한 도구가 들어있는 모듈
from torchvision import datasets, transforms, utils
import matplotlib.pyplot as plt
from torch.utils import data
import numpy as np
## 이미지를 텐서로 변환해주는 코드
## transforms.Resize(), transforms.Normalize() 등 사용할 수 있다.
## Compose 함수 안에 리스트로 입력하면 순서대로 변환이 이루어짐.
transform = transforms.Compose([
transforms.ToTensor()
])
## 학습용, 검증용 Fashion MNIST 데이터 셋 다운받아주는 함수.
## download = True | 데이터 셋을 root로 지정한 경로에 저장
## train = True | 학습용 데이터 셋일때 사용
train_dataset = datasets.FashionMNIST(
root = './dataset',
train = True,
download = True,
transform = transform
)
valid_dataset = datasets.FashionMNIST(
root = './dataset',
train = False,
download = True,
transform = transform
)
## 출력 결과
< ! > torchvision.Transforms에서 자주 사용되는 함수들
함수 이름 | 설명 |
ToTensor | 데이터를 파이토치 텐서로 변환 |
Resize | 이미지 크기 조정 |
Normalize | 주어진 평균과 표준편차를 이용하여 정규화 |
RandomHorizontalFlip | 무작위로 이미지의 좌우를 반전시킴 |
RandomCrop | 이미지를 무작위로 잘라주는 기능 |
## torchvision.datasets로 생성한 데이터 셋은 torch.utils.data.Dataset에 넣어 바로 사용할 수 있다.
## batch_size도 인자값으로 입력하여 한 번에 불러올 데이터의 갯수를 지정할 수 있다.
batch_size = 16
train_loader = data.DataLoader(
dataset = train_dataset,
batch_size = batch_size
)
valid_loader = data.DataLoader(
dataset = valid_dataset,
batch_size = batch_size
)
## 배치 한 개만 뽑아 데이터를 확인해 봄.
sample_data = iter(train_loader)
images, labels = next(sample_data)
## 여러 이미지를 모아 하나의 이미지로 생성 가능
image = utils.make_grid(images, padding = 0)
## image는 파이토치 텐서 : matplotlib과 호환 X → matplotlib과 호환되는numpy 행렬로 변환
np_img = image.numpy()
plt.figure(figsize = (10, 7))
## matplotlib이 인식하는 순서가 달라 np.transpose 함수를 사용하여 첫 번째 차원을 맨 뒤로 보냄.
plt.imshow(np.transpose(np_img, (1, 2, 0)))
plt.show()
print(f'\nlabels : {labels}, length of labels : {len(labels)}')
## 각 인덱스 번호에 맞는 레이블을 지정한 딕셔너리
CLASSES = {
0: 'T-shirt/top',
1: 'Trouser',
2: 'Pullover',
3: 'Dress',
4: 'Coat',
5: 'Sandal',
6: 'Shirt',
7: 'Sneaker',
8: 'Bag',
9: 'Ankle Boot'
}
print(f'labels : {[CLASSES[int(idx)] for idx in labels]}')
## 출력 결과
labels : ['Ankle Boot', 'T-shirt/top', 'T-shirt/top', 'Dress', 'T-shirt/top', 'Pullover', 'Sneaker', 'Pullover', 'Sandal', 'Sandal', 'T-shirt/top', 'Ankle Boot', 'Sandal', 'Sandal', 'Sneaker', 'Ankle Boot']
2. Fashion MNIST 분류모델 학습
- torch.cuda.is_available() 함수를 사용해 CUDA를 이용할 수 있는지 알 수 있음.
from torchvision import transforms, datasets
import torch.nn.functional as F
import torch.optim as optim
import torch.nn as nn
import torch
USE_CUDA = torch.cuda.is_available()
DEVICE = torch.device("cuda" if USE_CUDA else "cpu")
DEVICE
## 출력 결과
device(type='cuda')
2-1. 이미지 분류를 위한 인공 신경망 구현
## 하이퍼 파라미터 지정
#! CPU, GPU의 메모리 크기가 2의 배수이기 때문에 배치 크기가 2의 거듭제곱이면 메모리 면에서 데이터를
## 주고받는 효율을 높일 수 있다고 한다.
EPOCHS, BATCH_SIZE, LR = 30, 64, 1e-2
class NN(nn.Module):
def __init__(self):
super(NN, self).__init__()
## 입력 이미지 데이터가 28 x 28 x 1 = 784이기 때문에
self.fc1 = nn.Linear(784, 256)
self.fc2 = nn.Linear(256, 128)
## 분류해야하는 라벨의 갯수가 10개 이므로.
self.fc3 = nn.Linear(128, 10)
def forward(self, x):
x = x.view(-1, 784)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
2-2. 학습 및 시험 과정 작성
- torch_ANN에서는 이진분류로 binary cross entropy를 사용하였다.
- 하지만 이번 작업에서는 10개의 레이블을 분류하는 다중 분류로 cross entropy를 사용한다.
< ! > 크로스 엔트로피 참조 | [김성훈 교수님 강의]
## to() 함수를 통해 모델 학습을 CPU로 진행할지, GPU로 진행할지 지정해줌.
model = NN().to(DEVICE)
optimizer = optim.SGD(model.parameters(), lr = LR)
## 학습 함수
def train(model, train_loder, optim):
## 모델을 학습 모드로 전환
model.train()
train_loss, correct = 0, 0
for batch_idx, (data, lb) in enumerate(train_loader):
data, lb = data.to(DEVICE), lb.to(DEVICE)
optimizer.zero_grad()
output = model(data)
loss = F.cross_entropy(output, lb)
loss.backward()
optimizer.step()
## output.max는 가장 큰 값과 인덱스 값을 반환해준다.
pred = output.max(1, keepdim = True)[1]
## eq() 함수는 값이 일치하면 1, 아니면 0을 출력
## sum() 함수를 거쳐 미니 배치에서 모델이 정답을 맞힌 개수를 구할 수 있다.
## view_as() 함수는 인자값에 들어가는 텐서의 크기대로 바꿔준다.
correct += pred.eq(lb.view_as(pred)).sum().item()
train_loss += loss
train_loss /= len(train_loader.dataset)
train_acc = 100 * correct / len(train_loader.dataset)
return train_loss, train_acc
- 모델을 학습 시킬때 학습 데이터에 최적화한 모델이 아니라 모든 데이터에서 높은 성능을 보여야 한다.
- 모든 데이터에 최적화하는 것을 일반화 (generalization)이라 한다.
- 학습 데이터를 기반으로 한 모델이 학습하지 않은 데이터에 얼마나 적응하는지를 수치로 나타낸 것을
일반화 오류(generlization error)라고 한다. - 모델 층의 종류와 크기, 배치 크기, 학습률 등 머신러닝 모델이 배우지 않고 사용자가 지정해주는 값을
하이퍼 파라미터(hyper parameter)라고 한다.
## 시험 함수
def evaluate(model, test_loader):
## 모델을 평가 모드로 바꿈.
test_loss, correct = 0, 0
## 평가 과정에서는 기울기를 계산하지 않아도 되므로 torch.no_grad를 수행해줌.
with torch.no_grad():
for data, lb in test_loader:
data, lb = data.to(DEVICE), lb.to(DEVICE)
output = model(data)
## 모든 오차 더하기
## reduction = 'sum'을 지정해주어 미니배치의 평균 대신 합을 받아온다.
test_loss += F.cross_entropy(output, lb, reduction = 'sum').item()
## output.max는 가장 큰 값과 인덱스 값을 반환해준다.
pred = output.max(1, keepdim = True)[1]
## eq() 함수는 값이 일치하면 1, 아니면 0을 출력
## sum() 함수를 거쳐 미니 배치에서 모델이 정답을 맞힌 개수를 구할 수 있다.
## view_as() 함수는 인자값에 들어가는 텐서의 크기대로 바꿔준다.
correct += pred.eq(lb.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
test_acc = 100 * correct / len(test_loader.dataset)
return test_loss, test_acc
## 학습을 돌려보자
for epoch in range(1, EPOCHS + 1):
train_loss, train_acc = train(model, train_loader, optim)
test_loss, test_acc = evaluate(model, valid_loader)
print(f'[{epoch} / {EPOCHS}] \nTrain Loss : {train_loss:.3f} | Train Acc : {train_acc:.3f} \nTest Loss : {test_loss:.3f} | Test Acc : {test_acc:.3f}')
## 출력 결과
[1 / 30]
Train Loss : 0.014 | Train Acc : 70.517
Test Loss : 0.656 | Test Acc : 76.650
[2 / 30]
Train Loss : 0.008 | Train Acc : 80.910
Test Loss : 0.536 | Test Acc : 80.480
[3 / 30]
Train Loss : 0.008 | Train Acc : 83.017
Test Loss : 0.489 | Test Acc : 82.620
[4 / 30]
Train Loss : 0.007 | Train Acc : 84.153
Test Loss : 0.606 | Test Acc : 76.860
[5 / 30]
Train Loss : 0.007 | Train Acc : 84.948
Test Loss : 0.458 | Test Acc : 83.940
(... 중략 ...)
[29 / 30]
Train Loss : 0.004 | Train Acc : 90.772
Test Loss : 0.333 | Test Acc : 88.170
[30 / 30]
Train Loss : 0.004 | Train Acc : 90.755
Test Loss : 0.335 | Test Acc : 88.250
- 학습 데이터 셋에 대해서만 모델 성능이 제대로 나오고, 시험용 데이터 셋에 대해서 성능이 나오지 않는 경우
과대 적합(overfitting)이라 한다.
< ! > 너무 학습 데이터에만 치중되어 유연성이 부족해지고, 새로운 데이터에 대해 성능이 잘 나오지 않는 현상이다. - 과적합과 반대로 학습을 제대로 진행하지 않은 상황을 과소 적합(underfitting)현상이라고 한다.
- 검증 데이터셋에 대한 성능이 나빠지기 시작하기 직전이 가장 적합한 모델이다.
- 이 타이밍에 모델을 저장하여 이용하는 것을 조기 종료 (early stopping)이라 한다.
- 이 경우는 데이터가 너무 적어 발생한 overfitting의 예시로 보면된다.
2-3. 과대 적합 피하는 방법
- 과대 적합을 막는 방법으로는 두 가지 방법이 있다.
(1) 데이터를 늘리는 방법(data augmentation)
(2) 드랍아웃(dropout)을 모델 구조에 적용
2-3.1. 데이터 늘리기
- 세상의 모든 데이터를 모으고 레이블링 하는 것은 불가능 하기 때문에, 이미 가진 데이터를 최대한 늘려야한다.
- 파이토치에서 데이터를 늘리는 방법으로는 토치비전의 transforms 패키지를 사용한다.
train_loader = torch.utils.data.DataLoader(
datasets.FashionMNIST('./.data',
train = True,
download = True,
transform = transforms.Compose([
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize((0.1307, ), (0.3081, ))
])),
batch_size = BATCH_SIZE, shuffle = True)
valid_loader = torch.utils.data.DataLoader(
datasets.FashionMNIST('./.data',
train = False,
download = True,
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307, ), (0.3081, ))
])),
batch_size = BATCH_SIZE, shuffle = True)
2-3.2. 드롭아웃 (Dropout)
- 드롭아웃은 학습 진행과정에서 신경망의 일부를 사용하지 않는 방법
- 학습에서 배재된 뉴런 외에 다른 뉴런들에 가중치를 분산시키고 개별 뉴런이 특징에 고정되는 현상을 방지하는 기능
< ! > 검증과 시험 단계에서는 모든 뉴런을 사용함.
class Net(nn.Module):
def __init__(self, dropout_p = 0.2):
super(Net, self).__init__()
self.fc1 = nn.Linear(784, 256)
self.fc2 = nn.Linear(256, 128)
self.fc3 = nn.Linear(128, 10)
## 드롭아웃 지정
self.dropout_p = dropout_p
def forward(self, x):
x = x.view(-1, 784)
x = F.relu(self.fc1(x))
## 드롭아웃 추가
## self.training은 model.train()인 경우 True, model.eval()인 경우 False로 변함.
x = F.dropout(x, training = self.training, p = self.dropout_p)
x = F.relu(self.fc2(x))
x = F.dropout(x, training = self.training, p = self.dropout_p)
x = self.fc3(x)
return x
model = Net().to(DEVICE)
optimizer = optim.SGD(model.parameters(), lr = LR)
## 학습을 돌려보자
for epoch in range(1, EPOCHS + 1):
train_loss, train_acc = train(model, train_loader, optim)
test_loss, test_acc = evaluate(model, valid_loader)
print(f'[{epoch} / {EPOCHS}] \nTrain Loss : {train_loss:.3f} | Train Acc : {train_acc:.3f} \nTest Loss : {test_loss:.3f} | Test Acc : {test_acc:.3f}')
## 출력 결과
[1 / 30]
Train Loss : 0.016 | Train Acc : 65.202
Test Loss : 0.703 | Test Acc : 75.560
[2 / 30]
Train Loss : 0.010 | Train Acc : 78.405
Test Loss : 0.590 | Test Acc : 78.910
[3 / 30]
Train Loss : 0.008 | Train Acc : 81.563
Test Loss : 0.524 | Test Acc : 81.400
[4 / 30]
Train Loss : 0.008 | Train Acc : 82.907
Test Loss : 0.497 | Test Acc : 82.400
(... 중략 ...)
[27 / 30]
Train Loss : 0.005 | Train Acc : 89.115
Test Loss : 0.368 | Test Acc : 86.930
[28 / 30]
Train Loss : 0.005 | Train Acc : 89.272
Test Loss : 0.357 | Test Acc : 87.350
[29 / 30]
Train Loss : 0.005 | Train Acc : 89.367
Test Loss : 0.355 | Test Acc : 87.440
[30 / 30]
Train Loss : 0.005 | Train Acc : 89.377
Test Loss : 0.357 | Test Acc : 86.980
99. 참고자료
99-1. 도서
- 한빛 미디어 | 펭귄브로의 3분 딥러닝 - 파이토치 맛
99-2. 웹사이트
99-3. 데이터 셋
- torchvision | Fashion MNIST
전체코드
내용 추가 이력
부탁 말씀
개인적으로 공부하는 과정에서 오류가 있을 수 있으니, 오류가 있는 부분은 댓글로 정정 부탁드립니다.
728x90
'인공지능 공부 > Pytorch' 카테고리의 다른 글
[인공지능 기초 / pytorch] 5. 그래프와 pytorch_geometric (0) | 2022.11.24 |
---|---|
[인공지능 기초 / pytorch] 4. CNN (0) | 2022.07.20 |
[인공지능 기초 / pytorch] 2. ANN (0) | 2022.06.18 |
[인공지능 기초 / pytorch] 1. 파이토치 기초 (1) | 2022.06.18 |
Comments