3.3 다차원 배열

-다차원 배열

넘파이의 다차원 배열을 사용한 계산법을 숙달하면 신경망을 효율적으로 구현 가능하다.

다차원 배열도 기본은 '숫자의 집합'인데, N차원으로 나열하는 모든 것을 통틀어 다차원 배열이라 한다.

import numpy as np
A=np.array([1,2,3,4]) # 1차원 배열
print(A)
[1 2 3 4]

np.ndim(A)
1
A.shape
(4,)
A.shape[0]
4

B=np.array([[1,2],[3,4], [5,6]) #2차원 배열
print(B)
[[1 2]
 [3 4]
 [5 6]]
 
np.ndim(B)
2
B.shape
(3,2)

배열에 대해서는 다들 잘 알것이기에 간단하게 생략하고 넘어가겠다.

-행렬의 내적(행렬 곱)

이공계 출신이라면 당연히 내적을 할 줄 알것이라 믿는다. 그러므로 설명은 생략하고 파이썬을 통해 어떻게 내적을 구현하는지 살펴보자.

 

 

A=np.array([[1,2],[3,4]]) #2차원 배열 #2x2 행렬
B=np.array([[5,6],[7,8]]) #2차원 배열 #2x2 행렬

np.dot(A,B)

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

우리는 행렬의 내적을 할때 numpy 함수 np.dot()을 통해 계산할 수 있다. np.dot()은 넘파이 배열 2개를 인수로 받아 그 내적을 반환한다. 하지만 주의해야할 점은 np.dot(A,B)와 np.dot(B,A)는 다른 값이 될 수 있다는 점을 꼭 기억하자. 

 

또한 우리가 행렬의 곱을 할때는 중요한 점이 하나 있는데, 

행렬의 곱에서는 대응하는 차원의 원소 수를 일치시켜라.

다음과 같이 곱에서 차원의 원소수는 일치시켜야 한다. 그것이 약속이다!

 

-신경망의 내적

이번에는 신경망을 생각해보자, 

행렬의 곱으로 신경망의 계산을 수행한다

위 신경망은 편향과 활성화 함수를 생략하고 가중치만 갖는다. 위 그림을 파이썬으로 구현해보자.

X=np.array([1,2]) #1x2 행렬
W=np.array([[1,3,5],[2,4,6]]) #2x3 행렬
Y=np.dot(X,W)
print(Y)

[ 5 11 17]

 

-3층 신경망 구현하기

2층신경망까지 구현했다면, 3층 신경망은 어떨까? 

3층 신경망

위 신경망은 입력층(0층)은 2개, 첫 번째 은닉층(1층)은 3개, 두 번째 은닉층(2층)은 2개, 출력층(3층)은 2개의 뉴런으로 구성된다.

 

표기법은 다음과 같다.

 

중요한 표기

-각 층의 신호 전달 구현하기

우선 그림을 확인해보자.

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

편향을 뜻하는 뉴런 (1)이 추가되었다. 편향은 오른쪽 아래 인덱스가 하나밖에 없다는 것에 주의하자.

또한, 편향의 입력 신호는 항상 1임을 기억하자.

계산은 다음과 같다. 설명은 생략하겠다.

where)

코딩으로 구현하면,

X=np.array([1.0,.5])
W1=np.array([[.1,.3,.5],[.2,.4,.6]])
B1=np.array([.1,.2,.3])

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

[0.3 0.7 1.1]

다음은 1층의 활성화 함수에서의 처리를 살펴보자. 이 과정은 다음과 같다.

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

위 그림과 같이 은닉층에서의 가중치 합(가중 신호와 편향의 총합)을 a로 표기하고 활성화 함수h()로 변환된 신호를 z로 표기한다. 여기서 활성화 함수로 앞선 포스팅에서 배운 시그모이드를 사용한다. 

코딩으로 살펴보자

def sigmoid(x):
    return 1/(1+np.exp(-x))
X=np.array([1.0,.5])
W1=np.array([[.1,.3,.5],[.2,.4,.6]])
B1=np.array([.1,.2,.3])

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

[0.3 0.7 1.1]
[0.57444252 0.66818777 0.75026011]

 

그 다음은 1층에서 2층으로 가는 과정을 살펴보자. 우선 그림은 다음과 같다.

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

구현 방식은 똑같다.

이제 코딩으로 구현해보자.

def sigmoid(x):
    return 1/(1+np.exp(-x))
X=np.array([1.0,.5])
W1=np.array([[.1,.3,.5],[.2,.4,.6]])
B1=np.array([.1,.2,.3])

A1=np.dot(X,W1)+B1
Z1=sigmoid(A1)
W2=np.array([[.1,.4],[.2,.5],[.3,.6]])
B2=np.array([.1,.2])

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

print(A1)
print(Z1)
print(A2)
print(Z2)

[0.3 0.7 1.1]
[0.57444252 0.66818777 0.75026011]
[0.51615984 1.21402696]
[0.62624937 0.7710107 ]

마지막으로 2층에서 출력층으로의 신호 전달을 살펴보자. 우선은 그림은 다음과 같다.

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

출력층의 구현도 그동안의 구현과 거의 동일하다. 딱 한가지 다른점은, 활성화 함수만 지금까지의 은닉층과 다르다는 점이다. 

이를 코딩으로 구현해보자.

def sigmoid(x):
    return 1/(1+np.exp(-x))
X=np.array([1.0,.5])
W1=np.array([[.1,.3,.5],[.2,.4,.6]])
B1=np.array([.1,.2,.3])

A1=np.dot(X,W1)+B1
Z1=sigmoid(A1)
W2=np.array([[.1,.4],[.2,.5],[.3,.6]])
B2=np.array([.1,.2])

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

def identity_function(x):
    return x

W3=np.array([[.1,.3],[.2,.4]])
B3=np.array([.1,.2])

A3=np.dot(Z2,W3)+B3
Y=identity_function(A3)
print(A1)
print(Z1)
print(A2)
print(Z2)
print(A3)
print(Y)

[0.3 0.7 1.1]
[0.57444252 0.66818777 0.75026011]
[0.51615984 1.21402696]
[0.62624937 0.7710107 ]
[0.31682708 0.69627909]
[0.31682708 0.69627909]

 

-출력층 설계하기

신경망은 분류와 회귀 모두에 이용 가능하다. 다만 둘 중 어떤 문제냐에 따라 출력층에서 사용하는 활성화 함수가 달라진다. 일반적으로 회귀에는 항등 함수를 분류에는 소프트맥스 함수를 사용한다. 

활성함수는 앞선 포스트에 열심히 다뤄놓았으니 필요하면 꼭 찾아서 보길 바란다.

 

-출력층의 뉴런 수 정하기

출력층의 뉴런 수는 주어진 문제에 맞게 적절히 정해야 한다. 일반적으로 분류하고 싶은 클래스 수로 설정한다. 책의 예를 빌리자면, 흔히 MNIST 과정에서 숫자 이미지 0~9중 하나로 분류하는 문제라면 출력층의 뉴런의 수는 10개로 설정한다.

 

뒤에 손글자 숫자 인식은 너무나 흔하며, 실제 이 과정을 더 심화로 다루는 것이 뒤에 나오니 지금은 생략하도록 하겠다. 그럼 20000

'Deep Learning > 밑바닥부터 시작하는 딥러닝(1)' 카테고리의 다른 글

Chapter 6. 학습 관련 기술들(1)  (0) 2021.03.02
Chapter 5. 오차역전파법  (0) 2021.01.25
Chapter 4. 신경망 학습  (0) 2021.01.23
Chapter 3-1 신경망  (0) 2021.01.17
2.1 퍼셉트론이란?  (0) 2021.01.17

+ Recent posts