개발자의 스터디 노트
신경망의 기본 구성 요소 본문
1. 퍼셉트론 : 가장 간단한 신경망
- 가장 간단한 신경망으로 생물학적 뉴런을 대략 본떠 만들었습니다.
- 퍼셉트론 유닛에는 입력(x), 출력(y)과 3개의 다이얼이 있습니다. 가중치(w), 절편(b), 활성화 함수(f)입니다. 가중치와 절편은 데이터에서 학습됩니다. 활성화 함수(activation function)는 신경망과 타깃 출력을 기반으로 설계자의 직관에 따라 결정됩니다.
수학적으로 표현하면 아래와 같습니다.
y = f(w * x + b)
일반적으로 퍼셉트론에는 입력을 하나 이상 사용합니다. 이런 일반적인 상황을 벡터로 표현할 수 있습니다. 즉 x와 w는 벡터이고 x와 w의 곱셈은 점 곱으로 바뀝니다.
y=f(wx + b)
f로 표시한 활성화 함수는 일반적으로 비선형 함수입니다. 선형 함수는 그래프가 직선인 함수입니다. 이 예시의 wx+b는 선형함수입니다. 즉 퍼셉트론은 선형 함수와 비선형 함수의 조합입니다. 선형 함수 표현인 wx+b를 아핀 변환이라고도 부릅니다.
https://hooni-playground.com/1271/
# 파이토치로 구현한 퍼셉트론
import torch
import torch.nn as nn
class Preceptron(nn.Module):
""" 퍼셉트론은 하나의 선형 층입니다. """
def __init__(self, input_dim):
"""
매개변수 :
input_dim (int) : 입력 특성의 크기
"""
super(Preceptron, self).__init()
self.fc1 = nn.Linear(input_dim, 1)
def forward(self, x_in):
"""
퍼셉트론의 정방향 계산
매개변수 :
x_in (torch.Tensor) : 입력 데이터 텐서
x_in.shape는 (batch, num_features)입니다.
반환값 :
결과 텐서. tensor.shape는 (batch,) 입니다.
"""
return torch.sigmoid(self.fc1(x_in)).squeeze()
위 퍼셉트론 코드의 정방향 계산 함수의 활성화 함수는 sigmod 함수를 사용하였습니다.
2. 활성화 함수
- 활성화 함수는 비선형 함수로, 신경망에서 데이터의 복잡한 관게를 감지하는 데 사용합니다.
2.1 시그모이드
- 임의의 실수 값을 받아 0과 1사이의 범위로 압축합니다.
# 시그모이드 활성화 함수
import torch
import matplotlib.pyplot as plt
x = torch.range(-5., 5., 0.1)
y = torch.sigmoid(x)
plt.plot(x.numpy(), y.numpy())
plt.show()
그래프에서 볼 수 있듯이 시그모이드 함수는 입력 범위 대부분에서 매우 빠르게 포화됩니다. 이로 인해 그레디언트가 0이 되거나(그레디언트 소실 문제) 발산하여 부동소수 오버플로우가 되는 문제(그레디언트 폭주 문제)가 발생합니다. 이 때문에 신경망에서 시그모이드 활성화 함수는 거의 출력층에서만 사용합니다. 출력을 확률로 압축하는데 시그모이드 함수를 사용합니다.
2.2 하이포볼릭 탄젠트
- 하이퍼볼릭 탄젠트 활성화 함수는 시그모이드 함수의 변종입니다.
- 하이퍼볼릭 함수는 우리말로 쌍곡선 함수라고도 하며, 삼각함수는 단위원 그래프를 매개변수로 표시할 때 나오지만, 쌍곡선 함수는 표준 쌍곡선을 매개변수로 표시할 때 나온다는 특징이 있습니다.
# 하이퍼볼릭 탄젠트 활성화 함수
import torch
import matplotlib.pyplot as plt
x = torch.range(-5., 5., 0.1)
y = torch.tanh(x)
plt.plot(x.numpy(), y.numpy())
plt.show()
- 시그모이드와 하이퍼볼릭 탄젠트 함수의 가장 큰 차이는 출력 값의 범위로, 하이퍼볼릭 탄젠트 함수는 -1에서 1 사이의 값을 출력하며, 중앙값도 0입니다.
- 하이퍼볼릭 탄젠트는 중앙값이 0이기 때문에, 경사하강법 사용 시 시그모이드 함수에서 발생하는 편항 이동이 발생하지 않습니다. 즉, 기울기가 양수 음수 모두 나올 수 있기 때문에 시그모이드 함수보다 학습 효율성이 뛰어납니다. 또한, 시그모이드 함수보다 범위가 넓기 때문에 출력 값의 변화폭이 더 크고, 그로 인해 기울기 소실 증상이 더 적은 편입니다. 때문에 은닉층에서 시그모이드 함수와 같은 역할을 하는 레이어를 쌓고자 한다면, 하이퍼볼릭 탄젠트를 사용하는 것이 효과적입니다.
- 그러나, 시그모이드 함수보다 범위가 넓다 뿐이지 하이퍼볼릭 탄젠트 역시 그 구간이 그리 크지 않은 편이므로, x가 -5보다 작고 5보다 큰경우, 기울기가 0으로 작아져 소실되는 기울기가 0으로 작아져 소실되는 기울기 소실 현상 문제는 여전히 존재합니다.
2.3 렐루
- 렐루는 가장 중요한 활성화 함수 입니다.
- 렐루가 하는 일은 음숫값을 0으로 자르는 것입니다.
- f(x) = max(0, x)
import torch
import matplotlib.pyplot as plt
relu = torch.nn.ReLU()
x = torch.range(-5., 5., 0.1)
y = relu(x)
plt.plot(x.numpy(), y.numpy())
plt.show()
- 렐루는 음수를 제거해 그레디언트 소실 문제에 도움이 됩니다. 하지만 시간이 지나서 신경망의 특성 출력이 0이 되면 다시 돌아오지 않는다는 문제가 있습니다. 이를 죽은 렐루 문제라고 합니다. 이런 현상을 줄이기 위해 리키 렐루와 PReLU 활성화 함수 같은 변종이 개발되었습니다. PReLU의 누수 파라미터 a는 학습되는 파라미터입니다.
f(x) = max(x, ax)
#PReLU
import torch
import matplotlib.pyplot as plt
prelu = torch.nn.PReLU(num_parameters=1)
x = torch.arange(-5.,5., 0.1)
y = prelu(x)
# PReLU 함수의 결과가 텐서만이 아닌 기울기 값이 포함되어 있어 numpy로 컨버팅이 안되서
# 기울기를 없애고 텐서를 재정의 한 후에 numpy로 형변환 후 그래프를 그려야 했음.
# 파이토치 버번에 따라 사용법이 달라진듯 함.
y = torch.tensor(y,requires_grad=False)
plt.plot(x.numpy(), y.numpy())
plt.show()
#리키렐루
import torch
import matplotlib.pyplot as plt
leakyrelu = torch.nn.LeakyReLU(0.1)
x = torch.arange(-5.,5., 0.1)
y = leakyrelu(x)
plt.plot(x.numpy(), y.numpy())
plt.show()
렐루와 친구들 : 렐루의 변종들에 대해 더 알아본다.
2.4 소프트맥스
- 시그모이드 함수처럼 신경망 유닛의 출력을 0과 1 사이로 압축합니다. 소프트맥스 함수는 모든 출력의 합으로 각 출력을 나누어 k 개 클래스에 대한 이산 확률 분포를 만듭니다.
소프트맥스 출력의 합은 1이 됩니다. 이는 분류 작업의 출력을 해석하는데 매우 유용합니다. 따라서 이 함수는 보통 확률 기반의 목적 함수인 범주형 크로스 엔트로피와 함께 사용합니다.
import numpy as np
import matplotlib.pyplot as plt
def softmax(x):
e_x = np.exp(x - np.max(x))
return e_x / e_x.sum()
x = np.array([1.0,1.0,2.0])
y = softmax(x)
print(y)
print(np.sum(y))
ratio = y
labels = y
plt.pie(ratio, labels=labels, shadow=True, startangle=90)
plt.show()
3. 손실 함수
- 손실 함수는 정답과 예측을 입력으로 받아 실수 값 점수를 만듭니다. 이 점수가 높을수록 모델의 예측 성능이 나빠집니다. 파이토치는 nn 패키지 아래 손실 함수를 많이 구현해 놓았습니다.
3.1 평균 제곱 오차 손실
- 신경망의 출력과 타깃이 연속 값인 회귀 문제에서 널리 사용하는 손실 함수입니다.
- 평균 제곱 오차는 예측과 타깃 값의 차이를 제곱하여 평균한 값입니다.
import torch
import torch.nn as nn
mse_loss = nn.MSELoss()
outputs = torch.randn(3,5, requires_grad=True)
targets = torch.randn(3,5)
loss = mse_loss(outputs, targets)
print(loss)
tensor(1.6285, grad_fn=<MseLossBackward0>)
3.2 범주형 크로스 엔트로피 손실
- 일반적으로 출력을 클래스 소속 확률에 대한 예측으로 이해할 수 있는 다중 분류 문제에 사용합니다. 타깃은 모든 클래스에 대한 다항 분포를 나타내는 원소 n개로 이루어진 벡터입니다. 하나의 클래스만 정답이면 이 벡터는 원-핫 벡터입니다. 신경망의 출력(y)도 원소 n개로 구성된 벡터이며 다항 분포에 대한 신경망의 예측을 나타냅니다. 범주형 크로스 엔트로피는 이 두 벡터를 비교해 손실을 계산합니다.
# 크로스엔트로피 손실
import torch
import torch.nn as nn
ce_loss = nn.CrossEntropyLoss()
outputs = torch.randn(3,5, requires_grad=True)
targets = torch.tensor([1, 0, 3], dtype=torch.int64)
loss = ce_loss(outputs,targets)
print(loss)
tensor(1.9898, grad_fn=<NllLossBackward0>)
3.3 이진 크로스 엔트로피 손실
- 범주형 크로스 엔트로피 손실은 다중 클래스 분류 문제에 유용했다면, 이진 크로스 엔트로피 손실은 두 개를 구별하는 이진 분류에 유용한 손실 함수입니다.
import torch
import torch.nn as nn
bce_loss = nn.BCELoss()
sigmoid = nn.Sigmoid()
probabilities = sigmoid(torch.randn(4, 1))
targets = torch.tensor([1, 0, 1, 0], dtype=torch.float32).view(4,1)
loss = bce_loss(probabilities, targets)
print(probabilities)
print(loss)
tensor(1.0524)
'파이썬 > 파이토치 자연어처리' 카테고리의 다른 글
부가적인 훈련 개념 (0) | 2022.02.12 |
---|---|
지도학습 훈련 순서 (0) | 2022.02.12 |
텐서의 인덱싱, 슬라이싱, 연결 (0) | 2022.02.08 |
텐서의 연산 (0) | 2022.02.08 |
텐서를 만들어 봅시다. (0) | 2022.02.08 |