Oxford-IIIT Pet 37종 품종 분류로 ResNet 과 ResNeXt 를 PyTorch 로 직접 구현하고,
파라미터 수를 맞춘 뒤 동일 조건에서 처음부터 학습해 비교한다.
pretrained weight 도, torchvision.models 도 쓰지 않는다.
30 epoch, 하드웨어 셋(Apple M5 · Colab Tesla T4 · RTX 3060 Ti 8GB)에서 같은 코드, 같은 seed(42), 같은 설정으로 학습한 결과 셋으로 정리된다.
ResNet 학습 처리량 (합성 텐서, batch 32, 224px):
| M5 | Colab T4 | RTX 3060 Ti | |
|---|---|---|---|
| ResNet | 167.9 img/s | 302.5 | 628.2 |
| ResNeXt | 75.5 img/s | 111.2 | 306.3 |
| 실학습 ResNet | 20.1 s/epoch | 32.0 | 6.3 |
3060 Ti 는 M5 의 3.74배, T4 의 2.08배다. 3060 Ti 의 ResNeXt(306.3)가 T4 의 ResNet(302.5)과 같다 — 한 세대 위 하드웨어가 architecture 불이익을 통째로 상쇄한다.
단, 실학습에서는 T4 가 M5 보다 느렸다 (32.0 s vs 20.1 s). Colab VM 의 vCPU 2개가 데이터 공급을 못 따라가 GPU 가 절반 넘게 놀았다. 계산력 순서와 실제 학습 시간 순서가 어긋난 유일한 지점이고, 파이프라인을 고쳐 1.65배를 되찾은 기록이
data-pipeline.md에 있다.
파라미터는 5% 적은데도 그렇다.
| M5 | T4 | 3060 Ti | 평균 | |
|---|---|---|---|---|
| 학습 처리량 불이익 | 2.22배 | 2.72배 | 2.05배 | 2.33배 |
| Peak memory | 2.71배 | 2.56배 | 2.47배 | 2.58배 |
세 하드웨어에서 2.05~2.72배로 유지되고 방향이 한 번도 바뀌지 않는다.
grouped convolution 은 FLOPs 를 줄이지만 메모리 접근이 흩어지고,
expansion=4 bottleneck 이 activation 을 4배로 키운다.
파라미터 수는 속도·메모리의 대리 지표가 아니다.
2,944 장 · 37 클래스 · 30 epoch · scratch 라는 예산에서는 두 모델이 구분되지 않는다.
| ResNet | ResNeXt | 격차 | |
|---|---|---|---|
| M5 | 0.4813 | 0.4584 | +2.29%p |
| Colab T4 | 0.4590 | 0.4511 | +0.79%p |
| RTX 3060 Ti | 0.4372 | 0.4415 | −0.43%p |
하드웨어를 바꾸자 우열의 부호가 바뀌었다. 게다가 ResNet 한 모델을 하드웨어만 바꿔 가며 잰 진폭(4.41%p)이 어느 하드웨어에서 잰 두 모델의 격차보다도 크다. 신호가 잡음에 묻힌 정도가 아니라 잡음이 신호보다 크다.
M5 만 봤다면 "ResNet 이 낫다"고 적었을 것이다. 세 번째 하드웨어가 그 문장을 뒤집었다.
두 모델의 오분류는 겹치지 않는다. ResNet 만 맞힌 466 장, ResNeXt 만 맞힌 382 장. 합집합 58.5% 는 ResNet 단독 48.1% 보다 10.4%p 높다. 정확도로는 구분되지 않지만 서로 다른 것을 배운다.
→ 전체 분석: docs/results/RESULTS.md
(학습 곡선, 37×37 confusion matrix, 오분류 4분할, confidence 분포, 하드웨어 매트릭스)
→ 데이터 파이프라인 최적화: docs/results/data-pipeline.md
(T4 가 굶은 원인, GPU 증강, 그 이득이 모델마다 다른 이유)
uv sync
uv run pytest -q -m "not dataset" # 163 passed
uv run python scripts/train.py --model resnet --epochs 30
uv run python scripts/train.py --model resnext --epochs 30
uv run python scripts/evaluate.py --model resnet --checkpoint checkpoints/resnet/best.pt
uv run python scripts/compare.py실행 가이드: docs/running-training.md ·
원격 GPU: docs/colab-workflow.md
| 경로 | 역할 |
|---|---|
src/petvision/models/ |
직접 구현한 ResNet / ResNeXt. deeptool 을 import 하지 않는다 |
src/petvision/{classifier,training,data,config}.py |
deeptool 위의 학습 인프라 |
src/petvision/{metrics,analysis,figures,benchmark}.py |
지표·분석·그림·벤치마크 |
scripts/ |
train · evaluate · inference · compare · benchmark CLI |
notebooks/ |
학습용 노트북 4종 |
docs/superpowers/plans/CONTRACT.md |
세 트랙 병렬 개발에 쓴 인터페이스 계약 |
아래는 구현 전에 작성한 원본 기획서다. 실제 결과는 위 링크를 보라.
Oxford-IIIT Pet Dataset을 이용해 ResNet과 ResNeXt 두 모델을 직접 구현하고, 동일 조건에서 직접 학습 및 추론하여 비교한다.
이 프로젝트의 핵심은 pretrained model을 가져다가 fine-tuning하는 것이 아니라,
모델 구조 직접 구현
↓
Random Initialization
↓
직접 Training
↓
Checkpoint 저장
↓
직접 Inference
↓
ResNet vs ResNeXt 비교
전체 과정을 경험하는 것이다.
또한 동일한 프로젝트를
Apple M5
RTX 3060 8GB
Google Colab GPU
환경에서 실행하여 하드웨어별 학습 성능도 비교한다.
Python
│
├── uv
│ └── dependency / environment management
│
├── PyTorch
│ ├── Tensor
│ ├── nn.Module
│ ├── Conv2d
│ ├── BatchNorm
│ ├── Optimizer
│ └── Loss
│
├── torchvision
│ ├── Oxford-IIIT Pet Dataset
│ └── Image Transform
│
├── deeptool
│ ├── DataModule
│ ├── Module
│ ├── Trainer
│ ├── Checkpoint
│ ├── Prediction
│ └── Device Management
│
└── Colab CLI
└── Remote GPU Training
Main Task:
37-Class Pet Breed Classification
입력:
RGB Image
출력:
37 Pet Breeds
데이터셋은 torchvision의
torchvision.datasets.OxfordIIITPet을 사용한다.
이 프로젝트에서는 torchvision의 완성 모델을 사용하지 않는다.
사용하지 않는 예:
torchvision.models.resnet18(...)
torchvision.models.resnext50_32x4d(...)대신 PyTorch의 기본 layer를 이용해 모델을 직접 구현한다.
사용할 주요 component:
nn.Conv2d
nn.BatchNorm2d
nn.ReLU
nn.MaxPool2d
nn.AdaptiveAvgPool2d
nn.LinearResNeXt의 경우:
groups=옵션을 사용하는 grouped convolution도 직접 구성한다.
첫 번째 모델은 직접 구현한 ResNet이다.
기본 구성은 ResNet-18 스타일로 한다.
Architecture:
Input
↓
7×7 Conv
↓
BatchNorm
↓
ReLU
↓
MaxPool
↓
Residual Stage 1
2 × BasicBlock
Residual Stage 2
2 × BasicBlock
Residual Stage 3
2 × BasicBlock
Residual Stage 4
2 × BasicBlock
↓
Global Average Pooling
↓
Linear
↓
37 Classes
핵심 residual block을 직접 구현한다.
개념적으로:
x
│
├─────────────────────┐
│ │
▼ │
3×3 Conv │
BatchNorm │
ReLU │
│ │
▼ │
3×3 Conv │
BatchNorm │
│ │
├──────── Add ◀────────┘
│
ReLU
stride 또는 channel 수가 변경되는 경우 shortcut에 projection을 사용한다.
1×1 Conv
+
BatchNorm
이 부분도 직접 구현한다.
src/petvision/models/
├── resnet.py
└── blocks.py
예상 구조:
class ResidualBlock(nn.Module):
...
class ResNet(nn.Module):
...deeptool abstraction과 모델 architecture는 분리한다.
예:
class ResNetClassifier(dt.Module):
def __init__(self, ...):
super().__init__()
self.net = ResNet(...)두 번째 모델은 ResNeXt를 직접 구현한다.
ResNet과 동일한 residual learning을 사용하지만 block 내부에서 grouped convolution을 사용한다.
핵심 구조:
1×1 Conv
↓
3×3 Grouped Conv
↓
1×1 Conv
↓
Residual Add
ResNeXt의 핵심 parameter:
groups
bottleneck width
cardinality
를 직접 설정할 수 있도록 한다.
예:
ResNeXtBlock(
out_channels=256,
groups=32,
bottleneck_width=4
)PyTorch에서는 grouped convolution을:
nn.Conv2d(
...,
groups=groups
)형태로 구현한다.
처음부터 너무 큰 ResNeXt-50을 구현하지 않고, Oxford-IIIT Pet 규모에 맞춰 작은 variant를 사용한다.
예:
ResNeXt-29 style
또는 자체적으로:
[2, 2, 2, 2]
stage depth를 가진 lightweight ResNeXt를 구성한다.
목적은 ImageNet benchmark 재현이 아니라:
동일한 compute 조건에서 ResNet과 ResNeXt architecture의 차이를 관찰하는 것
이다.
공정한 비교를 위해 다음 조건은 동일하게 유지한다.
Dataset
Train / Validation split
Image size
Batch size
Epochs
Optimizer
Learning rate
Weight decay
Data augmentation
Random seed
Loss function
Evaluation metric
가능한 경우 parameter 수 역시 함께 기록한다.
두 모델 모두 Random Initialization부터 직접 학습한다.
Pretrained weights:
사용하지 않음
Training:
ResNet
↓
Random Initialization
↓
Train
↓
Validation
↓
Best Checkpoint
ResNeXt
↓
Random Initialization
↓
Train
↓
Validation
↓
Best Checkpoint
37-class classification이므로:
CrossEntropyLoss를 사용한다.
deeptool Module 안에서는 예를 들어:
@dt.add_to_class(PetClassifier)
def loss(self, y_hat, y):
return F.cross_entropy(y_hat, y)와 같은 형태로 구현한다.
초기 baseline:
AdamW
예:
learning rate: 3e-4
weight decay: 1e-4
이후 필요하면:
SGD + Momentum
실험을 추가할 수 있다.
단 ResNet vs ResNeXt 메인 비교에서는 optimizer 설정을 동일하게 유지한다.
Baseline:
20 epochs
필요하면:
30~50 epochs
까지 늘린다.
Early stopping은 deeptool의 Trainer 기능을 활용한다.
Train:
RandomResizedCrop(224)
RandomHorizontalFlip
ColorJitter
RandomRotation
Normalize
Validation / Test:
Resize
CenterCrop
Normalize
두 모델에 동일한 transform을 사용한다.
deeptool은 architecture를 구현하는 데 사용하지 않는다.
architecture:
직접 PyTorch 구현
training infrastructure:
deeptool
로 역할을 명확히 나눈다.
deeptool이 담당:
Trainer
Device
Training history
Checkpoint
Early stopping
Prediction
Metric collection
예상 구조:
class PetClassifier(dt.Module):
def __init__(
self,
architecture,
num_classes=37,
learning_rate=3e-4,
):
super().__init__()
if architecture == "resnet":
self.net = ResNet(...)
elif architecture == "resnext":
self.net = ResNeXt(...)이 구조를 이용해 동일한 Trainer에서 architecture만 바꿀 수 있도록 한다.
ResNet:
uv run python scripts/train.py \
--model resnet \
--epochs 20ResNeXt:
uv run python scripts/train.py \
--model resnext \
--epochs 20학습 이후 반드시 별도의 inference 과정을 수행한다.
Training script 안에서 validation만 하고 끝내지 않는다.
별도:
scripts/inference.py
를 만든다.
흐름:
이미지 입력
↓
Preprocessing
↓
Checkpoint Load
↓
Model Forward
↓
Softmax
↓
Top-K Prediction
ResNet:
uv run python scripts/inference.py \
--model resnet \
--checkpoint checkpoints/resnet_best.pt \
--image sample.jpgResNeXt:
uv run python scripts/inference.py \
--model resnext \
--checkpoint checkpoints/resnext_best.pt \
--image sample.jpg예:
Image:
sample.jpg
ResNet prediction:
1. British Shorthair 72.4%
2. Russian Blue 12.1%
3. Persian 5.7%
ResNeXt:
1. British Shorthair 81.6%
2. Russian Blue 8.4%
3. Persian 3.1%
이를 통해 accuracy 숫자만 비교하지 않고 실제 prediction confidence 차이도 확인한다.
Test dataset 전체에 대해서도 inference를 수행한다.
저장:
outputs/predictions/
resnet_predictions.csv
resnext_predictions.csv
형식:
image
true_label
predicted_label
confidence
correct
두 모델의 오분류 결과를 분석한다.
특히 다음 세 집합을 구분한다.
둘 다 맞음
ResNet만 맞음
ResNeXt만 맞음
둘 다 틀림
이 부분이 모델 비교에서 상당히 중요한 분석 요소가 된다.
ResNet:
37 × 37 confusion matrix
ResNeXt:
37 × 37 confusion matrix
를 각각 만든다.
또한 혼동이 가장 많은 breed pair를 찾는다.
예:
Russian Blue ↔ British Shorthair
Ragdoll ↔ Birman
Bengal ↔ Abyssinian
최종적으로 다음을 비교한다.
Model Parameters
Model Size
Training Loss
Validation Loss
Train Accuracy
Validation Accuracy
Test Accuracy
Macro Precision
Macro Recall
Macro F1
Training Time
Epoch Time
Images / Second
Peak GPU Memory
Inference Latency
Prediction Confidence
최종 분석에서는 다음 질문에 답한다.
ResNeXt가 ResNet보다 실제로 정확한가?
어느 모델이 더 빠르게 validation accuracy를 높이는가?
ResNeXt의 grouped convolution이 더 많은 계산 효율성을 제공하는가?
두 모델의 GPU VRAM 사용량은 어떻게 다른가?
어느 모델이 이미지 한 장을 더 빠르게 inference하는가?
두 모델 모두 다음 환경에서 가능하면 실행한다.
M5
RTX 3060 8GB
Colab GPU
따라서 최종 benchmark matrix는:
M5 RTX3060 Colab
ResNet ✓ ✓ ✓
ResNeXt ✓ ✓ ✓
가 된다.
예:
Model Device Images/s Epoch Time VRAM
ResNet M5
ResNet RTX3060
ResNet Colab T4
ResNeXt M5
ResNeXt RTX3060
ResNeXt Colab T4
이를 통해:
Architecture × Hardware
두 가지 축을 동시에 분석한다.
로컬에서 코드를 작성한 뒤:
VS Code
↓
uv
↓
deeptool + PyTorch
↓
Colab CLI
↓
Remote GPU
에서 실행한다.
ResNet:
Local source
↓
Colab GPU
↓
Train ResNet
↓
resnet_best.pt
↓
Download
ResNeXt:
Local source
↓
Colab GPU
↓
Train ResNeXt
↓
resnext_best.pt
↓
Download
petvision/
│
├── pyproject.toml
├── uv.lock
├── README.md
│
├── configs/
│ ├── resnet.yaml
│ └── resnext.yaml
│
├── src/
│ └── petvision/
│ │
│ ├── data.py
│ │
│ ├── models/
│ │ ├── blocks.py
│ │ ├── resnet.py
│ │ └── resnext.py
│ │
│ ├── classifier.py
│ ├── metrics.py
│ ├── benchmark.py
│ └── utils.py
│
├── scripts/
│ ├── train.py
│ ├── evaluate.py
│ ├── inference.py
│ └── benchmark.py
│
├── notebooks/
│ ├── 01_dataset.ipynb
│ ├── 02_resnet.ipynb
│ ├── 03_resnext.ipynb
│ └── 04_analysis.ipynb
│
├── checkpoints/
│ ├── resnet/
│ └── resnext/
│
└── outputs/
├── metrics/
├── predictions/
├── benchmarks/
└── figures/
Dataset Exploration
Oxford-IIIT Pet
↓
Class distribution
↓
Image samples
↓
Transforms
ResNet
Residual Block
↓
Skip Connection
↓
ResNet Architecture
↓
Forward Pass
↓
Training
ResNeXt
Bottleneck
↓
Grouped Convolution
↓
Cardinality
↓
ResNeXt Architecture
↓
Forward Pass
↓
Training
Analysis
ResNet vs ResNeXt
Accuracy
Loss
Convergence
Inference
Hardware
Misclassification
ResNet에서는 반드시 직접 이해해야 할 것:
Residual Learning
Skip Connection
Projection Shortcut
Stage
Stride
Channel Expansion
ResNeXt에서는 추가로:
Grouped Convolution
Cardinality
Bottleneck Width
Channel Grouping
을 직접 구현한다.
초기 프로젝트에서는 다음은 하지 않는다.
ImageNet pretrained model
torchvision 완성 ResNet
torchvision 완성 ResNeXt
HuggingFace Trainer
PyTorch Lightning
대규모 hyperparameter search
deeptool 외의 큰 training framework도 추가하지 않는다.
목적은 abstraction을 과도하게 늘리는 것이 아니라:
PyTorch architecture는 직접 배우고, 반복적인 training boilerplate만 deeptool로 줄이는 것
이다.
프로젝트가 끝났을 때 다음 질문에 답할 수 있어야 한다.
- ResNet의 residual connection은 어떻게 구현되는가?
- ResNeXt의 grouped convolution은 ResNet과 무엇이 다른가?
- Cardinality는 무엇이며 모델 구조에 어떤 영향을 주는가?
- 동일 데이터셋에서 ResNet과 ResNeXt 중 어느 모델이 더 잘 학습되는가?
- 두 모델의 학습 속도와 VRAM 사용량은 어떻게 다른가?
- 두 모델이 틀리는 이미지에는 어떤 차이가 있는가?
- M5, RTX 3060, Colab GPU에서 두 모델의 학습 속도는 얼마나 다른가?
- 로컬에서 개발한 프로젝트를 Colab CLI를 이용해 원격 GPU에서 동일하게 실행할 수 있는가?
프로젝트 완료 시 다음 결과가 존재해야 한다.
직접 구현한 ResNet
직접 구현한 ResNeXt
Oxford-IIIT Pet Dataset Pipeline
ResNet Training Checkpoint
ResNeXt Training Checkpoint
ResNet Inference
ResNeXt Inference
Test Prediction CSV
Confusion Matrix
Misclassification Analysis
Training Curve
Inference Benchmark
Hardware Benchmark
uv.lock
Colab CLI Workflow
README
이 프로젝트의 최종 핵심은:
동일한 Oxford-IIIT Pet Dataset에 대해 ResNet과 ResNeXt를 PyTorch로 직접 구현하고, 동일한 training pipeline에서 처음부터 학습한 뒤 실제 inference 결과와 하드웨어 성능까지 비교하는 것
이다.