{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":7327,"databundleVersionId":861871,"sourceType":"competition"},{"sourceId":12399592,"sourceType":"datasetVersion","datasetId":7819384},{"sourceId":1956358,"sourceType":"kernelVersion"}],"dockerImageVersionId":31040,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"#  <span style=\"color:red\">**初始事项加载**</span>","metadata":{}},{"cell_type":"code","source":"import torch\nfrom torch import optim\nimport numpy as np\nimport torch.nn as nn\nimport torch.nn.functional as F#added import\nfrom torch.autograd import Variable\nfrom torchvision import transforms\nfrom torch.utils.data import Dataset, DataLoader\nfrom PIL import Image\nfrom copy import deepcopy\nimport pandas as pd\nimport os\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import LabelEncoder\nimport matplotlib.pyplot as plt\n#此处引入头文件，需要可随时添加\n# 忽略警告\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# 设备设置（GPU/CPU）\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"使用设备: {device}\")\n\n# 数据路径与常量\nDATA_DIR = '/kaggle/input/dog-breed-identification'  # 数据集根目录\nIMAGE_SIZE = (128, 128)  # 图像统一尺寸\nBATCH_SIZE = 32          # 批次大小\nNUM_CLASSES = 120        # 狗品种总数\n\n#读取数据的标签\nlabels = pd.read_csv('/kaggle/input/dog-breed-identification/labels.csv')\nlabels.head()\n\n#id是train数据集样本图片的名称 ，breed是标签，运行后正常显示则读取成功。（用于测试数据集是否正常导入note）","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-09T14:21:05.714573Z","iopub.execute_input":"2025-07-09T14:21:05.714815Z","iopub.status.idle":"2025-07-09T14:21:17.783059Z","shell.execute_reply.started":"2025-07-09T14:21:05.714795Z","shell.execute_reply":"2025-07-09T14:21:17.782351Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#  <span style=\"color:red\">**数据集处理**</span>","metadata":{}},{"cell_type":"markdown","source":"## <span style=\"color:black\">**查看数据集内容（不用管这个小部分，只是用于测试）**</span>","metadata":{}},{"cell_type":"code","source":"\nbreed_count = labels['breed'].value_counts()\nbreed_count.head(10)#查看前十个类别\n#breed_count.shape 输入此命令可查看数据集涉及所有分类，共120个。（用于测试数据集是否有损坏）\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-09T14:21:17.784469Z","iopub.execute_input":"2025-07-09T14:21:17.784717Z","iopub.status.idle":"2025-07-09T14:21:17.798026Z","shell.execute_reply.started":"2025-07-09T14:21:17.784698Z","shell.execute_reply":"2025-07-09T14:21:17.797241Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## <span style=\"color:black\">**数据图片引入及处理**</span>","metadata":{}},{"cell_type":"code","source":"# 自定义数据集类\nclass DogBreedDataset(Dataset):\n    def __init__(self, filepaths, labels, transform=None):\n        self.filepaths = filepaths  # 图像路径列表\n        self.labels = labels        # 标签（数值编码）\n        self.transform = transform  # 图像变换函数\n\n    def __len__(self):\n        return len(self.filepaths)  # 数据集大小\n\n    def __getitem__(self, idx):\n        # 读取图像并转换为RGB格式\n        img_path = self.filepaths.iloc[idx]\n        image = Image.open(img_path).convert('RGB')\n\n        # 应用变换\n        if self.transform:\n            image = self.transform(image)\n\n        # 返回（图像，标签）\n        label = self.labels.iloc[idx]\n        return image, label\n\n\n# 图像变换定义（区分训练/验证/测试集）\n# 训练集：含数据增强\ntrain_transform = transforms.Compose([\n    transforms.Resize(IMAGE_SIZE),\n    transforms.RandomHorizontalFlip(p=0.5),  # Moderate flip\n    transforms.RandomRotation(degrees=10),   # Small rotation\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406],\n                         std=[0.229, 0.224, 0.225])\n])\n\n# 验证集：无增强，基础处理\nval_transform = transforms.Compose([\n    transforms.Resize(IMAGE_SIZE),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406],\n                         std=[0.229, 0.224, 0.225])\n])\n\n# 测试集：与验证集处理一致（无增强，确保评估公平）\ntest_transform = transforms.Compose([\n    transforms.Resize(IMAGE_SIZE),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406],\n                         std=[0.229, 0.224, 0.225])\n])\n\n# 数据加载与三部分划分（8:1:1）\n\n# 1. 读取原始标签文件\nlabels = pd.read_csv(os.path.join(DATA_DIR, 'labels.csv'))\nprint(f\"原始数据集总样本数: {labels.shape[0]}\")  # 输出 10222\n\n# 2. 生成图像路径\nlabels['filepath'] = os.path.join(DATA_DIR, 'train/') + labels['id'] + '.jpg'\n\n# 3. 编码品种标签（文本→数值）\nle = LabelEncoder()\nlabels['breed_encoded'] = le.fit_transform(labels['breed'])\nprint(f\"品种数: {labels['breed_encoded'].nunique()}\")  # 输出 120\n\n# 4. 第一次划分：先从原始数据中拆分出测试集（10%）\n# 剩余90%用于后续拆分为训练集和验证集\nX_temp, X_test, y_temp, y_test = train_test_split(\n    labels['filepath'],\n    labels['breed_encoded'],\n    test_size=0.1,  # 测试集占10%\n    random_state=10,\n    stratify=labels['breed_encoded']  # 分层抽样，保持类别比例\n)\n\n# 5. 第二次划分：从剩余90%中拆分出验证集（10% of 总数据）\n# 最终训练集占80%，验证集10%，测试集10%\nX_train, X_val, y_train, y_val = train_test_split(\n    X_temp,\n    y_temp,\n    test_size=1/9, \n    random_state=10,\n    stratify=y_temp  # 再次分层，确保验证集类别比例正确\n)\n\n\n# ----------------------------\n# 创建数据集实例\n# ----------------------------\ntrain_dataset = DogBreedDataset(\n    filepaths=X_train,\n    labels=y_train,\n    transform=train_transform\n)\n\nval_dataset = DogBreedDataset(\n    filepaths=X_val,\n    labels=y_val,\n    transform=val_transform\n)\n\ntest_dataset = DogBreedDataset(\n    filepaths=X_test,\n    labels=y_test,\n    transform=test_transform\n)\n\n\n# ----------------------------\n# 创建DataLoader\n# ----------------------------\ntrain_loader = DataLoader(\n    train_dataset,\n    batch_size=BATCH_SIZE,\n    shuffle=True,\n    num_workers=4,\n    pin_memory=True\n)\n\nval_loader = DataLoader(\n    val_dataset,\n    batch_size=BATCH_SIZE,\n    shuffle=False,\n    num_workers=4,\n    pin_memory=True\n)\n\ntest_loader = DataLoader(\n    test_dataset,\n    batch_size=BATCH_SIZE,\n    shuffle=False,\n    num_workers=4,\n    pin_memory=True\n)\n\n\n# ----------------------------\n# 验证结果\n# ----------------------------\ntotal = len(train_dataset) + len(val_dataset) + len(test_dataset)\nprint(f\"总样本数: {total}\")\nprint(f\"训练集样本数: {len(train_dataset)} ({len(train_dataset)/total:.0%})\")  # 约80%\nprint(f\"验证集样本数: {len(val_dataset)} ({len(val_dataset)/total:.0%})\")    # 约10%\nprint(f\"测试集样本数: {len(test_dataset)} ({len(test_dataset)/total:.0%})\")  # 约10%","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-09T14:21:17.798849Z","iopub.execute_input":"2025-07-09T14:21:17.799116Z","iopub.status.idle":"2025-07-09T14:21:17.866155Z","shell.execute_reply.started":"2025-07-09T14:21:17.799091Z","shell.execute_reply":"2025-07-09T14:21:17.865367Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## <span style=\"color:black\">**基于数据引入的训练函数构建**</span>","metadata":{}},{"cell_type":"code","source":"#YOUR CODE 包括训练函数定义，GPU等部分此处我直接引用了personal assignment部分的代码，因为似乎可以通用。\n# Define batch size and create data loaders.\nbatch_size = 64\n\n# Data loader (this provides queues and threads in a very simple way).\ntrain_loader = torch.utils.data.DataLoader(dataset=train_dataset,\n                                           batch_size=batch_size,\n                                           shuffle=True)\n\nval_loader = torch.utils.data.DataLoader(dataset=val_dataset,\n                                           batch_size=batch_size,\n                                           shuffle=False)\n\ntest_loader = torch.utils.data.DataLoader(dataset=test_dataset,\n                                           batch_size=batch_size,\n                                           shuffle=False)\n\n# Define the device.\ndevice = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nprint(f\"Using {device} device\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-09T14:21:17.867014Z","iopub.execute_input":"2025-07-09T14:21:17.867774Z","iopub.status.idle":"2025-07-09T14:21:17.873058Z","shell.execute_reply.started":"2025-07-09T14:21:17.867746Z","shell.execute_reply":"2025-07-09T14:21:17.872333Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"模型finetune（其实最开始还用了resnet，但是效果没有现在这个好，也可以简单说一下）","metadata":{}},{"cell_type":"code","source":"def train_and_val_with_lr_scheduler(model, train_loader, val_loader, loss_fn, optimizer, lr_scheduler, num_epochs, device, print_step=20):\n    # record the loss and accuracy\n    train_losses = []\n    val_accuracies = []\n\n    best_val_acc = 0.0\n    best_model_state = None\n    for epoch in range(num_epochs):\n        print(f\"Epoch {epoch+1}\\n-------------------------------\")\n        model.train()\n        size = len(train_loader.dataset)\n        epoch_loss = 0  # 这里初始化累积 loss\n\n        for batch, (X, y) in enumerate(train_loader):\n            X, y = X.to(device), y.to(device)\n            optimizer.zero_grad()\n            pred = model(X)\n            loss = loss_fn(pred, y)\n            epoch_loss += loss.item() \n            loss.backward()\n            optimizer.step()\n            if batch % print_step == 0:\n                loss_val, current = loss.item(), (batch+1)*len(X)\n                print(f\"loss: {loss_val:>7f}  [{current:>5d}/{size:>5d}]\")\n\n        # Trigger the scheduler step and print info if learning rate is reduced\n        old_lr = optimizer.param_groups[0]['lr']\n        new_lr = optimizer.param_groups[0]['lr']\n        if new_lr < old_lr:\n            print(f\"\\nLearning rate reduced from {old_lr} to {new_lr}.\\n\")\n\n         # 记录训练 loss\n        avg_loss = epoch_loss / len(train_loader)\n        train_losses.append(avg_loss)\n\n        # 验证准确率\n        val_acc = test(model, val_loader, device)\n        val_accuracies.append(val_acc)\n        lr_scheduler.step(val_acc)  # 传入验证准确率，自动调整学习率\n\n    return train_losses, val_accuracies\n\ndef test(model, test_loader, device):\n    # Test the neural network\n    correct = 0\n    total = 0\n\n    # Set the model to evaluation mode\n    model.eval()\n\n    # Disable gradient calculation\n    with torch.inference_mode():\n        for inputs, labels in test_loader:\n\n            # Move the inputs and labels to the GPU if available\n            inputs = inputs.to(device)\n            labels = labels.to(device)\n\n            # Forward pass\n            outputs = model(inputs)\n\n            # Get the predicted class\n            _, predicted = torch.max(outputs, dim=1)  # output size is (batch_size, num_classes)\n\n            # Update the total number of samples and correct predictions\n            total += labels.size(0)\n            correct += (predicted == labels).sum().item()\n\n    # Calculate the accuracy\n    accuracy = 100 * correct / total\n    print(f\"val_Accuracy: {accuracy:.2f}%\")\n    return accuracy\n\n# Define the cross-entropy loss function for classification\nloss_fn = nn.CrossEntropyLoss()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-09T15:07:54.995778Z","iopub.execute_input":"2025-07-09T15:07:54.996294Z","iopub.status.idle":"2025-07-09T15:07:55.005515Z","shell.execute_reply.started":"2025-07-09T15:07:54.996271Z","shell.execute_reply":"2025-07-09T15:07:55.004798Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#  <span style=\"color:red\">**模型finetune**</span>","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torchvision import models\nfrom torchvision.models import efficientnet_b0\n\n# Define a model using pretrained ResNet18\nclass EfficientNetTransfer(nn.Module):\n    def __init__(self, num_classes=120):\n        super(EfficientNetTransfer, self).__init__()\n        self.model = efficientnet_b0(pretrained=True)\n        in_features = self.model.classifier[1].in_features\n        self.model.classifier[1] = nn.Linear(in_features, num_classes)\n\n    def forward(self, x):\n        return self.model(x)\n\n\n# Instantiate the model and move to device\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel = EfficientNetTransfer(num_classes=120).to(device)\nprint(model)\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-09T15:07:34.686190Z","iopub.execute_input":"2025-07-09T15:07:34.686483Z","iopub.status.idle":"2025-07-09T15:07:34.835321Z","shell.execute_reply.started":"2025-07-09T15:07:34.686462Z","shell.execute_reply":"2025-07-09T15:07:34.834552Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#  <span style=\"color:red\">**训练代码【已完成】**</span>","metadata":{}},{"cell_type":"markdown","source":"## <span style=\"color:black\">**训练函数（目前无法运行是因为没有引入数据集）**</span>","metadata":{}},{"cell_type":"code","source":"\n# Set all layers trainable\nfor param in model.parameters():\n    param.requires_grad = True\n# Define optimizer and scheduler\noptimizer = torch.optim.Adam(model.parameters(), lr=0.0008, weight_decay=1e-4)\nlr_scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(\n    optimizer, mode='max', factor=0.5, patience=3, verbose=True, min_lr=1e-6\n)\n\n\n# Start training\nnum_epochs = 30  # Optional to reduce for faster testing\ntrain_losses, val_accuracies = train_and_val_with_lr_scheduler(\n    model, train_loader, val_loader,\n    loss_fn, optimizer, lr_scheduler,\n    num_epochs=num_epochs,\n    device=device\n)\nprint('\\nTest results for the baseline network:')\ntest(model, test_loader, device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-09T15:42:38.312101Z","iopub.execute_input":"2025-07-09T15:42:38.312391Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#  可视化","metadata":{}},{"cell_type":"code","source":"%matplotlib inline\n\nimport matplotlib.pyplot as plt\n\ndef plot_train_val(train_losses, val_accuracies):\n    epochs = range(1, len(train_losses) + 1)\n    fig, ax1 = plt.subplots()\n\n    color = 'tab:blue'\n    ax1.set_xlabel('Epoch')\n    ax1.set_ylabel('Train Loss', color=color)\n    ax1.plot(epochs, train_losses, color=color, label='Train Loss')\n    ax1.tick_params(axis='y', labelcolor=color)\n\n    ax2 = ax1.twinx()  # 创建第二个y轴\n    color = 'tab:red'\n    ax2.set_ylabel('Validation Accuracy (%)', color=color)\n    ax2.plot(epochs, val_accuracies, color=color, label='Val Accuracy')\n    ax2.tick_params(axis='y', labelcolor=color)\n\n    fig.tight_layout()\n    plt.title('Training Loss and Validation Accuracy')\n    plt.show()\nplot_train_val(train_losses, val_accuracies)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-09T14:21:17.989180Z","iopub.status.idle":"2025-07-09T14:21:17.989470Z","shell.execute_reply.started":"2025-07-09T14:21:17.989342Z","shell.execute_reply":"2025-07-09T14:21:17.989358Z"}},"outputs":[],"execution_count":null}]}