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

[PYTORCH] 커스텀 레이어(Custom Layer)를 정의하는 3가지 방법과 성능 최적화 해결 가이드

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

커스텀 레이어(Custom Layer)
커스텀 레이어 (Custom Layer)

 

 

딥러닝 모델 개발 과정에서 표준 라이브러리가 제공하는 nn.Linearnn.Conv2d만으로는 해결되지 않는 독창적인 아키텍처 설계가 필요할 때가 있습니다. PyTorch의 가장 큰 강점은 객체 지향 프로그래밍(OOP) 구조를 활용해 커스텀 레이어(Custom Layer)를 직관적이고 유연하게 정의할 수 있다는 점입니다. 본 가이드에서는 실무 개발자가 반드시 알아야 할 커스텀 레이어 정의 기법과 효율적인 텐서 연산 해결 방안을 심도 있게 다룹니다.


1. 커스텀 레이어 정의 시 고려해야 할 핵심 요소

단순히 클래스를 만드는 것을 넘어, 역전파(Backpropagation)가 자동으로 수행되는 Autograd 메커니즘을 이해하는 것이 중요합니다. 레이어 정의 시 가중치 초기화 전략과 메모리 효율성은 모델의 수렴 속도에 직접적인 차이를 만듭니다.

기본 구조 및 비교 분석

구분 nn.Module 상속 nn.Sequential 활용 torch.autograd.Function 확장
주요 목적 학습 가능한 파라미터가 포함된 복잡한 계층 정의 기존 레이어의 단순 직렬 연결 사용자 정의 미분 로직(Custom Backward) 구현
구현 난이도 중간 낮음 높음
유연성 매우 높음 제한적 최상 (수치적 안정성 직접 제어)
추천 상황 새로운 수식의 활성화 함수나 어텐션 모듈 설계 시 반복되는 블록의 단순 그룹화 시 비미분 연산에 대한 근사 미분이 필요할 때

2. 실무에 바로 적용 가능한 커스텀 레이어 실전 예제 7가지

다음은 실제 연구 및 서비스 배포 환경에서 성능을 개선하거나 특정 논문의 구조를 구현할 때 자주 쓰이는 7가지 코드 예제입니다.

Example 1: 학습 가능한 가중치가 있는 스케일링 레이어

입력 텐서에 학습 가능한 벡터를 곱하여 피처의 중요도를 스스로 조절하는 레이어입니다.


import torch
import torch.nn as nn

class LearnableScalarScaling(nn.Module):
    def __init__(self, num_features):
        super(LearnableScalarScaling, self).__init__()
        # 파라미터 정의: (1, num_features) 형태
        self.weights = nn.Parameter(torch.ones(1, num_features))

    def forward(self, x):
        # 브로드캐스팅을 활용한 요소별 곱셈
        return x * self.weights
    

Example 2: DropConnect 레이어 (가중치 노이즈 주입)

활성화 값이 아닌 가중치 자체에 드롭아웃을 적용하여 일반화 성능을 높이는 기법입니다.


class DropConnectLinear(nn.Module):
    def __init__(self, in_features, out_features, p=0.5):
        super().__init__()
        self.linear = nn.Linear(in_features, out_features)
        self.p = p

    def forward(self, x):
        if self.training:
            # 학습 시에만 가중치 마스킹 적용
            mask = torch.rand_like(self.linear.weight) > self.p
            sampled_weight = self.linear.weight * mask.float()
            return torch.nn.functional.linear(x, sampled_weight, self.linear.bias)
        return self.linear(x)
    

Example 3: 복합 활성화 함수 (Swish-Activation)

ReLU의 단점을 보완한 Sigmoid-Weighted Linear Unit을 직접 구현합니다.


class CustomSwish(nn.Module):
    def __init__(self, beta=1.0):
        super().__init__()
        self.beta = nn.Parameter(torch.tensor([beta]))

    def forward(self, x):
        # x * sigmoid(beta * x)
        return x * torch.sigmoid(self.beta * x)
    

Example 4: 텐서 채널 셔플(Channel Shuffle) 레이어

ShuffleNet 아키텍처에서 핵심이 되는 채널 간 정보 교류 레이어입니다.


class ChannelShuffle(nn.Module):
    def __init__(self, groups):
        super().__init__()
        self.groups = groups

    def forward(self, x):
        batch, channels, height, width = x.size()
        channels_per_group = channels // self.groups
        # 차원 재구성 후 셔플
        x = x.view(batch, self.groups, channels_per_group, height, width)
        x = torch.transpose(x, 1, 2).contiguous()
        return x.view(batch, channels, height, width)
    

Example 5: 데이터 정규화를 위한 커스텀 Running Stat 레이어


class SimpleGlobalNorm(nn.Module):
    def __init__(self, num_features, momentum=0.1):
        super().__init__()
        self.register_buffer('running_mean', torch.zeros(num_features))
        self.momentum = momentum

    def forward(self, x):
        if self.training:
            curr_mean = x.mean(dim=0)
            self.running_mean = (1 - self.momentum) * self.running_mean + self.momentum * curr_mean
        return x - self.running_mean
    

Example 6: Radial Basis Function (RBF) 레이어


class RBFLayer(nn.Module):
    def __init__(self, in_features, out_features):
        super().__init__()
        self.centers = nn.Parameter(torch.Tensor(out_features, in_features))
        nn.init.xavier_uniform_(self.centers)

    def forward(self, x):
        # 거리 계산 기반 활성화
        size = (x.size(0), self.centers.size(0), x.size(1))
        x = x.unsqueeze(1).expand(size)
        c = self.centers.unsqueeze(0).expand(size)
        return torch.exp(-torch.pow(x - c, 2).sum(2))
    

Example 7: 가중치 정규화(Weight Normalization) 직접 적용


class CustomWeightNorm(nn.Module):
    def __init__(self, module, name='weight'):
        super().__init__()
        self.module = module
        self.name = name
        # g (크기)와 v (방향) 파라미터 분리
        weight = getattr(self.module, name)
        self.g = nn.Parameter(torch.norm(weight, dim=0, keepdim=True))
        self.v = nn.Parameter(weight / self.g)

    def forward(self, x):
        # 런타임에 가중치 합성
        setattr(self.module, self.name, self.v * self.g)
        return self.module(x)
    

3. 자주 발생하는 문제와 기술적 해결 방법

커스텀 레이어를 정의할 때 초보 개발자가 가장 흔히 저지르는 실수는 메모리 비효율성미분 단절입니다.

  • 해결 1: In-place 연산 자제: x += y와 같은 연산은 Autograd가 역전파를 수행할 때 필요한 이전 상태 값을 덮어써 오류를 발생시킬 수 있습니다. x = x + y 형식을 권장합니다.
  • 해결 2: register_buffer 활용: 학습되지 않지만 모델의 상태로 저장되어야 하는 변수(예: BatchNorm의 moving average)는 nn.Parameter가 아닌 register_buffer를 사용해야 GPU 전송 시 함께 누락되지 않습니다.
  • 해결 3: 텐서 연속성(Contiguous): view() 연산 전 transpose를 사용했다면 반드시 .contiguous()를 호출하여 메모리 레이아웃 불일치 문제를 해결해야 합니다.

4. 결론: PyTorch의 유연성을 극대화하는 법

커스텀 레이어 정의는 단순히 새로운 기능을 추가하는 것이 아니라, 모델의 병목 지점을 해결하고 도메인 특화 지식을 인공신경망에 주입하는 과정입니다. 위 7가지 사례와 최적화 팁을 활용하여 더 강력하고 독창적인 딥러닝 모델을 구축해 보시기 바랍니다.


내용 출처 및 참고 문헌

  • PyTorch Documentation: Creating Custom Modules
  • Paszke, A., et al. (2019). "PyTorch: An Imperative Style, High-Performance Deep Learning Library".
  • GitHub: Official PyTorch Examples Repository
728x90