扩展说明1: 在GCC5.1发布的同时,为libstdc++添加了新的特性,其中也包括了std::string和std::list的新实现。这个新的实现使得两者符合了c++11的标准,具体来说是取消了Copy-On-Write。那么,这样子虽然符合了c++11的标注,旧版不就无法兼容了吗。为了避免上述混乱,对于旧版而言,GCC5.1添加了__cxx11命名空间,GCC5.1或者说c++11规范下的string和list,实际上是std::__cxx11::string和std::__cxx11::list,所以我们一般的using namespace std就会变成形如using namespace std::__cxx11的样子。也就是说,有旧版(c++03规范)的libstdc++.so,和新版(c++11规范)的libstdc++.so两个库同时存在。
-注意 implicit declaration of function ‘XXX’:未申明的引用的错误,通过-D_GLIBCXX_USE_CXX11_ABI=0来控制
set(Torch_DIR "path/to/libtorch/share/cmake/Torch")
find_package(Torch REQUIRED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 ${TORCH_CXX_FLAGS}")
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) # 使用旧版stdlibc++.so库, 如果有问题就改为=1
# target_link_libraries(${PROJ} ${TORCH_LIBRARIES})
// 一维
std::vector<float> vec;
torch::Tensor tensor = torch::tensor(vec);
// 二维
vector<float> data2dTo1d(const vector<vector<float>>& vec_vec) {
vector<float> vec;
for (const auto& v : vec_vec) {
for (auto d : v) {
vec.push_back(d);
}
}
return vec;
}
vector<float> vec = data2dTo1d(your2Dvector);
// 写法1:
torch::Tensor t = torch::from_blob(vec.data(), {n,m});
// 写法2:
// at::Tensor p_feat = at::tensor(vec, torch::kFloat32).to(device);
/***: 写法3
vector v={1,2,3,4};
at::TensorOptions opts=at::TensorOptions().dtype(at::kInt);
c10::IntArrayRef s={2,2};//设置返回的tensor的大小
at::Tensor t=at::from_blob(v.data(),s,opts).clone();
*/
// tensor --> vector
at::Tensor t=at::ones({3,3},at::kInt);
std::vector<int> v(t.data_ptr<int>(),t.data_ptr<int>()+t.numel());
// data ptr
float * ptr;
cudaMalloc(ptr, sizeof(float)*N);
torch::DeviceType devicetype;
if (torch::cuda::is_available()) {
std::cout << "CUDA available! Training on GPU." << std::endl;
devicetype = torch::kCUDA;
}
torch::Device device(devicetype);
// torch::Tensor t = torch::from_blob(ptr, {N, }, device=device);
// torch::TensorOptions option(torch::kFloat32);
// auto img_tensor = torch::from_blob(img.data, { 1,img.rows,img.cols,img.channels() }, option);//
float* ptr = tensor.data<float>();
Note: 官方pytorch对比C++的写法; 如果需要什么函数使用,可以参看官网,然后对照的改写; 这个过程特别需要举一反三能力和类比能力。 注意示例中的写法根据版本更新可能会有变化,仿照写即可。
std::tuple
,有些是std::vector
------------ | pytorch 写法 | libtorch写法 |
---|---|---|
tensor size | size=torch.tensor.size() | c10::ArrayRef size = torch::tensor.sizes() |
slice | t = torch.tensor.slice(1,2,4) | auto x = torch::tensor.slice(1, 2, 4) |
.cuda() | t = torch.tensor(2,4).cuda() | t = torch::tensor({2,4}, at::kCUDA); |
cuda().long() | t= torch.tensor(2,3).cuda().long() | t= torch::tensor({2,3}, at::device(at::kCUDA).dtype(at::kLong)); |
to(“cuda:1”) | t= torch.tensor(2,3).to(“cuda:1”) | t= torch::tensor({2,3}, at::device({at::kCUDA,1})); |
print tensor} | print(tensor) | std::cout << torch::tensor << std::endl; |
index_select | – | torch::Tensor b = torch::index_select(a, 0, indices); |
多个输出 | x,y = torch.tensor.max(a,1) | std::tuple |
where | – | std::vectortorch::Tensor index = torch::where( x >= 10); |
cat,stack等函数 | – | torch::Tensor x = torch::stack({a, b}, 1); |
permute | – | torch::Tensor x_ = x.permute({1,0,2}); |
类型转换 | – | torch::Tensor x = x.toType(torch::kFloat); |
split | – | std::vectortorch::Tensor> y = torch::split(x, 1, 1); |
A[mask] <–> torch::masked_select(A, mask) | A[mask] = B[mask] | A = torch::_s_where(mask, B, A); |
是否为空tensor | — | if(torch::tensor.numel() == 0) 是否为空 |
切片1 | c = a[1::2] | at::Tensor c = a.index({Slice(1, None, 2)}); 需要include torch/script.h,using namespace torch::indexing; |
切片2 | tensor[…, 1:] | tensor.index({“…”, Slice(1)}); |
切片3 | tensor[…, :2] | tensor.index({“…”, Slice({None, 2})}); |
取index | tensor[torch.tensor([1, 2])] | tensor.index({torch::tensor({1, 2})}); 注意大括号 |
切片赋值1 | tensor[1, 2] = 1 | tensor.index_put_({1, 2}, 1); |
切片赋值2 | x[…, 1] = 100 | x.index_put_({“…”, 1}, 100); |
/**
//python
a = torch.randn(3, 3) # 创建shape为3*3,值为[0,1]的随机数的tensor-float类型
b = torch.tensor([0, 1, 0]).bool() # 创建bool值向量,最终的结果是对应行向量的取舍
c = torch.tensor([0, 1, 0]).long() # 注意这里不是bool值,最终的结果只是按行(值)索引
a1 = a[b] # 按掩码操作
a2 = a[c] # 按索引操作
*/
// C++
torch::Tensor value = torch::randn({3,3}); // 被操作的tensor
torch::Tensor x = at::tensor({0,1,0}).toType(at::kBool); // bool型掩码向量,注意tensor类别要bool类型
torch::Tensor y = at::tensor({0,1,0}).toType(at::kLong); // 非bool型则按值索引(这里的索引用tensor必须是long, byte or bool类型的)
torch::Tensor value1 = value.index({x}); // bool类型按掩码操作
torch::Tensor value2 = value.index({y}); // 非bool类型按行索引
/**
permute不会改变tensor 的data指针内数据的顺序
但是调用contiguous之后会改变。
*/
tensor_image = tensor_image.permute({2,3,0,1});
tensor_image = tensor_image.contiguous();
在写实际用例测试按照libtorch复习的pytorch自定义算子时,需要保存这个算子pytorch的输入输出,来验证libtorch算子是否编写正确,需要注意,保存的时候,f.write 二进制保存或者使用numpy.tofile()去保存二进制;而不是用numpy.save(“*.npy”), 因为npy不是标准的二级制文件格式,C++读取npy可能有问题,会多出32个数。
主要原因是编译顺序导致,libtorch中头文件里的DeviceType使用的时候,没有显性的指明namespace,而是在开始的时候用namespace at/c10 {}括起来,从而是的与tensorRT中的nvinfer1::DeviceType 冲突ambiguous
有哪些冲突就把libtorch的那些冲突的head文件中 DeviceType前面加上c10::或者at::即可,总共冲突的文件不太多,手动改就好。
DeviceType --> c10::DeviceType