先占个坑,后续再整理。
文章目录
libtorch官方文档链接:https://pytorch.org/cppdocs/
创建Tensor
创建随机Tensor
#include <torch/torch.h>
#include <iostream>
int main()
{
auto options = torch::TensorOptions()
.dtype(torch::kFloat32)
.layout(torch::kStrided)
.device(torch::kCPU) // device(torch::kCUDA, 1)
.requires_grad(false);
torch::Tensor tensor = torch::rand({
2, 3}, options);
std::cout << tensor << std::endl;
std::cout << tensor.sizes() << std::endl;
std::cout << tensor.dtype() << std::endl;
return 0;
}
创建Zeros Tensor
torch::Tensor tensor = torch::zeros({
2, 3});
创建Ones Tensor
torch::Tensor tensor = torch::ones({
2, 3});
从内存中读取数据创建Tensor
#include <torch/torch.h>
#include <iostream>
int main()
{
float data[6] = {
1., 2., 3., 4., 5., 6.};
auto options = torch::TensorOptions()
.dtype(torch::kFloat32)
.layout(torch::kStrided)
.device(torch::kCPU) // device(torch::kCUDA, 1)
.requires_grad(false);
torch::Tensor tensor = torch::from_blob(data, {
3, 2}, options);
std::cout << tensor << std::endl;
return 0;
}
从Opencv Mat数据创建Tensor
#include <iostream>
#include <torch/torch.h>
#include <opencv2/opencv.hpp>
using namespace torch::indexing;
int main()
{
std::string img_path = "lena.jpg";
cv::Mat img = cv::imread(img_path);
std::cout << "size: " << img.size() << std::endl;
std::cout << "channels: " << img.channels() << std::endl;
std::cout << "dtype: " << img.type() << std::endl;
cv::Mat img_fp32;
img.convertTo(img_fp32, CV_32FC3);
std::cout << "dtype: " << img_fp32.type() << std::endl;
std::cout << cv::format(img_fp32(cv::Rect(0, 0, 3, 3)), cv::Formatter::FMT_NUMPY) << std::endl;
auto options = torch::TensorOptions()
.dtype(torch::kFloat32)
.layout(torch::kStrided)
.device(torch::kCPU)
.requires_grad(false);
torch::Tensor img_tensor = torch::from_blob(img_fp32.data,
{
img_fp32.rows, img_fp32.cols, img_fp32.channels()},
options);
std::cout << img_tensor.index({
Slice(0, 3), Slice(0, 3), Slice()}) << std::endl;
return 0;
}
从Tensor数据创建Opencv Mat
#include <iostream>
#include <torch/torch.h>
#include <opencv2/opencv.hpp>
using namespace torch::indexing;
int main()
{
torch::manual_seed(1234);
auto options = torch::TensorOptions()
.dtype(torch::kFloat32)
.layout(torch::kStrided)
.device(torch::kCPU)
.requires_grad(false);
torch::Tensor img_tensor = torch::randn({
224, 224, 3}, options);
std::cout << img_tensor.index({
Slice(0, 3), Slice(0, 3), Slice()}) << std::endl;
auto shape = img_tensor.sizes();
cv::Mat img(shape[0], shape[1], CV_32FC3, (float *) img_tensor.data_ptr());
std::cout << "size: " << img.size() << std::endl;
std::cout << "channels: " << img.channels() << std::endl;
std::cout << "dtype: " << img.type() << std::endl;
std::cout << cv::format(img(cv::Rect(0, 0, 3, 3)), cv::Formatter::FMT_NUMPY) << std::endl;
return 0;
}
其他
切片
#include <torch/torch.h>
#include <iostream>
using namespace torch::indexing;
int main()
{
// torch::manual_seed(1234); // 固定随机数种子
torch::Tensor tensor = torch::rand({
1, 3, 512, 512});
std::cout << tensor.sizes() << std::endl;
// tensor[:, :, 10:100, 10:100]
torch::Tensor slice_tensor = tensor.index({
Slice(), Slice(), Slice(10, 100), Slice(10, 100)});
std::cout << slice_tensor.sizes() << std::endl;
return 0;
}
注意,这里的切片操作是引用,如果对切片区域进行inplace操作,会影响原数据,可以看下下面的例子:
#include <torch/torch.h>
#include <iostream>
using namespace torch::indexing;
int main()
{
torch::manual_seed(1234);
torch::Tensor tensor = torch::rand({
5, 5});
std::cout << tensor << std::endl;
torch::Tensor slice_tensor = tensor.index({
Slice(0, 2), Slice(0, 2)});
std::cout << slice_tensor << std::endl;
slice_tensor.add_(5);
std::cout << slice_tensor << std::endl;
std::cout << tensor << std::endl;
return 0;
}
如果需要新创建一块内存可以在切片后加个clone
操作。
#include <torch/torch.h>
#include <iostream>
using namespace torch::indexing;
int main()
{
torch::manual_seed(1234);
torch::Tensor tensor = torch::rand({
5, 5});
std::cout << tensor << std::endl;
torch::Tensor slice_tensor = tensor.index({
Slice(0, 2), Slice(0, 2)}).clone();
std::cout << slice_tensor << std::endl;
slice_tensor.add_(5);
std::cout << slice_tensor << std::endl;
std::cout << tensor << std::endl;
return 0;
}
判断/设置Tensor内存连续
#include <torch/torch.h>
#include <iostream>
using namespace torch::indexing;
int main()
{
torch::Tensor tensor = torch::rand({
3, 2});
std::cout << tensor.is_contiguous() << std::endl;
// 这里切片后并不是内存连续的
torch::Tensor new_tensor = tensor.index({
Slice(), 0});
std::cout << new_tensor.is_contiguous() << std::endl;
new_tensor = new_tensor.contiguous();
std::cout << new_tensor.is_contiguous() << std::endl;
return 0;
}
强转数据类型
#include <torch/torch.h>
#include <iostream>
int main()
{
torch::Tensor tensor = torch::rand({
3, 2}) * 10;
std::cout << tensor << std::endl;
torch::Tensor new_tensor = tensor.to(torch::kInt32);
std::cout << new_tensor << std::endl;
return 0;
}
切换Tensor Device
#include <torch/torch.h>
#include <iostream>
int main()
{
torch::Tensor tensor = torch::rand({
3, 2}, torch::kCPU);
std::cout << tensor << std::endl;
torch::Tensor new_tensor = tensor.to(torch::Device(torch::kCUDA, 0));
std::cout << new_tensor << std::endl;
return 0;
}
Squeeze/Unsqueeze使用
Squeeze
可以压缩维度,Unsqueeze
可以新增维度。下面代码中使用的是inplace操作,即对原始数据进行处理不会生成新的数据,如果想生成新的数据需要使用不带下划线的对应函数。
#include <torch/torch.h>
#include <iostream>
int main()
{
torch::Tensor tensor = torch::rand({
3, 512, 512});
tensor.unsqueeze_(0); // inplace
// torch::Tensor new_tensor = tensor.unsqueeze(); // no inplace
std::cout << tensor.sizes() << std::endl;
tensor.squeeze_(0); // inplace
// torch::Tensor new_tensor = tensor.squeeze(); // no inplace
std::cout << tensor.sizes() << std::endl;
return 0;
}
Permute使用
#include <torch/torch.h>
#include <iostream>
namespace F = torch::nn::functional;
int main()
{
// [N, C, H, W]
torch::Tensor tensor = torch::rand({
1, 3, 512, 512});
std::cout << tensor.sizes() << std::endl;
// [N, C, H, W] -> [N, H, W, C]
torch::Tensor new_tensor = tensor.permute({
0, 2, 3, 1});
std::cout << new_tensor.sizes() << std::endl;
return 0;
}
Interpolation使用
#include <torch/torch.h>
#include <iostream>
namespace F = torch::nn::functional;
int main()
{
torch::Tensor tensor = torch::rand({
1, 3, 512, 512});
std::cout << tensor.sizes() << std::endl;
int64_t h = 1024;
int64_t w = 1024;
auto cfg = F::InterpolateFuncOptions()
.size(std::vector<int64_t>{
h, w})
.mode(torch::kBilinear)
.align_corners(false);
torch::Tensor new_tensor = F::interpolate(tensor, cfg);
std::cout << new_tensor.sizes() << std::endl;
return 0;
}
Flip使用
Flip
在RGB
和BGR
互转的情况下非常常用:
#include <torch/torch.h>
#include <iostream>
int main()
{
torch::Tensor tensor = torch::rand({
3, 2});
std::cout << tensor << std::endl;
torch::Tensor new_tensor = tensor.flip({
1});
std::cout << new_tensor << std::endl;
return 0;
}
Clip使用
#include <torch/torch.h>
#include <iostream>
int main()
{
torch::Tensor tensor = torch::randn({
3, 2});
std::cout << tensor << std::endl;
tensor.clip_(0., 1.); // inplace
// torch::Tensor new_tensor = tensor.clip(0., 1.); // no inplace
std::cout << tensor << std::endl;
return 0;
}
Rot90使用
#include <torch/torch.h>
#include <iostream>
int main()
{
torch::Tensor tensor = torch::randn({
1, 1, 3, 2});
std::cout << tensor << std::endl;
// torch.rot90(tensor, k=1, dims=[2, 3])
// 逆时针旋转90度, 如果要顺时针将dims改为[3, 2]
torch::Tensor new_tensor = torch::rot90(tensor, 1, {
2, 3});
std::cout << new_tensor << std::endl;
return 0;
}
关闭梯度跟踪
如果仅仅是当推理框架使用,可以把梯度相关功能给关闭掉。这样能够获得更好的性能。
Compared to NoGradMode, code run under this mode gets better performance by disabling autograd related work like view tracking and version counter bumps.
#include <torch/torch.h>
#include <iostream>
int main()
{
// like python torch.inference_mode()
torch::InferenceMode();
torch::Tensor tensor = torch::rand({
1, 3, 512, 512});
std::cout << tensor.sizes() << std::endl;
return 0;
}
VSCODE配置
CMakeLists.txt
配置:
cmake_minimum_required(VERSION 3.18 FATAL_ERROR)
project(example-app)
find_package(Torch REQUIRED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}")
add_executable(example-app example-app.cpp)
target_link_libraries(example-app "${TORCH_LIBRARIES}")
set_property(TARGET example-app PROPERTY CXX_STANDARD 17)
tasks.json
配置,注意需要将-DCMAKE_PREFIX_PATH
配置成libtorch
文件夹绝对路径:
{
"version": "2.0.0",
"tasks": [
{
"label": "mkdir",
"type": "shell",
"command": "mkdir",
"args": [
"-p", "build"
],
"options": {
"cwd": "${workspaceFolder}"
},
},
{
"label": "cmake",
"type": "shell",
"command": "cmake",
"args": [
"-DCMAKE_PREFIX_PATH=/absolute/path/to/libtorch",
"../"
],
"options": {
"cwd": "${workspaceFolder}/build"
},
"dependsOn": ["mkdir"]
},
{
"label": "make",
"type": "shell",
"command": "cmake",
"args": [
"--build",
".",
"--config", "Release"
],
"options": {
"cwd": "${workspaceFolder}/build"
},
"dependsOn": ["cmake"]
},
{
"label": "build",
"dependsOn": ["make"]
},
],
}
launch.json
配置:
{
"version": "0.2.0",
"configurations": [
{
"name": "Build and debug active file",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/${fileBasenameNoExtension}",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "build",
"miDebuggerPath": "/usr/bin/gdb"
}
]
}
如果需要添加相关头文件方便使用智能补全的,可以配置c_cpp_properties.json
文件:
{
"configurations": [
{
"name": "linux",
"intelliSenseMode": "linux-gcc-x64",
"compilerPath": "/usr/bin/gcc",
"cStandard": "c17",
"cppStandard": "gnu++17",
"includePath": ["/usr/local/include/opencv4/",
"/usr/local/include/opencv4/opencv2/",
"/absolute/path/to/libtorch/include",
"/absolute/path/to/libtorch/include/torch/csrc/api/include"]
}
],
"version": 4
}