operation
view/reshape
squeeze/unsqueeze
transpose/ t / permute
expand/ repeat
1.view/reshape
import torch
a = torch.rand(4, 1, 28, 28)
# 使用rand随机均匀初始化一个4维的tensor
# 在minist中可以理解为load进了4张灰度图片,长宽各为28
a.shape
output:
torch.Size([4, 1, 28, 28])
下面进行view操作:
a.view(4, 28 * 28)
# 4 1 * 28 * 28
output:
tensor([[0.1915, 0.9915, 0.4148, ..., 0.3582, 0.4939, 0.7734],
[0.7373, 0.5476, 0.0908, ..., 0.2784, 0.0220, 0.3247],
[0.6855, 0.1770, 0.6714, ..., 0.3116, 0.5055, 0.1866],
[0.0151, 0.9768, 0.4203, ..., 0.7694, 0.7458, 0.2084]])
(view必须满足物理意义,否则会导致数据的”污染“)
打印shape
a.view(4, 28 * 28).shape
output
torch.Size([4, 784])
b = a.view(4, 784)
# 这个时造成了数据的污染,b.view(4, 28, 28, 1)这样子也等于上面的数值,但是数据的理解不一样了。
数据的存储/维度顺序非常重要
下面这样view会出错:
a.view(4, 783)
ouput:
>RuntimeError: shape '[4, 783]' is invalid for input of size 3136
这是由于前后的size不一致导致的。
2.squeeze v.s squeeze
减少维度(挤压)v.s 增加维度
下面来看一些维度增加的举例:
a = torch.rand(4, 1, 28, 28)
在0维度前增加一个维度:
a.unsqueeze(0).shape
output:
torch.Size([1, 4, 1, 28, 28])
(增加了一个额外的维度,这里多出来的“1”可以理解为1个组)
这里并没有增加数据,而是增加了一个额外的概念 组
a.unsqueeze(-1).shape
# -1是指最后一个维度,这里是在-1之后插入,一般不用负数,正数已经可以覆盖所有的位置
output:
torch.Size([4, 1, 28, 28, 1])
下面这个表格展示了index的位置:
Pos.IDX
1
2
3
4
3
28
28
Neg.IDX
-4
-3
-2
-1
# 使用List来初始化一个维度为0,shape为[2]的tensor
a = torch.tensor([1.2, 2.3])
print(a.shape)
print(a.unsqueeze(1))
print(a.unsqueeze(1).shape)
output:
torch.Size([2])
tensor([[1.2000],
[2.3000]])
torch.Size([2, 1])
print(a.unsqueeze(0))
print(a.unsqueeze(0).shape)
output:
tensor([[1.2000, 2.3000]])
torch.Size([1, 2])
下面来看一个实际的案例,而不是简单的数学的计算
b = torch.rand(32)
# bias
b.shape
f = torch.rand(4, 32, 14, 14)
# 32 是channel
# 现在想累加 f + b,首先把 b 的dim=4,size与 f 相等
b = b.unsqueeze(1).unsqueeze(2).unsqueeze(0)
b.shape
output:
torch.Size([32])
torch.Size([1, 32, 1, 1])
下面再来看看维度删减操作
squeeze
b.shape
现在b的shape:
torch.Size([1, 32, 1, 1])
下面来挤压:
b.squeeze().shape
# squeeze不给参数即能挤压的都挤压,包括dim的size是1的
output:
torch.Size([32])
参数为0时:
b.squeeze(0).shape
output:
torch.Size([32, 1, 1])
这里本来应该挤压掉32的,但是32是不能被挤压得,所以这里按照原来得size输出:
b.squeeze(1).shape
output:
torch.Size([1, 32, 1, 1])
3.expand(改变理解方式,没有增加数据)(推荐用)/ repeat(增加了数据)
(维度扩展,改变shape)
e.p [1, 32, 1, 1] →[4, 32, 4, 32]
expand/expand_as
a = torch.rand(4, 32, 14, 14)
# b的shape是:torch.Size([1, 32, 1, 1])
b.expand(4, 32, 14, 14).shape
output:
torch.Size([4, 32, 14, 14])
调用expand的前提是,①dim一致,②对于原来是1的是可以扩张的,但是原来不为1时会报错
repeat(memory touched)
b.repeat(4,1,1,1).shape
output:
torch.Size([4, 32, 1, 1])
这里的4表示在这个维度上重复拷贝的次数
3. .t(矩阵的转置操作)
a = torch.randn(3, 4)
print(a)
a.t()
output:
tensor([[ 0.5026, 0.2715, -0.0155, 0.9671],
[-0.0519, -0.2210, 1.0061, 0.0739],
[-0.9153, 0.2358, 1.4426, 0.2461]])
tensor([[ 0.5026, -0.0519, -0.9153],
[ 0.2715, -0.2210, 0.2358],
[-0.0155, 1.0061, 1.4426],
[ 0.9671, 0.0739, 0.2461]])
转置操作只适用于2d的tensor
transport
(矩阵的维度交换操作)
a = torch.rand(4, 3, 32, 32)
a2 = a.transpose(1, 3).contiguous().view(4, 3*32*32).view(4,32,32,3).transpose(1, 3)
print(a2.shape)
torch.all(torch.eq(a, a2))
output:
torch.Size([4, 3, 32, 32])
tensor(True)
view会导致维度顺序关系变复杂,所以需要人为跟踪。
permute
b = torch.rand(4, 3, 28, 32)
# [b, c, h, w]
b.transpose(1, 3).shape
# [b, w, h, c]
# 这时列在前了,需要再交换一次
b.transpose(1, 3).transpose(1, 2).shape
# [b, h, w, c]
# 而使用permute就会简单很多
b.permute(0, 2, 3, 1).shape
output:
torch.Size([4, 28, 32, 3])