1. 선형분류기의 실패
•
결정 경계가 비선형이거나, 데이터를 완전히 분할하기 어려운 경우 선형 분류기의 성능이 저하됨.
•
특히 XOR 연산은 선형 함수로 구현이 불가하다.
2. 비선형 분류
•
AND, OR, NAND 연산은 선형 함수로 구현이 가능함.
◦
1-layer perceptron으로 구현이 가능하다는 것
◦
입력 데이터에 가중치가 곱해지고, 해당 값이 계단 함수를 거치도록 하면, 각 연산을 구현할 수 있다.
•
NAND, OR을 거친 데이터에 AND 연산을 취하면 NAND 구현이 가능
•
Layer를 깊게 쌓으면 비선형 분류가 가능
3. 신경망의 입력 데이터
•
이미지
◦
1-D : Flatten
◦
2-D : CNN 계열
•
시계열
◦
1-D
◦
3-D : 동영상
•
텍스트
◦
One-hot encoding
▪
각 단어를 고차원 공간에서 서로 다른 단 하나의 위치만 1인 벡터로 표현하여, 단어 간 유사도나 관계 없이 독립적으로 처리하는 방식
4. 신경망 구조
•
1-layer
◦
Input : 데이터의 종류에 따라 고정된 크기
◦
Output : 목적에 따라 고정된 크기
▪
위의 예에서는 10개의 class 중 하나로 구분하는 것이므로 output은 10개의 unit으로 표현됨
⇒ Input unit과 output unit 사이를 적절히 mapping하는 가중치 를 학습
•
2-layer
◦
Input : 데이터의 종류에 따라 고정된 크기
◦
Output : 목적에 따라 고정된 크기
◦
Hidden : Hidden의 개수는 표현 가능한 정보량에 비례함
◦
활성함수 : 모델의 목적에 따라 다름
▪
Softmax(다중 분류), Sigmoid(이진 분류) ..
•
2-layer 예시를 보면, 마지막에는 Hidden을 선형 분류하는 것과 마찬가지임을 알 수 있다. 즉, MLP는 여러 층을 거치면서 입력 데이터를 선형 분류 가능한 형태로 변환하고 마지막 층에서 선형 분류를 실시하는 형태임을 알 수 있다.
5. 손실함수
•
실제 값과 예측 값의 차이를 정의하는 방식
◦
SSE, MSE, RMSE : 회귀에서 주로 사용
◦
Cross-entropy : 분류에서 주로 사용
•
분류 문제의 경우 sigmoid 함수나 softmax함수를 통해 얻은 특정 class에 속할 확률 값을 바탕으로 Cross-entropy를 목적함수로 하여 MLE를 실시
◦
정답값에 대해 그 확률값을 높이는 방식으로 학습을 진행
6. MLP 최적화
•
역전파 알고리즘 사용
◦
손실 함수를 정의하고 이를 바탕으로 가중치 update를 진행하고자 할 때, 일반적으로 gradient descent를 사용함.
◦
MLP에서는 손실함수가 합성 함수를 포함하고 있어, 특정 가중치가 최종 출력에 직접적으로 기여하는 정도를 정확히 계산하기 위해서는 역방향으로 update를 진행하여야 함.
•
Gradient 계산
◦
위와 같이 계산할 경우 각 편미분 값 계산이 용이하며, 이전 층 연산에서 사용한 결과를 재활용할 수 있어 연산량에서 이점을 가진다. (위의 예시에서 빨간 박스)
7. Code
import numpy as np
# 데이터 생성
np.random.seed(0)
X = np.random.randn(100,2)
y = (X[:, 0]*X[:,1]>0).astype(int).reshape(-1,1)
# 활성함수 & 미분값 정의
def sigmoid(x):
return 1/(1+np.exp(-x))
def d_sigmoid(x):
s = sigmoid(x)
return s*(1-s)
def relu(x):
return np.maximum(0,x)
def d_relu(x):
return(x>0).astype(int)
# 초기값 설정
input_size = 2
output_size = 1
hidden1_size = 5
hidden2_size = 3
lr = 0.1
# 가중치 초기화
w1 = np.random.randn(input_size, hidden1_size) * np.sqrt(2 / input_size)
b1 = np.zeros((1,hidden1_size))
w2 = np.random.randn(hidden1_size, hidden2_size) * np.sqrt(2 / hidden1_size)
b2 = np.zeros((1,hidden2_size))
w3 = np.random.randn(hidden2_size, output_size) * np.sqrt(2 / hidden2_size)
b3 = np.zeros((1,output_size))
# 학습
for e in range(10000):
#forward
z1 = X @ w1 + b1
a1 = relu(z1)
z2 = a1 @ w2 + b2
a2 = relu(z2)
z3 = a2 @ w3 + b3
a3 = sigmoid(z3)
loss = -np.mean(y*np.log(a3+1e-8)+(1-y)*np.log(1-a3+1e-8))
if e==99 :
print(loss)
# backward
N = X.shape[0]
dz3 = a3 - y
dw3 = a2.T @ dz3 / N
db3 = np.mean(dz3, axis=0, keepdims=True)
da2 = dz3 @ w3.T
dz2 = da2 * d_relu(z2) # relu의 gradient는 해당 뉴런에만 영향을 줌
dw2 = a1.T @ dz2 / N
db2 = np.mean(dz2, axis=0, keepdims=True)
da1 = dz2 @ w2.T
dz1 = da1 * d_relu(z1)
dw1 = X.T @ dz1 / N
db1 = np.mean(dz1, axis=0, keepdims=True)
w3 -= lr * dw3
b3 -= lr * db3
w2 -= lr * dw2
b2 -= lr * db2
w1 -= lr * dw1
b1 -= lr * db1
# 평가
y_pred = (a3 > 0.5).astype(int)
acc = np.mean(y_pred == y)
print(f"accuracy: {acc:.2f}"
Python
복사