인공지능/머신러닝

[머신러닝 - 이론, Python] 활성화 함수 - 소프트맥스 함수 구현 (Activation Function - Softmax Function Implementation)

바보1 2022. 5. 7. 18:19

머신러닝에서 출력은 분류(Classfication)와 회귀(Regression)가 있습니다.

분류는 해당 데이터가 어느 클래스에 속하느냐의 문제이고,

회귀는 해당 데이터의 연속적인 수치를 예측하는 문제입니다.

 

회귀의 경우 항등 함수를 이용해서 입력 신호를 그대로 출력해도 되지만,

분류의 경우에는 보통 소프트맥스 함수를 사용합니다.

 

소프트맥스 함수는 다음과 같습니다.

\(y_k = \frac{exp(a_k)}{\sum_{i = 1}^{a} exp(a_i)}\)

 

따라서 분모는 모든 입력 신호를 exp()해서 더한 값이고, 분자는 원하는 k의 입력신호 a_k의 지수 함수가 됩니다.

 

따라서 코드로 구현하면 다음과 같습니다.

import numpy as np


def softmax(a):
    exp_a = np.exp(a)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a

    return y


if __name__ == "__main__":
    x = np.array([0.3, 2.9, 4.0])
    y = softmax(x)

    print(y)
[0.01821127 0.24519181 0.73659691]

간단하죠?

 

하지만 별로 안 이상해 보이지만 치명적인 문제가 있습니다.

 

바로 오버플로우입니다.

 

예를 들어 [1010, 1000, 990]을 넣어 보겠습니다.

import numpy as np


def softmax(a):
    exp_a = np.exp(a)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a

    return y


if __name__ == "__main__":
    x = np.array([1010, 1000, 990])
    y = softmax(x)

    print(y)
[nan nan nan]
C:\Users\Woo\Desktop\Python\ML\Deep Learning from Scratch\Activation_Function\softmax_function.py:5: RuntimeWarning: overflow encountered in exp
  exp_a = np.exp(a)
C:\Users\Woo\Desktop\Python\ML\Deep Learning from Scratch\Activation_Function\softmax_function.py:7: RuntimeWarning: invalid value encountered in true_divide
  y = exp_a / sum_exp_a

오버플로우가 났다고 오류가 나네요..

 

어떻게 해야할까요?

 

답은 간단합니다.

 

입력 값에서 입력 값의 최댓값 혹은 평균을 빼면 됩니다.

그래도 값은 똑같습니다.

 

코드는 아래와 같습니다.

import numpy as np


def softmax(a):
    c = np.max(a)
    exp_a = np.exp(a - c)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a

    return y


if __name__ == "__main__":
    x = np.array([1010, 1000, 990])
    y = softmax(x)

    print(y)
[9.99954600e-01 4.53978686e-05 2.06106005e-09]

간단하게 오버플로우를 막을 수 있습니다.

 

 

다시 처음으로 돌아가서,

맨 위의 코드를 다시 실행하면,

import numpy as np


def softmax(a):
    c = np.max(a)   
    exp_a = np.exp(a - c)       # 오버플로우를 막기 위해서
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a

    return y


if __name__ == "__main__":
    x = np.array([0.3, 2.9, 4.0])
    y = softmax(x)

    print(y)
[0.01821127 0.24519181 0.73659691]

최댓값을 빼더라도 원래의 출력 값과 똑같죠?

 

결과를 보시면 두 가지 사실을 알 수 있는데, 소프트맥스 함수의 출력 값은 0~1 사이의 실수이고, 모든 출력 값의 합이 1이라는 사실입니다.

 

즉 "확률"을 나타낼 수 있습니다.

 

또한 추가적으로 입력 값의 대소 관계가 그대로 소프트맥스 함수 출력 값의 대소 관계에 그대로 나타납니다.

쉽게 말해서 0.3 < 2.9 < 4.0이므로, 출력 값도 똑같이 대소 관계가 나타납니다.

 

결론적으로 지수함수를 계산하는 자원 낭비를 막기 위해서 생략하는 것이 일반적이라고 합니다.

 

감사합니다.

 

 

지적 환영합니다.