오뚝이개발자

[CH3] 신경망 본문

AI/밑바닥딥러닝1

[CH3] 신경망

땅어 2020. 6. 13. 18:39
728x90
300x250

신경망이 퍼셉트론과 다른 점


퍼셉트론에선 가중치 매개변수의 값을 사람이 직접 입력해주었지만 신경망에선 학습을 통해 가중치 매개변수의 값을 데이터로부터 자동으로 학습

 

신경망의 구조


 

신경망의 구조

 

편향을 명시한 신경망


편향을 명시한 신경망 구조

편향(bias)의 입력신호는 항상 1이다.

 

활성화 함수(activation function)의 등장


활성화 함수는 입력신호의 총합이 활성화를 일으키는지를 결정하며 퍼셉트론에서 신경망으로 가는 열쇠이다. 위의 그림과 아래의 그림을 비교해보면 다음층으로 가기전 입력신호의 총합이 h()라는 함수를 거쳐가는 것을 알 수 있다. 이것이 활성화 함수이다.

활성화함수를 도입한 신경망 구조

이를 수식으로 나타내보면 다음과 같다. (이 예시에서는 활성화함수를 step function이라 가정함.)

 

활성화 함수의 종류


1. 계단함수(step function)

import numpy as np
import matplotlib.pyplot as plt

# 계단함수 구현
def step_function(x):
    if x>0:
        return 1
    else:
        return 0

# 함수 입력으로 numpy 배열 받을 수 있는 계단함수 구현
def step_function2(x):
    y=x>0
    return y.astype(np.int) # astype : 넘파이 배열의 자료형 변환
x = np.array([-1.0, 1.0, 2.0])
print(step_function2(x))    # [0 1 1]

# 계단함수 그래프 그리기
def draw_step_function(x):
    return np.array(x>0, dtype=np.int)
x = np.arange(-5.0, 5.0, 0.1)
y = draw_step_function(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1) # y축 범위 지정
plt.show()

 

2. 시그모이드 함수(sigmoid function)->'시그모이드'란 S자란 뜻!

import numpy as np
import matplotlib.pyplot as plt

# 시그모이드 함수 구현
def sigmoid(x):
    return 1/(1+np.exp(-x))
x1 = np.array([-1.0, 1.0, 2.0])
print(sigmoid(x1))   # [0.26894142 0.73105858 0.88079708]

# 시그모이드 함수 그래프 그리기
x2 = np.arange(-5.0, 5.0, 0.1)
y = sigmoid(x2)
plt.plot(x2, y)
plt.ylim(-0.1, 1.1)
plt.show()

 

3. ReLU 함수(Rectified Linear Unit)

import numpy as np
import matplotlib.pyplot as plt

# ReLU 함수 구현
def relu(x):
    return np.maximum(0, x)
x1 = np.array([-1.0, 1.0, 2.0])
print(relu(x1))   # [0. 1. 2.]

# ReLU 함수 그래프 그리기
x2 = np.arange(-5.0, 10.0, 0.1)
y = relu(x2)
plt.plot(x2, y)
plt.ylim(-0.1, 10.0)
plt.show()

 

시그모이드 vs. 계단함수


  • 차이점

    • '매끄러움'의 정도(시그모이드는 입력에 따라 출력이 '연속적'으로 변화)

  • 공통점

    • 입력이 작을 때 출력이 0에 가깝고(혹은 0), 커지면 1에 가까워짐(혹은 1)

    • 출력은 0과 1 사잇값

    • 비선형 함수

  • 비선형 함수란?

    • 직선 1개로 그릴 수 없는 함수

    • 선형함수 ex) f(x) = ax + b

 

신경망의 활성화함수로 비선형함수를 써야 하는 이유


선형함수의 문제는 층을 아무리 깊게 해도 '은닉층이 없는 네트워크'로 똑같이 구현이 가능해 층을 깊게 쌓는 혜택을 누릴 수 없다.

ex) 선형함수 h(x) = cx를 활성화함수로 사용한 3층 네트워크의 경우 y = h(h(h(x))) = c^3x = ax로 표현 가능.

 

신경망의 행렬곱


# 신경망의 행렬곱
X = np.array([1,2])
W = np.array([[1,2,3], [4,5,6]])
Y = np.dot(X,W)
print(Y)    # [5 12 15]

행렬 간 곱셈을 할 때는 열과 행의 수를 맞추어 주는 것이 중요하다. 이 때, 행렬곱에선 교환법칙이 성립하지 않기 때문에  X x W는 W x X와 동일하지 않다는 것을 주의하자.

 

3층 신경망 구현해보기


import numpy as np

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

def identity_function(x):
    return x

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)    # [0.31682708 0.69627909]

 

1. 입력층에서 1층으로 신호전달

 

2. 1층에서 2층으로 신호전달

 

3. 2층에서 출력층으로 신호전달

 

출력층 설계


일반적으로 회귀문제의 경우 항등 함수를, 분류문제의 경우 소프트맥스 함수를 사용한다.

회귀문제 : 입력데이터에서 연속적인 수치 예측

분류문제 : 데이터가 어느 클래스에 속하는지 예측

 

1. 항등함수

=> 입력을 그대로 출력

2. 소프트맥스 함수

softmax 함수의 수식

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

    • 지수함수를 사용하기 때문에 수가 커져 '오버플로' 문제 발생할 수 있다.

    • 해결책

     

overflow를 막기 위한 softmax 보정식

 

C'은 overflow를 예방할 수 있는 어떠한 수를 써도 상관 없지만 일반적으로 입력신호 중 가장 큰 값을 사용한다.

import numpy as np

# softmax 함수 구현
def softmax1(a):
    exp_a = np.exp(a)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a

    return y

# 오버플로를 고려한 softmax
def softmax2(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

 

Softmax 함수의 특징


  • 출력은 0과 1사이의 실수값(출력 총합은 1) -> '확률'로 결과해석 가능

  • 문제를 '통계적으로' 대응 가능

  • EX) y = [0.018, 0.245, 0.737]일 때, 해당 데이터는 74%의 확률로 2번째 클래스, 25%의 확률로 1번째 클래스이다.

 

출력층의 뉴런 수 정하기


해결하고자 하는 문제에 맞게 설정하면 된다. 

Ex) 입력이미지가 0~9 중의 하나로 분류되는 문제라면 출력층의 뉴런 수는 10개가 된다.

 

정규화(Normalization)


정규화란 데이터를 특정 범위로 변환하는 것.

Ex) MNIST 손글씨 분류 예제에서 0~255의 픽셀값을 0~1로 정규화시켜 데이터값의 분포를 일정 범위내로 조정하는 경우

 

배치(Batch) 처리


입력데이터를 하나씩 처리하지 않고 여러 개 묶어 처리하는 것

why?) 큰 배열을 한꺼번에 계산하는 것이 작은 배열을 여러 번 계산하는 것보다 faster(느린 I/O를 통해 데이터를 읽는 횟수에 비해, 빠른 CPU계산을 수행하는 비율이 높아져서 최종적으로 속도 UP)

 

MNIST 손글씨 분류 문제 구현


아래의 코드는 MNIST의 손글씨 분류 예제를 구현한 것이다. 해당 코드를 구동시키기 위해선 여러 모듈들이 필요한데 관련해서는 깃헙 링크를 참고하기 바란다.

import sys, os
import pickle
sys.path.append(os.pardir)
from dataset.mnist import load_mnist
from sigmoid import sigmoid
from softmax import softmax2
import numpy as np

# 처음 한 번은 몇 분 정도 걸립니다.
# normalize 옵션은 0~255의 픽셀값을 0.0~1.0 사이의 값으로 정규화
# flatten 옵션은 1차원 배열로 만드는 것
# one_hot_label 옵션은 정답을 뜻하는 원소만 1인 배열로 만드는 것(False인 경우 '7'과 같이 숫자로 저장)
def get_data():
    (x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=True, one_hot_label=False)

    # 각 데이터의 형상 출력
    print(x_train.shape)    # (60000, 784)
    print(t_train.shape)    # (60000,)
    print(x_test.shape)     # (10000, 784)
    print(t_test.shape)     # (10000,)

    return x_test, t_test

def init_network():
    with open("sample_weight.pkl", 'rb') as f:
        network = pickle.load(f)

    return network

def predict(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 = softmax2(a3) 

    return y

# 배치처리 도입하지 않은 경우
x, t = get_data()
network = init_network()

accuracy_cnt = 0
for i in range(len(x)):
    y = predict(network, x[i])
    p = np.argmax(y) # 확률(y값)이 가장 높은 원소의 인덱스를 얻는다.
    if p == t[i]:
        accuracy_cnt += 1

print("Accuracy:", str(float(accuracy_cnt/len(x))))

# 배치처리 도입
x, t = get_data()
network = init_network()

batch_size = 100
accuracy_cnt = 0

for i in range(0, len(x), batch_size):
    x_batch = x[i:i+batch_size]
    y_batch = predict(network, x_batch)
    p = np.argmax(y_batch, axis=1)
    accuracy_cnt += np.sum(p == t[i:i+batch_size])

print("Accuracy:", str(float(accuracy_cnt/len(x))))


 

 

 

 

728x90
300x250

'AI > 밑바닥딥러닝1' 카테고리의 다른 글

[CH6] 학습 관련 기술들  (0) 2020.06.21
[CH5] 오차역전파법  (0) 2020.06.20
[CH4] 신경망 학습  (0) 2020.06.19
[CH2] 퍼셉트론(Perceptron)  (0) 2020.06.10
[CH1] Numpy, Matplotlib 실습  (0) 2020.06.09
Comments