今回
前回と前々回はチュートリアルしかやって来なかったので、ここからは少し生産的な内容になればと思います…。
C++で学習させる
前回は、Computation Graphの定義と実行の仕方をまとめました。
今回は、そのComputation Graphを繰り返し実行して学習を進めることを考えます。
tensorflow/cc/tutorials/example_trainer.cc
というexampleがあります。
このプログラムは、
A = [3 2; -1 0]
x = rand(2, 1)
として以下の計算を繰り返します。
x = y / y_norm
y = A * x
ではまずこの計算を行う、Computation Graphを作成する関数から確認しましょう。
GraphDef CreateGraphDef() {
GraphDefBuilder b;
using namespace ::tensorflow::ops; // NOLINT(build/namespaces)
// Store rows [3, 2] and [-1, 0] in row major format.
Node* a = Const({3.f, 2.f, -1.f, 0.f}, {2, 2}, b.opts());
// x is from the feed.
Node* x = Const({0.f}, {2, 1}, b.opts().WithName("x"));
// y = A * x
Node* y = MatMul(a, x, b.opts().WithName("y"));
// y2 = y.^2
Node* y2 = Square(y, b.opts());
// y2_sum = sum(y2)
Node* y2_sum = Sum(y2, Const(0, b.opts()), b.opts());
// y_norm = sqrt(y2_sum)
Node* y_norm = Sqrt(y2_sum, b.opts());
// y_normalized = y ./ y_norm
Div(y, y_norm, b.opts().WithName("y_normalized"));
GraphDef def;
TF_CHECK_OK(b.ToGraphDef(&def));
return def;
}
ここまでは前回までの内容で、説明不要でしょう。
(GraphDefBuilderに各ノードを追加してGraphDefを作成しています。)
さて、このグラフをn回*Mステップ実行しますが、example_trainere.ccではこれをMスレッドに分配してしています。
今回は簡単のため、1スレッドで実行するようなものを実装してみます。
void Steps() {
// Session作成
SessionOptions options;
std::unique_ptr<Session> session(NewSession(options));
GraphDef def = CreateGraphDef();
session->Create(def);
// Mステップ
for (int step = 0; step < M; ++step) {
// inputを乱数で初期化
Tensor x(DT_FLOAT, TensorShape({2, 1}));
x.flat<float>().setRandom();
// 各ステップはnum_iterations回計算する
std::vector<Tensor> outputs;
for (int iter = 0; iter < n; ++iter) {
outputs.clear();
// 上で定義したComputation Graphを実行する。出力のTensorはoutputs
session->Run({{"x", x}}, {"y:0", "y_normalized:0"}, {}, &outputs)
CHECK_EQ(size_t{2}, outputs.size());
const Tensor& y = outputs[0];
const Tensor& y_norm = outputs[1];
// Print out lambda, x, and y.
std::printf("%06d %s\n", step,
DebugString(x, y).c_str());
// Copies y_normalized to x.
x = y_norm;
}
}
session->Close();
}
Sessionを実行して、その結果を使用して更新、再び実行するというのを繰り返しているだけです。
また、オリジナルのexample_trainer.ccではこのMステップを更にN個の並列Sessionで行います。
ですがここでは簡単のため単一Sessionで行います。
int main(int argc, char* argv[]) {
// TensorFlowのセットアップ
tensorflow::port::InitMain(argv[0], &argc, &argv);
Steps();
}
これだけです!!
tensorflow::Session::Runの引数
これは、ここに記載されています。
tensorflow::Session::Run(
const std::vector< std::pair< string, Tensor > > &inputs,
const std::vector< string > &output_tensor_names,
const std::vector< string > &target_node_names,
std::vector< Tensor > *outputs
)
例えば、
session->Run({{"x", x}}, {"y:0", "y_normalized:0"}, {}, &outputs)
の場合、WithName("x")としたノードにxが入力として渡され、
WithName("y")とWithName("y_normalized")としたノードからの出力が、この順で、
outputsに入るのだと思います。
target_node_namesは…何だろう…
実行
とりあえず、10ステップ50イテレーションで実行してみしょう。
$ bazel build tensorflow/my_work/...
$ bazel-bin/tensorflow/my_work/my_work
000009 lambda = 5.219631 x = [0.227381 0.252351] y = [1.186847 -0.227381]
000009 lambda = 2.616831 x = [0.982138 -0.188162] y = [2.570089 -0.982138]
000009 lambda = 2.235717 x = [0.934118 -0.356965] y = [2.088423 -0.934118]
000009 lambda = 2.105432 x = [0.912847 -0.408302] y = [1.921937 -0.912847]
000009 lambda = 2.050076 x = [0.903291 -0.429029] y = [1.851815 -0.903291]
000009 lambda = 2.024426 x = [0.898775 -0.438410] y = [1.819504 -0.898775]
000009 lambda = 2.012066 x = [0.896580 -0.442881] y = [1.803979 -0.896580]
000009 lambda = 2.005997 x = [0.895499 -0.445064] y = [1.796367 -0.895499]
000009 lambda = 2.002990 x = [0.894962 -0.446143] y = [1.792599 -0.894962]
000009 lambda = 2.001493 x = [0.894694 -0.446679] y = [1.790724 -0.894694]
000009 lambda = 2.000746 x = [0.894561 -0.446947] y = [1.789788 -0.894561]
(略)
000009 lambda = 2.000000 x = [0.894427 -0.447214] y = [1.788854 -0.894427]
000009 lambda = 2.000000 x = [0.894427 -0.447214] y = [1.788854 -0.894427]
000009 lambda = 2.000000 x = [0.894427 -0.447214] y = [1.788854 -0.894427]
000009 lambda = 2.000000 x = [0.894427 -0.447214] y = [1.788854 -0.894427]
000009 lambda = 2.000000 x = [0.894427 -0.447214] y = [1.788854 -0.894427]
000009 lambda = 2.000000 x = [0.894427 -0.447214] y = [1.788854 -0.894427]
000009 lambda = 2.000000 x = [0.894427 -0.447214] y = [1.788854 -0.894427]
000009 lambda = 2.000000 x = [0.894427 -0.447214] y = [1.788854 -0.894427]
000009 lambda = 2.000000 x = [0.894427 -0.447214] y = [1.788854 -0.894427]
行列[3 2; -1 0]の最大固有値2に収束しているので、きっと正解
(bazel is 何状態なのでそのうち調べたいです…)
この例は、機械学習というか数値計算的(出力が次の入力になるだけ)なものだったので、
じゃあBack Propagationとかどうやんの!
とか言われたらまだわからないですね…