{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":70203,"databundleVersionId":8068726}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install librosa -q#قراءه وتحليل الصوت","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-17T21:16:20.699257Z","iopub.execute_input":"2026-05-17T21:16:20.699584Z","iopub.status.idle":"2026-05-17T21:16:24.072388Z","shell.execute_reply.started":"2026-05-17T21:16:20.699559Z","shell.execute_reply":"2026-05-17T21:16:24.071331Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport librosa#بتقرا ملفات .ogg وتحولها لارقام\nimport librosa.display\nimport os#التعامل مع الملفات والمجلدات\nimport warnings\nwarnings.filterwarnings('ignore')\n\nfrom pathlib import Path\nfrom sklearn.model_selection import train_test_split#بقسم البيانات ل test,train\nfrom sklearn.preprocessing import LabelEncoder#بحول اسماء الطيور لارقام \nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nfrom tensorflow.keras.layers import RandomFlip, RandomBrightness\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-17T21:16:24.073828Z","iopub.execute_input":"2026-05-17T21:16:24.074124Z","iopub.status.idle":"2026-05-17T21:16:33.871484Z","shell.execute_reply.started":"2026-05-17T21:16:24.074094Z","shell.execute_reply":"2026-05-17T21:16:33.870164Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"BASE_DIR = Path('/kaggle/input/competitions/birdclef-2024')#المسار الاساسي للداتا علي kaggle\n\ndf = pd.read_csv(BASE_DIR / 'train_metadata.csv')# open train_metadata.csv\n\nprint(\"Data shape:\", df.shape)# (# of rows,# of cols)\nprint(\"\\nFirst 5 rows:\")\nprint(df.head())# display the first 5 rows\nprint(\"\\nColumn names:\")\nprint(df.columns.tolist())# display name of all cols\nprint(\"\\nNumber of bird species:\", df['primary_label'].nunique())# display # of bird species","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-17T21:16:33.872882Z","iopub.execute_input":"2026-05-17T21:16:33.874572Z","iopub.status.idle":"2026-05-17T21:16:34.002699Z","shell.execute_reply.started":"2026-05-17T21:16:33.874514Z","shell.execute_reply":"2026-05-17T21:16:34.001978Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"NUM_CLASSES = 10\n\ntop_birds = df['primary_label'].value_counts().head(NUM_CLASSES).index.tolist()\n\nprint(\"Selected bird species:\")\nfor i, bird in enumerate(top_birds):\n    count = len(df[df['primary_label'] == bird])\n    common = df[df['primary_label'] == bird]['common_name'].iloc[0]\n    print(f\"  {i}: {bird:10} | {common:30} | {count} files\")\n\ndf_filtered = df[df['primary_label'].isin(top_birds)].copy()\nprint(f\"\\nTotal files after filtering: {len(df_filtered)}\")#500*10=5000 file","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-17T21:16:34.004619Z","iopub.execute_input":"2026-05-17T21:16:34.005225Z","iopub.status.idle":"2026-05-17T21:16:34.060355Z","shell.execute_reply.started":"2026-05-17T21:16:34.005197Z","shell.execute_reply":"2026-05-17T21:16:34.059730Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def audio_to_melspectrogram(filepath, duration=5, sr=22050):\n    try:\n        audio, sample_rate = librosa.load(filepath, sr=sr, duration=duration)\n\n        target_length = sr * duration\n        if len(audio) < target_length:\n            audio = np.pad(audio, (0, target_length - len(audio)))\n\n        mel_spec = librosa.feature.melspectrogram(\n            y=audio, sr=sr, n_mels=64, fmax=8000\n        )\n        mel_spec_db = librosa.power_to_db(mel_spec, ref=np.max)\n        return mel_spec_db\n\n    except Exception as e:\n        return None","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-17T21:16:34.061202Z","iopub.execute_input":"2026-05-17T21:16:34.061468Z","iopub.status.idle":"2026-05-17T21:16:34.068702Z","shell.execute_reply.started":"2026-05-17T21:16:34.061446Z","shell.execute_reply":"2026-05-17T21:16:34.068041Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Loading audio files...\")\n\nX = []\ny = []\nMAX_FILES_PER_CLASS = 400\n\nfor bird_idx, bird in enumerate(top_birds):\n    bird_files = df_filtered[\n        df_filtered['primary_label'] == bird\n    ]['filename'].tolist()[:MAX_FILES_PER_CLASS]\n\n    success = 0\n    for filename in bird_files:\n        filepath = BASE_DIR / 'train_audio' / filename\n        mel = audio_to_melspectrogram(str(filepath))\n        if mel is not None:\n            X.append(mel)\n            y.append(bird)\n            success += 1\n\n    print(f\"  [{bird_idx+1}/10] {bird}: {success} files loaded\")\n\nX = np.array(X)\n# Data Augmentation\ndata_augmentation = keras.Sequential([\n    RandomFlip(\"horizontal\"),\n    RandomBrightness(0.2),\n])\nX = X[..., np.newaxis]\nX = (X - X.min()) / (X.max() - X.min())\n\nprint(f\"\\nX shape: {X.shape}\")\nprint(f\"Total files loaded: {len(y)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-17T21:16:34.069529Z","iopub.execute_input":"2026-05-17T21:16:34.069806Z","iopub.status.idle":"2026-05-17T21:18:02.720363Z","shell.execute_reply.started":"2026-05-17T21:16:34.069785Z","shell.execute_reply":"2026-05-17T21:18:02.719471Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"le = LabelEncoder()\ny_encoded = le.fit_transform(y)\n\nprint(\"Label encoding:\")\nfor i, bird in enumerate(le.classes_):\n    common = df[df['primary_label'] == bird]['common_name'].iloc[0]\n    print(f\"  {i} -> {bird:10} | {common}\")\n\nx_train, x_test, y_train, y_test = train_test_split(\n    X, y_encoded,\n    test_size=0.2,\n    random_state=42,\n    stratify=y_encoded#بتضمن ان كل نوع موجود بنفس النسبه فال test,train\n)\n\nprint(f\"\\nx_train shape: {x_train.shape}\")#4000 file * 80%=3200\nprint(f\"x_test shape:  {x_test.shape}\")#4000-3200=800","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-17T21:18:02.721547Z","iopub.execute_input":"2026-05-17T21:18:02.721848Z","iopub.status.idle":"2026-05-17T21:18:02.817118Z","shell.execute_reply.started":"2026-05-17T21:18:02.721825Z","shell.execute_reply":"2026-05-17T21:18:02.816375Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = keras.Sequential([\n    layers.Conv2D(32, (3,3), activation='relu',\n                  padding='same', input_shape=x_train.shape[1:]),\n    # 3*3 kernel size*1(bias)=10, 10*32 filter=320 param\n    layers.BatchNormalization(),\n    #لكل قناه من ال 32 قناه 4 متغيرات (gamma,beta,moving_mean,moving_variance)-->32*4=128 params\n    layers.MaxPooling2D((2,2)),# input: 32,216,64 , output:32,108,32,0 params\n    layers.Dropout(0.3),# input=output=23,108,32,params=0,\n    # total cells:32*108*32=110592 , stopped=30% * 110592=33178 cell, remaining=110592-33178=77414 cell\n\n    layers.Conv2D(64, (3,3), activation='relu', padding='same'),#input:32,108,32 output:64,108,32\n    #3*3 * 32 (ناتج قنوات conv2D)+1=289,289*64 filter=18496 param\n    layers.BatchNormalization(),\n    layers.MaxPooling2D((2,2)),#input:64,108,32 , output:64,54,16\n    layers.Dropout(0.3),\n\n    layers.Conv2D(128, (3,3), activation='relu', padding='same'),# input:64,54,16 , output:128,54,16\n    #3*3*64+1=577,577*128=73856 param\n    layers.BatchNormalization(),\n    layers.MaxPooling2D((2,2)),#input:128,54,16 , output: 128,27,8\n    layers.Dropout(0.3),\n\n    layers.Flatten(),#8 × 27 × 128 = 27,648\n    layers.Dense(128, activation='relu'),#27,648 × 128 + 128 (bias)= 3,539,072\n    layers.Dropout(0.5),\n    layers.Dense(NUM_CLASSES, activation='softmax')#128 × 10 + 10 (bias) = 1,290 ,softmax->10 prob=100%\n])\n\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-17T21:18:02.818206Z","iopub.execute_input":"2026-05-17T21:18:02.818662Z","iopub.status.idle":"2026-05-17T21:18:03.643544Z","shell.execute_reply.started":"2026-05-17T21:18:02.818634Z","shell.execute_reply":"2026-05-17T21:18:03.642855Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.compile(\n    optimizer=keras.optimizers.Adam(learning_rate=0.0001),#كل تحديث للأوزان = الوزن القديم - (0.0001 × الخطأ)\n    loss='sparse_categorical_crossentropy',\n    metrics=['accuracy']\n)\n\nearly_stopping = keras.callbacks.EarlyStopping(\n    monitor='val_loss',\n    patience=10,\n    restore_best_weights=True\n)\n\nreduce_lr = keras.callbacks.ReduceLROnPlateau(\n    monitor='val_loss',\n    factor=0.5,\n    patience=5,\n    min_lr=0.000001,\n    verbose=1\n)\n\nhistory = model.fit(\n     # data_augmentation(x_train),\n    x_train, y_train,\n    epochs=100,#عدد الـ Batches في كل Epoch :2560 ÷ 32 = 80 batch\n    batch_size=32,\n    #Training after validation_split:3200 × 80% = 2560 file for training , 3200-2560=640 for validation \n    validation_split=0.2,\n    callbacks=[early_stopping, reduce_lr],\n    verbose=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-17T21:18:03.644498Z","iopub.execute_input":"2026-05-17T21:18:03.644773Z","iopub.status.idle":"2026-05-17T21:19:35.821894Z","shell.execute_reply.started":"2026-05-17T21:18:03.644751Z","shell.execute_reply":"2026-05-17T21:19:35.821180Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"loss, accuracy = model.evaluate(x_test, y_test, verbose=0)#x_test:800 files\nprint(f\"Test Loss:     {loss:.4f}\")\n#Test Loss:1.4538 , Test Accuracy: 65.75% , correct:65,75% * 800=526 file, wrong:800-526=274 \nprint(f\"Test Accuracy: {accuracy*100:.2f}%\")\n\nfig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))\n\nax1.plot(history.history['accuracy'],     label='Train', color='blue')\nax1.plot(history.history['val_accuracy'], label='Val',   color='orange')\nax1.set_title('Model Accuracy')\nax1.set_xlabel('Epoch')\nax1.set_ylabel('Accuracy')\nax1.legend()\n\nax2.plot(history.history['loss'],     label='Train', color='blue')\nax2.plot(history.history['val_loss'], label='Val',   color='orange')\nax2.set_title('Model Loss')\nax2.set_xlabel('Epoch')\nax2.set_ylabel('Loss')\nax2.legend()\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-17T21:19:35.823146Z","iopub.execute_input":"2026-05-17T21:19:35.823542Z","iopub.status.idle":"2026-05-17T21:19:36.462904Z","shell.execute_reply.started":"2026-05-17T21:19:35.823515Z","shell.execute_reply":"2026-05-17T21:19:36.461994Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict_bird(filepath):\n    mel = audio_to_melspectrogram(filepath)\n    if mel is None:\n        print(\"Error loading file!\")\n        return\n\n    mel = (mel - mel.min()) / (mel.max() - mel.min())#normalization\n    mel = mel[np.newaxis, ..., np.newaxis]\n    #add 2 axis (3 of images,# of channel)  (Batch, Height, Width, Channels)\n\n    predictions = model.predict(mel, verbose=0)[0]\n    top3_idx = predictions.argsort()[-3:][::-1]\n\n    print(f\"File: {filepath}\")\n    print(\"\\nTop 3 predictions:\")\n    for i, idx in enumerate(top3_idx):\n        bird_code = le.classes_[idx]\n        common = df[df['primary_label'] == bird_code]['common_name'].iloc[0]\n        print(f\"  {i+1}. {common:30} ({bird_code}) - {predictions[idx]*100:.1f}%\")\n\ntest_file = str(BASE_DIR / 'train_audio' / 'barswa' / 'XC142466.ogg')\npredict_bird(test_file)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-17T21:19:36.464123Z","iopub.execute_input":"2026-05-17T21:19:36.464463Z","iopub.status.idle":"2026-05-17T21:19:37.236932Z","shell.execute_reply.started":"2026-05-17T21:19:36.464439Z","shell.execute_reply":"2026-05-17T21:19:37.236214Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nbarswa_dir = BASE_DIR / 'train_audio' / 'barswa'#path for Barn Swallow folder\nfiles = list(os.listdir(barswa_dir))[:5] # take top 5 only \nfor f in files:# loop & prediction\n    predict_bird(str(barswa_dir / f))\n    # 3 of 5 correct :60%","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-17T21:19:37.239087Z","iopub.execute_input":"2026-05-17T21:19:37.239322Z","iopub.status.idle":"2026-05-17T21:19:38.034385Z","shell.execute_reply.started":"2026-05-17T21:19:37.239299Z","shell.execute_reply":"2026-05-17T21:19:38.033381Z"}},"outputs":[],"execution_count":null}]}