내일배움캠프(본캠프)

[내일배움캠프] 본캠프 5/1

anscodus 2026. 5. 1. 20:36

오늘 한 일

문제풀이

데이터분석 심화

 

오전 피드백

결측치 방법 별 머신러닝 -> 성능 비교

threshold = 0.3 : 재현율 상승. 30% 확률부터 불량으로 판정

반도체 공정에서 모델을 돌릴떄 threshold 설정값과 관련된 논문을 찾기(기준x, 근거를 찾아서)

성능 비교 barplot 시각화
낮춰가면서 성능 비교 -> 최적의 threshold 찾기 -> ROC curve로 비교

KNN으로 결정 한 근거에 머신러닝의 성능 비교도 있으면 좋겠다.(mean median knn) 비교

 

- 결측치 처리 근거

더보기
from sklearn.ensemble import RandomForestClassifier
from sklearn.impute import SimpleImputer, KNNImputer # KNNImputer 추가
from sklearn.metrics import recall_score, f1_score, roc_auc_score
from sklearn.preprocessing import StandardScaler # 스케일링을 위해 필요
from imblearn.over_sampling import SMOTE # SMOTE를 위해 필요

# 1. 결측치 처리 방식 정의
imputers_comparison = {
    'Mean': SimpleImputer(strategy='mean'),
    'Median': SimpleImputer(strategy='median'),
    'KNN': KNNImputer(n_neighbors=5) # KNN Imputer 객체 생성
}

results_imputation_comparison = {} # 결과 저장을 위한 새로운 딕셔너리

for name, imputer in imputers_comparison.items():
    print(f"\n--- Processing with {name} Imputation ---")

    # 원본 결측치가 있는 데이터 (X_train, X_test)에 Imputer 적용
    X_train_imputed_current = pd.DataFrame(imputer.fit_transform(X_train), columns=X_train.columns)
    X_test_imputed_current = pd.DataFrame(imputer.transform(X_test), columns=X_test.columns)

    # 결측치 처리 후 스케일링 적용
    scaler_current = StandardScaler()
    X_train_scaled_current = pd.DataFrame(scaler_current.fit_transform(X_train_imputed_current), columns=X_train.columns)
    X_test_scaled_current = pd.DataFrame(scaler_current.transform(X_test_imputed_current), columns=X_test.columns)

    # SMOTE 적용 (불균형 해소)
    smote_current = SMOTE(random_state=42)
    X_resampled_current, y_resampled_current = smote_current.fit_resample(X_train_scaled_current, y_train)

    # 모델 학습 (RandomForestClassifier)
    rf_model_current = RandomForestClassifier(n_estimators=100, random_state=42)
    rf_model_current.fit(X_resampled_current, y_resampled_current)

    # 예측 및 평가
    y_pred_current = rf_model_current.predict(X_test_scaled_current)
    y_prob_current = rf_model_current.predict_proba(X_test_scaled_current)[:, 1]

    # 결과 저장
    results_imputation_comparison[name] = {
        'Recall': recall_score(y_test, y_pred_current),
        'F1-Score': f1_score(y_test, y_pred_current),
        'AUC': roc_auc_score(y_test, y_prob_current)
    }

# 결과 출력
print("\n--- 결측치 처리 방법별 RandomForest 모델 성능 비교 --- ")
display(pd.DataFrame(results_imputation_comparison).T)
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd

# 결과 DataFrame 생성 (이미 생성된 results_imputation_comparison 사용)
results_df = pd.DataFrame(results_imputation_comparison).T

# DataFrame을 long format으로 변환하여 seaborn barplot에 적합하게
results_melted = results_df.reset_index().melt(id_vars='index', var_name='Metric', value_name='Score')
results_melted = results_melted.rename(columns={'index': 'Imputation Method'})

plt.figure(figsize=(12, 7))
sns.barplot(x='Metric', y='Score', hue='Imputation Method', data=results_melted, palette='viridis')

plt.title('Imputation Method Performance Comparison (RandomForest)', fontsize=16)
plt.xlabel('Evaluation Metric', fontsize=12)
plt.ylabel('Score', fontsize=12)
plt.legend(title='Imputation Method', bbox_to_anchor=(1.05, 1), loc='upper left')
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.tight_layout()
plt.show()

 

 KNN이 가장 적합하다고 볼 수 있다.

- 어제 AUC 결과 LightGBM이 가장 성능이 좋은것이라 판단. LGBM으로 최종 모델 결정. 

- LGBM으로 임계값 최적화

더보기
import lightgbm as lgb
import pandas as pd
import numpy as np
from sklearn.metrics import recall_score, precision_score, f1_score, confusion_matrix

# 2. 테스트 데이터에 대한 예측 확률 추출 (1번 클래스: 불량일 확률)
y_probs = lgbm_model.predict_proba(X_test_scaled)[:, 1]

# 3. 임계값 후보 설정
thresholds = [0.5, 0.4, 0.3, 0.2, 0.1]
results = []

print(f"{'Threshold':<12} | {'Recall':<10} | {'Precision':<10} | {'F1-Score':<10} | {'False Alarms'}")
print("-" * 75)

for t in thresholds:
    # 임계값 적용
    y_pred_t = (y_probs >= t).astype(int)

    # 지표 계산
    recall = recall_score(y_test, y_pred_t)
    precision = precision_score(y_test, y_pred_t)
    f1 = f1_score(y_test, y_pred_t)

    # 오경보(정상인데 불량으로 판정) 개수 확인 (혼동 행렬의 FP)
    tn, fp, fn, tp = confusion_matrix(y_test, y_pred_t).ravel()

    results.append({
        'Threshold': t,
        'Recall': recall,
        'Precision': precision,
        'F1-Score': f1,
        'False Positives (오보)': fp
    })

    print(f"{t:<12.1f} | {recall:<10.4f} | {precision:<10.4f} | {f1:<10.4f} | {fp}")

# 데이터프레임으로 정리
df_results = pd.DataFrame(results)
display(df_results)

 

 

해석 : 

해석
- 0.5 : 재현율과 정밀도가 비교적 균형. F1-Score도 높고 오경보 수도 낮은 편(기본 임계값)

- 0.3 : 재현율이 크게 높아졌으나, 정밀도 낮아짐. 오경보 증가 -> 불량을 더 정확히 감지하지만 어느 정도의 오경보는 감수 해야 함

- 0.1 :  재현율 가장 높음. 그러나 정밀도가 매우 낮고 오경보 급증 -> 정상을 불량으로 잘못 판단하는 경우가 매우 많아진다

최적 임계값 선택

- 재현율이 가장 중요한 경우(불량을 절대 놓치면 안 될 때) : 0.2~0.3

- 정밀도가 가장 중요한 경우(오경보를 최소화 해야 할 때) : 0.5~

- 재현율, 정밀도의 균형이 중요할 때 : F1-Score가 높은 임계값이 적합.

- 최종 결론

  1. 결측치 처리 : KNN
  2. 머신러닝 성능 : LightGBM
  3. 임계값(Threshold) : 0.3

- 다중공선성 확인 - VIF

더보기
from statsmodels.stats.outliers_influence import variance_inflation_factor
from statsmodels.tools.tools import add_constant

# VIF 계산을 위해 상수항 추가
X_train_vif = add_constant(X_train_final)

# VIF 계산 및 DataFrame으로 저장
vif_data = pd.DataFrame()
vif_data["feature"] = X_train_vif.columns
vif_data["VIF"] = [variance_inflation_factor(X_train_vif.values, i) for i in range(X_train_vif.shape[1])]

# 상수항(const) 제거 및 VIF 값 기준으로 정렬
vif_data = vif_data[vif_data['feature'] != 'const'].sort_values(by='VIF', ascending=False).reset_index(drop=True)

print("--- Features VIF Check ---")
display(vif_data)

 

VIF 확인 결과 stage2 flow랑 stage4 viscosity가 VIF 70이상이 나왔고, stage2 n, stage3 n, flow, stage4 co2, o2,stage5 flow가 10 이상으로 나왔다. 나머지는 모두 10 이하로 나옴.

- Feature Importance

더보기
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd

# 한글 폰트 설정
plt.rcParams['font.family'] = ['NanumGothic', 'Malgun Gothic', 'sans-serif']
plt.rcParams['axes.unicode_minus'] = False # 마이너스 기호 깨짐 방지

# LightGBM 모델의 feature importance 추출
feature_importances = lgbm_model.feature_importances_
feature_names = X_train_final.columns

# Feature importance를 DataFrame으로 변환
importance_df = pd.DataFrame({
    'Feature': feature_names,
    'Importance': feature_importances
})

# 중요도 기준으로 내림차순 정렬
importance_df = importance_df.sort_values(by='Importance', ascending=False)

# 가로 barplot으로 시각화
plt.figure(figsize=(12, 10))
sns.barplot(x='Importance', y='Feature', hue='Feature', data=importance_df, palette='viridis', legend=False)
plt.title('LightGBM Feature Importance', fontsize=16)
plt.xlabel('Importance', fontsize=12)
plt.ylabel('Feature Name', fontsize=12)
plt.grid(axis='x', linestyle='--', alpha=0.7)
plt.tight_layout()
plt.show()