python-torch(二)

目录

1、torch.meshgrid

2、pytorch.range() 和 pytorch.arange() 的区别

3、Pytorch中contiguous()函数理解

使用contiguous() 

4、pytorch学习 中 torch.squeeze() 和torch.unsqueeze()的用法

5、grid_sample()函数及双线性采样


1、torch.meshgrid

x1 ,y1 = torch.meshgrid(x,y)
参数是两个,第一个参数我们假设是x,第二个参数假设就是y
输出的是两个tensor,size就是x.size * y.size(行数是x的个数,列数是y的个数)
具体输出看下面
注意:两个参数的数据类型要相同,要么都是float,要么都是int,否则会报错。

# 【1】
import torch
a = torch.tensor([1, 2, 3, 4])
print(a)
b = torch.tensor([4, 5, 6])
print(b)
x, y = torch.meshgrid(a, b)
print(x)
print(y)
 
结果显示:
tensor([1, 2, 3, 4])
tensor([4, 5, 6])
tensor([[1, 1, 1],
        [2, 2, 2],
        [3, 3, 3],
        [4, 4, 4]])
tensor([[4, 5, 6],
        [4, 5, 6],
        [4, 5, 6],
        [4, 5, 6]])
 
 
 
# 【2】
import torch
a = torch.tensor([1, 2, 3, 4, 5, 6])
print(a)
b = torch.tensor([7, 8, 9, 10])
print(b)
x, y = torch.meshgrid(a, b)
print(x)
print(y)
 
结果显示:
tensor([1, 2, 3, 4, 5, 6])
tensor([ 7,  8,  9, 10])
tensor([[1, 1, 1, 1],
        [2, 2, 2, 2],
        [3, 3, 3, 3],
        [4, 4, 4, 4],
        [5, 5, 5, 5],
        [6, 6, 6, 6]])
tensor([[ 7,  8,  9, 10],
        [ 7,  8,  9, 10],
        [ 7,  8,  9, 10],
        [ 7,  8,  9, 10],
        [ 7,  8,  9, 10],
        [ 7,  8,  9, 10]])

2、pytorch.range() 和 pytorch.arange() 的区别

https://blog.csdn.net/m0_37586991/article/details/88830026

https://pytorch.org/docs/stable/torch.html#torch.range

>>> y=torch.range(1,6)
>>> y
tensor([1., 2., 3., 4., 5., 6.])
>>> y.dtype
torch.float32

>>> z=torch.arange(1,6)
>>> z
tensor([1, 2, 3, 4, 5])
>>> z.dtype
torch.int64

总结:

  1. torch.range(start=1, end=6) 的结果是会包含end的,
    torch.arange(start=1, end=6)的结果并不包含end
  2. 两者创建的tensor的类型也不一样。

3、Pytorch中contiguous()函数理解

在使用transpose()进行转置操作时,pytorch并不会创建新的、转置后的tensor,而是修改了tensor中的一些属性(也就是元数据),使得此时的offset和stride是与转置tensor相对应的。转置的tensor和原tensor的内存是共享的!  

transpose()后改变元数据,代码示例: 

import torch

x = torch.randn(3, 2)
y = torch.transpose(x, 0, 1)
print("修改前:")
print("x-", x)
print("y-", y)

print("\n修改后:")
y[0, 0] = 11
print("x-", x)
print("y-", y)


修改前:
x- tensor([[ 0.9611, -0.6472],
        [ 0.5917,  0.3073],
        [-0.6873, -0.3117]])
y- tensor([[ 0.9611,  0.5917, -0.6873],
        [-0.6472,  0.3073, -0.3117]])

修改后:
x- tensor([[11.0000, -0.6472],
        [ 0.5917,  0.3073],
        [-0.6873, -0.3117]])
y- tensor([[11.0000,  0.5917, -0.6873],
        [-0.6472,  0.3073, -0.3117]])

Process finished with exit code 0

可以看到,改变了y的元素的值的同时,x的元素的值也发生了变化

因此可以说,x是contiguous的,但y不是(因为内部数据不是通常的布局方式)。注意不要被contiguous的字面意思“连续的”误解,tensor中数据还是在内存中一块区域里,只是布局的问题!

为什么这么说:因为,y里面数据布局的方式和从头开始创建一个常规的tensor布局的方式是不一样的。这个可能只是python中之前常用的浅拷贝y还是指向x变量所处的位置,只是说记录了transpose这个变化的布局。

使用contiguous() 

如果想要断开这两个变量之间的依赖(x本身是contiguous的),就要使用contiguous()针对x进行变化感觉上就是我们认为的深拷贝

 当调用contiguous()时会强制拷贝一份tensor,让它的布局和从头创建的一模一样,但是两个tensor完全没有联系

x = torch.randn(3, 2)
y = torch.transpose(x, 0, 1).contiguous()
print("修改前:")
print("x-", x)
print("y-", y)
 
print("\n修改后:")
y[0, 0] = 11
print("x-", x)
print("y-", y)

修改前:
x- tensor([[-0.0392, -1.2368],
        [-1.6313,  0.7148],
        [ 0.6547,  0.0560]])
y- tensor([[-0.0392, -1.6313,  0.6547],
        [-1.2368,  0.7148,  0.0560]])

修改后:
x- tensor([[-0.0392, -1.2368],
        [-1.6313,  0.7148],
        [ 0.6547,  0.0560]])
y- tensor([[11.0000, -1.6313,  0.6547],
        [-1.2368,  0.7148,  0.0560]])

Process finished with exit code 0

4、pytorch学习 中 torch.squeeze() 和torch.unsqueeze()的用法

https://zhuanlan.zhihu.com/p/86763381

squeeze的用法主要就是对数据的维度进行压缩或者解压。

https://pytorch.org/docs/stable/generated/torch.squeeze.html#torch.squeeze

torch.squeeze(input, dim=None, out=None)

  • 参数:
  • input (Tensor) – 输入张量
  • dim (int, optional) – 如果给定,则input只会在给定维度挤压
  • out (Tensor, optional) – 输出张量

为何只去掉 1 呢?

多维张量本质上就是一个变换,如果维度是 1 ,那么,1 仅仅起到扩充维度的作用,而没有其他用途,因而,在进行降维操作时,为了加快计算,是可以去掉这些 1 的维度。

注意: 返回张量与输入张量共享内存,所以改变其中一个的内容会改变另一个。

>>> x = torch.zeros(2, 1, 2, 1, 2)
>>> x.size()
torch.Size([2, 1, 2, 1, 2])
>>> y = torch.squeeze(x)
>>> y.size()
torch.Size([2, 2, 2])
>>> y = torch.squeeze(x, 0)
>>> y.size()
torch.Size([2, 1, 2, 1, 2])
>>> y = torch.squeeze(x, 1)
>>> y.size()
torch.Size([2, 2, 1, 2])

unsqueeze的作用

torch.unsqueeze(input, dim, out=None)

参数:

  • tensor (Tensor) – 输入张量
  • dim (int) – 插入维度的索引
  • out (Tensor, optional) – 结果张量
  • 扩展维度

返回一个新的张量,对输入的既定位置插入维度 1

  • 注意: 返回张量与输入张量共享内存,所以改变其中一个的内容会改变另一个。

如果dim为负,则将会被转化dim+input.dim()+1

import torch

x = torch.Tensor([1, 2, 3, 4])  # torch.Tensor是默认的tensor类型(torch.FlaotTensor)的简称。

print('-' * 50)
print(x)  # tensor([1., 2., 3., 4.])
print(x.size())  # torch.Size([4])
print(x.dim())  # 1
print(x.numpy())  # [1. 2. 3. 4.]

print('-' * 50)
print(torch.unsqueeze(x, 0))  # tensor([[1., 2., 3., 4.]])
print(torch.unsqueeze(x, 0).size())  # torch.Size([1, 4])
print(torch.unsqueeze(x, 0).dim())  # 2
print(torch.unsqueeze(x, 0).numpy())  # [[1. 2. 3. 4.]]

print('-' * 50)
print(torch.unsqueeze(x, 1))
# tensor([[1.],
#         [2.],
#         [3.],
#         [4.]])
print(torch.unsqueeze(x, 1).size())  # torch.Size([4, 1])
print(torch.unsqueeze(x, 1).dim())  # 2

print('-' * 50)
print(torch.unsqueeze(x, -1))
# tensor([[1.],
#         [2.],
#         [3.],
#         [4.]])
print(torch.unsqueeze(x, -1).size())  # torch.Size([4, 1])
print(torch.unsqueeze(x, -1).dim())  # 2

print('-' * 50)
print(torch.unsqueeze(x, -2))  # tensor([[1., 2., 3., 4.]])
print(torch.unsqueeze(x, -2).size())  # torch.Size([1, 4])
print(torch.unsqueeze(x, -2).dim())  # 2

5、grid_sample()函数及双线性采样

https://zhuanlan.zhihu.com/p/112030273

首先Pytorch中grid_sample函数的接口声明如下:

torch.nn.functional.grid_sample(input, grid, mode='bilinear', padding_mode='zeros', align_corners=None)

在官方文档里面关于该函数的作用是这样描述的:

Given an input and a flow-field grid, computes the output using input values and pixel locations from grid.

简单来说就是,提供一个input的Tensor以及一个对应的flow-field网格(比如光流,体素流等),然后根据grid中每个位置提供的坐标信息(这里指input中pixel的坐标),将input中对应位置的像素值填充到grid指定的位置,得到最终的输出。

关于input、grid以及output的尺寸如下所示:(input也可以是5D的Tensor,这里我们只考虑4D的情况)

这里的input和output就是输入的图片,或者是网络中的feature map。关键的处理过程在于grid,grid的最后一维的大小为2,即表示input中pixel的位置信息(x,y),这里一般会将x和y的取值范围归一化到[-1. 1]之间,  (-1,-1)表示input左上角的像素的坐标, (1,1)表示input右下角的像素的坐标,对于超出这个范围的坐标,函数将会根据参数padding_mode的设定进行不同的处理。

  • padding_mode='zeros':对于越界的位置在网格中采用pixel value=0进行填充。
  • padding_mode='border':对于越界的位置在网格中采用边界的pixel value进行填充。
  • padding_mode='reflection':对于越界的位置在网格中采用关于边界的对称值进行填充。

对于mode='bilinear'参数,则定义了在input中指定位置的pixel value中进行插值的方法,为什么需要插值呢?因为前面我们说了,grid中表示的位置信息x和y的取值范围在[-1,1]之间,这就意味着我们要根据一个浮点型的坐标值在input中对pixel value进行采样,mode有nearest和bilinear两种模式。 nearest就是直接采用与(x,y)距离最近处的像素值来填充grid,而bilinear则是采用双线性插值的方法来进行填充,总之其与nearest的区别就是nearest只考虑最近点的pixel value,而bilinear则采用(x,y)周围的四个pixel value进行加权平均值来填充grid

双线性插值:

上面讲到双线性插值会对(x,y)周围的四个pixel value进行加权平均

猜你喜欢

转载自blog.csdn.net/Vpn_zc/article/details/113175796