본문 바로가기
IT 도서/Pytorch로 시작하는 딥 러닝 입문

05. 인공 신경망 (Artificial Neural Network)

by 이민우 2021. 3. 2.
728x90
반응형

wikidocs.net/60021

 

위키독스

온라인 책을 제작 공유하는 플랫폼 서비스

wikidocs.net

*해당 글은 학습을 목적으로 위의 포스팅 내용 중 일부 내용만을 요약하여 작성한 포스팅입니다.

 상세한 내용 및 전체 내용 확인은 위의 링크에서 확인하실 수 있습니다.

 

하이퍼 파라미터와 매개변수

  • 하이퍼파라미터는 값에 따라 모델의 성능에 영향을 주는 매개변수
  • 매개변수는 가중치와 편향 같이 학습을 통해 바뀌는 변수
  • 일반적으로 하이퍼 파라미터는 사용자가 직접 정해줄 수 있는 변수이고, 매개 변수는 사용자가 아닌 모델이 학습하는 과정에서 결정하는 변수이다.
  • 훈련용 데이터로 훈련을 시킨 모델을 테스트 데이터로 검증을 하며 하이퍼 파라미터를 튜닝한다.

 

 

분류와 회귀 (Classifier and Regression)

  • 분류는 입력 데이터가 어떤 클래스(라벨)에 속할지를 결정하는 과정이다.
  • 분류의 종류로는 이진 분류와 다중 클래스 분류가 있다.
  • 회귀는 분류처럼 분리된 답이 아니라, 연속된 결과를 출력하는 과정이다.
  • 시계열 데이터를 이용한 주가 예측, 생산량 예측, 지수 예측 등이 속한다.

 

 

지도 학습과 비지도 학습 (Supervised Learning and Unsupervised Learning)

  • 지도 학습은 라벨이라는 정답과 함께 학습하는 것
  • 비지도 학습은 라벨이 없는 데이터를 통해 학습하는 법. ex)군집, 차원 축소
  • 강화 학습은 어떤 환경 내에서 정의된 에이전트가 현재의 상태를 인식하여, 선택 가능한 행동 중 보상을 최대화하는 행동 혹은 행동 순서를 선택하는 방법이다.
  • 준지도 학습은 일부 라벨이 있는 데이터와 라벨이 없는 대부분의 데이터를 함께 사용하는 것.

 

 

샘플과 특성

https://wikidocs.net/61271

  • 샘플은 하나의 데이터(행), 특성은 종속 변수 y를 예측하기 위한 각각의 독립 변수 x이다.

 

 

 

 

혼동 행렬 (Confusion Matrix)

  • 정밀도는 양성이라 대답한 전체 케이스에 대한 TP의 비율
  • 재현율은 실제값이 양성인 데이터의 전체 개수에 대한 TP의 비율

 

 

 

과적합과 과소적합

  • 과적합이란 훈련 데이터에 지나치게 잘 적응해 실제 데이터에 적용할 수 없는 학습
  • 과소적합이란 성능이 올라갈 여지가 있음에도 훈련을 덜 한 상태.

 

 


퍼셉트론 (Perceptron)

  • 1957년 프랑크 로젠블라트가 제안한 초기 형태의 인공 신경망
  • 다수의 입력으로부터 하나의 결과를 내보내는 알고리즘이다.

 

 

단층 퍼셉트론 (Single-Layer Perceptron, SLP)

  • 값을 보내는 단계와 값을 받아서 출력하는 두 단계로 이루어진 퍼셉트론
  • 즉, 입력층과 출력층으로만 구성된 퍼셉트론
  • AND, NAND, OR 게이트 등을 만들 수 있다.
  • 하지만 XOR 연산이 불가능하다는 단점이 존재한다.
  • 단층 퍼셉트론은 하나의 직선으로 나눌 수 있는데, XOR 게이트는 [[0,0], [0,1], [1,0], [1,1]]로 직선이 아닌 곡선형으로 나눌 수 있기 때문.

 

 

다층 퍼셉트론 (Multi-Layer Perceptraon, MLP)

  • XOR 게이트는 기존의 AND, NAND, OR 게이트를 조합하면 만들 수 있다.
  • 즉, 퍼셉트론의 입장에서 봤을 때 단층 퍼셉트론을 여러 개 사용하면 만들 수 있다.
  • 단층 퍼셉트론에 비해 은닉층이라 불리는 층이 존재한다.
  • 다층 퍼셉트론은 1개 이상의 은닉층을 보유한 퍼셉트론을 말한다.
  • 2개 이상의 은닉층을 보유한 신경망은 심층 신경망 (DNN)이라고 부른다.
  • 이러한 심층 신경망을 학습시키는 과정을 딥 러닝이라고 부른다.

 

 


역전파 (BackPropagation)

  • 출력층에서 입력층으로 향하며 가중치를 업데이트 한다.
  • 상세하게 설명하면, 역방향으로 오차를 전파시키며 각 층의 가중치를 업데이트하고 최적의 학습 결과를 찾아가는 방법이다.
  • 순전파, 역전파, 가중치 업데이트의 과정을 반복해나가며 오차값을 줄여나간다.

 


비선형 활성화 함수

  • 활성화 함수의 특징은 선형 함수가 아닌 비선형 함수여야 한다.
  • 선형 함수란 출력이 입력의 상수배만큼 변하는 함수이다.
  • 비선형 함수는 직선 1개로 그릴 수 없는 함수이다.
  • 인공 신경망의 성능 향상을 위해서는 은닉층을 계속해서 추가해야 한다. 그런데 만약 활성화 함수로 선형 함수를 사용하면 은닉층을 계속 쌓을 수 없다. 몇 번을 추가해도 1회 추가한 것과 차이가 없기 때문.
  • 무슨 말이냐면, 선형 함수를 세 개를 쌓으면 y(x) = h(g(f(x))) 이 되는데, 이 또한 결국은 선형 함수이다.
  • 물론 아무 의미가 없는 것은 아니고, 학습 가능한 가중치가 추가로 생긴다는 점에서는 의미가 있다.

 

 

활성화 함수 종류

https://wikidocs.net/61271

  • ReLU는 입력값이 음수면 기울기도 0이 되고, 해당 뉴런은 회생이 불가능하다. 이 문제를 죽은 렐루 문제라고 한다.
  • 이러한 문제를 보완하기 위해 입력값이 음수이면 0이 아닌 매우 작은 수를 반환하는 Leaky ReLU가 등장했다.

 

  • 이진 분류에서는 시그모이드 함수를 주로 사용한다. (nn.BCELoss())
  • 다중 분류에는 소프트맥스 함수를 주로 사용한다. (nn.CrossEntropyLoss())
  • 참고로 nn.CrossEntropyLoss()는 소프트맥스 함수를 이미 포함하고 있다.

 

 

 


과적합을 막는 방법

1) 모델의 복잡도 줄이기

 

2) 가중치 규제

 2-1) L1규제 : 가중치 w들의 절대값 합계를 비용 함수에 추가한다.

 2-2) L2 규제 : 모든 가중치 w들의 제곱합을 비용 함수에 추가한다.

  • 복잡한 모델은 간단한 모델보다 많은 수의 매개변수를 가진다.
  • 복잡한 모델을 간단한 모델로 변환하기 위해 가중치에 규제를 가한다.
  • 위의 두 식 보두 비용 함수를 최소화하기 위해 가중치들의 값이 작아져야 한다.
  • L1 규제는 오차를 최소화되게 가중치를 찾으며, 동시에 가중치들의 절대값의 합도 최소가 되도록 한다.
  • L2 규제도 가중치를 업데이트하며 가중치들의 절대값의 제곱합도 최소가 되게 한다.
  • L1 규제는 필요가 없는 가중치는 0으로 만들어 버리는 경향이 있으나, L2 규제는 완전히 0으로 만들어버리지 않고 0에 가깝게 만든다.
  • 이러한 특성으로 L1 규제는 어떤 특성이 모델에 영향을 주는지 명확하게 판단할 수 있다.
  • 만약 이러한 판단이 필요가 없다면 L2 규제가 더 잘 작동하므로 L2 규제를 권장한다.
  • 파이토치에서는 weight_decay의 매개변수를 설정해 규제를 할 수 도 있다.

 

3) 드롭아웃

  • 신경망의 일부를 사용하지 않음으로써 모델이 학습 데이터에 완전히 적합하는 것을 막는다.
  • 엄밀히 말하면 학습 시 인공 신경망이 특정 뉴런 또는 특정 조합에 너무 의존적이 되는 것을 방지한다.
  • 또한 매번 랜덤 선택으로 뉴런들을 사용함으로써 서로 다른 신경망들을 앙상블하여 사용하는 것과 같은 효과를 낸다.
  • 단 학습 시에만 사용하고 테스트 시에는 사용하지 않는다.

 

 


기울기 소실과 폭주

  • 기울기 소실 : 역전파 과정에서 기울기가 점차 작아지며 결국 입력층에 가까운 층의 가중치들이 업데이트가 되지 않아 최적화 모델을 찾을 수 없게 되는 현상
  • 기울기 폭주 : 가중치들이 점차 커지며 비정상적으로 큰 값이 되어 발산하는 경우 (RNN에서 자주 발생)

 

 

기울기 소실과 폭주 방지

1) ReLU나 ReLU의 변형 사용

  • 시그모이드 함수는 입력의 절대값이 클 경우 출력값이 0, 1에 수렴하며 기울기가 0에 가까워진다.
  • 이로 인해 역전파 과정에서 전파시킬 기울기가 점차 사라져 입력층 방향으로 갈 수록 역전파가 진행되지 않는 기울기 소실 문제가 발생할 수 있다.
  • 이를 방지하기 위해 은닉층의 활성화 함수로 ReLU나 LeakyReLU를 사용하는 것이 좋다.
  • 특히 LeakyReLU는 죽은 렐루 문제를 해결해준다.

 

2) 가중치 초기화

  • 같은 모델을 훈련시키더라도 가중치가 초기에 어떤 값을 가졌느냐에 따라 모델의 훈련 결과가 달라지기도 한다.
  • 즉, 가중치 초기화만 적절하게 해줘도 기울기 소실과 같은 문제를 완화시킬 수 있다.

 

2-1) 세이비어 초기화 (Xavier Initialzation)

  • 글로럿 초기화라고도 부른다.
  • 균등 분포 또는 정규 분포로 초기화할 때로 나뉘며, 이전 층의 누런 개수와 다음 층의 뉴런 개수를 가지고 식을 세운다.
  • 이전 층의 뉴런의 개수를 n(in), 다음 층의 뉴런의 개수를 n(out)이라고 하자.
  • 논문에서는 균등 분포를 사용해 가중치를 초기화할 경우 다음과 같은 균등 분포 범위를 사용하라고 한다.

https://wikidocs.net/61271

  • 세이비어 초기화는 여러 층의 기울기 분산 사이에 균형을 맞춰 특정 층이 너무 주목을 받거나 다른 층이 뒤처지는 현상을 막아준다.
  • 하지만 세이비어 초기화는 시그모이드 함수나 tahn 함수처럼 S자 형태인 활성화 함수와 사용할 때는 좋은 성능을 보이지만, ReLU와 함께 사용하기에는 성능이 좋지 않다.
  • 세이비어 초기화와 ReLU를 함께 사용하면 층이 거듭될수록 출력값이 0에 수렴하고, 평균과 표준편차 역시 0에 수렴하게 된다.
  • 그렇기에 ReLU와 함께 사용할 경우 다른 초기화 방법을 찾는 것이 좋은데, 이를 HE 초기화라고 한다.

 

2-2) He 초기화

  • 세이비어 초기화와 유사하게 정규 분포와 균등 분포 두 가지 경우로 나뉜다.
  • 다만 He 초기화는 세이비어 초기화와 달리 다음 층의 뉴런의 수를 반영하지 않는다.
  • 이전 층의 뉴런의 개수를 n(in)이라고 하자.
  • He 초기화는 균등 분포로 초기화할 경우 다음과 같은 균등 분포의 범위를 가지도록 한다.

https://wikidocs.net/61271

 

 

 

3) 배치 정규화

https://wikidocs.net/61271

  • 인공 신경망의 각 층에 들어가는 입력을 평균과 분산으로 정규화하여 학습을 효율적으로 만든다.
  • 배치 정규화의 이해를 위해서는 내부 공변량 변화를 이해해야 한다.
  • 내부 공변량 변화 : 학습 과정에서 층 별로 입력 데이터 분포가 달라지는 현상
  • 이전 층들의 학습에 의해 이전 층의 가중치 값이 변경되면 현재 층에 전달되는 입력 데이터의 분포가 현재 층이 학습했던 시점의 분포와 차이가 발생한다.
  • 이러한 입력의 분포 차이는 기울기 소실, 폭주 등을 유발하여 모델을 불안정하게 만든다.
  • 배치 정규화는 표현 그대로 한 번에 들어오는 배치 단위로 정규화를 진행하는 것이다.
  • 각 층에서 활성화 함수를 통과하기 전에 수행되는데, 입력에 대해 평균을 0으로 만들고, 정규화된 데이터에 대해 스케일과 시프르틀 수행한다.
  • 배치 정규화는 시그모이드나 tahn 함수를 사용해도 기울기 소실 문제가 크게 개선된다.
  • 가중치 초기화에 덜 민감하다.
  • 큰 학습률을 사용할 수 있어 학습 속도 개선이 좋다.
  • 미니 배치마다 평균과 표준편차를 계산하기에 훈련 셋에 잡음을 넣는 부수 효과로 과적합 방지 효과가 있다.
  • 하지만 모델을 복잡하게 하며 추가 계산을 하기에 실행 시간이 느려진다.
  • 또한 한계도 존재하는데, 일단 미니 배치의 크기에 너무 의존적이다.
  • 작은 미니 배치에서는 배치 정규화의 효과가 극단적으로 작용하여 훈련에 악영향을 줄 수 이다.
  • 예를 들면 배치 크기를 1로 하면 분산은 0이 된다.
  • 또한 RNN에 적용하기 어렵다.

 

 

4) 층 정규화

https://wikidocs.net/61271

  • 배치 정규화를 사용할 수 없는 RNN에서 정규화를 사용하는 방법

 

 

 

 

 

코드-다층 퍼셉트론으로 손글씨 분류

%matplotlib inline
import matplotlib.pyplot as plt # 시각화를 위한 맷플롯립
from sklearn.datasets import load_digits
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F

USE_CUDA = torch.cuda.is_available() # GPU를 사용가능하면 True, 아니라면 False를 리턴
device = torch.device("cuda" if USE_CUDA else "cpu") # GPU 사용 가능하면 사용하고 아니면 CPU 사용
print(device)

# 랜덤 시드 고정
torch.manual_seed(777)
if device == 'cuda':
    torch.cuda.manual_seed_all(777)

# 데이터셋 로드
digits = load_digits() # 1,979개의 이미지 데이터 로드, shape은 8*8
train_x = digits.data
train_x = torch.tensor(train_x, dtype = torch.float32)
train_y = digits.target
train_y = torch.tensor(train_y, dtype = torch.int64)
train_x = train_x.to(device)
train_y = train_y.to(device)

'''
#시각화
images_and_labels = list(zip(digits.images, digits.target))
for index, (image, label) in enumerate(images_and_labels[:5]): # 5개의 샘플만 출력
    plt.subplot(2, 5, index + 1)
    plt.axis('off')
    plt.imshow(image, cmap=plt.cm.gray_r, interpolation='nearest')
    plt.title('sample: %i' % label)
'''

class MLP(nn.Module) :
    def __init__(self, input_size, output_size) :
        super().__init__()
        self.linear1 = nn.Linear(input_size, 32)
        self.linear2 = nn.Linear(32, 16)
        self.linear3 = nn.Linear(16, output_size)

    def forward(self, x) :
        output = self.linear1(x)
        output = F.relu(output)
        output = self.linear2(output)
        output = F.relu(output)
        output = self.linear3(output)
        output = F.softmax (output)
        return output

model = MLP(8*8, 10)
criterion = nn.CrossEntropyLoss().to(device) # 이 비용 함수는 소프트맥스 함수를 포함하고 있음.
optimizer = optim.Adam(model.parameters())

n_epoch = 100

for epoch in range(n_epoch) :
    y_pred = model(train_x)
    loss = criterion(y_pred, train_y)

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    acc = torch.argmax(y_pred, 1) == train_y # [True, Flase, True ...]
    acc = acc.sum()

    print('Epoch : {}/{}, cost : {:.5f}, acc : {:.5f}'.format(epoch+1, n_epoch, loss.item(), acc/len(train_x)))


#시각화
images_and_labels = list(zip(digits.images, digits.target))
for index, (image, label) in enumerate(images_and_labels[:5]): # 5개의 샘플만 출력
    pred = 0
    with torch.no_grad() :
        image = list(image)
        image = torch.tensor(image, dtype = torch.float32)
        image = image.to(device)
        image_x = image.view(-1, 64)
        pred = model(image_x)
    pred = torch.argmax(pred)
    plt.subplot(2, 5, index + 1)
    plt.axis('off')
    plt.imshow(image, cmap=plt.cm.gray_r, interpolation='nearest')
    plt.title('sample: {}\npred : {}'.format(label, pred))

plt.show()
728x90
반응형