1. 분석을 위해 필요한 라이브러리 및 데이터 불러오기¶
- Python에서는 pandas, seaborn, matplotlib, scikit-learn, tensorflow 등을 사용합니다.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_theme(style="whitegrid", palette="muted")
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import roc_curve, auc
import tensorflow as tf
from tensorflow import keras
- 그래프에 한글을 출력하고자 할 때에는 한글 폰트를 지정해야 합니다.
!apt-get -qq install fonts-nanum
'apt-get' is not recognized as an internal or external command, operable program or batch file.
from matplotlib import rc
rc('font', family='NanumGothic') # 그래프에 한글 출력시 폰트 지정해야 함
- KNHANES(국민건강영양조사) 2019년도 데이터를 R로 불러옵니다.
- 실습 편의상 미리 준비된 HN19_lec3.csv 파일을 불러 옵니다.
head()
함수를 통해 불러온 데이터의 앞부분을 확인 가능하며,n = 5
와 같이 표시할 행의 수를 명시할 수 있습니다.
# SPSS 데이터 불러오기
hn19 = pd.read_csv("HN19_lec3.csv")
hn19.head(n = 5)
age | HE_BMI | HE_HP | HE_DM_HbA1c | |
---|---|---|---|---|
0 | 61 | 25.987394 | 3.0 | 3.0 |
1 | 28 | 16.900942 | 1.0 | 1.0 |
2 | 53 | 19.781829 | 2.0 | 1.0 |
3 | 50 | 26.631647 | 1.0 | 2.0 |
4 | 16 | NaN | NaN | NaN |
2. 변수 생성 및 결측 처리¶
- 주어진 데이터로부터 BMI(체질량지수), 고혈압 여부, 당뇨 여부를 나타내는 범주형 변수를 생성합니다.
- 본래 고혈압 여부는 HE_HP에 1 = 정상, 2 = 고혈압 전단계, 3 = 고혈압으로 코딩돼 있는데 이를 hp에 0 = 정상, 1 = 고혈압 전단계 또는 고혈압으로 변환하고, 당뇨 여부는 HE_DM_HbA1c에 1 = 정상, 2 = 당뇨병 전단계, 3 = 당뇨병으로 코딩돼 있는데 이를 diabetes에 0 = 정상, 1 = 당뇨병 전단계 또는 고혈압으로 코딩합니다.
- 결측치(NA)가 포함된 행을 제거합니다.
- 분석에 필요한 변수만 선택합니다.
head()
와 동일한 방법으로,tail()
을 이용해 데이터의 뒷부분을 확인할 수 있으며,n = 3
과 같이 표시할 행의 수를 명시할 수 있습니다.
hn19 = hn19.assign(
bmi=hn19['HE_BMI'],
hp=np.where(hn19['HE_HP'].isin([2,3]), 1, 0),
diabetes=np.where(hn19['HE_DM_HbA1c'].isin([2,3]), 1, 0)
).dropna(subset=['bmi','hp','diabetes'])[['age','bmi','hp','diabetes']]
hn19.tail(3)
age | bmi | hp | diabetes | |
---|---|---|---|---|
8107 | 43 | 15.375148 | 0 | 0 |
8108 | 16 | 14.844970 | 0 | 0 |
8109 | 8 | 14.528000 | 0 | 0 |
3. 상관관계 분석 및 시각화¶
변수 간 상관관계를 확인하면 데이터 구조와 변수 간 관계를 이해하는 데 도움이 됩니다.
상관계수(correlation coefficient)는 두 변수 간 선형 관계를 -1 과 1 사이의 값으로 나타냅니다.
Python에서 상관관계는 수치형 설명변수끼리만 구할 수 있습니다.
우선 반응변수
y
와 수치형 설명변수age
,bmi
,hp
,diabetes
의 상관계수를 구하고 이를 시각화합니다.상관계수를 구할 변수들인
y
,age
,bmi
,hp
,diabetes
를 뽑아내고,corr()
함수를 이용해 상관계수 행렬을 만듭니다.
corr = hn19[['age','bmi','hp','diabetes']].corr()
- 상관계수 행렬을 시각화하기 위해
seaborn
패키지의heatmap()
함수를 이용합니다.
sns.heatmap(corr, annot=True, cmap='coolwarm', center=0)
plt.title("변수 간 상관계수 Heatmap")
plt.show()
4. BMI와 당뇨병 관계 시각화¶
- 변수 간 관계를 시각화하면 데이터의 패턴을 쉽게 확인할 수 있습니다.
seaborn
라이브러리의stripplot
을 이용해 반응변수와 설명변수에 대한 산점도를 그립니다.- 설명변수 중
BMI
를 선택해 x축으로 하고, 반응변수인diabetes
를 y축으로 하는 산점도를 그립니다. - 반응변수인
diabetes
가 0 또는 1이므로 그냥 사용하면y=0
또는y=1
에 겹쳐지게 됩니다. 따라서y
에 임의로 노이즈를 더한 걸y_jitter
로 놓고 이를 이용하면 점들이y=0
또는y=1
주변으로 흩뿌려지게 합니다.
y_jitter = hn19['diabetes'] + np.random.uniform(-0.05, 0.05, size=len(hn19))
sns.scatterplot(x='bmi', y=y_jitter, data=hn19, alpha=0.4)
plt.title("BMI vs diabetes")
plt.show()
5. 로지스틱 회귀 (2변수 모형) 및 예측 확률 시각화¶
- 로지스틱 회귀는 종속변수가 이항형(0/1)일 때 확률을 예측하는데 사용됩니다.
- 주어진 데이터에서 우선 설명변수를 2개 사용하여
age
,bmi
를 설명변수로,diabetes
를 반응변수로 하는 로지스틱 회귀모형을 적합합니다. - R에서 로지스틱 회귀모형을 적합하는 경우
sklearn.linear_model
의LogisticRegression
을 이용합니다.
model_logistic_2d = LogisticRegression(max_iter=1000)
model_logistic_2d.fit(hn19[['age','bmi']], hn19['diabetes'])
LogisticRegression(max_iter=1000)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
LogisticRegression(max_iter=1000)
- 시각화를 위해, 격자 데이터를
grid
에 생성합니다. - 격자의 각 지점별 당뇨 확률을 계산해
pred_logistic_2d
에 저장합니다. seaborn
라이브러리의contourf()
를 이용해 색상으로 시각화합니다.
# grid 생성
age_seq = np.linspace(hn19['age'].min(), hn19['age'].max(), 100)
bmi_seq = np.linspace(hn19['bmi'].min(), hn19['bmi'].max(), 100)
A, B = np.meshgrid(age_seq, bmi_seq)
grid = pd.DataFrame({'age': A.ravel(), 'bmi': B.ravel()})
pred_logistic_2d = model_logistic_2d.predict_proba(grid)[:, 1].reshape(A.shape)
contour = plt.contourf(A, B, pred_logistic_2d, levels=50, cmap='RdBu', alpha=0.7)
plt.colorbar(contour, label="P(당뇨병=1)")
plt.xlabel("나이(Age)")
plt.ylabel("BMI")
plt.title("로지스틱 회귀의 당뇨병 예측 확률")
plt.show()
6. 로지스틱 회귀 (다변량) 및 잔차 진단¶
- 이번에는 설명변수를 3개 사용하여
age
,bmi
,hp
를 설명변수로,diabetes
를 반응변수로 하는 로지스틱 회귀모형을 적합합니다. - summary() 함수를 통해 계수 및 유의성, 모델 적합도를 확인합니다.
model_logistic = LogisticRegression(max_iter=1000)
model_logistic.fit(hn19[['age','bmi','hp']], hn19['diabetes'])
print("회귀계수:", model_logistic.coef_, "절편:", model_logistic.intercept_)
회귀계수: [[0.05787047 0.15102873 0.34019309]] 절편: [-6.74213772]
7. 결정 나무 (2변수와 다변량 모델 + 가지치기)¶
결정나무(Decision Tree)는 변수 공간을 여러 영역으로 나누어 각 영역마다 같은 예측값을 갖도록 하는 모델입니다.
해석이 쉽고 비선형 관계를 잡아낼 수 있지만, 깊게 성장하면 과적합(overfitting)이 발생할 수 있습니다.
이를 방지하기 위해 가지치기(pruning) 를 수행하여 최적의 복잡도를 선택합니다
주어진 데이터에서 우선 설명변수를 2개 사용하여
age
,bmi
를 설명변수로,diabetes
를 반응변수로 하는 분류 결정나무를 적합합니다.R에서 결정나무를 적합하는 경우
sklearn.tree
의DecisionTreeClassifier()
함수를 이용합니다. 함수 호출 시 전달해야 하는 인자는 다음과 같습니다.plot_tree()
함수를 사용해 적합한 결정나무를 시각화합니다.
model_tree_2d = DecisionTreeClassifier(max_depth=2, random_state=2025)
model_tree_2d.fit(hn19[['age','bmi']], hn19['diabetes'])
plt.figure(figsize=(10,5))
plot_tree(model_tree_2d, feature_names=['age','bmi'], class_names=['0','1'], filled=True)
plt.show()
- 앞에서와 마찬가지로 시각화를 합니다. 격자 데이터는 앞에서 생성한
grid
를 이용합니다. - 격자의 각 지점별 당뇨 확률을 계산해
pred_tree_2d
에 저장합니다. contourf()
을 이용해 색상으로 시각화합니다.- 결정 나무의 경우에는 경계선이 항상 축과 평행하게 나옴을 알 수 있습니다.
# 2. 결정나무 예측 확률 계산
pred_tree_2d = model_tree_2d.predict_proba(grid)[:, 1].reshape(A.shape)
# 3. 시각화
plt.figure(figsize=(8, 6))
contour = plt.contourf(A, B, pred_tree_2d, levels=50, cmap='RdBu', alpha=0.7)
plt.colorbar(contour, label="P(당뇨병=1)")
plt.xlabel("나이(Age)")
plt.ylabel("BMI")
plt.title("결정 나무의 영역별 당뇨병 예측 확률")
plt.show()
- 이번에는 설명변수를 3개 사용하여
age
,bmi
,hp
를 설명변수로,diabetes
를 반응변수로 하는 분류 결정나무를 적합하고 가지치기(pruning)을 합니다. - 실행속도 관계상 자료에서 처음 100개의 행만 골라냅니다.
- Python 에서는 결정나무를 가지치기하는 코드가 복잡해, 여기서는 간단하게 복잡도를 조절할 수 있는 파라미터로 결정 나무를 생성합니다.
ccp_alpha
파라미터를 사용해 결정나무의 복잡도를 조절합니다.
# 가지치기 정도를 alpha(=cp)로 지정 (값이 클수록 많이 가지치기)
model_ptree = DecisionTreeClassifier(random_state=42, ccp_alpha=0.01)
model_ptree.fit(hn19[['age','bmi', 'hp']].iloc[0:100], hn19['diabetes'].iloc[0:100])
# 시각화
plt.figure(figsize=(10, 6))
plot_tree(model_ptree, feature_names=['age','bmi','hp'], class_names=['0','1'], filled=True)
plt.title("Pruned Decision Tree (ccp_alpha=0.01)")
plt.show()
8. 앙상블 모형 (배깅 & 랜덤 포레스트)¶
배깅(Bagging) 은 여러 부트스트랩 샘플에 대해 모델(결정나무)을 학습하고 예측을 평균/다수결로 결합합니다.
랜덤 포레스트(Random Forest) 는 배깅에 변수 무작위 선택을 추가하여 상관성을 줄이고 성능을 향상시킵니다.
배깅과 랜덤 포레스트는 모두
sklearn.ensemble
의RandomForestClassifier
로 적합할 수 있습니다. 이는 랜덤 포레스트에서 변수의 개수를 전부 다 쓰면 그게 배깅이 되기 때문입니다.앞에서와 마찬가지로 설명변수를 3개 사용하여
age
,bmi
,hp
를 설명변수로,diabetes
를 반응변수로 하는 앙상블 모형을 생각합니다.우선 배깅 모형을 적합합니다. 이는
RandomForestClassifier
에서 사용하는 설명변수의 개수를 전체 설명변수의 개수인 3으로 지정하면 됩니다 (max_features=3).
model_bag = RandomForestClassifier(n_estimators=100, max_features=3, random_state=42)
model_bag.fit(hn19[['age','bmi', 'hp']], hn19['diabetes'])
RandomForestClassifier(max_features=3, random_state=42)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
RandomForestClassifier(max_features=3, random_state=42)
- 다음으로 랜덤포레스트 모형을 적합합니다. 이는
RandomForestClassifier
에서 사용하는 설명변수의 개수를 전체 설명변수의 개수보다 적게 지정하면 됩니다 (max_features=3).
model_rf = RandomForestClassifier(n_estimators=100, max_features=2, random_state=2025)
model_rf.fit(hn19[['age','bmi', 'hp']], hn19['diabetes'])
RandomForestClassifier(max_features=2, random_state=2025)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
RandomForestClassifier(max_features=2, random_state=2025)
9. 신경망 모형 (Neural Network)¶
신경망(Neural Network)은 입력층, 은닉층, 출력층으로 구성되며, 비선형 관계 학습에 강점이 있습니다.
충분한 데이터와 계산 자원이 필요하지만, 복잡한 패턴을 학습할 수 있습니다.
앞에서와 마찬가지로 설명변수를 3개 사용하여
age
,bmi
,hp
를 설명변수로,diabetes
를 반응변수로 하는, 은닉층(hidden layer)이 하나인 신경망 모형을 생각합니다.신경망 모형을 적합하는 패키지는 여러 가지 있지만, 여기서는 가장 간단하게 할 수 있는
neuralnet
패키지의neuralnet()
함수를 소개합니다. 다만 이 패키지는 느린 편이어서, 더 복잡한 자료에서는keras
패키지 내지는torch
패키지를 사용하면 좀 더 빠르게 실행할 수 있습니다.실행속도 관계상 자료에서 처음 100개의 행만 골라냅니다.
hidden
은 은닉층(hidden layer)의 뉴런 수입니다. 아래에서는hidden = 2
를 사용했습니다.plot()
함수로 신경망 구조를 시각화합니다.
X_nn = hn19[['age','bmi','hp']].iloc[0:100].values
y_nn = hn19['diabetes'].iloc[0:100].values
model_nn = keras.Sequential([
keras.layers.Dense(2, activation='relu', input_shape=(3,)),
keras.layers.Dense(1, activation='sigmoid')
])
model_nn.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
model_nn.fit(X_nn, y_nn, epochs=50, verbose=0)
print("신경망 정확도:", model_nn.evaluate(X_nn, y_nn, verbose=0)[1])
C:\Users\rupik\anaconda3\Lib\site-packages\keras\src\layers\core\dense.py:92: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead. super().__init__(activity_regularizer=activity_regularizer, **kwargs)
신경망 정확도: 0.49000000953674316
10. 모델 평가 (데이터 분리, Threshold 성능 분석, ROC Curve)¶
혼동행렬, 민감도, 특이도(Slide 60~64)로 모델 성능을 평가합니다.
threshold 변화에 따른 민감도·특이도 변화(ROC Curve)를 시각화하고, AUC를 계산해 분류기 성능을 종합적으로 판단합니다.
ㅅㄱ먀ㅜ
학습용/테스트용 데이터 분리를 통해 과대적합을 방지합니다.
우선 학습용/테스트용 데이터(training data/test data)를 10% 대 90%로 분리합니다.
sklearn.model_selection
의train_test_split()
함수를 사용합니다.
train, test = train_test_split(hn19, test_size=0.9, random_state=42)
X_train, y_train = train[['age','bmi','hp']], train['diabetes']
X_test, y_test = test[['age','bmi','hp']], test['diabetes']
- 다음으로,threshold 별 평가 지표(accuracy, sensitivity, specificity)를 한 번에 계산하는 코드를 함수로 작성합니다.
thresholds
에는 threshold 의 값이 벡터로 들어옵니다.actual
은 실제 반응변수의 값,predicted
는 예측된 반응변수의 값이 들어옵니다. 단, 여기서predicted
는 확률로 값이 들어가야 합니다.
def calc_metrics(thresholds, actual, predicted_prob):
results = []
for t in thresholds:
# 예측 확률이 threshold보다 크면 1(양성), 작으면 0(음성)
pred_class = (predicted_prob > t).astype(int)
# 혼동 행렬 요소 계산
TP = np.sum((pred_class == 1) & (actual == 1))
TN = np.sum((pred_class == 0) & (actual == 0))
FP = np.sum((pred_class == 1) & (actual == 0))
FN = np.sum((pred_class == 0) & (actual == 1))
# 평가 지표 계산
accuracy = (TP + TN) / (TP + TN + FP + FN)
sensitivity = TP / (TP + FN) if (TP + FN) > 0 else 0
specificity = TN / (TN + FP) if (TN + FP) > 0 else 0
results.append((t, accuracy, sensitivity, specificity))
return pd.DataFrame(results, columns=["Threshold","Accuracy","Sensitivity","Specificity"])
- thresholds 후보를 0부터 1까지 0.01 단위의 벡터로 만듭니다.
thresholds = np.arange(0, 1.01, 0.01)
id_05 = np.where(thresholds == 0.5)
- 앞과 마찬가지로
age
,bmi
,hp
를 설명변수로,diabetes
를 반응변수로 하는 모형을 생각합니다. - 아래 코드에서는 로지스틱 회귀모형을 생각했습니다. 학습 데이터(training data)에서 모형을 학습하고, 테스트 데이터(test data)에서 예측 확률을 산출합니다.
model_logistic = LogisticRegression(max_iter=1000)
model_logistic.fit(X_train, y_train)
pred_logistic = model_logistic.predict_proba(X_test)[:,1]
- 앞에서 작성한
calc_metrics()
함수로 threshold 변화 시 accuracy, sensitivity, specificity 를 계산합니다. 특히나, accuracy 에서threshold = 0.5
에 해당하는 값이 우리가 일반적으로 생각하는 정확도입니다.
metrics_logistic = calc_metrics(thresholds, y_test.values, pred_logistic)
# threshold = 0.5 정확도
acc_05 = metrics_logistic[["Accuracy"]].values[id_05][0][0]
print("Logistic Accuracy: ", acc_05)
Logistic Accuracy: 0.7511227002752426
- 위에서 계산한 값을 바탕으로 accuracy, sensitivity, specificity 를 시각화합니다.
metrics_long = metrics_logistic.melt(id_vars="Threshold",
value_vars=["Accuracy","Sensitivity","Specificity"],
var_name="Metric", value_name="Value")
plt.figure(figsize=(8,6))
sns.lineplot(data=metrics_long, x="Threshold", y="Value", hue="Metric", linewidth=1.5)
plt.xlabel("Threshold")
plt.ylabel("Metric Value")
plt.title("Accuracy / Sensitivity / Specificity")
plt.legend(title="Metric")
plt.show()
- ROC curve 를 계산 및 시각화하고 AUC 를 계산합니다.
sklearn.metrics
의roc_curve()
함수를 이용하면 됩니다.
fpr, tpr, _ = roc_curve(y_test, pred_logistic)
roc_auc = auc(fpr, tpr)
plt.figure(figsize=(6,6))
plt.plot(fpr, tpr, label=f"AUC = {roc_auc:.3f}")
plt.plot([0,1],[0,1],'k--')
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.title("ROC Curve")
plt.legend()
plt.show()
연습 문제¶
- 똑같은 데이터를 사용합니다.
1. 로지스틱 회귀 모형¶
- 설명변수를 1개 사용하여
bmi
를 설명변수로,diabetes
를 반응변수로 하는 로지스틱 회귀모형을 적합합니다. - 4단계에서 그린 BMI와 당뇨병 관계 시각화에 더해서 BMI 값 별로 로지스틱 회귀모형의 당뇨병 예측 확률을 곡선으로 그립니다.
sns.lineplot(x=hn19["diabetes"], y=pred_logistic)
과 같은 형태로 할 수 있습니다.
풀이¶
bmi
를 설명변수로,diabetes
를 반응변수로 하는 로지스틱 회귀모형을 적합합니다.
model_logistic_bmi = LogisticRegression(max_iter=1000)
model_logistic_bmi.fit(hn19[['bmi']], hn19['diabetes'])
LogisticRegression(max_iter=1000)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
LogisticRegression(max_iter=1000)
bmi
에 해당하는 격자를grid_bmi
로 생성합니다.- 격자의 각 지점별 당뇨 확률을 계산해
pred_logistic
에 저장합니다.
grid_bmi = pd.DataFrame({'bmi': np.linspace(hn19['bmi'].min(), hn19['bmi'].max(), 100)})
pred_logistic = model_logistic_bmi.predict_proba(grid_bmi)[:, 1]
- 위의 BMI와 당뇨병 관계를 시각화는 코드를 가져오고, 여기에 회귀곡선을 시각화합니다.
grid_bmi
의bmi
에 격자가,pred_logistic
에 당뇨 확률이 계산돼 있으므로sns.lineplot(x=grid_bmi['bmi'], y=pred_logistic)
같은 식으로 코드를 작성하면 됩니다.
y_jitter = hn19['diabetes'] + np.random.uniform(-0.05, 0.05, size=len(hn19))
sns.scatterplot(x='bmi', y=y_jitter, data=hn19, alpha=0.4)
sns.lineplot(x=grid_bmi['bmi'], y=pred_logistic)
plt.title("BMI vs diabetes")
plt.show()
2. 결정나무¶
- 설명변수를 1개 사용하여
bmi
를 설명변수로,diabetes
를 반응변수로 하는 분류 결정나무를 적합합니다. 가지치기는 따로 안해도 됩니다. - 4단계에서 그린 BMI와 당뇨병 관계 시각화에 더해서 BMI 값 별로 분류 결정나무의 당뇨병 예측 확률을 곡선으로 그립니다.
sns.lineplot(x=hn19["diabetes"], y=pred_logistic)
과 같은 형태로 할 수 있습니다.
풀이¶
bmi
를 설명변수로,diabetes
를 반응변수로 하는 분류 결정나무를 적합합니다.
model_tree_bmi = DecisionTreeClassifier(max_depth=2, random_state=2025)
model_tree_bmi.fit(hn19[['bmi']], hn19['diabetes'])
DecisionTreeClassifier(max_depth=2, random_state=2025)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
DecisionTreeClassifier(max_depth=2, random_state=2025)
- 앞 문제의
grid_bmi
를bmi
에 해당하는 격자로 활용합니다. - 격자의 각 지점별 당뇨 확률을 계산해
pred_tree
에 저장합니다.
pred_tree = model_tree_bmi.predict_proba(grid_bmi)[:, 1]
- 위의 BMI와 당뇨병 관계를 시각화는 코드를 가져오고, 여기에 회귀곡선을 시각화합니다.
grid_bmi
의bmi
에 격자가,pred_tree
에 당뇨 확률이 계산돼 있으므로sns.lineplot(x=grid_bmi['bmi'], y=pred_tree)
같은 식으로 코드를 작성하면 됩니다.
y_jitter = hn19['diabetes'] + np.random.uniform(-0.05, 0.05, size=len(hn19))
sns.scatterplot(x='bmi', y=y_jitter, data=hn19, alpha=0.4)
sns.lineplot(x=grid_bmi['bmi'], y=pred_tree)
plt.title("BMI vs diabetes")
plt.show()
3. 배깅¶
- 설명변수를 2개 사용하여
age
,bmi
를 설명변수로,diabetes
를 반응변수로 하는 배깅 모형을 적합합니다. - 5단계 마지막에서 했던 것처럼 age 및 BMI 값 별로 배깅 모형의 당뇨병 예측 확률을 색칠해 나타냅니다.
풀이¶
- 우선 설명변수를 2개 사용하여
age
,bmi
를 설명변수로,diabetes
를 반응변수로 하는 배깅 모형을 적합합니다.
model_bag_2d = RandomForestClassifier(n_estimators=100, max_features=2, random_state=2025)
model_bag_2d.fit(hn19[['age','bmi']], hn19['diabetes'])
RandomForestClassifier(max_features=2, random_state=2025)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
RandomForestClassifier(max_features=2, random_state=2025)
- 그러고서는 5단계 마지막에 했던 시각화를 반복합니다.
- 우선 격자 데이터를
grid
에 생성합니다. - 격자의 각 지점별 당뇨 확률을 계산해
pred_bag_2d
에 저장합니다. seaborn
라이브러리의contourf()
를 이용해 색상으로 시각화합니다.
# grid 생성
age_seq = np.linspace(hn19['age'].min(), hn19['age'].max(), 100)
bmi_seq = np.linspace(hn19['bmi'].min(), hn19['bmi'].max(), 100)
A, B = np.meshgrid(age_seq, bmi_seq)
grid = pd.DataFrame({'age': A.ravel(), 'bmi': B.ravel()})
pred_bag_2d = model_bag_2d.predict_proba(grid)[:, 1].reshape(A.shape)
contour = plt.contourf(A, B, pred_bag_2d, levels=50, cmap='RdBu', alpha=0.7)
plt.colorbar(contour, label="P(당뇨병=1)")
plt.xlabel("나이(Age)")
plt.ylabel("BMI")
plt.title("배깅의 당뇨병 예측 확률")
plt.show()
4. 랜덤 포레스트¶
- 설명변수를 2개 사용하여
age
,bmi
를 설명변수로,diabetes
를 반응변수로 하는 랜덤 포레스트 모형을 적합합니다. - 5단계 마지막에서 했던 것처럼 age 및 BMI 값 별로 랜덤 포레스트 모형의 당뇨병 예측 확률을 색칠해 나타냅니다.
풀이¶
- 우선 설명변수를 2개 사용하여
age
,bmi
를 설명변수로,diabetes
를 반응변수로 하는 랜덤 포레스트 모형을 적합합니다.
model_rf_2d = RandomForestClassifier(n_estimators=100, max_features=1, random_state=2025)
model_rf_2d.fit(hn19[['age','bmi']], hn19['diabetes'])
RandomForestClassifier(max_features=1, random_state=2025)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
RandomForestClassifier(max_features=1, random_state=2025)
- 그러고서는 5단계 마지막에 했던 시각화를 반복합니다.
- 우선 격자 데이터를
grid
에 생성합니다. - 격자의 각 지점별 당뇨 확률을 계산해
pred_rf_2d
에 저장합니다. seaborn
라이브러리의contourf()
를 이용해 색상으로 시각화합니다.
# grid 생성
age_seq = np.linspace(hn19['age'].min(), hn19['age'].max(), 100)
bmi_seq = np.linspace(hn19['bmi'].min(), hn19['bmi'].max(), 100)
A, B = np.meshgrid(age_seq, bmi_seq)
grid = pd.DataFrame({'age': A.ravel(), 'bmi': B.ravel()})
pred_rf_2d = model_rf_2d.predict_proba(grid)[:, 1].reshape(A.shape)
contour = plt.contourf(A, B, pred_rf_2d, levels=50, cmap='RdBu', alpha=0.7)
plt.colorbar(contour, label="P(당뇨병=1)")
plt.xlabel("나이(Age)")
plt.ylabel("BMI")
plt.title("랜덤 포레스트의 당뇨병 예측 확률")
plt.show()
5. 인공 신경망¶
- 설명변수를 2개 사용하여
age
,bmi
를 설명변수로,diabetes
를 반응변수로 하는 인공 신경망을 적합합니다. - 5단계 마지막에서 했던 것처럼 age 및 BMI 값 별로 인공 신경망 모형의 당뇨병 예측 확률을 색칠해 나타냅니다.
풀이¶
- 우선 설명변수를 2개 사용하여
age
,bmi
를 설명변수로,diabetes
를 반응변수로 하는 인공 신경망을 적합합니다.
X_nn = hn19[['age','bmi']].iloc[0:100].values
y_nn = hn19['diabetes'].iloc[0:100].values
model_nn_2d = keras.Sequential([
keras.layers.Dense(2, activation='relu', input_shape=(2,)),
keras.layers.Dense(1, activation='sigmoid')
])
model_nn_2d.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
model_nn_2d.fit(X_nn, y_nn, epochs=50, verbose=0)
print("신경망 정확도:", model_nn_2d.evaluate(X_nn, y_nn, verbose=0)[1])
C:\Users\rupik\anaconda3\Lib\site-packages\keras\src\layers\core\dense.py:92: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead. super().__init__(activity_regularizer=activity_regularizer, **kwargs)
신경망 정확도: 0.6499999761581421
- 그러고서는 5단계 마지막에 했던 시각화를 반복합니다.
- 우선 격자 데이터를
grid
에 생성합니다. - 격자의 각 지점별 당뇨 확률을 계산해
pred_nn_2d
에 저장합니다. seaborn
라이브러리의contourf()
를 이용해 색상으로 시각화합니다.
# grid 생성
age_seq = np.linspace(hn19['age'].min(), hn19['age'].max(), 100)
bmi_seq = np.linspace(hn19['bmi'].min(), hn19['bmi'].max(), 100)
A, B = np.meshgrid(age_seq, bmi_seq)
grid = pd.DataFrame({'age': A.ravel(), 'bmi': B.ravel()})
pred_nn_2d = model_nn_2d.predict(grid).reshape(A.shape)
contour = plt.contourf(A, B, pred_nn_2d, levels=50, cmap='RdBu', alpha=0.7)
plt.colorbar(contour, label="P(당뇨병=1)")
plt.xlabel("나이(Age)")
plt.ylabel("BMI")
plt.title("인공 신경망의 당뇨병 예측 확률")
plt.show()
313/313 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step
6. 모델 평가¶
- 설명변수를 3개 사용하여
age
,bmi
,hp
를 설명변수로,diabetes
를 반응변수로 하는 모형을 생각합니다. - 위 실습에서는 로지스틱 회귀모형에 대해서 accuracy, sensitivity, specificity 의 곡선을 그리고 roc curve 를 그렸습니다. 이를 결정나무, 배깅, 랜덤 포레스트, 인공신경망에 대해 반복합니다.
풀이¶
- 앞에서처럼 학습용/테스트용 데이터(training data/test data)를 10% 대 90%로 분리합니다.
sklearn.model_selection
의train_test_split()
함수를 사용합니다.
train, test = train_test_split(hn19, test_size=0.9, random_state=2025)
X_train, y_train = train[['age','bmi','hp']], train['diabetes']
X_test, y_test = test[['age','bmi','hp']], test['diabetes']
- 앞에서 작성했던 threshold 별 평가 지표(accuracy, sensitivity, specificity)를 한 번에 계산하는
calc_metrics
함수를 가져옵니다.
def calc_metrics(thresholds, actual, predicted_prob):
results = []
for t in thresholds:
# 예측 확률이 threshold보다 크면 1(양성), 작으면 0(음성)
pred_class = (predicted_prob > t).astype(int)
# 혼동 행렬 요소 계산
TP = np.sum((pred_class == 1) & (actual == 1))
TN = np.sum((pred_class == 0) & (actual == 0))
FP = np.sum((pred_class == 1) & (actual == 0))
FN = np.sum((pred_class == 0) & (actual == 1))
# 평가 지표 계산
accuracy = (TP + TN) / (TP + TN + FP + FN)
sensitivity = TP / (TP + FN) if (TP + FN) > 0 else 0
specificity = TN / (TN + FP) if (TN + FP) > 0 else 0
results.append((t, accuracy, sensitivity, specificity))
return pd.DataFrame(results, columns=["Threshold","Accuracy","Sensitivity","Specificity"])
- 여기에 더해, accuracy, sensitivity, specificity 를 시각화하는 함수, roc curve 를 그리는 함수를 각각 만듭니다.
def plot_metrics(thresholds, actual, predicted):
metric = calc_metrics(thresholds, actual, predicted)
metrics_long = metric.melt(id_vars="Threshold",
value_vars=["Accuracy","Sensitivity","Specificity"],
var_name="Metric", value_name="Value")
plt.figure(figsize=(8,6))
sns.lineplot(data=metrics_long, x="Threshold", y="Value", hue="Metric", linewidth=1.5)
plt.xlabel("Threshold")
plt.ylabel("Metric Value")
plt.title("Accuracy / Sensitivity / Specificity")
plt.legend(title="Metric")
plt.show()
def plot_roc(actual, predicted):
fpr, tpr, _ = roc_curve(actual, predicted)
roc_auc = auc(fpr, tpr)
plt.figure(figsize=(6,6))
plt.plot(fpr, tpr, label=f"AUC = {roc_auc:.3f}")
plt.plot([0,1],[0,1],'k--')
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.title("ROC Curve")
plt.legend()
plt.show()
- 앞에서처럼 thresholds 후보를 0부터 1까지 0.01 단위의 벡터로 만듭니다.
thresholds = np.arange(0, 1.01, 0.01)
- 이제 결정나무에 대해 accuracy, sensitivity, specificity 의 곡선을 그리고 roc curve 를 그립니다.
- 학습 데이터(training data)에서 모형을 학습하고, 테스트 데이터(test data)에서 예측 확률을 산출합니다.
model_tree = DecisionTreeClassifier(max_depth=2, random_state=2025)
model_tree.fit(X_train, y_train)
pred_tree = model_tree.predict_proba(X_test)[:, 1]
- 앞에서 작성한
plot_metrics()
함수로 threshold 변화 시 accuracy, sensitivity, specificity 를 시각화합니다.
plot_metrics(thresholds, y_test, pred_tree)
- ROC curve 를 계산 및 시각화하고 AUC 를 계산합니다.
plot_roc(y_test, pred_tree)
- 이제 배깅에 대해 accuracy, sensitivity, specificity 의 곡선을 그리고 roc curve 를 그립니다.
- 학습 데이터(training data)에서 모형을 학습하고, 테스트 데이터(test data)에서 예측 확률을 산출합니다.
model_bag = RandomForestClassifier(n_estimators=100, max_features=3, random_state=2025)
model_bag.fit(X_train, y_train)
pred_bag = model_bag.predict_proba(X_test)[:, 1]
- 앞에서 작성한
plot_metrics()
함수로 threshold 변화 시 accuracy, sensitivity, specificity 를 시각화합니다.
plot_metrics(thresholds, y_test, pred_bag)
- ROC curve 를 계산 및 시각화하고 AUC 를 계산합니다.
plot_roc(y_test, pred_bag)
- 이제 랜덤포레스트에 대해 accuracy, sensitivity, specificity 의 곡선을 그리고 roc curve 를 그립니다.
- 학습 데이터(training data)에서 모형을 학습하고, 테스트 데이터(test data)에서 예측 확률을 산출합니다.
model_rf = RandomForestClassifier(n_estimators=100, max_features=2, random_state=2025)
model_rf.fit(X_train, y_train)
pred_rf = model_rf.predict_proba(X_test)[:, 1]
- 앞에서 작성한
plot_metrics()
함수로 threshold 변화 시 accuracy, sensitivity, specificity 를 시각화합니다.
plot_metrics(thresholds, y_test, pred_rf)
- ROC curve 를 계산 및 시각화하고 AUC 를 계산합니다.
plot_roc(y_test, pred_rf)
- 이제 배깅에 대해 accuracy, sensitivity, specificity 의 곡선을 그리고 roc curve 를 그립니다.
- 학습 데이터(training data)에서 모형을 학습하고, 테스트 데이터(test data)에서 예측 확률을 산출합니다.
model_bag = RandomForestClassifier(n_estimators=100, max_features=3, random_state=2025)
model_bag.fit(X_train, y_train)
pred_bag = model_bag.predict_proba(X_test)[:, 1]
- 앞에서 작성한
plot_metrics()
함수로 threshold 변화 시 accuracy, sensitivity, specificity 를 시각화합니다.
plot_metrics(thresholds, y_test, pred_bag)
- ROC curve 를 계산 및 시각화하고 AUC 를 계산합니다.
plot_roc(y_test, pred_bag)
- 마지막으로 인공신경망에 대해 accuracy, sensitivity, specificity 의 곡선을 그리고 roc curve 를 그립니다.
- 학습 데이터(training data)에서 모형을 학습하고, 테스트 데이터(test data)에서 예측 확률을 산출합니다.
model_nn = keras.Sequential([
keras.layers.Dense(2, activation='relu', input_shape=(3,)),
keras.layers.Dense(1, activation='sigmoid')
])
model_nn.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
model_nn.fit(X_train, y_train, epochs=50, verbose=0)
pred_nn = model_nn.predict(X_test)[:, 0]
C:\Users\rupik\anaconda3\Lib\site-packages\keras\src\layers\core\dense.py:92: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead. super().__init__(activity_regularizer=activity_regularizer, **kwargs)
216/216 ━━━━━━━━━━━━━━━━━━━━ 2s 6ms/step
- 앞에서 작성한
plot_metrics()
함수로 threshold 변화 시 accuracy, sensitivity, specificity 를 시각화합니다.
plot_metrics(thresholds, y_test, pred_nn)
- ROC curve 를 계산 및 시각화하고 AUC 를 계산합니다.
plot_roc(y_test, pred_nn)