본문으로 건너뛰기

MNIST 분류 튜토리얼

MNIST 손글씨 숫자 분류 모델을 처음부터 끝까지 학습하는 완전한 튜토리얼입니다. 런타임에 데이터셋을 다운로드하는 방식으로 별도의 데이터셋 업로드 없이 학습할 수 있습니다.

사전 요구사항

이 튜토리얼을 진행하기 전에:

  • keynet-train 0.8.5 이상 설치 (0.7.4 이상 호환)
  • Docker가 설치되어 있고 실행 중이어야 합니다
  • KeyNet platform 계정이 있어야 합니다

학습 Workflow

단계 구분
  • 파란색 박스: 로컬 환경에서 수행하는 작업
  • 주황색 박스: KeyNet Platform에서 수행하는 작업

1. Platform에서 프로젝트 생성

KeyNet platform에 로그인한 후 프로젝트를 생성합니다.

프로젝트 생성 버튼

프로젝트 생성 폼

모델은 자동 생성

이전 버전과 달리, 이제 모델을 수동으로 생성할 필요가 없습니다. keynet-train push 명령어가 자동으로 모델을 생성합니다.

2. 학습 코드 작성

프로젝트 구조

mkdir mnist-tutorial
cd mnist-tutorial

필요한 파일:

mnist-tutorial/
├── train.py # 학습 스크립트
└── requirements.txt # Python 의존성

requirements.txt

requirements.txt
keynet-train==0.8.5

train.py

Docker로 첫 학습 실행에서 사용한 코드를 그대로 사용합니다:

train.py
import argparse
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import mlflow
from keynet_train import trace_pytorch


# 간단한 CNN 모델 정의
class SimpleCNN(nn.Module):
def __init__(self):
super(SimpleCNN, self).__init__()
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.fc1 = nn.Linear(9216, 128)
self.fc2 = nn.Linear(128, 10)
self.dropout = nn.Dropout(0.25)

def forward(self, x):
x = torch.relu(self.conv1(x))
x = torch.relu(self.conv2(x))
x = torch.max_pool2d(x, 2)
x = self.dropout(x)
x = torch.flatten(x, 1)
x = torch.relu(self.fc1(x))
x = self.dropout(x)
x = self.fc2(x)
return torch.log_softmax(x, dim=1)


def train_epoch(model, device, train_loader, optimizer, epoch):
"""한 epoch 학습"""
model.train()
total_loss = 0
correct = 0
total = 0

for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = nn.functional.nll_loss(output, target)
loss.backward()
optimizer.step()

# 통계 수집
total_loss += loss.item()
pred = output.argmax(dim=1, keepdim=True)
correct += pred.eq(target.view_as(pred)).sum().item()
total += len(data)

avg_loss = total_loss / len(train_loader)
accuracy = 100.0 * correct / total
return avg_loss, accuracy


def validate(model, device, test_loader):
"""Validation 수행"""
model.eval()
test_loss = 0
correct = 0

with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += nn.functional.nll_loss(output, target, reduction="sum").item()
pred = output.argmax(dim=1, keepdim=True)
correct += pred.eq(target.view_as(pred)).sum().item()

test_loss /= len(test_loader.dataset)
accuracy = 100.0 * correct / len(test_loader.dataset)
return test_loss, accuracy


# @trace_pytorch 데코레이터로 자동화된 학습 함수
@trace_pytorch(
model_name="mnist-classification",
sample_input=torch.randn(1, 1, 28, 28),
base_image="pytorch/pytorch:2.8.0-cuda12.9-cudnn9-runtime"
)
def train_mnist(batch_size, epochs, learning_rate):
# Device 설정
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using device: {device}")

# Dataset 준비 (런타임 다운로드)
data_dir = "./data" # MNIST 데이터셋 저장 경로
transform = transforms.Compose(
[transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]
)

train_dataset = datasets.MNIST(
data_dir, train=True, download=True, transform=transform
)
test_dataset = datasets.MNIST(data_dir, train=False, transform=transform)

train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)

# Model 초기화
model = SimpleCNN().to(device)
optimizer = optim.Adam(model.parameters(), lr=learning_rate)

# Hyperparameters logging
mlflow.log_params({
"batch_size": batch_size,
"epochs": epochs,
"learning_rate": learning_rate,
"optimizer": "Adam",
"device": str(device),
})

print("🚀 학습 시작...")

# 학습 loop
for epoch in range(1, epochs + 1):
# Training
train_loss, train_acc = train_epoch(
model, device, train_loader, optimizer, epoch
)

# Validation
val_loss, val_acc = validate(model, device, test_loader)

# Metric logging
mlflow.log_metrics({
"train_loss": train_loss,
"train_accuracy": train_acc,
"val_loss": val_loss,
"val_accuracy": val_acc,
}, step=epoch)

print(
f"Epoch {epoch}/{epochs}: "
f"Train Loss={train_loss:.4f}, Train Acc={train_acc:.2f}% | "
f"Val Loss={val_loss:.4f}, Val Acc={val_acc:.2f}%"
)

# 최종 결과
print(f"✅ 학습 완료! 최종 Validation Accuracy: {val_acc:.2f}%")

return model


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="MNIST Classification Training")
parser.add_argument("--batch-size", type=int, default=64, help="Training batch size (default: 64)")
parser.add_argument("--epochs", type=int, default=5, help="Number of training epochs (default: 5)")
parser.add_argument("--learning-rate", type=float, default=0.001, help="Learning rate (default: 0.001)")
args = parser.parse_args()

train_mnist(
batch_size=args.batch_size,
epochs=args.epochs,
learning_rate=args.learning_rate,
)

핵심 포인트:

  • datasets.MNIST는 런타임에 데이터를 자동 다운로드합니다 (download=True)
  • Platform에서 별도의 데이터셋 업로드가 필요 없습니다
  • @trace_pytorch 데코레이터에 model_namebase_image를 지정해야 합니다

3. CLI 인증 및 이미지 업로드

keynet-train CLI를 사용하여 Platform 인증 및 이미지 빌드/업로드를 한 번에 처리합니다.

Platform 로그인

keynet login https://gateway.aiplatform.re.kr

입력 프롬프트:

Username: YOUR@EMAIL
Password: ********
Login Succeeded
✅ Logged in to https://gateway.aiplatform.re.kr
✅ Docker login successful

Email: YOUR@EMAIL
Password:

✓ Platform authentication successful
✓ Harbor login successful
✓ Configuration saved

╭────────────────── Login Complete ───────────────────╮
│ │
│ Server: https://gateway.aiplatform.re.kr │
│ User: YOUR@EMAIL │
│ Expires: 2025-12-10T10:53:23.619082Z │
│ Config: /Users/hbjs/.config/keynet/config.json │
│ │
╰─────────────────────────────────────────────────────╯

이미지 빌드 및 업로드

keynet-train push train.py

CLI가 자동으로 다음 작업을 수행합니다:

  1. @trace_pytorch 데코레이터에서 model_namebase_image 추출
  2. Dockerfile 자동 생성
  3. Docker 이미지 빌드
  4. Platform에 모델 생성 (자동)
  5. Harbor에 이미지 push

자동 생성되는 Dockerfile:

FROM pytorch/pytorch:2.8.0-cuda12.9-cudnn9-runtime
WORKDIR /workspace
COPY . /workspace/
RUN if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
CMD ["python", "train.py"]

4. Empty Dataset 생성

MNIST는 런타임에 데이터를 다운로드하므로 실제 데이터셋 업로드가 필요 없습니다.

"데이터셋 생성" 버튼을 클릭하고, 이름을 입력한 후 파일을 선택하지 않고 "생성" 버튼을 클릭합니다.

데이터셋 생성 버튼

Empty Dataset

Platform 제약사항으로 학습 실행 시 데이터셋 선택이 필수입니다. Empty Dataset은 실제 파일을 포함하지 않으며, Platform의 데이터셋 선택 요구사항을 충족하기 위한 용도로만 사용됩니다.

자세한 내용: Empty Dataset 개념

5. 학습 실행

Platform의 "학습" 메뉴로 이동하여 새 학습을 생성합니다.

모델 선택

업로드한 MNIST 학습 모델을 선택합니다.

학습할 모델 선택

데이터셋 선택

4번에서 생성한 Empty Dataset을 선택합니다.

데이터셋 선택

Hyperparameter 설정 (선택사항)

코드에 정의된 기본값(batch-size=64, epochs=5, learning-rate=0.001)으로 학습하거나, 필요한 경우 값을 변경할 수 있습니다.

학습 실행

Hyperparameter는 선택사항

코드에서 이미 기본값을 정의했으므로, Hyperparameter를 변경하지 않아도 바로 학습이 실행됩니다. Platform에서 Hyperparameter를 미리 등록하는 과정도 필요 없습니다.

사용 가능한 Hyperparameter:

ParameterTypeDefaultDescription
batch-sizeint64Training batch size
epochsint5Number of training epochs
learning-ratefloat0.001Learning rate

"학습 실행" 버튼을 클릭하면 Platform이 자동으로 다음 작업을 수행합니다:

  1. 이미지 Pull: Harbor에서 학습 이미지 다운로드 (최초 1회, 10~20분 소요 가능)
  2. 데이터 준비: data-initializer 컨테이너로 Empty Dataset 마운트 확인
  3. 학습 실행: train 컨테이너에서 학습 시작
    • 런타임에 MNIST 데이터셋 자동 다운로드
    • Epoch별 학습 진행
    • Validation 수행
  4. 모델 변환: 학습 완료 후 자동 ONNX 변환
  5. 결과 업로드: MLflow에 모델 및 메트릭 자동 기록
  6. 모니터링: monitor 사이드카로 학습 상태 추적

학습 진행 상황 확인

학습이 시작되면 학습 목록에서 상태를 확인할 수 있습니다.

생성된 학습

학습을 클릭하여 상세 페이지로 이동하면 학습 정보실시간 로그 탭에서 진행 상황을 확인할 수 있습니다.

학습 정보 탭:

학습 상세 정보

학습 정보 탭에서는 다음 정보를 확인할 수 있습니다:

  • 학습 정보: 학습 ID, 학습명, 모델, 데이터셋, Job 이름
  • Pod 상태: Pod 이름, 상태(Succeeded/Running/Failed), 시작 시간, 노드, IP 주소, 세부 상태
  • 컨테이너 상태:
    • 데이터 준비 (data-initializer): Empty Dataset 마운트 확인
    • 학습 실행 (train): 실제 학습 프로세스
    • 사이드카 (monitor): 학습 모니터링
  • Pod Events: Pod 생성부터 종료까지의 이벤트 타임라인
Pod Events 활용

학습 이미지를 pull하는 과정에서 시간이 오래 걸릴 수 있습니다. 학습 이미지 크기가 10GB ~ 20GB 정도 되므로 10분 이상 waiting 상태가 유지될 수 있습니다. 이러한 대기 상태에서의 현황은 Pod Events에서 확인하는 것이 가장 정확합니다.

실시간 로그 탭:

학습 실시간 로그

실시간 로그에서는 학습 진행 과정을 실시간으로 확인할 수 있습니다:

  1. 데이터 다운로드: MNIST 데이터셋 자동 다운로드 진행 상황
  2. 학습 진행: Epoch별 Train Loss, Train Accuracy, Validation Loss, Validation Accuracy
  3. ONNX 변환: 학습 완료 후 자동 ONNX 변환
  4. MLflow 업로드: 모델 및 메타데이터 업로드

예상 로그 출력:

Using device: cuda:0
Downloading MNIST dataset...
🚀 학습 시작...

Epoch 1/5: Train Loss=0.3456, Train Acc=89.23% | Val Loss=0.1234, Val Acc=96.45%
Epoch 2/5: Train Loss=0.1234, Train Acc=96.78% | Val Loss=0.0987, Val Acc=97.12%
...
✅ 학습 완료! 최종 Validation Accuracy: 98.23%

🔄 Converting PyTorch model to ONNX...
✅ ONNX conversion successful: model.onnx
📤 Uploading to MLflow...
Log streaming completed

GPU 인식 실패

증상:

RuntimeError: CUDA error: no kernel image is available for execution on the device

해결:

base_image의 CUDA 버전이 GPU를 지원하지 않습니다. Docker로 첫 학습 실행 가이드를 참고하여 적절한 PyTorch 이미지를 선택하세요.

다음 단계

MNIST 튜토리얼을 완료했습니다! 이제:

  1. YOLO 객체 탐지 튜토리얼: 실제 데이터셋 업로드 및 coco128.yaml 사용
  2. Hyperparameter 튜닝: 다양한 설정으로 실험하여 최적의 모델 찾기
  3. 모델 배포: ONNX 모델을 다운로드하여 프로덕션 환경에 배포