{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":97984,"databundleVersionId":14096757,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":2781575,"sourceType":"datasetVersion","datasetId":1279557}],"dockerImageVersionId":31153,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# =============================================================================\n# # BÖLÜM 1: Gerekli Kütüphanelerin Yüklenmesi\n# =============================================================================\n# Bu hücrede, projemiz boyunca ihtiyaç duyacağımız tüm kütüphaneleri yüklüyoruz.\n# - numpy ve pandas: Veri manipülasyonu ve analizi için temel araçlar.\n# - matplotlib: Sonuçları görselleştirmek için kullanılır.\n# - cv2 (OpenCV): Görüntü işleme operasyonları için gereklidir.\n# - tensorflow ve keras: Derin öğrenme modelini oluşturmak, eğitmek ve değerlendirmek için kullanılır.\n# - sklearn: Veri setini eğitim ve doğrulama olarak ayırmak için kullanılır.\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport os\nimport logging\nimport cv2\nimport time\nfrom tqdm.notebook import tqdm\nfrom scipy.signal import resample\nfrom sklearn.model_selection import train_test_split\n\n# TensorFlow ve Keras kütüphanelerinden gerekli modüller\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import Input, Dense, GlobalAveragePooling2D, Dropout\nfrom tensorflow.keras.applications import EfficientNetB0\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint\n\nprint(\"TensorFlow Sürümü:\", tf.__version__)","metadata":{"_uuid":"4fd4fd57-aff3-4f6c-a364-518e236c3a0a","_cell_guid":"38d230a2-6ebc-4608-93e6-230ba6157be6","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-10-22T16:01:15.809009Z","iopub.execute_input":"2025-10-22T16:01:15.809359Z","iopub.status.idle":"2025-10-22T16:01:15.818667Z","shell.execute_reply.started":"2025-10-22T16:01:15.809334Z","shell.execute_reply":"2025-10-22T16:01:15.817509Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =============================================================================\n# # BÖLÜM 2: Yapılandırma ve Loglama Ayarları\n# =============================================================================\n# Bu hücre, projenin temel hiperparametrelerini ve ayarlarını merkezi bir yerde toplar.\n# Bu sayede, farklı denemeler yapmak için sadece bu bölümü değiştirmek yeterli olacaktır.\n\n# Loglama ayarları, kod çalışırken önemli bilgileri ve uyarıları takip etmemizi sağlar.\nlog_format = '%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s'\nlogging.basicConfig(level=logging.INFO, format=log_format)\n\nclass Config:\n    \"\"\"\n    Tüm hiperparametreleri ve yapılandırma ayarlarını içeren sınıf.\n    DEBUG_MODE: True ise, daha küçük bir veri alt kümesi ile hızlı denemeler yapılır.\n    \"\"\"\n    # Genel Ayarlar\n    DEBUG_MODE = True  # Hızlı deneme modu\n    SEED = 42          # Tekrarlanabilir sonuçlar için rastgelelik tohumu\n\n    # Görüntü ve Sinyal Boyutları\n    IMG_HEIGHT = 128\n    IMG_WIDTH = 512\n    SEQ_LENGTH = 512   # Modelin üreteceği zaman serisi uzunluğu\n\n    # Eğitim Hiperparametreleri\n    BATCH_SIZE = 32\n    EPOCHS_HEAD = 10 if not DEBUG_MODE else 4  # Sadece üst katmanların eğitimi için\n    EPOCHS_FULL = 20 if not DEBUG_MODE else 6  # Tüm modelin ince ayarı için\n    LEARNING_RATE_HEAD = 1e-3\n    LEARNING_RATE_FULL = 1e-4 # İnce ayar için daha düşük bir öğrenme oranı\n    DROPOUT_RATE = 0.3 # Ezberlemeyi önlemek için dropout oranı\n\n    # Veri Kümesi Boyutları (DEBUG_MODE için)\n    TRAIN_SAMPLES = 100\n    VAL_SAMPLES = 20\n\n# Yapılandırma sınıfından bir nesne oluşturuluyor\nCONFIG = Config()\nLEAD_NAMES = ['I', 'II', 'III', 'aVR', 'aVL', 'aVF', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6']\n\n# Rastgelelik tohumunu ayarlayarak deneylerin tekrarlanabilirliğini sağlıyoruz\nnp.random.seed(CONFIG.SEED)\ntf.random.set_seed(CONFIG.SEED)\n\nlogging.info(f\"Hızlı mod (DEBUG_MODE) {'AÇIK' if CONFIG.DEBUG_MODE else 'KAPALI'}.\")\n\n# Önceden eğitilmiş model ağırlıklarının yolu\nEFFICIENTNET_WEIGHTS_PATH = '/kaggle/input/keras-applications-models/EfficientNetB0.h5'\nif not os.path.exists(EFFICIENTNET_WEIGHTS_PATH):\n    raise FileNotFoundError(\n        \"EfficientNet ağırlık dosyası bulunamadı! Lütfen sağdaki menüden '+ Add Data' \"\n        \"düğmesine basarak 'Keras Applications Models' veri setini notebook'unuza eklediğinizden emin olun.\"\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T16:01:15.820891Z","iopub.execute_input":"2025-10-22T16:01:15.821297Z","iopub.status.idle":"2025-10-22T16:01:15.897269Z","shell.execute_reply.started":"2025-10-22T16:01:15.821265Z","shell.execute_reply":"2025-10-22T16:01:15.896056Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =============================================================================\n# # BÖLÜM 3: Veri Yükleme ve Bölme\n# =============================================================================\n# Bu bölümde, yarışmanın sağladığı 'train.csv' ve 'test.csv' dosyalarını yüklüyoruz.\n# Eğitim verisini, modelin performansını objektif bir şekilde ölçebilmek için\n# eğitim (training) ve doğrulama (validation) setlerine ayırıyoruz.\n# Ayırma işlemi 'id' bazında yapılarak aynı hastaya ait EKG'lerin farklı setlere dağılması engellenir.\n\nlogging.info(\"Yarışma verileri yükleniyor...\")\ndata_dir = \"/kaggle/input/physionet-ecg-image-digitization\"\n\ntry:\n    # Tüm eğitim verisini içeren CSV dosyasını oku\n    full_train_df = pd.read_csv(os.path.join(data_dir, \"train.csv\"))\n    test_df = pd.read_csv(os.path.join(data_dir, \"test.csv\"))\n    \n    # Benzersiz hasta ID'lerini al\n    unique_ids = full_train_df['id'].unique()\n    \n    # Hasta ID'lerini %85 eğitim, %15 doğrulama olacak şekilde ayır\n    train_ids, val_ids = train_test_split(unique_ids, test_size=0.15, random_state=CONFIG.SEED)\n    \n    # DataFrame'leri bu ID'lere göre filtrele\n    train_df = full_train_df[full_train_df['id'].isin(train_ids)].copy()\n    val_df = full_train_df[full_train_df['id'].isin(val_ids)].copy()\n    \n    # DEBUG_MODE aktif ise, veri setlerinden küçük bir örneklem al\n    if CONFIG.DEBUG_MODE:\n        train_df = train_df.sample(n=CONFIG.TRAIN_SAMPLES, random_state=CONFIG.SEED).reset_index(drop=True)\n        val_df = val_df.sample(n=CONFIG.VAL_SAMPLES, random_state=CONFIG.SEED).reset_index(drop=True)\n\n    logging.info(f\"Eğitim verisi boyutu: {len(train_df)}, Doğrulama verisi boyutu: {len(val_df)}\")\n\nexcept Exception as e:\n    logging.error(f\"Veri yüklenirken/bölünürken hata oluştu: {e}\")\n    # Hata durumunda boş DataFrame'ler oluştur\n    train_df, val_df, test_df = pd.DataFrame(), pd.DataFrame(), pd.DataFrame()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T16:01:15.900465Z","iopub.execute_input":"2025-10-22T16:01:15.900759Z","iopub.status.idle":"2025-10-22T16:01:15.929279Z","shell.execute_reply.started":"2025-10-22T16:01:15.900740Z","shell.execute_reply":"2025-10-22T16:01:15.928017Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =============================================================================\n# # BÖLÜM 4: Veri Hattı (Data Pipeline) Fonksiyonları\n# =============================================================================\n# Bu hücre, EKG görüntülerinden ve sinyal dosyalarından veri okuyup,\n# modele uygun hale getiren fonksiyonları içerir. TensorFlow'un `tf.data` API'si\n# kullanılarak verimli bir veri yükleme hattı oluşturulur.\n\ndef get_lead_images_from_file(image_path):\n    \"\"\"\n    Bir EKG görüntüsünden 12 derivasyonun (lead) her birinin görüntüsünü kırparak çıkarır.\n    \n    Args:\n        image_path (str): EKG görüntüsünün dosya yolu.\n        \n    Returns:\n        list: 12 adet derivasyon görüntüsü içeren bir liste veya hata durumunda None.\n    \"\"\"\n    try:\n        image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)\n        if image is None: \n            logging.warning(f\"Görüntü okunamadı veya dosya bozuk: {image_path}\")\n            return None\n        \n        _, thresh = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)\n        height, width = thresh.shape\n        \n        lead_boxes = [\n            (140, 205, 120, 620), (270, 335, 120, 620), (140, 205, 620, 1120), \n            (205, 270, 620, 1120), (270, 335, 620, 1120), (140, 205, 1120, 1620), \n            (205, 270, 1120, 1620), (270, 335, 1120, 1620), (140, 205, 1620, 2120), \n            (205, 270, 1620, 2120), (270, 335, 1620, 2120)\n        ]\n        rhythm_strip = thresh[400:480, 120:width-120]\n        \n        lead_images = [thresh[y1:y2, x1:x2] for y1, y2, x1, x2 in lead_boxes]\n        lead_images.insert(1, rhythm_strip)\n        return lead_images\n    except Exception as e:\n        logging.error(f\"get_lead_images_from_file içinde beklenmedik hata: {e} - Dosya: {image_path}\")\n        return None\n\ndef process_path(image_path, signal_path=None):\n    \"\"\"\n    Tek bir veri örneğini (görüntü ve sinyal) işler.\n    Gelen 'image_path' ve 'signal_path' girdilerinin türünü kontrol ederek hem\n    TensorFlow tensörleri hem de normal Python string'leri ile çalışabilir.\n    \"\"\"\n    # Girdi türünü kontrol et: tf.Tensor ise Python string'e çevir.\n    if isinstance(image_path, tf.Tensor):\n        img_p_str = image_path.numpy().decode('utf-8')\n    else:\n        img_p_str = image_path\n\n    # Sinyal yolu için de aynı kontrolü yap.\n    if signal_path is not None and isinstance(signal_path, tf.Tensor):\n        sig_p_str = signal_path.numpy().decode('utf-8')\n    else:\n        sig_p_str = signal_path\n\n    lead_images_raw = get_lead_images_from_file(img_p_str)\n    \n    if lead_images_raw is None:\n        return tf.zeros((12, CONFIG.IMG_HEIGHT, CONFIG.IMG_WIDTH, 3)), tf.zeros((12, CONFIG.SEQ_LENGTH))\n    \n    processed_images = []\n    for img in lead_images_raw:\n        if img.shape[0] == 0 or img.shape[1] == 0: \n            img = np.zeros((CONFIG.IMG_HEIGHT, CONFIG.IMG_WIDTH), dtype=np.uint8)\n        img_resized = tf.image.resize(img[..., tf.newaxis], [CONFIG.IMG_HEIGHT, CONFIG.IMG_WIDTH])\n        img_3_channel = tf.image.grayscale_to_rgb(img_resized)\n        processed_images.append(img_3_channel)\n    images_tensor = tf.stack(processed_images)\n    \n    if sig_p_str is not None:\n        true_ts_df = pd.read_csv(sig_p_str)\n        true_ts_df.ffill(inplace=True); true_ts_df.bfill(inplace=True)\n        resampled_signals = [resample(true_ts_df[lead].values.astype(np.float32), CONFIG.SEQ_LENGTH) for lead in LEAD_NAMES]\n        signals_tensor = tf.stack(resampled_signals)\n        return images_tensor, signals_tensor\n    else:\n        signals_tensor = tf.zeros((12, CONFIG.SEQ_LENGTH))\n        return images_tensor, signals_tensor\n\ndef create_dataset(df, is_train=True):\n    \"\"\"\n    Verilen DataFrame'den bir tf.data.Dataset nesnesi oluşturur.\n    \"\"\"\n    image_paths = [f\"{data_dir}/train/{rec_id}/{rec_id}-0001.png\" for rec_id in df['id']]\n    signal_paths = [f\"{data_dir}/train/{rec_id}/{rec_id}.csv\" for rec_id in df['id']]\n    \n    dataset = tf.data.Dataset.from_tensor_slices((image_paths, signal_paths))\n\n    def py_func_wrapper(img_p, sig_p):\n        images, signals = tf.py_function(process_path, [img_p, sig_p], [tf.float32, tf.float32])\n        images.set_shape([12, CONFIG.IMG_HEIGHT, CONFIG.IMG_WIDTH, 3])\n        signals.set_shape([12, CONFIG.SEQ_LENGTH])\n        return images, signals\n\n    dataset = dataset.map(py_func_wrapper, num_parallel_calls=tf.data.AUTOTUNE)\n    \n    if is_train: \n        dataset = dataset.shuffle(buffer_size=1000, seed=CONFIG.SEED)\n        \n    dataset = dataset.unbatch()\n    dataset = dataset.batch(CONFIG.BATCH_SIZE)\n    dataset = dataset.prefetch(buffer_size=tf.data.AUTOTUNE)\n    \n    return dataset","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T16:01:15.931272Z","iopub.execute_input":"2025-10-22T16:01:15.931783Z","iopub.status.idle":"2025-10-22T16:01:15.952961Z","shell.execute_reply.started":"2025-10-22T16:01:15.931756Z","shell.execute_reply":"2025-10-22T16:01:15.951834Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =============================================================================\n# # BÖLÜM 5: TensorFlow Veri Setlerinin Oluşturulması\n# =============================================================================\n# Önceki hücrede tanımlanan fonksiyonları kullanarak eğitim ve doğrulama için\n# tf.data.Dataset nesnelerini oluşturuyoruz. Bu nesneler, veriyi verimli bir\n# şekilde modelin GPU/CPU'suna besleyecektir.\n\ntrain_dataset = create_dataset(train_df, is_train=True)\nval_dataset = create_dataset(val_df, is_train=False)\n\nlogging.info(f\"Eğitim ve doğrulama için tf.data.Dataset'ler oluşturuldu.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T16:01:15.954259Z","iopub.execute_input":"2025-10-22T16:01:15.955100Z","iopub.status.idle":"2025-10-22T16:01:16.100435Z","shell.execute_reply.started":"2025-10-22T16:01:15.955066Z","shell.execute_reply":"2025-10-22T16:01:16.099029Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =============================================================================\n# # BÖLÜM 6: Özel Değerlendirme Metriği (SNR) ve Callback'i\n# =============================================================================\n# Yarışmanın değerlendirme metriği olan Sinyal-Gürültü Oranı'nı (SNR)\n# hesaplamak için gerekli fonksiyonları ve bu metriği her epoch sonunda\n# doğrulama seti üzerinde hesaplayacak olan Keras Callback'ini tanımlıyoruz.\n\ndef align_and_get_powers(true_signal, pred_signal, fs):\n    \"\"\"\n    Tahmin edilen sinyali, gerçek sinyal ile en iyi şekilde hizalar ve\n    sinyal gücü ile hata gücünü hesaplar.\n    Hizalama, çapraz korelasyon (cross-correlation) ile yapılır.\n    \"\"\"\n    try:\n        pred_centered = pred_signal - np.mean(pred_signal)\n        true_centered = true_signal - np.mean(true_signal)\n        \n        max_shift = int(0.2 * fs)\n        corr = np.correlate(pred_centered, true_centered, mode='full')\n        best_shift = np.argmax(corr) - (len(pred_centered) - 1)\n        best_shift = np.clip(best_shift, -max_shift, max_shift)\n        \n        if best_shift > 0:\n            aligned_true = true_centered[:-best_shift]\n            aligned_pred = pred_centered[best_shift:]\n        else:\n            aligned_true = true_centered[-best_shift:]\n            aligned_pred = pred_centered[:len(pred_centered) + best_shift]\n        \n        min_len = min(len(aligned_true), len(aligned_pred))\n        if min_len == 0: return 0.0, 1.0\n        \n        aligned_true, aligned_pred = aligned_true[:min_len], aligned_pred[:min_len]\n        \n        signal_power = np.sum(aligned_true ** 2)\n        error_power = np.sum((aligned_true - aligned_pred) ** 2)\n        \n        return signal_power, max(error_power, 1e-9)\n    except Exception as e:\n        logging.error(f\"align_and_get_powers içinde hata: {e}\")\n        return 0.0, 1.0\n\nclass SNREvaluationCallback(keras.callbacks.Callback):\n    def __init__(self, val_df, full_df):\n        super().__init__()\n        self.val_df = val_df\n        self.full_df = full_df\n        self.best_snr = -np.inf\n\n    def on_epoch_end(self, epoch, logs=None):\n        logs = logs or {}\n        cv_scores = []\n        \n        for rec_id in self.val_df['id'].unique():\n            image_path = f\"{data_dir}/train/{rec_id}/{rec_id}-0001.png\"\n            \n            # Callback, Python'da (eager mode) çalıştığı için tf.py_function'a gerek yoktur.\n            # Python fonksiyonunu doğrudan çağırıyoruz.\n            lead_images_tensor, _ = process_path(image_path, signal_path=None)\n            \n            if tf.shape(lead_images_tensor)[0] != 12:\n                logging.warning(f\"SNR Hesaplanırken ID {rec_id} için 12 derivasyon bulunamadı, atlanıyor.\")\n                continue\n            \n            predicted_sequences = self.model.predict(lead_images_tensor, verbose=0)\n            \n            try:\n                true_ts_df = pd.read_csv(f\"{data_dir}/train/{rec_id}/{rec_id}.csv\")\n                fs = self.full_df[self.full_df['id'] == rec_id].iloc[0]['fs']\n            except FileNotFoundError:\n                logging.warning(f\"SNR Hesaplanırken ID {rec_id} için CSV dosyası bulunamadı, atlanıyor.\")\n                continue\n            \n            total_signal_power, total_error_power = 0.0, 0.0\n            \n            for i, lead_name in enumerate(LEAD_NAMES):\n                true_signal = true_ts_df[lead_name].values\n                if np.isnan(true_signal).any(): continue\n                \n                pred_resampled = resample(predicted_sequences[i], len(true_signal))\n                signal_power, error_power = align_and_get_powers(true_signal, pred_resampled, fs)\n                total_signal_power += signal_power\n                total_error_power += error_power\n            \n            if total_error_power > 0:\n                cv_scores.append(10 * np.log10(total_signal_power / total_error_power))\n        \n        avg_snr = np.mean(cv_scores) if cv_scores else -np.inf\n        logs['val_snr'] = avg_snr\n        \n        if avg_snr > self.best_snr:\n            self.best_snr = avg_snr\n            print(f\" - val_snr: {avg_snr:.4f} (Yeni en iyi skor!)\")\n        else:\n            print(f\" - val_snr: {avg_snr:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T16:04:24.616898Z","iopub.execute_input":"2025-10-22T16:04:24.617315Z","iopub.status.idle":"2025-10-22T16:04:24.633257Z","shell.execute_reply.started":"2025-10-22T16:04:24.617290Z","shell.execute_reply":"2025-10-22T16:04:24.632204Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =============================================================================\n# # BÖLÜM 7: Model Oluşturma Fonksiyonu\n# =============================================================================\n# Bu hücre, derin öğrenme modelimizi oluşturan fonksiyonu içerir.\n# - EfficientNetB0: Önceden ImageNet üzerinde eğitilmiş, güçlü bir evrişimli sinir ağı (CNN) modelidir.\n#   Bu modele \"temel model\" (base model) diyoruz.\n# - GlobalAveragePooling2D: Temel modelden gelen özellik haritalarını tek bir vektöre indirger.\n# - Dense ve Dropout: Regresyon (zaman serisi tahmini) yapmak için eklediğimiz üst katmanlardır.\n#   Dropout, ezberlemeyi önlemeye yardımcı olur.\n\ndef build_ecg_model(input_shape, seq_length, dropout_rate):\n    \"\"\"\n    EfficientNetB0 tabanlı transfer öğrenme modelini oluşturur.\n    \"\"\"\n    # Temel model: EfficientNetB0 (ImageNet ağırlıklarıyla)\n    # include_top=False, çünkü kendi sınıflandırma/regresyon katmanlarımızı ekleyeceğiz.\n    base_model = EfficientNetB0(\n        include_top=False, \n        weights=EFFICIENTNET_WEIGHTS_PATH, \n        input_shape=input_shape\n    )\n    # Başlangıçta temel modelin ağırlıklarını donduruyoruz.\n    base_model.trainable = False\n    \n    # Modelin Mimarisi\n    inputs = Input(shape=input_shape)\n    x = base_model(inputs, training=False) # training=False -> dondurulmuş katmanlar için önemli\n    x = GlobalAveragePooling2D()(x)\n    x = Dropout(dropout_rate)(x)\n    x = Dense(1024, activation='relu')(x)\n    x = Dropout(dropout_rate)(x)\n    x = Dense(512, activation='relu')(x)\n    # Çıktı katmanı: Tahmin edilecek zaman serisi uzunluğunda, lineer aktivasyonlu.\n    outputs = Dense(seq_length, activation='linear')(x)\n    \n    model = Model(inputs, outputs)\n    \n    return model, base_model\n\n# Modeli ve temel modeli oluştur\necg_digitizer_model, base_model = build_ecg_model(\n    input_shape=(CONFIG.IMG_HEIGHT, CONFIG.IMG_WIDTH, 3),\n    seq_length=CONFIG.SEQ_LENGTH,\n    dropout_rate=CONFIG.DROPOUT_RATE\n)\n\n# Modeli derle\necg_digitizer_model.compile(\n    optimizer=keras.optimizers.Adam(learning_rate=CONFIG.LEARNING_RATE_HEAD),\n    loss='mae' # Ortalama Mutlak Hata (Mean Absolute Error)\n)\n\n# Modelin özetini göster\necg_digitizer_model.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T16:01:16.120886Z","iopub.execute_input":"2025-10-22T16:01:16.121250Z","iopub.status.idle":"2025-10-22T16:01:18.568736Z","shell.execute_reply.started":"2025-10-22T16:01:16.121219Z","shell.execute_reply":"2025-10-22T16:01:18.567816Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =============================================================================\n# # BÖLÜM 8: Modelin Eğitilmesi (İki Aşamalı)\n# =============================================================================\n# Modeli iki aşamada eğiteceğiz:\n# 1. Feature Extraction (Özellik Çıkarımı): Sadece modelin tepesine eklediğimiz\n#    yeni katmanları (Dense, Dropout) eğitiriz. Bu sırada EfficientNetB0'ın\n#    ağırlıkları dondurulmuş kalır. Bu, yeni katmanların temel özellikleri\n#    kullanmayı öğrenmesini sağlar.\n# 2. Fine-Tuning (İnce Ayar): İlk aşama tamamlandıktan sonra, EfficientNetB0'ın\n#    son birkaç katmanını \"çözer\" ve çok daha düşük bir öğrenme oranı ile\n#    tüm modeli eğitmeye devam ederiz. Bu, modelin probleme daha özel\n#    özellikler öğrenmesini sağlar.\n\n# --- Gelişmiş Callback'lerin Tanımlanması ---\n\n# 1. EarlyStopping: val_loss 5 epok boyunca iyileşmezse eğitimi durdur.\n#    restore_best_weights=True sayesinde en iyi ağırlıklara geri dönülür.\nearly_stopping = EarlyStopping(\n    monitor='val_loss',\n    patience=5,\n    verbose=1,\n    mode='min',\n    restore_best_weights=True\n)\n\n# 2. ReduceLROnPlateau: val_loss 3 epok boyunca iyileşmezse öğrenme oranını 0.2 ile çarp.\nreduce_lr = ReduceLROnPlateau(\n    monitor='val_loss',\n    factor=0.2,\n    patience=3,\n    verbose=1,\n    mode='min',\n    min_lr=1e-7\n)\n\n# 3. ModelCheckpoint: En iyi val_snr skorunu elde eden model ağırlıklarını kaydet.\nmodel_checkpoint = ModelCheckpoint(\n    'best_model.keras',\n    monitor='val_snr',\n    save_best_only=True,\n    mode='max',\n    verbose=1\n)\n\n# 4. SNR Değerlendirme Callback'i\nsnr_callback = SNREvaluationCallback(val_df, full_train_df)\n\n\n# --- AŞAMA 1: Sadece Üst Katmanların Eğitimi ---\nlogging.info(\"Aşama 1: Sadece üst katmanların (head) eğitimi başlıyor...\")\nstart_time = time.time()\n\nhistory_head = ecg_digitizer_model.fit(\n    train_dataset,\n    epochs=CONFIG.EPOCHS_HEAD,\n    validation_data=val_dataset,\n    callbacks=[\n        snr_callback,\n        early_stopping,\n        reduce_lr,\n        model_checkpoint\n    ],\n    verbose=1\n)\n\nlogging.info(f\"Aşama 1, {time.time() - start_time:.2f} saniyede tamamlandı.\")\n\n# --- AŞAMA 2: İnce Ayar (Fine-Tuning) ---\nlogging.info(\"Aşama 2: İnce ayar (fine-tuning) başlıyor...\")\n\n# Temel modelin son 20 katmanını çöz (eğitilebilir yap)\nbase_model.trainable = True\nfine_tune_at = -20\nfor layer in base_model.layers[:fine_tune_at]:\n    layer.trainable = False\n\n# Modeli çok daha düşük bir öğrenme oranı ile yeniden derle\necg_digitizer_model.compile(\n    optimizer=keras.optimizers.Adam(learning_rate=CONFIG.LEARNING_RATE_FULL),\n    loss='mae'\n)\n\nlogging.info(f\"Temel modelin son {-fine_tune_at} katmanı eğitim için çözüldü.\")\n\nstart_time_full = time.time()\n# Eğitime devam et\nhistory_full = ecg_digitizer_model.fit(\n    train_dataset,\n    epochs=CONFIG.EPOCHS_HEAD + CONFIG.EPOCHS_FULL, # Toplam epoch sayısını artır\n    initial_epoch=history_head.epoch[-1] + 1,      # Kaldığı yerden devam et\n    validation_data=val_dataset,\n    callbacks=[\n        snr_callback,\n        early_stopping,\n        reduce_lr,\n        model_checkpoint\n    ],\n    verbose=1\n)\n\nlogging.info(f\"Aşama 2, {time.time() - start_time_full:.2f} saniyede tamamlandı.\")\n\n# En iyi modeli geri yükle\nlogging.info(\"Eğitim boyunca elde edilen en iyi model ağırlıkları yükleniyor...\")\necg_digitizer_model.load_weights('best_model.keras')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T16:04:30.960973Z","iopub.execute_input":"2025-10-22T16:04:30.961292Z","iopub.status.idle":"2025-10-22T16:20:38.499479Z","shell.execute_reply.started":"2025-10-22T16:04:30.961273Z","shell.execute_reply":"2025-10-22T16:20:38.498544Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =============================================================================\n# # BÖLÜM 9: Sonuçların Değerlendirilmesi ve Görselleştirilmesi\n# =============================================================================\n# Bu hücrede, modelin eğitim sürecindeki performansını değerlendiriyoruz.\n# Eğitim ve doğrulama kayıp (loss) değerlerini bir grafikte göstererek\n# modelin öğrenme sürecini ve olası ezberleme (overfitting) durumunu inceliyoruz.\n\nlogging.info(\"Eğitim ve doğrulama sonuçları özetleniyor...\")\n\n# İki eğitim aşamasının geçmişini birleştir\nhistory = {}\nfor key in history_head.history.keys():\n    history[key] = history_head.history[key] + history_full.history.get(key, [])\n\n# Son epoch'taki değerleri al\nfinal_train_loss = history['loss'][-1]\nfinal_val_loss = history['val_loss'][-1]\nfinal_val_snr = history.get('val_snr', [None])[-1]\n\n# Eğitim ve Doğrulama Kayıp Grafiği\nplt.figure(figsize=(16, 7))\nplt.plot(history['loss'], 'o-', label=f\"Eğitim Kaybı (Son: {final_train_loss:.4f})\")\nplt.plot(history['val_loss'], 'o-', label=f\"Doğrulama Kaybı (Son: {final_val_loss:.4f})\")\nplt.title(\"Epoch'lara Göre Model Kaybı (MAE)\", fontsize=16)\nplt.xlabel('Epoch', fontsize=12)\nplt.ylabel('Ortalama Mutlak Hata (Loss)', fontsize=12)\nplt.xticks(np.arange(len(history['loss'])))\nplt.legend(fontsize=12)\nplt.grid(True, linestyle='--')\nplt.show()\n\n# Performans değerlendirmesini detaylı bir şekilde yazdır\nprint(\"\\n\" + \"=\"*55)\nprint(\" \" * 15 + \"PERFORMANS DEĞERLENDİRMESİ\")\nprint(\"=\"*55)\nprint(\"\\n--- Modelin Eğitim Sonu Kayıp Değerleri ---\")\nprint(f\"Son Eğitim Kaybı (Train Loss):      {final_train_loss:.4f}\")\nprint(f\"Son Doğrulama Kaybı (Val Loss):    {final_val_loss:.4f}\")\nprint(\"\\n\" + \"-\"*55)\nprint(\"\\n--- Yarışma Metriği Skoru ---\")\n\nif final_val_snr is not None and not np.isinf(final_val_snr):\n    print(f\"LOKAL CV SKORU (Son Epoch Ort. SNR):   {final_val_snr:.4f} dB\")\n    print(f\"En İyi Val-SNR (Eğitim Boyunca):      {snr_callback.best_snr:.4f} dB\")\n    if snr_callback.best_snr > 0.1:\n        print(\"\\nSonuç: Başarılı! Lokal skor, hedef olan 0.1'i aştı.\")\n    else:\n        print(\"\\nSonuç: Hedefe ulaşmak için daha fazla eğitim veya ayarlama gerekebilir.\")\nelse:\n    print(\"\\nUYARI: Doğrulama seti üzerinde geçerli bir SNR skoru hesaplanamadı.\")\nprint(\"=\"*55 + \"\\n\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T16:22:00.218080Z","iopub.execute_input":"2025-10-22T16:22:00.218461Z","iopub.status.idle":"2025-10-22T16:22:00.672779Z","shell.execute_reply.started":"2025-10-22T16:22:00.218428Z","shell.execute_reply":"2025-10-22T16:22:00.671805Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =============================================================================\n# # BÖLÜM 10: Test Verisi Üzerinde Tahmin ve Sunum Dosyası Oluşturma\n# =============================================================================\n# Bu son bölümde, eğittiğimiz en iyi modeli kullanarak test seti üzerindeki\n# EKG görüntüleri için zaman serisi tahminleri yapıyoruz.\n# Tahminleri, yarışmanın istediği formatta bir 'submission.csv' dosyasına kaydediyoruz.\n\nlogging.info(\"Test seti üzerinde tahmin süreci başlıyor.\")\npredictions = []\n\nfor base_id, group in tqdm(test_df.groupby('id'), desc=\"Test Görüntüleri İşleniyor\"):\n    image_path = f\"{data_dir}/test/{base_id}.png\"\n    \n    # Bu döngü de Python'da çalıştığı için tf.py_function'a gerek yoktur.\n    # Fonksiyonu doğrudan çağırıyoruz.\n    lead_images_tensor, _ = process_path(image_path, signal_path=None)\n    \n    if tf.shape(lead_images_tensor)[0] != 12:\n        for _, row in group.iterrows():\n            for i in range(row['number_of_rows']):\n                predictions.append({'id': f\"{base_id}_{i}_{row['lead']}\", 'value': 0.0})\n        continue\n\n    predicted_sequences = ecg_digitizer_model.predict(lead_images_tensor, verbose=0)\n\n    for _, row in group.iterrows():\n        lead_name, num_rows_expected = row['lead'], row['number_of_rows']\n        lead_index = LEAD_NAMES.index(lead_name)\n        \n        final_signal = resample(predicted_sequences[lead_index], num_rows_expected)\n        \n        for i, value in enumerate(final_signal):\n            predictions.append({'id': f\"{base_id}_{i}_{row['lead']}\", 'value': float(value)})\n\nlogging.info(\"Tüm test tahminleri tamamlandı.\")\n\nsubmission_df = pd.DataFrame(predictions)\nsubmission_df.to_csv('submission.csv', index=False)\n\nlogging.info(\"submission.csv dosyası başarıyla oluşturuldu!\")\nprint(\"\\nÖrnek Sunum Dosyası:\")\ndisplay(submission_df.head(15))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T16:22:17.718286Z","iopub.execute_input":"2025-10-22T16:22:17.718645Z","iopub.status.idle":"2025-10-22T16:22:19.624519Z","shell.execute_reply.started":"2025-10-22T16:22:17.718619Z","shell.execute_reply":"2025-10-22T16:22:19.623564Z"}},"outputs":[],"execution_count":null}]}