x = np.arrange(-5.0, 5.0, 0.1)
y = sigmoid(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1)
plt.show()
[식 3.3]과 같이 활성화 함수로 임계값을 경계로 출력은 함수를 계단 함수(step function)라 한다. 그래서 퍼셉트론에서는 활성화 함수로 쓸 수 있는 여러 후보 중에서 퍼셉트론은 계단 함수를 사용하고 있다. 활성화 함수를 계단 함수에서 다른 함수로 변경하는 것이 신경망으로 나아가는 길이다.
3.2.1 시그모이드 함수
다음은 신경망에서 자주 이용하는 활성화 함수인 시그모이드 함수(sigmoid fuction)를 나타낸 식이다.
[식 3.6]에서 exp(\(-x\))는 \(e^{-x}\)를 뜻하며, \(e\)는 자연상수로 2.7182..의 값을 갖는 실수이다.
cf) 자연 상수 e는 자연로그의 밑, 오일러의 수등 여러 이름으로 불리는데, 파이(π)처럼 수학에서 중요하게 사용되는 무리수이며, 그 값은 대략 2.718281.. 이다.
예를 들어 시그모이드 함수에 1.0과 2.0을 입력하면 h(1.0)=약 0.731, h(2.0)=약 0.88처럼 특정 값을 출력한다.
신경망에서는 활성화 함수로 시그모이드 함수를 이용해 신호를 변환하고, 그 변환된 신호를 다음 뉴런한테 전달한다. 사실 퍼셉트론과 신경망의 주된 차이는 활성화 함수뿐이다.
3.2.2 계단 함수 구현하기
계단 함수는 [식 3.3]과 같이 입력이 0을 넘으면 1을 출력하고, 그 외에는 0을 출력하는 함수이다.
다음은 이러한 계단 함수를 단순하게 구현한 것이다.
// 계단 함수
def step_function(x):
if x > 0:
return 1
else:
return 0
인수 x는 실수만 받아들인다. 즉, step_function(3.0)은 되지만 넘파이 배열을 인수로 넣을 수는 없다.
그러므로 넘파이 배열도 인수로 넣을 수 있도록 구현해보겠다.
def step_function(x):
y = x > 0
return y.astype(np.int)
// astype(): 열의 요소의 dtype을 변경하는 메서드
넘파이의 트릭을 사용해 두 줄 만으로도 작성할 수 있다.
다음 예에서는 x라는 넘파이 배열을 준비하고 그 넘파이 배열에 부등호 연산을 수행한다.
>>> import numpy as np
>>> x = np.array([-1.0, 1.0, 2.0])
>>> y = x > 0
>>> y
array([False, True, True], dtype=bool)
넘파이 배열에 부등호 연산을 수행하면 원소 각각에 부등호 연산을 수행한 bool 배열이 생성된다.
그런데 우리가 원하는 계단 함수는 0이나 1의 'int'형을 출력하는 함수이다. 그래서 배열 y의 원소를 'bool' >> 'int'형으로 바꿔준다.
>>> y = y.astype(np.int)
>>> y
array([0, 1, 1])
이처럼 넘파이 배열의 자료형을 변환할 때는 astype() 메서드를 이용한다.
3.2.3 계단 함수의 그래프
이제 앞에서 정의한 계단 함수를 그래프로 그려보자.
import numpy as np
import matplotlib.pylab as plt
def step_function(x):
return np.array(x > 0, dtype=np.int)
x = np.arange(-5.0, 5.0, 0.1)
y = step_function(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1) # y축 범위 지정, -5.0에서 5.0전까지 0.1 간격
plt.show
이 x, y 배열을 그래프로 그리면 결과는 [그림 3-6]처럼 된다.
[그림 3-6]에서 보듯 계단 함수는 0을 경계로 출력이 0에서 1로 바뀐다. 그림처럼 값이 바뀌는 형태가 계단처럼 생겼기 때문에 '계단' 함수라고 불린다.
3.2.4 시그모이드 함수 구현하기
[식 3.6]의 시그모이드 함수는 파이썬으로 다음과 같이 작성한다.
def sigmoid(x):
return 1 / (1 np.exp(-x))
여기서 인수 x가 넘파이 배열이어도 올바른 결과가 나온다. 실제로 넘파이 배열을 처리해보자.
>>> x = np.array()[-1.0, 1.0, 2.0])
>>> sigmoid(x)
array([0.26894142, 0.73105858, 0.887908])
그럼, 시그모이드 함수를 그래프로 그려보자. 그래프를 그리는 코드는 계단 함수와 거의 같은데, 유일하게 y를 출력하는 함수를 sigmoid 함수로 변경했다는 점만 다르다.
// 시그모이드 함수 그래프로 구현
x = np.arange(-5.0, 5.0, 0.1)
y = sigmoid(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1)
plt.show()
cf) 시그모이드(sigmoid)란 'S자 모양'이라는 듯이다. 계단 함수처럼 그 모양을 따 지은 이름이다.
>>> '시그모이드=S자 모양'
3.2.5 시그모이드 함수와 계단 함수 비교
시그모이드 함수와 계단 함수를 [그림 3-8]에 함께 그려보자, 무엇이 다르고, 무엇이 같을까?
[그림 3-8]을 보고 가장 먼저 느껴지는 점은 '매끄러움'(신경망 학습에서 중요한 역할)의 차이이다.
- 차이점
- 계단 함수: 0을 경계로 출력이 갑자기 바뀌어버림, 0 or 1 중 하나의 값만 반환
- 시그모이드 함수: 부드러운 곡선, 입력에 따라 출력이 연속적으로 변화함, 실수를 반환
- 공통점
- 입력이 작을 때의 출력은 0에 가깝고, 입력이 커지면 출력이 1에 가까워짐
- 즉, 입력이 중요하면 큰 값을 출력, 중요하지 않으면 작은 값을 출력
- 입력이 아무리 작거나 커도 출력은 0에서 1 사이
- 계단 함수, 시그모이드 함수 모두 비선형 함수
즉, 퍼셉트론에서는 뉴런 사이에 0 혹은 1이 흘렀다면, 신경망에서는 연속적인 실수가 흐른다.
3.2.6 비선형 함수
앞서 나왔던 것처럼, 계단 함수와 시그모이드 함수는 모두 비선형 함수이다.
cf) 선형 함수: 무언가를 입력했을 때 출력이 입력의 상수배만큼 변하는 함수
비선형 함수: '선형이 아닌' 함수, 직선 1개로는 그릴 수 없다.
신경망에서는 활성화 함수로 비선형 함수를 사용해야 한다. 선형 함수를 이용하면 신경망의 층을 깊게 하는 의미가 없어지기 때문이다. 선형 함수의 문제는 층을 아무리 깊게 해도 '은닉층이 없는 네트워크'로도 똑같은 기능을 할 수 있다는 데에 있다.
이 문장만 보고는 전혀 이해가 가지 않는다.
예를 들어 보면 선형 함수인 \(h(x) = cx\)를 활성화 함수로 사용한 3층 네트워크를 떠올려보자. 이를 식으로 나타내면 \(y(x) = ax\)와 똑같은 식이다. \(a = c^3\)이라고만 하면 끝이다. 즉, 은닉층이 없는 네트워크로 표현할 수 있다.
이처럼 선형 함수를 이용해서는 여러 층으로 구성하는 이점을 살릴 수 없다. 그래서 층을 쌓는 혜택을 얻고 싶다면 활성화 함수로는 반드시 비선형 함수를 사용해야 한다.
3.2.7 ReLU 함수
시그모이드 함수는 신경망 분야에서 오래전부터 이용해왔으나, 최근에는 ReLU(Rectified Linear Unit, 렐루) 함수를 주로 이용한다.
ReLU는 입력이 0을 넘으면 그 입력을 그대로 출력하고, 0 이하이면 0을 출력하는 함수이다.
수식으로는 [식 3.7]처럼 쓸 수 있다.
그래프와 수식에서 보듯이 ReLU는 간단한 함수이다. 그래서 다음과 같이 쉽게 구현해 쓸 수 있다.
def relu(x):
return np.maximum(0, x) // 두 입력 중 큰 값을 반환한다.
'Data Science > 밑바닥부터 시작하는 딥러닝' 카테고리의 다른 글
데이터에서 학습한다! (0) | 2023.07.15 |
---|---|
Chapter 3.4 3층 신경망 구현하기 (1) | 2023.05.25 |
Chapter 3.3 다차원 배열의 계산 (0) | 2023.05.24 |
Chapter 3.1 퍼셉트론에서 신경망으로 (0) | 2023.05.23 |
퍼셉트론 (0) | 2023.05.15 |