首先我們先來看幾個簡單使用 C++ front-end API 的例子。
#include "tensorflow/cc/client/client_session.h"
#include "tensorflow/cc/ops/standard_ops.h"
#include "tensorflow/core/framework/tensor.h"
int main() {
using namespace tensorflow;
using namespace tensorflow::ops;
Scope root = Scope::NewRootScope();
// 使用常數張量來建立 Matrix A = [3 2; -1 0]
auto A = Const(root, { {3.f, 2.f}, {-1.f, 0.f} });
// 使用常數張量來建立 Vector b = [3 5]
auto b = Const(root, { {3.f, 5.f} });
// v = Ab^T
auto v = MatMul(root.WithOpName("v"), A, b, MatMul::TransposeB(true));
std::vector<Tensor> outputs;
ClientSession session(root);
// 執行並提取 v 的計算結果
TF_CHECK_OK(session.Run({v}, &outputs)); # 傳入已配置好的 `std::vector` 物件 outputs
// 期望右邊的陳述為真 outputs[0] == [19; -3]
// 列印結果
LOG(INFO) << outputs[0].matrix<float>();
return 0;
}
編譯似乎還是使用 google 自家的 bazel 比較順利一點。根據 Tensorflow 1.x 的 frontend C++ 文章要將原始碼放到整個專案底下,路徑則是 <github repo root>/tensorflow/cc/example/example.cc
。其中 <github repo root>
則是你 clone 下來的 tensorflow 資料夾。
除了原始碼,另外一個則是 BUILD。BUILD 檔案就像 CMake 的 CMakeLists.txt 或是 Unix Make 系統的 Makefile,必須列出編譯的目標和依賴的資源,如 libraries 等等。下面就是官方提供的 BUILD 內容,直接 copy-paste 就可以啦!
load("//tensorflow:tensorflow.bzl", "tf_cc_binary")
tf_cc_binary(
name = "example",
srcs = ["example.cc"],
deps = [
"//tensorflow/cc:cc_ops",
"//tensorflow/cc:client_session",
"//tensorflow/core:tensorflow",
],
)
若 BUILD 檔案已經準備就緒,那麼要先對編譯環境做相對應的組態調整,要達成這個目的就需在 <github repo root>
目錄下先執行 ./configure
。若 ./configure
沒有吐出任何錯誤訊息,再執行 bazel 命令: bazel run -c opt //tensorflow/cc/example:example
。
bazel run 其實是 bazel build 再加上執行編譯好的目標兩個命令。命令的格式如下:bazel run <options> -- <binary target> <flags to binary>
最後關於我用的版本,在這個例子我是用 git checkout v2.0.0-rc0
拉下 v2.0.0-rc0
tag branch。bazel 的版本是 0.26.1-homebrew,太新的 bazel 版本(homebrew 上市 0.29) 做 configure 的時候就會丟出錯誤訊息。
現在就來解釋例子:
程式一開始就是建立一個 Scope 物件,透過 tensorflow::Scope::NewRootScope
方法建立。一個 tensorflow::Scope
物件代表的是一組 Tensorflow 運算元,彼此之間有相同的性質並享有相同的命名空間。若希望建立一個與現在不同的命名空間可以傳入欲建立的命名空間字串到 NewRootScope
中,如 Scope linear = root.NewSubScope("linear");
看到當生成一個 Vector, Matrix 到呼叫 MatMul 做計算都需要於第一個引數傳入 tensorflow::Scope
。另外一種情況則是呼叫傳入 tensorflow::Scope
物件的方法,WithOpName(suffix_name)
來建立子命名空間,在這個例子則是 suffix_name 子命名空間。
Tensorflow 的 frontend C++ API 提供了一個 tensorflow::ClientSession 類別,該類別物件會使用運算元建構子建立計算圖。欲執行計算圖,只要將相對應的 Scope 物件傳入到 ClientSession 的建構子,便可以建構 ClientSession 物件。和 Tensorflow Python 1.x 的 Session 一樣,只要呼叫 Session.run
方法即可獲得計算結果。 Session.run()
的第一個引數是一個 array,傳入的是計算圖中的任一個計算元代表的節點,若想計算完整的計算圖結果,就要傳入計算圖的 root 節點。第二個引數則是預先為輸出配置記憶體的 std::vector
或其他容器,如 Tensor。