神经网络初步

神经网络各层详解

我们用一个极简例子贯穿全文:

输入: 28×28 灰度图
结构: Conv(1→8通道, 3×3核) → ReLU → MaxPool(2×2) → FC(→10类)
输出: 数字 0-9 的识别结果

2. 什么是算子

算子是计算图上的一个节点——确定输入输出形状的计算单元

输入 Tensor ──→ [ 算子 ] ──→ 输出 Tensor
                  ↑
           固定的数学公式 + 参数(可选)

类比 C 编程:算子是函数,算子组合是调用链,ONNX 是这些函数的静态调用图。

本文涉及的算子:

算子 有参数? 参数 作用
Conv weight (72), bias (8) 提取局部特征
BN γ (8), β (8) 归一化数值分布
ReLU 引入非线性
MaxPool 降采样、扩大感受野
FC weight (13520), bias (10) 综合全局做决策

3. Conv2d(1→8, 3×3):卷积层

3.1 参数组成

输入:  1 通道(灰度图)
输出:  8 通道
核:    3×3

权重:  [out=8] × [in=1] × [h=3] × [w=3] = 72 个 float32
偏置:  8 个 float32

3.2 卷积怎么算

一个 3×3 窗口在输入图上滑动,每次右移 1 格(stride=1):

  3×3 输入窗口       3×3 卷积核
 ┌──────────┐    ┌─────────────┐
 │ a  b  c  │    │ w₀  w₁  w₂  │
 │ d  e  f  │ ⊙  │ w₃  w₄  w₅  │ = a·w₀ + b·w₁ + ... + i·w₈ + bias
 │ g  h  i  │    │ w₆  w₇  w₈  │
 └──────────┘    └─────────────┘
                  ↑
         这 9 个数构成的核,在整张图上共享
         不管窗口滑到哪,用的都是同一组 w

权值共享:72 个 float 就描述了一个完整的卷积层。不是每个输出位置存一组权重,而是每个通道存一组。

28×28 输入, k=3, p=0 → 输出 26×26×8
总计算量: 26 × 26 × 8 × 9 = 48,672 次乘加 (MAC)

4. Stride 与 Padding:Conv 的两个超参数

这两个超参数控制核的滑动行为——不在权重文件里,但在 ONNX 里显式记录。

4.1 Stride(步幅)

核每次移动的间距。

S=1: 输出 ≈ 输入尺寸           S=2: 输出长宽减半(降采样)
输出 = floor((N - K) / S + 1)

4.2 Padding(填充)

在输入图边缘外围补 0。

P=0: 越卷越小                    P=1 (k=3): 输出等大 ("same padding")
输出 = floor((N + 2×P - K) / S + 1)

经典组合:

配置 效果
P=0, S=1 越卷越小,边缘信息丢失
P=1, S=1 (k=3) "same padding",输出等大
P=0, S=2 长宽减半(经典下采样)

4.3 ONNX 里 strides 二元组、pads 四元组的原因

  • strides: [H, W]:只有空间维度,从头到尾一致
  • pads: [H_begin, W_begin, H_end, W_end]:不对称场景需要补不同数量

例如 7×7 输入 + k=3 + S=2 → 需要总 padding=1,只能上补 1 下补 0:pads: [1, 0, 0, 0]


5. Pool:用空间换语义

5.1 本质

在固定窗口内做聚合操作,用一个统计量替代窗口内所有值。Pool 没有可学习参数

4×4 输入, 2×2 窗口, S=2:

原始:             MaxPool (取最大):    AveragePool (取平均):
┌───────────┐    ┌───────────┐       ┌───────────┐
│ 3  1  4  2│    │    7  8   │       │  1.8  2.7 │
│ 7  2  5  8│ →  │           │  或   │           │
│ 9  6  2  1│    │    9  6   │       │  3.8  2.6 │
│ 5  3  8  6│    └───────────┘       └───────────┘
└───────────┘

本质:用 1 个数概括 n×n 个数的信息,顺势扔掉空间精度。

5.2 降采样

窗口按 stride 跳着走 → 输出长宽减半。副作用是平移不变性——图像平移 1 像素,MaxPool 输出可能不变。对分类是好事(鲁棒),对定位是坏事。


6. 感受野

输出特征图上一个像素对应回输入图上的区域大小。

输入 28×28 ──Conv3×3──→ 26×26 ──MaxPool2×2──→ 13×13

输出[0,0] ← 2×2 Pool窗口 ← 每个来自 Conv 的 3×3 感受野
→ 对应输入 4×4 的区域

层越深,感受野越大:

浅层 Conv (k3):   看到 3×3   → 边角
+ Pool (2×2, S2):  翻倍
中层 Conv (k3):   看到 10×10 → 形状
+ Pool (2×2, S2):  再翻倍
深层 Conv (k3):   看到 22×22 → 语义("这像眼睛")

不 Pool,3 层 k3 Conv 只有 7×7 感受野;加 Pool,轻松到 22×22。


7. FC:全连接层

PyTorch: nn.Linear(in, out),ONNX: Gemm

7.1 本质:矩阵乘法 + 偏置

y₁ = x₁·w₁₁ + x₂·w₂₁ + x₃·w₃₁ + x₄·w₄₁ + b₁
y₂ = x₁·w₁₂ + x₂·w₂₂ + x₃·w₃₂ + x₄·w₄₂ + b₂
y₃ = x₁·w₁₃ + x₂·w₂₃ + x₃·w₃₃ + x₄·w₄₃ + b₃

矩阵: y = xW + b

7.2 FC vs Conv

Conv FC
连接方式 局部(每个输出连 9 个输入) 全连接(每个输出连所有输入)
参数共享 权重空间复用 每个连接独立参数
参数数量 Conv(1→8,k3): 72 FC(1352→10): 13520
空间结构 保留 丢弃(输入展平为 1D)

7.3 在网络里的角色

Conv + Pool → 提取局部特征,保留空间结构
Flatten     → 展平为一维向量
FC          → 综合全局做最终决策

8. BatchNorm:夹在 Conv 和 ReLU 之间

8.1 为什么需要 BN

Conv 输出的值域不可控,可能很大或很小,导致训练不稳定。BN 把每个通道的值拉到均值≈0、方差≈1 的分布。

Conv → BN → ReLU

         x - mean
  x̂ = ──────────────        ← 标准化
      √(var² + ε)

  y = γ × x̂ + β              ← 缩放+平移(γ、β 可学习)

8.2 四个参数

参数 个数 (8通道) 怎么来的
γ (weight) 8 训练学习(与 Conv.weight 同理)
β (bias) 8 训练学习
μ (running_mean) 8 训练累积统计量
σ² (running_var) 8 训练累积统计量

8.3 推理时的算子融合

BN 公式展开后就是一个乘加:y = αx + b(α、b 是推理时已知常量),可吸收进 Conv 的 weight 和 bias。Conv+BN+ReLU → 一个融合算子,省两次 DDR 读写。


9. Conv + Pool 组合的意义

分工

Conv:  提取特征,"这里有一条竖线"
Pool:  丢弃精度,换取视野,"大概左上角有笔画"

每经过一组 Conv+Pool:

Conv Pool
参数 学习"看什么" 无参数
空间尺寸 略减 减半
感受野 略增 (k-1) 翻倍
通道数 变深 不变
计算量 ≈零

最终效果

输入 28×28 (像素)
  → Conv: 纹理、边缘
  → Pool: 尺寸减半,感受野翻倍
  → Conv: 曲线、拐角组合
  → Pool: 再减半
  → Conv: 完整形状、语义
  → FC:   综合全局 → 输出决策

本质是用空间精度换语义抽象:像素 → 边角 → 形状 → 概念。

posted @ 2026-07-25 02:50  BorisDimitri  阅读(1)  评论(0)    收藏  举报