{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":95504,"databundleVersionId":11357675,"sourceType":"competition"}],"dockerImageVersionId":30918,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# CIFAR-10图片分类问题\n\n此`ipynb`文件的大纲如下：\n- DataLoader\n- Model(CNN, MLP)\n- Train_loop & Test_loop\n- Controller\n- Adjusting\n- Evaluater\n  \n导入需要使用的`Python package`","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader, Dataset\nimport numpy as np\nimport pandas as pd\nfrom pathlib import Path","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-03T08:19:01.278763Z","iopub.execute_input":"2025-04-03T08:19:01.279124Z","iopub.status.idle":"2025-04-03T08:19:01.283559Z","shell.execute_reply.started":"2025-04-03T08:19:01.279100Z","shell.execute_reply":"2025-04-03T08:19:01.282591Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 查看数据集\n在这个单独的模块中，当你运行这个模块时，它将随机输出4个训练集中的元素，包括图像以及标签","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport numpy as np\nimport random\nimport torch\n\ndef visualize_train_images(train_data_path, num_samples=4):\n    \"\"\"\n    可视化训练集图片\n    \n    参数:\n        train_data_path: 训练数据路径\n        num_samples: 要显示的样本数量\n    \"\"\"\n    # 加载数据\n    data = np.load(train_data_path)\n    x_data = data['x_train']\n    y_data = data['y_train']\n    \n    # CIFAR10的类别名称\n    classes = ['airplane', 'automobile', 'bird', 'cat', 'deer', \n               'dog', 'frog', 'horse', 'ship', 'truck']\n    \n    # 随机选择样本\n    indices = random.sample(range(len(x_data)), num_samples)\n    \n    # 创建图表\n    fig, axes = plt.subplots(1, num_samples, figsize=(15, 4))\n    fig.suptitle('examples of train data', fontsize=14)\n    \n    for idx, ax in zip(indices, axes):\n        # 获取图片和标签\n        img = x_data[idx]\n        img = img / 2 + 0.5\n        true_label = y_data[idx]\n        \n        # 显示图片\n        img_display = np.transpose(img, (1, 2, 0))  # 转换通道顺序为(H,W,C)\n        ax.imshow(img_display)\n        \n        # 添加标题：真实标签\n        ax.set_title(f'{classes[true_label]}')\n        ax.axis('off')\n    \n    plt.tight_layout()\n    plt.show()\n\n# 使用方法\nvisualize_train_images(\n    \"/kaggle/input/cnns-cifar-10/train_data.npz\",  # 训练数据路径\n    num_samples=4  # 显示4张图片\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-03T08:19:01.284639Z","iopub.execute_input":"2025-04-03T08:19:01.285004Z","iopub.status.idle":"2025-04-03T08:19:04.252262Z","shell.execute_reply.started":"2025-04-03T08:19:01.284948Z","shell.execute_reply":"2025-04-03T08:19:04.251081Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## `DataLoader` Part\n\n- `CIFAR10Dataset`：我们实现了`Pytorch`抽象类`Dataset`的一个子类，用于我们图片分类的任务。（所有`Dataset`子类必须要实现`__getitem__`方法，可选择实现`__len__`方法用于返回数据集的大小）\n\n`prepare_data_loader`: 准备训练和测试的数据加载器的函数。此函数加载数据集，将其分为训练集和测试集，并创建相应的数据加载器。数据加载过程使用`PyTorch`的`DataLoader`进行并行处理，以提高数据加载效率。工作流程：  \n- 从指定路径加载数据集\n- 根据给定比例将数据集分为训练集和测试集\n- 为训练集和测试集创建`Dataset`对象\n- 配置数据加载器，设置批量大小、工作进程数等\n\n返回一个包含训练和测试数据集的字典（{\"train\": `train_loader`, \"test\": `test_loader`}）\n\n\n\n","metadata":{}},{"cell_type":"code","source":"class CIFAR10Dataset(Dataset):\n    \"\"\"\n    CIFAR10数据集加载器\n    \"\"\"\n    def __init__(self, x, y):\n        self._x = x\n        self._y = y\n\n    def __getitem__(self, idx):\n        return {\n            \"images\": torch.tensor(self._x[idx], dtype=torch.float32),\n            \"label\": torch.tensor(self._y[idx], dtype=torch.long),\n        }\n\n    def __len__(self):\n        return len(self._x)\n\ndef prepare_data_loader(\n    path: str,\n    ratio: float,\n    batch_size: int,\n    num_workers: int,\n) -> dict:\n    \"\"\"\n    参数:\n        path (str): .npz格式的数据集文件路径\n        ratio (float): 训练集比例\n        batch_size (int): 批次大小\n        num_workers (int): 数据加载的工作进程数\n    返回:\n        dict: 包含训练和测试数据加载器的字典\n    \"\"\"\n    print(\"开始加载数据...\")  # 添加调试信息\n    train_data = np.load(path)\n    print(f\"数据文件已加载, 包含的keys: {train_data.files}\")  # 显示数据内容\n    \n    x_data = train_data['x_train'].astype(np.float32) \n    y_data = train_data['y_train']\n    print(f\"数据形状 - x: {x_data.shape}, y: {y_data.shape}\")  # 显示数据形状\n\n    num_samples = len(x_data)\n    split_idx = int(num_samples * ratio)\n    x_train = x_data[:split_idx]\n    y_train = y_data[:split_idx]\n    x_test = x_data[split_idx:]\n    y_test = y_data[split_idx:]\n    print(f\"训练集大小: {len(x_train)}, 测试集大小: {len(x_test)}\")  # 显示划分结果\n\n    train_batch_size = batch_size\n    test_batch_size = batch_size\n    \n    print(\"创建数据加载器...\")  # 添加调试信息\n    train_dataset = CIFAR10Dataset(x_train, y_train)\n    test_dataset = CIFAR10Dataset(x_test, y_test)\n\n    train_loader = DataLoader(\n        dataset=train_dataset,\n        batch_size=train_batch_size,\n        shuffle=True,\n        num_workers=1,  \n        drop_last=True,\n        pin_memory=True  \n    )\n\n    test_loader = DataLoader(\n        dataset=test_dataset,\n        batch_size=test_batch_size,\n        shuffle=False,\n        num_workers=1,  \n        pin_memory=True  \n    )\n    print(\"数据加载器创建完成\")  # 添加调试信息\n\n    return {\"train\": train_loader, \"test\": test_loader}\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-03T08:19:04.254410Z","iopub.execute_input":"2025-04-03T08:19:04.254659Z","iopub.status.idle":"2025-04-03T08:19:04.266041Z","shell.execute_reply.started":"2025-04-03T08:19:04.254638Z","shell.execute_reply":"2025-04-03T08:19:04.265183Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 卷积神经网络模型结构说明\n\n本代码实现了两种深度学习模型：卷积神经网络(CNN)和多层感知机(MLP)，用于CIFAR10图像分类任务。\n\n### CNN 模型\n\n卷积神经网络(CNN)特别适合处理图像数据，其核心组件包括：\n\n1. **卷积层(Convolution Layer)**：\n   - 使用滑动窗口(卷积核)扫描图像，提取局部特征\n   - 每个卷积核可以学习检测特定的模式（如边缘、纹理等）\n   - 数学表达式：$$x_{n+1} = \\sum (x_{n} * w_{n}) + b_{n}$$ \n   \n2. **池化层(Pooling Layer)**：\n   - 使用最大池化(MaxPool)对特征图进行降采样\n   - 减小数据维度，提高计算效率\n   - 增加模型对图像位置变化的鲁棒性\n   \n3. **全连接层(Fully Connected Layer)**：\n   - 将卷积层提取的特征映射到分类空间\n   - 通过多层变换学习特征组合\n   - 数学表达式：$$x_{n+1} = \\mathbf{ReLU}(x_{n} \\cdot w_{n} + b_{n})$$\n\n模型结构流程：\n```python\nInput Image (3×32×32)\n    ↓\nConvolution Blocks (可配置层数)\n    ↓\nFlatten (展平特征图)\n    ↓\nFully Connected Layers\n    ↓\nOutput (10个类别的概率)\n```\n\n### MLP 模型\n\n多层感知机(MLP)是一种基础的神经网络结构：\n\n1. **输入层**：\n   - 将3×32×32的图像展平为3072维向量\n   \n2. **隐藏层**：\n   - 多个全连接层组成\n   - 使用ReLU激活函数引入非线性\n   - 使用BatchNorm提高训练稳定性\n   - 使用Dropout防止过拟合\n   \n3. **输出层**：\n   - 10个神经元对应10个类别\n   - 使用Softmax函数得到概率分布\n\n关键概念解释：\n- **ReLU激活函数**：$$f(x) = max(0, x)$$，帮助模型学习非线性特征\n- **Dropout**：随机\"关闭\"一部分神经元，防止模型过度依赖某些特征\n- **BatchNorm**：对每一层的输出进行标准化，加速训练收敛\n- **Softmax函数**：$$\\mathbf{softmax}(x_i) = \\frac{e^{x_i}}{\\sum_j e^{x_j}}$$，将输出转换为概率分布\n\n### 模型优势比较\n\n- **CNN优势**：\n  - 能够有效利用图像的空间结构信息\n  - 参数共享减少了模型大小\n  - 适合处理具有局部相关性的数据\n\n- **MLP优势**：\n  - 结构简单，易于理解和实现\n  - 训练速度较快\n  - 适合处理一般的分类问题\n\n这两种模型结构都是深度学习中的基础架构，理解它们对于进一步学习更复杂的模型很有帮助。","metadata":{}},{"cell_type":"code","source":"class SimpleCNN(nn.Module):\n    \"\"\"\n    可配置的CNN模型\n    \n    参数:\n        in_channels (int): 输入通道数\n        num_classes (int): 分类类别数\n        conv_layers (list): 每个卷积层的输出通道数列表\n        fc_layers (list): 每个全连接层的输出维度列表\n        kernel_size (int): 卷积核大小\n        dropout_rate (float): Dropout比率\n    \"\"\"\n    def __init__(\n        self,\n        in_channels: int = 3,\n        num_classes: int = 10,\n        conv_layers: list = [32, 64],  # 默认两层卷积\n        fc_layers: list = [128, 64],   # 默认两层全连接\n        kernel_size: int = 3,\n        dropout_rate: float = 0.1\n    ):\n        super().__init__()\n        \n        # 构建卷积层\n        self.conv_blocks = nn.ModuleList()\n        current_channels = in_channels\n        \n        for i, out_channels in enumerate(conv_layers):\n            conv_block = nn.Sequential(\n                nn.Conv2d(\n                    in_channels=current_channels,\n                    out_channels=out_channels,\n                    kernel_size=kernel_size,\n                    stride=1,\n                    padding='same'\n                ),\n                # nn.BatchNorm2d(out_channels), 可选择是否添加BatchNorm层\n                nn.ReLU(),\n                nn.MaxPool2d(kernel_size=2)\n            )\n            self.conv_blocks.append(conv_block)\n            current_channels = out_channels\n           \n            \n        # 计算展平后的特征维度\n        # 每经过一次MaxPool2d或AvgPool2d，特征图尺寸减半\n        feature_size = current_channels * (32 // (2 ** len(conv_layers))) ** 2\n        \n        # 构建全连接层\n        self.fc_blocks = nn.ModuleList()\n        current_dim = feature_size\n        \n        for fc_dim in fc_layers:\n            fc_block = nn.Sequential(\n                nn.Linear(current_dim, fc_dim),\n                nn.ReLU(),\n                nn.Dropout(dropout_rate)\n            )\n            self.fc_blocks.append(fc_block)\n            current_dim = fc_dim\n            \n        # 输出层\n        self.output_layer = nn.Linear(current_dim, num_classes)\n\n    def forward(self, x):\n         # 通过所有卷积层\n         for conv_block in self.conv_blocks:\n             x = conv_block(x)\n    \n         # 展平\n         x = torch.flatten(x, start_dim=1)\n    \n         # 通过所有全连接层\n         for fc_block in self.fc_blocks:\n              x = fc_block(x)\n        \n         # 输出层\n         x = self.output_layer(x)\n         return x \n        \n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-03T08:19:04.267531Z","iopub.execute_input":"2025-04-03T08:19:04.267837Z","iopub.status.idle":"2025-04-03T08:19:04.281477Z","shell.execute_reply.started":"2025-04-03T08:19:04.267810Z","shell.execute_reply":"2025-04-03T08:19:04.280411Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class SimpleMLP(nn.Module):\n    \"\"\"\n    可配置的多层感知机模型\n    \n    参数:\n        input_size (int): 输入特征维度 (对于CIFAR10是3*32*32=3072)\n        num_classes (int): 分类类别数\n        hidden_layers (list): 每个隐藏层的输出维度列表\n        dropout_rate (float): Dropout比率\n        activation (str): 激活函数类型 ('relu', 'tanh', 'sigmoid')\n    \"\"\"\n    def __init__(\n        self,\n        input_size: int = 3072,  # 3*32*32 for CIFAR10\n        num_classes: int = 10,\n        hidden_layers: list = [512, 256, 128],  # 默认三层隐藏层\n        dropout_rate: float = 0.1,\n        activation: str = 'relu'\n    ):\n        super(SimpleMLP, self).__init__()\n        \n        # 选择激活函数\n        if activation == 'relu':\n            self.activation = nn.ReLU()\n        elif activation == 'tanh':\n            self.activation = nn.Tanh()\n        elif activation == 'sigmoid':\n            self.activation = nn.Sigmoid()\n        else:\n            raise ValueError(f\"Unsupported activation function: {activation}\")\n            \n        # 构建隐藏层\n        self.fc_blocks = nn.ModuleList()\n        current_dim = input_size\n        \n        for hidden_dim in hidden_layers:\n            fc_block = nn.Sequential(\n                nn.Linear(current_dim, hidden_dim),\n                self.activation,\n                nn.BatchNorm1d(hidden_dim),  # 添加批归一化\n                nn.Dropout(dropout_rate)\n            )\n            self.fc_blocks.append(fc_block)\n            current_dim = hidden_dim\n            \n        # 输出层\n        self.output_layer = nn.Linear(current_dim, num_classes)\n\n    def forward(self, x):\n        # 展平输入\n        batch_size = x.size(0)\n        x = x.view(batch_size, -1)\n        \n        # 通过所有隐藏层\n        for fc_block in self.fc_blocks:\n            x = fc_block(x)\n            \n        # 输出层\n        x = self.output_layer(x)\n        return x\n        ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-03T08:19:04.282828Z","iopub.execute_input":"2025-04-03T08:19:04.283415Z","iopub.status.idle":"2025-04-03T08:19:04.302520Z","shell.execute_reply.started":"2025-04-03T08:19:04.283380Z","shell.execute_reply":"2025-04-03T08:19:04.301604Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 定义损失函数","metadata":{}},{"cell_type":"code","source":"import torch.optim as optim\n\nloss_fn = nn.CrossEntropyLoss()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-03T08:19:04.303072Z","iopub.execute_input":"2025-04-03T08:19:04.303300Z","iopub.status.idle":"2025-04-03T08:19:04.320653Z","shell.execute_reply.started":"2025-04-03T08:19:04.303281Z","shell.execute_reply":"2025-04-03T08:19:04.319859Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## `Train_loop` & `Test_loop` Part\n这一部分是实现神经网络在一个训练周期中的两个关键步骤：\n- 在训练集上面计算损失，并依据损失更新参数\n- 在测试集上面计算度量(Metric)，跟踪当前参数下的泛化性能\n\n我们实现了`train_step`和`eval_step`两个函数分别执行单步(step)的训练步骤和测试步骤，`train_per_epoch`和`test_per_epoch`两个函数分别执行一个回合(epoch)的训练步骤和测试步骤。对于深度学习而言，一个`step`包含对一批(`batch`)数据的处理，一个`epoch`包含多个`step`，当把数据集的数据都训练过一遍，我们称为一个`epoch`，也就是说:\n$$ batch\\_sizes * num\\_steps = total\\_data $$\n\n`train_per_epoch` : 此函数处理一个完整`epoch`的训练过程:\n- 设置模型为训练模式\n- 遍历训练数据加载器中的所有批次\n- 对每个批次执行前向和后向传播并使用优化器更新模型参数\n- 使用进度条跟踪和显示训练指标\n\n`test_per_epoch` : 此函数处理一个完整`epoch`训练后的评估过程:\n- 设置模型为评估模式\n- 遍历测试数据加载器中的所有批次\n- 在不更新梯度的情况下计算前向传播\n- 计算评估指标（损失、准确率等），记录并显示评估结果","metadata":{}},{"cell_type":"code","source":"def train_step(\n    model: nn.Module, optimizer, batch: dict, device: torch.device\n):\n    \"\"\"\n    单步训练\n    \"\"\"\n    batch_images = batch[\"images\"].to(device)\n    labels = batch[\"label\"].to(device)\n        \n    optimizer.zero_grad()\n\n    logits = model(batch_images) # 模型正向过程\n        \n    loss = loss_fn(logits, labels) # 计算总损失\n        \n    loss.backward() # 反向传播\n        \n    torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) # 添加梯度裁剪，防止梯度爆炸\n        \n    optimizer.step()\n\n    return loss.item(), logits, labels\n        \n\ndef eval_step(model: nn.Module, batch: dict, device: torch.device):\n    # 单步评估\n    model.eval()\n    with torch.no_grad():\n        batch_images = batch[\"images\"].to(device)\n        labels = batch[\"label\"].to(device) \n        logits = model(batch_images) # 模型正向过程\n        loss = loss_fn(logits, labels) # 计算总损失\n        return loss.item(), logits, labels\n    \ndef train_per_epoch(\n    model: nn.Module,\n    optimizer: optim.Optimizer,\n    batch_size: int,\n    train_loader: DataLoader,\n    device: torch.device,\n):\n    model.train()\n    num_data = len(train_loader.dataset)\n    num_batches = len(train_loader)\n    correct = 0\n    total_loss = 0\n    print(f\"开始训练 - 总样本数: {num_data}\")\n    print(f\"总批次数: {len(train_loader)}\")\n    \n    for batch_idx, batch in enumerate(train_loader):\n        loss, logits, labels = train_step(model, optimizer, batch, device)\n        total_loss += loss\n        _, predicted = torch.max(logits, 1)\n        correct += (predicted == labels).sum().item()    \n        if batch_idx % 50 == 0:  # 改为每50个批次打印一次\n            current = batch_idx * batch_size + len(batch[\"images\"])\n            print(f\"批次 {batch_idx}: Loss: {loss:>6.4f}, 进度: {current:>5d}/{num_data:>5d}\")\n    accuracy = correct / num_data\n    avg_loss = total_loss / num_batches\n    print(f\"Train Error: \\n Accuracy: {(100*accuracy):>0.1f}%, Avg loss: {avg_loss:>.8f} \\n\")\n        \ndef test_per_epoch(\n    model: nn.Module,\n    test_loader: DataLoader,\n    device: torch.device,\n):\n    \"\"\"\n    每轮测试\n    \"\"\"\n    model.eval()\n    total_loss = 0.0\n    correct = 0\n    num_batches = len(test_loader)\n    num_data = len(test_loader.dataset)\n    with torch.no_grad():\n        for batch in test_loader:\n            loss, logits, labels = eval_step(model, batch, device)\n            total_loss += loss\n            _, predicted = torch.max(logits, 1)\n            correct += (predicted == labels).sum().item()\n\n    avg_loss = total_loss / num_batches\n    accuracy = correct / num_data\n    print(f\"Test Error: \\n Accuracy: {(100*accuracy):>0.1f}%, Avg loss: {avg_loss:>.8f} \\n\")\n    return 100*accuracy\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-03T08:19:04.321578Z","iopub.execute_input":"2025-04-03T08:19:04.321887Z","iopub.status.idle":"2025-04-03T08:19:04.338369Z","shell.execute_reply.started":"2025-04-03T08:19:04.321858Z","shell.execute_reply":"2025-04-03T08:19:04.337614Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## `Controller` Part\n这一部分是我们这份代码的控制中枢，我们实例化上文已经写好的类，调用上文已经写好的各个函数，组合起来实现我们的CIFAR-10分类任务。在这一部分，我们要指定需要设定的绝大部分超参数，开展训练并得到我们训练好的模型。","metadata":{}},{"cell_type":"code","source":"def controller(\n    seed: int,\n    model_type: str,  # 'cnn' 或 'mlp'\n    # CNN特有参数\n    in_channels: int = None,\n    conv_layers: list = None,\n    kernel_size: int = None,\n    fc_layers  : list = None,\n    # MLP特有参数\n    input_size: int = None,\n    hidden_layers: list = None,\n    activation: str = None,\n    # 通用参数\n    num_classes: int = 10,\n    dropout_rate: float = 0.1,\n    data_path: Path = None,\n    ratio: float = 0.8,\n    batch_size: int = 64,\n    num_workers: int = 4,\n    epochs: int = 10,\n    learning_rate: float = 0.001,\n    weight_decay: float = 0.004\n):\n    \"\"\"\n    训练控制器\n    \n    参数:\n        model_type: 选择模型类型 ('cnn' 或 'mlp')\n        其他参数见各模型的文档\n    \"\"\"\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    torch.manual_seed(seed)\n    \n    if model_type == 'cnn':\n        model = SimpleCNN(\n            in_channels=in_channels,\n            num_classes=num_classes,\n            conv_layers=conv_layers,\n            fc_layers=fc_layers,  \n            kernel_size=kernel_size,\n            dropout_rate=dropout_rate\n        ).to(device)\n    elif model_type == 'mlp':\n        model = SimpleMLP(\n            input_size=input_size,\n            num_classes=num_classes,\n            hidden_layers=hidden_layers,\n            dropout_rate=dropout_rate,\n            activation=activation\n        ).to(device)\n    else:\n        raise ValueError(f\"Unsupported model type: {model_type}\")\n\n    optimizer = optim.Adam(model.parameters(), lr=learning_rate)  #选择Adam优化器\n    scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer,T_max=epochs) # 选择余弦退火模型\n    loader_dict = prepare_data_loader(path=data_path, ratio=ratio, batch_size=batch_size, num_workers=2 )\n    train_loader = loader_dict[\"train\"]\n    test_loader = loader_dict[\"test\"]\n    print(f\"Using device: {device}\") \n    best_acc = 0.0\n    for epoch in range(epochs):\n        print(f\"Epoch {epoch + 1} \\n--------------------------------\")\n        train_per_epoch(model, optimizer, batch_size, train_loader, device)\n        acc = test_per_epoch(model, test_loader, device)\n        scheduler.step()\n        if acc > best_acc:\n            best_acc = acc\n            torch.save(model.state_dict(), 'best_model.pth')\n    print(f\"Training completed! Best accuracy: {best_acc:.2f}%\")\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-03T08:19:04.339263Z","iopub.execute_input":"2025-04-03T08:19:04.339549Z","iopub.status.idle":"2025-04-03T08:19:04.355973Z","shell.execute_reply.started":"2025-04-03T08:19:04.339521Z","shell.execute_reply":"2025-04-03T08:19:04.355164Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## `Adjusting` Part\n<div class=\"alert alert-block alert-warning\">\n<b>同学们只需要在此模块调参\n</div>\n若运行CNN模型，则在此模块下进行调参，只运行此模块，或注释MLP模块代码(注释快捷键 ctrl+/ )","metadata":{}},{"cell_type":"code","source":"# 随机数种子：确保每次运行代码得到相同的结果\nseed = 42\n\n# 模型类型：选择使用CNN（卷积神经网络）或MLP（多层感知机）\nmodel_type = 'cnn'\n\n# 卷积层配置：定义CNN模型中卷积层的结构\n# [32, 64]表示有两层卷积层，第一层输出32个特征图，第二层输出64个特征图\n# [32, 64, 128]则表示三层卷积层\nconv_layers = [32, 64]\n\n# 卷积核大小：定义每次卷积操作时，窗口的大小\n# kernel_size=3表示使用3×3的滑动窗口\nkernel_size = 3\n\n# 全连接层配置：定义CNN末端全连接层的结构\n# [128, 64]表示有两层全连接层，第一层有128个神经元，第二层有64个神经元\nfc_layers = [128, 64]\n\n# Dropout比率：随机\"关闭\"一部分神经元，防止模型过度依赖某些特征\n# 比率0.2表示每次训练时随机关闭20%的神经元\ndropout_rate = 0.2\n\n#ratio比率：训练集中用于训练的数据量/总数据量\n#比率0.8表示50000张图片中有40000张用于训练，10000用于测试\nratio = 0.8\n\n# 训练轮数：整个训练数据集要被训练的次数\nepochs = 50\n\n# 批次大小：每次训练选取的图片数量\nbatch_size = 128\n\n# 学习率：模型在训练过程中调整参数的步长\nlearning_rate = 0.001\n\n#  CNN模型示例\nmodel = controller(\n    seed=seed,\n    model_type=model_type,\n    in_channels=3,\n    conv_layers=conv_layers,  \n    kernel_size=kernel_size,\n    fc_layers=fc_layers,\n    dropout_rate=dropout_rate,\n    data_path=Path(\"/kaggle/input/cnns-cifar-10/train_data.npz\"),\n    ratio = ratio,\n    epochs=epochs,  \n    batch_size=batch_size,  \n    learning_rate=learning_rate,\n    weight_decay = 0.004 \n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-03T08:19:04.358290Z","iopub.execute_input":"2025-04-03T08:19:04.358484Z","iopub.status.idle":"2025-04-03T08:21:16.790924Z","shell.execute_reply.started":"2025-04-03T08:19:04.358468Z","shell.execute_reply":"2025-04-03T08:21:16.789740Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"若运行MLP模型，则在此模块下进行调参，只运行此模块，或注释CNN模块代码(注释快捷键 ctrl+/ )","metadata":{}},{"cell_type":"code","source":"# # 随机数种子：确保实验可以重复\n# # 就像在相同的土地上，用相同的种子，收获相同的果实\n# seed = 42\n\n# # 模型类型：这里选择 MLP（多层感知机）模型\n# model_type = 'mlp'\n\n# # 输入维度：将32×32像素，3个颜色通道的图片展平后的大小\n# # 计算方式：32 × 32 × 3 = 3072\n# input_size = 3072\n\n# # 隐藏层配置：定义神经网络中间层的结构\n# # [1024, 512, 256] 表示有三层隐藏层，每层的神经元数量依次减少\n# # 就像信息通过层层筛选，从复杂到简单的过程\n# # - 第一层1024个神经元：接收大量原始特征\n# # - 第二层512个神经元：进行中层特征提取\n# # - 第三层256个神经元：提取更高层的抽象特征\n# hidden_layers = [1024, 512, 256]\n\n# # 激活函数：使用ReLU（修正线性单元）\n# # ReLU的特点是：输入为正时保持不变，输入为负时输出为0\n# activation = 'relu'\n\n# # Dropout比率：随机\"关闭\"一部分神经元，防止模型过度依赖某些特征\n# # 比率0.2表示每次训练时随机关闭20%的神经元\n# dropout_rate = 0.2\n\n# # 训练轮数：整个训练数据集要被训练的次数\n# # 每个epoch会将所有训练数据都使用一遍\n# epochs = 5\n\n# # 批次大小：每次训练时同时处理的图片数量\n# # 较大的batch_size训练更稳定，但需要更多内存\n# # 较小的batch_size训练更快，但可能不够稳定\n# batch_size = 32\n\n# # 学习率：模型在训练过程中调整参数的步长\n# # 较大的学习率学习速度快但可能错过最优解\n# # 较小的学习率学习速度慢但更容易找到最优解\n# learning_rate = 0.001\n    \n# #MLP模型示例\n# model = controller(\n#         seed=seed,\n#         model_type='mlp',\n#         input_size=input_size,  \n#         hidden_layers=hidden_layers,\n#         activation=activation,\n#         dropout_rate=dropout_rate,\n#         data_path=Path(\"/kaggle/input/cnns-cifar-10/train_data.npz\"),\n#         epochs=epochs,  \n#         batch_size=batch_size,  \n#         learning_rate=learning_rate\n#     )\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-03T08:21:16.792274Z","iopub.execute_input":"2025-04-03T08:21:16.792529Z","iopub.status.idle":"2025-04-03T08:21:16.796395Z","shell.execute_reply.started":"2025-04-03T08:21:16.792506Z","shell.execute_reply":"2025-04-03T08:21:16.795631Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 训练结果可视化\n在训练结束后，运行此模块可视化训练结果","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport numpy as np\nimport random\n\ndef visualize_predictions(model, train_data_path, num_samples=4,device=torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")):\n    \"\"\"\n    可视化模型预测结果\n    \n    参数:\n        model: 训练好的模型\n        train_data_path: 训练数据路径\n        num_samples: 要显示的样本数量\n    \"\"\"\n    # 加载数据\n    data = np.load(train_data_path)\n    x_data = data['x_train']\n    y_data = data['y_train']\n    \n    # CIFAR10的类别名称\n    classes = ['airplane', 'automobile', 'bird', 'cat', 'deer', \n               'dog', 'frog', 'horse', 'ship', 'truck']\n    \n    # 随机选择样本\n    indices = random.sample(range(len(x_data)), num_samples)\n    \n    # 创建图表\n    fig, axes = plt.subplots(1, num_samples, figsize=(15, 4))\n    fig.suptitle('visualization results', fontsize=14)\n    \n    # 设置模型为评估模式\n    model.eval()\n    \n    with torch.no_grad():\n        for idx, ax in zip(indices, axes):\n            # 获取图片和标签\n            img = x_data[idx]\n            true_label = y_data[idx]\n            \n            # 预处理图片并进行预测\n            img_tensor = torch.tensor(img, dtype=torch.float32).unsqueeze(0).to(device)\n            outputs = model(img_tensor)\n            _, predicted = torch.max(outputs, 1)\n            pred_label = predicted.item()\n            \n            # 显示图片\n            img_display = np.transpose(img, (1, 2, 0))  # 转换通道顺序为(H,W,C)\n            ax.imshow(img_display)\n            \n            # 添加标题：真实标签 vs 预测标签\n            ax.set_title(f'true: {classes[true_label]}\\n pred: {classes[pred_label]}')\n            ax.axis('off')\n    \n    plt.tight_layout()\n    plt.show()\n\n# 使用方法\nvisualize_predictions(\n    model,  # 你训练好的模型\n    \"/kaggle/input/cnns-cifar-10/train_data.npz\",  # 训练数据路径\n    num_samples=4  # 显示4张图片\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-03T08:21:16.797263Z","iopub.execute_input":"2025-04-03T08:21:16.797542Z","iopub.status.idle":"2025-04-03T08:21:19.739529Z","shell.execute_reply.started":"2025-04-03T08:21:16.797512Z","shell.execute_reply":"2025-04-03T08:21:19.738690Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## `Evaluater` Part\n\n<div class=\"alert alert-block alert-warning\">\n<b>这部分代码同学们不用修改调参，不会影响我们训练模型的性能。\n</div>\n\n这一部分我们调用已经训练好的模型，去预测我们`kaggle`给出的不带标签(`label`)的测试数据集，并将我们预测的结果写成一个`solution.csv`文件，放在指定的`kaggle`云文件夹下，便于自动评估我们提交模型的表现。","metadata":{}},{"cell_type":"code","source":"def evaluater(model: nn.Module, test_data_path: Path, solution_path: Path):\n    \"\"\"\n    模型评估器\n    \"\"\"\n    model.eval()\n    with torch.no_grad():\n        test_data = np.load(test_data_path)\n        test_ids = test_data[\"ID\"]\n        test_images = test_data[\"x_test\"].astype(np.float32) \n        test_images = torch.tensor(test_images).cuda()\n        logits = model(test_images)\n        _, predicted_labels = torch.max(logits, dim=1)\n        predicted_labels = predicted_labels.cpu().numpy()\n    predicted_labels = pd.DataFrame(predicted_labels, index=test_ids, columns=[\"label\"])\n    predicted_labels.to_csv(solution_path, index=True, index_label=\"ID\")\n\n\n\nevaluater(\n    model,\n    test_data_path=Path(\"/kaggle/input/cnns-cifar-10/test_data.npz\"),\n    solution_path=Path(\"/kaggle/working/submission.csv\")\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-03T08:21:19.740352Z","iopub.execute_input":"2025-04-03T08:21:19.740629Z","iopub.status.idle":"2025-04-03T08:21:20.165452Z","shell.execute_reply.started":"2025-04-03T08:21:19.740608Z","shell.execute_reply":"2025-04-03T08:21:20.164757Z"}},"outputs":[],"execution_count":null}]}