4. 손글씨 도형 분류하기

2025. 1. 16. 12:25LLM(Large Language Model)의 기초/딥러닝

1. 손글씨 도형

 

 

예시 1)

# %cd 명령어는 현재 작업 중인 디렉토리를 변경할 때 사용됩니다.
%cd /content/drive/MyDrive/KDT 시즌 4/11. 딥러닝/data

 

예시 2)

# zip 파일을 압축 해제하는 명령어
# !unzip은 zip 파일을 풀 때 사용하는 명령어입니다.
# -qq 옵션은 "quick"의 약자로, 압축 해제 중 상태 메시지를 출력하지 않도록 설정합니다.
# 압축이 풀리면 현재 디렉토리 또는 zip 파일이 포함된 디렉토리에 파일이 생성됩니다.
!unzip -qq "/content/drive/MyDrive/KDT 시즌 4/11. 딥러닝/data/shape.zip"

 

예시 3)

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import Dataset, DataLoader
import matplotlib.pyplot as plt

 

예시 4)

# 이미지 변환 파이프라인 정의
# torchvision.transforms 모듈을 사용하여 이미지 데이터를 전처리합니다.
transform = transforms.Compose([
    # 1. Resize: 이미지를 28x28 크기로 리사이즈합니다.
    #    모든 이미지를 동일한 크기로 맞추어 모델 입력 형태를 통일합니다.
    transforms.Resize((28, 28)),

    # 2. Grayscale: 이미지를 1채널(흑백)로 변환합니다.
    #    원래 RGB(3채널) 이미지라도 단일 채널로 바꾸어 처리 용량을 줄입니다.
    transforms.Grayscale(1),

    # 3. ToTensor: 이미지를 Tensor로 변환합니다.
    #    이미지를 PyTorch 텐서 형태로 바꿔 모델이 처리할 수 있도록 합니다.
    #    동시에 픽셀 값이 0~255에서 0~1 사이로 정규화됩니다.
    transforms.ToTensor(),

    # 4. RandomInvert: 이미지 색상을 반전시킵니다.
    #    검은 바탕에 흰 글씨로 변환되며, 확률은 1로 설정하여 항상 반전됩니다.
    transforms.RandomInvert(1),

    # 5. Normalize: 텐서 데이터를 정규화합니다.
    #    평균 0.5, 표준편차 0.5로 정규화하여 픽셀 값을 -1~1 범위로 조정합니다.
    transforms.Normalize((0.5), (0.5))
])

 

예시 5)

# 트레이닝 폴더 경로와 테스트 폴더 경로를 설정해준다
train_path='/content/drive/MyDrive/KDT 시즌 4/11. 딥러닝/data/shape/train'
test_path='/content/drive/MyDrive/KDT 시즌 4/11. 딥러닝/data/shape/test'

 

예시 6)

# 학습 데이터셋과 테스트 데이터셋 생성
# torchvision.datasets.ImageFolder를 사용하여 이미지 데이터셋을 불러옵니다.
# 데이터는 디렉토리 구조에 따라 라벨이 자동으로 할당됩니다.

trainset = torchvision.datasets.ImageFolder(
    root=train_path,   # 학습 데이터가 저장된 디렉토리 경로를 지정
    transform=transform  # 앞에서 정의한 전처리(transform)를 적용
)

testset = torchvision.datasets.ImageFolder(
    root=test_path,    # 테스트 데이터가 저장된 디렉토리 경로를 지정
    transform=transform  # 동일한 전처리(transform)를 적용
)

# 데이터셋의 크기를 확인
# len(trainset): 학습 데이터셋에 포함된 이미지의 개수를 반환
# len(testset): 테스트 데이터셋에 포함된 이미지의 개수를 반환
len(trainset), len(testset)
--->
(240, 60)

 

예시 7)

trainset.__getitem__(10)
--->
(tensor([[[-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -0.9922, -0.9059, -0.6627, -0.7176, -0.9608,
           -0.9922, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -0.9922, -0.7725, -0.3647,  0.0118,  0.3725,  0.4039, -0.1137,
           -0.3961, -0.8745, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -0.9843, -0.7098,
           -0.0667,  0.2078, -0.0667, -0.4667, -0.6941, -0.4353, -0.2863,
            0.1137, -0.1451, -0.9373, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -0.9294, -0.0667,  0.4667,
           -0.2000, -0.7647, -0.9294, -1.0000, -1.0000, -1.0000, -0.9922,
           -0.7725,  0.2863, -0.6000, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -0.8745, -0.1373,  0.5922,  0.0353, -0.8196,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -0.0980, -0.2314, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -0.8118,  0.0039, -0.0039, -0.7725, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -0.1216, -0.2471, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -0.9922,  0.1294, -0.1137, -0.8588, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -0.0118, -0.3882, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -0.6000,  0.4510, -0.9373, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -0.9843,  0.1922, -0.6235, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -0.0431, -0.1216, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -0.7804,  0.1608, -0.8118, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -0.0824, -0.0510, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -0.4980,  0.0039, -0.8980, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -0.6471,  0.4275, -0.6078, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -0.9451,
           -0.1137, -0.2784, -0.9843, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -0.9843, -0.4824,  0.3804, -0.4902, -0.9843, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -0.6941,
            0.2784, -0.7490, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -0.9922, -0.5843,  0.4039,  0.2784, -0.2941, -0.7176,
           -0.8196, -0.8510, -0.8588, -0.8588, -0.8431, -0.6784,  0.3569,
           -0.3412, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -0.9137, -0.5294, -0.0588,  0.2157,
            0.1294,  0.0588,  0.0275,  0.0431,  0.0824,  0.2392, -0.3333,
           -0.9843, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -0.9922, -0.9216,
           -0.7804, -0.6706, -0.6706, -0.6706, -0.6863, -0.8824, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000,
           -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000]]]),
 0)

 

예시 8)

# 데이터셋의 클래스 확인
# trainset.classes와 testset.classes는 데이터셋에 포함된 클래스(라벨)의 이름을 리스트 형태로 반환합니다.

# 학습 데이터셋(trainset)에서 클래스 정보 확인
trainset.classes  # ImageFolder로 불러온 학습 데이터셋의 클래스(디렉토리 이름) 리스트

# 테스트 데이터셋(testset)에서 클래스 정보 확인
testset.classes  # ImageFolder로 불러온 테스트 데이터셋의 클래스(디렉토리 이름) 리스트
--->
(['cir', 'tri', 'x'], ['cir', 'tri', 'x'])

 

예시 9)

# class_map에 0은 circle, 1은 triangle, 3은 x를 정의
class_map = {0: 'cir', 1: 'tri', 2: 'x'}

 

예시 10)

# DataLoader를 사용하여 학습 데이터셋을 배치 단위로 로드
loader = DataLoader(
    dataset=trainset,  # 로드할 데이터셋을 지정 (여기서는 학습 데이터셋 trainset)
    batch_size=64,     # 한 번에 로드할 데이터 개수를 64로 설정 (배치 크기)
    shuffle=True       # 데이터를 로드할 때 매 epoch마다 데이터를 섞어서 랜덤으로 배치 생성
)

# DataLoader에서 하나의 배치를 가져옴
# iter(loader)는 DataLoader 객체를 반복 가능한(iterable) 객체로 만듦
# next()를 사용하여 DataLoader의 첫 번째 배치를 가져옴
imgs, labels = next(iter(loader))

# imgs.shape: 배치 내 이미지 텐서의 크기를 출력
# labels.shape: 배치 내 라벨 텐서의 크기를 출력
print(imgs.shape, labels.shape)
--->
torch.Size([64, 1, 28, 28]) torch.Size([64])

 

예제 11)

# 8x8 형태의 서브플롯을 생성하여 이미지 배치를 시각화
fig, axes = plt.subplots(8, 8, figsize=(16, 16))  
# subplots: 여러 개의 서브플롯을 생성
# 8x8 그리드로 64개의 서브플롯 생성
# figsize=(16, 16): 전체 플롯의 크기를 설정

# 배치에서 이미지를 하나씩 꺼내 각 서브플롯에 표시
for ax, img, label in zip(axes.flatten(), imgs, labels):
    # axes.flatten(): 8x8 배열을 1차원으로 평탄화하여 서브플롯을 반복적으로 가져옴
    # imgs: 배치에서 이미지 텐서
    # labels: 배치에서 라벨 텐서

    # 이미지 텐서를 28x28 형태로 변환하여 시각화
    # .reshape(28, 28): 흑백 이미지를 2차원 형태로 변환
    # cmap='gray': 이미지를 그레이스케일로 표시
    ax.imshow(img.reshape(28, 28), cmap='gray')

    # 이미지에 해당하는 클래스 이름을 제목으로 설정
    # class_map: 라벨 번호를 클래스 이름으로 매핑하는 딕셔너리
    # label.item(): 라벨 텐서를 정수 값으로 변환
    ax.set_title(class_map[label.item()])

    # 축 눈금과 프레임을 비활성화
    ax.axis('off')

--->

 

예제 12)

# 장치 확인
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print(device)

 

2. CNN모델 만들기

 

예시 1)

# CNN(Convolutional Neural Network) 모델 정의
class ConvNeuralNetwork(nn.Module):
    def __init__(self):
        # 부모 클래스(nn.Module)의 초기화 호출
        super(ConvNeuralNetwork, self).__init__()

        # 이미지 텐서를 1차원 텐서로 펼치는 레이어
        self.flatten = nn.Flatten()

        # 합성곱 기반 분류기 정의 (Sequential 블록)
        self.classifier = nn.Sequential(
            # 첫 번째 Convolution 레이어: 입력 채널 1개 → 출력 채널 28개
            nn.Conv2d(1, 28, kernel_size=3, padding='same'),  # (28x28 입력 → 28x28 출력)
            nn.ReLU(),  # 활성화 함수 ReLU

            # 두 번째 Convolution 레이어: 출력 채널 유지
            nn.Conv2d(28, 28, kernel_size=3, padding='same'),  # (28x28 → 28x28)
            nn.ReLU(),

            # 첫 번째 MaxPooling 레이어: 2x2 영역에서 최대값 추출
            nn.MaxPool2d(kernel_size=2),  # (28x28 → 14x14)
            nn.Dropout(0.25),  # 드롭아웃: 25%의 뉴런을 랜덤하게 비활성화하여 과적합 방지

            # 세 번째 Convolution 레이어: 입력 채널 28개 → 출력 채널 56개
            nn.Conv2d(28, 56, kernel_size=3, padding='same'),  # (14x14 → 14x14)
            nn.ReLU(),

            # 네 번째 Convolution 레이어: 출력 채널 유지
            nn.Conv2d(56, 56, kernel_size=3, padding='same'),  # (14x14 → 14x14)
            nn.ReLU(),

            # 두 번째 MaxPooling 레이어: 2x2 영역에서 최대값 추출
            nn.MaxPool2d(kernel_size=2),  # (14x14 → 7x7)
            nn.Dropout(0.25),  # 드롭아웃: 25% 비활성화
        )

        # 완전연결층: 마지막 출력 텐서를 Flatten하여 3개의 클래스 예측
        self.Linear = nn.Linear(56 * 7 * 7, 3)  # (56x7x7 → 3)

    def forward(self, x):
        # 순전파 정의
        x = self.classifier(x)  # Convolution, ReLU, MaxPooling, Dropout 처리
        x = self.flatten(x)  # 2D 이미지 텐서를 1차원 벡터로 펼침
        output = self.Linear(x)  # 완전연결층을 통과하여 최종 출력 (3 클래스)
        return output

 

 

예시 2)

# CNN 모델 인스턴스 생성 및 장치로 이동
model = ConvNeuralNetwork().to(device)
# ConvNeuralNetwork(): 정의된 CNN 모델 클래스의 인스턴스를 생성
# .to(device): 모델을 지정된 장치(GPU 또는 CPU)로 이동
# device: GPU('cuda') 또는 CPU('cpu')를 지정한 변수 (이전에 정의된 변수)

# 생성된 모델 구조를 출력
print(model)
# 모델의 세부 구조를 출력하여 각 레이어와 파라미터 개수를 확인
--->
ConvNeuralNetwork(
  (flatten): Flatten(start_dim=1, end_dim=-1)
  (classifier): Sequential(
    (0): Conv2d(1, 28, kernel_size=(3, 3), stride=(1, 1), padding=same)
    (1): ReLU()
    (2): Conv2d(28, 28, kernel_size=(3, 3), stride=(1, 1), padding=same)
    (3): ReLU()
    (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (5): Dropout(p=0.25, inplace=False)
    (6): Conv2d(28, 56, kernel_size=(3, 3), stride=(1, 1), padding=same)
    (7): ReLU()
    (8): Conv2d(56, 56, kernel_size=(3, 3), stride=(1, 1), padding=same)
    (9): ReLU()
    (10): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (11): Dropout(p=0.25, inplace=False)
  )
  (Linear): Linear(in_features=2744, out_features=3, bias=True)
)

 

예시 3)

# 손실 함수 정의
loss = nn.CrossEntropyLoss()
# nn.CrossEntropyLoss(): 분류 문제를 위한 손실 함수
# - 소프트맥스와 로그-우도 손실(Negative Log Likelihood)을 결합한 함수
# - 다중 클래스 분류에서 사용
# - 모델 출력(로짓)과 실제 정답(라벨)을 비교하여 손실 계산

# 옵티마이저(최적화 알고리즘) 정의
optimizer = optim.Adam(model.parameters(), lr=0.001)
# optim.Adam: Adam 옵티마이저를 사용하여 모델 파라미터를 학습
# - model.parameters(): 모델의 학습 가능한 파라미터를 전달
# - lr=0.001: 학습률(Learning Rate)을 0.001로 설정

 

예시 4)

# 학습 루프 정의
def train_loop(train_loader, model, loss_fn, optimizer):
    # 손실 합계와 정확도 합계 초기화
    sum_losses = 0  # 배치별 손실 합계
    sum_accs = 0    # 배치별 정확도 합계

    # DataLoader에서 배치 단위로 데이터를 가져옴
    for x_batch, y_batch in train_loader:
        # 입력 데이터와 라벨을 장치(GPU/CPU)로 이동
        x_batch = x_batch.to(device)
        y_batch = y_batch.to(device)

        # 모델을 통해 예측 수행
        y_pred = model(x_batch)  # x_batch 입력 → 모델 출력 y_pred

        # 손실 계산
        loss = loss_fn(y_pred, y_batch)  # 모델 출력과 실제 라벨 간의 손실 계산

        # 옵티마이저 초기화 (이전 그래디언트 값 초기화)
        optimizer.zero_grad()

        # 역전파로 그래디언트 계산
        loss.backward()

        # 옵티마이저로 모델 파라미터 업데이트
        optimizer.step()

        # 현재 배치의 손실 값을 합산
        sum_losses = sum_losses + loss

        # 정확도 계산
        # 모델 출력 y_pred를 확률로 변환 (Softmax)
        y_prob = nn.Softmax(1)(y_pred)

        # 가장 높은 확률을 가진 클래스의 인덱스 예측
        y_pred_index = torch.argmax(y_prob, axis=1)

        # 예측값과 실제 라벨 비교하여 정확도 계산
        acc = (y_batch == y_pred_index).float().sum() / len(y_batch) * 100  # 백분율로 변환
        sum_accs = sum_accs + acc  # 배치 정확도를 합산

    # 평균 손실과 평균 정확도 계산
    avg_loss = sum_losses / len(train_loader)  # 전체 배치의 평균 손실
    avg_acc = sum_accs / len(train_loader)    # 전체 배치의 평균 정확도

    return avg_loss, avg_acc  # 평균 손실과 정확도를 반환

 

예제 5)

# 학습 반복 횟수 설정
epochs = 50  # 총 학습 반복 횟수 (에폭 수)

# 에폭 루프 시작
for i in range(epochs):
    # 에폭 구분선 출력
    print(f"------------------------------------------------")

    # 학습 루프 실행
    avg_loss, avg_acc = train_loop(loader, model, loss, optimizer)
    # train_loop: 학습 데이터의 손실과 정확도를 계산하는 함수
    # loader: DataLoader 객체로 학습 데이터를 배치 단위로 제공
    # model: 학습할 모델
    # loss: 손실 함수 (예: nn.CrossEntropyLoss)
    # optimizer: 모델 파라미터를 업데이트하는 옵티마이저

    # 현재 에폭의 평균 손실과 정확도 출력
    print(f'Epoch {i:4d}/{epochs} Loss: {avg_loss:.6f} Accuracy: {avg_acc:.2f}%')
    # i: 현재 에폭 번호
    # epochs: 전체 에폭 수
    # avg_loss: 현재 에폭의 평균 손실 값 (소수점 6자리까지 출력)
    # avg_acc: 현재 에폭의 평균 정확도 (소수점 2자리까지 출력)

# 학습 완료 메시지 출력
print("Done!")  # 모든 에폭의 학습이 완료되었음을 알림

----->
------------------------------------------------
Epoch    0/50 Loss: 1.097150 Accuracy: 32.68%
------------------------------------------------
Epoch    1/50 Loss: 1.024138 Accuracy: 53.91%
------------------------------------------------
Epoch    2/50 Loss: 0.829824 Accuracy: 52.99%
------------------------------------------------
Epoch    3/50 Loss: 0.568259 Accuracy: 68.49%
------------------------------------------------
Epoch    4/50 Loss: 0.463168 Accuracy: 72.53%
------------------------------------------------
Epoch    5/50 Loss: 0.422348 Accuracy: 76.43%
------------------------------------------------
....
------------------------------------------------
Epoch   40/50 Loss: 0.026030 Accuracy: 98.83%
------------------------------------------------
Epoch   41/50 Loss: 0.037221 Accuracy: 98.70%
------------------------------------------------
Epoch   42/50 Loss: 0.016392 Accuracy: 99.61%
------------------------------------------------
Epoch   43/50 Loss: 0.058856 Accuracy: 99.61%
------------------------------------------------
Epoch   44/50 Loss: 0.033171 Accuracy: 98.31%
------------------------------------------------
Epoch   45/50 Loss: 0.013470 Accuracy: 100.00%
------------------------------------------------
Epoch   46/50 Loss: 0.031506 Accuracy: 98.18%
------------------------------------------------
Epoch   47/50 Loss: 0.037006 Accuracy: 97.92%
------------------------------------------------
Epoch   48/50 Loss: 0.042798 Accuracy: 98.83%
------------------------------------------------
Epoch   49/50 Loss: 0.030260 Accuracy: 98.44%
Done!

 

예제 6)

# 테스트 데이터 로더 생성
test_loader = DataLoader(
    dataset=testset,    # 테스트 데이터셋(testset)을 로드
    batch_size=32,      # 배치 크기를 32로 설정 (한 번에 32개의 데이터를 처리)
    shuffle=False       # 데이터를 섞지 않고 순서대로 로드 (테스트 시에는 일반적으로 순서를 유지)
)

 

예제 7)

# 테스트 데이터 로더에서 첫 번째 배치 가져오기
imgs, labels = next(iter(test_loader))
# next(iter(test_loader)): DataLoader 객체(test_loader)에서 첫 번째 배치를 가져옴
# imgs: 배치에 포함된 이미지 데이터 (텐서 형태)
# labels: 배치에 포함된 라벨 데이터 (텐서 형태)

# 4x8 서브플롯 생성
fig, axes = plt.subplots(4, 8, figsize=(16, 8))
# plt.subplots: 여러 서브플롯 생성
# 4x8 그리드(32개의 서브플롯)를 생성
# figsize=(16, 8): 전체 플롯의 크기를 설정

# 각 서브플롯에 이미지와 클래스 이름을 표시
for ax, img, label in zip(axes.flatten(), imgs, labels):
    # axes.flatten(): 4x8 배열을 1차원 리스트로 평탄화하여 서브플롯을 반복
    # img: 현재 이미지 데이터
    # label: 현재 라벨 데이터

    # 이미지 텐서를 28x28로 변환하여 시각화
    # .reshape(28, 28): 2D 형태로 변환
    # cmap='gray': 이미지를 그레이스케일로 표시
    ax.imshow(img.reshape(28, 28), cmap='gray')

    # 이미지에 해당하는 클래스 이름을 제목으로 설정
    # class_map: 라벨 번호를 클래스 이름으로 매핑하는 딕셔너리
    # label.item(): 라벨 텐서를 정수 값으로 변환
    ax.set_title(class_map[label.item()])

    # 축 눈금과 프레임을 비활성화
    ax.axis('off')

---->

 

예제 8)

# 테스트 함수 정의
def test(model, loader):
    # 모델을 평가 모드로 설정 (Dropout 등 비활성화)
    model.eval()

    # 정확도 합계 초기화
    sum_accs = 0  # 배치별 정확도 합계

    # 예측값, 실제값, 이미지 데이터를 저장할 텐서 초기화
    # 텐서는 장치(GPU/CPU)로 이동
    img_list = torch.Tensor().to(device)  # 이미지를 저장할 텐서
    y_pred_list = torch.Tensor().to(device)  # 예측 라벨을 저장할 텐서
    y_true_list = torch.Tensor().to(device)  # 실제 라벨을 저장할 텐서

    # DataLoader에서 배치 단위로 데이터 가져오기
    for x_batch, y_batch in loader:
        # 데이터를 장치(GPU/CPU)로 이동
        x_batch = x_batch.to(device)
        y_batch = y_batch.to(device)

        # 모델을 통해 예측 수행
        y_pred = model(x_batch)  # 모델 출력 (로짓)
        
        # 소프트맥스 활성화 함수로 확률 변환
        y_prob = nn.Softmax(1)(y_pred)  # 각 클래스의 확률
        
        # 확률이 가장 높은 클래스의 인덱스(예측 라벨) 추출
        y_pred_index = torch.argmax(y_prob, axis=1)

        # 예측 라벨, 실제 라벨, 이미지 데이터를 텐서에 누적
        y_pred_list = torch.cat((y_pred_list, y_pred_index), dim=0)  # 예측값 누적
        y_true_list = torch.cat((y_true_list, y_batch), dim=0)       # 실제값 누적
        img_list = torch.cat((img_list, x_batch), dim=0)            # 이미지 누적

        # 정확도 계산: 예측값과 실제값 비교
        acc = (y_batch == y_pred_index).float().sum() / len(y_batch) * 100
        sum_accs += acc  # 배치 정확도를 합산

    # 평균 정확도 계산
    avg_acc = sum_accs / len(loader)  # 전체 배치의 평균 정확도

    # 예측값, 실제값, 이미지 데이터, 평균 정확도를 반환
    return y_pred_list, y_true_list, img_list, avg_acc

 

예제 9)

y_pred_list, y_true_list, img_list, avg_acc = test(model, test_loader)
print(f'테스트 정확도는 {avg_acc:.2f}% 입니다.')
--->
테스트 정확도는 93.75% 입니다.

 

예제 10)

# 4x8 서브플롯 생성
fig, axes = plt.subplots(4, 8, figsize=(16, 8))
# plt.subplots: 여러 서브플롯 생성
# 4x8 그리드(32개의 서브플롯)를 생성
# figsize=(16, 8): 전체 플롯 크기를 설정

# GPU에서 저장된 텐서를 CPU로 이동
img_list_cpu = img_list.cpu()        # 테스트 이미지 데이터를 CPU로 이동
y_pred_list_cpu = y_pred_list.cpu()  # 예측 라벨을 CPU로 이동
y_true_list_cpu = y_true_list.cpu()  # 실제 라벨을 CPU로 이동

# 각 서브플롯에 이미지, 예측 라벨, 실제 라벨 표시
for ax, img, y_pred, y_true in zip(axes.flatten(), img_list_cpu, y_pred_list_cpu, y_true_list_cpu):
    # axes.flatten(): 4x8 배열을 1차원 리스트로 평탄화하여 서브플롯을 반복
    # img: 테스트 이미지
    # y_pred: 예측 라벨
    # y_true: 실제 라벨

    # 이미지 텐서를 28x28로 변환하여 시각화
    # cmap='gray': 이미지를 그레이스케일로 표시
    ax.imshow(img.reshape(28, 28), cmap='gray')

    # 서브플롯 제목에 예측 라벨과 실제 라벨 표시
    # class_map: 라벨 번호를 클래스 이름으로 매핑하는 딕셔너리
    # y_pred.item(): 예측 라벨의 정수값
    # y_true.item(): 실제 라벨의 정수값
    ax.set_title(f'pred: {class_map[y_pred.item()]}, true: {class_map[y_true.item()]}')

    # 축 눈금과 프레임을 비활성화
    ax.axis('off')

# 플롯 표시
plt.show()

--->

 

3. 모델 저장하고 불러오기

 

예제 1)

# 모델의 가중치와 매개변수만 저장
# 모델의 구조가 저장되지 않으므로 모델 클래스 정의가 없으면 복원할수 없음
torch.save(model.state_dict(), 'model_weights.pth')

 

예제 2)

# 새로운 CNN 모델 인스턴스 생성 및 장치로 이동
model2 = ConvNeuralNetwork().to(device)
# ConvNeuralNetwork(): 이전에 정의된 CNN 모델 클래스의 인스턴스를 생성
# .to(device): 모델을 지정된 장치(GPU 또는 CPU)로 이동
# device: GPU('cuda') 또는 CPU('cpu')를 지정한 변수 (이전에 정의됨)

# 생성된 모델2 구조 출력
print(model2)
# 모델 구조를 출력하여 각 레이어와 파라미터를 확인
---->
ConvNeuralNetwork(
  (flatten): Flatten(start_dim=1, end_dim=-1)
  (classifier): Sequential(
    (0): Conv2d(1, 28, kernel_size=(3, 3), stride=(1, 1), padding=same)
    (1): ReLU()
    (2): Conv2d(28, 28, kernel_size=(3, 3), stride=(1, 1), padding=same)
    (3): ReLU()
    (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (5): Dropout(p=0.25, inplace=False)
    (6): Conv2d(28, 56, kernel_size=(3, 3), stride=(1, 1), padding=same)
    (7): ReLU()
    (8): Conv2d(56, 56, kernel_size=(3, 3), stride=(1, 1), padding=same)
    (9): ReLU()
    (10): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (11): Dropout(p=0.25, inplace=False)
  )
  (Linear): Linear(in_features=2744, out_features=3, bias=True)
)

 

예제 3)

y_pred_list, y_true_list, img_list, avg_acc = test(model2, test_loader)
print(f'테스트 정확도는 {avg_acc:.2f}% 입니다.')
--->
테스트 정확도는 39.51% 입니다.

 

예제 4)

# 모델의 가중치(weights)와 매개변수(parameters)만 저장
# 모델의 구조가 저장되지 않으므로 모델 클래스 정의가 없으면 복원할 수 없음
model2.load_state_dict(torch.load('model_weights.pth'))
---->
<ipython-input-61-79c790690189>:3: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.
  model2.load_state_dict(torch.load('model_weights.pth'))
<All keys matched successfully>

 

 

예제 5)

# 모델 전체를 저장
# 모델 클래스와 가중치가 함께 저장되므로, 복원시 모델 구조를 별도로 정의할 필요가 있음
torch.save(model, 'model.pth')

 

예제 6)

# 저장된 모델 파일 불러오기
model3 = torch.load('model.pth')
# torch.load(): 저장된 PyTorch 객체(모델, 텐서 등)를 불러오는 함수
# 'model.pth': 저장된 모델 파일의 경로
# - 이 파일에는 모델의 구조와 학습된 파라미터(가중치와 편향)가 저장되어 있음
# 불러온 모델은 'model3' 변수에 저장됨
--->
<ipython-input-63-f3e520325b2b>:1: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.
  model3 = torch.load('model.pth')

 

예제 7)

y_pred_list, y_true_list, img_list, avg_acc = test(model3, test_loader)
print(f'테스트 정확도는 {avg_acc:.2f}% 입니다.')
--->
테스트 정확도는 93.75% 입니다.
728x90
LIST