각 예시에서, Precision은 정확도를 높이는 것에 중점을 두고, Recall은 관련된 모든 사례를 놓치지 않으려는 목표에 중점을 둡니다. 실제 응용에서는 이 두 지표 사이의 균형을 찾는 것이 중요하며, 이를 위해 F1 Score와 같은 다른 지표들도 함께 사용됩니다.
from sklearn.metrics import roc_auc_score, roc_curve, RocCurveDisplay, f1_score, accuracy_score, recall_score, precision_score
from sklearn.ensemble import RandomForestClassifier
import pandas as pd
import numpy as np
url_w = 'https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-white.csv'
url_r = 'https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv'
white_df = pd.read_csv(url_w,sep=';')
red_df = pd.read_csv(url_r,sep=';')
white_df.head()
| fixed acidity | volatile acidity | citric acid | residual sugar | chlorides | free sulfur dioxide | total sulfur dioxide | density | pH | sulphates | alcohol | quality | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7.0 | 0.27 | 0.36 | 20.7 | 0.045 | 45.0 | 170.0 | 1.0010 | 3.00 | 0.45 | 8.8 | 6 |
| 1 | 6.3 | 0.30 | 0.34 | 1.6 | 0.049 | 14.0 | 132.0 | 0.9940 | 3.30 | 0.49 | 9.5 | 6 |
| 2 | 8.1 | 0.28 | 0.40 | 6.9 | 0.050 | 30.0 | 97.0 | 0.9951 | 3.26 | 0.44 | 10.1 | 6 |
| 3 | 7.2 | 0.23 | 0.32 | 8.5 | 0.058 | 47.0 | 186.0 | 0.9956 | 3.19 | 0.40 | 9.9 | 6 |
| 4 | 7.2 | 0.23 | 0.32 | 8.5 | 0.058 | 47.0 | 186.0 | 0.9956 | 3.19 | 0.40 | 9.9 | 6 |
red_df.head()
| fixed acidity | volatile acidity | citric acid | residual sugar | chlorides | free sulfur dioxide | total sulfur dioxide | density | pH | sulphates | alcohol | quality | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7.4 | 0.70 | 0.00 | 1.9 | 0.076 | 11.0 | 34.0 | 0.9978 | 3.51 | 0.56 | 9.4 | 5 |
| 1 | 7.8 | 0.88 | 0.00 | 2.6 | 0.098 | 25.0 | 67.0 | 0.9968 | 3.20 | 0.68 | 9.8 | 5 |
| 2 | 7.8 | 0.76 | 0.04 | 2.3 | 0.092 | 15.0 | 54.0 | 0.9970 | 3.26 | 0.65 | 9.8 | 5 |
| 3 | 11.2 | 0.28 | 0.56 | 1.9 | 0.075 | 17.0 | 60.0 | 0.9980 | 3.16 | 0.58 | 9.8 | 6 |
| 4 | 7.4 | 0.70 | 0.00 | 1.9 | 0.076 | 11.0 | 34.0 | 0.9978 | 3.51 | 0.56 | 9.4 | 5 |
white_df['color'] = 0
red_df['color'] = 1
wine_df = pd.concat([white_df,red_df],axis=0)
wine_df.head()
| fixed acidity | volatile acidity | citric acid | residual sugar | chlorides | free sulfur dioxide | total sulfur dioxide | density | pH | sulphates | alcohol | quality | color | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7.0 | 0.27 | 0.36 | 20.7 | 0.045 | 45.0 | 170.0 | 1.0010 | 3.00 | 0.45 | 8.8 | 6 | 0 |
| 1 | 6.3 | 0.30 | 0.34 | 1.6 | 0.049 | 14.0 | 132.0 | 0.9940 | 3.30 | 0.49 | 9.5 | 6 | 0 |
| 2 | 8.1 | 0.28 | 0.40 | 6.9 | 0.050 | 30.0 | 97.0 | 0.9951 | 3.26 | 0.44 | 10.1 | 6 | 0 |
| 3 | 7.2 | 0.23 | 0.32 | 8.5 | 0.058 | 47.0 | 186.0 | 0.9956 | 3.19 | 0.40 | 9.9 | 6 | 0 |
| 4 | 7.2 | 0.23 | 0.32 | 8.5 | 0.058 | 47.0 | 186.0 | 0.9956 | 3.19 | 0.40 | 9.9 | 6 | 0 |
wine_df.info()
<class 'pandas.core.frame.DataFrame'>
Index: 6497 entries, 0 to 1598
Data columns (total 13 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 fixed acidity 6497 non-null float64
1 volatile acidity 6497 non-null float64
2 citric acid 6497 non-null float64
3 residual sugar 6497 non-null float64
4 chlorides 6497 non-null float64
5 free sulfur dioxide 6497 non-null float64
6 total sulfur dioxide 6497 non-null float64
7 density 6497 non-null float64
8 pH 6497 non-null float64
9 sulphates 6497 non-null float64
10 alcohol 6497 non-null float64
11 quality 6497 non-null int64
12 color 6497 non-null int64
dtypes: float64(11), int64(2)
memory usage: 710.6 KB
wine_df.describe()
| fixed acidity | volatile acidity | citric acid | residual sugar | chlorides | free sulfur dioxide | total sulfur dioxide | density | pH | sulphates | alcohol | quality | color | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 6497.000000 | 6497.000000 | 6497.000000 | 6497.000000 | 6497.000000 | 6497.000000 | 6497.000000 | 6497.000000 | 6497.000000 | 6497.000000 | 6497.000000 | 6497.000000 | 6497.000000 |
| mean | 7.215307 | 0.339666 | 0.318633 | 5.443235 | 0.056034 | 30.525319 | 115.744574 | 0.994697 | 3.218501 | 0.531268 | 10.491801 | 5.818378 | 0.246114 |
| std | 1.296434 | 0.164636 | 0.145318 | 4.757804 | 0.035034 | 17.749400 | 56.521855 | 0.002999 | 0.160787 | 0.148806 | 1.192712 | 0.873255 | 0.430779 |
| min | 3.800000 | 0.080000 | 0.000000 | 0.600000 | 0.009000 | 1.000000 | 6.000000 | 0.987110 | 2.720000 | 0.220000 | 8.000000 | 3.000000 | 0.000000 |
| 25% | 6.400000 | 0.230000 | 0.250000 | 1.800000 | 0.038000 | 17.000000 | 77.000000 | 0.992340 | 3.110000 | 0.430000 | 9.500000 | 5.000000 | 0.000000 |
| 50% | 7.000000 | 0.290000 | 0.310000 | 3.000000 | 0.047000 | 29.000000 | 118.000000 | 0.994890 | 3.210000 | 0.510000 | 10.300000 | 6.000000 | 0.000000 |
| 75% | 7.700000 | 0.400000 | 0.390000 | 8.100000 | 0.065000 | 41.000000 | 156.000000 | 0.996990 | 3.320000 | 0.600000 | 11.300000 | 6.000000 | 0.000000 |
| max | 15.900000 | 1.580000 | 1.660000 | 65.800000 | 0.611000 | 289.000000 | 440.000000 | 1.038980 | 4.010000 | 2.000000 | 14.900000 | 9.000000 | 1.000000 |
X = wine_df.drop(columns = 'color',axis = 1)
y = wine_df['color']
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 2024)
rf = RandomForestClassifier(max_depth = 1, n_estimators = 10, n_jobs = -1, random_state = 2024)
rf.fit(X_train, y_train)
rf.score(X_test, y_test)
0.9053846153846153
여기서 각 트리의 노드에서 value의 합과 samples과 다른이유:
랜덤포레스트의 각 트리는 부트스트랩 샘플링을 하는데 부트스트랩 샘플링은
중복을 허용하여 샘플링을 함.
samples는 unique(고유한)값들을 표시하고 value는 중복된 데이터포인트까지 count되어서 value의 각 클래스의 합과 samples가 일치하지 않는 것임
from sklearn.tree import plot_tree
import matplotlib.pyplot as plt
import seaborn as sns
for i in range(10):
plot_tree(rf.estimators_[i],filled=True,feature_names = X.columns.tolist(), class_names=['white','red'],precision = 3)
plt.show()










y_pred = rf.predict(X_test)
y_pred_proba = rf.predict_proba(X_test)
print('Accuracy Score:', accuracy_score(y_test, y_pred))
print('f1_score:', f1_score(y_test, y_pred))
print('recall:', recall_score(y_test, y_pred))
print('precision:', precision_score(y_test, y_pred))
print('roc-auc-score:',roc_auc_score(y_test, y_pred_proba[:,1]))
Accuracy Score: 0.9053846153846153
f1_score: 0.7823008849557522
recall: 0.6636636636636637
precision: 0.9525862068965517
roc-auc-score: 0.9773454944085762
fpr, tpr, threshold = roc_curve(y_test, y_pred_proba[:,1])
print(fpr)
print(tpr)
print(threshold)
[0. 0. 0. 0. 0.00206825 0.00206825
0.00310238 0.00310238 0.00310238 0.00310238 0.01034126 0.01034126
0.01137539 0.01137539 0.01240951 0.01240951 0.01447777 0.01551189
0.01551189 0.0196484 0.0196484 0.0196484 0.02998966 0.02998966
0.03102378 0.03826267 0.04136505 0.04136505 0.0475698 0.07859359
0.07962771 0.08169597 0.13547053 0.13960703 0.14064116 0.14684592
0.15201655 0.15201655 0.17166494 0.1737332 0.19648397 0.19855222
0.20268873 0.20889349 0.22026887 0.30713547 1. ]
[0. 0.18618619 0.24324324 0.38138138 0.4024024 0.44144144
0.47147147 0.47747748 0.50750751 0.51051051 0.57357357 0.60660661
0.63963964 0.66366366 0.66366366 0.66966967 0.68168168 0.68168168
0.68468468 0.69369369 0.84384384 0.86486486 0.86786787 0.87087087
0.91591592 0.91591592 0.91591592 0.91891892 0.91891892 0.92192192
0.92192192 0.92192192 0.96396396 0.96396396 0.96396396 0.96396396
0.96696697 0.96996997 0.98198198 0.98198198 0.98198198 0.98198198
0.98798799 0.98798799 0.98798799 0.99099099 1. ]
[ inf 0.77476273 0.72370061 0.71777354 0.69089071 0.66671142
0.63982859 0.63533131 0.63390152 0.61857945 0.5828394 0.57834212
0.54268114 0.51037158 0.4944701 0.49161902 0.48569195 0.48364531
0.45930946 0.45880912 0.45338239 0.44340798 0.407747 0.40324972
0.40232027 0.40181993 0.37543744 0.37109672 0.36951037 0.35075781
0.33828682 0.3193777 0.31844825 0.27828999 0.26831558 0.26238851
0.23926968 0.22722787 0.2213008 0.21332709 0.21132639 0.19441797
0.17023868 0.16226497 0.14335585 0.13742878 0.08636666]
plt.figure(figsize=(7,7))
plt.plot(fpr,tpr, color = 'hotpink')
plt.plot([0,1],[0,1], color = 'r',ls = '--')
plt.xlabel('FPR(1-specificity)')
plt.ylabel('TPR(recall)')
plt.title('ROC-AUC Curve')
plt.grid()
plt.show()

ROC-AUC 곡선은 분류 모델의 성능을 평가하는 데 사용되는 도구 중 하나입니다. ROC(Receiver Operating Characteristic) 곡선은 모델의 임계값을 다양하게 변화시킬 때 진짜 양성 비율(True Positive Rate, TPR) 대 거짓 양성 비율(False Positive Rate, FPR)을 그래프에 나타낸 것입니다. AUC(Area Under the Curve)는 이 ROC 곡선 아래의 면적을 의미하며, 모델의 성능을 하나의 숫자로 요약해 줍니다.
ROC 곡선은 다음과 같은 특성을 가집니다:

mpl.style.available
['Solarize_Light2',
'_classic_test_patch',
'_mpl-gallery',
'_mpl-gallery-nogrid',
'bmh',
'classic',
'dark_background',
'fast',
'fivethirtyeight',
'ggplot',
'grayscale',
'seaborn-v0_8',
'seaborn-v0_8-bright',
'seaborn-v0_8-colorblind',
'seaborn-v0_8-dark',
'seaborn-v0_8-dark-palette',
'seaborn-v0_8-darkgrid',
'seaborn-v0_8-deep',
'seaborn-v0_8-muted',
'seaborn-v0_8-notebook',
'seaborn-v0_8-paper',
'seaborn-v0_8-pastel',
'seaborn-v0_8-poster',
'seaborn-v0_8-talk',
'seaborn-v0_8-ticks',
'seaborn-v0_8-white',
'seaborn-v0_8-whitegrid',
'tableau-colorblind10']
import matplotlib as mpl
import seaborn as sns
# mpl.style.available 사용가능한 스타일 리스트 출력
mpl.style.use('seaborn-v0_8-whitegrid')
x = np.linspace(-3,2,100)
y1 = 3*x**2 + 25
y2 = 3*(x+1)**2 + 25
plt.figure(figsize=(10,10))
plt.plot(x,y1,label = '$3x^2 + 25$', color = 'blue',ls = 'dashed')
plt.plot(x,y2,label = '$3(x+1)^2 + 25$', color = 'red')
plt.legend(fontsize = 15)
plt.xlabel('$x$')
plt.ylabel('$y$')
plt.show()

x = np.linspace(-2,2,100)
a11, a12, a13 = 2, 3, 4
y11, y12, y13 = a11**x, a12**x, a13**x
a21, a22, a23 = 1/2, 1/3, 1/4
y21, y22, y23 = a21**x, a22**x, a23**x
fig, axes = plt.subplots(1,2,figsize=(10,10))
axes[0].plot(x, y11, label = '$2^x$', color = 'green', ls = ':')
axes[0].plot(x, y12, label = '$3^x$', color = 'hotpink', ls = 'dashed')
axes[0].plot(x, y13, label = '$4^x$', color = 'blue')
axes[1].plot(x, y21, label = '$1/2^x$', color = 'green', ls = ':')
axes[1].plot(x, y22, label = '$1/3^x$', color = 'hotpink', ls = 'dashed')
axes[1].plot(x, y23, label = '$1/4^x$', color = 'blue')
axes[0].legend(fontsize = 15)
axes[1].legend(fontsize = 15)
plt.show()



x1 = np.linspace(0.0001, 5, 1000)
x2 = np.linspace(0.01, 5, 100)
y11, y12 = np.log10(x1), np.log(x2)
y21, y22 = -np.log10(x1), -np.log(x2)
fig, axes = plt.subplots(1,2,figsize=(10,10))
axes[0].plot(x1, y11, ls = ':', label = '$\log_{10} x$')
axes[0].plot(x2, y12, label = '$\log_{e} x$')
axes[0].legend()
axes[0].set_xlabel('$x$')
axes[0].set_ylabel('$y$')
axes[1].plot(x1, y21, ls = ':', label = '$\log_{1/10} x$')
axes[1].plot(x2, y22, label = '$\log_{1/e} x$')
axes[1].legend()
axes[1].set_xlabel('$x$')
axes[1].set_ylabel('$y$')
plt.show()


z = np.linspace(-10,10,100)
y = 1/(1+np.exp(-z))
plt.figure(figsize=(7,7))
plt.plot(z,y,color = 'blue',lw = 1)
plt.title('sigmoid function')
plt.xlabel('$x$')
plt.ylabel('$y$')
plt.legend()
plt.show()





다변수 벡터함수는 여러 개의 독립변수를 가지고 있으며, 결과로 벡터 값을 반환하는 함수입니다. 간단히 말해, 다변수 벡터함수는 입력변수 에 대해 여러 개의 함수의 결과를 벡터 형태로 묶어서 반환합니다.
예를 들어, 입력변수가 이고, 이 의 각 요소에 대한 함수일 때, 다변수 벡터함수 는 다음과 같이 정의됩니다:
여기서 는 n차원 벡터를 반환하며, 각 요소는 입력변수 에 대한 각각의 함수 의 값입니다.
모든 함수들이 같은 독립변수 벡터 를 공유하지만, 각 함수는 를 다르게 해석하거나 다른 계산을 수행하여 각각 다른 결과를 반환할 수 있습니다. 예를 들어, 한 함수는 의 첫 번째 요소에 더 많은 가중치를 둘 수 있고, 다른 함수는 의 두 번째 요소에 더 많은 가중치를 둘 수 있습니다.
결과적으로, 는 에 대한 각 함수 의 출력을 요소로 가지는 벡터를 생성합니다. 이렇게 함으로써, 는 입력 벡터 를 고차원 공간에서의 하나의 점으로 매핑하는 변환을 수행합니다.
다변수 벡터함수 가 하나의 점으로 매핑된다는 표현은, 함수 가 입력 벡터 를 새로운 공간에서의 단일 벡터로 변환한다는 의미입니다. 구체적으로 설명하면:
이렇게 F를 통해 입력 벡터 는 n차원 벡터 로 변환되며, 이 변환된 벡터는 n차원 공간에서의 단일 점으로 해석될 수 있습니다. 이 변환은 공학, 물리학, 컴퓨터 그래픽스 등 다양한 분야에서 차원 변환, 상태 변화, 물리적 변형 등을 설명하는 데 사용됩니다.
예를 들어, 물리학에서 시간에 따른 입자의 위치를 나타내는 다변수 벡터함수는 3차원 공간에서의 입자의 경로를 하나의 곡선으로 매핑할 수 있습니다. 컴퓨터 그래픽스에서는 3D 객체의 모양을 2D 화면 상의 이미지로 변환하는 데 사용됩니다. 이러한 변환을 통해 다차원 공간에서의 복잡한 관계나 동작을 이해하고 시각화할 수 있습니다.
np.meshgrid 함수는 NumPy 라이브러리에서 제공하는 함수로, 두 벡터를 입력으로 받아서 2차원 격자(grid)를 형성합니다. 이 함수는 주로 2차원 공간에 대한 평가 지점들을 생성하는 데 사용되며, 3차원 공간으로 확장해서 사용할 수도 있습니다. 격자를 형성하는 원리는 각 입력 벡터의 모든 요소들 간의 조합을 만들어내는 것입니다.
예를 들어, 두 벡터 x = [x1, x2, x3]과 y = [y1, y2, y3, y4]가 있다고 가정해 보겠습니다. np.meshgrid(x, y)는 다음과 같이 작동합니다:
x 벡터는 각 y 요소에 대해 반복됩니다. 이것은 2차원 배열에서 각 행이 x 벡터의 복사본이 되도록 합니다.y 벡터는 각 x 요소에 대해 반복됩니다. 이것은 2차원 배열에서 각 열이 y 벡터의 복사본이 되도록 합니다.x 좌표를 위한 것이고, 다른 하나는 y 좌표를 위한 것입니다.이 두 배열을 함께 사용하여 2차원 공간에서 모든 (x, y) 쌍을 나타낼 수 있습니다.
np.meshgrid의 결과는 입력된 배열의 길이에 기반하여 생성되는데, 각각의 출력 배열의 차원은 입력 배열들의 길이에 의해 결정됩니다. 즉, x와 y 배열이 주어질 때, x의 길이가 n이고 y의 길이가 m이라면, np.meshgrid(x, y)를 통해 생성되는 두 개의 2차원 배열 X, Y는 각각 m x n 차원을 가집니다.

u = np.linspace(0,1,30)
v = np.linspace(0,1,30)
U, V = np.meshgrid(u, v)
Z = ( 1+ U ** 2) + V/(1 + V ** 2)
fig = plt.figure(figsize=(7,7))
axes = plt.axes(projection = '3d')
axes.xaxis.set_tick_params(labelsize=15)
axes.yaxis.set_tick_params(labelsize=15)
axes.zaxis.set_tick_params(labelsize=15)
axes.set_xlabel(r'$x$', fontsize=20)
axes.set_ylabel(r'$y$',fontsize=20)
axes.set_zlabel(r'$z$',fontsize=20)
axes.scatter3D(U, V, Z)
plt.show()

x = np.linspace(-4,4,100)
y = x**3 - 15*x + 30
z = np.log(y)
fig, axes = plt.subplots(1,2,figsize=(10,5))
axes[0].plot(x, y, ls = ':', color = 'blue', label = '$x^3 - 15x + 30$')
axes[1].plot(y, z, label = '$\log(y)$')
axes[0].legend()
axes[1].legend()
plt.show()

fig, axes = plt.subplots(1,2,figsize=(10,5))
axes[0].plot(x, y, ls = 'dashed', color = 'blue', label = '$x^3 - 15x + 30$') # f(x)
axes[1].plot(x, z, color = 'red', label = '$log(f(x))$') # g(f(x))
axes[1].legend()
ax_tmp = axes[0].twinx()
ax_tmp.plot(x,z,color = 'red',label = '$log(f(x))$')
ax_tmp.legend()
axes[0].legend(loc = 'lower left')
plt.show()
