TensorFlowを使ったプログラムの作りを確認する

def main(argv):
  del argv  # Unused
  hparams = set_default_hparams().parse(FLAGS.hparams)
  run_nngp_eval(hparams, FLAGS.experiment_dir)
  • まず、ニューラルネットワーク学習のハイパーパラメタ(hparams)を設定する
    • ニューラルネットワークでは、ノードレイヤー間で線形変換をし、その上で非線形関数をかぶせるのだが、その非線形関数を設定(こちら非線形関数のいろいろについて書いてある)し、線形変換部分と切片部分との正規乱数分散を定める。また、ネットワークのレイヤー数も定める
    • その上で、run_nngp_eval()関数を実行する
  • run_nngp_eval()関数は、大きく分けて
    • 前半のMNISTデータセットから3サブセットデータを読む込む部分(この読み込みについてはこちらに別途記載)と
    • 後半のニューラルネットワークにサブセットデータを適用する部分とに分かれる
      • 後半では、3サブセットにそれぞれ順次、ニューラルネットワークを適用する。ただし、最初のセットはトレーニングセットなので、自身のセットを学習に使い、その学習結果を自身に再適用して予測結果を得る。残りの2セットは、最初のセットを学習に使い、その結果を自身に適用して予測結果を得る。それにより、予測結果の正しさを確認する
      • このニューラルネットワークの適用をするのが、do_eval()関数
  • do_eval()関数はtf.Session()で作ったsessを引数として取っている。このsessを使って、sess.run()することで、初めてTensorFlowの各テンソルの値計算処理が始まる。逆に言うと、sess.run()までは、TensorFlowのテンソルの相互リンク関係を決める作業をしているだけ
  • 3つのデータセットに対するdo_eval()の異同
acc_train, mse_train, norm_train, final_eps = do_eval(
          sess, model, train_image[:FLAGS.num_eval],
          train_label[:FLAGS.num_eval])

acc_valid, mse_valid, norm_valid, _ = do_eval(
        sess, model, valid_image[:FLAGS.num_eval],
        valid_label[:FLAGS.num_eval])

acc_test, mse_test, norm_test, _ = do_eval(
        sess, model, test_image[:FLAGS.num_eval],
        test_label[:FLAGS.num_eval],
        save_pred=False)
    • 3番目だけsave_pred=Falseの条件付けがあるが、do_eval()関数のsave_predのデフォルト値はFalseなので、これは違いをもたらしていない
    • 結局違いは、渡すデータセット(画像とラベル)のみ
    • sessはTensorFlow的な計算セッションを動かすための装置であるからこれも共通
    • modelは以下のように作られている
 # Construct Gaussian Process Regression model
    model = gpr.GaussianProcessRegression(
        train_image, train_label, kern=nngp_kernel)
      • train_imageとirain_labelとを使うことが指定されている。このようにこのmodelオブジェクトがtrain_image,train_labelを「教師データ」として取り立てる部分とわかる
    • では、sessはどんなテンソルフローグラフの計算をしようとしているのかを確認する
      • do_eval()関数は内部でTensorFlowのセッションを回し、その結果(ベンチマーク実験結果)を返す
      • do_eval()関数の中で、さらに、model.predict(x_data,sess)のようにpredict()関数に引き渡されて使用される
      • 最初に出会う sess.run()は
self.k_np = sess.run(self.k_data_data,
                           feed_dict={self.x_pl: self.input_x})
      • カーネル行列をトレーニングxから作っている
      • これは、placeholderになっている(値がなく、入れ物だけのテンソル)x_plに、input_xを代入した上で、k_data_dataを計算してk_npに代入する、という処理
      • ここで、k_data_dataはテンソルとして登録されているわけであるが、それがどこで定義されているか、と言うとGaussianProcessRegressionクラスの__init__()の中で
self.k_data_data = tf.identity(self.kern.k_full(self.x_pl))
      • と、書かれている。これは、x_pl(プレースホルダ)を取って、k_full()関数を回し、その返り値を、tf.identityにて、(ちゃんと)TensorFlowのテンソルにしたものとせよ、というコマンド
      • このk_full()関数はNNGPKernelクラスの中で定義されていて、その末尾に
return tf.reshape(q_ab_all, cov_init.shape, "qab")
      • とあるように、reshape()関数にて作られたTensorFlowのテンソルである
      • ここで、もう一度、
self.k_data_data = tf.identity(self.kern.k_full(self.x_pl))
    • この後、model.predict()の中で、whileループでのsess.run()計算が回る
      while self.current_stability_eps < 1:
        try:
          start_time = time.time()
          self.l_np, self.v_np = sess.run(
              [self.l, self.v],
              feed_dict={self.y_pl: self.output_y,
                         self.k_data_data: self.k_np,
                         self.stability_eps: self.current_stability_eps})
          tf.logging.info(
              "Computed L_DD in %.3f secs"% (time.time() - start_time))
          break
    • Kernelはnngp.pyファイルでNGPKernelクラスが実装されている。それは__init__でインスタンス発生させられ、メンバー関数がTensorFlowを使ってテンソル・セッション対象にしてある。したがって、ひとたびSesseionが始まるとNNGPKernelクラスオブジェクトのカーネル行列(というテンソル)の値が計算される