TensorFlowを使ってIoTデータを人工知能してみる - 練習

[TensorFlowを使ってIoTデータを人工知能してみる](http://qiita.com/egplnt/items/3246711349bc1d97bdb8)の練習用に、学習と判定を繰り返し行えるよう、それぞれ別のportを開けてPOSTできるようにしました。また練習用に[Fisherのiris data set](https://en.wikipedia.org/wiki/Iris_flower_data_set)を使用します。

# 1. TensorFlowを準備します
1. [BluemixのDockerコンテナ環境にTensorFlowをインストールする](http://qiita.com/takeyan/items/7956ec65e9cca367f8bd)を参照し、Bluemix環境にTensorFlowを準備します
2. 「デプロイ」ステージを構成するステップで、portとして、8888以外に、6006,9000,9001も開けておくようにします
![スクリーンショット 2016-11-07 8.56.54.png](https://qiita-image-store.s3.amazonaws.com/0/125842/268c7e8a-0c9d-757d-0a41-c245c315f613.png)

3. ビルド⇨デプロイが完了すると下記のような画面が表示されます。デプロイの「最終実行の結果」欄に表示される外向け（パブリック）IPアドレスを確認します
![161111_white_page.jpg](https://qiita-image-store.s3.amazonaws.com/0/125842/ad85890f-c375-d39d-a9d0-32a27c4e0183.jpeg)

4. Bluemixのダッシュボードから、デプロイされたDockerコンテナーを開き、パブリックIPアドレス（前項と同じ）とプライベートIPアドレスを確認します
![161111_white_page3.jpeg](https://qiita-image-store.s3.amazonaws.com/0/125842/994740c6-a8c7-011e-f5d1-38def0742226.jpeg)

5. Chromeなどのブラウザで上記のパブリックIPアドレスの8888ポートへアクセスします。画面右上の「New」プルダウンメニューから「Terminal」でターミナル画面を開きます。
![スクリーンショット 2016-11-11 12.13.18.png](https://qiita-image-store.s3.amazonaws.com/0/125842/5817a834-b387-bd82-a0dd-3925c41e1317.png)

6. このページでご紹介しているサンプルを実行するため、表示されたターミナル画面でflaskとpandasをインストールします。flaskは`pip istall flask`などで、pandasは`pip install pandas`などでインストール可能です
![スクリーンショット 2016-11-11 12.16.17.png](https://qiita-image-store.s3.amazonaws.com/0/125842/dc6571d6-7296-a9a1-a6d2-6d79fa64431a.png)

7. 画面右上の「New」プルダウンメニューから新しいフォルダーを作成し、適当な名前にrenameしておきます
![スクリーンショット 2016-11-11 12.18.30.png](https://qiita-image-store.s3.amazonaws.com/0/125842/86ebf757-913e-1680-b725-79de91eeb37b.png)

8. 続いて作成したフォルダー内に入り、同様に画面右上の「New」プルダウンメニューからText Fileを作成し、このページの下記のセクションにあるサンプルのpythonプログラムをコピペしていきます

# 2. サンプルを準備します
- 用意したTensorFlow環境に下記のソースを配置します。
- `model.py`でモデルを定義します
    - 隠れ層2でそれぞれ32、16個のReLuで構成しています
    - `COLUMN_SIZE`に対象にするデータの項目数を指定します。iris data setでは、'Sepal Length', 'Sepal Width', 'Petal Length', 'Petal Width'を対象にしています
    - `actual_class`に判定に使用する目的変数の項目数を指定します。iris data setでは setosa = 0, versicolor = 1, virginica = 2の３個の値を使用しています
    - ここではGradientDescentOptimizerを使用していますが、AdamOptimizerなど異なるアルゴリズムを使用する事も可能です
    - 学習3０回毎に学習とテストの精度を出力します
    - その他詳細は[TensorFlowのTutorial](https://www.tensorflow.org/versions/r0.9/tutorials/index.html)をご参照ください

```py:model.py
# -*- coding: utf-8 -*-

import tensorflow as tf
import numpy as np
import os

class Model:
    HIDDEN_UNIT_SIZE  = 32
    HIDDEN_UNIT_SIZE2 = 16
    # 対象データの項目数
    COLUMN_SIZE = 4

    def __init__(self):
        self.__setup_placeholder()
        self.__setup_model()
        self.__setup_ops()

    def __enter__(self):
        self.session = tf.Session()
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.session.close()
        return False

    def save(self, path):
        saver = tf.train.Saver()
        saver.save(self.session, path)

    def restore(self, path):
        saver = tf.train.Saver()
        saver.restore(self.session, path)

    def __setup_placeholder(self):
        column_size = Model.COLUMN_SIZE
        self.iot_data   = tf.placeholder("float", [None, column_size])
        # 目的変数が３値
        self.actual_class = tf.placeholder("float", [None, 3])
        self.keep_prob    = tf.placeholder("float")
        self.label        = tf.placeholder("string")

    def __setup_model(self):
        column_size = Model.COLUMN_SIZE
        w1 = tf.Variable(tf.truncated_normal([column_size, Predictor.HIDDEN_UNIT_SIZE], stddev=0.1))
        b1 = tf.Variable(tf.constant(0.1, shape=[Predictor.HIDDEN_UNIT_SIZE]))
        h1 = tf.nn.relu(tf.matmul(self.iot_data, w1) + b1)

        w2 = tf.Variable(tf.truncated_normal([Predictor.HIDDEN_UNIT_SIZE, Predictor.HIDDEN_UNIT_SIZE2], stddev=0.1))
        b2 = tf.Variable(tf.constant(0.1, shape=[Predictor.HIDDEN_UNIT_SIZE2]))
        h2 = tf.nn.relu(tf.matmul(h1, w2) + b2)

        h2_drop = tf.nn.dropout(h2, self.keep_prob)
        # 目的変数が３値
        w2 = tf.Variable(tf.truncated_normal([Predictor.HIDDEN_UNIT_SIZE2, 3], stddev=0.1))
        # 目的変数が３値
        b2 = tf.Variable(tf.constant(0.1, shape=[3]))
        self.output = tf.nn.softmax(tf.matmul(h2_drop, w2) + b2)

    def __setup_ops(self):
        cross_entropy = -tf.reduce_sum(self.actual_class * tf.log(self.output))
        self.summary = tf.scalar_summary(self.label, cross_entropy)
        # 異なるアルゴリズムを使用
        # self.train_op = tf.train.AdamOptimizer(0.0001).minimize(cross_entropy)
        self.train_op = tf.train.GradientDescentOptimizer(0.0001).minimize(cross_entropy)
        self.merge_summaries = tf.merge_summary([self.summary])
        correct_prediction = tf.equal(tf.argmax(self.output,1), tf.argmax(self.actual_class,1))
        self.accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

class Trainer(Model):

    def train(self, steps, data):
        self.__prepare_train(self.session)
        for i in range(steps):
            self.__do_train(self.session, i, data)
            if i %20 == 0:
                self.__add_summary(self.session, i, data)
                self.__print_status(self.session, i, data)

    def __prepare_train(self, session):
        self.summary_writer = tf.train.SummaryWriter('logs', graph=session.graph)
        if not os.path.exists("./model.ckpt"):
            session.run(tf.initialize_all_variables())
            print 'variables initialized'

    def __do_train(self, session, i, data):
        session.run(self.train_op, feed_dict=self.train_feed_dict(data))

    def __add_summary(self, session, i, data):
        summary_str = session.run(self.merge_summaries, feed_dict=self.train_feed_dict(data))
        summary_str += session.run(self.merge_summaries, feed_dict=self.test_feed_dict(data))
        self.summary_writer.add_summary(summary_str, i)

    def __print_status(self, session, i, data):
        train_accuracy = session.run(self.accuracy, feed_dict=self.train_feed_dict(data))
        test_accuracy  = session.run(self.accuracy, feed_dict=self.test_feed_dict(data))
        print 'step {} ,train_accuracy={} ,test_accuracy={} '.format(i, train_accuracy, test_accuracy)

    def train_feed_dict(self, data):
        return {
            self.iot_data: data.train_data(),
            self.actual_class: data.train_up_down(),
            self.keep_prob: 0.8,
            self.label: "train"
        }

    def test_feed_dict(self, data):
        return {
            self.iot_data: data.test_data(),
            self.actual_class: data.test_up_down(),
            self.keep_prob: 1,
            self.label: "test"
        }


class Predictor(Model):

    def predict( self, data ):
        return self.session.run(tf.argmax(self.output,1), feed_dict=self.predict_feed_dict(data))

    def predict_feed_dict(self, data):
        return {
            self.iot_data: data,
            self.keep_prob: 1,
            self.label: "predict"
        }



```

- `iot_results_loader.py`で学習用のデータをロードします
    - `outcome`項で結果を定義しています
    - 全体の２/３を学習データに、１/３をテストデータに使用しています

```py:iot_results_loader.py
# -*- coding: utf-8 -*-

import pandas as pd

class IoTResults:

    def __float__(data):
        return 1.0

    def __init__(self, data):
        self.raw  = data.copy()
        self.data = data

    def predictor_data(self):
        return self.data

    def train_data(self):
        return self.__drop_outcome(self.__train_data())

    def test_data(self):
        return self.__drop_outcome(self.__test_data())

    def train_up_down(self):
        return self.__good_bad(self.__train_data()["outcome"])

    def test_up_down(self):
        return self.__good_bad(self.__test_data()["outcome"])

    def __train_data(self):
        # 全データの 2/3 を訓練データとして使用
        return self.data.loc[lambda df: df.index % 3 != 0, :]

    def __test_data(self):
        # 全データの 1/3 をテストデータとして使用
        return self.data.loc[lambda df: df.index % 3 == 0, :]

    def __drop_outcome(self, data):
        return data.drop("outcome", axis=1)

    def __good_bad(self, outcome):
        return outcome.apply(
            lambda p: pd.Series([
                1 if p <=  0 else 0,
                1 if p ==1  else 0,
                1 if p > 1  else 0
            ], index=['setosa', 'versicolor', 'virginica']))


class IoTResultsLoader:

    def retrieve_iot_data(self, data):
        df = pd.DataFrame(data)
        return df


```

- `trainer.py`へ学習用データをPOSTします
    - flaskで9000ポートでアクセスします。TenserFlowのDockerコンテナのデプロイによりBluemixから提供されるIPアドレスのうち、パブリックIPを使用してブラウザからアクセスします。flaskサーバーはプライベートIPへ向け9000ポートを開けます
    - 必要に応じ学習回数を変更して試してみます
    - 学習結果が./model.ckptに保存されます

```py:trainer.py
# -*- coding: utf-8 -*-

import os
import pandas as pd
from flask import Flask, jsonify, request
from iot_results_loader import *
from model import *

trainer = Trainer()
trainer.__enter__()
if os.path.exists("./model.ckpt"):
    trainer.restore("./model.ckpt")

loader = IoTResultsLoader()

# webapp
app = Flask(__name__)

@app.route('/api/trainer', methods=['POST'])
def train():
    data2 = pd.DataFrame(eval(request.data))
    data3 = IoTResults(loader.retrieve_iot_data(data2))
    # 学習回数
    trainer.train(60, data3)
    trainer.save("./model.ckpt")
    return 'train done'

if __name__ == '__main__':
    app.run(host='172.xx.xx.xx', port=9000)


```

- `predictor.py`へ判定用データを送付し結果を確認します
    - flaskで9001ポートでアクセスします。TenserFlowのDockerコンテナのデプロイによりBluemixから提供されるIPアドレスのうち、パブリックIPを使用してブラウザからアクセスします。flaskサーバーはプライベートIPへ向け9000ポートを開けます


```py:predictor.py
# -*- coding: utf-8 -*-

import os
import pandas as pd
from flask import Flask, jsonify, request
from iot_results_loader import *
from model import *

predictor = Predictor()

# webapp
app = Flask(__name__)

@app.route('/api/predictor', methods=['POST'])
def predict():
    predictor.__enter__()
    if os.path.exists("./model.ckpt"):
        predictor.restore("./model.ckpt")
    data = pd.DataFrame(eval(request.data))
    results = predictor.predict(IoTResults(data).predictor_data().iloc[[0]])
    predictor.__exit__(1,1,0)
    return jsonify(result=("2 virginica!" if results[0] == 2 else "1 versicolor!" if results[0] == 1 else "0 setosa!"))
        
if __name__ == '__main__':
    app.run(host='172.xx.xx.xx', port=9001)

```

- Node-REDのフローを用意します。フロー中の「trainerへ」と「predictorへ」のhttpノードを開き、前出のパブリックIPアドレスを指定します

```lang:TensorFlowへアクセスするフロー
[{"id":"7283e6b5.d9c0b","type":"http request","z":"97be8461.26496","name":"predictorへ","method":"POST","ret":"txt","url":"http://xx.xx.xx.xx:9001/api/predictor","tls":"","x":570,"y":280,"wires":[["8bdeafef.6ac928"]]},{"id":"c306faf.2136088","type":"inject","z":"97be8461.26496","name":"起動","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":110,"y":240,"wires":[["72f86ec9.57bc"]]},{"id":"72f86ec9.57bc","type":"template","z":"97be8461.26496","name":"iris test: 0 setosa","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{'Sepal Length': [4.9], 'Sepal Width':[3.1], 'Petal Length':[1.5], 'Petal Width':[0.1]}","x":330,"y":240,"wires":[["7283e6b5.d9c0b"]]},{"id":"2d37564e.bc98fa","type":"template","z":"97be8461.26496","name":"iris test: 1 versicolor","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{'Sepal Length': [5], 'Sepal Width':[2.3], 'Petal Length':[3.3], 'Petal Width':[1]}","x":340,"y":280,"wires":[["7283e6b5.d9c0b"]]},{"id":"977c9b69.d200d","type":"template","z":"97be8461.26496","name":"iris test: 2 virginica","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{'Sepal Length': [6.4], 'Sepal Width':[2.8], 'Petal Length':[5.6], 'Petal Width':[2.2]}","x":330,"y":320,"wires":[["7283e6b5.d9c0b"]]},{"id":"8f973a5b.a6df7","type":"inject","z":"97be8461.26496","name":"起動","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":110,"y":280,"wires":[["2d37564e.bc98fa"]]},{"id":"9d96a668.46b6a8","type":"inject","z":"97be8461.26496","name":"起動","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":110,"y":320,"wires":[["977c9b69.d200d"]]},{"id":"1e9bc0f2.e7629f","type":"http request","z":"97be8461.26496","name":"trainerへ","method":"POST","ret":"txt","url":"http://xx.xx.xx.xx:9000/api/trainer","tls":"","x":560,"y":120,"wires":[["74e86540.88dde4"]]},{"id":"8bdeafef.6ac928","type":"function","z":"97be8461.26496","name":"取り出し","func":"var data = JSON.parse(msg.payload);\nmsg.payload = data.result;\nreturn msg;\n","outputs":1,"noerr":0,"x":720,"y":280,"wires":[["5305aeb5.15d38"]]},{"id":"5305aeb5.15d38","type":"template","z":"97be8461.26496","name":"出力","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"これは {{payload}} ですね！","x":570,"y":360,"wires":[["a0a1c21a.92fd08"]]},{"id":"a0a1c21a.92fd08","type":"debug","z":"97be8461.26496","name":"","active":true,"console":"false","complete":"false","x":730,"y":360,"wires":[]},{"id":"f1d8f1f3.86eac","type":"template","z":"97be8461.26496","name":"iris training","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"            {'Sepal Length': [6.4,5,4.9,4.9,5.7,4.4,5.4,6.9,6.7,5.1,5.2,6.9,5.8,5.4,7.7,6.3,6.8,7.6,6.4,5.7,6.7,6.4,5.4,6.1,7.2,5.2,5.8,5.9,5.4,6.7,6.3,5.1,6.4,6.8,6.2,6.9,6.5,5.8,5.1,4.8,7.9,5.8,6.7,5.1,4.7,6,4.8,7.7,4.6,7.2,5,6.6,6.1,5,7,6,7.4,5.8,6.2,5,5.6,6.7,6.3,6.4,6.2,7.3,4.4,7.2,6.5,5,4.7,6.6,5.5,7.7,6.1,4.9,5.5,5.7,6,6.4,5.4,6.1,6.5,5.6,6.3,4.9,6.8,5.7,6,5,6.5,6.1,5.1,4.6,6.5,4.6,4.6,7.7,5.9,5.1,4.9,4.9,4.5,5.8,5,5.2,5.3,5,5.6,4.8,6.3,5.7,5,6.3,5,5.5,5.7,4.4,4.8,5.5,5.9,6.9,5.1,6,5.5,6.2,5.5,6.3,5.6,6.7,7.1,4.3,5.6,5.5,6,5.1,5.7,4.8,5.1,5.7,5.4,5.6,6.3,6.3,5.8,6.1,5.2,6.7,6.7,6.4],\n             'Sepal Width': [2.8,2.3,2.5,3.1,3.8,3.2,3.4,3.1,3.1,3.7,2.7,3.1,4,3.9,3.8,3.3,3.2,3,3.2,4.4,3.3,2.8,3.9,2.6,3,3.5,2.6,3,3,3,2.3,2.5,3.2,3,2.8,3.2,3.2,2.8,3.8,3,3.8,2.7,3,3.8,3.2,2.2,3.4,2.6,3.6,3.2,3.3,3,2.8,3.2,3.2,3,2.8,2.7,3.4,2,2.5,3.1,2.5,3.1,2.2,2.9,3,3.6,3,3.4,3.2,2.9,3.5,3,3,3.1,2.4,2.9,2.9,2.7,3.7,2.9,2.8,2.7,3.4,3.1,2.8,2.8,2.7,3.5,3,2.8,3.5,3.1,3,3.4,3.2,2.8,3.2,3.8,3,2.4,2.3,2.7,3.4,3.4,3.7,3.6,2.9,3.1,2.7,2.8,3,3.3,3.5,2.6,3,2.9,3,2.4,3,3.1,3.3,3.4,2.5,2.9,4.2,2.8,3,2.5,3,3,2.8,2.3,2.2,3.5,2.6,3.4,3.4,2.5,3.4,3,2.9,2.5,2.7,3,4.1,3.1,3.3,2.9],\n             'Petal Length': [5.6,3.3,4.5,1.5,1.7,1.3,1.5,5.1,4.4,1.5,3.9,4.9,1.2,1.7,6.7,4.7,5.9,6.6,5.3,1.5,5.7,5.6,1.3,5.6,5.8,1.5,4,5.1,4.5,5,4.4,3,4.5,5.5,4.8,5.7,5.1,5.1,1.5,1.4,6.4,5.1,5.2,1.9,1.6,5,1.6,6.9,1,6,1.4,4.4,4,1.2,4.7,4.8,6.1,5.1,5.4,3.5,3.9,5.6,5,5.5,4.5,6.3,1.3,6.1,5.5,1.5,1.3,4.6,1.3,6.1,4.9,1.5,3.8,4.2,4.5,5.3,1.5,4.7,4.6,4.2,5.6,1.5,4.8,4.5,5.1,1.3,5.2,4.7,1.4,1.5,5.8,1.4,1.4,6.7,4.8,1.6,1.4,3.3,1.3,4.1,1.6,1.4,1.5,1.4,3.6,1.6,4.9,4.1,1.6,6,1.6,4.4,4.2,1.4,1.4,3.7,4.2,5.4,1.7,4.5,4,4.3,1.4,5.1,4.1,5.8,5.9,1.1,4.9,4,4,1.4,3.5,1.9,1.5,5,1.7,4.5,5.6,4.9,3.9,4.6,1.5,4.7,5.7,4.3],\n             'Petal Width': [2.2,1,1.7,0.1,0.3,0.2,0.4,2.3,1.4,0.4,1.4,1.5,0.2,0.4,2.2,1.6,2.3,2.1,2.3,0.4,2.1,2.1,0.4,1.4,1.6,0.2,1.2,1.8,1.5,1.7,1.3,1.1,1.5,2.1,1.8,2.3,2,2.4,0.3,0.3,2,1.9,2.3,0.4,0.2,1.5,0.2,2.3,0.2,1.8,0.2,1.4,1.3,0.2,1.4,1.8,1.9,1.9,2.3,1,1.1,2.4,1.9,1.8,1.5,1.8,0.2,2.5,1.8,0.2,0.2,1.3,0.2,2.3,1.8,0.1,1.1,1.3,1.5,1.9,0.2,1.4,1.5,1.3,2.4,0.1,1.4,1.3,1.6,0.3,2,1.2,0.3,0.2,2.2,0.3,0.2,2,1.8,0.2,0.2,1,0.3,1,0.4,0.2,0.2,0.2,1.3,0.2,1.8,1.3,0.2,2.5,0.6,1.2,1.2,0.2,0.1,1,1.5,2.1,0.5,1.6,1.3,1.3,0.2,1.5,1.3,1.8,2.1,0.1,2,1.3,1,0.2,1,0.2,0.2,2,0.2,1.5,1.8,1.5,1.2,1.4,0.1,1.5,2.5,1.3],\n             'outcome': [2,1,2,0,0,0,0,2,1,0,1,1,0,0,2,1,2,2,2,0,2,2,0,2,2,0,1,2,1,1,1,1,1,2,2,2,2,2,0,0,2,2,2,0,0,2,0,2,0,2,0,1,1,0,1,2,2,2,2,1,1,2,2,2,1,2,0,2,2,0,0,1,0,2,2,0,1,1,1,2,0,1,1,1,2,0,1,1,1,0,2,1,0,0,2,0,0,2,1,0,0,1,0,1,0,0,0,0,1,0,2,1,0,2,0,1,1,0,0,1,1,2,0,1,1,1,0,2,1,2,2,0,2,1,1,0,1,0,0,2,0,1,2,1,1,1,0,1,2,1]}","x":310,"y":120,"wires":[["1e9bc0f2.e7629f"]]},{"id":"6027aa75.7a7a24","type":"inject","z":"97be8461.26496","name":"起動","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":110,"y":120,"wires":[["f1d8f1f3.86eac"]]},{"id":"74e86540.88dde4","type":"debug","z":"97be8461.26496","name":"","active":true,"console":"false","complete":"false","x":730,"y":120,"wires":[]},{"id":"d1575987.34ea48","type":"comment","z":"97be8461.26496","name":"学習用フロー","info":"","x":90,"y":80,"wires":[]},{"id":"bc6ca509.b1321","type":"comment","z":"97be8461.26496","name":"判定用フロー","info":"","x":90,"y":200,"wires":[]}]


```

![スクリーンショット 2016-11-11 12.36.10.png](https://qiita-image-store.s3.amazonaws.com/0/125842/3a9b6fa3-5f5e-9bef-f688-92868e3a450b.png)


# 3. 実行します

1. Terminalを開き、`python trainer.py`で学習用サーバーを起動します
![スクリーンショット 2016-11-11 12.34.36.png](https://qiita-image-store.s3.amazonaws.com/0/125842/f0098a5f-c758-4bda-ca05-1b825bc89b89.png)

2. もう一つ別のTerminalを開き、`python predictor.py`で判定用サーバーを実行します
![スクリーンショット 2016-11-11 12.35.17.png](https://qiita-image-store.s3.amazonaws.com/0/125842/dd825d42-dcb7-0e8e-2eb3-e14769a8f3d8.png)

3. 上記のNode-REDフローの「学習用フロー」を起動し、学習状況をtrainer.pyのターミナルから確認します
![スクリーンショット 2016-11-07 8.46.43.png](https://qiita-image-store.s3.amazonaws.com/0/125842/fcc03b7f-6c7c-d30e-e8aa-7a32c88a796d.png)
![スクリーンショット 2016-11-07 8.51.48.png](https://qiita-image-store.s3.amazonaws.com/0/125842/0ab80f3d-711b-ff44-70bd-d9d29b48339f.png)
4. 学習の進行に応じ、随時「判定用フロー」を起動し、判定結果を確認します
![スクリーンショット 2016-11-07 8.52.49.png](https://qiita-image-store.s3.amazonaws.com/0/125842/8d6f6d06-965a-347c-7494-ec91a5ffcd54.png)

5. `python -m tensorflow.tensorboard --logdir=./logs`などでtensorboardを稼働しブラウザで6006ポートへアクセスして学習の状況などを確認します。IPアドレスはTenserFlowのDockerコンテナのデプロイによりBluemixから提供されるIPアドレスのうち、パブリックIPを使用してブラウザからアクセスします。

![スクリーンショット 2016-10-31 15.11.26.png](https://qiita-image-store.s3.amazonaws.com/0/125842/6bf2a11d-af40-e359-d06c-03e562cdc3d7.png)
![スクリーンショット 2016-10-31 16.13.53.png](https://qiita-image-store.s3.amazonaws.com/0/125842/49f763ae-5758-3a6a-19fe-b60d9f56e2fe.png)

### 参考

[TensorFlowを使った為替(FX)のトレードシステムを作るチュートリアル](http://qiita.com/jiji_platform/items/268377c542706e6f44b1)を大変参考にさせていただきました。どうもありがとうございます。