**参数共享,稀疏连接,参数共享的物理意义使得卷积层具有平移等变性。**在一张图片上进行卷积再向右平移l像素的输出,与先将图片向右平移l像素再进行卷积的输出结果是相等的。
class myConv(object):
def __init__(self,input_data,weight_data,stride,padding='SAME'):
self.input = np.asarray(input_data, np.float32)
self.weights = np.asarray(weights_data, np.float32)
self.stride = stride
self.padding = padding
def compute_conv(self,fm,kernel):
[h,w] = fm.shape
[k,_] = kernel.shape
if self.padding == 'SAME':
pad_h = (self.stride *(h-1) + k - h)//2
pad_w = (self.stride *(w-1) + k - w)//2
rs_h = h
rs_w = w
elif self.padding == 'VALID':
pad_h = 0
pad_w = 0
rs_h = (h-k)/self.stride+1
rs_w = (w-k)/self.stride+1
elif self.padding == 'FULL':
pad_h = k-1
pad_w = k-1
rs_h = (h+k-2)/self.stride+1
rs_w = (w+k-2)/self.stride+1
else:
pad_h = 0
pad_w = 0
rs_h = (h - k) / self.stride + 1
rs_w = (w - k) / self.stride + 1
padding_fm = np.zeros([h+2*pad_h,w+2*pad_w],np.float32)
padding_fm[pad_h:pad_h+h,pad_w:pad_w+w] = fm
rs = np.zeros([rs_h,rs_w],np.float32)
for i in range(rs_h):
for j in range(rs_w):
roi = padding_fm[i*self.stride:(i*self.stride+k),j*self.stride:(j*self.stride+k)]
rs[i][j] = np.sum(roi*kernel)
return rs
def my_conv2d(self):
"""
self.input:c*h*w
self.weights:c*h*w
:return:
"""
[c,h,w] = self.input.shape
[kc,k,_] = self.weights.shape
assert c==kc
outputs = []
for i in range(c):
f_map = self.input[i]
kernel = self.weights[i]
rs = self.compute_conv(f_map,kernel)
if outputs==[]:
outputs = rs
else:
outputs += rs
return outputs
卷积–带strtide,自己实现,注意带stride输出与输入不同,使用公式计算。
out = ( h - kernal + 2 * padding ) // stride + 1
def my_conv2d(img, padding, kernal, stride):
h, w, c = img.shape
k, _ = kernal.shape
padding_image = np.zeros((img.shape[0]+padding*2, img.shape[1]+padding*2, img.shape[2]))
padding_image[padding:padding+img.shape[0], padding:padding+img.shape[1], :] = img
h_o = (h - k + 2 * padding) // stride + 1
w_o = (w - k + 2 * padding) // stride + 1
out = np.zeros((h_o, w_o))
print(padding_image)
for i in range(h_o):
for j in range(w_o):
roi = padding_image[i*stride: i*stride + k, j*stride: j*stride + k, :]
out[i][j] = np.sum(roi*kernal)
return out
img = np.ones((5, 5, 3))
kernal = np.array([[2,2,2],[2,2,2], [2,2,2]])
print(my_conv2d(img, 1, kernal, 2))
# 三种卷积的方式 : valid、same、full--tensorflow有区分valid和same,torch直接给padding。
import numpy as np
# 还是不用类型了,直接写进padding多少,下面用的是valid类型。
多通道,一个卷积核~得到一个特征图
def conv(padding, kernal, img, stride):
kernal = np.array(kernal)
img = np.array(img)
n = kernal.shape[0] # 默认是等大小的kernal
img_padding = np.zeros(img.shape[0] + padding*2, img.shape[1] + padding*2, img.shape[2])
img_padding[padding : img.shape[0]-padding][padding : img.shape[1]-padding][:] = img
img = img_padding
img_conved = np.array(img)
for i in range(0, img_padding.shape[0]-n+1, stride):
for j in range(0, img_padding.shape[1]-n+1, stride):
img_conved[i][j][:] = sum(img_padding[i-padding:i+padding+1][j-padding:j+padding+1] * kernal)
return np.sum(img_conved, axis=2) ## 输出只有一个channel。这里只是展示一个卷积核的输出。
torch.nn.conv2d()默认采用的是Valid。
三种padding:SAME、Valid、Full
第一种是最简单的 VALID, 就是不填充。第二种是SAME,进行足够的零填充保持输入和输出具有相同的大小。最后一种是 FULL.
空洞
作用:全卷积一般会使用池化来扩大感受野,但是会损失信息,所以引入空洞。
相当于扩张了卷积核,用0填充空洞。
反卷积
反卷积的实质就是先对输入的特征图进行填充,再进行卷积。等价的。有较多填充方法而已。