#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Explanation: Copyright 2019 The TensorFlow Authors.
End of explanation
"""


import tensorflow as tf

import pandas as pd

"""
Explanation: 事前作成された Estimator
<table class="tfo-notebook-buttons" align="left">
  <td> <img src="https://www.tensorflow.org/images/tf_logo_32px.png"><a target="_blank" href="https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ja/tutorials/estimator/premade.ipynb">TensorFlow.org で表示</a> </td>
  <td> <img src="https://www.tensorflow.org/images/colab_logo_32px.png"><a target="_blank" href="https://github.com/tensorflow/docs-l10n/blob/master/site/ja/tutorials/estimator/premade.ipynb">Google Colab で実行</a> </td>
  <td> <img src="https://www.tensorflow.org/images/GitHub-Mark-32px.png"><a target="_blank" href="https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ja/tutorials/estimator/premade.ipynb">GitHub でソースを表示</a> </td>
  <td> <img src="https://www.tensorflow.org/images/download_logo_32px.png"><a href="https://storage.googleapis.com/tensorflow_docs/docs/site/en/tutorials/estimator/premade.ipynb">ノートブックをダウンロード</a> </td>
</table>


警告: 新しいコードには Estimators は推奨されません。Estimators は v1.Session スタイルのコードを実行しますが、これは正しく記述するのはより難しく、特に TF 2 コードと組み合わせると予期しない動作をする可能性があります。Estimators は、互換性保証の対象となりますが、セキュリティの脆弱性以外の修正は行われません。詳細については、移行ガイドを参照してください。

このチュートリアルでは、Estimator を使用して、TensorFlow でアヤメの分類問題を解決する方法を示します。Estimator は、レガシー TensorFlow における完全なモデルの高レベルの表現です。詳細については、Estimatorをご覧ください。
注意: TensorFlow 2.0 では、Keras API でも同じタスクを実行でき、より学習しやすい API とされています。はじめて学習する場合は、Keras から着手することをお勧めします。
まず最初に
始めるには、最初に TensorFlow と必要となる多数のライブラリをインポートします。
End of explanation
"""


CSV_COLUMN_NAMES = ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth', 'Species']
SPECIES = ['Setosa', 'Versicolor', 'Virginica']

"""
Explanation: データセット
このドキュメントのサンプルプログラムは、アヤメの花を、萼片と花弁のサイズに基づいて、3 つの品種に分類するモデルを構築してテストします。
モデルのトレーニングには、Iris データセットを使用します。Iris データセットには 4 つの特徴量と 1 つのラベルが含まれます。4 つの特徴量は、次に示す各アヤメの植物学的特性を識別します。

萼片の長さ
萼片の幅
花弁の長さ
花弁の幅

この情報に基づき、データを解析する上で役立ついくつかの定数を定義できます。
End of explanation
"""


train_path = tf.keras.utils.get_file(
    "iris_training.csv", "https://storage.googleapis.com/download.tensorflow.org/data/iris_training.csv")
test_path = tf.keras.utils.get_file(
    "iris_test.csv", "https://storage.googleapis.com/download.tensorflow.org/data/iris_test.csv")

train = pd.read_csv(train_path, names=CSV_COLUMN_NAMES, header=0)
test = pd.read_csv(test_path, names=CSV_COLUMN_NAMES, header=0)

"""
Explanation: 次に、Keras と Pandas を使用して、Iris データセットをダウンロードして解析します。トレーニング用とテスト用に別々のデータセットを維持することに注意してください。
End of explanation
"""


train.head()

"""
Explanation: データを検査し、4 つの浮動小数型の特徴量カラムと 1 つの int32 ラベルがあることを確認します。
End of explanation
"""


train_y = train.pop('Species')
test_y = test.pop('Species')

# The label column has now been removed from the features.
train.head()

"""
Explanation: 各データセットに対し、モデルが予測するようにトレーニングされるラベルを分割します。
End of explanation
"""


def input_evaluation_set():
    features = {'SepalLength': np.array([6.4, 5.0]),
                'SepalWidth':  np.array([2.8, 2.3]),
                'PetalLength': np.array([5.6, 3.3]),
                'PetalWidth':  np.array([2.2, 1.0])}
    labels = np.array([2, 1])
    return features, labels

"""
Explanation: Estimator を使ったプログラミングの概要
データのセットアップが完了したので、TensorFlow Estimator を使ってモデルを定義できます。Estimator は、tf.estimator.Estimator から派生したクラスです。TensorFlow は、一群の tf.estimator（LinearRegressor など）を提供しており、一般的な ML アルゴリズムを実装することができます。このほか、独自のカスタム Estimator を作成することもできますが、使用し始めには、事前作成済みの Estimator を使用することをお勧めします。
事前作成済みの Estimator に基づいて TensorFlow プログラムを記述するには、次のタスクを実行する必要があります。

1 つ以上の入力関数を作成する。
モデルの特徴量カラムを定義する。
Estimator をインスタンス化する。特徴量カラムとさまざまなハイパーパラメータを指定します。
Estimator オブジェクトに 1 つ以上のメソッドを呼び出す。データのソースとして適切な入力関数を渡します。

では、アヤメの分類において、これらのタスクをどのように実装するのか見てみましょう。
入力関数を作成する
トレーニング、評価、および予測を行うためのデータを提供する入力関数を作成する必要があります。
入力関数とは、次の要素タプルを出力する tf.data.Dataset オブジェクトを返す関数です。

features - 次のような Python ディクショナリ。
各キーが特徴量の名前である。
各値が、特徴量の値のすべてを含む配列である。


label - 各サンプルの label の値を含む配列。

入力関数の書式を示すために、単純な実装を次に示します。
End of explanation
"""


def input_fn(features, labels, training=True, batch_size=256):
    """An input function for training or evaluating"""
    # Convert the inputs to a Dataset.
    dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))

    # Shuffle and repeat if you are in training mode.
    if training:
        dataset = dataset.shuffle(1000).repeat()
    
    return dataset.batch(batch_size)


"""
Explanation: 入力関数を自分で作成すれば、features ディクショナリと label リストを好みに合わせて生成できるようにすることができますが、あらゆる種類のデータを解析できる TensorFlow の Dataset API を使用することをお勧めします。
Dataset API は、多数の一般的な事例を処理することができます。たとえば、Dataset API を使用すると、大量のファイルのレコードを並列して読み取り、単一のストリームに結合することが簡単に行えます。
この例では事を単純にするために、pandas でデータを読み込み、このメモリ内のデータから入力パイプラインを構築します。
End of explanation
"""


# Feature columns describe how to use the input.
my_feature_columns = []
for key in train.keys():
    my_feature_columns.append(tf.feature_column.numeric_column(key=key))

"""
Explanation: 特徴量カラムを定義する
特徴量カラムは、特徴量ディクショナリの生の入力データを、モデルがどのように使用すべきかを説明するオブジェクトです。Estimator モデルを作成する際に、モデルが使用する各特徴量を説明する特徴量カラムをモデルに渡します。tf.feature_column モジュールには、モデルに対してデータを表現するためのオプションが多数含まれています。
Iris については、4 つの生の特徴量は数値であるため、Estimator に対して、これら 4 つの各特徴量を 32 ビットの浮動小数点数型の値として表現するように命令する特徴量カラムを構築します。したがって、特徴カラムを作成するためのコードは、次のようになります。
End of explanation
"""


# Build a DNN with 2 hidden layers with 30 and 10 hidden nodes each.
classifier = tf.estimator.DNNClassifier(
    feature_columns=my_feature_columns,
    # Two hidden layers of 30 and 10 nodes respectively.
    hidden_units=[30, 10],
    # The model must choose between 3 classes.
    n_classes=3)

"""
Explanation: 特徴量カラムは、ここに示すものよりもはるかに高度なものに構築することができます。特徴量カラムの詳細については、こちらのガイドをご覧ください。
モデルが生の特徴量をどのように表現するかに関する記述を準備できたので、Estimator を構築することができます。
Estimator をインスタンス化する
アヤメの問題はよく知られた分類問題です。幸いにも、TensorFlow は、次のような事前作成済みの分類子 Estimator を複数用意しています。

tf.estimator.DNNClassifier: 多クラス分類を実行するディープモデルに使用。
tf.estimator.DNNLinearCombinedClassifier: ワイド＆ディープモデルに使用。
tf.estimator.LinearClassifier: 線形モデルに基づく分類子に使用。

アヤメの問題に関しては、tf.estimator.DNNClassifier が最適な選択肢と言えます。この Estimator をインスタンス化する方法を次に示します。
End of explanation
"""


# Train the Model.
classifier.train(
    input_fn=lambda: input_fn(train, train_y, training=True),
    steps=5000)

"""
Explanation: トレーニングして評価して予測する
Estimator オブジェクトを準備したので、次の項目を行うメソッド呼び出すことができます。

モデルをトレーニングする。
トレーニングされたモデルを評価する。
トレーニングされたモデルを使用して、予測を立てる。

モデルをトレーニングする
次のように、Estimator の train メソッドを呼び出して、モデルをトレーニングします。
End of explanation
"""


eval_result = classifier.evaluate(
    input_fn=lambda: input_fn(test, test_y, training=False))

print('\nTest set accuracy: {accuracy:0.3f}\n'.format(**eval_result))

"""
Explanation: Estimator が期待するとおり、引数を取らない入力関数を指定しながら、input_fn 呼び出しを lambda にラッピングして引数をキャプチャするところに注意してください。steps 引数はメソッドに対して、あるトレーニングステップ数を完了した後にトレーニングを停止するように指定しています。
トレーニングされたモデルを評価する
モデルのトレーニングが完了したので、そのパフォーマンスに関する統計を得ることができます。次のコードブロックは、テストデータに対してトレーニングされたモデルの精度を評価します。
End of explanation
"""


# Generate predictions from the model
expected = ['Setosa', 'Versicolor', 'Virginica']
predict_x = {
    'SepalLength': [5.1, 5.9, 6.9],
    'SepalWidth': [3.3, 3.0, 3.1],
    'PetalLength': [1.7, 4.2, 5.4],
    'PetalWidth': [0.5, 1.5, 2.1],
}

def input_fn(features, batch_size=256):
    """An input function for prediction."""
    # Convert the inputs to a Dataset without labels.
    return tf.data.Dataset.from_tensor_slices(dict(features)).batch(batch_size)

predictions = classifier.predict(
    input_fn=lambda: input_fn(predict_x))

"""
Explanation: train メソッドへの呼び出しとは異なり、評価するsteps 引数を渡していません。eval の input_fn データの単一のエポックのみを返します。
eval_result ディクショナリには、average_loss（サンプル当たりの平均損失）、loss（ミニバッチ当たりの平均損失）、および Estimator の global_step の値（実行したトレーニングイテレーションの回数）も含まれます。
トレーニングされたモデルから予測（推論）を立てる
良質の評価結果を生み出すトレーニング済みのモデルを準備できました。これから、このトレーニング済みのモデルを使用し、ラベル付けできない測定に基づいてアヤメの品種を予測します。トレーニングと評価と同様に、単一の関数呼び出して予測を行います。
End of explanation
"""


for pred_dict, expec in zip(predictions, expected):
    class_id = pred_dict['class_ids'][0]
    probability = pred_dict['probabilities'][class_id]

    print('Prediction is "{}" ({:.1f}%), expected "{}"'.format(
        SPECIES[class_id], 100 * probability, expec))

"""
Explanation: predict メソッドは Python イテラブルを返し、各サンプルの予測結果のディクショナリを生成します。次のコードを使って、予測とその確率を出力します。
End of explanation
"""


