깊은 신경망을 안정화하는 ResNet·DenseNet·Batch Normalization

ResNet의 잔차 연결, DenseNet의 특징 재사용, Batch Normalization의 통계 정규화로 깊은 신경망 학습을 안정화하는 방법을 정리한다.

2026-08-14 · 최초 발행 2024-04-29

깊이를 늘릴 때 달라지는 학습 경로

깊은 신경망은 층을 쌓는 것만으로 성능이 보장되지 않는다. 기울기 소실, 특징 손실, 활성값 통계의 흔들림이 함께 발생할 수 있다. ResNet, DenseNet, Batch Normalization은 각각 항등 연결, 특징 재사용, 활성값 정규화로 이 문제를 다룬다.

ResNet은 F(x) + x 형태의 잔차 학습을 사용한다. 항등 경로가 기울기의 직통 경로가 되어 깊이가 커져도 기울기 소실을 완화하며, 100+ 층 네트워크 학습을 가능하게 한다. 기본 블록(BasicBlock), 병목 블록(Bottleneck), v2(Pre-activation) 변형이 있다.

DenseNet은 각 레이어가 이전 모든 레이어의 출력을 채널 차원으로 연결(concatenate)해 다음 입력으로 쓰는 구조다. 성장률(k)로 채널 증가 폭을 제어하며, Transition과 Compression(θ)으로 메모리와 연산량을 조절한다.

Batch Normalization(BN)은 미니배치 단위 활성값을 평균 0, 분산 1 기준으로 정규화한 뒤 학습 가능한 γ와 β로 재스케일·시프트한다. 내부 공변량 변화를 줄여 더 큰 학습률과 빠른 수렴을 목표로 한다. 학습에는 배치 통계를, 추론에는 이동 평균 통계를 사용하므로 작은 배치에서는 불안정할 수 있다.

연결 구조가 정보와 기울기를 다루는 방식

ResNet의 항등 경로는 깊은 블록을 통과하는 동안 기울기가 사라지는 문제를 완화한다. 해상도나 채널 수가 바뀌는 다운샘플 구간에서는 1x1 프로젝션으로 차원을 맞춘다. 병목 블록은 1x1 축소, 3x3 처리, 1x1 확장 순서로 연산량을 줄이면서 깊이를 늘릴 수 있게 한다.

DenseNet은 이전 특징 맵 전체에 접근하므로 정보 손실을 줄이고 특징 재사용을 높인다. 대신 연결이 누적될수록 메모리와 시간 복잡도가 커진다. BN-Conv-ReLU 패턴을 반복하고, Transition에서 채널을 압축하며 공간 크기도 줄인다. Compression은 θ∈[0,1] 범위에서 다룬다.

BN을 배치하는 순서도 모델 특성에 영향을 준다. Post-activation은 Conv→BN→ReLU 순서이고, Pre-activation은 BN→ReLU→Conv 순서다. Pre-activation은 잔차 경로의 정보 보존과 일반화 개선 경향을 가진다. BN의 eps, momentum, 통계 추정 방식 역시 수렴과 안정성에 영향을 준다.

BatchNorm Mechanism미니배치 활성μ,σ² 추정정규화:(x-μ)/√(σ²+ε)γ·정규화+β출력Dense Block (DenseNet)concatconcatconcatconcat입력 x0BN→ReLU→Conv출력 x1M=concat(x0,x1)BN→ReLU→Conv출력 x2M=concat(x0,x1,x2)Residual Block (ResNet)입력 xConv 3x3BNReLUConv 3x3BN항등/프로젝션 경로Add(F(x), x)ReLU

구현에서 확인할 ResNet과 DenseNet 블록

전제조건: Python 3.9+, PyTorch 2.3+, CUDA 선택사항.

ResNet의 pre-activation BasicBlock은 정규화와 활성화를 합성곱보다 앞에 둔다. 입력과 출력의 차원이 다르면 shortcut에 1x1 합성곱을 사용한다.

import torch
import torch.nn as nn

class PreActBasicBlock(nn.Module):
    expansion = 1
    def __init__(self, in_ch, out_ch, stride=1):
        super().__init__()
        self.bn1 = nn.BatchNorm2d(in_ch)
        self.conv1 = nn.Conv2d(in_ch, out_ch, 3, stride, 1, bias=False)
        self.bn2 = nn.BatchNorm2d(out_ch)
        self.conv2 = nn.Conv2d(out_ch, out_ch, 3, 1, 1, bias=False)
        self.shortcut = nn.Identity() if (stride==1 and in_ch==out_ch) \
            else nn.Conv2d(in_ch, out_ch, 1, stride, bias=False)

    def forward(self, x):
        out = self.conv1(torch.relu(self.bn1(x)))
        out = self.conv2(torch.relu(self.bn2(out)))
        return out + self.shortcut(x)

# Test
x = torch.randn(4, 64, 56, 56)
blk = PreActBasicBlock(64, 64)
y = blk(x)
print(y.shape)

DenseNet의 DenseLayer는 새 특징을 기존 입력과 채널 방향으로 결합한다. Transition은 압축된 채널에 평균 풀링을 적용해 다음 블록으로 넘긴다.

class DenseLayer(nn.Module):
    def __init__(self, in_ch, growth_rate):
        super().__init__()
        inter = 4 * growth_rate  # bottleneck
        self.net = nn.Sequential(
            nn.BatchNorm2d(in_ch), nn.ReLU(inplace=True),
            nn.Conv2d(in_ch, inter, kernel_size=1, bias=False),
            nn.BatchNorm2d(inter), nn.ReLU(inplace=True),
            nn.Conv2d(inter, growth_rate, kernel_size=3, padding=1, bias=False),
        )
    def forward(self, x):
        new_feat = self.net(x)
        return torch.cat([x, new_feat], dim=1)

class Transition(nn.Module):
    def __init__(self, in_ch, compression=0.5):
        super().__init__()
        out_ch = int(in_ch * compression)
        self.net = nn.Sequential(
            nn.BatchNorm2d(in_ch), nn.ReLU(inplace=True),
            nn.Conv2d(in_ch, out_ch, kernel_size=1, bias=False),
            nn.AvgPool2d(kernel_size=2, stride=2),
        )
    def forward(self, x):
        return self.net(x)

배치 통계와 메모리 제약을 운영 조건에 맞추기

BN은 model.train()에서 배치 통계를 쓰고 model.eval()에서는 이동 평균을 쓴다. 작은 배치에서는 nn.SyncBatchNorm이나 GroupNorm 대체를 고려할 수 있으며, BN을 고정(freeze)하는 전략도 선택지다. eps(예: 1e-5)는 0분산을 보호하고, NaN이 발생하면 학습률과 입력 스케일을 점검한다.

대규모 분산 학습에서는 BN 통계 동기화를 위해 SyncBatchNorm이 필요하다. 단일 기기 학습에서는 배치 독립 통계를 사용하고, 다중 기기 학습에서는 통계 동기화로 전역 일관성을 확보한다. 재현성을 위해 시드를 고정하고 Deterministic 연산을 선택하며, BN 통계를 저장·로딩해 추론 일관성을 보장한다.

DenseNet은 성장률과 압축비로 파라미터 및 메모리의 균형을 조절한다. 메모리가 부족하면 혼합정밀(amp), 체크포인팅, 채널 프루닝을 함께 검토할 수 있다. ResNet은 다운샘플 구간의 차원 불일치에 1x1 프로젝션 경로를 적용하고, DenseNet은 Transition으로 해상도와 채널을 정합한다.

학습 파이프라인은 데이터 적재 뒤 RandAugment, RandomCrop, MixUp 같은 증강을 거쳐 배치를 구성한다. 전방향에서는 Conv/BN/ReLU와 ResNet/DenseNet 블록을 통과하고 로스를 계산한 다음, 필요하면 기울기 클리핑을 적용해 역전파한다. 이후 AdamW 또는 SGD+Momentum으로 옵티마이저를 갱신하고 Cosine 또는 LR warmup 스케줄러와 메트릭 로깅을 이어간다.

백본 선택이 달라지는 작업

이미지 분류·탐지·분할에서는 ResNet-50/101 백본에 FPN 또는 Mask R-CNN을 조합하는 방식이 표준으로 쓰인다. DenseNet은 파라미터 예산이 제한된 경량·정확도 균형 과제에 적합하다.

의료 영상과 리모트 센싱에서는 DenseNet의 특징 재사용이 소량 데이터에서도 견고한 표현을 확보하는 데 활용된다. 자율주행 인지에서는 DeepLayerAgg, Dilated Conv 같은 ResNet 변형으로 멀티스케일 특징을 처리한다.

엣지 배포에서는 ResNet bottleneck에 채널 프루닝과 지식증류를 결합해 추론 효율을 높일 수 있다. BN folding은 추론 최적화에 사용된다. 시계열과 음성에는 1D-ResNet 변형을 적용하고, BN 대신 LayerNorm 또는 GroupNorm 조합을 고려한다.

수렴·비용·운영 특성 비교

BN 도입 시 2~4배 빠른 수렴이 보고되어 있으며(문헌 기준, 최신 정보 확인 필요), 더 큰 학습률과 안정적인 옵티마 도달에 기여한다. ResNet과 DenseNet은 동일 파라미터 예산 대비 Top-1 정확도 향상 경향을 보이고, DenseNet은 적은 파라미터로 유사 성능을 달성한 사례가 다수 있다.

깊은 네트워크에서도 기울기 소실과 폭주를 완화하고, 재현 가능한 추론 통계를 통해 운영 안정성을 높일 수 있다. 파라미터·연산량 대비 성능이 개선되면 학습·추론 비용 절감으로 이어진다. BN folding과 TensorRT 최적화는 지연시간 단축에 활용된다.

항목 ResNet DenseNet BatchNorm
성능(정확도) 깊은 모델에서 강점, 전이학습 우수 파라미터 효율 대비 높은 정확도 모델 전반 수렴 및 일반화 개선
확장성 50~152+ 층까지 안정적 확장 성장률/전이층으로 제어 가능, 메모리 한계 고려 대규모 분산 시 Sync 필요
일관성 항등 경로로 기울기 일관성 확보 모든 특징 접근으로 표현 일관성 추론 시 이동 평균으로 출력 일관성
안정성 Pre-activation로 학습 안정성 향상 과도한 연결 시 메모리/시간 부담 작은 배치에서 불안정 가능
운영 편의 생태계/가중치/최적화 풍부 메모리 관리와 최적화 필요 BN folding 등 추론 최적화 용이

대다수 CV 과제에서는 ResNet+BN을 기본 백본으로 두고, 파라미터와 메모리 제약이 큰 환경에서는 DenseNet을 검토할 수 있다. 작은 배치나 분산 학습 환경에서는 SyncBN·GroupNorm 같은 BN 변형 전략을 함께 선택한다.

딥러닝ResNetDenseNetBatch Normalization신경망