02.3 퍼셉트론 구현하기
2.3.1 간단한 구현부터
이제 논리 회로를 파이썬으로 구현해봅시다. 다음은 x1과 x2를 인수로 받는 AND라는 함수입니다.
def AND(x1, x2):
w1, w2, theta = 0.5, 0.5, 0.7
tmp = x1*w1 + x2*w2
if tmp <= theta:
return 0
elif tmp > theta:
return 1
도로시가 신비한 코딩 노트를 펴고 AND 논리 회로를 파이썬 함수로 직접 입력해봅니다. 토토가 옆에서 꼬리를 흔들며 지켜봅니다.
매개변수 w1, w2, theta는 함수 안에서 초기화하고, 가중치를 곱한 입력의 총합이 임계값을 넘으면 1을 반환하고 그 외에는 0을 반환합니다. 이 함수의 출력이 [그림 2-2]와 같은지 확인해봅시다.
AND(0, 0) # 0을 출력
AND(1, 0) # 0을 출력
AND(0, 1) # 0을 출력
AND(1, 1) # 1을 출력
기대한 대로 잘 작동하는군요! 이상으로 AND 게이트를 구현했습니다. NAND 게이트와 OR 게이트도 같은 식으로 구현할 수 있지만, 그 전에 이 구현을 조금만 손보고 싶습니다.
2.3.2 가중치와 편향 도입
앞서 구현한 AND 게이트는 직관적이고 알기 쉽지만, 앞으로를 생각해서 다른 방식으로 수정하고자 합니다. 그 전에 [식 2.1]의 θ를 -b로 치환하면 퍼셉트론의 동작이 [식 2.2]처럼 됩니다.
*y* = 1 (*b* + *w*1*x*1 + *w*2*x*2 > 0) [식 2.2]
[식 2.1]과 [식 2.2]는 기호 표기만 바꿨을 뿐, 그 의미는 같습니다. 여기에서 b를 편향 bias이라 하며 w1과 w2는 그대로 가중치 weight입니다. [식 2.2] 관점에서 해석해보면, 퍼셉트론은 입력 신호에 가중치를 곱한 값과 편향을 합하여, 그 값이 0을 넘으면 1을 출력하고 그렇지 않으면 0을 출력합니다. 그럼 넘파이를 사용해서 [식 2.2] 방식으로 구현해봅시다. 여기에서는 파이썬 인터프리터로 순서대로 결과를 확인하면서 진행해보겠습니다.
지니가 ‘BIAS(편향)’라고 적힌 대형 마법 조절기를 움직여 뉴런의 활성화 난이도를 조절하는 법을 도로시와 토토에게 가르쳐줍니다.
>>> import numpy as np
>>> x = np.array([0, 1]) # 입력
>>> w = np.array([0.5, 0.5]) # 가중치
>>> b = -0.7 # 편향
>>> w*x
array([ 0. , 0.5])
>>> np.sum(w*x)
0.5
>>> np.sum(w*x) + b
-0.19999999999999996 # 대략 -0.2 (부동소수점 수에 의한 연산 오차)
넘파이 배열끼리의 곱셈은 두 배열의 원소 수가 같다면 각 원소끼리 곱합니다. 그래서 이 예의 w*x에서는 인덱스가 같은 원소끼리 곱합니다($[0, 1] * [0.5, 0.5] \Rightarrow [0., 0.5]$). 또, np.sum() 메서드는 입력한 배열에 담긴 모든 원소의 총합을 계산합니다. 이 가중치에 편향을 더하면 [식 2.2]의 계산이 완료됩니다.
2.3.3 가중치와 편향 구현하기
‘가중치와 편향을 도입’한 AND 게이트는 다음과 같이 구현할 수 있습니다.
def AND(x1, x2):
x = np.array([x1, x2])
w = np.array([0.5, 0.5])
b = -0.7
tmp = np.sum(w*x) + b
if tmp <= 0:
return 0
else:
return 1
지니가 가마솥에 입력(x)과 가중치(w) 포션을 넣고 끓여 넘파이 행렬 계산(np.sum(wx) + b)을 뚝딱 완료해냅니다.*
여기에서 -θ가 편향 b로 치환되었습니다(2.3.1절에서 구현한 AND의 theta가 -b가 되었습니다). 그리고 편향은 가중치 w1, w2와 다른 기능을 한다는 사실에 주의합시다. 구체적으로 말하면 w1과 w2는 각 입력 신호가 결과에 주는 영향력(중요도)을 조절하는 매개변수고, 편향은 뉴런이 얼마나 쉽게 활성화(결과로 1을 출력)하느냐를 조절하는 매개변수입니다. 예를 들어 b가 -0.1이면 각 입력 신호에 가중치를 곱한 값들의 합이 0.1을 초과할 때만 뉴런이 활성화합니다. 반면 b가 -20.0이면 각 입력 신호에 가중치를 곱한 값들의 합이 20.0을 넘지 않으면 뉴런은 활성화하지 않습니다. 이처럼 편향의 값은 뉴런이 얼마나 쉽게 활성화되는지를 결정합니다. 한편 w1과 w2는 ‘가중치’로, b는 ‘편향’으로 서로 구별하기도 합니다만, 이 책에서는 문맥에 따라 셋 모두를 ‘가중치’라고 할 때도 있습니다.
NOTE_ 편향이라는 용어는 ‘한쪽으로 치우쳐 균형을 깬다’라는 의미를 담고 있습니다. 실제로 [식 2.2]는 두 입력이 모두 0이어도 결과로 (0이 아닌) 편향 값을 출력합니다.
이어서 NAND 게이트와 OR 게이트를 구현해봅시다.
def NAND(x1, x2):
x = np.array([x1, x2])
w = np.array([-0.5, -0.5]) # AND와는 가중치(w와 b)만 다르다!
b = 0.7
tmp = np.sum(w*x) + b
if tmp <= 0:
return 0
else:
return 1
def OR(x1, x2):
x = np.array([x1, x2])
w = np.array([0.5, 0.5]) # AND와는 가중치(w와 b)만 다르다!
b = -0.2
tmp = np.sum(w*x) + b
if tmp <= 0:
return 0
else:
return 1
앞 절에서 AND, NAND, OR는 모두 같은 구조의 퍼셉트론이고, 차이는 가중치 매개변수의 값뿐이라 했습니다. 실제로 파이썬으로 작성한 NAND와 OR 게이트의 코드에서도 AND와 다른 부분은 가중치와 편향 값을 설정하는 부분뿐입니다.