{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":91844,"databundleVersionId":11361821,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":11717609,"sourceType":"datasetVersion","datasetId":7355445}],"dockerImageVersionId":31012,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\n\nimport os","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-05-08T07:54:04.408338Z","iopub.execute_input":"2025-05-08T07:54:04.408515Z","iopub.status.idle":"2025-05-08T07:54:06.381860Z","shell.execute_reply.started":"2025-05-08T07:54:04.408498Z","shell.execute_reply":"2025-05-08T07:54:06.381052Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"labels = sorted(os.listdir('/kaggle/input/birdclef-2025/train_audio/'))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-08T07:54:06.383651Z","iopub.execute_input":"2025-05-08T07:54:06.383963Z","iopub.status.idle":"2025-05-08T07:54:06.395402Z","shell.execute_reply.started":"2025-05-08T07:54:06.383943Z","shell.execute_reply":"2025-05-08T07:54:06.394594Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"file_paths = []\nbase_path = '/kaggle/input/birdclef-2025/train_audio'\nsub_dir = []","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-08T07:54:06.395990Z","iopub.execute_input":"2025-05-08T07:54:06.396212Z","iopub.status.idle":"2025-05-08T07:54:06.420255Z","shell.execute_reply.started":"2025-05-08T07:54:06.396190Z","shell.execute_reply":"2025-05-08T07:54:06.419596Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for dir in os.listdir(base_path):\n    subdir_path = os.path.join(base_path, dir)\n    if os.path.isdir(subdir_path):\n        for file in os.listdir(subdir_path):\n            file_paths.append(f\"{dir}/{file}\")\n            sub_dir.append(dir)\ndf = pd.DataFrame({'classes': sub_dir, 'filename': file_paths})\ndf.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-08T07:54:06.421317Z","iopub.execute_input":"2025-05-08T07:54:06.421586Z","iopub.status.idle":"2025-05-08T07:54:08.194432Z","shell.execute_reply.started":"2025-05-08T07:54:06.421556Z","shell.execute_reply":"2025-05-08T07:54:08.193806Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tqdm import tqdm\nimport librosa\nfrom joblib import Parallel, delayed","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-08T07:54:08.195290Z","iopub.execute_input":"2025-05-08T07:54:08.195611Z","iopub.status.idle":"2025-05-08T07:54:08.234061Z","shell.execute_reply.started":"2025-05-08T07:54:08.195593Z","shell.execute_reply":"2025-05-08T07:54:08.233365Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def add_noise(audio, noise_factor=0.005):\n    noise = np.random.randn(len(audio))\n    return np.clip(audio + noise_factor * noise, -1.0, 1.0)\n\ndef shift_pitch_audio(audio, sr, n_steps=2):\n    return librosa.effects.pitch_shift(audio, sr=sr, n_steps=n_steps)\n\n\ndef change_volume(audio, gain=1.1):\n    return np.clip(audio * gain, -1.0, 1.0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-08T07:54:08.234805Z","iopub.execute_input":"2025-05-08T07:54:08.235041Z","iopub.status.idle":"2025-05-08T07:54:08.239718Z","shell.execute_reply.started":"2025-05-08T07:54:08.235019Z","shell.execute_reply":"2025-05-08T07:54:08.238978Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nimport numpy as np\nfrom tqdm import tqdm\nimport os\nimport librosa\nfrom joblib import Parallel, delayed\n\ndef features_extractor(file, label):\n    try:\n        audio, sample_rate = librosa.load(file, sr=32000)\n        mfcc_list = []\n        mfccs = librosa.feature.mfcc(y=audio, sr=sample_rate, n_mfcc=40)\n        mfccs_scaled = (mfccs - np.mean(mfccs, axis=1, keepdims=True)) / (np.std(mfccs, axis=1, keepdims=True) + 1e-8)\n        mfcc_list.append(mfccs_scaled)\n        label_list = [label]\n\n        if np.random.rand() < 0.3:\n            aug_type = np.random.choice([\"noise\", \"volume\", \"pitch\"], p=[0.6, 0.2, 0.2])\n            if aug_type == \"noise\":\n                augmented = add_noise(audio)\n            elif aug_type == \"volume\":\n                gain = np.random.uniform(0.8, 1.2)\n                augmented = change_volume(audio, gain)\n            elif aug_type == \"pitch\":\n                steps = np.random.randint(-2, 3)\n                augmented = shift_pitch_audio(audio, sr=sample_rate, n_steps=steps)\n            else:\n                augmented = audio\n\n            mfccs_aug = librosa.feature.mfcc(y=augmented, sr=sample_rate, n_mfcc=40)\n            mfccs_aug_scaled = (mfccs_aug - np.mean(mfccs_aug, axis=1, keepdims=True)) / (np.std(mfccs_aug, axis=1, keepdims=True) + 1e-8)\n            mfcc_list.append(mfccs_aug_scaled)\n            label_list.append(label)\n\n        return mfcc_list, label_list\n    except Exception as e:\n        print(f\"Error processing file {file}: {e}\")\n        return [], []\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-08T07:54:08.241925Z","iopub.execute_input":"2025-05-08T07:54:08.242465Z","iopub.status.idle":"2025-05-08T07:54:08.255125Z","shell.execute_reply.started":"2025-05-08T07:54:08.242442Z","shell.execute_reply":"2025-05-08T07:54:08.254510Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Function to process a single file\ndef process_file(filename, base_path, df):\n    file_path = os.path.join(base_path, filename)\n    try:\n        label = df.loc[df['filename'] == filename, 'classes'].values[0]\n        features, labels = features_extractor(file_path, label)\n        return features, labels\n    except Exception as e:\n        print(f\"Error processing {filename}: {e}\")\n        return [], []\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-08T07:54:08.255810Z","iopub.execute_input":"2025-05-08T07:54:08.256043Z","iopub.status.idle":"2025-05-08T07:54:08.273486Z","shell.execute_reply.started":"2025-05-08T07:54:08.256021Z","shell.execute_reply":"2025-05-08T07:54:08.272798Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def extract_features_parallel(df_train, base_path, n_jobs=4):\n    results = Parallel(n_jobs=8)(\n        delayed(process_file)(filename, base_path, df_train) for filename in tqdm(df_train['filename'])\n    )\n\n    extracted_features = []\n    labels = []\n    for feats, lbls in results:\n        extracted_features.extend(feats)\n        labels.extend(lbls)\n\n    return extracted_features, labels\n\n\nbase_path = '/kaggle/input/birdclef-2025/train_audio'\nextracted_features, labels = extract_features_parallel(df, base_path, n_jobs=16)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-08T07:54:08.274136Z","iopub.execute_input":"2025-05-08T07:54:08.274344Z","iopub.status.idle":"2025-05-08T08:28:06.886524Z","shell.execute_reply.started":"2025-05-08T07:54:08.274328Z","shell.execute_reply":"2025-05-08T08:28:06.885867Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\n\ndef pad_or_truncate(features, target_length=250): \n    if features.shape[1] > target_length:\n        return features[:, :target_length] \n    else:\n        padding = np.zeros((features.shape[0], target_length - features.shape[1]))\n        return np.concatenate((features, padding), axis=1)\n\npadded_features = [pad_or_truncate(f) for f in extracted_features]\npadded_features = [f for f in padded_features if f is not None]\n\ndata_x = np.array(padded_features)\ndata_y = np.array(labels[:len(data_x)])  \n\nprint(\"x.shape:\", data_x.shape)\nprint(\"y.shape:\", data_y.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-08T08:28:06.887406Z","iopub.execute_input":"2025-05-08T08:28:06.887633Z","iopub.status.idle":"2025-05-08T08:28:08.097977Z","shell.execute_reply.started":"2025-05-08T08:28:06.887612Z","shell.execute_reply":"2025-05-08T08:28:08.097080Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder\nfrom tensorflow.keras.utils import to_categorical\nlabel_encoder = LabelEncoder()\ninteger_encoded = label_encoder.fit_transform(data_y)\n\ndata_y_encoded = to_categorical(integer_encoded, num_classes=206)\ndata_y_encoded","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-08T08:28:08.099075Z","iopub.execute_input":"2025-05-08T08:28:08.099579Z","iopub.status.idle":"2025-05-08T08:28:23.780147Z","shell.execute_reply.started":"2025-05-08T08:28:08.099543Z","shell.execute_reply":"2025-05-08T08:28:23.779523Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split, StratifiedKFold","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-08T08:28:23.780776Z","iopub.execute_input":"2025-05-08T08:28:23.781185Z","iopub.status.idle":"2025-05-08T08:28:23.820288Z","shell.execute_reply.started":"2025-05-08T08:28:23.781168Z","shell.execute_reply":"2025-05-08T08:28:23.819603Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X_train, X_test, y_train, y_test = train_test_split(data_x, data_y_encoded, test_size=0.2, random_state=42)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-08T08:28:23.821102Z","iopub.execute_input":"2025-05-08T08:28:23.821330Z","iopub.status.idle":"2025-05-08T08:28:27.769419Z","shell.execute_reply.started":"2025-05-08T08:28:23.821312Z","shell.execute_reply":"2025-05-08T08:28:27.768884Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def adjust_learning_rate(epochs):\n  learning_rate = 1e-1\n  if epochs > 160:\n    learning_rate *= 5e-4\n  elif epochs > 120:\n    learning_rate *= 1e-3\n  elif epochs > 80:\n    learning_rate *= 5e-3\n  elif epochs > 40:\n    learning_rate *= 5e-2\n  elif epochs >= 0:\n    learning_rate *= 1e-1\n  return learning_rate","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-08T08:28:27.770187Z","iopub.execute_input":"2025-05-08T08:28:27.770827Z","iopub.status.idle":"2025-05-08T08:28:27.774710Z","shell.execute_reply.started":"2025-05-08T08:28:27.770798Z","shell.execute_reply":"2025-05-08T08:28:27.774040Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nimport tensorflow as tf\nfrom tensorflow.keras import layers, models\nfrom keras.optimizers import Adam\nfrom keras.models import Model\nfrom tensorflow.keras.models import Sequential\nfrom keras import layers\nfrom keras.layers import Dense, Input, BatchNormalization, Activation, Flatten, Dropout, TimeDistributed\nfrom keras.layers import Conv2D, SeparableConv2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D, ConvLSTM2D\nfrom keras.callbacks import ModelCheckpoint, LearningRateScheduler\nfrom keras.callbacks import ReduceLROnPlateau, EarlyStopping\nfrom keras.regularizers import l2\nfrom tensorflow.keras.models import load_model\n\nlr_scheduler = LearningRateScheduler(adjust_learning_rate)\n\nlr_reducer = ReduceLROnPlateau(factor=np.sqrt(0.1),\n                               cooldown=0,\n                               patience=5,\n                               min_lr=5e-6)\n\ndef cnn_model(input_shape=(40, 250, 1), num_classes=206):\n    inputs = tf.keras.Input(shape=input_shape)\n\n    x = layers.Conv2D(64, (3, 3), activation='relu', padding='same', kernel_initializer='he_normal', kernel_regularizer=tf.keras.regularizers.l2(1e-3))(inputs)\n    x = layers.BatchNormalization()(x)\n    x = layers.MaxPooling2D((2, 2))(x)\n    x = layers.Dropout(0.2)(x)\n    \n    x = layers.Conv2D(128, (3, 3), activation='relu', padding='same', kernel_initializer='he_normal', kernel_regularizer=tf.keras.regularizers.l2(1e-3))(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.MaxPooling2D((2, 2))(x)\n    x = layers.Dropout(0.2)(x)\n\n    x = layers.Conv2D(256, (3, 3), activation='relu', padding='same', kernel_initializer='he_normal', kernel_regularizer=tf.keras.regularizers.l2(1e-3))(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.MaxPooling2D((2, 2))(x)\n    x = layers.Dropout(0.2)(x)\n\n    x = layers.Conv2D(512, (3, 3), activation='relu', padding='same', kernel_initializer='he_normal', kernel_regularizer=tf.keras.regularizers.l2(1e-3))(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.MaxPooling2D((2, 2))(x)\n    x = layers.Dropout(0.2)(x)\n\n    x = layers.Conv2D(512, (3, 3), activation='relu', padding='same', kernel_initializer='he_normal', kernel_regularizer=tf.keras.regularizers.l2(1e-3))(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.GlobalAveragePooling2D()(x)\n\n    x = layers.Dense(256, activation='relu')(x)\n    outputs = layers.Dense(num_classes, activation='softmax', kernel_initializer='he_normal', kernel_regularizer=tf.keras.regularizers.l2(1e-3))(x)\n\n    return models.Model(inputs, outputs)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-08T08:28:27.775460Z","iopub.execute_input":"2025-05-08T08:28:27.775643Z","iopub.status.idle":"2025-05-08T08:28:27.800591Z","shell.execute_reply.started":"2025-05-08T08:28:27.775627Z","shell.execute_reply":"2025-05-08T08:28:27.799980Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model_type = 'CNN'\nsave_dir = os.path.join(os.getcwd(), 'saved_models') \nmodel_name = f'BirdClef_{model_type}_model.{{epoch:03d}}.keras'\nfilepath = os.path.join(save_dir, model_name)\nif not os.path.isdir(save_dir): \n    os.makedirs(save_dir) \nfilepath = os.path.join(save_dir, model_name)\ncheckpoint = ModelCheckpoint(filepath=filepath, \n                              monitor='val_auc_roc', \n                              verbose=1, \n                              save_best_only=True,\n                            mode='max') \ncallbacks = [checkpoint, lr_reducer, lr_scheduler]\ncnn = cnn_model()\ncnn.compile(optimizer=Adam(learning_rate=adjust_learning_rate(0)), loss='categorical_crossentropy', metrics=['accuracy', tf.keras.metrics.AUC(curve='ROC', name='auc_roc')])\n\ncnn.fit(X_train, y_train, epochs=200, batch_size=32, validation_data=(X_test, y_test),  shuffle=True, callbacks=callbacks)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-08T08:28:27.801349Z","iopub.execute_input":"2025-05-08T08:28:27.801608Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}