第 P4 周:猴痘病皮肤图像识别
第 P4 周:猴痘病皮肤图像识别
- 本文为 365 天深度学习训练营 中的学习记录博客
- 原作者:K同学啊
- 本篇为个人在 P4 关卡上的学习与实践记录
一、本周学习内容概览
在前面的关卡中,我们分别用 MNIST、CIFAR‑10 等「官方内置」数据集完成了手写数字和彩色图片分类。本周 P4 的任务进一步贴近真实场景:
使用公开的「Monkeypox vs Others」皮肤病图像数据集,搭建卷积神经网络完成二分类识别,并在 GPU 上训练与评估模型。
我这次主要做了以下几件事:
- 数据组织:在
4-data/目录下整理两类皮肤图像,按4-data/Monkeypox/*.jpg与4-data/Others/*.jpg的方式存放,方便ImageFolder自动识别类别。 - 数据加载:使用
pathlib.Path处理路径(避免split("\\")在 Linux 下引发的 bug),配合datasets.ImageFolder完成猴痘/其他皮肤病图像的读取。 - 数据预处理:将图片统一缩放到
224×224,并按照 ImageNet 常用的mean/std对 RGB 通道做标准化。 - 划分数据集:把完整数据集按
8:2比例随机划分为训练集和测试集,并用DataLoader组成 batch。 - 模型构建:实现了带有 BatchNorm 的卷积神经网络
Network_bn,网络包含多层卷积、批归一化和池化层,最后通过全连接层输出 2 维 logits。 - 训练与评估:编写
train/test函数,记录每个 epoch 的训练/测试准确率与损失;在 20 个 epoch 后,训练集准确率达到约 92.7%,测试集准确率约为 83.2%。
二、必要代码与关键点说明
1. 环境与设备检查
import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
from pathlib import Path
import matplotlib.pyplot as plt
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
device
import sys
print("我的环境:")
print("Python版本:", sys.version.split()[0])
print("PyTorch版本:", torch.__version__)
print("Torchvision版本:", torchvision.__version__)
print("CUDA:", torch.version.cuda if torch.cuda.is_available() else "未使用(CPU 模式)")
if torch.cuda.is_available():
print("GPU设备:", torch.cuda.get_device_name(0))
else:
print("GPU设备: 无(当前使用 CPU)")
print("-" * 40)
2. 数据集目录结构与路径处理
本次任务的数据集解压在 p4/4-data/ 目录下,结构大致如下:
4-data/
Monkeypox/
xxx1.jpg
xxx2.jpg
Others/
yyy1.jpg
yyy2.jpg
在 WSL / Linux 下,路径分隔符是 / 而不是 \,因此避免使用 split("\\") 去截路径,而是统一用 pathlib.Path:
import pathlib
data_dir = pathlib.Path("4-data")
data_paths = list(data_dir.glob("*"))
classNames = [p.name for p in data_paths] # ['Monkeypox', 'Others']
classNames
这里的 p.name 会自动返回末级目录名(如 Monkeypox、Others),不会受到操作系统路径分隔符差异的影响。
3. 使用 ImageFolder 与 DataLoader
利用 datasets.ImageFolder 可以直接按子文件夹名当作类别来读取图片,并结合 transforms 做预处理:
from torchvision import datasets
total_datadir = pathlib.Path("4-data")
train_transforms = transforms.Compose([
transforms.Resize([224, 224]),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std =[0.229, 0.224, 0.225]
),
])
total_data = datasets.ImageFolder(total_datadir, transform=train_transforms)
total_data, total_data.class_to_idx # {'Monkeypox': 0, 'Others': 1}
随后按 8:2 划分训练集与测试集,并构建 DataLoader(见下文第三节)。
4. CNN 模型结构与损失函数(本次实现)
本次没有使用预训练模型,而是自己搭建了一个带 BatchNorm 的小型 CNN Network_bn,核心结构如下:
import torch.nn.functional as F
class Network_bn(nn.Module):
def __init__(self):
super(Network_bn, self).__init__()
self.conv1 = nn.Conv2d(3, 12, kernel_size=5, stride=1, padding=0)
self.bn1 = nn.BatchNorm2d(12)
self.conv2 = nn.Conv2d(12, 12, kernel_size=5, stride=1, padding=0)
self.bn2 = nn.BatchNorm2d(12)
self.pool = nn.MaxPool2d(2, 2)
self.conv4 = nn.Conv2d(12, 24, kernel_size=5, stride=1, padding=0)
self.bn4 = nn.BatchNorm2d(24)
self.conv5 = nn.Conv2d(24, 24, kernel_size=5, stride=1, padding=0)
self.bn5 = nn.BatchNorm2d(24)
self.fc1 = nn.Linear(24 * 50 * 50, len(classNames))
def forward(self, x):
x = F.relu(self.bn1(self.conv1(x)))
x = F.relu(self.bn2(self.conv2(x)))
x = self.pool(x)
x = F.relu(self.bn4(self.conv4(x)))
x = F.relu(self.bn5(self.conv5(x)))
x = self.pool(x)
x = x.view(-1, 24 * 50 * 50)
x = self.fc1(x)
return x
device = "cuda" if torch.cuda.is_available() else "cpu"
model = Network_bn().to(device)
model

损失函数与优化器采用:
loss_fn = nn.CrossEntropyLoss()
learn_rate = 1e-4
opt = torch.optim.SGD(model.parameters(), lr=learn_rate)
三、训练与测试流程
1. 单轮训练函数(train)
def train(dataloader, model, loss_fn, optimizer):
size = len(dataloader.dataset)
num_batches = len(dataloader)
train_loss, train_acc = 0, 0
for X, y in dataloader:
X, y = X.to(device), y.to(device)
pred = model(X)
loss = loss_fn(pred, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_acc += (pred.argmax(1) == y).type(torch.float).sum().item()
train_loss += loss.item()
train_acc /= size
train_loss /= num_batches
return train_acc, train_loss
2. 测试函数(test)
def test(dataloader, model, loss_fn):
size = len(dataloader.dataset)
num_batches = len(dataloader)
test_loss, test_acc = 0, 0
with torch.no_grad():
for imgs, target in dataloader:
imgs, target = imgs.to(device), target.to(device)
target_pred = model(imgs)
loss = loss_fn(target_pred, target)
test_loss += loss.item()
test_acc += (target_pred.argmax(1) == target).type(torch.float).sum().item()
test_acc /= size
test_loss /= num_batches
return test_acc, test_loss
3. 主训练循环与日志

epochs = 20
train_loss = []
train_acc = []
test_loss = []
test_acc = []
for epoch in range(epochs):
model.train()
epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, opt)
model.eval()
epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)
train_acc.append(epoch_train_acc)
train_loss.append(epoch_train_loss)
test_acc.append(epoch_test_acc)
test_loss.append(epoch_test_loss)
template = ("Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, "
"Test_acc:{:.1f}%,Test_loss:{:.3f}")
print(template.format(
epoch + 1,
epoch_train_acc * 100, epoch_train_loss,
epoch_test_acc * 100, epoch_test_loss
))
print("Done")
在本次实验中,20 个 epoch 后的代表性结果为:
- 训练集:
Train_acc ≈ 92.7%,Train_loss ≈ 0.26 - 测试集:
Test_acc ≈ 83.2%,Test_loss ≈ 0.39

四、结果可视化与分析
1. 绘制训练 / 测试曲线
import matplotlib.pyplot as plt
import warnings
from datetime import datetime
warnings.filterwarnings(“ignore”)
plt.rcParams[“font.sans-serif”] = [“SimHei”]
plt.rcParams[“axes.unicode_minus”] = False
plt.rcParams[“figure.dpi”] = 100
current_time = datetime.now()
epochs_range = range(epochs)
plt.figure(figsize=(12, 3))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, train_acc, label=“Train Acc”)
plt.plot(epochs_range, test_acc, label=“Test Acc”)
plt.legend(loc=“lower right”)
plt.title(“Training and Validation Accuracy”)
plt.xlabel(current_time)
plt.subplot(1, 2, 2)
plt.plot(epochs_range, train_loss, label=“Train Loss”)
plt.plot(epochs_range, test_loss, label=“Test Loss”)
plt.legend(loc=“upper right”)
plt.title(“Training and Validation Loss”)
plt.tight_layout()
plt.show()
五、个人总结
环境方面:本次实验在 WSL2 + Linux 环境下完成,Python 版本为 3.11.13,PyTorch 为 2.5.1+cu121,Torchvision 为 0.20.1+cu121,当前硬件为 NVIDIA GeForce RTX 4060 Ti。一开始在路径与类别数上踩了几个坑(例如使用 split(“\”)、len(classeNames) 写错导致 out_features=0 和 CUDA 报错),后来统一改用 pathlib.Path 和 classNames 后就稳定了下来。
数据与模型方面:本次使用的是猴痘 vs 其他皮肤病的真实皮肤图像数据集,共约 2142 张图片,按 8:2 比例划分为训练集和测试集。使用自定义的 Network_bn 模型(多层卷积 + BatchNorm + 池化 + 全连接)在 20 个 epoch 后训练集准确率约 92.7%,测试集准确率约 83.2%,说明模型已经学到了一定的判别能力,但仍存在一定过拟合。
坑点与收获:除了前面提到的路径和类别映射问题,这次还再次巩固了「张量形状」与特征图尺寸推导的概念:从输入的 [B, 3, 224, 224] 经过多层卷积与池化,最终变成 [B, 24, 50, 50],再展平成 245050 接入全连接层。通过这些推导,更清楚了卷积核大小、步长、池化对特征尺寸的影响。
整体感受:P4 让我第一次在医学相关的小数据集上做二分类任务,感受到真实场景中数据量有限、类别可能不平衡、模型容易过拟合等问题。后面打算在当前基础上进一步尝试:引入预训练的 ResNet 进行迁移学习、在训练集中加入适度的数据增强,并微调优化器和正则化策略,看能否把测试集准确率继续往 90% 推进,同时重点关注 Monkeypox 这一类的召回率。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)