卷积
在深度学习里CNN卷积神经网络
是最常见的概念,可以算AI届的hello world了。https://www.jianshu.com/p/fc9175065d87这个文章中用动图很好的解释了什么叫做卷积。
其实很早的图像处理里,使用一个滤波器扫一遍图像就类似现在深度学习里卷积的计算过程,只是AI中核是需要通过学习得到的。
本文就不从理论上详细介绍卷积了,程序员就要有程序员的亚子,所以我直接上代码介绍怎么用numpy实现卷积
。
numpy实现卷积
基础定义
以CV中对图像卷积为例,图像卷积一般都是
输入:四维数组[B,H,W,C_in]
卷积核:四维数组[C_in,K,K,C_out]
输出:四维数组[B,H2,W2,C_out]
B---batchsize输入对图片张数
H,W---输入图片对高和宽
C_in---输入图片对通道数,比如RGB图像就是三通道,C_in=3
K---卷积核对宽/高,通常宽=高
C_out---有多少个卷积核
H2,W2---输出特征图对高核宽
为什么H和H2不一致,是因为需要根据padding等情况而定。
在卷积时还有stride等概念,本文设置stride=1,因为理解了本文代码后,其他情况完全可以方便实现。
单个核卷积单通道
首先我们从低维入手,图片张数为1,单个卷积核,单个通道输入:
输入---[H,W]
卷积核---[K,K]
输出---[H2,W2]
也就是下图过程:
上图我们发现输入核输出不一致,是因为它使用
VALID
模式的padding,如果我们希望输入和输出一致,就需要使用SAME
模式,如下图对padding
模式对输出大小做下简单介绍:
如果是VALID
模式,我们输出会变小,输出大小为
如果是SMAE
模式,输出和输入需要一样大小,所以需要Padding值,通常为0,假设
数组H=W=N,padding的维度为p,即上下左右都进行添加p
所以
好了~直接上代码,过程有详细注释:
def numpy_conv(inputs,filter,padding="VALID"):
H, W = inputs.shape
filter_size = filter.shape[0]
# default np.floor
filter_center = int(filter_size / 2.0)
filter_center_ceil = int(np.ceil(filter_size / 2.0))
# SAME模式,输入和输出大小一致,所以要在外面填充0
if padding == "SAME":
padding_inputs = np.zeros([H + filter_center_ceil, W + filter_center_ceil], np.float32)
padding_inputs[filter_center:-filter_center, filter_center:-filter_center] = inputs
inputs = padding_inputs
#这里先定义一个和输入一样的大空间,但是周围一圈后面会截掉
result = np.zeros((inputs.shape))
#更新下新输入,SAME模式下,会改变HW
H, W = inputs.shape
#print("new size",H,W)
#卷积核通过输入的每块区域,stride=1,注意输出坐标起始位置
for r in range(filter_center,H -filter_center):
for c in range(filter_center,W -filter_center ):
#获取卷积核大小的输入区域
cur_input = inputs[r -filter_center :r +filter_center_ceil,
c - filter_center:c + filter_center_ceil]
#和核进行乘法计算
cur_output = cur_input * filter
#再把所有值求和
conv_sum = np.sum(cur_output)
#当前点输出值
result[r, c] = conv_sum
# 外面一圈都是0,裁减掉
final_result = result[filter_center:result.shape[0] - filter_center,
filter_center:result.shape[1] -filter_center]
return final_result
现在我们完成了最简单的单核卷积。下面我们进行扩展,用多个核卷积多个通道维度的数据。
多卷积核多通道
和以上逻辑一致,只是多了一个维度而已,
输入---[C_in,H,W]
卷积核---[C_out,K,K]
输出---[C_out,C_in,H2,W2]
下图过程是单个核卷积多通道的过程:
多个核就是上面过程执行多遍,然后把结果累加。了解理论后就知道,就是多两个for循环
的事:
(1)一个循环是输入通道数对循环:把卷积核在每个通道数据上卷积,然后结果累加
(2)一个循环是核个数对循环:每个卷积核执行步骤(1),然后把结果累加
是不是很简单~
上代码:
def _conv(inputs, filter,padding="SAME"):
C_in, H, W = inputs.shape
#C_out指核对个数,也是最后结果对通道个数
C_out = filter.shape[0]
#同样我们任务核对宽高相等
filter_size = filter.shape[1]
filter_center_ceil = int(np.ceil(filter_size / 2.0))
# VALID模式,输入和输出不一致,需要减少;SAME模式,输入和输出一致
if padding == "VALID":
result = np.zeros([C_out, H-filter_center_ceil, W-filter_center_ceil], np.float32)
else:
result = np.zeros([C_out, H, W], np.float32)
#核个数对循环
for channel_out in range(C_out):
#输入通道数对循环
for channel_in in range(C_in):
#当前通道对数据
channel_data=inputs[channel_in]
#采用上面对逻辑,单核单通道卷积,然后累计
result[channel_out,:,:] += numpy_conv(channel_data,filter[channel_out][channel_in],padding)
#print(result)
return result
如果有多张图片呢?每个图片进行以上结果,然后cancat就好了~不就是再加一个循环的事么~~~~
现在!应该!!!!对卷积!!!完全懂了!!!!吧!!!!!
我们上完整代码
# -*- coding: utf-8 -*-
import numpy as np
def numpy_conv(inputs,filter,padding="VALID"):
H, W = inputs.shape
filter_size = filter.shape[0]
# default np.floor
filter_center = int(filter_size / 2.0)
filter_center_ceil = int(np.ceil(filter_size / 2.0))
# SAME模式,输入和输出大小一致,所以要在外面填充0
if padding == "SAME":
padding_inputs = np.zeros([H + filter_center_ceil, W + filter_center_ceil], np.float32)
padding_inputs[filter_center:-filter_center, filter_center:-filter_center] = inputs
inputs = padding_inputs
#这里先定义一个和输入一样的大空间,但是周围一圈后面会截掉
result = np.zeros((inputs.shape))
#更新下新输入,SAME模式下,会改变HW
H, W = inputs.shape
#print("new size",H,W)
#卷积核通过输入的每块区域,stride=1,注意输出坐标起始位置
for r in range(filter_center,H -filter_center):
for c in range(filter_center,W -filter_center ):
#获取卷积核大小的输入区域
cur_input = inputs[r -filter_center :r +filter_center_ceil,
c - filter_center:c + filter_center_ceil]
#和核进行乘法计算
cur_output = cur_input * filter
#再把所有值求和
conv_sum = np.sum(cur_output)
#当前点输出值
result[r, c] = conv_sum
# 外面一圈都是0,裁减掉
final_result = result[filter_center:result.shape[0] - filter_center,
filter_center:result.shape[1] -filter_center]
return final_result
def _conv(inputs, filter,padding="SAME"):
C_in, H, W = inputs.shape
#C_out指核对个数,也是最后结果对通道个数
C_out = filter.shape[0]
#同样我们任务核对宽高相等
filter_size = filter.shape[1]
filter_center_ceil = int(np.ceil(filter_size / 2.0))
# VALID模式,输入和输出不一致,需要减少;SAME模式,输入和输出一致
if padding == "VALID":
result = np.zeros([C_out, H-filter_center_ceil, W-filter_center_ceil], np.float32)
else:
result = np.zeros([C_out, H, W], np.float32)
#核个数对循环
for channel_out in range(C_out):
#输入通道数对循环
for channel_in in range(C_in):
#当前通道对数据
channel_data=inputs[channel_in]
#采用上面对逻辑,单核单通道卷积,然后累计
result[channel_out,:,:] += numpy_conv(channel_data,filter[channel_out][channel_in],padding)
#print(result)
return result
if __name__ == '__main__':
#输入[C_in,H,W]
inputs = np.zeros([3,9,9])
for i in range(3):
for j in range(9):
for z in range(9):
inputs[i][j][z] = i+j+z
print("input:\n",inputs,"\n")
#卷积核[C_out,C_in,K,K]
filter = np.zeros([2, 3, 3, 3])
for i in range(2):
for j in range(3):
for x in range(3):
for y in range(3):
filter[i][j][x][y] = i + j + x + y
print("filter\n",filter,"\n")
final_result = _conv(inputs,filter,"SAME")
print("result\n",final_result,"\n")
我怎么知道我结果是对还是错的呢~下次我们用tensorflow的API来进行验证!
CSDN:https://blog.csdn.net/xiongwei111/article/details/100610364
不知道为什么搬到简书,图就不动了。。。