{"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":"none","dataSources":[{"sourceId":91844,"databundleVersionId":11361821,"sourceType":"competition"}],"dockerImageVersionId":31040,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport cv2\nimport math\nimport time\nimport librosa\nimport pandas as pd\nimport numpy as np\nfrom tqdm.notebook import tqdm\n\n\nimport torch\nimport warnings\nwarnings.filterwarnings(\"ignore\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-05-25T14:35:55.043917Z","iopub.execute_input":"2025-05-25T14:35:55.044268Z","iopub.status.idle":"2025-05-25T14:35:55.051262Z","shell.execute_reply.started":"2025-05-25T14:35:55.044244Z","shell.execute_reply":"2025-05-25T14:35:55.050177Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Config:\n \n    DEBUG_MODE = False\n    \n    OUTPUT_DIR = '/kaggle/working/'\n    DATA_ROOT = '/kaggle/input/birdclef-2025'\n    FS = 32000\n    \n    # Mel spectrogram parameters\n    N_FFT = 1024\n    HOP_LENGTH = 512\n    N_MELS = 256\n    FMIN = 50\n    FMAX = 14000\n    \n    TARGET_DURATION = 5.0\n    TARGET_SHAPE = (256, 256)  \n    \n    N_MAX = 50 if DEBUG_MODE else None  \n\nconfig = Config()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T14:35:56.803852Z","iopub.execute_input":"2025-05-25T14:35:56.804228Z","iopub.status.idle":"2025-05-25T14:35:56.810789Z","shell.execute_reply.started":"2025-05-25T14:35:56.804201Z","shell.execute_reply":"2025-05-25T14:35:56.809524Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nimport librosa\nimport random\n\n# ─── 1. 简单的纯 Python 随机增强函数 ───────────────────────────────────────────\ndef augment_audio_librosa(audio: np.ndarray, sr: int, target_len: int) -> np.ndarray:\n    # 1) 添加高斯噪声\n    if random.random() < 0.5:\n        noise_amp = np.random.uniform(0.001, 0.015)\n        audio = audio + noise_amp * np.random.randn(len(audio))\n    # 2) 随机时域拉伸（并补偿长度）\n    if random.random() < 0.3:\n        rate = np.random.uniform(0.8, 1.25)\n        audio = librosa.effects.time_stretch(audio, rate)\n        # 裁剪／填零回到 target_len\n        if len(audio) < target_len:\n            pad = target_len - len(audio)\n            audio = np.pad(audio, (pad//2, pad-pad//2), mode=\"constant\")\n        else:\n            start = (len(audio) - target_len)//2\n            audio = audio[start:start+target_len]\n    # 3) 随机音高偏移\n    if random.random() < 0.4:\n        n_steps = np.random.uniform(-4, 4)\n        audio = librosa.effects.pitch_shift(audio, sr, n_steps)\n    # 4) 随机时间平移（roll）\n    if random.random() < 0.4:\n        shift_amt = int(np.random.uniform(-0.5, 0.5) * len(audio))\n        audio = np.roll(audio, shift_amt)\n    return audio","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T14:35:57.774802Z","iopub.execute_input":"2025-05-25T14:35:57.775135Z","iopub.status.idle":"2025-05-25T14:35:57.785111Z","shell.execute_reply.started":"2025-05-25T14:35:57.775113Z","shell.execute_reply":"2025-05-25T14:35:57.783923Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(f\"Debug mode: {'ON' if config.DEBUG_MODE else 'OFF'}\")\nprint(f\"Max samples to process: {config.N_MAX if config.N_MAX is not None else 'ALL'}\")\n\nprint(\"Loading taxonomy data...\")\ntaxonomy_df = pd.read_csv(f'{config.DATA_ROOT}/taxonomy.csv')\nspecies_class_map = dict(zip(taxonomy_df['primary_label'], taxonomy_df['class_name']))\n\nprint(\"Loading training metadata...\")\ntrain_df = pd.read_csv(f'{config.DATA_ROOT}/train.csv')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T14:35:59.554556Z","iopub.execute_input":"2025-05-25T14:35:59.555275Z","iopub.status.idle":"2025-05-25T14:35:59.844692Z","shell.execute_reply.started":"2025-05-25T14:35:59.555246Z","shell.execute_reply":"2025-05-25T14:35:59.843658Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"label_list = sorted(train_df['primary_label'].unique())\nlabel_id_list = list(range(len(label_list)))\nlabel2id = dict(zip(label_list, label_id_list))\nid2label = dict(zip(label_id_list, label_list))\n\nprint(f'Found {len(label_list)} unique species')\nworking_df = train_df[['primary_label', 'rating', 'filename']].copy()\nworking_df['target'] = working_df.primary_label.map(label2id)\nworking_df['filepath'] = config.DATA_ROOT + '/train_audio/' + working_df.filename\nworking_df['samplename'] = working_df.filename.map(lambda x: x.split('/')[0] + '-' + x.split('/')[-1].split('.')[0])\nworking_df['class'] = working_df.primary_label.map(lambda x: species_class_map.get(x, 'Unknown'))\ntotal_samples = min(len(working_df), config.N_MAX or len(working_df))\nprint(f'Total samples to process: {total_samples} out of {len(working_df)} available')\nprint(f'Samples by class:')\nprint(working_df['class'].value_counts())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T14:36:03.219248Z","iopub.execute_input":"2025-05-25T14:36:03.219640Z","iopub.status.idle":"2025-05-25T14:36:03.301428Z","shell.execute_reply.started":"2025-05-25T14:36:03.219616Z","shell.execute_reply":"2025-05-25T14:36:03.300185Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def audio2melspec(audio_data):\n    if np.isnan(audio_data).any():\n        mean_signal = np.nanmean(audio_data)\n        audio_data = np.nan_to_num(audio_data, nan=mean_signal)\n\n    mel_spec = librosa.feature.melspectrogram(\n        y=audio_data,\n        sr=config.FS,\n        n_fft=config.N_FFT,\n        hop_length=config.HOP_LENGTH,\n        n_mels=config.N_MELS,\n        fmin=config.FMIN,\n        fmax=config.FMAX,\n        power=2.0\n    )\n\n    mel_spec_db = librosa.power_to_db(mel_spec, ref=np.max)\n    mel_spec_norm = (mel_spec_db - mel_spec_db.min()) / (mel_spec_db.max() - mel_spec_db.min() + 1e-8)\n    \n    return mel_spec_norm","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T14:36:17.743176Z","iopub.execute_input":"2025-05-25T14:36:17.743595Z","iopub.status.idle":"2025-05-25T14:36:17.751452Z","shell.execute_reply.started":"2025-05-25T14:36:17.743567Z","shell.execute_reply":"2025-05-25T14:36:17.750357Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from concurrent.futures import ProcessPoolExecutor, as_completed\nimport multiprocessing\n\ndef process_row(row_dict):\n    \"\"\"\n    1) 读取音频并截取中心固定长度\n    2) 随机增强（纯 Python）\n    3) 生成 Mel 频谱\n    4) 返回 name, mel_spec\n    \"\"\"\n    try:\n        # 动态导入保持一致\n        import numpy as np, librosa, os\n        from types import SimpleNamespace\n\n        row = SimpleNamespace(**row_dict)\n        audio, _ = librosa.load(row.filepath, sr=config.FS)\n\n        # 中心截取或填零\n        tgt_len = int(config.TARGET_DURATION * config.FS)\n        if len(audio) < tgt_len:\n            pad = tgt_len - len(audio)\n            audio = np.pad(audio, (pad//2, pad-pad//2), mode='constant')\n        else:\n            start = (len(audio) - tgt_len)//2\n            audio = audio[start:start+tgt_len]\n\n        # 随机增强\n        if config.AUGMENT:\n            audio = augment_audio_librosa(audio, sr=config.FS, target_len=tgt_len)\n\n        # Mel 频谱\n        mel_spec = audio2melspec(\n            audio,\n            sr=config.FS,\n            n_mels=config.N_MELS,\n            hop_length=config.HOP_LENGTH,\n            win_length=config.WIN_LENGTH\n        )\n\n        name = getattr(row, 'id', None) or os.path.basename(row.filepath)\n        return name, mel_spec\n\n    except Exception as e:\n        name = row_dict.get('id', row_dict.get('filepath', 'unknown'))\n        return name, f\"ERROR {e}\"\n        \n# 启动处理\nprint(\"Starting parallel audio processing...\")\nstart_time = time.time()\n\nfrom types import SimpleNamespace\nall_bird_data = {}\nerrors = []\n\n# 转为 dict 传递到多进程（DataFrame 行不能直接传）\nrows = working_df.head(config.N_MAX or len(working_df)).to_dict(orient='records')\n\nwith ProcessPoolExecutor(max_workers=4) as executor:\n    futures = [executor.submit(process_row, row) for row in rows]\n    for future in tqdm(as_completed(futures), total=len(rows)):\n        name, result = future.result()\n        if isinstance(result, str) and result.startswith(\"ERROR\"):\n            errors.append((name, result))\n        else:\n            all_bird_data[name] = result\n\nend_time = time.time()\nprint(f\"✅ Done! Time: {end_time - start_time:.2f}s\")\nprint(f\"✅ Success: {len(all_bird_data)}\")\nprint(f\"❌ Errors: {len(errors)}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T14:36:19.474385Z","iopub.execute_input":"2025-05-25T14:36:19.474719Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\nsamples = []\ndisplayed_classes = set()\n\nmax_samples = min(4, len(all_bird_data))\n\nfor i, row in working_df.iterrows():\n    if i >= (config.N_MAX or len(working_df)):\n        break\n        \n    if row['samplename'] in all_bird_data:\n        if config.DEBUG_MODE:\n            if row['class'] not in displayed_classes:\n                samples.append((row['samplename'], row['class'], row['primary_label']))\n                displayed_classes.add(row['class'])\n        else:\n            if row['class'] not in displayed_classes:\n                samples.append((row['samplename'], row['class'], row['primary_label']))\n                displayed_classes.add(row['class'])\n        \n        if len(samples) >= max_samples:  \n            break\n\nif samples:\n    plt.figure(figsize=(16, 12))\n    \n    for i, (samplename, class_name, species) in enumerate(samples):\n        plt.subplot(2, 2, i+1)\n        plt.imshow(all_bird_data[samplename], aspect='auto', origin='lower', cmap='viridis')\n        plt.title(f\"{class_name}: {species}\")\n        plt.colorbar(format='%+2.0f dB')\n    \n    plt.tight_layout()\n    debug_note = \"debug_\" if config.DEBUG_MODE else \"\"\n    plt.savefig(f'{debug_note}melspec_examples.png')\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-23T12:26:50.311900Z","iopub.execute_input":"2025-05-23T12:26:50.312301Z","iopub.status.idle":"2025-05-23T12:26:54.115765Z","shell.execute_reply.started":"2025-05-23T12:26:50.312268Z","shell.execute_reply":"2025-05-23T12:26:54.114583Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\n\nnp.save('/kaggle/working/all_bird_data.npy', all_bird_data)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-23T12:28:52.635914Z","iopub.execute_input":"2025-05-23T12:28:52.636840Z","iopub.status.idle":"2025-05-23T12:29:20.497622Z","shell.execute_reply.started":"2025-05-23T12:28:52.636807Z","shell.execute_reply":"2025-05-23T12:29:20.495903Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}