[파이토치로 만드는 딥러닝 이론1] nn.Module & nn.Parameter & Backward

2024. 5. 31. 16:45MOOC

 

  • 기본 구조:
    • 모듈(Module): 모든 딥러닝 레이어(층)의 기본 단위다. 이 모듈을 사용해서 데이터가 어떻게 처리되고, 모델이 어떻게 학습할지 정의한다.
    • 파라미터(Parameter): 모델이 학습하는 변수들이다. 예를 들어, 데이터의 특성을 기반으로 예측을 할 때 조정되어야 하는 가중치(weight)나 편향(bias) 같은 값들이다.

  • 자동 미분(AutoGrad)과 최적화(Optimizer):
    • AutoGrad: 모델을 학습할 때 필요한 미분 계산을 자동으로 해주는 도구다. 모델이 어떻게 성능을 개선할지를 계산해준다.
    • Optimizer: 계산된 미분 값을 사용하여 모델의 파라미터(가중치, 편향)를 업데이트하고, 이를 통해 모델이 점점 더 좋은 예측을 하도록 돕는다.

실제 코드 예제:

  • MyLiner 클래스: 자신만의 선형 레이어를 만드는 예제로, 입력 데이터와 출력 결과 사이의 관계를 학습한다.
  • LR 클래스: 로지스틱 회귀 모델을 직접 만들고, 이 모델을 통해 예측과 실제 값 사이의 차이를 줄이는 방법을 구현한다. 여기서는 가중치와 편향을 직접 업데이트하는 과정도 포함된다.
class MyLiner(nn.Module):
    def __init__(self, in_features, out_features, bias=True):
        super().__init__()
        self.in_features = in_features  # 입력 특성의 개수
        self.out_features = out_features  # 출력 특성의 개수
        self.weights = nn.Parameter(torch.randn(in_features, out_features))  # 가중치 초기화
        self.bias = nn.Parameter(torch.randn(out_features))  # 편향 초기화

    def forward(self, x: Tensor):
        return x @ self.weights + self.bias  # 입력과 가중치의 행렬 곱 및 편향 추가

 

 

  • __init__ 메소드에서는 입력 및 출력 특성의 수를 정의하고, 가중치와 편향을 nn.Parameter로 초기화하여 학습 가능한 파라미터로 설정한다.
  • forward 메소드에서는 입력 x와 가중치의 행렬 곱을 수행하고 편향을 더해 결과를 반환한다.

class LR(nn.Module):
    def __init__(self, dim, lr=torch.scalar_tensor(0.01)):
        super(LR, self).__init__()
        self.w = torch.zeros(dim, 1, dtype=torch.float).to(device)  # 가중치를 0으로 초기화
        self.b = torch.scalar_tensor(0).to(device)  # 편향을 0으로 초기화
        self.grads = {"dw": torch.zeros(dim, 1, dtype=torch.float).to(device),
                      "db": torch.scalar_tensor(0).to(device)}  # 그래디언트 초기화
        self.lr = lr.to(device)  # 학습률

    def forward(self, x):
        z = torch.mm(self.w.T, x)  # 가중치와 입력의 행렬 곱
        a = self.sigmoid(z)  # 시그모이드 함수 적용
        return a

    def sigmoid(self, z):
        return 1 / (1 + torch.exp(-z))  # 시그모이드 활성화 함수

    def backward(self, x, yhat, y):
        self.grads["dw"] = (1 / x.shape[1]) * torch.mm(x, (yhat - y).T)  # 가중치에 대한 그래디언트 계산
        self.grads["db"] = (1 / x.shape[1]) * torch.sum(yhat - y)  # 편향에 대한 그래디언트 계산

    def optimize(self):
        self.w = self.w - self.lr * self.grads["dw"]  # 가중치 업데이트
        self.b = self.b - self.lr * self.grads["db"]  # 편향 업데이트

 

 

  • __init__ 메소드에서는 모델의 파라미터(가중치와 편향)를 초기화하고, 그래디언트와 학습률도 설정한다.
  • forward 메소드에서는 입력에 대한 모델의 예측을 계산한다.
  • backward 메소드에서는 예측과 실제 값 사이의 오차로부터 그래디언트를 계산한다.
  • optimize 메소드에서는 계산된 그래디언트를 사용하여 모델의 파라미터를 업데이트한다.

Backward의 과정

  1. Forward Pass:
    • 먼저, 모델은 입력 데이터를 받아 순전파를 통해 출력값을 계산한다.
    • 예측치와 실제 데이터 간의 손실(오차)가 계산된다.
  2. Loss 계산:
    • 손실 함수(loss function)를 사용하여 예측한 결과값과 실제 결과값 사이의 차이를 수치적으로 나타낸다. 이 손실을 최소화하는 것이 학습의 목표다.
  3. Backward Pass:
    • 계산된 손실 함수의 값에서 출발하여, 파라미터에 대한 손실 함수의 미분값(그래디언트)을 계산한다.
    • 이 계산은 연쇄법칙을 사용하는 자동 미분(autograd) 기능을 통해 수행된다.
    • 각 레이어와 각 파라미터에 대한 그래디언트가 계산되어 저장된다.
  4. 파라미터 업데이트:
    • Optimizer(최적화 알고리즘)를 사용하여 저장된 그래디언트를 바탕으로 모델의 파라미터를 업데이트한다. 대표적으로 SGD(확률적 경사 하강법)나 Adam 같은 알고리즘이 사용된다.
    • optimizer.step() 메소드를 호출하여 각 파라미터를 조정한다.
    • 다음 반복(iteration) 또는 에폭(epoch)에 사용하기 전에 optimizer.zero_grad() 메소드를 호출하여 이전 그래디언트를 초기화한다.

예시코드

for epoch in range(epochs):
    optimizer.zero_grad()  # 그래디언트 버퍼 초기화
    outputs = model(inputs)  # 모델을 통해 입력에서 출력을 계산
    loss = criterion(outputs, labels)  # 손실 계산
    loss.backward()  # 역전파 실행, 각 파라미터에 대한 손실의 그래디언트 계산
    optimizer.step()  # 파라미터 업데이트

PyTorch Dataset 이론

1. Dataset 클래스

Dataset 클래스는 데이터의 샘플과 레이블을 저장하고 관리하는 역할을 한다. PyTorch에서 Dataset을 사용하려면, 기본적으로 torch.utils.data.Dataset을 상속받은 사용자 정의 클래스를 만들어야 한다. 이 클래스는 주로 다음 세 가지 메소드를 오버라이드(재정의)한다:

import torch
from torch.utils.data import Dataset

class CustomDataset(Dataset):
    def __init__(self, text, labels):
        self.labels = labels
        self.data = text

    def __len__(self):
        return len(self.labels)

    def __getitem__(self, idx):
        label = self.labels[idx]
        text = self.data[idx]
        sample = {"Text": text, "Class": label}
        return sample

 

  • __init__: 데이터셋의 초기화를 담당한다. 데이터와 레이블을 로드하고 다른 필요한 초기 설정을 수행한다.
  • __len__: 데이터셋의 총 샘플 수를 반환한다. 이는 데이터셋의 길이를 알려주는 함수로 사용된다.
  • __getitem__: 인덱스(idx)에 해당하는 샘플을 데이터셋에서 불러와 반환한다. 이 메소드는 데이터셋에서 특정한 하나의 샘플을 가져오는 방법을 정의한다.

 

2. DataLoader 클래스

DataLoader는 Dataset을 입력으로 받아 이를 배치(batch) 단위로 처리하는 역할을 한다. 이 클래스는 배치 처리, 데이터 셔플링(shuffling), 다중 스레드를 이용한 데이터 로딩 등의 기능을 지원한다. 주요 매개변수는 다음과 같다:

 

from torch.utils.data import DataLoader

text = ['Happy', 'Amazing', 'Sad', 'Unhappy', 'Glum']
labels = ['Positive', 'Positive', 'Negative', 'Negative', 'Negative']

# Dataset 생성
myDataset = CustomDataset(text, labels)

# DataLoader 설정
myDataLoader = DataLoader(myDataset, batch_size=2, shuffle=True)

# DataLoader를 통해 데이터를 반복하여 출력
for dataset in myDataLoader:
    print(dataset)

 

 

  • batch_size: 한 번에 로드할 데이터 샘플의 수다.
  • shuffle: 매 에포크(epoch)마다 데이터를 섞을지의 여부를 결정한다.
  • num_workers: 데이터 로딩에 사용할 하위 프로세스의 수다.

 

출력

{'Text': ['Glum', 'Sad'], 'Class': ['Negative', 'Negative']}
{'Text': ['Unhappy', 'Amazing'], 'Class': ['Negative', 'Positive']}
{'Text': ['Happy'], 'Class': ['Positive']}