Schwertlilien
As a recoder: notes and ideas.

Mon Dec 22 2025 00:00:00 GMT+0800 (中國標準時間)

卷积相关代码

说来弄了这么久,到底ResNet到底是什么样的网络结构都没看过,所以去看看相关的东西,然后就有搜到了CNN的卷积,于是也看看。

填充一些文字,以便于网页能够正常显示。

二维互相关(交叉关联)的相关代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import torch
from torch import nn
from d21 import torch as d2l

def corr2d(X,K):
'''
X: input
K: kernel
Y: output
'''
h,w = K.shape
Y = torch.zeros((X.shape[0]-h+1,X.shape[1]-w+1))
for i in range(Y.shape[0]):
for j in range(Y.shape[1]):
Y(i,j) = (X[i:i+h,j:j+w]*K).sum()

return Y

实现二维卷积层:

1
2
3
4
5
6
7
8
class Conv2D(nn.Module):
def __init__(self,kernel_size):
super().__init__()
self.weight = nn.Parameter(torch.rand(kernel_size))
self.bias = nn.Parameter(torch.zeros(1))

def forward(self,x):
return corr2d(x,self.weight)+self.bias

使用库函数:

1
conv2d = nn.Conv2d(1,1,kernel_size=3,padding=1)
  1. kernel_size 传单个数值时,PyTorch 会自动将其转为正方形卷积核(n×n)
  2. 不仅是kernel_sizepaddingstridedilation等参数都遵循同样的规则
  3. 若需要非正方形卷积核,需传入元组形式(如(3,5))明确指定高度和宽度

池化层:增加平移不变性的冗余,避免很尖锐的单像素巨大差异。

1
pool2d =. nn.MaxPool2d(input=...,padding=1,stride=2)

批归一化层:增加noise,加速收敛。

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
26
def batch_norm(X,gamma,beta,moving_mean,moving_var,eps,momentum):
if not torch.is_grad_enabled():
# inference:使用全局的均值方差
X_hat = (X-moving_mean)/torch.sqrt(moving_var+eps)
else:
# train:断言表示只支持全连接层或是2D卷积层。
assert len(X.shape) in (2,4)
if len(X.shape) == 2:
# 全连接层的情况(B,H)
# dim=0: 对batch维度(第0维)求均值/方差,即对每个特征在所有样本上求统计量
mean = X.mean(dim=0)
var = ((X-mean)**2).mean(dim=0)
else:
# 卷积层的情况(B,C,H,W)
# dim=(0,2,3): 对batch(0)、高度(2)、宽度(3)维度求均值/方差,保留通道(1)维度的统计量
mean = X.mean(dim=(0,2,3),keepdim=True) #(1,C,1,1),通过keepdim=True保持4维,与X(B,C,H,W)维度匹配,支持广播计算
var = ((X-mean)**2).mean(dim=0,2,3)
X_hat = (X-mean)/torch.sqrt(var+eps)

# 对全局的均值方差进行更新
moving_mean = momentum*moving_mean+(1.0-momentum)*mean
moving_var = momentum*moving_var+(1.0-momentum)*var

Y = gamma*X_hat+beta
return Y,moving_mean.data,moving_var.data
# .data会返回当前 Tensor 的非梯度追踪版本(即requires_grad=False),且与原 Tensor共享内存(修改.data 返回的 Tensor 会影响原 Tensor)。

为什么这里要用.data

  1. moving_meanmoving_var全局滑动平均变量,不属于模型可训练参数(不需要计算梯度),只是用来保存训练过程中累计的均值 / 方差,供推理时使用。
  2. 如果直接返回moving_meanmoving_var,它们会携带梯度计算的信息(即使不需要),可能导致梯度图冗余,甚至意外参与梯度计算;而.data可以剥离这些梯度信息,只返回纯数值。
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class BatchNorm(nn.Module):
def __init__(self,num_features,num_dims):
super().__init__()
if num_dims==2:
shape = (1,num_features)
else:
shape = (1,num_features,1,1)
self.gamma = nn.Parameter(torch.ones(shape))
self.beta = nn.Parameter(torch.zeros(shape))
self.moving_mean = torch.zeros(shape)
self.moving_var = torch.ones(shape)
# 这四个值是batch norm layer需要维护的东西

def forward(self,X):
if self.moving_mean.device != X.device:
self.moving_mean = self.moving_mean.to(X.device)
self.moving_var = self.moving_var.to(X.device)

Y,self.moving_mean,self.moving_var = batch_norm(X,self.gamma,self.beta,self.moving_mean,self.moving_var,eps=1e-5,momentum=0.9)
return Y


# 使用
net = nn.Sequential(
nn.Conv2d(1,6,kernel_size=5),
BatchNorm(6,num_dims=4),
nn.Sigmoid(),
nn.MaxPool2d(kernel_size=2,stride=2),

nn.Conn2d(6,16,kernel_size=5),
BatchNorm(16,num_dims=4),
nn.Sigmoid(),
nn.MaxPool2d(kernel_size=2,stride=2),

nn.Flatten(),
nn.Linear(16*4*4,120),
BatchNorm(120,num_dims=2),
nn.Sigmoid(),

nn.Linear(120,84),
BatchNorm(184,num_dims=2),
nn.Sigmoid(),
nn.Linear(84,10)
)
# 调包
nn.BatchNorm(6)
搜索
匹配结果数:
未搜索到匹配的文章。