Deep Learning I 1~2강

길하균(Lagun)·2023년 2월 2일

Deep Learning I

목록 보기
2/6

Deep Learning I 1강 퍼셉트론

1강에서는 인공신경망에서 가장 기본 단위가 되는 '퍼셉트론'과 AND, NAND, OR, XOR 게이트를 퍼셉트론을 이용해 구현하는 방법에 대해 학습했습니다.

퍼셉트론이란?

우선 퍼셉트론이 무엇인지 간략히 설명하자면 인간의 신경계를 구성하는 뉴런의 동작 방식을 모방한 알고리즘입니다. 뉴런은 입력된 값들의 합이 임계값보다 높으면 전기신호를 다음 뉴런에게 보내고 낮으면 보내지 않는 방식입니다. 퍼셉트론도 이와 동일하게 입력된 값들의 합이 임계값보다 높으면 1을 출력하고 낮으면 0을 출력합니다. 입력된 전기신호의 세기를 조절하기 위해 퍼셉트론에는 가중치라는 변수가 존재합니다. 이 가중치를 이용하여 어떤 입력값을 더 중점적으로 다룰지를 조절할 수 있습니다.

단층 퍼셉트론과 XOR 게이트

퍼셉트론은 단층 퍼셉트론과 다층 퍼셉트론이 존재합니다. 단층 퍼셉트론은 말 그대로 하나의 층으로 구성된 퍼셉트론을 의미합니다. 단층 퍼셉트론을 이용하면 AND, NAND, OR 게이트를 구현하는데 문제가 없지만 XOR 게이트를 구현할 수 없다는 문제가 발생합니다. XOR 게이트는 (x1, x2)의 값이 (1,0), (0,1)일 때 1을 출력해야 하지만 단층 퍼셉트론만을 이용할 때는 OR 게이트와 구분할 수 없다는 문제가 발생합니다. 이러한 문제는 좌표평면에 점과 직선을 그려서 확인하면 직관적인 이해가 가능합니다. 좌표평면에 (0,0), (1,0), (0,1), (1,1) 네 점이 존재한다고 가정할 때 (1,0), (0,1) 두 점과 (0,0), (1,1) 두 점을 분리하는 직선을 어떤 방식으로도 그릴 수 없음을 알 수 있습니다. 따라서 XOR 게이트를 구현하기 위해서는 다층 퍼셉트론을 활용하여야 합니다.

코드로 구현한 논리 게이트

AND, NAND, OR 게이트 모두 기본적인 형태는 동일합니다. 다만 가중치와 임계값을 어떻게 설정하냐에 따라 AND 게이트가 될 수도 있고 OR게이트가 될 수도 있습니다. 따라서 우선 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(w*x) + b
    if tmp <= 0:
        return 0
    else:
        return 1


for xs in [(0, 0), (1, 0), (0, 1), (1, 1)]:
    y = AND(xs[0], xs[1])
    print(str(xs) + " -> " + str(y))

코드에서 xs는 입력받은 두 신호를 의미합니다. for 문을 이용하여 xs 안에 존재하는 4개의 신호들을 AND 함수에 넣어줍니다. AND 함수는 입력받은 x1과 x2가 모두 1일 때 1을 return하는 함수입니다. 이를 구현하기 위해 가중치는 [0.5, 0.5]를 설정하였고 임계값은 0.7로 설정하므로 b=-0.7로 설정합니다. 위의 코드를 돌리면 아래와 같은 값이 나옵니다.

(0, 0) -> 0
(1, 0) -> 0
(0, 1) -> 0
(1, 1) -> 1

NAND 게이트의 경우 AND 게이트의 반대이므로 AND의 가중치와 임계값을 반대로 설정하면 됩니다. 따라서 가중치는 [-0.5, -0.5]로 설정하고 임계값은 b=7로 설정합니다. OR 게이트의 경우 x1, x2 두 값중 하나만 1이면 되므로 가중치는 AND 게이트와 동일하게 설정하되 임계값을 b=-0.2로 낮게 설정합니다. NAND 게이트와 OR 게이트를 코드로 구현하면 아래와 같습니다.

def NAND(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
        
def OR(x1,x2):
    x = np.array([x1, x2])
    w = np.array([0.5, 0.5])
    b = -0.2
    tmp = np.sum(w*x) + b
    if tmp <= 0:
        return 0
    else:
        return 1

XOR 게이트를 코드로 구현하기 위해서는 앞서 말씀드린 것처럼 다층 퍼셉트론을 활용하여야 합니다. XOR 게이트를 구현하기 위해서는 1층에 NAND 게이트와 OR 게이트를 배치하고 2층에 AND 게이트를 배치하면 됩니다. 퍼셉트론을 이렇게 구현하면 (x1, x2)가 (1,0)일 때 NAND 게이트와 OR 게이트 모두 1이 출력되므로 AND 게이트 역시 1을 출력하여 (x1, x2)가 (1,0)일 때 1을 출력할 수 있게 됩니다. XOR을 코드로 구현하면 아래와 같습니다.

def XOR(x1, x2):
    s1 = NAND(x1, x2)
    s2 = OR(x1, x2)
    y = AND(s1, s2)
    return y

Deep Learning I 2강 활성화 함수

2강에서는 퍼셉트론에서 사용되는 '활성화 함수'의 이론과 활성화 함수들을 코드로 구현하는 방법에 대해 학습했습니다.

활성화 함수란?

활성화 함수란 이름에서 짐작할 수 있듯이 입력받은 신호를 다음 퍼셉트론에게 전달할지를 결정하는 함수라고 생각하시면 됩니다. 입력받은 신호들 각각의 가중치를 곱해준 값들과 편향(=임계값)을 모두 합한 것을 함수에 입력하여 출력된 값을 다음 퍼셉트론에게 전달합니다. 신경계의 뉴런은 1과 0만을 출력하지만 퍼셉트론에서도 똑같이 구현하면 여러 문제가 발생하므로 활성화 함수는 뉴런의 작동 방식과 조금은 다르게 구현되었습니다.

활성화 함수의 종류

뉴런은 Heaviside 함수와 같이 신호가 임계값보다 낮으면 0, 크다면 1을 출력하는 방식으로 작동합니다. Heaviside 함수는 선형 함수이기 때문에 학습을 할 때 미분을 이용하는 머신러닝에 적용할 수 없습니다. 따라서 Heaviside와 비슷한 역할을 하는 Sigmoid 함수를 이용합니다. Sigmoid 함수는 임계값보다 강한 신호를 입력받으면 1에 가까운 값을 반환하고 임계값보다 약한 신호를 입력받으면 0에 가까운 값을 반환합니다. Sigmoid 함수는 오래전부터 쓰였지만 기울기 소멸 문제와 같은 단점이 존재하여 이를 극복하기 위해 ReLU 함수를 주로 이용합니다. ReLU 함수는 임계값을 넘지 못한 신호는 Heaviside와 같이 0을 출력하고 임계값을 넘은 신호는 그대로 출력하는 형식입니다.

코드로 구현한 활성화 함수

활성화 함수를 코드로 구현하는 것은 매우 간단합니다. 함수에 입력할 값을 어떻게 처리할지 식만 정해주면 됩니다.

def heaviside(x):
    return np.array(x > 0, dtype=np.int)

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def relu(x):
    return np.maximum(0, x)

sigmoid 함수의 np.exp(-x)는 자연상수에 -x 제곱한 것을 의미합니다. 처음 relu를 코드로 구현하는 방법을 고민할 때는 if문을 이용해 x<0라면 0을 출력하도록 해야하나 생각했었습니다. 하지만 강의와 교재의 코드를 보니 np.maximum(0,x)를 이용해 0과 x 중 큰 값을 출력하도록 하면 relu 함수를 구현할 수 있었습니다. 역시 코딩하는 방법은 굉장히 다양하고 유연한 사고가 필요함을 느꼈습니다. Deep Learning을 배우는 것도 중요하지만 배운 지식을 코드로 구현하는 공부도 더 해야겠다는 생각이 들었습니다.

profile
AI, 빅데이터를 배우기 위해 항해 중

0개의 댓글