1.函数功能
torch.nn.functional.unfold是PyTorch中用于将输入张量展开成局部块(patches)的类,它通常用于卷积操作,可以需要将卷积核滑动窗口展开并计算卷积时使用。这个操作会把图像或特征图的每个局部区域(滑动窗口)展平成一个列向量,并且将所有这些局部区域排列成一个大的矩阵。
2.函数定义
torch.nn.Unfold(input, kernel_size, dilation=1, padding=0, stride=1)
input:输入张量,形状为 (B, C, H, W) 。
kernel_size:卷积核的大小,可以是整数或者是元组,如kernel_size=3 表示使用 3x3 的卷积核,kernel_size=(3, 5) 表示使用一个 3x5 的卷积核。
dilation:扩张因子(默认为 1)。扩张卷积时,增加卷积核元素之间的间距。如dilation=1 表示常规卷积,dilation=2表示卷积核的元素之间间隔了一个像素,dilation=3表示间隔了两个像素,以此类推。
padding:填充的大小(默认为 0)。这是在输入张量的边缘添加的零的数量,用来控制卷积输出的大小。
stride:步幅(默认为 1)。卷积核在输入张量上滑动的步长。
3.函数返回值
nn.Unfold 返回一个二维张量,形状为 [B, C * kernel_height * kernel_width, L],其中:
其中,L 是展开的局部块数量,等于卷积操作后有效区域的数量,d代表的是维度,通常只有两个维度,分别是图像的高度H和宽度W,spatial_size[d]是是输入张量在维度 d上的大小。公式为:
如何理解这个公式呢?假如我们在计算L在高度H维度上的长度时,可以令K=dilation*(kernel_size-1)+1,那
可以写成:
这就和卷积公式的输出形式一样了,就很好理解了,也是同理,所以
.
为什么K=dilation*(kernel_size-1)+1,举两个例子:
假设我们有一个 3x3 的卷积核,如果 dilation = 1,则使用原始卷积,操作如下:
Original 3x3 kernel:
[ 1, 1, 1 ]
[ 1, 1, 1 ]
[ 1, 1, 1 ]
如果 dilation = 2,则卷积核变成以下形式:
Dilated 3x3 kernel with dilation=2:
[ 1, 0, 1, 0, 1 ]
[ 0, 0, 0, 0, 0 ]
[ 1, 0, 1, 0, 1 ]
[ 0, 0, 0, 0, 0 ]
[ 1, 0, 1, 0, 1 ]
在这个扩张卷积中,卷积核的元素之间的间隔为1
如果 dilation = 3,依此类推。
4.应用举例
假设我们有一个批次大小为1、输入通道为1、图像大小为4x4的图像,并且使用一个3x3的卷积核,步幅为1,没有填充。
import torch
import torch.nn as nn
# 创建一个大小为 (1, 1, 4, 4) 的随机输入张量
input = torch.randn(1, 1, 4, 4)
# 使用 nn.Unfold 层
unfold = nn.Unfold(kernel_size=3, stride=1, padding=0)
unfolded = unfold(input)
# 打印展开后的结果
print(unfolded.shape)
#返回值的形状:torch.Size([1, 9, 4])
如果我们使用带填充和步幅的设置,unfold的返回值形状也会发生变化。假设我们使用步幅为2和填充为1:
import torch
import torch.nn as nn
input = torch.randn(1, 1, 6, 6)
# 使用 nn.Unfold 层,填充为 1,步幅为 2
unfold = nn.Unfold(kernel_size=3, stride=2, padding=1)
unfolded = unfold(input)
print(unfolded.shape)
#返回值的形状:torch.Size([1, 9, 9])