Krok 1: Import bibliotek¶
In [1]:
# Importujemy pandas - bibliotekę do pracy z danymi (jak Excel w Pythonie)
import pandas as pd
# Importujemy PyCaret - narzędzie AutoML, które automatyzuje proces uczenia maszynowego
from pycaret.classification import *
# Importujemy numpy - bibliotekę do obliczeń numerycznych
import numpy as np
print("✅ Biblioteki zaimportowane!")
✅ Biblioteki zaimportowane!
Krok 2: Wczytanie danych¶
In [2]:
# Wczytujemy dane o klientach firmy telekomunikacyjnej z pliku CSV
# CSV to format pliku, gdzie dane są oddzielone przecinkami (jak tabela w Excelu)
df = pd.read_csv('data/WA_Fn-UseC_-Telco-Customer-Churn.csv')
# Wyświetlamy informacje o danych
print(f"📊 Liczba klientów w bazie: {len(df)}")
print(f"📋 Liczba kolumn (cech): {len(df.columns)}")
# Pokazujemy pierwsze 5 wierszy, żeby zobaczyć jak wyglądają dane
print("\n🔍 Pierwsze 5 wierszy danych:")
df.head()
📊 Liczba klientów w bazie: 7043 📋 Liczba kolumn (cech): 21 🔍 Pierwsze 5 wierszy danych:
Out[2]:
| customerID | gender | SeniorCitizen | Partner | Dependents | tenure | PhoneService | MultipleLines | InternetService | OnlineSecurity | ... | DeviceProtection | TechSupport | StreamingTV | StreamingMovies | Contract | PaperlessBilling | PaymentMethod | MonthlyCharges | TotalCharges | Churn | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7590-VHVEG | Female | 0 | Yes | No | 1 | No | No phone service | DSL | No | ... | No | No | No | No | Month-to-month | Yes | Electronic check | 29.85 | 29.85 | No |
| 1 | 5575-GNVDE | Male | 0 | No | No | 34 | Yes | No | DSL | Yes | ... | Yes | No | No | No | One year | No | Mailed check | 56.95 | 1889.5 | No |
| 2 | 3668-QPYBK | Male | 0 | No | No | 2 | Yes | No | DSL | Yes | ... | No | No | No | No | Month-to-month | Yes | Mailed check | 53.85 | 108.15 | Yes |
| 3 | 7795-CFOCW | Male | 0 | No | No | 45 | No | No phone service | DSL | Yes | ... | Yes | Yes | No | No | One year | No | Bank transfer (automatic) | 42.30 | 1840.75 | No |
| 4 | 9237-HQITU | Female | 0 | No | No | 2 | Yes | No | Fiber optic | No | ... | No | No | No | No | Month-to-month | Yes | Electronic check | 70.70 | 151.65 | Yes |
5 rows × 21 columns
In [3]:
# Sprawdzamy kolumny w danych
print("\n📋 Kolumny w danych:")
print(df.columns.tolist())
# Informacje o typach danych
print("\n🔍 Typy danych:")
print(df.info())
📋 Kolumny w danych: ['customerID', 'gender', 'SeniorCitizen', 'Partner', 'Dependents', 'tenure', 'PhoneService', 'MultipleLines', 'InternetService', 'OnlineSecurity', 'OnlineBackup', 'DeviceProtection', 'TechSupport', 'StreamingTV', 'StreamingMovies', 'Contract', 'PaperlessBilling', 'PaymentMethod', 'MonthlyCharges', 'TotalCharges', 'Churn'] 🔍 Typy danych: <class 'pandas.core.frame.DataFrame'> RangeIndex: 7043 entries, 0 to 7042 Data columns (total 21 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 customerID 7043 non-null object 1 gender 7043 non-null object 2 SeniorCitizen 7043 non-null int64 3 Partner 7043 non-null object 4 Dependents 7043 non-null object 5 tenure 7043 non-null int64 6 PhoneService 7043 non-null object 7 MultipleLines 7043 non-null object 8 InternetService 7043 non-null object 9 OnlineSecurity 7043 non-null object 10 OnlineBackup 7043 non-null object 11 DeviceProtection 7043 non-null object 12 TechSupport 7043 non-null object 13 StreamingTV 7043 non-null object 14 StreamingMovies 7043 non-null object 15 Contract 7043 non-null object 16 PaperlessBilling 7043 non-null object 17 PaymentMethod 7043 non-null object 18 MonthlyCharges 7043 non-null float64 19 TotalCharges 7043 non-null object 20 Churn 7043 non-null object dtypes: float64(1), int64(2), object(18) memory usage: 1.1+ MB None
Krok 3: Przygotowanie danych¶
In [4]:
# Usuwamy kolumnę customerID, bo to tylko numer identyfikacyjny
# Nie pomaga w przewidywaniu (jak numer PESEL - nic nie mówi o zachowaniu klienta)
df = df.drop('customerID', axis=1)
# Sprawdzamy czy są brakujące wartości (puste komórki w danych)
print("🔍 Sprawdzanie brakujących wartości...")
missing = df.isnull().sum()
print(f"\nLiczba brakujących wartości: {missing.sum()}")
# Sprawdzamy rozkład kolumny Churn (ile klientów odeszło, ile zostało)
print("\n📊 Rozkład targetu (Churn):")
print(df['Churn'].value_counts())
print(f"\nProcent klientów, którzy odeszli: {(df['Churn'] == 'Yes').sum() / len(df) * 100:.1f}%")
🔍 Sprawdzanie brakujących wartości... Liczba brakujących wartości: 0 📊 Rozkład targetu (Churn): Churn No 5174 Yes 1869 Name: count, dtype: int64 Procent klientów, którzy odeszli: 26.5%
In [5]:
# TotalCharges powinno być liczbą, ale może być tekstem z spacjami
# Sprawdzamy i naprawiamy
print("\n🔧 Naprawiamy kolumnę TotalCharges...")
# Konwertujemy na liczby, nieprawidłowe wartości zastępujemy NaN
df['TotalCharges'] = pd.to_numeric(df['TotalCharges'], errors='coerce')
# Sprawdzamy ile mamy NaN
nan_count = df['TotalCharges'].isnull().sum()
print(f"Znaleziono {nan_count} nieprawidłowych wartości w TotalCharges")
if nan_count > 0:
# Wypełniamy NaN zerem (nowi klienci mają tenure=0, więc TotalCharges=0 ma sens)
df['TotalCharges'].fillna(0, inplace=True)
print("✅ Wypełniono zerami (nowi klienci bez historii płatności)")
🔧 Naprawiamy kolumnę TotalCharges... Znaleziono 11 nieprawidłowych wartości w TotalCharges ✅ Wypełniono zerami (nowi klienci bez historii płatności)
Krok 4: Konfiguracja PyCaret¶
In [6]:
# Inicjalizujemy PyCaret - przygotowujemy środowisko do uczenia maszynowego
# data = nasze dane
# target = kolumna, którą chcemy przewidzieć (czy klient odejdzie: Yes/No)
# session_id = ustawienie ziarna losowości (dla powtarzalności wyników)
# train_size = 80% danych do treningu, 20% do testu (jak nauka do egzaminu)
# fold = 5-fold cross-validation (dzielimy dane na 5 części i testujemy na każdej)
# normalize = normalizujemy dane numeryczne (wszystkie wartości w podobnej skali)
clf_setup = setup(
data=df,
target='Churn',
session_id=123,
train_size=0.8,
fold=5,
normalize=True,
verbose=False
)
print("\n✅ PyCaret skonfigurowany i gotowy do pracy!")
✅ PyCaret skonfigurowany i gotowy do pracy!
Krok 5: Porównanie modeli i wybór najlepszego¶
In [7]:
# Porównujemy różne algorytmy ML (Gradient Boosting, Random Forest, itd.)
# sort='AUC' - sortujemy modele według AUC (najlepsza metryka dla churn)
# n_select=1 - wybieramy tylko najlepszy model
print("🔄 Porównujemy ~15 różnych algorytmów ML...")
print("To zajmie kilka minut - testujemy każdy algorytm na 5 foldach!\n")
# Porównujemy wszystkie dostępne modele
best_model = compare_models(sort='AUC', n_select=1)
print("\n✅ Najlepszy model wybrany!")
🔄 Porównujemy ~15 różnych algorytmów ML... To zajmie kilka minut - testujemy każdy algorytm na 5 foldach!
| Model | Accuracy | AUC | Recall | Prec. | F1 | Kappa | MCC | TT (Sec) | |
|---|---|---|---|---|---|---|---|---|---|
| gbc | Gradient Boosting Classifier | 0.7993 | 0.8463 | 0.7993 | 0.7903 | 0.7924 | 0.4534 | 0.4578 | 0.3940 |
| lr | Logistic Regression | 0.8039 | 0.8457 | 0.8039 | 0.7967 | 0.7985 | 0.4720 | 0.4753 | 1.1140 |
| ada | Ada Boost Classifier | 0.8019 | 0.8441 | 0.8019 | 0.7936 | 0.7953 | 0.4617 | 0.4661 | 0.2320 |
| ridge | Ridge Classifier | 0.8003 | 0.8373 | 0.8003 | 0.7908 | 0.7921 | 0.4508 | 0.4571 | 0.1240 |
| lda | Linear Discriminant Analysis | 0.7971 | 0.8372 | 0.7971 | 0.7906 | 0.7927 | 0.4586 | 0.4607 | 0.1220 |
| lightgbm | Light Gradient Boosting Machine | 0.7900 | 0.8359 | 0.7900 | 0.7811 | 0.7837 | 0.4321 | 0.4352 | 0.3060 |
| rf | Random Forest Classifier | 0.7913 | 0.8248 | 0.7913 | 0.7802 | 0.7823 | 0.4242 | 0.4302 | 0.2240 |
| svm | SVM - Linear Kernel | 0.7815 | 0.8202 | 0.7815 | 0.7803 | 0.7805 | 0.4351 | 0.4358 | 0.1360 |
| nb | Naive Bayes | 0.6926 | 0.8193 | 0.6926 | 0.7965 | 0.7105 | 0.3795 | 0.4258 | 0.8480 |
| et | Extra Trees Classifier | 0.7746 | 0.7984 | 0.7746 | 0.7630 | 0.7663 | 0.3839 | 0.3880 | 0.2220 |
| knn | K Neighbors Classifier | 0.7581 | 0.7739 | 0.7581 | 0.7535 | 0.7552 | 0.3657 | 0.3667 | 0.8880 |
| qda | Quadratic Discriminant Analysis | 0.6793 | 0.6575 | 0.6793 | 0.6862 | 0.6696 | 0.1771 | 0.1855 | 0.1380 |
| dt | Decision Tree Classifier | 0.7240 | 0.6522 | 0.7240 | 0.7277 | 0.7257 | 0.3013 | 0.3014 | 0.7560 |
| dummy | Dummy Classifier | 0.7346 | 0.5000 | 0.7346 | 0.5397 | 0.6223 | 0.0000 | 0.0000 | 0.1180 |
✅ Najlepszy model wybrany!
Krok 6: Zapisanie wyników PRZED tuningiem¶
In [8]:
# Trenujemy najlepszy model jeszcze raz, żeby zobaczyć szczegółowe wyniki
# fold=5 - testujemy na 5 różnych podzbiór danych (cross-validation)
print("📊 Trenujemy najlepszy model i zapisujemy wyniki PRZED tuningiem...\n")
# Tworzymy model (ten sam algorytm co wybrany w compare_models)
model_before = create_model(best_model, fold=5)
# Pobieramy wyniki cross-validation (średnie wyniki z 5 testów)
# pull() pobiera ostatnią tabelę wyników
results_before = pull()
# Zapisujemy kluczowe metryki PRZED tuningiem
# loc['Mean'] - bierzemy wiersz ze średnimi wynikami
accuracy_before = results_before.loc['Mean', 'Accuracy']
auc_before = results_before.loc['Mean', 'AUC']
recall_before = results_before.loc['Mean', 'Recall']
precision_before = results_before.loc['Mean', 'Prec.']
print("\n✅ Wyniki PRZED tuningiem zapisane!")
print(f"\n📈 Kluczowe metryki PRZED tuningiem:")
print(f" - Accuracy (dokładność ogólna): {accuracy_before:.4f}")
print(f" - AUC (zdolność rozróżniania): {auc_before:.4f}")
print(f" - Recall (% wykrytych odejść): {recall_before:.4f}")
print(f" - Precision (% trafnych alertów): {precision_before:.4f}")
📊 Trenujemy najlepszy model i zapisujemy wyniki PRZED tuningiem...
| Accuracy | AUC | Recall | Prec. | F1 | Kappa | MCC | |
|---|---|---|---|---|---|---|---|
| Fold | |||||||
| 0 | 0.7924 | 0.8331 | 0.7924 | 0.7853 | 0.7879 | 0.4461 | 0.4477 |
| 1 | 0.7968 | 0.8504 | 0.7968 | 0.7861 | 0.7883 | 0.4399 | 0.4455 |
| 2 | 0.7986 | 0.8418 | 0.7986 | 0.7899 | 0.7925 | 0.4548 | 0.4580 |
| 3 | 0.7941 | 0.8431 | 0.7941 | 0.7853 | 0.7881 | 0.4434 | 0.4464 |
| 4 | 0.8144 | 0.8634 | 0.8144 | 0.8050 | 0.8051 | 0.4827 | 0.4913 |
| Mean | 0.7993 | 0.8463 | 0.7993 | 0.7903 | 0.7924 | 0.4534 | 0.4578 |
| Std | 0.0079 | 0.0101 | 0.0079 | 0.0075 | 0.0066 | 0.0155 | 0.0173 |
✅ Wyniki PRZED tuningiem zapisane! 📈 Kluczowe metryki PRZED tuningiem: - Accuracy (dokładność ogólna): 0.7993 - AUC (zdolność rozróżniania): 0.8463 - Recall (% wykrytych odejść): 0.7993 - Precision (% trafnych alertów): 0.7903
Krok 7: Tuning modelu (optymalizacja)¶
In [ ]:
# Tunujemy model - szukamy najlepszych ustawień (hiperparametrów)
# To jak regulacja silnika w samochodzie - szukamy optymalnych ustawień
# optimize='AUC' - optymalizujemy pod kątem AUC (najważniejsza metryka dla churn)
# do optymalizacji używamy domyślnego scikit-optimize (w tym przypadku nie modyfikujemy tego podejścia)
# n_iter=20 - testujemy 20 różnych kombinacji ustawień
print("🔧 Rozpoczynamy tuning modelu...")
print("Szukamy najlepszych ustawień algorytmu (hiperparametrów)")
print("To zajmie kilka minut - testujemy 20 różnych konfiguracji!\n")
# Tunujemy model
model_after = tune_model(model_before, optimize='AUC', n_iter=20)
# Pobieramy wyniki PO tuningu
results_after = pull()
# Zapisujemy kluczowe metryki PO tuningu
accuracy_after = results_after.loc['Mean', 'Accuracy']
auc_after = results_after.loc['Mean', 'AUC']
recall_after = results_after.loc['Mean', 'Recall']
precision_after = results_after.loc['Mean', 'Prec.']
print("\n✅ Tuning zakończony!")
print(f"\n📈 Kluczowe metryki PO tuningu:")
print(f" - Accuracy (dokładność ogólna): {accuracy_after:.4f}")
print(f" - AUC (zdolność rozróżniania): {auc_after:.4f}")
print(f" - Recall (% wykrytych odejść): {recall_after:.4f}")
print(f" - Precision (% trafnych alertów): {precision_after:.4f}")
🔧 Rozpoczynamy tuning modelu... Szukamy najlepszych ustawień algorytmu (hiperparametrów) To zajmie kilka minut - testujemy 20 różnych konfiguracji!
| Accuracy | AUC | Recall | Prec. | F1 | Kappa | MCC | |
|---|---|---|---|---|---|---|---|
| Fold | |||||||
| 0 | 0.7933 | 0.8359 | 0.7933 | 0.7800 | 0.7718 | 0.3834 | 0.4087 |
| 1 | 0.7959 | 0.8545 | 0.7959 | 0.7851 | 0.7717 | 0.3813 | 0.4136 |
| 2 | 0.7959 | 0.8474 | 0.7959 | 0.7829 | 0.7768 | 0.3980 | 0.4200 |
| 3 | 0.7844 | 0.8463 | 0.7844 | 0.7685 | 0.7611 | 0.3537 | 0.3790 |
| 4 | 0.8011 | 0.8622 | 0.8011 | 0.7939 | 0.7755 | 0.3912 | 0.4296 |
| Mean | 0.7941 | 0.8493 | 0.7941 | 0.7821 | 0.7714 | 0.3815 | 0.4102 |
| Std | 0.0055 | 0.0088 | 0.0055 | 0.0082 | 0.0055 | 0.0151 | 0.0171 |
Fitting 5 folds for each of 20 candidates, totalling 100 fits ✅ Tuning zakończony! 📈 Kluczowe metryki PO tuningu: - Accuracy (dokładność ogólna): 0.7941 - AUC (zdolność rozróżniania): 0.8493 - Recall (% wykrytych odejść): 0.7941 - Precision (% trafnych alertów): 0.7821
Krok 8: Porównanie wyników PRZED vs PO tuningu¶
In [10]:
# Obliczamy różnice między wynikami przed i po tuningu
# Różnica w punktach procentowych (p.p.) pokazuje realną poprawę
print("\n" + "="*60)
print("📊 PORÓWNANIE: MODEL PRZED vs PO TUNINGU")
print("="*60)
# Obliczamy różnice (w punktach procentowych)
accuracy_diff = (accuracy_after - accuracy_before) * 100
auc_diff = (auc_after - auc_before) * 100
recall_diff = (recall_after - recall_before) * 100
precision_diff = (precision_after - precision_before) * 100
# Wyświetlamy szczegółowe porównanie
print(f"\n1️⃣ ACCURACY (Dokładność ogólna):")
print(f" Przed: {accuracy_before:.4f} ({accuracy_before*100:.2f}%)")
print(f" Po: {accuracy_after:.4f} ({accuracy_after*100:.2f}%)")
print(f" Zmiana: {accuracy_diff:+.2f} punktów procentowych")
print(f"\n2️⃣ AUC (Zdolność rozróżniania klas):")
print(f" Przed: {auc_before:.4f}")
print(f" Po: {auc_after:.4f}")
print(f" Zmiana: {auc_diff:+.2f} punktów procentowych")
print(f"\n3️⃣ RECALL (Ile % odchodzących klientów wykrywamy):")
print(f" Przed: {recall_before:.4f} ({recall_before*100:.2f}%)")
print(f" Po: {recall_after:.4f} ({recall_after*100:.2f}%)")
print(f" Zmiana: {recall_diff:+.2f} punktów procentowych")
print(f"\n4️⃣ PRECISION (Ile % naszych alertów jest trafnych):")
print(f" Przed: {precision_before:.4f} ({precision_before*100:.2f}%)")
print(f" Po: {precision_after:.4f} ({precision_after*100:.2f}%)")
print(f" Zmiana: {precision_diff:+.2f} punktów procentowych")
print("\n" + "="*60)
============================================================ 📊 PORÓWNANIE: MODEL PRZED vs PO TUNINGU ============================================================ 1️⃣ ACCURACY (Dokładność ogólna): Przed: 0.7993 (79.93%) Po: 0.7941 (79.41%) Zmiana: -0.52 punktów procentowych 2️⃣ AUC (Zdolność rozróżniania klas): Przed: 0.8463 Po: 0.8493 Zmiana: +0.30 punktów procentowych 3️⃣ RECALL (Ile % odchodzących klientów wykrywamy): Przed: 0.7993 (79.93%) Po: 0.7941 (79.41%) Zmiana: -0.52 punktów procentowych 4️⃣ PRECISION (Ile % naszych alertów jest trafnych): Przed: 0.7903 (79.03%) Po: 0.7821 (78.21%) Zmiana: -0.82 punktów procentowych ============================================================
Krok 9: Analiza biznesowa - Czy tuning ma sens?¶
In [12]:
# Obliczamy czy poprawa jest istotna biznesowo
# Zakładamy bazę 10,000 klientów, z czego 27% chce odejść (2,700 klientów)
total_customers = 10000 # Całkowita liczba klientów
churn_rate = 0.27 # 27% klientów odchodzi (na podstawie naszych danych)
churning_customers = int(total_customers * churn_rate) # Klienci faktycznie odchodzący
# Koszt retencji (próba zatrzymania klienta) - np. telefon + oferta specjalna
retention_cost = 50 # 50 zł za próbę zatrzymania
# Wartość klienta - ile tracimy gdy klient odejdzie
customer_value = 500 # 500 zł rocznie (średni przychód)
print("\n" + "="*60)
print("💰 ANALIZA BIZNESOWA - CZY TUNING SIĘ OPŁACA?")
print("="*60)
print(f"\n📊 Założenia:")
print(f" - Baza klientów: {total_customers:,}")
print(f" - Klienci odchodzący: {churning_customers:,} ({churn_rate*100:.0f}%)")
print(f" - Koszt próby zatrzymania: {retention_cost} zł")
print(f" - Wartość klienta rocznie: {customer_value} zł")
# Ile klientów wykrywamy PRZED tuningiem
detected_before = int(churning_customers * recall_before)
# Ile klientów wykrywamy PO tuningu
detected_after = int(churning_customers * recall_after)
# Ile więcej klientów wykrywamy dzięki tuningowi
additional_detected = detected_after - detected_before
print(f"\n🎯 Wykrywanie klientów:")
print(f" - Przed tuningiem: {detected_before:,} klientów")
print(f" - Po tuningu: {detected_after:,} klientów")
print(f" - DODATKOWO wykrytych: {additional_detected:,} klientów")
# Zakładamy, że 30% wykrytych klientów uda się zatrzymać
retention_success_rate = 0.30
# Ile klientów zatrzymamy dodatkowo dzięki tuningowi
additional_retained = int(additional_detected * retention_success_rate)
# Dodatkowy koszt (więcej prób = więcej kosztów)
additional_cost = additional_detected * retention_cost
# Dodatkowy przychód (zatrzymani klienci = zachowany przychód)
additional_revenue = additional_retained * customer_value
# Zysk netto z tuningu
net_benefit = additional_revenue - additional_cost
print(f"\n💼 Skutki biznesowe (przy 30% skuteczności retencji):")
print(f" - Dodatkowo zatrzymanych klientów: {additional_retained:,}")
print(f" - Dodatkowy koszt retencji: {additional_cost:,} zł")
print(f" - Dodatkowy przychód (zatrzymani): {additional_revenue:,} zł")
if net_benefit > 0:
print(f" - ZYSK NETTO Z TUNINGU: {net_benefit:,} zł rocznie")
else:
print(f" - STRATA NETTO Z TUNINGU: {net_benefit:,} zł rocznie")
print("\n" + "="*60)
============================================================ 💰 ANALIZA BIZNESOWA - CZY TUNING SIĘ OPŁACA? ============================================================ 📊 Założenia: - Baza klientów: 10,000 - Klienci odchodzący: 2,700 (27%) - Koszt próby zatrzymania: 50 zł - Wartość klienta rocznie: 500 zł 🎯 Wykrywanie klientów: - Przed tuningiem: 2,158 klientów - Po tuningu: 2,144 klientów - DODATKOWO wykrytych: -14 klientów 💼 Skutki biznesowe (przy 30% skuteczności retencji): - Dodatkowo zatrzymanych klientów: -4 - Dodatkowy koszt retencji: -700 zł - Dodatkowy przychód (zatrzymani): -2,000 zł - STRATA NETTO Z TUNINGU: -1,300 zł rocznie ============================================================
Krok 10: Wnioski końcowe dla biznesu¶
In [13]:
# Podsumowanie - czy tuning ma sens?
# Oceniamy na podstawie poprawy metryk i zysku netto
print("\n" + "="*60)
print("🎯 WNIOSKI - CZY TUNING MA SENS BIZNESOWY?")
print("="*60)
# Sprawdzamy czy AUC się poprawił o więcej niż 0.5%
if auc_diff > 0.5:
print("\n✅ TUNING DAŁ REALNĄ POPRAWĘ TECHNICZNĄ!")
print(f" AUC wzrósł o {auc_diff:.2f} punktów procentowych")
print(f" To znacząca poprawa zdolności modelu do rozróżniania klientów")
elif auc_diff > 0:
print("\n⚠️ TUNING DAŁ NIEWIELKĄ POPRAWĘ TECHNICZNĄ")
print(f" AUC wzrósł o {auc_diff:.2f} punktów procentowych")
print(f" Poprawa jest minimalna, model niewiele zyskał")
else:
print("\n❌ TUNING NIE POPRAWIŁ MODELU")
print(f" AUC zmienił się o {auc_diff:.2f} punktów procentowych")
print(f" Model nie zyskał na tuningu")
# Sprawdzamy czy recall się poprawił (czy wykrywamy więcej klientów)
if recall_diff > 1.0:
print("\n✅ WYKRYWAMY ZNACZNIE WIĘCEJ ODCHODZĄCYCH KLIENTÓW!")
print(f" Recall wzrósł o {recall_diff:.2f} punktów procentowych")
print(f" Wykrywamy {additional_detected:,} więcej klientów zagrożonych odejściem")
elif recall_diff > 0:
print("\n✅ WYKRYWAMY TROCHĘ WIĘCEJ ODCHODZĄCYCH KLIENTÓW")
print(f" Recall wzrósł o {recall_diff:.2f} punktów procentowych")
print(f" Wykrywamy {additional_detected:,} więcej klientów zagrożonych odejściem")
else:
print("\n⚠️ NIE WYKRYWAMY WIĘCEJ KLIENTÓW")
print(f" Recall zmienił się o {recall_diff:.2f} punktów procentowych")
# Analiza zysku netto
print("\n💰 ANALIZA FINANSOWA:")
if net_benefit > 10000:
print(f" ✅ TUNING MA DUŻY SENS BIZNESOWY!")
print(f" Roczny zysk: {net_benefit:,} zł")
print(f" ROI: {(net_benefit/additional_cost)*100:.0f}% (świetny zwrot z inwestycji!)")
elif net_benefit > 0:
print(f" ✅ TUNING MA SENS BIZNESOWY")
print(f" Roczny zysk: {net_benefit:,} zł")
print(f" ROI: {(net_benefit/additional_cost)*100:.0f}% (opłaca się!)")
else:
print(f" ❌ TUNING NIE MA SENSU BIZNESOWEGO")
print(f" Strata: {abs(net_benefit):,} zł rocznie")
print(f" Koszt retencji przewyższa korzyści")
# Ostateczna rekomendacja
print("\n🎯 REKOMENDACJA:")
if net_benefit > 5000 and auc_diff > 0.3:
print(" 🌟 ZDECYDOWANIE WDRAŻAJ MODEL PO TUNINGU!")
print(" Tuning dał realną poprawę techniczną i biznesową")
print(" Model będzie lepiej wykrywał klientów zagrożonych odejściem")
elif net_benefit > 0:
print(" ✅ WARTO WDROŻYĆ MODEL PO TUNINGU")
print(" Tuning dał niewielką, ale pozytywną poprawę")
print(" Model będzie generował dodatkowy zysk")
else:
print(" ⚠️ ZOSTAŃ PRZY MODELU PODSTAWOWYM")
print(" Tuning nie dał poprawy lub okazał się nawet gorszy")
print(" Model podstawowy jest wystarczający")
print("\n" + "="*60)
============================================================ 🎯 WNIOSKI - CZY TUNING MA SENS BIZNESOWY? ============================================================ ⚠️ TUNING DAŁ NIEWIELKĄ POPRAWĘ TECHNICZNĄ AUC wzrósł o 0.30 punktów procentowych Poprawa jest minimalna, model niewiele zyskał ⚠️ NIE WYKRYWAMY WIĘCEJ KLIENTÓW Recall zmienił się o -0.52 punktów procentowych 💰 ANALIZA FINANSOWA: ❌ TUNING NIE MA SENSU BIZNESOWEGO Strata: 1,300 zł rocznie Koszt retencji przewyższa korzyści 🎯 REKOMENDACJA: ⚠️ ZOSTAŃ PRZY MODELU PODSTAWOWYM Tuning nie dał poprawy lub okazał się nawet gorszy Model podstawowy jest wystarczający ============================================================
Krok 11: Zapisanie najlepszego modelu¶
In [15]:
# Zapisujemy model przed tuningiem lub po tuningu do pliku (w zależności od wybranej ostatecznej opcji)
# Dzięki temu możemy go później użyć bez ponownego trenowania
print("💾 Zapisywanie modelu...")
# Zapisujemy model do folderu 'models'. Wybieramy model przed tuningiem bo był wystarczający a nawet lepszy
save_model(model_before, 'models/churn_best_model')
print("✅ Model zapisany w folderze 'models/churn_model'!")
print("\n📝 Możesz go później wczytać używając: load_model('models/churn_model')")
💾 Zapisywanie modelu...
Transformation Pipeline and Model Successfully Saved
✅ Model zapisany w folderze 'models/churn_model'!
📝 Możesz go później wczytać używając: load_model('models/churn_model')
📚 Podsumowanie - Co to wszystko znaczy?¶
Co zrobiliśmy?¶
- Wytrenowaliśmy model - algorytm uczący się przewidywać odejścia klientów
- Zoptymalizowaliśmy go (tuning) - szukaliśmy najlepszych ustawień
- Porównaliśmy wyniki - przed i po tuningu
- Oceniliśmy wartość biznesową - czy to się opłaca finansowo
Kluczowe metryki wyjaśnione:¶
- Accuracy = Ile % wszystkich przewidywań jest prawidłowych
- AUC = Jak dobrze model rozróżnia klientów (0.5 = losowanie, 1.0 = perfekcja)
- Recall = Ile % odchodzących klientów wykrywamy
- Precision = Ile % naszych alertów "klient odejdzie" jest trafnych
Co to znaczy w praktyce?¶
- Recall 80% = Na 100 klientów, którzy odejdą, wykryjemy 80
- Precision 75% = Na 100 alertów, 75 będzie prawdziwych, 25 fałszywych
Czy tuning się opłaca?¶
Sprawdź wyniki powyżej! Model sam obliczy czy poprawa jest warta czasu i pieniędzy.