{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.11.11"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":91844,"databundleVersionId":11361821,"sourceType":"competition"},{"sourceId":11633037,"sourceType":"datasetVersion","datasetId":7298741},{"sourceId":11892863,"sourceType":"datasetVersion","datasetId":7433665}],"dockerImageVersionId":31040,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Import and config","metadata":{}},{"cell_type":"code","source":"!cp /kaggle/input/nvidia-dali-installation-package/nvidia-dali/* .\n!pip install --no-index --find-links=. \\\n    nvidia_nvjpeg_cu12*.whl \\\n    nvidia_nvjpeg2k_cu12*.whl \\\n    nvidia_nvtiff_cu12*.whl \\\n    nvidia_nvimgcodec_cu12*.whl \\\n    packaging*.whl \n!cd /kaggle/input/nvidia-dali-installation-package/nvidia-dali && ls |grep nvidia_dali_nightly_cuda120 |xargs pip install  \nimport matplotlib.pyplot as plt\n%matplotlib inline\nimport os, gc, random \nimport pandas as pd\nimport pickle\nfrom pathlib import Path\nfrom tqdm.notebook import tqdm\nimport IPython.display as ipd\nfrom IPython.display import display, clear_output\nimport ipywidgets as widgets\nimport librosa\nimport librosa.display\nimport soundfile as sf\nimport numpy as np\nimport joblib\nimport timm\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import roc_auc_score, accuracy_score, confusion_matrix\n\nfrom nvidia.dali import pipeline_def\nimport nvidia.dali.fn as fn\nimport nvidia.dali.types as types\nimport nvidia.dali as dali\n\n# clear_output()\n# print(\"Install and Import DONE\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\nclass Config:\n    def __init__(self, **kwargs):\n        for k, v in kwargs.items():\n            setattr(self, k, v)\n\n    def update(self, **kwargs):\n        for k, v in kwargs.items():\n            setattr(self, k, v)\n\n# Initialize and set basic configuration\ncfg = Config(\n    SEED=42, \n    USE_AUDIO_AS_INPUT = True, # tiền xử lý lại từ đầu nếu set True, set False sẽ lấy đầu vào là ảnh.\n    USE_SMOOTH_LABEL = True,\n    # MEATA_DATA_PATH = Path('train_soundscape_data/meta_train_soundscape.csv'),\n    SUBMISSION_SAMPLE_PATH = Path('/kaggle/input/birdclef-2025/sample_submission.csv'),\n    DATA_PATH=Path(\"/kaggle/input/birdclef-2025/test_soundscapes\"),\n    OUTPUT_FOLDER =Path(\"evaluation\"),\n    MODEL_PATH = Path(\"/kaggle/input/modelbirdclef/best_epoch_rms_21_05.pth\"),\n    CLASS_NAME = joblib.load(\"/kaggle/input/modelbirdclef/label_encoder.pkl\").tolist(),\n    NUM_CLASSES = 206,\n    COLOR_MAP ='inferno',\n    SAMPLE_RATE=32000,\n    WINDOW=\"hann\",\n    NFILTER_MEL=128,\n    WINDOW_LENGTH= 1024,\n    WINDOW_STEP= 512,\n    FREQ_HIGH=14000,\n    TARGET_DURATION_S = 5,\n    TARGET_SAMPLES = 5*32000,\n    DEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\"),\n    )\n# Function to seed everything to ensure reproducibility\ndef seed_everything(seed):\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = False # Change to true if input sizes are kept constant\n\nseed_everything(cfg.SEED)\n# Verifying changes\nprint(cfg.__dict__)\n# Device check\nprint(f\"Using device: {cfg.DEVICE}\")","metadata":{"execution":{"iopub.status.busy":"2025-05-16T08:39:49.785787Z","iopub.execute_input":"2025-05-16T08:39:49.786144Z","iopub.status.idle":"2025-05-16T08:39:49.798506Z","shell.execute_reply.started":"2025-05-16T08:39:49.786116Z","shell.execute_reply":"2025-05-16T08:39:49.797234Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data pre-processing function","metadata":{}},{"cell_type":"code","source":"from torchvision import transforms\n# ============= Data transform to convert 2 tensor==============\nimport torch.nn.functional as F\n\nclass ResizeTensor:\n    def __init__(self, size=(224, 224)):\n        self.size = size\n\n    def __call__(self, x: torch.Tensor) -> torch.Tensor:\n        # x: [1, H, W] → resize → [1, 224, 224]\n        x = F.interpolate(x.unsqueeze(0), size=self.size, mode='bilinear', align_corners=False)\n        return x.squeeze(0)\n\nclass To3Channels:\n    def __call__(self, x: torch.Tensor) -> torch.Tensor:\n        # x: [1, H, W] → [3, H, W]\n        return x.expand(3, -1, -1)\n\nclass NormalizeTensor:\n    def __init__(self, mean=0.5, std=0.5):\n        self.mean = mean\n        self.std = std\n\n    def __call__(self, x: torch.Tensor) -> torch.Tensor:\n        # Normalize to: (x - mean) / std\n        return (x - self.mean) / self.std\n\nclass TransformCompose:\n    def __init__(self, transforms):\n        self.transforms = transforms\n\n    def __call__(self, x):\n        for t in self.transforms:\n            x = t(x)\n        return x\n# ========== TRANSFORMS ==========\ntensor_transform = TransformCompose([\n    ResizeTensor((224, 224)),  # Resize [1, 128, 312] → [1, 224, 224]\n    To3Channels(),             # Convert [1, 224, 224] → [3, 224, 224]\n    NormalizeTensor(0.5, 0.5)  # Normalize float32 tensor to [-1, 1]\n])\n\n# ============================\n# 1. Hàm load và tiền xử lý audio\n# ============================\ndef load_and_preprocess(audio_path, sr):\n    \"\"\"\n    - Load file .ogg bằng librosa.\n    # - Giảm nhiễu bằng noisereduce.\n    # - Tăng âm lượng bằng pedalboard.\n    \"\"\"\n    samples, sr = librosa.load(audio_path, sr=sr)\n    # samples_nr = nr.reduce_noise(y=samples, sr=sr)\n    # board = Pedalboard([Gain(gain_db=10)])\n    # samples_proc = board(samples_nr, sr)\n    return samples, sr\n\n# ============================\n# 2. Hàm tạo sliding window (5 giây, bước nhảy 0.5 giây)\n# ============================\ndef get_sliding_windows(audio, sr, segment_duration=5.0, overlap_percent=0.5):\n    \"\"\"\n    Trả về danh sách các tuple (start_sample, end_sample) cho mỗi window.\n    \"\"\"\n    step = segment_duration*(1-overlap_percent)\n    seg_samples = int(segment_duration * sr)\n    step_samples = int(step * sr)\n    windows = []\n    for start in range(0, len(audio) - seg_samples + 1, step_samples):\n        end = start + seg_samples\n        windows.append((start, end))\n    return windows\n\n# -------------------------\n# 3. Tạo Mel Spectrogram\n# -------------------------\n\ndef compute_mel_spectrogram(audio_segment, cfg):\n    audio_data = np.array(audio_segment, dtype=np.float32)\n\n    @pipeline_def\n    def mel_spectrogram_pipe(nfft, window_length, window_step, sample_rate, nfilter, freq_high, device=\"cpu\"):\n        audio = types.Constant(device=device, value=audio_data)\n        spectrogram = fn.spectrogram(\n            audio,\n            device=device,\n            nfft=nfft,\n            window_length=window_length,\n            window_step=window_step,\n        )\n        mel_spectrogram = fn.mel_filter_bank(\n            spectrogram,\n            device=device,\n            sample_rate=sample_rate,\n            nfilter=nfilter,\n            freq_high=freq_high\n        )\n        mel_spectrogram_dB = fn.to_decibels(\n            mel_spectrogram,\n            device=device,\n            multiplier=10.0,\n            cutoff_db=-80\n        )\n        return mel_spectrogram_dB\n\n    pipe = mel_spectrogram_pipe(\n        device=\"cpu\",\n        batch_size=1,\n        num_threads=1,\n        nfft=cfg.WINDOW_LENGTH,\n        window_length=cfg.WINDOW_LENGTH,\n        window_step=cfg.WINDOW_STEP,\n        sample_rate=cfg.SAMPLE_RATE,\n        nfilter=cfg.NFILTER_MEL,\n        freq_high=cfg.FREQ_HIGH,\n    )\n\n    pipe.build()\n    outputs = pipe.run()\n    mel_spectrogram_dali_db = np.array(outputs[0][0])  # No need for .as_cpu()\n\n    return mel_spectrogram_dali_db\n    \n# ============================\n # 5. Smoothing Label\n# ============================\n \ndef smooth_label(sub):\n    cols = sub.columns[1:]\n    groups = sub['row_id'].str.rsplit('_', n=1).str[0]\n    groups = groups.values\n    for group in np.unique(groups):\n        sub_group = sub[group == groups]\n        predictions = sub_group[cols].values\n        new_predictions = predictions.copy()\n        for i in range(1, predictions.shape[0]-1):\n            new_predictions[i] = (predictions[i-1] * 0.2) + (predictions[i] * 0.6) + (predictions[i+1] * 0.2)\n        new_predictions[0] = (predictions[0] * 0.9) + (predictions[1] * 0.1)\n        new_predictions[-1] = (predictions[-1] * 0.9) + (predictions[-2] * 0.1)\n        sub_group[cols] = new_predictions\n        sub[group == groups] = sub_group\n    return sub\n\n#==============================\n#6. Process 1 audio file\n#==============================\ndef process_audio_file(audio_path, model, output_folder, cfg, overlap_percent):\n    saved_data = [] # list of [row_id, saved image path] \n    samples_proc, sr = load_and_preprocess(audio_path, cfg.SAMPLE_RATE)\n    # Tạo sliding windows, mỗi window TARGET_DURATION_S giây, bước 0.5 giây\n    windows = get_sliding_windows(samples_proc, sr, cfg.TARGET_DURATION_S, overlap_percent = overlap_percent)\n    \n    # Lấy human voice intervals nếu tồn tại cho file này (dùng đường dẫn tương đối so với DATA_PATH)\n    for start_sample, end_sample in windows:\n        # Format for saving\n        window_start_time = start_sample / sr\n        base_name = os.path.splitext(Path(audio_path).name)[0]\n        # output_filename = f\"{base_name}_{window_start_time:.1f}_{(window_start_time+cfg.TARGET_DURATION_S):.1f}.jpg\"\n        row_id = base_name + f'_{int (window_start_time+cfg.TARGET_DURATION_S)}'\n\n        chunk = samples_proc[start_sample:end_sample]\n        # Preprocessing \n        segment = np.array(chunk, dtype=np.float32)\n        spec = compute_mel_spectrogram(segment, cfg)\n        spec = (spec + 80.0) / 80.0\n        spec_tensor = torch.from_numpy(spec).unsqueeze(0)\n        x = tensor_transform(spec_tensor)     # [3, 224, 224]\n        x = x.unsqueeze(0).to(cfg.DEVICE)  # [1, 3, 224, 224]\n        \n        # pil_img, saved_path = get_and_save_spectrogram_image(mel_spec_db, cfg, output_folder, output_filename,(640,480))\n        # x = data_transforms['test'](pil_img).unsqueeze(0).to(cfg.DEVICE) # (1, C, H, W)\n        # Model prediction\n        with torch.no_grad():\n            logits = model(x)  \n            scores = torch.sigmoid(logits)  \n            scores = scores.squeeze(0).cpu().numpy()  \n        saved_data.append([row_id]+ list(scores))\n    return saved_data   \n","metadata":{"execution":{"iopub.status.busy":"2025-05-16T08:36:07.630317Z","iopub.execute_input":"2025-05-16T08:36:07.630662Z","iopub.status.idle":"2025-05-16T08:36:07.652770Z","shell.execute_reply.started":"2025-05-16T08:36:07.630638Z","shell.execute_reply":"2025-05-16T08:36:07.651816Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Submmission","metadata":{}},{"cell_type":"code","source":"# =====================Load model ===================\nmodel = timm.create_model(\"efficientnet_b0\", pretrained=False, num_classes=cfg.NUM_CLASSES)\nmodel.load_state_dict(torch.load(str(cfg.MODEL_PATH), map_location=cfg.DEVICE))\nmodel.to(cfg.DEVICE)\nmodel.eval()\n","metadata":{"execution":{"iopub.status.busy":"2025-05-16T08:39:05.257039Z","iopub.execute_input":"2025-05-16T08:39:05.258056Z","iopub.status.idle":"2025-05-16T08:39:05.611613Z","shell.execute_reply.started":"2025-05-16T08:39:05.258020Z","shell.execute_reply":"2025-05-16T08:39:05.610657Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import time\n\n# Set seed\nnp.random.seed(cfg.SEED)\n# Prepare empty list for rows\nrows = []\n# sample submission:\nsubmission_example = pd.read_csv(cfg.SUBMISSION_SAMPLE_PATH)\n# Class labels from train audio\nclass_labels = sorted(cfg.CLASS_NAME)\n\nif cfg.USE_AUDIO_AS_INPUT:\n    # List of test soundscapes (only visible during submission)\n    test_soundscape_path = cfg.DATA_PATH\n    test_soundscapes = [os.path.join(test_soundscape_path, afile) for afile in sorted(os.listdir(test_soundscape_path)) if afile.endswith('.ogg')]\n    \n    # Open each soundscape and make predictions for 5-second segments\n    # Use pandas df with 'row_id' plus class labels as columns\n\n    for count,soundscape in enumerate(test_soundscapes):\n        print(\"[\",count+1, \"/\", len(test_soundscapes),\"] processing file:\",soundscape)\n        rows += process_audio_file(audio_path= Path(soundscape),\n                              model = model,\n                              output_folder = cfg.OUTPUT_FOLDER,\n                              cfg = cfg,\n                              overlap_percent = 0.0)\n\n\n# Build predictions DataFrame\npredictions = pd.DataFrame(rows, columns=['row_id'] + class_labels)\n\n# Now remap to submission columns\nsubmission_cols = submission_example.columns[1:]\n\n# Reindex predictions to match submission\nsubmission = predictions[['row_id'] + list(submission_cols)]\n# meta = predictions[['row_id','path']]\n# Smoothing for final submission\nif cfg.USE_SMOOTH_LABEL:\n    submission = smooth_label(submission)\nsubmission.to_csv('submission.csv', index=False)\n# meta.to_csv('metadata_trainSoundscape.csv', index=False)\n","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","execution":{"iopub.status.busy":"2025-05-16T08:39:54.797214Z","iopub.execute_input":"2025-05-16T08:39:54.797588Z","iopub.status.idle":"2025-05-16T08:39:54.847684Z","shell.execute_reply.started":"2025-05-16T08:39:54.797562Z","shell.execute_reply":"2025-05-16T08:39:54.846828Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission.head(5)\n","metadata":{"execution":{"iopub.status.busy":"2025-05-16T08:19:26.397079Z","iopub.status.idle":"2025-05-16T08:19:26.397427Z","shell.execute_reply.started":"2025-05-16T08:19:26.397302Z","shell.execute_reply":"2025-05-16T08:19:26.397317Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}