神经网络中的卷积运算具有较快的运算速度,现有的神经网络中所需要的卷积运算已经写的比较方便了,即便要写新的网络层,继承下来这些类也差不多满足所需要的要求。但有的时候不尽然,比如我需要运算一个固定卷积核的卷积运算。
下面通过numpy来实现,在贴代码之前,
首先先说一下卷积快速运算的逻辑
快速的实现卷积运算,是将所需要的卷积核和所卷积核所对应的元素拉伸到一维向量,然后所有需要卷积计算的位置都准备这种长度的向量,排列起来成为一个新的矩阵,然后再以矩阵运算的方式进行快速计算。
矩阵运算,是numpy,Tensor(估计其他的神经框架语言的基本实现核接口都相类似)所擅长的,速度快。而且像numpy和pytorch 的Tensor都支持broadcast广播计算(比如,5*3*1维度的向量和1*4维度的向量之间运算时,这些库会按照对应维度补齐的方式来运算,这俩运算相当于5*3*4和5*3*4的两个向量之间的矩阵计算结果)。
所以,给定卷积核的快速运算的关键,是怎么将所需要的卷积核大小的矩阵元素组织成相应的向量。
图示说明
比如一个2*2的卷积核,如图中标识出数字的部分,如果要对这幅图进行卷积运算,就要依次将234位置的像素拍到1后面,形成一个向量。其他形式的卷积核也是如此操作。
代码:
image = Variable(torch.from_numpy(image)).unsqueeze(0).unsqueeze(0).float()#模拟了pytorch中常见的四维矩阵
# creat four layer
kernel = Variable(torch.FloatTensor([1, -1, -1, 1]))#2*2的卷积核
#这以下两个为了模拟固定的错位,从而使相应位置的像素排列过来
mod_1 = sign.clone()
mod = nn.ZeroPad2d((0, 1, 0, 0))
mod_2 = mod(sign)[:, :, :, 1:]
mod = nn.ZeroPad2d((0, 0, 0, 1))
mod_3 = mod(sign)[:, :, 1:, :]
mod = nn.ZeroPad2d((0, 1, 0, 1))
mod_4 = mod(sign)[:, :, 1:, 1:]
mod_ = torch.cat((mod_1, mod_2, mod_3, mod_4), 1)#将这些组合起来,形成上面所说的向量
mod_ = mod_.transpose(1, 2).transpose(2, 3) # 使用的时候需要改变一下维度,卷积核的大小长度放到最后一个维度上
out = torch.matmul(mod_, kernel)
so,类似的方法可用在简单的求梯度上面,
像这样求算图像的梯度
image = Variable(torch.from_numpy(image)).unsqueeze(0).unsqueeze(0).float() #
# 下面两个变量为了形成一定的错位
image_x = nn.ZeroPad2d((1, 0, 0, 0)) # move left by 1 pixel
image_y = nn.ZeroPad2d((0, 0, 1, 0)) # move up by 1 pixel
grad_x = image - image_x(image)[:, :, :, :-1] + 1e-15 #计算梯度
grad_y = image - image_y(image)[:, :, :-1, :] + 1e-15
sign_x = (grad_x / grad_x.abs()) # .char() #下面的这几行代码是为了让特定的模式表现出某种特征,(正负)
sign_y = (grad_y / grad_y.abs()) # .char()
sign = sign_x * sign_y # useful
valid = (image > 0).float() # useful
sign = sign * valid + (1 - valid) # image_xy will has misplacement