본문 바로가기
  • 비둘기다
  • 비둘기다
  • 비둘기다
AI/Deep learning from Scratch

[머신러닝, 딥러닝] 신경망 (1) - 활성화 함수 (계단함수, 시그모이드, ReLU)

by parzival56 2023. 1. 4.

이전에 퍼셉트론에 대해 다뤄보았습니다. 퍼셉트론은 논리회로였습니다. 수학으로 따지면 덧셈, 뺄셈 같은 것이죠. 그러나 우리는 덧셈과 뺄셈을 활용하여 문제를 해결하고 때론 만들곤 합니다. 그렇다면 퍼셉트론이라는 논리회로도 더하기처럼 활용을 해야 합니다. 그리고 그 활용처가 바로 신경망입니다. 

신경망이 퍼셉트론을 가지고 운영하는 것이라기 보단 퍼셉트론의 틀을 가지고 간다는 것이 알맞은 표현이겠네요.

 

신경망은 크게 3개의 층으로 나뉩니다. 

입력층, 은닉층, 출력층이 있습니다. 앞선 퍼셉트론에서는 입력과 출력층 밖에 없었습니다. 가중치는 그저 입력과 출력 사이에 들어가는 하나의 요소였을 뿐이었지만, 신경망에서는 은닉층에서 가중치를 고려한 계산이 이루어집니다. 그러나 은닉층은 말그대로 '은닉'이기에 실제로 보이는 층은 아닙니다. 그리고 출력층에서 은닉층에서의 계산을 거치는 등의 프로세스가 끝난 뒤 최종 값이 도출되는 방식으로 이루어집니다. 

신경망에서의 중요한 부분은 퍼셉트론에서 다뤘던 가중치-편향 부등식이 하나의 변수 요소로 작용한다는 것입니다.

퍼셉트론에서 저희는 가중치-편향 부등식에 매개변수들을 넣고 최종적인 값이 0보다 크면 1을 0이하이면 0을 출력하는 것 자체에 중점을 두었습니다. 즉, 하나 or 둘 등의 입력값에 대해 0 혹은 1의 값을 출력되면 끝이었습니다.

그러나 신경망에서 다루게 되는 활성화함수는 가중치-편향 부등식으로 도출되는 값을 변수로 설정하여 함수에 집어넣어 색다른 데이터의 형태가 나옵니다. 

 

활성화 함수 

활성화 함수는 이름이 말해주듯 입력 신호의 총합이 활성화를 일으키지를 정하는 역할을 합니다.

활성화 함수를 h(x)라고 하게 되면 위의 설명을 빌려 

a = b + w1x1 + w2x2

y = h(a)

의 식이 나옵니다. 여기서 등장하는 y가 해당 입력에 대한 최종값이 되는 것입니다. x는 다양하게 받을 수 있기 때문에 '해당 입력'이라고 하였습니다. 

위처럼 출력 직전에 값을 함수로 인해 변환시키는 작업입니다. 

 

먼저 활성화 함수 중 계단함수에 대해 소개하겠습니다.

 

1. 계단함수

시그모이드나 ReLU를 더 많이 들어보셨을건데 계단함수를 먼저 말하는 이유는 우리가 알고 있는 퍼셉트론과 유사하기 때문입니다. 퍼셉트론은 가중치-편향 공식을 통해 0보다 큰지 아닌지만 보아 값을 0과 1 두 가지로 나눴습니다. 

계단함수도 비숫합니다. 주어진 입력값에 대해 특정 수 이상일 때를 bool형으로 바꾸고 이를 int로 바꿔 그래프로 시각화한 것입니다.

# 계단 함수
import numpy as np
import matplotlib.pylab as plt

def step_func(x):
    return np.array(x > 0, dtype=np.int)

x = np.arange(-5.0, 5.0, 0.1)
y = step_func(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1) # y축의 범위 지정
plt.show()

여기서 함수의 반환값의 의미는 x가 0이상인 경우를 true로 하는 bool형의 넘파이 배열을 만드는데 여기서 또 이 bool형을 int형으로 다시 바꾸는 것입니다. 그래야 0 아니면 1 밖에 나오지 않겠죠?

위 그래프가 코드의 결과입니다. 0에서 그어지는 세로선은 사실상 없는 값이죠. 그러니 값이 0, 1 두 개뿐입니다.

계단 함수는 시그모이드 함수 설명 뒤 같이 비교하며 다시 설명하겠습니다.

 

2. 시그모이드

시그모이드는 정말 별거 없습니다. 

1 / ( 1 + exp(-x))

위 식의 x값에 입력 값을 넣어주면 됩니다. 여기서 exp는 자연상수 e이고 -x제곱을 해준다는 것입니다.

# 시그모이드 함수
def sigmoid (x):
    return 1 / (1 + np.exp(-x))

x = np.array([-1.0, 1.0, 2.0])
sigmoid(x)

x = np.arange(-5.0, 5.0, 0.1)
y = sigmoid(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1)
plt.show()

여기서 볼 수 있듯이 계단함수의 코드와 범위는 똑같이 했음에도 결과는 비슷하듯 다릅니다. 훨씬 '매끄럽다'는 것이 특징입니다. 

그럼 아까의 계단함수와 비교해 보겠습니다. 형태는 s자같이 비슷하고 모두 비선형입니다. 그렇다면 매끄러운 것이 왜 장점인가에 대해서 말해보자면 저는 이것이 우리가 딥러닝을 배우는 이유와 근접하다고 생각합니다.

딥러닝이든 머신러닝이든 이걸 우리가 배우는 이유는 인간의 손으로 계산하지 못하는 영역, 복잡하거나, 많거나 등의 이유로 해결을 할 수가 없을 때 컴퓨터의 도움을 빌려 이걸 최대한 현실의 결과와 가깝게 그 오차를 최소한 거의 무한대 수준으로 줄여 인간에게  편의성을 제공하기 위함인데 아시다시피 0과 1 사이에는 무한개의 수가 존재합니다. 그럼 만약에 아까같이 계단함수처럼 0과 1로만 결괏값이 이루어진다면 그만큼 정밀하지 않다는 것을 의미하겠죠.

반면에 시그모이드는 뭐 굉장히 많은 수를 다루고 있지는 않아도 그 입력값의 개수에 상응하는 결과가 나오기에 위의 코드로만 따지면 정확히 100배나 정밀하다고 할 수 있습니다. 

 

3. ReLU 함수

마지막으로 렐루 함수입니다. 렐루 함수는 식부터 설명드리면, 0 이하이면 0을 반환, 0보다 크면 해당 입력값 자체를 반환합니다. 그래서 시그모이드처럼 작은 수의 영역에서 놀기보단 무엇을 입력하는지에 따라 결과 값이 굉장히 커질 수 있는 함수입니다. 

# ReLU 함수
def relu(x):
    return np.maximum(0, x) # 넘파이 maximum은 (앞, 뒤) 앞과 뒤 중 더 큰 값을 선택해서 반환하는 함수

x = np.arange(-5.0, 5.0, 0.1)
y = relu(x)
plt.plot(x, y)
plt.ylim(-1.0, 5.0)
plt.show()

여기서 한 가지 정도 알고 가야 할 것이 넘파이의 maximum 메서드입니다. 렐루 함수를 보며 이 맥시멈 메서드는 정말 렐루 함수를 구현하기 위해 나온 것이 아닌가 생각이 들 정도입니다. 

maximum(앞, 뒤)에서 앞과 뒤의 숫자를 비교하여 더 큰 쪽을 반환하는 것이 그 역할입니다.

 

 

이상으로 신경망의 기본적인 개념과 이에 속하는 과정 중 하나인 활성화 함수에 대해 살펴보았습니다.

댓글