机器视觉零基础入门:(五)卷积与卷积核
主要介绍了卷积和卷积核的概念
文章目录
前言
卷积一般我们又称为滤波。在传统学习和卷积神经网络的学习中,卷积和卷积核是绕不开的核心概念,它们就像图像特征提取的"挖掘机",决定了模型能从数据中挖掘出哪些关键信息。
一、卷积和卷积核
在聊复杂的神经网络之前,咱们先明确两个核心定义卷积和卷积核的定义。
1.1 卷积:特征提取的"计算法则"
卷积(Convolution)本质是一种数学运算,它的作用是让输入数据与一个固定的权重矩阵进行滑动计算,从而提取局部特征。你可以把它想象成用一个"探测器"在图像上慢慢滑动,每到一个位置就做一次计算,记录下这个位置的特征信息。
比如处理一张手写数字图片时,卷积运算能帮我们找到图片中的边缘、线条等基础特征,再通过多层叠加逐步提取更复杂的形状特征。
1.2 卷积核:特征提取的"探测器"
刚才说的"探测器",就是卷积核(Kernel/Filter),它是一个小型的可学习权重矩阵。CNN的核心优势就在于这些卷积核可以通过训练不断调整权重,从而学会识别不同的特征。
举个直观的例子:如果我们想检测图像中的水平边缘,就可以设计一个特定权重的3×3卷积核;训练完成后,这个卷积核在图像的水平边缘区域计算时会输出较大的值,相当于"发现了目标特征"。比如下图的Sobel边缘检测的水平梯度卷积核:
1.3 关键配角:输入/输出特征图
卷积运算需要"原材料"和"成品",这就对应着输入特征图和输出特征图:
-
输入特征图(Input Feature Map):卷积运算的处理对象,可以是原始图像(比如RGB图像是3通道特征图),也可以是上一层卷积的输出结果。通常用维度表示为 (H_in, W_in, C_in),其中H是高度、W是宽度、C是通道数。
-
输出特征图(Output Feature Map):卷积运算的结果,每个输出通道都对应一个卷积核提取的一类特征。维度表示为 (H_out, W_out, C_out),其中C_out(输出通道数)严格等于卷积核的数量。
二、核心关系:卷积核如何影响输出特征图
很多新手困惑的是:输入特征图、卷积核和输出特征图之间的关系。其实关键就在于三个维度的关系——尺寸、通道、数值。
2.1 尺寸关系
输出特征图的宽和高,不是随便定的,而是由输入尺寸、卷积核大小、步幅(Stride)和填充(Padding)这四个参数共同决定的。先明确两个关键参数:
-
步幅(S):卷积核在输入特征图上每次滑动的像素数,默认是1。步幅越大,输出特征图越小。
-
填充(P):在输入特征图边缘补充的像素层数(通常补0),目的是避免边缘特征丢失或控制输出尺寸,默认是0(不填充)。

对于方形输入和方形卷积核,输出尺寸的计算公式非常固定,如下:
H_out = (H_in - K + 2P) / S + 1
W_out = (W_in - K + 2P) / S + 1
注意:公式计算结果必须是整数,要是出现小数,框架会通过舍弃小数或调整填充来处理。咱们用一个实际例子验证一下:
| 输入尺寸(H_in,W_in) | 卷积核大小(K) | 步幅(S) | 填充(P) | 输出尺寸(H_out,W_out) | 输出通道数(C_out) |
|---|---|---|---|---|---|
| (28,28) | 3×3 | 1 | 0 | (28-3+0)/1+1=26 | 16(16个卷积核) |
| (28,28) | 3×3 | 1 | 1(SAME填充) | (28-3+2)/1+1=28 | 32(32个卷积核) |
| (28,28) | 5×5 | 2 | 0 | (28-5+0)/2+1=12 | 8(8个卷积核) |
2.2 通道关系
这是一个核心规律:输出特征图的通道数,完全等于卷积核的数量。因为每个卷积核都专门负责提取一种特征,一个卷积核对应输出一个通道的特征图。
举个例子:用3个不同的3×3卷积核处理一张RGB图像(输入通道数C_in=3),每个卷积核都会和输入的3个通道分别计算,然后把结果累加得到一个单通道特征图。3个卷积核就会输出3个通道的特征图,最终输出特征图维度就是 (H_out, W_out, 3)。
单个卷积核的通道数必须和输入特征图的通道数一致,比如输入是3通道,卷积核也得是3通道(维度为K×K×3),这样才能逐通道匹配计算。
2.3 数值关系
输出特征图的每个像素值,都是卷积核与输入局部区域"内积运算"的结果,这是卷积的底层逻辑。咱们用单通道输入举个具体例子,步骤拆解得明明白白:
假设输入特征图是5×5单通道,卷积核是3×3单通道,步幅S=1,填充P=0:
-
滑动窗口:卷积核在输入特征图上以步幅1滑动,每次覆盖一个3×3的局部区域(这个区域叫"感受野")。
-
逐元素相乘:把卷积核的每个权重和局部区域的对应像素相乘,比如卷积核(0,0)位置的权重乘以输入(0,0)位置的像素。
-
求和加偏置:把所有相乘的结果加起来,再加上一个可学习的偏置项,就得到输出特征图的一个像素值。
-
重复滑动:直到卷积核扫完整个输入特征图,生成完整的输出特征图。
计算过程如图所示
我们再举个简单的例子验证一下上面的结论:
输入局部区域(3×3): 卷积核(3×3): 计算过程:
1 2 3 1 0 -1 (1×1)+(2×0)+(3×-1)
4 5 6 0 1 0 → +(4×0)+(5×1)+(6×0)
7 8 9 -1 0 1 +(7×-1)+(8×0)+(9×1) + 偏置
= (1-3) + (5) + (-7+9) + b = 5 + b
这个输出值5+b就代表了"输入这个局部区域是否匹配该卷积核的特征"。比如上面这个卷积核是边缘检测核,要是输入区域有边缘,输出值就会更大。
三、实例演示:用MNIST理解卷积过程
我们用经典的MNIST手写数字数据集举个完整例子,看看卷积和卷积核是怎么工作的:
-
输入:MNIST手写数字图像,维度(28,28,1)(28×28像素,单通道灰度图)。
-
卷积核设置:用16个3×3卷积核(维度3×3×1),步幅1,填充1(SAME填充)。
-
输出计算:根据尺寸公式,输出尺寸=(28-3+2×1)/1+1=28,通道数=16,所以输出特征图维度是(28,28,16)。
-
特征提取效果:这16个卷积核会分别提取不同的基础特征,比如有的提取水平边缘,有的提取垂直边缘,有的提取拐角,这些基础特征会传递到下一层继续加工。
代码如下:
# 导入所需库
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras.datasets import mnist
from tensorflow.keras.layers import Conv2D
if tf.__version__.startswith('1.'):
tf.enable_eager_execution()
print(f"使用TensorFlow版本:{tf.__version__}")
# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# -------------------------- 1. 加载并预处理MNIST数据 --------------------------
# 加载MNIST数据集(训练集和测试集)
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# 选择一张测试图像作为演示
sample_img = x_test[0] # 形状:(28, 28) 单通道灰度图
print(f"原始MNIST图像形状:{sample_img.shape}")
# 预处理:1. 归一化(像素值0-255转为0-1) 2. 增加通道维度(转为(28,28,1)) 3. 增加批次维度(模型输入要求(batch, h, w, c))
sample_img = sample_img / 255.0 # 归一化
sample_img = np.expand_dims(sample_img, axis=-1) # 加通道维度:(28,28) → (28,28,1)
sample_input = np.expand_dims(sample_img, axis=0) # 加批次维度:(28,28,1) → (1,28,28,1)
print(f"卷积层输入形状:{sample_input.shape}")
# -------------------------- 2. 搭建卷积层 --------------------------
# 构建单个卷积层,参数完全匹配需求:16个3×3卷积核、步幅1、SAME填充
conv_layer = Conv2D(
filters=16, # 16个卷积核(输出通道数为16)
kernel_size=(3, 3), # 3×3卷积核
strides=(1, 1), # 步幅1
padding='same', # SAME填充(保证输出尺寸与输入一致)
kernel_initializer='he_normal', # 权重初始化
bias_initializer='zeros',
activation='relu', # 激活函数(可注释,直接看原始卷积输出)
input_shape=(28, 28, 1) # 输入形状(单通道28×28)
)
# 初始化卷积层(通过一次前向传播完成权重初始化)
conv_output = conv_layer(sample_input)
print(f"卷积层输出特征图形状:{conv_output.shape}") # 输出应为(1,28,28,16)
# -------------------------- 3. 可视化结果:原始图像 + 卷积特征图 --------------------------
# 提取卷积后的特征图(去除批次维度)
feature_maps = conv_output.numpy()[0] # 形状:(28,28,16)
print(f"提取的特征图形状:{feature_maps.shape}")
# 绘制原始图像
plt.figure(figsize=(16, 8))
plt.subplot(4, 5, 1) # 4行5列,第一个位置放原始图像
plt.imshow(sample_img[:, :, 0], cmap='gray')
plt.title('原始MNIST手写数字', fontsize=12)
plt.axis('off')
# 绘制16个卷积核提取的特征图
for i in range(16):
plt.subplot(4, 5, i + 2) # 从第二个位置开始放特征图
plt.imshow(feature_maps[:, :, i], cmap='gray')
plt.title(f'卷积核{i+1}提取的特征', fontsize=10)
plt.axis('off')
# 调整布局并保存图片
plt.tight_layout()
plt.savefig('mnist_convolution_demo.png', dpi=300, bbox_inches='tight')
plt.show()
# 打印卷积层关键信息
print("\n卷积层核心参数验证:")
print(f"卷积核数量(输出通道数):{conv_layer.filters}")
print(f"卷积核尺寸:{conv_layer.kernel_size}")
print(f"步幅:{conv_layer.strides}")
print(f"填充方式:{conv_layer.padding}")
print(f"卷积核权重形状:{conv_layer.kernel.shape}") # (3,3,1,16) → (核高,核宽,输入通道数,输出通道数)
四、总结
| 关系类型 | 核心结论 |
|---|---|
| 尺寸关系 | 输出尺寸=(输入尺寸-核大小+2×填充)/步幅 +1 |
| 通道关系 | 输出通道数=卷积核数量,单核通道数=输入通道数 |
| 数值关系 | 输出像素=卷积核与局部区域内积 + 偏置 |
卷积和卷积核的核心逻辑其实很简单:用可学习的卷积核通过滑动计算提取特征,不同核提取不同特征,多层叠加就能从简单到复杂地挖掘数据信息。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)