【深度学习】PyTorch实现VGG16模型及网络层数学原理

一、Demo概述

代码已附在文末

1.1 代码功能

  • ✅ 实现VGG16网络结构
  • ✅ 在CIFAR10数据集上训练分类模型

在这里插入图片描述

1.2 环境配置

详见【深度学习】Windows系统Anaconda + CUDA + cuDNN + Pytorch环境配置


二、各网络层概念

2.1 卷积层(nn.Conv2d)

nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, padding=1)
参数含义作用说明
in_channels输入通道数(如RGB图为3)接收输入的维度
out_channels输出通道数(卷积核数量)提取不同特征类型的数量
kernel_size卷积核尺寸(如3x3)决定感知的局部区域大小
padding边缘填充像素数保持输出尺寸与输入一致

作用:通过滑动窗口提取局部特征(如边缘、颜色分布)
示例: 输入3通道224x224图片 → 通过64个3x3卷积核 → 输出64通道224x224特征图

1)卷积后的输出尺寸

卷积后的输出尺寸由以下公式决定:
输出尺寸 = 输入尺寸 − 卷积核尺寸 + 2 × 填充 步长 + 1 \text{输出尺寸} = \frac{\text{输入尺寸} - \text{卷积核尺寸} + 2 \times \text{填充}}{\text{步长}} + 1 输出尺寸=步长输入尺寸卷积核尺寸+2×填充+1

在代码中:

  • 输入尺寸:224x224
  • 卷积核尺寸:3x3 → (k=3)
  • 填充 (padding):1 → (p=1)
  • 步长 (stride):1 → (s=1)(默认值)

代入公式:
输出尺寸 = 224 − 3 + 2 × 1 1 + 1 = 224 \text{输出尺寸} = \frac{224 - 3 + 2 \times 1}{1} + 1 = 224 输出尺寸=12243+2×1+1=224
因此,宽度和高度保持不变(仍为224x224)。

2)64个卷积核的输出不同
  • 参数初始化差异
  • 初始权重随机:每个卷积核的权重矩阵在训练前通过随机初始化生成(如正态分布)
  • 示例
    • 卷积核1初始权重可能偏向检测水平边缘
    • 卷积核2初始权重可能随机偏向检测红色区域

在这里插入图片描述

  • 反向传播差异:每个卷积核根据其当前权重计算出的梯度不同
    数学表达:
    Δ W k = − η ∂ L ∂ W k \Delta W_k = -\eta \frac{\partial \mathcal{L}}{\partial W_k} ΔWk=ηWkL
    • W k W_k Wk:第k个卷积核的权重
    • η \eta η:学习率
    • 不同位置的梯度导致权重更新方向不同
3)卷积核参数的「通道敏感度」

卷积操作的完整计算式为:
输出 = ∑ c = 1 C in ( 输入通道 c ∗ 卷积核权重 c ) + 偏置 \text{输出} = \sum_{c=1}^{C_{\text{in}}} (\text{输入通道}_c \ast \text{卷积核权重}_c) + \text{偏置} 输出=c=1Cin(输入通道c卷积核权重c)+偏置
其中:

  • C in C_{\text{in}} Cin:输入通道数(例如RGB图为3)
  • ∗ \ast 表示卷积运算
  • 偏置的意义在于允许激活非零特征
  • 不同的卷积核权重决定了通道敏感度,比如RGB三个通道,R通道权重放大即偏好红色特征,红色通道的输入会被加强
4)卷积核参数的「空间敏感度」​
  • 卷积核矩阵决定空间关注模式,每个卷积核的权重矩阵就像一张「特征检测模板」,决定了在图像中的哪些空间位置组合能激活该核的输出。
    [ [ − 1 , 0 , 1 ] , [ − 2 , 0 , 2 ] , [ − 1 , 0 , 1 ] ] [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]] [[1,0,1],[2,0,2],[1,0,1]]
  • 这是一个经典的Sobel水平边缘检测核,当输入图像在水平方向有明暗变化时(如水平边缘),左右两侧的权重差异会放大响应值
5)参数协同工作

例. 综合检测红色水平边缘
假设一个卷积核的参数如下:

  • 空间权重(与之前Sobel核相同):
    [[-1, 0, 1],[-2, 0, 2],[-1, 0, 1]]
    
  • 通道权重
    • 红色:0.9, 绿色:0.1, 蓝色:-0.2
  • 在红色通道中检测水平边缘 → 高响应
  • 在绿色/蓝色通道的同类边缘 → 响应被抑制
  • 最终输出:红色物体的水平边缘被突出显示

2.2 激活函数(nn.ReLU)

没有激活函数的神经网络等效于单层线性模型

nn.ReLU(inplace=True)

激活函数有很多种,这里是最简单的一种ReLU
在这里插入图片描述

1)ReLU 的数学原理

ReLU(Rectified Linear Unit)的数学定义非常简单:
f ( x ) = max ⁡ ( 0 , x ) f(x) = \max(0, x) f(x)=max(0,x)

  • 正向传播

    • 当输入 x > 0 x > 0 x>0 时,输出 f ( x ) = x f(x) = x f(x)=x(直接传递信号)。
    • 当输入 x < = 0 x <= 0 x<=0 时,输出 f ( x ) = 0 f(x) = 0 f(x)=0(完全抑制信号)。
  • 反向传播

    • x > 0 x > 0 x>0 时,梯度为 ∂ f ∂ x = 1 \frac{\partial f}{\partial x} = 1 xf=1(梯度无衰减)。
    • x < = 0 x <= 0 x<=0 时,梯度为 ∂ f ∂ x = 0 \frac{\partial f}{\partial x} = 0 xf=0(梯度归零)。
2)引入非线性

如果神经网络只使用线性激活函数(如 ( f(x) = x )),无论堆叠多少层,最终等效于单层线性变换(( W_{\text{total}} = W_1 W_2 \cdots W_n )),无法建模复杂函数。

  • ReLU 的非线性:通过分段处理(保留正信号、抑制负信号),打破线性组合,使网络能够学习非线性决策边界。
  • 实际意义:ReLU 允许网络在不同区域使用不同的线性函数(正区间为线性,负区间为常数),从而组合出复杂的非线性函数。
3)缓解梯度消失

梯度消失问题通常发生在深层网络中,当反向传播时梯度逐层衰减,导致浅层参数无法更新。
ReLU 的缓解机制如下:

  • 导数值恒为 1(正区间): 相比于 Sigmoid(导数最大 0.25)、Tanh(导数最大 1),ReLU 在正区间的梯度恒为 1,避免梯度随网络深度指数级衰减。

  • 稀疏激活性: ReLU 会抑制负值信号(输出 0),导致部分神经元“死亡”,但活跃的神经元梯度保持完整,使有效路径的梯度稳定传递。

  • 对比其他激活函数Sigmoid:导数 ( f’(x) = f(x)(1-f(x)) ),当 ( |x| ) 较大时,导数趋近 0。 ReLU:仅需判断 ( x > 0 ),计算高效且梯度稳定。

4)输入与输出

我们输入张量尺寸为 [Channels=64, Height=224, Width=224]

  • 维度不变性
    • ReLU 是逐元素操作(element-wise),不会改变输入输出的形状,输出尺寸仍为 64×224×224
  • 数值变化
    • 正区间:保留原始值,维持特征强度。
    • 负区间:置零,可能造成特征稀疏性(部分像素/通道信息丢失)。
  • 实际影响
    • 如果输入中存在大量负值(如未规范化的数据),ReLU 会过滤掉这些信息,可能影响模型性能。
    • 通常需配合批归一化(BatchNorm) 使用,将输入调整到以 0 为中心,减少负值抑制。
5)局限性与拓展
  • 神经元死亡(Dead ReLU)
    • 当输入恒为负时,梯度为 0,导致神经元永久失效。
  • 解决方案
    • 使用 Leaky ReLU:允许负区间有微小梯度(如 ( f(x) = \max(0.01x, x) ))。
    • Parametric ReLU (PReLU):将负区间的斜率作为可学习参数。

2.3 池化层(nn.MaxPool2d)

nn.MaxPool2d(kernel_size=2, stride=2)
1)数学原理

最大池化(Max Pooling)是一种非线性下采样操作,其核心是对输入张量的局部区域取最大值。以参数 kernel_size=2, stride=2 为例:

  • 窗口划分:在输入张量的每个通道上,以 2×2 的窗口(无重叠)滑动。
  • 步长操作:每次滑动 2 个像素(横向和纵向均移动 2 步),确保窗口不重叠。
  • 计算规则:每个窗口内的最大值作为输出。

示例
输入矩阵(2×2 窗口,步长 2):

输入 (4×4):
[[1,  2,  3,  4],[5,  6,  7,  8],[9,  10, 11, 12],[13, 14, 15, 16]]输出 (2×2):
[[6,  8],[14, 16]]
  • 第一个窗口(左上角)的值为 [1,2;5,6] → 最大值 6
  • 第二个窗口(右上角)的值为 [3,4;7,8] → 最大值 8
  • 依此类推。
2)核心作用
  1. 降维(下采样)

    • 降低特征图的空间分辨率(高度和宽度),减少后续层的计算量和内存消耗。
    • 例如,输入尺寸 64×224×224 → 输出 64×112×112(通道数不变)。
  2. 特征不变性增强

    • 平移不变性:即使目标在输入中有轻微平移,最大池化仍能捕捉到其主要特征。
    • 旋转/缩放鲁棒性:通过保留局部最显著特征,降低对细节变化的敏感度。
  3. 防止过拟合

    • 减少参数量的同时,抑制噪声对模型的影响。
  4. 扩大感受野

    • 通过逐步下采样,后续层的神经元能覆盖输入图像中更大的区域。
3)对输入输出的影响

以 PyTorch 的 nn.MaxPool2d(kernel_size=2, stride=2) 为例:

  1. 输入尺寸[Batch, Channels, Height, Width](如 64×3×224×224)。

  2. 输出尺寸
    输出高度 = ⌊ 输入高度 − kernel_size stride ⌋ + 1 \text{输出高度} = \left\lfloor \frac{\text{输入高度} - \text{kernel\_size}}{\text{stride}} \right\rfloor + 1 输出高度=stride输入高度kernel_size+1
    同理计算宽度。

    • 若输入为 224×224 → 输出为 112×112(224-2)/2 +1 = 112)。
  3. 通道数不变:池化操作独立作用于每个通道,不改变通道数。

  4. 数值变化

    • 每个窗口仅保留最大值,其余数值被丢弃。
    • 输出张量的值域与输入一致,但稀疏性可能增加(大量低值被过滤)。
4)与卷积层的区别
特性卷积层 (nn.Conv2d)最大池化层 (nn.MaxPool2d)
可学习参数是(权重和偏置)否(固定操作)
作用提取局部特征并组合下采样,保留显著特征
输出通道数可自定义(通过 out_channels与输入通道数相同
非线性需配合激活函数(如 ReLU)自带非线性(取最大值)

2.4 全连接层(nn.Linear)

nn.Linear(512*7*7, 4096)
1)数学原理

全连接层(Fully Connected Layer)的数学本质是线性变换 + 偏置,其公式为:
y = W x + b y = Wx + b y=Wx+b

  • 输入向量 x ∈ R n x \in \mathbb{R}^{n} xRn:将输入张量展平为一维向量(例如 512×7×7 512 × 7 × 7 = 25088 512 \times 7 \times 7 = 25088 512×7×7=25088维)。

  • 权重矩阵 W ∈ R m × n W \in \mathbb{R}^{m \times n} WRm×n:维度为 [输出维度, 输入维度],即 4096 × 25088 4096 \times 25088 4096×25088

  • 偏置向量 b ∈ R m b \in \mathbb{R}^{m} bRm:维度为 4096

  • 输出向量 y ∈ R m y \in \mathbb{R}^{m} yRm:维度为 4096

  • 假设输入向量 x ∈ R 25088 x \in \mathbb{R}^{25088} xR25088,权重矩阵 W ∈ R 4096 × 25088 W \in \mathbb{R}^{4096 \times 25088} WR4096×25088,偏置 b ∈ R 4096 b \in \mathbb{R}^{4096} bR4096,则输出向量的第 i i i 个元素为:
    y i = ∑ j = 1 25088 W i , j ⋅ x j + b i y_i = \sum_{j=1}^{25088} W_{i,j} \cdot x_j + b_i yi=j=125088Wi,jxj+bi
    每个输出元素是输入向量的加权和,权重矩阵的每一行定义了一个“特征选择器”。


2)核心作用
  1. 全局特征整合

    • 将卷积层提取的局部特征(如边缘、纹理)通过矩阵乘法整合为全局语义信息(如物体类别)。
    • 例如:将 512×7×7 的特征图(对应图像不同区域的特征)映射到更高维度的抽象语义空间(如“猫”“狗”的分类特征)。
  2. 非线性建模能力

    • 通常配合激活函数(如 ReLU)使用,增强网络的非线性表达能力。
  3. 维度压缩/扩展

    • 通过调整输出维度(如 4096),实现特征压缩(降维)或扩展(升维)。

3)对输入输出的影响

nn.Linear(512*7*7, 4096) 为例:

  1. 输入尺寸

    • 假设输入为 [Batch=64, Channels=512, Height=7, Width=7],需先展平为 [64, 512×7×7=25088]
  2. 输出尺寸

    • 输出为 [Batch=64, 4096],即每个样本被映射到 4096 维的特征空间。
  3. 参数数量

    • 权重矩阵参数: 25088 × 4096 = 102 , 760 , 448 25088 \times 4096 = 102,760,448 25088×4096=102,760,448
    • 偏置参数: 4096 4096 4096
    • 总计:102,764,544 个可训练参数。
4)适用场景与局限性
  1. 适用场景

    • 传统卷积网络(如 AlexNet、VGG)的分类头部。
    • 需要全局特征交互的任务(如语义分割中的上下文建模)。
  2. 局限性

    • 参数量过大:例如本例中超过 1 亿参数,易导致过拟合和计算成本高。
    • 空间信息丢失:展平操作破坏特征图的空间结构,不适合需要保留位置信息的任务(如目标检测)。
  3. 替代方案

    • 全局平均池化(GAP):将 512×7×7 压缩为 512×1×1,再输入全连接层,大幅减少参数(例如 ResNet)。
    • 1×1 卷积:保留空间维度,实现局部特征交互。

三、VGG16网络结构实现

3.1 特征提取层

在这里插入图片描述

self.features = nn.Sequential(# Block 1 (2 conv layers)nn.Conv2d(3, 64, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(64, 64, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),  # 输出尺寸112x112# 后续Block结构类似,此处省略...
)
3.1.1 特征提取的作用
  • 特征提取的本质:通过卷积核的局部计算、ReLU的非线性激活、池化的降维,将原始像素逐步抽象为高层语义特征。
  • 数学公式的递进
    像素 → 卷积+ReLU 边缘 → 卷积+ReLU 纹理 → 池化 物体部件 → ... 语义特征 \text{像素} \xrightarrow{\text{卷积+ReLU}} \text{边缘} \xrightarrow{\text{卷积+ReLU}} \text{纹理} \xrightarrow{\text{池化}} \text{物体部件} \xrightarrow{\text{...}} \text{语义特征} 像素卷积+ReLU 边缘卷积+ReLU 纹理池化 物体部件... 语义特征
  • 对输入的影响:空间分辨率降低,通道数增加,特征语义逐步抽象化。
3.1.2 数学原理
  • 卷积层(核心操作)
    每个卷积核(如 3×3)在输入特征图上滑动,计算局部区域的加权和:
    输出 ( x , y ) = ∑ i = − 1 1 ∑ j = − 1 1 输入 ( x + i , y + j ) ⋅ 权重 ( i , j ) + 偏置 \text{输出}(x,y) = \sum_{i=-1}^{1}\sum_{j=-1}^{1} \text{输入}(x+i, y+j) \cdot \text{权重}(i,j) + \text{偏置} 输出(x,y)=i=11j=11输入(x+i,y+j)权重(i,j)+偏置

    • 权重共享:同一卷积核在不同位置使用相同权重,捕捉空间不变性特征。
    • 多通道:每个卷积核输出一个通道,多个卷积核组合可提取多维度特征。
  • ReLU 激活函数
    ReLU ( x ) = max ⁡ ( 0 , x ) \text{ReLU}(x) = \max(0, x) ReLU(x)=max(0,x)

    • 作用:引入非线性,增强模型对复杂特征的表达能力。
  • 最大池化层
    输出 ( x , y ) = max ⁡ i , j ∈ 窗口 输入 ( x + i , y + j ) \text{输出}(x,y) = \max_{i,j \in \text{窗口}} \text{输入}(x+i, y+j) 输出(x,y)=i,j窗口max输入(x+i,y+j)

    • 作用:降维并保留最显著特征,提升模型对位置变化的鲁棒性。

以输入图像 [Batch, 3, 224, 224] 为例,逐层分析变化:

层类型输入尺寸输出尺寸数学影响
Conv2d(3→64)[B,3,224,224][B,64,224,224]提取 64 种基础特征(边缘/颜色)
ReLU[B,64,224,224][B,64,224,224]非线性激活,抑制负响应
Conv2d(64→64)[B,64,224,224][B,64,224,224]细化特征,增强局部模式组合
MaxPool2d[B,64,224,224][B,64,112,112]下采样,保留最显著特征
重复块(128→256→512)逐层增加通道数,提取更复杂特征
最终输出[B,512,7,7][B,512,7,7]高层语义特征,输入分类器或检测头
  • 通道数变化3 → 64 → 128 → 256 → 512,表示特征复杂度递增。
  • 空间分辨率下降224x224 → 7x7,通过池化逐步聚焦全局语义。

3.2 分类器层

在这里插入图片描述

self.classifier = nn.Sequential(nn.Dropout(p=0.5),  # 防止过拟合nn.Linear(512*7*7, 4096),  # 特征图维度计算nn.ReLU(inplace=True),nn.Dropout(p=0.5),nn.Linear(4096, 4096),nn.ReLU(inplace=True),nn.Linear(4096, num_classes)
)
3.2.1 分类器层作用

分类器层(Classifier)是网络的最后阶段,负责将卷积层提取的高级语义特征映射到类别概率空间。其核心功能包括:

  1. 特征整合:将全局特征转化为与任务相关的判别性表示。
  2. 分类决策:通过全连接层(Linear)和激活函数(ReLU)生成类别得分。
  3. 正则化:通过Dropout减少过拟合,提升模型泛化能力。
3.2.2 数学原理

假设输入特征为 x ∈ R 512 × 7 × 7 x \in \mathbb{R}^{512 \times 7 \times 7} xR512×7×7(展平后为25088维),分类器层的计算流程如下:

  1. Dropout层(训练阶段):
    x drop = Dropout ( x , p = 0.5 ) x_{\text{drop}} = \text{Dropout}(x, p=0.5) xdrop=Dropout(x,p=0.5)

    • 随机将50%的神经元输出置零,防止过拟合。
  2. 全连接层1(降维):
    y 1 = W 1 x drop + b 1 ( W 1 ∈ R 4096 × 25088 , b 1 ∈ R 4096 ) y_1 = W_1 x_{\text{drop}} + b_1 \quad (W_1 \in \mathbb{R}^{4096 \times 25088}, \, b_1 \in \mathbb{R}^{4096}) y1=W1xdrop+b1(W1R4096×25088,b1R4096)

    • 将25088维特征压缩到4096维。
  3. ReLU激活
    a 1 = max ⁡ ( 0 , y 1 ) a_1 = \max(0, y_1) a1=max(0,y1)

  4. 重复Dropout和全连接层
    y 2 = W 2 ( Dropout ( a 1 ) ) + b 2 ( W 2 ∈ R 4096 × 4096 ) y_2 = W_2 (\text{Dropout}(a_1)) + b_2 \quad (W_2 \in \mathbb{R}^{4096 \times 4096}) y2=W2(Dropout(a1))+b2(W2R4096×4096)
    a 2 = max ⁡ ( 0 , y 2 ) a_2 = \max(0, y_2) a2=max(0,y2)

  5. 最终分类层
    y logits = W 3 a 2 + b 3 ( W 3 ∈ R 10 × 4096 ) y_{\text{logits}} = W_3 a_2 + b_3 \quad (W_3 \in \mathbb{R}^{10 \times 4096}) ylogits=W3a2+b3(W3R10×4096)

    • 输出10维向量(CIFAR-10的类别数)。
3.2.3 与特征提取层的对比
特性特征提取层(卷积层)分类器层(全连接层)
输入类型原始像素或低级特征高级语义特征(如512×7×7)
操作类型局部卷积、池化全局线性变换、非线性激活
参数分布权重共享(卷积核)全连接权重(无共享)
主要功能提取空间局部特征(边缘→纹理→语义部件)整合全局特征,输出类别概率
维度变化通道数增加,空间分辨率降低特征维度压缩,最终输出类别数

三,完整代码

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms, models
from torch.utils.data import DataLoader
import time# 数据预处理
# 将CIFAR-10的32x32图像缩放至224x224(VGG16的标准输入尺寸)。
# 使用ImageNet的均值和标准差进行归一化。
# 缺少数据增强(如随机裁剪、翻转等)。
transform = transforms.Compose([transforms.Resize(224),  # VGG16 需要 224x224 的输入transforms.ToTensor(),transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])# 加载 CIFAR-10 数据集
train_dataset = datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
test_dataset = datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=4)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False, num_workers=4)# 定义 VGG16 模型
# 自定义VGG16模型,包含13个卷积层和3个全连接层。
# 输入尺寸为224x224,经过5次最大池化后特征图尺寸为7x7,全连接层输入维度为512 * 7 * 7=25088,符合原版VGG16设计。
# ​特征提取层:13个卷积层(含ReLU激活)+ 5个最大池化层
# ​分类层:3个全连接层(含Dropout)
# ​输出维度:num_classes(CIFAR-10为10)
class VGG16(nn.Module):def __init__(self, num_classes=10):super(VGG16, self).__init__()# VGG16 的卷积层部分self.features = nn.Sequential(nn.Conv2d(3, 64, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(64, 64, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),nn.Conv2d(64, 128, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(128, 128, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),nn.Conv2d(128, 256, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(256, 256, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(256, 256, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),nn.Conv2d(256, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2))# VGG16 的全连接层部分self.classifier = nn.Sequential(nn.Dropout(),nn.Linear(512 * 7 * 7, 4096),nn.ReLU(inplace=True),nn.Dropout(),nn.Linear(4096, 4096),nn.ReLU(inplace=True),nn.Linear(4096, num_classes))def forward(self, x):x = self.features(x)  # 通过卷积层x = torch.flatten(x, 1)  # 展平x = self.classifier(x)  # 通过全连接层return x# 实例化模型
model = VGG16(num_classes=10)# 使用 GPU 如果可用
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)# 损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)# 训练函数
def train(model, train_loader, criterion, optimizer, num_epochs=10):model.train()  # 切换到训练模式epoch_times = []for epoch in range(num_epochs):start_time = time.time()running_loss = 0.0correct = 0total = 0for i, (inputs, labels) in enumerate(train_loader):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 = outputs.max(1)total += labels.size(0)correct += predicted.eq(labels).sum().item()epoch_loss = running_loss / len(train_loader)epoch_acc = 100. * correct / totalepoch_end_time = time.time()epoch_duration = epoch_end_time - start_timeepoch_times.append(epoch_duration)print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {epoch_loss:.4f}, Accuracy: {epoch_acc:.2f}%, Time: {epoch_duration:.2f}s')avg_epoch_time = sum(epoch_times) / num_epochsprint(f'\nAverage Epoch Time: {avg_epoch_time:.2f}s')# 测试函数
def test(model, test_loader):model.eval()  # 切换到评估模式correct = 0total = 0with torch.no_grad():for inputs, labels in test_loader:inputs, labels = inputs.to(device), labels.to(device)outputs = model(inputs)_, predicted = outputs.max(1)total += labels.size(0)correct += predicted.eq(labels).sum().item()accuracy = 100. * correct / totalprint(f'Test Accuracy: {accuracy:.2f}%')# 训练和测试模型
train(model, train_loader, criterion, optimizer, num_epochs=10)
test(model, test_loader)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/76670.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决RecyclerView在调用smoothScrollToPosition后最后一个item底部超出屏幕的问题

要解决RecyclerView在调用smoothScrollToPosition后最后一个item底部超出屏幕的问题&#xff0c;可以使用自定义的LinearSmoothScroller&#xff0c;使其底部对齐屏幕。步骤如下&#xff1a; 创建自定义的SmoothScroller类&#xff1a; 继承LinearSmoothScroller并重写getVerti…

k8s亲和力和非亲和力

在 Kubernetes 中&#xff0c;亲和力&#xff08;Affinity&#xff09;和非亲和力&#xff08;Anti-Affinity&#xff09;是用于控制 Pod 调度策略的机制&#xff0c;它们可以帮助优化资源利用率、提高应用性能和可用性。以下是亲和力和非亲和力的详细解释&#xff1a; 亲和力…

开发一款游戏需要哪些岗位角色参与?

常见分类 1. 游戏策划&#xff08;Game Designer&#xff09; 核心职责&#xff1a;设计游戏的玩法、规则、内容和整体体验。 具体工作&#xff1a; 系统设计&#xff1a;设计游戏的战斗、经济、成长、社交等核心系统。 数值设计&#xff1a;平衡角色属性、装备数值、经济系…

Asp.NET Core WebApi 创建带鉴权机制的Api

构建一个包含 JWT&#xff08;JSON Web Token&#xff09;鉴权的 Web API 是一种常见的做法&#xff0c;用于保护 API 端点并验证用户身份。以下是一个基于 ASP.NET Core 的完整示例&#xff0c;展示如何实现 JWT 鉴权。 1. 创建 ASP.NET Core Web API 项目 使用 .NET CLI 或 …

Jenkins 发送钉钉消息

这里不介绍 Jenkins 的安装&#xff0c;可以网上找到很多安装教程&#xff0c;重点介绍如何集成钉钉消息。 需要提前准备钉钉机器人的 webhook 地址。&#xff08;网上找下&#xff0c;很多教程&#xff09; 下面开始配置钉钉机器人&#xff0c;登录 Jenkins&#xff0c;下载 …

CentOS中离线安装DockerCompos并用其部署Rabbitmq(使用离线导入导出docker镜像方式)

场景 DockerDockerCompose实现部署jenkins,并实现jenkinsfile打包SpringBootVue流水线项目过程详解、踩坑记录(附镜像资源、离线包资源下载)&#xff1a; DockerDockerCompose实现部署jenkins,并实现jenkinsfile打包SpringBootVue流水线项目过程详解、踩坑记录(附镜像资源、离…

stm32week11

stm32学习 八.stm32基础 2.stm32内核和芯片 F1系统架构&#xff1a;4个主动单元和4个被动单元 AHB是内核高性能总线&#xff0c;APB是外围总线 总线矩阵将总线和各个主动被动单元连到一起 ICode总线直接连接Flash接口&#xff0c;不需要经过总线矩阵 AHB&#xff1a;72MHz&am…

贪心算法:部分背包问题深度解析

简介&#xff1a; 该Java代码基于贪心算法实现了分数背包问题的求解&#xff0c;核心通过单位价值降序排序和分阶段装入策略实现最优解。首先对Product数组执行双重循环冒泡排序&#xff0c;按wm(价值/重量比)从高到低重新排列物品&#xff1b;随后分两阶段装入&#xff1a;循环…

13. Langchain异步处理:提升应用性能的关键技巧

引言&#xff1a;从"顺序等待"到"并行加速" 2025年某电商平台引入LangChain异步处理后&#xff0c;大促期间订单处理能力提升5倍&#xff0c;系统响应延迟降低70%。本文将基于LangChain的异步架构&#xff0c;详解如何通过并行执行流式处理&#xff0c;让…

ros2-rviz2控制unity仿真的6关节机械臂,探索从仿真到实际应用的过程

文章目录 前言&#xff08;Introduction&#xff09;搭建开发环境&#xff08;Setup Development Environment&#xff09;在window中安装Unity&#xff08;Install Unity in window&#xff09;创建Docker容器&#xff0c;并安装相关软件&#xff08;Create Docker containers…

计算机组成原理笔记(十四)——3.4指令类型

一台计算机的指令系统可以有上百条指令&#xff0c;这些指令按其功能可以分成几种类型&#xff0c;下面分别介绍。 3.4.1数据传送类指令 一、核心概念与功能定位 数据传送类指令是计算机指令系统中最基础的指令类型&#xff0c;负责在 寄存器、主存、I/O设备 之间高效复制数…

各开源协议一览

在 GitHub 上&#xff0c;开源项目通常会使用一些常见的开源协议来定义项目的使用、修改和分发规则。以下是目前 GitHub 上最常见的几种开源协议及其差异和示例说明&#xff1a; TL;DR 协议宽松程度是否强制开源专利保护适用场景MIT最宽松否无希望代码被广泛使用Apache 2.0宽松…

51c自动驾驶~合集17

我自己的原文哦~ https://blog.51cto.com/whaosoft/13793157 #汇聚感知、定位、规划控制的自动驾驶系统 自动驾驶技术在应用到车辆上之后可以通过提高吞吐量来缓解道路拥堵&#xff0c;通过消除人为错误来提高道路安全性&#xff0c;并减轻驾驶员的驾驶负担&#xff0c;从…

小程序开发指南

小程序开发指南 目录 1. 小程序开发概述 1.1 什么是小程序1.2 小程序的优势1.3 小程序的发展历程 2. 开发准备工作 2.1 选择开发平台2.2 开发环境搭建2.3 开发模式选择 3. 小程序开发流程 3.1 项目规划3.2 界面设计3.3 代码开发3.4 基本开发示例3.5 数据存储3.6 网络请求3.7 …

Day15:关于MySQL的编程技术——基础知识

前言&#xff1a;先创建一个练习的数据库和数据 1.创建数据库并创建数据表的基本结构 -- 创建练习数据库 CREATE DATABASE db_programming; USE db_programming;-- 创建员工表&#xff08;包含各种数据类型&#xff09; CREATE TABLE employees (emp_id INT PRIMARY KEY AUTO…

批处理脚本bat丨遍历一个包含项目名称的数组,并对每个文件中的项目执行 git pull 操作 (一键拉很多文件的代码)

文章目录 前言一、操作方式二、文件展示三、分析代码结构四、代码五、需要注意的潜在问题六、改进后的代码七、改进说明八、感谢 前言 由于之前git服务部署在本地服务器&#xff0c;处于代码安全角度考虑。领导让我将所有的项目代码手动物理备份一份并且发给他。 这种傻傻的操…

【C++】C与C++、C++内存空间、堆与栈

C嘎嘎嘎嘎嘎~ C与C的区别与联系 C内存空间 int global_var; // 未初始化全局变量&#xff0c;BSS段 const char* str "Hello"; // 字符串常量text段 in数据段void func() {static int static_var; // 未初始化的静态变量&#xff0c;数据段int local_var; …

舵机:机器人领域的“关节革命者”

机器人的技术&#xff0c;每一个细微的进步都可能引领一场行业变革。而在这场变革中&#xff0c;舵机作为机器人关节的核心部件&#xff0c;正悄然上演着一场革命性的应用风暴。从简单的关节运动到复杂的姿态控制&#xff0c;舵机以其卓越的性能和无限的可能&#xff0c;重新定…

微前端的不断探索之路—— qiankun 实战与思考!

全文目录&#xff1a; 开篇语&#x1f4dd; 前言&#x1f6e0;️ 微前端是什么&#xff1f;为什么需要它&#xff1f;&#x1f4a1; 先从“前端痛点”说起&#x1f9d0; 微前端的优势 &#x1f939;‍♀️ qiankun 简介与核心概念&#x1f31f; 为什么选择 qiankun&#xff1f;…

拆解加密黑盒

在Web安全与数据爬取领域&#xff0c;JavaScript加密黑盒的逆向工程是核心技术之一。本文基于行业通用方法论与实战案例&#xff0c;提炼出一套标准化的五步逆向流程&#xff0c;涵盖目标定位、代码提取、逻辑分析、算法复现到自动化集成的全链路解决方案&#xff0c;帮助开发者…