// 실습 코드
# Bagging
from sklearn.ensemble import BaggingClassifier # Bagging 앙상블 학습기
from sklearn.tree import DecisionTreeClassifier # 기본 학습기로 사용할 결정 트리
from sklearn.datasets import load_breast_cancer # 유방암 데이터셋 로드
from sklearn.model_selection import train_test_split, GridSearchCV # 데이터 분할 및 하이퍼파라미터 탐색
from sklearn.metrics import accuracy_score # 모델 성능 평가를 위한 정확도 지표
import numpy as np
# 1️. 데이터 로드 및 분할
cancer = load_breast_cancer() # 유방암 데이터셋 로드
# 훈련 데이터와 테스트 데이터로 분리
X_train, X_test, y_train, y_test = train_test_split(
cancer.data, cancer.target,
stratify=cancer.target, # 클래스 비율을 유지하도록 설정
random_state=42 # 결과 재현성을 위한 랜덤 시드 고정
)
# 2️. Bagging 모델 설정
# 기본 학습기로 DecisionTreeClassifier 사용
base_dt = DecisionTreeClassifier(random_state=42)
# BaggingClassifier 생성 (결정 트리를 여러 개 앙상블로 학습)
bagging_clf = BaggingClassifier(estimator=base_dt, random_state=42)
# 3️. 하이퍼파라미터 탐색을 위한 설정 (GridSearchCV)
param_grid = {
'n_estimators': [50, 100, 200, 300], # 트리 개수 (모델 수)
'max_samples': [0.5, 0.7, 1.0], # 각 기본 모델이 학습할 샘플 비율
'max_features': [0.5, 0.7, 1.0], # 각 기본 모델이 학습할 특성(Feature) 개수 비율
}
# GridSearchCV 설정
grid_search = GridSearchCV(
bagging_clf, # BaggingClassifier 모델
param_grid, # 탐색할 하이퍼파라미터 조합
cv=5, # 5-Fold 교차 검증 수행
scoring='accuracy', # 평가 지표: 정확도 (accuracy)
n_jobs=-1 # 병렬 처리 (CPU 전체 사용)
)
# 4️. 하이퍼파라미터 최적화 수행
grid_search.fit(X_train, y_train) # 학습 데이터로 GridSearchCV 수행
# 5️. 최적 하이퍼파라미터 확인
best_params_extend = grid_search.best_params_ # 최적 하이퍼파라미터 출력
print("Best Parameters:", best_params_extend)
# 6️. 최적 모델을 사용하여 평가
best_model = grid_search.best_estimator_ # 최적 모델 가져오기
y_pred = best_model.predict(X_test) # 테스트 데이터 예측 수행
# 모델 성능 평가 (Accuracy)
accuracy = accuracy_score(y_test, y_pred)
print('Test Accuracy:', accuracy) # 테스트 정확도 출력
"""
Best Parameters: {'max_features': 0.7, 'max_samples': 0.7, 'n_estimators': 100}
Test Accuracy: 0.958041958041958
"""
데이터 변수 설명
// 실습 코드
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, precision_score,recall_score,roc_auc_score
from sklearn.metrics import f1_score, confusion_matrix,precision_recall_curve, roc_curve
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
diabetes_data = pd.read_csv('/content/drive/MyDrive/LG헬로비전_DX_School/250212/diabetes.csv')
print(diabetes_data['Outcome'].value_counts())
# 불균형 문제 -> acc만 보는 것은 신뢰하기 어려움. confusion matrix를 봐야함
"""
Outcome
0 500
1 268
Name: count, dtype: int64
"""
=> 데이터 불균형 문제 → Accuracy만으로 평가 어려움 → confusion matrix도 함께 확인해야 함
// 실습 코드
diabetes_data['Pregnancies'].hist()
plt.show()
print('평균값 : {:.2f}'.format(diabetes_data['Pregnancies'].mean()))
diabetes_data['Glucose'].hist()
print('평균값 : {:.2f}'.format(diabetes_data['Glucose'].mean()))
diabetes_data['BloodPressure'].hist()
print('평균값 : {:.2f}'.format(diabetes_data['BloodPressure'].mean()))

평균값 : 3.85

평균값 : 120.89

평균값 : 69.11
// 실습 코드
import seaborn as sns
sns.heatmap(diabetes_data.corr(method='pearson'), annot=True) # 피어슨 -> 연속형, 스피어만 -> 이산, 연속 둘다 가능
plt.show()

// 실습 코드
sns.pairplot(diabetes_data)

// 실습 코드
sns.boxplot(x='Outcome', y='Pregnancies', data=diabetes_data)
# 임신 횟수가 적을수록 당뇨병 발병이 적을 것이다.
sns.boxplot(x='Outcome', y='BMI', data=diabetes_data)
# BMI가 낮으면 당뇨병 발병이 적을 것이다. -> BMI식이 뭐지? -> 우리나라 계산방법과 다를 것이다.
sns.boxplot(x='Outcome', y='Glucose', data=diabetes_data)
sns.boxplot(x='Outcome', y='Insulin', data=diabetes_data)
# Outcome이 1인 부분은 왜 중위수가 안보이는가? -> 위 또는 아래에 붙어있을 것으로 예상(Q3 또는 Q1)




// 실습 코드
# 나이대별 발병 여부는 다를까?
sns.barplot(x='Age', y='Outcome', data=diabetes_data)
plt.show()
# -> 나이대에서 값이 고르게 분포되어 있으면 신뢰구간이 좁고,
# 나이대가 고르게 분포되어 있지 않으면 신뢰구간이 넓음.

// 실습 코드
plt.figure(figsize=(12, 6))
sns.barplot(x='Pregnancies', y='Outcome', data=diabetes_data)
plt.show()

// 실습 코드
print(diabetes_data.isna().sum())
# 결측치는 없음
// 실습 코드
# Outlier Detection -> IQR9interquantile range), 3-sigma 사용
# 구간 벗어나는 값들을 삭제하려고 함
cols = diabetes_data.columns
for col in cols:
mean = diabetes_data[col].mean()
std = diabetes_data[col].std()
threshold = mean + 3 * std
n_outlier = np.sum(diabetes_data[col] > threshold)
print(col + 'num of outlier:' + str(n_outlier))
# 0보다 작은 값들이 없으므로 3-sigma로도 충분함
"""
Pregnanciesnum of outlier:4
Glucosenum of outlier:0
BloodPressurenum of outlier:0
SkinThicknessnum of outlier:1
Insulinnum of outlier:18
BMInum of outlier:3
DiabetesPedigreeFunctionnum of outlier:11
Agenum of outlier:5
Outcomenum of outlier:0
"""
// 실습 코드
cols = diabetes_data.columns
for col in cols:
mean = diabetes_data[col].mean()
std = diabetes_data[col].std()
threshold = mean + 3 * std
diabetes_data.drop(diabetes_data[diabetes_data[col]>threshold].index[:],inplace=True)
diabetes_data.dropna()
print('after drop outlier:{}'.format(diabetes_data.shape))
# after drop outlier:(706, 9)
// 실습 코드
X = diabetes_data.iloc[:, :-1]
y = diabetes_data.iloc[:, -1]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
// 실습 코드
lr_clf = LogisticRegression(solver = 'liblinear')
lr_clf.fit(X_train,y_train)
pred = lr_clf.predict(X_test)
pred_proba = lr_clf.predict_proba(X_test)
// 실습 코드
def get_clf_eval(y_test=None, pred=None):
confusion = confusion_matrix(y_test, pred)
accuracy = accuracy_score(y_test, pred)
precision = precision_score(y_test, pred)
recall = recall_score(y_test, pred)
f1 = f1_score(y_test, pred)
# ROC-AUROC 추가
roc_auc = roc_auc_score(y_test, pred)
print('오차 행렬')
print(confusion)
# ROC-AUROC print 추가
print('정확도: {0:.4f}, 정밀도: {1:.4f}, 재현율: {2:.4f}, F1: {3:.4f}, AUC:{4:.4f}'.format(accuracy, precision, recall, f1, roc_auc))
get_clf_eval(y_test,pred)
"""
오차 행렬
[[83 12]
[19 28]]
정확도: 0.7817, 정밀도: 0.7000, 재현율: 0.5957, F1: 0.6437, AUC:0.7347
"""
// 실습 코드
from sklearn.metrics import classification_report
print(classification_report(y_test, pred))
# f1_score -> 정밀도, 민감도(재현율) -> 조화평균
# 재현율이 낮음. -> threshold -> recall -> 강화를 해줄 수 있음
// 실습 코드
def precision_recall_curve_plot(y_test=None, pred_proba_c1=None):
precisions, recalls, thresholds = precision_recall_curve(y_test, pred_proba_c1)
# X축을 threshold값으로, Y축은 정밀도, 재현율 값으로 각각 Plot 수행. 정밀도는 점선으로 표시
plt.figure(figsize=(8, 6))
threshold_boundary = thresholds.shape[0]
plt.plot(thresholds, precisions[0:threshold_boundary], linestyle='--', label='precision')
plt.plot(thresholds, recalls[0:threshold_boundary], label='recall')
# threshold 값 X축의 Scale을 0, 1 단위로 변경
start, end = plt.xlim()
plt.xticks(np.round(np.arange(start, end, 0.1), 2))
# x축, y축 label과 legend, grid 설정
plt.xlabel('Threshold value'); plt.ylabel('Precision and Recall value')
plt.legend(); plt.grid()
plt.show()
pred_proba_c1 = lr_clf.predict_proba(X_test)[:, 1]
precision_recall_curve_plot(y_test, pred_proba_c1)
"""
precision recall f1-score support
0 0.81 0.87 0.84 95
1 0.70 0.60 0.64 47
accuracy 0.78 142
macro avg 0.76 0.73 0.74 142
weighted avg 0.78 0.78 0.78 142
"""

// 실습 코드
zero_features = ['Glucose', 'BloodPressure', 'SkinThickness', 'Insulin', 'BMI']
total_count = diabetes_data['Glucose'].count()
for feature in zero_features:
zero_count = diabetes_data[diabetes_data[feature] == 0][feature].count()
print('{0} 0 건수는 {1}, 퍼센트는 {2:.2f} %'.format(feature, zero_count, 100*zero_count/total_count))
# zero_feature 리스트 내부에 저장된 개별 피쳐들에 대해서 0값을 평균값으로 대체
diabetes_data[zero_features] = diabetes_data[zero_features].replace(0, diabetes_data[zero_features].mean())
print('after drop outlier : {}'.format(diabetes_data.shape))
"""
Glucose 0 건수는 0, 퍼센트는 0.00 %
BloodPressure 0 건수는 0, 퍼센트는 0.00 %
SkinThickness 0 건수는 0, 퍼센트는 0.00 %
Insulin 0 건수는 0, 퍼센트는 0.00 %
BMI 0 건수는 0, 퍼센트는 0.00 %
after drop outlier : (706, 9)
"""
// 실습 코드
X = diabetes_data.iloc[:, :-1]
y = diabetes_data.iloc[:, -1]
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size = 0.2, random_state = 156, stratify=y)
lr_clf = LogisticRegression()
lr_clf.fit(X_train , y_train)
pred = lr_clf.predict(X_test)
get_clf_eval(y_test , pred)
"""
오차 행렬
[[88 7]
[21 26]]
정확도: 0.8028, 정밀도: 0.7879, 재현율: 0.5532, F1: 0.6500, AUC:0.7398
"""
// 실습 코드
# Binarizer : 연속형 변수의 이항 변수화(binarization)
from sklearn.preprocessing import Binarizer
def get_eval_by_threshold(y_test, pred_proba_c1, thresholds):
for custom_threshold in thresholds:
binarizer = Binarizer(threshold=custom_threshold).fit(pred_proba_c1)
custom_predict = binarizer.transform(pred_proba_c1)
print('임계값:', custom_threshold)
get_clf_eval(y_test, custom_predict)
thresholds = [0.3, 0.33, 0.36, 0.39, 0.42, 0.45, 0.48, 0.50]
pred_proba = lr_clf.predict_proba(X_test)
get_eval_by_threshold(y_test, pred_proba[:, 1].reshape(-1, 1), thresholds)
"""
임계값: 0.3
오차 행렬
[[75 20]
[12 35]]
정확도: 0.7746, 정밀도: 0.6364, 재현율: 0.7447, F1: 0.6863, AUC:0.7671
임계값: 0.33
오차 행렬
[[78 17]
[14 33]]
정확도: 0.7817, 정밀도: 0.6600, 재현율: 0.7021, F1: 0.6804, AUC:0.7616
임계값: 0.36
오차 행렬
[[81 14]
[15 32]]
정확도: 0.7958, 정밀도: 0.6957, 재현율: 0.6809, F1: 0.6882, AUC:0.7667
임계값: 0.39
오차 행렬
[[82 13]
[18 29]]
정확도: 0.7817, 정밀도: 0.6905, 재현율: 0.6170, F1: 0.6517, AUC:0.7401
임계값: 0.42
오차 행렬
[[84 11]
[19 28]]
정확도: 0.7887, 정밀도: 0.7179, 재현율: 0.5957, F1: 0.6512, AUC:0.7400
임계값: 0.45
오차 행렬
[[86 9]
[20 27]]
정확도: 0.7958, 정밀도: 0.7500, 재현율: 0.5745, F1: 0.6506, AUC:0.7399
임계값: 0.48
오차 행렬
[[87 8]
[21 26]]
정확도: 0.7958, 정밀도: 0.7647, 재현율: 0.5532, F1: 0.6420, AUC:0.7345
임계값: 0.5
오차 행렬
[[88 7]
[21 26]]
정확도: 0.8028, 정밀도: 0.7879, 재현율: 0.5532, F1: 0.6500, AUC:0.7398
"""