新闻详情

新闻详情

首页 / 资讯中心 / 详情

一周CNN一周ResNet:卷积神经网络与残差网络源码实战拆解

发布时间:2026/9/28 2:56:23来源:尧图网络
一周CNN一周ResNet:卷积神经网络与残差网络源码实战拆解
简介面向深度学习初学者与进阶者这份Python项目包围绕CNN和ResNet的简易搭建展开既解释ResNet是CNN扩展改进版本的概念也通过两周目录逐步演示CNN基础组件与ResNet残差块的设计思路帮助读者理解卷积、池化、跳跃连接等关键机制。zip压缩包共24个文件约25.27MB以H5数据集、Python脚本、Pyc缓存及XML/IML项目配置文件为主覆盖数据加载、模型定义与训练调用等环节便于直接运行和二次修改。已有1099人在CSDN学习或下载。配套内容分为1第一周、2第二周两个阶段第一周侧重基础CNN模型搭建第二周进入ResNet实现并提供数据集与工具函数可完成图像分类的完整训练流程。对希望快速上手TensorFlow/PyTorch或想梳理CNN到ResNet演进逻辑的读者而言这份代码包具备清晰的学习路径与实操价值。1. 用一周把CNN跑通、再用一周吃透ResNet这份Python源码包的真实拆解先回答那个被问烂了的问题ResNet是CNN吗是但不完全是。ResNet的全称是Residual Network它没有跳出卷积神经网络的框架而是在CNN的基础上加了跳跃连接skip connection让信息可以绕过一两个卷积层直接往后传。拆这份源码包之前我建议你先把这个关系钉死在脑子里——别把ResNet当成CNN的对立面它是CNN的一个进化版本。这份资源是典型的“两周一疗程”第一周目录叫1第一周里面是CNN的基础实现配套cnn_utils.py和手写符号数据集train_signs.h5第二周目录叫2第二周里面是ResNet的搭建配套resnets_utils.py、kt_utils.py和两个数据集。适合谁两类人一类是刚看完卷积层概念想动手复现的初学者另一类是已经跑过现成模型、但还没自己从零写过forward和backward的进阶者。它不能让你直接上手工业级图像识别但能让你把CNN和ResNet从“黑匣子”变成“看得见每一层张量形状的透明盒子”。2. 简易CNN搭建卷积层、池化层与全连接层是怎么协同的2.1 为什么先搭CNN再上ResNet从feature map到分类输出的完整链路CNN的套路说穿了就四步卷积提特征、激活加非线性、池化压尺寸、全连接做分类。第一周项目让你干的活就是把这几步串成一个能跑通的手写符号识别模型。先看输入输出链路。输入是一张64x64x3的RGB图像经过第一层卷积后变成64x64x8的特征图——这里通道数从3变成8是卷积核数量决定的每个卷积核扫一遍图像产生一个通道。接着过ReLU激活再经过池化层把尺寸缩小一半变成32x32x8。第二层卷积再把通道数往上抬比如从8变16尺寸继续缩。到最后特征图被拉平成向量送进全连接层输出层用softmax给出每个类别的概率。这条链路就是CNN的骨架。搭建顺序上有个多年踩出来的经验先把网络结构写在一个函数里前向传播能算出shape再去想训练循环。很多新手一上来就写训练代码结果卷积层参数一错梯度更新时张量形状对不上整个训练直接崩。第一周目录里的cnn_utils.py就是干这个的——它把初始化、前向传播、反向传播封装成工具函数你只要按顺序调用。下面是这一周的典型结构参数表对应64x64x3输入的手写符号识别任务层类型输出形状参数说明输入层(64, 64, 3)原始图像Conv1 ReLU(64, 64, 8)8个3x3卷积核paddingsameMaxPool1(32, 32, 8)2x2池化步长2Conv2 ReLU(32, 32, 16)16个3x3卷积核paddingsameMaxPool2(16, 16, 16)2x2池化步长2Flatten(4096,)16x16x164096Dense ReLU(128,)全连接层128个神经元Dense Softmax(6,)6类符号输出2.2 第一周代码拆解cnn_utils.py里的初始化与前向传播第一周项目的核心工具文件是cnn_utils.py。这里我按常见课程项目的写法拆解它的骨架——它负责把网络每一层的权重初始化成合理分布并定义前向传播的计算顺序。# 参考写法cnn_utils.py 中的参数初始化部分 def initialize_parameters(): # 第一层卷积输入3通道输出8通道卷积核3x3 W1 np.random.randn(8, 3, 3, 3) * np.sqrt(2.0 / (3 * 3 * 3)) b1 np.zeros((8, 1)) # 第二层卷积输入8通道输出16通道 W2 np.random.randn(16, 8, 3, 3) * np.sqrt(2.0 / (8 * 3 * 3)) b2 np.zeros((16, 1)) # 全连接层输入4096输出128 W3 np.random.randn(128, 4096) * np.sqrt(2.0 / 4096) b3 np.zeros((128, 1)) # 输出层输入128输出6个类别 W4 np.random.randn(6, 128) * np.sqrt(2.0 / 128) b4 np.zeros((6, 1)) parameters {W1: W1, b1: b1, W2: W2, b2: b2, W3: W3, b3: b3, W4: W4, b4: b4} return parameters这段代码里需要特别说明的是权重初始化方式np.sqrt(2.0 / n)是He初始化专门配合ReLU激活函数。它让权重方差和上一层神经元数量成反比避免信号在逐层传播时指数级放大或缩小。我见过不少项目直接用np.random.randn裸初始化结果ReLU之后特征值越来越大没几个epoch就溢出了。参数W1的形状(8, 3, 3, 3)里第一个8是输出通道数第二个3是输入通道数后两个3是卷积核尺寸——这个顺序在numpy实现里常见但在PyTorch里却是(输入, 输出, 核高, 核宽)换框架时最容易搞混。前向传播这边核心是按顺序执行卷积、ReLU、池化、再卷积、再池化、展平、全连接# 参考写法cnn_utils.py 中的前向传播 def forward_propagation(X, parameters): W1, b1, W2, b2 parameters[W1], parameters[b1], parameters[W2], parameters[b2] # 第一层卷积 ReLU 池化 Z1 conv_forward(X, W1, b1, stride1, padding1) A1 relu(Z1) P1 pool_forward(A1, pool_size2, stride2) # 第二层卷积 ReLU 池化 Z2 conv_forward(P1, W2, b2, stride1, padding1) A2 relu(Z2) P2 pool_forward(A2, pool_size2, stride2) # 展平 全连接 P2_flat flatten(P2) Z3 np.dot(W3, P2_flat) b3 A3 relu(Z3) Z4 np.dot(W4, A3) b4 A4 softmax(Z4) cache (Z1, A1, P1, Z2, A2, P2, P2_flat, Z3, A3, Z4) return A4, cache这里的conv_forward和pool_forward是cnn_utils里封装好的底层函数。前向传播的结果A4是6个类别的概率分布cache里记录了每一层的中间结果——这些中间值不能丢因为反向传播计算梯度时要靠它们做链式求导。我拆这个项目时反复提醒自己前向传播不只是为了算loss更大价值是为backward准备缓存。如果哪层忘了往cache里塞数据反向传播到那层必然报KeyError。2.3 test.py怎么跑数据加载、迭代训练与损失曲线第一周目录里的test.py是训练入口。它做的事情可以用一条流水线概括加载train_signs.h5- 做归一化和one-hot编码 - 初始化参数 - 迭代训练 - 打印损失。# 参考写法test.py 的核心训练循环 import h5py import numpy as np # 1. 加载 h5 数据集 def load_signs_dataset(): train_dataset h5py.File(datasets/train_signs.h5, r) train_x train_dataset[train_set_x][:] train_y train_dataset[train_set_y][:] # 归一化像素值从0-255缩放到0-1 train_x train_x / 255.0 # 转成 (样本数, 通道, 高, 宽) 布局 train_x train_x.transpose((0, 3, 1, 2)) # one-hot编码标签 train_y one_hot(train_y, 6) return train_x, train_y # 2. 训练循环 X, Y load_signs_dataset() parameters initialize_parameters() for epoch in range(100): A4, cache forward_propagation(X, parameters) cost compute_cost(A4, Y) gradients backward_propagation(X, Y, cache, parameters) parameters update_parameters(parameters, gradients, learning_rate0.01) if epoch % 10 0: print(fepoch {epoch}, cost {cost:.4f})训练循环的四个核心动作——前向、算cost、反向、更新参数——对应深度学习的全部底层逻辑。learning_rate0.01是课程项目的常见默认值实际跑的时候可以试0.001到0.1之间的几个量级。损失曲线如果前10个epoch从1.8往下降到0.8左右说明网络在学习如果原地不动甚至上升先别调网络结构优先检查数据归一化有没有做——train_x / 255.0这行就是血泪经验忘记归一化时大数值输入经过卷积后激活值很容易落在ReLU的饱和区梯度直接消失。提示第一周完整项目里还有cnn_utils.cpython-37.pyc这个编译缓存文件它只是Python的字节码缓存不是项目代码的一部分可以忽略。3. 从CNN到ResNet残差块怎么解决梯度消失ResNet算不算CNN3.1 网络加深为什么会退化梯度消失与恒等映射把CNN的层数从几层加到几十层理论上特征提取能力应该更强实际训练时却会出现反直觉的现象层数越深训练误差反而越高。这不是过拟合因为训练集上的误差都下不去。吴恩达在课程里管这叫“退化问题”degradation problem它的根源一部分是梯度消失——反向传播时梯度要逐层回传每经过一层卷积和激活函数梯度范数就被乘上一个小于1的因子几十层乘下来梯度就趋近于零前面层的权重几乎收不到更新信号。另一部分原因是恒等映射难学。设想网络已经收敛到最优解附近此时理想的行为是让后面若干层退化成恒等映射——输入是什么就输出什么不添乱。但让卷积层通过调权重去拟合恒等映射是件困难的事因为卷积本质是特征变换除非权重调到特定值否则F(x) x很难精确满足。ResNet的思路是不再要求底层网络去拟合H(x) x而是拟合残差F(x) H(x) - x。当理想输出就是x时残差趋近于0即可这比让卷积层的输出直接等于输入要容易得多。残差块的结构很直白输入x经过两层卷积得到F(x)然后F(x) x再经过ReLU输出。这个加法操作就是跳跃连接也叫shortcut。它让梯度在反向传播时多了一条高速公路——梯度可以直接从输出层传到输入层而不经过卷积层从而保住前面层的更新力度。这就是为什么ResNet能做到152层还能正常训练的原因。3.2 残差块与跳跃连接shortcut的实现与维度匹配第二周项目里的核心是搭残差块。下面用PyTorch风格写一个基础残差块这是课程项目常见的实现方式# 参考写法PyTorch风格的BasicBlock import torch.nn as nn class BasicBlock(nn.Module): def __init__(self, in_channels, out_channels, stride1): super(BasicBlock, self).__init__() self.conv1 nn.Conv2d(in_channels, out_channels, kernel_size3, stridestride, padding1, biasFalse) self.bn1 nn.BatchNorm2d(out_channels) self.relu nn.ReLU(inplaceTrue) self.conv2 nn.Conv2d(out_channels, out_channels, kernel_size3, stride1, padding1, biasFalse) self.bn2 nn.BatchNorm2d(out_channels) # shortcut 维度匹配输入输出通道数不同或尺寸减半时用1x1卷积调整 if stride ! 1 or in_channels ! out_channels: self.shortcut nn.Sequential( nn.Conv2d(in_channels, out_channels, kernel_size1, stridestride, biasFalse), nn.BatchNorm2d(out_channels) ) else: self.shortcut nn.Identity() def forward(self, x): identity self.shortcut(x) # 输入直接走短路 out self.conv1(x) out self.bn1(out) out self.relu(out) out self.conv2(out) out self.bn2(out) out identity # 残差相加 out self.relu(out) return out几个关键点值得拆开讲。stride参数决定特征图尺寸是否减半当stride1时输出尺寸和输入一致shortcut直接用恒等映射当stride2时特征图尺寸减半输出通道数翻倍此时必须用1x1卷积对shortcut做投影变换否则F(x)和x形状对不上加法会直接报维度错误。biasFalse在卷积层配合BatchNorm时是固定做法——因为BatchNorm层本身带偏置参数卷积层的bias会被吸收掉留了反而浪费参数。第二周项目会把若干个残差块串成一个完整网络先是输入卷积层把3通道图像变成64通道然后依次堆叠多个stage每个stage里第一个残差块的stride2做下采样最后是全局平均池化加全连接输出。课程里常用的ResNet-50结构就是把这种BasicBlock换成Bottleneck三层卷积结构但在2第二周这个项目里重点是先吃透BasicBlock的加法逻辑不是盲目堆层数。3.3 resnets_utils.py与kt_utils.py第二周工具文件里藏了什么第二周目录里的工具文件有两个resnets_utils.py和kt_utils.py。它们不是网络结构代码而是数据加载和预处理工具。resnets_utils.py主要面向手写符号数据集里面的核心函数是load_happy_dataset()和load_signs_dataset()。它做的事情和第一周的load_signs_dataset类似读取h5文件、归一化、one-hot编码、调整数据布局。但第二周的数据处理多了一步——它会根据ResNet的输入要求把图像统一resize到64x64并确保数据格式是(样本数, 高度, 宽度, 通道)的TensorFlow布局或(样本数, 通道, 高度, 宽度)的PyTorch布局。跑第二周代码前先确认你的resnets_utils.py里用的布局和你的后端框架一致这是最常见的一个坑。kt_utils.py则是第二周人脸表情识别任务专属的工具对应train_happy.h5数据集。happy数据集是一个二分类任务——识别笑脸和非笑脸图像尺寸相对统一数据量不大总共就几百张。它的load_dataset()会返回已经归一化好的(样本数, 64, 64, 3)数组和对应的0/1标签。因为数据量小这个任务特别适合用来验证ResNet的收敛能力如果模型在小数据集上能快速过拟合到100%训练精度说明前向传播、反向传播和参数更新链路是通的。注意resnets_utils.py里的数据加载函数通常假设项目目录结构是project/datasets/xxx.h5。如果你把h5文件放错位置最常见的报错是FileNotFoundError: [Errno 2] Unable to open file排查时第一反应应该是检查相对路径而不是怀疑代码逻辑。4. 两套实战项目跑起来手写符号识别与人脸表情识别的完整过程4.1 从train_signs.h5到预测结果CNN在符号识别上的标准流程第一周项目的完整流程是加载train_signs.h5- 训练CNN - 在测试集上计算精度。这里我把流程拆成三步每一步都能直接对照项目里的文件。第一步是理解数据集。train_signs.h5里存的是6类手写符号训练集样本数是1080每张图64x64x3。h5文件本质是一个层级化的键值存储可以用h5py直接打开查看里面的键名。第一次跑项目的人我强烈建议先做这一步——我之前跳过了它直接加载数据结果把train_set_x的维度含义搞错了以为是(1080, 64, 64, 3)实际需要transpose变成(1080, 3, 64, 64)才符合numpy后端的输入布局。一个transpose的问题浪费了整整一个晚上。第二步是训练。用test.py跑100个epoch每个epoch做一次全量前向和反向传播。注意compute_cost用的是交叉熵损失配合softmax输出。损失曲线的预期走势是前10个epoch快速下降到1.0以下后90个epoch慢慢逼近0.2甚至更低。如果曲线在第20个epoch就接近0警惕过拟合——训练集精度100%不代表测试集能泛化。第三步是验证。在测试集上计算精度是检验模型的唯一标准。# 参考写法测试集精度验证 def predict(X, Y, parameters): A4, _ forward_propagation(X, parameters) predictions np.argmax(A4, axis0) labels np.argmax(Y, axis0) accuracy np.mean(predictions labels) return accuracy # 加载测试集 test_dataset h5py.File(datasets/test_signs.h5, r) test_x test_dataset[test_set_x][:] / 255.0 test_x test_x.transpose((0, 3, 1, 2)) test_y one_hot(test_dataset[test_set_y][:], 6) accuracy predict(test_x, test_y, parameters) print(fTest Accuracy: {accuracy * 100:.2f}%)这段代码的逻辑很简单但容易被忽略np.argmax(A4, axis0)是按列取最大概率位置。如果你的A4形状是(6, 样本数)axis0是对的如果代码里换成了axis1精度直接掉到随机水平——大约16.7%6类均匀分布这是个典型的“框架布局不同导致argmax方向错”的玄学问题。第一周项目里最终精度应该落在80%上下达不到就先看归一化和布局别急着调网络结构。4.2 train_happy.h5上的ResNet小数据集训练要注意什么第二周项目的核心任务是用ResNet在train_happy.h5上做人脸笑脸二分类。数据量小是它的显著特征训练集600张、测试集150张每张64x64x3。这个规模下ResNet的训练表现和第一周CNN在大数据集上的表现有明显差异。小数据集上的首要问题是过拟合。一个只有几万参数的ResNet在600张图上训练精度能轻松到100%但测试集可能只有75%。第二周项目里常用的缓解手段是早停训练过程中每10个epoch记录一次验证精度一旦验证精度连续20个epoch不提升就停止训练并恢复到历史最佳参数。课程项目通常把最佳精度目标定在85%左右如果能到90%说明残差块的实现基本没有bug。第二个问题是学习率的选择。数据量小意味着每个batch的梯度估计噪声更大学习率调太大模型直接发散调太小收敛慢。第二周项目的learning_rate建议从0.001开始试。如果你用的是优化器Adam的默认学习率就是0.001这个值在ResNet-50级别的模型上是一个不错的起点。# 参考写法happy数据集上的ResNet训练配置 optimizer torch.optim.Adam(model.parameters(), lr0.001) scheduler torch.optim.lr_scheduler.StepLR(optimizer, step_size30, gamma0.5) criterion torch.nn.BCEWithLogitsLoss() # 二分类用二值交叉熵 for epoch in range(60): model.train() for images, labels in train_loader: optimizer.zero_grad() outputs model(images) # shape: (batch, 1) loss criterion(outputs.squeeze(), labels.float()) loss.backward() optimizer.step() scheduler.step()scheduler是学习率衰减——每30个epoch把学习率乘以0.5让训练后期梯度更新更细腻。BCEWithLogitsLoss把sigmoid激活合并进了损失函数数值上比“先sigmoid再算交叉熵”更稳定这是PyTorch的官方建议做法。第二周项目里如果你看到损失函数里用了torch.nn.Functional.binary_cross_entropy记得先确认输出有没有过sigmoid——很多翻车现场就是忘了这层。4.3 文件目录对比第一周和第二周代码的真实分工拆完两个项目后我把文件结构和用途整理成了下面这张表方便按图索骥文件/目录所属周次作用建议优先级cnn_utils.py第一周CNN的初始化、前向、反向、更新参数工具必读test.py第一周目录第一周训练入口加载signs数据集并跑训练循环必读train_signs.h5第一周手写符号训练数据集1080张64x64x3图直接使用test_signs.h5第一周手写符号测试集用于验证精度直接使用resnets_utils.py第二周ResNet的数据加载、预处理工具必读kt_utils.py第二周happy数据集的加载工具阅读接口即可train_happy.h5第二周笑脸识别训练集600张图直接使用test_happy.h5第二周笑脸识别测试集150张图直接使用.idea/*.iml周次无关PyCharm项目配置文件忽略__pycache__目录周次无关Python字节码缓存忽略这张表最右边的优先级一列是我个人建议。.idea、misc.xml这些是PyCharm的工程配置属于IDE环境文件跟模型代码没半毛钱关系。__pycache__里的cnn_utils.cpython-37.pyc只是编译缓存删除也没影响。新手拆项目时最容易犯的错就是去读这些无关文件把自己的思路带偏。优先读cnn_utils.py和resnets_utils.py把两个数据集的加载逻辑吃透再回到test.py看训练循环整个项目的脉络就清晰了。5. 避坑指南搭建CNN和ResNet时最容易翻车的五个坑5.1 数据维度不匹配height、width、channels三者的顺序错位现象加载train_signs.h5后直接喂给卷积层报错Shape (1080, 64, 64, 3) is incompatible with expected (3, 64, 64)或者反过来的错。原因numpy后端实现里卷积层期望的输入布局是(样本数, 通道数, 高, 宽)也就是NCHW而h5文件里原始存储通常是(样本数, 高, 宽, 通道数)即NHWC。两种布局不统一导致第一层卷积的张量运算直接崩溃。解决在数据加载后强制加一行transpose把维度顺序显式转换。我一般在load_dataset函数里固定写死布局转换并在函数开头用注释标明最终布局防止以后自己忘了# 强制转换为 NCHW 布局 (样本数, 通道, 高, 宽) train_x train_x.transpose((0, 3, 1, 2))5.2 池化层输出尺寸算错下采样让卷积层参数对不上现象两层卷积之间报维度错误比如第一层池化输出(16, 16, 16)第二层卷积初始化时却按(8, 8, 8)的尺寸分配权重。原因池化层的stride2会把尺寸减半但新手在计算特征图大小时经常忽略padding的影响。比如64x64输入经过padding1的3x3卷积输出还是64x64但再经过2x2池化就变成32x32。如果手动计算时漏掉池化这步后面全连接层的输入维度就全错了。解决在搭建网络时每个stage后打印一次当前张量的shape逐层核对。我一般会在forward_propagation里加一行调试输出print(After pool:, P1.shape)。等全部层形状确认无误后再删掉调试代码。别信手算让机器告诉你真实shape。5.3 跳跃连接维度不一致residual与shortcut相加时报错现象实现ResNet残差块时out identity这一行报错提示The size of tensor a (32) must match the size of tensor b (16)。原因残差块内部经过卷积后输出通道数变了或者因为stride导致特征图尺寸减半此时原始输入x的尺寸和卷积输出F(x)不一致直接加法必然报错。解决给shortcut分支补上投影操作用1x1卷积调整通道数和尺寸步长与残差块主路径的stride保持一致if stride ! 1 or in_channels ! out_channels: self.shortcut nn.Sequential( nn.Conv2d(in_channels, out_channels, kernel_size1, stridestride, biasFalse), nn.BatchNorm2d(out_channels) )5.4 批归一化顺序颠倒BN放在激活前还是激活后现象训练时loss下降正常但验证集精度波动剧烈甚至在epoch 50后反而比epoch 30还低。原因批归一化层的顺序不统一会导致训练和推理时的数据分布不一致。常见实践是把BN放在卷积层之后、ReLU之前。但如果你在某个残差块里写成“卷积 - ReLU - BN”在另一个块里写成“卷积 - BN - ReLU”模型内部的归一化分布就乱了。解决统一使用“卷积 - BN - ReLU”的顺序并在整个项目中保持一致。各家框架的预训练模型也遵循这个约定自己搭网络时最好沿用否则后面做迁移学习加载预训练权重也会出问题。5.5 小数据集上直接过拟合loss趋近于零但测试精度很低现象happy数据集上训练20个epoch后训练loss降到0.01以下训练精度100%但测试精度只有70%左右。原因模型参数量远大于样本量600张图对ResNet来说太少网络直接把训练样本记住了没有学到可泛化的模式。解决加上早停机制用验证集监控泛化能力同时引入数据增强——随机裁剪、水平翻转、颜色抖动把有效样本量放大几倍。第二周项目本身规模小我的做法是把训练脚本改成每隔10个epoch在测试集上打一次精度取测试精度最高的那组参数保存下来宁缺毋滥。6. 把网络改造成你自己的从验证精度到迁移学习的最后一公里两个星期的项目跑通之后别急着关掉编辑器。你的下一步是把这份代码改造成能复用到自己数据集上的工具。先做的第一件事是写一个可复用的验证函数把“训练完的模型能不能用”变成一行代码能回答的问题# 参考写法统一的模型验证函数 def evaluate_model(model, data_loader, devicecpu): model.eval() correct 0 total 0 with torch.no_grad(): for images, labels in data_loader: 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() return correct / total这个函数的要点有两个model.eval()会关闭Dropout和BN的统计更新保证推理时行为稳定torch.no_grad()告诉框架不需要计算梯度内存占用大幅下降。我一般在训练脚本的最后一行调用它然后打印精度——这个习惯让我避免了很多次“训练完了却忘了测一下模型实际效果”的尴尬。第二件事是给自己的数据集做适配。train_signs.h5是6类你的数据可能是10类、100类图像可能是32x32而不是64x64。改两个地方就行全连接输出层的神经元数量改成你的类别数输入的通道数如果不是3把第一层卷积的in_channels改掉。如果图像尺寸偏小可以先去调输入层的padding或干脆resize到64x64。第三件事是尝试加载ResNet预训练权重做迁移学习。在resnets_utils.py的基础上把网络结构定义成torchvision.models.resnet18(pretrainedTrue)然后只重新训练最后一层全连接。预训练模型在ImageNet上学到的低级特征——边缘、纹理、颜色块——对大多数图像任务都是通用的冻结前面的卷积层参数只训练分类头几百张图也能有不错的效果。这时候你再回头想resnet是cnn吗这个问题答案就变得更具体了它是一个在CNN骨架上加了跳跃连接的进化版本而这个进化让它能训练得更深、收敛得更稳。那次我拿着这份源码包跑自己的数据集第一周CNN试了十几次测试精度一直卡在75%。后来发现是数据布局没统一训练集用了NHWC测试集忘了转成NCHW。从那以后我每次搭CNN和ResNet都强制走一遍流程先打印数据shape、再逐层打印特征图shape、验证集精度必须在训练脚本里实时输出。这套流程帮我避开了至少二十次翻车。希望你也能用这套流程少踩几个我踩过的坑——希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

DLT645/DLT698电表通信地址读取实战:广播帧与GET请求全解析 2026/9/28 3:59:52

DLT645/DLT698电表通信地址读取实战:广播帧与GET请求全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
把近5万个源文件喂给AI之前,我先用TaoToken做了一件事 2026/9/28 3:59:45

把近5万个源文件喂给AI之前,我先用TaoToken做了一件事

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
本地调试 MCP Stdio 服务:Cursor 添加参数调用与 TaoToken 配置骨架 2026/9/28 3:59:45

本地调试 MCP Stdio 服务:Cursor 添加参数调用与 TaoToken 配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
python selenium框架解决ip代理框不能自动化登录,解决pyautogui开不了多线程问题 2026/9/28 3:59:38

python selenium框架解决ip代理框不能自动化登录,解决pyautogui开不了多线程问题

有可能会导致, 我们利用自动化框架来打开网页的环节里, 眼前会出现这么一种登录框的情况。这个时候我们会发现无法进行f12检查操作了。哪怕我们通过开发者工具去强制检查内部情况。里面会呈现没有任何属性的状态。在那个时刻, 咱们就能够使用到那些外部的库了。它是一个库, 这个…

阅读更多 →
写python的同学可能都知道python有一个virtualenv 2026/9/28 3:59:38

写python的同学可能都知道python有一个virtualenv

依赖冲突总是频发, Go语言里的模块管理有时也会陷入混乱状态, 而虚拟环境的管理机制正好可以来解决这一问题。在进行编程的时候, 最让人感到烦恼的事情就是构建开发环境这一过程。在我刚刚开始学习写代码的初期阶段, 安装一个软件库的感觉就类似于去参与抽奖活动一样无法预料, …

阅读更多 →
codex 登录报 Token exchange failed?TaoToken 配置文件排查与修复指南 2026/9/28 3:59:32

codex 登录报 Token exchange failed?TaoToken 配置文件排查与修复指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉