detach() 和 .data

PyTorch0.4中,.data 仍保留,但建议使用 .detach(), 区别在于 .data 返回和 x 的相同数据 tensor, 但不会加入到x的计算历史里,且require s_grad = False, 这样有些时候是不安全的, 因为 x.data 不能被 autograd 追踪求微分 。 .detach() 返回相同数据的 tensor ,且 requires_grad=False ,但能通过 in-place 操作报告给 autograd 在进行反向传播的时候.
举例:
tensor.data

1
2
3
4
5
6
7
8
9
10
11
12
>>> a = torch.tensor([1,2,3.], requires_grad =True)
>>> out = a.sigmoid()
>>> c = out.data
>>> c.zero_()
tensor([ 0., 0., 0.])

>>> out # out的数值被c.zero_()修改
tensor([ 0., 0., 0.])

>>> out.sum().backward() # 反向传播
>>> a.grad # 这个结果很严重的错误,因为out已经改变了
tensor([ 0., 0., 0.])

tensor.detach()

1
2
3
4
5
6
7
8
9
10
11
12
>>> a = torch.tensor([1,2,3.], requires_grad =True)
>>> out = a.sigmoid()
>>> c = out.detach()
>>> c.zero_()
tensor([ 0., 0., 0.])

>>> out # out的值被c.zero_()修改 !!
tensor([ 0., 0., 0.])

>>> out.sum().backward() # 需要原来out得值,但是已经被c.zero_()覆盖了,结果报错
RuntimeError: one of the variables needed for gradient
computation has been modified by an

torch.cat() 张量拼接

对张量沿着某一维度进行拼接。连接后数据的总维数不变。,ps:能拼接的前提是对应的维度相同!!!

例如对两个2维tensor(分别为2*3,1*3)进行拼接,拼接完后变为3*3的2维 tensor。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
In [1]: import torch

In [2]: torch.manual_seed(1)
Out[2]: <torch._C.Generator at 0x19e56f02e50>

In [3]: x = torch.randn(2,3)

In [4]: y = torch.randn(1,3)

In [5]: x
Out[5]:
tensor([[ 0.6614, 0.2669, 0.0617],
[ 0.6213, -0.4519, -0.1661]])

In [6]: y
Out[6]: tensor([[-1.5228, 0.3817, -1.0276]])

In [9]: torch.cat((x,y),0)
Out[9]:
tensor([[ 0.6614, 0.2669, 0.0617],
[ 0.6213, -0.4519, -0.1661],
[-1.5228, 0.3817, -1.0276]])

以上dim=0 表示按列进行拼接,dim=1表示按行进行拼接。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
In [11]: z = torch.randn(2,2)

In [12]: z
Out[12]:
tensor([[-0.5631, -0.8923],
[-0.0583, -0.1955]])

In [13]: x
Out[13]:
tensor([[ 0.6614, 0.2669, 0.0617],
[ 0.6213, -0.4519, -0.1661]])

In [14]: torch.cat((x,z),1)
Out[14]:
tensor([[ 0.6614, 0.2669, 0.0617, -0.5631, -0.8923],
[ 0.6213, -0.4519, -0.1661, -0.0583, -0.1955]])

torch.stack() 张量堆叠

torch.cat()拼接不会增加新的维度,但torch.stack()则会增加新的维度。

例如对两个1*2 维的 tensor 在第0个维度上stack,则会变为2*1*2的 tensor;在第1个维度上stack,则会变为1*2*2 的tensor。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
In [22]: x = torch.randn(1,2)

In [23]: y = torch.randn(1,2)

In [24]: x.shape
Out[24]: torch.Size([1, 2])

In [25]: x = torch.randn(1,2)

In [26]: y = torch.randn(1,2)

In [27]: torch.stack((x,y),0) # 维度0堆叠
Out[27]:
tensor([[[-1.8313, 1.5987]],

[[-1.2770, 0.3255]]])

In [28]: torch.stack((x,y),0).shape
Out[28]: torch.Size([2, 1, 2])

In [29]: torch.stack((x,y),1) # 维度1堆叠
Out[29]:
tensor([[[-1.8313, 1.5987],
[-1.2770, 0.3255]]])

In [30]: torch.stack((x,y),1).shape
Out[30]: torch.Size([1, 2, 2])

torch.transpose() 矩阵转置

举例说明

1
2
3
torch.manual_seed(1)
x = torch.randn(2,3)
print(x)

原来x的结果:

1
2
3
 0.6614  0.2669  0.0617
0.6213 -0.4519 -0.1661
[torch.FloatTensor of size 2x3]

将x的维度互换:x.transpose(0,1) ,其实相当于转置操作!
结果

1
2
3
4
0.6614  0.6213
0.2669 -0.4519
0.0617 -0.1661
[torch.FloatTensor of size 3x2]

torch.permute() 多维度互换

permute是更灵活的transpose,可以灵活的对原数据的维度进行调换,而数据本身不变。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
In [31]: x = torch.randn(2,3,4)

In [32]: x
Out[32]:
tensor([[[ 0.7626, 0.4415, 1.1651, 2.0154],
[ 0.2152, -0.5242, -1.8034, -1.3083],
[ 0.4100, 0.4085, 0.2579, 1.0950]],

[[-0.5065, 0.0998, -0.6540, 0.7317],
[-1.4567, 1.6089, 0.0938, -1.2597],
[ 0.2546, -0.5020, -1.0412, 0.7323]]])

In [33]: x.shape
Out[33]: torch.Size([2, 3, 4])

In [34]: x.permute(1,0,2) # 0维和1维互换,2维不变!
Out[34]:
tensor([[[ 0.7626, 0.4415, 1.1651, 2.0154],
[-0.5065, 0.0998, -0.6540, 0.7317]],

[[ 0.2152, -0.5242, -1.8034, -1.3083],
[-1.4567, 1.6089, 0.0938, -1.2597]],

[[ 0.4100, 0.4085, 0.2579, 1.0950],
[ 0.2546, -0.5020, -1.0412, 0.7323]]])

In [35]: x.permute(1,0,2).shape
Out[35]: torch.Size([3, 2, 4])

torch.squeeze() 和 torch.unsqueeze()

常用来增加或减少维度,如没有batch维度时,增加batch维度为1。

  • squeeze(dim_n)压缩,减少dim_n维度 ,即去掉元素数量为1的dim_n维度。
  • unsqueeze(dim_n),增加dim_n维度,元素数量为1。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
In [38]: x = torch.randn(1,3,4)

In [39]: x.shape
Out[39]: torch.Size([1, 3, 4])

In [40]: x
Out[40]:
tensor([[[-0.4791, 0.2912, -0.8317, -0.5525],
[ 0.6355, -0.3968, -0.6571, -1.6428],
[ 0.9803, -0.0421, -0.8206, 0.3133]]])

In [41]: x.squeeze()
Out[41]:
tensor([[-0.4791, 0.2912, -0.8317, -0.5525],
[ 0.6355, -0.3968, -0.6571, -1.6428],
[ 0.9803, -0.0421, -0.8206, 0.3133]])

In [42]: x.squeeze().shape
Out[42]: torch.Size([3, 4])

In [43]: x.unsqueeze(0)
Out[43]:
tensor([[[[-0.4791, 0.2912, -0.8317, -0.5525],
[ 0.6355, -0.3968, -0.6571, -1.6428],
[ 0.9803, -0.0421, -0.8206, 0.3133]]]])

In [44]: x.unsqueeze(0).shape
Out[44]: torch.Size([1, 1, 3, 4])

torch.Tensor有两个实例方法可以用来扩展某维的数据的尺寸,分别是 repeat()expand()

expand()

返回当前张量在某维扩展更大后的张量。按照指定size扩充。

扩展(expand)张量不会分配新的内存,只是在存在的张量上创建一个新的视图(view),一个大小(size)等于1的维度扩展到更大的尺寸。
代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
In [45]: x = torch.randn(1,3)

In [46]: x
Out[46]: tensor([[-1.1352, 0.3773, -0.2824]])

In [47]: x.expand(2, 3)
Out[47]:
tensor([[-1.1352, 0.3773, -0.2824],
[-1.1352, 0.3773, -0.2824]])

In [48]: x.expand(2, -1)
Out[48]:
tensor([[-1.1352, 0.3773, -0.2824],
[-1.1352, 0.3773, -0.2824]])

repeat()

沿着特定的维度重复这个张量,按照倍数扩充;和expand()不同的是,这个函数拷贝张量的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
In [53]: x
Out[53]: tensor([[-1.1352, 0.3773, -0.2824]])

In [54]: x.shape
Out[54]: torch.Size([1, 3])

In [55]: x.repeat(2,3)
Out[55]:
tensor([[-1.1352, 0.3773, -0.2824, -1.1352, 0.3773, -0.2824, -1.1352, 0.3773,
-0.2824],
[-1.1352, 0.3773, -0.2824, -1.1352, 0.3773, -0.2824, -1.1352, 0.3773,
-0.2824]])

In [56]: x.repeat(2,3).shape
Out[56]: torch.Size([2, 9])

torch.narrow()

PyTorch 中的narrow()函数起到了筛选一定维度上的数据作用。个人感觉与x[begin:end] 相同!

参考官网:torch.narrow()

用法:torch.narrow(input, dim, start, length) → Tensor

返回输入张量的切片操作结果。 输入tensor和返回的tensor共享内存。

参数说明:

  • input (Tensor) – 需切片的张量
  • dim (int) – 切片维度
  • start (int) – 开始的索引
  • length (int) – 切片长度

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
In [1]: import torch

In [2]: x = torch.randn(3,3)

In [3]: x
Out[3]:
tensor([[ 1.2474, 0.1820, -0.0179],
[ 0.1388, -1.7373, 0.5934],
[ 0.2288, 1.1102, 0.6743]])

In [4]: x.narrow(0, 1, 2) # 行切片
Out[4]:
tensor([[ 0.1388, -1.7373, 0.5934],
[ 0.2288, 1.1102, 0.6743]])

In [5]: x.narrow(1, 1, 2) # 列切片
Out[5]:
tensor([[ 0.1820, -0.0179],
[-1.7373, 0.5934],
[ 1.1102, 0.6743]])


torch.unbind()

torch.unbind()移除指定维后,返回一个元组,包含了沿着指定维切片后的各个切片。

参考官网:torch.unbind()

用法:torch.unbind(input, dim=0) → seq

返回指定维度切片后的元组。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
In [6]: x
Out[6]:
tensor([[ 1.2474, 0.1820, -0.0179],
[ 0.1388, -1.7373, 0.5934],
[ 0.2288, 1.1102, 0.6743]])

In [7]: torch.unbind(x, 0)
Out[7]:
(tensor([ 1.2474, 0.1820, -0.0179]),
tensor([ 0.1388, -1.7373, 0.5934]),
tensor([0.2288, 1.1102, 0.6743]))

In [8]: torch.unbind(x, 1)
Out[8]:
(tensor([1.2474, 0.1388, 0.2288]),
tensor([ 0.1820, -1.7373, 1.1102]),
tensor([-0.0179, 0.5934, 0.6743]))

torch.gather()

参考官网:torch.gather

用法:torch.gather(input, dim, index, out=None, sparse_grad=False) → Tensor

收集输入的特定维度dim指定位置index的数值。

对于一个三维tensor,结果如下:

1
2
3
out[i][j][k] = input[index[i][j][k]][j][k]  # if dim == 0
out[i][j][k] = input[i][index[i][j][k]][k] # if dim == 1
out[i][j][k] = input[i][j][index[i][j][k]] # if dim == 2

参数说明:

  • input (Tensor) – 输入张量
  • dim (int) – 索引维度
  • index (LongTensor) – 收集元素的索引
  • out (Tensor, optional) – 目标张量
  • sparse_grad (bool,optional) – 输入为稀疏张量

直接看官网解释有点不明白,参考另一文章的实例说明:https://blog.csdn.net/cpluss/article/details/90260550

在序列标注问题上,我们给每一个单词都标上一个标签。不妨假设我们有4个句子,每个句子的长度不一定相同,标签如下:

1
2
3
4
5
6
input = [
[2, 3, 4, 5],
[1, 4, 3],
[4, 2, 2, 5, 7],
[1]
]

上例中有四个句子,长度分别为4,3,5,1,其中第一个句子的标签为2,3,4,5。我们知道,处理自然语言问题时,一般都需要进行padding,即将不同长度的句子padding到同一长度,以0为padding,那么上述经padding后变为:

1
2
3
4
5
6
input = [
[2, 3, 4, 5, 0, 0],
[1, 4, 3, 0, 0, 0],
[4, 2, 2, 5, 7, 0],
[1, 0, 0, 0, 0, 0]
]

那么问题来了,现在我们想获得每个句子中最后一个词语的标签,该怎么得到呢?既是,第一句话中的5,第二句话中的3,第三句话中7,第四句话中的1。

此时就需要用gather函数。

此时我们的input就是填充之后的tensor,dim=1, index就是各个句子的长度,即[[4],[3],[5],[1]]。之所以维度是4*1,是为了满足index维度和input维度之间的关系(讲解参数时有讲)。

代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
In [26]: import torch
...: input = [
...: [2, 3, 4, 5, 0, 0],
...: [1, 4, 3, 0, 0, 0],
...: [4, 2, 2, 5, 7, 0],
...: [1, 0, 0, 0, 0, 0]
...: ]
...: input = torch.tensor(input)
...: #注意index的类型
...: length = torch.LongTensor([[4],[3],[5],[1]])
...: #index之所以减1,是因为序列维度是从0开始计算的
...: out = torch.gather(input, 1, length-1)
...: out
Out[26]:
tensor([[5],
[3],
[7],
[1]])

Contact