넘파이의 다차원 배열을 사용하여, 3층 신경망에서 수행되는 입력부터 출력까지의 처리를 구현한다.
3층 신경망의 입력층은 2개, 첫 번째 은닉층은 3개, 두 번째 은닉층은 2개, 출력층은 2개의 뉴런으로 구성되어 있다.
3.4.1. 표기법 설명
먼저, 신경망 처리를 설명하기 위한 표기법을 알아본다.
입력층의 뉴런에서 다음 층의 뉴런으로 향하는 선 위에 가중치를 표시한다.
가중치와 은닉층 뉴런의 오른쪽 위에는 (1)이 붙어 있고, 이는 1층의 가중치임을 뜻한다.
가중치의 오른쪽 아래의 두 숫자는 차례로 다음 층 뉴런과 앞 층 뉴런의 인덱스 번호이다.
3.4.2. 각 층의 신호 전달 구현하기
이제 입력층에서 1층의 첫 번째 뉴런으로 가는 신호를 살펴본다.
그림을 살펴보면, 편향을 뜻하는 뉴런이 추가되었다.
이때 편향은 앞 층의 편향 뉴런이 하나뿐이기 때문에 오른쪽 아래 인덱스가 하나 밖에 없다.
a1(1)은 가중치를 곱한 신호 두개와 편향을 합해서 다음과 같이 계산한다.
여기에서 행렬의 곱을 이용하면 1층의 가중치 부분을 다음 식처럼 간소화할 수 있다.
이때 행렬 A, X, B, W는 각각 다음과 같다.
넘파이의 다차원 배열을 사용해 식을 구현해본다.
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로 표기한다.
활성화 함수로 시그모이드 함수를 사용하며, 파이썬으로 구현하면 다음과 같다.
Z1 = sigmoid(A1)
print(A1) # [0.3, 0.7, 1.1]
print(Z1) # [0.57444252, 0.66818777, 0.750262011]
다음으로, 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(W2.shape)
print(B2.shape)
A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid(A2)
1층의 출력 Z1이 2층의 입력이 된다는 점을 제외하면 이전의 구현과 같다.
마지막으로 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)
출력층의 구현도 거의 비슷하지만, 활성화함수에 차이가 있다.
입력을 그대로 출력하는 항등 함수인 identity_function()을 정의하고, 이를 출력층의 활성화 함수로 이용했다.
3.4.3. 구현 정리
def init_network():
network = {}
network['W1'] = np.array([[0.1, 0.3, 05], [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, 0.3])
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(t)
init_network() 함수는 가중치와 편향을 초기화하고 이들을 딕셔너리 변수인 network에 저장한다.
딕셔너리 변수 network에는 각 층에 필요한 매개변수를 저장한다.
forward() 함수는 입력 신호를 출력으로 변환하는 처리 과정을 모두 구현하고 있다.
퀴즈 1. 다음 코드의 출력 결과는?
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])
A1 = np.dot(X, W1) + B1
print(A1)
퀴즈 2. 다음 코드에서 init_network() 함수의 역할로 옳은 것은?
def init_network():
network = {}
network['W1'] = np.array([[0.1, 0.3, 05], [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, 0.3])
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(t)
A) 신경망을 학습시켜 가중치를 조정하는 역할을 한다.
B) 신경망의 가중치와 편향을 초기화하고 저장하는 역할을 한다.
C) 입력 신호를 출력으로 변환하는 처리 과정을 구현한다.
D) 활성화 함수(sigmoid)를 적용하는 역할을 한다.
'Miscellaneous' 카테고리의 다른 글
[2025-1] 주서영 - Deep Reinforcement Learning from Human Preferences (0) | 2025.03.06 |
---|---|
[2025-1] 임준수 - 밑바닥부터 시작하는 딥러닝 리뷰, (CH 3.2) 활성화 함수 (0) | 2025.03.05 |
[2025-1] 박경태 - 밑바닥부터 시작하는 딥러닝 리뷰, (CH 3.3) 다차원 배열의 계산 (0) | 2025.03.05 |
[2025-1] 윤선우 - 밑바닥부터 시작하는 딥러닝 리뷰, (CH 3.5) 출력층 설계하기 (0) | 2025.03.05 |
[2025-1] 윤선우 - 밑바닥부터 시작하는 딥러닝 리뷰, (CH 2.1, 2.2) 퍼셉트론과 단순한 논리 회로 (0) | 2025.03.04 |