참고링크: https://wikidocs.net/60680

활성화 함수

활성화 함수(activation function)이란 입력 신호의 총합을 출력 신호로 변환하는 함수를 의미한다. 인공 신경망의 활성화 함수는 비선형 함수를 사용해야 한다.

  • 시그모이드(sigmoid)

  • ReLU (Rectified Linear Unit)
    ReLU(z)={z(z0)0(z<0)\mathrm{ReLU}(z) = \begin{cases} \:z \quad(z\geq0) \\ \:0 \quad (z<0) \end{cases}

단층 퍼셉트론

nn.Linear는 PyTorch에서 사용되는 선형 변환(linear transformation)을 수행하는 클래스이다. 두 개의 행렬 가중치(weight)와 편향(bias)을 학습하며, 입력 텐서를 선형 변환하여 출력 텐서를 생성한다. 선형 변환은 입력 텐서와 가중치 행렬의 행렬 곱을 계산하고, 편향을 더하는 연산으로 이루어진다.

  1. torch.ones()로 입력값 weight을 모두 1로 초기화
  2. nn.Linear로 linear layer 생성
  3. nn.ReLU()로 활성화 함수 생성
  4. 입력값을 linear layer에 넣어서 output 생성
  5. output을 ReLU에 넣어서 결과 출력
import torch
import torch.nn as nn

input_size = 10
output_size = 5

input_data = torch.ones(input_size)  # input_size 크기의 1로 채워진 텐서 생성
linear_layer = nn.Linear(input_size, output_size)
relu = nn.ReLU()
output = linear_layer(input_data)
output_relu = relu(output)
print(output_relu)

nn.Linear 클래스를 사용하지 않고 수동으로 해줄 수도 있다. 이때 @ 연산자는 행렬곱을 의미한다.

output_manual = input_data @ linear_layer.weight.data.T + linear_layer.bias.data
print("Manual linear output:", output_manual)

다층 퍼셉트론 (MLP)

은닉층인 hidden_layer 부분이 다르다. nn.Sequential을 사용하여 입력층, 은닉층, 출력층을 모은다.

input_size = 4
hidden_size = 5
output_size = 3

input_data = torch.ones(input_size)
linear_layer = nn.Linear(input_size, hidden_size)
hidden_layer = nn.Linear(hidden_size, output_size)
relu = nn.ReLU()
model = nn.Sequential(linear_layer, relu, hidden_layer)
output = model(input_data)
print(output)

nn.Sequential에서 linear_layer, relu, hidden_layer 순으로 쌓는 이유가 무엇인가? 참고

hi=W1xi+b1h_i = W_1 x_i + b_1 oi=W2hi+b2o_i = W_2 h_i + b_2 y^=ReLU(oi)\hat{y} = \mathrm{ReLU} (o_i)

oi=W2(W1xi+b1)+b2=W1W2xi+b1W2+b2o_i = W_2 (W_1 x_i + b_1) + b_2 = W_1 W_2 x_i + b_1 W_2 + b_2

즉 은닉층을 W=W1W2W = W_1 W_2b=W2b1+b2b = W_2 b_1 + b_2를 사용해서 oi=Wxi+bo_i = W x_i + b로 재구성할 수 있기 때문에, 다층이 아닌 단층 퍼셉트론이 된다. 이를 해결하는 방법은 모든 층의 다음에 ReLU\mathrm{ReLU}와 같은 비선형 함수 σ\sigma를 추가하는 것이다. 즉, hi=σ(W1xi+b1)h_i = \sigma(W_1 x_i + b_1) oi=W2hi+b2o_i = W_2 h_i + b_2 y^=ReLU(oi)\hat{y} = \mathrm{ReLU} (o_i)

역전파

역전파(backpropagation)란, 계산 결과와 정답의 오차를 구해 이 오차에 관여하는 값들의 가증치를 수정하여 오차가 작아지는 방향으로 반복 수정하는 방법이다.

손실함수 MSE와 Adam optimizer를 사용하여 진행해보자.

평균제곱오차(Mean Squared Error, MSE)는 오차를 제곱한 값의 평균 1ni=1n(yiyi^)2\dfrac{1}{n}\displaystyle\sum_{i=1}^n \left(y_i - \hat{y_i} \right)^2 이다.

from torch.optim import Adam

loss_fn = nn.MSELoss()
ground_truth = torch.tensor([0, 0.5, 1], dtype=torch.float)
optimizer = Adam(model.parameters(), lr=0.1)

while True: # backpropagate until the loss is small enough
    optimizer.zero_grad()  # 1. Clear gradient 
    output = model(input_data)  # 2. Forward propagation
    loss = loss_fn(output, ground_truth)  # 3. Compute loss  
    loss.backward()  # 4. Backward propagation  
    optimizer.step()  # 5. Update parameters
    print(f"{i} Loss:", loss.item())
    if loss.item() < 1e-5:
        break
    i += 1

print("Output:", model(input_data))

ground_truth는 우리가 인공지능에게 입력해주는 desired output이다. 즉, [0, 0.5, 1]10510^{-5} 미만의 오차를 갖는 값이 나올 떄까지 반복하라는 의미다.

Adam으로 optimizer를 생성할 때 lr라는 파라미터가 있는데 이는 학습률을 의미한다. 다음을 참고하기 바란다.