尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
CNN图像分类实战:从数据准备到迁移学习的完整PyTorch流程
从标题就能看出来这已经是我“多模态大模型学习笔记”系列的第十篇了。前九篇大多在讲理论、架构和各类模态对齐的思路但说实话学深度学习光看公式和结构图总觉得隔着一层纱真正把代码跑起来、看着损失降下去、把准确率调上来才算是把知识焊死在脑子里。所以这一篇不做花哨的多模态融合也不聊大模型推理就踏踏实实做一次基于卷积神经网络的图像分类实战用一套完整可复现的流程把CNN从数据准备到模型部署的整个链条走通。这篇笔记适合两类人看一类是像我一样在啃多模态但视觉基础还不牢的另一类是刚入门深度学习、想看看一个正经图像分类项目到底长什么样子的新手。我会把选型理由、参数计算、踩坑记录都写在里面尽量让读者看完能直接照着跑。1. 整体设计与思路拆解1.1 为什么在多模态系列里插入CNN实战很多人会问你写多模态大模型怎么突然插进来一篇纯视觉的CNN实战笔记其实恰恰是因为多模态模型里视觉分支占据的位置太重要了。无论是CLIP那种图文对齐模型还是以视觉编码器为基础的各种跨模态架构底层几乎都跑不脱卷积神经网络那一套特征提取逻辑。多模态大模型想理解图像第一步永远是把像素变成有语义的特征向量而这个特征提取器当前主流方案要么是CNN要么是Vision Transformer。ViT的很多训练技巧、数据增强策略、优化器选择其实都继承自CNN时代的经验。所以把CNN图像分类吃透对理解多模态模型里视觉分支的输入输出格式、特征层次、迁移学习行为都有直接帮助。另一方面实战本身就是最好的学习路径。我之前也试过一上来就啃ViT的MAE预训练、对比学习这些前沿方向但如果没有CNN打底很多概念理解起来等于空中楼阁。比如位置编码、patch embedding、分层特征金字塔这些术语如果没见过CNN里卷积核扫过图像产生特征图的过程很难建立直观认识。所以我坚持在系列中间插入这样一篇实操笔记既是对自己学习进度的检验也是给读者一个缓冲落地的机会。1.2 数据集与模型选型考量实战第一步是定数据集。我选了CIFAR-10原因很现实它足够小单张图片32x32像素整个数据集训练集加测试集也就不到200MB普通笔记本的CPU都能跑有独立显卡的话更是毫无压力。同时CIFAR-10又是图像分类领域最标准的benchmark之一网上参考资源多指标可比性强不容易出现“我这个结果到底算好还是算坏”的迷茫感。相比之下ImageNet虽然更权威但完整下载和训练门槛太高不适合作为一篇学习笔记的实战对象。数据集选完模型选型也要讲究。我这次没有直接用torchvision里封装好的ResNet18或VGG16而是从一个简化的自定义CNN开始。原因是我希望大家能看清卷积层、池化层、全连接层到底是怎么衔接的。用封装好的模型一行代码就实例化了内部参数怎么流动、特征图尺寸怎么变化全被黑盒挡住学完还是不会自己搭网络。自定义模型就不一样每一层的输入输出通道数、卷积核大小、步长、填充都必须自己算清楚跑通一遍之后再回去看ResNet的源码瞬间就通透了。1.3 环境配置与框架选型环境这块我踩过一次大坑这里必须先说。PyTorch的版本和CUDA版本必须匹配否则明明装好了GPU版torch运行的时候却提示CUDA不可用。我的建议是直接用Anaconda创建一个干净的虚拟环境不要图省事装在base环境里。命令很简单conda create -n cnn_learning python3.9 conda activate cnn_learning pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118这个命令是给CUDA 11.8用户准备的如果你的显卡驱动版本较新也可以把cu118换成cu121。怎么确认自己的CUDA版本在终端输入nvidia-smi看右上角CUDA Version那一行就行。没有NVIDIA显卡的读者也不用灰心CPU版本照样能跑通本文全部代码只是训练时间会长一些后面我会专门说CPU训练时怎么调整参数。pip install torch torchvision matplotlib numpy tqdm另外建议装一下tensorboard或者直接使用tqdm打印训练日志前者适合看曲线后者适合监控实时loss。我这次两种都用了个人更推荐tqdm加简单的loss打印够用且不折腾。2. 核心细节解析与实操要点2.1 卷积层到底在算什么CNN的核心是卷积操作。初学者最容易困惑的就是卷积层输出的特征图尺寸是怎么算出来的。公式其实很简单输出尺寸 (输入尺寸 - 卷积核大小 2 * 填充) / 步长 1以CIFAR-10的32x32输入为例第一层我用3x3卷积核、padding1、stride1那么输出尺寸就是(32 - 3 2*1) / 1 1 32尺寸不变。如果不用padding输出就变成30x30特征图直接缩水。这就是为什么现代网络普遍喜欢padding1的3x3卷积因为它能在不改变空间尺寸的前提下增加通道数、提取特征。后面我设计网络时通道数逐层翻倍32 - 64 - 128符合经验法则——空间尺寸减半通道数加倍这样参数量和计算量能保持相对平衡。关于卷积核大小我见过不少新手上来就堆7x7、5x5的大卷积核。不是说不能用而是小卷积核堆叠有理论优势。两个3x3卷积叠加感受野等于一个5x5卷积但参数量只有后者的18/25而且中间多了一层非线性激活表达能力更强。这是VGG系列验证过的思路自己设计小网络时也用得上。2.2 池化与激活函数不是可以随便省的东西池化层的作用经常被低估。很多人觉得池化不就是降采样吗随便用个步长为2的卷积也能替代。确实可以但最大池化还有个隐藏作用提供平移不变性。图像里物体稍微偏移几个像素最大池化因为取窗口内最大值响应基本不受影响这个特性对分类任务很重要。我的网络里在每两个卷积层之后接一个2x2、步长为2的最大池化特征图从32x32降到16x16再降到8x8最后接全局平均池化变成一维向量送进全连接层。激活函数我统一用ReLU有几个注意点想强调。ReLU在负区域的梯度是0所以如果学习率设太大可能会出现大量神经元“死亡”即输出恒为0。实操中我会监控训练集acc是否突然掉到接近随机水平一旦出现优先降低学习率。另外虽然现在GELU、SiLU这些激活函数很流行但对于CIFAR-10这种小数据集上的小网络ReLU完全够用稳定性和收敛速度都令人满意。2.3 Batch Size与学习率的配套调节训练超参里Batch Size和学习率的关系是最容易翻车的地方。它们的联动逻辑是Batch Size越大每个batch的梯度越平滑方差越小所以可以承受更大的学习率Batch Size越小梯度噪声大学习率就得调小否则训练过程会震荡。我这次把Batch Size设为64学习率初始设为0.001使用Adam优化器。如果显存充足Batch Size调到128学习率可以相应提到0.002左右。想验证这个配比是否合适最简单的方法是观察前几个epoch的loss曲线。正常情况应该是稳步下降如果出现剧烈震荡或者loss直接变成NaN大概率是学习率偏大。有个实操小技巧把初始学习率设成0.001后先用50个step跑一下如果50步内loss比初始值高直接减半重来。2.4 数据增强不是锦上添花而是必需品CIFAR-10训练集有50000张图听起来不少但CNN的参数量动辄几十万上百万这点数据量根本不够看。数据增强就是解决这个问题最直接的手段。我用的组合是随机水平翻转加随机裁剪加标准化。随机水平翻转好理解图片左右翻转不改变类别语义随机裁剪我做了个padding4的操作先把图片从32x32扩到40x40再随机裁回32x32相当于给模型看更多局部信息标准化则是把RGB三通道的像素值减去均值除以标准差让数据分布落在激活函数的敏感区间数值稳定性会好很多。训练集和测试集必须用同一套标准化参数这一点经常有人忽略。如果在训练集上统计的均值和标准差测试集却用了另一套相当于测试时数据分布跟训练时不一致结果根本不可信。正确做法是先在整个训练集上算好三个通道的mean和std训练和测试都用这一组。3. 实操过程与核心环节实现3.1 数据准备与可视化代码敲起来之前先把数据下载好的习惯一定要有。torchvision里自带CIFAR-10数据集第一次运行会自动下载但如果网络不给力很容易下到一半断掉。我的建议是直接用脚本下载完整包再放到指定目录让torchvision去读避免反复断点续传的折磨。import torch import torchvision import torchvision.transforms as transforms from torch.utils.data import DataLoader transform_train transforms.Compose([ transforms.RandomHorizontalFlip(), transforms.RandomCrop(32, padding4), transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)), ]) transform_test transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)), ]) trainset torchvision.datasets.CIFAR10(root./data, trainTrue, downloadTrue, transformtransform_train) testset torchvision.datasets.CIFAR10(root./data, trainFalse, downloadTrue, transformtransform_test) trainloader DataLoader(trainset, batch_size64, shuffleTrue, num_workers2) testloader DataLoader(testset, batch_size64, shuffleFalse, num_workers2)这里num_workers2是Windows上的保守设定Linux机器可以调到4或8。数据加载是训练过程中最容易被忽略的瓶颈num_workers太小会导致GPU等着CPU喂数据训练效率大幅下降。加载完数据之后我习惯先画几张图看看增强效果确认图片没被过度扭曲。import matplotlib.pyplot as plt import numpy as np dataiter iter(trainloader) images, labels next(dataiter) # 反标准化看原图 images images * torch.tensor((0.2023, 0.1994, 0.2010)).view(1, 3, 1, 1) torch.tensor((0.4914, 0.4822, 0.4465)).view(1, 3, 1, 1) def imshow(img): img img / 2 0.5 npimg img.numpy() plt.imshow(np.transpose(npimg, (1, 2, 0))) plt.axis(off) plt.figure(figsize(10, 10)) imshow(torchvision.utils.make_grid(images[:16])) plt.show()3.2 搭建你的第一个CNN模型网络结构我尽量精简让每一步的变化都能看明白。整体结构是两个卷积块加一个全连接头。每个卷积块里有两个3x3卷积层后面接ReLU和2x2最大池化。第一个块输出通道从3升到64第二个块从64升到128池化后特征图从32x32降到16x16再降到8x8。最后接一个全局平均池化把8x8x128压成128维的向量经过全连接层映射到10类输出。import torch.nn as nn import torch.nn.functional as F class SimpleCNN(nn.Module): def __init__(self, num_classes10): super(SimpleCNN, self).__init__() self.conv1 nn.Conv2d(3, 64, kernel_size3, padding1) self.bn1 nn.BatchNorm2d(64) self.conv2 nn.Conv2d(64, 64, kernel_size3, padding1) self.bn2 nn.BatchNorm2d(64) self.pool1 nn.MaxPool2d(kernel_size2, stride2) self.conv3 nn.Conv2d(64, 128, kernel_size3, padding1) self.bn3 nn.BatchNorm2d(128) self.conv4 nn.Conv2d(128, 128, kernel_size3, padding1) self.bn4 nn.BatchNorm2d(128) self.pool2 nn.MaxPool2d(kernel_size2, stride2) self.avgpool nn.AdaptiveAvgPool2d((1, 1)) self.fc nn.Linear(128, num_classes) def forward(self, x): x F.relu(self.bn1(self.conv1(x))) x F.relu(self.bn2(self.conv2(x))) x self.pool1(x) x F.relu(self.bn3(self.conv3(x))) x F.relu(self.bn4(self.conv4(x))) x self.pool2(x) x self.avgpool(x) x torch.flatten(x, 1) x self.fc(x) return x设置batch size为1跑一次forward打印每层的输出尺寸这是检查网络结构有没有搭错最直接的方式。model SimpleCNN() x torch.randn(1, 3, 32, 32) print(model)3.3 训练循环与损失函数选择分类任务损失函数闭眼选交叉熵这个没什么可纠结的。交叉熵内部包含了softmax操作所以模型最后一层不需要额外接softmax直接输出logits就行。如果手动在fc后面加了softmax再算交叉熵数值上虽然没错但反向传播的数值稳定性会变差训练过程容易出现loss不降的情况。训练循环代码看起来长但逻辑就是固定四步清空梯度、前向传播、算loss、反向传播。import torch.optim as optim device torch.device(cuda if torch.cuda.is_available() else cpu) model SimpleCNN().to(device) criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr0.001) num_epochs 30 train_loss_history [] train_acc_history [] for epoch in range(num_epochs): model.train() running_loss 0.0 correct 0 total 0 for i, data in enumerate(trainloader, 0): inputs, labels data inputs, labels inputs.to(device), labels.to(device) optimizer.zero_grad() outputs model(inputs) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() _, predicted torch.max(outputs, 1) total labels.size(0) correct (predicted labels).sum().item() if i % 100 99: print(f[Epoch {epoch1}, Batch {i1}] loss: {running_loss / 100:.3f}, acc: {100 * correct / total:.2f}%) running_loss 0.0 correct 0 total 0 train_acc 100 * correct / total train_acc_history.append(train_acc) print(Finished Training)这里有个小小的备忘训练时一定要调用model.train()因为BatchNorm在训练和测试时的行为不一样。训练时它用当前batch的均值和方差做归一化测试时用的是训练阶段统计好的全局均值和方差。如果忘了切换模式BatchNorm的行为混乱结果自然不对。3.4 测试集评估与准确率计算训练完之后在测试集上评估模型这是检验泛化能力的一步。用训练集acc做宣传指标意义不大因为模型可能只是背下了训练集。测试集评估代码需要加上torch.no_grad()告诉PyTorch不需要计算梯度这样能省下大量内存和计算时间。model.eval() correct 0 total 0 with torch.no_grad(): for data in testloader: images, labels data images, labels images.to(device), labels.to(device) outputs model(images) _, predicted torch.max(outputs.data, 1) total labels.size(0) correct (predicted labels).sum().item() print(fAccuracy on test set: {100 * correct / total:.2f}%)我自己跑下来的结果这个简单网络在30个epoch后测试集准确率大约在78%到82%之间浮动跟数据增强和随机种子有关。如果想要更高的准确率有几个常见抓手一是加深网络结构堆到VGG那种深度二是加入学习率衰减比如每10个epoch把学习率乘0.1三是用更激进的随机裁剪增强。但作为学习笔记这个准确率已经达到了检验理解和训练流程的目的。3.5 让训练过程可视化只打印文字日志不够直观我强烈建议记录loss和acc曲线。简单记录可以用matplotlib画图或者用tensorboard做实时监控。记录的方式就是在训练循环里把每个epoch的loss和准确率append到列表里训练结束后画图。import matplotlib.pyplot as plt plt.figure(figsize(12, 4)) plt.subplot(1, 2, 1) plt.plot(train_loss_history) plt.title(Training Loss Curve) plt.xlabel(Epoch) plt.ylabel(Loss) plt.subplot(1, 2, 2) plt.plot(train_acc_history) plt.title(Training Accuracy Curve) plt.xlabel(Epoch) plt.ylabel(Accuracy) plt.show()曲线长得不漂亮就是调参线索。如果loss前期迅速下降然后几乎不动可能是学习率偏小或者网络容量不够。如果loss震荡剧烈检查学习率是否偏大、batch size是否太小。如果loss根本不降优先检查数据预处理和模型结构而不是调参。4. 常见问题与排查技巧实录4.1 训练loss不下降的“三板斧”我把这个放在第一个说因为它的出现频率最高而且原因五花八门。遇到loss不下降或者降得极慢我一般按顺序排查三个环节。先看数据预处理。检查ToTensor()是否被遗忘如果数据没被正确归一化到[0,1]区间模型一开始看到的输入数值范围混乱梯度方向不稳定。再看标签是否有问题CIFAR-10的标签是0到9之间的整数如果标签数据读取错误交叉熵根本算不对。再看模型结构。确认最后一个线性层的输出维度等于类别数CIFAR-10就是10。确认没有在最后一层后面套softmax再接CrossEntropyLoss这也是新手经常犯的错误前面说过会造成数值不稳定。最后看优化器。把学习率调到0.0001试试如果这个很小的学习率下loss能缓慢下降说明之前的学习率确实偏大了。还有一种情况是loss一直卡在某个固定值附近比如2.302这个数字等于ln(10)对应10分类的随机交叉熵说明模型根本没学到东西问题大概率出在数据或者网络上。4.2 过拟合的识别与对症处理训练集准确率一路飙升到95%以上测试集准确率却只有70%这基本就是过拟合的实锤。CIFAR-10数据集上模型参数量和训练样本量之间严重失衡过拟合几乎是必然发生的。最直接的解法是加正则化。数据增强本身就是一种正则化手段如果增强已经做了还是过拟合可以尝试在model.train()阶段给全连接层加dropout概率从0.3开始调。BatchNorm也有轻微正则化效果这就是为什么我在每个卷积后都接了BatchNorm。还有一种思路是降低模型复杂度把通道数减半比如64改成32、128改成64牺牲一点上限换取更好的泛化。监控过拟合有个好习惯每个epoch训练结束后顺便跑一下测试集的loss把训练loss和测试loss画在同一张图上。当两边开始分道扬镳测试loss掉头上涨说明模型开始背训练集了这时候就应该早停或者加强正则化。4.3 CUDA out of memory的解决办法显存溢出这个报错新手遇上就容易慌但其实解法很套路。最常见的情况是batch size设太大或者输入图片分辨率太高。CIFAR-10的32x32小图导致显存溢出几乎都是batch size的锅。把batch size从128往下降到64、32直到程序能跑为止。如果batch size已经降到8还是溢出检查是不是别的地方占用了显存比如同时开着多个训练脚本或者没有释放旧模型的显存。还有个隐蔽的原因DataLoader的num_workers设置在Windows上如果大于0配合某些PyTorch版本会出现内存持续增长而不是显存溢出看起来像是显存泄漏其实是子进程反复拷贝数据。这种情况最简单的处理是把num_workers设为0或者升级PyTorch版本。4.4 训练速度慢得让人怀疑人生CPU跑CNN是挺折磨人的30个epoch可能要跑几个小时。我实测的优化优先级是这样排的把num_workers调到能用的最大值CPU训练时并行加载数据能明显提升效率确认数据增强的计算量合理RandomCrop加padding已经是比较轻量的操作把batch size适当调大让CPU的矩阵计算更高效。另外把模型里所有卷积层的biasFalse设一下BatchNorm层自带偏置功能卷积层再用bias等于重复参数省不了多少计算量但能减少显存占用。支持MPS加速的Apple Silicon Mac用户可以把设备切换到mps实测比CPU快好几倍。device torch.device(mps if torch.backends.mps.is_available() else cpu)4.5 常见问题速查表现象可能原因优先处理方案loss不降数据未归一化学习率过大标签有误检查预处理调小学习率到0.0001loss震荡剧烈学习率偏大batch size太小学习率减半batch size增大训练acc高测试acc低过拟合增强数据增强加dropout降通道数显存不足batch size过大显存被占调小batch size重启释放显存训练速度极慢CPU训练num_workers太小调大num_workers用GPU/MPSRandomCrop报错图片尺寸小于裁剪尺寸先Padding再CropCIFAR下载失败网络问题手动下载数据包放入目录4.6 断点续训与模型保存的偷懒技巧训练到第20个epoch停电了或者崩了要从头再来一遍想想都头疼。所以训练脚本里最好加上模型保存逻辑每几个epoch保存一次checkpoint包含模型参数、优化器状态、当前epoch数、历史loss曲线这样不仅断点能续训后期想调整超参对比实验也有据可依。import os checkpoint_dir ./checkpoints os.makedirs(checkpoint_dir, exist_okTrue) checkpoint { epoch: epoch, model_state_dict: model.state_dict(), optimizer_state_dict: optimizer.state_dict(), train_loss_history: train_loss_history, train_acc_history: train_acc_history, } torch.save(checkpoint, os.path.join(checkpoint_dir, fcheckpoint_epoch_{epoch1}.pth))续训的时候加载checkpoint再跑循环就行。需要提醒的一点是保存checkpoint时要连同随机数生成器的状态一起存否则续训后的数据顺序可能跟原来不一致导致实验结果对不上。checkpoint torch.load(./checkpoints/checkpoint_epoch_20.pth) model.load_state_dict(checkpoint[model_state_dict]) optimizer.load_state_dict(checkpoint[optimizer_state_dict]) start_epoch checkpoint[epoch] 15. 迁移学习让成果走向实用的捷径5.1 为什么在这个节点讲迁移学习自己的CNN跑通了、测试集准确率到80%左右这时候就该想想怎么让这份经验发挥更大的价值。CIFAR-10上80%的准确率离实际应用还有不小的距离。如果换到真实业务场景数据集可能只有几千张图从头训练一个深层CNN几乎必挂这时候迁移学习就派上用场了。迁移学习的核心思想很简单把一个在大数据集上预训练好的模型作为起点在目标任务上做微调而不是从随机初始化开始训练。视觉领域最经典的预训练模型就是ImageNet上训练出来的那批比如ResNet系列和EfficientNet系列。5.2 用ResNet18实战迁移学习Torchvision里封装好了大量预训练模型用起来非常方便。关键代码就几行import torchvision.models as models model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) num_features model.fc.in_features model.fc nn.Linear(num_features, 10)这里做了两件事加载在ImageNet上预训练好的ResNet18参数然后替换掉最后一层全连接层因为ResNet18原本是1000分类的我们要改成10分类。微调有两种常见策略一种是冻结所有卷积层参数只训练最后一层适合数据量非常少的情况另一种是全部参数继续训练只是把学习率调小适合数据量尚可的情况。冻结参数只需要设置requires_gradFalsefor param in model.parameters(): param.requires_grad True # 如果只训练最后一层改成False实测下来迁移学习模型的收敛速度非常快十几个epoch就能达到90%以上的准确率比从头训练的自定义CNN高了将近10个百分点。这个实验强烈建议做一遍体验一下“站在巨人肩膀上”的感觉。5.3 理解特征提取器与分类头的分工做过一次迁移学习之后你会对CNN内部结构有更深的直觉。预训练模型的前面所有层都可以看作特征提取器——类似一台通用显微镜能识别出图像的边缘、纹理、形状等通用特征最后一层全连接层是分类头——类似一块可以换的铭牌告诉模型当前任务要分几个类。这就是为什么替换最后一层就能适配新任务通用特征是全领域共享的只有分类决策部分是任务相关的。这个认知放在多模态模型里同样成立。很多多模态架构的视觉编码器就是ImageNet预训练的CNN或ViT只替换后面的投影层来适配图文对齐任务。理解了迁移学习的原理等于理解了当前多模态模型里视觉部分的一种常见做法。6. 多模态模型的后续衔接思路6.1 从图像分类到图文匹配的过渡图像分类只是视觉任务的起点。掌握了CNN分类之后“CLIP怎么把图像和文本拉到同一个向量空间”这个问题就变得可以理解了。分类模型输出的是10维的类别概率分布CLIP那类模型输出的是图像特征向量和文本特征向量的相似度。但中间的卷积特征提取过程是共通的。如果你想把这篇笔记的知识延伸到多模态方向接下来最值得看的是对比学习。简单说对比学习的思路是同一张图片经过不同的数据增强生成的两个视图应该拥有相近的特征表示不同图片的特征表示应该相互远离。这个想法任何写过CNN训练代码的人都能立刻上手因为模型结构、数据增强、训练循环都是现成的只需要改一下损失函数和训练目标。6.2 特征向量的可视化与分析训练好CNN之后把池化层输出的128维特征向量拿出来用t-SNE或者PCA降到二维然后按类别染色画散点图。你会发现同一个类别的图片在特征空间里聚成一团不同类别的团相互分开。这个可视化实验对理解模型学到了什么非常有帮助。实现的代码也不复杂只是把最终分类层前面那一层输出拿出来做降维。from sklearn.manifold import TSNE feature_extractor nn.Sequential(*list(model.children())[:-1]) features [] labels [] with torch.no_grad(): for data in testloader: images, labels_batch data images images.to(device) feat feature_extractor(images) features.append(feat.cpu().numpy().reshape(feat.size(0), -1)) labels.extend(labels_batch.numpy()) features np.concatenate(features, axis0) tsne TSNE(n_components2, random_state42) features_2d tsne.fit_transform(features)跑完之后你会看到一张充满信息量的图建议把这个实验和分类准确率配合起来看对特征分布的理解会立体很多。6.3 后续学习路线建议这篇笔记做完你已经把图像分类的一个闭环走通了。接下来如果继续深入计算机视觉方向可以去看目标检测和语义分割这两块VGG、ResNet那一套基础依然适用。如果直接转多模态方向建议按照“视觉编码器 文本编码器 对比学习”的路线展开理解了CNN特征提取和迁移学习再去看CLIP和它的变体会顺畅得多。到这里基于卷积神经网络的图像分类实战算是完整落地了。我个人实际操作中的体会是训练过程里偶然掉到78%准确率、调一调又涨回81%的波动远比看一百遍理论公式更能建立对参数、结构、数据之间关系的直觉。所以在笔记最后多嘴一句哪怕代码全抄对了也一定亲手改一两个超参、换一种增强方式看看曲线怎么变那才是这一篇笔记真正想让你带走的东西。
RELATED

相关推荐

上下文工程实战:从72%到91%准确率的智能体优化复盘

上下文工程实战:从72%到91%准确率的智能体优化复盘

从一次客服机器人改版说起:我把一个"看起来什么都懂"的智能体,从准确率72%调到了91%,单次调用成本还降了一半以上。这个过程中最深刻的体会是——模型本身没换,prompt也没有玄学式的反复修改,真正的杠杆在上…

📅 2026/10/22 13:46:33
数字串123222工程实战:进制转换、质因数分解与分布式分片

数字串123222工程实战:进制转换、质因数分解与分布式分片

我看到“123222”这种数字串,第一反应不是“这有什么可挖的”,而是“这能当多少种样本用”。六位数字,字符组成简单,却同时具备统计特征、数学性质、进制转换后的多重身份,甚至可以直接扔进测试用例、分布式分片和校验…

📅 2026/10/22 13:46:33
RESTful接口设计:资源、路径、方法的对应关系全拆解

RESTful接口设计:资源、路径、方法的对应关系全拆解

RESTful 接口这块,很多团队聊起来能聊一下午,但真到写代码的时候,最容易出问题的反而是最基础的三个词:资源、路径、方法。我见过不少前后端联调翻车的现场,根因几乎都不是框架不会用,而是这三者的对应关系…

📅 2026/10/22 13:46:33
MORE NEWS

更多资讯

📰

AI率居高不下?从原理到实操,详解降AIGC平台的10类方向

上个月有个学弟抱着一篇毕业论文初稿来找我,说导师在群里扔下一句话:AI率超过40%的,不用交。他这篇稿子是对话式AI花了一夜拼出来的,自己都没细读,一上学院系统测试,AI率73%。他当时特别茫然:不…

📰

Python调用大模型API实战:从零开始构建你的首个AI应用

很多刚入门的朋友会问我,做“AI大模型”相关的应用到底要不要先把模型部署到本机,是不是得先啃完一堆底层原理。我的回答一直很统一:如果目标是尽快做出能用的东西,先把“Python调用大模型API”这条路走通就足够了。把请求发出去、…

📰

从收藏夹到私域知识库:基于RAG打造B站视频问答系统

1. 收藏夹里的六百个视频,为什么我决定做一个问答系统先说一个我自己的尴尬事。某天晚上我想找半年前看过的一个关于注意力机制的讲解视频,印象里UP主拿Transformer铺开讲了一整期,当时觉得讲得特别好,顺手点了收藏,还…

📰

AI迈入智能体时代:从生成内容到执行任务的技术演进与落地实践

1. 从"能说会道"到"能动手干活":智能体到底改变了什么 如果你在过去两年里深度使用过各类生成式大模型,应该会有一种微妙的感觉:它们很聪明,但总差一口气。你问它一个问题,它能给你一段漂亮的回答…

📰

视觉SLAM前沿思考:几何、学习与多传感器融合的鲁棒性之路

前阵子听了一场关于视觉SLAM的分享,主题是“视觉SLAM最新观点”,分享人是章国锋教授。我一边听一边记了不少笔记,事后又回看了一遍,把里面让我反复琢磨的几个点单拎出来聊聊。这篇不是讲稿复述,算是我消化之后的一个梳…

📰

基于扩散模型的年龄迁移:从少年到老年的连续可控人脸生成

1. 先把任务拆清楚:年龄"迁移"到底在改什么 最近我拿了一批跨越不同年龄段的人脸照片做实验,目标是让同一个人既能回到少年时代,也能一键推到七八十岁,而且要"看起来像同一个人",不是换头&#xf…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬