Pytorch學習手札(二)---張量的索引、切片、維度變換

張量的索引、切片、維度變換

1.張量的切片

類似於數組的切片,但是又稍微有些不同
a=torch.rand(4,3,28,28):DIM=4的張量數據a
(1)a[:2]:取第一個維度的前2個維度數據(不包括2);
(2)a[:2,:1,:,:]:取第一個維度的前兩個數據,取第2個維度的前1個數據,後兩個維度全都取到;
(3)a[:2,1:,:,:]:取第一個維度的前兩個數據,取第2個維度的第1個索引到最後索引的數據(包含1),後兩個維度全都取到;
(4)a[:2,-3:]:負號表示第2個維度上從倒數第3個數據取到最後倒數第一個數據-1(包含-3);
(5)a[:,:,0:28:2,0:28:2]:兩個冒號表示隔行取數據,一定的間隔;
(6)a[:,:,::2,::3]:兩個冒號直接寫表示從所有的數據中隔行取數據。

2.切取某個維度部分數據方法

  a.index_select(x,torch.tensor([m,n])):表示提取tensor數據a的第x個維度上的索引爲m和n的數據

3.torch.masked_select(x,mask)

  該函數主要用來選取x數據中的mask性質的數據,比如mask=x.ge(0.5)表示選出大於0.5的所有數據,並且輸出時將其轉換爲了dim=1的打平tensor數據。

4.take函數

先將張量數據打平爲一個dim=1的張量數據(依次排序下來成爲一個數據列),然後按照索引進行取數據
a=torch.tensor([[1,2,3],[4,5,6]])
torch.take(a,torch.tensor([1,2,5])):表示提取a這個tensor數據打平以後的索引爲1/2/5的數據元素
(1-2)tensor數據的維度變換
  1.對於tensor數據的維度變換主要有四大API函數:
(1)view/reshape:主要是在保證tensor數據大小不變的情況下對tensor數據進行形狀的重新定義與轉換
(2)Squeeze/unsqueeze:刪減維度或者增加維度操作
(3)transpose/t/permute:類似矩陣的轉置操作,對於多維的數據具有多次或者單次的轉換操作
(4)Expand/repeat:維度的擴展,將低維數據轉換爲高維的數據
   2.view(reshape)維度轉換操作時需要保證數據的大小numl保持不變,即數據變換前後的prod是相同的:
prod(a.size)=prod(b.size)
另外,對於view操作有一個致命的缺陷就是在數據進行維度轉換之後數據之前的存儲與維度順序信息會丟失掉,不能夠復原,而這對於訓練的數據來說非常重要。
   3.squeeze/unsqueeze擠壓和增加維度操作的函數

a=torch.rand(4,3,28,28)
a.unsqueeze(1):在a原來維度索引1之間增加一個維度
a.unsqueeze(-1):在a原來維度索引-1之後增加維度
例如:
a=torch.tensor([1.2,1.3]) #[2]
print(a.unsqueeze(0)) #[1,2]
print(a.unsqueeze(-1)) #[2,1]
a=torch.rand(4,32,28,28)
    b=torch.rand(32) #如果要實現a和數據b的疊加,則需要對於數據b進行維度擴張
    print(b.unsqueeze(1).unsqueeze(2).unsqueeze(0).shape)
   **   4.維度刪減squeeze()**
    對於維度的擠壓squeeze,主要是擠壓掉tensor數據中維度特徵數爲1的維度,如果不是1的話就不可以擠壓
b=torch.rand(32)
b=b.unsqueeze(1).unsqueeze(2).unsqueeze(0)
print(b.squeeze().shape)
print(b.squeeze(0).shape)
print(b.squeeze(1).shape)
print(b.squeeze(-1).shape)

5.維度的擴展

expand(絕對擴展)/repeat(相對擴展):
  維度的擴張expand(絕對值)/repeat,repeat擴展實質是重複拷貝的次數-相對值,並且由於拷貝操作,原來的數據不能再用,已經改變,而expand是絕對擴展,其實現只能從1擴張到n,不能從M擴張到N,另外-1表示對該維度保持不變的操作。

a=torch.rand(4,32,14,14)
b=torch.rand(32)
b=b.unsqueeze(1).unsqueeze(2).unsqueeze(0)
print(a.shape,b.shape)
print(b.expand(4,32,14,14).shape)
print(b.expand(-1,32,-1,-1).shape) #-1表示對維度保持不變
print(b.repeat(4,32,1,1).shape)
print(b.repeat(4,1,14,14).shape)

6.維度交換操作

(1).t()操作:只可以對DIM=2的矩陣進行轉置操作
(2)transpose操作:對不同的DIM均可以進行維度交換

a=torch.rand(4,3,32,32)     a1=a.transpose(1,3).contiguous().view(4,32*32*3).view(4,32,32,3).transpose(1,3)
print(a1.shape)
print(torch.all(torch.eq(a,a1)))
整體的變換順序爲a[b,c,h,w]->[b,w,h,c]->[b,w*h*c]->[b,w,h,c]->[b,c,h,w]

7.permute操作

  相比於transpose只可以進行兩個維度之間的一次交換操作,permute維度交換操作可以一步實現多個維度之間的交換(相當於transpose操作的多步操作)
  #.t()和transpose/permute維度交換操作,需要考慮數據的信息保存,不能出現數據的污染和混亂.contiguous()操作保持存儲順序不變

c=torch.rand(3,4)
print(c)
print(c.t())
a=torch.rand(4,3,32,32)
a1=a.transpose(1,3).contiguous().view(4,32*32*3).view(4,32,32,3).transpose(1,3)
print(a1.shape)
print(torch.all(torch.eq(a,a1)))
a=torch.rand(4,3,28,32)
a1=a.permute(0,2,3,1)
print(a1.shape)
a2=a.contiguous().permute(0,2,3,1)
print(torch.all(torch.eq(a1,a2)))

8.本節代碼demo彙總

# -*- coding: utf-8 -*-
'''
@Author: Jason
@Desc  : 
'''
import torch
a=torch.rand(4,3,28,28) #RGB  [size,Chanle,Hidth,Width]
# print(a)
# print(a.shape)
# print(a.dim())
# #索引與切片操作
# print(a[0].shape) #4其中1個的信息 torch.Size([3, 28, 28])
# print(a[0,0].shape) #第一張圖片的第一個通道的信息 torch.Size([28, 28])
# print(a[0,0,2,4]) #第一張圖片的第一個通道的2行4列的值 tensor(0.1269)
# print(a[:2].shape)
# print(a[:2,:1,:,:].shape) #前兩張圖片的第一個通道
# print(a[:2,1:,:,:].shape)
# print(a[:2,-3:].shape)
# print(a[:,:,0:28:2,0:28:2].shape) #高,寬隔行採樣
# print(a[:,:,::2,::3].shape)


# #選擇其中某維度的某些索引數據
# b=torch.rand(5,3,3)
# print(b)
# print(b.index_select(0,torch.tensor([1,2,4])))
# print(b.index_select(2,torch.arange(2)).shape)


# #...操作表示自動判斷其中得到維度區間
# a=torch.rand(4,3,28,28)
# print(a[...,2].shape) #這步驟不太理解  torch.Size([4, 3, 28])
# print(a[0,...,::2].shape) #第一張圖片的[3,28,14]
# print(a[...].shape) #等於a ,全取

# #msaked_select
x=torch.randn(3,4) #生成3行4列
# print(x)
'''
tensor([[-0.1629,  0.9772,  0.9447, -0.5927],
        [ 1.3970, -1.9437,  1.9602,  0.3793],
        [-0.0344, -0.8546, -0.0564,  0.1060]])
'''
mask=x.ge(0.5) #選出所有元素中大於0.5的數據
# print(mask) #值大於0.5標註爲True
'''
tensor([[False,  True,  True, False],
        [ True, False,  True, False],
        [False, False, False, False]])
'''
# print(torch.masked_select(x,mask)) #選出所有元素中大於0.5的數據,並且輸出時將其轉換爲了dim=1的打平tensor數據
# tensor([0.9772, 0.9447, 1.3970, 1.9602])

# #take函數的應用:先將張量數據打平爲一個dim=1的張量數據(依次排序下來成爲一個數據列),然後按照索引進行取數據
a=torch.tensor([[1,2,3],[4,5,6]])
# print(a)
'''
tensor([[1, 2, 3],
        [4, 5, 6]])
'''
# print(a.shape)  #torch.Size([2, 3])
b = torch.take(a,torch.tensor([1,2,5])) #先打平爲[1,2,3,4,5,6],再取值
# print(b)
#
# #tensor數據的維度變換
# #view/reshape操作:不進行額外的記住和存貯就會丟失掉原來的數據的數據和維度順序信息,而這是非常重要的
a=torch.rand(4,1,28,28)
# print(a.view(4,28*28)) #torch.Size([4, 784])的張量
b=a.view(4,28*28)
# print(b.shape) #torch.Size([4, 784])
# #squeeze/unsqueeze 擠壓和增加維度的操作
a=torch.rand(4,3,28,28)
#        正序(0,1,2,3,4(可以爲4,大於4報錯))
#      逆序(-5(同上),-4,-3,-2,-1)
# print(a)
# print(a.unsqueeze(0).shape) #unsqueeze(index)參數爲索引:torch.Size([1, 4, 3, 28, 28])
# print(a.unsqueeze(-1).shape) #最後一列增加:torch.Size([4, 3, 28, 28, 1])
# print(a.unsqueeze(-4).shape) #-4位置增加一列,原-4左移,torch.Size([4, 1, 3, 28, 28])
a=torch.tensor([1.2,1.3]) #[2]
# print(a.unsqueeze(0)) #[1,2]
# print(a.unsqueeze(-1)) #[2,1]
# a=torch.rand(4,32,28,28)
# b=torch.rand(32) #如果要實現a和數據b的疊加,則需要對於數據b進行維度擴張
# print(b.unsqueeze(1).unsqueeze(2).unsqueeze(0).shape) #[1,32,1,1]
# b=b.unsqueeze(1).unsqueeze(2).unsqueeze(0)

b = torch.rand([1,32,1,1])
# print(b.shape) #torch.Size([1, 32, 1, 1])\
# print(b.squeeze().shape) #torch.Size([32])
# print(b.squeeze(0).shape)#torch.Size([32, 1, 1])
# print(b.squeeze(-1).shape)#torch.Size([1,32, 1])
# print(b.squeeze(1).shape) #維度不爲1,擠壓不掉 torch.Size([1, 32, 1, 1])
# print(b.squeeze(-4).shape) #torch.Size([32, 1, 1])

# #維度的擴張expand(絕對值)/repeat(重複拷貝的次數-相對值,並且由於拷貝操作,原來的數據不能再用,已經改變),只能從1擴張到n,不能從M擴張到N,另外-1表示對該維度保持不變的操作
a=torch.rand(4,32,14,14)
b=torch.rand(32)
b=b.unsqueeze(1).unsqueeze(2).unsqueeze(0)
# print(a.shape,b.shape) #torch.Size([4, 32, 14, 14]) torch.Size([1, 32, 1, 1])
# print(b.expand(4,32,14,14).shape) #複製值之後變成 torch.Size([4, 32, 14, 14])
# print(b.expand(-1,32,-1,-1).shape) #-1表示對維度保持不變 torch.Size([1, 32, 1, 1])
# print(b.expand(-1,32,-1,-4).shape) #除了負1,其他負數將使該行無意義 torch.Size([1, 32, 1, -4])

# print(b.repeat(4,32,1,1).shape) #代表拷貝的次數 torch.Size([4, 1024, 1, 1])
# print(b.repeat(4,1,14,14).shape) #torch.Size([4, 32, 14, 14])

# 矩陣的轉置 和 維度交換 操作
# #.t()轉置 和 transpose/permute維度交換操作,需要考慮數據的信息保存,不能出現數據的污染和混亂.contiguous()操作保持存儲順序不變
c=torch.rand(3,4)
# print(c)
'''
tensor([[0.9533, 0.4853, 0.4694, 0.2958],
        [0.7191, 0.1076, 0.5389, 0.0039],
        [0.9428, 0.0071, 0.4543, 0.4585]])
'''
# print(c.t()) #只能適用於二維
'''
tensor([[0.9533, 0.7191, 0.9428],
        [0.4853, 0.1076, 0.0071],
        [0.4694, 0.5389, 0.4543],
        [0.2958, 0.0039, 0.4585]])
'''
a=torch.rand(4,3,32,32)
#先交換1,3維度 (b,c,h,w) 到 (b,h,c,w)==>[b,h*c*w] ==> [b,h,c,w] ==> [b,c,h,w]
a1=a.transpose(1,3).contiguous().view(4,32*32*3).view(4,32,32,3).transpose(1,3)
# print(a1.shape) #torch.Size([4, 3, 32, 32])
# print(torch.all(torch.eq(a,a1))) #查看a和a1的是否一致 tensor(True)


a=torch.rand(4,3,28,32)
a1=a.permute(0,2,3,1)
print(a1.shape) #torch.Size([4, 28, 32, 3])
# a2=a.contiguous().permute(0,2,3,1)
# print(torch.all(torch.eq(a1,a2)))

if __name__ == "__main__":
    print(" ")

轉載請標明轉自:https://leejason.blog.csdn.net/article/details/106865689

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章