딥러닝을 쉽게 쓰려고 노력까진 했습니다 - Chapter 2. 퍼셉트론
이 시리즈는 ‘밑바닥부터 시작하는 딥러닝’이라는 책을 토대로 작성하였으며, 말 그대로 간단한 딥러닝을 이용한 손글씨 분석 알고리즘을 python을 통해 구현해보는 내용을 다룰 예정입니다.
이번 시리즈에서는 퍼셉트론 알고리즘을 설명하겠습니다.
먼저 퍼셉트론은 프랑크 로젠블라트가 1957년에 고안한 알고리즘입니다. 이렇게 오래된, 요즘은 사용하지도 않을것만 같은 알고리즘을 배우는 이유는 퍼셉트론이 신경망(딥러닝)의 기원이 되는 알고리즘이기 때문입니다. 그래서 퍼셉트론의 구조를 배우는 것은 신경망과 딥러닝으로 나아가는데 중요한 아이디어를 배우는 일도 됩니다. 이번 시리즈에서는 페셉트론을 설명하고 퍼셉트론을 써서 간단한 문제를 풀 예정입니다. 딥러닝의 시작이라 할 수 있는 부분이므로 이번 시리즈는 매우 가볍게 진행될 것입니다.
1. 퍼셉트론이란?
퍼셉트론은 다수의 신호를 입력으로 받아 하나의 신호를 출력합니다. 여기서 말하는 신호란 전류처럼 흐름이 있는 것을 상상하시면 좋습니다. 전류가 전선을 타고 흐르는 전자를 내보내듯, 퍼셉트론 신호도 흐름을 만드록 정보를 앞으로 전달합니다. 다만, 실제 전류와는 달리 퍼셉트론의 신호는 ‘흐른다/안흐른다(1이나 0)’의 두 가지 값만을 가질 수 있습니다. 이 시리즈에서는 1을 ‘신호가 흐른다’, 0을 ‘신호가 흐르지 않는다’로 사용하겠습니다.

위의 그림은 2개의 신호를 받은 퍼셉트론의 예입니다. x1, x2는 입력 신호, y는 출력 신호, w1, w2는 가중치를 뜻합니다(w는 weight의 약자입니다). 그림의 원을 뉴런 혹은 노드라고 부릅니다. 입력 신호가 뉴런에 보내질 때는 각각 고유한 가중치가 곱해집니다(w1x1, w2x2). 뉴런에서 보내온 신호의 총합이 정해진 한계를 넘어설 때만 1을 출력합니다(이를 ‘뉴런이 활성화한다’라고 표현하기도 합니다). 보통 그 한계를 임계값이라 하며, Θ 기호로 나타냅니다.
퍼셉트론의 동작 원리는 이게 전부입니다! 와! 끝났어요!
그럼 이상으로 이번 시리즈를 마치………
고 싶지만 아직 알아볼 내용이 추가로 있습니다.(ㅠㅠ)
우선 지금까지 한 내용을 수식으로 표현하면 다음과 같습니다.

퍼셉트론은 복수의 입력 신호 각각에 고유한 가중치를 부여합니다. 가중치는 각 신호가 결과에 주는 영향력을 조절하는 요소로 작용합니다. 즉, 가중치가 클수록 해당 신호가 그만큼 더 중요함을 의미합니다.
2. 단순한 논리 회로
2-1. AND 게이트
그럼 이제부터 퍼셉트론을 이용한 간단한 문제들을 확인해보도고 하겠습니다. 우선 처음으로 볼 것은 AND 게이트입니다. AND 게이트는 입력이 둘이고 출력은 하나입니다.

위와 같은 입력 신호와 출력 신호의 대응 표를 진리표라고 합니다. 이 표는 AND 게이트의 진리표로, 두 입력이 모두 1일 때만 1을 출력하고, 그 외에는 0을 출력합니다. 그렇다면 이 AND 게이트를 퍼셉트론으로 표현하려면 어떻게 표현해야 할까요? 이를 하기 위해서 우리가 할 일은 w1, w2, Θ의 값을 정하는 것입니다. 이를 만족하는 매개변수의 조합을 무한히 많습니다. 예를 몇가지만 들자면 가 (0.5, 0.5, 0.7)일 때, (0.5, 0.5, 0.8), (1.0, 1.0, 1.0)일 때 모두 AND 게이트의 조건을 만족합니다. 매개변수를 이렇게 설정하면 x1, x2 모두가 1일 때만 가중 신호의 총합이 주어진 임계값을 웃돌게 됩니다.
2-2. NAND 게이트와 OR 게이트
이어서 NAND 게이트를 먼저 살펴봅시다. NAND는 Not AND를 의미하며 AND 게이트의 출력을 반대로 하면 됩니다. 진리표로 나타내면

다음과 같이 x1과 x2가 1일 때만 0을 출력하고 나머지 경우에는 1을 출력합니다. 이런 NAND 게이트를 표현하려면 예를 들어 (w1, w2, Θ) = (-0.5, -0.5, 0.7)와 같은 경우를 생각할 수 있을 것입니다. (사실 AND 게이트에서 매개변수의 부호를 전부 반전하면 NAND 게이트가 됩니다.)
그 다음으로 알아볼 것은 OR 게이트입니다. OR 게이트는 입력 신호 중 하나라도 1이면 출력이 1이 되는 논리 회로입니다. 우선 진리표는

다음과 같이 나타낼 수 있습니다. 이 OR 게이트의 매개변수는 (w1, w2, Θ) = (1.5, 1.5, 1.0)와 같은 경우를 생각할 수 있을 겁니다.
2-3. 정리
여기까지 퍼셉트론으로 AND, NAND, OR 논리 회로를 표현할 수 있음을 알았습니다. 여기서 가장 중요한 점은 퍼셉트론의 구조는 AND, NAND, OR 게이트에서 모두 같다는 것입니다. 세 가지 게이트에서 다른 것이라고는 매개변수의 값뿐입니다.
이렇게 퍼셉트론의 매개변수의 값을 정하는 것은 컴퓨터가 아닌 인간입니다. 인간이 직접 진리표라는 ‘학습 데이터’를 보면서 매개변수의 값을 생각합니다. 기계학습의 경우 이 과정을 컴퓨터가 자동으로 하도록 합니다. 학습이란 적절한 매개변수 값을 정하는 작업이며, 사람은 퍼셉트론의 구조를 고민하고 컴퓨터가 학습할 데이터를 주는 일을 합니다.
3. 퍼셉트론 구현하기
3-1. 간단한 구현
이제 논리 회로를 python으로 구현해봅시다! 다음은 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
매개변수 w1, w2, theta는 함수 안에서 초기화하고, 가중치를 곱한 입력의 총합이 임계값을 넘으면 1을, 그 외에는 0을 반환하는 함수입니다. 이상으로 AND 게이트의 구현을 마쳤습니다! 이 방식을 이용해 NAND, OR 게이트 모두 만들 수 있지만, 그 전에 잠시 내용을 추가해 보겠습니다.
3-2. 가중치와 편향 도입
앞에서 구현한 AND 게이트는 직관적이고 알기 쉽지만, 앞으로 배울 신경망 학습을 위해 다른 방식으로 수정하고자 합니다. 위의 식에서 Θ를 -b로 치환하면 퍼셉트론의 동작이 다음과 같은 식으로 됩니다.

위의 식은 기호 표기만 바꿨을 뿐, 그 의미는 같습니다. 이 때, b를 편향(bias)라고 합니다. 퍼셉트론은 입력신호에 가중치를 곱한 값과 편향을 합하여, 그 값이 0을 넘으면 1을 출력하고 그렇지 않으면 0을 출력합니다. 그럼 numpy를 이용해서 위의 식으로 구현을 해봅시다.
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.1999999999999999996 #대략 0.2(부동소수점에 의한 오차)
=====================
numpy 배열끼리의 곱셈은 두 배열의 원소 수가 같다면 각 원소끼리 곱합니다. 그래서 이 예의 w*x에서는 인덱스가 같은 원소끼리 곱합니다([0, 1] * [0.5, 0.5] => [0, 0.5]). 또한 np.sum() 매서드는 입력한 배열에 담긴 모든 원소의 총합을 계산합니다. 이 가중치에 편향을 더하면 위에서 봤던 식의 계산이 완료됩니다.
3-3. 가중치와 편향 구현하기
위에서 사용했던 numpy를 이용해 다시 구현한 AND 게이트는 다음과 같습니다.
import numpy as np
def AND(x1, x2):
x = np.array([x1, x2])
w = np.array([0.5, 0.5])
b = -0.7
tmp = np.sum(x*w) + b
if tmp <= 0:
return 0
else:
return 1
여기서 -Θ 가 편향 b로 치환되었습니다. 이 때, 헷갈리면 안되는 점이 w1 와 w2는 각 입력 신호가 결과에 주는 영향력(중요도)을 조절하는 매개변수고, b(편향)는 뉴런이 얼마나 쉽게 활성화 하느냐를 조정하는 매개변수 입니다. 예를 들어 편향이 -0.1이라면 입력 신호에 가중치를 곱한 값들의 합이 -0.1을 넘을때 활성화 합니다. 반면 편향이 -20.0이라면 각 입력 신호에 가중치를 곱한 값들의 합이 20.0을 넘지 않으면 뉴런이 활성화하지 않습니다.
이어서 NAND 게이트와 OR 게이트를 구현해보겠습니다.
import numpy as np
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(x*w) + 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(x*w) + b
if tmp <= 0:
return 0
else:
return 1
앞에서 언급했듯 AND, NAND, OR는 모두 같은 구조의 퍼셉트론이고, 차이는 가중치 매개변수의 값뿐입니다.
4. 퍼셉트론의 한계
이렇게 3가지 퍼셉트론으로 모든 것을 표현할 수 있었으면 얼마나 좋았을까요… 하지만 이 퍼셉트론도 한계점이 드러나게 되었습니다. 이 한계를 보여준 논리 회로가 바로 ‘XOR 게이트’입니다. XOR 게이트는 배타적 논리합이라는 논리 회로입니다.(‘배타적’이란 자신 외에는 거부한다는 의미입니다!)

다음과 같이 x1과 x2 둘 중 하나가 1일 때만 1을 출력합니다. 자, 그럼 이 XOR 게이트를 퍼셉트론으로 구현하려면 가중치 매개변수 값을 어떻게 설정하면 될까요?
여기서 퍼셉트론의 한계가 드러나게 됩니다. XOR 게이트는 퍼셉트론으로 구현할 수가 없습니다. 그 이유를 한번 시각적으로 알아보겠습니다.
우선 OR 게이트의 동작을 시각적으로 생각해보겠습니다. OR 게이트는, 예를 들어 가중치 매개변수가 (b, w1, w2) = (-0.5, -1.0, 1.0)일 때 다음과 같은 식으로 표현할 수 있습니다.

위 식의 퍼셉트론은 직선으로 나뉜 두 영역을 만듭니다. 직선으로 나뉜 한쪽 영역은 1을 출력하고 다른 한쪽은 0을 출력합니다. 이를 그려보면 다음과 같습니다.

0을 출력하는 곳은 원으로, 1을 출력하는 곳을 삼각형으로 표현하겠습니다. OR 게이트를 만들기 위해선 원과 삼각형을 ‘직선’으로 나눠야 합니다. 실제로 이 그림의 직선은 삼각형과 원을 잘 나누고 있습니다.
그렇다면 XOR 게이트를 볼까요?

어떻습니까? OR 게이트 처럼 직선으로 원과 삼각형을 나누는 영역을 만들 수 있을까요? 이 그림에서 원과 삼각형을 직선 하나로 나누는 방법은 존재하지 않습니다.
직선 하나로는 원과 삼각형을 나눌 수 없습니다. 하지만 ‘직선’이라는 제약을 없앤다면 가능하게 됩니다. 예를 들어 다음 그림과 같이 나타낼 수 있을 것입니다.

퍼셉트론은 직선 하나로 나눈 영역만 표현할 수 있다는 한계가 있습니다. 위의 그림과 같은 곡선은 표현할 수 없다는 것이죠. 추가로 아까 OR 게이트에서 봤던 직선의 영역을 선형 영역이라고 하고, XOR 게이트를 설명하며 봤던 곡선의 영역을 비선형 영역이라고 합니다. 선형과 비선형을 기계학습 분야에서 자주 쓰이는 용어로, 위의 이미지를 떠올리면 됩니다.
5. 다층 퍼셉트론이 출동한다면?
슬프게도 퍼셉트론으로는 XOR 게이트를 표현할 수 없었습니다. 하지만 퍼셉트론의 진짜 가치는 ‘층을 쌓아’ 다층 퍼셉트론을 만들 수 있다는데 있습니다. 이번 절에서는 층을 더 쌓아서 XOR을 표현해볼 것입니다.
XOR 게이트를 만드는 방법은 다양합니다. 그중 하나는 앞서 만든 AND, NAND, OR 게이트를 조합하는 방법입니다. 여기서는 3가지 게이트를 각각 다음과 같이 표현하겠습니다.

그럼 XOR 게이트를 만들기 위해선 어떻게 게이트들을 조합해야 할까요? 한번 생각해 보세요!
(힌트! 다음 그림의 ‘?’에 세 가지 게이트를 각각 하나씩 대입하면 XOR 게이트를 만들 수 있습니다!)

한번 생각해 보셨나요? 다음과 같은 조합이면 XOR 게이트를 만들 수 있습니다. x1과 x2가 입력신호, y가 출력 신호입니다. x1과 x2는 NAND와 OR 게이트의 입력이 되고, NAND와 OR의 출력이 AND 게이트의 입력으로 이어지게 됩니다.

그럼 이 조합이 정말로 XOR 게이트를 구현하는 지 알아보기 위해 진리표를 만들어 보겠습니다. 진리표를 만들면 다음과 같습니다.

이 진리표를 보면 XOR 게이트와 출력이 같음을 알 수 있습니다.
6. XOR 게이트 구현하기
그렇다면 이제 python으로 XOR 게이트를 구현해봅시다! 지금까지 정의했던 AND, NAND, OR을 사용하면 다음과 같이 구현할 수 있습니다. (지금까지 써온 코드 밑에 적으면 됩니다!)
def XOR(x1, x2):
s1 = NAND(x1, x2)
s2 = OR(x1, x2)
y = AND(s1, s2)
return y
------------------------
XOR(0, 0) #0을 출력
XOR(1, 0) #1을 출력
XOR(0, 1) #1을 출력
XOR(1, 1) #0을 출력
이 함수는 예상한 결과를 출력합니다.
이로써 XOR 게이트를 완성했습니다. 지금까지 구현한 XOR을 뉴런을 이용한 퍼셉트론으로 나타내면 다음과 같습니다.

지금까지 본 AND, OR 퍼셉트론과 형태가 다릅니다. 실제로 AND, OR가 단층 퍼셉트론인 데 반해 XOR는 2층 퍼셉트론입니다.(가중치를 가지는 층을 기준으로 층의 개수를 셉니다.) 이처럼 층이 여러개인 퍼셉트론을 다층 퍼셉트론 이라고 합니다.
다층 퍼셉트론은 지금까지 보아온 회로보다 복잡한 회로를 만들 수 있습니다.
컴퓨터는 퍼셉트론처럼 입력과 출력으로 구성된 특정 규칙대로 계산을 구행합니다. 컴퓨터 정보처리 과정이 매우 복잡할 거 같지만, 사실은 NAND 게이트의 조합만으로 컴퓨터가 수행하는 일을 재현할 수 있습니다. 그렇다면 어느 정도의 퍼셉트론이면 컴퓨터를 표현할 수 있을까요?
이론상 2층 퍼셉트론이면 컴퓨터를 표현할 수 있습니다! 와! 말도 안 되는 소리 같지만, 2층 퍼셉트론, 정확히는 비선형인 시그모이드 함수를 활성화 함수로 이용하면 임의의 함수를 표현할 수 있다는 사실이 증명되었습니다. 그러나 2층 퍼셉트론 구조에서 가중치를 적절히 설정하여 컴퓨터를 만들기란 매우 어렵습니다. 실제로도 NAND 등의 저수준 소자에서 시작해 컴퓨터를 만드는 데 필요한 모듈을 단계적으로 만들어가는 쪽이 자연스러운 방법입니다. 즉, 처음에서는 AND와 OR 게이트, 그다음은 반가산기와 전가산기, 그 다음으로는 ALU, 그다음은 CPU라는 식인 겁니다. 그래서 퍼셉트론으로 표현하는 컴퓨터도 여러 층을 다시 층층이 겹친 구조로 만드는 방향이 자연스러운 흐름입니다.
물론 저희는 컴퓨터를 만들것은 아니지만 퍼셉트론은 층을 거듭해 쌓으면 비선형적인 표현도 가능하고, 이론상 컴퓨터가 수행하는 처리도 모두 표현할 수 있다는 점을 알고 넘어가면 됩니다!
7. 정리
이번 글에서는 퍼셉트론을 배웠습니다. 퍼셉트론은 간단한 알고리즘이라 그 구조를 쉽게 이해할 수 있습니다. 퍼셉트론은 다음 글에서 배울 신경망의 기초가 됩니다. 그러니 이번 글에서 배운 내용은 매우 중요합니다.
이번 장에서 배운 내용
-
퍼셉트론은 입출력을 갖춘 알고리즘이다. 입력을 주면 정해진 규칙에 따른 값을 출력한다.
-
퍼셉트론에서는 ‘가중치’와 ‘편향’을 매개변수로 설정한다.
-
퍼셉트론으로 AND, OR 게이트 등의 논리 회로를 표현할 수 있다.
-
XOR 게이트는 단층 퍼셉트론으로는 표현할 수 없다.
-
2층 퍼셉트론을 이용하면 XOR 게이트를 표현할 수 있다.
-
단층 퍼셉트론은 직선형 영역만 표현할 수 있고, 다층 퍼셉트론은 비선형 영역도 표현할 수 있다.
-
다층 퍼셉트론은 (이론상) 컴퓨터를 표현할 수 있다.
이것으로 딥러닝을 배우기 위한 퍼셉트론을 알아봤습니다. 다음 시리즈에서는 신경망에 대해 알아보도록 하겠습니다.