{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Imports, paths, and generators","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport tensorflow as tf\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras import layers, models\n\nTRAIN_DIR = '/kaggle/input/datasets/kelbycraft/kwc-freesound-2018-melspec-128-magma/spectrograms/train'\nTEST_DIR  = '/kaggle/input/datasets/kelbycraft/kwc-freesound-2018-melspec-128-magma/spectrograms/test'          # contains subfolder 'all'\n\nIMG_H, IMG_W = 100, 150\nBATCH = 64\n\nprint('GPU:', tf.config.list_physical_devices('GPU'))\n\n# split train into train/val, 80/20\ntrain_gen = ImageDataGenerator(rescale=1./255, validation_split=0.2)\n\ntrain_flow = train_gen.flow_from_directory(\n    TRAIN_DIR, target_size=(IMG_H, IMG_W), batch_size=BATCH,\n    class_mode='categorical', subset='training', shuffle=True, seed=42)\n\nval_flow = train_gen.flow_from_directory(\n    TRAIN_DIR, target_size=(IMG_H, IMG_W), batch_size=BATCH,\n    class_mode='categorical', subset='validation', shuffle=False, seed=42)\n\nNUM_CLASSES = train_flow.num_classes\nprint('classes:', NUM_CLASSES)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-07-18T21:52:04.307912Z","iopub.execute_input":"2026-07-18T21:52:04.308659Z","iopub.status.idle":"2026-07-18T21:52:11.582345Z","shell.execute_reply.started":"2026-07-18T21:52:04.308627Z","shell.execute_reply":"2026-07-18T21:52:11.581561Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Starter CNN","metadata":{}},{"cell_type":"code","source":"model = models.Sequential([\n    layers.Input((IMG_H, IMG_W, 3)),\n    layers.Conv2D(32, 3, activation='relu', padding='same'),\n    layers.BatchNormalization(),\n    layers.MaxPooling2D(),\n    layers.Conv2D(64, 3, activation='relu', padding='same'),\n    layers.BatchNormalization(),\n    layers.MaxPooling2D(),\n    layers.Conv2D(128, 3, activation='relu', padding='same'),\n    layers.BatchNormalization(),\n    layers.MaxPooling2D(),\n    layers.GlobalAveragePooling2D(),\n    layers.Dropout(0.3),\n    layers.Dense(256, activation='relu'),\n    layers.Dropout(0.3),\n    layers.Dense(NUM_CLASSES, activation='softmax'),\n])\n\nmodel.compile(optimizer='adam',\n              loss='categorical_crossentropy',\n              metrics=['accuracy'])\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-18T21:58:59.290528Z","iopub.execute_input":"2026-07-18T21:58:59.290849Z","iopub.status.idle":"2026-07-18T21:59:01.878935Z","shell.execute_reply.started":"2026-07-18T21:58:59.290824Z","shell.execute_reply":"2026-07-18T21:59:01.878341Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Train","metadata":{}},{"cell_type":"code","source":"history = model.fit(\n    train_flow,\n    validation_data=val_flow,\n    epochs=20,\n    callbacks=[\n        tf.keras.callbacks.EarlyStopping(patience=4, restore_best_weights=True),\n        tf.keras.callbacks.ReduceLROnPlateau(patience=2, factor=0.5),\n    ],\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-18T21:59:57.879584Z","iopub.execute_input":"2026-07-18T21:59:57.879852Z","iopub.status.idle":"2026-07-18T22:13:01.066579Z","shell.execute_reply.started":"2026-07-18T21:59:57.879831Z","shell.execute_reply":"2026-07-18T22:13:01.065530Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# MAP@3 Score","metadata":{}},{"cell_type":"code","source":"def mapk_from_probs(probs, true_idx, k=3):\n    top_k = np.argsort(-probs, axis=1)[:, :k]\n    score = 0.0\n    for i, true in enumerate(true_idx):\n        hits = np.where(top_k[i] == true)[0]\n        if len(hits):                       # precision at the rank it appears\n            score += 1.0 / (hits[0] + 1)\n    return score / len(true_idx)\n\n# val_flow has shuffle=False, so classes line up with prediction order\nval_probs = model.predict(val_flow, verbose=1)\nval_true = val_flow.classes\nprint('Validation MAP@3:', round(mapk_from_probs(val_probs, val_true), 4))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-18T22:50:21.249780Z","iopub.execute_input":"2026-07-18T22:50:21.250507Z","iopub.status.idle":"2026-07-18T22:50:31.468735Z","shell.execute_reply.started":"2026-07-18T22:50:21.250479Z","shell.execute_reply":"2026-07-18T22:50:31.467891Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Predict test / MAP@3 Submission","metadata":{}},{"cell_type":"code","source":"test_gen = ImageDataGenerator(rescale=1./255)\ntest_flow = test_gen.flow_from_directory(\n    TEST_DIR, target_size=(IMG_H, IMG_W), batch_size=BATCH,\n    class_mode=None, shuffle=False)\n\nprobs = model.predict(test_flow, verbose=1)\n\n# map generator's class indices -> label names\nidx_to_label = {v: k for k, v in train_flow.class_indices.items()}\n\n# top-3 per clip, space-joined (MAP@3 format)\ntop3 = np.argsort(-probs, axis=1)[:, :3]\nlabels = [' '.join(idx_to_label[i] for i in row) for row in top3]\n\n# filenames from generator (strip 'all/' prefix and .png -> .wav)\nfnames = [f.split('/')[-1].replace('.png', '.wav') for f in test_flow.filenames]\n\nsubmission = pd.DataFrame({'fname': fnames, 'label': labels})\nsubmission.to_csv('/kaggle/working/submission.csv', index=False)\nprint(submission.head())\nprint('rows:', len(submission))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-18T22:47:41.169697Z","iopub.execute_input":"2026-07-18T22:47:41.170510Z","iopub.status.idle":"2026-07-18T22:49:22.129328Z","shell.execute_reply.started":"2026-07-18T22:47:41.170481Z","shell.execute_reply":"2026-07-18T22:49:22.128597Z"}},"outputs":[],"execution_count":null}]}