Nehmen Sie zuerst eine Grube und sortieren Sie sie später aus.
Artikelverzeichnis
Link zur offiziellen libtorch-Dokumentation:https://pytorch.org/cppdocs/
CreateTensor
Erstellen Sie einen zufälligen 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;
}
Erstellen Sie einen Nulltensor
torch::Tensor tensor = torch::zeros({
2, 3});
CreateOnesTensor
torch::Tensor tensor = torch::ones({
2, 3});
Lesen Sie Daten aus dem Speicher, um Tensor zu erstellen
#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;
}
Erstellen Sie einen Tensor aus Opencv Mat-Daten
#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;
}
Erstellen Sie eine Opencv-Mat aus Tensor-Daten
#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;
}
andere
Scheibe
#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;
}
Beachten Sie, dass der Slicing-Vorgang hier eine Referenz ist. Wenn der Inplace-Vorgang im Slicing-Bereich ausgeführt wird, sind die Originaldaten betroffen. Sie können das folgende Beispiel sehen:
#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;
}
Wenn Sie einen neuen Teil des Speichers erstellen müssen, können Sie nach dem Slicing einenclone
-Vorgang hinzufügen.
#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;
}
Bestimmen/stellen Sie die Tensor-Speicherkontinuität ein
#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;
}
Erzwungene Datentypkonvertierung
#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;
}
SwitchTensorDevice
#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-Nutzung
Squeeze
kann Dimensionen komprimieren, Unsqueeze
kann neue Dimensionen hinzufügen. Im folgenden Code wird die Inplace-Operation verwendet, d. h. durch die Verarbeitung der Originaldaten werden keine neuen Daten generiert. Wenn Sie neue Daten generieren möchten, müssen Sie die entsprechende Funktion ohne Unterstreichung verwenden.
#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;
}
Verwendung permutieren
#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 verwenden
#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;
}
Umdrehen
Flip
wird sehr häufig bei der Konvertierung zwischen RGB
und BGR
verwendet:
#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-Nutzung
#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-Nutzung
#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;
}
Deaktivieren Sie die Verlaufsverfolgung
Wenn es nur als Inferenzrahmen verwendet wird, können die Gradientenfunktionen deaktiviert werden. Dies führt zu einer besseren Leistung.
Im Vergleich zu NoGradMode erzielt Code, der in diesem Modus ausgeführt wird, eine bessere Leistung, indem er Autograd-bezogene Arbeiten wie Ansichtsverfolgung und Versionszähler-Stöße deaktiviert.
#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-Konfiguration
CMakeLists.txt
Aufbau:
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
-Konfiguration, bitte beachten Sie, dass -DCMAKE_PREFIX_PATH
als libtorch
der absolute Pfad des Ordners konfiguriert werden muss:
{
"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
Aufbau:
{
"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"
}
]
}
Wenn Sie relevante Header-Dateien hinzufügen müssen, um die Verwendung der intelligenten Vervollständigung zu erleichtern, können Sie die Datei c_cpp_properties.json
konfigurieren:
{
"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
}