20/76-卷积,填充,步幅,多通道输入输出

19/76

卷积层总结
1、卷积层将输入矩阵和核矩阵进行交叉相关,加上偏移所得到输出。
2、核矩阵和偏移是可学习的参数。
3、核矩阵的大小是超参数。

import torch
from torch import nn

def corr2d(X, K):  # 本函数已保存在d2lzh_pytorch包中方便以后使用,x是输入,k是核矩阵
    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

'''
X = torch.tensor([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
K = torch.tensor([[0, 1], [2, 3]])
print(corr2d(X, K))
'''

#二维卷积层
class Conv2D(nn.Module):
    def __init__(self, kernel_size):
        super(Conv2D, self).__init__()
        self.weight = nn.Parameter(torch.randn(kernel_size))
        self.bias = nn.Parameter(torch.randn(1))

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


#物体边缘检测,-1代表黑到白色的转变

X1 = torch.ones(6, 8)
X1[:, 2:6] = 0
K1 = torch.tensor([[1, -1]])
Y1 = corr2d(X1, K1)
print(Y1)
#Y = corr2d(X.t(), K)检测不出来,x转置之后




#不知道k,从x到y中学习
# 4. 学习卷积核
# 当有了更复杂数值的卷积核,或者连续的卷积层时,我们不可能手动设计滤波器。那么我们是否可以学习由X生成Y的卷积核
# 构造一个二维卷积层,它具有1个输出通道和形状为(1,2)的卷积核
conv2d = nn.Conv2d(1,1, kernel_size=(1, 2), bias=False)

# 这个二维卷积层使用四维输入和输出格式(批量大小、通道、高度、宽度),
# 其中批量大小和通道数都为1
X = X1.reshape((1, 1, 6, 8))
Y = Y1.reshape((1, 1, 6, 7))
lr = 3e-2  # 学习率

for i in range(30):
    Y_hat = conv2d(X)
    l = (Y_hat - Y) ** 2
    conv2d.zero_grad()
    l.sum().backward()
    # 迭代卷积核
    conv2d.weight.data[:] -= lr * conv2d.weight.grad
    if (i + 1) % 2 == 0:
        print(f'epoch {i+1}, loss {l.sum():.3f}')

# 我们所学的卷积核的权重张量
print(conv2d.weight.data.reshape((1, 2)))

填充:在输入周围添加额外的行或列
一圈0.
通常填充的是核的长-1

填充和步幅都是卷积层的超参数。
填充在输入周围添加额外的行或列,来控制输出形状的减少量。
步幅是每次滑动核窗口时的行或列,成倍减少形状。

import os

import torch
from torch import nn
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"

# 为了方便起见,我们定义了一个计算卷积层的函数。
# 此函数初始化卷积层权重,并对输入和输出提高和缩减相应的维数
def comp_conv2d(conv2d, X):
    # 这里的(1,1)表示批量大小和通道数都是1
    X = X.reshape((1, 1) + X.shape)
    Y = conv2d(X)
    # 省略前两个维度:批量大小和通道
    return Y.reshape(Y.shape[2:])

# 请注意,这里每边都填充了1行或1列,因此总共添加了2行或2列
conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1)
X = torch.rand(size=(8, 8))
print(comp_conv2d(conv2d, X).shape)

# 如下示例中,我们使用高度为5,宽度为3的卷积核,高度和宽度两边的填充分别为2和1
conv2d = nn.Conv2d(1, 1, kernel_size=(5, 3), padding=(2, 1))
print(comp_conv2d(conv2d, X).shape)

# 我们将高度和宽度的步幅设置为2,从而将输入的高度和宽度减半
# 在输入图像的边界填充元素称为填充(padding) 每次滑动元素的数量称为步幅(stride)
conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1, stride=2)
print(comp_conv2d(conv2d, X).shape)

conv2d = nn.Conv2d(1, 1, kernel_size=(3, 5), padding=(0, 1), stride=(3, 4))
print(comp_conv2d(conv2d, X).shape)

# 在实践中,我们很少使用不一致的步幅或填充

多通道输入输出:
输出通道是卷积层的超参数。
每个输入通道有独立的二维卷积核,所有通道结果相加得到一个输出通道结果。
每个输出通道有独立的三维卷积核。

import os

import torch
from torch import nn
from d2l import torch as d2l
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"

# 1. 多输入通道(互相关)
def corr2d_multi_in(X, K):
    # 先遍历“X”和“K”的第0个维度(通道维度),再把它们加在一起
    return sum(d2l.corr2d(x, k) for x, k in zip(X, K))

X = torch.tensor([[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]],
               [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]])
K = torch.tensor([[[0.0, 1.0], [2.0, 3.0]], [[1.0, 2.0], [3.0, 4.0]]])
print(corr2d_multi_in(X, K))
#56,72,104 120
# 2. 多输出通道
def corr2d_multi_in_out(X, K):
    # 迭代“K”的第0个维度,每次都对输入“X”执行互相关运算。
    # 最后将所有结果都叠加在一起
    #print(torch.stack([corr2d_multi_in(X, k) for k in K], 0))
    return torch.stack([corr2d_multi_in(X, k) for k in K], 0)
K = torch.stack((K, K + 1, K + 2), 0)#核多了k+1和k+2
print(K)
print(K.shape)
print(corr2d_multi_in_out(X, K))

# 3. 1X1卷积层 等价于一个全连接
def corr2d_multi_in_out_1x1(X, K):
    c_i, h, w = X.shape
    print('c_i: ', c_i)  # 输入的通道数
    print('h: ', h)  # 输入的高
    print('w: ', w)  # 输入的宽
    c_o = K.shape[0]  # 卷积核的通道数
    X = X.view(c_i, h * w)  # 3 * 9
    K = K.view(c_o, c_i)  # 2 * 3
    Y = torch.mm(K, X)  # 全连接层的矩阵乘法
    return Y.view(c_o, h, w)

X = torch.normal(0, 1, (3, 3, 3))
K = torch.normal(0, 1, (2, 3, 1, 1))

Y1 = corr2d_multi_in_out_1x1(X, K)
Y2 = corr2d_multi_in_out(X, K)
assert float(torch.abs(Y1 - Y2).sum()) < 1e-6

#print(float(torch.abs(Y1 - Y2).sum()))

# (Y1 - Y2).norm().item() < 1e-6 为真,不会报错
  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值