本文博主将用Python代码简单演示卷积操作的过程,让你直观理解卷积是如何工作的。
1. 手动实现简单的2D卷积
importnumpyasnpdefmanual_conv2d(image,kernel):""" 手动实现简单的2D卷积(无填充,步长为1) 参数: image: 输入图像(2D numpy数组) kernel: 卷积核/滤波器(2D numpy数组) 返回: 卷积结果 """# 获取图像和卷积核的尺寸img_height,img_width=image.shape kernel_height,kernel_width=kernel.shape# 计算输出特征图的尺寸output_height=img_height-kernel_height+1output_width=img_width-kernel_width+1# 初始化输出特征图output=np.zeros((output_height,output_width))# 滑动窗口进行卷积操作foriinrange(output_height):forjinrange(output_width):# 提取当前窗口window=image[i:i+kernel_height,j:j+kernel_width]# 计算点积并求和output[i,j]=np.sum(window*kernel)returnoutput# 创建一个简单的6x6图像(数字边缘检测示例)image=np.array([[0,0,0,0,0,0],[0,1,1,1,1,0],[0,1,2,2,1,0],[0,1,2,2,1,0],[0,1,1,1,1,0],[0,0,0,0,0,0]])print("原始图像:")print(image)print()# 定义一个垂直边缘检测卷积核vertical_kernel=np.array([[1,0,-1],[1,0,-1],[1,0,-1]])# 定义一个水平边缘检测卷积核horizontal_kernel=np.array([[1,1,1],[0,0,0],[-1,-1,-1]])# 定义一个锐化卷积核sharpen_kernel=np.array([[0,-1,0],[-1,5,-1],[0,-1,0]])# 执行卷积vertical_edges=manual_conv2d(image,vertical_kernel)horizontal_edges=manual_conv2d(image,horizontal_kernel)sharpened=manual_conv2d(image,sharpen_kernel)print("垂直边缘检测卷积核:")print(vertical_kernel)print("垂直边缘检测结果:")print(vertical_edges)print()print("水平边缘检测卷积核:")print(horizontal_kernel)print("水平边缘检测结果:")print(horizontal_edges)print()print("锐化卷积核:")print(sharpen_kernel)print("锐化结果:")print(sharpened)2. 使用NumPy的高级函数实现卷积
importnumpyasnpimportmatplotlib.pyplotaspltdefvisualize_convolution():"""可视化卷积操作过程"""# 创建一个8x8的图像(中间有个矩形)image=np.zeros((8,8))image[2:6,2:6]=1# 创建一个4x4的白色矩形# 定义一个边缘检测卷积核edge_kernel=np.array([[-1,-1,-1],[-1,8,-1],[-1,-1,-1]])# 使用NumPy的convolve2d(需要scipy)try:fromscipyimportsignal# 执行卷积convolved=signal.convolve2d(image,edge_kernel,mode='valid')print("原始图像 (8x8):")print(image)print("\n边缘检测卷积核 (3x3):")print(edge_kernel)print("\n卷积结果 (6x6):")print(convolved)# 可视化fig,axes=plt.subplots(1,3,figsize=(12,4))# 原始图像axes[0].imshow(image,cmap='gray',interpolation='nearest')axes[0].set_title('原始图像')axes[0].axis('off')# 卷积核axes[1].imshow(edge_kernel,cmap='gray',interpolation='nearest')axes[1].set_title('卷积核')axes[1].axis('off')# 卷积结果axes[2].imshow(convolved,cmap='gray',interpolation='nearest')axes[2].set_title('卷积结果')axes[2].axis('off')plt.tight_layout()plt.show()exceptImportError:print("需要安装scipy: pip install scipy")# 如果scipy不可用,使用手动实现print("\n使用手动实现:")result=manual_conv2d(image,edge_kernel)print("卷积结果:")print(result)# 运行可视化visualize_convolution()3. 实现带步长和填充的卷积
defconv2d_with_params(image,kernel,stride=1,padding=0):""" 支持步长和填充的卷积实现 参数: image: 输入图像 kernel: 卷积核 stride: 步长 padding: 填充大小 返回: 卷积结果 """# 如果padding>0,给图像添加填充ifpadding>0:image=np.pad(image,((padding,padding),(padding,padding)),mode='constant',constant_values=0)img_height,img_width=image.shape kernel_height,kernel_width=kernel.shape# 计算输出尺寸output_height=(img_height-kernel_height)//stride+1output_width=(img_width-kernel_width)//stride+1output=np.zeros((output_height,output_width))# 滑动窗口(考虑步长)foriinrange(0,output_height):forjinrange(0,output_width):# 计算窗口起始位置start_i=i*stride start_j=j*stride end_i=start_i+kernel_height end_j=start_j+kernel_width# 提取窗口window=image[start_i:end_i,start_j:end_j]# 卷积操作output[i,j]=np.sum(window*kernel)returnoutput# 测试带参数的卷积print("=== 测试不同参数的卷积 ===")# 创建一个小图像test_image=np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]])test_kernel=np.array([[1,0],[0,-1]])print("原始图像:")print(test_image)print("\n卷积核:")print(test_kernel)# 测试不同步长result_stride1=conv2d_with_params(test_image,test_kernel,stride=1)print(f"\n步长=1的结果 (3x3):")print(result_stride1)result_stride2=conv2d_with_params(test_image,test_kernel,stride=2)print(f"\n步长=2的结果 (2x2):")print(result_stride2)# 测试带填充result_padding1=conv2d_with_params(test_image,test_kernel,stride=1,padding=1)print(f"\n步长=1, 填充=1的结果 (5x5):")print(result_padding1)4. 使用PyTorch实现卷积(更接近真实深度学习场景)
importtorchimporttorch.nnasnnimporttorch.nn.functionalasFdefpytorch_convolution():"""使用PyTorch演示卷积"""print("=== PyTorch卷积演示 ===")# 创建一个批次的图像 (batch_size=2, channels=1, height=5, width=5)# 在PyTorch中,图像格式为:[批大小, 通道数, 高度, 宽度]batch_images=torch.tensor([# 第一张图像[[[1,1,1,0,0],[0,1,1,1,0],[0,0,1,1,1],[0,0,1,1,0],[0,1,1,0,0]]],# 第二张图像[[[0,0,0,0,0],[0,1,1,0,0],[0,1,1,0,0],[0,0,0,0,0],[0,0,0,0,0]]]],dtype=torch.float32)print(f"输入图像形状:{batch_images.shape}")print(f"第一张图像:\n{batch_images[0,0]}")# 创建卷积层# 参数: 输入通道数, 输出通道数, 卷积核大小conv_layer=nn.Conv2d(in_channels=1,out_channels=2,kernel_size=3,stride=1,padding=1,bias=False)# 手动设置卷积核权重(为了演示清晰)# 第一个卷积核:边缘检测conv_layer.weight.data[0,0]=torch.tensor([[-1,-1,-1],[-1,8,-1],[-1,-1,-1]],dtype=torch.float32)/9.0# 第二个卷积核:模糊/平滑conv_layer.weight.data[1,0]=torch.tensor([[1,1,1],[1,1,1],[1,1,1]],dtype=torch.float32)/9.0print(f"\n卷积层权重形状:{conv_layer.weight.shape}")print(f"第一个卷积核:\n{conv_layer.weight[0,0]}")print(f"第二个卷积核:\n{conv_layer.weight[1,0]}")# 执行卷积output=conv_layer(batch_images)print(f"\n卷积输出形状:{output.shape}")print(f"第一张图像的输出 - 特征图1 (边缘检测):\n{output[0,0].detach().numpy().round(3)}")print(f"第一张图像的输出 - 特征图2 (平滑):\n{output[0,1].detach().numpy().round(3)}")# 也可以使用F.conv2d函数式APIoutput_func=F.conv2d(batch_images,conv_layer.weight,stride=1,padding=1)print(f"\n使用F.conv2d的结果与nn.Conv2d是否一致:{torch.allclose(output,output_func)}")returnoutput# 运行PyTorch示例try:output=pytorch_convolution()exceptExceptionase:print(f"需要安装PyTorch: pip install torch")print(f"错误:{e}")5. 可视化卷积滑动过程
defvisualize_sliding_window():"""可视化卷积滑动窗口的过程"""# 创建一个小图像img=np.array([[1,2,3],[4,5,6],[7,8,9]])kernel=np.array([[1,0],[0,-1]])print("图像:")print(img)print("\n卷积核:")print(kernel)print("\n--- 卷积过程 ---")output_height=img.shape[0]-kernel.shape[0]+1output_width=img.shape[1]-kernel.shape[1]+1result=np.zeros((output_height,output_width))foriinrange(output_height):forjinrange(output_width):window=img[i:i+2,j:j+2]conv_value=np.sum(window*kernel)result[i,j]=conv_valueprint(f"\n位置 ({i},{j}):")print(f"窗口:\n{window}")print(f"窗口 × 卷积核:")print(f"{window}×{kernel}={window*kernel}")print(f"求和:{conv_value}")print(f"\n最终卷积结果:")print(result)returnresult# 运行滑动窗口可视化print("=== 详细滑动过程演示 ===")visualize_sliding_window()运行说明
核心操作:卷积的本质就是在输入图像上滑动一个窗口(卷积核),计算窗口与卷积核的对应元素乘积之和。
关键公式:对于位置
(i, j)的输出是:output[i, j] = Σ_m Σ_n image[i+m, j+n] * kernel[m, n]其中 m, n 是卷积核的索引。
参数影响:
- 步长:控制滑动窗口的移动距离
- 填充:在图像边缘添加像素,控制输出尺寸
- 卷积核:不同的卷积核提取不同的特征(边缘、纹理等)
深度学习中的卷积:
- 实际使用的是3D卷积(处理多通道图像)
- 通常有多个卷积核,每个生成一个特征图
- 包含可学习的参数(权重)
这个演示展示了卷积从最基本的手动实现到深度学习框架中使用的完整过程。