numpy实现卷积
1 卷积本质
-
设计这样的一个 滤波器(filter,也称为kernel),用这个filter,往我们的图片上“盖”,覆盖一块跟filter一样大的区域之后,对应元素相乘,然后求和。计算一个区域之后,就向其他区域挪动,接着计算,直到把原图片的每一个角落都覆盖到了为止。这个过程就是 “卷积”。 -
可以通过设计特定的filter,让它去跟图片做卷积,就可以识别出图片中的某些特征。 -
CNN(convolutional neural network),主要就是通过一个个的filter,不断地提取特征,从局部的特征到总体的特征,从而进行图像识别等等功能 -
filter中的参数是通过大量的数据,让机器自己去“学习”这些参数。
2 Padding方式
-
每次卷积,图像都缩小,采用padding的方法。每次卷积前,先给图片周围都补一圈空白,让卷积之后图片跟原来一样大,同时,原来的边缘也被计算了更多次。 -
“让卷积之后的大小不变”的padding方式,称为 “Same”方式, 把不经过任何填白的,称为 “Valid”方式。
2.1 “Valid”方式
2.1 “Same”方式
2.3 Padding计算公式
3 多卷积核多通道
-
在单核单通道的基础上执行多遍,然后把结果累加。 -
(1)一个循环是输入通道数对循环:把卷积核在每个通道数据上卷积,然后结果累加 -
(2)一个循环是核个数对循环:每个卷积核执行步骤(1),然后把结果累加,如下图
4 代码实现(循环计算实现和矩阵计算实现)
4.1 一维卷积
在 NumPy 中实现卷积操作可以通过使用 numpy.convolve
函数或手动编写卷积操作的代码来完成。以下是两种方法的示例:
方法一:使用 numpy.convolve
函数
numpy.convolve
函数用于执行一维卷积操作。首先,您需要定义一个卷积核(滤波器),然后将它应用于输入信号。下面是一个示例:
import numpy as np
# 定义输入信号
signal = np.array([1, 2, 3, 4, 5])
# 定义卷积核(滤波器)
kernel = np.array([0.5, 1, 0.5])
# 使用 numpy.convolve 执行卷积操作
convolution_result = np.convolve(signal, kernel, mode='valid')
print("卷积结果:", convolution_result)
在这个示例中,signal
是输入信号,kernel
是卷积核。mode='valid'
表示执行有效卷积,即仅计算信号和卷积核完全重叠的部分。输出将是卷积的结果。
方法二:手动实现卷积操作
您也可以手动编写卷积操作的代码,通过遍历输入信号并应用卷积核来计算卷积的结果。以下是一个示例:
import numpy as np
# 定义输入信号
signal = np.array([1, 2, 3, 4, 5])
# 定义卷积核(滤波器)
kernel = np.array([0.5, 1, 0.5])
# 初始化卷积结果数组
convolution_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)
这个示例手动执行了卷积操作,遍历输入信号并计算卷积结果。输出将与使用 numpy.convolve
函数得到的结果相同。
这两种方法都可用于实现简单的卷积操作。然而,对于更复杂的卷积神经网络 (CNN) 等应用,通常会使用深度学习框架(如 TensorFlow 或 PyTorch),它们提供了高度优化的卷积操作,能够更高效地处理大型数据集和复杂的模型。
4.2 二维卷积
手动实现二维图片数据的卷积操作涉及到卷积核(滤波器)在输入图像上滑动并执行点积运算的过程。以下是一个使用 NumPy 手动实现的简单示例:
import numpy as np
# 创建一个示例的二维图片数据(4x4 像素)
image = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]], dtype=np.float32)
# 定义一个卷积核(滤波器)
kernel = np.array([[1, 0],
[0, -1]], dtype=np.float32)
# 获取输入图像和卷积核的尺寸
image_height, image_width = image.shape
kernel_height, kernel_width = kernel.shape
# 计算卷积后的输出图像的尺寸
output_height = image_height - kernel_height + 1
output_width = image_width - kernel_width + 1
# 初始化输出图像
output_image = np.zeros((output_height, output_width), dtype=np.float32)
# 执行卷积操作
for i in range(output_height):
for j in range(output_width):
output_image[i, j] = np.sum(image[i:i+kernel_height, j:j+kernel_width] * kernel)
# 打印卷积结果
print(output_image)
在这个示例中,我们首先创建了一个示例的二维图片数据和一个卷积核。然后,我们计算了卷积后的输出图像的尺寸,并初始化一个用于存储输出的数组。接下来,我们使用两层嵌套的循环遍历输入图像,并在每个位置执行卷积操作,将结果存储在输出图像中。最后,我们打印了卷积的结果。
这个示例是一个简单的手动卷积实现,用于说明卷积的基本原理。在实际应用中,通常会使用深度学习框架来执行卷积操作,因为它们提供了高度优化的卷积操作,并且能够处理更大的图像和复杂的卷积核。手动实现卷积通常用于教育目的或深入理解卷积的工作原理。
4.3 添加padding和stride
要用 NumPy 实现类似 PyTorch 的 Conv2D 函数,需要考虑卷积核的大小、步幅、填充以及多通道的情况。以下是一个简化的示例,用 NumPy 实现一个基本的 2D 卷积操作:
import numpy as np
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
# 创建一个示例的二维图片数据(4x4 像素)
image = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]], dtype=np.float32)
# 定义一个卷积核(滤波器)
kernel = np.array([[1, 0],
[0, -1]], dtype=np.float32)
# 执行自定义的卷积操作
result = conv2d_numpy(image, kernel, stride=1, padding=0)
# 打印卷积结果
print(result)
这个示例中,我们首先定义了一个自定义的 conv2d_numpy
函数,用于执行二维卷积操作。然后,我们创建了一个示例的二维图片数据和一个卷积核,并调用自定义函数来执行卷积操作。最后,我们打印了卷积的结果。
请注意,这是一个简化的实现,不包括多通道、批次处理以及更复杂的功能。实际上,深度学习框架(如 PyTorch)提供了高度优化的卷积操作,能够处理多通道、批次和更复杂的模型。手动实现卷积通常用于教育目的或深入理解卷积的工作原理。
4.4 多通道支持
要为 conv2d_numpy
函数添加多通道卷积支持,您需要考虑输入数据和卷积核的通道维度,并执行多通道卷积操作。以下是一个经过修改的示例,支持多通道卷积:
import numpy as np
def conv2d_numpy(input_data, kernel, stride=1, padding=0):
# 获取输入数据的尺寸和通道数
input_height, input_width, input_channels = input_data.shape
# 获取卷积核的尺寸和通道数
kernel_height, kernel_width, kernel_channels = 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), (0, 0)), mode='constant')
# 执行多通道卷积操作
for i in range(0, input_height - kernel_height + 1, stride):
for j in range(0, input_width - kernel_width + 1, stride):
for k in range(kernel_channels):
output_data[i // stride, j // stride] += np.sum(input_data[i:i+kernel_height, j:j+kernel_width, k] * kernel[:, :, k])
return output_data
# 创建一个示例的多通道二维图片数据(4x4 像素,3个通道)
image = np.random.rand(4, 4, 3).astype(np.float32)
# 定义一个多通道的卷积核(滤波器)
kernel = np.random.rand(2, 2, 3).astype(np.float32)
# 执行多通道卷积操作
result = conv2d_numpy(image, kernel, stride=1, padding=0)
# 打印卷积结果
print(result)
这个示例中,我们修改了 conv2d_numpy
函数以支持多通道的输入数据和卷积核。在执行卷积操作时,我们遍历了通道维度并对每个通道进行卷积,然后将结果相加以生成输出。请注意,多通道卷积需要确保输入图像和卷积核的通道数匹配,否则会出现维度错误。
本文由 mdnice 多平台发布