本文主要讲tensor的裁剪、索引、降维和增维
Tensor与numpy互转、Tensor运算等,请看这篇文章
目录
9.1、首先看torch.squeeze()函数:
示例9.1:(基本的使用)
小技巧1:如何看维数
示例9.2:(指定降多少维)
小技巧2:如何理解如size([2,1,2,1,2])等等张量的形状
示例9.3:(不可降维的张量)
9.2、torch.unsqueeze()函数
9.3、torch.view()函数和torch.resize_()函数
十、Tensor的索引
10.1、Tensor的一般索引(共享内存)
10.2、Tensor的高级索引(不共享内存)
理解辅助:
小技巧:什么是共享内存?
python列表的共享内存:
张量的一般索引或高级索引得出的张量是否共享内存:
十一、Tensor梯度裁剪
torch.clamp(tensor,min,max,out=None)函数:
本文主要讲tensor的裁剪、索引、降维和增维。同时详细讲了入门者的痛点就是看不懂tensor的size表示或者看不懂其内部结构,并且补充了一个大多数人都不知道的增、降维的方法,索引位置如何理解等
九、Tensor的降维和增维
我们常见的用于tensor的维数操作有很多如torch.squeeze()可降维、torch.unsqueeze()可增维,下面我们通过一些例子简介两个函数
import torch as t
a=t.ones(2,1,2,1,2)
print("a==",a)
print("a.size()==",a.size())
b=t.squeeze(a)
print("b==",b)
print("b.size()==",b.size())
运行结果:
a== tensor([[[[[1., 1.]],
[[1., 1.]]]],
[[[[1., 1.]],
[[1., 1.]]]]])
a.size()== torch.Size([2, 1, 2, 1, 2])
b== tensor([[[1., 1.],
[1., 1.]],[[1., 1.],
[1., 1.]]])
b.size()== torch.Size([2, 2, 2])
一般我们可以直接认为 ( 后面有多少个 [ 就是多少维,如上面的示例9.1的a我们数一下发现其 ( 后面有5个 [ 那么我们可以说它是一个五维的张量或者说是一个五阶的矩阵,再如示例9.1的b,他是一个三维的张量,我们发现在用了一次torch.squeeze()函数就降了两维,那如果我只需要降一维要怎么操作呢?下面示例9.2操作一下:
import torch as t
a=t.ones(2,1,2,1,2)
print("a==",a)
print("a.size()==",a.size())
b=t.squeeze(a,1)
print("b==",b)
print("b.size()==",b.size())
c=t.squeeze(a,0)
print("c==",c)
print("c.size()==",c.size())
运行结果:
a== tensor([[[[[1., 1.]],
[[1., 1.]]]],
[[[[1., 1.]],
[[1., 1.]]]]])
a.size()== torch.Size([2, 1, 2, 1, 2])
b== tensor([[[[1., 1.]],[[1., 1.]]],
[[[1., 1.]],[[1., 1.]]]])
b.size()== torch.Size([2, 2, 1, 2])
c== tensor([[[[[1., 1.]],[[1., 1.]]]],
[[[[1., 1.]],
[[1., 1.]]]]])
c.size()== torch.Size([2, 1, 2, 1, 2])
小结:
由示例9.2我们可以看出torch.squeeze(input,dim=None)函数的参数dim当指定值为0时则不进行降维操作,若为1,则降一维;那么在这里我们要降多少维就用多少次就可以啦
在上面两个例子中如果你看torch.Size([2, 1, 2, 1, 2])这些一脸懵逼,看不出来是几维异或看不出来这个张量的结构是怎么样的,请看看我的理解,入门你可以认为这里 [] 里面多少个数字就是多少维那么怎么看这个张量的形状或者说行和列是怎么个构成呢?比如一个张量torch.Size([a, b, c, d, e])
那么这就是一个有a个元素的五维张量,这a个元素均是四维张量,一个四维张量又由b个三维张量组成,一个三维张量由c个二维张量组成,这个二维张量的形状为d*e即d行e列
如示例9.2的c
那么你发现规律了吗?
从这个图例也可以很好理解
再如torch.Size([1, 2, 3, 4, 5])就是一个有1个元素的五维张量,这1个元素均是四维张量,一个四维张量又由2个三维张量组成,一个三维张量由3个二维张量组成,这个二维张量的形状为4*5即4行5列
看看输出:
tensor([[[[[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.]],[[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.]],[[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.]]],
[[[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.]],[[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.]],[[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.]]]]])
那么是不是所有的张量都可以用进行降维呢?显然不是,在上面的例子中我们就可以看出被降的维数都是只有一个元素的,下面一个例子说明:
import torch as t
a=t.ones(2,2,2).squeeze(1)
b=t.ones(1,2,2).squeeze(1)
c=t.ones(2,1,2).squeeze(1)
print(b)
print("a.shape=={}\nb.shape=={}\nc.shape=={}\n".format(a.shape,b.shape,c.shape))
运行结果:
tensor([[[1., 1.],
[1., 1.]]])
a.shape==torch.Size([2, 2, 2])
b.shape==torch.Size([1, 2, 2])
c.shape==torch.Size([2, 2])
与torch.squeeze(input,dim=None)函数使用方法类似
import torch as t
a=t.ones(2,2,2).unsqueeze(1)
b=t.ones(1,2,2).unsqueeze(2)
c=t.ones(2,1,2)
d=t.unsqueeze(c,0)#与d=t.ones(2,1,2).unsqueeze(0)同
print("a.shape=={}\nb.shape=={}\nd.shape=={}\n".format(a.shape,b.shape,d.shape))
运行结果:
a.shape==torch.Size([2, 1, 2, 2])
b.shape==torch.Size([1, 2, 1, 2])
d.shape==torch.Size([1, 2, 1, 2])
不知道大家发现没有在上一篇中torch.view()函数和torch.resize_()函数也可以实现增、降维的能力
但不推荐在实际中增、降维用torch.resize_()函数,因为它会会为Tensor自动分配新的内存空间,所以下面将示例torch.view()函数,而torch.resize_()函数使用方法基本一致可自行测试
示例:
import torch as t
a=t.linspace(-1,1,10)
b=a.view(10,1)
c=a.view(1,2,5)
d=c.view(5,2)
print("a.shape=={}\nb.shape=={}\nc.shape=={}\nd.shape=={}\n".format(a.shape,b.shape,c.shape,d.shape))
print("a=={}\nb=={}\nc=={}\nd=={}\n".format(a,b,c,d))
运行结果:
a.shape==torch.Size([10])
b.shape==torch.Size([10, 1])
c.shape==torch.Size([1, 2, 5])
d.shape==torch.Size([5, 2])a==tensor([-1.0000, -0.7778, -0.5556, -0.3333, -0.1111, 0.1111, 0.3333, 0.5556,
0.7778, 1.0000])
b==tensor([[-1.0000],
[-0.7778],
[-0.5556],
[-0.3333],
[-0.1111],
[ 0.1111],
[ 0.3333],
[ 0.5556],
[ 0.7778],
[ 1.0000]])
c==tensor([[[-1.0000, -0.7778, -0.5556, -0.3333, -0.1111],
[ 0.1111, 0.3333, 0.5556, 0.7778, 1.0000]]])
d==tensor([[-1.0000, -0.7778],
[-0.5556, -0.3333],
[-0.1111, 0.1111],
[ 0.3333, 0.5556],
[ 0.7778, 1.0000]])
Tensor的索引与列表索引相似
import torch as t
a=t.arange(0,6).view(2,3)
print("a={}\na[0]={}\na[:,0]={}\na[:2]={}\na[:1,:1]={}\n".format(a,a[0],a[:,0],a[:2],a[:1,:1]))
运行结果:
a=tensor([[0, 1, 2],
[3, 4, 5]])
a[0]=tensor([0, 1, 2])
a[:,0]=tensor([0, 3])
a[:2]=tensor([[0, 1, 2],
[3, 4, 5]])
a[:1,:1]=tensor([[0]])
小结:可以看见上述示例中张量的索引方式为:a[Rows,Columns] ,这种方式对于三维等也是可以的,对于精准到一个单一元素可以加一参数,如:
import torch as t
a=t.arange(0,18).view(2,3,3)
print("a={}\na[0]={}\na[1,1,2]={}\na[1,2,0]={}\na[:1,:1]={}\n".format(a,a[0],a[1,1,2],a[1,2,0],a[:1,:1]))
运行结果:
a=tensor([[[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8]],[[ 9, 10, 11],
[12, 13, 14],
[15, 16, 17]]])
a[0]=tensor([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
a[1,1,2]=14
a[1,2,0]=15
a[:1,:1]=tensor([[[0, 1, 2]]])
小结:由上面的例子可以看见,对于三维张量索引时,a[Rows,Columns,index],不一样的就是此时只a[Rows,Columns]得到的是一个一维张量而不是一个元素,所以再加一个元素的索引就可以达到单一元素,四维张量也是类似
import torch as t
a=t.arange(0,18).view(2,3,3)
print("a={}\na[[0,1],...]={}\na[[1,0],[1,2],[2,2]]={}\na[[0,1,1],[2],[1]]={}\n".format(a,a[[0,1],...],a[[1,0],[1,2],[2,2]],a[[0,1,1],[2],[1]]))
运行结果:
a=tensor([[[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8]],[[ 9, 10, 11],
[12, 13, 14],
[15, 16, 17]]])
a[[0,1],...]=tensor([[[ 0, 1, 2], //##相当于a[0] and a[1] 即输出第一、二行
[ 3, 4, 5],
[ 6, 7, 8]],[[ 9, 10, 11],
[12, 13, 14],
[15, 16, 17]]])
a[[1,0],[1,2],[2,2]]=tensor([14, 8]) //##相当于a[1,1,2] and a[0,2,2]
a[[0,1,1],[2],[1]]=tensor([ 7, 16, 16]) //##相当于a[0,2,1] and a[1,2,1] and a[1,2,1]
理解辅助:
由上图可见,一个三维张量 行 是一个二维张量,列是一个一维张量,那么index就是这个一维张量里面的元素啦!
小结:一维索引一个参数,二维索引两个参数,三维索引三个参数
有python编程基础的朋友应该都看过下面这个例子:
>>> a=[1,2,3,4,5,6]
>>> b=a
>>> b[1]=10
>>> a
[1, 10, 3, 4, 5, 6]
由上面的例子中我们不难看出来,python列表中将一个已经存在的列表(a)赋给另一个列表(b)得出列表(b)是和原列表()共用一块内存的,共用即说明这两个列表无论是修改哪一个的元素值,另一个也会随着变化,就比如上面的列表a,b,改变b的值a也会变,同样改变a,b也变,大家可以之行测试一下
直接上例子:
import torch as t
a=t.arange(0,9).view(3,3)
b=a[1]
c=a[[1],...]
print("未改变前a:\n{}".format(a))
b[0]=100
print("改变一般索引得出张量b的值,此时a:\n{}".format(a))
c[0]=1000
print("改变一般索引得出张量c的值,此时a:\n{}".format(a))
运行结果:
未改变前a:
tensor([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
改变一般索引得出张量b的值,此时a:
tensor([[ 0, 1, 2],
[100, 4, 5],
[ 6, 7, 8]])
改变一般索引得出张量c的值,此时a:
tensor([[ 0, 1, 2],
[100, 4, 5],
[ 6, 7, 8]])
可见改变张量b时张量a随着改变,而改变c时a保持原来的值,即张量的一般索引与原张量共享内存,而张量的高级索引与原张量一般不共享内存
划重点:共享内存的两个列表一般内存地址相同,而共享内存的两个张量内存地址一般不相同
逐个比对tensor里面的元素,当tensor的一个元素
当 min<= tensor的一个元素 <=max,返回tensor的对应元素
当tensor的一个元素>max,返回max
示例如下:
import torch as t
a=t.arange(0,6).view(2,3)
b=t.clamp(a,3)
c=t.clamp(a,2,5)
print("a={}\nb={}\nc={}\n".format(a,b,c))
运行结果:
a=tensor([[0, 1, 2],
[3, 4, 5]])
b=tensor([[3, 3, 3],
[3, 4, 5]])
c=tensor([[2, 2, 2],
[3, 4, 5]])