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))
-
-
- を見直してみる。これは、x_plなるテンソルを引数として、reshape()関数の返り値であるテンソルを返す関数とも見える。TensorFlowでは、テンソルをとってテンソルを返すその動作が「テンソル」なので、この行は、k_data_dataというテンソル(テンソルを取ってテンソルを返す計算器としてのテンソルでもあり、その出力としてのテンソル(次に何かの入力になる、という意味での計算対象としてのテンソル)
- 以下、色々なところでsess.run()が出てくるが、すでにテンソルとしての宣言が済んでいるオブジェクト(クラス作成のときにTensorFlowのテンソルオブジェクトとして作られている者たち)の間で行われる、「テンソルを取ってテンソルを返す」という関数として定義された〜それもテンソルとみなせる〜の塊が処理されて、sess.run()関数で指定されたテンソルの計算結果の「値」が取り出される
- ここで言う、「テンソルを取ってテンソルを返す」というとき、普通のテンソル・テンソル=テンソルの計算では、「多次元アレイとしてのテンソルの計算規則」が成り立つことだけでなく、TensorFlow的に定義されたテンソル(numpyのarray)ではあるものの、それのdimensionは必ずしも、テンソル計算のdimensionルールと合致していないようなテンソル計算も含む(らしい)
- この後、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クラスオブジェクトのカーネル行列(というテンソル)の値が計算される