
简介这份资源是面向Python初学者与深度学习入门者的图像分类项目实战包基于Keras与CNN构建帮助读者从零跑通训练、验证到预测的完整流程。包内共9个文件以5个Python脚本为核心涵盖模型训练、精度验证、向量化处理与分类接口等模块另附requirements依赖清单、README说明文档及演示页面压缩包约10KB结构精简、便于快速上手。使用前只需在data目录下按分类数量建立train与val子文件夹并放入对应图片安装依赖后运行train.py即可训练再通过val.py指定图片路径验证模型精度同时支持网络调参与类别序号配置。目前已有362人学习关注适合希望掌握图像分类工程落地、理解数据组织与模型调参思路的课程学习者与开发者参考。1. 拿到一个图像分类项目源码包先别急着 pip install你从群里或者某个资源站下到一个基于Python实现图像分类项目源码文档说明.zip解压完看到一堆.py、一个requirements.txt、几个.md还有一两个不知道什么格式的权重文件。这时候最容易犯的错是直接pip install -r requirements.txt然后python train.py结果报错糊脸连数据从哪来都没搞清。图像分类项目的落地路径其实很固定先确认任务类型是猫狗二分类还是 ImageNet 千类再确认数据组织方式文件夹分目录还是 CSV 索引然后才是模型选型和训练。这个标题背后真正值钱的东西不是那几百行 Python而是「数据怎么进、模型怎么出、指标怎么看」这条链路。适合刚入门想跑通第一个分类项目的人也适合手里有源码但跑不起来、想搞清每一步在干什么的从业者。下面按我实际拆包的习惯从目录结构一路讲到训练和排错。2. 拆包先看三样东西目录结构、依赖、数据入口2.1 一个典型图像分类源码包的目录长什么样不同作者风格差异很大但能跑起来的项目目录结构基本逃不出这几种。我一般先tree -L 2或者直接看根目录重点找data、models、train.py、config这几个关键词。下面是一个常见布局你手里的包大概率是它的变体image-classification/ ├── configs/ │ └── default.yaml ├── data/ │ ├── train/ │ │ ├── cat/ │ │ └── dog/ │ └── val/ │ ├── cat/ │ └── dog/ ├── models/ │ └── resnet.py ├── utils/ │ ├── dataset.py │ └── metrics.py ├── train.py ├── predict.py ├── requirements.txt └── README.md看到data/train/cat、data/train/dog这种结构说明用的是torchvision.datasets.ImageFolder那一套类别名就是文件夹名标签按文件夹字母序自动编号。这是最常见的做法好处是零配置坏处是类别顺序不可控cat和dog谁先谁后取决于字母序预测时映射错就全乱。如果看到data/train.csv加一列filename,label那是 CSV 索引式适合多标签或类别名不规范的情况。提示先别改代码把README.md从头到尾读一遍重点看「数据准备」和「训练命令」两节。很多包跑不起来就是因为跳过了这两段。2.2 依赖安装requirements.txt 不是万能药requirements.txt里通常写着torch、torchvision、numpy、pillow、tqdm这些。直接pip install -r requirements.txt在干净环境里大概率能装上但有两个坑一是没写版本号装到最新版可能和源码里的 API 对不上二是torch的 CUDA 版本和你的显卡驱动不匹配装完torch.cuda.is_available()返回False。我一般先建虚拟环境再手动装 torch最后装其余依赖python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate # 先确认 CUDA 版本再决定装哪个 torch nvidia-smi # 看右上角 CUDA Version pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install -r requirements.txtnvidia-smi右上角的 CUDA Version 是驱动支持的最高版本不是你必须装的版本。装比它低的 CUDA 版本 torch 都能跑。如果没显卡把cu121换成cpu。装完立刻验证import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else CPU only)这三行输出决定了你后面训练是几分钟一轮还是几小时一轮。is_available()为False时别急着怀疑代码九成是 torch 装成了 CPU 版。2.3 数据入口dataset.py 里藏着标签映射打开utils/dataset.py找ImageFolder或者自定义Dataset类。重点看两件事图像预处理用了哪些 transform以及类别到索引的映射怎么存的。典型代码from torchvision import datasets, transforms train_transform transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) train_dataset datasets.ImageFolder(data/train, transformtrain_transform) print(train_dataset.classes) # [cat, dog] print(train_dataset.class_to_idx) # {cat: 0, dog: 1}Resize((224, 224))是因为后面要接 ResNet 这类在 ImageNet 上预训练的模型输入尺寸固定 224。Normalize里的均值方差是 ImageNet 统计出来的用预训练权重就必须用这套自己随便改会让预训练特征失效。class_to_idx一定要打印出来存好预测阶段要把索引映射回类别名映射错了模型再准也白搭。注意如果data/train下还有一层没用的文件夹比如解压多套了一层ImageFolder会把那层当类别类别数直接翻倍训练时 loss 不降就是这个原因。3. 模型选型与训练从 ResNet 到 Transformer 图像分类3.1 小数据集别硬上大模型先跑通 ResNet18源码包里models/下常见的是 ResNet、VGG、MobileNet 这几种。如果你的数据只有几千张别一上来就上 ViT 或者 Swin Transformer参数量大、需要的数据量也大小数据上很容易过拟合训练半天还不如 ResNet18。我一般先用 ResNet18 跑通全流程确认数据管道没问题再换大模型对比。import torch.nn as nn from torchvision import models def build_model(num_classes, pretrainedTrue): model models.resnet18(weightsmodels.ResNet18_Weights.DEFAULT if pretrained else None) # 替换最后的全连接层输出类别数改成自己的 in_features model.fc.in_features model.fc nn.Linear(in_features, num_classes) return model model build_model(num_classes2) print(sum(p.numel() for p in model.parameters()) / 1e6, M params)weightsResNet18_Weights.DEFAULT是较新版本 torchvision 的写法老版本用pretrainedTrue。替换model.fc是因为原版输出 1000 类你的任务可能只有 2 类或 10 类。只替换最后一层、冻结前面层是小数据集微调的标准做法数据量够大每类上千张时再解冻全部层一起训。3.2 训练循环里必须盯住的四个量训练脚本train.py里核心就是一个 epoch 循环。不管作者写得多花哨你只要盯住四个量训练 loss、训练 acc、验证 loss、验证 acc。典型循环for epoch in range(num_epochs): model.train() for imgs, labels in train_loader: imgs, labels imgs.to(device), labels.to(device) optimizer.zero_grad() outputs model(imgs) loss criterion(outputs, labels) loss.backward() optimizer.step() # 验证阶段 model.eval() with torch.no_grad(): for imgs, labels in val_loader: imgs, labels imgs.to(device), labels.to(device) outputs model(imgs) val_loss criterion(outputs, labels)model.train()和model.eval()必须成对出现前者开启 Dropout 和 BatchNorm 的训练行为后者关掉。忘了写model.eval()验证指标会抖得厉害这是新手最常见的玄学来源。optimizer.zero_grad()不能省否则梯度会累加。torch.no_grad()在验证时省显存不加也能跑但没必要浪费。判断训练是否正常看四条曲线训练 loss 稳定下降、验证 loss 先降后升说明过拟合、两个 loss 都不降说明学习率太大或数据有问题、训练 acc 高但验证 acc 低说明过拟合或数据泄漏。学习率一般从1e-3或1e-4起步用 Adam 或 SGDSGD 配 momentum 0.9 是经典组合。3.3 最新的图像分类模型怎么接进来热搜里常出现 transformer 图像分类、最新的图像分类模型说明不少人想把手里的 ResNet 换成 ViT 或 ConvNeXt。torchvision 从 0.13 起就内置了这些接口和 ResNet 一致from torchvision import models # ViT-B/16输入必须是 224 vit models.vit_b_16(weightsmodels.ViT_B_16_Weights.DEFAULT) vit.heads.head nn.Linear(vit.heads.head.in_features, num_classes) # ConvNeXt-Tiny同样 224 输入 convnext models.convnext_tiny(weightsmodels.ConvNeXt_Tiny_Weights.DEFAULT) convnext.classifier[2] nn.Linear(convnext.classifier[2].in_features, num_classes)注意替换的层名不一样ViT 是heads.headConvNeXt 是classifier[2]ResNet 是fc。换模型时先print(model)看清结构再改别照搬。ViT 对数据量要求高小数据集上要么冻结主干只训 head要么用强增强RandAugment、MixUp。ConvNeXt 介于两者之间小数据上比 ViT 稳。提示换模型后输入尺寸、归一化参数可能变。ViT 和 ConvNeXt 都用 ImageNet 的 224 和那套均值方差和 ResNet 一致所以 transform 不用大改。4. 训练跑完不算完评估、预测与文档说明怎么用4.1 混淆矩阵比 accuracy 更能说明问题accuracy在类别不平衡时会骗人。九成样本是 A 类模型全预测 A 也有 90% 准确率但 B 类一个没学到。跑完训练一定要看混淆矩阵from sklearn.metrics import confusion_matrix, classification_report import numpy as np all_preds, all_labels [], [] model.eval() with torch.no_grad(): for imgs, labels in val_loader: outputs model(imgs.to(device)) preds outputs.argmax(dim1).cpu().numpy() all_preds.extend(preds) all_labels.extend(labels.numpy()) print(confusion_matrix(all_labels, all_preds)) print(classification_report(all_labels, all_preds, target_namestrain_dataset.classes))classification_report会给出每个类别的 precision、recall、f1。如果某个类 recall 特别低说明模型把它和别的类搞混了回去看混淆矩阵是哪两类在互相误判再针对性补数据或调权重。target_names用train_dataset.classes保证索引和类别名对得上。4.2 单张图片预测把索引映射回类别名predict.py通常封装了单图推理。核心是把训练时的 transform 原样搬过来不能少Normalizefrom PIL import Image import torch def predict(image_path, model, class_names, devicecuda): transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) img Image.open(image_path).convert(RGB) tensor transform(img).unsqueeze(0).to(device) model.eval() with torch.no_grad(): logits model(tensor) probs torch.softmax(logits, dim1) conf, idx probs.max(dim1) return class_names[idx.item()], conf.item()convert(RGB)处理灰度图或带 alpha 通道的 PNG不加这步遇到四通道图会报错。unsqueeze(0)加 batch 维度模型要的是[N, C, H, W]。softmax把 logits 转成概率max同时拿到置信度和索引。置信度低于 0.6 的结果我一般会人工复核尤其是医疗、工业质检这类场景。4.3 文档说明里真正该看的部分源码包里的文档说明或README.md别只看安装步骤。重点找三块数据集来源和类别数、训练超参epoch、lr、batch size、复现指标在验证集上的 accuracy。如果文档写了「在某某数据集上达到 95%」你要确认自己的数据分布和它是否一致不一致就别指望同样指标。文档里没写的去configs/default.yaml里找超参一般都在那。注意文档里的指标如果是作者在自己划分的验证集上跑的换一批数据掉点很正常。别拿文档数字当基线自己跑一遍拿到真实基线再优化。5. 避坑与排查跑不起来时按这个顺序查5.1 报错 CUDA out of memory现象训练刚开始就RuntimeError: CUDA out of memory。原因batch size 太大或者模型和输入尺寸不匹配。解决先把 batch size 减半还不行就减到 8 或 4再不行把输入从 224 降到 128但注意预训练模型对输入尺寸敏感降太多掉点。另外检查是不是忘了torch.no_grad()验证阶段显存会翻倍。5.2 loss 一直是 nan现象训练几个 step 后 loss 变成 nan。原因学习率太大、数据里有损坏图片、或者归一化没做。解决学习率降到1e-4甚至1e-5写个脚本遍历数据集用 PIL 打开每张图打不开的删掉确认 transform 里有Normalize。数据里混进一张 0 字节的图整个训练就废了这是血泪经验。5.3 验证准确率卡在类别数分之一现象二分类任务验证 acc 一直在 0.5 附近多分类在1/num_classes附近。原因标签映射错了或者数据加载时标签和图像对不上。解决打印一个 batch 的labels和对应图像路径肉眼确认检查ImageFolder的目录结构是不是多套了一层CSV 索引式的话检查文件名和标签列有没有错位。5.4 训练 acc 高但预测时全错现象训练和验证指标都很好但拿新图预测结果离谱。原因预测时的 transform 和训练时不一致最常见的是漏了Normalize或用了不同的 Resize 尺寸。解决把训练时的 transform 定义抽成一个函数训练和预测共用别各写一份。另一个原因是class_to_idx没保存预测时索引映射反了。5.5 pip 装完 import torch 报 DLL 错误现象Windows 上import torch报OSError: [WinError 126]找不到指定模块。原因缺 Visual C 运行库或者 torch 版本和 Python 版本不匹配。解决装最新的 VC redistributable确认 Python 是 64 位用 conda 装 torch 通常能自动处理依赖比 pip 省心。6. 把项目改成自己的迁移学习微调的三个技巧跑通原版之后真正有价值的是把它改成你自己的分类任务。我一般按这个顺序做先冻结主干只训分类头 5 个 epoch再解冻最后两个 stage 用低学习率微调最后全解冻用更小的学习率收尾。这样比一上来全量微调稳小数据上尤其明显。# 阶段一只训分类头 for param in model.parameters(): param.requires_grad False for param in model.fc.parameters(): param.requires_grad True optimizer torch.optim.Adam(model.fc.parameters(), lr1e-3) # 阶段二解冻 layer4用低学习率 for param in model.layer4.parameters(): param.requires_grad True optimizer torch.optim.Adam([ {params: model.fc.parameters(), lr: 1e-3}, {params: model.layer4.parameters(), lr: 1e-4}, ])分组学习率是关键新加的分类头用大学习率预训练的主干用小学习率避免把学好的特征冲掉。requires_grad控制哪些层参与更新冻结的层不计算梯度省显存也省时间。数据增强上小数据集我必开RandomHorizontalFlip、RandomRotation(15)、ColorJitter。再往上可以试RandAugment但别一上来就堆增强太猛反而学不动。验证集绝对不做增强只做 Resize 和 Normalize否则指标不可比。最后一个习惯每次实验都把配置、指标、混淆矩阵存到一个带时间戳的文件夹里。我吃过亏跑了十几组实验回头分不清哪组对应哪个参数只能重跑。现在固定用runs/20250101_120000/这种命名里面放config.yaml、metrics.json、confusion_matrix.png。这个习惯比任何调参技巧都值钱。希望帮到你。本文还有配套的精品资源点击获取