0. 시작
2024.08.11 - [[Deep daiv.] 복습] - [Deep daiv.] TIL - 4강. 지도 학습(분류)
과 목표는 똑같습니다.
선수들의 데이터를 모아 포지션을 예측하는 문제입니다.
이번에는 다양한 기법을 통해 해당 모델의 성능을 향상시켜보겠습니다.
1. 데이터 전처리
4강. 지도학습과 동일합니다.
2. 랜덤 포레스트
랜덤 포레스트는 다수의 의사 결정 나무를 결합하여 예측을 수행하는 앙상블 학습 방법입니다.
이 기법은 각 개별 트리의 예측을 결합하여 더 강력한 성능을 제공하며, 특히 과적합(* overfitting) 을 방지하는 데 효과적인 방법입니다.
랜덤 포레스트의 핵심 수학적 아이디어는 다수의 약한 학습자를 결합하여 강한 학습자를 만드는 것입니다.
이는 Bagging 과 Randomness 로 구현됩니다.
Bagging
Bootstrap Aggregation 이라고도 불리는 Bagging 은 주어진 학습 데이터를 여러 번 샘플링하여 각각의 샘플로 학습시키고, 그 결과를 회귀의 경우 평균화, 분류의 경우 다수결 투표를 하여 최종 예측을 수행하는 방식입니다.
이 과정에서 각 트리는 서로 다른 샘플로 학습되므로, 과적합을 줄이는 데 도움이 됩니다.
Randomness
랜덤성은 2가지 방법을 통해 도입됩니다.
- Feature Randomness(특성의 랜덤 추출): 각 노드를 분리할 때 전체 특징 중 일부를 무작위로 선택하여 그 중 최적의 분할 기준을 찾습니다. 이는 트리들 간의 상관성을 줄이고, 앙상블의 성능을 향상시킵니다.
- Bootstrap Samples: 학습 데이터의 무작위 서브샘플링을 통해 각 트리를 학습시킵니다. 이 과정에서 일부 데이터는 포함되지 않을 수 있으며, 이를 OOB(Out-of-Bag) 데이터라 합니다.
특징
- 비선형성 처리: 의사 결정 나무와 마찬가지로 비선형 데이터를 잘 처리합니다.
- 다양성, 각 트리가 다른 샘플과 특성으로 학습되기 때문에, 모델이 데이터의 특정 부분에 과적합되지 않습니다.
- OOB 평가: OOB 데이터를 사용하여 모델의 성능을 별도의 검증 데이터 없이 평가할 수 있습니다.
장점
- 과적합 방지: 개별 트리가 약한 학습자일지라도, 다수의 트리를 결합함으로써 강력한 모델을 형성하며 과적합을 방지합니다.
- 높은 성능: 다양한 트리의 결과를 결합하므로, 단일 의사 결정 나무에 비해 높은 예측 성능을 보입니다.
- 다양한 데이터 타입 지원: 수치형, 범주형 데이터를 포함한 다양한 유형의 데이터를 처리할 수 있습니다.
- Feature Importance: 각 특징의 중요도를 제공하여 해석 가능성을 높입니다.
단점
- 계산 비용: 많은 수의 트리를 생성해야 하므로 계산 비용이 높습니다.
- 메모리 사용: 각 트리를 독립적으로 저장해야 하므로 메모리 사용량이 큽니다.
- 훈련 시간: 대규모 데이터 셋의 경우 훈련 시간이 길어질 수 있습니다.
랜덤 포레스트를 이용하여 모델을 생성해보겠습니다.
# 랜덤 포레스트
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier(max_depth = 15, random_state = 1201)
rf.fit(X_train, y_train)
y_pred = rf.predict(X_test)
acc = accuracy_score(y_test, y_pred)
print('Random Forest 모델의 정확도:', '%.4f' %acc)
# Ramdom Forest 모델의 정확도: 0.7056
# 특징 중요도 출력
importances = rf.feature_importances_
for feature, importance in zip(X_train.columns, importances):
print(f'{feature}: {importance:.f}')
'''
Crossing: 0.05
Finishing: 0.04
HeadingAccuracy: 0.07
ShortPassing: 0.02
Volleys: 0.02
Dribbling: 0.03
Curve: 0.02
FKAccuracy: 0.02
LongPassing: 0.04
BallControl: 0.02
Acceleration: 0.03
SprintSpeed: 0.03
Agility: 0.03
Reactions: 0.01
Balance: 0.02
ShotPower: 0.01
Jumping: 0.02
Stamina: 0.02
Strength: 0.04
LongShots: 0.02
Aggression: 0.02
Interceptions: 0.05
Positioning: 0.06
Vision: 0.04
Penalties: 0.02
Composure: 0.01
StandingTackle: 0.05
SlidingTackle: 0.06
GKDiving: 0.02
GKHandling: 0.02
GKKicking: 0.02
GKPositioning: 0.02
GKReflexes: 0.03
'''
만약 더 좋은 성능을 내고 싶다면 XGBoost 를 사용할 수도 있습니다.
# XGBoost
import xgboost
from xgboost import XGBClassifier
xgb = XGBClassifier(max_depth = 15)
xgb.fit(X_train, y_train)
y_pred = xgb.predict(X_test)
acc = accuracy_score(y_test, y_pred)
print('XGBoost 모델의 정확도:', '%.4f'%acc)
# XGBoost 모델의 정확도: 0.7183
3. Logistic Regression
Logistic Regression 은 이진 분류 문제에서 주로 사용되는 통계 모델로, 주어진 입력 데이터가 특정 클래스에 속할 확률을 예측하는 데 사용됩니다.
Logistic Regression 은 회귀 분석의 일종이지만, 출력값이 연속적인 값 대신에 특정 범주(0~1)로 제한된다는 점에서 일반적인 회귀 분석과 차별화됩니다.
Logistic Regression 의 수학적 아이디어는 로지스틱 함수, 또는 시그모이드 함수를 사용하여 입력값을 확률로 변환하는 것입니다. 이 확률을 기반으로, 주어진 샘플이 특정 클래스에 속할지를 예측하게 됩니다.
Logistic Regression의 핵심은 로지스틱 함수입니다. 이 함수는 선형 회귀 모델의 출력을 0과 1 사이의 값으로 변환하여 확률을 모델링합니다.
로지스틱 함수
로지스틱 함수는 다음과 같이 정의됩니다.
이때 x 에 들어가는 것이 선형 회귀 모델의 출력값이 됩니다.
이 함수는 입력 x가 매우 크거나 작을 때는 출력이 1 또는 0에 가까워지며, x가 0일 때는 0.5가 됩니다.
즉, 로지스틱 함수는 선형 회귀의 결과를 확률 값으로 변환합니다.
결정 함수
Logistic Regression 은 로지스틱 함수를 통해 특정 클래스에 속할 확률을 예측하며, 일반적으로 0.5를 기준으로 결정을 내립니다. 즉, 출력 확률이 0.5 이상이면 해당 클래스로, 그렇지 않으면 다른 클래스로 분류됩니다.
비용 함수
Logistic Regression에서는 이진 크로스엔트로피(Binary Cross-Entropy) 또는 로그 손실(Log Loss) 을 비용 함수로 사용합니다 .이 함수는 모델의 예측이 실제 클래스와 얼마나 차이가 나는지를 측정합니다.이 비용 함수를 최소화하기 위해 경사 하강법등의 최적화 알고리즘이 사용됩니다.
특징
- 이진 분류에 적합: Logistic Regression 은 기본적으로 이진 분류에 사용되며, 다중 클래스 분류는 One-vs-Rest 기법을 통해 확장할 수 있습니다.
- 확률적 해석: 모델의 출력은 특정 클래스에 속할 확률로 해석될 수 있으므로, 예측 결과에 대한 신뢰도를 제공할 수 있습니다.
- 선형 결정 경계: Logistic Regression 은 선형 결정 경계를 가지므로, 데이터가 선형적으로 구분 가능한 경우에 효과적입니다.
장점
- 해석 용이성: Logistic Regression 의 계수는 각 특징이 출력에 미치는 영향을 직관적으로 이해할 수 있게 해줍니다.
- 효율성: Logistic Regression 은 계산이 비교적 간단하고 빠르게 수행됩니다. 특히 대규모 데이터셋에서 효과적입니다.
- 오버피팅 방지: 필요에 따라 L1, L2 정규화를 통해 오버피팅을 방지할 수 있습니다.
단점
- 비선형 데이터에 대한 한계: Logistic Regression 은 선형 모델이기 때문에, 비선형적으로 분리된 데이터에는 성능이 떨어질 수 있습니다.
- 다중 클래스 분류의 복잡성: 기본적으로 이진 분류만을 처리하므로, 다중 클래스 문제에서는 추가적인 전략이 필요합니다.(* SoftMax 함수)
- 이상치에 민감: 모델이 이상치에 민감할 수 있으며, 이는 예측 성능에 부정적인 영향을 미칠 수 있습니다.
로지스틱 회귀를 사용해보고, 로지스틱 회귀의 계수를 통해 각 특성들의 가중치를 살펴보겠습니다.
(* 다중 클래스 분류에서의 로지스틱 회귀의 경우, 각 클래스 별로 특성 개수만큼의 가중치가 존재합니다.)
# Logistic Regression
from sklearn.linear_model import LogisticRegression
lr = LogisticRegression(max_iter = 1000)
lr.fit(X_train, y_train)
y_pred = lr.predict(X_test)
acc = accuracy_score(y_test, y_pred)
print('Logistic Regression 모델의 정확도:', '%.4f'%acc)
# Logistic Regression 모델의 정확도: 0.7530
# Logistic Regression 의 계수
matrix = pd.DataFrame(lr.coef_, columns = X_train_columns, index = encoder.inverse_transform(range(15))
plt.figure(figsize = (13,3), dpi = 150)
sns.heatmap(matrix, cmap = 'vlag')
plt.show()
결과
이를 통해 ST를 분류하는 특성들의 계수들은 Finishing 이 가장 높게 정해졌다는 것,
CB의 경우, StandingTackle 을 가장 중요하게 본다는 점 등을 알 수 있습니다.
한편, 모델이 어떤 클래스들을 틀렸는지를 살펴본다면,
모델 성능의 개선 방향을 알 수 있습니다.
y_test_label = encoder.inverse_transform(y_test)
y_pred_label = encoder.inverse_transform(y_pred)
# 실제 값과 예측 값을 비교
error = pd.DataFrame(zip(y_test_label, y_pred_label), columns = ['test', 'pred'])
# 피벗 테이블을 통해 히트맵 시각화를 위한 표 만들기
error['count'] = 1 # 이를 통해 틀린 개수를 보다 쉽게 볼 수 있습니다.
error = error.pivot_table(values = 'count', columns = 'pred', index = 'test', aggfunc = 'sum').copy()
# 히트맵 시각화
plt.figure(figsize = (7,3), dpi = 150)
sns.heatmap(error, cmp = 'flare')
plt.show()
결과
위 결과를 통해 모델은 RB와 LB, RM과 LM 을 구분하기 힘들어 한다는 점을 알 수 있습니다.
그렇다면 이를 위해 '주발' 특성을 학습 특성에 추가시켜 모델의 성능을 끌어올릴 수 있습니다.
4. 피처 엔지니어링
특성 공학이라고도 불리는 피처 엔지니어링은 말 그대로 성능을 개선하기 위해 특성을 조작하는 것입니다.
방법은 정말 다양한데, 지난 시간에 학습한 PCA를 활용하여 Feature Extraction 하는 방법도 있고,
특성의 일부만 활용하여 Feature Selection 하는 방법도 있습니다.
'주발'의 정보를 데이터 셋에 추가시켜 보겠습니다.
# Preferred Foot 값 확인
df['Preferred Foot'].unique()
# array(['Right', 'Left'], dtype=object)
# 선호하는 발 특성 추가
X = pd.concat([X, df['Preferred Foot']], axis = 1)
# 선호하는 발 정수 인덱싱
X['Preferred Foot'] = X['Preferred Foot'].map(lambda foot: 1 if foot == 'Right' else 0)
# 새로운 결과 확인
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3, random_state = 1201)
pipeline = Pipeline([('scaler', StandardScaler), ('lr', LogisticRegression(solver = 'lbdgs', max_iter = 1000))])
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
acc = accuracy_score(y_test, y_pred)
print('Logistic Regression 모델의 정확도:', '%.4f'%acc)
# Logistic Regression 모델의 정확도: 0.8129
5. 하이퍼 파라미터 튜닝
앞서 k-NN의 k나 Decision Tree 의 max_depth 와 같은 값은 모델의 결과에 큰 영향을 미칠 수 있는 중요한 하이퍼 파라미터입니다.
최적의 하이퍼 파라미터를 찾기 위한 방법론들이 있지만, 가장 좋은 방법은 직접 다 넣어보는 것입니다.
쉽게 하이퍼 파라미터에 따른 결과를 비교해볼 수 있도록 사이킷런에서는 GridSearch 를 제공합니다.
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3, random_state = 1201)
from sklearn.model_selection import StratifiedKFold
from sklearn.model_selection import GridSearchCV
# 모델 파이프라인 설정
pipeline = Pipeline([('scaler', StandardScaler()), ('lr', LogisticRegression(max_iter = 1000))])
# 하이퍼 파라미터 설정
solvers = ['newton-cg', 'lbfgs', 'liblinear', 'sag', 'saga']
c_values = [100, 10, 1.0, 0.1, 0.01]
# GridSearch 적용
grid = dict(lr__solver = solvers, lr__C = c_values) # c_value 는 정규화 정도
# CrossValidation 적용
cv = StratifiedKFold(n_split = 3, random_state = 1201, shuffle = True)
# GridSearchCV 학습
grid_search = GridSearchCV(estimator = pipeline, param_grid = grid, n_jobs = 1, cv = cv, scoring = 'accuracy', error_score = 0)
grid_result = grid_search.fit(X_train, y_train)
# 결과 요약
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
print("%f (%f) with: %r" % (mean, stdev, param))
# 결과 요약
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
print("%f (%f) with: %r" % (mean, stdev, param))
'''
Best: 0.799094 using {'lr__C': 1.0, 'lr__solver': 'lbfgs'}
0.797897 (0.000209) with: {'lr__C': 100, 'lr__solver': 'newton-cg'}
0.797897 (0.000209) with: {'lr__C': 100, 'lr__solver': 'lbfgs'}
0.759340 (0.002602) with: {'lr__C': 100, 'lr__solver': 'liblinear'}
0.797640 (0.000209) with: {'lr__C': 100, 'lr__solver': 'sag'}
0.797726 (0.000242) with: {'lr__C': 100, 'lr__solver': 'saga'}
0.798239 (0.000320) with: {'lr__C': 10, 'lr__solver': 'newton-cg'}
0.798324 (0.000242) with: {'lr__C': 10, 'lr__solver': 'lbfgs'}
0.759255 (0.002585) with: {'lr__C': 10, 'lr__solver': 'liblinear'}
0.798324 (0.000121) with: {'lr__C': 10, 'lr__solver': 'sag'}
0.798068 (0.000436) with: {'lr__C': 10, 'lr__solver': 'saga'}
0.799008 (0.002098) with: {'lr__C': 1.0, 'lr__solver': 'newton-cg'}
0.799094 (0.002200) with: {'lr__C': 1.0, 'lr__solver': 'lbfgs'}
0.758913 (0.002770) with: {'lr__C': 1.0, 'lr__solver': 'liblinear'}
0.798923 (0.002304) with: {'lr__C': 1.0, 'lr__solver': 'sag'}
0.798923 (0.002304) with: {'lr__C': 1.0, 'lr__solver': 'saga'}
0.788835 (0.003481) with: {'lr__C': 0.1, 'lr__solver': 'newton-cg'}
0.788920 (0.003370) with: {'lr__C': 0.1, 'lr__solver': 'lbfgs'}
0.747115 (0.004012) with: {'lr__C': 0.1, 'lr__solver': 'liblinear'}
0.788920 (0.003498) with: {'lr__C': 0.1, 'lr__solver': 'sag'}
0.788835 (0.003481) with: {'lr__C': 0.1, 'lr__solver': 'saga'}
0.754894 (0.004930) with: {'lr__C': 0.01, 'lr__solver': 'newton-cg'}
0.754723 (0.005148) with: {'lr__C': 0.01, 'lr__solver': 'lbfgs'}
0.706762 (0.005767) with: {'lr__C': 0.01, 'lr__solver': 'liblinear'}
0.754894 (0.004930) with: {'lr__C': 0.01, 'lr__solver': 'sag'}
0.754809 (0.005039) with: {'lr__C': 0.01, 'lr__solver': 'saga'}
'''
배움
파이프라인
KFold, StratifiedKFold
'[Deep daiv.] > [Deep daiv.] 복습' 카테고리의 다른 글
[Deep daiv.] TIL - 7. 딥러닝 (1) | 2024.08.18 |
---|---|
[Deep daiv.] TIL - 6. Word2Vec을 활용한 단어 임베딩 (1) | 2024.08.16 |
[Deep daiv.] TIL - 4.1 k-NN 알고리즘과 의사 결정 나무 (1) | 2024.08.11 |
[Deep daiv.] TIL - 4강. 지도 학습(분류) (0) | 2024.08.11 |
[Deep daiv.] TIL - 3.1 차원축소와 클러스터링 (0) | 2024.08.09 |