{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Hafta 6: Model Dağıtım Stratejileri (API & Konteynerler)\n",
        "\n",
        "**Dersin Hedefleri:**\n",
        "1.  Model dağıtımının (deployment) ne anlama geldiğini ve MLOps yaşam döngüsündeki yerini anlamak.\n",
        "2.  Bir makine öğrenmesi modelini, bir API (Application Programming Interface) olarak sunmanın mantığını kavramak.\n",
        "3.  **FastAPI** kütüphanesini kullanarak yüksek performanslı bir tahmin (prediction) API'si oluşturmak.\n",
        "4.  **Docker** kullanarak modelimizi ve tüm bağımlılıklarını taşınabilir bir konteynere paketlemek.\n",
        "5.  \"Benim makinemde çalışıyordu\" problemini konteynerleştirme ile kalıcı olarak çözmek.\n",
        "\n",
        "## 1. Model Dağıtımı Nedir?\n",
        "\n",
        "Bir makine öğrenmesi modeli, `.pkl`, `.h5` gibi bir dosyada durduğu sürece kimseye değer katmaz. **Model Dağıtımı**, eğitilmiş bir modeli, son kullanıcıların veya diğer yazılım sistemlerinin tahminler almak için erişebileceği bir üretim ortamına yerleştirme sürecidir.\n",
        "\n",
        "En yaygın dağıtım şekli, modeli bir **web servisi** olarak sunmaktır. Bu servis, belirli bir ağ adresine (endpoint) gönderilen verilere karşılık olarak modelin tahminini döndürür. Bu iletişim genellikle bir API aracılığıyla sağlanır.\n",
        "\n",
        "## 2. FastAPI ile Bir Tahmin API'si Oluşturma\n",
        "\n",
        "**FastAPI**, modern, hızlı (yüksek performanslı), web API'leri oluşturmak için kullanılan bir Python kütüphanesidir. `pydantic` (Hafta 2'den hatırlayalım) ve asenkron yetenekleri sayesinde ML modellerini sunmak için mükemmel bir seçimdir.\n",
        "\n",
        "**Kurulum:**\n",
        "```bash\n",
        "pip install \"fastapi[all]\"\n",
        "```\n",
        "Bu komut, FastAPI'yi ve `uvicorn` adında bir ASGI sunucusunu kurar.\n",
        "\n",
        "### Uygulama: Churn Modelimiz için API Yazma\n",
        "\n",
        "Hafta 4'te `MLflow` ile eğittiğimiz ve kaydettiğimiz bir modeli şimdi bir API ile dünyaya açalım.\n",
        "\n",
        "**Proje Yapısı:**\n",
        "```\n",
        ".\n",
        "├── app/\n",
        "│   ├── main.py             <-- FastAPI uygulamamız\n",
        "│   ├── model.pkl           <-- Eğitilmiş modelimiz\n",
        "│   └── requirements.txt\n",
        "├── Dockerfile              <-- Konteyner tarifimiz\n",
        "```"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "requirements_content = \"\"\"fastapi\n",
        "uvicorn\n",
        "scikit-learn\n",
        "joblib\n",
        "pandas\n",
        "pydantic\n",
        "\"\"\"\n",
        "import os\n",
        "requirements_path = os.path.join(app_dir, \"requirements.txt\")\n",
        "with open(requirements_path, \"w\") as f:\n",
        "    f.write(requirements_content)\n",
        "print(f\"✓ requirements.txt oluşturuldu: {requirements_path}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Adım 4: `requirements.txt` Dosyasını Oluşturma\n",
        "\n",
        "API'nin çalışması için gerekli tüm Python paketlerini listeleyen dosyayı oluşturuyoruz."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "main_py_content = \"\"\"\n",
        "import joblib\n",
        "import pandas as pd\n",
        "from fastapi import FastAPI\n",
        "from pydantic import BaseModel\n",
        "\n",
        "# 1. FastAPI uygulamasını başlat\n",
        "app = FastAPI(title=\"Churn Prediction API\", version=\"1.0\")\n",
        "\n",
        "# 2. Model dosyasını yükle\n",
        "# Bu, uygulama başladığında sadece bir kez yapılır.\n",
        "model = joblib.load('model.pkl')\n",
        "\n",
        "# 3. Girdi verisinin şemasını Pydantic ile tanımla\n",
        "# Bu, API'ye gönderilecek JSON verisinin yapısını zorunlu kılar.\n",
        "class CustomerFeatures(BaseModel):\n",
        "    age: int\n",
        "    total_spent: float\n",
        "    membership_days: int\n",
        "    \n",
        "    class Config:\n",
        "        schema_extra = {\n",
        "            \"example\": {\n",
        "                \"age\": 45,\n",
        "                \"total_spent\": 550.75,\n",
        "                \"membership_days\": 850\n",
        "            }\n",
        "        }\n",
        "\n",
        "# 4. API'nin ana endpoint'ini oluştur\n",
        "@app.get(\"/\")\n",
        "def read_root():\n",
        "    return {\"message\": \"Churn Prediction API'sine hoş geldiniz!\"}\n",
        "\n",
        "# 5. Tahmin endpoint'ini oluştur\n",
        "# Bu endpoint, POST isteklerini kabul eder ve /predict adresinde çalışır.\n",
        "@app.post(\"/predict/\")\n",
        "def predict_churn(features: CustomerFeatures):\n",
        "    # Gelen Pydantic objesini modelin beklediği DataFrame'e çevir\n",
        "    input_df = pd.DataFrame([features.dict()])\n",
        "    \n",
        "    # Tahmini yap\n",
        "    prediction_proba = model.predict_proba(input_df)[0][1] # Churn olma olasılığı\n",
        "    prediction = model.predict(input_df)[0]\n",
        "    \n",
        "    return {\n",
        "        \"churn_probability\": float(prediction_proba),\n",
        "        \"prediction\": int(prediction)\n",
        "    }\n",
        "\"\"\"\n",
        "\n",
        "# Dosyaya yaz\n",
        "main_py_path = os.path.join(app_dir, \"main.py\")\n",
        "with open(main_py_path, \"w\") as f:\n",
        "    f.write(main_py_content)\n",
        "print(f\"✓ FastAPI uygulaması oluşturuldu: {main_py_path}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Adım 3: FastAPI Uygulamasını (`main.py`) Oluşturma\n",
        "\n",
        "Bu adımda API uygulamamızın ana kodunu yazacağız. Bu kod:\n",
        "1. FastAPI instance'ı oluşturur\n",
        "2. Model dosyasını yükler\n",
        "3. Girdi verisinin şemasını Pydantic ile tanımlar\n",
        "4. Tahmin endpoint'lerini tanımlar"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "import joblib\n",
        "import pandas as pd\n",
        "from sklearn.linear_model import LogisticRegression\n",
        "\n",
        "# Eğitim verisi oluştur (customer churn prediction için)\n",
        "X_train_dummy = pd.DataFrame({\n",
        "    'age': [25, 45, 35, 50],\n",
        "    'total_spent': [100, 500, 250, 800],\n",
        "    'membership_days': [100, 1000, 400, 1500]\n",
        "})\n",
        "y_train_dummy = pd.Series([0, 1, 0, 1])  # 0: Churn olmadı, 1: Churn oldu\n",
        "\n",
        "# Model eğit\n",
        "model = LogisticRegression()\n",
        "model.fit(X_train_dummy, y_train_dummy)\n",
        "\n",
        "# Modeli dosyaya kaydet\n",
        "model_path = os.path.join(app_dir, \"model.pkl\")\n",
        "joblib.dump(model, model_path)\n",
        "print(f\"✓ Model kaydedildi: {model_path}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Adım 2: Eğitilmiş Model Oluşturma ve Kaydetme\n",
        "\n",
        "Gerçek dünyada bu model MLflow'dan veya başka bir model registry'den indirilir. Bu örnekte basit bir Logistic Regression modeli eğitip kaydediyoruz."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### API'yi Yerel Olarak Çalıştırma\n",
        "\n",
        "Yukarıdaki yapı hazır olduğunda, API'yi çalıştırmak için:\n",
        "1.  Terminali açın ve `hafta6_projesi/app` dizinine gidin.\n",
        "2.  Aşağıdaki komutu çalıştırın:\n",
        "    ```bash\n",
        "    uvicorn main:app --reload\n",
        "    ```\n",
        "    *   `main`: `main.py` dosyasının adı.\n",
        "    *   `app`: Dosya içinde `app = FastAPI()` ile oluşturulan obje.\n",
        "    *   `--reload`: Kodda her değişiklik yaptığınızda sunucuyu otomatik olarak yeniden başlatır.\n",
        "3.  Tarayıcınızda `http://127.0.0.1:8000` adresini açın. \"Hoş geldiniz\" mesajını göreceksiniz.\n",
        "4.  `http://127.0.0.1:8000/docs` adresini açın. FastAPI'nin otomatik olarak oluşturduğu interaktif API dokümantasyonunu (Swagger UI) göreceksiniz. Buradan `/predict` endpoint'ini test edebilir, örnek veriler gönderebilir ve dönen yanıtı görebilirsiniz.\n",
        "\n",
        "## 3. Docker ile Konteynerleştirme\n",
        "\n",
        "API'miz yerel makinemizde çalışıyor. Ancak bunu başka bir sunucuya veya bir bulut platformuna nasıl taşırız? Gerekli tüm kütüphanelerin (fastapi, sklearn vb.) orada da kurulu olduğundan nasıl emin oluruz?\n",
        "\n",
        "**Docker**, bu sorunu çözer. Uygulamamızı, tüm bağımlılıkları ve işletim sistemi seviyesindeki gereksinimleri ile birlikte **konteyner** adı verilen izole bir pakete koymamızı sağlar. Bu konteyner, Docker'ın çalıştığı her yerde **aynı şekilde** çalışır.\n",
        "\n",
        "### `Dockerfile` Yazma\n",
        "\n",
        "`Dockerfile`, Docker'a bizim imajımızı (image) nasıl oluşturacağını söyleyen bir tarif dosyasıdır."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# --- Dockerfile Oluşturma ---\n",
        "dockerfile_content = \"\"\"\n",
        "# Adım 1: Temel imajı belirle. Uygulamamız Python 3.9 gerektiriyor.\n",
        "FROM python:3.9-slim\n",
        "\n",
        "# Adım 2: Çalışma dizinini ayarla. Konteyner içindeki varsayılan klasör.\n",
        "WORKDIR /code\n",
        "\n",
        "# Adım 3: Bağımlılıkları kopyala ve kur.\n",
        "# Önce sadece requirements'ı kopyalamak, Docker katman önbelleğini (layer cache)\n",
        "# verimli kullanır. Kod değişse bile bağımlılıklar değişmediyse bu adım tekrar çalışmaz.\n",
        "COPY ./app/requirements.txt /code/requirements.txt\n",
        "RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt\n",
        "\n",
        "# Adım 4: Uygulama kodunu konteynere kopyala.\n",
        "COPY ./app /code/app\n",
        "\n",
        "# Adım 5: Uygulamanın çalışacağı portu belirt.\n",
        "EXPOSE 8000\n",
        "\n",
        "# Adım 6: Konteyner başladığında çalışacak olan komut.\n",
        "# FastAPI sunucusunu başlatıyoruz. 0.0.0.0 host'u, konteynerin dışarıdan erişilebilir olmasını sağlar.\n",
        "CMD [\"uvicorn\", \"app.main:app\", \"--host\", \"0.0.0.0\", \"--port\", \"8000\"]\n",
        "\"\"\"\n",
        "dockerfile_path = os.path.join(project_root, \"Dockerfile\")\n",
        "with open(dockerfile_path, \"w\") as f:\n",
        "    f.write(dockerfile_content)\n",
        "\n",
        "print(f\"Dockerfile şuraya oluşturuldu: {dockerfile_path}\")\n",
        "print(\"\\n--- Dockerfile İçeriği ---\")\n",
        "print(dockerfile_content)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Docker İmajı Oluşturma ve Konteyneri Çalıştırma\n",
        "\n",
        "Docker'ın makinenizde kurulu olduğunu varsayarsak, aşağıdaki komutlar kullanılır:\n",
        "\n",
        "1.  **İmajı Oluşturma (Build):**\n",
        "    Terminalde `hafta6_projesi` kök dizinindeyken:\n",
        "    ```bash\n",
        "    docker build -t churn-api:1.0 .\n",
        "    ```\n",
        "    *   `-t churn-api:1.0`: İmajımıza bir isim (`churn-api`) ve etiket (`1.0`) verir.\n",
        "    *   `.`: Dockerfile'ın bulunduğu mevcut dizini belirtir.\n",
        "\n",
        "2.  **Konteyneri Çalıştırma (Run):**\n",
        "    İmaj oluşturulduktan sonra, ondan bir konteyner başlatabiliriz:\n",
        "    ```bash\n",
        "    docker run -p 8000:8000 churn-api:1.0\n",
        "    ```\n",
        "    *   `-p 8000:8000`: Yerel makinenizin 8000 portunu, konteynerin 8000 portuna yönlendirir (map).\n",
        "\n",
        "Artık API'niz, makinenizin geri kalanından tamamen izole bir ortamda çalışıyor. Tarayıcınızdan yine `http://127.0.0.1:8000/docs` adresine giderek aynı interaktif dokümantasyonu görebilirsiniz.\n",
        "\n",
        "### Alıştırma: API'ye Yeni Bir Özellik Ekleme\n",
        "\n",
        "1.  `app/main.py` dosyasını açın.\n",
        "2.  `/model-info` adında yeni bir GET endpoint'i ekleyin.\n",
        "3.  Bu endpoint, yüklü olan model hakkında basit bilgiler döndürmelidir. Örneğin, modelin `scikit-learn` sınıfının adını (`model.__class__.__name__`) ve modelin eğitilirken gördüğü özelliklerin listesini (`model.feature_names_in_`) döndüren bir JSON yanıtı oluşturun.\n",
        "4.  Değişikliklerinizi kaydedin.\n",
        "5.  Eğer `uvicorn`'u `--reload` ile çalıştırıyorsanız, değişikliğin anında yansıdığını `/docs` sayfasını yenileyerek görün.\n",
        "6.  Eğer Docker kullanıyorsanız, imajı yeniden build edip konteyneri tekrar çalıştırarak değişikliğin konteynerize edilmiş uygulamaya nasıl yansıtıldığını gözlemleyin."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Adım 1: Gerekli Bilgileri Anlama\n",
        "\n",
        "Model objelerinden alabileceğimiz meta veriler:\n",
        "\n",
        "1. **`model.__class__.__name__`**: Modelin sınıf adı (örn: \"LogisticRegression\")\n",
        "2. **`model.feature_names_in_`**: Model eğitilirken kullanılan özellik isimleri (scikit-learn 1.0+)\n",
        "3. **`model.n_features_in_`**: Özellik sayısı\n",
        "\n",
        "**Not:** Tüm scikit-learn modelleri `feature_names_in_` özelliğine sahip değildir. Model DataFrame ile eğitilmişse bu özellik otomatik olarak eklenir."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Alıştırma Özeti\n",
        "\n",
        "Bu alıştırmada öğrendikleriniz:\n",
        "\n",
        "1. **FastAPI Endpoint Anatomisi:**\n",
        "   - `@app.get(\"/endpoint-path\")` decorator'ü ile GET endpoint tanımlama\n",
        "   - Fonksiyon docstring'lerinin otomatik dokümantasyona eklenmesi\n",
        "   - Python dictionary döndürerek JSON response oluşturma\n",
        "\n",
        "2. **Model Introspection:**\n",
        "   - `__class__.__name__`: Nesnenin sınıf ismini alma\n",
        "   - `hasattr()`: Bir nesnenin belirli bir özelliğe sahip olup olmadığını kontrol etme\n",
        "   - scikit-learn model özelliklerine erişim\n",
        "\n",
        "3. **Development Workflow:**\n",
        "   - Hot-reload ile hızlı iterasyon (`--reload` flag'i)\n",
        "   - Swagger UI (`/docs`) ile interaktif test\n",
        "   \n",
        "4. **Production Workflow:**\n",
        "   - Docker imajını yeniden build etme\n",
        "   - Semantic versioning (1.0 → 1.1)\n",
        "   - İmaj etiketleme best practices"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Adım 4: Docker İmajını Güncelleme\n",
        "\n",
        "Değişiklikleri Docker konteynerine yansıtmak için imajı yeniden build etmeniz gerekir:\n",
        "\n",
        "```bash\n",
        "cd hafta6_projesi\n",
        "docker build -t churn-api:1.1 .\n",
        "docker run -p 8000:8000 churn-api:1.1\n",
        "```\n",
        "\n",
        "**Önemli Gözlem:** Versiyon numarasını `1.0`'dan `1.1`'e çıkardık. Bu, sürüm kontrolünde en iyi pratiktir.\n",
        "\n",
        "**Alternatif:** Aynı etiketi kullanarak yeniden build edebilirsiniz:\n",
        "```bash\n",
        "docker build -t churn-api:1.0 .\n",
        "```\n",
        "Ancak bu, önceki imajın üzerine yazacaktır."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Test kodu (API çalışıyorsa kullanabilirsiniz)\n",
        "# import requests\n",
        "# \n",
        "# response = requests.get(\"http://127.0.0.1:8000/model-info\")\n",
        "# print(\"Status Code:\", response.status_code)\n",
        "# print(\"Response:\")\n",
        "# print(response.json())\n",
        "\n",
        "# Beklenen çıktı:\n",
        "print(\"\"\"\n",
        "Beklenen çıktı:\n",
        "{\n",
        "  \"model_type\": \"LogisticRegression\",\n",
        "  \"n_features\": 3,\n",
        "  \"feature_names\": [\"age\", \"total_spent\", \"membership_days\"]\n",
        "}\n",
        "\"\"\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Adım 3: Endpoint'i Test Etme\n",
        "\n",
        "Endpoint'in çalışıp çalışmadığını kontrol etmek için `requests` kütüphanesini kullanabiliriz.\n",
        "\n",
        "**Not:** API'nin çalışıyor olması gerekir. Yukarıdaki hücreyi çalıştırdıktan sonra, terminalde:\n",
        "```bash\n",
        "cd hafta6_projesi/app\n",
        "uvicorn main:app --reload\n",
        "```\n",
        "komutunu çalıştırın."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Alıştırma çözümü: /model-info endpoint'i ekleme\n",
        "\n",
        "updated_main_py = \"\"\"\n",
        "import joblib\n",
        "import pandas as pd\n",
        "from fastapi import FastAPI\n",
        "from pydantic import BaseModel\n",
        "\n",
        "app = FastAPI(title=\"Churn Prediction API\", version=\"1.0\")\n",
        "model = joblib.load('model.pkl')\n",
        "\n",
        "class CustomerFeatures(BaseModel):\n",
        "    age: int\n",
        "    total_spent: float\n",
        "    membership_days: int\n",
        "    \n",
        "    class Config:\n",
        "        schema_extra = {\n",
        "            \"example\": {\n",
        "                \"age\": 45,\n",
        "                \"total_spent\": 550.75,\n",
        "                \"membership_days\": 850\n",
        "            }\n",
        "        }\n",
        "\n",
        "@app.get(\"/\")\n",
        "def read_root():\n",
        "    return {\"message\": \"Churn Prediction API'sine hoş geldiniz!\"}\n",
        "\n",
        "@app.post(\"/predict/\")\n",
        "def predict_churn(features: CustomerFeatures):\n",
        "    input_df = pd.DataFrame([features.dict()])\n",
        "    prediction_proba = model.predict_proba(input_df)[0][1]\n",
        "    prediction = model.predict(input_df)[0]\n",
        "    \n",
        "    return {\n",
        "        \"churn_probability\": float(prediction_proba),\n",
        "        \"prediction\": int(prediction)\n",
        "    }\n",
        "\n",
        "# YENİ ENDPOINT: Model bilgilerini döndürür\n",
        "@app.get(\"/model-info\")\n",
        "def get_model_info():\n",
        "    '''\n",
        "    Model hakkında meta verileri döndürür.\n",
        "    \n",
        "    Returns:\n",
        "        dict: Model tipi, özellik sayısı ve özellik isimleri\n",
        "    '''\n",
        "    info = {\n",
        "        \"model_type\": model.__class__.__name__,\n",
        "        \"n_features\": model.n_features_in_,\n",
        "    }\n",
        "    \n",
        "    # feature_names_in_ varsa ekle (opsiyonel)\n",
        "    if hasattr(model, 'feature_names_in_'):\n",
        "        info[\"feature_names\"] = model.feature_names_in_.tolist()\n",
        "    else:\n",
        "        info[\"feature_names\"] = None\n",
        "    \n",
        "    return info\n",
        "\"\"\"\n",
        "\n",
        "# Dosyayı güncelle\n",
        "with open(os.path.join(app_dir, \"main.py\"), \"w\") as f:\n",
        "    f.write(updated_main_py)\n",
        "\n",
        "print(\"✓ main.py güncellendi - /model-info endpoint'i eklendi\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Adım 2: Endpoint Kodunu Yazma\n",
        "\n",
        "`app/main.py` dosyasına aşağıdaki endpoint'i ekleyin:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Temizlik\n",
        "shutil.rmtree(project_root)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Haftanın Özeti\n",
        "\n",
        "Bu hafta, makine öğrenmesi modellerimizi birer laboratuvar deneyinden, kullanılabilir yazılım ürünlerine dönüştürmenin ilk ve en önemli adımını attık.\n",
        "\n",
        "- **API'ler**, modellerimizi diğer sistemlerle konuşturan standart bir arayüz sağlar.\n",
        "- **FastAPI**, Python'da yüksek performanslı ve modern API'ler oluşturmak için mükemmel bir araçtır. Otomatik veri doğrulama ve dokümantasyon gibi özellikleri geliştirme sürecini hızlandırır.\n",
        "- **Docker**, uygulamalarımızı ve bağımlılıklarını izole, taşınabilir ve yeniden üretilebilir **konteynerlere** paketlememizi sağlar. Bu, MLOps'ta \"her yerde aynı şekilde çalışır\" garantisi için altın standarttır.\n",
        "\n",
        "### Sonraki Adımlar\n",
        "\n",
        "Artık çalışan ve paketlenmiş bir model servisimiz var. Peki bu servisin performansı zamanla düşerse ne olur? Gelen verinin yapısı değişirse modelimiz hala doğru tahminler yapar mı? Bir sonraki hafta, **Hafta 7: Üretim Ortamında Model İzleme ve Sürekli Teslimat (CD)**'da, bu sorulara cevap arayacağız. Canlıdaki bir modelin \"sağlığını\" nasıl izleyeceğimizi ve MLOps döngüsünü yeniden eğitimle nasıl tamamlayacağımızı öğreneceğiz.\n",
        "\n",
        "---\n",
        "\n",
        "## **Hafta 7: Üretim Ortamında Model İzleme ve Sürekli Teslimat (CD)**\n",
        "```python"
      ]
    }
  ],
  "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
}