스프린트미션

규칙2

goodella 2026. 2. 12. 15:33

 

 

 

Instance Variable & Method Visibility Rule

1.설계 의도:

  • 파이토치의 state_dict 직렬화 메커니즘을 고려하여 변수 언더스코어 규칙을 설계함. 학습되어야 할 레이어와 파라미터는 _ 없이 공개하여 공식 Key로 등록하고, 외부에서 사용할 수 있게 함.
  • 모델의 구조와 무관한 정적 설정값, 외부에서 함부로 건드리면 안 되는 내부 로직에는 _를 붙여 격리하여 모델 저장 파일의 가독성을 확보하고 오용을 방지하고자 함

2. 빠른 판단 가이드

  • 인스턴스 변수
# 질문: "학습(gradient)으로 업데이트되는가?"
self.conv1 = nn.Conv2d(...)           # YES → Public
self.param = nn.Parameter(...)        # YES → Public
self._dropout_rate = 0.5              # NO → Private
self._optimizer = optimizer           # NO → Private

 

 

  •  메서드 
# 질문: "외부(main.py, DataLoader 등)에서 직접 호출하는가?"
def train(self):                      # YES → Public
def forward(self, x):                 # YES → Public
def __getitem__(self, idx):           # YES → Public (표준 API)

def _run_epoch(self):                 # NO → Internal
def _load_from_disk(self, idx):       # NO → Internal
def _init_weights(self):              # NO → Internal

 

 

3. 상세 내용 
(1)인스턴스 변수 규칙
기본 원칙: Private (언더스코어 O)

-대상: 내부 구현 상세, 설정값, 플래그
-의미: "외부에서 직접 접근 금지, 나중에 구현이 바뀔 수 있음"
-클래스: Trainer, Dataset, nn.Module(State), 유틸리티 클래스 등
-예시: self._optimizer, self._best_loss, self._checkpoint_dir

 

class Trainer:
    def __init__(self, model, optimizer):
        # [Public] 외부에서 접근/수정 가능한 안정적인 인터페이스
        self.model = model              
        self.current_epoch = 0          
        
        # [Private] 내부 구현 상세 (외부 접근 비권장, 언제든 로직 변경 가능)
        self._optimizer = optimizer     # 예: 나중에 다른 옵티마이저로 교체될 수 있음
        self._best_loss = float('inf')  # 내부 추적용 변수
        self._checkpoint_dir = "/tmp"   # 저장 로직이 S3로 바뀔 수도 있음

 

 

예외: 

nn.Module의 경우 Public (언더스코어 X)
-대상: 학습 가능한 레이어와 파라미터
-이유: state_dict() 저장 시 공개 API 키가 되기 때문
-주의: 설정값(정적 파라미터)는 여전히 _ 사용

 

예외 1: 학습 가능한 레이어

class VisionTransformer(nn.Module):
    def __init__(self, *, input_dim: int, output_dim: int) -> None:
        super().__init__()
        
        # ✅ Public: 학습 가능한 레이어
        # 이유: state_dict에 'conv1.weight', 'conv1.bias'로 저장됨
        self.conv1 = nn.Conv2d(3, 64, kernel_size=3)
        self.fc = nn.Linear(input_dim, output_dim)
        self.lstm = nn.LSTM(128, 256)
        
        # ❌ 잘못된 예
        # self._conv1 = nn.Conv2d(3, 64, kernel_size=3)
        # → state_dict에 '_conv1.weight'로 저장되어 재사용 어려움!
 

 

예외 2: 학습 가능한 파라미터

class VisionTransformer(nn.Module):
    def __init__(self):
        super().__init__()
        
        # ✅ Public: 레이어에 속하지 않는 독립적인 학습 파라미터
        # 이유: optimizer가 업데이트하는 대상
        self.pos_embedding = nn.Parameter(torch.zeros(1, 196, 768))
        self.class_token = nn.Parameter(torch.randn(1, 1, 768))
        
        # ❌ 잘못된 예
        # self._pos_embedding = nn.Parameter(torch.zeros(1, 196, 768))
        # → state_dict에 '_pos_embedding'으로 저장됨
 

Private: 정적 하이퍼파라미터

class VisionTransformer(nn.Module):
    def __init__(self):
        super().__init__()
        
        # 레이어 (Public)
        self.conv1 = nn.Conv2d(3, 64, kernel_size=3)
        
        # 🔒 Private: 학습되지 않는 설정값
        # 이유: state_dict에 포함 불필요, 내부 로직용
        self._dropout_rate = 0.5           # 정적 하이퍼파라미터
        self._is_initialized = False       # 내부 로직 제어용 플래그
        self._debug_mode = True            # 디버깅 설정
        self._num_layers = 12              # 모델 구조 정보
 
 
 

 


2.메서드 규칙

Public 메서드

-규칙: 접두사 없이 작성
-대상: 외부에서 직접 호출하는 진입점
-의미: 안정적인 인터페이스, 외부에서 호출 가능

Internal 메서드

-규칙: _ 접두사 사용
-대상: Public 메서드를 돕는 헬퍼 함수(Helper)나 내부 구현 상세(Refactoring 대상).
-의미: "내부용, 외부 호출 금지, 나중에 로직 변경 가능"

# [Example] 메서드 가시성 적용 가이드

class Trainer:
    def __init__(self, model):
        self.model = model
    
    # [Public] 외부(main.py 등)에서 "학습 시작!"을 명령할 때 호출하는 진입점
    def train(self):
        for epoch in range(10):
            self._run_epoch()  # 내부 로직 호출
    
    # [Internal] 학습 한 바퀴를 돌리는 구체적인 방법 (구현 상세)
    # 굳이 외부에서 이 메서드만 따로 호출할 일이 없으므로 숨김
    def _run_epoch(self):
        pass

    # [Internal] 가중치 초기화는 학습 시작 전 내부적으로 한 번만 수행
    # 외부에서 임의로 호출하여 학습된 모델을 초기화하는 사고를 방지하기 위해 숨김
    def _init_weights(self):
        pass


class CustomDataset(Dataset):
    # [Public] PyTorch DataLoader가 "데이터 몇 개야?"라고 물어볼 때 쓰는 공개 API
    def __len__(self):
        return 100
    
    # [Public] DataLoader가 "i번째 데이터 줘"라고 할 때 쓰는 공개 API
    # 외부 사용자는 이 내부가 어떻게 변하는지 전혀 신경 쓸 필요 없음 
    def __getitem__(self, idx):
        return self._load_from_disk(idx)
    
    # [Internal] 실제로 하드디스크에서 파일을 읽어오는 구체적인 방식
    # 나중에 AWS(S3)로 로직을 바꿔도 외부(__getitem__)는 알 필요 없음
    def _load_from_disk(self, idx):
        """데이터 로딩 구현 상세"""
        pass


class VisionTransformer(nn.Module):
    # [Public] PyTorch가 추론 시 호출하는 표준 메서드
    def forward(self, x):
        if self._debug_mode:
           self._log_shapes(x)
        return x

    # [Internal] 개발자용 디버깅 도구
    # 실제 서비스 배포 시에는 필요 없거나 바뀔 수 있는 기능이므로 숨김
    def _log_shapes(self, x):
        print(f"Shape: {x.shape}")
 

 

'스프린트미션' 카테고리의 다른 글

위클리페이퍼 7  (0) 2026.03.03
위클리 페이퍼 #6  (0) 2026.02.22
규칙3  (0) 2026.02.12
규칙1  (0) 2026.02.12
[AI] 위클리페이퍼 #5  (0) 2026.02.09