πΉΠΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠ° Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠ΅ΠΉ#
ΠΠΎΡΡΠ°Π½ΠΎΠ²ΠΊΠ° ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ#
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import log_loss, brier_score_loss, roc_auc_score
from sklearn.model_selection import train_test_split
from sklearn.svm import LinearSVC
from sklearn.linear_model import LogisticRegression
from sklearn.calibration import CalibratedClassifierCV, calibration_curve
from scipy.special import expit
sns.set_style("whitegrid")
plt.rcParams.update({"font.size": 14})
Π ΡΡΠΎΠΌ Π·Π°Π΄Π°Π½ΠΈΠΈ ΠΌΡ Π±ΡΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Π΄Π°Π½Π½ΡΠ΅ ΠΎ Π±ΠΈΠ½Π°ΡΠ½ΠΎΠΉ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΈ ΡΠΈΡΠ°.
import kagglehub
path = (
kagglehub.dataset_download("mssmartypants/rice-type-classification")
+
"/riceClassification.csv"
)
data = pd.read_csv(path)
data.head()
| id | Area | MajorAxisLength | MinorAxisLength | Eccentricity | ConvexArea | EquivDiameter | Extent | Perimeter | Roundness | AspectRation | Class | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 4537 | 92.229316 | 64.012769 | 0.719916 | 4677 | 76.004525 | 0.657536 | 273.085 | 0.764510 | 1.440796 | 1 |
| 1 | 2 | 2872 | 74.691881 | 51.400454 | 0.725553 | 3015 | 60.471018 | 0.713009 | 208.317 | 0.831658 | 1.453137 | 1 |
| 2 | 3 | 3048 | 76.293164 | 52.043491 | 0.731211 | 3132 | 62.296341 | 0.759153 | 210.012 | 0.868434 | 1.465950 | 1 |
| 3 | 4 | 3073 | 77.033628 | 51.928487 | 0.738639 | 3157 | 62.551300 | 0.783529 | 210.657 | 0.870203 | 1.483456 | 1 |
| 4 | 5 | 3693 | 85.124785 | 56.374021 | 0.749282 | 3802 | 68.571668 | 0.769375 | 230.332 | 0.874743 | 1.510000 | 1 |
ΠΡΠΌΠ°ΡΡΡΠ°Π±ΠΈΡΡΠ΅ΠΌ Π΄Π°Π½Π½ΡΠ΅ ΠΈ ΡΠ°Π·Π΄Π΅Π»ΠΈΠΌ Π½Π° ΠΎΠ±ΡΡΠ΅Π½ΠΈΠ΅, Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΡ (Π½Π° Π½Π΅ΠΉ ΠΌΡ Π±ΡΠ΄Π΅ΠΌ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²Π°ΡΡ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠΈ) ΠΈ ΡΠ΅ΡΡ.
X = data.drop(columns=["id", "Class"])
y = data.Class
X_train, X_temp, y_train, y_temp = train_test_split(
X, y, test_size=0.40, random_state=999, stratify=y
)
X_cal, X_test, y_cal, y_test = train_test_split(
X_temp, y_temp, test_size=0.50, random_state=999, stratify=y_temp
)
scaler = StandardScaler().fit(X_train)
X_train = scaler.transform(X_train)
X_cal = scaler.transform(X_cal)
X_test = scaler.transform(X_test)
ΠΠΎΡΠΌΠΎΡΡΠΈΠΌ Π½Π° Π±Π°Π»Π°Π½Ρ ΠΊΠ»Π°ΡΡΠΎΠ² Π² Π΄Π°Π½Π½ΡΡ .
print("Class balance:", y_train.mean())
Class balance: 0.5490789111905416
ΠΠ»Π°ΡΡΡ ΠΌΠΎΠΆΠ½ΠΎ Π½Π°Π·Π²Π°ΡΡ ΡΠ±Π°Π»Π°Π½ΡΠΈΡΠΎΠ²Π°Π½Π½ΡΠΌΠΈ. ΠΠ±ΡΡΠΈΠΌ ΠΌΠ΅ΡΠΎΠ΄ ΠΎΠΏΠΎΡΠ½ΡΡ Π²Π΅ΠΊΡΠΎΡΠΎΠ² (SVC β Support Vector Classification) ΠΈ Π»ΠΎΠ³ΠΈΡΡΠΈΡΠ΅ΡΠΊΡΡ ΡΠ΅Π³ΡΠ΅ΡΡΠΈΡ, Π² ΠΊΠ°ΡΠ΅ΡΡΠ²Π΅ ΠΌΠ΅ΡΡΠΈΠΊΠΈ Π²ΠΎΠ·ΡΠΌΠ΅ΠΌ ROC-AUC.
ΠΠ»Ρ SVC Π² ΠΊΠ°ΡΠ΅ΡΡΠ²Π΅ ΡΠΊΠΎΡΠΎΠ² Π±ΡΠ΄Π΅ΠΌ ΡΠ°ΡΡΠΌΠ°ΡΡΠΈΠ²Π°ΡΡ Π²ΡΡ
ΠΎΠ΄ decision_function, ΠΊΠΎΡΠΎΡΡΠΉ ΠΏΡΠΎΠΏΠΎΡΡΠΈΠΎΠ½Π°Π»Π΅Π½ ΡΠ°ΡΡΡΠΎΡΠ½ΠΈΡ Π΄ΠΎ ΡΠ°Π·Π΄Π΅Π»ΡΡΡΠ΅ΠΉ Π³ΠΈΠΏΠ΅ΡΠΏΠ»ΠΎΡΠΊΠΎΡΡΠΈ, Π²Π·ΡΡΠΎΠ³ΠΎ ΡΠΎ Π·Π½Π°ΠΊΠΎΠΌ. Π’Π°ΠΊ ΠΊΠ°ΠΊ ΡΠ°ΡΡΡΠΎΡΠ½ΠΈΠ΅ ΠΏΠΎ ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΡ ΠΏΡΠΈΠ½Π°Π΄Π»Π΅ΠΆΠΈΡ Π²ΡΠ΅ΠΌ Π²Π΅ΡΠ΅ΡΡΠ²Π΅Π½Π½ΡΠΌ Π·Π½Π°ΡΠ΅Π½ΠΈΡΠΌ, ΡΠΎ ΡΡΠΎΠ±Ρ Π·Π½Π°ΡΠ΅Π½ΠΈΡ ΠΎΡΡΠ°ΠΆΠ°Π»ΠΈ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠΈ ΠΌΡ ΠΏΠ΅ΡΠ΅Π²Π΅Π΄Π΅ΠΌ ΠΈΡ
Π² ΠΏΡΠΎΠΌΠ΅ΠΆΡΡΠΎΠΊ \([0, 1]\) ΠΌΠ°ΡΡΡΠ°Π±ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ ΡΠ΅ΡΠ΅Π· ΠΌΠΈΠ½ΠΈΠΌΡΠΌ-ΠΌΠ°ΠΊΡΠΈΠΌΡΠΌ ΠΈΠ»ΠΈ ΡΡΠ½ΠΊΡΠΈΠ΅ΠΉ ΡΠΈΠ³ΠΌΠΎΠΈΠ΄Ρ.
ΠΠ°ΠΆΠ½ΠΎ! ΠΠ»Ρ Π½Π°ΡΠΈΡ
ΡΠ΅Π»Π΅ΠΉ Π½ΡΠΆΠ΅Π½ ΠΈΠΌΠ΅Π½Π½ΠΎ LinearSVC, Π° Π½Π΅ SVC!. Π ΠΏΠΎΡΠ»Π΅Π΄Π½Π΅ΠΌ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠ° ΡΠΆΠ΅ Π΄Π΅Π»Π°Π΅ΡΡΡ ΠΏΠΎ-ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ.
ΠΠ»Ρ Π»ΠΎΠ³ΠΈΡΡΠΈΡΠ΅ΡΠΊΠΎΠΉ ΡΠ΅Π³ΡΠ΅ΡΡΠΈΠΈ Π±ΡΠ΄Π΅ΠΌ ΡΡΠ°Π·Ρ ΠΏΡΠΎΠ³Π½ΠΎΠ·ΠΈΡΠΎΠ²Π°ΡΡ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠΈ.
svc = LinearSVC(max_iter=100000, C=0.1).fit(X_train, y_train)
svc_pred = svc.decision_function(X_test)
svc_pred = (svc_pred - svc_pred.min()) / (svc_pred.max() - svc_pred.min())
print("SVC ROC-AUC:", roc_auc_score(y_test, svc_pred))
SVC ROC-AUC: 0.9994134494424565
lr = LogisticRegression(max_iter=100000, C=0.1).fit(X_train, y_train)
lr_pred = lr.predict_proba(X_test)[:, 1] # Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΡ ΠΏΠΎΠ»ΠΎΠΆΠΈΡΠ΅Π»ΡΠ½ΠΎΠ³ΠΎ ΠΊΠ»Π°ΡΡΠ°
print("Logistic regression ROC-AUC:", roc_auc_score(y_test, lr_pred))
Logistic regression ROC-AUC: 0.9992525373425992
ROC-AUC ΠΏΠΎΠΊΠ°Π·ΡΠ²Π°Π΅Ρ, ΡΡΠΎ ΠΌΡ ΠΏΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈ ΠΈΠ΄Π΅Π°Π»ΡΠ½ΠΎ ΠΏΡΠ΅Π΄ΡΠΊΠ°Π·ΡΠ²Π°Π΅ΠΌ ΡΠ΅Π»Π΅Π²ΡΡ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ. ΠΠΎΡΠΌΠΎΡΡΠΈΠΌ ΡΠ΅ΠΏΠ΅ΡΡ Π½Π° ΡΠ°ΡΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ ΡΠΊΠΎΡΠΎΠ² Π΄Π»Ρ ΡΠ΅ΡΡΠΎΠ²ΡΡ ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ².
fig, axs = plt.subplots(1, 2, figsize=(10, 5))
axs[0].hist(svc_pred, bins=20, color="blue", density="True")
axs[1].hist(lr_pred, bins=20, color="orange", density="True")
axs[0].set_title("SVC")
axs[1].set_title("Logistic regression")
plt.suptitle("Outputs distribution")
plt.show()
ΠΠΎ ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΡ, ΠΌΡ Π½Π°Π·ΡΠ²Π°Π΅ΠΌ ΠΎΡΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²Π°Π½Π½ΡΠΌΠΈ ΠΏΡΠΎΠ³Π½ΠΎΠ·Ρ \(\hat{y}\), ΡΠ°ΠΊΠΈΠ΅ ΡΡΠΎ:
Π΅ΡΠ»ΠΈ ΠΌΡ Π·Π°ΡΠΈΠΊΡΠΈΡΡΠ΅ΠΌ ΡΠΏΡΠΎΠ³Π½ΠΎΠ·ΠΈΡΠΎΠ²Π°Π½Π½ΡΡ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΡ \(\hat{p}\) ΠΈ Π²ΠΎΠ·ΡΠΌΠ΅ΠΌ Π²ΡΠ΅ ΠΎΠ±ΡΠ΅ΠΊΡΡ Ρ ΡΡΠΎΠΉ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΡΡ, ΡΠΎ ΡΡΠ΅Π΄ΠΈ Π½ΠΈΡ \(\hat{p}*100\) ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ² Π΄Π΅ΠΉΡΡΠ²ΠΈΡΠ΅Π»ΡΠ½ΠΎ Π±ΡΠ΄ΡΡ ΠΏΡΠΈΠ½Π°Π΄Π»Π΅ΠΆΠ°ΡΡ ΠΏΠΎΠ»ΠΎΠΆΠΈΡΠ΅Π»ΡΠ½ΠΎΠΌΡ ΠΊΠ»Π°ΡΡΡ.
ΠΡ Π²ΠΈΠ΄ΠΈΠΌ, ΡΡΠΎ βΡΠ²Π΅ΡΠ΅Π½Π½ΠΎΡΡΡβ Π»ΠΎΠ³ΠΈΡΡΠΈΡΠ΅ΡΠΊΠΎΠΉ ΡΠ΅Π³ΡΠ΅ΡΡΠΈΠΈ ΠΏΠΎ ΠΊΠ»Π°ΡΡΠ°ΠΌ ΡΠΈΠ»ΡΠ½Π΅Π΅, ΡΠΎΠ³Π΄Π° ΠΊΠ°ΠΊ Π΄Π»Ρ SVC Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠΈ ΠΏΠΎΠ»ΠΎΠΆΠΈΡΠ΅Π»ΡΠ½ΠΎΠ³ΠΎ ΠΊΠ»Π°ΡΡΠ° ΠΈΠΌΠ΅ΡΡ ΡΠ°Π·Π½ΡΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΡ. ΠΠΎΡΠΌΠΎΡΡΠΈΠΌ, ΠΊΠ°ΠΊ ΡΡΠΎ ΠΎΡΡΠ°ΠΆΠ°Π΅ΡΡΡ Π½Π° ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΎΡΠ½ΡΡ ΠΊΡΠΈΠ²ΡΡ .
plt.figure(figsize=(10, 5))
svc_true_prob, svc_pred_prob = calibration_curve(y_test, svc_pred, n_bins=15)
lr_true_prob, lr_pred_prob = calibration_curve(y_test, lr_pred, n_bins=15)
plt.plot(svc_pred_prob, svc_true_prob, label="SVC", color="blue")
plt.plot(lr_pred_prob, lr_true_prob, label="LR", color="orange")
plt.plot([0, 1], [0, 1], label="Perfect", linestyle="--", color="green")
plt.xlabel("Mean predicted probability")
plt.ylabel("Fraction of positives")
plt.title("Calibration curves")
plt.legend()
plt.show()
ΠΡΠΈΠ²Π°Ρ Π΄Π»Ρ Π»ΠΎΠ³ΠΈΡΡΠΈΡΠ΅ΡΠΊΠΎΠΉ ΡΠ΅Π³ΡΠ΅ΡΡΠΈΠΈ ΠΏΡΠΈΠ±Π»ΠΈΠΆΠ°Π΅Ρ Π΄ΠΈΠ°Π³ΠΎΠ½Π°Π»Ρ Π»ΡΡΡΠ΅, ΡΠ΅ΠΌ ΠΊΡΠΈΠ²Π°Ρ SVC. Π§ΡΠΎΠ±Ρ ΠΏΠΎΠ½ΡΡΡ, ΠΏΠΎΡΠ΅ΠΌΡ ΡΠ°ΠΊ, ΡΠ°ΡΡΠΌΠΎΡΡΠΈΠΌ ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΡΡ ΡΠΎΡΠΊΡ βΒ 0.6.
ΠΠ»Ρ Π²ΡΠ΅Ρ ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ², Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΡ ΠΊΠΎΡΠΎΡΡΡ Π±ΡΡΡ ΠΏΡΠΈΠ½Π°Π΄Π»Π΅ΠΆΠ°ΡΠΈΠΌΠΈ ΠΊΠ»Π°ΡΡΡ 1 ΡΠ°Π²Π½Π° 0.6:
Π΄Π»Ρ SVC - 100% ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ² ΠΏΡΠΈΠ½Π°Π΄Π»Π΅ΠΆΠ°Ρ ΠΏΠΎΠ»ΠΎΠΆΠΈΡΠ΅Π»ΡΠ½ΠΎΠΌΡ ΠΊΠ»Π°ΡΡΡ;
Π΄Π»Ρ LR - 50% ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ² ΠΏΡΠΈΠ½Π°Π΄Π»Π΅ΠΆΠ°Ρ ΠΏΠΎΠ»ΠΎΠΆΠΈΡΠ΅Π»ΡΠ½ΠΎΠΌΡ ΠΊΠ»Π°ΡΡΡ.
ΠΠΎΠΏΡΠΎΠ±ΡΠ΅ΠΌ ΠΎΡΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²Π°ΡΡ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΎΡΡ ΠΈ ΠΏΠΎΡΡΡΠΎΠΈΡΡ Π½ΠΎΠ²ΡΠ΅ ΠΊΡΠΈΠ²ΡΠ΅.
ΠΠΎΠ²ΠΎΡΡ Π² ΡΠ΅ΡΠΌΠΈΠ½Π°Ρ ΡΠΎΠ±ΡΡΠΈΠΉ:
ΠΊΠΎΠ³Π΄Π° SVC ΡΠ²Π΅ΡΠ΅Π½ Π² ΡΠΎΠ±ΡΡΠΈΠΈ Π½Π° 60%, ΠΎΠ½ΠΎ Π²ΡΠ΅Π³Π΄Π° ΠΏΡΠΎΠΈΡΡ ΠΎΠ΄ΠΈΡ;
ΠΊΠΎΠ³Π΄Π° LR ΡΠ²Π΅ΡΠ΅Π½Π½Π° Π² ΡΠΎΠ±ΡΡΠΈΠΈ Π½Π° 60%, ΠΎΠ½ΠΎ ΠΏΡΠΎΠΈΡΡ ΠΎΠ΄ΠΈΡ Π² 50% ΡΠ»ΡΡΠ°Π΅Π².
Π‘Π»Π΅Π΄ΠΎΠ²Π°ΡΠ΅Π»ΡΠ½ΠΎ, Π²ΠΈΠ΄Π½Π° ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ°
ΠΠ±Π΅ ΠΌΠΎΠ΄Π΅Π»ΠΈ ΠΈΠΌΠ΅ΡΡ Ρ ΠΎΡΠΎΡΡΡ ΠΏΡΠ΅Π΄ΡΠΊΠ°Π·Π°ΡΠ΅Π»ΡΠ½ΡΡ ΡΠΎΡΠ½ΠΎΡΡΡ, Π½ΠΎ ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΠΈΠ΅ ΠΈΡ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠ΅ΠΉ ΡΠΌΠΏΠΈΡΠΈΡΠ΅ΡΠΊΠΈΠΌ Π΄Π°Π½Π½ΡΠΌ ΡΠ°Π·Π»ΠΈΡΠ½ΠΎ. ΠΠΌΠ΅Π½Π½ΠΎ ΡΡΡ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ ΠΌΡ Π±ΡΠ΄Π΅ΠΌ ΡΠ±ΠΈΡΠ°ΡΡ ΠΌΠ΅ΡΠΎΠ΄Π°ΠΌΠΈ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠΈ.
Π§ΡΠΎΠ±Ρ Π»ΡΡΡΠ΅ ΠΎΡΠ΅Π½ΠΈΡΡ ΡΡΠ²ΡΡΠ²ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΡ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠΈ ΠΌΠΎΠ΄Π΅Π»ΠΈ ΠΊ ΡΠΌΠΏΠΈΡΠΈΡΠ΅ΡΠΊΠΈΠΌ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΡΠΌ, ΡΠ°ΡΡΠΌΠΎΡΡΠΈΠΌ ΡΠΈΠ½ΡΠ΅ΡΠΈΡΠ΅ΡΠΊΠΈΠΉ ΠΏΡΠΈΠΌΠ΅Ρ Ρ ΡΠ΅ΠΌΠΈ ΠΆΠ΅ Π΄Π°Π½Π½ΡΠΌΠΈ Π² ΡΡΠ»ΠΎΠ²ΠΈΡΡ Π΄ΠΈΡΠ±Π°Π»Π°Π½ΡΠ°.
minority_class = 0
majority_class = 1
X_min = X[y == minority_class]
X_maj = X[y == majority_class]
y_min = y[y == minority_class]
y_maj = y[y == majority_class]
# ΠΠΎΠ·ΡΠΌΡΠΌ ΠΌΠ°Π»ΠΎ ΠΊΠ»Π°ΡΡΠ° 0 ΠΈ ΠΌΠ½ΠΎΠ³ΠΎ ΠΊΠ»Π°ΡΡΠ° 1
X_sub = pd.concat(
[X_min.sample(frac=0.3, random_state=42), X_maj.sample(frac=1.0, random_state=42)]
)
y_sub = pd.concat(
[y_min.sample(frac=0.3, random_state=42), y_maj.sample(frac=1.0, random_state=42)]
)
# Train Test
X_train_imb, X_test_imb, y_train_imb, y_test_imb = train_test_split(
X_sub, y_sub, test_size=0.4, random_state=42, stratify=None
)
# ΠΠ°ΡΡΡΠ°Π±ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅
scaler = StandardScaler().fit(X_train_imb)
X_train_imb = scaler.transform(X_train_imb)
X_test_imb = scaler.transform(X_test_imb)
# ΠΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ ΡΠ°ΡΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ ΠΊΠ»Π°ΡΡΠΎΠ²
print("Class distributions")
display(
pd.concat(
[
y_train_imb.value_counts(normalize=True),
y_test_imb.value_counts(normalize=True),
],
axis=1,
keys=["Train", "Test"],
)
)
Class distributions
| Train | Test | |
|---|---|---|
| Class | ||
| 1 | 0.804473 | 0.799116 |
| 0 | 0.195527 | 0.200884 |
ΠΡΠ΄Π΅ΠΌ Π·Π°ΠΏΠΈΡΡΠ²Π°ΡΡ Π²ΡΠ΅ ΠΌΠ΅ΡΡΠΈΠΊΠΈ Π² Π΄Π°ΡΠ°ΡΡΠ΅ΠΉΠΌ Π΄Π»Ρ ΠΊΡΠ°ΡΠΎΡΡ.
cal_metrics = []
probas = []
# ΠΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ ΠΌΠΎΠ΄Π΅Π»ΠΈ
svc_imb = LinearSVC(max_iter=100000, C=0.1).fit(X_train_imb, y_train_imb)
svc_pred_imb = svc_imb.decision_function(X_test_imb)
svc_pred_imb = (
(svc_pred_imb - svc_pred_imb.min())
/
(svc_pred_imb.max() - svc_pred_imb.min())
)
lr_imb = LogisticRegression(max_iter=100000, C=0.1).fit(X_train_imb, y_train_imb)
lr_pred_imb = lr_imb.predict_proba(X_test_imb)[:, 1] # ΠΠ΅ΡΠΎΡΡΠ½ΠΎΡΡΡ ΠΏΠΎΠ»ΠΎΠΆΠΈΡΠ΅Π»Π½ΡΠΎΠ³ΠΎ ΠΊΠ»Π°ΡΡΠ°
cal_metrics.extend(
[
{
"model": "SVC",
"metric": "ROC-AUC",
"calibration": "None",
"score": roc_auc_score(y_test_imb, svc_pred_imb),
},
{
"model": "LogReg",
"metric": "ROC-AUC",
"calibration": "None",
"score": roc_auc_score(y_test_imb, lr_pred_imb),
},
]
)
probas.extend(
[
{"model": "SVC", "calibration": "None", "proba": svc_pred},
{"model": "LogReg", "calibration": "None", "proba": lr_pred},
]
)
pd.DataFrame(cal_metrics)
| model | metric | calibration | score | |
|---|---|---|---|---|
| 0 | SVC | ROC-AUC | None | 0.998224 |
| 1 | LogReg | ROC-AUC | None | 0.998038 |
ΠΠ°ΡΠ΅ΡΡΠ²ΠΎ ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ ΠΏΠΎ AUC ΠΎΡΡΠ°Π»ΠΎΡΡ Π½Π΅ΠΈΠ·ΠΌΠ΅Π½Π½ΠΎ Ρ ΠΎΡΠΎΡΠΈΠΌ. Π’Π΅ΠΏΠ΅ΡΡ ΠΏΠΎΡΠΌΠΎΡΡΠΈΠΌ Π½Π° Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠΈ ΠΈ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΎΡΠ½ΡΠ΅ ΠΊΡΠΈΠ²ΡΠ΅ Π·Π½Π°Ρ, ΡΡΠΎ Π½Π°ΡΠΈ ΡΠΌΠΏΡΠΈΡΠΈΡΠ΅ΡΠΊΠΈΠ΅ ΡΠ°ΡΡΠΎΡΡ Π² ΡΠ΅ΡΡΠΎΠ²ΠΎΠΉ Π²ΡΠ±ΠΎΡΠΊΠ΅ ΡΠ°ΠΊΠΎΠ²Ρ:
1 - 0.799116
0 - 0.200884
fig, axs = plt.subplots(1, 3, figsize=(20, 6))
svc_true_prob_imb, svc_pred_prob_imb = calibration_curve(
y_test_imb, svc_pred_imb, n_bins=15
)
lr_true_prob_imb, lr_pred_prob_imb = calibration_curve(
y_test_imb, lr_pred_imb, n_bins=15
)
axs[0].hist(svc_pred_imb, bins=20, color="blue", density="True")
axs[1].hist(lr_pred_imb, bins=20, color="orange", density="True")
axs[2].plot(svc_pred_prob_imb, svc_true_prob_imb, label="SVC", color="blue")
axs[2].plot(lr_pred_prob_imb, lr_true_prob_imb, label="LR", color="orange")
axs[2].plot([0, 1], [0, 1], label="Perfect", linestyle="--", color="green")
axs[0].set_title("SVC outputs")
axs[1].set_title("Logistic regression outputs")
axs[2].set_title("Calibration curves")
plt.show()
Π‘ΠΈΡΡΠ°ΡΠΈΡ Π°Π½Π°Π»ΠΎΠ³ΠΈΡΠ½Π° ΡΠ±Π°Π»Π°Π½ΡΠΈΡΠΎΠ²Π°Π½Π½ΠΎΠΌΡ ΡΠ»ΡΡΠ°Ρ, Ρ ΠΏΠΎΠΏΡΠ°Π²ΠΊΠΎΠΉ Π½Π° ΡΠΌΠΏΠΈΡΠΈΡΠ΅ΡΠΊΠΈΠ΅ ΡΠ°ΡΡΠΎΡΡ.
ΠΠΎΠ»ΠΈΡΠ΅ΡΡΠ²Π΅Π½Π½Π°Ρ ΠΎΡΠ΅Π½ΠΊΠ° ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠΈ: Log Loss, Brier Score#
ΠΠ°ΠΊ Π²ΠΈΠ΄Π½ΠΎ Π²ΡΡΠ΅, roc-auc Π½Π΅ Π΄Π°Π΅Ρ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ ΠΎ ΠΊΠ°ΡΠ΅ΡΡΠ²Π΅ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠΈ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠ΅ΠΉ. Π§ΡΠΎΠ±Ρ Π½Π΅ ΠΏΡΠΈΠ±Π΅Π³Π°ΡΡ Π²ΡΠ΅Π³Π΄Π° ΠΊ Π²ΠΈΠ·ΡΠ°Π»ΡΠ½ΠΎΠΉ ΠΎΡΠ΅Π½ΠΊΠ΅ (Ρ ΠΎΡΡ ΡΡΠΎ Π½Π°Π³Π»ΡΠ΄Π½ΠΎ), ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²Π΅Π½Π½ΡΠ΅ ΠΌΠ΅ΡΡΠΈΠΊΠΈ. ΠΡΠ½ΠΎΠ²Π½ΡΠ΅ ΠΈΠ· ΡΠ°ΠΊΠΈΡ βΒ Log Loss ΠΈ Brier Score.
LogLoss
Π ΠΈΠ΄Π΅Π°Π»ΡΠ½ΠΎΠΌ ΡΠ»ΡΡΠ°Π΅ Log Loss ΡΠ°Π²Π½Π° 0.
Π Ρ ΡΠ΄ΡΠ΅ΠΌ ΡΠ»ΡΡΠ°Π΅ Log Loss ΡΡΡΠ΅ΠΌΠΈΡΡΡ ΠΊ \(β\).
Brier Score
Π Ρ ΡΠ΄ΡΠ΅ΠΌ ΡΠ»ΡΡΠ°Π΅ Brier score ΡΠ°Π²Π½Π° 1.
Π Π»ΡΡΡΠ΅ΠΌ ΡΠ»ΡΡΠ°Π΅ Brier score \(\to\) 0.
cal_metrics.extend(
[
{
"model": "LogReg",
"metric": "BrierScore",
"calibration": "None",
"score": brier_score_loss(y_test, lr_pred),
},
{
"model": "SVC",
"metric": "BrierScore",
"calibration": "None",
"score": brier_score_loss(y_test, svc_pred),
},
{
"model": "LogReg",
"metric": "LogLoss",
"calibration": "None",
"score": log_loss(y_test, lr_pred),
},
{
"model": "SVC",
"metric": "LogLoss",
"calibration": "None",
"score": log_loss(y_test, svc_pred),
},
]
)
metrics = pd.DataFrame(cal_metrics)
metrics[metrics.metric.isin(["BrierScore", "LogLoss"])]
| model | metric | calibration | score | |
|---|---|---|---|---|
| 2 | LogReg | BrierScore | None | 0.009350 |
| 3 | SVC | BrierScore | None | 0.099743 |
| 4 | LogReg | LogLoss | None | 0.035032 |
| 5 | SVC | LogLoss | None | 0.364177 |
ΠΠ°ΠΊ ΠΌΠΎΠΆΠ½ΠΎ Π·Π°ΠΌΠ΅ΡΠΈΡΡ, SVC Π½Π° ΠΏΠΎΡΡΠ΄ΠΎΠΊ ΠΏΡΠΎΠΈΠ³ΡΡΠ²Π°Π΅Ρ ΠΏΠΎ ΠΎΠ±Π΅ΠΈΠΌ ΠΌΠ΅ΡΡΠΈΠΊΠ°ΠΌ.
ΠΠ΅ΡΠΎΠ΄Ρ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠΈ#
ΠΠ»Π°ΡΡΠΈΡΠ΅ΡΠΊΠΈΠΌΠΈ ΠΌΠ΅ΡΠΎΠ΄Π°ΠΌΠΈ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠΈ ΡΠ²Π»ΡΡΡΡΡ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠ° ΠΠ»Π°ΡΡΠ° (ΡΠΈΠ³ΠΌΠΎΠΈΠ΄Π½Π°Ρ) ΠΈ ΠΈΠ·ΠΎΡΠΎΠ½ΠΈΡΠ΅ΡΠΊΠ°Ρ ΡΠ΅Π³ΡΠ΅ΡΡΠΈΡ. ΠΠ±Π° ΡΡΠΈΡ
ΠΌΠ΅ΡΠΎΠ΄Π° ΡΠ΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½Ρ Π² sklearn.
ΠΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠ° ΠΠ»Π°ΡΡΠ°
ΠΠΎΠΏΡΡΡΠΈΠΌ, Ρ Π½Π°Ρ Π΅ΡΡΡ ΠΎΠ±ΡΡΠ΅Π½Π½ΡΠΉ ΠΊΠ»Π°ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡ \(b(x)\), ΠΊΠΎΡΠΎΡΡΠΉ Π²ΡΠ΄Π°Π΅Ρ ΡΠ²Π΅ΡΠ΅Π½Π½ΠΎΡΡΡ (ΡΠΊΠΎΡ) Π² ΡΠΎΠΌ, ΡΡΠΎ \(x\) ΠΎΡΠ½ΠΎΡΠΈΡΡΡ ΠΊ ΠΏΠΎΠ»ΠΎΠΆΠΈΡΠ΅Π»ΡΠ½ΠΎΠΌΡ ΠΊΠ»Π°ΡΡΡ. ΠΠ΅ΡΠΎΠ΄ ΠΏΡΠΈΠ±Π»ΠΈΠΆΠ°Π΅Ρ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΡ ΠΏΠΎΠ»ΠΎΠΆΠΈΡΠ΅Π»ΡΠ½ΠΎΠ³ΠΎ ΠΊΠ»Π°ΡΡΠ° Ρ ΠΏΠΎΠΌΠΎΡΡΡ ΡΠΈΠ³ΠΌΠΎΠΈΠ΄Π½ΠΎΠΉ ΡΡΠ½ΠΊΡΠΈΠΈ:
ΠΠ΄Π΅ΡΡ ΠΎΠ±ΡΡΠ°Π΅ΠΌΡΠΌΠΈ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠ°ΠΌΠΈ ΡΠ²Π»ΡΡΡΡΡ \(A, C \in \mathbb{R}\), ΠΈΡ ΠΏΠΎΠ΄Π±ΠΈΡΠ°ΡΡ Ρ ΠΏΠΎΠΌΠΎΡΡΡ ΠΌΠ΅ΡΠΎΠ΄Π° ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΠΎΠ³ΠΎ ΠΏΡΠ°Π²Π΄ΠΎΠΏΠΎΠ΄ΠΎΠ±ΠΈΡ (ΡΠΎΡΠ½ΠΎ ΡΠ°ΠΊ ΠΆΠ΅, ΠΊΠ°ΠΊ Π² Π»ΠΎΠ³ΠΈΡΡΠΈΡΠ΅ΡΠΊΠΎΠΉ ΡΠ΅Π³ΡΠ΅ΡΡΠΈΠΈ). Π‘Π΄Π΅Π»Π°ΡΡ ΡΡΠΎ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎ ΠΊΡΠΎΡΡ-Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΠΈ ΠΈΠ»ΠΈ Π½Π° ΠΎΡΠ»ΠΎΠΆΠ΅Π½Π½ΠΎΠΉ Π²ΡΠ±ΠΎΡΠΊΠ΅.
ΠΠ·ΠΎΡΠΎΠ½ΠΈΡΠ΅ΡΠΊΠ°Ρ ΡΠ΅Π³ΡΠ΅ΡΡΠΈΡ
ΠΡΡΠ³ΠΎΠΉ ΠΌΠ΅ΡΠΎΠ΄ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ ΡΠ°ΠΊ Π½Π°Π·ΡΠ²Π°Π΅ΠΌΡΡ ΠΈΠ·ΠΎΡΠΎΠ½ΠΈΡΠ΅ΡΠΊΡΡ ΡΡΠ½ΠΊΡΠΈΡ β ΠΊΡΡΠΎΡΠ½ΠΎ-Π»ΠΈΠ½Π΅ΠΉΠ½ΡΡ Π²ΠΎΠ·ΡΠ°ΡΡΠ°ΡΡΡΡ ΡΡΠ½ΠΊΡΠΈΡ \(f: \mathbb{R} \rightarrow \mathbb{R}\). Π€ΡΠ½ΠΊΡΠΈΡ ΠΏΠΎΠ΄Π±ΠΈΡΠ°Π΅ΡΡΡ ΡΠ°ΠΊ, ΡΡΠΎΠ±Ρ ΠΌΠΈΠ½ΠΈΠΌΠΈΠ·ΠΈΡΠΎΠ²Π°ΡΡ MSE ΠΏΠΎ Π²ΡΠ±ΠΎΡΠΊΠ΅:
ΠΡΠΎΡ ΠΌΠ΅ΡΠΎΠ΄ ΡΠΊΠ»ΠΎΠ½Π΅Π½ ΠΊ ΠΏΠ΅ΡΠ΅ΠΎΠ±ΡΡΠ΅Π½ΠΈΡ, ΠΏΠΎΡΡΠΎΠΌΡ Π΅Π³ΠΎ ΡΠ΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡΠ΅ΡΡΡ ΠΏΡΠΈΠΌΠ΅Π½ΡΡΡ ΡΠΎΠ»ΡΠΊΠΎ Π΄Π»Ρ Π±ΠΎΠ»ΡΡΠΈΡ Π²ΡΠ±ΠΎΡΠΎΠΊ.
ΠΡΠΎΠΌΠ΅ ΡΠΎΠ³ΠΎ, ΠΌΠΎΠΆΠ΅Ρ Π½Π΅ Π±ΡΡΡ Π±ΠΎΠ»ΡΡΠΎΠ³ΠΎ ΡΠΌΡΡΠ»Π° ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²Π°ΡΡ Π»ΠΎΠ³ΠΈΡΡΠΈΡΠ΅ΡΠΊΡΡ ΡΠ΅Π³ΡΠ΅ΡΡΠΈΡ, Π½ΠΎ ΠΌΡ ΠΏΡΠΎΠ²Π΅Π΄Π΅ΠΌ Π½Π΅Π±ΠΎΠ»ΡΡΠΎΠΉ ΡΠΊΡΠΏΠ΅ΡΠΈΠΌΠ΅Π½Ρ.
ΠΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΡ ΠΏΡΠΎΠ²ΠΎΠ΄ΡΡ Π½Π° ΠΎΡΠ»ΠΎΠΆΠ΅Π½Π½ΠΎΠΉ Π²ΡΠ±ΠΎΡΠΊΠ΅, Π²ΡΠ΄Π΅Π»Π΅Π½Π½ΠΎΠΉ ΠΏΠΎΠ΄ Π½Π΅Ρ. ΠΠ½ΠΎΠ³Π΄Π° ΠΏΡΠΈΡΡΡΡΡΠ²ΡΠ΅Ρ ΠΏΡΠ°ΠΊΡΠΈΠΊΠ° ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠΈ Π½Π° ΡΡΠ΅Π½ΠΈΡΠΎΠ²ΠΎΡΠ½ΡΡ Π΄Π°Π½Π½ΡΡ . ΠΡ ΠΎΡΠ»ΠΎΠΆΠΈΠ»ΠΈ Π²ΡΠ±ΠΎΡΠΊΡ Π²ΡΡΠ΅, ΡΠ°ΠΊ ΡΡΠΎ Π±ΡΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Π΅Ρ.
ΠΠ»ΡΡ Π°Π½Π°Π»ΠΈΠ·Π°, ΠΏΠΎΡΠΌΠΎΡΡΠΈΠΌ Π½Π° ΠΌΠ΅ΡΡΠΈΠΊΠΈ, Π² ΡΠΎΠΌ ΡΠΈΡΠ»Π΅ ΡΠ΅, ΠΊΠΎΡΠΎΡΡΠ΅ ΠΎΡ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠ΅ΠΉ Π½Π΅ Π·Π°Π²ΠΈΡΡΡ ΠΈ ΠΏΡΠΎΠ²Π΅ΡΠΈΠΌ, Π΅ΡΡΡ Π»ΠΈ Π½Π°Π±Π»ΡΠ΄Π°Π΅ΠΌΡΠΉ ΡΡΡΠ΅ΠΊΡ
def log_metric(model_name, method, y_test, calibrated_model_proba):
cal_metrics.extend(
[
{
"model": model_name,
"metric": "ROC-AUC",
"calibration": method,
"score": roc_auc_score(y_test, calibrated_model_proba),
},
{
"model": model_name,
"metric": "LogLoss",
"calibration": method,
"score": log_loss(y_test, calibrated_model_proba),
},
{
"model": model_name,
"metric": "BrierScore",
"calibration": method,
"score": brier_score_loss(y_test, calibrated_model_proba),
},
]
)
probas.extend(
[
{
"model": model_name,
"calibration": method,
"proba": calibrated_model_proba,
},
]
)
for model, model_name in zip([svc, lr], ("SVC", "LogReg")):
for method in ["Sigmoid", "Isotonic"]:
calibrated_model = CalibratedClassifierCV(model, cv=3, method=method.lower()).fit(X_cal, y_cal)
calibrated_model_proba = calibrated_model.predict_proba(X_test)[:, 1]
log_metric(model_name, method, y_test, calibrated_model_proba)
metrics_df = pd.DataFrame(cal_metrics)
probas_df = pd.DataFrame(probas)
print("SVC Metrics")
metrics_df[metrics_df.model == "SVC"].pivot(
columns=["calibration"], index=["metric"], values=["score"]
)["score"][["None", "Sigmoid", "Isotonic"]]
SVC Metrics
| calibration | None | Sigmoid | Isotonic |
|---|---|---|---|
| metric | |||
| BrierScore | 0.099743 | 0.008806 | 0.008632 |
| LogLoss | 0.364177 | 0.032401 | 0.030417 |
| ROC-AUC | 0.998224 | 0.999347 | 0.999328 |
ΠΠ· ΠΈΠ½ΡΠ΅ΡΠ΅ΡΠ½ΠΎΠ³ΠΎ Π·Π΄Π΅ΡΡ ΡΠ»Π΅Π΄ΡΡΡΠ΅Π΅:
ROC-AUC ΠΏΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈ Π½Π΅ ΠΈΠ·ΠΌΠ΅Π½ΠΈΠ»ΡΡ ΠΏΡΠΈ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠ΅, Π½Π΅ΡΠΌΠΎΡΡΡ Π½Π° ΡΠΎ ΡΡΠΎ ΡΡΠ° ΠΌΠ΅ΡΡΠΈΠΊΠ° ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠΈ. ΠΡΠΈΡΠΈΠ½Π° Π² ΡΠΎΠΌ, ΡΡΠΎ ROC-AUC Π·Π°Π²ΠΈΡΠΈΡ ΡΠΎΠ»ΡΠΊΠΎ ΠΎΡ ΠΏΠΎΡΡΠ΄ΠΊΠ° ΠΏΡΠ΅Π΄ΡΠΊΠ°Π·Π°Π½ΠΈΠΉ (ΡΠ°Π½Π³ΠΎΠ²), Π° ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠ° β ΡΡΠΎ ΠΌΠΎΠ½ΠΎΡΠΎΠ½Π½ΠΎΠ΅ ΠΏΡΠ΅ΠΎΠ±ΡΠ°Π·ΠΎΠ²Π°Π½ΠΈΠ΅ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠ΅ΠΉ. Π‘Π»Π΅Π΄ΠΎΠ²Π°ΡΠ΅Π»ΡΠ½ΠΎ, ΠΎΡΠ½ΠΎΡΠΈΡΠ΅Π»ΡΠ½ΡΠΉ ΠΏΠΎΡΡΠ΄ΠΎΠΊ ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ² Π½Π΅ ΠΌΠ΅Π½ΡΠ΅ΡΡΡ, ΠΈ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ ΠΌΠ΅ΡΡΠΈΠΊΠΈ ΠΎΡΡΠ°ΡΡΡΡ ΠΏΡΠ΅ΠΆΠ½ΠΈΠΌ. Π’ΠΎ ΠΆΠ΅ ΡΠ°ΠΌΠΎΠ΅ Π±ΡΠ΄Π΅Ρ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΎ Π΄Π»Ρ Π»ΡΠ±ΡΡ ΠΌΠ΅ΡΡΠΈΠΊ, ΠΊΠΎΡΠΎΡΡΠ΅ ΠΎΠΏΠΈΡΠ°ΡΡΡΡ ΠΈΡΠΊΠ»ΡΡΠΈΡΠ΅Π»ΡΠ½ΠΎ Π½Π° ΠΌΠ΅ΡΠΊΠΈ ΠΊΠ»Π°ΡΡΠΎΠ² ΠΈΠ»ΠΈ ΠΏΠΎΡΡΠ΄ΠΎΠΊ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠ΅ΠΉ, Π° Π½Π΅ Π½Π° ΠΈΡ Π°Π±ΡΠΎΠ»ΡΡΠ½ΡΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΡ.
ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠ° ΠΠ»Π°ΡΡΠ° ΡΠΈΠ»ΡΠ½ΠΎ ΡΠ»ΡΡΡΠΈΠ»Π° ΠΈ BrierScore, ΠΈ LogLoss
ΠΈΠ·ΠΎΡΠΎΠ½ΠΈΡΠ΅ΡΠΊΠ°Ρ ΡΠ΅Π³ΡΠ΅ΡΡΠΈΡ ΡΠΎΠΆΠ΅, ΠΏΡΠΈΡΠ΅ΠΌ ΠΏΠΎΠΊΠ°Π·ΡΠ²Π°Π΅Ρ Π»ΡΡΡΠ΅Π΅ ΠΊΠ°ΡΠ΅ΡΡΠ²ΠΎ, Ρ ΠΎΡΡ ΠΈ Π½Π΅Π·Π½Π°ΡΠΈΡΠ΅Π»ΡΠ½ΠΎ
Π’Π΅ΠΏΠ΅ΡΡ Π²Π·Π³Π»ΡΠ½Π΅ΠΌ Π½Π° Π»ΠΎΠ³ΠΈΡΡΠΈΡΠ΅ΡΠΊΡΡ ΡΠ΅Π³ΡΠ΅ΡΡΠΈΡ
print("LogReg Metrics")
metrics_df[metrics_df.model == "LogReg"].pivot(
columns=["calibration"], index=["metric"], values=["score"]
)["score"][["None", "Sigmoid", "Isotonic"]]
LogReg Metrics
| calibration | None | Sigmoid | Isotonic |
|---|---|---|---|
| metric | |||
| BrierScore | 0.009350 | 0.009643 | 0.009460 |
| LogLoss | 0.035032 | 0.035662 | 0.035123 |
| ROC-AUC | 0.998038 | 0.999182 | 0.999133 |
ΠΠ°ΠΊ Π²ΠΈΠ΄ΠΈΠΌ, ΡΡΡ ΠΏΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈ Π½Π΅Ρ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ, ΡΡΠΎ Π² ROC-AUC, ΡΡΠΎ Π² Π΄ΡΡΠ³ΠΈΡ ΠΌΠ΅ΡΡΠΈΠΊΠ°Ρ ΠΏΠΎΡΠΊΠΎΠ»ΡΠΊΡ ΠΌΠΎΠ΄Π΅Π»Ρ ΠΈΠ·Π½Π°ΡΠ°ΡΠ½ΠΎ Π½Π΅ΠΏΠ»ΠΎΡ ΠΎ ΠΎΡΠ΅Π½ΠΈΠ²Π°Π΅Ρ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠΈ.
ΠΠΎΡΡΡΠΎΠΈΠΌ Π½ΠΎΠ²ΡΠ΅ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΎΡΠ½ΡΠ΅ ΠΊΡΠΈΠ²ΡΠ΅:
fig, axs = plt.subplots(1, 3, figsize=(16, 5))
for idx, calibration in enumerate(["None", "Sigmoid", "Isotonic"]):
for model in ["SVC", "LogReg"]:
true_prob, pred_prob = calibration_curve(
y_test,
probas_df[(probas_df.model == model) & (probas_df.calibration == calibration)]["proba"].item(),
n_bins=15,
)
axs[idx].plot(pred_prob, true_prob, label=model)
axs[idx].plot([0, 1], [0, 1], label="Perfect", linestyle="--", color="green")
if calibration == "None":
calibration = "No"
axs[idx].set_title(f"{calibration} Calibration")
for ax in axs:
ax.set_xlabel("Mean predicted probability")
ax.set_ylabel("Fraction of positives")
ax.legend()
plt.show()
ΠΠ°ΠΊ ΠΌΡ Π²ΠΈΠ΄ΠΈΠΌ, ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠ° ΠΠ»Π°ΡΡΠ° Π΄Π΅ΠΉΡΡΠ²ΠΈΡΠ΅Π»ΡΠ½ΠΎ ΡΠ»ΡΡΡΠΈΠ»Π° Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠΈ, ΠΊΠΎΡΠΎΡΡΠ΅ ΠΏΠΎΠ»ΡΡΠ°ΡΡΡΡ Ρ SVC ΠΈ ΡΠΈΠ»ΡΠ½Π΅Π΅ ΠΏΡΠΈΠ±Π»ΠΈΠ·ΠΈΠ»Π° ΠΊ ΠΈΠ΄Π΅Π°Π»ΡΠ½ΡΠΌ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠΈ Π»ΠΎΠ³ΠΈΡΡΠΈΡΠ΅ΡΠΊΠΎΠΉ ΠΌΠΎΠ΄Π΅Π»ΠΈ.
Π ΡΠΎ ΠΆΠ΅ Π²ΡΠ΅ΠΌΡ ΠΈΠ·ΠΎΡΠΎΠ½ΠΈΡΠ΅ΡΠΊΠ°Ρ ΡΠ΅Π³ΡΠ΅ΡΡΠΈΡ ΡΠ»ΡΡΡΠΈΠ»Π° Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠΈ SVC, Π½ΠΎ ΡΡ ΡΠ΄ΡΠΈΠ»Π° ΠΊΡΠΈΠ²ΡΡ Π»ΠΎΠ³ΠΈΡΡΠΈΡΠ΅ΡΠΊΠΎΠΉ ΡΠ΅Π³ΡΠ΅ΡΡΠΈΠΈ. ΠΡΠΎ ΠΌΠΎΠΆΠ΅Ρ ΡΠΈΠ³Π½Π°Π»ΠΈΠ·ΠΈΡΠΎΠ²Π°ΡΡ ΠΎ ΡΠΎΠΌ, ΡΡΠΎ ΠΌΠ΅ΡΠΎΠ΄ Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΠ΅ΡΠ΅ΠΎΠ±ΡΡΠΈΠ»ΡΡ, Ρ ΠΎΡΡ Ρ Π½Π°Ρ ΠΈ Π±ΡΠ»Π° Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ Π±ΠΎΠ»ΡΡΠ°Ρ Π²ΡΠ±ΠΎΡΠΊΠ°.
ΠΡΠ»ΡΡΠΈΠΊΠ»Π°ΡΡΠΎΠ²ΡΠΉ ΡΠ»ΡΡΠ°ΠΉ#
Π Π·Π°Π΄Π°ΡΠ°Ρ Ρ Π±ΠΎΠ»Π΅Π΅ ΡΠ΅ΠΌ Π΄Π²ΡΠΌΡ ΠΊΠ»Π°ΡΡΠ°ΠΌΠΈ ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ° ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠΈ ΠΌΠΎΠ΄ΠΈΡΠΈΡΠΈΡΡΠ΅ΡΡΡ. Π ΠΈΠΌΠ΅Π½Π½ΠΎ, ΡΠ΅ΠΏΠ΅ΡΡ Π½Π°ΠΌ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌ ΠΊΠΎΡΡΠ΅ΠΊΡΠ½ΡΠ΅ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠΈ Π΄Π»Ρ Π²ΡΠ΅Ρ ΠΊΠ°ΡΠ΅Π³ΠΎΡΠΈΠΉ, ΠΊΠΎΡΠΎΡΡΠ΅ Π΄ΠΎΠ»ΠΆΠ½Ρ ΡΡΠΌΠΌΠΈΡΠΎΠ²Π°ΡΡΡΡ Π² Π΅Π΄ΠΈΠ½ΠΈΡΡ. ΠΠ΄ΠΈΠ½ ΠΈΠ· ΠΏΠΎΠ΄Ρ ΠΎΠ΄ΠΎΠ² ΠΊ ΠΌΡΠ»ΡΡΠΈΠΊΠ»Π°ΡΡΠΎΠ²ΠΎΠΉ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠ΅ β ΡΠ²Π΅Π΄Π΅Π½ΠΈΠ΅ Π·Π°Π΄Π°ΡΠΈ ΠΊ Π½Π°Π±ΠΎΡΡ Π±ΠΈΠ½Π°ΡΠ½ΡΡ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΉ Ρ ΠΏΠΎΡΠ»Π΅Π΄ΡΡΡΠΈΠΌ ΠΎΠ±ΡΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠ΅ΠΌ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠΎΠ².
Π‘ΡΡΠ΅ΡΡΠ²ΡΠ΅Ρ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ ΡΡΠ°Π½Π΄Π°ΡΡΠ½ΡΡ ΡΠΏΠΎΡΠΎΠ±ΠΎΠ² ΡΠ°Π·Π»ΠΎΠΆΠ΅Π½ΠΈΡ ΠΌΠ½ΠΎΠ³ΠΎΠΊΠ»Π°ΡΡΠΎΠ²ΠΎΠΉ Π·Π°Π΄Π°ΡΠΈ Π½Π° Π±ΠΈΠ½Π°ΡΠ½ΡΠ΅ ΠΏΠΎΠ΄Π·Π°Π΄Π°ΡΠΈ. ΠΠ°Π·ΠΎΠ²ΡΠ΅:
One-vs-One (all pairs). ΠΠ»Ρ ΠΊΠ»Π°ΡΡΠΎΠ² \(c_1, c_2, ..., c_n\) ΡΡΡΠΎΠΈΠΌ \(\frac{n(n-1)}{2}\) ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΎΡΠΎΠ² ΠΈ ΠΊΠ°Π»ΠΈΠ±ΡΡΠ΅ΠΌ ΠΊΠ°ΠΆΠ΄ΡΠΉ ΠΈΠ· Π½ΠΈΡ .
One-vs-All: Π΄Π»Ρ \(n\) ΠΊΠ»Π°ΡΡΠΎΠ² ΠΎΠ±ΡΡΠ°Π΅ΠΌ \(n\) ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΎΡΠΎΠ² Β«ΠΎΠ΄ΠΈΠ½ ΠΊΠ»Π°ΡΡ ΠΏΡΠΎΡΠΈΠ² Π²ΡΠ΅Ρ ΠΎΡΡΠ°Π»ΡΠ½ΡΡ Β».
Π sklearn ΠΈ Π΄ΡΡΠ³ΠΈΡ
Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ°Ρ
(Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, SplineCalib) ΡΠ΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½Π° ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠ° Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ One-vs-All.
ΠΠΎΡΠΌΠΎΡΡΠΈΠΌ Π½Π° ΠΏΡΠΈΠΌΠ΅Ρ. Π§ΡΠΎΠ±Ρ ΠΏΠ΅ΡΠ΅ΠΉΡΠΈ ΠΊ ΠΌΠ½ΠΎΠ³ΠΎΠΊΠ»Π°ΡΡΠΎΠ²ΠΎΠΉ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΈ β ΡΠΉΠ΄Π΅ΠΌ ΠΎΡ ΡΠΈΡΠ° ΠΈ Π²ΠΎΠ·ΡΠΌΠ΅ΠΌ ΠΈΡΠΈΡΡ. Π ΠΊΠ°ΡΠ΅ΡΡΠ²Π΅ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΎΡΠ° Π²ΠΎΠ·ΡΠΌΠ΅ΠΌ ΡΠ»ΡΡΠ°ΠΉΠ½ΡΠΉ Π»Π΅Ρ, ΠΊΠΎΡΠΎΡΡΠΉ ΡΠ°ΠΊΠΆΠ΅ ΠΌΠΎΠΆΠ΅Ρ ΠΈΠ· ΠΊΠΎΡΠΎΠ±ΠΊΠΈ Π΄Π°Π²Π°ΡΡ Π²Π΅ΡΠΎΡΡΠΎΡΡΠΈ.
multiclass_results = []
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import label_binarize
X_multiclass, y_multiclass = load_iris(return_X_y=True)
X_train_multiclass, X_temp_multiclass, y_train_multiclass, y_temp_multiclass = (
train_test_split(
X_multiclass,
y_multiclass,
test_size=0.4,
random_state=42,
stratify=y_multiclass,
)
)
X_valid_multiclass, X_test_multiclass, y_valid_multiclass, y_test_multiclass = (
train_test_split(
X_temp_multiclass,
y_temp_multiclass,
test_size=0.5,
random_state=42,
stratify=y_temp_multiclass,
)
)
clf = RandomForestClassifier(random_state=999, max_depth=1)
# ΠΎΠ±ΡΡΠ°Π΅ΠΌ Π±Π΅Π· ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠΈ
clf.fit(X_train_multiclass, y_train_multiclass)
probs_base = clf.predict_proba(X_test_multiclass)
multiclass_results.extend(
[
{
"Calibration": None,
"LogLoss": log_loss(y_test_multiclass, probs_base),
"BrierScore": brier_score_loss(
label_binarize(y_test_multiclass, classes=[0, 1, 2]).ravel(),
probs_base.ravel(),
),
}
]
)
pd.DataFrame(multiclass_results)
| Calibration | LogLoss | BrierScore | |
|---|---|---|---|
| 0 | None | 0.477278 | 0.098811 |
ΠΠΎΡΠΌΠΎΡΡΠΈΠΌ Π½Π° ΡΠΌΠΏΡΠΈΡΠΈΡΠ΅ΡΠΊΠΈΠ΅ ΡΠ°ΡΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΡ Π² Π΄Π°Π½Π½ΡΡ .
for y, name in zip(
[y_train_multiclass, y_valid_multiclass, y_test_multiclass],
["Train", "Calibration", "Test"],
):
data = pd.DataFrame(np.unique(y, return_counts=True))
print(name)
display(data)
print("\n")
Train
| 0 | 1 | 2 | |
|---|---|---|---|
| 0 | 0 | 1 | 2 |
| 1 | 30 | 30 | 30 |
Calibration
| 0 | 1 | 2 | |
|---|---|---|---|
| 0 | 0 | 1 | 2 |
| 1 | 10 | 10 | 10 |
Test
| 0 | 1 | 2 | |
|---|---|---|---|
| 0 | 0 | 1 | 2 |
| 1 | 10 | 10 | 10 |
ΠΠ»Π°ΡΡΡ ΠΈΠ΄Π΅Π°Π»ΡΠ½ΠΎ ΡΠ±Π°Π»Π°Π½ΡΠΈΡΠΎΠ²Π°Π½Π½Ρ. ΠΠ°ΠΊ Π²ΡΠ³Π»ΡΠ΄ΡΡ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠΈ Π»Π΅ΡΠ°?
import matplotlib.pyplot as plt
probas_unc_multiclass = clf.predict_proba(X_test_multiclass)
n_classes = probas_unc_multiclass.shape[1]
fig, axes = plt.subplots(1, n_classes, figsize=(14, 4), sharey=True)
for i in range(n_classes):
axes[i].hist(probas_unc_multiclass[:, i], bins=10, alpha=0.7, color=f"C{i}")
axes[i].set_title(f"ΠΠ»Π°ΡΡ {i}", fontsize=15)
axes[i].set_xlabel("ΠΠ΅ΡΠΎΡΡΠ½ΠΎΡΡΡ", fontsize=10)
axes[i].axvline(x=0.0, color="black", linestyle="--", linewidth=1)
axes[i].axvline(x=1.0, color="black", linestyle="--", linewidth=1)
axes[i].set_xlim(-0.05, 1.05)
if i == 0:
axes[i].set_ylabel("Π§Π°ΡΡΠΎΡΠ°", fontsize=15)
Π’Π΅ΠΏΠ΅ΡΡ Π²ΡΠΏΠΎΠ»Π½ΠΈΠΌ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΡ.
from sklearn.frozen import FrozenEstimator
cal_clf = CalibratedClassifierCV(FrozenEstimator(clf), method="sigmoid")
cal_clf.fit(X_valid_multiclass, y_valid_multiclass)
probs_cal = cal_clf.predict_proba(X_test_multiclass)
multiclass_results.extend(
[
{
"Calibration": "sigmoid",
"LogLoss": log_loss(y_test_multiclass, probs_cal),
"BrierScore": brier_score_loss(
label_binarize(y_test_multiclass, classes=[0, 1, 2]).ravel(),
probs_cal.ravel(),
),
}
]
)
pd.DataFrame(multiclass_results)
| Calibration | LogLoss | BrierScore | |
|---|---|---|---|
| 0 | None | 0.477278 | 0.098811 |
| 1 | sigmoid | 0.377704 | 0.074093 |
import matplotlib.pyplot as plt
n_classes = probas_unc_multiclass.shape[1]
fig, axes = plt.subplots(1, n_classes, figsize=(14, 4), sharey=True)
for i in range(n_classes):
axes[i].hist(probs_cal[:, i], bins=10, alpha=0.7, color=f"C{i}")
axes[i].set_title(f"ΠΠ»Π°ΡΡ {i}", fontsize=15)
axes[i].set_xlabel("ΠΠ΅ΡΠΎΡΡΠ½ΠΎΡΡΡ", fontsize=10)
axes[i].axvline(x=0.0, color="black", linestyle="--", linewidth=1)
axes[i].axvline(x=1.0, color="black", linestyle="--", linewidth=1)
axes[i].set_xlim(-0.05, 1.05)
if i == 0:
axes[i].set_ylabel("Π§Π°ΡΡΠΎΡΠ°", fontsize=15)
ΠΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠ° Π½Π΅Π·Π½Π°ΡΠΈΠΌΠΎ, Π½ΠΎ ΡΠ»ΡΡΡΠΈΠ»Π° Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠΈ. ΠΡΠ»ΠΈ ΠΎΡ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠ° ΡΡΠ΅Π±ΡΠ΅ΡΡΡ ΠΈΠ΄Π΅Π°Π», ΡΠΎ, ΠΊΠ°ΠΊ ΠΌΡ ΡΠΏΠΎΠΌΡΠ½ΡΠ»ΠΈ Π² ΡΠ΅ΠΌΠΈΠ½Π°ΡΠ΅, ΡΡΠΎΠΈΡ ΡΠ°ΡΡΠΌΠΎΡΡΠ΅ΡΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ, ΠΊΠΎΡΠΎΡΡΠ΅ ΠΈΠ·Π½Π°ΡΠ°Π»ΡΠ½ΠΎ ΡΡΡΠΎΡΡ ΠΌΠ½ΠΎΠ³ΠΎΠΊΠ»Π°ΡΡΠΎΠ²ΠΎΠ΅ ΡΠ°ΡΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅.
Π’Π°ΠΊΠΆΠ΅ ΠΎΠ±ΡΠ°ΡΠΈΡΠ΅ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅, ΡΡΠΎ ΡΠ°ΡΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠ΅ΠΉ, Π΄Π°ΠΆΠ΅ ΠΏΠΎΡΠ»Π΅ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠΈ, Π½Π΅ Π²ΡΠ΅Π³Π΄Π° ΠΈΠΌΠ΅Π΅Ρ Π·Π½Π°ΡΠ΅Π½ΠΈΡ, Π±Π»ΠΈΠ·ΠΊΠΈΠ΅ ΠΊ Π΅Π΄ΠΈΠ½ΠΈΡΠ΅ Π² ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½Π½ΡΡ ΠΊΠ»Π°ΡΡΠ°Ρ . ΠΠ°ΠΆΠ΅ Π² ΡΠ»ΡΡΠ°Π΅ Π±ΠΈΠ½Π°ΡΠ½ΠΎΠΉ ΠΊΠ»Π°ΡΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΈ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠ° Π½Π΅ΡΠ΅Π΄ΠΊΠΎ ΠΏΡΠΈΠ²ΠΎΠ΄ΠΈΡ ΠΊ ΡΠΈΡΡΠ°ΡΠΈΠΈ, ΠΊΠΎΠ³Π΄Π°, ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½Π°Ρ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΡ ΠΏΠΎΠ»ΠΎΠΆΠΈΡΠ΅Π»ΡΠ½ΠΎΠ³ΠΎ ΠΊΠ»Π°ΡΡΠ°, Π²ΡΠ΄Π°Π²Π°Π΅ΠΌΠ°Ρ ΠΎΡΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²Π°Π½Π½ΠΎΠΉ ΠΌΠΎΠ΄Π΅Π»ΡΡ, ΠΌΠ΅Π½ΡΡΠ΅ 1. ΠΡΠΎ Π½Π΅ Π±Π°Π³, Π° ΡΠΈΡΠ°, ΠΊΠΎΡΠΎΡΠ°Ρ Π»ΠΈΡΡ Π³ΠΎΠ²ΠΎΡΠΈΡ ΠΎ ΡΠΎΠΌ, ΡΡΠΎ Π΄Π°ΠΆΠ΅ ΡΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²Π°Π½Π½Π°Ρ ΠΌΠΎΠ΄Π΅Π»Ρ Π½Π΅ Π±ΡΠ²Π°Π΅Ρ ΡΠ²Π΅ΡΠ΅Π½Π° Π½Π° 100% ΠΏΡΠΎΡΠ΅Π½ΡΠΎΠ² ΠΈ ΡΠ»Π΅Π΄ΡΠ΅Ρ ΡΠΌΠΎΡΡΠ΅ΡΡ Π² ΡΠΎΠΌ ΡΠΈΡΠ»Π΅ Π½Π° Π΄ΡΡΠ³ΠΈΠ΅ Π½Π΅ ΠΌΠ΅Π½Π΅Π΅ Π²Π°ΠΆΠ½ΡΠ΅ ΠΌΠ΅ΡΡΠΈΠΊΠΈ
ΠΠ΄Π΅Π°Π»ΡΠ½ΠΎΠ΅ ΠΏΡΠΈΠ±Π»ΠΈΠΆΠ΅Π½ΠΈΠ΅#
Π Ρ ΠΎΠ΄Π΅ ΡΠ΅ΠΌΠΈΠ½Π°ΡΠ° ΠΌΡ Π½Π°Π·ΡΠ²Π°Π»ΠΈ ΠΏΡΠΈΠ΅ΠΌΠ»Π΅ΠΌΡΠΌΠΈ Π΄Π°ΠΆΠ΅ ΡΠ΅ ΡΠ»ΡΡΠ°ΠΈ, Π³Π΄Π΅ Π±ΡΠ»ΠΈ ΠΎΡΠΊΠ»ΠΎΠ½Π΅Π½ΠΈΡ ΠΎΡ ΠΈΠ΄Π΅Π°Π»ΡΠ½ΠΎΠΉ ΠΊΡΠΈΠ²ΠΎΠΉ. ΠΡΠΈΡΠΈΠ½Π° ΠΊΡΠΎΠ΅ΡΡΡ Π² Π΄ΠΎΠ²Π΅ΡΠΈΡΠ΅Π»ΡΠ½ΠΎΠΌ ΠΈΠ½ΡΠ΅ΡΠ²Π°Π»Π΅ - ΡΡΠΎ Π΄ΠΎΠΏΡΡΡΠΈΠΌΡΠΉ ΡΠ°Π·Π±ΡΠΎΡ, Π² ΠΏΡΠ΅Π΄Π΅Π»Π°Ρ ΠΊΠΎΡΠΎΡΠΎΠ³ΠΎ ΠΏΡΠ΅Π΄ΡΠΊΠ°Π·Π°Π½Π½ΡΠ΅ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΠΈ Π²ΡΡ Π΅ΡΡ ΡΡΠΈΡΠ°ΡΡΡΡ ΠΊΠΎΡΡΠ΅ΠΊΡΠ½ΡΠΌΠΈ.
ΠΡΡΡΡΠΎ ΠΎΡΠ΅Π½ΠΈΡΡ Π΄ΠΎΠ²Π΅ΡΠΈΡΠ΅Π»ΡΠ½ΡΠΉ ΠΈΠ½ΡΠ΅ΡΠ²Π°Π» ΠΌΠΎΠΆΠ½ΠΎ Ρ ΠΏΠΎΠΌΠΎΡΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ ml_insights. Π₯ΠΎΡΡ ΡΡΠΎΡ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½Ρ ΠΈ Π½Π΅ Π½ΠΎΠ²ΡΠΉ, Π΄Π»Ρ Π½Π°ΡΠ΅ΠΉ Π·Π°Π΄Π°ΡΠΈ ΠΎΠ½ ΠΎΡΠ»ΠΈΡΠ½ΠΎ ΠΏΠΎΠ΄ΠΎΠΉΠ΄ΡΡ. ΠΡΡΡ ΡΠ°ΠΊΠΆΠ΅ Π°Π»ΡΡΠ΅ΡΠ½Π°ΡΠΈΠ²Π° Π² Π²ΠΈΠ΄Π΅ Π±ΠΎΠ»Π΅Π΅ ΠΌΠΎΠ΄Π½ΠΎΠ³ΠΎ relplot ΠΎΡ ΠΊΠΎΠΌΠΏΠ°Π½ΠΈΠΈ Apple
!pip install -qU ml_insights relplot
WARNING: Ignoring invalid distribution ~lotly (/usr/local/lib/python3.12/dist-packages)
WARNING: Ignoring invalid distribution ~lotly (/usr/local/lib/python3.12/dist-packages)
Π MLI Π΅ΡΡΡ Π΄Π²Π° ΡΠΏΠΎΡΠΎΠ±Π° ΡΠ°ΡΡΡΠ΅ΡΠ° ΠΠ, ΠΎΡΠ½ΠΎΡΠΈΡΠ΅Π»ΡΠ½ΠΎ ΡΠΎΡΠΊΠΈ (ci_ref='point') ΠΈ ΠΎΡΠ½ΠΎΡΠΈΡΠ΅Π»ΡΠ½ΠΎ ΠΈΠ΄Π΅Π°Π»ΡΠ½ΠΎΠΉ ΠΎΡΠΈ (ci_ref='axis').
ΠΠ΅ΡΠ²Π°Ρ ΡΡΡΠ°ΡΠ΅Π³ΠΈΡ ci_ref='point' ΠΏΠΎΠΊΠ°Π·ΡΠ²Π°Π΅Ρ:
Ρ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΡΡ \(1-\alpha\) ΡΠΌΠΏΠΈΡΠΈΡΠ΅ΡΠΊΠ°Ρ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΡ ΠΌΠΎΠΆΠ΅Ρ Π±ΡΡΡ Π² ΡΡΠΈΡ Π΄ΠΈΠΏΠ°Π·ΠΎΠ½Π°Ρ . Π ΡΡΠΎΠΌ ΡΠ»ΡΡΠ°Π΅ ΡΠΌΠΎΡΡΠΈΠΌ, ΠΏΠΎΠΏΠ°Π΄Π°Π΅Ρ Π»ΠΈ ΠΠ Π½Π° ΠΈΠ΄Π΅Π°Π»ΡΠ½ΡΡ ΠΊΡΠΈΠ²ΡΡ;
ΠΡΠΎΡΠ°Ρ ΡΡΡΠ°ΡΠ΅Π³ΠΈΡ ci_ref='axis' ΠΏΠΎΠΊΠ°Π·ΡΠ²Π°Π΅Ρ:
Ρ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΡΡ \(1-\alpha\) ΡΠΌΠΏΠΈΡΠΈΡΠ΅ΡΠΊΠ°Ρ ΡΠ°ΡΡΠΎΡΠ° Π»Π΅ΠΆΠΈΡ Π² ΡΡΠΎΠΌ ΠΈΠ½ΡΠ΅ΡΠ²Π°Π»Π΅. Π ΡΡΠΎΠΌ ΡΠ»ΡΡΠ°Π΅ ΡΠΌΠΎΡΡΠΈΠΌ, ΠΏΠΎΠΏΠ°Π΄Π°Π΅Ρ Π»ΠΈ ΡΠΎΡΠΊΠ° Π² ΠΠ.
import ml_insights as mli
plt.figure(figsize=(14, 5))
fig = mli.plot_reliability_diagram(
y_test, lr_pred, show_histogram=True, ci_ref="axis", error_bar_alpha=0.05
)
ΠΠ°ΠΊ Π²ΠΈΠ΄ΠΈΠΌ, Π΄Π»Ρ ΡΠ΅Π³ΡΠ΅ΡΡΠΈΠΈ ΠΏΡΠΎΠ³Π½ΠΎΠ·Ρ (Π²ΡΠ΅ ΡΠΎΡΠΊΠΈ) Π½Π°Ρ ΠΎΠ΄ΡΡΡΡ Π² ΠΎΠΆΠΈΠ΄Π°Π΅ΠΌΡΡ ΠΠ.
plt.figure(figsize=(14, 5))
fig = mli.plot_reliability_diagram(
y_test, svc_pred, show_histogram=True, ci_ref="axis", error_bar_alpha=0.05
)
ΠΠ»Ρ SVC ΡΡΠΎ ΡΠΆΠ΅ Π½Π΅ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΎ.
Π relplot Π΅ΡΡΡ ΠΊΠ°ΠΊ ΠΏΡΠΈΠ²ΡΡΠ½ΡΠ΅ ΠΎΡΠ΅Π½ΠΊΠΈ, ΡΠ°ΠΊ ΠΈ Π±ΠΎΠ»Π΅Π΅ ΠΈΠ½ΡΠ΅ΡΠ΅ΡΠ½ΡΠ΅, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, Π±ΠΈΠ½Π°ΡΠΈΠ·ΠΎΠ²Π°Π½Π½Π°Ρ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΎΡΠ½Π°Ρ ΠΊΡΠΈΠ²Π°Ρ, ΡΠ³Π»Π°ΠΆΠ΅Π½Π½Π°Ρ ΠΈ Π΄ΡΡΠ³ΠΈΠ΅, ΠΎ ΠΊΠΎΡΠΎΡΡΡ
ΠΌΠΎΠΆΠ΅ΡΠ΅ ΠΏΠΎΡΠΈΡΠ°ΡΡ Π² Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΠΈ
import relplot.diagrams as rd
from relplot.estimators import Binning
from sklearn.model_selection import GridSearchCV
def plot_compare(f, y, **kwargs):
fig, axs = plt.subplots(1, 2, figsize=(15, 6), sharey=True)
f, y = map(lambda x: np.array(x, dtype=np.double).reshape(-1).copy(), [f, y])
plot_params = dict(
split_densities=False,
plot_density_ticks=True,
plot_density=True,
plot_confidence_band=False,
)
if kwargs is not None:
plot_params.update(kwargs)
num_bootstrap = 500
binning = GridSearchCV(
Binning(),
param_grid={"bins_cnt": range(10, 25, 1)},
cv=5,
scoring="neg_mean_squared_error",
verbose=1,
)
binning.fit(f.reshape(-1, 1), y - f)
nbins = binning.best_params_["bins_cnt"]
# binning = Binning(15)
_ = rd.rel_diagram_binned(f, y, fig=fig, ax=axs[0], nbins=nbins)
axs[0].set_title("BinnedECE")
_ = rd.rel_diagram(
f, y, fig=fig, ax=axs[1], num_bootstrap=num_bootstrap, **plot_params
)
axs[1].set_title("SmoothECE")
return fig, axs
fig, axs = plot_compare(lr_pred, y_test)
Fitting 5 folds for each of 15 candidates, totalling 75 fits
fig, axs = plot_compare(svc_pred, y_test)
Fitting 5 folds for each of 15 candidates, totalling 75 fits
ΠΡΡΠ³ΠΈΠ΅ ΠΌΠ΅ΡΠΎΠ΄Ρ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠΈ Π΄Π»Ρ ΠΎΠ·Π½Π°ΠΊΠΎΠΌΠ»Π΅Π½ΠΈΡ#
ΠΠ° ΠΏΡΠ°ΠΊΡΠΈΠΊΠ΅ ΡΠ°ΡΡΠΎ Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ ΠΈΠ·ΠΎΡΠΎΠ½ΠΈΡΠ΅ΡΠΊΠΎΠΉ ΡΠ΅Π³ΡΠ΅ΡΡΠΈΠΈ ΠΈΠ»ΠΈ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠΈ ΠΠ»Π°ΡΡΠ°. ΠΠ΄Π½Π°ΠΊΠΎ, Π΅ΡΠ»ΠΈ ΠΎΠ½ΠΈ ΠΏΠΎΠΊΠ°Π·ΡΠ²Π°ΡΡ Π½Π΅ΡΠ΄ΠΎΠ²Π»Π΅ΡΠ²ΠΎΡΠΈΡΠ΅Π»ΡΠ½ΡΠΉ ΡΠ΅Π·ΡΠ»ΡΡΠ°Ρ Π½Π° Π΄Π°Π½Π½ΡΡ , ΠΌΠΎΠΆΠ½ΠΎ ΡΠ°ΠΊΠΆΠ΅ ΡΠ°ΡΡΠΌΠΎΡΡΠ΅ΡΡ Π΄ΡΡΠ³ΠΈΠ΅ Π²Π°ΡΠΈΠ°Π½ΡΡ:
Beta Calubration, ΠΊΠΎΡΠΎΡΠ°Ρ, ΠΊΠ°ΠΊ ΠΌΡ ΠΊΡΠ°ΡΠΊΠΎ ΠΎΠ±ΡΡΠ΄ΠΈΠ»ΠΈ Π² Π»Π΅ΠΊΡΠΈΠΎΠ½Π½ΠΎΠΌ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»Π΅, ΠΏΡΠΎΠ΄ΠΎΠ»ΠΆΠ°Π΅Ρ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΡ ΠΠ»Π°ΡΡΠ°
SplineCalib ΠΈΠ· Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ ML insights.
Venn-Abers, ΠΊΠΎΡΠΎΡΡΠΉ Π·Π°ΡΠ°ΡΡΡΡ ΠΏΠΎΠΊΠ°Π·ΡΠ²Π°Π΅Ρ Π»ΡΡΡΠΈΠΉ ΡΠ΅Π·ΡΠ»ΡΡΠ°Ρ Π·Π° ΡΠ΅ ΠΆΠ΅ Π΄Π΅Π½ΡΠ³ΠΈ
ΠΡΠ΅ Π±ΠΎΠ»ΡΡΠ΅ ΠΌΠ΅ΡΠΎΠ΄ΠΎΠ² ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΠΉΡΠΈ Π² ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΡΡΡΠΈΡ ΠΏΠ°ΠΊΠ΅ΡΠ°Ρ , ΡΡΠ°ΡΡΡΡ ΠΈΠ»ΠΈ ΠΊΡΡΡΠ°Ρ
!pip install -qU betacal venn-abers
WARNING: Ignoring invalid distribution ~lotly (/usr/local/lib/python3.12/dist-packages)
WARNING: Ignoring invalid distribution ~lotly (/usr/local/lib/python3.12/dist-packages)
model = svc
model_name = "SVC"
from venn_abers import VennAbersCV
method = "Venn-Abers"
# ΡΠ΅ΡΠ½Π°Ρ ΠΌΠ°Π³ΠΈΡ, ΡΡΠΎΠ±Ρ venn-abers Π·Π°ΡΠ°Π±ΠΎΡΠ°Π» Π² ΠΊΠ»Π°ΡΡΠ°Ρ
Π±Π΅Π· predict_proba
model.predict_proba = lambda x: np.vstack(
[model.decision_function(x), model.decision_function(x)]
).T
va = VennAbersCV(
estimator=model, inductive=True, cal_size=0.2, random_state=101, shuffle=False
)
va.fit(X_train, y_train.values)
calibrated_model_proba = va.predict_proba(X_test)[:, 1]
log_metric(model_name, method, y_test, calibrated_model_proba)
from betacal import BetaCalibration
method = "Beta"
# ΡΠ΅ΡΠ½Π°Ρ ΠΌΠ°Π³ΠΈΡ, ΡΡΠΎΠ±Ρ va Π·Π°ΡΠ°Π±ΠΎΡΠ°Π» Π² ΠΊΠ»Π°ΡΡΠ°Ρ
Π±Π΅Π· predict_proba
bc = BetaCalibration()
bc.fit(model.predict_proba(X_train)[:, 1], y_train.values)
calibrated_model_proba = bc.predict(model.predict_proba(X_test)[:, 1])
log_metric(model_name, method, y_test, calibrated_model_proba)
from ml_insights import SplineCalib
method = "Spline"
# ΡΠ΅ΡΠ½Π°Ρ ΠΌΠ°Π³ΠΈΡ, ΡΡΠΎΠ±Ρ va Π·Π°ΡΠ°Π±ΠΎΡΠ°Π» Π² ΠΊΠ»Π°ΡΡΠ°Ρ
Π±Π΅Π· predict_proba
sc = SplineCalib()
sc.fit(model.predict_proba(X_train)[:, 1], y_train.values)
calibrated_model_proba = sc.calibrate(model.predict_proba(X_test)[:, 1])
log_metric(model_name, method, y_test, calibrated_model_proba)
# ΠΏΡΠΎΡΡΠΈΡΠ΅, Π»ΡΠ±ΠΈΡΠ΅Π»ΠΈ ΠΏΠ°Π½Π΄Π°ΡΠ°, ΡΠ°ΠΊ Π±ΡΠ΄Π΅Ρ Π³ΠΎΡΠ°Π·Π΄ΠΎ ΠΏΡΠΎΡΠ΅
import polars as pl
metrics = pd.DataFrame(cal_metrics)
probas_df = pd.DataFrame(probas)
print("SVC Metrics")
display(
pl.from_pandas(metrics)
.filter(pl.col("model") == "SVC")
.sort("score")
.group_by("metric")
.agg(pl.col("score", "calibration").first())
)
print("SVC LogLoss")
display(
pl.from_pandas(metrics)
.filter((pl.col("model") == "SVC") & (pl.col("metric") == "LogLoss"))
.sort("score")
)
SVC Metrics
| metric | score | calibration |
|---|---|---|
| str | f64 | str |
| "LogLoss" | 0.030417 | "Isotonic" |
| "BrierScore" | 0.008632 | "Isotonic" |
| "ROC-AUC" | 0.995556 | "Beta" |
SVC LogLoss
| model | metric | calibration | score |
|---|---|---|---|
| str | str | str | f64 |
| "SVC" | "LogLoss" | "Isotonic" | 0.030417 |
| "SVC" | "LogLoss" | "Sigmoid" | 0.032401 |
| "SVC" | "LogLoss" | "Venn-Abers" | 0.036094 |
| "SVC" | "LogLoss" | "Spline" | 0.044212 |
| "SVC" | "LogLoss" | "Beta" | 0.046303 |
| "SVC" | "LogLoss" | "None" | 0.364177 |
ΠΠ°ΠΊ Π²ΠΈΠ΄Π½ΠΎ, Π½ΠΎΠ²ΡΠ΅ ΠΌΠ΅ΡΠΎΠ΄Ρ ΠΊΠ°ΡΠ΄ΠΈΠ½Π°Π»ΡΠ½ΠΎ Π½Π΅ ΡΠ»ΡΡΡΠΈΠ»ΠΈ ΡΠΈΡΡΠ°ΡΠΈΡ, Π½ΠΎ, ΠΊΠ°ΠΊ ΠΈ Π²ΡΠ΅Π³Π΄Π° Π² ΠΌΠΈΡΠ΅ ΠΌΠ°ΡΠΈΠ½Π½ΠΎΠ³ΠΎ ΠΎΠ±ΡΡΠ΅Π½ΠΈΡ, Π½ΠΈΠΊΠΎΠ³Π΄Π° Π½Π΅ ΠΈΠ·Π²Π΅ΡΡΠ½ΠΎ, ΡΡΠΎ ΠΈΠΌΠ΅Π½Π½ΠΎ Π·Π°ΠΉΠ΄Π΅Ρ Π² ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΠΎΠΉ Π·Π°Π΄Π°ΡΠ΅, ΠΏΠΎΡΡΠΎΠΌΡ Π½Π΅ ΡΡΠΎΠΈΡ ΡΡΠ°Π·Ρ ΡΠΏΠΈΡΡΠ²Π°ΡΡ ΠΈΡ ΡΠΎ ΡΡΠ΅ΡΠΎΠ²
ΠΠ°ΠΊΠΎΠ½Π΅Ρ, ΠΏΠΎΡΡΡΠΎΠΈΠΌ ΡΠΈΠ½Π°Π»ΡΠ½ΡΠΉ Π³ΡΠ°ΡΠΈΠΊ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΎΡΠ½ΡΡ ΠΊΡΠΈΠ²ΡΡ . ΠΠΈΠ½ΠΈΠΉ ΡΠ°ΠΌ Π΄ΠΎΠ²ΠΎΠ»ΡΠ½ΠΎ ΠΌΠ½ΠΎΠ³ΠΎ, ΠΏΠΎΡΡΠΎΠΌΡ ΠΎΠ½ ΡΠ΄Π΅Π»Π°Π½ ΠΈΠ½ΡΠ΅ΡΠ°ΠΊΡΠΈΠ²Π½ΡΠΌ, ΠΏΡΠΈ ΠΆΠ΅Π»Π°Π½ΠΈΠΈ ΠΌΠΎΠΆΠ΅ΡΠ΅ ΠΏΠΎΡΡΠΊΠ°ΡΡ ΠΈ ΡΠ±ΡΠ°ΡΡ ΡΠ°ΡΡΡ ΠΈΠ· Π½ΠΈΡ
!pip install -qU plotly
WARNING: Ignoring invalid distribution ~lotly (/usr/local/lib/python3.12/dist-packages)
WARNING: Ignoring invalid distribution ~lotly (/usr/local/lib/python3.12/dist-packages)
WARNING: Ignoring invalid distribution ~lotly (/usr/local/lib/python3.12/dist-packages)
import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(
go.Scatter(
x=[0, 1], y=[0, 1], line=dict(dash="dash", color="green"), name="Perfect"
)
)
for idx, method in enumerate(
["None", "Sigmoid", "Isotonic", "Spline", "Beta", "Venn-Abers"]
):
svc_true_prob, svc_pred_prob = calibration_curve(
y_test,
probas_df[(probas_df.model == "SVC") & (probas_df.calibration == method)][
"proba"
].item(),
n_bins=15,
)
fig.add_trace(go.Scatter(x=svc_pred_prob, y=svc_true_prob, name=method))
fig.update_layout(
height=800,
title="Calibration Comparison",
xaxis=dict(title=dict(text="Mean predicted probability")),
yaxis=dict(title=dict(text="Fraction of positives")),
legend=dict(title=dict(text="Calibration")),
)
fig.show()
ΠΠ°ΠΊΠΎΠ³ΠΎ-ΡΠΎ Π»ΡΡΡΠ΅Π³ΠΎ ΠΌΠ΅ΡΠΎΠ΄Π°, ΡΠ²Ρ Π½Π΅ ΡΡΡΠ΅ΡΡΠ²ΡΠ΅Ρ. ΠΡΠ±ΠΈΡΠ°ΡΡ ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΎΡΠ½ΡΡ ΠΊΡΠΈΠ²ΡΡ ΠΏΠΎΠ΄ Π²Π°ΡΡ Π·Π°Π΄Π°ΡΡ - ΡΡΠΎ ΡΠΆΠ΅ ΠΈΡΠΊΡΡΡΡΠ²ΠΎ. ΠΠΎΠΆΠ½ΠΎ Π»ΠΈΡΡ ΡΠ°ΡΡΡΠ΄ΠΈΡΡ, ΡΡΠΎ ΠΊΠ°ΠΊΠΈΠ΅-ΡΠΎ ΠΌΠ΅ΡΠΎΠ΄Ρ, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, ΡΠΏΠ»Π°ΠΉΠ½-ΠΊΠ°Π»ΠΈΠ±ΡΠΎΠ²ΠΊΠ° ΠΏΠ»ΠΎΡ ΠΎ ΠΏΠΎΠΊΠ°Π·ΡΠ²Π°ΡΡ ΡΠ΅Π±Ρ Π½Π° ΠΎΠ±ΡΠ΅ΠΊΡΠ°Ρ Ρ Π½ΠΈΠ·ΠΊΠΎΠΉ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΡΡ, ΠΊΠ°ΠΊΠΈΠ΅-ΡΠΎ Π½Π°ΠΎΠ±ΠΎΡΠΎΡ, Π½Π° ΠΎΠ±ΡΠ΅ΠΊΡΠ°Ρ Ρ Π²ΡΡΠΎΠΊΠΎΠΉ. Π§ΡΠΎ ΠΈΠ· ΡΡΠΎΠ³ΠΎ Π»ΡΡΡΠ΅ ΡΠΆΠ΅ Π·Π°Π²ΠΈΡΠΈΡ ΠΎΡ ΠΏΠΎΡΡΠ°Π½ΠΎΠ²ΠΊΠΈ Π·Π°Π΄Π°ΡΠΈ. ΠΡΠΌΠ°ΠΉΡΠ΅ ΠΈ ΠΈΡΡΠ»Π΅Π΄ΡΠΉΡΠ΅!