{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":70203,"databundleVersionId":8068726,"sourceType":"competition"},{"sourceId":287469238,"sourceType":"kernelVersion"},{"sourceId":3749,"sourceType":"modelInstanceVersion","modelInstanceId":2676,"modelId":314}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# <font color =red >**开始自己的数据处理流程**</font>","metadata":{}},{"cell_type":"markdown","source":"<font color =blue>注意：kaggle中：input可以直接导入官方的所有数据集，比赛数据集，以及别人公开的数据集.\nkaggle中也可以导入你自己写的其他notebook的输出。不过需要按右上角的Save Version才能把输出保存到notebook里，\n你在 Kaggle Notebook 里跑出来的文件（如 .pt、.csv、.npy）默认只是临时的，如果你不“保存版本”，这些文件不会真正存在于 Kaggle 的持久存储中，其他 Notebook 也根本看不到\n</font>\n\n而且：需要按右上角的Save Version才能把输出保存到notebook里，已经交互式运行完的输出需要quick save并且要在advanced settings里调整到保存当前数据。","metadata":{}},{"cell_type":"code","source":"#import package\nimport os\nimport numpy as np\nimport pandas as pd\nimport random\n\n#torch related:\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torchvision\nimport torchvision.models as models\nimport torchaudio\nimport torchaudio.transforms as T\nfrom torch.optim import Adam\n\n#audio related:\nimport librosa\nimport IPython.display as ipd\nimport librosa.display as lid\n\n#plot related:\nimport matplotlib as mpl\nimport matplotlib.pyplot as plt\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T13:33:50.127173Z","iopub.execute_input":"2025-12-22T13:33:50.127514Z","iopub.status.idle":"2025-12-22T13:33:57.857388Z","shell.execute_reply.started":"2025-12-22T13:33:50.127489Z","shell.execute_reply":"2025-12-22T13:33:57.856744Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#选择是否使用GPU加速\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\ndevice","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T13:33:57.858477Z","iopub.execute_input":"2025-12-22T13:33:57.858865Z","iopub.status.idle":"2025-12-22T13:33:57.919550Z","shell.execute_reply.started":"2025-12-22T13:33:57.858840Z","shell.execute_reply":"2025-12-22T13:33:57.918670Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 训练数据可视化","metadata":{}},{"cell_type":"code","source":"Base_dir_path = \"/kaggle/input/birdclef-2024\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T13:33:57.920349Z","iopub.execute_input":"2025-12-22T13:33:57.921415Z","iopub.status.idle":"2025-12-22T13:33:57.948291Z","shell.execute_reply.started":"2025-12-22T13:33:57.921384Z","shell.execute_reply":"2025-12-22T13:33:57.947551Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#train_meta数据参考：\ntrain_meta_example = pd.read_csv(Base_dir_path + \"/train_metadata.csv\")\ntrain_meta_example.head(3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T13:33:57.950209Z","iopub.execute_input":"2025-12-22T13:33:57.950573Z","iopub.status.idle":"2025-12-22T13:33:58.096998Z","shell.execute_reply.started":"2025-12-22T13:33:57.950555Z","shell.execute_reply":"2025-12-22T13:33:58.096416Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 样例提交数据可视化","metadata":{}},{"cell_type":"code","source":"#样例提交数据可视化：\ntest_submit_example = pd.read_csv(Base_dir_path + \"/sample_submission.csv\")\ntest_submit_example.head(1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T13:33:58.097945Z","iopub.execute_input":"2025-12-22T13:33:58.098273Z","iopub.status.idle":"2025-12-22T13:33:58.121697Z","shell.execute_reply.started":"2025-12-22T13:33:58.098247Z","shell.execute_reply":"2025-12-22T13:33:58.121133Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 可视化数据总结：\n由此可见，最后我们需要输出的数据表格如上图所示,最后一共有多少测试数据，则我们的输出的dataframe就应该有多少行\n\n除了第一列代表:\n\nsoundscape_[soundscape_id]_[end_time]\n\n其余别的列代表：\n\n每一行预测每种鸟类出现的概率","metadata":{}},{"cell_type":"markdown","source":"### <font color =red> 开始机器学习流程</font>\n\n一般来说：机器学习流程分为这几步：1.数据下载 2.数据清洗与加强 3.数据加载 4.模型建立 5.损失函数/优化器建立 6.训练 7.测试 8.保存模型 9.转化为Kaggle要求的输出数据格式 (以下就按照这几个数据流程来阐述这个project流程) 10.上传项目","metadata":{}},{"cell_type":"markdown","source":"# 1.数据下载\n\n由于在kaggle平台上，所以数据下载不需要自己手动进行下载，直接把kaggle的竞赛数据导入到项目的project即可\n处理方式：由于具有train-data的元数据：train_metadata，所以将train_metadata作为文件根基，加入对应文件路径，从而建立dataset","metadata":{}},{"cell_type":"code","source":"#listdir 实现将训练文件夹中所有文件列出并且排序:\nclass_names = sorted(os.listdir('/kaggle/input/birdclef-2024/train_audio/'))\n#len 输出多少类:\nnum_classes = len(class_names)\n#list(range(数量)) 创建一个从0-(数量-1)的列表:\nclass_labels = list(range(num_classes))\n\n#创建标签到类别的映射\nlabels_names = dict(zip(class_labels, class_names))\n#创建类别到标签的映射\nnames_labels = dict(zip(class_names , class_labels))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T13:33:58.122429Z","iopub.execute_input":"2025-12-22T13:33:58.122775Z","iopub.status.idle":"2025-12-22T13:33:58.130981Z","shell.execute_reply.started":"2025-12-22T13:33:58.122749Z","shell.execute_reply":"2025-12-22T13:33:58.130426Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#dataframe创建:(基于train_metadata)\ndf = pd.read_csv(Base_dir_path+\"/train_metadata.csv\")\nclass_size = df[\"primary_label\"].nunique()\nprint(f\"训练的种类一共有：{class_size} 种,训练数据一共有：{df.shape[0]}个\")\n\ndf[\"filepath\"]= Base_dir_path + \"/train_audio/\" + df[\"filename\"] \ndf[\"target\"] = df[\"primary_label\"].map(lambda x: names_labels[x])\n\n#取出3个数据进行可视化\ndf = df.fillna(\"NaN\")\ndf.iloc[[1,500,1000]]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T13:33:58.131756Z","iopub.execute_input":"2025-12-22T13:33:58.132005Z","iopub.status.idle":"2025-12-22T13:33:58.273815Z","shell.execute_reply.started":"2025-12-22T13:33:58.131987Z","shell.execute_reply":"2025-12-22T13:33:58.272910Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 2.数据清洗与加强","metadata":{}},{"cell_type":"code","source":"# 采用GPU进行加速:\n# 加载音频文件ogg→采样后的时域图\nsample_rate = 32000\nn_mels=256\nn_fft=2048\nhop_length=512\nfmax=16000\nfmin=20\ntarget_length = 256\n\ndef load_audio(filepath, sample_rate=sample_rate):\n    waveform, sr = torchaudio.load(filepath, normalize=True)\n    # 重采样到目标采样率（如果需要）\n    if sr != sample_rate:\n        resampler = torchaudio.transforms.Resample(orig_freq=sr, new_freq=sample_rate)\n        waveform = resampler(waveform)\n    sr = sample_rate\n    return waveform, sr\n\n#创建 Mel-Spectrogram 变换器\nmel_transform = T.MelSpectrogram(\n    sample_rate=sample_rate, #采样频率\n    n_mels=n_mels, #用 Mel 滤波器组把线性频率 bins 映射到 n_mels 个 mel 频带\n    n_fft=n_fft,\n    hop_length=hop_length,\n    f_max=fmax,\n    f_min=fmin\n).to(device)\n\n# 创建 log-mel Spectrogram 变换器\nmel_spec_db = torchaudio.transforms.AmplitudeToDB()\n\n# 函数: 时域图 → log-mel-spectrogram\ndef get_spectrogram(audio, sample_rate=32000,mel_spec_db=mel_spec_db):\n\n    audio = audio.to(device, non_blocking=True)\n    mel_spec = mel_transform(audio)\n\n    mel_spec_db = mel_spec_db(mel_spec)\n    \n    # 归一化\n    min_ = mel_spec_db.min()\n    max_ = mel_spec_db.max()\n    if max_ != min_:\n        mel_spec_db = (mel_spec_db - min_) / (max_ - min_)\n    \n    # 填充或裁剪到固定大小（例如，将所有 Mel-spectrogram 填充或裁剪为 1000 个时间步长）\n    current_length = mel_spec_db.shape[-1]\n    \n    if current_length < target_length:\n        # 填充\n        padding = target_length - current_length\n        mel_spec_db = F.pad(mel_spec_db, (0, padding))  # 填充到 target_length\n    elif current_length > target_length:\n        # # 取前裁剪\n        mel_spec_db = mel_spec_db[:, :, :target_length]\n        # # 随机裁剪\n        # start_frame = random.randint(0, current_length - target_length)\n        # mel_spec_db = mel_spec_db[:, :, start_frame:start_frame + target_length]\n    \n    return mel_spec_db\n\n\n# 可视化音频波形和 Mel-spectrogram\ndef display_audio(row):\n    # Caption for viz\n    caption = f'Id: {row.filename} | Name: {row.common_name} | Sci.Name: {row.scientific_name} | Rating: {row.rating}'\n    \n    # 读取音频文件\n    audio, sr = load_audio(row.filepath)\n    \n    # 保证音频长度一致（15秒）\n    audio = audio[:, :15*32000]  # 只取前 15 秒音频\n    \n    # 生成 Mel-spectrogram\n    spec = get_spectrogram(audio, sample_rate=32000)\n    \n    # 显示音频\n    print(\"# Audio:\")\n    display(ipd.Audio(audio, rate=32000))\n    \n    # 可视化\n    print('# Visualization:')\n    fig, ax = plt.subplots(2, 1, figsize=(12, 6))\n    fig.suptitle(caption)\n    \n    # 绘制音频波形\n    ax[0].plot(audio.squeeze().numpy())\n    ax[0].set_ylabel(\"Amplitude\")\n    \n    # 绘制 Mel-spectrogram\n    im = ax[1].imshow(spec[0].cpu().numpy(), cmap='coolwarm', origin='lower', aspect='auto', interpolation='none')\n    ax[1].set_ylabel(\"Mel Frequency\")\n    ax[1].set_xlabel(\"Time (s)\")\n    \n    # 添加颜色条\n    fig.colorbar(im, ax=ax[1], format='%+2.0f dB')\n\n    # 显示图像\n    fig.show()\n\n    # 播放音频\n    display(ipd.Audio(audio, rate=32000))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T13:33:58.274904Z","iopub.execute_input":"2025-12-22T13:33:58.275219Z","iopub.status.idle":"2025-12-22T13:33:58.457237Z","shell.execute_reply.started":"2025-12-22T13:33:58.275195Z","shell.execute_reply":"2025-12-22T13:33:58.456561Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#test:\nget_spectrogram(load_audio(\"/kaggle/input/birdclef-2024/train_audio/asbfly/XC134896.ogg\")[0]).shape\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T13:33:58.458035Z","iopub.execute_input":"2025-12-22T13:33:58.458331Z","iopub.status.idle":"2025-12-22T13:33:59.404480Z","shell.execute_reply.started":"2025-12-22T13:33:58.458308Z","shell.execute_reply":"2025-12-22T13:33:59.403850Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#example1:\ndisplay_audio(df.iloc[1])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T13:33:59.406527Z","iopub.execute_input":"2025-12-22T13:33:59.407194Z","iopub.status.idle":"2025-12-22T13:34:00.120947Z","shell.execute_reply.started":"2025-12-22T13:33:59.407164Z","shell.execute_reply":"2025-12-22T13:34:00.120326Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#example2:\ndisplay_audio(df.iloc[100])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T13:34:00.121640Z","iopub.execute_input":"2025-12-22T13:34:00.121829Z","iopub.status.idle":"2025-12-22T13:34:00.678302Z","shell.execute_reply.started":"2025-12-22T13:34:00.121815Z","shell.execute_reply":"2025-12-22T13:34:00.677586Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#example3:\ndisplay_audio(df.iloc[500])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T13:34:00.678921Z","iopub.execute_input":"2025-12-22T13:34:00.679184Z","iopub.status.idle":"2025-12-22T13:34:01.258481Z","shell.execute_reply.started":"2025-12-22T13:34:00.679159Z","shell.execute_reply":"2025-12-22T13:34:01.257880Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 3.数据加载(original)","metadata":{}},{"cell_type":"code","source":"# from torch.utils.data import DataLoader, Dataset\n\n# class BirdDataset(Dataset):\n#     def __init__(self,train_meta_df, transform=None):\n#         self.train_meta_df = train_meta_df\n#         self.transform = transform\n    \n#     def __len__(self):\n#         return self.train_meta_df.shape[0]\n    \n#     def __getitem__(self, idx):\n#         file_name = self.train_meta_df.iloc[idx][\"filename\"]\n#         audio_path = self.train_meta_df.iloc[idx][\"filepath\"]\n        \n#         audio, sr = load_audio(audio_path)\n#         audio = audio[:, :15*32000]  # 只取前 15 秒音频\n#         audio_img = get_spectrogram(audio, sample_rate=32000)\n        \n#         if self.transform:\n#             audio_img = self.transform(audio_img)\n\n#         label = self.train_meta_df.iloc[idx][\"target\"]\n        \n#         return audio_img, label\n\n\n# train_dataset = BirdDataset(train_meta_df=df)\n# train_loader = DataLoader(train_dataset, \n#                           batch_size=128, \n#                           shuffle=True,\n#                           # num_workers=4,  # 根据CPU核心数调整，通常设置为CPU核心数\n#                           pin_memory=True,\n#                          ) # 加速CPU到GPU的数据传输","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T13:34:01.259200Z","iopub.execute_input":"2025-12-22T13:34:01.259466Z","iopub.status.idle":"2025-12-22T13:34:01.263776Z","shell.execute_reply.started":"2025-12-22T13:34:01.259449Z","shell.execute_reply":"2025-12-22T13:34:01.263016Z"},"jupyter":{"source_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 3.数据加载(finally1)","metadata":{}},{"cell_type":"code","source":"# #采用.pt格式分别存储:\n\n# def save_spectrograms_as_tensors(meta_df, output_dir, sample_rate=32000):\n#     # 创建输出目录\n#     os.makedirs(output_dir, exist_ok=True)\n    \n#     unique_labels = meta_df['target'].unique()\n    \n#     for label in unique_labels:\n#         os.makedirs(os.path.join(output_dir, labels_names[label]), exist_ok=True)\n        \n#     for idx, row in meta_df.iterrows():\n#         audio_path = row['filepath']\n#         label = labels_names[row['target']]\n#         #basename 返回path的最后一级文件名称\n#         #splittext根据.来进行分割,分割成列表: 例: ('XC134896', '.ogg')\n#         filename = os.path.splitext(os.path.basename(audio_path))[0]\n        \n#         # 生成图像保存路径 (保存在对应标签的子文件夹下)\n#         tensor_filename = f\"{filename}.pt\"\n#         tensor_save_path = os.path.join(output_dir, str(label), tensor_filename)\n        \n#         # 如果图像已存在，则跳过\n#         if os.path.exists(tensor_save_path):\n#             continue\n        \n#         # 加载音频并生成频谱图 (使用你已有的函数)\n#         audio, sr = load_audio(audio_path, sample_rate)\n#         audio = audio[:, :15*sample_rate]\n#         spectrogram_tensor = get_spectrogram(audio, sample_rate)\n        \n#         torch.save(spectrogram_tensor, tensor_save_path)\n        \n        \n#         if idx % 100 == 0:\n#             print(f\"已处理 {idx}/{len(meta_df)} 个文件\")\n                \n    \n#     print(\"所有音频文件已成功转换为图像！\")\n\n# # 使用示例\n# save_spectrograms_as_tensors(df, '/kaggle/working/spectrogram_tensors')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T13:34:01.264548Z","iopub.execute_input":"2025-12-22T13:34:01.264858Z","iopub.status.idle":"2025-12-22T13:34:01.280441Z","shell.execute_reply.started":"2025-12-22T13:34:01.264835Z","shell.execute_reply":"2025-12-22T13:34:01.279758Z"},"_kg_hide-input":false,"jupyter":{"source_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# from torch.utils.data import DataLoader, Dataset\n\n# #数据集建立\n# class PrecomputedTensorDataset(Dataset):\n#     def __init__(self, meta_df, tensor_base_dir, transform=None):\n#         self.meta_df = meta_df\n#         self.tensor_base_dir = tensor_base_dir\n#         self.transform = transform\n        \n#         # 验证文件是否存在\n#         self.valid_samples = []\n#         for idx, row in meta_df.iterrows():\n#             file_path = df.iloc[idx][\"filepath\"]\n#             filename = os.path.splitext(os.path.basename(file_path))[0]\n#             label = row['target']\n#             tensor_path = os.path.join(tensor_base_dir, labels_names[label], f\"{filename}.pt\")\n            \n#             if os.path.exists(tensor_path):\n#                 self.valid_samples.append((tensor_path, label))\n#             else:\n#                 print(f\"警告: Tensor文件不存在 {tensor_path}\")\n    \n#     def __len__(self):\n#         return len(self.valid_samples)\n    \n#     def __getitem__(self, idx):\n#         tensor_path, label = self.valid_samples[idx]\n        \n#         # 直接加载预计算的Tensor\n#         spectrogram_tensor = torch.load(tensor_path)\n        \n#         if self.transform:\n#             spectrogram_tensor = self.transform(spectrogram_tensor)\n        \n#         return spectrogram_tensor, label\n        \n# # 使用示例\n# train_dataset = PrecomputedTensorDataset(\n#     meta_df=df,\n#     tensor_base_dir='/kaggle/working/spectrogram_tensors',\n#     transform=None\n# )\n# train_loader = DataLoader(train_dataset, \n#                           batch_size=64, \n#                           shuffle=True,\n#                          )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T13:34:01.281201Z","iopub.execute_input":"2025-12-22T13:34:01.281427Z","iopub.status.idle":"2025-12-22T13:34:01.297710Z","shell.execute_reply.started":"2025-12-22T13:34:01.281407Z","shell.execute_reply":"2025-12-22T13:34:01.297106Z"},"jupyter":{"source_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 3.数据加载(finally2)","metadata":{}},{"cell_type":"code","source":"#采用numpy分X,Y,ids分别存储,然后压缩成numpy的压缩包形式:\n\ndef save_spectrograms_to_one_npz(meta_df, output_path, sample_rate=32000, dtype=np.float16):\n    N = meta_df.shape[0]\n    ids = []\n    y = np.empty((N,), dtype=np.int64)\n\n    # 先算第 1 条确定形状\n    first_row = meta_df.iloc[0]\n    audio, _ = load_audio(first_row[\"filepath\"], sample_rate)\n    audio = audio[:, :15 * sample_rate]\n    spec = get_spectrogram(audio, sample_rate)\n    spec_np = spec.detach().cpu().numpy().astype(dtype)\n\n    X = np.empty((N,) + spec_np.shape, dtype=dtype)\n    X[0] = spec_np\n\n    filename0 = os.path.splitext(os.path.basename(first_row[\"filepath\"]))[0]\n    ids.append(filename0)\n    y[0] = int(first_row[\"target\"])\n\n    for i in range(1, N):\n        row = meta_df.iloc[i]\n        audio_path = row[\"filepath\"]\n        audio, _ = load_audio(audio_path, sample_rate)\n        audio = audio[:, :15 * sample_rate]\n        spec = get_spectrogram(audio, sample_rate)\n        X[i] = spec.detach().cpu().numpy().astype(dtype)\n\n        filename = os.path.splitext(os.path.basename(audio_path))[0]\n        ids.append(filename)\n        y[i] = int(row[\"target\"])\n\n        if i % 100 == 0:\n            print(f\"已处理 {i}/{N} 个文件\")\n\n    os.makedirs(os.path.dirname(output_path), exist_ok=True)\n    np.savez_compressed(\n        output_path,\n        X=X,\n        y=y,\n        ids=np.array(ids, dtype=object)\n    )\n    print(f\"保存完成：{output_path}\")\n\n\nsave_spectrograms_to_one_npz(df, '/kaggle/working/spectrogram_tensors')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T13:34:01.298494Z","iopub.execute_input":"2025-12-22T13:34:01.298734Z","iopub.status.idle":"2025-12-22T13:55:26.477220Z","shell.execute_reply.started":"2025-12-22T13:34:01.298718Z","shell.execute_reply":"2025-12-22T13:55:26.476486Z"},"_kg_hide-output":false,"_kg_hide-input":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import DataLoader, Dataset\nfrom torchvision import transforms\nimport torchaudio.transforms as T\n\n#数据集建立\nclass PrecomputedTensorDataset(Dataset):\n    def __init__(self, meta_df, tensor_base_dir, transform=None):\n        self.meta_df = meta_df\n        self.tensor_base_dir = tensor_base_dir\n        self.transform = transform\n        self.data = np.load(\"/kaggle/working/spectrogram_tensors.npz\",allow_pickle=True)\n\n        self.X = torch.from_numpy(self.data[\"X\"]).float()  # 与 numpy 共享内存（CPU）\n        self.y = torch.from_numpy(self.data[\"y\"]).long()   # label 用 long\n        self.ids = self.data[\"ids\"]\n        \n        \n    def __len__(self):\n        return self.X.shape[0]\n    \n    def __getitem__(self, idx):\n        spectrogram_tensor, label = self.X[idx],self.y[idx]\n        \n        if self.transform:\n            spectrogram_tensor = self.transform(spectrogram_tensor)\n        \n        return spectrogram_tensor, label\n\ntime_masking = T.TimeMasking(time_mask_param=25)\nfreq_masking = T.FrequencyMasking(freq_mask_param=20)\n\ndef transform(spec):\n    spec = time_masking(spec)\n    spec = freq_masking(spec)\n    return spec\n\n# 使用示例\ntrain_dataset = PrecomputedTensorDataset(\n    meta_df=df,\n    tensor_base_dir='/kaggle/working/spectrogram_tensors',\n    transform=transform\n)\nif device != \"cpu\":\n    num_workers = 4\n    pin_memory = True\nelse:\n    num_workers = None\n    pin_memory = None\n\ntrain_loader = DataLoader(train_dataset, \n                          batch_size=64, \n                          shuffle=True,\n                          num_workers = num_workers,\n                          pin_memory = pin_memory\n                         )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T13:55:26.478148Z","iopub.execute_input":"2025-12-22T13:55:26.478778Z","iopub.status.idle":"2025-12-22T13:55:50.506444Z","shell.execute_reply.started":"2025-12-22T13:55:26.478757Z","shell.execute_reply":"2025-12-22T13:55:50.505839Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 4.模型建立","metadata":{}},{"cell_type":"code","source":"# 加载 MobileNetV2 模型\n# model = models.mobilenet_v3_small(weights=None)\nmodel = models.mobilenet_v3_small(weights=torchvision.models.mobilenetv3.MobileNet_V3_Small_Weights.IMAGENET1K_V1)\n\n# 修改 Mobilenet_V3 的第一层卷积层\nmodel.features[0][0] = torch.nn.Conv2d(1, 16, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n\n# 获取原分类器最后一层线性层的输入特征数\n\nin_features = model.classifier[-1].in_features\n\n# 替换为一个新的线性层，输出单元数为182\nmodel.classifier[-1] = nn.Linear(in_features, 182)\n\n# #加载预训练模型参数\n# weights = torch.load(\"/kaggle/input/tf-mobilenet-v3/pytorch/tf-mobilenetv3-small-100/1/tf_mobilenetv3_small_100-37f49e2b.pth\", map_location='cpu')\n# model.load_state_dict(weights,strict=False)\n\n\n# 转移到GPU（如果有的话）\nmodel = model.to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T13:55:50.507229Z","iopub.execute_input":"2025-12-22T13:55:50.507544Z","iopub.status.idle":"2025-12-22T13:55:50.837136Z","shell.execute_reply.started":"2025-12-22T13:55:50.507520Z","shell.execute_reply":"2025-12-22T13:55:50.836240Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(train_dataset[230])\nprint(train_dataset[230][0].shape)\nprint(train_dataset[230][0].dtype)\nprint(len(train_dataset))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T13:55:50.838714Z","iopub.execute_input":"2025-12-22T13:55:50.838927Z","iopub.status.idle":"2025-12-22T13:55:50.860571Z","shell.execute_reply.started":"2025-12-22T13:55:50.838910Z","shell.execute_reply":"2025-12-22T13:55:50.859784Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 5.损失函数 与 优化器定义","metadata":{}},{"cell_type":"code","source":"# 定义损失函数和优化器\ncriterion = nn.CrossEntropyLoss().to(device)\noptimizer = Adam(model.parameters(), lr=1e-3, weight_decay=1e-4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T13:55:50.861487Z","iopub.execute_input":"2025-12-22T13:55:50.861741Z","iopub.status.idle":"2025-12-22T13:55:50.866045Z","shell.execute_reply.started":"2025-12-22T13:55:50.861724Z","shell.execute_reply":"2025-12-22T13:55:50.865334Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 6.训练，保存\n由于kaggle的比赛需要，本比赛的测试集仅在提交之后会进行测试，因此这里不进行特别的测试。\n仅进行训练和模型的保存，并在最后实现对kaggle测试集的测试，从而输出kaggle比赛所需要的csv类型。","metadata":{}},{"cell_type":"code","source":"def Test_GPU(model,imgs,labels):\n    print(f\"模型参数所在设备: {next(model.parameters()).device}\")\n    print(f\"输入图像所在设备: {imgs.device}\")\n    print(f\"标签所在设备: {labels.device}\")\n    print(f\"CUDA是否可用: {torch.cuda.is_available()}\")\n    print(f\"“当前CUDA设备索引: {torch.cuda.current_device()}\")\n    print(f\"“设备名称: {torch.cuda.get_device_name(0)}\")\n\ndef Get_acc(labels,outputs)->float:\n    train_labels = labels.size(0)\n    correct_num = (outputs.argmax(1) == labels).sum()\n    acc = correct_num / train_labels\n    return acc * 100\n\nepochs = 100\nacc = 0.0\nagents_test_correct = 0\n\niter = 0\niter_list = []\ntrain_loss = []\ntrain_acc = []\nacc_mean_group = []\n\nfor epoch in range(epochs):\n    for data in train_loader:\n        iter += 1\n        model.train()\n        imgs,labels = data\n        imgs = imgs.to(device)\n        labels = labels.to(device)\n        optimizer.zero_grad()\n        outputs = model(imgs)\n        loss = criterion(outputs,labels)\n        loss.backward()\n        optimizer.step()\n        \n        if iter % 100 == 0:\n            mean = 0.0\n            print(f\"epoch:{epoch} iter:{iter} loss:{loss.item()}\")\n            acc = Get_acc(labels,outputs)\n            print(f\"epoch:{epoch} iter:{iter} acc:{acc:<.2f} %\")\n            mean = sum(acc_mean_group) / len(acc_mean_group)\n            if mean >= 80: break\n            acc_mean_group = []\n            \n        if iter % 10 == 0:\n            iter_list.append(iter)\n            train_loss.append(loss)\n            train_acc.append(acc)\n            acc_mean_group.append(acc)\n            \n    if mean >= 80: break\n            \n\ntorch.save(model.state_dict(), '/kaggle/working/best_model.pth')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T14:49:50.194187Z","iopub.execute_input":"2025-12-22T14:49:50.194917Z","iopub.status.idle":"2025-12-22T14:50:02.818995Z","shell.execute_reply.started":"2025-12-22T14:49:50.194884Z","shell.execute_reply":"2025-12-22T14:50:02.818205Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Finished!\ndf = pd.read_csv(\"/kaggle/input/birdclef-2024/sample_submission.csv\")\ndf","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T14:50:17.289287Z","iopub.execute_input":"2025-12-22T14:50:17.289588Z","iopub.status.idle":"2025-12-22T14:50:17.315942Z","shell.execute_reply.started":"2025-12-22T14:50:17.289562Z","shell.execute_reply":"2025-12-22T14:50:17.315161Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_loss = list(map(lambda x:x.detach().cpu(),train_loss))\ntrain_acc = list(map(lambda x:torch.tensor(x).detach().cpu(),train_acc))\nplt.plot(iter_list,train_acc)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T14:52:41.267433Z","iopub.execute_input":"2025-12-22T14:52:41.268108Z","iopub.status.idle":"2025-12-22T14:52:41.397063Z","shell.execute_reply.started":"2025-12-22T14:52:41.268076Z","shell.execute_reply":"2025-12-22T14:52:41.396458Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.plot(iter_list,train_loss)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-22T14:52:05.436614Z","iopub.execute_input":"2025-12-22T14:52:05.437399Z","iopub.status.idle":"2025-12-22T14:52:05.587875Z","shell.execute_reply.started":"2025-12-22T14:52:05.437372Z","shell.execute_reply":"2025-12-22T14:52:05.587155Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}