복잡도 파라미터 튜닝
복잡도 파라미터는 복잡도에 영향을 주는 파라미터로, 이 값에 따라 과적합 정도가 결정되므로 매우 신중하게 튜닝을 해야한다. 만약 너무 복잡하게 설정하면 과적합 가능성이 높아질 것이고 너무 단순하게 설정하면 과소적합 가능성이 높아질 것이다.

(Tip)

  • 휴리스틱하게 학습되는 모든 모델(학습할때 마다 그 결과가 조금씩 다를 수 있는 모델)
  • 정규화 회귀 모델에서 alpha값은 크면 클수록 패널티가 더 부과되기 때문에 모델이 단순해진다.
  • 의사결정나무에서 max_depth를 크게 둘수록 나무가 더 깊어질수 있기 때문에 복잡해짐, min_samples_leaf는 잎노드(끝마디)로 판단하기 위한 최소 기준 샘플 수로 그 값이 크면 클수록 나무가 분지가 안되는 효과가 있기 때문에 복잡해지지 않는다.
  • SVM에서 C는 오차 패널티 계수(오차를 어느정도 허용 할지, 즉 하드마진/소프트마진 결정),gamma는 시그모이드 커널, rbf 커널에 포함된 하이퍼 파라미터, degree는 polynomial 커널에 포함된 하이퍼 파라미터이다.
  • 로지스틱에서 C는 패널티 계수이다.
  • 신경망에서 hidden_layer_sizes 은닉층의 수를 조절하는 매개변수로 은닉층이 복잡해지면 노드가 많아진다는 소리이고 노드가 많아지면 연결된 엣지(선)가 많다는 소리이고 엣지가 많다는 것은 거기에 부여된 가중치가 많다는 소리이기 때문에 직접적으로 복잡도에 강한 영향을 준다.
  • SVR에서 epsilon은 마진을 설정하기 위한 허용 오차 범위이다.
  • Tree Ensemble(XGboost, LightGBM, RandomForest) 트리 계열 모델의 max_depth는 의사결정나무와 동일하고 여러 개의 모델을 쓰다 보니 max_depth를 보통 4정도로 낮게 잡는것이 일반적이다.

  • 학습 시 우연성이 개입되는 모델(학습할때 마다 그 결과가 조금씩 다를 수 있는 모델)->회귀모델, 신경망

경사하강법 등의 방법으로 학습되는 모델(예: 회귀모델, 신경망)은 똑같은 데이터로 똑같은 모델을 학습할때 마다 다른 결과를 낼수 있다. 즉, 초기값에 의한 영향이 매우 크다.
따라서 복잡도 파라미터 변화에 따른 성능 변화의 패턴을 제대로 보기 위해서 seed를 고정한 뒤 튜닝을 하는것이 바람직하다. 단, seed는 어떤 seed가 좋은지 정확히 알 수 없기 때문에 seed를 고정하되 하이퍼 파라미터 그리드를 설계할 때 seed도 하나의 축으로 탐색해서 좋은 seed를 찾아내서 고정하는 것이 좋다.

  • seed가 고정되어 있거나, 학습 시 우연성이 개입되지 않는 모델

위의 학습 시 우연성이 개입되는 모델보다 복잡도 파라미터에 따른 성능 변화 패턴 확인이 당연히 상대적으로 쉽다.


★★복잡도 파라미터를 튜닝 시 복잡도 파라미터가 둘 이상인 경우에는 서로 영향을 주기 때문에 반드시 두 파라미터를 같이 조정해야 하고 분석가가 처음부터 파라미터를 특정 범위를 설정하여 테스트를 할 경우 파라미터의 어느 범위가 좋은지 알수도 없고 불필요한 많은 범위를 테스트하기 때문에 시간의 문제와 효율적이지 못하다. 따라서 몇 가지 파라미터 값을 테스트한 후 범위를 설정하는 것이 바람직하다.


## 복잡도 파라미터 튜닝 실습 ##

import os
os.chdir(r"C:\Users\82102\Desktop\데이터 전처리\지도학습 주요모델 및 개념\데이터")

import numpy as np
import pandas as pd
df = pd.read_csv("Sonar_Mines_Rocks.csv")
df.info()
X = df.drop('Y', axis=1)
Y = df['Y']
# 학습 데이터와 평가 데이터 분리
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(X, Y)
x_train.shape 
# 샘플 156개, 특징 60개 -> 샘플에 비해 특징 개수가 많다
y_train.value_counts()
# 라벨 인코딩 진행
y_train.replace({"M":-1, "R":1}, inplace=True)
y_test.replace({"M":-1, "R":1}, inplace=True)
from sklearn.metrics import f1_score
from sklearn.model_selection import ParameterGrid

1. Logistic Regression(복잡도 파라미터 한개, 모델이 단순하고, 우연성이 어느정도 있는 모델)

from sklearn.linear_model import LogisticRegression as LR
# 복잡도 하이퍼 파라미터 튜닝
def LR_model_test(C):
    model = LR(C=C, max_iter = 100000, random_state=10).fit(x_train, y_train) # 단순한(가벼운) 모델이므로 max_iter를 크게 잡음
    y_pred = model.predict(x_test)
    return f1_score(y_test, y_pred)
print("C = 0.1:\t{}".format(LR_model_test(C=0.1)))
print("C = 1:\t{}".format(LR_model_test(C=1)))
print("C = 5:\t{}".format(LR_model_test(C=5)))
# C=1일 때 가장 좋기 때문에 0.1이하, 5이상 일때는 굳이 탐색할 필요가 없다.
# 범위가 여전히 넓기 때문에 몇 가지 더 탐색
print("C = 0.5:\t{}".format(LR_model_test(C=0.5)))
print("C = 2:\t{}".format(LR_model_test(C=2)))
# C가 1일때 0.766이고 C가 2일때 0.74이니 C가 2보다 큰 것은 바람직하지 않다.
# 튜닝 범위: 0.5 < C < 1
# 파라미터 그리드 설정
LR_parameter_grid = ParameterGrid({"C":np.linspace(0.5, 1, 50),
                                  "max_iter":[100000],
                                  "random_state":[1004, 10, 1000]})

# 파라미터 튜닝 수행(분류문제:f1_score->최대값 찾기->best_score를 작은 값 설정)
best_score = -1
for parameter in LR_parameter_grid:
    model = LR(**parameter).fit(x_train, y_train)
    y_pred = model.predict(x_test)
    score = f1_score(y_test, y_pred)
    
    if score > best_score:
        best_score = score
        best_parameter = parameter
        
print(best_parameter, best_score)
# 결론: 로지스틱을 튜닝할때는 random_state, C를 같이 고려하고 
# 로지스틱은 단순한 모델이므로 max_iter를 작게 잡을 필요가 없기 때문에 큰 값으로 설정


2. Decision Tree(복잡도 파라미터 두개, 모델이 단순하고, 우연성이 거의 없는 모델)

from sklearn.tree import DecisionTreeClassifier as DTC
# 복잡도 하이퍼 파라미터 튜닝
def DTC_model_test(max_depth, min_samples_leaf):
    model = DTC(max_depth=max_depth, min_samples_leaf=min_samples_leaf).fit(x_train, y_train)
    y_pred = model.predict(x_test)
    return f1_score(y_test, y_pred)
for max_depth in [3,6,9]:
    for min_samples_leaf in [1,2,3]:
        score = DTC_model_test(max_depth=max_depth, min_samples_leaf=min_samples_leaf)
        print("{}-{}:{}".format(max_depth, min_samples_leaf, score))

# "max_depth"가 3일 때 다른 수치에 비해 좋게 안나왔기 때문에 3을 제외하고 6부터 14까지 볼 것이고
# "min_samples_leaf"가 1일 때 다른 수치에 비해 좋게 안나왔기 때문에 2부터 4까지 볼 것이다.
# 파라미터 그리드 설정
DTC_parameter_grid = ParameterGrid({"max_depth":np.arange(6,15),
                                   "min_samples_leaf":np.arange(2,5)})

# 파라미터 튜닝 수행(분류문제:f1_score->최대값 찾기->best_score를 작은 값 설정)
best_score = -1
for parameter in DTC_parameter_grid:
    model = DTC(**parameter).fit(x_train, y_train)
    y_pred = model.predict(x_test)
    score = f1_score(y_test, y_pred)
    
    if score > best_score:
        best_score = score
        best_parameter = parameter
        
print(best_parameter, best_score)



3. 신경망(복잡도 파라미터가 하나이면서, 모델이 복잡하고, 우연성이 있는 모델)

from sklearn.neural_network import MLPClassifier as MLP
# 복잡도 하이퍼 파라미터 튜닝
def MLP_model_test(hidden_layer_sizes):
    model = MLP(hidden_layer_sizes=hidden_layer_sizes, random_state=12).fit(x_train, y_train) # max_iter는 default값으로 일부로 작게 잡음
    y_pred = model.predict(x_test)
    return f1_score(y_test, y_pred)
for hidden_layer_sizes in [(5, ), (10, ), (3, 3), (5, 5), (10, 10)]:
    score = MLP_model_test(hidden_layer_sizes=hidden_layer_sizes)
    print(hidden_layer_sizes, score)

# (5,): 첫 번째 은닉층이 5개 노드로 구성된 층의 구조
# (3,3): 첫 번째 은닉층이 3개 노드, 2번째 은닉층이 3개 노드로 구성된 층의 구조
# max_iter warning 발생 
# (5, 5)에서는 f1-score가 0이 나옴 => 초기값 영향으로 보여짐 (근거: 더 단순한 모델과 복잡한 모델에서는 성능이 나왔으므로)
# (10, )와 (10, 10)에서 best_score가 나옴 => 더 복잡한 모델이 필요할지 판단이 필요
# 파라미터 그리드 설정
MLP_parameter_grid = ParameterGrid({"random_state":[41,102,15],
                                   "hidden_layer_sizes":[(5, 5), (10, 10), (5, 5, 5), (10, 10, 10)],
                                   "max_iter":[200, 2000, 20000]})

# 파라미터 튜닝 수행(분류문제:f1_score->최대값 찾기->best_score를 작은 값 설정)
best_score = -1
for parameter in MLP_parameter_grid:
    model = MLP(**parameter).fit(x_train, y_train)
    y_pred = model.predict(x_test)
    score = f1_score(y_test, y_pred)
    
    if score > best_score:
        best_score = score
        best_parameter = parameter

print(best_parameter, best_score)
# 결론: 신경망을 튜닝할때는 random_state, hidden_layer_sizes, max_iter를 같이 고려
# 결론: 신경망, SVR은 하이퍼파라미터를 튜닝했을 때 정말 좋은 성능을 보여주는 모델이다.

오늘은 복잡도에 영향을 주는 하이퍼 파라미터 튜닝하는 방법을 알아보았다.
복잡도 하이퍼 파라미터 튜닝 후에 복잡도와 관련된 최적의 하이퍼 파라미터를 대략적으로 구한 뒤 그 값으로 하이퍼 파라미터 그리드를 설정 한 후 최종적인 하이퍼 파라미터 튜닝을 수행해서 가장 좋은 score 값과 하이퍼 파라미터를 찾는다.

이번에는 데이터 크기 즉, 샘플 개수와 특징 개수에 따른 적절한 모델을 알아볼려고 한다.

 

샘플 개수와 특징 개수에 따른 적절한 모델

샘플 개수를 기본적으로 10,000개로 보고 샘플 개수가 10,000개 미만이면 샘플이 적다고 볼것이고 샘플 개수가 10,000개 이상이면 샘플이 많다고 볼것이다.

특징 개수는 샘플 개수를 30으로 나누었을 때 그 값보다 많으면 특징이 많다고 볼 것이고 그렇지 않으면 적다고 볼것이다.

(단, 10000개는 임의로 설정한 값이지 무조건적이지 않다->특징 개수와 샘플 개수를 같이 고려해야한다)

위의 그림은 일반적으로 경향이 이렇다고 말하며 항상 그렇지 않다는 것에 주의한다. 다시 말해 매우 단순한 모델 및 파라미터에서 라쏘/릿지, 나이브베이즈가 아닌 SVM이 좋을 수도 있기 때문에 참고만 한다.

->특징 개수와 샘플 개수에 따라서 모델의 복잡도를 어느정도로 설정할 것인지 감을 가지고 가는 것이 이번 시간의 핵심이다.

 

# 코드 실습 #

이 문제는 예측 문제이다.

import os
os.chdir(r"C:\Users\82102\Desktop\데이터 전처리\지도학습 주요모델 및 개념\데이터")

import numpy as np
import pandas as pd
  • 특징 개수가 매우 적은 경우
## 데이터 크기에 따른 모델 선택: 특징 개수가 매우 적은 경우
df = pd.read_csv("Combined_Cycle_Power_Plant.csv")
df.shape
# 샘플: 9568개 / 특징: 5개 -> 샘플은 적당히 많은편, 특징은 샘플 대비 매우 적음
X = df.drop('EP', axis=1)
Y = df['EP']

SVR이나 신경망 같은 경우는 파라미터에 영향을 많이 받는 모델이기 때문에 공정한 비교를 위해 전부 default 값을 사용하기로 결정했다. 우연성에 영향을 받는 모델인 신경망, 의사결정나무, 랜덤포레스트의 random_state는 다 같은 값으로 고정시킬 것이다. 그리고 각 모델을 학습시키고 평가하는 것을 for문을 돌게 하기 위해서 model_list를 정의하고 각 모델의 결과를 손쉽게 확인하기 위해서  model_name_list를 만든다.

from sklearn.svm import SVR
from sklearn.neural_network import MLPRegressor as MLP
from sklearn.linear_model import LinearRegression as LR
from sklearn.tree import DecisionTreeRegressor as DTR
from sklearn.ensemble import RandomForestRegressor as RFR
from sklearn.neighbors import KNeighborsRegressor as KNR

SVR_model = SVR()
MLP_model = MLP(random_state=1004)
LR_model = LR()
DTR_model = DTR(random_state=1004)
RFR_model = RFR(random_state=1004)
KNR_model = KNR()

model_list = [SVR_model, MLP_model, LR_model, DTR_model, RFR_model, KNR_model]
model_name_list = ['SVR', 'MLP', 'LR', 'DTR', 'RFR', 'KNR']

train data와 test data를 따로 나누지 않은 이유는 k겹 교차검증을 하기 위함이다.(좀 더 공정한 비교를 하기 위해 학습 데이터와 평가 데이터로 나눠져서 성능 차이가 나는 것을 방지할려고 k겹 교차검증 사용) -> 특별한 전처리가 필요없는 경우 쓸 수 있는 cross_val_score 함수를 사용한다.

cross_val_score는 학습되지 않은 모델을 입력으로 받고, X와 Y를 통으로 입력으로 받고, cv(폴드의 개수)를 입력으로 받고, scoring값을 입력으로 받는다. scoring에서 'neg_mean_absolute_error'를 입력한 이유는 사이킷런에서 평가를 할 때 score는 크면 클수록 좋게 설계가 되어있다. 그런데 MAE(예측 평가지표)는 작으면 작을수록 좋은 score이기 때문에 그런 충돌을 방지하기 위해서 앞에 'negative' 즉, -를 붙혀 놓은것 뿐이지 특별한 의미가 있지는 않다.

그리고 각 평가에 대한 점수를 평균을 내고 그 점수가 -가 붙어 있기 때문에 앞에 -를 붙혀서 각 모델의 score를 낸다. 

 

참고링크:https://www.inflearn.com/questions/35479/scoring%EC%97%90-%EB%8C%80%ED%95%9C-%EC%A7%88%EB%AC%B8

 

# 모델별 k 겹 교차검증 기반(k=5)의 MAE값 계산
from sklearn.model_selection import cross_val_score
for (model, model_name) in zip(model_list, model_name_list):
    score = -cross_val_score(model, X, Y, cv=5, scoring='neg_mean_absolute_error').mean()
    print(model_name, score)

->인사이트: 특징이 적으면 복잡한 모델을 쓰는게 특별한 의미가 없다. 하지만 SVR과 MLP에 대한 파라미터 튜닝을 잘 한다면 좋은 성능을 얻었을 가능성이 크다. 

 

 

  • 샘플이 매우 적고, 특징이 상대적으로 많은 경우
import os
os.chdir(r"C:\Users\82102\Desktop\데이터 전처리\지도학습 주요모델 및 개념\데이터")

import numpy as np
import pandas as pd
## 샘플이 매우 적고, 특징이 상대적으로 많은 경우
df = pd.read_csv("baseball.csv")
df.shape
# 샘플: 337개 / 특징: 17개
X = df.drop('Salary', axis=1)
Y = df['Salary']
from sklearn.svm import SVR
from sklearn.neural_network import MLPRegressor as MLP
from sklearn.linear_model import LinearRegression as LR
from sklearn.tree import DecisionTreeRegressor as DTR
from sklearn.ensemble import RandomForestRegressor as RFR
from sklearn.neighbors import KNeighborsRegressor as KNR

SVR_model = SVR()
MLP_model = MLP(random_state=1004)
LR_model = LR()
DTR_model = DTR(random_state=1004)
RFR_model = RFR(random_state=1004)
KNR_model = KNR()

model_list = [SVR_model, MLP_model, LR_model, DTR_model, RFR_model, KNR_model]
model_name_list = ['SVR', 'MLP', 'LR', 'DTR', 'RFR', 'KNR']

SVR과 MLP 같은 경우 기본적으로 많은 데이터, 특징이 필요하기 때문에 성능이 안좋은 것을 확인했고,

MLP의 경우 convergence warning이 떴는데 이는 학습 데이터가 부족하다 보니 제대로 수렴하지 못하고 끝난 상황이라고 볼 수 있다.(특징이 많다 보니까 거기에 추정할 가중치가 많고 가지고 있는 데이터로는 수렴하지 못하고 끝난 상황)

# 모델별 k겹 교차검증 기반(k=5)의 MAE값 계산
# SVR의 성능이 최악
# MLP에서 max_iter 이슈 발생
from sklearn.model_selection import cross_val_score
for (model, model_name) in zip(model_list, model_name_list):
    score = -cross_val_score(model, X, Y, cv=5, scoring='neg_mean_absolute_error').mean()
    print(model_name, score)

 

그리드 서치(하이퍼 파라미터와 하이퍼 파라미터 튜닝)

모델 및 파라미터 선정을 할 때 어떠한 데이터에 대해서도 우수한 모델과 그 하이퍼 파라미터는 절대 존재하지 않고 또한, 분석적인 방법으로 좋은 모델과 하이퍼 파라미터를 선정하는 것도 불가능하다.

모델 및 파라미터 선정 문제

머신러닝에서 하이퍼 파라미터란 사용자의 입력값, 혹은 설정 가능한 옵션이라고 볼 수 있다. 모든 데이터와 문제에 대해 가장 좋은 하이퍼 파라미터 값이 있으면 좋겠지만 데이터에 따라 좋은 하이퍼 파라미터라는 것이 다르다. 그래서 데이터마다 좋은 입력값과 모델을 설정해주는 작업이 필요한데, 이를 하이퍼 파라미터 튜닝이라고 한다. 예를 들어, k-최근접 이웃에서 k를 3으로도 해보고, 5로도 해보고, 10으로도 해 본 다음 / 서포트 벡터 머신에서 kernel을 'rbf'로도 해보고, 'linear'로도 해 본 다음 그 가운데 가장 좋은 모델과 그 모델의 하이퍼 파라미터를 찾는 과정이다. 하이퍼 파라미터 튜닝을 노가다라고  표현한 이유는 해보기 전까지 어떤 모델, 어떤 하이퍼 파라미터가 좋을지 알 수 없기 때문이다.

 

  • 그리드 서치 개요

하이퍼 파라미터 그리드는 한 모델의 하이퍼 파라미터 조합을 나타내며, 그리드 서치란 하이퍼 파라미터 그리드에 속한 모든 파라미터 조합을 비교 평가하는 방법을 의미한다.

예시: k-최근접 이웃의 파라미터 그리드

->총 여섯 개의 하이퍼 파라미터 조합에 대한 성능을 평가하여, 그 중 가장 우수한 하이퍼 파라미터를 선택한다.

 

  • 그리드 서치 코드 구현: GridSearchCV

sklearn을 활용하여 그리드 서치를 구현하려면 사전 형태로 하이퍼 파라미터 그리드를 정의해야 한다.

-Key: 하이퍼 파라미터명(str)

-Value: 해당 파라미터의 범위(list)

★sklearn.model_selection.GridSearchCV

▶주요 입력

- estimator: 모델(sklearn 인스턴스)

- param_grid: 파라미터 그리드(사전)

- cv: k겹 교차 검증에서의 k(2 이상의 자연수)

- scoring_func: 평가 함수(sklearn 평가 함수: accuray, f1-score, mae 등)

▶GridSearchCV 인스턴스(GSCV)의 주요 method 및 attribute

- GSCV = GridSearchCV(estimator, param_grid, cv, scoring_func): 인스턴스화

- GSCV.fit(X, Y): 특징 벡터 X와 라벨 Y에 대해 param_grid에 속한 파라미터를 갖는 모델을 k-겹 교차검증 방식으로 평가하여, 그 중 가장 우수한 파라미터를 찾음

-GSCV.get_params(): 가장 우수한 파라미터를 반환

▶GridSearchCV는 사용이 편하다는 장점이 있지만, k-겹 교차검증 방식을 사용하기에 속도가 느리고

모델 성능 향상을 위한 전처리 기법을 적용할 수 없다는 단점이 있다. 그래서 사용하는 것이 ParameterGrid이다.

 

 

 

★sklearn.model_selection.ParameterGrid

▶param_grid(사전 형태의 하이퍼 파라미터 그리드)를 입력 받아, 가능한 모든 파라미터 조합을 사전 형태의 요소로 하는 generator를 반환하는 함수이다.

▶GridSearchCV에 비해 사용이 어렵다는 단점이 있지만,

모델 성능 향상을 위한 전처리 기법을 적용하는데 문제가 없어서 실무에서 훨씬 자주 사용된다.

 

 

ParameterGrid 사용을 위해 알아야 하는 문법이 두 가지가 있다.

첫째, 함수의 입력으로 사전 자료형을 사용하는 경우에는 **를 사전 앞에 붙여야 함

이를 활용하면, ParameterGrid 인스턴스를 순회하는 사전 자료형인 변수(파라미터)를 모델의 입력으로 넣을 수 있다.

둘째, ParameterGrid 인스턴스를 순회하면서 성능이 가장 우수한 값을 찾으려면 최대값(최소값)을 찾는 알고리즘을 알아야 한다.

->분류 모델의 경우 모델 평가지표가 클수록 좋은 모델이기 때문에 최대값을 찾는 것이 목적이고, 예측 모델의 경우 모델 평가지표가 작을수록 좋은 모델이기 때문에 최소값을 찾는 것이 목적이다.

->내장 함수인 max 함수나 min 함수를 사용해도 되지만, 평가해야 하는 하이퍼 파라미터 개수가 많으면 불필요한 메모리 낭비로 이어질 수 있으며, 더욱이 모델도 같이 추가되야 하므로 메로리 에러로 이어지기 쉽기 때문에 최대값(최소값)을 찾는 알고리즘을 알아야 한다.

위 그림을 보면 최대값을 찾기 위해서는 Max_value란 값을 매우 작은 값으로 초기화를 시켜준 뒤 비교를 진행하고 최소값을 찾기 위해서는 Min_value란 값을 매우 큰 값으로 초기화를 시켜준 뒤 비교를 진행한다.

(만약, accuracy의 최대값을 찾는다면 Max_value를 -1로 초기화 하더라도 문제가 없다. 왜냐하면 accuracy는 아무리 작아도 0이기 때문이다.)

 

위에서 설명한 과정을 코드를 통해 구현을 해볼것이다.

## ParameterGrid
from sklearn.model_selection import ParameterGrid

grid = {'n_neighbors': [3,5,7],
       'metric': ['Manhattan','Euclidean']}

list(ParameterGrid(grid))

## 함수의 입력으로 사전을 입력받기: **의 사용
def f(a,b):
    return a + b

input_f = {"a":1, "b":2}
f(**input_f)

## 최대값 찾기 알고리즘 예시
L = [10, 20, 30, 10, 20]
max_value = -99999 # 매우 작은 값으로 설정
for value in L:
    if max_value < value:
        max_value = value # 탐색한 값이 현재 최대값보다 크면 업데이트

print(max_value)

## 최소값 찾기 알고리즘 예시
L = [10, 20, 30, 10, 20]
min_value = 99999 # 매우 큰 값으로 설정
for value in L:
    if min_value > value:
        min_value = value # 탐색한 값이 현재 최소값보다 작으면 업데이트

print(min_value)

## 그리드 서치 실습(하이퍼 파라미터 튜닝 실습) ##

*사용 데이터: iris dataset

*사용 모델: (1) k-최근접 이웃

  • n_neighbors(3, 5, 7)
  • metric(euclidean, manhattan)

*사용 모델: (2) 서포트 벡터 머신

  • kernel: rbf, linear
  • C: 0.1, 1, 10

*평가 척도:f1 score

# iris 데이터 불러오기
from sklearn.datasets import load_iris
X = load_iris()['data'] # feature
Y = load_iris()['target'] # label
# 학습 데이터와 평가 데이터 분할
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(X, Y, random_state=1004)

모델은 KNN과 SVM을 사용한다

# 모델, ParameterGrid 불러오기
from sklearn.model_selection import ParameterGrid
from sklearn.neighbors import KNeighborsClassifier as KNN
from sklearn.svm import SVC

모델별 파라미터 그리드를 설계한다.

param_grid_for_knn은 KNN의 파라미터 그리드를, param_grid_for_svm은 SVM의 파라미터 그리드를 정의한다.

그리고 이 값들을 param_grid라는 사전에 추가한다. 이는 모델이 여러 개 있을 때 파라미터 튜닝을 쉽게 하는 방법이다.

# 파라미터 그리드 생성
param_grid = dict()
# 입력: 모델 함수, 출력: 모델의 하이퍼 파라미터 그리드

# 모델별 파라미터 그리드 생성
param_grid_for_knn = ParameterGrid({'n_neighbors': [3,5,7],
                                   'metric': ['euclidean', 'manhattan']})
param_grid_for_svm = ParameterGrid({'C':[0.1,1,10],
                                   'kernel':['rbf','linear']})

# 모델-하이퍼 파라미터 그리드를 param_grid에 추가
param_grid[KNN] = param_grid_for_knn
param_grid[SVC] = param_grid_for_svm

밑의 코드는 튜닝을 시작하는 과정이다.

먼저,  f1_score는 절대 0보다 작을수 없기에 -1로 초기화한다.

model_func으로 KNN과 SVC 함수를 순회하고, 앞서 정의한 param_grid 사전을 이용하여 각 모델의 파라미터를 param으로 받는다. 이제 param으로 각 파라미터를 돌면서 model_func에 param을 입력하여 모델을 학습하고 f1_score를 계산한다. 이 score가 best_score보다 크다면 최고 모델, 점수, 파라미터를 업데이트한다. 

# 하이퍼 파라미터 튜닝
best_score = -1 # 분류모델로 평가 척도로 f1_score 사용-> f1_score는 절대 0보다 작을수 없기에 -1로 설정
# 분류 모델의 경우 모델 평가지표가 클수록 좋은 모델이기 때문에 최대값을 찾는 것이 목적

from sklearn.metrics import f1_score

for model_func in [KNN, SVC]:
    for param in param_grid[model_func]:
        model = model_func(**param).fit(x_train, y_train)
        y_pred = model.predict(x_test)
        score = f1_score(y_test, y_pred, average='micro')
        # iris 데이터는 클래스가 3개인 다중 클래스 분류 문제이다. 그래서 average가 micro나 macro라는 방식으로 정의 되어야 함
        
        if score > best_score:
            # 점수가 지금까지 찾은 최고 점수보다 좋으면 최고 모델, 점수, 파라미터 업데이트
            best_model_func = model_func
            best_score = score
            best_param = param
            
            # best_model = model -> 최고 모델과 우수한 파라미터를 적용한 모델을 바로 쓸 경우 이 코드 사용

튜닝 결과는 다음과 같다.

print("모델:",  best_model_func)
print("점수:", best_score)
print("파라미터:", best_param)

 

 

 

k-최근접 이웃(k-Nearest Neighbors; kNN)

  • 모델 구조
분류를 위한 kNN

위에 ? 샘플은 이웃 세 개(k=3)로 설정되어 있고 초록색이 더 많으니까 초록색으로 판단을 할 것이고, 밑에 ? 샘플은 세 개의 이웃(k=3) 가운데 파란색이 더 많으니까 파란색이라고 판단할 것이다.

예측을 위한 kNN

왼쪽 ? 샘플은 이웃 세 개(k=3)로 설정되어 있고 이웃의 클래스 값들이 1, 2, 3 이니까 이들의 평균인 2로 클래스 값을 예측 할 것이고, 오른쪽 ? 샘플은 세 개의 이웃(k=3) 가운데 클래스 값들이 8, 8, 9 이니까 이들의 평균으로 클래스 값을 예측 할 것이다.

  • 주요 파라미터와 설정 방법

▶이웃 수(k):홀수로 설정하며, 특징 수 대비 샘플 수가 적은 경우에는 k를 작게 설정하는 것이 바람직하다.
->홀수로 설정한 이유는 동점이 나오는 것을 방지하기 위해서이다. 예를들어 분류의 경우 이웃이 4개일때 클래스1이 2개, 클래스2가 2개일때 동점이 발생함으로써 분류를 할 수가 없다.
▶거리 및 유사도 척도
모든 변수가 서열형 혹은 정수인 경우:맨하탄 거리
방향성이 중요한 경우(예:상품 추천 시스템):코사인 유사도
모든 변수가 이진형이면서 희소하지 않은 경우:매칭 유사도
모든 변수가 이진형이면서 희소한 경우:자카드 유사도
그외:유클리디안 거리

  • 특징 추출이 어려우나 유사도 및 거리 계산만 가능한 경우(예:시퀀스 데이터)에 주로 활용한다.
  • 모든 특징이 연속형이고 샘플 수가 많지 않은 경우에 좋은 성능을 보인다고 알려져 있다.
  • (거리 기반으로)특징 간 스케일 차이에 크게 영향을 받아, 스케일링이 반드시 필요하다(코사인 유사도는 방향성을 보는 것이기 때문에 코사인 유사도를 사용할 경우 스케일링을 할 필요가 없다.)
  • 거리 및 유사도 계산에 문제가 없다면, 별다른 특징 변환이 필요하지 않다.

의사결정나무(Decision tree)

  • 예측 과정을 잘 설명할 수 있다는 장점(설명력이 높기 때문에)덕분에 많은 프로젝트에서 활용한다.

예시1) A 보험사: 고객의 이탈 여부를 예측하고, 그 원인을 파악해라
->이탈 여부를 잘 맞추는 것 보다 그 고객이 왜 이탈하는지 설명이 필요하기 때문에 즉, 설명이 필요한 과제에서는 가장 먼저 생각할수 있는 모델이 Decision tree이다.

  • 의사결정나무의 가장 큰 단점은 선형 분류기라는 한계로 예측력이 좋은 편에 속하지 못한다. 하지만 최근 각광받고 있는 앙상블 모델(예:XGBoost, LightGBM, RandomForest 등)의 기본 모형으로 사용됨으로써 좋은 예측력을 보인다. 이런 앙상블 모델은 예측력은 좋을지 모르지만 설명력이 떨어진다는 문제가 있다.

  • 주요 파라미터

▶max_depth: 최대 깊이로 그 크기가 클수록 모델이 복잡해진다.
▶min_samples_leaf:잎 노드(끝마디)에 있어야 하는 최소 샘플 수로, 그 크기가 작을수록 모델이 복잡해진다.
->복잡도에 영향을 주는 파라미터가 2개 이상인 경우에는 반드시 조정을 할 때 둘을 같이 고려해야한다.


나이브 베이즈(Naive Bayes)

  • 모델 구조

베이즈 정리를 사용하고 특징 간 독립을 가정하여 사후 확률 Pr(ylx)을 계산한다.

가능도 Pr(xj l y)은 조건부 분포를 가정하여 추정한다.
-이진형 변수 : 베르누이 분포
-범주형 변수 : 다항 분포
-연속형 변수 : 가우시안 분포

  • 모델 특성

1. 특징 간 독립이라는 것 자체가 통계학에서 많이 쓰이는 가정임에도 불구하고 굉장히 비현실적이므로 일반적으로 높은 성능을 기대하기 어렵다.
2. 연속형 변수에 대해서 베르누이 분포를 가정 혹은 이진형 변수에 대해서 가우시안 분포를 가정했다 라고 하면 당연히 좋은 결과를 기대하기 어렵다. 즉, 설정한 분포에 따라 성능 차이가 크므로 특징의 타입이 서로 같은 경우에 사용하기 바람직하다. 예를 들어 이진형 변수와 연속형 변수가 섞여있는 경우에는 어떤 분포를 가정하더라도 좋은 결과를 내기 어렵다.
->나이브 베이즈는 특징의 타입이 전부 같은 경우에 사용하는게 바람직하다.
3. 특징이 매우 많고 그 타입이 같은 문제(예:이진형 텍스트 분류)에 주로 사용된다.


서포트 벡터 머신(Support Vector Machine; SVM)

  • 모델 구조

서포트 벡터 머신의 구조는 일반 선형 분류기랑 완벽히 같다.

  • 최적화 모델

목적식에서 왼쪽에 있는 llwll(가중치 벡터의 2차 norm)이 모델의 마진, 크사이i 부분이 모델의 오차를 나타낸다.
# norm은 벡터의 길이나 크기를 측정하는 방법이다.
제약식은 모든 샘플들을 잘 분류하는데 그 때 오차로 크사이i까지 봐주겠다라고 해석한다.
(목적식과 제약식 부분 이해가 잘안가네...)

  • 오차를 최소화하면서 동시에 마진을 최대화하는 분류, 예측 모델로 커널 트릭을 활용하여 저차원 공간을 고차원 공간으로 매핑한다. 이 때 매핑시키는 이유는 선형 분류가 불가능한 경우가 훨씬 많기 때문에 선형분류가 가능하게 하기 위해서이다.
  • 마진의 개념을 회귀에 활용한 모델을 서포트 벡터 회귀(Support Vector Regression)이라 한다.

  • 주요 파라미터

▶kernel: 통상적으로 이진 변수가 많으면 linear 커널이, 연속 변수가 많으면 rbf 커널이 잘 맞는다고 알려져 있다.
->linear 커널은 기본적으로 두 특징간 곱을 커널에 반영시키고 rbf 커널은 두 특징간 차이를 커널에 반영시킨다. 그러면 이진 변수 같은 경우 두 변수의 곱이 의미있는 경우가 많지만 차이는 유의한 경우가 별로 없다.(연속 변수는 이진 변수와 반대)
▶C: 오차 패널티에 대한 계수로, 이 값이 작을수록 마진 최대화에 클수록 학습 오차 최소화에 신경을 쓰며, 보통 10ⁿ, 2ⁿ 형태로 튜닝한다.
->목적식에서 C가 작으면 작을수록 모델의 마진(llwll)을 신경쓰고 C가 크면 클수록 모델의 오차(크사이i 부분)에 신경을 쓴다.(오차 패널티가 작으면 오차에 대해서 관대하게 생각하니 모델의 마진에 신경을 쓴다고 생각하고 오차 패널티가 크면 오차에 대해 관대하게 생각 안하니 오차에 신경을 쓴다라고 생각, 하드마진/소프트마진을 설정할 때 씀)
▶γ(감마): rbf 커널의 파라미터로, 크면 클수록 데이터의 모양을 잘 잡아내지만 오차가 커질 위험이 있으며, C가 증가하면 γ도 증가하게 튜닝하는 것이 일반적이다.

  • 파라미터 튜닝이 까다로운 모델이지만, 튜닝만 잘하면 좋은 성능을 보장하는 모델이다.

신경망

  • 모델 구조

입력 노드는 입력층에 있는 노드들로 입력 값을 받는 역할을 수행한다.
은닉 노드는 은닉층에 있는 노드, 출력 노드는 출력층에 있는 노드이다.
H1을 보면 I0와 I0에 부여된 가중치가 곱해서 값이 들어온다. (마찬가지로 I1, ..., Id+1)
이렇게 전달 받은 값들이 합해진다. 그 후 활성 함수를 통해서 출력을 낸다.

  • 초기 가중치에 크게 영향을 받는 모델로, random_state와 max_iter 값을 잘 조정해야한다.
  • 은닉 노드가 하나 추가되면 그에 따라 하나 이상의 가중치가 추가되어, 복잡도가 크게 증가할 수 있다.
  • 모든 특징 타입이 연속형인 경우에 성능이 잘 나오는 것으로 알려져 있으며, 은닉층 구조에 따른 복잡도 조절이 파라미터 튜닝에서 고려해야 할 가장 중요한 요소이다.
  • 최근 딥러닝의 발전으로 크게 주목받는 모델이지만, 특정 주제(예:시계열 예측, 이미지 분류, 객체 탐지 등)를 제외하고는 깊은 층의 신경망은 과적합으로 인한 성능 이슈가 자주 발생한다. 즉, 특정 도메인(시계열 예측, 이미지 분류, 객체 탐지 등)의 문제를 제외하면은 깊은 층의 신경망은 좋은 모델이라고 보기 어렵다.


트리 기반의 앙상블 모델
최근 의사결정나무를 기본 모형으로 하는 앙상블 모형이 캐글 등에서 자주 사용되며, 좋은 성능을 보인다.

  • 랜덤 포레스트: 배깅(bagging) 방식으로 여러 트리를 학습하여 결합한 모델
  • XGboost & LightGBM: 부스팅 방식으로 여러 트리를 순차적으로 학습하여 결합한 모델
  • 랜덤 포레스트를 사용할 때는 트리의 개수와 나무의 최대 깊이를 조정해야 하며, XGboost와 LightGBM을 사용할 때는 트리의 개수, 나무의 최대 깊이, 학습률을 조정해야 한다.

▶트리의 개수: 통상적으로 트리의 개수가 많으면 많을수록 좋은 성능을 내지만, 어느 수준 이상에서는 거의 큰 차이를 보이지 않는다.(계산 시간 측면에서 조정해야하는 파라미터)
▶나무의 최대 깊이: 4이하로 설정해주는 것이 과적합을 피할 수 있어 바람직하다.
▶학습률: 이 값은 작으면 작을수록 과소적합 위험이 있으며, 크면 클수록 과적합 위험이 있다. 통상적으로 0.1, 0.05로 주로 설정을 한다.


오늘은 각 모델들을 간단하게 알아보았다. 각 모델들을 학술적으로 공부하게되면 수학적으로 난이도가 있기 때문에 너무 어렵게 느껴진다. 각 모델들을 교재와 함께 리마인드를 해야겠다.

선형 결합이란? 선형 결합은 선의 관계, 다시 말해 직선 관계이다. y=x, y=2x, y=3x와 같은 꼴로 그래프를 그려보면 직선 관계이다.

선형 회귀 모델과 정규화 회귀 모델

  • 모델 구조

->각 특징에 대해서 가중치가 붙은 형태로 되어있고 각각들이 선형 형태로 결합되어 있다.(각각, 특징 벡터와 가중치 벡터의 선형 결합(선의 형태)으로 표현)

  • 비용 함수:오차제곱합

->선형 회귀 모델의 경우 비용 함수로 오차제곱합을 사용하고 정규화 회귀 모델의 경우 오차제곱합에 계수들의 패널티를 추가시킨다.
->오차제곱합을 쓰는 이유는 양의 오차와 음의 오차가 손실되는 것을 방지하기 위해서 쓴다.
->정규화 회귀 모델에서 패널티를 추가시킨 이유는 모델이 너무 복잡해지는 것을 방지하기 위해서이다. 즉, 계수들의 절대값이 너무 커지는 것을 방지하기 위해서이다. 람다의 경우 사용자가 설정하는 하이퍼 파라미터이고 람다가 크면 클수록 계수들의 패널티에 집중하고 단순한 모델이 만들어지고, 람다가 작으면 작을수록 오차제곱합에 집중하고 복잡한 모델이 만들어 진다.

  • 특징과 라벨 간 비선형 관계가 무시될 수 있으므로, 특징 변환이 필요

->특징벡터와 가중치 벡터의 선형 결합으로 표현이 되어있는데 만약, 가중치 벡터와 특징벡터의 관계가 선형이 아니라 비선형 관계가 있으면 그 관계가 완벽하게 무시될수 있으므로 특징 변환이 필요하다.

데이터(파란색 선)의 분포를 보면, x와 y는 2차식 관계라는 것을 대략적으로 추정할 수 있다. x를 x^2으로 변환한다면 과소적합된 주황색 모델이 아닌 적절하게 적합된 초록색 모델을 학습할 수 있다.
이 과정을 코드를 통해서 확인해볼 것이다.

import numpy as np
from sklearn.linear_model import LinearRegression as LR
from matplotlib import pyplot as plt

## 가상 데이터 생성
X = np.linspace(-5,10,500) # -5부터 10까지 등간격으로 500개의 요소를 갖는 벡터
X_2 = X**2 # X의 모든 요소에 제곱이 된 형태

Y = 3*X**2 + np.random.normal(0,4,size=len(X)) # X와 Y는 2차식 관계
# np.random.normal:정규분포로부터 무작위 표본 추출
# 평균 0, 표준편차 4인 정규분포로 부터 난수 len(X)개를 생성

plt.scatter(X,Y)
# X는 1차원으로 sklearn의 인풋 구조와 맞지 않음->reshape 사용
# .fit(X,Y) => X = [[record1], [record2], ..., [record n]]
# 1차원: X = [record1, record2, ..., record n]
# reshape() 메소드는 차원을 변경할 때 사용
# reshape(-1,1)에서 -1은 원래 배열의 길이와 남은 차원으로부터 추정해서 알아서 지정하라는 의미

## 모델 학습
model_1 = LR().fit(X.reshape(-1,1), Y) # 인자가 2개이므로 2차원 변경
model_2 = LR().fit(X_2.reshape(-1,1), Y)

## 모델 적용
model_1_predict = model_1.predict(X.reshape(-1,1))
model_2_predict = model_2.predict(X_2.reshape(-1,1))
## 결과 시각화
%matplotlib inline
plt.xlabel("X")
plt.ylabel("Y")

plt.plot(X,Y,label="data", color='y')
plt.plot(X, model_1_predict, label="Y=f(x)")
plt.plot(X, model_2_predict, label="Y=f(x**2)", color='g')

plt.legend(loc='upper left')

데이터랑 초록색 모델은 거의 일치하는 것을 볼수있으나 파란색 모델은 실제 데이터의 분포를 거의 반영하지 못한다.
모델1은 X를 그대로 사용하고 모델2는 X^2을 사용한 것 밖에 차이가 없지만 단순한 변환임에도 불구하고 데이터의 X와 Y간 비선형 관계가 있는 경우에는 특징 변환을 해줬을 때 엄청난 퍼포먼스를 얻을 수 있다.
->MAE나 RMSE는 구하지 않아도 당연히 초록색 모델의 성능이 훨씬 우수할 것으로 예상한다.

  • (선형 결합이기 때문에)특징 간 스케일 차이에 크게 영향을 받아, 예측 모델링을 할 때 스케일링이 필요함
  • λ와 max_iter에 따라 과적합 정도가 직접 결정됨

로지스틱 회귀 모델

  • 모델 구조

로지스틱 회귀 모델은 분류에 쓰는 회귀 모델로 라벨이 1일 확률을 출력해주는 모델이다.
라벨이 1일 확률이 cut-off-value 이상이면 1, 아니면 0으로 본다.

  • 비용 함수:크로스 엔트로피

라벨이 실제로 1인 샘플에 대해서는 1일 확률을 높게 잡아야 비용이 작아지고 실제로 0인 샘플에 대해서는 0일 확률을 높게 잡아야(1일 확률을 적게잡아야) 비용이 적게 받도록 설계된 함수이다.

  • 특징의 구간 별로 라벨의 분포가 달라지는 경우, 적절한 구간을 나타낼 수 있도록 특징 변환이 필요함

이 과정을 코드를 통해서 확인해볼 것이다.

import numpy as np
from sklearn.linear_model import LogisticRegression as LR
from matplotlib import pyplot as plt

# np.random.random(size=10):개수가 10개인 0~1 사이의 난수 생성
# tolist():ndarray를 리스트로 변환

## 가상 데이터 생성
X = np.random.random(size=10).tolist() + (np.random.random(size=10)+1).tolist() + (np.random.random(size=10)+2).tolist()
Y = [0]*9 + [1]*1 + [0]*2 + [1]*8 + [0]*9 + [1]*1

plt.scatter(X,Y) # 명확하지는 않지만 2이상, 1이하에서는 0이 많고, 1~2 중간에서는 1이 많은 것을 확인
X = np.array(X) # array로 변환
Y = np.array(Y)

#조건 생성
cond = np.logical_and(X>1, X<=2) # np.logical_and(bool1, bool2):모든 조건을 충족할 경우 True, 아닐 경우 False
# 부울리스트가 둘 다 True이면 True 반환, 하나라도 False이면 False 반환

X_tilda = X.copy()

X_tilda[cond]=1 # X_tilda에 cond 조건을 만족시키면 1을 가지게 함
X_tilda[~cond]=0 # 아니면 0
## 모델 학습

model_1 = LR().fit(X.reshape(-1,1), Y) # X를 특징으로 그대로 사용한 모델
model_2 = LR().fit(X_tilda.reshape(-1,1), Y) # 변환한 특징을 사용한 모델(0과 1사이의 실수를 0과 1의 정수로 변환한 ndarray)
# decision boundary 유도
# Pr(y = 1 | x) >= 0.5
# <=> 1 / (1 + exp(-coef * x - intercept)) >= 0.5
# <=> (1 + exp(-coef * x - intercept)) <= 2
# <=> exp(-coef * x - intercept) <= 1
# <=> -coef * x - intercept <= 0
# <=> x >= - intercept(bias) / coef(계수)
# 위 식을 통해 decision boundary는 "- intercept / coef" 정의됨(참고)

## 결과 시각화(X를 특징으로 그대로 사용한 모델 시각화)
decision_boundary = -(model_1.intercept_[0] / model_1.coef_[0])
plt.plot([decision_boundary, decision_boundary], [-0.1, 1.1], color='r')
plt.scatter(X[Y==0], Y[Y==0]) # 실제로 Y가 0인 부분 시각화
plt.scatter(X[Y==1], Y[Y==1]) # 실제로 Y가 1인 부분 시각화
# 모든 샘플들이 decision_boundary를 기준으로 같은 공간에 있음->구분이 어려움
## 결과 시각화(변환한 특징을 사용한 모델 시각화)
decision_boundary = -(model_2.intercept_[0] / model_2.coef_[0])
plt.plot([decision_boundary, decision_boundary], [-0.1, 1.1], color='r')
plt.scatter(X[Y==0], Y[Y==0])
plt.scatter(X[Y==1], Y[Y==1])
# 오른쪽에는 주황색, 왼쪽에는 파란색으로 잘 분리된걸 확인
# 오차가 존재하긴 하지만 변환한 특징이 X를 특징으로 그대로 사용한 것 보다 확연히 좋아진 것을 확인

오늘은 예측 모델인 선형회귀모델, 정규화 회귀모델 / 분류 모델인 로지스틱 회귀모델을 알아보았다. 이 모델들은 선형성을 기반으로 한 모델이다. 학교를 다닐 때 회귀분석을 배운적이 있지만 지금 다시 복습을 해보니 그때보다는 조금 더 알 것 같다. 아직까지는 이 지식이 어렵게 와닿지만 후에 다시 머신러닝을 공부할 때 이 자료와 교재를 기반으로 한 번 더 리마인드를 할 것이다.

모델 개발 프로세스

위의 그림을 통해 모델 개발 프로세스의 전반적인 흐름을 알 수 있다. 지금부터는 세부적으로 각각의 단계를 알아 보려고 한다.


문제 정의 단계는 전체 프로세스 가운데 가장 중요한 단계로, 명확한 목적 의식을 가지고 프로세스를 시작해야 한다.
이 단계에서 수행하는 활동은 다음과 같다.

  • 과업 종류 결정(분류, 예측 등)
  • 클래스 정의
  • 도메인 지식 기반의 특징 정의
  • 사용 데이터 정의

데이터 수집 단계는 문제 정의 단계에서 정의한 데이터를 수집하는 단계로, 크롤링, 센서 활용, 로그 활용 등으로 데이터를 수집한다. 기업의 경우 기업 내 구축된 DB에서 SQL을 통해 추출하는 경우가 가장 많으며, 이때는 클래스를 중심으로 수집한다.

데이터 탐색 단계는 데이터가 어떻게 생겼는지를 확인하여 프로세스를 구체화하는 단계이다. 데이터 탐색 단계에서 변수별 분포, 변수 간 상관성, 이상치와 결측치, 변수 개수, 클래스 변수 분포 등을 확인하며, 이 탐색 결과는 데이터 전처리 및 모델 선택에 크게 영향을 미친다.

데이터 전처리 단계는 원할한 모델링을 위해 데이터를 가공하는 단계로, 여기서 수행하는 대표적인 작업은 결측값 처리, 데이터 통합, 이상치 제거, 재샘플링, 특징 선택, 더미 변수 생성이 있다.

모델링 단계는 모델 선택->하이퍼 파라미터 설정->모델 학습 순으로 진행되며
모델 선택에서는 데이터 특성, 성능, 설명력 등을 기준으로 모델을 선택한다. 첫 번째 예시로 설명력이 중요한 경우 의사결정나무 혹은 베이지안 네트워크를 사용하고, 두 번째 예시로 이진 텍스트 분류를 하는 경우 나이브베이즈 혹은 서포트 벡터 머신을 사용한다.
하이퍼 파라미터 설정에서는 모델의 성능을 결정짓는 하이퍼 파라미터를 설정한다. 하지만 최적의 하이퍼 파라미터 설정은 굉장히 어려운 작업이다.
모델 학습에서는 모델에 포함된 파라미터를 추정하고 이 모델 학습에서는 이미 잘 개발되어 있는 모듈/패키지가 있기에 전혀 어렵지 않은 작업이다.

모델 평가 단계의 경우 분류 모델일 경우와 예측 모델일 경우로 나뉜다.
분류 모델의 대표적인 지표로는 정확도(accuracy), 정밀도(precision), 재현율(recall), F1 점수(F1-score)가 있다.
예측 모델의 대표적인 지표로는 평균 제곱 오차(MSE), 평균 절대 오차(MAE) 등이 있다.
->모델 평가 단계에서 잘못된 평가를 피하기 위해, 둘 이상의 평가 지표를 사용하는 것이 바람직하다.


결과보고서 작성 단계는 지금까지의 분석 결과를 바탕으로 보고서를 작성하는 단계로 결과보고서의 통일된 구성은 없지만, 일반적으로 다음과 같이 구성된다.

  1. 분석 목적
  2. 데이터 탐색 및 전처리
  3. 분석 방법
  4. 분석 결과 및 활용 방안

지도학습이란?

  • 컴퓨터에게 입력과 출력을 주고, 입력과 출력 간 관계를 학습하여 새로운 입력에 대해 적절한 출력을 내도록 하는 기계학습의 한 분야이다
  • 입력을 특징(feature) 혹은 특징 벡터(feature vector)라고 하며, 출력을 라벨(label)이라고 한다
  • 라벨이 범주형 변수면 분류라고 하며, 연속형 변수면 예측이라고 한다

과적합이란?

  • 지도학습 모델은 학습 데이터를 분류하고 예측하는 수준으로, 학습에 사용되지 않은 데이터도 정확히 분류하고 예측하리라 기대하며, 이러한 기대가 충족되는 경우 일반화되었다고 한다
  • 모델이 너무 복잡해서 학습 데이터에 대해서만 정확히 분류하고 예측하는 모델을 과적합되었다고 하며, 반대로 너무 단순해서 어떠한 데이터에 대해서도 부적합한 모델을 과소적합되었다고 한다
  • 과적합과 과소적합에 영향을 끼치는 주요 인자로는 모델의 복잡도, 샘플 수, 차원의 크기 등이 있다

차원의 크기가 클수록(특징이 많을수록) 가지고 있는 학습 데이터를 잘 설명하기에 좋기 때문에 학습 오차는 계속해서 줄어든다. 그러나 평가 오차는 어느정도 잘 설명하다가 새로운 데이터를 설명하는 과정에서 과적합이 일어날 위험이 크다.
정리하면 특징이 너무 적으면 과소적합이 일어나기 좋고 특징이 너무 많으면 과적합이 일어나기 좋다.

샘플 수는 많으면 많을수록 과적합에서 벗어난다. 샘플 수가 너무 많아서 생기는 문제는 계산량 문제를 제외하고는 없다.
샘플 수가 적으면은 가지고 있는 데이터로만 설명하기 때문에 학습 오차는 작고 평가 오차는 크다. 하지만 샘플 수가 많을수록 적정 적합이 되어서 평가 오차와 학습 오차가 거의 같아지는 지점이 오고, 어느 지점부터는 샘플 수가 더 늘어나도 평가 오차와 학습 오차가 크게 감소하지 않는 그런 패턴을 볼 수 있다.


데이터 분할

  • 과적합된 모델을 좋게 평가하는 것을 방지하기 위해서, 데이터를 학습 데이터와 평가 데이터로 분할한다

  • 학습 데이터와 평가 데이터가 지나치게 유사하거나 특정 패턴을 갖지 않도록 분할해야 한다

파라미터와 하이퍼 파라미터
하이퍼 파라미터(hyper parameter)는 일종의 사용자 옵션으로, 모델 성능에 직접적으로 영향을 끼치므로 자세한 데이터 탐색 결과를 바탕으로 선택해야 한다


이진 분류 모델 평가 : 혼동 행렬

  • 이진 분류:클래스 변수의 상태 공간이 크기가 2인 분류
  • 혼동 행렬:분류 모델을 평가하는데 사용하는 표

▶Positive class:분석의 관심 대상(보통 1로 설정)
▶Negative class:분석 관심 대상 외(보통 0이나 -1로 설정)
->학습 데이터인 x_train, y_train으로 모델을 학습한 후 평가 데이터인 x_test를 사용해서 예측 값을 구하고 실제 값인 y_test와 비교를 한 것

  • 각 지표의 한계 때문에, 가능한 여러 지표를 사용하여 모델을 평가해야 한다
이진 분류 모델 평가:대표적인 지표

다중 분류 모델 평가

  • 다중 분류:클래스 변수의 상태 공간이 크기가 3이상인 분류
  • 각 클래스를 긍정으로 간주하여 평가 지표를 계산한 뒤, 이들의 산술 평균(macro average), 가중 평균(micro average, weighted average)으로 평가한다


예측 모델 평가

  • 대표적인 예측 모델 평가 지표로 루트 평균 제곱 오차(root mean squared error; RMSE)와 평균 절대 오차(mean absolute error; MAE)가 있으며, 두 지표 모두 값이 작을수록 좋다

RMSE와 MAE를 정확히 평가하려면, 해당 분야의 도메인 지식이나 클래스 변수의 스케일을 고려해야 한다고 한다. 예를 들어, 코스피 지수를 예측하는 모델의 MAE가 1010이라면 무의미한 수준의 모델이지만, 전세계 인구 수를 예측하는 모델의 MAE가 1010이라면 매우 우수한 모델이라고 나타나있는데 이게 정확히 무슨 의미인지는 잘 모르겠다...

+ Recent posts