관리 메뉴

Bull

[ML] 활성화 함수 / 순전파와 역전파 | study book 본문

Artificial Intelligence/Machine Learning

[ML] 활성화 함수 / 순전파와 역전파 | study book

Bull_ 2024. 8. 18. 15:49

활성화 함수

활성화함수는 인공 신경망에서 사용되는 은닉층을 활성화하기 위한 함수이다. 뉴런의 출력값을 선형에서 비선형으로 변환한다. 즉 활성화 함수는 네트워크가 복잡한 데이터의 패턴을 기반으로 학습하고 결정을 할 수 있도록 한다.

 

활성화 함수는 비선형 구조를 가져 역전파 과정에서 미분값을 통해 학습이 진행될 수 있게 한다. 만약 활성화 함수가 선형구조라면 미분 과정에서 항상 상수가 나오기 때문에 학습을 진행하기가 어렵다. 활성화 함수는 정규화 과정으로 볼 수 있다.

이진 분류

이진 분류는 규칙에 따라 두 그룹으로 분류한다. 예를 들어 0에서 1사이의 값들 중 0.5를 임계값으로 정하여 0.5보다 작으면 false, 크면 true를 결정한다. 이처럼 0에서 1 값을 갖게 하기 위해 시그모이드와 같은 함수가 사용된다.

시그모이드 함수

활성화 함수는 비선형으로 이뤄져 있어 활성화 함수를 적용하면 입력값에 대한 출력이 비선형으로 변환된다. 시그모이드 함수는 S자형 곡선으로 0에서1 또는 -1에서 1범위를 가진다.


$$ Sigmoid(x) = \frac{1}{1+e^{-x}} $$
시그모이드 함수는 주로 로지스틱 회귀에 사용된다. 로지스틱 회구는 독립 변수 X의 선형 결합을 활용하여 결과를 예측한다. 종속 변수 Y를 범주형 데이터를 대상으로 계산하기 때문에 해당 데이터의 결과가 특정 분류로 나뉘게 된다. 즉 로지스틱 회귀는 분류에서도 사용될 수 있다.

 

출력 폭이 0에서1사이로 제한됨으로써 정규화 중 기울기 폭주 문제가 발생하지 않고 미분 식이 단순한 형태를 지닌다.

이진 교차 엔트로피

MSE는 이진 분류에 사용하면 좋은 결과를 얻기 어렵다. 임의의 예측값과 실젯값을 MSE에 적용할 때 차이가 작으면 계산되는 오차도 작아져 학습을 진행하기 어렵다.
$$ MSE = (0.9999999999-1)^2 ⇒ 0 $$
$$ MSE = (0.0000000001-0)^2 ⇒ 0 $$
$$ MSE = (0.9999999999-1)^2 ⇒ 1 $$
즉, 예측값이 실제값에 매우 근접한 경우, MSE는 거의 0에 가까운 값이 되어버린다. 이로 인해 손실이 거의 없다고 판단되며, 모델이 더 이상 학습할 여지가 없다고 오해할 수 있다.

이진 교차 엔트로피

$$ BCE1 = -Y_i·log(\hat{Y_i}) $$

$$ BCE2 = -(1-Y_i)·log(1-\hat{Y_i}) $$

$$ BCE\ =\ BCE1+BCE2 $$

이진 교차 엔트로피는 로그 함수로 오차 함수를 구현한다. 기존의 MSE는 명확하게 불일치해도 높은 손실 값을 반환하지 않았다. (겨우 1) 하지만 이전에 0.000000001과 1을 BCE에 넣으면 약 12정도의 손실 값을 반환한다. 위의 그래프와 같이 BCE1과 BCE2를 합치면 기울기가 0이 되는 지점을 찾을 수 있다. (예측값=실제값이 되는 지점)

이진 분류: 파이토치

책에서 제공되는 데이터로 코드를 실습해본다. x,y,z 가 40이상이고 평균이 60이상이라면 True를 반환한다.

class CustomDataset(Dataset):
    def __init__(self, file_path):
        df = pd.read_csv(file_path)
        self.x1 = df.iloc[:, 0].values
        self.x2 = df.iloc[:, 1].values
        self.x3 = df.iloc[:, 2].values
        self.y = df.iloc[:, 3].values
        self.length = len(df)

    def __getitem__(self, index):
        x = torch.FloatTensor([self.x1[index], self.x2[index], self.x3[index]])
        y = torch.FloatTensor([int(self.y[index])])
        return x, y

y는 0또는 1의 값을 가진다. 나머지 x1,x2,x3는 csv 파일에서 x,y,z의 값을 불러온다.

class CustomModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.layer = nn.Sequential(
          nn.Linear(3, 1),
          nn.Sigmoid()
        )

    def forward(self, x):
        x = self.layer(x)
        return x

모델은 일반 선형 모델에 출력층을 시그모이드 함수를 적용해주었다. Sequential 을 통해서 코드의 가독성을 높였다. 입력차원 3,출력차원 1이다.

criterion = nn.BCELoss().to(device)

비용함수는 BCE(이진 교차 엔트로피)를 사용해주었다.

tensor([[ 75., 100.,  76.],
        [ 71.,  61.,  66.],
        [ 18.,   0.,  40.],
        [ 99.,  63.,  97.]]) 
tensor([[1.],
        [1.],
        [0.],
        [1.]])
tensor([[0.7563],
        [0.6743],
        [0.3967],
        [0.7840]])
tensor([[ True],
        [ True],
        [False],
        [ True]])

배치크기가 4이기 때문에 4개씩 붙어 있다. 행을 나란히 한 값들이 대치되므로 하나만 보겠다. 2번째 행은 71,61,66 이 x값이고 결과가 1이기 때문에 참이 나와야 한다. 3,4번째 텐서를 보면 평균에 대한 예측값과 결과가 정답으로 나오는 것을 확인할 수 있다.

비선형 활성화 함수

비선형 활성화 함수는 네트워크의 비선형성을 적용하기 위해 인공 신경망에 사용된다.

비선형 activation - 계단 함수

$$
H(x) =
\begin{cases}
0 & \text{if } x < 0 \\
1 & \text{if } x \geq 0
\end{cases}
$$
임계값을 넘으면 1, 넘지 못하면 0을 출력한다.

비선형 activation - 임곗값 함수

$$
f(x) =
\begin{cases}
\text{x}& \text{if } x \leq \text{threshold} \\
\text{value} & \text{if } x > \text{threshold}
\end{cases}
$$
임곗값 함수는 임곗값 보다 크면 x를 그대로 전달하고 임게값보다 작으면 특정값으로 변경한다.

비선형 activation - 시그모이드 함수

$$ Sigmoid(x) = \frac{1}{1+e^{-x}} $$
앞에서 했으므로 설명은 넘어 간다.

비선형 activation - 하이퍼블릭 탄젠트 함수

$$ Tanh(x) = \frac{e^x-e^{-x}}{e^x+e^{-x}} $$
출력값이 -1에서 1사이이다. 시그모이드와 마찬가지로 입력값이 크면 기울기 소실이 발생한다.

비선형 activation - ReLU 함수

$$
f(x) = \max(0, x)
$$
또는
$$
f(x) =
\begin{cases}
0 & \text{if } x < 0 \\
x & \text{if } x \geq 0
\end{cases}
$$
ReLu(Rectified Linear Unit Function)은 0보다작으면 0, 0보타 크면 입력값 그대로를 출력한다. ReLU는 출력값이 제한되지 않아 기울기 소실이 발생하지 않는다. 수식 또한 간단하여 역전파가 굉장히 빠르다. 하지만 입력값이 음수라면 가중치나 편향이 갱신되지 않을 수 있다. 이를 죽은 뉴런이라고도 부른다.

비선형 activation - LeakyReLU

$$
f(x) =
\begin{cases}
x & \text{if } x > 0 \\
negative_s lope \times x & \text{if } x \leq 0
\end{cases}
$$

비선형 activation - PReLU

$$
f(x) =
\begin{cases}
x & \text{if } x > 0 \\
a \times x & \text{if } x \leq 0
\end{cases}
$$
PReLU(Parametric ReLU)는 LeackyReLU와 동일하지만 음수 기울기값을 고정 값이 아닌 학습을 통해 갱신되는 값으로 간주한다. 즉 음수 기울기가 지속적으로 변경된다.

비선형 activation - ELU

$$
f(x) =
\begin{cases}
\alpha (\exp(x) - 1) & \text{if } x < 0 \\
x & \text{if } x \geq 0
\end{cases}
$$

여기서 (\alpha)는 양수 하이퍼파라미터로, 일반적으로 (\alpha = 1.0)로 설정됩니다. ELU는 ReLU와 유사하지만, 음수 입력에 대해 지수적으로 감소하여 평균 출력을 0에 더 가깝게 만듭니다. 이는 신경망의 학습을 더 안정적으로 하는 데 도움이 됩니다.


ELU(Exponential Linear Unit Function)는 부드러운 곡선의 형태를 갖는다. 기존 ReLU는 0에서 끊기는데 ELU는 음의 기울기에서 비선형 구조를 갖는다. 그러므로 입력이 0이어도 출력값이 급변하지 않아, 경사하강법의 수렴 속도가 비교적 빠르다. 더 복잡한 연산이므로 학습 속도는 더 느려진다. 하지만 복잡한 패턴관계를 학습하는 능력 향상은 좋다.

비선형 activation - Softmax

$$
\sigma(\mathbf{z})_i = \frac{\exp(z_i)}{\sum_{j=1}^{n} \exp(z_j)} \quad \text{for } i = 1, 2, \dots, n
$$
소프트맥스는 차원 벡터에서 특정 출력 값이 k 번째 클래스에 속할 확률을 계산한다. 즉 클래스를 확률 분포로 매핑할 수있다. 이 외에 Softmin, Log Softmax 도 존재한다.

순전파와 역전파

순전파는 입력이 주어지면 신경망의 출력을 계싼하는 프로세스다. 입력층부터 출력층까지 차례대로 변수를 계산하고 추론한 결과를 전달한다. 계층마다 계산된 가중치와 편향 값을 활성화 함수에 전달한다. 활성화 함수는 출력값을 계산하고 실제값과 연산된 출력값을 손실함수를 통해 오차를 계산한다.

 

역전파는 순전파와 반대 연산을 진행한다. 예측된 출력값과 실제 출력값 사이의 오류를 최소화하기 위해 가중치와 편향을 조정한다. 역전파 과정은 연쇄 법칙을 활용한다.가중치의 조점 방법은 이전에 배웠던 경사 하강법 공식이 사용된다.

import torch
from torch import nn
from torch import optim


class CustomModel(nn.Module):
    def __init__(self):
        super().__init__()

        self.layer1 = nn.Sequential(
            nn.Linear(2, 2),
            nn.Sigmoid()
        )
        self.layer2 = nn.Sequential(
            nn.Linear(2, 1),
            nn.Sigmoid()
        )

        self.layer1[0].weight.data = torch.nn.Parameter(
            torch.Tensor([[0.4352, 0.3545],
                         [0.1951, 0.4835]])
        )

        self.layer1[0].bias.data = torch.nn.Parameter(
            torch.Tensor([-0.1419,  0.0439])
        )

        self.layer2[0].weight.data = torch.nn.Parameter(
            torch.Tensor([[-0.1725,  0.1129]])
        )

        self.layer2[0].bias.data = torch.nn.Parameter(
            torch.Tensor([-0.3043])
        )

device = "cuda" if torch.cuda.is_available() else "cpu"
model = CustomModel().to(device)
criterion = nn.BCELoss().to(device)
optimizer = optim.SGD(model.parameters(), lr=1)

위 코드는 모델 레이어에 입력 차원 2개, 은닉 차원 1번 2개 출력차원 1개가 있는데 각 레이어에 활성화 함수로 시그모이드가 적용되었다. (은닉 차원을 굳이 1번으로 해놓은 이유는 2개 이상이 될 수 있기 때문이다. 여기서는 1개만 있어서 해도 되지만 구분을 위해 1번이라 하였다) 각 층에는 편향도 정의되었다. 그 아래는 가중치와 편향을 초기화하였다. 마지막으로 오차 함수로는 BCE를 사용한다. 출력은 이진 분류를 사용하기 때문이다.

순전파 계산

순전파 계산은 역전파 계산에 비해 간단하다. 위의 코드를 수식으로 바꾸면 다음과 같다.
$$ z_1 = W_1x_1+W_2x_2+b_1 $$
$$ z_2 = W_3x_1+W_4x_2+b_2 $$
x를 통해 출력된 z는 시그모이드 적용 후 은닉차원 1번의 입력 값이 된다.
$$ \sigma_1 = \frac{1}{1+e^{z_1}} $$
$$ \sigma_2 = \frac{1}{1+e^{z_2}} $$
이후 연산(은닉 -> 출력)을 그대로 하면 다음과 같다.
$$ z_3 = W_5\sigma_1+W_6\sigma_2+b_3 $$
$$ \sigma_3 = \frac{1}{1+e^{z_3}} $$

오차 계산

$$ L = -(y-log(\hat{y}) + (1-y)log(1-\hat{y})) $$
loss = criterion(output, y) 로 계산된 과정으로 다시 역전파를 진행하게 된다.

역전파 계산

역전파 계산 각 계층의 가중치와 편향을 역순으로 갱신한다. $W_5$, $W_6$, $b_3$를 갱신한 다음 $W_1$, $W_2$, $W_3$, $W_4$, $b_1$, $b_2$가 갱신된다. 즉 Layer2 -> Layer1로 계산을 진행한다.
Layer2
$W_5(2) = W_5(1) - \alpha \frac{\partial{L}}{\partial{W_5(1)}}$
$W_6(2) = W_6(1) - \alpha \frac{\partial{L}}{\partial{W_6(1)}}$
$b_3(2) = b_3(1) - \alpha \frac{\partial{L}}{\partial{b_3(1)}}$
Layer1
$W_1(2) = W_1(1) - \alpha \frac{\partial{L}}{\partial{W_1(1)}}$
$W_2(2) = W_2(1) - \alpha \frac{\partial{L}}{\partial{W_2(1)}}$
$W_3(2) = W_3(1) - \alpha \frac{\partial{L}}{\partial{W_3(1)}}$
$W_4(2) = W_4(1) - \alpha \frac{\partial{L}}{\partial{W_4(1)}}$
$b_1(2) = b_1(1) - \alpha \frac{\partial{L}}{\partial{b_1(1)}}$
$b_2(2) = b_2(1) - \alpha \frac{\partial{L}}{\partial{b_2(1)}}$

순서는 위의 수식대로 가중치와 편향의 값이 조절된다. 여기서 연쇄법칙이 어떻게 이뤄지는지 편미분 과정은 하나만 보겠다.
$$W_5(2) = W_5(1) - \alpha \frac{\partial{L}}{\partial{W_5(1)}}$$
여기서 $W_5(1)$은 갱신 전 값으로 상수이다. 편미분된 값만 보겠다.

$$ \frac{\partial{L}}{\partial{W_5(1)}} = \frac{\partial{L}}{\partial{\sigma_3}} \times \frac{\partial{\sigma_3}}{\partial{z_3}} \times \frac{\partial{z_3}}{\partial{W_5}} $$
연쇄법칙 편미분은 고등학교에 합성함수의 미분을 생각하면 된다. 변수가 많이 추가되어서 헷갈릴 뿐, 편미분이 목적이 되는 변수를 제외한 나머지는 모두 상수취급하고 기존의 미분 공식을 이용하면된다. 역전파가 가능한 이유는 우리가 수식을 모두 알고 있기 때문이다.
1번째
$$\frac{\partial{L}}{\partial{\sigma_3}} = -\frac{\partial}{\partial{\sigma_3}}ylog(\hat{y})+(1-y)log(1-\hat{y}))$$
$= -\frac{\partial}{\partial{\sigma_3}}ylog(\sigma_3)+(1-y)log(1-\sigma_3)) $
$= -(\frac{y}{\sigma_3}-\frac{1-y}{1-\sigma_3}) $
이제 이 식에서 우리는 변수의 값을 알고 있다. 이전 순전파에서 $\sigma_3$을 구했고 $y$는 정답 값이기 때문에 값을 구할 수 있다.
2번째
$$ \frac{\partial{\sigma_3}}{\partial{z_3}} = \frac{\partial}{\partial{z_3}}(\frac{1}{1+e^{z_3}}) $$
$=\frac{e^{-z_3}}{(1-e^{-z_3})^2}$
$z_3$도 알고 있다.
3번째
$$ \frac{\partial{z_3}}{\partial{W_5}} = \frac{\partial}{\partial{W_5}}(W_5\sigma_1+W_6\sigma_1+b_3) $$
$= \sigma_1$
$\sigma_1$도 알고 있다.

결론적으로 1번째, 2번째, 3번째 모두 상수값으로 구할 수 있기 때문에 $W_5(2)$를 구할 수 있다. 따라서 위에서 정의한 가중치와 편향 값을 모두 구할 수 있다.

퍼셉트론

퍼셉트론은 인공 신경망의 한 종류로 출력이 0 또는 1인 작업을 의미하는 이진 분류에서 사용되는 간단한 모델이다. 퍼셉트론은 신경 세포가 신호를 전달하는 구조와 유사한 방식으로 구현되었다.

 

생물학적 신경망은 가지돌기가 외부의 신경 자극을 통해 신경세포에서 가중치 입력을 받아 신호를 전달한다. 전달된 신호는 축삭을 통해 다른 신경세포로 최종 신호를 전달한다. 퍼셉트론을 비유해보면 외부 자극으로 입력을 받고 축삭을 통해 다른 신경세포로 최종 신호를 전달하는 건 활성화 함수로 비유할 수 있겠다

 

퍼셉트론은 TLU(Threshold Logic Unit) 형태를 기반으로 계단함수를 적용하여 반환한다. 임계값은 0에서1 또는 -1에서1 사이의 값을 출력하는 모델을 의미한다.

단층 퍼셉트론

단층 퍼셉트론은 은닉층없이 하나의 입력층과 하나의 출력층만 존재하는 신경망 모델이다. 이러한 단층 퍼셉트론은 입력층이 선형일 때 한계가 존재한다. 대표적으로 AND, OR, NAND 게이트는 단층 퍼셉트론으로 모델을 구현할 수 있지만 XOR 게이트를 구현할 때는 한계가 존재한다. 이러한 문제를 해결하기 위해 다층 퍼셉트론을 활용한다.

다층 퍼셉트론 (MLP)

다층 퍼셉트론(MLP, Multi-Layer Perceptorn) 단층 퍼셉트론에서 은닉층을 여러 개 쌓아 생성하는 것이다. 은닉층을 2개 이상 연결하면 심층 신경망 (DNN, Deep Neural Network) 라고 부른다. 은닉층이 늘어날수록 더 복잡한 구조의 문제를 해결할 수 있다.

 

XOR에 대한 다층 퍼셉트론을 할 수 있다. 실습 코드는 참고자료의 원작자의 git 에서 확인할 수 있다.

https://github.com/wikibook/pytorchtrf/blob/main/03%EC%9E%A5%20%ED%8C%8C%EC%9D%B4%ED%86%A0%EC%B9%98%20%EA%B8%B0%EC%B4%88/%EC%98%88%EC%A0%9C%203.59%20%EB%8B%A4%EC%B8%B5%20%ED%8D%BC%EC%85%89%ED%8A%B8%EB%A1%A0%20%EA%B5%AC%EC%A1%B0.ipynb

 

pytorchtrf/03장 파이토치 기초/예제 3.59 다층 퍼셉트론 구조.ipynb at main · wikibook/pytorchtrf

《파이토치 트랜스포머를 활용한 자연어 처리와 컴퓨터비전 심층학습》 예제 코드. Contribute to wikibook/pytorchtrf development by creating an account on GitHub.

github.com

 

참고 자료

https://product.kyobobook.co.kr/detail/S000209621433

 

파이토치 트랜스포머를 활용한 자연어 처리와 컴퓨터비전 심층학습 | 윤대희 - 교보문고

파이토치 트랜스포머를 활용한 자연어 처리와 컴퓨터비전 심층학습 | 트랜스포머는 딥러닝 분야에서 성능이 우수한 모델로 현대 인공지능 분야의 핵심 기술입니다. 트랜스포머와 비전 트랜스

product.kyobobook.co.kr