import numpy as np
A = np.array([1, 2, 3, 4])
print(A) # [1 2 3 4]
print(np.ndim(A)) # 1 (배열의 차원 수)
print(A.shape) # (4,) (배열의 형상(크기)) 1차원이어도 튜플로 반환
print(A.shape[0]) # 4
B = np.array([[1, 2], [3, 4], [5, 6]])
print(B)
'''
[[1 2]
[3 4]
[5 6]]
'''
print(np.ndim(B)) # 2
print(B.shape) # (3, 2)
1차원 배열 A와 2차원 배열 B를 작성하였다.
2차원 배열은 특히 행렬(matrix)이라고 부른다.
가로 방향을 행(row), 세로 방향을 열(column)이라고 부른다.
# 행렬의 곱
행렬의 곱은 위와 같이 계산한다.
import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(np.dot(A, B)) # 곱셈을 수행하는 함수
'''
[[19 22]
[43 50]]
'''
파이썬으로는 이렇게 계산할 수 있다.
행과 열의 수에 주의하자. 같은 shape끼리 곱한 결과이다.
A = np.array([[1, 2, 3], [4, 5, 6]])
print(A.shape) # (2, 3)
B = np.array([[1, 2], [3, 4], [5, 6]])
print(B.shape) # (3, 2)
print(np.dot(A, B))
'''
[[22 28]
[49 64]]
'''
다른 shape여도 형상이 이와 같으면 곱할 수 있다.
(2, 3) 행렬과 (2, 2) 행렬을 곱하면 오류가 발생한다.
# 신경망에서의 행렬 곱
이 신경망은 절편과 활성화 함수를 생략하고 가중치만 갖는다.
import numpy as np
X = np.array([1, 2])
print(X.shape) # (2,)
W = np.array([[1, 3, 5], [2, 4, 6]])
print(W.shape) # (2, 3)
Y = np.dot(X, W)
print(Y) # [ 5 11 17]
# 3층 신경망 구현하기
구현에 앞서 이 절에서 쓰일 표기법은 다음과 같다.
입력층에서 1층으로 신호가 전달되는 것을 살펴볼 것이다.
을 수식으로 나타내보자.
가중치를 곱한 신호 두 개와 절편을 합해서 계산한다.
여기서 행렬의 곱을 이용하면 1층의 가중치 부분을 다음 식처럼 간소화할 수 있다.
넘파이의 다차원 배열을 사용해서 식 3.9를 구현하자.
import numpy as np
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) # (2, 3)
print(X.shape) # (2,)
print(B1.shape) # (3,)
A1 = np.dot(X, W1) + B1
이어서 1층의 활성화 함수에서의 처리를 살펴본다.
은닉층에서의 가중치 합(가중 신호와 절편의 총합)을 a로 표기하고
활성화 함수 h()로 변환된 신호를 z로 표기한다.
여기에서는 활성화 함수로 시그모이드 함수를 사용하기로 한다.
이를 파이썬으로 구현하면 다음과 같다.
def sigmoid(x):
return 1 / (1 + np.exp(-x))
Z1 = sigmoid(A1)
print(A1) # [0.3 0.7 1.1]
print(Z1) # [0.57444252 0.66818777 0.75026011]
이어서 1층에서 2층으로 가는 과정을 살펴본다.
W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
B2 = np.array([0.1, 0.2])
print(Z1.shape)
print(W1.shape)
print(B2.shape)
A2 = np.dot(Z1, W2)
Z2 = sigmoid(A2)
마지막으로 2층에서 출력층으로의 신호 전달이다.
def identity_function(x): # 항등 함수
return x
W3 = np.array([[0.1, 0.3], [0.2, 0.4]])
B3 = np.array([0.1, 0.2])
A3 = np.dot(Z2, W3) + B3
Y = identity_function(A3) # 혹은 Y = A3
여기에서는 항등함수를 출력층의 활성화 함수로 사용했다.
항등함수는 입력을 그대로 출력하는 함수여서 굳이 정의할 필요는 없다.
출력층의 활성화 함수를 sigma()로 표현하여
은닉층의 활성화 함수 h()와는 다름을 명시했다.
# 구현 정리
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]
신경망 구현의 관례에 따라 가중치만 대문자로 썼다.
init_network() 함수는 가중치와 절편을 초기화하고 이들을 딕셔너리 변수인 network에 저장한다.
forward() 함수는 입력 신호를 출력으로 변환하는 처리 과정을 모두 구현하고 있다.
함수 이름을 forward라고 한 것은 신호가 순방향(입력에서 출력 방향)으로 전달됨(순전파)을 알리기 위함이다.
'프로그래밍 > DeepLearning' 카테고리의 다른 글
[밑바닥부터 시작하는 딥러닝 1] 3. MNIST 손글씨 숫자 인식 (0) | 2023.07.15 |
---|---|
[밑바닥부터 시작하는 딥러닝 1] 3. 신경망 출력층 설계, 소프트맥스 함수(softmax) (0) | 2023.07.14 |
[밑바닥부터 시작하는 딥러닝 1] 3. 신경망, 활성화함수 (0) | 2023.07.14 |
[밑바닥부터 시작하는 딥러닝 1] 2. 퍼셉트론, 논리 회로 (0) | 2023.07.13 |
[밑바닥부터 시작하는 딥러닝 1] 1.6 <맷플롯립(matplotlib)> 사용법 (0) | 2023.07.09 |