본문 바로가기
Artificial Intelligence/21. PyTorch

[PYTORCH] nn.ModuleList와 일반 Python List의 3가지 핵심 차이와 파라미터 등록 해결 방법 7가지

by Papa Martino V 2026. 3. 24.
728x90

nn.ModuleList와 일반 Python List의 3가지 핵심
nn.ModuleList와 일반 Python List의 3가지 핵심

 

 

파이토치(PyTorch)로 딥러닝 모델을 설계하다 보면, 동일한 구조의 레이어를 여러 개 반복해서 쌓아야 하는 상황이 자주 발생합니다. 이때 많은 초보 개발자들은 파이썬의 익숙한 도구인 일반 List를 사용하여 레이어를 담으려 시도합니다. 하지만 코드를 실행하고 학습을 시작하는 순간, 모델의 파라미터가 업데이트되지 않거나 GPU로 모델이 이동하지 않는 기괴한 현상을 마주하게 됩니다. 이는 파이토치의 상태 관리 시스템이 일반 파이썬 리스트를 인식하지 못하기 때문에 발생하는 문제입니다. 본 포스팅에서는 시니어 프레임워크 엔지니어의 시각으로 nn.ModuleList의 내부 메커니즘을 해부하고, 왜 일반 리스트를 사용하면 모델이 '고장'나는지에 대한 수학적, 구조적 근거와 함께 실무 현장에서 즉시 적용 가능한 7가지 해결 시나리오를 제시합니다.


1. nn.ModuleList vs 일반 Python List의 결정적 차이

가장 큰 차이는 '자동 등록(Automatic Registration)' 여부입니다. 파이토치의 nn.Module은 자신에게 할당된 속성들을 감시하며, 그 속성이 모듈일 경우 내부적인 관리 명단에 추가합니다. 하지만 일반 리스트는 이 감시망을 벗어납니다.

비교 항목 nn.ModuleList 일반 Python List (list)
파라미터 등록 자동으로 parameters()에 포함됨 포함되지 않음 (학습 대상 제외)
장치 이동 (.to) model.to(device) 호출 시 함께 이동 GPU로 이동하지 않고 CPU에 잔류
상태 저장 (state_dict) state_dict()에 레이어 가중치 저장됨 저장되지 않음 (가중치 손실 발생)
동작 방식 PyTorch 전용 레이어 컨테이너 단순한 객체 참조 저장소
유연성 리스트와 동일한 인덱싱/슬라이싱 지원 동일함

2. 왜 nn.ModuleList를 써야만 하는가? (독창적 가치 분석)

  • 그래디언트 전파의 생명선: 옵티마이저는 model.parameters()를 통해 업데이트할 대상을 찾습니다. 일반 리스트에 담긴 레이어는 옵티마이저에게 '투명인간'과 같습니다. nn.ModuleList는 이들을 명시적으로 가시화합니다.
  • 재귀적 모듈 관리: 파이토치는 모델 내부에 또 다른 모델이 있는 계층 구조를 가집니다. nn.ModuleList는 이 계층 구조를 깨뜨리지 않고 자식 모듈들을 부모의 관리 하에 둡니다.
  • 직렬화 안전성: 협업 환경에서 모델을 .pth 파일로 공유할 때, 일반 리스트를 쓴 모델은 가중치가 없는 빈 껍데기만 저장되는 치명적인 결함이 발생합니다.

3. 실무 해결을 위한 핵심 Sample Examples (7가지)

반복적인 레이어 생성부터 동적 네트워크 설계까지, nn.ModuleList를 올바르게 활용하는 7가지 실전 해결 예제입니다.

Example 1: 동일한 레이어를 반복 생성하는 해결 방법

import torch.nn as nn

class RepeatNet(nn.Module):
    def __init__(self, depth):
        super().__init__()
        # 일반 리스트 [nn.Linear(10, 10) for _ in range(depth)] 대신 사용
        self.layers = nn.ModuleList([nn.Linear(10, 10) for _ in range(depth)])

    def forward(self, x):
        for layer in self.layers:
            x = layer(x)
        return x
    

Example 2: 일반 List 사용 시 파라미터 누락 확인 디버깅

# 잘못된 예시
bad_list = [nn.Linear(10, 10)]
model = nn.Module()
model.sub = bad_list
print(len(list(model.parameters()))) # 0 출력 (문제 발생)

# 해결 예시
good_list = nn.ModuleList([nn.Linear(10, 10)])
model.sub = good_list
print(len(list(model.parameters()))) # 2 출력 (Weight, Bias 정상 등록)
    

Example 3: 동적으로 변화하는 아키텍처(Dynamic Depth) 해결

class DynamicNet(nn.Module):
    def __init__(self, config_list):
        super().__init__()
        # 설정값에 따라 레이어 구성을 동적으로 변경
        self.linears = nn.ModuleList([nn.Linear(in_f, out_f) for in_f, out_f in config_list])

    def forward(self, x):
        for i, l in enumerate(self.linears):
            x = l(x)
        return x
    

Example 4: ModuleDict와 ModuleList의 혼합 해결

class MultiHeadNet(nn.Module):
    def __init__(self):
        super().__init__()
        # 여러 갈래의 출력을 가질 때 유용
        self.heads = nn.ModuleList([nn.Linear(50, 10) for _ in range(5)])

    def forward(self, x):
        return [head(x) for head in self.heads]
    

Example 5: .append()를 이용한 레이어 추가 해결

class IncrementalNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.layers = nn.ModuleList()
        self.layers.append(nn.Linear(10, 20))
        self.layers.append(nn.ReLU())
        # 추가하는 즉시 parameters()에 등록됨
    

Example 6: 특정 레이어만 얼리는(Freeze) 부분 학습 해결

model = RepeatNet(depth=5)
# ModuleList이므로 인덱싱을 통해 특정 레이어만 접근 가능
for param in model.layers[:3].parameters():
    param.requires_grad = False
    

Example 7: 가변 인자(*args)를 통한 컨테이너 확장 해결

def make_layers(layer_list):
    return nn.ModuleList(layer_list)

class CustomContainer(nn.Module):
    def __init__(self, *layers):
        super().__init__()
        self.modular_layers = make_layers(layers)
    

4. 시니어의 한 수: nn.Sequential과의 차이점 유의

실무에서 nn.ModuleList를 사용할 때 흔히 하는 실수는 이것이 nn.Sequential처럼 입력값을 넣으면 자동으로 실행될 것이라 믿는 것입니다. nn.ModuleList는 단순히 '저장소'일 뿐이며, forward 메서드 내에서 반드시 명시적인 반복문(for loop)을 통해 연산 순서를 정의해 주어야 합니다. 순서가 고정된 단순 스택이라면 nn.Sequential을, 조건부 연산이나 복잡한 인덱싱이 필요하다면 nn.ModuleList를 선택하는 것이 아키텍처 설계의 정석입니다.


5. 결론 및 요약

파이토치 모델 내에서 레이어 집합을 다룰 때 일반 파이썬 리스트는 금기입니다.

  • nn.ModuleList: 레이어를 파이토치 관리 시스템에 등록하여 학습과 장치 이동을 보장한다.
  • 일반 List: 레이어를 단순히 묶어둘 뿐, 파이토치 엔진과의 연결고리가 없다.
  • 해결책: 반복되는 레이어나 동적 구조 설계 시에는 반드시 nn.ModuleList를 사용하고 forward에서 루프로 실행하라.

참조 및 출처 (Sources)

  • PyTorch Official Docs: torch.nn.ModuleList.
  • PyTorch Forums: Difference between nn.ModuleList and python list
  • Deep Learning with PyTorch (Eli Stevens): Building Neural Networks with Modules
728x90