Post
Ch04 Deep Learning시작 | Gihun Son

Ch04 Deep Learning시작

4.1 Artificial Neural Network(인공신경망)의 한계와 Deep Learning

Perceptron(퍼셉트론): input layer, output layer, weight로 구성된 구조, 다수의 입력을 하나의 값으로 출력(Deep learning의 기원) image.png

위 그림 예시에서 AND나 OR 게이트는 (x1,x2)값에 따라 선형적으로 분류되기 때문에 single-layer perceptron으로 학습이 가능하다. 하지만 XOR 게이트 같은 경우 선형적으로 분류가 불가능하다.

-> “multi-layer perceptron”고안

Deep Neural Network(DNN): input layer와 output layer 사이에 hidden layer가 여러개 있는 신경망

4.2 Deep Learning 구조

4.2.1 Deep Learning 용어

Deep Learning은 아래 그림과 같이 Input layer, Output layer 그리고 2개 이상의 hidden layer로 구성되어 있다.

image.png

[Layer]

input layer: 데이터를 받아들이는 layer

hidden layer: 모든 input node로부터 입력값을 받아 weighted sum을 계산하고, 이 값을 activate function에 적용하여 output layer에 전달하는 layer

output layer: Neural Network의 결과값을 도출하는 layer

[Weight]: Node와 Node사이의 연결 강도

[Bias]: weighted sum에 더해주는 상수, activate function를 통과한 값을 조절하는 역할

[Weighted sum(Transfer function)]: 가중치와 신호의 곱을 합한 것

[Activate function]: 신호를 입력받아 처리해주는 함수

[Loss function]: Weigth 학습을 위해 Output function의 결과와 실제 값 간의 오차를 측정하는 함수

[Weight]

가중치는 입력값이 연산 결과에 미치는 영향력을 조절한다. 예시로 x1값이 아무리 커도 w1이 0에 가까운 수라면 x1 x w1은 0에 가까운 값이 될 것이다.

image.png

[Weighted sum(Transfer function)]

각 노드에서 들어오는 신호에 weight를 곱하여 모두 더한 값이다. 이때 계산된 weigted sum을 activate function으로 전달하기 때문에 transfer function이라고도 한다.

image.png

[Activate function]

weighted sum을 일정한 기준에 따라 변화시키는 Non-linear function이다. 종류는 Sigmoid, Hyperbolic tangent, ReLU 함수 등이 있다.

[Activate function(Sigmoid)]

image.png

Linear function의 결과를 0~1사이의 Non-linear한 형태로 변형해준다.(주로 logistic regression과 같은 classification을 확률적으로 표현하는 것에 사용)

하지만 Vanishing gradient problem(기울기 소멸 문제)로 deep learning에서는 잘 사용하지 않는다.

(sigmoid의 값은 0~1값이므로 계속 곱하면 0에 가까워 진다)

image.png

[Activate function(Hyperbolic tangent)]

Linear function의 결과값을 -1~1사이의 Non-linear 형태로 변형해준다. sigmoid함수에서 결과값의 평균이 0이 아닌 양수로 편향된 문제를 해결하는 것에 사용하였지만, 기울기 소멸문제는 여전히 발생한다.

image.png

[Activate function(ReLU)]

입력(x)이 음수일 때는 0을 출력하고, 양수일 때는 x를 출력한다. Gradient Descent에 영향을 주지않아 학습속도가 빠르고, 기울기 소명 문제도 발생하지 않는다.

하지만 음수값을 받으면 항상 0을 출력하기 때문에 학습 능력이 감소하는데 이를 해결하기 위해 Leaky ReLU 함수 등을 사용한다. image.png

[Activate function(Softmax)]

image.png

입력값을 0~1 사이의 값으로 정규화하여 출력값들의 총합이 항상 1이 되도록 한다. Softmax 함수는 보통 output layer의 activate function으로 활용된다.

다음 코드는 ReLU함수와 Softmax함수를 Pytorch에서 구현하는 코드이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Net(torch.nn.Module):
    def __init__(self, n_feature, n_hidden, n_output):
        super(Net, self).__init__()
        self.hidden = torch.nn.Linear(n_feature, n_hidden) #------ 은닉층
        self.relu = torch.nn.ReLu(inplace=True)
        self.out = torch.nn.Linear(n_hidden, n_output) #------ 출력층
        self.softmax = torch.nn.Softmax(dim=n_output)
    def forward(self, x):
        x = self.hidden(x)
        x = self.relu(x) #------ 은닉층을 위한 렐루 활성화 함수
        x = self.out(x)
        x = self.softmax(x) #------ 출력층을 위한 소프트맥스 활성화 함수
        return x

[Loss function]

Gradient Decsent는 Learning Rate와 Loss function의 순간 기울기를 이용하여 가중치를 업데이트하는 방법이다. 즉 미분의 기울기를 이용하여 오차를 비교하고 최소화하는 방향으로 이동시키는 방법(이때 오차를 구하는 방법이 Loss Function)

Loss function은 학습을 통해 얻은 데이터의 추정치가 실제 데이터와 얼마나 차이가 나는지 평가하는 지표(0에 가까울 수록 완벽하게 추정할 수 있다는 것)

대표적인 Loss function으로는 평균 제곱 오차(Mean Squared Error,MSE)엔트로피 오차(Cross Entropy Error,CEE)가 있다.

평균 제곱 오차(Mean Squared Error,MSE)

image.png

Pytorch에서는 아래와 같이 사용한다.

1
2
3
4
5
import torch

loss_fn = torch.nn.MSELoss(reduction='sum')
y_pred = model(x)
loss = loss_fn(y_pred, y)

크로스 엔트로피 오차(Cross Entropy Error,CEE)

Cross entropy error는 Classification문제에서 One-hot encoding을 하였을 때만 사용할 수 있는 오차 계산법이다.

  • One-hot encoding이란 표현하고 싶은 데이터의 인덱스에 1을 부여하고, 나머지에는 0을 부여하여 벡터화 하는 방식

일반적으로 Classification문제에서는 데이터의 출력을 0과 1로 구분하기 위해 sigmoid함수를 사용하는데, sigmoid에 포함된 자연상수 e로 인해 MSE를 적용하면 매끄럽지 못한 그래프가 출력된다. 따라서 CEE를 사용한다.

Ex)

Pr(Class A), Pr(Class B), Pr(Class C)에서 Class B 데이터: [0.0 1.0 0.0]

Pr(Class A) Pr(Class B) Pr(Class C)에서 모델이 예측한 값: [0.228 0.619 0.153] image.png

Pytorch에서는 아래와 같이 사용한다.

1
2
3
4
5
loss = nn.CrossEntropyLoss()
input = torch.randn(5, 6, requires_grad=True) #------ torch.randn은 평균이 0이고 표준편차가 1인 가우시안 정규분포를 이용하여 숫자를 생성
target = torch.empty(3, dtype=torch.long).random_(5) #------ torch.empty는 dtype torch.float32의 랜덤한 값으로 채워진 텐서를 반환
output = loss(input, target)
output.backward()

4.2.2 Deep Learning Training

Deep Learning학습은 Feedforward(순전파)Backpropagation(역전파) 두 단계로 이루어진다.

image.png

첫 번째 단계인 순전파(feedforward)는 네트워크에 훈련 데이터가 들어올 때 발생하며, 데이터를 기반으로 예측 값을 계산하기 위해 전체 신경망을 교차해 지나갑니다. 즉, 모든 뉴런이 이전 층의 뉴런에서 수신한 정보에 변환(가중합 및 활성화 함수)을 적용하여 다음 층(은닉층)의 뉴런으로 전송하는 방식입니다. 네트워크를 통해 입력 데이터를 전달하며, 데이터가 모든 층을 통과하고 모든 뉴런이 계산을 완료하면 그 예측 값은 최종 층(출력층)에 도달하게 됩니다.

그다음 손실 함수로 네트워크의 예측 값과 실제 값의 차이(손실, 오차)를 추정합니다. 이때 손실 함수 비용은 ‘0’이 이상적입니다. 따라서 손실 함수 비용이 0에 가깝도록 하기 위해 모델이 훈련을 반복하면서 가중치를 조정합니다. 손실(오차)이 계산되면 그 정보는 역으로 전파(출력층 → 은닉층 → 입력층)되기 때문에 역전파(backpropagation)라고 합니다. 출력층에서 시작된 손실 비용은 은닉층의 모든 뉴런으로 전파되지만, 은닉층의 뉴런은 각 뉴런이 원래 출력에 기여한 상대적 기여도에 따라(즉, 가중치에 따라) 값이 달라집니다. 좀 더 수학적으로 표현하면 예측 값과 실제 값 차이를 각 뉴런의 가중치로 미분한 후 기존 가중치 값에서 뺍니다. 이 과정을 출력층 → 은닉층 → 입력층 순서로 모든 뉴런에 대해 진행하여 계산된 각 뉴런 결과를 또다시 순전파의 가중치 값으로 사용합니다.

4.2.3 Deep Learning의 문제점과 해결방안

Deep Learning의 핵심은 Activate function이 적용된 여러 hidden layer를 결합하여 Non-linear영역을 표현하는 것이다. 아래 그림과 같이 hidden layer가 많을수록 data분류가 잘된다.

image.png

하지만 hidden layer가 많을수록 다음과 같은 문제가 발생한다.

[Overfitting]

Training data에 너무 과하게 적합되어 실제 Test시에 오차가 증가

->Dropout: Overfitting을 해결하기 위한 방법 중 하나로 학습 과정에서 임의로 일부 node들의 학습을 제외

image.png

아래는 Pytorch에서 Dropout을 구현하는 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class DropoutModel(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))
        x = self.dropout1(x)
        x = F.relu(self.layer2(x))
        x = self.dropout2(x)
        return self.layer3(x)

[기울기 소멸 문제]

Output layer에서 hidden layer로 전달되는 오차가 줄어들어 학습이 이루어지지 않는 현상이다. 이는 sigmoid함수 대신 ReLU함수를 사용하면 해결할 수 있다.

image.png

아래 설명을 읽으면 더 잘 이해할 수 있다.

image.png

[성능이 안좋아지는 문제 발생]

Gradient Descent Method는 Cost Function(Loss function)이 최소가 되는 지점을 찾을 때까지 기울기가 낮은 방향으로 계속 이동시키는데, 이때 성능이 저하됨.

image.png

이를 해결하기 위해 Stochastic Gradient Descent와 Mini Batch Gradient Descent를 사용한다.

image.png

[Batch Gradient Descent(BGD)]

전체 데이터셋에 대한 오류를 구한 후 기울기를 한번만 계산하여 모델의 파라미터를 업데이트하는 방법 image.png

Batch Gradient Descent(BGD)는 1step에 모든 training set을 사용하므로 학습이 오래걸린다는 단점이 있다. 이 단점을 개선한 방법이 Stochastic Gradient Descent(SGD)이다.

[Stochastic Gradient Descent(SGD), 확률적 경사 하강법]

임의로 선택한 데이터에 대해 기울기를 계산하는 방법, 적은 데이터를 사용하므로 빠른 계산이 가능하다.

아래 그림과 같이 parameter변경 폭이 불안정하고, 정확도가 낮을 수는 있지만 속도가 빠르다는 장점이 있다.

image.png

[Mini-batch Gradient Descent, 미니 배치 경사 하강법]

전체 데이터셋을 미니 배치(mini-batch) 여러개로 나누고, mini-batch 하나마다 기울기를 구한 후, 그것의 평균 기울기를 이용하여 모델을 업데이트하여 학습하는 방법

image.png

Mini-batch Gradient Descent는 전체 데이터를 계산하는 것보다 빠르고, Stochastic Gradient Descent에 비해 안정적이라는 장점이 있기 때문에 가장 많이 사용된다.

image.png

Mini-batch Gradient Descent의 구현 코드 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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( #DataLoader에서 알아서 설정이 되는 값, 실제 프로젝트를 해보니 자주 쓰인다. 알아두자
    dataset,# ------ 데이터셋
    batch_size=2,# ------ 미니 배치 크기로 2의 제곱수를 사용하겠다는 의미입니다.
    shuffle=True,# ------ 데이터를 불러올 때마다 랜덤으로 섞어서 가져옵니다.
)

[Optimizer]

Stochastic Gradient Descent의 파라미터 변경 폭이 불안정한 문제를 해결하기 위해 학습 속도와 운동량을 조정하는 Optimizer를 적용해볼 수 있다.

image.png

“속도를 조정하는 방법”

  • Adagrad(Adaptive gradient), 아다그리드

Adagrad는 Weight의 업데이트 횟수에 따라 LR를 조정하는 방법이다. 많이 변화하지 않는 Weight들의 학습률은 크게 하고, 많이 변화하는 Weigtht들은 학습률을 작게 한다. 즉, 많이 변화한 Weight는 최적 값에 근접했을 것이라는 가정하에 세밀하게 조정을 하는 것

image.png

parameter마다 서로 다른 LR를 주기 위해 G함수를 추가. G값은 이전 G값의 누적(기울기 크기의 누적)이다.

기울기가 크면 G값이 커지기 때문에 LR인 η값이 작아진다. Adagrad는 다음과 같이 구현가능하다.

(Adgrad는 기울기가 0에 수렴하는 문제가 있어 사용하지 않음)

1
optimizer = torch.optim.Adagrad(model.parameters(), lr=0.01)
  • Adadelta(Adaptive delta), 아다델타

Adadelta는 Adgrad에서 G값이 커짐에 따라 학습이 멈추는 문제를 해결하기 위해 등장한 방법 수식에서 Learning Rate(η)을 D함수(가중치의 변화량 크기를 누적한 값)로 변환하였기 때문에 LR에 대한 하이퍼파라미터가 필요하지 않다.

image.png

Adadelta는 다음과 같이 구현할 수 있다.

1
optimizer = torch.optim.Adadelta(model.parameters(), lr=1.0)
  • RMSProp(알엠에스프롭)

RMSProp은 Adagrad의 G(i)값이 무한히 커지는 것을 방지하고자 제안된 방법이다.

image.png

Adagrad에서 학습이 되지 않는 문제를 해결하기 위해 G함수에 γ(gamma)만을 추가하였다. G(i)값이 너무 커지면 학습률이 작아져서 학습이 되지 않을 수도 있기 때문에 γ값을 사용하여 LR의 크기를 비율로 조정할 수 있다.

RMSProp은 다음과 같이 구현할 수 있다.

1
optimizer = torch.optim.RMSprop(model.parameters(), lr=0.01)

“운동량을 조정하는 방법”

  • Momentum(모멘텀)

Gradient Descent와 마찬가지로 매번 기울기를 구하지만, Weight를 수정하기 전에 이전 수정방향(+, -)을 고려하여 같은 방향으로 일정한 비율만 수정하는 방법이다. 수정이 양(+)의 방향과 음(-)의 방향으로 순차적으로 일어나며 지그재그로 움직는 현상이 줄어들고, 이전 이동값을 고려하여 일정 비율만큼 다음 값을 결정하므로 관성 효과를 얻을 수 있는 장점이 있다.(SGD와 함꼐 사용)

image.png

위와 같이 SGD의 수식이 있다. 이때 가장 뒤의 값을 사용하여 Weight를 계산하는데, Gradient크기와 반대방향으로 Weight를 업데이트한다.(즉, Gradient가 크면 (-)방향으로 업데이트)

또한 SGD Momentaum은 SGD에서 기울기를 속도(v)로 대체하여 사용하는 방식으로, 이전 속도의 일정 부분을 반영한다. 따라서 이전에 학습했던 속도와 현재 기울기를 반영해서 Weight를 업데이트하는 것이다.

image.png

코드는 아래와 같다.

1
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
  • Nestrov Accelerated Gradient(NAG), 네스트로프 모멘텀

네스테로프 모멘텀은 모멘텀 값과 기울기 값이 더해져 실제 값을 만드는 기존 모멘텀과 달리 모멘텀 값이 적용된 지점에서 기울기 값을 계산한다. 모멘텀 방법은 멈추어야 할 시점에서도 관성에 의해 훨씬 멀리 갈 수 있는 단점이 있지만, 네스테로프 방법은 모멘텀으로 절반 정도 이동한 후 어떤 방식으로 이동해야 하는지 다시 계산하여 결정하기 때문에 모멘텀 방법의 단점을 극복할 수 있다. 따라서 모멘텀 방법의 이점인 빠른 이동 속도는 그대로 가져가면서 멈추어야 할 적절한 시점에서 제동을 거는 데 훨씬 용이

image.png

모멘텀과 비슷하지만 속도(v)를 구하는 과정에서 차이가 있다. 이전에 학습했던 속도와 현재 기울기에서 이전 속도를 뺸 변화량을 더하여 Weight를 구한다.

코드는 아래와 같다.

1
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9, nesterov=True)

“속도와 운동량에 대한 혼용 방법”

  • Adam(Adaptive Moment Estimation), 아담

아담은 모멘텀과 알엠에스프롭의 장점을 결합한 Gradient Descent이다. RMSProp의 특징인 기울기의 제곱을 지수 평균한 값과 모멘텀 특징인 v(i)를 수식에 활용(즉, RMSProp의 G함수와 모멘텀의 v(i)를 사용하여 가중치를 업데이트)

image.png

코드는 아래와 같다.

1
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)

4.2.4 딥러닝을 사용할 때 이점

[Feature Extraction]

컴퓨터가 입력받은 데이터를 분석하여 일정한 패턴이나 규칙을 찾아내려면 사람이 인지하는 데이터를 컴퓨터가 인지할 수 있는 데이터로 변환해 주어야 한다. 이때 데이터별로 어떤 특징을 가지고 있는지 찾아내고, 그것을 토대로 데이터를 벡터로 변환하는 작업을 특성 추출(feature extraction)이라고 한다.

딥러닝이 활성화되기 이전에 많이 사용되었던 머신 러닝 알고리즘인 SVM, 나이브 베이즈(Naïve Bayes), 로지스틱 회귀의 특성 추출은 매우 복잡하며 수집된 데이터에 대한 전문 지식(ex. 제조, 의료 등 수집된 데이터의 도메인 분야에 대한 지식)이 필요했다. 하지만 딥러닝에서는 이러한 특성 추출 과정을 알고리즘에 통합하였다. 데이터 특성을 잘 잡아내고자 hidden layer를 깊게 쌓는 방식으로 파라미터를 늘린 모델 구조 덕분이다.

[빅데이터의 효율적 활용]

딥러닝에서는 특성 추출을 알고리즘에 통합이 가능한 이유는 빅데이터 때문이다. 딥러닝 학습을 이용한 특성 추출은 데이터 사례가 많을수록 성능이 향상되기 때문

즉, 확보된 데이터가 적다면 딥러닝의 성능 향상을 기대하기 힘들기 때문에 머신 러닝을 고려해 보아야 한다.

This post is licensed under CC BY 4.0 by the author.