이론만 공부하면 지치니 실습 코드 리뷰를 하나 진행하자.
손글씨 Dataset으로 유명한 mnist 데이터 셋이다.
1. Module Import
import numpy as np
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.nn.functional as F
from torchvision import transforms, datasets
필요한 모듈들 import한다.
- numpy, plt 는 수치 계산과 시각화를 위하여.
- nn은 신경망 모델을 위해, F는 함수들 따오기 위해 등
2. 딥러닝 모델을 설계할 때 활용되는 장비 확인
if torch.cuda.is_available():
DEVICE = torch.device('cuda')
else:
DEVICE = torch.device('cpu')
print('Using PyTorch version:', torch.__version__, ' Device:', DEVICE)
- cuda를 지원하는 gpu가 사용가능한지 확인, 만일 가능하면 gpu를, 그렇지 않다면 cpu를 사용하도록 설정.
BATCH_SIZE = 32
EPOCHS = 10
- hyperparameter 설정. 이전에 확인했듯 batch size는 2의 거듭승이 좋음.
3. MNIST 데이터 다운로드
train_dataset = datasets.MNIST(root = "../data/MNIST",
train = True,
download = True,
transform = transforms.ToTensor())
test_dataset = datasets.MNIST(root = "../data/MNIST",
train = False,
transform = transforms.ToTensor())
train_loader = torch.utils.data.DataLoader(dataset = train_dataset,
batch_size = BATCH_SIZE,
shuffle = True)
test_loader = torch.utils.data.DataLoader(dataset = test_dataset,
batch_size = BATCH_SIZE,
shuffle = False)
- train에 대한 param 설정을 통해 train set / test set 구분하여 다운
- transforms.ToTensor(): PIL 이미지나 넘파이 배열을 PyTorch의 FloatTensor로 변환하고, 픽셀 값을 [0, 1] 범위로 스케일링합니다.
- 그 후 받아진 dataset을 dataloader로 변환(변환이 맞는 표현인진 모르겠다).
- 이전에 설정한 BATCH_SIZE (하이퍼파라미터)를 받아오며, train data에 대해선 shuffle을 사용하여 bias를 방지.
4. 데이터 확인하기 (1)
for (X_train, y_train) in train_loader:
print('X_train:', X_train.size(), 'type:', X_train.type())
print('y_train:', y_train.size(), 'type:', y_train.type())
break
결과는 다음과 같다.
for-loop를 바로 break 때리는 것을 보아 하나의 배치만 확인하고자 하는 용도로 사용한듯.
-> 데이터 형태와 타입이 제대로 들어오는 지 단순 확인 용도.
X train set에 대해서는
- 배치 크기(batch size): 32
- 채널 수(channels): 1 (MNIST는 흑백 이미지이므로 채널이 1개)
- 이미지 높이(height): 28 픽셀
- 이미지 너비(width): 28 픽셀
와 같이 이루어지며, float tensor로 지정되게 됨.
반면 y train 데이터는 1차원 텐서로 label 값인 '정수'를 표현하게 됨.
5. 데이터 확인하기 (2)
pltsize = 1
plt.figure(figsize=(10 * pltsize, pltsize))
for i in range(10):
plt.subplot(1, 10, i + 1)
plt.axis('off')
plt.imshow(X_train[i, :, :, :].numpy().reshape(28, 28), cmap = "gray_r")
plt.title('Class: ' + str(y_train[i].item()))
- figsize (10,1)로 설정.
- 위에서 for loop을 돌려서 X_train과 y_train에 첫번째 batch에 대한 데이터가 할당됨.
- 그 중 1~10번째 데이터들만 가져오고 y_train[i]를 통해 같이 표기하여 붙여줌.
--------------------- 여기까진 기본적인 data load 및 세팅 ---------------------
6. Multi Layer Perceptron (MLP) 모델 설계하기
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(28 * 28, 512)
self.fc2 = nn.Linear(512, 256)
self.fc3 = nn.Linear(256, 10)
def forward(self, x):
x = x.view(-1, 28 * 28)
x = self.fc1(x)
x = F.sigmoid(x)
x = self.fc2(x)
x = F.sigmoid(x)
x = self.fc3(x)
x = F.log_softmax(x, dim = 1)
return x
- 여기서 다층 퍼셉트론에 대한 모델 정의
- MLP는 입력층, 하나 이상의 은닉층, 출력층으로 구성된 feed-forward 신경망
- MNIST 데이터셋의 이미지(28x28 픽셀)를 입력으로 받아서, 10개의 클래스(숫자 0~9)에 대한 예측을 출력
여기서 PyTorch의 장점 -> feed-forward function만 정의를 하여도 backward function에 대해서는 자동으로 정의가 됨.
(물론 function들은 nn.functional에서 제공해주는 함수들을 사용하여하여함.)
Net 클래스는 nn.Module을 상속하여 모델 정의. 부모 클래스인 nn.Module의 생성자를 호출하여 초기화.
fc1, fc2, fc3은 레이어 정의.
fc1은 28*28 픽셀 데이터를 1차원 벡터로 펼침. (MNIST 이미지 사이즈가 28*28 임)
fc3의 출력 노드수는 당연히 10으로 맞춰준다. (0~9까지의 10가지 숫자)
forward 메서드를 정의하며
입력 이미지를 flatten한 후 first fully-connected layer를 통과 시킨 후 sigmoid적용. 이하 반복.
그리고 마지막에 fc3까지 적용된 출력 값에 log softmax 함수를 적용하여 클래스별 로그 확률 계산
7. Optimizer, Objective Function 설정하기
model = Net().to(DEVICE)
optimizer = torch.optim.SGD(model.parameters(), lr = 0.01, momentum = 0.5)
criterion = nn.CrossEntropyLoss()
print(model)
- 위에서 생성한 Net class의 객체 생성.
- 옵티마이저 설정. SGD 사용하며 learning rate은 0.01.
- momentum은 처음 봤는데 '이전 업데이트의 방향을 일정 부분 유지하여 학습 속도를 향상시키는 역할'이라고 한다.
- loss function (criterion) 설정. 크로스 엔트로피 손실 함수 사용.
print한 결과값은 다음과 같다.
8. MLP 모델 학습을 진행하며 학습 데이터에 대한 모델 성능을 확인하는 함수 정의
def train(model, train_loader, optimizer, log_interval):
model.train()
for batch_idx, (image, label) in enumerate(train_loader):
image = image.to(DEVICE)
label = label.to(DEVICE)
optimizer.zero_grad()
output = model(image)
loss = criterion(output, label)
loss.backward()
optimizer.step()
if batch_idx % log_interval == 0:
print("Train Epoch: {} [{}/{} ({:.0f}%)]\tTrain Loss: {:.6f}".format(
epoch, batch_idx * len(image),
len(train_loader.dataset), 100. * batch_idx / len(train_loader),
loss.item()))
- train이라는 function 정의. 뒤에 10에서 보면 알겠지만 한 epoch에 대해 학습 / 학습에 대한 성능 확인하는 함수.
- 큰 틀에서 함수는 parameter로 모델과 훈련 데이터(엄밀하게 말하면 배치 단위로 제공되는), 옵티마이저, 학습 상태를 출력할 간격 총 4개를 받는다.
- model.train() : 모델의 학습 모드를 설정하는 메소드.
- for loop에서 enumerate을 통해 batch index를 출력하며 돌림.
- image와 label로 가져오며 이는 X(우리가 학습시킬 이미지), y(label) 정답을 의미한다.
- pytorch의 옵티마이저는 gradient가 누적되므로 계속 초기화 해준다.
- 모델에 image를 집어넣어 output을 계산해주고 이를 loss function에 넣어 오차를 구한다.
- 역전파가 계산되며 각 파라미터에 gradient가 저장된다.
- 이를 optimizer.step()을 통해 모델의 파라미터들을 업데이트 시킨다.
그 아래는 batch를 돌리며 모델이 어느 정도 성능을 내는지, 배치가 얼마나 돌아갔는지 등을 표기한다.
9. 학습되는 과정 속에서 검증 데이터에 대한 모델 성능을 확인하는 함수 정의
def evaluate(model, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for image, label in test_loader:
image = image.to(DEVICE)
label = label.to(DEVICE)
output = model(image)
test_loss += criterion(output, label).item()
prediction = output.max(1, keepdim = True)[1]
correct += prediction.eq(label.view_as(prediction)).sum().item()
test_loss /= (len(test_loader.dataset) / BATCH_SIZE)
test_accuracy = 100. * correct / len(test_loader.dataset)
return test_loss, test_accuracy
모델의 성능을 평가할 evaluate이라는 function을 정의하자.
model.eval()을 호출하면 모델이 평가 모드로 전환됨.
- 평가 모드로 전환되면 드롭아웃 비활성화, 배치 정규화의 이동 평균과 이동 분산이 업데이트 되지 않음.
- test_loss와 correct을 0으로 초기화.
- test_loss : 평가할 데이터의 총 loss 수치. correct : 모델이 정확하게 맞춘 숫자.
- with torch.no_grad(): 이 블럭 내에서는 자동 미분 비활성화. 불필요한 계산 안하니 메모리 할당(사용량?) 이점.
- 이전 train function과 대략적 비슷한 흐름. test_loader를 인자로 받아 for문을 돌리며, loss값을 계속 test_loss에 누적시켜 더함.
- output의 가장 큰 값을의 Index를 출력하여 prediction과 label의 동일한지 여부 확인. 맞으면 더하고 아님말고.
10. MLP 학습 실행하며 Train, Test set의 Loss 및 Test set Accuracy 확인하기
for epoch in range(1, EPOCHS + 1):
train(model, train_loader, optimizer, log_interval = 200)
test_loss, test_accuracy = evaluate(model, test_loader)
print("\n[EPOCH: {}], \tTest Loss: {:.4f}, \tTest Accuracy: {:.2f} % \n".format(
epoch, test_loss, test_accuracy))
맨 마지막 부분이다.
위에서 정의한 함수들을 사용하며 맨 앞에서 정의한 epoch만큼 for문을 돌리며 train, evaluate을 실행한다.
Notice
모든 코드는 하기 링크에서 가져왔으며 내 이해를 위한 글임.
https://github.com/Justin-A/DeepLearning101/blob/master/2-1_MNIST_MLP.ipynb
DeepLearning101/2-1_MNIST_MLP.ipynb at master · Justin-A/DeepLearning101
Code about DeepLearning101 Book. Contribute to Justin-A/DeepLearning101 development by creating an account on GitHub.
github.com
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
이제 모델을 만들고 학습을 했으면 실제 상황에 써봐야하지 않겠는가.
1. 테스트 할 데이터 생성
파워포인트로 직접 그렸다. 물론 데이터는 하나하나 저장해주어야 한다.
2. Preprocessing
우리가 이전에 데이터셋으로 쓴 이미지는 다음과 같이
1. 검은색 배경에 흰색 글씨
2. 28*28 pixel의 데이터이다.
def preprocess_image(image_path):
image = Image.open(image_path).convert('L')
image = image.resize((28, 28), Image.LANCZOS)
image = ImageOps.invert(image)
transform = transforms.Compose([
transforms.ToTensor()
])
image = transform(image)
image = image.unsqueeze(0)
print(image)
return image
따라서 image = ImageOps.invert(image) 를 통해 반전을 시켜주며 Resize로 사이즈를 맞춰준다.
그럼 다음과 같은 이미지가 나온다.
3. 실제 테스트
image_path = '/test_data_5.png'
input_image = preprocess_image(image_path).to(DEVICE)
with torch.no_grad():
output = model(input_image)
_, predicted = torch.max(output.data, 1)
print('예측된 숫자:', predicted.item())
이를 예측한다.
(근데 생각보다 많이 부정확하다. 5개 중 3개만 맞췄다. 원인 파악을 해봐야겠다.)
'ML & AI > Pytorch' 카테고리의 다른 글
순환 신경망(Recurrent Neural Network) (2) | 2024.09.24 |
---|---|
[Pytorch로 시작하는 딥 러닝 입문] 3. 머신 러닝 입문하기(Machine Learning Basics) (0) | 2024.09.21 |
[Pytorch로 시작하는 딥 러닝 입문] 2. 파이토치 기초(PyTorch Basic) (4) | 2024.09.21 |
[Pytorch로 시작하는 딥 러닝 입문] 1. 시작 & 딥 러닝을 시작하기 전에 (1) | 2024.09.18 |