서포트벡터머신

결정경계 (Decision Boundary), 즉 분류를 위한 기준 선을 정의하는 모델

그리하여, 분류되지 않은 새로운 점이 나타나면, 경계의 어느 쪽에 속하는지 확인해서 분류 과제를 수행할 수 있게됨.

차원이 늘어남에따라, 선이 아닌 평면의 형태로 결정경계가 바뀌는데, 더욱 고차원으로 가게되면 이를 초평면 (Hyperplane)이라 함

어떤 경계가 좋을까

위와 같은 경계들을 보면, A~E 보다 F가 더욱 안정적으로 보인다. 결정경계가 데이터 군으로부터 최대한 멀리 떨어지는게 좋다는 것이다.
실제로 Support Vectors는 결정경계와 가까이 있는 데이터 포인트들을 의미한다.

Margin 마진

마진은 결정경계와 서포트벡터 사이의 거리를 의미한다.

초록실선이 결정경계이고, 그 실선으로부터 검은 테두리가 있는 빨간 점 1개, 파란 점 2개가 영역을 두고 점선을 그려놓았는데, 점선으로부터 결정경계 까지의 거리가 바로 마진(margin)이다.

최적의 결정경계는 마진을 최대화 한다.

위 그림엔 2개의 속성을 가진 데이터들을 기반으로 결정경계를 그었는데, 이때 총 3개의 데이터포인트가 필요했다. 즉 n개의 속성을 가진 데이터에는 최소 n+1개의 서포트 벡터가 존재한다는 것을 알 수 있다.

SVM에서는 결정경계를 정의하는것이 결국 서포트벡터이기에, 데이터포인트 중 서포트벡터만 잘 골라낸다면, 나머지 쓸데없는 수많은 데이터포인트들을 무시할 수 있기에 매우 빠르다.

이상치를 얼마나 허용할것인가

선을 튀어나가버린 데이터가 발생하기마련이다.

여기서 하드마진과 소프트마진을 이야기한다.

하드마진

아웃라이어를 허용하지않고 까다로운 기준으로 세운것을 말한다. 그렇기에 서포트벡터와 결정경계 사이의 거리가 매우 좁다. (Margin이 작아진다).

이렇게 될 경우 오버피팅문제가 발생할 수 있다.

소프트마진

아웃라이어들이 마진안에 어느정도 포함되도록 너그럽게 기준을 잡은것이다. 이렇게 잡으니, 서포트벡터와 결정경계 사이의 거리가 멀어진다 (margin이커진다)

즉 대충대충 학습하는 꼴이라 언더피팅문제가 발생할 수 있다.

파라미터 C

from sklearn.svm import SVC
classifier = SVC(C=0.01)

이런식으로 C를 설정할 수 있는데, SVM모델이 오류를 어느정도 허용할 것인지 파라미터를 통해 지정할 수 있다.

C값이 클 수록 하드마진 (오류허용X) , 작을수록 소프트마진 (허용)이다.

Kernel 커널

poly

선으로 그을 수 없는 경우라면?

SVM을 사용할 떄 kernel을 지정하여 해결할 수 있다.

from sklearn.svm import SVC
SVC(kernel = 'poly') 

와 같이 사용함으로써 사용할 수 있다. 그치만, 다른 커널을 사용할 떄 주의가 필요하다.

단순히 outlier때문에 선형분리가 불가능하다고 판단해서는 안된다.

RBF (방사기저함수 = 가우시안커널)

RBF커널은 2차원의 점을 무한한 차원의 점으로 변환한다.. 상당히 선형대수학적으로 어려운 내용이다..

Kernel 커널

poly

선으로 그을 수 없는 경우라면?

SVM을 사용할 떄 kernel을 지정하여 해결할 수 있다.

from sklearn.svm import SVC
SVC(kernel = 'poly') 

와 같이 사용함으로써 사용할 수 있다. 그치만, 다른 커널을 사용할 떄 주의가 필요하다.

단순히 outlier때문에 선형분리가 불가능하다고 판단해서는 안된다.

RBF (방사기저함수 = 가우시안커널)

RBF커널은 2차원의 점을 무한한 차원의 점으로 변환한다.. 상당히 선형대수학적으로 어려운 내용이다..

파라미터 gamma

결정경계를 얼마나 유연하게 그을 것인가.

gamma값을 높이면 학습 데이터에 많이 의존해서 결정경계를 구불구불 긋게된다. → 오버피팅

낮추면, 결정경계를 직선에 가깝게 긋게된다 → 언더피팅

 

요약

어쩜 이렇게 잘 정리하셨을까.... 대단하시다..

  • SVM은 분류에 사용되는 지도학습 머신러닝 모델이다.
  • SVM은 **서포트 벡터(support vectors)**를 사용해서 결정 경계(Decision Boundary)를 정의하고, 분류되지 않은 점을 해당 결정 경계와 비교해서 분류한다.
  • **서포트 벡터(support vectors)**는 결정 경계에 가장 가까운 각 클래스의 점들이다.
  • 서포트 벡터와 결정 경계 사이의 거리를 **마진(margin)**이라고 한다.
  • SVM은 허용 가능한 오류 범위 내에서 가능한 최대 마진을 만들려고 한다.
  • **파라미터 C**는 허용되는 오류 양을 조절한다. C 값이 클수록 오류를 덜 허용하며 이를 **하드 마진(hard margin)**이라 부른다. 반대로 C 값이 작을수록 오류를 더 많이 허용해서 **소프트 마진(soft margin)**을 만든다.
  • SVM에서는 선형으로 분리할 수 없는 점들을 분류하기 위해 **커널(kernel)**을 사용한다.
  • **커널(kernel)**은 원래 가지고 있는 데이터를 더 높은 차원의 데이터로 변환한다. 2차원의 점으로 나타낼 수 있는 데이터를 다항식(polynomial) 커널은 3차원으로, RBF 커널은 점을 무한한 차원으로 변환한다.
  • RBF 커널에는 **파라미터 감마(gamma)**가 있다. 감마가 너무 크면 학습 데이터에 너무 의존해서 오버피팅이 발생할 수 있다.

 

참조의 첫번째 아무튼워라밸님의 블로그에 정말 잘 소개되어있는것을 보고 기억하고자 많은 부분을 가져왔다. 
이분의 블로그에 SVM말고도 다른 많은 좋은 자료들이 있으니, 들어가서 찾아보면 공부할거리들을 많이 얻을 수 있을 것이다. 

참조 : http://hleecaster.com/ml-svm-concept/ , https://scikit-learn.org/stable/modules/svm.html

'Machine Learning' 카테고리의 다른 글

크로스엔트로피 Cross Entropy, KL Divergence  (0) 2021.06.07

참조 :
https://www.youtube.com/watch?v=7GBXCD-B6fo
https://www.youtube.com/watch?v=Jt5BS71uVfI
https://theeluwin.postype.com/post/6080524
https://velog.io/@vanang7/%EC%97%94%ED%8A%B8%EB%A1%9C%ED%94%BC%EC%99%80-%ED%81%AC%EB%A1%9C%EC%8A%A4-%EC%97%94%ED%8A%B8%EB%A1%9C%ED%94%BC

머신러닝/딥러닝에서 분류문제를 풀 때 크로스엔트로피를 써서 손실을 구한다.

틀릴 수 있는 정보를 갖고 구한 엔트로피. 즉 불확실성 정도의 양이라고 할 수 있는데, 이 틀릴 수 있는 정보는 바로 머신러닝모델의 아웃풋이 있다. 모델의 아웃풋은 예측값이기에 틀릴 수 있다.

Cross Entropy is optimal entropy When ifro is from estimated probability

간단한 딥러닝 분류문제이다. 최종단에 softmax레이어가있고, 이것을 활용해 예측값을 구할 수 있다.
이것을 정답과 비교할 땐 정답값을 원-핫 인코딩으로 비교하는 형태이다. 이 과정에서 softmax값과 one-hot인코딩된 실제 값을 사용한다.

소프트맥스 값을 Q라고 하고, 실제 라벨을 P라고 했을 때
Q : Estimated Probability , P : True Probability

H(P,Q)가 크로스 엔트로피 공식이고, H(X)가 엔트로피 공식이다.
각각 클래스마다 어떤 확률로 존재하는지를 나타낸다 -> 여기서 $p_i$는 (어떠한 정보의) 확률이다.

$ log_2{1\over{q_i}}$ 는 정보의 양을 나타낸다

왜 크로스엔트로피에서 정보의 양에 $log_2$ $1\over{q_i}$ 을 사용하는걸까?

첫번쨰로 우리는 딥러닝 모델을 학습시킬때 이 예측값이 정답과 얼마나 근사한지 알고싶다.
그를 위해 실제 확률값을 사용해야한다. 단 정보의 양은 모델을 통해 온 정보의 양(예측값의 확률)을 사용해야해서 위의 log 안에 q 가 들어간것이다.
실제 정답의 확률을 사용함으로써 이 엔트로피값이 과연 실제 정답값과 얼마나 근사한지를 알 수 있게되는것이다.

틀릴 수 있는 정보를 갖고 구한 엔트로피값이다 라고 한마디로 정리할 수 있다.

KL Divergence

단순히 어떠한 모델을 설명할때, 차이가 크다-작다 보다 정확한 수치로 얼마만큼 차이난다! 라고 나타냄으로써 어떤 모델이 더 정답과 비슷하게 유추하는지 나타낼 수 있도록 하기위하여 KL Divergence를 사용한다.
이는 수치값으로 분포도를 나타낸다.

P : 정답,   Qa : 모델a,   Qb : 모델b

$Q_A$가 실제 정답과 상당히 비슷한 분포를 갖고있다고 할 수 있다.

KL Divergence는 실제 정답값의 분포도에서 상대적으로 얼마만큼 다른지에 대해 수치적으로 나타내는 값이다. 

상대엔트로피값과 같다. 

1. 항상 0과 같거나, 0보다 크다. 
2. 비대칭적이다.

 

 

그렇다면 왜? 크로스엔트로피를 cost func으로 쓰고, minimize함으로써 최적화를 하는데, 왜 KL Divergence를 쓰지 않는가?

실제로, 크로스엔트로피를 쓰거나, KL Divergence를 쓰거나 같은 현상을 일으킨다. 

결국 KL-Divergence 를 사용해도, Entropy(P)가 상수이므로 뒷부분이 무시되고, 크로스엔트로피를 쓰는것과 같은 효과를 발휘하는것. 

 

 

 

'Machine Learning' 카테고리의 다른 글

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

이전 글 : [Tree Based Models/Boosting] - XGBoost_1_트리만들기, Similarity Score, Gain, Pruning(Gamma)

이제 각 Leaf의 Output Value에 대하여 공부해보자. 

$${Output\,Value} = { Sum\,of\,Residuals \over {Number\,of\,Residuals + \lambda} }$$

$${Similarity\,Score} = { Sum\,of\,Residuals,\,Squared \over {Number\,of\,Residuals + \lambda} }$$

분자(잔차 합)의 제곱을 하느냐 마느냐 차이가 있다.

Output Value에서 $\lambda$값이 커진다는것은 Output Value가 작아지는것을 의미하고, 이는 Prediction값을 줄인다는 뜻이다. 

다른 Gradient Boost처럼, XGBoost도 값을 예측하는데에 Initial Prediction (Default = 0.5) 와  Learning Rate , 그리고 해당 리프의Output Value를 사용한다.

$ Prediction = Initial\, Prediction + Learning\,Rate * Output\,Value$

Learning Rate를 eta라고 하는데, 각 Output Value에 이를 곱하여 사용한다. 

Prediction

시리즈 1번에서의 예시자료를 그대로 사용하면,

이로써, 초기의 예측 0.5에서 -2.65로 한층 더 가까워졌다. 

나머지 값들도 예측을 진행

2,3,4번의 값들도 각각의 Leaf에 맞는 output Value를 사용하여 새로운 예측을 하였고, 이로써 Initial Prediction보다 좀 더 나아졌다. 

이제 이렇게 만들어진 Prediction을 통하여 또 잔차를 계산, 그 잔차들을 통해 Root에 넣고, SS계산
Max_Depth가 허용하는 만큼 or leaf에 하나남을때 까지 Split 지점을 찾아서 (최대 Gain을 기준으로) Split 진행, 
최종 Split 후 Gamma 값에 따라서 Gain과 비교하여 Pruning진행, 
Pruning 까지 진행한 후 Output Value를 계산,
Initial Prediction부터 이전 트리까지의 Output Value와 Learning Rate를 사용하여 새로운 Prediction 을 한다.

위 과정을 Maximum Number에 닿을때 까지 or 더이상 진전이 없을때 까지 반복하면 점차점차 각각의 값들에 맞는 Tree들이 만들어지고, 그렇게 모델이 학습된다! 

Gain이 최대가 되는 Split지점을 찾아서 트리만들고 SS계산 
Gain과 Gamma값의 비교를 통하여 Pruning
Leaf의 Output Value를 계산

아래는 StatQuest의 XGBoost Mathmatical Detail을 들으며,,,, 열심히 정리하며 이해하려 노력한 흔적이다!

테일러 근사를 통해 Loss Function을 쉽게 근사하고, Gradient와 Gession을 통해 식을 편하게 바꾸고나서 풀고 정리하고 풀고 정리하면, 
어떻게 Output Value 식이 만들어졌는지 알 수 있다.!

 

 

 

'Machine Learning > Boosting' 카테고리의 다른 글

XGBoost_1_트리만들기, Similarity Score, Gain, Pruning(Gamma)  (0) 2021.03.03
LightGBM  (0) 2021.03.01

출처 :
XGBoost: A Scalable Tree Boosting System / Tianqi Chen , University of Washington
www.youtube.com/watch?v=ZVFeW798-2I&list=RDCMUCtYLUTtgS3k1Fg4y5tAhLbw&start_radio=1&t=900 

Ensemble 모델 중 Boosting의 Gradient 부스팅의 상위호환버전과도 같은 XGBOOST이다. 

강력한 성능을 가진 XGBoost의 논문과, StatQuest의 강의를 열심히 공부하여 아래와 같이 정리해본다. 

초기 prediction 을 만들고 (default=0.5) 시작하는 것은 기존의 부스팅과 동일하다. 그러고 나서 잔차를 먼저 하나의 리프에 넣어준다. 

그리고, 해당 leaf의 Similarity Score를 계산하는데,

$$ {Similarity.Score} = { Sum. of. Residuals, Squared \over{Number. of. Residuals + \lambda }} = { (\Sigma_{i=1}^{n}{Residual})^2 \over { n +\lambda}  }$$
n=해당 leaf의 Residual 수

여기서 $\lambda$는 Regularization Parameter로 우리가 지정해주는 것이다. 
StatQuest에서는 간단하게 설명하기위해 4개의 row를 가진 데이터를 예를 들었다.

최 상위 4개가 들어있을때의 SS는 $(-10.5+6.5+7.5-7.5)\over{4 + \lambda}$이고, 람다를 0이라 가정했을때 (계산을 간편히하기위해)
SS=4이다. 

SS(Similarity Score) 계산이 끝나면 이제 이 리프를 분기시켜서 트리를 만들어본다.
4개의 데이터 사이에 분기점을 만들면 총 3개를 만들 수 있고, 이렇게 분기되는 때마다 SS를 계산하여, Gain을 계산한다.
각 트리를 만들때마다 왼쪽, 오른쪽 리프의 SS계산을 하여 기록하면 이제 분기 전후에 대한 비교가 가능해진다. 
분기 전에는 7.5와 -7.5가 서로 상쇄시켜서 ss가 낮았던 반면, 분기 후엔, 왼쪽 오른쪽 리프들 각각 상쇄작용이 약해서 ss점수가 더 크다. 

이제 각 분기점에 대한 Gain을 계산하는데, Gain의 식은 이러하다. $$Gain = Left_{Similarity} + Right_{Similarity} - ROOT_{Similarity}$$

분기점을 1,2번 데이터 사이로 했을 때 : Gain=120.33

분기점을 1번과 2번데이터 사이(Dosage < 15)로 했을때 Gain은 120.33이다.

22.5를 기준으로 분기하면 왼쪽에 잔차는 -10.5 , 6.5 이고, 오른쪽 잔차는 7.5 , -7.5로

$\lambda=0$으로 설정하였으니 각각의 SS는 $왼쪽 = {{(-10.5 + 6.5)}^2 \over {2 + \lambda}}= {4^2 \over {2}} = 8$ , $오른쪽 = {({7.5-7.5})^2 \over{2+\lambda}}=0$에서 왼쪽이 8 오른쪽이 0이고, Gain은 $8+0 -4 = 4$ 이다.

분기점을 2,3번 데이터 사이(Dosage<22.5)로 했을 때 Gain = 8 + 0 - 4 = 4

이제 분기점을 3,4번 데이터 사이 (Dosage < 30)으로 했을 때를 계산하면 

분기점을 3,4번 데이터 사이(Dosage < 30)으로 했을 때 Gain = 56.33

더이상 관측치 사이에 Split 할 것이 없다. 따라서 각 분기점의 Gain을 비교하면, 1,2번 사이의 Gain (120.33)이 가장 크므로, Dosage < 15을 선택.
왼쪽 리프에는 -10.5 하나만 들어와있고, 오른쪽엔 6.5, 7.5, -7.5가 들어와있다. 

이제 오른쪽 리프에 3개를 분할해보는데, 맨 처음 루트에서 SS와 각각 split지점에 대한 SS, Gain을 계산했던 것과 동일하게 진행한다.
2,3번 사이에 split을 하면, 아래 그림과 같이 나눠지고

그때의 SS와 Gain은 
$SS_{(Dosage<22.5)} = {  {(6.5+7.5-7.5)^2}\over{3+0} } = 14.08  $, $SS_{left}= {{(6.5)^2}\over{1+0} }= 42.25$ , $SS_{right}= { { (7.5-7.5)^2 } \over {2+0} } = 0$이고,
${Gain} = { SS_{left} + SS_{right} - SS_{ROOT} } = {42.25 +0 - 14.08} = 28.17$ 이다.

이후, 

3,4번 사이에 split을 하면, 아래 그림과 같은 분기가 이루어지고, 이 떄의 SS를 계산하면 각각 
$SS_{(Dosage<30)} = {  {(6.5+7.5-7.5)^2}\over{3+0} } = 14.08  $ ,  $SS_{left}= {{(6.5+7.5)^2}\over{2+0} }= 98$ , $SS_{right}= { { (-7.5)^2 } \over {2+0} } = 56.25$이고, 
${Gain} = { SS_{left} + SS_{right} - SS_{ROOT} } = {98 + 56.25 - 14.08} = 140.17$ 이다. 

22.5로 나눴을 때 보다, 30으로 나눴을 때 더욱 Gain이 크므로, Dosage < 30 을 선택한다. 
이후, max_depth (default = 6) 이 허용하는 만큼 한번 더 왼쪽 리프를 분기할 수 있지만, 계산을 간편히 하기위해 max_depth = 2로 제한하여 분기종료. 

이로써, SS Score와 Gain 을 통하여 트리를 만들었다. 
이제 트리를 Prune해야한다. Prune에서는 Gamma($\gamma$)를 사용한다. 
Gain보다 $\gamma$가 크면 가지친것을 삭제하는 것이다.

$\gamma = 130$이라면, 오른쪽 분기점의 Gain ( 140.17 )이 더 크므로 오른쪽 brunch가 살아남는다, 이에따라, 루트도 함께 살아남게된다.

 

만약 $\gamma=150$이라면?

먼저 오른쪽 brunch의 gain이 150보다 작으므로, 삭제하고, 루트또한 gain이 120.33으로 150보다 작으므로 이번에 만든 트리 자체를 날려버리는 것이다. 

$\lambda$의 역할은?

람다가 어떤 정규화 파라미터로 작용한다는 것은 앞서 설명했다. 하지만 어떻게 작용하길래 정규화 파라미터역할을 하는 것일까?
이는 Similarity Score를 확인하면 알 수 있다.

$$ {Similarity Score} = { { { Sum.Of.Residuals  }^2} \over {Num.Of.Residuals + \lambda }  }$$

$${Gain} = { SS_{left} + SS_{right} - SS_{ROOT} } $$

에서, $\lambda$가 커질수록, SS는 작아지고, 이에따라 Gain도 같이 작아지게된다. XGBoost에서 Gain에 따라 Pruning을 진행하는데, $\gamma$값보다 Gain이 작으면, 그 트리(or Brunch)를 삭제하므로, 트리가 자주 지워지게되고, 이는 결국 Overfitting을 방지하는 역할을 하게된다!

 λ 값에 따른동일 트리에 대한 Gain 차이

λ 값에 따른 Gain의 차이가 위와같다. SS를 계산할 때,  λ가 분모에 들어있으므로,  λ가 커지면 -> SS가 작아지고, 이에따라 Gain도 작아지는 것이다.

하나의 리프 내 샘플의 수에 따라서 λ가 어떤 영향을 주는가? 

SS와 Gain 모두 Number of Residual로, 한 리프에 몇개의 샘플(잔차)이 들어있는지에 영향을 받는다.
λ는 모두 분모에 있으며, 이에 대한 영향은 λ가 1만큼 커짐에 따라, Residual이 1개있는 리프는 SS가 $1\over2$만큼 작아진다.
2개있을경우 분모가 2->3으로 변화하므로 한 leaf에 잔차 수가 많으면 많을 수록 λ의 영향이 작다는것이고, 이는 한 leaf의 샘플 수가 많을 수록 λ의 영향이 적어지므로, 향후 파라미터 튜닝에서 λ의 값과, min_samples_leaf를 함께 생각해야 할 것이며 이 둘의 상호 영향또한 염려해야한다는 것으로 이해된다.

위의 내용에 대한 정리

$$ {Similarity.Score} = { Sum. of. Residuals, Squared \over{Number. of. Residuals + \lambda }} = { (\Sigma_{i=1}^{n}{Residual})^2 \over { n +\lambda}  }$$
$${Gain} = { SS_{left} + SS_{right} - SS_{ROOT} } $$
n=해당 leaf의 Residual 수

λ가 0보다 커진다 = Gain이 작아진다 = Leaves를 Prune하기 쉬워진다 = OverFitting을 방지한다 
$\gamma$가 커진다 = Gain-$\gamma$가 음수가 될 확률이 높아진다 =  Brunch를 Prune하기 쉬워진다 = OverFitting을 방지한다.

 

 

'Machine Learning > Boosting' 카테고리의 다른 글

XGBoost_2_Output Value  (0) 2021.03.03
LightGBM  (0) 2021.03.01

lightgbm.readthedocs.io/en/latest/\_images/LightGBM\_logo\_black\_text.svg

Motivation
Random Forest 가 병렬적으로 무지 많은 Decision Tree를 만든다면, 부스팅에서는 Decision Tree를 점진적으로 발전시킨 후 이를 통합하는 과정을 한다.
AdaBoost와 같이, 중요한 데이터에 대해 weight를 주는방식 vs GBDT와 같이 딮러닝의 loss function 마냥 정답지와 오답지간의 차이를 다음번 훈련에 다시 투입시켜서 gradient를 적극적으로 활용, 모델을 개선하는 방식이 있는데, XGBoost, LightGBM이 이에(후자) 속한다.

LightGBM에서 (GOSS와 EFB방식을 통해) 번들을 구성, 데이터 feature를 줄여서 학습한다

Conventional GBM need to, for every feature, scam all data instances to estimate the information gain of all the possible split points.

GOSS (Gradient-based One-Side Sampling)

  1. Data instances with different gradients play different roles in the computation of information gain
  2. Keep instances with large gradients and randomly drop instances with small gradients
  1. 각 데이터들은 서로다른 gradient를 가지며, 이에대한 information gain이 다르다 -> 어느지점을 습득하는지에 따라 다른 역할을 수행하게된다.
  2. large gradient한 데이터(instances)는 갖고가고, small gradient한 데이터에서 랜덤선택한다. -> 상위몇개를 고정, 하위몇개 중 랜덤하게 골라서 학습한다.

GBDT에서 모든 feature에 대해 스캔을 쭉 해서 가능한 split point를 찾아, information gain을 측정하는데, LightGBM에서는 모든 feature를 스캔하지않기위해, gradient-based One-Side Sampling 를 한다. (GOSS)

GBDT에는 weight는 없지만, gradient가 있다. 따라서 데이터의 개수를 내부적으로 줄여서 계산할 때, 큰 gradient를 가진 데이터는 그대로 사용하고, 낮은 gradient를 랜덤하게 drop하고 데이터를 가져와서, 샘플링을 해준다. gradient가 적다고 버리면 데이터 분포 자체가 왜곡될 수 있기에, 이대로 훈련하면 정확도가 낮아진다.

이를 방지하기위해 가져온 낮은 gradient 데이터에 대하여 ${1-a}\over{b}$ 만큼 뻥튀기 해준다.
(a: 큰 gradient데이터 비율, b: 작은 gradient 데이터 비율)

낮은 gradient를 가진 데이터만 drop하므로, One-Side Sampling이라고한다.
이렇게 feature를 줄여서 학습하는 것이다.

topN = a * len(I) ,전체 데이터의 a만큼 index (ex. 100개 중 a=0.2라면 topN=20)
randN = b* len(I) ,topN과 비슷하게 b만큼 index

  1. 모델로 일단 예측
  2. 실제값과의 error로 loss를 계산, weight를 계산하여 저장
  3. loss대로 정렬 -> sorted에 저장. sorted[1:topN] 만큼 Loss 상위 데이터들 topSet에 저장 (ex. 전체 100개 중 a=0.2라면 20개)
  4. 나머지 80개 중 randN개 만큼 랜덤샘플링하여 randSet에 저장 (ex. 나머지 80개 중 b=0.2라면 16개 데이터)
  5. UsedSet에 topSet과 randSet을 저장
  6. small gradient data에 fact(=${1-a}\over {b}$)만큼 weight를 부여해줌
  7. weak learner를 만들어서 새로운 모델로 부여 , weak leaner에는 샘플링된 데이터(UsedSet = topSet + fact assigned randSet)와 loss와 weight가 들어감
  8. 모델 저장

EFB (Exclusive Feature Bundling)

  1. In a sparse feature space, many features are (almost) exclusive, i,e., they rarely take nonzero values simultaneously (ex. one-hot encoding)
  2. Bundling these exclusive features does not degenerate the performance
  1. 하나의 객체(feature)에 대해 특정 두개의 변수가 Non-Zero값을 갖는 경우가 드물다.
  2. 그리하여 exclusive한 변수들을 번들링해도 성능저하가 거의 없다.

Greedy Bundling에서는 지금 현재 존재하는 feature set들에 대해서 어떠한 feature들을 하나의 번들로 묶을지 결정

Merge Exclusive Features에서는 번들링되어야하는 변수들을 이용, 하나의 변수로 값을 표현(Merge해줌)

예를들어 이러한 데이터셋(x1~x5) 먼저 각각의 conflict를 Edge로 하는 Graph를 생성한다 (오른쪽 위)
여기서 conflict는 서로 상충하는 데이터 개수(동시에 0이 아닌 데이터 개수)이다.
이를 바탕으로 오른쪽 아래의 Degree를 계산할 수 있고, 이를통해 시작점을 계산한다. 위의 그림에서는 x5부터 시작.

이렇게 그래프에서 x5부터 시작하며, 각각의 edge는 상충하는 데이터 개수(conflict)이다.
여기서 cut-off가 등장하는데, 이를 기준으로, 번들링에서 cut-off만큼 이상의 conflict라면, 하나의 번들로 묶지않는것이다.
위의 데이터에서는 10개 중 cut-off = 0.2로, 2개 이상 conflict 등장하면 엣지날림.
x5는 동떨어지게되므로 그대로가고,
x1을 계산할 때, x1과 x2는 6의 conflict로 날리고 x3도 날리면, ... x1,x4가 하나의 번들로 묶이게된다.
x2를 계산할 때, 이미 번들링된것을 제외하고, x3과 번들로 묶임. 더이상 묶을게 없으므로 종료.

이제 이렇게 묶은 번들에 대하여 Merge Exclusive Features를 진행한다.

각각 column(feature)에 대해 cardinality처럼 최대와 최소값을 기록하고, 기준이 되는 feature에 함께묶인 feature와 merge를 해주는것이다.
이때, 기준값이 있다면 그대로 기준값을 사용하고, 기준값이 없다면, 기준feature의 최대값 + 묶인녀석의 값을 해서 넣게된다.
conflict한 경우(둘다 값이 있을 때 or 둘다 0일때) 기준값을 사용한다. (빨강 박스)

사용 예

사이킷런 사용

import lightgbm
lgbm = lightgbm.LGBMClassifier(n_estimators=1500, n_jobs=-1, 
                            #    scale_pos_weight=(0.888/0.111)  # Unbalanced한 데이터
                                is_unbalance=True # Unbalanced한 데이터
                               )
eval_set=[(X_train,y_train),(X_val,y_val)]
lgbm.fit(X_train,y_train, eval_set=eval_set,eval_metric='f1',verbose=50 ,early_stopping_rounds=100)
lgbpred = lgbm.predict(X_val)

print(confusion_matrix(y_val, lgbpred))
print('Accuracy Score : ',accuracy_score(y_val, lgbpred))
print('F1 Score : ',f1_score(y_val, lgbpred))
print('\nRandomForest Regression Matrix\n' , classification_report(y_val, lgbpred))

Parameter Tunning

BayesianOptimization

!pip install bayesian-optimization
from bayes_opt import BayesianOptimization

def bayesian_param_opt_lgb(X,y,init_round=15,opt_round=25,n_folds=5,random_seed=42,n_estimators=10000,learning_rate=0.05, output_process=False):
    train_data=lightgbm.Dataset(data=X_train, label=y)
    def lgb_eval(num_leaves, feature_fraction, bagging_fraction, max_depth, min_split_gain, min_child_weight):
        params={'application':'binary','num_iterations': n_estimators, 'learning_rate':learning_rate,'early_stopping_round':100,'metric':'auc'}
        params['num_leaves'] = int(round(num_leaves))
        params['feature_fraction'] = max(min(feature_fraction,1),0)
        params['bagging_fraction'] = max(min(bagging_fraction,1),0)
        params['max_depth'] = int(round(max_depth))
        params['min_split_gain'] = min_split_gain
        params['min_child_weight'] = min_child_weight
        cv_result=lightgbm.cv(params, train_data, nfold=n_folds,seed=random_seed, stratified=True, verbose_eval=200, metrics=['auc'])
        return max(cv_result['auc-mean'])
    lgbBO=BayesianOptimization(lgb_eval, {'num_leaves':(24,45),
                                          'feature_fraction':(0.1,0.9),
                                          'bagging_fraction':(0.8,1),
                                          'max_depth':(5,9),
                                          'min_split_gain':(0.001,0.1),
                                          'min_child_weight':(5,50)}, random_state=42)
    lgbBO.maximize(init_points=init_round, n_iter=opt_round)
    return lgbBO
opt_params=bayesian_param_opt_lgb(X_train,y_train)##,init_round=5, opt_round=10, n_folds=3, random_seed=42, n_estimators=100,learning_rate=0.05)

Featrue Importance를 확인해볼 수 있는 모듈을 제공한다.

from lightgbm import plot_importance
plot_importance(lgb_model)

 

또한, eli5를 통해 Permutation Importance를 볼 수 있고, pdpbox의 pdp_plot, shap으로도 feature_importance를 볼 수 있다.

핵심 파라미터

max_depth : Tree의 최대 깊이를 말합니다. 이 파라미터는 모델 과적합을 다룰 때 사용됩니다. 만약 여러분의 모델이 과적합된 것 같다고 느끼신다면 제 조언은 max_depth 값을 줄이라는 것입니다.

min_data_in_leaf : Leaf가 가지고 있는 최소한의 레코드 수입니다. 디폴트값은 20으로 최적 값입니다. 과적합을 해결할 때 사용되는 파라미터입니다.

feature_fraction : Boosting (나중에 다뤄질 것입니다) 이 랜덤 포레스트일 경우 사용합니다. 0.8 feature_fraction의 의미는 Light GBM이 Tree를 만들 때 매번 각각의 iteration 반복에서 파라미터 중에서 80%를 랜덤하게 선택하는 것을 의미합니다.

bagging_fraction : 매번 iteration을 돌 때 사용되는 데이터의 일부를 선택하는데 트레이닝 속도를 높이고 과적합을 방지할 때 주로 사용됩니다.

early_stopping_round : 이 파라미터는 분석 속도를 높이는데 도움이 됩니다. 모델은 만약 어떤 validation 데이터 중 하나의 지표가 지난 early_stopping_round 라운드에서 향상되지 않았다면 학습을 중단합니다. 이는 지나친 iteration을 줄이는데 도움이 됩니다.

lambda : lambda 값은 regularization 정규화를 합니다. 일반적인 값의 범위는 0 에서 1 사이입니다.

min_gain_to_split : 이 파라미터는 분기하기 위해 필요한 최소한의 gain을 의미합니다. Tree에서 유용한 분기의 수를 컨트롤하는데 사용됩니다.

max_cat_group : 카테고리 수가 클 때, 과적합을 방지하는 분기 포인트를 찾습니다. 그래서 Light GBM 알고리즘이 카테고리 그룹을 max_cat_group 그룹으로 합치고 그룹 경계선에서 분기 포인트를 찾습니다. 디폴트 값은 64 입니다.


출처 :
LightGBM
Go Lab
고려대 일반대학원 산업경영공학과 비즈니스 애널리틱스 강의 (Pilsung Kang교수님)
파라미터튜닝
https://lsjsj92.tistory.com/548?category=853217
https://lightgbm.readthedocs.io/en/latest/Python-Intro.html#setting-parameters
http://machinelearningkorea.com/2019/09/25/lightgbm의-핵심이해/

eli5

Permutation Importance

각 특성의 값을 random하게 변형을 하고, 그 때 얼마나 error가 커지는지를 기준으로 각 특성의 중요도를 산출

!pip install eli5
import eli5
permuter = eli5.sklearn.PermutationImportance(
    xgbr, ## 이미 학습이 완료된 모델
    scoring = 'r2',
    n_iter=5,
    random_state=42
)
permuter.fit(X_val, y_val)
feature_names = X_val.columns.tolist()
sns.barplot(data=eli5.format_as_dataframe(eli5.explain_weights(permuter, feature_names=feature_names)), x='weight', y='feature')
eli5.show_weights(permuter, feature_names=feature_names)

pdpbox

Ensemble모델을 만들었을때 가장 큰 문제가 뭐냐함은, 이 모델의 성능은 알겠는데... 어찌 해석해야할지 난감할때가 많다는 것이다. 

Decision 트리를 제외하곤 세부과정들에서 만들어진 트리들을 하나하나 그려보거나 뜯어볼 수 없으니.. 이를 어떻게 확인할 것이냐. 라는 의문속에 많은 데싸인들이 고통받아왔다. 

pdpbox는 이를 해결하기 위해 나온 라이브러리이다. 

특성값에 따라 타겟값이 증가하는지, 감소하는지를 알 수 있다. 

이는 특성들을 선형적으로 변화시키면서 타겟이 어떻게 되는지를 보는것이다.

pdp에서는 최대 두 특성이 동시에 모델에 끼치는 영향을 볼 수 있으며 이를 통해 의존도를 볼 수 있다. 

아주 간단하다. 모델넣고, Validation Data넣고, 그 모델의 Feature들을 이름을 넣어주고나서 확인하고싶은 feature를 입력하면된다. 

#!pip install pdpbox
from pdpbox import pdp
feature = 'duration'
pdp_dist = pdp.pdp_isolate(model=xgb, dataset = X_val , model_features=X_val.columns, feature = feature)
pdp.pdp_plot(pdp_dist, feature)

 

features = ['age', 'education']
interaction = pdp.pdp_interact(
    model=xgb, 
    dataset=X_train, 
    model_features=X_train.columns, 
    features=features
)
pdp.pdp_interact_plot(interaction, plot_type='grid', feature_names=features, plot_params={'font_family':'NanumBarunGothic'});

 

shap

블랙박스와도 같은 모델을 투명하게 바꿔준다. 

게임이론에 나오는 shapely values에 기초하여 계산함. 

[모든 특성들을 조합했을 때 결과]에서 [관심있는 특성들을 제외한 조합]으로 계산된 결과를 뺀 값이 그 특성이 기여한 가치라고 판단. 

-> 그녀석이 없어지니 100점이 까였어 -> 그녀석의 기여도 ==100

데이터 특성 하나에 대한 설명 ->  개별 관측치 하나마다 특성의 영향도가 다르게 계산될 수 있음. 

plot type 을 bar로 주면, feature_importance_ 와 비슷하게, 특성의 영향력을 전체적으로 평가해줌. 

기존의 Permutation Importance (eli5)보다 정확하다. 그 이유는 특성중요도는 음의관계에 대하여 계산하지 않고, 서로 다른 특성들 간의 영향을 주는 경우 결과가 정확하지 않을 수 있기 때문이다. 

# !pip install shap
import shap
row=5
row = X_val.iloc[[9]]
explainer = shap.TreeExplainer(lgb_model)
shap_values = explainer.shap_values(row)
print(y_val.iloc[9])

shap.initjs()
shap.force_plot(
    base_value=explainer.expected_value, 
    shap_values=shap_values,
    features=row
)

 

 

출처 : 

pdpbox.readthedocs.io/en/latest/

 

PDPbox — PDPbox 0.2.0+13.g73c6966.dirty documentation

The common headache When using black box machine learning algorithms like random forest and boosting, it is hard to understand the relations between predictors and model outcome. For example, in terms of random forest, all we get is the feature importance.

pdpbox.readthedocs.io

github.com/SauceCat/PDPbox

 

SauceCat/PDPbox

python partial dependence plot toolbox. Contribute to SauceCat/PDPbox development by creating an account on GitHub.

github.com

 

Ensemble 학습의 Bagging 의 대표적인 방식

앙상블이랑 여러개의 알고리즘을 사용하여, 그 예측을 결정함으로써 보다 정확한 예측을 도출하는 기법.

기존의 Decision Tree에서 그 트리가 엄청많아져서, 그것들의 최종 값의 비율로 계산하자!

각각 부트스트랩으로 만들어진 데이터셋으로 학습하게되는데, 이를 Bagging (Bootstrap AGGregatING) 이라고 한다.

오리지널 데이터에서 복원추출하는데, 샘플을 뽑고, 다시 원본에서 샘플을 뽑는다. (중복될 수 있다는것)
원본 데이터셋에서 복원추출하는 것을 Bootstrap Sampling이라 한다.
그렇게 samples들을 뽑아서 원본과 같은 수의 sample을 구성하면 그게 부트스트랩샘플을 하나 만들었다고 한다.
각각의 부트스트랩 샘플의 Training set 에서 뽑히지 않은 샘플들을 test set으로 분류하고 이를 OOB Sample이라고 한다.
→ 100 개 데이터 중 70개로 트레인하고, 나머지 30개로 validation을 매 트리 생성시마다 한다고 보면 됨.

앙상블 기법은 한 종류의 데이터로 여러 학습모델을 만들어, 그 모델들의 예측결과를 다수결이나 펴균을 내어 예측하는 방법을 말한다.

랜덤포레스트는 Decision Tree를 기본모델로 사용한다.

부트스트랩 세트의 크기가 n일 때, 한번의 추출에서 어느 한 샘플이 추출되지 않을 확률은 ${n-1} \over n$ 이다. 
그것을 n번 반복였을 때에도 그 샘플이 추출되지 않았을 확률은 $ ({{n-1}\over n})^2$ 이다.
이게 이제 무한히 커지면 어느 한 점으로 수렴하게 되는데.

결과적으로 데이터가 충분할 때 한 부트스트랩 세트는 63.2%의 샘플을 갖고, 
여기에 속하지 않은 36.8% 의 샘플이 Out-of_Bag 샘플이며, 이것이 검증에 사용된다.

트리모델에서는 One-hot Encoding보다 Ordinal Encoding이 좀더 성능이 좋을 때가 많다.
원핫으로 피쳐가 추가되었을 때, 트리에서는 상위노드에서 중요한 피처가 사용되므로, 하나의 피쳐가 여러개의 피쳐로 쪼개진 One-Hot에서 범주 종류가 많은 (High Cardinality) 피쳐였을 경우 상위노드에서 선택될 가능성이 적어진다.

  • n_estimators = 트리의 갯수 | number of trees in the foreset
  • max_features = 노드들 구분할 때 고려할 최대 피쳐수 | max number of features considered for splitting a node
  • max_depth = 각 트리의 최대 깊이 | max number of levels in each decision tree
  • min_samples_split = 최소 분할 샘플 갯수 | min number of data points placed in a node before the node is split
  • min_samples_leaf = 최소 잎 노드 데이터 수 | min number of data points allowed in a leaf node
  • bootstrap = 복원 추출 | method for sampling data points (with or without replacement)

랜덤 포레스트의 Pseudo Code

S는 트레이닝셋, Feature 는 F, 랜덤포레스트 안의 트리는 B 라고 한다.
H를 초기화하고, 
1부터 B까지 (트리들을 순회하면서),
$S^(i)$ 에 S로 부터 만든 부트스트랩 샘플을 넣고, 
$h_i$ 에 트리를 학습시키고(트리의 학습은, 각각의 노드에서 feature의 subset을 최적으로 쪼개면서 학습시킨다. )
이후, 학습된 트리들 하나하나를 H에 합친 후, 최종 H를 리턴. 

from sklearn.model_selection import train_test_split

train, val = train_test_split(train, train_size=0.8, test_size=0.2,
                              stratify=train[target], random_state=2)
                              
##############################################                              
from category_encoders import OrdinalEncoder
from sklearn.pipeline import make_pipeline

pipe_ordinal_simple=make_pipeline(verbose=0,
    OrdinalEncoder(verbose=0),
    SimpleImputer(),
    RandomForestClassifier(n_jobs=-1, random_state=10, oob_score=True)
)

pipe_ordinal_simple.fit(X_train,y_train)
pipe_ordinal_simple.score(X_val,y_val) #0.8258806784485826

ㅇ 이렇게 파이프라인속에 넣어서 사용할 수도 있다. 
X_train으로 fit(학습)시키고, X_val로 분리해놓은 데이터를 통하여 점수를 매겨보는 등의 작업을 하면된다. 

 

하이퍼파라미터 튜닝

  • n_estimators = 트리의 갯수 | number of trees in the foreset
  • max_features = 노드들 구분할 때 고려할 최대 피쳐수 | max number of features considered for splitting a node
  • max_depth = 각 트리의 최대 깊이 | max number of levels in each decision tree
  • min_samples_split = 최소 분할 샘플 갯수 | min number of data points placed in a node before the node is split
  • min_samples_leaf = 최소 잎 노드 데이터 수 | min number of data points allowed in a leaf node
  • bootstrap = 복원 추출 | method for sampling data points (with or without replacement)
  • class_weight =  sample의 라벨 비율 ->  기존의 분류문제에서 라벨비율이 비슷하지 않으면, 좋지 않은 학습결과를 가져올 수 있는데, 이를 처리해줌.

class_weight 설정

어떠한경우 target 의 class 비율이 다를 수 있다. 이는 모델 학습에 악영향을 끼치는데, 이 부분에 대하여 RandomForest에서는 class_weight라는 파라미터를 준다. 

from sklearn.utils.class_weight import compute_class_weight
print(y_train.unique(),'\n',y_train.value_counts())
compute_class_weight(class_weight='balanced', classes = y_train.unique(), y=y_train)
# [0 1] 
# 0    29269
# 1     3681
# Name: y, dtype: int64
# array([0.56288223, 4.47568595])
## _____________________ ## 
from sklearn.ensemble import RandomForestClassifier
rfc = RandomForestClassifier(n_estimators=250, class_weight={1:0.56288223 , 0:4.47568595})
rfc.fit(X_train, y_train)
rfcpred = rfc.predict(X_val)

print(confusion_matrix(y_val, rfcpred))
print('Accuracy Score : ',accuracy_score(y_val, rfcpred))
print('f1 Score : ',f1_score(y_val, rfcpred))
print('\nRandomForest Regression Matrix\n' , classification_report(y_val,rfcpred))

이를 통하여 불균형한 데이터 셋에서 Upsampling과 Downsampling하지 않고도 주어진 데이터를 최선으로 활용하여 학습을 진행할 수 있다. 

 

Max_depth 튜닝

def pipe_ordinal_simple_elbow(howmany,X_train, y_train, X_val, y_val):
    trainAccuracy=[]
    valAccuracy=[]
    for depth in range(1,howmany):
        pipe_ordinal_simple=make_pipeline(
            OrdinalEncoder(verbose=0),
            SimpleImputer(),
            RandomForestClassifier(max_depth=depth,n_jobs=-1, random_state=10, oob_score=True)
        )
        pipe_ordinal_simple.fit(X_train,y_train)
        trainAccuracy.append(pipe_ordinal_simple.score(X_train,y_train))
        valAccuracy.append(pipe_ordinal_simple.score(X_val,y_val))
    return trainAccuracy, valAccuracy
ta_msl , tv_msl = pipe_ordinal_simple_elbow(howmany=20 , 
                                             X_train=X_train,
                                             y_train=y_train, 
                                             X_val=X_val, 
                                             y_val=y_val)
                                             
pd.DataFrame({'msl_training': ta_msl, 'msl_val':tv_msl}, index=range(1,20)).plot(figsize=(10,8))
plt.grid()

 

depth 가 7~8을 넘어가는 부분부터 training과 validation의 accuracy score가 확연히 달라진다. 
깊어질수록 training은 정확도가 높아지는반면, val의 정확도는 거의 변하지않는다. 이는 과적합되는 부분이라고 봐도 될것이다.

n_estimator

n_estimators = 트리의 갯수 | number of trees in the foreset
한마디로 투표용지의 개수가 얼마나 많아지는가? 라고 생각하면 된다. 

def pipe_ordinal_simple_elbow_estimators(howmany,X_train, y_train, X_val, y_val):
    trainAccuracy=[]
    valAccuracy=[]
    for depth in range(10,howmany, 20):
        pipe_ordinal_simple=make_pipeline(
            OrdinalEncoder(verbose=0),
            SimpleImputer(),
            RandomForestClassifier(n_estimators=depth,n_jobs=-1, random_state=10, oob_score=True)
        )
        pipe_ordinal_simple.fit(X_train,y_train)
        trainAccuracy.append(pipe_ordinal_simple.score(X_train,y_train))
        valAccuracy.append(pipe_ordinal_simple.score(X_val,y_val))
    return trainAccuracy, valAccuracy
ta_msl , tv_msl = pipe_ordinal_simple_elbow_estimators(howmany=200 , 
                                             X_train=X_train,
                                             y_train=y_train, 
                                             X_val=X_val, 
                                             y_val=y_val) 
                                             
pd.DataFrame({'msl_training': ta_msl, 'msl_val':tv_msl}, index=range(10,200,20)).plot(figsize=(10,8))
plt.grid()

시작을 10부터 잡아서 그런지, 거의 변화가 너무 적다. 하지만 확실하게 1부터 시작한다면 증가하는 폭을 볼 수 있을것이다.

 

GridSearchCV 를 통한 하이퍼파라미터 튜닝

from sklearn.model_selection import GridSearchCV

params = { 'randomforestclassifier__n_estimators' : [50, 100, 150],
           'randomforestclassifier__max_depth' : [6, 9, 12],
           'randomforestclassifier__min_samples_leaf' : [8, 12],
           'randomforestclassifier__min_samples_split' : [8, 16]
            }
pipe_ordinal_simple=make_pipeline(
            OrdinalEncoder(verbose=0),
            SimpleImputer(),
            RandomForestClassifier(n_jobs=-1, random_state=10, oob_score=True)
        )
grid=GridSearchCV(pipe_ordinal_simple , param_grid=params , cv=5, verbose=0)
grid.fit(X_train,y_train)
grid.best_estimator_
Pipeline(steps=[('ordinalencoder',
                 OrdinalEncoder(cols=['opinion_h1n1_vacc_effective',
                                      'opinion_h1n1_risk',
                                      'opinion_h1n1_sick_from_vacc',
                                      'opinion_seas_vacc_effective',
                                      'opinion_seas_risk',
                                      'opinion_seas_sick_from_vacc', 'agegrp',
                                      'employment_status', 'census_msa',
                                      'state'],
                                mapping=[{'col': 'opinion_h1n1_vacc_effective',
                                          'data_type': dtype('O'),
                                          'mapping': Somew...
TENNESSEE              39
FLORIDA                40
NEW HAMPSHIRE          41
IDAHO                  42
MAINE                  43
ALASKA                 44
WISCONSIN              45
OKLAHOMA               46
MASSACHUSETTS          47
NORTH DAKOTA           48
WASHINGTON             49
NEBRASKA               50
HAWAII                 51
NaN                    -2
dtype: int64}])),
                ('simpleimputer', SimpleImputer()),
                ('randomforestclassifier',
                 RandomForestClassifier(max_depth=12, min_samples_leaf=12,
                                        min_samples_split=8, n_estimators=150,
                                        n_jobs=-1, oob_score=True,
                                        random_state=10))])

이러한 출력을 볼 수 있다. 

이제 이 파라미터들의 상태를 시각화해본다

cols='param_randomforestclassifier__max_depth param_randomforestclassifier__min_samples_leaf	param_randomforestclassifier__min_samples_split	param_randomforestclassifier__n_estimators mean_test_score'.split()
griddf=pd.DataFrame(grid.cv_results_)
fig,axs=plt.subplots(2,2, figsize=(10,10))
sns.pointplot(ax=axs[0][0], data=griddf, x=cols[0],y=cols[-1])
sns.pointplot(ax=axs[0][1], data=griddf, x=cols[1],y=cols[-1])
sns.pointplot(ax=axs[1][0], data=griddf, x=cols[2],y=cols[-1])
sns.pointplot(ax=axs[1][1], data=griddf, x=cols[3],y=cols[-1])

max_depth의 변화에 가장 크게 증가하였으며, 그외에는 오히려 조금 떨어진 경우가 많았다. 

Feature Importance

plt.figure(figsize=(4,8), dpi=110)
sns.barplot(data=
            pd.DataFrame(grid.best_estimator_['randomforestclassifier'].feature_importances_,
                         index=X_train.columns, columns=['feature_importances_']).reset_index().sort_values('feature_importances_',ascending=False)
                         , x='feature_importances_', y='index')

 

'Machine Learning > Tree Based Models' 카테고리의 다른 글

Decision Tree (결정트리)  (0) 2021.02.10

결정트리모델은 특성들을 기준으로 샘플을 분류해 나가는데, 마치 Tree model처럼 뻗어나간다고해서 Decision Tree라는 이름을 갖는다.

결정트리는 학습 결과로 IF-THEN 형태의 규칙이 만들어진다.

결정트리는 퍼셉트론이나 로지스틱 회귀와 달리 선형 분리 불가능한 데이터도 분류할 수 있으나, 선형분리 가능한 문제는 잘 풀지 못한다. 또한 데이터를 조건 분기로 나눠가는 특성상, 트리가 깊어질수록 학습에 사용되는 데이터 수가 적어져 과적합을 일으키기 쉽다.

결정 트리는 학습 데이터로부터 조건식을 만들고, 예측할 때는 트리의 루트(최상위조건)부터 순서대로 조건분기를 타면서 리프에 도착하면 예측결과를 내는 알고리즘이다.

불순도(Impurity)를 기준으로 가능한 한 같은 클래스끼리 모이도록 조건 분기를 학습한다.

구체적으로는 정보획득(Information Gain)이나 지니게수(Gini Coefficient)등의 값을 불순도로 사용해, 그 값이 낮아지도록 데이터를 분할한다.

이후 결정트리가 응용되어 랜덤포레스트와 경사부스팅결정트리가 생겨났다.

결정트리의 각 노드는 뿌리, 중간, 말단 노드로 나뉠 수 있다.

지니불순도

※ 지니계수 : 경제학에서 불평등지수를 나타낼 때 사용하는 것으로 0일 때 완전 평등, 1일 때 완전 불평등을 의미합니다.

머신러닝에서는 데이터가 다양한 값을 가질수록 평등하며 특정 값으로 쏠릴 때 불평등한 값이 됩니다. 즉, 다양성이 낮을수록 균일도가 높다는 의미로 1로 갈수록 균일도가 높아 지니계수가 높은 속성을 기준으로 분할

임의의 새로운 변수가 데이터셋으로부터 클래스 라벨의 분포를 따라 무작위로 분류된 경우, 임의 변수의 새로운 인스턴스의 잘못된 분류 가능성에 대한 측정치.
분류가 잘 되었는지 판단할 수 있는 척도이다.
$G_i = {1 - \Sigma_{k=1}^n{p_ik^2} }$

$I_G(p) = \Sigma_{i=1}^J{p_i ( 1-p_i) } = 1 - \Sigma_{i=1}^J{p_i^2} $
정보획득은 특정한 특성을 사용해 분할했을 떄 엔트로피의 감소량을 뜻한다.

여기서 불순도(impurity) 라는 개념은 여러 범주가 섞여 있는 정도를 이야기 한다.
예를들어 A, B 두 클래스가 혼합된 데이터가 있을 때 (A, B) 비율이 (45%, 55%)인 샘플(두 범주 수가 비슷)은 불순도가 높은 것이며
(80%, 20%)인 샘플이 있다면 상대적으로 위의 상태보다 불순도가 낮은 것 입니다. (순수도(purity)는 높음)

지니 이득 = 뿌리 지니값 - 좌측 잎 비율 좌측 지니값 - 우측 잎 비율 우측 지니값

엔트로피

${\displaystyle \mathrm {H} (T)=\operatorname {I} _{E}\left(p_{1},p_{2},...,p_{J}\right)=-\sum _{i=1}^{J}{p_{i}\log _{2}p_{i}}}$

불순도를 측정하는 지표로서, 정보량의 기댓값.

$Entropy(S) = \Sigma_{i=1}^c{p_i * I(x_i)}$

S : 이미 발생한 사건의 모음 , c: 사건의 개수

정보량이란? :: 어떤 사건이 갖고있는 정보의 양을 의미하며, $I(x) = \log_2{1\over{p(x)}}$ 이다.

여기서 $p(x) : 사건이 발생할 확률$

사건 x가 발생할 확률을 x축, 정보량을 y축으로 그래프를 그리면

사건 x가 발생할 확률이 증가할 수록 정보량은 0에 수렴 → 자주 발생하는 사건일수록 정보량이 떨어진다!

순도 100% (한 종류의 class만 있는경우) 의 경우에는 Entropy가 0이고, 두 클래스가 정확히 반반 섞여있을 때 Entropy가 가장 높다.

___

결정트리의 특성

  • 학습한 모델을 사람이 해석하기 쉽다.
  • 입력 데이터를 정규화 할 필요가 없다.
  • 범주형 변수나 데이터의 누락값 (NaN)이 있어도 용인된다.
  • 특정 조건에서 과적합을 일으키기 쉽다.
  • 비선형 문제에는 적용할 수 있지만, 선형 분리 문제는 잘 풀지 못한다.
  • 데이터의 분포가 특정 클래스에 쏠려있으면 잘 풀지 못한다.
  • 데이터의 작은 변화에도 결과가 크게 바뀌기 쉽다.
  • 예측 성능은 보통이다.
  • 배치 학습으로만 학습할 수 있다.
Parameter Describe
min_samples_split 노드를 분할하기 위한 최소한의 샘플 데이터수 → 과적합을 제어하는데 사용-
  Default = 2 → 작게 설정할 수록 분할 노드가 많아져 과적합 가능성 증가
min_samples_leaf 리프노드가 되기 위해 필요한 최소한의 샘플 데이터수- min_samples_split과 함께 과적합 제어 용도

  - 불균형 데이터의 경우 특정 클래스의 데이터가 극도로 작을 수 있으므로 작게 설정 필요
max_features 최적의 분할을 위해 고려할 최대 feature 개수- Default = None → 데이터 세트의 모든 피처를 사용
  - int형으로 지정 →피처 갯수 / float형으로 지정 →비중- sqrt 또는 auto : 전체 피처 중 √(피처개수) 만큼 선정
  - log : 전체 피처 중 log2(전체 피처 개수) 만큼 선정
max_depth 트리의 최대 깊이- default = None→ 완벽하게 클래스 값이 결정될 때 까지 분할또는 데이터 개수가 min_samples_split보다 작아질 때까지 분할
  -깊이가 깊어지면 과적합될 수 있으므로 적절히 제어 필요
max_leaf_nodes 리프노드의 최대 개수

Decision Tree의 과적합을 줄이기 위한 파라미터 튜닝

(1) max_depth 를 줄여서 트리의 깊이 제한

(2) min_samples_split 를 높여서 데이터가 분할하는데 필요한 샘플 데이터의 수를 높이기

(3) min_samples_leaf 를 높여서 말단 노드가 되는데 필요한 샘플 데이터의 수를 높이기

(4) max_features를 높여서 분할을 하는데 고려하는 feature의 수 제한

min_samples_leaf 를 높여서 말단 노드가 되는데 필요한 샘플 데이터의 수를 높이기

from category_encoders import OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.pipeline import make_pipeline
from sklearn.tree import DecisionTreeClassifier

def pipe_min_sample_leaf_elbow(howmany,X_train,y_train,X_val,y_val):
    trainAccuracy=[]
    valAccuracy=[]
    for leafs in range(1,howmany):
        pipe_msl=make_pipeline(
            OneHotEncoder(),
            SimpleImputer(),
            DecisionTreeClassifier(min_samples_leaf=leafs, random_state=2)
        )
        pipe_msl.fit(X_train,y_train)
        trainAccuracy.append(pipe_msl.score(X_train,y_train))
        valAccuracy.append(pipe_msl.score(X_val,y_val))
    return trainAccuracy, valAccuracy


ta_msl , tv_msl = pipe_min_sample_leaf_elbow(howmany=30 , 
                                             X_train=X_train_oh,
                                             y_train=y_train, 
                                             X_val=X_val_oh, 
                                             y_val=y_val)

pd.DataFrame({'msl_training': ta_msl, 'msl_val':tv_msl}, index=range(1,30)).plot(figsize=(10,8))
plt.grid()

최종 Leaf의 수가 많아질 수록 점점 train데이터의 정확도가 줄어드는 반면, Validation데이터의 정확도는 증가합니다.

하지만 어느정도 한계가 있는듯, 곡선의 기울기가 약 leafs=15부터는 상승하는 정도가 매우 작아집니다.

max_depth 를 줄여서 트리의 깊이 제한

  • 트리의 최대 깊이
  • default = None → 완벽하게 클래스 값이 결정될 때 까지 분할 또는 데이터 개수가 min_samples_split보다 작아질 때까지 분할
  • 깊이가 깊어지면 과적합될 수 있으므로 적절히 제어 필요
def pipe_max_depth_elbow(howmany,X_train,y_train,X_val,y_val):
    trainAccuracy=[]
    valAccuracy=[]
    for depth in range(1,howmany):
        pipe_msl=make_pipeline(
        	OneHotEncoder(),
            SimpleImputer(),
            DecisionTreeClassifier(max_depth=depth, random_state=2)
        )
        pipe_msl.fit(X_train,y_train)
        trainAccuracy.append(pipe_msl.score(X_train,y_train))
        valAccuracy.append(pipe_msl.score(X_val,y_val))
    return trainAccuracy, valAccuracy
    
ta_mxd , tv_mxd = pipe_max_depth_elbow(howmany=20 , 
                                             X_train=X_train_oh,
                                             y_train=y_train, 
                                             X_val=X_val_oh, 
                                             y_val=y_val)

pd.DataFrame({'mxd_training': ta_mxd, 'mxd_val':tv_mxd}, index=range(1,20)).plot(figsize=(10,8))
plt.grid()

 

max_depth 는 트리의 최대 깊이를 나타냅니다.
default 는 None으로 완벽하게 클래스 값이 결정될때까지 or min_samples_split보다 작아질 때 까지 분할합니다.
깊이가 깊어질 수록 과적합될 가능성이 높아집니다. training데이터의 경우 깊이가 깊어질수록 정확도가 한없이 증가하는 모습을 보입니다.
하지만 validation데이터의 경우 깊이가 깊어질 수록 정확도가 떨어지는 모습을 보입니다.
해당 depth에 맞게 training데이터가 학습(fit)된 이후, validation데이터가 해당 트리를 통해 걸러져서 분류된 결과가 depth가 깊어짐에 따라 점점 안맞게 된다는 뜻으로 해석됩니다.

min_samples_split 를 높여서 데이터가 분할하는데 필요한 샘플 데이터의 수를 높이기

def pipe_min_sample_split_elbow(howmany,X_train,y_train,X_val,y_val):
    trainAccuracy=[]
    valAccuracy=[]
    for splits in range(1,howmany):
        pipe_msl=make_pipeline(
        	OneHotEncoder(),
            SimpleImputer(),
            DecisionTreeClassifier(min_samples_split=splits, random_state=2)
        )
        pipe_msl.fit(X_train,y_train)
        trainAccuracy.append(pipe_msl.score(X_train,y_train))
        valAccuracy.append(pipe_msl.score(X_val,y_val))
    return trainAccuracy, valAccuracy
    
ta_mss , tv_mss = pipe_min_sample_leaf_elbow(howmany=20 , 
                                             X_train=X_train_oh,
                                             y_train=y_train, 
                                             X_val=X_val_oh, 
                                             y_val=y_val)
                                             
pd.DataFrame({'mss_training': ta_mss, 'mss_val':tv_mss}, index=range(1,20)).plot(figsize=(10,8))
plt.grid()

min_samples_split : int or float, default=2

The minimum number of samples required to split an internal node: 규칙노드(내부노드)의 분기를 위한 최소 샘플 개수를 지정하는 방식입니다.
기존의 디폴트값은 2로, 어떤 하나와 다른 하나가 나타나는 순간 그 지점에서 분기를 하는반면, 값이 커질수록 다른 n개의 sample이 나타나야 분기를 하는것으로, 분기하는 정도가 조금 더 약해진다고 볼 수 있습니다.

GridSearchCV를 통한 튜닝

from sklearn.model_selection import GridSearchCV
params = {
    'max_depth' : [4, 5, 6, 7, 8, 9, 10],
    'min_samples_split' : range(10,20),
    'min_samples_leaf' : range(5,15)
         }

gridCV = GridSearchCV(DecisionTreeClassifier(random_state=2), 
                      param_grid=params, scoring='accuracy', cv=5, verbose=1)

## OneHotEncoder() 미리 사용

ohe=OneHotEncoder()
X_train_oh = ohe.fit_transfort(X_train)
X_val_oh = ohe.transform(X_val)
X_test_oh = ohe.transform(X_test)

## SimpleImuter() 를 미리 사용
imp_mean=SimpleImputer()
X_train_oh_imputed = imp_mean.fit_transform(X_train_oh)
X_val_oh_imputed = imp_mean.transform(X_val_oh)
X_test_oh_imputed = imp_mean.transform(X_test_oh)

## GridSearchCV사용
gridCV.fit(X_train_oh_imputed , y_train)

print(gridCV.best_score_)
gridCV.best_params_

# 0.8244818005868766
# {'max_depth': 7, 'min_samples_leaf': 14, 'min_samples_split': 10}

GridSearchCV를 사용해서 params의 조합을 통하여 엄청난 연산을 수행 끝에 거의 5분~10분만에 끝났습니다.
최고 스코어는 : 0.8244818005868766
그 때의 조합 : {'max_depth': 7, 'min_samples_leaf': 14, 'min_samples_split': 10}
이 조합이 기존에 Pipeline으로 하던 것에서 최상의 조합이라고 볼 수 있습니다.
(지금까지는.. 아마 나중에 뭔가 다른방법을 알게된다면 시도해보겠습니다.)

from sklearn import tree

gridbest = gridCV.best_estimator_

plt.figure(figsize=(12,12), dpi=200)
tree.plot_tree(gridbest,max_depth=3);

 

각 columns의 중요도 시각화
doctor_recc_h1n1
의사의 권유가 있었다는 feature가 중요성이 높았고, 그 다음 건강보험 유무였는데, 이 두 변수 사이에 작은 상관관계가 있을것으로 추정됩니다. 그 이유는 보험있는사람들이 병원에 좀더 자유롭게 (가격부담없이) 진료받으러 갔을것이며, 또한 보험이 있기에 백신접종 또한 권유를 더 잘받지않았을까? 또한, 세번째 피쳐가 백신이 효과적이라고 생각하는것 네번째가 의료종사자, 다섯번째는 백신위험이 적다는의견입니다.

plt.figure(figsize=(6,10))
pd.Series(gridbest.feature_importances_, X_train_oh.columns).sort_values().tail(15).plot.barh()

 

 

참조:

머신러닝 실무 프로젝트 / 아리아 미치아키 / 한빛미디어

[Chapter 4. 분류] Decision Tree Classifier :: 데이터분석, 머신러닝 정리 노트 (tistory.com)
참조2

(https://injo.tistory.com/15)

'Machine Learning > Tree Based Models' 카테고리의 다른 글

Random Forests 랜덤포레스트  (0) 2021.02.10

Multiple Linear Regression 다중회귀

기존의 1 : 1 의 관계 ( $y = wx + b $)가 아닌 $y = w_1x_1+w_2x_2+w_3x_3 \cdots + w_nx_n + \beta$ 의 형태

Multicollinearity 다중공선성

다중회귀를 시작하면서부터는 변수선택의 문제가 발생하는데, (독립)변수들끼리 상관관계를 갖고있을 때 생기는 다중공선성을 염려해야함

독립변수들 간에 정확한 선형관계가 존재하는 완전공선성의 경우와 독립변수들 간에 높은 선형관계가 존재하는 다중공선성으로 구분하기도함

이는 회귀분석의 전제 가정을 위배하는 것이므로 적절한 회귀분석을 위해 해결해야 하는 문제가 됨.

진단법

결정게수 $R^2$값은 높아 회귀식의 설명력은 높지만, 식 안의 독립변수의 P-value 값이 커서 개별 인자들이 유의하지 않는 경우가 있다. 이런 경우 독립변수들 간에 높은 상관관계가 있다고 의심

독립변수들 간의 상관계수를 구한다.

분산팽창요인 (Variance Inflation Factor)를 구하여 이 값이 10을 넘는다면 보통 다중공선성의 문제가 있다

해결법

상관관계가 높은 독립변수 중 하나 혹은 일부를 제거.

변수를 변형시키거나 새로운 관측치를 이용

자료를 수집하는 현장의 상황을 보아 상관관계의 이유를 파악하여 해결

출처 : 위키백과


소거법

전진선택법 (Forward Selection)

참조:
https://todayisbetterthanyesterday.tistory.com/10
https://zephyrus1111.tistory.com/65
https://datascienceschool.net/03 machine learning/14.03 특징 선택.html

기존 모형에 가장 설명력이 좋은 변수를 하나씩 추가

유의수준을 통해 결정하는데, 이를 만족하지 못하면 선택되지 않음 → 전부 선택되지 않을수도있음!

$S$를 기존모형에 포함된 변수들의 집합, $\tilde S$를 모형에 포함되지 않은 변수집합 이라 하고 유의수준을 $\alpha$ 라 하자.

  1. 아직 모형에 적합시키지 않은 변수 ( $X_k \in \tilde S$ ) 를 기존 모형에 적합. (기존모형의 변수 + $X_k$ 를 통해 모형생성)

이때 최소 p-value 값과 유의수준을 비교하여 p-value < $\alpha$ 이면 최소 p-value에 해당하는 변수를 S에 포함, 1~2 단계 수행

# 참조 : https://zephyrus1111.tistory.com/65
## 전진 선택법
variables = df.columns[:-2].tolist() ## 설명 변수 리스트

y = df['Survival_Time'] ## 반응 변수
selected_variables = [] ## 선택된 변수들
sl_enter = 0.05

sv_per_step = [] ## 각 스텝별로 선택된 변수들
adjusted_r_squared = [] ## 각 스텝별 수정된 결정계수
steps = [] ## 스텝
step = 0
while len(variables) > 0:
    remainder = list(set(variables) - set(selected_variables))
    pval = pd.Series(index=remainder) ## 변수의 p-value
    ## 기존에 포함된 변수와 새로운 변수 하나씩 돌아가면서 
    ## 선형 모형을 적합한다.
    for col in remainder: 
        X = df[selected_variables+[col]]
        X = sm.add_constant(X)
        model = sm.OLS(y,X).fit()
        pval[col] = model.pvalues[col]

    min_pval = pval.min()
    if min_pval < sl_enter: ## 최소 p-value 값이 기준 값보다 작으면 포함
        selected_variables.append(pval.idxmin())

        step += 1
        steps.append(step)
        adj_r_squared = sm.OLS(y,sm.add_constant(df[selected_variables])).fit().rsquared_adj
        adjusted_r_squared.append(adj_r_squared)
        sv_per_step.append(selected_variables.copy())
    else:
        break

위와같은 식으로 전진선택법을 사용할 수 있으며, 그 외 후진선택법 등이 있다. 이는 코드블럭내의 블로그를 가면 자세히 설명되어있으니 그곳에서 읽어보는것이 더욱 도움될것이다.

sklearn.feature_selection.SelectKBest

사이킷런에서는 SelectKBest라는 모듈을제공하여, Feature Selection을 더욱 간편하게 해준다.
가장 성능이 좋은 변수만 선택해주며, K 라는 파라미터를 통하여, 몇개의 feature를 선택할 지 고를 수 있다.

from sklearn.feature_selection import f_regression, SelectKBest

## selctor 정의합니다.
selector = SelectKBest(score_func=f_regression, k=20)

## 학습데이터에 fit_transform 
X_train_selected = selector.fit_transform(X_train, y_train)

## 테스트 데이터는 transform
X_test_selected = selector.transform(X_test)

all_names = X_train.columns

## selector.get_support()
selected_mask = selector.get_support() #이름만 가져온것

## 선택된 특성들
selected_names = all_names[selected_mask]

## 선택되지 않은 특성들
unselected_names = all_names[~selected_mask] 

print('Selected names: ', selected_names)
print('Unselected names: ', unselected_names)

그렇다면! k를 몇을 해야 가장 좋을까? -> 이전의 PCA나, 클러스터링에서 했듯 Elbow method처럼 plot을 그려보면된다!

from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, r2_score

training = []
testing = []
ks = range(1, len(X_train.columns)+1)

# 1 부터 특성 수 만큼 사용한 모델을 만들어서 MAE 값을 비교 합니다.
for k in range(1, len(X_train.columns)+ 1):
    print(f'{k} features')

    selector = SelectKBest(score_func=f_regression, k=k)

    X_train_selected = selector.fit_transform(X_train, y_train)
    X_test_selected = selector.transform(X_test)

    all_names = X_train.columns
    selected_mask = selector.get_support()
    selected_names = all_names[selected_mask]
    print('Selected names: ', selected_names)


    model = LinearRegression()
    model.fit(X_train_selected, y_train)
    y_pred = model.predict(X_train_selected)
    mae = mean_absolute_error(y_train, y_pred)
    training.append(mae)

    y_pred = model.predict(X_test_selected)
    mae = mean_absolute_error(y_test, y_pred)
    r2 = r2_score(y_test, y_pred)
    testing.append(mae)
    print(f'Test MAE: ${mae:,.0f}')
    print(f'Test R2: {r2} \n')

plt.plot(ks, training, label='Training Score', color='b')
plt.plot(ks, testing, label='Testing Score', color='g')
plt.ylabel("MAE ($)")
plt.xlabel("Number of Features")
plt.title('Validation Curve')
plt.legend()
plt.show()

참 편하게도, 이렇게 feature의 변화가 K-best로 뽑혔을 때  MAE 가 낮아지는 정도를 볼 수 있으니 참 좋다!

 

## 실습

from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, r2_score
from sklearn.preprocessing import StandardScaler, RobustScaler
features , test = ['sqft_living', 'sqft_lot'], ['price']

x_train = df_train[features]
y_train = df_train[test]

x_test = df_test[features]
y_test = df_test[test]


x=dfraw.loc[:,features].values
x=StandardScaler().fit_transform(x)
x_train_Standard=pd.DataFrame(x, columns=[features])

y=dfraw.loc[:,test].values
y=StandardScaler().fit_transform(y)
y_train_Standard=pd.DataFrame(y, columns=[test])


# Train의 경우 model을 학습시키고, model을 뱉어낸다.
# Test의 경우, 입력된 모델의 predict만을 이용하여 시행하고, 결과를 뱉어낸다
def regression_test(model,xtrain, ytrain , isTrain='test'):
    try:
        if isTrain.upper()=='TRAIN': #Training부터 시작하는 경우 (트레이닝)
            print('Start with Training')
            model = LinearRegression()  
            model.fit(xtrain,ytrain)
    except:
        pass

    y_pred = model.predict(xtrain)
    mse = mean_squared_error(ytrain, y_pred)
    mae = mean_absolute_error(ytrain, y_pred)
    rmse = mse ** 0.5
    r2 = r2_score(ytrain, y_pred)
    dic={'mse':round(mse,2),'mae':round(mae,2),'rmse':round(rmse,2),'r2':round(r2,2)}
    # print(f'MAE : {dic[mse]}\nMAE : {dic[mae]}\nRMSE : {dic[rmse]}\nR_Square : {dic[r2]} ')
    return model,dic

model_st_3, std_dic=regression_test(model, xtrain=x_train_Standard, ytrain=y_train_Standard, isTrain='train')
std_dic

# Start with Training 
# {'mae': 0.47, 'mse': 0.51, 'r2': 0.49, 'rmse': 0.71}

regression_test(model_st_3, x_test,y_test) # StandardScaler
#(LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=False),
# {'mae': 541048.28, 'mse': 424174008111.0, 'r2': -2.22, 'rmse': 651286.43})

'Machine Learning > Linear Models' 카테고리의 다른 글

Simple Linear Regression ( 단순 선형 회귀 )  (0) 2021.02.07

회귀분석이란?

독립변수 x에 대응하는 종속변수 y와 가장 비슷한 값 $\hat y$ 를 출력하는 함수 $f(x)$ 를 찾는 과정

회귀분석에서 가장 중요한 개념은 예측값과 잔차(residual)이다. 예측값은 만들어진 모델이 추정하는 값이고, 잔차는 예측값과 관측값의 차이이다.

\hat\beta = {{ { n(\Sigma_i^n{x_i}{y_i}) - (\Sigma_i^n{x_i} (\Sigma_i^n{y_i}) } }\over{ n(\Sigma_i^n{x^2}) - (\Sigma_i^n{x})^2 } }

$$ \hat \alpha = \bar{y} - \hat \beta x$$

$$\hat y = \hat\alpha + \hat\beta x = \bar {y} + \hat\beta(x-\bar {x})$$

Example

lotsize=[10,20,30,40,40,50,60,60,70,80] #로트 크기
peo = [20,29,50,60,70,85,90,95,109,120] #생산 인력
df=pd.DataFrame({'lot':lotsize, 'peo':peo})
df
plt.scatter(data=df , x='lot',y='peo')
plt.show()

이 예에서 로트크기와 같이 영향을 주는 변수를 독립변수(explanatory variable), 설명변수(independent variable)라고 하며, 생산인력과 같이 영향을 받는 변수를 종속변수 (dependent variable) 또는 반응변수(response variable) 라고 한다.

회귀분석은 설명변수의 값으로부터 종속변수의 값을 에측하고자 함이 그 목적으로서, 설명변수와 종속변수의 관계를 구체적인 함수 형태로 나타내고자 하는 것이 분석의 주요 내용이다.

설명변수가 한 개일 때의 회귀분석을 단순 회귀분석 (simple regression)이라 하고, 두 개 이상의 설명변수를 고려하는 회귀분석을 multiple regression이라 한다.

설명변수가 하나이고 설명변수와 종속변수의 관계가 직선관계인 경우 Simple Linear Regression이라 한다.

회귀분석의 첫 단계에서 할 일은 산점도를 그려보고, 독립변수와 종속변수의 관게에 대한 대략적인 파악을 하고, 적절한 모형을 설정하는 것이다.

df['x_square'] = df['lot'].apply(lambda x : x**2)
df['y_square'] = df['peo'].apply(lambda x : x**2)
df['xy']=df[['lot','peo']].apply(lambda x : x['lot'] * x['peo'], axis=1)
df

n=len(df['lot'])
beta_up = n*(df['xy'].sum()) - df['lot'].sum() * df['peo'].sum()
beta_down = n*(df['x_square'].sum()) - (df['lot'].sum())**2
beta = beta_up/beta_down

alpha = df['peo'].mean() - beta*df['lot'].mean()
alpha , beta
#(4.711711711711715, 1.4801801801801802)

def simpleLinearRegression(x,y):

    # input : two list x and y (x and y must have same length)
    # output : alpha, beta ( y = alpha + beta * x )

    meanx=sum(x)/len(x)
    meany=sum(y)/len(y)
    분자=sum(list(map(lambda x : (x[0]-meanx)*(x[1]-meany),zip(x,y))))
    분모=sum(list(map(lambda x : (x-meanx)**2 , x)))
    beta=분자/분모
    alpha=meany - beta*meanx
    print(f"y = {beta} * x + ({alpha})")
    return alpha, beta

simpleLinearRegression(df['lot'],df['peo'])

y = 1.4801801801801804 * x + (4.7117117117117004)
(4.7117117117117004, 1.4801801801801804)
'''

sklearn.linear_model.LinearRegression사용

공식문서

from sklearn.linear_model import LinearRegression
model = LinearRegression()
feature = ['YearBuilt','GarageArea','GrLivArea','OverallQual','FullBath']
target = ['SalePrice']

def new(dfraw,feature,target):
    model = LinearRegression()
    X_train = dfraw[[feature]]
    y_train = dfraw[target]
    model.fit(X_train, y_train)
    print(f'{feature}\t&\t{target[0]} \ty(SalesPrice) = {round(model.coef_[0][0],3)} * {feature} + {round(model.intercept_[0],3)}')
    return model, model.coef_ , model.intercept_

+ Recent posts