1. 數據類型
根據官方文檔顯示,張量定義為包含單一數據類型元素的多維矩陣。
在Pytorch中,有9種CPU張量類型和9種GPU張量類型。具體類型如下圖所示:
在Pytorch中,可以通過Python 列表和torch.tensor()構造函數構造一個張量。
torch.tensor([[1., -1.], [1., -1.]])
torch.tensor(np.array([[1, 2, 3], [4, 5, 6]]))
2. 張量的基本信息
tensor = torch.randn(2,3,4)
print(tensor.type()) # 數據類型 torch.FloatTensor,是一個浮點型的張量
print(tensor.size()) # 張量的shape,是個元組 torch.Size([2, 3, 4])
print(tensor.dim()) # 維度的數量 3
3. 張量的命名
程序中,一個好的命名可以便於其他人讀懂代碼,張量的命名也是如此。這樣可以方便地使用維度的名字來做索引或其他操作,提高了可讀性、易用性,防止程序出錯,便於其他人閱讀和修改。
# 在PyTorch 1.3之前,需要使用注釋
# Tensor[N, C, H, W]
images = torch.randn(32, 3, 56, 56)
images.sum(dim=1)
images.select(dim=1, index=0)
# PyTorch 1.3之後
NCHW = [『N』, 『C』, 『H』, 『W』]
images = torch.randn(32, 3, 56, 56, names=NCHW)
images.sum('C')
images.select('C', index=0)
# 也可以這麼設置
tensor = torch.rand(3,4,1,2,names=('C', 'N', 'H', 'W'))
# 使用align_to可以對維度方便地排序
tensor = tensor.align_to('N', 'C', 'H', 'W')
4.張量數據類型轉換
在Pytorch中,FloatTensor處理速度遠遠快於DoubleTensor,因此默認採用FloatTensor,也可以通過轉換變成其他類型的數據。
# 設置默認類型
torch.set_default_tensor_type(torch.FloatTensor)
# 類型轉換
tensor = tensor.cuda()
tensor = tensor.cpu()
tensor = tensor.float()
tensor = tensor.long()
4.1 torch.Tensor與np.ndarray轉換
除了CharTensor類型外,其他所有CPU上的張量都支持轉換為numpy格式,當然也可以再轉換回來。
ndarray = tensor.cpu().numpy()
tensor = torch.from_numpy(ndarray).float()
tensor = torch.from_numpy(ndarray.copy()).float() # If ndarray has negative stride.
4.2 torch.tensor與PIL.Image轉換
在Pytorch中,張量默認採用[N, C, H, W]的順序,並且數據範圍在[0,1],有時候處理數據時需要進行轉置和規範化。
# torch.Tensor -> PIL.Image
image = PIL.Image.fromarray(torch.clamp(tensor*255, min=0, max=255).byte().permute(1,2,0).cpu().numpy())
image = torchvision.transforms.functional.to_pil_image(tensor)
# PIL.Image -> torch.Tensor
path = r'./figure.jpg'
tensor = torch.from_numpy(np.asarray(PIL.Image.open(path))).permute(2,0,1).float() / 255
tensor = torchvision.transforms.functional.to_tensor(PIL.Image.open(path)) # Equivalently way
5.張量的常用操作
5.1 矩陣乘法
# Matrix multiplcation: (m*n) * (n*p) * -> (m*p).
result = torch.mm(tensor1, tensor2)
# Batch matrix multiplication: (b*m*n) * (b*n*p) -> (b*m*p)
result = torch.bmm(tensor1, tensor2)
# Element-wise multiplication.
result = tensor1 * tensor2
5.2 計算兩組數據之間的兩兩歐式距離
dist = torch.sqrt(torch.sum((X1[:,None,:] - X2) ** 2, dim=2))
5.3 張量形變
將卷積層輸入全連接層的情況時,通常需要對張量做形變處理如.view()和.reshape()等,但是相比torch.view,torch.reshape可以自動處理輸入張量不連續的情況。
tensor = torch.rand(2,3,4)
shape = (6, 4)
tensor = torch.reshape(tensor, shape)
5.4 打亂順序
tensor = tensor[torch.randperm(tensor.size(0))] # 打亂第一個維度
5.5 水平翻轉
Pytorch不支持tensor[::-1]這樣的負步長操作,水平翻轉可以通過張量索引實現。
# 假設張量的維度為[N, D, H, W].
tensor = tensor[:, :, :, torch.arange(tensor.size(3) - 1, -1, -1).long()]
5.6 張量複製
tensor.clone()
tensor.detach()
tensor.detach.clone()
5.7 張量拼接
torch.cat和torch.stack的區別在於torch.cat沿著給定的維度拼接,而torch.stack會新增一維。當參數是3個10x5的張量,torch.cat的結果是30x5的張量,而torch.stack的結果是3x10x5的張量。
tensor = torch.cat(list_of_tensors, dim=0)
tensor = torch.stack(list_of_tensors, dim=0)
5.8 將整數標籤轉為one-hot編碼
Pytorch的標記默認從0開始,轉換為one-hot編碼在數據處理時也經常用到。
tensor = torch.tensor([0, 2, 1, 3])
N = tensor.size(0)
num_classes = 4
one_hot = torch.zeros(N, num_classes).long()
one_hot.scatter_(dim=1, index=torch.unsqueeze(tensor, dim=1), src=torch.ones(N, num_classes).long())
5.9 得到非零元素
torch.nonzero(tensor) # index of non-zero elements,索引非零元素
torch.nonzero(tensor==0) # index of zero elements,索引零元素
torch.nonzero(tensor).size(0) # number of non-zero elements,非零元素的個數
torch.nonzero(tensor == 0).size(0) # number of zero elements,零元素的個數
5.10 張量擴展
將64*512的張量擴展為64*512*7*7的張量
tensor = torch.rand(64,512)
torch.reshape(tensor, (64, 512, 1, 1)).expand(64, 512, 7, 7)
未完待續
如有遺漏或在錯誤的地方,希望大家指出;如果還有大家認為很重要的張量操作,也麻煩大家指出,互相進步,不甚感激!