本篇主要记录一下一些基础的pytorch知识,接上一篇文章的tensor基础,维度变换的相关的常用api

常用api

·View/reshape(改变维度)

# view 和 reshape 功能相同 会造成维度信息丢失,信息污染
a = torch.randn(4, 1, 28, 28)
# view要保证前后的 元素 个数一致
a.view(4, 28*28)
'''
a.shape
Out[24]: torch.Size([4, 1, 28, 28])
a.view(4, 28*28).shape
Out[25]: torch.Size([4, 784])
'''
a.view(4*28, 28)
'''
a.view(4*28, 28).shape
Out[26]: torch.Size([112, 28])
'''  
```  
#### ·Squeeze/unsqueeze(增减维度)  
  **注意,可插入的维度范围(以4维的tensor为例): [-5,5)**  
``` python  
# 2. Squeeze unsqueeze
# 2.1 unsqueeze
a = torch.randn(4, 1, 28, 28)
# 在0维之前插入一个维度
a.unsqueeze(0).shape
# 在-1维度(最后一个维度之后插入一个维度)
a.unsqueeze(-1).shape
a.unsqueeze(4).shape
a.unsqueeze(-5).shape
'''
a.shape
Out[31]: torch.Size([4, 1, 28, 28])
a.unsqueeze(0).shape
Out[27]: torch.Size([1, 4, 1, 28, 28])
a.unsqueeze(-1).shape
Out[28]: torch.Size([4, 1, 28, 28, 1])
a.unsqueeze(4).shape
Out[29]: torch.Size([4, 1, 28, 28, 1])
a.unsqueeze(-5).shape
Out[30]: torch.Size([1, 4, 1, 28, 28])
'''
# 一个例子
a = torch.tensor([1.2, 3.4])
a.unsqueeze(-1)
a.unsqueeze(-1).shape
a.unsqueeze(0)
a.unsqueeze(0).shape
'''
a = torch.tensor([1.2, 3.4])
a.unsqueeze(-1)
Out[33]: 
tensor([[1.2000],
        [3.4000]])
a.unsqueeze(-1).shape
Out[35]: torch.Size([2, 1])
a.unsqueeze(0)
Out[34]: tensor([[1.2000, 3.4000]])
a.unsqueeze(0).shape
Out[36]: torch.Size([1, 2])
'''
# 一个实际的例子
# 给一个bias, 相当于给每一个channel上的所有像素点增加一个偏置
b = torch.rand(32)
f = torch.rand(4, 32, 14, 14)
# 从左往右执行
b = b.unsqueeze(1).unsqueeze(2).unsqueeze(0)
b.shape
'''
b = torch.rand(32)
b.shape
Out[38]: torch.Size([32])
f = torch.rand(4, 32, 14, 14)
b = b.unsqueeze(1).unsqueeze(2).unsqueeze(0)
b.shape
Out[42]: torch.Size([1, 32, 1, 1])
'''
# 2.2 squeeze
b = torch.rand(1, 32, 1, 1)
b.shape
b.squeeze().shape
b.squeeze(0).shape
b.squeeze(-4).shape
# 注意 如果维数不是1,则不能挤压成功(不会报错)
b.squeeze(1).shape
'''
b.squeeze().shape
Out[43]: torch.Size([32])
b.squeeze(0).shape
Out[44]: torch.Size([32, 1, 1])
b.squeeze(-4).shape
Out[45]: torch.Size([32, 1, 1])
b.squeeze(1).shape
Out[5]: torch.Size([1, 32, 1, 1])
'''  

·Transpose/t/permute(矩阵的转置)

# 4 矩阵的转置
# 4.1 t()
a = torch.rand(3, 4)
a
# 对a作转置 需要注意的是 .t()只能用于2d的tensor(即矩阵)
a.t()
'''
a = torch.rand(3, 4)
a
Out[17]: 
tensor([[0.4887, 0.5217, 0.5592, 0.8425],
        [0.6659, 0.9403, 0.5832, 0.4546],
        [0.9414, 0.2914, 0.2852, 0.9977]])
a.t()
Out[18]: 
tensor([[0.4887, 0.6659, 0.9414],
        [0.5217, 0.9403, 0.2914],
        [0.5592, 0.5832, 0.2852],
        [0.8425, 0.4546, 0.9977]])
'''
# 4.2 transpose() 更通用的方法
a = torch.rand(4, 3, 32, 32)
# [b,c,h,w] => [b,w,h,c] => [b, w*c*h] => [b, c, w, h]
# 这里体现了view对维度信息的损失,会导致数据污染,要追踪数据
a1 = a.transpose(1, 3).contiguous().view(4, 3*32*32).view(4, 3, 32, 32)
# [b,c,h,w] => [b,w,h,c] => [b, w*c*h] => [b, c, w, h] => [b,c,h,w]
a2 = a.transpose(1, 3).contiguous().view(4, 3*32*32).view(4, 32, 32, 3).transpose(1, 3)
# 此处a1, a2的shape都是一样的,但是只有a2和a是一样的
a1.shape, a2.shape
torch.all(torch.eq(a, a1))
torch.all(torch.eq(a, a2))
'''
a = torch.rand(4, 3, 32, 32)
a1 = a.transpose(1, 3).contiguous().view(4, 3*32*32).view(4, 3, 32, 32)
a2 = a.transpose(1, 3).contiguous().view(4, 3*32*32).view(4, 32, 32, 3).transpose(1, 3)
a1.shape, a2.shape
Out[41]: (torch.Size([4, 3, 32, 32]), torch.Size([4, 3, 32, 32]))
torch.all(torch.eq(a, a1))
Out[43]: tensor(False)
torch.all(torch.eq(a, a2))
Out[44]: tensor(True)
'''
# 4.3 permute() 更方便 相对于transpose一次只能两两交换
# tip [b,c,h,w] => [b,h,w,c] 后者是numpy中的图片存储形式,需要这一步才能导出numpy
b = torch.rand(4, 3, 28, 32)
b.transpose(1, 3).shape
b.transpose(1, 3).transpose(1, 2).shape
b.permute(0, 2, 3, 1).shape
'''
b = torch.rand(4, 3, 28, 32)
b.transpose(1, 3).shape
Out[48]: torch.Size([4, 32, 28, 3])
b.transpose(1, 3).transpose(1, 2).shape
Out[49]: torch.Size([4, 28, 32, 3])
b.permute(0, 2, 3, 1).shape
Out[50]: torch.Size([4, 28, 32, 3])
'''  

```  

#### ·Expand/repeat(维度的扩展)  
``` python  
# 3. Expand / repeat 维度扩展(或者说是[]里面值的大小而不是个数的扩展)
# expand: 不增加数据 repeat: 复制数据 推荐第一种
# 3.1 expand
a = torch.rand(4, 32, 14, 14)
b = torch.rand(1, 32, 1, 1)
a.shape
b.shape
# 仅限于1 -> N 的时候可以正确执行,对如 3 -> N 的类似操作则会报错
b.expand(4, 32, 14, 14).shape
# -1表示这个维度不作改变
b.expand(-1, 32, -1, -1).shape
# 这是个bug 最新版本被修复了,我实际运行结果还是-4(这个结果没有任何意义)
b.expand(-1, 32, -1, -4).shape
'''
a = torch.rand(4, 32, 14, 14)
b = torch.rand(1, 32, 1, 1)
a.shape
Out[7]: torch.Size([4, 32, 14, 14])
b.shape
Out[8]: torch.Size([1, 32, 1, 1])
b.expand(4, 32, 14, 14).shape
Out[9]: torch.Size([4, 32, 14, 14])
b.expand(-1, 32, -1, -1).shape
Out[10]: torch.Size([1, 32, 1, 1])
b.expand(-1, 32, -1, -4).shape
Out[11]: torch.Size([1, 32, 1, -4])
'''
# 3.2 repeat
# 这里的参数意义有些不一样,参数不再是新的shape,而是要拷贝的次数,即new.shape = old.shape * 参数
b.repeat(4, 32, 1, 1).shape
b.repeat(4, 1, 1, 1).shape

'''
b.repeat(4, 32, 1, 1).shape
Out[14]: torch.Size([4, 1024, 1, 1])
b.repeat(4, 1, 1, 1).shape
Out[15]: torch.Size([4, 32, 1, 1])
'''