카테고리 없음

손글씨 데이터셋

dldbwls0818 2025. 1. 31. 06:48

1. 손글씨 데이터셋

- sklearn에서 제공하는 데이터 셋으로 0~9까지 8x8픽셀 이미지 데이터들로 이루어진 데이터셋입니다.

(이번엔 모델을 직접 만들어 사용해보도록 하겠습니다)

 

import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split

digits = load_digits()
X_data = digits['data']
y_data = digits['target']
print(X_data, y_data)
print(X_data.shape, y_data.shape)

 

※ axes.flatten()

- axes.flatten()은 다차원 배열 형태로 구성된 Matplotlib의 서브플롯 배열을 1차원 배열로 변환하는 메서드입니다.

- Matplotlib에서 다수의 서브플롯을 생성할 때, plt.subplots()는 2차원 배열 형태로 서브플롯 객체를 반환합니다.

- 이 배열은 각 서브 플롯을 접근하기 위해 행과 열의 인덱스를 사용해야하지만, flatten() 메서드를 사용하면 이 배열을 1차원으로 펼쳐서 각 서브플롯을 단일 인덱스로 순회할 수 있게 됩니다.

fig, axes = plt.subplot(nrows=2, ncols=5, figsize=(14, 8))

for i, ax in enumerate(axes.flatten()) :
    ax.imshow(X_data[i].reshape(8,8), cmap='gray')
    ax.set_title(y_data[i])
    ax.axis('off')
    
X_data = torch.FloatTensor(X_data)
y_data = torch.LongTensor(y_data)

X_train, X_test, y_train, y_test = train_test_split(X_data, y_data, test_size=0.3, random_state=2025)

 

2. DataLoader

- DataLoader는 딥러닝 프레임워크(ex : PyTorch)에서 데이터를 효과적으로 관리하고 모델에 공급하기 위해 사용되는 도구입니다.

- 데이터셋에서 batch 단위로 데이터를 로드하고, 학습 중에 필요한 데이터 샘플링, 셔플링, 병렬 처리 등을 간편하게 수행할 수 있습니다.

- DataLoader는 데이터셋 객체와 함께 작동하며, 데이터셋의 개별 항목에 접근하는 방식을 정의하는 데이터셋 Class와 달리, 데이터를 미니배치로 묶어서 반복(iteration) 가능한 형태로 제공합니다.

from torch.utils.data import DataLoader

loader = DataLoader(
	# 하나의 데이터와 label이 tuple로 묶여 리스트로 들어간다. ex) ([1, 0, 25, 255, 40], 8)
	dataset = list(zip(X_train, y_train)),
    batch_size = 64,
    shuffle = True,
    # digits 데이터가 64개의 미니배치로 나누면 5개의 데이터가 남는다
    # 5개의 데이터를 버린다면 True, 아니면 False
    drop_last = False
)

imgs, labels = next(iter(loader))

fig, axes = plt.subplots(nrows=8, ncols=8, figsize=(14, 14))

for ax, img, label in zip(axes.flatten(), imgs, labels) :
    ax.imshow(img.reshape(8,8), cmap='gray')
    ax.set_title(str(label))
    ax.axis('off')
model = nn.Sequential(
	nn.Linear(64, 10)
)

optimizer = optim.Adam(model.parameters(), lr=0.01)
epochs = 50

for epoch in range(epochs + 1) :
	# 배치 단위로 loss와 accuracy를 확인하기 위한 변수
    sum_losses = 0
    sum_accs = 0
    for x_batch, y_batch in loader :
        y_pred = model(x_batch)
        loss = nn.CrossEntropyLoss()(y_pred, y_batch)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        sum_losses += loss
        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 += acc

    avg_loss = sum_losses / len(loader)
    avg_acc = sum_accs / len(loader)

    print(f'Epoch {epoch:4d}/{epochs}, Loss: {avg_loss:.6f}, Accuracy: {avg_acc:.2f}%')
    
plt.imshow(X_test[10].reshape(8,8), cmap='gray')
print(y_test[10])

y_pred = model(X_test)
y_prob = nn.Softmax(1)(y_pred)

for i in range(10) :
    print(f'숫자 {i}일 확률: {y_prob[10][i]:.2f}')
    
y_pred_index = torch.argmax(y_prob, axis=1)
accuracy = (y_test == y_pred_inde).float().sum() / len(y_test) * 100
print(f'테스트 정확도는 {accuracy:.2f}% 입니다')

3. 데이터 증강

- 데이터 증강(Data Augmentation)은 학습 데이터를 인위적으로 변환하여 데이터셋의 다양성을 높이고 모델의 일반화 성능을 향상 시키는 기법입니다.

- 회전, 크기 조정, 반전, 블러링, 밝기 조정 등 다양한 변환을 적용하여 원본 데이터로부터 새로운 데이터를 생성합니다.

- 이를 통해 데이터 부족 문제를 완화하고 모델이 특정 패턴에 Overfitting 되지 않도록 도와줍니다.

- 특히, 이미지나 음성 데이터와 같이 특징이 직관적인 데이터에서 효과적으로 활용되며, 증강된 데이터는 모델이 예측 대상의 다양한 변형에 대해 강하게 학습할 수 있도록 돕습니다.

# 데이터 증강을 위한 모듈
from torchvision import transforms
from torch.utils.data import Dataset
# 데이터셋을 Tensor형태로 변환
from torch.utils.data import TensorDataset

X_train, X_test, y_train, y_test = train_test_split(X_data, y_data, test_size=0.3, random_state=2025)
print(X_train.shape, X_test.shape)
print(y_train.shape, y_test.shape)

train_dataset = TensorDataset(X_train, y_train)
test_dataset = TensorDataset(X_test, y_test)

 

※ transforms.Compose()

- 여러 데이터 변환(transform) 작업을 순차적으로 적용할 수 있도록 해줍니다.

- 이미지 데이터 전처리와 증강 과정에서 자주 사용되며, 각 변환을 하나의 리스트로 묶어 실행합니다.

  1.  transforms.RandomRotation(10)
    • 기능 : 이미지를 -10도에서 +10도 사이로 무작위 회전시킵니다.
      • 10은 회전 범위를 나타냅니다.
      • 각 호출 시, -10도 ~ +10도 범위에서 무작위로 각도를 선택하여 이미지를 회전합니다
  2. transforms.RandomAffine(0, shears=5, scale=(0.9, 1.1))
    • 기능 : 이미지를 비틀기(shear), 크기 조정(scale) 등의 변환을 수행합니다.
      • 0 : 회전(각도) 변환을 수행하지 않음을 의미합니다.
      • shear = 5 : 이미지를 최대 5도 만큼 비스듬하게 비틀기 변환을 수행합니다.
        • ex) 정사각형이 평행사변형처럼 기울어질 수 있습니다.
      • scale = (0.9, 1.1)
        • 이미지를 0.9배에서 1.1배 범위 내에서 무작위 크기 조정을 수행합니다.
        • 각 호출 시, 무작위로 크기가 변경됩니다. 
# 데이터 증강 적용
transform = transforms.Compose([
	transforms.RandomRotation(10),
    transforms.RandomAffine(0, shear=5, scale=(0.9, 1.1))
])

# 데이터 증강을 적용시킨 데이터셋을 객체로 만들기 위해 class 정의
class AugmentedDataset(Dataset) :
    def __init__(self, dataset, transform) :
        self.dataset = dataset
        self.transform = transform

    def __len__(self) :
        return len(self.dataset)

    def __getitem__(self, idx) :
        x, y = self.dataset[idx]
        # 이미지로 만들기
        x = x.view(8, 8).unsqueeze(0) # x: (64,) -> (8, 8) -> (1, 8, 8)
        x = self.transform(x)
        return x.flatten(), y
        
augmented_train_dataset = AugmentDataset(train_dataset, transform)
train_loader = DataLoader(
	augmented_train_dataset,
    batch_size = 64,
    shuffle = True
)

test_loader = DataLoader(
	test_dataset,
    batch_size = 64,
    shuffle = False
)

imgs, labels = next(iter(train_loader))
fig, axes = plt.subplots(nrows=8, ncols=8, figsize=(14, 14))

for ax, img, label in zip(axes.flatten(), imgs, labels):
    ax.imshow(img.reshape((8, 8)), cmap='gray')
    ax.set_title(str(label))
    ax.axis('off')
    
model = nn.Sequential(
	nn.Linear(64, 10)
)

optimizer = optim.Adam(model.parameters(), lr=0.01)
epochs = 50

for epoch in range(epochs + 1):
    sum_losses = 0
    sum_accs = 0

    # data_loader에서 꺼내면서 데이터 증강이 이루어진다
    for x_batch, y_batch in train_loader:
        y_pred = model(x_batch)
        loss = nn.CrossEntropyLoss()(y_pred, y_batch)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        sum_losses = sum_losses + loss

        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

    if epoch % 10 == 0:
        avg_loss = sum_losses / len(loader)
        avg_acc = sum_accs / len(loader)
        print(f'Epoch {epoch:4d}/{epochs} Loss: {avg_loss:.6f} Accuracy: {avg_acc:.2f}%')
        
plt.imshow(X_test[11].reshape((8, 8)), cmap='gray')
print(y_test[11])

y_pred = model(X_test)
y_prob = nn.Softmax(1)(y_pred)

for i in range(10) :
    print(f'숫자 {i}일 확률 : {y_prob[11][i]:.2f}')