深度学习 —— 个人学习笔记9(图像卷积、填充和步幅及多输入多输出通道)

声明

  本文章为个人学习使用,版面观感若有不适请谅解,文中知识仅代表个人观点,若出现错误,欢迎各位批评指正。

十九、图像卷积

import torch
from torch import nn

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")


def corr2d(X, K):
    """计算二维互相关运算"""
    h, w = K.shape  # 获取输入张量维度
    Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1)).to(device)  # 初始化输出张量
    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().to(device)  # 二维互相关计算
    return Y


X = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]]).to(device)
K = torch.tensor([[0.0, 1.0], [2.0, 3.0]]).to(device)
print("corr2d(X, K) : ", corr2d(X, K))


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


Z = torch.ones((6, 8)).to(device)
Z[:, 2:6] = 0
print(f'构建一个 6*8 像素的黑白图像 : {Z}')

K = torch.tensor([[1.0, -1.0]]).to(device)

Y = corr2d(Z, K)
print(f'垂直边缘检测 : {Y}')

# 构造一个二维卷积层,它具有 1 个输出通道和形状为(1,2)的卷积核
conv2d = nn.Conv2d(1, 1, kernel_size=(1, 2), bias=False).to(device)

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

for i in range(15):
    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) % 5 == 0:
        print(f'epoch {i + 1}, loss {l.sum():.3f}')

Y_hat = conv2d.weight.data.reshape((1, 2))

print(f'训练得到的卷积核的权重张量 : {Y_hat}')
print("使用训练张量做边缘检测 : ", torch.round(corr2d(Z, Y_hat)))

二十、填充和步幅

  在应用多层卷积时,常常丢失边缘像素。 由于通常使用小卷积核,因此对于任何单个卷积,可能只会丢失几个像素。 但随着多个连续卷积层的应用,累积丢失的像素数就多了。解决这个问题的简单方法即为填充(padding):在输入图像的边界填充元素(通常填充元素是 0 )。

import torch
from torch import nn

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

##### 填充 #####

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


# padding 指四周每边都填充了 1 行或 1 列,因此总共添加了 2 行或 2 列
conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1).to(device)
X = torch.rand(size=(8, 8)).to(device)
print("padding=1 时维度是否不变 : ", comp_conv2d(conv2d, X).shape == X.shape)

conv2d = nn.Conv2d(1, 1, kernel_size=(5, 3), padding=(2, 1)).to(device)
print("padding=(2, 1) 时维度是否不变 : ", comp_conv2d(conv2d, X).shape == X.shape)

##### 步幅 #####
conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1, stride=2).to(device)
print("步幅为 2 时 : ", comp_conv2d(conv2d, X).shape)

conv2d = nn.Conv2d(1, 1, kernel_size=(3, 5), padding=(0, 1), stride=(3, 4)).to(device)
print("步幅为 (3, 4)) 时 : ", comp_conv2d(conv2d, X).shape)

二十、多输入多输出通道

import torch

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

def corr2d(X, K):
    """计算二维互相关运算"""
    h, w = K.shape  # 获取输入张量维度
    Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1)).to(device)  # 初始化输出张量
    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().to(device)  # 二维互相关计算
    return Y

def corr2d_multi_in(X, K):
    # 先遍历“X”和“K”的第0个维度(通道维度),再把它们加在一起
    return sum(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]]]).to(device)
K = torch.tensor([[[0.0, 1.0], [2.0, 3.0]], [[1.0, 2.0], [3.0, 4.0]]]).to(device)

print("多输入时互相关运算结果 : ", corr2d_multi_in(X, K))

def corr2d_multi_in_out(X, K):
    # 迭代“K”的第0个维度,每次都对输入“X”执行互相关运算。
    # 最后将所有结果都叠加在一起
    return torch.stack([corr2d_multi_in(X, k) for k in K], 0)


K = torch.stack((K, K + 1, K + 2), 0).to(device)
# print(K)              # 如果不理解这里,可以把 K 输出看一下结果
print("卷积核张量 K 的维度 : ", K.shape)
print("多维度输出结果 : ", corr2d_multi_in_out(X, K))

def corr2d_multi_in_out_1x1(X, K):
    c_i, h, w = X.shape
    c_o = K.shape[0]
    X = X.reshape((c_i, h * w))
    K = K.reshape((c_o, c_i))
    # 全连接层中的矩阵乘法
    Y = torch.matmul(K, X)
    return Y.reshape((c_o, h, w))

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

Y1 = corr2d_multi_in_out_1x1(X, K)
Y2 = corr2d_multi_in_out(X, K)

if float(torch.abs(Y1 - Y2).sum()) < 1e-6:
    print("Test PASS!(验证 1 * 1 卷积层可以看为全连接层)")


  文中部分知识参考:B 站 —— 跟李沐学AI;百度百科

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值