모듈을 만드는 법¶
파이토치 (라이트닝 포함) 신경망의 뼈대라고 할 수 있는 모듈을 만드는 방법은 여러가지가 있는데, 그중에서 대표적인 몇가지 정리하면
import pytorch_lightning as pl
from torchinfo import summary
Lighting 의 모듈 컨테이너를 사용하는 방법¶
가장 기본적으로 사용되는 방법으로 init 에서 필요한 layer들을 정의하고, 그 layer 들이 어떻게 서로 연결되는지를 forward 에서 정의하는 방법
import torch.nn as nn
import torch.nn.functional as F
class Model( pl.LightningModule ):
def __init__(self):
super().__init__()
self.layer1 = nn.Linear(5, 10)
self.layer2 = nn.Linear(10, 1)
def forward(self, x):
x = F.relu(self.layer1(x))
x = F.relu(self.layer2(x))
return x
model = Model()
summary(model, input_size = (1, 5))
==========================================================================================
Layer (type:depth-idx) Output Shape Param #
==========================================================================================
Model [1, 1] --
├─Linear: 1-1 [1, 10] 60
├─Linear: 1-2 [1, 1] 11
==========================================================================================
Total params: 71
Trainable params: 71
Non-trainable params: 0
Total mult-adds (Units.MEGABYTES): 0.00
==========================================================================================
Input size (MB): 0.00
Forward/backward pass size (MB): 0.00
Params size (MB): 0.00
Estimated Total Size (MB): 0.00
==========================================================================================
model
Model(
(layer1): Linear(in_features=5, out_features=10, bias=True)
(layer2): Linear(in_features=10, out_features=1, bias=True)
)
model.layer1
Linear(in_features=5, out_features=10, bias=True)
이렇게 만들 경우 summary 에서 보이는 layer에 직접 접근하기가 쉬울 뿐 더러 자유도가 높으며 init 과 forward 에서 순서를 알아서 지정할 수 있으므로, 병렬로 연결되거나 데이터에 따라 다른 분기를 타게 하는 등의 trick 을 사용할 때 유리하다. 또한 하나의 모듈을 다시 모듈안에 넣을 수 있는데 아래와 같은 경우를 보자
class Model2( pl.LightningModule):
def __init__(self):
super().__init__()
self.layers = Model()
def forward(self, x):
out = self.layers(x)
return out
model = Model2()
summary(model, input_size=(1, 5))
==========================================================================================
Layer (type:depth-idx) Output Shape Param #
==========================================================================================
Model2 [1, 1] --
├─Model: 1-1 [1, 1] --
│ └─Linear: 2-1 [1, 10] 60
│ └─Linear: 2-2 [1, 1] 11
==========================================================================================
Total params: 71
Trainable params: 71
Non-trainable params: 0
Total mult-adds (Units.MEGABYTES): 0.00
==========================================================================================
Input size (MB): 0.00
Forward/backward pass size (MB): 0.00
Params size (MB): 0.00
Estimated Total Size (MB): 0.00
==========================================================================================
위의 경우 하나의 모델이 더 큰 모델의 서브 모델로 들어갔다. 이런식으로 PyTorch 또는 PyTorch Lightning 의 모듈을 상속받아 init 과 forward 를 활용해서 사용하는 게 가장 일반적인 방법이다.
순차적으로 진행되는 경우 : Sequantial container¶
모듈의 flow 가 순차적으로 진행되는 경우 Sequantial container 를 사용할 수 있다. argument로 module의 순서를 쭉 써주기만 하면 되는데, keras의 sequential과 굉장히 흡사하다. 이 경우에는 별도로 forward 에서 layers 를 연결해주지 않아도, 위에서 아래로 차례로 진행되므로 간단한 모델을 사용할 때 주로 사용된다.
class Model(pl.LightningModule):
def __init__(self):
super().__init__()
self.layers = nn.Sequential(
nn.Linear(5, 10),
nn.ReLU(),
nn.Linear(10, 1),
nn.ReLU()
)
def forward(self, x):
out = self.layers(x)
return out
model = Model()
summary(model, input_size=(1, 5))
==========================================================================================
Layer (type:depth-idx) Output Shape Param #
==========================================================================================
Model [1, 1] --
├─Sequential: 1-1 [1, 1] --
│ └─Linear: 2-1 [1, 10] 60
│ └─ReLU: 2-2 [1, 10] --
│ └─Linear: 2-3 [1, 1] 11
│ └─ReLU: 2-4 [1, 1] --
==========================================================================================
Total params: 71
Trainable params: 71
Non-trainable params: 0
Total mult-adds (Units.MEGABYTES): 0.00
==========================================================================================
Input size (MB): 0.00
Forward/backward pass size (MB): 0.00
Params size (MB): 0.00
Estimated Total Size (MB): 0.00
==========================================================================================
자연스럽가 앞 layer의결과가 다음 layer 의 입력으로 들어간다. 또는 아예 Keras 의 add. method 와 비슷하게도 사용이 가능하다.
class Model(pl.LightningModule):
def __init__(self):
super().__init__()
self.layers = nn.Sequential()
self.layers.add_module('linear1', nn.Linear(5, 10))
self.layers.add_module('ReLU1', nn.ReLU())
self.layers.add_module('linear2', nn.Linear(10,1))
self.layers.add_module('ReLU2', nn.ReLU())
def forward(self, x):
out = self.layers(x)
return out
model = Model()
summary(model, input_size=(1, 5))
==========================================================================================
Layer (type:depth-idx) Output Shape Param #
==========================================================================================
Model [1, 1] --
├─Sequential: 1-1 [1, 1] --
│ └─Linear: 2-1 [1, 10] 60
│ └─ReLU: 2-2 [1, 10] --
│ └─Linear: 2-3 [1, 1] 11
│ └─ReLU: 2-4 [1, 1] --
==========================================================================================
Total params: 71
Trainable params: 71
Non-trainable params: 0
Total mult-adds (Units.MEGABYTES): 0.00
==========================================================================================
Input size (MB): 0.00
Forward/backward pass size (MB): 0.00
Params size (MB): 0.00
Estimated Total Size (MB): 0.00
==========================================================================================
리스트를 활용한 Module 만들기¶
만약 반복되는 layer를 많이 쌓아야 하는 경우 (100층이 넘는 거대 신경망의 경우) 일일이 이걸 치는 대신 list 를 사용할 수 있다. 또한 python의 가장 큰 장점중 하나인 list comprehension 을 사용할 수 있는데 다음의 예를 보자
class Model(pl.LightningModule):
def __init__(self):
super(Model, self).__init__()
self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(5)])
def forward(self, x):
# ModuleList can act as an iterable, or be indexed using ints
for i, l in enumerate(self.linears):
x = self.linears[i // 2](x) + l(x)
return x
#0: x = l[0](x)+l[0](x)
#1: x = l[0](x)+l[1](x)
#2: x = l[1](x)+l[2](x)
#3: x = l[1](x)+l[3](x)
#4: x = l[2](x)+l[4](x)
model = Model()
summary(model, input_size=(1, 10))
==========================================================================================
Layer (type:depth-idx) Output Shape Param #
==========================================================================================
Model [1, 10] --
├─ModuleList: 1-1 -- --
│ └─Linear: 2-1 [1, 10] 110
│ └─Linear: 2-2 [1, 10] (recursive)
│ └─Linear: 2-3 [1, 10] (recursive)
│ └─Linear: 2-4 [1, 10] 110
│ └─Linear: 2-5 [1, 10] (recursive)
│ └─Linear: 2-6 [1, 10] 110
│ └─Linear: 2-7 [1, 10] (recursive)
│ └─Linear: 2-8 [1, 10] 110
│ └─Linear: 2-9 [1, 10] (recursive)
│ └─Linear: 2-10 [1, 10] 110
==========================================================================================
Total params: 550
Trainable params: 550
Non-trainable params: 0
Total mult-adds (Units.MEGABYTES): 0.00
==========================================================================================
Input size (MB): 0.00
Forward/backward pass size (MB): 0.00
Params size (MB): 0.00
Estimated Total Size (MB): 0.00
==========================================================================================
위와 같은 경우에는 layer 가 반복해서 사용되는데, 그 때에도 parameter 를 공유 하기 때문에 parameter 의 숫자는 매우 크진 않다. (param에서 재활용 되는 경우 recursive : 재귀적) 으로 표시된다. 물론 현실에서 이렇게 생긴 신경망으로 학습을 해야하는 경우는 보통 없긴 하지만, 굉장히 크고 복잡한 신경망을 구현하고 싶을 경우 일일이 타자를 치는 대안이 될 수 있다.
'딥러닝-공부하기' 카테고리의 다른 글
[파이토치] 05 - Training step 내부 동작 알아보기 (0) | 2023.10.21 |
---|---|
[파이토치] 04 - 모델의 시각화 (0) | 2023.10.21 |
[Keras] 02 - Keras로 회귀 예측해보기 (1) | 2023.10.08 |
[Keras] 01 - Keras 시작하기 (1) | 2023.10.08 |
[파이토치] 02 - 기초모델 리뷰 그리고 개선 (0) | 2023.09.09 |