블로그 글 중에서 딥러닝을 다루고 있는 페이지가 있습니다.
본 글은 지난 글에서 이미 작성한 내용들은 제외하고 새로운 내용들을 위주로 넣었으니 참고 바랍니다.
참고하시면 되는 글은 해당 문단에서 알려드리겠습니다.
먼저 신경망을 구성하는 가장 기본적인 원리인 퍼셉트론입니다.
퍼셉트론은 AND, OR, NAND, XOR 등 여러 가지 게이트들로 이루어져 있고, 이들은 각각 논리회로들입니다.
자세한 설명은 블로그 내의 퍼셉트론 페이지에 있습니다.
간략하게 설명하자면 퍼셉트론은 많은 숫자들을 0과 1로 나타내는 하나의 과정입니다.
각각의 게이트들이 0과 1로 나타내는 과정과 결과는 다르지만 우리는 여기서 공통점을 찾을 수 있었습니다.
입력값에 대한 출력을 그래프 상으로 나타낼 때 정확히 하나의 직선으로 0과 1을 분리할 수 있었습니다.
직선으로 구간을 분리한다. 즉, 선형인 것입니다.
그러나 XOR게이트 등 점점 더 복잡하고 조건이 많아지는 과정을 다루며 더 이상 하나의 선으로 완벽하게 0과 1을 분리할 수 없게 되었습니다. 이러한 문제를 해결하기 위해선 꼬불꼬불한 선으로 구간을 나눠야겠죠.
위와 같은 형태가 비선형입니다.
비선형은 복잡해 보이지만 어떻게 보면 만능이고, 현재 우리가 딥러닝을 배우는 목적을 잘 보여주기도 합니다.
예를 들어 서울 한복판에 사람들을 하늘에서 쳐다봤을 때 무채색의 옷과 그 외로 구분한다고 할 때 하나의 선으로 이들을 구분 짓는다는 것은 불가능합니다. 그러나 비선형이라면 기괴한 모양이 나올지라도 가능은 하겠죠. 이렇게 복잡하고 이상한 모양이 나올지라도 정확하게 구분을 하는 게 결국 우리가 인공지능에게 바라는 것입니다. 그냥 대충 선으로 그어서 할 거라면 인공지능이 필요가 없겠죠.
그렇게 인간이 할 수 없는 복잡한 연산과 데이터량을 기반으로 정확한 결괏값을 뱉어내는 것이 컴퓨터가 하는 일인데 이를 위해서는 기존의 퍼셉트론을 기반으로 한 단층 신경망은 한계가 명확했던 것입니다.
위와 같은 과정들을 비탕으로 등장한 것이 다층 신경망입니다.
위가 다층신경망의 대략적인 모습이고 입력층, 은닉층, 출력층에 대한 설명도 '신경망' 게시글에 있으니 참고 바랍니다.
바로 활성화 함수로 넘어가겠습니다.
활성화 함수 중 시그모이드, ReLU, 소프트맥스 함수는 다뤘기 때문에 다루지 않은 2개를 다뤄보겠습니다.
1. 하이퍼볼릭 탄젠트 함수
하이퍼볼릭 탄젠트 함수는 선형함수의 결과를 -1 ~ 1 사이에서 비선형 형태로 변형해 줍니다.
2. 리키 ReLU 함수
리키 ReLU 함수는 입력 값이 음수이면 0이 아니라 0.000001처럼 매우 작은 수를 반환합니다. 이는 기존의 ReLU 함수의 문제점 중 입력 값이 수렴하는 구간의 문제를 제거하여 보완한 함수라고 할 수 있습니다.
위의 그래프에서 leak는 영어로 물이 조금 샌다는 뜻인데 이처럼 작은 값이 사이에 존재한다는 것을 알 수 있습니다.
다음은 손실함수입니다.
손실함수에는 평균제곱오차(MSE)와 크로스 엔트로피 오차가 있습니다.
이 둘 모두 게시글 '손실함수'에 설명이 있으니 참고 바랍니다.
딥러닝 학습
딥러닝의 학습은 크게 순전파와 역전파로 진행됩니다.
먼저 첫 번째 단계인 순전파입니다.
순전파는 네트워크에 훈련 데이터가 들어올 때 발생하고, 데이터를 기반으로 예측 값을 계산하기 위해 전체 신경망을 교차해 지나갑니다. 정리하자면, 모든 뉴런이 이전 층을 가중치를 바탕으로 다 거치고 출력 층에 도달한다는 것입니다.
이렇게 출력층에 도달하면 손실함수로 네트워크의 오차값을 추청 하게 됩니다. 물론 오차가 0인 것이 가장 이상적이지만 쉽지 않고, 0애 최대한 가까운 오차가 나오게 훈련시키는 것으로 조정합니다.
오차가 계산되면 그 정보는 역으로 전파되고 이를 역전파라고 부릅니다.
역전파에서의 작용만을 간단하게 설명하자면 은닉층의 뉴런은 각 뉴런이 출력에 기여한 상대적 기여도에 따라 값이 달라지는데, 이를 위해 예측값과 실제 값의 차이를 각 뉴런의 가중치로 미분한 후 기존 가중치 값에서 뺍니다.
이렇게 계산된 뉴런의 결과를 또다시 순전파의 가중치 값으로 사용하여 계속 돌리는 것이죠.
딥러닝의 문제점과 해결 방안
딥러닝에서의 핵심은 비선형 영역을 표현하기 위해 여러 은닉층을 결합하는 것입니다. 그렇다면 은닉층의 개수가 많아질수록 데이터가 잘 분류될 것입니다. 그러나 은닉층이 많아져도 발생하는 문제점이 있습니다.
1. 과적합
과적합은 훈련 데이터를 과하게 학습하여 발생합니다. 훈련 데이터를 과하게 학습하면 오차가 감소하겠지만, 검증 데이터에 대해서는 오차가 증가할 수 있습니다. 그래서 훈련 데이터를 너무 과하게 학습하면 실제 데이터에 대한 오차가 커질 수 있는 문제가 발생할 수 있다는 것입니다.
위가 과적합의 모습들입니다.
왼쪽은 과소적합의 모습입니다. 과소적합은 제대로 분류가 되지 않은 상태를 의미합니다.
가운데 사진은 적정적합입니다. 말 그대로 가장 적당한 상태라는 것입니다. 그러나 보시면 선을 기준으로 다른 모양이 한두 개 삐져나와있습니다. 이가 오차에 해당하는 것입니다. 그러나 왜 오차가 존재하는게 적정인지 궁금할 수 있습니다.
이유는 다음과 같습니다.
딥러닝을 배우는데 있어서 모델을 만드는게 목표입니다. 그리고 이 모델은 범용성을 가져야 합니다. 예를 들어 이 과일이 사과인지 배인지를 판단하는 모델을 만드는데 만약 사과에 대한 훈련을 할 때 빨간 사과로만 한다면 실제 데이터에 초록색 풋사과가 있을 때 이를 사과라고 하지 않을 것입니다. 그러나 우리는 어떠한 색의 사과 사진, 그림, 혹은 애플의 로고를 보고도 이게 사과라고 판단할 수 있는 모델이 필요합니다. 이렇게 훈련 데이터에 실제로는 없지만 실제 데이터를 보고 유추할 수 있게 하는 것이 적절한 오차가 하는 역할인 것입니다.
위의 문제점이 드러나는 것이 과대적합(오버피팅)입니다. 훈련 데이터에서 학습한 내용 이외의 것들은 판단이 불가능한 문제입니다. 이러한 오버피팅 문제를 해결하기 위해 모델을 조정해 줄 필요가 있습니다.
이를 해결하는 방법은 여러 가지가 있지만 대표적으로는 드롭아웃이 있습니다.
간단하게 설명하면 학습 과정 중 일부 노드들을 학습에서 제외시키는 것입니다.
# 드롭아웃 신경망
import torch
class DropouModel(torch.nn.Module):
def __init__(self):
super(DropoutModel, self).__init__()
self.layer1 = torch.nn.Linear(784, 1200)
self.dropout1 = torch.nn.Dropout(0.5) -> 50%의 노드를 무작위로 골라서 쓰지 않겠다.
self.layer2 = torch.nn.Linear(1200, 1200)
self.dropout2 = torch.nn.Dropout(0.5)
self.layer3 = torch.nn.Linear(1200, 10)
def forward(self, x):
x = F.relu(self.layer1(x)) -> F는 any라는 뜻
x = self.dropout(x)
x = F.relu(self.layer2(x))
x = self.dropout2(x)
return self.layer3(x)
간단하게 만든 드롭아웃을 구현한 코드입니다.
2. 기울기 소멸
기울기 소멸은 은닉층이 많아져서 생기는 대표적인 문제입니다. 출력층에서 은닉층으로 전달되는 오차가 크게 줄어들어 학습이 되지 않는 현상입니다. 즉, 기울기가 소멸되기 때문에 학습하는 양이 0에 가까워져 학습이 더뎌지고 오차가 그 상태 그대로 수렴하는 현상입니다.
기울기가 소멸하는 문제는 시그모이드나 ReLU 함수는 사용하여 해결할 수 있습니다.
위의 코드에서도 보면 ReLU 함수를 중간중간에 사용하였습니다.
3. 성능 저하
성능 저하의 문제는 경사 하강법을 하면서 발생합니다. 경사 하강법이란, 손실 함수의 비용이 최소가 되는 지점을 찾을 때까지 기울기가 낮은 쪽으로 계속 이동시키는 과정을 반복하는데, 이때 성능 저하 문제가 발생합니다.
경사 하강법에서 발생하는 문제를 해결하는 방법은 두 가지가 있습니다.
배치 경사 하강법은 전체 데이터셋에 대한 오류를 구한 후 기울기를 한 번만 계산하여 모델의 파라미터를 업데이트하는 방법입니다. 즉, 전체 훈련 데이터셋에 대해 가중치를 편미분 하는 방법입니다.
배치 경사 하강법은 한 번에 모든 데이터셋을 다룬다는 점에서 학습이 오래 걸린다는 단점이 있습니다.
확률적 경사 하강법은 배치 경사 하강법의 문제를 보완하는 방법이라고 할 수 있습니다.
이는 임의로 선택한 데이터에 대해 기울기를 계산하는 방법으로 적은 데이터를 사용하므로 빠른 계산이 가능합니다.
확률적 경사 하강법은 그림과 같이 파라미터 변경 폭이 불안정하고, 때로는 배치 경사 하강법보다 정확도가 낮을 수도 있지만 속도가 빠릅니다.
미니 배치 경사 하강법은 여기서 또 확률적 경사 하강법의 문제를 보완합니다. 그래서 가장 많이 사용하는 방법이기도 합니다. 설명하자면, 전체 데이터셋을 여러 개의 미니 배치로 나누고, 미니 배치 하나마다 기울기를 구한 후 평균 기울기를 구하여 모델을 업데이트하는 방식입니다.
파이토치에서 다음과 같이 구현할 수 있습니다.
class CustomDataset(Dataset):
def __init__(self):
self.x_data = [[1,2,3], [4,5,6], [7,8,9]]
self.y_data = [[12], [18], [11]]
def __len__(self):
return len(self.x_data)
def __getitem__(self, idx):
x = torch.FloatTensor(self.x_data[idx])
y = torch.FloatTensor(self.y_data[idx])
return x, y
dataset = CustomDataset()
dataloader = DataLoader(
dataset,
batch_size = 2,
shuffle = True,
)
옵티마이저는 확률적 겅사 하강에서 파라키터의 폭 문제를 해결하기 위해 적용하는 것입니다.
여러 옵티마이저의 종류들을 이해하기 쉽게 정리한 것입니다.
GD는 배치 경사 하강, SGD가 확률적 경사 하강 되겠습니다.
위에서 보시면 알겠지만 Adam과 Nadam이 가장 성능이 우수하여 보편적인 것을 알 수 있습니다.
optimizer1 = torch.optim.Adagrad(model.parameters(), lr=0.01) # 아다그라드
optimizer2 = torch.optim.Adadelta(model.parameters(), lr=1.0) # 아다델타
optimizer3 = torch.optim.RMSprop(model.parameters(), lr=0.01) # 알엠에스프롭
optimizer4 = torch.optim.SGD(model.parameters(), lr=0.01, momentum = 0.9) # 모멘텀
optimizer5 = torch.optim.SGD(model.parameters(), lr=0.01, momentum = 0.9, nesterov = True) # 네스테로프 모멘텀
optimizer6 = torch.optim.Adam(model.parameters(), lr=0.01) # 아담
딥러닝 알고리즘
딥러닝 알고리즘에도 여러 가지가 존재합니다.
종류만 간단하게 말씀드리면 심층신경망, 합성곱 신경망, 순환 신경망, 제한된 볼츠 머신, 심층 신뢰 신경망이 있습니다.
합성곱 신경망이 가장 유명할 텐데 이는 주로 영상 및 사진이 포함된 이미지 데이터를 다루는데 많이 사용합니다.
이상으로 딥러닝에 대한 개요에 대해 알아봤습니다.
'AI > pytorch' 카테고리의 다른 글
[파이토치] 합성곱 신경망 (1) (0) | 2023.02.09 |
---|
댓글