円周率を計算するコードをTensorFlowで書いてみる

# 本稿の目的
![tensorflow-lead.jpg](https://qiita-image-store.s3.amazonaws.com/0/126750/20eb0d8d-67a1-ab85-b542-f914f49f2df4.jpeg)
　TensorFlow...かっこいい名前ですね．ロゴとかクール・クーラー・クーレストの三拍子です．坂本ですね．ただこのライブラリに限った話ではないと思いますが，少し記述に癖があって個人的に描きづらいというのが第一印象です．そこで，自分へのメモとして，もしくは成長の一環としてここに記事を残してみようと思います．

　今回私は初めから Deep な沼に入水するのではなく，勉強の一歩目として TensorFlow を用いて円周率を求めるコードを書きました．記述言語であるpython，TensorFlowともに未熟者ですがどうぞ宜しくお願いします．また，両ツールともに現在勉強中であるために，間違ったことを記述している場合があります．訂正コメントなどでご指摘・ご指導いただけたら幸いです．

# TensorFlow
TensorFlowは機械学習のライブラリです．詳細は[公式ページ](https://www.tensorflow.org/)をご覧ください．
また，TensorFlowが行う処理方法について以下のサイトが非常にわかりやすく参考になりました．

 - [TensorFlow で Hello World を動かしてみた & その解説](http://dev.classmethod.jp/machine-learning/tensorflow-hello-world/)

# 環境
- OS X El Capitan 10.11.4
- python 2.7.10

# 円周率
円周率とは円に関する数学定数のひとつで......必要ないですね．あれです．3.14159です．私は3.14で覚えさせられ，個人で3.14159で覚えましたが皆さんはどうでしたか？巷では3で教育されるとの噂が流れましたが，[文部科学省のホームページ](http://www.mext.go.jp/a_menu/shotou/cs/1321018.htm)には否定する記述がありましたので真相はわからないです．
ちなみに，円周率は無理数で値が無限に続きますが，そのうち[10万桁を暗唱した](http://worldrecord314.com/)かたがいらっしゃって16時間30分かかったそうです．カプ麺330個作れます．すごいですね．もうお腹いっぱいです．

　話を戻します．円周率を求める公式にはたくさんの種類があります．そのいくつかの公式を見てみると「多角形を利用した求め方」や「モンテカルロ法を用いて求める方法」などがあり面白そうですが，今回はなんとなく名前がBP法に似ていたBBP系の公式を利用したいと思います．

## BBP
BBPは以下の式で表されます．

```math
\pi = \sum_{k=0}^{\infty}\frac{1}{16^k}\left( \frac{4}{8k+1} - \frac{2}{8k+4} - \frac{1}{8k+5} - \frac{1}{8k+6} \right)
```

この式の詳細は私は興味な...分かりませんので本稿では触れません．詳しい内容は[円周率.jp/BBP系公式](http://xn--w6q13e505b.jp/formula/bbp.html)で確認してください．
　今回のコードはこの公式を用います．

# コード
はじめに `tensorflow` のライブラリをインポートします．長いので短く `tf` と別名でインポートします．

```py
import tensorflow as tf
```
次にBBPの公式を書きます！

```py
def BBP(k_):
  result = tf.div((4./(8.*k_+1.)-1./(4.*k_+2.)-1./(8.*k_+5.)-1./(8.*k_+6.)), tf.pow(16., k_))
  return result
```

横に長い......
tensorflow ライブラリには [Math API](https://www.tensorflow.org/versions/r0.8/api_docs/python/math_ops.html#math) も存在しますので，それを用いています．
`tf.div(x, y) -> return x / y`
`tf.pow(x, y) -> return x^y`
他にも多数存在します．詳細は上記のリンク先から確認してください． 
なぜそこだけ使ったかなどは使ってみたかっただけですので追及しないでいただけると嬉しいです．
スカラーの計算に使ってもありがたみがあまりないですね．

そして，変数を用意します．
変数の定義には `tf.placeholder()` と `tf.Variable()` を用います．

```py
# Variables
sum_ = tf.Variable(0.)      # 全てを足し合わせた結果を入れていく変数

# Placeholders
k_ = tf.placeholder("float32")        # 定義する最大のkを受け取る
output = tf.placeholder("float32")    # 実際に計算された値を受け取る
```

`tf.placeholder()` `tf.Variable()`の二つについてですが，私の中でいまいちパッとしていないのが現状です．もっと良いコードがあるのだと思います．

変数を定義した場合，それらを初期化しなければなりません．
そこで以下の文で初期化を行います．

```py
model = tf.initialize_all_variables()
```
次に実際に処理方法を記述します．

```py
bbp_op = BBP(k_)  # bbp_opが呼ばれたらBBP関数にK_を渡す
sum_results = tf.assign(sum_, tf.add(sum_, output))  # これまでの計算結果を足し合わせる
```

下ごしらえは以上です．
次からは，ここまでで用意した変数や関数を組み立てていき，実際に処理を行うことを考えていきます．

まずは，これから`Session()`をはじめることを記述します．これ以下のコードが実際に計算を命令していくところになります．

```py
with tf.Session() as sess:
  # 処理する内容・手順を記述
```
処理を始める前に変数の初期化を実行します．

```py
sess.run(model)  # init
```

BBP は実際には無限の足しあわせを行いますが，今回は簡略のために ~~5~~ 10 回とします．(2016-5-26 訂正)
そこで実行させるのは `bbp_op` `sum_results` の二つになります．ここで `Session()` で指定して記述すると実行できます．

```py
bbp_ = sess.run(bbp_op, feed_dict={k_: i})
res_ = sess.run(sum_results, feed_dict={output: bbp_})

# 訂正
-) print("BBP Result: {0}  Accuracy: {1}").format(bbp_, res_)
+) print("Accuracy: {0}  BBP Result: {1}").format(bbp_, res_)
```
`feed_dict={}`で変数をリプレスすることができます．今回はカウントと計算結果を置いています．
コードの説明は以上になります．

# 結果

```
$ python pi.py
Accuracy: 3.13333320618	BBP Result: 3.13333320618
Accuracy: 0.00808913446963	BBP Result: 3.14142227173
Accuracy: 0.00016492389841	BBP Result: 3.14158725739
Accuracy: 5.06721880811e-06	BBP Result: 3.14159226418
Accuracy: 1.8789302203e-07	BBP Result: 3.14159250259
Accuracy: 7.76774555789e-09	BBP Result: 3.14159250259
Accuracy: 3.44793082974e-10	BBP Result: 3.14159250259
Accuracy: 1.60918778302e-11	BBP Result: 3.14159250259
Accuracy: 7.79569768114e-13	BBP Result: 3.14159250259
Accuracy: 3.88711415486e-14	BBP Result: 3.14159250259
```
計算自体はできました．回数を重ねるにつれて精度が上がっていることがわかります．
しかしながら案の定サチりましたね．端的に`float`を用いたことが原因でしょう．
今回はこの問題は本題ではないってことでおっけーとします．（時間が空いたらやるかも）

# まとめ
今回 TensorFlow を用いて円周率を計算するコードを作成しました．
結果，欲しい形を出力できたとおもいますが，まだまだ勉強不足や綺麗な書き方などたくさんあると思います．まだまだ勉強ですね．
もっと，公式サイトを読んで勉強していきたいと思います．

# コード全体

```py:pi.py
"""  
円周率を求めるプログラムをtensorflowで作成
"""  
import tensorflow as tf

def PI():
  # BBP method 
  def BBP(k_):
    result = tf.div((4./(8.*k_+1.)-1./(4.*k_+2.)-1./(8.*k_+5.)-1./(8.*k_+6.)), tf.pow(16., k_))
    return result

  # Variables
  sum_ = tf.Variable(0.)

  # Placeholders
  k_ = tf.placeholder("float32")
  output = tf.placeholder("float32")

  model = tf.initialize_all_variables()
  
  bbp_op = BBP(k_)  # calculate the pi
  sum_results = tf.assign(sum_, tf.add(sum_, output)) # Summention of all results
  
  with tf.Session() as sess:
    sess.run(model)  # init

    for i in range(5): 
      bbp_ = sess.run(bbp_op, feed_dict={k_: i})
-)      print("BBP Result: {0}  Accuracy: {1}").format(bbp_, sess.run(sum_results, feed_dict={output: bbp_}))
+)      print("Accuracy: {0}  BBP Result: {1}").format(bbp_, sess.run(sum_results, feed_dict={output: bbp_}))
if __name__ == '__main__':
  PI()
```

# Reference
- [TensorFlow](https://www.tensorflow.org/)
- [NNをちょっとかじった人のための，はじめてのTensorFlow](http://qiita.com/sergeant-wizard/items/55256ac6d5d8d7c53a5a)
- [TensorFlowでHello Worldを動かしてみた&その解説](http://dev.classmethod.jp/machine-learning/tensorflow-hello-world/)