{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":3004,"databundleVersionId":861823}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# 环境设置代码","metadata":{}},{"cell_type":"markdown","source":"我自己导入这么多库，，也是看到了不同的库有不同的作用，有数据处理的库，有神经网络专用的库，知道怎么用就可以。这块我基本上都理解了。\n","metadata":{}},{"cell_type":"code","source":"# ===================== 导入需要的库（带详细注释）=====================\nimport torch  # PyTorch 核心库：张量计算、自动求导、模型训练\nimport pandas as pd  # 数据处理库：读取表格、数据清洗\nimport numpy as np  # 数值计算库：数组、数学运算\nimport matplotlib.pyplot as plt  # 绘图库：画损失曲线、显示图片\n\n# PyTorch 神经网络专用模块\nimport torch.nn as nn  # 构建网络层（线性层、卷积层等）\nimport torch.nn.functional as F  # 激活函数、池化等函数\nimport torch.optim as optim  # 优化器：SGD、Adam 等\n\n# 数据集 + 数据加载器\nfrom torchvision import datasets  # 内置经典数据集（MNIST、CIFAR10）\nfrom torchvision import transforms  # 图像预处理：归一化、resize\nfrom torch.utils.data import Dataset, DataLoader  # 自定义数据集 + 批量加载\n\n# 工具库\nimport os  # 文件路径管理\nimport time  # 计时\nimport datetime  # 时间记录\nfrom tqdm.auto import tqdm  # 训练进度条（非常好用）\n\n# 忽略不影响运行的警告\nimport warnings\nwarnings.filterwarnings('ignore', category=UserWarning)\nwarnings.filterwarnings('ignore', category=DeprecationWarning)","metadata":{"_kg_hide-input":false,"_kg_hide-output":true,"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T02:18:04.230134Z","iopub.execute_input":"2026-05-02T02:18:04.231061Z","iopub.status.idle":"2026-05-02T02:18:07.406694Z","shell.execute_reply.started":"2026-05-02T02:18:04.231022Z","shell.execute_reply":"2026-05-02T02:18:07.405760Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 读取数据","metadata":{}},{"cell_type":"code","source":"# ===================== Kaggle 正确数据路径 =====================\n# 这是 Kaggle 上 Digit Recognizer 比赛的标准路径\ninput_dir = '/kaggle/input/competitions/digit-recognizer'\n\n# 读取数据集\ntrain_df = pd.read_csv(os.path.join(input_dir, 'train.csv'))\ntest_df = pd.read_csv(os.path.join(input_dir, 'test.csv'))\n\n# 查看数据信息\nprint(\"Train shape:\", train_df.shape)\nprint(\"Test shape:\", test_df.shape)\n\n# 显示 16 张数字图片\nplt.figure(figsize=(6, 6))\n\nfor i in range(16):\n    # 取第 i 个样本\n    sample = train_df.iloc[i]\n    label = sample['label']\n    pixels = sample.drop('label').values.reshape(28, 28)\n    \n    plt.subplot(4, 4, i + 1)\n    plt.imshow(pixels, cmap='gray')\n    plt.title(f\"Label: {label}\", fontsize=8)\n    plt.axis('off')\n\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T02:18:07.408227Z","iopub.execute_input":"2026-05-02T02:18:07.408667Z","iopub.status.idle":"2026-05-02T02:18:10.885502Z","shell.execute_reply.started":"2026-05-02T02:18:07.408643Z","shell.execute_reply":"2026-05-02T02:18:10.884358Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Check for the MNIST dataset\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_kg_hide-input":false,"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","trusted":true,"execution":{"iopub.status.busy":"2026-05-02T02:18:10.886789Z","iopub.execute_input":"2026-05-02T02:18:10.887529Z","iopub.status.idle":"2026-05-02T02:18:10.893138Z","shell.execute_reply.started":"2026-05-02T02:18:10.887501Z","shell.execute_reply":"2026-05-02T02:18:10.892434Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 读取设备","metadata":{}},{"cell_type":"markdown","source":"我第一次使用GPU，感觉还不错。感觉这种简单的数据，使用CPU或者GPU差不多，数据量比较少，用的时间差不多。不建议大家用GPU，感觉没必要\n\n","metadata":{}},{"cell_type":"code","source":"# 检查 GPU 信息（跨平台兼容）\nimport subprocess\ntry:\n    # 尝试获取 GPU 信息\n    result = subprocess.run(['nvidia-smi'], capture_output=True, text=True)\n    if result.returncode == 0:\n        print(\"=\"*80)\n        print(\"GPU 设备信息\")\n        print(\"=\"*80)\n        print(result.stdout)\n    else:\n        print(\"=\"*80)\n        print(\"GPU 设备信息\")\n        print(\"=\"*80)\n        print(\"未检测到 NVIDIA GPU 或 nvidia-smi 命令不可用\")\n        print(f\"当前设备: {'GPU (CUDA)' if torch.cuda.is_available() else 'CPU'}\")\nexcept FileNotFoundError:\n    print(\"=\"*80)\n    print(\"GPU 设备信息\")\n    print(\"=\"*80)\n    print(\"nvidia-smi 命令不可用（可能未安装 NVIDIA 驱动）\")\n    print(f\"当前设备: {'GPU (CUDA)' if torch.cuda.is_available() else 'CPU'}\")\nexcept Exception as e:\n    print(f\"获取 GPU 信息时出错: {e}\")\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint(\"当前运行设备:\", device)","metadata":{"trusted":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2026-05-02T02:18:10.894756Z","iopub.execute_input":"2026-05-02T02:18:10.895371Z","iopub.status.idle":"2026-05-02T02:18:11.286862Z","shell.execute_reply.started":"2026-05-02T02:18:10.895345Z","shell.execute_reply":"2026-05-02T02:18:11.285979Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!nvidia-smi","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T02:18:11.287986Z","iopub.execute_input":"2026-05-02T02:18:11.288540Z","iopub.status.idle":"2026-05-02T02:18:11.741517Z","shell.execute_reply.started":"2026-05-02T02:18:11.288514Z","shell.execute_reply":"2026-05-02T02:18:11.740797Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 加载数据","metadata":{}},{"cell_type":"markdown","source":"学习到数据加载器，比如就像打饭阿姨，把数据喂给我的模型","metadata":{}},{"cell_type":"code","source":"# ---------------------- 路径配置 ----------------------\n# 定义数据存放的文件夹路径（Kaggle 比赛标准路径）\ninput_dir = '/kaggle/input/competitions/digit-recognizer'\n\n# 打印提示信息：当前目录文件列表\nprint(\"当前目录文件列表：\")\n\n# 列出指定文件夹下的所有文件和子文件夹，用于确认数据是否存在\nprint(os.listdir(input_dir))\n\n# ---------------------- 自定义数据集类 ----------------------\n# 继承 PyTorch 的 Dataset，创建自定义的手写数字数据集类\nclass DigitDataset(Dataset):\n    \n    # 初始化函数：创建数据集时自动调用\n    # df：传入的 pandas 数据框（csv 读取后的数据）\n    # transform：图像预处理方法\n    # is_test：是否为测试集（测试集没有标签）\n    def __init__(self, df, transform=None, is_test=False):\n        # 把传入的数据保存到类内部变量\n        self.df = df\n        # 保存预处理方法\n        self.transform = transform\n        # 标记是否是测试集\n        self.is_test = is_test\n        \n    # 返回数据集的总长度（多少张图片）\n    def __len__(self):\n        return len(self.df)\n    \n    # 根据索引 idx 获取一条数据（一张图片 + 标签）\n    def __getitem__(self, idx):\n        # 如果是测试集：没有标签，只取像素\n        if self.is_test:\n            # 取出第 idx 行所有数据 → 转为数组 →  reshape 成 28x28x1 的图片格式 → 转为浮点型\n            image = self.df.iloc[idx].values.reshape(28, 28, 1).astype(np.float32)\n        # 如果是训练/验证集：有标签，第一列是标签，后面是像素\n        else:\n            # 取出第 idx 行第 0 列：标签（数字 0-9）\n            label = self.df.iloc[idx, 0]\n            # 取出第 idx 行第 1 列到最后：像素数据 → reshape 成 28x28x1 → 转为浮点型\n            image = self.df.iloc[idx, 1:].values.reshape(28, 28, 1).astype(np.float32)\n        \n        # 如果定义了预处理方法，就对图片进行处理\n        if self.transform:\n            image = self.transform(image)\n        \n        # 测试集只返回图片\n        if self.is_test:\n            return image\n        # 训练/验证集返回 图片 + 标签\n        return image, label\n\n# ========== 关键修改：训练集、测试集使用不同预处理 ==========\n# 训练集：数据增强 + 标准化，提高泛化\ntrain_transform = transforms.Compose([\n    transforms.ToTensor(),                   \n    transforms.RandomRotation(20),          # 随机旋转 ±20度\n    transforms.RandomAffine(degrees=0, translate=(0.1, 0.1), scale=(0.9, 1.1)),  # 平移和缩放\n    transforms.RandomPerspective(distortion_scale=0.15, p=0.3),  # 透视变换\n    transforms.Normalize((0.1307,), (0.3081,))  \n])\n\n# 测试集/验证集：只归一化，不做任何随机变化\ntest_transform = transforms.Compose([\n    transforms.ToTensor(),\n\n    transforms.Normalize((0.1307,), (0.3081,))\n])\n\n# 划分训练、验证\ntrain_data = train_df.sample(frac=0.8, random_state=42)\nval_data = train_df.drop(train_data.index)\n\n# 绑定不同预处理\ntrain_dataset = DigitDataset(train_data, train_transform)\nval_dataset = DigitDataset(val_data, test_transform)\ntest_dataset = DigitDataset(test_df, test_transform, is_test=True)\n\n# 数据加载器\ntrain_loader = DataLoader(train_dataset, batch_size=128, shuffle=True)  # 训练打乱\nval_loader = DataLoader(val_dataset, batch_size=256, shuffle=False)   # 验证不打乱\ntest_loader = DataLoader(test_dataset, batch_size=256, shuffle=False)  # 测试不打乱\n\nprint(f\"训练集:{len(train_dataset)} 验证集:{len(val_dataset)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T02:18:11.743061Z","iopub.execute_input":"2026-05-02T02:18:11.743321Z","iopub.status.idle":"2026-05-02T02:18:11.949214Z","shell.execute_reply.started":"2026-05-02T02:18:11.743296Z","shell.execute_reply":"2026-05-02T02:18:11.948580Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 简单CNN网络","metadata":{}},{"cell_type":"code","source":"# 定义一个CNN卷积神经网络类，继承自nn.Module（PyTorch所有模型都必须继承这个）\nclass CNN_Net(nn.Module):\n    \n    def __init__(self):\n        super().__init__()\n        \n        # 优化：更深的网络 + 残差连接\n        \n        # 第一层：卷积块\n        self.conv1 = nn.Sequential(\n            nn.Conv2d(1, 32, 3, padding=1),\n            nn.BatchNorm2d(32),\n            nn.ReLU(),\n            nn.Conv2d(32, 32, 3, padding=1),\n            nn.BatchNorm2d(32),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n            nn.Dropout2d(0.25)\n        )\n        \n        # 第二层：卷积块\n        self.conv2 = nn.Sequential(\n            nn.Conv2d(32, 64, 3, padding=1),\n            nn.BatchNorm2d(64),\n            nn.ReLU(),\n            nn.Conv2d(64, 64, 3, padding=1),\n            nn.BatchNorm2d(64),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n            nn.Dropout2d(0.25)\n        )\n        \n        # 第三层：卷积块\n        self.conv3 = nn.Sequential(\n            nn.Conv2d(64, 128, 3, padding=1),\n            nn.BatchNorm2d(128),\n            nn.ReLU(),\n            nn.Conv2d(128, 128, 3, padding=1),\n            nn.BatchNorm2d(128),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n            nn.Dropout2d(0.25)\n        )\n        \n        # 全连接层\n        self.fc = nn.Sequential(\n            nn.Flatten(),\n            nn.Linear(128 * 3 * 3, 256),\n            nn.BatchNorm1d(256),\n            nn.ReLU(),\n            nn.Dropout(0.5),\n            nn.Linear(256, 128),\n            nn.ReLU(),\n            nn.Dropout(0.3),\n            nn.Linear(128, 10)\n        )\n    \n    def forward(self, x):\n        x = self.conv1(x)\n        x = self.conv2(x)\n        x = self.conv3(x)\n        x = self.fc(x)\n        return x\n\n\n# ==================== 模型、损失函数、优化器 ====================\n# 创建模型实例，并把模型放到设备上（GPU/CPU）\nmodel = CNN_Net().to(device)\n\n# 定义损失函数：交叉熵损失，专门用于分类任务\ncriterion = nn.CrossEntropyLoss(label_smoothing=0.1)  # Label Smoothing\n\n# 定义优化器：Adam优化器，更新模型参数\n# lr=1e-3：学习率，控制参数更新的步长\noptimizer = torch.optim.Adam(model.parameters(), lr=1e-3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T02:18:11.950305Z","iopub.execute_input":"2026-05-02T02:18:11.950659Z","iopub.status.idle":"2026-05-02T02:18:12.220998Z","shell.execute_reply.started":"2026-05-02T02:18:11.950622Z","shell.execute_reply":"2026-05-02T02:18:12.220098Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 损失函数","metadata":{}},{"cell_type":"code","source":"criterion = nn.CrossEntropyLoss(label_smoothing=0.1)  # Label Smoothing\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T02:18:12.222091Z","iopub.execute_input":"2026-05-02T02:18:12.222743Z","iopub.status.idle":"2026-05-02T02:18:12.226574Z","shell.execute_reply.started":"2026-05-02T02:18:12.222711Z","shell.execute_reply":"2026-05-02T02:18:12.226005Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 优化器","metadata":{}},{"cell_type":"code","source":"optimizer = optim.Adam(model.parameters(), lr=1e-3)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T02:18:12.227546Z","iopub.execute_input":"2026-05-02T02:18:12.227906Z","iopub.status.idle":"2026-05-02T02:18:12.238311Z","shell.execute_reply.started":"2026-05-02T02:18:12.227876Z","shell.execute_reply":"2026-05-02T02:18:12.237733Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 训练函数","metadata":{}},{"cell_type":"code","source":"# 设置训练的总轮数\nn_epochs = 30  # 增加训练轮数\n\n# 创建空列表，用于存储每一轮的训练损失和验证准确率\ntrain_loss_curve = []\nval_acc_curve = []\n\n# 优化: 学习率调度器 - OneCycleLR\nscheduler = torch.optim.lr_scheduler.OneCycleLR(\n    optimizer, \n    max_lr=1e-3,\n    epochs=n_epochs,\n    steps_per_epoch=len(train_loader)\n)\n\n# 定义模型训练函数\ndef model_train():\n    # 将模型设置为训练模式\n    model.train()\n\n    # 记录训练开始的时间\n    start = time.time()\n\n    # 创建训练进度条\n    pbar = tqdm(range(1, n_epochs + 1))\n\n    # 外层循环：遍历每一个训练轮次\n    for epoch in pbar:\n        # 初始化当前轮次的总损失为0\n        train_loss = 0.0\n\n        # 内层循环：从数据加载器中批量读取图片和标签\n        for images, labels in train_loader:\n            # 将数据移动到指定设备\n            images, labels = images.to(device), labels.to(device)\n            \n            # 清空梯度\n            optimizer.zero_grad()\n            \n            # 前向传播\n            outputs = model(images)\n            \n            # 计算损失\n            loss = criterion(outputs, labels)\n            \n            # 反向传播\n            loss.backward()\n            \n            # 更新参数\n            optimizer.step()\n            scheduler.step()\n            \n            # 累加损失\n            train_loss += loss.item() * images.size(0)\n\n        # 计算当前轮次的平均损失\n        train_loss = train_loss / len(train_loader.dataset)\n        train_loss_curve.append(train_loss)\n        \n        # ========== 验证集评估 ==========\n        model.eval()\n        correct, total = 0, 0\n        with torch.no_grad():\n            for val_images, val_labels in val_loader:\n                val_images, val_labels = val_images.to(device), val_labels.to(device)\n                outputs = model(val_images)\n                _, predicted = torch.max(outputs, 1)\n                total += val_labels.size(0)\n                correct += (predicted == val_labels).sum().item()\n        val_acc = 100 * correct / total\n        val_acc_curve.append(val_acc)\n        model.train()\n        \n        # 更新进度条描述\n        pbar.set_description(f'Epoch {epoch}  损失:{train_loss:.4f}  验证:{val_acc:.2f}%')\n\n    # 记录训练结束的时间\n    end = time.time()\n    print(f\"\\n神经网络训练总耗时: {str(datetime.timedelta(seconds=round(end-start)))}\")\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T02:18:12.240550Z","iopub.execute_input":"2026-05-02T02:18:12.240876Z","iopub.status.idle":"2026-05-02T02:18:12.251623Z","shell.execute_reply.started":"2026-05-02T02:18:12.240812Z","shell.execute_reply":"2026-05-02T02:18:12.250878Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model_train()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T02:18:12.252463Z","iopub.execute_input":"2026-05-02T02:18:12.252758Z","iopub.status.idle":"2026-05-02T02:45:59.548821Z","shell.execute_reply.started":"2026-05-02T02:18:12.252732Z","shell.execute_reply":"2026-05-02T02:45:59.547914Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 损失图","metadata":{}},{"cell_type":"code","source":"plt.plot(train_loss_curve, linewidth=2)\nplt.grid(True)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T02:45:59.550306Z","iopub.execute_input":"2026-05-02T02:45:59.550686Z","iopub.status.idle":"2026-05-02T02:45:59.675455Z","shell.execute_reply.started":"2026-05-02T02:45:59.550645Z","shell.execute_reply":"2026-05-02T02:45:59.674727Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 测试准确性","metadata":{}},{"cell_type":"code","source":"# 定义模型评估函数（用于在验证集上测试模型准确率）\ndef model_eval():\n    # 将模型设置为评估模式（关闭dropout、batchnorm等训练时才用的层）\n    model.eval()\n    \n    # 初始化：预测正确的样本数量\n    correct = 0\n    \n    # 初始化：验证集的总样本数量\n    total = 0\n    \n    # 关闭梯度计算（评估时不需要更新参数，节省显存+加速）\n    with torch.no_grad():\n        # 遍历验证集的每一批数据（图片 + 标签）\n        for images, labels in val_loader:\n            # 将数据移动到 GPU/CPU 上\n            images, labels = images.to(device), labels.to(device)\n            \n            # 前向传播：得到模型输出\n            outputs = model(images)\n             \n            # 取出输出中概率最大的类别作为预测结果\n            # _ 表示忽略最大值本身，只取预测的类别索引\n            _, predicted = torch.max(outputs, 1)\n            \n            # 累加当前批次的总样本数\n            total += labels.size(0)\n            \n            # 统计预测正确的数量，并累加到correct中\n            correct += (predicted == labels).sum().item()\n\n    # 计算并打印验证集准确率（百分比形式，保留2位小数）\n    print(f'验证集 Accuracy: {100 * correct / total:.2f}%')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T02:45:59.676369Z","iopub.execute_input":"2026-05-02T02:45:59.676690Z","iopub.status.idle":"2026-05-02T02:45:59.683811Z","shell.execute_reply.started":"2026-05-02T02:45:59.676666Z","shell.execute_reply":"2026-05-02T02:45:59.682935Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model_eval()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T02:45:59.685095Z","iopub.execute_input":"2026-05-02T02:45:59.685437Z","iopub.status.idle":"2026-05-02T02:46:02.263953Z","shell.execute_reply.started":"2026-05-02T02:45:59.685412Z","shell.execute_reply":"2026-05-02T02:46:02.263076Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 定义生成比赛提交文件的函数（用于测试集预测并输出CSV）\ndef generate_submission():\n    # 将模型设置为评估模式，关闭训练时的层（如Dropout）\n    model.eval()\n    \n    # 创建空列表，用于保存所有测试集的预测结果\n    all_preds = []\n    \n    # 关闭梯度计算，节省显存、加速推理\n    with torch.no_grad():\n        # 遍历测试集数据加载器\n        # 测试集只有图片，没有标签，所以只接收 images\n        for images in test_loader:\n            # 将图片数据移动到指定设备（GPU/CPU）\n            images = images.to(device)\n            \n            # 前向传播，得到模型输出\n            outputs = model(images)\n            \n            # 获取预测结果：取输出中概率最大的类别\n            # _ 是最大概率值，predicted 是预测的数字类别（0-9）\n            _, predicted = torch.max(outputs, 1)\n            \n            # 将预测结果从GPU转回CPU，转成numpy格式，添加到总预测列表中\n            all_preds.extend(predicted.cpu().numpy())\n    \n    # 生成提交格式的DataFrame表格\n    submission_df = pd.DataFrame({\n        # 图片ID：从1到预测总数（比赛要求格式）\n        'ImageId': range(1, len(all_preds) + 1),\n        # 模型预测的标签（数字0-9）\n        'Label': all_preds\n    })\n    \n    # ===================== 已修改：Kaggle 输出路径 =====================\n    output_dir = '/kaggle/working'\n    os.makedirs(output_dir, exist_ok=True)\n    \n    # 构建提交文件路径\n    submission_path = os.path.join(output_dir, 'submission.csv')\n    \n    # 将DataFrame保存为CSV文件，不保存行索引（比赛要求）\n    submission_df.to_csv(submission_path, index=False)\n    \n    # 打印提示信息\n    print(f\"✅ 提交文件已生成！\")\n    print(f\"📁 文件路径: {submission_path}\")\n\n# 调用函数，执行预测并生成提交文件\ngenerate_submission()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T02:46:02.264937Z","iopub.execute_input":"2026-05-02T02:46:02.265227Z","iopub.status.idle":"2026-05-02T02:46:07.124460Z","shell.execute_reply.started":"2026-05-02T02:46:02.265205Z","shell.execute_reply":"2026-05-02T02:46:07.123699Z"}},"outputs":[],"execution_count":null}]}