这是我最近遇到的一个问题,对图片每个像素点进行遍历,并且对每个像素点所在的33区域进行数据计算,最开始我是用for来写的,因为for循环是一个框一个框的遍历计算,速度很慢,电脑计算量也没有上去。为了解决这个问题,我百度了很多次,摸索出了以下方法,用nn.Unfold 批量切片,再用矩阵计算所对应的33区域,最后用F.conv2d 指定卷积核二维卷积进行数据整合。大致思路就是这样。
参数说明:
kernel_size:滑块大小
dilation:控制滑动过程中所跨越元素的个数
padding:填充
stride:步长
unfold = nn.Unfold(kernel_size=(3, 3), dilation=1, padding=0, stride=(1, 1))
inp_unf = unfold(input)
平时我们经常会用到for循环进行切片操作,选取数组一定区域的数据。比如需要对一张图片遍历每个像素点所在的3*3区域,用for循环的来依次切片速度会很慢。通过nn.Unfold函数可以把所需要遍历的区域的数值提取出来放到一个新的数组里。举例如下:
import torch
import torch.nn as nn
# inp大小 [1, 1, 5, 6]
inp = torch.tensor([[[[1.0, 2, 3, 4, 5, 6],
[7, 8, 9, 10, 11, 12],
[13, 14, 15, 16, 17, 18],
[19, 20, 21, 22, 23, 24],
[25, 26, 27, 28, 29, 30],
]]])
# 实例化 3*3滑块依次遍历inp,一共有12个3*3的框
unfold = nn.Unfold(kernel_size=(3, 3), dilation=1, padding=0, stride=(1, 1))
inp_unf = unfold(inp)
print('inp=',inp.shape,inp)
print('inp_unf=',inp_unf.shape,inp_unf)
inp= torch.Size([1, 1, 5, 6])
tensor([[[[ 1., 2., 3., 4., 5., 6.],
[ 7., 8., 9., 10., 11., 12.],
[13., 14., 15., 16., 17., 18.],
[19., 20., 21., 22., 23., 24.],
[25., 26., 27., 28., 29., 30.]]]])
inp_unf= torch.Size([1, 9, 12])
每一竖排代表一个3*3区域的框, 一共12个
tensor([[[ 1., 2., 3., 4., 7., 8., 9., 10., 13., 14., 15., 16.],
[ 2., 3., 4., 5., 8., 9., 10., 11., 14., 15., 16., 17.],
[ 3., 4., 5., 6., 9., 10., 11., 12., 15., 16., 17., 18.],
[ 7., 8., 9., 10., 13., 14., 15., 16., 19., 20., 21., 22.],
[ 8., 9., 10., 11., 14., 15., 16., 17., 20., 21., 22., 23.],
[ 9., 10., 11., 12., 15., 16., 17., 18., 21., 22., 23., 24.],
[13., 14., 15., 16., 19., 20., 21., 22., 25., 26., 27., 28.],
[14., 15., 16., 17., 20., 21., 22., 23., 26., 27., 28., 29.],
[15., 16., 17., 18., 21., 22., 23., 24., 27., 28., 29., 30.]]])
举例2
import torch
inputs = torch.randn(1, 2, 8, 8)
unfold = torch.nn.Unfold(kernel_size=(2, 2), stride=1)
patches = unfold(inputs)
print(inputs.size())
print(patches.size())
nn.Unfold 函数可以把数据批量切片,对每一个小块进行数据处理,我们可以通过矩阵计算的形式批量计算。也有可能会用到下面指定卷积核的卷积。
import torch
from torch.nn import functional as F
"""手动定义卷积核(weight)和偏置"""
w = torch.rand(16, 3, 5, 5) # 16种3通道的5乘5卷积核
b = torch.rand(16) # 和卷积核种类数保持一致(不同通道共用一个bias)
"""定义输入样本"""
x = torch.randn(1, 3, 28, 28) # 1张3通道的28乘28的图像
"""2D卷积得到输出"""
out = F.conv2d(x, w, b, stride=1, padding=1) # 步长为1,外加1圈padding
print(out.shape)
out = F.conv2d(x, w, b, stride=2, padding=2) # 步长为2,外加2圈padding
print(out.shape)
torch.Size([1, 16, 26, 26])
torch.Size([1, 16, 14, 14])
import torch
from torch import nn
"""要输入的原始图像"""
x = torch.rand(1, 1, 28, 28)
"""2维的卷积层,用于图片的卷积"""
# 输入图像的通道数=1(灰度图像),卷积核的种类数=3
# 卷积核的shape是3乘3的,扫描步长为1,padding为0
layer = nn.Conv2d(1, 3, kernel_size=3, stride=1, padding=0)
out = layer(x)
print(out.shape) # torch.Size([1, 3, 26, 26])
# 这次使用padding为1.所以原始图像上下左右都加了一层0
layer = nn.Conv2d(1, 3, kernel_size=3, stride=1, padding=1)
print(layer.forward(x).shape) # torch.Size([1, 3, 28, 28])
# stride设置为2,也就是每次移动2格子(向上或者向右)
layer = nn.Conv2d(1, 3, kernel_size=3, stride=2, padding=1)
# 相当于每次跳1个像素地扫描,输出的Feature Map直接小了一半
print(layer.forward(x).shape) # torch.Size([1, 3, 14, 14])
# 查看一下卷积层的权重(即卷积核)信息和偏置信息:
print(layer.weight)
print(layer.bias)