LoginSignup
26
27

More than 5 years have passed since last update.

C++でTensorFlow

Last updated at Posted at 2016-03-07

今回

前回と前々回はチュートリアルしかやって来なかったので、ここからは少し生産的な内容になればと思います…。

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とかどうやんの!
とか言われたらまだわからないですね…

26
27
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
27