创建tensor
参考链接:Pytorch 入门与提高(1)—创建 Tensor - 掘金 (juejin.cn)
使用列表或者numpy来创建tensor
import torch
import numpy as np
x_data = torch.tensor([1,2,3])
x_data = torch.from_numpy(np.array([1,2,3])) # tensor([1, 2, 3], dtype=torch.int32)
x_data = torch.Tensor([1,2,3,4])
x_data = torch.Tensor(np.array([1,2,3])) # torch.FloatTensor
通过torch.tensor(data)或者torch.Tensor(data)都可以,其中data可以是列表或者numpy创建的array。两者的有三个区别:(参考链接:PyTorch中torch.tensor与torch.Tensor的区别详解 - 云+社区 - 腾讯云 (tencent.com))
- torch.tensor(data)创建的数据类型是由data推测过来的,而torch.Tensor(data)是由当前torch的默认的数据类型决定。如上面注释所示。
- torch.Tensor(size)可以给定大小size通过Tensor(size)来创建一个张量,其值随机。如
size_Tensor = torch.Tensor(2,3)
output:
tensor([[9.5461e-01, 4.4377e+27, 1.4013e-45],
[0.0000e+00, 1.4013e-45, 0.0000e+00]])
- torch.Tensor(1)创建的是一个张量,torch.tensor(1)创建的是一个标量。
one_data = torch.tensor(1)
one_data_Tensor = torch.Tensor(1)
print(one_data, one_data_Tensor)
output:
tensor(1) tensor([0.])
查看tensor的类型:torch_data.type()
查看张量的shape:torch_data.shape
查看张量的总维度:a.dim()
设置当前默认的张量类型:torch.set_default_tensor_type(torch.DoubleTensor)
创建随机张量
- 创建随机tensor : rand(size)
rtensor = torch.rand(2,3)
print(rtensor)
output:
tensor([[0.0979, 0.4639, 0.0337],
[0.5375, 0.3682, 0.2702]])
- 创建整数随机tensor:randint(min, max, size) 其中取值范围为[min,max)
ritensor = torch.randint(0, 10, (2,3))
print(ritensor)
output:
tensor([[6, 9, 9],
[9, 9, 8]])
- 创建正态分布的随机tensor:normal(mean, std) 其中mean和std都是tensor,可以为标量或者为张量。mean和std中的元素个数相同,两两组合表示一个正态分布,从每个分布中取得一个值,组合起来作为最终的输出。
nortensor = torch.normal(mean=torch.tensor([1.,2.,3.]), std=torch.tensor([0.1, 0.2, 0.3]))#都需要为浮点数
print(nortensor)
output:
tensor([1.0137, 2.0991, 3.2177])
除此之外,mean或者std可以为同时为标量或者某一个为标量。当某一个为标量时表示此时所有的输出共享一个std。(参考链接:torch.normal()_sxs11的博客-CSDN博客)
- 在学习的过程中报了这样一个错误,
RuntimeError: “normal_kernel_cpu“ not implemented for ‘Long‘此处表明mean或者std都不能为整型。
- 创建正态分布的随机tensor:randn(size) 其中mean=0, std=1.
rntensor = torch.randn(2,3)
print(rntensor)
output:
tensor([[-0.4798, 0.7536, -0.7998],
[-0.7511, 1.1558, -0.6084]])
- 创建随机区间值:randperm(10) 其中输出范围为0~9,共10个数,但是其输出顺序被打乱。很适合用于下标打乱表示。
rptensor = torch.randperm(10)
print(rptensor)
output:
tensor([3, 6, 1, 0, 7, 4, 8, 9, 5, 2])
#举例:
rptensor = torch.randperm(10)
print(rptensor)
a = torch.rand(2,3)
b = torch.rand(2,2)
print(a,"\n", b)
idx = torch.randperm(2)
print(idx)
print(a[idx])
print(b[idx])
output:
tensor([[0.8594, 0.7068, 0.1541],
[0.4272, 0.6582, 0.2319]])
tensor([[0.6648, 0.2729],
[0.9917, 0.4138]])
tensor([0, 1])
tensor([[0.8594, 0.7068, 0.1541],
[0.4272, 0.6582, 0.2319]])
tensor([0.0000, 4.5000, 9.0000])
创建规律型张量
- 创建等距采样的tensor:linspace(min, max, steps) 其中,steps为输出的个数,输出的数据中包含min和max。
lintensor = torch.linspace(0, 10, steps=4)
print(lintensor)
output:
tensor([ 0.0000, 3.3333, 6.6667, 10.0000])
- 创建固定步幅的tensor:arange(min, max, step) 其中取值范围为[min,max),step为步幅。
artensor = torch.arange(0, 11, 2)
print(artensor)
output:
tensor([0, 2, 4, 6, 8])
创建填充型tensor
全为x:torch.full(size, x)
全为1:torch.ones(size)
全为0:torch.zeros(size)
对角线为1:torch.eye(size)
tensor切片
参考链接:Pytorch 入门与提高(2)—通过 slice 来创建 Tensor - 掘金 (juejin.cn)
张量理解:
创建一批图像张量:
a = torch.rand(4,3,28,28)
其中张量表示(B×C×H×W)。
使用:获取张量切片
在某个维度上设定一个区间,其中区间是左闭右开[start,end),不需要设定区间的维度可以使用:来代替,如果是有多个可以使用...来代替。
对于[start,end)的要求,左闭右开区间,其中start或者end都可以为负值,表示从最后一个开始,-1表示最后一个,-2表示倒数第二个。
此外,[start,end)还可以设定步幅,按照步幅选择数值。
print(a[:2,...].shape) # (2,3,28,28)
print(a[1:,...].shape) #(3,3,28,28)
print(a[0, 0,...].shape) #(28,28) 维度在边上,就不保留了
print(a[:2, :-2,...].shape) #(2, 1, 28,28)
output:
torch.Size([4, 3, 28, 28])
torch.Size([2, 3, 28, 28])
torch.Size([3, 3, 28, 28])
torch.Size([28, 28])
torch.Size([2, 1, 28, 28])
注意:第三个获取切片的方式,如果边上的维度上只取一个,那么这个维度在切片tensor中不被保留。
在指定维度上选择某些位置的切片
torch_data.index_select(dim, position)
如下代码,在dim=2(从dim=0开始)上选择pos=1,2,3,4位置上的切片.
b = a.index_select(2, torch.tensor([0,1,2,3,4]))
print(b.shape)
output:
torch.Size([4, 3, 5, 28])
筛选tensor并创建新tensor
torch_data.ge(other) 大于等于
torch_data.mask_select(mask)
注意:mask_select()将为true位置的数组合为一维向量输出,不能够保持原有的形状。
a = torch.randint(0, 10, (3,3))
b = a>=5
print(a)
print(b)
c = a.ge(5)# 其效果等价于a>=5
print(c)
output:
tensor([[7, 8, 1],
[1, 0, 7],
[8, 8, 0]])
tensor([[ True, True, False],
[False, False, True],
[ True, True, False]])
tensor([[ True, True, False],
[False, False, True],
[ True, True, False]])
out = a.masked_select(c)
print(out)
output:
tensor([9, 9, 8, 8, 6, 6])
tensor的维度变换
参考链接:Pytorch 入门与提高(3)—tensor 的 reshape 操作 - 掘金 (juejin.cn)
读到作者的第三篇博客,作者已经收到了掘金的搪瓷杯了。我也想......
- 改变形状:reshape、view
- 增删维度:squeeze、unsqueeze
- 维度变换:transpose、permute
- 维度扩展:expand、repeat
- 数据拷贝:clone、detach
改变形状view和reshape
a = torch.arange(27).reshape((3,3,3))
b = a.view(9,3)
print(a.shape)
print(b.shape)
output:
torch.Size([3, 3, 3])
torch.Size([9, 3])
view和shape的区别
参考链接:PyTorch:view() 与 reshape() 区别详解_Flag_ing的博客-CSDN博客
torch的view()与reshape()方法都可以用来重塑tensor的shape,区别就是使用的条件不一样。view()方法只适用于满足连续性条件的tensor,并且该操作不会开辟新的内存空间,只是产生了对原存储空间的一个新别称和引用,返回值是视图。而reshape()方法的返回值既可以是视图,也可以是副本,当满足连续性条件时返回view,否则返回副本[ 此时等价于先调用contiguous()方法在使用view() ]。因此当不确能否使用view时,可以使用reshape。如果只是想简单地重塑一个tensor的shape,那么就是用reshape,但是如果需要考虑内存的开销而且要确保重塑后的tensor与之前的tensor共享存储空间,那就使用view()。
- tensor存储方式 tensor存储包含信息区和存储区,在直接等号赋值(x1=x0)或者获取切片(x1=x0[1:])或者转置时都指向同一片存储区,发生变化的只是对数据索引而已。
- 数据存储的连续性问题 原张量x0是的数据在内存中是连续保存的,经过转置后得到的新张量x1,其与x0都指向同一片数据存储区,只不过对数据的访问方式发生了变化。在x0中访问第一行时stride=1,但是经过转置后的x1中,访问第一行数据stride=x0的列数,因为其要跳着去访问数据。
- view和reshape
view是视图的意思,其概念有点类似mysql中的感觉。其在使用时,不重新开辟空间建立张量,而只是改变访问方式。其只能针对连续性访问的张量起作用,而对于不连续的张量如转置后的会报错:
RuntimeError: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Use .reshape(...) instead.
当不满足连续性条件时,可以先使用函数 .contiguous() 将其变为连续而后再执行view()。
reshape 在张量是连续性时,其直接返回view的操作结果,如果是非连续性的,其先执行.contiguous()而后执行view(),此时会重新开辟空间。
增删维度:squeeze和unsqueeze
torch_tensor.unsqueeze(dim)在指定dim上增加一个维度,比如在dim=0增加一个维度,那么就是在张量整体前增加,如果dim=-1,表示在张量整体后加一个维度,还可以在中间某个维度添加。
a = torch.arange(27).reshape((3,3,3))
b = a.unsqueeze(dim=0)
c = a.unsqueeze(dim=1)
d = a.unsqueeze(dim=-1)
print(a.shape)
print(b.shape)
print(c.shape)
print(d.shape)
output:
torch.Size([3, 3, 3])
torch.Size([1, 3, 3, 3])
torch.Size([3, 1, 3, 3])
torch.Size([3, 3, 3, 1])
特别注意的是:在获取切片的时候,维度数量可能会发生变化。特别是获得切片后,dim=0或者dim=-1的维度上,如果位置数为1,那么此维度就会消失。
x = torch.empty((2,3,4,5))
y = x[0,...]
print(x.shape)
print(y.shape)
output:
torch.Size([2, 3, 4, 5])
torch.Size([3, 4, 5]) #注意此处
报错: 在张量已有的维度上插入维度,会让已有的维度都往后窜,或者在张量的最后一个维度后插入维度,其他的地方插入都会报如下错误:
#*===========不报错================
a = torch.arange(4)
b = a.unsqueeze(dim=0).unsqueeze(dim=2)
print(a.shape)
print(b.shape)
output:
torch.Size([4])
torch.Size([1, 4, 1])
#=============报错================
a = torch.arange(27).reshape((3,3,3))
b = a.unsqueeze(dim=10)
output:
Traceback (most recent call last):
File "D:/08_studyProject/02_pytorchTutorials/Dataset/dataOperator3.py", line 48, in <module>
b = a.unsqueeze(dim=10)
IndexError: Dimension out of range (expected to be in range of [-4, 3], but got 10)
Process finished with exit code 1
torch_tensor.squeeze(dim)是对指定维度进行压缩,此维度的位置数为1,(位置数是指在shape中的某个维度上的数值)此时可以对其压缩。如果没有参数torch_tensor.squeeze()是对所有的维度上位置数等于1的维度全部压缩。
a = torch.arange(4)
b = a.unsqueeze(dim=0).unsqueeze(dim=2)
c = b.squeeze()
print(a.shape)
print(b.shape)
print(c.shape)
output:
torch.Size([4])
torch.Size([1, 4, 1])
torch.Size([4])
维度变换:transpose和permute
transpose:只能选择tensor中两个维度进行转置,其中两个维度书写顺序无关。
a = torch.arange(9).reshape(3, 3)
b = a.transpose(dim0=0, dim1=1)
print(b)
b = b.transpose(dim0=1, dim1=0)
print(b)
output:
tensor([[0, 3, 6],
[1, 4, 7],
[2, 5, 8]])
tensor([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
permute:可以让tensor按照指定维度顺序(维度的个数就是该tensor的维度数)进行转置
a = torch.empty(2,3,4)
b = a.permute((1,0,2))
print(a.shape)
print(b.shape)
output:
torch.Size([2, 3, 4])
torch.Size([3, 2, 4])
维度扩展:expand和repeat
expand(*sizes) -> Tensor. *sizes(torch.Size or int) - the desired expanded size. Returns a new view of the self tensor with singleton dimensions expanded to a larger size.
expand 返回当前张量在某维扩展更大后的张量。扩展(expand)张量不会分配新的内存,只是在存在的张量上创建一个新的视图(view),一个大小(size)等于1的维度扩展到更大的尺寸。如果不需要更改的维度可以使用-1来代替,size是指扩展后的张量的shape。
x = torch.tensor([1, 2, 3]) #shape (3,)
y = x.expand(2, 3)
print(y)
output:
tensor([[1, 2, 3],
[1, 2, 3]])
repeat(*sizes) -> Tensor. *size(torch.Size or int) - The number of times to repeat this tensor along each dimension. Repeats this tensor along the specified dimensions.
repeat 沿着特定的维度重复这个张量,和expand() 不同的是,这个函数拷贝张量的数据。其中size的含义是沿着对应的维度重复的次数。
x = torch.tensor([1, 2, 3])
y = x.repeat(3, 2)
print(y)
output:
tensor([[1, 2, 3, 1, 2, 3],
[1, 2, 3, 1, 2, 3],
[1, 2, 3, 1, 2, 3]])
数据拷贝:clone和detach
pytorch中的浅拷贝与深拷贝
参考文章:PyTorch中的拷贝 - 知乎 (zhihu.com)
- 浅拷贝的方法 共享内存地址,数据同步变化
| 函数 | 说明 |
|---|---|
| a.numpy() | Tensor->Numpy array |
| torch.from_numpy() | Numpy array->Tensor |
| .view() | 改变tensor的形状,但是共享内存,不要使用id直接判断 |
| y=x[:] | 索引 |
| torch.detach() | 新tensor会脱离计算图,不会涉及梯度计算 |
| 选择函数 | index_select\masked_select\gather |
- 深拷贝方法
| 函数 | 说明 |
|---|---|
| torch.clone() | 新的tensor会保留在计算图参与梯度计算 |
| torch.Tensor() | 类构造张量 |
| torch.tensor() | 工厂函数 |
clone和detach
参考链接:Pytorch中clone(),copy_(),detach(),.data的辨析与应用 - 知乎 (zhihu.com)
clone 创建一个tensor与源tensor有相同的shape,dtype和device,不共享内存地址,但新tensor的梯度会叠加在源tensor上。
其中进行了计算:
b = a.clone()
c = a*2
d = b*3
此时a和b不共享内存,但是从c-->a以及从d-->b的梯度值都会叠加到a上。也就是从a--->b进行了一个恒等映射。图中蓝色是原数据计算,红色是梯度走向。
detach 函数返回与调用对象tensor相关的一个tensor,此新tensor与源tensor共享数据内存(那么tensor的数据必然是相同的),但其requires_grad为False,并且不包含源tensor的计算图信息。
如果在detach语句后增加一条语句 b.requires_grad(),那么此时pytorch会计算跟踪其有关计算,但是此时梯度不会从b流向a。
clone函数使用时b = a.clone(),b新开辟内存,但是从叶子结点计算到b的梯度也会累加到a上。detach函数使用时b=a.detch(),b不新开辟内存,而是与a共享内存,但是其梯度不会累加到a上,并且b的记录梯度的设置默认为false,当然也可以通过b.requires_grad_()将其设置为true。
clone()可在新内存空间复制原tensor但是梯度信息不独立,detach()可以独立梯度信息,但是与原tensor具有相同的内存,因此常常联合使用二者创建出数据行啊同,完全独立的新tensor。常用的手段是b=a.clone().detach()或者b=a.detach().clone()。