标准卷积的初始化和详细计算步骤,在代码中哪一步开始更新卷积核(权重)

标准卷积的初始化和详细计算步骤,在代码中哪一步开始更新卷积核(权重)

flyfish

卷积 - 感受野(Receptive Field)
在卷积神经网络(CNN)中为什么可以使用多个较小的卷积核替代一个较大的卷积核,以达到相同的感受野

卷积操作转换为矩阵乘法
动画展示卷积的计算过程
卷积层的输出

卷积的计算 - numpy的实现 1
卷积的计算 - numpy的实现 2

卷积的计算 - im2col 1
卷积的计算 - im2col 2
卷积的计算 - im2col 3

卷积核的初始化

实践经验,卷积核(权重)常用的初始化方法包括:

  1. 随机初始化
    使用标准正态分布(均值为0,标准差为1)的随机数初始化权重。
    例如:nn.init.normal_(tensor, mean=0, std=1)

  2. 均匀分布初始化
    使用均匀分布的随机数初始化权重。
    例如:nn.init.uniform_(tensor, a=0, b=1)

  3. Xavier初始化(Glorot初始化)
    适用于Sigmoid和Tanh激活函数。
    公式:U[-sqrt(6 / (fan_in + fan_out)), sqrt(6 / (fan_in + fan_out))]
    例如:nn.init.xavier_uniform_(tensor)

  4. He初始化(Kaiming初始化)
    适用于ReLU激活函数。
    公式:N(0, sqrt(2 / fan_in))
    例如:nn.init.kaiming_normal_(tensor, mode='fan_in', nonlinearity='relu')

  5. 常数初始化
    使用常数值初始化权重。
    例如:nn.init.constant_(tensor, val=0)

卷积核初始化代码示例

一个简单的卷积神经网络中初始化卷积核。这里使用了Xavier初始化方法来初始化权重,并将偏置初始化为零。

import torch
import torch.nn as nn
import torch.nn.init as init

class ConvNet(nn.Module):
    def __init__(self):
        super(ConvNet, self).__init__()
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=1, padding=0)
        self.conv2 = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=1, padding=0)
        
        # Initialize weights using Xavier initialization
        init.xavier_uniform_(self.conv1.weight)
        init.xavier_uniform_(self.conv2.weight)
        
        # Initialize biases (if using) to zero
        if self.conv1.bias is not None:
            init.constant_(self.conv1.bias, 0)
        if self.conv2.bias is not None:
            init.constant_(self.conv2.bias, 0)

    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        return x

# Instantiate the network
net = ConvNet()

# Print initialized weights
print("Conv1 weights:", net.conv1.weight)
print("Conv2 weights:", net.conv2.weight)

一个完整的训练示例

import torch
import torch.nn as nn
import torch.optim as optim

# 定义一个简单的卷积神经网络
import torch
import torch.nn as nn
import torch.optim as optim

# 定义一个简单的卷积神经网络
class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=1, padding=0)
        self.conv2 = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=1, padding=0)

    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        return x

# 初始化网络、损失函数和优化器
net = SimpleCNN()
criterion = nn.MSELoss()
optimizer = optim.SGD(net.parameters(), lr=0.01)

# 示例输入和目标
input_tensor = torch.randn(1, 1, 5, 5)
target_tensor = torch.randn(1, 1, 1, 1)

# 示例训练步骤
num_epochs = 3
for epoch in range(num_epochs):
    print(f'Epoch {epoch+1}/{num_epochs}',":start")
    optimizer.zero_grad()   # 清空梯度
    outputs = net(input_tensor)  # 前向传播
    loss = criterion(outputs, target_tensor)  # 计算损失
    loss.backward()  # 反向传播,计算梯度

    print(f'Updated before optimizer.step() conv1 weights: {net.conv1.weight}')

    optimizer.step()  # 使用优化器更新参数

    # 打印损失
    print(f'Epoch {epoch+1}/{num_epochs}, Loss: {loss.item()}')

    # 打印卷积核的梯度
    print(f'Gradient of conv1 weights after backward: {net.conv1.weight.grad}')
    
    # 打印更新后的卷积核权重
    print(f'Updated after optimizer.step()conv1 weights: {net.conv1.weight}')

代码解释

对每个 epoch:
1. optimizer.zero_grad():清空之前计算的梯度。
2. outputs = net(input_tensor):前向传播,计算模型输出。
3. loss = criterion(outputs, target_tensor):计算损失。
4. loss.backward():反向传播,计算梯度,并将其存储在参数的 .grad 属性中。
5. optimizer.step():优化器根据存储的梯度和学习率更新模型的参数。

打印输出

Epoch 1/3 :start
Updated before optimizer.step() conv1 weights: Parameter containing:
tensor([[[[-0.0435, -0.0704,  0.0776],
          [-0.2496, -0.2133,  0.1125],
          [-0.0481, -0.2408,  0.1175]]]], requires_grad=True)
Epoch 1/3, Loss: 0.12892843782901764
Gradient of conv1 weights after backward: tensor([[[[ 0.0707, -0.2553, -0.1103],
          [ 0.3029,  0.4249,  0.3205],
          [-0.2261, -0.0902,  0.1702]]]])
Updated after optimizer.step()conv1 weights: Parameter containing:
tensor([[[[-0.0442, -0.0679,  0.0787],
          [-0.2527, -0.2175,  0.1093],
          [-0.0459, -0.2399,  0.1158]]]], requires_grad=True)
Epoch 2/3 :start
Updated before optimizer.step() conv1 weights: Parameter containing:
tensor([[[[-0.0442, -0.0679,  0.0787],
          [-0.2527, -0.2175,  0.1093],
          [-0.0459, -0.2399,  0.1158]]]], requires_grad=True)
Epoch 2/3, Loss: 0.11386992782354355
Gradient of conv1 weights after backward: tensor([[[[ 0.0681, -0.2368, -0.1066],
          [ 0.2894,  0.4049,  0.2971],
          [-0.2128, -0.0811,  0.1610]]]])
Updated after optimizer.step()conv1 weights: Parameter containing:
tensor([[[[-0.0449, -0.0655,  0.0797],
          [-0.2556, -0.2216,  0.1063],
          [-0.0437, -0.2391,  0.1142]]]], requires_grad=True)
Epoch 3/3 :start
Updated before optimizer.step() conv1 weights: Parameter containing:
tensor([[[[-0.0449, -0.0655,  0.0797],
          [-0.2556, -0.2216,  0.1063],
          [-0.0437, -0.2391,  0.1142]]]], requires_grad=True)
Epoch 3/3, Loss: 0.10046691447496414
Gradient of conv1 weights after backward: tensor([[[[ 0.0655, -0.2198, -0.1027],
          [ 0.2760,  0.3853,  0.2757],
          [-0.2002, -0.0729,  0.1523]]]])
Updated after optimizer.step()conv1 weights: Parameter containing:
tensor([[[[-0.0455, -0.0633,  0.0808],
          [-0.2583, -0.2254,  0.1036],
          [-0.0417, -0.2383,  0.1127]]]], requires_grad=True)

根据输出结果

Updated after optimizer.step()conv1 weights: Parameter containing:
tensor([[[[-0.0442, -0.0679,  0.0787],
          [-0.2527, -0.2175,  0.1093],
          [-0.0459, -0.2399,  0.1158]]]], requires_grad=True)
Epoch 2/3 :start
Updated before optimizer.step() conv1 weights: Parameter containing:
tensor([[[[-0.0442, -0.0679,  0.0787],
          [-0.2527, -0.2175,  0.1093],
          [-0.0459, -0.2399,  0.1158]]]], requires_grad=True)

net.conv1.weight.grad 存储了 conv1 层的权重梯度,
optimizer.step() 则真正的更新模型的参数。

更新模型参数

在卷积神经网络中,每一层都有若干卷积核。这些卷积核的权重和偏置是在训练过程中更新的。
通过反向传播算法(backpropagation),计算每个参数的梯度,并使用优化算法(如SGD、Adam等)更新这些权重和偏置,以最小化损失函数。其中特征图是通过卷积运算得到的输出,不是模型的参数。使用优化器更新参数 optimizer.step()这步更新的就是卷积核 (权重)和偏置 (如果有的话)

标准卷积操作详细计算

参数
  • 输入特征图:5x5,内容为 1 到 25

  • 卷积核:3x3,内容为 1 到 9

  • 步幅:1

  • 填充:0

输入特征图和卷积核

输入特征图

1   2   3   4   5
6   7   8   9   10
11  12  13  14  15
16  17  18  19  20
21  22  23  24  25

卷积核

1  2  3
4  5  6
7  8  9

使用 PyTorch 计算结果

import torch
import torch.nn as nn

# 定义输入特征图和卷积核
input_tensor = torch.tensor([
    [1, 2, 3, 4, 5],
    [6, 7, 8, 9, 10],
    [11, 12, 13, 14, 15],
    [16, 17, 18, 19, 20],
    [21, 22, 23, 24, 25]
], dtype=torch.float32).unsqueeze(0).unsqueeze(0)  # 添加批次维度和通道维度

# 定义卷积核
kernel = torch.tensor([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
], dtype=torch.float32)

# 转换卷积核为与Conv2d兼容的形状
weight = kernel.view(1, 1, 3, 3)  # 1个输出通道,1个输入通道,3x3卷积核

# 定义卷积层
conv = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=1, padding=0, bias=False)

# 设置卷积层的权重为我们定义的卷积核
conv.weight = nn.Parameter(weight)

# 执行卷积操作
output_tensor = conv(input_tensor)

# 打印输出特征图
print(output_tensor)

运行结果应该如下:

tensor([[[[411., 456., 501.],
          [636., 681., 726.],
          [861., 906., 951.]]]], grad_fn=<ConvolutionBackward0>)

计算输出特征图尺寸

根据公式:
H out = ⌊ H in + 2 P − K S ⌋ + 1 H_{\text{out}} = \left\lfloor \frac{H_{\text{in}} + 2P - K}{S} \right\rfloor + 1 Hout=SHin+2PK+1
W out = ⌊ W in + 2 P − K S ⌋ + 1 W_{\text{out}} = \left\lfloor \frac{W_{\text{in}} + 2P - K}{S} \right\rfloor + 1 Wout=SWin+2PK+1由于步幅 S = 1 S=1 S=1,填充 P = 0 P=0 P=0,卷积核大小 K = 3 K=3 K=3,输入特征图大小 5 × 5 5 \times 5 5×5
H out = ⌊ 5 + 0 − 3 1 ⌋ + 1 = 3 H_{\text{out}} = \left\lfloor \frac{5 + 0 - 3}{1} \right\rfloor + 1 = 3 Hout=15+03+1=3
W out = ⌊ 5 + 0 − 3 1 ⌋ + 1 = 3 W_{\text{out}} = \left\lfloor \frac{5 + 0 - 3}{1} \right\rfloor + 1 = 3 Wout=15+03+1=3
所以输出特征图的尺寸是 3x3。

详细逐位置计算输出特征图

第一位置(左上角,起始点 (0,0))
输入区域:
1   2   3
6   7   8
11  12  13

卷积计算:
1*1 + 2*2 + 3*3 + 6*4 + 7*5 + 8*6 + 11*7 + 12*8 + 13*9
= 1 + 4 + 9 + 24 + 35 + 48 + 77 + 96 + 117 = 411
第二位置(水平移动一格,起始点 (0,1))
输入区域:
2   3   4
7   8   9
12  13  14

卷积计算:
2*1 + 3*2 + 4*3 + 7*4 + 8*5 + 9*6 + 12*7 + 13*8 + 14*9
= 2 + 6 + 12 + 28 + 40 + 54 + 84 + 104 + 126 = 456
第三位置(水平移动一格,起始点 (0,2))
输入区域:
3   4   5
8   9   10
13  14  15

卷积计算:
3*1 + 4*2 + 5*3 + 8*4 + 9*5 + 10*6 + 13*7 + 14*8 + 15*9
= 3 + 8 + 15 + 32 + 45 + 60 + 91 + 112 + 135 = 501
第四位置(垂直移动一格,起始点 (1,0))
输入区域:
6   7   8
11  12  13
16  17  18

卷积计算:
6*1 + 7*2 + 8*3 + 11*4 + 12*5 + 13*6 + 16*7 + 17*8 + 18*9
= 6 + 14 + 24 + 44 + 60 + 78 + 112 + 136 + 162 = 636
第五位置(水平移动一格,起始点 (1,1))
输入区域:
7   8   9
12  13  14
17  18  19

卷积计算:
7*1 + 8*2 + 9*3 + 12*4 + 13*5 + 14*6 + 17*7 + 18*8 + 19*9
= 7 + 16 + 27 + 48 + 65 + 84 + 119 + 144 + 171 = 681
第六位置(水平移动一格,起始点 (1,2))
输入区域:
8   9   10
13  14  15
18  19  20

卷积计算:
8*1 + 9*2 + 10*3 + 13*4 + 14*5 + 15*6 + 18*7 + 19*8 + 20*9
= 8 + 18 + 30 + 52 + 70 + 90 + 126 + 152 + 180 = 726
第七位置(垂直移动一格,起始点 (2,0))
输入区域:
11  12  13
16  17  18
21  22  23

卷积计算:
11*1 + 12*2 + 13*3 + 16*4 + 17*5 + 18*6 + 21*7 + 22*8 + 23*9
= 11 + 24 + 39 + 64 + 85 + 108 + 147 + 176 + 207 = 861
第八位置(水平移动一格,起始点 (2,1))
输入区域:
12  13  14
17  18  19
22  23  24

卷积计算:
12*1 + 13*2 + 14*3 + 17*4 + 18*5 + 19*6 + 22*7 + 23*8 + 24*9
= 12 + 26 + 42 + 68 + 90 + 114 + 154 + 184 + 216 = 906
第九位置(水平移动一格,起始点 (2,2))
输入区域:
13  14  15
18  19  20
23  24  25

卷积计算:
13*1 + 14*2 + 15*3 + 18*4 + 19*5 + 20*6 + 23*7 + 24*8 + 25*9
= 13 + 28 + 45 + 72 + 95 + 120 + 161 + 192 + 225 = 951

最终输出特征图

通过上述计算步骤,我们得到输出特征图为:

411  456  501
636  681  726
861  906  951
  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

西笑生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值