{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":91844,"databundleVersionId":11361821,"sourceType":"competition"},{"sourceId":189366517,"sourceType":"kernelVersion"}],"dockerImageVersionId":31041,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"**#Import stuff**","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport librosa\nimport glob\nimport pandas.api.types\nimport torch \nimport torch.nn as nn\nimport os\nimport random\nfrom matplotlib import pyplot as plt\nimport seaborn as sns\nimport timm\nimport kaggle_metric_utilities\nimport sklearn.metrics\n\nfrom warnings import filterwarnings\nfilterwarnings(\"ignore\")\nfrom ast import literal_eval ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-08T07:05:45.436477Z","iopub.execute_input":"2025-06-08T07:05:45.437023Z","iopub.status.idle":"2025-06-08T07:06:00.133623Z","shell.execute_reply.started":"2025-06-08T07:05:45.437000Z","shell.execute_reply":"2025-06-08T07:06:00.132867Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Config**","metadata":{}},{"cell_type":"code","source":"class Config:\n    train_dir=\"/kaggle/input/birdclef-2025/train_audio\"\n    train_csv=\"/kaggle/input/birdclef-2025/train.csv\"\n    sample_csv=\"/kaggle/input/birdclef-2025/sample_submission.csv\"\n    test_soundscapes=\"/kaggle/input/birdclef-2025/test_soundscapes\"\n    seed = 42\n    sr = int(32e3)\n    num_classes= 206\n    n_fft= 1024\n    hop_length=500\n    n_mels=128\n    fmin= 50\n    fmax=16000\n    power=2\n    ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-08T07:17:28.497274Z","iopub.execute_input":"2025-06-08T07:17:28.497972Z","iopub.status.idle":"2025-06-08T07:17:28.501842Z","shell.execute_reply.started":"2025-06-08T07:17:28.497949Z","shell.execute_reply":"2025-06-08T07:17:28.501137Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Helper**","metadata":{}},{"cell_type":"code","source":"def set_seed(seed: int =Config.seed)-> None:\n    random.seed(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    if torch.cuda.is_available():\n        torch.cuda.manual_seed(seed)\n        torch.cuda.manual_seed_all(seed)\n    torch.backends.cudnn.deterministic= True\n    torch.backends.cudnn.benchmark = False\n    print(f\"[INFO] Set seed: {seed}\")\n\nset_seed()\n\n    \n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-08T07:17:34.295810Z","iopub.execute_input":"2025-06-08T07:17:34.296277Z","iopub.status.idle":"2025-06-08T07:17:34.378035Z","shell.execute_reply.started":"2025-06-08T07:17:34.296254Z","shell.execute_reply":"2025-06-08T07:17:34.377305Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Dataset loader**","metadata":{}},{"cell_type":"code","source":"data_df = pd.read_csv(Config.train_csv)\nfor col in ('secondary_labels','type'):\n    data_df[col]=data_df[col].apply(lambda x:\"###\".join(literal_eval(x)))\ndata_df['filename']= data_df['filename'].apply(lambda x: Config.train_dir + \"/\"+x)\ndata_df.sample(10)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-08T07:17:41.285763Z","iopub.execute_input":"2025-06-08T07:17:41.286026Z","iopub.status.idle":"2025-06-08T07:17:41.871476Z","shell.execute_reply.started":"2025-06-08T07:17:41.286008Z","shell.execute_reply":"2025-06-08T07:17:41.870810Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**NULL CHECK**","metadata":{}},{"cell_type":"code","source":"data_df.isnull().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-08T07:17:53.653589Z","iopub.execute_input":"2025-06-08T07:17:53.653876Z","iopub.status.idle":"2025-06-08T07:17:53.674092Z","shell.execute_reply.started":"2025-06-08T07:17:53.653856Z","shell.execute_reply":"2025-06-08T07:17:53.673485Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Ratings dist**","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(20,5))\nsns.histplot(data_df, x='rating')\nplt.xticks(np.arange(0,5.5,0.5))\nplt.show();","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-08T07:18:03.847033Z","iopub.execute_input":"2025-06-08T07:18:03.847289Z","iopub.status.idle":"2025-06-08T07:18:04.192994Z","shell.execute_reply.started":"2025-06-08T07:18:03.847271Z","shell.execute_reply":"2025-06-08T07:18:04.192314Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for r in range (0,7):\n    plt.figure(figsize=(20,5))\n    sns.histplot(data_df[data_df['rating']==float(r)], x='primary_label')\n    plt.title(f\"rating {r}\")\n    plt.xticks(rotation=90)\n    plt.show();","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-08T07:19:55.115479Z","iopub.execute_input":"2025-06-08T07:19:55.115836Z","iopub.status.idle":"2025-06-08T07:20:00.836600Z","shell.execute_reply.started":"2025-06-08T07:19:55.115812Z","shell.execute_reply":"2025-06-08T07:20:00.835996Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**audio duration**","metadata":{}},{"cell_type":"code","source":"durations=[]\nfor idx, row in data_df.sample(100).iterrows():\n    data, _ = librosa.load(row['filename'], sr = Config.sr)\n    durations.append(librosa.get_duration(y=data, sr = Config.sr))\nd_df = pd.DataFrame(columns=[\"durations\"], data=durations)\nplt.figure(figsize=(12,5))\nplt.title(\"dist of audio lenghts\")\nsns.histplot(d_df,x=\"durations\")\nplt.show();\nd_df.describe()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-08T07:21:23.791438Z","iopub.execute_input":"2025-06-08T07:21:23.791728Z","iopub.status.idle":"2025-06-08T07:21:42.563803Z","shell.execute_reply.started":"2025-06-08T07:21:23.791708Z","shell.execute_reply":"2025-06-08T07:21:42.563074Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Spectograms**","metadata":{}},{"cell_type":"code","source":"def show_signal(file_path):\n    class_,collector =file_path.split(\"/\")[-2:]\n    y,sr=librosa.load(file_path,sr=Config.sr)\n    fig,axes=plt.subplots(2,2,figsize=(20,10))\n    fig.suptitle(f\"Class:{class_}|Collector:{collector}\",fontsize=16)\n    librosa.display.waveshow(y,sr=sr,ax=axes[0,0])\n    axes[0,0].set_title(\"raw signal\")\n#short time fourier transform \n    ft=np.abs(librosa.stft(\n        y,\n        n_fft=Config.n_fft,\n        hop_length=Config.hop_length\n        \n    ))\n    im1= librosa.display.specshow(\n        ft,\n        sr=sr,\n        x_axis='time',\n        y_axis='linear',\n        ax=axes[0,1]\n    )\n    fig.colorbar(im1,ax=axes[0,1])\n    axes[0,1].set_title(\"spectogram\")\n\n    ft_db=librosa.amplitude_to_db(ft,ref=np.max)\n    im2=librosa.display.specshow(\n        ft_db,\n        sr=sr,\n        x_axis='time',\n        y_axis='log',\n        ax=axes[1,0]\n    )\n    fig.colorbar(im1,ax=axes[1,0])\n    axes[1,0].set_title(\"Log scaled\")\n\n    mel_sp=librosa.feature.melspectrogram(\n        y=y,\n        sr=Config.sr,\n        fmin=Config.fmin,\n        fmax=Config.fmax,\n        power=Config.power,\n        n_mels=Config.n_mels,  \n    )\n    mel_sp=librosa.power_to_db(mel_sp,ref=np.max)\n    im3=librosa.display.specshow(\n        mel_sp,\n        y_axis='mel',\n        sr= Config.sr,\n        fmin=Config.fmin,\n        x_axis='time',\n        fmax=Config.fmax,\n        ax=axes[1,1]\n    )\n    fig.colorbar(im1,ax=axes[1,1])\n    axes[1,1].set_title(\"mel spectogram\")\n    \n    plt.show()\nshow_signal(data_df['filename'].values[0])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-08T07:22:07.112293Z","iopub.execute_input":"2025-06-08T07:22:07.112790Z","iopub.status.idle":"2025-06-08T07:22:14.550912Z","shell.execute_reply.started":"2025-06-08T07:22:07.112770Z","shell.execute_reply":"2025-06-08T07:22:14.550155Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for idx, row in data_df.sample(5).iterrows(): show_signal(row['filename'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-08T07:07:58.708501Z","iopub.execute_input":"2025-06-08T07:07:58.708799Z","iopub.status.idle":"2025-06-08T07:07:58.724574Z","shell.execute_reply.started":"2025-06-08T07:07:58.708772Z","shell.execute_reply":"2025-06-08T07:07:58.723786Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"label_mapper={\n    label:idx\n    for idx,label in enumerate (sorted(data_df['primary_label'].unique()))\n}\nrev_mapper={\n    idx:label\n    for label,idx in label_mapper.items()\n}\n\nclass BirdClefDataset(torch.utils.data.Dataset):\n    def __init__(self,df,mode=\"train\"):\n        self.df=df\n        self.mode=mode\n        \n    def __len__(self):return len(self.df)\n\n    \n    def process(self, audio_path):\n        data,_=librosa.load(audio_path, sr= Config.sr)\n        data=data*1024\n        chunk_duration=10\n        min_len= chunk_duration* Config.sr\n        # less than min \n        if len(data)< min_len:\n            cnt=int(np.ceil(min_len/len(data)))\n            data=np.tile(data,cnt)\n            \n        #len div by the min\n        leftover=len(data)%min_len\n        if leftover>0:\n            front_crop=leftover//2\n            back_crop=leftover-front_crop\n            data=data[front_crop:len(data)-back_crop]\n\n        data=data[:min_len]\n        data=data.reshape(-1,min_len)\n        mel_sp=librosa.feature.melspectrogram(\n            y=data,\n            sr=Config.sr,\n            fmin=Config.fmin,\n            fmax=Config.fmax,\n            power=Config.power,\n            n_mels=Config.n_mels,\n            n_fft=Config.n_fft,\n            hop_length=Config.hop_length\n        )\n        mel_sp=librosa.power_to_db(mel_sp,ref=1)\n\n\n        #normalize the feature values\n\n        eps=1e-12\n        mel_sp=(mel_sp-mel_sp.min())/(mel_sp.max()-mel_sp.min()+eps)\n        mel_sp=mel_sp[:,:,:640]\n        return mel_sp\n        \n    def __getitem__ (self,idx):\n        row=self.df.loc[idx,:]\n        filename= row['filename']\n        x=self.process(filename)\n\n        if self.mode==\"train\":\n            y= label_mapper[row['primary_label']]\n            return x,y\n        return x\n\n            ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-09T02:31:55.652292Z","iopub.execute_input":"2025-06-09T02:31:55.652610Z","iopub.status.idle":"2025-06-09T02:31:55.665460Z","shell.execute_reply.started":"2025-06-09T02:31:55.652586Z","shell.execute_reply":"2025-06-09T02:31:55.664607Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"mel_sp=BirdClefDataset(data_df).process(data_df['filename'].values[0])\nprint(mel_sp.shape)\n#transf from c,h,w->h,w,c\nplt.imshow(mel_sp.reshape(128,640,-1))\nplt.show();","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-09T02:32:06.062556Z","iopub.execute_input":"2025-06-09T02:32:06.063073Z","iopub.status.idle":"2025-06-09T02:32:06.072314Z","shell.execute_reply.started":"2025-06-09T02:32:06.063046Z","shell.execute_reply":"2025-06-09T02:32:06.071271Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Model**","metadata":{}},{"cell_type":"code","source":"class Model(nn.Module):\n    def __init__(self,model_name:str):\n        super().__init__()\n        self.base_model=timm.create_model(\n            model_name=model_name,\n            num_classes=Config.num_classes,\n            pretrained= False,\n            in_chans=1\n        )\n    def forward(self,x):\n        return self.base_model(x)\n    ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-09T02:30:53.079994Z","iopub.execute_input":"2025-06-09T02:30:53.080226Z","iopub.status.idle":"2025-06-09T02:30:53.185559Z","shell.execute_reply.started":"2025-06-09T02:30:53.080201Z","shell.execute_reply":"2025-06-09T02:30:53.184614Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Predictions without training**","metadata":{}},{"cell_type":"code","source":"tmp_ds=BirdClefDataset(data_df.sample(10).reset_index())\nmodel = Model(\"tf_efficientnet_b0\")\n\nfor i in range(10):\n    x,y = tmp_ds[i]\n    \n    model.eval()\n    \n    preds = model (torch.tensor([x]))\n    preds = torch.argmax(torch.softmax(preds,dim=1),dim=1).item()\n    \n    plt.imshow(x.reshape(128,640,-1))\n    plt.title(f\"Label:{rev_mapper[y]}|{x.shape}| model_pred:{rev_mapper[preds]}\")\n    plt.show()\ndel model\ndel tmp_ds","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-09T02:32:16.982263Z","iopub.execute_input":"2025-06-09T02:32:16.982901Z","iopub.status.idle":"2025-06-09T02:32:16.992633Z","shell.execute_reply.started":"2025-06-09T02:32:16.982856Z","shell.execute_reply":"2025-06-09T02:32:16.991539Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#for competition scoring\nclass ParticipantVisibleError(Exception):\n    pass\n\n\ndef score(solution: pd.DataFrame, submission: pd.DataFrame, row_id_column_name: str) -> float:\n    '''\n    Version of macro-averaged ROC-AUC score that ignores all classes that have no true positive labels.\n    '''\n    del solution[row_id_column_name]\n    del submission[row_id_column_name]\n\n    if not pandas.api.types.is_numeric_dtype(submission.values):\n        bad_dtypes = {x: submission[x].dtype  for x in submission.columns if not pandas.api.types.is_numeric_dtype(submission[x])}\n        raise ParticipantVisibleError(f'Invalid submission data types found: {bad_dtypes}')\n\n    solution_sums = solution.sum(axis=0)\n    scored_columns = list(solution_sums[solution_sums > 0].index.values)\n    assert len(scored_columns) > 0\n\n    return kaggle_metric_utilities.safe_call_score(sklearn.metrics.roc_auc_score, solution[scored_columns].values, submission[scored_columns].values, average='macro')\n\ndef cal_score(labels,preds):\n    labels=np.concatenate(labels)\n    preds=np.concatenate(preds)\n\n    labels_df=pd.DataFrame(labels>0.5,columns=list(label_mapper.keys()))\n    pred_df=pd.DataFrame(preds,columns=list(label_mapper.keys()))\n\n    labels_df['id']=np.arange(len(labels_df))\n    pred_df['id']=np.arange(len(pred_df))\n    return score(labels_df,pred_df,row_id_column_name='id')\n    ","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Training**","metadata":{}},{"cell_type":"markdown","source":"training congis:","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import StratifiedKFold\nfrom tqdm import tqdm\nimport gc\nepochs=20\nnum_folds=3\ndevice=\"cuda\" if torch.cuda.is_available() else \"cpu\"\nlr=1e-4\ntarget_col='primary_label'\ndf= data_df\nskf = StratifiedKFold(\n    n_splits=num_folds,\n    shuffle= True,\n    random_state=Config.seed\n)\ndf['kfold']=-1\nfor fold, (train_idx, val_idx) in enumerate(skf.split(X=df, y=df[target_col])):\n    df.loc[val_idx,'kfold']=fold","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"raw","source":"","metadata":{"execution":{"iopub.status.busy":"2025-06-06T21:07:06.630725Z","iopub.execute_input":"2025-06-06T21:07:06.631451Z","iopub.status.idle":"2025-06-06T21:07:06.652763Z","shell.execute_reply.started":"2025-06-06T21:07:06.63142Z","shell.execute_reply":"2025-06-06T21:07:06.651427Z"}}},{"cell_type":"code","source":"\n\nfor fold in range(num_folds):\n    train_df=df[df['kfold']!=fold].reset_index(drop= True)\n    val_df=df[df['kfold']== fold].reset_index(drop=True)\n    train_ds= BirdClefDataset(train_df)\n    val_ds= BirdClefDataset(val_df)\n\n    train_loader=torch.utils.data.DataLoader(\n        train_ds,\n        batch_size=8,\n        shuffle= True,\n        num_workers=2,\n        drop_last=True\n    )\n    val_loader=torch.utils.data.DataLoader(\n        val_ds,\n        batch_size=16,\n        shuffle= False,\n        num_workers=2,\n        drop_last=False\n    )   \n    \n    model= Model(model_name = \"tf_efficientnet_b0\").to(device)\n    criterion=nn.CrossEntropyLoss()\n\n    optimizer=torch.optim.Adam(model.parameters(),lr=lr)\n    best_auc=0\n    #per fold training\n    for epoch in range(epochs):\n        model.train()\n        pred_train=[]\n        label_train=[]\n        running_loss=0.0\n\n        for(x,y) in tqdm(train_loader,desc=\"Training\"):\n            x,y=x.to(device),y.to(device)\n            y_one_hot=nn.functional.one_hot(\n                y,\n                num_classes=Config.num_classes\n            ).float()\n            optimizer.zero_grad()\n            outputs=model(x)\n            loss=criterion(outputs,y)\n            loss.backward()\n            optimizer.step()\n            running_loss +=loss.item()\n            probs=torch.softmax(outputs,dim=1)\n            pred_train.append(probs.detach().cpu().numpy())\n            label_train.append(y_one_hot.detach().cpu().numpy())\n#validation\n    model.eval()\n    pred_val=[]\n    label_val=[]\n    running_val_loss=0.0\n\n    with torch.no_grad():\n        for(x,y) in tqdm(val_loader,desc=\"Validation\"):\n            x, y =x.to(device),y.to(device)\n            y_one_hot=nn.functional.one_hot(\n                y,\n                num_classes=Config.num_classes\n            ).float()\n            \n            outputs=model(x)\n            loss=criterion(outputs,y)\n       \n      \n            running_val_loss +=loss.item()\n            probs=torch.softmax(outputs,dim=1)\n            pred_val.append(probs.detach().cpu().numpy())\n            label_val.append(y_one_hot.detach().cpu().numpy())\n                \n #computin AUC and loss           \n        auc_train=cal_score(label_train,pred_train)\n        auc_val=cal_score(label_val,pred_val)\n        avg_train_loss=running_loss/len(train_loader)\n        avg_val_loss=running_loss/len(val_loader)\n        print(f\"[Fold]: {fold}| [EPOCH]: {epoch} | Loss: {avg_train_loss:.4f} | Val loss: {avg_val_loss:.4f}\")\n        print(f\"[Fold]: {fold}| [EPOCH]: {epoch} | Train AUC: {auc_train:.4f} | Val AUC: {auc_val:.4f}\")\n        if best_auc<=auc_val and epoch<5:\n            best_auc=auc_val\n            torch.save(\n                model.state_dict(),\n                f\"fold{fold}_epoch_{epoch}_effnetB0_val_auc_{auc_val}_val_loss_{avg_val_loss}.pth\"\n            )\n    del train_df,val_df,train_ds,val_ds,train_loader,val_loader,model,criterion,optimizer\n    gc.collect()\n    torch.cuda.empty_cache()\n    ","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(label_train),label_train[0].shape,len(pred_train),pred_train[0].shape","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"np.concatenate(pred_train).shape","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}