libtorch (pytorch c++) 教程(二)


本章講述張量的常見操作,可以先初步了解,具體在設計損失函數等任務中可以用到。隨時翻閱

張量初始化

libtorch(pytorch c++)的大多數api和pytorch保持一致,因此,libtorch中張量的初始化也和pytorch中的類似。本文介紹四種深度圖像編程需要的初始化方法。
第一種,固定尺寸和值的初始化。

//常見固定值的初始化方式
auto b = torch::zeros({3,4});
b = torch::ones({3,4});
b= torch::eye(4);
b = torch::full({3,4},10);
b = torch::tensor({33,22,11});

pytorch中用[]表示尺寸,而cpp中用{}表示。zeros產生值全為0的張量。ones產生值全為1的張量。eye產生單位矩陣張量。full產生指定值和尺寸的張量。torch::tensor({})也可以產生張量,效果和pytorch的torch.Tensor([])或者torch.tensor([])一樣。

第二種,固定尺寸,隨機值的初始化方法

//隨機初始化
auto r = torch::rand({3,4});
r = torch::randn({3, 4});
r = torch::randint(0, 4,{3,3});

rand產生0-1之間的隨機值,randn取正態分布N(0,1)的隨機值,randint取[min,max)的隨機整型數值。

第三種,從c++的其他數據類型轉換而來

int aa[10] = {3,4,6};
std::vector<float> aaaa = {3,4,6};
auto aaaaa = torch::from_blob(aa,{3},torch::kFloat);
auto aaa = torch::from_blob(aaaa.data(),{3},torch::kFloat);

pytorch可以接受從其他數據類型如numpy和list的數據轉化成張量。libtorch同樣可以接受其他數據指針,通過from_blob函數即可轉換。這個方式在部署中經常用到,如果圖像是opencv加載的,那么可以通過from_blob將圖像指針轉成張量。

第四種,根據已有張量初始化

auto b = torch::zeros({3,4});
auto d = torch::Tensor(b);
d = torch::zeros_like(b);
d = torch::ones_like(b);
d = torch::rand_like(b,torch::kFloat);
d = b.clone();

這里,auto d = torch::Tensor(b)等價於auto d = b,兩者初始化的張量d均受原張量b的影響,b中的值發生改變,d也將發生改變,但是b如果只是張量變形,d卻不會跟着變形,仍舊保持初始化時的形狀,這種表現稱為淺拷貝。zeros_like和ones_like顧名思義將產生和原張量b相同形狀的0張量和1張量,randlike同理。最后一個clone函數則是完全拷貝成一個新的張量,原張量b的變化不會影響d,這被稱作深拷貝。

張量變形

torch改變張量形狀,不改變張量存儲的data指針指向的內容,只改變張量的取數方式。libtorch的變形方式和pytorch一致,有view,transpose,reshape,permute等常用變形。

auto b = torch::full({10},3);
b.view({1, 2,-1});
std::cout<<b;
b = b.view({1, 2,-1});
std::cout<<b;
auto c = b.transpose(0,1);
std::cout<<c;
auto d = b.reshape({1,1,-1});
std::cout<<d;
auto e = b.permute({1,0,2});
std::cout<<e;

.view不是inplace操作,需要加=。變形操作沒太多要說的,和pytorch一樣。還有squeeze和unsqueeze操作,也與pytorch相同。

張量截取

通過索引截取張量,代碼如下

auto b = torch::rand({10,3,28,28});
std::cout<<b[0].sizes();//第0張照片
std::cout<<b[0][0].sizes();//第0張照片的第0個通道
std::cout<<b[0][0][0].sizes();//第0張照片的第0個通道的第0行像素 dim為1
std::cout<<b[0][0][0][0].sizes();//第0張照片的第0個通道的第0行的第0個像素 dim為0

除了索引,還有其他操作是常用的,如narrow,select,index,index_select。

std::cout<<b.index_select(0,torch::tensor({0, 3, 3})).sizes();//選擇第0維的0,3,3組成新張量[3,3,28,28]
std::cout<<b.index_select(1,torch::tensor({0,2})).sizes(); //選擇第1維的第0和第2的組成新張量[10, 2, 28, 28]
std::cout<<b.index_select(2,torch::arange(0,8)).sizes(); //選擇十張圖片每個通道的前8列的所有像素[10, 3, 8, 28]
std::cout<<b.narrow(1,0,2).sizes();//選擇第1維,從0開始,截取長度為2的部分張量[10, 2, 28, 28]
std::cout<<b.select(3,2).sizes();//選擇第3維度的第二個張量,即所有圖片的第2行組成的張量[10, 3, 28]

index需要單獨說明用途。在pytorch中,通過掩碼Mask對張量進行篩選是容易的直接Tensor[Mask]即可。但是c++中無法直接這樣使用,需要index函數實現,代碼如下:

auto c = torch::randn({3,4});
auto mask = torch::zeros({3,4});
mask[0][0] = 1;
std::cout<<c;
std::cout<<c.index({mask.to(torch::kBool)});

張量間操作

拼接和堆疊

auto b = torch::ones({3,4});
auto c = torch::zeros({3,4});
auto cat = torch::cat({b,c},1);//1表示第1維,輸出張量[3,8]
auto stack = torch::stack({b,c},1);//1表示第1維,輸出[3,2,4]
std::cout<<b<<c<<cat<<stack;

到這讀者會發現,從pytorch到libtorch,掌握了[]到{}的變化就簡單很多,大部分操作可以直接遷移。

四則運算操作同理,像對應元素乘除直接用*和/即可,也可以用.mul和.div。矩陣乘法用.mm,加入批次就是.bmm。

auto b = torch::rand({3,4});
auto c = torch::rand({3,4});
std::cout<<b<<c<<b*c<<b/c<<b.mm(c.t());

其他一些操作像clamp,min,max這種都和pytorch類似,仿照上述方法可以自行探索。

分享不易,如果有用請不吝給我一個👍,轉載注明出處:https://allentdan.github.io/
代碼見LibtorchTutorials


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM