앞서 인공지능, 머신러닝, 딥러닝에 대해 간략하게 언급해보았다. 해당 내용만으로는 앞으로 우리가 학습해나갈 머신러닝에 대해 구체적으로 알기 어려우므로, 이번에는 전통적인 알고리즘과 머신러닝 알고리즘을 직접 비교해보도록 하겠다.
명시적인 프로그래밍 없이 컴퓨터가 학습하는 능력을 갖추게 하는 연구 분야 - 아서 새뮤얼(Arthur Samuel), 1959 -
어떤 작업 T에 대한 컴퓨터 프로그램의 성능을 P로 측정하였을 때, 경험 E로 인해 성능이 향상됐다면, 이 컴퓨터 프로그램은 작업 T와 성능 측정 P에 대해 경험 E로 학습한 것이다. - 톰 미첼(Tom Mitchell) 1998 -
머신러닝(Machine Learning)이라는 용어를 만든 아서 새뮤얼은 머신러닝에 대해 컴퓨터가 학습하는 능력을 갖추게 하는 연구 분야라고 하였다. AI 분야의 권위자인 톰 미첼은 경험 E에 의해 어떤 작업 T의 성능을 측정한 P가 향상되게 하는 것이 머신러닝이라 했다.
머신러닝은 전통적인 프로그래밍 기법과 달리 데이터만을 가지고 그 안에서 패턴을 찾아낸다는 학습이라는 개념을 사용하여, 사용자가 원하는 목적을 이루어내는 것으로, 이번 포스트에서는 전통적인 프로그래밍 기법과 머신러닝 기법을 두고, 어째서 머신러닝 기법이 그토록 강조되고 있는지에 대해 알아보도록 하겠다.
0. 예시 및 용어 정리
예시 1. 우편에 쓰인 손글씨 인식 모델
예시 2. 이메일 이용자가 스팸 처리한 스팸 메일과 정상 메일 분류 모델
0.1. 용어 정리
훈련 데이터(Training data): 시스템을 학습시키는 데 사용하는 데이터
훈련 사례(Training instance): 각각의 훈련 데이터셋
정확도(Accuracy): 모델의 성능을 측정하는 도구
0.2. 톰 미첼의 정의와 연결
어떤 작업 T: 어떤 목적을 위해 만들어진 알고리즘
경험 E: 훈련 데이터
성능 측정 P: 알고리즘 작성자가 정의한 모델 성능 평가 도구 - 정확도(Accuracy)
1. 전통적인 프로그래밍 기법
A. 문제 연구
훈련 데이터(training data)에 대하여 통계 분석 방법, 특정 집단에서 공통적으로 발생하는 패턴을 파악한다.
"예시 1. 손글씨"의 경우, 손 글씨를 텐서화하고, 그 텐서에서 각 글자마다 각 Row에서 수치들이 어디서 등장하는지. 수치가 흩어진 정도를 이용하여, 각 글자의 경향성을 찾아냈다.
"예시 2. 스팸 메일"의 경우, 사용자가 스팸 메일 처리한 메일들에서 "무료", "기회", "굉장한", "대단한"과 같은 특정 단어가 메일의 제목이나 본문에서 자주 등장하는 경향이 있다.
B. 규칙 작성
문제 연구에서 찾아낸 패턴을 바탕으로, 알고리즘을 만들어낸다.
"예시 1. 손글씨"의 경우, 앞에서 찾아낸 각 글자의 경향성을 기반으로, 그 패턴에 속하는 경우 해당 단어로, 속하지 않는 경우, 가장 가까운 패턴에 해당하는 단어를 찾아낸다.
"예시 2. 스팸 메일" 스팸 메일에서 주로 발생하는 단어들이 제목이나, 본문에서 등장하는 경우, 이를 스팸 메일로 분류한다.
C. 평가
훈련 데이터를 기반으로 만든 알고리즘에 시험 데이터(Test data)를 넣었을 때, 실제 분류와 어느정도 일치하는 분류가 나오는지 확인한다.
1.1. 전통적인 프로그래밍 기법의 한계점
연구자가 찾아낸 패턴은 표면적인 패턴일 가능성이 매우 높으므로, 반례가 존재할 가능성이 매우 높으며, 그 반례의 양도 많아, 이들을 하나하나 해결하다 보면 코드도 길어지고, 복잡해진다.
새로운 패턴을 갖는 데이터 셋이 추가 되면, 기존에 해결했던 반례가 되려 문제를 일으키거나, 새로운 반례가 생기는 등 알고리즘에 단순하게 반영하기가 매우 어렵다.
즉, 유지보수 난이도가 매우 높다(산출물 관리를 아주 아주 잘해야 한다.)
2. 머신러닝 접근 방법
머신러닝은 데이터를 기반으로 하여, 머신러닝 알고리즘 스스로 패턴을 찾아낸다.
물론, 알고리즘이 그 패턴을 제대로 찾아내게 하기 위해서는 연구자가 패턴이 잘 드러나는 방향으로 전처리를 해줘야 한다. (아무리 머신러닝 알고리즘의 성능이 뛰어나다 할지라도, 연구자가 그 알고리즘에 맞게 데이터를 세팅해주지 않는다면, 머신러닝 알고리즘의 성능을 전부 이끌어낼 수 없다.)
머신러닝 알고리즘 자체가 패턴을 찾아내기 때문에 사용자가 패턴을 찾아내는 과정이 따로 필요하지 않으며, 머신러닝 알고리즘은 데이터에서 연구자가 인식하지 못하는 패턴마저도 뽑아내므로, 정확도가 상당히 높다.
그러나, 머신러닝 알고리즘이 분류한 것에 대하여 연구자가 인식하는 것은 불가능에 가깝기 때문에 분류 과정에 대해 설명할 수 없다.
2.1. 새로운 패턴이 등장하는 경우
"예시 2. 스팸 메일"의 경우, "무료", "기회", "굉장한", "대단한"과 같은 특정 단어가 들어간 메일이 자동 스펨 처리되는 것을 눈치챈 발송자가 해당 단어를 사용하지 않고 "Free", "흔치않은", "오늘만"과 같은 기존 사전에 없던 단어를 이용할 수 있다.
또는, "F.R.E.E", "!기!회!", "!@!기!@! 오.늘!! !@!무.료!@! !@!회!@!" 같이 정규표현식이나, 비교적 단순한 알고리즘으로 잡기 어렵게 패턴에 노이즈를 주어 스펨 메일을 보낼 수도 있다.
머신러닝 알고리즘 기반으로 이를 해결하는 경우엔, 머신러닝 알고리즘 스스로 스펨 메일에 자주 등장하는 패턴을 찾아내 해당 패턴이 등장하는 경우를 스펨 처리하므로, 새로운 규칙을 추가하지 않고 동일한 모델로 패턴을 찾아낼 수 있다. (물론, 머신이 노이즈를 잘 인지할 수 있도록, 전처리를 해줘야 한다.)
2.2. 전통적인 방식에는 없는 알고리즘이 필요한 경우
"예시 1. 손글씨 인식"의 경우, 텐서의 Row 별로 수치가 변하는 위치와 편차를 이용해서 구해보겠다고 하였다. 그러나 해당 방법은 사람마다 손글씨의 차이가 매우 크기 때문에 제대로 작동하기 힘들 가능성이 높다.
머신러닝, 그중에서도 딥러닝의 CNN을 이용한다면, 이미지 데이터의 특징 자체를 찾아낼 수 있다.
즉, 전통적인 알고리즘은 특정 상황에 맞는 특정 알고리즘을 써야 하지만, 머신러닝을 사용하는 경우, 데이터 성격에 맞는 머신러닝 알고리즘을 사용하면, 수치로 표현하기 힘든 특징도 찾아낼 수 있다.
2.3. 데이터 마이닝(Data Mining)
연구자는 머신러닝을 통해 그동안 알지 못했던 새로운 사실을 알 수도 있다.
빅데이터를 사용하여 머신러닝 알고리즘을 훈련하는 경우, 기존에 노이즈인지 알고 제거했던 데이터에서 패턴이 존재한다는 것을 알 수 있다.
또는, 어떤 패턴이 분류에서 가장 결정적인 역할을 했는지 찾아낼 수도 있다.
2.4. 머신러닝의 장점 정리
전통적인 프로그래밍 기법보다 머신러닝 모델의 코드가 간단하며, 유지보수가 쉽다.
전통적인 알고리즘의 반례로 인한 알고리즘 복잡도 증가가 없다.
전통적인 방식으로 해결할 수 없는 문제도 해결할 수 있다.
데이터의 변화에 쉽게 적응할 수 있다.
복잡한 문제와 대량의 데이터를 통해 그간 알지 못했던 아이디어를 얻을 수 있다.
[참고 서적]
지금까지 전통적인 프로그래밍 기법과 머신러닝의 차이점에 대해 알아보았다. 다음 포스트에서는 지도 학습과 비지도 학습, 준지도 학습, 강화 학습에 대해 알아보도록 하겠다.
패턴을 거의 완벽하게 찾아내었으며, 정확도(Accuracy) 역시 0.000010789(e-05는 $10^{-5}$을 하라는 소리다.)로 거의 0에 근사하게 나왔다.
2. 정리
위 결과를 보면, 아무리 단순한 패턴이라 할지라도, 그 데이터 셋의 형태를 반영하지 못한다면, 정확히 그 결과를 찾아내지 못할 수 있다는 것을 알 수 있다.
인공지능은 흔히들 생각하는 빅데이터를 넣으면, 그 안에 숨어 있는 패턴이 자동으로 나오는 마법의 상자가 아니라, 연구자가 그 데이터에 대한 이해를 가지고 여러 시도를 해, 제대로 된 설계를 해야만 내가 원하는 제대로 된 패턴을 찾아낼 수 있는 도구다.
그러나, 실전에서는 지금처럼 우리가 이미 패턴을 알고 있는 경우는 없기 때문에 다양한 도구를 이용해서, 데이터를 파악하고, 적절한 하이퍼 파라미터를 찾아낸다.
넣을 수 있는 모든 하이퍼 파라미터를 다 넣어보는 "그리드 서치(Greed search)"나 랜덤 한 값을 넣어보고 지정한 횟수만큼 평가하는 "랜덤 서치(Random Search)", 순차적으로 값을 넣어보고, 더 좋은 해들의 조합에 대해서 찾아가는 "베이지안 옵티마이제이션(Bayesian Optimization)" 등 다양한 방법이 있다.
같은 알고리즘이라 할지라도, 데이터를 어떻게 전처리하느냐, 어떤 활성화 함수를 쓰느냐, 손실 함수를 무엇을 쓰느냐 등과 같은 다양한 요인으로 인해 다른 결과가 나올 수 있으므로, 경험을 많이 쌓아보자.
이전 포스트에서 만든 모델의 결과는 그리 나쁘진 않았으나, 패턴이 아주 단순함에도 쉽게 결과를 찾아내지 못했고, 학습에 자원 낭비도 많이 되었다.
왜 그럴까?
특성 스케일 조정
특성 스케일 조정을 보다 쉽게 말해보면, 표준화라고 할 수 있다.
이번에 학습한 대상은 변수(다른 정보에 대한 벡터 성분)가 1개밖에 없어서 그나마 나았으나, 만약, 키와 몸무게가 변수로 주어져 벡터의 원소로 들어갔다고 생각해보자.
키나 몸무게는 그 자리 수가 너무 큰 값이다 보니, 파라미터 역시 그 값의 변화가 지나치게 커지게 되고, 그로 인해 제대로 된 결과를 찾지 못할 수 있다.
또한 키와 몸무게는 그 단위마저도 크게 다르다 보니, 키에서 160이 몸무게에서의 160과 같다고 볼 수 있다. 그러나 모두가 알다시피 키 160은 대한민국 남녀 성인 키 평균에 못 미치는 값이며, 몸무게 160은 심각한 수준의 비만이다. 전혀 다른 값임에도 이를 같게 볼 위험이 있다는 것이다.
이러한 표준화가 미치는 영향은 손실 함수에서 보다 이해하기 쉽게 볼 수 있는데, 이로 인해 발생하는 문제가 바로 경사 하강법의 zigzag 문제다.
$w_1$과 $w_2$의 스케일 크기가 동일하다면(값의 범위가 동일), 손실 함수가 보다 쉽게 최적해에서 수렴할 수 있다.
$w_1$과 $w_2$의 스케일 크기가 많이 다르다면, 손실 함수는 쉽게 최적해에 수렴하지 못한다.
1. 특성 스케일 조정 방법
특성 스케일 조정 방법은 크게 2가지가 있다.
첫 번째는 특성 스케일 범위 조정이고, 두 번째는 표준 정규화를 하는 것이다.
A. 특성 스케일 범위 조정
특성 스케일 범위 조정은 말 그대로, 값의 범위를 조정하는 것이다.
바꿀 범위는 [0, 1]이다.
이 방법에는 최솟값과 최댓값이 사용되므로 "최소-최대 스케일 변환(min-max scaling)"이라고도 한다.
범위 축소에 흔히들 사용되는 해당 방법은, 가장 쉽게 표준화하는 방법이지만, 값이 지나치게 축소되어 존재하던 이상치가 사라져 버릴 수 있다.
특히나, 이상치가 존재한다면, 이상치보다 작은 값들을 지나치게 좁은 공간에 모아버리게 된다.
B. 표준 정규분포
표준 정규분포는 평균 = 0, 표준편차 = 1로 바꾸는 가장 대표적인 표준화 방법이다.
공식은 다음과 같다.
$$ x_{std} = \frac{x_i - \mu_x}{\sigma_x} $$
위 공식에서 $x_i$는 표준화 대상 array다.
표준 정규분포로 만들게 되면, 평균 = 0, 표준편차 = 1로 값이 축소되게 되지만, 여전히 이상치의 존재가 남아 있기 때문에 개인적으론 표준 정규분포로 만드는 것을 추천한다.
특성 스케일 조정에서 가장 중요한 것은, 조정의 기준이 되는 최솟값, 최댓값, 평균, 표준편차는 Train Dataset의 값이라는 것이다. 해당 방법 사용 시, Train Dataset을 기준으로 하지 않는다면, Test Dataset의 값이 Train Dataset과 같아져 버릴 수 있다.
2. 표준 정규분포를 이용해서 특성 스케일을 조정해보자.
# Import Module
import pandas as pd
import numpy as np
from tensorflow import keras
from tensorflow.keras.layers import Dense
# Dataset Setting
def f(x):
return x + 10
# Data set 생성
np.random.seed(1234) # 동일한 난수가 나오도록 Seed를 고정한다.
X_train = np.random.randint(0, 100, (100, 1))
X_test = np.random.randint(100, 200, (20, 1))
# Label 생성
y_train = f(X_train)
y_test = f(X_test)
# Model Setting
model = keras.Sequential()
model.add(Dense(16, activation='relu'))
model.add(Dense(1, activation='linear'))
# Compile: 학습 셋팅
opt = keras.optimizers.Adam(learning_rate=0.01)
model.compile(optimizer=opt, loss = 'mse')
지난 포스트에서 데이터 셋에 대해 간략히 설명해보았다. 이번 포스트부터 본격적으로 텐서플로우를 사용해서, 내가 찾아내고 싶은 알고리즘을 찾아내 보자.
학습 목표
분석가가 알고 있는 패턴으로 데이터를 생성하고, 그 패턴을 찾아내는 모델을 만들어보자.
Input이 1개, Output이 1개인 연속형 데이터에서 패턴을 찾아보자.
1. 데이터 셋 생성
패턴: $f(x) = x + 10$
# Module 설정
import pandas as pd
import numpy as np
from tensorflow import keras
from tensorflow.keras.layers import Dense
def f(x):
return x + 10
# Data set 생성
np.random.seed(1234) # 동일한 난수가 나오도록 Seed를 고정한다.
X_train = np.random.randint(0, 100, (100, 1))
X_test = np.random.randint(100, 200, (20, 1))
# Label 생성
y_train = f(X_train)
y_test = f(X_test)
데이터 셋 생성 코드의 함수 설명
np.random.seed(int): 난수(랜덤 한 데이터) 생성 시, 그 값은 생성할 때마다 바뀌게 된다. 데이터 셋이 바뀌게 되면, 일관된 결과를 얻기가 힘들어, 제대로 된 비교가 힘들어지므로, 난수를 생성하는 방식을 고정시킨다. 이를 시드 결정(Set seed)이라 하며, 숫자는 아무 숫자나 넣어도 상관없다.
np.random.randint(시작 int, 끝 int, shape): 시작 숫자(포함)부터 끝 숫자(미포함)까지 shape의 형태대로 array를 생성한다.
데이터 셋 생성 코드 설명
Train set은 0~100까지의 숫자를 랜덤으로 (100, 1)의 형태로 추출하였다.
Test set은 100~200까지의 숫자로 랜덤으로 (20, 1)의 형태로 추출했다. 여기서 값은 Train set과 절대 겹쳐선 안된다.
Label 데이터인 y_train과 y_test는 위에서 설정된 함수 f(x)에 의해 결정되었다.
기본적으로 Tensorflow에 Input 되고 Output 되는 데이터의 형태는 이렇다고 생각하자.
2. 모델 생성하기
tensorflow를 사용해 모델을 생성하는 경우, tensorflow가 아닌 keras를 사용하게 된다.
위에서 tensorflow의 기능을 가져올 때, 아래와 같은 코드로 가져왔다.
from tensorflow import keras
이는, tensorflow라는 프레임워크에서 keras라는 모듈을 가지고 온다는 의미이다.
keras는 추후 설명하게 될지도 모르지만, 모델 생성 및 학습에 있어 직관적으로 코드를 짤 수 있게 해 주므로, 쉽게 tensorflow를 사용할 수 있게 해 준다.
물론, keras와 tensorflow는 태생적으로 서로 다른 프레임워크이므로, 이 둘이 따로 에러를 일으켜, 에러 해결을 어렵게 한다는 단점이 있긴 하지만, 그걸 감안하고 쓸만한 가치가 있다.
model = keras.Sequential()
model.add(Dense(16, activation='relu'))
model.add(Dense(1, activation='linear'))
keras를 사용해서 모델을 만드는 방법은 크게 2가지가 있다.
하나는 위 같이 add를 이용해서 layer를 하나씩 추가해 가는 방법이 있고
model = keras.Sequential([
Dense(16, activation='relu'),
Dense(1, activation='linear')
])
이렇게 keras.Sequential([]) 안에 층(layer)을 직접 넣는 방법이 있다.
처음 방법처럼 add를 사용하는 방법은 API 사용 방법이고, 아래와 같이 층을 Sequential([])에 직접 넣는 방식은 Layer 인스턴스를 생성자에게 넘겨주는 방법이라 하는데, 전자인 API를 사용하는 방법을 개인적으로 추천한다.
그 이유는 다중-아웃풋 모델, 비순환 유향 그래프, 레이어 공유 모델 같이 복잡한 모델 정의 시, 매우 유리하기 때문으로, 이는 나중에 다루겠으나, 이 것이 Tensorflow의 장점이다.
모델 생성 코드 함수 설명
keras.Sequential(): 순차 모델이라 하며, 레이어를 선형으로 연결해 구성한다. 일반적으로 사용하는 모델로 하나의 텐서가 입력되고 출력되는 단일 입력, 단일 출력에 사용된다. 다중 입력, 다중 출력을 하는 경우나, 레이어를 공유하는 등의 경우엔 사용하지 않는다.
model.add(layer): layer를 model에 층으로 쌓는다. 즉, 위 모델은 2개의 층을 가진 모델이다.
Dense(노드 수, 활성화 함수): 완전 연결 계층으로, 전, 후 층을 완전히 연결해주는 Layer다. 가장 일반적으로 사용되는 Layer다.
모델 생성 코드 설명
해당 모델은 Input 되는 tensor도 1개 Output 되는 tensor도 1개이므로, Sequential()로 모델을 구성했다.
은닉층에는 일반적으로 ReLU 활성화 함수가 사용된다고 하니, ReLU를 넣었다.
출력층에는 출력 결과가 입력 값과 같은 노드 1개이므로, 노드 1개로 출력층을 만들었다.
일반적으로 Node의 수를 $2^n$으로 해야 한다고 하지만, 크게 상관없다는 말이 있으므로, 굳이 신경 쓰지 않아도 된다. 처음엔 자기가 넣고 싶은 값을 넣다가, 성능이 안 나온다 싶으면 바꿔보는 수준이니 크게 신경 쓰지 말자.
사용된 활성화 함수(activation)는 일반적으로 은닉층에 ReLU를 넣고, 연속형 데이터이므로 출력층에 Linear를 넣어보았다.
Tensorflow를 사용하는 사람 중 상당 수가 Github에서 다른 사람들이 어떤 목적을 위해 만들어놓은 모델을 그저 가져오거나, 남이 만든 모델에서 노드 크기를 수정하거나, 상황에 맞게 레이어를 바꿔보고, 내가 인공지능을 사용할 수 있다고 생각하는 경우가 많다.
마치 통계 분석을 할 때, "서로 다른 두 집단이 있고, 그 집단에 대한 평균을 비교해보고 싶다면, t-test를 사용해야한다."라 생각하듯, 머신러닝에 접근하면, 인공지능을 단순한 마법의 상자로 생각해버릴 수 있다.
흔히들 인공지능을 "내가 무언가를 넣으면, 원리는 잘 모르겠지만, 정답이 나오는 마법의 상자"라고 생각하는 경향이 있는데, 인공지능은 단순한 블랙박스가 아닌, 사용자가 의도를 가지고 설계한 것에 맞는 결과를 도출해주는 알고리즘이다.
그러므로, 제대로 인공지능을 다루고자 한다면, 인공지능이 할 수 있는 영역 안에서 내가 원하는 결과를 이끌어낼 수 있어야 한다.
1. 데이터셋 생성하기
분석가라면, 상황에 맞는 실험용 데이터 셋 만들기는 기본 중 기본이다.
데이터셋은 크게 훈련(Train), 시험(Tes), 검증(Validation) 데이터 셋으로 나뉜다.
훈련 데이터 셋(Train Dataset)
신경망 훈련 시 사용되는, 모델 학습 용 데이터 셋으로, 수능을 보기 위해 공부하는 문제집에 해당한다.
과도하게 훈련 데이터셋을 학습시키는 경우, 과적합(Overfitting) 현상이 발생하여, 훈련 데이터 셋은 잘 분류하나, 시험 데이터 셋이나 실제 데이터에는 적합하지 않을 수 있다.
훈련 데이터 셋은 모델의 기준이 된다!
시험 데이터 셋(Test Dataset)
모델의 성능을 최종적으로 평가하기 위한 데이터 셋으로 실제 데이터 셋이다. 고등학교의 최종 목적 시험인 수능에 해당한다.
훈련 데이터 셋과 시험 데이터 셋은 중첩되지 않는 것이 좋다.
예를 들어, 데이터를 날짜별로 뽑아낼 수 있다면, 시험 데이터 셋은 다른 날짜의 데이터 셋을 사용하는 것이 좋다.
시험 데이터 셋과 모델이 예측한 결과를 비교해 정확도(Accuarcy), 정밀도(Precision), 재현율(Recall), F1 점수를 계산하여, 모델이 얼마나 잘 만들어졌는지를 확인해볼 수 있다.
검증 데이터 셋(Validation Dataset)
학습을 할 때, 학습이 얼마나 잘 돼는지를 평가하는 수단으로, 공부가 잘되었는지를 평가하는 모의고사에 해당 한다.
Development Dataset이라고도 불린다.
검증 데이터 셋은 학습 시, 학습된 모델의 성능 평가에 사용되며, 그 결과가 파라미터에 반영된다.
즉, 검증 데이터 셋의 목적은 학습 데이터에 의해 학습된 파라미터 중, 실제 데이터에도 잘 맞을 수 있도록 최적의 파라미터를 찾아낼 수 있도록, 파라미터를 튜닝하기 위해 존재한다고 할 수 있다.
검증 데이더 셋은 학습 데이터 셋에서 분리되며, 때에 따라 검증 데이터 셋을 만들지 않고, 전부 훈련 데이터에 사용할 수도 있다.
물론, 검증 데이터 셋을 사용하는 경우 성능이 더 좋다고 한다.
학습 데이터 셋과 검증 데이터 셋은 그 내용이 중첩되지 말아야 한다. 만약 중첩되는 경우, 이 현상을 leakage라고 한다(학습 데이터 셋과 검증 데이터셋에 교집합 존재).
2. 과대 적합 피하기
모델이 훈련(Train) 데이터 셋에 대해선 분류가 잘되었으나, 시험(Test) 데이터 셋에 대해 구분을 지나치게 못한다면, 과대 적합일 가능성이 있다.
이는, 훈련 데이터 셋에 모델이 지나치게 맞춰져, 새로운 데이터에 대해 일반화가 되지 못한다는 소리로, 모델이 지나치게 훈련 데이터 셋에만 맞춰진, 모델의 분산이 큰 상태라고 할 수 있다.
이를 해결하는 방법은 다음과 같다.
훈련 데이터를 늘린다.
Dropout과 같은 규제를 실시하여 복잡도를 줄인다.
데이터의 차원을 줄인다.
모델을 보다 간략화시켜 파라미터의 수를 줄인다.
위 내용은 꽤 심도 깊은 영역이므로, 이는 추후 자세히 다루도록 하겠다.
3. 데이터 셋의 비율
일반적으로 훈련(Train) 데이터셋과 시험(Test) 데이터셋의 비율은 7:3으로 나누며, 훈련 데이터의 안에서도 학습 도중 모델을 평가할 검증(Validation) 셋을 학습 데이터 셋에서 떼어내기도 한다. 이 경우, 일반적으로 훈련 데이터셋과 검증 데이터셋의 비율을 8:2로 한다고 한다.
그러나, 위 비율은 절대로 절대적인 것이 아니며, 총데이터의 양과 훈련 데이터 셋과 시험 데이터 셋의 형태 차이 등에 따라 그 비율은 위와 크게 다를 수 있다.
학습 데이터는 내가 원하는 특징이 잘 들어가 있는 깔끔한 데이터일 수 있으나, 실제 이 모델을 이용해 분류될 대상인 시험 데이터 셋엔 상당한 노이즈가 들어가 있을 수 있다.
예를 들어, 우리가 학습에 사용한 데이터는 증명사진이지만, 실제 사람들이 이 인공지능에 사용할 사진은 온갖 바탕과 포토샵 등 우리가 학습 시 고려하지 않은 노이즈가 들어가 있을 수 있다.
이를 방지하기 위해, 때에 따라 시험 데이터 셋이 없이 모두 학습 데이터로 사용하거나, 학습 데이터에 의도적으로 노이즈를 부여하기도 한다.
데이터 셋의 양이 매우 적다면(예를 들어, 데이터의 수가 1만에도 못 미친다면), 위 비율대로 나눠도 상관없으나, 데이터의 양이 매우 많다면(데이터의 수가 100만 이상이라면), 테스트 데이터 셋이나 검증 데이터 셋의 비율을 0.1~1%로 잡기도 한다.
즉, Valid Dataset과 Test Datset의 목적은 생소한 데이터를 이용해 모델을 일반화시키기 위한 것이므로, 그 비중이 그리 크지 않아도 된다(물론 Test Dataset은 최종 평가지만, 간접적으로 영향을 미치므로).
다음 포스트에서는 실제로 데이터 셋을 생성하고, 이를 이용해서 학습을 해보고, 그 성능을 평가해보자!
이전 퍼셉트론에서 학습했던 내용을 보면, 입력층(Input layer)에서 전달된 정보(값)는 가중치를 받아 값이 변하고, 가중치를 받아 합산된 값($w_1x_1 + x_2x_2$)이 편향($b=-\theta$)보다 크거나 작다에 의해 정보가 전달(0 또는 1) 된다고 하였다.
이번 포스트에서는 이 정보가 전달되는지, 즉 정보가 활성화 되는지 혹은 정보가 활성화된다면, 어떻게 활성화되어 출력 값을 생성해내는지를 결정하는 활성화 함수(Activation Function)에 대해 학습해보겠다.
1. 퍼셉트론에서 활성화 함수가 적용된 방법
우리가 퍼셉트론에서 사용했던 "임계값을 넘으면 정보가 전달되고, 임계값을 넘지 않으면 정보가 전달되지 않는다. "는 말을 활성화 함수에 초점을 맞춰서 보다 단순화된 공식으로 만들어보자.
위 코드에서 새롭게 ster_function()이라는 함수가 생겼는데, 이는 계단 함수로, 앞서 우리가 if문을 이용해서 합산된 결과가 0보다 클 때, 1을 반환하고, 0보다 작거나 같으면, 0을 반환하던 부분이다.
이를 활성화 함수(Activation Function)라 하는데, 활성화 함수를 통해 합산된 값을 출력할지, 출력한다면 어떻게 출력할지를 결정한다.
계단 함수는 이후 활성화 함수를 자세히 다룰 때, 다시 이야기하도록 하겠다.
위 코드를 보면, 오로지 가중치만 다른 퍼셉트론을 겹쳐서 사용했는데, XOR 게이트 문제를 해결한 것을 알 수 있다.
이렇게 2개 이상의 퍼셉트론을 쌓는 것을 다층 퍼셉트론이라고 한다.
3. 논리를 비약시켜보자.
위에서 우리는 2개 이상의 층(Layer)을 쌓아, 우리가 원하는 문제를 해결하였다.
이 곳에 사용된 층들은 오로지 가중치만 다르며, 이 가중치가 갖는 의미도 꽤 다르다.
$w_1, w_2$ 같은 가중치는, 각 입력 신호에 부여되는 영향력(중요도)을 조절한다.
$\theta$는 임계점이라 하였는데, 좌변으로 이항 시켜, $b$로 만들어주었고, 이는 편향(bias)라고 한다. 편향은 뉴런이 얼마나 쉽게 활성화되느냐에 영향을 미치며, $b$의 크기에 따라 활성화 함수가 작동하는지, 마는지가 결정된다.
지금까지 우리가 학습한 내용을 단순화시켜보면, 층을 많이 쌓고, 가중치를 맞게 설정해주면, 내가 원하는 결과를 얻을 수 있다는 것이다.
층을 많이 쌓는다는 것이 바로 우리가 아는 딥러닝(Deep Learning)의 딥(Deep)을 의미하며, 층이 많이 쌓인 신경망은 각 노드에서 다음 노드로 이동하는 신호에 부여하는 가중치를 다르게만 한다면, 구체적으로 함수 식을 모른다 할지라도 우리가 원하는 결과를 알 수 있다는 것이다.
그런데, 만약 그 가중치를 컴퓨터가 알아서 찾을 수 있다면 어떨까?
여기서 조금 어려워질지도 모르겠는데, 기계가 어떠한 방법(손실 함수)을 이용해서 가장 적합한 가중치를 찾아낸다면, 우리가 스스로 공부를 해서 어떤 결과를 도출하는 것처럼 컴퓨터도 적합한 가중치를 찾기 위한 활동, 즉! 학습과 동일하게 보이는 행동을 통해 데이터만 가지고 원하는 결과를 도출할 수 있다는 것이다.
여기서 "기계가 학습한다(Machine Learning)"이라는 말이 나오게 되는 것이며, 학습을 하되(가중치를 찾는 과정) 그것을 다층 레이어를 통해 찾아내는 것을 딥러닝(Deep Learning)이라 하게 되는 것이다.
이번 포스트에선 퍼셉트론을 이용해 머신러닝이라는 단어와 딥러닝이라는 단어가 어떻게 만들어졌는지를 학습해보았다. 다음 포스트에서는 짧게 짚고 넘어갔던 활성화 함수에 대해 학습해보자.