군장병 AI 트랙(중급) PART 5 필기

배코딩·2022년 12월 8일
0

군장병 AI 교육

목록 보기
7/8

1강 : 쉽게 풀어서 살펴보는 인공지능

포함 관계

인공지능 > 머신러닝 > 딥러닝


인공지능의 정의

인공지능 : 주변의 환경을 인식하고, 행동을 취하여, 목표를 달성할 가능성을 최대화하는 기계(가 가진 지능)


지능의 규정 범위에 따른 구분

약인공지능 : 현재 단계임. 단일 작업만 수행가능한 단계. 예를 들어 바둑 두는 알파고한테 스타크래프트 해달라고 못하는것처럼

강인공지능 : 사람과 유사하게 동작하는 인공지능

Artificial Super Intelligence : 사람보다 더 뛰어난 인공지능


모델과 기능

데이터 수집 > 모델 만들기 > 기능 구현

요즘에는 더욱 더 뛰어난 모델을 만드는 것보다 모델을 활용해서 어떤 기능을 만들 것인지(고객의 니즈 충족)를 중점적으로 생각하는 능력이 필요해짐

인공지능 기술을 문제 해결을 위한 도구로서 활용한다는 마인드


모델이란?

데이터를 가장 잘 표현하는 설명 방법을 찾는 과정

그러한 모델을 찾는 것을 model fitting이라고 함

1) 초기 모델(가설 모델)에 데이터 넣기

2) 결과 평가 (mean squared error 등등)

3) 결과를 개선하기 위해 모델 수정 (모델 내부 parameter 수정 및 아예 모델 종류를 변경)


parameter란?

모델 모양 에 영향을 주는 바뀌는 값들

예를 들어 선형 모델 y=ax+b가 있다고 치면,

여기서 y는 예측값, x는 입력 데이터고 a와 b가 파라미터다.

보통 파라미터를 세타로 표기하고, 모델을 학습시켜 최적의 세타값을 찾는다.


학습이란?

: 실제 정답과 예측 결과 사이의 오차(loss, cost, error)를 줄여나가는 최적화 과정


autoML

자동회된 머신러닝.

구글 Cloud Platform의 AutoML, 또는 AutoAI, auto keras, auto-sklearn 등이 있음.

정형 데이터를 넣으면 그거에 맞는 가장 좋은 성능의 모델을 찾아서 리턴해줌 (물론 세상에 존재하는 모든 모델 중에서 최고의 모델을 찾아주는 정도는 아님)

전제조건으로서 1000행 이상의 데이터가 필요함


2강 : 머신러닝 핵심 개념 이해 (1) - 머신러닝의 대분류 3가지

머신러닝의 분류

1) 지도학습 : 정답이 있는 데이터로 학습

classification : 예측값이 수치가 아니라 특정 클래스로 구분지음(ex- ~인지 아닌지를 0과 1로 나타내고 예측)

regression : 회귀. 수치 데이터를 대상으로 학습

2) 비지도학습 : 정답이 없는 데이터로 학습

clustering : 그룹화(군집화)

dimensionally reduction : 차원 축소


Supervised Learning

학습의 목적 : Input data에 대한 정답을 예측(Function approximator)

데이터에 정답이 존재한다.


회귀 : Numerical output

분류 : categorical output


어떤 문제를 해결하고자할 때 이걸 회귀 모델을 활용할지 분류 모델을 활용하는게 좋을지 생각해내는게 중요함. output의 형태를 잘 생각해보자. 참고로 같은 문제라도 output의 여러 형태에 따라 회귀도 가능하고 분류도 가능할 수 있는데 성능이 더 좋게 나오는걸 골라서 쓰자.


시계열 데이터 대상으로 추후 추가 학습하자

11가지 전통적인 시계열 예측 모델
TF 2.0 시계열 예측 공식 튜토리얼
PapersWithCode for 시계열 예측


Unsupervised Learning

학습 목표 : Input data 속 규칙성 발견 (shorter description)

데이터에 정답(label, target)이 존재하지 않는다. 비슷한 것끼리 그룹을 지음

군집 분석, 차원 축소


ex) 장바구니 분석, 추천 시스템, 고객군 분류 등

처음에 추천 시스템으로 보통 프로젝트 많이하는 듯


Reinforcement Learning

Trial & Error를 통한 학습 (Sequential decision making)

지도/비지도학습으로 구했던 한 번의 의사 결정을, 계속해서 반복하는 행동을 한다.

순차적 의사 결정으로 목적지까지 도달하는 경우를 계속 반복하고, 이를 통해 최종적으로 얻게 될 기대 보상을 최대화하기 위한 행동 선택 정책을 학습하는 것이 목표임.

1) 연속적인 단계마다 상태(State, 미로의 좌표)를 인식

2) 각 상태에 대해 결정한 행동(Action, 상하좌우 이동)들의 집합에 대해

3) 환경으로부터 받는 보상(Reward, 목적지까지 도달할 때 cost 등)을 학습하여

4) 전체 행동에 대한 보상을 최대화하는 행동 선택 정책(Policy, 어떤 길로 가야 더 많은 보상을 얻을 수 있는가)을 찾는 알고리즘이다.


DP나 그리디같은 느낌인듯



다만 State(미로에서의 벽), Action(추후 대각선으로 이동할 수 있게되는 등)이 추후 변화할 수도 있는데, 이런 경우에 대한 강화학습 모델이 또 있음.

처음에는 State와 Action을 정적으로 지정해놓고 학습하는 걸 공부하고 그 이후에 Dynamic한 변화에 대한 강화학습을 공부하면 됨.

NHN FORWARD 강화 학습 기초 1~4


3강 : 머신러닝 핵심 개념 이해 (2) - Capacity & Overfitting

Capacity

모델의 복잡도, 현실 세계를 얼마나 더 잘 반영하는가


overfitting

다만 Capacity도 적당히 높아야한다.

너무 높으면 학습 데이터에 지나치게 fit되어 오히려 추후 새로운 데이터가 들어왔을 때의 오차가 더 커지는 경우가 발생한다. (overfitting -> generalization error(=일반화 성능 저하))


x_data 명칭 : feature

y_data 명칭 : label / target

둘 다 가능 : attribute / dimension / column


4강 : 머신러닝 핵심 개념 이해 (3) Cross-validation & K-Fold CV

Cross validation

데이터를 3개의 그룹으로 나눈다.

1) 60%의 Training data로 모델을 학습

2) 20%의 validation data로 모델(or 하이퍼 파라미터)을 최적화/선택(tune)

3) 20%의 test data로 모델 평가(test only, no more tune)

다만 여러 모델(또는 하이퍼파라미터) 중 하나를 선택해야하는 경우가 아니라면 그냥 모델 하나만 가지고 70%로 학습, 30%로 모델 평가할 수도 있다.

데이터가 아주 많다면 98:1:1로 하는 경우도 있다. 98%의 데이터가 모집단의 분포 정도를 잘 반영하고 있을 것이라고 가정하고 하는거임. 대부분은 6:2:2나 7:3으로 하긴 한다.


(Stratified) K-Fold cross validation

: K-Fold CV, 후보 모델 간 비교 및 선택을 위한 알고리즘

보통 CV라고 하면 cross validation이거나 computer vision이거나 curriculum vitae임

K는 직접 정하는 값임. 보통 5나 10으로 정하는 듯



  • 전체 과정 설명
  1. 데이터를 80%, 20%로 나눈다. (80은 train data, 20은 test data)

  2. 여러 후보군 모델을 준비한다.

  3. 모델 하나를 학습 및 검증한다.

    3-1) 80%의 데이터를 10토막낸다. (K=10, 10-Fold CV)

    3-2) 토막에 번호를 붙인다고 치면, 일단 phase1에서는 1~9번으로 train, 10번으로 validation한 후 성능 지표를 본다. (성능 몇퍼) 그리고 다시 모델의 학습 상태를 초기화한다.

    3-3) phase2에서는 1~8, 10번으로 train, 9번으로 validation, 성능 보고 모델 초기화

    3-4) 이런 식으로 validation data를 바꿔가면서 phase10까지 성능 평가

    3-5) 10번의 성능을 평균내어 해당 모델의 최종적인 성능 지표를 구함

  4. 다른 모델도 똑같은 방법으로 성능을 구한다.

  5. 그 중 가장 높은 성능의 모델을 선택한다.

  6. 선택된 모델을 다시 초기화 후, 이번에는 전체 데이터 80%를 토막내지않고 전부 다 활용해서 train, 20%로 test해서 나온 성능을 모델의 최종 성능으로 삼는다.

  7. 만약 classification과 같은 경우에서 클래스가 0과 1이 있다고 칠 때, 80%에 클래스 1, 20%에 클래스 0이 몰려버리는 경우가 발생할 수 있다.

    이를 방지하기 위해 stratified를 활성화하면 토막낸 데이터 내에서도 클래스 0과 1이 8:2 비율을 유지하도록 데이터를 나눠낼 수 있게하여 유효한 모델 학습을 진행할 수 있게된다.


교차 검증 외에도 traning data를 많이 확보하는 등의 방법도 있다.


전통적인 머신러닝에서 사실 데이터가 데이터가 너무 적으면 실용적으로 대부분 처음에 8:2로 안 쪼개고 그냥 하는 듯


클래스 불균형 해결 참고 자료

딥러닝에서 클래스 불균형을 다루는 방법
불균형 데이터 분류


5강 : 비용함수와 경사하강법 (1) - Cost function & MSE

linear regression

: 종속 변수 y와 한 개 이상의 독립 변수 (= 설명 변수) x 사이의 선형 상관 관계를 모델링하는 회귀분석 기법 (정답이 있는 데이터의 추세를 잘 설명하는 선형 함수를 찾아 x에 대한 y를 예측


  • hΘ(x)=Θ0+Θ1xh_\Theta(x) = \Theta_0 + \Theta_1x ( y=ax+b )

Θ1\Theta_1 (a) : 가중치 (기울기)
Θ0\Theta_0 (b) : 보정치 (y절편, bias)


선형 회귀에서의 target : 보정치 + 각 feature(x_data)에 가중치를 각각 곱한 값의 합

선형 회귀 일반식(=선형 결합=linear combination) : hΘ(x)=Θ0+Θ1x1+Θ2x2++Θnxnh_\Theta(x) = \Theta_0 + \Theta_1x_1 + \Theta_2x_2 + ··· + \Theta_nx_n

선형 회귀의 목적 : 어떤 데이터를 넣었을 때 최적의 결과값을 도출할 수 있는 가장 적합한 Θ\Theta들의 set을 찾는 것


Cost Function = J(Θ\Theta)

: Θ\Theta값이 적합한지 판단할 수 있는 성능 지표. 예측 값과 실제 값의 차이를 기반으로 모델의 성능(정확도)을 판단하기 위한 함수

  • Cost = Loss = Error

  • J(Θ\Theta) = 1mi=1m(hΘ(x(i))y(i))2\frac{1}{m}\sum_{i=1}^{m}(h_\Theta(x^{(i)})-y^{(i)})^2


선형 회귀에서 cost function은 보통 평균 제곱 오차 함수(MSE function, Mean Square(d) Error Function)를 활용함.

모든 데이터들에 대해 error의 제곱을 다 더한 후 데이터의 총 개수로 나눠준 값을 성능 지표로 삼음.

이 값이 크면 모델의 성능이 안 좋은거임


MSE와 비슷한 다른 함수들도 있음

1) 제곱안하고 절댓값 씌워주고 계산하면 MAE function임.

MAE와 MSE는 각각의 장단점이 있음.

2) MAPE (mean absolute percentage error) : 비율값으로 계산

3) RMSE (root mean square error) : 제곱합에 루트 씌우고 총 개수로 나눔


6강 : 비용함수와 경사하강법 (2) - Gradient Descent

Gradient Descent

: MSE Function은 J(Θ\Theta) = 1mi=1m(hΘ(x(i))y(i))2\frac{1}{m}\sum_{i=1}^{m}(h_\Theta(x^{(i)})-y^{(i)})^2 인데, 여기서 x와 y는 데이터로 들어오는 값이고, hΘ(x(i))h_\Theta(x^{(i)})는 모델의 일차 함수이다. 일차 함수 내에서 y절편은 상수로 고정하고 기울기만을 놓고보면 MSE는 기울기의 값에 따라 변하는 일차 함수 관계가 있다.

이 때 기울기(Θ1\Theta_1)과 MSE 간의 함수 그래프를 그려보면, 제곱이 있으므로 이차 함수 형태의 어떤 곡선 그래프가 나올 것으로 예상된다.

Θ1\Theta_1의 초기 값을 적당히 잡고, Θ1\Theta_1 지점에서 미분을 해서 접선의 기울기를 구해본다.

구해진 기울기의 반대 방향으로 Θ1\Theta_1의 값을 바꾸면서 계속 미분하다보면 0에 가까워질텐데, 0에 수렴하는 그 지점이 곡선 그래프에서 y값, 즉 MSE가 최소인 Θ1\Theta_1가 바로 거기다. (그 지점을 global minimum이라 부른다.)


7강 : 비용함수와 경사하강법 (3) - Learning rate & Hyper-parameter

Learning rate

Θjnew:=ΘjαΘjJ(Θ0,Θ1)\Theta_{j_{new}} := \Theta_j -\alpha\frac{\partial}{\partial\Theta_j} J(\Theta _0, \Theta _1) (for j = 0 and j = 1)

여기서 α\alpha가 학습률임. 사용자가 직접 정하는 값임

학습률이 너무 작으면 지나치게 오래 걸리고, 너무 크면 오히려 최저점에서 멀어져버림. 적당한 값으로 보통 0.01 ~ 0.001 정도 쓰는 듯


Hyper-parameter

학습률 등과 같이, 사람이 직접 정해주는 값을 통틀어서 Hyper-parameter라고함. (초매개변수)

이와 대조되는 개념이 parameter $$\Theta$$

하이퍼 파라미터로 가장 적절한 값을 자동으로 측정하는 기술도 있음. AutoML 분야 중에 AutoHPO (Hyper parameter optimization = hyper parameter tuning = model tuning)


경사하강법 설명에서 세타 하나만 변수로 두고 설명했는데, 원래는 모든 세타를 동시에 다 고려해서 함.

만약 세타가 2개라면 x,y축은 세타가 되고 z축이 loss값이 될거임.

모든 세타를 변수로서 MSE의 변화 추이를 예상하고 이를 대상으로 경사하강법을 적용하여 최저점에서의 세타 순서쌍을 찾음.


경사하강법이라는 방법 외의, 그냥 도함수=0 이라는 방식으로 세타를 찾는 것도 가능함. 이런 기법 등을 통틀어 수학에서는 optimization이라고 칭함.

경사하강법을 쓰는 이유는, 딥러닝 시대로 오면서 x_data가 행, 열방향으로 너무 방대해져서 미분방정식 푸는 방식으로는 역행렬 곱하고 이런 연산에 너무 많은 시간이 들어서 경사하강법을 채택하게 된거임. 데이터가 크지 않다면 도함수=0 으로 찾아도 무방함.


경사하강법 설명할 때 2차함수로 예를 들었는데, 만약 세타-MSE 함수 형태가 변곡점이 존재한다면 아래로 볼록한 부분이 한 번 나오고, 그 지점을 지나면 다시 MSE값이 하락하는 형태의 그래프의 경우에는 어떡하나? 라는 의문이 들 수 있다.

맞는 말임. 변곡점 이전에 나오는 미분값=0인 지점 같은 함정같은 경우를 local minima라고 함.

그러나 다행히도 딥러닝 연구가 진행되오면서 이 함정을 탈출해서 가던 방향으로 조금 더 가는, 그런 기법들이 만들어졌음. 그래서 약한 함정들은 얼추 빠져나오는 기대를 할 수 있음. 만약 강한 local minima를 빠져나오지 못하고 global minima에 도달하지 못하고 종료되는 경우에는 그 때의 local minima가 global minima와 거의 유사하다고 경험적으로 판단하고 있는 상황임.

그러니 local minima에 대한 걱정은 안해도 될 것 같다.


8강 : Linear Regression (1) - Scikit-learn 소개 & One-hot encoding

Scikit-learn

: Traditional machine learning 알고리즘을 python으로 구현한 오픈 소스 라이브러리

파이썬의 다른 라이브러리(numpy, padnas, matplotlib 등)와 호환성이 좋음

전체에 걸쳐 통일된 인터페이스를 갖고 있어 아주 간단하게 여러 알고리즘을 적용할 수 있음



  1. 데이터 셋을 df로 불러와 np array로 바꿔주기 (df에서 모든 열이 수치형 데이터여야 array로 잘 변환가능)

    또는 sklearn.datasets.load_~~~() 로 예시 데이터 활용 가능


  1. Train/Test set으로 데이터 나누기

sklearn.model_selection.train_test_split(X, Y, test_size)

전통적인 머신러닝은 데이터 크기가 크지않아 validation 없이 train과 test로 나눈다.


  1. 모델 객체 생성하기

model = sklearn.linear_model.LinearRegression()

sklearn.linear_model.LogisticRegression()
sklearn.neighbors.KNeighborsClassifier(n_neighbors) # 인자 : hyper parameter
sklearn.cluster.KMeans(n_clusters) # 인자 : hyper parameter
sklearn.decomposition.PCA(n_components) # 인자 : hyper parameter
sklearn.svm.SVC(Kernal, C, gamma) # 인자 : hyper parameter


  1. 모델 학습 시키기 (model fitting)

model.fit(train_X, train_Y)


  1. 모델로 새로운 데이터 예측하기 (predict on test data)

model.predict(test_X)
model.predict_proba(test_X)

sklearn.metrics.mean_squared_error(predicted_Y, test_Y)
sklearn.metrics.accuracy_score(predicted_Y, test_Y) # 분류 모델의 정확도 지표
sklearn.metrics.precision_score
sklearn.metrics.recall_score
sklearn.metrics.r2_score


LSE(or LSE) : least squared estimation, 최소 자승법, 제곱 값이 작아지는 방향으로 수정. 즉 MSE랑 같은 개념이라고 보면 됨. 매개변수명으로 저렇게 약어로 나오는 경우 있으니 참고하자.


Feature Normalization (Scailng)

: 모델 학습 전 데이터 전처리

Numerical Column : min-max algorithm, standardization

Categorical Column : one-hot encoding


One-hot encoding

예를 들어 데이터의 행이 집이고, 집으로부터 지하철까지의 거리의 구간을 나타내는 범주 값을 값으로 갖는 column이 있다고 치자.

이는 Categorical Column인데 선형 모델에 이걸 그대로 써버리면 산술 연산 가능 feature라고 인식을 해버려서 곤란하다.

그래서 One-hot encoding을 통해 이를 선형 모델에 적용가능한 feature로 바꿔준다.


집에서 지하철까지의 구간이 1, 2, 3, 4 ~ 24 까지 있다고 치자.

만약 column의 값이 맨 윗행부터 1, 2, 3, 1, 24, 5라고 치자.

우선 새로운 DF를 하나 만든다고 생각하자.

이 DF의 열은 categorical value로 삼는다. 즉 1, 2, 3, 4, ~ 24이다.

그리고 원래 categorical column에서의 첫번째 값 1을 이 DF에 넣어주는데,

이 값 자체가 DF의 행이 되고, 자신의 값과 열의 값이 일치하는 셀에만 1을 넣고 나머지는 0을 넣어준다.

즉 (1, 0, 0, 0, ..., 0)의 행벡터가 된다.

이를 나머지 원래 column의 모든 값들에 대해 전부 수행해준다.

그럼 새로 만들어진 DF는 각 행에 1이 하나있고 나머지는 다 0인 모습이 된다.

그 행 하나하나를 one-hot vector라고 칭한다.


참고로 선형 모델이 아닌 tree 계열 모델은 굳이 one-hot encoding을 안해줘도 된다. 물론 해도 상관은 없다. 하지만 선형 모델의 경우에는 반드시 categorical column을 one-hot vector들을 모아둔 DF로 one-hot encoding 해줘야한다.


DF 형태의 데이터를 np.array() 로 형변환 해주고 넣자.


9강 : Linear Regression (2) - 선형회귀 실습 (Boston House prices dataset)

feature selection

우선 하나의 feature만으로 모델을 학습시켜보자.

모델에 넘겨줄 데이터는 항상 2차원 이상의 행렬 형태여야한다. 즉 feature가 하나인 경우 vector말고 n행 1열 형태로 넘겨줘야한다.


random_state

random_seed 나 seed로도 표기된다.

x_train, x_test, y_train, y_test = model_selection.train_test_split(boston_X, boston_Y, test_size=0.3, random_state=0)

여기서 random_state 파라미터를 볼수 있다.

보통 랜덤은 코드를 실행할 때마다 값이 바뀌는데, 저렇게 random_state를 특정 번호로 값을 넘겨주면 그 상태값에 해당하는 패턴으로 랜덤값을 만들어 내어준다. 즉 랜덤은 랜덤인데 저 패턴값에 해당하는 패턴으로 나오는 고정된 하나의 랜덤값을 리턴하는 것이다.

즉 A 컴퓨터에서 저 코드를 실행하고 B 컴퓨터에서 저 코드를 실행해도 둘 다 쪼개진 결과 데이터가 서로 같다. 코드를 여러번 실행해도 동일한 랜덤 값이 나온다.

이는 특정 실험의 재현 가능성과 밀접한 관련이 있는 부분이다.


이게 가능한 이유는, 사실 프로그래밍 언어에서는 진짜 랜덤한 값을 리턴하는게 아니라 난수 생성기가 특정한 패턴에 따라 값을 만들어주는걸 리턴하는 것이다.

즉 이 패턴을 지정해주면 어떤 하나의 고정된 랜덤값을 상시 얻어낼 수 있는 것이다.


model.fit(x_train, y_train) 이후

model.coef_ : 학습된 계수(가중치) 리턴 (Θ\Theta)

model.intercept_ : 보정치(y절편) 리턴


학습이 끝난 모델 성능 테스트

from sklearn.metrics import mean_squared_error

mean_squared_error(model.predict(x_train), y_train)

MSE 값 확인해볼 수 있음

overfitting인지를 확인하는 방법이 따로 있다. MSE값 추이를 보고, 값 자체의 scale도 보고 판단하면 됨


RMSE로 대충 얼마나 틀리고 있는지 객관적인 수치로 판단 가능


모델 시각화

plt.figure(figsize=(10, 10))

plt.scatter(x_test, y_test, color="black") # Test data
plt.scatter(x_train, y_train, color="red", s=1) # Train data

plt.plot(x_test, model.predict(x_test), color="blue", linewidth=3)

plt.show()

10강 : Linear Regression (3) - 선형회귀 실습 (Diabetes dataset)

dir 함수 코딩테스트칠 때 애용하자.


선형 회귀 모델이 분석이 아주 쉽지만, 성능 자체는 많이 떨어지는 편이라 다른 좋은 모델을 추후에 학습하고 활용하도록 하자.


다른 모델들 쓸 때도 지금 선형 회귀에서 배운 큰 틀과 거의 비슷함. 모델명만 바꿔넣어서 잘 쓰자


11강 : Logistic Regression (1) - Sigmoid function & Cutoff

Logistic Regression

: 이진 분류 (binary classification) 문제를 해결하기 위한 모델

분석은 용이하지만 성능은 아쉬움

용도 : ex) 스팸 메일 분류, 질병 양성/음성 분류 등

클래스가 여러개인 다항 로지스틱 회귀와, 서수 로지스틱 회귀란 것도 있긴 함


sigmoid function

sin이랑 비슷한 모양인듯

11+eΘTx\frac{1}{1+e^{-\Theta^{Tx}}} = 11+eΘ1X+Θ0\frac{1}{1+e^{\Theta_1X + \Theta_0}}

선형 회귀와 달리 추후 다른 모습의 데이터가 들어와도 학습한 모델이 이를 잘 커버할 수 있어서 괜찮음

결과값을 0에서 1 사이의 클래스 1일 확률값으로 매핑함. -> .predict_proba(x) -> ex)[0.32, 0.68]

또는 확률값말고 그냥 특정 Cutoff를 넘기면 1, 아니면 0으로, 확률값말고 클래스 분류 결과만 리턴하도록 할 수도 있음. (predict(x))

cutoff의 default value : 0.5

확률값같은 numerical value로 y값이 나오기 때문에 regression이라는 용어가 쓰이는데 분류 모델이니까 헷갈리지말자.


12강 : Logistic Regression (2) - Cross-entropy & Softmax

Cross-entropy

MSE를 성능 지표로 로지스틱 회귀를 평가하면 그래프에 변곡점이 굉장히 많아서 경사하강법으로 global minima, 혹은 그것에 근접한 local minima에 도달하기가 굉장히 어려워짐. 함수식에 지수가 포함되기 때문에 그래프가 복잡해져서 그럼.

따라서 logistic regression의 경우 corss-entropy를 성능 지표 함수로 삼는다.

J(Θ\Theta) = iy(i)log(hΘ(x(i)))-\sum_{i}y^{(i)}log(h_\Theta(x^{(i)}))


ex) 모델의 예측값 0.3 0.3 0.4 / 실제 정답 0, 0, 1 (one-hot label)

딥러닝의 경우를 에르 들면, Integer 형태의 클래스 번호를 값으로 갖던 정답 데이터를 one-hot encoding하여 one-hot label로 우선 바꿔줌

이 경우에 cross-entropy를 적용하면 각 클래스에 대응되는 값끼리 곱하고 더한다.

ln(0.3)x0 + ln(0.3)x0 + ln(0.4)x1 = ln(0.4)

이를 모든 x데이터에 대해 수행해서 그 값을 구하고, 그걸 또 모두 더한 후 마이너스를 곱 해주고 데이터 개수로 나눠주면 그게 성능 지표값이다.(양수임)

softmax function

식 자체는 간단하다.

S(yiy_i) = eyijeyj\frac{e^{y_i}}{\sum_je^{y_j}}

예측 vector의 모든 값을 더하고 그걸 각 값마다 나눠줘서 0~1 사이의 확률값으로 매핑해준다.

딥러닝의 경우 예측값 score가 확률값이 아닌 그냥 정수로 나오는데, 이를 softmax function을 통해 모든 cell의 값의 합이 1이 되도록 데이터 하나하나를 0~1 사이의 확률값으로 매핑해주고 이를 대상으로 one-hot label과 함께 cross-entropy를 계산한다.

이 softmax function을 활용하면 multiple class 문제를 풀 수 있다. 여러 클래스에 대한 확률값을 리턴해주기 때문

softmax와 cross-entropy를 묶어서 이름을 기억해두자. 딥러닝 때 중요함.


13강 : Logisitc Regression (3) - 로지스틱 회귀 실습 & Confusion matrix

리스트 인덱싱에서 불연속적으로 여러 열 지정하기

arr[ : , (5, 7)] -> 모든행, 5열과 7열


분류 모델의 정확도 분석 (성능 지표)

from sklearn.metrics import accuracy_score # accuracy

print('Accuracy: ', accuracy_score(model.predict(x_test), y_test))

모델의 정확도를 계산하는 방법 중 하나로 Confusion matrix를 활용하는 방법이 있다.

TP : True Positive, 예측 모델은 positive를 예측, 실제 결과도 positive로 일치하는 경우
TN : True Nagative, 예측 결과, 실제 결과 둘다 Nagative인 경우
FP : False Positive, 예측 모델은 Positive를 예측, 그러나 실제 결과는 nagative로 불일치하는 경우
FN : False Nagative, 예측 모델은 Nagative를 예측, 그러나 실제 결과는 positive로 불일치하는 경우

accuracy는 (TP + TN) / (TP + TN + FP + FN) 으로 계산된다.

다만 이 경우에는 한계점이 존재하긴 한다.

FP와 FN을 구분지어 분석할 수 없다는 것이다.


FP와 FN을 구분하여 분석해야 할 필요가 있는 경우는 다음과 같다.

1) 암 환자 예측의 경우, 실제로 암에 걸린 환자인데 예측 모델이 안 걸렸다고 예측을 해버리면 중대한 문제가 되버린다. 이런 경우 FN 카운팅을 알아내어 이를 최대한 줄이는 방향으로 모델을 개선해야한다.

2) 스팸 메일 분류의 경우, 스팸 메일이 아닌데 스팸 메일로 예측을 해버리면 중요한 문서였을 경우 그게 날라가버리는 문제가 생긴다. 즉, FP 카운팅을 알아내어 이를 최대한 줄이는 방향으로 모델을 개선해야한다.


recall, precision

위와 같은 confusion matrix가 할 수 없는 부분을 분석 가능케하는게 recall과 precision이다.

recall은 암 환자의 경우에 쓰면 좋다. 재현율로 번역되는데, 쉽게 기억하려면 실제로 positive인 것 중에 예측으로 positive를 얼마나 재현했는지에 대한 비율로 생각하자.

즉 recall = TP / (TP + FN) 이다.

precision은 스팸 메일의 경우에 쓰면 좋다. 정밀도로 번역되는데, 쉽게 기억하려면 사격에서의 정밀도를 생각하자. 사격은 일단 쏘고 표적지를 회수해서 정밀도를 분석하는데, 이처럼 일단 positive로 예측하고, 그 중에서 실제 결과도 positive로 적중한 비율이 어느정도인지를 분석하는 것이다.

즉 precision = TP / (TP + FP) 이다.


F1-Score

recall, precision 중 어느 것으로 분석을 해야 더 좋을지 판단이 애매한 상황이 있을 수 있다. 그럴 때는 둘 다 고려한 F1-Score를 사용하자.

recall 분석 값을 R, precision 분석 값을 P라고 하면 이 둘의 조화평균을 값으로 삼는다.

F1-Score = 2RP / (R + P) 이다.


F-beta score

만약 recall, precision를 어느 것을 어느 정도의 비중을 두고 둘 다 포함해서 분석하고 싶다면 F-beta score를 사용하자.

F1-Score도 사실 F-beta score 중에서 recall, precision에 비중을 1:1로 동등하게 둔 케이스이다.


14강 : Logistic Regression (4) - ROC Curve & AUC

cut off

= threshold, decision boundary

높일 수록 클래스 1을 판단하는 기준이 엄격해졌다고 표현.
낮출 수록 클래스 1을 판단하는 기준이 너그러워졌다고 표현.


ROC Curve

: Receiver Operating Characteristic Curve (수신자 조작 특성 곡선)

cost function은 아니고, 성능 지표임

그래프

암 환자 분류 모델을 예로 들어보자.

x축 : 위양성율 (실제 양성이 아닌 환자를 양성이라고 예측해낸 비율)
y축 : 진양성율 (실제 양성인 환자를 양성이라고 잘 예측해낸 비율)

일반적으로 위양성율을 높일수록 진양성율이 높아진다. 암이 아닌데 암이라고 진단하는 비율을 좀 높게 용인할수록 실제 암 환자를 정확하게 진단해내는 비율도 높아지는 것이다.

이를 그래프로 그려보면 왼쪽 위 방향으로 볼록한 포물선 형태가 나오는데, 포물선의 꼭짓점이 왼쪽 위에 가까울수록 좋다. (왼쪽 위는 위양성율이 0, 진양성율이 1인 이상적인 판정 형태)

모델들에 대해 이 그래프를 그려보고 더 나은 모델을 선택해야하는데, 비슷해서 애매한 경우가 있다. 이럴 때는 포물선 아래의 면적을 비교해서 선택하는데, 그 면적을 AOC라고 한다.


AUC

: Area Under the ROC Curve

AUC가 1이면 perfect model, 0.5이면 그래프가 직선 형태로 나타나는데, 반반 찍어서 맞춘다는 걸 의미한다. 즉 이보다 낮은 모델은 성능에 의미가 없다.

보통 0.79만 넘어도 실용적으로 쓸만한 모델이고, 성능 좋기로 유명한 모델은 대부분 0.9x 정도이다.


from sklearn.metrics import roc_curve, auc

fpr, tpr, _ = roc_curve(y_true=y_test, y_score=pred_test[:,1]) # real y & predicted y (based on "Sepal width")
roc_auc = auc(fpr, tpr) # AUC 면적의 값 (수치)
roc_auc

roc_curve에서 y_true는 실제 클래스 값, y_score에는 sigmoid function을 거쳐 모델이 뱉어낸 예측 결과에서 클래스 1일 확률만 따로 모아서 넘겨주면 된다.

fpr은 ROC Curve 그래프에서 꺾이는 지점의 x좌표, tpr은 y좌표 리스트이다.

이 둘을 auc 함수에 넘겨주면 모델의 AUC 값을 구할 수 있다.

AUC 개념을 accuracy와 헷갈리지 않도록 주의하자.


15강 : Adaboost & Gradient Boosting & XGBoost

XG Boost

: extreme gradient boost

지도학습 중 decision tree에 boosting 기법을 적용한 알고리즘

회귀, 분류 둘 다 가능함

의사 결정 나무에 extreme, gradient, boost를 적용시켜 성능을 강화한다.


Decision Tree

트리 형태로 어떤 분기 조건을 걸고 그걸로 계속 분기해나가는 모양임.

basic한 의사결정나무는 데이터의 작은 변동에도 트리 구조가 크게 바뀔 수 있어 안정성이 떨어지고, 과적합이 쉽게 발생하여 basic한 의사결정나무만으로는 좋은 성능을 기대하긴 어렵다.


1) AdaBoost

: adaptive boost

의사 결정 나무에 boosting 기법을 적용시킨다.

model ensemble 기법 중 하나인데, 여러 예측 모델을 생성 후, 각 모델의 예측 결과를 수합하여 다수결로 최종 결과를 뽑아서 취한다. (회귀일땐 평균)

이 때 각 모델의 decision tree의 depth는 너무 깊지않게 적당히만 학습시킨다. (overfitting 방지)


우선 weak learner를 하나 만든다. 정확도가 한 70%? 정도로 약하다.

이 모델에서 예측 결과가 틀린 데이터에 가중치를 곱해주어 다시 학습하는 2번 weak learner를 만든다. 같은 과정으로 3번을 또 만들고 이런 식으로 전의 결과를 보충해주는 방향으로 weak learner 여러개를 순차적으로 만들어낸다.

이렇게 만든 여러개의 weak learner 모델들의 결과를 수합하여 회귀의 경우 평균, 분류의 경우 투표를 해서 최종 결과를 낸다. (strong learner)


Random Forest (Bagging 적용)

AdaBoost와 유사하다.

Bagging : bootstrap aggregating

AdaBoost는 여러 weak learner를 만들 때 직전 모델의 결점을 보완하는 방향으로 순차적으로 모델을 만들었다면, random forest는 x-data에 해당하는 feature를 랜덤으로 여러 쌍 골라서, 그 각각의 쌍으로 각각의 모델을 만드는 알고리즘이다.

따라서 여러 모델들을 병렬적으로 학습할 수 있어 시간이 좀 더 빠르지만, 성능은 일반적으로 Boost보다는 딸리는 편이다.

다만 실제로 이런 모델들을 사용할때는, 내가 가지고 있는 data로 boost 기법 모델도 써보고, bagging 모델도 써보면서 성능을 직접 비교해서 선택하자. 물론 boost류 모델이 성능이 대체로 더 나은 것도 팩트긴 하다.


Gradient Descent

AdaBoost에서 틀린 데이터에 가중치를 1 이상의 랜덤한 값으로 곱해주는데, 높은 weight를 가진 data point가 존재하는 시점에 성능이 크게 떨어지는 단점이 있다.

따라서 error를 최소화하는 방향으로, 곱해줄 weight의 크기를 결정해야할 필요가 있다.

바로 여기서, weight를 대상으로 gradient descent를 적용하여 error가 가장 낮은 지점의 weight를 찾는다.


XG Boost (Extreme Gradient Boosting)

경사 하강법까지 적용하면 학습 성능은 꽤 괜찮아지지만, decision tree를 구성함에 있어 많은 학습 시간이 걸린다는 단점이 여전히 존재한다. 여기에 경사 하강법까지 적용하면 더 오래 걸린다.

이를 해결하는게 extreme gradient boosting이다.

의사 결정 나무를 구성할 때 원래는 여러 가지 조건(x-data에서의 열에 해당)에 대해, 일단 루트 노드로부터의 첫 번째 분기 조건을 결정할 때 feature를 다 갖다놔보고, 가장 데이터가 극명하게 잘 분기되는 feature를 찾는 식으로 트리를 구성한다.

근데 feature가 수천 수만개라면 너무 오래 걸리는 작업이다.

따라서 이걸 병렬적으로 작업하는게 XG Boost이다.


이 알고리즘도 단점이 없는 건 아니다. 하이퍼 파라미터가 수십개나 돼서 정하는게 되게 어렵다. 일반적으로는 사람들이 추천하는 적당한 하이퍼 파라미터 값을 넘겨주고 성능이 잘 나오길 기도하는 수밖에..

이런 부분 때문에 gradient boosting도 아직 현역으로 많이 쓰이고 있다.


2018년에 Light GBM (light gradient boosting model) 이 나옴.


이런 애들을 통틀어 Tree-based Ensemble Models 라고 칭함

현재로서는 gradient boosting이 이 분야에서 top급 모델이라고 보면 됨

엑셀같은 정형 데이터 대상으로 성능이 잘 나와줌. 물론 비정형 데이터도 가능은 하지만 정형 데이터 쪽으로는 머신러닝이 훨씬 성능이 좋게 나옴.


16강 : Gradient Boosting (1) - 회귀분석 적용 & Feature Importance

  • 모델 쓸 때 구글링해서 코드 보고 훑어보면서 해석해보고 복붙해서 쓰자

featureimportances

  • gradient boosting 모델 등의 decision tree 기반 모델, 그리고 몇몇 모델이 갖고 있는 featureimportances 메소드를 시각화하면 어떤 feature가 예측에 어느정도의 영향력 지분을 차지하는지 알 수 있다. 중요한 기능이니까 숙지하자

17강 : Gradient Boosting (2) - 회귀분석 적용 & Permutation Importance

permutation_importance

feature importance보다 신뢰도가 더 높아서 가능하면 이걸로 영향력이 가장 높은 feature를 판단해보자. 근데 모델 중에 이걸 못 그려내는 모델이 있어서, 그럴 때는 feature importance를 참고하자.

해당 알고리즘의 원리는, 특정 feature를 제외하고 모델을 돌려서 얼마나 정확도가 떨어지는지를 보고 그 feature의 중요도를 판단하는 것이다.

다만 그렇게하면 시간이 너무 오래걸리기에 실제로는 feature의 value들을 무작위로 섞어서 그 값이 x_data(행)과 연관이 아예 없도록 만들면 그 feature는 없는거나 마찬가지라고 할 수 있다.


이 메소드의 리턴 array의 각 cell의 값은 모델의 score이다. 모델의 scorer 또는 임의로 지정해준 scorer가 x_data, y_data를 받아 내부적으로 어떤 값을 리턴해주는데, 이 값은 1에 가까울수록 좋은 모델, 0에 가까울수록 나쁜 모델, 음수면 쓰레기 모델로 판단하면 된다.

리턴값은 모델에 따라 다른데, 회귀 모델일 때는 기본 scorer가 R2 score, 분류 모델일 때는 Accuracy score를 리턴한다. 둘 다 1에 가까울수록 좋은 모델이라는 점은 똑같다.

permutation_importance의 cell 값은 원래 feature일 때의 score(metric)과 feature에 노이즈를 만들었을 때(무작위로 섞기)의 score(metric)의 차이이다. 즉 이 차이가 클수록 그 feature가 모델의 정확도에 중요한 역할을 한다는 것을 의미한다.


수염상자그림은 각 feature를 n_repeats 번 무작위로 섞어 모델 학습 및 예측을 했을 때의 그 n_repeats개의 permutation_importance(baseline_metric - metric from 열 섞은거) 값의 quanter 분포를 나타낸 것이다.

18강 : Gradient Boosting (3) - 분류분석 적용 & Classification Report

회귀 또는 분류 모델의 default score 함수를 쓰기보다는 sklearn.metrics에서 직접 꺼내서 쓰자. 그래야 뭘 쓰는지 명확함


19강 : SVM (1) - Soft-margin Kernelized SVM

SVM (Support Vector Machine)

지도 학습 모델의 일종으로, 회귀와 분류 모델 둘 다 있음

SVM의 목적은 여유 있게 데이터를 가르는 decision boundary를 찾는 것.

여기서 여유 있게 데이터를 가른다는 것은, 만약 어떤 실선이 두 클래스를 구분짓고있을 때 그 실선과 분류된 데이터 사이의 여유 공간이 최대한 넓도록 선을 잡는다는 뜻임.

decision boundary에 첨예하게 붙어있는 데이터를 support vector라고 함. vector는 한 좌표의 순서쌍이라고 생각하자.


클래스 0과 1이 있다고 칠 때, support vector를 지나는 직선 두 개를 각각 plus-plane, minus-plane이라고 하는데 이 둘 사이의 거리 margin을 최대화하는 decision boundary를 찾는 기법이 바로 SVM이다.


Soft-margin SVM

현실 세계에서는 데이터가 정확하게 군집화되어있지 않고, 서로 다른 클래스끼리 서로의 영역에 들어가있는 이상치들이 존재할 가능성이 높다.

따라서 Hard Margin SVM 방식이라면 다른 영역에 넘어가 있는 이상치까지 고려해서 margin을 설정하기 때문에 굉장히 좁아지거나 아예 설정 불가능한 경우가 생기는데,

Soft Margin 방식은 이상치를 어느 정도 허용해서 선을 긋기 때문에 추후 들어올 데이터에 대해 안전하게 분류할 가능성을 높혀 robustness를 높힐 수 있게 된다.

minus plane과 plus plane는 직선의 방정식을 예로 들면 wx + b = 1 or -1 꼴로 나타나는데, 이 사이의 거리는 w놈/2로 나타난다. 이를 최대화하는게 목표, 즉 w놈을 최소화하는게 목적함수가 된다. 목적함수를 살펴보면

(1/2)(w놈)^2 + Cx시그마(크사이 합) 으로 나타는데,

w놈 식은 미분의 편의를 위해 제곱을 해놨고, 크사이는 이상치 편차 정도를 의미한다. C는 하이퍼 파라미터로서 이상치 허용 범위를 조정할 수 있는데, 저 값이 클수록 모델이 이상치를 의식하여 더 엄격하게 이상치를 고려하여 margin을 최대화하고, 값이 작을수록 이상치를 무시하면서 margin을 구한다.


Kernel SVM

선형 분리 불가능 (linearly unseperable)

어떤 데이터를 선 하나를 그어서 클래스 분류를 해내지 못하는 경우도 있다.

한 쪽에도 클래스 0, 클래스 1 뭉탱이가 다량 있고, 다른 반대편쪽에도 클래스 0, 클래스 1 뭉태이가 다량 있는게 그 예 중 하나이다.

이를 해결하기 위해, 비선형 매핑을 한다.

feature가 x1, x2 두 개 있다고 치자. 그럼 이걸 kernel Function을 적용해 (x1, x2, x2^2)로 만들자. 이 때 kernel Function은 하이퍼 파라미터로 직접 정하는것이다. 실용적으로 자주 쓰이는 함수로는 다항 커널, 가우시안 커널 함수 등이 있다.

암튼 이렇게 커널 함수를 통해 고차원 공간으로 데이터를 매핑해서, 그 공간에서 데이터를 분류하는 plane같은 것을 그릴 수 있다면, 그걸 다시 원래의 차원으로 들고와 그려준다.

예시에서는 2차원을 3차원으로 옮겨가 plane을 그리는 것이니 그걸 다시 2차원에서 그릴 때는 어떤 곡선 형태로 나타날 것이다. (non-linear decision boundary)

kernel function 원리는 딥러닝에서 layer의 값에 대해 세타를 곱해 다음 layer의 값을 만들 때도 이용된다. 다만 이 때의 세타는 하이퍼파라미터가 아닌, 모델이 오류를 최소화하는 방향으로 직접 구한다. 즉, SVM에서의 경우와 달리 Kernel Fucntion을 컴퓨터가 직접 찾아서 적용해주는 것이다.

참고로 Kernel Function 원리를 Feature Crosses라고 지칭하기도 한다.

이런 의미에서 Layer를 Learnable Kernel 이라고도 부른다.


20강 : SVM (2) - SVM's Hyper-parameters

gamma

SVM의 하이퍼 파라미터 중 하나이다.

가우시안 그래프에서 가로 방향 단면 원의 반지름의 역수가 감마이다.

gamma가 커질수록 단면 원의 반지름이 작아지고, 이 원은 SVM 그래프에서 군집끼리 묶는 decision boundary라고 생각하자. 즉 gamma가 많이 커질수록 decision boundary의 크기가 작아져서 overfitting의 위험성이 높아진다.


<요약>

C와 gamma가 커질수록 overfitting 확률이 커진다.


21강 : SVM (3) - Feature Normalization & StandardScaler

Feature Normalization (Scaling)

tree 기반 모델의 경우에는 상관없는데 Kernelized SVM에서 feature들의 scale이 차이가 많이 난다면 overfitting이 심하게 난다.

범주형 열의 경우 pipeline으로 one-hot encoding해주고, 수치형 열의 경우 min-max normalization이나 standardization으로 scale 맞춰주기


중요한 점은,

test data의 feature들도 scaling을 할텐데, 이 때 scaling에 쓰이는 feature의 min, max, mean, std 값 정보는 반드시 training data의 feature에 대해서만 구한 후 scaling을 해야한다.

즉, training data의 feature를 대상으로 min, max, mean, std값을 구한 후, 그걸로 training data feature와 그에 대응되는 test data feature를 scaling해주는 것이다.

scaling에 사용되는 저 값 정보들에 절대로 test data가 관여되면 안된다. 헷갈릴 수 있으니 잘 숙지해두자!

요약 : data split 후 training data만 사용하여 scaler 객체 fit해놓은 후 그걸로 training data와 test data scaling 하기


당연히 추후 들어오는 데이터도 똑같이 scaling 후 모델에 넣어줘야함

이 때 데이터가 딱 하나더라도 반드시 2차원으로 넘겨주기


StandardScaler or MinMaxScaler

from sklearn.preprocessing import StandardScaler  # (sklearn.preprocessing.MinMaxScaler is also available)

sc = StandardScaler()
sc.fit(X_train) # X_train 의 평균과 표준편차를 구함

# As with all the transformations, it is important to fit the scalers to the training data only, not to the full dataset (including the test set).
X_train_scaled = sc.transform(X_train)
X_test_scaled = sc.transform(X_test)

df = pd.DataFrame(X_train_scaled)
df.describe()

MinMaxScaler도 있음


22강 : SVM (4) - HPO & GridSearchCV

HPO

= Hyper-Parameter Optimization
= Hyper-Parameter Tuning
= Model Tuning

최적의 하이퍼 파라미터를 찾아주는 라이브러리가 몇 가지 있다.

1) Grid-Search HPO : 널리 쓰이고 성능도 잘 나오는 HPO 라이브러리. dict로 HP 값 후보군들을 넘겨주면 가능한 모든 경우의 수를 돌리면서 최적의 쌍을 찾아줌

2) Randomized-Search HPO : 분포를 넘겨주면 그걸 대상으로 최적의 쌍을 찾아줌

3) Bayesian-Search HPO : 베이즈 통계학을 기반으로 한 라이브러리


Grid-search HPO

from sklearn.model_selection import GridSearchCV

# 아래 param_grid dict 의 C & gamma 에 후보 Hyper-params 값들을 리스트업합니다.
param_grid = {'C' : [0.1, 1, 10, 100, 1000], 
             'gamma' : [1, 0.1, 0.01, 0.001, 0.0001],
             'kernel' : ['rbf']}

grid = GridSearchCV(SVC(), param_grid, refit=True, verbose=1)
# refit : 찾아진 가장 좋은 params로 estimator를 setting할 지 여부 (setting해줘야 곧바로 predict가 가능)
# verbose : 설명의 자세한 정도 (verbose를 3과 같이 바꿔보시면 더 자세하게 매 param set 마다의 결과를 확인할 수 있습니다.)

grid.fit(X_train_scaled, y_train)
print('The best parameters are ', grid.best_params_)

뒤에 CV가 붙는 이유는, 후보군 각각의 쌍에 대해 한번만 실행하는게 아니라 여러번 실행하면서 cross validation을 진행하여 평균을 낸다든지 해서 결과에 신빙성을 줌

함수의 첫 번째 매개변수로 estimator를 넘기는데, 이 때 함수 괄호안에서 바로 객체 선언을 해서 넘겨줄 때, 생성자 식의 괄호안에서 인자를 넘겨주거나 하면 안됨.

모델의 매개변수로 넘겨줄게 있다면 dict에 추가 후 두 번째 매개변수로 넘겨주자.

함수의 반환값은 모델임. grid 변수에 저장되는 것은 SVC이고(SVM classifier), refit 인자를 True로 넘겨줘야 하이퍼 파라미터를 최적으로 재적용한 SVC 모델이 grid에 저장됨.

이후에는 grid에 모델이 담겨있으니 원래 하던 것처럼 training data를 fit하고 predict하면 된다.


23강 : K-Nearest Neighbor Algorithm

KNN (K-Nearest Neighbor Algorithm)

: K 값만큼 가장 가까운 거리에 있는 데이터를 고르고, 가장 많은 수의 데이터 class로 현재 데이터의 class를 정하는 알고리즘

K 값이 작을수록 overfitting 확률이 높아진다.

model = neighbors.KNeighborsClassifier(Hyper-Parameters)

성능을 기대하긴 어려우나 원리가 쉽고 직관적이다.


24강 : Clustering & K-Means Algorithm

K-Means Algorithm

1) K개의 임의의 중심값 고르기 (일반적으로 데이터 샘플 중 하나를 선택)

2) 각 데이터마다 중심값까지의 거리들 중 가장 가까운 중심값의 클러스터에 해당 데이터를 할당시킨다.

3) 각 클러스터에 속한 데이터들의 평균값으로 각 중심값을 갱신한다.

4) 데이터에 대한 클러스터 할당이 변하지 않을 때까지 2~3번을 반복한다.


한계점 : 연산량이 많아서 오래 걸린다.


from sklearn import cluster
import numpy as np

kmeans = cluster.KMeans(n_clusters=2, random_state=0).fit(X)

print("Clusters : ", kmeans.labels_)

print("Cluster centroids: ", kmeans.cluster_centers_)

print("Prediction cluster of [0, 0], [8, 4]: ", (kmeans.predict([[0, 0], [8, 4]]))) 

kMeans의 init 매개변수를 k-means++로 기본값으로 지정되어있다.

초기 중심값을 완전 랜덤이 아닌, 데이터 중에서 랜덤으로 지정해서 중심값으로 삼으므로 안전성을 높혀준다.


25강 : 클러스터 수 결정 기법 - Elbow method & Silhouette score

엘보우(elbow) 기법

def elbow(X):
    total_distance = []
    for i in range(1, 11):
        model = cluster.KMeans(n_clusters=i, random_state=0)
        model.fit(X)
        
        # inertia : Sum of squared distances of samples to their closest cluster center.
        total_distance.append(model.inertia_) 
        
    plt.plot(range(1, 11), total_distance, marker='o')
    plt.xlabel('# of clusters')
    plt.ylabel('Total distance (SSE)')
    plt.show()

elbow(X) # Iris case : 2

K를 늘려가면서, 각 K에 대한 KMeans 모델에 대해 SSE 값을 수합한다.

SSE : Sum of Squared Errors, 각 샘플이 속한 클러스터의 중심값까지의 거리들을 각각 제곱 후 다 더한 값

SSE(X) 그래프 추이를 보면 X가 커질수록 SSE값이 점점 작아지는데, 급격하게 줄어드는 부분이 있다. 바로 그 부분이 팔꿈치처럼 생겼는데 그 부분이 최적의 클러스터 개수가 된다.

이 기법은 직관적이고 이해 및 사용이 쉽지만 한계점으로는 클러스터끼리의 거리가 어느정도인지는 반영하지 못한다는 것이다.

따라서 보다 발전된 실루엣 기법이 있다.


실루엣(Silhouette) 기법

모든 데이터들에 대해 실루엣 계수를 구하고, 그 것의 평균을 구한다. 이 평균 실루엣 계수가 0.7에 가깝거나 그 이상이면 그 때의 K는 최적이라고 할 수 있다.

실루엣 계수 s(i)s^{(i)} = b(i)a(i)max{a(i),b(i)}\frac{b^{(i)} - a^{(i)}}{max\{a^{(i)}, b^{(i)}\}}

a(i)a^{(i)} : 클러스터 내 데이터 응집도(cohesion) == 데이터 x(i)x^{(i)}가 속한 클러스터 내에서 x(i)와 나머지 데이터들과의 거리의 평균 값

b(i)b^{(i)} : 클러스터 간 분리도(separation) == 데이터 x(i)x^{(i)}와 가장 가까운 외부 클러스터 내의 모든 데이터들과의 거리의 평균 값

가장 이상적인 클러스터링의 경우에 b(i)b^{(i)}은 커지고, a(i)a^{(i)}는 클러스터 내의 모든 데이터가 한 점에 모여있으므로 0이 된다. 즉 각각의 실루엣 계수가 1이고, 모든 실루엣 계수를 평균낸 값도 1이 된다.

요약하면, 클러스터의 개수가 최적화되어 있으면 평균 실루엣 계수는 1에 가까운 값이 된다.


sklearn.metrics의 silhouette_samples : 데이터 하나의 실루엣 계수 구하는 함수

sklearn.metrics의 silhouette_score : 평균 실루엣 계수

from sklearn.metrics import silhouette_score

model = cluster.KMeans(n_clusters=2) # Change the number of clusters
y_fitted = model.fit_predict(X)
silhouette_avg = silhouette_score(X, y_fitted)
print("The average of silhouette coefficients is :", silhouette_avg)

이 값이 0.7에 가깝거나 0.7 이상이면 그 때의 K가 최적이라고 할만하다.


26강 : Dimensionality Reduction & PCA

PCA

: Principal Component Analysis
== 주성분 분석
== 차원 축소를 통해 최소 차원의 정보로 원래 차원의 정보를 모사(approximate)하는 알고리즘

모델이라기보다는 데이터 전처리기라고 보는게 맞다.


1) 분산이 가장 큰 축 하나를 찾는다. (데이터의 끝과 끝 거리가 가장 긴 선을 찾고, 그걸 하나의 새로운 Principal Component 축으로 삼는다.

2) 그 축과 직교하는 방향에서, 또 다시 분산이 높은 방향으로 축을 찾는다. (회전 변환, rotation transform)

3) 이런 식으로 축을 다 찾고, 그 축들만으로 이루어진 공간 component space에 원래 데이터들을 projection해서 나타낸다.


PC 축은 원본 dimension 개수까지 세울 수 있다. 물론 차원 축소의 의의가 차원을 줄이는 것이기에 당연히 더 적게 쓰게 될 것이다.

보통은 1 ~ 원본dimension-1 까지로 줄임


fit까진 똑같고, predict 대신 transform이라는 함수로 차원 축소된 x 데이터를 리턴받을 수 있음.


PC를 몇개를 꺼내든, 1차원 PC, 2차원 PC 등등은 모두 고유하게 하나로 정해져있다. 즉, PCA에서 n_components를 1로 설정하든 3으로 설정하든 두 경우 모두 1차원 PC의 값들은 변함없이 같다.


그렇다면 몇 개의 PC면 충분할까?

각 축 별로 원본 데이터의 분산을 어느정도까지 표현가능한지 확인할 수 있는 지표 함수가 있다.

: model.explainedvariance_ratio

보통 축들의 explain variance의 합이 0.9가 넘으면 충분히 신빙성 있고 정확도 높다고 본다.

좀 더 엄밀하게 하고싶다면 95%를 넘도록 PC 개수를 설정하면 된다. 일반적으로 95% 이상으로 한다.


위에서 봤듯이 차원 축소를 실행하면 반드시 원본 데이터 대비 손실이 발생한다.

즉 굳이 차원 축소를 해야되는 경우가 아니라면 안하는게 낫다.

애초에 PCA 자체가 성능 향상이 목적이 아니고, 모델의 학습 속도를 향상시키기 위해 쓰는 알고리즘이다. 보통 feature 개수가 너무 많아서 학습 속도가 오래 걸리는 경우에 쓴다.


몇 개의 축까지가 적당할지를 코드로 자동화해서 알아보고 싶다면, PCA 매개변수의 n_components에 축 개수가 아닌, 최소로 보장받기를 원하는 분산 표현 가능 비율값을 1 미만의 값(float)으로 넣어주면 그거에 맞게 알아서 축 개수를 지정 및 축소하여 정제된 x 데이터를 리턴해준다.


27강 : Scikit-learn 실습 요약 & Choosing the right estimator

풀어내려는 문제의 종류와 데이터 타입에 따른 ML 알고리즘 선택 가이드가 있다. 이걸 참고해서 사용할 모델을 적절하게 선택해서 쓰자.

cheat sheet도 전체적인 요약본이니 모델들 import문이라던가 그런게 기억 안날 때 참고하자.


Appendix : 추가 학습 자료 (1) - missingno를 활용한 결측치 분포 시각화

missingno

결측치 분포를 시각화하여 한눈에 파악하기 쉬움

import missingno as msno

msno.matrix(df.sample(250)) # 결측치 분포

msno.bar(collisions.sample(1000)) # 결측치량 비율

df.sample(n) : n개의 행을 무작위로 선택


Appendix : 추가 학습 자료 (2) - Auto-Sklearn을 활용한 AutoML

X, y = datasets.load_breast_cancer(return_X_y=True)

return_X_y를 True로 두면 바로 data와 target을 리턴해줌

Auto-Sklearn은 아직 성능이나 안전성면에서 조금 부족한 면이 많음. 그냥 AutoML이 이런 느낌이구나 정도를 느끼는 용도로만 배우자

model = autosklearn.classification.AutoSklearnClassifier(time_left_for_this_task=600) # ML model 찾는데 소요할 시간. 10분 정도만 돌려도 그럭저럭 쓸만함

model.fit(X_train, y_train) # Feature Scaling 불필요

prediction = model.predict(X_test) 

대체로 직접 최적의 모델을 판단해서 사용자가 적용한 모델의 정확도에 비해서는 AutoML이 좀 떨어진다. 데이터 개수가 많은 경우에는 시간이 엄청 오래걸린다.

따라서 AutoML은 시간이 없거나 가벼운 모델을 만들어서 쓸 때, 서브 도구로서 사용하는게 바람직하다.


Appendix : 추가 학습 자료 (3) - IQR 기반 Outlier 탐지 및 제거

class-imbalance

신용카드 데이터셋 예시를 보면,

class 0 : 정상적인 신용카드 거래 데이터
class 1 : 사기 신용카드 거래 데이터

전체 데이터셋 중 0.1715% 만이 class 1이다.

이처럼 보통 fraud detection & anomaly detection의 대상이 되는 데이터셋의 경우 class-imbalanced data인 경우가 많다.


전처리를 할지 안할지 판단 기준

data scaling, outlier handling 등 데이터를 전처리할 때는 했을 때와 안했을 때의 성능 변화를 보고 결정하면 된다. 오히려 성능이 떨어지는 경우도 있으니 반드시 실험적으로 접근하여 쓸지 말지를 판단하자. 직접 해보고 판단하면 되는거임.


Outlier 탐지 및 제거

모든 feature를 대상으로 이상치 검출 & 제거를 해버리면, 시간도 많이 소요되고 지나치게 많은 행이 삭제될 수 있으며, label과 상관관계가 낮은 feature의 경우 이상치 제거 후 모델 성능 향상 효과가 미미하다.

따라서 label 열과 가장 높은 상관관계를 갖고 있는 feature들을 위주로 outlier handling을 해준다.


1) df의 corr()를 시각화하여 상관관계 높은 feature 찾기 (class행과 나머지 feature들의 상관관계 Series로 인덱싱해서 꺼내어 sort_values로 파악)

plt.figure(figsize=(9, 9))
sns.heatmap(card_df.corr(), cmap='RdBu')

card_df.corr().loc['Class'].sort_values()

2) IQR 기법으로 이상치 탐지 및 제거

행을 제거하는 것 외에도 이상치 값을 직접 조정하는 방법도 있음. 본인 선택

아래 예시 코드에서는 class 1의 비율이 너무 적어서, 전체를 대상으로 outlier handling을 해버리면 너무 많이 줄어들어서 어쩔 수 없이 class 1만을 대상으로 이상치 탐지 및 제거를 실행했음.

outlier_index = []

for col in ['V14', 'V17']:
    
    # 전체 데이터프레임을 대상으로 outlier 제거 시, class 1 기준 행의 수가 492개에서 47개로 줄어들어버립니다.
    df_fraud = card_df[card_df['Class'] == 1] 
    
    quantile_25 = df_fraud[col].quantile(0.25)
    quantile_75 = df_fraud[col].quantile(0.75)
    iqr = quantile_75 - quantile_25

    outlier_df = df_fraud[(df_fraud[col] < quantile_25 - iqr * 1.5) | (df_fraud[col] > quantile_75 + iqr * 1.5) ]
    outlier_index = outlier_index + list(outlier_df.index)

card_df = card_df.drop(outlier_index)

Appendix : 추가 학습 자료 (4) - Class-imbalance & SMOTE 기반 Over-sampling

Over-sampling

class-imbalance가 심한 데이터셋을 활용할 때, 모델의 원활한 학습을 위해 적은 수의 데이터가 포함된 class의 데이터를 값을 살짝식 변경해 새로운 데이터를 생성하여 양을 늘려주는 기법이다.


SMOTE

: Synthetic Minority Over-sampling Technique

Over-sampling 기법 중 하나이다.

적은 수의 데이터가 포함된 class의 개별 데이터들에 대하여 K-Nearest-Neighbor들을 찾아, 원본과 이웃들의 차이를 일정 값으로 만들어, 기존 데이터와 약간씩 차이나는 새로운 데이터를 만든다.

SMOTE를 적용할 때는 반드시 학습 데이터셋만 오버 샘플링해야한다.


X_train, X_test, y_train, y_test = train_test_split(x_data, y_data, 
                                                    test_size=0.3, 
                                                    stratify=y_data)

stratify=y_data

: 전체에 대한 y_data의 비율이, train과 test로 쪼개고 나서도 그 각각에서 유지되도록 나눌 수 있다.


from imblearn.over_sampling import SMOTE

smote = SMOTE(random_state=42)

X_train_over, y_train_over = smote.fit_sample(X_train, y_train)

ROC 값을 바로 구하고싶다면 metrics.roc_auc_score 함수 활용


over-sampling을 하고 안하고의 차이를 볼 때 recall 값을 비교해보자.

recall : 실제로 True인 것 중에 모델이 True라고 맞춘 비율

일반적으로는 recall과 precision은 trade-off 관계가 있다. recall을 올리면 precision이 떨어진다.

precision : 모델이 True라고 예측한 것 중 실제로도 True인 비율


Appendix : 추가 학습 자료 (5) - Model Saving & Loading + Model Stacking

  • scikit-learn ML 관련 책의 대장격인 Hands On Machine Learning (PDF 파일 참고)

Model Saving & Loading

import joblib

joblib.dump(model, 'model_iris_svm_v1.pkl', compress=True) # 모델을 pkl 파일로 저장

model_loaded = joblib.load('model_iris_svm_v1.pkl') # 모델 불러오기

전처리기가 있다면 전처리기도 같이 저장해줘야함. (scaler 등)


vecstack

model ensemble 기법 중 하나인 model stacking 라이브러리다.

원리

전에 배웠던 앙상블 기법에서, 여러 모델들의 예측 결과 중에서 투표하는게 아닌, 그 결과를 또 다른 x_data로 삼아 다른 모델 하나를 가지고 또 y를 예측하는 모델을 만드는 방식이다.


1) 1st stage

x_train : (120, 4)
y_train : (120, 1)
x_test : (30, 4)
y_test : (30, 1)

1st stage 모델을 여러개 선정한다. 서로 다른 종류면 좋다. (트리 기반, 딥러닝, 회귀 등등..)

예를 들어 3개의 모델을 선정했다고 치자. (모델1, 2, 3)

x_train와 y_train로 3개의 모델을 모두 학습시킨다.


2) 3개의 모델에 x_train로 예측을 시키고, 그 결과를 수합한다.

s_train: 각 모델의 예측 벡터 (120, 1)을 수합하여 vector(120, 3)


3) 2nd stage 모델 학습

s_train(120, 3)과 y_train(120, 1)로 2nd stage의 모델 하나를 학습시킨다.


4) 최종 예측 및 성능 평가

s_test : x_test(30, 4)를 1st stage 모델에 넣어 vector(30, 3) 만들기

s_test를 2nd stage 모델에 넣어 예측하면 vector(30, 1)이 나오는데, 이걸 y_test와 비교하여 성능 검증


vecstack의 stacking 활용

from sklearn.datasets import load_iris 
from sklearn.model_selection import train_test_split 
from sklearn.metrics import accuracy_score 

from sklearn.ensemble import ExtraTreesClassifier 
from sklearn.ensemble import RandomForestClassifier 
from xgboost import XGBClassifier 

import warnings                 
warnings.filterwarnings('ignore')

from vecstack import stacking

iris = load_iris() 
X, y = iris.data, iris.target 

X_train, X_test, y_train, y_test = train_test_split(X, y, 
                                                    test_size = 0.2, random_state = 0)
                                                    
models = [ 
    ExtraTreesClassifier(random_state = 0, n_jobs = -1, n_estimators = 100, max_depth = 3), 
    RandomForestClassifier(random_state = 0, n_jobs = -1, n_estimators = 100, max_depth = 3), 
    XGBClassifier(seed = 0, n_jobs = -1, learning_rate = 0.1, n_estimators = 100, max_depth = 3)] 
    
S_train, S_test = stacking(models,
                           X_train, y_train, X_test, 
                           regression = False, 
                           metric = accuracy_score, 
                           n_folds = 4, stratified = True, shuffle = True, 
                           random_state = 0, verbose = 2)     
                           
model = XGBClassifier(seed = 0, n_jobs = -1, learning_rate = 0.1, n_estimators = 100, max_depth = 3, eval_metric='mlogloss') 

model = model.fit(S_train, y_train) 

y_pred = model.predict(S_test) 

print('Final prediction score: [%.8f]' % accuracy_score(y_test, y_pred))

vecstack의 StackingTransformer 활용

from sklearn.datasets import load_iris 
from sklearn.model_selection import train_test_split 
from sklearn.metrics import accuracy_score 

from sklearn.ensemble import ExtraTreesClassifier 
from sklearn.ensemble import RandomForestClassifier 
from xgboost import XGBClassifier 

import warnings                 
warnings.filterwarnings('ignore')

from vecstack import StackingTransformer

iris = load_iris() 
X, y = iris.data, iris.target 

X_train, X_test, y_train, y_test = train_test_split(X, y, 
                                                    test_size = 0.2, random_state = 0)
                                                    
estimators = [ 
    ('ExtraTrees', ExtraTreesClassifier(random_state = 0, n_jobs = -1, n_estimators = 100, max_depth = 3)),
    ('RandomForest', RandomForestClassifier(random_state = 0, n_jobs = -1, n_estimators = 100, max_depth = 3)),
    ('XGB', XGBClassifier(seed = 0, n_jobs = -1, learning_rate = 0.1, n_estimators = 100, max_depth = 3, eval_metric='mlogloss'))]
    
stack = StackingTransformer(estimators, 
                            regression = False, 
                            metric = accuracy_score, 
                            n_folds = 4, stratified = True, shuffle = True, 
                            random_state = 0, verbose = 2) 
                            
stack = stack.fit(X_train, y_train)

S_train = stack.transform(X_train)
S_test = stack.transform(X_test)

model = XGBClassifier(seed = 0, n_jobs = -1, learning_rate = 0.1, n_estimators = 100, max_depth = 3, eval_metric='mlogloss') 
model = model.fit(S_train, y_train) 

y_pred = model.predict(S_test) 
print('Final prediction score: [%.8f]' % accuracy_score(y_test, y_pred))


둘 다 본질적으로 같으니 편한 방식으로 쓰자


Appendix : 추가 학습 자료 (6) - Pipeline for StandardScaler & OneHotEncoder

Pipeline

data에 범주형 열과 숫자형 열이 혼재되어있고, 숫자형 열을 scaling까지 해줘야하는 경우 scaling과 one-hot encoding을 일괄적으로 간편하게 하는 것에 Pipeline이 거의 항상 활용된다.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from sklearn import datasets, model_selection, linear_model
from sklearn.metrics import mean_squared_error

import warnings
warnings.filterwarnings("ignore")

df_data = pd.read_excel('boston_house_data.xlsx', index_col=0)
df_data.columns = ['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT']

df_target = pd.read_excel('boston_house_target.xlsx', index_col=0)
df_target.columns = ['Price']

mean_price = df_target['Price'].mean()
df_target['Price'] = df_target['Price'].apply(lambda x : 1 if x > mean_price else 0)

x_train, x_test, y_train, y_test = model_selection.train_test_split(df_data, 
                                                                    df_target, 
                                                                    test_size=0.3, 
                                                                    random_state=0)                                                  

보스턴 집 값 데이터를 준비한다.

CRIM : 범죄율
ZN : 25,000 평방피트를 초과하는 거주지역 비율
INDUS : 비소매상업지역 면적 비율
CHAS : 찰스강의 경계에 위치한 경우는 1, 아니면 0
NOX : 일산화질소 농도
RM : 주택당 방 수 (거실 외 subroom)
AGE : 1940년 이전에 건축된 주택의 비율
DIS : 직업센터의 거리
RAD : 방사형 고속도로까지의 거리
TAX : 재산세율
PTRATIO : 학생/교사 비율
B : 인구 중 흑인 비율
LSTAT : 인구 중 하위 계층 비율

CHAS와 RAD가 범주형 열이고 나머지는 숫자형 열이다.

숫자형 열들은 서로 scale도 차이가 많이 나는 상태이다.


from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline

numeric_features = ['CRIM', 'ZN', 'INDUS', 'NOX', 'RM', 'AGE', 'DIS', 'TAX', 'PTRATIO', 'B', 'LSTAT']
numeric_transformer = StandardScaler() # cf) RobustScaler

categorical_features = ['CHAS', 'RAD']
categorical_transformer = OneHotEncoder(categories='auto', handle_unknown='ignore') 

preprocessor = ColumnTransformer(
    transformers=[ # List of (name, transformer, column(s))
        ('num', numeric_transformer, numeric_features),
        ('cat', categorical_transformer, categorical_features)])

먼저 df에 열 이름들을 다 지정해놔야한다. 그래야 Pipeline이 범주형 열과 숫자형 열을 구분지어 전처리할 수 있다.

Pipeline에는 preprocessor로 ColumnTransformer를 넘겨주는데, 여기에 전처리기들을 지정해주면 된다.

여기서는 숫자형 열에 대한 전치리기인 StandardScaler를 사용했고, 범주형 열에 대한 OneHotEncoder를 사용했다.

참고로 OneHotEncoder의 handle_unknown 매개변수의 default는 error이다.

만약 ohe를 fit해놓은 상태에서 추후 다른 범주값의 데이터가 들어와서 transform하라고 요청하는 경우에, 에러를 발생시킨다.

매개변수 값이 ignore인 경우 그런 경우에 대해 그냥 모든 열이 0인 벡터로 encoding 해낸다.


preprocessor_pipe = Pipeline(steps=[('preprocessor', preprocessor)])

preprocessor_pipe.fit(x_train)

x_train_transformed = preprocessor_pipe.transform(x_train)
x_test_transformed = preprocessor_pipe.transform(x_test)

from sklearn.ensemble import GradientBoostingClassifier

model = GradientBoostingClassifier(n_estimators=200, random_state=0)
model.fit(x_train_transformed, y_train) # <- x_train_transformed (not x_train)

accuracy = model.score(x_test_transformed, y_test)
print("model score:", round(accuracy, 4))

Pipeline에 preprocessor를 넘겨주고, x_train으로 fit한 후(반드시 train data로 fit해줘야함! test data는 금물), transform으로 전처리된 x_train과 x_test를 얻어낸다.

참고로, 원래 StandardScaler에서 작동하던 inverse_transform (정규화된 데이터를 역으로 원본으로) 같은 함수는 Pipeline에 대해서는 작동안됨.

암튼 전처리된 데이터로 평소 하던대로 모델 학습시키고 예측하고 하면 끝


sagemaker

AWS에서 제공하는 클라우드 기계 학습 플랫폼


Appendix : 추가 학습 자료 (7) - Stratified K-Fold CV & cross_val_score

(Stratified) K-Fold CV

model_selection의 KFold나 StratifiedKFold 함수를 써서 구현할 수도 있지만 좀 복잡해서 그냥 cross_val_score 쓰자.

참고로 KFold나 StratifiedKFold 객체는 split 메소드를 갖고있는데 이 것은 들어온 x_data, y_data를 n_split 만큼 쪼개어 n_split 만큼의 뭉탱이를 리턴해줌. 각 뭉탱이는 x_data와 y_data의 행 번호를 담고 있는 2차원 튜플임. 여기 담긴 idx 튜플을 통째로 리스트 인덱싱에 넣어 나눠진 행데이터들을 인덱싱할 수 있음.


cross_val_score

from sklearn import model_selection

model = create_model()

valid_scores = model_selection.cross_val_score(model, x_data, y_data, 
                                               cv=10, verbose=1,
                                               n_jobs=-1) # Number of jobs to run in parallel. '-1' == use all processors
                                              
print(valid_scores)

print('Cross-Validation Score : {:.2f}%'.format(np.mean(valid_scores * 100)))
  • cv에는 숫자를 지정하면 회귀 모델인 경우 KFold, 분류 문제인 경우 Stratified KFold로 자동으로 지정해줌. 숫자 말고 KFold 또는 Stratified KFold 객체를 직접 넘겨줄 수도 있다.

  • n_jobs를 -1로 넘기면 알고리즘을 병렬적으로 수행함. 속도가 더 빠름

GridSearchCV로 HPO를 할 때는 내부적으로 KFold를 쓰기 때문에 따로 KFold를 할 필요가 없음.

profile
PS, 풀스택, 앱 개발, 각종 프로젝트 내용 정리 (https://github.com/minsu-cnu)

0개의 댓글