ML – ANN 이론

딥러닝의 기초가 되는 Artificial Neural Network(인공 신경망)을 소개하겠습니다.

ANN모델은 뇌의 정보 처리 방법을 모방한 학습 모델입니다.

ANN의 특성은 다음과 같습니다.

  • 학습 가능
  • 뛰어난 일반화 능력
  • 병렬 처리 가능
  • 현실적 문제에서 우수한 성능
  • 다양한 문제 해결 도구 (분류,예측,함수 근사화,합성,평가)


ANN의 기초인 퍼셉트론부터 살펴 보겠습니다!

퍼셉트론은 뉴런을 모방한 모양을 가지고 있습니다.
다수의 신호를 입력으로 받아 하나 혹은 여러 신호를 출력하는 역할을 합니다.

입력은 x(x1,x2,..xn)로 표현되고 가중치는 w(w1,w2,..wn), 출력은 y로 표현됩니다.

위 그림에는 나오지 않았지만 activation function(활성화 함수)이라는 것도 있습니다.
activation function은 신호의 총합이 임계치(theta)를 넘으면 1, 아니면 0(-1)을 출력하는 역할을 합니다.

Regression(회귀)는 값이 실수로 나와야 되기 때문에 activation function을 적용하지 않습니다.
Classification(분류)는 값이 정수로 나와야 되기 때문에 activation function을 적용합니다.


퍼셉트론을 이용한 분류 모델 구조입니다.

feature가 d개일 때, 즉 d차원일 때 입력층은 d+1개의 노드를 가지고 있습니다.

1차원일 때 신호의 총합 s는 w1x1+b 가 됩니다.
2차원일 때 신호의 총합 s는 w1x1+w2x2+b가 됩니다.
d차원일 때 신호의 총합 s는 w1x1+w2x2+w3x3+….+wdxd+b가 됩니다.
s는 선형 모델, 결정 경계와 같은 식(d(x)=wtx+b)이 됩니다.

즉 퍼셉트론은 선형 모델에 활성 함수(activation function)가 추가되었다고 생각하면 됩니다.

활성 함수에 s값이 온 경우, 0(theata)보다 크면 1을 반환하고 0보다 작으면 -1을 반환합니다.
어떤 활성 함수인지에 따라 1이나 0을 반환할 수 도 있습니다.

결국 분류 문제에서 출력 값(y)은 신호 총합 s를 활성 함수 T에 대입해서 얻은 1 or -1이 됩니다.


퍼셉트론으로 논리 게이트 만들기

퍼셉트론으로 OR 게이트(분류기)를 만들어 보겠습니다.

대부분의 논리 게이트는 입력이 2개, 즉 feature가 2개인 2차원입니다.

2차원일 때의 신호 총합 s를 구하고 활성 함수 T에 대입해서 분류를 할 것입니다.
그러나 우리는 분류 결과를 이미 알고 있습니다.

OR 게이트는 입력 중 하나라도 1이 오면 1로 분류되고, 그렇지 않으면 0으로 분류합니다.
(다만 활성 함수는 1이나 -1을 반환하니 1이나 -1로 분류한다고 생각하겠습니다.)

결국 입력과 출력을 알고 있으니 s식(w1x1+w2x2+b)에 포함되는 w(가중치)와 b(bias)는 직접 구할 수 있습니다.

아래처럼 w를 (1,1), b를 -0.5로 설정하면 OR게이트가 완성됩니다.

위의 과정에서 theta(임계치)는 0입니다.
a는 -0.5가 돼서 -1로 분류, 나머지는 0보다 커서 1로 분류되었습니다.

만약 theta를 0으로 지정하지 않고 풀려면 theta=-b(x0노드에서 나가는 가중치)로 설정하고 (x0노드 및 b)는 사용하지 않으면 해결됩니다.
그렇게 하면 결정 직선 d(x)=x1+x2가 되고, d(a)는 0인데 0.5보다 작으므로 -1로 분류, 나머지는 0.5보다 크므로 1로 분류됩니다.

같은 이치로 다른 게이트(분류기)도 만들 수 있습니다.

Ex) a=(0,0)T -> 0+0 < 0.7 -> 0,
d=(1,1)T -> 0.5+0.5 >0.7 -> 1

Ex) a=(0,0)T -> 0+0 > -0.7 -> 1,
d=(1,1)T -> -0.5-0.5 < -0.7 -> 0


위에서 만든 분류기를 파이썬 함수로 만들면 아래 코드와 같습니다.

위처럼 입력과 출력을 알고 있으면 w와 b는 쉽게 선정할 수 있다고 생각할 수 있습니다.
하지만 이러한 단순한 퍼셉트론 분류로는 XOR 게이트를 만들 수 없다는 것이 특징입니다!


퍼셉트론 모델 구현 (퍼셉트론 학습)

위에서 설명한 분류기는 이미 정보를 알고 있는 상태에서 만든 것입니다.

이번에는 샘플만 주어졌고 아무런 정보가 없을 때, 좋은 분류 모델을 만드는 방법에 대해 소개하겠습니다.

기계학습 방법론 3단계를 거쳐서 최적의 분류 모델을 만들 것입니다.

단계 1: 퍼셉트론 선형 분류기를 사용하겠다.

앞서 설명했지만 분류 문제에서 출력 값 y는 신호 총합 s를 활성 함수 T에 대입해서 얻은 1 or -1입니다.
활성 함수 T는 매개변수가 0보다 작으면 -1, 아니면 1을 반환합니다.

위 문구는 아래의 수식과 완전히 같습니다.

위 수식으로 분류기 구조를 정의합니다.

단계 2: 선형 분류기 품질 체크를 위한 LossFunction을 정의하겠다.

모델 품질을 체크할 수 있는 손실 함수(Loss Function)는 위처럼 정의합니다.

이 때, Z(시그마 안쪽 식)는 오분류가 되면 무조건 0보다 크게 나옵니다.

논리 게이트로 예를 들겠습니다.
AND게이트에 입력으로 d(1,1)를 넣으면 1이 나오기 때문에 wTx+b가 0보다 크게 나오는 것이 맞습니다.
그런데 만약 오분류가 발생한다면 wTx+b가 0보다 작다고 동작할 것입니다.
이 때 td=1이고 wTx+b는 음수이니 Z값은 0보다 커지게 됩니다.

예시 d뿐만 아니라 더 많은 오류가 일어난다면, 즉 오분류된 샘플이 많다면 양수인 Z값들이 많아 진다는 것이고, J값도 커지게 됩니다. 결국 |Y|가 커질수록 J가 커집니다.

반대로 오분류 샘플이 없으면 J값은 최소치(0)가 됩니다. 즉 Y가 공집합이면 J는 0입니다.

결론적으로 J가 0이 나오는 모델이 최적의 모델이 됩니다.

단계3: 선형 분류기 품질을 가장 좋게 하기 위해 최적화를 하겠다. (J값 최소화)

J값을 최소화하는 것이 목적이니 경사하강법이 최적화하기에 가장 좋습니다.

경사하강법은 경사도를 이용하여 손실함수의 최솟값을 찾는 방법입니다.

임의로 초기 값(w,b)을 지정한 후에 미분한 값이 0이 되는지(극솟값인지) 확인합니다.
0이 아니면 기울기가 감소하는 방향으로 w,b를 업데이트합니다.

이 때 기울기가 감소하는 폭을 ‘학습률(learnig rate)‘이라고 합니다.
학습률이 너무 크면 발산해서 최적의 모델을 구하지 못 할 수도 있습니다.

즉 위 그림처럼 미분 값이 0인 지점을 찾지 못해서 지그재그 형태로 역행하는 일이 발생할 수 있습니다.

반대로 학습률이 너무 작으면 최적의 모델을 구할 수는 있으나 상당히 느리게 구할 것입니다.


아래는 경사하강법을 이용한 퍼셉트론 최적화 알고리즘입니다.
Y가 공집합이 될 때까지(J가 0이 될 때까지) w(weight)와 b(bias)는 계속 업데이트 될 것입니다.


다중 퍼셉트론 (MLP)

XOR의 한계를 이겨내기 위해 퍼셉트론을 다중으로 사용하는 모델을 다중 퍼셉트론이라고 합니다.

위와 같은 MLP를 ‘층이 2개인 Neural Network’ 혹은 ‘은닉층이 1개인 Neural Network’라고 표현합니다.

위와 같은 MLP를 ‘층이 3개인 Neural Network’ 혹은 ‘은닉층이 2개인 Neural Network’라고 표현합니다.
또한 위 에시들은 모든 노드들이 다 연결돼 있으므로 Fully-connected Layers에 해당합니다.

Unit(유닛,노드)와 Layer(층)이 많아질수록 정확도는 높아집니다.
Unit(유닛,노드)과 Layer가 너무 많으면 오버피팅이 발생할 수 있습니다.


활성화 함수의 필요성

위같이 간단한 단일 퍼셉트론은 f=Wx식을 만족합니다.

만약 층이 1개 더 늘었다면 f= W2(W1X)식을 만족합니다.
그러나 이 식을 정리하면 결국 f=Wx가 됩니다.

결국 MLP의 층이 아무리 많더라도 선형 분류기에 해당한다는 의미입니다.

만약 비선형 분류를 하고 싶으면 Activation Function을 이용하면 됩니다.
아래 함수 f는 비선형의 결정 경계를 나타낼 것입니다!


파이썬 이용 MLP 모델 구축

마지막으로 파이썬을 이용해서 MLP모델을 구축하고 분석하겠습니다.

from sklearn.neural_network import MLPClassifier
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from preamble import *

# 분류를 위한 100개의 샘플 생성 
X,y=make_moons(n_samples=100,noise=0.25,random_state=3)
# stratify=y (=>) 치우쳐지지 않고 5대5 비율을 만듦 
X_train,X_test,y_train,y_test=train_test_split(X,y,stratify=y,random_state=0) 
mglearn.discrete_scatter(X_train[:,0],X_train[:,1],y_train)

# 100개의 유닛 수(default) 
mlp=MLPClassifier(solver='lbfgs',random_state=0).fit(X_train,y_train)
mglearn.plots.plot_2d_separator(mlp,X_train,fill=True,alpha=.3)
mglearn.discrete_scatter(X_train[:,0],X_train[:,1],y_train)
plt.xlabel("feature 0")
plt.ylabel("feature 1")

# 10개의 유닛수, 유닛(노드)과 레이어 늘릴수록 결과가 좋아짐. -> 너무 많이 높으면 오버피팅 발생
# 10개는 유닛이 너무 적으므로 언더피팅이 발생할 수 있음 
mlp=MLPClassifier(solver='lbfgs',random_state=0,hidden_layer_sizes=[10]).fit(X_train,y_train)
mglearn.plots.plot_2d_separator(mlp,X_train,fill=True,alpha=.3)
mglearn.discrete_scatter(X_train[:,0],X_train[:,1],y_train)
plt.xlabel("feature 0")
plt.ylabel("feature 1")

# 10개의 유닛으로 된 두 개의 은닉층
mlp=MLPClassifier(solver='lbfgs',random_state=0,hidden_layer_sizes=[10,10]).fit(X_train,y_train)
mglearn.plots.plot_2d_separator(mlp,X_train,fill=True,alpha=.3)
mglearn.discrete_scatter(X_train[:,0],X_train[:,1],y_train)
plt.xlabel("feature 0")
plt.ylabel("feature 1")

# tanh활성화 함수가 적용된 10개의 유닛으로 된 두 개의 은닉층
mlp=MLPClassifier(solver='lbfgs',activation='tanh',random_state=0,hidden_layer_sizes=[10,10]).fit(X_train,y_train)
mglearn.plots.plot_2d_separator(mlp,X_train,fill=True,alpha=.3)
mglearn.discrete_scatter(X_train[:,0],X_train[:,1],y_train)
plt.xlabel("feature 0")
plt.ylabel("feature 1")

# 규제를 다르게 한 여러 모델 분석 
fig,axes=plt.subplots(2,4,figsize=(20,8))
for axx, n_hidden_nodes in zip(axes,[10,100]):
    for ax, alpha in zip(axx,[0.0001,0.01,0.1,1]):
        mlp=MLPClassifier(solver='lbfgs',random_state=0,hidden_layer_sizes=[n_hidden_nodes,n_hidden_nodes],alpha=alpha)
        mlp.fit(X_train,y_train)
        mglearn.plots.plot_2d_separator(mlp,X_train,fill=True,alpha=.3,ax=ax)
        mglearn.discrete_scatter(X_train[:,0],X_train[:,1],y_train,ax=ax)
        ax.set_title("n_hidden=[{},{}]\alpha={:.4f}".format(n_hidden_nodes,n_hidden_nodes,alpha))

위는 유닛(노드)가 10개, 레이어가 1개인 모델
아래는 유닛(노드)가 10개, 레이어가 2개인 모델입니다.

레이어가 많을수록 정확도는 높습니다. (유닛도 마찬가지)

이번에는 규제를 다르게 해보았습니다.
규제를 많이 할수록 (Alpha가 커질수록) 일반화는 많이 되고 정확도는 떨어집니다.

감사합니다.

Leave a Reply

Your email address will not be published. Required fields are marked *