__init__ 메소드 공부

thon·2024년 7월 24일

이전에 매직 메소드에 대해서 공부했다. (매직메소드)

매직 메소드 중에서도 가장 많이 접하는 __init__ 메소드를 좀 더 정확히 이해해 두면 좋을 것 같아서 추가적인 공부를 했다.


__init__ 메소드

객체를 초기화 할 때 사용된다.
초기화는 객체의 초기 상태를 설정하는 역할을 말한다.

예를 들어, MyClass라는 클래스를 정의하고,
a = MyClass()를 통해
a를 MyClass 객체에 해당하는 인스턴스로 만들었을 때,
a 객체의 초기 상태가 __init__ 메소드 설정대로 설정되는 것이다.


__init__ 메소드의 역할

__init__ 메소드에는 여러 역할이 있다.

  1. 객체 초기화
  • 객체가 생성될 때, __init__ 메소드가 자동으로 호출된다.
  • __init__ 메소드 내부의 초기 상태 설정대로 객체의 초기 상태를 설정한다.
  1. 파라미터 정의
  • 객체 생성에 필요한 파라미터를 정의한다.
  • 파라미터들은 객체의 속성(attributes)로 설정된다.
    ※ 객체의 파라미터를 입력하지 않으면, TypeError가 뜰 수 있다.
  • 그래서 객체 파라미터의 초기값을 __init__ 함수 설정할 때 입력해 주기도 한다.
  1. 속성 초기화
  • 메소드 내부에서 객체의 속성을 초기화 할 수 있다.

    class MyClass:
        def __init__(self, value1, value2):
            self.value1 = value1
            self.value2 = value2
    
    # 객체 생성
    obj = MyClass(10, 20)
    
    # 객체의 속성에 접근
    print(obj.value1)  # 출력: 10
    print(obj.value2)  # 출력: 20

    위 코드를 보면, self.value1, self.value2는 객체의 속성이고 이 속성을 __init__의 parameter 값(value1, value2)으로 초기화해준다.

  • 객체의 속성에 대한 접근이 가능하도록 만든다.

초기 설정에 매우 중요한 역할을 한다.


__init__ 메소드의 특징

  1. 자동 호출
    객체가 생성될 때 자동으로 호출됨

  2. 객체 속성의 설정
    self를 사용하여 객체의 속성을 설정할 수 있음

  3. 초기화 논리 포함

  • 객체 초기화와 관련된 추가 로직을 메소드에 포함할 수 있음

  • 예를 들어, 클래스를 정의하면서 초기화에 복잡한 로직이 필요하여 이를 함수로 class 안에 정의해 두는 경우가 있다.
    이 로직을 __init__ 메소드 안에서 속성의 값으로 불러와 저장할 수 있다.

    class DatabaseConnection:
        def __init__(self, host, port, username, password):
            self.host = host
            self.port = port
            self.username = username
            self.password = password
            self.connection = self.connect_to_database() # 아래에 생성한 함수가 초기화에 포함됨
    
        def connect_to_database(self):
            # 데이터베이스에 연결하는 논리 (예시)
            print(f'Connecting to database at {self.host}:{self.port} as {self.username}')
            # 실제 연결 코드는 여기에 포함됩니다
            return f'Connection to {self.host}:{self.port}'
    
    # 객체 생성
    db_conn = DatabaseConnection('localhost', 5432, 'admin', 'password')
    
    # 객체의 속성에 접근
    print(db_conn.connection)  # 출력: Connection to localhost:5432
    

이러한 __init__ 메소드의 역할과 특징에 따라서, 우리가 PyTorch 등에서 신경망 레이어를 쌓을 때,
초기 속성으로 레이어를 저장하고 (self.conv1 = nn.Conv2d(...))
이를 나중에 forward() 함수 안에 정의하면서 x = self.conv1(x) 이런 식으로 레이어 속성을 불러와 적용하도록 하는 것이다.


번외

문득, forward 함수에서 x = self.conv1(x)으로 합성곱 신경망을 적용하는지 궁금해졌다.

왜냐면, nn 모듈의 Conv2d가 입력받는 인자는 (in_channels, out_channels, kernel_size, ...) 등등이고, 입력받는 인자에 data는 없기 때문에 어떻게 x라는 데이터를 input으로 받는 건가 궁금했다.

그리고 그 답은 매직 메소드에 있었다.


우리가 신경망 레이어를 짤 때 PyTorch에서 torch.nn 모듈을 불러와서 이 안에 있는 함수를 보통 사용한다.

그리고 이 레이어를 짜기 이전에 우리는 class를 정의하면서 nn.Module을 상속받는다.

nn.Module을 상속받으면서 nn.Module 클래스에서 정의한 __call__ 메소드를 같이 상속받게 된다.

__call__ 메소드의 역할은 바로 객체를 함수처럼 호출하는 것이고, nn.Module에서는 내부적으로 forward() 메소드를 호출하도록 되어 있다.


nn.Module의 __call__ 메소드 내부 동작 원리
PyTorch의 nn.Module 클래스의 __call__ 메소드는 다음과 같은 단계를 수행한다.
  1. 후크(hook) 호출: 필요한 경우 후크를 호출하여 입력 데이터를 조작하거나 처리
    (후크는 신경망의 특정 지점에서 실행되는 사용자 정의 함수를 등록하여, 중간 결과를 가로채거나 변형할 수 있는 매커니즘)
    디버깅, 시각화, 중간 결과 저장, 특성 추출 등에도 사용됨
    gpt의 설명

  2. forward 메소드 호출: call 메소드는 내부적으로 forward 메소드를 호출

  3. 결과 반환: forward 메소드의 결과를 반환합니다. (return 값이 계산된 가중치 행렬 값이겠지)

이를 통해 PyTorch의 모든 레이어는 함수처럼 호출될 수 있으며, 입력 데이터를 전달받아 연산을 수행할 수 있다.


그리고 nn.Conv2d 등은 nn.Module의 하위 클래스이므로 nn.Conv2d도 객체를 함수처럼 호출할 수 있게 된다.

이에 따라서, 달리 forward 함수 안에서 달리 input data를 받아올 것 같지 않지만, 사실 받아올 수 있는 것이다.

예를 들어서 이 과정의 원리를 설명해보자.

import torch
import torch.nn as nn
import torch.nn.functional as F

# myCNN 정의
class myCNN(nn.Module):
    def __init__(self):
        super(myCNN, self).__init__()
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=20, kernel_size=5)
        self.conv2 = nn.Conv2d(in_channels=20, out_channels=20, kernel_size=5)

    def forward(self, x): # forward는 x를 입력으로 받는다
        x = F.relu(self.conv1(x))
        x = F.relu(self.conv2(x))
        return x

# 모델 인스턴스 생성
model = myCNN()

# 입력 데이터 생성
input_data = torch.randn(1, 1, 28, 28)

# 모델에 입력 데이터를 전달하여 순전파 수행
output = model(input_data) #  
  1. myCNN이라는 클래스를 생성했다고 하자.
  2. model = myCNN()를 통해 model을 myCNN 클래스 객체인 인스턴스로 만들게 된다.
  3. 순전파를 수행하기 위해 output = model(input_data)를 수행한다.
  4. myCNN 객체는 nn.Module을 상속받았기 때문에 __call__ 메소드를 호출한다.
    call 메소드 덕분에 myCNN 객체인 model은 이제 model(input_data)처럼 함수 형태로 사용할 수 있게 된다.
  5. __call__ 메소드는 내부적으로 forward() 함수를 호출한다.
  6. __call__ 메소드에 정의된 바에 따라 forward(input_data)를 수행하게 된다.
  7. forward() 메소드 내부의 동작에 정의된 대로 순차적인 레이어를 적용하게 된다.
    이는, 실제 연산을 수행하는 과정이 된다.

요약

간단히 생각하면, __call__ 매직 메소드를 써서,
신경망의 초기 속성으로 정의된 각 레이어를 함수처럼 사용할 수 있게 되는 것이다.

상당히 많은 공부가 되었다.

0개의 댓글