ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AlexNet图像分类实战:从数据划分到模型推理的完整指南

AlexNet图像分类实战:从数据划分到模型推理的完整指南 简介这份资源是围绕经典卷积神经网络AlexNet的图像分类实践包面向深度学习入门者、计算机视觉方向学生以及需要复现经典模型的开发者帮助解决从数据准备到模型训练、推理的完整流程搭建问题。压缩包共24413个文件以24407张jpg图像数据为主体辅以4个Python脚本和2个Markdown说明文档整体约749.04MB规模适合直接开展训练与验证。其中脚本覆盖模型结构定义、训练循环、数据集划分与预测推理等环节README文档则提供环境配置与使用指引便于快速理解项目组织方式。已有195人学习关注读者可借此掌握AlexNet的网络架构细节、数据预处理流程以及训练与预测脚本的编写思路并基于现成图像数据完成一次端到端的图像分类实践适合作为深度学习入门与课程实验的参考素材。1. 从一份 alex-net-image.rar 说起它到底能跑出什么结果如果你手里正好有一批按数字命名的图片比如10404.jpg、10073.jpg、8767.jpg这种又想把 AlexNet 这套经典卷积网络真正跑起来而不是停在「我看过论文」的阶段那这份alex-net-image.rar值得拆开看看。它不是一个空壳 demo里面按常规工程结构放了train.py、model.py、split_data.py、predict.py、save_model目录以及中英文两份 README。换句话说从数据划分、模型定义、训练、保存权重到单张推理这条链路是齐的。它适合两类人一类是刚学完卷积基础、想找一个结构完整又不臃肿的项目练手的人另一类是手头有自己的一批图片想拿 AlexNet 当 baseline 快速验证分类效果的人。不适合指望它直接产出 SOTA 精度的人——AlexNet 是 2012 年的结构它的价值在于结构清晰、参数量可控、单卡能跑通而不是刷榜。下面我按「先看清结构、再动手跑、最后避坑」的顺序把这份资源拆到能复现的程度。2. 拆开压缩包先看结构model.py 与 train.py 怎么配合2.1 目录里每个文件负责什么拿到压缩包别急着python train.py。先花五分钟把文件职责理清楚后面报错时你才知道该翻哪个文件。按这类项目的常见组织方式各文件分工大致如下文件/目录作用你该关注的点README.md/README.en.md环境依赖与运行说明先看依赖版本尤其是 torch 版本model.pyAlexNet 网络结构定义输入尺寸、分类数、是否用预训练权重train.py训练主循环学习率、batch size、epoch、设备选择split_data.py数据集划分训练/验证比例、划分是否随机predict.py单张或批量推理预处理是否和训练一致save_model/保存权重权重文件名与加载路径是否对应alex与 AlexNet 相关的附加资源可能是配置或结构变体按实际内容判断这里有个血泪经验很多人一上来就改train.py的学习率结果发现模型根本不收敛最后查出来是split_data.py把同一批图同时分进了训练集和验证集验证精度虚高。所以顺序应该是先确认数据划分再动训练参数。2.2 model.py 里的 AlexNet 结构长什么样AlexNet 的经典结构是 5 个卷积层加 3 个全连接层卷积部分负责提取边缘、纹理、部件等特征全连接部分负责分类。这份资源里的model.py大概率是按这个骨架写的。你需要重点确认三件事输入图像尺寸、最后的分类类别数、有没有加载预训练权重。下面给出一段符合该结构的参考实现你可以拿它和包里的model.py对照import torch import torch.nn as nn class AlexNet(nn.Module): def __init__(self, num_classes1000): super(AlexNet, self).__init__() # 特征提取5 个卷积层逐步扩大通道、缩小空间尺寸 self.features nn.Sequential( nn.Conv2d(3, 64, kernel_size11, stride4, padding2), # 输入 224x224 - 55x55 nn.ReLU(inplaceTrue), nn.MaxPool2d(kernel_size3, stride2), # - 27x27 nn.Conv2d(64, 192, kernel_size5, padding2), nn.ReLU(inplaceTrue), nn.MaxPool2d(kernel_size3, stride2), # - 13x13 nn.Conv2d(192, 384, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.Conv2d(384, 256, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.Conv2d(256, 256, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.MaxPool2d(kernel_size3, stride2), # - 6x6 ) # 分类头3 个全连接层 self.classifier nn.Sequential( nn.Dropout(), nn.Linear(256 * 6 * 6, 4096), nn.ReLU(inplaceTrue), nn.Dropout(), nn.Linear(4096, 4096), nn.ReLU(inplaceTrue), nn.Linear(4096, num_classes), ) def forward(self, x): x self.features(x) x torch.flatten(x, 1) # 展平成 (batch, 256*6*6) x self.classifier(x) return x逻辑说明features负责卷积特征提取classifier负责分类。num_classes是最关键的参数——如果你自己的数据集只有 5 类就必须把它改成 5否则最后一层输出维度和标签对不上训练直接报维度错误。torch.flatten(x, 1)从第 1 维开始展平保留 batch 维这是接全连接层前的标准动作。参数说明卷积核kernel_size11, stride4是 AlexNet 原论文为 224 输入设计的如果你把输入改成 128特征图尺寸会变256*6*6这个全连接输入维度就得跟着重算。常见做法是保持 224 输入不动省去改结构的麻烦。2.3 train.py 的训练循环该盯哪几个参数train.py是真正干活的地方。它一般包含设备选择、数据集加载、模型实例化、损失函数、优化器、训练循环、验证循环、权重保存。下面这段是这类项目的典型骨架import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from model import AlexNet device torch.device(cuda if torch.cuda.is_available() else cpu) # 1. 模型与分类数num_classes 必须和你的数据集类别数一致 model AlexNet(num_classes5).to(device) # 2. 损失函数多分类用交叉熵 criterion nn.CrossEntropyLoss() # 3. 优化器AlexNet 常用 SGD 动量学习率别一上来就太大 optimizer optim.SGD(model.parameters(), lr0.01, momentum0.9, weight_decay5e-4) # 4. 训练循环 for epoch in range(30): model.train() running_loss 0.0 for images, labels in train_loader: images, labels images.to(device), labels.to(device) optimizer.zero_grad() # 梯度清零别漏 outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() print(fepoch {epoch1}, loss {running_loss/len(train_loader):.4f}) # 5. 保存权重 torch.save(model.state_dict(), save_model/alexnet.pth)逻辑说明optimizer.zero_grad()必须在backward()之前否则梯度会累加这是新手最常见的翻车点之一。model.train()和后面的model.eval()切换影响 Dropout 和 BatchNorm 行为验证阶段忘了切eval()会让结果不稳定。参数说明lr0.01配 SGD 是 AlexNet 时代的经典组合但如果你用了预训练权重做微调学习率通常要降到0.001甚至更低。momentum0.9帮助跳出局部极小weight_decay5e-4是轻量正则。batch size受显存限制8GB 显存下 32 或 64 比较稳。3. 把数据喂进去split_data.py 的划分逻辑与预处理3.1 数据划分为什么不能随手切split_data.py看着简单但它决定了你后面所有指标可不可信。常见做法是按 8:1:1 或 7:2:1 划分训练、验证、测试集。这里有个容易被忽略的点如果你的图片是按类别放在不同子文件夹里的划分时必须保证每个类别都按比例进入三个集合否则某个类别可能全进了训练集验证集里一张都没有指标就没意义了。下面是一段按类别分层划分的参考脚本import os import random import shutil def split_dataset(src_dir, dst_dir, train_ratio0.8, val_ratio0.1): # src_dir 下每个子文件夹是一个类别 classes os.listdir(src_dir) for cls in classes: cls_dir os.path.join(src_dir, cls) images os.listdir(cls_dir) random.shuffle(images) # 打乱避免按文件名顺序切 n len(images) n_train int(n * train_ratio) n_val int(n * val_ratio) subsets { train: images[:n_train], val: images[n_train:n_train n_val], test: images[n_train n_val:], } for subset, files in subsets.items(): out_dir os.path.join(dst_dir, subset, cls) os.makedirs(out_dir, exist_okTrue) for f in files: shutil.copy(os.path.join(cls_dir, f), os.path.join(out_dir, f)) split_dataset(raw_data, dataset)逻辑说明先random.shuffle再切是为了避免原始文件按类别或时间有序排列导致分布偏移。按类别循环保证每个类别都独立划分这就是分层抽样的朴素实现。参数说明train_ratio、val_ratio按数据量调整。数据量小的时候比如每类几十张验证集比例可以提到 0.2测试集留 0.1 即可。数据量上万时8:1:1 足够。3.2 训练时的图像预处理要和推理对齐训练和推理的预处理必须一致否则会出现「训练精度很高、预测一塌糊涂」的玄学现象。常见做法是训练阶段做随机裁剪、翻转增强验证和推理阶段只做缩放和归一化。下面这段是标准写法from torchvision import transforms # 训练带数据增强 train_tf 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]), # ImageNet 统计值 ]) # 验证/推理不做随机增强 val_tf transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ])逻辑说明Normalize用的均值方差是 ImageNet 统计出来的如果你用了预训练权重就必须用这组值否则输入分布和权重预期不匹配。RandomHorizontalFlip只在训练时开验证和推理时开会让同一张图每次结果不同。参数说明Resize((224, 224))对应 AlexNet 的输入要求。如果你的原图长宽比差异很大直接 resize 会变形常见做法是先Resize(256)再CenterCrop(224)但这份资源里如果没写就按 resize 走先跑通再说。4. 训练跑起来之后predict.py 推理与 save_model 权重管理4.1 权重保存与加载的路径要对上save_model目录是存权重的地方。训练脚本里torch.save(model.state_dict(), save_model/alexnet.pth)存的是参数字典推理时就必须用model.load_state_dict()加载而不是torch.load()直接当模型用。这两者搞混是高频翻车点。参考写法import torch from model import AlexNet device torch.device(cuda if torch.cuda.is_available() else cpu) model AlexNet(num_classes5).to(device) # 加载权重先实例化结构再灌参数 state_dict torch.load(save_model/alexnet.pth, map_locationdevice) model.load_state_dict(state_dict) model.eval() # 推理前必须切 eval逻辑说明map_locationdevice让权重能跨设备加载比如在 GPU 上训练、在 CPU 上推理时不会报错。model.eval()关闭 Dropout 并固定 BatchNorm 统计量保证推理结果可复现。参数说明num_classes必须和训练时一致否则load_state_dict会报 size mismatch。如果只想加载部分层做微调可以用strictFalse但要知道自己在干什么。4.2 predict.py 单张推理的完整流程推理脚本一般做四件事读图、预处理、前向、取最大概率类别。下面这段可以直接对照包里的predict.pyfrom PIL import Image import torch from torchvision import transforms from model import AlexNet class_names [class0, class1, class2, class3, class4] # 换成你的类别名 def predict(image_path): model AlexNet(num_classeslen(class_names)) model.load_state_dict(torch.load(save_model/alexnet.pth, map_locationcpu)) model.eval() tf 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) # 统一转 RGB防灰度图报错 x tf(img).unsqueeze(0) # 加 batch 维 with torch.no_grad(): # 推理不需要梯度 out model(x) prob torch.softmax(out, dim1) idx torch.argmax(prob, dim1).item() return class_names[idx], prob[0][idx].item() print(predict(10404.jpg))逻辑说明convert(RGB)处理灰度图或带 alpha 通道的图避免通道数不匹配。unsqueeze(0)把单张图变成 batch1 的输入。torch.no_grad()省显存、加速推理。参数说明class_names的顺序必须和训练时标签映射一致这个映射通常由ImageFolder按文件夹名字母序生成改错顺序会让预测结果整体错位。5. 避坑与排查这几处不查清楚跑十遍也白搭5.1 现象loss 一直是 nan 或剧烈震荡原因学习率过大或者输入没有归一化像素值在 0-255 直接进网络。解决确认Normalize已加学习率从 0.01 往下调先跑几个 epoch 看 loss 是否平稳下降。5.2 现象验证精度远高于训练精度原因数据划分时训练集和验证集有重叠或者验证集太小且恰好都是简单样本。解决检查split_data.py是否对同一张图重复复制确认划分前做了 shuffle验证集比例别低于 10%。5.3 现象load_state_dict 报 size mismatch原因num_classes和训练时不一致或模型结构改过。解决确认推理脚本里的类别数和训练时完全相同结构定义和model.py保持一致。5.4 现象显存爆了报 CUDA out of memory原因batch size 太大或输入尺寸被改大。解决把 batch size 降到 16 或 8确认输入是 224 而不是 448推理时加torch.no_grad()。5.5 现象预测结果全是同一类原因权重没加载成功路径错或加载方式错或类别极度不平衡导致模型退化成多数类预测。解决打印state_dict的 key 确认加载成功检查各类别样本数量是否悬殊。6. 进阶技巧用预训练权重把收敛速度提上来AlexNet 从零训练在小数据集上很容易过拟合一个实用技巧是加载预训练权重做微调。常见做法是只替换最后一层全连接让分类数匹配你的数据集其余层用预训练参数初始化学习率调小。这样通常几个 epoch 就能看到明显效果比从零训练省一半以上时间。import torch import torch.nn as nn from torchvision import models # 加载 torchvision 自带的 alexnet 预训练权重 model models.alexnet(weightsmodels.AlexNet_Weights.DEFAULT) # 替换最后一层匹配自己的类别数 num_features model.classifier[6].in_features model.classifier[6] nn.Linear(num_features, 5) # 微调时学习率调小只对分类头用大一点的学习率也可以 optimizer torch.optim.SGD([ {params: model.features.parameters(), lr: 1e-4}, {params: model.classifier.parameters(), lr: 1e-3}, ], momentum0.9)逻辑说明models.alexnet(weights...)拿到的是在 ImageNet 上训练好的权重特征提取层已经学到了通用纹理和形状。替换classifier[6]是因为它是最后一层分类层输出维度必须等于你的类别数。分组设置学习率是让底层特征小幅调整、分类头快速适配。参数说明lr1e-4给特征层1e-3给分类头是微调的常见起点。如果你的数据集和 ImageNet 差异很大比如医学影像可以把特征层学习率再调大一点或者解冻更多层。验证微调是否生效最直接的办法是看第一个 epoch 的验证精度如果从零训练第一个 epoch 精度在随机猜测附近而微调后直接跳到 60% 以上说明预训练权重起作用了。另一个技巧是保存每个 epoch 的最优权重而不是最后一个避免过拟合后精度回落。从那以后我每次拿到这类项目都强制先跑一遍split_data.py并打印三个集合的样本数确认没有重叠再动训练参数。这个习惯帮我省下了大量排查「精度虚高」的时间。希望这份拆解能帮你把alex-net-image.rar真正跑通而不是让它躺在硬盘里吃灰。本文还有配套的精品资源点击获取
返回列表