이번 블로그에서는 퍼셉트론에 대하여 알아보도록 하겠습니다.
퍼셉트론의 개념은 1957년도에 구현된 알고리즘이며, 신경망(딥러닝)의 기원이 되는 알고리즘이라고 할 수 있습니다. 즉, 퍼셉트론의 구조를 배워야만 신경망과 딥러닝으로 확장되는 아이디어를 생각해 낼 수 있는 중요한 기초 내용이 됩니다.
퍼셉트론이란?
퍼셉트론(인공 뉴런 이라고도 합니다)은 다수의 신호를 받아 하나의 신호를 출력합니다.
여기서 신호는 뇌과학에서는 일정한 자극, 신경망에서는 feature(특성)가 된다고 할 수 있는데요, 어떤 흐름을 타고 데이터(신호 - feature)가 들어와 처리된 결과를 다음 퍼셉트론으로 보내는 것으로 생각해 보면 됩니다.
보통 전류와 비교하게 되는데, 전류는 보통 실수값( ex : n볼트의 전류 )이 흐르지만, 지금 배워볼 우리의 퍼셉트론에서는 오로지 흐른다(1) / 흐르지 않는다.(0)으로 표현합니다.
이번 장에서는 1 => 흐른다 / 0 => 흐르지 않는다.로 표현하겠습니다.
신호가 퍼셉트론을 통과하는 과정
우리의 뇌는 여러가지 신호를 동시에 받아, 뇌에 있는 뉴런뉴런이라는 존재가 이 신호들을 처리합니다.
생물학적으로 뇌에 부담이 가지 않는 범위를 결정지어 주기 위해 신호별 가중치(weight)가 존재하는데요, 이 가중치가 존재하는 이유는 뇌가 받은 전기적 신호에 대해 적절하게 반응하기 위해 설정되어야 하기 때문입니다.
예를 들어 친구들과 게임을 하다가 이마를 맞았을 때( 입력 신호 ) 아픈 느낌이 점점 사라져야겠죠? 처음 입력받은 신호(자극)가 계속 사라지지 않고 유지되면 살 수가 없습니다. 따라서 처음 이마를 맞았을 때 받은 신호를 점점 줄여가는 식으로 가중치라는 것이 각 신호마다 곱해지게 됩니다.
이제는 퍼셉트론에 빗대어서 이야기해보겠습니다. 입력 신호( 입력 특성 )가 두 가지라고 가정해 보겠습니다. 그리고 이 신호들을 각각 x1, x2라고 가정해 보겠습니다.
퍼셉트론은 뇌의 뉴런과 대응되기 때문에 각각의 신호에 대한 가중치도 있다고 가정해 볼 수 있는데요. 이 각각의 가중치들을 w1, w2라고 가정하겠습니다.
본격적으로 퍼셉트론을 알아보자~!
입력 신호( x1, x2 )와 각 신호들에 대한 가중치( w1, w2 )가 준비되었습니다.신경망에서 입력 신호를 가중치와 함께 처리하여 출력하는 것을 뉴런 또는 노드라고 합니다. 즉 퍼셉트론 = 뉴런(노드)라고 이야기할 수 있습니다.
퍼셉트론에서 입력 신호를 받을 때는 각 가중치가 곱해지면서 받아집니다. 수식으로 표현하면 각 퍼셉트론마다 $w_1x_1 + w_2x_2$ 의 계산이 된다고 볼 수 있겠네요
퍼셉트론의 출력 결과는 흐른다(1) / 안 흐른다(0)
입력 특성과 가중치가 곱해져서 퍼셉트론에 흘러 들어오고, 거기에 대한 결과값이 나온 다는 것이 확인되었습니다.
여기서 중요한 것은, 각 퍼셉트론마다정해진 한계값이라는 것이 있습니다. 이것을 임계값( $\theta$ theta )이라고 합니다. 퍼셉트론로 들어온 입력 값들과 가중치가 곱해진 결과물의 합이 임계값 $\theta$를 넘어가면 0으로 출력, 넘어가지 않으면 1로 출력됩니다. 수식으로 표현하면 다음과 같습니다.
이를 반응 조건 계산식이라고 하겠습니다.
위의 수식을 일반화한 그래프를 그려 보도록 하겠습니다. 각 특성별 가중치는 임의로 0.5, 0.5로 설정하고, 임계값 𝜃는 2로 임의로 설정하겠습니다.
import matplotlib.pyplot as plt
import numpy as np
- 사전에 알 수 있는 값 : 특성 값
- 가중치는 아직 우리가 몰라요 : 모델이 학습하는 값
def func1(x1, x2):
w1, w2, theta = 0.5, 0.5, 2 # 가중치를 각각 0.5, 0.5로 설정하고, 임계값은 2로 설정
y = w1*x1 + w2*x2
return y, y >= theta
# 입력 데이터 생성
x1 = np.linspace(-3, 7, 100)
x2 = np.linspace(-3, 7, 100)
y_value, y_result = func1(x1, x2)
plt.plot(y_value, y_result)
plt.yticks([0, 1])
plt.xticks([-3,2,7], [r'-$\infty$', r'$\theta$', r'-$\infty$'])
plt.xlabel(r"$w_1x_1+w_2x_2$")
plt.ylabel("y", rotation=0)
plt.show()
중간에 𝜃 를 기준으로 𝑤1𝑥1+𝑤2𝑥2의 결과가(y축) 0, 1로 결정지어지는 것이 확인됩니다. 또한 위 그래프를 함수로 일반화하면 다음과 같습니다. 이러한 함수를 단위 계단 함수(u)라고 합니다.
계산 식 𝑧의 결과가 계단 함수 u의 인자로 전달되었을 때 0보다 크면 1, 0보다 작으면 0으로 설정되는 것이 확인됩니다. 계단 함수 u(𝑧)의 그래프는 다음과 같습니다.
z = np.linspace(-3, 3, 100)
plt.plot(z, z > 0)
plt.yticks([0, 1])
plt.xticks([-3, 0, 3],[r'-$\infty$', r'$0$', r'-$\infty$'])
plt.xlabel(r"$z$")
plt.ylabel(r"$y=\mathcal{u}(z)$", rotation=0)
plt.show()
이항 법칙에 따라 반응 조건 계산식의 𝜃를 이항 하고, 계단 함수를 적용하면 다음과 같은 수식이 완성됩니다.
결과적으로 각 퍼셉트론의 입력 특성(x1, x2)과 가중치(w1, w2)를 이용한 결과를 계단 함수 u로 표현하기 위한 인자 𝑧는
가 됩니다. 참고로 계단 함수의 인자로 들어가는 것을 퍼셉트론의 가중입력 이라고도 표현합니다. 계단 함수의 결과에 따라 𝑦의 결과물은 반응 조건 계산식 𝑤1𝑥1+𝑤2𝑥2−𝜃 에 따라 항상 0 또는 1의 값만 가지게 됩니다.
단층 퍼셉트론 vs 다층 퍼셉트론
퍼셉트론은 이처럼 입력 값에 따른 가중치를 이용해서 출력 값을 내게 됩니다. 이번 예제는 하나의 퍼셉트론만 이용하는 단층 퍼셉트론의 한계에 대해 알아보고, 이를 해결할 수 있는 다층 퍼셉트론에 대해 알아봅니다.
AND Gate
논리 회로 게이트 문제를 이용해 알아보도록 하겠습니다. 먼저 가장 유명한 게이트죠? AND 게이트의 진리표를 확인해 보겠습니다.
AND 게이트의 특징은 입력값 𝑥1, 𝑥2가 둘 다 1일 때만 결과물이 1이 나옵니다. 이제 저희가 해야 할 일은 퍼셉트론에 적용되는 매개변수인 각각의 가중치들과 임계값을 정하는 일들입니다. AND 게이트의 매개변수의 조합은 무수히 많습니다. 여기는 𝑤1, 𝑤2를 각각 0.5로 설정하고, 𝜃는 0.7로 사용해 보겠습니다. 다음은 NAND와 OR 게이트입니다.
NAND Gate
NAND 게이트는 Not AND라는 뜻을 가지고 있으며, AND 게이트의 반대 동작을 하게 됩니다. 다음은 NAND 게이트의 진리표입니다.
입력 특성 𝑥1과 𝑥2가 둘 다 1일 때만 0이고, 나머지 상황에 대해서는 모두 1이 출력되는 것이 확인됩니다. NAND 게이트의 가중치와 임계치는 AND 게이트 매개변수 부호의 반대를 넣어 주면 됩니다. 즉 위의 AND 게이트의 가중치와 임계값의 부호를 바꾼 NAND 게이트의 𝑤1, 𝑤2, 𝜃 는 각각 -0.5, -0.5, -0.7로 설정되면 됩니다.
OR Gate
OR 게이트는 입력 신호 중 하나 이상이 1 이상이면 출력이 1이 되는 게이트입니다. 진리표를 바로 확인해보겠습니다.
각 진리표를 이용해 모두 퍼셉트론으로 AND, NAND, OR 게이트를 표현할 수 있습니다. 여기서 중요하게 생각해야 할 것은 퍼셉트론의 구조 자체는 AND, NAND, OR 게이트 모두 똑같다는 것입니다. 다른 점은 각 게이트의 매개변수(가중치와 임계값)만 다르다는 점입니다. 즉, 퍼셉트론의 매개변수 값만 적절히 조절하면 하나의 퍼셉트론이 AND, NAND, OR로 변신할 수 있다는 것입니다.
Gate의 퍼셉트론화
먼저 가장 기본 공식인 𝑤1𝑥1+𝑤2𝑥2−𝜃를 함수와 시킨 AND게이트의 퍼셉트론입니다.
def AND(x1, x2):
w1, w2, theta = 0.5, 0.5, 0.7
tmp = w1 * x1 + w2 * x2 - theta
if tmp <= 0 :
return 0
elif tmp > 0 :
return 1
AND(0, 0), AND(1, 0), AND(0, 1), AND(1, 1)
생각했던 대로 잘 동작하는 것 같습니다! NAND와 OR 게이트도 매개변수만 적절히 조절하면 똑같이 만들 수 있겠습니다만, 위의 함수를 약간만 손보도록 하겠습니다.
편향(bias) 도입
나름 𝑤1𝑥1+𝑤2𝑥2−𝜃 같은 계산 식도 활용하기는 좋지만, −𝜃 를 −𝑏로 치환하는 것이 더 보기도 좋고 일반적입니다. 즉, 우리가 앞으로 볼 퍼셉트론의 일반화된 식은
로 표현 됩니다. 결론을 내자면 퍼셉트론은 입력 신호에 가중치를 곱한 값과 편향(bias)을 합하여, 그 값이 0을 넘으면 1로, 그렇지 않으면 0이 출력된다고 일반화해볼 수 있겠습니다. 약간 프로그래밍 적으로, 입력 특성과 가중치를 두 개만 사용했지만, 여러 개의 입력 특성과 가중치가 있을 수 있으므로, 넘파이 배열을 이용해서 계산해보도록 하겠습니다. 위의 편향을 사용한 일반화된 공식에서 배열 곱에 대해 잠시 살펴보겠습니다. 위에서 구현한 AND 함수의 매개변수를 그대로 사용합니다.
# theta를 -b로 치환시킨 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
AND(0, 0), AND(1, 0), AND(0, 1), AND(1, 1)
간단히 −𝜃가 편향 𝑏로 치환된 것을 확인할 수 있습니다!
여기서 중요한 점은 가중치와 편향의 기능은 다르다는 것인데요, 가중치의 역할은 입력 신호가 결과에 주는 영향력(중요도)을 조절하는 매개변수고, 편향은 퍼셉트론이 얼마나 쉽게 활성화( 1로 출력 ) 되는가를 조정하는 매개변수입니다. 즉 편향이 크면 퍼셉트론의 흥분도가 커져 쉽게 활성화되고( 민감 ), 편향이 작으면 흥분도가 낮아 활성화가 잘 안 된다(둔감) 고 생각하면 됩니다. 이어서 NAND와 OR Gate를 모두 구현해 보도록 하겠습니다.
def NAND(x1, x2):
x = np.array([x1, x2])
w = np.array([-0.5, -0.5]) # AND 게이트의 매개 변수 부호를 반대로
b = 0.7
tmp = np.sum(w * x) + b
if tmp <= 0:
return 0
else:
return 1
NAND(0, 0), NAND(1, 0), NAND(0, 1), NAND(1, 1)
def OR(x1, x2):
x = np.array([x1, x2])
w = np.array([0.5, 0.5])
b = -0.2 # 적절하게 편향만 조절하여 OR 게이트를 구현!
tmp = np.sum(w*x) + b
if tmp <= 0:
return 0
else:
return 1
OR(0, 0), OR(1, 0), OR(0, 1), OR(1, 1)
단층 퍼셉트론
여태 AND, NAND, OR 게이트를 이야기하다 갑자기 단층 퍼셉트론이라는 주제로 바뀌었습니다.
하지만 여러분들이 구현한 위의 함수들이 곧 퍼셉트론이 된다는 사실! 그렇다면 단층 퍼셉트론이 어떤 것인지 알아보죠, 위의 각 퍼셉트론들은 각각 입력값을 받아 각자의 역할을 수행합니다. 즉 한 번의 입력에 한번의 출력이 이루어 진다는 이야기가 되겠죠. 이처럼 한번의 입력, 한번의 결과를 내는 퍼셉트론들을 단층 퍼셉트론이라고 합니다.
단층 퍼셉트론의 한계, XOR 게이트 구현
XOR 게이트를 하나의 퍼셉트론에서 구현할 수 있을까요? XOR 게이트란 배타적 논리합이라는 논리회로로써𝑥1과 𝑥2 중 한쪽의 값만 1일 때 결과가 1이 됩니다. 즉 𝑥1, 𝑥2 가 똑같은 입력이라면 그 결과가 0이 된다고 볼 수 있습니다. 다음은 XOR 게이트의 진리표입니다.
지금까지 AND, NAND, OR 게이트를 만들었던 것처럼 XOR 게이트도 하나의 퍼셉트론으로 처리할 수 있을까요?
정답은 XOR 게이트는 단일 퍼셉트론으로 처리할 수 없다!
임의로 OR 게이트의 매개변수 𝑤1,𝑤2, 𝑏 = ( 1.0, 1.0, -0.5 )로 설정했다고 생각해 보겠습니다. 𝑥1+𝑥2−0.52−0.5라는 식으로 표현가능 할 것 같습니다. 간단히 결과만 살펴보면 𝑥1 또는 𝑥2 가 1만 넘어가면 결과가 양수가 되기 때문에 좌표평면에 그래프를 그려보면 완벽하게 직선 그래프를 이용해 간단히 그려질 것 같습니다.
- XOR는 어떻게 구현해야 할까
- XOR : 배타적 논리합
# OR 시각화
plt.figure(figsize=(4,4))
plt.scatter([0],[0], marker='o')
plt.scatter([1,0,1],[0,1,1], marker='^')
plt.xticks([0, 0.5, 1])
plt.yticks([0, 0.5, 1])
plt.xlim((-0.2,1.2))
plt.ylim((-0.2,1.2))
plt.xlabel(r'$x_1$')
plt.ylabel(r'$x_2$', rotation=0)
plt.show()
직선 한 개를 그어서 위의 점들을 두 분류(동그라미, 세모)로 나눌 수 있을까요? 당연히 할 수 있습니다..! 이처럼 OR 게이트를 만들면 직선으로 1 또는 0으로 나눠지는 구간을 그려줄 수 있습니다.
하지만 XOR은 이야기가 좀 다릅니다!
XOR 진리표를 이용해 그래프를 찍어 보면 다음과 같습니다.
# XOR 시각화
plt.figure(figsize=(4,4))
plt.scatter([0,1],[0,1], marker='o')
plt.scatter([1,0],[0,1], marker='^')
plt.xticks([0, 0.5, 1])
plt.yticks([0, 0.5, 1])
plt.xlim((-0.2,1.2))
plt.ylim((-0.2,1.2))
plt.xlabel(r'$x_1$')
plt.ylabel(r'$x_2$', rotation=0)
plt.show()
XOR은 위의 그래프처럼 교차 되어있는 형태 이기 때문에 하나의 직선으로 점들을 구분하기는 불가능합니다.
단층 퍼셉트론의 한계가 보이시나요?
퍼셉트론 자체가 직선 하나의 영역으로 나눈 영역만 설정할 수 있다는 한계가 있습니다. 위의 XOR 같은 경우는 곡선 또는 여러 번의 직선을 그어야 구분할 수 있는데요, 이때 직선 하나로만 영역을 나누는 것을 선형으로 표현하고, 그 외에 곡선이나 끊긴 직선들은 비선형으로 표현합니다. 따라서 단층 퍼셉트론으로는 XOR 게이트를 표현할 수 없다. 즉 단층 퍼셉트론으로는 비선형 영역을 분리할 수 없다로 이해할 수 있겠습니다.
어떻게 해결하는가?
단층 퍼셉트론의 층을 쌓으면 된다!
각 게이트에 대한 중요한 이야기를 다루지 않기 때문에, 결과적으로만 이해해보겠습니다. XOR 게이트는 지금까지 위에서 이야기했었던 AND, NAND, OR 게이터를 적절하게 조합하면 구현해 낼 수 있습니다. 방법은 바로 NAND의 결과와 OR의 결과를 AND 게이트의 결과물로 사용하면 됩니다. 진리표로 확인해 보겠습니다.
𝑥1과 𝑥2의 입력 값에 따라 출력되는 결과물 𝑦의 값이 위에 작성했던 XOR 진리표와 일치하는 것을 볼 수 있습니다. XOR 게이트를 함수로 구현하면 다음과 같습니다.
def XOR(x1, x2):
s1 = NAND(x1, x2)
s2 = OR(x1, x2)
y = AND(s1, s2)
return y
XOR(0, 0), XOR(1, 0), XOR(0, 1), XOR(1, 1)
정확하게 XOR이 구현된 것이 확인됩니다!
입력값 𝑥1, 𝑥2에 대해 각각 다른 류의 퍼셉트론인 NAND, OR 퍼셉트론을 통과시켰고, 그 결과를 다시 하나의 퍼셉트론인 AND 게이트에 통과시켰습니다. 이처럼 두 개 이상의 퍼셉트론 층이 생기는 구조를 다층 퍼셉트론이라고 합니다.
정리하자면 NAND, OR 게이트는 𝑥1, 𝑥2를 전달받아 각자의 작업을 한 후 최종적으로 AND 게이트에 부품을 전달하는 역할만 수행하게 되고 AND 게이트는 전달받은 부품으로 최종 결과물을 수행해 내는 역할을 하게 됩니다. 위처럼 2층 이상의 다층 퍼셉트론을 이용하면 단층 퍼셉트론으로는 표현하지 못한 것을 층을 늘려 구현할 수 있습니다. 퍼셉트론은 층을 쌓아 더 다양한 것을 표현해 낼 수 있습니다.
지금까지 단층 & 다층 퍼셉트론에 대하여 알아보았습니다. 다음 포스팅에서는 신경망과 활성화 함수에 대하여 알아보도록 하겠습니다~ ;)
'Programming > Deep Learning' 카테고리의 다른 글
[Python/DeepLearning] #6. 출력층(output layer) 설계 (1) | 2023.10.11 |
---|---|
[Python/DeepLearning] #5. 행렬과 신경망 (0) | 2023.08.30 |
[Python/DeepLearning] #4. 다차원 배열의 계산 (0) | 2023.08.23 |
[Python/DeepLearning] #3. 활성화 함수의 기본 (0) | 2023.08.18 |
[Python/DeepLearning] #1. Numpy 기초 총 정리 (0) | 2023.06.20 |