{"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":"none","dataSources":[{"sourceType":"competition","sourceId":70203,"databundleVersionId":8068726}],"dockerImageVersionId":31328,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torchaudio\nimport torchaudio.transforms as T\nfrom torch.utils.data import Dataset, DataLoader\nfrom sklearn.preprocessing import LabelEncoder\nfrom sklearn.model_selection import train_test_split\nfrom tqdm import tqdm\n\nDATA_DIR = \"/kaggle/input/competitions/birdclef-2024\"\nTRAIN_AUDIO = os.path.join(DATA_DIR, \"train_audio\")\nTEST_AUDIO = os.path.join(DATA_DIR, \"test_soundscapes\")\nMETADATA = os.path.join(DATA_DIR, \"train_metadata.csv\")\n\nDEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint(f\"The Device : {DEVICE}\")\n\nSAMPLE_RATE = 32000       \nDURATION = 5              \nN_MELS = 128              \nN_FFT = 1024\nHOP_LENGTH = 512\n\nTARGET_LENGTH = SAMPLE_RATE * DURATION   \n\ndf = pd.read_csv(METADATA)\nprint(f\"Total Recording: {len(df)}\")\n\nle = LabelEncoder()\ndf['label'] = le.fit_transform(df['primary_label'])\nnum_classes = len(le.classes_)\nprint(f\"The Num Of type Birds: {num_classes}\")\n\ntrain_df, val_df = train_test_split(df, test_size=0.2, stratify=df['label'], random_state=42)\nprint(f\"Training: {len(train_df)}، Validation: {len(val_df)}\")\n\ndef sound_to_image(file_path):\n    waveform, orig_sr = torchaudio.load(file_path)\n    \n    if orig_sr != SAMPLE_RATE:\n        resampler = T.Resample(orig_sr, SAMPLE_RATE)\n        waveform = resampler(waveform)\n    \n    if waveform.shape[0] > 1:\n        waveform = torch.mean(waveform, dim=0, keepdim=True)\n    \n    if waveform.shape[1] < TARGET_LENGTH:\n        pad = TARGET_LENGTH - waveform.shape[1]\n        waveform = F.pad(waveform, (0, pad))\n    else:\n        waveform = waveform[:, :TARGET_LENGTH]\n    \n    mel_spec = T.MelSpectrogram(\n        sample_rate=SAMPLE_RATE,\n        n_fft=N_FFT,\n        hop_length=HOP_LENGTH,\n        n_mels=N_MELS\n    )(waveform)\n    \n    log_mel = torch.log(mel_spec + 1e-6)\n    \n    log_mel = log_mel.squeeze(0)\n    \n    return log_mel   # (128, 313)\n\n\nclass BirdImageDataset(Dataset):\n    def __init__(self, dataframe, audio_dir):\n        self.df = dataframe\n        self.audio_dir = audio_dir\n    \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        file_path = os.path.join(self.audio_dir, row['filename'])\n        try:\n            image = sound_to_image(file_path)   # (128, 313)\n        except Exception as e:\n            print(f\"Loading Error {file_path}: {e}\")\n            image = torch.zeros(N_MELS, 313)\n        \n        label = torch.tensor(row['label'], dtype=torch.long)\n        return image, label\n\n\nbatch_size = 32\ntrain_dataset = BirdImageDataset(train_df, TRAIN_AUDIO)\nval_dataset = BirdImageDataset(val_df, TRAIN_AUDIO)\n\ntrain_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=2)\nval_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False, num_workers=2)\n\nprint(\"Data loaders ready.\")\n\n\nclass SimpleCNN(nn.Module):\n    def __init__(self, num_classes):\n        super().__init__()\n        self.conv1 = nn.Conv2d(1, 32, kernel_size=3, padding=1)\n        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)\n        self.conv3 = nn.Conv2d(64, 128, kernel_size=3, padding=1)\n        self.pool = nn.MaxPool2d(2, 2)   \n        \n        self.adaptive_pool = nn.AdaptiveAvgPool2d((7, 7))   \n        \n        self.fc1 = nn.Linear(128 * 7 * 7, 256)  \n        self.fc2 = nn.Linear(256, num_classes)\n        self.dropout = nn.Dropout(0.3)\n    \n    def forward(self, x):\n        x = self.pool(F.relu(self.conv1(x)))   # (batch,32,64,156) \n        x = self.pool(F.relu(self.conv2(x)))   # (batch,64,32,78)\n        x = self.pool(F.relu(self.conv3(x)))   # (batch,128,16,39)\n        x = self.adaptive_pool(x)              # (batch,128,7,7)\n        x = x.view(x.size(0), -1)              # (batch, 128*7*7=6272)\n        x = F.relu(self.fc1(x))\n        x = self.dropout(x)\n        x = self.fc2(x)\n        return x\n\nmodel = SimpleCNN(num_classes).to(DEVICE)\nprint(\"Model Done\")\n\n\ncriterion = nn.CrossEntropyLoss()\noptimizer = torch.optim.Adam(model.parameters(), lr=0.001)\n\nnum_epochs = 5   \nbest_val_acc = 0.0\n\n\nfor epoch in range(num_epochs):\n    model.train()\n    train_loss = 0.0\n    train_correct = 0\n    train_total = 0\n    \n    loop = tqdm(train_loader, desc=f'Epoch {epoch+1} Training')\n    for images, labels in loop:\n        images = images.unsqueeze(1).to(DEVICE)\n        labels = labels.to(DEVICE)\n        \n        optimizer.zero_grad()\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        \n        train_loss += loss.item()\n        _, predicted = torch.max(outputs, 1)\n        train_total += labels.size(0)\n        train_correct += (predicted == labels).sum().item()\n        \n        loop.set_postfix(loss=train_loss/(train_total//batch_size + 1), acc=100*train_correct/train_total)\n    \n    train_acc = 100 * train_correct / train_total\n    avg_train_loss = train_loss / len(train_loader)\n    \n    model.eval()\n    val_correct = 0\n    val_total = 0\n    with torch.no_grad():\n        for images, labels in tqdm(val_loader, desc='Validation'):\n            images = images.unsqueeze(1).to(DEVICE)\n            labels = labels.to(DEVICE)\n            outputs = model(images)\n            _, predicted = torch.max(outputs, 1)\n            val_total += labels.size(0)\n            val_correct += (predicted == labels).sum().item()\n    \n    val_acc = 100 * val_correct / val_total\n    print(f'Epoch [{epoch+1}/{num_epochs}] Train Loss: {avg_train_loss:.4f}, Train Acc: {train_acc:.2f}%, Val Acc: {val_acc:.2f}%')\n    \n    if val_acc > best_val_acc:\n        best_val_acc = val_acc\n        torch.save(model.state_dict(), 'best_model.pth')\n        print(f'--> New best model saved with val_acc: {val_acc:.2f}%')\n\nprint(f\"Best validation accuracy: {best_val_acc:.2f}%\")\n\nif os.path.exists(TEST_AUDIO):\n    print(\"Predicting on test soundscapes.....\")\n    model.load_state_dict(torch.load('best_model.pth'))\n    model.eval()\n    \n    test_files = [f for f in os.listdir(TEST_AUDIO) if f.endswith('.wav')]\n    results = []\n    for fname in tqdm(test_files):\n        file_path = os.path.join(TEST_AUDIO, fname)\n        try:\n            image = sound_to_image(file_path)   # (128,313)\n            image = image.unsqueeze(0).unsqueeze(0).to(DEVICE)  # (1,1,128,313)\n            with torch.no_grad():\n                output = model(image)\n                pred = torch.argmax(output, dim=1).item()\n                pred_label = le.classes_[pred]\n        except:\n            pred_label = \"unknown\"\n        results.append([fname, pred_label])\n    \n    submission = pd.DataFrame(results, columns=['filename', 'primary_label'])\n    submission.to_csv('submission.csv', index=False)\n    print(\"تم حفظ submission.csv\")\nelse:\n    print(\"No test_soundscapes folder. Training completed.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-17T23:32:01.540687Z","iopub.execute_input":"2026-05-17T23:32:01.54154Z"}},"outputs":[{"name":"stdout","text":"The Device : cpu\nTotal Recording: 24459\nThe Num Of type Birds: 182\nTraining: 19567، Validation: 4892\nData loaders ready.\nModel Done\n","output_type":"stream"},{"name":"stderr","text":"Epoch 1 Training:   2%|▏         | 14/612 [00:45<28:26,  2.85s/it, acc=1.79, loss=4.79] ","output_type":"stream"}],"execution_count":null}]}