부스트캠프 2주차 #2 - PyTorch 구조 ①
1. 서
2주차 #1 정리에서 파이토치의 기본적인 개념들을 정리하였다. 이번에는 기본 개념을 토대로 하여 파이토치 프로젝트 구조가 어떻게 되는지 이해하는 시간을 갖는다. 이번에는 관련한 내용이 많기 때문에 #2와 #3 두번에 걸쳐 정리해 나갈 계획이다. 파이토치 프로젝트 구조를 정리하는 과정에서는 디테일하고 구체적인 부분보다는 높은 시야에서 전체적인 흐름을 이해하는 것에 중점을 두어야 할 것이다. 전체적인 흐름을 놓치지 않는 것에 유의하면서 두번째 정리를 시작해본다.
2. 학습내용 리뷰
(1) PyTorch Project Template
파이토치를 사용한 프로젝트 템플릿은 많이 존재하는데, 이러한 템플릿은 필요에 따라 수정하여 사용하게 된다. 일반적으로 템플릿에는 분리된 다양한 모듈들(실행, 데이터, 모델, 설정, 로깅, 지표, 유틸리티 등)이 있고, 이들이 모여 프로젝트의 기본구조를 이루고 있다. 강의에서는 아래 링크의 repository를 추천 템플릿으로 소개하고 있으므로, 해당 템플릿의 구조도를 보면서 전체적인 템플릿의 구조를 파악하도록 하자.
GitHub - victoresque/pytorch-template: PyTorch deep learning projects made easy.
PyTorch deep learning projects made easy. Contribute to victoresque/pytorch-template development by creating an account on GitHub.
github.com
(2) torch.nn 모듈
1) 구성 요소
torch.nn은 PyTorch에서 신경망을 구축하기 위한 모듈로, 신경망을 구성하는 데 필요한 다양한 레이어(layer), 활성화 함수(activation function), 손실 함수(loss function) 등을 제공한다. torch.nn으로 복잡한 신경망 구조를 비교적 쉽게 구현할 수 있어 사용자가 커스텀 레이어를 만드는데 사용된다.
- 레이어 : 신경망의 기본 구성 요소로 nn.Linear, nn.Conv2d, nn.LSTM 등 다양한 형태의 레이어가 제공된다.
- 활성화 함수 : 신경망의 비선형성을 도입하는 함수로, 레이어의 출력에 적용되어 다음 레이어의 입력으로 전달된다. nn.ReLU, nn.Sigmoid, nn.Tanh 등이 있다.
- 손실 함수 : 모델의 예측값과 실제값 사이의 차이를 계산하는 함수로 최적화 과정에서 사용된다. nn.MSELoss, nn.CrossEntropyLoss 등이 있다.
- 컨테이너 : 여러 레이어를 그룹화하고, 모듈화하여 관리하기 위한 클래스이다. nn.Module은 모든 신경망 모듈의 기본 클래스이고, nn.Sequential은 여러 모듈들을 순서대로 담아 순차적으로 실행할 수 있게 한다. nn.ModuleList은 Module 객체들의 리스트와 유사하여 모듈들을 모아두고 원하는 것을 인덱싱을 통해 쓸 수 있게 한다. nn.ModuleDict은 딕셔너리와 유사하게 key, value 쌍으로 모듈을 저장하고 이로써 이름을 통해 각 모듈에 접근 할 수 있다.
import torch.nn as nn
// Sequential 예시
model = nn.Sequential(
nn.Conv2d(1, 20, 5),
nn.ReLU(),
nn.Conv2d(20, 64, 5),
nn.ReLU()
)
// ModuleList 예시
class MyModel(nn.Module):
def __init__(self):
super(MyModel, self).__init__()
self.layers = nn.ModuleList([nn.Linear(10, 10) for _ in range(5)])
def forward(self, x):
for layer in self.layers:
x = layer(x)
return x
// ModuleDict 예시
class MyModel(nn.Module):
def __init__(self):
super(MyModel, self).__init__()
self.layers = nn.ModuleDict({
'linear': nn.Linear(10, 10),
'act': nn.ReLU(),
})
def forward(self, x):
x = self.layers['linear'](x)
x = self.layers['act'](x)
return x
2) 사용예시 - 간단한 신경망 구조
컨볼루션 레이어, 풀링 레이어, 완전 연결 레이어를 사용한 간단한 신경망 구조를 정의하는 예시이다.
import torch
import torch.nn as nn
import torch.nn.functional as F
class SimpleNet(nn.Module):
def __init__(self):
super(SimpleNet, self).__init__()
self.conv1 = nn.Conv2d(1, 20, 5)
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(20, 50, 5)
self.fc1 = nn.Linear(50 * 4 * 4, 500)
self.fc2 = nn.Linear(500, 10)
def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = x.view(-1, 50 * 4 * 4)
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
(3) nn.Module 클래스
1) 신경망 모델의 기본단위
nn.Module은 PyTorch에서 신경망 모델을 구성하는 기본 단위이다. 다시말하면, PyTorch의 모든 신경망 모듈은 nn.Module 클래스를 상속받아서 구현된다. 딥러닝 모델에서 최소 기능 단위가 function, function들로 이루어진 것이 layer, layer로 이루어진 것이 model이고, 모든 신경망 레이어들과 함수들은 nn.Module을 상속받아 구현된다. 따라서 모델 구조의 이해는 Module과 Module이 연결되어 만들어내는 '흐름'을 인식하는 것이 중요하다.
nn.Module은 딥러닝을 구성하는 Layer의 base class로, input과 output, forward와 backward때 각각 일어나는 일에 대해 정의하고 있고, 학습의 대상이 되는 parameter(tensor)를 정의하고 있다.
2) 제공하는 기능
- 모델의 파라미터 관리 : nn.Module을 상속받은 클래스는 내부적으로 모델 파라미터를 저장, 업데이트, 조회할 수 있는 메커니즘을 제공한다. 예를들어, .parameters() 메서드를 통해 모델의 모든 학습가능한 파라미터에 접근할 수 있다.
- 또한 PyTorch는 Parameter로 지정된 tensor의 경우 역전파에서 grad 값을 계산하여 값을 업데이트 해주고 모델을 저장할때 값을 저장해준다.
- 구체적으로는 nn.Parameter는 Tensor 객체의 상속 객체로, nn.Module 내에 attribute가 될 때는 required_grad = True로 지정되어 학습대상이 되는 Tensor가 된다.
- 다만 대부분 torch.nn에 구현된 layer들을 가져다 사용하기 때문에 Parameter를 직접 다루는 것은 드물다고 한다.
- 참고로 Parameter로 지정되지 않아 값이 업데이트 되지 않더라도 저장하고 싶은 tensor의 경우 buffer에 등록해주면 된다.
- 모듈화 : 신경망을 구성하는 여러 레이어와 함수를 nn.Module의 서브클래스로 정의하여 코드의 재사용성과 가독성을 높이고, 복잡한 신경망을 체계적으로 구성할 수 있게 한다.
- 자동미분 : nn.Module 내에 정의된 연산들은 자동으로 grad를 계산할 수 있게 된다. 따라서 역전파를 통한 파라미터 업데이트를 간단하게 만들어 준다.
- 모델 저장 및 로드 : nn.Module 기반의 모델은 전체 모델을 하나의 파일로 쉽게 불러올 수 있게 한다.
- Hook : PyTorch에서 모델의 forward 및 backward pass 과정에 사용자 정의 동작을 삽입할 수 있게 해주는 기능이다. 주로 register_forward_hook과 register_backward_hook함수를 이용하여 hook을 등록하고 이를 통해 forward pass와 backward pass 시점에 호출될 함수를 지정할 수 있다. 이 hook함수에서 데이터의 흐름을 확인하거나 조작할 수 있다.
- Apply : 모듈의 모든 하위 모듈에 대해 재귀적으로 특정 함수를 적용하고 싶을 때 사용된다. 모델의 모든 파라미터나 모듈에 일괄적으로 연산을 적용해야 할 때 유용한 기능으로, 모델의 모든 가중치 초기화, 동일한 설정 적용 등의 작업에 사용할 수 있다.
3) 사용 예시 - 사용자 정의 모델
import torch.nn as nn
import torch.nn.functional as F
class CustomModel(nn.Module):
def __init__(self):
super(CustomModel, self).__init__()
self.conv1 = nn.Conv2d(1, 20, 5)
self.conv2 = nn.Conv2d(20, 50, 5)
self.fc1 = nn.Linear(50*4*4, 500)
self.fc2 = nn.Linear(500, 10)
def forward(self, x):
x = F.relu(self.conv1(x))
x = F.max_pool2d(x, 2, 2)
x = F.relu(self.conv2(x))
x = F.max_pool2d(x, 2, 2)
x = x.view(-1, 50*4*4)
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
3. 느낀점
정리를 하다보면 정리의 경계를 명확히 하는 것이 가장 어려운 것 같다. 그래도 벌써부터 1주차의 정리를 요긴하게 쓰고 있는걸 보면, 지금 당장 정리가 힘들어도 곧 보람을 느끼지 않을까 싶다. 파이토치는 이 공부를 시작하면서 겪은 첫 고비인 것 같다. 그래도 조만간 파이토치를 자유자재로 다루는 나의 모습을 상상하며 끈기있게 공부해보려 한다.