捷徑

TVTensors 常見問題

注意

Colab 上試用,或前往結尾下載完整的範例程式碼。

TVTensors 是與 torchvision.transforms.v2 一起引入的 Tensor 子類別。此範例展示了這些 TVTensors 是什麼以及它們的行為方式。

警告

目標受眾 除非您正在編寫自己的轉換或自己的 TVTensors,否則您可能不需要閱讀本指南。這是一個相當底層的主題,大多數使用者不需要擔心:您不需要了解 TVTensors 的內部運作方式,即可有效率地依賴 torchvision.transforms.v2。然而,對於嘗試實作自己的資料集、轉換或直接使用 TVTensors 的進階使用者來說,這可能會很有用。

import PIL.Image

import torch
from torchvision import tv_tensors

什麼是 TVTensors?

TVTensors 是零複製張量子類別

tensor = torch.rand(3, 256, 256)
image = tv_tensors.Image(tensor)

assert isinstance(image, torch.Tensor)
assert image.data_ptr() == tensor.data_ptr()

在底層,torchvision.transforms.v2 中需要它們來正確地為輸入資料調度到適當的函式。

torchvision.tv_tensors 支援四種類型的 TVTensors

我可以使用 TVTensor 做什麼?

TVTensors 看起來和感覺起來就像一般的張量 - 它們就是張量。在普通的 torch.Tensor 上支援的所有功能,例如 .sum() 或任何 torch.* 運算子,也適用於 TVTensors。請參閱 我原本有一個 TVTensor,但現在變成 Tensor 了。救命啊! 以了解一些注意事項。

我該如何建構 TVTensor?

使用建構函式

每個 TVTensor 類別都接受任何可以轉換為 Tensor 的類張量資料

image = tv_tensors.Image([[[[0, 1], [1, 0]]]])
print(image)
Image([[[[0, 1],
         [1, 0]]]], )

與其他 PyTorch 建立運算類似,建構函式也接受 dtypedevicerequires_grad 參數。

float_image = tv_tensors.Image([[[0, 1], [1, 0]]], dtype=torch.float32, requires_grad=True)
print(float_image)
Image([[[0., 1.],
        [1., 0.]]], grad_fn=<AliasBackward0>, )

此外,ImageMask 也可以直接接受 PIL.Image.Image

image = tv_tensors.Image(PIL.Image.open("../assets/astronaut.jpg"))
print(image.shape, image.dtype)
torch.Size([3, 512, 512]) torch.uint8

有些 TVTensors 需要傳入額外的中繼資料才能建構。例如,BoundingBoxes 除了實際值之外,還需要座標格式以及對應影像的大小 (canvas_size)。這些中繼資料是正確轉換邊界框所必需的。

bboxes = tv_tensors.BoundingBoxes(
    [[17, 16, 344, 495], [0, 10, 0, 10]],
    format=tv_tensors.BoundingBoxFormat.XYXY,
    canvas_size=image.shape[-2:]
)
print(bboxes)
BoundingBoxes([[ 17,  16, 344, 495],
               [  0,  10,   0,  10]], format=BoundingBoxFormat.XYXY, canvas_size=torch.Size([512, 512]))

使用 tv_tensors.wrap()

您也可以使用 wrap() 函式將張量物件包裝成 TVTensor。當您已經擁有所需類型的物件時,這非常有用,這通常發生在編寫轉換時:您只是想將輸出包裝得像輸入一樣。

new_bboxes = torch.tensor([0, 20, 30, 40])
new_bboxes = tv_tensors.wrap(new_bboxes, like=bboxes)
assert isinstance(new_bboxes, tv_tensors.BoundingBoxes)
assert new_bboxes.canvas_size == bboxes.canvas_size

new_bboxes 的中繼資料與 bboxes 相同,但您可以將其作為參數傳遞以覆寫它。

我原本有一個 TVTensor,但現在變成 Tensor 了。救命啊!

預設情況下,對 TVTensor 物件的操作將傳回純 Tensor

assert isinstance(bboxes, tv_tensors.BoundingBoxes)

# Shift bboxes by 3 pixels in both H and W
new_bboxes = bboxes + 3

assert isinstance(new_bboxes, torch.Tensor)
assert not isinstance(new_bboxes, tv_tensors.BoundingBoxes)

注意

此行為僅影響原生 torch 運算。如果您使用的是內建 torchvision 轉換或函式,您將始終獲得與您傳遞的輸入類型相同的輸出(純 TensorTVTensor)。

但我想要回傳 TVTensor!

您可以透過呼叫 TVTensor 建構函式,或使用 wrap() 函式(請參閱上方 我該如何建構 TVTensor? 中的更多詳細資訊)將純張量重新包裝成 TVTensor

new_bboxes = bboxes + 3
new_bboxes = tv_tensors.wrap(new_bboxes, like=bboxes)
assert isinstance(new_bboxes, tv_tensors.BoundingBoxes)

或者,您可以使用 set_return_type() 作為整個程式的全域組態設定,或作為上下文管理器(閱讀其文件以了解更多關於注意事項的資訊)

with tv_tensors.set_return_type("TVTensor"):
    new_bboxes = bboxes + 3
assert isinstance(new_bboxes, tv_tensors.BoundingBoxes)

為什麼會這樣?

為了效能考量TVTensor 類別是 Tensor 子類別,因此任何涉及 TVTensor 物件的操作都將通過 __torch_function__ 協定。這會產生少量額外負擔,我們希望盡可能避免這種情況。這對於內建 torchvision 轉換來說沒問題,因為我們可以在那裡避免額外負擔,但在您模型的 forward 中可能會成為問題。

無論如何,替代方案也好不到哪裡去。 對於每個保留 TVTensor 類型有意義的操作,也有許多操作更適合傳回純 Tensor:例如,img.sum() 仍然是 Image 嗎?如果我們一直保留 TVTensor 類型,即使模型的 logits 或損失函數的輸出最終也會是 Image 類型,而這肯定不是我們所希望的。

注意

這種行為是我們積極尋求回饋的內容。如果您覺得這令人驚訝,或者您對如何更好地支援您的使用案例有任何建議,請透過此問題與我們聯繫:https://github.com/pytorch/vision/issues/7319

例外情況

此「解包裝」規則有一些例外情況:clone()to()torch.Tensor.detach()requires_grad_() 保留 TVTensor 類型。

對 TVTensors 進行原地操作(例如 obj.add_())將保留 obj 的類型。但是,原地操作的回傳值將是純張量

image = tv_tensors.Image([[[0, 1], [1, 0]]])

new_image = image.add_(1).mul_(2)

# image got transformed in-place and is still a TVTensor Image, but new_image
# is a Tensor. They share the same underlying data and they're equal, just
# different classes.
assert isinstance(image, tv_tensors.Image)
print(image)

assert isinstance(new_image, torch.Tensor) and not isinstance(new_image, tv_tensors.Image)
assert (new_image == image).all()
assert new_image.data_ptr() == image.data_ptr()
Image([[[2, 4],
        [4, 2]]], )

腳本總執行時間: (0 分鐘 0.009 秒)

由 Sphinx-Gallery 產生圖庫

文件

存取 PyTorch 的完整開發者文件

檢視文件

教學

取得針對初學者和進階開發者的深入教學

檢視教學

資源

尋找開發資源並獲得問題解答

檢視資源