numpy手搓卷积

发布时间 2023-10-05 18:53:16作者: 海王大人

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多平台发布