思路:
本部分实现了一个简单的二维卷积神经网络(Conv2D),并进行了简单的实践,以下是代码流程。
代码流程:
import torch
from torch import nn
import torch.nn.functional as F
导入所需的 PyTorch 模块。
def corr2d(X, K, bias=None, stride=1, padding=0):
h, w = K.shape
# 计算输出矩阵的大小
out_h = (X.shape[0] - h + 2 * padding) // stride + 1
out_w = (X.shape[1] - w + 2 * padding) // stride + 1
# 构建输出矩阵
Y = torch.zeros(out_h, out_w)
# 添加填充
X_padded = F.pad(X, (padding, padding, padding, padding))
# 滑动,并进行求值
for i in range(0, X_padded.shape[0] - h + 1, stride):
for j in range(0, X_padded.shape[1] - w + 1, stride):
Y[i // stride, j // stride] = (X_padded[i:i + h, j:j + w] * K).sum()
# 添加偏置项
if bias is not None:
Y += bias
return Y
这是一个自定义的二维卷积函数 corr2d
,用于计算输入 X
与卷积核 K
的卷积结果。它支持设置步长(stride)和填充(padding),并可以选择添加偏置项(bias)。
class Conv2D(nn.Module):
# 对参数进行初始化,w设为随机值,b设为零值
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
这是一个继承自 nn.Module
的自定义卷积层类 Conv2D
。在初始化函数 __init__
中,它定义了卷积核的权重 self.weight
和偏置项 self.bias
,并将它们都设置为可训练的参数。在前向传播函数 forward
中,它调用了之前定义的 corr2d
函数来计算卷积结果,并加上偏置项。
简单实践:
kernel_size = (1, 2)
conv_layer = Conv2D(kernel_size)
定义了卷积核的大小为 (1, 2),并创建了一个 Conv2D
的实例 conv_layer
。
X = torch.ones((6, 8))
X[:, 2:6] = 0
创建了一个大小为 (6, 8) 的张量 X
,并将其中的一部分置为 0。
K = torch.tensor([[1.0, -1.0]])
Y = corr2d(X, K)
创建了一个大小为 (1, 2) 的卷积核张量 K
,并通过调用 corr2d
函数计算了输入 X
与卷积核 K
的卷积结果,保存在张量 Y
中。
optimizer = torch.optim.SGD(conv_layer.parameters(), lr=0.1)
定义了一个随机梯度下降(SGD)优化器,用于更新卷积层的参数。优化器的参数是卷积层的可训练参数 conv_layer.parameters()
,学习率为 0.1。
for i in range(100):
optimizer.zero_grad()
Y_hat = conv_layer(X)
l = F.mse_loss(Y_hat, Y) # 均方误差
l.sum().backward()
optimizer.step()
if (i + 1) % 2 == 0:
print(f'batch {i + 1}, loss {l.sum():.3f}')
进行训练循环,迭代 100 次。在每次迭代中,首先将优化器的梯度缓存清零 optimizer.zero_grad()
,然后通过调用卷积层的前向传播函数 conv_layer(X)
得到预测结果 Y_hat
,并计算预测结果与真实结果之间的均方误差损失 l
。接着,通过调用 l.sum().backward()
计算损失相对于可训练参数的梯度,并通过调用 optimizer.step()
更新参数。最后,每隔 2 次迭代,打印当前迭代的批次号和损失值。
print(conv_layer.weight.data)
打印训练后的卷积层权重的数值。
结果:
batch 90, loss 0.002
batch 92, loss 0.001
batch 94, loss 0.001
batch 96, loss 0.001
batch 98, loss 0.001
batch 100, loss 0.001
tensor([[ 0.9462, -0.9426]])
可以看到预测的结果跟 [1,-1]还算接近,说明效果还算不错。