이 포스팅은 밑바닥부터 시작하는 딥러닝을 공부하고 정리한것 입니다.
앞에서 배운 퍼셉트론의 장점은 복잡한 함수를 표현할 수 있다는 것이다. 그러나 단점은 가중치를 설정하는 작업은 여전히 사람이 수동으로 한다는 점이다.
신경망이 이러한 단점을 커버해준다. 가중치 매개변수의 적절한 값을 데이터로부터 자동으로 학습하는 능력이 이제부터 살펴볼 신경망의 중요한 성질이다.
퍼셉트론에서 신경망으로
여기서는 입력,은닉,출력층을 순서대로 0층,1층,2층 이라 하겠다. 그림 3-1의 신경망은 모두 3층으로 구성된다. 가중치를 갖는 층은 2개뿐이기 때문에 2층 신경망이라고 한다.
위 그림에서는 가중치가 b이고 입력이 1인 뉴런이 추가되었다. 이 퍼셉트론의 동작은 x1,x2,1 이라는 3개의 신호가 뉴런에 입력되어, 각 신호에 가중치를 곱한 후, 다음 뉴런에 전달된다. 위 그림을 더 간결한 형태로 작성해보자.
위 식은 입력 신호의 총합이 h(x)라는 함수를 거쳐 변환되고, 그 변환된 값이 y의 출력이 됨을 보여준다. 그리고 h(x) 함수는 입력이 0을 넘으면 1을 돌려준다.
h(x)함수가 등장했는데, 이처럼 입력 신호의 총합을 출력 신호로 변환하는 함수를 일반적으로 활성화 함수(activation function)라 한다.
활성화 함수
위에서 소개한 활성화 함수는 임계값을 경계로 출력이 바뀌는데, 이런 함수를 계단 함수(step function)라 한다. 그래서 퍼셉트론에서는 활성화 함수로 계단 함수를 이용한다 라고 할 수 있다.
다음은 신경망에서 자주 이용하는 활성화 함수인 시그모이드 함수(sigmoid function)를 나타낸 식이다.
신경망에서는 활성화 함수로 시그모이드 함수를 이용하여 신호를 변화하고, 그 변환된 신호를 다음 뉴런에 전달한다.
파이썬으로 계단 함수를 구현해보겠다.
import numpy as np
def step_function(x):
y=x>0 #return type는 bool형
return y.astype(np.int)
step_function(np.array([3,-1,2]))
import matplotlib.pyplot 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)
plt.show()
시그모이드 함수를 구현해보겠다.
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()
시그모이드 함수와 계단 함수를 비교해보자. 먼저 '매끄러움'의 차이가 있다. 시그모이드 함수의 이 매끈함이 신경망 학습에서 아주 중요한 역할을 하게 된다. 이러한 이유로 신경망에서는 연속적인 실수가 흐른다.
신경망에서는 활성화 함수로 비선형 함수를 사용해야 한다. 선형함수를 사용하면 안되는 것이다. 왜냐하면 선형 함수를 이용하면 신경망의 층을 깊게 하는 의미가 없어지기 때문이다. 예를 들어서 h(x)=cx를 활성화 함수로 사용한 3층 네트워크를 생각해보자. 이를 식으로 나타내면 y(x)=h(h(h(x)))가 된다. 이 계산은 y(x)=c*c*c*x처럼 곱셈을 세 번 수행하지만 실은 y(x)=ax와 똑같은 식이다. a=c^3이라고만 하면 끝이다. 즉 은닉층이 없는 네트워크로 표현할 수 있다. 이 예처럼 선형 함수를 이용해서는 여러 층으로 구성하는 이점을 살릴 수 없다. 그래서 층을 쌓는 혜택을 얻고 싶다면 활성화 함수로는 반드시 비선형 함수를 사용해야한다.
최근에는 ReLU(Rectified Linear Unit,렐루) 함수를 주로 이용한다. ReLU는 입력이 0을 넘으면 그 입력을 그대로 출력하고, 0 이하면 0을 출력하는 함수이다.
def relu(x):
return np.maximum(0,x)
신경망에서의 행렬 곱
3층 신경망 구현하기
넘파이의 다차원 배열을 사용해서 구현해보자.
x=np.array([1.0,0.5])
w1=np.array([[0.1,0.3,0.5],[0.2,0.4,0.6]])
B1=np.array([0.1,0.2,0.3])
print(x.shape) #(2,3)
print(w1.shape) #(2,)
print(B1.shape) #(3,)
A1=np.dot(x,w1) +B1
이어서 1층의 활성화 함수에서의 처리를 살펴보자.
위 그림과 같이 은닉층에서의 가중치 합(가중 신호와 편향의 총합)을 a로 표기하고 활성화 함수h()로 변환된 신호를 z로 표기한다. 여기에서 활성화 함수로 시그모이드 함수를 사용하기로 한다.
Z1=sigmoid(A1)
print(A1)
print(Z1)
출력층의 활성화 함수는 풀고자 하는 문제의 성질에 맞게 정한다. 예를 들어 회귀에는 항등함수를, 2클래스 분류에는 시그모이드 함수를, 다중 클래스 분류에는 소프트맥스 함수를 사용하는 것이 일반적이다.
def init_network():
network={}
network['W1']=np.array([[0.1,0.3,0.5],[0.2,0.4,0.6]])
network['b1']=np.array([0.1,0.2,0.3])
network['W2']=np.array([[0.1,0.4],[0.2,0.5],[0.3,0.6]])
network['b2']=np.array([0.1,0.2])
network['W3']=np.array([[0.1,0.3],[0.2,0.4]])
network['b3']=np.array([0.1,0.2])
return network
def forward(network,x):
W1,W2,W3=network['W1'],network['W2'],network['W3']
b1,b2,b3=network['b1'],network['b2'],network['b3']
a1=np.dot(x,W1)+b1
z1=sigmoid(a1)
a2=np.dot(z1,W2) + b2
z2=sigmoid(a2)
a3=np.dot(z2,W3) +b3
y=identity_function(a3)
return y
network=init_network()
x=np.array([1.0,0.5])
y=forward(network,x)
print(y)
함수 이름을 forward라 한 것은 신호가 순방향(입력에서 출력 방향)으로 전달됨(순전파)을 알리기 위함이다.
출력층 설계하기
신경망은 분류와 회귀 모두에 이용할 수 있다. 다만 둘 중 어떤 문제냐에 따라 출력층에서 사용하는 활성화 함수가 달라진다. 일반적으로 회귀에는 항등 함수를, 분류에는 소프트맥스 함수를 사용한다.
항등 함수는(identity function)은 입력을 그대로 출력한다.
소프트맥스 함수(softmax function)의 식은 다음과 같다.
소프트 맥스 함수를 구현해보자.
a=np.array([0.3,2.9,4.0])
exp_a=np.exp(a)
print(exp_a)
sum_exp_a=np.sum(exp_a)
print(sum_exp_a)
y=exp_a/sum_exp_a
print(y)
def softmax(a):
exp_a=np.exp(a)
sum_exp_a=np.sum(exp_a)
y=exp_a/sum_exp_a
return y
위에서 구현한 softmax() 함수의 코드는 컴퓨터로 계산할 때는 결함이 있다. 바로 오버플로 문제이다. 소프트맥스 함수는 지수 함수를 사용하는데, 지수 함수란 것이 쉽게 아주 큰 값을 낸다. 이렇게 큰 값끼리 나눗셈을 하면 수치가 불안정 해진다.
이 문제를 해결하도록 소프트맥스 함수 구현을 개선해보자.
위 식이 말하는 것은 소프트맥스의 지수 함수를 계산할 때 어떤 정수를 더해도(혹은 빼도) 결과는 바뀌지 않는다는 것이다. 여기서 C'에 어떤 값을 대입해도 상관없지만, 오버플로를 막는 목적으로는 입력 신호 중 최댓값을 이용하는 것이 일반적이다.
a=np.array([1010,1000,990])
print(np.exp(a) / np.sum(np.exp(a)))
c=np.max(a)
print(a-c)
np.exp(a-c) / np.sum(np.exp(a-c))
소프트맥스 함수의 특징
소프트맥스 함수의 출력은 0에서 1.0 사이의 실수이다. 또, 소프트맥스 함수 출력의 총합은 1이다. 출력 총합이 1이 된다는 점은 소프트맥스 함수의 중요한 성질이다. 이 성질 덕분에 소프트맥스 함수의 출력을 '확률'로 해석할 수 있다. 여기서 주의점은 소프트맥스 함수를 적용해도 각 원소의 대소 관계는 변하지 않는다. 이는 지수 함수가 단조 증가 함수이기 때문이다.
신경망을 이용한 분류에서는 일반적으로 가장 큰 출력을 내는 뉴런에 해당하는 클래스로만 인식한다. 그리고 소프트맥스 함수를 적용해도 출력이 가장 큰 뉴런의 위치는 달라지지 않는다. 결과적으로 신경망으로 분류할 때는 출력층의 소프트맥스 함수를 생략해도 된다. 현업에서도 지수 함수 계산에 드는 자원낭비를 줄이고자 출력층의 소프트맥스 함수는 생략하는 것이 일반적이다.
'Deep learning > 모델 구현' 카테고리의 다른 글
6. 신경망 학습 (3) - 학습 알고리즘 구현하기 (0) | 2021.04.20 |
---|---|
5. 신경망 학습 (2) - 기울기 (0) | 2021.04.20 |
4. 신경망 학습 (1) - 손실함수 (0) | 2021.04.19 |
3. 신경망(2) - 손글씨 숫자 인식 (0) | 2021.04.19 |
1. 퍼셉트론 (0) | 2021.04.17 |