딥러닝에서 최적화 알고리즘이란?

Study/Deep Learning|2023. 3. 31. 15:16
728x90
반응형

 

딥러닝 모델의 학습에서는 최적화 알고리즘이 매우 중요한 역할을 합니다. 최적화 알고리즘은 모델의 파라미터 값을 조정하여 손실 함수(loss function) 값을 최소화하는 것이 목적입니다. 이번에는 대표적인 최적화 알고리즘 몇 가지에 대해 자세히 설명해드리겠습니다.

 


1. 확률적 경사 하강법(Stochastic Gradient Descent, SGD)

확률적 경사 하강법은 가장 기본적인 최적화 알고리즘 중 하나로, 모든 데이터 샘플을 사용하지 않고 일부 샘플을 사용하여 계산합니다. 이를 통해 계산 속도를 높이고, 메모리 사용량을 줄일 수 있습니다. SGD는 각 파라미터에 대한 그래디언트(기울기)를 계산하고, 파라미터를 그래디언트 반대 방향으로 이동시켜 손실 함수 값을 줄입니다.

 

SGD는 각 파라미터에 대한 그래디언트(기울기)를 계산하고, 파라미터를 그래디언트 반대 방향으로 이동시켜 손실 함수 값을 줄입니다. 이 과정에서 파라미터를 업데이트할 때, 학습률(learning rate)이라는 하이퍼파라미터를 사용합니다. 학습률은 파라미터를 얼마나 크게 이동시킬지 결정하는데, 학습률이 크면 빠르게 수렴할 수 있지만, 최적점을 지나쳐 발산할 수 있고, 학습률이 작으면 최적점에 수렴하는 데 시간이 오래 걸립니다.

 

SGD의 동작 방식은 다음과 같습니다.

 

  1. 데이터에서 임의로 하나의 샘플을 선택합니다.
  2. 선택된 샘플을 모델에 입력하고, 출력을 계산합니다.
  3. 선택된 샘플에 대한 손실 함수 값을 계산합니다.
  4. 손실 함수의 그래디언트(기울기)를 계산합니다.
  5. 그래디언트 반대 방향으로 파라미터를 이동시킵니다.

위 과정을 반복합니다.

 

확률적 경사 하강법(Stochastic Gradient Descent, SGD)은 기계 학습에서 매개 변수를 최적화하는데 사용되는 반복적인 최적화 알고리즘이다. 일반적인 경사 하강법과 달리, 확률적 경사 하강법은 각 반복에서 하나의 데이터 포인트를 임의로 선택하여 경사를 계산한다. 이렇게 하면 계산 속도가 빨라지고, 더 빈번한 업데이트를 통해 최적화에 도달할 수 있다.

 

수학적 모델로 표현하면 다음과 같다:

 

목적 함수 (비용 함수) J(θ)를 최소화하고자 한다. 여기서 θ는 모델의 매개 변수이다.

 

J(θ) = (1/N) * [L(y^(i), f(x^(i), θ))] (i = 1, 2, ..., N)

 

N은 데이터 포인트의 수, L은 손실 함수, (x^(i), y^(i))i번째 데이터 포인트 (입력, 출력)이다.

각 반복에서, 데이터 포인트 (x^(i), y^(i)) 중에서 하나를 무작위로 선택한다.

선택된 데이터 포인트에 대해 경사(그레디언트)를 계산한다.

 

J(θ) ≈ ∇L(y^(i), f(x^(i), θ))

 

매개 변수 θ를 업데이트한다.

 

θ = θ - α * J(θ)

 

여기서 α는 학습률(learning rate), 얼마나 빨리 최적화에 도달할지를 결정하는 하이퍼파라미터다.

수렴 기준을 충족하거나 최대 반복 횟수에 도달할 때까지 2-4단계를 반복한다.

 

이러한 방식으로, 확률적 경사 하강법은 전체 데이터셋의 경사를 한 번에 계산하는 것이 아니라, 하나의 데이터 포인트를 사용하여 근사치를 계산하여 최적화를 빠르게 수행한다. 이로 인해 속도가 빨라지지만, 확률적 경사 하강법의 수렴은 일반적인 경사 하강법보다 더 불안정하다는 단점이 있다. 이를 극복하기 위해 여러 가지 변형이 제안되었다.

 

import torch
import torch.nn as nn
import torch.optim as optim

# 데이터 준비
x = torch.tensor([[1.0], [2.0], [3.0], [4.0]])
y = torch.tensor([[2.0], [4.0], [6.0], [8.0]])

# 모델 정의
class LinearRegression(nn.Module):
    def __init__(self):
        super(LinearRegression, self).__init__()
        self.linear = nn.Linear(1, 1)

    def forward(self, x):
        return self.linear(x)

model = LinearRegression()

# 손실 함수
criterion = nn.MSELoss()

# 하이퍼 파라미터
learning_rate = 0.01
epochs = 1000

 

확률적 경사 하강법(Stochastic Gradient Descent, SGD)의 불안정한 수렴 특성을 개선하기 위해 여러 가지 변형이 제안되었습니다. 이러한 변형들은 주로 학습률 조정, 모멘텀 사용, 그리고 미니 배치를 사용하는 것입니다. 여기 일부 주요 변형에 대해 설명하겠습니다.

 

<SGD 소스>

optimizer = optim.SGD(model.parameters(), lr=learning_rate)

for epoch in range(epochs):
    for i in range(x.size(0)):
        inputs = x[i]
        targets = y[i]

        # 경사 초기화
        optimizer.zero_grad()

        # 순전파
        outputs = model(inputs)

        # 손실 계산
        loss = criterion(outputs, targets)

        # 역전파
        loss.backward()

        # 매개변수 업데이트
        optimizer.step()

 

1-1. Mini-Batch Gradient Descent (미니 배치 경사 하강법):

 

미니 배치 경사 하강법은 SGD와 일반 경사 하강법의 절충안입니다. 이 방법에서는 한 번의 반복에서 전체 데이터셋을 사용하는 것이 아니라, 데이터셋의 작은 무작위 부분집합(미니 배치)을 사용하여 경사를 계산합니다. 이렇게 하면 계산 효율성과 수렴 속도 사이에 균형을 이룰 수 있습니다.

 

batch_size = 2
optimizer = optim.SGD(model.parameters(), lr=learning_rate)

for epoch in range(epochs):
    for i in range(0, x.size(0), batch_size):
        inputs = x[i:i+batch_size]
        targets = y[i:i+batch_size]

        optimizer.zero_grad()
        outputs = model(inputs)
        loss = criterion(outputs, targets)
        loss.backward()
        optimizer.step()

 

1-2. Momentum (모멘텀):

 

모멘텀은 SGD의 변형 알고리즘으로, 이전 그래디언트를 고려하여 파라미터 값을 업데이트합니다. 이전 그래디언트와 현재 그래디언트를 일정 비율로 조합하여 이전 그래디언트 방향과 현재 그래디언트 방향을 모두 고려하여 파라미터 값을 업데이트합니다. 이를 통해 SGD보다 더 빠르게 수렴할 수 있습니다. 

 

모멘텀은 경사 하강법에 관성 개념을 도입하여 최적화를 개선합니다. 이전 반복에서의 경사를 고려하여 매개 변수를 업데이트하는 방식입니다. 이를 통해 수렴 속도가 빨라지고, 지역 최소값(local minima)에서 벗어나는 데 도움이 됩니다.

θ = θ - α * J(θ) + β * v_t

v_tt-1 시점에서의 경사에 대한 가중치입니다. β는 모멘텀 계수로 01 사이의 값입니다.

 

momentum = 0.9
optimizer = optim.SGD(model.parameters(), lr=learning_rate, momentum=momentum)

for epoch in range(epochs):
    optimizer.zero_grad()
    outputs = model(x)
    loss = criterion(outputs, y)
    loss.backward()
    optimizer.step()

 

1-3. Adagrad (Adaptive Gradient Algorithm):

 

아다그라드는 학습률을 각각의 파라미터에 대해 동적으로 조정하여 파라미터를 업데이트합니다. 이전 그래디언트의 크기가 큰 파라미터는 학습률을 작게 조정하고, 이전 그래디언트의 크기가 작은 파라미터는 학습률을 크게 조정합니다. 이를 통해 SGD와 모멘텀보다 더 빠르게 수렴할 수 있습니다.

 

Adagrad는 학습률을 매개 변수별로 독립적으로 조정하는 방법입니다. 이는 각 매개 변수에 대한 그래디언트의 제곱 누적을 사용하여 학습률을 조정합니다. 이 방법은 희소한 데이터에 대해 더 나은 결과를 가져오며, 수렴 속도를 개선합니다.

θ = θ - (α / (G + ε)) * J(θ)

G는 각 매개 변수에 대한 그래디언트의 제곱 누적이며, ε0으로 나누는 것을 방지하기 위한 작은 상수입니다.

 

optimizer = optim.Adagrad(model.parameters(), lr=learning_rate)

for epoch in range(epochs):
    optimizer.zero_grad()
    outputs = model(x)
    loss = criterion(outputs, y)
    loss.backward()
    optimizer.step()

 

1-4. RMSprop (Root Mean Square Propagation):

 

RMSProp은 아다그라드와 유사한 알고리즘으로, 그래디언트 제곱 평균을 사용하여 학습률을 동적으로 조정합니다. 그러나 아다그라드와 달리, 모든 이전 그래디언트를 누적하는 대신, 최근 그래디언트에 더 많은 가중치를 부여합니다. 이를 통해 SGD보다 더 빠르게 수렴할 수 있습니다. 

 

RMSpropAdagrad의 학습률 감소 문제를 해결하기 위해 제안되었습니다. 이 방법은 최근 반복에서 계산된 그래디언트의 제곱 평균을 사용하여 학습률을 조정합니다.

θ = θ - (α / (E[g^2] + ε)) * J(θ)

E[g^2]는 최근 반복에서의 그래디언트의 제곱 평균을 나타냅니다. 이는 지수 가중 평균(exponentially weighted average)을 사용하여 계산됩니다.

 

optimizer = optim.RMSprop(model.parameters(), lr=learning_rate)

for epoch in range(epochs):
    optimizer.zero_grad()
    outputs = model(x)
    loss = criterion(outputs, y)
    loss.backward()
    optimizer.step()

 

1-5. Adam (Adaptive Moment Estimation):

 

아담은 모멘텀과 아다그라드를 합친 알고리즘으로, 그래디언트와 그래디언트 제곱을 각각 모멘텀과 아다그라드처럼 사용하여 파라미터 값을 업데이트합니다. 또한, 학습률도 각각의 파라미터에 대해 동적으로 조정합니다. 이를 통해 SGD, 모멘텀, 아다그라드보다 더 빠르고 안정적으로 수렴할 수 있습니다. 

 

Adam은 모멘텀과 RMSprop의 아이디어를 결합한 최적화 알고리즘입니다. Adam은 각 매개 변수에 대해 일차 모멘트 추정(모멘텀)과 이차 모멘트 추정(RMSprop)을 유지하고, 이를 사용하여 학습률을 동적으로 조정합니다.

m_t = β1 * m_t-1 + (1 - β1) * J(θ)

v_t = β2 * v_t-1 + (1 - β2) * (J(θ))^2

m_t_hat = m_t / (1 - β1^t)

v_t_hat = v_t / (1 - β2^t)

θ = θ - α * (m_t_hat / (v_t_hat + ε))

 

여기서 m_tv_t는 각각 일차 및 이차 모멘트 추정값입니다. β1β2는 각각 일차 및 이차 모멘트에 대한 감쇠 계수이며, ε0으로 나누는 것을 방지하기 위한 작은 상수입니다. t는 현재 반복 횟수를 나타냅니다.

 

optimizer = optim.Adam(model.parameters(), lr=learning_rate)

for epoch in range(epochs):
    optimizer.zero_grad()
    outputs = model(x)
    loss = criterion(outputs, y)
    loss.backward()
    optimizer.step()

  

1-6. AdaMax:

 

AdaMaxAdam 알고리즘의 확장으로, 그래디언트의 무한 노름(infinity norm)을 사용하여 업데이트를 조정합니다. 이 방법은 무한 노름을 사용하기 때문에 Adam보다 더 강인하다고 알려져 있습니다.

이러한 변형 중 일부는 다른 상황이나 문제에 대해 더 나은 성능을 보일 수 있습니다. 따라서 실제로는 문제에 따라 가장 적합한 최적화 알고리즘을 선택하는 것이 중요합니다. 이를 위해 교차 검증(cross-validation)과 같은 기법을 사용하여 여러 알고리즘의 성능을 비교해 볼 수 있습니다.

 
optimizer = optim.Adamax(model.parameters(), lr=learning_rate)

for epoch in range(epochs):
    optimizer.zero_grad()
    outputs = model(x)
    loss = criterion(outputs, y)
    loss.backward()
    optimizer.step()

 

1-7. Adadelta

AdadeltaRMSProp의 변형 알고리즘으로, 학습률을 동적으로 조정하는 대신 그래디언트 제곱 평균과 파라미터 업데이트 값의 제곱 평균을 사용합니다. 이전 업데이트 값의 제곱 평균도 함께 사용하여 파라미터를 업데이트합니다. 이를 통해 학습률을 더욱 자동으로 조정하고, RMSProp보다 더 빠르게 수렴할 수 있습니다.

 

AdadeltaAdagrad의 학습률 감소 문제를 해결하기 위해 제안되었습니다. Adadelta는 최근 반복에서 계산된 그래디언트의 제곱 평균과 이전 반복에서의 매개변수 업데이트의 제곱 평균을 사용하여 학습률을 조정합니다.

E[g^2]_t = ρ * E[g^2]_t-1 + (1 - ρ) * (J(θ))^2

E[∆θ^2]_t = ρ * E[∆θ^2]_t-1 + (1 - ρ) * (∆θ_t)^2

∆θ_t = - ((E[∆θ^2]_t + ε) / (E[g^2]_t + ε)) * J(θ)

θ = θ + ∆θ_t

 

여기서 ρ는 감쇠 계수이고, ε0으로 나누는 것을 방지하기 위한 작은 상수입니다.

 
optimizer = optim.Adadelta(model.parameters(), lr=learning_rate)

for epoch in range(epochs):
    optimizer.zero_grad()
    outputs = model(x)
    loss = criterion(outputs, y)
    loss.backward()
    optimizer.step()

 

1-8. AdamW

AdamW는 아담의 변형 알고리즘으로, 가중치 감쇠(weight decay)를 추가하여 학습률을 조정합니다. 가중치 감쇠는 모델의 복잡도를 줄이기 위해 가중치 값이 작아지도록 하는 역할을 합니다. 이를 통해 아담보다 더 안정적으로 수렴할 수 있습니다.

 

weight_decay = 0.01
optimizer = optim.AdamW(model.parameters(), lr=learning_rate, weight_decay=weight_decay)

for epoch in range(epochs):
    optimizer.zero_grad()
    outputs = model(x)
    loss = criterion(outputs, y)
    loss.backward()
    optimizer.step()

 

1-9. AdamP

AdamPAdam의 변형 알고리즘 중 하나로, L2 정규화 항에 대한 학습률을 동적으로 조정합니다. 기존의 AdamL2 정규화 항에 대해 학습률을 고정시켜 사용하는 반면, AdamPL2 정규화 항의 크기에 따라 학습률을 동적으로 조절합니다. 이를 통해 더욱 안정적인 최적화를 이룰 수 있습니다.

 

AdamP는 적응형 페널티를 도입하여 계층별로 학습률을 조절하는 최적화 알고리즘입니다. 이 최적화기는 adamp 라이브러리를 설치하여 사용할 수 있습니다.

 
>> pip install adamp  ## 설치 필요
from adamp import AdamP

optimizer = AdamP(model.parameters(), lr=learning_rate)

for epoch in range(epochs):
    optimizer.zero_grad()
    outputs = model(x)
    loss = criterion(outputs, y)
    loss.backward()
    optimizer.step()

 

 

1-10. AdaBelief

AdaBelief는 그래디언트와 이전 그래디언트의 변화율을 고려한 학습률 동적 조절 방식을 사용합니다. 이전 그래디언트 방향의 변화량이 크면 학습률을 감소시키고, 변화량이 작으면 학습률을 증가시킵니다. 또한, 두 그래디언트의 표준 편차 비율을 사용하여 이상치(outlier)를 제거합니다. 이를 통해 Adam보다 더욱 안정적인 최적화를 이룰 수 있습니다.

 

from adabelief_pytorch import AdaBelief

optimizer = AdaBelief(model.parameters(), lr=learning_rate)

for epoch in range(epochs):
    optimizer.zero_grad()
    outputs = model(x)
    loss = criterion(outputs, y)
    loss.backward()
    optimizer.step()

 

1-11. RAdam

RAdam은 그래디언트의 분산에 대한 편향 보정(bias correction)을 수행하는 Adam의 변형 알고리즘입니다. 기존의 Adam은 처음에는 그래디언트가 적을 때 학습률을 크게 하고, 이후에는 그래디언트가 커질수록 학습률을 작게 조절합니다. 그러나 이 방식은 초기에 그래디언트가 적을 때, 파라미터의 큰 흔들림(shaking) 현상이 발생할 수 있습니다. RAdam은 이러한 현상을 막기 위해, 초기에 그래디언트에 대한 분산을 과소추정(underestimate)한 것을 보정하는 방식을 사용합니다. 이를 통해 Adam보다 더욱 안정적인 최적화를 이룰 수 있습니다.

 
from radam import RAdam

optimizer = RAdam(model.parameters(), lr=learning_rate)

for epoch in range(epochs):
    optimizer.zero_grad()
    outputs = model(x)
    loss = criterion(outputs, y)
    loss.backward()
    optimizer.step()​

 


 

이러한 최적화 알고리즘은 다양한 딥러닝 문제에 적용할 수 있으며, 각 알고리즘이 특정 문제에 대해 얼마나 잘 작동하는지는 문제에 따라 달라집니다. 따라서 여러 알고리즘을 실험하고 문제에 가장 적합한 최적화 알고리즘을 선택하는 것이 중요합니다.

 

 

728x90
반응형

'Study > Deep Learning' 카테고리의 다른 글

딥러닝에서 정규화란?  (0) 2023.04.04
베이지안 딥러닝  (0) 2023.04.03
딥러닝에서 손실함수(loss function)란?  (0) 2023.03.31
딥러닝에서 데이터 분포란?  (0) 2023.03.30
딥러닝에서 확률과 통계  (0) 2023.03.30

댓글()