这个就是普通的卷积计算原理,输入有3个通道,输出有2个通道,没加激活函数等。
如上图所示,输入有3个通道,输出有2个通道,也就是有2个卷积核(一个卷积核就是322)。对于每个卷积核,先在输入3个通道分别作卷积,再将3个通道结果加起来得到卷积输出。所以对于某个卷积层,无论输入图像有多少个通道,输出图像通道数总是等于卷积核数量!
(1)参数量就是:输入通道数卷积核的宽度卷积核的高度*输出通道数
(2)计算量:补0和不补0不一样
(3)总结:
<1>卷积核是3维的,其中一个维度是输入通道,另外两个维度是卷积核的宽度和高度;卷积核中一般不说长度,说高度。
<2>输出图像通道数总是等于卷积核数量。
参考:https://zhuanlan.zhihu.com/p/31426458
(4)卷积核的参数一般4个,
《1》卷积核大小,也就是宽,高;
《2》pad补0与否;
《3》跨度;
《4》输出通数。
(1)卷积核就是一个参与运算的二维矩阵,对应位置相乘再相加。3个参数,大小-跨度-padding边缘填充
(2)卷积运算原理,最基本的就是单通道对应一个输出通道,那就用一个二维矩阵(比如33)对输入图像进行运算,提取图像特征,原理如下图。但是单通道输入得到多通道(k个通道)输出,就需要k个滤波器,每个滤波器是就是一个二维的33矩阵,也就是每个滤波器包含一个卷积核。但是当输入是多通道(t)得到多通道(k个通道)输出,那这次卷积就包括k个滤波器,每个滤波器就是一个3维矩阵,33t,每个滤波器就包含t个卷积核。上面输出通道是k,那就得到了k个特征图,每个输出通道就是一个特征图。具体深入理解请看(多通道图像卷积过程及计算方式https://blog.csdn.net/briblue/article/details/83063170)
图 k 单通道卷积
(2)输入是一个5x5x3的矩阵,有三个通道。filter是一个3x3x3的矩阵。首先,filter中的每个卷积核(记住卷积核是3维的,不是2维的)分别应用于输入层的三个通道。执行三次卷积,产生3个3x3的通道。
然后,这三个通道相加(矩阵加法),得到一个3x3x1的单通道。这个通道就是在输入层(5x5x3矩阵)应用filter(3x3x3矩阵)的结构。
(3)filter和kernel之间的不同很微妙。很多时候,它们可以互换,所以这可能造成我们的混淆。那它们之间的不同在于哪里呢?一个"kernel"更倾向于是2D的权重矩阵。而’filter"则是指多个Kernel堆叠的3D结构。如果是一个2D的filter,那么两者就是一样的。但是一个3Dfilter, 在大多数深度学习的卷积中,它是包含kernel的。每个卷积核都是独一无二的,主要在于强调输入通道的不同方面。
def conv_(img, conv_filter):
"""
卷积核计算操作
:param img: 图片数据
:param conv_filter: 卷积核
:return:
"""
# 1、获取卷积核的大小
filter_size = conv_filter.shape[1]
# 初始化卷积后的结果,给个较大的输出结果
result = np.zeros((img.shape))
# 2、对图片进行循环使用卷积操作(获取当前区域并使用过滤器进行相乘操作.)
# (1)r和c为特征图的下表,从0到特征图输出大小
for r in np.uint16(np.arange(filter_size/2.0, img.shape[0]-filter_size/2.0+1)):
for c in np.uint16(np.arange(filter_size/2.0, img.shape[1]-filter_size/2.0+1)):
# 取出过滤器大小的图片区域,从图片左上角开始
curr_region = img[r-np.uint16(np.floor(filter_size/2.0)):r+np.uint16(np.ceil(filter_size/2.0)),
c-np.uint16(np.floor(filter_size/2.0)):c+np.uint16(np.ceil(filter_size/2.0))]
# 图片当前区域与卷积核进行线性相乘
curr_result = curr_region * conv_filter
# 结果求和并保存,按照下表保存
conv_sum = np.sum(curr_result)
result[r, c] = conv_sum
# 裁剪矩阵
final_result = result[np.uint16(filter_size/2.0):result.shape[0]-np.uint16(filter_size/2.0),
np.uint16(filter_size/2.0):result.shape[1]-np.uint16(filter_size/2.0)]
return final_result
def conv(img, conv_filter):
"""
卷积过程实现
:param img: 图像
:param conv_filter: 卷积过滤器
:return:
"""
# 1、输入的参数大小做异常检测
# 检查输入的图片和卷积核是否一样大小
if len(img.shape) != len(conv_filter.shape) - 1:
print("Error: Number of dimensions in conv filter and image do not match.")
exit()
# 检查输入的图片的通道数和卷积的深度一样
if len(img.shape) > 2 or len(conv_filter.shape) > 3:
if img.shape[-1] != conv_filter.shape[-1]:
print("Error: Number of channels in both image and filter must match.")
sys.exit()
# 检查是否过滤器的长宽一样
if conv_filter.shape[1] != conv_filter.shape[2]:
print('Error: Filter must be a square matrix. I.e. number of rows and columns must match.')
sys.exit()
# 检查过滤器的维度是奇数
if conv_filter.shape[1] % 2 == 0:
print('Error: Filter must have an odd size. I.e. number of rows and columns must be odd.')
sys.exit()
# 2、初始化一个空的特征图来装入计算的结果
feature_maps = np.zeros((img.shape[0]-conv_filter.shape[1]+1,
img.shape[1]-conv_filter.shape[1]+1,
conv_filter.shape[0]))
# 3、图片的卷积完整操作(分别使用每一个过滤器进行过滤操作)
for filter_num in range(conv_filter.shape[0]):
print("Filter ", filter_num + 1)
# 获取当前的filter参数
curr_filter = conv_filter[filter_num, :]
# 当前filter进行卷积核计算操作
if len(curr_filter.shape) > 2:
# 对图片的每个channel进行卷积运算
conv_map = conv_(img[:, :, 0], curr_filter[:, :, 0])
for ch_num in range(1, curr_filter.shape[-1]):
conv_map = conv_map + conv_(img[:, :, ch_num], curr_filter[:, :, ch_num])
else:
# 只有一个filter的情况
conv_map = conv_(img, curr_filter)
feature_maps[:, :, filter_num] = conv_map
return feature_maps
# 使用过程
# 1、定义这层有两个卷积核,每个大小3x3(例子默认对黑白图片进行计算),默认一个步长,不零填充
l1_filter = np.zeros((2,3,3))
# 初始化参数
l1_filter[0, :, :] = np.array([[[-1, 0, 1],
[-1, 0, 1],
[-1, 0, 1]]])
l1_filter[1, :, :] = np.array([[[1, 1, 1],
[0, 0, 0],
[-1, -1, -1]]])
# 卷积计算
l1_feature_map = cnn.conv(img, l1_filter)