이번 포스트에서는 본격적으로 우리가 흔히 모델로 알고 있는 신경망을 구축하는것을 알아보자. 아직 텐서변환에 대해 공부하지 않은 분들은 이전의 포스트를 먼저 보고 오면 도움이 될것이라고 생각한다.
[파이토치] Transform
이번 포스트는 파이토치에서 학습을 위해 데이터를 처리의 한 부분인 Transform을 하는지 공부해 보자. Transform은 왜 필요하고 어떻게 할까? 머신러닝과 딥러닝에서 데이터를 학습에 용이하게 활용
dream-be.tistory.com
신경망은 데이터의 연산을 담당하는 레이어와 모듈로 이루어져 있다. torch.nn은 신경망을 구축하는데 필요한 모든 구성요소들을 제공한다. 파이토치가 제공하믄 모든 모듈은 nn.Module의 하위 클래스이다. 신경망은 그 자체가 다른 레이어와 모듈로로 이루어진 모듈이다. 이렇게 중첩되는 구조가 복작한 구조를 설계하고 관리하는 것을 용이하게 한다.
1. Class 정의
신경망을 nn.Module의 하위클래스로 정의 하고 __init__을 통해 신경망 레이어들을 초기화 한다. 모든 nn.Module의 하위클래스는 데이터에 대해 forward 방식으로 연산을 수행한다.
class NeuralNetwork(nn.Module):
def __init__(self):
super(NeuralNetwork, self).__init__()
self.flatten =nn.Flatten()
self.linear_relu_stack = nn.Sequential(
nn.Linear(28*28),
nn.ReLU(),
nn.Linear(512,512),
nn.ReLU(),
nn.Linear(512,10),
)
def forward(self, x):
x = self.flatten(x)
logits = self.linear_relu_stack(x)
return logits
위에서 만든 NeuralNetwork 클래스의 인스턴스를 만든후, 이것을 device로 옮기고 구조를 확인한다.
model = NeuralNetwork().to(device)
print(model)
Out:
NeuralNetwork(
(flatten): Flatten(start_dim=1, end_dim=-1)
(linear_relu_stack): Sequential(
(0): Linear(in_features=784, out_features=512, bias=True)
(1): ReLU()
(2): Linear(in_features=512, out_features=512, bias=True)
(3): ReLU()
(4): Linear(in_features=512, out_features=10, bias=True)
)
)
이제 모델을 사용하기 위해 입력데이터를 전달해야 한다. 이는 백그라운드 연산과 함께 모델의 forward 함수를 실행한다. 여기서 주의 할점은 모델의 forward를 직접적으로 부르면 안된다는 것이다.
X = torch.rand(1,28,28, device= device)
logits = model(X)
pred_probab = nn.Softmax(dim=1)(logits)
y_pred = pred_probab.argmax(1) # 1차원으로 줄인다.
print(y_pred)
Out:
2. 모델 레이어
데이터가 신경망을 통과하면서 어떤일이 일어나는지 확인하기 위해, 사이즈가 28x28인 3장의 이미지로 이루어진 미니배치를 신경망에 통과 시켜보자.
먼저 배치크기가 3이고 사이즈가 28x28 텐서를 만든다.
input_image = torch.rand(3,28,28)
print(input_image.size())
Out:
2.1 nn.Flatten
nn.Flatten은 0번째 차원인 배치크기는 유지하되 나머지 다차원의 데이터를1차원으로 줄여주는 기능을 한다.
flatten = nn.Flatten()
flat_image = flatten(input_image)
print(flat_image.size())
Out:
보이는것과 같이 사이즈 28x28 2차원의 텐서가 flatten과정을 거쳐서 1차원의 784로 바뀐것을 확인할수 있다.
2.2 nn.Linear
Linear 레이어는 입려되는 데이터에 저장된 가중치와 편향을 적용하여 선형변환을 적용하는 모듈이다.
layer1 = nn.Linear(in_features=28*28, out_features=20)
hidden1 = layer1(flat_image)
print(hidden1.size())
Out:
2.3 nn.ReLU
nn.ReLU는 우리가 잘 알고있는 ReLU 함수를 적용하는것을 의미한다. 신경망에서 입력데이터가 저장된 가중치와 편향의 영향을 받고 그 값들이 저장된 hidden layer의 값에 비선형성을 적용하는것이다. 비선형성이 추가되면 입력과 출력 사이에 복잡한 매핑이 가능하게 되고 신경망의 깊이의 층을 깊이 하는것을 유의미 하게 한다.
print(f"Before ReLU: {hidden1[1]}\n\n")
hidden1 = nn.ReLU()(hidden1[1])
print(f"After ReLU: {hidden1}")
Out:
2.4 nn.sequential
nn.sequential는 우선 컨테이너라고 생각하면 이해하기 쉽다. 그렇다면 이 안에 무엇을 담을까? 모듈들을 순서대로 담는다. 그렇게 함으로서 입력 데이터를 순차적으로 처리할 수 있다.
seq_modules = nn.Sequential(
flatten,
layer1,
nn.ReLU(),
nn.Linear(20, 10)
)
input_image = torch.rand(3,28,28)
logits = seq_modules(input_image)
2.5 nn.Softmax
신경망의 마지막 선형 레이어로서 범위가 [-inf, inf] 데이터를 raw value인 logit을 변환한다. 이렇게 변환하는것은 모델이 각 클래스에 대한 예측확률을 [0,1]의 범위에서 스케일링 한다. 이후 logit은 nn.Softmax를 거쳐 각 요소별로 확률값을 반환한다. 아래에서 logit이 softmax를 거친 후에 어떻게 바뀌는지 확인해보자.
logits:
softmax 적용
softmax = nn.Softmax(dim=1)
pred_probab = softmax(logits)
print(pred_probab)
Out:
softmax를 적용한 후의 텐서내 요소들의 값을 확인해보면 모든 값들이 0~1 범위로 변환된 것을 확인할 수 있다.
3. 모델 파라미터
신경망의 많은 레이어들은 파라미터화 된다. nn.Module의 하위 클래스를 만드는 것은 자동적으로 모델 객체내에서 정의한 모든 필드를 추적하고, parameter()와, named_parameters()를 이용하여 모든 파라미터들에 접근할 수 있다.
예시로 모든 파라미터를 순회하면서 각각의 파라미터의 크기와 값들을 확인해보자.
print(f"Model structure: {model}\n\n")
for name, param in model.named_parameters():
print(f"Layer: {name} | Size: {param.size()} | Values : {param[:2]} \n")
Out:
이번 포스트에서는 딥러닝의 핵심인 신경망을 구성하는 것에 대해 공부했다. 다음은 모델이 학습을 하면서 기울기를 자동으로 미분해주는 AUTOGRAD에 대해 알아볼 것이다.
'AI' 카테고리의 다른 글
[파이토치]최적화 (1) | 2022.10.06 |
---|---|
[파이토치]자동미분_AUTOGRAD (0) | 2022.09.30 |
[논문리뷰]Conditional Generative Adversarial Nets(CGAN) (1) | 2022.09.23 |
[파이토치] Transform (1) | 2022.09.21 |
[논문리뷰]Attention Is All You Need (1) | 2022.09.20 |
댓글