深度學習框架集成平臺C++ Guide指南
這個指南詳細地介紹了神經網絡C++的API,並介紹了許多不同的方法來處理模型。
提示
所有框架運行時接口都是相同的,因此本指南適用於所有受支持框架(包括TensorFlow、PyTorch、Keras和TorchScript)中的模型。
導入神經網絡
最簡單的導入方法如下:
#include “neuropod/neuropod.hh”
Neuropod neuropod(PATH_TO_MY_MODEL);
其中PATH_TO_MY_MODEL是使用其中一個打包程序導出的模型的路徑。
選項
您還可以在加載模型時提供運行時選項。
要選擇運行模型的設備,可以指定“visible_device”選項:
neuropod::RuntimeOptions opts;
// Set the
visible device to the first GPU
opts.visible_device = Device::GPU0;
Neuropod neuropod(PATH_TO_MY_MODEL, opts);
默認爲GPU0。如果沒有可用的GPU,將嘗試退回到CPU。
設置opts.visible_device = Device::CPU將強制模型在CPU上運行。
獲取模型的輸入和輸出
要獲取模型的輸入和輸出,可以執行以下操作:
Neuropod neuropod(PATH_TO_MY_MODEL);
// Both of these
are std::vector<TensorSpec>
const auto &inputs = neuropod.get_inputs();
const auto &outputs = neuropod.get_outputs();
for (const auto &item : inputs)
{
// A `TensorSpec` is a struct with a `name`, `dims` and `type`
std::cout << "Tensor name: " << item.name << std::endl;
}
Tensor張量類型
支持以下Tensor張量類型:
·
float
·
double
·
string
·
int8
·
int16
·
int32
·
int64
·
uint8
·
uint16
·
uint32
·
uint64
提示
PyTorch或TorchScript不支持uint16、uint32和uint64。請參閱PyTorch文檔中支持的類型列表。
TorchScript不支持字符串Tensor張量,因此我們將它們表示爲字符串列表。因此,TorchScript-Neuropod模型只支持一維字符串“Tensor張量”。請參見此處的用法示例。
創建Tensor張量
提示
有關最適合您的用例的方法的指南,請參見高效張量創建頁面。
有很多不同的方法來創建神經網絡Tensor張量,但是所有的方法都是從分配器開始的。
要獲取加載模型的分配器,可以執行以下操作:
Neuropod neuropod(PATH_TO_MY_MODEL);
auto allocator = neuropod.get_tensor_allocator();
對於未加載模型的場景(例如單元測試),可以使用通用張量分配器:
#include “neuropod/core/generic_tensor.hh”
auto allocator = neuropod::get_generic_tensor_allocator();
分配新內存
爲此,我們只需要我們想要分配的張量的維數和類型。
auto tensor = allocator->allocate_tensor({1, 2, 3});
也可以在不使用模板函數的情況下手動指定類型
auto tensor = allocator->allocate_tensor({1, 2, 3}, neuropod::FLOAT_TENSOR);
要對這些張量做一些有用的事情,請參閱下面的“與張量的交互”部分。
從現有內存
提供了一種包裝現有記憶並以零拷貝方式使用它的方法。
要做到這一點,需要四件事:
要創建的張量的維數
要創建的張量類型
指向要包裝的數據的指針
注意:這應該是64字節對齊的
deleter函數
使用此數據完成底層庫後,將調用此deleter函數。在調用此函數之前,釋放數據是不安全的。
傳遞正確的刪除程序以確保內存不會過早釋放,這一點非常重要。下面是一些例子。
cv::Mat
cv::Mat image = … // An image from somewhere
auto tensor = allocator->tensor_from_memory<uint8_t>(
// Dimensions
{1, image.rows, image.cols, image.channels()},
// Data
image.data,
// Deleter
[image](void * unused) {
// By capturing `image` in this deleter, we ensure
// that the underlying data does not get deallocated
// before we're done with the tensor.
}
);
提示
也可以在不使用模板函數的情況下指定類型
cv::Mat image = … // An image from somewhere
auto tensor = allocator->tensor_from_memory(
// Dimensions
{1, image.rows, image.cols, image.channels()},
// Tensor Type
get_tensor_type_from_cv_mat(image),
// Data
image.data,
// Deleter
[image](void * unused) {}
);
將來的版本中將添加用於包裝來自公共庫的類型的實用程序。
特徵Eigen
#include “neuropod/conversions/eigen.hh”
auto tensor = allocator->allocate_tensor({1, 2, 3});
// Returns an
Eigen::Map
auto eigen_map = neuropod::as_eigen(*tensor);
提示
如果您不使用eign的特性,只需要簡單的元素接入,請使用接入設備。
工廠函數
這些函數對於創建測試數據非常有用。
0
返回T類型的張量,input_dims輸入爲0。
auto zeros = allocator->zeros(input_dims);
1
返回T類型的張量和input_dims輸入爲1。
auto ones = allocator->ones(input_dims);
full滿的
返回T類型的張量和輸入input_dims = fill_value。
auto full = allocator->full(input_dims, fill_value);
隨機數randn
返回一個T型張量和形狀輸入維度,用正態分佈中的隨機數填充,平均值和標準偏差爲stddev。
auto full = allocator->randn(input_dims, mean = 0, stddev = 1);
序列分配
返回T類型的1D張量,其中包含從步長爲步長的開始處開始的一系列數字。
auto range1 = allocator->arange(end);
auto range2 = allocator->arange(start, end, step = 1);
示例:
// 0, 1, 2, 3, 4
auto range1 = allocator->arange(5);
// 2, 3, 4, 5
auto range2 = allocator->arange(2, 6);
// 0, 2, 4, 6, 8
auto range3 = allocator->arange(0, 10, 2);
關注eye
返回T類型和形狀(M,N)的標識矩陣。這個矩陣的對角線上爲1,其餘元素都有0。
auto eye1 = allocator->eye(M, N);
示例:
// 1, 0, 0, 0,
// 0, 1, 0, 0,
// 0, 0, 1, 0,
// 0, 0, 0, 1
auto eye1 = allocator->eye(4, 4);
// 1, 0, 0, 0,
0, 0, 0,
// 0, 1, 0, 0,
0, 0, 0,
// 0, 0, 1, 0,
0, 0, 0
auto eye2 = allocator->eye(3, 7);
與張量相互作用
本節將介紹與現有張量交互的各種方式。
張量類型
神經網絡集成軟件有幾種不同的表示張力的方法:神經網絡集成軟件值、神經網絡集成軟件張力和類型的神經網絡集成軟件張力
神經網絡值value是基類型,表示庫可以存儲和傳遞的任何值。
神經網絡張量tensor是一個神經odvalue,它是一個張量。這將添加元數據功能(維度、類型、num元素等),但不允許數據訪問。
dneuropodtensor是一種特殊類型的神經網絡張量tensor。此層次結構級別添加類型安全數據訪問。
這就是類層次結構的表示:
要從neuromodvalue轉換爲neuromodtensor,可以使用as_tensor()。
auto my_value = …
auto my_tensor = my_value->as_tensor();
要從一個NeuropodValue或NeuropodTensor轉換爲一個特定類型的TypedNeuropodTensor,可以使用as_typed_tensor)。這將對請求的類型進行類型檢查,如果請求的類型與張量的實際類型不匹配,則拋出錯誤。
auto my_value = …
auto my_float_tensor = my_value->as_typed_tensor();
// This will
throw an error
auto my_uint_tensor = my_value->as_typed_tensor<uint8_t>();
下面的部分將介紹更多的用法和示例。
提示
大多數用例不需要使用這些方法(因爲工廠和模板分配器已經返回TypedNeuropodTensors)。
通常,數據訪問需要一個TypedNeuropodTensor類型,而元數據訪問至少需要一個NeuropodTensor。
將數據copy到Tensor張量
Requires
TypedNeuropodTensor
如果要複製數據(並且無法使用上面的tensor_from_memory API包裝數據),可以執行以下操作:
float * my_data = …;
size_t num_elements = …;
tensor->copy_from(my_data, num_elements);
可用向量vectorcopy數據
std::vector my_data;
tensor->copy_from(my_data);
直接設置/獲取數據
Requires
TypedNeuropodTensor
您可以使用訪問器接口來實現這一點,該接口與PyTorch的訪問器接口非常相似。
auto tensor = allocator->allocate_tensor({6, 6});
// 2 is the
number of dimensions of this tensor
auto accessor = tensor->accessor<2>();
accessor[5][3] = 1.0;
基於範圍的for循環也適用於訪問器:
auto tensor = allocator->allocate_tensor({3, 5});
// 2 is the
number of dimensions of this tensor
auto accessor = tensor->accessor<2>();
for (const auto &row : accessor)
{
for (const auto &item : row)
{
// Do something
}
}
張量Tensor字符示例:
auto tensor = allocator->allocate_tensorstd::string({3, 5});
// 2 is the
number of dimensions of this tensor
auto accessor = tensor->accessor<2>();
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
accessor[i][j] = std::to_string(i * 5 + j);
}
}
使用訪問器的單個元素訪問非常高效,與優化生成期間的原始指針操作相當。
提示
有關最適合您的用例的方法的指南,請參見高效張量創建頁面。使用
獲取張量Tensor維數
Requires
NeuropodTensor
const auto &dims = tensor->get_dims();
獲取張量Tensor中元素數目
Requires
NeuropodTensor
auto num_elements = tensor->get_num_elements();
獲取張量Tensor類型
Requires
NeuropodTensor
auto tensor_type = tensor->get_tensor_type();
獲取指向基礎數據的原始指針
Requires
TypedNeuropodTensor
auto data = tensor->get_raw_data_ptr();
提示
此方法不適用於字符串張量。改用訪問器。
獲取向量vector的數據
Requires
TypedNeuropodTensor
auto data = tensor->get_data_as_vector();
warning
此方法執行復制。
推論
基本推斷方法如下:
std::unique_ptr infer(const NeuropodValueMap &inputs);
neuromodvaluemap只是從std::string到 std::shared_ptr
與它的交互等同於與std::unordered_map的交互。
示例
// Get an
allocator
auto alloctor = neuropod.get_tensor_allocator();
// Create some
tensors
auto x = allocator->randn({5, 5});
auto y = allocator->ones({5, 5});
// Run inference
const auto output_data = neuropod.infer({
{"x", x},
{"y", y}
});
// Get the
outputs
auto z = output_data->at(“z”)->as_typed_tensor();
還可以通過提供請求的輸出列表來獲取模型輸出的子集:
std::unique_ptr infer(const NeuropodValueMap &inputs, const std:: vectorstd::string requested_outputs);
例如,如果要返回僅包含張量“z”的映射,可以執行以下操作:
const auto output_data = neuropod.infer(input_data, {“z”});
序列化
所有內置的神經網絡Value類型都是可序列化的。此外,NeuropodValueMap也是可序列化的。
// A stream to
serialize to. Any ostream is allowed, but we use a
// stringstream
in this example
std::stringstream ss;
neuropod::NeuropodValueMap data = …;
neuropod::serialize(my_stream, data);
同樣,反序列化也同樣容易。
auto deserialized = neuropod::deserializeneuropod::NeuropodValueMap(ss, allocator);
提示
序列化和反序列化工作在Python和C++之間。有關更多信息,請參見Python綁定文檔。
Warning
這個API的目標是支持臨時序列化。不能保證向後兼容,因此此API不應用於數據的長期存儲。