学会如何手推卷积!

目录

1. What is convolution?

1.1 kernel and filter Definition

1.2 什么是卷?

2. 代码实现

2.1 一维卷积 (对应torch.nn.Conv1d)

Code Implementation

2.2 二维卷积 (对应torch.nn.Conv2d)

Code Implementation

1. 简单版

 2. 图像验证版(以及考虑padding和stride)

核心Conv2D实现Code

Code:上述过程可以用torch.nn.Conv2d实现

参考文章


1. What is convolution?

卷积的本质是卷!什么是卷?

  • 在给出“卷”的定义前,先介绍一个先验知识—— kernel and filter

1.1 kernel and filter Definition

kernel 是filter 的基本元素, 多张kernel 组成一个filter (形象解释如下图)

注:

  • 一个filter中的kernel个数=input channel number (由输入通道数决定)

当输入通道是3个特征时,后续的每个filter中包含3个kernel;如果输入通道数是64,则后续每个filter中包含64个kernel.

  • filter个数=feature number (输出多少特征,即这一层设置多少个filter)

一个filter负责提取某一种特征,N个filter即提取N个特征.

1.2 什么是卷?

举一个Kernel size=3,Stride=1和Padding=1的2D-卷积栗子:

  • Kernel size(核的大小):1.1讨论了Kernel。核的大小定义了卷积的视图。
  • Stride(步长):它定义了在图像中滑动时,Kernels的步长。Stride=1表示Kernels逐像素滑动通过图像。

Stride=2表示Kernels通过每步移动2个像素(即跳过1个像素)在图像中滑动。我们可以使用Stride >= 2对图像进行下采样。

  • Padding(填充):Padding定义了图像边框的处理方式。Padding方式有两种 "valid" & "same"。
  1. same: 填充卷积(Tensorflow中的same填充)通过在输入边界周围填充0来保持输出的大小等于输入图像的大小。(上图2D-卷积的例子为 same方式!)
  2. valid: 未填充的卷积(Tensorflow中的valid填充)仅对输入图像的像素执行卷积,而在输入边界周围不填充0,输出的大小小于输入的大小。


2. 代码实现

2.1 一维卷积 (对应torch.nn.Conv1d)

  • 一维卷积常用于数字信号处理以及时间序列数据分析(一般输入向量长度>卷积核长度,卷积核长度一般为奇数)
  • 一维的意思是说卷积的方向是一维的。
  • 首先,您需要定义一个卷积核 ,然后将它应用于输入信号。
  • 关于输出向量的长度:(输入向量长度: m; 卷积核长度: n)
  1. 窄卷积:输出向量长度=m-n+1 (下面例子:5-3+1=3)
  2. 等宽卷积:输入向量两边都填充(n-1)/2长度的0 padding,然后最终的输出向量长度:(m+((n-1)/2)*2)-n+1 = m

Code Implementation

# numpy.convolve 函数用于执行一维卷积操作

import numpy as np

# 定义输入信号
signal = np.array([1, 2, 3, 4, 5])

# 定义卷积核(滤波器)
kernel = np.array([-1, 2, 1])


# 法一:numpy.convolve 函数用于执行一维卷积操作
# 使用 numpy.convolve 执行卷积操作
convolution_result = np.convolve(signal, kernel, mode='valid')

print("使用numpy.convolve卷积结果:", convolution_result)

# 徒手写
# output len


# 初始化卷积结果数组
conv_result = np.zeros(len(signal) - len(kernel) + 1)

# 执行卷积操作
for i in range(len(convolution_result)):
    convolution_result[i] = np.sum(signal[i:i + len(kernel)] * kernel)
    
print("徒手写卷积结果:", convolution_result)

2.2 二维卷积 (对应torch.nn.Conv2d)

  • 输入图像上滑动并执行点积运算的过程(又称之为二维互相关运算)。
  • 常见于图像处理。

Padding公式:

Code Implementation

1. 简单版

import torch

def corr2d(x,k):
    k_h,k_w = k.shape
    result=torch.zeros(x.shape[0]+1-k_h, x.shape[1]+1-k_w) # 初始化最后的输出

    for i in range(result.shape[0]):
        for j in range(result.shape[1]):
            result[i,j]=(x[i:i+k_h, j:j+k_w]*k).sum() #点积运算:对应数值相乘,最后相加
    return result

X = torch.tensor([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) # input tensor
K = torch.tensor([[0, 1], [2, 3]]) # kernel

print(corr2d(X, K))

 2. 图像验证版(以及考虑padding和stride)

使用loopy来验证一下hhh!

核心Conv2D实现Code
# 定义Conv2D (以及考虑padding和stride=1)

def conv2d_numpy(input_data, kernel, stride=1, padding=0):
    
    input_height, input_width = input_data.shape
    # 获取卷积核的尺寸
    kernel_height, kernel_width = kernel.shape
    # 计算输出图像的尺寸
    output_height = (input_height - kernel_height + 2 * padding) // stride + 1
    output_width = (input_width - kernel_width + 2 * padding) // stride + 1
    
    # 初始化输出图像
    output_data = np.zeros((output_height, output_width))
    
    # 填充输入数据(根据填充数量添加额外的行和列)
    if padding > 0:
        input_data = np.pad(input_data, ((padding, padding), (padding, padding)), mode='constant')
    
    # 执行卷积操作
    for i in range(0, input_height - kernel_height + 1, stride):
        for j in range(0, input_width - kernel_width + 1, stride):
            output_data[i // stride, j // stride] = np.sum(input_data[i:i+kernel_height, j:j+kernel_width] * kernel)
    
    return output_data



# 执行自定义的卷积操作
result = conv2d_numpy(input_image, k1, stride=1, padding=0)

# 打印卷积结果
print(result)


#卷积结果可视化
plt.imshow(result, cmap='gray')
plt.axis('off')

Code:上述过程可以用torch.nn.Conv2d实现
import torch
import torch.nn as nn
import numpy as np

input_image_tensor = torch.tensor(input_image[np.newaxis, np.newaxis, :, :], dtype=torch.float32)

# 指定卷积核k1
k1 = np.array([
    [1, 0, -1],
    [1, 0, -1],
    [1, 0, -1]
])

# 将k1转换为PyTorch张量,并添加必要的维度
kernel_tensor = torch.tensor(k1[np.newaxis, np.newaxis, :, :], dtype=torch.float32)

conv_layer = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=k1.shape, stride=1, padding=0)

# 使用自定义卷积核
with torch.no_grad():  # 不计算梯度,因为我们只是在设置权重
    conv_layer.weight = nn.Parameter(kernel_tensor)
    conv_layer.bias.data.fill_(0)  # 假设没有偏置,或者设置为0

# 应用卷积层
result_tensor = conv_layer(input_image_tensor)

# 如果需要,将结果转换回numpy数组
result_numpy = result_tensor.detach().numpy()
print(result_numpy)

plt.imshow(result, cmap='gray')
plt.axis('off')

 输出与之前的效果一致!Bingo!!!


参考文章

计算机视觉:卷积步长(Stride)_csdn卷积 步长-CSDN博客 (图片展现了什么是stride=1以及stride=2)

深度学习笔记(3)——kernel(内核)与filter(滤波器)_深度学习filter-CSDN博客

numpy手搓卷积 - 知乎

如何理解卷积神经网络中的卷积? - 知乎

[CNN] 卷积、反卷积、池化、反池化「建议收藏」-腾讯云开发者社区-腾讯云 (本文还讲解了池化的三种方式:最大池化,平均池化,和随机池化)

手写代码实现卷积操作(Python)_手写卷积-CSDN博客 (使用了金鱼姬的图片例子,生动展现了两种kernel对图片的处理的影响(两种kernel分别为:垂直边缘检测和水平边缘检测))

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值