이번 시간에는 신경망에 대해서 정리해보도록 한다.
퍼셉트론의 문제점
- 1층 퍼셉트론으로는 선형분리 외의 문제를 해결할 수 없음
- XOR 게이트는 2층 퍼셉트론으로 표현할 수 있다
퍼셉트론의 문제점은 다층 구조의 퍼셉트론 (MLP : Multi Layer Perceptron) 구조로 층을 늘리면 해결이 되지만, 어떻게 데이터로부터 자동으로 가중치를 학습할 것인지 배워야할 것이다.
신경망의 예
- 2층 신경망 의 구조는 입력층 (0층) / 은닉층 (1층) / 출력층 (2층) 으로 구성되어있다.
- 3층으로 구성되어 있지만, 가중치를 갖는 층은 2개이기 때문에, 2층 신경망으로 표시한다.
활성화 함수의 등장
- y = 0 (b + x1w1 + x2w2 <= 0)
- y = 1 (b + x1w1 + x2w2 > 0)
위 식을 다르게 표현하면
- y = h(b + x1w1 + x2w2)
- y = h(b + x1w1 + x2w2)
그리고
- h(x) = 0 (x <= 0)
- h(x) = 1 (x > 0)
이 식이 나온다.
활성화 함수는 입력 신호의 총합을 출력 신호로 변환하는 함수로, 입력 신호의 총합이 활성화를 일으키는지 판단한다.
- a = b + w1x1 + w2x2
- y = h(a)
활성화 함수의 종류
- 계단 함수
- 임계값을 경계로 출력이 바뀜
- 시그모이드 함수
- 0과 1사이의 연속적인 output
- 시그모이드 : S자 모양 뜻
- 미분이 가능하다.
- ReLU 함수
퍼셉트론 → 신경망
- 뉴런이 여러 층으로 이어지는 구조나 신호 전달 방법은 같음
- 활성화 함수가 계단 함수가 아닌 다른 함수로 변경
- 신경망 부터는 활성화 함수로 시그모이드 함수를 사용
계산 함수 구현
import numpy as np
def step_function(x):
y = x >0
return y.astype( np.int )
x = np.array([-1.0, 1.0, 2.0])
print(x)
# [-1. 1. 2.]
y = x >0
print(y)
# [False True True]
y = y.astype(int)
print(y)
# [0 1 1]
계단 함수의 그래프
import matplotlib.pyplot as plt
X = np.arange(-5.0, 5.0, 0.1)
Y = step_function(X)
plt.plot(X, Y)
plt.ylim(-0.1, 1.1)
plt.show()
시그모이드 함수 구현
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
x = np.array([-1.0, 1.0, 2.0])
print( sigmoid(x) )
# [0.26894142 0.73105858 0.88079708]
시그모이드 함수의 그래프
import matplotlib.pyplot as plt
x = np.arange(-5.0, 5.0, 0.1)
y = sigmoid(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1)
plt.show()
시그모이드 함수와 계단 함수 비교
- 차이점 1
- 시그모이드 함수는 부드러운 곡선이며, 입력에 따라 출력이 연속적으로 변화
- 계단 함수는 0을 경계로 출력이 갑자기 변화
- 차이점 2
- 계단 함수는 0 아니면 1 둘 중 하나의 값 반환
- 시그모이드는 0과 1 사이의 실수를 반환
- 공통점
- 입력이 작을 때 출력은 0, 입력이 커지면 출력이 1이 되는 구조
- 입력이 아무리 작거나 커도 출력은 0에서 1 사이의 값
- 직선 하나로는 그릴 수 없는 비선형 함수
신경망에서 활성화 함수로 비선형 함수를 사용해야하는 이유는 신경망의 층을 깊게하는 효과를 얻을 수 있고, 층을 늘려도 활성화 함수가 선형 함수라면 은닉층이 없는 네트워크로 표현 되기 때문에 비선형 함수를 써야한다.
- 선형 함수 h(x) = cx 를 활성화 함수로 사용한 3층 네트워크 식으로 나타나면 y(x) = h(h(h(x))) 이고, 결국 y(x) = c*c*c*x 처럼 곱셈을 세번 하지만 y(x) = ax 와 같은 똑같은 식
- 즉, 은닉층이 없는 네트워크로 표현할 수 있음
ReLU 함수
- 최근에는 주로 ReLU (Rectified Linear Unit) 함수 이용
- 입력이 0을 넘으면 입력을 그대로 출력
- 입력이 0 이하이면 0을 출력
ReLU 함수의 구현
import numpy as np
def relu(x):
return np.maximum(0, x)
A = np.array([1, 2, 3, 4])
print(A)
# [1 2 3 4]
print( np.ndim(A) )
#1
print( A.shape )
# (4,)
print( A.shape[0] )
#4
신경망에서의 행렬 곱
신경망에서는 가중치 합을 구할 때, ndarray(다차원 행렬 자료구조) 끼리의 곱셈으로 신경망을 구현하기 때문에 행렬에 대해 잘 알아야 한다.
3층 신경망 구현해보기
- 입력층(0층) - 2개
- 첫 번째 은닉층(1층) - 3개
- 두 번째 은닉층(2층) - 2개
- 출력층(3층) -2개
이렇게 가정해본다.
import numpy as np
# 0 ~ 1층
X = np.array([1.0, 0.5]) # 2
W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]]) # 2*3
B1 = np.array([0.1, 0.2, 0.3])
A1 = np.dot(X, W1) + B1
print(A1) # [0.3 0.7 1.1]
Z1 = sigmoid(A1)
print(A1) # [0.3 0.7 1.1]
print(Z1) # [0.57444252 0.66818777 0.75026011]
# =====================================================
# 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)
print(A2) # [0.51615984 1.21402696]
print(Z2) # [0.62624937 0.7710107 ]
# =====================================================
# 2 ~ 3층
# 출력층 활성화 함수 - 항등 함수 사용
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)
print(Y) # [0.31682708 0.69627909]
더 간단히 표현하면 이렇게 된다.
조금만 더 손을 보면 반복문을 사용해서 좀 더 효율적인 구조로 짜볼 수도 있을 것 같다.
import numpy as np
def init_network():
network = {}
network['W1'] = np.array([[0.1, 0.3, 0.5], [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.4], [0.3, 0.6]])
network['b2'] = np.array([0.1, 0.2])
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(y)
지도학습 (Supervised Learning)
머신러닝의 알고리즘을 분류할 때 3가지 분류(지도, 비지도, 강화 학습) 이렇게 나뉘는데, 그 중에 지도 학습에 대한 내용이다.
- 분류
- 미리 정의된 클래스 중 하나를 예측하는 것
- 스팸메일 / 정상메일 분류
- 손글씨 분류
- 미리 정의된 클래스 중 하나를 예측하는 것
- 회귀
- 연속적인 수를 예측하는 것
- 기상데이터로 내일 기온 예층
- 공부시간으로 성적 예측
- 연속적인 수를 예측하는 것
출력층 설계
신경망은 분류와 회귀 모두에 이용 가능하다.
- 문제에 따라 출력층에서 사용하는 활성화 함수를 다르게 사용해야 한다.
- 회귀 - 항등 함수 사용
- 분류 - 소프트맥스 함수 사용
항등 함수
- 항등 함수는 입력을 그대로 출력한다.
소프트맥스 함수
- 클래스 분류를 위해 마지막 출력값을 정규화 하는 함수
- 모든 결과 수치의 합을 1.0 으로 만들어 주기 때문에 각 클래스에 대한 확률로써 수치화하기에 좋다.
- 학습 단계에서는 출력층에서의 소프트맥스 함수를 사용하지만, 추론 단계에서는 출력층에서의 소프트맥스 함수를 생략할 수 있다.
- 추론에서는 가장 큰 값의 클래스만 알면 되기 때문이다.
- 출력층의 뉴런 수는 풀려는 문제에 맞게 적절히 정해야 한다.
- 예를 들어 숫자 손글씨 이미지를 0~9로 분류하는 문제에서, 출력층의 뉴런은 10개 이다.
정리
- 신경망 활성화 함수 - 활성화 여부 판단
- 은닉층 활성화 함수로 시그모이드, ReLU 함수 등 사용
- 출력층 활성화 함수로 회귀 문제에서는 항등 함수를, 분류 문제에서는 소프트맥스 함수를 사용한다.
- 분류 문제에서 출력층의 뉴런 수는 분류하려는 클래스의 수와 같게 한다.
우선 이론적인 내용을 쭉 다뤄봤는데
이후엔 좀 더 코드 중심으로 다뤄볼까 한다..
'Dev > AI' 카테고리의 다른 글
[리뷰] 랭체인으로 LLM 기반의 AI 서비스 개발하기 (0) | 2024.05.15 |
---|---|
[리뷰] 머신러닝 교과서: 파이토치 편 (0) | 2024.02.21 |
[인공지능개론] 신경망 학습에 대해서 (0) | 2022.05.02 |
[인공지능개론] 퍼셉트론(Perceptron)에 대해서 (2) | 2022.04.11 |
파이썬으로 할 수 있는 것들. (0) | 2019.08.23 |