張量(Tensor)、標量(scalar)、向量(vector)、矩陣(matrix)
飛槳 使用張量(Tensor) 來表示神經網路中傳遞的資料,Tensor 可以理解為多維陣列,類似于 Numpy 陣列(ndarray) 的概念,與 Numpy 陣列相比,Tensor 除了支持運行在 CPU 上,還支持運行在 GPU 及各種 AI 芯片上,以實作計算加速;此外,飛槳基于 Tensor,實作了深度學習所必須的反向傳播功能和多種多樣的組網算子,從而可更快捷地實作深度學習組網與訓練等功能,
Tensor 必須形如矩形,即在任何一個維度上,元素的數量必須相等,否則會拋出例外
Tensor 的創建
指定資料創建
import paddle
# 創建類似向量(vector)的 1 維 Tensor:
ndim_1_Tensor = paddle.to_tensor([2.0, 3.0, 4.0])
print(ndim_1_Tensor)
# 創建類似矩陣(matrix)的 2 維 Tensor:
ndim_2_Tensor = paddle.to_tensor([[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0]])
print(ndim_2_Tensor)
# 創建 3 維 Tensor:
ndim_3_Tensor = paddle.to_tensor([[[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10]],
[[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20]]])
print(ndim_3_Tensor)
輸出
"D:\Program Files\Python38\python.exe" D:/OpenSource/PaddlePaddle/Tensor.py
Tensor(shape=[3], dtype=float32, place=Place(cpu), stop_gradient=True,
[2., 3., 4.])
Tensor(shape=[2, 3], dtype=float32, place=Place(cpu), stop_gradient=True,
[[1., 2., 3.],
[4., 5., 6.]])
Tensor(shape=[2, 2, 5], dtype=int64, place=Place(cpu), stop_gradient=True,
[[[1 , 2 , 3 , 4 , 5 ],
[6 , 7 , 8 , 9 , 10]],
[[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20]]])
Process finished with exit code 0

指定形狀創建
paddle.zeros([m, n]) # 創建資料全為 0,形狀為 [m, n] 的 Tensor
paddle.ones([m, n]) # 創建資料全為 1,形狀為 [m, n] 的 Tensor
paddle.full([m, n], 10) # 創建資料全為 10,形狀為 [m, n] 的 Tensor
paddle.ones([2,3])
輸出
Tensor(shape=[2, 3], dtype=float32, place=Place(gpu:0), stop_gradient=True,
[[1., 1., 1.],
[1., 1., 1.]])
指定區間創建
如果要在指定區間內創建 Tensor,可以使用paddle.arange、 paddle.linspace 實作,
paddle.arange(start, end, step) # 創建以步長 step 均勻分隔區間[start, end)的 Tensor
paddle.linspace(start, stop, num) # 創建以元素個數 num 均勻分隔區間[start, stop)的 Tensor
paddle.arange(start=1, end=5, step=1)
輸出
Tensor(shape=[4], dtype=int64, place=Place(gpu:0), stop_gradient=True,
[1, 2, 3, 4])
指定影像、文本資料創建
在常見深度學習任務中,資料樣本可能是圖片(image)、文本(text)、語音(audio)等多種型別,在送入神經網路訓練或推理前,這些資料和對應的標簽均需要創建為 Tensor,以下是影像場景和 NLP 場景中手動轉換 Tensor 方法的介紹,
- 對于影像場景,可使用 paddle.vision.transforms.ToTensor 直接將 PIL.Image 格式的資料轉為 Tensor,使用 paddle.to_tensor 將影像的標簽(Label,通常是 Python 或 Numpy 格式的資料)轉為 Tensor,
- 對于文本場景,需將文本資料解碼為數字后,再通過 paddle.to_tensor 轉為 Tensor,不同文本任務標簽形式不一樣,有的任務標簽也是文本,有的則是數字,均需最終通過 paddle.to_tensor 轉為 Tensor,
下面以影像場景為例介紹,以下示例代碼中將隨機生成的圖片轉換為 Tensor,
import numpy as np
from PIL import Image
import paddle.vision.transforms as T
import paddle.vision.transforms.functional as F
fake_img = Image.fromarray((np.random.rand(224, 224, 3) * 255.).astype(np.uint8)) # 創建隨機圖片
transform = T.ToTensor()
tensor = transform(fake_img) # 使用 ToTensor()將圖片轉換為 Tensor
print(tensor)
自動創建 Tensor 的功能介紹
除了手動創建 Tensor 外,實際在飛槳框架中有一些 API 封裝了 Tensor 創建的操作,從而無需用戶手動創建 Tensor,例如 paddle.io.DataLoader 能夠基于原始 Dataset,回傳讀取 Dataset 資料的迭代器,迭代器回傳的資料中的每個元素都是一個 Tensor,另外在一些高層 API,如 paddle.Model.fit 、paddle.Model.predict ,如果傳入的資料不是 Tensor,會自動轉為 Tensor 再進行模型訓練或推理,
paddle.Model.fit、paddle.Model.predict 等高層 API 支持傳入 Dataset 或 DataLoader,如果傳入的是 Dataset,那么會用 DataLoader 封裝轉為 Tensor 資料;如果傳入的是 DataLoader,則直接從 DataLoader 迭代讀取 Tensor 資料送入模型訓練或推理,因此即使沒有寫將資料轉為 Tensor 的代碼,也能正常執行,提升了編程效率和容錯性,
以下示例代碼中,分別列印了原始資料集的資料,和送入 DataLoader 后回傳的資料,可以看到資料結構由 Python list 轉為了 Tensor,
import paddle
from paddle.vision.transforms import Compose, Normalize
transform = Compose([Normalize(mean=[127.5],
std=[127.5],
data_format='CHW')])
test_dataset = paddle.vision.datasets.MNIST(mode='test', transform=transform)
print(test_dataset[0][1]) # 列印原始資料集的第一個資料的 label
loader = paddle.io.DataLoader(test_dataset)
for data in enumerate(loader):
x, label = data[1]
print(label) # 列印由 DataLoader 回傳的迭代器中的第一個資料的 label
break
Tensor 的屬性
在前文中,可以看到列印 Tensor 時有 shape、dtype、place 等資訊,這些都是 Tensor 的重要屬性,想要了解如何操作 Tensor 需要對其屬性有一定了解,接下來分別展開介紹 Tensor 的屬性相關概念,
Tensor(shape=[3], dtype=float32, place=Place(gpu:0), stop_gradient=True,
[2., 3., 4.])
Tensor 的形狀(shape)
(1)形狀的介紹
形狀是 Tensor 的一個重要的基礎屬性,可以通過 Tensor.shape 查看一個 Tensor 的形狀,以下為相關概念:
- shape:描述了 Tensor 每個維度上元素的數量,
- ndim: Tensor 的維度數量,例如向量的維度為 1,矩陣的維度為 2,Tensor 可以有任意數量的維度,
- axis 或者 dimension:Tensor 的軸,即某個特定的維度,
- size:Tensor 中全部元素的個數,
創建 1 個四維 Tensor ,并通過圖形來直觀表達以上幾個概念之間的關系:
ndim_4_Tensor = paddle.ones([2, 3, 4, 5])
print("Data Type of every element:", ndim_4_Tensor.dtype)
print("Number of dimensions:", ndim_4_Tensor.ndim)
print("Shape of Tensor:", ndim_4_Tensor.shape)
print("Elements number along axis 0 of Tensor:", ndim_4_Tensor.shape[0])
print("Elements number along the last axis of Tensor:", ndim_4_Tensor.shape[-1])

(2)重置 Tensor 形狀(Reshape) 的方法
重新設定 Tensor 的 shape 在深度學習任務中比較常見,如一些計算類 API 會對輸入資料有特定的形狀要求,這時可通過 paddle.reshape 介面來改變 Tensor 的 shape,但并不改變 Tensor 的 size 和其中的元素資料,
以下示例代碼中,創建 1 個 shape=[3] 的一維 Tensor,使用 reshape 功能將該 Tensor 重置為 shape=[1, 3] 的二維 Tensor,這種做法經常用在把一維的標簽(label)資料擴展為二維,由于飛槳框架中神經網路通常需要傳入一個 batch 的資料進行計算,因此可將資料增加一個 batch 維,方便后面的資料計算,
ndim_1_Tensor = paddle.to_tensor([1, 2, 3])
print("the shape of ndim_1_Tensor:", ndim_1_Tensor.shape)
reshape_Tensor = paddle.reshape(ndim_1_Tensor, [1, 3])
print("After reshape:", reshape_Tensor.shape)
在指定新的 shape 時存在一些技巧:
- -1 表示這個維度的值是從 Tensor 的元素總數和剩余維度自動推斷出來的,因此,有且只有一個維度可以被設定為 -1,
- 0 表示該維度的元素數量與原值相同,因此 shape 中 0 的索引值必須小于 Tensor 的維度(索引值從 0 開始計,如第 1 維的索引值是 0,第二維的索引值是 1),
origin:[3, 2, 5] reshape:[3, 10] actual: [3, 10] # 直接指定目標 shape
origin:[3, 2, 5] reshape:[-1] actual: [30] # 轉換為 1 維,維度根據元素總數推斷出來是 3*2*5=30
origin:[3, 2, 5] reshape:[-1, 5] actual: [6, 5] # 轉換為 2 維,固定一個維度 5,另一個維度根據元素總數推斷出來是 30÷5=6
origin:[3, 2, 5] reshape:[0, -1] actual: [3, 6] # reshape:[0, -1]中 0 的索引值為 0,按照規則,轉換后第 0 維的元素數量與原始 Tensor 第 0 維的元素數量相同,為 3;第 1 維的元素數量根據元素總值計算得出為 30÷3=10,
origin:[3, 2] reshape:[3, 1, 0] error: # reshape:[3, 1, 0]中 0 的索引值為 2,但原 Tensor 只有 2 維,無法找到與第 3 維對應的元素數量,因此出錯,
(3)原位(Inplace)操作和非原位操作的區別
飛槳框架的 API 有原位(Inplace)操作和非原位操作之分,原位操作即在原 Tensor 上保存操作結果,輸出 Tensor 將與輸入 Tensor 共享資料,并且沒有 Tensor 資料拷貝的程序,非原位操作則不會修改原 Tensor,而是回傳一個新的 Tensor,通過 API 名稱區分兩者,如 paddle.reshape 是非原位操作,paddle.reshape_ 是原位操作,
Tensor 的資料型別(dtype)
(1)指定資料型別的介紹
Tensor 的資料型別 dtype 可以通過 Tensor.dtype 查看,支持型別包括:bool、float16、float32、float64、uint8、int8、int16、int32、int64、complex64、complex128,
同一 Tensor 中所有元素的資料型別均相同,通常通過如下方式指定:
(2)修改資料型別的方法
飛槳框架提供了paddle.cast 介面來改變 Tensor 的 dtype:
float32_Tensor = paddle.to_tensor(1.0)
float64_Tensor = paddle.cast(float32_Tensor, dtype='float64')
print("Tensor after cast to float64:", float64_Tensor.dtype)
int64_Tensor = paddle.cast(float32_Tensor, dtype='int64')
print("Tensor after cast to int64:", int64_Tensor.dtype)
Tensor 的設備位置(place)
Tensor 的名稱(name)
Tensor 的 stop_gradient 屬性
Tensor 的操作
索引和切片
索引或切片的第一個值對應第 0 維,第二個值對應第 1 維,依次類推,如果某個維度上未指定索引,則默認為 :
Python Numpy 切片
import paddle
# 創建 3 維 Tensor:
ndim_3_Tensor = paddle.to_tensor([[[[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10],
[11, 12, 13, 14, 15]],
[[21, 22, 23, 24, 25],
[26, 27, 28, 29, 30],
[31, 32, 33, 34, 35]]],
[[[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10],
[11, 12, 13, 14, 15]],
[[29, 22, 23, 24, 25],
[26, 57, 28, 29, 30],
[31, 32, 33, 34, 59]]]])
print("Origin Tensor:", ndim_3_Tensor.ndim)
print("Tensor Shape:", ndim_3_Tensor.shape) # [2, 2, 3, 5]
print("Slice:", ndim_3_Tensor[1, 1, 2, 4].numpy()) # 對應 shape
print("First row:", ndim_3_Tensor[0].numpy())
print("First row:", ndim_3_Tensor[1, 1, 2, 4].numpy())
print("First column:", ndim_3_Tensor[:, 0].numpy())
print("Last column:", ndim_3_Tensor[:, -1].numpy())
print("All element:", ndim_3_Tensor[:].numpy())
print("First row and second column:", ndim_3_Tensor[1, 0].numpy())
運算
Tensor 的廣播機制
飛槳 Tensor 的廣播機制
Python NumPy 廣播(Broadcast)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/552223.html
標籤:其他
下一篇:返回列表
