-
如果还不清楚卷积在生活中的意义,可以看看这则转载自疯子朱磊的比喻。
-
首先看定义和公式
卷积就是以一个函数为输入函数,在输入函数每个点上,以输入函数为系数叠加位移了的响应函数,最终得到的函数。
哇,相当抽象, 这是个啥,这又是个啥?好的先不着急弄清楚这符号是什么。先清楚这个符号代表卷积运算就行。那卷积的运算结果是什么?也就是这个东西最终会等于什么?大家学过积分的认真看这个这个东西是什么?是这个东西关于的积分,他的结果将会是关于n的一个函数,什么意思?就是卷积结果不会是一个固定的值,而是关于n的函数,n取不同值它的结果也会不同。比如说卷积结果是2n+1,那当n等于0时,卷积结果就是1,当n等于1000时,卷积结果就是2001。
那么到底如何得到卷积结果呢?也就是这个关于n的函数呢?
先看下转载自百度文库刘真彬老师的PPT:
如何理解这个过程呢?绝知此事要躬行!我们来自己算一个。先来个最简单,最具体的!下面是我的丑字
贴个博主半截木头渡海洋的图片 ,简明的过程!
看完简单的序列卷积,再来看看稍微复杂的函数卷积。
自己尝试算一遍,如果你能算清楚,那你对于卷积一维的运算就问题不大了。
- 卷积的二维运算
二维卷积实现可以分为两大步。一是预处理:将卷积核翻转180°,将原始数据扩边(不同的mode(full、valid、same)模式存在不同的扩边方案);二是滑动卷积计算。
举个简单的例子计算一次。
假设现在有个4*4的待操作矩阵:
卷积核为
第一步预处理:将卷积核翻转180°。
实际上就是上下翻转和左右翻转。
第二步,将卷积核h的中心对准x的第一个元素,然后对应元素相乘后相加,没有元素的地方补0。mode = same
最终得到的结果是:
而不同mode得到的输出结果尺寸问题请参考二维卷积(Full卷积、Same卷积、Valid卷积、带深度的二维卷积)。
- 加深理解编个程(python)
先实现一个简单的一维卷积的运算
把算一维卷积的算法再看一遍
def conv1d(x,h):
result = []
length1= len(x)
lenth2 = len(h)
x_changed = []
for i in range(len(h)-1):#根据一维卷积的性质,卷积结果的长度是lenth1+lenth2-1
x_changed.append(0) #填充0把长度补满,理论上是需要翻转h,翻转后h的计数为负,而序列是从0计数,故这里右移x
for i in range(len(x)):
x_changed.append(x[i]) #再把x的元素填充进来
h_changed = [] #h_changed是翻转后的h
for i in range(1,len(h)+1): #先填充翻转后的h
h_changed.append(h[-i])
for i in range(len(x)-1):
h_changed.append(0)
#print(x_changed,h_changed)
for i in range(len(h_changed)): #开始平移
print('t =',i)
if i==0: #未平移
h_changed2 = h_changed #h_changed2是翻转平移以后的h
else:
h_changed.insert(0,h_changed.pop()) #意识是h_changed中的元素全体向右平移一位,不懂这个函数的可以自己试用一下
h_changed2 = h_changed
print('x_changed :',x_changed)
print('h_changed2 :',h_changed2)
v = 0
for item1,item2 in zip(x_changed,h_changed2):#对应位置相乘然后加和
v = v + item1*item2
result.append(v)
return result
print('conv1d(x,h) :',conv1d([1,2,3],[4,5,6]))
#输出结果
'''
t = 0
x_changed : [0, 0, 1, 2, 3]
h_changed2 : [6, 5, 4, 0, 0]
t = 1
x_changed : [0, 0, 1, 2, 3]
h_changed2 : [0, 6, 5, 4, 0]
t = 2
x_changed : [0, 0, 1, 2, 3]
h_changed2 : [0, 0, 6, 5, 4]
t = 3
x_changed : [0, 0, 1, 2, 3]
h_changed2 : [4, 0, 0, 6, 5]
t = 4
x_changed : [0, 0, 1, 2, 3]
h_changed2 : [5, 4, 0, 0, 6]
conv1d(x,h) : [4, 13, 28, 27, 18]
'''
- 二维卷积编程实现
回顾一下我们举的二维卷积计算例子。再开始敲代码。
卷积核为
第一步预处理:将卷积核翻转180°。
实际上就是上下翻转和左右翻转。
第二步,将卷积核h的中心对准x的第一个元素,然后对应元素相乘后相加,没有元素的地方补0。mode = same
最终得到的结果是:
下面编程实现:
import numpy as np
def zero_pad(x, pad_height, pad_width):# 先在待处理矩阵周围填充0
H, W = x.shape# H为待处理矩阵的高(行),Wi为待处理矩阵的宽(列)
out = None
out = np.zeros((H+2*pad_height, W+2*pad_width))# 知道尺寸后先全填入0
out[pad_height:pad_height+H, pad_width:pad_width+W] = x# 后在中间填入x,这样边缘填充0就完成了
return out
def conv_fast(x, h):
Hi, Wi = x.shape# Hi为待处理矩阵的高(行),Wi为待处理矩阵的宽(列)
Hh, Wh = h.shape# Hh为卷积核的高(行),Wi为卷积的宽(列)
out = np.zeros((Hi, Wi))# 相当于占位
pad_height = Hh // 2 #mode为same情况下,填充0的数量取决于卷积核h的尺寸
pad_width = Wh // 2
image_padding = zero_pad(x, pad_height, pad_width)
h_flip = np.flip(np.flip(h, 0), 1) #np.flip 是翻转函数,参数0为上下翻转也就是行翻转,而参数1为左右翻转也就是列翻转
for i in range(Hi):
for j in range(Wi):
out[i][j] = np.sum(np.multiply(h_flip, image_padding[i:(i+Hh), j:(j+Wh)]))# 加权求和后写入结果到out对应位置
return out
x = np.array([[1,2,3,4],
[5,6,7,8],
[9,10,11,12],
[13,14,15,16]])
h = np.array([[-1,-2,-1],
[0,0,0],
[1,2,1]])
print('conv2d(x,h) :')
print(conv_fast(x,h))
#输出:
conv2d(x,h) :
[[-16. -24. -28. -23.]
[-24. -32. -32. -24.]
[-24. -32. -32. -24.]
[ 28. 40. 44. 35.]]
以上便是卷积的含义以及一维卷积和二维卷积的计算过程。
如果对您有所帮助请点个赞吧。
后续我还会介绍,不同框架下对于卷积计算方式的优化,以及一些图片卷积的可视化的结果。
敬请期待!