import torch
from torch import nn
from d2l import torch as d2l
def corr2d(X,K):
#计算二维互相关运算
h,w = K.shape #kernel核矩阵 K.shape里有h和w,表示行数和列数/高和宽
Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))
#输出Y的高度:X.shape[0] - h + 1 输入的高h - kernel的高h + 1
#输出Y的宽度:X.shape[1] - w + 1 输入的宽度 - kernel的宽度w + 1
#用2个for loop对Y的i和j作计算
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()
#从i开始,到h行; 从j列开始,往后到w列, 和kernel矩阵作点积, 最后求和
return Y
X = torch.tensor([[0.0,1.0,2.0], [3.0,4.0,5.0], [6.0,7.0,8.0]]) #和书上一样
K = torch.tensor([[0.0,1.0],[2.0,3.0]])
corr2d(X,K) #使用上面的函数
print(corr2d(X,K))
'''
和书上计算的一样
tensor([[19., 25.],
[37., 43.]])
'''
# 实现二维卷积层
class Conv2D(nn.Module):
def _init_(self,kernel_size): #kernel size 是3*3的矩阵
super().__init__()
self.weight = nn.Parameter(torch.rand(kernel_size))
self.bias = nn.Parameter(torch.zeros(1)) #bias是0,一个标量
def forward(self,x):
return corr2d(x,self.weight) + self.bias
#x和weight做互相关运算 + bias
#卷积层的简单运用:检测图像中不同颜色的边缘
X= torch.ones((6,8)) #图像输入6*8
X[:, 2:6] = 0 #把图片中间一块变成0
print(X)
'''
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.]])
从1-0的时候,图片会有从黑到白的过程
'''
K = torch.tensor([[1.0,-1.0]]) #自定义一个核
#输出Y中的1达标从白色到黑色的边缘,-1代表从黑色到白色的边缘
Y = corr2d(X,K)
print(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.]])
'''
#卷积核k只能检测垂直边缘,不垂直就不能检测了
print(corr2d(X.t(),K)) #对X做转置
'''
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.]])
'''
#学习由X生成Y的卷积核
conv2d = nn.Conv2d(1,1,kernel_size=(1,2),bias = False)
#卷积核思维输入和输出格式:批量大小、通道、高度、宽度
X = X.reshape((1,1,6,8))
Y = Y.reshape((1,1,6,7))
for i in range(10):
Y_hat = conv2d(X)
l = (Y_hat - Y ) **2
conv2d.zero_grad()
l.sum().backward()
conv2d.weight.data[:] -= 3e-2 * conv2d.weight.grad
#learning rate = 3e - 2 * weight
if (i+1) % 2 == 0:
print(f'batch{i+1}, loss{l.sum():.3f}')
#每2个batch之后print参数
'''
batch2, loss13.181
batch4, loss2.263
batch6, loss0.401
batch8, loss0.076
batch10, loss0.016
'''
print(conv2d.weight.data.reshape((1, 2)))
'''
tensor([[ 0.9732, -0.9893]])
'''
总结:
对全连接层使用1.平移不变 2.局部性 原则, 就会得到卷积层
卷积运算是交叉相关
二维:一般图像是二维
一维:文本、语言、时序序列
三维:视频、医学图像、气象地图
练习:
-
构建一个具有对角线边缘的图像
X
。-
如果将本节中举例的卷积核
K
应用于X
,会发生什么情况? -
如果转置
X
会发生什么? -
如果转置
K
会发生什么?
-
-
在我们创建的
Conv2D
自动求导时,有什么错误消息? -
如何通过改变输入张量和卷积核张量,将互相关运算表示为矩阵乘法?
-
手工设计一些卷积核:
-
二阶导数的核的形式是什么?
-
积分的核的形式是什么?
-
得到d次导数的最小核的大小是多少?
-