{
  "cells": [
    {
      "cell_type": "markdown",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Hafta 4: MLOps Prensipleri ve Deney Yönetimi\n",
        "\n",
        "**Dersin Hedefleri:**\n",
        "1.  MLOps'un ne olduğunu ve geleneksel DevOps'tan neden farklı olduğunu anlamak.\n",
        "2.  Makine öğrenmesi yaşam döngüsünün (ML Lifecycle) ana aşamalarını tanımak.\n",
        "3.  Deney yönetiminin (Experiment Tracking) neden bir lüks değil, bilimsel titizlik için bir zorunluluk olduğunu kavramak.\n",
        "4.  `MLflow` kütüphanesini kullanarak deney parametrelerini, metrikleri ve model dosyalarını (artifacts) kaydetmek.\n",
        "5.  Farklı deney sonuçlarını karşılaştırarak en iyi modeli seçmek.\n",
        "\n",
        "## 1. MLOps Nedir?\n",
        "\n",
        "**MLOps (Machine Learning Operations)**, makine öğrenmesi modellerini güvenilir ve verimli bir şekilde üretime almak, dağıtmak ve sürdürmek için gereken pratikler, prensipler ve kültür bütünüdür.\n",
        "\n",
        "**MLOps != DevOps:**\n",
        "Geleneksel yazılım (DevOps) sadece **koda** odaklanır. MLOps ise üç hareketli parçayı yönetmek zorundadır:\n",
        "\n",
        "1.  **Kod (Code):** Veri işleme, eğitim, tahmin vb. için yazılan script'ler.\n",
        "2.  **Model (Model):** Eğitim süreci sonunda ortaya çıkan ve versiyonlanması gereken dosya (artifact).\n",
        "3.  **Veri (Data):** Modelin performansını doğrudan etkileyen ve sürekli değişebilen girdi.\n",
        "\n",
        "Bu üçlü yapı, ML sistemlerini daha karmaşık ve dinamik hale getirir.\n",
        "\n",
        "## 2. Deney Yönetimi: Kaostan Düzene\n",
        "\n",
        "Bir veri bilimci olarak sürekli şu sorularla karşılaşırsınız:\n",
        "- *En iyi sonucu veren modeli hangi hiperparametrelerle eğitmiştim?*\n",
        "- *Bu model, veri setinin hangi versiyonuyla eğitildi?*\n",
        "- *İki hafta önce yaptığım denemenin sonuçlarını şimdikilerle nasıl adil bir şekilde karşılaştırabilirim?*\n",
        "\n",
        "Bu sorulara cevap veremiyorsanız, yaptığınız iş tekrarlanabilir değildir. Deney yönetimi, bu soruların cevabını sistematik olarak kaydetme sürecidir.\n",
        "\n",
        "**Ne Kaydedilir?**\n",
        "- **Parametreler:** Modelin hiperparametreleri (örn: `n_estimators`, `learning_rate`), özellik mühendisliği adımları.\n",
        "- **Metrikler:** Modelin performansını ölçen değerler (örn: `accuracy`, `f1-score`, `roc_auc`).\n",
        "- **Artifacts (Çıktılar):** Eğitilmiş model dosyası (`model.pkl`), görselleştirmeler (`confusion_matrix.png`), özellik önem sıralaması (`features.csv`).\n",
        "- **Kaynak Kod:** Deneyi çalıştıran kodun `git` commit hash'i.\n",
        "\n",
        "## 3. `MLflow` ile Pratik Uygulama\n",
        "\n",
        "`MLflow`, deney yönetimi için açık kaynaklı bir standarttır.\n",
        "\n",
        "**Kurulum:**\n",
        "```bash\n",
        "pip install mlflow\n",
        "```\n",
        "\n",
        "**Temel Bileşenler:**\n",
        "- **MLflow Tracking:** Parametreleri, metrikleri ve artifact'leri kaydetmek için kullanılan API.\n",
        "- **MLflow UI:** Deney sonuçlarını görsel olarak incelemek ve karşılaştırmak için web tabanlı bir arayüz. Terminalden `mlflow ui` komutu ile başlatılır.\n",
        "\n",
        "### Uygulama: Churn Modeli Deneylerini Yönetme\n",
        "\n",
        "Önceki haftadan gelen `customer_df` verisini kullanarak bir churn tahmin modeli eğiteceğiz ve farklı parametrelerle yaptığımız denemeleri `MLflow` ile kaydedeceğiz."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "import pandas as pd\n",
        "import numpy as np\n",
        "import mlflow\n",
        "from datetime import datetime, timedelta\n",
        "from sklearn.model_selection import train_test_split\n",
        "from sklearn.linear_model import LogisticRegression\n",
        "from sklearn.ensemble import RandomForestClassifier\n",
        "from sklearn.metrics import accuracy_score, f1_score, roc_auc_score\n",
        "import warnings\n",
        "\n",
        "# Uyarı mesajlarını gizleme (temiz çıktı için)\n",
        "warnings.filterwarnings(\"ignore\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Adım 2: Veri Hazırlama Fonksiyonu\n",
        "\n",
        "Bu fonksiyon, önceki haftalarda öğrendiğimiz veri ön işleme adımlarını kapsüller. Fonksiyonun amacı:\n",
        "\n",
        "1. **Tekrarlanabilirlik**: Aynı veri üretim sürecini her çalıştırmada tutarlı şekilde uygulamak\n",
        "2. **Modülerlik**: Veri hazırlama kodunu ana deney akışından ayırarak kod okunabilirliğini artırmak\n",
        "3. **Esneklik**: Farklı boyutlarda veri setleri oluşturabilme imkanı sağlamak\n",
        "\n",
        "Fonksiyon içinde gerçekleştirilen işlemler:\n",
        "- Sentetik müşteri verisi oluşturma (demographics, behavior, target)\n",
        "- Eksik değer analizi ve doldurma (imputation)\n",
        "- Kategorik değişken kodlama (one-hot encoding)\n",
        "- Zaman tabanlı özellik çıkarımı (feature engineering)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Veri hazırlama fonksiyonu (Hafta 3'ten)\n",
        "def get_prepared_data(num_customers=500):\n",
        "    \"\"\"\n",
        "    Churn prediction için sentetik müşteri verisi oluşturur ve hazırlar.\n",
        "    \n",
        "    Adımlar:\n",
        "    1. Sentetik veri oluşturma\n",
        "    2. Eksik değerleri doldurma (Imputation)\n",
        "    3. Kategorik değişkenleri kodlama (Encoding)\n",
        "    4. Tarih özelliklerini çıkarma (Feature extraction)\n",
        "    \"\"\"\n",
        "    np.random.seed(42)\n",
        "    \n",
        "    # 1. Sentetik veri oluşturma\n",
        "    customer_ids = range(1, num_customers + 1)\n",
        "    ages = np.random.randint(18, 70, size=num_customers)\n",
        "    ages = [age if np.random.rand() > 0.1 else np.nan for age in ages]\n",
        "    cities = np.random.choice(['Istanbul', 'Ankara', 'Izmir'], size=num_customers, p=[0.5, 0.3, 0.2])\n",
        "    end_date = datetime.now()\n",
        "    start_date = end_date - timedelta(days=3*365)\n",
        "    registration_dates = [start_date + timedelta(seconds=np.random.randint(0, int((end_date - start_date).total_seconds()))) for _ in range(num_customers)]\n",
        "    total_spent = np.random.gamma(2, 150, size=num_customers).round(2)\n",
        "    total_spent += np.array([age * 1.5 if not np.isnan(age) else 0 for age in ages])\n",
        "    last_seen_days_ago = np.random.randint(0, 365, size=num_customers)\n",
        "    last_seen_days_ago = [d if np.random.rand() > 0.15 else np.nan for d in last_seen_days_ago]\n",
        "    churn_probability = 1 / (1 + np.exp(-( (np.array(last_seen_days_ago, dtype=float) / 300) - (total_spent / 600) )))\n",
        "    churn = (np.random.rand(num_customers) < churn_probability).astype(int)\n",
        "    \n",
        "    df = pd.DataFrame({\n",
        "        'customer_id': customer_ids, \n",
        "        'age': ages, \n",
        "        'city': cities, \n",
        "        'registration_date': registration_dates, \n",
        "        'total_spent': total_spent, \n",
        "        'last_seen_days_ago': last_seen_days_ago, \n",
        "        'churn': churn\n",
        "    })\n",
        "    \n",
        "    # 2. Eksik değerleri doldurma (Imputation)\n",
        "    df['age'].fillna(df['age'].median(), inplace=True)\n",
        "    df['last_seen_days_ago'].fillna(df['last_seen_days_ago'].median(), inplace=True)\n",
        "    \n",
        "    # 3. Kategorik değişkenleri kodlama (One-hot encoding)\n",
        "    city_dummies = pd.get_dummies(df['city'], prefix='city', drop_first=True)\n",
        "    df = pd.concat([df, city_dummies], axis=1)\n",
        "    df.drop('city', axis=1, inplace=True)\n",
        "\n",
        "    # 4. Tarih özelliklerini çıkarma\n",
        "    df['registration_date'] = pd.to_datetime(df['registration_date'])\n",
        "    df['membership_days'] = (datetime.now() - df['registration_date']).dt.days\n",
        "    df.drop('registration_date', axis=1, inplace=True)\n",
        "    \n",
        "    return df\n",
        "\n",
        "print(\"✓ Veri hazırlama fonksiyonu tanımlandı\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Adım 3: Veri Yükleme ve Train/Test Ayırma\n",
        "\n",
        "Bu adımda veri seti oluşturulur ve makine öğrenmesi modellemesi için uygun formata dönüştürülür.\n",
        "\n",
        "**Stratified Split Kullanımı:**\n",
        "- `stratify=y` parametresi, hedef değişkenin (churn) dağılımının hem eğitim hem de test setinde orantılı şekilde korunmasını sağlar\n",
        "- Bu yaklaşım, özellikle dengesiz sınıf dağılımlarında modelin performansının daha güvenilir değerlendirilmesini mümkün kılar\n",
        "\n",
        "**Test Seti Oranı:**\n",
        "- %20 test verisi, genellikle yeterli değerlendirme gücü sağlarken eğitim için yeterli veri bırakır\n",
        "- Daha küçük veri setlerinde bu oran artırılabilir (örn: %30), büyük veri setlerinde azaltılabilir (örn: %10)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Veriyi hazırlama\n",
        "customer_df = get_prepared_data()\n",
        "\n",
        "# Özellikler (X) ve hedef değişken (y) ayırma\n",
        "X = customer_df.drop(['churn', 'customer_id'], axis=1)\n",
        "y = customer_df['churn']\n",
        "\n",
        "# Train/Test split\n",
        "X_train, X_test, y_train, y_test = train_test_split(\n",
        "    X, y, test_size=0.2, random_state=42, stratify=y\n",
        ")\n",
        "\n",
        "print(f\"✓ Veri hazırlandı: {len(customer_df)} müşteri\")\n",
        "print(f\"  - Eğitim seti: {len(X_train)} örnek\")\n",
        "print(f\"  - Test seti: {len(X_test)} örnek\")\n",
        "print(f\"  - Özellik sayısı: {X.shape[1]}\")\n",
        "print(f\"\\nÖzellikler: {list(X.columns)}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Adım 4: MLflow Deneyini Başlatma\n",
        "\n",
        "**Deney (Experiment) Kavramı:**\n",
        "\n",
        "MLflow'da bir \"experiment\" (deney), belirli bir problemi çözmek için yapılan tüm denemelerin (runs) mantıksal olarak gruplandığı bir konteynerdir. \n",
        "\n",
        "**Deney Organizasyonu:**\n",
        "- Her proje için ayrı bir deney oluşturmak, sonuçların daha kolay yönetilmesini sağlar\n",
        "- Deney adları açıklayıcı olmalıdır (örn: \"Churn Prediction Experiments\", \"Customer Segmentation v2\")\n",
        "- Aynı deney altındaki tüm çalıştırmalar karşılaştırılabilir ve sıralanabilir\n",
        "\n",
        "**MLflow Tracking Serveri:**\n",
        "- Tüm deney verileri yerel olarak `mlruns/` dizininde saklanır\n",
        "- Her deneme için benzersiz bir ID oluşturulur\n",
        "- Parametreler, metrikler ve artifactlar otomatik olarak sürümlenir"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# MLflow deneyini ayarlama\n",
        "mlflow.set_experiment(\"Churn Prediction Experiments\")\n",
        "\n",
        "print(\"✓ MLflow deneyi başlatıldı: 'Churn Prediction Experiments'\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Adım 5: Deney 1 - Logistic Regression\n",
        "\n",
        "**Model Seçimi: Logistic Regression**\n",
        "\n",
        "Logistic Regression, ikili sınıflandırma problemleri için temel bir başlangıç noktasıdır. Avantajları:\n",
        "- Hesaplama açısından verimli\n",
        "- İyi yorumlanabilirlik (katsayılar değişken etkilerini gösterir)\n",
        "- Doğrusal ayırılabilir problemlerde yüksek performans\n",
        "- Overfitting riski düşük\n",
        "\n",
        "**MLflow İş Akışı:**\n",
        "\n",
        "Bir MLflow denemesi beş temel adımdan oluşur:\n",
        "\n",
        "1. **Run Başlatma**: `mlflow.start_run()` ile yeni bir deneme başlatılır\n",
        "2. **Parametre Kaydetme**: `mlflow.log_param()` ile hiperparametreler kaydedilir\n",
        "3. **Model Eğitimi**: Standart scikit-learn API kullanılır\n",
        "4. **Metrik Kaydetme**: `mlflow.log_metric()` ile performans metrikleri kaydedilir\n",
        "5. **Model Kaydetme**: `mlflow.sklearn.log_model()` ile eğitilmiş model artifact olarak saklanır\n",
        "\n",
        "**Metrik Seçimi:**\n",
        "- **Accuracy**: Genel doğruluk oranı (dengeli veri setleri için uygundur)\n",
        "- **F1-Score**: Precision ve recall'un harmonik ortalaması (dengesiz sınıflarda tercih edilir)\n",
        "- **ROC-AUC**: Model ayrım gücünün olasılık eşiklerinden bağımsız ölçüsü"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "with mlflow.start_run(run_name=\"Logistic Regression\"):\n",
        "    print(\"🔄 Çalışan Deney: Logistic Regression\")\n",
        "    \n",
        "    # 1. Hiperparametreleri MLflow'a kaydetme\n",
        "    # Bu parametreler daha sonra deney karşılaştırmalarında kullanılabilir\n",
        "    mlflow.log_param(\"model_type\", \"LogisticRegression\")\n",
        "    mlflow.log_param(\"max_iter\", 1000)\n",
        "    mlflow.log_param(\"solver\", \"lbfgs\")  # Default solver\n",
        "    \n",
        "    # 2. Model eğitimi\n",
        "    # random_state: Tekrarlanabilirlik için sabit tohum değeri\n",
        "    model = LogisticRegression(max_iter=1000, random_state=42)\n",
        "    model.fit(X_train, y_train)\n",
        "    \n",
        "    # 3. Tahminlerin hesaplanması\n",
        "    # y_pred: Sınıf tahminleri (0 veya 1)\n",
        "    # y_proba: Olasılık tahminleri (ROC-AUC hesabı için gerekli)\n",
        "    y_pred = model.predict(X_test)\n",
        "    y_proba = model.predict_proba(X_test)[:, 1]\n",
        "    \n",
        "    # 4. Performans metriklerinin hesaplanması ve kaydedilmesi\n",
        "    accuracy = accuracy_score(y_test, y_pred)\n",
        "    f1 = f1_score(y_test, y_pred)\n",
        "    roc_auc = roc_auc_score(y_test, y_proba)\n",
        "    \n",
        "    mlflow.log_metric(\"accuracy\", accuracy)\n",
        "    mlflow.log_metric(\"f1_score\", f1)\n",
        "    mlflow.log_metric(\"roc_auc\", roc_auc)\n",
        "    \n",
        "    # 5. Sonuçların görüntülenmesi\n",
        "    print(f\"  ✓ Accuracy: {accuracy:.4f}\")\n",
        "    print(f\"  ✓ F1-Score: {f1:.4f}\")\n",
        "    print(f\"  ✓ ROC AUC: {roc_auc:.4f}\")\n",
        "    \n",
        "    # 6. Eğitilmiş modelin artifact olarak kaydedilmesi\n",
        "    # Bu model daha sonra yüklenip kullanılabilir\n",
        "    mlflow.sklearn.log_model(model, \"logistic_regression_model\")\n",
        "    print(f\"  ✓ Model MLflow'a kaydedildi\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Adım 6: Deney 2 - Random Forest (Basit Model)\n",
        "\n",
        "**Model Seçimi: Random Forest**\n",
        "\n",
        "Random Forest, ensemble learning (topluluk öğrenmesi) yaklaşımına dayanan güçlü bir algoritmadır. Temel özellikleri:\n",
        "\n",
        "- **Bagging Mekanizması**: Birden fazla karar ağacının tahminlerini birleştirir\n",
        "- **Özellik Rastgeleliği**: Her ağaç, özelliklerin rastgele alt kümesini kullanır\n",
        "- **Overfitting Direnci**: Tek ağaçlara göre daha genelleştirilebilir\n",
        "\n",
        "**Hiperparametre Konfigürasyonu:**\n",
        "\n",
        "Bu denemede kasıtlı olarak düşük parametre değerleri kullanılmıştır:\n",
        "- `n_estimators=10`: Küçük topluluk (hızlı eğitim, düşük performans)\n",
        "- `max_depth=5`: Sınırlı ağaç derinliği (underfitting riski)\n",
        "\n",
        "**Amaç**: Bu basit model, sonraki gelişmiş modelle karşılaştırma için bir temel (baseline) oluşturur. Parametre seçiminin model performansı üzerindeki etkisini gösterir."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "with mlflow.start_run(run_name=\"Random Forest (n=10)\"):\n",
        "    print(\"🔄 Çalışan Deney: Random Forest (n=10)\")\n",
        "    \n",
        "    # 1. Hiperparametre tanımlama ve kaydetme\n",
        "    n_estimators = 10  # Topluluk içindeki ağaç sayısı\n",
        "    max_depth = 5      # Her ağacın maksimum derinliği\n",
        "    \n",
        "    mlflow.log_param(\"model_type\", \"RandomForestClassifier\")\n",
        "    mlflow.log_param(\"n_estimators\", n_estimators)\n",
        "    mlflow.log_param(\"max_depth\", max_depth)\n",
        "    mlflow.log_param(\"criterion\", \"gini\")  # Bölme kriteri\n",
        "    \n",
        "    # 2. Model oluşturma ve eğitim\n",
        "    # random_state: Bootstrap örneklemesi için sabit tohum\n",
        "    model = RandomForestClassifier(\n",
        "        n_estimators=n_estimators, \n",
        "        max_depth=max_depth, \n",
        "        random_state=42\n",
        "    )\n",
        "    model.fit(X_train, y_train)\n",
        "    \n",
        "    # 3. Tahmin aşaması\n",
        "    y_pred = model.predict(X_test)\n",
        "    y_proba = model.predict_proba(X_test)[:, 1]\n",
        "    \n",
        "    # 4. Metrik hesaplama ve kaydetme\n",
        "    accuracy = accuracy_score(y_test, y_pred)\n",
        "    f1 = f1_score(y_test, y_pred)\n",
        "    roc_auc = roc_auc_score(y_test, y_proba)\n",
        "    \n",
        "    mlflow.log_metric(\"accuracy\", accuracy)\n",
        "    mlflow.log_metric(\"f1_score\", f1)\n",
        "    mlflow.log_metric(\"roc_auc\", roc_auc)\n",
        "\n",
        "    # 5. Sonuç raporlama\n",
        "    print(f\"  ✓ Accuracy: {accuracy:.4f}\")\n",
        "    print(f\"  ✓ F1-Score: {f1:.4f}\")\n",
        "    print(f\"  ✓ ROC AUC: {roc_auc:.4f}\")\n",
        "    \n",
        "    # 6. Model saklama\n",
        "    mlflow.sklearn.log_model(model, \"random_forest_model\")\n",
        "    print(f\"  ✓ Model MLflow'a kaydedildi\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Adım 7: Deney 3 - Random Forest (Gelişmiş Model)\n",
        "\n",
        "**Parametre Optimizasyonu:**\n",
        "\n",
        "Bu denemede, Random Forest modelinin kapasitesi artırılmıştır:\n",
        "- `n_estimators=100`: Daha büyük topluluk (daha kararlı tahminler)\n",
        "- `max_depth=10`: Daha derin ağaçlar (daha karmaşık kalıpları öğrenme)\n",
        "\n",
        "**Beklenen Davranış:**\n",
        "\n",
        "Parametre artışının olası etkileri:\n",
        "1. **Performans İyileşmesi**: Daha fazla ağaç ve derinlik, modelin veri setindeki karmaşık ilişkileri öğrenmesine olanak tanır\n",
        "2. **Eğitim Süresi**: Hesaplama maliyeti artabilir\n",
        "3. **Overfitting Riski**: Çok yüksek parametreler, test setinde performans düşüşüne yol açabilir\n",
        "\n",
        "**Model Karşılaştırması:**\n",
        "\n",
        "MLflow UI kullanarak bu üç modeli karşılaştırırken dikkat edilecek noktalar:\n",
        "- ROC-AUC skorlarındaki değişim\n",
        "- Parametre-performans ilişkisi\n",
        "- Eğitim süresi vs. doğruluk ödünleşmesi (trade-off)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "with mlflow.start_run(run_name=\"Random Forest (n=100)\"):\n",
        "    print(\"🔄 Çalışan Deney: Random Forest (n=100)\")\n",
        "    \n",
        "    # 1. Gelişmiş hiperparametre konfigürasyonu\n",
        "    n_estimators = 100  # Daha geniş topluluk için ağaç sayısı artırıldı\n",
        "    max_depth = 10      # Daha karmaşık kalıpları öğrenmek için derinlik artırıldı\n",
        "    \n",
        "    mlflow.log_param(\"model_type\", \"RandomForestClassifier\")\n",
        "    mlflow.log_param(\"n_estimators\", n_estimators)\n",
        "    mlflow.log_param(\"max_depth\", max_depth)\n",
        "    mlflow.log_param(\"criterion\", \"gini\")\n",
        "    mlflow.log_param(\"min_samples_split\", 2)  # Varsayılan değer (dokümantasyon için)\n",
        "    \n",
        "    # 2. Model eğitimi\n",
        "    model = RandomForestClassifier(\n",
        "        n_estimators=n_estimators, \n",
        "        max_depth=max_depth, \n",
        "        random_state=42\n",
        "    )\n",
        "    model.fit(X_train, y_train)\n",
        "    \n",
        "    # 3. Tahmin üretimi\n",
        "    y_pred = model.predict(X_test)\n",
        "    y_proba = model.predict_proba(X_test)[:, 1]\n",
        "    \n",
        "    # 4. Kapsamlı metrik değerlendirmesi\n",
        "    accuracy = accuracy_score(y_test, y_pred)\n",
        "    f1 = f1_score(y_test, y_pred)\n",
        "    roc_auc = roc_auc_score(y_test, y_proba)\n",
        "    \n",
        "    mlflow.log_metric(\"accuracy\", accuracy)\n",
        "    mlflow.log_metric(\"f1_score\", f1)\n",
        "    mlflow.log_metric(\"roc_auc\", roc_auc)\n",
        "\n",
        "    # 5. Performans değerlendirmesi\n",
        "    print(f\"  ✓ Accuracy: {accuracy:.4f}\")\n",
        "    print(f\"  ✓ F1-Score: {f1:.4f}\")\n",
        "    print(f\"  ✓ ROC AUC: {roc_auc:.4f}\")\n",
        "    \n",
        "    # 6. Model persistance (kalıcı saklama)\n",
        "    mlflow.sklearn.log_model(model, \"random_forest_model\")\n",
        "    print(f\"  ✓ Model MLflow'a kaydedildi\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## MLflow UI ile Deneyleri İnceleme\n",
        "\n",
        "### Tracking Verilerine Erişim\n",
        "\n",
        "Kod hücreleri çalıştırıldıktan sonra, tüm deney verileri otomatik olarak kaydedilir. Bu veriler iki şekilde erişilebilir:\n",
        "\n",
        "**1. Dosya Sistemi:**\n",
        "- Proje kök dizininde `mlruns/` klasörü oluşur\n",
        "- Her deney için ayrı bir alt dizin\n",
        "- Parametreler, metrikler ve artifactlar JSON formatında saklanır\n",
        "\n",
        "**2. MLflow UI (Önerilen):**\n",
        "\n",
        "MLflow UI, deneyleri görselleştirmek ve karşılaştırmak için web tabanlı bir arayüz sağlar.\n",
        "\n",
        "**UI'ı Başlatma:**\n",
        "```bash\n",
        "mlflow ui --host 127.0.0.1 --port 5000\n",
        "```\n",
        "\n",
        "**Tarayıcıda Açma:**\n",
        "- URL: `http://127.0.0.1:5000`\n",
        "- Sol panelde \"Churn Prediction Experiments\" deneyini seçin\n",
        "\n",
        "### UI Özellikleri\n",
        "\n",
        "**1. Deney Listesi:**\n",
        "- Tüm runs kronolojik olarak listelenir\n",
        "- Her run için tarih, süre ve durum bilgisi görünür\n",
        "\n",
        "**2. Metrik Karşılaştırma:**\n",
        "- İlgili runs'ları seçin\n",
        "- \"Compare\" butonuna tıklayın\n",
        "- Paralel koordinat grafikleri ve tablo görünümü kullanılabilir\n",
        "\n",
        "**3. Model Artifactları:**\n",
        "- Her run'ın sayfasında \"Artifacts\" sekmesi\n",
        "- Kaydedilen modeller indirilebilir veya doğrudan yüklenebilir\n",
        "- Model signature (input/output şeması) otomatik kaydedilir\n",
        "\n",
        "**4. Metrik Görselleştirme:**\n",
        "- Zaman serisi grafikleri (iteratif eğitimlerde)\n",
        "- Parametre vs. metrik scatter plotları\n",
        "- En iyi modeli belirlemek için sıralama seçenekleri"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Pratik Alıştırma: Kendi Denemenizi Tasarlayın\n",
        "\n",
        "Bu bölümde öğrendiklerinizi pekiştirmek için aşağıdaki görevi tamamlayın:\n",
        "\n",
        "### Görev Tanımı\n",
        "\n",
        "Yukarıdaki kod yapısını kullanarak dördüncü bir deney ekleyin:\n",
        "\n",
        "**Önerilen Konfigürasyon:**\n",
        "- Model: `RandomForestClassifier` veya farklı bir algoritma (örn: `GradientBoostingClassifier`, `SVC`)\n",
        "- Hiperparametreler: Kendi seçtiğiniz değerler\n",
        "- Run adı: Modeli ve parametreleri tanımlayan açıklayıcı bir isim\n",
        "\n",
        "### Değerlendirme Kriterleri\n",
        "\n",
        "1. **Teknik Uygulama:**\n",
        "   - Tüm parametreler `mlflow.log_param()` ile kaydedilmiş mi?\n",
        "   - Üç metrik (accuracy, f1_score, roc_auc) hesaplanıp kaydedilmiş mi?\n",
        "   - Model artifact olarak saklanmış mı?\n",
        "\n",
        "2. **Karşılaştırmalı Analiz:**\n",
        "   - MLflow UI'da yeni denemeyi önceki 3 deneyle karşılaştırın\n",
        "   - Hangi model en yüksek ROC-AUC değerini verdi?\n",
        "   - Parametre değişikliklerinin performansa etkisi nasıl?\n",
        "\n",
        "3. **Bilimsel Yorum:**\n",
        "   - Sonuçları bir markdown hücresinde yorumlayın\n",
        "   - Model seçiminizi ve parametre değerlerinizi gerekçelendirin\n",
        "   - Olası iyileştirme önerilerinizi belirtin"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Öğrenci Çalışma Alanı: Buraya kendi denemenizi ekleyin\n",
        "\n",
        "# with mlflow.start_run(run_name=\"Kendi Modeliniz\"):\n",
        "#     # Kodunuzu buraya yazın\n",
        "#     pass"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Hafta 4: Özet ve Anahtar Kavramlar\n",
        "\n",
        "### MLOps Temelleri\n",
        "\n",
        "**MLOps'un Kapsamı:**\n",
        "- Geleneksel DevOps'tan farklı olarak, MLOps üç bileşeni birlikte yönetir: **kod**, **model** ve **veri**\n",
        "- Bu üçlü yapı, makine öğrenmesi sistemlerinin doğası gereği dinamik ve stokastik olmasından kaynaklanır\n",
        "- Versiyon kontrolü, test stratejileri ve deployment süreçleri bu üç bileşeni göz önünde bulundurmalıdır\n",
        "\n",
        "### Deney Yönetiminin Önemi\n",
        "\n",
        "**Tekrarlanabilirlik (Reproducibility):**\n",
        "- Bilimsel araştırma standartlarına uygun çalışma\n",
        "- Aynı koşullarda aynı sonuçları elde edebilme garantisi\n",
        "- Kod, veri ve çevre (environment) versiyonlarının kaydı\n",
        "\n",
        "**Karşılaştırılabilirlik (Comparability):**\n",
        "- Farklı modellerin ve hiperparametrelerin objektif değerlendirilmesi\n",
        "- Zaman içinde performans değişimlerinin izlenmesi\n",
        "- Ekip içinde bilgi paylaşımının kolaylaştırılması\n",
        "\n",
        "### MLflow'un Rolü\n",
        "\n",
        "**Experiment Tracking:**\n",
        "- Otomatik parametre ve metrik kaydı\n",
        "- Artifact yönetimi (modeller, grafikler, veri)\n",
        "- UI üzerinden görselleştirme ve filtreleme\n",
        "\n",
        "**Model Registry (İleri Konular):**\n",
        "- Model versiyonlama\n",
        "- Production'a terfi mekanizması\n",
        "- Model lineage (soy ağacı) takibi\n",
        "\n",
        "### En İyi Uygulamalar\n",
        "\n",
        "1. **İsimlendirme Standartları:**\n",
        "   - Run adları açıklayıcı ve tutarlı olmalı (örn: \"model_parametre1_parametre2\")\n",
        "   - Parametre isimleri kısaltma içermemeli, anlaşılır olmalı\n",
        "\n",
        "2. **Kapsamlı Kayıt:**\n",
        "   - Sadece ana hiperparametreleri değil, tüm konfigürasyonu kaydedin\n",
        "   - Model eğitim süresini metrik olarak loglamayı düşünün\n",
        "   - Veri seti versiyonunu veya hash değerini parametre olarak ekleyin\n",
        "\n",
        "3. **Metrik Seçimi:**\n",
        "   - Problem tipine uygun metrikleri kullanın (sınıflandırma vs. regresyon)\n",
        "   - Dengesiz sınıflarda accuracy yerine F1-score veya ROC-AUC tercih edin\n",
        "   - İş hedeflerine uygun custom metrikler tanımlayın\n",
        "\n",
        "4. **Model Artifactları:**\n",
        "   - Eğitilmiş modeli her zaman kaydedin\n",
        "   - Özellik önem değerlerini veya model açıklama grafiklerini artifact olarak ekleyin\n",
        "   - Preprocessing adımlarını da model ile birlikte saklayın (pipeline kullanımı)\n",
        "\n"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "venv",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.11.2"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 4
}