尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

卷积神经网络(CNN)原理详解:从核心组件到PyTorch实战

卷积神经网络(CNN)原理详解:从核心组件到PyTorch实战 1. 从“看”到“理解”为什么我们需要卷积神经网络如果你尝试过用传统的全连接神经网络来处理一张哪怕只有100x100像素的灰度图片你很快就会陷入困境。一张100x100的图片意味着有10,000个像素点每个点作为一个输入特征。如果第一层隐藏层有1000个神经元那么仅这一层的权重参数数量就达到了惊人的1000万10,000 * 1000。这还只是灰度图如果是RGB彩色图参数数量直接翻三倍。这带来的问题不仅仅是计算量爆炸更致命的是如此庞大的参数网络几乎无法训练它会迅速过拟合记住训练集中的每一张图片但对任何没见过的新图片都束手无策。这揭示了一个核心矛盾图像数据具有极强的空间局部相关性和平移不变性而全连接网络的结构完全忽略了这一点。所谓空间局部相关性指的是图片中一个像素点其颜色和意义主要取决于它周围的一小片邻居而不是远在天边的另一个像素。比如判断一张脸的眼睛我们只需要关注图片上部的局部区域即可。所谓平移不变性指的是无论眼睛出现在图片的左上角还是右下角它都应该被识别为“眼睛”识别这个特征的“探测器”应该是通用的。卷积神经网络CNN的诞生正是为了解决这个矛盾。它的设计哲学不是让每个神经元都去“看”整张图片而是设计一种特殊的、小巧的“特征探测器”卷积核让这个探测器只在图片的一小块区域感受野上滑动提取局部特征。无论这个探测器滑动到哪里它使用的参数权重都是同一套这就天然具备了平移不变性。这种设计完美契合了图像数据的本质使得CNN成为计算机视觉领域近十年来最核心、最成功的架构没有之一。从人脸识别、自动驾驶到医学影像分析CNN的身影无处不在。2. 拆解CNN的核心组件不止是卷积很多人一提到CNN脑子里就只有“卷积层”。这其实是一个很大的误解。CNN是一个精心设计的层次化特征提取流水线每一层都有其独特且不可替代的使命。理解这个流水线是掌握CNN的关键。2.1 卷积层特征探测器的滑动扫描这是CNN的灵魂。你可以把一个卷积核想象成一个拿着特定模板比如边缘检测模板的巡逻员。这个巡逻员从输入图像的左上角开始将模板覆盖在图像的一小块区域上比如3x3进行点乘求和运算得到一个数值。这个数值代表了输入图像的这一小块区域与模板的匹配程度。然后巡逻员向右滑动一步步长继续计算下一个位置。如此这般滑过整张图像最终生成一张新的二维平面图我们称之为“特征图”。为什么是点乘求和这其实是在计算图像局部区域与卷积核模板的相似度。如果图像那块区域恰好是一条垂直的边而卷积核恰好是一个垂直边缘检测器例如中间一列为正数两边为负数那么点乘求和的结果就会是一个很大的正数在特征图上形成一个亮斑标志着“这里检测到了一条垂直边”。一个关键细节参数共享。在整个滑动过程中巡逻员手里的模板卷积核的权重是不变的。这意味着无论要检测的特征如垂直边出现在图像的哪个位置都使用同一套参数去识别。这极大地减少了参数量一个3x3卷积核只有9个参数外加1个偏置并且强制网络学习到平移不变的特征。多通道卷积对于RGB彩色图像3个通道卷积核也必须是3D的其深度与输入通道数相同。计算时卷积核在每个通道上分别与图像的对应通道进行卷积然后将三个通道的结果相加再加上偏置得到特征图上的一个点。所以一个卷积核无论输入通道是多少它只输出一张特征图。如果我们想要提取多种特征比如同时检测垂直边、水平边、45度角边就需要使用多个不同的卷积核。假设我们使用32个卷积核那么输入一张图像经过这一层卷积后就会得到32张特征图这32张图堆叠起来就构成了输出给下一层的“新图像”其通道数变成了32。2.2 激活函数引入非线性的“开关”卷积操作本质上是线性的加权求和。然而现实世界的数据和特征之间的关系绝大多数是非线性的。如果只有线性变换无论堆叠多少层整个网络最终等效于一个单层线性网络能力极其有限。因此在卷积之后我们必须立即引入一个非线性激活函数。这相当于给每个神经元的输出加了一个非线性的“开关”或“阀门”。最经典、也最常用的就是ReLU函数f(x) max(0, x)。它的意义非常直观如果卷积计算出的特征强度x是正的说明很可能存在该特征就让它原样通过如果是负的说明很可能不存在该特征就直接置零将其“关闭”。ReLU的优势在于计算简单、不存在梯度饱和问题在正区间梯度恒为1能有效加速网络训练。它让网络具备了拟合复杂非线性函数的能力。没有它深度学习不可能达到今天的深度。2.3 池化层信息浓缩与空间降维经过卷积和激活我们得到了一系列特征图它们标识了原始图像中各种基础特征如边、角、点的位置。但这些特征图通常还非常“细致”并且对特征的微小位置变化非常敏感比如同一个边缘平移一个像素在特征图上的响应位置就变了。我们需要的是一种更鲁棒、更抽象的表达。池化层应运而生最常用的是最大池化。它在一个小的局部区域比如2x2的窗口内只保留最强的那个信号最大值然后丢弃其他三个。这个窗口以固定的步长通常为2在特征图上滑动。池化层解决了几个核心问题降维减少计算量2x2最大池化步长为2会将特征图的长和宽各缩小一半像素点减少到1/4。这为后续更深的网络层节省了大量计算。平移、旋转、缩放的不变性增强只要最强的特征响应还在池化窗口内无论它的精确位置在窗口内如何微动池化后的输出都是一样的。这使网络对输入图像的微小形变更加鲁棒。防止过拟合在一定程度上池化提供了一种类似“信息摘要”的功能避免了网络对过于精确的、可能带有噪声的位置信息进行记忆。2.4 全连接层从特征到决策的“分类器”经过若干轮“卷积-激活-池化”的循环原始图像已经被提炼成了一系列高度抽象、语义化的特征图例如可能有些特征图对应“车轮”有些对应“玻璃窗”。但这些特征图仍然是二维的空间网格结构。全连接层的任务就是将这些空间特征“拍平”并完成最终的分类或回归决策。具体操作是将最后一个池化层或卷积层输出的所有特征图全部展开成一个一维的长向量然后输入到传统的全连接神经网络中。通常在最终输出层例如10个类别的分类之前会有一到两个全连接层。它们的作用是整合所有抽象特征学习特征之间的非线性组合关系最终映射到样本的标签空间。例如当“车轮”、“车窗”、“车灯”等多个特征同时以高权重出现时全连接层就能以很高的置信度判断这是一辆“汽车”。3. 经典CNN架构演进从LeNet到ResNet的设计哲学只看理论组件是枯燥的结合历史上那些里程碑式的网络架构我们能更深刻地理解CNN是如何一步步变得更强大、更高效的。它们不仅是工具更是设计思想的结晶。3.1 LeNet-5开山鼻祖与基础范式1998年由Yann LeCun提出的LeNet-5用于手写数字识别确立了CNN的基本范式卷积层 - 池化层 - 卷积层 - 池化层 - 全连接层 - 输出层。这个简单的架构已经包含了特征提取、降维、分类的全部思想。它成功的关键在于用局部连接和权值共享极大地减少了参数使得用当时的算力训练一个实用的识别系统成为可能。虽然今天看来它很浅但所有现代CNN都是它的直系后代。3.2 AlexNet深度学习的“唤醒者”2012年AlexNet在ImageNet大赛上以压倒性优势夺冠将Top-5错误率从26%大幅降低到15%震惊了整个学术界正式开启了深度学习时代。AlexNet的成功并非偶然它引入了多个关键技术和设计深度增加相比LeNet的5层AlexNet有8层5个卷积层3个全连接层证明了“深度”对于特征抽象至关重要。ReLU激活函数用ReLU替代传统的Sigmoid/Tanh解决了深层网络梯度消失的问题训练速度大幅提升。Dropout在全连接层使用Dropout随机“关闭”一部分神经元强制网络学习冗余的表征是防止过拟合的一剂猛药。数据增强对训练图像进行随机裁剪、水平翻转等操作人工扩大数据集提升模型泛化能力。GPU训练首次利用GPU的并行计算能力使得训练如此大的网络成为可能。AlexNet告诉我们更深、更宽的网络配合正确的训练技巧和硬件能产生质的飞跃。3.3 VGGNet深度与规整化的力量VGGNet2014年的核心思想异常简洁使用更小的卷积核3x3构建更深的网络。为什么是3x3两个3x3卷积层堆叠其感受野相当于一个5x5卷积层但参数量更少2*(33)18 vs 5525并且中间多了一次非线性激活使得判别函数更具判别力。VGGNet通过反复堆叠3x3卷积构建了11层、13层、16层、19层等不同深度的网络VGG-16, VGG-19最为著名。它极致的规整化设计卷积核全是3x3池化全是2x2使其成为理解CNN结构的绝佳教材也证明了网络的深度是提升性能的关键。但它的缺点也很明显参数量巨大主要来自后面的全连接层计算成本高。3.4 GoogLeNet (Inception)宽度与高效计算当大家都在思考如何让网络更深时Google的Inception模块提出了另一种思路让网络更“宽”。一个Inception模块在同一层上并行使用多种尺寸的卷积核1x1, 3x3, 5x5和池化操作然后将所有结果在通道维度上拼接起来。其核心思想是不同尺度的特征在同一个层次上可能都有用让网络自己选择和学习。但直接拼接会导致计算量和通道数爆炸。这里就用到了一个神来之笔1x1卷积。在3x3和5x5卷积之前先使用1x1卷积来降维减少通道数这就像一个“瓶颈”结构能以极小的计算代价大幅减少参数量。1x1卷积的本质是在通道维度上进行全连接操作用于融合和压缩通道信息。GoogLeNet通过堆叠这种高效的Inception模块在保持高性能的同时参数量远少于VGGNet展示了模型设计的精巧性。3.5 ResNet解决深度网络的退化问题当网络深度达到几十层甚至上百层时一个反直觉的现象出现了更深的网络其训练误差和测试误差反而比浅层网络更大。这不是过拟合因为训练误差也高了这被称为网络退化问题。理论上深层网络至少可以学习一个恒等映射把多余层变成yx这样性能不应该比浅层网络差。但现实是在标准网络结构中让多层网络拟合一个恒等映射非常困难。ResNet残差网络2015年提出了一个革命性的解决方案残差学习。它不再让堆叠的层直接去拟合目标映射H(x)而是去拟合残差映射F(x) H(x) - x。这样原始映射就变成了H(x) F(x) x。这个“ x”操作是通过一条快捷连接或称恒等映射将输入直接绕到输出达成的。注意这里的“加”是逐元素相加要求F(x)的输出维度必须与x相同。如果维度不同快捷连接需要通过一个1x1卷积进行升维或降维来匹配。这种结构有两大好处解决梯度消失/爆炸梯度可以通过快捷连接几乎无损地反向传播到更浅的层使得训练极深的网络如ResNet-152成为可能。简化学习目标让网络层去学习一个相对于输入的“微小扰动”F(x)这比学习一个完整的、全新的映射要容易得多。如果最优解就是恒等映射那么直接将F(x)学习为0即可。ResNet的出现使得网络深度突破了千层大关并成为此后几乎所有视觉任务的基础骨架。它的思想深远影响了后续的DenseNet等网络设计。4. 动手实践用PyTorch构建一个CNN进行图像分类理论再精彩不如动手跑一遍代码。我们以经典的CIFAR-10数据集10类彩色小图片32x32像素为例使用PyTorch框架从零构建、训练并评估一个CNN模型。这个过程会让你对数据流、层间维度变化有最直观的感受。4.1 环境准备与数据加载首先确保安装了PyTorch和TorchVision。我们将使用TorchVision提供的数据集和预处理工具。import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim import torchvision import torchvision.transforms as transforms import matplotlib.pyplot as plt import numpy as np # 检查GPU是否可用 device torch.device(cuda if torch.cuda.is_available() else cpu) print(fUsing device: {device}) # 定义数据预处理管道 # CIFAR-10图像已经是32x32比较小我们进行归一化即可。 # 归一化参数均值标准差是CIFAR-10数据集的统计值能加速训练。 transform transforms.Compose([ transforms.ToTensor(), # 将PIL图像或numpy数组转换为Tensor并缩放到[0,1] transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2470, 0.2435, 0.2616)) # (R, G, B)通道的均值和标准差 ]) # 下载并加载训练集和测试集 batch_size 64 trainset torchvision.datasets.CIFAR10(root./data, trainTrue, downloadTrue, transformtransform) trainloader torch.utils.data.DataLoader(trainset, batch_sizebatch_size, shuffleTrue, num_workers2) testset torchvision.datasets.CIFAR10(root./data, trainFalse, downloadTrue, transformtransform) testloader torch.utils.data.DataLoader(testset, batch_sizebatch_size, shuffleFalse, num_workers2) # 类别名称 classes (plane, car, bird, cat, deer, dog, frog, horse, ship, truck)4.2 定义网络模型我们来构建一个简化版的VGG风格网络包含两个卷积块每个块是Conv - ReLU - Conv - ReLU - MaxPool然后接三个全连接层。class SimpleCNN(nn.Module): def __init__(self): super(SimpleCNN, self).__init__() # 第一个卷积块 self.conv1 nn.Conv2d(3, 32, 3, padding1) # 输入3通道(RGB)输出32通道3x3卷积核填充1保持尺寸 self.conv2 nn.Conv2d(32, 64, 3, padding1) self.pool nn.MaxPool2d(2, 2) # 2x2池化步长2 # 第二个卷积块 self.conv3 nn.Conv2d(64, 128, 3, padding1) self.conv4 nn.Conv2d(128, 128, 3, padding1) # 全连接层 # 经过两次2x2池化32x32的图像会变成 32 - 16 - 8 # 最后一个卷积层输出128个通道特征图尺寸为8x8 # 所以展平后的向量长度是 128 * 8 * 8 8192 self.fc1 nn.Linear(128 * 8 * 8, 512) self.fc2 nn.Linear(512, 64) self.fc3 nn.Linear(64, 10) # 输出10个类别 # Dropout层防止过拟合 self.dropout nn.Dropout(0.5) def forward(self, x): # 第一个卷积块 x F.relu(self.conv1(x)) x F.relu(self.conv2(x)) x self.pool(x) # 第二个卷积块 x F.relu(self.conv3(x)) x F.relu(self.conv4(x)) x self.pool(x) # 展平特征图 x x.view(-1, 128 * 8 * 8) # 全连接层 x F.relu(self.fc1(x)) x self.dropout(x) # 通常在全连接层后加Dropout x F.relu(self.fc2(x)) x self.dropout(x) x self.fc3(x) # 输出层不需要激活函数后面用CrossEntropyLoss自带Softmax return x net SimpleCNN().to(device) print(net)维度变化跟踪对于一个batch:输入:[64, 3, 32, 32](batch, channels, height, width)conv1后:[64, 32, 32, 32](padding1尺寸不变)conv2后:[64, 64, 32, 32]pool后:[64, 64, 16, 16](高宽减半)conv3后:[64, 128, 16, 16]conv4后:[64, 128, 16, 16]pool后:[64, 128, 8, 8]view后:[64, 8192]fc1后:[64, 512]fc2后:[64, 64]fc3后:[64, 10]4.3 定义损失函数与优化器我们使用交叉熵损失函数它非常适合多分类任务并且内部集成了Softmax操作。优化器选择带动量的随机梯度下降SGD这是经过时间考验的稳定选择。criterion nn.CrossEntropyLoss() # 学习率是训练中最重要的超参数之一需要根据情况调整 optimizer optim.SGD(net.parameters(), lr0.01, momentum0.9, weight_decay5e-4) # 也可以使用Adam优化器它通常对学习率不那么敏感但有时泛化性能略逊于SGDmomentum # optimizer optim.Adam(net.parameters(), lr0.001)4.4 训练循环训练过程就是反复迭代数据计算损失反向传播更新权重。def train(epochs10): net.train() # 设置为训练模式启用Dropout等 for epoch in range(epochs): running_loss 0.0 for i, data in enumerate(trainloader, 0): # 获取输入数据 inputs, labels data inputs, labels inputs.to(device), labels.to(device) # 梯度清零 optimizer.zero_grad() # 前向传播 计算损失 outputs net(inputs) loss criterion(outputs, labels) # 反向传播 优化 loss.backward() optimizer.step() # 打印统计信息 running_loss loss.item() if i % 200 199: # 每200个mini-batch打印一次 print(f[Epoch {epoch 1}, Batch {i 1}] loss: {running_loss / 200:.3f}) running_loss 0.0 print(Finished Training) # 开始训练先训练5个epoch看看效果 train(epochs5)4.5 模型测试与评估训练完成后我们需要在测试集上评估模型的泛化能力。def evaluate(): net.eval() # 设置为评估模式关闭Dropout等 correct 0 total 0 # 在测试阶段不需要计算梯度可以节省内存和计算 with torch.no_grad(): for data in testloader: images, labels data images, labels images.to(device), labels.to(device) outputs net(images) # 获取预测结果输出中最大值的索引 _, predicted torch.max(outputs.data, 1) total labels.size(0) correct (predicted labels).sum().item() accuracy 100 * correct / total print(fAccuracy of the network on the 10000 test images: {accuracy:.2f} %) return accuracy test_accuracy evaluate()对于一个简单的网络训练5个epoch在CIFAR-10上达到65%-75%的准确率是合理的。要获得更好的性能85%你需要更深的网络如ResNet-18、更长时间的训练、学习率调度、更复杂的数据增强如随机裁剪、水平翻转以及可能的正则化技巧。4.6 可视化卷积核与特征图进阶理解为了更直观地理解CNN在学什么我们可以可视化第一层的卷积核以及中间层的特征图。# 获取第一层卷积核的权重 weights net.conv1.weight.data.cpu() # 权重形状: [out_channels, in_channels, kernel_height, kernel_width] - [32, 3, 3, 3] # 可视化前16个卷积核每个核有3个通道对应RGB fig, axes plt.subplots(4, 8, figsize(12, 6)) # 4行8列 for i, ax in enumerate(axes.flat): if i 32: # 将一个3通道的卷积核转换为图像显示需要做归一化 kernel weights[i] # 将每个通道的值归一化到[0,1]以便显示 kernel (kernel - kernel.min()) / (kernel.max() - kernel.min()) ax.imshow(kernel.permute(1, 2, 0)) # 将形状从[3,3,3]变为[3,3,3]显示 ax.axis(off) ax.set_title(fKernel {i1}) plt.suptitle(First Layer Conv Filters) plt.tight_layout() plt.show()你可能会看到一些类似边缘检测器不同方向的模糊图案。网络从随机初始化开始自动学习到了这些基础特征提取器。5. 超越图像CNN在其他领域的应用与变体虽然CNN起源于图像处理但其核心思想——局部连接、权值共享、层次化特征提取——具有普适性。只要数据具有局部相关性和平移不变性或某种形式的平稳性CNN就能大显身手。5.1 自然语言处理一维卷积文本数据可以看作一个一维序列每个词或字符用一个向量表示。一维卷积核在句子序列上滑动可以捕捉到词级别的n-gram特征即连续几个词组成的短语模式。例如一个大小为3的卷积核每次查看连续的3个词向量提取出一个局部特征。多个这样的卷积核可以提取多种不同的局部语义模式。这在文本分类、情感分析等任务中非常有效并且比RNN/Transformer训练更快。5.2 时序信号分析一维卷积的另一个舞台心电图ECG、脑电图EEG、股票价格、传感器读数等时序数据也具有强烈的局部相关性相邻时间点的值高度相关。一维CNN可以自动学习到信号中的关键波形模式如ECG中的QRS波群用于疾病诊断、异常检测或预测。5.3 图卷积网络将卷积推广到非欧空间传统CNN处理的数据图像、文本、语音本质上是排列在规则网格欧几里得空间上的。但现实世界中很多数据是图结构如社交网络、分子结构、知识图谱。图卷积网络GCN的核心思想是重新定义“局部邻居”和“卷积”操作。在图数据中一个节点的“邻居”是其直接相连的节点。GCN通过聚合节点自身及其邻居的特征信息来更新节点表示这种“消息传递”机制可以看作是图上的卷积操作。GCN在节点分类、链接预测、图分类等任务上取得了巨大成功。5.4 深度可分离卷积移动端与高效模型的利器这是Google在MobileNet等轻量级模型中提出的结构旨在极大减少计算量和参数量。它将标准卷积分解为两个步骤深度卷积每个卷积核只负责一个输入通道在单个通道的空间维度上进行卷积。输入有多少通道就产生多少通道的中间结果。这一步负责过滤空间特征。逐点卷积使用1x1卷积将深度卷积输出的多通道特征进行组合生成新的特征图。这一步负责组合通道信息。深度可分离卷积的计算成本远低于标准卷积通常能减少8到9倍的计算量而精度损失很小非常适合部署在手机、嵌入式设备等资源受限的场景。6. 训练CNN的实战经验与常见陷阱构建网络结构只是第一步让网络有效地学习才是真正的挑战。以下是一些从实践中总结出的关键经验。6.1 数据是王道质量、规模与增强没有好的数据再强大的模型也无济于事。数据质量错误的标签、模糊的图片、不一致的格式会严重干扰训练。在开始前务必进行数据清洗和检查。数据规模深度学习是数据饥渴型的。如果数据量不足模型很容易过拟合。除了收集更多数据数据增强是低成本扩大数据集、提升模型泛化能力的首选方法。对于图像常见的增强包括随机水平/垂直翻转、随机旋转、随机裁剪、颜色抖动亮度、对比度、饱和度、添加噪声等。关键是要使用与测试环境相符的增强例如对于街景识别水平翻转是合理的但垂直翻转可能就不合理。数据标准化将输入数据归一化到零均值和单位方差如我们之前对CIFAR-10做的可以加速模型收敛提升训练稳定性。6.2 优化器与学习率调度训练的“油门”和“刹车”优化器选择SGD with momentum和Adam是最常用的两种。经验上SGDmomentum配合良好的学习率衰减往往能在最终测试精度上达到更好的效果但需要更多的超参数调优。Adam自适应调整每个参数的学习率初期收敛速度极快对初始学习率不敏感是快速实验和原型开发的首选。学习率调度固定学习率通常不是最优的。常见策略包括StepLR每过一定epoch将学习率乘以一个衰减因子如0.1。CosineAnnealingLR学习率按余弦函数从初始值衰减到0效果通常很好。ReduceLROnPlateau当验证集指标不再提升时自动降低学习率。这是最实用的策略之一。Warmup训练初期使用一个很小的学习率逐步提升到预设值有助于稳定训练初期。6.3 过拟合的对抗正则化技术当模型在训练集上表现很好但在测试集上表现很差时就是过拟合了。Dropout如前所述在训练时随机“丢弃”一部分神经元迫使网络不依赖于任何单个神经元学习更鲁棒的特征。通常在最后的全连接层使用。权重衰减在优化器的weight_decay参数中设置本质上是L2正则化惩罚大的权重值鼓励模型使用更小的、更分散的权重从而简化模型。Batch Normalization虽然最初是为了解决内部协变量偏移、加速训练而提出的但它也具有轻微的正则化效果。BN层对每个小批量的数据进行归一化并引入可学习的缩放和平移参数。它允许使用更高的学习率并且对初始化不那么敏感现在已成为深度网络的标配。早停持续监控验证集上的性能。当验证集损失在连续多个epoch不再下降时就停止训练避免在训练集上过度优化。6.4 梯度消失与爆炸深度网络的顽疾在非常深的网络中梯度在反向传播时可能会变得极小消失或极大爆炸导致浅层网络参数无法更新或更新不稳定。梯度爆炸相对容易发现训练时loss会变成NaN。解决方法包括梯度裁剪设置一个阈值当梯度超过时进行缩放、更小的初始化权重、使用BatchNorm。梯度消失更隐蔽表现为深层网络的前几层权重几乎不更新。ReLU及其变体如Leaky ReLU是解决此问题的关键因为它们在正区间的梯度为常数1。残差连接ResNet是另一个根本性的解决方案它创建了一条梯度高速公路让梯度可以直接回流到浅层。权重初始化不恰当的初始化如全零初始化会破坏对称性导致训练失败。常用的初始化方法有Xavier初始化适用于tanh、sigmoid、He初始化适用于ReLU及其变体。在PyTorch中默认的卷积层和全连接层初始化通常已经比较合理。6.5 调试与监控让训练过程透明化监控损失和准确率曲线这是最基本的。训练损失应稳步下降验证损失在初期下降后可能逐渐平稳或上升出现过拟合。准确率应同步上升。可视化特征图如我们之前所做查看中间层的输出可以直观判断网络是否在学习有意义的特征或者某一层是否已经“死掉”输出全零。检查梯度流在训练初期可以打印各层权重的梯度范数。如果某一层的梯度范数异常小接近0可能发生了梯度消失如果异常大可能是梯度爆炸。使用TensorBoard或Weights Biases这些工具可以实时、可视化地记录损失、准确率、权重分布、梯度分布、计算图等是进行复杂实验和调试的利器。从我个人的项目经验来看成功训练一个CNN模型30%在于合理的网络结构设计70%在于数据准备、超参数调优和训练技巧。一个常见的误区是过早地追求复杂的网络结构比如一上来就搞ResNet-152而忽略了数据质量和基础训练管道的搭建。我的建议是从一个像我们上面构建的简单模型开始确保整个数据加载、训练、评估的流程是通畅的得到一个baseline性能。然后再系统地、一次只改变一个变量比如增加数据增强、换优化器、加深网络来观察性能提升这样才能清晰地知道是什么在起作用。记住在深度学习里可复现的、稳定的实验过程比盲目尝试各种“魔法”要重要得多。
返回列表