1. K-Nearest Neighbor Classifier (KNN)
1-1. 데이터 포인트 간의 거리 - 2D
이번 실습에서는 KNN 모델로 영화 평가 분류기를 구현해볼 것입니다. 먼저 데이터 포인트들 간의 거리 개념부터 알아봅니다.
두 점이 서로 가깝거나 멀리 떨어져 있는 정도를 측정하기 위해 거리 공식을 사용할 것입니다.
이 예제의 경우 데이터의 차원은 다음과 같습니다.
- 영화의 러닝타임
- 영화 개봉 연도
스타워즈와 인디아나 존스를 예로 들겠습니다. 스타워즈는 125분이며 1977년에 개봉했습니다. 인디아나 존스는 115분이며 1981년에 개봉했습니다.
이 두 영화를 의미하는 두 데이터 포인트들의 거리는 아래와 같이 계산됩니다.
Practice 1
1. movie1과 movie2라는 두 리스트을 매개 변수로 사용하는 distance라는 함수를 선언합니다.
- 각 리스트의 첫 번째 인덱스는 영화의 러닝타임이고 두 번째 인덱스는 영화의 개봉 연도입니다. distance 함수는 두 리스트 사이의 거리를 반환해야 합니다.
2. 아래 영화에 대해 distance 함수를 호출하여 거리를 비교해봅니다.
10.770329614269007
38.897300677553446
1-2. 데이터 포인트 간의 거리 - 3D
영화의 길이와 개봉일만을 기준으로 영화 평가 분류기를 만드는 것은 상당히 제한적입니다. 영화의 다른 속성을 데이터에 포함하여 세번째 차원을 추가해 보겠습니다.
추가된 세 번째 차원은 영화 예산입니다. 이제 영화를 나타내는 두 데이터 포인트들의 거리를 3차원으로 찾아야 합니다.
데이터의 차원이 삼차원보다 많아지면 시각화 하는 것은 어렵지만 그래도 거리를 구할 수 있습니다.
N차원의 데이터 포인트 A와 B 사이의 거리 공식은 다음과 같습니다.
여기서 A1−B1은 각 데이터 포인트의 첫 번째 feature 간의 차입니다. An−Bn은(는) 각 점의 마지막 feature 간의 차입니다.
이 공식을 이용하면 N차원 공간에서 한 데이터 포인트의 K-Nearest Neighbors를 찾을 수 있습니다.
이 거리를 사용하여 label되지 않은 데이터 포인트의 가장 가까운 이웃을 찾아 결과적으로 분류를 하게 됩니다.
Practice 2
1. distance 함수를 N개의 차원의 데이터의 거리를 반환하는 함수로 수정합니다.
2. 위 활동에서 주어진 영화들에 영화 예산 속성이 추가되었습니다. 아래 영화에 대해 distance 함수를 호출하여 거리를 비교해봅니다.
7000000.000008286
6000000.000126083
1-3. 척도가 다른 데이터: 정규화
이 활동에서는 K-Nearest Neighborhood 알고리즘의 첫 번째 단계를 구현합니다.
- 데이터를 정규화합니다.
- 가장 가까운 이웃인 k를 찾습니다.
- 이러한 이웃을 기준으로 새로운 포인트를 분류합니다.
우리가 영화 예산 속성을 추가하면 데이터의 범위가 변화한것을 확인할 수 있습니다.
영화 개봉 연도와 영화 예산을 살펴보겠습니다. 어떠한 두 영화의 개봉 날짜의 최대 차이는 약 125년입니다((루미에르 브라더스는 1890년대에 영화를 만들고 있었습니다). 하지만, 어떠한 두 영화의 예산 차이는 수백만 달러가 될 수 있습니다.
거리 공식의 문제는 규모에 상관없이 모든 차원을 동등하게 취급한다는 것입니다. 이는 1년 차이가 나는 영화 개봉 년도를 1달러의 차이의 영화 예산과 같은 정도로 취급한다는 뜻입니다.
이 문제에 대한 해결책은 정규화입니다. 이는 모든 값은 0과 1 사이로 변환합니다.
이번 실습은 최소-최대 정규화(Min-Max Normalization)를 사용할 것입니다.
참고: scikit-learn https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.MinMaxScaler.html#sklearn.preprocessing.MinMaxScaler
1-3-1. Normalization vs Standardization
Practice 3
[0.047619047619047616, 0.8492063492063492, 0.8650793650793651, 0.4523809523809524,
0.5634920634920635, 0.46825396825396826, 0.6666666666666666, 0.5476190476190477,
1.0, 0.36507936507936506, 0.6111111111111112, 0.8333333333333334,
0.42063492063492064, 0.0, 0.8253968253968254, 0.4523809523809524,
0.9523809523809523, 0.5873015873015873, 0.0, 0.6904761904761905]
1-4. Nearest Neighbors 찾기
K-Nearest Neighbors 알고리즘의 두번째 단계를 구현해보겠습니다.
- 데이터를 정규화합니다.
- 가장 가까운 이웃 k를 찾습니다.
- 이러한 이웃을 기준으로 새로운 포인트를 분류합니다.
Label되지 않은 데이터 포인터를 분류하기 위해서는 이 데이터 포인트와 가장 가까운 k개의 이웃을 찾아야 합니다. 가장 적합한 k를 찾는 방법도 있지만, 일단은 k=5로 정하도록 하겠습니다.
가장 가까운 5개의 이웃을 찾기 위해서는 label되지 않은 데이터 포인트를 데이터의 다른 모든 데이터 포인트와의 거리를 계산하여 비교합니다. 이번 실습에서 구현할 함수의 반환값은 label되지 않은 한 영화에 대해 데이터의 모든 영화와의 거리가 정렬된 리스트입니다.
반환값의 예시는 다음과 같습니다.
[
[0.30, 'Superman II'],
[0.31, 'Finding Nemo'],
...
...
[0.38, 'Blazing Saddles']
]
이 예에서 label되지 않은 영화와 슈퍼맨2 영화와의 거리는 0.30입니다.
Practice 4
1. 위에 주어진 영화 데이터 중 The Avengers 영화를 출력해봅니다. 각 영화의 세 가지 feature은 다음과 같습니다.
- 정규화된 영화 예산(달러)
- 정규화된 러닝타임(분)
- 정규화된 개봉 연도
위 데이터의 label은 좋은 영화와 나쁜 영화를 나타냅니다. The Avengers 영화의 label은 1로, 좋은 영화입니다. 분류 기준은 IMDb에서 7.0 이상의 평가를 받으면 좋은 영화로 분류됩니다.
[0.018009887923225047, 0.4641638225255973, 0.9550561797752809]
1
2. classify 함수를 선언하고 구현해봅니다.
3. 아래 주어진 매개변수로 classify 함수를 테스트하고 결과를 출력합니다.
- [.4, .2, .9]
- movie_dataset
- 5
[[0.08273614694606074, 'Lady Vengeance'], [0.22989623153818367, 'Steamboy'], [0.23641372358159884, 'Fateless'], [0.26735445689589943, 'Princess Mononoke'], [0.3311022951533416, 'Godzilla 2000']]
1-5. Neighbors 세기
이 활동에서는 K-Nearest Neighbor 알고리즘 중 마지막 단계를 구현합니다.
- 데이터를 정규화합니다.
- 가장 가까운 이웃인 k를 찾습니다.
- 이웃을 기준으로 새로운 포인트를 분류합니다.
이전 활동에서 어떠한 label되지 않은 데이터 포인트에 대해 가장 가까운 이웃 k개를 찾아 다음과 같은 형태로 리스트에 저장했습니다.
[
[0.083, 'Lady Vengeance'],
[0.236, 'Steamboy'],
...
...
[0.331, 'Godzilla 2000']
]
이 리스트의 좋고 나쁜 영화의 갯수를 셈으로써 분류를 할 수 있습니다. 만약 좋은 영화로 분류된 이웃들의 갯수가 더 많아면, label되지 않은 영화는 좋은 영화로 분류됩니다. 반대의 경우에는 나쁜 영화로 분류됩니다.
만약 좋은 영화로 분류된 이웃들의 갯수와 나쁜 영화로 분류된 이웃들의 갯수가 같다면 가장 거리가 가까운 영화의 label을 선택하게 됩니다.
Practice 5
- 이 활동에서는 위의 classify 함수를 수정하여 unknown 데이터 포인트에 대한 분류값을 반환하겠습니다. labels 매개 변수를 classify함수에 추가합니다.
- num_good와 num_bad라는 두 변수를 만들고 각각 0으로 초기화합니다.
- labels과 title을 사용하여 각 영화의 label을 받아옵니다.
- 해당 라벨이 0이면 num_bad에 1을 증가시킵니다.
- 해당 라벨이 1이면 num_good에 1을 증가시킵니다.
- 이제 label되지 않은 영화를 분류할 수 있습니다.
- num_good가 num_bad보다 크면 1을 반환합니다.
- 그렇지 않으면 0을 반환합니다.
5. 다음 파라미터를 사용하여 classify 함수를 호출하여 결과를 출력해봅니다.
- 분류하려는 영화는 [.4, .2, .9] 입니다.
- 훈련 데이터는 movie_dataset 입니다.
- 훈련 데이터의 label은 movie_labels입니다.
- k는 5로 지정합니다.
1
1-6. 하나의 데이터 포인트 분류하기
위의 활동까지는 임의의 데이터인 [.4, .2, .9]에만 테스트 해보았습니다. 이번 활동에서는 실제 영화 Call Me By Your Name 데이터를 정규화하고 예측해 볼 것입니다.
1. 먼저 분류하고자 하는 영화가 훈련 데이터에 있지는 않은지 확인해야 합니다.
False
2. 분류하고자 하는 영화가 훈련 데이터에 없는 것으로 확인 되면 my_movie 변수를 생성하고 영화 예산, 러닝타임, 개봉 연도의 순으로 데이터를 구성합니다.
Call Me By Your Name의 예산은 35만 달러, 런타임은 132분, 개봉 연도는 2017년입니다.
3. 주어진 normalize_dimension 함수를 사용하여 my_movie를 정규화합니다. normalized_my_movie라는 변수를 만들고 my_movie의 정규화된 값을 저장합니다. 결과를 출력해봅니다.
[0.00028650338197026213, 0.3242320819112628, 1.0112359550561798]
4. normalized_my_movie, movie_dataset, movie_labels, k=5 매개 변수를 사용하여 "classify"를 호출하고 결과를 출력합니다.
1
1-7. scikit-learn으로 KNN 구현하기
사용 데이터: https://archive.ics.uci.edu/dataset/17/breast+cancer+wisconsin+diagnostic
사용 모델: https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html
Index(['diagnosis', 'radius_mean', 'texture_mean', 'perimeter_mean', 'area_mean', 'smoothness_mean', 'compactness_mean', 'concavity_mean', 'concave points_mean', 'symmetry_mean', 'fractal_dimension_mean', 'radius_se', 'texture_se', 'perimeter_se', 'area_se', 'smoothness_se', 'compactness_se', 'concavity_se', 'concave points_se', 'symmetry_se', 'fractal_dimension_se', 'radius_worst', 'texture_worst', 'perimeter_worst', 'area_worst', 'smoothness_worst', 'compactness_worst', 'concavity_worst', 'concave points_worst', 'symmetry_worst', 'fractal_dimension_worst'], dtype='object')
diagnosis False
radius_mean False
texture_mean False
perimeter_mean False
area_mean False
smoothness_mean False
compactness_mean False
concavity_mean False
concave points_mean False
symmetry_mean False
fractal_dimension_mean False
radius_se False
texture_se False
perimeter_se False
area_se False
smoothness_se False
compactness_se False
concavity_se False
concave points_se False
...
B 357
M 212
Name: diagnosis, dtype: int64
0.9590643274853801
1-8. 가장 적합한 k 찾기
9
2. Random Forest
앙상블(ensemble)은 기존에 존재하는 기계학습 알고리즘으로 구축된 여러 모델의 예측을 결합하여 단일 모델에 비해 generalizability / robustness을 향상시키는 방법입니다.
앙상블 방법은 보통 두 가지 계열로 구분됩니다.
- 평균화(averaging) 방법: 여러 개의 모델을 독립적으로 생성 후 예측을 평균화합니다. 결합 추정기는 분산(variance)이 감소하기 때문에 일반적으로 단일 기본 모델보다 낫습니다.
- 부스팅(boosting) 방법: 기본 모델이 순차적으로 구축되고 결합된 모델의 편향(bias)을 줄이려고 합니다. 강력한 앙상블을 만들기 위해 몇 가지 약한 모델을 결합하는 방법입니다.
가장 대표적인 앙상블 모델은 랜덤 포레스트(random forest)이며, 이 모델은 기본 구성 요소로 결정 트리를 사용합니다.
결정 트리의 단점은 훈련 데이터에 과적합되는 경향이 있다는 것입니다. 랜덤 포레스트는 조금씩 다른 여러 결정 트리를 묶어 과적합 문제를 피할 수 있습니다. 랜덤 포레스트의 원리는, 각 트리는 비교적 예측을 잘 할 수 있지만 데이터의 일부에 과적합하는 경향을 가진다는 데 기초합니다. 잘 작동하되 서로 다른 방향으로 과적합된 트리를 많이 만들면 그 결과를 평균냄으로써 과적합된 양을 줄일 수 있습니다. 이렇게 하면 트리 모델의 예측 성능이 유지되면서 과적합이 줄어드는 것이 수학적으로 증명되었습니다.
2-1. Random Forest: Bagging (=Bootstrap Aggregation)
Bootstrap sample은 더 큰 샘플에서 "bootstrap"된 더 작은 샘플입니다. Bootstraping은 동일한 크기의 작은 샘플을 하나의 원래 샘플에서 대량으로 반복적으로 추출하는 리샘플링의 한 유형입니다.
통계에 대한 표본 분포를 생성하기 위해 모집단(population)에서 반복되지 않는 큰 표본을 그리는 것이 이상적입니다. 그렇지 않은 작은 데이터에서 bootstrap sample은 모집단 모수(population parameter)에 대해 상당히 좋은 근사치가 될 수 있습니다.
Decision tree은 훈련 데이터에 매우 민감합니다. 훈련 데이터를 약간 변경하면 트리 구조가 크게 달라질 수 있습니다. 랜덤 포레스트는 각 개별 트리가 데이터 세트에서 무작위로 대체하여 샘플을 추출할 수 있도록 하여 서로 다른 트리를 생성함으로써 이를 활용합니다. 이 과정을 bagging이라고 합니다.
2-2. Random Forest: Feature Randomness
일반적인 의사 결정 트리에서 노드를 분할할 때, 모든 feature을 고려하여 왼쪽 노드와 오른쪽 노드 사이에 불순도가 가장 낮도록 분할합니다. 반면에 랜덤 포리스트의 각 트리는 feature의 random subset에서만 선택할 수 있습니다. 이로 인해 모델의 트리 간에 훨씬 더 많은 변동이 발생하고 궁극적으로 트리 간 상관 관계가 낮아지고 더 다양화됩니다.
결론적으로, 이러한 과정을 통해 구성된 랜덤 포레스트는 bagging을 통해 각기 다른 데이터셋에 훈련됨과 동시에 각기 다른 feature을 사용한 여러개의 결정 트리로 이루어지게 됩니다.
2-3. Scikit-learn Random Forest
scikit-learn RandomForestClassifier: https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html
다섯 개의 트리가 만든 결정 경계는 확연하게 다르다는 것을 알 수 있습니다. Bootstrap sampling 때문에 한쪽 트리에 나타나는 훈련 포인트가 다른 트리에는 포함되지 않을 수 있어 각 트리는 불완전합니다.
랜덤 포레스트는 개개의 트리보다는 덜 과적합되고 훨씬 좋은 결정 경계를 만들어줍니다. 실제 애플리케이션에서는 매우 많은 트리를 사용하기 때문에(수백, 수천 개) 더 부드러운 결정 경계가 만들어집니다.
scikit-learn에서 제공하는 유방암 데이터셋에 100개의 트리로 이뤄진 랜덤 포레스트를 적용해보겠습니다.
array(['mean radius', 'mean texture', 'mean perimeter', 'mean area', 'mean smoothness', 'mean compactness', 'mean concavity', 'mean concave points', 'mean symmetry', 'mean fractal dimension', 'radius error', 'texture error', 'perimeter error', 'area error', 'smoothness error', 'compactness error', 'concavity error', 'concave points error', 'symmetry error', 'fractal dimension error', 'worst radius', 'worst texture', 'worst perimeter', 'worst area', 'worst smoothness', 'worst compactness', 'worst concavity', 'worst concave points', 'worst symmetry', 'worst fractal dimension'], dtype='<U23')
Random Forest Training Set Accuracy: 1.000
Random Forest Test Set Accuracy: 0.965
랜덤 포레스트는 아무런 매개변수 튜닝 없이도 선형 모델이나 단일 결정 트리보다 높은 정확도를 내고 있습니다. 단일 결정 트리에서 한 것처럼 max_features 매개변수를 조정하거나 사전 가지치기를 할 수도 있습니다. 하지만 랜덤 포레스트는 기본 설정으로도 좋은 결과를 만들어줄 때가 많습니다.
결정 트리처럼 랜덤 포레스트도 특성 중요도를 제공하는데 각 트리의 특성 중요도를 취합하여 계산한 것입니다. 일반적으로 랜덤 포레스트에서 제공하는 특성 중요도가 하나의 트리에서 제공하는 것보다 더 신뢰할 만합니다.
Decision Tree Training Set Accuracy: 1.000
Decision Tree Test Set Accuracy: 0.951
array([0. , 0.02601101, 0. , 0. , 0. ,
0. , 0. , 0.69593688, 0. , 0. ,
0. , 0. , 0. , 0.01277192, 0.00155458,
0. , 0.00670697, 0.01702539, 0. , 0. ,
0.0877369 , 0.10787925 , 0. , 0.03452044, 0.00985664,
0. , 0. , 0. , 0. , 0. ])
array([0.03971058, 0.01460399, 0.05314639, 0.04277978, 0.00816485,
0.01140166, 0.08321459, 0.0902992 , 0.00443533, 0.00443395,
0.01951684, 0.00459978, 0.00868228, 0.04355077, 0.00464415,
0.0036549 , 0.00701442, 0.00504716, 0.00371411, 0.00658253,
0.08127686, 0.01649014, 0.07138828, 0.12319232, 0.01033481,
0.01580059, 0.03174022, 0.17229521, 0.01310266, 0.00518165])
그림에서 알 수 있듯이 랜덤 포레스트에서는 단일 트리의 경우보다 훨신 많은 특성이 0 이상의 중요도 값을 갖습니다. 랜덤 포레스트를 만드는 무작위성은 알고리즘이 가능성 있는 많은 경우를 고려할 수 있도록 하므로, 그 결과 랜덤 포레스트가 단일 트리보다 더 넓은 시각으로 데이터를 바라볼 수 있습니다.
3. Ensemble Methods: Bagging and Boosting
3-1. Bagging (=Bootstrap Aggregation)
scikit-learn: https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.BaggingClassifier.html
BaggingClassifier은 분류기가 predict_proba() 메서드를 지원하는 경우 확률값을 평균하여 예측을 수행합니다. 그렇지 않은 분류기를 사용할 때는 가장 빈도가 높은 클래스 레이블이 예측 결과가 됩니다.
3-1-1. Out-of-Bag error (OOB)
Out-of-Bag error은 기계학습에서 bagging을 사용한 모델의 성능을 측정합니다. OOB error은 모델 훈련에 사용되지 않은 데이터에 대해 계산됩니다. OOB error은 bootstrap 훈련 데이터셋에 OOB 샘플이 포함되지 않은 결정 트리의 하위 집합만 사용하여 계산됩니다.
OOB error은 validation score와 다른 성능 측정치입니다. 때때로 데이터 세트의 크기가 충분하지 않기 때문에 validation dataset을 구성하기 어려운 경우가 있습니다. 대규모 데이터가 아니며, 모든 데이터를 훈련 데이터셋으로 사용하려는 경우 OOB error으로 모델의 성능을 판단할 수 있습니다.
Train set accuracy: 0.813
Test set accuracy: 0.880
OOB sample score: 0.807
결과 그래프는 랜덤 포레스트의 결정 경계와 매우 비슷합니다.
Train set accuracy: 1.000
Test set accuracy: 0.900
OOB sample score: 0.900
배깅은 랜덤 포레스트와 달리 max_samples 매개변수에서 부트스트랩 샘플의 크기를 지정할 수 있습니다. 또한 랜덤 포레스트는 DecisionTreeClassifier(splitter=“best”)를 사용하도록 고정되어 있습니다. 결정 트리를 splitter=‘random’으로 설정하면 무작위로 분할한 후보 노드 중에서 최선의 분할을 찾습니다.
3-2. Boosting
Adaboost(=Adaptive Boosting)은 ensemble method의 boosting method 중 가장 유명한 방법입니다. 다른 boosting 방법들 중에는 XGBoost, GradientBoost, 그리고 BrownBoost 등이 있습니다. Adaboost은 일련의 단일 모델을 반복적으로 가중치가 부여된 데이터에 학습시킵니다. 그 다음 모든 예측은 가중 다수결 (혹은 합계)를 통해 결합되어 최종 예측을 생성합니다.
처음에는 가중치가 모든 데이터 포인트에 1/N으로 동일하게 설정됩니다. 순차적으로 훈련을 반복하면서 가중치는 개별적으로 수정되고, 단일 모델은 재조정된 데이터에 다시 훈련됩니다. 잘못 예측된 데이터는 가중치가 증가하며, 올바르게 예측된 데이터는 가중치가 감소합니다. 훈련이 반복될수록 예측하기 어려운 데이터가 더욱 많은 가중치를 받게 됩니다. 이러한 원리 때문에 boosting이라고 불립니다.
scikit-learn의 AdaBoostClassifier는 기본적으로 DecisionTreeClassifier(max_depth=1)를 사용하고 AdaBoostRegressor는 decisionTreeRegressor(max_depth=3)를 사용하지만 base_estimator 매개변수에서 다른 모델을 지정할 수도 있습니다. 순차적으로 학습해야 하기 때문에 n_jobs 매개변수를 지원하지 않습니다.
scikit-learn: https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.AdaBoostClassifier.html
scikit-learn: https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.AdaBoostRegressor.html
AdaBoostClassifier는 깊이가 1인 결정 트리를 사용하기 때문에 각 트리의 결정 경계가 하나의 직선입니다. 앙상블된 결정 경계도 다른 앙상블 모델에 비해 더 단순합니다.
Train set accuracy: 1.000
Test set accuracy: 0.944
AdaBoost의 특성 중요도를 확인해보면 다른 모델에서 부각되지 않았던 compactness error 특성을 크게 강조하고 있습니다.
3-2-1. Bagging vs Boosting
4. Feature Selection
새로운 특성을 만드는 방법이 많으므로 데이터의 차원이 원본 특성의 수 이상으로 증가하기 쉽습니다. 그러나 특성이 추가되면 모델은 더 복잡해지고 과적합될 가능성도 높아집니다. 보통 새로운 특성을 추가할 때나 고차원 데이터셋을 사용할 때, 가장 유용한 특성만 선택하고 나머지는 무시해서 특성의 수를 줄이는 것이 좋습니다. 이렇게 하면 모델이 간단해지고 일반화 성능이 올라갑니다.
이를 위한 전략으로 1) 일변량 통계 (univariate statistics), 2) 모델 기반 선택 (model-based selection), 3) 반복적 선택 (iterative selection)이 있습니다. 이 방법들은 모두 지도 학습 방법이므로 최적값을 찾으려면 타깃값이 필요합니다. 그리고 데이터를 훈련 세트와 테스트 세트로 나눈 다음 훈련 데이터만 특성 선택에 사용해야 합니다.
4-1. 일변량 통계
일변량 통계에서는 개개의 특성과 타깃 사이에 중요한 통계적 관계가 있는지를 계산합니다. 그런 다음 깊게 관련되어 있다고 판단되는 특성을 선택합니다. 분산분석(ANOVA)이라고도 합니다.
이 방식의 핵심 요소는 일변량, 즉 각 특성이 독립적으로 평가된다는 점입니다. 따라서 다른 특성과 깊게 연관된 특성은 선택되지 않을 것입니다. 일변량 분석은 계산이 매우 빠르고 평가를 위해 모델을 만들 필요가 없습니다. 한편으로 이 방식은 특성을 선택한 후 적용하려는 모델에 상관없이 사용할 수 있습니다.
scikit-learn에서 일변량 분석으로 특성을 선택하려면 분류에서는 f-classif을, 회귀에서는 f_regression을 보통 선택하여 테스트하고, 계산한 p-value에 기초하여 특성을 제외하는 방식을 선택합니다. 이런 방식들은 매우 높은 p-value를 가진 특성(즉, 타깃값과 연관성이 작은 특성)을 제외할 수 있도록 임계값을 조정하는 매개변수를 사용합니다. 임계값을 계산하는 방법은 각각 다르며, 가장 간단한 SelectKBest는 고정된 k개의 특성을 선택하고, SelectPercentile은 지정된 비율만큼 특성을 선택합니다. 그럼 cancer 데이터셋에 분류를 위한 특성 선택을 적용해보겠습니다. 문제를 조금 복잡하게 하기위해 의미 없는 노이즈 특성을 데이터에 추가하겠습니다. 특성 선택이 이 의미 없는 특성을 식별해서 제거하는지 보겠습니다.
scikit-learn: https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.SelectPercentile.html
scikit-learn: https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.f_classif.html
(569, 30)
(569, 80)
결과에서 볼 수 있듯이 특성 개수가 80개에서 40개로 줄었습니다. (원본 특성의 50%)
[ True True True True True True True True True True True False True True False True True True False False True True True True True True True True True True False False False True False True False False False False False False False True False False False True False False False False False False True False False False True False False False True False False False True True True False False False True True False False False False True True]
마스킹된 그래프에서 볼 수 있듯이 선택된 특성은 대부분 원본 특성이고 노이즈 특성이 거의 모두 제거되었습니다. 그러나 원본 특성이 완벽하게 복원된 것은 아닙니다. 전체 특성을 이용했을 때와 선택된 특성만 사용했을 때 로지스틱 회귀의 성능을 비교해보겠습니다.
전체 특성을 사용한 정확도 0.9614035087719298
선택된 일부 특성을 사용한 정확도 0.9649122807017544
이 경우에서는 일부 원본 특성이 없더라도 노이즈 특성을 제거한 쪽의 성능이 더 높습니다. 이 예는 인위적으로 간단하게 만든 예제이고 실제 데이터에서의 결과는 보통 엇갈리는 경우도 많습니다. 하지만 너무 많은 특성때문에 모델을 만들기가 현실적으로 어려울 때 일변량 분석으로 사용하여 특성을 선택하면 큰 도움이 될 수 있습니다. 또는 많은 특성들이 확실히 도움이 안 된다고 생각될 때 사용할 수 있습니다.
4-1-1. Selecting K best features
SelectPercentile 말고도 K 개의 중요한 특성을 선택하는 방법이 있습니다. SelectKBest를 이용하면 가능합니다. SelectKBest는 사용자가 지정한 k개의 중요한 특성을 반환해줍니다. 다만 위에서 사용한 데이터에는 음의 값이 있기 때문에 SelectKBest를 사용할 수 없습니다. 따라서 이 예제는 Iris 데이터를 사용합니다.
scikit-learn: https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.SelectKBest.html
scikit-learn: https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.chi2.html#sklearn.feature_selection.chi2
(150, 4)
(150, 2)
4.2 모델 기반 특성 선택
모델 기반 특성 선택은 지도 학습 머신러닝 모델을 사용하여 특성의 중요도를 평가해서 가장 중요한 특성들만 선택합니다. 특성 선택에 사용되는 지도 학습 모델은 최종적으로 사용할 지도 학습 모델과 같을 필요는 없습니다. 특성 선택을 위한 모델은 각 특성의 중요도를 측정하여 순서를 매길 수 있어야 합니다. 결정 트리와 이를 기반으로 한 모델은 각 특성의 중요도가 담겨있는 feature_importance_ 속성을 제공합니다. 일변량 분석과는 반대로 모델 기반 특성 선택은 한번에 모든 특성을 고려하므로 (사용된 모델이 상호작용을 잡아낼 수 있다면) 상호작용 부분을 반영할 수 있습니다. 모델 기반의 특성 선택은 SelectFromModel에 구현되어 있습니다.
scikit-learn: https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.SelectFromModel.html
SelectFromModel은 지도 학습 모델로 계산된 중요도가 지정한 임계치보다 큰 모든 특성을 선택합니다. 일변량 분석으로 선택한 특성과 결과를 비교하기 위해 절반 가량의 특성이 선택될 수 있도록 중간값을 임계치로 사용하겠습니다. 트리 100개로 만든 랜덤포레스트 분류기를 사용해 특성 중요도를 계산합니다. 이는 매우 복잡한 모델이고 일변량 분석보다는 훨씬 강력한 방법입니다.
X_train.shape: (284, 80)
X_train_l1.shape: (284, 40)
이번에는 두 개를 제외한 모든 원본 특성이 선택되었습니다. 특성을 40개 선택하도록 지정했으므로 일부 노이즈 특성도 선택되었습니다.
test score 0.9508771929824561
3.3 반복적 특성 선택
반복적 특성 선택(iterative feature selection)에서는 특성의 수가 각기 다른 일련의 모델이 만들어집니다. 기본적으로 두가지 방법이 있습니다.
- 특성을 하나도 선택하지 않은 상태로 시작해서 어떤 종료 조건에 도달할 때 까지 하나씩 추가하는 방법
- 모든 특성을 가지고 시작해서 어떤 종료 조건이 될 때까지 특성을 하나씩 제거해가는 방법
일련의 모델이 만들어지기 때문에 이 방법은 앞서 소개한 방법들보다 계산 비용이 월씬 많이 듭니다. 재귀적 특성 제거 (Recursive Feature Elimination, RFE)가 이런 방법의 하나입니다. 이 방법은 모든 특성으로 시작해서 모델을 만들고 특성 중요도가 가장 낮은 특성을 제거합니다. 그런 다음 제거한 특성을 빼고 나머지 특성 전체로 새로운 모델을 만듭니다. 이런 식으로 미리 정의한 특성 개수가 남을 때까지 계속합니다. 이를 위해 모델 기반 선택에서처럼 특성 선택에 사용할 모델은 특성의 중요도를 결정하는 방법을 제공해야 합니다. 다음은 앞에서와 같은 랜덤 포레스트 모델을 사용합니다.
일변량 분석이나 모델 기반 선택보다 특성 선택이 나아졌지만, 여전히 특성 한개를 놓쳤습니다. 랜덤포레스트 모델은 특성이 누락될 때마다 다시 학습하므로 40번이나 실행됩니다. 그래서 이 코드를 실행하면 모델 기반 선택보다 훨씬 오래 걸립니다.
test score 0.9578947368421052
0.9508771929824561
RFE안에 있는 랜덤 포레스트의 성능이 이 모델에서 선택한 특성으로 만든 로지스틱 회귀의 성능과 비슷합니다. 다른 말로 하면, 특성 선택이 제대로 되면 선형 모델의 성능은 랜덤 포레스트와 견줄만 합니다.
머신 러닝 알고리즘에 어떤 입력값을 넣을지 확신이 안선다면 특성 자동 선택이 도움이 될 수 있습니다. 또한 예측속도를 높이거나 해석하기 더 쉬운 모델을 만드는 데 필요한 만큼 특성의 수를 줄이는 데도 효과적입니다.
'대학공부 > 기계학습' 카테고리의 다른 글
실습 5차시: 성능 평가, cross validation (0) | 2023.10.20 |
---|---|
실습 4차시: 퍼셉트론, MLP (0) | 2023.10.20 |
실습 3차시: Linear/Logistic Regression (0) | 2023.10.13 |
실습 2차시: DT (0) | 2023.10.13 |
실습 1차시: ZeroR, OneR, Naive Bayes Classifier (0) | 2023.10.11 |