[밑바닥부터 시작하는 딥러닝] - Chapter 3. 신경망

Dreamer ·2021년 6월 28일
0

위 자료는 "밑바닥부터 시작하는 딥러닝, 사이토 고키 저, 한빛미디어 출판 도서를 참고하여 작성하였습니다.

3.1 퍼셉트론에서 신경망으로

3.1.1 신경망의 예

신경망을 그림으로 나타내면 아래 그림처럼 된다. 여기에서 가장 왼쪽 줄을 입력층, 맨 오른쪽 줄을 출력층, 중간 줄을 은닉층이라고 한다. 은닉층의 뉴런은 사람 눈에는 보이지 않기에 '은닉'이라고 한다. 신경망은 뉴런이 연결되는 방식은 퍼셉트론과 달라진 것이 없다.

3.1.2 퍼셉트론 복습

x1과 x2라는 두 신호를 입력받아 y를 출력하는 퍼셉트론을 설명하면 다음과 같다.

y(출력값)은 "b+w1x1+w2x2"의 값에 따라 달라진다.

여기에서 b는 편향을 나타내는 매개변수로, 뉴런이 얼마나 쉽게 활성화 되느냐를 제어한다.
한편, w1, w2는 각 입력 신호의 가중치를 나타내는 매개변수로, 각 신호의 영향력을 제어한다.

"b+w1x1+w2x2" 값이 0보다 작거나 같으면 y(출력값은)은 0이 산출되고, 0보다 클 경우엔 1이 출력된다. 위 퍼셉트론을 그림으로 표현하면 다음과 같다.

편향의 입력 신호는 항상 1이기 때문에 그림에서는 해당 뉴런을 회색으로 표현하였다.

위 퍼셉트론을 간단한 수식으로 표현하면 다음과 같다.
조건 분기의 동작(0을 넘으면 1을 출력하고, 그렇지 않으면 0을 출력)을 하나의 함수로 나타낸다. 이 함수를 h(x)라 하면 다음과 같은 수식으로 표현할 수 있다.

입력 신호의 총합이 h(x)라는 함수를 거쳐 변환되어, 그 변환된 값이 y의 출력이 된다. h(x) 함수는 입력이 0을 넘으면 1을 돌려주고 그렇지 않으면 0을 돌려준다.

3.1.3 활성화 함수의 등장

위에서 나온 h(x) 함수처럼 입력 신호의 총합을 출력 신호로 변환하는 함수를 일반적으로 "활성화 함수(Activation Function)이라고 한다.
활성화 함수는 입력 신호의 총합이 활성화를 일으키는지를 결정하는 역할을 한다.

그림을 통해 알 수 있듯이, 가중치 신호를 조합한 결과가 a라는 노드가 되고, 활성화 함수 h()를 통과하여 y라는 노드로 변환된다.
이 활성화 함수가 퍼셉트론에서 신경망으로 가는 길잡이다.

3.2 활성화 함수

활성화 함수는 임계값을 경계로 출력이 바뀌는데, 이런 함수를 계단 함수(step function)라 한다. 계단 함수외의 다른 함수들을 소개한다.

3.2.1 시그모이드 함수(Sigmoid Function)

신경망에서 흔히 보이는 함수인 시그모이드 함수이다.
시그모이드 함수를 수식으로 표현하면 다음과 같다.

h(x)=1/1+exp(x)h(x) = 1/1+exp(-x)

위 식에서 exp(-x)는 e^-x를 의미하며, e는 자연상수로 2.7182...의 값을 갖는 실수이다.

3.2.2 계단 함수 구현하기

def step_function(x):
    if x >0 :
       return 1
    else:
       return 0 

위 함수에서 인수 x는 실수만 받아들인다. 즉, 넘파이 배열을 인수로 넣을 수 없다. 넘파이 배열을 받기 위해서는 다음과 같이 코드를 수정해야 한다.

def step_function(x) : 
    y = x > 0
    return y.astype(np.int)

x가 0보다 크면 True, 0보다 작으면 False로 변환된 bool 배열을 int형으로 바꿔주면 True는 1로, False는 0으로 변환된다.

3.2.3 계단 함수의 그래프

그림에서 보듯 계단 함수는 0을 경계로 출력이 0에서 1로 바귄다.

3.2.4 시그모이드 함수 구현하기

def sigmoid(x):
    return 1 / (1+np.exp(-x)

3.2.5 시그모이드 함수와 계단 함수 비교

시그모이드 함수는 입력에 따라 출력이 연속적으로 변화하지만 계단 함수는 0을 기점으로 출력이 0 또는 1로 바뀌어 버린다.
즉, 퍼셉트론에서는 뉴런 사이에 0 또는 1이 흘렀다면, 신경망에서는 연속적인 실수가 흐른다.
단, 입력이 중요하면 큰 값을 출력하고 입력이 중요하지 않으면 작은 값을 출력한다는 점, 출력값이 0~1 사이라는 점은 동일하다.

3.2.6 비선형 함수

신경망에서는 활성화 함수로 비선형 함수를 사용해야 한다. 선형 함수를 이용하면 신경망의 층을 깊에 하는 의미가 없어지기 때문이다.

수식으로 설명하자면, 선형 함수인

h(x)=cxh(x) = cx

를 활성화 함수로 사용한 3층 네트워크를 생각해보자.
3층으로 층이 깊어진다고 해도

y(x)=h(h(h(x)))y(x) =h(h(h(x)))

가 된다.
즉, y(x) = cccx임에도 실은 y(x) = ax임.

3.2.7 ReLU 함수

ReLU는 아래 그래프에서도 볼 수 있듯이 입력이 0을 넘으면 그대로 출력하고, 0 이하이면 0을 출력한다.

3.3 다차원 배열의 계산

3.3.1 다차원 배열

넘파이 배열(np.array)은 N차원 배열을 작성 가능.
1차원 배열은 벡터(Vector), 2차원 배열은 행렬(Matrix) 라고 부름.
3차원 이상의 배열을 '다차원 배열'이라 지칭.

예로 다음과 같은 행렬이 있다.

이를 파이썬으로 구현하면 다음과 같다.

np.ndim() 함수로 차원을 확인 가능.
shape로 배열의 형상 확인 가능함.
2차원 배열일 때는 (4,3), 3차원 배열일 때는 (4,3,2) 같은 튜플로 결과 반환됨.

3.3.2 행렬의 곱

행렬의 곱은 넘파이 함수 np.dot()으로 계산한다.
np.dot()은 입력이 1차원 배열이면 벡터를, 2차월 배열이면 행렬 곱을 계산한다.

*np.dot(A,B) =! np.dot(B,A)

행렬 A와 행렬 B의 곱이 이뤄지려면 행렬 A의 "열 수"와 행렬 B의 "행 수"가 같아야 한다.

ex) 행렬 A : 3*2 X 행렬 B : 2*4 -> 행렬 C : 3*4

3.4 3층 신경망 구현하기

3.4.3 구현 정리

아래의 3층 신경망을 파이썬 코드로 나타내면 다음과 같다.

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) 

init_network() 함수는 가중치과 편향을 초기화하고 이들을 딕셔너리 변수인 Network에 저장한다. 이 딕셔너리 변수 network에는 각 층에 필요한 매개변수(가중치와 편향)을 저장한다.
그리고 forward() 함수는 입력 신호를 출력으로 변환하는 처리 과정 구현한다. 신호가 순방향으로 전달되는 순전파이다. (입력->출력)

3.5 출력층 설계하기

3.5.1 향등 함수와 소프트맥스 함수 구현하기

일반적으로 회귀 문제에서 사용되는 향등 함수(identify function)는 입력을 그대로 출력한다. 입력과 출력이 항상 같다는 뜻이다.
분류 문제에 주로 사용되는 소프트맥스 함수(softmax function)은 다음과 같다.

exp(x)는 ex 을 뜻하는 지수 함수이다.
n은 출력층의 뉴런 수, Yk는 그 중 k번째 출력임을 뜻한다. 소프트맥스의 출력은 모든 입력 신호로부터 화살표를 받는다.

소프트맥스 함수를 파이썬으로 표현하면 다음과 같다.

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

3.5.2 소프트맥스 함수 구현 시 주의점

소프트맥스 함수의 가장 큰 결함은 오버플로 문제이다. 지수 함수는 아주 큰 값을 내뱉기 때문에 이런 큰 값끼리 나눗셈을 하면 결과 수치가 '불안정'해진다.

이 문제를 해결하도록 소프트맥스 수식을 개선하면 다음과 같다.

즉, C라는 임의의 정수를 분자와 분모 양쪽에 곱한 후, C를 지수 함수 exp() 안으로 옮겨 logC로 만든다. 원래 소프트맥스의 지수 함수를 계산할 때 어떤 정수를 더하거나 빼도 결과는 바뀌지 않는다. 다만, C는 오버플로를 막을 목적으로 입력 신호 중 최대값을 이용하는 것이다.

profile
To be a changer who can overturn world

0개의 댓글