반응형

출처: https://sacko.tistory.com/37?category=632408

 

간단하게 MNIST 데이터를 이용해서 숫자를 구분할 수 있는 인공신경망 구현을 실습해보았다. 그리고 앞으로 계속 사용되는 주요 개념들에 대해서 복습을 진행하였다.

Sigmoid 함수, ReLU 함수, Softmax 함수, Batch 개념 등을 또한 다루었다. 앞으로 계속 중요하게 다루는 개념이므로 직접 코드를 치면서 짚어볼 필요가 있었다.

 

 

신경망 복습 Neural Network

문과생도 이해하는 딥러닝 (4)

 

1. Sigmoid 함수

지난 포스팅에서 시그모이드 함수를 다루었고 뉴런의 활성화함수로서 계단함수와 비교했을 때 비선형적으로 매끄럽게 값을 출력할 수 있기 때문에 사용한다고 하였으며, 이진 분류 문제를 위해 주로 사용된다고 하였다.

 

아래는 Jupyter Notebook을 사용하여 시그모이드 함수를 표현해보았다

 

In [2]:

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

In [3]:

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

In [4]:

sigmoid(x)

Out[4]:

array([ 0.26894142, 0.73105858, 0.88079708])

In [5]:

x = np.arange(-5.0, 5.0, 0.1) y = sigmoid(x) plt.plot(x, y) plt.plot([0,0],[1.0,0.0], ':') plt.ylim(-0.1, 1.1) #y축 범위 지정 plt.title('Sigmoid Function') plt.show()

 

 

 

2. ReLU 함수

Relu 함수는 입력 값이 0 이상이면 입력 값을 그대로 출력하는 함수로 회귀 문제를 다룰 때 주로 사용된다. 최근에 주로 시그모이보다는 ReLu 함수를 주로 이용한다고 한다.

 

 

 

In [6]:

def relu(x): return np.maximum(0, x)

In [7]:

x = np.arange(-2.0, 2.0, 0.2) y = relu(x) plt.plot(x, y) plt.plot([0,0],[1.0,0.0], ':') plt.ylim(-0.1, 1.1) #y축 범위 지정 plt.title('Sigmoid Function') plt.show()

 

 

 

 

 

3. 다차원 배열 다루기

인공신경망의 Layer들은 파이썬에서 NumPy 배열로 표현되며 가중치 매트릭스를 나타낸다. 인공신경망은 기본적으로 이 배열들 간의 곱 연산을 통해서 출력 계층에서 목표로 했던 값을 구한다.

 

 

3.1 다차원배열

In [8]:

A = np.array([1,2,3,4]) print(A) print(np.ndim(A)) print(A.shape) print(A.shape[0])

[1 2 3 4] 1 (4,) 4

In [9]:

B = np.array([[1,2],[3,4],[5,6]]) print(B) print(np.ndim(B)) print(B.shape)

[[1 2] [3 4] [5 6]] 2 (3, 2)

 

 

 

3.2 행렬의 내적

행렬 곱을 위해서는 (a,b) * (c,d) 일 때

1) b = c 이어야 하고 2) 결과 행렬은 (a,d) 이다

In [10]:

A = np.array([[1,2],[3,4]]) A.shape

Out[10]:

(2, 2)

In [11]:

B = np.array([[5,6],[7,8]]) B.shape

Out[11]:

(2, 2)

In [12]:

np.dot(A,B)

Out[12]:

array([[19, 22], [43, 50]])

 

 

행렬의 내적은 순서가 바뀌면 결과도 바뀐다

In [13]:

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

Out[13]:

array([[False, False], [False, False]], dtype=bool)

In [14]:

A = np.array([[1,2],[3,4],[5,6]]) A.shape

Out[14]:

(3, 2)

In [15]:

B = np.array([7,8]) B.shape

Out[15]:

(2,)

In [16]:

np.dot(A,B)

Out[16]:

array([23, 53, 83])

 

 

 

3.3 신경망의 내적

In [17]:

X = np.array([1,2]) X.shape

Out[17]:

(2,)

In [18]:

W = np.array([[1,3,5],[2,4,6]]) print(W)

[[1 3 5] [2 4 6]]

In [19]:

np.dot(X,W)

Out[19]:

array([ 5, 11, 17])

 

 

 

4. 신경망 구현하기 

신경망 3층 구하기

In [20]:

# 첫번째 은닉층 계산 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(W1.shape) print(X.shape) print(B1.shape)

(2, 3) (2,) (3,)

In [21]:

A1 = np.dot(X, W1) + B1 A1

Out[21]:

array([ 0.3, 0.7, 1.1])

In [22]:

Z1 = sigmoid(A1) Z1

Out[22]:

array([ 0.57444252, 0.66818777, 0.75026011])

In [23]:

# 두번째 은닉층 계산 W2 = np.array([[0.1, 0.4],[0.2,0.5],[0.3,0.6]]) B2 = np.array([0.1, 0.2])

In [24]:

print(Z1.shape) print(W2.shape) print(B2.shape)

(3,) (3, 2) (2,)

In [25]:

A2 = np.dot(Z1, W2) + B2 Z2 = sigmoid(A2) Z2

Out[25]:

array([ 0.62624937, 0.7710107 ])

In [26]:

def identity_function(x): return x

In [27]:

# 출력층 계산 W3 = np.array([[0.1, 0.3],[0.2,0.4]]) B3 = np.array([0.1,0.2]) A3 = np.dot(Z2, W3)+B3 Z3 = identity_function(A3) Z3

Out[27]:

array([ 0.31682708, 0.69627909])

 

In [28]:

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 foward(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 = foward(network, x) print(y)

[[ 0.31682708 0.69627909]]

 

 

 

5. Softmax 함수

멀티 분류에서 소프트맥스 함수를 사용한다는 이야기를 많이 들었다. 카테고리 분류 등에서 마지막 출력층에서 활성화함수로 사용하여 카테고리 별로 확률과 비슷한 값이 나와 확률과 비슷하게 해석한다고 한다.

 

In [29]:

a = np.array([0.3, 2.9, 4.0]) exp_a = np.exp(a) print(exp_a)

[ 1.34985881 18.17414537 54.59815003]

In [30]:

sum_exp_a = np.sum(exp_a) print(sum_exp_a)

74.1221542102

In [31]:

y = exp_a / sum_exp_a print(y)

[ 0.01821127 0.24519181 0.73659691]

In [32]:

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

 

 

 

소프트맥스 함수 구현시 주의할 점은 소프트맥스는 지수함수를 사용하기 때문에 값이 급격하게 증가하여 오버플로(overflow) 문제가 발생한다. 컴퓨터는 계산할 수 있는 또는 표현할 수 있는 값 이상의 값은 계산/표현이 안되기 때문에 문제가 생긴다.

 

 

In [33]:

a = np.array([1010, 1000, 990]) np.exp(a) / np.sum(np.exp(a))

/home/db2/anaconda3/lib/python3.6/site-packages/ipykernel_launcher.py:2: RuntimeWarning: overflow encountered in exp /home/db2/anaconda3/lib/python3.6/site-packages/ipykernel_launcher.py:2: RuntimeWarning: invalid value encountered in true_divide

Out[33]:

array([ nan, nan, nan])

 

 

소프트맥수 함수식을 보면 exp(a)에 a 값에 임의의 상수를 더해도 결과는 변하지 않는다. exp(a+c). 오버플로 문제를 없애기 위해서 임의의 상수를 더해 a의 값이 작아지도록 만든다

 

In [34]:

c = np.max(a) print(a - c) np.exp(a-c)/np.sum(np.exp(a-c))

[ 0 -10 -20]

Out[34]:

array([ 9.99954600e-01, 4.53978686e-05, 2.06106005e-09])

 

 

따라서 수정된 소프트맥수 함수는 다음과 같다

In [35]:

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

 

 

소프트맥스 함수는 0과 0.1 사이의 실수를 출력하며 전체 결과 값의 합은 1이기 때문에 소프트맥스 함수의 출력값을 '확률'로 해석하기도 함

 

 

6. MNIST 실습

 

 

import sys, os from mnist import load_mnist

 

(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False)

 

print(x_train.shape) print(t_train.shape) print(x_test.shape) print(t_test.shape)

 

(60000, 784) (60000,) (10000, 784) (10000,)

 

from PIL import Image def img_show(img): return Image.fromarray(np.uint8(img)) (x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize = False) img = x_train[0] label = t_train[0] print(label) print(img.shape) img = img.reshape(28, 28) print(img.shape) img_show(img)

 

5 (784,) (28, 28)

 

 

 

import pickle def get_data(): (x_train, t_train), (x_test, t_test) = \ load_mnist(normalize=True, flatten=True, one_hot_label=False) 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 = softmax(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) if p == t[i]: accuracy_cnt += 1 print("Accuracy:" + str(float(accuracy_cnt)/len(x)))

 

Accuracy:0.9352

 

print('predicted:',p,',','real:',t[i])

 

predicted: 6 , real: 6

 

 

 

입력데이터를 특정 범위로 변환하는 처리(정규화normalization)를 하고 이렇게 특정 변환을 가하는 것을 전처리(preprocessing(이라고 한다. 신경망은 성능을 개선하고 학습속도를 개선하기 위해 전처리를 많이 한다. 단순 정규화를 수행하기도 하지만 데이터 전체의 분포를 고려해 전처리하는 경우도 많음. 표준화(standardization), 백색화(whitening) 등이 있음

 

 

 

7. 배치 Batch

 

 

 

배치라 함은 묶음이라고 보면 되는데, 일반적으로 100개의 샘플에 대해서 예측을 한다면 각 샘플에 예측식을 계산하여 예측을 하는데 이 때 약 총 100번의 계산을 하게 된다.

배치를 적용해서 약 배치의 개수를 50이라 한다면 예측식에 50개의 샘플을 넣어 계산하도록 하여 결과적으로 컴퓨터가 총 2번의 계산을 하게 된다.

이는 컴퓨터의 과부화를 줄여 원래 배열보다 큰 배열로 계산을 하게 하여 연산을 빠르게 해준다. 배치를 처리하면 데이터를 효율적으로 빠르게 적절하게 묶어서 학습할 수 있게 된다

예를 들어 신경망의 내적으로 본다면

[[ Batch 1 ]]

(1, 784) → (784, 50) → (50, 100) → (100, 10) → (10, 1)

하나의 샘플(행)에 대해서만 한다면 위와 같이 신경망이 구성될 것이다

[[ Batch 100 ]]

만약 배치를 100으로 한다면

(100, 784) → (784, 50) → (50, 100) → (100, 10) → (10, 100)

위와 같이 최종 결과는 100개 샘플에대한 10개 뉴런에 대한 아웃풋이 나온다.

앞에서 말했던 것처럼 (a,b) 와 (c,d)의 행렬 곱에서는 b와 c가 같아야하고, 결과는 (a,d)의 형태로 나온다.

 

# batch1 x, _ = get_data() network = init_network() W1, W2, W3 = network['W1'], network['W2'], network['W3'] print(x.shape) print(x[0].shape) print(W1.shape) print(W2.shape) print(W3.shape)

 

(10000, 784) (784,) (784, 50) (50, 100) (100, 10)

 

# batch100 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)))

 

Accuracy:0.9352

반응형
반응형

출처: https://sacko.tistory.com/19?category=632408

 

이전 시간까지 신경망이 무엇인지 어떻게 생긴 것인지 작동원리 등을 살펴보았다. 이후의 책에서 다루는 내용은 갑자기 난이도가 점프하는 느낌이 있어 필수 개념들을 빠르게 숙지하고 간단하게라도 전체적인 신경망을 끝까지 구현해볼 필요가 있다는 생각이 들어서 조금 더 쉬운 교재에서 내용을 다루고 다시 밑바닥으로 돌아가기로 했다.

 

오차 역전파, 경사하강법

문과생도 이해하는 딥러닝 (3)

 

1. 오차 역전파 Error Backpropagation 

2. 경사하강법

 

1. 오차 역전파   Error Backpropagation

 

지난 시간까지는 Input에서 Output으로 가중치를 업데이트하면서 활성화 함수를 통해서 결과값을 가져오는 것까지 배웠다. 이렇게 쭉 오는 것을 순전파(foward)라고 하며 말 그대로 앞쪽으로 input 값을 전파, 보내는 것이라고 보면 된다. 하지만 우리가 임의로 한 번 순전파 했다고 출력 값이 정확하지는 않을 것이다. 우리가 임의로 설정한 가중치 값이 input에 의해서 한 번 업데이트 되긴 했지만 많은 문제가 있을 수 있다.

 

역전파 방법은 결과 값을 통해서 다시 역으로 input 방향으로 오차를 다시 보내며 가중치를 재업데이트 하는 것이다. 물론 결과에 영향을 많이 미친 노드(뉴런)에 더 많은 오차를 돌려줄 것이다.

 

 

위의 그림을 보면 Input이 들어오는 방향(순전파)으로 output layer에서 결과 값이 나온다. 결과값은 오차(error)를 가지게 되는데 역전파는 이 오차(error)를 다시 역방향으로 hidden layer와 input layer로 오차를 다시 보내면서 가중치를 계산하면서 output에서 발생했던 오차를 적용시키는 것이다.

 

 

한 번 돌리는 것을 1 epoch 주기라고 하며 epoch를 늘릴 수록 가중치가 계속 업데이트(학습)되면서 점점 오차가 줄어나가는 방법이다. 

 

위의 그림을 보면 Output layer에서 나온 결과 값이 가진 오차가 0.6이라고 되어 있다. 이전 노드(뉴런에서) Output layer에 각각 3, 2라는 값을 전달하였기 때문에 Ouput의 Error에 위 노드는 60%, 아래 노드는 40% 영향을 주었다고 볼 수 있다. 균등하게 가중치를 나눠줄 수 있지만 영향을 미친 만큼 오차를 다시 역전파하는게 맞는 것 같다.

 

error 0.6을 0.6, 0.4를 곱하니  위 노드에는 error가 0.36이, 아래 노드에는 0.24가 전달된다. 

 

오차 역전파는 말 그대로 이렇게 오차를 점점 거슬러 올라가면서 다시 전파하는 것을 의미한다.

 

 

 

 

2. 경사하강법 Gradient Descent

 

경사하강법을 왜 소개하는 것인가?

 

앞에서 오차가 역전파되는 것을 알아보았는데 오차를 역전파하여 계속 업데이트 하는 이유는 신경망을 통해 더 나은 결과 값을 내기 위해서 weight를 조정하는데 오차가 영향을 주기 때문이다. 위의 예처럼 간단한 신경망이라면 오차를 계산하는 식은 굉장히 간단할 것이지만 효과적인 신경망은 절대 저렇게 간단하지 않다. 수십, 수백개의 노드(뉴런)이 연결되어서 수많은 가중치의 조합으로 특정 노드의 weight를 계산해야 한다면... 효율적인 방법이 필요할 것이다.

 

경사하강법은 너무나 많은 신경망 안의 가중치 조합을 모두 계산하면 시간이 오래걸리기 때문에 효율적으로 이를 하기위해 고안된 방법입이다. 

 

아래 그림을 보면 조심스럽게 산을 내려가고 있는 한 남성이 보일 것이다. 여기서 소개하고자 하는 경사하강법은 아래의 남성과 같이 조심스럽게 천천히 산을 내려가는 것과 같다.

 

 

 

위와 같이 절벽은 산을 내려갈 수 있는 가장 빠른 방법이다(사진이 하산 하는것 처럼 보이지는 않지만...). 한 발 한 발 조심스럽게 내딛어야 한다. 경사가 급하기 때문에 천천히 내려가며 하산할 때까지 한 발 한 발 내딛는 과정을 반복하게 된다. 경사는 지형의 기울기를 의미한다. 

 

경사하강법을 사용하면 정확한 답을 얻지는 못할 수도 있다. 단계적으로 접근하는 것이기 때문에 만족스러운 정확도에 이를 때까지 계속해서 답을 찾아나가는 방식이다.

 

신경망의 오차를 경사하강법으로 최저 오차를 찾아나가는 방식이며 신경망의 계산 속도를 빠르게 한다. 아래 그림은 이를 보여주는 그래프이다. x축의 오른쪽으로 조금씩 이동하면서 최저 지점을 찾는다.

 

문제는 이 조금씩이라는 정도의 문제다  조금씩이더라도 너무 많이 이동하면 최저 지점을 지나가게 될 것이다. 또는 너무 작은 조금씩은 이동횟수가 많아져서 최저 지점을 찾지 못할 수도 있다.

 

 

 

 

또 다른 문제는 엉뚱한 최저점을 찾을 수 있다는 것이다. 만약 차원이 여러개라고 한다면 위에서 본 것처럼 단순한 2차함수 형태가 되지는 않을 것이다. 아래의 그림과 같이 여러 개의 계곡이 있고 발을 잘못 디딛으면 엉뚱한 계곡으로 갈 수도 있다.

 

이러한 문제를 피하기 위해서는 서로 다른 초기값으로 주어 산을 내려가게 하는 방법으로 신경망의 경우에서는 weight의 초기 값을 다르게 주면서 경사하강법을 활용해본다는 의미이다.

 

아래 그림과 같이 수학적으로 설명하면 ("낮은 쪽의 방향을 찾기 위해 오차함수를 현재 위치에서 미분함") 우리 문과생들은 이런말 잘 모르니까 이렇게 말해도 될 지 모르겠다. 한 발 내려간 시점에서 등산객이 자기가 산을 제대로 내려가고 있는지 주변 경사(기울기)를 보면서 판단하듯이 기울기가 음수인지? 양수인지? 확인하면서 최저지점으로 내려가는 거라고 보면 되지 않을까 싶다.

 

 

출처: https://www.slideshare.net/RickyPark3/2linear-regression-and-logistic-regression

 

 

 

 

경사하강법의 특징

  • 함수의 최저점을 구하기 좋은 방법으로 신경망과 같이 계산해야 하는 양이 많고(선형대수학) 접근하기가 복잡한 수식에서 잘 작동
  • 데이터가 불완전해도 유도리 있게 작동

 

반응형
반응형

출처: https://sacko.tistory.com/17?category=632408

한동안 인스타나 기타 SNS 크로러를 개발하느라고 딥러닝 정리가 조금 소홀했었다. 딥러닝과 집적 관련된 내용은 퍼셉트론 하나였는데 많은 분들이 관심을 가지고 계신 것 같다. 서둘러서 마무리하고 다음 단계로 넘어가야 할 것 같아서 서둘러 포스팅 작성을 시작했다.

 

신경망 Neural Network

문과생도 이해하는 딥러닝 (2)

 

지난 시간에 다루었던 퍼셉트론의 기본 개념과 한계점, 그 극복방안 등에 대해서 다루었다. 퍼셉트론이 가중치를 직접 수동으로 설정하는 작업을 했다는 한계가 있었는데 이를 해결할 방법이 바로 신경망이다. 우리가 딥러닝을 이야기할 때 '자동으로 알아서 학습하는' 이라는 말을 많이 한다. 알아서 가중치 값을 설정하고 조정하는 것이 자동으로 학습한다는 말과 동일하며 이것이 신경망의 가장 큰 특징 중 하나이다.

 

 

1. 퍼셉트론과의 비교

퍼셉트론이 하나의 뉴런 단위로 다루어진다면 각 뉴런이 모여 하나의 뇌가 되는 것과 같은 신경망은 퍼셉트론의 하위요소일 것이다. 단층 퍼셉트론이 하나의 나무와 같이 신경망은 나무가 모여 숲을 이룬 것과 같은 느낌으로 이해하면 된다. 퍼셉트론이라는 말을 어떻게 정의하느냐에 따라 가리키는 것이 달라진다. 일반적으로는 단층 퍼셉트론은 step function(임계값을 넘어섰을 때 출력을 1로 하는 함수)을 활성화 함수로 사용한 모델을 가리킨다. 다층 퍼셉트론은 층이 여러개이며 sigmoid function을 활성화함수로 사용하는 네트워크를 가리킨다.

 

신경망은 아래와 같이 왼쪽부터 Input(입력층), Hidden(은닉층), Output(출력층)으로 표현할 수 있다. 은닉층은 양쪽의 입력층과 출력층과는 달리 우리 눈에는 보이지 않기 때문에 (내부적으로 돌아가고 있는 Black Box와도 같다. 모르기 때문에) 'Hidden(은닉)'이라고 한다. 지난 포스팅에서 봤던 퍼셉트론과는 크게 달라 보이지 않는다.

 

 

2. 활성화 함수 Activation Function

 

지난 포스팅에서 다루었던 퍼셉트론의 개념을 떠올려보자. Input의 값이 weight와 계산하여 다 더하고 사전에 설정한 임계값(threshold)과 비교해서 임계값을 넘으면 Output으로 1을 출력하고, 0을 출력했다.

 

여기서... 함수가 무엇인지 생각해보자. 무언가를 집어넣었을 때 아래에서 보듯이 박스 안에서 어떤 일들이 일어나 결과를 뱉어내준다. 그 어떤 일을 하는 것이 함수이다. 편의점에서 삼각김밥을 사먹을 때 삼각김밥을 전자레인지에 돌리고 꺼내면 뜨겁다. 차가운 나의 삼각김밥(Input)이 전자레인지(Function에 들어갔다가 밖으로 나오니 뜨거워졌다(Output). 활성화 함수도 무언가를 해주는 기능을 가진 것으로 활성화 activation 말 그대로 입력신호의 총합이 활성화를 일으킬지 정하는 역할을 한다. 그 정하는 것을 하는 내용들이 함수 안에 담겨 있는 것이다.

 

 

계단함수 step function

퍼셉트론은 활성화 함수로 step function(계단 함수)를 이용한다. 특정 임계값을 넘기면 활성화되는 함수이다. 아래 왼쪽(a)가 계단 함수이다. 0에서 멈추어있다. 어느 기점에서 1로 바뀐다. 학습과 관련해서 많이 들었을 것이다. 지금 열심히 공부할 때는 실력이 오르는지 모르겠지만 어느 순간 점수가 확 오를 것이라고... 우리는 우리도 모르는 사이에 계단 함수를 배우고 있었다.

 

 

 

시그모이드 함수 sigmoid fuction

앞서 말했듯이 신경망에서 주로 이용하는 활성화 함수는 시그모이드 함수이다. 아래는 시그모이드 함수를 나타낸 식이다. e는 자연상수로 2.7192...의 값을 갖는 실수 이다. 아직은 자연상수가 뭔지 왜 이걸 사용하는지는 모르겠다. 수학적인 개념에 대해서는 아무래도 잘 모는데 이 부분은 나중에 다시 추가할 예정이다. 어쨌든 복잡해 보이는 이 시그모이드 함수도 역시 활성화를 위한 '함수'일 뿐이다. 입력을 받아서 출력을 돌려주는 변환기일 뿐이다.

 

신경망에서는 입력 신호를 받아서 변환하여 전달할 뿐이다. 계단함수에 비해 완만한 곡선 형태로 비선형이다. 특정 경계를 기준으로 출력이 확 바뀌어버리는 계단함수와는 달리 시그모이드 함수는 완만하게 매끄럽게 변화하는데 이 매끄러움이 신경망 학습에서 중요하며 활성화 함수로 시그모이드 함수를 사용하는 이유이기도 하다.

 

 

 

우리 문과생들은 일단 활성화 함수를 출력값을 반환하기 위해서 무언가를 처리해주는 변환기정도로만 생각해 놓자. 신경망에서는 활성화함수를 통해서 각 노드(뉴런)로부터 받은 신호를 변환하고 변환된 신호를 다음 뉴런으로 전달한다. 실제로는 계단 함수와 시그모이드 함수는 사용되지 않는 것으로 보인다.

 

 

그래프로 보면 더 이해가 잘 될 것이다.

 

계단 함수 Step Function (좌)       vs      시그모이드 함수 Sigmoid Function (우)

          

 

 

시그모이드 함수는 값을 실수형으로 가지는 것을 볼 수 있다. 시그모이드 함수의 매끄러움은 가중치 값을 전달할 때 좀 더 부드럽게 양을 조절해서 전달할 수 있다는 점이 계단 함수와 다른 점이다.

 

둘다 비선형인 점은 동일하다.

 

 

그렇다면 왜 비선형 함수를 사용해야 하는가?

선형함수를 사용했을 때는 은닉층을 사용하는 이점이 없기 때문이다. 다시 말해 선형함수를 여러층으로 구성한다 하더라도 이는 선형함수를 세번 연속 반복한 것에 지나지 않는다는 의미와 같기 때문이다. y = ax라는 선형함수가 있다고 한다면 이 것을 3층으로 구성하면 y = a(a(a(x))) 와 동일한 것으로 이는 y = a3(x)와 같다. 굳이 은닉층 없이 선형함수로 네트워크를 구성하는 것은 의미가 없다는 뜻입니다.

 

 

 ReLU 함수

입력이 0을 넘으면 그 입력을 그대로 출력하고, 0 이하이면 0을 출력하는 함수

 

 

 

3. 다차원 배열 계산

다차원 배열의 계산은

 

2017/10/09 - [Data Science/Python] - Python 기초 - NumPy로 선형대수 표현하기 (1)

2017/10/09 - [Data Science/Python] - Python 기초 - NumPy Broadcasting 이해하기 (2)

 

위의 두 포스팅을 먼저 보는 것이 도움이 될 것이다. 다차원 배열의 계산은 앞서 설명한 가중치의 값을 보다 편하게 하기 위해서 행렬 연산을 이용하는 것이다. 한 두개의 신경망 층은 인간이 계산할 수 있을지 모르겠지만 그 이상의 수 많은 차원의 수많은 뉴런층으로 구성된 신경망의 weight를 일일이 계산하는 것은 불가능한 일이다. 이를 컴퓨팅적으로도 쉽게 할 수 있도록 돕는 것이 행렬 연산이다. 이래서 선형대수의 개념을 알아야 했던 것이다. (문과생을 위한 선형대수도 공부하고 조만간 정리할 예정. 우선 위의 포스팅을 참조하자) 

 

여기서 중요한 개념은 다차원 배열(행렬) 간의 곱 연산이다. 위에 소개한 포스팅 NumPy Broadcasting에서도 나오지만 행렬의 곱이 성립하기 위해서는 기본적으로 아래의 조건을 따라야 한다. *(곱 연산)의 안쪽에 있는 b와 c의 값이 일치해야 하며, 곱 연산을 했을 때 결과값은 a x d의 형태로 나온다는 점이다. 이는 한쪽이 1차원 배열일 때도 동일하게 적용된다.

 

a x b  *  c x d  =  a x d  (when b = c )

 

 

>> A = np.array([[1,2], [3,4], [5,6]])

>> print(A.shape)    #(3, 2)

 

>> B = np.array([7,8])

>> print(B.shape)    #(2,)

 

>> np.dot(A, B)  #array([23, 53, 83])

 

위의 예제는  3 x 2  *  2 x 1 의 행렬곱이다. 결과는 3 x 1 이다. 안쪽이 2 = 2로 동일하고 결과 값은 3 x 1 로 바깥쪽의 값으로 이루어진 행렬 차원을 보인다.

 

아래와 같은 식으로 된다. Y의 값들은 활성화 함수를 통해 변환된다.

 

 

 

 

 

 

이런식으로 어떤 층의 노드(뉴런)의 개수가 몇 개가 되든 (위에서는 3개) 한 번의 연산으로 이 작업을 빠르게 수행할 수 있다. 행렬의 내적은 신경망에서 아주 중요한 개념인 것이다.

 

층이 몇 개이든 이와 같은 방법으로 가중치를 계속해서 계산해 나가는 것이라고 보면 된다.

 

 

 

 

4. 신경망 구현해보기

딥러닝을 돌려보기 위해서 구현 방법론이 머리 속에 정리되어야 한다.

 

  1. 신경망 환경설정 (네트워크 세팅)
  2. 초기 가중치(weight), 편향(bias) 설정
  3. 활성화 함수 설정

우선은 foward 방식만 고려했을 때 이렇게 간단하게 구성할 수 있으며, 패키지를 사용하게 되면 각 번호가 하나의 parameter를 설정하는 값들이 된다. 순차적으로 계산하는 방식은 신경망 모형의 가장 기본적인 내용이며 앞으로 갔다가 다시 뒤로 돌아오는 역전파(backpropagation)에 대해서도 곧 포스팅할 예정이다.

 

 

 

 

5. 출력층 설계

앞서 잠깐 설명했지만 신경망은 분류(classification)와 회귀(regression) 문제에 모두 활용할 수 있다. 어떤 문제냐에 따라 활성화 함수가 달라질 뿐이다. 분류는 어떤 사람이 사기를 쳤는지(1), 안 쳤는지(0) 예측하는 것이고, 회귀는 사기당한 금액이 얼마($10,000)였는지 에측하는 문제이다. 둘다 크게 보면 예측(prediction)이다.

 

어떤 상황에 어떤 활성화 함수를 사용해야 하는가

출력 부분에서의 활성화 함수는 문제 상황에 따라 다를 것이다. 일반적으로

 

회귀 --> 항등 함수 (출력 값을 그대로 반환하는 함수) identity function

분류(0/1) --> 시그모이드 함수 sigmoid function

분류(multiple) --> 소프트맥스 함수 softmax function

 

소프트맥수 함수(Softmax Function)는 아주아주 중요한 개념이므로 다른 포스팅에서 조금 더 설명해보겠다. 0~1의 실수 값을 출력하는 소프트맥스는 그 성질 때문에 분류하는 결과에 대한 확률값으로 해석되기도 하여 분류 문제를 확률적으로 풀어볼 수 있도록 한다.

 

 

 

 

 


 

신경망에 대한 기본적인 개념들을 알아보았다.  너무 이론적인 것만 했으니 다음 포스트에서는 간단하게라도 한번 딥러닝이 어떻게 돌아가는지 실습을 해볼 수 있도록 하겠다. + 소프트맥스 함수

반응형
반응형

출처: 아이리포

 

회귀분석 유형의 이해

 

 

반응형

+ Recent posts