pytorch

[pytorch] Deep Neural Network (DNN) 로 FashionMNIST 구현해보기

독립성이 강한 ISFP 2023. 4. 25. 23:53
728x90
반응형

fashion_mnist 데이터셋을 사용하여 심층 신경망을 직접 구현해 보겠습니다.

 

fashion_mnist 데이터셋은 토치비전에 내장된 예제 데이터로 운동화, 셔츠, 샌들 같은 작은 이미지의 모음이며, 기본 MNIST 데이터셋처럼 열 가지로 분류될 수 있는 28x28 픽셀의 이미지 7만 개로 구성되어 있다.

데이터셋을 자세히 살펴보면 훈련 데이터는 0-255 사이의 값을 갖는 28x28 크기의 넘파이 배열이고, 레이블(정답) 데이터는 0-9 사이 정수 값을 갖는 배열입니다.

1. 라이브러리 호출

import numpy as np
import matplotlib.pyplot as plt

import torch
import torch.nn as nn
from torch.autograd import Variable
import torch.nn.functional as F

import torchvision
import torchvision.transforms as transforms
from torch.utils.data import Dataset, DataLoader

파이토치는 기본적으로 GPU 사용을 권장합니다.

하지만 GPU가 장착되지 않은 환경에서도 파이토치를 정상적으로 실행하고 사용할 수 있습니다.

GPU가 장착되어 있고, GPU를 사용하기 위한 설정이 되어 있다면 파이토치에서 자동으로 인식합니다.

하지만 책에서는 아직 GPU를 설정하지 않았기 때문에 CPU를 사용할 것입니다.

2. CPU 혹은 GPU 장치 확인

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

※참고※

<하나의 GPU를 사용할 때>

device = torch.device("cuda : 0" if torch.cuda.is.available() else "cpu")
model = Net()
model.to(device)

<다수의 GPU를 사용할 때>

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = Net()
if torch.cuda.device_count() > 1:
    model = nn.DataParallel(net)
model.to(device)

3. fashion_mnist 데이터셋 다운로드하기

import torch
import torchvision
import torchvision.transforms as transforms

transform = transforms.Compose(
    [transforms.ToTensor()]
    )
    
trainset = torchvision.datasets.FashionMNIST(root='./data', train=True, 
                                        download=True, transform=transform)
                                        
testset = torchvision.datasets.FashionMNIST(root='./data', train=False,
                                        download=True, transform=transform)

torchvision.datasetstorch.utils.data.Dataset의 하위 클래스로 다양한 데이터셋(CIFAR, COCO, MNIST, ImageNet 등)을 포함합니다.

 

< torchvision.datasets 파라미터 >

  • root: FashionMNIST 데이터셋이 저장될 경로입니다. 지정한 경로가 없으면 새로 생성됩니다.
  • train=True: 학습용 데이터를 다운로드합니다.
  • download=True: "데이터셋이 저장될 경로"에 데이터셋이 있는지 확인한 후 없는 경우 다운로드됩니다.
  • transform=trainsform: 입력된 이미지 데이터를 텐서(0~1) 형태로 변경합니다.

4. fashion_mnist 데이터를 데이터로더에 전달

다운로드한 fashion_mnist 데이터를 일정한 batch size로 묶어서  데이터로더(DataLoader)를 만들어주는 코드입니다.

train_loader = torch.utils.data.DataLoader(
    trainset, 
    batch_size=100, 
    shuffle=True
)

test_loader = torch.utils.data.DataLoader(
    testset, 
    batch_size=100, 
    shuffle=False
)

torch.utils.data.DataLoader()를 사용하여 원하는 크기의 배치 단위로 데이터를 불러오거나, 순서가 무작위로 섞이도록 할 수 있습니다.

 

< DataLoader 파라미터 >

  • trainset : 데이터를 불러올 데이터셋을 지정합니다.
  • batch_size : 데이터를 배치로 묶어줍니다. 100개 단위로 묶어줍니다.
  • shuffle : 데이터를 불러올 때마다 데이터를 섞어서 불러옵니다. 

5. 분류에 사용될 클래스 정의 

FashionMNIST 데이터셋으로부터 랜덤으로 24개의 샘플 이미지와 라벨을 가져와서 시각화하는 코드입니다.

DataLoader에서 shuffle=True로 설정되어 있기 때문에 매 epoch마다 데이터가 랜덤하게 섞입니다.

next(iter(train_loader))를 호출할 때마다 다른 미니배치가 반환됩니다.

import matplotlib.pyplot as plt
import numpy as np

# train_loader 에서 첫 번째 배치를 가져와 이미지와 라벨을 sample, labels에 저장
samples, labels = next(iter(train_loader))

# 각 클래스의 이름을 숫자와 매칭하여 저장 (총 10개)
classes = {0:'T-Shirt', 1:'Trouser', 2:'Pullover', 3:'Dress', 4:'Coar',
5:'Sandal', 6:'Shirt', 7:'Sneaker', 8:'Bag', 9:'Ankle Boot'}

fig, axs = plt.subplots(nrows=4, ncols=6, figsize=(16, 10)) # 4행 6열의 서브플롯을 생성

for ax, image, label in zip(axs.flatten(), samples, labels):
    image = np.transpose(image.numpy(), (1, 2, 0)) # 차원 변환: (C, H, W) -> (H, W, C)
    
    ax.imshow(image, cmap='gray') # 흑백 이미지로 출력
    ax.set_title(classes[label.item()], fontsize=12) # label.item(): torch.Tensor 타입의 스칼라 값(정수)을 얻기 위해 사용
    ax.set_xticks([])
    ax.set_yticks([])
    
plt.tight_layout()
plt.show()

24개의 이미지 데이터를 시각적으로 표현

6. 심층 신경망 모델(DNN) 생성

class FashionDNN(nn.Module):
    def __init__(self): ## 1
        super(FashionDNN, self).__init__() 
        self.fc1 = nn.Linear(in_features=784, out_features=256) ## 2
        self.drop = nn.Dropout(0.25) ## 3
        self.fc2 = nn.Linear(in_features=256, out_features=128) 
        self.fc3 = nn.Linear(in_features=128, out_features=10) # 10은 클래스 개수를 의미
        
    def forward(self, input_data): ## 4
        out = input_data.view(-1, 784) ## 5
        out = F.relu(self.fc1(out)) ## 6
        out = self.drop(out)
        out = F.relu(self.fc2(out))
        out = self.fc3(out)
        return out

1.  클래스(class) 형태의 모델은 항상 torch.nn.Module을 상속받습니다.

__init__.()은 객체가 갖는 속성 값을 초기화하는 역할을 하며, 객체가 생성될 때 자동으로 호출됩니다.

super(FashionDNN, self).__init__()는 FashionDNN이라는 부모(super) 클래스를 상속받겠다는 의미로 이해하면 됩니다.

 

2.  nn 딥러닝 모델구성에 필요한 모듈이 모여 있는 패키지이며, Linear은 단순 선형 회귀 모델을 만들 때 사용합니다.

 

< nn.Linear(in_features=784, out_features=256) 파라미터>

  •  in_features: 입력의 크기(input size)
  • out_features: 출력의 크기(output size)

데이터 연산이 진행되는 forward() 부분에는 첫 번째 파라미터 값(in_features)만 넘겨주게 되며,

두 번째 파라미터(out_features)에서 정의된 크기가 forward() 연산의 결과가 됩니다. 

 

3.  torch.nn.Dropout(p) 입력을 확률 p로 무작위로 0으로 만드는 레이어입니다. 이것은 일종의 regularization 기법으로, 모델이 training 데이터에 overfitting 되는 것을 방지할 수 있습니다.

 nn.Dropout(0.25)는 입력의 25%를 0으로 만든다는 의미입니다.

 

4.  forward()  함수는 모델에 학습 데이터를 입력받아서 순전파 연산을 진행하는 함수이며, 객체를 데이터와 함께 호출하면 자동으로 실행됩니다. 이때 순전파 연산이란 H(x)^2 식에 입력 x로부터 예측된 y를 얻는 것입니다.

 

5. .view(-1,784) 는 2차원 텐서로 변경하되 (?, 784)의 크기로 변경하라는 의미입니다. 첫 번째 차원 -1은 사용자가 잘 모르겠으니 파이토치에 맡기겠으며,  두 번째 차원의 길이는 784를 가지도록 하라는 의미입니다.(.reshape 함수와 같은 역할입니다.)

 

7. 심층 신경망에서 필요한 파라미터 정의

learning_rate = 0.001
model = FashionDNN()
model.to(device)

criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)

모델을 학습시키지 전에 손실함수, 학습률, 옵티마이저에 대해 정의합니다.

옵티마이저를 위한 경사 하강법은 Adam을 사용하며, 학습률을 의미하는 lr은 0.001을 사용한다는 의미입니다.

 

8. 심층 신경망을 이용한 모델 학습

num_epochs = 5
count = 0

loss_list = []
iteration_list = []
accuracy_list = []

predictions_list = []
labels_list = []

for epoch in range(num_epochs): # 100개의 이미지와 레이블이 하나의 배치로 묶여서 가져옴
    for images, labels in train_loader:
        images, labels = images.to(device), labels.to(device)
        
        train = Variable(images.view(100, 1, 28, 28)) ## 1
        labels = Variable(labels)
        
        outputs = model(train) # 학습 데이터(batch)를 모델에 입력하여 출력값 계산
        loss = criterion(outputs, labels) # 손실값 계산
        optimizer.zero_grad()  # 이전에 계산된 그래디언트 값 초기화
        loss.backward()        # 손실에 대한 그래디언트 계산
        optimizer.step()       # 계산된 그래디언트를 이용하여 가중치 업데이트
        count += 1
        
        if (count % 50)==0: #  매 50번째 iteration마다 test data를 이용해 accuracy를 계산
            total = 0
            correct = 0

            for images, labels in test_loader:
                images, labels = images.to(device), labels.to(device)

                labels_list.append(labels)
                test = Variable(images.view(-1, 1, 28, 28))
                
                outputs = model(test)
                predictions = torch.argmax(outputs, 1).to(device) # 최댓값의 위치
                predictions_list.append(predictions)
                
                correct += (predictions == labels).sum() # 정답을 맞춘 데이터의 수
                total += len(labels) # 총 데이터의 수
        
            accuracy = correct / total * 100 # 정확도
            loss_list.append(loss.data)
            iteration_list.append(count)
            accuracy_list.append(accuracy)
                
        if (count % 500)==0:
            print("Iteration: {}, Loss: {}, Accuracy: {}%".format(count, loss.data, 
                accuracy))

1. Autograd는 자동 미분을 수행하는 파이토치의 패키지입니다. Autograd는 Variable을 사용해서 역전파를 위한 미분 값을 자동으로 계산해 줍니다. Variable은 PyTorch에서 기존의 tensor를 감싸서 미분 가능한 tensor로 만드는 역할을 합니다.

이것은 주어진 입력에 대한 기울기(gradient)를 자동으로 계산하기 위한 것입니다.  

 

images.view(100, 1, 28, 28)는 images tensor를 (100, 1, 28, 28) 크기의 tensor로 변환합니다.

이것은 이미지 데이터를 100개씩 묶어서 1채널, 28x28 픽셀의 형태로 변환하는 것입니다.

따라서, 이 변환된 이미지 데이터를 모델에 입력하기 위해 Variable로 감싸서 미분 가능한 tensor로 만듭니다.

labels는 이미지 데이터에 해당하는 라벨 값입니다. 이것 역시 Variable로 감싸서 미분 가능한 tensor로 만듭니다.


8번에 있는 코드를 마지막으로 실행하면 최종적으로 정확도가 86%로 높은 수치가 출력됩니다.

심층 신경망은 여기까지!

합성곱 신경망을 구현하는 방법이 궁금하다면!

https://resultofeffort.tistory.com/97

 

[pytorch] Convolutional Neural Network (CNN) 로 MNIST 구현해보기

fashion_mnist 데이터셋을 사용하여 합성곱 신경망을 직접 구현해 보겠습니다. fashion_mnist 데이터셋은 토치비전에 내장된 예제 데이터로 운동화, 셔츠, 샌들 같은 작은 이미지의 모음이며, 기본 MNIST

resultofeffort.tistory.com

 

728x90
반응형