1.导入包
import torch
from torch import nn
from d2l import torch as d2l
2.卷积层的相关运算:跟着沐神手写二维交叉运算。我承认我是一个打字员
def corr2d(X, K):
'''计算二维互相关运算'''
kh, kw = K.shape # 把卷积核的高和宽赋值给kh=K.shape[0],kw=K.shape[1]
xh, xw = X.shape # 把输入X的高和宽赋值给xh,xw
Y = torch.zeros((xh - kh + 1, xw - kw + 1)) # 定义输出Y的矩阵大小
for i in range(Y.shape[0]): # 获取Y矩阵的 高
for j in range(Y.shape[1]): # 获取Y矩阵的 宽
Y[i, j] = (X[i:i + kh, j:j + kw] * K).sum() # i + kh就是矩阵适应卷积核的大小
# a * b 这个*就是卷积运算
return Y
3.写完之后我们验证一下:
看看输出是不是Output

X = torch.tensor([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
K = torch.tensor([[0, 1], [2, 3]])
print(corr2d(X, K))
输出结果:
tensor([[19., 25.],
[37., 43.]])
4.实现二维卷积层:y=wx+b。w是卷积核自动生成,b是偏置
class Conv2D(nn.Module):
def __init__(self, kernel_size):
super(Conv2D, self).__init__()
self.weight = nn.Parameter(torch.rand(kernel_size)) # 均匀分布权重w
self.bias = nn.Parameter(torch.zeros(1)) # 偏置b
def forward(self, x):
return corr2d(x, self.weight) + self.bias
5.写好之后我们进行一个简单的应用,检测图像中不同颜色的边缘 1代表黑色 0是白色
X = torch.ones((6, 8))
X[:, 2:6] = 0
'''
tensor([[1., 1., 0., 0., 0., 0., 1., 1.],
[1., 1., 0., 0., 0., 0., 1., 1.],
[1., 1., 0., 0., 0., 0., 1., 1.],
[1., 1., 0., 0., 0., 0., 1., 1.],
[1., 1., 0., 0., 0., 0., 1., 1.],
[1., 1., 0., 0., 0., 0., 1., 1.]])
'''
设置卷积核,如果颜色一样 输出是 0 如果不一样输出非0
K = torch.tensor([[1.0, -1.0]]) # 如果颜色一样 输出是 0 如果不一样输出非0
Y = corr2d(X, K)
Y
'''
tensor([[ 0., 1., 0., 0., 0., -1., 0.],
[ 0., 1., 0., 0., 0., -1., 0.],
[ 0., 1., 0., 0., 0., -1., 0.],
[ 0., 1., 0., 0., 0., -1., 0.],
[ 0., 1., 0., 0., 0., -1., 0.],
[ 0., 1., 0., 0., 0., -1., 0.]])
1代表 从白到黑
-1代表 从黑到白
'''
但是 此刻的卷积核只能检测垂直的边缘,如果给图像转置后,横向检测后就检测不出来
corr2d(X.t(), K)
'''
tensor([[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]])
全是0 代表颜色不变化,错误
'''
所以,卷积核要学习。下面用到的是Conv2d系统提供的不是上面写的哪个。
①卷积层的有关设置,输入通道是1,因为是矩阵(黑白图像。彩色是3).输出也是1。卷积核的大小是1*2.
conv2d = nn.Conv2d(1, 1, kernel_size=(1, 2), bias=False) # in_channels输入通道是1,矩阵
②给输入X和真实值Y reshape
通道数是1,样本数是1.大小是6*8
X = X.reshape((1, 1, 6, 8)) # 通道数1,样本维1, 输入X大小6*8
Y = Y.reshape((1, 1, 6, 7))
③进行训练
for i in range(38):
Y_hat = conv2d(X) # 预测值
l = (Y_hat - Y) ** 2 # loss
conv2d.zero_grad() # 梯度为0
l.sum().backward() # 标量才能反向传播,所以l.sum()
conv2d.weight.data[:] -= 3e-2 * conv2d.weight.grad # 梯度下降
if (i + 1) % 2 == 0:
print(f'batch{i + 1},loss{l.sum():.3f}')
# 得到的卷积核K
print(conv2d.weight.data.reshape((1,2)))
得到学习的卷积核:tensor([[ 1.0000, -1.0000]])

二 填充和步幅
①获取填充后的输出 从序号2开始
import torch
from torch import nn
# 填充和步幅
def comp_conv2d(conv2d, X):
X = X.reshape((1, 1) + X.shape) # 通道数1,样本维1, 输入X维度大小
Y = conv2d(X) # 调用conv2d函数
return Y.reshape(Y.shape[2:]) # Y是4D,从序号2开始取
②padding=1,四周填充1行 8=8-3+1+1*2
conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1) # 左右都填充一行 8-3+1+ 2
X = torch.rand(size=(8, 8))
print(comp_conv2d(conv2d, X).shape) # 输出torch.Size([8, 8])
③填充不同的高度padding=(2, 1)
# 填充不同的高度
conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=(2, 1)) # 左右都填充一行 h:8-3+1+2*2 w 8-3+1+1*2 = 8
X = torch.rand(size=(8, 8))
print(comp_conv2d(conv2d, X).shape) # torch.Size([10, 8])
2.①padding=1, stride=2步幅为2,填充1的输出大小的计算 输入的维度整除步长 8/2=4
8-3+1+2/2
conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1, stride=2)
print(comp_conv2d(conv2d, X).shape) # torch.Size([4, 4])
②复杂:(8-3+0+3)/3=2 ( 8-5+1+4)/4=2
conv2d = nn.Conv2d(1, 1, kernel_size=(3, 5), padding=(0, 1), stride=(3, 4))
print(comp_conv2d(conv2d,X).shape) # torch.Size([2, 2]) (8-3+0+3)/3 (8-5+1*2+4)/4
本文介绍了如何使用PyTorch实现二维卷积运算,并通过实例演示了卷积核如何检测图像边缘。随后,通过学习卷积层参数来适应性地检测颜色变化,同时探讨了填充、步幅在卷积过程中的作用。

1069

被折叠的 条评论
为什么被折叠?



