1. 개요

숭산텍은 딥러닝 프레임워크로 PyTorch 2.x를, 그리고 BC/RL 모델의 백본으로 ResNet18(ImageNet pretrained)을 채택합니다.

2. 왜 ResNet18인가?

“시스템 우선” 원칙에 따라, 새로운 아키텍처를 발명하기보다는 검증된 모델을 사용하여 파이프라인 전체에 집중합니다. ResNet18 채택의 이유는 다음과 같습니다:

이유 설명
입증된 성능 ImageNet 등 다양한 비전 태스크에서 검증
경량성 약 11M 파라미터로 Jetson 환경에 적합
풍부한 사전학습 torchvision에서 가중치 즉시 활용 가능
단순한 구조 Skip Connection 외에 특별한 트릭 없음 → 디버깅 용이

3. 모델 정의 예시

import torch
import torch.nn as nn
from torchvision.models import resnet18, ResNet18_Weights

class BehavioralCloningModel(nn.Module):
    def __init__(self):
        super().__init__()
        # ImageNet pretrained backbone
        self.backbone = resnet18(weights=ResNet18_Weights.IMAGENET1K_V1)
        # 마지막 FC를 제거하고 특징 추출기로만 사용
        self.backbone.fc = nn.Identity()
        # FC Head: 512 -> 256 -> 2 (steering, throttle)
        self.fc_head = nn.Sequential(
            nn.Linear(512, 256),
            nn.ReLU(inplace=True),
            nn.Dropout(0.1),
            nn.Linear(256, 2),
        )
    
    def forward(self, x):
        features = self.backbone(x)  # (B, 512)
        out = self.fc_head(features)  # (B, 2)
        return out

4. 학습 하이퍼파라미터

항목 값
Optimizer AdamW
Learning Rate 1e-4
Weight Decay 1e-4
Batch Size 64
Scheduler CosineAnnealingLR
Loss MSE (multi-task)
Epochs 50 (Early Stopping with patience=5)

5. 입력 전처리

from torchvision import transforms

train_transform = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.RandomHorizontalFlip(p=0.0),  # 자율주행은 좌우 의미가 다름
    transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406],
                         std=[0.229, 0.224, 0.225]),
])

6. 모델 저장 및 체크포인트

학습 중에는 검증 손실 기준으로 best 체크포인트를 저장하며, 양자화 변환을 위해 별도로 ONNX export가 필요합니다.

# Best checkpoint
torch.save({
    'epoch': epoch,
    'backbone': model.backbone.state_dict(),
    'fc_head': model.fc_head.state_dict(),
    'optimizer': optimizer.state_dict(),
    'val_loss': val_loss,
}, 'checkpoints/bc_best.pth')

# ONNX export
dummy_input = torch.randn(1, 3, 224, 224).cuda()
torch.onnx.export(
    model, dummy_input, 'bc_model.onnx',
    input_names=['input'], output_names=['output'],
    opset_version=17,
)