PyTorch学习之torch.nn.functional.pad函数
一、简介
torch.nn.functional.pad
是 PyTorch 中用于对张量进行填充操作的函数。填充操作在处理图像、序列数据等任务时非常常见,它可以在张量的指定维度两端添加一定数量的元素,填充方式多样,包括零填充、常数填充、反射填充和边界填充等。
二、基本语法
torch.nn.functional.pad(input, pad, mode='xxx', value=x)
三、参数说明
input
(Tensor): 输入的张量。pad
(tuple): 指定每个维度填充的数目。格式为 (left, right, top, bottom, …)。mode
(str, 可选): 填充模式,包括constant
(常数填充,默认)、reflect
(反射填充)、replicate
(边界填充)和circular
(循环填充)。value
(float, 可选): 常数填充模式下填充值,仅当mode
为 ‘constant’ 时有效,其他模式时即使指定了值也会被忽略掉。
四、返回值
返回一个新的张量,对输入张量进行了指定方式的填充。
五、填充模式详解
在详细讲解和演示每一种模式的效果前,我想对填充的一些规则进行提前说明:
-
我们会填充【 d i m = l e n ( p a d ) 2 \mathrm{dim=\frac{len(pad)}{2}} dim=2len(pad)】个维度
举个例子:有一个
(4, 4)
形状的矩阵,我给的pad=(1,1)
,那么根据公式 d i m = 2 2 = 1 \mathrm{dim=\frac22=1} dim=22=1因此我们只填充1个维度。有人想,既然只填充1个维度,那为什么给两个值?矩阵是4x4的,有横向有纵向对吧,最后一个维度肯定是横向的(我们常说矩阵存储是行优先的),此时矩阵是不是有左右两边?那么给出的2个填充值分别对应于在左边(left)要填充1个像素,在右边(right)要填充一个像素。
同样的随着维度扩展,pad可以提供更长的参数,如
pad=(1, 1, 2, 2, 3, 3)
,根据公式填充3个维度,哪3个?(left, right, top, bottom, front, back)
-
填充是
从内向外
的:例如pad=(1, 1, 2, 2, 3, 3)
,(1, 1)对应填充(left, right),(2, 2)对应填充(top, bottom),(3, 3)对应填充(front, back)
5.1 constant模式
在该模式下,我们必须指定参数value
,value的值将填充到我们扩充的区域。
例如有如下代码:
import torch
import torch.nn as nn
data = torch.tensor([[
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]
]])
print(data.size())
print(data)
paddata1 = nn.functional.pad(data, (1, 1, 1, 1), mode="constant", value=1)
print(paddata1.size())
print(paddata1)
输出:
torch.Size([1, 4, 4])
tensor([[[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15, 16]]])
torch.Size([1, 6, 6])
tensor([[[ 1, 1, 1, 1, 1, 1],
[ 1, 1, 2, 3, 4, 1],
[ 1, 5, 6, 7, 8, 1],
[ 1, 9, 10, 11, 12, 1],
[ 1, 13, 14, 15, 16, 1],
[ 1, 1, 1, 1, 1, 1]]])
可以看到,我们在(left,right,top,bottom)
四个方向都填充了一个像素,像素值=value
。
5.2 reflect模式
该模式分我们既可以说是反射模式
,也可以说是镜像模式
,当然我个人认为镜像模式更形象:
我们看代码:
import torch
import torch.nn as nn
data = torch.tensor([[
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]
]])
print(data.size())
print(data)
paddata2 = nn.functional.pad(data, (1, 1, 1, 1), mode="reflect", value=0) # value指定值将无效
print(paddata2.size())
print(paddata2)
输出:
torch.Size([1, 4, 4])
tensor([[[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15, 16]]])
torch.Size([1, 6, 6])
tensor([[[ 6, 5, 6, 7, 8, 7],
[ 2, 1, 2, 3, 4, 3],
[ 6, 5, 6, 7, 8, 7],
[10, 9, 10, 11, 12, 11],
[14, 13, 14, 15, 16, 15],
[10, 9, 10, 11, 12, 11]]])
此刻,我们来看,为什么说用镜像
一词更贴切:
- 第1列是以第2列为镜子的第3列的镜像
- 最后1列是以倒数第2列为镜子的倒数第3列的镜像
- 左上角的6是以1为镜子的6的镜像,右上角的7是以4为镜子的7的镜像
- 以此类推,可以看出符合我们的说法
再看一组测试,此时我们将填充语句变更为
paddata2 = nn.functional.pad(data, (2, 1, 1, 1), mode="reflect", value=0)
输出:
torch.Size([1, 4, 4])
tensor([[[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15, 16]]])
torch.Size([1, 6, 7])
tensor([[[ 7, 6, 5, 6, 7, 8, 7],
[ 3, 2, 1, 2, 3, 4, 3],
[ 7, 6, 5, 6, 7, 8, 7],
[11, 10, 9, 10, 11, 12, 11],
[15, 14, 13, 14, 15, 16, 15],
[11, 10, 9, 10, 11, 12, 11]]])
读者体会一下吧,看看此时各方向的镜子是哪些列?被镜像的又是哪一列?相信读者通过这两个实例对该填充模式有比较清晰的认识。
5.3 replicate模式
该模式称为复制模式
或拷贝模式
:其将数据的边缘(1像素)进行原封不动的拷贝。说起来抽象,直接上代码:
import torch
import torch.nn as nn
data = torch.tensor([[
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]
]])
print(data.size())
print(data)
paddata3 = nn.functional.pad(data, (1, 1, 1, 1), mode="replicate")
print(paddata3)
print(paddata3)
输出:
torch.Size([1, 4, 4])
tensor([[[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15, 16]]])
torch.Size([1, 6, 6])
tensor([[[ 1, 1, 2, 3, 4, 4],
[ 1, 1, 2, 3, 4, 4],
[ 5, 5, 6, 7, 8, 8],
[ 9, 9, 10, 11, 12, 12],
[13, 13, 14, 15, 16, 16],
[13, 13, 14, 15, 16, 16]]])
从输出易得,确实是将边缘进行了复制,这样做可以使得我们对张量进行扩充填充后,张量的边缘信息不变:图像处理时,这种机制会经常用到。
如果修改pad参数,会修改拷贝的列数吗?我们对填充语句做如下修改:
paddata3 = nn.functional.pad(data, (2, 1, 4, 1), mode="replicate")
输出:
torch.Size([1, 4, 4])
tensor([[[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15, 16]]])
torch.Size([1, 9, 7])
tensor([[[ 1, 1, 1, 2, 3, 4, 4],
[ 1, 1, 1, 2, 3, 4, 4],
[ 1, 1, 1, 2, 3, 4, 4],
[ 1, 1, 1, 2, 3, 4, 4],
[ 1, 1, 1, 2, 3, 4, 4],
[ 5, 5, 5, 6, 7, 8, 8],
[ 9, 9, 9, 10, 11, 12, 12],
[13, 13, 13, 14, 15, 16, 16],
[13, 13, 13, 14, 15, 16, 16]]])
从输出可以看出,不论你怎么修改pad参数元组,被拷贝的总是一个像素宽的边缘。而你更改的填充宽度如果大于1,至于将1像素边缘复制多次罢了。
5.4 circular模式
该模式称为循环填充模式
,它是怎么干的呢?以2维矩阵上下左右均填充1个像素为例,它的填充值取自其对立面:左侧填充列的值=原矩阵最右侧1列的值,右侧填充列的值=原矩阵最左侧1列的值;上下同理,直接看代码:
import torch
import torch.nn as nn
data = torch.tensor([[
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]
]])
print(data.size())
print(data)
paddata4 = nn.functional.pad(data, (1, 1, 1, 1), mode="circular", value=0)
print(paddata4.size())
print(paddata4)
输出:
torch.Size([1, 4, 4])
tensor([[[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15, 16]]])
torch.Size([1, 6, 6])
tensor([[[16, 13, 14, 15, 16, 13],
[ 4, 1, 2, 3, 4, 1],
[ 8, 5, 6, 7, 8, 5],
[12, 9, 10, 11, 12, 9],
[16, 13, 14, 15, 16, 13],
[ 4, 1, 2, 3, 4, 1]]])
从输出可得,我们的解释是正确的。
哪如果我要填充多个像素又是什么样的效果?我们将填充语句进行修改如下:
paddata4 = nn.functional.pad(data, (2, 3, 1, 1), mode="circular", value=0)
输出:
torch.Size([1, 4, 4])
tensor([[[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15, 16]]])
torch.Size([1, 6, 9])
tensor([[[15, 16, 13, 14, 15, 16, 13, 14, 15],
[ 3, 4, 1, 2, 3, 4, 1, 2, 3],
[ 7, 8, 5, 6, 7, 8, 5, 6, 7],
[11, 12, 9, 10, 11, 12, 9, 10, 11],
[15, 16, 13, 14, 15, 16, 13, 14, 15],
[ 3, 4, 1, 2, 3, 4, 1, 2, 3]]])
读者可以自行体会一下,该模式下,它的多像素填充有什么规律。
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » Pytorch学习之torch.nn.functional.pad()函数
发表评论 取消回复